Build HELLO page tables from unused memory instead of scratch buffer

This commit is contained in:
Josh Holtrop 2022-11-02 22:49:42 -04:00
parent 0b326e2e36
commit 8aa01ba122
4 changed files with 99 additions and 75 deletions

View File

@ -5,7 +5,6 @@ module hello.hello;
import uefi;
import hello.console;
import hello.scratch;
import hulk.bootinfo;
import hulk.header;
import hulk.pagetable;
@ -16,6 +15,9 @@ import ldc.llvmasm;
__gshared EFI_SYSTEM_TABLE * st;
extern extern(C) __gshared ubyte _hulk_bin_start;
extern extern(C) __gshared ubyte _hulk_bin_end;
private align(4096) __gshared ubyte[1024 * 1024] scratch_buffer;
/** Index to the memory map region being used to allocate page tables. */
private __gshared size_t memory_map_page_table_alloc_index;
private HulkHeader * hulk_header()
{
@ -87,9 +89,9 @@ private bool in_qemu()
private bool set_graphics_mode()
{
uint max_horizontal_resolution = in_qemu() ? 1920u : 0xFFFFFFFFu;
UINTN buffer_size = scratch.free();
UINTN buffer_size = scratch_buffer.sizeof;
EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
EFI_HANDLE * handles = cast(EFI_HANDLE *)scratch.current();
EFI_HANDLE * handles = cast(EFI_HANDLE *)&scratch_buffer[0];
EFI_STATUS status = st.BootServices.LocateHandle(ByProtocol,
&gop_guid, null, &buffer_size, handles);
if (status != EFI_SUCCESS)
@ -148,10 +150,10 @@ private void get_memory_map(ulong * bss_phys, ulong * stack_phys, ulong * physic
{
immutable static ubyte[] efi_to_hulk_memory_map_type = [
BootInfo.MemoryRegion.Type.Reserved, // EfiReservedMemoryType
BootInfo.MemoryRegion.Type.Conventional, // EfiLoaderCode
BootInfo.MemoryRegion.Type.Conventional, // EfiLoaderData
BootInfo.MemoryRegion.Type.Conventional, // EfiBootServicesCode
BootInfo.MemoryRegion.Type.Conventional, // EfiBootServicesData
BootInfo.MemoryRegion.Type.Bootloader, // EfiLoaderCode
BootInfo.MemoryRegion.Type.Bootloader, // EfiLoaderData
BootInfo.MemoryRegion.Type.Bootloader, // EfiBootServicesCode
BootInfo.MemoryRegion.Type.Bootloader, // EfiBootServicesData
BootInfo.MemoryRegion.Type.Reserved, // EfiRuntimeServicesCode
BootInfo.MemoryRegion.Type.Reserved, // EfiRuntimeServicesData
BootInfo.MemoryRegion.Type.Conventional, // EfiConventionalMemory
@ -163,10 +165,10 @@ private void get_memory_map(ulong * bss_phys, ulong * stack_phys, ulong * physic
BootInfo.MemoryRegion.Type.PalCode, // EfiPalCode
];
*physical_address_limit = 0u;
UINTN memory_map_size = scratch.free();
UINTN memory_map_size = scratch_buffer.sizeof;
UINTN descriptor_size;
UINT32 descriptor_version;
ubyte * scratch_base = scratch.current();
ubyte * scratch_base = cast(ubyte *)&scratch_buffer[0];
UINTN status = st.BootServices.GetMemoryMap(
&memory_map_size,
cast(EFI_MEMORY_DESCRIPTOR *)scratch_base,
@ -235,12 +237,37 @@ private void get_memory_map(ulong * bss_phys, ulong * stack_phys, ulong * physic
}
}
private void * alloc_pt_page()
{
BootInfo.MemoryRegion * regions = &bootinfo().memory_map[0];
for (;;)
{
BootInfo.MemoryRegion * region = &bootinfo().memory_map[memory_map_page_table_alloc_index];
if ((region.type == BootInfo.MemoryRegion.Type.Conventional) && (region.size >= PAGE_SIZE))
{
void * addr = cast(void *)region.base;
region.base += PAGE_SIZE;
region.size -= PAGE_SIZE;
return addr;
}
memory_map_page_table_alloc_index++;
if (memory_map_page_table_alloc_index >= bootinfo().memory_map_count)
{
return null;
}
}
}
/**
* Allocate a new page table.
*/
private PageTable * new_page_table()
{
PageTable * pt = cast(PageTable *)scratch.alloc(1u);
PageTable * pt = cast(PageTable *)alloc_pt_page();
if (pt == null)
{
return null;
}
memset64(pt, 0u, 512);
return pt;
}
@ -252,7 +279,7 @@ private PageTable * new_page_table()
* @param dest_page Destination page address.
* @param pt_base Page table base address.
*/
private void map4k(ulong source_page, ulong dest_page, PageTable * pt_base)
private bool map4k(ulong source_page, ulong dest_page, PageTable * pt_base)
{
PageTable * pt = pt_base;
for (size_t level = 0; level < 4u; level++)
@ -270,6 +297,10 @@ private void map4k(ulong source_page, ulong dest_page, PageTable * pt_base)
if (level < 3u)
{
next_pt = new_page_table();
if (next_pt == null)
{
return false;
}
addr = cast(ulong)next_pt;
}
else
@ -280,6 +311,7 @@ private void map4k(ulong source_page, ulong dest_page, PageTable * pt_base)
pt = next_pt;
}
}
return true;
}
/**
@ -290,15 +322,19 @@ private void map4k(ulong source_page, ulong dest_page, PageTable * pt_base)
* @param size Region size.
* @param pt_base Page table base address.
*/
private void map4kregion(ulong source, ulong dest, size_t size, PageTable * pt_base)
private bool map4kregion(ulong source, ulong dest, size_t size, PageTable * pt_base)
{
ulong end = source + size;
while (source < end)
{
map4k(source, dest, pt_base);
if (!map4k(source, dest, pt_base))
{
return false;
}
source += 4096u;
dest += 4096u;
}
return true;
}
/**
@ -308,7 +344,7 @@ private void map4kregion(ulong source, ulong dest, size_t size, PageTable * pt_b
* @param dest_page Destination page address.
* @param pt_base Page table base address.
*/
private void map2m(ulong source_page, ulong dest_page, PageTable * pt_base)
private bool map2m(ulong source_page, ulong dest_page, PageTable * pt_base)
{
PageTable * pt = pt_base;
for (size_t level = 0; level < 3u; level++)
@ -325,6 +361,10 @@ private void map2m(ulong source_page, ulong dest_page, PageTable * pt_base)
if (level < 2u)
{
next_pt = new_page_table();
if (next_pt == null)
{
return false;
}
*ppte = PageTableEntry(next_pt, PT_WRITABLE | PT_PRESENT);
}
else
@ -334,6 +374,7 @@ private void map2m(ulong source_page, ulong dest_page, PageTable * pt_base)
pt = next_pt;
}
}
return true;
}
/**
@ -341,17 +382,27 @@ private void map2m(ulong source_page, ulong dest_page, PageTable * pt_base)
*
* @param pt_base Page table base address.
*/
private void map_hulk(PageTable * pt_base, ulong bss_phys, ulong stack_phys)
private bool map_hulk(PageTable * pt_base, ulong bss_phys, ulong stack_phys)
{
/* Map HULK bin region. */
ulong virt = hulk_virt_base_address();
map4kregion(virt, hulk_bin_phys(), hulk_bin_size(), pt_base);
if (!map4kregion(virt, hulk_bin_phys(), hulk_bin_size(), pt_base))
{
return false;
}
/* Map HULK bss region. */
virt += hulk_bin_size();
map4kregion(virt, bss_phys, hulk_bss_size(), pt_base);
if (!map4kregion(virt, bss_phys, hulk_bss_size(), pt_base))
{
return false;
}
/* Map HULK stack. */
virt = hulk_virt_stack_top() - hulk_stack_size();
map4kregion(virt, stack_phys, hulk_stack_size(), pt_base);
if (!map4kregion(virt, stack_phys, hulk_stack_size(), pt_base))
{
return false;
}
return true;
}
/**
@ -359,13 +410,20 @@ private void map_hulk(PageTable * pt_base, ulong bss_phys, ulong stack_phys)
*
* @param physical_address_limit Maximum physical address to identity map.
*/
private void build_page_tables(ulong physical_address_limit, ulong bss_phys, ulong stack_phys)
private bool build_page_tables(ulong physical_address_limit, ulong bss_phys, ulong stack_phys)
{
PageTable * pt_base = new_page_table();
if (pt_base == null)
{
return false;
}
/* Map physical RAM. */
for (size_t addr = 0u; addr < physical_address_limit; addr += (2u * 1024u * 1024u))
{
map2m(addr, addr, pt_base);
if (!map2m(addr, addr, pt_base))
{
return false;
}
}
/* Map any memory regions that are outside physical RAM. */
for (size_t i = 0u; i < bootinfo().memory_map_count; i++)
@ -373,18 +431,28 @@ private void build_page_tables(ulong physical_address_limit, ulong bss_phys, ulo
ulong addr = bootinfo().memory_map[i].base;
if (addr >= physical_address_limit)
{
map4kregion(addr, addr, bootinfo().memory_map[i].size, pt_base);
if (!map4kregion(addr, addr, bootinfo().memory_map[i].size, pt_base))
{
return false;
}
}
}
/* Map graphics framebuffer. */
ulong framebuffer_size = bootinfo().fb.height * bootinfo().fb.stride * 4u;
ulong fb_phys = cast(ulong)bootinfo().fb.buffer;
ulong fb_virt = hulk_virt_framebuffer_address();
map4kregion(fb_virt, fb_phys, framebuffer_size, pt_base);
if (!map4kregion(fb_virt, fb_phys, framebuffer_size, pt_base))
{
return false;
}
/* Map HULK regions. */
map_hulk(pt_base, bss_phys, stack_phys);
if (!map_hulk(pt_base, bss_phys, stack_phys))
{
return false;
}
/* Switch to the new page table. */
write_cr3(cast(ulong)pt_base);
return true;
}
/**
@ -447,8 +515,6 @@ extern (C) EFI_STATUS efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE * st)
return EFI_SUCCESS;
}
bootinfo().pt_phys = cast(ulong)scratch.current();
if (!set_graphics_mode())
{
console.wait_key();
@ -477,12 +543,14 @@ extern (C) EFI_STATUS efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE * st)
return EFI_SUCCESS;
}
build_page_tables(physical_address_limit, bss_phys, stack_phys);
if (!build_page_tables(physical_address_limit, bss_phys, stack_phys))
{
return EFI_SUCCESS;
}
bootinfo().hulk_phys = hulk_bin_phys();
bootinfo().bss_phys = bss_phys;
bootinfo().stack_phys = stack_phys;
bootinfo().pt_size = cast(ulong)scratch.current() - bootinfo().pt_phys;
jump_to_hulk();

View File

@ -1,39 +0,0 @@
/**
* HELLO scratch buffer.
*/
module hello.scratch;
struct scratch
{
/* Scratch buffer. */
private static align(4096) __gshared ubyte[10 * 1024 * 1024] scratch;
/* Number of scratch buffer bytes used. */
private static __gshared size_t scratch_used;
/**
* Get the number of free bytes in the scratch buffer.
*/
public static size_t free()
{
return scratch.sizeof - scratch_used;
}
/**
* Get the current free scratch buffer address.
*/
public static ubyte * current()
{
return &scratch[scratch_used];
}
/**
* Allocate pages from the scratch buffer.
*/
public static ubyte * alloc(size_t n = 1)
{
ubyte * address = &scratch[scratch_used];
scratch_used += 4096u * n;
return address;
}
}

View File

@ -33,6 +33,7 @@ struct BootInfo
enum Type
{
Reserved,
Bootloader,
Conventional,
Unusable,
ACPIReclaim,
@ -68,12 +69,6 @@ struct BootInfo
/* Physical address of stack while jumping to HULK. */
ulong stack_phys;
/* Physical address of page tables while jumping to HULK. */
ulong pt_phys;
/* Size of page tables while jumping to HULK. */
ulong pt_size;
/* Physical address of ACPI XSDT table. */
ulong acpi_xsdt_phys;
}

View File

@ -53,12 +53,11 @@ struct hippo
public static void initialize(HulkHeader * header)
{
size_t usable_memory;
ulong[2][5] reserved = [
ulong[2][4] reserved = [
[header.bootinfo.hulk_phys, cast(ulong)header.total_size - LinkerAddresses.hulk_bss_size],
[header.bootinfo.bss_phys, LinkerAddresses.hulk_bss_size],
[header.bootinfo.stack_phys, header.stack_size],
[cast(ulong)header.bootinfo.fb.buffer, header.bootinfo.fb.height * header.bootinfo.fb.stride * 4u],
[header.bootinfo.pt_phys, header.bootinfo.pt_size],
];
for (size_t ri = 0u; ri < reserved.length; ri++)
{
@ -66,7 +65,8 @@ struct hippo
}
for (size_t bii = 0u; bii < header.bootinfo.memory_map_count; bii++)
{
if (header.bootinfo.memory_map[bii].type == BootInfo.MemoryRegion.Type.Conventional)
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;