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

vmm: switch exception handling to use the new gate package

This commit is contained in:
Achilleas Anagnostopoulos 2018-05-31 21:22:29 +01:00
parent 7b12fbd940
commit 7deeab3cbc
4 changed files with 59 additions and 53 deletions

View File

@ -2,12 +2,24 @@ package vmm
import (
"gopheros/kernel"
"gopheros/kernel/irq"
"gopheros/kernel/gate"
"gopheros/kernel/kfmt"
"gopheros/kernel/mm"
)
func pageFaultHandler(errorCode uint64, frame *irq.Frame, regs *irq.Regs) {
var (
// handleInterruptFn is used by tests.
handleInterruptFn = gate.HandleInterrupt
)
func installFaultHandlers() {
handleInterruptFn(gate.PageFaultException, 0, pageFaultHandler)
handleInterruptFn(gate.GPFException, 0, generalProtectionFaultHandler)
}
// pageFaultHandler is invoked when a PDT or PDT-entry is not present or when a
// RW protection check fails.
func pageFaultHandler(regs *gate.Registers) {
var (
faultAddress = uintptr(readCR2Fn())
faultPage = mm.PageFromAddress(faultAddress)
@ -35,9 +47,9 @@ func pageFaultHandler(errorCode uint64, frame *irq.Frame, regs *irq.Regs) {
)
if copy, err = mm.AllocFrame(); err != nil {
nonRecoverablePageFault(faultAddress, errorCode, frame, regs, err)
nonRecoverablePageFault(faultAddress, regs, err)
} else if tmpPage, err = mapTemporaryFn(copy); err != nil {
nonRecoverablePageFault(faultAddress, errorCode, frame, regs, err)
nonRecoverablePageFault(faultAddress, regs, err)
} else {
// Copy page contents, mark as RW and remove CoW flag
kernel.Memcopy(faultPage.Address(), tmpPage.Address(), mm.PageSize)
@ -55,44 +67,46 @@ func pageFaultHandler(errorCode uint64, frame *irq.Frame, regs *irq.Regs) {
}
}
nonRecoverablePageFault(faultAddress, errorCode, frame, regs, errUnrecoverableFault)
nonRecoverablePageFault(faultAddress, regs, errUnrecoverableFault)
}
func nonRecoverablePageFault(faultAddress uintptr, errorCode uint64, frame *irq.Frame, regs *irq.Regs, err *kernel.Error) {
// generalProtectionFaultHandler is invoked for various reasons:
// - segment errors (privilege, type or limit violations)
// - executing privileged instructions outside ring-0
// - attempts to access reserved or unimplemented CPU registers
func generalProtectionFaultHandler(regs *gate.Registers) {
kfmt.Printf("\nGeneral protection fault while accessing address: 0x%x\n", readCR2Fn())
kfmt.Printf("Registers:\n")
regs.DumpTo(kfmt.GetOutputSink())
// TODO: Revisit this when user-mode tasks are implemented
panic(errUnrecoverableFault)
}
func nonRecoverablePageFault(faultAddress uintptr, regs *gate.Registers, err *kernel.Error) {
kfmt.Printf("\nPage fault while accessing address: 0x%16x\nReason: ", faultAddress)
switch {
case errorCode == 0:
case regs.Info == 0:
kfmt.Printf("read from non-present page")
case errorCode == 1:
case regs.Info == 1:
kfmt.Printf("page protection violation (read)")
case errorCode == 2:
case regs.Info == 2:
kfmt.Printf("write to non-present page")
case errorCode == 3:
case regs.Info == 3:
kfmt.Printf("page protection violation (write)")
case errorCode == 4:
case regs.Info == 4:
kfmt.Printf("page-fault in user-mode")
case errorCode == 8:
case regs.Info == 8:
kfmt.Printf("page table has reserved bit set")
case errorCode == 16:
case regs.Info == 16:
kfmt.Printf("instruction fetch")
default:
kfmt.Printf("unknown")
}
kfmt.Printf("\n\nRegisters:\n")
regs.Print()
frame.Print()
regs.DumpTo(kfmt.GetOutputSink())
// TODO: Revisit this when user-mode tasks are implemented
panic(err)
}
func generalProtectionFaultHandler(_ uint64, frame *irq.Frame, regs *irq.Regs) {
kfmt.Printf("\nGeneral protection fault while accessing address: 0x%x\n", readCR2Fn())
kfmt.Printf("Registers:\n")
regs.Print()
frame.Print()
// TODO: Revisit this when user-mode tasks are implemented
panic(errUnrecoverableFault)
}

View File

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

View File

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

View File

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