From f6974712173d60830b7b8aa86b8ceac5a7cfd0c6 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 3 Jan 2018 14:32:33 -0700 Subject: ARM: Tegra186: mem parsing fixes from downstream Apply a few small fixes for the DTB /memory node parsing from NVIDIA's downstream U-Boot: - Allow arbitrary number of DRAM banks. - Correctly calculate the number of DRAM banks. - Clip PCIe memory in the same way as U-Boot CPU memory use. Signed-off-by: Stephen Warren Reviewed-by: Simon Glass Signed-off-by: Tom Warren --- arch/arm/mach-tegra/tegra186/nvtboot_mem.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'arch/arm/mach-tegra/tegra186/nvtboot_mem.c') diff --git a/arch/arm/mach-tegra/tegra186/nvtboot_mem.c b/arch/arm/mach-tegra/tegra186/nvtboot_mem.c index 5224ef641c..5a165a9d8e 100644 --- a/arch/arm/mach-tegra/tegra186/nvtboot_mem.c +++ b/arch/arm/mach-tegra/tegra186/nvtboot_mem.c @@ -16,10 +16,10 @@ extern unsigned long nvtboot_boot_x0; /* * A parsed version of /memory/reg from the DTB that is passed to U-Boot in x0. * - * We only support up to two banks since that's all the binary bootloader - * ever sets. We assume bank 0 is RAM below 4G and bank 1 is RAM above 4G. - * This is all a fairly safe assumption, since the L4T kernel makes the same - * assumptions, so the bootloader is unlikely to change. + * We assume bank 0 is RAM completely below 4G mostly ignore other banks; + * assuming they contain RAM above 4G. This is all a fairly safe assumption, + * since the L4T kernel makes the same assumption, so the bootloader is + * unlikely to change. * * This is written to before relocation, and hence cannot be in .bss, since * .bss overlaps the DTB that's appended to the U-Boot binary. The initializer @@ -29,7 +29,7 @@ extern unsigned long nvtboot_boot_x0; static struct { u64 start; u64 size; -} ram_banks[2] = {{1}}; +} ram_banks[CONFIG_NR_DRAM_BANKS] = {{1}}; int dram_init(void) { @@ -54,7 +54,9 @@ int dram_init(void) hang(); } - len /= (na + ns); + /* 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); @@ -76,11 +78,15 @@ int dram_init_banksize(void) { int i; - for (i = 0; i < 2; i++) { + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { gd->bd->bi_dram[i].start = ram_banks[i].start; gd->bd->bi_dram[i].size = ram_banks[i].size; } +#ifdef CONFIG_PCI + gd->pci_ram_top = gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size; +#endif + return 0; } -- cgit From d5859255d9799db8f18ac3487ec3ac9427503edb Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 3 Jan 2018 14:32:34 -0700 Subject: ARM: Tegra186: search for best RAM bank In the future, the list of DRAM regions passed to U-Boot in the DTB may be quite long and fragmented. Due to this, U-Boot must search through the regions to find the best region to relocate into, rather than relying on the current assumption that the top of bank 0 is a reasonable relocation target. This change implements such searching. Signed-off-by: Stephen Warren Reviewed-by: Simon Glass Signed-off-by: Tom Warren --- arch/arm/mach-tegra/tegra186/nvtboot_mem.c | 88 +++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 19 deletions(-) (limited to 'arch/arm/mach-tegra/tegra186/nvtboot_mem.c') diff --git a/arch/arm/mach-tegra/tegra186/nvtboot_mem.c b/arch/arm/mach-tegra/tegra186/nvtboot_mem.c index 5a165a9d8e..33e331f4ca 100644 --- a/arch/arm/mach-tegra/tegra186/nvtboot_mem.c +++ b/arch/arm/mach-tegra/tegra186/nvtboot_mem.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, NVIDIA CORPORATION. + * Copyright (c) 2016-2018, NVIDIA CORPORATION. * * SPDX-License-Identifier: GPL-2.0+ */ @@ -9,27 +9,51 @@ #include #include +#define SZ_4G 0x100000000ULL + +/* + * Size of a region that's large enough to hold the relocated U-Boot and all + * other allocations made around it (stack, heap, page tables, etc.) + * In practice, running "bdinfo" at the shell prompt, the stack reaches about + * 5MB from the address selected for ram_top as of the time of writing, + * so a 16MB region should be plenty. + */ +#define MIN_USABLE_RAM_SIZE SZ_16M +/* + * The amount of space we expect to require for stack usage. Used to validate + * that all reservations fit into the region selected for the relocation target + */ +#define MIN_USABLE_STACK_SIZE SZ_1M + DECLARE_GLOBAL_DATA_PTR; extern unsigned long nvtboot_boot_x0; /* - * A parsed version of /memory/reg from the DTB that is passed to U-Boot in x0. - * - * We assume bank 0 is RAM completely below 4G mostly ignore other banks; - * assuming they contain RAM above 4G. This is all a fairly safe assumption, - * since the L4T kernel makes the same assumption, so the bootloader is - * unlikely to change. - * - * This is written to before relocation, and hence cannot be in .bss, since - * .bss overlaps the DTB that's appended to the U-Boot binary. The initializer - * forces this into .data and avoids this issue. This also has the nice side- - * effect of the content being valid after relocation. + * These variables are written to before relocation, and hence cannot be + * in.bss, since .bss overlaps the DTB that's appended to the U-Boot binary. + * The section attribute forces this into .data and avoids this issue. This + * 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] = {{1}}; +} 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"))); + +/* + * The usable top-of-RAM for U-Boot. This is both: + * a) Below 4GB to avoid issues with peripherals that use 32-bit addressing. + * b) At the end of a region that has enough space to hold the relocated U-Boot + * and all other allocations made around it (stack, heap, page tables, etc.) + */ +static u64 ram_top __attribute__((section(".data"))); +/* The base address of the region of RAM that ends at ram_top */ +static u64 region_base __attribute__((section(".data"))); int dram_init(void) { @@ -59,32 +83,58 @@ int dram_init(void) len /= (na + ns); /* Convert cells to number of banks */ if (len > ARRAY_SIZE(ram_banks)) len = ARRAY_SIZE(ram_banks); + ram_bank_count = len; gd->ram_size = 0; - for (i = 0; i < len; i++) { + for (i = 0; i < ram_bank_count; i++) { + u64 bank_end, usable_bank_size; + ram_banks[i].start = fdt_read_number(prop, na); prop += na; ram_banks[i].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); + 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; + 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; + debug("ram top now %llx\n", ram_top); + } + } + if (!ram_top) { + pr_err("Can't find a usable RAM top"); + hang(); } return 0; } -extern unsigned long nvtboot_boot_x0; - int dram_init_banksize(void) { int i; - for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + if ((gd->start_addr_sp - region_base) < MIN_USABLE_STACK_SIZE) { + pr_err("Reservations exceed chosen region size"); + hang(); + } + + 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; } #ifdef CONFIG_PCI - gd->pci_ram_top = gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size; + gd->pci_ram_top = ram_top; #endif return 0; @@ -92,5 +142,5 @@ int dram_init_banksize(void) ulong board_get_usable_ram_top(ulong total_size) { - return ram_banks[0].start + ram_banks[0].size; + return ram_top; } -- cgit From cdcf55584e7e6b9be92a258d70d4d08a0216dbd0 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 4 Jan 2018 11:07:14 -0700 Subject: ARM: Tegra186: don't map memory not in RAM banks Tegra186 currently restricts its DRAM usage to entries in the /memory node in the DTB passed to it. However, the MMU configuration always maps the entire first 2GB of RAM. This could allow the CPU to speculatively access RAM that isn't part of the in-use banks. This patch switches to runtime construction of the table that's used to construct the MMU translation tables, and thus prevents access to RAM that's not part of a valid bank. Note: This patch is intended to prevent access to RAM regions which U-Boot does not need to access, with the primary purpose of avoiding theoretical speculative access to physical regions for which the HW will throw errors (e.g. carve-outs that the CPU has no permission to access at a bus level, bad ECC pages, etc.). In particular, this patch is not deliberately related to the speculation-related security issues that were recently announced. The apparent similarity is a coincidence. Signed-off-by: Stephen Warren Signed-off-by: Tom Warren --- arch/arm/mach-tegra/tegra186/nvtboot_mem.c | 78 ++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 25 deletions(-) (limited to 'arch/arm/mach-tegra/tegra186/nvtboot_mem.c') diff --git a/arch/arm/mach-tegra/tegra186/nvtboot_mem.c b/arch/arm/mach-tegra/tegra186/nvtboot_mem.c index 33e331f4ca..2ca59747d4 100644 --- a/arch/arm/mach-tegra/tegra186/nvtboot_mem.c +++ b/arch/arm/mach-tegra/tegra186/nvtboot_mem.c @@ -8,6 +8,7 @@ #include #include #include +#include #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 -- cgit