1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00

Merge pull request #69 from achilleasa/refactor-gate-handling

Refactor gate handling
This commit is contained in:
Achilleas Anagnostopoulos 2018-06-01 09:00:33 +01:00 committed by GitHub
commit 0b1429c115
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 518 additions and 526 deletions

View File

@ -75,14 +75,12 @@ go.o:
@# objcopy to make that symbol exportable. Since nasm does not support externs @# objcopy to make that symbol exportable. Since nasm does not support externs
@# with slashes we create a global symbol alias for kernel.Kmain @# with slashes we create a global symbol alias for kernel.Kmain
@echo "[objcopy] create kernel.Kmain alias to gopheros/kernel/kmain.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 \ @objcopy \
--add-symbol kernel.Kmain=.text:0x`nm $(BUILD_DIR)/go.o | grep "kmain.Kmain$$" | cut -d' ' -f1` \ --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.g0 \
--globalize-symbol runtime.m0 \ --globalize-symbol runtime.m0 \
--globalize-symbol runtime.physPageSize \ --globalize-symbol runtime.physPageSize \
--globalize-symbol runtime.useAVXmemmove \
$(BUILD_DIR)/go.o $(BUILD_DIR)/go.o $(BUILD_DIR)/go.o $(BUILD_DIR)/go.o
binutils_version_check: binutils_version_check:
@ -191,6 +189,8 @@ lint: lint-check-deps
--deadline 300s \ --deadline 300s \
--exclude 'possible misuse of unsafe.Pointer' \ --exclude 'possible misuse of unsafe.Pointer' \
--exclude 'x \^ 0 always equals x' \ --exclude 'x \^ 0 always equals x' \
--exclude 'dispatchInterrupt is unused' \
--exclude 'interruptGateEntries is unused' \
src/... src/...
lint-check-deps: lint-check-deps:

View File

@ -6,20 +6,6 @@ bits 64
section .bss section .bss
align 8 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 ; 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, ; (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 ; 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 r0_g_ptr: resq 1
tcb_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 section .text
;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------
@ -49,7 +27,6 @@ section .text
global _rt0_64_entry global _rt0_64_entry
_rt0_64_entry: _rt0_64_entry:
call _rt0_install_redirect_trampolines call _rt0_install_redirect_trampolines
call _rt0_64_load_idt
call _rt0_64_setup_go_runtime_structs call _rt0_64_setup_go_runtime_structs
; Call the kernel entry point passing a pointer to the multiboot data ; Call the kernel entry point passing a pointer to the multiboot data
@ -130,261 +107,6 @@ _rt0_64_setup_go_runtime_structs:
ret 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 ; Error messages
;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -2,6 +2,7 @@ package kmain
import ( import (
"gopheros/kernel" "gopheros/kernel"
"gopheros/kernel/gate"
"gopheros/kernel/goruntime" "gopheros/kernel/goruntime"
"gopheros/kernel/hal" "gopheros/kernel/hal"
"gopheros/kernel/kfmt" "gopheros/kernel/kfmt"
@ -31,6 +32,7 @@ func Kmain(multibootInfoPtr, kernelStart, kernelEnd, kernelPageOffset uintptr) {
multiboot.SetInfoPtr(multibootInfoPtr) multiboot.SetInfoPtr(multibootInfoPtr)
var err *kernel.Error var err *kernel.Error
gate.Init()
if err = pmm.Init(kernelStart, kernelEnd); err != nil { if err = pmm.Init(kernelStart, kernelEnd); err != nil {
panic(err) panic(err)
} else if err = vmm.Init(kernelPageOffset); err != nil { } else if err = vmm.Init(kernelPageOffset); err != nil {

View File

@ -2,12 +2,24 @@ package vmm
import ( import (
"gopheros/kernel" "gopheros/kernel"
"gopheros/kernel/irq" "gopheros/kernel/gate"
"gopheros/kernel/kfmt" "gopheros/kernel/kfmt"
"gopheros/kernel/mm" "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 ( var (
faultAddress = uintptr(readCR2Fn()) faultAddress = uintptr(readCR2Fn())
faultPage = mm.PageFromAddress(faultAddress) 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 { 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 { } else if tmpPage, err = mapTemporaryFn(copy); err != nil {
nonRecoverablePageFault(faultAddress, errorCode, frame, regs, err) nonRecoverablePageFault(faultAddress, regs, err)
} else { } else {
// Copy page contents, mark as RW and remove CoW flag // Copy page contents, mark as RW and remove CoW flag
kernel.Memcopy(faultPage.Address(), tmpPage.Address(), mm.PageSize) 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) kfmt.Printf("\nPage fault while accessing address: 0x%16x\nReason: ", faultAddress)
switch { switch {
case errorCode == 0: case regs.Info == 0:
kfmt.Printf("read from non-present page") kfmt.Printf("read from non-present page")
case errorCode == 1: case regs.Info == 1:
kfmt.Printf("page protection violation (read)") kfmt.Printf("page protection violation (read)")
case errorCode == 2: case regs.Info == 2:
kfmt.Printf("write to non-present page") kfmt.Printf("write to non-present page")
case errorCode == 3: case regs.Info == 3:
kfmt.Printf("page protection violation (write)") kfmt.Printf("page protection violation (write)")
case errorCode == 4: case regs.Info == 4:
kfmt.Printf("page-fault in user-mode") kfmt.Printf("page-fault in user-mode")
case errorCode == 8: case regs.Info == 8:
kfmt.Printf("page table has reserved bit set") kfmt.Printf("page table has reserved bit set")
case errorCode == 16: case regs.Info == 16:
kfmt.Printf("instruction fetch") kfmt.Printf("instruction fetch")
default: default:
kfmt.Printf("unknown") kfmt.Printf("unknown")
} }
kfmt.Printf("\n\nRegisters:\n") kfmt.Printf("\n\nRegisters:\n")
regs.Print() regs.DumpTo(kfmt.GetOutputSink())
frame.Print()
// TODO: Revisit this when user-mode tasks are implemented // TODO: Revisit this when user-mode tasks are implemented
panic(err) 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)
}

View File

@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"gopheros/kernel" "gopheros/kernel"
"gopheros/kernel/cpu" "gopheros/kernel/cpu"
"gopheros/kernel/irq" "gopheros/kernel/gate"
"gopheros/kernel/kfmt" "gopheros/kernel/kfmt"
"gopheros/kernel/mm" "gopheros/kernel/mm"
"strings" "strings"
@ -15,8 +15,7 @@ import (
func TestRecoverablePageFault(t *testing.T) { func TestRecoverablePageFault(t *testing.T) {
var ( var (
frame irq.Frame regs gate.Registers
regs irq.Regs
pageEntry pageTableEntry pageEntry pageTableEntry
origPage = make([]byte, mm.PageSize) origPage = make([]byte, mm.PageSize)
clonedPage = make([]byte, mm.PageSize) clonedPage = make([]byte, mm.PageSize)
@ -91,7 +90,8 @@ func TestRecoverablePageFault(t *testing.T) {
pageEntry = 0 pageEntry = 0
pageEntry.SetFlags(spec.pteFlags) pageEntry.SetFlags(spec.pteFlags)
pageFaultHandler(2, &frame, &regs) regs.Info = 2
pageFaultHandler(&regs)
}) })
} }
@ -141,9 +141,8 @@ func TestNonRecoverablePageFault(t *testing.T) {
} }
var ( var (
regs irq.Regs regs gate.Registers
frame irq.Frame buf bytes.Buffer
buf bytes.Buffer
) )
kfmt.SetOutputSink(&buf) kfmt.SetOutputSink(&buf)
@ -156,7 +155,8 @@ func TestNonRecoverablePageFault(t *testing.T) {
} }
}() }()
nonRecoverablePageFault(0xbadf00d000, spec.errCode, &frame, &regs, errUnrecoverableFault) regs.Info = spec.errCode
nonRecoverablePageFault(0xbadf00d000, &regs, errUnrecoverableFault)
if got := buf.String(); !strings.Contains(got, spec.expReason) { if got := buf.String(); !strings.Contains(got, spec.expReason) {
t.Errorf("expected reason %q; got output:\n%q", spec.expReason, got) t.Errorf("expected reason %q; got output:\n%q", spec.expReason, got)
} }
@ -169,10 +169,7 @@ func TestGPFHandler(t *testing.T) {
readCR2Fn = cpu.ReadCR2 readCR2Fn = cpu.ReadCR2
}() }()
var ( var regs gate.Registers
regs irq.Regs
frame irq.Frame
)
readCR2Fn = func() uint64 { readCR2Fn = func() uint64 {
return 0xbadf00d000 return 0xbadf00d000
@ -184,5 +181,5 @@ func TestGPFHandler(t *testing.T) {
} }
}() }()
generalProtectionFaultHandler(0, &frame, &regs) generalProtectionFaultHandler(&regs)
} }

View File

@ -3,16 +3,14 @@ package vmm
import ( import (
"gopheros/kernel" "gopheros/kernel"
"gopheros/kernel/cpu" "gopheros/kernel/cpu"
"gopheros/kernel/irq"
"gopheros/kernel/mm" "gopheros/kernel/mm"
) )
var ( var (
// the following functions are mocked by tests and are automatically // the following functions are mocked by tests and are automatically
// inlined by the compiler. // inlined by the compiler.
handleExceptionWithCodeFn = irq.HandleExceptionWithCode readCR2Fn = cpu.ReadCR2
readCR2Fn = cpu.ReadCR2 translateFn = Translate
translateFn = Translate
errUnrecoverableFault = &kernel.Error{Module: "vmm", Message: "page/gpf fault"} errUnrecoverableFault = &kernel.Error{Module: "vmm", Message: "page/gpf fault"}
) )
@ -24,13 +22,10 @@ func Init(kernelPageOffset uintptr) *kernel.Error {
return err return err
} }
if err := reserveZeroedFrame(); err != nil { // Install arch-specific handlers for vmm-related faults.
return err installFaultHandlers()
}
handleExceptionWithCodeFn(irq.PageFaultException, pageFaultHandler) return reserveZeroedFrame()
handleExceptionWithCodeFn(irq.GPFException, generalProtectionFaultHandler)
return nil
} }
// reserveZeroedFrame reserves a physical frame to be used together with // reserveZeroedFrame reserves a physical frame to be used together with

View File

@ -3,7 +3,7 @@ package vmm
import ( import (
"gopheros/kernel" "gopheros/kernel"
"gopheros/kernel/cpu" "gopheros/kernel/cpu"
"gopheros/kernel/irq" "gopheros/kernel/gate"
"gopheros/kernel/mm" "gopheros/kernel/mm"
"gopheros/multiboot" "gopheros/multiboot"
"testing" "testing"
@ -18,7 +18,7 @@ func TestInit(t *testing.T) {
translateFn = Translate translateFn = Translate
mapTemporaryFn = MapTemporary mapTemporaryFn = MapTemporary
unmapFn = Unmap unmapFn = Unmap
handleExceptionWithCodeFn = irq.HandleExceptionWithCode handleInterruptFn = gate.HandleInterrupt
}() }()
// reserve space for an allocated page // reserve space for an allocated page
@ -42,7 +42,7 @@ func TestInit(t *testing.T) {
switchPDTFn = func(_ uintptr) {} switchPDTFn = func(_ uintptr) {}
unmapFn = func(p mm.Page) *kernel.Error { return nil } unmapFn = func(p mm.Page) *kernel.Error { return nil }
mapTemporaryFn = func(f mm.Frame) (mm.Page, *kernel.Error) { return mm.Page(f), 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 { if err := Init(0); err != nil {
t.Fatal(err) t.Fatal(err)
@ -92,7 +92,7 @@ func TestInit(t *testing.T) {
switchPDTFn = func(_ uintptr) {} switchPDTFn = func(_ uintptr) {}
unmapFn = func(p mm.Page) *kernel.Error { return nil } unmapFn = func(p mm.Page) *kernel.Error { return nil }
mapTemporaryFn = func(f mm.Frame) (mm.Page, *kernel.Error) { return mm.Page(f), 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 { if err := Init(0); err != expErr {
t.Fatalf("expected error: %v; got %v", expErr, err) t.Fatalf("expected error: %v; got %v", expErr, err)
@ -112,7 +112,7 @@ func TestInit(t *testing.T) {
switchPDTFn = func(_ uintptr) {} switchPDTFn = func(_ uintptr) {}
unmapFn = func(p mm.Page) *kernel.Error { return nil } unmapFn = func(p mm.Page) *kernel.Error { return nil }
mapTemporaryFn = func(f mm.Frame) (mm.Page, *kernel.Error) { return mm.Page(f), expErr } 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 { if err := Init(0); err != expErr {
t.Fatalf("expected error: %v; got %v", expErr, err) t.Fatalf("expected error: %v; got %v", expErr, err)