From a97bfa5ff18b2682e3b9c528cbd5fb16ceec3393 Mon Sep 17 00:00:00 2001 From: AlexeiFedorov Date: Wed, 14 Dec 2022 17:28:11 +0000 Subject: [PATCH 1/3] feat(rme): set DRAM information in Boot Manifest platform data This patch adds support for setting configuration of DRAM banks for FVP model in RMM-EL3 Boot Manifest structure. Structure 'rmm_manifest' is extended with 'plat_dram' structure which contains information about platform's DRAM layout: - number of DRAM banks; - pointer to 'dram_bank[]' array; - check sum: two's complement 64-bit value of the sum of data in 'plat_dram' and 'dram_bank[] array. Each 'dram_bank' structure holds information about DRAM bank base address and its size. This values must be aligned to 4KB page size. The patch increases Boot Manifest minor version to 2 and removes 'typedef rmm_manifest_t' as per "3.4.15.1. Avoid anonymous typedefs of structs/enums in headers" of https://trustedfirmware-a.readthedocs.io/en/latest/process/coding-style.html Signed-off-by: AlexeiFedorov Change-Id: I5176caa5780e27d1e0daeb5dea3e40cf6ad5fd12 --- include/plat/arm/common/arm_def.h | 2 ++ include/plat/common/platform.h | 5 +-- include/services/rmm_core_manifest.h | 50 ++++++++++++++++++++++------ include/services/trp/platform_trp.h | 4 ++- plat/arm/board/fvp/fvp_common.c | 40 +++++++++++++++++++--- plat/arm/common/arm_bl2_setup.c | 4 --- plat/arm/common/arm_bl31_setup.c | 2 -- plat/arm/common/trp/arm_trp_setup.c | 9 +++-- services/std_svc/rmmd/rmmd_main.c | 4 +-- services/std_svc/rmmd/trp/trp_main.c | 2 +- 10 files changed, 89 insertions(+), 33 deletions(-) diff --git a/include/plat/arm/common/arm_def.h b/include/plat/arm/common/arm_def.h index 36b1bdb6d..1e22c1073 100644 --- a/include/plat/arm/common/arm_def.h +++ b/include/plat/arm/common/arm_def.h @@ -235,6 +235,8 @@ #define ARM_DRAM2_SIZE PLAT_ARM_DRAM2_SIZE #define ARM_DRAM2_END (ARM_DRAM2_BASE + \ ARM_DRAM2_SIZE - 1U) +/* Number of DRAM banks */ +#define ARM_DRAM_BANKS_NUM 2UL #define ARM_IRQ_SEC_PHY_TIMER 29 diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index 335103612..8543ac713 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -11,7 +11,7 @@ #include #if defined(SPD_spmd) - #include +#include #endif #if ENABLE_RME #include @@ -37,6 +37,7 @@ struct bl_params; struct mmap_region; struct spm_mm_boot_info; struct sp_res_desc; +struct rmm_manifest; enum fw_enc_status_t; /******************************************************************************* @@ -322,7 +323,7 @@ int plat_rmmd_get_cca_attest_token(uintptr_t buf, size_t *len, int plat_rmmd_get_cca_realm_attest_key(uintptr_t buf, size_t *len, unsigned int type); size_t plat_rmmd_get_el3_rmm_shared_mem(uintptr_t *shared); -int plat_rmmd_load_manifest(rmm_manifest_t *manifest); +int plat_rmmd_load_manifest(struct rmm_manifest *manifest); #endif /******************************************************************************* diff --git a/include/services/rmm_core_manifest.h b/include/services/rmm_core_manifest.h index 7edef469b..a59788392 100644 --- a/include/services/rmm_core_manifest.h +++ b/include/services/rmm_core_manifest.h @@ -14,7 +14,7 @@ #include #define RMMD_MANIFEST_VERSION_MAJOR U(0) -#define RMMD_MANIFEST_VERSION_MINOR U(1) +#define RMMD_MANIFEST_VERSION_MINOR U(2) /* * Manifest version encoding: @@ -35,16 +35,44 @@ #define RMMD_GET_MANIFEST_VERSION_MINOR(_version) \ (_version & 0xFFFF) -/* Boot manifest core structure as per v0.1 */ -typedef struct rmm_manifest { - uint32_t version; /* Manifest version */ - uint32_t padding; /* RES0 */ - uintptr_t plat_data; /* Manifest platform data */ -} rmm_manifest_t; +/* DRAM bank structure */ +struct dram_bank { + uintptr_t base; /* Base address */ + uint64_t size; /* Size of bank */ +}; -CASSERT(offsetof(rmm_manifest_t, version) == 0, - rmm_manifest_t_version_unaligned); -CASSERT(offsetof(rmm_manifest_t, plat_data) == 8, - rmm_manifest_t_plat_data_unaligned); +CASSERT(offsetof(struct dram_bank, base) == 0, + rmm_manifest_base_unaligned); +CASSERT(offsetof(struct dram_bank, size) == 8, + rmm_manifest_size_unaligned); + +/* DRAM layout info structure */ +struct dram_info { + uint64_t banks_num; /* Number of DRAM banks */ + struct dram_bank *dram_data; /* Pointer to dram_bank[] */ + uint64_t check_sum; /* Checksum of dram_info data */ +}; + +CASSERT(offsetof(struct dram_info, banks_num) == 0, + rmm_manifest_banks_num_unaligned); +CASSERT(offsetof(struct dram_info, dram_data) == 8, + rmm_manifest_dram_data_unaligned); +CASSERT(offsetof(struct dram_info, check_sum) == 16, + rmm_manifest_check_sum_unaligned); + +/* Boot manifest core structure as per v0.2 */ +struct rmm_manifest { + uint32_t version; /* Manifest version */ + uint32_t padding; /* RES0 */ + uintptr_t plat_data; /* Manifest platform data */ + struct dram_info plat_dram; /* Platform DRAM data */ +}; + +CASSERT(offsetof(struct rmm_manifest, version) == 0, + rmm_manifest_version_unaligned); +CASSERT(offsetof(struct rmm_manifest, plat_data) == 8, + rmm_manifest_plat_data_unaligned); +CASSERT(offsetof(struct rmm_manifest, plat_dram) == 16, + rmm_manifest_plat_dram_unaligned); #endif /* RMM_CORE_MANIFEST_H */ diff --git a/include/services/trp/platform_trp.h b/include/services/trp/platform_trp.h index 1c963c851..756e9db6c 100644 --- a/include/services/trp/platform_trp.h +++ b/include/services/trp/platform_trp.h @@ -9,9 +9,11 @@ #include +struct rmm_manifest; + /******************************************************************************* * Mandatory TRP functions (only if platform contains a TRP) ******************************************************************************/ -void trp_early_platform_setup(rmm_manifest_t *manifest); +void trp_early_platform_setup(struct rmm_manifest *manifest); #endif /* PLATFORM_TRP_H */ diff --git a/plat/arm/board/fvp/fvp_common.c b/plat/arm/board/fvp/fvp_common.c index f5d9940f0..e6d5b159b 100644 --- a/plat/arm/board/fvp/fvp_common.c +++ b/plat/arm/board/fvp/fvp_common.c @@ -17,14 +17,13 @@ #include #include #include -#if ENABLE_RME #include -#endif #if SPM_MM #include #endif #include +#include #include #include @@ -531,15 +530,46 @@ size_t plat_rmmd_get_el3_rmm_shared_mem(uintptr_t *shared) return (size_t)RMM_SHARED_SIZE; } -int plat_rmmd_load_manifest(rmm_manifest_t *manifest) +CASSERT(ARM_DRAM_BANKS_NUM == 2UL, ARM_DRAM_BANKS_NUM_mismatch); + +/* FVP DRAM banks */ +const struct dram_bank fvp_dram_banks[ARM_DRAM_BANKS_NUM] = { + {ARM_PAS_2_BASE, ARM_PAS_2_SIZE}, + {ARM_PAS_4_BASE, ARM_PAS_4_SIZE} +}; + +int plat_rmmd_load_manifest(struct rmm_manifest *manifest) { + uint64_t check_sum; + struct dram_bank *bank_ptr; + assert(manifest != NULL); manifest->version = RMMD_MANIFEST_VERSION; manifest->padding = 0U; /* RES0 */ manifest->plat_data = (uintptr_t)NULL; + manifest->plat_dram.banks_num = ARM_DRAM_BANKS_NUM; + + /* Array dram_banks[] follows dram_info structure */ + bank_ptr = (struct dram_bank *) + ((uintptr_t)&manifest->plat_dram.check_sum + + sizeof(manifest->plat_dram.check_sum)); + + manifest->plat_dram.dram_data = bank_ptr; + + /* Copy FVP DRAM banks data to Boot Manifest */ + (void)memcpy((void *)bank_ptr, &fvp_dram_banks, sizeof(fvp_dram_banks)); + + /* Calculate check sum of plat_dram structure */ + check_sum = ARM_DRAM_BANKS_NUM + (uint64_t)bank_ptr; + + for (unsigned long i = 0UL; i < ARM_DRAM_BANKS_NUM; i++) { + check_sum += bank_ptr[i].base + bank_ptr[i].size; + } + + /* Check sum must be 0 */ + manifest->plat_dram.check_sum = ~check_sum + 1UL; return 0; } - -#endif +#endif /* ENABLE_RME */ diff --git a/plat/arm/common/arm_bl2_setup.c b/plat/arm/common/arm_bl2_setup.c index 02e419a43..ca9842285 100644 --- a/plat/arm/common/arm_bl2_setup.c +++ b/plat/arm/common/arm_bl2_setup.c @@ -18,16 +18,12 @@ #include #include #include -#if ENABLE_RME #include -#endif /* ENABLE_RME */ #ifdef SPD_opteed #include #endif #include -#if ENABLE_RME #include -#endif /* ENABLE_RME */ #include #include diff --git a/plat/arm/common/arm_bl31_setup.c b/plat/arm/common/arm_bl31_setup.c index cf403b161..19efdd32e 100644 --- a/plat/arm/common/arm_bl31_setup.c +++ b/plat/arm/common/arm_bl31_setup.c @@ -13,9 +13,7 @@ #include #include #include -#if ENABLE_RME #include -#endif #include #include #include diff --git a/plat/arm/common/trp/arm_trp_setup.c b/plat/arm/common/trp/arm_trp_setup.c index aeacd1054..04063219b 100644 --- a/plat/arm/common/trp/arm_trp_setup.c +++ b/plat/arm/common/trp/arm_trp_setup.c @@ -26,7 +26,7 @@ extern uint32_t trp_boot_manifest_version; ******************************************************************************/ static console_t arm_trp_runtime_console; -static int arm_trp_process_manifest(rmm_manifest_t *manifest) +static int arm_trp_process_manifest(struct rmm_manifest *manifest) { /* padding field on the manifest must be RES0 */ assert(manifest->padding == 0U); @@ -38,12 +38,12 @@ static int arm_trp_process_manifest(rmm_manifest_t *manifest) } trp_boot_manifest_version = manifest->version; - flush_dcache_range((uintptr_t)manifest, sizeof(rmm_manifest_t)); + flush_dcache_range((uintptr_t)manifest, sizeof(struct rmm_manifest)); return 0; } -void arm_trp_early_platform_setup(rmm_manifest_t *manifest) +void arm_trp_early_platform_setup(struct rmm_manifest *manifest) { int rc; @@ -66,10 +66,9 @@ void arm_trp_early_platform_setup(rmm_manifest_t *manifest) console_set_scope(&arm_trp_runtime_console, CONSOLE_FLAG_BOOT | CONSOLE_FLAG_RUNTIME); - } -void trp_early_platform_setup(rmm_manifest_t *manifest) +void trp_early_platform_setup(struct rmm_manifest *manifest) { arm_trp_early_platform_setup(manifest); } diff --git a/services/std_svc/rmmd/rmmd_main.c b/services/std_svc/rmmd/rmmd_main.c index 6bd9fdf30..e12eae79f 100644 --- a/services/std_svc/rmmd/rmmd_main.c +++ b/services/std_svc/rmmd/rmmd_main.c @@ -171,7 +171,7 @@ int rmmd_setup(void) uint32_t ep_attr; unsigned int linear_id = plat_my_core_pos(); rmmd_rmm_context_t *rmm_ctx = &rmm_context[linear_id]; - rmm_manifest_t *manifest; + struct rmm_manifest *manifest; int rc; /* Make sure RME is supported. */ @@ -206,7 +206,7 @@ int rmmd_setup(void) ((void *)shared_buf_base != NULL)); /* Load the boot manifest at the beginning of the shared area */ - manifest = (rmm_manifest_t *)shared_buf_base; + manifest = (struct rmm_manifest *)shared_buf_base; rc = plat_rmmd_load_manifest(manifest); if (rc != 0) { ERROR("Error loading RMM Boot Manifest (%i)\n", rc); diff --git a/services/std_svc/rmmd/trp/trp_main.c b/services/std_svc/rmmd/trp/trp_main.c index 196bc119e..4eb3e1266 100644 --- a/services/std_svc/rmmd/trp/trp_main.c +++ b/services/std_svc/rmmd/trp/trp_main.c @@ -62,7 +62,7 @@ void trp_setup(uint64_t x0, sizeof(trp_shared_region_start)); /* Perform early platform-specific setup */ - trp_early_platform_setup((rmm_manifest_t *)trp_shared_region_start); + trp_early_platform_setup((struct rmm_manifest *)trp_shared_region_start); } int trp_validate_warmboot_args(uint64_t x0, uint64_t x1, From 826859049859a5bd88e142695e10a559d85721c1 Mon Sep 17 00:00:00 2001 From: AlexeiFedorov Date: Thu, 29 Dec 2022 15:57:40 +0000 Subject: [PATCH 2/3] feat(rme): read DRAM information from FVP DTB This patch builds on the previous patch by implementing support for reading NS DRAM layout of FVP model from HW_CONFIG Device tree. Macro _RMMD_MANIFEST_VERSION is renamed to SET_RMMD_MANIFEST_VERSION to suppress MISRA-C "rule MC3R1.D4.5: (advisory) Identifiers in the same name space with overlapping visibility should be typographically unambiguous" warning Signed-off-by: AlexeiFedorov Change-Id: Ifc2461b4441a1efdd4b7c656ab4d15e62479f77b --- include/plat/arm/common/arm_def.h | 4 +- include/services/rmm_core_manifest.h | 48 ++++++------ .../board/fvp/fconf/fconf_hw_config_getter.c | 62 ++++++++++++++- plat/arm/board/fvp/fvp_common.c | 78 +++++++++++++------ .../fvp/include/fconf_hw_config_getter.h | 14 +++- plat/arm/board/fvp/jmptbl.i | 3 +- plat/arm/common/arm_bl2_setup.c | 6 +- 7 files changed, 158 insertions(+), 57 deletions(-) diff --git a/include/plat/arm/common/arm_def.h b/include/plat/arm/common/arm_def.h index 1e22c1073..7cd32b1f6 100644 --- a/include/plat/arm/common/arm_def.h +++ b/include/plat/arm/common/arm_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2023, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -236,7 +236,7 @@ #define ARM_DRAM2_END (ARM_DRAM2_BASE + \ ARM_DRAM2_SIZE - 1U) /* Number of DRAM banks */ -#define ARM_DRAM_BANKS_NUM 2UL +#define ARM_DRAM_NUM_BANKS 2UL #define ARM_IRQ_SEC_PHY_TIMER 29 diff --git a/include/services/rmm_core_manifest.h b/include/services/rmm_core_manifest.h index a59788392..b89de9f28 100644 --- a/include/services/rmm_core_manifest.h +++ b/include/services/rmm_core_manifest.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Arm Limited. All rights reserved. + * Copyright (c) 2022-2023, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -22,57 +22,57 @@ * - Bits [30:16] Major version * - Bits [15:0] Minor version */ -#define _RMMD_MANIFEST_VERSION(_major, _minor) \ +#define SET_RMMD_MANIFEST_VERSION(_major, _minor) \ ((((_major) & 0x7FFF) << 16) | ((_minor) & 0xFFFF)) -#define RMMD_MANIFEST_VERSION _RMMD_MANIFEST_VERSION( \ - RMMD_MANIFEST_VERSION_MAJOR, \ +#define RMMD_MANIFEST_VERSION SET_RMMD_MANIFEST_VERSION( \ + RMMD_MANIFEST_VERSION_MAJOR, \ RMMD_MANIFEST_VERSION_MINOR) -#define RMMD_GET_MANIFEST_VERSION_MAJOR(_version) \ +#define RMMD_GET_MANIFEST_VERSION_MAJOR(_version) \ ((_version >> 16) & 0x7FFF) -#define RMMD_GET_MANIFEST_VERSION_MINOR(_version) \ +#define RMMD_GET_MANIFEST_VERSION_MINOR(_version) \ (_version & 0xFFFF) -/* DRAM bank structure */ -struct dram_bank { +/* NS DRAM bank structure */ +struct ns_dram_bank { uintptr_t base; /* Base address */ uint64_t size; /* Size of bank */ }; -CASSERT(offsetof(struct dram_bank, base) == 0, +CASSERT(offsetof(struct ns_dram_bank, base) == 0UL, rmm_manifest_base_unaligned); -CASSERT(offsetof(struct dram_bank, size) == 8, +CASSERT(offsetof(struct ns_dram_bank, size) == 8UL, rmm_manifest_size_unaligned); -/* DRAM layout info structure */ -struct dram_info { - uint64_t banks_num; /* Number of DRAM banks */ - struct dram_bank *dram_data; /* Pointer to dram_bank[] */ - uint64_t check_sum; /* Checksum of dram_info data */ +/* NS DRAM layout info structure */ +struct ns_dram_info { + uint64_t num_banks; /* Number of NS DRAM banks */ + struct ns_dram_bank *banks; /* Pointer to ns_dram_bank[] */ + uint64_t checksum; /* Checksum of ns_dram_info data */ }; -CASSERT(offsetof(struct dram_info, banks_num) == 0, - rmm_manifest_banks_num_unaligned); -CASSERT(offsetof(struct dram_info, dram_data) == 8, +CASSERT(offsetof(struct ns_dram_info, num_banks) == 0UL, + rmm_manifest_num_banks_unaligned); +CASSERT(offsetof(struct ns_dram_info, banks) == 8UL, rmm_manifest_dram_data_unaligned); -CASSERT(offsetof(struct dram_info, check_sum) == 16, - rmm_manifest_check_sum_unaligned); +CASSERT(offsetof(struct ns_dram_info, checksum) == 16UL, + rmm_manifest_checksum_unaligned); /* Boot manifest core structure as per v0.2 */ struct rmm_manifest { uint32_t version; /* Manifest version */ uint32_t padding; /* RES0 */ uintptr_t plat_data; /* Manifest platform data */ - struct dram_info plat_dram; /* Platform DRAM data */ + struct ns_dram_info plat_dram; /* Platform NS DRAM data */ }; -CASSERT(offsetof(struct rmm_manifest, version) == 0, +CASSERT(offsetof(struct rmm_manifest, version) == 0UL, rmm_manifest_version_unaligned); -CASSERT(offsetof(struct rmm_manifest, plat_data) == 8, +CASSERT(offsetof(struct rmm_manifest, plat_data) == 8UL, rmm_manifest_plat_data_unaligned); -CASSERT(offsetof(struct rmm_manifest, plat_dram) == 16, +CASSERT(offsetof(struct rmm_manifest, plat_dram) == 16UL, rmm_manifest_plat_dram_unaligned); #endif /* RMM_CORE_MANIFEST_H */ diff --git a/plat/arm/board/fvp/fconf/fconf_hw_config_getter.c b/plat/arm/board/fvp/fconf/fconf_hw_config_getter.c index 45e3b7eb2..43dc17bc8 100644 --- a/plat/arm/board/fvp/fconf/fconf_hw_config_getter.c +++ b/plat/arm/board/fvp/fconf/fconf_hw_config_getter.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Arm Limited. All rights reserved. + * Copyright (c) 2020-2023, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -18,6 +18,15 @@ struct gicv3_config_t gicv3_config; struct hw_topology_t soc_topology; struct uart_serial_config_t uart_serial_config; struct cpu_timer_t cpu_timer; +struct ns_dram_layout dram_layout; + +/* + * Each NS DRAM bank entry is 'reg' node property which is + * a sequence of (address, length) pairs of 32-bit values. + */ +#define DRAM_ENTRY_SIZE (4UL * sizeof(uint32_t)) + +CASSERT(ARM_DRAM_NUM_BANKS == 2UL, ARM_DRAM_NUM_BANKS_mismatch); #define ILLEGAL_ADDR ULL(~0) @@ -293,7 +302,58 @@ int fconf_populate_cpu_timer(uintptr_t config) return 0; } +int fconf_populate_dram_layout(uintptr_t config) +{ + int node, len; + const uint32_t *reg; + + /* Necessary to work with libfdt APIs */ + const void *hw_config_dtb = (const void *)config; + + /* Find 'memory' node */ + node = fdt_node_offset_by_prop_value(hw_config_dtb, -1, "device_type", + "memory", sizeof("memory")); + if (node < 0) { + WARN("FCONF: Unable to locate 'memory' node\n"); + return node; + } + + reg = fdt_getprop(hw_config_dtb, node, "reg", &len); + if (reg == NULL) { + ERROR("FCONF failed to read 'reg' property\n"); + return len; + } + + switch (len) { + case DRAM_ENTRY_SIZE: + /* 1 DRAM bank */ + dram_layout.num_banks = 1UL; + break; + case 2UL * DRAM_ENTRY_SIZE: + /* 2 DRAM banks */ + dram_layout.num_banks = 2UL; + break; + default: + ERROR("FCONF: Invalid 'memory' node\n"); + return -FDT_ERR_BADLAYOUT; + } + + for (unsigned long i = 0UL; i < dram_layout.num_banks; i++) { + int err = fdt_get_reg_props_by_index( + hw_config_dtb, node, (int)i, + &dram_layout.dram_bank[i].base, + (size_t *)&dram_layout.dram_bank[i].size); + if (err < 0) { + ERROR("FCONF: Failed to read 'reg' property #%lu of 'memory' node\n", i); + return err; + } + } + + return 0; +} + FCONF_REGISTER_POPULATOR(HW_CONFIG, gicv3_config, fconf_populate_gicv3_config); FCONF_REGISTER_POPULATOR(HW_CONFIG, topology, fconf_populate_topology); FCONF_REGISTER_POPULATOR(HW_CONFIG, uart_config, fconf_populate_uart_config); FCONF_REGISTER_POPULATOR(HW_CONFIG, cpu_timer, fconf_populate_cpu_timer); +FCONF_REGISTER_POPULATOR(HW_CONFIG, dram_layout, fconf_populate_dram_layout); diff --git a/plat/arm/board/fvp/fvp_common.c b/plat/arm/board/fvp/fvp_common.c index e6d5b159b..c7bf93e60 100644 --- a/plat/arm/board/fvp/fvp_common.c +++ b/plat/arm/board/fvp/fvp_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -530,45 +531,72 @@ size_t plat_rmmd_get_el3_rmm_shared_mem(uintptr_t *shared) return (size_t)RMM_SHARED_SIZE; } -CASSERT(ARM_DRAM_BANKS_NUM == 2UL, ARM_DRAM_BANKS_NUM_mismatch); - -/* FVP DRAM banks */ -const struct dram_bank fvp_dram_banks[ARM_DRAM_BANKS_NUM] = { - {ARM_PAS_2_BASE, ARM_PAS_2_SIZE}, - {ARM_PAS_4_BASE, ARM_PAS_4_SIZE} -}; - int plat_rmmd_load_manifest(struct rmm_manifest *manifest) { - uint64_t check_sum; - struct dram_bank *bank_ptr; + uint64_t checksum, num_banks; + struct ns_dram_bank *bank_ptr; assert(manifest != NULL); + /* Get number of DRAM banks */ + num_banks = FCONF_GET_PROPERTY(hw_config, dram_layout, num_banks); + assert(num_banks <= ARM_DRAM_NUM_BANKS); + manifest->version = RMMD_MANIFEST_VERSION; manifest->padding = 0U; /* RES0 */ manifest->plat_data = (uintptr_t)NULL; - manifest->plat_dram.banks_num = ARM_DRAM_BANKS_NUM; + manifest->plat_dram.num_banks = num_banks; - /* Array dram_banks[] follows dram_info structure */ - bank_ptr = (struct dram_bank *) - ((uintptr_t)&manifest->plat_dram.check_sum + - sizeof(manifest->plat_dram.check_sum)); + /* + * Array ns_dram_banks[] follows ns_dram_info structure: + * + * +-----------------------------------+ + * | offset | field | comment | + * +----------+-----------+------------+ + * | 0 | version | 0x00000002 | + * +----------+-----------+------------+ + * | 4 | padding | 0x00000000 | + * +----------+-----------+------------+ + * | 8 | plat_data | NULL | + * +----------+-----------+------------+ + * | 16 | num_banks | | + * +----------+-----------+ | + * | 24 | banks | plat_dram | + * +----------+-----------+ | + * | 32 | checksum | | + * +----------+-----------+------------+ + * | 40 | base 0 | | + * +----------+-----------+ bank[0] | + * | 48 | size 0 | | + * +----------+-----------+------------+ + * | 56 | base 1 | | + * +----------+-----------+ bank[1] | + * | 64 | size 1 | | + * +----------+-----------+------------+ + */ + bank_ptr = (struct ns_dram_bank *) + ((uintptr_t)&manifest->plat_dram.checksum + + sizeof(manifest->plat_dram.checksum)); - manifest->plat_dram.dram_data = bank_ptr; + manifest->plat_dram.banks = bank_ptr; - /* Copy FVP DRAM banks data to Boot Manifest */ - (void)memcpy((void *)bank_ptr, &fvp_dram_banks, sizeof(fvp_dram_banks)); + /* Calculate checksum of plat_dram structure */ + checksum = num_banks + (uint64_t)bank_ptr; - /* Calculate check sum of plat_dram structure */ - check_sum = ARM_DRAM_BANKS_NUM + (uint64_t)bank_ptr; + /* Store FVP DRAM banks data in Boot Manifest */ + for (unsigned long i = 0UL; i < num_banks; i++) { + uintptr_t base = FCONF_GET_PROPERTY(hw_config, dram_layout, dram_bank[i].base); + uint64_t size = FCONF_GET_PROPERTY(hw_config, dram_layout, dram_bank[i].size); - for (unsigned long i = 0UL; i < ARM_DRAM_BANKS_NUM; i++) { - check_sum += bank_ptr[i].base + bank_ptr[i].size; + bank_ptr[i].base = base; + bank_ptr[i].size = size; + + /* Update checksum */ + checksum += base + size; } - /* Check sum must be 0 */ - manifest->plat_dram.check_sum = ~check_sum + 1UL; + /* Checksum must be 0 */ + manifest->plat_dram.checksum = ~checksum + 1UL; return 0; } diff --git a/plat/arm/board/fvp/include/fconf_hw_config_getter.h b/plat/arm/board/fvp/include/fconf_hw_config_getter.h index ca85f7a70..b7a124726 100644 --- a/plat/arm/board/fvp/include/fconf_hw_config_getter.h +++ b/plat/arm/board/fvp/include/fconf_hw_config_getter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Arm Limited. All rights reserved. + * Copyright (c) 2020-2023, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,12 +8,16 @@ #define FCONF_HW_CONFIG_GETTER_H #include +#include + +#include /* Hardware Config related getter */ #define hw_config__gicv3_config_getter(prop) gicv3_config.prop #define hw_config__topology_getter(prop) soc_topology.prop #define hw_config__uart_serial_config_getter(prop) uart_serial_config.prop #define hw_config__cpu_timer_getter(prop) cpu_timer.prop +#define hw_config__dram_layout_getter(prop) dram_layout.prop struct gicv3_config_t { uint64_t gicd_base; @@ -36,13 +40,21 @@ struct cpu_timer_t { uint32_t clock_freq; }; +struct ns_dram_layout { + uint64_t num_banks; + struct ns_dram_bank dram_bank[ARM_DRAM_NUM_BANKS]; +}; + int fconf_populate_gicv3_config(uintptr_t config); int fconf_populate_topology(uintptr_t config); int fconf_populate_uart_config(uintptr_t config); int fconf_populate_cpu_timer(uintptr_t config); +int fconf_populate_dram_layout(uintptr_t config); extern struct gicv3_config_t gicv3_config; extern struct hw_topology_t soc_topology; extern struct uart_serial_config_t uart_serial_config; extern struct cpu_timer_t cpu_timer; +extern struct ns_dram_layout dram_layout; + #endif /* FCONF_HW_CONFIG_GETTER_H */ diff --git a/plat/arm/board/fvp/jmptbl.i b/plat/arm/board/fvp/jmptbl.i index 85e6e3a27..927ffefd3 100644 --- a/plat/arm/board/fvp/jmptbl.i +++ b/plat/arm/board/fvp/jmptbl.i @@ -1,5 +1,5 @@ # -# Copyright (c) 2018-2022, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2018-2023, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -21,6 +21,7 @@ fdt fdt_getprop_namelen fdt fdt_setprop_inplace fdt fdt_check_header fdt fdt_node_offset_by_compatible +fdt fdt_node_offset_by_prop_value fdt fdt_setprop_inplace_namelen_partial fdt fdt_first_subnode fdt fdt_next_subnode diff --git a/plat/arm/common/arm_bl2_setup.c b/plat/arm/common/arm_bl2_setup.c index ca9842285..b142b6241 100644 --- a/plat/arm/common/arm_bl2_setup.c +++ b/plat/arm/common/arm_bl2_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -23,7 +23,9 @@ #include #endif #include +#if ENABLE_RME #include +#endif #include #include @@ -127,7 +129,6 @@ void bl2_platform_setup(void) } #if ENABLE_RME - static void arm_bl2_plat_gpt_setup(void) { /* @@ -167,7 +168,6 @@ static void arm_bl2_plat_gpt_setup(void) panic(); } } - #endif /* ENABLE_RME */ /******************************************************************************* From 1db295cf4b716a410e8f0c365413df171074fb4f Mon Sep 17 00:00:00 2001 From: AlexeiFedorov Date: Wed, 18 Jan 2023 14:53:56 +0000 Subject: [PATCH 3/3] docs(rme): update RMM-EL3 Boot Manifest structure description This patch updates description of RMM-EL3 Boot Manifest structure and its corresponding diagram and tables with DRAM layout data. Signed-off-by: AlexeiFedorov Change-Id: I1b092bc1ad5f1c7909d25c1a0dc89c2b210ada27 --- docs/components/rmm-el3-comms-spec.rst | 99 +++++++++++++----- .../diagrams/rmm_el3_manifest_struct.dia | Bin 2589 -> 0 bytes .../diagrams/rmm_el3_manifest_struct.png | Bin 14167 -> 0 bytes 3 files changed, 71 insertions(+), 28 deletions(-) delete mode 100644 docs/resources/diagrams/rmm_el3_manifest_struct.dia delete mode 100644 docs/resources/diagrams/rmm_el3_manifest_struct.png diff --git a/docs/components/rmm-el3-comms-spec.rst b/docs/components/rmm-el3-comms-spec.rst index 25c426970..6b57c0e1f 100644 --- a/docs/components/rmm-el3-comms-spec.rst +++ b/docs/components/rmm-el3-comms-spec.rst @@ -53,7 +53,7 @@ are explained below: consistency with the versioning schemes used in other parts of RMM. This document specifies the 0.1 version of Boot Interface ABI and RMM-EL3 -services specification and the 0.1 version of the Boot Manifest. +services specification and the 0.2 version of the Boot Manifest. .. _rmm_el3_boot_interface: @@ -71,7 +71,7 @@ for configuration files. The Boot Interface ABI defines a set of register conventions and also a memory based manifest file to pass information from EL3 to RMM. The -boot manifest and the associated platform data in it can be dynamically created +Boot Manifest and the associated platform data in it can be dynamically created by EL3 and there is no restriction on how the data can be obtained (e.g by DTB, hoblist or other). @@ -99,7 +99,7 @@ During cold boot RMM expects the following register values: x0,Linear index of this PE. This index starts from 0 and must be less than the maximum number of CPUs to be supported at runtime (see x2). x1,Version for this Boot Interface as defined in :ref:`rmm_el3_ifc_versioning`. x2,Maximum number of CPUs to be supported at runtime. RMM should ensure that it can support this maximum number. - x3,Base address for the shared buffer used for communication between EL3 firmware and RMM. This buffer must be of 4KB size (1 page). The boot manifest must be present at the base of this shared buffer during cold boot. + x3,Base address for the shared buffer used for communication between EL3 firmware and RMM. This buffer must be of 4KB size (1 page). The Boot Manifest must be present at the base of this shared buffer during cold boot. During cold boot, EL3 firmware needs to allocate a 4KB page that will be passed to RMM in x3. This memory will be used as shared buffer for communication @@ -162,8 +162,8 @@ as per the following table: ``E_RMM_BOOT_CPUS_OUT_OF_RAGE``,Number of CPUs reported by EL3 larger than maximum supported by RMM,-3 ``E_RMM_BOOT_CPU_ID_OUT_OF_RAGE``,Current CPU Id is higher or equal than the number of CPUs supported by RMM,-4 ``E_RMM_BOOT_INVALID_SHARED_BUFFER``,Invalid pointer to shared memory area,-5 - ``E_RMM_BOOT_MANIFEST_VERSION_NOT_SUPPORTED``,Version reported by the boot manifest not supported by RMM,-6 - ``E_RMM_BOOT_MANIFEST_DATA_ERROR``,Error parsing core boot manifest,-7 + ``E_RMM_BOOT_MANIFEST_VERSION_NOT_SUPPORTED``,Version reported by the Boot Manifest not supported by RMM,-6 + ``E_RMM_BOOT_MANIFEST_DATA_ERROR``,Error parsing core Boot Manifest,-7 For any error detected in RMM during cold or warm boot, RMM will return back to EL3 using ``RMM_BOOT_COMPLETE`` SMC with an appropriate error code. It is @@ -177,25 +177,28 @@ warm boot by any PE should not enter RMM using the warm boot interface. Boot Manifest ~~~~~~~~~~~~~ -During cold boot, EL3 Firmware passes a memory boot manifest to RMM containing +During cold boot, EL3 Firmware passes a memory Boot Manifest to RMM containing platform information. -This boot manifest is versioned independently of the boot interface, to help -evolve the boot manifest independent of the rest of Boot Manifest. -The current version for the boot manifest is ``v0.1`` and the rules explained +This Boot Manifest is versioned independently of the Boot Interface, to help +evolve the former independent of the latter. +The current version for the Boot Manifest is ``v0.2`` and the rules explained in :ref:`rmm_el3_ifc_versioning` apply on this version as well. -The boot manifest is divided into two different components: +The Boot Manifest v0.2 has the following fields: - - Core Manifest: This is the generic parameters passed to RMM by EL3 common to all platforms. - - Platform data: This is defined by the platform owner and contains information specific to that platform. + - version : Version of the Manifest (v0.2) + - plat_data : Pointer to the platform specific data and not specified by this + document. These data are optional and can be NULL. + - plat_dram : Structure encoding the NS DRAM information on the platform. This + field is also optional and platform can choose to zero out this structure if + RMM does not need EL3 to send this information during the boot. -For the current version of the manifest, the core manifest contains a pointer -to the platform data. EL3 must ensure that the whole boot manifest, -including the platform data, if available, fits inside the RMM EL3 shared -buffer. +For the current version of the Boot Manifest, the core manifest contains a pointer +to the platform data. EL3 must ensure that the whole Boot Manifest, including +the platform data, if available, fits inside the RMM EL3 shared buffer. -For the type specification of the RMM Boot Manifest v0.1, refer to +For the data structure specification of Boot Manifest, refer to :ref:`rmm_el3_manifest_struct` .. _runtime_services_and_interface: @@ -525,19 +528,59 @@ _____ RMM-EL3 Boot Manifest structure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The RMM-EL3 Boot Manifest structure contains platform boot information passed -from EL3 to RMM. The width of the Boot Manifest is 128 bits - -.. image:: ../resources/diagrams/rmm_el3_manifest_struct.png +The RMM-EL3 Boot Manifest v0.2 structure contains platform boot information passed +from EL3 to RMM. The size of the Boot Manifest is 40 bytes. The members of the RMM-EL3 Boot Manifest structure are shown in the following table: -.. csv-table:: - :header: "Name", "Range", "Type", Description - :widths: 2 1 1 4 ++-----------+--------+----------------+----------------------------------------+ +| Name | Offset | Type | Description | ++===========+========+================+========================================+ +| version | 0 | uint32_t | Boot Manifest version | ++-----------+--------+----------------+----------------------------------------+ +| padding | 4 | uint32_t | Reserved, set to 0 | ++-----------+--------+----------------+----------------------------------------+ +| plat_data | 8 | uintptr_t | Pointer to Platform Data section | ++-----------+--------+----------------+----------------------------------------+ +| plat_dram | 16 | ns_dram_info | NS DRAM Layout Info structure | ++-----------+--------+----------------+----------------------------------------+ + +.. _ns_dram_info_struct: + +NS DRAM Layout Info structure +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +NS DRAM Layout Info structure contains information about platform Non-secure +DRAM layout. The members of this structure are shown in the table below: + ++-----------+--------+----------------+----------------------------------------+ +| Name | Offset | Type | Description | ++===========+========+================+========================================+ +| num_banks | 0 | uint64_t | Number of NS DRAM banks | ++-----------+--------+----------------+----------------------------------------+ +| banks | 8 | ns_dram_bank * | Pointer to 'ns_dram_bank'[] array | ++-----------+--------+----------------+----------------------------------------+ +| checksum | 16 | uint64_t | Checksum | ++-----------+--------+----------------+----------------------------------------+ + +Checksum is calculated as two's complement sum of 'num_banks', 'banks' pointer +and DRAM banks data array pointed by it. + +.. _ns_dram_bank_struct: + +NS DRAM Bank structure +~~~~~~~~~~~~~~~~~~~~~~ + +NS DRAM Bank structure contains information about each Non-secure DRAM bank: + ++-----------+--------+----------------+----------------------------------------+ +| Name | Offset | Type | Description | ++===========+========+================+========================================+ +| base | 0 | uintptr_t | Base address | ++-----------+--------+----------------+----------------------------------------+ +| size | 8 | uint64_t | Size of bank in bytes | ++-----------+--------+----------------+----------------------------------------+ + + - ``Version Minor``,15:0,uint16_t,Version Minor part of the Boot Manifest Version. - ``Version Major``,30:16,uint16_t,Version Major part of the Boot Manifest Version. - ``RES0``,31,bit,Reserved. Set to 0. - ``Platform Data``,127:64,Address,Pointer to the Platform Data section of the Boot Manifest. diff --git a/docs/resources/diagrams/rmm_el3_manifest_struct.dia b/docs/resources/diagrams/rmm_el3_manifest_struct.dia deleted file mode 100644 index 7b7a9c2ba2eee0c1436b0e3b2db48c866f11eb18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2589 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