d2ebf624ddb6a918704d8411df637b9c32284fee
[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 (slot >= 1024) {
71         printf("slot = %"PRIuCSLOT"\n",slot);
72         panic("oops: slot id >= 1024");
73         return SYS_ERR_VNODE_SLOT_INVALID;
74     }
75
76     if (pte_count != 1) {
77         printf("pte_count = %zu\n",(size_t)pte_count);
78         panic("oops: pte_count");
79         return SYS_ERR_VM_MAP_SIZE;
80     }
81
82     if (src->type != ObjType_VNode_ARM_l2) {
83         //large page mapping goes here
84         printf("kernel large page\n");
85         //panic("oops: wrong src type");
86         assert(0 == (kpi_paging_flags & ~KPI_PAGING_FLAGS_MASK));
87
88         // ARM L1 has 4K entries, but we treat it as if it had 1K
89         if (slot >= (256 * 4)) {
90             panic("oops: slot >= (256 * 4)");
91             return SYS_ERR_VNODE_SLOT_INVALID;
92         }
93
94         if (src->type != ObjType_Frame && src->type != ObjType_DevFrame) {
95             panic("oops: src->type != ObjType_Frame && src->type != ObjType_DevFrame");
96             return SYS_ERR_WRONG_MAPPING;
97         }
98
99         // check offset within frame
100         if ((offset + BYTES_PER_SECTION > get_size(src)) ||
101             ((offset % BYTES_PER_SECTION) != 0)) {
102             panic("oops: frame offset invalid");
103             return SYS_ERR_FRAME_OFFSET_INVALID;
104         }
105
106         // check mapping does not overlap leaf page table
107         if (slot + pte_count > (256 * 4)) {
108             return SYS_ERR_VM_MAP_SIZE;
109         }
110
111         // Destination
112         lpaddr_t dest_lpaddr = gen_phys_to_local_phys(get_address(dest));
113         lvaddr_t dest_lvaddr = local_phys_to_mem(dest_lpaddr);
114
115         union arm_l1_entry* entry = (union arm_l1_entry*)dest_lvaddr + slot;
116         if (entry->invalid.type != L1_TYPE_INVALID_ENTRY) {
117             panic("Remapping valid page.");
118         }
119
120         lpaddr_t src_lpaddr = gen_phys_to_local_phys(get_address(src) + offset);
121         if ((src_lpaddr & (LARGE_PAGE_SIZE - 1))) {
122             panic("Invalid target");
123         }
124
125         struct cte *src_cte = cte_for_cap(src);
126         src_cte->mapping_info.pte_count = pte_count;
127         src_cte->mapping_info.pte = dest_lpaddr;
128         src_cte->mapping_info.offset = offset;
129
130         for (int i = 0; i < pte_count; i++) {
131             entry->raw = 0;
132
133             entry->section.type = L1_TYPE_SECTION_ENTRY;
134             entry->section.bufferable = 1;
135             entry->section.cacheable = (kpi_paging_flags & KPI_PAGING_FLAGS_NOCACHE)? 0: 1;
136             entry->section.ap10 = (kpi_paging_flags & KPI_PAGING_FLAGS_READ)? 2:0;
137             entry->section.ap10 |= (kpi_paging_flags & KPI_PAGING_FLAGS_WRITE)? 3:0;
138             entry->section.ap2 = 0;
139             entry->section.base_address = (src_lpaddr + i * BYTES_PER_SECTION) >> 12;
140
141             entry++;
142
143             debug(SUBSYS_PAGING, "L2 mapping %08"PRIxLVADDR"[%"PRIuCSLOT"] @%p = %08"PRIx32"\n",
144                    dest_lvaddr, slot, entry, entry->raw);
145         }
146
147         // Flush TLB if remapping.
148         cp15_invalidate_tlb();
149         return SYS_ERR_OK;
150         return SYS_ERR_WRONG_MAPPING;
151     }
152
153     if (slot >= ARM_L1_OFFSET(MEMORY_OFFSET) / ARM_L1_SCALE) {
154         printf("slot = %"PRIuCSLOT"\n",slot);
155         panic("oops: slot id");
156         return SYS_ERR_VNODE_SLOT_RESERVED;
157     }
158
159     // Destination
160     lpaddr_t dest_lpaddr = gen_phys_to_local_phys(get_address(dest));
161     lvaddr_t dest_lvaddr = local_phys_to_mem(dest_lpaddr);
162
163     union arm_l1_entry* entry = (union arm_l1_entry*)dest_lvaddr + (slot * ARM_L1_SCALE);
164
165     // Source
166     genpaddr_t src_gpaddr = get_address(src);
167     lpaddr_t   src_lpaddr = gen_phys_to_local_phys(src_gpaddr);
168
169     assert(offset == 0);
170     assert(aligned(src_lpaddr, 1u << 10));
171     assert((src_lpaddr < dest_lpaddr) || (src_lpaddr >= dest_lpaddr + 16384));
172
173     struct cte *src_cte = cte_for_cap(src);
174     src_cte->mapping_info.pte_count = pte_count;
175     src_cte->mapping_info.pte = dest_lpaddr + (slot * ARM_L1_SCALE);
176     src_cte->mapping_info.offset = 0;
177
178     for (int i = 0; i < 4; i++, entry++)
179     {
180         entry->raw = 0;
181         entry->page_table.type   = L1_TYPE_PAGE_TABLE_ENTRY;
182         entry->page_table.domain = 0;
183         entry->page_table.base_address =
184             (src_lpaddr + i * BASE_PAGE_SIZE / ARM_L1_SCALE) >> 10;
185         debug(SUBSYS_PAGING, "L1 mapping %"PRIuCSLOT". @%p = %08"PRIx32"\n",
186               slot * ARM_L1_SCALE + i, entry, entry->raw);
187     }
188
189     cp15_invalidate_tlb();
190
191     return SYS_ERR_OK;
192 }
193
194 static errval_t
195 caps_map_l2(struct capability* dest,
196             cslot_t            slot,
197             struct capability* src,
198             uintptr_t          kpi_paging_flags,
199             uintptr_t          offset,
200             uintptr_t          pte_count)
201 {
202     assert(0 == (kpi_paging_flags & ~KPI_PAGING_FLAGS_MASK));
203
204     // ARM L2 has 256 entries, but we treat a 4K page as a consecutive
205     // region of L2 with a single index. 4K == 4 * 1K
206     if (slot >= (256 * 4)) {
207         panic("oops: slot >= (256 * 4)");
208         return SYS_ERR_VNODE_SLOT_INVALID;
209     }
210
211     if (src->type != ObjType_Frame && src->type != ObjType_DevFrame) {
212         panic("oops: src->type != ObjType_Frame && src->type != ObjType_DevFrame");
213         return SYS_ERR_WRONG_MAPPING;
214     }
215
216     // check offset within frame
217     if ((offset + BYTES_PER_PAGE > get_size(src)) ||
218         ((offset % BYTES_PER_PAGE) != 0)) {
219         panic("oops: frame offset invalid");
220         return SYS_ERR_FRAME_OFFSET_INVALID;
221     }
222
223     // check mapping does not overlap leaf page table
224     if (slot + pte_count > (256 * 4)) {
225         return SYS_ERR_VM_MAP_SIZE;
226     }
227
228     // Destination
229     lpaddr_t dest_lpaddr = gen_phys_to_local_phys(get_address(dest));
230     lvaddr_t dest_lvaddr = local_phys_to_mem(dest_lpaddr);
231
232     union arm_l2_entry* entry = (union arm_l2_entry*)dest_lvaddr + slot;
233     if (entry->small_page.type != L2_TYPE_INVALID_PAGE) {
234         panic("Remapping valid page.");
235     }
236
237     lpaddr_t src_lpaddr = gen_phys_to_local_phys(get_address(src) + offset);
238     if ((src_lpaddr & (BASE_PAGE_SIZE - 1))) {
239         panic("Invalid target");
240     }
241
242     struct cte *src_cte = cte_for_cap(src);
243     src_cte->mapping_info.pte_count = pte_count;
244     src_cte->mapping_info.pte = dest_lpaddr;
245     src_cte->mapping_info.offset = offset;
246
247     for (int i = 0; i < pte_count; i++) {
248         entry->raw = 0;
249
250         entry->small_page.type = L2_TYPE_SMALL_PAGE;
251         paging_set_flags(entry, kpi_paging_flags);
252         entry->small_page.base_address = (src_lpaddr + i * BYTES_PER_PAGE) >> 12;
253
254         entry++;
255
256         debug(SUBSYS_PAGING, "L2 mapping %08"PRIxLVADDR"[%"PRIuCSLOT"] @%p = %08"PRIx32"\n",
257                dest_lvaddr, slot, entry, entry->raw);
258     }
259
260     // Flush TLB if remapping.
261     cp15_invalidate_tlb();
262
263     return SYS_ERR_OK;
264 }
265
266 /// Create page mappings
267 errval_t caps_copy_to_vnode(struct cte *dest_vnode_cte, cslot_t dest_slot,
268                             struct cte *src_cte, uintptr_t flags,
269                             uintptr_t offset, uintptr_t pte_count)
270 {
271     struct capability *src_cap  = &src_cte->cap;
272     struct capability *dest_cap = &dest_vnode_cte->cap;
273
274     if (src_cte->mapping_info.pte) {
275         return SYS_ERR_VM_ALREADY_MAPPED;
276     }
277
278     if (ObjType_VNode_ARM_l1 == dest_cap->type) {
279         //printf("caps_map_l1: %zu\n", (size_t)pte_count);
280         return caps_map_l1(dest_cap, dest_slot, src_cap,
281                            flags,
282                            offset,
283                            pte_count
284                           );
285     }
286     else if (ObjType_VNode_ARM_l2 == dest_cap->type) {
287         //printf("caps_map_l2: %zu\n", (size_t)pte_count);
288         return caps_map_l2(dest_cap, dest_slot, src_cap,
289                            flags,
290                            offset,
291                            pte_count
292                           );
293     }
294     else {
295         panic("ObjType not VNode");
296     }
297 }
298
299 size_t do_unmap(lvaddr_t pt, cslot_t slot, size_t num_pages)
300 {
301     size_t unmapped_pages = 0;
302     union arm_l2_entry *ptentry = (union arm_l2_entry *)pt + slot;
303     for (int i = 0; i < num_pages; i++) {
304         ptentry++->raw = 0;
305         unmapped_pages++;
306     }
307     return unmapped_pages;
308 }
309
310 static inline void read_pt_entry(struct capability *pgtable, size_t slot, genpaddr_t *paddr)
311 {
312     assert(type_is_vnode(pgtable->type));
313     assert(paddr);
314
315     genpaddr_t gp = get_address(pgtable);
316     lpaddr_t lp = gen_phys_to_local_phys(gp);
317     lvaddr_t lv = local_phys_to_mem(lp);
318
319     switch (pgtable->type) {
320         case ObjType_VNode_ARM_l1:
321         {
322             union arm_l1_entry *e = (union arm_l1_entry*)lv;
323             *paddr = (genpaddr_t)(e->page_table.base_address) << 10;
324             return;
325         }
326         case ObjType_VNode_ARM_l2:
327         {
328             union arm_l2_entry *e = (union arm_l2_entry*)lv;
329             *paddr = (genpaddr_t)(e->small_page.base_address) << 12;
330             return;
331         }
332         default:
333             assert(!"Should not get here");
334     }
335 }
336
337 errval_t page_mappings_unmap(struct capability *pgtable, struct cte *mapping, size_t slot, size_t num_pages)
338 {
339     assert(type_is_vnode(pgtable->type));
340     //printf("page_mappings_unmap(%zd pages, slot = %zd)\n", num_pages, slot);
341
342     // get page table entry data
343     genpaddr_t paddr;
344     //lpaddr_t pte;
345     read_pt_entry(pgtable, slot, &paddr);
346     lvaddr_t pt = local_phys_to_mem(gen_phys_to_local_phys(get_address(pgtable)));
347
348     // get virtual address of first page
349     // TODO: error checking
350     genvaddr_t vaddr;
351     struct cte *leaf_pt = cte_for_cap(pgtable);
352     compile_vaddr(leaf_pt, slot, &vaddr);
353     //genvaddr_t vend = vaddr + num_pages * BASE_PAGE_SIZE;
354     // printf("vaddr = 0x%"PRIxGENVADDR"\n", vaddr);
355     // printf("num_pages = %zu\n", num_pages);
356
357     // get cap for mapping
358     /*
359     struct cte *mem;
360     errval_t err = lookup_cap_for_mapping(paddr, pte, &mem);
361     if (err_is_fail(err)) {
362         printf("page_mappings_unmap: %ld\n", err);
363         return err;
364     }
365     */
366     //printf("state before unmap: mapped_pages = %zd\n", mem->mapping_info.mapped_pages);
367     //printf("state before unmap: num_pages    = %zd\n", num_pages);
368
369     if (num_pages != mapping->mapping_info.pte_count) {
370         printf("num_pages = %zu, mapping = %zu\n", num_pages, mapping->mapping_info.pte_count);
371         // want to unmap a different amount of pages than was mapped
372         return SYS_ERR_VM_MAP_SIZE;
373     }
374
375     do_unmap(pt, slot, num_pages);
376
377     // flush TLB for unmapped pages
378     // TODO: selective TLB flush
379     cp15_invalidate_tlb();
380
381     // update mapping info
382     memset(&mapping->mapping_info, 0, sizeof(struct mapping_info));
383
384     return SYS_ERR_OK;
385 }
386
387 errval_t paging_modify_flags(struct capability *frame, uintptr_t offset,
388                              uintptr_t pages, uintptr_t kpi_paging_flags)
389 {
390     // check flags
391     assert(0 == (kpi_paging_flags & ~KPI_PAGING_FLAGS_MASK));
392
393     struct cte *mapping = cte_for_cap(frame);
394     struct mapping_info *info = &mapping->mapping_info;
395
396     /* Calculate location of page table entries we need to modify */
397     lvaddr_t base = local_phys_to_mem(info->pte) + offset;
398
399     for (int i = 0; i < pages; i++) {
400         union arm_l2_entry *entry =
401             (union arm_l2_entry *)base + i;
402         paging_set_flags(entry, kpi_paging_flags);
403     }
404
405     return paging_tlb_flush_range(mapping, pages);
406 }
407
408 void paging_dump_tables(struct dcb *dispatcher)
409 {
410     printf("dump_hw_page_tables\n");
411     lvaddr_t l1 = local_phys_to_mem(dispatcher->vspace);
412
413     for (int l1_index = 0; l1_index < ARM_L1_MAX_ENTRIES; l1_index++) {
414         // get level2 table
415         union arm_l1_entry *l1_e = (union arm_l1_entry *)l1 + l1_index;
416         if (!l1_e->raw) { continue; }
417         genpaddr_t ptable_gp = (genpaddr_t)(l1_e->page_table.base_address) << 10;
418         lvaddr_t ptable_lv = local_phys_to_mem(gen_phys_to_local_phys(ptable_gp));
419
420         for (int entry = 0; entry < ARM_L2_MAX_ENTRIES; entry++) {
421             union arm_l2_entry *e =
422                 (union arm_l2_entry *)ptable_lv + entry;
423             genpaddr_t paddr = (genpaddr_t)(e->small_page.base_address) << BASE_PAGE_BITS;
424             if (!paddr) {
425                 continue;
426             }
427             printf("%d.%d: 0x%"PRIxGENPADDR"\n", l1_index, entry, paddr);
428         }
429     }
430 }