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

Use go:redirect-from directive to map panic to kernel.Panic

All calls (but one) to kernel.Panic have been replaced by calls to
panic. A call to kernel.Panic is still required to prevent the compiler
from treating kernel.Panic as dead code and eliminating it.
This commit is contained in:
Achilleas Anagnostopoulos 2017-06-25 20:42:35 +01:00
parent b238442ccc
commit 5fc6ce188e
5 changed files with 129 additions and 90 deletions

View File

@ -8,6 +8,10 @@ import (
"github.com/achilleasa/gopher-os/kernel/mem/vmm" "github.com/achilleasa/gopher-os/kernel/mem/vmm"
) )
var (
errKmainReturned = &kernel.Error{Module: "kmain", Message: "Kmain returned"}
)
// Kmain is the only Go symbol that is visible (exported) from the rt0 initialization // Kmain is the only Go symbol that is visible (exported) from the rt0 initialization
// code. This function is invoked by the rt0 assembly code after setting up the GDT // code. This function is invoked by the rt0 assembly code after setting up the GDT
// and setting up a a minimal g0 struct that allows Go code using the 4K stack // and setting up a a minimal g0 struct that allows Go code using the 4K stack
@ -27,8 +31,12 @@ func Kmain(multibootInfoPtr, kernelStart, kernelEnd uintptr) {
var err *kernel.Error var err *kernel.Error
if err = allocator.Init(kernelStart, kernelEnd); err != nil { if err = allocator.Init(kernelStart, kernelEnd); err != nil {
kernel.Panic(err) panic(err)
} else if err = vmm.Init(); err != nil { } else if err = vmm.Init(); err != nil {
kernel.Panic(err) panic(err)
} }
// Use kernel.Panic instead of panic to prevent the compiler from
// treating kernel.Panic as dead-code and eliminating it.
kernel.Panic(errKmainReturned)
} }

View File

@ -16,9 +16,10 @@ 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.
panicFn = kernel.Panic
handleExceptionWithCodeFn = irq.HandleExceptionWithCode handleExceptionWithCodeFn = irq.HandleExceptionWithCode
readCR2Fn = cpu.ReadCR2 readCR2Fn = cpu.ReadCR2
errUnrecoverableFault = &kernel.Error{Module: "vmm", Message: "page/gpf fault"}
) )
// FrameAllocatorFn is a function that can allocate physical frames. // FrameAllocatorFn is a function that can allocate physical frames.
@ -78,7 +79,7 @@ func pageFaultHandler(errorCode uint64, frame *irq.Frame, regs *irq.Regs) {
} }
} }
nonRecoverablePageFault(faultAddress, errorCode, frame, regs, nil) nonRecoverablePageFault(faultAddress, errorCode, frame, regs, errUnrecoverableFault)
} }
func nonRecoverablePageFault(faultAddress uintptr, errorCode uint64, frame *irq.Frame, regs *irq.Regs, err *kernel.Error) { func nonRecoverablePageFault(faultAddress uintptr, errorCode uint64, frame *irq.Frame, regs *irq.Regs, err *kernel.Error) {
@ -107,7 +108,7 @@ func nonRecoverablePageFault(faultAddress uintptr, errorCode uint64, frame *irq.
frame.Print() frame.Print()
// TODO: Revisit this when user-mode tasks are implemented // TODO: Revisit this when user-mode tasks are implemented
panicFn(err) panic(err)
} }
func generalProtectionFaultHandler(_ uint64, frame *irq.Frame, regs *irq.Regs) { func generalProtectionFaultHandler(_ uint64, frame *irq.Frame, regs *irq.Regs) {
@ -117,7 +118,7 @@ func generalProtectionFaultHandler(_ uint64, frame *irq.Frame, regs *irq.Regs) {
frame.Print() frame.Print()
// TODO: Revisit this when user-mode tasks are implemented // TODO: Revisit this when user-mode tasks are implemented
panicFn(nil) panic(errUnrecoverableFault)
} }
// reserveZeroedFrame reserves a physical frame to be used together with // reserveZeroedFrame reserves a physical frame to be used together with

View File

@ -2,6 +2,7 @@ package vmm
import ( import (
"bytes" "bytes"
"fmt"
"strings" "strings"
"testing" "testing"
"unsafe" "unsafe"
@ -17,18 +18,16 @@ import (
func TestRecoverablePageFault(t *testing.T) { func TestRecoverablePageFault(t *testing.T) {
var ( var (
frame irq.Frame frame irq.Frame
regs irq.Regs regs irq.Regs
panicCalled bool pageEntry pageTableEntry
pageEntry pageTableEntry origPage = make([]byte, mem.PageSize)
origPage = make([]byte, mem.PageSize) clonedPage = make([]byte, mem.PageSize)
clonedPage = make([]byte, mem.PageSize) err = &kernel.Error{Module: "test", Message: "something went wrong"}
err = &kernel.Error{Module: "test", Message: "something went wrong"}
) )
defer func(origPtePtr func(uintptr) unsafe.Pointer) { defer func(origPtePtr func(uintptr) unsafe.Pointer) {
ptePtrFn = origPtePtr ptePtrFn = origPtePtr
panicFn = kernel.Panic
readCR2Fn = cpu.ReadCR2 readCR2Fn = cpu.ReadCR2
frameAllocator = nil frameAllocator = nil
mapTemporaryFn = MapTemporary mapTemporaryFn = MapTemporary
@ -58,97 +57,87 @@ func TestRecoverablePageFault(t *testing.T) {
mockTTY() mockTTY()
panicFn = func(_ *kernel.Error) {
panicCalled = true
}
ptePtrFn = func(entry uintptr) unsafe.Pointer { return unsafe.Pointer(&pageEntry) } ptePtrFn = func(entry uintptr) unsafe.Pointer { return unsafe.Pointer(&pageEntry) }
readCR2Fn = func() uint64 { return uint64(uintptr(unsafe.Pointer(&origPage[0]))) } readCR2Fn = func() uint64 { return uint64(uintptr(unsafe.Pointer(&origPage[0]))) }
unmapFn = func(_ Page) *kernel.Error { return nil } unmapFn = func(_ Page) *kernel.Error { return nil }
flushTLBEntryFn = func(_ uintptr) {} flushTLBEntryFn = func(_ uintptr) {}
for specIndex, spec := range specs { for specIndex, spec := range specs {
mapTemporaryFn = func(f pmm.Frame) (Page, *kernel.Error) { return Page(f), spec.mapError } t.Run(fmt.Sprint(specIndex), func(t *testing.T) {
SetFrameAllocator(func() (pmm.Frame, *kernel.Error) { defer func() {
addr := uintptr(unsafe.Pointer(&clonedPage[0])) err := recover()
return pmm.Frame(addr >> mem.PageShift), spec.allocError 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++ { for i := 0; i < len(origPage); i++ {
origPage[i] = byte(i % 256) if origPage[i] != clonedPage[i] {
clonedPage[i] = 0 t.Errorf("expected clone page to be a copy of the original page; mismatch at index %d", i)
} }
}
panicCalled = false
pageEntry = 0
pageEntry.SetFlags(spec.pteFlags)
pageFaultHandler(2, &frame, &regs)
if spec.expPanic != panicCalled {
t.Errorf("[spec %d] expected panic %t; got %t", specIndex, spec.expPanic, panicCalled)
}
if !spec.expPanic {
for i := 0; i < len(origPage); i++ {
if origPage[i] != clonedPage[i] {
t.Errorf("[spec %d] expected clone page to be a copy of the original page; mismatch at index %d", specIndex, 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, &regs)
})
} }
} }
func TestNonRecoverablePageFault(t *testing.T) { func TestNonRecoverablePageFault(t *testing.T) {
defer func() {
panicFn = kernel.Panic
}()
specs := []struct { specs := []struct {
errCode uint64 errCode uint64
expReason string expReason string
expPanic bool
}{ }{
{ {
0, 0,
"read from non-present page", "read from non-present page",
true,
}, },
{ {
1, 1,
"page protection violation (read)", "page protection violation (read)",
true,
}, },
{ {
2, 2,
"write to non-present page", "write to non-present page",
true,
}, },
{ {
3, 3,
"page protection violation (write)", "page protection violation (write)",
true,
}, },
{ {
4, 4,
"page-fault in user-mode", "page-fault in user-mode",
true,
}, },
{ {
8, 8,
"page table has reserved bit set", "page table has reserved bit set",
true,
}, },
{ {
16, 16,
"instruction fetch", "instruction fetch",
true,
}, },
{ {
0xf00, 0xf00,
"unknown", "unknown",
true,
}, },
} }
@ -157,58 +146,45 @@ func TestNonRecoverablePageFault(t *testing.T) {
frame irq.Frame frame irq.Frame
) )
panicCalled := false
panicFn = func(_ *kernel.Error) {
panicCalled = true
}
for specIndex, spec := range specs { for specIndex, spec := range specs {
fb := mockTTY() t.Run(fmt.Sprint(specIndex), func(t *testing.T) {
panicCalled = false defer func() {
if err := recover(); err != errUnrecoverableFault {
t.Errorf("expected a panic with errUnrecoverableFault; got %v", err)
}
}()
fb := mockTTY()
nonRecoverablePageFault(0xbadf00d000, spec.errCode, &frame, &regs, nil) nonRecoverablePageFault(0xbadf00d000, spec.errCode, &frame, &regs, errUnrecoverableFault)
if got := readTTY(fb); !strings.Contains(got, spec.expReason) { if got := readTTY(fb); !strings.Contains(got, spec.expReason) {
t.Errorf("[spec %d] expected reason %q; got output:\n%q", specIndex, spec.expReason, got) t.Errorf("expected reason %q; got output:\n%q", spec.expReason, got)
continue }
} })
if spec.expPanic != panicCalled {
t.Errorf("[spec %d] expected panic %t; got %t", specIndex, spec.expPanic, panicCalled)
}
} }
} }
func TestGPtHandler(t *testing.T) { func TestGPtHandler(t *testing.T) {
defer func() { defer func() {
panicFn = kernel.Panic
readCR2Fn = cpu.ReadCR2 readCR2Fn = cpu.ReadCR2
}() }()
var ( var (
regs irq.Regs regs irq.Regs
frame irq.Frame frame irq.Frame
fb = mockTTY()
) )
readCR2Fn = func() uint64 { readCR2Fn = func() uint64 {
return 0xbadf00d000 return 0xbadf00d000
} }
panicCalled := false defer func() {
panicFn = func(_ *kernel.Error) { if err := recover(); err != errUnrecoverableFault {
panicCalled = true t.Errorf("expected a panic with errUnrecoverableFault; got %v", err)
} }
}()
mockTTY()
generalProtectionFaultHandler(0, &frame, &regs) generalProtectionFaultHandler(0, &frame, &regs)
exp := "\nGeneral protection fault while accessing address: 0xbadf00d000\nRegisters:\nRAX = 0000000000000000 RBX = 0000000000000000\nRCX = 0000000000000000 RDX = 0000000000000000\nRSI = 0000000000000000 RDI = 0000000000000000\nRBP = 0000000000000000\nR8 = 0000000000000000 R9 = 0000000000000000\nR10 = 0000000000000000 R11 = 0000000000000000\nR12 = 0000000000000000 R13 = 0000000000000000\nR14 = 0000000000000000 R15 = 0000000000000000\nRIP = 0000000000000000 CS = 0000000000000000\nRSP = 0000000000000000 SS = 0000000000000000\nRFL = 0000000000000000"
if got := readTTY(fb); got != exp {
t.Errorf("expected output:\n%q\ngot:\n%q", exp, got)
}
if !panicCalled {
t.Error("expected kernel.Panic to be called")
}
} }
func TestInit(t *testing.T) { func TestInit(t *testing.T) {

View File

@ -8,11 +8,28 @@ import (
var ( var (
// cpuHaltFn is mocked by tests and is automatically inlined by the compiler. // cpuHaltFn is mocked by tests and is automatically inlined by the compiler.
cpuHaltFn = cpu.Halt cpuHaltFn = cpu.Halt
errRuntimePanic = &Error{Module: "rt", Message: "unknown cause"}
) )
// Panic outputs the supplied error (if not nil) to the console and halts the // Panic outputs the supplied error (if not nil) to the console and halts the
// CPU. Calls to Panic never return. // CPU. Calls to Panic never return. Panic also works as a redirection target
func Panic(err *Error) { // for calls to panic() (resolved via runtime.gopanic)
//go:redirect-from runtime.gopanic
func Panic(e interface{}) {
var err *Error
switch t := e.(type) {
case *Error:
err = t
case string:
errRuntimePanic.Message = t
err = errRuntimePanic
case error:
errRuntimePanic.Message = t.Error()
err = errRuntimePanic
}
early.Printf("\n-----------------------------------\n") early.Printf("\n-----------------------------------\n")
if err != nil { if err != nil {
early.Printf("[%s] unrecoverable error: %s\n", err.Module, err.Message) early.Printf("[%s] unrecoverable error: %s\n", err.Module, err.Message)

View File

@ -2,6 +2,7 @@ package kernel
import ( import (
"bytes" "bytes"
"errors"
"testing" "testing"
"unsafe" "unsafe"
@ -20,7 +21,7 @@ func TestPanic(t *testing.T) {
cpuHaltCalled = true cpuHaltCalled = true
} }
t.Run("with error", func(t *testing.T) { t.Run("with *kernel.Error", func(t *testing.T) {
cpuHaltCalled = false cpuHaltCalled = false
fb := mockTTY() fb := mockTTY()
err := &Error{Module: "test", Message: "panic test"} err := &Error{Module: "test", Message: "panic test"}
@ -38,6 +39,42 @@ func TestPanic(t *testing.T) {
} }
}) })
t.Run("with error", func(t *testing.T) {
cpuHaltCalled = false
fb := mockTTY()
err := errors.New("go error")
Panic(err)
exp := "\n-----------------------------------\n[rt] unrecoverable error: go error\n*** kernel panic: system halted ***\n-----------------------------------"
if got := readTTY(fb); got != exp {
t.Fatalf("expected to get:\n%q\ngot:\n%q", exp, got)
}
if !cpuHaltCalled {
t.Fatal("expected cpu.Halt() to be called by Panic")
}
})
t.Run("with string", func(t *testing.T) {
cpuHaltCalled = false
fb := mockTTY()
err := "string error"
Panic(err)
exp := "\n-----------------------------------\n[rt] unrecoverable error: string error\n*** kernel panic: system halted ***\n-----------------------------------"
if got := readTTY(fb); got != exp {
t.Fatalf("expected to get:\n%q\ngot:\n%q", exp, got)
}
if !cpuHaltCalled {
t.Fatal("expected cpu.Halt() to be called by Panic")
}
})
t.Run("without error", func(t *testing.T) { t.Run("without error", func(t *testing.T) {
cpuHaltCalled = false cpuHaltCalled = false
fb := mockTTY() fb := mockTTY()