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"
|
||||
|
||||
"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)
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user