[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