* [PATCH v2 1/3] sparc32: honour phys_base in the viking cache flush routines
2026-08-16 7:50 [PATCH v2 0/3] sparc32: allow a kernel loaded away from the start of RAM Magnus Lindholm
@ 2026-08-16 7:50 ` Magnus Lindholm
2026-08-16 7:50 ` [PATCH v2 2/3] sparc32: derive phys_base from the PAGE_OFFSET mapping Magnus Lindholm
2026-08-16 7:50 ` [PATCH v2 3/3] sparc32: advertise relocatable kernel with HdrS 0x0300 Magnus Lindholm
2 siblings, 0 replies; 4+ messages in thread
From: Magnus Lindholm @ 2026-08-16 7:50 UTC (permalink / raw)
To: davem, andreas; +Cc: sparclinux, linux-kernel, Magnus Lindholm, Sam Ravnborg
viking_flush_page() and viking_mxcc_flush_page() derive the physical
address of the page they are asked to flush by subtracting PAGE_OFFSET
from the kernel virtual address:
sethi %hi(PAGE_OFFSET), %g2
sub %o0, %g2, %g3
That is only the physical address when phys_base is zero. The C side spells
the same conversion __pa(), which adds phys_base, and every caller passes a
kernel virtual address expecting exactly that.
With a kernel loaded away from the start of RAM the two disagree by
phys_base. viking_flush_page() then compares cache tags against the wrong
page and flushes nothing, and viking_mxcc_flush_page() streams a page that
is phys_base lower than the one it was given, so the intended lines stay
dirty in the cache while unrelated ones are pushed out.
The visible effect is that anything relying on a flush to make memory
visible to another bus master silently keeps working from stale data. On a
SPARCstation 20 this shows up as every SCSI transfer failing with a DMA
error: iommu_flush_iotlb() cannot get the IOPTEs out to RAM, so the IOMMU
walks stale entries and the ESP DMA faults.
Add phys_base, so these agree with __pa() again. No change when phys_base
is zero, which is why this went unnoticed.
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
---
arch/sparc/mm/viking.S | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/sparc/mm/viking.S b/arch/sparc/mm/viking.S
index 48f062de7a7f..8b4e251bbba2 100644
--- a/arch/sparc/mm/viking.S
+++ b/arch/sparc/mm/viking.S
@@ -38,6 +38,9 @@ sun4dsmp_flush_tlb_spin:
viking_flush_page:
sethi %hi(PAGE_OFFSET), %g2
sub %o0, %g2, %g3
+ sethi %hi(phys_base), %g2
+ ld [%g2 + %lo(phys_base)], %g2
+ add %g3, %g2, %g3 ! + phys_base = physical address
srl %g3, 12, %g1 ! ppage >> 12
clr %o1 ! set counter, 0 - 127
@@ -91,6 +94,9 @@ viking_flush_page:
viking_mxcc_flush_page:
sethi %hi(PAGE_OFFSET), %g2
sub %o0, %g2, %g3
+ sethi %hi(phys_base), %g2
+ ld [%g2 + %lo(phys_base)], %g2
+ add %g3, %g2, %g3 ! + phys_base = physical address
sub %g3, -PAGE_SIZE, %g3 ! ppage + PAGE_SIZE
sethi %hi(MXCC_SRCSTREAM), %o3 ! assume %hi(MXCC_SRCSTREAM) == %hi(MXCC_DESTSTREAM)
mov 0x10, %g2 ! set cacheable bit
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v2 2/3] sparc32: derive phys_base from the PAGE_OFFSET mapping
2026-08-16 7:50 [PATCH v2 0/3] sparc32: allow a kernel loaded away from the start of RAM Magnus Lindholm
2026-08-16 7:50 ` [PATCH v2 1/3] sparc32: honour phys_base in the viking cache flush routines Magnus Lindholm
@ 2026-08-16 7:50 ` Magnus Lindholm
2026-08-16 7:50 ` [PATCH v2 3/3] sparc32: advertise relocatable kernel with HdrS 0x0300 Magnus Lindholm
2 siblings, 0 replies; 4+ messages in thread
From: Magnus Lindholm @ 2026-08-16 7:50 UTC (permalink / raw)
To: davem, andreas; +Cc: sparclinux, linux-kernel, Magnus Lindholm
setup_arch() computes phys_base as the base of the lowest sp_banks[]
entry, that is, where RAM starts, and assumes the kernel image was loaded
there. That holds for the traditional boot path, where SILO places the
image at physical 0x4000 and PAGE_OFFSET is mapped to physical 0.
It stops holding once the image no longer fits there. SILO loads a kernel
between physical 0x4000 and its own text at 0x280000, a window of 2605056
bytes; a current sparc32 kernel is roughly twice that. The loader must
then place the image elsewhere in physical memory and map PAGE_OFFSET to
it, at which point phys_base describes where RAM begins rather than what
PAGE_OFFSET maps to, and the two disagree.
phys_base is the offset __pa() and __va() are defined in terms of, so once
it is wrong every early translation is wrong by the difference, including
the physical addresses written into page table descriptors. The
tablewalker then follows pointers into pages that hold nothing while the
same tables read back correctly through the nocache view. The failure
surfaces as a hang right after the context table pointer is installed and
the TLB flushed, with nothing on the console to explain it, since the PROM
mappings the early console depends on have become just as unreachable.
Ask the MMU what PAGE_OFFSET actually translates to and adopt that.
__get_phys() already implements this probe for sun4m and sun4d and returns
zero elsewhere, so no new low level MMU access is introduced and machines
without an SRMMU are unaffected.
Memory below the kernel cannot be reached through the linear map, which
runs upward from PAGE_OFFSET, so drop the banks that fall below it rather
than leave entries that __va() would translate to below PAGE_OFFSET.
With this a 6MB kernel loaded at physical 0x03000000 boots on sun4m: the
context table lands at its true physical address,
srmmu_inherit_prom_mappings() preserves the PROM console mappings, and
srmmu.c needs no change at all, since map_kernel() already handles a
non-zero phys_base via do_large_mapping().
The cost is the RAM below the load address the loader chose. SILO's
memory_find() picks 48MB on machines with 64MB or more.
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
arch/sparc/kernel/setup_32.c | 40 ++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/arch/sparc/kernel/setup_32.c b/arch/sparc/kernel/setup_32.c
index 1b0db16cd37b..34e8f7c87685 100644
--- a/arch/sparc/kernel/setup_32.c
+++ b/arch/sparc/kernel/setup_32.c
@@ -254,6 +254,30 @@ static __init void leon_patch(void)
struct tt_entry *sparc_ttable;
+/* Drop RAM below the kernel; the linear map runs upward from phys_base
+ * and cannot reach it.
+ */
+static void __init trim_sp_banks_below(unsigned long base)
+{
+ int i, j = 0;
+
+ for (i = 0; sp_banks[i].num_bytes != 0; i++) {
+ unsigned long start = sp_banks[i].base_addr;
+ unsigned long end = start + sp_banks[i].num_bytes;
+
+ if (end <= base)
+ continue; /* wholly below - drop it */
+ if (start < base)
+ start = base; /* straddles - trim the front */
+
+ sp_banks[j].base_addr = start;
+ sp_banks[j].num_bytes = end - start;
+ j++;
+ }
+ sp_banks[j].base_addr = 0;
+ sp_banks[j].num_bytes = 0;
+}
+
/* Called from head_32.S - before we have setup anything
* in the kernel. Be very careful with what you do here.
*/
@@ -332,6 +356,22 @@ void __init setup_arch(char **cmdline_p)
if (highest_paddr < top)
highest_paddr = top;
}
+
+ /* phys_base must describe what PAGE_OFFSET maps to, not where RAM starts. */
+ {
+ unsigned long real_base = __get_phys(PAGE_OFFSET);
+
+ prom_printf("phys_base: RAM starts 0x%lx, kernel is at 0x%lx\n",
+ phys_base, real_base);
+
+ if (real_base && real_base != phys_base) {
+ phys_base = real_base;
+ trim_sp_banks_below(phys_base);
+ prom_printf("phys_base: adopted 0x%lx, RAM below it dropped\n",
+ phys_base);
+ }
+ }
+
pfn_base = phys_base >> PAGE_SHIFT;
if (!root_flags)
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH v2 3/3] sparc32: advertise relocatable kernel with HdrS 0x0300
2026-08-16 7:50 [PATCH v2 0/3] sparc32: allow a kernel loaded away from the start of RAM Magnus Lindholm
2026-08-16 7:50 ` [PATCH v2 1/3] sparc32: honour phys_base in the viking cache flush routines Magnus Lindholm
2026-08-16 7:50 ` [PATCH v2 2/3] sparc32: derive phys_base from the PAGE_OFFSET mapping Magnus Lindholm
@ 2026-08-16 7:50 ` Magnus Lindholm
2 siblings, 0 replies; 4+ messages in thread
From: Magnus Lindholm @ 2026-08-16 7:50 UTC (permalink / raw)
To: davem, andreas; +Cc: sparclinux, linux-kernel, Magnus Lindholm, Sam Ravnborg
HdrS version 0x0300 tells the boot loader that the kernel supports being
located somewhere other than physical 0x4000, which is where SILO places
the image on the traditional path. Anything older is copied back down
there, and refused outright when it no longer fits.
sparc32 could not make that claim before, because setup_arch() took
phys_base from the lowest memory bank rather than from what PAGE_OFFSET
maps to. It can now, so say so.
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
---
arch/sparc/kernel/head_32.S | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/sparc/kernel/head_32.S b/arch/sparc/kernel/head_32.S
index 8c320fa25a67..11a1746829a4 100644
--- a/arch/sparc/kernel/head_32.S
+++ b/arch/sparc/kernel/head_32.S
@@ -69,7 +69,7 @@ sun4e_notsup:
*/
.ascii "HdrS"
.word LINUX_VERSION_CODE
- .half 0x0203 /* HdrS version */
+ .half 0x0300 /* HdrS version */
root_flags:
.half 1
root_dev:
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread