1/* 2 * Copyright 2019, 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 <assert.h> 14#include <stdint.h> 15#include <stdlib.h> 16#include <stdbool.h> 17#include <limits.h> 18#include <errno.h> 19 20#include <sel4/sel4.h> 21#include <sel4platsupport/irq.h> 22#include <sel4platsupport/device.h> 23#include <platsupport/irq.h> 24#include <platsupport/io.h> 25#include <simple/simple.h> 26#include <utils/util.h> 27#include <vka/vka.h> 28#include <vka/capops.h> 29 30#define UNPAIRED_ID -1 31#define UNALLOCATED_BADGE_INDEX -1 32 33typedef enum irq_iface_type { 34 STANDARD_IFACE, 35 MINI_IFACE 36} irq_iface_type_t; 37 38typedef struct { 39 /* These are always non-empty if this particular IRQ ID is in use */ 40 bool allocated; 41 ps_irq_t irq; 42 cspacepath_t handler_path; 43 44 /* These are not always non-empty if this particular IRQ ID is in use */ 45 46 /* Driver registers these */ 47 irq_callback_fn_t irq_callback_fn; 48 void *callback_data; 49 50 /* User registers these */ 51 cspacepath_t ntfn_path; 52 ntfn_id_t paired_ntfn; 53 int8_t allocated_badge_index; 54} irq_entry_t; 55 56typedef struct { 57 bool allocated; 58 cspacepath_t root_ntfn_path; 59 size_t num_irqs_bound; 60 61 seL4_Word usable_mask; 62 /* Bitfield tracking which of the bits in the badge are allocated */ 63 seL4_Word status_bitfield; 64 /* Bitfield tracking which IRQs have arrived but not served */ 65 seL4_Word pending_bitfield; 66 67 irq_id_t bound_irqs[MAX_INTERRUPTS_TO_NOTIFICATIONS]; 68} ntfn_entry_t; 69 70typedef struct { 71 irq_iface_type_t iface_type; 72 size_t num_registered_irqs; 73 size_t num_allocated_ntfns; 74 75 size_t max_irq_ids; 76 size_t max_ntfn_ids; 77 size_t num_irq_bitfields; 78 size_t num_ntfn_bitfields; 79 /* Array of bitfields tracking which IDs have been allocated */ 80 seL4_Word *allocated_irq_bitfields; 81 seL4_Word *allocated_ntfn_bitfields; 82 83 irq_entry_t *irq_table; 84 ntfn_entry_t *ntfn_table; 85 simple_t *simple; 86 vka_t *vka; 87 ps_malloc_ops_t *malloc_ops; 88} irq_cookie_t; 89 90typedef struct { 91 irq_cookie_t *irq_cookie; 92 irq_id_t irq_id; 93} ack_data_t; 94 95static inline bool check_irq_id_is_valid(irq_cookie_t *irq_cookie, irq_id_t id) 96{ 97 if (unlikely(id < 0 || id >= irq_cookie->max_irq_ids)) { 98 return false; 99 } 100 return true; 101} 102 103static inline bool check_ntfn_id_is_valid(irq_cookie_t *irq_cookie, ntfn_id_t id) 104{ 105 if (unlikely(id < 0 || id >= irq_cookie->max_ntfn_ids)) { 106 return false; 107 } 108 return true; 109} 110 111static inline bool check_irq_id_all_allocated(irq_cookie_t *irq_cookie) 112{ 113 if (unlikely(irq_cookie->num_registered_irqs >= irq_cookie->max_irq_ids)) { 114 return true; 115 } 116 return false; 117} 118 119static inline bool check_ntfn_id_all_allocated(irq_cookie_t *irq_cookie) 120{ 121 if (unlikely(irq_cookie->num_allocated_ntfns >= irq_cookie->max_ntfn_ids)) { 122 return true; 123 } 124 return false; 125} 126 127static inline bool check_ntfn_id_is_allocated(irq_cookie_t *irq_cookie, ntfn_id_t id) 128{ 129 if (likely(irq_cookie->ntfn_table[id].allocated)) { 130 return true; 131 } 132 return false; 133} 134 135static inline bool check_irq_id_is_allocated(irq_cookie_t *irq_cookie, irq_id_t id) 136{ 137 if (likely(irq_cookie->irq_table[id].allocated)) { 138 return true; 139 } 140 return false; 141} 142 143static inline void fill_bit_in_bitfield(seL4_Word *bitfield_array, int index) 144{ 145 int bitfield_index = index % (sizeof(seL4_Word) * CHAR_BIT); 146 int array_index = index / (sizeof(seL4_Word) * CHAR_BIT); 147 bitfield_array[array_index] |= BIT(bitfield_index); 148} 149 150static inline void unfill_bit_in_bitfield(seL4_Word *bitfield_array, int index) 151{ 152 int bitfield_index = index % (sizeof(seL4_Word) * CHAR_BIT); 153 int array_index = index / (sizeof(seL4_Word) * CHAR_BIT); 154 bitfield_array[array_index] &= ~(BIT(bitfield_index)); 155} 156 157static irq_id_t find_free_irq_id(irq_cookie_t *irq_cookie) 158{ 159 for (int i = 0; i < irq_cookie->num_irq_bitfields; i++) { 160 unsigned long unallocated_bitfield = ~(irq_cookie->allocated_irq_bitfields[i]); 161 if (i == irq_cookie->num_irq_bitfields - 1) { 162 /* Hide the bits that cannot be allocated, i.e. max_irq_ids is 163 * not a multiple of sizeof(seL4_Word) */ 164 unsigned long num_leftover_bits = irq_cookie->num_irq_bitfields * sizeof(seL4_Word) - 165 irq_cookie->max_irq_ids; 166 unsigned long mask = MASK(num_leftover_bits); 167 /* Reverse the mask, (bit 0 is the smallest ID of that bitfield) */ 168 mask = BSWAP_WORD(mask); 169 170 unallocated_bitfield &= mask; 171 } 172 /* Check to avoid undefined behaviour of CTZL(0) */ 173 if (likely(unallocated_bitfield)) { 174 return CTZL(unallocated_bitfield) + (i * sizeof(seL4_Word) * CHAR_BIT); 175 } 176 } 177 return -1; 178} 179 180static ntfn_id_t find_free_ntfn_id(irq_cookie_t *irq_cookie) 181{ 182 for (int i = 0; i < irq_cookie->num_ntfn_bitfields; i++) { 183 unsigned long unallocated_bitfield = ~(irq_cookie->allocated_ntfn_bitfields[i]); 184 if (i == irq_cookie->num_irq_bitfields - 1) { 185 /* Hide the bits that cannot be allocated, i.e. max_irq_ids is 186 * not a multiple of sizeof(seL4_Word) */ 187 unsigned long num_leftover_bits = irq_cookie->num_ntfn_bitfields * sizeof(seL4_Word) - 188 irq_cookie->max_ntfn_ids; 189 unsigned long mask = MASK(num_leftover_bits); 190 /* Reverse the mask, (bit 0 is the smallest ID of that bitfield) */ 191 mask = BSWAP_WORD(mask); 192 193 unallocated_bitfield &= mask; 194 } 195 /* Check to avoid undefined behaviour of CTZL(0) */ 196 if (likely(unallocated_bitfield)) { 197 return CTZL(unallocated_bitfield) + (i * sizeof(seL4_Word) * CHAR_BIT); 198 } 199 } 200 return -1; 201} 202 203static int find_free_ntfn_badge_index(ntfn_entry_t *ntfn_entry) 204{ 205 unsigned long unallocated_bitfield = ~(ntfn_entry->status_bitfield); 206 /* Mask the bits that we can use */ 207 unallocated_bitfield &= ntfn_entry->usable_mask; 208 /* Check to avoid undefined behaviour of CTZL(0) */ 209 if (unlikely(!unallocated_bitfield)) { 210 return -1; 211 } 212 return CTZL(unallocated_bitfield); 213} 214 215static int irq_set_ntfn_common(irq_cookie_t *irq_cookie, ntfn_id_t ntfn_id, irq_id_t irq_id, 216 seL4_Word *ret_badge) 217{ 218 irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]); 219 220 /* Check if the IRQ is already bound to something */ 221 if (irq_entry->paired_ntfn > UNPAIRED_ID) { 222 return -EINVAL; 223 } 224 225 ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[ntfn_id]); 226 227 /* Check if we have space to bound another IRQ to this notification */ 228 if (ntfn_entry->num_irqs_bound >= MAX_INTERRUPTS_TO_NOTIFICATIONS) { 229 return -ENOSPC; 230 } 231 232 /* Find an empty space (unclaimed bit in the badge) for the IRQ */ 233 int badge_index = find_free_ntfn_badge_index(ntfn_entry); 234 if (badge_index == -1) { 235 return -ENOSPC; 236 } 237 238 /* Allocate a CSpace slot and mint the root notification object with badge */ 239 cspacepath_t mint_path = {0}; 240 int error = vka_cspace_alloc_path(irq_cookie->vka, &mint_path); 241 if (error) { 242 return -ENOMEM; 243 } 244 245 seL4_Word badge = BIT(badge_index); 246 error = vka_cnode_mint(&mint_path, &(ntfn_entry->root_ntfn_path), seL4_AllRights, badge); 247 if (error) { 248 vka_cspace_free_path(irq_cookie->vka, mint_path); 249 return -ENOMEM; 250 } 251 252 /* Bind the notification with the handler now */ 253 error = seL4_IRQHandler_SetNotification(irq_entry->handler_path.capPtr, mint_path.capPtr); 254 if (error) { 255 ZF_LOGE("Failed to set a notification with the IRQ handler"); 256 error = vka_cnode_delete(&mint_path); 257 ZF_LOGF_IF(error, "Failed to cleanup after a failed IRQ set ntfn operation"); 258 vka_cspace_free_path(irq_cookie->vka, mint_path); 259 return -EFAULT; 260 } 261 262 /* Acknowledge the handler so interrupts can arrive on the notification */ 263 error = seL4_IRQHandler_Ack(irq_entry->handler_path.capPtr); 264 if (error) { 265 ZF_LOGE("Failed to ack an IRQ handler"); 266 error = seL4_IRQHandler_Clear(irq_entry->handler_path.capPtr); 267 ZF_LOGF_IF(error, "Failed to unpair a notification after a failed IRQ set ntfn operation"); 268 error = vka_cnode_delete(&mint_path); 269 ZF_LOGF_IF(error, "Failed to cleanup after a failed IRQ set ntfn operation"); 270 vka_cspace_free_path(irq_cookie->vka, mint_path); 271 return -EFAULT; 272 } 273 274 if (ret_badge) { 275 *ret_badge = badge; 276 } 277 278 /* Fill in the bookkeeping information */ 279 ntfn_entry->num_irqs_bound++; 280 ntfn_entry->status_bitfield |= badge; 281 ntfn_entry->bound_irqs[badge_index] = irq_id; 282 283 irq_entry->ntfn_path = mint_path; 284 irq_entry->paired_ntfn = ntfn_id; 285 irq_entry->allocated_badge_index = badge_index; 286 287 return 0; 288} 289 290static irq_id_t irq_register_common(irq_cookie_t *irq_cookie, ps_irq_t irq, irq_callback_fn_t callback, 291 void *callback_data) 292{ 293 if (check_irq_id_all_allocated(irq_cookie)) { 294 return -EMFILE; 295 } 296 297 if (!callback) { 298 return -EINVAL; 299 } 300 301 irq_id_t free_id = find_free_irq_id(irq_cookie); 302 if (free_id == -1) { 303 /* Probably wouldn't get here, as we checked above already */ 304 ZF_LOGE("Failed to find a free IRQ id"); 305 return -EMFILE; 306 } 307 308 /* Allocate a path for the IRQ handler object */ 309 cspacepath_t irq_handler_path = {0}; 310 vka_cspace_alloc_path(irq_cookie->vka, &irq_handler_path); 311 312 /* Create an IRQ handler object for this IRQ */ 313 int err = sel4platsupport_copy_irq_cap(irq_cookie->vka, irq_cookie->simple, &irq, &irq_handler_path); 314 if (err) { 315 /* Give a slightly ambigious message as we don't want to leak implementation details */ 316 ZF_LOGE("Failed to register an IRQ"); 317 vka_cspace_free_path(irq_cookie->vka, irq_handler_path); 318 return -EFAULT; 319 } 320 321 irq_entry_t *irq_entry = &(irq_cookie->irq_table[free_id]); 322 irq_entry->allocated = true; 323 irq_entry->irq = irq; 324 irq_entry->handler_path = irq_handler_path; 325 irq_entry->irq_callback_fn = callback; 326 irq_entry->callback_data = callback_data; 327 328 irq_cookie->num_registered_irqs++; 329 fill_bit_in_bitfield(irq_cookie->allocated_irq_bitfields, free_id); 330 331 return free_id; 332} 333 334static void provide_ntfn_common(irq_cookie_t *irq_cookie, seL4_CPtr ntfn, seL4_Word usable_mask, 335 ntfn_id_t allocated_id) 336{ 337 cspacepath_t ntfn_path = {0}; 338 vka_cspace_make_path(irq_cookie->vka, ntfn, &ntfn_path); 339 340 /* Clear the notification entry and then fill in bookkeeping information */ 341 ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[allocated_id]); 342 memset(ntfn_entry, 0, sizeof(ntfn_entry_t)); 343 ntfn_entry->allocated = true; 344 ntfn_entry->root_ntfn_path = ntfn_path; 345 ntfn_entry->usable_mask = usable_mask; 346 347 irq_cookie->num_allocated_ntfns++; 348 fill_bit_in_bitfield(irq_cookie->allocated_ntfn_bitfields, allocated_id); 349} 350 351static irq_cookie_t *new_irq_ops_common(vka_t *vka, simple_t *simple, irq_interface_config_t irq_config, 352 ps_malloc_ops_t *malloc_ops, irq_iface_type_t iface_type) 353{ 354 int err = 0; 355 /* Figure out how many bitfields we need to keep track of the allocation status of the IDs */ 356 size_t bits_in_seL4_Word = sizeof(seL4_Word) * CHAR_BIT; 357 size_t num_irq_bitfields = ALIGN_UP(irq_config.max_irq_ids, bits_in_seL4_Word) / sizeof(seL4_Word); 358 size_t num_ntfn_bitfields = ALIGN_UP(irq_config.max_ntfn_ids, bits_in_seL4_Word) / sizeof(seL4_Word); 359 360 irq_cookie_t *cookie = 0; 361 err = ps_calloc(malloc_ops, 1, sizeof(irq_cookie_t), (void **) &cookie); 362 if (err) { 363 ZF_LOGE("Failed to allocate %zu bytes for cookie", sizeof(irq_cookie_t)); 364 goto error; 365 } 366 367 /* Allocate the IRQ bookkeeping array, and set default values for some of the members */ 368 err = ps_calloc(malloc_ops, 1, sizeof(irq_entry_t) * irq_config.max_irq_ids, (void **) & (cookie->irq_table)); 369 if (err) { 370 ZF_LOGE("Failed to allocate IRQ bookkeeping array"); 371 goto error; 372 } 373 for (int i = 0; i < irq_config.max_irq_ids; i++) { 374 cookie->irq_table[i].paired_ntfn = UNPAIRED_ID; 375 cookie->irq_table[i].allocated_badge_index = UNALLOCATED_BADGE_INDEX; 376 } 377 378 /* Allocate the notification bookkeeping array, and set default values for some of the members */ 379 err = ps_calloc(malloc_ops, 1, sizeof(ntfn_entry_t) * irq_config.max_ntfn_ids, 380 (void **) & (cookie->ntfn_table)); 381 if (err) { 382 ZF_LOGE("Failed to allocate notification bookkeeping array"); 383 goto error; 384 } 385 for (int i = 0; i < irq_config.max_ntfn_ids; i++) { 386 memset(cookie->ntfn_table[i].bound_irqs, UNPAIRED_ID, sizeof(irq_id_t) * MAX_INTERRUPTS_TO_NOTIFICATIONS); 387 } 388 389 err = ps_calloc(malloc_ops, 1, num_irq_bitfields * sizeof(seL4_Word), 390 (void **) & (cookie->allocated_irq_bitfields)); 391 if (err) { 392 ZF_LOGE("Failed to allocate the IRQ bitfields"); 393 goto error; 394 } 395 err = ps_calloc(malloc_ops, 1, num_ntfn_bitfields * sizeof(seL4_Word), 396 (void **) & (cookie->allocated_ntfn_bitfields)); 397 if (err) { 398 ZF_LOGE("Failed to allocate the notification bitfields"); 399 goto error; 400 } 401 402 cookie->iface_type = iface_type; 403 cookie->simple = simple; 404 cookie->vka = vka; 405 cookie->malloc_ops = malloc_ops; 406 cookie->max_irq_ids = irq_config.max_irq_ids; 407 cookie->max_ntfn_ids = irq_config.max_ntfn_ids; 408 cookie->num_irq_bitfields = num_irq_bitfields; 409 cookie->num_ntfn_bitfields = num_ntfn_bitfields; 410 411 return cookie; 412 413error: 414 if (cookie) { 415 if (cookie->irq_table) { 416 ps_free(malloc_ops, sizeof(irq_entry_t) * irq_config.max_irq_ids, cookie->irq_table); 417 } 418 419 if (cookie->ntfn_table) { 420 ps_free(malloc_ops, sizeof(ntfn_entry_t) * irq_config.max_ntfn_ids, 421 cookie->ntfn_table); 422 } 423 424 if (cookie->allocated_irq_bitfields) { 425 ps_free(malloc_ops, sizeof(seL4_Word) * num_irq_bitfields, cookie->allocated_irq_bitfields); 426 } 427 428 ps_free(malloc_ops, sizeof(irq_cookie_t), cookie); 429 } 430 431 return NULL; 432} 433 434static int sel4platsupport_irq_unregister(void *cookie, irq_id_t irq_id) 435{ 436 irq_cookie_t *irq_cookie = cookie; 437 438 if (!check_irq_id_is_valid(irq_cookie, irq_id)) { 439 return -EINVAL; 440 } 441 442 if (!check_irq_id_is_allocated(irq_cookie, irq_id)) { 443 return -EINVAL; 444 } 445 446 irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]); 447 448 if (irq_entry->paired_ntfn > UNPAIRED_ID) { 449 /* Clear the handler */ 450 int error = seL4_IRQHandler_Clear(irq_entry->handler_path.capPtr); 451 if (error) { 452 /* Give a slightly ambigious message as we don't want to leak implementation details */ 453 ZF_LOGE("Failed to unregister an IRQ"); 454 return -EFAULT; 455 } 456 457 /* Delete the notification */ 458 vka_cnode_delete(&(irq_entry->ntfn_path)); 459 vka_cspace_free_path(irq_cookie->vka, irq_entry->ntfn_path); 460 461 /* Clear the necessary information in the notification array */ 462 ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[irq_entry->paired_ntfn]); 463 ntfn_entry->status_bitfield &= ~BIT(irq_entry->allocated_badge_index); 464 ntfn_entry->pending_bitfield &= ~BIT(irq_entry->allocated_badge_index); 465 ntfn_entry->bound_irqs[irq_entry->allocated_badge_index] = UNPAIRED_ID; 466 ntfn_entry->num_irqs_bound--; 467 } 468 469 /* Delete the handler */ 470 vka_cnode_delete(&(irq_entry->handler_path)); 471 vka_cspace_free_path(irq_cookie->vka, irq_entry->handler_path); 472 473 /* Zero-out the entire entry */ 474 memset(irq_entry, 0, sizeof(irq_entry_t)); 475 /* Reset parts of the entry */ 476 irq_entry->paired_ntfn = UNPAIRED_ID; 477 irq_entry->allocated_badge_index = UNALLOCATED_BADGE_INDEX; 478 479 irq_cookie->num_registered_irqs--; 480 unfill_bit_in_bitfield(irq_cookie->allocated_irq_bitfields, irq_id); 481 482 return 0; 483} 484 485/* The register function for the standard IRQ interface */ 486static irq_id_t sel4platsupport_irq_register(void *cookie, ps_irq_t irq, irq_callback_fn_t callback, 487 void *callback_data) 488{ 489 irq_cookie_t *irq_cookie = cookie; 490 491 return irq_register_common(irq_cookie, irq, callback, callback_data); 492} 493 494/* The register function for the mini IRQ interface */ 495static irq_id_t sel4platsupport_irq_register_mini(void *cookie, ps_irq_t irq, irq_callback_fn_t callback, 496 void *callback_data) 497{ 498 irq_cookie_t *irq_cookie = cookie; 499 500 irq_id_t assigned_id = irq_register_common(irq_cookie, irq, callback, callback_data); 501 if (assigned_id < 0) { 502 /* Contains the error code if < 0 */ 503 return assigned_id; 504 } 505 506 /* Pair this IRQ with the only notification */ 507 int error = irq_set_ntfn_common(irq_cookie, MINI_IRQ_INTERFACE_NTFN_ID, assigned_id, NULL); 508 if (error) { 509 ZF_LOGF_IF(sel4platsupport_irq_unregister(irq_cookie, assigned_id), 510 "Failed to clean-up a failed operation"); 511 return error; 512 } 513 514 return assigned_id; 515} 516 517static int sel4platsupport_irq_acknowledge(void *ack_data) 518{ 519 if (!ack_data) { 520 return -EINVAL; 521 } 522 523 int ret = 0; 524 525 ack_data_t *data = ack_data; 526 irq_cookie_t *irq_cookie = data->irq_cookie; 527 irq_id_t irq_id = data->irq_id; 528 529 if (!check_irq_id_is_valid(irq_cookie, irq_id)) { 530 ret = -EINVAL; 531 goto exit; 532 } 533 534 if (!check_irq_id_is_allocated(irq_cookie, irq_id)) { 535 ret = -EINVAL; 536 goto exit; 537 } 538 539 irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]); 540 int error = seL4_IRQHandler_Ack(irq_entry->handler_path.capPtr); 541 if (error) { 542 ZF_LOGE("Failed to acknowledge IRQ"); 543 ret = -EFAULT; 544 goto exit; 545 } 546 547exit: 548 ps_free(irq_cookie->malloc_ops, sizeof(ack_data_t), data); 549 550 return ret; 551} 552 553int sel4platsupport_new_irq_ops(ps_irq_ops_t *irq_ops, vka_t *vka, simple_t *simple, 554 irq_interface_config_t irq_config, ps_malloc_ops_t *malloc_ops) 555{ 556 if (!irq_ops || !vka || !simple || !malloc_ops) { 557 return -EINVAL; 558 } 559 560 if (irq_config.max_irq_ids == 0 || irq_config.max_ntfn_ids == 0) { 561 return -EINVAL; 562 } 563 564 irq_cookie_t *cookie = new_irq_ops_common(vka, simple, irq_config, malloc_ops, STANDARD_IFACE); 565 if (!cookie) { 566 return -ENOMEM; 567 } 568 569 /* Fill in the actual IRQ ops structure now */ 570 irq_ops->cookie = (void *) cookie; 571 irq_ops->irq_register_fn = sel4platsupport_irq_register; 572 irq_ops->irq_unregister_fn = sel4platsupport_irq_unregister; 573 574 return 0; 575} 576 577int sel4platsupport_new_mini_irq_ops(ps_irq_ops_t *irq_ops, vka_t *vka, simple_t *simple, 578 ps_malloc_ops_t *malloc_ops, seL4_CPtr ntfn, seL4_Word usable_mask) 579{ 580 if (!irq_ops || !vka || !simple || !malloc_ops || !usable_mask) { 581 return -EINVAL; 582 } 583 584 if (ntfn == seL4_CapNull) { 585 return -EINVAL; 586 } 587 588 /* Get the number of bits that we can use in the badge, 589 * this is the amount of interrupts that can be registered at a given time */ 590 size_t bits_in_mask = POPCOUNTL(usable_mask); 591 592 /* max_ntfn_ids is kinda irrelevant in the mini interface, but set it anyway */ 593 irq_interface_config_t irq_config = { .max_irq_ids = bits_in_mask, .max_ntfn_ids = 1 }; 594 595 irq_cookie_t *cookie = new_irq_ops_common(vka, simple, irq_config, malloc_ops, MINI_IFACE); 596 if (!cookie) { 597 return -ENOMEM; 598 } 599 600 /* Fill in the actual IRQ ops structure now */ 601 irq_ops->cookie = (void *) cookie; 602 irq_ops->irq_register_fn = sel4platsupport_irq_register_mini; 603 irq_ops->irq_unregister_fn = sel4platsupport_irq_unregister; 604 605 /* Provide the ntfn */ 606 provide_ntfn_common(cookie, ntfn, usable_mask, MINI_IRQ_INTERFACE_NTFN_ID); 607 608 return 0; 609} 610 611ntfn_id_t sel4platsupport_irq_provide_ntfn(ps_irq_ops_t *irq_ops, seL4_CPtr ntfn, seL4_Word usable_mask) 612{ 613 if (!irq_ops || ntfn == seL4_CapNull || !usable_mask) { 614 return -EINVAL; 615 } 616 617 irq_cookie_t *irq_cookie = irq_ops->cookie; 618 619 if (irq_cookie->iface_type == MINI_IFACE) { 620 ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__); 621 return -EPERM; 622 } 623 624 if (check_ntfn_id_all_allocated(irq_cookie)) { 625 return -EMFILE; 626 } 627 628 ntfn_id_t free_id = find_free_ntfn_id(irq_cookie); 629 if (free_id == -1) { 630 return -EMFILE; 631 } 632 633 provide_ntfn_common(irq_cookie, ntfn, usable_mask, free_id); 634 635 return free_id; 636} 637 638int sel4platsupport_irq_provide_ntfn_with_id(ps_irq_ops_t *irq_ops, seL4_CPtr ntfn, 639 seL4_Word usable_mask, ntfn_id_t id_hint) 640{ 641 if (!irq_ops || ntfn == seL4_CapNull || !usable_mask) { 642 return -EINVAL; 643 } 644 645 irq_cookie_t *irq_cookie = irq_ops->cookie; 646 647 if (irq_cookie->iface_type == MINI_IFACE) { 648 ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__); 649 return -EPERM; 650 } 651 652 if (check_ntfn_id_all_allocated(irq_cookie)) { 653 return -EMFILE; 654 } 655 656 if (irq_cookie->ntfn_table[id_hint].allocated) { 657 return -EADDRINUSE; 658 } 659 660 provide_ntfn_common(irq_cookie, ntfn, usable_mask, id_hint); 661 662 return 0; 663} 664 665int sel4platsupport_irq_return_ntfn(ps_irq_ops_t *irq_ops, ntfn_id_t ntfn_id, 666 seL4_CPtr *ret_cptr) 667{ 668 if (!irq_ops) { 669 return -EINVAL; 670 } 671 672 irq_cookie_t *irq_cookie = irq_ops->cookie; 673 674 if (irq_cookie->iface_type == MINI_IFACE) { 675 ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__); 676 return -EPERM; 677 } 678 679 if (!check_ntfn_id_is_valid(irq_cookie, ntfn_id)) { 680 return -EINVAL; 681 } 682 683 if (!check_ntfn_id_is_allocated(irq_cookie, ntfn_id)) { 684 return -EINVAL; 685 } 686 687 ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[ntfn_id]); 688 689 if (ntfn_entry->num_irqs_bound > 0) { 690 unsigned long allocated_bits = ntfn_entry->status_bitfield; 691 while (allocated_bits) { 692 unsigned long index = CTZL(allocated_bits); 693 irq_entry_t *irq_entry = &(irq_cookie->irq_table[ntfn_entry->bound_irqs[index]]); 694 seL4_IRQHandler_Clear(irq_entry->handler_path.capPtr); 695 int error = vka_cnode_delete(&(irq_entry->ntfn_path)); 696 ZF_LOGF_IF(error, "Failed to delete a minted notification"); 697 irq_entry->ntfn_path = (cspacepath_t) { 698 0 699 }; 700 irq_entry->paired_ntfn = UNPAIRED_ID; 701 irq_entry->allocated_badge_index = UNALLOCATED_BADGE_INDEX; 702 703 allocated_bits &= ~BIT(index); 704 } 705 } 706 707 if (ret_cptr) { 708 *ret_cptr = ntfn_entry->root_ntfn_path.capPtr; 709 } 710 711 /* Zero out the entire entry */ 712 memset(ntfn_entry, 0, sizeof(ntfn_entry_t)); 713 /* Reset the bound_irqs array for the entry */ 714 memset(ntfn_entry->bound_irqs, UNPAIRED_ID, sizeof(irq_id_t) * MAX_INTERRUPTS_TO_NOTIFICATIONS); 715 716 irq_cookie->num_allocated_ntfns--; 717 unfill_bit_in_bitfield(irq_cookie->allocated_ntfn_bitfields, ntfn_id); 718 719 return 0; 720} 721 722int sel4platsupport_irq_set_ntfn(ps_irq_ops_t *irq_ops, ntfn_id_t ntfn_id, irq_id_t irq_id, seL4_Word *ret_badge) 723{ 724 if (!irq_ops) { 725 return -EINVAL; 726 } 727 728 irq_cookie_t *irq_cookie = irq_ops->cookie; 729 730 if (irq_cookie->iface_type == MINI_IFACE) { 731 ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__); 732 return -EPERM; 733 } 734 735 if (!check_ntfn_id_is_valid(irq_cookie, ntfn_id) || 736 !check_ntfn_id_is_allocated(irq_cookie, ntfn_id)) { 737 return -EINVAL; 738 } 739 740 if (!check_irq_id_is_valid(irq_cookie, irq_id) || 741 !check_irq_id_is_allocated(irq_cookie, irq_id)) { 742 return -EINVAL; 743 } 744 745 return irq_set_ntfn_common(irq_cookie, ntfn_id, irq_id, ret_badge); 746} 747 748int sel4platsupport_irq_unset_ntfn(ps_irq_ops_t *irq_ops, irq_id_t irq_id) 749{ 750 if (!irq_ops) { 751 return -EINVAL; 752 } 753 754 irq_cookie_t *irq_cookie = irq_ops->cookie; 755 756 if (irq_cookie->iface_type == MINI_IFACE) { 757 ZF_LOGE("Trying to use %s with the mini IRQ Interface", __func__); 758 return -EPERM; 759 } 760 761 if (!check_irq_id_is_valid(irq_cookie, irq_id) || 762 !check_irq_id_is_allocated(irq_cookie, irq_id)) { 763 return -EINVAL; 764 } 765 766 irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]); 767 768 /* Check if the IRQ is bound to a notification */ 769 if (irq_entry->paired_ntfn == UNPAIRED_ID) { 770 return -EINVAL; 771 } 772 773 /* Unbind the notification from the handler */ 774 int error = seL4_IRQHandler_Clear(irq_entry->handler_path.capPtr); 775 if (error) { 776 ZF_LOGE("Failed to unpair a notification"); 777 return -EFAULT; 778 } 779 780 /* Delete the minted notification and free the path */ 781 vka_cnode_delete(&(irq_entry->ntfn_path)); 782 vka_cspace_free_path(irq_cookie->vka, irq_entry->ntfn_path); 783 784 /* Free the allocated badge index and update the bookkeeping in the notification array and the irq array */ 785 ntfn_id_t paired_id = irq_entry->paired_ntfn; 786 ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[paired_id]); 787 ntfn_entry->status_bitfield &= ~BIT(irq_entry->allocated_badge_index); 788 ntfn_entry->pending_bitfield &= ~BIT(irq_entry->allocated_badge_index); 789 ntfn_entry->num_irqs_bound--; 790 ntfn_entry->bound_irqs[irq_entry->allocated_badge_index] = UNPAIRED_ID; 791 792 irq_entry->ntfn_path = (cspacepath_t) { 793 0 794 }; 795 irq_entry->paired_ntfn = UNPAIRED_ID; 796 irq_entry->allocated_badge_index = UNALLOCATED_BADGE_INDEX; 797 798 return 0; 799} 800 801static bool perform_callback(irq_cookie_t *irq_cookie, irq_id_t irq_id, unsigned long badge_bit) 802{ 803 irq_entry_t *irq_entry = &(irq_cookie->irq_table[irq_id]); 804 irq_callback_fn_t callback = irq_entry->irq_callback_fn; 805 806 /* Check if callback was registered, if so, then run it */ 807 if (callback) { 808 ack_data_t *ack_data = NULL; 809 int error = ps_calloc(irq_cookie->malloc_ops, 1, sizeof(ack_data_t), (void **) &ack_data); 810 ZF_LOGF_IF(error, "Failed to allocate memory for a cookie for the acknowledge function"); 811 *ack_data = (ack_data_t) { 812 .irq_cookie = irq_cookie, .irq_id = irq_id 813 }; 814 callback(irq_entry->callback_data, 815 sel4platsupport_irq_acknowledge, ack_data); 816 return true; 817 } 818 return false; 819} 820 821int sel4platsupport_irq_handle(ps_irq_ops_t *irq_ops, ntfn_id_t ntfn_id, seL4_Word handle_mask) 822{ 823 if (!irq_ops) { 824 return -EINVAL; 825 } 826 827 irq_cookie_t *irq_cookie = irq_ops->cookie; 828 829 if (!check_ntfn_id_is_valid(irq_cookie, ntfn_id) || 830 !check_ntfn_id_is_allocated(irq_cookie, ntfn_id)) { 831 return -EINVAL; 832 } 833 834 ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[ntfn_id]); 835 836 /* Just in case, but probably should throw an error at the user for passing in bits that 837 * we dont' handle */ 838 unsigned long unchecked_bits = handle_mask & ntfn_entry->usable_mask; 839 840 while (unchecked_bits) { 841 unsigned long bit_index = CTZL(unchecked_bits); 842 irq_id_t paired_irq_id = ntfn_entry->bound_irqs[bit_index]; 843 bool callback_called = perform_callback(irq_cookie, paired_irq_id, bit_index); 844 if (callback_called && ntfn_entry->pending_bitfield & BIT(bit_index)) { 845 /* Unset the bit, we've performed the callback for that interrupt */ 846 ntfn_entry->pending_bitfield &= ~BIT(bit_index); 847 } 848 unchecked_bits &= ~BIT(bit_index); 849 } 850 851 return 0; 852} 853 854static void serve_irq(irq_cookie_t *irq_cookie, ntfn_id_t id, seL4_Word mask, 855 seL4_Word badge, seL4_Word *ret_leftover_bits) 856{ 857 seL4_Word served_mask = 0; 858 859 ntfn_entry_t *ntfn_entry = &(irq_cookie->ntfn_table[id]); 860 861 /* Mask out the bits the are not relevant to us */ 862 unsigned long unchecked_bits = badge & ntfn_entry->usable_mask; 863 /* Also check the interrupts that were leftover and not served */ 864 unchecked_bits |= ntfn_entry->pending_bitfield; 865 866 while (unchecked_bits) { 867 unsigned long bit_index = CTZL(unchecked_bits); 868 869 if (likely(BIT(bit_index) & mask)) { 870 irq_id_t paired_irq_id = ntfn_entry->bound_irqs[bit_index]; 871 if (perform_callback(irq_cookie, paired_irq_id, bit_index)) { 872 /* Record that this particular IRQ was served */ 873 served_mask |= BIT(bit_index); 874 } 875 } 876 877 unchecked_bits &= ~BIT(bit_index); 878 } 879 880 /* Record the IRQs that were not served but arrived, note that we don't want to 881 * override the leftover IRQs still inside the pending bitfield */ 882 ntfn_entry->pending_bitfield ^= badge & ntfn_entry->usable_mask & ~(served_mask); 883 884 /* Write bits that are leftover */ 885 if (ret_leftover_bits) { 886 *ret_leftover_bits = badge & ~(served_mask); 887 } 888} 889 890int sel4platsupport_irq_wait(ps_irq_ops_t *irq_ops, ntfn_id_t ntfn_id, 891 seL4_Word wait_mask, 892 seL4_Word *ret_leftover_bits) 893{ 894 if (!irq_ops) { 895 return -EINVAL; 896 } 897 898 irq_cookie_t *irq_cookie = irq_ops->cookie; 899 900 if (!check_ntfn_id_is_valid(irq_cookie, ntfn_id)) { 901 return -EINVAL; 902 } 903 904 if (!check_ntfn_id_is_allocated(irq_cookie, ntfn_id)) { 905 return -EINVAL; 906 } 907 908 seL4_CPtr ntfn = irq_cookie->ntfn_table[ntfn_id].root_ntfn_path.capPtr; 909 910 seL4_Word badge = 0; 911 912 /* Wait on the notification object */ 913 seL4_Wait(ntfn, &badge); 914 915 serve_irq(irq_cookie, ntfn_id, wait_mask, badge, ret_leftover_bits); 916 917 return 0; 918 919} 920 921int sel4platsupport_irq_poll(ps_irq_ops_t *irq_ops, ntfn_id_t ntfn_id, 922 seL4_Word poll_mask, 923 seL4_Word *ret_leftover_bits) 924{ 925 if (!irq_ops) { 926 return -EINVAL; 927 } 928 929 irq_cookie_t *irq_cookie = irq_ops->cookie; 930 931 if (!check_ntfn_id_is_valid(irq_cookie, ntfn_id)) { 932 return -EINVAL; 933 } 934 935 if (!check_ntfn_id_is_allocated(irq_cookie, ntfn_id)) { 936 return -EINVAL; 937 } 938 939 seL4_CPtr ntfn = irq_cookie->ntfn_table[ntfn_id].root_ntfn_path.capPtr; 940 941 seL4_Word badge = 0; 942 943 /* Poll the notification object */ 944 seL4_Poll(ntfn, &badge); 945 946 serve_irq(irq_cookie, ntfn_id, poll_mask, badge, ret_leftover_bits); 947 948 return 0; 949} 950