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

Exclude the region where the kernel is loaded when allocating frames

The linked.ld script is extended to include the _kernel_start and
_kernel_end symbols which are passed by the rt0 code to Kmain. The
allocator converts these addresses to a start/end frame index by
rounding down the kernel start address to the nearest page and rounding
up the kernel end address to the nearest page.

When allocating frames, the allocator will treat the region defined by
these 2 indices as reserved and skip over it.
This commit is contained in:
Achilleas Anagnostopoulos 2017-06-17 10:53:53 +01:00
parent c81fd8b758
commit 8698e7bc93
3 changed files with 124 additions and 42 deletions

View File

@ -24,7 +24,7 @@ func Kmain(multibootInfoPtr, kernelStart, kernelEnd uintptr) {
hal.InitTerminal()
hal.ActiveTerminal.Clear()
if err := allocator.Init(); err != nil {
if err := allocator.Init(kernelStart, kernelEnd); err != nil {
early.Printf("[%s] error: %s\n", err.Module, err.Message)
}
}

View File

@ -34,11 +34,22 @@ type bootMemAllocator struct {
// lastAllocFrame tracks the last allocated frame number.
lastAllocFrame pmm.Frame
// Keep track of kernel location so we exclude this region.
kernelStartAddr, kernelEndAddr uintptr
kernelStartFrame, kernelEndFrame pmm.Frame
}
// init sets up the boot memory allocator internal state.
func (alloc *bootMemAllocator) init() {
// TODO
func (alloc *bootMemAllocator) init(kernelStart, kernelEnd uintptr) {
// round down kernel start to the nearest page and round up kernel end
// to the nearest page.
pageSizeMinus1 := uintptr(mem.PageSize - 1)
alloc.kernelStartAddr = kernelStart
alloc.kernelEndAddr = kernelEnd
alloc.kernelStartFrame = pmm.Frame((kernelStart & ^pageSizeMinus1) >> mem.PageShift)
alloc.kernelEndFrame = pmm.Frame(((kernelEnd+pageSizeMinus1) & ^pageSizeMinus1)>>mem.PageShift) - 1
}
// AllocFrame scans the system memory regions reported by the bootloader and
@ -60,21 +71,37 @@ func (alloc *bootMemAllocator) AllocFrame() (pmm.Frame, *kernel.Error) {
regionStartFrame := pmm.Frame(((region.PhysAddress + pageSizeMinus1) & ^pageSizeMinus1) >> mem.PageShift)
regionEndFrame := pmm.Frame(((region.PhysAddress+region.Length) & ^pageSizeMinus1)>>mem.PageShift) - 1
// Ignore already allocated regions
if alloc.allocCount != 0 && alloc.lastAllocFrame >= regionEndFrame {
// Skip over already allocated regions
if alloc.lastAllocFrame >= regionEndFrame {
return true
}
// The last allocated frame will be either pointing to a
// previous region or will point inside this region. In the
// first case (or if this is the first allocation) we select
// the start frame for this region. In the latter case we
// select the next available frame.
if alloc.allocCount == 0 || alloc.lastAllocFrame < regionStartFrame {
// If last frame used a different region and the kernel image
// is located at the beginning of this region OR we are in
// current region but lastAllocFrame + 1 points to the kernel
// start we need to jump to the page following the kernel end
// frame
if (alloc.lastAllocFrame <= regionStartFrame && alloc.kernelStartFrame == regionStartFrame) ||
(alloc.lastAllocFrame <= regionEndFrame && alloc.lastAllocFrame+1 == alloc.kernelStartFrame) {
//fmt.Printf("last: %d, case: 1, set last: %d\n", alloc.lastAllocFrame, alloc.kernelEndFrame+1)
alloc.lastAllocFrame = alloc.kernelEndFrame + 1
} else if alloc.lastAllocFrame < regionStartFrame || alloc.allocCount == 0 {
// we are in the previous region and need to jump to this one OR
// this is the first allocation and the region begins at frame 0
//fmt.Printf("last: %d, case: 2, set last: %d\n", alloc.lastAllocFrame, regionStartFrame)
alloc.lastAllocFrame = regionStartFrame
} else {
// we are in the region and we can select the next frame
//fmt.Printf("last: %d, case: 3, set last: %d\n", alloc.lastAllocFrame, alloc.lastAllocFrame+1)
alloc.lastAllocFrame++
}
// The above adjustment might push lastAllocFrame outside of the
// region end (e.g kernel ends at last page in the region)
if alloc.lastAllocFrame > regionEndFrame {
return true
}
err = nil
return false
})
@ -100,12 +127,17 @@ func (alloc *bootMemAllocator) printMemoryMap() {
}
return true
})
early.Printf("[boot_mem_alloc] free memory: %dKb\n", uint64(totalFree/mem.Kb))
early.Printf("[boot_mem_alloc] available memory: %dKb\n", uint64(totalFree/mem.Kb))
early.Printf("[boot_mem_alloc] kernel loaded at 0x%x - 0x%x\n", alloc.kernelStartAddr, alloc.kernelEndAddr)
early.Printf("[boot_mem_alloc] size: %d bytes, reserved pages: %d\n",
uint64(alloc.kernelEndAddr-alloc.kernelStartAddr),
uint64(alloc.kernelEndFrame-alloc.kernelStartFrame+1),
)
}
// Init sets up the kernel physical memory allocation sub-system.
func Init() *kernel.Error {
earlyAllocator.init()
func Init(kernelStart, kernelEnd uintptr) *kernel.Error {
earlyAllocator.init(kernelStart, kernelEnd)
earlyAllocator.printMemoryMap()
return nil
}

View File

@ -13,34 +13,84 @@ import (
func TestBootMemoryAllocator(t *testing.T) {
multiboot.SetInfoPtr(uintptr(unsafe.Pointer(&multibootMemoryMap[0])))
// region 1 extents get rounded to [0, 9f000] and provides 159 frames [0 to 158]
// region 1 uses the original extents [100000 - 7fe0000] and provides 32480 frames [256-32735]
var totalFreeFrames uint64 = 159 + 32480
var (
alloc bootMemAllocator
allocFrameCount uint64
)
for {
frame, err := alloc.AllocFrame()
if err != nil {
if err == errBootAllocOutOfMemory {
break
}
t.Fatalf("[frame %d] unexpected allocator error: %v", allocFrameCount, err)
}
allocFrameCount++
if frame != alloc.lastAllocFrame {
t.Errorf("[frame %d] expected allocated frame to be %d; got %d", allocFrameCount, alloc.lastAllocFrame, frame)
}
if !frame.Valid() {
t.Errorf("[frame %d] expected IsValid() to return true", allocFrameCount)
}
specs := []struct {
kernelStart, kernelEnd uintptr
expAllocCount uint64
}{
{
// the kernel is loaded in a reserved memory region
0xa0000,
0xa0000,
// region 1 extents get rounded to [0, 9f000] and provides 159 frames [0 to 158]
// region 1 uses the original extents [100000 - 7fe0000] and provides 32480 frames [256-32735]
159 + 32480,
},
{
// the kernel is loaded at the beginning of region 1 taking 2.5 pages
0x0,
0x2800,
// region 1 extents get rounded to [0, 9f000] and provides 159 frames [0 to 158]; out of these
// frames 0,1 and 2 (round up kernel end) are used by the kernel
// region 1 uses the original extents [100000 - 7fe0000] and provides 32480 frames [256-32735]
159 - 3 + 32480,
},
{
// the kernel is loaded at the end of region 1 taking 2.5 pages
0x9c800,
0x9f000,
// region 1 extents get rounded to [0, 9f000] and provides 159 frames [0 to 158]; out of these
// frames 156,157 and 158 (round down kernel start) are used by the kernel
// region 1 uses the original extents [100000 - 7fe0000] and provides 32480 frames [256-32735]
159 - 3 + 32480,
},
{
// the kernel (after rounding) uses the entire region 1
0x123,
0x9fc00,
// region 1 extents get rounded to [0, 9f000] and provides 159 frames [0 to 158]; all are used
// by the kernel
// region 1 uses the original extents [100000 - 7fe0000] and provides 32480 frames [256-32735]
32480,
},
{
// the kernel is loaded at region 2 start + 2K taking 1.5 pages
0x100800,
0x102000,
// region 1 extents get rounded to [0, 9f000] and provides 159 frames [0 to 158]
// region 1 uses the original extents [100000 - 7fe0000] and provides 32480 frames [256-32735];
// out of these frames 256 (kernel start rounded down) and 257 is used by the kernel
159 + 32480 - 2,
},
}
if allocFrameCount != totalFreeFrames {
t.Fatalf("expected allocator to allocate %d frames; allocated %d", totalFreeFrames, allocFrameCount)
var alloc bootMemAllocator
for specIndex, spec := range specs {
alloc.allocCount = 0
alloc.lastAllocFrame = 0
alloc.init(spec.kernelStart, spec.kernelEnd)
for {
frame, err := alloc.AllocFrame()
if err != nil {
if err == errBootAllocOutOfMemory {
break
}
t.Errorf("[spec %d] [frame %d] unexpected allocator error: %v", specIndex, alloc.allocCount, err)
break
}
if frame != alloc.lastAllocFrame {
t.Errorf("[spec %d] [frame %d] expected allocated frame to be %d; got %d", specIndex, alloc.allocCount, alloc.lastAllocFrame, frame)
}
if !frame.Valid() {
t.Errorf("[spec %d] [frame %d] expected IsValid() to return true", specIndex, alloc.allocCount)
}
}
if alloc.allocCount != spec.expAllocCount {
t.Errorf("[spec %d] expected allocator to allocate %d frames; allocated %d", specIndex, spec.expAllocCount, alloc.allocCount)
}
}
}
@ -48,7 +98,7 @@ func TestAllocatorPackageInit(t *testing.T) {
fb := mockTTY()
multiboot.SetInfoPtr(uintptr(unsafe.Pointer(&multibootMemoryMap[0])))
Init()
Init(0x100000, 0x1fa7c8)
var buf bytes.Buffer
for i := 0; i < len(fb); i += 2 {
@ -58,7 +108,7 @@ func TestAllocatorPackageInit(t *testing.T) {
buf.WriteByte(fb[i])
}
exp := "[boot_mem_alloc] system memory map: [0x0000000000 - 0x000009fc00], size: 654336, type: available [0x000009fc00 - 0x00000a0000], size: 1024, type: reserved [0x00000f0000 - 0x0000100000], size: 65536, type: reserved [0x0000100000 - 0x0007fe0000], size: 133038080, type: available [0x0007fe0000 - 0x0008000000], size: 131072, type: reserved [0x00fffc0000 - 0x0100000000], size: 262144, type: reserved[boot_mem_alloc] free memory: 130559Kb"
exp := "[boot_mem_alloc] system memory map: [0x0000000000 - 0x000009fc00], size: 654336, type: available [0x000009fc00 - 0x00000a0000], size: 1024, type: reserved [0x00000f0000 - 0x0000100000], size: 65536, type: reserved [0x0000100000 - 0x0007fe0000], size: 133038080, type: available [0x0007fe0000 - 0x0008000000], size: 131072, type: reserved [0x00fffc0000 - 0x0100000000], size: 262144, type: reserved[boot_mem_alloc] available memory: 130559Kb[boot_mem_alloc] kernel loaded at 0x100000 - 0x1fa7c8[boot_mem_alloc] size: 1025992 bytes, reserved pages: 251"
if got := buf.String(); got != exp {
t.Fatalf("expected printMemoryMap to generate the following output:\n%q\ngot:\n%q", exp, got)
}