1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00
Achilleas Anagnostopoulos 76532089c5 Define PageDirectoryTable and helpers that support inactive tables
The Map/Unmap methods of PageDirectoryTable operate similar to the
global Map/Unmap functions. While the global functions work with the
currently active PDT, the PageDirectoryTable methods can also
work with inactive page tables by temporarily modifying the recursive
mapping of the active PDT to point to the inactive PDT frame before
delegating the mapping/unmapping to the global Map/Unmap functions.
2017-06-06 19:35:07 +01:00

136 lines
4.6 KiB
Go

package vmm
import (
"unsafe"
"github.com/achilleasa/gopher-os/kernel"
"github.com/achilleasa/gopher-os/kernel/mem"
"github.com/achilleasa/gopher-os/kernel/mem/pmm"
)
var (
// activePDTFn is used by tests to override calls to activePDT which
// will cause a fault if called in user-mode.
activePDTFn = activePDT
// switchPDTFn is used by tests to override calls to switchPDT which
// will cause a fault if called in user-mode.
switchPDTFn = switchPDT
// mapFn is used by tests and is automatically inlined by the compiler.
mapFn = Map
// mapTemporaryFn is used by tests and is automatically inlined by the compiler.
mapTemporaryFn = MapTemporary
// unmapmFn is used by tests and is automatically inlined by the compiler.
unmapFn = Unmap
)
// PageDirectoryTable describes the top-most table in a multi-level paging scheme.
type PageDirectoryTable struct {
pdtFrame pmm.Frame
}
// Init sets up the page table directory starting at the supplied physical
// address. If the supplied frame does not match the currently active PDT, then
// Init assumes that this is a new page table directory that needs
// bootstapping. In such a case, a temporary mapping is established so that
// Init can:
// - call mem.Memset to clear the frame contents
// - setup a recursive mapping for the last table entry to the page itself.
func (pdt *PageDirectoryTable) Init(pdtFrame pmm.Frame, allocFn FrameAllocator) *kernel.Error {
pdt.pdtFrame = pdtFrame
// Check active PDT physical address. If it matches the input pdt then
// nothing more needs to be done
activePdtAddr := activePDTFn()
if pdtFrame.Address() == activePdtAddr {
return nil
}
// Create a temporary mapping for the pdt frame so we can work on it
pdtPage, err := mapTemporaryFn(pdtFrame, allocFn)
if err != nil {
return err
}
// Clear the page contents and setup recursive mapping for the last PDT entry
mem.Memset(pdtPage.Address(), 0, mem.PageSize)
lastPdtEntry := (*pageTableEntry)(unsafe.Pointer(pdtPage.Address() + (((1 << pageLevelBits[0]) - 1) << mem.PointerShift)))
*lastPdtEntry = 0
lastPdtEntry.SetFlags(FlagPresent | FlagRW)
lastPdtEntry.SetFrame(pdtFrame)
// Remove temporary mapping
unmapFn(pdtPage)
return nil
}
// Map establishes a mapping between a virtual page and a physical memory frame
// using this PDT. This method behaves in a similar fashion to the global Map()
// function with the difference that it also supports inactive page PDTs by
// establishing a temporary mapping so that Map() can access the inactive PDT
// entries.
func (pdt PageDirectoryTable) Map(page Page, frame pmm.Frame, flags PageTableEntryFlag, allocFn FrameAllocator) *kernel.Error {
var (
activePdtFrame = pmm.Frame(activePDTFn() >> mem.PageShift)
lastPdtEntryAddr uintptr
lastPdtEntry *pageTableEntry
)
// If this table is not active we need to temporarily map it to the
// last entry in the active PDT so we can access it using the recursive
// virtual address scheme.
if activePdtFrame != pdt.pdtFrame {
lastPdtEntryAddr = activePdtFrame.Address() + (((1 << pageLevelBits[0]) - 1) << mem.PointerShift)
lastPdtEntry = (*pageTableEntry)(unsafe.Pointer(lastPdtEntryAddr))
lastPdtEntry.SetFrame(pdt.pdtFrame)
flushTLBEntryFn(lastPdtEntryAddr)
}
err := mapFn(page, frame, flags, allocFn)
if activePdtFrame != pdt.pdtFrame {
lastPdtEntry.SetFrame(activePdtFrame)
flushTLBEntryFn(lastPdtEntryAddr)
}
return err
}
// Unmap removes a mapping previousle installed by a call to Map() on this PDT.
// This method behaves in a similar fashion to the global Unmap() function with
// the difference that it also supports inactive page PDTs by establishing a
// temporary mapping so that Unmap() can access the inactive PDT entries.
func (pdt PageDirectoryTable) Unmap(page Page) *kernel.Error {
var (
activePdtFrame = pmm.Frame(activePDTFn() >> mem.PageShift)
lastPdtEntryAddr uintptr
lastPdtEntry *pageTableEntry
)
// If this table is not active we need to temporarily map it to the
// last entry in the active PDT so we can access it using the recursive
// virtual address scheme.
if activePdtFrame != pdt.pdtFrame {
lastPdtEntryAddr = activePdtFrame.Address() + (((1 << pageLevelBits[0]) - 1) << mem.PointerShift)
lastPdtEntry = (*pageTableEntry)(unsafe.Pointer(lastPdtEntryAddr))
lastPdtEntry.SetFrame(pdt.pdtFrame)
flushTLBEntryFn(lastPdtEntryAddr)
}
err := unmapFn(page)
if activePdtFrame != pdt.pdtFrame {
lastPdtEntry.SetFrame(activePdtFrame)
flushTLBEntryFn(lastPdtEntryAddr)
}
return err
}
// Activate enables this page directory table and flushes the TLB
func (pdt PageDirectoryTable) Activate() {
switchPDTFn(pdt.pdtFrame.Address())
}