diff --git a/src/boot/main.c b/src/boot/main.c index 53d08e6..2a35df1 100644 --- a/src/boot/main.c +++ b/src/boot/main.c @@ -163,6 +163,9 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table) status = memory_get_map(bootsvc, &map); CHECK_EFI_STATUS_OR_FAIL(status); + data_header->memory_map_length = map.length; + data_header->memory_map_desc_size = map.size; + // bootsvc->Stall(5000000); status = bootsvc->ExitBootServices(image_handle, map.key); diff --git a/src/boot/memory.c b/src/boot/memory.c index c737fac..483a1ee 100644 --- a/src/boot/memory.c +++ b/src/boot/memory.c @@ -68,7 +68,7 @@ memory_init_pointer_fixup(EFI_BOOT_SERVICES *bootsvc, EFI_RUNTIME_SERVICES *runs // Reserve a page for our replacement PML4 EFI_PHYSICAL_ADDRESS addr = 0; - status = bootsvc->AllocatePages(AllocateAnyPages, EfiLoaderData, 1, &addr); + status = bootsvc->AllocatePages(AllocateAnyPages, EfiLoaderData, 4, &addr); CHECK_EFI_STATUS_OR_RETURN(status, "Failed to allocate PML4 page."); new_pml4 = (uint64_t *)addr; @@ -192,12 +192,10 @@ memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map) case KERNEL_DATA_MEMTYPE: case KERNEL_LOG_MEMTYPE: d->Attribute |= EFI_MEMORY_RUNTIME; - d->VirtualStart = d->PhysicalStart + KERNEL_VIRT_ADDRESS; - break; default: if (d->Attribute & EFI_MEMORY_RUNTIME) { - d->VirtualStart = d->PhysicalStart; + d->VirtualStart = d->PhysicalStart + KERNEL_VIRT_ADDRESS; } } d = INCREMENT_DESC(d, map->size); diff --git a/src/include/kernel_data.h b/src/include/kernel_data.h index 6406745..2fab707 100644 --- a/src/include/kernel_data.h +++ b/src/include/kernel_data.h @@ -26,6 +26,9 @@ struct popcorn_data { size_t log_length; void *memory_map; + size_t memory_map_length; + size_t memory_map_desc_size; + void *runtime; void *acpi_table; diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 1152289..1dce85a 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -6,6 +6,7 @@ #include "font.h" #include "interrupts.h" #include "kernel_data.h" +#include "memory.h" #include "screen.h" extern "C" { @@ -40,6 +41,10 @@ void kernel_main(popcorn_data *header) { console cons = load_console(header); + memory_manager::create( + header->memory_map, + header->memory_map_length, + header->memory_map_desc_size); interrupts_init(); interrupts_enable(); diff --git a/src/kernel/memory.cpp b/src/kernel/memory.cpp new file mode 100644 index 0000000..3623bce --- /dev/null +++ b/src/kernel/memory.cpp @@ -0,0 +1,187 @@ +#include + +#include "kutil/enum_bitfields.h" +#include "assert.h" +#include "console.h" +#include "memory.h" + +memory_manager *memory_manager::s_instance = nullptr; + +enum class efi_memory_type : uint32_t +{ + reserved, + loader_code, + loader_data, + boot_services_code, + boot_services_data, + runtime_services_code, + runtime_services_data, + available, + unusable, + acpi_reclaim, + acpi_nvs, + mmio, + mmio_port, + pal_code, + + efi_max, + + popcorn_kernel = 0x80000000, + popcorn_font, + popcorn_data, + popcorn_log, + popcorn_pml4, + + popcorn_max +}; + +const char *efi_memory_type_names[] = { + " reserved", + " loader_code", + " loader_data", + " boot_services_code", + " boot_services_data", + "runtime_services_code", + "runtime_services_data", + " available", + " unusable", + " acpi_reclaim", + " acpi_nvs", + " mmio", + " mmio_port", + " pal_code", + + " popcorn_kernel", + " popcorn_font", + " popcorn_data", + " popcorn_log", + " popcorn_pml4", +}; + +static const char * +get_efi_name(efi_memory_type t) +{ + static const unsigned offset = + (unsigned)efi_memory_type::popcorn_kernel - (unsigned)efi_memory_type::efi_max; + + return t >= efi_memory_type::popcorn_kernel ? + efi_memory_type_names[(unsigned)t - offset] : + efi_memory_type_names[(unsigned)t]; +} + +enum class efi_memory_flag : uint64_t +{ + can_mark_uc = 0x0000000000000001, // uc = un-cacheable + can_mark_wc = 0x0000000000000002, // wc = write-combining + can_mark_wt = 0x0000000000000004, // wt = write through + can_mark_wb = 0x0000000000000008, // wb = write back + can_mark_uce = 0x0000000000000010, // uce = un-cacheable exported + can_mark_wp = 0x0000000000001000, // wp = write protected + can_mark_rp = 0x0000000000002000, // rp = read protected + can_mark_xp = 0x0000000000004000, // xp = exceute protected + can_mark_ro = 0x0000000000020000, // ro = read only + + non_volatile = 0x0000000000008000, + more_reliable = 0x0000000000010000, + runtime = 0x8000000000000000 +}; +IS_BITFIELD(efi_memory_flag); + +struct efi_memory_descriptor +{ + efi_memory_type type; + uint32_t pad; + uint64_t physical_start; + uint64_t virtual_start; + uint64_t pages; + efi_memory_flag flags; +}; + +static const efi_memory_descriptor * +desc_incr(const efi_memory_descriptor *d, size_t desc_length) +{ + return reinterpret_cast( + reinterpret_cast(d) + desc_length); +} + +struct page_table +{ + uint64_t entries[512]; + + page_table * next(int i) const + { + return reinterpret_cast(entries[i] & 0xfffffffffffff000); + } +}; + +struct page_block +{ + uint64_t physical_address; + uint32_t count; + uint32_t flags; +}; + +void +memory_manager::create(const void *memory_map, size_t map_length, size_t desc_length) +{ + // The bootloader reserved 4 pages for page tables, which we'll use to bootstrap. + // The first one is the already-installed PML4, so grab it from CR3. + page_table *tables = nullptr; + __asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (tables) ); + + // Now go through EFi's memory map and find a 4MiB region of free space to + // use as a scratch space. We'll use the 2MiB that fits naturally aligned + // into a single page table. + efi_memory_descriptor const *free_mem = + reinterpret_cast(memory_map); + efi_memory_descriptor const *end = desc_incr(free_mem, map_length); + + while (free_mem < end) { + if (free_mem->type == efi_memory_type::available && free_mem->pages >= 1024) + break; + + free_mem = desc_incr(free_mem, desc_length); + continue; + } + kassert(free_mem < end, "Couldn't find 4MiB of contiguous scratch space."); + + uint64_t start = (free_mem->physical_start & 0x1fffff) == 0 ? + free_mem->physical_start : + free_mem->physical_start + 0x1fffff & 0x1fffff; + + // Identity-map that region. We'll need to copy any existing tables (except + // the PML4 which the bootloader gave us) into our 4 reserved pages so we + // can edit them. + uint64_t pml4_index = (start >> 39) & 0x1ff; + uint64_t pdpt_index = (start >> 30) & 0x1ff; + uint64_t pdt_index = (start >> 21) & 0x1ff; + + if (tables[0].entries[pml4_index] & 0x1) { + page_table *old_pdpt = tables[0].next(pml4_index); + for (int i = 0; i < 512; ++i) tables[1].entries[i] = old_pdpt->entries[i]; + } else { + for (int i = 0; i < 512; ++i) tables[1].entries[i] = 0; + } + tables[0].entries[pml4_index] = reinterpret_cast(&tables[1]) | 0xb; + + if (tables[1].entries[pdpt_index] & 0x1) { + page_table *old_pdt = tables[1].next(pdpt_index); + for (int i = 0; i < 512; ++i) tables[2].entries[i] = old_pdt->entries[i]; + } else { + for (int i = 0; i < 512; ++i) tables[2].entries[i] = 0; + } + tables[1].entries[pdpt_index] = reinterpret_cast(&tables[2]) | 0xb; + + for (int i = 0; i < 512; ++i) + tables[3].entries[i] = (start + 0x1000) | 0xb; + tables[2].entries[pdt_index] = reinterpret_cast(&tables[3]) | 0xb; + + // We now have 2MiB starting at "start" to bootstrap ourselves + char const *hello = "Hello, beautiful memory! A little breathing space!"; + char *world = reinterpret_cast(start); + while (*hello) *world++ = *hello++; +} + +memory_manager::memory_manager(void *efi_runtime, void *memory_map, size_t map_length) +{ +} diff --git a/src/kernel/memory.h b/src/kernel/memory.h new file mode 100644 index 0000000..ceef2a8 --- /dev/null +++ b/src/kernel/memory.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class memory_manager +{ +public: + + static void create(const void *memory_map, size_t map_length, size_t desc_length); + static memory_manager * get() { return s_instance; } + +private: + memory_manager(void *efi_runtime, void *memory_map, size_t map_length); + + memory_manager() = delete; + memory_manager(const memory_manager &) = delete; + + static memory_manager * s_instance; +}; diff --git a/src/modules/kutil/enum_bitfields.h b/src/modules/kutil/enum_bitfields.h new file mode 100644 index 0000000..443f68d --- /dev/null +++ b/src/modules/kutil/enum_bitfields.h @@ -0,0 +1,74 @@ +#pragma once + +#include + +template +struct is_enum_bitfield { static constexpr bool value = false; }; + +#define IS_BITFIELD(name) \ + template<> struct is_enum_bitfield {static constexpr bool value=true;} + +template +typename std::enable_if::value,E>::type +operator & (E lhs, E rhs) +{ + return static_cast ( + static_cast::type>(lhs) & + static_cast::type>(rhs)); +} + +template +typename std::enable_if::value,E>::type +operator ^ (E lhs, E rhs) +{ + return static_cast ( + static_cast::type>(lhs) ^ + static_cast::type>(rhs)); +} + +template +typename std::enable_if::value,E>::type +operator ~ (E rhs) +{ + return static_cast(~static_cast::type>(rhs)); +} + +template +typename std::enable_if::value,E>::type& +operator |= (E &lhs, E rhs) +{ + lhs = static_cast( + static_cast::type>(lhs) | + static_cast::type>(rhs)); + + return lhs; +} + +template +typename std::enable_if::value,E>::type& +operator &= (E &lhs, E rhs) +{ + lhs = static_cast( + static_cast::type>(lhs) & + static_cast::type>(rhs)); + + return lhs; +} + +template +typename std::enable_if::value,E>::type& +operator ^= (E &lhs, E rhs) +{ + lhs = static_cast( + static_cast::type>(lhs) ^ + static_cast::type>(rhs)); + + return lhs; +} + +template +typename std::enable_if::value,bool>::type +operator ! (E rhs) +{ + return static_cast::type>(rhs) == 0; +}