The previous frame allocator involved a lot of splitting and merging linked lists and lost all information about frames while they were allocated. The new allocator is based on an array of descriptor structures and a bitmap. Each memory map region of allocatable memory becomes one or more descriptors, each mapping up to 1GiB of physical memory. The descriptors implement two levels of a bitmap tree, and have a pointer into the large contiguous bitmap to track individual pages.
219 lines
5.3 KiB
C++
219 lines
5.3 KiB
C++
#include <uefi/types.h>
|
|
#include <uefi/guid.h>
|
|
#include <uefi/tables.h>
|
|
#include <uefi/protos/simple_text_output.h>
|
|
|
|
#include <stdalign.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#include "console.h"
|
|
#include "cpu/cpu.h"
|
|
#include "error.h"
|
|
#include "fs.h"
|
|
#include "hardware.h"
|
|
#include "loader.h"
|
|
#include "memory.h"
|
|
#include "paging.h"
|
|
#include "status.h"
|
|
|
|
#include "kernel_args.h"
|
|
|
|
namespace kernel {
|
|
#include "kernel_memory.h"
|
|
}
|
|
|
|
namespace args = kernel::args;
|
|
|
|
namespace boot {
|
|
|
|
constexpr int max_modules = 5; // Max modules to allocate room for
|
|
constexpr int max_programs = 5; // Max programs to allocate room for
|
|
|
|
struct program_desc
|
|
{
|
|
const wchar_t *name;
|
|
const wchar_t *path;
|
|
};
|
|
|
|
const program_desc program_list[] = {
|
|
{L"kernel", L"jsix.elf"},
|
|
{L"null driver", L"nulldrv.elf"},
|
|
{L"fb driver", L"fb.elf"},
|
|
};
|
|
|
|
/// Change a pointer to point to the higher-half linear-offset version
|
|
/// of the address it points to.
|
|
template <typename T>
|
|
void change_pointer(T *&pointer)
|
|
{
|
|
pointer = offset_ptr<T>(pointer, kernel::memory::page_offset);
|
|
}
|
|
|
|
/// Allocate space for kernel args. Allocates enough space from pool
|
|
/// memory for the args header and the module and program headers.
|
|
args::header *
|
|
allocate_args_structure(
|
|
uefi::boot_services *bs,
|
|
size_t max_modules,
|
|
size_t max_programs)
|
|
{
|
|
status_line status {L"Setting up kernel args memory"};
|
|
|
|
args::header *args = nullptr;
|
|
|
|
size_t args_size =
|
|
sizeof(args::header) + // The header itself
|
|
max_modules * sizeof(args::module) + // The module structures
|
|
max_programs * sizeof(args::program); // The program structures
|
|
|
|
try_or_raise(
|
|
bs->allocate_pool(uefi::memory_type::loader_data, args_size,
|
|
reinterpret_cast<void**>(&args)),
|
|
L"Could not allocate argument memory");
|
|
|
|
bs->set_mem(args, args_size, 0);
|
|
|
|
args->modules =
|
|
reinterpret_cast<args::module*>(args + 1);
|
|
args->num_modules = 0;
|
|
|
|
args->programs =
|
|
reinterpret_cast<args::program*>(args->modules + max_modules);
|
|
args->num_programs = 0;
|
|
|
|
return args;
|
|
}
|
|
|
|
/// Add a module to the kernel args list
|
|
inline void
|
|
add_module(args::header *args, args::mod_type type, buffer &data)
|
|
{
|
|
args::module &m = args->modules[args->num_modules++];
|
|
m.type = type;
|
|
m.location = data.data;
|
|
m.size = data.size;
|
|
}
|
|
|
|
/// Check that all required cpu features are supported
|
|
void
|
|
check_cpu_supported()
|
|
{
|
|
status_line status {L"Checking CPU features"};
|
|
|
|
cpu::cpu_id cpu;
|
|
uint64_t missing = cpu.missing();
|
|
if (missing) {
|
|
#define CPU_FEATURE_OPT(...)
|
|
#define CPU_FEATURE_REQ(name, ...) \
|
|
if (!cpu.has_feature(cpu::feature::name)) { \
|
|
status::fail(L"CPU required feature " L ## #name, uefi::status::unsupported); \
|
|
}
|
|
#include "cpu/features.inc"
|
|
#undef CPU_FEATURE_REQ
|
|
#undef CPU_FEATURE_OPT
|
|
|
|
error::raise(uefi::status::unsupported, L"CPU not supported");
|
|
}
|
|
}
|
|
|
|
/// The main procedure for the portion of the loader that runs while
|
|
/// UEFI is still in control of the machine. (ie, while the loader still
|
|
/// has access to boot services.
|
|
args::header *
|
|
uefi_preboot(uefi::handle image, uefi::system_table *st)
|
|
{
|
|
status_line status {L"Performing UEFI pre-boot"};
|
|
|
|
uefi::boot_services *bs = st->boot_services;
|
|
uefi::runtime_services *rs = st->runtime_services;
|
|
memory::init_pointer_fixup(bs, rs);
|
|
|
|
args::header *args =
|
|
allocate_args_structure(bs, max_modules, max_programs);
|
|
|
|
args->magic = args::magic;
|
|
args->version = args::version;
|
|
args->runtime_services = rs;
|
|
args->acpi_table = hw::find_acpi_table(st);
|
|
paging::allocate_tables(args, bs);
|
|
|
|
memory::mark_pointer_fixup(&args->runtime_services);
|
|
|
|
fs::file disk = fs::get_boot_volume(image, bs);
|
|
|
|
buffer symbols = loader::load_file(disk, L"symbol table", L"symbol_table.dat",
|
|
uefi::memory_type::loader_data);
|
|
add_module(args, args::mod_type::symbol_table, symbols);
|
|
|
|
for (auto &desc : program_list) {
|
|
buffer buf = loader::load_file(disk, desc.name, desc.path);
|
|
args::program &program = args->programs[args->num_programs++];
|
|
loader::load_program(program, desc.name, buf, bs);
|
|
}
|
|
|
|
for (unsigned i = 0; i < args->num_modules; ++i) {
|
|
args::module &mod = args->modules[i];
|
|
change_pointer(mod.location);
|
|
}
|
|
|
|
return args;
|
|
}
|
|
|
|
memory::efi_mem_map
|
|
uefi_exit(args::header *args, uefi::handle image, uefi::boot_services *bs)
|
|
{
|
|
status_line status {L"Exiting UEFI", nullptr, false};
|
|
|
|
memory::efi_mem_map map =
|
|
memory::build_kernel_mem_map(args, bs);
|
|
|
|
try_or_raise(
|
|
bs->exit_boot_services(image, map.key),
|
|
L"Failed to exit boot services");
|
|
|
|
return map;
|
|
}
|
|
|
|
} // namespace boot
|
|
|
|
/// The UEFI entrypoint for the loader.
|
|
extern "C" uefi::status
|
|
efi_main(uefi::handle image, uefi::system_table *st)
|
|
{
|
|
using namespace boot;
|
|
console con(st->boot_services, st->con_out);
|
|
check_cpu_supported();
|
|
|
|
args::header *args = uefi_preboot(image, st);
|
|
memory::efi_mem_map map = uefi_exit(args, image, st->boot_services);
|
|
|
|
args->video = con.fb();
|
|
status_bar status {con.fb()}; // Switch to fb status display
|
|
|
|
// Map the kernel to the appropriate address
|
|
args::program &kernel = args->programs[0];
|
|
for (auto §ion : kernel.sections)
|
|
if (section.size)
|
|
paging::map_section(args, section);
|
|
|
|
memory::fix_frame_blocks(args);
|
|
|
|
kernel::entrypoint kentry =
|
|
reinterpret_cast<kernel::entrypoint>(kernel.entrypoint);
|
|
status.next();
|
|
|
|
|
|
hw::setup_control_regs();
|
|
memory::virtualize(args->pml4, map, st->runtime_services);
|
|
status.next();
|
|
|
|
change_pointer(args->pml4);
|
|
status.next();
|
|
|
|
kentry(args);
|
|
debug_break();
|
|
return uefi::status::unsupported;
|
|
}
|
|
|