Added MAP_POPULATE
[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         printd("mmap(addr %x, len %x, prot %x, flags %x, fd %x, off %x)\n", addr,
23                len, prot, flags, fd, offset);
24         if (fd >= 0 && (flags & MAP_SHARED)) {
25                 printk("[kernel] mmap() for files requires !MAP_SHARED.\n");
26                 return (void*)-1;
27         }
28         if (fd >= 0 && (flags & MAP_ANON)) {
29                 printk("[kernel] mmap() with MAP_ANONYMOUS requires fd == -1.\n");
30                 return (void*)-1;
31         }
32
33         struct file* file = NULL;
34         if(fd != -1)
35         {
36                 file = file_open_from_fd(p,fd);
37                 if(!file)
38                         return (void*)-1;
39         }
40
41         void* result = do_mmap(p,addr,len,prot,flags,file,offset);
42
43         if(file)
44                 file_decref(file);
45
46         return result;
47 }
48
49 void *do_mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
50               struct file* file, size_t offset)
51 {
52         /* TODO: make this work, instead of a ghetto hack
53          * Find a valid range, make sure it doesn't run into the kernel
54          * make sure there's enough memory (not exceeding quotas)
55          * allocate and map the pages, update appropriate structures (vm_region)
56          * return appropriate pointer
57          * Right now, all we can do is give them the range they ask for.
58          * (or try to find one on sparc) */
59
60         if((flags & MAP_FIXED) && PGOFF(addr)) {
61                 printk("[kernel] mmap() page align your addr.\n");
62                 return (void*SAFE)TC(-1);
63         }
64
65         // TODO: grab the appropriate mm_lock
66         spin_lock_irqsave(&p->proc_lock);
67
68         int num_pages = ROUNDUP(len, PGSIZE) / PGSIZE;
69
70         if(!(flags & MAP_FIXED))
71         {
72                 addr = (uintptr_t)get_free_va_range(p->env_pgdir,addr,len);
73                 if(!addr)
74                         goto mmap_abort;
75
76                 assert(!PGOFF(addr));
77                 assert(addr + num_pages*PGSIZE <= USTACKBOT);
78         }
79
80         // get a list of pfault_info_t's and pte's a priori,
81         // because if their allocation fails, we could end up
82         // in an inconsistent state
83
84         pfault_info_t** pfis = kmalloc(sizeof(pfault_info_t*)*num_pages,0);
85         pte_t** ptes = kmalloc(sizeof(pte_t*)*num_pages,0);
86         if(!pfis || !ptes)
87         {
88                 kfree(ptes);
89                 kfree(pfis);
90                 goto mmap_abort;
91         }
92
93         for(int i = 0; i < num_pages; i++)
94         {
95                 pfis[i] = pfault_info_alloc(file);
96                 ptes[i] = pgdir_walk(p->env_pgdir,(char*)addr+i*PGSIZE,1);
97
98                 // cleanup allocated pfault_info_t's on allocation failure
99                 if(!pfis[i] || !ptes[i])
100                 {
101                         int free_until = pfis[i] ? i+1 : i;
102                         for(int j = 0; j < free_until; j++)
103                                 pfault_info_free(pfis[j]);
104
105                         kfree(ptes);
106                         kfree(pfis);
107                         goto mmap_abort;
108                 }
109         }
110
111         // make the lazy mapping finally
112         for(int i = 0; i < num_pages; i++)
113         {
114                 // free an old page that was present here
115                 if(PAGE_PRESENT(*ptes[i]))
116                         page_decref(ppn2page(PTE2PPN(*ptes[i])));
117                 // free the pfault_info for a page that wasn't faulted-in yet
118                 else if(PAGE_PAGED_OUT(*ptes[i]))
119                         pfault_info_free(PTE2PFAULT_INFO(*ptes[i]));
120
121                 pfis[i]->file = file;
122                 pfis[i]->pgoff = offset+i;
123                 pfis[i]->read_len = PGSIZE;
124                 // zero-fill end of last page
125                 if(i == num_pages-1 && len % PGSIZE)
126                         pfis[i]->read_len = len % PGSIZE;
127                 pfis[i]->prot = prot;
128                 *ptes[i] = PFAULT_INFO2PTE(pfis[i]);
129         }
130
131         kfree(ptes);
132         kfree(pfis);
133
134         // TODO: release the appropriate mm_lock
135         spin_unlock_irqsave(&p->proc_lock);
136
137         // fault in pages now if MAP_POPULATE.  die on failure.
138         if(flags & MAP_POPULATE)
139                 for(int i = 0; i < num_pages; i++)
140                         if(handle_page_fault(p,addr+i*PGSIZE,PROT_READ))
141                                 proc_destroy(p);
142
143         return (void*SAFE)TC(addr);
144
145         // TODO: if there's a failure, we should go back through the addr+len range
146         // and dealloc everything.  or at least define what we want to do if we run
147         // out of memory.
148         mmap_abort:
149                 // TODO: release the appropriate mm_lock
150                 spin_unlock_irqsave(&p->proc_lock);
151                 // not a kernel problem, like if they ask to mmap a mapped location.
152                 printk("[kernel] mmap() aborted!\n");
153                 // mmap's semantics.  we need a better error propagation system
154                 return (void*SAFE)TC(-1); // this is also ridiculous
155 }
156
157 int mprotect(struct proc* p, void* addr, size_t len, int prot)
158 {
159         printd("mprotect(addr %x, len %x, prot %x)\n",addr,len,prot);
160         if((uintptr_t)addr % PGSIZE || (len == 0 && (prot & PROT_UNMAP)))
161         {
162                 set_errno(current_tf,EINVAL);
163                 return -1;
164         }
165
166         // overflow of end is handled in the for loop's parameters
167         char* end = ROUNDUP((char*)addr+len,PGSIZE);
168         if(addr >= (void*)UTOP || end > (char*)UTOP)
169         {
170                 set_errno(current_tf, (prot & PROT_UNMAP) ? EINVAL : ENOMEM);
171                 return -1;
172         }
173
174         spin_lock_irqsave(&p->proc_lock);
175
176         int newperm = (prot & PROT_WRITE) ? PTE_USER_RW :
177                       (prot & (PROT_READ|PROT_EXEC)) ? PTE_USER_RO : 0;
178
179         for(char* a = (char*)addr; a < end; a += PGSIZE)
180         {
181                 pte_t* pte = pgdir_walk(p->env_pgdir,a,0);
182
183                 // unmapped page? error out, behavior undefined (per POSIX)
184                 if(!pte || PAGE_UNMAPPED(*pte))
185                 {
186                         set_errno(current_tf,ENOMEM);
187                         return -1;
188                 }
189                 // common case: the page is present
190                 else if(PAGE_PRESENT(*pte))
191                 {
192                         // TODO: do munmap() in munmap(), instead of mprotect()
193                         if(prot & PROT_UNMAP)
194                         {
195                                 page_t* page = ppn2page(PTE2PPN(*pte));
196                                 *pte = 0;
197                                 page_decref(page);
198                         }
199                         else
200                         {
201                                 *pte = (*pte & ~PTE_PERM) | newperm;
202                         }
203                 }
204                 // or, the page might be mapped, but not yet faulted-in
205                 else // PAGE_PAGED_OUT(*pte)
206                 {
207                         if(prot & PROT_UNMAP)
208                         {
209                                 pfault_info_free(PTE2PFAULT_INFO(*pte));
210                                 *pte = 0;
211                         }
212                         else
213                                 PTE2PFAULT_INFO(*pte)->prot = prot;
214                 }
215         }
216
217         spin_unlock_irqsave(&p->proc_lock);
218
219         //TODO: TLB shootdown - needs to be process wide
220         tlbflush();
221         return 0;
222 }
223
224 int munmap(struct proc* p, void* addr, size_t len)
225 {
226         return mprotect(p, addr, len, PROT_UNMAP);
227 }
228
229 int handle_page_fault(struct proc* p, uintptr_t va, int prot)
230 {
231         int ret = -1;
232         va = ROUNDDOWN(va,PGSIZE);
233
234         if(prot != PROT_READ && prot != PROT_WRITE && prot != PROT_EXEC)
235                 panic("bad prot!");
236
237         spin_lock_irqsave(&p->proc_lock);
238
239         /// find offending PTE
240         pte_t* ppte = pgdir_walk(p->env_pgdir,(void*)va,0);
241         // if PTE is NULL, this is a fault that should kill the process
242         if(!ppte)
243                 goto out;
244
245         pte_t pte = *ppte;
246
247         // if PTE is present, why did we fault?
248         if(PAGE_PRESENT(pte))
249         {
250                 int perm = pte & PTE_PERM;
251                 // a race is possible: the page might have been faulted in by
252                 // another core already, in which case we should just return.
253                 // otherwise, it's a fault that should kill the user
254                 switch(prot)
255                 {
256                         case PROT_READ:
257                         case PROT_EXEC:
258                                 if(perm == PTE_USER_RO || perm == PTE_USER_RW)
259                                         ret = 0;
260                                 goto out;
261                         case PROT_WRITE:
262                                 if(perm == PTE_USER_RW)
263                                         ret = 0;
264                                 goto out;
265                 }
266                 // can't get here
267         }
268
269         // if the page isn't present, kill the user
270         if(PAGE_UNMAPPED(pte))
271                 goto out;
272
273         // now, we know that PAGE_PAGED_OUT(pte) is true
274         pfault_info_t* info = PTE2PFAULT_INFO(pte);
275
276         // allocate a page; maybe zero-fill it
277         int zerofill = info->file == NULL;
278         page_t* a_page;
279         if(upage_alloc(p, &a_page, zerofill))
280                 goto out;
281
282         // if this isn't a zero-filled page, read it in from file
283         if(!zerofill)
284         {
285                 int read_len = file_read_page(info->file,page2pa(a_page),info->pgoff);
286                 if(read_len < 0)
287                 {
288                         page_free(a_page);
289                         goto out;
290                 }
291
292                 // if we read too much, zero that part out
293                 if(info->read_len < read_len)
294                         memset(page2kva(a_page)+info->read_len,0,read_len-info->read_len);
295
296                 // if this is an executable page, we might have to flush
297                 // the instruction cache if our HW requires it
298                 if(info->prot & PROT_EXEC)
299                         icache_flush_page((void*)va,page2kva(a_page));
300         }
301
302         int perm = (info->prot & PROT_WRITE) ? PTE_USER_RW :
303                    (info->prot & (PROT_READ|PROT_EXEC))  ? PTE_USER_RO : 0;
304
305         // update the page table
306         page_incref(a_page);
307         *ppte = PTE(page2ppn(a_page),PTE_P | perm);
308
309         pfault_info_free(info);
310         ret = 0;
311
312 out:
313         spin_unlock_irqsave(&p->proc_lock);
314         tlbflush();
315         return ret;
316 }
317
318 struct kmem_cache* pfault_info_cache;
319 void mmap_init(void)
320 {
321         pfault_info_cache = kmem_cache_create("pfault_info",
322                                               sizeof(pfault_info_t), 8, 0, 0, 0);
323 }
324
325 pfault_info_t* pfault_info_alloc(struct file* file)
326 {
327         if(file)
328                 file_incref(file);
329         return kmem_cache_alloc(pfault_info_cache,0);
330 }
331
332 void pfault_info_free(pfault_info_t* pfi)
333 {
334         if(pfi->file)
335                 file_decref(pfi->file);
336         kmem_cache_free(pfault_info_cache,pfi);
337 }
338