application loading
This commit is contained in:
parent
882e74b219
commit
4130562b15
21 changed files with 918 additions and 124 deletions
24
applications/init/main.cpp
Normal file
24
applications/init/main.cpp
Normal 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
38
applications/link.ld
Normal 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
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,14 @@
|
||||||
only the first 32GiB of physical memory are considered. this can be changed
|
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
|
with pram_pages in paging.cpp. vram layout is as follows:
|
||||||
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.
|
|
||||||
|
|
||||||
if any of these facts change, the linker script and paging code should be
|
0x0000.0000.0000 - 0x0000.001f.ffff: always unmapped
|
||||||
checked closely.
|
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
|
||||||
|
|
27
documentation/syscalls.txt
Normal file
27
documentation/syscalls.txt
Normal 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.)
|
72
include/mercury/kernel/application.hpp
Normal file
72
include/mercury/kernel/application.hpp
Normal 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
|
|
@ -5,8 +5,10 @@
|
||||||
|
|
||||||
namespace mercury::kernel::framebuffer {
|
namespace mercury::kernel::framebuffer {
|
||||||
|
|
||||||
|
extern uint32_t *vaddr;
|
||||||
extern int width;
|
extern int width;
|
||||||
extern int height;
|
extern int height;
|
||||||
|
extern int dword_pitch;
|
||||||
|
|
||||||
void init_framebuffer(uint64_t vaddr, uint64_t width, uint64_t height, uint64_t pitch);
|
void init_framebuffer(uint64_t vaddr, uint64_t width, uint64_t height, uint64_t pitch);
|
||||||
|
|
||||||
|
|
|
@ -11,27 +11,36 @@ namespace mercury::kernel::paging {
|
||||||
void mark_all_pram_used();
|
void mark_all_pram_used();
|
||||||
void mark_all_vram_free();
|
void mark_all_vram_free();
|
||||||
|
|
||||||
//assumes page-alignment
|
|
||||||
void mark_pram_region_free(uint64_t start_addr, uint64_t end_addr);
|
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);
|
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 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 init_kernel_page_tables(uint64_t kernel_offset);
|
||||||
|
|
||||||
void map_kernel_stack();
|
void map_kernel_stacks();
|
||||||
|
|
||||||
//assumes page-alignment
|
|
||||||
void map_kernel_page(
|
void map_kernel_page(
|
||||||
uint64_t paddr, uint64_t vaddr, bool write, bool execute);
|
uint64_t paddr, uint64_t vaddr, bool write, bool execute);
|
||||||
|
|
||||||
|
void unmap_kernel_page(uint64_t vaddr);
|
||||||
|
|
||||||
//maps writable and not executable
|
//maps writable and not executable
|
||||||
void *map_new_kernel_pages(uint64_t count);
|
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_used_vram_page_count();
|
||||||
uint64_t get_free_pram_page_count();
|
uint64_t get_free_pram_page_count();
|
||||||
|
|
||||||
|
extern uint64_t kernel_p4e;
|
||||||
|
|
||||||
|
uint64_t take_2mib_pram_page();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -28,6 +28,8 @@ namespace mercury::kernel::terminal {
|
||||||
|
|
||||||
void put_int_decimal(uint64_t n, bool with_commas = true);
|
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
|
#endif
|
||||||
|
|
|
@ -48,6 +48,11 @@ namespace mercury::kernel::vfile {
|
||||||
//dir_entry.type is assumed to be directory. out must be empty on entry.
|
//dir_entry.type is assumed to be directory. out must be empty on entry.
|
||||||
storage::fs_result get_children(utility::vector<vfile> &out) const;
|
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.
|
//path must be absolute. follows symlinks on all but the last node.
|
||||||
|
|
26
include/mercury/syscall.hpp
Normal file
26
include/mercury/syscall.hpp
Normal 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
266
kernel/application.cpp
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
142
kernel/entry.cpp
142
kernel/entry.cpp
|
@ -1,5 +1,6 @@
|
||||||
#include <mercury/kernel/storage/bd/memory.hpp>
|
#include <mercury/kernel/storage/bd/memory.hpp>
|
||||||
#include <mercury/kernel/storage/fs/tarfs.hpp>
|
#include <mercury/kernel/storage/fs/tarfs.hpp>
|
||||||
|
#include <mercury/kernel/application.hpp>
|
||||||
#include <mercury/kernel/framebuffer.hpp>
|
#include <mercury/kernel/framebuffer.hpp>
|
||||||
#include <mercury/kernel/terminal.hpp>
|
#include <mercury/kernel/terminal.hpp>
|
||||||
#include <mercury/kernel/limine.hpp>
|
#include <mercury/kernel/limine.hpp>
|
||||||
|
@ -103,6 +104,8 @@ uint64_t initfs_len;
|
||||||
|
|
||||||
[[noreturn]] static void with_kernel_p4();
|
[[noreturn]] static void with_kernel_p4();
|
||||||
|
|
||||||
|
extern "C" void load_gdt_and_idt();
|
||||||
|
|
||||||
extern "C" [[noreturn]] void entry() {
|
extern "C" [[noreturn]] void entry() {
|
||||||
|
|
||||||
//TODO?: maybe we should check if the limine requests were
|
//TODO?: maybe we should check if the limine requests were
|
||||||
|
@ -113,9 +116,9 @@ extern "C" [[noreturn]] void entry() {
|
||||||
paging::mark_all_pram_used();
|
paging::mark_all_pram_used();
|
||||||
auto memmap = memmap_request.response;
|
auto memmap = memmap_request.response;
|
||||||
for (uint64_t i = 0; i < memmap->entry_count; ++i) {
|
for (uint64_t i = 0; i < memmap->entry_count; ++i) {
|
||||||
//we don't start allocating physical pages until after we are done using
|
//we don't allocate any physical pages until after we are done using limine
|
||||||
//limine structures (specifically at the call to paging::map_kernel_stack),
|
//structures (specifically at the call to paging::map_kernel_stacks), so
|
||||||
//so we consider bootloader reclaimable to be free. usable and bootloader
|
//we consider bootloader reclaimable to be free. usable and bootloader
|
||||||
//reclaimable are guaranteed by limine spec to be page-aligned.
|
//reclaimable are guaranteed by limine spec to be page-aligned.
|
||||||
auto entry = memmap->entries[i];
|
auto entry = memmap->entries[i];
|
||||||
if (entry->type == LIMINE_MEMMAP_USABLE ||
|
if (entry->type == LIMINE_MEMMAP_USABLE ||
|
||||||
|
@ -175,79 +178,29 @@ extern "C" [[noreturn]] void entry() {
|
||||||
|
|
||||||
//switch to kernel p4
|
//switch to kernel p4
|
||||||
|
|
||||||
paging::map_kernel_stack();
|
paging::map_kernel_stacks();
|
||||||
|
load_gdt_and_idt();
|
||||||
switch_to_kernel_p4(&with_kernel_p4);
|
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) {
|
[[noreturn]] static void print_and_halt(const char *msg) {
|
||||||
terminal::put_string_sz(msg);
|
terminal::put_string_sz(msg);
|
||||||
while (1)
|
while (1)
|
||||||
asm ("hlt");
|
asm ("hlt");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw_indent(unsigned indent) {
|
extern "C" [[noreturn]] void start_user_mode(
|
||||||
for (unsigned i = 0; i < indent; ++i)
|
uint64_t rip, uint64_t rsp, uint64_t p4_paddr);
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[[noreturn]] static void with_kernel_p4() {
|
[[noreturn]] static void with_kernel_p4() {
|
||||||
|
|
||||||
|
@ -257,27 +210,46 @@ static void dir_tree(
|
||||||
storage::fs::tarfs_instance initfs_fs(&initfs_bd);
|
storage::fs::tarfs_instance initfs_fs(&initfs_bd);
|
||||||
initfs_bd.mounted_as = &initfs_fs;
|
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;
|
if (initfs_fs.get_root_node(vfs_root.dir_entry.node) !=
|
||||||
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) !=
|
|
||||||
storage::fs_result::success)
|
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");
|
utility::string init_path_string("/bin/init.elf", 13);
|
||||||
dir_tree(root, root, 0);
|
vfile::canon_path init_path;
|
||||||
print_and_halt("halting.");
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,13 @@ extern "C" {
|
||||||
namespace mercury::kernel::paging {
|
namespace mercury::kernel::paging {
|
||||||
|
|
||||||
static constexpr uint64_t kernel_vram_start = 0xffffffffc0000000;
|
static constexpr uint64_t kernel_vram_start = 0xffffffffc0000000;
|
||||||
static constexpr uint64_t kernel_vram_pages = 261888;
|
static constexpr uint64_t kernel_vram_end = 0xffffffffffe00000;
|
||||||
static constexpr uint64_t kernel_stack_bottom = 0xfffffffffff01000;
|
static constexpr uint64_t kernel_vram_pages =
|
||||||
static constexpr uint64_t kernel_stack_top = 0xfffffffffffff000;
|
(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 constexpr uint64_t pram_pages = 1 << 23;
|
||||||
static uint64_t pram_usage_bitmap[pram_pages / 64];
|
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_p2[512];
|
||||||
[[gnu::aligned(4096)]] uint64_t kernel_p1s[512 * 512];
|
[[gnu::aligned(4096)]] uint64_t kernel_p1s[512 * 512];
|
||||||
|
|
||||||
static uint64_t encode_pte(
|
uint64_t kernel_p4e;
|
||||||
uint64_t addr, bool user, bool write, bool execute) {
|
|
||||||
|
uint64_t encode_pte(
|
||||||
|
uint64_t addr, bool user, bool write, bool execute, bool ps
|
||||||
|
) {
|
||||||
return (addr & 0x0000ffffffffffff) | (execute ? 0 : (1ULL << 63))
|
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) {
|
void init_kernel_page_tables(uint64_t kernel_offset) {
|
||||||
__kernel_p4_paddr = (uint64_t)kernel_p4 - kernel_offset;
|
__kernel_p4_paddr = (uint64_t)kernel_p4 - kernel_offset;
|
||||||
for (int i = 0; i < 511; ++i)
|
for (int i = 0; i < 511; ++i)
|
||||||
kernel_p4[i] = 0;
|
kernel_p4[i] = 0;
|
||||||
kernel_p4[511] = encode_pte(
|
kernel_p4e = encode_pte(
|
||||||
(uint64_t)kernel_p3 - kernel_offset, false, true, true);
|
(uint64_t)kernel_p3 - kernel_offset, false, true, true, false);
|
||||||
|
kernel_p4[511] = kernel_p4e;
|
||||||
for (int i = 0; i < 511; ++i)
|
for (int i = 0; i < 511; ++i)
|
||||||
kernel_p3[i] = 0;
|
kernel_p3[i] = 0;
|
||||||
kernel_p3[511] = encode_pte(
|
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)
|
for (int i = 0; i < 512; ++i)
|
||||||
kernel_p2[i] = encode_pte(
|
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)
|
for (int i = 0; i < 512 * 512; ++i)
|
||||||
kernel_p1s[i] = 0;
|
kernel_p1s[i] = 0;
|
||||||
}
|
}
|
||||||
|
@ -58,14 +66,22 @@ namespace mercury::kernel::paging {
|
||||||
void map_kernel_page(
|
void map_kernel_page(
|
||||||
uint64_t paddr, uint64_t vaddr, bool write, bool execute) {
|
uint64_t paddr, uint64_t vaddr, bool write, bool execute) {
|
||||||
uint64_t i = (vaddr - kernel_vram_start) / 4096;
|
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() {
|
static uint64_t take_pram_page() {
|
||||||
for (uint64_t i = 0; i < pram_pages / 64; ++i)
|
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)
|
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);
|
pram_usage_bitmap[i] |= (1ULL << j);
|
||||||
return 4096 * (i * 64 + j);
|
return 4096 * (i * 64 + j);
|
||||||
}
|
}
|
||||||
|
@ -73,9 +89,27 @@ namespace mercury::kernel::paging {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void map_kernel_stack() {
|
uint64_t take_2mib_pram_page() {
|
||||||
for (uint64_t vaddr = kernel_stack_bottom;
|
for (uint64_t i = 0; i < pram_pages / 512; ++i) {
|
||||||
vaddr < kernel_stack_top; vaddr += 4096)
|
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);
|
map_kernel_page(take_pram_page(), vaddr, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,6 +137,12 @@ namespace mercury::kernel::paging {
|
||||||
return (void *)vaddr;
|
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 get_used_vram_page_count() {
|
||||||
uint64_t count = 0;
|
uint64_t count = 0;
|
||||||
for (uint64_t i = 0; i < kernel_vram_pages; ++i)
|
for (uint64_t i = 0; i < kernel_vram_pages; ++i)
|
||||||
|
|
160
kernel/syscall.asm
Normal file
160
kernel/syscall.asm
Normal 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
46
kernel/syscall.cpp
Normal 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];
|
||||||
|
}
|
||||||
|
}
|
|
@ -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('.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
storage::fs_result lookup_path(
|
||||||
const vfile &root, const canon_path &path, std::optional<vfile> &out
|
const vfile &root, const canon_path &path, std::optional<vfile> &out
|
||||||
) {
|
) {
|
||||||
|
|
58
makefile
58
makefile
|
@ -1,6 +1,12 @@
|
||||||
GPP_ARGS = -Wall -Wextra -O3 -ggdb -I include -ffreestanding -fno-exceptions -fno-rtti -mno-sse
|
GPP_ARGS = -Wall -Wextra -O3 -ggdb -I include \
|
||||||
KERNEL_OBJECTS = allocator.cpp entry.cpp framebuffer.cpp paging.asm paging.cpp storage.cpp \
|
-ffreestanding -fno-exceptions -fno-rtti -mno-sse
|
||||||
storage/bd/memory.cpp storage/fs/tarfs.cpp terminal.cpp utility.cpp vfile.cpp
|
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
|
all: out/disk.iso
|
||||||
|
|
||||||
|
@ -15,37 +21,61 @@ dist-clean:
|
||||||
rm -f include/mercury/kernel/limine.hpp
|
rm -f include/mercury/kernel/limine.hpp
|
||||||
|
|
||||||
limine:
|
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
|
cd limine && ./bootstrap && ./configure -q --enable-bios --enable-bios-cd
|
||||||
+make -C limine
|
+make -C limine
|
||||||
cp limine/limine.h include/mercury/kernel/limine.hpp
|
cp limine/limine.h include/mercury/kernel/limine.hpp
|
||||||
|
|
||||||
obj/kernel/entry.cpp.o: kernel/entry.cpp limine
|
obj/kernel/entry.cpp.o: kernel/entry.cpp limine
|
||||||
@mkdir -p $(@D)
|
@mkdir -p $(@D)
|
||||||
g++ -c ${GPP_ARGS} $< -o $@
|
g++ -c ${KGPP_ARGS} $< -o $@
|
||||||
|
|
||||||
obj/kernel/%.cpp.o: kernel/%.cpp
|
obj/kernel/%.cpp.o: kernel/%.cpp
|
||||||
@mkdir -p $(@D)
|
@mkdir -p $(@D)
|
||||||
g++ -c ${GPP_ARGS} $< -o $@
|
g++ -c ${KGPP_ARGS} $< -o $@
|
||||||
|
|
||||||
obj/kernel/%.asm.o: kernel/%.asm
|
obj/kernel/%.asm.o: kernel/%.asm
|
||||||
@mkdir -p $(@D)
|
@mkdir -p $(@D)
|
||||||
nasm -f elf64 $< -o $@
|
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}
|
obj/kernel.elf: ${KERNEL_OBJECTS:%=obj/kernel/%.o}
|
||||||
ld -T kernel/link.ld $^ -o $@
|
ld ${KLD_ARGS} $^ -o $@
|
||||||
|
|
||||||
obj/initfs.tgz:
|
obj/stdlib/%.asm.o: stdlib/%.asm
|
||||||
@mkdir -p obj/initfs
|
@mkdir -p $(@D)
|
||||||
echo test > obj/initfs/test.txt
|
nasm -f elf64 $< -o $@
|
||||||
mkdir -p obj/initfs/dir/dir2
|
|
||||||
ln -s ../test.txt obj/initfs/dir/test2.txt
|
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 .
|
tar czf obj/initfs.tgz -C obj/initfs .
|
||||||
|
|
||||||
out/disk.iso: obj/kernel.elf obj/initfs.tgz limine
|
out/disk.iso: obj/kernel.elf obj/initfs.tgz limine
|
||||||
mkdir -p obj/iso out
|
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
|
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 $@
|
limine/bin/limine bios-install $@
|
||||||
rm -rf obj/iso
|
rm -rf obj/iso
|
||||||
|
|
2
qemu.gdb
2
qemu.gdb
|
@ -1,6 +1,8 @@
|
||||||
target remote | qemu-system-x86_64 -gdb stdio -cdrom out/disk.iso -boot d
|
target remote | qemu-system-x86_64 -gdb stdio -cdrom out/disk.iso -boot d
|
||||||
symbol-file obj/kernel.elf
|
symbol-file obj/kernel.elf
|
||||||
|
add-symbol-file obj/initfs/bin/init.elf
|
||||||
set disassembly-flavor intel
|
set disassembly-flavor intel
|
||||||
set print asm-demangle on
|
set print asm-demangle on
|
||||||
break entry
|
break entry
|
||||||
|
break main
|
||||||
layout src
|
layout src
|
||||||
|
|
14
stdlib/entry.cpp
Normal file
14
stdlib/entry.cpp
Normal 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
35
stdlib/syscall.asm
Normal 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
|
Reference in a new issue