1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00
Achilleas Anagnostopoulos 5fc6ce188e 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.
2017-06-25 21:39:56 +01:00

156 lines
4.7 KiB
Go

package vmm
import (
"github.com/achilleasa/gopher-os/kernel"
"github.com/achilleasa/gopher-os/kernel/cpu"
"github.com/achilleasa/gopher-os/kernel/irq"
"github.com/achilleasa/gopher-os/kernel/kfmt/early"
"github.com/achilleasa/gopher-os/kernel/mem"
"github.com/achilleasa/gopher-os/kernel/mem/pmm"
)
var (
// frameAllocator points to a frame allocator function registered using
// SetFrameAllocator.
frameAllocator FrameAllocatorFn
// the following functions are mocked by tests and are automatically
// inlined by the compiler.
handleExceptionWithCodeFn = irq.HandleExceptionWithCode
readCR2Fn = cpu.ReadCR2
errUnrecoverableFault = &kernel.Error{Module: "vmm", Message: "page/gpf fault"}
)
// FrameAllocatorFn is a function that can allocate physical frames.
type FrameAllocatorFn func() (pmm.Frame, *kernel.Error)
// SetFrameAllocator registers a frame allocator function that will be used by
// the vmm code when new physical frames need to be allocated.
func SetFrameAllocator(allocFn FrameAllocatorFn) {
frameAllocator = allocFn
}
func pageFaultHandler(errorCode uint64, frame *irq.Frame, regs *irq.Regs) {
var (
faultAddress = uintptr(readCR2Fn())
faultPage = PageFromAddress(faultAddress)
pageEntry *pageTableEntry
)
// Lookup entry for the page where the fault occurred
walk(faultPage.Address(), func(pteLevel uint8, pte *pageTableEntry) bool {
nextIsPresent := pte.HasFlags(FlagPresent)
if pteLevel == pageLevels-1 && nextIsPresent {
pageEntry = pte
}
// Abort walk if the next page table entry is missing
return nextIsPresent
})
// CoW is supported for RO pages with the CoW flag set
if pageEntry != nil && !pageEntry.HasFlags(FlagRW) && pageEntry.HasFlags(FlagCopyOnWrite) {
var (
copy pmm.Frame
tmpPage Page
err *kernel.Error
)
if copy, err = frameAllocator(); err != nil {
nonRecoverablePageFault(faultAddress, errorCode, frame, regs, err)
} else if tmpPage, err = mapTemporaryFn(copy); err != nil {
nonRecoverablePageFault(faultAddress, errorCode, frame, regs, err)
} else {
// Copy page contents, mark as RW and remove CoW flag
mem.Memcopy(faultPage.Address(), tmpPage.Address(), mem.PageSize)
unmapFn(tmpPage)
// Update mapping to point to the new frame, flag it as RW and
// remove the CoW flag
pageEntry.ClearFlags(FlagCopyOnWrite)
pageEntry.SetFlags(FlagPresent | FlagRW)
pageEntry.SetFrame(copy)
flushTLBEntryFn(faultPage.Address())
// Fault recovered; retry the instruction that caused the fault
return
}
}
nonRecoverablePageFault(faultAddress, errorCode, frame, regs, errUnrecoverableFault)
}
func nonRecoverablePageFault(faultAddress uintptr, errorCode uint64, frame *irq.Frame, regs *irq.Regs, err *kernel.Error) {
early.Printf("\nPage fault while accessing address: 0x%16x\nReason: ", faultAddress)
switch {
case errorCode == 0:
early.Printf("read from non-present page")
case errorCode == 1:
early.Printf("page protection violation (read)")
case errorCode == 2:
early.Printf("write to non-present page")
case errorCode == 3:
early.Printf("page protection violation (write)")
case errorCode == 4:
early.Printf("page-fault in user-mode")
case errorCode == 8:
early.Printf("page table has reserved bit set")
case errorCode == 16:
early.Printf("instruction fetch")
default:
early.Printf("unknown")
}
early.Printf("\n\nRegisters:\n")
regs.Print()
frame.Print()
// TODO: Revisit this when user-mode tasks are implemented
panic(err)
}
func generalProtectionFaultHandler(_ uint64, frame *irq.Frame, regs *irq.Regs) {
early.Printf("\nGeneral protection fault while accessing address: 0x%x\n", readCR2Fn())
early.Printf("Registers:\n")
regs.Print()
frame.Print()
// TODO: Revisit this when user-mode tasks are implemented
panic(errUnrecoverableFault)
}
// reserveZeroedFrame reserves a physical frame to be used together with
// FlagCopyOnWrite for lazy allocation requests.
func reserveZeroedFrame() *kernel.Error {
var (
err *kernel.Error
tempPage Page
)
if ReservedZeroedFrame, err = frameAllocator(); err != nil {
return err
} else if tempPage, err = mapTemporaryFn(ReservedZeroedFrame); err != nil {
return err
}
mem.Memset(tempPage.Address(), 0, mem.PageSize)
unmapFn(tempPage)
// From this point on, ReservedZeroedFrame cannot be mapped with a RW flag
protectReservedZeroedPage = true
return nil
}
// Init initializes the vmm system and installs paging-related exception
// handlers.
func Init() *kernel.Error {
if err := reserveZeroedFrame(); err != nil {
return err
}
handleExceptionWithCodeFn(irq.PageFaultException, pageFaultHandler)
handleExceptionWithCodeFn(irq.GPFException, generalProtectionFaultHandler)
return nil
}