mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
Define and implement API for registering exception handlers
This commit is contained in:
parent
5a2efb2bd3
commit
9adde8f5c1
1
Makefile
1
Makefile
@ -59,6 +59,7 @@ go.o:
|
||||
@echo "[objcopy] creating global symbol alias 'kernel.Kmain' for 'github.com/achilleasa/gopher-os/kernel.Kmain' in go.o"
|
||||
@objcopy \
|
||||
--add-symbol kernel.Kmain=.text:0x`nm $(BUILD_DIR)/go.o | grep "kmain.Kmain$$" | cut -d' ' -f1` \
|
||||
--globalize-symbol _rt0_interrupt_handlers \
|
||||
$(BUILD_DIR)/go.o $(BUILD_DIR)/go.o
|
||||
|
||||
binutils_version_check:
|
||||
|
41
kernel/irq/handler_amd64.go
Normal file
41
kernel/irq/handler_amd64.go
Normal file
@ -0,0 +1,41 @@
|
||||
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)
|
36
kernel/irq/handler_amd64.s
Normal file
36
kernel/irq/handler_amd64.s
Normal file
@ -0,0 +1,36 @@
|
||||
#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
|
51
kernel/irq/interrupt_amd64.go
Normal file
51
kernel/irq/interrupt_amd64.go
Normal file
@ -0,0 +1,51 @@
|
||||
package irq
|
||||
|
||||
import "github.com/achilleasa/gopher-os/kernel/kfmt/early"
|
||||
|
||||
// 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() {
|
||||
early.Printf("RAX = %16x RBX = %16x\n", r.RAX, r.RBX)
|
||||
early.Printf("RCX = %16x RDX = %16x\n", r.RCX, r.RDX)
|
||||
early.Printf("RSI = %16x RDI = %16x\n", r.RSI, r.RDI)
|
||||
early.Printf("RBP = %16x\n", r.RBP)
|
||||
early.Printf("R8 = %16x R9 = %16x\n", r.R8, r.R9)
|
||||
early.Printf("R10 = %16x R11 = %16x\n", r.R10, r.R11)
|
||||
early.Printf("R12 = %16x R13 = %16x\n", r.R12, r.R13)
|
||||
early.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() {
|
||||
early.Printf("RIP = %16x CS = %16x\n", f.RIP, f.CS)
|
||||
early.Printf("RSP = %16x SS = %16x\n", f.RSP, f.SS)
|
||||
early.Printf("RFL = %16x\n", f.RFlags)
|
||||
}
|
84
kernel/irq/interrupt_amd64_test.go
Normal file
84
kernel/irq/interrupt_amd64_test.go
Normal file
@ -0,0 +1,84 @@
|
||||
package irq
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"unsafe"
|
||||
|
||||
"github.com/achilleasa/gopher-os/kernel/driver/video/console"
|
||||
"github.com/achilleasa/gopher-os/kernel/hal"
|
||||
)
|
||||
|
||||
func TestRegsPrint(t *testing.T) {
|
||||
fb := mockTTY()
|
||||
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"
|
||||
|
||||
if got := readTTY(fb); got != exp {
|
||||
t.Fatalf("expected to get:\n%q\ngot:\n%q", exp, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFramePrint(t *testing.T) {
|
||||
fb := mockTTY()
|
||||
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"
|
||||
|
||||
if got := readTTY(fb); got != exp {
|
||||
t.Fatalf("expected to get:\n%q\ngot:\n%q", exp, got)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func readTTY(fb []byte) string {
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(fb); i += 2 {
|
||||
ch := fb[i]
|
||||
if ch == 0 {
|
||||
if i+2 < len(fb) && fb[i+2] != 0 {
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
buf.WriteByte(ch)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func mockTTY() []byte {
|
||||
// Mock a tty to handle early.Printf output
|
||||
mockConsoleFb := make([]byte, 160*25)
|
||||
mockConsole := &console.Ega{}
|
||||
mockConsole.Init(80, 25, uintptr(unsafe.Pointer(&mockConsoleFb[0])))
|
||||
hal.ActiveTerminal.AttachTo(mockConsole)
|
||||
|
||||
return mockConsoleFb
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user