feat(tl): package SPs in TL

In addition to the SP Pkg, allow for SPs to be loaded
as Transfer Lists.

Signed-off-by: J-Alves <joao.alves@arm.com>
Change-Id: Ic0297aae7b543c8ba91bbb866efae60db82de377
diff --git a/src/partition_pkg.c b/src/partition_pkg.c
index 093de43..9aa1ccd 100644
--- a/src/partition_pkg.c
+++ b/src/partition_pkg.c
@@ -76,6 +76,116 @@
 	return ret;
 }
 
+/**
+ * It creates a memory range structure which relates to the region of the
+ * TE data.
+ * It returns false if there is no TE entry with the specified type.
+ * It returns true, if there is a TE entry with the specified type, and
+ * returns the memory range via `mem_range`.
+ */
+static bool partition_pkg_init_memory_range_from_te(
+	struct mem_range *mem_range, struct transfer_list_entry *te)
+{
+	void *te_data;
+
+	assert(mem_range != NULL);
+
+	te_data = transfer_list_entry_data(te);
+
+	if (te == NULL || te_data == NULL) {
+		mem_range->begin = pa_init(0);
+		mem_range->end = pa_init(0);
+		return false;
+	}
+
+	mem_range->begin = pa_from_va(va_init((uintptr_t)te_data));
+	mem_range->end = pa_add(mem_range->begin, te->data_size);
+
+	return true;
+}
+
+static bool partition_pkg_from_tl(struct mm_stage1_locked stage1_locked,
+				  paddr_t pkg_start, struct partition_pkg *pkg,
+				  struct mpool *ppool)
+{
+	struct transfer_list_header *tl = ptr_from_va(va_from_pa(pkg_start));
+	enum transfer_list_ops tl_res;
+
+	dlog_verbose("%s: partition loaded in a transfer list.\n", __func__);
+
+	/* The total memory for the partition package. */
+	pkg->total.begin = pkg_start;
+	pkg->total.end = pa_add(pkg_start, tl->size);
+
+	/* Map the whole TL as RO. */
+	CHECK(mm_identity_map(stage1_locked, pkg->total.begin, pkg->total.end,
+			      MM_MODE_R, ppool));
+
+	tl_res = transfer_list_check_header(tl);
+
+	if (tl_res == TL_OPS_NON || tl_res == TL_OPS_CUS) {
+		return false;
+	}
+
+	/*
+	 * Get the memory ranges from the TL for:
+	 * - FFA_MANIFEST.
+	 * - Partition Image.
+	 */
+	if (!partition_pkg_init_memory_range_from_te(
+		    &(pkg->pm),
+		    transfer_list_find(tl, TL_TAG_DT_FFA_MANIFEST)) ||
+	    !partition_pkg_init_memory_range_from_te(
+		    &(pkg->img),
+		    transfer_list_find(tl, TL_TAG_FFA_SP_BINARY))) {
+		return false;
+	}
+
+	/* An HOB entry is optional. */
+	partition_pkg_init_memory_range_from_te(
+		&(pkg->hob), transfer_list_find(tl, TL_TAG_HOB_LIST));
+
+	if (!mem_range_aligns(pkg->pm, PAGE_SIZE)) {
+		dlog_error(
+			"%s: the partition manifest range must be 4k page "
+			"aligned.\n",
+			__func__);
+		return false;
+	}
+
+	if (!mem_range_aligns(pkg->img, PAGE_SIZE)) {
+		dlog_error(
+			"%s: the partition image range must be 4k page "
+			"aligned.\n",
+			__func__);
+		return false;
+	}
+
+	if (mem_range_is_valid(pkg->hob) &&
+	    !mem_range_aligns(pkg->hob, PAGE_SIZE)) {
+		dlog_error("%s: the hob range must be 4k page aligned.\n",
+			   __func__);
+		return false;
+	}
+
+	/*
+	 * For the boot information descriptor, repurpose the TL's first page.
+	 * The TL is only processed by Hafnium, and all items are placed at
+	 * a page aligned offset.
+	 * At this point, all references to artefacts in the TL have been
+	 * obtained so the first page of the package can be repurposed to the
+	 * FF-A boot information. There is no expectation that the boot info
+	 * descriptors will need more than a page for the time being. If it does
+	 * get full, Hafnium will fail at populating the boot info descriptors.
+	 */
+	pkg->boot_info.begin = pkg_start;
+	pkg->boot_info.end = pa_add(pkg_start, PAGE_SIZE);
+
+	dump_partition_package(pkg);
+
+	return true;
+}
+
 bool partition_pkg_init(struct mm_stage1_locked stage1_locked,
 			paddr_t pkg_start, struct partition_pkg *pkg,
 			struct mpool *ppool)
@@ -104,6 +214,12 @@
 			goto out;
 		}
 		break;
+	case TRANSFER_LIST_SIGNATURE:
+		if (!partition_pkg_from_tl(stage1_locked, pkg_start, pkg,
+					   ppool)) {
+			goto out;
+		}
+		break;
 	default:
 		dlog_error("%s: invalid secure partition package %x @ %lx\n",
 			   __func__, *magic, (uint64_t)magic);