summaryrefslogtreecommitdiff
path: root/arch/arm64/lib/cache_va.c
diff options
context:
space:
mode:
authorBradley Morgan <brads@mainlining.org>2026-10-03 22:58:47 +0000
committerBradley Morgan <brads@mainlining.org>2026-10-03 22:58:47 +0000
commit61af8d6209b395a03ceab156b6df6720d638048e (patch)
tree3dde763a13edcc56d9432e5403a3e1a6d30c5710 /arch/arm64/lib/cache_va.c
parent6b3fcc0def1e173c76943682dcf3cba6edcd3b55 (diff)
tashaboot: smp, psci, initrd, timer, cache by va
The bootloader now does the whole job of machine firmware it owns: boots 4 cpus, hands over an initrd, answers PSCI, and carries the delay and cache primitives the arch layer needs. SMP: the secondary pen is the Wait For Event mechanism from the manual (B2-144, D1-2255), each secondary watches its spin gate, WFE, the release writes the entry and SEVs, the recheck after each wake covers a release that lands between the load and the sleep. The gates land in the dtb cpu-release-addr slots, rewritten in place by a small walker, no libfdt, structure per the devicetree specification, values only, the properties themselves are fixed at build time like firmware shipping a fixed blob. PSCI 0.2 at EL2 (DEN 0022): the HVC trap arrives at the current EL SP_ELx sync slot (EC 0x16 in ESR_EL2, the vector layout Table D1-7), dispatch on the standard function ids, VERSION, CPU_ON writes the target gate and SEVs, CPU_OFF clears the gate and returns to the pen, SYSTEM_OFF and SYSTEM_RESET drive RMR_EL2.RR. On qemu the cores are held by the machine's own firmware and released through its PSCI (hvc with -kernel, smc with virtualization=on), the handler here is the real hardware path where the bootloader is the conduit. The initrd handoff: loaded at a fixed address clear of the image and dtb, the dtb /chosen carries linux,initrd-start and -end. Delays are the generic timer (D10), CNTFRQ_EL0 frequency, CNTVCT_EL0 count, busy wait, no interrupts. Cache maintenance by virtual address, dc cvac, dc ivac, dc civac, ic ivau with the barrier pairs the manual requires, the by VA form beats set and way when the range is known. Boot receipt, 4 cpus, el2, initrd: tashaboot 0.1 initrd at 46000000 [ 0.000000] Booting Linux on physical CPU 0x0000000000 [ 0.130621] smp: Brought up 1 node, 4 CPUs [ 1.830692] Run /init as init process tashaboot linux userspace reached cores: 4 BusyBox v1.37.0 built-in shell (ash) ~ # Signed-off-by: Bradley Morgan <brads@mainlining.org>
Diffstat (limited to 'arch/arm64/lib/cache_va.c')
-rw-r--r--arch/arm64/lib/cache_va.c73
1 files changed, 73 insertions, 0 deletions
diff --git a/arch/arm64/lib/cache_va.c b/arch/arm64/lib/cache_va.c
new file mode 100644
index 0000000..1fd7804
--- /dev/null
+++ b/arch/arm64/lib/cache_va.c
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * cache_va.c - cache maintenance by virtual address, the operations
+ * the manual prescribes for boot handoff: clean to point of
+ * coherency (dc cvac), invalidate (dc ivac), and clean and
+ * invalidate (dc civac), plus icache invalidate by VA to the point
+ * of unification (ic ivau). by VA beats by set and way when the
+ * address range is known, the manual's own guidance, set and way
+ * only for the full flush cases in cache.S.
+ *
+ * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#define CACHE_LINE_SHIFT 6 /* 64 byte lines on cortex-a class */
+#define CACHE_LINE_SIZE (1 << CACHE_LINE_SHIFT)
+
+void tb_clean_dcache_range(uintptr_t start, size_t len)
+{
+ uintptr_t line = start & ~(uintptr_t)(CACHE_LINE_SIZE - 1);
+ uintptr_t end = start + len;
+
+ while (line < end) {
+ asm volatile("dc cvac, %0" :: "r" (line) : "memory");
+ line += CACHE_LINE_SIZE;
+ }
+
+ asm volatile("dsb sy" ::: "memory");
+}
+
+void tb_inval_dcache_range(uintptr_t start, size_t len)
+{
+ uintptr_t line = start & ~(uintptr_t)(CACHE_LINE_SIZE - 1);
+ uintptr_t end = start + len;
+
+ while (line < end) {
+ asm volatile("dc ivac, %0" :: "r" (line) : "memory");
+ line += CACHE_LINE_SIZE;
+ }
+
+ asm volatile("dsb sy" ::: "memory");
+}
+
+void tb_clean_inval_dcache_range(uintptr_t start, size_t len)
+{
+ uintptr_t line = start & ~(uintptr_t)(CACHE_LINE_SIZE - 1);
+ uintptr_t end = start + len;
+
+ while (line < end) {
+ asm volatile("dc civac, %0" :: "r" (line) : "memory");
+ line += CACHE_LINE_SIZE;
+ }
+
+ asm volatile("dsb sy" ::: "memory");
+}
+
+void tb_inval_icache_range(uintptr_t start, size_t len)
+{
+ uintptr_t line = start & ~(uintptr_t)(CACHE_LINE_SIZE - 1);
+ uintptr_t end = start + len;
+
+ while (line < end) {
+ asm volatile("ic ivau, %0" :: "r" (line) : "memory");
+ line += CACHE_LINE_SIZE;
+ }
+
+ asm volatile(
+ "dsb ish\n"
+ "isb\n"
+ ::: "memory");
+}