void virq_init(uint32_t const * const user_vtor) { /* Detect the number of implemented priority bits. * The architecture specifies that unused/not implemented bits in the * NVIC IP registers read back as 0. */ __disable_irq(); uint8_t volatile * prio = (uint8_t volatile *) &(NVIC->IPR[0]); uint8_t prio_bits = *prio; *prio = 0xFFU; g_virq_prio_bits = (uint8_t) __builtin_popcount(*prio); *prio = prio_bits; __enable_irq(); /* Verify that the priority bits read at runtime are realistic. */ assert(g_virq_prio_bits > 0 && g_virq_prio_bits <= 8); /* Set the minimum IRQ priority. */ g_virq_min_priority = (uint8_t) ((1 << g_virq_prio_bits) - 1); /* At the beginning, all IRQs are dis-owned. */ for (size_t irqn = 0; irqn < NVIC_VECTORS; ++irqn) { g_virq_states[irqn].box_id = UVISOR_BOX_ID_INVALID; NVIC_ClearTargetState(irqn); } }
/*----------------- NVIC interrupt target state to NS configuration ----------*/ void nvic_interrupt_target_state_cfg() { /* Target every interrupt to NS; unimplemented interrupts will be WI */ for (uint8_t i=0; i<sizeof(NVIC->ITNS)/sizeof(NVIC->ITNS[0]); i++) { NVIC->ITNS[i] = 0xFFFFFFFF; } /* Make sure that MPC and PPC are targeted to S state */ NVIC_ClearTargetState(SEC_VIO_IRQn); }
void virq_switch(uint8_t src_id, uint8_t dst_id) { bool src_box_in_active_irq = false; for (uint32_t irqn = 0; irqn < NVIC_VECTORS; ++irqn) { TVirqState * irq = &g_virq_states[irqn]; /* Put all the source box IRQs on hold. * Putting an IRQ on hold means: * - Promote it to secure state, so that NS code cannot modify it. * - De-prioritize it, so that the destination box can be pre-empted. */ if (irq->box_id == src_id) { irq->enabled = TZ_NVIC_GetEnableIRQ_NS(irqn); irq->priority = TZ_NVIC_GetPriority_NS(irqn); if (TZ_NVIC_GetActive_NS(irqn)) { src_box_in_active_irq = true; } assert(irq->priority < g_virq_min_priority); TZ_NVIC_SetPriority_NS(irqn, g_virq_min_priority); TZ_NVIC_DisableIRQ_NS(irqn); NVIC_ClearTargetState(irqn); } /* Re-enable all the destination box IRQs. */ if (irq->box_id == dst_id) { NVIC_SetTargetState(irqn); if (irq->enabled) { TZ_NVIC_EnableIRQ_NS(irqn); } TZ_NVIC_SetPriority_NS(irqn, irq->priority); } } /* System exceptions * These exceptions are banked between secure states, and hence need to be * saved and restored at every context switch. */ /* Note: The very first time that an exception state is saved or restored, * the box has actually never run. The code below assumes that all the * save/restore functions use a sensible default state, i.e., an * inactive, not pending, not enabled exception state. */ /* SysTick */ src_box_in_active_irq |= virq_copy_systick_ns(src_id); virq_load_systick_ns(dst_id); /* SVCall */ src_box_in_active_irq |= virq_copy_svcall_ns(src_id); virq_load_svcall_ns(dst_id); /* Save the active state for the source box. */ g_virq_box_in_active_irq[src_id] = src_box_in_active_irq; }
/** * * @brief Set the target security state for the given IRQ * * Function sets the security state (Secure or Non-Secure) targeted * by the given irq. It requires ARMv8-M MCU. * It is only compiled if ARM_SECURE_FIRMWARE is defined. * It should only be called while in Secure state, otherwise, a write attempt * to NVIC.ITNS register is write-ignored(WI), as the ITNS register is not * banked between security states and, therefore, has no Non-Secure instance. * * It shall assert if the operation is not performed successfully. * * @param irq IRQ line * @param secure_state 1 if target state is Secure, 0 otherwise. * * @return N/A */ void irq_target_state_set(unsigned int irq, int secure_state) { if (secure_state) { /* Set target to Secure */ if (NVIC_ClearTargetState(irq) != 0) { __ASSERT(0, "NVIC SetTargetState error"); } } else { /* Set target state to Non-Secure */ if (NVIC_SetTargetState(irq) != 1) { __ASSERT(0, "NVIC SetTargetState error"); } } }