[boot] Restructure boot paging and program loading

Restructuring paging into an object that carries its page cache with it
and makes for simpler code. Program loading is also changed to not copy
the pages loaded from the file into new pages - we can impose a new
constraint that anything loaded by boot have a simple, page-aligned
layout so that we can just map the existing pages into the right
addresses. Also included are some linker script changes to help
accommodate this.
This commit is contained in:
Justin C. Miller
2023-02-05 22:02:41 -08:00
parent aba45b9b67
commit ab31825ab3
16 changed files with 406 additions and 301 deletions

View File

@@ -1,7 +1,6 @@
#include <uefi/boot_services.h>
#include <uefi/types.h>
#include <bootproto/init.h>
#include <elf/file.h>
#include <elf/headers.h>
#include <util/pointers.h>
@@ -22,9 +21,7 @@ namespace loader {
using memory::alloc_type;
util::buffer
load_file(
fs::file &disk,
const wchar_t *path)
load_file(fs::file &disk, const wchar_t *path)
{
status_line status(L"Loading file", path);
@@ -61,73 +58,108 @@ verify_kernel_header(elf::file &kernel, util::const_buffer data)
header->version_gitsha);
}
bootproto::program *
load_program(
fs::file &disk,
const wchar_t *name,
const descriptor &desc,
bool verify)
inline void
elf_error(const elf::file &elf, util::const_buffer data)
{
status_line status(L"Loading program", name);
auto *header = elf.header();
console::print(L" progam size: %d\r\n", data.count);
console::print(L" word size: %d\r\n", header->word_size);
console::print(L" endianness: %d\r\n", header->endianness);
console::print(L" ELF ident version: %d\r\n", header->ident_version);
console::print(L" OS ABI: %d\r\n", header->os_abi);
console::print(L" file type: %d\r\n", header->file_type);
console::print(L" machine type: %d\r\n", header->machine_type);
console::print(L" ELF version: %d\r\n", header->version);
util::const_buffer data = load_file(disk, desc.path);
error::raise(uefi::status::load_error, L"ELF file not valid");
}
elf::file program {data};
if (!program.valid()) {
auto *header = program.header();
console::print(L" progam size: %d\r\n", data.count);
console::print(L" word size: %d\r\n", header->word_size);
console::print(L" endianness: %d\r\n", header->endianness);
console::print(L" ELF ident version: %d\r\n", header->ident_version);
console::print(L" OS ABI: %d\r\n", header->os_abi);
console::print(L" file type: %d\r\n", header->file_type);
console::print(L" machine type: %d\r\n", header->machine_type);
console::print(L" ELF version: %d\r\n", header->version);
inline uintptr_t
allocate_bss(elf::segment_header seg)
{
size_t page_count = memory::bytes_to_pages(seg.mem_size);
void *pages = g_alloc.allocate_pages(page_count, alloc_type::program, true);
return reinterpret_cast<uintptr_t>(pages);
}
error::raise(uefi::status::load_error, L"ELF file not valid");
}
if (verify)
verify_kernel_header(program, data);
void
parse_program(const wchar_t *name, util::const_buffer data, bootproto::program &program)
{
status_line status(L"Preparing program", name);
elf::file elf {data};
if (!elf.valid())
elf_error(elf, data); // does not return
size_t num_sections = 0;
for (auto &seg : program.segments()) {
for (auto &seg : elf.segments()) {
if (seg.type == elf::segment_type::load)
++num_sections;
}
bootproto::program_section *sections = new bootproto::program_section [num_sections];
bootproto::program_section *sections =
new bootproto::program_section [num_sections];
size_t next_section = 0;
for (auto &seg : program.segments()) {
for (auto &seg : elf.segments()) {
if (seg.type != elf::segment_type::load)
continue;
bootproto::program_section &section = sections[next_section++];
uintptr_t virt_addr = seg.vaddr;
size_t mem_size = seg.mem_size;
// Page-align the section, which may require increasing the size
size_t prelude = virt_addr & 0xfff;
mem_size += prelude;
virt_addr &= ~0xfffull;
size_t page_count = memory::bytes_to_pages(mem_size);
void *pages = g_alloc.allocate_pages(page_count, alloc_type::program, true);
const void *source = util::offset_pointer(data.pointer, seg.offset);
g_alloc.copy(util::offset_pointer(pages, prelude), source, seg.file_size);
section.phys_addr = reinterpret_cast<uintptr_t>(pages);
section.virt_addr = virt_addr;
section.size = mem_size;
section.phys_addr = elf.base() + seg.offset;
section.virt_addr = seg.vaddr;
section.size = seg.mem_size;
section.type = static_cast<bootproto::section_flags>(seg.flags);
if (seg.mem_size != seg.file_size)
section.phys_addr = allocate_bss(seg);
}
bootproto::program *prog = new bootproto::program;
prog->sections = { .pointer = sections, .count = num_sections };
prog->phys_base = program.base();
prog->entrypoint = program.entrypoint();
return prog;
program.sections = { .pointer = sections, .count = num_sections };
program.phys_base = elf.base();
program.entrypoint = elf.entrypoint();
}
uintptr_t
load_program(
util::const_buffer data,
const wchar_t *name,
paging::pager &pager,
bool verify)
{
using util::bits::has;
status_line status(L"Loading program", name);
elf::file elf {data};
if (!elf.valid())
elf_error(elf, data); // does not return
if (verify)
verify_kernel_header(elf, data);
size_t num_sections = 0;
for (auto &seg : elf.segments()) {
if (seg.type == elf::segment_type::load)
++num_sections;
}
for (auto &seg : elf.segments()) {
if (seg.type != elf::segment_type::load)
continue;
uintptr_t phys_addr = elf.base() + seg.offset;
if (seg.mem_size != seg.file_size)
phys_addr = allocate_bss(seg);
pager.map_pages(phys_addr, seg.vaddr,
memory::bytes_to_pages(seg.mem_size),
has(seg.flags, elf::segment_flags::write),
has(seg.flags, elf::segment_flags::exec));
}
return elf.entrypoint();
}
void