PSA FF-A: add device-region parsing support
This patch implements support for parsing device-regions described in
Section 3.1, Table 11 of PSA FFA EAC specification.
A maximum of 8 device-regions can be defined for a given partition.
Change-Id: I671b727a771aaa348b2fbb140059c7f7bedc1c60
Signed-off-by: Manish Pandey <manish.pandey2@arm.com>
diff --git a/inc/hf/manifest.h b/inc/hf/manifest.h
index fc3307b..3d8dfb2 100644
--- a/inc/hf/manifest.h
+++ b/inc/hf/manifest.h
@@ -22,6 +22,9 @@
#define SP_RTX_BUF_NAME_SIZE 10
#define SP_MAX_MEMORY_REGIONS 8
+#define SP_MAX_DEVICE_REGIONS 8
+#define SP_MAX_INTERRUPTS_PER_DEVICE 4
+#define SP_MAX_STREAMS_PER_DEVICE 4
/** Mask for getting read/write/execute permission */
#define MM_PERM_MASK 0x7
@@ -62,6 +65,33 @@
struct string name;
};
+struct interrupt {
+ uint32_t id;
+ uint32_t attributes;
+};
+
+/**
+ * Partition Device region as described in PSA FFA v1.0 spec, Table 11
+ */
+struct device_region {
+ /** Device base PA - mandatory */
+ uintptr_t base_address;
+ /** Page count - mandatory */
+ uint32_t page_count;
+ /** Memory attributes - mandatory */
+ uint32_t attributes;
+ /** List of physical interrupt ID's and their attributes - optional */
+ struct interrupt interrupts[SP_MAX_INTERRUPTS_PER_DEVICE];
+ /** SMMU ID - optional */
+ uint32_t smmu_id;
+ /** List of Stream IDs assigned to device - optional */
+ uint32_t stream_ids[SP_MAX_STREAMS_PER_DEVICE];
+ /** Exclusive access to an endpoint - optional */
+ bool exclusive_access;
+ /** Name of Device region - optional */
+ struct string name;
+};
+
/**
* Partition manifest as described in PSA FF-A v1.0 spec section 3.1
*/
@@ -118,6 +148,8 @@
/** Memory regions */
struct memory_region mem_regions[SP_MAX_MEMORY_REGIONS];
+ /** Device regions */
+ struct device_region dev_regions[SP_MAX_DEVICE_REGIONS];
};
/**
@@ -191,6 +223,7 @@
MANIFEST_ERROR_MALFORMED_INTEGER_LIST,
MANIFEST_ERROR_MALFORMED_BOOLEAN,
MANIFEST_ERROR_MEMORY_REGION_NODE_EMPTY,
+ MANIFEST_ERROR_DEVICE_REGION_NODE_EMPTY,
};
enum manifest_return_code manifest_init(struct mm_stage1_locked stage1_locked,
diff --git a/src/manifest.c b/src/manifest.c
index 879ed12..e5076b6 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -164,6 +164,20 @@
return MANIFEST_SUCCESS;
}
+static enum manifest_return_code read_optional_uint32(
+ const struct fdt_node *node, const char *property,
+ uint32_t default_value, uint32_t *out)
+{
+ enum manifest_return_code ret;
+
+ ret = read_uint32(node, property, out);
+ if (ret == MANIFEST_ERROR_PROPERTY_NOT_FOUND) {
+ *out = default_value;
+ return MANIFEST_SUCCESS;
+ }
+ return ret;
+}
+
static enum manifest_return_code read_uint16(const struct fdt_node *node,
const char *property,
uint16_t *out)
@@ -337,6 +351,107 @@
return MANIFEST_SUCCESS;
}
+static enum manifest_return_code parse_ffa_device_region_node(
+ struct fdt_node *dev_node, struct device_region *dev_regions)
+{
+ struct uint32list_iter list;
+ unsigned int i = 0;
+ unsigned int j = 0;
+
+ dlog_verbose(" Partition Device Regions\n");
+
+ if (!fdt_is_compatible(dev_node, "arm,ffa-manifest-device-regions")) {
+ return MANIFEST_ERROR_NOT_COMPATIBLE;
+ }
+
+ if (!fdt_first_child(dev_node)) {
+ return MANIFEST_ERROR_DEVICE_REGION_NODE_EMPTY;
+ }
+
+ do {
+ dlog_verbose(" Device Region[%u]\n", i);
+
+ TRY(read_optional_string(dev_node, "description",
+ &dev_regions[i].name));
+ dlog_verbose(" Name: %s\n",
+ string_data(&dev_regions[i].name));
+
+ TRY(read_uint64(dev_node, "base-address",
+ &dev_regions[i].base_address));
+ dlog_verbose(" Base address: %#x\n",
+ dev_regions[i].base_address);
+
+ TRY(read_uint32(dev_node, "pages-count",
+ &dev_regions[i].page_count));
+ dlog_verbose(" Pages_count: %u\n",
+ dev_regions[i].page_count);
+
+ TRY(read_uint32(dev_node, "attributes",
+ &dev_regions[i].attributes));
+ dev_regions[i].attributes =
+ (dev_regions[i].attributes & MM_PERM_MASK) | MM_MODE_D;
+ dlog_verbose(" Attributes: %u\n",
+ dev_regions[i].attributes);
+
+ TRY(read_optional_uint32list(dev_node, "interrupts", &list));
+ dlog_verbose(" Interrupt List:\n");
+ j = 0;
+ while (uint32list_has_next(&list) &&
+ j < SP_MAX_INTERRUPTS_PER_DEVICE) {
+ TRY(uint32list_get_next(
+ &list, &dev_regions[i].interrupts[j].id));
+ if (uint32list_has_next(&list)) {
+ TRY(uint32list_get_next(&list,
+ &dev_regions[i]
+ .interrupts[j]
+ .attributes));
+ } else {
+ return MANIFEST_ERROR_MALFORMED_INTEGER_LIST;
+ }
+
+ dlog_verbose(" ID = %u, attributes = %u\n",
+ dev_regions[i].interrupts[j].id,
+ dev_regions[i].interrupts[j].attributes);
+ j++;
+ }
+ if (j == 0) {
+ dlog_verbose(" Empty\n");
+ }
+
+ TRY(read_optional_uint32(dev_node, "smmu-id",
+ (uint32_t)MANIFEST_INVALID_ADDRESS,
+ &dev_regions[i].smmu_id));
+ dlog_verbose(" smmu-id: %u\n", dev_regions[i].smmu_id);
+
+ TRY(read_optional_uint32list(dev_node, "stream-ids", &list));
+ dlog_verbose(" Stream IDs assigned:\n");
+
+ j = 0;
+ while (uint32list_has_next(&list) &&
+ j < SP_MAX_STREAMS_PER_DEVICE) {
+ 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");
+ }
+
+ TRY(read_bool(dev_node, "exclusive-access",
+ &dev_regions[i].exclusive_access));
+ dlog_verbose(" Exclusive_access: %d\n",
+ dev_regions[i].exclusive_access);
+
+ i++;
+ } while (fdt_next_sibling(dev_node) && (i < SP_MAX_DEVICE_REGIONS));
+
+ dlog_verbose(" Total %u device regions found\n", i);
+
+ return MANIFEST_SUCCESS;
+}
+
static enum manifest_return_code parse_ffa_manifest(struct fdt *fdt,
struct manifest_vm *vm)
{
@@ -347,6 +462,7 @@
struct fdt_node ffa_node;
struct string rxtx_node_name = STRING_INIT("rx_tx-info");
struct string mem_region_node_name = STRING_INIT("memory-regions");
+ struct string dev_region_node_name = STRING_INIT("device-regions");
if (!fdt_find_node(fdt, "/", &root)) {
return MANIFEST_ERROR_NO_ROOT_NODE;
@@ -424,6 +540,13 @@
vm->sp.mem_regions));
}
+ /* Parse Device-regions */
+ ffa_node = root;
+ if (fdt_find_child(&ffa_node, &dev_region_node_name)) {
+ TRY(parse_ffa_device_region_node(&ffa_node,
+ vm->sp.dev_regions));
+ }
+
return MANIFEST_SUCCESS;
}
@@ -683,6 +806,8 @@
return "Malformed boolean property";
case MANIFEST_ERROR_MEMORY_REGION_NODE_EMPTY:
return "Memory-region node should have at least one entry";
+ case MANIFEST_ERROR_DEVICE_REGION_NODE_EMPTY:
+ return "Device-region node should have at least one entry";
}
panic("Unexpected manifest return code.");
diff --git a/src/manifest_test.cc b/src/manifest_test.cc
index fcd109e..d58cb70 100644
--- a/src/manifest_test.cc
+++ b/src/manifest_test.cc
@@ -861,6 +861,71 @@
MANIFEST_ERROR_PROPERTY_NOT_FOUND);
}
+TEST(manifest, ffa_validate_dev_regions)
+{
+ struct manifest m;
+
+ /* Not Compatible */
+ /* clang-format off */
+ std::vector<char> dtb = ManifestDtBuilder()
+ .FfaValidManifest()
+ .StartChild("device-regions")
+ .Compatible({ "foo,bar" })
+ .EndChild()
+ .Build();
+ /* clang-format on */
+ ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
+ MANIFEST_ERROR_NOT_COMPATIBLE);
+
+ /* Memory regions unavailable */
+ /* clang-format off */
+ dtb = ManifestDtBuilder()
+ .FfaValidManifest()
+ .StartChild("device-regions")
+ .Compatible({ "arm,ffa-manifest-device-regions" })
+ .EndChild()
+ .Build();
+ /* clang-format on */
+ ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
+ MANIFEST_ERROR_DEVICE_REGION_NODE_EMPTY);
+
+ /* Missing Properties */
+ /* clang-format off */
+ dtb = ManifestDtBuilder()
+ .FfaValidManifest()
+ .StartChild("device-regions")
+ .Compatible({ "arm,ffa-manifest-device-regions" })
+ .StartChild("test-device")
+ .Description("test-device")
+ .EndChild()
+ .EndChild()
+ .Build();
+ /* clang-format on */
+ ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
+ MANIFEST_ERROR_PROPERTY_NOT_FOUND);
+
+ /* Malformed interrupt list pair */
+ /* clang-format off */
+ dtb = ManifestDtBuilder()
+ .FfaValidManifest()
+ .StartChild("device-regions")
+ .Compatible({ "arm,ffa-manifest-device-regions" })
+ .StartChild("test-device")
+ .Description("test-device")
+ .Property("base-address", "<0x7200000>")
+ .Property("pages-count", "<16>")
+ .Property("attributes", "<3>")
+ .Property("smmu-id", "<1>")
+ .Property("stream-ids", "<0 1>")
+ .Property("interrupts", "<2 3>, <4>")
+ .EndChild()
+ .EndChild()
+ .Build();
+ /* clang-format on */
+ ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
+ MANIFEST_ERROR_MALFORMED_INTEGER_LIST);
+}
+
TEST(manifest, ffa_valid)
{
struct manifest m;
@@ -877,6 +942,18 @@
.Property("attributes", "<7>")
.EndChild()
.EndChild()
+ .StartChild("device-regions")
+ .Compatible({ "arm,ffa-manifest-device-regions" })
+ .StartChild("test-device")
+ .Description("test-device")
+ .Property("base-address", "<0x7200000>")
+ .Property("pages-count", "<16>")
+ .Property("attributes", "<3>")
+ .Property("smmu-id", "<1>")
+ .Property("stream-ids", "<0 1>")
+ .Property("interrupts", "<2 3>, <4 5>")
+ .EndChild()
+ .EndChild()
.Build();
/* clang-format on */
@@ -896,6 +973,17 @@
ASSERT_EQ(m.vm[0].sp.mem_regions[0].base_address, 0x7100000);
ASSERT_EQ(m.vm[0].sp.mem_regions[0].page_count, 4);
ASSERT_EQ(m.vm[0].sp.mem_regions[0].attributes, 7);
+ ASSERT_EQ(m.vm[0].sp.dev_regions[0].base_address, 0x7200000);
+ ASSERT_EQ(m.vm[0].sp.dev_regions[0].page_count, 16);
+ /* Attribute is ORed with MM_MODE_D */
+ ASSERT_EQ(m.vm[0].sp.dev_regions[0].attributes, (3 | 8));
+ ASSERT_EQ(m.vm[0].sp.dev_regions[0].smmu_id, 1);
+ ASSERT_EQ(m.vm[0].sp.dev_regions[0].stream_ids[0], 0);
+ ASSERT_EQ(m.vm[0].sp.dev_regions[0].stream_ids[1], 1);
+ ASSERT_EQ(m.vm[0].sp.dev_regions[0].interrupts[0].id, 2);
+ ASSERT_EQ(m.vm[0].sp.dev_regions[0].interrupts[0].attributes, 3);
+ ASSERT_EQ(m.vm[0].sp.dev_regions[0].interrupts[1].id, 4);
+ ASSERT_EQ(m.vm[0].sp.dev_regions[0].interrupts[1].attributes, 5);
}
} /* namespace */