This repository has been archived on 2025-02-26. You can view files and clone it, but cannot push or open issues or pull requests.
hilbert-os/kernel/entry.cpp

294 lines
8.8 KiB
C++

#include <mercury/kernel/framebuffer.hpp>
#include <mercury/kernel/bd/memory.hpp>
#include <mercury/kernel/fs/tarfs.hpp>
#include <mercury/kernel/terminal.hpp>
#include <mercury/kernel/limine.hpp>
#include <mercury/kernel/paging.hpp>
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.");
}