diff mbox series

[1/3] sparc32: honour phys_base in the viking cache flush routines

Message ID 20260814105723.3454511-2-linmag7@gmail.com
State New
Headers show
Series sparc32: allow a kernel loaded away from the start of RAM | expand

Commit Message

Magnus Lindholm Aug. 14, 2026, 10:52 a.m. UTC
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>
---
 arch/sparc/mm/viking.S | 6 ++++++
 1 file changed, 6 insertions(+)

Comments

Sam Ravnborg Aug. 14, 2026, 8:43 p.m. UTC | #1
Hi Magnus.

On Fri, Aug 14, 2026 at 12:52:32PM +0200, Magnus Lindholm wrote:
> 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>
Looks good, I checked and as the other variants are coherent this
should be the only place we need this.

Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
diff mbox series

Patch

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