elf: working loader / syscall: introduce the syscall api.
This commit is contained in:
parent
2d4031eacc
commit
5afe37a798
12 changed files with 267 additions and 37 deletions
|
@ -1,8 +1,13 @@
|
||||||
|
#include "exec/exec.h"
|
||||||
|
#include "mm/memop.h"
|
||||||
|
#include "mm/pmm.h"
|
||||||
|
#include "mm/vmm.h"
|
||||||
|
#include <mm/liballoc/liballoc.h>
|
||||||
#include <exec/elf.h>
|
#include <exec/elf.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <sys/log.h>
|
#include <sys/log.h>
|
||||||
|
|
||||||
void elf_load(char *data) {
|
program_t *elf_load(char *data) {
|
||||||
Elf64_Ehdr *ehdr = (Elf64_Ehdr*)data;
|
Elf64_Ehdr *ehdr = (Elf64_Ehdr*)data;
|
||||||
if ( ehdr->e_ident[EI_MAG0] != ELFMAG0
|
if ( ehdr->e_ident[EI_MAG0] != ELFMAG0
|
||||||
|| ehdr->e_ident[EI_MAG1] != ELFMAG1
|
|| ehdr->e_ident[EI_MAG1] != ELFMAG1
|
||||||
|
@ -10,7 +15,7 @@ void elf_load(char *data) {
|
||||||
|| ehdr->e_ident[EI_MAG3] != ELFMAG3)
|
|| ehdr->e_ident[EI_MAG3] != ELFMAG3)
|
||||||
{
|
{
|
||||||
log("elf - loading failed: magic is incorrect\n");
|
log("elf - loading failed: magic is incorrect\n");
|
||||||
return;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
log("elf - e_ident[EI_DATA]: %d\n", ehdr->e_ident[EI_DATA]);
|
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_machine: %d\n", ehdr->e_machine);
|
||||||
log("elf - e_entry: %p\n", ehdr->e_entry);
|
log("elf - e_entry: %p\n", ehdr->e_entry);
|
||||||
log("elf - e_type: %p\n", ehdr->e_type);
|
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
|
if ( ehdr->e_ident[EI_CLASS] != ELFCLASS64
|
||||||
|| ehdr->e_machine != EM_X86_64)
|
|| ehdr->e_machine != EM_X86_64)
|
||||||
{
|
{
|
||||||
log("elf - loading failed: is the file built for amd64?\n");
|
log("elf - loading failed: is the file built for amd64?\n");
|
||||||
return;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ehdr->e_type != ET_EXEC)
|
if (ehdr->e_type != ET_EXEC)
|
||||||
{
|
{
|
||||||
log("elf - loading failed: ELF type isn't ET_EXEC\n");
|
log("elf - loading failed: ELF type isn't ET_EXEC\n");
|
||||||
return;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Elf64_Phdr *phdr = (Elf64_Phdr*)data + ehdr->e_phoff;
|
// There's the interesing part
|
||||||
for (uint16_t i = 0; i < ehdr->e_phnum; i++) {
|
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)
|
if (phdr[i].p_type != PT_LOAD)
|
||||||
continue;
|
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;
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "exec/exec.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
// ELF magic.
|
// ELF magic.
|
||||||
|
@ -115,4 +116,10 @@ typedef struct {
|
||||||
uint64_t p_align;
|
uint64_t p_align;
|
||||||
} Elf64_Phdr;
|
} Elf64_Phdr;
|
||||||
|
|
||||||
void elf_load(char *data);
|
typedef struct {
|
||||||
|
program_t program;
|
||||||
|
|
||||||
|
Elf64_Ehdr *ehdr;
|
||||||
|
} elf_program_t;
|
||||||
|
|
||||||
|
program_t *elf_load(char *data);
|
25
kernel/src/exec/exec.h
Normal file
25
kernel/src/exec/exec.h
Normal file
|
@ -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;
|
|
@ -1 +1,27 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// btw fuck sun
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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;
|
|
@ -1,4 +1,5 @@
|
||||||
#include "exec/elf.h"
|
#include "exec/elf.h"
|
||||||
|
#include "exec/exec.h"
|
||||||
#include "mm/pmm.h"
|
#include "mm/pmm.h"
|
||||||
#include "mm/vma.h"
|
#include "mm/vma.h"
|
||||||
#include "mm/vmm.h"
|
#include "mm/vmm.h"
|
||||||
|
@ -6,6 +7,7 @@
|
||||||
#include "sched/sched.h"
|
#include "sched/sched.h"
|
||||||
#include "sys/arch/x86_64/pit.h"
|
#include "sys/arch/x86_64/pit.h"
|
||||||
#include "sys/arch/x86_64/sse.h"
|
#include "sys/arch/x86_64/sse.h"
|
||||||
|
#include "sys/syscall.h"
|
||||||
#include <sys/log.h>
|
#include <sys/log.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
@ -116,7 +118,6 @@ void kmain(void) {
|
||||||
gdt_init(&kstack[8192]);
|
gdt_init(&kstack[8192]);
|
||||||
idt_init();
|
idt_init();
|
||||||
fpu_activate();
|
fpu_activate();
|
||||||
|
|
||||||
sse_init();
|
sse_init();
|
||||||
|
|
||||||
pmm_init();
|
pmm_init();
|
||||||
|
@ -129,11 +130,7 @@ void kmain(void) {
|
||||||
asm("hlt");
|
asm("hlt");
|
||||||
}
|
}
|
||||||
|
|
||||||
char *a = malloc(1);
|
syscall_init();
|
||||||
*a = 32;
|
|
||||||
log("Allocated 1 byte at 0x%.16llx\n", (uint64_t)a);
|
|
||||||
free(a);
|
|
||||||
|
|
||||||
pit_init(1000);
|
pit_init(1000);
|
||||||
sched_init();
|
sched_init();
|
||||||
//user_init();
|
//user_init();
|
||||||
|
@ -141,7 +138,8 @@ void kmain(void) {
|
||||||
struct limine_file *f = module_request.response->modules[0];
|
struct limine_file *f = module_request.response->modules[0];
|
||||||
log("kmain - %s\n", f->path);
|
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");
|
log("kernel - Soaplin initialized sucessfully.\n");
|
||||||
while (1)
|
while (1)
|
||||||
|
|
|
@ -11,6 +11,18 @@ sched_process *curr_proc;
|
||||||
int current_pid = 0;
|
int current_pid = 0;
|
||||||
int standby = 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() {
|
void sched_init() {
|
||||||
// TODO: It may be good to implement heap memory to save space.
|
// 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;
|
proc->regs.ss = 0x30;
|
||||||
}
|
}
|
||||||
else if (flags == SCHED_USER_PROCESS) {
|
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.ss = 0x3B;
|
||||||
}
|
}
|
||||||
proc->regs.rflags = 0x202; // Enable interrupts
|
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++;
|
current_pid++;
|
||||||
|
|
||||||
|
map_range_to_pagemap(pm, vmm_kernel_pm, 0x1000, 0x10000, VMM_PRESENT | VMM_WRITABLE | VMM_USER);
|
||||||
|
|
||||||
if (standby) {
|
if (standby) {
|
||||||
// Disable standby mode as there's actually something to
|
// Disable standby mode as there's actually something to
|
||||||
// run, now.
|
// run, now.
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "sys/arch/x86_64/pic.h"
|
#include "sys/arch/x86_64/pic.h"
|
||||||
#include "sys/arch/x86_64/rtc.h"
|
#include "sys/arch/x86_64/rtc.h"
|
||||||
#include "sys/log.h"
|
#include "sys/log.h"
|
||||||
|
#include "sys/syscall.h"
|
||||||
//#include "sys/sched.h"
|
//#include "sys/sched.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <sys/arch/x86_64/idt.h>
|
#include <sys/arch/x86_64/idt.h>
|
||||||
|
@ -71,10 +72,7 @@ void exception_handler(registers_t *regs) {
|
||||||
}
|
}
|
||||||
else if (regs->int_no == 0x80)
|
else if (regs->int_no == 0x80)
|
||||||
{
|
{
|
||||||
log("syscall - Hello World! Current process: %s\n", curr_proc->name);
|
syscall_handle(regs);
|
||||||
if (curr_proc->flags == SCHED_USER_PROCESS)
|
|
||||||
log("syscall - Btw we made it to userspace, baby!\n", curr_proc->name);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
//logln(info, "arch/ints", "Received interrupt %d\n", regs->int_no);
|
//logln(info, "arch/ints", "Received interrupt %d\n", regs->int_no);
|
||||||
pic_ack(regs->int_no - 32);
|
pic_ack(regs->int_no - 32);
|
||||||
|
|
48
kernel/src/sys/syscall.c
Normal file
48
kernel/src/sys/syscall.c
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#include "sched/sched.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <sys/log.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
18
kernel/src/sys/syscall.h
Normal file
18
kernel/src/sys/syscall.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sys/arch/x86_64/idt.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/// 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();
|
13
kernel/src/sys/uname.h
Normal file
13
kernel/src/sys/uname.h
Normal file
|
@ -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;
|
|
@ -1,20 +1,23 @@
|
||||||
|
OUTPUT_FORMAT("elf64-x86-64")
|
||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
SECTIONS {
|
SECTIONS {
|
||||||
. = 0x1000;
|
. = 0x100000;
|
||||||
|
|
||||||
.text : {
|
.text : {
|
||||||
*(.text*)
|
*(.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
.rodata : {
|
.rodata : {
|
||||||
*(.rodata*)
|
*(.rodata)
|
||||||
}
|
}
|
||||||
|
|
||||||
.data : {
|
.data : {
|
||||||
*(.data*)
|
*(.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
.bss : {
|
.bss : {
|
||||||
*(.bss*)
|
*(COMMON)
|
||||||
*(COMMON)
|
*(.bss)
|
||||||
}
|
}
|
||||||
}
|
}
|
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue