void procfs_rmentry(pid_t pid) { vnode_t * pdir; char name[PROCFS_NAMELEN_MAX]; struct procfs_file ** file; if (!vn_procfs) return; /* Not yet initialized. */ uitoa32(name, pid); KERROR_DBG("%s(pid = %s)\n", __func__, name); vref(vn_procfs); if (vn_procfs->vnode_ops->lookup(vn_procfs, name, &pdir)) { KERROR_DBG("pid dir doesn't exist\n"); goto out; } SET_FOREACH(file, procfs_files) { if ((*file)->filetype < PROCFS_KERNEL_SEPARATOR) { pdir->vnode_ops->unlink(pdir, (*file)->filename); } } vrele(pdir); if (vn_procfs->vnode_ops->rmdir(vn_procfs, name)) { KERROR_DBG("Can't rmdir(%s)\n", name); } out: vrele(vn_procfs); }
struct buf * geteblk_special(size_t size, uint32_t control) { struct proc_info * proc = proc_ref(0); const uintptr_t kvaddr = get_ksect_addr(size); struct buf * buf; int err; KASSERT(proc, "Can't get the PCB of pid 0"); proc_unref(proc); if (kvaddr == 0) { KERROR_DBG("Returned kvaddr is NULL\n"); return NULL; } buf = vm_newsect(kvaddr, size, VM_PROT_READ | VM_PROT_WRITE); if (!buf) { KERROR_DBG("vm_newsect() failed\n"); return NULL; } buf->b_mmu.control = control; err = vm_insert_region(proc, buf, VM_INSOP_MAP_REG); if (err < 0) { panic("Mapping a kernel special buffer failed"); } buf->b_data = buf->b_mmu.vaddr; /* Should be same as kvaddr */ return buf; }
static pid_t proc_get_next_pid(void) { const pid_t pid_reset = (configMAXPROC < 20) ? 2 : (configMAXPROC < 200) ? configMAXPROC / 2 : 100; pid_t newpid = (proc_lastpid >= configMAXPROC) ? pid_reset : proc_lastpid + 1; KERROR_DBG("%s()\n", __func__); PROC_LOCK(); while (proc_exists_locked(newpid)) { newpid++; if (newpid > configMAXPROC) { newpid = pid_reset; } } proc_lastpid = newpid; PROC_UNLOCK(); KERROR_DBG("%s done\n", __func__); return newpid; }
pid_t proc_get_random_pid(void) { pid_t last_maxproc, newpid; int count = 0, iterations = 0; KERROR_DBG("%s()\n", __func__); PROC_LOCK(); last_maxproc = configMAXPROC; newpid = last_maxproc; /* * The new PID will be "randomly" selected between proc_lastpid and * maxproc. */ do { long d = last_maxproc - proc_lastpid - 1; if (d <= 1 || count == 20) { proc_lastpid = 2; count = 0; continue; } if (newpid + 1 > last_maxproc) newpid = proc_lastpid + kunirand(d); newpid++; count++; if (iterations++ > 10000) { /* Just try to find any sufficient PID. */ iterations = 0; for (pid_t pid = 2; pid <= configMAXPROC; pid++) { if (!proc_exists_locked(newpid)) { newpid = pid; break; } } } } while (proc_exists_locked(newpid)); proc_lastpid = newpid; PROC_UNLOCK(); KERROR_DBG("%s done\n", __func__); return newpid; }
int procfs_mkentry(const struct proc_info * proc) { char name[PROCFS_NAMELEN_MAX]; vnode_t * pdir = NULL; struct procfs_file ** file; int err; if (!vn_procfs) return 0; /* Not yet initialized. */ uitoa32(name, proc->pid); KERROR_DBG("%s(pid = %s)\n", __func__, name); err = vn_procfs->vnode_ops->mkdir(vn_procfs, name, PROCFS_PERMS); if (err == -EEXIST) { return 0; } else if (err) { goto fail; } err = vn_procfs->vnode_ops->lookup(vn_procfs, name, &pdir); if (err) { pdir = NULL; goto fail; } SET_FOREACH(file, procfs_files) { const enum procfs_filetype filetype = (*file)->filetype; if (filetype < PROCFS_KERNEL_SEPARATOR) { const char * filename = (*file)->filename; err = create_proc_file(pdir, proc->pid, filename, filetype); if (err) goto fail; } } fail: if (pdir) vrele(pdir); if (err) KERROR_DBG("Failed to create a procfs entry\n"); return err; }
static void set_proc_inher(struct proc_info * old_proc, struct proc_info * new_proc) { KERROR_DBG("Updating inheriance attributes of new_proc\n"); mtx_init(&new_proc->inh.lock, PROC_INH_LOCK_TYPE, PROC_INH_LOCK_OPT); new_proc->inh.parent = old_proc; PROC_INH_INIT(new_proc); mtx_lock(&old_proc->inh.lock); PROC_INH_INSERT_HEAD(old_proc, new_proc); mtx_unlock(&old_proc->inh.lock); }
/** * Clone old process descriptor. */ static struct proc_info * clone_proc_info(struct proc_info * const old_proc) { struct proc_info * new_proc; KERROR_DBG("clone proc info of pid %u\n", old_proc->pid); new_proc = kmalloc(sizeof(struct proc_info)); if (!new_proc) { return NULL; } memcpy(new_proc, old_proc, sizeof(struct proc_info)); return new_proc; }
static int clone_stack(struct proc_info * new_proc, struct proc_info * old_proc) { struct buf * const old_region = (*old_proc->mm.regions)[MM_STACK_REGION]; struct buf * new_region; int err; if (unlikely(!old_region)) { KERROR_DBG("No stack created\n"); return 0; } err = clone2vr(old_region, &new_region); if (err) return err; err = vm_replace_region(new_proc, new_region, MM_STACK_REGION, VM_INSOP_MAP_REG); return err; }
static pthread_t create_uinit_main(void * stack_addr) { struct _sched_pthread_create_args init_ds = { .param.sched_policy = SCHED_OTHER, .param.sched_priority = NZERO, .stack_addr = stack_addr, .stack_size = configUSRINIT_SSIZE, .flags = 0, .start = uinit, /* We have to first get into user space to use exec * and mount the rootfs. */ .arg1 = (uintptr_t)rootfs, .del_thread = (void (*)(void *))uinit_exit, }; return thread_create(&init_ds, THREAD_MODE_PRIV); } /** * Map vmstack to proc. */ static void map_vmstack2proc(struct proc_info * proc, struct buf * vmstack) { struct vm_pt * vpt; (*proc->mm.regions)[MM_STACK_REGION] = vmstack; vm_updateusr_ap(vmstack); vpt = ptlist_get_pt(&proc->mm, vmstack->b_mmu.vaddr, MMU_PGSIZE_COARSE, VM_PT_CREAT); if (vpt == 0) panic("Couldn't get vpt for init stack"); vmstack->b_mmu.pt = &(vpt->pt); vm_map_region(vmstack, vpt); } /** * Create init process. */ int __kinit__ kinit(void) { SUBSYS_DEP(sched_init); SUBSYS_DEP(proc_init); SUBSYS_DEP(ramfs_init); SUBSYS_DEP(sysctl_init); SUBSYS_INIT("kinit"); char strbuf[80]; /* Buffer for panic messages. */ struct buf * init_vmstack; pthread_t tid; pid_t pid; struct thread_info * init_thread; struct proc_info * init_proc; /* * FIXME Memory allocation, protection or manipulation bug! * There is a critical bug causing random crashes in userland. I suspect * something is overwriting user space allocation from the kernel space. * Allocating some memory before init is executed seems to fix this issue, * however naturally this is not the proper way to fix the bug. * Without the allocation here the issue is sometimes seen in init or * usually after couple of fork + exec + exit cycles. The usual symptom is * that the userland app first calls some 0:0 syscalls and then tries to * execute undefined instruction, which probably means that either some * jump table in the heap or some part of the executable code is modified * by a bad access in kernel mode just before this happens. */ (void)geteblk(MMU_PGSIZE_COARSE * 10); mount_tmp_rootfs(); /* * User stack for init */ init_vmstack = create_vmstack(); if (!init_vmstack) panic("Can't allocate a stack for init"); /* * Create a thread for init */ tid = create_uinit_main((void *)(init_vmstack->b_mmu.paddr)); if (tid < 0) { ksprintf(strbuf, sizeof(strbuf), "Can't create a thread for init. %i", tid); panic(strbuf); } /* * pid of init */ pid = proc_fork(); if (pid <= 0) { ksprintf(strbuf, sizeof(strbuf), "Can't fork a process for init. %i", pid); panic(strbuf); } init_thread = thread_lookup(tid); if (!init_thread) { panic("Can't get thread descriptor of init_thread!"); } init_proc = proc_ref(pid); if (!init_proc || (init_proc->state == PROC_STATE_INITIAL)) { panic("Failed to get proc struct or invalid struct"); } init_thread->pid_owner = pid; init_thread->curr_mpt = &init_proc->mm.mpt; /* * Map the previously created user stack with init process page table. */ map_vmstack2proc(init_proc, init_vmstack); /* * Map tkstack of init with vm_pagetable_system. */ mmu_map_region(&init_thread->kstack_region->b_mmu); init_proc->main_thread = init_thread; KERROR_DBG("Init created with pid: %u, tid: %u, stack: %p\n", pid, tid, (void *)init_vmstack->b_mmu.vaddr); proc_unref(init_proc); return 0; }
pid_t proc_fork(void) { /* * http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html */ KERROR_DBG("%s(%u)\n", __func__, curproc->pid); struct proc_info * const old_proc = curproc; struct proc_info * new_proc; pid_t retval = 0; /* Check that the old process is in valid state. */ if (!old_proc || old_proc->state == PROC_STATE_INITIAL) return -EINVAL; new_proc = clone_proc_info(old_proc); if (!new_proc) return -ENOMEM; /* Clear some things required to be zeroed at this point */ new_proc->state = PROC_STATE_INITIAL; new_proc->files = NULL; new_proc->pgrp = NULL; /* Must be NULL so we don't free the old ref. */ memset(&new_proc->tms, 0, sizeof(new_proc->tms)); /* ..and then start to fix things. */ /* * Process group. */ PROC_LOCK(); proc_pgrp_insert(old_proc->pgrp, new_proc); PROC_UNLOCK(); /* * Initialize the mm struct. */ retval = vm_mm_init(&new_proc->mm, old_proc->mm.nr_regions); if (retval) goto out; /* * Clone the master page table. * This is probably something we would like to get rid of but we are * stuck with because it's the easiest way to keep some static kernel * mappings valid between processes. */ if (mmu_ptcpy(&new_proc->mm.mpt, &old_proc->mm.mpt)) { retval = -EAGAIN; goto out; } /* * Clone L2 page tables. */ if (vm_ptlist_clone(&new_proc->mm.ptlist_head, &new_proc->mm.mpt, &old_proc->mm.ptlist_head) < 0) { retval = -ENOMEM; goto out; } retval = clone_code_region(new_proc, old_proc); if (retval) goto out; /* * Clone stack region. */ retval = clone_stack(new_proc, old_proc); if (retval) { KERROR_DBG("Cloning stack region failed.\n"); goto out; } /* * Clone other regions. */ retval = clone_regions_from(new_proc, old_proc, MM_HEAP_REGION); if (retval) goto out; /* * Set break values. */ new_proc->brk_start = (void *)( (*new_proc->mm.regions)[MM_HEAP_REGION]->b_mmu.vaddr + (*new_proc->mm.regions)[MM_HEAP_REGION]->b_bcount); new_proc->brk_stop = (void *)( (*new_proc->mm.regions)[MM_HEAP_REGION]->b_mmu.vaddr + (*new_proc->mm.regions)[MM_HEAP_REGION]->b_bufsize); /* fork() signals */ ksignal_signals_fork_reinit(&new_proc->sigs); /* * Copy file descriptors. */ KERROR_DBG("Copy file descriptors\n"); int nofile_max = old_proc->rlim[RLIMIT_NOFILE].rlim_max; if (nofile_max < 0) { #if configRLIMIT_NOFILE < 0 #error configRLIMIT_NOFILE can't be negative. #endif nofile_max = configRLIMIT_NOFILE; } new_proc->files = fs_alloc_files(nofile_max, nofile_max); if (!new_proc->files) { KERROR_DBG( "\tENOMEM when tried to allocate memory for file descriptors\n"); retval = -ENOMEM; goto out; } /* Copy and ref old file descriptors */ for (int i = 0; i < old_proc->files->count; i++) { new_proc->files->fd[i] = old_proc->files->fd[i]; fs_fildes_ref(new_proc->files, i, 1); /* null pointer safe */ } KERROR_DBG("All file descriptors copied\n"); /* * Select PID. */ if (likely(nprocs != 1)) { /* Tecnically it would be good idea to have lock * on nprocs before reading it but I think this * should work fine... */ new_proc->pid = proc_get_random_pid(); } else { /* Proc is init */ KERROR_DBG("Assuming this process to be init\n"); new_proc->pid = 1; } if (new_proc->cwd) { KERROR_DBG("Increment refcount for the cwd\n"); vref(new_proc->cwd); /* Increment refcount for the cwd */ } /* Update inheritance attributes */ set_proc_inher(old_proc, new_proc); /* Insert the new process into the process array */ procarr_insert(new_proc); /* * A process shall be created with a single thread. If a multi-threaded * process calls fork(), the new process shall contain a replica of the * calling thread. * We left main_thread null if calling process has no main thread. */ KERROR_DBG("Handle main_thread\n"); if (old_proc->main_thread) { KERROR_DBG("Call thread_fork() to get a new main thread for the fork.\n"); if (!(new_proc->main_thread = thread_fork(new_proc->pid))) { KERROR_DBG("\tthread_fork() failed\n"); retval = -EAGAIN; goto out; } KERROR_DBG("\tthread_fork() fork OK\n"); /* * We set new proc's mpt as the current mpt because the new main thread * is going to return directly to the user space. */ new_proc->main_thread->curr_mpt = &new_proc->mm.mpt; } else { KERROR_DBG("No thread to fork.\n"); new_proc->main_thread = NULL; } retval = new_proc->pid; new_proc->state = PROC_STATE_READY; #ifdef configPROCFS procfs_mkentry(new_proc); #endif if (new_proc->main_thread) { KERROR_DBG("Set the new main_thread (%d) ready\n", new_proc->main_thread->id); thread_ready(new_proc->main_thread->id); } KERROR_DBG("Fork %d -> %d created.\n", old_proc->pid, new_proc->pid); out: if (unlikely(retval < 0)) { _proc_free(new_proc); } return retval; }