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

Provide replacement for runtime.sysAlloc

This commit is contained in:
Achilleas Anagnostopoulos 2017-06-30 07:55:12 +01:00
parent 636220ab1d
commit b4f4a9a738
2 changed files with 141 additions and 0 deletions

View File

@ -6,12 +6,14 @@ import (
"unsafe"
"github.com/achilleasa/gopher-os/kernel/mem"
"github.com/achilleasa/gopher-os/kernel/mem/pmm/allocator"
"github.com/achilleasa/gopher-os/kernel/mem/vmm"
)
var (
mapFn = vmm.Map
earlyReserveRegionFn = vmm.EarlyReserveRegion
frameAllocFn = allocator.AllocFrame
)
//go:linkname mSysStatInc runtime.mSysStatInc
@ -65,6 +67,39 @@ func sysMap(virtAddr unsafe.Pointer, size uintptr, reserved bool, sysStat *uint6
return unsafe.Pointer(regionStartAddr)
}
// sysAlloc reserves enough phsysical frames to satisfy the allocation request
// and establishes a contiguous virtual page mapping for them returning back
// the pointer to the virtual region start.
//
// This function replaces runtime.sysMap and is required for initializing the
// Go allocator.
//
//go:redirect-from runtime.sysAlloc
//go:nosplit
func sysAlloc(size uintptr, sysStat *uint64) unsafe.Pointer {
regionSize := (mem.Size(size) + mem.PageSize - 1) & ^(mem.PageSize - 1)
regionStartAddr, err := earlyReserveRegionFn(regionSize)
if err != nil {
return unsafe.Pointer(uintptr(0))
}
mapFlags := vmm.FlagPresent | vmm.FlagNoExecute | vmm.FlagRW
pageCount := regionSize >> mem.PageShift
for page := vmm.PageFromAddress(regionStartAddr); pageCount > 0; pageCount, page = pageCount-1, page+1 {
frame, err := frameAllocFn()
if err != nil {
return unsafe.Pointer(uintptr(0))
}
if err = mapFn(page, frame, mapFlags); err != nil {
return unsafe.Pointer(uintptr(0))
}
}
mSysStatInc(sysStat, uintptr(regionSize))
return unsafe.Pointer(regionStartAddr)
}
func init() {
// Dummy calls so the compiler does not optimize away the functions in
// this file.
@ -76,4 +111,5 @@ func init() {
sysReserve(zeroPtr, 0, &reserved)
sysMap(zeroPtr, 0, reserved, &stat)
sysAlloc(0, &stat)
}

View File

@ -7,6 +7,7 @@ import (
"github.com/achilleasa/gopher-os/kernel"
"github.com/achilleasa/gopher-os/kernel/mem"
"github.com/achilleasa/gopher-os/kernel/mem/pmm"
"github.com/achilleasa/gopher-os/kernel/mem/pmm/allocator"
"github.com/achilleasa/gopher-os/kernel/mem/vmm"
)
@ -130,3 +131,107 @@ func TestSysMap(t *testing.T) {
sysMap(nil, 0, false, nil)
})
}
func TestSysAlloc(t *testing.T) {
defer func() {
earlyReserveRegionFn = vmm.EarlyReserveRegion
mapFn = vmm.Map
frameAllocFn = allocator.AllocFrame
}()
t.Run("success", func(t *testing.T) {
specs := []struct {
reqSize mem.Size
expMapCallCount int
}{
// exact multiple of page size
{4 * mem.PageSize, 4},
// round up to nearest page size
{(4 * mem.PageSize) + 1, 5},
}
expRegionStartAddr := uintptr(10 * mem.PageSize)
earlyReserveRegionFn = func(_ mem.Size) (uintptr, *kernel.Error) {
return expRegionStartAddr, nil
}
frameAllocFn = func() (pmm.Frame, *kernel.Error) {
return pmm.Frame(0), nil
}
for specIndex, spec := range specs {
var (
sysStat uint64
mapCallCount int
)
mapFn = func(_ vmm.Page, _ pmm.Frame, flags vmm.PageTableEntryFlag) *kernel.Error {
expFlags := vmm.FlagPresent | vmm.FlagNoExecute | vmm.FlagRW
if flags != expFlags {
t.Errorf("[spec %d] expected map flags to be %d; got %d", specIndex, expFlags, flags)
}
mapCallCount++
return nil
}
if got := sysAlloc(uintptr(spec.reqSize), &sysStat); uintptr(got) != expRegionStartAddr {
t.Errorf("[spec %d] expected sysAlloc to return address 0x%x; got 0x%x", specIndex, expRegionStartAddr, uintptr(got))
}
if mapCallCount != spec.expMapCallCount {
t.Errorf("[spec %d] expected vmm.Map call count to be %d; got %d", specIndex, spec.expMapCallCount, mapCallCount)
}
if exp := uint64(spec.expMapCallCount << mem.PageShift); sysStat != exp {
t.Errorf("[spec %d] expected stat counter to be %d; got %d", specIndex, exp, sysStat)
}
}
})
t.Run("earlyReserveRegion fails", func(t *testing.T) {
earlyReserveRegionFn = func(rsvSize mem.Size) (uintptr, *kernel.Error) {
return 0, &kernel.Error{Module: "test", Message: "consumed available address space"}
}
var sysStat uint64
if got := sysAlloc(1, &sysStat); got != unsafe.Pointer(uintptr(0)) {
t.Fatalf("expected sysAlloc to return 0x0 if EarlyReserveRegion returns an error; got 0x%x", uintptr(got))
}
})
t.Run("frame allocation fails", func(t *testing.T) {
expRegionStartAddr := uintptr(10 * mem.PageSize)
earlyReserveRegionFn = func(rsvSize mem.Size) (uintptr, *kernel.Error) {
return expRegionStartAddr, nil
}
frameAllocFn = func() (pmm.Frame, *kernel.Error) {
return pmm.InvalidFrame, &kernel.Error{Module: "test", Message: "out of memory"}
}
var sysStat uint64
if got := sysAlloc(1, &sysStat); got != unsafe.Pointer(uintptr(0)) {
t.Fatalf("expected sysAlloc to return 0x0 if AllocFrame returns an error; got 0x%x", uintptr(got))
}
})
t.Run("map fails", func(t *testing.T) {
expRegionStartAddr := uintptr(10 * mem.PageSize)
earlyReserveRegionFn = func(rsvSize mem.Size) (uintptr, *kernel.Error) {
return expRegionStartAddr, nil
}
frameAllocFn = func() (pmm.Frame, *kernel.Error) {
return pmm.Frame(0), nil
}
mapFn = func(_ vmm.Page, _ pmm.Frame, _ vmm.PageTableEntryFlag) *kernel.Error {
return &kernel.Error{Module: "test", Message: "map failed"}
}
var sysStat uint64
if got := sysAlloc(1, &sysStat); got != unsafe.Pointer(uintptr(0)) {
t.Fatalf("expected sysAlloc to return 0x0 if AllocFrame returns an error; got 0x%x", uintptr(got))
}
})
}