feat: route secure interrupts to any physical PE
This patch makes changes necessary to allow a physical interrupt to be
routed to a particular PE (by mpidr). Until now, all interrupts were
routed to the boot core by default. To maintain compatiblity with
existing behavior and manifests, the patch adds a new manifest field
'interrupts-target' through which the interrupt id and corresponding
MPIDR are supplied. This is then used to configure the gic
appropriately, to route interrupts to the desired PE.
Change-Id: I3ed8f51e7679bb65c0aca030b21395598553e6ec
Signed-off-by: Raghu Krishnamurthy <raghu.ncstate@gmail.com>
diff --git a/inc/hf/interrupt_desc.h b/inc/hf/interrupt_desc.h
index 958777c..6fcb938 100644
--- a/inc/hf/interrupt_desc.h
+++ b/inc/hf/interrupt_desc.h
@@ -102,6 +102,8 @@
uint8_t type_config_sec_state;
uint8_t priority;
bool valid;
+ bool mpidr_valid;
+ uint64_t mpidr;
};
/**
@@ -137,6 +139,18 @@
return int_desc.priority;
}
+static inline uint64_t interrupt_desc_get_mpidr(
+ struct interrupt_descriptor int_desc)
+{
+ return int_desc.mpidr;
+}
+
+static inline bool interrupt_desc_get_mpidr_valid(
+ struct interrupt_descriptor int_desc)
+{
+ return int_desc.mpidr_valid;
+}
+
static inline bool interrupt_desc_get_valid(
struct interrupt_descriptor int_desc)
{
@@ -149,6 +163,20 @@
int_desc->interrupt_id = interrupt_id;
}
+static inline void interrupt_desc_set_mpidr(
+ struct interrupt_descriptor *int_desc, uint64_t mpidr)
+{
+ int_desc->mpidr_valid = true;
+ int_desc->mpidr = mpidr;
+}
+
+static inline void interrupt_desc_set_mpidr_invalid(
+ struct interrupt_descriptor *int_desc)
+{
+ int_desc->mpidr_valid = false;
+ int_desc->mpidr = 0;
+}
+
static inline void interrupt_desc_set_type_config_sec_state(
struct interrupt_descriptor *int_desc, uint8_t value)
{
diff --git a/inc/hf/manifest.h b/inc/hf/manifest.h
index d2f9428..3f4cd3e 100644
--- a/inc/hf/manifest.h
+++ b/inc/hf/manifest.h
@@ -67,6 +67,8 @@
struct interrupt_info {
uint32_t id;
uint32_t attributes;
+ bool mpidr_valid;
+ uint64_t mpidr;
};
/**
@@ -231,6 +233,7 @@
MANIFEST_ERROR_INVALID_MEM_PERM,
MANIFEST_ERROR_INTERRUPT_ID_REPEATED,
MANIFEST_ILLEGAL_NS_ACTION,
+ MANIFEST_ERROR_INTERRUPT_ID_NOT_IN_LIST,
};
enum manifest_return_code manifest_init(struct mm_stage1_locked stage1_locked,
diff --git a/src/arch/aarch64/plat/ffa/spmc.c b/src/arch/aarch64/plat/ffa/spmc.c
index 0159d01..3bd272c 100644
--- a/src/arch/aarch64/plat/ffa/spmc.c
+++ b/src/arch/aarch64/plat/ffa/spmc.c
@@ -1908,7 +1908,7 @@
void plat_ffa_sri_init(struct cpu *cpu)
{
- struct interrupt_descriptor sri_desc;
+ struct interrupt_descriptor sri_desc = {0};
/* TODO: when supported, make the interrupt driver use cpu structure. */
(void)cpu;
diff --git a/src/arch/aarch64/plat/interrupts/gicv3.c b/src/arch/aarch64/plat/interrupts/gicv3.c
index 3e8bde8..7e7c7ea 100644
--- a/src/arch/aarch64/plat/interrupts/gicv3.c
+++ b/src/arch/aarch64/plat/interrupts/gicv3.c
@@ -657,12 +657,22 @@
gicd_set_icfgr(plat_gicv3_driver.dist_base, intr_num, config);
}
- /* Target SPI to primary CPU using affinity routing. */
+ /*
+ * Target SPI to primary PE using affinity routing if no PE was
+ * specified in the manifest. If one was specified, target the interrupt
+ * to the corresponding PE.
+ */
if (IS_SPI(intr_num)) {
uint64_t gic_affinity_val;
- gic_affinity_val =
- gicd_irouter_val_from_mpidr(read_msr(MPIDR_EL1), 0U);
+ if (interrupt_desc_get_mpidr_valid(int_desc)) {
+ gic_affinity_val = gicd_irouter_val_from_mpidr(
+ interrupt_desc_get_mpidr(int_desc),
+ GICV3_IRM_PE);
+ } else {
+ gic_affinity_val = gicd_irouter_val_from_mpidr(
+ read_msr(MPIDR_EL1), 0U);
+ }
gicd_write_irouter(plat_gicv3_driver.dist_base, intr_num,
gic_affinity_val);
}
diff --git a/src/load.c b/src/load.c
index af3519a..0e277e6 100644
--- a/src/load.c
+++ b/src/load.c
@@ -143,6 +143,12 @@
(((attr >> INT_DESC_CONFIG_SHIFT) & 0x1) << 1) |
((attr >> INT_DESC_SEC_STATE_SHIFT) & 0x1));
+ if (interrupt.mpidr_valid) {
+ interrupt_desc_set_mpidr(int_desc, interrupt.mpidr);
+ } else {
+ interrupt_desc_set_mpidr_invalid(int_desc);
+ }
+
interrupt_desc_set_valid(int_desc, true);
}
@@ -170,7 +176,7 @@
PARTITION_MAX_INTERRUPTS_PER_DEVICE);
for (uint8_t j = 0; j < dev_region.interrupt_count; j++) {
- struct interrupt_descriptor int_desc;
+ struct interrupt_descriptor int_desc = {0};
interrupt = dev_region.interrupts[j];
infer_interrupt(interrupt, &int_desc);
diff --git a/src/manifest.c b/src/manifest.c
index 8570148..2eeaf98 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -538,6 +538,17 @@
return MANIFEST_SUCCESS;
}
+static struct interrupt_info *device_region_get_interrupt_info(
+ struct device_region *dev_regions, uint32_t intid)
+{
+ for (uint32_t i = 0; i < ARRAY_SIZE(dev_regions->interrupts); i++) {
+ if (dev_regions->interrupts[i].id == intid) {
+ return &(dev_regions->interrupts[i]);
+ }
+ }
+ return NULL;
+}
+
static enum manifest_return_code parse_ffa_device_region_node(
struct fdt_node *dev_node, struct device_region *dev_regions,
uint16_t *count)
@@ -629,6 +640,9 @@
return MANIFEST_ERROR_MALFORMED_INTEGER_LIST;
}
+ dev_regions[i].interrupts[j].mpidr_valid = false;
+ dev_regions[i].interrupts[j].mpidr = 0;
+
dlog_verbose(" attributes = %u\n",
dev_regions[i].interrupts[j].attributes);
j++;
@@ -637,6 +651,46 @@
dev_regions[i].interrupt_count = j;
if (j == 0) {
dlog_verbose(" Empty\n");
+ } else {
+ TRY(read_optional_uint32list(
+ dev_node, "interrupts-target", &list));
+ dlog_verbose(" Interrupt Target List:\n");
+
+ while (uint32list_has_next(&list)) {
+ uint32_t intid;
+ uint64_t mpidr = 0;
+ uint32_t mpidr_lower = 0;
+ uint32_t mpidr_upper = 0;
+ struct interrupt_info *info = NULL;
+
+ TRY(uint32list_get_next(&list, &intid));
+
+ dlog_verbose(" ID = %u\n", intid);
+
+ if (interrupt_bitmap_get_value(
+ &allocated_intids, intid) != 1U) {
+ return MANIFEST_ERROR_INTERRUPT_ID_NOT_IN_LIST;
+ }
+
+ TRY(uint32list_get_next(&list, &mpidr_upper));
+ TRY(uint32list_get_next(&list, &mpidr_lower));
+ mpidr = mpidr_upper;
+ mpidr <<= 32;
+ mpidr |= mpidr_lower;
+
+ info = device_region_get_interrupt_info(
+ &dev_regions[i], intid);
+ /*
+ * We should find info since
+ * interrupt_bitmap_get_value already ensures
+ * that we saw the interrupt and allocated ids
+ * for it.
+ */
+ assert(info != NULL);
+ info->mpidr = mpidr;
+ info->mpidr_valid = true;
+ dlog_verbose(" MPIDR = %#x\n", mpidr);
+ }
}
TRY(read_optional_uint32(dev_node, "smmu-id",
@@ -1191,6 +1245,8 @@
case MANIFEST_ILLEGAL_NS_ACTION:
return "Illegal value specidied for the field: Action in "
"response to NS Interrupt";
+ case MANIFEST_ERROR_INTERRUPT_ID_NOT_IN_LIST:
+ return "Interrupt ID is not in the list of interrupts";
}
panic("Unexpected manifest return code.");
diff --git a/src/manifest_test.cc b/src/manifest_test.cc
index 77e69ff..b3807ea 100644
--- a/src/manifest_test.cc
+++ b/src/manifest_test.cc
@@ -1389,4 +1389,77 @@
ASSERT_EQ(vm->partition.dev_regions[1].attributes, (8 | 1));
}
+TEST_F(manifest, ffa_valid_interrupt_target_manifest)
+{
+ struct manifest_vm *vm;
+ struct_manifest *m;
+
+ /* clang-format off */
+ std::vector<char> dtb = ManifestDtBuilder()
+ .FfaValidManifest()
+ .StartChild("device-regions")
+ .Compatible({ "arm,ffa-manifest-device-regions" })
+ .StartChild("test-device")
+ .Description("test-device")
+ .Property("base-address", "<0x7400000>")
+ .Property("pages-count", "<16>")
+ .Property("attributes", "<3>")
+ .Property("smmu-id", "<1>")
+ .Property("stream-ids", "<0 1>")
+ .Property("interrupts", "<2 3>, <4 5>")
+ .Property("interrupts-target", "<2 0x1234 0x5678>, <4 0x12345678 0x87654321>")
+ .EndChild()
+ .EndChild()
+ .Build();
+ /* clang-format on */
+
+ ASSERT_EQ(ffa_manifest_from_vec(&m, dtb), MANIFEST_SUCCESS);
+
+ vm = &m->vm[0];
+
+ ASSERT_EQ(vm->partition.dev_regions[0].base_address, 0x7400000);
+ ASSERT_EQ(vm->partition.dev_regions[0].page_count, 16);
+ ASSERT_EQ(vm->partition.dev_regions[0].attributes, 3);
+ ASSERT_EQ(vm->partition.dev_regions[0].smmu_id, 1);
+ ASSERT_EQ(vm->partition.dev_regions[0].stream_ids[0], 0);
+ ASSERT_EQ(vm->partition.dev_regions[0].stream_ids[1], 1);
+ ASSERT_EQ(vm->partition.dev_regions[0].interrupts[0].id, 2);
+ ASSERT_EQ(vm->partition.dev_regions[0].interrupts[0].attributes, 3);
+ ASSERT_EQ(vm->partition.dev_regions[0].interrupts[0].mpidr_valid, true);
+ ASSERT_EQ(vm->partition.dev_regions[0].interrupts[0].mpidr,
+ 0x123400005678);
+ ASSERT_EQ(vm->partition.dev_regions[0].interrupts[1].id, 4);
+ ASSERT_EQ(vm->partition.dev_regions[0].interrupts[1].attributes, 5);
+ ASSERT_EQ(vm->partition.dev_regions[0].interrupts[1].mpidr_valid, true);
+ ASSERT_EQ(vm->partition.dev_regions[0].interrupts[1].mpidr,
+ 0x1234567887654321);
+}
+
+TEST_F(manifest, ffa_invalid_interrupt_target_manifest)
+{
+ struct_manifest *m;
+
+ /* clang-format off */
+ std::vector<char> dtb = ManifestDtBuilder()
+ .FfaValidManifest()
+ .StartChild("device-regions")
+ .Compatible({ "arm,ffa-manifest-device-regions" })
+ .StartChild("test-device")
+ .Description("test-device")
+ .Property("base-address", "<0x7400000>")
+ .Property("pages-count", "<16>")
+ .Property("attributes", "<3>")
+ .Property("smmu-id", "<1>")
+ .Property("stream-ids", "<0 1>")
+ .Property("interrupts", "<2 3>, <4 5>")
+ .Property("interrupts-target", "<20 0x1234 0x5678>")
+ .EndChild()
+ .EndChild()
+ .Build();
+ /* clang-format on */
+
+ ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
+ MANIFEST_ERROR_INTERRUPT_ID_NOT_IN_LIST);
+}
+
} /* namespace */