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