diff --git a/include/pcpu.h b/include/pcpu.h index 6e342c9957..5c0c6d5ac2 100644 --- a/include/pcpu.h +++ b/include/pcpu.h @@ -3,11 +3,13 @@ typedef struct thread thread_t; typedef struct pmap pmap_t; +typedef struct vm_map vm_map_t; typedef struct pcpu { thread_t *curthread; thread_t *idle_thread; pmap_t *curpmap; + vm_map_t *uspace; } pcpu_t; extern pcpu_t _pcpu_data[1]; diff --git a/include/proc.h b/include/proc.h index 639c8266bd..57a4a7d4ce 100644 --- a/include/proc.h +++ b/include/proc.h @@ -4,6 +4,7 @@ #include #include #include +#include typedef struct thread thread_t; typedef struct proc proc_t; @@ -14,9 +15,13 @@ typedef TAILQ_HEAD(, proc) proc_list_t; struct proc { mtx_t p_lock; /* Process lock */ TAILQ_ENTRY(proc) p_all; /* A link on all processes list */ + /* XXX: At the moment we don't support multiple threads in a single process! + */ + unsigned p_nthreads; thread_list_t p_threads; /* Threads belonging to this process */ pid_t p_pid; /* Process ID */ proc_t *p_parent; /* Parent process */ + vm_map_t *p_uspace; /* process' user space map */ /* file descriptors table */ fdtab_t *p_fdtable; }; diff --git a/include/thread.h b/include/thread.h index 90bd85dc68..6e87b9f912 100644 --- a/include/thread.h +++ b/include/thread.h @@ -48,7 +48,6 @@ typedef struct thread { intptr_t td_onfault; /* program counter for copyin/copyout faults */ vm_page_t *td_kstack_obj; stack_t td_kstack; - vm_map_t *td_uspace; /* thread's user space map */ /* waiting channel */ sleepq_t *td_sleepqueue; void *td_wchan; diff --git a/include/vm_map.h b/include/vm_map.h index 542b1ede21..42ae971bbf 100644 --- a/include/vm_map.h +++ b/include/vm_map.h @@ -42,7 +42,7 @@ typedef struct vm_map { * * vm_map_entry_t* vm_map_allocate_space(vm_map_t* map, size_t length) */ -vm_map_t *vm_map_activate(vm_map_t *map); +void vm_map_activate(vm_map_t *map); vm_map_t *get_user_vm_map(); vm_map_t *get_kernel_vm_map(); vm_map_t *get_active_vm_map_by_addr(vm_addr_t addr); diff --git a/mips/genassym.c b/mips/genassym.c index bdfba1bbea..dbc7f95dc1 100644 --- a/mips/genassym.c +++ b/mips/genassym.c @@ -2,18 +2,19 @@ #include #include #include +#include #include #include ASSYM(TDF_NEEDSWITCH, TDF_NEEDSWITCH); +ASSYM(TD_PROC, offsetof(thread_t, td_proc)); ASSYM(TD_UCTX, offsetof(thread_t, td_uctx)); ASSYM(TD_UCTX_FPU, offsetof(thread_t, td_uctx_fpu)); ASSYM(TD_KFRAME, offsetof(thread_t, td_kframe)); ASSYM(TD_KCTX, offsetof(thread_t, td_kctx)); ASSYM(TD_KSTACK, offsetof(thread_t, td_kstack)); ASSYM(TD_FLAGS, offsetof(thread_t, td_flags)); -ASSYM(TD_USPACE, offsetof(thread_t, td_uspace)); ASSYM(TD_ONFAULT, offsetof(thread_t, td_onfault)); ASSYM(STK_BASE, offsetof(stack_t, stk_base)); @@ -85,4 +86,6 @@ ASSYM(EXC_CAUSE, offsetof(exc_frame_t, cause)); ASSYM(EXC_FRAME_SIZ, sizeof(exc_frame_t) + CALLFRAME_SIZ); +ASSYM(P_USPACE, offsetof(proc_t, p_uspace)); + ASSYM(PCPU_CURTHREAD, offsetof(pcpu_t, curthread)); diff --git a/mips/switch.S b/mips/switch.S index 78cb681fc7..f2f91bcc46 100644 --- a/mips/switch.S +++ b/mips/switch.S @@ -39,8 +39,11 @@ ctx_resume: LOAD_PCPU($t0) sw $s0, PCPU_CURTHREAD($t0) - lw $a0, TD_USPACE($s0) - jal vm_map_activate + lw $a0, TD_PROC($s0) + beqz $a0, 1f # switching to kernel thread ? + nop + lw $a0, P_USPACE($a0) +1: jal vm_map_activate nop addu $a1, $s0, TD_KCTX diff --git a/sys/exec.c b/sys/exec.c index 83226d1cfd..6df848ad81 100644 --- a/sys/exec.c +++ b/sys/exec.c @@ -91,12 +91,35 @@ int do_exec(const exec_args_t *args) { return -ENOEXEC; } + thread_t *td = thread_self(); + + /* If this is a kernel thread becoming a user thread, then we need to create + * (the first!) process. */ + if (!td->td_proc) { + proc_t *p = proc_create(); + proc_populate(p, td); + + /* Prepare file descriptor table */ + fdtab_t *fdt = fdtab_alloc(); + fdtab_ref(fdt); + td->td_proc->p_fdtable = fdt; + } + + /* We assume process may only have a single thread. But if there were more + than one thread in the process that called exec, all other threads must be + forcefully terminated. */ + /* * We can not destroy the current vm map, because exec can still fail, * and in that case we must be able to return to the original address space. */ vm_map_t *vmap = vm_map_new(); - vm_map_t *old_vmap = vm_map_activate(vmap); + vm_map_t *old_vmap = td->td_proc ? td->td_proc->p_uspace : NULL; + + /* We are the only live thread in this process. We can safely give it a new + * uspace. */ + td->td_proc->p_uspace = vmap; + vm_map_activate(vmap); /* Iterate over prog headers */ log("ELF has %d program headers", eh.e_phnum); @@ -215,26 +238,12 @@ int do_exec(const exec_args_t *args) { log("Stack real bottom at %p", (void *)stack_bottom); prepare_program_stack(args, &stack_bottom); - thread_t *td = thread_self(); - /* ... sbrk segment ... */ sbrk_create(vmap); /* ... and user context. */ uctx_init(thread_self(), eh.e_entry, stack_bottom); - /* If this is a kernel thread becoming a user thread, then we need to create - * (the first!) process. */ - if (!td->td_proc) { - proc_t *proc = proc_create(); - proc_populate(proc, td); - - /* Prepare file descriptor table */ - fdtab_t *fdt = fdtab_alloc(); - fdtab_ref(fdt); - td->td_proc->p_fdtable = fdt; - } - /* Before we have a working fork, let's initialize file descriptors required by the standard library. */ int ignore; @@ -243,11 +252,8 @@ int do_exec(const exec_args_t *args) { do_open(td, "/dev/cons", O_WRONLY, 0, &ignore); /* - * At this point we are certain that exec suceeds. - * We can safely destroy the previous vm map. - * - * One can use do_exec() to start new user program from kernel space, - * in such case there is no old user vm space to dismantle. + * At this point we are certain that exec succeeds. We can safely destroy the + * previous vm map, and permanently assign this one to the current process. */ if (old_vmap) vm_map_delete(old_vmap); @@ -262,6 +268,7 @@ int do_exec(const exec_args_t *args) { exec_fail: /* Return to the previous map, unmodified by exec. */ + td->td_proc->p_uspace = old_vmap; vm_map_activate(old_vmap); /* Destroy the vm map we began preparing. */ vm_map_delete(vmap); diff --git a/sys/fork.c b/sys/fork.c index 5345572dfd..a6e1faa17c 100644 --- a/sys/fork.c +++ b/sys/fork.c @@ -11,7 +11,7 @@ int do_fork() { thread_t *td = thread_self(); /* Cannot fork non-user threads. */ - assert(td->td_uspace); + assert(td->td_proc); thread_t *newtd = thread_create(td->td_name, NULL, NULL); @@ -38,9 +38,6 @@ int do_fork() { starting from user_exc_leave (which serves as fork_trampoline). */ ctx_init(newtd, user_exc_leave, NULL); - /* Clone the entire process memory space. */ - newtd->td_uspace = vm_map_clone(td->td_uspace); - newtd->td_sleepqueue = sleepq_alloc(); newtd->td_wchan = NULL; newtd->td_wmesg = NULL; @@ -50,8 +47,12 @@ int do_fork() { /* Now, prepare a new process. */ assert(td->td_proc); proc_t *proc = proc_create(); + proc->p_parent = td->td_proc; proc_populate(proc, newtd); + /* Clone the entire process memory space. */ + proc->p_uspace = vm_map_clone(td->td_proc->p_uspace); + /* Copy the parent descriptor table. */ /* TODO: Optionally share the descriptor table between processes. */ proc->p_fdtable = fdtab_copy(td->td_proc->p_fdtable); diff --git a/sys/mmap.c b/sys/mmap.c index e04399d6c0..ebc85a217e 100644 --- a/sys/mmap.c +++ b/sys/mmap.c @@ -5,6 +5,7 @@ #include #include #include +#include int sys_mmap(thread_t *td, syscall_args_t *args) { vm_addr_t addr = args->args[0]; @@ -25,7 +26,8 @@ int sys_mmap(thread_t *td, syscall_args_t *args) { vm_addr_t do_mmap(vm_addr_t addr, size_t length, vm_prot_t prot, int flags, int *error) { thread_t *td = thread_self(); - vm_map_t *vmap = td->td_uspace; + assert(td->td_proc); + vm_map_t *vmap = td->td_proc->p_uspace; assert(vmap); diff --git a/sys/proc.c b/sys/proc.c index 7ff7044772..a8dd1dff71 100644 --- a/sys/proc.c +++ b/sys/proc.c @@ -21,6 +21,7 @@ proc_t *proc_create() { proc_t *proc = kmalloc(M_PROC, sizeof(proc_t), M_ZERO); mtx_init(&proc->p_lock, MTX_DEF); TAILQ_INIT(&proc->p_threads); + proc->p_nthreads = 0; mtx_lock(&all_proc_list_mtx); TAILQ_INSERT_TAIL(&all_proc_list, proc, p_all); @@ -38,6 +39,7 @@ void proc_populate(proc_t *p, thread_t *td) { mtx_scoped_lock(&td->td_lock); td->td_proc = p; TAILQ_INSERT_TAIL(&p->p_threads, td, td_procq); + p->p_nthreads += 1; } proc_t *proc_find(pid_t pid) { diff --git a/sys/sysent.c b/sys/sysent.c index 1d7505ffd4..d13d6589f0 100644 --- a/sys/sysent.c +++ b/sys/sysent.c @@ -27,9 +27,9 @@ int sys_sbrk(thread_t *td, syscall_args_t *args) { return -ENOMEM; } - assert(td->td_uspace); + assert(td->td_proc); - return sbrk_resize(td->td_uspace, increment); + return sbrk_resize(td->td_proc->p_uspace, increment); } /* This is just a stub. A full implementation of this syscall will probably diff --git a/sys/vm_map.c b/sys/vm_map.c index 1f91020ae7..42779f2b34 100644 --- a/sys/vm_map.c +++ b/sys/vm_map.c @@ -8,27 +8,23 @@ #include #include #include +#include #include +#include static MALLOC_DEFINE(M_VMMAP, "vm-map", 1, 2); static vm_map_t kspace; -vm_map_t *vm_map_activate(vm_map_t *map) { - vm_map_t *old; - +void vm_map_activate(vm_map_t *map) { critical_enter(); - thread_t *td = thread_self(); - old = td->td_uspace; - td->td_uspace = map; + PCPU_SET(uspace, map); pmap_activate(map ? map->pmap : NULL); critical_leave(); - - return old; } vm_map_t *get_user_vm_map() { - return thread_self()->td_uspace; + return PCPU_GET(uspace); } vm_map_t *get_kernel_vm_map() { @@ -240,14 +236,17 @@ void vm_map_dump(vm_map_t *map) { /* This entire function is a nasty hack, but we'll live with it until proper COW is implemented. */ vm_map_t *vm_map_clone(vm_map_t *map) { + thread_t *td = thread_self(); + assert(td->td_proc); + assert(td->td_proc->p_nthreads == 1); + vm_map_t *orig_current_map = get_user_vm_map(); vm_map_t *newmap = vm_map_new(); rw_scoped_enter(&map->rwlock, RW_READER); - /* Temporarily switch to the new map, so that we may write contents. Note that - it's okay if we get preempted - the working vm map will be restored on - context switch. */ + /* Temporarily switch to the new map, so that we may write contents. */ + td->td_proc->p_uspace = newmap; vm_map_activate(newmap); vm_map_entry_t *it; @@ -263,6 +262,7 @@ vm_map_t *vm_map_clone(vm_map_t *map) { } /* Return to original vm map. */ + td->td_proc->p_uspace = orig_current_map; vm_map_activate(orig_current_map); return newmap; diff --git a/tests/sched.c b/tests/sched.c index cb3cdc12ba..461aee1f8a 100644 --- a/tests/sched.c +++ b/tests/sched.c @@ -40,6 +40,7 @@ void main() { } #endif +#if 0 static struct { intptr_t start, end; } range[] = { @@ -91,3 +92,4 @@ static int test_sched() { } KTEST_ADD(sched, test_sched, KTEST_FLAG_NORETURN); +#endif