PSA FF-A: map secondary VM memory/device regions in its stage-2 mapping
Add support for mapping secondary VM's memory/device regions in its
stage-2 translation along with unmapping it from primary VM as all the
regions are by default mapped in primary VM's translation regime.
As per PSA FF-A EAC spec, if memory-region does not have base-address
specified, the memory-region of given size is allocated inside the
partition's translation regime. This patch allocates memory-region at
the end of VM's memory. At the max half the VM's memory can be allocated
to memory regions.
When base-address is present this field specify a PA, VA (for S-EL0
partitions) or IPA (for S-EL1 and EL1 partitions). In both cases this
patch identity maps the region.
Change-Id: Id53e66d2109f05e76f8b8dbe85e64c84eabd5a5d
Signed-off-by: Manish Pandey <manish.pandey2@arm.com>
diff --git a/inc/hf/addr.h b/inc/hf/addr.h
index efb1844..59e1b7a 100644
--- a/inc/hf/addr.h
+++ b/inc/hf/addr.h
@@ -55,6 +55,14 @@
}
/**
+ * Move backward physical address.
+ */
+static inline paddr_t pa_subtract(paddr_t pa, size_t n)
+{
+ return pa_init(pa_addr(pa) - n);
+}
+
+/**
* Returns the difference between two physical addresses.
*/
static inline size_t pa_difference(paddr_t start, paddr_t end)
diff --git a/inc/hf/manifest.h b/inc/hf/manifest.h
index 1299c5e..dc4b7c7 100644
--- a/inc/hf/manifest.h
+++ b/inc/hf/manifest.h
@@ -149,8 +149,10 @@
uint32_t stream_ep_ids[1];
/** Memory regions */
+ uint8_t mem_region_count;
struct memory_region mem_regions[SP_MAX_MEMORY_REGIONS];
/** Device regions */
+ uint8_t dev_region_count;
struct device_region dev_regions[SP_MAX_DEVICE_REGIONS];
};
diff --git a/src/load.c b/src/load.c
index 8962d65..26a9641 100644
--- a/src/load.c
+++ b/src/load.c
@@ -307,6 +307,7 @@
* Loads a secondary VM.
*/
static bool load_secondary(struct mm_stage1_locked stage1_locked,
+ struct vm_locked primary_vm_locked,
paddr_t mem_begin, paddr_t mem_end,
const struct manifest_vm *manifest_vm,
const struct memiter *cpio, struct mpool *ppool)
@@ -380,14 +381,132 @@
goto out;
}
- dlog_info("Loaded with %u vCPUs, entry at %#x.\n",
- manifest_vm->secondary.vcpu_count, pa_addr(mem_begin));
-
if (manifest_vm->is_ffa_partition) {
+ int j = 0;
+ paddr_t region_begin;
+ paddr_t region_end;
+ paddr_t alloc_base = mem_end;
+ size_t size;
+ size_t total_alloc = 0;
+
+ /* Map memory-regions */
+ while (j < manifest_vm->sp.mem_region_count) {
+ size = manifest_vm->sp.mem_regions[j].page_count *
+ PAGE_SIZE;
+ /*
+ * For memory-regions without base-address, memory
+ * should be allocated inside partition's page table.
+ * Start allocating memory regions in partition's
+ * page table, starting from the end.
+ * TODO: Add mechanism to let partition know of these
+ * memory regions
+ */
+ if (manifest_vm->sp.mem_regions[j].base_address ==
+ MANIFEST_INVALID_ADDRESS) {
+ total_alloc += size;
+ /* Don't go beyond half the VM's memory space */
+ if (total_alloc >
+ (manifest_vm->secondary.mem_size / 2)) {
+ dlog_error(
+ "Not enough space for memory-"
+ "region allocation");
+ ret = false;
+ goto out;
+ }
+
+ region_end = alloc_base;
+ region_begin = pa_subtract(alloc_base, size);
+ alloc_base = region_begin;
+
+ if (!vm_identity_map(
+ vm_locked, region_begin, region_end,
+ manifest_vm->sp.mem_regions[j]
+ .attributes,
+ ppool, NULL)) {
+ dlog_error(
+ "Unable to map secondary VM "
+ "memory-region.\n");
+ ret = false;
+ goto out;
+ }
+
+ dlog_info(
+ " Memory region %#x - %#x allocated\n",
+ region_begin, region_end);
+ } else {
+ /*
+ * Identity map memory region for both case,
+ * VA(S-EL0) or IPA(S-EL1).
+ */
+ region_begin =
+ pa_init(manifest_vm->sp.mem_regions[j]
+ .base_address);
+ region_end = pa_add(region_begin, size);
+
+ if (!vm_identity_map(
+ vm_locked, region_begin, region_end,
+ manifest_vm->sp.mem_regions[j]
+ .attributes,
+ ppool, NULL)) {
+ dlog_error(
+ "Unable to map secondary VM "
+ "memory-region.\n");
+ ret = false;
+ goto out;
+ }
+ }
+
+ /* Deny the primary VM access to this memory */
+ if (!vm_unmap(primary_vm_locked, region_begin,
+ region_end, ppool)) {
+ dlog_error(
+ "Unable to unmap secondary VM memory-"
+ "region from primary VM.\n");
+ ret = false;
+ goto out;
+ }
+
+ j++;
+ }
+
+ /* Map device-regions */
+ j = 0;
+ while (j < manifest_vm->sp.dev_region_count) {
+ region_begin = pa_init(
+ manifest_vm->sp.dev_regions[j].base_address);
+ size = manifest_vm->sp.dev_regions[j].page_count *
+ PAGE_SIZE;
+ region_end = pa_add(region_begin, size);
+
+ if (!vm_identity_map(
+ vm_locked, region_begin, region_end,
+ manifest_vm->sp.dev_regions[j].attributes,
+ ppool, NULL)) {
+ dlog_error(
+ "Unable to map secondary VM "
+ "device-region.\n");
+ ret = false;
+ goto out;
+ }
+ /* Deny primary VM access to this region */
+ if (!vm_unmap(primary_vm_locked, region_begin,
+ region_end, ppool)) {
+ dlog_error(
+ "Unable to unmap secondary VM device-"
+ "region from primary VM.\n");
+ ret = false;
+ goto out;
+ }
+ j++;
+ }
+
secondary_entry =
ipa_add(secondary_entry, manifest_vm->sp.ep_offset);
}
+ dlog_info("Loaded with %u vCPUs, entry at %#x.\n",
+ manifest_vm->secondary.vcpu_count, pa_addr(mem_begin));
+
vcpu = vm_get_vcpu(vm, 0);
if (has_fdt) {
@@ -565,9 +684,9 @@
continue;
}
- if (!load_secondary(stage1_locked, secondary_mem_begin,
- secondary_mem_end, manifest_vm, cpio,
- ppool)) {
+ if (!load_secondary(stage1_locked, primary_vm_locked,
+ secondary_mem_begin, secondary_mem_end,
+ manifest_vm, cpio, ppool)) {
dlog_error("Unable to load VM.\n");
continue;
}
diff --git a/src/manifest.c b/src/manifest.c
index 9c98b5f..da25156 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -306,10 +306,10 @@
static enum manifest_return_code parse_ffa_memory_region_node(
struct fdt_node *mem_node, struct memory_region *mem_regions,
- struct rx_tx *rxtx)
+ uint8_t *count, struct rx_tx *rxtx)
{
uint32_t phandle;
- unsigned int i = 0;
+ uint8_t i = 0;
dlog_verbose(" Partition memory regions\n");
@@ -360,17 +360,18 @@
i++;
} while (fdt_next_sibling(mem_node) && (i < SP_MAX_MEMORY_REGIONS));
- dlog_verbose(" Total %u memory regions found\n", i);
+ *count = i;
return MANIFEST_SUCCESS;
}
static enum manifest_return_code parse_ffa_device_region_node(
- struct fdt_node *dev_node, struct device_region *dev_regions)
+ struct fdt_node *dev_node, struct device_region *dev_regions,
+ uint8_t *count)
{
struct uint32list_iter list;
- unsigned int i = 0;
- unsigned int j = 0;
+ uint8_t i = 0;
+ uint8_t j = 0;
dlog_verbose(" Partition Device Regions\n");
@@ -461,7 +462,7 @@
i++;
} while (fdt_next_sibling(dev_node) && (i < SP_MAX_DEVICE_REGIONS));
- dlog_verbose(" Total %u device regions found\n", i);
+ *count = i;
return MANIFEST_SUCCESS;
}
@@ -552,15 +553,20 @@
ffa_node = root;
if (fdt_find_child(&ffa_node, &mem_region_node_name)) {
TRY(parse_ffa_memory_region_node(&ffa_node, vm->sp.mem_regions,
+ &vm->sp.mem_region_count,
&vm->sp.rxtx));
}
+ dlog_verbose(" Total %u memory regions found\n",
+ vm->sp.mem_region_count);
/* 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));
+ TRY(parse_ffa_device_region_node(&ffa_node, vm->sp.dev_regions,
+ &vm->sp.dev_region_count));
}
+ dlog_verbose(" Total %u device regions found\n",
+ vm->sp.dev_region_count);
return MANIFEST_SUCCESS;
}