#include #include #include #include #include #include using namespace mercury::kernel; LIMINE_BASE_REVISION(1) static volatile limine_memmap_request memmap_request { .id = LIMINE_MEMMAP_REQUEST, .revision = 0, .response = 0 }; static volatile limine_kernel_address_request kernel_address_request { .id = LIMINE_KERNEL_ADDRESS_REQUEST, .revision = 0, .response = 0 }; static volatile limine_framebuffer_request framebuffer_request { .id = LIMINE_FRAMEBUFFER_REQUEST, .revision = 0, .response = 0 }; static volatile limine_hhdm_request hhdm_request { .id = LIMINE_HHDM_REQUEST, .revision = 0, .response = 0 }; static limine_internal_module initfs_module = { .path = "initfs.tgz", .cmdline = "initfs", .flags = LIMINE_INTERNAL_MODULE_REQUIRED | LIMINE_INTERNAL_MODULE_COMPRESSED }; static limine_internal_module termfont_module = { .path = "termfont.psf", .cmdline = "termfont", .flags = LIMINE_INTERNAL_MODULE_REQUIRED }; static limine_internal_module *internal_modules[] = { &initfs_module, &termfont_module }; static volatile limine_module_request module_request = { .id = LIMINE_MODULE_REQUEST, .revision = 2, .response = 0, .internal_module_count = 2, .internal_modules = internal_modules }; bool try_map_module_by_cmdline( const char *cmdline, void *&vaddr_out, uint64_t &len_out ) { auto response = module_request.response; for (uint64_t i = 0; i < response->module_count; ++i) { limine_file *file = response->modules[i]; for (uint64_t j = 0; cmdline[j] == file->cmdline[j]; ++j) if (!cmdline[j]) { //module start is guaranteed to be page-aligned, end is not. uint64_t start_paddr = (uint64_t)file->address - hhdm_request.response->offset; uint64_t end_paddr = ((start_paddr + file->size - 1) / 4096 + 1) * 4096; uint64_t start_vaddr = paging::find_unmapped_vram_region((end_paddr - start_paddr) / 4096); for (uint64_t i = 0; i < end_paddr - start_paddr; i += 4096) paging::map_kernel_page( start_paddr + i, start_vaddr + i, true, false); vaddr_out = (void *)start_vaddr; len_out = file->size; return true; } } return false; } //defined in linker script, page-aligned: extern uint8_t __kernel_start; extern uint8_t __kernel_end; extern uint8_t __kernel_rx_start; extern uint8_t __kernel_rx_end; extern uint8_t __kernel_ro_start; extern uint8_t __kernel_ro_end; extern uint8_t __kernel_rw_start; extern uint8_t __kernel_rw_end; uint8_t *initfs; uint64_t initfs_len; [[noreturn]] static void with_kernel_p4(); extern "C" [[noreturn]] void entry() { //TODO?: maybe we should check if the limine requests were // fulfilled and display some error message if not //set up the physical memory usage bitmap: paging::mark_all_pram_used(); auto memmap = memmap_request.response; for (uint64_t i = 0; i < memmap->entry_count; ++i) { //we don't start allocating physical pages until after we are done using //limine structures (specifically at the call to paging::map_kernel_stack), //so we consider bootloader reclaimable to be free. usable and bootloader //reclaimable are guaranteed by limine spec to be page-aligned. auto entry = memmap->entries[i]; if (entry->type == LIMINE_MEMMAP_USABLE || entry->type == LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE) paging::mark_pram_region_free(entry->base, entry->base + entry->length); } //set up page mappings: auto kernel_address = kernel_address_request.response; uint64_t kernel_offset = kernel_address->virtual_base - kernel_address->physical_base; uint64_t hhdm = hhdm_request.response->offset; //framebuffer might not be page-aligned auto framebuffer = framebuffer_request.response->framebuffers[0]; uint64_t fb_start = ((uint64_t)framebuffer->address / 4096) * 4096 - hhdm; uint64_t fb_end = (uint64_t)framebuffer->address + framebuffer->pitch * framebuffer->height - hhdm; fb_end = ((fb_end - 1) / 4096 + 1) * 4096; paging::init_kernel_page_tables(kernel_offset); //kernel image rx for (uint64_t vaddr = (uint64_t)&__kernel_rx_start; vaddr < (uint64_t)&__kernel_rx_end; vaddr += 4096) paging::map_kernel_page(vaddr - kernel_offset, vaddr, false, true); //kernel image ro for (uint64_t vaddr = (uint64_t)&__kernel_ro_start; vaddr < (uint64_t)&__kernel_ro_end; vaddr += 4096) paging::map_kernel_page(vaddr - kernel_offset, vaddr, false, false); //kernel image rw for (uint64_t vaddr = (uint64_t)&__kernel_rw_start; vaddr < (uint64_t)&__kernel_rw_end; vaddr += 4096) paging::map_kernel_page(vaddr - kernel_offset, vaddr, true, false); //framebuffer uint64_t fb_vaddr = paging::find_unmapped_vram_region((fb_end - fb_start) / 4096); for (uint64_t i = 0; i < fb_end - fb_start; i += 4096) paging::map_kernel_page(fb_start + i, fb_vaddr + i, true, false); //initfs and termfont - these are required modules //so there is no worry about them not being present. try_map_module_by_cmdline("initfs", (void *&)initfs, initfs_len); try_map_module_by_cmdline( "termfont", (void *&)terminal::termfont, terminal::termfont_len); //set up framebuffer and terminal: //TODO: assumes framebuffer is 32-bpp rgb framebuffer::init_framebuffer( fb_vaddr, framebuffer->width, framebuffer->height, framebuffer->pitch); //switch to kernel p4 paging::map_kernel_stack(); switch_to_kernel_p4(&with_kernel_p4); } [[noreturn]] static void print_and_halt(const char *msg) { terminal::put_string_sz(msg); while (1) ; } static void dir_tree( storage::block_device *bd, storage::node_id_t node, unsigned indent ) { storage::node_id_t child_node; storage::directory_iter_t dir_iter; storage::io_result result = bd->mounted_as->get_first_child(node, child_node, dir_iter); if (result == storage::io_result::not_found) { for (unsigned i = 0; i < indent; ++i) terminal::put_char(' '); terminal::put_string_sz("[empty]\n"); return; } else if (result) print_and_halt("error getting first child."); while (true) { unsigned name_len; if (bd->mounted_as->get_name_length(child_node, name_len)) print_and_halt("failed to get name length."); char *name_buf = new char[name_len]; if (bd->mounted_as->get_name(child_node, name_buf, name_len)) print_and_halt("failed to get name."); for (unsigned i = 0; i < indent; ++i) terminal::put_char(' '); terminal::put_string(name_buf, name_len); terminal::put_string(":\n", 2); delete[] name_buf; storage::file_type type; if (bd->mounted_as->get_file_type(child_node, type)) print_and_halt("failed to get type."); if (type == storage::file_type::directory) dir_tree(bd, child_node, indent + 2); else { uint64_t len; if (bd->mounted_as->get_file_length(child_node, len)) print_and_halt("failed to get length."); char *contents = new char[len]; if (bd->mounted_as->read_bytes_from_file(child_node, 0, len, contents)) print_and_halt("failed to read file."); if (contents[len - 1] == '\n') len--; for (unsigned i = 0; i < indent + 2; ++i) terminal::put_char(' '); if (len == 0) terminal::put_string_sz("[empty]\n"); else { terminal::put_string(contents, len); terminal::put_char('\n'); } delete[] contents; } storage::io_result result = bd->mounted_as->get_next_child(node, child_node, dir_iter); if (result == storage::io_result::not_found) return; else if (result) print_and_halt("error getting next child."); } } [[noreturn]] static void with_kernel_p4() { terminal::init_terminal(); storage::init_storage(); storage::block_device *initfs_bd = new bd::memory(initfs, initfs_len); storage::block_devices->insert_end(initfs_bd); storage::canon_path root; storage::canonize_path("/", 1, root); if (storage::mount_device(initfs_bd, root, &fs::tarfs_mounter)) print_and_halt("failed to mount initfs."); terminal::put_string_sz("kernel initialization complete.\n"); int used_vram_kib = paging::get_used_vram_page_count() * 4; int free_pram_kib = paging::get_free_pram_page_count() * 4; terminal::put_int_decimal(used_vram_kib); terminal::put_string_sz(" kiB kernel memory mapped.\n"); terminal::put_int_decimal(free_pram_kib); terminal::put_string_sz(" kiB physical memory free.\n"); storage::node_id_t root_node; if (initfs_bd->mounted_as->get_root_node(root_node)) print_and_halt("failed to get root initfs node."); terminal::put_string_sz("initfs:\n"); dir_tree(initfs_bd, root_node, 2); print_and_halt("halting."); }