1 /*
2 * Copyright (c) 2015-2026, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <string.h>
8
9 #include <common/debug.h>
10 #include <drivers/fwu/fwu_metadata.h>
11 #include <drivers/io/io_driver.h>
12 #include <drivers/io/io_encrypted.h>
13 #include <drivers/io/io_fip.h>
14 #include <drivers/io/io_memmap.h>
15 #include <drivers/io/io_storage.h>
16 #include <drivers/partition/partition.h>
17 #include <lib/utils.h>
18
19 #include <plat/arm/common/arm_fconf_getter.h>
20 #include <plat/arm/common/arm_fconf_io_storage.h>
21 #include <plat/arm/common/arm_gpt_partition_guid.h>
22 #include <plat/arm/common/plat_arm.h>
23 #include <plat/common/platform.h>
24 #include <platform_def.h>
25
26 /* IO devices */
27 static const io_dev_connector_t *fip_dev_con;
28 uintptr_t fip_dev_handle;
29 static const io_dev_connector_t *memmap_dev_con;
30 uintptr_t memmap_dev_handle;
31 #ifndef DECRYPTION_SUPPORT_none
32 static const io_dev_connector_t *enc_dev_con;
33 uintptr_t enc_dev_handle;
34 #endif
35
36 #if ARM_GPT_SUPPORT
37 /* fip partition names */
38 static const char * const fip_part_names[] = {"FIP_A", "FIP_B"};
39 CASSERT(sizeof(fip_part_names)/sizeof(char *) == NR_OF_FW_BANKS,
40 assert_fip_partition_names_missing);
41
42 #if PSA_FWU_SUPPORT
43 static const struct efi_guid fwu_metadata_type_guid =
44 ARM_GPT_FWU_METADATA_TYPE_GUID;
45 #if PSA_FWU_METADATA_FW_STORE_DESC
46 static const struct efi_guid fip_img_type_guid = ARM_FIP_IMG_TYPE_GUID;
47 #else
48 static const struct efi_guid fip_img_type_guid = NULL_GUID;
49 #endif /* PSA_FWU_METADATA_FW_STORE_DESC */
50 #endif /* PSA_FWU_SUPPORT */
51 static const struct efi_guid null_guid = NULL_GUID;
52 #endif /* ARM_GPT_SUPPORT */
53
54 /* Weak definitions may be overridden in specific ARM standard platform */
55 #pragma weak plat_arm_io_setup
56 #pragma weak plat_arm_get_alt_image_source
57
open_fip(const uintptr_t spec)58 int open_fip(const uintptr_t spec)
59 {
60 int result;
61 uintptr_t local_image_handle;
62
63 /* See if a Firmware Image Package is available */
64 result = io_dev_init(fip_dev_handle, (uintptr_t)FIP_IMAGE_ID);
65 if (result == 0 && spec != (uintptr_t)NULL) {
66 result = io_open(fip_dev_handle, spec, &local_image_handle);
67 if (result == 0) {
68 VERBOSE("Using FIP\n");
69 io_close(local_image_handle);
70 }
71 }
72 return result;
73 }
74
open_memmap(const uintptr_t spec)75 int open_memmap(const uintptr_t spec)
76 {
77 int result;
78 uintptr_t local_image_handle;
79
80 result = io_dev_init(memmap_dev_handle, (uintptr_t)NULL);
81 if (result == 0) {
82 result = io_open(memmap_dev_handle, spec, &local_image_handle);
83 if (result == 0) {
84 VERBOSE("Using Memmap\n");
85 io_close(local_image_handle);
86 }
87 }
88 return result;
89 }
90
arm_io_setup(void)91 int arm_io_setup(void)
92 {
93 int io_result;
94
95 io_result = register_io_dev_fip(&fip_dev_con);
96 if (io_result < 0) {
97 return io_result;
98 }
99
100 io_result = register_io_dev_memmap(&memmap_dev_con);
101 if (io_result < 0) {
102 return io_result;
103 }
104
105 /* Open connections to devices and cache the handles */
106 io_result = io_dev_open(fip_dev_con, (uintptr_t)NULL,
107 &fip_dev_handle);
108 if (io_result < 0) {
109 return io_result;
110 }
111
112 io_result = io_dev_open(memmap_dev_con, (uintptr_t)NULL,
113 &memmap_dev_handle);
114
115 if (io_result < 0) {
116 return io_result;
117 }
118
119 #ifndef DECRYPTION_SUPPORT_none
120 io_result = register_io_dev_enc(&enc_dev_con);
121 if (io_result < 0) {
122 return io_result;
123 }
124
125 io_result = io_dev_open(enc_dev_con, (uintptr_t)NULL,
126 &enc_dev_handle);
127 if (io_result < 0) {
128 return io_result;
129 }
130 #endif
131
132 return io_result;
133 }
134
plat_arm_io_setup(void)135 void plat_arm_io_setup(void)
136 {
137 int err;
138
139 err = arm_io_setup();
140 if (err < 0) {
141 panic();
142 }
143 }
144
plat_arm_get_alt_image_source(unsigned int image_id __unused,uintptr_t * dev_handle __unused,uintptr_t * image_spec __unused)145 int plat_arm_get_alt_image_source(
146 unsigned int image_id __unused,
147 uintptr_t *dev_handle __unused,
148 uintptr_t *image_spec __unused)
149 {
150 /* By default do not try an alternative */
151 return -ENOENT;
152 }
153
154 /* Return an IO device handle and specification which can be used to access
155 * an image. Use this to enforce platform load policy */
plat_get_image_source(unsigned int image_id,uintptr_t * dev_handle,uintptr_t * image_spec)156 int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle,
157 uintptr_t *image_spec)
158 {
159 int result;
160 const struct plat_io_policy *policy;
161
162 policy = FCONF_GET_PROPERTY(arm, io_policies, image_id);
163 assert(policy->check != NULL);
164 result = policy->check(policy->image_spec);
165 if (result == 0) {
166 *image_spec = policy->image_spec;
167 *dev_handle = *(policy->dev_handle);
168 } else {
169 VERBOSE("Trying alternative IO\n");
170 result = plat_arm_get_alt_image_source(image_id, dev_handle,
171 image_spec);
172 }
173
174 return result;
175 }
176
177 /*
178 * See if a Firmware Image Package is available,
179 * by checking if TOC is valid or not.
180 */
arm_io_is_toc_valid(void)181 bool arm_io_is_toc_valid(void)
182 {
183 return (io_dev_init(fip_dev_handle, (uintptr_t)FIP_IMAGE_ID) == 0);
184 }
185
186 #if ARM_GPT_SUPPORT
187 /******************************************************************************
188 * Retrieve partition entry details such as offset and length, and set these
189 * details in the I/O policy of the requested image.
190 *
191 * @image_id: image id whose I/O policy to be updated
192 *
193 * @part_guid: partition GUID whose details to be retrieved (preferred)
194 * @part_name: partition name whose details to be retrieved (fallback)
195 *
196 * Returns 0 on success, error otherwise
197 * Alongside, returns device handle and image specification of requested
198 * image.
199 ******************************************************************************/
arm_get_partition_entry(const struct efi_guid * part_guid,const char * part_name)200 static const partition_entry_t *arm_get_partition_entry(
201 const struct efi_guid *part_guid, const char *part_name)
202 {
203 const partition_entry_t *entry = NULL;
204
205 if ((part_guid != NULL) && (guidcmp(part_guid, &null_guid) != 0)) {
206 entry = get_partition_entry_by_guid(part_guid);
207 }
208
209 if ((entry == NULL) && (part_name != NULL)) {
210 entry = get_partition_entry(part_name);
211 }
212
213 return entry;
214 }
215
arm_set_image_source(unsigned int image_id,const struct efi_guid * part_guid,const char * part_name,uintptr_t * dev_handle,uintptr_t * image_spec)216 int arm_set_image_source(unsigned int image_id, const struct efi_guid *part_guid,
217 const char *part_name, uintptr_t *dev_handle,
218 uintptr_t *image_spec)
219 {
220 const partition_entry_t *entry = arm_get_partition_entry(part_guid,
221 part_name);
222
223 if (entry == NULL) {
224 if (part_name != NULL) {
225 ERROR("Unable to find the %s partition\n", part_name);
226 } else {
227 ERROR("Unable to find partition by GUID\n");
228 }
229 return -ENOENT;
230 }
231
232 struct plat_io_policy *policy = FCONF_GET_PROPERTY(arm,
233 io_policies,
234 image_id);
235
236 assert(policy != NULL);
237 assert(policy->image_spec != 0UL);
238
239 io_block_spec_t *spec = (io_block_spec_t *)policy->image_spec;
240 /* set offset and length of the image */
241 spec->offset = PLAT_ARM_FLASH_IMAGE_BASE + entry->start;
242 spec->length = entry->length;
243
244 *dev_handle = *(policy->dev_handle);
245 *image_spec = policy->image_spec;
246
247 return 0;
248 }
249
250 /*******************************************************************************
251 * Set the source offset and length of the FIP image in its I/O policy.
252 *
253 * @active_fw_bank_idx: active firmware bank index gathered from FWU metadata.
254 ******************************************************************************/
arm_set_fip_addr(uint32_t active_fw_bank_idx)255 void arm_set_fip_addr(uint32_t active_fw_bank_idx)
256 {
257 uintptr_t dev_handle __unused;
258 uintptr_t image_spec __unused;
259
260 assert(active_fw_bank_idx < NR_OF_FW_BANKS);
261
262 INFO("Booting with partition %s\n", fip_part_names[active_fw_bank_idx]);
263
264 int result = arm_set_image_source(FIP_IMAGE_ID,
265 NULL,
266 fip_part_names[active_fw_bank_idx],
267 &dev_handle,
268 &image_spec);
269 if (result != 0) {
270 panic();
271 }
272 }
273 #endif /* ARM_GPT_SUPPORT */
274
275 #if PSA_FWU_SUPPORT
276 /*******************************************************************************
277 * Read the FIP partition of the GPT image corresponding to the active firmware
278 * bank to get its offset and length, and update these details in the I/O policy
279 * of the FIP image.
280 ******************************************************************************/
plat_fwu_set_images_source(const struct fwu_metadata * metadata)281 void plat_fwu_set_images_source(const struct fwu_metadata *metadata)
282 {
283 const struct fwu_fw_store_descriptor *fw_desc;
284 const struct fwu_image_entry *img_entry;
285 const struct efi_guid *img_guid = NULL;
286 struct efi_guid img_guid_local;
287 uint32_t i;
288 uintptr_t dev_handle __unused;
289 uintptr_t image_spec __unused;
290
291 if (metadata->desc_offset == 0U) {
292 arm_set_fip_addr(metadata->active_index);
293 return;
294 }
295
296 fw_desc = (const struct fwu_fw_store_descriptor *)
297 ((const uint8_t *)metadata + metadata->desc_offset);
298 img_entry = fw_desc->img_entry;
299 for (i = 0U; i < NR_OF_IMAGES_IN_FW_BANK; i++) {
300 if (guidcmp(&img_entry[i].img_type_guid,
301 &fip_img_type_guid) == 0) {
302 /* Copy out of packed metadata to avoid unaligned pointer. */
303 memcpy(&img_guid_local,
304 &img_entry[i]
305 .img_bank_info[metadata->active_index]
306 .img_guid,
307 sizeof(img_guid_local));
308 img_guid = &img_guid_local;
309 break;
310 }
311 }
312
313 if (img_guid == NULL) {
314 arm_set_fip_addr(metadata->active_index);
315 return;
316 }
317
318 if (arm_set_image_source(FIP_IMAGE_ID, img_guid,
319 fip_part_names[metadata->active_index],
320 &dev_handle, &image_spec) != 0) {
321 panic();
322 }
323 }
324
325 /*******************************************************************************
326 * Read the requested FWU metadata partition of the GPT image to get its offset
327 * and length, and update these details in the I/O policy of the requested FWU
328 * metadata image.
329 ******************************************************************************/
plat_fwu_set_metadata_image_source(unsigned int image_id,uintptr_t * dev_handle,uintptr_t * image_spec)330 int plat_fwu_set_metadata_image_source(unsigned int image_id,
331 uintptr_t *dev_handle,
332 uintptr_t *image_spec)
333 {
334 const partition_entry_t *entry = NULL;
335 struct plat_io_policy *policy = NULL;
336 io_block_spec_t *spec = NULL;
337
338 if (image_id == FWU_METADATA_IMAGE_ID) {
339 if (guidcmp(&fwu_metadata_type_guid, &null_guid) != 0) {
340 entry = get_partition_entry_by_type_index(
341 &fwu_metadata_type_guid, 0U);
342 }
343 } else if (image_id == BKUP_FWU_METADATA_IMAGE_ID) {
344 if (guidcmp(&fwu_metadata_type_guid, &null_guid) != 0) {
345 entry = get_partition_entry_by_type_index(
346 &fwu_metadata_type_guid, 1U);
347 }
348 }
349
350 if (entry == NULL) {
351 if (image_id == FWU_METADATA_IMAGE_ID) {
352 return arm_set_image_source(FWU_METADATA_IMAGE_ID,
353 NULL, "FWU-Metadata",
354 dev_handle, image_spec);
355 }
356 return arm_set_image_source(BKUP_FWU_METADATA_IMAGE_ID,
357 NULL, "Bkup-FWU-Metadata",
358 dev_handle, image_spec);
359 }
360
361 policy = FCONF_GET_PROPERTY(arm, io_policies, image_id);
362 assert(policy != NULL);
363 assert(policy->image_spec != 0UL);
364
365 spec = (io_block_spec_t *)policy->image_spec;
366 spec->offset = PLAT_ARM_FLASH_IMAGE_BASE + entry->start;
367 spec->length = entry->length;
368 *dev_handle = *(policy->dev_handle);
369 *image_spec = policy->image_spec;
370
371 return 0;
372 }
373 #endif /* PSA_FWU_SUPPORT */
374
375 #ifndef DECRYPTION_SUPPORT_none
open_enc_fip(const uintptr_t spec)376 int open_enc_fip(const uintptr_t spec)
377 {
378 int result;
379 uintptr_t local_image_handle;
380
381 /* See if an encrypted FIP is available */
382 result = io_dev_init(enc_dev_handle, (uintptr_t)ENC_IMAGE_ID);
383 if (result == 0) {
384 result = io_open(enc_dev_handle, spec, &local_image_handle);
385 if (result == 0) {
386 VERBOSE("Using encrypted FIP\n");
387 io_close(local_image_handle);
388 }
389 }
390 return result;
391 }
392 #endif
393