summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/entry.cpp212
-rw-r--r--kernel/framebuffer.cpp59
-rw-r--r--kernel/link.ld47
-rw-r--r--kernel/paging.asm16
-rw-r--r--kernel/paging.cpp115
-rw-r--r--kernel/terminal.cpp106
-rw-r--r--kernel/utility.cpp45
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;
+
+ }
+
+}