[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.