Files
jsix/src/boot/main.cpp
Justin C. Miller d75b9c22d4 [boot] Don't use custom UEFI memory types
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
2021-01-08 22:40:30 -08:00

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;
}