346e222241a0a076c7e1fcbad0a85e700decc641
[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
17 void *mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
18            int fd, size_t offset)
19 {
20         printd("mmap(addr %x, len %x, prot %x, flags %x, fd %x, off %x)\n", addr,
21                len, prot, flags, fd, offset);
22         if (fd >= 0 && (flags & MAP_SHARED)) {
23                 printk("[kernel] mmap() for files requires !MAP_SHARED.\n");
24                 return (void*)-1;
25         }
26         if (fd >= 0 && (flags & MAP_ANON)) {
27                 printk("[kernel] mmap() with MAP_ANONYMOUS requires fd == -1.\n");
28                 return (void*)-1;
29         }
30
31         struct file* file = file_open_from_fd(p,fd);
32         if(!file)
33                 return (void*)-1;
34
35         return do_mmap(p,addr,len,prot,flags,file,offset);
36 }
37
38 void *do_mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
39               struct file* file, size_t offset)
40 {
41         /* TODO: make this work, instead of a ghetto hack
42          * Find a valid range, make sure it doesn't run into the kernel
43          * make sure there's enough memory (not exceeding quotas)
44          * allocate and map the pages, update appropriate structures (vm_region)
45          * return appropriate pointer
46          * Right now, all we can do is give them the range they ask for.
47          * (or try to find one on sparc) */
48
49         if((flags & MAP_FIXED) && PGOFF(addr)) {
50                 printk("[kernel] mmap() page align your addr.\n");
51                 return (void*SAFE)TC(-1);
52         }
53
54         // TODO: grab the appropriate mm_lock
55         spin_lock_irqsave(&p->proc_lock);
56
57         int num_pages = ROUNDUP(len, PGSIZE) / PGSIZE;
58
59         if(!(flags & MAP_FIXED))
60         {
61                 addr = (uintptr_t)get_free_va_range(p->env_pgdir,addr,len);
62                 if(!addr)
63                         goto mmap_abort;
64
65                 assert(!PGOFF(addr));
66                 assert(addr + num_pages*PGSIZE <= USTACKBOT);
67         }
68
69         page_t *a_page;
70         for (int i = 0; i < num_pages; i++) {
71                 if (upage_alloc(p, &a_page, 1))
72                         goto mmap_abort;
73
74                 // This is dumb--should not read until faulted in.
75                 // This is just to get it correct at first
76                 if(!(flags & MAP_ANON))
77                 {
78                         if(file_read_page(file,page2pa(a_page),offset+i) < 0)
79                                 goto mmap_abort;
80
81                         // zero-fill end of last page
82                         if(len % PGSIZE && i == num_pages-1)
83                                 memset(page2kva(a_page)+len%PGSIZE,0,PGSIZE-len%PGSIZE);
84                 }
85
86                 // TODO: TLB shootdown if replacing an old mapping
87                 // TODO: handle all PROT flags
88                 if (page_insert(p->env_pgdir, a_page, (void*SNT)(addr + i*PGSIZE),
89                                 (prot & PROT_WRITE) ? PTE_USER_RW : PTE_USER_RO)) {
90                         page_free(a_page);
91                         goto mmap_abort;
92                 }
93         }
94
95         // TODO: release the appropriate mm_lock
96         spin_unlock_irqsave(&p->proc_lock);
97         return (void*SAFE)TC(addr);
98
99         // TODO: if there's a failure, we should go back through the addr+len range
100         // and dealloc everything.  or at least define what we want to do if we run
101         // out of memory.
102         mmap_abort:
103                 // TODO: release the appropriate mm_lock
104                 spin_unlock_irqsave(&p->proc_lock);
105                 // not a kernel problem, like if they ask to mmap a mapped location.
106                 printk("[kernel] mmap() aborted!\n");
107                 // mmap's semantics.  we need a better error propagation system
108                 return (void*SAFE)TC(-1); // this is also ridiculous
109 }
110
111 int mprotect(struct proc* p, void* addr, size_t len, int prot)
112 {
113         printd("mprotect(addr %x, len %x, prot %x)\n",addr,len,prot);
114         if((uintptr_t)addr % PGSIZE || (len == 0 && (prot & PROT_UNMAP)))
115         {
116                 set_errno(current_tf,EINVAL);
117                 return -1;
118         }
119
120         // overflow of end is handled in the for loop's parameters
121         char* end = ROUNDUP((char*)addr+len,PGSIZE);
122         if(addr >= (void*)UTOP || end > (char*)UTOP)
123         {
124                 set_errno(current_tf, (prot & PROT_UNMAP) ? EINVAL : ENOMEM);
125                 return -1;
126         }
127
128         spin_lock_irqsave(&p->proc_lock);
129
130         int newperm = (prot & PROT_WRITE) ? PTE_USER_RW :
131                       (prot & (PROT_READ|PROT_EXEC)) ? PTE_USER_RO : 0;
132
133         for(char* a = (char*)addr; a < end; a += PGSIZE)
134         {
135                 pte_t* pte = pgdir_walk(p->env_pgdir,a,0);
136                 if(pte && *pte & PTE_P)
137                 {
138                         // TODO: do munmap() in munmap(), instead of mprotect()
139                         if(prot & PROT_UNMAP)
140                         {
141                                 page_t* page = ppn2page(PTE2PPN(*pte));
142                                 *pte = 0;
143                                 page_decref(page);
144                         }
145                         else
146                                 *pte = (*pte & ~PTE_PERM) | newperm;
147                 }
148                 else
149                 {
150                         set_errno(current_tf,ENOMEM);
151                         return -1;
152                 }
153         }
154
155         spin_unlock_irqsave(&p->proc_lock);
156
157         //TODO: TLB shootdown - needs to be process wide
158         tlbflush();
159         return 0;
160 }
161
162 int munmap(struct proc* p, void* addr, size_t len)
163 {
164         return mprotect(p, addr, len, PROT_UNMAP);
165 }
166
167 int handle_page_fault(struct proc* p, uintptr_t va, int prot)
168 {
169         int ret = -1;
170         va = ROUNDDOWN(va,PGSIZE);
171
172         spin_lock_irqsave(&p->proc_lock);
173
174
175 out:
176         spin_unlock_irqsave(&p->proc_lock);
177         return ret;
178 }
179