diff --git a/Makefile b/Makefile index 13189b2..94347f9 100644 --- a/Makefile +++ b/Makefile @@ -75,14 +75,12 @@ go.o: @# objcopy to make that symbol exportable. Since nasm does not support externs @# with slashes we create a global symbol alias for kernel.Kmain @echo "[objcopy] create kernel.Kmain alias to gopheros/kernel/kmain.Kmain" - @echo "[objcopy] globalizing symbols {_rt0_interrupt_handlers, runtime.g0/m0/physPageSize/useAVXmemmove}" + @echo "[objcopy] globalizing symbols {runtime.g0/m0/physPageSize}" @objcopy \ --add-symbol kernel.Kmain=.text:0x`nm $(BUILD_DIR)/go.o | grep "kmain.Kmain$$" | cut -d' ' -f1` \ - --globalize-symbol _rt0_interrupt_handlers \ --globalize-symbol runtime.g0 \ --globalize-symbol runtime.m0 \ --globalize-symbol runtime.physPageSize \ - --globalize-symbol runtime.useAVXmemmove \ $(BUILD_DIR)/go.o $(BUILD_DIR)/go.o binutils_version_check: @@ -191,6 +189,8 @@ lint: lint-check-deps --deadline 300s \ --exclude 'possible misuse of unsafe.Pointer' \ --exclude 'x \^ 0 always equals x' \ + --exclude 'dispatchInterrupt is unused' \ + --exclude 'interruptGateEntries is unused' \ src/... lint-check-deps: diff --git a/src/arch/x86_64/asm/rt0_64.s b/src/arch/x86_64/asm/rt0_64.s index 784331e..d7a8605 100644 --- a/src/arch/x86_64/asm/rt0_64.s +++ b/src/arch/x86_64/asm/rt0_64.s @@ -6,20 +6,6 @@ bits 64 section .bss align 8 -; Allocate space for the interrupt descriptor table (IDT). -; This arch supports up to 256 interrupt handlers -%define IDT_ENTRIES 0xff -_rt0_idt_start: - resq 2 * IDT_ENTRIES ; each 64-bit IDT entry is 16 bytes -_rt0_idt_end: - -_rt0_idt_desc: - resw 1 - resq 1 - -; Allocates space for the IRQ handlers pointers registered by the IRQ package -_rt0_irq_handlers resq IDT_ENTRIES - ; According to the "ELF handling for TLS" document section 3.4.6 ; (https://www.akkadia.org/drepper/tls.pdf) for the GNU variant for x86-64, ; fs:0x00 contains a pointer to the TCB. Variables in the TLS are stored @@ -27,14 +13,6 @@ _rt0_irq_handlers resq IDT_ENTRIES r0_g_ptr: resq 1 tcb_ptr: resq 1 -; Go < 1.9 does not define runtime.useAVXmemmove; to avoid linker errors define -; a dummy symbol so that the gate entry code can work as expected. -%if WITH_RUNTIME_AVXMEMMOVE == 0 - runtime.useAVXmemmove resb 1 -%else - extern runtime.useAVXmemmove -%endif - section .text ;------------------------------------------------------------------------------ @@ -49,7 +27,6 @@ section .text global _rt0_64_entry _rt0_64_entry: call _rt0_install_redirect_trampolines - call _rt0_64_load_idt call _rt0_64_setup_go_runtime_structs ; Call the kernel entry point passing a pointer to the multiboot data @@ -130,261 +107,6 @@ _rt0_64_setup_go_runtime_structs: ret - -;------------------------------------------------------------------------------ -; Setup and load IDT. We preload each IDT entry with a pointer to a gate handler -; but set it as inactive. The code in irq_amd64 is responsible for enabling -; individual IDT entries when handlers are installed. -;------------------------------------------------------------------------------ -_rt0_64_load_idt: - mov rax, _rt0_idt_start - -%assign gate_num 0 -%rep IDT_ENTRIES - mov rbx, _rt0_64_gate_entry_%+ gate_num - mov word [rax], bx ; gate entry bits 0-15 - mov word [rax+2], 0x8 ; GDT descriptor - mov byte [rax+5], 0x0 ; Mark the entry as NOT present - shr rbx, 16 - mov word [rax+6], bx ; gate entry bits 16-31 - shr rbx, 16 - mov dword [rax+8], ebx ; gate entry bits 32-63 - - add rax, 16 ; size of IDT entry -%assign gate_num gate_num+1 -%endrep - mov rax, _rt0_idt_desc - mov word [rax], _rt0_idt_end - _rt0_idt_start - 1 ; similar to GDT this must be len(IDT) - 1 - mov rbx, _rt0_idt_start - mov qword [rax+2], rbx - lidt [rax] -ret - - -;------------------------------------------------------------------------------ -; Generate gate entries. Each gate handler pushes the address of the registered -; handler to the stack before jumping to a dispatcher function. -; -; Some exceptions push an error code to the stack after the stack frame. This -; code must be popped off the stack before calling iretq. The generated handlers -; are aware whether they need to deal with the code or not and jump to the -; appropriate get dispatcher. -;------------------------------------------------------------------------------ -%assign gate_num 0 -%rep IDT_ENTRIES -extern _rt0_interrupt_handlers -_rt0_64_gate_entry_%+ gate_num: - push rax - mov rax, _rt0_interrupt_handlers - add rax, 8*gate_num - mov rax, [rax] - xchg rax, [rsp] ; store handler address and restore original rax - - ; For a list of gate numbers that push an error code see: - ; http://wiki.osdev.org/Exceptions - %if (gate_num == 8) || (gate_num >= 10 && gate_num <= 14) || (gate_num == 17) || (gate_num == 30) - jmp _rt0_64_gate_dispatcher_with_code - %else - jmp _rt0_64_gate_dispatcher_without_code - %endif -%assign gate_num gate_num+1 -%endrep - -%macro save_gp_regs 0 - push r15 - push r14 - push r13 - push r12 - push r11 - push r10 - push r9 - push r8 - push rbp - push rdi - push rsi - push rdx - push rcx - push rbx - push rax -%endmacro - -%macro restore_gp_regs 0 - pop rax - pop rbx - pop rcx - pop rdx - pop rsi - pop rdi - pop rbp - pop r8 - pop r9 - pop r10 - pop r11 - pop r12 - pop r13 - pop r14 - pop r15 -%endmacro - -%macro save_xmm_regs 0 - sub rsp, 16*16 - movdqu [rsp+0*16], xmm0 - movdqu [rsp+1*16], xmm1 - movdqu [rsp+2*16], xmm2 - movdqu [rsp+3*16], xmm3 - movdqu [rsp+4*16], xmm4 - movdqu [rsp+5*16], xmm5 - movdqu [rsp+6*16], xmm6 - movdqu [rsp+7*16], xmm7 - movdqu [rsp+8*16], xmm8 - movdqu [rsp+9*16], xmm9 - movdqu [rsp+10*16], xmm10 - movdqu [rsp+11*16], xmm11 - movdqu [rsp+12*16], xmm12 - movdqu [rsp+13*16], xmm13 - movdqu [rsp+14*16], xmm14 - movdqu [rsp+15*16], xmm15 -%endmacro - -%macro restore_xmm_regs 0 - movdqu xmm0, [rsp+0*16] - movdqu xmm1, [rsp+1*16] - movdqu xmm2, [rsp+2*16] - movdqu xmm3, [rsp+3*16] - movdqu xmm4, [rsp+4*16] - movdqu xmm5, [rsp+5*16] - movdqu xmm6, [rsp+6*16] - movdqu xmm7, [rsp+7*16] - movdqu xmm8, [rsp+8*16] - movdqu xmm9, [rsp+9*16] - movdqu xmm10, [rsp+10*16] - movdqu xmm11, [rsp+11*16] - movdqu xmm12, [rsp+12*16] - movdqu xmm13, [rsp+13*16] - movdqu xmm14, [rsp+14*16] - movdqu xmm15, [rsp+15*16] - add rsp, 16*16 -%endmacro - - -;------------------------------------------------------------------------------ -; This dispatcher is invoked by gate entries that expect a code to be pushed -; by the CPU to the stack. -; -; This is the stack layout used by this function. Items are 8-bytes -; wide with the exception of the xmm regs that are 16 bytes wide -; -; ----------------| -; useAVXmemmove | <- original value of runtime.useAVXmemmove -;-----------------| -; xmm regs (16) | -;-----------------| <- RBP will point at the last pushed GP reg -; gp regs (15) | -;-----------------| -; handler address | <- pushed by gate_entry_xxx (RSP initially points here) -;-----------------| -; exception code | <- pushed by CPU (must be popped before returning) -;-----------------| -; RIP | <- pushed by CPU (exception frame) -; CS | -; RFLAGS | -; RSP | -; SS | -;----------------- -;------------------------------------------------------------------------------ -_rt0_64_gate_dispatcher_with_code: - cld - - ; save general-purpose regs - save_gp_regs - mov rbp, rsp ; rbp points to saved rax - - ; save xmm regs as the fault handler may clobber them by calling an - ; SSE-enabled runtime function like copy (calls runtime.memmove). In - ; addition temporarily disable AVX support for runtime.memmove so we - ; don't need to also preserve the avx regs. - save_xmm_regs - mov rax, runtime.useAVXmemmove - push qword [rax] - mov byte [rax], 0 - - ; push exception handler args and call registered handler - push qword [rbp] ; ptr to regs - push qword [rbp+17*8] ; ptr to exception frame - push qword [rbp+16*8] ; exception code - call qword [rbp+15*8] - add rsp, 3 * 8 - - ; restore xmm regs and restore AVX support for runtime.memmove - mov rax, runtime.useAVXmemmove - pop rbx - mov byte [rax], bl - restore_xmm_regs - - ; restore general purpose regs - restore_gp_regs - - ; pop handler address + exception code so RSP points to the stack frame. - add rsp, 2*8 - iretq - -;------------------------------------------------------------------------------ -; This dispatcher is invoked by gate entries that do not use exception codes. -; -; This is the stack layout used by this function. Items are 8-bytes -; wide with the exception of the xmm regs that are 16 bytes wide -; -; ----------------| -; useAVXmemmove | <- original value of runtime.useAVXmemmove -;-----------------| -; xmm regs (16) | -;-----------------| <- RBP will point at the last pushed GP reg -; gp regs (15) | -;-----------------| -; handler address | <- pushed by gate_entry_xxx (RSP initially points here) -;-----------------| -; RIP | <- pushed by CPU (exception frame) -; CS | -; RFLAGS | -; RSP | -; SS | -;----------------- -;------------------------------------------------------------------------------ -_rt0_64_gate_dispatcher_without_code: - cld - - ; save general-purpose regs - save_gp_regs - mov rbp, rsp ; rbp points to saved rax - - ; save xmm regs as the fault handler may clobber them by calling an - ; SSE-enabled runtime function like copy (calls runtime.memmove). In - ; addition temporarily disable AVX support for runtime.memmove so we - ; don't need to also preserve the avx regs. - save_xmm_regs - mov rax, runtime.useAVXmemmove - push qword [rax] - mov byte [rax], 0 - - ; push exception handler args and call registered handler - push qword [rbp] ; ptr to regs - push qword [rbp+16*8] ; ptr to exception frame - call qword [rbp+15*8] - add rsp, 2 * 8 - - ; restore xmm regs and restore AVX support for runtime.memmove - mov rax, runtime.useAVXmemmove - pop rbx - mov byte [rax], bl - restore_xmm_regs - - ; restore general purpose regs - restore_gp_regs - - ; pop handler address so RSP points to the stack frame. - add rsp, 8 - iretq - ;------------------------------------------------------------------------------ ; Error messages ;------------------------------------------------------------------------------ diff --git a/src/gopheros/kernel/gate/gate_amd64.go b/src/gopheros/kernel/gate/gate_amd64.go new file mode 100644 index 0000000..c332bb2 --- /dev/null +++ b/src/gopheros/kernel/gate/gate_amd64.go @@ -0,0 +1,153 @@ +package gate + +import ( + "gopheros/kernel/kfmt" + "io" +) + +// Registers contains a snapshot of all register values when an exception, +// interrupt or syscall occurs. +type Registers struct { + RAX uint64 + RBX uint64 + RCX uint64 + RDX uint64 + RSI uint64 + RDI uint64 + RBP uint64 + R8 uint64 + R9 uint64 + R10 uint64 + R11 uint64 + R12 uint64 + R13 uint64 + R14 uint64 + R15 uint64 + + // Info contains the exception code for exceptions, the syscall number + // for syscall entries or the IRQ number for HW interrupts. + Info uint64 + + // The return frame used by IRETQ + RIP uint64 + CS uint64 + RFlags uint64 + RSP uint64 + SS uint64 +} + +// DumpTo outputs the register contents to w. +func (r *Registers) DumpTo(w io.Writer) { + kfmt.Fprintf(w, "RAX = %16x RBX = %16x\n", r.RAX, r.RBX) + kfmt.Fprintf(w, "RCX = %16x RDX = %16x\n", r.RCX, r.RDX) + kfmt.Fprintf(w, "RSI = %16x RDI = %16x\n", r.RSI, r.RDI) + kfmt.Fprintf(w, "RBP = %16x\n", r.RBP) + kfmt.Fprintf(w, "R8 = %16x R9 = %16x\n", r.R8, r.R9) + kfmt.Fprintf(w, "R10 = %16x R11 = %16x\n", r.R10, r.R11) + kfmt.Fprintf(w, "R12 = %16x R13 = %16x\n", r.R12, r.R13) + kfmt.Fprintf(w, "R14 = %16x R15 = %16x\n", r.R14, r.R15) + kfmt.Fprintf(w, "\n") + kfmt.Fprintf(w, "RIP = %16x CS = %16x\n", r.RIP, r.CS) + kfmt.Fprintf(w, "RSP = %16x SS = %16x\n", r.RSP, r.SS) + kfmt.Fprintf(w, "RFL = %16x\n", r.RFlags) +} + +// InterruptNumber describes an x86 interrupt/exception/trap slot. +type InterruptNumber uint8 + +const ( + // DivideByZero occurs when dividing any number by 0 using the DIV or + // IDIV instruction. + DivideByZero = InterruptNumber(0) + + // NMI (non-maskable-interrupt) is a hardware interrupt that indicates + // issues with RAM or unrecoverable hardware problems. It may also be + // raised by the CPU when a watchdog timer is enabled. + NMI = InterruptNumber(2) + + // Overflow occurs when an overflow occurs (e.g result of division + // cannot fit into the registers used). + Overflow = InterruptNumber(4) + + // BoundRangeExceeded occurs when the BOUND instruction is invoked with + // an index out of range. + BoundRangeExceeded = InterruptNumber(5) + + // InvalidOpcode occurs when the CPU attempts to execute an invalid or + // undefined instruction opcode. + InvalidOpcode = InterruptNumber(6) + + // DeviceNotAvailable occurs when the CPU attempts to execute an + // FPU/MMX/SSE instruction while no FPU is available or while + // FPU/MMX/SSE support has been disabled by manipulating the CR0 + // register. + DeviceNotAvailable = InterruptNumber(7) + + // DoubleFault occurs when an unhandled exception occurs or when an + // exception occurs within a running exception handler. + DoubleFault = InterruptNumber(8) + + // InvalidTSS occurs when the TSS points to an invalid task segment + // selector. + InvalidTSS = InterruptNumber(10) + + // SegmentNotPresent occurs when the CPU attempts to invoke a present + // gate with an invalid stack segment selector. + SegmentNotPresent = InterruptNumber(11) + + // StackSegmentFault occurs when attempting to push/pop from a + // non-canonical stack address or when the stack base/limit (set in + // GDT) checks fail. + StackSegmentFault = InterruptNumber(12) + + // GPFException occurs when a general protection fault occurs. + GPFException = InterruptNumber(13) + + // PageFaultException occurs when a page directory table (PDT) or one + // of its entries is not present or when a privilege and/or RW + // protection check fails. + PageFaultException = InterruptNumber(14) + + // FloatingPointException occurs while invoking an FP instruction while: + // - CR0.NE = 1 OR + // - an unmasked FP exception is pending + FloatingPointException = InterruptNumber(16) + + // AlignmentCheck occurs when alignment checks are enabled and an + // unaligmed memory access is performed. + AlignmentCheck = InterruptNumber(17) + + // MachineCheck occurs when the CPU detects internal errors such as + // memory-, bus- or cache-related errors. + MachineCheck = InterruptNumber(18) + + // SIMDFloatingPointException occurs when an unmasked SSE exception + // occurs while CR4.OSXMMEXCPT is set to 1. If the OSXMMEXCPT bit is + // not set, SIMD FP exceptions cause InvalidOpcode exceptions instead. + SIMDFloatingPointException = InterruptNumber(19) +) + +// Init runs the appropriate CPU-specific initialization code for enabling +// support for interrupt handling. +func Init() { + installIDT() +} + +// HandleInterrupt ensures that the provided handler will be invoked when a +// particular interrupt number occurs. The value of the istOffset argument +// specifies the offset in the interrupt stack table (if 0 then IST is not +// used). +func HandleInterrupt(intNumber InterruptNumber, istOffset uint8, handler func(*Registers)) + +// installIDT populates idtDescriptor with the address of IDT and loads it to +// the CPU. All gate entries are initially marked as non-present and must be +// explicitly enabled via a call to install{Trap,IRQ,Task}Handler. +func installIDT() + +// dispatchInterrupt is invoked by the interrupt gate entrypoints to route +// an incoming interrupt to the selected handler. +func dispatchInterrupt() + +// interruptGateEntries contains a list of generated entries for each possible +// interrupt number. Depending on the +func interruptGateEntries() diff --git a/src/gopheros/kernel/gate/gate_amd64.s b/src/gopheros/kernel/gate/gate_amd64.s new file mode 100644 index 0000000..abdd942 --- /dev/null +++ b/src/gopheros/kernel/gate/gate_amd64.s @@ -0,0 +1,301 @@ +#include "textflag.h" + +#define NUM_IDT_ENTRIES 256 +#define IDT_ENTRY_SIZE 16 +#define IDT_ENTRY_SIZE_SHIFT 4 + +#define ENTRY_TYPE_INTERRUPT_GATE 0x8e + +// The 64-bit SIDT consists of 10 bytes and has the following layout: +// BYTE +// [00 - 01] size of IDT minus 1 +// [02 - 09] address of the IDT +GLOBL ·idtDescriptor<>(SB), NOPTR, $10 + +// The 64-bit IDT consists of NUM_IDT_ENTRIES slots containing 16-byte entries +// with the following layout: +// BYTE +// [00 - 01] 64-bit gate entry address (bits 0:15) +// [02 - 03] CS selector +// [04 - 04] interrupt stack table offset (bits 0:2) +// [05 - 05] gate type and attributes +// [06 - 07] 64-bit gate entry address (bits 16:31) +// [08 - 11] 64-bit gate entry address (bits 32:63) +// [12 - 15] reserved +GLOBL ·idt<>(SB), NOPTR, $NUM_IDT_ENTRIES*IDT_ENTRY_SIZE + +// A list of 256 function pointers for installed gate handlers. These pointers +// serve as the jump targets for the trap/int/task dispatchers. +GLOBL ·gateHandlers<>(SB), NOPTR, $NUM_IDT_ENTRIES*8 + +// installIDT populates idtDescriptor with the address of IDT and loads it to +// the CPU. All gate entries are initially marked as non-present and must be +// explicitly enabled by invoking HandleInterrupt. +TEXT ·installIDT(SB),NOSPLIT,$0 + LEAQ ·idtDescriptor<>(SB), AX + MOVW $(NUM_IDT_ENTRIES*IDT_ENTRY_SIZE)-1, 0(AX) + LEAQ ·idt<>(SB), BX + MOVQ BX, 2(AX) + MOVQ 0(AX), IDTR // LIDT[RAX] + RET + +// HandleInterrupt ensures that the provided handler will be invoked when a +// particular interrupt number occurs. The value of the istOffset argument +// specifies the offset in the interrupt stack table (if 0 then IST is not +// used). +TEXT ·HandleInterrupt(SB),NOSPLIT,$0-10 + MOVBQZX intNumber+0(FP), CX + + // Dereference pointer to trap handler and copy it into gateHandlers + MOVQ handler+8(FP), BX + MOVQ 0(BX), BX + LEAQ ·gateHandlers<>+0(SB), DI + MOVQ BX, (DI)(CX*8) + + // Calculate IDT entry address + LEAQ ·idt<>+0(SB), DI + MOVQ CX, BX + SHLQ $IDT_ENTRY_SIZE_SHIFT, BX + ADDQ BX, DI + + // The trap gate entries have variable lengths depending on whether + // the CPU pushes an exception code or not. Each generated entry ends + // with a sequence of 4 NOPs (0x90). The code below uses this information + // to locate the correct entry point address. + LEAQ ·interruptGateEntries(SB), SI // SI points to entry for trap 0 +check_next_entry: + TESTB CX, CX + JZ update_idt_entry + +find_nop_delimiter: + INCQ SI + CMPL 0(SI), $0x90909090 + JNE find_nop_delimiter + + // SI points to the 4xNOP delimiter start + ADDQ $4, SI + DECB CX + JMP check_next_entry + +update_idt_entry: + // IDT entry layout (bytes) + // ------------------------ + // [00-01] bits 0-15 of 64-bit handler address + // [02-03] CS selector + // [04-04] interrupt stack table offset (IST) + // [05-05] gate type/attributes + // [06-07] bits 16-31 of 64-bit handler address + // [08-11] bits 32-63 of 64-bit handler address + // [12-15] reserved + //------------------------- + + // Mark entry as non-present while updating the handler address + MOVB $0, 5(DI) + + // Use the kernel CS selector from the rt0-loaded GDT and use the + // specified IST offset + MOVW $0x8, 2(DI) + MOVB istOffset+1(FP), AX + MOVB AX, 4(DI) + + // Copy the entrypoint address from SI + MOVW SI, 0(DI) + SHRQ $16, SI + MOVW SI, 6(DI) + SHRQ $16, SI + MOVL SI, 8(DI) + + // Mark entry as a present, 32-bit interrupt gate + MOVB $ENTRY_TYPE_INTERRUPT_GATE, 5(DI) + + RET + +// Emit interrupt dispatching code for traps where the CPU pushes an exception +// code to the stack. The code below just pushes the handler's address to the +// stack and jumps to dispatchInterrupt. +// +// This code uses some tricks to bypass Go assembler limitations: +// - replace PUSH with: SUBQ $8, RSP; MOVQ X, 0(RSP). This prevents the Go +// assembler from complaining about unbalanced PUSH/POP statements. +// - use a PUSH/RET (0xc3 byte instead of RET mnemonic) trick instead of a +// "JMP dispatchInterrupt" to prevent the optimizer from optimizing away all +// but the first entry in interruptGateEntries. +// +// Finally, each entry block ends with a series of 4 NOP instructions. This +// delimiter is used by the HandleInterrupt implementation to locate the correct +// entrypoint address for a particular interrupt. +#define INT_ENTRY_WITH_CODE(num) \ + SUBQ $16, SP; \ + MOVQ R15, 0(SP); \ + MOVQ ·gateHandlers<>+8*num(SB), R15; \ + MOVQ R15, 8(SP); \ + LEAQ ·dispatchInterrupt(SB), R15; \ + XCHGQ R15, 0(SP); \ + BYTE $0xc3; \ + BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; + +// Emit interrupt dispatching code for traps where the CPU does not push an +// exception code to the stack. The implementation is identical with the +// INT_ENTRY_WITH_CODE above with the exception that the interrupt number is +// manually pushed to the stack before the handler address so both entry +// variants can use the same dispatching code. +#define INT_ENTRY_WITHOUT_CODE(num) \ + SUBQ $24, SP; \ + MOVQ R15, 0(SP); \ + MOVQ ·gateHandlers<>+8*num(SB), R15; \ + MOVQ R15, 8(SP); \ + MOVQ $num, 16(SP); \ + LEAQ ·dispatchInterrupt(SB), R15; \ + XCHGQ R15, 0(SP); \ + BYTE $0xc3; \ + BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; + +// dispatchInterrupt is invoked by the interrupt gate entrypoints to route +// an incoming interrupt to the selected handler. +// +// Callers MUST ensure that the stack has the following layout before calling +// dispatchInterrupt: +// +// |-----------------| <=== SP after jumping to dispatchInterrupt +// | handler address | <- pushed by the interrupt entry code +// |-----------------| +// | exception code | <- pushed by CPU or a dummy code pushed by the gate entry +// |-----------------| +// | RIP | <- pushed by CPU (exception frame) +// | CS | +// | RFLAGS | +// | RSP | +// | SS | +// |-----------------| +// +// Once the handler returns, the GP regs are restored and the stack is unwinded +// so that the CPU can resume excecution of the code that triggered the +// interrupt. +// +// Interrupts are automatically disabled by the CPU upon entry and re-enabled +// when this function returns. +//--------------------------- ----------------------------------------- +TEXT ·dispatchInterrupt(SB),NOSPLIT,$0 + // Save GP regs. The push order MUST match the field layout in the + // Registers struct. + XCHGQ R15, 0(SP) // Swap handler address on stack with R15 contents + PUSHQ R14 + PUSHQ R13 + PUSHQ R12 + PUSHQ R11 + PUSHQ R10 + PUSHQ R9 + PUSHQ R8 + PUSHQ BP + PUSHQ DI + PUSHQ SI + PUSHQ DX + PUSHQ CX + PUSHQ BX + PUSHQ AX + + // Save XMM regs; the amd64 Go runtime uses SSE instructions to implement + // functionality such as memmove which may trigger page faults (e.g + // when resizing a slice and copying the data to the new location). As + // the registered handler may clobber the xmm regs we need to save them + // here and restore them once the handler returns. + SUBQ $16*16, SP + MOVOU X0, 0*16(SP) + MOVOU X1, 1*16(SP) + MOVOU X2, 2*16(SP) + MOVOU X3, 3*16(SP) + MOVOU X4, 4*16(SP) + MOVOU X5, 5*16(SP) + MOVOU X6, 6*16(SP) + MOVOU X7, 7*16(SP) + MOVOU X8, 8*16(SP) + MOVOU X9, 9*16(SP) + MOVOU X10, 10*16(SP) + MOVOU X11, 11*16(SP) + MOVOU X12, 12*16(SP) + MOVOU X13, 13*16(SP) + MOVOU X14, 14*16(SP) + MOVOU X15, 15*16(SP) + + // Setup call stack and invoke handler + MOVQ SP, R14 + ADDQ $16*16, R14 + PUSHQ R14 + CALL R15 + ADDQ $8, SP + + // Restore XMM regs + MOVOU 0*16(SP), X0 + MOVOU 1*16(SP), X1 + MOVOU 2*16(SP), X2 + MOVOU 3*16(SP), X3 + MOVOU 4*16(SP), X4 + MOVOU 5*16(SP), X5 + MOVOU 6*16(SP), X6 + MOVOU 7*16(SP), X7 + MOVOU 8*16(SP), X8 + MOVOU 9*16(SP), X9 + MOVOU 10*16(SP), X10 + MOVOU 11*16(SP), X11 + MOVOU 12*16(SP), X12 + MOVOU 13*16(SP), X13 + MOVOU 14*16(SP), X14 + MOVOU 15*16(SP), X15 + ADDQ $16*16, SP + + // Restore GP regs + POPQ AX + POPQ BX + POPQ CX + POPQ DX + POPQ SI + POPQ DI + POPQ BP + POPQ R8 + POPQ R9 + POPQ R10 + POPQ R11 + POPQ R12 + POPQ R13 + POPQ R14 + POPQ R15 + + // Handler must manually pop the exception (real or dummy) from the stack + // before returning; interrupts will be automatically enabled by the + // CPU upon returning. + ADDQ $8, SP + IRETQ + +// interruptGateEntries contains a list of generated entries for each possible +// interrupt number. Depending on the +TEXT ·interruptGateEntries(SB),NOSPLIT,$0 + // For a list of gate numbers that push an error code see: http://wiki.osdev.org/Exceptions + INT_ENTRY_WITHOUT_CODE(0) INT_ENTRY_WITHOUT_CODE(1) INT_ENTRY_WITHOUT_CODE(2) INT_ENTRY_WITHOUT_CODE(3) INT_ENTRY_WITHOUT_CODE(4) INT_ENTRY_WITHOUT_CODE(5) INT_ENTRY_WITHOUT_CODE(6) INT_ENTRY_WITHOUT_CODE(7) + INT_ENTRY_WITH_CODE(8) + INT_ENTRY_WITHOUT_CODE(9) + INT_ENTRY_WITH_CODE(10) INT_ENTRY_WITH_CODE(11) INT_ENTRY_WITH_CODE(12) INT_ENTRY_WITH_CODE(13) INT_ENTRY_WITH_CODE(14) + INT_ENTRY_WITHOUT_CODE(15) INT_ENTRY_WITHOUT_CODE(16) + INT_ENTRY_WITH_CODE(17) + INT_ENTRY_WITHOUT_CODE(18) INT_ENTRY_WITHOUT_CODE(19) INT_ENTRY_WITHOUT_CODE(20) INT_ENTRY_WITHOUT_CODE(21) INT_ENTRY_WITHOUT_CODE(22) INT_ENTRY_WITHOUT_CODE(23) INT_ENTRY_WITHOUT_CODE(24) INT_ENTRY_WITHOUT_CODE(25) INT_ENTRY_WITHOUT_CODE(26) INT_ENTRY_WITHOUT_CODE(27) INT_ENTRY_WITHOUT_CODE(28) INT_ENTRY_WITHOUT_CODE(29) + INT_ENTRY_WITH_CODE(30) + INT_ENTRY_WITHOUT_CODE(31) INT_ENTRY_WITHOUT_CODE(32) INT_ENTRY_WITHOUT_CODE(33) INT_ENTRY_WITHOUT_CODE(34) INT_ENTRY_WITHOUT_CODE(35) INT_ENTRY_WITHOUT_CODE(36) INT_ENTRY_WITHOUT_CODE(37) INT_ENTRY_WITHOUT_CODE(38) INT_ENTRY_WITHOUT_CODE(39) INT_ENTRY_WITHOUT_CODE(40) INT_ENTRY_WITHOUT_CODE(41) INT_ENTRY_WITHOUT_CODE(42) + INT_ENTRY_WITHOUT_CODE(43) INT_ENTRY_WITHOUT_CODE(44) INT_ENTRY_WITHOUT_CODE(45) INT_ENTRY_WITHOUT_CODE(46) INT_ENTRY_WITHOUT_CODE(47) INT_ENTRY_WITHOUT_CODE(48) INT_ENTRY_WITHOUT_CODE(49) INT_ENTRY_WITHOUT_CODE(50) INT_ENTRY_WITHOUT_CODE(51) INT_ENTRY_WITHOUT_CODE(52) INT_ENTRY_WITHOUT_CODE(53) INT_ENTRY_WITHOUT_CODE(54) + INT_ENTRY_WITHOUT_CODE(55) INT_ENTRY_WITHOUT_CODE(56) INT_ENTRY_WITHOUT_CODE(57) INT_ENTRY_WITHOUT_CODE(58) INT_ENTRY_WITHOUT_CODE(59) INT_ENTRY_WITHOUT_CODE(60) INT_ENTRY_WITHOUT_CODE(61) INT_ENTRY_WITHOUT_CODE(62) INT_ENTRY_WITHOUT_CODE(63) INT_ENTRY_WITHOUT_CODE(64) INT_ENTRY_WITHOUT_CODE(65) INT_ENTRY_WITHOUT_CODE(66) + INT_ENTRY_WITHOUT_CODE(67) INT_ENTRY_WITHOUT_CODE(68) INT_ENTRY_WITHOUT_CODE(69) INT_ENTRY_WITHOUT_CODE(70) INT_ENTRY_WITHOUT_CODE(71) INT_ENTRY_WITHOUT_CODE(72) INT_ENTRY_WITHOUT_CODE(73) INT_ENTRY_WITHOUT_CODE(74) INT_ENTRY_WITHOUT_CODE(75) INT_ENTRY_WITHOUT_CODE(76) INT_ENTRY_WITHOUT_CODE(77) INT_ENTRY_WITHOUT_CODE(78) + INT_ENTRY_WITHOUT_CODE(79) INT_ENTRY_WITHOUT_CODE(80) INT_ENTRY_WITHOUT_CODE(81) INT_ENTRY_WITHOUT_CODE(82) INT_ENTRY_WITHOUT_CODE(83) INT_ENTRY_WITHOUT_CODE(84) INT_ENTRY_WITHOUT_CODE(85) INT_ENTRY_WITHOUT_CODE(86) INT_ENTRY_WITHOUT_CODE(87) INT_ENTRY_WITHOUT_CODE(88) INT_ENTRY_WITHOUT_CODE(89) INT_ENTRY_WITHOUT_CODE(90) + INT_ENTRY_WITHOUT_CODE(91) INT_ENTRY_WITHOUT_CODE(92) INT_ENTRY_WITHOUT_CODE(93) INT_ENTRY_WITHOUT_CODE(94) INT_ENTRY_WITHOUT_CODE(95) INT_ENTRY_WITHOUT_CODE(96) INT_ENTRY_WITHOUT_CODE(97) INT_ENTRY_WITHOUT_CODE(98) INT_ENTRY_WITHOUT_CODE(99) INT_ENTRY_WITHOUT_CODE(100) INT_ENTRY_WITHOUT_CODE(101) INT_ENTRY_WITHOUT_CODE(102) + INT_ENTRY_WITHOUT_CODE(103) INT_ENTRY_WITHOUT_CODE(104) INT_ENTRY_WITHOUT_CODE(105) INT_ENTRY_WITHOUT_CODE(106) INT_ENTRY_WITHOUT_CODE(107) INT_ENTRY_WITHOUT_CODE(108) INT_ENTRY_WITHOUT_CODE(109) INT_ENTRY_WITHOUT_CODE(110) INT_ENTRY_WITHOUT_CODE(111) INT_ENTRY_WITHOUT_CODE(112) INT_ENTRY_WITHOUT_CODE(113) INT_ENTRY_WITHOUT_CODE(114) + INT_ENTRY_WITHOUT_CODE(115) INT_ENTRY_WITHOUT_CODE(116) INT_ENTRY_WITHOUT_CODE(117) INT_ENTRY_WITHOUT_CODE(118) INT_ENTRY_WITHOUT_CODE(119) INT_ENTRY_WITHOUT_CODE(120) INT_ENTRY_WITHOUT_CODE(121) INT_ENTRY_WITHOUT_CODE(122) INT_ENTRY_WITHOUT_CODE(123) INT_ENTRY_WITHOUT_CODE(124) INT_ENTRY_WITHOUT_CODE(125) INT_ENTRY_WITHOUT_CODE(126) + INT_ENTRY_WITHOUT_CODE(127) INT_ENTRY_WITHOUT_CODE(128) INT_ENTRY_WITHOUT_CODE(129) INT_ENTRY_WITHOUT_CODE(130) INT_ENTRY_WITHOUT_CODE(131) INT_ENTRY_WITHOUT_CODE(132) INT_ENTRY_WITHOUT_CODE(133) INT_ENTRY_WITHOUT_CODE(134) INT_ENTRY_WITHOUT_CODE(135) INT_ENTRY_WITHOUT_CODE(136) INT_ENTRY_WITHOUT_CODE(137) INT_ENTRY_WITHOUT_CODE(138) + INT_ENTRY_WITHOUT_CODE(139) INT_ENTRY_WITHOUT_CODE(140) INT_ENTRY_WITHOUT_CODE(141) INT_ENTRY_WITHOUT_CODE(142) INT_ENTRY_WITHOUT_CODE(143) INT_ENTRY_WITHOUT_CODE(144) INT_ENTRY_WITHOUT_CODE(145) INT_ENTRY_WITHOUT_CODE(146) INT_ENTRY_WITHOUT_CODE(147) INT_ENTRY_WITHOUT_CODE(148) INT_ENTRY_WITHOUT_CODE(149) INT_ENTRY_WITHOUT_CODE(150) + INT_ENTRY_WITHOUT_CODE(151) INT_ENTRY_WITHOUT_CODE(152) INT_ENTRY_WITHOUT_CODE(153) INT_ENTRY_WITHOUT_CODE(154) INT_ENTRY_WITHOUT_CODE(155) INT_ENTRY_WITHOUT_CODE(156) INT_ENTRY_WITHOUT_CODE(157) INT_ENTRY_WITHOUT_CODE(158) INT_ENTRY_WITHOUT_CODE(159) INT_ENTRY_WITHOUT_CODE(160) INT_ENTRY_WITHOUT_CODE(161) INT_ENTRY_WITHOUT_CODE(162) + INT_ENTRY_WITHOUT_CODE(163) INT_ENTRY_WITHOUT_CODE(164) INT_ENTRY_WITHOUT_CODE(165) INT_ENTRY_WITHOUT_CODE(166) INT_ENTRY_WITHOUT_CODE(167) INT_ENTRY_WITHOUT_CODE(168) INT_ENTRY_WITHOUT_CODE(169) INT_ENTRY_WITHOUT_CODE(170) INT_ENTRY_WITHOUT_CODE(171) INT_ENTRY_WITHOUT_CODE(172) INT_ENTRY_WITHOUT_CODE(173) INT_ENTRY_WITHOUT_CODE(174) + INT_ENTRY_WITHOUT_CODE(175) INT_ENTRY_WITHOUT_CODE(176) INT_ENTRY_WITHOUT_CODE(177) INT_ENTRY_WITHOUT_CODE(178) INT_ENTRY_WITHOUT_CODE(179) INT_ENTRY_WITHOUT_CODE(180) INT_ENTRY_WITHOUT_CODE(181) INT_ENTRY_WITHOUT_CODE(182) INT_ENTRY_WITHOUT_CODE(183) INT_ENTRY_WITHOUT_CODE(184) INT_ENTRY_WITHOUT_CODE(185) INT_ENTRY_WITHOUT_CODE(186) + INT_ENTRY_WITHOUT_CODE(187) INT_ENTRY_WITHOUT_CODE(188) INT_ENTRY_WITHOUT_CODE(189) INT_ENTRY_WITHOUT_CODE(190) INT_ENTRY_WITHOUT_CODE(191) INT_ENTRY_WITHOUT_CODE(192) INT_ENTRY_WITHOUT_CODE(193) INT_ENTRY_WITHOUT_CODE(194) INT_ENTRY_WITHOUT_CODE(195) INT_ENTRY_WITHOUT_CODE(196) INT_ENTRY_WITHOUT_CODE(197) INT_ENTRY_WITHOUT_CODE(198) + INT_ENTRY_WITHOUT_CODE(199) INT_ENTRY_WITHOUT_CODE(200) INT_ENTRY_WITHOUT_CODE(201) INT_ENTRY_WITHOUT_CODE(202) INT_ENTRY_WITHOUT_CODE(203) INT_ENTRY_WITHOUT_CODE(204) INT_ENTRY_WITHOUT_CODE(205) INT_ENTRY_WITHOUT_CODE(206) INT_ENTRY_WITHOUT_CODE(207) INT_ENTRY_WITHOUT_CODE(208) INT_ENTRY_WITHOUT_CODE(209) INT_ENTRY_WITHOUT_CODE(210) + INT_ENTRY_WITHOUT_CODE(211) INT_ENTRY_WITHOUT_CODE(212) INT_ENTRY_WITHOUT_CODE(213) INT_ENTRY_WITHOUT_CODE(214) INT_ENTRY_WITHOUT_CODE(215) INT_ENTRY_WITHOUT_CODE(216) INT_ENTRY_WITHOUT_CODE(217) INT_ENTRY_WITHOUT_CODE(218) INT_ENTRY_WITHOUT_CODE(219) INT_ENTRY_WITHOUT_CODE(220) INT_ENTRY_WITHOUT_CODE(221) INT_ENTRY_WITHOUT_CODE(222) + INT_ENTRY_WITHOUT_CODE(223) INT_ENTRY_WITHOUT_CODE(224) INT_ENTRY_WITHOUT_CODE(225) INT_ENTRY_WITHOUT_CODE(226) INT_ENTRY_WITHOUT_CODE(227) INT_ENTRY_WITHOUT_CODE(228) INT_ENTRY_WITHOUT_CODE(229) INT_ENTRY_WITHOUT_CODE(230) INT_ENTRY_WITHOUT_CODE(231) INT_ENTRY_WITHOUT_CODE(232) INT_ENTRY_WITHOUT_CODE(233) INT_ENTRY_WITHOUT_CODE(234) + INT_ENTRY_WITHOUT_CODE(235) INT_ENTRY_WITHOUT_CODE(236) INT_ENTRY_WITHOUT_CODE(237) INT_ENTRY_WITHOUT_CODE(238) INT_ENTRY_WITHOUT_CODE(239) INT_ENTRY_WITHOUT_CODE(240) INT_ENTRY_WITHOUT_CODE(241) INT_ENTRY_WITHOUT_CODE(242) INT_ENTRY_WITHOUT_CODE(243) INT_ENTRY_WITHOUT_CODE(244) INT_ENTRY_WITHOUT_CODE(245) INT_ENTRY_WITHOUT_CODE(246) + INT_ENTRY_WITHOUT_CODE(247) INT_ENTRY_WITHOUT_CODE(248) INT_ENTRY_WITHOUT_CODE(249) INT_ENTRY_WITHOUT_CODE(250) INT_ENTRY_WITHOUT_CODE(251) INT_ENTRY_WITHOUT_CODE(252) INT_ENTRY_WITHOUT_CODE(253) INT_ENTRY_WITHOUT_CODE(254) INT_ENTRY_WITHOUT_CODE(255) + RET + diff --git a/src/gopheros/kernel/irq/handler_amd64.go b/src/gopheros/kernel/irq/handler_amd64.go deleted file mode 100644 index b289ab7..0000000 --- a/src/gopheros/kernel/irq/handler_amd64.go +++ /dev/null @@ -1,41 +0,0 @@ -package irq - -// ExceptionNum defines an exception number that can be -// passed to the HandleException and HandleExceptionWithCode -// functions. -type ExceptionNum uint8 - -const ( - // DoubleFault occurs when an exception is unhandled - // or when an exception occurs while the CPU is - // trying to call an exception handler. - DoubleFault = ExceptionNum(8) - - // GPFException is raised when a general protection fault occurs. - GPFException = ExceptionNum(13) - - // PageFaultException is raised when a PDT or - // PDT-entry is not present or when a privilege - // and/or RW protection check fails. - PageFaultException = ExceptionNum(14) -) - -// ExceptionHandler is a function that handles an exception that does not push -// an error code to the stack. If the handler returns, any modifications to the -// supplied Frame and/or Regs pointers will be propagated back to the location -// where the exception occurred. -type ExceptionHandler func(*Frame, *Regs) - -// ExceptionHandlerWithCode is a function that handles an exception that pushes -// an error code to the stack. If the handler returns, any modifications to the -// supplied Frame and/or Regs pointers will be propagated back to the location -// where the exception occurred. -type ExceptionHandlerWithCode func(uint64, *Frame, *Regs) - -// HandleException registers an exception handler (without an error code) for -// the given interrupt number. -func HandleException(exceptionNum ExceptionNum, handler ExceptionHandler) - -// HandleExceptionWithCode registers an exception handler (with an error code) -// for the given interrupt number. -func HandleExceptionWithCode(exceptionNum ExceptionNum, handler ExceptionHandlerWithCode) diff --git a/src/gopheros/kernel/irq/handler_amd64.s b/src/gopheros/kernel/irq/handler_amd64.s deleted file mode 100644 index f2aa3a9..0000000 --- a/src/gopheros/kernel/irq/handler_amd64.s +++ /dev/null @@ -1,36 +0,0 @@ -#include "textflag.h" - -// The maximum number of interrupt handlers is 256 so we need to allocate space -// for 256 x 8-byte pointers. This symbol is made global by the Makefile so it -// can be accessed by the gate entries defined in the rt0 assembly code. -GLOBL _rt0_interrupt_handlers(SB), NOPTR, $2048 - -// In 64-bit mode SIDT stores 8+2 bytes for the IDT address and limit -GLOBL _rt0_idtr<>(SB), NOPTR, $10 - -TEXT ·HandleException(SB),NOSPLIT,$0 - JMP ·HandleExceptionWithCode(SB) - RET - -TEXT ·HandleExceptionWithCode(SB),NOSPLIT,$0 - // Install the handler address in _rt0_interrupt_handlers - LEAQ _rt0_interrupt_handlers+0(SB), CX - MOVBQZX exceptionNum+0(FP), AX // exceptionNum is a uint8 so we zero-extend it to 64bits - MOVQ handler+8(FP), BX - MOVQ 0(BX), BX // dereference pointer to handler fn - MOVQ BX, (CX)(AX*8) - - // To enable the handler we need to lookup the appropriate IDT entry - // and modify its type/attribute byte. To acquire the IDT base address - // we use the SIDT instruction. - MOVQ IDTR, _rt0_idtr<>+0(SB) - LEAQ _rt0_idtr<>(SB), CX - MOVQ 2(CX), CX // CX points to IDT base address - SHLQ $4, AX // Each IDT entry uses 16 bytes so we multiply num by 16 - ADDQ AX, CX // and add it to CX to get the address of the IDT entry - // we want to tweak - - MOVB $0x8e, 5(CX) // 32/64-bit ring-0 interrupt gate that is present - // see: http://wiki.osdev.org/Interrupt_Descriptor_Table - - RET diff --git a/src/gopheros/kernel/irq/interrupt_amd64.go b/src/gopheros/kernel/irq/interrupt_amd64.go deleted file mode 100644 index ef92ab1..0000000 --- a/src/gopheros/kernel/irq/interrupt_amd64.go +++ /dev/null @@ -1,51 +0,0 @@ -package irq - -import "gopheros/kernel/kfmt" - -// Regs contains a snapshot of the register values when an interrupt occurred. -type Regs struct { - RAX uint64 - RBX uint64 - RCX uint64 - RDX uint64 - RSI uint64 - RDI uint64 - RBP uint64 - R8 uint64 - R9 uint64 - R10 uint64 - R11 uint64 - R12 uint64 - R13 uint64 - R14 uint64 - R15 uint64 -} - -// Print outputs a dump of the register values to the active console. -func (r *Regs) Print() { - kfmt.Printf("RAX = %16x RBX = %16x\n", r.RAX, r.RBX) - kfmt.Printf("RCX = %16x RDX = %16x\n", r.RCX, r.RDX) - kfmt.Printf("RSI = %16x RDI = %16x\n", r.RSI, r.RDI) - kfmt.Printf("RBP = %16x\n", r.RBP) - kfmt.Printf("R8 = %16x R9 = %16x\n", r.R8, r.R9) - kfmt.Printf("R10 = %16x R11 = %16x\n", r.R10, r.R11) - kfmt.Printf("R12 = %16x R13 = %16x\n", r.R12, r.R13) - kfmt.Printf("R14 = %16x R15 = %16x\n", r.R14, r.R15) -} - -// Frame describes an exception frame that is automatically pushed by the CPU -// to the stack when an exception occurs. -type Frame struct { - RIP uint64 - CS uint64 - RFlags uint64 - RSP uint64 - SS uint64 -} - -// Print outputs a dump of the exception frame to the active console. -func (f *Frame) Print() { - kfmt.Printf("RIP = %16x CS = %16x\n", f.RIP, f.CS) - kfmt.Printf("RSP = %16x SS = %16x\n", f.RSP, f.SS) - kfmt.Printf("RFL = %16x\n", f.RFlags) -} diff --git a/src/gopheros/kernel/irq/interrupt_amd64_test.go b/src/gopheros/kernel/irq/interrupt_amd64_test.go deleted file mode 100644 index 79bb5a2..0000000 --- a/src/gopheros/kernel/irq/interrupt_amd64_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package irq - -import ( - "bytes" - "gopheros/kernel/kfmt" - "testing" -) - -func TestRegsPrint(t *testing.T) { - defer func() { - kfmt.SetOutputSink(nil) - }() - var buf bytes.Buffer - - regs := Regs{ - RAX: 1, - RBX: 2, - RCX: 3, - RDX: 4, - RSI: 5, - RDI: 6, - RBP: 7, - R8: 8, - R9: 9, - R10: 10, - R11: 11, - R12: 12, - R13: 13, - R14: 14, - R15: 15, - } - regs.Print() - - exp := "RAX = 0000000000000001 RBX = 0000000000000002\nRCX = 0000000000000003 RDX = 0000000000000004\nRSI = 0000000000000005 RDI = 0000000000000006\nRBP = 0000000000000007\nR8 = 0000000000000008 R9 = 0000000000000009\nR10 = 000000000000000a R11 = 000000000000000b\nR12 = 000000000000000c R13 = 000000000000000d\nR14 = 000000000000000e R15 = 000000000000000f\n" - - kfmt.SetOutputSink(&buf) - if got := buf.String(); got != exp { - t.Fatalf("expected to get:\n%q\ngot:\n%q", exp, got) - } -} - -func TestFramePrint(t *testing.T) { - defer func() { - kfmt.SetOutputSink(nil) - }() - var buf bytes.Buffer - - frame := Frame{ - RIP: 1, - CS: 2, - RFlags: 3, - RSP: 4, - SS: 5, - } - frame.Print() - - exp := "RIP = 0000000000000001 CS = 0000000000000002\nRSP = 0000000000000004 SS = 0000000000000005\nRFL = 0000000000000003\n" - - kfmt.SetOutputSink(&buf) - if got := buf.String(); got != exp { - t.Fatalf("expected to get:\n%q\ngot:\n%q", exp, got) - } - -} diff --git a/src/gopheros/kernel/kmain/kmain.go b/src/gopheros/kernel/kmain/kmain.go index 4d9d127..9f63028 100644 --- a/src/gopheros/kernel/kmain/kmain.go +++ b/src/gopheros/kernel/kmain/kmain.go @@ -2,6 +2,7 @@ package kmain import ( "gopheros/kernel" + "gopheros/kernel/gate" "gopheros/kernel/goruntime" "gopheros/kernel/hal" "gopheros/kernel/kfmt" @@ -31,6 +32,7 @@ func Kmain(multibootInfoPtr, kernelStart, kernelEnd, kernelPageOffset uintptr) { multiboot.SetInfoPtr(multibootInfoPtr) var err *kernel.Error + gate.Init() if err = pmm.Init(kernelStart, kernelEnd); err != nil { panic(err) } else if err = vmm.Init(kernelPageOffset); err != nil { diff --git a/src/gopheros/kernel/mm/vmm/fault.go b/src/gopheros/kernel/mm/vmm/fault_amd64.go similarity index 63% rename from src/gopheros/kernel/mm/vmm/fault.go rename to src/gopheros/kernel/mm/vmm/fault_amd64.go index 630d3ac..7ed65a8 100644 --- a/src/gopheros/kernel/mm/vmm/fault.go +++ b/src/gopheros/kernel/mm/vmm/fault_amd64.go @@ -2,12 +2,24 @@ package vmm import ( "gopheros/kernel" - "gopheros/kernel/irq" + "gopheros/kernel/gate" "gopheros/kernel/kfmt" "gopheros/kernel/mm" ) -func pageFaultHandler(errorCode uint64, frame *irq.Frame, regs *irq.Regs) { +var ( + // handleInterruptFn is used by tests. + handleInterruptFn = gate.HandleInterrupt +) + +func installFaultHandlers() { + handleInterruptFn(gate.PageFaultException, 0, pageFaultHandler) + handleInterruptFn(gate.GPFException, 0, generalProtectionFaultHandler) +} + +// pageFaultHandler is invoked when a PDT or PDT-entry is not present or when a +// RW protection check fails. +func pageFaultHandler(regs *gate.Registers) { var ( faultAddress = uintptr(readCR2Fn()) faultPage = mm.PageFromAddress(faultAddress) @@ -35,9 +47,9 @@ func pageFaultHandler(errorCode uint64, frame *irq.Frame, regs *irq.Regs) { ) if copy, err = mm.AllocFrame(); err != nil { - nonRecoverablePageFault(faultAddress, errorCode, frame, regs, err) + nonRecoverablePageFault(faultAddress, regs, err) } else if tmpPage, err = mapTemporaryFn(copy); err != nil { - nonRecoverablePageFault(faultAddress, errorCode, frame, regs, err) + nonRecoverablePageFault(faultAddress, regs, err) } else { // Copy page contents, mark as RW and remove CoW flag kernel.Memcopy(faultPage.Address(), tmpPage.Address(), mm.PageSize) @@ -55,44 +67,46 @@ func pageFaultHandler(errorCode uint64, frame *irq.Frame, regs *irq.Regs) { } } - nonRecoverablePageFault(faultAddress, errorCode, frame, regs, errUnrecoverableFault) + nonRecoverablePageFault(faultAddress, regs, errUnrecoverableFault) } -func nonRecoverablePageFault(faultAddress uintptr, errorCode uint64, frame *irq.Frame, regs *irq.Regs, err *kernel.Error) { +// generalProtectionFaultHandler is invoked for various reasons: +// - segment errors (privilege, type or limit violations) +// - executing privileged instructions outside ring-0 +// - attempts to access reserved or unimplemented CPU registers +func generalProtectionFaultHandler(regs *gate.Registers) { + kfmt.Printf("\nGeneral protection fault while accessing address: 0x%x\n", readCR2Fn()) + kfmt.Printf("Registers:\n") + regs.DumpTo(kfmt.GetOutputSink()) + + // TODO: Revisit this when user-mode tasks are implemented + panic(errUnrecoverableFault) +} + +func nonRecoverablePageFault(faultAddress uintptr, regs *gate.Registers, err *kernel.Error) { kfmt.Printf("\nPage fault while accessing address: 0x%16x\nReason: ", faultAddress) switch { - case errorCode == 0: + case regs.Info == 0: kfmt.Printf("read from non-present page") - case errorCode == 1: + case regs.Info == 1: kfmt.Printf("page protection violation (read)") - case errorCode == 2: + case regs.Info == 2: kfmt.Printf("write to non-present page") - case errorCode == 3: + case regs.Info == 3: kfmt.Printf("page protection violation (write)") - case errorCode == 4: + case regs.Info == 4: kfmt.Printf("page-fault in user-mode") - case errorCode == 8: + case regs.Info == 8: kfmt.Printf("page table has reserved bit set") - case errorCode == 16: + case regs.Info == 16: kfmt.Printf("instruction fetch") default: kfmt.Printf("unknown") } kfmt.Printf("\n\nRegisters:\n") - regs.Print() - frame.Print() + regs.DumpTo(kfmt.GetOutputSink()) // TODO: Revisit this when user-mode tasks are implemented panic(err) } - -func generalProtectionFaultHandler(_ uint64, frame *irq.Frame, regs *irq.Regs) { - kfmt.Printf("\nGeneral protection fault while accessing address: 0x%x\n", readCR2Fn()) - kfmt.Printf("Registers:\n") - regs.Print() - frame.Print() - - // TODO: Revisit this when user-mode tasks are implemented - panic(errUnrecoverableFault) -} diff --git a/src/gopheros/kernel/mm/vmm/fault_test.go b/src/gopheros/kernel/mm/vmm/fault_amd64_test.go similarity index 91% rename from src/gopheros/kernel/mm/vmm/fault_test.go rename to src/gopheros/kernel/mm/vmm/fault_amd64_test.go index e7589bb..3fd6334 100644 --- a/src/gopheros/kernel/mm/vmm/fault_test.go +++ b/src/gopheros/kernel/mm/vmm/fault_amd64_test.go @@ -5,7 +5,7 @@ import ( "fmt" "gopheros/kernel" "gopheros/kernel/cpu" - "gopheros/kernel/irq" + "gopheros/kernel/gate" "gopheros/kernel/kfmt" "gopheros/kernel/mm" "strings" @@ -15,8 +15,7 @@ import ( func TestRecoverablePageFault(t *testing.T) { var ( - frame irq.Frame - regs irq.Regs + regs gate.Registers pageEntry pageTableEntry origPage = make([]byte, mm.PageSize) clonedPage = make([]byte, mm.PageSize) @@ -91,7 +90,8 @@ func TestRecoverablePageFault(t *testing.T) { pageEntry = 0 pageEntry.SetFlags(spec.pteFlags) - pageFaultHandler(2, &frame, ®s) + regs.Info = 2 + pageFaultHandler(®s) }) } @@ -141,9 +141,8 @@ func TestNonRecoverablePageFault(t *testing.T) { } var ( - regs irq.Regs - frame irq.Frame - buf bytes.Buffer + regs gate.Registers + buf bytes.Buffer ) kfmt.SetOutputSink(&buf) @@ -156,7 +155,8 @@ func TestNonRecoverablePageFault(t *testing.T) { } }() - nonRecoverablePageFault(0xbadf00d000, spec.errCode, &frame, ®s, errUnrecoverableFault) + regs.Info = spec.errCode + nonRecoverablePageFault(0xbadf00d000, ®s, errUnrecoverableFault) if got := buf.String(); !strings.Contains(got, spec.expReason) { t.Errorf("expected reason %q; got output:\n%q", spec.expReason, got) } @@ -169,10 +169,7 @@ func TestGPFHandler(t *testing.T) { readCR2Fn = cpu.ReadCR2 }() - var ( - regs irq.Regs - frame irq.Frame - ) + var regs gate.Registers readCR2Fn = func() uint64 { return 0xbadf00d000 @@ -184,5 +181,5 @@ func TestGPFHandler(t *testing.T) { } }() - generalProtectionFaultHandler(0, &frame, ®s) + generalProtectionFaultHandler(®s) } diff --git a/src/gopheros/kernel/mm/vmm/vmm.go b/src/gopheros/kernel/mm/vmm/vmm.go index c6f40ad..e88645d 100644 --- a/src/gopheros/kernel/mm/vmm/vmm.go +++ b/src/gopheros/kernel/mm/vmm/vmm.go @@ -3,16 +3,14 @@ package vmm import ( "gopheros/kernel" "gopheros/kernel/cpu" - "gopheros/kernel/irq" "gopheros/kernel/mm" ) var ( // the following functions are mocked by tests and are automatically // inlined by the compiler. - handleExceptionWithCodeFn = irq.HandleExceptionWithCode - readCR2Fn = cpu.ReadCR2 - translateFn = Translate + readCR2Fn = cpu.ReadCR2 + translateFn = Translate errUnrecoverableFault = &kernel.Error{Module: "vmm", Message: "page/gpf fault"} ) @@ -24,13 +22,10 @@ func Init(kernelPageOffset uintptr) *kernel.Error { return err } - if err := reserveZeroedFrame(); err != nil { - return err - } + // Install arch-specific handlers for vmm-related faults. + installFaultHandlers() - handleExceptionWithCodeFn(irq.PageFaultException, pageFaultHandler) - handleExceptionWithCodeFn(irq.GPFException, generalProtectionFaultHandler) - return nil + return reserveZeroedFrame() } // reserveZeroedFrame reserves a physical frame to be used together with diff --git a/src/gopheros/kernel/mm/vmm/vmm_test.go b/src/gopheros/kernel/mm/vmm/vmm_test.go index 2cefdf2..6f39735 100644 --- a/src/gopheros/kernel/mm/vmm/vmm_test.go +++ b/src/gopheros/kernel/mm/vmm/vmm_test.go @@ -3,7 +3,7 @@ package vmm import ( "gopheros/kernel" "gopheros/kernel/cpu" - "gopheros/kernel/irq" + "gopheros/kernel/gate" "gopheros/kernel/mm" "gopheros/multiboot" "testing" @@ -18,7 +18,7 @@ func TestInit(t *testing.T) { translateFn = Translate mapTemporaryFn = MapTemporary unmapFn = Unmap - handleExceptionWithCodeFn = irq.HandleExceptionWithCode + handleInterruptFn = gate.HandleInterrupt }() // reserve space for an allocated page @@ -42,7 +42,7 @@ func TestInit(t *testing.T) { switchPDTFn = func(_ uintptr) {} unmapFn = func(p mm.Page) *kernel.Error { return nil } mapTemporaryFn = func(f mm.Frame) (mm.Page, *kernel.Error) { return mm.Page(f), nil } - handleExceptionWithCodeFn = func(_ irq.ExceptionNum, _ irq.ExceptionHandlerWithCode) {} + handleInterruptFn = func(_ gate.InterruptNumber, _ uint8, _ func(*gate.Registers)) {} if err := Init(0); err != nil { t.Fatal(err) @@ -92,7 +92,7 @@ func TestInit(t *testing.T) { switchPDTFn = func(_ uintptr) {} unmapFn = func(p mm.Page) *kernel.Error { return nil } mapTemporaryFn = func(f mm.Frame) (mm.Page, *kernel.Error) { return mm.Page(f), nil } - handleExceptionWithCodeFn = func(_ irq.ExceptionNum, _ irq.ExceptionHandlerWithCode) {} + handleInterruptFn = func(_ gate.InterruptNumber, _ uint8, _ func(*gate.Registers)) {} if err := Init(0); err != expErr { t.Fatalf("expected error: %v; got %v", expErr, err) @@ -112,7 +112,7 @@ func TestInit(t *testing.T) { switchPDTFn = func(_ uintptr) {} unmapFn = func(p mm.Page) *kernel.Error { return nil } mapTemporaryFn = func(f mm.Frame) (mm.Page, *kernel.Error) { return mm.Page(f), expErr } - handleExceptionWithCodeFn = func(_ irq.ExceptionNum, _ irq.ExceptionHandlerWithCode) {} + handleInterruptFn = func(_ gate.InterruptNumber, _ uint8, _ func(*gate.Registers)) {} if err := Init(0); err != expErr { t.Fatalf("expected error: %v; got %v", expErr, err)