diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/entry.cpp | 212 | ||||
-rw-r--r-- | kernel/framebuffer.cpp | 59 | ||||
-rw-r--r-- | kernel/link.ld | 47 | ||||
-rw-r--r-- | kernel/paging.asm | 16 | ||||
-rw-r--r-- | kernel/paging.cpp | 115 | ||||
-rw-r--r-- | kernel/terminal.cpp | 106 | ||||
-rw-r--r-- | kernel/utility.cpp | 45 |
7 files changed, 600 insertions, 0 deletions
diff --git a/kernel/entry.cpp b/kernel/entry.cpp new file mode 100644 index 0000000..c8f74c8 --- /dev/null +++ b/kernel/entry.cpp @@ -0,0 +1,212 @@ +#include <mercury/kernel/framebuffer.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 with_kernel_p4() { + + terminal::init_terminal(); + 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"); + + terminal::put_string_sz("initfs first sector:"); + for (int y = 0; y < 8; ++y) { + terminal::put_string_sz("\n "); + for (int x = 0; x < 64; ++x) + terminal::put_char((char)initfs[y * 64 + x]); + } + + terminal::put_string_sz("\ninitfs second sector:"); + for (int y = 0; y < 8; ++y) { + terminal::put_string_sz("\n "); + for (int x = 0; x < 64; ++x) + terminal::put_char((char)initfs[512 + y * 64 + x]); + } + + while (1) + ; + + //TODO + +} diff --git a/kernel/framebuffer.cpp b/kernel/framebuffer.cpp new file mode 100644 index 0000000..a115e0b --- /dev/null +++ b/kernel/framebuffer.cpp @@ -0,0 +1,59 @@ +#include <mercury/kernel/framebuffer.hpp> + +namespace mercury::kernel::framebuffer { + + uint32_t *vaddr; + int width; + int height; + int dword_pitch; + + void init_framebuffer( + uint64_t vaddr, uint64_t width, uint64_t height, uint64_t pitch + ) { + //TODO: assumes 32-bpp rgb + framebuffer::vaddr = (uint32_t *)vaddr; + framebuffer::width = width; + framebuffer::height = height; + dword_pitch = pitch / 4; + } + + color encode_color(uint8_t r, uint8_t g, uint8_t b) { + return ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b; + } + + void set_pixel(int x, int y, color c) { + vaddr[y * dword_pitch + x] = c; + } + + void move_region( + int from_start_x, int from_start_y, int from_end_x, + int from_end_y, int to_start_x, int to_start_y + ) { + + int region_width = from_end_x - from_start_x; + int region_height = from_end_y - from_start_y; + + int from_start_offset = from_start_y * dword_pitch + from_start_x; + int to_start_offset = to_start_y * dword_pitch + to_start_x; + + if (from_start_offset > to_start_offset) + for (int y = 0; y < region_height; ++y) + for (int x = 0; x < region_width; ++x) + vaddr[to_start_offset + y * dword_pitch + x] = + vaddr[from_start_offset + y * dword_pitch + x]; + + else if (from_start_offset < to_start_offset) + for (int y = region_height - 1; y >= 0; --y) + for (int x = region_width - 1; x >= 0; --x) + vaddr[to_start_offset + y * dword_pitch + x] = + vaddr[from_start_offset + y * dword_pitch + x]; + + } + + void fill_region(int start_x, int start_y, int end_x, int end_y, color c) { + for (int y = start_y; y < end_y; ++y) + for (int x = start_x; x < end_x; ++x) + vaddr[y * dword_pitch + x] = c; + } + +} diff --git a/kernel/link.ld b/kernel/link.ld new file mode 100644 index 0000000..884e633 --- /dev/null +++ b/kernel/link.ld @@ -0,0 +1,47 @@ +OUTPUT_FORMAT(elf64-x86-64) +OUTPUT_ARCH(i386:x86-64) + +ENTRY(entry) + +PHDRS { + rx PT_LOAD FLAGS(5); + ro PT_LOAD FLAGS(4); + rw PT_LOAD FLAGS(6); +} + +SECTIONS { + + /* see also ../documentation/memory.txt */ + . = 0xffffffffc0000000; + __kernel_rx_start = .; + + .text : { + *(.text .text.*) + } : rx + + . = ALIGN(4096); + __kernel_rx_end = .; + __kernel_ro_start = .; + + .rodata : { + *(.rodata .rodata.*) + } : ro + + . = ALIGN(4096); + __kernel_ro_end = .; + __kernel_rw_start = .; + + .data : { + *(.data .data.*) + } : rw + + . = ALIGN(4096); + .bss : { + *(.bss .bss.*) + *(COMMON) + } : rw + + . = ALIGN(4096); + __kernel_rw_end = .; + +} diff --git a/kernel/paging.asm b/kernel/paging.asm new file mode 100644 index 0000000..f1047a9 --- /dev/null +++ b/kernel/paging.asm @@ -0,0 +1,16 @@ +bits 64 + +;see also ../documentation/memory.txt + +global switch_to_kernel_p4 + +;from paging.cpp: +extern __kernel_p4_paddr + +section .text + +switch_to_kernel_p4: + mov rax, qword [__kernel_p4_paddr] + mov cr3, rax + mov rsp, 0xfffffffffffff000 + jmp rdi diff --git a/kernel/paging.cpp b/kernel/paging.cpp new file mode 100644 index 0000000..3bd27d0 --- /dev/null +++ b/kernel/paging.cpp @@ -0,0 +1,115 @@ +#include <mercury/kernel/utility.hpp> +#include <mercury/kernel/paging.hpp> + +//see also ../documentation/memory.txt + +extern "C" { + uint64_t __kernel_p4_paddr; +} + +namespace mercury::kernel::paging { + + static constexpr uint64_t kernel_vram_start = 0xffffffffc0000000; + static constexpr uint64_t kernel_vram_pages = 261888; + static constexpr uint64_t kernel_stack_bottom = 0xfffffffffff01000; + static constexpr uint64_t kernel_stack_top = 0xfffffffffffff000; + + static constexpr uint64_t pram_pages = 1 << 23; + static uint64_t pram_usage_bitmap[pram_pages / 64]; + + void mark_all_pram_used() { + utility::mark_bitmap_region_one(pram_usage_bitmap, 0, pram_pages); + } + + void mark_pram_region_free(uint64_t start_addr, uint64_t end_addr) { + utility::mark_bitmap_region_zero( + pram_usage_bitmap, start_addr / 4096, + utility::min(end_addr / 4096, pram_pages)); + } + + [[gnu::aligned(4096)]] uint64_t kernel_p4[512]; + [[gnu::aligned(4096)]] uint64_t kernel_p3[512]; + [[gnu::aligned(4096)]] uint64_t kernel_p2[512]; + [[gnu::aligned(4096)]] uint64_t kernel_p1s[512 * 512]; + + static uint64_t encode_pte( + uint64_t addr, bool user, bool write, bool execute) { + return (addr & 0x0000ffffffffffff) | (execute ? 0 : (1ULL << 63)) + | (user << 2) | (write << 1) | 1; + } + + void init_kernel_page_tables(uint64_t kernel_offset) { + __kernel_p4_paddr = (uint64_t)kernel_p4 - kernel_offset; + for (int i = 0; i < 511; ++i) + kernel_p4[i] = 0; + kernel_p4[511] = encode_pte( + (uint64_t)kernel_p3 - kernel_offset, false, true, true); + for (int i = 0; i < 511; ++i) + kernel_p3[i] = 0; + kernel_p3[511] = encode_pte( + (uint64_t)kernel_p2 - kernel_offset, false, true, true); + for (int i = 0; i < 512; ++i) + kernel_p2[i] = encode_pte( + (uint64_t)kernel_p1s + 4096 * i - kernel_offset, false, true, true); + for (int i = 0; i < 512 * 512; ++i) + kernel_p1s[i] = 0; + } + + void map_kernel_page( + uint64_t paddr, uint64_t vaddr, bool write, bool execute) { + uint64_t i = (vaddr - kernel_vram_start) / 4096; + kernel_p1s[i] = encode_pte(paddr, false, write, execute); + } + + static uint64_t take_pram_page() { + for (uint64_t i = 0; i < pram_pages / 64; ++i) + if (pram_usage_bitmap[i] != 0xffffffffffffffff) + for (int j = 0; j < 64; ++j) + if (!(pram_usage_bitmap[i] & (1ULL << j))) { + pram_usage_bitmap[i] |= (1ULL << j); + return 4096 * (i * 64 + j); + } + //TODO: handle error + return 0; + } + + void map_kernel_stack() { + for (uint64_t vaddr = kernel_stack_bottom; + vaddr < kernel_stack_top; vaddr += 4096) + map_kernel_page(take_pram_page(), vaddr, true, false); + } + + uint64_t find_unmapped_vram_region(uint64_t page_count) { + uint64_t start = 0; + uint64_t len = 0; + for (uint64_t i = 0; i < kernel_vram_pages; ++i) + if (kernel_p1s[i] == 0) { + ++len; + if (len == page_count) + return start * 4096 + kernel_vram_start; + } + else { + start = i + 1; + len = 0; + } + //TODO: handle error + return 0; + } + + uint64_t get_used_vram_page_count() { + uint64_t count = 0; + for (uint64_t i = 0; i < kernel_vram_pages; ++i) + if (kernel_p1s[i] != 0) + ++count; + return count; + } + + uint64_t get_free_pram_page_count() { + uint64_t used_count = 0; + for (uint64_t i = 0; i < pram_pages / 64; ++i) + for (uint64_t j = 0; j < 64; ++j) + used_count += (pram_usage_bitmap[i] >> j) & 1; + return pram_pages - used_count; + } + +} diff --git a/kernel/terminal.cpp b/kernel/terminal.cpp new file mode 100644 index 0000000..f017cad --- /dev/null +++ b/kernel/terminal.cpp @@ -0,0 +1,106 @@ +#include <mercury/kernel/framebuffer.hpp> +#include <mercury/kernel/terminal.hpp> + +namespace mercury::kernel::terminal { + + uint8_t *termfont; + uint64_t termfont_len; + + int width; + int height; + + int cursor_x; + int cursor_y; + + framebuffer::color bg_color; + framebuffer::color fg_color; + + static uint8_t glyph_height; + + void init_terminal() { + //TODO - verify that termfont fits inside termfont_len (i.e. that no other + // functions in this file will try to access memory outside termfont) + //TODO - check magic header to verify that this is actually a font and to + // see whether this is a psf1 font or a psf2 font. + //TODO - support psf2 fonts. currently psf1 is assumed. + //TODO - read unicode table if there is one. currently it is assumed that + // all 256 codepoints have glyphs, and that they appear in order. + + glyph_height = termfont[3]; + width = framebuffer::width / 8; + height = framebuffer::height / glyph_height; + cursor_x = 0; + cursor_y = 0; + bg_color = framebuffer::encode_color(0, 0, 0); + fg_color = framebuffer::encode_color(255, 255, 255); + + } + + static void cursor_down() { + if (++cursor_y == height) { + --cursor_y; + framebuffer::move_region( + 0, glyph_height, width * 8, height * glyph_height, 0, 0); + framebuffer::fill_region(0, (height - 1) * glyph_height, + width * 8, height * glyph_height, bg_color); + } + } + + static void cursor_right() { + if (++cursor_x == width) { + cursor_x = 0; + cursor_down(); + } + } + + void draw_char(char ch, int x, int y) { + const uint8_t *glyph = termfont + 4 + glyph_height * (unsigned)ch; + for (int i = 0; i < glyph_height; ++i) + for (int j = 0; j < 8; ++j) + framebuffer::set_pixel(x * 8 + j, y * glyph_height + i, + ((glyph[i] << j) & 0x80) ? fg_color : bg_color); + } + + void put_char(char ch) { + switch (ch) { + case '\n': + cursor_x = 0; + cursor_down(); + break; + default: + draw_char(ch, cursor_x, cursor_y); + cursor_right(); + break; + } + } + + void put_string_sz(const char *str) { + for (int i = 0; str[i]; ++i) + put_char(str[i]); + } + + void put_int_decimal(uint64_t n, bool with_commas) { + + if (n == 0) { + put_char('0'); + return; + } + + uint64_t d = 1; + int i = 0; + while (d <= n / 10) { + d *= 10; + ++i; + } + + while (d) { + put_char('0' + ((n / d) % 10)); + d /= 10; + if (with_commas && (i % 3 == 0) && (i != 0)) + put_char(','); + --i; + } + + } + +} diff --git a/kernel/utility.cpp b/kernel/utility.cpp new file mode 100644 index 0000000..865b817 --- /dev/null +++ b/kernel/utility.cpp @@ -0,0 +1,45 @@ +#include <mercury/kernel/utility.hpp> + +namespace mercury::kernel::utility { + + void mark_bitmap_region_zero( + uint64_t *bitmap, uint64_t start_i, uint64_t end_i) { + + if (start_i % 64 != 0) { + uint64_t keep = (1 << (start_i % 64)) - 1; + bitmap[start_i / 64] &= keep; + start_i = (start_i / 64 + 1) * 64; + } + + if (end_i % 64 != 0) { + uint64_t replace = (1 << (end_i % 64)) - 1; + bitmap[end_i / 64] &= ~replace; + end_i = (end_i / 64) * 64; + } + + for (uint64_t i = start_i / 64; i < end_i / 64; ++i) + bitmap[i] = 0; + + } + + void mark_bitmap_region_one( + uint64_t *bitmap, uint64_t start_i, uint64_t end_i) { + + if (start_i % 64 != 0) { + uint64_t keep = (1 << (start_i % 64)) - 1; + bitmap[start_i / 64] |= ~keep; + start_i = (start_i / 64 + 1) * 64; + } + + if (end_i % 64 != 0) { + uint64_t replace = (1 << (end_i % 64)) - 1; + bitmap[end_i / 64] |= replace; + end_i = (end_i / 64) * 64; + } + + for (uint64_t i = start_i / 64; i < end_i / 64; ++i) + bitmap[i] = 0xffffffffffffffff; + + } + +} |