This implements OPAL_REGISTER_OS_OPS and implements the printf
service.

When this API is called, OPAL switches to V4 mode which requires
the OS to subsequently handle its program interrupts and printf
calls.

Signed-off-by: Nicholas Piggin <npig...@gmail.com>
---
 arch/powerpc/include/asm/opal-api.h        |  7 ++++-
 arch/powerpc/include/asm/opal.h            |  1 +
 arch/powerpc/platforms/powernv/opal-call.c |  1 +
 arch/powerpc/platforms/powernv/opal.c      | 36 ++++++++++++++++++++++
 4 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/include/asm/opal-api.h 
b/arch/powerpc/include/asm/opal-api.h
index 0be5ff4e51b5..1b2f176677fc 100644
--- a/arch/powerpc/include/asm/opal-api.h
+++ b/arch/powerpc/include/asm/opal-api.h
@@ -218,7 +218,8 @@
 #define OPAL_SYM_TO_ADDR                       182
 #define OPAL_REPORT_TRAP                       183
 #define OPAL_FIND_VM_AREA                      184
-#define OPAL_LAST                              184
+#define OPAL_REGISTER_OS_OPS                   185
+#define OPAL_LAST                              185
 
 #define QUIESCE_HOLD                   1 /* Spin all calls at entry */
 #define QUIESCE_REJECT                 2 /* Fail all calls with OPAL_BUSY */
@@ -1202,6 +1203,10 @@ struct opal_vm_area {
        __be64  vm_flags;
 };
 
+struct opal_os_ops {
+       __be64  os_printf; /* void printf(int32_t level, const char *str) */
+};
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* __OPAL_API_H */
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 199b5582b700..09985b7718b3 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -406,6 +406,7 @@ void opal_psr_init(void);
 void opal_sensor_groups_init(void);
 
 int64_t opal_find_vm_area(uint64_t addr, struct opal_vm_area *opal_vm_area);
+int64_t opal_register_os_ops(struct opal_os_ops *ops, uint64_t size);
 
 #endif /* __ASSEMBLY__ */
 
diff --git a/arch/powerpc/platforms/powernv/opal-call.c 
b/arch/powerpc/platforms/powernv/opal-call.c
index 4bdad3d2fa18..11f419e76059 100644
--- a/arch/powerpc/platforms/powernv/opal-call.c
+++ b/arch/powerpc/platforms/powernv/opal-call.c
@@ -350,3 +350,4 @@ OPAL_CALL(opal_addr_to_sym,                 
OPAL_ADDR_TO_SYM);
 OPAL_CALL(opal_sym_to_addr,                    OPAL_SYM_TO_ADDR);
 OPAL_CALL(opal_report_trap,                    OPAL_REPORT_TRAP);
 OPAL_CALL(opal_find_vm_area,                   OPAL_FIND_VM_AREA);
+OPAL_CALL(opal_register_os_ops,                        OPAL_REGISTER_OS_OPS);
diff --git a/arch/powerpc/platforms/powernv/opal.c 
b/arch/powerpc/platforms/powernv/opal.c
index 98d6d7fc5411..0fbfcd088c58 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -47,6 +47,7 @@ static LIST_HEAD(msg_list);
 
 struct mm_struct *opal_mm __read_mostly;
 bool opal_v4_present __read_mostly;
+bool opal_v4_enabled __read_mostly;
 bool opal_mm_enabled __read_mostly;
 
 /* /sys/firmware/opal */
@@ -152,6 +153,8 @@ unsigned long arch_symbol_lookup_name(const char *name)
        return be64_to_cpu(addr);
 }
 
+static void os_printf(int32_t level, const char *str);
+
 int __init early_init_dt_scan_opal(unsigned long node,
                                   const char *uname, int depth, void *data)
 {
@@ -1045,6 +1048,28 @@ static void opal_init_heartbeat(void)
                kopald_tsk = kthread_run(kopald, NULL, "kopald");
 }
 
+static void os_printf(int32_t level, const char *str)
+{
+       const char *l;
+
+       /* Assuming printk does not work in real mode */
+       if (WARN_ON_ONCE(!(mfmsr() & (MSR_IR|MSR_DR))))
+               return;
+
+       switch (level) {
+       case 0: l = KERN_EMERG; break;
+       case 1: l = KERN_ALERT; break;
+       case 2: l = KERN_CRIT; break;
+       case 3: l = KERN_ERR; break;
+       case 4: l = KERN_WARNING; break;
+       case 5: l = KERN_NOTICE; break;
+       case 6: l = KERN_INFO; break;
+       case 7: l = KERN_DEBUG; break;
+       default: l = KERN_ERR;
+       }
+       printk("%s[OPAL] %s", l, str);
+}
+
 static pgprot_t opal_vm_flags_to_prot(uint64_t flags)
 {
        pgprot_t prot;
@@ -1137,6 +1162,8 @@ static int __init opal_init_early(void)
        int rc;
 
        if (opal_v4_present) {
+               struct opal_os_ops opal_os_ops;
+
                if (radix_enabled()) {
                        /* Hash can't resolve SLB faults to the switched mm */
                        rc = opal_init_mm();
@@ -1144,6 +1171,15 @@ static int __init opal_init_early(void)
                                pr_warn("OPAL virtual memory init failed, 
firmware will run in real-mode.\n");
                        }
                }
+
+               memset(&opal_os_ops, 0, sizeof(opal_os_ops));
+               opal_os_ops.os_printf = cpu_to_be64(&os_printf);
+               if (opal_register_os_ops(&opal_os_ops, sizeof(opal_os_ops))) {
+                       pr_warn("OPAL register OS ops failed, firmware will run 
in v3 mode.\n");
+               } else {
+                       opal_v4_enabled = true;
+                       pr_warn("OPAL running in v4 mode!\n");
+               }
        }
 
        return 0;
-- 
2.23.0

Reply via email to