[PATCH v4 1/2] mach-k3: add runtime memory carveouts for MMU table
Andrew Davis
afd at ti.com
Fri Jun 27 20:06:50 CEST 2025
On 6/18/25 7:42 AM, Anshul Dalal wrote:
> In u-boot we only provide a single MMU table for all k3 platforms,
> this does not scale for devices with reserved memory outside the range
> 0x9e780000 - 0xa0000000 or for devices with < 2GiB of memory (eg
> am62-SIP with 512MiB of RAM).
>
> To properly configure the MMU on various k3 platforms, the
> reserved-memory regions need to be queried at runtime from the
> device-tree and the MMU table should be updated accordingly.
>
> This patch adds the required fixups to the MMU table (during proper
> U-boot stage) by marking the reserved regions as non cacheable and
> keeping the remaining area as cacheable.
>
> For the A-core SPL, the 128MiB region starting from SPL_TEXT_BASE
> is marked as cacheable i.e 0x80080000 to 0x88080000.
>
> The 128MiB size is chosen to allow for future use cases such as falcon
> boot from the A-Core SPL which would require loading kernel image from
> the SPL stage. This change also ensures the reserved memory regions that
> all exist past 0x88080000 are non cacheable preventing speculative
> accesses to those addresses.
>
> Signed-off-by: Anshul Dalal <anshuld at ti.com>
> ---
> Changes for v4:
> - Add call to k3_mem_map_init for beagleplay
> - Mark reserved regions as non-cacheable
In v2 I said you cannot mark any regions as non-caching. How did this
sneak back in? If the reserved memory region is marked in DT as "no-map"
it must not be mapped at all. So NAK.
Andrew
> - More debug logs
>
> v3: https://lore.kernel.org/u-boot/20250617135844.2873701-1-anshuld@ti.com/
>
> Changes for v3:
> - Remove unused memory regions in SPL's map
> - Add runtime addition of MMU entry for the framebuffer in SPL
> - Refactor k3_mem_map_init to use standard u-boot APIs
> - Unmap reserved-memory regions instead of keeping them uncached
>
> v2: https://lore.kernel.org/u-boot/20250610160833.1705534-1-anshuld@ti.com/
>
> Changes in v2:
> - Removed dependency to:
> https://lore.kernel.org/u-boot/20250522150941.563959-1-anshuld@ti.com/
>
> v1: https://lore.kernel.org/u-boot/20250602120054.1466951-1-anshuld@ti.com/
> ---
> arch/arm/mach-k3/arm64/arm64-mmu.c | 212 +++++++++++++++++++++++--
> arch/arm/mach-k3/include/mach/k3-ddr.h | 9 ++
> board/beagle/beagleplay/beagleplay.c | 12 ++
> board/ti/common/k3-ddr.c | 10 ++
> 4 files changed, 230 insertions(+), 13 deletions(-)
>
> diff --git a/arch/arm/mach-k3/arm64/arm64-mmu.c b/arch/arm/mach-k3/arm64/arm64-mmu.c
> index 0e07b1b7ce0..135f842556d 100644
> --- a/arch/arm/mach-k3/arm64/arm64-mmu.c
> +++ b/arch/arm/mach-k3/arm64/arm64-mmu.c
> @@ -11,9 +11,16 @@
>
> #include <asm/system.h>
> #include <asm/armv8/mmu.h>
> +#include <mach/k3-ddr.h>
> +#include <sort.h>
>
> -struct mm_region k3_mem_map[] = {
> +#include "../common_fdt.h"
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +struct mm_region k3_mem_map[K3_MMU_REGIONS_COUNT] = {
> {
> + /* Peripherals */
> .virt = 0x0UL,
> .phys = 0x0UL,
> .size = 0x80000000UL,
> @@ -21,30 +28,27 @@ struct mm_region k3_mem_map[] = {
> PTE_BLOCK_NON_SHARE |
> PTE_BLOCK_PXN | PTE_BLOCK_UXN
> }, {
> - .virt = 0x80000000UL,
> - .phys = 0x80000000UL,
> - .size = 0x1e780000UL,
> - .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
> - PTE_BLOCK_INNER_SHARE
> - }, {
> - .virt = 0xa0000000UL,
> - .phys = 0xa0000000UL,
> - .size = 0x60000000UL,
> - .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
> - PTE_BLOCK_INNER_SHARE
> - }, {
> + /* Higher DDR banks */
> .virt = 0x880000000UL,
> .phys = 0x880000000UL,
> .size = 0x80000000UL,
> .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
> PTE_BLOCK_INNER_SHARE
> }, {
> + /* Flash peripherals */
> .virt = 0x500000000UL,
> .phys = 0x500000000UL,
> .size = 0x380000000UL,
> .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
> PTE_BLOCK_NON_SHARE |
> PTE_BLOCK_PXN | PTE_BLOCK_UXN
> + }, {
> + /* Map SPL load region and the next 128MiB as cacheable */
> + .virt = CONFIG_SPL_TEXT_BASE,
> + .phys = CONFIG_SPL_TEXT_BASE,
> + .size = SZ_128M,
> + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
> + PTE_BLOCK_INNER_SHARE
> }, {
> /* List terminator */
> 0,
> @@ -52,3 +56,185 @@ struct mm_region k3_mem_map[] = {
> };
>
> struct mm_region *mem_map = k3_mem_map;
> +
> +static int dt_reserved_cmp(const void *a, const void *b)
> +{
> + const struct fdt_resource *val_a = a, *val_b = b;
> +
> + return (val_a->start - val_b->start);
> +}
> +
> +int k3_mem_map_init(void)
> +{
> + fdt_addr_t mem_base;
> + fdt_size_t mem_size;
> + struct fdt_resource dt_reserved[K3_MMU_REGIONS_COUNT],
> + coalesced[K3_MMU_REGIONS_COUNT];
> + int k3_map_idx = -EINVAL, ret, nodeoffset, subnode;
> + void *blob = (void *)gd->fdt_blob;
> + unsigned int carveout_len, i, j;
> +
> + ret = fdt_fixup_reserved(blob, "tfa", CONFIG_K3_ATF_LOAD_ADDR, 0x80000);
> + if (ret) {
> + pr_err("%s: Failed to fixup reserved node for tfa [%d]\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + ret = fdt_fixup_reserved(blob, "optee", CONFIG_K3_OPTEE_LOAD_ADDR,
> + 0x1800000);
> + if (ret) {
> + pr_err("%s: Failed to fixup reserved node for optee [%d]\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + nodeoffset = fdt_subnode_offset(blob, 0, "memory");
> + if (nodeoffset < 0) {
> + pr_err("%s: Failed to get memory data: %s\n", __func__,
> + fdt_strerror(nodeoffset));
> + return nodeoffset;
> + }
> +
> + mem_base = fdtdec_get_addr_size(blob, nodeoffset, "reg", &mem_size);
> + if (mem_base != CFG_SYS_SDRAM_BASE)
> + return -EINVAL;
> +
> + for (i = 0; i < K3_MMU_REGIONS_COUNT; i++) {
> + if (k3_mem_map[i].virt == CONFIG_SPL_TEXT_BASE) {
> + k3_map_idx = i;
> + break;
> + }
> + }
> +
> + if (k3_map_idx == -EINVAL) {
> + pr_err("%s: Failed to find DDR region in MMU memory map\n",
> + __func__);
> + return -EINVAL;
> + }
> +
> + i = 0;
> + nodeoffset = fdt_subnode_offset(blob, 0, "reserved-memory");
> + fdt_for_each_subnode(subnode, blob, nodeoffset) {
> + const char *name;
> + fdt_addr_t addr, end_addr;
> + fdt_size_t size;
> +
> + if (i >= K3_MMU_REGIONS_COUNT) {
> + /*
> + * This is a recoverable error if the regions can be
> + * coalesced, the required logic can be implemented once
> + * requirement arises.
> + */
> + pr_err("%s: Not enough space in MMU map for carveouts\n",
> + __func__);
> + return -ENOMEM;
> + }
> +
> + name = fdt_get_name(blob, subnode, NULL);
> + addr = fdtdec_get_addr_size(blob, subnode, "reg", &size);
> +
> + if (addr == FDT_ADDR_T_NONE)
> + continue;
> +
> + if (!fdtdec_get_bool(blob, subnode, "no-map"))
> + continue;
> +
> + if (addr >= mem_base + mem_size)
> + continue;
> +
> + end_addr = addr + size;
> +
> + if (end_addr <= mem_base)
> + continue;
> +
> + debug("Added memory carveout at 0x%llx, size: 0x%llx for '%s'\n",
> + addr, size, name);
> +
> + addr = max(addr, mem_base);
> + end_addr = min(end_addr, mem_base + mem_size);
> + size = end_addr - addr;
> + dt_reserved[i].start = addr;
> + dt_reserved[i].end = end_addr;
> + i++;
> + }
> + carveout_len = i;
> +
> + if (!carveout_len)
> + return 0;
> +
> + /* sort carveout regions by address required for creating carveouts */
> + qsort(dt_reserved, carveout_len, sizeof(dt_reserved[0]),
> + dt_reserved_cmp);
> +
> + /* coalesce regions */
> + struct fdt_resource coalescing_temp = dt_reserved[0];
> +
> + j = 0;
> + for (i = 1; i < carveout_len; i++) {
> + struct fdt_resource current = dt_reserved[i];
> +
> + if (coalescing_temp.end >= current.start) {
> + coalescing_temp.end = current.end;
> + continue;
> + }
> + coalesced[j] = coalescing_temp;
> + coalescing_temp = current;
> + j++;
> + }
> +
> + coalesced[j] = coalescing_temp;
> + carveout_len = j + 1;
> +
> + if (coalesced[0].start != mem_base) {
> + k3_mem_map[k3_map_idx].virt = mem_base;
> + k3_mem_map[k3_map_idx].phys = mem_base;
> + k3_mem_map[k3_map_idx].size = coalesced[0].start - mem_base;
> + k3_mem_map[k3_map_idx].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
> + PTE_BLOCK_INNER_SHARE;
> + k3_map_idx++;
> + }
> +
> + for (i = 1; i < carveout_len; i++) {
> + k3_mem_map[k3_map_idx].virt = coalesced[i - 1].end;
> + k3_mem_map[k3_map_idx].phys = coalesced[i - 1].end;
> + k3_mem_map[k3_map_idx].size =
> + coalesced[i].start - coalesced[i - 1].end;
> + k3_mem_map[k3_map_idx].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
> + PTE_BLOCK_INNER_SHARE;
> + k3_map_idx++;
> + }
> +
> + k3_mem_map[k3_map_idx].virt = coalesced[carveout_len - 1].end;
> + k3_mem_map[k3_map_idx].phys = coalesced[carveout_len - 1].end;
> + k3_mem_map[k3_map_idx].size =
> + mem_base + mem_size - coalesced[carveout_len - 1].end;
> + k3_mem_map[k3_map_idx].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
> + PTE_BLOCK_INNER_SHARE;
> + k3_map_idx++;
> +
> + /* map reserved memory as non cachable */
> + for (i = 0; i < carveout_len; i++) {
> + k3_mem_map[k3_map_idx].virt = coalesced[i].start;
> + k3_mem_map[k3_map_idx].phys = coalesced[i].start;
> + k3_mem_map[k3_map_idx].size =
> + coalesced[i].end - coalesced[i].start;
> + k3_mem_map[k3_map_idx].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL_NC) |
> + PTE_BLOCK_INNER_SHARE;
> + k3_map_idx++;
> + }
> +
> + debug("%s: MMU Table configured as:\n", __func__);
> + debug(" |virt start\t\t|virt end\t|phys\t\t|size\t\t|attrs:\n");
> + for (i = 0; i < k3_map_idx; i++) {
> + debug("%2d: 0x%-12llx\t0x%-12llx\t0x%-12llx\t0x%-12llx\t0x%llx\n",
> + i, k3_mem_map[i].virt,
> + k3_mem_map[i].virt + k3_mem_map[i].size,
> + k3_mem_map[i].phys, k3_mem_map[i].size,
> + k3_mem_map[i].attrs);
> + }
> +
> + k3_mem_map[k3_map_idx] = (const struct mm_region){ 0 };
> +
> + return 0;
> +}
> diff --git a/arch/arm/mach-k3/include/mach/k3-ddr.h b/arch/arm/mach-k3/include/mach/k3-ddr.h
> index 39e6725bb9b..0b164ebf5e6 100644
> --- a/arch/arm/mach-k3/include/mach/k3-ddr.h
> +++ b/arch/arm/mach-k3/include/mach/k3-ddr.h
> @@ -8,10 +8,19 @@
>
> #include <spl.h>
>
> +/* Number of mappable regions in the MMU page table */
> +#define K3_MMU_REGIONS_COUNT 32
> +
> int dram_init(void);
> int dram_init_banksize(void);
>
> void fixup_ddr_driver_for_ecc(struct spl_image_info *spl_image);
> void fixup_memory_node(struct spl_image_info *spl_image);
>
> +/*
> + * Modifies the MMU memory map based on DDR size and reserved-memory
> + * nodes in DT
> + */
> +int k3_mem_map_init(void);
> +
> #endif /* _K3_DDR_H_ */
> diff --git a/board/beagle/beagleplay/beagleplay.c b/board/beagle/beagleplay/beagleplay.c
> index 78635810585..031040fe705 100644
> --- a/board/beagle/beagleplay/beagleplay.c
> +++ b/board/beagle/beagleplay/beagleplay.c
> @@ -13,6 +13,7 @@
> #include <spl.h>
>
> #include <asm/arch/hardware.h>
> +#include <mach/k3-ddr.h>
>
> DECLARE_GLOBAL_DATA_PTR;
>
> @@ -48,6 +49,17 @@ int board_init(void)
>
> int dram_init(void)
> {
> + int ret;
> +
> + if (IS_ENABLED(CONFIG_ARM64) && xpl_phase() != PHASE_SPL) {
> + ret = k3_mem_map_init();
> + if (ret) {
> + printf("%s: Error fixing up MMU memory map: %d\n",
> + __func__, ret);
> + return ret;
> + }
> + }
> +
> return fdtdec_setup_mem_size_base();
> }
>
> diff --git a/board/ti/common/k3-ddr.c b/board/ti/common/k3-ddr.c
> index a8425da8de5..ee882f62109 100644
> --- a/board/ti/common/k3-ddr.c
> +++ b/board/ti/common/k3-ddr.c
> @@ -7,6 +7,7 @@
> #include <dm/uclass.h>
> #include <k3-ddrss.h>
> #include <spl.h>
> +#include <mach/k3-ddr.h>
>
> #include "k3-ddr.h"
>
> @@ -14,6 +15,15 @@ int dram_init(void)
> {
> s32 ret;
>
> + if (IS_ENABLED(CONFIG_ARM64) && xpl_phase() != PHASE_SPL) {
> + ret = k3_mem_map_init();
> + if (ret) {
> + printf("%s: Error fixing up MMU memory map: %d\n",
> + __func__, ret);
> + return ret;
> + }
> + }
> +
> ret = fdtdec_setup_mem_size_base_lowest();
> if (ret)
> printf("Error setting up mem size and base. %d\n", ret);
More information about the U-Boot
mailing list