diff mbox series

[v2,2/3] sparc32: derive phys_base from the PAGE_OFFSET mapping

Message ID 20260816075141.3489194-3-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. 16, 2026, 7:50 a.m. UTC
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(+)

Comments

Sam Ravnborg Aug. 16, 2026, 7:54 p.m. UTC | #1
Hi Magnus.

On Sun, Aug 16, 2026 at 09:50:16AM +0200, Magnus Lindholm wrote:
> 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 patch looks good but I dislike we introduce more code that uses
sp_banks. Can we somehow use memblock for this?

Part of my old grand plan was to replace all uses of sp_banks with
memblock. I have some old patches somewhere in case you are ready to
give this a spin.

The patch is:
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>

My sp_banks comment shall not hold it back.
It is anyway Andreas that will handle them.

	Sam
Magnus Lindholm Aug. 16, 2026, 8:56 p.m. UTC | #2
On Sun, Aug 16, 2026 at 9:54 PM Sam Ravnborg <sam@ravnborg.org> wrote:

> sp_banks. Can we somehow use memblock for this?
>
> Part of my old grand plan was to replace all uses of sp_banks with
> memblock. I have some old patches somewhere in case you are ready to
> give this a spin.
>
> The patch is:
> Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
>
> My sp_banks comment shall not hold it back.
> It is anyway Andreas that will handle them.
>

Thanks a lot for taking the time to review this series.

Moving sparc32 off sp_banks and onto memblock sounds like the right
direction, setup_arch() already had the array to hand, which is the only
reason this patch reaches for it. I'd be glad to pick up your old patches
and take them for a spin, just point me at where I can find them.

Magnus
Sam Ravnborg Aug. 17, 2026, 3:32 p.m. UTC | #3
Hi Magnus.

> Moving sparc32 off sp_banks and onto memblock sounds like the right
> direction, setup_arch() already had the array to hand, which is the only
> reason this patch reaches for it. I'd be glad to pick up your old patches
> and take them for a spin, just point me at where I can find them.

The patches are likely online somewhere, but I found the relevant ones
that I have attached here.

These are from the middle of my sun4m/sun4d removal patches, expect
parts to be missed out and for sure they do not apply in your tree.

I recall they were tested one-by-one via QEMU, but I did not test on
real HW.

Feel free to claim full authorship on patches where you do anything
than trivial edits.

I hope you will have fun working on this.

	Sam
Magnus Lindholm Aug. 18, 2026, 9:01 p.m. UTC | #4
Hi Sam,

On Mon, Aug 17, 2026 at 5:32 PM Sam Ravnborg <sam@ravnborg.org> wrote:

> The patches are likely online somewhere, but I found the relevant ones
> that I have attached here.
>
> These are from the middle of my sun4m/sun4d removal patches, expect
> parts to be missed out and for sure they do not apply in your tree.
>
> I recall they were tested one-by-one via QEMU, but I did not test on
> real HW.
>
> Feel free to claim full authorship on patches where you do anything
> than trivial edits.
>
> I hope you will have fun working on this.
>

Thanks alot for taking the time to dig up those patches. I'll take a closer
look and see what I can do. Having the patches as a reference will for
sure save me some time even if they don't apply cleanly as-is.

I'm sure I'll have some fun with it. My old sparcstation has just been
sitting around lately, I'm happy to be able to make good use of them
again. I'll keep you posted on any progress.

Magnus
diff mbox series

Patch

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)