Fixed EXPER_TRADPROC mmaping
[akaros.git] / kern / src / mm.c
1 /*
2  * Copyright (c) 2009 The Regents of the University of California
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  * See LICENSE for details.
5  *
6  */
7
8 #include <frontend.h>
9 #include <ros/common.h>
10 #include <ros/mman.h>
11 #include <pmap.h>
12 #include <mm.h>
13 #include <process.h>
14 #include <stdio.h>
15 #include <syscall.h>
16 #include <slab.h>
17 #include <kmalloc.h>
18
19 void *mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
20            int fd, size_t offset)
21 {
22 #ifdef __CONFIG_EXPER_TRADPROC__
23 if (!is_real_proc(p))
24         p = p->true_proc;
25 #endif /* __CONFIG_EXPER_TRADPROC__ */
26
27         printd("mmap(addr %x, len %x, prot %x, flags %x, fd %x, off %x)\n", addr,
28                len, prot, flags, fd, offset);
29         if (fd >= 0 && (flags & MAP_SHARED)) {
30                 printk("[kernel] mmap() for files requires !MAP_SHARED.\n");
31                 return (void*)-1;
32         }
33         if (fd >= 0 && (flags & MAP_ANON)) {
34                 printk("[kernel] mmap() with MAP_ANONYMOUS requires fd == -1.\n");
35                 return (void*)-1;
36         }
37         if((flags & MAP_FIXED) && PGOFF(addr)) {
38                 printk("[kernel] mmap() page align your addr.\n");
39                 return (void*SAFE)TC(-1);
40         }
41
42         struct file* file = NULL;
43         if(fd != -1)
44         {
45                 file = file_open_from_fd(p,fd);
46                 if(!file)
47                         return (void*)-1;
48         }
49
50         void* result = do_mmap(p,addr,len,prot,flags,file,offset);
51
52         if(file)
53                 file_decref(file);
54
55         return result;
56 }
57
58 void *do_mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
59               struct file* file, size_t offset)
60 {
61 #ifdef __CONFIG_EXPER_TRADPROC__
62 if (!is_real_proc(p))
63         p = p->true_proc;
64 #endif /* __CONFIG_EXPER_TRADPROC__ */
65
66         // TODO: grab the appropriate mm_lock
67         spin_lock(&p->proc_lock);
68         void* ret = __do_mmap(p,addr,len,prot,flags,file,offset);
69         spin_unlock(&p->proc_lock);
70         return ret;
71 }
72
73 void *__do_mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
74                 struct file* file, size_t offset)
75 {
76 #ifdef __CONFIG_EXPER_TRADPROC__
77 if (!is_real_proc(p))
78         p = p->true_proc;
79 #endif /* __CONFIG_EXPER_TRADPROC__ */
80
81         int num_pages = ROUNDUP(len, PGSIZE) / PGSIZE;
82
83 #ifndef __CONFIG_DEMAND_PAGING__
84         flags |= MAP_POPULATE;
85 #endif
86         
87         if(!(flags & MAP_FIXED))
88         {
89                 addr = (uintptr_t)get_free_va_range(p->env_pgdir,addr,len);
90                 if(!addr)
91                         goto mmap_abort;
92
93                 assert(!PGOFF(addr));
94                 assert(addr + num_pages*PGSIZE <= USTACKBOT);
95         }
96
97         // get a list of pfault_info_t's and pte's a priori,
98         // because if their allocation fails, we could end up
99         // in an inconsistent state
100
101         pfault_info_t** pfis = kmalloc(sizeof(pfault_info_t*)*num_pages,0);
102         pte_t** ptes = kmalloc(sizeof(pte_t*)*num_pages,0);
103         if(!pfis || !ptes)
104         {
105                 kfree(ptes);
106                 kfree(pfis);
107                 goto mmap_abort;
108         }
109
110         for(int i = 0; i < num_pages; i++)
111         {
112                 pfis[i] = pfault_info_alloc(file);
113                 ptes[i] = pgdir_walk(p->env_pgdir,(char*)addr+i*PGSIZE,1);
114
115                 // cleanup allocated pfault_info_t's on allocation failure
116                 if(!pfis[i] || !ptes[i])
117                 {
118                         int free_until = pfis[i] ? i+1 : i;
119                         for(int j = 0; j < free_until; j++)
120                                 pfault_info_free(pfis[j]);
121
122                         kfree(ptes);
123                         kfree(pfis);
124                         goto mmap_abort;
125                 }
126         }
127
128         // make the lazy mapping finally
129         for(int i = 0; i < num_pages; i++)
130         {
131                 // free an old page that was present here
132                 if(PAGE_PRESENT(*ptes[i]))
133                         page_decref(ppn2page(PTE2PPN(*ptes[i])));
134                 // free the pfault_info for a page that wasn't faulted-in yet
135                 else if(PAGE_PAGED_OUT(*ptes[i]))
136                         pfault_info_free(PTE2PFAULT_INFO(*ptes[i]));
137
138                 pfis[i]->file = file;
139                 pfis[i]->pgoff = offset+i;
140                 pfis[i]->read_len = PGSIZE;
141                 // zero-fill end of last page
142                 if(i == num_pages-1 && len % PGSIZE)
143                         pfis[i]->read_len = len % PGSIZE;
144                 pfis[i]->prot = prot;
145                 *ptes[i] = PFAULT_INFO2PTE(pfis[i]);
146         }
147
148         kfree(ptes);
149         kfree(pfis);
150
151         // fault in pages now if MAP_POPULATE.  die on failure.
152         if(flags & MAP_POPULATE)
153                 for(int i = 0; i < num_pages; i++)
154                         if(__handle_page_fault(p,addr+i*PGSIZE,PROT_READ))
155                                 proc_destroy(p);
156
157         return (void*SAFE)TC(addr);
158
159         // TODO: if there's a failure, we should go back through the addr+len range
160         // and dealloc everything.  or at least define what we want to do if we run
161         // out of memory.
162         mmap_abort:
163                 // not a kernel problem, like if they ask to mmap a mapped location.
164                 printk("[kernel] mmap() aborted!\n");
165                 // mmap's semantics.  we need a better error propagation system
166                 return (void*SAFE)TC(-1); // this is also ridiculous
167 }
168
169 int mprotect(struct proc* p, void* addr, size_t len, int prot)
170 {
171 #ifdef __CONFIG_EXPER_TRADPROC__
172 if (!is_real_proc(p))
173         p = p->true_proc;
174 #endif /* __CONFIG_EXPER_TRADPROC__ */
175
176         printd("mprotect(addr %x, len %x, prot %x)\n",addr,len,prot);
177         if((uintptr_t)addr % PGSIZE || (len == 0 && (prot & PROT_UNMAP)))
178         {
179                 set_errno(current_tf,EINVAL);
180                 return -1;
181         }
182
183         // overflow of end is handled in the for loop's parameters
184         char* end = ROUNDUP((char*)addr+len,PGSIZE);
185         if(addr >= (void*)UTOP || end > (char*)UTOP)
186         {
187                 set_errno(current_tf, (prot & PROT_UNMAP) ? EINVAL : ENOMEM);
188                 return -1;
189         }
190
191         spin_lock(&p->proc_lock);
192         int ret = __mprotect(p,addr,len,prot);
193         spin_unlock(&p->proc_lock);
194
195         return ret;
196 }
197
198 int __mprotect(struct proc* p, void* addr, size_t len, int prot)
199 {
200 #ifdef __CONFIG_EXPER_TRADPROC__
201 if (!is_real_proc(p))
202         p = p->true_proc;
203 #endif /* __CONFIG_EXPER_TRADPROC__ */
204
205         int newperm = (prot & PROT_WRITE) ? PTE_USER_RW :
206                       (prot & (PROT_READ|PROT_EXEC)) ? PTE_USER_RO : 0;
207
208         char* end = ROUNDUP((char*)addr+len,PGSIZE);
209         for(char* a = (char*)addr; a < end; a += PGSIZE)
210         {
211                 pte_t* pte = pgdir_walk(p->env_pgdir,a,0);
212
213                 // unmapped page? error out, behavior undefined (per POSIX)
214                 if(!pte || PAGE_UNMAPPED(*pte))
215                 {
216                         set_errno(current_tf,ENOMEM);
217                         return -1;
218                 }
219                 // common case: the page is present
220                 else if(PAGE_PRESENT(*pte))
221                 {
222                         // TODO: do munmap() in munmap(), instead of mprotect()
223                         if(prot & PROT_UNMAP)
224                         {
225                                 page_t* page = ppn2page(PTE2PPN(*pte));
226                                 *pte = 0;
227                                 page_decref(page);
228                         }
229                         else
230                         {
231                                 *pte = (*pte & ~PTE_PERM) | newperm;
232                         }
233                 }
234                 // or, the page might be mapped, but not yet faulted-in
235                 else // PAGE_PAGED_OUT(*pte)
236                 {
237                         if(prot & PROT_UNMAP)
238                         {
239                                 pfault_info_free(PTE2PFAULT_INFO(*pte));
240                                 *pte = 0;
241                         }
242                         else
243                                 PTE2PFAULT_INFO(*pte)->prot = prot;
244                 }
245         }
246
247         /* TODO: (TLB) make this take a sensible range.  For now, it doesn't matter
248          * since we ignore it in the process code.  Also, make sure you are holding
249          * the proc_lock when calling this. */
250         __proc_tlbshootdown(p, 0, 0xffffffff);
251         return 0;
252 }
253
254 int munmap(struct proc* p, void* addr, size_t len)
255 {
256 #ifdef __CONFIG_EXPER_TRADPROC__
257 if (!is_real_proc(p))
258         p = p->true_proc;
259 #endif /* __CONFIG_EXPER_TRADPROC__ */
260
261         return mprotect(p, addr, len, PROT_UNMAP);
262 }
263
264 int __munmap(struct proc* p, void* addr, size_t len)
265 {
266 #ifdef __CONFIG_EXPER_TRADPROC__
267 if (!is_real_proc(p))
268         p = p->true_proc;
269 #endif /* __CONFIG_EXPER_TRADPROC__ */
270
271         return __mprotect(p, addr, len, PROT_UNMAP);
272 }
273
274 int handle_page_fault(struct proc* p, uintptr_t va, int prot)
275 {
276 #ifdef __CONFIG_EXPER_TRADPROC__
277 if (!is_real_proc(p))
278         p = p->true_proc;
279 #endif /* __CONFIG_EXPER_TRADPROC__ */
280
281         va = ROUNDDOWN(va,PGSIZE);
282
283         if(prot != PROT_READ && prot != PROT_WRITE && prot != PROT_EXEC)
284                 panic("bad prot!");
285
286         spin_lock(&p->proc_lock);
287         int ret = __handle_page_fault(p,va,prot);
288         spin_unlock(&p->proc_lock);
289         return ret;
290 }
291         
292 int __handle_page_fault(struct proc* p, uintptr_t va, int prot)
293 {
294 #ifdef __CONFIG_EXPER_TRADPROC__
295 if (!is_real_proc(p))
296         p = p->true_proc;
297 #endif /* __CONFIG_EXPER_TRADPROC__ */
298
299         int ret = -1;
300         // find offending PTE
301         pte_t* ppte = pgdir_walk(p->env_pgdir,(void*)va,0);
302         // if PTE is NULL, this is a fault that should kill the process
303         if(!ppte)
304                 goto out;
305
306         pte_t pte = *ppte;
307
308         // if PTE is present, why did we fault?
309         if(PAGE_PRESENT(pte))
310         {
311                 int perm = pte & PTE_PERM;
312                 // a race is possible: the page might have been faulted in by
313                 // another core already, in which case we should just return.
314                 // otherwise, it's a fault that should kill the user
315                 switch(prot)
316                 {
317                         case PROT_READ:
318                         case PROT_EXEC:
319                                 if(perm == PTE_USER_RO || perm == PTE_USER_RW)
320                                         ret = 0;
321                                 goto out;
322                         case PROT_WRITE:
323                                 if(perm == PTE_USER_RW)
324                                         ret = 0;
325                                 goto out;
326                 }
327                 // can't get here
328         }
329
330         // if the page isn't present, kill the user
331         if(PAGE_UNMAPPED(pte))
332                 goto out;
333
334         // now, we know that PAGE_PAGED_OUT(pte) is true
335         pfault_info_t* info = PTE2PFAULT_INFO(pte);
336
337         // allocate a page; maybe zero-fill it
338         int zerofill = info->file == NULL;
339         page_t* a_page;
340         if(upage_alloc(p, &a_page, zerofill))
341                 goto out;
342
343         // if this isn't a zero-filled page, read it in from file
344         if(!zerofill)
345         {
346                 int read_len = file_read_page(info->file,page2pa(a_page),info->pgoff);
347                 if(read_len < 0)
348                 {
349                         page_free(a_page);
350                         goto out;
351                 }
352
353                 // if we read too much, zero that part out
354                 if(info->read_len < read_len)
355                         memset(page2kva(a_page)+info->read_len,0,read_len-info->read_len);
356
357                 // if this is an executable page, we might have to flush
358                 // the instruction cache if our HW requires it
359                 if(info->prot & PROT_EXEC)
360                         icache_flush_page((void*)va,page2kva(a_page));
361         }
362
363         int perm = (info->prot & PROT_WRITE) ? PTE_USER_RW :
364                    (info->prot & (PROT_READ|PROT_EXEC))  ? PTE_USER_RO : 0;
365
366         // update the page table
367         page_incref(a_page);
368         *ppte = PTE(page2ppn(a_page),PTE_P | perm);
369
370         pfault_info_free(info);
371         ret = 0;
372
373 out:
374         tlbflush();
375         return ret;
376 }
377
378 struct kmem_cache* pfault_info_cache;
379 void mmap_init(void)
380 {
381         pfault_info_cache = kmem_cache_create("pfault_info",
382                                               sizeof(pfault_info_t), 8, 0, 0, 0);
383 }
384
385 pfault_info_t* pfault_info_alloc(struct file* file)
386 {
387         if(file)
388                 file_incref(file);
389         return kmem_cache_alloc(pfault_info_cache,0);
390 }
391
392 void pfault_info_free(pfault_info_t* pfi)
393 {
394         if(pfi->file)
395                 file_decref(pfi->file);
396         kmem_cache_free(pfault_info_cache,pfi);
397 }
398