Support for proc_create and proc_run from KFS
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 17 Jul 2009 21:42:59 +0000 (14:42 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 17 Jul 2009 21:42:59 +0000 (14:42 -0700)
Userspace can create processes and run them (a two-phase deal) from
files stored as binary blobs at the end of the kernel (KFS).  Eventually
will have some sort of VFS support.  You can also list and run these
programs from the kernel monitor.  KFS entries need to be statically
specified in kern/src/Makefrag and kern/src/kfs.c.  Fixed various other
issues with environments (parent id, page table changes) and some other
things in the process.

16 files changed:
include/env.h
include/kfs.h [new file with mode: 0644]
include/monitor.h
include/ros/env.h
include/ros/error.h
kern/src/Makefrag
kern/src/env.c
kern/src/kfs.c [new file with mode: 0644]
kern/src/manager.c
kern/src/monitor.c
kern/src/pmap.c
kern/src/syscall.c
user/apps/roslib/hello.c
user/apps/roslib/spawn.c [new file with mode: 0644]
user/roslib/inc/syswrapper.h
user/roslib/src/syswrapper.c

index d6adb2c..469c543 100644 (file)
@@ -37,8 +37,10 @@ void run_env_handler(trapframe_t *tf, void* data);
 #define ENV_CREATE(x)                  ({                                             \
        extern uint8_t _binary_obj_user_apps_##x##_start[],                        \
                _binary_obj_user_apps_##x##_size[];                                    \
-       env_create(_binary_obj_user_apps_##x##_start,                              \
+       env_t *e = env_create(_binary_obj_user_apps_##x##_start,                   \
                (int)_binary_obj_user_apps_##x##_size);                                \
+       e->env_status = ENV_RUNNABLE;                                              \
+       e;                                                                         \
 })
 
 #endif // !ROS_KERN_ENV_H
diff --git a/include/kfs.h b/include/kfs.h
new file mode 100644 (file)
index 0000000..d0c4efe
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * KFS (Kernel File System)
+ *
+ * This gives runtime access to the binary blobs (usually userspace programs)
+ * linked at the end of the kernel.  Extremely rudimentary.
+ * Also allows for process creation from file (can consider moving this).
+ *
+ * Add the files you want in KFS in kfs.c.
+ */
+
+#ifndef ROS_KERN_KFS_H
+#define ROS_KERN_KFS_H
+
+#include <arch/types.h>
+#include <env.h>
+
+#pragma nodeputy
+
+struct kfs_entry {
+       char name[256];
+       uint8_t *start;
+       size_t size;
+};
+
+#define MAX_KFS_FILES 10
+extern struct kfs_entry kfs[MAX_KFS_FILES];
+
+ssize_t kfs_lookup_path(char* path);
+env_t* kfs_proc_create(int kfs_inode);
+
+#endif // !ROS_KERN_KFS_H
index 862758e..25506b6 100644 (file)
@@ -20,5 +20,7 @@ int mon_showmapping(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
 int mon_setmapperm(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
 int mon_cpuinfo(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
 int mon_nanwan(int argc, char **argv, trapframe_t *tf);
+int mon_kfs_ls(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
+int mon_kfs_run(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf);
 
 #endif // !ROS_KERN_MONITOR_H
index b218708..349855a 100644 (file)
@@ -35,11 +35,13 @@ typedef int32_t envid_t;
 #define ENVX(envid)            ((envid) & (NENV - 1))
 
 // Values of env_status in struct Env
+// TODO: think about what states we want.
 #define ENV_FREE                       0
 #define ENV_RUNNING                    1
 #define ENV_RUNNABLE           2
 #define ENV_NOT_RUNNABLE       3
 #define ENV_DYING                      4
+#define ENV_CREATED                    5
 
 struct Env {
        LIST_ENTRY(Env) env_link NOINIT;        // Free list link pointers
index ed0ec3a..6c34999 100644 (file)
@@ -17,6 +17,7 @@ typedef enum {
        EBADENV,                 // Bad environment 
        ENOFREEENV,              // No free environment
        EUNSPECIFIED,            // Unspecified
+       EMORON,                  // Moron
        NUMERRORS,               // Total number of error codes
 } error_t;
 
@@ -35,11 +36,12 @@ static const char * const error_string[NUMERRORS] =
        "Would cause deadlock",
        "Currently busy, try again later",
        "No memory available",
-       "Invalid arguments"
-       "Segmentation fault"
-       "Bad environment"
-       "No free environment"
-       "Unspecified"
+       "Invalid arguments",
+       "Segmentation fault",
+       "Bad environment",
+       "No free environment",
+       "You are a moron",
+       "Unspecified",
 };
 
 #endif // !ROS_INC_ERROR_H */
index b391eee..b10ec95 100644 (file)
@@ -36,6 +36,7 @@ KERN_SRCFILES := $(KERN_SRC_DIR)/entry.S \
                  $(KERN_SRC_DIR)/readline.c \
                  $(KERN_SRC_DIR)/string.c \
                  $(KERN_SRC_DIR)/timer.c \
+                 $(KERN_SRC_DIR)/kfs.c \
                  $(KERN_SRC_DIR)/kmalloc.c
 # Only build files if they exist.
 KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
@@ -44,6 +45,7 @@ KERN_APPFILES :=    $(USER_APPS_PARLIB_DIR)/matrix \
                     $(USER_APPS_ROSLIB_DIR)/null \
                     $(USER_APPS_ROSLIB_DIR)/hello \
                     $(USER_APPS_ROSLIB_DIR)/proctests \
+                    $(USER_APPS_ROSLIB_DIR)/spawn \
                     $(USER_APPS_ROSLIB_DIR)/measurements
 #                    $(USER_APPS_PARLIB_DIR)/draw_nanwan
 #                    $(USER_APPS_PARLIB_DIR)/open_read \
index a683d38..528d6f8 100644 (file)
@@ -242,7 +242,7 @@ env_alloc(env_t **newenv_store, envid_t parent_id)
        // Set the basic status variables.
     e->lock = 0;
        e->env_parent_id = parent_id;
-       e->env_status = ENV_RUNNABLE;
+       e->env_status = ENV_CREATED;
        e->env_runs = 0;
        e->env_refcnt = 1;
        e->env_flags = 0;
@@ -390,8 +390,7 @@ load_icode(env_t *e, uint8_t *COUNT(size) binary, size_t size)
 
        // to actually access any pages alloc'd for this environment, we
        // need to have the hardware use this environment's page tables.
-       // we can use e's tables as long as we want, since it has the same
-       // mappings for the kernel as does boot_pgdir
+       uintreg_t old_cr3 = rcr3();
        lcr3(e->env_cr3);
 
        // TODO: how do we do a runtime COUNT?
@@ -413,8 +412,10 @@ load_icode(env_t *e, uint8_t *COUNT(size) binary, size_t size)
 
        // Now map one page for the program's initial stack
        // at virtual address USTACKTOP - PGSIZE.
-
        segment_alloc(e, (void*SNT)(USTACKTOP - PGSIZE), PGSIZE);
+
+       // reload the original address space
+       lcr3(old_cr3);
 }
 
 //
@@ -424,8 +425,11 @@ env_t* env_create(uint8_t *binary, size_t size)
 {
        env_t *e;
        int r;
-
-       if ((r = env_alloc(&e, 0)) < 0)
+       env_t *curenv = curenvs[lapic_get_id()];
+       envid_t curid;
+       
+       curid = (curenv ? curenv->env_id : 0);  
+       if ((r = env_alloc(&e, curid)) < 0)
                panic("env_create: %e", r);
        load_icode(e, binary, size);
        return e;
diff --git a/kern/src/kfs.c b/kern/src/kfs.c
new file mode 100644 (file)
index 0000000..9c32bd6
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ */
+
+#include <kfs.h>
+#include <string.h>
+#include <assert.h>
+#include <ros/error.h>
+
+#define DECL_PROG(x) extern uint8_t _binary_obj_user_apps_##x##_start[], _binary_obj_user_apps_##x##_size[];      
+
+#define KFS_ENTRY(x) {#x, _binary_obj_user_apps_##x##_start, (size_t) _binary_obj_user_apps_##x##_size},
+
+/*
+ * Hardcode the files included in the KFS.  This needs to be in sync with the
+ * userapps in kern/src/Makefrag.
+ * Make sure to declare it, and add an entry.  Keep MAX_KFS_FILES big enough too
+ */
+DECL_PROG(roslib_hello);
+DECL_PROG(roslib_null);
+DECL_PROG(roslib_spawn);
+
+struct kfs_entry kfs[MAX_KFS_FILES] = {
+       KFS_ENTRY(roslib_hello)
+       KFS_ENTRY(roslib_null)
+       KFS_ENTRY(roslib_spawn)
+};
+
+ssize_t kfs_lookup_path(char* path)
+{
+       for (int i = 0; i < MAX_KFS_FILES; i++)
+               // need to think about how to copy-in something of unknown length
+               if (!strncmp(kfs[i].name, path, strlen(path)))
+                       return i;
+       return -EINVAL;
+}
+
+/*
+ * Creates a process from the file pointed to by the KFS inode (index)
+ * This should take a real inode or something to point to the real location,
+ * and env_create shouldn't assume everything is contiguous
+ */
+env_t* kfs_proc_create(size_t kfs_inode)
+{
+       if (kfs_inode < 0 || kfs_inode >= MAX_KFS_FILES)
+               panic("Invalid kfs_inode.  Check you error codes!");
+       return env_create(kfs[kfs_inode].start, kfs[kfs_inode].size);
+}
index 22c7409..6d0363d 100644 (file)
@@ -17,6 +17,7 @@
 #include <workqueue.h>
 #include <syscall.h>
 #include <testing.h>
+#include <kfs.h>
 
 /*
  * Currently, if you leave this function by way of env_run (process_workqueue
@@ -30,13 +31,19 @@ void manager(void)
 
        switch (progress++) {
                case 0:
+                       // careful, should check error codes returned from kfs_lookup_path
+                       envs[0] = kfs_proc_create(kfs_lookup_path("roslib_spawn"));
+                       env_run(envs[0]);
+                       break;
+               case 1:
+                       #if 0
                        envs[0] = ENV_CREATE(roslib_proctests);
                        envs[1] = ENV_CREATE(roslib_proctests);
                        envs[2] = ENV_CREATE(roslib_proctests);
                        envs[3] = ENV_CREATE(roslib_proctests);
                        env_run(envs[0]);
                        break;
-               case 1:
+                       #endif
                case 2:
                case 3:
                #if 0
index db4fc57..42e4010 100644 (file)
@@ -19,6 +19,7 @@
 #include <pmap.h>
 #include <kdebug.h>
 #include <testing.h>
+#include <kfs.h>
 
 #include <ros/memlayout.h>
 
@@ -40,6 +41,8 @@ static command_t commands[] = {
        { "setmapperm", "Sets permissions on a VA->PA mapping", mon_setmapperm},
        { "cpuinfo", "Prints CPU diagnostics", mon_cpuinfo},
        { "nanwan", "Meet Nanwan!!", mon_nanwan},
+       { "kfs_ls", "List files in KFS", mon_kfs_ls},
+       { "kfs_run", "Create and run a program from KFS", mon_kfs_run},
 };
 #define NCOMMANDS (sizeof(commands)/sizeof(commands[0]))
 
@@ -257,6 +260,35 @@ int mon_nanwan(int argc, char **argv, trapframe_t *tf)
        return 0;
 }
 
+int mon_kfs_ls(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf)
+{
+       printk("Files in KFS:\n-------------------------------\n");
+       for (int i = 0; i < MAX_KFS_FILES; i++)
+               if (kfs[i].name[0])
+                       printk("%s\n", kfs[i].name);
+       return 0;
+}
+
+int mon_kfs_run(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf)
+{
+       if (argc != 2) {
+               printk("Usage: kfs_run FILENAME\n");
+               return 1;
+       }
+       int kfs_inode = kfs_lookup_path(argv[1]);
+       if (kfs_inode < 0) {
+               printk("Bad filename!\n");
+               return 1;
+       }
+       env_t *new_e = kfs_proc_create(kfs_inode);
+       new_e->env_status = ENV_RUNNABLE;
+       // Should never return from schedule (env_pop in there)
+       // also note you may not get the process you created, in the event there
+       // are others floating around that are runnable
+       schedule();
+       return 0;
+}
+
 /***** Kernel monitor command interpreter *****/
 
 #define WHITESPACE "\t\r\n "
index 632f5ae..5d85506 100644 (file)
@@ -1022,7 +1022,7 @@ static void *DANGEROUS user_mem_check_addr;
 // erroneous virtual address.
 //
 // Returns 0 if the user program can access this range of addresses,
-// and -E_FAULT otherwise.
+// and -EFAULT otherwise.
 //
 // Hint: The TA solution uses pgdir_walk.
 //
index b8de0a9..9efdd19 100644 (file)
@@ -17,6 +17,7 @@
 #include <pmap.h>
 #include <trap.h>
 #include <syscall.h>
+#include <kfs.h> // eventually replace this with vfs.h
 
 /* This is called from sysenter's asm, with the tf on the kernel stack. */
 void sysenter_callwrapper(struct Trapframe *tf)
@@ -225,10 +226,53 @@ static void sys_yield(env_t *e)
        schedule();
 }
 
+/*
+ * Creates a process found at the user string 'path'.  Currently uses KFS.
+ * Not runnable by default, so it needs it's status to be changed so that the
+ * next call to schedule() will try to run it.
+ * TODO: once we have a decent VFS, consider splitting this up
+ */
+static int sys_proc_create(env_t *e, const char *DANGEROUS path)
+{
+       #define MAX_PATH_LEN 256 // totally arbitrary
+       int pid = 0;
+       char tpath[MAX_PATH_LEN];
+       /*
+        * There's a bunch of issues with reading in the path, which we'll
+        * need to sort properly in the VFS.  Main concerns are TOCTOU (copy-in),
+        * whether or not it's a big deal that the pointer could be into kernel
+        * space, and resolving both of these without knowing the length of the
+        * string. (TODO)
+        */
+       strncpy(tpath, path, MAX_PATH_LEN);
+       int kfs_inode = kfs_lookup_path(tpath);
+       if (kfs_inode < 0)
+               return -EINVAL;
+       env_t *new_e = kfs_proc_create(kfs_inode);
+       return new_e->env_id; // TODO replace this with a real proc_id
+}
+
+/* Makes process PID runnable.  Consider moving the functionality to env.c */
+static error_t sys_proc_run(env_t *e, int pid)
+{
+       // TODO PIDs are currently env_id's and encapsulate these functions better
+       // TODO worry about concurrency on the statuses
+       // get the target process (techincally a neg number is invalid)
+       env_t *target = &envs[ENVX(pid)];
+       // make sure it's in the right state to be activated
+       if (target->env_status != ENV_CREATED)
+               return -EINVAL;
+       // make sure we have access (are the parent of)
+       if (target->env_parent_id != e->env_id)
+               return -EINVAL;
+       target->env_status = ENV_RUNNABLE;      
+       return 0;
+}
+
 // TODO: Build a dispatch table instead of switching on the syscallno
 // Dispatches to the correct kernel function, passing the arguments.
 intreg_t syscall(env_t* e, uint32_t syscallno, uint32_t a1, uint32_t a2,
-                uint32_t a3, uint32_t a4, uint32_t a5)
+                 uint32_t a3, uint32_t a4, uint32_t a5)
 {
        // Call the function corresponding to the 'syscallno' parameter.
        // Return any appropriate return value.
@@ -270,8 +314,9 @@ intreg_t syscall(env_t* e, uint32_t syscallno, uint32_t a1, uint32_t a2,
                        sys_yield(e);
                        return 0;
                case SYS_proc_create:
+                       return sys_proc_create(e, (char *DANGEROUS)a1);
                case SYS_proc_run:
-                       panic("Not implemented");
+                       return sys_proc_run(e, (size_t)a1);
                default:
                        // or just return -EINVAL
                        panic("Invalid syscall number %d for env %x!", syscallno, *e);
index 1db1943..7b3eefe 100644 (file)
@@ -2,6 +2,6 @@
 
 int main(int argc, char** argv)
 {
-       cprintf("goodbye, world!\n");
+       cprintf("Goodbye, world, from PID: %d!\n", env->env_id);
        return 0;
 }
diff --git a/user/apps/roslib/spawn.c b/user/apps/roslib/spawn.c
new file mode 100644 (file)
index 0000000..f5658f5
--- /dev/null
@@ -0,0 +1,29 @@
+#include <lib.h>
+#include <syswrapper.h>
+
+int main(int argc, char** argv)
+{
+       /* try some bad combos */
+       int pid = proc_create("garbagexxx");
+       cprintf("Garbage pid result: %d\n", pid);
+
+       error_t err = proc_run(2342);
+       cprintf("proc_run(2342) error: %e\n", err);
+
+       err = proc_run(-1);
+       cprintf("proc_run(-1) error: %e\n", err);
+
+
+       #define NUM_KIDS 5
+       int child_pid[NUM_KIDS];
+       cprintf("U: attempting to create hello(s)\n");
+       for (int i = 0; i < NUM_KIDS; i++)
+               child_pid[i] = proc_create("roslib_hello");
+
+       for (int i = 0; i < NUM_KIDS; i++) {
+               cprintf("U: attempting to run hello (pid: %d)\n", child_pid[i]);
+               proc_run(child_pid[i]);
+       }
+
+       return 0;
+}
index 89a5298..0b0a093 100644 (file)
@@ -10,7 +10,7 @@ error_t cache_buster_async(async_desc_t** desc, uint32_t num_writes,
                            uint32_t num_pages, uint32_t flags);
 uint32_t getcpuid(void);
 void yield(void);
-int proc_create(char* path);
+int proc_create(char *NT path);
 error_t proc_run(int pid);
 
 #endif // ROS_INC_SYSWRAPPER_H
index 2f7dc4e..4742a30 100644 (file)
@@ -46,7 +46,7 @@ void yield(void)
        return;
 }
 
-int proc_create(char* path)
+int proc_create(char *NT path)
 {
        return sys_proc_create(path);
 }