diff --git a/kernel/kmain/kmain.go b/kernel/kmain/kmain.go index e9d3f63..8d84538 100644 --- a/kernel/kmain/kmain.go +++ b/kernel/kmain/kmain.go @@ -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) } } diff --git a/kernel/mem/pmm/allocator/bootmem.go b/kernel/mem/pmm/allocator/bootmem.go index 23b1c5d..4f6910c 100644 --- a/kernel/mem/pmm/allocator/bootmem.go +++ b/kernel/mem/pmm/allocator/bootmem.go @@ -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 } diff --git a/kernel/mem/pmm/allocator/bootmem_test.go b/kernel/mem/pmm/allocator/bootmem_test.go index 713dd3c..f6964c5 100644 --- a/kernel/mem/pmm/allocator/bootmem_test.go +++ b/kernel/mem/pmm/allocator/bootmem_test.go @@ -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) }