| /* |
| * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| */ |
| |
| #include <autoconf.h> |
| #include <sel4utils/gen_config.h> |
| |
| #include <inttypes.h> |
| #include <sel4/sel4.h> |
| #include <vka/object.h> |
| #include <vka/capops.h> |
| #include <sel4utils/mapping.h> |
| #include <sel4utils/util.h> |
| #include <vspace/mapping.h> |
| |
| static int map_page(vka_t *vka, vspace_map_page_fn_t map_page_fn, vspace_get_map_obj_fn map_obj_fn, |
| seL4_CPtr root, seL4_CPtr frame, void *vaddr, seL4_CapRights_t rights, |
| int cacheable, vka_object_t *objects, int *num_objects) |
| { |
| int n; |
| if (!num_objects) { |
| num_objects = &n; |
| } |
| |
| if (!vka || !root) { |
| return EINVAL; |
| } |
| |
| seL4_ARCH_VMAttributes attr = cacheable ? seL4_ARCH_Default_VMAttributes : |
| seL4_ARCH_Uncached_VMAttributes; |
| |
| /* EPT attributes are different than a standard page table. Previously, a kernel bug |
| * masked the problem by always setting EPT mappings to WriteBack. Once the bug was |
| * fixed, every page became uncached, killing VM performance. |
| * |
| * This check ensure the pages are properly cached |
| */ |
| #ifdef CONFIG_VTX |
| if (seL4_X86_Page_MapEPT == map_page_fn) { |
| attr = cacheable ? seL4_X86_EPT_Default_VMAttributes : |
| seL4_X86_EPT_Uncached_VMAttributes; |
| } |
| #endif |
| |
| *num_objects = 0; |
| int error = map_page_fn(frame, root, (seL4_Word) vaddr, rights, attr); |
| while (error == seL4_FailedLookup) { |
| vspace_map_obj_t obj = {0}; |
| error = map_obj_fn(seL4_MappingFailedLookupLevel(), &obj); |
| /* we should not get an incorrect values for seL4_MappingFailedLookupLevel */ |
| assert(error == 0); |
| |
| vka_object_t object; |
| error = vka_alloc_object(vka, obj.type, obj.size_bits, &object); |
| if (objects) { |
| objects[*num_objects] = object; |
| } |
| if (error) { |
| ZF_LOGE("Mapping structure allocation failed"); |
| return error; |
| } |
| |
| error = vspace_map_obj(&obj, object.cptr, root, |
| (seL4_Word)vaddr, seL4_ARCH_Default_VMAttributes); |
| if (error == seL4_DeleteFirst) { |
| /* this is the case where the allocation of the page table needed to map in |
| * a page table for the meta data, so delete this one and continue */ |
| vka_free_object(vka, &object); |
| } else { |
| (*num_objects)++; |
| if (error) { |
| ZF_LOGE("Failed to map page table %d", error); |
| return error; |
| } |
| } |
| error = map_page_fn(frame, root, (seL4_Word) vaddr, rights, attr); |
| } |
| if (error != seL4_NoError) { |
| ZF_LOGE("Failed to map page at address %p with cap %"PRIuPTR", error: %d", vaddr, frame, error); |
| } |
| return error; |
| } |
| |
| int sel4utils_map_page(vka_t *vka, seL4_CPtr vspace_root, seL4_CPtr frame, void *vaddr, |
| seL4_CapRights_t rights, int cacheable, vka_object_t *objects, int *num_objects) |
| { |
| return map_page(vka, seL4_ARCH_Page_Map, vspace_get_map_obj, vspace_root, frame, vaddr, rights, |
| cacheable, objects, num_objects); |
| } |
| |
| #ifndef CONFIG_ARCH_RISCV |
| int sel4utils_map_iospace_page(vka_t *vka, seL4_CPtr iospace, seL4_CPtr frame, seL4_Word vaddr, |
| seL4_CapRights_t rights, int cacheable, seL4_Word size_bits, |
| vka_object_t *pts, int *num_pts) |
| { |
| return map_page(vka, vspace_iospace_map_page, vspace_get_iospace_map_obj, iospace, frame, (void *) vaddr, rights, |
| cacheable, pts, num_pts); |
| } |
| #endif /* CONFIG_ARCH_RISCV */ |
| #ifdef CONFIG_VTX |
| |
| /*map a frame into guest os's physical address space*/ |
| int sel4utils_map_ept_page(vka_t *vka, seL4_CPtr pd, seL4_CPtr frame, seL4_Word vaddr, |
| seL4_CapRights_t rights, int cacheable, seL4_Word size_bits, |
| vka_object_t *pagetable, vka_object_t *pagedir, vka_object_t *pdpt) |
| { |
| vka_object_t objects[3]; |
| int num_objects; |
| int error = map_page(vka, seL4_X86_Page_MapEPT, vspace_get_ept_map_obj, pd, frame, (void *) vaddr, rights, cacheable, |
| objects, |
| &num_objects); |
| *pagetable = objects[0]; |
| *pagedir = objects[1]; |
| *pdpt = objects[2]; |
| return error; |
| } |
| |
| #endif /* CONFIG_VTX */ |
| |
| /* Some more generic routines for helping with mapping */ |
| void *sel4utils_dup_and_map(vka_t *vka, vspace_t *vspace, seL4_CPtr page, size_t size_bits) |
| { |
| cspacepath_t page_path; |
| cspacepath_t copy_path; |
| /* First need to copy the cap */ |
| int error = vka_cspace_alloc_path(vka, ©_path); |
| if (error != seL4_NoError) { |
| return NULL; |
| } |
| vka_cspace_make_path(vka, page, &page_path); |
| error = vka_cnode_copy(©_path, &page_path, seL4_AllRights); |
| if (error != seL4_NoError) { |
| vka_cspace_free(vka, copy_path.capPtr); |
| return NULL; |
| } |
| /* Now map it in */ |
| void *mapping = vspace_map_pages(vspace, ©_path.capPtr, NULL, seL4_AllRights, 1, size_bits, 1); |
| if (!mapping) { |
| vka_cnode_delete(©_path); |
| vka_cspace_free(vka, copy_path.capPtr); |
| return NULL; |
| } |
| return mapping; |
| } |
| |
| void sel4utils_unmap_dup(vka_t *vka, vspace_t *vspace, void *mapping, size_t size_bits) |
| { |
| /* Grab a copy of the cap */ |
| seL4_CPtr copy = vspace_get_cap(vspace, mapping); |
| cspacepath_t copy_path; |
| assert(copy); |
| /* now free the mapping */ |
| vspace_unmap_pages(vspace, mapping, 1, size_bits, VSPACE_PRESERVE); |
| /* delete and free the cap */ |
| vka_cspace_make_path(vka, copy, ©_path); |
| vka_cnode_delete(©_path); |
| vka_cspace_free(vka, copy); |
| } |