Xen Ring Buffers
[akaros.git] / kern / env.c
index 1c7257c..d1aecb6 100644 (file)
@@ -9,15 +9,18 @@
 #include <inc/string.h>
 #include <inc/assert.h>
 #include <inc/elf.h>
+#include <inc/syscall.h>
 
 #include <kern/env.h>
 #include <kern/pmap.h>
 #include <kern/trap.h>
 #include <kern/monitor.h>
+#include <kern/apic.h>
+#include <kern/smp.h>
 
-struct Env *envs = NULL;               // All environments
-struct Env *curenv = NULL;             // The current env
-static struct Env_list env_free_list;  // Free list
+env_t *envs = NULL;            // All environments
+env_t *curenv = NULL;          // The current env
+static env_list_t env_free_list;       // Free list
 
 #define ENVGENSHIFT    12              // >= LOGNENV
 
@@ -30,9 +33,9 @@ static struct Env_list env_free_list; // Free list
 //   On error, sets *env_store to NULL.
 //
 int
-envid2env(envid_t envid, struct Env **env_store, bool checkperm)
+envid2env(envid_t envid, env_t **env_store, bool checkperm)
 {
-       struct Env *e;
+       env_t *e;
 
        // If envid is zero, return the current environment.
        if (envid == 0) {
@@ -41,7 +44,7 @@ envid2env(envid_t envid, struct Env **env_store, bool checkperm)
        }
 
        // Look up the Env structure via the index part of the envid,
-       // then check the env_id field in that struct Env
+       // then check the env_id field in that env_t
        // to ensure that the envid is not stale
        // (i.e., does not refer to a _previous_ environment
        // that used the same slot in the envs[] array).
@@ -95,21 +98,28 @@ env_init(void)
 //     -E_NO_MEM if page directory or table could not be allocated.
 //
 static int
-env_setup_vm(struct Env *e)
+env_setup_vm(env_t *e)
 {
        int i, r;
-       struct Page *p = NULL;
-
-       // Allocate a page for the page directory
-       if ((r = page_alloc(&p)) < 0)
+       page_t *pgdir = NULL, *pginfo = NULL, *pgdata = NULL;
+
+       // Allocate pages for the page directory, shared info, and shared data pages
+       r = page_alloc(&pgdir);
+       r = page_alloc(&pginfo);
+       r = page_alloc(&pgdata);
+       if (r < 0) {
+               page_free(pgdir);
+               page_free(pginfo);
                return r;
-       
+       }
+
        // Now, set e->env_pgdir and e->env_cr3,
        // and initialize the page directory.
        //
        // Hint:
        //    - The VA space of all envs is identical above UTOP
        //      (except at VPT and UVPT, which we've set below).
+       //      (and not for UINFO either)
        //      See inc/memlayout.h for permissions and layout.
        //      Can you use boot_pgdir as a template?  Hint: Yes.
        //      (Make sure you got the permissions right in Lab 2.)
@@ -119,26 +129,50 @@ env_setup_vm(struct Env *e)
        //      mapped above UTOP -- but you do need to increment
        //      env_pgdir's pp_ref!
 
-       p->pp_ref++;
-       e->env_pgdir = page2kva(p);
-       e->env_cr3 = page2pa(p);
+       // need to up pgdir's reference, since it will never be done elsewhere
+       pgdir->pp_ref++;
+       e->env_pgdir = page2kva(pgdir);
+       e->env_cr3 = page2pa(pgdir);
+       e->env_procinfo = page2kva(pginfo);
+       e->env_procdata = page2kva(pgdata);
 
        memset(e->env_pgdir, 0, PGSIZE);
+       memset(e->env_procinfo, 0, PGSIZE);
+       memset(e->env_procdata, 0, PGSIZE);
+
+       // Initialize the generic syscall ring buffer
+       SHARED_RING_INIT((syscall_sring_t*)e->env_procdata);
 
        // should be able to do this so long as boot_pgdir never has
        // anything put below UTOP
        memcpy(e->env_pgdir, boot_pgdir, PGSIZE);
-       
+
        // something like this.  TODO, if you want
        //memcpy(&e->env_pgdir[PDX(UTOP)], &boot_pgdir[PDX(UTOP)], PGSIZE - PDX(UTOP));
        // check with
        // assert(memcmp(e->env_pgdir, boot_pgdir, PGSIZE) == 0);
-       
+
        // VPT and UVPT map the env's own page table, with
        // different permissions.
        e->env_pgdir[PDX(VPT)]  = e->env_cr3 | PTE_P | PTE_W;
        e->env_pgdir[PDX(UVPT)] = e->env_cr3 | PTE_P | PTE_U;
 
+       // Insert the per-process info and data pages into this process's pgdir
+       // I don't want to do these two pages later (like with the stack), since
+       // the kernel wants to keep pointers to it easily.
+       // Could place all of this with a function that maps a shared memory page
+       // that can work between any two address spaces or something.
+       r = page_insert(e->env_pgdir, pginfo, (void*)UINFO, PTE_U);
+       r = page_insert(e->env_pgdir, pgdata, (void*)UDATA, PTE_U | PTE_W);
+       if (r < 0) {
+               // note that we can't currently deallocate the pages created by
+               // pgdir_walk (inside insert).  should be able to gather them up when
+               // we destroy environments and their page tables.
+               page_free(pgdir);
+               page_free(pginfo);
+               page_free(pgdata);
+               return r;
+       }
        return 0;
 }
 
@@ -151,11 +185,11 @@ env_setup_vm(struct Env *e)
 //     -E_NO_MEM on memory exhaustion
 //
 int
-env_alloc(struct Env **newenv_store, envid_t parent_id)
+env_alloc(env_t **newenv_store, envid_t parent_id)
 {
        int32_t generation;
        int r;
-       struct Env *e;
+       env_t *e;
 
        if (!(e = LIST_FIRST(&env_free_list)))
                return -E_NO_FREE_ENV;
@@ -169,7 +203,7 @@ env_alloc(struct Env **newenv_store, envid_t parent_id)
        if (generation <= 0)    // Don't create a negative env_id.
                generation = 1 << ENVGENSHIFT;
        e->env_id = generation | (e - envs);
-       
+
        // Set the basic status variables.
        e->env_parent_id = parent_id;
        e->env_status = ENV_RUNNABLE;
@@ -182,7 +216,7 @@ env_alloc(struct Env **newenv_store, envid_t parent_id)
        memset(&e->env_tf, 0, sizeof(e->env_tf));
 
        // Set up appropriate initial values for the segment registers.
-       // GD_UD is the user data segment selector in the GDT, and 
+       // GD_UD is the user data segment selector in the GDT, and
        // GD_UT is the user text segment selector (see inc/memlayout.h).
        // The low 2 bits of each segment register contains the
        // Requestor Privilege Level (RPL); 3 means user mode.
@@ -192,11 +226,19 @@ env_alloc(struct Env **newenv_store, envid_t parent_id)
        e->env_tf.tf_esp = USTACKTOP;
        e->env_tf.tf_cs = GD_UT | 3;
        // You will set e->env_tf.tf_eip later.
+       // set the env's EFLAGSs to have interrupts enabled
+       e->env_tf.tf_eflags |= 0x00000200; // bit 9 is the interrupts-enabled
 
        // commit the allocation
        LIST_REMOVE(e, env_link);
        *newenv_store = e;
 
+       // TODO: for now, the only info at procinfo is this env's struct
+       // note that we need to copy this over every time we make a change to env
+       // that we want userspace to see.  also note that we don't even want to
+       // show them all of env, only specific things like PID, PPID, etc
+       memcpy(e->env_procinfo, e, sizeof(env_t));
+
        cprintf("[%08x] new env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
        return 0;
 }
@@ -209,12 +251,12 @@ env_alloc(struct Env **newenv_store, envid_t parent_id)
 // Panic if any allocation attempt fails.
 //
 static void
-segment_alloc(struct Env *e, void *va, size_t len)
+segment_alloc(env_t *e, void *va, size_t len)
 {
        void *start, *end;
        size_t num_pages;
        int i, r;
-       struct Page *page;
+       page_t *page;
        pte_t *pte;
 
        start = ROUNDDOWN(va, PGSIZE);
@@ -237,7 +279,6 @@ segment_alloc(struct Env *e, void *va, size_t len)
                        continue;
                if ((r = page_alloc(&page)) < 0)
                        panic("segment_alloc: %e", r);
-               page->pp_ref++;
                page_insert(e->env_pgdir, page, start, PTE_U | PTE_W);
        }
 }
@@ -265,9 +306,9 @@ segment_alloc(struct Env *e, void *va, size_t len)
 //  - How might load_icode fail?  What might be wrong with the given input?
 //
 static void
-load_icode(struct Env *e, uint8_t *binary, size_t size)
+load_icode(env_t *e, uint8_t *binary, size_t size)
 {
-       // Hints: 
+       // Hints:
        //  Load each program segment into virtual memory
        //  at the address specified in the ELF section header.
        //  You should only load segments with ph->p_type == ELF_PROG_LOAD.
@@ -296,7 +337,7 @@ load_icode(struct Env *e, uint8_t *binary, size_t size)
        //  to make sure that the environment starts executing there.
        //  What?  (See env_run() and env_pop_tf() below.)
 
-       struct Elf *elfhdr = (struct Elf*)binary;
+       elf_t *elfhdr = (elf_t *)binary;
        int i, r;
 
        // is this an elf?
@@ -304,17 +345,17 @@ load_icode(struct Env *e, uint8_t *binary, size_t size)
        // make sure we have proghdrs to load
        assert(elfhdr->e_phnum);
 
-       // to actually access any pages alloc'd for this environment, we 
+       // 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
        lcr3(e->env_cr3);
 
-       struct Proghdr *phdr = (struct Proghdr*)(binary + elfhdr->e_phoff);
+       proghdr_t *phdr = (proghdr_t *)(binary + elfhdr->e_phoff);
        for (i = 0; i < elfhdr->e_phnum; i++, phdr++) {
                if (phdr->p_type != ELF_PROG_LOAD)
                        continue;
-               // seg alloc creates PTE_U|PTE_W pages.  if you ever want to change 
+               // seg alloc creates PTE_U|PTE_W pages.  if you ever want to change
                // this, there will be issues with overlapping sections
                segment_alloc(e, (void*)phdr->p_va, phdr->p_memsz);
                memcpy((void*)phdr->p_va, binary + phdr->p_offset, phdr->p_filesz);
@@ -335,14 +376,14 @@ load_icode(struct Env *e, uint8_t *binary, size_t size)
 // before running the first user-mode environment.
 // The new env's parent ID is set to 0.
 //
-// Where does the result go? 
+// Where does the result go?
 // By convention, envs[0] is the first environment allocated, so
 // whoever calls env_create simply looks for the newly created
-// environment there. 
+// environment there.
 void
 env_create(uint8_t *binary, size_t size)
 {
-       struct Env *e;
+       env_t *e;
        int r;
 
        if ((r = env_alloc(&e, 0)) < 0)
@@ -352,9 +393,9 @@ env_create(uint8_t *binary, size_t size)
 
 //
 // Frees env e and all memory it uses.
-// 
+//
 void
-env_free(struct Env *e)
+env_free(env_t *e)
 {
        pte_t *pt;
        uint32_t pdeno, pteno;
@@ -406,14 +447,23 @@ env_free(struct Env *e)
 // to the caller).
 //
 void
-env_destroy(struct Env *e) 
+env_destroy(env_t *e)
 {
        env_free(e);
 
-       int i;
+       // for old envs that die on user cores.  since env run never returns, cores
+       // never get back to their old hlt/relaxed/spin state, so we need to force
+       // them back to an idle function.
+       uint32_t id = lapic_get_id();
+       if (id) {
+               smp_idle();
+               panic("should never see me");
+       }
+       // else we're core 0 and can do the usual
+
        // ugly, but for now just linearly search through all possible
        // environments for a runnable one.
-       for (i = 0; i < NENV; i++) {
+       for (int i = 0; i < NENV; i++) {
                e = &envs[ENVX(i)];
                if (e && e->env_status == ENV_RUNNABLE)
                        env_run(e);
@@ -430,7 +480,7 @@ env_destroy(struct Env *e)
 // This function does not return.
 //
 void
-env_pop_tf(struct Trapframe *tf)
+env_pop_tf(trapframe_t *tf)
 {
        __asm __volatile("movl %0,%%esp\n"
                "\tpopal\n"
@@ -448,7 +498,7 @@ env_pop_tf(struct Trapframe *tf)
 //  (This function does not return.)
 //
 void
-env_run(struct Env *e)
+env_run(env_t *e)
 {
        // Step 1: If this is a context switch (a new environment is running),
        //         then set 'curenv' to the new environment,
@@ -462,7 +512,7 @@ env_run(struct Env *e)
        //      e->env_tf.  Go back through the code you wrote above
        //      and make sure you have set the relevant parts of
        //      e->env_tf to sensible values.
-       
+
                // would set the curenv->env_status if we had more states
        if (e != curenv) {
                curenv = e;