Major reworking to integrate cache coloring into the kernel.
[akaros.git] / kern / src / pmap.c
1 /* See COPYRIGHT for copyright information. */
2
3 #include <arch/arch.h>
4 #include <arch/mmu.h>
5
6 #include <ros/error.h>
7
8 #include <atomic.h>
9 #include <string.h>
10 #include <assert.h>
11 #include <pmap.h>
12 #include <kclock.h>
13 #include <process.h>
14 #include <stdio.h>
15
16 static void *DANGEROUS user_mem_check_addr;
17
18 //
19 // Map the physical page 'pp' at virtual address 'va'.
20 // The permissions (the low 12 bits) of the page table
21 //  entry should be set to 'perm|PTE_P'.
22 //
23 // Details
24 //   - If there is already a page mapped at 'va', it is page_remove()d.
25 //   - If necessary, on demand, allocates a page table and inserts it into
26 //     'pgdir'.
27 //   - page_incref() should be called if the insertion succeeds.
28 //   - The TLB must be invalidated if a page was formerly present at 'va'.
29 //     (this is handled in page_remove)
30 //
31 // RETURNS: 
32 //   0 on success
33 //   -ENOMEM, if page table couldn't be allocated
34 //
35 // Hint: The TA solution is implemented using pgdir_walk, page_remove,
36 // and page2pa.
37 //
38 // No support for jumbos here.  will need to be careful of trying to insert
39 // regular pages into something that was already jumbo, and the overloading
40 // of the PTE_PS and PTE_PAT flags...
41 int
42 page_insert(pde_t *pgdir, page_t *pp, void *va, int perm) 
43 {
44         pte_t* pte = pgdir_walk(pgdir, va, 1);
45         if (!pte)
46                 return -ENOMEM;
47         // need to up the ref count in case pp is already mapped at va
48         // and we don't want to page_remove (which could free pp) and then 
49         // continue as if pp wasn't freed.  moral = up the ref asap
50         page_incref(pp);
51         if (*pte & PTE_P) {
52                 page_remove(pgdir, va);
53         }
54         *pte = PTE(page2ppn(pp), PTE_P | perm);
55         return 0;
56 }
57
58 //
59 // Map the physical page 'pp' at the first virtual address that is free 
60 // in the range 'vab' to 'vae'.
61 // The permissions (the low 12 bits) of the page table entry get set to 
62 // 'perm|PTE_P'.
63 //
64 // Details
65 //   - If there is no free entry in the range 'vab' to 'vae' this 
66 //     function returns -ENOMEM.
67 //   - If necessary, on demand, this function will allocate a page table 
68 //     and inserts it into 'pgdir'.
69 //   - page_incref() should be called if the insertion succeeds.
70 //
71 // RETURNS: 
72 //   NULL, if no free va in the range (vab, vae) could be found
73 //   va,   the virtual address where pp has been mapped in the 
74 //         range (vab, vae)
75 //
76 void* page_insert_in_range(pde_t *pgdir, page_t *pp, 
77                            void *vab, void *vae, int perm) 
78 {
79         pte_t* pte = NULL;
80         void*SNT new_va;
81         
82         for(new_va = vab; new_va <= vae; new_va+= PGSIZE) {
83                 pte = pgdir_walk(pgdir, new_va, 1);
84                 if(pte != NULL && !(*pte & PTE_P)) break;
85                 else pte = NULL;
86         }
87         if (!pte) return NULL;
88         *pte = page2pa(pp) | PTE_P | perm;
89         return TC(new_va); // trusted because mapping a page is like allocation
90 }
91
92 //
93 // Return the page mapped at virtual address 'va'.
94 // If pte_store is not zero, then we store in it the address
95 // of the pte for this page.  This is used by page_remove
96 // but should not be used by other callers.
97 //
98 // Return 0 if there is no page mapped at va.
99 //
100 // Hint: the TA solution uses pgdir_walk and pa2page.
101 //
102 // For jumbos, right now this returns the first Page* in the 4MB
103 page_t *page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
104 {
105         pte_t* pte = pgdir_walk(pgdir, va, 0);
106         if (!pte || !(*pte & PTE_P))
107                 return 0;
108         if (pte_store)
109                 *pte_store = pte;
110         return pa2page(PTE_ADDR(*pte));
111 }
112
113 //
114 // Unmaps the physical page at virtual address 'va'.
115 // If there is no physical page at that address, silently does nothing.
116 //
117 // Details:
118 //   - The ref count on the physical page should decrement.
119 //   - The physical page should be freed if the refcount reaches 0.
120 //   - The pg table entry corresponding to 'va' should be set to 0.
121 //     (if such a PTE exists)
122 //   - The TLB must be invalidated if you remove an entry from
123 //     the pg dir/pg table.
124 //
125 // Hint: The TA solution is implemented using page_lookup,
126 //      tlb_invalidate, and page_decref.
127 //
128 // This may be wonky wrt Jumbo pages and decref.  
129 void
130 page_remove(pde_t *pgdir, void *va)
131 {
132         pte_t* pte;
133         page_t *page;
134         page = page_lookup(pgdir, va, &pte);
135         if (!page)
136                 return;
137         *pte = 0;
138         tlb_invalidate(pgdir, va);
139         page_decref(page);
140 }
141
142 //
143 // Invalidate a TLB entry, but only if the page tables being
144 // edited are the ones currently in use by the processor.
145 //
146 // Need to sort this for cross core lovin'  TODO
147 void
148 tlb_invalidate(pde_t *pgdir, void *va)
149 {
150         // Flush the entry only if we're modifying the current address space.
151         // For now, there is only one address space, so always invalidate.
152         invlpg(va);
153 }
154
155 //
156 // Check that an environment is allowed to access the range of memory
157 // [va, va+len) with permissions 'perm | PTE_P'.
158 // Normally 'perm' will contain PTE_U at least, but this is not required.
159 // 'va' and 'len' need not be page-aligned; you must test every page that
160 // contains any of that range.  You will test either 'len/PGSIZE',
161 // 'len/PGSIZE + 1', or 'len/PGSIZE + 2' pages.
162 //
163 // A user program can access a virtual address if (1) the address is below
164 // ULIM, and (2) the page table gives it permission.  These are exactly
165 // the tests you should implement here.
166 //
167 // If there is an error, set the 'user_mem_check_addr' variable to the first
168 // erroneous virtual address.
169 //
170 // Returns 0 if the user program can access this range of addresses,
171 // and -EFAULT otherwise.
172 //
173 // Hint: The TA solution uses pgdir_walk.
174 //
175
176 // zra: I've modified the interface to these two functions so that Ivy can
177 // check that user pointers aren't dereferenced. User pointers get the
178 // DANGEROUS qualifier. After validation, these functions return a
179 // COUNT(len) pointer. user_mem_check now returns NULL on error instead of
180 // -EFAULT.
181
182 void *
183 user_mem_check(env_t *env, const void *DANGEROUS va, size_t len, int perm)
184 {
185         // TODO - will need to sort this out wrt page faulting / PTE_P
186         // also could be issues with sleeping and waking up to find pages
187         // are unmapped, though i think the lab ignores this since the 
188         // kernel is uninterruptible
189         void *DANGEROUS start, *DANGEROUS end;
190         size_t num_pages, i;
191         pte_t *pte;
192
193         perm |= PTE_P;
194         start = ROUNDDOWN((void*DANGEROUS)va, PGSIZE);
195         end = ROUNDUP((void*DANGEROUS)va + len, PGSIZE);
196         if (start >= end) {
197                 warn("Blimey!  Wrap around in VM range calculation!");  
198                 return NULL;
199         }
200         num_pages = PPN(end - start);
201         for (i = 0; i < num_pages; i++, start += PGSIZE) {
202                 pte = pgdir_walk(env->env_pgdir, start, 0);
203                 // ensures the bits we want on are turned on.  if not, error out
204                 if ( !pte || ((*pte & perm) != perm) ) {
205                         if (i == 0)
206                                 user_mem_check_addr = (void*DANGEROUS)va;
207                         else
208                                 user_mem_check_addr = start;
209                         return NULL;
210                 }
211         }
212         // this should never be needed, since the perms should catch it
213         if ((uintptr_t)end > ULIM) {
214                 warn ("I suck - Bug in user permission mappings!");
215                 return NULL;
216         }
217         return (void *COUNT(len))TC(va);
218 }
219
220 size_t
221 user_mem_strlcpy(env_t *env, char *dst, const char *DANGEROUS va,
222                  size_t len, int perm)
223 {
224         const char *DANGEROUS src = va;
225         char *NT COUNT(len-1) dst_in = dst;
226
227         if (len > 0) {
228                 while (1) {
229                         char *c;
230                         if (--len <= 0) break;
231                         c = user_mem_check(env, src, 1, perm);
232                         if (!c) break;
233                         if (*c == '\0') break;
234                         *dst++ = *c;
235                         src++;
236                 }
237                 *dst = '\0';
238         }
239
240         return dst - dst_in;
241 }
242
243 //
244 // Checks that environment 'env' is allowed to access the range
245 // of memory [va, va+len) with permissions 'perm | PTE_U'.
246 // If it can, then the function simply returns.
247 // If it cannot, 'env' is destroyed.
248 //
249 void *
250 user_mem_assert(env_t *env, const void *DANGEROUS va, size_t len, int perm)
251 {
252     void *COUNT(len) res = user_mem_check(env,va,len,perm | PTE_USER_RO);
253         if (!res) {
254                 cprintf("[%08x] user_mem_check assertion failure for "
255                         "va %08x\n", env->env_id, user_mem_check_addr);
256                 proc_destroy(env);      // may not return
257         return NULL;
258         }
259     return res;
260 }
261
262 // copies data from a user buffer to a kernel buffer.
263 // EFAULT if page not present, user lacks perms, or invalid addr.
264 error_t
265 memcpy_from_user(env_t* env, void* COUNT(len) dest,
266                  const void *DANGEROUS va, size_t len)
267 {
268         const void *DANGEROUS start, *DANGEROUS end;
269         size_t num_pages, i;
270         pte_t *pte;
271         uintptr_t perm = PTE_P | PTE_USER_RO;
272         size_t bytes_copied = 0;
273
274         static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
275
276         start = ROUNDDOWN(va, PGSIZE);
277         end = ROUNDUP(va + len, PGSIZE);
278
279         if(start >= (void*SNT)ULIM || end >= (void*SNT)ULIM)
280                 return -EFAULT;
281
282         num_pages = PPN(end - start);
283         for(i = 0; i < num_pages; i++)
284         {
285                 pte = pgdir_walk(env->env_pgdir, start+i*PGSIZE, 0);
286                 if(!pte || (*pte & perm) != perm)
287                         return -EFAULT;
288
289                 void*COUNT(PGSIZE) kpage = KADDR(PTE_ADDR(pte));
290                 void* src_start = i > 0 ? kpage : kpage+(va-start);
291                 void* dst_start = dest+bytes_copied;
292                 size_t copy_len = PGSIZE;
293                 if(i == 0)
294                         copy_len -= va-start;
295                 if(i == num_pages-1)
296                         copy_len -= end-(start+len);
297
298                 memcpy(dst_start,src_start,copy_len);
299                 bytes_copied += copy_len;
300         }
301
302         assert(bytes_copied == len);
303
304         return ESUCCESS;
305 }