/* * Copyright 2014, General Dynamics C4 Systems * * SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include exception_t decodeIRQControlInvocation(word_t invLabel, word_t length, cte_t *srcSlot, extra_caps_t excaps, word_t *buffer) { if (invLabel == IRQIssueIRQHandler) { word_t index, depth, irq_w; irq_t irq; cte_t *destSlot; cap_t cnodeCap; lookupSlot_ret_t lu_ret; exception_t status; if (length < 3 || excaps.excaprefs[0] == NULL) { current_syscall_error.type = seL4_TruncatedMessage; return EXCEPTION_SYSCALL_ERROR; } irq_w = getSyscallArg(0, buffer); irq = CORE_IRQ_TO_IRQT(0, irq_w); index = getSyscallArg(1, buffer); depth = getSyscallArg(2, buffer); cnodeCap = excaps.excaprefs[0]->cap; status = Arch_checkIRQ(irq_w); if (status != EXCEPTION_NONE) { return status; } if (isIRQActive(irq)) { current_syscall_error.type = seL4_RevokeFirst; userError("Rejecting request for IRQ %u. Already active.", (int)IRQT_TO_IRQ(irq)); return EXCEPTION_SYSCALL_ERROR; } lu_ret = lookupTargetSlot(cnodeCap, index, depth); if (lu_ret.status != EXCEPTION_NONE) { userError("Target slot for new IRQ Handler cap invalid: cap %lu, IRQ %u.", getExtraCPtr(buffer, 0), (int)IRQT_TO_IRQ(irq)); return lu_ret.status; } destSlot = lu_ret.slot; status = ensureEmptySlot(destSlot); if (status != EXCEPTION_NONE) { userError("Target slot for new IRQ Handler cap not empty: cap %lu, IRQ %u.", getExtraCPtr(buffer, 0), (int)IRQT_TO_IRQ(irq)); return status; } setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart); return invokeIRQControl(irq, destSlot, srcSlot); } else { return Arch_decodeIRQControlInvocation(invLabel, length, srcSlot, excaps, buffer); } } exception_t invokeIRQControl(irq_t irq, cte_t *handlerSlot, cte_t *controlSlot) { setIRQState(IRQSignal, irq); cteInsert(cap_irq_handler_cap_new(IRQT_TO_IDX(irq)), controlSlot, handlerSlot); return EXCEPTION_NONE; } exception_t decodeIRQHandlerInvocation(word_t invLabel, irq_t irq, extra_caps_t excaps) { switch (invLabel) { case IRQAckIRQ: setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart); invokeIRQHandler_AckIRQ(irq); return EXCEPTION_NONE; case IRQSetIRQHandler: { cap_t ntfnCap; cte_t *slot; if (excaps.excaprefs[0] == NULL) { current_syscall_error.type = seL4_TruncatedMessage; return EXCEPTION_SYSCALL_ERROR; } ntfnCap = excaps.excaprefs[0]->cap; slot = excaps.excaprefs[0]; if (cap_get_capType(ntfnCap) != cap_notification_cap || !cap_notification_cap_get_capNtfnCanSend(ntfnCap)) { if (cap_get_capType(ntfnCap) != cap_notification_cap) { userError("IRQSetHandler: provided cap is not an notification capability."); } else { userError("IRQSetHandler: caller does not have send rights on the endpoint."); } current_syscall_error.type = seL4_InvalidCapability; current_syscall_error.invalidCapNumber = 0; return EXCEPTION_SYSCALL_ERROR; } setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart); invokeIRQHandler_SetIRQHandler(irq, ntfnCap, slot); return EXCEPTION_NONE; } case IRQClearIRQHandler: setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart); invokeIRQHandler_ClearIRQHandler(irq); return EXCEPTION_NONE; default: userError("IRQHandler: Illegal operation."); current_syscall_error.type = seL4_IllegalOperation; return EXCEPTION_SYSCALL_ERROR; } } void invokeIRQHandler_AckIRQ(irq_t irq) { #ifdef CONFIG_ARCH_RISCV plic_complete_claim(irq); #else #if defined ENABLE_SMP_SUPPORT && defined CONFIG_ARCH_ARM if (IRQ_IS_PPI(irq) && IRQT_TO_CORE(irq) != getCurrentCPUIndex()) { doRemoteMaskPrivateInterrupt(IRQT_TO_CORE(irq), false, IRQT_TO_IDX(irq)); return; } #endif maskInterrupt(false, irq); #endif } void invokeIRQHandler_SetIRQHandler(irq_t irq, cap_t cap, cte_t *slot) { cte_t *irqSlot; irqSlot = intStateIRQNode + IRQT_TO_IDX(irq); /** GHOSTUPD: "(True, gs_set_assn cteDeleteOne_'proc (-1))" */ cteDeleteOne(irqSlot); cteInsert(cap, slot, irqSlot); } void invokeIRQHandler_ClearIRQHandler(irq_t irq) { cte_t *irqSlot; irqSlot = intStateIRQNode + IRQT_TO_IDX(irq); /** GHOSTUPD: "(True, gs_set_assn cteDeleteOne_'proc (-1))" */ cteDeleteOne(irqSlot); } void deletingIRQHandler(irq_t irq) { cte_t *slot; slot = intStateIRQNode + IRQT_TO_IDX(irq); /** GHOSTUPD: "(True, gs_set_assn cteDeleteOne_'proc (ucast cap_notification_cap))" */ cteDeleteOne(slot); } void deletedIRQHandler(irq_t irq) { setIRQState(IRQInactive, irq); } void handleInterrupt(irq_t irq) { if (unlikely(IRQT_TO_IRQ(irq) > maxIRQ)) { /* mask, ack and pretend it didn't happen. We assume that because * the interrupt controller for the platform returned this IRQ that * it is safe to use in mask and ack operations, even though it is * above the claimed maxIRQ. i.e. we're assuming maxIRQ is wrong */ printf("Received IRQ %d, which is above the platforms maxIRQ of %d\n", (int)IRQT_TO_IRQ(irq), (int)maxIRQ); maskInterrupt(true, irq); ackInterrupt(irq); return; } switch (intStateIRQTable[IRQT_TO_IDX(irq)]) { case IRQSignal: { cap_t cap; cap = intStateIRQNode[IRQT_TO_IDX(irq)].cap; if (cap_get_capType(cap) == cap_notification_cap && cap_notification_cap_get_capNtfnCanSend(cap)) { sendSignal(NTFN_PTR(cap_notification_cap_get_capNtfnPtr(cap)), cap_notification_cap_get_capNtfnBadge(cap)); } else { #ifdef CONFIG_IRQ_REPORTING printf("Undelivered IRQ: %d\n", (int)IRQT_TO_IRQ(irq)); #endif } #ifndef CONFIG_ARCH_RISCV maskInterrupt(true, irq); #endif break; } case IRQTimer: #ifdef CONFIG_KERNEL_MCS ackDeadlineIRQ(); NODE_STATE(ksReprogram) = true; #else timerTick(); resetTimer(); #endif break; #ifdef ENABLE_SMP_SUPPORT case IRQIPI: handleIPI(irq, true); break; #endif /* ENABLE_SMP_SUPPORT */ case IRQReserved: handleReservedIRQ(irq); break; case IRQInactive: /* * This case shouldn't happen anyway unless the hardware or * platform code is broken. Hopefully masking it again should make * the interrupt go away. */ maskInterrupt(true, irq); #ifdef CONFIG_IRQ_REPORTING printf("Received disabled IRQ: %d\n", (int)IRQT_TO_IRQ(irq)); #endif break; default: /* No corresponding haskell error */ fail("Invalid IRQ state"); } ackInterrupt(irq); } bool_t isIRQActive(irq_t irq) { return intStateIRQTable[IRQT_TO_IDX(irq)] != IRQInactive; } void setIRQState(irq_state_t irqState, irq_t irq) { intStateIRQTable[IRQT_TO_IDX(irq)] = irqState; #if defined ENABLE_SMP_SUPPORT && defined CONFIG_ARCH_ARM if (IRQ_IS_PPI(irq) && IRQT_TO_CORE(irq) != getCurrentCPUIndex()) { doRemoteMaskPrivateInterrupt(IRQT_TO_CORE(irq), irqState == IRQInactive, IRQT_TO_IDX(irq)); return; } #endif maskInterrupt(irqState == IRQInactive, irq); }