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

114 lines
4.1 KiB
Go

package allocator
import (
"github.com/achilleasa/gopher-os/kernel"
"github.com/achilleasa/gopher-os/kernel/hal/multiboot"
"github.com/achilleasa/gopher-os/kernel/kfmt/early"
"github.com/achilleasa/gopher-os/kernel/mem"
"github.com/achilleasa/gopher-os/kernel/mem/pmm"
)
var (
// EarlyAllocator points to a static instance of the boot memory allocator
// which is used to bootstrap the kernel before initializing a more
// advanced memory allocator.
EarlyAllocator BootMemAllocator
errBootAllocUnsupportedPageSize = &kernel.Error{Module: "boot_mem_alloc", Message: "allocator only support allocation requests of order(0)"}
errBootAllocOutOfMemory = &kernel.Error{Module: "boot_mem_alloc", Message: "out of memory"}
)
// BootMemAllocator implements a rudimentary physical memory allocator which is used
// to bootstrap the kernel.
//
// The allocator implementation uses the memory region information provided by
// the bootloader to detect free memory blocks and return the next available
// free frame.
//
// Allocations are tracked via an internal counter that contains the last
// allocated frame index. The system memory regions are mapped into a linear
// page index by aligning the region start address to the system's page size
// and then dividing by the page size.
//
// Due to the way that the allocator works, it is not possible to free
// allocated pages. Once the kernel is properly initialized, the allocated
// blocks will be handed over to a more advanced memory allocator that does
// support freeing.
type BootMemAllocator struct {
// allocCount tracks the total number of allocated frames.
allocCount uint64
// lastAllocIndex tracks the last allocated frame index.
lastAllocIndex int64
}
// Init sets up the boot memory allocator internal state and prints out the
// system memory map.
func (alloc *BootMemAllocator) Init() {
alloc.lastAllocIndex = -1
early.Printf("[boot_mem_alloc] system memory map:\n")
var totalFree mem.Size
multiboot.VisitMemRegions(func(region *multiboot.MemoryMapEntry) bool {
early.Printf("\t[0x%10x - 0x%10x], size: %10d, type: %s\n", region.PhysAddress, region.PhysAddress+region.Length, region.Length, region.Type.String())
if region.Type == multiboot.MemAvailable {
totalFree += mem.Size(region.Length)
}
return true
})
early.Printf("[boot_mem_alloc] free memory: %dKb\n", uint64(totalFree/mem.Kb))
}
// AllocFrame scans the system memory regions reported by the bootloader and
// reserves the next available free frame.
//
// AllocFrame returns an error if no more memory can be allocated or when the
// requested page order is > 0.
func (alloc *BootMemAllocator) AllocFrame(order mem.PageOrder) (pmm.Frame, *kernel.Error) {
if order > 0 {
return pmm.InvalidFrame, errBootAllocUnsupportedPageSize
}
var (
foundPageIndex int64 = -1
regionStartPageIndex, regionEndPageIndex int64
)
multiboot.VisitMemRegions(func(region *multiboot.MemoryMapEntry) bool {
if region.Type != multiboot.MemAvailable {
return true
}
// Align region start address to a page boundary and find the start
// and end page indices for the region
regionStartPageIndex = int64(((mem.Size(region.PhysAddress) + (mem.PageSize - 1)) & ^(mem.PageSize - 1)) >> mem.PageShift)
regionEndPageIndex = int64(((mem.Size(region.PhysAddress+region.Length) - (mem.PageSize - 1)) & ^(mem.PageSize - 1)) >> mem.PageShift)
// Ignore already allocated regions
if alloc.lastAllocIndex >= regionEndPageIndex {
return true
}
// We found a block that can be allocated. The last allocated
// index will be either pointing to a previous region or will
// point inside this region. In the first case we just need to
// select the regionStartPageIndex. In the latter case we can
// simply select the next available page in the current region.
if alloc.lastAllocIndex < regionStartPageIndex {
foundPageIndex = regionStartPageIndex
} else {
foundPageIndex = alloc.lastAllocIndex + 1
}
return false
})
if foundPageIndex == -1 {
return pmm.InvalidFrame, errBootAllocOutOfMemory
}
alloc.allocCount++
alloc.lastAllocIndex = foundPageIndex
return pmm.Frame(foundPageIndex), nil
}