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:
parent
7b12fbd940
commit
7deeab3cbc
@ -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,8 +141,7 @@ func TestNonRecoverablePageFault(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
regs irq.Regs
|
regs gate.Registers
|
||||||
frame irq.Frame
|
|
||||||
buf bytes.Buffer
|
buf bytes.Buffer
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -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,14 +3,12 @@ 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
|
||||||
|
|
||||||
@ -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