Add A1 memory allocator
This commit is contained in:
1 parent
103205bff2
commit
7b09c9beed
3 files changed
+58
-1
No files matched your search
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* A1 memory allocator.
|
||||
*/
|
||||
module hulk.hurl.a1;
|
||||
|
||||
import hulk.hurl;
|
||||
import hulk.hippo;
|
||||
|
||||
/**
|
||||
* The A1 memory allocator is a one-shot memory allocator for kernel memory
|
||||
* that is allocated but never freed.
|
||||
*/
|
||||
struct A1
|
||||
{
|
||||
/**
|
||||
* Number of bytes allocated in the A1 region so far.
|
||||
*/
|
||||
private static __gshared size_t allocated;
|
||||
|
||||
/**
|
||||
* Allocate memory.
|
||||
*
|
||||
* @param size
|
||||
* Size of memory to allocate.
|
||||
*
|
||||
* @return Address of allocated memory. This address will always be aligned
|
||||
* to a multiple of 16 bytes.
|
||||
*/
|
||||
public static void * allocate(size_t size)
|
||||
{
|
||||
/* Round size up to a multiple of 16. */
|
||||
size = (size + 0xFu) & ~0xFu;
|
||||
|
||||
ulong address = Hurl.A1_BASE + allocated;
|
||||
|
||||
ulong mapped_limit = (address + 0xFFFu) & ~0xFFFu;
|
||||
|
||||
allocated += size;
|
||||
|
||||
ulong desired_limit = (Hurl.A1_BASE + allocated + 0xFFFu) & ~0xFFFu;
|
||||
|
||||
while (desired_limit > mapped_limit)
|
||||
{
|
||||
void * page = Hippo.allocate_page();
|
||||
Hurl.map(Hurl.A1_BASE + mapped_limit, page, PT_WRITABLE);
|
||||
mapped_limit += PAGE_SIZE;
|
||||
}
|
||||
|
||||
return cast(void *)address;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
/**
|
||||
* HURL, the HOS Unreal Region Locator.
|
||||
*
|
||||
* HURL provides virtual memory management for HULK.
|
||||
*/
|
||||
module hulk.hurl.hurl;
|
||||
|
||||
public import hulk.pagetable;
|
||||
import hulk.cpu;
|
||||
import hulk.hippo;
|
||||
import hulk.memory;
|
||||
import hulk.klog;
|
||||
import hulk.bootinfo;
|
||||
import hulk.header;
|
||||
import hulk.linker_addresses;
|
||||
|
||||
struct Hurl
|
||||
{
|
||||
/** HULK base address. */
|
||||
enum ulong HULK_BASE = 0xFFFF_8000_0000_0000u;
|
||||
|
||||
/** HULK stack top address. */
|
||||
enum ulong HULK_STACK_TOP = 0xFFFF_8001_0000_0000u;
|
||||
|
||||
/** HULK framebuffer address. */
|
||||
enum ulong HULK_FRAMEBUFFER = 0xFFFF_8001_0000_0000u;
|
||||
|
||||
/** A1 allocator range base address. */
|
||||
enum ulong A1_BASE = 0xFFFF_8002_0000_0000u;
|
||||
|
||||
/**
|
||||
* Pointer to the base page table.
|
||||
*/
|
||||
private static __gshared PageTable * m_pt_base;
|
||||
|
||||
/**
|
||||
* Build HULK page tables.
|
||||
*
|
||||
* @param physical_address_limit
|
||||
* Limit of physical memory.
|
||||
*/
|
||||
private static void build_page_tables(HulkHeader * header, size_t physical_address_limit)
|
||||
{
|
||||
m_pt_base = allocate_pt();
|
||||
/* Identity map all physical RAM. */
|
||||
map_range(0u,
|
||||
0u,
|
||||
physical_address_limit,
|
||||
PT_WRITABLE | PT_NO_EXECUTE);
|
||||
ulong phys_address = header.bootinfo.hulk_phys;
|
||||
ulong virt_address = HULK_BASE;
|
||||
/* Map HULK header region. */
|
||||
map_range(virt_address,
|
||||
phys_address,
|
||||
LinkerAddresses.hulk_header_size,
|
||||
PT_NO_EXECUTE);
|
||||
phys_address += LinkerAddresses.hulk_header_size;
|
||||
virt_address += LinkerAddresses.hulk_header_size;
|
||||
/* Map HULK text region. */
|
||||
map_range(virt_address,
|
||||
phys_address,
|
||||
LinkerAddresses.hulk_text_size,
|
||||
0u);
|
||||
phys_address += LinkerAddresses.hulk_text_size;
|
||||
virt_address += LinkerAddresses.hulk_text_size;
|
||||
/* Map HULK rodata region. */
|
||||
map_range(virt_address,
|
||||
phys_address,
|
||||
LinkerAddresses.hulk_rodata_size,
|
||||
PT_NO_EXECUTE);
|
||||
phys_address += LinkerAddresses.hulk_rodata_size;
|
||||
virt_address += LinkerAddresses.hulk_rodata_size;
|
||||
/* Map HULK data region. */
|
||||
map_range(virt_address,
|
||||
phys_address,
|
||||
LinkerAddresses.hulk_data_size,
|
||||
PT_WRITABLE | PT_NO_EXECUTE);
|
||||
virt_address += LinkerAddresses.hulk_data_size;
|
||||
/* Map HULK BSS region. */
|
||||
map_range(virt_address,
|
||||
header.bootinfo.bss_phys,
|
||||
LinkerAddresses.hulk_bss_size,
|
||||
PT_WRITABLE | PT_NO_EXECUTE);
|
||||
/* Map HULK stack. */
|
||||
map_range(HULK_STACK_TOP - header.stack_size,
|
||||
header.bootinfo.stack_phys,
|
||||
header.stack_size,
|
||||
PT_WRITABLE | PT_NO_EXECUTE);
|
||||
/* Map device framebuffer. */
|
||||
map_range(HULK_FRAMEBUFFER,
|
||||
cast(ulong)header.bootinfo.fb.buffer,
|
||||
header.bootinfo.fb.height * header.bootinfo.fb.stride * 4u,
|
||||
PT_WRITABLE | PT_NO_EXECUTE);
|
||||
/* Map framebuffer buffer1. */
|
||||
map_range(cast(ulong)header.bootinfo.fb_buffer1_phys,
|
||||
cast(ulong)header.bootinfo.fb_buffer1_phys,
|
||||
header.bootinfo.fb.height * header.bootinfo.fb.stride * 4u,
|
||||
PT_WRITABLE | PT_NO_EXECUTE);
|
||||
write_cr3(cast(ulong)m_pt_base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reclaim the pages that were used for the bootloader page tables.
|
||||
*
|
||||
* @param pt
|
||||
* The page table to reclaim pages from.
|
||||
* @param level
|
||||
* Page table level (internally used while recursing the page tables).
|
||||
*
|
||||
* @return Number of pages reclaimed.
|
||||
*/
|
||||
private static size_t reclaim_bootloader_page_table_pages(PageTable * pt, size_t level = 0u)
|
||||
{
|
||||
map(pt, pt, PT_WRITABLE | PT_NO_EXECUTE);
|
||||
size_t reclaimed_pages;
|
||||
for (size_t i = 0u; i < PageTable.N_ENTRIES; i++)
|
||||
{
|
||||
PageTableEntry pte = (*pt)[i];
|
||||
if (pte.present && (level < 2u) && !pte.huge)
|
||||
{
|
||||
/* For the first two levels of page tables, first
|
||||
* recurse and free pages from the lower level page
|
||||
* tables before reclaiming this entry. */
|
||||
reclaimed_pages += reclaim_bootloader_page_table_pages(pte.follow(), level + 1u);
|
||||
}
|
||||
}
|
||||
Hippo.free_page(pt);
|
||||
reclaimed_pages++;
|
||||
return reclaimed_pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize HURL.
|
||||
*
|
||||
* @param bootinfo HULK boot information structure.
|
||||
*/
|
||||
public static void initialize(HulkHeader * header)
|
||||
{
|
||||
PageTable * bootloader_pt_base = cast(PageTable *)read_cr3();
|
||||
|
||||
/*
|
||||
* Ok, what we do here is iterate through all of the memory map regions
|
||||
* in the bootinfo memory_map array and mark each page that is not in
|
||||
* use in a reserved range as available. Within the available memory
|
||||
* regions, we have to watch out for the following items:
|
||||
* 1) HULK binary (header + text + rodata + data)
|
||||
* 2) Framebuffer
|
||||
* In addition to these ranges, there are also pages for the following
|
||||
* purposes that are not represented in the memory map from the
|
||||
* bootloader:
|
||||
* 3) HULK BSS
|
||||
* 4) HULK stack
|
||||
* 5) bootloader page table pages
|
||||
* 6) framebuffer buffer1
|
||||
*/
|
||||
size_t usable_memory;
|
||||
size_t physical_address_limit;
|
||||
const(size_t) fb_size = (header.bootinfo.fb.height * header.bootinfo.fb.stride * 4u + PAGE_SIZE - 1u) & ~(PAGE_SIZE - 1u);
|
||||
ulong[2][2] reserved = [
|
||||
[header.bootinfo.hulk_phys, LinkerAddresses.hulk_binary_size],
|
||||
[cast(ulong)header.bootinfo.fb.buffer, fb_size],
|
||||
];
|
||||
for (size_t ri = 0u; ri < reserved.length; ri++)
|
||||
{
|
||||
reserved[ri][1] += reserved[ri][0];
|
||||
}
|
||||
for (size_t bii = 0u; bii < header.bootinfo.memory_map_count; bii++)
|
||||
{
|
||||
if ((header.bootinfo.memory_map[bii].type == BootInfo.MemoryRegion.Type.Bootloader) ||
|
||||
(header.bootinfo.memory_map[bii].type == BootInfo.MemoryRegion.Type.Conventional))
|
||||
{
|
||||
ulong phys = header.bootinfo.memory_map[bii].base;
|
||||
ulong phys_end = phys + header.bootinfo.memory_map[bii].size;
|
||||
if (phys_end > physical_address_limit)
|
||||
{
|
||||
physical_address_limit = phys_end;
|
||||
}
|
||||
usable_memory += header.bootinfo.memory_map[bii].size;
|
||||
while (phys < phys_end)
|
||||
{
|
||||
bool is_reserved = false;
|
||||
for (size_t ri = 0u; ri < reserved.length; ri++)
|
||||
{
|
||||
if ((reserved[ri][0] <= phys) && (phys < reserved[ri][1]))
|
||||
{
|
||||
is_reserved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!is_reserved)
|
||||
{
|
||||
Hippo.free_page(phys);
|
||||
}
|
||||
phys += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that we have available physical pages to allocate from, we can
|
||||
* build new page tables to replace the bootloader page tables.
|
||||
*/
|
||||
build_page_tables(header, physical_address_limit);
|
||||
|
||||
/*
|
||||
* After we have switched to the newly constructed page tables, we can
|
||||
* iterate through and free the bootloader page tables. They are most
|
||||
* likely already mapped because we just identity mapped every page up
|
||||
* to the physical_address_limit determined above. But just in case the
|
||||
* bootloader used the last pages of RAM for the page table pages, we
|
||||
* will make sure to map them as we traverse them.
|
||||
*/
|
||||
size_t reclaimed_bootloader_page_table_memory = reclaim_bootloader_page_table_pages(bootloader_pt_base) << 10u;
|
||||
usable_memory += reclaimed_bootloader_page_table_memory;
|
||||
|
||||
usable_memory += LinkerAddresses.hulk_bss_size;
|
||||
usable_memory += header.stack_size;
|
||||
usable_memory += fb_size;
|
||||
Klog.writefln("Usable memory: %uKB", usable_memory >> 10u);
|
||||
Klog.writefln("Kernel size: %uKB", (LinkerAddresses.hulk_binary_size + LinkerAddresses.hulk_bss_size + header.stack_size) >> 10u);
|
||||
}
|
||||
|
||||
public static void map(T, U)(T virtual, U physical, ulong flags)
|
||||
{
|
||||
PageTable * pt = m_pt_base;
|
||||
for (size_t level = 0; level < 4u; level++)
|
||||
{
|
||||
PageTableEntry * ppte = pt.entry(virtual, level);
|
||||
if (level < 3u)
|
||||
{
|
||||
PageTableEntry pte = *ppte;
|
||||
if (pte.present)
|
||||
{
|
||||
pt = pte.follow();
|
||||
}
|
||||
else
|
||||
{
|
||||
PageTable * next_pt = allocate_pt();
|
||||
*ppte = PageTableEntry(next_pt, PT_WRITABLE | PT_PRESENT);
|
||||
pt = next_pt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*ppte = PageTableEntry(cast(ulong)physical, flags | PT_PRESENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void map_range(size_t virtual, size_t physical, size_t length, ulong flags)
|
||||
{
|
||||
size_t end = virtual + length;
|
||||
while (virtual < end)
|
||||
{
|
||||
map(virtual, physical, flags);
|
||||
virtual += PAGE_SIZE;
|
||||
physical += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
public static void identity_map_range(size_t address, size_t length, ulong flags)
|
||||
{
|
||||
size_t end = address + length;
|
||||
for (size_t page = address & ~0xFFFu; page < end; page += PAGE_SIZE)
|
||||
{
|
||||
map(page, page, flags);
|
||||
}
|
||||
}
|
||||
|
||||
public static void debug_lookup(void * address)
|
||||
{
|
||||
Klog.writefln("Debugging page table lookup of 0x%x", address);
|
||||
PageTable * pt = m_pt_base;
|
||||
for (size_t level = 0; level < 4u; level++)
|
||||
{
|
||||
PageTableEntry pte = *pt.entry(address, level);
|
||||
Klog.writefln("Level %u, index %u, entry = 0x%x", level, pt.index(address, level), pte);
|
||||
if (pte.present)
|
||||
{
|
||||
pt = pte.follow();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (pte.huge)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static PageTable * allocate_pt()
|
||||
{
|
||||
PageTable * pt = cast(PageTable *)Hippo.allocate_page();
|
||||
memset64(pt, 0u, PAGE_SIZE / 8u);
|
||||
return pt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module hulk.hurl;
|
||||
|
||||
public import hulk.hurl.hurl;
|
||||
Reference in new issue
Block a user