mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
The UEFI spec specifically calls out memory types with the high bit set as being available for OS loaders' custom use. However, it seems many UEFI firmware implementations don't handle this well. (Virtualbox, and the firmware on my Intel NUC and Dell XPS laptop to name a few.) So sadly since we can't rely on this feature of UEFI in all cases, we can't use it at all. Instead, treat _all_ memory tagged as EfiLoaderData as possibly containing data that's been passed to the OS by the bootloader and don't free it yet. This will need to be followed up with a change that copies anything we need to save and frees this memory. See: https://github.com/kiznit/rainbow-os/blob/master/boot/machine/efi/README.md
188 lines
4.6 KiB
C++
188 lines
4.6 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 "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;
|
|
}
|
|
|
|
/// 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"};
|
|
|
|
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);
|
|
|
|
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
|
|
|
|
args::program &kernel = args->programs[0];
|
|
paging::map_pages(args, kernel.phys_addr, kernel.virt_addr, kernel.size);
|
|
kernel::entrypoint kentry =
|
|
reinterpret_cast<kernel::entrypoint>(kernel.entrypoint);
|
|
status.next();
|
|
|
|
memory::virtualize(args->pml4, map, st->runtime_services);
|
|
status.next();
|
|
|
|
change_pointer(args->pml4);
|
|
hw::setup_cr4();
|
|
status.next();
|
|
|
|
kentry(args);
|
|
debug_break();
|
|
return uefi::status::unsupported;
|
|
}
|
|
|