Bootstrap in-kernel memory management
This commit is contained in:
@@ -163,6 +163,9 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
|||||||
status = memory_get_map(bootsvc, &map);
|
status = memory_get_map(bootsvc, &map);
|
||||||
CHECK_EFI_STATUS_OR_FAIL(status);
|
CHECK_EFI_STATUS_OR_FAIL(status);
|
||||||
|
|
||||||
|
data_header->memory_map_length = map.length;
|
||||||
|
data_header->memory_map_desc_size = map.size;
|
||||||
|
|
||||||
// bootsvc->Stall(5000000);
|
// bootsvc->Stall(5000000);
|
||||||
|
|
||||||
status = bootsvc->ExitBootServices(image_handle, map.key);
|
status = bootsvc->ExitBootServices(image_handle, map.key);
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ memory_init_pointer_fixup(EFI_BOOT_SERVICES *bootsvc, EFI_RUNTIME_SERVICES *runs
|
|||||||
|
|
||||||
// Reserve a page for our replacement PML4
|
// Reserve a page for our replacement PML4
|
||||||
EFI_PHYSICAL_ADDRESS addr = 0;
|
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.");
|
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to allocate PML4 page.");
|
||||||
|
|
||||||
new_pml4 = (uint64_t *)addr;
|
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_DATA_MEMTYPE:
|
||||||
case KERNEL_LOG_MEMTYPE:
|
case KERNEL_LOG_MEMTYPE:
|
||||||
d->Attribute |= EFI_MEMORY_RUNTIME;
|
d->Attribute |= EFI_MEMORY_RUNTIME;
|
||||||
d->VirtualStart = d->PhysicalStart + KERNEL_VIRT_ADDRESS;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (d->Attribute & EFI_MEMORY_RUNTIME) {
|
if (d->Attribute & EFI_MEMORY_RUNTIME) {
|
||||||
d->VirtualStart = d->PhysicalStart;
|
d->VirtualStart = d->PhysicalStart + KERNEL_VIRT_ADDRESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d = INCREMENT_DESC(d, map->size);
|
d = INCREMENT_DESC(d, map->size);
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ struct popcorn_data {
|
|||||||
size_t log_length;
|
size_t log_length;
|
||||||
|
|
||||||
void *memory_map;
|
void *memory_map;
|
||||||
|
size_t memory_map_length;
|
||||||
|
size_t memory_map_desc_size;
|
||||||
|
|
||||||
void *runtime;
|
void *runtime;
|
||||||
|
|
||||||
void *acpi_table;
|
void *acpi_table;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "font.h"
|
#include "font.h"
|
||||||
#include "interrupts.h"
|
#include "interrupts.h"
|
||||||
#include "kernel_data.h"
|
#include "kernel_data.h"
|
||||||
|
#include "memory.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -40,6 +41,10 @@ void
|
|||||||
kernel_main(popcorn_data *header)
|
kernel_main(popcorn_data *header)
|
||||||
{
|
{
|
||||||
console cons = load_console(header);
|
console cons = load_console(header);
|
||||||
|
memory_manager::create(
|
||||||
|
header->memory_map,
|
||||||
|
header->memory_map_length,
|
||||||
|
header->memory_map_desc_size);
|
||||||
|
|
||||||
interrupts_init();
|
interrupts_init();
|
||||||
interrupts_enable();
|
interrupts_enable();
|
||||||
|
|||||||
187
src/kernel/memory.cpp
Normal file
187
src/kernel/memory.cpp
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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<const efi_memory_descriptor *>(
|
||||||
|
reinterpret_cast<const uint8_t *>(d) + desc_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct page_table
|
||||||
|
{
|
||||||
|
uint64_t entries[512];
|
||||||
|
|
||||||
|
page_table * next(int i) const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<page_table *>(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<efi_memory_descriptor const *>(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<uint64_t>(&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<uint64_t>(&tables[2]) | 0xb;
|
||||||
|
|
||||||
|
for (int i = 0; i < 512; ++i)
|
||||||
|
tables[3].entries[i] = (start + 0x1000) | 0xb;
|
||||||
|
tables[2].entries[pdt_index] = reinterpret_cast<uint64_t>(&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<char *>(start);
|
||||||
|
while (*hello) *world++ = *hello++;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_manager::memory_manager(void *efi_runtime, void *memory_map, size_t map_length)
|
||||||
|
{
|
||||||
|
}
|
||||||
19
src/kernel/memory.h
Normal file
19
src/kernel/memory.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
74
src/modules/kutil/enum_bitfields.h
Normal file
74
src/modules/kutil/enum_bitfields.h
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
template<typename E>
|
||||||
|
struct is_enum_bitfield { static constexpr bool value = false; };
|
||||||
|
|
||||||
|
#define IS_BITFIELD(name) \
|
||||||
|
template<> struct is_enum_bitfield<name> {static constexpr bool value=true;}
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
typename std::enable_if<is_enum_bitfield<E>::value,E>::type
|
||||||
|
operator & (E lhs, E rhs)
|
||||||
|
{
|
||||||
|
return static_cast<E> (
|
||||||
|
static_cast<typename std::underlying_type<E>::type>(lhs) &
|
||||||
|
static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
typename std::enable_if<is_enum_bitfield<E>::value,E>::type
|
||||||
|
operator ^ (E lhs, E rhs)
|
||||||
|
{
|
||||||
|
return static_cast<E> (
|
||||||
|
static_cast<typename std::underlying_type<E>::type>(lhs) ^
|
||||||
|
static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
typename std::enable_if<is_enum_bitfield<E>::value,E>::type
|
||||||
|
operator ~ (E rhs)
|
||||||
|
{
|
||||||
|
return static_cast<E>(~static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
|
||||||
|
operator |= (E &lhs, E rhs)
|
||||||
|
{
|
||||||
|
lhs = static_cast<E>(
|
||||||
|
static_cast<typename std::underlying_type<E>::type>(lhs) |
|
||||||
|
static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
|
||||||
|
operator &= (E &lhs, E rhs)
|
||||||
|
{
|
||||||
|
lhs = static_cast<E>(
|
||||||
|
static_cast<typename std::underlying_type<E>::type>(lhs) &
|
||||||
|
static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
|
||||||
|
operator ^= (E &lhs, E rhs)
|
||||||
|
{
|
||||||
|
lhs = static_cast<E>(
|
||||||
|
static_cast<typename std::underlying_type<E>::type>(lhs) ^
|
||||||
|
static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
typename std::enable_if<is_enum_bitfield<E>::value,bool>::type
|
||||||
|
operator ! (E rhs)
|
||||||
|
{
|
||||||
|
return static_cast<typename std::underlying_type<E>::type>(rhs) == 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user