2 * Copyright (c) 2009 - 2012 ETH Zurich.
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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13 #include <paging_kernel_arch.h>
15 #include <exceptions.h>
17 #include <cap_predicates.h>
21 * Kernel L1 page table
23 //XXX: We reserve double the space needed to be able to align the pagetable
24 // to 16K after relocation
25 static union arm_l1_entry kernel_l1_table[2*ARM_L1_MAX_ENTRIES]
26 __attribute__((aligned(ARM_L1_ALIGN)));
27 static union arm_l1_entry *aligned_kernel_l1_table;
31 * Kernel L2 page table for first MB
33 //XXX: We reserve double the space needed to be able to align the pagetable
34 // to 1K after relocation
35 static union arm_l2_entry low_l2_table[2*ARM_L2_MAX_ENTRIES]
36 __attribute__((aligned(ARM_L2_ALIGN)));
37 static union arm_l2_entry *aligned_low_l2_table;
40 // ------------------------------------------------------------------------
41 // Utility declarations
43 inline static uintptr_t paging_round_down(uintptr_t address, uintptr_t size)
45 return address & ~(size - 1);
48 inline static uintptr_t paging_round_up(uintptr_t address, uintptr_t size)
50 return (address + size - 1) & ~(size - 1);
53 inline static int aligned(uintptr_t address, uintptr_t bytes)
55 return (address & (bytes - 1)) == 0;
58 static inline struct cte *cte_for_cap(struct capability *cap)
60 return (struct cte *) (cap - offsetof(struct cte, cap));
65 paging_write_l1_entry(uintptr_t ttbase, lvaddr_t va, union arm_l1_entry l1)
67 union arm_l1_entry *l1_table;
69 if(va < MEMORY_OFFSET)
70 ttbase = cp15_read_ttbr0() + MEMORY_OFFSET;
72 ttbase = cp15_read_ttbr1() + MEMORY_OFFSET;
74 l1_table = (union arm_l1_entry *) ttbase;
75 l1_table[ARM_L1_OFFSET(va)] = l1;
77 // ------------------------------------------------------------------------
81 void paging_map_kernel_section(uintptr_t ttbase, lvaddr_t va, lpaddr_t pa)
84 union arm_l1_entry l1;
87 l1.section.type = L1_TYPE_SECTION_ENTRY;
88 l1.section.bufferable = 1;
89 l1.section.cacheable = 1;
90 l1.section.ap10 = 1; // RW/NA
92 l1.section.base_address = pa >> 20u;
94 paging_write_l1_entry(ttbase, va, l1);
97 void paging_map_memory(uintptr_t ttbase, lpaddr_t paddr, size_t bytes)
99 lpaddr_t pend = paging_round_down(paddr + bytes, BYTES_PER_SECTION);
100 while (paddr < pend) {
101 paging_map_kernel_section(ttbase, paddr, paddr);
102 paddr += BYTES_PER_SECTION;
107 paging_map_device_section(uintptr_t ttbase, lvaddr_t va, lpaddr_t pa);
110 paging_map_device_section(uintptr_t ttbase, lvaddr_t va, lpaddr_t pa)
112 union arm_l1_entry l1;
115 l1.section.type = L1_TYPE_SECTION_ENTRY;
116 l1.section.bufferable = 0;
117 l1.section.cacheable = 0;
118 l1.section.ap10 = 3; // prev value: 3 // RW/NA RW/RW
120 l1.section.base_address = pa >> 20u;
122 paging_write_l1_entry(ttbase, va, l1);
125 lvaddr_t paging_map_device(lpaddr_t device_base, size_t device_bytes)
127 // HACK to put device in high memory.
128 // Should likely track these allocations.
129 static lvaddr_t dev_alloc = DEVICE_OFFSET;
130 assert(device_bytes <= BYTES_PER_SECTION);
131 dev_alloc -= BYTES_PER_SECTION;
133 printf("paging_map_device_section: 0x%"PRIxLVADDR", 0x%"PRIxLVADDR", "
134 "0x%"PRIxLPADDR".\n",
135 (uintptr_t)aligned_kernel_l1_table, dev_alloc, device_base);
137 paging_map_device_section((uintptr_t)aligned_kernel_l1_table, dev_alloc,
140 cp15_write_ttbr1(mem_to_local_phys((uintptr_t)aligned_kernel_l1_table));
141 cp15_invalidate_i_and_d_caches_fast();
142 cp15_invalidate_tlb();
148 * \brief Reset kernel paging.
150 * This function resets the page maps for kernel and memory-space. It clears out
151 * all other mappings. Use this only at system bootup!
153 void paging_arm_reset(lpaddr_t paddr, size_t bytes)
155 // make sure kernel pagetable is aligned to 16K after relocation
156 aligned_kernel_l1_table = (union arm_l1_entry *)ROUND_UP(
157 (uintptr_t)kernel_l1_table, ARM_L1_ALIGN);
159 // Re-map physical memory
161 paging_map_memory((uintptr_t)aligned_kernel_l1_table , paddr, bytes);
163 //map high-mem relocated exception vector to kernel section
164 paging_map_kernel_section((uintptr_t)aligned_kernel_l1_table, ETABLE_ADDR,
167 cp15_write_ttbr1(mem_to_local_phys((uintptr_t)aligned_kernel_l1_table));
168 cp15_invalidate_tlb();
173 * \brief Reset kernel paging.
175 * This function resets the page maps for kernel and memory-space. It clears out
176 * all other mappings. Use this only at system bootup!
178 void paging_arm_reset(lpaddr_t paddr, size_t bytes)
180 // make sure kernel pagetable is aligned to 16K after relocation
181 aligned_kernel_l1_table = (union arm_l1_entry *)ROUND_UP(
182 (uintptr_t)aligned_kernel_l1_table, ARM_L1_ALIGN);
184 // make sure low l2 pagetable is aligned to 1K after relocation
185 aligned_low_l2_table = (union arm_l2_entry *)ROUND_UP(
186 (uintptr_t)low_l2_table, ARM_L2_ALIGN);
188 // Re-map physical memory
189 paging_map_memory((uintptr_t)aligned_kernel_l1_table, paddr, bytes);
191 // map first MB at granularity of 4K pages
192 uint32_t l2_flags = ARM_L2_SMALL_USR_NONE | ARM_L2_SMALL_CACHEABLE |
193 ARM_L2_SMALL_BUFFERABLE;
194 paging_map_user_pages_l1((uintptr_t)aligned_kernel_l1_table,
195 MEMORY_OFFSET, mem_to_local_phys((uintptr_t)aligned_low_l2_table));
196 for(lpaddr_t pa=0; pa < ARM_L1_SECTION_BYTES; pa += BYTES_PER_PAGE)
198 lvaddr_t va = pa + MEMORY_OFFSET;
199 paging_set_l2_entry((uintptr_t *)&aligned_low_l2_table[ARM_L2_OFFSET(va)], pa, l2_flags);
202 // map high-mem relocated exception vector to corresponding page in low MB
203 // core 0: 0xffff0000 -> 0x80000
204 // core 1: 0xffff0000 -> 0x81000
206 paging_map_user_pages_l1((uintptr_t)aligned_kernel_l1_table, ETABLE_ADDR,
207 mem_to_local_phys((uintptr_t)aligned_low_l2_table));
208 int core_id = hal_get_cpu_id();
209 lpaddr_t addr = ETABLE_PHYS_BASE + core_id * BASE_PAGE_SIZE;
210 paging_set_l2_entry((uintptr_t *)&aligned_low_l2_table[ARM_L2_OFFSET(ETABLE_ADDR)], addr, l2_flags);
213 //map section containing sysflag registers 1:1
214 paging_map_device_section((uintptr_t)aligned_kernel_l1_table, sysflagset_base, sysflagset_base);
216 cp15_write_ttbr1(mem_to_local_phys((uintptr_t)aligned_kernel_l1_table));
218 cp15_invalidate_tlb();
225 void paging_make_good(lvaddr_t new_table_base, size_t new_table_bytes)
227 assert(new_table_base >= MEMORY_OFFSET);
228 assert(new_table_bytes == ARM_L1_ALIGN);
229 assert(aligned(new_table_base, ARM_L1_ALIGN));
231 lvaddr_t ttbr = local_phys_to_mem(cp15_read_ttbr0());
232 size_t st = (MEMORY_OFFSET / ARM_L1_SECTION_BYTES) * ARM_L1_BYTES_PER_ENTRY;
234 // Copy kernel pages (everything from MEMORY_OFFSET upwards)
235 memcpy((void*)new_table_base + st, (void*)ttbr + st,
236 ARM_L1_MAX_ENTRIES * ARM_L1_BYTES_PER_ENTRY - st);
239 void paging_map_user_pages_l1(lvaddr_t table_base, lvaddr_t va, lpaddr_t pa)
241 assert(aligned(table_base, ARM_L1_ALIGN));
242 assert(aligned(pa, BYTES_PER_SMALL_PAGE));
244 union arm_l1_entry e;
247 e.page_table.type = L1_TYPE_PAGE_TABLE_ENTRY;
248 e.page_table.domain = 0;
249 e.page_table.base_address = (pa >> 10);
251 paging_write_l1_entry(table_base, va, e);
254 void paging_set_l2_entry(uintptr_t* l2e, lpaddr_t addr, uintptr_t flags)
256 assert(0 == (flags & 0xfffff000));
257 assert(0 == (flags & 0x3));
258 assert(0 == (addr & 0xfff));
260 union arm_l2_entry e;
263 e.small_page.type = L2_TYPE_SMALL_PAGE;
264 e.small_page.base_address = (addr >> 12);
269 void paging_context_switch(lpaddr_t ttbr)
271 // printf("paging context switch to %"PRIxLPADDR"\n", ttbr);
272 lpaddr_t old_ttbr = cp15_read_ttbr0();
273 if (ttbr != old_ttbr) {
274 cp15_write_ttbr0(ttbr);
275 cp15_invalidate_tlb();
276 cp15_invalidate_i_and_d_caches();
281 paging_set_flags(union arm_l2_entry *entry, uintptr_t kpi_paging_flags)
283 entry->small_page.bufferable = 1;
284 entry->small_page.cacheable =
285 (kpi_paging_flags & KPI_PAGING_FLAGS_NOCACHE) ? 0 : 1;
286 entry->small_page.ap10 =
287 (kpi_paging_flags & KPI_PAGING_FLAGS_READ) ? 2 : 0;
288 entry->small_page.ap10 |=
289 (kpi_paging_flags & KPI_PAGING_FLAGS_WRITE) ? 3 : 0;
290 entry->small_page.ap2 = 0;
294 caps_map_l1(struct capability* dest,
296 struct capability* src,
297 uintptr_t kpi_paging_flags,
304 // We have chicken-and-egg problem in initializing resources so
305 // instead of treating an L2 table it's actual 1K size, we treat
306 // it as being 4K. As a result when we map an "L2" table we actually
307 // map a page of memory as if it is 4 consecutive L2 tables.
309 // See lib/barrelfish/arch/arm/pmap_arch.c for more discussion.
311 const int ARM_L1_SCALE = 4;
314 printf("slot = %"PRIuCSLOT"\n",slot);
315 panic("oops: slot id >= 1024");
316 return SYS_ERR_VNODE_SLOT_INVALID;
319 if (pte_count != 1) {
320 printf("pte_count = %zu\n",(size_t)pte_count);
321 panic("oops: pte_count");
322 return SYS_ERR_VM_MAP_SIZE;
325 if (src->type != ObjType_VNode_ARM_l2) {
326 panic("oops: wrong src type");
327 return SYS_ERR_WRONG_MAPPING;
330 if (slot >= ARM_L1_OFFSET(MEMORY_OFFSET) / ARM_L1_SCALE) {
331 printf("slot = %"PRIuCSLOT"\n",slot);
332 panic("oops: slot id");
333 return SYS_ERR_VNODE_SLOT_RESERVED;
337 lpaddr_t dest_lpaddr = gen_phys_to_local_phys(get_address(dest));
338 lvaddr_t dest_lvaddr = local_phys_to_mem(dest_lpaddr);
340 union arm_l1_entry* entry = (union arm_l1_entry*)dest_lvaddr + (slot * ARM_L1_SCALE);
343 genpaddr_t src_gpaddr = get_address(src);
344 lpaddr_t src_lpaddr = gen_phys_to_local_phys(src_gpaddr);
347 assert(aligned(src_lpaddr, 1u << 10));
348 assert((src_lpaddr < dest_lpaddr) || (src_lpaddr >= dest_lpaddr + 16384));
350 struct cte *src_cte = cte_for_cap(src);
351 src_cte->mapping_info.pte_count = pte_count;
352 src_cte->mapping_info.pte = dest_lpaddr + (slot * ARM_L1_SCALE);
353 src_cte->mapping_info.offset = 0;
355 for (int i = 0; i < 4; i++, entry++)
358 entry->page_table.type = L1_TYPE_PAGE_TABLE_ENTRY;
359 entry->page_table.domain = 0;
360 entry->page_table.base_address =
361 (src_lpaddr + i * BASE_PAGE_SIZE / ARM_L1_SCALE) >> 10;
362 debug(SUBSYS_PAGING, "L1 mapping %"PRIuCSLOT". @%p = %08"PRIx32"\n",
363 slot * ARM_L1_SCALE + i, entry, entry->raw);
366 cp15_invalidate_tlb();
372 caps_map_l2(struct capability* dest,
374 struct capability* src,
375 uintptr_t kpi_paging_flags,
379 assert(0 == (kpi_paging_flags & ~KPI_PAGING_FLAGS_MASK));
381 // ARM L2 has 256 entries, but we treat a 4K page as a consecutive
382 // region of L2 with a single index. 4K == 4 * 1K
383 if (slot >= (256 * 4)) {
384 panic("oops: slot >= (256 * 4)");
385 return SYS_ERR_VNODE_SLOT_INVALID;
388 if (src->type != ObjType_Frame && src->type != ObjType_DevFrame) {
389 panic("oops: src->type != ObjType_Frame && src->type != ObjType_DevFrame");
390 return SYS_ERR_WRONG_MAPPING;
393 // check offset within frame
394 if ((offset + BYTES_PER_PAGE > get_size(src)) ||
395 ((offset % BYTES_PER_PAGE) != 0)) {
396 panic("oops: frame offset invalid");
397 return SYS_ERR_FRAME_OFFSET_INVALID;
400 // check mapping does not overlap leaf page table
401 if (slot + pte_count > (256 * 4)) {
402 return SYS_ERR_VM_MAP_SIZE;
406 lpaddr_t dest_lpaddr = gen_phys_to_local_phys(get_address(dest));
407 lvaddr_t dest_lvaddr = local_phys_to_mem(dest_lpaddr);
409 union arm_l2_entry* entry = (union arm_l2_entry*)dest_lvaddr + slot;
410 if (entry->small_page.type != L2_TYPE_INVALID_PAGE) {
411 panic("Remapping valid page.");
414 lpaddr_t src_lpaddr = gen_phys_to_local_phys(get_address(src) + offset);
415 if ((src_lpaddr & (BASE_PAGE_SIZE - 1))) {
416 panic("Invalid target");
419 struct cte *src_cte = cte_for_cap(src);
420 src_cte->mapping_info.pte_count = pte_count;
421 src_cte->mapping_info.pte = dest_lpaddr;
422 src_cte->mapping_info.offset = offset;
424 for (int i = 0; i < pte_count; i++) {
427 entry->small_page.type = L2_TYPE_SMALL_PAGE;
428 paging_set_flags(entry, kpi_paging_flags);
429 entry->small_page.base_address = (src_lpaddr + i * BYTES_PER_PAGE) >> 12;
433 debug(SUBSYS_PAGING, "L2 mapping %08"PRIxLVADDR"[%"PRIuCSLOT"] @%p = %08"PRIx32"\n",
434 dest_lvaddr, slot, entry, entry->raw);
437 // Flush TLB if remapping.
438 cp15_invalidate_tlb();
443 /// Create page mappings
444 errval_t caps_copy_to_vnode(struct cte *dest_vnode_cte, cslot_t dest_slot,
445 struct cte *src_cte, uintptr_t flags,
446 uintptr_t offset, uintptr_t pte_count)
448 struct capability *src_cap = &src_cte->cap;
449 struct capability *dest_cap = &dest_vnode_cte->cap;
451 if (ObjType_VNode_ARM_l1 == dest_cap->type) {
452 //printf("caps_map_l1: %zu\n", (size_t)pte_count);
453 return caps_map_l1(dest_cap, dest_slot, src_cap,
459 else if (ObjType_VNode_ARM_l2 == dest_cap->type) {
460 //printf("caps_map_l2: %zu\n", (size_t)pte_count);
461 return caps_map_l2(dest_cap, dest_slot, src_cap,
468 panic("ObjType not VNode");
472 size_t do_unmap(lvaddr_t pt, cslot_t slot, size_t num_pages)
474 size_t unmapped_pages = 0;
475 union arm_l2_entry *ptentry = (union arm_l2_entry *)pt + slot;
476 for (int i = 0; i < num_pages; i++) {
480 return unmapped_pages;
483 static inline void read_pt_entry(struct capability *pgtable, size_t slot, genpaddr_t *paddr)
485 assert(type_is_vnode(pgtable->type));
488 genpaddr_t gp = get_address(pgtable);
489 lpaddr_t lp = gen_phys_to_local_phys(gp);
490 lvaddr_t lv = local_phys_to_mem(lp);
492 switch (pgtable->type) {
493 case ObjType_VNode_ARM_l1:
495 union arm_l1_entry *e = (union arm_l1_entry*)lv;
496 *paddr = (genpaddr_t)(e->page_table.base_address) << 10;
499 case ObjType_VNode_ARM_l2:
501 union arm_l2_entry *e = (union arm_l2_entry*)lv;
502 *paddr = (genpaddr_t)(e->small_page.base_address) << 12;
506 assert(!"Should not get here");
510 errval_t page_mappings_unmap(struct capability *pgtable, struct cte *mapping, size_t slot, size_t num_pages)
512 assert(type_is_vnode(pgtable->type));
513 //printf("page_mappings_unmap(%zd pages, slot = %zd)\n", num_pages, slot);
515 // get page table entry data
518 read_pt_entry(pgtable, slot, &paddr);
519 lvaddr_t pt = local_phys_to_mem(gen_phys_to_local_phys(get_address(pgtable)));
521 // get virtual address of first page
522 // TODO: error checking
524 struct cte *leaf_pt = cte_for_cap(pgtable);
525 compile_vaddr(leaf_pt, slot, &vaddr);
526 //genvaddr_t vend = vaddr + num_pages * BASE_PAGE_SIZE;
527 // printf("vaddr = 0x%"PRIxGENVADDR"\n", vaddr);
528 // printf("num_pages = %zu\n", num_pages);
530 // get cap for mapping
533 errval_t err = lookup_cap_for_mapping(paddr, pte, &mem);
534 if (err_is_fail(err)) {
535 printf("page_mappings_unmap: %ld\n", err);
539 //printf("state before unmap: mapped_pages = %zd\n", mem->mapping_info.mapped_pages);
540 //printf("state before unmap: num_pages = %zd\n", num_pages);
542 if (num_pages != mapping->mapping_info.pte_count) {
543 printf("num_pages = %zu, mapping = %zu\n", num_pages, mapping->mapping_info.pte_count);
544 // want to unmap a different amount of pages than was mapped
545 return SYS_ERR_VM_MAP_SIZE;
548 do_unmap(pt, slot, num_pages);
550 // flush TLB for unmapped pages
551 // TODO: selective TLB flush
552 cp15_invalidate_tlb();
554 // update mapping info
555 memset(&mapping->mapping_info, 0, sizeof(struct mapping_info));
560 errval_t paging_modify_flags(struct capability *frame, uintptr_t offset,
561 uintptr_t pages, uintptr_t kpi_paging_flags)
564 assert(0 == (kpi_paging_flags & ~KPI_PAGING_FLAGS_MASK));
566 struct cte *mapping = cte_for_cap(frame);
567 struct mapping_info *info = &mapping->mapping_info;
569 /* Calculate location of page table entries we need to modify */
570 lvaddr_t base = info->pte + offset;
572 for (int i = 0; i < pages; i++) {
573 union arm_l2_entry *entry =
574 (union arm_l2_entry *)base + i;
575 paging_set_flags(entry, kpi_paging_flags);
581 void paging_dump_tables(struct dcb *dispatcher)
583 printf("dump_hw_page_tables\n");
584 lvaddr_t l1 = local_phys_to_mem(dispatcher->vspace);
586 for (int l1_index = 0; l1_index < ARM_L1_MAX_ENTRIES; l1_index++) {
588 union arm_l1_entry *l1_e = (union arm_l1_entry *)l1 + l1_index;
589 if (!l1_e->raw) { continue; }
590 genpaddr_t ptable_gp = (genpaddr_t)(l1_e->page_table.base_address) << 10;
591 lvaddr_t ptable_lv = local_phys_to_mem(gen_phys_to_local_phys(ptable_gp));
593 for (int entry = 0; entry < ARM_L2_MAX_ENTRIES; entry++) {
594 union arm_l2_entry *e =
595 (union arm_l2_entry *)ptable_lv + entry;
596 genpaddr_t paddr = (genpaddr_t)(e->small_page.base_address) << BASE_PAGE_BITS;
600 printf("%d.%d: 0x%"PRIxGENPADDR"\n", l1_index, entry, paddr);