- 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 <[email protected]>
Cc: Hari Bathini <[email protected]>
Cc: Madhavan Srinivasan <[email protected]>
Cc: Mahesh Salgaonkar <[email protected]>
Cc: Michael Ellerman <[email protected]>
Cc: Ritesh Harjani (IBM) <[email protected]>
Cc: Shivang Upadhyay <[email protected]>
Signed-off-by: Sourabh Jain <[email protected]>
---
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