80dad62bcc07cea1b1faff041ed433af242e57ac
[barrelfish] / kernel / arch / armv7 / paging.c
1 /*
2  * Copyright (c) 2009 - 2013 ETH Zurich.
3  * All rights reserved.
4  *
5  * This file is distributed under the terms in the attached LICENSE file.
6  * If you do not find this file, copies can be found by writing to:
7  * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
8  */
9
10 #include <kernel.h>
11 #include <dispatch.h>
12 #include <cp15.h>
13 #include <paging_kernel_arch.h>
14 #include <string.h>
15 #include <exceptions.h>
16 #include <arm_hal.h>
17 #include <cap_predicates.h>
18 #include <dispatch.h>
19
20 inline static uintptr_t paging_round_down(uintptr_t address, uintptr_t size)
21 {
22     return address & ~(size - 1);
23 }
24
25 inline static uintptr_t paging_round_up(uintptr_t address, uintptr_t size)
26 {
27     return (address + size - 1) & ~(size - 1);
28 }
29
30 inline static int aligned(uintptr_t address, uintptr_t bytes)
31 {
32     return (address & (bytes - 1)) == 0;
33 }
34
35
36 union arm_l2_entry;
37 static void
38 paging_set_flags(union arm_l2_entry *entry, uintptr_t kpi_paging_flags)
39 {
40         entry->small_page.bufferable = 1;
41         entry->small_page.cacheable =
42             (kpi_paging_flags & KPI_PAGING_FLAGS_NOCACHE) ? 0 : 1;
43         entry->small_page.ap10  =
44             (kpi_paging_flags & KPI_PAGING_FLAGS_READ)  ? 2 : 0;
45         entry->small_page.ap10 |=
46             (kpi_paging_flags & KPI_PAGING_FLAGS_WRITE) ? 3 : 0;
47         entry->small_page.ap2 = 0;
48 }
49
50 static errval_t
51 caps_map_l1(struct capability* dest,
52             cslot_t            slot,
53             struct capability* src,
54             uintptr_t          kpi_paging_flags,
55             uintptr_t          offset,
56             uintptr_t          pte_count)
57 {
58     //
59     // Note:
60     //
61     // We have chicken-and-egg problem in initializing resources so
62     // instead of treating an L2 table it's actual 1K size, we treat
63     // it as being 4K. As a result when we map an "L2" table we actually
64     // map a page of memory as if it is 4 consecutive L2 tables.
65     //
66     // See lib/barrelfish/arch/arm/pmap_arch.c for more discussion.
67     //
68     const int ARM_L1_SCALE = 4;
69
70     if (src->type != ObjType_VNode_ARM_l2) {
71         //large page mapping goes here
72         assert(0 == (kpi_paging_flags & ~KPI_PAGING_FLAGS_MASK));
73
74         // ARM L1 has 4K entries, we need to fill in individual entries for
75         // 1M sections
76         // XXX: magic constant
77         if (slot >= 4096) {
78             panic("oops: slot >= 4096");
79             return SYS_ERR_VNODE_SLOT_INVALID;
80         }
81
82         if (src->type != ObjType_Frame && src->type != ObjType_DevFrame) {
83             panic("oops: src->type != ObjType_Frame && src->type != ObjType_DevFrame");
84             return SYS_ERR_WRONG_MAPPING;
85         }
86
87         // check offset within frame
88         if ((offset + pte_count * BYTES_PER_SECTION > get_size(src)) ||
89             ((offset % BYTES_PER_SECTION) != 0)) {
90             printf("offset = %"PRIuPTR", pte_count=%"PRIuPTR
91                    ", src->size = %"PRIuGENSIZE", src->type = %d\n",
92                     offset, pte_count, get_size(src), src->type);
93             panic("oops: frame offset invalid");
94             return SYS_ERR_FRAME_OFFSET_INVALID;
95         }
96
97         // check mapping does not overlap leaf page table
98         if (slot + pte_count > 4096) {
99             return SYS_ERR_VM_MAP_SIZE;
100         }
101
102         // Destination
103         lpaddr_t dest_lpaddr = gen_phys_to_local_phys(get_address(dest));
104         lvaddr_t dest_lvaddr = local_phys_to_mem(dest_lpaddr);
105
106         union arm_l1_entry* entry = ((union arm_l1_entry*)dest_lvaddr) + slot;
107         if (entry->invalid.type != L1_TYPE_INVALID_ENTRY) {
108             panic("Remapping valid page.");
109         }
110
111         lpaddr_t src_lpaddr = gen_phys_to_local_phys(get_address(src) + offset);
112         if ((src_lpaddr & (LARGE_PAGE_SIZE - 1))) {
113             panic("Invalid target");
114         }
115
116         struct cte *src_cte = cte_for_cap(src);
117         src_cte->mapping_info.pte_count = pte_count;
118         src_cte->mapping_info.pte = dest_lpaddr;
119         src_cte->mapping_info.offset = offset;
120
121         for (int i = 0; i < pte_count; i++) {
122             entry->raw = 0;
123
124             entry->section.type = L1_TYPE_SECTION_ENTRY;
125             entry->section.bufferable = 1;
126             entry->section.cacheable = (kpi_paging_flags & KPI_PAGING_FLAGS_NOCACHE)? 0: 1;
127             entry->section.ap10 = (kpi_paging_flags & KPI_PAGING_FLAGS_READ)? 2:0;
128             entry->section.ap10 |= (kpi_paging_flags & KPI_PAGING_FLAGS_WRITE)? 3:0;
129             entry->section.ap2 = 0;
130             entry->section.base_address = (src_lpaddr + i * BYTES_PER_SECTION) >> 20;
131
132             entry++;
133
134             debug(SUBSYS_PAGING, "L2 mapping %08"PRIxLVADDR"[%"PRIuCSLOT"] @%p = %08"PRIx32"\n",
135                    dest_lvaddr, slot, entry, entry->raw);
136         }
137
138         // Flush TLB if remapping.
139         cp15_invalidate_tlb();
140         return SYS_ERR_OK;
141     }
142
143     // XXX: magic constant
144     if (slot >= 4096) {
145         printf("slot = %"PRIuCSLOT"\n",slot);
146         panic("oops: slot id >= 4096");
147         return SYS_ERR_VNODE_SLOT_INVALID;
148     }
149
150
151     // check offset within frame
152     if ((offset + pte_count * 1024 > get_size(src)) ||
153             ((offset % 1024) != 0)) {
154         printf("offset = %"PRIuPTR", pte_count=%"PRIuPTR
155                 ", src->size = %"PRIuGENSIZE", src->type = %d\n",
156                 offset, pte_count, get_size(src), src->type);
157         panic("oops: ptable offset invalid");
158         return SYS_ERR_FRAME_OFFSET_INVALID;
159     }
160
161     // check mapping does not overlap leaf page table
162     if (slot + pte_count > 4096) {
163         return SYS_ERR_VM_MAP_SIZE;
164     }
165
166
167     if (slot >= ARM_L1_OFFSET(MEMORY_OFFSET)) {
168         printf("slot = %"PRIuCSLOT"\n",slot);
169         panic("oops: slot id");
170         return SYS_ERR_VNODE_SLOT_RESERVED;
171     }
172
173     debug(SUBSYS_PAGING, "caps_map_l1: mapping %"PRIuPTR" L2 tables @%"PRIuCSLOT"\n",
174             pte_count, slot);
175     // Destination
176     lpaddr_t dest_lpaddr = gen_phys_to_local_phys(get_address(dest));
177     lvaddr_t dest_lvaddr = local_phys_to_mem(dest_lpaddr);
178
179     union arm_l1_entry* entry = (union arm_l1_entry*)dest_lvaddr + slot;
180
181     // Source
182     genpaddr_t src_gpaddr = get_address(src);
183     lpaddr_t   src_lpaddr = gen_phys_to_local_phys(src_gpaddr) + offset;
184
185     assert(aligned(src_lpaddr, 1u << 10));
186     assert((src_lpaddr < dest_lpaddr) || (src_lpaddr >= dest_lpaddr + 16384));
187
188     struct cte *src_cte = cte_for_cap(src);
189     src_cte->mapping_info.pte_count = pte_count;
190     src_cte->mapping_info.pte = dest_lpaddr + slot;
191     src_cte->mapping_info.offset = 0;
192
193     for (int i = 0; i < pte_count; i++, entry++)
194     {
195         entry->raw = 0;
196         entry->page_table.type   = L1_TYPE_PAGE_TABLE_ENTRY;
197         entry->page_table.domain = 0;
198         entry->page_table.base_address =
199             (src_lpaddr + i * BASE_PAGE_SIZE / ARM_L1_SCALE) >> 10;
200         debug(SUBSYS_PAGING, "L1 mapping %"PRIuCSLOT". @%p = %08"PRIx32"\n",
201               slot + i, entry, entry->raw);
202     }
203
204     cp15_invalidate_tlb();
205
206     return SYS_ERR_OK;
207 }
208
209 static errval_t
210 caps_map_l2(struct capability* dest,
211             cslot_t            slot,
212             struct capability* src,
213             uintptr_t          kpi_paging_flags,
214             uintptr_t          offset,
215             uintptr_t          pte_count)
216 {
217     assert(0 == (kpi_paging_flags & ~KPI_PAGING_FLAGS_MASK));
218
219     // ARM L2 has 256 entries, but we treat a 4K page as a consecutive
220     // region of L2 with a single index. 4K == 4 * 1K
221     if (slot >= (256 * 4)) {
222         panic("oops: slot >= (256 * 4)");
223         return SYS_ERR_VNODE_SLOT_INVALID;
224     }
225
226     if (src->type != ObjType_Frame && src->type != ObjType_DevFrame) {
227         panic("oops: src->type != ObjType_Frame && src->type != ObjType_DevFrame");
228         return SYS_ERR_WRONG_MAPPING;
229     }
230
231     // check offset within frame
232     if ((offset + BYTES_PER_PAGE > get_size(src)) ||
233         ((offset % BYTES_PER_PAGE) != 0)) {
234         panic("oops: frame offset invalid");
235         return SYS_ERR_FRAME_OFFSET_INVALID;
236     }
237
238     // check mapping does not overlap leaf page table
239     if (slot + pte_count > (256 * 4)) {
240         return SYS_ERR_VM_MAP_SIZE;
241     }
242
243     // Destination
244     lpaddr_t dest_lpaddr = gen_phys_to_local_phys(get_address(dest));
245     lvaddr_t dest_lvaddr = local_phys_to_mem(dest_lpaddr);
246
247     union arm_l2_entry* entry = (union arm_l2_entry*)dest_lvaddr + slot;
248     if (entry->small_page.type != L2_TYPE_INVALID_PAGE) {
249         panic("Remapping valid page.");
250     }
251
252     lpaddr_t src_lpaddr = gen_phys_to_local_phys(get_address(src) + offset);
253     if ((src_lpaddr & (BASE_PAGE_SIZE - 1))) {
254         panic("Invalid target");
255     }
256
257     struct cte *src_cte = cte_for_cap(src);
258     src_cte->mapping_info.pte_count = pte_count;
259     src_cte->mapping_info.pte = dest_lpaddr;
260     src_cte->mapping_info.offset = offset;
261
262     for (int i = 0; i < pte_count; i++) {
263         entry->raw = 0;
264
265         entry->small_page.type = L2_TYPE_SMALL_PAGE;
266         paging_set_flags(entry, kpi_paging_flags);
267         entry->small_page.base_address = (src_lpaddr + i * BYTES_PER_PAGE) >> 12;
268
269         debug(SUBSYS_PAGING, "L2 mapping %08"PRIxLVADDR"[%"PRIuCSLOT"] @%p = %08"PRIx32"\n",
270                dest_lvaddr, slot, entry, entry->raw);
271
272         entry++;
273     }
274
275     // Flush TLB if remapping.
276     cp15_invalidate_tlb();
277
278     return SYS_ERR_OK;
279 }
280
281 /// Create page mappings
282 errval_t caps_copy_to_vnode(struct cte *dest_vnode_cte, cslot_t dest_slot,
283                             struct cte *src_cte, uintptr_t flags,
284                             uintptr_t offset, uintptr_t pte_count)
285 {
286     struct capability *src_cap  = &src_cte->cap;
287     struct capability *dest_cap = &dest_vnode_cte->cap;
288
289     if (src_cte->mapping_info.pte) {
290         return SYS_ERR_VM_ALREADY_MAPPED;
291     }
292
293     if (ObjType_VNode_ARM_l1 == dest_cap->type) {
294         //printf("caps_map_l1: %zu\n", (size_t)pte_count);
295         return caps_map_l1(dest_cap, dest_slot, src_cap,
296                            flags,
297                            offset,
298                            pte_count
299                           );
300     }
301     else if (ObjType_VNode_ARM_l2 == dest_cap->type) {
302         //printf("caps_map_l2: %zu\n", (size_t)pte_count);
303         return caps_map_l2(dest_cap, dest_slot, src_cap,
304                            flags,
305                            offset,
306                            pte_count
307                           );
308     }
309     else {
310         panic("ObjType not VNode");
311     }
312 }
313
314 size_t do_unmap(lvaddr_t pt, cslot_t slot, size_t num_pages)
315 {
316     size_t unmapped_pages = 0;
317     union arm_l2_entry *ptentry = (union arm_l2_entry *)pt + slot;
318     for (int i = 0; i < num_pages; i++) {
319         ptentry++->raw = 0;
320         unmapped_pages++;
321     }
322     return unmapped_pages;
323 }
324
325 static inline void read_pt_entry(struct capability *pgtable,
326         size_t slot, bool is_section, genpaddr_t *paddr)
327 {
328     assert(type_is_vnode(pgtable->type));
329     assert(paddr);
330
331     genpaddr_t gp = get_address(pgtable);
332     lpaddr_t lp = gen_phys_to_local_phys(gp);
333     lvaddr_t lv = local_phys_to_mem(lp);
334
335     switch (pgtable->type) {
336         case ObjType_VNode_ARM_l1:
337         {
338             union arm_l1_entry *e = (union arm_l1_entry*)lv;
339             if (is_section) {
340                 *paddr = (genpaddr_t)(e->section.base_address) << 20;
341                 return;
342             } else {
343                 *paddr = (genpaddr_t)(e->page_table.base_address) << 10;
344                 return;
345             }
346         }
347         case ObjType_VNode_ARM_l2:
348         {
349             union arm_l2_entry *e = (union arm_l2_entry*)lv;
350             *paddr = (genpaddr_t)(e->small_page.base_address) << 12;
351             return;
352         }
353         default:
354             assert(!"Should not get here");
355     }
356 }
357
358 errval_t page_mappings_unmap(struct capability *pgtable, struct cte *mapping, size_t slot, size_t num_pages)
359 {
360     assert(type_is_vnode(pgtable->type));
361     //printf("page_mappings_unmap(%zd pages, slot = %zd)\n", num_pages, slot);
362
363     bool is_section = false;
364     if (pgtable->type == ObjType_VNode_ARM_l1) {
365         // transform slot to hw slot
366         if (mapping->cap.type == ObjType_VNode_ARM_l2) {
367             // l2 table
368             debug(SUBSYS_PAGING, "unmapping l2 tables: %zu, #pages: %zu\n",
369                     slot, num_pages);
370         }
371         else {
372             // section
373             is_section = true;
374             debug(SUBSYS_PAGING, "unmapping section: %zu, #pages: %zu\n",
375                     slot, num_pages);
376         }
377     }
378     // get page table entry data
379     genpaddr_t paddr;
380     //lpaddr_t pte;
381     read_pt_entry(pgtable, slot, is_section, &paddr);
382     lvaddr_t pt = local_phys_to_mem(gen_phys_to_local_phys(get_address(pgtable)));
383
384     // get virtual address of first page
385     // TODO: error checking
386     genvaddr_t vaddr;
387     struct cte *leaf_pt = cte_for_cap(pgtable);
388     compile_vaddr(leaf_pt, slot, &vaddr);
389     //genvaddr_t vend = vaddr + num_pages * BASE_PAGE_SIZE;
390     // printf("vaddr = 0x%"PRIxGENVADDR"\n", vaddr);
391     // printf("num_pages = %zu\n", num_pages);
392
393     // get cap for mapping
394     /*
395     struct cte *mem;
396     errval_t err = lookup_cap_for_mapping(paddr, pte, &mem);
397     if (err_is_fail(err)) {
398         printf("page_mappings_unmap: %ld\n", err);
399         return err;
400     }
401     */
402     //printf("state before unmap: mapped_pages = %zd\n", mem->mapping_info.mapped_pages);
403     //printf("state before unmap: num_pages    = %zd\n", num_pages);
404
405     if (num_pages != mapping->mapping_info.pte_count) {
406         debug(SUBSYS_PAGING, "num_pages = %zu, mapping = %zu\n",
407                 num_pages, mapping->mapping_info.pte_count);
408         // want to unmap a different amount of pages than was mapped
409         return SYS_ERR_VM_MAP_SIZE;
410     }
411
412     do_unmap(pt, slot, num_pages);
413
414     // flush TLB for unmapped pages
415     // TODO: selective TLB flush
416     cp15_invalidate_tlb();
417
418     // update mapping info
419     memset(&mapping->mapping_info, 0, sizeof(struct mapping_info));
420
421     return SYS_ERR_OK;
422 }
423
424 errval_t paging_modify_flags(struct capability *frame, uintptr_t offset,
425                              uintptr_t pages, uintptr_t kpi_paging_flags)
426 {
427     // check flags
428     assert(0 == (kpi_paging_flags & ~KPI_PAGING_FLAGS_MASK));
429
430     struct cte *mapping = cte_for_cap(frame);
431     struct mapping_info *info = &mapping->mapping_info;
432
433     /* Calculate location of page table entries we need to modify */
434     lvaddr_t base = local_phys_to_mem(info->pte) + offset;
435
436     for (int i = 0; i < pages; i++) {
437         union arm_l2_entry *entry =
438             (union arm_l2_entry *)base + i;
439         paging_set_flags(entry, kpi_paging_flags);
440     }
441
442     return paging_tlb_flush_range(mapping, offset, pages);
443 }
444
445 void paging_dump_tables(struct dcb *dispatcher)
446 {
447     printf("dump_hw_page_tables\n");
448     lvaddr_t l1 = local_phys_to_mem(dispatcher->vspace);
449
450     for (int l1_index = 0; l1_index < ARM_L1_MAX_ENTRIES; l1_index++) {
451         // get level2 table
452         union arm_l1_entry *l1_e = (union arm_l1_entry *)l1 + l1_index;
453         if (!l1_e->raw) { continue; }
454         if (l1_e->invalid.type == 2) { // section 
455             genpaddr_t paddr = (genpaddr_t)(l1_e->section.base_address) << 20;
456             printf("%d: (section) 0x%"PRIxGENPADDR"\n", l1_index, paddr);
457         } else if (l1_e->invalid.type == 1) { // l2 table
458             genpaddr_t ptable_gp = (genpaddr_t)(l1_e->page_table.base_address) << 10;
459             lvaddr_t ptable_lv = local_phys_to_mem(gen_phys_to_local_phys(ptable_gp));
460
461             printf("%d: (l2table) 0x%"PRIxGENPADDR"\n", l1_index, ptable_gp);
462
463             for (int entry = 0; entry < ARM_L2_MAX_ENTRIES; entry++) {
464                 union arm_l2_entry *e =
465                     (union arm_l2_entry *)ptable_lv + entry;
466                 genpaddr_t paddr = (genpaddr_t)(e->small_page.base_address) << BASE_PAGE_BITS;
467                 if (!paddr) {
468                     continue;
469                 }
470                 printf("%d.%d: 0x%"PRIxGENPADDR"\n", l1_index, entry, paddr);
471             }
472         }
473     }
474 }