/* 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; }