From 7deeab3cbc2d6d6f1405c250819c8f7cab1eda0a Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Thu, 31 May 2018 21:22:29 +0100 Subject: [PATCH] vmm: switch exception handling to use the new gate package --- .../mm/vmm/{fault.go => fault_amd64.go} | 64 +++++++++++-------- .../{fault_test.go => fault_amd64_test.go} | 23 +++---- src/gopheros/kernel/mm/vmm/vmm.go | 15 ++--- src/gopheros/kernel/mm/vmm/vmm_test.go | 10 +-- 4 files changed, 59 insertions(+), 53 deletions(-) rename src/gopheros/kernel/mm/vmm/{fault.go => fault_amd64.go} (63%) rename src/gopheros/kernel/mm/vmm/{fault_test.go => fault_amd64_test.go} (91%) diff --git a/src/gopheros/kernel/mm/vmm/fault.go b/src/gopheros/kernel/mm/vmm/fault_amd64.go similarity index 63% rename from src/gopheros/kernel/mm/vmm/fault.go rename to src/gopheros/kernel/mm/vmm/fault_amd64.go index 630d3ac..7ed65a8 100644 --- a/src/gopheros/kernel/mm/vmm/fault.go +++ b/src/gopheros/kernel/mm/vmm/fault_amd64.go @@ -2,12 +2,24 @@ package vmm import ( "gopheros/kernel" - "gopheros/kernel/irq" + "gopheros/kernel/gate" "gopheros/kernel/kfmt" "gopheros/kernel/mm" ) -func pageFaultHandler(errorCode uint64, frame *irq.Frame, regs *irq.Regs) { +var ( + // handleInterruptFn is used by tests. + handleInterruptFn = gate.HandleInterrupt +) + +func installFaultHandlers() { + handleInterruptFn(gate.PageFaultException, 0, pageFaultHandler) + handleInterruptFn(gate.GPFException, 0, generalProtectionFaultHandler) +} + +// pageFaultHandler is invoked when a PDT or PDT-entry is not present or when a +// RW protection check fails. +func pageFaultHandler(regs *gate.Registers) { var ( faultAddress = uintptr(readCR2Fn()) faultPage = mm.PageFromAddress(faultAddress) @@ -35,9 +47,9 @@ func pageFaultHandler(errorCode uint64, frame *irq.Frame, regs *irq.Regs) { ) if copy, err = mm.AllocFrame(); err != nil { - nonRecoverablePageFault(faultAddress, errorCode, frame, regs, err) + nonRecoverablePageFault(faultAddress, regs, err) } else if tmpPage, err = mapTemporaryFn(copy); err != nil { - nonRecoverablePageFault(faultAddress, errorCode, frame, regs, err) + nonRecoverablePageFault(faultAddress, regs, err) } else { // Copy page contents, mark as RW and remove CoW flag kernel.Memcopy(faultPage.Address(), tmpPage.Address(), mm.PageSize) @@ -55,44 +67,46 @@ func pageFaultHandler(errorCode uint64, frame *irq.Frame, regs *irq.Regs) { } } - nonRecoverablePageFault(faultAddress, errorCode, frame, regs, errUnrecoverableFault) + nonRecoverablePageFault(faultAddress, regs, errUnrecoverableFault) } -func nonRecoverablePageFault(faultAddress uintptr, errorCode uint64, frame *irq.Frame, regs *irq.Regs, err *kernel.Error) { +// generalProtectionFaultHandler is invoked for various reasons: +// - segment errors (privilege, type or limit violations) +// - executing privileged instructions outside ring-0 +// - attempts to access reserved or unimplemented CPU registers +func generalProtectionFaultHandler(regs *gate.Registers) { + kfmt.Printf("\nGeneral protection fault while accessing address: 0x%x\n", readCR2Fn()) + kfmt.Printf("Registers:\n") + regs.DumpTo(kfmt.GetOutputSink()) + + // TODO: Revisit this when user-mode tasks are implemented + panic(errUnrecoverableFault) +} + +func nonRecoverablePageFault(faultAddress uintptr, regs *gate.Registers, err *kernel.Error) { kfmt.Printf("\nPage fault while accessing address: 0x%16x\nReason: ", faultAddress) switch { - case errorCode == 0: + case regs.Info == 0: kfmt.Printf("read from non-present page") - case errorCode == 1: + case regs.Info == 1: kfmt.Printf("page protection violation (read)") - case errorCode == 2: + case regs.Info == 2: kfmt.Printf("write to non-present page") - case errorCode == 3: + case regs.Info == 3: kfmt.Printf("page protection violation (write)") - case errorCode == 4: + case regs.Info == 4: kfmt.Printf("page-fault in user-mode") - case errorCode == 8: + case regs.Info == 8: kfmt.Printf("page table has reserved bit set") - case errorCode == 16: + case regs.Info == 16: kfmt.Printf("instruction fetch") default: kfmt.Printf("unknown") } kfmt.Printf("\n\nRegisters:\n") - regs.Print() - frame.Print() + regs.DumpTo(kfmt.GetOutputSink()) // TODO: Revisit this when user-mode tasks are implemented panic(err) } - -func generalProtectionFaultHandler(_ uint64, frame *irq.Frame, regs *irq.Regs) { - kfmt.Printf("\nGeneral protection fault while accessing address: 0x%x\n", readCR2Fn()) - kfmt.Printf("Registers:\n") - regs.Print() - frame.Print() - - // TODO: Revisit this when user-mode tasks are implemented - panic(errUnrecoverableFault) -} diff --git a/src/gopheros/kernel/mm/vmm/fault_test.go b/src/gopheros/kernel/mm/vmm/fault_amd64_test.go similarity index 91% rename from src/gopheros/kernel/mm/vmm/fault_test.go rename to src/gopheros/kernel/mm/vmm/fault_amd64_test.go index e7589bb..3fd6334 100644 --- a/src/gopheros/kernel/mm/vmm/fault_test.go +++ b/src/gopheros/kernel/mm/vmm/fault_amd64_test.go @@ -5,7 +5,7 @@ import ( "fmt" "gopheros/kernel" "gopheros/kernel/cpu" - "gopheros/kernel/irq" + "gopheros/kernel/gate" "gopheros/kernel/kfmt" "gopheros/kernel/mm" "strings" @@ -15,8 +15,7 @@ import ( func TestRecoverablePageFault(t *testing.T) { var ( - frame irq.Frame - regs irq.Regs + regs gate.Registers pageEntry pageTableEntry origPage = make([]byte, mm.PageSize) clonedPage = make([]byte, mm.PageSize) @@ -91,7 +90,8 @@ func TestRecoverablePageFault(t *testing.T) { pageEntry = 0 pageEntry.SetFlags(spec.pteFlags) - pageFaultHandler(2, &frame, ®s) + regs.Info = 2 + pageFaultHandler(®s) }) } @@ -141,9 +141,8 @@ func TestNonRecoverablePageFault(t *testing.T) { } var ( - regs irq.Regs - frame irq.Frame - buf bytes.Buffer + regs gate.Registers + buf bytes.Buffer ) kfmt.SetOutputSink(&buf) @@ -156,7 +155,8 @@ func TestNonRecoverablePageFault(t *testing.T) { } }() - nonRecoverablePageFault(0xbadf00d000, spec.errCode, &frame, ®s, errUnrecoverableFault) + regs.Info = spec.errCode + nonRecoverablePageFault(0xbadf00d000, ®s, errUnrecoverableFault) if got := buf.String(); !strings.Contains(got, spec.expReason) { t.Errorf("expected reason %q; got output:\n%q", spec.expReason, got) } @@ -169,10 +169,7 @@ func TestGPFHandler(t *testing.T) { readCR2Fn = cpu.ReadCR2 }() - var ( - regs irq.Regs - frame irq.Frame - ) + var regs gate.Registers readCR2Fn = func() uint64 { return 0xbadf00d000 @@ -184,5 +181,5 @@ func TestGPFHandler(t *testing.T) { } }() - generalProtectionFaultHandler(0, &frame, ®s) + generalProtectionFaultHandler(®s) } diff --git a/src/gopheros/kernel/mm/vmm/vmm.go b/src/gopheros/kernel/mm/vmm/vmm.go index c6f40ad..e88645d 100644 --- a/src/gopheros/kernel/mm/vmm/vmm.go +++ b/src/gopheros/kernel/mm/vmm/vmm.go @@ -3,16 +3,14 @@ package vmm import ( "gopheros/kernel" "gopheros/kernel/cpu" - "gopheros/kernel/irq" "gopheros/kernel/mm" ) var ( // the following functions are mocked by tests and are automatically // inlined by the compiler. - handleExceptionWithCodeFn = irq.HandleExceptionWithCode - readCR2Fn = cpu.ReadCR2 - translateFn = Translate + readCR2Fn = cpu.ReadCR2 + translateFn = Translate errUnrecoverableFault = &kernel.Error{Module: "vmm", Message: "page/gpf fault"} ) @@ -24,13 +22,10 @@ func Init(kernelPageOffset uintptr) *kernel.Error { return err } - if err := reserveZeroedFrame(); err != nil { - return err - } + // Install arch-specific handlers for vmm-related faults. + installFaultHandlers() - handleExceptionWithCodeFn(irq.PageFaultException, pageFaultHandler) - handleExceptionWithCodeFn(irq.GPFException, generalProtectionFaultHandler) - return nil + return reserveZeroedFrame() } // reserveZeroedFrame reserves a physical frame to be used together with diff --git a/src/gopheros/kernel/mm/vmm/vmm_test.go b/src/gopheros/kernel/mm/vmm/vmm_test.go index 2cefdf2..6f39735 100644 --- a/src/gopheros/kernel/mm/vmm/vmm_test.go +++ b/src/gopheros/kernel/mm/vmm/vmm_test.go @@ -3,7 +3,7 @@ package vmm import ( "gopheros/kernel" "gopheros/kernel/cpu" - "gopheros/kernel/irq" + "gopheros/kernel/gate" "gopheros/kernel/mm" "gopheros/multiboot" "testing" @@ -18,7 +18,7 @@ func TestInit(t *testing.T) { translateFn = Translate mapTemporaryFn = MapTemporary unmapFn = Unmap - handleExceptionWithCodeFn = irq.HandleExceptionWithCode + handleInterruptFn = gate.HandleInterrupt }() // reserve space for an allocated page @@ -42,7 +42,7 @@ func TestInit(t *testing.T) { switchPDTFn = func(_ uintptr) {} unmapFn = func(p mm.Page) *kernel.Error { return nil } mapTemporaryFn = func(f mm.Frame) (mm.Page, *kernel.Error) { return mm.Page(f), nil } - handleExceptionWithCodeFn = func(_ irq.ExceptionNum, _ irq.ExceptionHandlerWithCode) {} + handleInterruptFn = func(_ gate.InterruptNumber, _ uint8, _ func(*gate.Registers)) {} if err := Init(0); err != nil { t.Fatal(err) @@ -92,7 +92,7 @@ func TestInit(t *testing.T) { switchPDTFn = func(_ uintptr) {} unmapFn = func(p mm.Page) *kernel.Error { return nil } mapTemporaryFn = func(f mm.Frame) (mm.Page, *kernel.Error) { return mm.Page(f), nil } - handleExceptionWithCodeFn = func(_ irq.ExceptionNum, _ irq.ExceptionHandlerWithCode) {} + handleInterruptFn = func(_ gate.InterruptNumber, _ uint8, _ func(*gate.Registers)) {} if err := Init(0); err != expErr { t.Fatalf("expected error: %v; got %v", expErr, err) @@ -112,7 +112,7 @@ func TestInit(t *testing.T) { switchPDTFn = func(_ uintptr) {} unmapFn = func(p mm.Page) *kernel.Error { return nil } mapTemporaryFn = func(f mm.Frame) (mm.Page, *kernel.Error) { return mm.Page(f), expErr } - handleExceptionWithCodeFn = func(_ irq.ExceptionNum, _ irq.ExceptionHandlerWithCode) {} + handleInterruptFn = func(_ gate.InterruptNumber, _ uint8, _ func(*gate.Registers)) {} if err := Init(0); err != expErr { t.Fatalf("expected error: %v; got %v", expErr, err)