elf: working loader / syscall: introduce the syscall api.

This commit is contained in:
RaphProductions 2025-05-09 12:19:32 +02:00
parent 2d4031eacc
commit 5afe37a798
12 changed files with 267 additions and 37 deletions

View file

@ -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 <stdint.h>
#include <sys/log.h>
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;
}

View file

@ -1,5 +1,6 @@
#pragma once
#include "exec/exec.h"
#include <stdint.h>
// ELF magic.
@ -115,4 +116,10 @@ typedef struct {
uint64_t p_align;
} 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
View 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;

View file

@ -1 +1,27 @@
#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;

View file

@ -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 <sys/log.h>
#include <stdint.h>
#include <stddef.h>
@ -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)

View file

@ -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;
}

View file

@ -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 <stdint.h>
#include <sys/arch/x86_64/idt.h>
@ -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);

48
kernel/src/sys/syscall.c Normal file
View 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
View 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
View 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;