You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
5.1 KiB

/*
* Copyright (c) 2020-2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <libfdt.h>
#include <stdint.h>
#include <string.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <common/fdt_wrappers.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <platform_def.h>
#include <services/spm_core_manifest.h>
#define ATTRIBUTE_ROOT_NODE_STR "attribute"
/*******************************************************************************
* SPMC attribute node parser
******************************************************************************/
static int manifest_parse_attribute(spmc_manifest_attribute_t *attr,
const void *fdt,
int node)
{
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&#39;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 &#34;read array&#34; 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 &lt;andre.przywara@arm.com&gt;
5 years ago
uint32_t val32;
int rc;
assert((attr != NULL) && (fdt != NULL));
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&#39;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 &#34;read array&#34; 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 &lt;andre.przywara@arm.com&gt;
5 years ago
rc = fdt_read_uint32(fdt, node, "maj_ver", &attr->major_version);
if (rc != 0) {
ERROR("Missing FFA %s version in SPM Core manifest.\n",
"major");
return rc;
}
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&#39;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 &#34;read array&#34; 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 &lt;andre.przywara@arm.com&gt;
5 years ago
rc = fdt_read_uint32(fdt, node, "min_ver", &attr->minor_version);
if (rc != 0) {
ERROR("Missing FFA %s version in SPM Core manifest.\n",
"minor");
return rc;
}
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&#39;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 &#34;read array&#34; 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 &lt;andre.przywara@arm.com&gt;
5 years ago
rc = fdt_read_uint32(fdt, node, "spmc_id", &val32);
if (rc != 0) {
ERROR("Missing SPMC ID in manifest.\n");
return rc;
}
attr->spmc_id = val32 & 0xffff;
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&#39;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 &#34;read array&#34; 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 &lt;andre.przywara@arm.com&gt;
5 years ago
rc = fdt_read_uint32(fdt, node, "exec_state", &attr->exec_state);
if (rc != 0) {
NOTICE("%s not specified in SPM Core manifest.\n",
"Execution state");
}
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&#39;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 &#34;read array&#34; 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 &lt;andre.przywara@arm.com&gt;
5 years ago
rc = fdt_read_uint32(fdt, node, "binary_size", &attr->binary_size);
if (rc != 0) {
NOTICE("%s not specified in SPM Core manifest.\n",
"Binary size");
}
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&#39;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 &#34;read array&#34; 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 &lt;andre.przywara@arm.com&gt;
5 years ago
rc = fdt_read_uint64(fdt, node, "load_address", &attr->load_address);
if (rc != 0) {
NOTICE("%s not specified in SPM Core manifest.\n",
"Load address");
}
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&#39;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 &#34;read array&#34; 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 &lt;andre.przywara@arm.com&gt;
5 years ago
rc = fdt_read_uint64(fdt, node, "entrypoint", &attr->entrypoint);
if (rc != 0) {
NOTICE("%s not specified in SPM Core manifest.\n",
"Entry point");
}
VERBOSE("SPM Core manifest attribute section:\n");
VERBOSE(" version: %u.%u\n", attr->major_version, attr->minor_version);
VERBOSE(" spmc_id: 0x%x\n", attr->spmc_id);
VERBOSE(" binary_size: 0x%x\n", attr->binary_size);
VERBOSE(" load_address: 0x%" PRIx64 "\n", attr->load_address);
VERBOSE(" entrypoint: 0x%" PRIx64 "\n", attr->entrypoint);
return 0;
}
/*******************************************************************************
* Root node handler
******************************************************************************/
static int manifest_parse_root(spmc_manifest_attribute_t *manifest,
const void *fdt,
int root)
{
int node;
assert(manifest != NULL);
node = fdt_subnode_offset_namelen(fdt, root, ATTRIBUTE_ROOT_NODE_STR,
sizeof(ATTRIBUTE_ROOT_NODE_STR) - 1);
if (node < 0) {
ERROR("Root node doesn't contain subnode '%s'\n",
ATTRIBUTE_ROOT_NODE_STR);
return node;
}
return manifest_parse_attribute(manifest, fdt, node);
}
/*******************************************************************************
* Platform handler to parse a SPM Core manifest.
******************************************************************************/
int plat_spm_core_manifest_load(spmc_manifest_attribute_t *manifest,
const void *pm_addr)
{
int rc, unmap_ret;
uintptr_t pm_base, pm_base_align;
size_t mapped_size;
assert(manifest != NULL);
assert(pm_addr != NULL);
/*
* Assume TOS_FW_CONFIG is not necessarily aligned to a page
* boundary, thus calculate the remaining space between SPMC
* manifest start address and upper page limit.
*
*/
pm_base = (uintptr_t)pm_addr;
pm_base_align = page_align(pm_base, UP);
if (pm_base == pm_base_align) {
/* Page aligned */
mapped_size = PAGE_SIZE;
} else {
mapped_size = pm_base_align - pm_base;
}
/* Check space within the page at least maps the FDT header */
if (mapped_size < sizeof(struct fdt_header)) {
ERROR("Error while mapping SPM Core manifest.\n");
return -EINVAL;
}
/* Map first SPMC manifest page in the SPMD translation regime */
pm_base_align = page_align(pm_base, DOWN);
rc = mmap_add_dynamic_region((unsigned long long)pm_base_align,
pm_base_align,
PAGE_SIZE,
MT_RO_DATA | EL3_PAS);
if (rc != 0) {
ERROR("Error while mapping SPM Core manifest (%d).\n", rc);
return rc;
}
rc = fdt_check_header(pm_addr);
if (rc != 0) {
ERROR("Wrong format for SPM Core manifest (%d).\n", rc);
goto exit_unmap;
}
/* Check SPMC manifest fits within the upper mapped page boundary */
if (mapped_size < fdt_totalsize(pm_addr)) {
ERROR("SPM Core manifest too large.\n");
rc = -EINVAL;
goto exit_unmap;
}
VERBOSE("Reading SPM Core manifest at address %p\n", pm_addr);
rc = fdt_node_offset_by_compatible(pm_addr, -1,
"arm,ffa-core-manifest-1.0");
if (rc < 0) {
ERROR("Unrecognized SPM Core manifest\n");
goto exit_unmap;
}
rc = manifest_parse_root(manifest, pm_addr, rc);
exit_unmap:
unmap_ret = mmap_remove_dynamic_region(pm_base_align, PAGE_SIZE);
if (unmap_ret != 0) {
ERROR("Error while unmapping SPM Core manifest (%d).\n",
unmap_ret);
if (rc == 0) {
rc = unmap_ret;
}
}
return rc;
}