[PATCH] arm: mvebu: armada8k: do not offer the ATF/TEE region as free memory
Bruno Banelli <[email protected]>
| Newsgroups | org.u-boot-project.lists.u-boot |
|---|---|
| Message-ID | <[email protected]> |
mvebu_mem_map[] deliberately leaves the ATF and TEE region unmapped:
{
/* RAM 0-64MB */
.phys = 0x0UL,
.size = ATF_REGION_START,
...
},
/* ATF and TEE region 0x4000000-0x5400000 not mapped */
{
/* RAM 66MB-2GB */
.phys = ATF_REGION_END,
...
},
Nothing tells LMB about the hole, so the whole of DRAM bank 0 is added
as available memory (sizes elided from the dumps below for width):
lmb_dump_all:
memory.count = 0x2
memory[0] [0x0-0xbfffffff], flags: none
memory[1] [0x100000000-0x43fffffff], flags: none
reserved.count = 0x4
reserved[0] [0x4000000-0x41fffff], flags: no-map
reserved[1] [0x7eb1d000-0x7eb1ffff], flags: no-notify, no-overwrite
reserved[2] [0x7eb20f40-0xbfffffff], flags: no-overwrite
reserved[3] [0x100000000-0x43fffffff], flags: no-overwrite
The only part of the hole that is reserved is the 2 MiB psci-area from
armada-ap80x.dtsi. The remaining 18 MiB is allocatable, and since
lmb_add() notifies the EFI allocator, it is also published to EFI
payloads as EFI_CONVENTIONAL_MEMORY. Any access to it faults:
=> md 0x4200000 4
"Synchronous Abort" handler, esr 0x96000006, far 0x4200000
Resetting CPU ...
ESR 0x96000006 is a data abort at the current exception level with
DFSC 0x06, a level 2 translation fault - there is no mapping, which is
exactly what mvebu_mem_map[] intends. The same abort with WnR set
(esr 0x96000046) is what MemTest86 v11 for ARM64 hits on its first
write, because the EFI memory map it is handed says:
0x000004000000 - 0x0000041FFFFF (2MB) {Reserved Memory}
0x000004200000 - 0x000006FFFFFF (46MB) {Free Memory}
Add an lmb_arch_add_memory() for Armada 8k that adds each DRAM bank
around the ATF and TEE region instead of over it, and select
LMB_ARCH_MEM_MAP for ARMADA_8K. The hook replaces the generic
lmb_add_memory() rather than extending it, so it also has to reproduce
the LMB_LIMIT_DMA_BELOW_RAM_TOP reservation above gd->ram_top; both are
needed on this SoC, which maps only the first 2 GiB of DRAM.
After the change:
lmb_dump_all:
memory.count = 0x3
memory[0] [0x0-0x3ffffff], flags: none
memory[1] [0x5400000-0xbfffffff], flags: none
memory[2] [0x100000000-0x43fffffff], flags: none
reserved.count = 0x3
reserved[0] [0x7eb1d000-0x7eb1ffff], flags: no-notify, no-overwrite
reserved[1] [0x7eb20f40-0xbfffffff], flags: no-overwrite
reserved[2] [0x100000000-0x43fffffff], flags: no-overwrite
=> md 0x4200000 4
"Synchronous Abort" handler, esr 0x96000006, far 0x4200000
=> md 0x5400000 4
05400000: 00000000 ffffffff 00000000 ffdfffff ................
and MemTest86 completes passes instead of aborting.
Note the psci-area reservation disappears from the dump: the region is
no longer part of the LMB memory map, so boot_fdt_reserve_region() gets
-EINVAL back from lmb_alloc_mem() and skips it. That is harmless - the
memory is not allocatable either way - but it is a visible change in
"bdinfo" output.
board_get_usable_ram_top() already clamps gd->ram_top to the 2 GiB that
mvebu_mem_map[] maps, for the same underlying reason. A ram_top limit
cannot express a hole in the middle of a bank, hence this patch.
Tested on a SolidRun MACCHIATObin (Armada 8040) with 16 GiB of DRAM.
Signed-off-by: Bruno Banelli <[email protected]>
---
arch/arm/mach-mvebu/armada8k/cpu.c | 89 ++++++++++++++++++++++++++++++
lib/Kconfig | 2 +-
2 files changed, 90 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-mvebu/armada8k/cpu.c b/arch/arm/mach-mvebu/armada8k/cpu.c
index 220b32dd02..666be973a7 100644
--- a/arch/arm/mach-mvebu/armada8k/cpu.c
+++ b/arch/arm/mach-mvebu/armada8k/cpu.c
@@ -6,8 +6,11 @@
#include <cpu_func.h>
#include <dm.h>
#include <fdtdec.h>
+#include <lmb.h>
+#include <log.h>
#include <linux/libfdt.h>
#include <linux/sizes.h>
+#include <asm/global_data.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/arch/cpu.h>
@@ -17,6 +20,8 @@
#include "soc_info.h"
+DECLARE_GLOBAL_DATA_PTR;
+
/* Armada 7k/8k */
#define MVEBU_RFU_BASE (MVEBU_REGISTER(0x6f0000))
#define RFU_GLOBAL_SW_RST (MVEBU_RFU_BASE + 0x84)
@@ -61,6 +66,90 @@ static struct mm_region mvebu_mem_map[] = {
struct mm_region *mem_map = mvebu_mem_map;
+#if CONFIG_IS_ENABLED(LMB_ARCH_MEM_MAP)
+/**
+ * mvebu_lmb_reserve() - mark a region as present but not allocatable
+ * @base: start of the region
+ * @size: size of the region
+ */
+static void mvebu_lmb_reserve(phys_addr_t base, phys_size_t size)
+{
+ phys_addr_t addr = base;
+
+ if (lmb_alloc_mem(LMB_MEM_ALLOC_ADDR, 0, &addr, size, LMB_NOOVERWRITE))
+ log_err("Failed to reserve 0x%llx bytes at 0x%llx\n",
+ (unsigned long long)size, (unsigned long long)base);
+}
+
+/**
+ * mvebu_lmb_add_bank() - add one memory range, honouring ram_top
+ * @base: start of the range
+ * @size: size of the range
+ * @ram_top: highest address U-Boot may allocate from
+ *
+ * The reservation above @ram_top mirrors the generic lmb_add_memory(), which
+ * this hook replaces.
+ */
+static void mvebu_lmb_add_bank(phys_addr_t base, phys_size_t size, u64 ram_top)
+{
+ phys_addr_t bank_end = base + size;
+
+ lmb_add(base, size);
+
+ if (!IS_ENABLED(CONFIG_LMB_LIMIT_DMA_BELOW_RAM_TOP))
+ return;
+
+ if (base >= ram_top)
+ mvebu_lmb_reserve(base, size);
+ else if (bank_end > ram_top)
+ mvebu_lmb_reserve(ram_top, bank_end - ram_top);
+}
+
+/**
+ * lmb_arch_add_memory() - add DRAM to LMB, minus the ATF and TEE region
+ *
+ * mvebu_mem_map[] above deliberately has no entry for ATF_REGION_START to
+ * ATF_REGION_END, so U-Boot has no translation for that range and any access
+ * to it takes a translation fault. It must not be handed to LMB either:
+ * everything LMB holds as available is published to EFI payloads as
+ * EFI_CONVENTIONAL_MEMORY, and the first payload to use it aborts.
+ */
+void lmb_arch_add_memory(void)
+{
+ phys_addr_t bank_start, bank_end;
+ u64 ram_top = gd->ram_top;
+ int i;
+
+ /* Assume a 4GB ram_top if not defined */
+ if (!ram_top)
+ ram_top = 0x100000000ULL;
+
+ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
+ if (!gd->dram[i].size)
+ continue;
+
+ bank_start = gd->dram[i].start;
+ bank_end = bank_start + gd->dram[i].size;
+
+ if (bank_end <= ATF_REGION_START ||
+ bank_start >= ATF_REGION_END) {
+ mvebu_lmb_add_bank(bank_start, gd->dram[i].size,
+ ram_top);
+ continue;
+ }
+
+ if (bank_start < ATF_REGION_START)
+ mvebu_lmb_add_bank(bank_start,
+ ATF_REGION_START - bank_start,
+ ram_top);
+ if (bank_end > ATF_REGION_END)
+ mvebu_lmb_add_bank(ATF_REGION_END,
+ bank_end - ATF_REGION_END,
+ ram_top);
+ }
+}
+#endif
+
void enable_caches(void)
{
icache_enable();
diff --git a/lib/Kconfig b/lib/Kconfig
index 24e55ade4d..6b6b90bc91 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -1295,7 +1295,7 @@ config SPL_LMB
config LMB_ARCH_MEM_MAP
bool
depends on LMB
- default y if FSL_LAYERSCAPE || X86
+ default y if ARMADA_8K || FSL_LAYERSCAPE || X86
help
Some architectures have special or unique aspects which need
consideration when adding memory ranges to the list of available
--
2.43.0