arm-trusted-firmware/plat/arm/common/fconf/arm_fconf_sp.c

109 lines
2.9 KiB
C
Raw Normal View History

/*
* Copyright (c) 2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <common/debug.h>
#include <common/desc_image_load.h>
#include <common/fdt_wrappers.h>
#include <drivers/io/io_storage.h>
#include <lib/object_pool.h>
#include <libfdt.h>
#include <plat/arm/common/arm_fconf_getter.h>
#include <plat/arm/common/arm_fconf_io_storage.h>
#include <plat/arm/common/fconf_arm_sp_getter.h>
#include <platform_def.h>
#include <tools_share/firmware_image_package.h>
#ifdef IMAGE_BL2
bl_mem_params_node_t sp_mem_params_descs[MAX_SP_IDS];
struct arm_sp_t arm_sp;
int fconf_populate_arm_sp(uintptr_t config)
{
int sp_node, node, err;
union uuid_helper_t uuid_helper;
unsigned int index = 0;
fdt/wrappers: Replace fdtw_read_cells() implementation Our fdtw_read_cells() implementation goes to great lengths to sanity-check every parameter and result, but leaves a big hole open: The size of the storage the value pointer points at needs to match the number of cells given. This can't be easily checked at compile time, since we lose the size information by using a void pointer. Regardless the current usage of this function is somewhat wrong anyways, since we use it on single-element, fixed-length properties only, for which the DT binding specifies the size. Typically we use those functions dealing with a number of cells in DT context to deal with *dynamically* sized properties, which depend on other properties (#size-cells, #clock-cells, ...), to specify the number of cells needed. Another problem with the current implementation is the use of ambiguously sized types (uintptr_t, size_t) together with a certain expectation about their size. In general there is no relation between the length of a DT property and the bitness of the code that parses the DTB: AArch64 code could encounter 32-bit addresses (where the physical address space is limited to 4GB [1]), while AArch32 code could read 64-bit sized properties (/memory nodes on LPAE systems, [2]). To make this more clear, fix the potential issues and also align more with other DT users (Linux and U-Boot), introduce functions to explicitly read uint32 and uint64 properties. As the other DT consumers, we do this based on the generic "read array" function. Convert all users to use either of those two new functions, and make sure we never use a pointer to anything other than uint32_t or uint64_t variables directly. This reveals (and fixes) a bug in plat_spmd_manifest.c, where we write 4 bytes into a uint16_t variable (passed via a void pointer). Also we change the implementation of the function to better align with other libfdt users, by using the right types (fdt32_t) and common variable names (*prop, prop_names). [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi#n874 [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm/boot/dts/ecx-2000.dts Change-Id: I718de960515117ac7a3331a1b177d2ec224a3890 Signed-off-by: Andre Przywara <andre.przywara@arm.com>
2020-03-26 11:22:37 +00:00
uint32_t val32;
const unsigned int sp_start_index = MAX_NUMBER_IDS - MAX_SP_IDS;
/* As libfdt use void *, we can't avoid this cast */
const void *dtb = (void *)config;
/* Assert the node offset point to "arm,sp" compatible property */
const char *compatible_str = "arm,sp";
node = fdt_node_offset_by_compatible(dtb, -1, compatible_str);
if (node < 0) {
ERROR("FCONF: Can't find %s in dtb\n", compatible_str);
return node;
}
fdt_for_each_subnode(sp_node, dtb, node) {
err = fdt_read_uint32_array(dtb, sp_node, "uuid", 4,
uuid_helper.word);
if (err < 0) {
ERROR("FCONF: cannot read SP uuid\n");
return -1;
}
arm_sp.uuids[index] = uuid_helper;
fdt/wrappers: Replace fdtw_read_cells() implementation Our fdtw_read_cells() implementation goes to great lengths to sanity-check every parameter and result, but leaves a big hole open: The size of the storage the value pointer points at needs to match the number of cells given. This can't be easily checked at compile time, since we lose the size information by using a void pointer. Regardless the current usage of this function is somewhat wrong anyways, since we use it on single-element, fixed-length properties only, for which the DT binding specifies the size. Typically we use those functions dealing with a number of cells in DT context to deal with *dynamically* sized properties, which depend on other properties (#size-cells, #clock-cells, ...), to specify the number of cells needed. Another problem with the current implementation is the use of ambiguously sized types (uintptr_t, size_t) together with a certain expectation about their size. In general there is no relation between the length of a DT property and the bitness of the code that parses the DTB: AArch64 code could encounter 32-bit addresses (where the physical address space is limited to 4GB [1]), while AArch32 code could read 64-bit sized properties (/memory nodes on LPAE systems, [2]). To make this more clear, fix the potential issues and also align more with other DT users (Linux and U-Boot), introduce functions to explicitly read uint32 and uint64 properties. As the other DT consumers, we do this based on the generic "read array" function. Convert all users to use either of those two new functions, and make sure we never use a pointer to anything other than uint32_t or uint64_t variables directly. This reveals (and fixes) a bug in plat_spmd_manifest.c, where we write 4 bytes into a uint16_t variable (passed via a void pointer). Also we change the implementation of the function to better align with other libfdt users, by using the right types (fdt32_t) and common variable names (*prop, prop_names). [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi#n874 [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm/boot/dts/ecx-2000.dts Change-Id: I718de960515117ac7a3331a1b177d2ec224a3890 Signed-off-by: Andre Przywara <andre.przywara@arm.com>
2020-03-26 11:22:37 +00:00
err = fdt_read_uint32(dtb, sp_node, "load-address", &val32);
if (err < 0) {
ERROR("FCONF: cannot read SP load address\n");
return -1;
}
fdt/wrappers: Replace fdtw_read_cells() implementation Our fdtw_read_cells() implementation goes to great lengths to sanity-check every parameter and result, but leaves a big hole open: The size of the storage the value pointer points at needs to match the number of cells given. This can't be easily checked at compile time, since we lose the size information by using a void pointer. Regardless the current usage of this function is somewhat wrong anyways, since we use it on single-element, fixed-length properties only, for which the DT binding specifies the size. Typically we use those functions dealing with a number of cells in DT context to deal with *dynamically* sized properties, which depend on other properties (#size-cells, #clock-cells, ...), to specify the number of cells needed. Another problem with the current implementation is the use of ambiguously sized types (uintptr_t, size_t) together with a certain expectation about their size. In general there is no relation between the length of a DT property and the bitness of the code that parses the DTB: AArch64 code could encounter 32-bit addresses (where the physical address space is limited to 4GB [1]), while AArch32 code could read 64-bit sized properties (/memory nodes on LPAE systems, [2]). To make this more clear, fix the potential issues and also align more with other DT users (Linux and U-Boot), introduce functions to explicitly read uint32 and uint64 properties. As the other DT consumers, we do this based on the generic "read array" function. Convert all users to use either of those two new functions, and make sure we never use a pointer to anything other than uint32_t or uint64_t variables directly. This reveals (and fixes) a bug in plat_spmd_manifest.c, where we write 4 bytes into a uint16_t variable (passed via a void pointer). Also we change the implementation of the function to better align with other libfdt users, by using the right types (fdt32_t) and common variable names (*prop, prop_names). [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi#n874 [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm/boot/dts/ecx-2000.dts Change-Id: I718de960515117ac7a3331a1b177d2ec224a3890 Signed-off-by: Andre Przywara <andre.przywara@arm.com>
2020-03-26 11:22:37 +00:00
arm_sp.load_addr[index] = val32;
VERBOSE("FCONF: %s UUID %x-%x-%x-%x load_addr=%lx\n",
__func__,
uuid_helper.word[0],
uuid_helper.word[1],
uuid_helper.word[2],
uuid_helper.word[3],
arm_sp.load_addr[index]);
/* Add SP information in mem param descriptor */
sp_mem_params_descs[index].image_id = sp_start_index + index;
SET_PARAM_HEAD(&sp_mem_params_descs[index].image_info,
PARAM_IMAGE_BINARY, VERSION_2, 0);
sp_mem_params_descs[index].image_info.image_max_size =
ARM_SP_MAX_SIZE;
sp_mem_params_descs[index].next_handoff_image_id =
INVALID_IMAGE_ID;
sp_mem_params_descs[index].image_info.image_base =
arm_sp.load_addr[index];
/* Add SP information in IO policies structure */
policies[sp_start_index + index].image_spec =
(uintptr_t)&arm_sp.uuids[index];
policies[sp_start_index + index].dev_handle = &fip_dev_handle;
policies[sp_start_index + index].check = open_fip;
index++;
if (index >= MAX_SP_IDS) {
ERROR("FCONF: reached max number of SPs\n");
return -1;
}
}
if ((sp_node < 0) && (sp_node != -FDT_ERR_NOTFOUND)) {
ERROR("%d: fdt_for_each_subnode(): %d\n", __LINE__, node);
return sp_node;
}
arm_sp.number_of_sp = index;
return 0;
}
FCONF_REGISTER_POPULATOR(TB_FW, arm_sp, fconf_populate_arm_sp);
#endif /* IMAGE_BL2 */