[PATCH v2 05/15] mach-snapdragon: fix reserved memory carveout
Casey Connolly
casey.connolly at linaro.org
Mon May 4 20:57:33 CEST 2026
The memory carveout logic was fairly limited and had a few issues,
rework it and teach it not to unmap regions that have a compatible
property (since they may be used in U-Boot) or that don't have the
no-map property.
The carveout process adds ~100ms to the boot time depending on the
platform.
This prepares us for using SMEM as a source of truth and improving
support for U-boot as a first stage bootloader since SMEMs memory map
doesn't already carve out some regions like ABL does.
Signed-off-by: Casey Connolly <casey.connolly at linaro.org>
---
arch/arm/mach-snapdragon/board.c | 86 +++++++++++++++++++++++++---------------
1 file changed, 53 insertions(+), 33 deletions(-)
diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
index 829a0109ac78..e12d3d00caa4 100644
--- a/arch/arm/mach-snapdragon/board.c
+++ b/arch/arm/mach-snapdragon/board.c
@@ -622,27 +622,36 @@ u64 get_page_table_size(void)
{
return SZ_1M;
}
+struct mem_resource_attrs {
+ fdt_addr_t start;
+ fdt_addr_t size;
+ u64 attrs;
+};
+
static int fdt_cmp_res(const void *v1, const void *v2)
{
- const struct fdt_resource *res1 = v1, *res2 = v2;
+ const struct mem_resource_attrs *res1 = v1, *res2 = v2;
return res1->start - res2->start;
}
-#define N_RESERVED_REGIONS 32
+#define N_RESERVED_REGIONS 64
-/* Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
+/* Map and unmap reserved memory regions as appropriate.
+ * Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
* On some platforms this is enough to trigger a security violation and trap
* to EL3.
+ * Regions that may be accessed by drivers get mapped explicitly.
*/
-static void carve_out_reserved_memory(void)
+static void configure_reserved_memory(void)
{
- static struct fdt_resource res[N_RESERVED_REGIONS] = { 0 };
+ static struct mem_resource_attrs res[N_RESERVED_REGIONS] = { 0 };
int parent, rmem, count, i = 0;
phys_addr_t start;
size_t size;
+ u64 attrs;
/* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise
* attempt to access them, causing a security exception.
*/
@@ -651,14 +660,19 @@ static void carve_out_reserved_memory(void)
log_err("No reserved memory regions found\n");
return;
}
- /* Collect the reserved memory regions */
+ /* Collect the reserved memory regions and appropriate attrs */
fdt_for_each_subnode(rmem, gd->fdt_blob, parent) {
const fdt32_t *ptr;
- int len;
+ attrs = PTE_TYPE_FAULT;
+ /* If the no-map property isn't set then the region is valid */
if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL))
- continue;
+ attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL);
+ /* If the compatible property is set then this region may be accessed by drivers and should
+ * be marked valid too. */
+ if (fdt_getprop(gd->fdt_blob, rmem, "compatible", NULL))
+ attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL);
if (i == N_RESERVED_REGIONS) {
log_err("Too many reserved regions!\n");
break;
@@ -667,50 +681,55 @@ static void carve_out_reserved_memory(void)
/* Read the address and size out from the reg property. Doing this "properly" with
* fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here
* takes <1ms... Oh the woes of no dcache.
*/
- ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", &len);
+ ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", NULL);
if (ptr) {
/* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within
* the 32-bit address space. So we can cheat here for speed.
*/
res[i].start = fdt32_to_cpu(ptr[1]);
- res[i].end = res[i].start + fdt32_to_cpu(ptr[3]);
+ res[i].size = fdt32_to_cpu(ptr[3]);
+ res[i].attrs = attrs;
i++;
}
}
/* Sort the reserved memory regions by address */
count = i;
- qsort(res, count, sizeof(struct fdt_resource), fdt_cmp_res);
+ qsort(res, count, sizeof(res[0]), fdt_cmp_res);
+ debug("Mapping %d regions!\n", count);
/* Now set the right attributes for them. Often a lot of the regions are tightly packed together
- * so we can optimise the number of calls to mmu_change_region_attr() by combining adjacent
+ * so we can optimise the number of calls to mmu_change_region_attr_nobreak() by combining adjacent
* regions.
*/
- start = ALIGN_DOWN(res[0].start, SZ_2M);
- size = ALIGN(res[0].end - start, SZ_2M);
+ start = res[0].start;
+ size = res[0].size;
+ attrs = res[0].attrs;
+ /* For each region after the first one, either increase the `size` to eventually be mapped or
+ * map the region we have and start a new one, this allows us to reduce the number of calls to
+ * mmu_map_region(). The loop is therefore "lagging" behind by one iteration. */
for (i = 1; i <= count; i++) {
- /* We ideally want to 2M align everything for more efficient pagetables, but we must avoid
- * overwriting reserved memory regions which shouldn't be mapped as FAULT (like those with
- * compatible properties).
- * If within 2M of the previous region, bump the size to include this region. Otherwise
- * start a new region.
- */
- if (i == count || start + size < res[i].start - SZ_2M) {
- debug(" 0x%016llx - 0x%016llx: reserved\n",
- start, start + size);
- mmu_change_region_attr(start, size, PTE_TYPE_FAULT);
- /* If this is the final region then quit here before we index
- * out of bounds...
- */
+ /* If i == count we are done, just map the last region. If the last region is
+ * too far away or the attrs don't match then map the meta-region we have and
+ * start a new one. */
+ if (i == count || start + size < res[i].start - SZ_8K || attrs != res[i].attrs) {
+ debug(" 0x%016llx - 0x%016llx: %s\n",
+ start, start + size, attrs == PTE_TYPE_FAULT ? "FAULT" : "VALID");
+ /* No need to break-before-make since dcache is disabled */
+ mmu_change_region_attr_nobreak(start, size, attrs);
+ /* We have now mapped all the regions */
if (i == count)
break;
- start = ALIGN_DOWN(res[i].start, SZ_2M);
- size = ALIGN(res[i].end - start, SZ_2M);
+ /* Start a new meta-region */
+ start = res[i].start;
+ size = res[i].size;
+ attrs = res[i].attrs;
} else {
- /* Bump size if this region is immediately after the previous one */
- size = ALIGN(res[i].end - start, SZ_2M);
+ /* This region is next to (<8K) the previous one so combine them.
+ * Accounting for any small (<8K) gap. */
+ size = (res[i].start - start) + res[i].size;
}
}
}
@@ -744,13 +763,14 @@ void enable_caches(void)
gd->arch.tlb_emerg = gd->arch.tlb_addr;
gd->arch.tlb_addr = tlb_addr;
gd->arch.tlb_size = tlb_size;
- /* We do the carveouts only for QCS404, for now. */
+ /* On some boards speculative access may trigger a NOC or XPU violation so explicitly mark reserved
+ * regions as inacessible (PTE_TYPE_FAULT) */
if (fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) {
carveout_start = get_timer(0);
/* Takes ~20-50ms on SDM845 */
- carve_out_reserved_memory();
+ configure_reserved_memory();
debug("carveout time: %lums\n", get_timer(carveout_start));
}
dcache_enable();
}
--
2.53.0
More information about the U-Boot
mailing list