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:
commit
0b1429c115
6
Makefile
6
Makefile
@ -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:
|
||||||
|
@ -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
|
||||||
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
||||||
|
153
src/gopheros/kernel/gate/gate_amd64.go
Normal file
153
src/gopheros/kernel/gate/gate_amd64.go
Normal 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()
|
301
src/gopheros/kernel/gate/gate_amd64.s
Normal file
301
src/gopheros/kernel/gate/gate_amd64.s
Normal 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
|
||||||
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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 {
|
||||||
|
@ -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)
|
|
||||||
}
|
|
@ -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, ®s)
|
regs.Info = 2
|
||||||
|
pageFaultHandler(®s)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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, ®s, errUnrecoverableFault)
|
regs.Info = spec.errCode
|
||||||
|
nonRecoverablePageFault(0xbadf00d000, ®s, 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, ®s)
|
generalProtectionFaultHandler(®s)
|
||||||
}
|
}
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user