From 5f0006cfa7c499a501969641ac7bd630cb4706de Mon Sep 17 00:00:00 2001 From: Aqua-sama Date: Thu, 4 Mar 2021 15:43:28 +0200 Subject: pass cpu_state_t to interrupt handler --- libk/stdlib/abort.cc | 6 ---- makefile | 2 +- src/boot.S | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/boot.s | 96 --------------------------------------------------- src/cpu/exceptions.s | 51 --------------------------- src/cpu/interrupts.s | 50 --------------------------- src/cpu/registers.h | 26 ++++++++++++++ src/idt.cc | 30 +++++++++++----- src/idt/stubs.S | 82 ++++++++++++++++++++++++++++++++++++++++++++ src/makefile | 4 +-- 10 files changed, 229 insertions(+), 215 deletions(-) create mode 100644 src/boot.S delete mode 100644 src/boot.s delete mode 100644 src/cpu/exceptions.s delete mode 100644 src/cpu/interrupts.s create mode 100644 src/cpu/registers.h create mode 100644 src/idt/stubs.S diff --git a/libk/stdlib/abort.cc b/libk/stdlib/abort.cc index 41bf991..13bee4d 100644 --- a/libk/stdlib/abort.cc +++ b/libk/stdlib/abort.cc @@ -12,9 +12,3 @@ void abort() { __builtin_unreachable(); } -extern "C" void print_exception(uint8_t irq) { - printk("exception ", uhex{irq}, '\n'); - asm volatile("cli"); - while (true) asm volatile("hlt"); - __builtin_unreachable(); -}; diff --git a/makefile b/makefile index f570a4f..9af9064 100644 --- a/makefile +++ b/makefile @@ -20,7 +20,7 @@ glitch.elf: $(autogen) $(AS_OBJ) $(CXX_OBJ) @echo " LD $@" @$(LD) $(LD_FLAGS) -o $@ $(AS_OBJ) $(CXX_OBJ) -$(AS_OBJ): $(OBJ_DIR)/%.o: %.s +$(AS_OBJ): $(OBJ_DIR)/%.o: %.S @mkdir -p $(@D) @echo " AS $<" @$(AS) -target $(TARGET) -nostdlib -Wall -Wextra -c $^ -o $@ diff --git a/src/boot.S b/src/boot.S new file mode 100644 index 0000000..fff1644 --- /dev/null +++ b/src/boot.S @@ -0,0 +1,97 @@ +/* + Declare a multiboot header that marks the program as a kernel. + https://www.gnu.org/software/grub/manual/multiboot2/html_node/index.html + + The Multiboot2 header must be contained completely within the first + 32kB of the OS image, and must be 64-bit (8 byte) aligned. +*/ +.set MULTIBOOT_HEADER_MAGIC, 0xe85250d6 # multiboot2 magic number +.set MULTIBOOT_ARCHITECTURE, 0 # protected mode i386 +.set MULTIBOOT_HEADER_TAG_END, 0 + +.section .multiboot +.align 8 +header_start: + .int MULTIBOOT_HEADER_MAGIC + .int MULTIBOOT_ARCHITECTURE + .int header_end - header_start + .int -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE + (header_end - header_start)) + + # TODO tags go here + + .short MULTIBOOT_HEADER_TAG_END + .short 0 + .int 8 +header_end: + +/* + The stack on x86 must be 16-byte aligned according to the System V ABI + standard and de-facto extensions. The compiler will assume the stack is + properly aligned and failure to align the stack will result in undefined + behavior. +*/ +.section .stack, "aw", @nobits +.align 16 +stack_bottom: + .skip 16 * 1024 +stack_top: + +/* + The linker script specifies _start as the entry point to the kernel and the + bootloader will jump to this position once the kernel has been loaded. +*/ +.section .text +.extern kernel_constructors +.extern kernel_main +.global _start +.type _start, @function +_start: + mov $stack_bottom, %ebp + mov $stack_top, %esp # point the stack pointer to the stack + + /* + This is a good place to initialize crucial processor state before the + high-level kernel is entered. It's best to minimize the early + environment where crucial features are offline. Note that the + processor is not fully initialized yet: Features such as floating + point instructions and instruction set extensions are not initialized + yet. The GDT should be loaded here. Paging should be enabled here. + C++ features such as global constructors and exceptions will require + runtime support to work as well. + */ + + pushl %ebx # push the pointer to the multiboot structure + pushl %eax # push the multiboot magic value + call kernel_constructors + + /* + Enter the high-level kernel. The ABI requires the stack is 16-byte + aligned at the time of the call instruction (which afterwards pushes + the return pointer of size 4 bytes). The stack was originally 16-byte + aligned above and we've pushed a multiple of 16 bytes to the + stack since (pushed 0 bytes so far), so the alignment has thus been + preserved and the call is well defined. + */ + call kernel_main + + /* + If the system has nothing more to do, put the computer into an + infinite loop. To do that: + 1) Disable interrupts with cli (clear interrupt enable in eflags). + They are already disabled by the bootloader, so this is not needed. + Mind that you might later enable interrupts and return from + kernel_main (which is sort of nonsensical to do). + 2) Wait for the next interrupt to arrive with hlt (halt instruction). + Since they are disabled, this will lock up the computer. + 3) Jump to the hlt instruction if it ever wakes up due to a + non-maskable interrupt occurring or due to system management mode. + */ + cli +hang: hlt + jmp hang + +/* +Set the size of the _start symbol to the current location '.' minus its start. +This is useful when debugging or when you implement call tracing. +*/ +.size _start, . - _start diff --git a/src/boot.s b/src/boot.s deleted file mode 100644 index 2286152..0000000 --- a/src/boot.s +++ /dev/null @@ -1,96 +0,0 @@ -/* - Declare a multiboot header that marks the program as a kernel. - https://www.gnu.org/software/grub/manual/multiboot2/html_node/index.html - - The Multiboot2 header must be contained completely within the first - 32kB of the OS image, and must be 64-bit (8 byte) aligned. -*/ -.set MULTIBOOT_HEADER_MAGIC, 0xe85250d6 # multiboot2 magic number -.set MULTIBOOT_ARCHITECTURE, 0 # protected mode i386 -.set MULTIBOOT_HEADER_TAG_END, 0 - -.section .multiboot -.align 8 -header_start: - .int MULTIBOOT_HEADER_MAGIC - .int MULTIBOOT_ARCHITECTURE - .int header_end - header_start - .int -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE + (header_end - header_start)) - - # TODO tags go here - - .short MULTIBOOT_HEADER_TAG_END - .short 0 - .int 8 -header_end: - -/* - The stack on x86 must be 16-byte aligned according to the System V ABI - standard and de-facto extensions. The compiler will assume the stack is - properly aligned and failure to align the stack will result in undefined - behavior. -*/ -.section .stack, "aw", @nobits -.align 16 -stack_bottom: - .skip 16 * 1024 -stack_top: - -/* - The linker script specifies _start as the entry point to the kernel and the - bootloader will jump to this position once the kernel has been loaded. -*/ -.section .text -.extern kernel_constructors -.extern kernel_main -.global _start -.type _start, @function -_start: - mov $stack_top, %esp # point the stack pointer to the stack - - /* - This is a good place to initialize crucial processor state before the - high-level kernel is entered. It's best to minimize the early - environment where crucial features are offline. Note that the - processor is not fully initialized yet: Features such as floating - point instructions and instruction set extensions are not initialized - yet. The GDT should be loaded here. Paging should be enabled here. - C++ features such as global constructors and exceptions will require - runtime support to work as well. - */ - - pushl %ebx # push the pointer to the multiboot structure - pushl %eax # push the multiboot magic value - call kernel_constructors - - /* - Enter the high-level kernel. The ABI requires the stack is 16-byte - aligned at the time of the call instruction (which afterwards pushes - the return pointer of size 4 bytes). The stack was originally 16-byte - aligned above and we've pushed a multiple of 16 bytes to the - stack since (pushed 0 bytes so far), so the alignment has thus been - preserved and the call is well defined. - */ - call kernel_main - - /* - If the system has nothing more to do, put the computer into an - infinite loop. To do that: - 1) Disable interrupts with cli (clear interrupt enable in eflags). - They are already disabled by the bootloader, so this is not needed. - Mind that you might later enable interrupts and return from - kernel_main (which is sort of nonsensical to do). - 2) Wait for the next interrupt to arrive with hlt (halt instruction). - Since they are disabled, this will lock up the computer. - 3) Jump to the hlt instruction if it ever wakes up due to a - non-maskable interrupt occurring or due to system management mode. - */ - cli -hang: hlt - jmp hang - -/* -Set the size of the _start symbol to the current location '.' minus its start. -This is useful when debugging or when you implement call tracing. -*/ -.size _start, . - _start diff --git a/src/cpu/exceptions.s b/src/cpu/exceptions.s deleted file mode 100644 index 9736be1..0000000 --- a/src/cpu/exceptions.s +++ /dev/null @@ -1,51 +0,0 @@ -.section .text -.extern print_exception - -.macro exception num -.global exception\num -exception\num: - movb $\num, (exc) - jmp exception_common -.endm - -exception 0x00 -exception 0x01 -exception 0x02 -exception 0x03 -exception 0x04 -exception 0x05 -exception 0x06 -exception 0x07 -exception 0x08 -exception 0x09 -exception 0x0a -exception 0x0b -exception 0x0c -exception 0x0d -exception 0x0e -exception 0x0f -exception 0x10 -exception 0x11 -exception 0x12 -exception 0x13 - -exception_common: - pusha - pushl %ds - pushl %es - pushl %fs - pushl %gs - - push (exc) - call print_exception - - popl %gs - popl %fs - popl %es - popl %ds - popa - iret - -.data - exc: .byte 0 - diff --git a/src/cpu/interrupts.s b/src/cpu/interrupts.s deleted file mode 100644 index 17c39e9..0000000 --- a/src/cpu/interrupts.s +++ /dev/null @@ -1,50 +0,0 @@ -.section .text -.extern handle_interrupt - -.macro interrupt num -.global interrupt\num -interrupt\num: - movb $\num, (irq) - jmp interrupt_common -.endm - -interrupt 0x00 # system timer -interrupt 0x01 # keyboard controller -interrupt 0x02 # slave pic -interrupt 0x03 # serial port 2 and 4 -interrupt 0x04 # serial port 1 and 3 -interrupt 0x05 # parallel port 2 and 3, sound card -interrupt 0x06 # floppy controller -interrupt 0x07 # parallel port 1 - -interrupt 0x08 # real-time clock -interrupt 0x09 # acpi -interrupt 0x0a # -interrupt 0x0b # -interrupt 0x0c # mouse on ps/2 -interrupt 0x0d # fpu -interrupt 0x0e # primary ATA -interrupt 0x0f # secondary ATA - -interrupt_common: - pusha - pushl %ds - pushl %es - pushl %fs - pushl %gs - - pushl %esp - push (irq) - call handle_interrupt - mov %eax, %esp - - popl %gs - popl %fs - popl %es - popl %ds - popa - iret - -.data - irq: .byte 0 - diff --git a/src/cpu/registers.h b/src/cpu/registers.h new file mode 100644 index 0000000..edb6b7b --- /dev/null +++ b/src/cpu/registers.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace x86 { + +struct cpu_state { + /* pusha */ + uint32_t edi, esi; // destination index, source index + uint32_t ebp, esp; // base pointer, stack pointer + uint32_t ebx, edx, ecx, eax; // general registers + + uint32_t irq; + uint32_t error; + + /* stack frame, pushed by cpu */ + uint32_t eip; + uint32_t cs; + uint32_t eflags; + uint32_t __esp; + uint32_t ss; +} __attribute__((packed)); + +} // namespace x86 + +typedef x86::cpu_state cpu_state_t; diff --git a/src/idt.cc b/src/idt.cc index 68ae260..363afcd 100644 --- a/src/idt.cc +++ b/src/idt.cc @@ -1,22 +1,24 @@ #include "idt.h" #include #include "cpu/irq.h" +#include "cpu/registers.h" #include "ports.h" static_assert(sizeof(IDT::Pointer) == 6); +constexpr uint8_t irq_base = 0x20; static InterruptHandler* handlers[256] = {nullptr}; bool IDT::install(uint8_t irq, InterruptHandler* h) { - if (h != nullptr && handlers[irq] == nullptr) { - handlers[irq] = h; + if (h != nullptr && handlers[irq + irq_base] == nullptr) { + handlers[irq + irq_base] = h; return true; } return false; } bool IDT::uninstall(uint8_t irq, InterruptHandler* h) { - if (handlers[irq] == h) { - handlers[irq] = nullptr; + if (handlers[irq + irq_base] == h) { + handlers[irq + irq_base] = nullptr; return true; } return false; @@ -37,18 +39,28 @@ bool IDT::uninstall(uint8_t irq, InterruptHandler* h) { #define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ #define ICW4_SFNM 0x10 /* Special fully nested (not) */ -extern "C" uint32_t handle_interrupt(uint8_t irq, uint32_t esp) { - if (handlers[irq] != nullptr) { - handlers[irq]->trigger(); +extern "C" void print_exception(cpu_state_t* r) { + printk("exception ", uhex{r->irq}, " error ", uhex{r->error}, '\n'); + asm volatile("cli"); + while (true) asm volatile("hlt"); + __builtin_unreachable(); +}; + +extern "C" uint32_t handle_interrupt(cpu_state_t* r) { + if (r->irq < irq_base) print_exception(r); + + if (handlers[r->irq] != nullptr) { + handlers[r->irq]->trigger(); } pic1_t pic1; pic1.write(0x20); - if (irq > 7) { + if (r->irq > 7 + irq_base) { pic2_t pic2; pic2.write(0x20); } - return esp; + + return reinterpret_cast(r); }; IDT::IDT(const uint16_t selector) { diff --git a/src/idt/stubs.S b/src/idt/stubs.S new file mode 100644 index 0000000..97f1797 --- /dev/null +++ b/src/idt/stubs.S @@ -0,0 +1,82 @@ +.set IRQ_BASE, 0x20 + +.section .text +.extern handle_interrupt + +.macro interrupt num +.global interrupt\num +interrupt\num: + push $0 + push $\num + IRQ_BASE + jmp interrupt_common +.endm + +.macro exception num +.global exception\num +exception\num: + push $0 + push $\num + jmp interrupt_common +.endm + +.macro exception_ec num +.global exception\num +exception\num: + push $\num + jmp interrupt_common +.endm + +/* exceptions */ +exception 0x00 +exception 0x01 +exception 0x02 +exception 0x03 +exception 0x04 +exception 0x05 +exception 0x06 +exception 0x07 +exception_ec 0x08 +exception 0x09 +exception_ec 0x0a +exception_ec 0x0b +exception_ec 0x0c +exception_ec 0x0d +exception_ec 0x0e +exception 0x0f +exception 0x10 +exception_ec 0x11 +exception 0x12 +exception 0x13 + +/* interrupts - master pic */ +interrupt 0x00 # system timer +interrupt 0x01 # keyboard controller +interrupt 0x02 # slave pic +interrupt 0x03 # serial port 2 and 4 +interrupt 0x04 # serial port 1 and 3 +interrupt 0x05 # parallel port 2 and 3, sound card +interrupt 0x06 # floppy controller +interrupt 0x07 # parallel port 1 +/* interrupts - slave pic */ +interrupt 0x08 # real-time clock +interrupt 0x09 # acpi +interrupt 0x0a # +interrupt 0x0b # +interrupt 0x0c # mouse on ps/2 +interrupt 0x0d # fpu +interrupt 0x0e # primary ATA +interrupt 0x0f # secondary ATA + +interrupt_common: + pusha + + push %esp + call handle_interrupt + mov %eax, %esp + + popa + /* remove error code and irq from stack */ + add $8, %esp + + iret + diff --git a/src/makefile b/src/makefile index 8db63d6..64853fe 100644 --- a/src/makefile +++ b/src/makefile @@ -1,5 +1,5 @@ AS_OBJ += src/boot.o \ - src/cpu/exceptions.o src/cpu/interrupts.o + src/idt/stubs.o CXX_OBJ += src/kernel.o \ src/kernel/dump_gdt.o \ @@ -10,7 +10,7 @@ CXX_OBJ += src/kernel.o \ src/idt.o \ src/idt/interruptgate.o src/idt/interrupthandler.o -src/cpu/irq.h: $(OBJ_DIR)/src/cpu/exceptions.o $(OBJ_DIR)/src/cpu/interrupts.o +src/cpu/irq.h: $(OBJ_DIR)/src/idt/stubs.o @echo " GEN $@" @echo '#pragma once' > $@ @echo 'extern "C" {' >> $@ -- cgit v1.2.1