Re: [PATCH v2 2/3] sparc32: derive phys_base from the PAGE_OFFSET mapping
From: Andreas Larsson
Date: Fri Sep 25 2026 - 11:56:36 EST
On 2026-08-16 09:50, 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 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@xxxxxxxxx>
> ---
> 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);
This must not print bogus information on LEON systems.
It is a pity that leon_swprobe() is not ready for use at this point or
it could have been used in __get_phys(). It could be changed in the
future to not do the strict checks that now depends on variables
initialized later with, the here calculated, phys_base as input. For
now, please refrain from printing the above on systems not supported
by __get_phys(), or move it or parts of it back under the if below.
Nit: I think it should be "...RAM starts at 0x%lx..."
Apart from that, this patch, as well as the rest of the series looks
good!
> +
> + 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)
Thanks,
Andreas