arm: Add new APIs for generating SGIs

Allow SGIs to be generated from non-SMP kernels.

Signed-off-by: Kent McLeod <kent@kry10.com>
Signed-off-by: Gerwin Klein <gerwin.klein@proofcraft.systems>
This commit is contained in:
Kent McLeod 2022-09-13 23:24:34 +10:00 committed by Gerwin Klein
parent bcf5ab7924
commit 34725d060b
20 changed files with 306 additions and 10 deletions

View File

@ -102,6 +102,19 @@ block vcpu_cap {
}
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
block sgi_signal_cap {
field capSGITarget 32
padding 20
field capSGIIRQ 4
field capType 8
}
#endif
#ifdef CONFIG_TK1_SMMU
-- IO space caps
-- each module has an engine that can be enabled
@ -179,6 +192,9 @@ tagged_union cap capType {
tag io_space_cap 0x1f
tag io_page_table_cap 0x2f
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
tag sgi_signal_cap 0x3f
#endif
}
---- Arm specific fault types

View File

@ -298,6 +298,10 @@ static inline word_t CONST cap_get_archCapSizeBits(cap_t cap)
case cap_vcpu_cap:
return VCPU_SIZE_BITS;
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
case cap_sgi_signal_cap:
return 0;
#endif
#ifdef CONFIG_TK1_SMMU
case cap_io_page_table_cap:
return seL4_IOPageTableBits;
@ -339,6 +343,10 @@ static inline bool_t CONST cap_get_archCapIsPhysical(cap_t cap)
case cap_vcpu_cap:
return true;
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
case cap_sgi_signal_cap:
return false;
#endif
#ifdef CONFIG_TK1_SMMU
case cap_io_page_table_cap:
@ -379,6 +387,10 @@ static inline void *CONST cap_get_archCapPtr(cap_t cap)
case cap_vcpu_cap:
return VCPU_PTR(cap_vcpu_cap_get_capVCPUPtr(cap));
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
case cap_sgi_signal_cap:
return NULL;
#endif
#ifdef CONFIG_TK1_SMMU
case cap_io_page_table_cap:

View File

@ -87,6 +87,18 @@ block vcpu_cap {
}
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
block sgi_signal_cap {
padding 32
field capSGITarget 32
field capType 5
field capSGIIRQ 4
padding 55
}
#endif
#ifdef CONFIG_ARM_SMMU
block sid_control_cap {
@ -172,6 +184,9 @@ tagged_union cap capType {
#ifdef CONFIG_ALLOW_SMC_CALLS
tag smc_cap 25
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
tag sgi_signal_cap 27
#endif
}
---- Arch-independent object types

View File

@ -107,6 +107,10 @@ static inline word_t CONST cap_get_archCapSizeBits(cap_t cap)
case cap_vcpu_cap:
return seL4_VCPUBits;
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
case cap_sgi_signal_cap:
return 0;
#endif
default:
/* Unreachable, but GCC can't figure that out */
@ -141,6 +145,10 @@ static inline bool_t CONST cap_get_archCapIsPhysical(cap_t cap)
case cap_vcpu_cap:
return true;
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
case cap_sgi_signal_cap:
return false;
#endif
default:
/* Unreachable, but GCC can't figure that out */
@ -174,6 +182,10 @@ static inline void *CONST cap_get_archCapPtr(cap_t cap)
case cap_vcpu_cap:
return VCPU_PTR(cap_vcpu_cap_get_capVCPUPtr(cap));
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
case cap_sgi_signal_cap:
return NULL;
#endif
default:
/* Unreachable, but GCC can't figure that out */

View File

@ -23,6 +23,8 @@ void setIRQTrigger(irq_t irq, bool_t trigger);
#ifdef ENABLE_SMP_SUPPORT
void setIRQTarget(irq_t irq, seL4_Word target);
#endif
bool_t plat_SGITargetValid(word_t target);
void plat_sendSGI(word_t irq, word_t target);
static inline void plat_cleanL2Range(paddr_t start, paddr_t end);
static inline void plat_invalidateL2Range(paddr_t start, paddr_t end);

View File

@ -22,6 +22,7 @@
/* CPU specific IRQ's */
#define SGI_START 0u
#define PPI_START 16u
#define NUM_SGIS 16u
/* Shared Peripheral Interrupts */
#define SPI_START 32u

View File

@ -23,6 +23,8 @@
#define IRQ_MASK MASK(10u)
#define GIC_VCPU_MAX_NUM_LR 64
#define GIC_SGI_NUM_TARGETS 8
/* Helpers for VGIC */
#define VGIC_HCR_EOI_INVALID_COUNT(hcr) (((hcr) >> 27) & 0x1f)
#define VGIC_HCR_VGRP1DIE (1U << 7)

View File

@ -30,6 +30,8 @@
#define IRQ_MASK MASK(16u)
#define GIC_VCPU_MAX_NUM_LR 16
#define GIC_SGI_NUM_TARGETS 16
/* Register bits */
#define GICD_CTLR_RWP BIT(31)
#define GICD_CTLR_ARE_NS BIT(5)

View File

@ -15,6 +15,9 @@
exception_t Arch_decodeIRQControlInvocation(word_t invLabel, word_t length,
cte_t *srcSlot, word_t *buffer);
exception_t decodeSGISignalInvocation(word_t invLabel, word_t length,
cap_t cap, word_t *buffer);
/* Handle a platform-reserved IRQ. */
static inline void handleReservedIRQ(irq_t irq)
{

View File

@ -143,4 +143,3 @@ exception_t handle_SysDebugSendIPI(void);
#endif
#endif /* ENABLE_SMP_SUPPORT */

View File

@ -783,6 +783,59 @@
</description>
</error>
</method>
<method id="ARMIRQIssueSGISignal" name="IssueSGISignal" manual_name="IssueSGISignal"
manual_label="irq_controlissuesgisignal">
<condition><not><config var="CONFIG_ENABLE_SMP_SUPPORT"/></not></condition>
<brief>
Create a software generated interrupt (SGI) signal capability.
</brief>
<description>
Create an SGISignal capability and place it in the specific target CSpace slot. The
capability can be used to raise an SGI with a specific ID on a specific target core.
Currently this feature is supported on GICv2 and GICv3 hardware. Only available on
non-SMP configurations.
The resulting capability can be invoked like a notification capability that supports
only signal/send. SGIs can be received by IRQ notification objects on the target
core like other IRQs. See also <autoref label="sec:interrupts"/>.
</description>
<param dir="in" name="irq" type="seL4_Word" description="The SGI INTID (0-15) that can be signalled."/>
<param dir="in" name="target" type="seL4_Word" description="The node ID that will be
targeted. 0-7 for GICv2 and 0-31 for GICv3. Targets within that range that are not
supported by the hardware will be ignored. For example, on a GICv3 board with 4 CPUs, the
capability for target 13 can be created, but signals to it will have no effect."/>
<param dir="in" name="root" type="seL4_CNode" description="CPtr to the CNode that forms the root of the destination CSpace. Must be at a depth equivalent to the wordsize."/>
<param dir="in" name="index" type="seL4_Word" description="CPtr to the destination slot. Resolved from the root of the destination CSpace."/>
<param dir="in" name="depth" type="seL4_Uint8" description="Number of bits of dest_index to resolve to find the destination slot."/>
<error name="seL4_DeleteFirst">
<description>
The destination slot contains a capability.
</description>
</error>
<error name="seL4_FailedLookup">
<description>
The <texttt text="index"/> or <texttt text="depth"/> is invalid <docref>(see <autoref label="s:cspace-addressing"/>)</docref>.
Or, <texttt text="root"/> is a CPtr to a capability of the wrong type.
</description>
</error>
<error name="seL4_IllegalOperation">
<description>
The <texttt text="_service"/> is a CPtr to a capability of the wrong type.
</description>
</error>
<error name="seL4_InvalidCapability">
<description>
The <texttt text="_service"/> is a CPtr to a capability of the wrong type.
</description>
</error>
<error name="seL4_RangeError">
<description>
The value of <texttt text="irq"/> or <texttt text="target"/> is out of range.
Or, <texttt text="depth"/> is invalid <docref>(see <autoref label="s:cspace-addressing"/>)</docref>.
</description>
</error>
</method>
</interface>
<interface name="seL4_ARM_SIDControl" manual_name="SID Control" cap_description="A SIDControl capability. This gives you the authority to make this call.">
<method id="ARMSIDIssueSIDManager" name="GetSID" manual_name="Get SID" manual_label="sid_controlgetsid">

View File

@ -649,8 +649,8 @@ LIBSEL4_INLINE_FUNC void seL4_DebugSendIPI(seL4_Uint8 target, unsigned irq)
{
arm_sys_send(seL4_SysDebugSendIPI, target, irq, 0, 0, 0, 0);
}
#endif /* CONFIG_ENABLE_SMP_SUPPORT */
#endif
#endif /* CONFIG_DEBUG_BUILD */
#ifdef CONFIG_DANGEROUS_CODE_INJECTION
LIBSEL4_INLINE_FUNC void seL4_DebugRun(void (* userfn)(void *), void *userarg)

View File

@ -23,6 +23,7 @@ typedef seL4_CPtr seL4_ARM_SID;
typedef seL4_CPtr seL4_ARM_CBControl;
typedef seL4_CPtr seL4_ARM_CB;
typedef seL4_CPtr seL4_ARM_SMC;
typedef seL4_CPtr seL4_ARM_SGI_Signal;
typedef enum {
seL4_ARM_PageCacheable = 0x01,

View File

@ -50,12 +50,7 @@
<syscall name="DebugNameThread"/>
</config>
<config>
<condition>
<and>
<config var="CONFIG_DEBUG_BUILD"/>
<config var="CONFIG_ENABLE_SMP_SUPPORT"/>
</and>
</condition>
<condition><config var="CONFIG_DEBUG_BUILD"/></condition>
<syscall name="DebugSendIPI"/>
</config>
<config>

View File

@ -87,6 +87,13 @@ deriveCap_ret_t Arch_deriveCap(cte_t *slot, cap_t cap)
return ret;
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
case cap_sgi_signal_cap:
ret.cap = cap;
ret.status = EXCEPTION_NONE;
return ret;
#endif
#ifdef CONFIG_TK1_SMMU
case cap_io_space_cap:
ret.cap = cap;
@ -225,6 +232,13 @@ finaliseCap_ret_t Arch_finaliseCap(cap_t cap, bool_t final)
break;
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
case cap_sgi_signal_cap:
// do nothing
break;
#endif
#ifdef CONFIG_TK1_SMMU
case cap_io_space_cap:
if (final) {
@ -300,6 +314,18 @@ bool_t CONST Arch_sameRegionAs(cap_t cap_a, cap_t cap_b)
break;
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
case cap_sgi_signal_cap:
if (cap_get_capType(cap_b) == cap_sgi_signal_cap) {
return (cap_sgi_signal_cap_get_capSGIIRQ(cap_a) ==
cap_sgi_signal_cap_get_capSGIIRQ(cap_b) &&
cap_sgi_signal_cap_get_capSGITarget(cap_a) ==
cap_sgi_signal_cap_get_capSGITarget(cap_b));
}
break;
#endif
#ifdef CONFIG_TK1_SMMU
case cap_io_space_cap:
if (cap_get_capType(cap_b) == cap_io_space_cap) {
@ -553,7 +579,7 @@ exception_t Arch_decodeInvocation(word_t invLabel, word_t length, cptr_t cptr,
/* The C parser cannot handle a switch statement with only a default
* case. So we need to do some gymnastics to remove the switch if
* there are no other cases */
#if defined(CONFIG_TK1_SMMU) || defined(CONFIG_ARM_HYPERVISOR_SUPPORT)
#if defined(CONFIG_TK1_SMMU) || defined(CONFIG_ARM_HYPERVISOR_SUPPORT) || !defined(CONFIG_ENABLE_SMP_SUPPORT)
switch (cap_get_capType(cap)) {
#ifdef CONFIG_TK1_SMMU
case cap_io_space_cap:
@ -565,6 +591,11 @@ exception_t Arch_decodeInvocation(word_t invLabel, word_t length, cptr_t cptr,
case cap_vcpu_cap:
return decodeARMVCPUInvocation(invLabel, length, cptr, slot, cap, call, buffer);
#endif /* end of CONFIG_ARM_HYPERVISOR_SUPPORT */
#ifndef CONFIG_ENABLE_SMP_SUPPORT
case cap_sgi_signal_cap:
return decodeSGISignalInvocation(invLabel, length, cap, buffer);
#endif /* end of !CONFIG_ENABLE_SMP_SUPPORT */
default:
#else
{

View File

@ -73,6 +73,13 @@ deriveCap_ret_t Arch_deriveCap(cte_t *slot, cap_t cap)
ret.status = EXCEPTION_NONE;
return ret;
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
case cap_sgi_signal_cap:
ret.cap = cap;
ret.status = EXCEPTION_NONE;
return ret;
#endif
#ifdef CONFIG_ARM_SMMU
case cap_sid_control_cap:
case cap_cb_control_cap:
@ -177,6 +184,13 @@ finaliseCap_ret_t Arch_finaliseCap(cap_t cap, bool_t final)
}
break;
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
case cap_sgi_signal_cap:
// do nothing
break;
#endif
#ifdef CONFIG_ARM_SMMU
case cap_cb_cap:
if (cap_cb_cap_get_capBindSID(cap) != SID_INVALID) {
@ -249,6 +263,17 @@ bool_t CONST Arch_sameRegionAs(cap_t cap_a, cap_t cap_b)
}
break;
#endif
#ifndef CONFIG_ENABLE_SMP_SUPPORT
case cap_sgi_signal_cap:
if (cap_get_capType(cap_b) == cap_sgi_signal_cap) {
return (cap_sgi_signal_cap_get_capSGIIRQ(cap_a) ==
cap_sgi_signal_cap_get_capSGIIRQ(cap_b) &&
cap_sgi_signal_cap_get_capSGITarget(cap_a) ==
cap_sgi_signal_cap_get_capSGITarget(cap_b));
}
break;
#endif
#ifdef CONFIG_ARM_SMMU
case cap_sid_control_cap:
if (cap_get_capType(cap_b) == cap_sid_control_cap ||
@ -470,12 +495,16 @@ exception_t Arch_decodeInvocation(word_t label, word_t length, cptr_t cptr,
/* The C parser cannot handle a switch statement with only a default
* case. So we need to do some gymnastics to remove the switch if
* there are no other cases */
#if defined(CONFIG_ARM_HYPERVISOR_SUPPORT) || defined(CONFIG_ARM_SMMU) || defined(CONFIG_ALLOW_SMC_CALLS)
#if defined(CONFIG_ARM_HYPERVISOR_SUPPORT) || defined(CONFIG_ARM_SMMU) || defined(CONFIG_ALLOW_SMC_CALLS) || !defined(CONFIG_ENABLE_SMP_SUPPORT)
switch (cap_get_capType(cap)) {
#ifdef CONFIG_ARM_HYPERVISOR_SUPPORT
case cap_vcpu_cap:
return decodeARMVCPUInvocation(label, length, cptr, slot, cap, call, buffer);
#endif /* end of CONFIG_ARM_HYPERVISOR_SUPPORT */
#ifndef CONFIG_ENABLE_SMP_SUPPORT
case cap_sgi_signal_cap:
return decodeSGISignalInvocation(label, length, cap, buffer);
#endif /* end of !CONFIG_ENABLE_SMP_SUPPORT */
#ifdef CONFIG_ARM_SMMU
case cap_sid_control_cap:
return decodeARMSIDControlInvocation(label, length, cptr, slot, cap, call, buffer);

View File

@ -175,6 +175,16 @@ BOOT_CODE void cpu_initLocalIRQController(void)
cpu_iface_init();
}
bool_t plat_SGITargetValid(word_t target)
{
return target < GIC_SGI_NUM_TARGETS;
}
void plat_sendSGI(word_t irq, word_t target)
{
gic_dist->sgi_control = (BIT(target) << (GICD_SGIR_CPUTARGETLIST_SHIFT)) | (irq << GICD_SGIR_SGIINTID_SHIFT);
}
#ifdef ENABLE_SMP_SUPPORT
/*
* 25-24: target lister filter

View File

@ -28,6 +28,9 @@
#define ICC_SGI1R_INTID_SHIFT (24)
#define ICC_SGI1R_AFF1_SHIFT (16)
#define ICC_SGI1R_AFF2_SHIFT (32)
#define ICC_SGI1R_AFF3_SHIFT (48)
#define ICC_SGI1R_RS_SHIFT (44)
#define ICC_SGI1R_IRM_BIT (40)
#define ICC_SGI1R_CPUTARGETLIST_MASK 0xffff
@ -74,6 +77,17 @@ static inline uint64_t mpidr_to_gic_affinity(void)
return affinity;
}
static inline uint64_t sgir_word_from_args(word_t irq, word_t target)
{
uint64_t t = target; /* make sure shifts below are on 64 bit */
return (uint64_t) irq << ICC_SGI1R_INTID_SHIFT
| (1llu << (t & 0xf)) // AFF0 base
| ((t >> 4) & 0x0f) << ICC_SGI1R_RS_SHIFT // AFF0 Range select
| ((t >> 8) & 0xff) << ICC_SGI1R_AFF1_SHIFT // AFF1
| ((t >> 16) & 0xff) << ICC_SGI1R_AFF2_SHIFT // AFF2
| ((t >> 24) & 0xff) << ICC_SGI1R_AFF2_SHIFT; // AFF3
}
/* Wait for completion of a distributor change */
/** DONT_TRANSLATE */
static uint32_t gicv3_do_wait_for_rwp(volatile uint32_t *ctlr_addr)
@ -343,6 +357,18 @@ BOOT_CODE void cpu_initLocalIRQController(void)
cpu_iface_init();
}
bool_t plat_SGITargetValid(word_t target)
{
return target < GIC_SGI_NUM_TARGETS;
}
void plat_sendSGI(word_t irq, word_t target)
{
uint64_t sgi1r_base = sgir_word_from_args(irq, target);
SYSTEM_WRITE_64(ICC_SGI1R_EL1, sgi1r_base);
isb();
}
#ifdef ENABLE_SMP_SUPPORT
#define MPIDR_MT(x) (x & BIT(24))

View File

@ -18,6 +18,17 @@ static exception_t Arch_invokeIRQControl(irq_t irq, cte_t *handlerSlot, cte_t *c
return invokeIRQControl(irq, handlerSlot, controlSlot);
}
#ifndef CONFIG_ENABLE_SMP_SUPPORT
static exception_t Arch_invokeIssueSGISignal(word_t irq, word_t target, cte_t *sgiSlot, cte_t *controlSlot)
{
cteInsert(cap_sgi_signal_cap_new(target, irq), controlSlot, sgiSlot);
return EXCEPTION_NONE;
}
#endif
exception_t Arch_decodeIRQControlInvocation(word_t invLabel, word_t length,
cte_t *srcSlot, word_t *buffer)
{
@ -77,6 +88,52 @@ exception_t Arch_decodeIRQControlInvocation(word_t invLabel, word_t length,
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
return Arch_invokeIRQControl(irq, destSlot, srcSlot, trigger);
#ifndef CONFIG_ENABLE_SMP_SUPPORT
} else if (invLabel == ARMIRQIssueSGISignal) {
if (length < 3 || current_extra_caps.excaprefs[0] == NULL) {
userError("IRQControl: IssueSGISignal: Truncated message.");
current_syscall_error.type = seL4_TruncatedMessage;
return EXCEPTION_SYSCALL_ERROR;
}
word_t irq = getSyscallArg(0, buffer);
word_t target = getSyscallArg(1, buffer);
word_t index = getSyscallArg(2, buffer);
word_t depth = getSyscallArg(3, buffer);
cap_t cnodeCap = current_extra_caps.excaprefs[0]->cap;
if (irq >= NUM_SGIS) {
current_syscall_error.type = seL4_RangeError;
current_syscall_error.rangeErrorMin = 0;
current_syscall_error.rangeErrorMax = NUM_SGIS -1;
userError("IRQControl: IssueSGISignal: Invalid SGI IRQ 0x%lx.", irq);
return EXCEPTION_SYSCALL_ERROR;
}
if (!plat_SGITargetValid(target)) {
current_syscall_error.type = seL4_InvalidArgument;
userError("IRQControl: IssueSGISignal: Invalid SGI Target 0x%lx.", target);
return EXCEPTION_SYSCALL_ERROR;
}
lookupSlot_ret_t lu_ret = lookupTargetSlot(cnodeCap, index, depth);
if (lu_ret.status != EXCEPTION_NONE) {
userError("IRQControl: IssueSGISignal: Target slot for new ARM_SGI_Signal cap invalid: cap %lu.",
getExtraCPtr(buffer, 0));
return lu_ret.status;
}
cte_t *destSlot = lu_ret.slot;
exception_t status = ensureEmptySlot(destSlot);
if (status != EXCEPTION_NONE) {
userError("IRQControl: IssueSGISignal: Target slot for new ARM_SGI_Signal cap not empty: cap %lu.",
getExtraCPtr(buffer, 0));
return status;
}
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
return Arch_invokeIssueSGISignal(irq, target, destSlot, srcSlot);
#endif
#ifdef ENABLE_SMP_SUPPORT
} else if (invLabel == ARMIRQIssueIRQHandlerTriggerCore) {
word_t irq_w = getSyscallArg(0, buffer);
@ -135,3 +192,24 @@ exception_t Arch_decodeIRQControlInvocation(word_t invLabel, word_t length,
return EXCEPTION_SYSCALL_ERROR;
}
}
#ifndef CONFIG_ENABLE_SMP_SUPPORT
static exception_t invokeSGISignalGenerate(word_t irq, word_t target)
{
plat_sendSGI(irq, target);
return EXCEPTION_NONE;
}
exception_t decodeSGISignalInvocation(word_t invLabel, word_t length,
cap_t cap, word_t *buffer)
{
word_t irq = cap_sgi_signal_cap_get_capSGIIRQ(cap);
word_t target = cap_sgi_signal_cap_get_capSGITarget(cap);
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
return invokeSGISignalGenerate(irq, target);
}
#endif /* !CONFIG_ENABLE_SMP_SUPPORT */

View File

@ -342,6 +342,9 @@ bool_t CONST sameRegionAs(cap_t cap_a, cap_t cap_b)
case cap_irq_control_cap:
if (cap_get_capType(cap_b) == cap_irq_control_cap ||
#if CONFIG_MAX_NUM_NODES == 1
cap_get_capType(cap_b) == cap_sgi_signal_cap ||
#endif
cap_get_capType(cap_b) == cap_irq_handler_cap) {
return true;
}
@ -389,6 +392,12 @@ bool_t CONST sameObjectAs(cap_t cap_a, cap_t cap_b)
cap_get_capType(cap_b) == cap_irq_handler_cap) {
return false;
}
#if CONFIG_MAX_NUM_NODES == 1
if (cap_get_capType(cap_a) == cap_irq_control_cap &&
cap_get_capType(cap_b) == cap_sgi_signal_cap) {
return false;
}
#endif
if (isArchCap(cap_a) && isArchCap(cap_b)) {
return Arch_sameObjectAs(cap_a, cap_b);
}