diff options
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/driver_subr.c | 64 | ||||
-rw-r--r-- | sys/kern/exec_elf64.c | 34 | ||||
-rw-r--r-- | sys/kern/init_main.c | 32 | ||||
-rw-r--r-- | sys/kern/kern_descrip.c | 107 | ||||
-rw-r--r-- | sys/kern/kern_exec.c | 4 | ||||
-rw-r--r-- | sys/kern/kern_exit.c | 97 | ||||
-rw-r--r-- | sys/kern/kern_panic.c | 8 | ||||
-rw-r--r-- | sys/kern/kern_sched.c | 127 | ||||
-rw-r--r-- | sys/kern/kern_spawn.c | 275 | ||||
-rw-r--r-- | sys/kern/kern_stub.c | 24 | ||||
-rw-r--r-- | sys/kern/kern_syscall.c | 10 | ||||
-rw-r--r-- | sys/kern/kern_syslog.c | 136 | ||||
-rw-r--r-- | sys/kern/kern_time.c (renamed from sys/kern/kern_fork.c) | 81 | ||||
-rw-r--r-- | sys/kern/vfs_init.c | 4 | ||||
-rw-r--r-- | sys/kern/vfs_lookup.c | 54 | ||||
-rw-r--r-- | sys/kern/vfs_syscalls.c | 46 |
16 files changed, 951 insertions, 152 deletions
diff --git a/sys/kern/driver_subr.c b/sys/kern/driver_subr.c new file mode 100644 index 0000000..29eac71 --- /dev/null +++ b/sys/kern/driver_subr.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/driver.h> +#include <sys/proc.h> +#include <sys/cdefs.h> +#include <sys/syslog.h> +#include <sys/panic.h> +#include <dev/timer.h> +#include <machine/sync.h> + +/* + * Initialize early drivers + * + * XXX: This should *NOT* be called directly, + * use DRIVERS_SCHED() instead. + */ +void +__driver_init_td(void) +{ + const struct driver *dp; + struct driver_var *var; + struct proc *td; + uintptr_t start, end; + + td = this_td(); + start = (uintptr_t)__driversd_init_start; + end = (uintptr_t)__driversd_init_end; + + for (dp = (void *)start; (uintptr_t)dp < end; ++dp) { + var = dp->data; + dp->init(); + var->deferred = 0; + } + + exit1(td, 0); + __builtin_unreachable(); +} diff --git a/sys/kern/exec_elf64.c b/sys/kern/exec_elf64.c index 3767b0b..9706e77 100644 --- a/sys/kern/exec_elf64.c +++ b/sys/kern/exec_elf64.c @@ -49,11 +49,43 @@ #define PHDR(HDRP, IDX) \ (void *)((uintptr_t)HDRP + (HDRP)->e_phoff + (HDRP->e_phentsize * IDX)) +#define SHDR(HDRP, IDX) \ + (void *)((uintptr_t)HDRP + (HDRP)->e_shoff + (HDRP->e_shentsize * IDX)) + struct elf_file { char *data; size_t size; }; +static int +elf_parse_shdrs(Elf64_Ehdr *eh) +{ + Elf64_Shdr *shp; + uint32_t nshdr; + + if (eh == NULL) { + return -EINVAL; + } + + nshdr = eh->e_shnum; + for (uint32_t i = 0; i < nshdr; ++i) { + shp = SHDR(eh, i); + + /* Drop null entries */ + if (shp->sh_type == SHT_NULL) { + continue; + } + + switch (shp->sh_type) { + case SHT_NOBITS: + memset((void *)shp->sh_addr, 0x0, shp->sh_size); + break; + } + } + + return 0; +} + /* * Load the file and give back an "elf_file" * structure. @@ -192,6 +224,7 @@ elf64_load(const char *pathname, struct proc *td, struct exec_prog *prog) if ((status = elf64_verify(hdr)) != 0) goto done; + memset(loadmap, 0, sizeof(loadmap)); pcbp = &td->pcb; start = -1; end = 0; @@ -242,6 +275,7 @@ elf64_load(const char *pathname, struct proc *td, struct exec_prog *prog) } } + elf_parse_shdrs(hdr); memcpy(prog->loadmap, loadmap, sizeof(loadmap)); prog->start = start; prog->end = end; diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 667bb97..797319f 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -35,6 +35,8 @@ #include <sys/exec.h> #include <sys/driver.h> #include <sys/panic.h> +#include <sys/systm.h> +#include <dev/acpi/uacpi.h> #include <dev/cons/cons.h> #include <dev/acpi/acpi.h> #include <machine/cpu.h> @@ -42,7 +44,13 @@ #include <vm/vm.h> #include <string.h> -static struct proc proc0; +#if defined(_INSTALL_MEDIA) +#define _START_PATH "/usr/sbin/install" +#else +#define _START_PATH "/usr/sbin/init" +#endif /* _INSTALL_MEDIA */ + +struct proc g_proc0; static void copyright(void) @@ -56,9 +64,10 @@ start_init(void) { struct proc *td = this_td(); struct execve_args execve_args; - char *argv[] = { "/usr/bin/osh", NULL }; + char *argv[] = { _START_PATH, NULL, NULL }; char *envp[] = { NULL }; + kprintf("starting init...\n"); execve_args.pathname = argv[0]; execve_args.argv = argv; execve_args.envp = envp; @@ -95,19 +104,30 @@ main(void) /* Expose the console to devfs */ cons_expose(); + uacpi_init(); + /* Start scheduler and bootstrap APs */ md_intoff(); sched_init(); + memset(&g_proc0, 0, sizeof(g_proc0)); + /* Startup pid 1 */ - memset(&proc0, 0, sizeof(proc0.tf)); - fork1(&proc0, 0, start_init, NULL); + spawn(&g_proc0, start_init, NULL, 0, NULL); + md_inton(); - /* Load all drivers */ + /* Load all early drivers */ DRIVERS_INIT(); - /* Bootstrap APs and here we go! */ + /* Only log to kmsg from here */ + syslog_silence(true); + + /* + * Bootstrap APs, schedule all other drivers + * and here we go! + */ mp_bootstrap_aps(&g_bsp_ci); + DRIVERS_SCHED(); sched_enter(); __builtin_unreachable(); } diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index d122e89..d4c9885 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -149,6 +149,7 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write) { char *kbuf = NULL; ssize_t n; + uint32_t seal; struct filedesc *filedes; struct sio_txn sio; scret_t retval = 0; @@ -159,8 +160,17 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write) } filedes = fd_get(fd); - kbuf = dynalloc(count); + seal = filedes->flags; + + /* Check the seal */ + if (write && !ISSET(seal, O_ALLOW_WR)) { + return -EPERM; + } + if (!write && ISSET(seal, O_WRONLY)) { + return -EPERM; + } + kbuf = dynalloc(count); if (kbuf == NULL) { retval = -ENOMEM; goto done; @@ -187,6 +197,7 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write) sio.buf = kbuf; sio.offset = filedes->offset; + spinlock_acquire(&filedes->lock); if (write) { /* Copy in user buffer */ if (copyin(buf, kbuf, count) < 0) { @@ -205,19 +216,52 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write) goto done; } + /* End of file? */ + if (n == 0) { + retval = 0; + goto done; + } + if (copyout(kbuf, buf, count) < 0) { retval = -EFAULT; goto done; } } + + /* Increment the offset per read */ + filedes->offset += n; retval = count; done: if (kbuf != NULL) { dynfree(kbuf); } + spinlock_release(&filedes->lock); return retval; } +static int +fd_do_create(const char *path, struct nameidata *ndp) +{ + struct vop_create_args cargs; + struct vnode *dirvp = ndp->vp; + const struct vops *vops = dirvp->vops; + int error; + + if (vops->create == NULL) { + return -EINVAL; + } + + cargs.path = path; + cargs.ppath = ndp->path; + cargs.dirvp = dirvp; + cargs.vpp = &ndp->vp; + if ((error = vops->create(&cargs)) < 0) { + return error; + } + + return 0; +} + int fd_read(unsigned int fd, void *buf, size_t count) { @@ -236,18 +280,17 @@ fd_write(unsigned int fd, void *buf, size_t count) * * @pathname: Path of file to open. * @flags: Flags to use. - * - * TODO: Use of flags. */ int fd_open(const char *pathname, int flags) { int error; + const struct vops *vops; struct filedesc *filedes; struct nameidata nd; nd.path = pathname; - nd.flags = 0; + nd.flags = ISSET(flags, O_CREAT) ? NAMEI_WANTPARENT : 0; if ((error = namei(&nd)) < 0) { return error; @@ -258,6 +301,14 @@ fd_open(const char *pathname, int flags) return error; } + vops = nd.vp->vops; + if (ISSET(flags, O_CREAT) && vops->create != NULL) { + error = fd_do_create(pathname, &nd); + } + if (error < 0) { + return error; + } + filedes->vp = nd.vp; filedes->flags = flags; return filedes->fdno; @@ -285,3 +336,51 @@ fd_dup(int fd) new_desc->vp = tmp->vp; return new_desc->fdno; } + +off_t +fd_seek(int fildes, off_t offset, int whence) +{ + struct filedesc *tmp; + struct vattr attr; + struct vop_getattr_args getattr_args; + + tmp = fd_get(fildes); + if (tmp == NULL) { + return -EBADF; + } + + getattr_args.vp = tmp->vp; + getattr_args.res = &attr; + if ((vfs_vop_getattr(tmp->vp, &getattr_args)) < 0) { + return -EPIPE; + } + + switch (whence) { + case SEEK_SET: + tmp->offset = offset; + break; + case SEEK_CUR: + tmp->offset += offset; + break; + case SEEK_END: + tmp->offset = attr.size + offset; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * Update file offset + * + * arg0: `filedes' + * arg1: `offset' + * arg2: `whence' + */ +scret_t +sys_lseek(struct syscall_args *scargs) +{ + return fd_seek(scargs->arg0, scargs->arg1, scargs->arg2); +} diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index bf6a26e..2a53b8a 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -37,6 +37,7 @@ #include <vm/map.h> #include <vm/physmem.h> #include <machine/pcb.h> +#include <machine/cdefs.h> #include <string.h> /* @@ -87,6 +88,7 @@ execve(struct proc *td, const struct execve_args *args) release_stack(td); /* Save program state */ + md_intoff(); memcpy(&td->exec, &prog, sizeof(td->exec)); /* Set new stack and map it to userspace */ @@ -99,7 +101,7 @@ execve(struct proc *td, const struct execve_args *args) stack_top = td->stack_base + (PROC_STACK_SIZE - 1); /* Setup registers, signals and stack */ - md_td_stackinit(td, (void *)(stack_top + VM_HIGHER_HALF), &prog); + stack_top = md_td_stackinit(td, (void *)(stack_top + VM_HIGHER_HALF), &prog); setregs(td, &prog, stack_top); signals_init(td); diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index 75ab0e9..c00f39b 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -35,6 +35,7 @@ #include <vm/vm.h> #include <vm/map.h> #include <machine/pcb.h> +#include <machine/cpu.h> #define pr_trace(fmt, ...) kprintf("exit: " fmt, ##__VA_ARGS__) #define pr_error(...) pr_trace(__VA_ARGS__) @@ -48,6 +49,11 @@ unload_td(struct proc *td) struct pcb *pcbp; size_t len; + sched_detach(td); + if (ISSET(td->flags, PROC_KTD)) { + return; + } + execp = &td->exec; auxvalp = &execp->auxval; pcbp = &td->pcb; @@ -72,56 +78,107 @@ unload_td(struct proc *td) } } +void +proc_reap(struct proc *td) +{ + struct pcb *pcbp; + vaddr_t stack_va; + paddr_t stack_pa; + + pcbp = &td->pcb; + unload_td(td); + + /* + * User space stacks are identity mapped and + * kernel space stacks are not. + */ + if (ISSET(td->flags, PROC_KTD)) { + stack_va = td->stack_base; + stack_pa = td->stack_base - VM_HIGHER_HALF; + } else { + stack_va = td->stack_base; + stack_pa = td->stack_base; + vm_unmap(pcbp->addrsp, stack_va, PROC_STACK_SIZE); + } + + vm_free_frame(stack_pa, PROC_STACK_PAGES); + pmap_destroy_vas(pcbp->addrsp); +} + /* * Kill a thread and deallocate its resources. * * @td: Thread to exit */ int -exit1(struct proc *td) +exit1(struct proc *td, int flags) { - struct pcb *pcbp; - struct proc *curtd; - uintptr_t stack; + struct proc *curtd, *procp; + struct proc *parent; + struct cpu_info *ci; pid_t target_pid, curpid; + ci = this_cpu(); target_pid = td->pid; curtd = this_td(); - pcbp = &td->pcb; curpid = curtd->pid; - stack = td->stack_base; td->flags |= PROC_EXITING; + parent = td->parent; - /* - * If this is on the higher half, it is kernel - * mapped and we need to convert it to a physical - * address. - */ - if (stack >= VM_HIGHER_HALF) { - stack -= VM_HIGHER_HALF; + /* If we have any children, kill them too */ + if (td->nleaves > 0) { + TAILQ_FOREACH(procp, &td->leafq, leaf_link) { + if (!ISSET(procp->flags, PROC_EXITING)) + exit1(procp, flags); + } } - unload_td(td); - vm_unmap(pcbp->addrsp, td->stack_base, PROC_STACK_SIZE); - vm_free_frame(stack, PROC_STACK_PAGES); + if (target_pid != curpid) { + proc_reap(td); + } - pmap_destroy_vas(pcbp->addrsp); - dynfree(td); + if (td->data != NULL) { + dynfree(td->data); + } + + /* + * Only free the process structure if we aren't + * being waited on, otherwise let it be so the + * parent can examine what's left of it. + */ + if (!ISSET(td->flags, PROC_WAITED)) { + dynfree(td); + } else { + td->flags |= PROC_ZOMB; + td->flags &= ~PROC_WAITED; + } /* * If we are the thread exiting, reenter the scheduler * and do not return. */ - if (target_pid == curpid) + if (target_pid == curpid) { + ci->curtd = NULL; + if (parent->pid == 0) + sched_enter(); + + parent->flags &= ~PROC_SLEEP; sched_enter(); + } return 0; } +/* + * arg0: Exit status. + */ scret_t sys_exit(struct syscall_args *scargs) { - exit1(this_td()); + struct proc *td = this_td(); + + td->exit_status = scargs->arg0; + exit1(td, 0); __builtin_unreachable(); } diff --git a/sys/kern/kern_panic.c b/sys/kern/kern_panic.c index 950ea8f..7660fff 100644 --- a/sys/kern/kern_panic.c +++ b/sys/kern/kern_panic.c @@ -31,6 +31,8 @@ #include <sys/spinlock.h> #include <sys/syslog.h> #include <sys/reboot.h> +#include <machine/cdefs.h> +#include <machine/cpu.h> /* * Burn and sizzle - the core logic that really ends @@ -69,8 +71,12 @@ panic(const char *fmt, ...) { va_list ap; + /* Shut everything else up */ + md_intoff(); + cpu_halt_others(); + va_start(ap, fmt); - kprintf(OMIT_TIMESTAMP "panic: "); + kprintf(OMIT_TIMESTAMP "\npanic: "); vkprintf(fmt, &ap); bas(true, REBOOT_HALT); diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c index 4bbe5a0..ec5592e 100644 --- a/sys/kern/kern_sched.c +++ b/sys/kern/kern_sched.c @@ -104,12 +104,29 @@ sched_dequeue_td(void) for (size_t i = 0; i < SCHED_NQUEUE; ++i) { queue = &qlist[i]; - if (!TAILQ_EMPTY(&queue->q)) { - td = TAILQ_FIRST(&queue->q); - TAILQ_REMOVE(&queue->q, td, link); - spinlock_release(&tdq_lock); - return td; + if (TAILQ_EMPTY(&queue->q)) { + continue; } + + td = TAILQ_FIRST(&queue->q); + if (td == NULL) { + continue; + } + + while (ISSET(td->flags, PROC_SLEEP)) { + td = TAILQ_NEXT(td, link); + if (td == NULL) { + break; + } + } + + if (td == NULL) { + continue; + } + + TAILQ_REMOVE(&queue->q, td, link); + spinlock_release(&tdq_lock); + return td; } /* We got nothing */ @@ -176,62 +193,50 @@ td_pri_update(struct proc *td) } } +void +sched_switch_to(struct trapframe *tf, struct proc *td) +{ + struct cpu_info *ci; + struct pcb *pcbp; + + ci = this_cpu(); + + if (tf != NULL) { + memcpy(tf, &td->tf, sizeof(*tf)); + } + + ci->curtd = td; + pcbp = &td->pcb; + pmap_switch_vas(pcbp->addrsp); +} + /* * Perform a context switch. */ void sched_switch(struct trapframe *tf) { - struct cpu_info *ci; - struct pcb *pcbp; struct proc *next_td, *td; - bool use_current = true; + struct cpu_info *ci; ci = this_cpu(); td = ci->curtd; if (td != NULL) { - dispatch_signals(td); - td_pri_update(td); - } - - /* - * Get the next thread and use it only if it isn't - * in the middle of an exit, exec, or whatever. - */ - do { - if ((next_td = sched_dequeue_td()) == NULL) { - sched_oneshot(false); + if (td->pid == 0) return; - } - /* - * If we are in the middle of an exec, don't use this - * thread. - */ - if (ISSET(next_td->flags, PROC_EXEC)) { - use_current = false; - } - - /* - * Don't use this thread if we are currently - * exiting. - */ - if (ISSET(next_td->flags, PROC_EXITING)) { - use_current = false; - } - } while (!use_current); - - /* Save the previous thread */ - if (td != NULL) { + dispatch_signals(td); + td_pri_update(td); sched_save_td(td, tf); } - memcpy(tf, &next_td->tf, sizeof(*tf)); - ci->curtd = next_td; - pcbp = &next_td->pcb; + if ((next_td = sched_dequeue_td()) == NULL) { + sched_oneshot(false); + return; + } - pmap_switch_vas(pcbp->addrsp); + sched_switch_to(tf, next_td); sched_oneshot(false); } @@ -242,9 +247,8 @@ void sched_enter(void) { md_inton(); - md_sync_all(); + sched_oneshot(false); for (;;) { - sched_oneshot(false); md_pause(); } } @@ -252,14 +256,39 @@ sched_enter(void) void sched_yield(void) { - struct proc *td = this_td(); + struct proc *td; + struct cpu_info *ci = this_cpu(); - if (td != NULL) { - td->rested = true; + if ((td = ci->curtd) == NULL) { + return; + } + + td->rested = true; + + /* FIXME: Hang yielding when waited on */ + if (ISSET(td->flags, PROC_WAITED)) { + return; } + ci->curtd = NULL; + md_inton(); sched_oneshot(false); - while (td->rested); + + md_hlt(); + md_intoff(); + ci->curtd = td; +} + +void +sched_detach(struct proc *td) +{ + struct sched_queue *queue; + + spinlock_acquire(&tdq_lock); + queue = &qlist[td->priority]; + + TAILQ_REMOVE(&queue->q, td, link); + spinlock_release(&tdq_lock); } void diff --git a/sys/kern/kern_spawn.c b/sys/kern/kern_spawn.c new file mode 100644 index 0000000..a953a6e --- /dev/null +++ b/sys/kern/kern_spawn.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/spawn.h> +#include <sys/proc.h> +#include <sys/exec.h> +#include <sys/mman.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/syslog.h> +#include <sys/syscall.h> +#include <sys/atomic.h> +#include <sys/signal.h> +#include <sys/limits.h> +#include <sys/sched.h> +#include <vm/dynalloc.h> +#include <string.h> + +#define pr_trace(fmt, ...) kprintf("spawn: " fmt, ##__VA_ARGS__) +#define pr_error(...) pr_trace(__VA_ARGS__) + +#define ARGVP_MAX (ARG_MAX / sizeof(void *)) + +static volatile size_t nthreads = 0; + +/* + * TODO: envp + */ +struct spawn_args { + char path[PATH_MAX]; + char argv_blk[ARG_MAX]; + char *argv[ARGVP_MAX]; +}; + +static inline void +try_free_data(void *p) +{ + if (p != NULL) { + dynfree(p); + } +} + +static void +spawn_thunk(void) +{ + const char *path; + char pathbuf[PATH_MAX]; + struct proc *cur; + struct execve_args execve_args; + struct spawn_args *args; + char *envp[] = { NULL }; + + cur = this_td(); + args = cur->data; + path = args->path; + memset(pathbuf, 0, sizeof(pathbuf)); + memcpy(pathbuf, path, strlen(path)); + + execve_args.pathname = pathbuf; + execve_args.argv = (char **)&args->argv[0]; + execve_args.envp = envp; + path = NULL; + + if (execve(cur, &execve_args) != 0) { + pr_error("execve failed, aborting\n"); + exit1(this_td(), 0); + } + __builtin_unreachable(); +} + +/* + * Spawn a new process + * + * @cur: Parent (current) process. + * @func: Address of start code. + * @p: Data to pass to new process (used for user procs) + * @flags: Spawn flags. + * @newprocp: If not NULL, will contain the new process. + * + * Returns the PID of the child on success, otherwise an + * errno value that is less than zero. + * + * XXX: `p` is only used by sys_spawn and should be set + * to NULL if called in the kernel. + */ +pid_t +spawn(struct proc *cur, void(*func)(void), void *p, int flags, struct proc **newprocp) +{ + struct proc *newproc; + struct mmap_lgdr *mlgdr; + int error; + pid_t pid; + + newproc = dynalloc(sizeof(*newproc)); + if (newproc == NULL) { + pr_error("could not alloc proc (-ENOMEM)\n"); + try_free_data(p); + return -ENOMEM; + } + + mlgdr = dynalloc(sizeof(*mlgdr)); + if (mlgdr == NULL) { + dynfree(newproc); + try_free_data(p); + pr_error("could not alloc proc mlgdr (-ENOMEM)\n"); + return -ENOMEM; + } + + memset(newproc, 0, sizeof(*newproc)); + error = md_spawn(newproc, cur, (uintptr_t)func); + if (error < 0) { + dynfree(newproc); + dynfree(mlgdr); + try_free_data(p); + pr_error("error initializing proc\n"); + return error; + } + + /* Set proc output if we can */ + if (newprocp != NULL) { + *newprocp = newproc; + } + + if (!ISSET(cur->flags, PROC_LEAFQ)) { + TAILQ_INIT(&cur->leafq); + cur->flags |= PROC_LEAFQ; + } + + /* Add to parent leafq */ + TAILQ_INSERT_TAIL(&cur->leafq, newproc, leaf_link); + atomic_inc_int(&cur->nleaves); + newproc->parent = cur; + newproc->data = p; + newproc->exit_status = -1; + + /* Initialize the mmap ledger */ + mlgdr->nbytes = 0; + RBT_INIT(lgdr_entries, &mlgdr->hd); + newproc->mlgdr = mlgdr; + newproc->flags |= PROC_WAITED; + + newproc->pid = ++nthreads; + signals_init(newproc); + sched_enqueue_td(newproc); + pid = newproc->pid; + + if (ISSET(flags, SPAWN_WAIT)) { + cur->flags |= PROC_SLEEP; + + while (ISSET(cur->flags, PROC_SLEEP)) { + sched_yield(); + } + while (!ISSET(newproc->flags, PROC_ZOMB)) { + sched_yield(); + } + + if (newproc->exit_status < 0) { + pid = newproc->exit_status; + } + + proc_reap(newproc); + } + + return pid; +} + +/* + * Get the child of a process by PID. + * + * @cur: Parent process. + * @pid: Child PID. + * + * Returns NULL if no child was found. + */ +struct proc * +get_child(struct proc *cur, pid_t pid) +{ + struct proc *procp; + + TAILQ_FOREACH(procp, &cur->leafq, leaf_link) { + if (procp->pid == pid) { + return procp; + } + } + + return NULL; +} + +/* + * arg0: The file /path/to/executable + * arg1: Argv + * arg2: Envp (TODO) + * arg3: Optional flags (`flags') + */ +scret_t +sys_spawn(struct syscall_args *scargs) +{ + struct spawn_args *args; + char *path; + const char *u_path, **u_argv; + const char *u_p = NULL; + struct proc *td; + int flags, error; + size_t len, bytes_copied = 0; + size_t argv_i = 0; + + td = this_td(); + u_path = (const char *)scargs->arg0; + u_argv = (const char **)scargs->arg1; + flags = scargs->arg3; + + args = dynalloc(sizeof(*args)); + if (args == NULL) { + return -ENOMEM; + } + + error = copyinstr(u_path, args->path, sizeof(args->path)); + if (error < 0) { + dynfree(args); + return error; + } + + memset(args->argv, 0, ARG_MAX); + for (size_t i = 0; i < ARG_MAX - 1; ++i) { + error = copyin(&u_argv[argv_i], &u_p, sizeof(u_p)); + if (error < 0) { + dynfree(args); + return error; + } + if (u_p == NULL) { + args->argv[argv_i++] = NULL; + break; + } + + path = &args->argv_blk[i]; + error = copyinstr(u_p, path, ARG_MAX - bytes_copied); + if (error < 0) { + dynfree(args); + return error; + } + + args->argv[argv_i++] = &args->argv_blk[i]; + len = strlen(path); + bytes_copied += (len + 1); + i += len; + } + + return spawn(td, spawn_thunk, args, flags, NULL); +} diff --git a/sys/kern/kern_stub.c b/sys/kern/kern_stub.c index 8603fd5..17c6e54 100644 --- a/sys/kern/kern_stub.c +++ b/sys/kern/kern_stub.c @@ -40,8 +40,10 @@ sigfpe_default(int signo) static struct proc *td; td = this_td(); - kprintf("Floating point exception (pid=%d)\n", td->pid); - exit1(td); + syslog_silence(false); + kprintf(OMIT_TIMESTAMP "Floating point exception (pid=%d)\n", td->pid); + syslog_silence(true); + exit1(td, 0); } void @@ -50,8 +52,10 @@ sigkill_default(int signo) static struct proc *td; td = this_td(); - kprintf("Terminated (pid=%d)\n", td->pid); - exit1(td); + syslog_silence(false); + kprintf(OMIT_TIMESTAMP "Terminated (pid=%d)\n", td->pid); + syslog_silence(true); + exit1(td, 0); } void @@ -60,8 +64,10 @@ sigsegv_default(int signo) static struct proc *td; td = this_td(); - kprintf("Segmentation fault (pid=%d)\n", td->pid); - exit1(td); + syslog_silence(false); + kprintf(OMIT_TIMESTAMP "Segmentation fault (pid=%d)\n", td->pid); + syslog_silence(true); + exit1(td, 0); } int @@ -75,3 +81,9 @@ dev_nowrite(void) { return -ENOTSUP; } + +int +dev_nobsize(void) +{ + return -ENOTSUP; +} diff --git a/sys/kern/kern_syscall.c b/sys/kern/kern_syscall.c index 986d82a..b1deb7a 100644 --- a/sys/kern/kern_syscall.c +++ b/sys/kern/kern_syscall.c @@ -29,7 +29,10 @@ #include <sys/syscall.h> #include <sys/sysctl.h> +#include <sys/reboot.h> #include <sys/types.h> +#include <sys/time.h> +#include <sys/mman.h> #include <sys/proc.h> #include <sys/vfs.h> @@ -42,6 +45,13 @@ scret_t(*g_sctab[])(struct syscall_args *) = { sys_stat, /* SYS_stat */ sys_sysctl, /* SYS_sysctl */ sys_write, /* SYS_write */ + sys_spawn, /* SYS_spawn */ + sys_reboot, /* SYS_reboot */ + sys_mmap, /* SYS_mmap */ + sys_munmap, /* SYS_munap */ + sys_access, /* SYS_access */ + sys_lseek, /* SYS_lseek */ + sys_sleep, /* SYS_sleep */ }; const size_t MAX_SYSCALLS = NELEM(g_sctab); diff --git a/sys/kern/kern_syslog.c b/sys/kern/kern_syslog.c index 10bf348..eb4fa8d 100644 --- a/sys/kern/kern_syslog.c +++ b/sys/kern/kern_syslog.c @@ -28,9 +28,14 @@ */ #include <sys/syslog.h> +#include <sys/cdefs.h> +#include <sys/sio.h> #include <sys/spinlock.h> +#include <sys/device.h> +#include <sys/errno.h> #include <dev/cons/cons.h> #include <dev/timer.h> +#include <fs/devfs.h> #include <stdarg.h> #include <string.h> @@ -40,21 +45,106 @@ #define SERIAL_DEBUG 0 #endif +#if defined(__USER_KMSG) +#define USER_KMSG __USER_KMSG +#else +#define USER_KMSG 0 +#endif + +#define KBUF_SIZE (1 << 16) + +/* Sanity check */ +__static_assert(KBUF_SIZE <= (1 << 16), "KBUF_SIZE too high!"); + /* Global logger lock */ static struct spinlock lock = {0}; +static struct spinlock kmsg_lock = {0}; +static bool no_cons_log = false; + +/* Kernel message buffer */ +static char kmsg[KBUF_SIZE]; +static size_t kmsg_i = 0; +static struct cdevsw kmsg_cdevw; + +static void +kmsg_append(const char *s, size_t len) +{ + spinlock_acquire(&kmsg_lock); + if ((kmsg_i + len) >= KBUF_SIZE) { + kmsg_i = 0; + } + + for (size_t i = 0; i < len; ++i) { + kmsg[kmsg_i + i] = s[i]; + } + kmsg_i += len; + spinlock_release(&kmsg_lock); +} + +/* + * Character device function. + */ +static int +kmsg_read(dev_t dev, struct sio_txn *sio, int flags) +{ + size_t len, offset, j; + size_t bytes_read = 0; + char *p = sio->buf; + + spinlock_acquire(&kmsg_lock); + len = sio->len; + offset = sio->offset; + + if (len == 0) { + spinlock_release(&kmsg_lock); + return -EINVAL; + } + if (offset >= kmsg_i) { + spinlock_release(&kmsg_lock); + return 0; + } + + for (size_t i = 0; i < len; ++i) { + j = offset + i; + if (j > kmsg_i) { + break; + } + + p[i] = kmsg[j]; + ++bytes_read; + } + + spinlock_release(&kmsg_lock); + return bytes_read; +} static void syslog_write(const char *s, size_t len) { - const char *p = s; + const char *p; + size_t l; - while (len--) { - cons_putch(&g_root_scr, *p); - if (SERIAL_DEBUG) { + if (SERIAL_DEBUG) { + p = s; + l = len; + while (l--) { serial_putc(*p); + ++p; } - ++p; } + + kmsg_append(s, len); + + /* + * If the USER_KMSG option is disabled in kconf, + * do not log to the console if everything else + * has already started. + */ + if (!USER_KMSG && no_cons_log) { + return; + } + + cons_putstr(&g_root_scr, s, len); } /* @@ -101,14 +191,48 @@ kprintf(const char *fmt, ...) } } + spinlock_acquire(&lock); if (use_timestamp) { syslog_write(timestamp, strlen(timestamp)); } - spinlock_acquire(&lock); va_start(ap, fmt); vkprintf(fmt_p, &ap); va_end(ap); spinlock_release(&lock); } + +/* + * Silence kernel messages in if the system + * is already operating in a user context. + * + * XXX: This is ignored if the kconf USER_KMSG + * option is set to "no". A kmsg device file + * is also created on the first call. + */ +void +syslog_silence(bool option) +{ + static bool once = false; + static char devname[] = "kmsg"; + devmajor_t major; + dev_t dev; + + if (!once) { + once = true; + major = dev_alloc_major(); + dev = dev_alloc(major); + + dev_register(major, dev, &kmsg_cdevw); + devfs_create_entry(devname, major, dev, 0444); + + } + + no_cons_log = option; +} + +static struct cdevsw kmsg_cdevw = { + .read = kmsg_read, + .write = nowrite +}; diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_time.c index abb7707..102648c 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_time.c @@ -27,61 +27,50 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include <sys/mman.h> -#include <sys/tree.h> #include <sys/types.h> -#include <sys/proc.h> +#include <sys/time.h> +#include <sys/syscall.h> +#include <sys/systm.h> #include <sys/errno.h> -#include <sys/sched.h> -#include <sys/signal.h> -#include <vm/dynalloc.h> -#include <string.h> - -static size_t nthreads = 0; +#include <sys/cdefs.h> +#include <dev/timer.h> +#include <machine/cdefs.h> /* - * Fork1 - fork and direct a thread to 'ip' - * - * @cur: Current process. - * @flags: Flags to set. - * @ip: Location for new thread to start at. - * @newprocp: Will contain new thread if not NULL. + * arg0: Timespec + * arg1: Remaining timeval */ -int -fork1(struct proc *cur, int flags, void(*ip)(void), struct proc **newprocp) +scret_t +sys_sleep(struct syscall_args *scargs) { - struct proc *newproc; - struct mmap_lgdr *mlgdr; - int status = 0; - - newproc = dynalloc(sizeof(*newproc)); - if (newproc == NULL) - return -ENOMEM; - - mlgdr = dynalloc(sizeof(*mlgdr)); - if (mlgdr == NULL) - return -ENOMEM; + struct timespec ts; + struct timer tmr; + size_t timeout_msec; + tmrr_status_t status; + int error; - memset(newproc, 0, sizeof(*newproc)); - status = md_fork(newproc, cur, (uintptr_t)ip); - if (status != 0) - goto done; + error = copyin((void *)scargs->arg0, &ts, sizeof(ts)); + if (error < 0) { + return error; + } - /* Set proc output if we can */ - if (newprocp != NULL) - *newprocp = newproc; + if (ts.tv_nsec >= 1000000000) { + return -EINVAL; + } - /* Initialize the mmap ledger */ - mlgdr->nbytes = 0; - RBT_INIT(lgdr_entries, &mlgdr->hd); - newproc->mlgdr = mlgdr; + status = req_timer(TIMER_GP, &tmr); + if (__unlikely(status != TMRR_SUCCESS)) { + return -ENOTSUP; + } + if (__unlikely(tmr.msleep == NULL)) { + return -ENOTSUP; + } - newproc->pid = ++nthreads; - signals_init(newproc); - sched_enqueue_td(newproc); -done: - if (status != 0) - dynfree(newproc); + timeout_msec = ts.tv_nsec / 1000000; + timeout_msec += ts.tv_sec * 1000; - return status; + md_inton(); + tmr.msleep(timeout_msec); + md_intoff(); + return 0; } diff --git a/sys/kern/vfs_init.c b/sys/kern/vfs_init.c index caa0766..bc7f8b0 100644 --- a/sys/kern/vfs_init.c +++ b/sys/kern/vfs_init.c @@ -36,7 +36,9 @@ struct vnode *g_root_vnode = NULL; static struct fs_info fs_list[] = { {MOUNT_RAMFS, &g_initramfs_vfsops, 0, 0}, - {MOUNT_DEVFS, &g_devfs_vfsops, 0, 0} + {MOUNT_DEVFS, &g_devfs_vfsops, 0, 0}, + {MOUNT_CTLFS, &g_ctlfs_vfsops, 0, 0}, + {MOUNT_TMPFS, &g_tmpfs_vfsops, 0, 0} }; void diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index 7419d1d..d88c447 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -29,6 +29,7 @@ #include <sys/namei.h> #include <sys/vnode.h> +#include <sys/param.h> #include <sys/mount.h> #include <sys/errno.h> #include <vm/dynalloc.h> @@ -118,20 +119,60 @@ vfs_get_fname_at(const char *path, size_t idx) } /* + * Count the number of components that exist within + * a path minus the delimiter as well as any redundant + * delimiters. + * + * @path: Path to count + */ +static uint8_t +namei_num_cnp(const char *path) +{ + const char *p = path; + uint8_t count = 0; + + while (*p != '\0') { + /* Skip redundant delimiters */ + if (p[0] == '/' && p[1] == '/') { + ++p; + continue; + } + + if (*p == '/') { + ++count; + } + ++p; + } + + /* Don't count leading slash */ + if (*(p - 1) == '/') { + --count; + } + + return count; +} + +/* * Search for a path within a mountpoint. * * @mp: Mountpoint to search in. * @path: Path to search for. + * @ndp: Namei data pointer */ static struct vnode * -namei_mp_search(struct mount *mp, const char *path) +namei_mp_search(struct mount *mp, const char *path, struct nameidata *ndp) { struct vop_lookup_args lookup_args; struct vnode *vp = mp->vp; + uint8_t n_cnp = 0; char *name; int status; - for (size_t i = 1;; ++i) { + n_cnp = namei_num_cnp(path); + if (ISSET(ndp->flags, NAMEI_WANTPARENT)) { + --n_cnp; + } + for (size_t i = 1; i < n_cnp; ++i) { name = vfs_get_fname_at(path, i); if (name == NULL) break; @@ -143,11 +184,12 @@ namei_mp_search(struct mount *mp, const char *path) status = vfs_vop_lookup(vp, &lookup_args); dynfree(name); - if (status == 0) - return vp; + if (status != 0) { + return NULL; + } } - return NULL; + return vp; } /* @@ -211,7 +253,7 @@ namei(struct nameidata *ndp) /* If the name matches, search within */ if (strcmp(mp->name, name) == 0) - vp = namei_mp_search(mp, path); + vp = namei_mp_search(mp, path, ndp); /* Did we find it at this mountpoint? */ if (vp != NULL) { diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 6f2d683..0d51331 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -43,7 +43,7 @@ static int vfs_dostat(const char *path, struct stat *sbuf) { char pathbuf[PATH_MAX]; - struct vattr *attr; + struct vattr attr; struct stat st; struct vnode *vp; struct vop_getattr_args gattr; @@ -54,11 +54,11 @@ vfs_dostat(const char *path, struct stat *sbuf) return -EINVAL; } - if ((copyinstr(path, pathbuf, sizeof(path))) < 0) { + if ((copyinstr(path, pathbuf, sizeof(pathbuf))) < 0) { return -EFAULT; } - nd.path = path; + nd.path = pathbuf; nd.flags = 0; if ((error = namei(&nd)) != 0) { @@ -67,19 +67,42 @@ vfs_dostat(const char *path, struct stat *sbuf) vp = nd.vp; gattr.vp = vp; + gattr.res = &attr; error = vfs_vop_getattr(vp, &gattr); if (error != 0) { return error; } - attr = gattr.res; memset(&st, VNOVAL, sizeof(st)); /* Copy stat data to userspace statbuf */ - st.st_mode = attr->mode; - st.st_size = attr->size; + st.st_mode = attr.mode; + st.st_size = attr.size; copyout(&st, sbuf, sizeof(*sbuf)); + vfs_release_vnode(vp); + return 0; +} + +static int +vfs_doaccess(const char *path) +{ + struct nameidata nd; + char pathbuf[PATH_MAX]; + int error; + + if ((copyinstr(path, pathbuf, sizeof(pathbuf))) < 0) { + return -EFAULT; + } + + nd.path = pathbuf; + nd.flags = 0; + + if ((error = namei(&nd)) != 0) { + return error; + } + + vfs_release_vnode(nd.vp); return 0; } @@ -149,3 +172,14 @@ sys_stat(struct syscall_args *scargs) { return vfs_dostat((const char *)scargs->arg0, (void *)scargs->arg1); } + +/* + * Check if a file can be accessed. + * + * @arg0: path + */ +scret_t +sys_access(struct syscall_args *scargs) +{ + return vfs_doaccess((const char *)scargs->arg0); +} |