feat(plat/arm): improve dram bank lookup
Next generation neoverse reference designs, such as RD-V3, can have
more than two non-contiguous DRAM banks. On such platforms, these
banks are spread across the system memory map.
This change modifies the common platform granule handlers to more
efficiently translate address-to-index and index-to-address on
platforms with a large number of DRAM banks. To support this arm
platforms have now have a common config option to select the
maximum number of DRAM banks - PLAT_ARM_MAX_DRAM_BANKS.
Change-Id: I6d5b02f8d3052d6b189f21ef48d1f684acb63b86
Signed-off-by: Harry Moulton <harry.moulton@arm.com>
Signed-off-by: Vivek Gautam <vivek.gautam@arm.com>
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index 4718195..42c798d 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -291,7 +291,8 @@
RMM_CBMC_VIEWER_OUTPUT ,ON | OFF ,OFF ,"Generate report of CBMC results using the tool cbmc-viewer"
RMM_CBMC_SINGLE_TESTBENCH , ,OFF ,"Run CBMC on a single testbench instead on all of them"
RMM_CCA_DA ,ON | OFF ,OFF ,"Enable Device Assignment support in RMM (experimental)"
- ATTEST_PLAT_TOKEN_SIZE , ,0x1000 ,"Maximum size in bytes expected for the Attestation platform token"
+ ATTEST_PLAT_TOKEN_SIZE , ,0x1000 ,"Maximum size in bytes expected for the Attestation platform token"
+ PLAT_ARM_MAX_DRAM_BANKS , ,2 ,"Maximum number of DRAM banks allowed in Arm platform layer"
.. _llvm_build:
diff --git a/plat/arm/CMakeLists.txt b/plat/arm/CMakeLists.txt
index f06d63e..1360191 100644
--- a/plat/arm/CMakeLists.txt
+++ b/plat/arm/CMakeLists.txt
@@ -3,6 +3,15 @@
# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
#
+#
+# Additional config for maximum number of DRAM banks allowed to be managed
+#
+arm_config_option(
+ NAME PLAT_ARM_MAX_DRAM_BANKS
+ HELP "Maximum number of DRAM banks allowed in Arm platform layer"
+ TYPE STRING
+ DEFAULT 0x2)
+
add_library(rmm-plat-arm)
#
@@ -13,6 +22,13 @@
add_subdirectory("${RMM_SOURCE_DIR}/drivers/pl011" ${RMM_BINARY_DIR}/drivers/pl011)
add_subdirectory("${RMM_SOURCE_DIR}/plat/common" ${RMM_BINARY_DIR}/plat/common)
+if(PLAT_ARM_MAX_DRAM_BANKS EQUAL 0x0)
+ message(FATAL_ERROR "Invalid PLAT_ARM_MAX_DRAM_BANKS")
+endif()
+
+target_compile_definitions(rmm-plat-arm
+ PRIVATE "PLAT_ARM_MAX_DRAM_BANKS=U(${PLAT_ARM_MAX_DRAM_BANKS})")
+
target_link_libraries(rmm-plat-arm
PRIVATE rmm-driver-pl011
rmm-lib
diff --git a/plat/arm/src/arm_granule.c b/plat/arm/src/arm_granule.c
index 983b6ee..a674794 100644
--- a/plat/arm/src/arm_granule.c
+++ b/plat/arm/src/arm_granule.c
@@ -17,33 +17,86 @@
unsigned long plat_granule_addr_to_idx(unsigned long addr)
{
+ const struct arm_dram_layout *dram = &arm_dram;
+ unsigned long r, l = 0UL;
+
if (!GRANULE_ALIGNED(addr)) {
return UINT64_MAX;
}
- if ((addr >= arm_dram.arm_bank[0].start_addr) &&
- (addr <= arm_dram.arm_bank[0].end_addr)) {
- return (addr - arm_dram.arm_bank[0].start_addr) / GRANULE_SIZE;
- }
+ assert(dram->num_banks > 0UL);
+ assert(dram->num_banks <= PLAT_ARM_MAX_DRAM_BANKS);
+ r = dram->num_banks - 1UL;
- if ((arm_dram.arm_bank[1].start_addr != 0UL) &&
- (addr >= arm_dram.arm_bank[1].start_addr) &&
- (addr <= arm_dram.arm_bank[1].end_addr)) {
- return ((addr - arm_dram.arm_bank[1].start_addr) /
- GRANULE_SIZE) + arm_dram.idx_bank_1;
- }
+ /*
+ * Use a binary search rather than a linear one to locate the bank which
+ * the address falls within, then use the start_gran_idx (which is a
+ * cumulative idx from previous dram banks) to calculate the required
+ * granule index.
+ */
+ while (l <= r) {
+ const struct arm_dram_bank *bank;
+ unsigned long i;
+ i = l + ((r - l) / 2UL);
+ assert(i < PLAT_ARM_MAX_DRAM_BANKS);
+
+ bank = &dram->bank[i];
+
+ if (addr < bank->base) {
+ if (i == 0UL) {
+ break;
+ }
+ r = i - 1UL;
+ } else if (addr > (bank->base + bank->size - 1UL)) {
+ l = i + 1UL;
+ } else {
+ return (bank->start_gran_idx +
+ ((addr - bank->base) >> GRANULE_SHIFT));
+ }
+ }
return UINT64_MAX;
}
unsigned long plat_granule_idx_to_addr(unsigned long idx)
{
- assert(idx < arm_dram.num_granules);
+ const struct arm_dram_layout *dram = &arm_dram;
+ unsigned long r, l = 0UL, addr = 0UL;
- if (idx < arm_dram.idx_bank_1) {
- return arm_dram.arm_bank[0].start_addr + (idx * GRANULE_SIZE);
+ assert(dram->num_banks > 0UL);
+ assert(idx < dram->num_granules);
+
+ r = dram->num_banks - 1UL;
+
+ /*
+ * Calculate the start and end granule index of each bank using the
+ * start_gran_idx (which is a cumulative idx from previous dram banks)
+ * and then check whether the given index falls within it.
+ */
+ while (l <= r) {
+ const struct arm_dram_bank *bank;
+ unsigned long i;
+ unsigned long idx_start, idx_end;
+
+ i = l + ((r - l) / 2UL);
+ assert(i < PLAT_ARM_MAX_DRAM_BANKS);
+
+ bank = &dram->bank[i];
+
+ idx_start = bank->start_gran_idx;
+ idx_end = idx_start + (bank->size >> GRANULE_SHIFT) - 1UL;
+
+ if (idx < idx_start) {
+ assert(i != 0UL);
+ r = i - 1UL;
+ } else if (idx > idx_end) {
+ l = i + 1UL;
+ } else {
+ addr = bank->base + ((idx - idx_start) << GRANULE_SHIFT);
+ break;
+ }
}
-
- return arm_dram.arm_bank[1].start_addr +
- ((idx - arm_dram.idx_bank_1) * GRANULE_SIZE);
+ /* Assert that the search was successful */
+ assert(l <= r);
+ return addr;
}
diff --git a/plat/arm/src/arm_memory.c b/plat/arm/src/arm_memory.c
index f534572..c528f0d 100644
--- a/plat/arm/src/arm_memory.c
+++ b/plat/arm/src/arm_memory.c
@@ -8,42 +8,39 @@
#include <assert.h>
#include <rmm_el3_ifc.h>
-COMPILER_ASSERT(MAX_DRAM_NUM_BANKS == 2UL);
-
void arm_set_dram_layout(struct ns_dram_info *plat_dram)
{
- uint64_t num_banks, num_granules = 0UL;
struct ns_dram_bank *bank_ptr;
struct arm_dram_layout *dram_ptr = arm_get_dram_layout();
+ uint64_t num_banks, num_granules;
assert(!is_mmu_enabled());
/* Number of banks */
num_banks = plat_dram->num_banks;
- assert(num_banks <= MAX_DRAM_NUM_BANKS);
+ assert(num_banks > 0UL);
+ assert(num_banks <= PLAT_ARM_MAX_DRAM_BANKS);
/* Pointer to dram_bank[] array */
bank_ptr = plat_dram->banks;
+ num_granules = 0UL;
+
for (unsigned long i = 0UL; i < num_banks; i++) {
- uintptr_t start = bank_ptr->base;
+ uint64_t base = bank_ptr->base;
uint64_t size = bank_ptr->size;
- uintptr_t end = start + size - 1UL;
- if (i == 1UL) {
- /* Start granule index in bank 1 */
- dram_ptr->idx_bank_1 = num_granules;
- }
+ dram_ptr->bank[i].base = base;
+ dram_ptr->bank[i].size = size;
+ dram_ptr->bank[i].start_gran_idx = num_granules;
- /* Total number of granules */
- num_granules += (size / GRANULE_SIZE);
-
- dram_ptr->arm_bank[i].start_addr = start;
- dram_ptr->arm_bank[i].end_addr = end;
-
+ num_granules += (size >> GRANULE_SHIFT);
bank_ptr++;
}
+ assert(num_granules <= RMM_MAX_GRANULES);
+
+ dram_ptr->num_banks = num_banks;
dram_ptr->num_granules = num_granules;
inv_dcache_range((uintptr_t)dram_ptr, sizeof(struct arm_dram_layout));
diff --git a/plat/arm/src/arm_setup.c b/plat/arm/src/arm_setup.c
index e17dccf..ef58384 100644
--- a/plat/arm/src/arm_setup.c
+++ b/plat/arm/src/arm_setup.c
@@ -112,7 +112,7 @@
* to the platform DRAM info structure
*/
ret = rmm_el3_ifc_get_dram_data_validated_pa(
- MAX_DRAM_NUM_BANKS,
+ PLAT_ARM_MAX_DRAM_BANKS,
&plat_dram);
if (ret != E_RMM_BOOT_SUCCESS) {
ERROR("DRAM data error\n");
diff --git a/plat/arm/src/include/arm_dram.h b/plat/arm/src/include/arm_dram.h
index 468bc94..0103599 100644
--- a/plat/arm/src/include/arm_dram.h
+++ b/plat/arm/src/include/arm_dram.h
@@ -10,19 +10,19 @@
#include <stddef.h>
#include <stdint.h>
-/* Maximum number of DRAM banks supported */
-#define MAX_DRAM_NUM_BANKS 2UL
-
-/* Arm runtime structures */
+/* Arm platform dram management structures */
struct arm_dram_bank {
- uintptr_t start_addr; /* start address */
- uintptr_t end_addr; /* end address */
+ uint64_t base; /* bank base address */
+ uint64_t size; /* size of this bank */
+ /* This idx is a cumulative granule count of previous banks */
+ uint64_t start_gran_idx; /* Start granule index for this bank */
};
struct arm_dram_layout {
- unsigned long idx_bank_1; /* start granule index in bank 1 */
unsigned long num_granules; /* number of granules */
- struct arm_dram_bank arm_bank[MAX_DRAM_NUM_BANKS];
+ unsigned long num_banks; /* number of dram banks */
+ struct arm_dram_bank bank[PLAT_ARM_MAX_DRAM_BANKS];
+ /* Sorted array of DRAM banks */
};
void arm_set_dram_layout(struct ns_dram_info *plat_dram);