1/* 2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <autoconf.h> 8#include <sel4vm/gen_config.h> 9 10#include <sel4utils/vspace.h> 11#include <sel4utils/vspace_internal.h> 12#include <vka/capops.h> 13 14#include <sel4vm/guest_iospace.h> 15 16#include "guest_vspace.h" 17#include "guest_vspace_arch.h" 18 19typedef struct guest_iospace { 20 seL4_CPtr iospace; 21 struct sel4utils_alloc_data iospace_vspace_data; 22 vspace_t iospace_vspace; 23} guest_iospace_t; 24 25typedef struct guest_vspace { 26 /* We abuse struct ordering and this member MUST be the first 27 * thing in the struct */ 28 struct sel4utils_alloc_data vspace_data; 29 /* debug flag for checking if we add io spaces late */ 30 int done_mapping; 31 int num_iospaces; 32 guest_iospace_t **iospaces; 33} guest_vspace_t; 34 35static int guest_vspace_map(vspace_t *vspace, seL4_CPtr cap, void *vaddr, seL4_CapRights_t rights, 36 int cacheable, size_t size_bits) 37{ 38 int error; 39 /* perform the guest mapping */ 40 error = guest_vspace_map_page_arch(vspace, cap, vaddr, rights, cacheable, size_bits); 41 if (error) { 42 return error; 43 } 44 45#if defined(CONFIG_TK1_SMMU) || defined(CONFIG_IOMMU) 46 struct sel4utils_alloc_data *data = get_alloc_data(vspace); 47 /* this type cast works because the alloc data was at the start of the struct 48 * so it has the same address. 49 * This conversion is guaranteed to work by the C standard */ 50 guest_vspace_t *guest_vspace = (guest_vspace_t *) data; 51 /* set the mapping bit */ 52 guest_vspace->done_mapping = 1; 53 cspacepath_t orig_path; 54 /* duplicate the cap so we can do a mapping */ 55 vka_cspace_make_path(guest_vspace->vspace_data.vka, cap, &orig_path); 56 /* map into all the io spaces */ 57 for (int i = 0; i < guest_vspace->num_iospaces; i++) { 58 cspacepath_t new_path; 59 error = vka_cspace_alloc_path(guest_vspace->vspace_data.vka, &new_path); 60 if (error) { 61 ZF_LOGE("Failed to allocate cslot to duplicate frame cap"); 62 return error; 63 } 64 error = vka_cnode_copy(&new_path, &orig_path, seL4_AllRights); 65 66 guest_iospace_t *guest_iospace = guest_vspace->iospaces[i]; 67 68 assert(error == seL4_NoError); 69 error = sel4utils_map_iospace_page(guest_vspace->vspace_data.vka, guest_iospace->iospace, 70 new_path.capPtr, (uintptr_t)vaddr, rights, 1, 71 size_bits, NULL, NULL); 72 if (error) { 73 ZF_LOGE("Failed to map page into iospace"); 74 return error; 75 } 76 77 /* Store the slot of the frame cap copy in a vspace so they can be looked up and 78 * freed when this address gets unmapped. */ 79 error = update_entries(&guest_iospace->iospace_vspace, (uintptr_t)vaddr, new_path.capPtr, size_bits, 0 /* cookie */); 80 if (error) { 81 ZF_LOGE("Failed to add iospace mapping information"); 82 return error; 83 } 84 } 85#endif 86 return 0; 87} 88 89void guest_vspace_unmap(vspace_t *vspace, void *vaddr, size_t num_pages, size_t size_bits, vka_t *vka) 90{ 91 struct sel4utils_alloc_data *data = get_alloc_data(vspace); 92 guest_vspace_t *guest_vspace = (guest_vspace_t *) data; 93 94 int error; 95 96 /* Unmap pages from PT. 97 * vaddr is a guest physical address. 98 * This can be done in a single call as mappings are contiguous in this vspace. */ 99 sel4utils_unmap_pages(vspace, vaddr, num_pages, size_bits, vka); 100 101#if defined(CONFIG_ARM_SMMU) || defined(CONFIG_IOMMU) 102 /* Each page must be unmapped individually from the vmm vspace, as mappings are not 103 * necessarily host-virtually contiguous. */ 104 size_t page_size = BIT(size_bits); 105 for (int i = 0; i < num_pages; i++) { 106 void *page_vaddr = (void *)(vaddr + i * page_size); 107 /* Unmap the vaddr from each iospace, freeing the cslots used to store the 108 * copy of the frame cap. */ 109 for (int i = 0; i < guest_vspace->num_iospaces; i++) { 110 guest_iospace_t *guest_iospace = guest_vspace->iospaces[i]; 111 seL4_CPtr iospace_frame_cap_copy = vspace_get_cap(&guest_iospace->iospace_vspace, page_vaddr); 112 113 error = seL4_ARCH_Page_Unmap(iospace_frame_cap_copy); 114 if (error) { 115 ZF_LOGE("Failed to unmap page from iospace"); 116 return; 117 } 118 119 cspacepath_t path; 120 vka_cspace_make_path(guest_vspace->vspace_data.vka, iospace_frame_cap_copy, &path); 121 error = vka_cnode_delete(&path); 122 if (error) { 123 ZF_LOGE("Failed to delete frame cap copy"); 124 return; 125 } 126 127 vka_cspace_free(guest_vspace->vspace_data.vka, iospace_frame_cap_copy); 128 129 error = clear_entries(&guest_iospace->iospace_vspace, (uintptr_t)page_vaddr, size_bits); 130 if (error) { 131 ZF_LOGE("Failed to clear iospace mapping information"); 132 return; 133 } 134 } 135 } 136#endif 137} 138 139int vm_guest_add_iospace(vm_t *vm, vspace_t *loader, seL4_CPtr iospace) 140{ 141 struct sel4utils_alloc_data *data = get_alloc_data(&vm->mem.vm_vspace); 142 guest_vspace_t *guest_vspace = (guest_vspace_t *) data; 143 144 assert(!guest_vspace->done_mapping); 145 146 guest_vspace->iospaces = realloc(guest_vspace->iospaces, sizeof(guest_iospace_t) * (guest_vspace->num_iospaces + 1)); 147 assert(guest_vspace->iospaces); 148 guest_vspace->iospaces[guest_vspace->num_iospaces] = calloc(1, sizeof(guest_iospace_t)); 149 assert(guest_vspace->iospaces[guest_vspace->num_iospaces]); 150 151 guest_iospace_t *guest_iospace = guest_vspace->iospaces[guest_vspace->num_iospaces]; 152 guest_iospace->iospace = iospace; 153 int error = sel4utils_get_empty_vspace(loader, &guest_iospace->iospace_vspace, &guest_iospace->iospace_vspace_data, 154 guest_vspace->vspace_data.vka, seL4_CapNull, NULL, NULL); 155 if (error) { 156 ZF_LOGE("Failed to allocate vspace for new iospace"); 157 return error; 158 } 159 160 guest_vspace->num_iospaces++; 161 return 0; 162} 163 164int vm_init_guest_vspace(vspace_t *loader, vspace_t *vmm, vspace_t *new_vspace, vka_t *vka, seL4_CPtr page_directory) 165{ 166 int error; 167 guest_vspace_t *vspace = calloc(1, sizeof(*vspace)); 168 if (!vspace) { 169 ZF_LOGE("Malloc failed"); 170 return -1; 171 } 172 vspace->done_mapping = 0; 173 vspace->num_iospaces = 0; 174 vspace->iospaces = malloc(0); 175 assert(vspace->iospaces); 176 error = sel4utils_get_empty_vspace_with_map(loader, new_vspace, &vspace->vspace_data, vka, page_directory, NULL, NULL, 177 guest_vspace_map); 178 if (error) { 179 ZF_LOGE("Failed to create guest vspace"); 180 return error; 181 } 182 183 new_vspace->unmap_pages = guest_vspace_unmap; 184 185 return 0; 186} 187