feat: track dma devices and map to stream ids
In order to enforce static dma isolation, SPMC would have to maintain
a unique set of page tables for each dma device. This is done partly
by tracking each dma device declared across memory region nodes.
Correspondingly, the stream ids belonging to each device must be
mapped to an implementation defined field tracking the device.
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
Change-Id: I7ab0104b9223bcf920003d431ee7d20f07d12a45
diff --git a/inc/hf/ffa_partition_manifest.h b/inc/hf/ffa_partition_manifest.h
index 75a85cb..cef1bed 100644
--- a/inc/hf/ffa_partition_manifest.h
+++ b/inc/hf/ffa_partition_manifest.h
@@ -117,6 +117,8 @@
uint8_t interrupt_count;
/** SMMU ID - optional */
uint32_t smmu_id;
+ /** IMPDEF id tracking DMA peripheral device - optional */
+ uint8_t dma_device_id;
/** Count of Stream IDs assigned to device - optional */
uint8_t stream_count;
/** List of Stream IDs assigned to device - optional */
@@ -212,6 +214,9 @@
/** Device regions */
uint16_t dev_region_count;
struct device_region dev_regions[PARTITION_MAX_DEVICE_REGIONS];
+ /** DMA device count. */
+ uint8_t dma_device_count;
+
/** optional - action in response to Other-Secure interrupt */
uint8_t other_s_interrupts_action;
};
diff --git a/src/manifest.c b/src/manifest.c
index 581d3be..abde808 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -788,12 +788,13 @@
static enum manifest_return_code parse_ffa_device_region_node(
struct fdt_node *dev_node, struct device_region *dev_regions,
- uint16_t *count)
+ uint16_t *count, uint8_t *dma_device_count)
{
struct uint32list_iter list;
uint16_t i = 0;
uint32_t j = 0;
struct interrupt_bitmap allocated_intids = manifest_data->intids;
+ static uint8_t dma_device_id = 0;
dlog_verbose(" Partition Device Regions\n");
@@ -805,6 +806,8 @@
return MANIFEST_ERROR_DEVICE_REGION_NODE_EMPTY;
}
+ *dma_device_count = 0;
+
do {
dlog_verbose(" Device Region[%u]\n", i);
@@ -942,17 +945,34 @@
dlog_verbose(" Stream IDs assigned:\n");
j = 0;
- while (uint32list_has_next(&list) &&
- j < PARTITION_MAX_STREAMS_PER_DEVICE) {
+ while (uint32list_has_next(&list)) {
+ if (j == PARTITION_MAX_STREAMS_PER_DEVICE) {
+ return MANIFEST_ERROR_STREAM_IDS_OVERFLOW;
+ }
+
TRY(uint32list_get_next(&list,
&dev_regions[i].stream_ids[j]));
dlog_verbose(" %u\n",
dev_regions[i].stream_ids[j]);
j++;
}
+
if (j == 0) {
dlog_verbose(" None\n");
+ } else if (dev_regions[i].smmu_id != MANIFEST_INVALID_ID) {
+ dev_regions[i].dma_device_id = dma_device_id++;
+ *dma_device_count = dma_device_id;
+
+ dlog_verbose(" dma peripheral device id: %u\n",
+ dev_regions[i].dma_device_id);
+ } else {
+ /*
+ * SMMU ID must be specified if the partition specifies
+ * Stream IDs for any device upstream of SMMU.
+ */
+ return MANIFEST_ERROR_MISSING_SMMU_ID;
}
+
dev_regions[i].stream_count = j;
TRY(read_bool(dev_node, "exclusive-access",
@@ -1077,6 +1097,60 @@
return ret_code;
}
+/**
+ * Find the device id allocated to the device region node corresponding to the
+ * specified stream id.
+ */
+static bool find_dma_device_id_from_dev_region_nodes(
+ const struct manifest_vm *manifest_vm, uint32_t sid, uint8_t *device_id)
+{
+ for (uint16_t i = 0; i < manifest_vm->partition.dev_region_count; i++) {
+ struct device_region dev_region =
+ manifest_vm->partition.dev_regions[i];
+
+ for (uint8_t j = 0; j < dev_region.stream_count; j++) {
+ if (sid == dev_region.stream_ids[j]) {
+ *device_id = dev_region.dma_device_id;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * Identify the device id of a DMA device node corresponding to a stream id
+ * specified in the memory region node.
+ */
+static bool map_dma_device_id_to_stream_ids(struct manifest_vm *vm)
+{
+ for (uint16_t i = 0; i < vm->partition.mem_region_count; i++) {
+ struct memory_region mem_region = vm->partition.mem_regions[i];
+
+ for (uint8_t j = 0; j < mem_region.dma_prop.stream_count; j++) {
+ uint32_t sid = mem_region.dma_prop.stream_ids[j];
+ uint8_t device_id = 0;
+
+ /*
+ * Every stream id must have been declared in the
+ * device node as well.
+ */
+ if (!find_dma_device_id_from_dev_region_nodes(
+ vm, sid, &device_id)) {
+ dlog_verbose(
+ "Stream ID not found in any device "
+ "region node of partition manifest\n",
+ sid);
+ return false;
+ }
+
+ mem_region.dma_prop.dma_device_id = device_id;
+ }
+ }
+
+ return true;
+}
+
enum manifest_return_code parse_ffa_manifest(
struct fdt *fdt, struct manifest_vm *vm,
struct fdt_node *boot_info_node, const struct boot_params *boot_params)
@@ -1319,11 +1393,16 @@
if (fdt_find_child(&ffa_node, &dev_region_node_name)) {
TRY(parse_ffa_device_region_node(
&ffa_node, vm->partition.dev_regions,
- &vm->partition.dev_region_count));
+ &vm->partition.dev_region_count,
+ &vm->partition.dma_device_count));
}
dlog_verbose(" Total %u device regions found\n",
vm->partition.dev_region_count);
+ if (!map_dma_device_id_to_stream_ids(vm)) {
+ return MANIFEST_ERROR_NOT_COMPATIBLE;
+ }
+
return sanity_check_ffa_manifest(vm);
}