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/* IO port/device functionality. This is meant for interaction with 14 * libplatsupport infrastructure. 15 */ 16 17#include <assert.h> 18#include <libfdt.h> 19#include <camkes/dataport.h> 20#include <camkes/dma.h> 21#include <camkes/io.h> 22#include <camkes/interface_registration.h> 23#include <camkes/irq.h> 24#include <camkes/arch/io.h> 25#include <platsupport/io.h> 26#include <platsupport/driver_module.h> 27#include <stdint.h> 28#include <stdlib.h> 29#include <utils/util.h> 30 31/* Force the _dataport_frames section to be created even if no modules are defined. */ 32static USED SECTION("_dataport_frames") struct {} dummy_dataport_frame; 33/* Definitions so that we can find the exposed dataport frames */ 34extern dataport_frame_t __start__dataport_frames[]; 35extern dataport_frame_t __stop__dataport_frames[]; 36 37typedef struct camkes_defer_token { 38 char *device_path; 39 ps_driver_init_fn_t init_func; 40} camkes_defer_token_t; 41 42/* Basic linked-list implementation. */ 43typedef struct ll_ { 44 void *data; 45 struct ll_ *next; 46} ll_t; 47 48#ifndef NDEBUG 49bool malloc_ops_initialised = false; 50static ps_malloc_ops_t io_mapper_malloc_ops = {0}; 51#endif 52 53static UNUSED int ll_prepend(ps_malloc_ops_t *malloc_ops, ll_t **list, const void *data) 54{ 55 ll_t *node = NULL; 56 int error = ps_calloc(malloc_ops, 1, sizeof * node, (void **) &node); 57 if (error) { 58 return -1; 59 } 60 node->data = (void *)data; 61 node->next = *list; 62 *list = node; 63 return 0; 64} 65 66static UNUSED int ll_append(ps_malloc_ops_t *malloc_ops, ll_t **list, const void *data) 67{ 68 ll_t *node = NULL; 69 int error = ps_calloc(malloc_ops, 1, sizeof * node, (void **) &node); 70 if (error) { 71 return -1; 72 } 73 node->data = (void *)data; 74 node->next = NULL; 75 if (*list == NULL) { 76 *list = node; 77 return 0; 78 } 79 ll_t *curr = NULL; 80 for (curr = *list; curr->next != NULL; curr = curr->next); 81 curr->next = node; 82 return 0; 83} 84 85static UNUSED int ll_remove(ps_malloc_ops_t *malloc_ops, ll_t **list, const void *data) 86{ 87 for (ll_t **l = list; *l != NULL; l = &(*l)->next) { 88 if ((*l)->data == data) { 89 /* found it */ 90 ll_t *temp = *l; 91 *l = (*l)->next; 92 ps_free(malloc_ops, sizeof(*temp), temp); 93 return 0; 94 } 95 } 96 return -1; 97} 98 99typedef struct { 100 ps_malloc_ops_t *malloc_ops; 101 ps_io_map_fn_t map; 102 ll_t *mapped; 103} cookie_t; 104 105/* Debug wrapper for IO map. This function calls the underlying map function 106 * and tracks results for the purpose of catching illegal unmapping operations. 107 * Note that this function is unused when NDEBUG is defined. 108 */ 109static UNUSED void *io_map(void *cookie, uintptr_t paddr, size_t size, 110 int cached, ps_mem_flags_t flags) 111{ 112 113 /* Call the real IO map function. */ 114 cookie_t *c = cookie; 115 void *p = c->map(NULL, paddr, size, cached, flags); 116 117 if (p != NULL) { 118 /* The IO map function gave us a successful result; track this pointer 119 * to lookup during unmapping. 120 */ 121 if (ll_prepend(c->malloc_ops, &c->mapped, p) != 0) { 122 LOG_ERROR("failed to track mapped IO pointer %p\n", p); 123 } 124 } 125 126 return p; 127} 128 129static int UNUSED pointer_compare(void *a, void *b) 130{ 131 uintptr_t p = (uintptr_t)a; 132 uintptr_t q = (uintptr_t)b; 133 if (p > q) { 134 return 1; 135 } else if (p < q) { 136 return -1; 137 } else { 138 return 0; 139 } 140} 141 142static void *camkes_io_map(void *cookie UNUSED, uintptr_t paddr, 143 size_t size, int cached UNUSED, ps_mem_flags_t flags UNUSED) 144{ 145 if (paddr % PAGE_SIZE_4K != 0 && size % PAGE_SIZE_4K != 0) { 146 ZF_LOGE("paddr or size has incorrect alignment: (%p, 0x%zx)", (void *) paddr, size); 147 return NULL; 148 } 149 150 /* Given a base paddr and size, we try to find a region of mapped memory that 151 * is a superset of the given parameters. */ 152 size_t size_counter = 0; 153 bool counting_frames = false; 154 uintptr_t base_vaddr = 0; 155 for (dataport_frame_t *frame = __start__dataport_frames; 156 frame < __stop__dataport_frames; frame++) { 157 if (counting_frames) { 158 if (paddr == (frame->paddr - size_counter)) { 159 size_counter += frame->size; 160 } else { 161 /* We've encountered a different region of physical memory that does 162 not match what we want, reset the counters */ 163 counting_frames = false; 164 base_vaddr = 0; 165 size_counter = 0; 166 } 167 } else { 168 if (paddr >= frame->paddr && (frame->paddr + frame->size) > paddr) { 169 /* We've found the first frame of the mapped region, 170 start counting from here */ 171 counting_frames = true; 172 base_vaddr = frame->vaddr + (paddr - frame->paddr); 173 size_counter += (frame->vaddr + frame->size) - base_vaddr; 174 } 175 } 176 177 if (size_counter >= size) { 178 /* We've found all the frames that cover the desired region */ 179 return (void *)base_vaddr; 180 } 181 } 182 183 /* Not found. */ 184 return NULL; 185} 186 187/* We never unmap anything. */ 188static void io_unmap(void *cookie UNUSED, void *vaddr UNUSED, size_t size UNUSED) 189{ 190#ifndef NDEBUG 191 cookie_t *c = cookie; 192 /* Make sure we previously mapped the pointer the caller gave us. */ 193 if (ll_remove(c->malloc_ops, &c->mapped, vaddr) != 0) { 194 LOG_ERROR("unmapping an IO pointer that was not previously mapped: %p\n", 195 vaddr); 196 } 197#endif 198} 199 200int camkes_io_mapper(ps_io_mapper_t *mapper) 201{ 202 if (mapper == NULL) { 203 ZF_LOGE("mapper is NULL"); 204 return -1; 205 } 206#ifdef NDEBUG 207 mapper->cookie = NULL; 208 mapper->io_map_fn = camkes_io_map; 209#else 210 if (!malloc_ops_initialised) { 211 ZF_LOGF_IF(camkes_ps_malloc_ops(&io_mapper_malloc_ops), 212 "Failed to get malloc_ops for DEBUG mode io mapper"); 213 malloc_ops_initialised = true; 214 } 215 cookie_t *c = NULL; 216 int error = ps_calloc(&io_mapper_malloc_ops, 1, sizeof(*c), (void **) &c); 217 if (error) { 218 return -1; 219 } 220 c->malloc_ops = &io_mapper_malloc_ops; 221 c->map = camkes_io_map; 222 c->mapped = NULL; 223 mapper->cookie = c; 224 mapper->io_map_fn = io_map; 225#endif 226 mapper->io_unmap_fn = io_unmap; 227 return 0; 228} 229 230static int camkes_io_port_in(void *cookie UNUSED, uint32_t port, int io_size, uint32_t *result) 231{ 232 return camkes_arch_io_port_in(port, io_size, result); 233} 234 235static int camkes_io_port_out(void *cookie UNUSED, uint32_t port, int io_size, uint32_t val) 236{ 237 return camkes_arch_io_port_out(port, io_size, val); 238} 239 240int camkes_io_port_ops(ps_io_port_ops_t *ops) 241{ 242 if (ops == NULL) { 243 ZF_LOGE("ops is NULL"); 244 return -1; 245 } 246 ops->io_port_in_fn = camkes_io_port_in; 247 ops->io_port_out_fn = camkes_io_port_out; 248 return 0; 249} 250 251int camkes_ps_malloc_ops(ps_malloc_ops_t *ops) 252{ 253 if (ops == NULL) { 254 ZF_LOGE("ops is NULL"); 255 return -1; 256 } 257 258 int ret = ps_new_stdlib_malloc_ops(ops); 259 if (ret) { 260 return ret; 261 } 262 263#ifndef NDEBUG 264 /* This works as malloc_ops contains pointers */ 265 malloc_ops_initialised = true; 266 io_mapper_malloc_ops = (ps_malloc_ops_t) * ops; 267#endif 268 269 return 0; 270} 271 272static char *camkes_io_fdt_get(void *cookie) 273{ 274 return (char *)(cookie ? cookie : NULL); 275} 276 277int camkes_io_fdt(ps_io_fdt_t *io_fdt) 278{ 279 if (io_fdt == NULL) { 280 ZF_LOGE("io_fdt is NULL"); 281 return -1; 282 } 283 284 extern char *dtb_symbol WEAK; 285 286 if (!&dtb_symbol) { 287 io_fdt->cookie = NULL; 288 } else { 289 /* the buffer contains the bootinfo header, so we skip it */ 290 io_fdt->cookie = (void *) &dtb_symbol + sizeof(seL4_BootInfoHeader); 291 } 292 293 io_fdt->get_fn = camkes_io_fdt_get; 294 295 return 0; 296} 297 298/* Force the _driver_modules section to be created even if no modules are defined. */ 299static USED SECTION("_driver_modules") struct {} dummy_driver_module; 300/* Definitions so that we can find the list of driver modules that can be initialised */ 301extern ps_driver_module_t *__start__driver_modules[]; 302extern ps_driver_module_t *__stop__driver_modules[]; 303 304static ll_t *driver_defer_list = NULL; 305 306static int defer_driver_init(ps_io_ops_t *ops, char *device_path, ps_driver_init_fn_t init_func) 307{ 308 if (ops == NULL) { 309 ZF_LOGE("ops is NULL"); 310 return -1; 311 } 312 313 if (device_path == NULL) { 314 ZF_LOGE("device path is NULL"); 315 return -1; 316 } 317 318 if (init_func == NULL) { 319 ZF_LOGE("init_func is NULL"); 320 return -1; 321 } 322 323 camkes_defer_token_t *defer_token = NULL; 324 int error = ps_calloc(&ops->malloc_ops, 1, sizeof(*defer_token), (void **) &defer_token); 325 if (error) { 326 ZF_LOGE("Failed to allocate memory for a bookkeeping structure for the driver defer list"); 327 return -1; 328 } 329 330 defer_token->device_path = device_path; 331 defer_token->init_func = init_func; 332 333 error = ll_append(&ops->malloc_ops, &driver_defer_list, defer_token); 334 if (error) { 335 ZF_LOGE("Failed to add the driver init function to the defer list"); 336 } 337 338 return 0; 339} 340 341static int find_compatible_driver_module(ps_io_ops_t *ops, int node_offset, char *device_path) 342{ 343 if (ops == NULL) { 344 ZF_LOGF("ops is NULL"); 345 } 346 347 char *dtb_blob = ps_io_fdt_get(&ops->io_fdt); 348 if (!dtb_blob) { 349 ZF_LOGF("No DTB supplied!"); 350 } 351 352 /* Look through the list of hardware modules that are registered and 353 * pick the most suitable one by comparing the module's compatible 354 * strings against the device node's */ 355 for (ps_driver_module_t **module = __start__driver_modules; module < __stop__driver_modules; module++) { 356 for (const char **curr_str = (*module)->compatible_list; *curr_str != NULL; curr_str++) { 357 int match_ret = fdt_node_check_compatible(dtb_blob, node_offset, *curr_str); 358 if (match_ret == 0) { 359 /* Found a match! */ 360 int ret = (*module)->init(ops, device_path); 361 if (ret == PS_DRIVER_INIT_DEFER) { 362 ZF_LOGF_IF(defer_driver_init(ops, device_path, (*module)->init), 363 "Failed to defer the initialisation of node %s", device_path); 364 return 0; 365 } else if (ret < 0) { 366 ZF_LOGE("Node pointed to by path %s failed to have a driver initialise properly, ignoring", device_path); 367 return -1; 368 } else if (ret == PS_DRIVER_INIT_SUCCESS) { 369 return 0; 370 } else { 371 ZF_LOGE("Initialisation function for node pointed to by path %s returned an unexpected code", device_path); 372 return -1; 373 } 374 } else if (match_ret < 0) { 375 ZF_LOGE("Node pointed to by path %s is malformed, ignoring", device_path); 376 return -1; 377 } 378 /* Ignore the no match case */ 379 } 380 } 381 382 /* No suitable module was found for this device node */ 383 ZF_LOGE("No suitable driver was found for path %s, ignoring", device_path); 384 return 0; 385} 386 387static int handle_defered_modules(ps_io_ops_t *ops) 388{ 389 if (ops == NULL) { 390 ZF_LOGF("ops is NULL"); 391 } 392 393 /* The set of modules that have deferred and that we need to work through */ 394 ll_t *working_set = driver_defer_list; 395 /* The set of modules that have deferred again */ 396 ll_t *deferred_set = NULL; 397 ll_t *deferred_tail = NULL; 398 399 while (working_set != NULL) { 400 ll_t *curr = working_set; 401 while (curr != NULL) { 402 camkes_defer_token_t *defer_token = curr->data; 403 int ret = defer_token->init_func(ops, defer_token->device_path); 404 if (ret == PS_DRIVER_INIT_DEFER) { 405 /* Throw this into deferred set and also remove it from the 406 * working set */ 407 if (deferred_tail != NULL) { 408 deferred_tail->next = curr; 409 } 410 if (deferred_set == NULL) { 411 deferred_set = curr; 412 } 413 deferred_tail = curr; 414 curr = curr->next; 415 deferred_tail->next = NULL; 416 } else { 417 /* Diagnostics */ 418 if (ret < 0) { 419 ZF_LOGE("Node pointed to by path %s failed to have a driver initialise properly, ignoring", 420 defer_token->device_path); 421 } else if (ret != PS_DRIVER_INIT_SUCCESS) { 422 ZF_LOGE("Initialisation function for node pointed to by path %s returned an unexpected code", 423 defer_token->device_path); 424 } 425 /* Remove this from the working set and deallocate memory */ 426 ll_t *temp = curr; 427 curr = curr->next; 428 ZF_LOGF_IF(ps_free(&ops->malloc_ops, sizeof(*defer_token), defer_token), 429 "Failed to deallocate a camkes_defer_token_t instance"); 430 ZF_LOGF_IF(ps_free(&ops->malloc_ops, sizeof(*temp), temp), "Failed to deallocate a ll_t node"); 431 } 432 } 433 /* Swap the two sets and continue on */ 434 working_set = deferred_set; 435 deferred_set = NULL; 436 deferred_tail = NULL; 437 } 438 439 return 0; 440} 441 442/* Force the _hardware_init section to be created even if no modules are defined. */ 443static USED SECTION("_hardware_init") struct {} dummy_init_module; 444/* Definitions so that we can find the list of hardware modules that we need to initialise */ 445extern char **__start__hardware_init[]; 446extern char **__stop__hardware_init[]; 447 448int camkes_call_hardware_init_modules(ps_io_ops_t *ops) 449{ 450 if (__start__hardware_init == __stop__hardware_init) { 451 /* Exit early if there are no modules to initialise */ 452 return 0; 453 } 454 455 if (ops == NULL) { 456 ZF_LOGE("ops is NULL"); 457 return -1; 458 } 459 460 char *dtb_blob = ps_io_fdt_get(&ops->io_fdt); 461 if (!dtb_blob) { 462 ZF_LOGE("No DTB supplied!"); 463 return -1; 464 } 465 466 for (char ***curr_path = __start__hardware_init; curr_path < __stop__hardware_init; curr_path++) { 467 int node_offset = fdt_path_offset(dtb_blob, **curr_path); 468 if (node_offset < 0) { 469 ZF_LOGE("Path %s doesn't seem to be in the DTB, ignoring", **curr_path); 470 continue; 471 } 472 473 /* Quick sanity check to see if the node has a compatible property */ 474 const void *dummy = fdt_getprop(dtb_blob, node_offset, "compatible", NULL); 475 if (dummy == NULL) { 476 ZF_LOGE("Node pointed to by path %s doesn't have a compatible string or is malformed, ignoring", **curr_path); 477 continue; 478 } 479 480 /* Currently most of the errors in this function as they aren't fatal */ 481 find_compatible_driver_module(ops, node_offset, **curr_path); 482 } 483 484 /* Now take care of the deferred modules, again ignore most of the errors 485 * in this function as they aren't fatal */ 486 handle_defered_modules(ops); 487 488 return 0; 489} 490 491int camkes_io_ops(ps_io_ops_t *ops) 492{ 493 if (ops == NULL) { 494 ZF_LOGE("ops is NULL"); 495 return -1; 496 } 497 498 int ret = camkes_ps_malloc_ops(&ops->malloc_ops); 499 if (ret) { 500 return ret; 501 } 502 503 ret = camkes_io_mapper(&ops->io_mapper) || 504 camkes_io_port_ops(&ops->io_port_ops) || 505 camkes_dma_manager(&ops->dma_manager) || 506 camkes_io_fdt(&ops->io_fdt) || 507 camkes_irq_ops(&ops->irq_ops) || 508 camkes_interface_registration_ops(&ops->interface_registration_ops, &ops->malloc_ops); 509 510 return ret; 511} 512