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:
parent
636220ab1d
commit
b4f4a9a738
@ -6,12 +6,14 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/achilleasa/gopher-os/kernel/mem"
|
"github.com/achilleasa/gopher-os/kernel/mem"
|
||||||
|
"github.com/achilleasa/gopher-os/kernel/mem/pmm/allocator"
|
||||||
"github.com/achilleasa/gopher-os/kernel/mem/vmm"
|
"github.com/achilleasa/gopher-os/kernel/mem/vmm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mapFn = vmm.Map
|
mapFn = vmm.Map
|
||||||
earlyReserveRegionFn = vmm.EarlyReserveRegion
|
earlyReserveRegionFn = vmm.EarlyReserveRegion
|
||||||
|
frameAllocFn = allocator.AllocFrame
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:linkname mSysStatInc runtime.mSysStatInc
|
//go:linkname mSysStatInc runtime.mSysStatInc
|
||||||
@ -65,6 +67,39 @@ func sysMap(virtAddr unsafe.Pointer, size uintptr, reserved bool, sysStat *uint6
|
|||||||
return unsafe.Pointer(regionStartAddr)
|
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() {
|
func init() {
|
||||||
// Dummy calls so the compiler does not optimize away the functions in
|
// Dummy calls so the compiler does not optimize away the functions in
|
||||||
// this file.
|
// this file.
|
||||||
@ -76,4 +111,5 @@ func init() {
|
|||||||
|
|
||||||
sysReserve(zeroPtr, 0, &reserved)
|
sysReserve(zeroPtr, 0, &reserved)
|
||||||
sysMap(zeroPtr, 0, reserved, &stat)
|
sysMap(zeroPtr, 0, reserved, &stat)
|
||||||
|
sysAlloc(0, &stat)
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/achilleasa/gopher-os/kernel"
|
"github.com/achilleasa/gopher-os/kernel"
|
||||||
"github.com/achilleasa/gopher-os/kernel/mem"
|
"github.com/achilleasa/gopher-os/kernel/mem"
|
||||||
"github.com/achilleasa/gopher-os/kernel/mem/pmm"
|
"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"
|
"github.com/achilleasa/gopher-os/kernel/mem/vmm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -130,3 +131,107 @@ func TestSysMap(t *testing.T) {
|
|||||||
sysMap(nil, 0, false, nil)
|
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))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user