[PATCH 2/4] powerpc: move to 64-bit RTAS
Sourabh Jain
sourabhjain at linux.ibm.com
Wed Oct 29 23:52:18 AEDT 2025
On 27/10/25 20:43, Sourabh Jain wrote:
> Kdump kernels loaded at high addresses (above 4G) could not boot
> because the kernel used 32-bit RTAS.
>
> Until now, the kernel always used 32-bit RTAS, even for 64-bit kernels.
> Before making an RTAS call, it sets the MSR register with the SF bit off
> and sets rtas_return_loc/rtas_entry.s to LR as the return address.
> For kdump kernels loaded above 4G, RTAS cannot jump back to this LR
> correctly and instead jumps to a 32-bit truncated address. This usually
> causes exception which leads to kernel panic.
>
> To fix this, the kernel initializes 64-bit RTAS and sets the SF bit in
> the MSR register before each RTAS call, ensuring that RTAS jumps back
> correctly if the LR address is higher than 4G. This allows kdump kernels
> at high addresses to boot properly.
>
> If 64-bit RTAS initialization fails or is not supported (e.g., in QEMU),
> the kernel falls back to 32-bit RTAS. In this case, high-address kdump
> kernels will not be allowed (handled in upcoming patches), and RTAS
> calls will use SF bit off.
>
> Changes made to achieve this:
> - Initialize 64-bit RTAS in prom_init and add a new FDT property
> linux,rtas-64
I just realized that when RTAS is instantiated using
instantiate-rtas-64, the kernel
must not only set the SF bit in the MSR but also make sure that each cell in
the RTAS Argument Call Buffer is a 64-bit, sign-extended value aligned to an
8-byte boundary. This is currently not handled correctly.
Please review the other patches; I’ll fix this issue in the next version.
> - Kernel reads linux,rtas-64 and sets a global variable rtas_64 to
> indicate whether RTAS is 64-bit or 32-bit
> - Prepare MSR register for RTAS calls based on whether RTAS is 32-bit
> or 64-bit
>
> Cc: Baoquan he <bhe at redhat.com>
> Cc: Hari Bathini <hbathini at linux.ibm.com>
> Cc: Madhavan Srinivasan <maddy at linux.ibm.com>
> Cc: Mahesh Salgaonkar <mahesh at linux.ibm.com>
> Cc: Michael Ellerman <mpe at ellerman.id.au>
> Cc: Ritesh Harjani (IBM) <ritesh.list at gmail.com>
> Cc: Shivang Upadhyay <shivangu at linux.ibm.com>
> Signed-off-by: Sourabh Jain <sourabhjain at linux.ibm.com>
> ---
> arch/powerpc/include/asm/rtas.h | 2 ++
> arch/powerpc/kernel/prom_init.c | 26 ++++++++++++++++++++++----
> arch/powerpc/kernel/rtas.c | 5 +++++
> arch/powerpc/kernel/rtas_entry.S | 17 ++++++++++++++++-
> 4 files changed, 45 insertions(+), 5 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
> index d046bbd5017d..aaa4c3bc1d61 100644
> --- a/arch/powerpc/include/asm/rtas.h
> +++ b/arch/powerpc/include/asm/rtas.h
> @@ -10,6 +10,8 @@
> #include <linux/time.h>
> #include <linux/cpumask.h>
>
> +extern int rtas_64;
> +
> /*
> * Definitions for talking to the RTAS on CHRP machines.
> *
> diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
> index 827c958677f8..ab85b8bb8d4f 100644
> --- a/arch/powerpc/kernel/prom_init.c
> +++ b/arch/powerpc/kernel/prom_init.c
> @@ -1841,6 +1841,7 @@ static void __init prom_instantiate_rtas(void)
> u32 base, entry = 0;
> __be32 val;
> u32 size = 0;
> + u32 rtas_64 = 1;
>
> prom_debug("prom_instantiate_rtas: start...\n");
>
> @@ -1867,12 +1868,25 @@ static void __init prom_instantiate_rtas(void)
>
> prom_printf("instantiating rtas at 0x%x...", base);
>
> + /*
> + * First, try to instantiate 64-bit RTAS. If that fails, fall back
> + * to 32-bit. Although 64-bit RTAS support has been available on
> + * real machines for some time, QEMU still lacks this support.
> + */
> if (call_prom_ret("call-method", 3, 2, &entry,
> - ADDR("instantiate-rtas"),
> + ADDR("instantiate-rtas-64"),
> rtas_inst, base) != 0
> - || entry == 0) {
> - prom_printf(" failed\n");
> - return;
> + || entry == 0) {
> +
> + rtas_64 = 0;
> + if (call_prom_ret("call-method", 3, 2, &entry,
> + ADDR("instantiate-rtas"),
> + rtas_inst, base) != 0
> + || entry == 0) {
> +
> + prom_printf(" failed\n");
> + return;
> + }
> }
> prom_printf(" done\n");
>
> @@ -1884,6 +1898,9 @@ static void __init prom_instantiate_rtas(void)
> val = cpu_to_be32(entry);
> prom_setprop(rtas_node, "/rtas", "linux,rtas-entry",
> &val, sizeof(val));
> + val = cpu_to_be32(rtas_64);
> + prom_setprop(rtas_node, "/rtas", "linux,rtas-64",
> + &val, sizeof(val));
>
> /* Check if it supports "query-cpu-stopped-state" */
> if (prom_getprop(rtas_node, "query-cpu-stopped-state",
> @@ -1893,6 +1910,7 @@ static void __init prom_instantiate_rtas(void)
> prom_debug("rtas base = 0x%x\n", base);
> prom_debug("rtas entry = 0x%x\n", entry);
> prom_debug("rtas size = 0x%x\n", size);
> + prom_debug("rtas 64-bit = 0x%x\n", rtas_64);
>
> prom_debug("prom_instantiate_rtas: end...\n");
> }
> diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
> index 8d81c1e7a8db..723806468984 100644
> --- a/arch/powerpc/kernel/rtas.c
> +++ b/arch/powerpc/kernel/rtas.c
> @@ -45,6 +45,8 @@
> #include <asm/trace.h>
> #include <asm/udbg.h>
>
> +int rtas_64 = 1;
> +
> struct rtas_filter {
> /* Indexes into the args buffer, -1 if not used */
> const int buf_idx1;
> @@ -2087,6 +2089,9 @@ int __init early_init_dt_scan_rtas(unsigned long node,
> entryp = of_get_flat_dt_prop(node, "linux,rtas-entry", NULL);
> sizep = of_get_flat_dt_prop(node, "rtas-size", NULL);
>
> + if (!of_get_flat_dt_prop(node, "linux,rtas-64", NULL))
> + rtas_64 = 0;
> +
> #ifdef CONFIG_PPC64
> /* need this feature to decide the crashkernel offset */
> if (of_get_flat_dt_prop(node, "ibm,hypertas-functions", NULL))
> diff --git a/arch/powerpc/kernel/rtas_entry.S b/arch/powerpc/kernel/rtas_entry.S
> index 6ce95ddadbcd..df776f0103c9 100644
> --- a/arch/powerpc/kernel/rtas_entry.S
> +++ b/arch/powerpc/kernel/rtas_entry.S
> @@ -54,6 +54,10 @@ _ASM_NOKPROBE_SYMBOL(enter_rtas)
> /*
> * 32-bit rtas on 64-bit machines has the additional problem that RTAS may
> * not preserve the upper parts of registers it uses.
> + *
> + * Note: In 64-bit RTAS, the SF bit is set so that RTAS can return
> + * correctly if the return address is above 4 GB. Everything else
> + * works the same as in 32-bit RTAS.
> */
> _GLOBAL(enter_rtas)
> mflr r0
> @@ -113,7 +117,18 @@ __enter_rtas:
> * from the saved MSR value and insert into the value RTAS will use.
> */
> extrdi r0, r6, 1, 63 - MSR_HV_LG
> - LOAD_REG_IMMEDIATE(r6, MSR_ME | MSR_RI)
> +
> + LOAD_REG_ADDR(r7, rtas_64) /* Load the address rtas_64 into r7 */
> + ld r8, 0(r7) /* Load the value of rtas_64 from memory into r8 */
> + cmpdi r8, 0 /* Compare r8 with 0 (check if rtas_64 is zero) */
> + beq no_sf_bit /* Branch to no_sf_bit if rtas_64 is zero */
> + LOAD_REG_IMMEDIATE(r6, MSR_ME | MSR_RI | MSR_SF) /* r6 = ME|RI|SF */
> + b continue
> +
> +no_sf_bit:
> + LOAD_REG_IMMEDIATE(r6, MSR_ME | MSR_RI) /* r6 = ME|RI (NO SF bit in MSR) */
> +
> +continue:
> insrdi r6, r0, 1, 63 - MSR_HV_LG
>
> li r0,0
More information about the Linuxppc-dev
mailing list