armv5: make cpu driver run again.
[barrelfish] / kernel / paging_generic.c
1 /**
2  * \file
3  * \brief Kernel memory management.
4  */
5
6 /*
7  * Copyright (c) 2012, ETH Zurich.
8  * Copyright (c) 2014, HP Labs.
9  * All rights reserved.
10  *
11  * This file is distributed under the terms in the attached LICENSE file.
12  * If you do not find this file, copies can be found by writing to:
13  * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
14  */
15
16 #include <paging_generic.h>
17 #include <barrelfish_kpi/paging_arch.h>
18 #include <kernel.h>
19 #include <paging_kernel_arch.h>
20 #include <capabilities.h>
21 #include <cap_predicates.h>
22 #include <mdb/mdb_tree.h>
23 #include <stdio.h>
24
25 static inline errval_t find_next_ptable(struct cte *old, struct cte **next)
26 {
27     errval_t err;
28     if (old->mapping_info.pte) {
29         err = mdb_find_cap_for_address(local_phys_to_gen_phys((lpaddr_t)old->mapping_info.pte), next);
30         if (err_no(err) == CAPS_ERR_CAP_NOT_FOUND) {
31             debug(SUBSYS_PAGING, "could not find cap associated "
32                     "with 0x%"PRIxLPADDR"\n", old->mapping_info.pte);
33             return SYS_ERR_VNODE_NOT_INSTALLED;
34         }
35         if (err_is_fail(err)) {
36             debug(SUBSYS_PAGING, "error in compile_vaddr:"
37                    " mdb_find_range: 0x%"PRIxERRV"\n", err);
38             return err;
39         }
40         if (!type_is_vnode((*next)->cap.type)) {
41                 return SYS_ERR_VNODE_LOOKUP_NEXT;
42         }
43         return SYS_ERR_OK;
44     }
45     else {
46         *next = NULL;
47         return SYS_ERR_VNODE_SLOT_INVALID;
48     }
49 }
50
51 static inline size_t get_offset(struct cte *old, struct cte *next)
52 {
53     return (old->mapping_info.pte - get_address(&next->cap)) / get_pte_size();
54 }
55
56 /*
57  * compile_vaddr returns the lowest address that is addressed by entry 'entry'
58  * in page table 'ptable'
59  */
60 errval_t compile_vaddr(struct cte *ptable, size_t entry, genvaddr_t *retvaddr)
61 {
62     if (!type_is_vnode(ptable->cap.type)) {
63         return SYS_ERR_VNODE_TYPE;
64     }
65
66     genvaddr_t vaddr = 0;
67     // shift at least by BASE_PAGE_BITS for first vaddr part
68     size_t shift = BASE_PAGE_BITS;
69
70     // figure out how much we need to shift (assuming that
71     // compile_vaddr can be used on arbitrary page table types)
72     // A couple of cases have fallthroughs in order to avoid having
73     // multiple calls to vnode_objbits with the same type argument.
74     switch (ptable->cap.type) {
75         case ObjType_VNode_x86_64_pml4:
76             shift += vnode_objbits(ObjType_VNode_x86_64_pdpt);
77         case ObjType_VNode_x86_64_pdpt:
78             shift += vnode_objbits(ObjType_VNode_x86_64_pdir);
79         case ObjType_VNode_x86_64_pdir:
80             shift += vnode_objbits(ObjType_VNode_x86_64_ptable);
81         case ObjType_VNode_x86_64_ptable:
82             break;
83
84         case ObjType_VNode_x86_32_pdpt:
85             shift += vnode_objbits(ObjType_VNode_x86_32_pdir);
86         case ObjType_VNode_x86_32_pdir:
87             shift += vnode_objbits(ObjType_VNode_x86_32_ptable);
88         case ObjType_VNode_x86_32_ptable:
89             break;
90
91         case ObjType_VNode_ARM_l2:
92             shift += vnode_objbits(ObjType_VNode_ARM_l1);
93         case ObjType_VNode_ARM_l1:
94             break;
95
96         default:
97             return SYS_ERR_VNODE_TYPE;
98     }
99
100     size_t mask = (1ULL<<vnode_objbits(ptable->cap.type))-1;
101     vaddr = ((genvaddr_t)(entry & mask)) << shift;
102
103     // add next piece of virtual address until we are at root page table
104     struct cte *old = ptable;
105     struct cte *next;
106     errval_t err;
107     while (!is_root_pt(old->cap.type))
108     {
109         err = find_next_ptable(old, &next);
110         if (err == SYS_ERR_VNODE_NOT_INSTALLED) { // no next page table
111             *retvaddr = 0;
112             return err;
113         }
114         if (err_is_fail(err)) {
115             return err;
116         }
117         // calculate offset into next level ptable
118         size_t offset = get_offset(old, next);
119         // shift new part of vaddr by old shiftwidth + #entries of old ptable
120         shift += vnode_entry_bits(old->cap.type);
121
122         mask = (1ULL<<vnode_objbits(next->cap.type))-1;
123         vaddr |= ((offset & mask) << shift);
124         old = next;
125     }
126
127     *retvaddr = vaddr;
128     return SYS_ERR_OK;
129 }
130
131 errval_t unmap_capability(struct cte *mem)
132 {
133     if (!mem->mapping_info.pte) {
134         // mem is not mapped, so just return
135         return SYS_ERR_OK;
136     }
137
138     errval_t err;
139
140     // get leaf pt cap
141     struct cte *pgtable;
142     err = mdb_find_cap_for_address(mem->mapping_info.pte, &pgtable);
143     if (err_is_fail(err)) {
144         // no page table, should be ok.
145         return SYS_ERR_OK;
146     }
147     lpaddr_t ptable_lp = gen_phys_to_local_phys(get_address(&pgtable->cap));
148     lvaddr_t ptable_lv = local_phys_to_mem(ptable_lp);
149     cslot_t slot = (mem->mapping_info.pte - ptable_lp) / PTABLE_ENTRY_SIZE;
150     genvaddr_t vaddr;
151     err = compile_vaddr(pgtable, slot, &vaddr);
152     if (err_is_ok(err)) {
153         // only perform unmap when we successfully reconstructed the virtual address
154         do_unmap(ptable_lv, slot, mem->mapping_info.pte_count);
155         if (mem->mapping_info.pte_count > 1) {
156             do_full_tlb_flush();
157         } else {
158             do_one_tlb_flush(vaddr);
159         }
160     }
161
162     return SYS_ERR_OK;
163 }
164
165 errval_t lookup_cap_for_mapping(genpaddr_t paddr, lvaddr_t pte, struct cte **retcte)
166 {
167     // lookup matching cap
168     struct cte *mem, *last, *orig;
169     // find a cap for paddr
170 #if 0
171     printf("lookup request = 0x%"PRIxGENPADDR"\n", paddr);
172 #endif
173     errval_t err = mdb_find_cap_for_address(paddr, &mem);
174     if (err_is_fail(err)) {
175         printf("could not find a cap for 0x%"PRIxGENPADDR" (%ld)\n", paddr, err);
176         return err;
177     }
178 #if 0
179     printf("lookup request = 0x%"PRIxGENPADDR"\n", paddr);
180     printf("has_copies(mem) = %d\n", has_copies(mem));
181     printf("pte = 0x%lx\n", pte);
182     printf("0x%lx, %zd\n", get_address(&mem->cap), get_size(&mem->cap));
183     printf("mem->mapping_info.pte          = 0x%lx\n", mem->mapping_info.pte);
184     printf("mem->mapping_info.offset       = %zd\n", mem->mapping_info.offset);
185     printf("mem->mapping_info.pte_count    = %zd\n", mem->mapping_info.pte_count);
186     printf("mem = %p\n", mem);
187 #endif
188
189     // look at all copies of mem
190     last = mem;
191     orig = mem;
192     // search backwards in tree
193     while (is_copy(&mem->cap, &last->cap)) {
194         struct capability *cap = &mem->cap;
195         struct mapping_info *map = &mem->mapping_info;
196         genpaddr_t base = get_address(cap);
197         // only match mappings that start where we want to unmap
198         if (base + map->offset == paddr && map->pte == pte)
199         {
200             // found matching cap
201             *retcte = mem;
202             return SYS_ERR_OK;
203         }
204         last = mem;
205         mem = mdb_predecessor(mem);
206     }
207     last = orig;
208     // search forward in tree
209     mem = mdb_successor(orig);
210     while (is_copy(&mem->cap, &last->cap)) {
211         struct capability *cap = &mem->cap;
212         struct mapping_info *map = &mem->mapping_info;
213         genpaddr_t base = get_address(cap);
214         // only match mappings that start where we want to unmap
215         if (base + map->offset == paddr && map->pte == pte)
216         {
217             // found matching cap
218             *retcte = mem;
219             return SYS_ERR_OK;
220         }
221         last = mem;
222         mem = mdb_successor(mem);
223     }
224
225     // if we get here, we have not found a matching cap
226     return SYS_ERR_CAP_NOT_FOUND;
227 }
228
229 // TODO: cleanup arch compatibility mess for page size selection
230 errval_t paging_tlb_flush_range(struct cte *frame, size_t pages)
231 {
232     // reconstruct first virtual address for TLB flushing
233     struct cte *leaf_pt;
234     errval_t err;
235     err = mdb_find_cap_for_address(frame->mapping_info.pte, &leaf_pt);
236     if (err_is_fail(err)) {
237         return err;
238     }
239     genvaddr_t vaddr;
240     size_t entry = (frame->mapping_info.pte - get_address(&leaf_pt->cap)) /
241         PTABLE_ENTRY_SIZE;
242     err = compile_vaddr(leaf_pt, entry, &vaddr);
243     if (err_is_fail(err)) {
244         if (err_no(err) == SYS_ERR_VNODE_NOT_INSTALLED) {
245             debug(SUBSYS_PAGING, "couldn't reconstruct virtual address\n");
246         }
247         else {
248             return err;
249         }
250     }
251     debug(SUBSYS_PAGING, "flushing TLB entries for vaddrs 0x%"
252             PRIxGENVADDR"--0x%"PRIxGENVADDR"\n",
253             vaddr, vaddr+(pages * BASE_PAGE_SIZE));
254     // flush TLB entries for all modified pages
255     size_t page_size = 0;
256     switch(leaf_pt->cap.type) {
257 #if defined(__x86_64__)
258         case ObjType_VNode_x86_64_ptable:
259             page_size = X86_64_BASE_PAGE_SIZE;
260             break;
261         case ObjType_VNode_x86_64_pdir:
262             page_size = X86_64_LARGE_PAGE_SIZE;
263             break;
264         case ObjType_VNode_x86_64_pdpt:
265             page_size = X86_64_HUGE_PAGE_SIZE;
266             break;
267 #elif defined(__i386__)
268         case ObjType_VNode_x86_32_ptable:
269             page_size = X86_32_BASE_PAGE_SIZE;
270             break;
271         case ObjType_VNode_x86_32_pdir:
272             page_size = X86_32_LARGE_PAGE_SIZE;
273             break;
274 #elif defined(__ARM_ARCH_5__)
275             // XXX: cannot add code here without breaking CPU driver?!
276             // -SG, 2015-05-04.
277 #elif defined(__ARM_ARCH_7__)
278         case ObjType_VNode_ARM_l1:
279             panic("large page support for ARM NYI!\n");
280             break;
281         case ObjType_VNode_ARM_l2:
282             page_size = BASE_PAGE_SIZE;
283             break;
284 #else
285 #error setup page sizes for arch
286 #endif
287         default:
288             break;
289     }
290     assert(page_size);
291     // TODO: check what tlb flushing instructions expect for large/huge pages
292     for (int i = 0; i < pages; i++) {
293         do_one_tlb_flush(vaddr);
294         vaddr += page_size;
295     }
296
297     return SYS_ERR_OK;
298 }