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:
parent
b238442ccc
commit
5fc6ce188e
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -2,6 +2,7 @@ package vmm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@ -19,7 +20,6 @@ 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)
|
||||||
@ -28,7 +28,6 @@ func TestRecoverablePageFault(t *testing.T) {
|
|||||||
|
|
||||||
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,16 +57,31 @@ 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 {
|
||||||
|
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 }
|
mapTemporaryFn = func(f pmm.Frame) (Page, *kernel.Error) { return Page(f), spec.mapError }
|
||||||
SetFrameAllocator(func() (pmm.Frame, *kernel.Error) {
|
SetFrameAllocator(func() (pmm.Frame, *kernel.Error) {
|
||||||
addr := uintptr(unsafe.Pointer(&clonedPage[0]))
|
addr := uintptr(unsafe.Pointer(&clonedPage[0]))
|
||||||
@ -79,76 +93,51 @@ func TestRecoverablePageFault(t *testing.T) {
|
|||||||
clonedPage[i] = 0
|
clonedPage[i] = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
panicCalled = false
|
|
||||||
pageEntry = 0
|
pageEntry = 0
|
||||||
pageEntry.SetFlags(spec.pteFlags)
|
pageEntry.SetFlags(spec.pteFlags)
|
||||||
|
|
||||||
pageFaultHandler(2, &frame, ®s)
|
pageFaultHandler(2, &frame, ®s)
|
||||||
|
})
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
t.Run(fmt.Sprint(specIndex), func(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != errUnrecoverableFault {
|
||||||
|
t.Errorf("expected a panic with errUnrecoverableFault; got %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
fb := mockTTY()
|
fb := mockTTY()
|
||||||
panicCalled = false
|
|
||||||
|
|
||||||
nonRecoverablePageFault(0xbadf00d000, spec.errCode, &frame, ®s, nil)
|
nonRecoverablePageFault(0xbadf00d000, spec.errCode, &frame, ®s, 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, ®s)
|
generalProtectionFaultHandler(0, &frame, ®s)
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user