diff --git a/arch/arm/mach-tegra/arm64-mmu.c b/arch/arm/mach-tegra/arm64-mmu.c
index a79a5192e076910e294d6e07f14fec21b8282c2e..3a126bdec4ccfb097e551b6605a312fbe252ddd1 100644
--- a/arch/arm/mach-tegra/arm64-mmu.c
+++ b/arch/arm/mach-tegra/arm64-mmu.c
@@ -12,7 +12,8 @@
 #include <asm/system.h>
 #include <asm/armv8/mmu.h>
 
-static struct mm_region tegra_mem_map[] = {
+/* size: IO + NR_DRAM_BANKS + terminator */
+struct mm_region tegra_mem_map[1 + CONFIG_NR_DRAM_BANKS + 1] = {
 	{
 		.virt = 0x0UL,
 		.phys = 0x0UL,
diff --git a/arch/arm/mach-tegra/tegra186/nvtboot_mem.c b/arch/arm/mach-tegra/tegra186/nvtboot_mem.c
index 33e331f4ca87c4bf348bc3cb1c1ca2d9afcd03b8..2ca59747d4ca4bafb0a0503170582c62ed7d54ca 100644
--- a/arch/arm/mach-tegra/tegra186/nvtboot_mem.c
+++ b/arch/arm/mach-tegra/tegra186/nvtboot_mem.c
@@ -8,6 +8,7 @@
 #include <fdt_support.h>
 #include <fdtdec.h>
 #include <asm/arch/tegra.h>
+#include <asm/armv8/mmu.h>
 
 #define SZ_4G 0x100000000ULL
 
@@ -28,6 +29,7 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 extern unsigned long nvtboot_boot_x0;
+extern struct mm_region tegra_mem_map[];
 
 /*
  * These variables are written to before relocation, and hence cannot be
@@ -36,12 +38,6 @@ extern unsigned long nvtboot_boot_x0;
  * also has the nice side-effect of the content being valid after relocation.
  */
 
-/* A parsed version of /memory/reg from the DTB passed to U-Boot in x0 */
-static struct {
-	u64 start;
-	u64 size;
-} ram_banks[CONFIG_NR_DRAM_BANKS] __attribute__((section(".data")));
-
 /* The number of valid entries in ram_banks[] */
 static int ram_bank_count __attribute__((section(".data")));
 
@@ -62,8 +58,6 @@ int dram_init(void)
 	int node, len, i;
 	const u32 *prop;
 
-	memset(ram_banks, 0, sizeof(ram_banks));
-
 	na = fdtdec_get_uint(nvtboot_blob, 0, "#address-cells", 2);
 	ns = fdtdec_get_uint(nvtboot_blob, 0, "#size-cells", 2);
 
@@ -81,36 +75,70 @@ int dram_init(void)
 	/* Calculate the true # of base/size pairs to read */
 	len /= 4;		/* Convert bytes to number of cells */
 	len /= (na + ns);	/* Convert cells to number of banks */
-	if (len > ARRAY_SIZE(ram_banks))
-		len = ARRAY_SIZE(ram_banks);
-	ram_bank_count = len;
+	if (len > CONFIG_NR_DRAM_BANKS)
+		len = CONFIG_NR_DRAM_BANKS;
 
+	/* Parse the /memory node, and save useful entries */
 	gd->ram_size = 0;
-	for (i = 0; i < ram_bank_count; i++) {
-		u64 bank_end, usable_bank_size;
+	ram_bank_count = 0;
+	for (i = 0; i < len; i++) {
+		u64 bank_start, bank_end, bank_size, usable_bank_size;
 
-		ram_banks[i].start = fdt_read_number(prop, na);
+		/* Extract raw memory region data from DTB */
+		bank_start = fdt_read_number(prop, na);
 		prop += na;
-		ram_banks[i].size = fdt_read_number(prop, ns);
+		bank_size = fdt_read_number(prop, ns);
 		prop += ns;
-		gd->ram_size += ram_banks[i].size;
-		debug("Bank %d: start: %llx size: %llx\n", i,
-		      ram_banks[i].start, ram_banks[i].size);
-
-		bank_end = ram_banks[i].start + ram_banks[i].size;
-		debug("  end  %llx\n", bank_end);
+		gd->ram_size += bank_size;
+		bank_end = bank_start + bank_size;
+		debug("Bank %d: %llx..%llx (+%llx)\n", i,
+		      bank_start, bank_end, bank_size);
+
+		/*
+		 * Align the bank to MMU section size. This is not strictly
+		 * necessary, since the translation table construction code
+		 * handles page granularity without issue. However, aligning
+		 * the MMU entries reduces the size and number of levels in the
+		 * page table, so is worth it.
+		 */
+		bank_start = ROUND(bank_start, SZ_2M);
+		bank_end = bank_end & ~(SZ_2M - 1);
+		bank_size = bank_end - bank_start;
+		debug("  aligned: %llx..%llx (+%llx)\n",
+		      bank_start, bank_end, bank_size);
+		if (bank_end <= bank_start)
+			continue;
+
+		/* Record data used to create MMU translation tables */
+		ram_bank_count++;
+		/* Index below is deliberately 1-based to skip MMIO entry */
+		tegra_mem_map[ram_bank_count].virt = bank_start;
+		tegra_mem_map[ram_bank_count].phys = bank_start;
+		tegra_mem_map[ram_bank_count].size = bank_size;
+		tegra_mem_map[ram_bank_count].attrs =
+			PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE;
+
+		/* Determine best bank to relocate U-Boot into */
 		if (bank_end > SZ_4G)
 			bank_end = SZ_4G;
 		debug("  end  %llx (usable)\n", bank_end);
-		usable_bank_size = bank_end - ram_banks[i].start;
+		usable_bank_size = bank_end - bank_start;
 		debug("  size %llx (usable)\n", usable_bank_size);
 		if ((usable_bank_size >= MIN_USABLE_RAM_SIZE) &&
 		    (bank_end > ram_top)) {
 			ram_top = bank_end;
-			region_base = ram_banks[i].start;
+			region_base = bank_start;
 			debug("ram top now %llx\n", ram_top);
 		}
 	}
+
+	/* Ensure memory map contains the desired sentinel entry */
+	tegra_mem_map[ram_bank_count + 1].virt = 0;
+	tegra_mem_map[ram_bank_count + 1].phys = 0;
+	tegra_mem_map[ram_bank_count + 1].size = 0;
+	tegra_mem_map[ram_bank_count + 1].attrs = 0;
+
+	/* Error out if a relocation target couldn't be found */
 	if (!ram_top) {
 		pr_err("Can't find a usable RAM top");
 		hang();
@@ -129,8 +157,8 @@ int dram_init_banksize(void)
 	}
 
 	for (i = 0; i < ram_bank_count; i++) {
-		gd->bd->bi_dram[i].start = ram_banks[i].start;
-		gd->bd->bi_dram[i].size = ram_banks[i].size;
+		gd->bd->bi_dram[i].start = tegra_mem_map[1 + i].virt;
+		gd->bd->bi_dram[i].size = tegra_mem_map[1 + i].size;
 	}
 
 #ifdef CONFIG_PCI