Rewrite fork/clone.

This is still a bit ugly, needs cleanup.
Fixes that weird GCC issue.
This commit is contained in:
Kevin Lange 2013-06-06 18:04:13 -07:00
parent e70ebf8857
commit 0f344f2900
6 changed files with 110 additions and 150 deletions

View File

@ -5,28 +5,10 @@
#include <system.h>
#include <logging.h>
/*
* XXX XXX XXX XXX
* This file cointains but one function - and it doesn't work in gcc except when compiled -O2!
* It is a deep and dark mystery, and we will work around it with some very terrible macros and
* attributes and you should probably just ignore it entirely.
*
* If you figure out why the output from GCC 4.6.0 (or any other gcc for that matter) fails
* except when using -O2 (the optimize(2) below) and you are not me, then please do let me know
* and I will actually send you a cookie (no guarantees on where the cookie came from, but
* it probably won't be me, I can't bake worth shit.)
* XXX XXX XXX XXX
*/
irq_handler_t isrs_routines[256];
extern void fault_error(struct regs *r);
void
#ifndef __clang__
/* I'm sorry, I'm going to assume gcc here. */
__attribute__((optimize(2)))
#endif
fault_handler(struct regs *r) {
void fault_handler(struct regs *r) {
irq_handler_t handler;
handler = isrs_routines[r->int_no];
if (handler) {

View File

@ -49,6 +49,8 @@ extern void tss_flush(void);
extern void spin_lock(uint8_t volatile * lock);
extern void spin_unlock(uint8_t volatile * lock);
extern void return_to_userspace(void);
/* Kernel Main */
extern int max(int,int);
extern int min(int,int);

View File

@ -183,16 +183,13 @@ system(
int argc, /* Argument count (ie, /bin/echo hello world = 3) */
char ** argv /* Argument strings (including executable path) */
) {
int child = fork();
if (child == 0) {
char * env[] = {NULL};
exec(path,argc,argv,env);
debug_print(ERROR, "Failed to execute process!");
kexit(-1);
return -1;
} else {
switch_next();
return -1;
}
char * env[] = {NULL};
set_process_environment((process_t*)current_process, clone_directory(current_directory));
current_directory = current_process->thread.page_directory;
switch_page_directory(current_directory);
exec(path,argc,argv,env);
debug_print(ERROR, "Failed to execute process!");
kexit(-1);
return -1;
}

View File

@ -84,6 +84,17 @@ idt_load:
lidt [idtp]
ret
; Return to Userspace (from thread creation)
global return_to_userspace
return_to_userspace:
pop gs
pop fs
pop es
pop ds
popa
add esp, 8
iret
; Interrupt Service Routines
%macro ISR_NOERR 1
global _isr%1

View File

@ -158,7 +158,7 @@ process_t * spawn_init(void) {
/* Set its tree entry pointer so we can keep track
* of the process' entry in the process tree. */
init->tree_entry = process_tree->root;
init->id = 0; /* Init is PID 1 */
init->id = 1; /* Init is PID 1 */
init->group = 0;
init->name = strdup("init"); /* Um, duh. */
init->cmdline = NULL;
@ -201,8 +201,12 @@ process_t * spawn_init(void) {
init->sleep_node.next = NULL;
init->sleep_node.value = init;
set_process_environment(init, current_directory);
/* What the hey, let's also set the description on this one */
init->description = strdup("[init]");
list_insert(process_list, (void *)init);
return init;
}
@ -213,7 +217,7 @@ process_t * spawn_init(void) {
*/
pid_t get_next_pid(void) {
/* Terribly naïve, I know, but it works for now */
static pid_t next = 1;
static pid_t next = 2;
return (next++);
}

View File

@ -13,6 +13,9 @@
uint32_t next_pid = 0;
#define PUSH(stack, type, item) stack -= sizeof(type); \
*((type *) stack) = item
/*
* Clone a page directory and its contents.
* (If you do not intend to clone the contents, do it yourself!)
@ -173,7 +176,9 @@ void tasking_install(void) {
/* Spawn the initial process */
current_process = spawn_init();
/* Initialize the paging environment */
#if 0
set_process_environment((process_t *)current_process, current_directory);
#endif
/* Switch to the kernel directory */
switch_page_directory(current_process->thread.page_directory);
@ -189,8 +194,7 @@ void tasking_install(void) {
uint32_t fork(void) {
IRQ_OFF;
unsigned int magic = TASK_MAGIC;
uintptr_t esp, ebp, eip;
uintptr_t esp, ebp;
current_process->syscall_registers->eax = 0;
@ -207,70 +211,52 @@ uint32_t fork(void) {
assert(new_proc && "Could not allocate a new process!");
/* Set the new process' page directory to clone */
set_process_environment(new_proc, directory);
/* Read the instruction pointer */
eip = read_eip();
if (current_process == parent) {
/* Returned as the parent */
/* Verify magic */
assert(magic == TASK_MAGIC && "Bad process fork magic (parent)!");
/* Collect the stack and base pointers */
asm volatile ("mov %%esp, %0" : "=r" (esp));
asm volatile ("mov %%ebp, %0" : "=r" (ebp));
/* Calculate new ESP and EBP for the child process */
if (current_process->image.stack > new_proc->image.stack) {
new_proc->thread.esp = esp - (current_process->image.stack - new_proc->image.stack);
new_proc->thread.ebp = ebp - (current_process->image.stack - new_proc->image.stack);
} else {
new_proc->thread.esp = esp + (new_proc->image.stack - current_process->image.stack);
new_proc->thread.ebp = ebp - (current_process->image.stack - new_proc->image.stack);
}
/* Copy the kernel stack from this process to new process */
memcpy((void *)(new_proc->image.stack - KERNEL_STACK_SIZE), (void *)(current_process->image.stack - KERNEL_STACK_SIZE), KERNEL_STACK_SIZE);
struct regs r;
memcpy(&r, current_process->syscall_registers, sizeof(struct regs));
new_proc->syscall_registers = &r;
/* Move the syscall_registers pointer */
uintptr_t o_stack = ((uintptr_t)current_process->image.stack - KERNEL_STACK_SIZE);
uintptr_t n_stack = ((uintptr_t)new_proc->image.stack - KERNEL_STACK_SIZE);
uintptr_t offset = ((uintptr_t)current_process->syscall_registers - o_stack);
new_proc->syscall_registers = (struct regs *)(n_stack + offset);
esp = new_proc->image.stack;
ebp = esp;
/* Set the new process instruction pointer (to the return from read_eip) */
new_proc->thread.eip = eip;
new_proc->syscall_registers->eax = 0;
/* Clear page table tie-ins for shared memory mappings */
PUSH(esp, struct regs, r);
new_proc->thread.esp = esp;
new_proc->thread.ebp = ebp;
new_proc->thread.eip = (uintptr_t)&return_to_userspace;
/* Clear page table tie-ins for shared memory mappings */
#if 0
assert((new_proc->shm_mappings->length == 0) && "Spawned process had shared memory mappings!");
foreach (n, current_process->shm_mappings) {
shm_mapping_t * mapping = (shm_mapping_t *)n->value;
assert((new_proc->shm_mappings->length == 0) && "Spawned process had shared memory mappings!");
foreach (n, current_process->shm_mappings) {
shm_mapping_t * mapping = (shm_mapping_t *)n->value;
for (uint32_t i = 0; i < mapping->num_vaddrs; i++) {
/* Get the vpage address (it's the same for the cloned directory)... */
uintptr_t vpage = mapping->vaddrs[i];
assert(!(vpage & 0xFFF) && "shm_mapping_t contained a ptr to the middle of a page (bad)");
for (uint32_t i = 0; i < mapping->num_vaddrs; i++) {
/* Get the vpage address (it's the same for the cloned directory)... */
uintptr_t vpage = mapping->vaddrs[i];
assert(!(vpage & 0xFFF) && "shm_mapping_t contained a ptr to the middle of a page (bad)");
/* ...and from that, the cloned dir's page entry... */
page_t * page = get_page(vpage, 0, new_proc->thread.page_directory);
assert(test_frame(page->frame * 0x1000) && "ptr wasn't mapped in?");
/* ...and from that, the cloned dir's page entry... */
page_t * page = get_page(vpage, 0, new_proc->thread.page_directory);
assert(test_frame(page->frame * 0x1000) && "ptr wasn't mapped in?");
/* ...which refers to a bogus frame that we don't want. */
clear_frame(page->frame * 0x1000);
memset(page, 0, sizeof(page_t));
}
/* ...which refers to a bogus frame that we don't want. */
clear_frame(page->frame * 0x1000);
memset(page, 0, sizeof(page_t));
}
}
#endif
/* Add the new process to the ready queue */
make_process_ready(new_proc);
/* Add the new process to the ready queue */
make_process_ready(new_proc);
IRQ_RES;
IRQ_RES;
/* Return the child PID */
return new_proc->id;
} else {
assert(magic == TASK_MAGIC && "Bad process fork magic (child)!");
/* Child fork is complete, return */
return 0;
}
/* Return the child PID */
return new_proc->id;
}
/*
@ -279,8 +265,7 @@ uint32_t fork(void) {
*/
uint32_t
clone(uintptr_t new_stack, uintptr_t thread_func, uintptr_t arg) {
unsigned int magic = TASK_MAGIC;
uintptr_t esp, ebp, eip;
uintptr_t esp, ebp;
IRQ_OFF;
@ -297,71 +282,53 @@ clone(uintptr_t new_stack, uintptr_t thread_func, uintptr_t arg) {
set_process_environment(new_proc, directory);
directory->ref_count++;
/* Read the instruction pointer */
eip = read_eip();
if (current_process == parent) {
/* Returned as the parent */
/* Verify magic */
assert(magic == TASK_MAGIC && "Bad process fork magic (parent clone)!");
/* Collect the stack and base pointers */
asm volatile ("mov %%esp, %0" : "=r" (esp));
asm volatile ("mov %%ebp, %0" : "=r" (ebp));
/* Calculate new ESP and EBP for the child process */
if (current_process->image.stack > new_proc->image.stack) {
new_proc->thread.esp = esp - (current_process->image.stack - new_proc->image.stack);
new_proc->thread.ebp = ebp - (current_process->image.stack - new_proc->image.stack);
} else {
new_proc->thread.esp = esp + (new_proc->image.stack - current_process->image.stack);
new_proc->thread.ebp = ebp - (current_process->image.stack - new_proc->image.stack);
}
/* Copy the kernel stack from this process to new process */
memcpy((void *)(new_proc->image.stack - KERNEL_STACK_SIZE), (void *)(current_process->image.stack - KERNEL_STACK_SIZE), KERNEL_STACK_SIZE);
struct regs r;
memcpy(&r, current_process->syscall_registers, sizeof(struct regs));
new_proc->syscall_registers = &r;
/* Move the syscall_registers pointer */
uintptr_t o_stack = ((uintptr_t)current_process->image.stack - KERNEL_STACK_SIZE);
uintptr_t n_stack = ((uintptr_t)new_proc->image.stack - KERNEL_STACK_SIZE);
uintptr_t offset = ((uintptr_t)current_process->syscall_registers - o_stack);
new_proc->syscall_registers = (struct regs *)(n_stack + offset);
esp = new_proc->image.stack;
ebp = esp;
/* Set the gid */
if (current_process->group) {
new_proc->group = current_process->group;
} else {
/* We are the session leader */
new_proc->group = current_process->id;
}
new_proc->syscall_registers->ebp = new_stack;
new_proc->syscall_registers->eip = thread_func;
/* Push arg, bogus return address onto the new thread's stack */
new_stack -= sizeof(uintptr_t);
*((uintptr_t *)new_stack) = arg;
new_stack -= sizeof(uintptr_t);
*((uintptr_t *)new_stack) = THREAD_RETURN;
/* Set esp, ebp, and eip for the new thread */
new_proc->syscall_registers->esp = new_stack;
new_proc->syscall_registers->useresp = new_stack;
free(new_proc->fds);
new_proc->fds = current_process->fds;
new_proc->fds->refs++;
/* Set the new process instruction pointer (to the return from read_eip) */
new_proc->thread.eip = eip;
/* Add the new process to the ready queue */
make_process_ready(new_proc);
IRQ_RES;
/* Return the child PID */
return new_proc->id;
/* Set the gid */
if (current_process->group) {
new_proc->group = current_process->group;
} else {
assert(magic == TASK_MAGIC && "Bad process clone magic (child clone)!");
/* Child fork is complete, return */
return 0;
/* We are the session leader */
new_proc->group = current_process->id;
}
new_proc->syscall_registers->ebp = new_stack;
new_proc->syscall_registers->eip = thread_func;
/* Push arg, bogus return address onto the new thread's stack */
new_stack -= sizeof(uintptr_t);
*((uintptr_t *)new_stack) = arg;
new_stack -= sizeof(uintptr_t);
*((uintptr_t *)new_stack) = THREAD_RETURN;
/* Set esp, ebp, and eip for the new thread */
new_proc->syscall_registers->esp = new_stack;
new_proc->syscall_registers->useresp = new_stack;
PUSH(esp, struct regs, r);
new_proc->thread.esp = esp;
new_proc->thread.ebp = ebp;
free(new_proc->fds);
new_proc->fds = current_process->fds;
new_proc->fds->refs++;
new_proc->thread.eip = (uintptr_t)&return_to_userspace;
/* Add the new process to the ready queue */
make_process_ready(new_proc);
IRQ_RES;
/* Return the child PID */
return new_proc->id;
}
/*
@ -499,9 +466,6 @@ void switch_next(void) {
}
#define PUSH(stack, type, item) stack -= sizeof(type); \
*((type *) stack) = item
/*
* Enter ring 3 and jump to `location`.
*