diff --git a/src/boot/loader.c b/src/boot/loader.c index d2dfbd0..a37712b 100644 --- a/src/boot/loader.c +++ b/src/boot/loader.c @@ -4,11 +4,14 @@ static CHAR16 kernel_name[] = KERNEL_FILENAME; EFI_STATUS -loader_load_kernel(void **kernel_image, uint64_t *length) +loader_load_kernel(void **kernel_image, uint64_t *kernel_length, void **kernel_data, uint64_t *data_length) { - if (kernel_image == 0 || length == 0) + if (kernel_image == 0 || kernel_length == 0) CHECK_EFI_STATUS_OR_RETURN(EFI_INVALID_PARAMETER, "NULL kernel_image or length pointer"); + if (kernel_data == 0 || data_length == 0) + CHECK_EFI_STATUS_OR_RETURN(EFI_INVALID_PARAMETER, "NULL kernel_data or length pointer"); + EFI_STATUS status; EFI_GUID guid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; EFI_HANDLE *handles = NULL; @@ -48,7 +51,7 @@ loader_load_kernel(void **kernel_image, uint64_t *length) CHECK_EFI_STATUS_OR_RETURN(status, "Freeing kernel file info memory"); UINTN page_count = ((buffer_size - 1) / 0x1000) + 1; - EFI_PHYSICAL_ADDRESS addr = KERNEL_PHYS_ADDRESS; // Try to load the kernel in at 1MiB + EFI_PHYSICAL_ADDRESS addr = KERNEL_PHYS_ADDRESS; EFI_MEMORY_TYPE mem_type = KERNEL_MEMTYPE; // Special value to tell the kernel it's here status = ST->BootServices->AllocatePages(AllocateAddress, mem_type, page_count, &addr); if (status == EFI_NOT_FOUND) { @@ -57,6 +60,7 @@ loader_load_kernel(void **kernel_image, uint64_t *length) ST->BootServices->AllocatePages(AllocateAnyPages, mem_type, page_count, &addr); } CHECK_EFI_STATUS_OR_RETURN(status, "Allocating kernel pages"); + Print(L"\n Allocating %u pages at 0x%x", page_count, addr); buffer = (void *)addr; status = file->Read(file, &buffer_size, buffer); @@ -65,8 +69,25 @@ loader_load_kernel(void **kernel_image, uint64_t *length) status = file->Close(file); CHECK_EFI_STATUS_OR_RETURN(status, "Closing kernel file handle"); - *length = buffer_size; + *kernel_length = buffer_size; *kernel_image = buffer; + + addr += page_count * 0x1000; // Get the next page after the kernel pages + mem_type = KERNEL_DATA_MEMTYPE; // Special value for kernel data + page_count = ((*data_length - 1) / 0x1000) + 1; + status = ST->BootServices->AllocatePages(AllocateAddress, mem_type, page_count, &addr); + if (status == EFI_NOT_FOUND) { + // couldn't get the address we wanted, try loading anywhere + status = + ST->BootServices->AllocatePages(AllocateAnyPages, mem_type, page_count, &addr); + } + CHECK_EFI_STATUS_OR_RETURN(status, "Allocating kernel data pages"); + Print(L"\n Allocating %u pages at 0x%x", page_count, addr); + + *data_length = page_count * 0x1000; + *kernel_data = (void *)addr; + ST->BootServices->SetMem(*kernel_data, *data_length, 0); + return EFI_SUCCESS; } } diff --git a/src/boot/loader.h b/src/boot/loader.h index dba4fd8..62089a7 100644 --- a/src/boot/loader.h +++ b/src/boot/loader.h @@ -5,16 +5,24 @@ #define KERNEL_PHYS_ADDRESS 0x100000 #endif -#ifndef KERNEL_VIRT_ADDRESS -#define KERNEL_VIRT_ADDRESS 0xf00000000 +#ifndef VIRTUAL_OFFSET +#define VIRTUAL_OFFSET 0xf00000000 #endif #ifndef KERNEL_MEMTYPE #define KERNEL_MEMTYPE 0x80000000 #endif +#ifndef KERNEL_DATA_MEMTYPE +#define KERNEL_DATA_MEMTYPE 0x80000001 +#endif + +#ifndef KERNEL_DATA_PAGES +#define KERNEL_DATA_PAGES 1 +#endif + #ifndef KERNEL_FILENAME #define KERNEL_FILENAME L"kernel.bin" #endif -EFI_STATUS loader_load_kernel(void **kernel_image, UINT64 *length); +EFI_STATUS loader_load_kernel(void **kernel_image, uint64_t *kernel_length, void **kernel_data, uint64_t *data_length); diff --git a/src/boot/main.c b/src/boot/main.c index 0b172a7..6219dee 100644 --- a/src/boot/main.c +++ b/src/boot/main.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include "console.h" #include "loader.h" @@ -10,24 +12,57 @@ #define GIT_VERSION L"no version" #endif -#define KERNEL_MAGIC 0x600db007 +#define KERNEL_HEADER_MAGIC 0x600db007 +#define KERNEL_HEADER_VERSION 1 + +#define DATA_HEADER_MAGIC 0x600dda7a +#define DATA_HEADER_VERSION 1 + #pragma pack(push, 1) struct kernel_version { uint32_t magic; + uint16_t version; + uint16_t length; + uint8_t major; uint8_t minor; uint16_t patch; uint32_t gitsha; + void *entrypoint; }; + +struct popcorn_data { + uint32_t magic; + uint16_t version; + uint16_t length; + + uint16_t data_pages; + uint16_t _reserverd; + uint32_t flags; + + EFI_MEMORY_DESCRIPTOR *memory_map; + EFI_RUNTIME_SERVICES *runtime; +} +__attribute__((aligned(_Alignof(EFI_MEMORY_DESCRIPTOR)))); #pragma pack(pop) + EFI_STATUS efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { EFI_STATUS status; + UINTN memmap_size = 0; + UINTN memmap_key = 0; + UINTN desc_size = 0; + UINTN newmap_size = 0; + UINTN data_length = 0; + + UINT32 desc_version = 0; + + InitializeLib(ImageHandle, SystemTable); // When checking the console initialization error, @@ -38,33 +73,63 @@ efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) // From here on out, use CHECK_EFI_STATUS_OR_FAIL instead // because the console is now set up + con_status_begin(L"Computing needed data pages..."); + status = memory_get_map_size(&data_length); + size_t header_size = sizeof(struct popcorn_data); + const size_t header_align = alignof(struct popcorn_data); + if (header_size % header_align) + header_size += header_align - (header_size % header_align); + data_length += header_size; + con_status_ok(); + con_status_begin(L"Loading kernel into memory..."); - void *kernel_image = NULL; + void *kernel_image = NULL, *kernel_data = NULL; uint64_t kernel_length = 0; - status = loader_load_kernel(&kernel_image, &kernel_length); + status = loader_load_kernel(&kernel_image, &kernel_length, &kernel_data, &data_length); CHECK_EFI_STATUS_OR_FAIL(status); Print(L"\n %u bytes at 0x%x", kernel_length, kernel_image); + Print(L"\n %u data bytes at 0x%x", data_length, kernel_data); struct kernel_version *version = (struct kernel_version *)kernel_image; - if (version->magic != KERNEL_MAGIC) { + if (version->magic != KERNEL_HEADER_MAGIC) { Print(L"\n bad magic %x", version->magic); CHECK_EFI_STATUS_OR_FAIL(EFI_CRC_ERROR); } Print(L"\n Kernel version %d.%d.%d %x%s", version->major, version->minor, version->patch, version->gitsha & 0x0fffffff, version->gitsha & 0xf0000000 ? "*" : ""); - Print(L"\n Entrypoint %d", version->entrypoint); + Print(L"\n Entrypoint 0x%x", version->entrypoint); void (*kernel_main)() = version->entrypoint; + con_status_ok(); - // memory_dump_map(); + struct popcorn_data *data_header = (struct popcorn_data *)kernel_data; + data_header->magic = DATA_HEADER_MAGIC; + data_header->version = DATA_HEADER_VERSION; + data_header->length = sizeof(struct popcorn_data); + data_header->data_pages = data_length / 0x1000; + data_header->_reserverd = 0; + data_header->flags = 0; + data_header->memory_map = (EFI_MEMORY_DESCRIPTOR *)(data_header + 1); + data_header->runtime = SystemTable->RuntimeServices; + + // Get info about this image + con_status_begin(L"Gathering image information..."); + EFI_LOADED_IMAGE *info = 0; + EFI_GUID image_proto = EFI_LOADED_IMAGE_PROTOCOL_GUID; + status = ST->BootServices->HandleProtocol(ImageHandle, &image_proto, (void **)&info); + CHECK_EFI_STATUS_OR_FAIL(status); + con_status_ok(); + + EFI_MEMORY_DESCRIPTOR *memory_map; + + /* + memory_get_map(&memory_map, &memmap_size, &memmap_key, &desc_size, &desc_version); + memory_dump_map(memory_map, memmap_size, desc_size); + */ con_status_begin(L"Exiting boot services..."); - UINTN memmap_size = 0, memmap_key = 0; - UINTN desc_size = 0; - UINT32 desc_version = 0; - EFI_MEMORY_DESCRIPTOR *memory_map; status = memory_mark_address_for_update((void **)&ST); CHECK_EFI_STATUS_OR_FAIL(status); @@ -75,15 +140,18 @@ efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) status = memory_mark_address_for_update((void **)&kernel_main); CHECK_EFI_STATUS_OR_FAIL(status); + status = memory_mark_address_for_update((void **)&kernel_data); + CHECK_EFI_STATUS_OR_FAIL(status); + status = memory_get_map(&memory_map, &memmap_size, &memmap_key, &desc_size, &desc_version); CHECK_EFI_STATUS_OR_FAIL(status); + status = memory_copy_map(memory_map, data_header->memory_map, memmap_size, desc_size, info->ImageBase, &newmap_size); + CHECK_EFI_STATUS_OR_FAIL(status); + status = ST->BootServices->ExitBootServices(ImageHandle, memmap_key); CHECK_EFI_STATUS_OR_ASSERT(status, 0); - status = memory_virtualize(memory_map, memmap_size, desc_size, desc_version); - CHECK_EFI_STATUS_OR_ASSERT(status, 0); - kernel_main(); return EFI_LOAD_ERROR; } diff --git a/src/boot/memory.c b/src/boot/memory.c index fc323d9..656f167 100644 --- a/src/boot/memory.c +++ b/src/boot/memory.c @@ -1,11 +1,14 @@ #include #include +#include #include "loader.h" #include "memory.h" #include "utility.h" -const UINTN PAGE_SIZE = 4096; +#define INCREMENT_DESC(p, b) (EFI_MEMORY_DESCRIPTOR*)(((uint8_t*)(p))+(b)) + +const size_t PAGE_SIZE = 4096; const CHAR16 *memory_type_names[] = { L"EfiReservedMemoryType", @@ -29,8 +32,9 @@ static const CHAR16 * memory_type_name(UINT32 value) { if (value >= (sizeof(memory_type_names) / sizeof(CHAR16 *))) { - if (value == KERNEL_MEMTYPE) return L"Kernel Image"; - return L"Bad Type Value"; + if (value == KERNEL_DATA_MEMTYPE) return L"Kernel Data"; + else if (value == KERNEL_MEMTYPE) return L"Kernel Image"; + else return L"Bad Type Value"; } return memory_type_names[value]; } @@ -57,36 +61,71 @@ memory_mark_address_for_update(void **pointer) CHECK_EFI_STATUS_OR_ASSERT(status, pointer); } -EFI_STATUS -memory_virtualize(EFI_MEMORY_DESCRIPTOR *memory_map, UINTN memmap_size, UINTN desc_size, - UINT32 desc_version) +void +copy_desc(EFI_MEMORY_DESCRIPTOR *src, EFI_MEMORY_DESCRIPTOR *dst, size_t len) { - - EFI_MEMORY_DESCRIPTOR *end = (EFI_MEMORY_DESCRIPTOR *)((uint8_t *)memory_map + memmap_size); - EFI_MEMORY_DESCRIPTOR *d = memory_map; - while (d < end) { - if (d->Type == KERNEL_MEMTYPE) { - // d->VirtualStart = (EFI_VIRTUAL_ADDRESS)KERNEL_VIRT_ADDRESS; - d->VirtualStart = (EFI_VIRTUAL_ADDRESS)d->PhysicalStart; - d->Attribute |= EFI_MEMORY_RUNTIME; - } else /*if (d->Attribute & EFI_MEMORY_RUNTIME)*/ { - d->VirtualStart = (EFI_VIRTUAL_ADDRESS)d->PhysicalStart; - } - - d = (EFI_MEMORY_DESCRIPTOR *)((uint8_t *)d + desc_size); - } - - return ST->RuntimeServices->SetVirtualAddressMap(memmap_size, desc_size, desc_version, - memory_map); + uint8_t *srcb = (uint8_t *)src; + uint8_t *dstb = (uint8_t *)dst; + uint8_t *endb = srcb + len; + while (srcb < endb) + *dstb++ = *srcb++; } EFI_STATUS -memory_get_map(EFI_MEMORY_DESCRIPTOR **buffer, UINTN *buffer_size, UINTN *key, UINTN *desc_size, +memory_copy_map(EFI_MEMORY_DESCRIPTOR *oldmap, EFI_MEMORY_DESCRIPTOR *newmap, size_t oldmap_size, + size_t desc_size, void *this_image, size_t *newmap_size) +{ + size_t count = 0; + + EFI_MEMORY_DESCRIPTOR *s; + EFI_MEMORY_DESCRIPTOR *end = INCREMENT_DESC(oldmap, oldmap_size); + for (s = oldmap; s < end; s = INCREMENT_DESC(s, desc_size)) { + if (this_image != (void *)s->PhysicalStart && + s->Type == EfiLoaderCode || + s->Type == EfiLoaderData || + s->Type == EfiBootServicesCode || + s->Type == EfiBootServicesData || + s->Type == EfiConventionalMemory) { + // These are memory types we don't need to keep + continue; + } + + s->Attribute |= EFI_MEMORY_RUNTIME; + s->VirtualStart = (EFI_VIRTUAL_ADDRESS)(s->PhysicalStart + VIRTUAL_OFFSET); + + EFI_MEMORY_DESCRIPTOR *d = INCREMENT_DESC(newmap, count*desc_size); + copy_desc(s, d, desc_size); + ++count; + } + + *newmap_size = desc_size * count; + return EFI_SUCCESS; +} + +EFI_STATUS +memory_get_map_size(size_t *size) +{ + if (size == NULL) + return EFI_INVALID_PARAMETER; + + EFI_STATUS status; + size_t key, desc_size; + uint32_t desc_version; + *size = 0; + status = ST->BootServices->GetMemoryMap(size, 0, &key, &desc_size, &desc_version); + if (status != EFI_BUFFER_TOO_SMALL) { + CHECK_EFI_STATUS_OR_RETURN(status, "Failed to get memory map size"); + } + return EFI_SUCCESS; +} + +EFI_STATUS +memory_get_map(EFI_MEMORY_DESCRIPTOR **buffer, size_t *buffer_size, size_t *key, size_t *desc_size, UINT32 *desc_version) { EFI_STATUS status; - UINTN needs_size = 0; + size_t needs_size = 0; status = ST->BootServices->GetMemoryMap(&needs_size, 0, key, desc_size, desc_version); if (status != EFI_BUFFER_TOO_SMALL) { CHECK_EFI_STATUS_OR_RETURN(status, "Failed to load memory map"); @@ -103,32 +142,30 @@ memory_get_map(EFI_MEMORY_DESCRIPTOR **buffer, UINTN *buffer_size, UINTN *key, U } EFI_STATUS -memory_dump_map() +memory_dump_map(EFI_MEMORY_DESCRIPTOR *memory_map, size_t memmap_size, size_t desc_size) { - EFI_MEMORY_DESCRIPTOR *buffer; - UINTN buffer_size, desc_size, key; - UINT32 desc_version; - - EFI_STATUS status = memory_get_map(&buffer, &buffer_size, &key, &desc_size, &desc_version); - CHECK_EFI_STATUS_OR_RETURN(status, "Failed to get memory map"); - - const UINTN count = buffer_size / desc_size; + const size_t count = memmap_size / desc_size; Print(L"Memory map:\n"); - Print(L"\t Descriptor Count: %d (%d bytes)\n", count, buffer_size); - Print(L"\t Version Key: %d\n", key); - Print(L"\tDescriptor Version: %d (%d bytes)\n\n", desc_version, desc_size); + Print(L"\t Descriptor Count: %d (%d bytes)\n", count, memmap_size); + Print(L"\t Descriptor Size: %d bytes\n", desc_size); + Print(L"\t Type offset: %d\n", offsetof(EFI_MEMORY_DESCRIPTOR, Type)); + Print(L"\t Physical offset: %d\n", offsetof(EFI_MEMORY_DESCRIPTOR, PhysicalStart)); + Print(L"\t Virtual offset: %d\n", offsetof(EFI_MEMORY_DESCRIPTOR, VirtualStart)); + Print(L"\t Pages offset: %d\n", offsetof(EFI_MEMORY_DESCRIPTOR, NumberOfPages)); + Print(L"\t Attr offset: %d\n\n", offsetof(EFI_MEMORY_DESCRIPTOR, Attribute)); - EFI_MEMORY_DESCRIPTOR *end = (EFI_MEMORY_DESCRIPTOR *)((uint8_t *)buffer + buffer_size); - EFI_MEMORY_DESCRIPTOR *d = buffer; + EFI_MEMORY_DESCRIPTOR *end = INCREMENT_DESC(memory_map, memmap_size); + EFI_MEMORY_DESCRIPTOR *d = memory_map; while (d < end) { int runtime = (d->Attribute & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME; Print(L"%23s%s ", memory_type_name(d->Type), runtime ? L"*" : L" "); - Print(L"%016llx (%3d pages)\n", d->PhysicalStart, d->NumberOfPages); + Print(L"%016llx ", d->PhysicalStart); + Print(L"%016llx ", d->VirtualStart); + Print(L"[%4d]\n", d->NumberOfPages); - d = (EFI_MEMORY_DESCRIPTOR *)((uint8_t *)d + desc_size); + d = INCREMENT_DESC(d, desc_size); } - ST->BootServices->FreePool(buffer); return EFI_SUCCESS; } diff --git a/src/boot/memory.h b/src/boot/memory.h index f4f3486..ad9bc9a 100644 --- a/src/boot/memory.h +++ b/src/boot/memory.h @@ -1,12 +1,26 @@ #pragma once #include +struct memory_map { + size_t length; + size_t size; + size_t key; + uint32_t version; + EFI_MEMORY_DESCRIPTOR *entries; +}; + EFI_STATUS memory_mark_address_for_update(void **pointer); -EFI_STATUS memory_virtualize(EFI_MEMORY_DESCRIPTOR *memory_map, UINTN memmap_size, UINTN desc_size, +EFI_STATUS memory_virtualize(EFI_MEMORY_DESCRIPTOR *memory_map, size_t memmap_size, size_t desc_size, UINT32 desc_version); -EFI_STATUS memory_get_map(EFI_MEMORY_DESCRIPTOR **buffer, UINTN *buffer_size, UINTN *key, - UINTN *desc_size, UINT32 *desc_version); +EFI_STATUS memory_get_map_size(size_t *sizsize); + +EFI_STATUS memory_get_map(EFI_MEMORY_DESCRIPTOR **buffer, size_t *buffer_size, size_t *key, + size_t *desc_size, UINT32 *desc_version); + +EFI_STATUS memory_copy_map(EFI_MEMORY_DESCRIPTOR *oldmap, EFI_MEMORY_DESCRIPTOR *newmap, + size_t oldmap_size, size_t desc_size, void *this_image, + size_t *newmap_size); EFI_STATUS memory_dump_map(); diff --git a/src/modules/main/boot.s b/src/modules/main/boot.s index 22529c3..6df8f28 100644 --- a/src/modules/main/boot.s +++ b/src/modules/main/boot.s @@ -4,12 +4,14 @@ section .header align 4 global _header _header: - dd MAGIC - db VERSION_MAJOR + dd MAGIC ; Kernel header magic + dw 1 ; Header version 1 + dw 1 ; Kernel header length + db VERSION_MAJOR ; Kernel version db VERSION_MINOR dw VERSION_PATCH dd VERSION_GITSHA - dq _start + dq _start ; Kernel entrypoint section .text align 16