application loading

This commit is contained in:
Benji Dial 2024-01-13 16:43:49 -05:00
parent 882e74b219
commit 4130562b15
21 changed files with 918 additions and 124 deletions

View file

@ -0,0 +1,24 @@
#include <mercury/syscall.hpp>
using mercury::syscall::encoded_color;
using mercury::syscall::color;
encoded_color *fb;
uint32_t fb_width;
uint32_t fb_height;
uint32_t fb_pitch;
int main(int, char **) {
mercury::syscall::get_framebuffer(fb, fb_width, fb_height, fb_pitch);
for (uint32_t y = 0; y < fb_height; ++y)
for (uint32_t x = 0; x < fb_width; ++x) {
color c = {
.r = 0,
.g = (uint8_t)(y * 255 / fb_height),
.b = (uint8_t)(x * 255 / fb_width)
};
fb[y * fb_pitch + x] = mercury::syscall::encode_color(c);
}
mercury::syscall::draw_framebuffer();
return 0;
}

38
applications/link.ld Normal file
View file

@ -0,0 +1,38 @@
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 */
. = 0x200000;
.text : {
*(.text .text.*)
} : rx
. = ALIGN(0x200000);
.rodata : {
*(.rodata .rodata.*)
} : ro
. = ALIGN(0x200000);
.data : {
*(.data .data.*)
} : rw
.bss : {
*(.bss .bss.*)
*(COMMON)
} : rw
}

View file

@ -1,7 +1,14 @@
only the first 32GiB of physical memory are considered. this can be changed
with pram_pages in paging.cpp. as for virtual memory, the top 1GiB is reserved
for the kernel. the top 1MiB of that is reserved for the kernel stack, with an
unmapped guard page at the top and bottom of the kernel stack.
with pram_pages in paging.cpp. vram layout is as follows:
if any of these facts change, the linker script and paging code should be
checked closely.
0x0000.0000.0000 - 0x0000.001f.ffff: always unmapped
0x0000.0020.0000 - 0x0000.3fbf.ffff: available to user process
0x0000.3fc0.0000 - 0x0000.3fdf.ffff: always unmapped
0x0000.3fe0.0000 - 0x0000.3fff.ffff: user stack
0x0000.4000.0000 - 0xffff.bfff.ffff: always unmapped
0xffff.c000.0000 - 0xffff.ffdf.ffff: available to kernel
0xffff.ffe0.0000 - 0xffff.ffe0.0fff: always unmapped
0xffff.ffe0.1000 - 0xffff.ffef.efff: interrupt stack
0xffff.ffef.f000 - 0xffff.fff0.0fff: always unmapped
0xffff.fff0.1000 - 0xffff.ffff.efff: syscall stack / kernel init stack
0xffff.ffff.f000 - 0xffff.ffff.ffff: always unmapped

View file

@ -0,0 +1,27 @@
on application entry:
there is a 1MiB - 8KiB area mapped writable and not
executable with guard pages on either side, and rsp is
set to the top of that. all other registers are set to 0.
for all system calls:
rax, rdi, rsi, rdx are in/out paramters.
rbx, rbp, rsp, r12-r15 are preserved.
rcx, rflags, r8-r11 are clobbered.
encode color:
rax in: 0
edi in: r + g * 256 + b * 65536
eax out: encoded color
get framebuffer:
rax in: 1
rax out: pointer to framebuffer
rdi out: width + height * 2 ** 32
esi out: pitch
framebuffer is always 32 bpp. use the encode color syscall
to encode colors. pitch is in dwords, not in bytes.
draw framebuffer:
rax in: 2
this draws changes to the framebuffer gotten by the get framebuffer
system call. (currently, the entire thing is copied, not just changes.)

View file

@ -0,0 +1,72 @@
#ifndef MERCURY_KERNEL_APPLICATION_HPP
#define MERCURY_KERNEL_APPLICATION_HPP
#include <mercury/kernel/vfile.hpp>
#include <cstdint>
namespace mercury::kernel::application {
enum class app_state {
running,
paused,
zombie
};
struct app_instance {
app_state state;
uint64_t *p4;
uint64_t *p3;
uint64_t *p2;
bool *p2es_to_free_on_exit;
uint64_t p4_paddr;
//set to 0 if none
uint64_t framebuffer_vaddr;
//only valid if state is zombie
int exit_code;
//only valid if state is paused
struct {
uint64_t rip;
uint64_t rsp;
//TODO: etc.
} saved_regs;
app_instance();
~app_instance();
//2MiB page. vaddr and paddr must be aligned, and vaddr in valid range.
void map_page(uint64_t vaddr, uint64_t paddr,
bool write, bool execute, bool free_pram_on_exit);
//2MiB pages. returns start of first page.
uint64_t get_free_vaddr_pages(uint64_t count);
void create_stack();
void set_instruction_pointer(uint64_t vaddr);
//2MiB pages; only lower half.
uint64_t count_mapped_vram_pages();
};
extern app_instance *running_app;
enum class create_app_result {
success,
device_error,
app_corrupt,
fs_corrupt
};
create_app_result create_app(const vfile::vfile &file, app_instance *&out);
}
#endif

View file

@ -5,8 +5,10 @@
namespace mercury::kernel::framebuffer {
extern uint32_t *vaddr;
extern int width;
extern int height;
extern int dword_pitch;
void init_framebuffer(uint64_t vaddr, uint64_t width, uint64_t height, uint64_t pitch);

View file

@ -11,27 +11,36 @@ namespace mercury::kernel::paging {
void mark_all_pram_used();
void mark_all_vram_free();
//assumes page-alignment
void mark_pram_region_free(uint64_t start_addr, uint64_t end_addr);
//assumes page-alignment
void mark_vram_region_used(uint64_t start_addr, uint64_t end_addr);
uint64_t find_unmapped_vram_region(uint64_t page_count);
uint64_t encode_pte(
uint64_t addr, bool user, bool write, bool execute, bool ps);
void init_kernel_page_tables(uint64_t kernel_offset);
void map_kernel_stack();
void map_kernel_stacks();
//assumes page-alignment
void map_kernel_page(
uint64_t paddr, uint64_t vaddr, bool write, bool execute);
void unmap_kernel_page(uint64_t vaddr);
//maps writable and not executable
void *map_new_kernel_pages(uint64_t count);
//maps writable and not executable
void map_new_kernel_page(uint64_t &vaddr_out, uint64_t &paddr_out);
uint64_t get_used_vram_page_count();
uint64_t get_free_pram_page_count();
extern uint64_t kernel_p4e;
uint64_t take_2mib_pram_page();
}
#endif

View file

@ -28,6 +28,8 @@ namespace mercury::kernel::terminal {
void put_int_decimal(uint64_t n, bool with_commas = true);
void put_int_hex(uint64_t n, int digits, bool with_dots = true);
}
#endif

View file

@ -48,6 +48,11 @@ namespace mercury::kernel::vfile {
//dir_entry.type is assumed to be directory. out must be empty on entry.
storage::fs_result get_children(utility::vector<vfile> &out) const;
//assumes file is a regular file and [start, start + length)
//is in bounds of file. start and length are in bytes.
storage::fs_result read_file(
uint64_t start, uint64_t length, void *into) const;
};
//path must be absolute. follows symlinks on all but the last node.

View file

@ -0,0 +1,26 @@
#ifndef MERCURY_SYSCALL_HPP
#define MERCURY_SYSCALL_HPP
#include <cstdint>
namespace mercury::syscall {
typedef uint32_t encoded_color;
struct [[gnu::packed]] color {
uint8_t r;
uint8_t g;
uint8_t b;
};
extern "C" encoded_color encode_color(color c);
extern "C" void get_framebuffer(encoded_color *&framebuffer_out,
uint32_t &width_out, uint32_t &height_out, uint32_t &pitch_out
);
extern "C" void draw_framebuffer();
}
#endif

266
kernel/application.cpp Normal file
View file

@ -0,0 +1,266 @@
#include <mercury/kernel/application.hpp>
#include <mercury/kernel/paging.hpp>
//TODO - scheduling.
namespace mercury::kernel::application {
app_instance::app_instance() : state(app_state::paused) {
framebuffer_vaddr = 0;
uint64_t p3_paddr;
uint64_t p2_paddr;
uint64_t p4_vaddr;
uint64_t p3_vaddr;
uint64_t p2_vaddr;
paging::map_new_kernel_page(p4_vaddr, p4_paddr);
paging::map_new_kernel_page(p3_vaddr, p3_paddr);
paging::map_new_kernel_page(p2_vaddr, p2_paddr);
p4 = (uint64_t *)p4_vaddr;
p3 = (uint64_t *)p3_vaddr;
p2 = (uint64_t *)p2_vaddr;
for (int i = 1; i < 511; ++i)
p4[i] = 0;
p4[511] = paging::kernel_p4e;
p4[0] = paging::encode_pte(p3_paddr, true, true, true, false);
for (int i = 1; i < 512; ++i)
p3[i] = 0;
p3[0] = paging::encode_pte(p2_paddr, true, true, true, false);
for (int i = 0; i < 512; ++i)
p2[i] = 0;
p2es_to_free_on_exit = new bool[512];
}
app_instance::~app_instance() {
for (int i = 1; i < 512; ++i)
if (p2[i] != 0 && p2es_to_free_on_exit[i]) {
uint64_t paddr = p2[i] & ~0x1fffffULL;
paging::mark_pram_region_free(paddr, paddr + 0x200000);
}
delete[] p2es_to_free_on_exit;
uint64_t p2_paddr = p3[0] & ~0x1fffffULL;
paging::unmap_kernel_page((uint64_t)p2);
paging::mark_pram_region_free(p2_paddr, p2_paddr + 4096);
uint64_t p3_paddr = p4[0] & ~0x1fffffULL;
paging::unmap_kernel_page((uint64_t)p3);
paging::mark_pram_region_free(p3_paddr, p3_paddr + 4096);
paging::unmap_kernel_page((uint64_t)p4);
paging::mark_pram_region_free(p4_paddr, p4_paddr + 4096);
}
void app_instance::map_page(uint64_t vaddr, uint64_t paddr,
bool write, bool execute, bool free_pram_on_exit
) {
uint64_t i = vaddr / 0x200000;
p2[i] = paging::encode_pte(paddr, true, write, execute, true);
p2es_to_free_on_exit[i] = free_pram_on_exit;
}
uint64_t app_instance::get_free_vaddr_pages(uint64_t count) {
uint64_t start = 1;
uint64_t length = 0;
while (start + length < 510) {
if (length == count)
return start * 0x200000;
if (p2[start + length] == 0)
++length;
else {
start += length + 1;
length = 0;
}
}
//TODO: handle out of memory
return 0;
}
void app_instance::create_stack() {
uint64_t stack_paddr = paging::take_2mib_pram_page();
map_page(0x3fe00000, stack_paddr, true, false, true);
for (int i = 0; i < 512; ++i) {
uint64_t vaddr = paging::find_unmapped_vram_region(1);
paging::map_kernel_page(stack_paddr + 512 * i, vaddr, true, false);
for (int j = 0; j < 4096 / 8; ++j)
*(uint64_t *)(vaddr + j * 8) = 0;
paging::unmap_kernel_page(vaddr);
}
saved_regs.rsp = 0x40000000;
}
void app_instance::set_instruction_pointer(uint64_t vaddr) {
saved_regs.rip = vaddr;
}
uint64_t app_instance::count_mapped_vram_pages() {
uint64_t count = 0;
for (int i = 1; i < 512; ++i)
if (p2[i] != 0)
++count;
return count;
}
app_instance *running_app;
static uint8_t correct_magic[16] = {
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00
};
#define READ(a, b, c) \
{ \
storage::fs_result _result = file.read_file(a, b, c); \
if (_result == storage::fs_result::device_error) \
return create_app_result::device_error; \
if (_result == storage::fs_result::fs_corrupt) \
return create_app_result::fs_corrupt; \
}
struct load_info {
uint64_t foffset;
uint64_t fsize;
uint64_t vaddr;
uint64_t vpages;
bool writable;
bool executable;
};
create_app_result create_app(const vfile::vfile &file, app_instance *&out) {
uint8_t magic[16];
if (file.dir_entry.length < 64)
return create_app_result::app_corrupt;
READ(0, 8, magic)
READ(16, 8, magic + 8)
for (int i = 0; i < 16; ++i)
if (magic[i] != correct_magic[i])
return create_app_result::app_corrupt;
uint64_t entry_point;
uint64_t phead_start;
uint16_t phead_entry_size;
uint16_t phead_entry_count;
READ(24, 8, &entry_point)
READ(32, 8, &phead_start)
READ(54, 2, &phead_entry_size)
READ(56, 2, &phead_entry_count)
if (file.dir_entry.length <
phead_start + phead_entry_size * phead_entry_count)
return create_app_result::app_corrupt;
utility::vector<load_info> load_infos;
for (uint16_t i = 0; i < phead_entry_count; ++i) {
uint64_t entry_start = phead_start + phead_entry_size * i;
uint32_t seg_type;
READ(entry_start, 4, &seg_type)
if (seg_type != 1)
continue;
uint64_t foffset;
uint64_t vaddr;
uint64_t fsize;
uint64_t vsize;
uint32_t flags;
READ(entry_start + 8, 8, &foffset)
READ(entry_start + 16, 8, &vaddr)
READ(entry_start + 32, 8, &fsize)
READ(entry_start + 40, 8, &vsize)
READ(entry_start + 4, 4, &flags)
if (vaddr & 0x1fffff)
return create_app_result::app_corrupt;
if (file.dir_entry.length < foffset + fsize)
return create_app_result::app_corrupt;
if (fsize > vsize)
return create_app_result::app_corrupt;
if (vaddr == 0)
return create_app_result::app_corrupt;
uint64_t vpages = (vsize - 1) / 0x200000 + 1;
if (vaddr + vpages * 0x200000 > ((1 << 30) - (4 << 20)))
return create_app_result::app_corrupt;
load_info info = {
.foffset = foffset,
.fsize = fsize,
.vaddr = vaddr,
.vpages = vpages,
.writable = (flags & 2) == 2,
.executable = (flags & 1) == 1
};
load_infos.add_end(info);
}
out = new app_instance();
for (unsigned i = 0; i < load_infos.count; ++i) {
const auto &info = load_infos.buffer[i];
for (uint64_t j = 0; j < info.vpages; ++j) {
uint64_t paddr = paging::take_2mib_pram_page();
out->map_page(info.vaddr + j * 0x200000, paddr,
info.writable, info.executable, true);
for (int k = 0; k < 512; ++k) {
uint64_t offset_in_segment = j * 0x200000 + k * 4096;
uint64_t kvaddr = paging::find_unmapped_vram_region(1);
paging::map_kernel_page(paddr + k * 4096, kvaddr, true, false);
storage::fs_result result = storage::fs_result::success;
if (info.fsize > offset_in_segment) {
if (info.fsize >= offset_in_segment + 4096)
result = file.read_file(
info.foffset + offset_in_segment, 4096, (void *)kvaddr);
else {
int to_read = info.fsize - offset_in_segment;
result = file.read_file(
info.foffset + offset_in_segment, to_read, (void *)kvaddr);
uint8_t *blank = (uint8_t *)(kvaddr + to_read);
for (int i = 0; i < 4096 - to_read; ++i)
blank[i] = 0;
}
}
else {
uint8_t *blank = (uint8_t *)kvaddr;
for (int i = 0; i < 4096; ++i)
blank[i] = 0;
}
paging::unmap_kernel_page(kvaddr);
if (result == storage::fs_result::device_error) {
delete out;
return create_app_result::device_error;
}
if (result == storage::fs_result::fs_corrupt) {
delete out;
return create_app_result::fs_corrupt;
}
}
}
}
out->create_stack();
out->set_instruction_pointer(entry_point);
return create_app_result::success;
}
}

View file

@ -1,5 +1,6 @@
#include <mercury/kernel/storage/bd/memory.hpp>
#include <mercury/kernel/storage/fs/tarfs.hpp>
#include <mercury/kernel/application.hpp>
#include <mercury/kernel/framebuffer.hpp>
#include <mercury/kernel/terminal.hpp>
#include <mercury/kernel/limine.hpp>
@ -103,6 +104,8 @@ uint64_t initfs_len;
[[noreturn]] static void with_kernel_p4();
extern "C" void load_gdt_and_idt();
extern "C" [[noreturn]] void entry() {
//TODO?: maybe we should check if the limine requests were
@ -113,9 +116,9 @@ extern "C" [[noreturn]] void entry() {
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
//we don't allocate any physical pages until after we are done using limine
//structures (specifically at the call to paging::map_kernel_stacks), 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 ||
@ -175,79 +178,29 @@ extern "C" [[noreturn]] void entry() {
//switch to kernel p4
paging::map_kernel_stack();
paging::map_kernel_stacks();
load_gdt_and_idt();
switch_to_kernel_p4(&with_kernel_p4);
}
static void print_mem() {
uint64_t used_vram_mib = (paging::get_used_vram_page_count() + 128) / 256;
uint64_t free_pram_mib = (paging::get_free_pram_page_count() + 128) / 256;
terminal::put_int_decimal(used_vram_mib);
terminal::put_string_sz(" MiB kernel memory mapped.\n");
terminal::put_int_decimal(free_pram_mib);
terminal::put_string_sz(" MiB physical memory free.\n");
}
[[noreturn]] static void print_and_halt(const char *msg) {
terminal::put_string_sz(msg);
while (1)
asm ("hlt");
}
static void draw_indent(unsigned indent) {
for (unsigned i = 0; i < indent; ++i)
terminal::put_char(' ');
}
static void dir_tree(
const vfile::vfile &root, const vfile::vfile &f, unsigned indent
) {
draw_indent(indent);
terminal::put_string(f.dir_entry.name);
terminal::put_char('\n');
std::optional<vfile::vfile> followed;
utility::vector<vfile::vfile> children;
switch (f.dir_entry.type) {
case storage::file_type::regular_file:
draw_indent(indent + 2);
terminal::put_string_sz("type: regular file\n");
draw_indent(indent + 2);
terminal::put_string_sz("length: ");
terminal::put_int_decimal(f.dir_entry.length);
terminal::put_string_sz(" bytes\n");
break;
case storage::file_type::symlink:
draw_indent(indent + 2);
terminal::put_string_sz("type: symlink\n");
draw_indent(indent + 2);
terminal::put_string_sz("target: ");
terminal::put_string(f.dir_entry.target);
terminal::put_char('\n');
if (f.follow_symlinks(root, followed) != storage::fs_result::success)
print_and_halt("failed to follow symlink.");
if (!followed) {
draw_indent(indent + 4);
terminal::put_string_sz("[broken]\n");
}
dir_tree(root, *followed, indent + 4);
break;
case storage::file_type::directory:
if (f.get_children(children) != storage::fs_result::success)
print_and_halt("failed to get children.");
draw_indent(indent + 2);
terminal::put_string_sz("type: directory\n");
draw_indent(indent + 2);
terminal::put_string_sz("children:\n");
if (children.count == 0) {
draw_indent(indent + 4);
terminal::put_string_sz("[empty]\n");
}
else
for (unsigned i = 0; i < children.count; ++i)
dir_tree(root, children.buffer[i], indent + 4);
break;
}
}
extern "C" [[noreturn]] void start_user_mode(
uint64_t rip, uint64_t rsp, uint64_t p4_paddr);
[[noreturn]] static void with_kernel_p4() {
@ -257,27 +210,46 @@ static void dir_tree(
storage::fs::tarfs_instance initfs_fs(&initfs_bd);
initfs_bd.mounted_as = &initfs_fs;
terminal::put_string_sz("kernel initialization complete.\n");
vfile::vfile vfs_root;
vfs_root.bd = &initfs_bd;
vfs_root.dir_entry.type = storage::file_type::directory;
vfs_root.path.absolute = true;
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");
vfile::vfile root;
root.bd = &initfs_bd;
root.dir_entry.type = storage::file_type::directory;
root.path.absolute = true;
if (initfs_fs.get_root_node(root.dir_entry.node) !=
if (initfs_fs.get_root_node(vfs_root.dir_entry.node) !=
storage::fs_result::success)
print_and_halt("failed to get root node.");
print_and_halt("failed to get root node of initfs.");
terminal::put_string_sz("initfs");
dir_tree(root, root, 0);
print_and_halt("halting.");
utility::string init_path_string("/bin/init.elf", 13);
vfile::canon_path init_path;
vfile::canonize_path(init_path_string, init_path);
std::optional<vfile::vfile> init_file;
if (vfile::lookup_path(vfs_root, init_path, init_file) !=
storage::fs_result::success)
print_and_halt("failed to look up /bin/init.elf.");
if (!init_file)
print_and_halt("/bin/init.elf does not exist.");
terminal::put_string_sz("/bin/init.elf is ");
terminal::put_int_decimal(init_file->dir_entry.length);
terminal::put_string_sz(" bytes long.\n");
application::app_instance *init;
if (application::create_app(*init_file, init) !=
application::create_app_result::success)
print_and_halt("failed to parse /bin/init.elf.");
terminal::put_string_sz("/bin/init.elf loaded:\n instruction pointer 0x");
terminal::put_int_hex(init->saved_regs.rip, 8);
terminal::put_string_sz("\n stack pointer 0x");
terminal::put_int_hex(init->saved_regs.rsp, 8);
terminal::put_string_sz("\n ");
terminal::put_int_decimal(init->count_mapped_vram_pages() * 2);
terminal::put_string_sz(" MiB userspace memory used\n");
print_mem();
terminal::put_string_sz("switching to /bin/init.elf.\n");
application::running_app = init;
start_user_mode(init->saved_regs.rip, init->saved_regs.rsp, init->p4_paddr);
}

View file

@ -10,9 +10,13 @@ extern "C" {
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 kernel_vram_end = 0xffffffffffe00000;
static constexpr uint64_t kernel_vram_pages =
(kernel_vram_end - kernel_vram_start) / 4096;
static constexpr uint64_t syscall_stack_bottom = 0xfffffffffff01000;
static constexpr uint64_t syscall_stack_top = 0xfffffffffffff000;
static constexpr uint64_t interrupt_stack_bottom = 0xffffffffffe01000;
static constexpr uint64_t interrupt_stack_top = 0xffffffffffeff000;
static constexpr uint64_t pram_pages = 1 << 23;
static uint64_t pram_usage_bitmap[pram_pages / 64];
@ -32,25 +36,29 @@ namespace mercury::kernel::paging {
[[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) {
uint64_t kernel_p4e;
uint64_t encode_pte(
uint64_t addr, bool user, bool write, bool execute, bool ps
) {
return (addr & 0x0000ffffffffffff) | (execute ? 0 : (1ULL << 63))
| (user << 2) | (write << 1) | 1;
| (ps << 7) | (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);
kernel_p4e = encode_pte(
(uint64_t)kernel_p3 - kernel_offset, false, true, true, false);
kernel_p4[511] = kernel_p4e;
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);
(uint64_t)kernel_p2 - kernel_offset, false, true, true, false);
for (int i = 0; i < 512; ++i)
kernel_p2[i] = encode_pte(
(uint64_t)kernel_p1s + 4096 * i - kernel_offset, false, true, true);
(uint64_t)kernel_p1s + 4096 * i - kernel_offset, false, true, true, false);
for (int i = 0; i < 512 * 512; ++i)
kernel_p1s[i] = 0;
}
@ -58,14 +66,22 @@ namespace mercury::kernel::paging {
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);
kernel_p1s[i] = encode_pte(paddr, false, write, execute, false);
}
void unmap_kernel_page(uint64_t vaddr) {
uint64_t i = (vaddr - kernel_vram_start) / 4096;
kernel_p1s[i] = 0;
asm volatile (
"mov %%cr3, %%rax\nmov %%rax, %%cr3" ::: "%rax"
);
}
static uint64_t take_pram_page() {
for (uint64_t i = 0; i < pram_pages / 64; ++i)
if (pram_usage_bitmap[i] != 0xffffffffffffffff)
if (~pram_usage_bitmap[i] != 0)
for (int j = 0; j < 64; ++j)
if (!(pram_usage_bitmap[i] & (1ULL << j))) {
if (~pram_usage_bitmap[i] & (1ULL << j)) {
pram_usage_bitmap[i] |= (1ULL << j);
return 4096 * (i * 64 + j);
}
@ -73,9 +89,27 @@ namespace mercury::kernel::paging {
return 0;
}
void map_kernel_stack() {
for (uint64_t vaddr = kernel_stack_bottom;
vaddr < kernel_stack_top; vaddr += 4096)
uint64_t take_2mib_pram_page() {
for (uint64_t i = 0; i < pram_pages / 512; ++i) {
for (int j = 0; j < 8; ++j)
if (pram_usage_bitmap[i * 8 + j] != 0)
goto next_i;
for (int j = 0; j < 8; ++j)
pram_usage_bitmap[i * 8 + j] = ~0ULL;
return 0x200000 * i;
next_i:
;
}
//TODO: handle error
return 0;
}
void map_kernel_stacks() {
for (uint64_t vaddr = syscall_stack_bottom;
vaddr < syscall_stack_top; vaddr += 4096)
map_kernel_page(take_pram_page(), vaddr, true, false);
for (uint64_t vaddr = interrupt_stack_bottom;
vaddr < interrupt_stack_top; vaddr += 4096)
map_kernel_page(take_pram_page(), vaddr, true, false);
}
@ -103,6 +137,12 @@ namespace mercury::kernel::paging {
return (void *)vaddr;
}
void map_new_kernel_page(uint64_t &vaddr_out, uint64_t &paddr_out) {
vaddr_out = find_unmapped_vram_region(1);
paddr_out = take_pram_page();
map_kernel_page(paddr_out, vaddr_out, true, false);
}
uint64_t get_used_vram_page_count() {
uint64_t count = 0;
for (uint64_t i = 0; i < kernel_vram_pages; ++i)

160
kernel/syscall.asm Normal file
View file

@ -0,0 +1,160 @@
bits 64
global load_gdt_and_idt
global start_user_mode
section .rodata
;0x28 picked to align with limine choice
;0x28 - kernel code
;0x30 - kernel data
;0x38 - user data
;0x40 - user code
gdtr:
dw 0x47
dq gdt
gdt:
dq 0
dq 0
dq 0
dq 0
dq 0
dq 0x00209b0000000000
dq 0x00009b0000000000
dq 0x0000fb0000000000
dq 0x0020fb0000000000
idtr:
dw 4095
dq idt
idt:
times 256 - ($ - idt) / 16 dq 0
section .text
load_gdt_and_idt:
lgdt [gdtr]
lidt [idtr]
ret
extern syscall_encode_color
encode_color_syscall:
call syscall_encode_color
mov edi, eax
xor rax, rax
mov eax, edi
xor rdi, rdi
xor rsi, rsi
xor rdx, rdx
jmp syscall_return
extern syscall_get_fb_vaddr
extern syscall_get_fb_dims
extern syscall_get_fb_pitch
get_framebuffer_syscall:
call syscall_get_fb_vaddr
push rax
call syscall_get_fb_dims
push rax
call syscall_get_fb_pitch
xor rsi, rsi
mov esi, eax
pop rdi
pop rax
xor rdx, rdx
jmp syscall_return
extern syscall_copy_framebuffer
draw_framebuffer_syscall:
call syscall_copy_framebuffer
xor rax, rax
xor rdi, rdi
xor rsi, rsi
xor rdx, rdx
jmp syscall_return
bad_syscall:
xor rax, rax
xor rdi, rdi
xor rsi, rsi
xor rdx, rdx
jmp syscall_return
syscall_entry:
mov r11, rsp
mov rsp, 0xfffffffffffff000
push r11
push rcx
cmp rax, 0
je encode_color_syscall
cmp rax, 1
je get_framebuffer_syscall
cmp rax, 2
je draw_framebuffer_syscall
jmp bad_syscall
syscall_return:
xor r8, r8
xor r9, r9
xor r10, r10
xor r11, r11
or r11, 0x200
pop rcx
pop rsp
o64 sysret
start_user_mode:
;intended rip in rdi
;intended rsp in rsi
;intended p4_paddr in rdx
mov rax, rdx
mov cr3, rax
;efer <- efer | 0x1
mov rcx, 0xc0000080
rdmsr
or al, 1
wrmsr
;lstar <- syscall_entry
mov rdx, syscall_entry
mov eax, edx
shr rdx, 32
mov ecx, 0xc0000082
wrmsr
;star <- 0x0030.0028.0000.0000
mov edx, 0x00300028
xor eax, eax
mov ecx, 0xc0000081
wrmsr
mov rcx, rdi
mov rsp, rsi
xor r11, r11
or r11, 0x200
xor rax, rax
xor rbx, rbx
xor rdx, rdx
xor rdi, rdi
xor rsi, rsi
xor rbp, rbp
xor r8, r8
xor r9, r9
xor r10, r10
xor r12, r12
xor r13, r13
xor r14, r14
xor r15, r15
o64 sysret

46
kernel/syscall.cpp Normal file
View file

@ -0,0 +1,46 @@
#include <mercury/kernel/application.hpp>
#include <mercury/kernel/framebuffer.hpp>
#include <mercury/kernel/paging.hpp>
using namespace mercury::kernel;
extern "C" uint32_t syscall_encode_color(uint32_t c) {
return (uint32_t)framebuffer::encode_color(
c & 0xff, (c >> 8) & 0xff, (c >> 16) & 0xff
);
}
extern "C" uint64_t syscall_get_fb_vaddr() {
auto *app = application::running_app;
if (app->framebuffer_vaddr != 0)
return app->framebuffer_vaddr;
uint64_t fb_len = framebuffer::dword_pitch * framebuffer::height * 4;
uint64_t fb_pages = (fb_len - 1) / 0x200000 + 1;
uint64_t vaddr = app->get_free_vaddr_pages(fb_pages);
for (uint64_t i = 0; i < fb_pages; ++i) {
uint64_t paddr = paging::take_2mib_pram_page();
app->map_page(vaddr + i * 0x200000, paddr, true, false, true);
}
app->framebuffer_vaddr = vaddr;
return vaddr;
}
extern "C" uint64_t syscall_get_fb_dims() {
return (uint64_t)(uint32_t)framebuffer::width +
((uint64_t)(uint32_t)framebuffer::height << 32);
}
extern "C" uint32_t syscall_get_fb_pitch() {
return (uint32_t)framebuffer::dword_pitch;
}
extern "C" void syscall_copy_framebuffer() {
auto *app = application::running_app;
if (app->framebuffer_vaddr != 0) {
const uint32_t *source = (const uint32_t *)app->framebuffer_vaddr;
for (int y = 0; y < framebuffer::height; ++y)
for (int x = 0; x < framebuffer::width; ++x)
framebuffer::vaddr[y * framebuffer::dword_pitch + x]
= source[y * framebuffer::dword_pitch + x];
}
}

View file

@ -108,4 +108,14 @@ namespace mercury::kernel::terminal {
}
static char hex_digits[] = "0123456789abcdef";
void put_int_hex(uint64_t n, int digits, bool with_dots) {
for (int digit = digits - 1; digit >= 0; --digit) {
put_char(hex_digits[(n >> (digit * 4)) & 0xf]);
if (with_dots && digit % 4 == 0 && digit != 0)
put_char('.');
}
}
}

View file

@ -163,6 +163,13 @@ namespace mercury::kernel::vfile {
}
storage::fs_result vfile::read_file(
uint64_t start, uint64_t length, void *into
) const {
return bd->mounted_as->read_bytes_from_file(
dir_entry.node, start, length, into);
}
storage::fs_result lookup_path(
const vfile &root, const canon_path &path, std::optional<vfile> &out
) {

View file

@ -1,6 +1,12 @@
GPP_ARGS = -Wall -Wextra -O3 -ggdb -I include -ffreestanding -fno-exceptions -fno-rtti -mno-sse
KERNEL_OBJECTS = allocator.cpp entry.cpp framebuffer.cpp paging.asm paging.cpp storage.cpp \
storage/bd/memory.cpp storage/fs/tarfs.cpp terminal.cpp utility.cpp vfile.cpp
GPP_ARGS = -Wall -Wextra -O3 -ggdb -I include \
-ffreestanding -fno-exceptions -fno-rtti -mno-sse
KGPP_ARGS = ${GPP_ARGS}
AGPP_ARGS = ${GPP_ARGS}
LD_ARGS = -z noexecstack
KLD_ARGS = -T kernel/link.ld ${LD_ARGS}
ALD_ARGS = -T applications/link.ld ${LD_ARGS}
LLD_ARGS = ${LD_ARGS}
all: out/disk.iso
@ -15,37 +21,61 @@ dist-clean:
rm -f include/mercury/kernel/limine.hpp
limine:
git clone --depth=1 -b v6.x-branch https://github.com/limine-bootloader/limine.git limine
git clone --depth=1 -b v6.x-branch \
https://github.com/limine-bootloader/limine.git limine
cd limine && ./bootstrap && ./configure -q --enable-bios --enable-bios-cd
+make -C limine
cp limine/limine.h include/mercury/kernel/limine.hpp
obj/kernel/entry.cpp.o: kernel/entry.cpp limine
@mkdir -p $(@D)
g++ -c ${GPP_ARGS} $< -o $@
g++ -c ${KGPP_ARGS} $< -o $@
obj/kernel/%.cpp.o: kernel/%.cpp
@mkdir -p $(@D)
g++ -c ${GPP_ARGS} $< -o $@
g++ -c ${KGPP_ARGS} $< -o $@
obj/kernel/%.asm.o: kernel/%.asm
@mkdir -p $(@D)
nasm -f elf64 $< -o $@
KERNEL_OBJECTS = allocator.cpp application.cpp entry.cpp framebuffer.cpp \
paging.asm paging.cpp storage.cpp storage/bd/memory.cpp terminal.cpp \
storage/fs/tarfs.cpp utility.cpp vfile.cpp syscall.asm syscall.cpp
obj/kernel.elf: ${KERNEL_OBJECTS:%=obj/kernel/%.o}
ld -T kernel/link.ld $^ -o $@
ld ${KLD_ARGS} $^ -o $@
obj/initfs.tgz:
@mkdir -p obj/initfs
echo test > obj/initfs/test.txt
mkdir -p obj/initfs/dir/dir2
ln -s ../test.txt obj/initfs/dir/test2.txt
obj/stdlib/%.asm.o: stdlib/%.asm
@mkdir -p $(@D)
nasm -f elf64 $< -o $@
obj/stdlib/%.cpp.o: stdlib/%.cpp
@mkdir -p $(@D)
g++ -c ${AGPP_ARGS} $< -o $@
STDLIB_OBJECTS = entry.cpp syscall.asm
obj/stdlib.o: ${STDLIB_OBJECTS:%=obj/stdlib/%.o}
ld -r ${LLD_ARGS} $^ -o $@
obj/%.cpp.o: applications/%.cpp
@mkdir -p $(@D)
g++ -c ${AGPP_ARGS} $< -o $@
INIT_OBJECTS = main.cpp
obj/initfs/bin/init.elf: ${INIT_OBJECTS:%=obj/init/%.o} obj/stdlib.o
@mkdir -p $(@D)
ld ${ALD_ARGS} $^ -o $@
APPLICATIONS = init
obj/initfs.tgz: ${APPLICATIONS:%=obj/initfs/bin/%.elf}
tar czf obj/initfs.tgz -C obj/initfs .
out/disk.iso: obj/kernel.elf obj/initfs.tgz limine
mkdir -p obj/iso out
cp obj/kernel.elf obj/initfs.tgz limine/bin/limine-bios.sys limine/bin/limine-bios-cd.bin limine.cfg obj/iso/
cp obj/kernel.elf obj/initfs.tgz limine/bin/limine-bios.sys \
limine/bin/limine-bios-cd.bin limine.cfg obj/iso/
cp terminus/ter-u16b.psf obj/iso/termfont.psf
xorriso -as mkisofs -quiet -no-emul-boot -boot-info-table -boot-load-size 4 -b limine-bios-cd.bin obj/iso -o $@
xorriso -as mkisofs -quiet -no-emul-boot -boot-info-table \
-boot-load-size 4 -b limine-bios-cd.bin obj/iso -o $@
limine/bin/limine bios-install $@
rm -rf obj/iso

View file

@ -1,6 +1,8 @@
target remote | qemu-system-x86_64 -gdb stdio -cdrom out/disk.iso -boot d
symbol-file obj/kernel.elf
add-symbol-file obj/initfs/bin/init.elf
set disassembly-flavor intel
set print asm-demangle on
break entry
break main
layout src

14
stdlib/entry.cpp Normal file
View file

@ -0,0 +1,14 @@
int main(int argc, char **argv);
extern "C" [[noreturn]] void _entry() {
//TODO: get command line via system call and populate argc and argv.
int argc = 0;
char **argv = 0;
int result = main(argc, argv);
//TODO: exit via system call and return result.
(void)result;
while (1)
;
}

35
stdlib/syscall.asm Normal file
View file

@ -0,0 +1,35 @@
bits 64
global encode_color
global get_framebuffer
global draw_framebuffer
section .text
encode_color:
mov rax, 0
syscall
ret
get_framebuffer:
push rcx
push rdx
push rsi
push rdi
mov rax, 1
syscall
pop rcx
mov qword [rcx], rax
pop rcx
mov dword [rcx], edi
shr rdi, 32
pop rcx
mov dword [rcx], edi
pop rcx
mov dword [rcx], esi
ret
draw_framebuffer:
mov rax, 2
syscall
ret