[PATCH 2/4] powerpc: move to 64-bit RTAS
Sourabh Jain
sourabhjain at linux.ibm.com
Tue Oct 28 02:13:36 AEDT 2025
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
- 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
--
2.51.0
More information about the Linuxppc-dev
mailing list