newland/arch/x86/src/proc.c
/**
* NewLand Kernel - (C) 2019 Tristan Ross
*/
#include <libfile/elf.h>
#include <newland/arch/fpu.h>
#include <newland/arch/irq.h>
#include <newland/arch/misc.h>
#include <newland/arch/proc.h>
#include <newland/dev/tty.h>
#include <newland/alloc.h>
#include <newland/log.h>
#include <newland/errno.h>
#include <signal.h>
SLIST_HEAD(proc_list, proc_t);
struct proc_list processes;
static pid_t next_pid = 1;
static pid_t curr_pid = 0;
static size_t proc_count = 0;
static uint32_t ticks = 0;
static int sched_rec[SCHED_RECCOUNT] = { 0 };
size_t process_count() {
return proc_count;
}
proc_t* process_get(size_t i) {
proc_t* proc = NULL;
size_t index = 0;
SLIST_FOREACH(proc, &processes, proc_list) {
if (index == i) return proc;
index++;
}
return NULL;
}
proc_t* process_frompid(pid_t pid) {
proc_t* proc = NULL;
SLIST_FOREACH(proc, &processes, proc_list) {
if (proc->id == pid) return proc;
}
return NULL;
}
proc_t* process_curr() {
if (proc_count == 0) return NULL;
if (curr_pid == 0) return NULL;
return process_frompid(curr_pid);
}
proc_t* process_next() {
if (proc_count == 0) return NULL;
if (curr_pid == 0) return NULL;
proc_t* curr_proc = process_frompid(curr_pid);
if (curr_proc == NULL) return SLIST_FIRST(&processes);
if (SLIST_NEXT(curr_proc, proc_list) != NULL) return SLIST_NEXT(curr_proc, proc_list);
return SLIST_FIRST(&processes);
}
proc_t* proc_create(proc_t* parent, const char* name, int isuser) {
proc_t* proc = kmalloc(sizeof(proc_t));
if (proc == NULL) return NULL;
uint32_t irqflgs = irq_disable();
memset(proc, 0, sizeof(proc_t));
int parent_index = -1;
if (parent != NULL) {
for (size_t i = 0; i < parent->child_count; i++) {
if (parent->child[i] == 0) {
parent_index = i;
break;
}
}
if (parent_index == -1) {
if (parent->child_count + 1 > CHILD_MAX) {
errno = -NEWLAND_ECHILD;
kfree(proc);
irq_restore(irqflgs);
return NULL;
}
parent_index = parent->child_count++;
}
}
proc->id = next_pid++;
strncpy((char*)proc->name, name, 256);
proc->status = PROC_READY;
proc->isuser = isuser;
if (parent == NULL) {
strncpy((char*)proc->cwd, "/", PATH_MAX);
if (tty_get(0) != NULL) strncpy((char*)proc->tty, tty_get(0)->name, NAME_MAX);
proc->gid = proc->uid = proc->parent = 0;
} else {
strncpy((char*)proc->cwd, parent->cwd, PATH_MAX);
proc->gid = parent->gid;
proc->uid = parent->uid;
proc->parent = parent->id;
parent->child[parent_index] = proc->id;
strncpy((char*)proc->tty, parent->tty, NAME_MAX);
}
for (size_t i = 0; i < OPEN_MAX; i++) {
proc->fd[i].node = NULL;
}
proc->regs.esp = (uint32_t)proc->stack + PROC_STACKSIZE;
if ((proc->isuser = isuser)) proc->regs.cr3 = (uint32_t)mem_alloc_pgdir();
else proc->regs.cr3 = (uint32_t)get_krnlpgdir();
fpu_savectx(proc);
SLIST_INSERT_HEAD(&processes, proc, proc_list);
proc_count++;
irq_restore(irqflgs);
if (proc_count == 1 && curr_pid == 0) curr_pid = proc->id;
return proc;
}
int proc_destroy(proc_t** procptr) {
uint32_t irqflgs = irq_disable();
proc_t* proc = *procptr;
for (size_t i = 0; i < OPEN_MAX; i++) {
if (proc->fd[i].node == NULL) continue;
fs_node_close(&proc->fd[i].node, &proc->fd[i]);
}
if ((page_dir_t*)proc->regs.cr3 != get_krnlpgdir()) mem_free_pgdir((page_dir_t*)proc->regs.cr3);
if (proc->parent != 0) {
proc_t* parent = process_frompid(proc->parent);
for (int i = 0; i < parent->child_count; i++) {
if (parent->child[i] == proc->id) {
parent->child[i] = 0;
parent->child_count--;
break;
}
}
}
kfree(proc);
SLIST_REMOVE(&processes, proc, proc_t, proc_list);
proc_count--;
irq_restore(irqflgs);
return 0;
}
int proc_pushstack(proc_t** procptr, const void* value, size_t size) {
proc_t* proc = *procptr;
proc->regs.esp -= size;
memcpy((void*)proc->regs.esp, value, size);
return proc->regs.esp;
}
page_dir_t* proc_switch_pgdir(proc_t** procptr, page_dir_t* pgdir) {
proc_t* proc = *procptr;
page_dir_t* old = (page_dir_t*)proc->regs.cr3;
proc->regs.cr3 = (uint32_t)pgdir;
paging_loaddir(pgdir);
return old;
}
int proc_sigenter(proc_t** procptr, uint8_t signum, void* data, size_t datasz) {
proc_t* proc = *procptr;
if (proc->issignaling) return -NEWLAND_EINPROGRESS;
if (proc->signal_handler == NULL) {
if (signum == SIGKILL || signum == SIGSEGV || signum == SIGSTKFLT || signum == SIGILL) {
proc->status = PROC_ZOMBIE;
return 0;
}
return -NEWLAND_ENOSYS;
}
proc->issignaling = 1;
proc->signum = signum;
if (datasz > 0 && data != NULL) proc_pushstack(procptr, data, datasz);
return 0;
}
int proc_sigleave(proc_t** procptr) {
proc_t* proc = *procptr;
if (!proc->issignaling) return -NEWLAND_EINTR;
proc->issignaling = 0;
proc->signum = 0;
return 0;
}
void processes_cleanup() {
proc_t* proc;
SLIST_FOREACH(proc, &processes, proc_list) {
if (proc->status == PROC_ZOMBIE) proc_destroy(&proc);
}
}
void schedule(regs_t* regs) {
if (proc_count == 0) return;
proc_t* curr_proc = process_frompid(curr_pid);
proc_t* next_proc = process_next();
if (curr_proc == NULL) return;
fpu_savectx(curr_proc);
sched_rec[ticks % SCHED_RECCOUNT] = curr_pid;
ticks++;
curr_proc->status = PROC_READY;
curr_pid = next_proc->id;
next_proc->status = PROC_RUNNING;
fpu_loadctx(next_proc);
printk(KLOG_INFO "proc: switching context %d\n", next_proc->id);
proc_regswitch(&curr_proc->regs, &next_proc->regs);
}
int proc_exec(const char* path, const char** argv) {
uint32_t eflags = irq_disable();
fs_node_t* node;
int r = fs_resolve(&node, path);
if (r < 0) {
irq_restore(eflags);
return r;
}
elf_header_t hdr;
r = fs_node_read(&node, 0, &hdr, sizeof(elf_header_t));
if (r < 0) {
irq_restore(eflags);
return r;
}
if (!(elf_isvalid(&hdr) && hdr.machine == 3)) {
irq_restore(eflags);
return -NEWLAND_EINVAL;
}
proc_t* curr = process_curr();
proc_t* proc = proc_create(curr, path, 1);
if (proc == NULL) {
irq_restore(eflags);
return errno;
}
proc->regs.eip = hdr.entry;
int argc = 0;
if (argv != NULL) {
while (argv[argc] != NULL) {
proc_pushstack(&proc, argv[argc], strlen((const char*)argv[argc]) + 1);
argc++;
}
}
proc_pushstack(&proc, &argc, sizeof(int));
elf_program_t prog;
for (int i = 0; i < hdr.phnum; i++) {
r = fs_node_read(&node, hdr.phoff + (hdr.phentsize * i), &prog, sizeof(elf_program_t));
if (r < 0) {
proc_destroy(&proc);
irq_restore(eflags);
return r;
}
if (prog.vaddr >= 0x100000) {
page_dir_t* oldpgdir = proc_switch_pgdir(&curr, (page_dir_t*)proc->regs.cr3);
paging_loaddir((page_dir_t*)proc->regs.cr3);
mem_map((page_dir_t*)proc->regs.cr3, prog.vaddr, PAGE_ALIGN_UP(prog.memsz) / PAGE_SIZE, 1, 1);
memset((void*)prog.vaddr, 0, prog.memsz);
r = fs_node_read(&node, prog.offset, (void*)prog.vaddr, prog.filesz);
if (r < 0) {
proc_destroy(&proc);
irq_restore(eflags);
return r;
}
proc_switch_pgdir(&curr, oldpgdir);
}
}
irq_restore(eflags);
return proc->id;
}
int sched_getusage(pid_t pid) {
int count = 0;
for (int i = 0; i < SCHED_RECCOUNT; i++) {
if (sched_rec[i] == pid) count++;
}
return count;
}
static void proc_handle_interrrupt(regs_t* regs) {
proc_t* curr_proc = process_curr();
if (curr_proc == NULL) {
printk(KLOG_ERR "received unhandled interrupt: %d\n", regs->err_code);
halt();
}
switch (regs->int_no) {
/* Invalid Opcode */
case 0x06:
proc_sigenter(&curr_proc, SIGILL, NULL, 0);
break;
/* FPU */
case 0x10:
proc_sigenter(&curr_proc, SIGFPE, NULL, 0);
break;
/* Stack Fault */
case 0x0C:
proc_sigenter(&curr_proc, SIGSTKFLT, NULL, 0);
break;
/* Page Fault */
case 0x0E:
proc_sigenter(&curr_proc, SIGSEGV, NULL, 0);
break;
}
}
void sched_init() {
register_int_handler(0x06, proc_handle_interrrupt);
register_int_handler(0x10, proc_handle_interrrupt);
register_int_handler(0x0C, proc_handle_interrrupt);
register_int_handler(0x0E, proc_handle_interrrupt);
register_irq_handler(0x00, schedule);
}