diff --git a/kernel/mem/vmm/addr_space.go b/kernel/mem/vmm/addr_space.go new file mode 100644 index 0000000..695168c --- /dev/null +++ b/kernel/mem/vmm/addr_space.go @@ -0,0 +1,35 @@ +package vmm + +import ( + "github.com/achilleasa/gopher-os/kernel" + "github.com/achilleasa/gopher-os/kernel/mem" +) + +var ( + // earlyReserveLastUsed tracks the last reserved page address and is + // decreased after each allocation request. Initially, it points to + // tempMappingAddr which coincides with the end of the kernel address + // space. + earlyReserveLastUsed = tempMappingAddr + + errEarlyReserveNoSpace = &kernel.Error{Module: "early_reserve", Message: "remaining virtual address space not large enough to satisfy reservation request"} +) + +// EarlyReserveRegion reserves a page-aligned contiguous virtual memory region +// with the requested size in the kernel address space and returns its virtual +// address. If size is not a multiple of mem.PageSize it will be automatically +// rounded up. +// +// This function allocates regions starting at the end of the kernel address +// space. It should only be used during the early stages of kernel initialization. +func EarlyReserveRegion(size mem.Size) (uintptr, *kernel.Error) { + size = (size + (mem.PageSize - 1)) & ^(mem.PageSize - 1) + + // reserving a region of the requested size will cause an underflow + if uintptr(size) > earlyReserveLastUsed { + return 0, errEarlyReserveNoSpace + } + + earlyReserveLastUsed -= uintptr(size) + return earlyReserveLastUsed, nil +} diff --git a/kernel/mem/vmm/addr_space_test.go b/kernel/mem/vmm/addr_space_test.go new file mode 100644 index 0000000..e457381 --- /dev/null +++ b/kernel/mem/vmm/addr_space_test.go @@ -0,0 +1,29 @@ +package vmm + +import ( + "runtime" + "testing" +) + +func TestEarlyReserveAmd64(t *testing.T) { + if runtime.GOARCH != "amd64" { + t.Skip("test requires amd64 runtime; skipping") + } + + defer func(origLastUsed uintptr) { + earlyReserveLastUsed = origLastUsed + }(earlyReserveLastUsed) + + earlyReserveLastUsed = 4096 + next, err := EarlyReserveRegion(42) + if err != nil { + t.Fatal(err) + } + if exp := uintptr(0); next != exp { + t.Fatal("expected reservation request to be rounded to nearest page") + } + + if _, err = EarlyReserveRegion(1); err != errEarlyReserveNoSpace { + t.Fatalf("expected to get errEarlyReserveNoSpace; got %v", err) + } +}