hos/src/hello/hello.d

300 lines
8.7 KiB
D

import uefi;
import output;
import scratch;
import hulk.bootinfo;
import hos.page_table;
import hos.cpu;
import hos.memory;
__gshared EFI_SYSTEM_TABLE * st;
__gshared BootInfo bootinfo;
__gshared UINTN memory_map_key;
extern extern(C) __gshared ubyte hulk_start;
extern extern(C) __gshared ubyte hulk_end;
private void wait_key()
{
writeln("Press any key...");
st.ConIn.Reset(st.ConIn, FALSE);
EFI_INPUT_KEY key;
while (st.ConIn.ReadKeyStroke(st.ConIn, &key) == EFI_NOT_READY)
{
}
}
private bool in_qemu()
{
ulong * firmware_vendor = cast(ulong *) st.FirmwareVendor;
ulong fv1 = firmware_vendor[0];
return fv1 ==
((cast(ulong)'E') |
(cast(ulong)'D' << 16) |
(cast(ulong)'K' << 32) |
(cast(ulong)' ' << 48));
}
private bool set_graphics_mode()
{
uint max_horizontal_resolution = in_qemu() ? 1920u : 0xFFFFFFFFu;
UINTN buffer_size = scratch_free();
EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
EFI_HANDLE * handles = cast(EFI_HANDLE *)scratch_current();
EFI_STATUS status = st.BootServices.LocateHandle(ByProtocol,
&gop_guid, null, &buffer_size, handles);
if (status != EFI_SUCCESS)
{
writeln("LocateHandle: error %x", status);
return false;
}
EFI_HANDLE gop_handle = handles[0];
EFI_HANDLE gop_interface;
status = st.BootServices.HandleProtocol(gop_handle,
&gop_guid, &gop_interface);
if (status != EFI_SUCCESS)
{
writeln("HandleProtocol: error %x", status);
return false;
}
if (gop_interface == null)
{
writeln("null interface from HandleProtocol");
return false;
}
EFI_GRAPHICS_OUTPUT_PROTOCOL * gop = cast(EFI_GRAPHICS_OUTPUT_PROTOCOL *)gop_interface;
UINTN info_size;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION * info;
uint best_width;
uint best_mode_number;
for (uint mode_number = 0u;
(status = gop.QueryMode(gop, mode_number, &info_size, &info)) == EFI_SUCCESS;
mode_number++)
{
if ((info.HorizontalResolution > best_width) &&
(info.HorizontalResolution <= max_horizontal_resolution))
{
best_width = info.HorizontalResolution;
best_mode_number = mode_number;
}
st.BootServices.FreePool(info);
}
if ((status = gop.SetMode(gop, best_mode_number)) != EFI_SUCCESS)
{
writeln("SetMode: Error %x\n", status);
return false;
}
bootinfo.fb.buffer = cast(uint *)gop.Mode.FrameBufferBase;
bootinfo.fb.width = gop.Mode.Info.HorizontalResolution;
bootinfo.fb.height = gop.Mode.Info.VerticalResolution;
bootinfo.fb.stride = gop.Mode.Info.PixelsPerScanLine;
bootinfo.fb.format = gop.Mode.Info.PixelFormat;
return true;
}
private ulong get_memory_map()
{
UINTN memory_map_size = scratch_free();
UINTN descriptor_size;
UINT32 descriptor_version;
ubyte * scratch_base = scratch_current();
UINTN status = st.BootServices.GetMemoryMap(
&memory_map_size,
cast(EFI_MEMORY_DESCRIPTOR *)scratch_base,
&memory_map_key,
&descriptor_size,
&descriptor_version);
if (status != EFI_SUCCESS)
{
writeln("GetMemoryMap: Error %x", status);
return 0u;
}
ulong max_physical_address;
size_t n_entries = memory_map_size / descriptor_size;
size_t count;
for (count = 0u; count < n_entries; count++)
{
if (count > bootinfo.memory_map.length)
{
writeln("Memory map too large");
for (;;)
{
}
}
EFI_MEMORY_DESCRIPTOR * descriptor = cast(EFI_MEMORY_DESCRIPTOR *)&scratch_base[count * descriptor_size];
ulong end_address = descriptor.PhysicalStart + descriptor.NumberOfPages * 4096u;
if ((end_address > max_physical_address) &&
((descriptor.Type == EfiLoaderCode) ||
(descriptor.Type == EfiLoaderData) ||
(descriptor.Type == EfiBootServicesCode) ||
(descriptor.Type == EfiBootServicesData) ||
(descriptor.Type == EfiRuntimeServicesCode) ||
(descriptor.Type == EfiRuntimeServicesData) ||
(descriptor.Type == EfiConventionalMemory)))
{
max_physical_address = end_address;
}
bootinfo.memory_map[count].base = descriptor.PhysicalStart;
bootinfo.memory_map[count].size = descriptor.NumberOfPages * 4096u;
bootinfo.memory_map[count].type = cast(ubyte)descriptor.Type;
}
bootinfo.memory_map_count = count;
return max_physical_address;
}
private PageTableEntry * new_page_table()
{
PageTableEntry * pt = cast(PageTableEntry *)scratch_alloc(1u);
memset64(pt, 0u, 512);
return pt;
}
private void map4k(ulong source_page, ulong dest_page, PageTableEntry * pt_base)
{
PageTableEntry * pt = pt_base;
PageTableEntry * next_pt;
for (size_t level = 0; level < 4u; level++)
{
size_t pt_index = PageTableEntry.page_table_index(source_page, level);
if (pt[pt_index].present())
{
pt = pt[pt_index].next();
}
else
{
ulong addr;
if (level < 3u)
{
next_pt = new_page_table();
addr = cast(ulong)next_pt;
}
else
{
addr = dest_page;
}
pt[pt_index] = PageTableEntry(addr,
0u, 0u, 0u, 0u, 0u, 0u, 1u, 1u);
pt = next_pt;
}
}
}
private void map2m(ulong source_page, ulong dest_page, PageTableEntry * pt_base)
{
PageTableEntry * pt = pt_base;
PageTableEntry * next_pt;
for (size_t level = 0; level < 3u; level++)
{
size_t pt_index = PageTableEntry.page_table_index(source_page, level);
if (pt[pt_index].present())
{
pt = pt[pt_index].next();
}
else
{
if (level < 2u)
{
next_pt = new_page_table();
pt[pt_index] = PageTableEntry(cast(ulong)next_pt,
0u, 0u, 0u, 0u, 0u, 0u, 1u, 1u);
}
else
{
pt[pt_index] = PageTableEntry(dest_page,
0u, 0u, 1u, 0u, 0u, 0u, 1u, 1u);
}
pt = next_pt;
}
}
}
private void map_hulk(PageTableEntry * pt_base)
{
ulong virt = HULK_VIRTUAL_START;
ulong phys = cast(ulong)&hulk_start;
while (phys < cast(ulong)&hulk_end)
{
map4k(virt, phys, pt_base);
virt += 4096u;
phys += 4096u;
}
}
private void build_page_tables(ulong max_physical_address)
{
PageTableEntry * pt_base = new_page_table();
/* Map physical RAM. */
for (size_t addr = 0u; addr < max_physical_address; addr += (2u * 1024u * 1024u))
{
map2m(addr, addr, pt_base);
}
/* Map any memory regions that are outside physical RAM. */
for (size_t i = 0u; i < bootinfo.memory_map_count; i++)
{
if (bootinfo.memory_map[i].base > max_physical_address)
{
for (size_t offset = 0u; offset < bootinfo.memory_map[i].size; offset += 4096u)
{
ulong addr = bootinfo.memory_map[i].base + offset;
map4k(addr, addr, pt_base);
}
}
}
/* Map graphics framebuffer. */
ulong framebuffer_size = bootinfo.fb.height * bootinfo.fb.stride;
for (size_t offset = 0u; offset < framebuffer_size; offset++)
{
ulong addr = cast(ulong)bootinfo.fb.buffer + offset;
map4k(addr, addr, pt_base);
}
/* Map HULK to its requested starting virtual address. */
map_hulk(pt_base);
/* Switch to the new page table. */
write_cr3(cast(ulong)pt_base);
}
private void jump_to_hulk()
{
void function(BootInfo *) hulk_start = cast(void function(BootInfo *))HULK_VIRTUAL_START;
hulk_start(&bootinfo);
}
extern (C) EFI_STATUS efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE * st)
{
.st = st;
st.ConOut.ClearScreen(st.ConOut);
writeln("HELLO, HOS EFI Lightweight LOader, v0.1.0");
writeln("Firmware vendor: '%S', version: 0x%x", st.FirmwareVendor, st.FirmwareVendor);
if (!set_graphics_mode())
{
wait_key();
return EFI_SUCCESS;
}
ulong max_physical_address;
for (;;)
{
max_physical_address = get_memory_map();
EFI_STATUS status = st.BootServices.ExitBootServices(image_handle, memory_map_key);
if (status == EFI_INVALID_PARAMETER)
{
continue;
}
if (status == EFI_SUCCESS)
{
break;
}
writeln("ExitBootServices: Error %x", status);
wait_key();
return EFI_SUCCESS;
}
build_page_tables(max_physical_address);
jump_to_hulk();
return EFI_SUCCESS;
}