1*91f16700Schasinglulu /* 2*91f16700Schasinglulu * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. 3*91f16700Schasinglulu * 4*91f16700Schasinglulu * SPDX-License-Identifier: BSD-3-Clause 5*91f16700Schasinglulu */ 6*91f16700Schasinglulu 7*91f16700Schasinglulu #include <arch_helpers.h> 8*91f16700Schasinglulu #include <assert.h> 9*91f16700Schasinglulu #include <inttypes.h> 10*91f16700Schasinglulu #include <stddef.h> 11*91f16700Schasinglulu #include <stdint.h> 12*91f16700Schasinglulu #include <string.h> 13*91f16700Schasinglulu 14*91f16700Schasinglulu #include <bl31/bl31.h> 15*91f16700Schasinglulu #include <bl31/ehf.h> 16*91f16700Schasinglulu #include <bl31/interrupt_mgmt.h> 17*91f16700Schasinglulu #include <common/bl_common.h> 18*91f16700Schasinglulu #include <common/debug.h> 19*91f16700Schasinglulu #include <common/runtime_svc.h> 20*91f16700Schasinglulu #include <context.h> 21*91f16700Schasinglulu #include <lib/cassert.h> 22*91f16700Schasinglulu #include <lib/el3_runtime/pubsub.h> 23*91f16700Schasinglulu #include <lib/utils.h> 24*91f16700Schasinglulu #include <plat/common/platform.h> 25*91f16700Schasinglulu #include <services/sdei.h> 26*91f16700Schasinglulu 27*91f16700Schasinglulu #include "sdei_private.h" 28*91f16700Schasinglulu 29*91f16700Schasinglulu #define MAJOR_VERSION 1ULL 30*91f16700Schasinglulu #define MINOR_VERSION 0ULL 31*91f16700Schasinglulu #define VENDOR_VERSION 0ULL 32*91f16700Schasinglulu 33*91f16700Schasinglulu #define MAKE_SDEI_VERSION(_major, _minor, _vendor) \ 34*91f16700Schasinglulu ((((_major)) << 48ULL) | (((_minor)) << 32ULL) | (_vendor)) 35*91f16700Schasinglulu 36*91f16700Schasinglulu #define LOWEST_INTR_PRIORITY 0xff 37*91f16700Schasinglulu 38*91f16700Schasinglulu CASSERT(PLAT_SDEI_CRITICAL_PRI < PLAT_SDEI_NORMAL_PRI, 39*91f16700Schasinglulu sdei_critical_must_have_higher_priority); 40*91f16700Schasinglulu 41*91f16700Schasinglulu static unsigned int num_dyn_priv_slots, num_dyn_shrd_slots; 42*91f16700Schasinglulu 43*91f16700Schasinglulu /* Initialise SDEI map entries */ 44*91f16700Schasinglulu static void init_map(sdei_ev_map_t *map) 45*91f16700Schasinglulu { 46*91f16700Schasinglulu map->reg_count = 0; 47*91f16700Schasinglulu } 48*91f16700Schasinglulu 49*91f16700Schasinglulu /* Convert mapping to SDEI class */ 50*91f16700Schasinglulu static sdei_class_t map_to_class(sdei_ev_map_t *map) 51*91f16700Schasinglulu { 52*91f16700Schasinglulu return is_event_critical(map) ? SDEI_CRITICAL : SDEI_NORMAL; 53*91f16700Schasinglulu } 54*91f16700Schasinglulu 55*91f16700Schasinglulu /* Clear SDEI event entries except state */ 56*91f16700Schasinglulu static void clear_event_entries(sdei_entry_t *se) 57*91f16700Schasinglulu { 58*91f16700Schasinglulu se->ep = 0; 59*91f16700Schasinglulu se->arg = 0; 60*91f16700Schasinglulu se->affinity = 0; 61*91f16700Schasinglulu se->reg_flags = 0; 62*91f16700Schasinglulu } 63*91f16700Schasinglulu 64*91f16700Schasinglulu /* Perform CPU-specific state initialisation */ 65*91f16700Schasinglulu static void *sdei_cpu_on_init(const void *arg) 66*91f16700Schasinglulu { 67*91f16700Schasinglulu unsigned int i; 68*91f16700Schasinglulu sdei_ev_map_t *map; 69*91f16700Schasinglulu sdei_entry_t *se; 70*91f16700Schasinglulu 71*91f16700Schasinglulu /* Initialize private mappings on this CPU */ 72*91f16700Schasinglulu for_each_private_map(i, map) { 73*91f16700Schasinglulu se = get_event_entry(map); 74*91f16700Schasinglulu clear_event_entries(se); 75*91f16700Schasinglulu se->state = 0; 76*91f16700Schasinglulu } 77*91f16700Schasinglulu 78*91f16700Schasinglulu SDEI_LOG("Private events initialized on %lx\n", read_mpidr_el1()); 79*91f16700Schasinglulu 80*91f16700Schasinglulu /* All PEs start with SDEI events masked */ 81*91f16700Schasinglulu (void) sdei_pe_mask(); 82*91f16700Schasinglulu 83*91f16700Schasinglulu return NULL; 84*91f16700Schasinglulu } 85*91f16700Schasinglulu 86*91f16700Schasinglulu /* CPU initialisation after wakeup from suspend */ 87*91f16700Schasinglulu static void *sdei_cpu_wakeup_init(const void *arg) 88*91f16700Schasinglulu { 89*91f16700Schasinglulu SDEI_LOG("Events masked on %lx\n", read_mpidr_el1()); 90*91f16700Schasinglulu 91*91f16700Schasinglulu /* All PEs wake up with SDEI events masked */ 92*91f16700Schasinglulu sdei_pe_mask(); 93*91f16700Schasinglulu 94*91f16700Schasinglulu return 0; 95*91f16700Schasinglulu } 96*91f16700Schasinglulu 97*91f16700Schasinglulu /* Initialise an SDEI class */ 98*91f16700Schasinglulu static void sdei_class_init(sdei_class_t class) 99*91f16700Schasinglulu { 100*91f16700Schasinglulu unsigned int i; 101*91f16700Schasinglulu bool zero_found __unused = false; 102*91f16700Schasinglulu int ev_num_so_far __unused; 103*91f16700Schasinglulu sdei_ev_map_t *map; 104*91f16700Schasinglulu 105*91f16700Schasinglulu /* Sanity check and configuration of shared events */ 106*91f16700Schasinglulu ev_num_so_far = -1; 107*91f16700Schasinglulu for_each_shared_map(i, map) { 108*91f16700Schasinglulu #if ENABLE_ASSERTIONS 109*91f16700Schasinglulu /* Ensure mappings are sorted */ 110*91f16700Schasinglulu assert((ev_num_so_far < 0) || (map->ev_num > ev_num_so_far)); 111*91f16700Schasinglulu 112*91f16700Schasinglulu ev_num_so_far = map->ev_num; 113*91f16700Schasinglulu 114*91f16700Schasinglulu /* Event 0 must not be shared */ 115*91f16700Schasinglulu assert(map->ev_num != SDEI_EVENT_0); 116*91f16700Schasinglulu 117*91f16700Schasinglulu /* Check for valid event */ 118*91f16700Schasinglulu assert(map->ev_num >= 0); 119*91f16700Schasinglulu 120*91f16700Schasinglulu /* Make sure it's a shared event */ 121*91f16700Schasinglulu assert(is_event_shared(map)); 122*91f16700Schasinglulu 123*91f16700Schasinglulu /* No shared mapping should have signalable property */ 124*91f16700Schasinglulu assert(!is_event_signalable(map)); 125*91f16700Schasinglulu 126*91f16700Schasinglulu /* Shared mappings can't be explicit */ 127*91f16700Schasinglulu assert(!is_map_explicit(map)); 128*91f16700Schasinglulu #endif 129*91f16700Schasinglulu 130*91f16700Schasinglulu /* Skip initializing the wrong priority */ 131*91f16700Schasinglulu if (map_to_class(map) != class) 132*91f16700Schasinglulu continue; 133*91f16700Schasinglulu 134*91f16700Schasinglulu /* Platform events are always bound, so set the bound flag */ 135*91f16700Schasinglulu if (is_map_dynamic(map)) { 136*91f16700Schasinglulu assert(map->intr == SDEI_DYN_IRQ); 137*91f16700Schasinglulu assert(is_event_normal(map)); 138*91f16700Schasinglulu num_dyn_shrd_slots++; 139*91f16700Schasinglulu } else { 140*91f16700Schasinglulu /* Shared mappings must be bound to shared interrupt */ 141*91f16700Schasinglulu assert(plat_ic_is_spi(map->intr) != 0); 142*91f16700Schasinglulu set_map_bound(map); 143*91f16700Schasinglulu } 144*91f16700Schasinglulu 145*91f16700Schasinglulu init_map(map); 146*91f16700Schasinglulu } 147*91f16700Schasinglulu 148*91f16700Schasinglulu /* Sanity check and configuration of private events for this CPU */ 149*91f16700Schasinglulu ev_num_so_far = -1; 150*91f16700Schasinglulu for_each_private_map(i, map) { 151*91f16700Schasinglulu #if ENABLE_ASSERTIONS 152*91f16700Schasinglulu /* Ensure mappings are sorted */ 153*91f16700Schasinglulu assert((ev_num_so_far < 0) || (map->ev_num > ev_num_so_far)); 154*91f16700Schasinglulu 155*91f16700Schasinglulu ev_num_so_far = map->ev_num; 156*91f16700Schasinglulu 157*91f16700Schasinglulu if (map->ev_num == SDEI_EVENT_0) { 158*91f16700Schasinglulu zero_found = true; 159*91f16700Schasinglulu 160*91f16700Schasinglulu /* Event 0 must be a Secure SGI */ 161*91f16700Schasinglulu assert(is_secure_sgi(map->intr)); 162*91f16700Schasinglulu 163*91f16700Schasinglulu /* 164*91f16700Schasinglulu * Event 0 can have only have signalable flag (apart 165*91f16700Schasinglulu * from being private 166*91f16700Schasinglulu */ 167*91f16700Schasinglulu assert(map->map_flags == (SDEI_MAPF_SIGNALABLE | 168*91f16700Schasinglulu SDEI_MAPF_PRIVATE)); 169*91f16700Schasinglulu } else { 170*91f16700Schasinglulu /* No other mapping should have signalable property */ 171*91f16700Schasinglulu assert(!is_event_signalable(map)); 172*91f16700Schasinglulu } 173*91f16700Schasinglulu 174*91f16700Schasinglulu /* Check for valid event */ 175*91f16700Schasinglulu assert(map->ev_num >= 0); 176*91f16700Schasinglulu 177*91f16700Schasinglulu /* Make sure it's a private event */ 178*91f16700Schasinglulu assert(is_event_private(map)); 179*91f16700Schasinglulu 180*91f16700Schasinglulu /* 181*91f16700Schasinglulu * Other than priority, explicit events can only have explicit 182*91f16700Schasinglulu * and private flags set. 183*91f16700Schasinglulu */ 184*91f16700Schasinglulu if (is_map_explicit(map)) { 185*91f16700Schasinglulu assert((map->map_flags | SDEI_MAPF_CRITICAL) == 186*91f16700Schasinglulu (SDEI_MAPF_EXPLICIT | SDEI_MAPF_PRIVATE 187*91f16700Schasinglulu | SDEI_MAPF_CRITICAL)); 188*91f16700Schasinglulu } 189*91f16700Schasinglulu #endif 190*91f16700Schasinglulu 191*91f16700Schasinglulu /* Skip initializing the wrong priority */ 192*91f16700Schasinglulu if (map_to_class(map) != class) 193*91f16700Schasinglulu continue; 194*91f16700Schasinglulu 195*91f16700Schasinglulu /* Platform events are always bound, so set the bound flag */ 196*91f16700Schasinglulu if (map->ev_num != SDEI_EVENT_0) { 197*91f16700Schasinglulu if (is_map_dynamic(map)) { 198*91f16700Schasinglulu assert(map->intr == SDEI_DYN_IRQ); 199*91f16700Schasinglulu assert(is_event_normal(map)); 200*91f16700Schasinglulu num_dyn_priv_slots++; 201*91f16700Schasinglulu } else if (is_map_explicit(map)) { 202*91f16700Schasinglulu /* 203*91f16700Schasinglulu * Explicit mappings don't have a backing 204*91f16700Schasinglulu * SDEI interrupt, but verify that anyway. 205*91f16700Schasinglulu */ 206*91f16700Schasinglulu assert(map->intr == SDEI_DYN_IRQ); 207*91f16700Schasinglulu } else { 208*91f16700Schasinglulu /* 209*91f16700Schasinglulu * Private mappings must be bound to private 210*91f16700Schasinglulu * interrupt. 211*91f16700Schasinglulu */ 212*91f16700Schasinglulu assert(plat_ic_is_ppi((unsigned) map->intr) != 0); 213*91f16700Schasinglulu set_map_bound(map); 214*91f16700Schasinglulu } 215*91f16700Schasinglulu } 216*91f16700Schasinglulu 217*91f16700Schasinglulu init_map(map); 218*91f16700Schasinglulu } 219*91f16700Schasinglulu 220*91f16700Schasinglulu /* Ensure event 0 is in the mapping */ 221*91f16700Schasinglulu assert(zero_found); 222*91f16700Schasinglulu 223*91f16700Schasinglulu (void) sdei_cpu_on_init(NULL); 224*91f16700Schasinglulu } 225*91f16700Schasinglulu 226*91f16700Schasinglulu /* SDEI dispatcher initialisation */ 227*91f16700Schasinglulu void sdei_init(void) 228*91f16700Schasinglulu { 229*91f16700Schasinglulu plat_sdei_setup(); 230*91f16700Schasinglulu sdei_class_init(SDEI_CRITICAL); 231*91f16700Schasinglulu sdei_class_init(SDEI_NORMAL); 232*91f16700Schasinglulu 233*91f16700Schasinglulu /* Register priority level handlers */ 234*91f16700Schasinglulu ehf_register_priority_handler(PLAT_SDEI_CRITICAL_PRI, 235*91f16700Schasinglulu sdei_intr_handler); 236*91f16700Schasinglulu ehf_register_priority_handler(PLAT_SDEI_NORMAL_PRI, 237*91f16700Schasinglulu sdei_intr_handler); 238*91f16700Schasinglulu } 239*91f16700Schasinglulu 240*91f16700Schasinglulu /* Populate SDEI event entry */ 241*91f16700Schasinglulu static void set_sdei_entry(sdei_entry_t *se, uint64_t ep, uint64_t arg, 242*91f16700Schasinglulu unsigned int flags, uint64_t affinity) 243*91f16700Schasinglulu { 244*91f16700Schasinglulu assert(se != NULL); 245*91f16700Schasinglulu 246*91f16700Schasinglulu se->ep = ep; 247*91f16700Schasinglulu se->arg = arg; 248*91f16700Schasinglulu se->affinity = (affinity & MPIDR_AFFINITY_MASK); 249*91f16700Schasinglulu se->reg_flags = flags; 250*91f16700Schasinglulu } 251*91f16700Schasinglulu 252*91f16700Schasinglulu static uint64_t sdei_version(void) 253*91f16700Schasinglulu { 254*91f16700Schasinglulu return MAKE_SDEI_VERSION(MAJOR_VERSION, MINOR_VERSION, VENDOR_VERSION); 255*91f16700Schasinglulu } 256*91f16700Schasinglulu 257*91f16700Schasinglulu /* Validate flags and MPIDR values for REGISTER and ROUTING_SET calls */ 258*91f16700Schasinglulu static int validate_flags(uint64_t flags, uint64_t mpidr) 259*91f16700Schasinglulu { 260*91f16700Schasinglulu /* Validate flags */ 261*91f16700Schasinglulu switch (flags) { 262*91f16700Schasinglulu case SDEI_REGF_RM_PE: 263*91f16700Schasinglulu if (!is_valid_mpidr(mpidr)) 264*91f16700Schasinglulu return SDEI_EINVAL; 265*91f16700Schasinglulu break; 266*91f16700Schasinglulu case SDEI_REGF_RM_ANY: 267*91f16700Schasinglulu break; 268*91f16700Schasinglulu default: 269*91f16700Schasinglulu /* Unknown flags */ 270*91f16700Schasinglulu return SDEI_EINVAL; 271*91f16700Schasinglulu } 272*91f16700Schasinglulu 273*91f16700Schasinglulu return 0; 274*91f16700Schasinglulu } 275*91f16700Schasinglulu 276*91f16700Schasinglulu /* Set routing of an SDEI event */ 277*91f16700Schasinglulu static int sdei_event_routing_set(int ev_num, uint64_t flags, uint64_t mpidr) 278*91f16700Schasinglulu { 279*91f16700Schasinglulu int ret; 280*91f16700Schasinglulu unsigned int routing; 281*91f16700Schasinglulu sdei_ev_map_t *map; 282*91f16700Schasinglulu sdei_entry_t *se; 283*91f16700Schasinglulu 284*91f16700Schasinglulu ret = validate_flags(flags, mpidr); 285*91f16700Schasinglulu if (ret != 0) 286*91f16700Schasinglulu return ret; 287*91f16700Schasinglulu 288*91f16700Schasinglulu /* Check if valid event number */ 289*91f16700Schasinglulu map = find_event_map(ev_num); 290*91f16700Schasinglulu if (map == NULL) 291*91f16700Schasinglulu return SDEI_EINVAL; 292*91f16700Schasinglulu 293*91f16700Schasinglulu /* The event must not be private */ 294*91f16700Schasinglulu if (is_event_private(map)) 295*91f16700Schasinglulu return SDEI_EINVAL; 296*91f16700Schasinglulu 297*91f16700Schasinglulu se = get_event_entry(map); 298*91f16700Schasinglulu 299*91f16700Schasinglulu sdei_map_lock(map); 300*91f16700Schasinglulu 301*91f16700Schasinglulu if (!is_map_bound(map) || is_event_private(map)) { 302*91f16700Schasinglulu ret = SDEI_EINVAL; 303*91f16700Schasinglulu goto finish; 304*91f16700Schasinglulu } 305*91f16700Schasinglulu 306*91f16700Schasinglulu if (!can_sdei_state_trans(se, DO_ROUTING)) { 307*91f16700Schasinglulu ret = SDEI_EDENY; 308*91f16700Schasinglulu goto finish; 309*91f16700Schasinglulu } 310*91f16700Schasinglulu 311*91f16700Schasinglulu /* Choose appropriate routing */ 312*91f16700Schasinglulu routing = (unsigned int) ((flags == SDEI_REGF_RM_ANY) ? 313*91f16700Schasinglulu INTR_ROUTING_MODE_ANY : INTR_ROUTING_MODE_PE); 314*91f16700Schasinglulu 315*91f16700Schasinglulu /* Update event registration flag */ 316*91f16700Schasinglulu se->reg_flags = (unsigned int) flags; 317*91f16700Schasinglulu if (flags == SDEI_REGF_RM_PE) { 318*91f16700Schasinglulu se->affinity = (mpidr & MPIDR_AFFINITY_MASK); 319*91f16700Schasinglulu } 320*91f16700Schasinglulu 321*91f16700Schasinglulu /* 322*91f16700Schasinglulu * ROUTING_SET is permissible only when event composite state is 323*91f16700Schasinglulu * 'registered, disabled, and not running'. This means that the 324*91f16700Schasinglulu * interrupt is currently disabled, and not active. 325*91f16700Schasinglulu */ 326*91f16700Schasinglulu plat_ic_set_spi_routing(map->intr, routing, (u_register_t) mpidr); 327*91f16700Schasinglulu 328*91f16700Schasinglulu finish: 329*91f16700Schasinglulu sdei_map_unlock(map); 330*91f16700Schasinglulu 331*91f16700Schasinglulu return ret; 332*91f16700Schasinglulu } 333*91f16700Schasinglulu 334*91f16700Schasinglulu /* Register handler and argument for an SDEI event */ 335*91f16700Schasinglulu static int64_t sdei_event_register(int ev_num, 336*91f16700Schasinglulu uint64_t ep, 337*91f16700Schasinglulu uint64_t arg, 338*91f16700Schasinglulu uint64_t flags, 339*91f16700Schasinglulu uint64_t mpidr) 340*91f16700Schasinglulu { 341*91f16700Schasinglulu int ret; 342*91f16700Schasinglulu unsigned int routing; 343*91f16700Schasinglulu sdei_entry_t *se; 344*91f16700Schasinglulu sdei_ev_map_t *map; 345*91f16700Schasinglulu sdei_state_t backup_state; 346*91f16700Schasinglulu 347*91f16700Schasinglulu if ((ep == 0U) || (plat_sdei_validate_entry_point( 348*91f16700Schasinglulu ep, sdei_client_el()) != 0)) { 349*91f16700Schasinglulu return SDEI_EINVAL; 350*91f16700Schasinglulu } 351*91f16700Schasinglulu 352*91f16700Schasinglulu ret = validate_flags(flags, mpidr); 353*91f16700Schasinglulu if (ret != 0) 354*91f16700Schasinglulu return ret; 355*91f16700Schasinglulu 356*91f16700Schasinglulu /* Check if valid event number */ 357*91f16700Schasinglulu map = find_event_map(ev_num); 358*91f16700Schasinglulu if (map == NULL) 359*91f16700Schasinglulu return SDEI_EINVAL; 360*91f16700Schasinglulu 361*91f16700Schasinglulu /* Private events always target the PE */ 362*91f16700Schasinglulu if (is_event_private(map)) { 363*91f16700Schasinglulu /* 364*91f16700Schasinglulu * SDEI internally handles private events in the same manner 365*91f16700Schasinglulu * as public events with routing mode=RM_PE, since the routing 366*91f16700Schasinglulu * mode flag and affinity fields are not used when registering 367*91f16700Schasinglulu * a private event, set them here. 368*91f16700Schasinglulu */ 369*91f16700Schasinglulu flags = SDEI_REGF_RM_PE; 370*91f16700Schasinglulu /* 371*91f16700Schasinglulu * Kernel may pass 0 as mpidr, as we set flags to 372*91f16700Schasinglulu * SDEI_REGF_RM_PE, so set mpidr also. 373*91f16700Schasinglulu */ 374*91f16700Schasinglulu mpidr = read_mpidr_el1(); 375*91f16700Schasinglulu } 376*91f16700Schasinglulu 377*91f16700Schasinglulu se = get_event_entry(map); 378*91f16700Schasinglulu 379*91f16700Schasinglulu /* 380*91f16700Schasinglulu * Even though register operation is per-event (additionally for private 381*91f16700Schasinglulu * events, registration is required individually), it has to be 382*91f16700Schasinglulu * serialised with respect to bind/release, which are global operations. 383*91f16700Schasinglulu * So we hold the lock throughout, unconditionally. 384*91f16700Schasinglulu */ 385*91f16700Schasinglulu sdei_map_lock(map); 386*91f16700Schasinglulu 387*91f16700Schasinglulu backup_state = se->state; 388*91f16700Schasinglulu if (!can_sdei_state_trans(se, DO_REGISTER)) 389*91f16700Schasinglulu goto fallback; 390*91f16700Schasinglulu 391*91f16700Schasinglulu /* 392*91f16700Schasinglulu * When registering for dynamic events, make sure it's been bound 393*91f16700Schasinglulu * already. This has to be the case as, without binding, the client 394*91f16700Schasinglulu * can't know about the event number to register for. 395*91f16700Schasinglulu */ 396*91f16700Schasinglulu if (is_map_dynamic(map) && !is_map_bound(map)) 397*91f16700Schasinglulu goto fallback; 398*91f16700Schasinglulu 399*91f16700Schasinglulu if (is_event_private(map)) { 400*91f16700Schasinglulu /* Multiple calls to register are possible for private events */ 401*91f16700Schasinglulu assert(map->reg_count >= 0); 402*91f16700Schasinglulu } else { 403*91f16700Schasinglulu /* Only single call to register is possible for shared events */ 404*91f16700Schasinglulu assert(map->reg_count == 0); 405*91f16700Schasinglulu } 406*91f16700Schasinglulu 407*91f16700Schasinglulu if (is_map_bound(map)) { 408*91f16700Schasinglulu /* Meanwhile, did any PE ACK the interrupt? */ 409*91f16700Schasinglulu if (plat_ic_get_interrupt_active(map->intr) != 0U) 410*91f16700Schasinglulu goto fallback; 411*91f16700Schasinglulu 412*91f16700Schasinglulu /* The interrupt must currently owned by Non-secure */ 413*91f16700Schasinglulu if (plat_ic_get_interrupt_type(map->intr) != INTR_TYPE_NS) 414*91f16700Schasinglulu goto fallback; 415*91f16700Schasinglulu 416*91f16700Schasinglulu /* 417*91f16700Schasinglulu * Disable forwarding of new interrupt triggers to CPU 418*91f16700Schasinglulu * interface. 419*91f16700Schasinglulu */ 420*91f16700Schasinglulu plat_ic_disable_interrupt(map->intr); 421*91f16700Schasinglulu 422*91f16700Schasinglulu /* 423*91f16700Schasinglulu * Any events that are triggered after register and before 424*91f16700Schasinglulu * enable should remain pending. Clear any previous interrupt 425*91f16700Schasinglulu * triggers which are pending (except for SGIs). This has no 426*91f16700Schasinglulu * affect on level-triggered interrupts. 427*91f16700Schasinglulu */ 428*91f16700Schasinglulu if (ev_num != SDEI_EVENT_0) 429*91f16700Schasinglulu plat_ic_clear_interrupt_pending(map->intr); 430*91f16700Schasinglulu 431*91f16700Schasinglulu /* Map interrupt to EL3 and program the correct priority */ 432*91f16700Schasinglulu plat_ic_set_interrupt_type(map->intr, INTR_TYPE_EL3); 433*91f16700Schasinglulu 434*91f16700Schasinglulu /* Program the appropriate interrupt priority */ 435*91f16700Schasinglulu plat_ic_set_interrupt_priority(map->intr, sdei_event_priority(map)); 436*91f16700Schasinglulu 437*91f16700Schasinglulu /* 438*91f16700Schasinglulu * Set the routing mode for shared event as requested. We 439*91f16700Schasinglulu * already ensure that shared events get bound to SPIs. 440*91f16700Schasinglulu */ 441*91f16700Schasinglulu if (is_event_shared(map)) { 442*91f16700Schasinglulu routing = (unsigned int) ((flags == SDEI_REGF_RM_ANY) ? 443*91f16700Schasinglulu INTR_ROUTING_MODE_ANY : INTR_ROUTING_MODE_PE); 444*91f16700Schasinglulu plat_ic_set_spi_routing(map->intr, routing, 445*91f16700Schasinglulu (u_register_t) mpidr); 446*91f16700Schasinglulu } 447*91f16700Schasinglulu } 448*91f16700Schasinglulu 449*91f16700Schasinglulu /* Populate event entries */ 450*91f16700Schasinglulu set_sdei_entry(se, ep, arg, (unsigned int) flags, mpidr); 451*91f16700Schasinglulu 452*91f16700Schasinglulu /* Increment register count */ 453*91f16700Schasinglulu map->reg_count++; 454*91f16700Schasinglulu 455*91f16700Schasinglulu sdei_map_unlock(map); 456*91f16700Schasinglulu 457*91f16700Schasinglulu return 0; 458*91f16700Schasinglulu 459*91f16700Schasinglulu fallback: 460*91f16700Schasinglulu /* Reinstate previous state */ 461*91f16700Schasinglulu se->state = backup_state; 462*91f16700Schasinglulu 463*91f16700Schasinglulu sdei_map_unlock(map); 464*91f16700Schasinglulu 465*91f16700Schasinglulu return SDEI_EDENY; 466*91f16700Schasinglulu } 467*91f16700Schasinglulu 468*91f16700Schasinglulu /* Enable SDEI event */ 469*91f16700Schasinglulu static int64_t sdei_event_enable(int ev_num) 470*91f16700Schasinglulu { 471*91f16700Schasinglulu sdei_ev_map_t *map; 472*91f16700Schasinglulu sdei_entry_t *se; 473*91f16700Schasinglulu int ret; 474*91f16700Schasinglulu bool before, after; 475*91f16700Schasinglulu 476*91f16700Schasinglulu /* Check if valid event number */ 477*91f16700Schasinglulu map = find_event_map(ev_num); 478*91f16700Schasinglulu if (map == NULL) 479*91f16700Schasinglulu return SDEI_EINVAL; 480*91f16700Schasinglulu 481*91f16700Schasinglulu se = get_event_entry(map); 482*91f16700Schasinglulu ret = SDEI_EDENY; 483*91f16700Schasinglulu 484*91f16700Schasinglulu if (is_event_shared(map)) 485*91f16700Schasinglulu sdei_map_lock(map); 486*91f16700Schasinglulu 487*91f16700Schasinglulu before = GET_EV_STATE(se, ENABLED); 488*91f16700Schasinglulu if (!can_sdei_state_trans(se, DO_ENABLE)) 489*91f16700Schasinglulu goto finish; 490*91f16700Schasinglulu after = GET_EV_STATE(se, ENABLED); 491*91f16700Schasinglulu 492*91f16700Schasinglulu /* 493*91f16700Schasinglulu * Enable interrupt for bound events only if there's a change in enabled 494*91f16700Schasinglulu * state. 495*91f16700Schasinglulu */ 496*91f16700Schasinglulu if (is_map_bound(map) && (!before && after)) 497*91f16700Schasinglulu plat_ic_enable_interrupt(map->intr); 498*91f16700Schasinglulu 499*91f16700Schasinglulu ret = 0; 500*91f16700Schasinglulu 501*91f16700Schasinglulu finish: 502*91f16700Schasinglulu if (is_event_shared(map)) 503*91f16700Schasinglulu sdei_map_unlock(map); 504*91f16700Schasinglulu 505*91f16700Schasinglulu return ret; 506*91f16700Schasinglulu } 507*91f16700Schasinglulu 508*91f16700Schasinglulu /* Disable SDEI event */ 509*91f16700Schasinglulu static int sdei_event_disable(int ev_num) 510*91f16700Schasinglulu { 511*91f16700Schasinglulu sdei_ev_map_t *map; 512*91f16700Schasinglulu sdei_entry_t *se; 513*91f16700Schasinglulu int ret; 514*91f16700Schasinglulu bool before, after; 515*91f16700Schasinglulu 516*91f16700Schasinglulu /* Check if valid event number */ 517*91f16700Schasinglulu map = find_event_map(ev_num); 518*91f16700Schasinglulu if (map == NULL) 519*91f16700Schasinglulu return SDEI_EINVAL; 520*91f16700Schasinglulu 521*91f16700Schasinglulu se = get_event_entry(map); 522*91f16700Schasinglulu ret = SDEI_EDENY; 523*91f16700Schasinglulu 524*91f16700Schasinglulu if (is_event_shared(map)) 525*91f16700Schasinglulu sdei_map_lock(map); 526*91f16700Schasinglulu 527*91f16700Schasinglulu before = GET_EV_STATE(se, ENABLED); 528*91f16700Schasinglulu if (!can_sdei_state_trans(se, DO_DISABLE)) 529*91f16700Schasinglulu goto finish; 530*91f16700Schasinglulu after = GET_EV_STATE(se, ENABLED); 531*91f16700Schasinglulu 532*91f16700Schasinglulu /* 533*91f16700Schasinglulu * Disable interrupt for bound events only if there's a change in 534*91f16700Schasinglulu * enabled state. 535*91f16700Schasinglulu */ 536*91f16700Schasinglulu if (is_map_bound(map) && (before && !after)) 537*91f16700Schasinglulu plat_ic_disable_interrupt(map->intr); 538*91f16700Schasinglulu 539*91f16700Schasinglulu ret = 0; 540*91f16700Schasinglulu 541*91f16700Schasinglulu finish: 542*91f16700Schasinglulu if (is_event_shared(map)) 543*91f16700Schasinglulu sdei_map_unlock(map); 544*91f16700Schasinglulu 545*91f16700Schasinglulu return ret; 546*91f16700Schasinglulu } 547*91f16700Schasinglulu 548*91f16700Schasinglulu /* Query SDEI event information */ 549*91f16700Schasinglulu static int64_t sdei_event_get_info(int ev_num, int info) 550*91f16700Schasinglulu { 551*91f16700Schasinglulu sdei_entry_t *se; 552*91f16700Schasinglulu sdei_ev_map_t *map; 553*91f16700Schasinglulu 554*91f16700Schasinglulu uint64_t flags; 555*91f16700Schasinglulu bool registered; 556*91f16700Schasinglulu uint64_t affinity; 557*91f16700Schasinglulu 558*91f16700Schasinglulu /* Check if valid event number */ 559*91f16700Schasinglulu map = find_event_map(ev_num); 560*91f16700Schasinglulu if (map == NULL) 561*91f16700Schasinglulu return SDEI_EINVAL; 562*91f16700Schasinglulu 563*91f16700Schasinglulu se = get_event_entry(map); 564*91f16700Schasinglulu 565*91f16700Schasinglulu if (is_event_shared(map)) 566*91f16700Schasinglulu sdei_map_lock(map); 567*91f16700Schasinglulu 568*91f16700Schasinglulu /* Sample state under lock */ 569*91f16700Schasinglulu registered = GET_EV_STATE(se, REGISTERED); 570*91f16700Schasinglulu flags = se->reg_flags; 571*91f16700Schasinglulu affinity = se->affinity; 572*91f16700Schasinglulu 573*91f16700Schasinglulu if (is_event_shared(map)) 574*91f16700Schasinglulu sdei_map_unlock(map); 575*91f16700Schasinglulu 576*91f16700Schasinglulu switch (info) { 577*91f16700Schasinglulu case SDEI_INFO_EV_TYPE: 578*91f16700Schasinglulu return is_event_shared(map); 579*91f16700Schasinglulu 580*91f16700Schasinglulu case SDEI_INFO_EV_NOT_SIGNALED: 581*91f16700Schasinglulu return !is_event_signalable(map); 582*91f16700Schasinglulu 583*91f16700Schasinglulu case SDEI_INFO_EV_PRIORITY: 584*91f16700Schasinglulu return is_event_critical(map); 585*91f16700Schasinglulu 586*91f16700Schasinglulu case SDEI_INFO_EV_ROUTING_MODE: 587*91f16700Schasinglulu if (!is_event_shared(map)) 588*91f16700Schasinglulu return SDEI_EINVAL; 589*91f16700Schasinglulu if (!registered) 590*91f16700Schasinglulu return SDEI_EDENY; 591*91f16700Schasinglulu return (flags == SDEI_REGF_RM_PE); 592*91f16700Schasinglulu 593*91f16700Schasinglulu case SDEI_INFO_EV_ROUTING_AFF: 594*91f16700Schasinglulu if (!is_event_shared(map)) 595*91f16700Schasinglulu return SDEI_EINVAL; 596*91f16700Schasinglulu if (!registered) 597*91f16700Schasinglulu return SDEI_EDENY; 598*91f16700Schasinglulu if (flags != SDEI_REGF_RM_PE) 599*91f16700Schasinglulu return SDEI_EINVAL; 600*91f16700Schasinglulu return affinity; 601*91f16700Schasinglulu 602*91f16700Schasinglulu default: 603*91f16700Schasinglulu return SDEI_EINVAL; 604*91f16700Schasinglulu } 605*91f16700Schasinglulu } 606*91f16700Schasinglulu 607*91f16700Schasinglulu /* Unregister an SDEI event */ 608*91f16700Schasinglulu static int sdei_event_unregister(int ev_num) 609*91f16700Schasinglulu { 610*91f16700Schasinglulu int ret = 0; 611*91f16700Schasinglulu sdei_entry_t *se; 612*91f16700Schasinglulu sdei_ev_map_t *map; 613*91f16700Schasinglulu 614*91f16700Schasinglulu /* Check if valid event number */ 615*91f16700Schasinglulu map = find_event_map(ev_num); 616*91f16700Schasinglulu if (map == NULL) 617*91f16700Schasinglulu return SDEI_EINVAL; 618*91f16700Schasinglulu 619*91f16700Schasinglulu se = get_event_entry(map); 620*91f16700Schasinglulu 621*91f16700Schasinglulu /* 622*91f16700Schasinglulu * Even though unregister operation is per-event (additionally for 623*91f16700Schasinglulu * private events, unregistration is required individually), it has to 624*91f16700Schasinglulu * be serialised with respect to bind/release, which are global 625*91f16700Schasinglulu * operations. So we hold the lock throughout, unconditionally. 626*91f16700Schasinglulu */ 627*91f16700Schasinglulu sdei_map_lock(map); 628*91f16700Schasinglulu 629*91f16700Schasinglulu if (!can_sdei_state_trans(se, DO_UNREGISTER)) { 630*91f16700Schasinglulu /* 631*91f16700Schasinglulu * Even if the call is invalid, and the handler is running (for 632*91f16700Schasinglulu * example, having unregistered from a running handler earlier), 633*91f16700Schasinglulu * return pending error code; otherwise, return deny. 634*91f16700Schasinglulu */ 635*91f16700Schasinglulu ret = GET_EV_STATE(se, RUNNING) ? SDEI_EPEND : SDEI_EDENY; 636*91f16700Schasinglulu 637*91f16700Schasinglulu goto finish; 638*91f16700Schasinglulu } 639*91f16700Schasinglulu 640*91f16700Schasinglulu map->reg_count--; 641*91f16700Schasinglulu if (is_event_private(map)) { 642*91f16700Schasinglulu /* Multiple calls to register are possible for private events */ 643*91f16700Schasinglulu assert(map->reg_count >= 0); 644*91f16700Schasinglulu } else { 645*91f16700Schasinglulu /* Only single call to register is possible for shared events */ 646*91f16700Schasinglulu assert(map->reg_count == 0); 647*91f16700Schasinglulu } 648*91f16700Schasinglulu 649*91f16700Schasinglulu if (is_map_bound(map)) { 650*91f16700Schasinglulu plat_ic_disable_interrupt(map->intr); 651*91f16700Schasinglulu 652*91f16700Schasinglulu /* 653*91f16700Schasinglulu * Clear pending interrupt. Skip for SGIs as they may not be 654*91f16700Schasinglulu * cleared on interrupt controllers. 655*91f16700Schasinglulu */ 656*91f16700Schasinglulu if (ev_num != SDEI_EVENT_0) 657*91f16700Schasinglulu plat_ic_clear_interrupt_pending(map->intr); 658*91f16700Schasinglulu 659*91f16700Schasinglulu assert(plat_ic_get_interrupt_type(map->intr) == INTR_TYPE_EL3); 660*91f16700Schasinglulu plat_ic_set_interrupt_type(map->intr, INTR_TYPE_NS); 661*91f16700Schasinglulu plat_ic_set_interrupt_priority(map->intr, LOWEST_INTR_PRIORITY); 662*91f16700Schasinglulu } 663*91f16700Schasinglulu 664*91f16700Schasinglulu clear_event_entries(se); 665*91f16700Schasinglulu 666*91f16700Schasinglulu /* 667*91f16700Schasinglulu * If the handler is running at the time of unregister, return the 668*91f16700Schasinglulu * pending error code. 669*91f16700Schasinglulu */ 670*91f16700Schasinglulu if (GET_EV_STATE(se, RUNNING)) 671*91f16700Schasinglulu ret = SDEI_EPEND; 672*91f16700Schasinglulu 673*91f16700Schasinglulu finish: 674*91f16700Schasinglulu sdei_map_unlock(map); 675*91f16700Schasinglulu 676*91f16700Schasinglulu return ret; 677*91f16700Schasinglulu } 678*91f16700Schasinglulu 679*91f16700Schasinglulu /* Query status of an SDEI event */ 680*91f16700Schasinglulu static int sdei_event_status(int ev_num) 681*91f16700Schasinglulu { 682*91f16700Schasinglulu sdei_ev_map_t *map; 683*91f16700Schasinglulu sdei_entry_t *se; 684*91f16700Schasinglulu sdei_state_t state; 685*91f16700Schasinglulu 686*91f16700Schasinglulu /* Check if valid event number */ 687*91f16700Schasinglulu map = find_event_map(ev_num); 688*91f16700Schasinglulu if (map == NULL) 689*91f16700Schasinglulu return SDEI_EINVAL; 690*91f16700Schasinglulu 691*91f16700Schasinglulu se = get_event_entry(map); 692*91f16700Schasinglulu 693*91f16700Schasinglulu if (is_event_shared(map)) 694*91f16700Schasinglulu sdei_map_lock(map); 695*91f16700Schasinglulu 696*91f16700Schasinglulu /* State value directly maps to the expected return format */ 697*91f16700Schasinglulu state = se->state; 698*91f16700Schasinglulu 699*91f16700Schasinglulu if (is_event_shared(map)) 700*91f16700Schasinglulu sdei_map_unlock(map); 701*91f16700Schasinglulu 702*91f16700Schasinglulu return (int) state; 703*91f16700Schasinglulu } 704*91f16700Schasinglulu 705*91f16700Schasinglulu /* Bind an SDEI event to an interrupt */ 706*91f16700Schasinglulu static int sdei_interrupt_bind(unsigned int intr_num) 707*91f16700Schasinglulu { 708*91f16700Schasinglulu sdei_ev_map_t *map; 709*91f16700Schasinglulu bool retry = true, shared_mapping; 710*91f16700Schasinglulu 711*91f16700Schasinglulu /* Interrupt must be either PPI or SPI */ 712*91f16700Schasinglulu if (!(plat_ic_is_ppi(intr_num) || plat_ic_is_spi(intr_num))) 713*91f16700Schasinglulu return SDEI_EINVAL; 714*91f16700Schasinglulu 715*91f16700Schasinglulu shared_mapping = (plat_ic_is_spi(intr_num) != 0); 716*91f16700Schasinglulu do { 717*91f16700Schasinglulu /* 718*91f16700Schasinglulu * Bail out if there is already an event for this interrupt, 719*91f16700Schasinglulu * either platform-defined or dynamic. 720*91f16700Schasinglulu */ 721*91f16700Schasinglulu map = find_event_map_by_intr(intr_num, shared_mapping); 722*91f16700Schasinglulu if (map != NULL) { 723*91f16700Schasinglulu if (is_map_dynamic(map)) { 724*91f16700Schasinglulu if (is_map_bound(map)) { 725*91f16700Schasinglulu /* 726*91f16700Schasinglulu * Dynamic event, already bound. Return 727*91f16700Schasinglulu * event number. 728*91f16700Schasinglulu */ 729*91f16700Schasinglulu return map->ev_num; 730*91f16700Schasinglulu } 731*91f16700Schasinglulu } else { 732*91f16700Schasinglulu /* Binding non-dynamic event */ 733*91f16700Schasinglulu return SDEI_EINVAL; 734*91f16700Schasinglulu } 735*91f16700Schasinglulu } 736*91f16700Schasinglulu 737*91f16700Schasinglulu /* 738*91f16700Schasinglulu * The interrupt is not bound yet. Try to find a free slot to 739*91f16700Schasinglulu * bind it. Free dynamic mappings have their interrupt set as 740*91f16700Schasinglulu * SDEI_DYN_IRQ. 741*91f16700Schasinglulu */ 742*91f16700Schasinglulu map = find_event_map_by_intr(SDEI_DYN_IRQ, shared_mapping); 743*91f16700Schasinglulu if (map == NULL) 744*91f16700Schasinglulu return SDEI_ENOMEM; 745*91f16700Schasinglulu 746*91f16700Schasinglulu /* The returned mapping must be dynamic */ 747*91f16700Schasinglulu assert(is_map_dynamic(map)); 748*91f16700Schasinglulu 749*91f16700Schasinglulu /* 750*91f16700Schasinglulu * We cannot assert for bound maps here, as we might be racing 751*91f16700Schasinglulu * with another bind. 752*91f16700Schasinglulu */ 753*91f16700Schasinglulu 754*91f16700Schasinglulu /* The requested interrupt must already belong to NS */ 755*91f16700Schasinglulu if (plat_ic_get_interrupt_type(intr_num) != INTR_TYPE_NS) 756*91f16700Schasinglulu return SDEI_EDENY; 757*91f16700Schasinglulu 758*91f16700Schasinglulu /* 759*91f16700Schasinglulu * Interrupt programming and ownership transfer are deferred 760*91f16700Schasinglulu * until register. 761*91f16700Schasinglulu */ 762*91f16700Schasinglulu 763*91f16700Schasinglulu sdei_map_lock(map); 764*91f16700Schasinglulu if (!is_map_bound(map)) { 765*91f16700Schasinglulu map->intr = intr_num; 766*91f16700Schasinglulu set_map_bound(map); 767*91f16700Schasinglulu retry = false; 768*91f16700Schasinglulu } 769*91f16700Schasinglulu sdei_map_unlock(map); 770*91f16700Schasinglulu } while (retry); 771*91f16700Schasinglulu 772*91f16700Schasinglulu return map->ev_num; 773*91f16700Schasinglulu } 774*91f16700Schasinglulu 775*91f16700Schasinglulu /* Release a bound SDEI event previously to an interrupt */ 776*91f16700Schasinglulu static int sdei_interrupt_release(int ev_num) 777*91f16700Schasinglulu { 778*91f16700Schasinglulu int ret = 0; 779*91f16700Schasinglulu sdei_ev_map_t *map; 780*91f16700Schasinglulu sdei_entry_t *se; 781*91f16700Schasinglulu 782*91f16700Schasinglulu /* Check if valid event number */ 783*91f16700Schasinglulu map = find_event_map(ev_num); 784*91f16700Schasinglulu if (map == NULL) 785*91f16700Schasinglulu return SDEI_EINVAL; 786*91f16700Schasinglulu 787*91f16700Schasinglulu if (!is_map_dynamic(map)) 788*91f16700Schasinglulu return SDEI_EINVAL; 789*91f16700Schasinglulu 790*91f16700Schasinglulu se = get_event_entry(map); 791*91f16700Schasinglulu 792*91f16700Schasinglulu sdei_map_lock(map); 793*91f16700Schasinglulu 794*91f16700Schasinglulu /* Event must have been unregistered before release */ 795*91f16700Schasinglulu if (map->reg_count != 0) { 796*91f16700Schasinglulu ret = SDEI_EDENY; 797*91f16700Schasinglulu goto finish; 798*91f16700Schasinglulu } 799*91f16700Schasinglulu 800*91f16700Schasinglulu /* 801*91f16700Schasinglulu * Interrupt release never causes the state to change. We only check 802*91f16700Schasinglulu * whether it's permissible or not. 803*91f16700Schasinglulu */ 804*91f16700Schasinglulu if (!can_sdei_state_trans(se, DO_RELEASE)) { 805*91f16700Schasinglulu ret = SDEI_EDENY; 806*91f16700Schasinglulu goto finish; 807*91f16700Schasinglulu } 808*91f16700Schasinglulu 809*91f16700Schasinglulu if (is_map_bound(map)) { 810*91f16700Schasinglulu /* 811*91f16700Schasinglulu * Deny release if the interrupt is active, which means it's 812*91f16700Schasinglulu * probably being acknowledged and handled elsewhere. 813*91f16700Schasinglulu */ 814*91f16700Schasinglulu if (plat_ic_get_interrupt_active(map->intr) != 0U) { 815*91f16700Schasinglulu ret = SDEI_EDENY; 816*91f16700Schasinglulu goto finish; 817*91f16700Schasinglulu } 818*91f16700Schasinglulu 819*91f16700Schasinglulu /* 820*91f16700Schasinglulu * Interrupt programming and ownership transfer are already done 821*91f16700Schasinglulu * during unregister. 822*91f16700Schasinglulu */ 823*91f16700Schasinglulu 824*91f16700Schasinglulu map->intr = SDEI_DYN_IRQ; 825*91f16700Schasinglulu clr_map_bound(map); 826*91f16700Schasinglulu } else { 827*91f16700Schasinglulu SDEI_LOG("Error release bound:%d cnt:%d\n", is_map_bound(map), 828*91f16700Schasinglulu map->reg_count); 829*91f16700Schasinglulu ret = SDEI_EINVAL; 830*91f16700Schasinglulu } 831*91f16700Schasinglulu 832*91f16700Schasinglulu finish: 833*91f16700Schasinglulu sdei_map_unlock(map); 834*91f16700Schasinglulu 835*91f16700Schasinglulu return ret; 836*91f16700Schasinglulu } 837*91f16700Schasinglulu 838*91f16700Schasinglulu /* Perform reset of private SDEI events */ 839*91f16700Schasinglulu static int sdei_private_reset(void) 840*91f16700Schasinglulu { 841*91f16700Schasinglulu sdei_ev_map_t *map; 842*91f16700Schasinglulu int ret = 0, final_ret = 0; 843*91f16700Schasinglulu unsigned int i; 844*91f16700Schasinglulu 845*91f16700Schasinglulu /* Unregister all private events */ 846*91f16700Schasinglulu for_each_private_map(i, map) { 847*91f16700Schasinglulu /* 848*91f16700Schasinglulu * The unregister can fail if the event is not registered, which 849*91f16700Schasinglulu * is allowed, and a deny will be returned. But if the event is 850*91f16700Schasinglulu * running or unregister pending, the call fails. 851*91f16700Schasinglulu */ 852*91f16700Schasinglulu ret = sdei_event_unregister(map->ev_num); 853*91f16700Schasinglulu if ((ret == SDEI_EPEND) && (final_ret == 0)) 854*91f16700Schasinglulu final_ret = SDEI_EDENY; 855*91f16700Schasinglulu } 856*91f16700Schasinglulu 857*91f16700Schasinglulu return final_ret; 858*91f16700Schasinglulu } 859*91f16700Schasinglulu 860*91f16700Schasinglulu /* Perform reset of shared SDEI events */ 861*91f16700Schasinglulu static int sdei_shared_reset(void) 862*91f16700Schasinglulu { 863*91f16700Schasinglulu const sdei_mapping_t *mapping; 864*91f16700Schasinglulu sdei_ev_map_t *map; 865*91f16700Schasinglulu int ret = 0, final_ret = 0; 866*91f16700Schasinglulu unsigned int i, j; 867*91f16700Schasinglulu 868*91f16700Schasinglulu /* Unregister all shared events */ 869*91f16700Schasinglulu for_each_shared_map(i, map) { 870*91f16700Schasinglulu /* 871*91f16700Schasinglulu * The unregister can fail if the event is not registered, which 872*91f16700Schasinglulu * is allowed, and a deny will be returned. But if the event is 873*91f16700Schasinglulu * running or unregister pending, the call fails. 874*91f16700Schasinglulu */ 875*91f16700Schasinglulu ret = sdei_event_unregister(map->ev_num); 876*91f16700Schasinglulu if ((ret == SDEI_EPEND) && (final_ret == 0)) 877*91f16700Schasinglulu final_ret = SDEI_EDENY; 878*91f16700Schasinglulu } 879*91f16700Schasinglulu 880*91f16700Schasinglulu if (final_ret != 0) 881*91f16700Schasinglulu return final_ret; 882*91f16700Schasinglulu 883*91f16700Schasinglulu /* 884*91f16700Schasinglulu * Loop through both private and shared mappings, and release all 885*91f16700Schasinglulu * bindings. 886*91f16700Schasinglulu */ 887*91f16700Schasinglulu for_each_mapping_type(i, mapping) { 888*91f16700Schasinglulu iterate_mapping(mapping, j, map) { 889*91f16700Schasinglulu /* 890*91f16700Schasinglulu * Release bindings for mappings that are dynamic and 891*91f16700Schasinglulu * bound. 892*91f16700Schasinglulu */ 893*91f16700Schasinglulu if (is_map_dynamic(map) && is_map_bound(map)) { 894*91f16700Schasinglulu /* 895*91f16700Schasinglulu * Any failure to release would mean there is at 896*91f16700Schasinglulu * least a PE registered for the event. 897*91f16700Schasinglulu */ 898*91f16700Schasinglulu ret = sdei_interrupt_release(map->ev_num); 899*91f16700Schasinglulu if ((ret != 0) && (final_ret == 0)) 900*91f16700Schasinglulu final_ret = ret; 901*91f16700Schasinglulu } 902*91f16700Schasinglulu } 903*91f16700Schasinglulu } 904*91f16700Schasinglulu 905*91f16700Schasinglulu return final_ret; 906*91f16700Schasinglulu } 907*91f16700Schasinglulu 908*91f16700Schasinglulu /* Send a signal to another SDEI client PE */ 909*91f16700Schasinglulu static int sdei_signal(int ev_num, uint64_t target_pe) 910*91f16700Schasinglulu { 911*91f16700Schasinglulu sdei_ev_map_t *map; 912*91f16700Schasinglulu 913*91f16700Schasinglulu /* Only event 0 can be signalled */ 914*91f16700Schasinglulu if (ev_num != SDEI_EVENT_0) 915*91f16700Schasinglulu return SDEI_EINVAL; 916*91f16700Schasinglulu 917*91f16700Schasinglulu /* Find mapping for event 0 */ 918*91f16700Schasinglulu map = find_event_map(SDEI_EVENT_0); 919*91f16700Schasinglulu if (map == NULL) 920*91f16700Schasinglulu return SDEI_EINVAL; 921*91f16700Schasinglulu 922*91f16700Schasinglulu /* The event must be signalable */ 923*91f16700Schasinglulu if (!is_event_signalable(map)) 924*91f16700Schasinglulu return SDEI_EINVAL; 925*91f16700Schasinglulu 926*91f16700Schasinglulu /* Validate target */ 927*91f16700Schasinglulu if (!is_valid_mpidr(target_pe)) 928*91f16700Schasinglulu return SDEI_EINVAL; 929*91f16700Schasinglulu 930*91f16700Schasinglulu /* Raise SGI. Platform will validate target_pe */ 931*91f16700Schasinglulu plat_ic_raise_el3_sgi((int) map->intr, (u_register_t) target_pe); 932*91f16700Schasinglulu 933*91f16700Schasinglulu return 0; 934*91f16700Schasinglulu } 935*91f16700Schasinglulu 936*91f16700Schasinglulu /* Query SDEI dispatcher features */ 937*91f16700Schasinglulu static uint64_t sdei_features(unsigned int feature) 938*91f16700Schasinglulu { 939*91f16700Schasinglulu if (feature == SDEI_FEATURE_BIND_SLOTS) { 940*91f16700Schasinglulu return FEATURE_BIND_SLOTS(num_dyn_priv_slots, 941*91f16700Schasinglulu num_dyn_shrd_slots); 942*91f16700Schasinglulu } 943*91f16700Schasinglulu 944*91f16700Schasinglulu return (uint64_t) SDEI_EINVAL; 945*91f16700Schasinglulu } 946*91f16700Schasinglulu 947*91f16700Schasinglulu /* SDEI top level handler for servicing SMCs */ 948*91f16700Schasinglulu uint64_t sdei_smc_handler(uint32_t smc_fid, 949*91f16700Schasinglulu uint64_t x1, 950*91f16700Schasinglulu uint64_t x2, 951*91f16700Schasinglulu uint64_t x3, 952*91f16700Schasinglulu uint64_t x4, 953*91f16700Schasinglulu void *cookie, 954*91f16700Schasinglulu void *handle, 955*91f16700Schasinglulu uint64_t flags) 956*91f16700Schasinglulu { 957*91f16700Schasinglulu 958*91f16700Schasinglulu uint64_t x5; 959*91f16700Schasinglulu unsigned int ss = (unsigned int) get_interrupt_src_ss(flags); 960*91f16700Schasinglulu int64_t ret; 961*91f16700Schasinglulu bool resume = false; 962*91f16700Schasinglulu cpu_context_t *ctx = handle; 963*91f16700Schasinglulu int ev_num = (int) x1; 964*91f16700Schasinglulu 965*91f16700Schasinglulu if (ss != NON_SECURE) 966*91f16700Schasinglulu SMC_RET1(ctx, SMC_UNK); 967*91f16700Schasinglulu 968*91f16700Schasinglulu /* Verify the caller EL */ 969*91f16700Schasinglulu if (GET_EL(read_spsr_el3()) != sdei_client_el()) 970*91f16700Schasinglulu SMC_RET1(ctx, SMC_UNK); 971*91f16700Schasinglulu 972*91f16700Schasinglulu switch (smc_fid) { 973*91f16700Schasinglulu case SDEI_VERSION: 974*91f16700Schasinglulu SDEI_LOG("> VER\n"); 975*91f16700Schasinglulu ret = (int64_t) sdei_version(); 976*91f16700Schasinglulu SDEI_LOG("< VER:%" PRIx64 "\n", ret); 977*91f16700Schasinglulu SMC_RET1(ctx, ret); 978*91f16700Schasinglulu 979*91f16700Schasinglulu case SDEI_EVENT_REGISTER: 980*91f16700Schasinglulu x5 = SMC_GET_GP(ctx, CTX_GPREG_X5); 981*91f16700Schasinglulu SDEI_LOG("> REG(n:%d e:%" PRIx64 " a:%" PRIx64 " f:%x m:%" PRIx64 "\n", ev_num, 982*91f16700Schasinglulu x2, x3, (int) x4, x5); 983*91f16700Schasinglulu ret = sdei_event_register(ev_num, x2, x3, x4, x5); 984*91f16700Schasinglulu SDEI_LOG("< REG:%" PRId64 "\n", ret); 985*91f16700Schasinglulu SMC_RET1(ctx, ret); 986*91f16700Schasinglulu 987*91f16700Schasinglulu case SDEI_EVENT_ENABLE: 988*91f16700Schasinglulu SDEI_LOG("> ENABLE(n:%d)\n", (int) x1); 989*91f16700Schasinglulu ret = sdei_event_enable(ev_num); 990*91f16700Schasinglulu SDEI_LOG("< ENABLE:%" PRId64 "\n", ret); 991*91f16700Schasinglulu SMC_RET1(ctx, ret); 992*91f16700Schasinglulu 993*91f16700Schasinglulu case SDEI_EVENT_DISABLE: 994*91f16700Schasinglulu SDEI_LOG("> DISABLE(n:0x%x)\n", ev_num); 995*91f16700Schasinglulu ret = sdei_event_disable(ev_num); 996*91f16700Schasinglulu SDEI_LOG("< DISABLE:%" PRId64 "\n", ret); 997*91f16700Schasinglulu SMC_RET1(ctx, ret); 998*91f16700Schasinglulu 999*91f16700Schasinglulu case SDEI_EVENT_CONTEXT: 1000*91f16700Schasinglulu SDEI_LOG("> CTX(p:%d):%lx\n", (int) x1, read_mpidr_el1()); 1001*91f16700Schasinglulu ret = sdei_event_context(ctx, (unsigned int) x1); 1002*91f16700Schasinglulu SDEI_LOG("< CTX:%" PRId64 "\n", ret); 1003*91f16700Schasinglulu SMC_RET1(ctx, ret); 1004*91f16700Schasinglulu 1005*91f16700Schasinglulu case SDEI_EVENT_COMPLETE_AND_RESUME: 1006*91f16700Schasinglulu resume = true; 1007*91f16700Schasinglulu /* Fallthrough */ 1008*91f16700Schasinglulu 1009*91f16700Schasinglulu case SDEI_EVENT_COMPLETE: 1010*91f16700Schasinglulu SDEI_LOG("> COMPLETE(r:%u sta/ep:%" PRIx64 "):%lx\n", 1011*91f16700Schasinglulu (unsigned int) resume, x1, read_mpidr_el1()); 1012*91f16700Schasinglulu ret = sdei_event_complete(resume, x1); 1013*91f16700Schasinglulu SDEI_LOG("< COMPLETE:%" PRIx64 "\n", ret); 1014*91f16700Schasinglulu 1015*91f16700Schasinglulu /* 1016*91f16700Schasinglulu * Set error code only if the call failed. If the call 1017*91f16700Schasinglulu * succeeded, we discard the dispatched context, and restore the 1018*91f16700Schasinglulu * interrupted context to a pristine condition, and therefore 1019*91f16700Schasinglulu * shouldn't be modified. We don't return to the caller in this 1020*91f16700Schasinglulu * case anyway. 1021*91f16700Schasinglulu */ 1022*91f16700Schasinglulu if (ret != 0) 1023*91f16700Schasinglulu SMC_RET1(ctx, ret); 1024*91f16700Schasinglulu 1025*91f16700Schasinglulu SMC_RET0(ctx); 1026*91f16700Schasinglulu 1027*91f16700Schasinglulu case SDEI_EVENT_STATUS: 1028*91f16700Schasinglulu SDEI_LOG("> STAT(n:0x%x)\n", ev_num); 1029*91f16700Schasinglulu ret = sdei_event_status(ev_num); 1030*91f16700Schasinglulu SDEI_LOG("< STAT:%" PRId64 "\n", ret); 1031*91f16700Schasinglulu SMC_RET1(ctx, ret); 1032*91f16700Schasinglulu 1033*91f16700Schasinglulu case SDEI_EVENT_GET_INFO: 1034*91f16700Schasinglulu SDEI_LOG("> INFO(n:0x%x, %d)\n", ev_num, (int) x2); 1035*91f16700Schasinglulu ret = sdei_event_get_info(ev_num, (int) x2); 1036*91f16700Schasinglulu SDEI_LOG("< INFO:%" PRId64 "\n", ret); 1037*91f16700Schasinglulu SMC_RET1(ctx, ret); 1038*91f16700Schasinglulu 1039*91f16700Schasinglulu case SDEI_EVENT_UNREGISTER: 1040*91f16700Schasinglulu SDEI_LOG("> UNREG(n:0x%x)\n", ev_num); 1041*91f16700Schasinglulu ret = sdei_event_unregister(ev_num); 1042*91f16700Schasinglulu SDEI_LOG("< UNREG:%" PRId64 "\n", ret); 1043*91f16700Schasinglulu SMC_RET1(ctx, ret); 1044*91f16700Schasinglulu 1045*91f16700Schasinglulu case SDEI_PE_UNMASK: 1046*91f16700Schasinglulu SDEI_LOG("> UNMASK:%lx\n", read_mpidr_el1()); 1047*91f16700Schasinglulu sdei_pe_unmask(); 1048*91f16700Schasinglulu SDEI_LOG("< UNMASK:%d\n", 0); 1049*91f16700Schasinglulu SMC_RET1(ctx, 0); 1050*91f16700Schasinglulu 1051*91f16700Schasinglulu case SDEI_PE_MASK: 1052*91f16700Schasinglulu SDEI_LOG("> MASK:%lx\n", read_mpidr_el1()); 1053*91f16700Schasinglulu ret = sdei_pe_mask(); 1054*91f16700Schasinglulu SDEI_LOG("< MASK:%" PRId64 "\n", ret); 1055*91f16700Schasinglulu SMC_RET1(ctx, ret); 1056*91f16700Schasinglulu 1057*91f16700Schasinglulu case SDEI_INTERRUPT_BIND: 1058*91f16700Schasinglulu SDEI_LOG("> BIND(%d)\n", (int) x1); 1059*91f16700Schasinglulu ret = sdei_interrupt_bind((unsigned int) x1); 1060*91f16700Schasinglulu SDEI_LOG("< BIND:%" PRId64 "\n", ret); 1061*91f16700Schasinglulu SMC_RET1(ctx, ret); 1062*91f16700Schasinglulu 1063*91f16700Schasinglulu case SDEI_INTERRUPT_RELEASE: 1064*91f16700Schasinglulu SDEI_LOG("> REL(0x%x)\n", ev_num); 1065*91f16700Schasinglulu ret = sdei_interrupt_release(ev_num); 1066*91f16700Schasinglulu SDEI_LOG("< REL:%" PRId64 "\n", ret); 1067*91f16700Schasinglulu SMC_RET1(ctx, ret); 1068*91f16700Schasinglulu 1069*91f16700Schasinglulu case SDEI_SHARED_RESET: 1070*91f16700Schasinglulu SDEI_LOG("> S_RESET():%lx\n", read_mpidr_el1()); 1071*91f16700Schasinglulu ret = sdei_shared_reset(); 1072*91f16700Schasinglulu SDEI_LOG("< S_RESET:%" PRId64 "\n", ret); 1073*91f16700Schasinglulu SMC_RET1(ctx, ret); 1074*91f16700Schasinglulu 1075*91f16700Schasinglulu case SDEI_PRIVATE_RESET: 1076*91f16700Schasinglulu SDEI_LOG("> P_RESET():%lx\n", read_mpidr_el1()); 1077*91f16700Schasinglulu ret = sdei_private_reset(); 1078*91f16700Schasinglulu SDEI_LOG("< P_RESET:%" PRId64 "\n", ret); 1079*91f16700Schasinglulu SMC_RET1(ctx, ret); 1080*91f16700Schasinglulu 1081*91f16700Schasinglulu case SDEI_EVENT_ROUTING_SET: 1082*91f16700Schasinglulu SDEI_LOG("> ROUTE_SET(n:%d f:%" PRIx64 " aff:%" PRIx64 ")\n", ev_num, x2, x3); 1083*91f16700Schasinglulu ret = sdei_event_routing_set(ev_num, x2, x3); 1084*91f16700Schasinglulu SDEI_LOG("< ROUTE_SET:%" PRId64 "\n", ret); 1085*91f16700Schasinglulu SMC_RET1(ctx, ret); 1086*91f16700Schasinglulu 1087*91f16700Schasinglulu case SDEI_FEATURES: 1088*91f16700Schasinglulu SDEI_LOG("> FTRS(f:%" PRIx64 ")\n", x1); 1089*91f16700Schasinglulu ret = (int64_t) sdei_features((unsigned int) x1); 1090*91f16700Schasinglulu SDEI_LOG("< FTRS:%" PRIx64 "\n", ret); 1091*91f16700Schasinglulu SMC_RET1(ctx, ret); 1092*91f16700Schasinglulu 1093*91f16700Schasinglulu case SDEI_EVENT_SIGNAL: 1094*91f16700Schasinglulu SDEI_LOG("> SIGNAL(e:%d t:%" PRIx64 ")\n", ev_num, x2); 1095*91f16700Schasinglulu ret = sdei_signal(ev_num, x2); 1096*91f16700Schasinglulu SDEI_LOG("< SIGNAL:%" PRId64 "\n", ret); 1097*91f16700Schasinglulu SMC_RET1(ctx, ret); 1098*91f16700Schasinglulu 1099*91f16700Schasinglulu default: 1100*91f16700Schasinglulu /* Do nothing in default case */ 1101*91f16700Schasinglulu break; 1102*91f16700Schasinglulu } 1103*91f16700Schasinglulu 1104*91f16700Schasinglulu WARN("Unimplemented SDEI Call: 0x%x\n", smc_fid); 1105*91f16700Schasinglulu SMC_RET1(ctx, SMC_UNK); 1106*91f16700Schasinglulu } 1107*91f16700Schasinglulu 1108*91f16700Schasinglulu /* Subscribe to PSCI CPU on to initialize per-CPU SDEI configuration */ 1109*91f16700Schasinglulu SUBSCRIBE_TO_EVENT(psci_cpu_on_finish, sdei_cpu_on_init); 1110*91f16700Schasinglulu 1111*91f16700Schasinglulu /* Subscribe to PSCI CPU suspend finisher for per-CPU configuration */ 1112*91f16700Schasinglulu SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, sdei_cpu_wakeup_init); 1113