feat(boot): writing boot info to partition's memory

Implemented writing VM's boot info:
- Setting boot info header and descriptors array.
- Copying info if it has been listed in the partition's manifest.
- Providing the boot info memory address to partition in the GP register
specified in the partition's manifest.

Change-Id: I39248811289c6b8c4f4cac6080c600ce73cad993
Signed-off-by: J-Alves <joao.alves@arm.com>
diff --git a/inc/hf/sp_pkg.h b/inc/hf/sp_pkg.h
index 5e5f545..0ae763c 100644
--- a/inc/hf/sp_pkg.h
+++ b/inc/hf/sp_pkg.h
@@ -41,6 +41,12 @@
 	return (size_t)(sp_pkg->img_offset + sp_pkg->img_size);
 }
 
+/** Get the size of the boot information descriptors section. */
+static inline size_t sp_pkg_get_boot_info_size(struct sp_pkg_header *sp_pkg)
+{
+	return sp_pkg->pm_offset;
+}
+
 bool sp_pkg_init(struct mm_stage1_locked stage1_locked, paddr_t pkg_start,
 		 struct sp_pkg_header *header, struct mpool *ppool);
 
diff --git a/src/arch/aarch64/plat/ffa/spmc.c b/src/arch/aarch64/plat/ffa/spmc.c
index 82428de..53651d4 100644
--- a/src/arch/aarch64/plat/ffa/spmc.c
+++ b/src/arch/aarch64/plat/ffa/spmc.c
@@ -1440,10 +1440,10 @@
 			     current_vm_locked.vm->id,
 			     current_vm_locked.vm->boot_order);
 
-		if (current_vm_locked.vm->next_boot != NULL) {
+		vm_next = current_vm_locked.vm->next_boot;
+		if (vm_next != NULL) {
 			/* Refer FF-A v1.1 Beta0 section 7.5 Rule 2. */
 			current->state = VCPU_STATE_WAITING;
-			vm_next = current_vm_locked.vm->next_boot;
 			CHECK(vm_next->initialized == false);
 			*next = vm_get_vcpu(vm_next, vcpu_index(current));
 			arch_regs_reset(*next);
@@ -1451,6 +1451,8 @@
 			(*next)->state = VCPU_STATE_RUNNING;
 			(*next)->regs_available = false;
 
+			vm_set_boot_info_gp_reg(vm_next, (*next));
+
 			ret = true;
 			goto out;
 		}
diff --git a/src/boot_info.c b/src/boot_info.c
index 2b65dee..89611ad 100644
--- a/src/boot_info.c
+++ b/src/boot_info.c
@@ -10,12 +10,84 @@
 
 #include "hf/assert.h"
 #include "hf/dlog.h"
+#include "hf/ffa.h"
 #include "hf/memiter.h"
 #include "hf/std.h"
 
 #include "vmapi/hf/ffa.h"
 
 /**
+ * Initializes the ffa_boot_info_header in accordance to the specification.
+ */
+static void ffa_boot_info_header_init(struct ffa_boot_info_header *header,
+				      size_t blob_size)
+{
+	assert(header != NULL);
+	assert(blob_size != 0U);
+
+	header->signature = FFA_BOOT_INFO_SIG;
+	header->version = FFA_BOOT_INFO_VERSION;
+	header->info_blob_size = blob_size;
+	header->desc_size = sizeof(struct ffa_boot_info_desc);
+	header->desc_count = 0;
+	header->desc_offset =
+		(uint32_t)offsetof(struct ffa_boot_info_header, boot_info);
+	header->reserved = 0U;
+}
+
+static void ffa_boot_info_desc_init(struct ffa_boot_info_desc *info_desc,
+				    uint8_t content_format, bool std_type,
+				    uint8_t type_id, uint32_t size,
+				    uint64_t content)
+{
+	assert(info_desc != NULL);
+
+	/*
+	 * Init name size with 0s, as it is currently unused. Data can be
+	 * identified checking the type field.
+	 */
+	memset_s(info_desc, FFA_BOOT_INFO_NAME_LEN, 0, FFA_BOOT_INFO_NAME_LEN);
+
+	info_desc->type = std_type == true ? FFA_BOOT_INFO_TYPE_STD
+					   : FFA_BOOT_INFO_TYPE_IMPDEF;
+	info_desc->type <<= FFA_BOOT_INFO_TYPE_SHIFT;
+	info_desc->type |= (type_id & FFA_BOOT_INFO_TYPE_ID_MASK);
+
+	info_desc->reserved = 0U;
+	info_desc->flags =
+		((content_format << FFA_BOOT_INFO_FLAG_CONTENT_FORMAT_SHIFT) &
+		 FFA_BOOT_INFO_FLAG_CONTENT_FORMAT_MASK);
+	info_desc->size = size;
+	info_desc->content = content;
+}
+
+/*
+ * Write initialization parameter to the boot info descriptor array.
+ */
+static void boot_info_write_desc(struct ffa_boot_info_header *header,
+				 uint8_t content_format, bool std_type,
+				 uint8_t type_id, uint32_t size,
+				 uint64_t content,
+				 const size_t max_info_desc_count)
+{
+	assert(header != NULL);
+
+	/* Check that writing the data won't surpass the blob memory limit. */
+	if (header->desc_count >= max_info_desc_count) {
+		dlog_error(
+			"Boot info memory is full. No space for a "
+			"descriptor.\n");
+		return;
+	}
+
+	ffa_boot_info_desc_init(&header->boot_info[header->desc_count],
+				content_format, std_type, type_id, size,
+				content);
+
+	header->desc_count++;
+}
+
+/**
  * Looks for the FF-A manifest boot information node, and writes the
  * requested information into the boot info memory.
  */
@@ -23,12 +95,26 @@
 			struct sp_pkg_header *pkg_header)
 {
 	struct memiter data;
+	struct ffa_boot_info_header *boot_info_header =
+		(struct ffa_boot_info_header *)ptr_from_va(pkg_address);
+	const size_t boot_info_size = sp_pkg_get_boot_info_size(pkg_header);
+	const size_t max_boot_info_desc_count =
+		boot_info_size / sizeof(struct ffa_boot_info_desc);
 
 	assert(boot_info_node != NULL);
 	assert(pkg_header != NULL);
+	assert(boot_info_header != NULL);
 
-	(void)pkg_address;
-	(void)pkg_header;
+	/*
+	 * FF-A v1.1 EAC0 specification states the region for the boot info
+	 * descriptors, and the contents of the boot info shall be contiguous.
+	 * Together they constitute the boot info blob. The are for the boot
+	 * info blob is allocated in the SP's respective package.
+	 * Retrieve from the SP package the size of the region for the boot info
+	 * descriptors. The size of boot info contents to be incremented,
+	 * depending on the info specified in the partition's FF-A manifest.
+	 */
+	ffa_boot_info_header_init(boot_info_header, boot_info_size);
 
 	if (!fdt_is_compatible(boot_info_node, "arm,ffa-manifest-boot-info")) {
 		dlog_verbose("The node 'boot-info' is not compatible.\n");
@@ -39,7 +125,28 @@
 
 	if (fdt_read_property(boot_info_node, "ffa_manifest", &data) &&
 	    memiter_size(&data) == 0U) {
+		ipaddr_t manifest_address = ipa_init(
+			va_addr(va_add(pkg_address, pkg_header->pm_offset)));
+
 		dlog_verbose("    FF-A Manifest\n");
+		boot_info_write_desc(
+			boot_info_header,
+			FFA_BOOT_INFO_FLAG_CONTENT_FORMAT_ADDR, true,
+			FFA_BOOT_INFO_TYPE_ID_FDT, pkg_header->pm_size,
+			ipa_addr(manifest_address), max_boot_info_desc_count);
+
+		/*
+		 * Incrementing the size of the boot information blob with the
+		 * size of the partition's manifest.
+		 */
+		boot_info_header->info_blob_size += pkg_header->pm_size;
+
+		/*
+		 * Flush the data cache in case partition initializes with
+		 * caches disabled.
+		 */
+		arch_mm_flush_dcache((void *)boot_info_header,
+				     boot_info_header->info_blob_size);
 		return true;
 	}
 
diff --git a/src/main.c b/src/main.c
index 32e0476..45624e5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -42,5 +42,7 @@
 	/* Initialize SRI for running core. */
 	plat_ffa_sri_init(c);
 
+	vm_set_boot_info_gp_reg(first_boot, vcpu);
+
 	return vcpu;
 }
diff --git a/src/vm.c b/src/vm.c
index aa25fb8..37a4541 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -920,3 +920,16 @@
 {
 	return vm_locked.vm->notifications.npi_injected;
 }
+
+/**
+ * Sets the designated GP register that the VM expects to receive the boot
+ * info's address.
+ */
+void vm_set_boot_info_gp_reg(struct vm *vm, struct vcpu *vcpu)
+{
+	if (!vm->initialized && vm->boot_info.blob_addr.ipa != 0U) {
+		arch_regs_set_gp_reg(&vcpu->regs,
+				     ipa_addr(vm->boot_info.blob_addr),
+				     vm->boot_info.gp_register_num);
+	}
+}