/* * After leaving Non-secure world, stash current Non-secure Priority Mask, and * set Priority Mask to the highest Non-secure priority so that Non-secure * interrupts cannot preempt Secure execution. * * If the current running priority is in the secure range, or if there are * outstanding priority activations, this function does nothing. * * This function subscribes to the 'cm_exited_normal_world' event published by * the Context Management Library. */ static void *ehf_exited_normal_world(const void *arg) { unsigned int run_pri; pe_exc_data_t *pe_data = this_cpu_data(); /* If the running priority is in the secure range, do nothing */ run_pri = plat_ic_get_running_priority(); if (IS_PRI_SECURE(run_pri)) return 0; /* Do nothing if there are explicit activations */ if (has_valid_pri_activations(pe_data)) return 0; assert(pe_data->ns_pri_mask == 0); pe_data->ns_pri_mask = plat_ic_set_priority_mask(GIC_HIGHEST_NS_PRIORITY); /* The previous Priority Mask is not expected to be in secure range */ if (IS_PRI_SECURE(pe_data->ns_pri_mask)) { ERROR("Priority Mask (0x%x) already in secure range\n", pe_data->ns_pri_mask); panic(); } EHF_LOG("Priority Mask: 0x%x => 0x%x\n", pe_data->ns_pri_mask, GIC_HIGHEST_NS_PRIORITY); return 0; }
/* * Top-level EL3 interrupt handler. */ static uint64_t ehf_el3_interrupt_handler(uint32_t id, uint32_t flags, void *handle, void *cookie) { int ret = 0; uint32_t intr_raw; unsigned int intr, pri, idx; ehf_handler_t handler; /* * Top-level interrupt type handler from Interrupt Management Framework * doesn't acknowledge the interrupt; so the interrupt ID must be * invalid. */ assert(id == INTR_ID_UNAVAILABLE); /* * Acknowledge interrupt. Proceed with handling only for valid interrupt * IDs. This situation may arise because of Interrupt Management * Framework identifying an EL3 interrupt, but before it's been * acknowledged here, the interrupt was either deasserted, or there was * a higher-priority interrupt of another type. */ intr_raw = plat_ic_acknowledge_interrupt(); intr = plat_ic_get_interrupt_id(intr_raw); if (intr == INTR_ID_UNAVAILABLE) return 0; /* Having acknowledged the interrupt, get the running priority */ pri = plat_ic_get_running_priority(); /* Check EL3 interrupt priority is in secure range */ assert(IS_PRI_SECURE(pri)); /* * Translate the priority to a descriptor index. We do this by masking * and shifting the running priority value (platform-supplied). */ idx = pri_to_idx(pri); /* Validate priority */ assert(pri == IDX_TO_PRI(idx)); handler = (ehf_handler_t) RAW_HANDLER( exception_data.ehf_priorities[idx].ehf_handler); if (handler == NULL) { ERROR("No EL3 exception handler for priority 0x%x\n", IDX_TO_PRI(idx)); panic(); } /* * Call registered handler. Pass the raw interrupt value to registered * handlers. */ ret = handler(intr_raw, flags, handle, cookie); return (uint64_t) ret; }
/* * Conclude Secure execution and prepare for return to Non-secure world. Restore * the Non-secure Priority Mask previously stashed upon leaving Non-secure * world. * * If there the current running priority is in the secure range, or if there are * outstanding priority activations, this function does nothing. * * This function subscribes to the 'cm_entering_normal_world' event published by * the Context Management Library. */ static void *ehf_entering_normal_world(const void *arg) { unsigned int old_pmr, run_pri; pe_exc_data_t *pe_data = this_cpu_data(); /* If the running priority is in the secure range, do nothing */ run_pri = plat_ic_get_running_priority(); if (IS_PRI_SECURE(run_pri)) return 0; /* * If there are explicit activations, do nothing. The Priority Mask will * be restored upon the last deactivation. */ if (has_valid_pri_activations(pe_data)) return 0; /* Do nothing if we don't have a valid Priority Mask to restore */ if (pe_data->ns_pri_mask == 0) return 0; old_pmr = plat_ic_set_priority_mask(pe_data->ns_pri_mask); /* * When exiting secure world, the current Priority Mask must be * GIC_HIGHEST_NS_PRIORITY (as set during entry), or the Non-secure * priority mask set upon calling ehf_allow_ns_preemption() */ if ((old_pmr != GIC_HIGHEST_NS_PRIORITY) && (old_pmr != pe_data->ns_pri_mask)) { ERROR("Invalid Priority Mask (0x%x) restored\n", old_pmr); panic(); } EHF_LOG("Priority Mask: 0x%x => 0x%x\n", old_pmr, pe_data->ns_pri_mask); pe_data->ns_pri_mask = 0; return 0; }
/* * Return whether Secure execution has explicitly allowed Non-secure interrupts * to preempt itself, viz. during Yielding SMC calls. */ unsigned int ehf_is_ns_preemption_allowed(void) { unsigned int run_pri; pe_exc_data_t *pe_data = this_cpu_data(); /* If running priority is in secure range, return false */ run_pri = plat_ic_get_running_priority(); if (IS_PRI_SECURE(run_pri)) return 0; /* * If Non-secure preemption was permitted by calling * ehf_allow_ns_preemption() earlier: * * - There wouldn't have been priority activations; * - We would have cleared the stashed the Non-secure Priority Mask. */ if (has_valid_pri_activations(pe_data)) return 0; if (pe_data->ns_pri_mask != 0) return 0; return 1; }