diff --git a/kernel/src/exec/elf.c b/kernel/src/exec/elf.c index e02c0ee..3da3213 100644 --- a/kernel/src/exec/elf.c +++ b/kernel/src/exec/elf.c @@ -1,8 +1,13 @@ +#include "exec/exec.h" +#include "mm/memop.h" +#include "mm/pmm.h" +#include "mm/vmm.h" +#include #include #include #include -void elf_load(char *data) { +program_t *elf_load(char *data) { Elf64_Ehdr *ehdr = (Elf64_Ehdr*)data; if ( ehdr->e_ident[EI_MAG0] != ELFMAG0 || ehdr->e_ident[EI_MAG1] != ELFMAG1 @@ -10,7 +15,7 @@ void elf_load(char *data) { || ehdr->e_ident[EI_MAG3] != ELFMAG3) { log("elf - loading failed: magic is incorrect\n"); - return; + return NULL; } log("elf - e_ident[EI_DATA]: %d\n", ehdr->e_ident[EI_DATA]); @@ -19,25 +24,100 @@ void elf_load(char *data) { log("elf - e_machine: %d\n", ehdr->e_machine); log("elf - e_entry: %p\n", ehdr->e_entry); log("elf - e_type: %p\n", ehdr->e_type); + log("elf - e_phnum: %p\n", ehdr->e_phnum); if ( ehdr->e_ident[EI_CLASS] != ELFCLASS64 || ehdr->e_machine != EM_X86_64) { log("elf - loading failed: is the file built for amd64?\n"); - return; + return NULL; } if (ehdr->e_type != ET_EXEC) { log("elf - loading failed: ELF type isn't ET_EXEC\n"); - return; + return NULL; } - Elf64_Phdr *phdr = (Elf64_Phdr*)data + ehdr->e_phoff; - for (uint16_t i = 0; i < ehdr->e_phnum; i++) { + // There's the interesing part + elf_program_t *ret = malloc(sizeof(elf_program_t)); // Allocate memory for the program. + + pagemap_t *pm = vmm_alloc_pm(); // Allocate a pagemap, so that we can map the program inside. + ret->program.pm = pm; + ret->program.entry = ehdr->e_entry; + ret->ehdr = ehdr; + + Elf64_Phdr *phdr = (Elf64_Phdr *)((uint8_t *)data + ehdr->e_phoff); + + for (uint16_t i = 0; i <= ehdr->e_phnum; i++) + { + log("elf - ELF segment type: %d\n", phdr[i].p_type); if (phdr[i].p_type != PT_LOAD) continue; - log("elf - Loadable program header: vaddr: %p", phdr[i].p_vaddr); + uint64_t vaddr_start = ALIGN_DOWN(phdr[i].p_vaddr, PMM_PAGE_SIZE); + uint64_t vaddr_end = ALIGN_UP(phdr[i].p_vaddr + phdr[i].p_memsz, PMM_PAGE_SIZE); + uint64_t offset = phdr[i].p_offset; + + uint64_t flags = VMM_PRESENT; + if (phdr[i].p_flags & PF_W) + flags |= VMM_WRITABLE; + if (!(phdr[i].p_flags & PF_X)) + flags |= VMM_NX; + + flags |= VMM_USER; // User mode access + + log("elf - loading ELF program header %u: vaddr 0x%llx - 0x%llx, offset 0x%llx, filesz 0x%llx, size 0x%llx, flags 0x%llx\n", + i, vaddr_start, vaddr_end, offset, phdr[i].p_filesz, phdr[i].p_memsz, flags); + + uint64_t page_offset = phdr[i].p_vaddr & (PMM_PAGE_SIZE - 1); + + for (uint64_t vaddr = vaddr_start; vaddr < vaddr_end; vaddr += PMM_PAGE_SIZE) + { + uint64_t phys = (uint64_t)pmm_request_page(); + if (!phys) + { + log("elf - pmm page alloc failed. out of memory?\n"); + return 0; + } + + vmm_map(pm, vaddr, phys, flags); + memset((void *)HIGHER_HALF(phys), 0, PMM_PAGE_SIZE); + + uint64_t file_page_offset = offset + (vaddr - vaddr_start); + uint64_t file_data_end = offset + phdr[i].p_filesz; + + if (file_page_offset < file_data_end) + { + uint64_t bytes_from_start = vaddr - vaddr_start; + uint64_t page_data_offset = 0; + + if (bytes_from_start == 0 && page_offset > 0) + { + page_data_offset = page_offset; + } + + uint64_t copy_offset = file_page_offset; + uint64_t copy_size = PMM_PAGE_SIZE - page_data_offset; + + if (copy_offset + copy_size > file_data_end) + { + copy_size = file_data_end - copy_offset; + } + + if (copy_size > 0) + { + void *dest = (void *)(HIGHER_HALF(phys) + page_data_offset); + void *src = (uint8_t *)data + copy_offset; + memcpy(dest, src, copy_size); + + //log("elf - copied 0x%llx bytes from ELF file offset 0x%llx to vaddr 0x%llx (phys 0x%llx)\n", + // copy_size, copy_offset, vaddr + page_data_offset, phys + page_data_offset); + } + } + } } + + log("elf - loaded ELF program in memory.\n"); + return (program_t*)ret; } \ No newline at end of file diff --git a/kernel/src/exec/elf.h b/kernel/src/exec/elf.h index d63ebc1..89e02a5 100644 --- a/kernel/src/exec/elf.h +++ b/kernel/src/exec/elf.h @@ -1,5 +1,6 @@ #pragma once +#include "exec/exec.h" #include // ELF magic. @@ -115,4 +116,10 @@ typedef struct { uint64_t p_align; } Elf64_Phdr; -void elf_load(char *data); \ No newline at end of file +typedef struct { + program_t program; + + Elf64_Ehdr *ehdr; +} elf_program_t; + +program_t *elf_load(char *data); \ No newline at end of file diff --git a/kernel/src/exec/exec.h b/kernel/src/exec/exec.h new file mode 100644 index 0000000..f0bb547 --- /dev/null +++ b/kernel/src/exec/exec.h @@ -0,0 +1,25 @@ +#pragma once + +/* + * A program (or a script, if a shebang is found inside), literally. + */ +#include "mm/vmm.h" + +#define EXEC_TYPE_ELF 0 + +typedef struct { + // The path to the program/script that will be executed. + char *path; + + // The pagemap where the program's code is loaded. + pagemap_t *pm; + + // The path to the first instruction. This will be passed to the new process's rip register. + uint64_t entry; + + // The program type. Used to get additional, unneeded information out of a program + int type; + + // That is what Soaplin needs to know. Executable file loaders are encouraged to extend this structure + // to include info such as the EHDR for the ELF loader... +} program_t; \ No newline at end of file diff --git a/kernel/src/fs/vfs.h b/kernel/src/fs/vfs.h index 6f70f09..25afce2 100644 --- a/kernel/src/fs/vfs.h +++ b/kernel/src/fs/vfs.h @@ -1 +1,27 @@ #pragma once + +// btw fuck sun + +#include + +#define VNODE_TYPE_FILE 0 +#define VNODE_TYPE_DIR 1 +#define VNODE_TYPE_DEV 2 + +typedef struct __vnode { + char name[128]; + int type; + + struct __vnode *children; + struct __vnode *next; + struct __vnode *parent; + + void(*write)(void *buffer, uint64_t off, uint64_t size); + void(*read)(void *buffer, uint64_t off, uint64_t size); +} vnode; + +typedef struct __vfs_mount { + struct vfs_node *mount_point; // The directory in the main VFS where this is mounted + struct vfs_node *mounted_root; // The root node of the mounted filesystem + struct vfs_mount *next; // Pointer to next mount point +} vfs_mount; \ No newline at end of file diff --git a/kernel/src/main.c b/kernel/src/main.c index ecc9422..440a3e3 100755 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -1,4 +1,5 @@ #include "exec/elf.h" +#include "exec/exec.h" #include "mm/pmm.h" #include "mm/vma.h" #include "mm/vmm.h" @@ -6,6 +7,7 @@ #include "sched/sched.h" #include "sys/arch/x86_64/pit.h" #include "sys/arch/x86_64/sse.h" +#include "sys/syscall.h" #include #include #include @@ -116,7 +118,6 @@ void kmain(void) { gdt_init(&kstack[8192]); idt_init(); fpu_activate(); - sse_init(); pmm_init(); @@ -129,11 +130,7 @@ void kmain(void) { asm("hlt"); } - char *a = malloc(1); - *a = 32; - log("Allocated 1 byte at 0x%.16llx\n", (uint64_t)a); - free(a); - + syscall_init(); pit_init(1000); sched_init(); //user_init(); @@ -141,7 +138,8 @@ void kmain(void) { struct limine_file *f = module_request.response->modules[0]; log("kmain - %s\n", f->path); - elf_load((char*)f->address); + program_t *prog = elf_load((char*)f->address); + sched_create("Init", prog->entry, prog->pm, SCHED_USER_PROCESS); log("kernel - Soaplin initialized sucessfully.\n"); while (1) diff --git a/kernel/src/sched/sched.c b/kernel/src/sched/sched.c index 2ef96e3..a2c6dda 100755 --- a/kernel/src/sched/sched.c +++ b/kernel/src/sched/sched.c @@ -11,6 +11,18 @@ sched_process *curr_proc; int current_pid = 0; int standby = 0; +void map_range_to_pagemap(pagemap_t *dest_pagemap, pagemap_t *src_pagemap, uint64_t start, uint64_t size, uint64_t flags) +{ + for (uint64_t offset = 0; offset < size; offset += PMM_PAGE_SIZE) + { + uint64_t phys = virt_to_phys(src_pagemap, start + offset); + if (phys) + { + vmm_map(dest_pagemap, start + offset, phys, flags); + } + } +} + void sched_init() { // TODO: It may be good to implement heap memory to save space. @@ -71,7 +83,7 @@ sched_process *sched_create(char *name, uint64_t entry_point, pagemap_t* pm, uin proc->regs.ss = 0x30; } else if (flags == SCHED_USER_PROCESS) { - proc->regs.cs = 0x43; // Run in kernel mode + proc->regs.cs = 0x43; // Run in user mode proc->regs.ss = 0x3B; } proc->regs.rflags = 0x202; // Enable interrupts @@ -83,6 +95,8 @@ sched_process *sched_create(char *name, uint64_t entry_point, pagemap_t* pm, uin current_pid++; + map_range_to_pagemap(pm, vmm_kernel_pm, 0x1000, 0x10000, VMM_PRESENT | VMM_WRITABLE | VMM_USER); + if (standby) { // Disable standby mode as there's actually something to // run, now. @@ -90,7 +104,7 @@ sched_process *sched_create(char *name, uint64_t entry_point, pagemap_t* pm, uin log("sched - Standby mode has been" "disabled.\n"); } - + log("sched - created process '%s' (pid: %d, rip: %p)\n", proc->name, proc->pid, proc->regs.rip); return proc; } diff --git a/kernel/src/sys/arch/x86_64/interrupts.c b/kernel/src/sys/arch/x86_64/interrupts.c index 968c7a3..7bcd7ba 100755 --- a/kernel/src/sys/arch/x86_64/interrupts.c +++ b/kernel/src/sys/arch/x86_64/interrupts.c @@ -5,6 +5,7 @@ #include "sys/arch/x86_64/pic.h" #include "sys/arch/x86_64/rtc.h" #include "sys/log.h" +#include "sys/syscall.h" //#include "sys/sched.h" #include #include @@ -71,10 +72,7 @@ void exception_handler(registers_t *regs) { } else if (regs->int_no == 0x80) { - log("syscall - Hello World! Current process: %s\n", curr_proc->name); - if (curr_proc->flags == SCHED_USER_PROCESS) - log("syscall - Btw we made it to userspace, baby!\n", curr_proc->name); - + syscall_handle(regs); } //logln(info, "arch/ints", "Received interrupt %d\n", regs->int_no); pic_ack(regs->int_no - 32); diff --git a/kernel/src/sys/syscall.c b/kernel/src/sys/syscall.c new file mode 100644 index 0000000..80f280f --- /dev/null +++ b/kernel/src/sys/syscall.c @@ -0,0 +1,48 @@ +#include "sched/sched.h" +#include +#include +#include +#include + +static syscall syscall_table[1024]; + +static int syscall_initialized = 0; + +// Stub function for undefined syscalls. +static uint64_t __syscall_undefined() { return 0; } + +void syscall_handle(registers_t *regs) { + if (regs->rax > 1024) { + log("syscall - syscall_handle was called with rax better than what Soaplin supports (1024). did you forget to set rax?\n"); + return; + } + + if (curr_proc == NULL || curr_proc->regs.cs != 0x43) { + log("syscall - syscall_handle was called by the kernel. is this wanted?\n"); + return; + } + + if (syscall_table[regs->rax] == (syscall)__syscall_undefined) { + log("syscall - syscall_handle was called with an undefined system call. (%d)\n", regs->rax); + return; + } + + regs->rax = syscall_table[regs->rax](regs->rsp, regs->rbp, regs->r12, regs->r13, regs->r14, regs->r15); + return; +} + +void syscall_register(int id, syscall handler) { + if (syscall_table[id] != (syscall)__syscall_undefined) + { + log("syscall - warning: syscall_register has been called to try replacing an existing syscall.\n"); + return; + } + + syscall_table[id] = handler; + log("syscall - System call %d has been set to %p", handler); +} + +void syscall_init() { + for (int i = 0; i < 1024; i++) + syscall_table[i] = (syscall)__syscall_undefined; +} \ No newline at end of file diff --git a/kernel/src/sys/syscall.h b/kernel/src/sys/syscall.h new file mode 100644 index 0000000..5896352 --- /dev/null +++ b/kernel/src/sys/syscall.h @@ -0,0 +1,18 @@ +#pragma once + +#include "sys/arch/x86_64/idt.h" +#include + +/// A function that defines a system call. +/// NOTE: Arguments are defined as uint64_t, but you can simply put your own type, and then cast your function to syscall. +typedef uint64_t(*syscall)(uint64_t rsp, uint64_t rbp, uint64_t r12, uint64_t r13, uint64_t r14, uint64_t r15); + +/// Registers a system call. +/// NOTE: an existing system call cannot be replaced by another. +void syscall_register(int id, syscall handler); + +/// Called by the interupt handler, or the "syscall" instruction handler +void syscall_handle(registers_t*); + +/// Initialize the system calls. +void syscall_init(); \ No newline at end of file diff --git a/kernel/src/sys/uname.h b/kernel/src/sys/uname.h new file mode 100644 index 0000000..b667962 --- /dev/null +++ b/kernel/src/sys/uname.h @@ -0,0 +1,13 @@ +#pragma once + +typedef struct __uname { + char sysname[128]; /* Operating system name */ + char nodename[128]; /* Name within communications network + to which the node is attached, if any */ + char release[128]; /* Operating system release */ + char version[128]; /* Operating system version */ + char machine[128]; /* Hardware type identifier */ +#ifdef _GNU_SOURCE + char domainname[]; /* NIS or YP domain name */ +#endif +} uname_t; \ No newline at end of file diff --git a/testing/link.ld b/testing/link.ld index 6e0346c..d234f6a 100644 --- a/testing/link.ld +++ b/testing/link.ld @@ -1,20 +1,23 @@ +OUTPUT_FORMAT("elf64-x86-64") +ENTRY(_start) + SECTIONS { - . = 0x1000; + . = 0x100000; - .text : { - *(.text*) - } + .text : { + *(.text) + } + + .rodata : { + *(.rodata) + } - .rodata : { - *(.rodata*) - } + .data : { + *(.data) + } - .data : { - *(.data*) - } - - .bss : { - *(.bss*) - *(COMMON) - } -} + .bss : { + *(COMMON) + *(.bss) + } +} \ No newline at end of file diff --git a/testing/sk-hello.elf b/testing/sk-hello.elf index 161dae3..51f21a4 100755 Binary files a/testing/sk-hello.elf and b/testing/sk-hello.elf differ