1 /*
2  * Copyright (c) 2020, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 #include <errno.h>
9 #include <string.h>
10 #include <libfdt.h>
11 
12 #include <common/bl_common.h>
13 #include <common/debug.h>
14 #include <common/fdt_wrappers.h>
15 #include <lib/xlat_tables/xlat_tables_v2.h>
16 #include <platform_def.h>
17 #include <services/spm_core_manifest.h>
18 
19 #define ATTRIBUTE_ROOT_NODE_STR "attribute"
20 
21 /*******************************************************************************
22  * SPMC attribute node parser
23  ******************************************************************************/
manifest_parse_attribute(spmc_manifest_attribute_t * attr,const void * fdt,int node)24 static int manifest_parse_attribute(spmc_manifest_attribute_t *attr,
25 				    const void *fdt,
26 				    int node)
27 {
28 	uint32_t val32;
29 	int rc;
30 
31 	assert((attr != NULL) && (fdt != NULL));
32 
33 	rc = fdt_read_uint32(fdt, node, "maj_ver", &attr->major_version);
34 	if (rc != 0) {
35 		ERROR("Missing FFA %s version in SPM Core manifest.\n",
36 			"major");
37 		return rc;
38 	}
39 
40 	rc = fdt_read_uint32(fdt, node, "min_ver", &attr->minor_version);
41 	if (rc != 0) {
42 		ERROR("Missing FFA %s version in SPM Core manifest.\n",
43 			"minor");
44 		return rc;
45 	}
46 
47 	rc = fdt_read_uint32(fdt, node, "spmc_id", &val32);
48 	if (rc != 0) {
49 		ERROR("Missing SPMC ID in manifest.\n");
50 		return rc;
51 	}
52 
53 	attr->spmc_id = val32 & 0xffff;
54 
55 	rc = fdt_read_uint32(fdt, node, "exec_state", &attr->exec_state);
56 	if (rc != 0) {
57 		NOTICE("%s not specified in SPM Core manifest.\n",
58 			"Execution state");
59 	}
60 
61 	rc = fdt_read_uint32(fdt, node, "binary_size", &attr->binary_size);
62 	if (rc != 0) {
63 		NOTICE("%s not specified in SPM Core manifest.\n",
64 			"Binary size");
65 	}
66 
67 	rc = fdt_read_uint64(fdt, node, "load_address", &attr->load_address);
68 	if (rc != 0) {
69 		NOTICE("%s not specified in SPM Core manifest.\n",
70 			"Load address");
71 	}
72 
73 	rc = fdt_read_uint64(fdt, node, "entrypoint", &attr->entrypoint);
74 	if (rc != 0) {
75 		NOTICE("%s not specified in SPM Core manifest.\n",
76 			"Entry point");
77 	}
78 
79 	VERBOSE("SPM Core manifest attribute section:\n");
80 	VERBOSE("  version: %u.%u\n", attr->major_version, attr->minor_version);
81 	VERBOSE("  spmc_id: 0x%x\n", attr->spmc_id);
82 	VERBOSE("  binary_size: 0x%x\n", attr->binary_size);
83 	VERBOSE("  load_address: 0x%llx\n", attr->load_address);
84 	VERBOSE("  entrypoint: 0x%llx\n", attr->entrypoint);
85 
86 	return 0;
87 }
88 
89 /*******************************************************************************
90  * Root node handler
91  ******************************************************************************/
manifest_parse_root(spmc_manifest_attribute_t * manifest,const void * fdt,int root)92 static int manifest_parse_root(spmc_manifest_attribute_t *manifest,
93 			       const void *fdt,
94 			       int root)
95 {
96 	int node;
97 
98 	assert(manifest != NULL);
99 
100 	node = fdt_subnode_offset_namelen(fdt, root, ATTRIBUTE_ROOT_NODE_STR,
101 		sizeof(ATTRIBUTE_ROOT_NODE_STR) - 1);
102 	if (node < 0) {
103 		ERROR("Root node doesn't contain subnode '%s'\n",
104 			ATTRIBUTE_ROOT_NODE_STR);
105 		return node;
106 	}
107 
108 	return manifest_parse_attribute(manifest, fdt, node);
109 }
110 
111 /*******************************************************************************
112  * Platform handler to parse a SPM Core manifest.
113  ******************************************************************************/
plat_spm_core_manifest_load(spmc_manifest_attribute_t * manifest,const void * pm_addr)114 int plat_spm_core_manifest_load(spmc_manifest_attribute_t *manifest,
115 				const void *pm_addr)
116 {
117 	int rc, unmap_ret;
118 	uintptr_t pm_base, pm_base_align;
119 	size_t mapped_size;
120 
121 	assert(manifest != NULL);
122 	assert(pm_addr != NULL);
123 
124 	/*
125 	 * Assume TOS_FW_CONFIG is not necessarily aligned to a page
126 	 * boundary, thus calculate the remaining space between SPMC
127 	 * manifest start address and upper page limit.
128 	 *
129 	 */
130 	pm_base = (uintptr_t)pm_addr;
131 	pm_base_align = page_align(pm_base, UP);
132 
133 	if (pm_base == pm_base_align) {
134 		/* Page aligned */
135 		mapped_size = PAGE_SIZE;
136 	} else {
137 		mapped_size = pm_base_align - pm_base;
138 	}
139 
140 	/* Check space within the page at least maps the FDT header */
141 	if (mapped_size < sizeof(struct fdt_header)) {
142 		ERROR("Error while mapping SPM Core manifest.\n");
143 		return -EINVAL;
144 	}
145 
146 	/* Map first SPMC manifest page in the SPMD translation regime */
147 	pm_base_align = page_align(pm_base, DOWN);
148 	rc = mmap_add_dynamic_region((unsigned long long)pm_base_align,
149 				     pm_base_align,
150 				     PAGE_SIZE,
151 				     MT_RO_DATA);
152 	if (rc != 0) {
153 		ERROR("Error while mapping SPM Core manifest (%d).\n", rc);
154 		return rc;
155 	}
156 
157 	rc = fdt_check_header(pm_addr);
158 	if (rc != 0) {
159 		ERROR("Wrong format for SPM Core manifest (%d).\n", rc);
160 		goto exit_unmap;
161 	}
162 
163 	/* Check SPMC manifest fits within the upper mapped page boundary */
164 	if (mapped_size < fdt_totalsize(pm_addr)) {
165 		ERROR("SPM Core manifest too large.\n");
166 		rc = -EINVAL;
167 		goto exit_unmap;
168 	}
169 
170 	VERBOSE("Reading SPM Core manifest at address %p\n", pm_addr);
171 
172 	rc = fdt_node_offset_by_compatible(pm_addr, -1,
173 				"arm,ffa-core-manifest-1.0");
174 	if (rc < 0) {
175 		ERROR("Unrecognized SPM Core manifest\n");
176 		goto exit_unmap;
177 	}
178 
179 	rc = manifest_parse_root(manifest, pm_addr, rc);
180 
181 exit_unmap:
182 	unmap_ret = mmap_remove_dynamic_region(pm_base_align, PAGE_SIZE);
183 	if (unmap_ret != 0) {
184 		ERROR("Error while unmapping SPM Core manifest (%d).\n",
185 			unmap_ret);
186 		if (rc == 0) {
187 			rc = unmap_ret;
188 		}
189 	}
190 
191 	return rc;
192 }
193