mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
257 lines
6.3 KiB
Go
257 lines
6.3 KiB
Go
package vmm
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"gopheros/kernel"
|
|
"gopheros/kernel/cpu"
|
|
"gopheros/kernel/irq"
|
|
"gopheros/kernel/kfmt"
|
|
"gopheros/kernel/mem"
|
|
"gopheros/kernel/mem/pmm"
|
|
"strings"
|
|
"testing"
|
|
"unsafe"
|
|
)
|
|
|
|
func TestRecoverablePageFault(t *testing.T) {
|
|
var (
|
|
frame irq.Frame
|
|
regs irq.Regs
|
|
pageEntry pageTableEntry
|
|
origPage = make([]byte, mem.PageSize)
|
|
clonedPage = make([]byte, mem.PageSize)
|
|
err = &kernel.Error{Module: "test", Message: "something went wrong"}
|
|
)
|
|
|
|
defer func(origPtePtr func(uintptr) unsafe.Pointer) {
|
|
ptePtrFn = origPtePtr
|
|
readCR2Fn = cpu.ReadCR2
|
|
frameAllocator = nil
|
|
mapTemporaryFn = MapTemporary
|
|
unmapFn = Unmap
|
|
flushTLBEntryFn = cpu.FlushTLBEntry
|
|
}(ptePtrFn)
|
|
|
|
specs := []struct {
|
|
pteFlags PageTableEntryFlag
|
|
allocError *kernel.Error
|
|
mapError *kernel.Error
|
|
expPanic bool
|
|
}{
|
|
// Missing pge
|
|
{0, nil, nil, true},
|
|
// Page is present but CoW flag not set
|
|
{FlagPresent, nil, nil, true},
|
|
// Page is present but both CoW and RW flags set
|
|
{FlagPresent | FlagRW | FlagCopyOnWrite, nil, nil, true},
|
|
// Page is present with CoW flag set but allocating a page copy fails
|
|
{FlagPresent | FlagCopyOnWrite, err, nil, true},
|
|
// Page is present with CoW flag set but mapping the page copy fails
|
|
{FlagPresent | FlagCopyOnWrite, nil, err, true},
|
|
// Page is present with CoW flag set
|
|
{FlagPresent | FlagCopyOnWrite, nil, nil, false},
|
|
}
|
|
|
|
ptePtrFn = func(entry uintptr) unsafe.Pointer { return unsafe.Pointer(&pageEntry) }
|
|
readCR2Fn = func() uint64 { return uint64(uintptr(unsafe.Pointer(&origPage[0]))) }
|
|
unmapFn = func(_ Page) *kernel.Error { return nil }
|
|
flushTLBEntryFn = func(_ uintptr) {}
|
|
|
|
for specIndex, spec := range specs {
|
|
t.Run(fmt.Sprint(specIndex), func(t *testing.T) {
|
|
defer func() {
|
|
err := recover()
|
|
if spec.expPanic && err == nil {
|
|
t.Error("expected a panic")
|
|
} else if !spec.expPanic {
|
|
if err != nil {
|
|
t.Error("unexpected panic")
|
|
return
|
|
}
|
|
|
|
for i := 0; i < len(origPage); i++ {
|
|
if origPage[i] != clonedPage[i] {
|
|
t.Errorf("expected clone page to be a copy of the original page; mismatch at index %d", i)
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
mapTemporaryFn = func(f pmm.Frame) (Page, *kernel.Error) { return Page(f), spec.mapError }
|
|
SetFrameAllocator(func() (pmm.Frame, *kernel.Error) {
|
|
addr := uintptr(unsafe.Pointer(&clonedPage[0]))
|
|
return pmm.Frame(addr >> mem.PageShift), spec.allocError
|
|
})
|
|
|
|
for i := 0; i < len(origPage); i++ {
|
|
origPage[i] = byte(i % 256)
|
|
clonedPage[i] = 0
|
|
}
|
|
|
|
pageEntry = 0
|
|
pageEntry.SetFlags(spec.pteFlags)
|
|
|
|
pageFaultHandler(2, &frame, ®s)
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func TestNonRecoverablePageFault(t *testing.T) {
|
|
defer func() {
|
|
kfmt.SetOutputSink(nil)
|
|
}()
|
|
|
|
specs := []struct {
|
|
errCode uint64
|
|
expReason string
|
|
}{
|
|
{
|
|
0,
|
|
"read from non-present page",
|
|
},
|
|
{
|
|
1,
|
|
"page protection violation (read)",
|
|
},
|
|
{
|
|
2,
|
|
"write to non-present page",
|
|
},
|
|
{
|
|
3,
|
|
"page protection violation (write)",
|
|
},
|
|
{
|
|
4,
|
|
"page-fault in user-mode",
|
|
},
|
|
{
|
|
8,
|
|
"page table has reserved bit set",
|
|
},
|
|
{
|
|
16,
|
|
"instruction fetch",
|
|
},
|
|
{
|
|
0xf00,
|
|
"unknown",
|
|
},
|
|
}
|
|
|
|
var (
|
|
regs irq.Regs
|
|
frame irq.Frame
|
|
buf bytes.Buffer
|
|
)
|
|
|
|
kfmt.SetOutputSink(&buf)
|
|
for specIndex, spec := range specs {
|
|
t.Run(fmt.Sprint(specIndex), func(t *testing.T) {
|
|
buf.Reset()
|
|
defer func() {
|
|
if err := recover(); err != errUnrecoverableFault {
|
|
t.Errorf("expected a panic with errUnrecoverableFault; got %v", err)
|
|
}
|
|
}()
|
|
|
|
nonRecoverablePageFault(0xbadf00d000, spec.errCode, &frame, ®s, errUnrecoverableFault)
|
|
if got := buf.String(); !strings.Contains(got, spec.expReason) {
|
|
t.Errorf("expected reason %q; got output:\n%q", spec.expReason, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGPtHandler(t *testing.T) {
|
|
defer func() {
|
|
readCR2Fn = cpu.ReadCR2
|
|
}()
|
|
|
|
var (
|
|
regs irq.Regs
|
|
frame irq.Frame
|
|
)
|
|
|
|
readCR2Fn = func() uint64 {
|
|
return 0xbadf00d000
|
|
}
|
|
|
|
defer func() {
|
|
if err := recover(); err != errUnrecoverableFault {
|
|
t.Errorf("expected a panic with errUnrecoverableFault; got %v", err)
|
|
}
|
|
}()
|
|
|
|
generalProtectionFaultHandler(0, &frame, ®s)
|
|
}
|
|
|
|
func TestInit(t *testing.T) {
|
|
defer func() {
|
|
frameAllocator = nil
|
|
mapTemporaryFn = MapTemporary
|
|
unmapFn = Unmap
|
|
handleExceptionWithCodeFn = irq.HandleExceptionWithCode
|
|
}()
|
|
|
|
// reserve space for an allocated page
|
|
reservedPage := make([]byte, mem.PageSize)
|
|
|
|
t.Run("success", func(t *testing.T) {
|
|
// fill page with junk
|
|
for i := 0; i < len(reservedPage); i++ {
|
|
reservedPage[i] = byte(i % 256)
|
|
}
|
|
|
|
SetFrameAllocator(func() (pmm.Frame, *kernel.Error) {
|
|
addr := uintptr(unsafe.Pointer(&reservedPage[0]))
|
|
return pmm.Frame(addr >> mem.PageShift), nil
|
|
})
|
|
unmapFn = func(p Page) *kernel.Error { return nil }
|
|
mapTemporaryFn = func(f pmm.Frame) (Page, *kernel.Error) { return Page(f), nil }
|
|
handleExceptionWithCodeFn = func(_ irq.ExceptionNum, _ irq.ExceptionHandlerWithCode) {}
|
|
|
|
if err := Init(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// reserved page should be zeroed
|
|
for i := 0; i < len(reservedPage); i++ {
|
|
if reservedPage[i] != 0 {
|
|
t.Errorf("expected reserved page to be zeroed; got byte %d at index %d", reservedPage[i], i)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("blank page allocation error", func(t *testing.T) {
|
|
expErr := &kernel.Error{Module: "test", Message: "out of memory"}
|
|
|
|
SetFrameAllocator(func() (pmm.Frame, *kernel.Error) { return pmm.InvalidFrame, expErr })
|
|
unmapFn = func(p Page) *kernel.Error { return nil }
|
|
mapTemporaryFn = func(f pmm.Frame) (Page, *kernel.Error) { return Page(f), nil }
|
|
handleExceptionWithCodeFn = func(_ irq.ExceptionNum, _ irq.ExceptionHandlerWithCode) {}
|
|
|
|
if err := Init(); err != expErr {
|
|
t.Fatalf("expected error: %v; got %v", expErr, err)
|
|
}
|
|
})
|
|
|
|
t.Run("blank page mapping error", func(t *testing.T) {
|
|
expErr := &kernel.Error{Module: "test", Message: "map failed"}
|
|
|
|
SetFrameAllocator(func() (pmm.Frame, *kernel.Error) {
|
|
addr := uintptr(unsafe.Pointer(&reservedPage[0]))
|
|
return pmm.Frame(addr >> mem.PageShift), nil
|
|
})
|
|
unmapFn = func(p Page) *kernel.Error { return nil }
|
|
mapTemporaryFn = func(f pmm.Frame) (Page, *kernel.Error) { return Page(f), expErr }
|
|
handleExceptionWithCodeFn = func(_ irq.ExceptionNum, _ irq.ExceptionHandlerWithCode) {}
|
|
|
|
if err := Init(); err != expErr {
|
|
t.Fatalf("expected error: %v; got %v", expErr, err)
|
|
}
|
|
})
|
|
}
|