diff --git a/src/boot/loader.cpp b/src/boot/loader.cpp index 735036e..5fd8482 100644 --- a/src/boot/loader.cpp +++ b/src/boot/loader.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include #include @@ -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(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 §ion = 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(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(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 diff --git a/src/boot/loader.h b/src/boot/loader.h index 7b713c2..5bf0521 100644 --- a/src/boot/loader.h +++ b/src/boot/loader.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include namespace bootproto { @@ -11,32 +12,47 @@ namespace bootproto { namespace boot { -class descriptor; - -namespace fs { - class file; -} +namespace fs { class file; } +namespace paging { class pager; } namespace loader { +// Bootloader ELF file requirements +// ================================ +// The bootloader accepts a subset of valid ELF files to load, with +// the following requiresments: +// 1. All program segments are page-aligned. +// 2. PT_LOAD segments cannot contain a mix of PROGBITS and NOBITS +// sections. i.e., section memory size must equal either zero or +// its file size. +// 3. There are only one or zero PT_LOAD NOBITS program segments. + + /// Load a file from disk into memory. /// \arg disk The opened UEFI filesystem to load from /// \arg path The path of the file to load -util::buffer -load_file( - fs::file &disk, - const wchar_t *path); +util::buffer load_file(fs::file &disk, const wchar_t *path); -/// Parse and load an ELF file in memory into a loaded image. -/// \arg disk The opened UEFI filesystem to load from -/// \arg desc The descriptor identifying the program +/// Parse a buffer holding ELF data into a bootproto::program /// \arg name The human-readable name of the program to load -/// \arg verify If this is the kernel and should have its header verified -bootproto::program * -load_program( - fs::file &disk, +/// \arg data A buffer containing an ELF executable +/// \arg program A program structure to fill +void parse_program( const wchar_t *name, - const descriptor &desc, + util::const_buffer data, + bootproto::program &program); + +/// Parse a buffer holding ELF data and map it to be runnable +/// \arg data The ELF data in memory +/// \arg name The human-readable name of the program to load +/// \arg pager The kernel space pager, to map programs into +/// \arg verify If this is the kernel and should have its header verified +/// \returns The entrypoint to the loaded program +uintptr_t +load_program( + util::const_buffer data, + const wchar_t *name, + paging::pager &pager, bool verify = false); /// Load a file from disk into memory, creating an init args module diff --git a/src/boot/main.cpp b/src/boot/main.cpp index cbdbe2d..0461b22 100644 --- a/src/boot/main.cpp +++ b/src/boot/main.cpp @@ -60,14 +60,17 @@ uefi_preboot(uefi::handle image, uefi::system_table *st) args->acpi_table = hw::find_acpi_table(st); memory::mark_pointer_fixup(&args->runtime_services); - paging::allocate_tables(args); - return args; } /// Load the kernel and other programs from disk -void -load_resources(bootproto::args *args, video::screen *screen, uefi::handle image, uefi::boot_services *bs) +bootproto::entrypoint +load_resources( + bootproto::args *args, + video::screen *screen, + uefi::handle image, + paging::pager &pager, + uefi::boot_services *bs) { status_line status {L"Loading programs"}; @@ -75,17 +78,16 @@ load_resources(bootproto::args *args, video::screen *screen, uefi::handle image, util::buffer bc_data = loader::load_file(disk, L"jsix\\boot.conf"); bootconfig bc {bc_data, bs}; - args->kernel = loader::load_program(disk, L"kernel", bc.kernel(), true); - args->init = loader::load_program(disk, L"init server", bc.init()); - args->flags = static_cast(bc.flags()); + util::buffer kernel = loader::load_file(disk, bc.kernel().path); + uintptr_t kentry = loader::load_program(kernel, L"jsix kernel", pager, true); - loader::load_module(disk, L"initrd", bc.initrd(), - bootproto::module_type::initrd, 0); + args->flags = static_cast(bc.flags()); namespace bits = util::bits; using bootproto::desc_flags; bool has_panic = false; + util::buffer panic; if (screen) { video::make_module(screen); @@ -94,7 +96,7 @@ load_resources(bootproto::args *args, video::screen *screen, uefi::handle image, // give it priority for (const descriptor &d : bc.panics()) { if (bits::has(d.flags, desc_flags::graphical)) { - args->panic = loader::load_program(disk, L"panic handler", d); + panic = loader::load_file(disk, d.path); has_panic = true; break; } @@ -104,16 +106,28 @@ load_resources(bootproto::args *args, video::screen *screen, uefi::handle image, if (!has_panic) { for (const descriptor &d : bc.panics()) { if (!bits::has(d.flags, desc_flags::graphical)) { - args->panic = loader::load_program(disk, L"panic handler", d); + panic = loader::load_file(disk, d.path); has_panic = true; break; } } } - const wchar_t *symbol_file = bc.symbols(); - if (has_panic && symbol_file && *symbol_file) - args->symbol_table = loader::load_file(disk, symbol_file).pointer; + if (has_panic) { + args->panic_handler = loader::load_program(panic, L"panic handler", pager); + + const wchar_t *symbol_file = bc.symbols(); + if (symbol_file && *symbol_file) + args->symbol_table = loader::load_file(disk, symbol_file); + } + + util::buffer init = loader::load_file(disk, bc.init().path); + loader::parse_program(L"init server", init, args->init); + + loader::load_module(disk, L"initrd", bc.initrd(), + bootproto::module_type::initrd, 0); + + return reinterpret_cast(kentry); } memory::efi_mem_map @@ -156,35 +170,31 @@ efi_main(uefi::handle image, uefi::system_table *st) con.announce(); bootproto::args *args = uefi_preboot(image, st); - load_resources(args, screen, image, bs); + + paging::pager pager {bs}; + + bootproto::entrypoint kentry = + load_resources(args, screen, image, pager, bs); + + pager.update_kernel_args(args); memory::efi_mem_map map = uefi_exit(args, image, st->boot_services); args->allocations = allocs; - args->modules = reinterpret_cast(modules); + args->init_modules = reinterpret_cast(modules); status_bar status {screen}; // Switch to fb status display - // Map the kernel and panic handler to the appropriate addresses - paging::map_program(args, *args->kernel); - paging::map_program(args, *args->panic); + memory::fix_frame_blocks(args, pager); - memory::fix_frame_blocks(args); - - bootproto::entrypoint kentry = - reinterpret_cast(args->kernel->entrypoint); //status.next(); hw::setup_control_regs(); - memory::virtualize(args->pml4, map, st->runtime_services); + memory::virtualize(pager, map, st->runtime_services); //status.next(); change_pointer(args); change_pointer(args->pml4); - - change_pointer(args->kernel); - change_pointer(args->kernel->sections.pointer); - change_pointer(args->init); - change_pointer(args->init->sections.pointer); + change_pointer(args->init.sections.pointer); //status.next(); diff --git a/src/boot/memory.cpp b/src/boot/memory.cpp index 6cd43bb..f64a73c 100644 --- a/src/boot/memory.cpp +++ b/src/boot/memory.cpp @@ -57,16 +57,14 @@ mark_pointer_fixup(void **p) } void -virtualize(void *pml4, efi_mem_map &map, uefi::runtime_services *rs) +virtualize(paging::pager &pager, efi_mem_map &map, uefi::runtime_services *rs) { - paging::add_current_mappings(reinterpret_cast(pml4)); + pager.add_current_mappings(); for (auto &desc : map) desc.virtual_start = desc.physical_start + bootproto::mem::linear_offset; - // Write our new PML4 pointer to CR3 - asm volatile ( "mov %0, %%cr3" :: "r" (pml4) ); - __sync_synchronize(); + pager.install(); try_or_raise( rs->set_virtual_address_map( diff --git a/src/boot/memory.h b/src/boot/memory.h index 42c649e..42cce1b 100644 --- a/src/boot/memory.h +++ b/src/boot/memory.h @@ -10,6 +10,11 @@ namespace uefi { } namespace boot { + +namespace paging { + class pager; +} + namespace memory { class efi_mem_map; @@ -42,7 +47,7 @@ void mark_pointer_fixup(void **p); /// \arg pml4 The root page table for the new mappings /// \arg map The UEFI memory map, used to update runtime services void virtualize( - void *pml4, + paging::pager &pager, efi_mem_map &map, uefi::runtime_services *rs); diff --git a/src/boot/memory_map.cpp b/src/boot/memory_map.cpp index a59e794..afbc6be 100644 --- a/src/boot/memory_map.cpp +++ b/src/boot/memory_map.cpp @@ -278,7 +278,7 @@ build_frame_blocks(const util::counted &kmap) } void -fix_frame_blocks(bootproto::args *args) +fix_frame_blocks(bootproto::args *args, paging::pager &pager) { util::counted &blocks = args->frame_blocks; @@ -290,8 +290,7 @@ fix_frame_blocks(bootproto::args *args) uintptr_t addr = reinterpret_cast(blocks.pointer); // Map the frame blocks to the appropriate address - paging::map_pages(args, addr, - bootproto::mem::bitmap_offset, pages, true, false); + pager.map_pages(addr, bootproto::mem::bitmap_offset, pages, true, false); uintptr_t offset = bootproto::mem::bitmap_offset - addr; diff --git a/src/boot/memory_map.h b/src/boot/memory_map.h index 053e927..7c696b6 100644 --- a/src/boot/memory_map.h +++ b/src/boot/memory_map.h @@ -17,6 +17,11 @@ namespace bootproto { } namespace boot { + +namespace paging { + class pager; +} + namespace memory { /// Struct that represents UEFI's memory map. Contains a pointer to the map data @@ -56,7 +61,7 @@ util::counted build_kernel_map(efi_mem_map &map); util::counted build_frame_blocks(const util::counted &kmap); /// Map the frame allocation maps to the right spot and fix up pointers -void fix_frame_blocks(bootproto::args *args); +void fix_frame_blocks(bootproto::args *args, paging::pager &pager); } // namespace boot } // namespace memory diff --git a/src/boot/paging.cpp b/src/boot/paging.cpp index f3af3c0..f335b4b 100644 --- a/src/boot/paging.cpp +++ b/src/boot/paging.cpp @@ -4,7 +4,6 @@ #include #include "allocator.h" -#include "console.h" #include "error.h" #include "loader.h" #include "memory.h" @@ -20,7 +19,7 @@ using memory::page_size; // Flags: 0 0 0 1 0 0 0 0 0 0 0 1 = 0x0101 // IGN | | | | | | | | +- Present // | | | | | | | +--- Writeable -// | | | | | | +----- Usermode access (supervisor only) +// | | | | | | +----- Usermode access (Supervisor only) // | | | | | +------- PWT (determining memory type for page) // | | | | +---------- PCD (determining memory type for page) // | | | +------------ Accessed flag (not accessed yet) @@ -33,7 +32,7 @@ constexpr uint64_t page_flags = 0x101; // Flags: 0 0 0 0 1 1 0 0 0 1 0 1 1 = 0x018b // | IGN | | | | | | | | +- Present // | | | | | | | | +--- Writeable -// | | | | | | | +----- Supervisor only +// | | | | | | | +----- Usermode access (Supervisor only) // | | | | | | +------- PWT (determining memory type for page) // | | | | | +---------- PCD (determining memory type for page) // | | | | +------------ Accessed flag (not accessed yet) @@ -57,17 +56,22 @@ constexpr uint64_t huge_page_flags = 0x18b; constexpr uint64_t table_flags = 0x003; -inline void * -pop_pages(util::counted &pages, size_t count) +/// Struct to allow easy accessing of a memory page being used as a page table. +struct page_table { - if (count > pages.count) - error::raise(uefi::status::out_of_resources, L"Page table cache empty", 0x7ab1e5); + uint64_t entries[512]; - void *next = pages.pointer; - pages.pointer = util::offset_pointer(pages.pointer, count*page_size); - pages.count -= count; - return next; -} + inline page_table * get(int i, uint16_t *flags = nullptr) const { + uint64_t entry = entries[i]; + if ((entry & 1) == 0) return nullptr; + if (flags) *flags = entry & 0xfff; + return reinterpret_cast(entry & ~0xfffull); + } + + inline void set(int i, void *p, uint16_t flags) { + entries[i] = reinterpret_cast(p) | (flags & 0xfff); + } +}; /// Iterator over page table entries. template @@ -75,25 +79,23 @@ class page_entry_iterator { public: /// Constructor. - /// \arg virt Virtual address this iterator is starting at - /// \arg pml4 Root of the page tables to iterate - /// \arg pages Cache of usable table pages + /// \arg virt Virtual address this iterator is starting at + /// \arg pgr The pager, used for its page table cache page_entry_iterator( uintptr_t virt, - page_table *pml4, - util::counted &pages) : - m_pages(pages) + pager &pgr) : + m_pager(pgr) { - m_table[0] = pml4; + m_table[0] = pgr.m_pml4; for (unsigned i = 0; i < D; ++i) { m_index[i] = static_cast((virt >> (12 + 9*(3-i))) & 0x1ff); ensure_table(i); } } - uintptr_t vaddress() const { + uintptr_t vaddress(unsigned level = D) const { uintptr_t address = 0; - for (unsigned i = 0; i < D; ++i) + for (unsigned i = 0; i < level; ++i) address |= static_cast(m_index[i]) << (12 + 9*(3-i)); if (address & (1ull<<47)) // canonicalize the address address |= (0xffffull<<48); @@ -114,6 +116,7 @@ public: } uint64_t & operator*() { return entry(D-1); } + uint64_t operator[](int i) { return i < D ? m_index[i] : 0; } private: inline uint64_t & entry(unsigned level) { return m_table[level]->entries[m_index[level]]; } @@ -129,7 +132,7 @@ private: uint64_t & parent_ent = entry(level - 1); if (!(parent_ent & 1)) { - page_table *table = reinterpret_cast(pop_pages(m_pages, 1)); + page_table *table = reinterpret_cast(m_pager.pop_pages(1)); parent_ent = (reinterpret_cast(table) & ~0xfffull) | table_flags; m_table[level] = table; } else { @@ -137,44 +140,62 @@ private: } } - util::counted &m_pages; + pager &m_pager; page_table *m_table[D]; uint16_t m_index[D]; }; - -static void -add_offset_mappings(page_table *pml4, util::counted &pages) +pager::pager(uefi::boot_services *bs) : + m_bs {bs} { - uintptr_t phys = 0; - uintptr_t virt = bootproto::mem::linear_offset; // Start of offset-mapped area - size_t page_count = 64 * 1024; // 64 TiB of 1 GiB pages - constexpr size_t GiB = 0x40000000ull; + status_line status(L"Allocating initial page tables"); - page_entry_iterator<2> iterator{virt, pml4, pages}; + // include 1 extra for kernel PML4 + static constexpr size_t tables_needed = pd_tables + extra_tables + 1; - while (true) { - *iterator = phys | huge_page_flags; - if (--page_count == 0) - break; + void *addr = g_alloc.allocate_pages(tables_needed, alloc_type::page_table, true); + m_bs->set_mem(addr, tables_needed * page_size, 0); + m_table_pages = { .pointer = addr, .count = tables_needed }; - iterator.increment(); - phys += GiB; - } -} - -static void -add_kernel_pds(page_table *pml4, util::counted &pages) -{ - constexpr unsigned start = arch::kernel_root_index; - constexpr unsigned end = arch::table_entries; - - for (unsigned i = start; i < end; ++i) - pml4->set(i, pop_pages(pages, 1), table_flags); + create_kernel_tables(); } void -add_current_mappings(page_table *new_pml4) +pager::map_pages( + uintptr_t phys, uintptr_t virt, size_t count, + bool write_flag, bool exe_flag) +{ + if (!count) + return; + + page_entry_iterator<4> iterator {virt, *this}; + + uint64_t flags = page_flags; + if (!exe_flag) flags |= (1ull << 63); // set NX bit + if (write_flag) flags |= 2; + + while (true) { + uint64_t entry = phys | flags; + *iterator = entry; + + if (--count == 0) + break; + + iterator.increment(); + phys += page_size; + } +} + +void +pager::update_kernel_args(bootproto::args *args) +{ + status_line status {L"Updating kernel args"}; + args->pml4 = reinterpret_cast(m_pml4); + args->page_tables = m_table_pages; +} + +void +pager::add_current_mappings() { // Get the pointer to the current PML4 page_table *old_pml4 = 0; @@ -185,98 +206,49 @@ add_current_mappings(page_table *new_pml4) for (int i = 0; i < halfway; ++i) { uint64_t entry = old_pml4->entries[i]; if (entry & 1) - new_pml4->entries[i] = entry; + m_pml4->entries[i] = entry; } } -void -allocate_tables(bootproto::args *args) +page_table * +pager::pop_pages(size_t count) { - status_line status(L"Allocating initial page tables"); + if (count > m_table_pages.count) + error::raise(uefi::status::out_of_resources, L"Page table cache empty", 0x7ab1e5); - static constexpr size_t pd_tables = 256; // number of pages for kernelspace PDs - static constexpr size_t extra_tables = 64; // number of extra pages - - // number of pages for kernelspace PDs + PML4 - static constexpr size_t kernel_tables = pd_tables + 1; - - static constexpr size_t tables_needed = kernel_tables + extra_tables; - - void *addr = g_alloc.allocate_pages(tables_needed, alloc_type::page_table, true); - page_table *pml4 = reinterpret_cast(addr); - - args->pml4 = pml4; - args->page_tables = { .pointer = pml4 + 1, .count = tables_needed - 1 }; - - console::print(L" First page (pml4) at: 0x%lx\r\n", pml4); - - add_kernel_pds(pml4, args->page_tables); - add_offset_mappings(pml4, args->page_tables); - - //console::print(L" Set up initial mappings, %d spare tables.\r\n", args->table_count); -} - -template -constexpr bool has_flag(E set, E flag) { - return - (static_cast(set) & static_cast(flag)) == - static_cast(flag); + page_table *next = reinterpret_cast(m_table_pages.pointer); + m_table_pages.pointer = util::offset_pointer(m_table_pages.pointer, count*page_size); + m_table_pages.count -= count; + return next; } void -map_pages( - bootproto::args *args, - uintptr_t phys, uintptr_t virt, - size_t count, bool write_flag, bool exe_flag) +pager::create_kernel_tables() { - if (!count) - return; + m_pml4 = pop_pages(1); + page_table *pds = pop_pages(pd_tables); - paging::page_table *pml4 = - reinterpret_cast(args->pml4); + // Add PDs for all of high memory into the PML4 + constexpr unsigned start = arch::kernel_root_index; + constexpr unsigned end = arch::table_entries; + for (unsigned i = start; i < end; ++i) + m_pml4->set(i, &pds[i - start], table_flags); - page_entry_iterator<4> iterator{virt, pml4, args->page_tables}; - - uint64_t flags = page_flags; - if (!exe_flag) - flags |= (1ull << 63); // set NX bit - if (write_flag) - flags |= 2; + // Add the linear offset-mapped area + uintptr_t phys = 0; + uintptr_t virt_base = bootproto::mem::linear_offset; + size_t page_count = 64 * 1024; // 64 TiB of 1 GiB pages + constexpr size_t GiB = 0x40000000ull; + page_entry_iterator<2> iterator {virt_base, *this}; while (true) { - *iterator = phys | flags; - if (--count == 0) + *iterator = phys | huge_page_flags; + if (--page_count == 0) break; - iterator.increment(); - phys += page_size; + phys += GiB; } } -void -map_section( - bootproto::args *args, - const bootproto::program_section §ion) -{ - using bootproto::section_flags; - - map_pages( - args, - section.phys_addr, - section.virt_addr, - memory::bytes_to_pages(section.size), - has_flag(section.type, section_flags::write), - has_flag(section.type, section_flags::execute)); -} - -void -map_program( - bootproto::args *args, - bootproto::program &program) -{ - for (auto §ion : program.sections) - paging::map_section(args, section); -} - } // namespace paging } // namespace boot diff --git a/src/boot/paging.h b/src/boot/paging.h index 32e25bd..27b00b7 100644 --- a/src/boot/paging.h +++ b/src/boot/paging.h @@ -3,60 +3,67 @@ /// Page table structure and related definitions #include #include -#include + +namespace bootproto { + struct args; +} namespace boot { namespace paging { -/// Struct to allow easy accessing of a memory page being used as a page table. -struct page_table +struct page_table; + +class pager { - uint64_t entries[512]; +public: + static constexpr size_t pd_tables = 256; // number of pages for kernelspace PDs + static constexpr size_t extra_tables = 64; // number of extra pages - inline page_table * get(int i, uint16_t *flags = nullptr) const { - uint64_t entry = entries[i]; - if ((entry & 1) == 0) return nullptr; - if (flags) *flags = entry & 0xfff; - return reinterpret_cast(entry & ~0xfffull); + pager(uefi::boot_services *bs); + + /// Map physical memory pages to virtual addresses in the given page tables. + /// \arg phys The physical address of the pages to map + /// \arg virt The virtual address at which to map the pages + /// \arg count The number of pages to map + /// \arg write_flag If true, mark the pages writeable + /// \arg exe_flag If true, mark the pages executable + void map_pages( + uintptr_t phys, + uintptr_t virt, + size_t count, + bool write_flag, + bool exe_flag); + + /// Update the kernel args structure before handing it off to the kernel + void update_kernel_args(bootproto::args *args); + + /// Copy existing page table entries from the UEFI PML4 into our new PML4. + /// Does not do a deep copy - the new PML4 is updated to point to the + /// existing next-level page tables in the current PML4. + void add_current_mappings(); + + /// Write the pager's PML4 pointer to CR3 + inline void install() const { + asm volatile ( "mov %0, %%cr3" :: "r" (m_pml4) ); + __sync_synchronize(); } - inline void set(int i, void *p, uint16_t flags) { - entries[i] = reinterpret_cast(p) | (flags & 0xfff); - } +private: + template + friend class page_entry_iterator; + + /// Get `count` table pages from the cache + page_table * pop_pages(size_t count); + + /// Allocate the kernel PML4 and PD tables out of the cache, and add the + /// linear offset mappings to them + void create_kernel_tables(); + + uefi::boot_services *m_bs; + util::counted m_table_pages; + page_table *m_pml4; + page_table *m_kernel_pds; }; -/// Allocate memory to be used for initial page tables. Initial offset-mapped -/// page tables are pre-filled. All pages are saved as a module in kernel args -/// and kernel args' `page_table_cache` and `num_free_tables` are updated with -/// the leftover space. -/// \arg args The kernel args struct, used for the page table cache and pml4 -void allocate_tables(bootproto::args *args); - -/// Copy existing page table entries to a new page table. Does not do a deep -/// copy - the new PML4 is updated to point to the existing next-level page -/// tables in the current PML4. -/// \arg new_pml4 The new PML4 to copy into -void add_current_mappings(page_table *new_pml4); - -/// Map physical memory pages to virtual addresses in the given page tables. -/// \arg args The kernel args struct, used for the page table cache and pml4 -/// \arg phys The physical address of the pages to map -/// \arg virt The virtual address at which to map the pages -/// \arg count The number of pages to map -/// \arg write_flag If true, mark the pages writeable -/// \arg exe_flag If true, mark the pages executable -void map_pages( - bootproto::args *args, - uintptr_t phys, uintptr_t virt, - size_t count, bool write_flag, bool exe_flag); - -/// Map the sections of a program in physical memory to their virtual memory -/// addresses in the given page tables. -/// \arg args The kernel args struct, used for the page table cache and pml4 -/// \arg program The program to load -void map_program( - bootproto::args *args, - bootproto::program &program); - } // namespace paging } // namespace boot diff --git a/src/kernel/assert.cpp b/src/kernel/assert.cpp index bdd162c..44f9bbb 100644 --- a/src/kernel/assert.cpp +++ b/src/kernel/assert.cpp @@ -7,10 +7,10 @@ uint32_t *apic_icr = reinterpret_cast(0xffffc000fee00300); void const *symbol_table = nullptr; void -install(uintptr_t entrypoint, const void *symbol_data) +install(uintptr_t entrypoint, util::const_buffer symbol_data) { IDT::set_nmi_handler(entrypoint); - symbol_table = symbol_data; + symbol_table = symbol_data.pointer; } } // namespace panic diff --git a/src/kernel/assert.h b/src/kernel/assert.h index 555f225..4d7f011 100644 --- a/src/kernel/assert.h +++ b/src/kernel/assert.h @@ -1,6 +1,8 @@ #pragma once #include + +#include #include "cpu.h" namespace panic { @@ -47,8 +49,8 @@ inline void panic( /// Install a panic handler. /// \arg entrypoint Virtual address of the panic handler's entrypoint -/// \arg symbol_data Pointer to the symbol table data -void install(uintptr_t entrypoint, const void *symbol_data); +/// \arg symbol_data Symbol table data +void install(uintptr_t entrypoint, util::const_buffer symbol_data); } // namespace panic diff --git a/src/kernel/kernel_main.cpp b/src/kernel/kernel_main.cpp index 4822c27..767f9ef 100644 --- a/src/kernel/kernel_main.cpp +++ b/src/kernel/kernel_main.cpp @@ -32,9 +32,8 @@ void load_init_server(bootproto::program &program, uintptr_t modules_address); void kernel_main(bootproto::args *args) { - if (args->panic) { - const void *syms = util::offset_pointer(args->symbol_table, mem::linear_offset); - panic::install(args->panic->entrypoint, syms); + if (args->panic_handler) { + panic::install(args->panic_handler, args->symbol_table); } logger_init(); @@ -75,7 +74,7 @@ kernel_main(bootproto::args *args) smp::ready(); // Load the init server - load_init_server(*args->init, args->modules); + load_init_server(args->init, args->init_modules); sched->start(); } diff --git a/src/kernel/panic.serial/panic.serial.ld b/src/kernel/panic.serial/panic.serial.ld index 0da39aa..d33c2f6 100644 --- a/src/kernel/panic.serial/panic.serial.ld +++ b/src/kernel/panic.serial/panic.serial.ld @@ -1,20 +1,36 @@ +PHDRS +{ + rodata PT_LOAD PHDRS FILEHDR FLAGS (4) /* read-only */; + text PT_LOAD ; + rwdata PT_LOAD ; + bss PT_LOAD ; +} + +MEMORY +{ + panic (rwxa) : ORIGIN = 0xFFFF800080000000, LENGTH = 256M +} + ENTRY(_panic_entry) + SECTIONS { - . = 0xFFFF800080000000; - - .text ALIGN(4096) : { - *(.text*) - } - - .data ALIGN(4096) : { - *(.data*) + .rodata ORIGIN(panic) + SIZEOF_HEADERS : { *(.rodata*) - } + } :rodata - .bss ALIGN(4096) : { + .text ALIGN(4K) : { + *(.text*) + } :text + + .data ALIGN(4K) : { + *(.data*) + *(.init_array*) + } :rwdata + + .bss ALIGN(4K) : { __bss_start = .; *(.bss*) __bss_end = .; - } + } :bss } diff --git a/src/libraries/bootproto/bootproto/kernel.h b/src/libraries/bootproto/bootproto/kernel.h index f00fa42..d834a96 100644 --- a/src/libraries/bootproto/bootproto/kernel.h +++ b/src/libraries/bootproto/bootproto/kernel.h @@ -129,13 +129,13 @@ struct args util::counted page_tables; util::counted mem_map; util::counted frame_blocks; - - program *kernel; - program *init; - program *panic; allocation_register *allocations; - uintptr_t modules; - void const *symbol_table; + + uintptr_t panic_handler; + util::buffer symbol_table; + + program init; + uintptr_t init_modules; void *runtime_services; void *acpi_table; diff --git a/src/user/srv.init/init.ld b/src/user/srv.init/init.ld new file mode 100644 index 0000000..5d49ed5 --- /dev/null +++ b/src/user/srv.init/init.ld @@ -0,0 +1,43 @@ +PHDRS +{ + rodata PT_LOAD PHDRS FILEHDR FLAGS (4) /* read-only */; + text PT_LOAD ; + rwdata PT_LOAD ; + bss PT_LOAD ; +} + +SECTIONS +{ + . = 0x20000000 + SIZEOF_HEADERS ; + + .rodata : { + *(.rodata*) + } :rodata + + .text ALIGN(4K) : { + *(.text*) + } :text + + .data ALIGN(4K) : { + *(.data*) + } :rwdata + + .init_array : { + *(.init_array*) + } :rwdata + + .got : { + *(.got*) + } :rwdata + + .bss ALIGN(4K) : { + __bss_start = .; + *(.bss*) + __bss_end = .; + } :bss + + /DISCARD/ : { + *(.gcc_except_table*) + *(.eh_frame*) + } +} diff --git a/src/user/srv.init/init.module b/src/user/srv.init/init.module index fcf98de..75f148e 100644 --- a/src/user/srv.init/init.module +++ b/src/user/srv.init/init.module @@ -4,6 +4,7 @@ init = module("srv.init", targets = [ "user" ], deps = [ "libc", "elf", "bootproto", "zstd" ], description = "Init server", + ld_script = "init.ld", sources = [ "j6romfs.cpp", "loader.cpp",