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.InitTerminal()
hal.ActiveTerminal.Clear() 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) 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 tracks the last allocated frame number.
lastAllocFrame pmm.Frame 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. // init sets up the boot memory allocator internal state.
func (alloc *bootMemAllocator) init() { func (alloc *bootMemAllocator) init(kernelStart, kernelEnd uintptr) {
// TODO // 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 // 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) regionStartFrame := pmm.Frame(((region.PhysAddress + pageSizeMinus1) & ^pageSizeMinus1) >> mem.PageShift)
regionEndFrame := pmm.Frame(((region.PhysAddress+region.Length) & ^pageSizeMinus1)>>mem.PageShift) - 1 regionEndFrame := pmm.Frame(((region.PhysAddress+region.Length) & ^pageSizeMinus1)>>mem.PageShift) - 1
// Ignore already allocated regions // Skip over already allocated regions
if alloc.allocCount != 0 && alloc.lastAllocFrame >= regionEndFrame { if alloc.lastAllocFrame >= regionEndFrame {
return true return true
} }
// The last allocated frame will be either pointing to a // If last frame used a different region and the kernel image
// previous region or will point inside this region. In the // is located at the beginning of this region OR we are in
// first case (or if this is the first allocation) we select // current region but lastAllocFrame + 1 points to the kernel
// the start frame for this region. In the latter case we // start we need to jump to the page following the kernel end
// select the next available frame. // frame
if alloc.allocCount == 0 || alloc.lastAllocFrame < regionStartFrame { 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 alloc.lastAllocFrame = regionStartFrame
} else { } 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++ 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 err = nil
return false return false
}) })
@ -100,12 +127,17 @@ func (alloc *bootMemAllocator) printMemoryMap() {
} }
return true 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. // Init sets up the kernel physical memory allocation sub-system.
func Init() *kernel.Error { func Init(kernelStart, kernelEnd uintptr) *kernel.Error {
earlyAllocator.init() earlyAllocator.init(kernelStart, kernelEnd)
earlyAllocator.printMemoryMap() earlyAllocator.printMemoryMap()
return nil return nil
} }

View File

@ -13,34 +13,84 @@ import (
func TestBootMemoryAllocator(t *testing.T) { func TestBootMemoryAllocator(t *testing.T) {
multiboot.SetInfoPtr(uintptr(unsafe.Pointer(&multibootMemoryMap[0]))) multiboot.SetInfoPtr(uintptr(unsafe.Pointer(&multibootMemoryMap[0])))
// region 1 extents get rounded to [0, 9f000] and provides 159 frames [0 to 158] specs := []struct {
// region 1 uses the original extents [100000 - 7fe0000] and provides 32480 frames [256-32735] kernelStart, kernelEnd uintptr
var totalFreeFrames uint64 = 159 + 32480 expAllocCount uint64
}{
var ( {
alloc bootMemAllocator // the kernel is loaded in a reserved memory region
allocFrameCount uint64 0xa0000,
) 0xa0000,
for { // region 1 extents get rounded to [0, 9f000] and provides 159 frames [0 to 158]
frame, err := alloc.AllocFrame() // region 1 uses the original extents [100000 - 7fe0000] and provides 32480 frames [256-32735]
if err != nil { 159 + 32480,
if err == errBootAllocOutOfMemory { },
break {
} // the kernel is loaded at the beginning of region 1 taking 2.5 pages
t.Fatalf("[frame %d] unexpected allocator error: %v", allocFrameCount, err) 0x0,
} 0x2800,
allocFrameCount++ // region 1 extents get rounded to [0, 9f000] and provides 159 frames [0 to 158]; out of these
if frame != alloc.lastAllocFrame { // frames 0,1 and 2 (round up kernel end) are used by the kernel
t.Errorf("[frame %d] expected allocated frame to be %d; got %d", allocFrameCount, alloc.lastAllocFrame, frame) // region 1 uses the original extents [100000 - 7fe0000] and provides 32480 frames [256-32735]
} 159 - 3 + 32480,
},
if !frame.Valid() { {
t.Errorf("[frame %d] expected IsValid() to return true", allocFrameCount) // 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 { var alloc bootMemAllocator
t.Fatalf("expected allocator to allocate %d frames; allocated %d", totalFreeFrames, allocFrameCount) 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() fb := mockTTY()
multiboot.SetInfoPtr(uintptr(unsafe.Pointer(&multibootMemoryMap[0]))) multiboot.SetInfoPtr(uintptr(unsafe.Pointer(&multibootMemoryMap[0])))
Init() Init(0x100000, 0x1fa7c8)
var buf bytes.Buffer var buf bytes.Buffer
for i := 0; i < len(fb); i += 2 { for i := 0; i < len(fb); i += 2 {
@ -58,7 +108,7 @@ func TestAllocatorPackageInit(t *testing.T) {
buf.WriteByte(fb[i]) 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 { if got := buf.String(); got != exp {
t.Fatalf("expected printMemoryMap to generate the following output:\n%q\ngot:\n%q", exp, got) t.Fatalf("expected printMemoryMap to generate the following output:\n%q\ngot:\n%q", exp, got)
} }