@@ -34,12 +34,24 @@ start:
/* zero BSS */
ldr r4, =bss
ldr r5, =ebss
+ mov r3, r4
+ dmb sy
+ dcache_by_line_op dcimvac, sy, r4, r5, r6, r7
+ mov r4, r3
zero_range r4, r5, r6, r7
+ mov r4, r3
+ dmb sy
+ dcache_by_line_op dcimvac, sy, r4, r5, r6, r7
/* zero stack */
ldr r5, =stacktop
sub r4, r5, #THREAD_SIZE
+ mov r3, r4
+ dmb sy
+ dcache_by_line_op dcimvac, sy, r4, r5, r6, r7
+ mov r4, r3
zero_range r4, r5, r6, r7
+ /* asm_mmu_enable will perform the rest of the cache maintenance. */
/*
* set stack, making room at top of stack for cpu0's
@@ -145,7 +157,12 @@ secondary_entry:
lsr r2, #THREAD_SHIFT
lsl r2, #THREAD_SHIFT
add r3, r2, #THREAD_SIZE
+ mov r6, r2
+ dmb sy
+ dcache_by_line_op dcimvac, sy, r2, r3, r4, r5
+ mov r2, r6
zero_range r2, r3, r4, r5
+ /* asm_mmu_enable will perform the rest of the cache maintenance. */
mov sp, r0
bl exceptions_init
@@ -204,6 +221,13 @@ asm_mmu_enable:
mcrr p15, 0, r0, r1, c2
isb
+ dmb sy
+ mov r0, sp
+ lsr r0, #THREAD_SHIFT
+ lsl r0, #THREAD_SHIFT
+ add r1, r0, #THREAD_SIZE
+ dcache_by_line_op dcimvac, sy, r0, r1, r2, r3
+
/* SCTLR */
mrc p15, 0, r2, c1, c0, 0
orr r2, #CR_C
@@ -53,6 +53,7 @@ start:
add x5, x5, :lo12:reloc_start
adrp x6, reloc_end
add x6, x6, :lo12:reloc_end
+
1:
cmp x5, x6
b.hs 1f
@@ -60,22 +61,44 @@ start:
ldr x8, [x5, #16] // r_addend
add x8, x8, x4 // val = base + r_addend
str x8, [x4, x7] // base[r_offset] = val
+ add x7, x4, x7
+ dmb sy
+ /* Image is cleaned to PoC, no need for CMOs before the memory write. */
+ dc ivac, x7
add x5, x5, #24
b 1b
1:
+ /* Complete the cache maintenance operations. */
+ dsb sy
+
/* zero BSS */
adrp x4, bss
add x4, x4, :lo12:bss
adrp x5, ebss
add x5, x5, :lo12:ebss
+ /* Stash start of bss, as dcache_by_line_op corrupts it. */
+ mov x9, x4
+ dmb sy
+ /* Make sure there are no dirty cache lines that can be evicted. */
+ dcache_by_line_op ivac, sy, x4, x5, x6, x7
+ mov x4, x9
zero_range x4, x5
+ mov x9, x4
+ dmb sy
+ /* Invalidate clean and potentially stale cache lines. */
+ dcache_by_line_op ivac, sy, x4, x5, x6, x7
/* zero and set up stack */
adrp x5, stacktop
add x5, x5, :lo12:stacktop
sub x4, x5, #THREAD_SIZE
+ mov x9, x4
+ dmb sy
+ dcache_by_line_op ivac, sy, x4, x5, x6, x7
+ mov x4, x9
zero_range x4, x5
+ /* asm_mmu_enable will perform the rest of the cache maintenance. */
/* set SCTLR_EL1 to a known value */
ldr x4, =INIT_SCTLR_EL1_MMU_OFF
@@ -149,7 +172,12 @@ secondary_entry:
ldr x0, [x0, :lo12:secondary_data]
and x1, x0, #THREAD_MASK
add x2, x1, #THREAD_SIZE
+ mov x9, x1
+ dmb sy
+ dcache_by_line_op ivac, sy, x1, x2, x3, x4
+ mov x1, x9
zero_range x1, x2
+ /* asm_mmu_enable will perform the rest of the cache maintenance. */
mov sp, x0
/* Enable FP/ASIMD */
@@ -242,6 +270,12 @@ asm_mmu_enable:
msr ttbr0_el1, x0
isb
+ dmb sy
+ mov x9, sp
+ and x9, x9, #THREAD_MASK
+ add x10, x9, #THREAD_SIZE
+ dcache_by_line_op ivac, sy, x9, x10, x11, x12
+
/* SCTLR */
mrs x1, sctlr_el1
orr x1, x1, SCTLR_EL1_C
@@ -8,6 +8,8 @@
#include <linux/const.h>
+#include <libcflat.h>
+
#define PAGE_SHIFT 12
#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE-1))
@@ -22,6 +22,8 @@
*/
#include <linux/compiler.h>
+#include <asm/cacheflush.h>
+
#define pgtable_va(x) ((void *)(unsigned long)(x))
#define pgtable_pa(x) ((unsigned long)(x))
@@ -44,7 +46,9 @@
static inline pgd_t *pgd_alloc_early(void)
{
pgd_t *pgd = memalign(PAGE_SIZE, PAGE_SIZE);
+ dcache_inval_page_poc((unsigned long)pgd);
memset(pgd, 0, PAGE_SIZE);
+ dcache_inval_page_poc((unsigned long)pgd);
return pgd;
}
static inline pgd_t *pgd_alloc(void)
@@ -74,7 +78,9 @@ static inline pmd_t *pgd_page_vaddr(pgd_t pgd)
static inline pmd_t *pmd_alloc_one_early(void)
{
pmd_t *pmd = memalign(PAGE_SIZE, PAGE_SIZE);
+ dcache_inval_page_poc((unsigned long)pmd);
memset(pmd, 0, PAGE_SIZE);
+ dcache_inval_page_poc((unsigned long)pmd);
return pmd;
}
static inline pmd_t *pmd_alloc_one(void)
@@ -89,6 +95,8 @@ static inline pmd_t *pmd_alloc(pgd_t *pgd, unsigned long addr)
pgd_t entry;
pgd_val(entry) = pgtable_pa(pmd_alloc_one()) | PMD_TYPE_TABLE;
WRITE_ONCE(*pgd, entry);
+ if (!page_alloc_initialized())
+ dcache_clean_inval_addr_poc((unsigned long)pgd);
}
return pmd_offset(pgd, addr);
}
@@ -107,7 +115,9 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd)
static inline pte_t *pte_alloc_one_early(void)
{
pte_t *pte = memalign(PAGE_SIZE, PAGE_SIZE);
+ dcache_inval_page_poc((unsigned long)pte);
memset(pte, 0, PAGE_SIZE);
+ dcache_inval_page_poc((unsigned long)pte);
return pte;
}
static inline pte_t *pte_alloc_one(void)
@@ -122,6 +132,8 @@ static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
pmd_t entry;
pmd_val(entry) = pgtable_pa(pte_alloc_one()) | PMD_TYPE_TABLE;
WRITE_ONCE(*pmd, entry);
+ if (!page_alloc_initialized())
+ dcache_clean_inval_addr_poc((unsigned long)pmd);
}
return pte_offset(pmd, addr);
@@ -5,6 +5,7 @@
*
* This work is licensed under the terms of the GNU LGPL, version 2.
*/
+#include <asm/cacheflush.h>
#include <asm/setup.h>
#include <asm/thread_info.h>
#include <asm/cpumask.h>
@@ -15,6 +16,7 @@
#include <asm/pgtable.h>
#include <asm/pgtable-hwdef.h>
+#include "alloc_phys.h"
#include "io.h"
#include "vmalloc.h"
@@ -236,6 +238,8 @@ void mmu_setup_early(phys_addr_t phys_end)
ioremap((phys_addr_t)(unsigned long)uart_early_base(), PAGE_SIZE);
+ phys_alloc_perform_cache_maintenance(dcache_clean_inval_addr_poc);
+
/*
* Open-code part of mmu_enabled(), because at this point thread_info
* hasn't been initialized. mmu_mark_enabled() cannot be called here
@@ -19,6 +19,7 @@
#include <vmalloc.h>
#include <auxinfo.h>
#include <argv.h>
+#include <asm/cacheflush.h>
#include <asm/thread_info.h>
#include <asm/setup.h>
#include <asm/page.h>
@@ -197,6 +198,7 @@ static void mem_init(phys_addr_t freemem_start)
struct mem_region *freemem, *r, mem = {
.start = (phys_addr_t)-1,
};
+ int nr_regions = 0;
freemem = mem_region_find(freemem_start);
assert(freemem && !(freemem->flags & (MR_F_IO | MR_F_CODE)));
@@ -208,6 +210,7 @@ static void mem_init(phys_addr_t freemem_start)
if (r->end > mem.end)
mem.end = r->end;
}
+ nr_regions++;
}
assert(mem.end && !(mem.start & ~PHYS_MASK));
mem.end &= PHYS_MASK;
@@ -221,6 +224,9 @@ static void mem_init(phys_addr_t freemem_start)
/* Ensure our selected freemem range is somewhere in our full range */
assert(freemem_start >= mem.start && freemem->end <= mem.end);
+ dcache_inval_poc((unsigned long)mem_regions,
+ (unsigned long)(mem_regions + nr_regions));
+
__phys_offset = mem.start; /* PHYS_OFFSET */
__phys_end = mem.end; /* PHYS_END */
@@ -240,32 +246,69 @@ static void mem_init(phys_addr_t freemem_start)
page_alloc_ops_enable();
}
-void setup(const void *fdt, phys_addr_t freemem_start)
+extern const void *fdt;
+static void do_fdt_move(void *freemem, const void *fdt_addr, u32 *fdt_size)
{
- void *freemem;
- const char *bootargs, *tmp;
- u32 fdt_size;
int ret;
- assert(sizeof(long) == 8 || freemem_start < (3ul << 30));
- freemem = (void *)(unsigned long)freemem_start;
-
/* Move the FDT to the base of free memory */
- fdt_size = fdt_totalsize(fdt);
- ret = fdt_move(fdt, freemem, fdt_size);
+ *fdt_size = fdt_totalsize(fdt_addr);
+
+ /* Invalidate potentially dirty cache lines. */
+ dcache_inval_poc((unsigned long)freemem, (unsigned long)freemem + *fdt_size);
+ ret = fdt_move(fdt_addr, freemem, *fdt_size);
assert(ret == 0);
+
ret = dt_init(freemem);
+ /*
+ * Invalidate the clean (the bootloader cleans the test image to PoC),
+ * but potentially stale, cache line that holds the value of the
+ * variable fdt, to force the CPU to fetch it from memory when the MMU
+ * is enabled.
+ */
+ dcache_clean_inval_addr_poc((unsigned long)&fdt);
assert(ret == 0);
- freemem += fdt_size;
+}
+
+static void initrd_move(void *freemem)
+{
+ const char *tmp;
+ int ret;
/* Move the initrd to the top of the FDT */
ret = dt_get_initrd(&tmp, &initrd_size);
assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
if (ret == 0) {
initrd = freemem;
+ /* Invalidate the potentially stale value for the variable. */
+ dcache_clean_inval_addr_poc((unsigned long)&initrd);
+ /*
+ * Invalidate potentially dirty cache lines for where the initrd
+ * will be moved.
+ */
+ dcache_inval_poc((unsigned long)freemem, (unsigned long)freemem + initrd_size);
memmove(initrd, tmp, initrd_size);
- freemem += initrd_size;
}
+}
+
+void setup(const void *fdt_addr, phys_addr_t freemem_start)
+{
+ void *freemem;
+ const char *bootargs;
+ u32 fdt_size;
+ int ret;
+
+ assert(sizeof(long) == 8 || freemem_start < (3ul << 30));
+ freemem = (void *)(unsigned long)freemem_start;
+
+ do_fdt_move(freemem, fdt_addr, &fdt_size);
+ freemem += fdt_size;
+
+ initrd_move(freemem);
+ freemem += initrd_size;
+
+ /* Invalidate potentially stale cache lines for the fdt and initrd. */
+ dcache_inval_poc(freemem_start, (unsigned long)freemem);
mem_regions_add_dt_regions();
mem_regions_add_assumed();
@@ -335,6 +378,7 @@ static efi_status_t efi_mem_init(efi_bootinfo_t *efi_bootinfo)
uintptr_t data = (uintptr_t)&_data, edata = ALIGN((uintptr_t)&_edata, 4096);
const void *fdt = efi_bootinfo->fdt;
int fdt_size, ret;
+ int nr_regions = 0;
/*
* Record the largest free EFI_CONVENTIONAL_MEMORY region
@@ -398,7 +442,16 @@ static efi_status_t efi_mem_init(efi_bootinfo_t *efi_bootinfo)
__phys_end = r.end;
}
mem_region_add(&r);
+ nr_regions++;
}
+
+ /*
+ * The mem_regions will be used after mmu_disable and before mmu_enable
+ * again, so clean the dcache to poc.
+ */
+ dcache_clean_poc((unsigned long)mem_regions,
+ (unsigned long)(mem_regions + nr_regions));
+
if (fdt) {
/* Move the FDT to the base of free memory */
fdt_size = fdt_totalsize(fdt);
@@ -15,6 +15,8 @@
*/
#include <alloc.h>
#include <alloc_page.h>
+
+#include <asm/cacheflush.h>
#include <asm/setup.h>
#include <asm/page.h>
#include <asm/pgtable-hwdef.h>
@@ -50,7 +52,9 @@
static inline pgd_t *pgd_alloc_early(void)
{
pgd_t *pgd = memalign(PAGE_SIZE, PAGE_SIZE);
+ dcache_inval_page_poc((unsigned long)pgd);
memset(pgd, 0, PAGE_SIZE);
+ dcache_inval_page_poc((unsigned long)pgd);
return pgd;
}
static inline pgd_t *pgd_alloc(void)
@@ -84,7 +88,9 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd)
static inline pmd_t *pmd_alloc_one_early(void)
{
pmd_t *pmd = memalign(PAGE_SIZE, PAGE_SIZE);
+ dcache_inval_page_poc((unsigned long)pmd);
memset(pmd, 0, PAGE_SIZE);
+ dcache_inval_page_poc((unsigned long)pmd);
return pmd;
}
static inline pmd_t *pmd_alloc_one(void)
@@ -99,6 +105,8 @@ static inline pmd_t *pmd_alloc(pud_t *pud, unsigned long addr)
pud_t entry;
pud_val(entry) = pgtable_pa(pmd_alloc_one()) | PMD_TYPE_TABLE;
WRITE_ONCE(*pud, entry);
+ if (!page_alloc_initialized())
+ dcache_clean_inval_addr_poc((unsigned long)pud);
}
return pmd_offset(pud, addr);
}
@@ -117,7 +125,9 @@ static inline pmd_t *pmd_alloc(pud_t *pud, unsigned long addr)
static inline pud_t *pud_alloc_one_early(void)
{
pud_t *pud = memalign(PAGE_SIZE, PAGE_SIZE);
+ dcache_inval_page_poc((unsigned long)pud);
memset(pud, 0, PAGE_SIZE);
+ dcache_inval_page_poc((unsigned long)pud);
return pud;
}
static inline pud_t *pud_alloc_one(void)
@@ -132,6 +142,8 @@ static inline pud_t *pud_alloc(pgd_t *pgd, unsigned long addr)
pgd_t entry;
pgd_val(entry) = pgtable_pa(pud_alloc_one()) | PMD_TYPE_TABLE;
WRITE_ONCE(*pgd, entry);
+ if (!page_alloc_initialized())
+ dcache_clean_inval_addr_poc((unsigned long)pgd);
}
return pud_offset(pgd, addr);
}
@@ -150,7 +162,9 @@ static inline pud_t *pud_alloc(pgd_t *pgd, unsigned long addr)
static inline pte_t *pte_alloc_one_early(void)
{
pte_t *pte = memalign(PAGE_SIZE, PAGE_SIZE);
+ dcache_inval_page_poc((unsigned long)pte);
memset(pte, 0, PAGE_SIZE);
+ dcache_inval_page_poc((unsigned long)pte);
return pte;
}
static inline pte_t *pte_alloc_one(void)
@@ -165,6 +179,8 @@ static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
pmd_t entry;
pmd_val(entry) = pgtable_pa(pte_alloc_one()) | PMD_TYPE_TABLE;
WRITE_ONCE(*pmd, entry);
+ if (!page_alloc_initialized())
+ dcache_clean_inval_addr_poc((unsigned long)pmd);
}
return pte_offset(pmd, addr);
}
@@ -7,7 +7,7 @@
#include "libfdt/libfdt.h"
#include "devicetree.h"
-static const void *fdt;
+const void *fdt;
const void *dt_fdt(void)
{