1/* 2 * Copyright 2017, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12 13#include <autoconf.h> 14#include <sel4platsupport/gen_config.h> 15 16#include <sel4platsupport/io.h> 17#ifdef CONFIG_ARCH_ARM 18#include <platsupport/clock.h> 19#include <platsupport/mux.h> 20#endif 21#include <utils/util.h> 22#include <vspace/page.h> 23 24#include <vspace/vspace.h> 25#include <vka/capops.h> 26 27#include <stdint.h> 28#include <stdlib.h> 29 30typedef struct io_mapping { 31 /* address we returned to the user */ 32 void *returned_addr; 33 /* base address of the mapping with respect to the vspace */ 34 void *mapped_addr; 35 size_t num_pages; 36 size_t page_size; 37 size_t page_size_bits; 38 /* caps for the mappings (s) */ 39 seL4_CPtr *caps; 40 /* allocation cookie for allocation(s) */ 41 seL4_Word *alloc_cookies; 42 struct io_mapping *next, *prev; 43} io_mapping_t; 44 45typedef struct sel4platsupport_io_mapper_cookie { 46 vspace_t *vspace; 47 vka_t *vka; 48 io_mapping_t *head; 49} sel4platsupport_io_mapper_cookie_t; 50 51static void free_node(io_mapping_t *node) 52{ 53 assert(node); 54 if (node->caps) { 55 free(node->caps); 56 } 57 if (node->alloc_cookies) { 58 free(node->alloc_cookies); 59 } 60 free(node); 61} 62 63static io_mapping_t *new_node(size_t num_pages) 64{ 65 io_mapping_t *ret = calloc(1, sizeof(io_mapping_t)); 66 67 if (!ret) { 68 return NULL; 69 } 70 71 ret->caps = calloc(num_pages, sizeof(seL4_CPtr)); 72 if (!ret->caps) { 73 free_node(ret); 74 return NULL; 75 } 76 77 ret->alloc_cookies = calloc(num_pages, sizeof(seL4_Word)); 78 if (!ret->alloc_cookies) { 79 free_node(ret); 80 return NULL; 81 } 82 83 ret->num_pages = num_pages; 84 return ret; 85} 86 87static void destroy_node(vka_t *vka, io_mapping_t *mapping) 88{ 89 cspacepath_t path; 90 for (size_t i = 0; i < mapping->num_pages; i++) { 91 /* free the allocation */ 92 vka_utspace_free(vka, kobject_get_type(KOBJECT_FRAME, mapping->page_size_bits), 93 mapping->page_size_bits, mapping->alloc_cookies[i]); 94 /* free the caps */ 95 vka_cspace_make_path(vka, mapping->caps[i], &path); 96 vka_cnode_delete(&path); 97 vka_cspace_free(vka, mapping->caps[i]); 98 } 99 free_node(mapping); 100} 101 102static void insert_node(sel4platsupport_io_mapper_cookie_t *io_mapper, io_mapping_t *node) 103{ 104 node->prev = NULL; 105 node->next = io_mapper->head; 106 if (io_mapper->head) { 107 io_mapper->head->prev = node; 108 } 109 io_mapper->head = node; 110} 111 112static io_mapping_t *find_node(sel4platsupport_io_mapper_cookie_t *io_mapper, void *returned_addr) 113{ 114 io_mapping_t *current; 115 for (current = io_mapper->head; current; current = current->next) { 116 if (current->returned_addr == returned_addr) { 117 return current; 118 } 119 } 120 return NULL; 121} 122 123static void remove_node(sel4platsupport_io_mapper_cookie_t *io_mapper, io_mapping_t *node) 124{ 125 if (node->prev) { 126 node->prev->next = node->next; 127 } else { 128 assert(io_mapper->head == node); 129 io_mapper->head = node->next; 130 } 131 if (node->next) { 132 node->next->prev = node->prev; 133 } 134} 135 136static void *sel4platsupport_map_paddr_with_page_size(sel4platsupport_io_mapper_cookie_t *io_mapper, uintptr_t paddr, 137 size_t size, size_t page_size_bits, bool cached) 138{ 139 140 vka_t *vka = io_mapper->vka; 141 vspace_t *vspace = io_mapper->vspace; 142 143 /* search at start of page */ 144 int page_size = BIT(page_size_bits); 145 uintptr_t start = ROUND_DOWN(paddr, page_size); 146 uintptr_t offset = paddr - start; 147 size += offset; 148 149 io_mapping_t *mapping = new_node(BYTES_TO_SIZE_BITS_PAGES(size, page_size_bits)); 150 assert(mapping->num_pages << page_size_bits >= size); 151 if (!mapping) { 152 ZF_LOGE("Failed to allocate node for %zu pages", mapping->num_pages); 153 return NULL; 154 } 155 mapping->page_size_bits = page_size_bits; 156 157 seL4_Word type = kobject_get_type(KOBJECT_FRAME, mapping->page_size_bits); 158 /* allocate all of the physical frame caps */ 159 for (unsigned int i = 0; i < mapping->num_pages; i++) { 160 /* allocate a cslot */ 161 int error = vka_cspace_alloc(vka, &mapping->caps[i]); 162 if (error) { 163 ZF_LOGE("cspace alloc failed"); 164 assert(error == 0); 165 /* we don't clean up as everything has gone to hell */ 166 return NULL; 167 } 168 169 /* create a path */ 170 cspacepath_t path; 171 vka_cspace_make_path(vka, mapping->caps[i], &path); 172 173 /* allocate the frame */ 174 error = vka_utspace_alloc_at(vka, &path, type, page_size_bits, start + (i * page_size), 175 &mapping->alloc_cookies[i]); 176 if (error) { 177 /* free this slot, and then do general cleanup of the rest of the slots. 178 * this avoids a needless seL4_CNode_Delete of this slot, as there is no 179 * cap in it */ 180 vka_cspace_free(vka, mapping->caps[i]); 181 mapping->num_pages = i; 182 goto error; 183 } 184 } 185 186 /* Now map the frames in */ 187 mapping->mapped_addr = vspace_map_pages(vspace, mapping->caps, mapping->alloc_cookies, seL4_AllRights, 188 mapping->num_pages, 189 mapping->page_size_bits, cached); 190 if (mapping->mapped_addr != NULL) { 191 /* fill out and insert node */ 192 mapping->returned_addr = mapping->mapped_addr + offset; 193 insert_node(io_mapper, mapping); 194 return mapping->returned_addr; 195 } 196error: 197 destroy_node(vka, mapping); 198 return NULL; 199} 200 201static void *sel4platsupport_map_paddr(void *cookie, uintptr_t paddr, size_t size, int cached, 202 UNUSED ps_mem_flags_t flags) 203{ 204 if (!cookie) { 205 ZF_LOGE("cookie is NULL"); 206 return NULL; 207 } 208 209 sel4platsupport_io_mapper_cookie_t *io_mapper = (sel4platsupport_io_mapper_cookie_t *)cookie; 210 int frame_size_index = 0; 211 /* find the largest reasonable frame size */ 212 while (frame_size_index + 1 < SEL4_NUM_PAGE_SIZES) { 213 if (size >> sel4_page_sizes[frame_size_index + 1] == 0) { 214 break; 215 } 216 frame_size_index++; 217 } 218 219 /* try mapping in this and all smaller frame sizes until something works */ 220 for (int i = frame_size_index; i >= 0; i--) { 221 void *result = sel4platsupport_map_paddr_with_page_size(io_mapper, paddr, size, sel4_page_sizes[i], cached); 222 if (result) { 223 return result; 224 } 225 } 226 227 /* shit out of luck */ 228 ZF_LOGE("Failed to find a way to map address %p", (void *)paddr); 229 return NULL; 230} 231 232static void sel4platsupport_unmap_vaddr(void *cookie, void *vaddr, UNUSED size_t size) 233{ 234 if (!cookie) { 235 ZF_LOGE("cookie is NULL"); 236 } 237 238 sel4platsupport_io_mapper_cookie_t *io_mapper = cookie; 239 240 vspace_t *vspace = io_mapper->vspace; 241 vka_t *vka = io_mapper->vka; 242 io_mapping_t *mapping = find_node(io_mapper, vaddr); 243 244 if (!mapping) { 245 ZF_LOGF("Tried to unmap vaddr %p, which was never mapped in", vaddr); 246 return; 247 } 248 249 /* unmap the pages */ 250 vspace_unmap_pages(vspace, mapping->mapped_addr, mapping->num_pages, mapping->page_size_bits, 251 VSPACE_PRESERVE); 252 253 /* clean up the node */ 254 remove_node(io_mapper, mapping); 255 destroy_node(vka, mapping); 256} 257 258int sel4platsupport_new_io_mapper(vspace_t *vspace, vka_t *vka, ps_io_mapper_t *io_mapper) 259{ 260 sel4platsupport_io_mapper_cookie_t *cookie = calloc(1, sizeof(sel4platsupport_io_mapper_cookie_t)); 261 if (!cookie) { 262 ZF_LOGE("Failed to allocate %zu bytes", sizeof(sel4platsupport_io_mapper_cookie_t)); 263 return -1; 264 } 265 266 cookie->vspace = vspace; 267 cookie->vka = vka; 268 io_mapper->cookie = cookie; 269 io_mapper->io_map_fn = sel4platsupport_map_paddr; 270 io_mapper->io_unmap_fn = sel4platsupport_unmap_vaddr; 271 272 return 0; 273} 274 275int sel4platsupport_new_malloc_ops(ps_malloc_ops_t *ops) 276{ 277 ps_new_stdlib_malloc_ops(ops); 278 return 0; 279} 280 281static char *sel4platsupport_io_fdt_get(void *cookie) 282{ 283 return cookie != NULL ? (char *) cookie : NULL; 284} 285 286int sel4platsupport_new_fdt_ops(ps_io_fdt_t *io_fdt, simple_t *simple, ps_malloc_ops_t *malloc_ops) 287{ 288 if (!io_fdt || !simple || !malloc_ops) { 289 ZF_LOGE("arguments are NULL"); 290 return -1; 291 } 292 293 ssize_t block_size = simple_get_extended_bootinfo_length(simple, SEL4_BOOTINFO_HEADER_FDT); 294 if (block_size > 0) { 295 int error = ps_calloc(malloc_ops, 1, block_size, &io_fdt->cookie); 296 if (error) { 297 ZF_LOGE("Failed to allocate %zu bytes for the FDT", block_size); 298 return -1; 299 } 300 301 /* Copy the FDT from the extended bootinfo */ 302 ssize_t copied_size = simple_get_extended_bootinfo(simple, SEL4_BOOTINFO_HEADER_FDT, 303 io_fdt->cookie, block_size); 304 if (copied_size != block_size) { 305 ZF_LOGE("Failed to copy the FDT"); 306 ZF_LOGF_IF(ps_free(malloc_ops, block_size, io_fdt->cookie), 307 "Failed to clean-up after a failed operation!"); 308 return -1; 309 } 310 311 /* Cut off the bootinfo header from the start of the buffer */ 312 ssize_t fdt_size = block_size - sizeof(seL4_BootInfoHeader); 313 void *fdt_start = io_fdt->cookie + sizeof(seL4_BootInfoHeader); 314 memmove(io_fdt->cookie, fdt_start, fdt_size); 315 316 /* Trim off the extra bytes at the end of the FDT */ 317 void *fdt_end = io_fdt->cookie + fdt_size; 318 memset(fdt_end, 0, sizeof(seL4_BootInfoHeader)); 319 } else { 320 /* No FDT is available so just set the cookie to NULL */ 321 io_fdt->cookie = NULL; 322 } 323 324 /* Set the function pointer inside the io_fdt interface */ 325 io_fdt->get_fn = sel4platsupport_io_fdt_get; 326 327 return 0; 328} 329 330int sel4platsupport_new_io_ops(vspace_t *vspace, vka_t *vka, simple_t *simple, ps_io_ops_t *io_ops) 331{ 332 memset(io_ops, 0, sizeof(ps_io_ops_t)); 333 334 int error = 0; 335 336 /* Initialise the interfaces which do not require memory allocation/need to be initialised first */ 337 error = sel4platsupport_new_malloc_ops(&io_ops->malloc_ops); 338 if (error) { 339 return error; 340 } 341 342 /* Now allocate the IO-specific interfaces (the ones that can be found in this file) */ 343 error = sel4platsupport_new_io_mapper(vspace, vka, &io_ops->io_mapper); 344 if (error) { 345 return error; 346 } 347 348 error = sel4platsupport_new_fdt_ops(&io_ops->io_fdt, simple, &io_ops->malloc_ops); 349 if (error) { 350 free(io_ops->io_mapper.cookie); 351 io_ops->io_mapper.cookie = NULL; 352 return error; 353 } 354 355 error = sel4platsupport_new_irq_ops(&io_ops->irq_ops, vka, simple, DEFAULT_IRQ_INTERFACE_CONFIG, 356 &io_ops->malloc_ops); 357 if (error) { 358 free(io_ops->io_mapper.cookie); 359 io_ops->io_mapper.cookie = NULL; 360 ssize_t fdt_size = simple_get_extended_bootinfo_length(simple, SEL4_BOOTINFO_HEADER_FDT); 361 if (fdt_size > 0) { 362 /* The FDT is available on this platform and we actually copied it, so we free it */ 363 ps_free(&io_ops->malloc_ops, fdt_size, &io_ops->io_fdt.cookie); 364 } 365 return error; 366 } 367 368 return 0; 369} 370