From 33ccfd0c2f87e76a8302ad40c4ce145106b849b7 Mon Sep 17 00:00:00 2001 From: sbosse Date: Mon, 14 Oct 2024 23:08:11 +0200 Subject: [PATCH] Mon 14 Oct 23:06:38 CEST 2024 --- kernel/process.c | 561 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 561 insertions(+) create mode 100644 kernel/process.c diff --git a/kernel/process.c b/kernel/process.c new file mode 100644 index 0000000..2d3201f --- /dev/null +++ b/kernel/process.c @@ -0,0 +1,561 @@ +/* +Copyright (C) 2015-2019 The University of Notre Dame +This software is distributed under the GNU General Public License. +See the file LICENSE for details. +*/ + +#include "process.h" +#include "kobject.h" +#include "page.h" +#include "string.h" +#include "list.h" +#include "x86.h" +#include "interrupt.h" +#include "memorylayout.h" +#include "kmalloc.h" +#include "kernel/types.h" +#include "kernelcore.h" +#include "main.h" +#include "keyboard.h" +#include "clock.h" + +struct process *current = 0; +struct list ready_list = { 0, 0 }; +struct list grave_list = { 0, 0 }; +struct list grave_watcher_list = { 0, 0 }; // parent processes are put here to wait for their children +struct process *process_table[PROCESS_MAX_PID] = { 0 }; + +void process_init() +{ + current = process_create(); + + pagetable_load(current->pagetable); + pagetable_enable(); + + current->state = PROCESS_STATE_READY; + + current->waiting_for_child_pid = 0; +} + +void process_kstack_reset(struct process *p, unsigned entry_point) +{ + struct x86_stack *s; + + p->state = PROCESS_STATE_CRADLE; + + s = (struct x86_stack *) p->kstack_ptr; + + s->regs2.ebp = (uint32_t) (p->kstack_ptr + 28); + s->old_ebp = (uint32_t) (p->kstack_ptr + 32); + s->old_eip = (unsigned) intr_return; + s->fs = 0; + s->gs = 0; + s->es = X86_SEGMENT_USER_DATA; + s->ds = X86_SEGMENT_USER_DATA; + s->cs = X86_SEGMENT_USER_CODE; + s->eip = entry_point; + s->eflags.interrupt = 1; + s->eflags.iopl = 3; + s->esp = PROCESS_STACK_INIT; + s->ss = X86_SEGMENT_USER_DATA; +} + +void process_kstack_copy(struct process *parent, struct process *child) +{ + child->kstack_top = child->kstack + PAGE_SIZE - 8; + child->kstack_ptr = child->kstack_top - sizeof(struct x86_stack); + + struct x86_stack *child_regs = (struct x86_stack *) child->kstack_ptr; + struct x86_stack *parent_regs = (struct x86_stack *) (parent->kstack_top - sizeof(struct x86_stack)); + + *child_regs = *parent_regs; + + child_regs->regs2.ebp = (uint32_t) (child->kstack_ptr + 28); + child_regs->old_ebp = (uint32_t) (child->kstack_ptr + 32); + child_regs->old_eip = (unsigned) intr_return; + child_regs->regs1.eax = 0; +} + +/* +Valid pids start at 1 and go to PROCESS_MAX_PID. +To avoid confusion, keep picking increasing +pids until it is necessary to wrap around. +"last" is the most recently selected pid. +*/ + +static int process_allocate_pid() +{ + static int last = 0; + + int i; + + for(i = last + 1; i < PROCESS_MAX_PID; i++) { + if(!process_table[i]) { + last = i; + return i; + } + } + + for(i = 1; i < last; i++) { + if(!process_table[i]) { + last = i; + return i; + } + } + + return 0; +} + +void process_selective_inherit(struct process *parent, struct process *child, int * fds, int length) +{ + int i; + + for (i=0;i-1) { + child->ktable[i] = kobject_copy(parent->ktable[fds[i]]); + } else { + child->ktable[i] = 0; + } + } + + child->ppid = parent->pid; +} + +void process_inherit(struct process *parent, struct process *child) +{ + /* Child inherits everything parent inherits */ + int i; + int * fds = kmalloc(sizeof(int)*PROCESS_MAX_OBJECTS); + for (i = 0; i < PROCESS_MAX_OBJECTS; i++) + { + if (parent->ktable[i]) { + fds[i] = i; + } else { + fds[i] = -1; + } + } + process_selective_inherit(parent, child, fds, PROCESS_MAX_OBJECTS); + kfree(fds); +} + +int process_data_size_set(struct process *p, unsigned size) +{ + // XXX check valid ranges + // XXX round up to page size + + if(size % PAGE_SIZE) { + size += (PAGE_SIZE - size % PAGE_SIZE); + } + + if(size > p->vm_data_size) { + uint32_t start = PROCESS_ENTRY_POINT + p->vm_data_size; + pagetable_alloc(p->pagetable, start, size, PAGE_FLAG_USER | PAGE_FLAG_READWRITE | PAGE_FLAG_CLEAR); + } else if(size < p->vm_data_size) { + uint32_t start = PROCESS_ENTRY_POINT + size; + pagetable_free(p->pagetable, start, p->vm_data_size); + } else { + // requested size is equal to current. + } + + p->vm_data_size = size; + pagetable_refresh(); + + return 0; +} + +int process_stack_size_set(struct process *p, unsigned size) +{ + // XXX check valid ranges + // XXX round up to page size + + if(size > p->vm_stack_size) { + uint32_t start = -size; + pagetable_alloc(p->pagetable, start, size - p->vm_stack_size, PAGE_FLAG_USER | PAGE_FLAG_READWRITE | PAGE_FLAG_CLEAR); + } else { + uint32_t start = -p->vm_stack_size; + pagetable_free(p->pagetable, start, p->vm_stack_size - size); + } + + p->vm_stack_size = size; + pagetable_refresh(); + + return 0; +} + +void process_stack_reset(struct process *p, unsigned size) +{ + process_stack_size_set(p, size); + memset((void *) -size, size, 0); +} + +struct process *process_create() +{ + struct process *p; + + p = page_alloc(1); + + p->pid = process_allocate_pid(); + process_table[p->pid] = p; + + p->pagetable = pagetable_create(); + pagetable_init(p->pagetable); + + p->vm_data_size = 0; + p->vm_stack_size = 0; + + process_data_size_set(p, 2 * PAGE_SIZE); + process_stack_size_set(p, 2 * PAGE_SIZE); + + p->kstack = page_alloc(1); + p->kstack_top = p->kstack + PAGE_SIZE - 8; + p->kstack_ptr = p->kstack_top - sizeof(struct x86_stack); + + process_kstack_reset(p, PROCESS_ENTRY_POINT); + + // XXX table should be allocated + int i; + for(i = 0; i < PROCESS_MAX_OBJECTS; i++) { + p->ktable[i] = 0; + } + + p->state = PROCESS_STATE_READY; + + return p; +} + +void process_delete(struct process *p) +{ + int i; + for(i = 0; i < PROCESS_MAX_OBJECTS; i++) { + if(p->ktable[i]) { + kobject_close(p->ktable[i]); + } + } + pagetable_delete(p->pagetable); + page_free(p->kstack); + page_free(p); + process_table[p->pid] = 0; +} + +void process_launch(struct process *p) +{ + list_push_tail(&ready_list, &p->node); +} + +static void process_switch(int newstate) +{ + interrupt_block(); + + if(current) { + if(current->state != PROCESS_STATE_CRADLE) { + asm("pushl %ebp"); + asm("pushl %edi"); + asm("pushl %esi"); + asm("pushl %edx"); + asm("pushl %ecx"); + asm("pushl %ebx"); + asm("pushl %eax"); + asm("movl %%esp, %0":"=r"(current->kstack_ptr)); + } + + interrupt_stack_pointer = (void *) INTERRUPT_STACK_TOP; + current->state = newstate; + + if(newstate == PROCESS_STATE_READY) { + list_push_tail(&ready_list, ¤t->node); + } + if(newstate == PROCESS_STATE_GRAVE) { + list_push_tail(&grave_list, ¤t->node); + } + } + + current = 0; + + while(1) { + current = (struct process *) list_pop_head(&ready_list); + if(current) + break; + + interrupt_unblock(); + interrupt_wait(); + interrupt_block(); + } + + current->state = PROCESS_STATE_RUNNING; + interrupt_stack_pointer = current->kstack_top; + + asm("movl %0, %%cr3"::"r"(current->pagetable)); + asm("movl %0, %%esp"::"r"(current->kstack_ptr)); + + asm("popl %eax"); + asm("popl %ebx"); + asm("popl %ecx"); + asm("popl %edx"); + asm("popl %esi"); + asm("popl %edi"); + asm("popl %ebp"); + + interrupt_unblock(); +} + +int allow_preempt = 0; + +void process_preempt() +{ + if(allow_preempt && current && ready_list.head) { + process_switch(PROCESS_STATE_READY); + } +} + +void process_yield() +{ + /* no-op if process module not yet initialized. */ + if(!current) return; + process_switch(PROCESS_STATE_READY); +} + +void process_exit(int code) +{ + // printf("process %d exiting with status %d...\n", current->pid, code); --> transport to kshell run + current->exitcode = code; + current->exitreason = PROCESS_EXIT_NORMAL; + process_wakeup_parent(&grave_watcher_list); // On exit, wake up parent if need be + process_switch(PROCESS_STATE_GRAVE); +} + +void process_wait(struct list *q) +{ + list_push_tail(q, ¤t->node); + process_switch(PROCESS_STATE_BLOCKED); +} + +void process_wakeup(struct list *q) +{ + struct process *p; + p = (struct process *) list_pop_head(q); + if(p) { + p->state = PROCESS_STATE_READY; + list_push_tail(&ready_list, &p->node); + } +} + +void process_reap_all() +{ + struct process *p; + while((p = (struct process *) list_pop_head(&grave_list))) { + process_delete(p); + } +} + +/* Wakes up parent off of the corresponding list*/ +void process_wakeup_parent(struct list *q) +{ + struct process *p = (struct process *) q->head; + // Loop through all the waiting parents to see if one needs to be woken up + while(p) { + if(p->pid == current->ppid && (p->waiting_for_child_pid == 0 || p->waiting_for_child_pid == current->pid)) { + p->state = PROCESS_STATE_READY; + p->waiting_for_child_pid = 0; + list_remove(&p->node); + list_push_tail(&ready_list, &p->node); + break; + } + p = (struct process *) (&p->node)->next; + } +} + +void process_wakeup_all(struct list *q) +{ + struct process *p; + while((p = (struct process *) list_pop_head(q))) { + p->state = PROCESS_STATE_READY; + list_push_tail(&ready_list, &p->node); + } +} + +void process_dump(struct process *p) +{ + struct x86_stack *s = (struct x86_stack *) (INTERRUPT_STACK_TOP - sizeof(*s)); + printf("kstack: %x\n", p->kstack); + printf("stackp: %x\n", p->kstack_ptr); + printf("eax: %x cs: %x\n", s->regs1.eax, s->cs); + printf("ebx: %x ds: %x\n", s->regs1.ebx, s->ds); + printf("ecx: %x ss: %x\n", s->regs1.ecx, s->ss); + printf("edx: %x eflags: %x\n", s->regs1.edx, s->eflags); + printf("esi: %x\n", s->regs1.esi); + printf("edi: %x\n", s->regs1.edi); + printf("ebp: %x\n", s->regs1.ebp); + printf("esp: %x\n", s->esp); + printf("eip: %x\n", s->eip); +} + +int process_available_fd(struct process *p) +{ + struct kobject **fdtable = current->ktable; + int i; + for(i = 0; i < PROCESS_MAX_OBJECTS; i++) { + if(fdtable[i] == 0) + return i; + } + return -1; +} + +int process_object_max(struct process *p) +{ + struct kobject **fdtable = current->ktable; + int i; + // Because of 0-indexing, PROCESS_MAX_OBJECTS is the size and + // therefor 1 offset, don't look for 0 there. + for(i = PROCESS_MAX_OBJECTS - 1; i > -1; i--) { + if(fdtable[i] != 0) + return i; + } + return -1; +} + +void process_make_dead(struct process *dead) +{ + int i; + for(i = 0; i < PROCESS_MAX_PID; i++) { + if(process_table[i] && process_table[i]->ppid == dead->pid) { + process_make_dead(process_table[i]); + } + } + dead->exitcode = 0; + dead->exitreason = PROCESS_EXIT_KILLED; + if(dead == current) { + process_switch(PROCESS_STATE_GRAVE); + } else { + list_remove(&dead->node); + list_push_tail(&grave_list, &dead->node); + } +} + +int process_kill(uint32_t pid) +{ + if(pid > 0 && pid <= PROCESS_MAX_PID) { + struct process *dead = process_table[pid]; + if(dead) { + printf("process killed\n"); + process_make_dead(dead); + return 0; + } else { + return 1; + } + } else { + return 1; + } +} + +int process_wait_child(uint32_t pid, struct process_info *info, int timeout) +{ + clock_t start, elapsed; + uint32_t total; + + if(!info) + return -1; + + start = clock_read(); + + do { + struct process *p = (struct process *) (grave_list.head); + while(p) { + struct process *next = (struct process *) p->node.next; + if((pid != 0 && p->pid == pid) || (p->ppid == current->pid)) { + info->exitcode = p->exitcode; + info->exitreason = p->exitreason; + info->pid = p->pid; + return p->pid; + } + p = next; + } + + current->waiting_for_child_pid = pid; + process_wait(&grave_watcher_list); + + elapsed = clock_diff(start, clock_read()); + total = elapsed.millis + elapsed.seconds * 1000; + } while(total < timeout || timeout < 0); + + return 0; +} + +int process_reap(uint32_t pid) +{ + struct process *p = (struct process *) (grave_list.head); + while(p) { + struct process *next = (struct process *) p->node.next; + if(p->pid == pid) { + list_remove(&p->node); + process_delete(p); + return 0; + } + p = next; + } + return 1; +} + +/* +process_pass_arguments set up the current process stack +so that it contains a copy of p->args and p->args_length +at the very top, serving as the initial arguments to the entry function: +void entry( const char *args, int args_length ); +*/ + +#define PUSH_INTEGER( value ) esp -= sizeof(int); *((int*)esp)=(int)(value); + +void process_pass_arguments(struct process *p, int argc, char **argv) +{ + /* Get the default stack pointer position. */ + char *esp = (char *) PROCESS_STACK_INIT; + + /* Make a local array to keep track of user addresses. */ + char **addr_of_argv = kmalloc(sizeof(char *) * argc); + + /* For each argument, in reverse order: */ + int i; + for(i = (argc - 1); i >= 0; i--) { + /* Size is strlen plus null terminator, integer aligned. */ + int length = strlen(argv[i]) + 1; + if(length % 4) { + length += (4 - length % 4); + } + esp -= length; + + /* Push length bytes onto the stack. */ + memcpy(esp, argv[i], length); + + /* Remember that address for later */ + addr_of_argv[i] = esp; + } + + /* Push each item of argc, in reverse order. */ + for(i = (argc - 1); i >= 0; i--) { + PUSH_INTEGER(addr_of_argv[i]); + } + + /* Keep the address of the array start. */ + const char *addr_of_addr_of_argv = esp; + + /* Push the arguments to main */ + PUSH_INTEGER(addr_of_addr_of_argv); + PUSH_INTEGER(argc); + + /* Final stack pointer should be below the last item. */ + esp -= 4; + + /* Set the starting stack pointer on the kstack to this new value */ + struct x86_stack *s = (struct x86_stack *) p->kstack_ptr; + s->esp = (int) (esp); + + kfree(addr_of_argv); +} + +int process_stats(int pid, struct process_stats *s) +{ + if(pid > PROCESS_MAX_PID || !process_table[pid]) { + return 1; + } + *s = process_table[pid]->stats; + return 0; +}