9ns: ensure the parent of a rename target is a directory
[akaros.git] / kern / src / env.c
1 /* See COPYRIGHT for copyright information. */
2
3 #include <arch/arch.h>
4 #include <arch/mmu.h>
5 #include <bitmask.h>
6 #include <elf.h>
7 #include <smp.h>
8 #include <atomic.h>
9 #include <string.h>
10 #include <assert.h>
11 #include <process.h>
12 #include <pmap.h>
13 #include <trap.h>
14 #include <monitor.h>
15 #include <manager.h>
16 #include <stdio.h>
17 #include <schedule.h>
18 #include <kmalloc.h>
19 #include <mm.h>
20
21 #include <ros/syscall.h>
22 #include <error.h>
23
24 atomic_t num_envs;
25
26 // Initialize the kernel virtual memory layout for environment e.
27 // Allocate a page directory, set e->env_pgdir and e->env_cr3 accordingly,
28 // and initialize the kernel portion of the new environment's address space.
29 // Do NOT (yet) map anything into the user portion
30 // of the environment's virtual address space.
31 //
32 // Returns 0 on success, < 0 on error.  Errors include:
33 //      -ENOMEM if page directory or table could not be allocated.
34 //
35 int env_setup_vm(env_t *e)
36 {
37         int i, ret;
38         static page_t *shared_page = 0;
39
40         if ((ret = arch_pgdir_setup(boot_pgdir, &e->env_pgdir)))
41                 return ret;
42         e->env_cr3 = arch_pgdir_get_cr3(e->env_pgdir);
43
44         /* These need to be contiguous, so the kernel can alias them.  Note the
45          * pages return with a refcnt, but it's okay to insert them since we
46          * free them manually when the process is cleaned up. */
47         if (!(e->procinfo = kpages_alloc(PROCINFO_NUM_PAGES * PGSIZE,
48                                          MEM_WAIT)))
49                 goto env_setup_vm_error_i;
50         if (!(e->procdata = kpages_alloc(PROCDATA_NUM_PAGES * PGSIZE,
51                                          MEM_WAIT)))
52                 goto env_setup_vm_error_d;
53         /* Normally we would 0 the pages here.  We handle it in proc_init_proc*.
54          * Do not start the process without calling those. */
55         for (int i = 0; i < PROCINFO_NUM_PAGES; i++) {
56                 if (page_insert(e->env_pgdir,
57                                 kva2page((void*)e->procinfo + i * PGSIZE),
58                                 (void*)(UINFO + i * PGSIZE), PTE_USER_RO) < 0)
59                         goto env_setup_vm_error;
60         }
61         for (int i = 0; i < PROCDATA_NUM_PAGES; i++) {
62                 if (page_insert(e->env_pgdir,
63                                 kva2page((void*)e->procdata + i * PGSIZE),
64                                 (void*)(UDATA + i * PGSIZE), PTE_USER_RW) < 0)
65                         goto env_setup_vm_error;
66         }
67         for (int i = 0; i < PROCGINFO_NUM_PAGES; i++) {
68                 if (page_insert(e->env_pgdir,
69                                 kva2page((void*)&__proc_global_info
70                                          + i * PGSIZE),
71                                 (void*)(UGINFO + i * PGSIZE), PTE_USER_RO) < 0)
72                         goto env_setup_vm_error;
73         }
74         /* Finally, set up the Global Shared Data page for all processes.  Can't
75          * be trusted, but still very useful at this stage for us.  Consider
76          * removing when we have real processes (TODO).
77          *
78          * Note the page is alloced only the first time through, and its ref is
79          * stored in shared_page. */
80         if (!shared_page) {
81                 if (upage_alloc(e, &shared_page, 1) < 0)
82                         goto env_setup_vm_error;
83         }
84         if (page_insert(e->env_pgdir, shared_page, (void*)UGDATA, PTE_USER_RW)
85             < 0)
86                 goto env_setup_vm_error;
87
88         return 0;
89
90 env_setup_vm_error:
91         kpages_free(e->procdata, PROCDATA_NUM_PAGES * PGSIZE);
92 env_setup_vm_error_d:
93         kpages_free(e->procinfo, PROCINFO_NUM_PAGES * PGSIZE);
94 env_setup_vm_error_i:
95         env_user_mem_free(e, 0, UVPT);
96         env_pagetable_free(e);
97         return -ENOMEM;
98 }
99
100 /* Frees (decrefs) all memory mapped in the given range */
101 void env_user_mem_free(env_t* e, void* start, size_t len)
102 {
103         assert((uintptr_t)start + len <= UVPT);
104         int user_page_free(env_t* e, pte_t pte, void* va, void* arg)
105         {
106                 if (!pte_is_mapped(pte))
107                         return 0;
108                 page_t *page = pa2page(pte_get_paddr(pte));
109                 pte_clear(pte);
110                 page_decref(page);
111                 /* TODO: consider other states here (like !P, yet still tracking
112                  * a page, for VM tricks, page map stuff, etc.  Should be okay:
113                  * once we're freeing, everything else about this proc is dead.
114                  * */
115                 return 0;
116         }
117
118         env_user_mem_walk(e,start,len,&user_page_free,NULL);
119         tlbflush();
120 }
121
122 void set_username(struct username *u, char *name)
123 {
124         ERRSTACK(1);
125
126         spin_lock(&u->name_lock);
127
128         if (waserror()) {
129                 spin_unlock(&u->name_lock);
130                 nexterror();
131         }
132
133         __set_username(u, name);
134
135         poperror();
136         spin_unlock(&u->name_lock);
137 }
138
139 /*
140  * This function exists so that you can do your own locking - do not use it
141  * without locking the username's spinlock yourself.
142  */
143 void __set_username(struct username *u, char *name)
144 {
145         if (!name)
146                 error(EINVAL, "New username is NULL");
147
148         if (strlen(name) > sizeof(u->name) - 1)
149                 error(EINVAL,
150                       "New username for process more than %d chars long",
151                       sizeof(u->name) - 1);
152
153         // 'backward' copy since reads aren't protected
154         u->name[0] = 0;
155         wmb(); // ensure user.name="" before writing the rest of the new name
156         strlcpy(&u->name[1], &name[1], sizeof(u->name));
157         wmb(); // ensure new name is written before writing first byte
158         u->name[0] = name[0];
159 }