OPAL may advertise new endian-specific entry point which has different calling conventions including using the caller's stack, but otherwise provides the standard OPAL call API without any changes required to the OS.
Signed-off-by: Nicholas Piggin <npig...@gmail.com> --- arch/powerpc/boot/opal.c | 5 +++ arch/powerpc/platforms/powernv/opal-call.c | 36 ++++++++++++++++++++++ arch/powerpc/platforms/powernv/opal.c | 30 +++++++++++------- 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/boot/opal.c b/arch/powerpc/boot/opal.c index b69818ce592b..8b006a0282ac 100644 --- a/arch/powerpc/boot/opal.c +++ b/arch/powerpc/boot/opal.c @@ -13,6 +13,7 @@ struct opal { u64 base; u64 entry; + u64 v4_le_entry; } opal; static u32 opal_con_id; @@ -75,6 +76,10 @@ static void opal_init(void) if (getprop(opal_node, "opal-entry-address", &opal.entry, sizeof(u64)) < 0) return; opal.entry = be64_to_cpu(opal.entry); + + if (getprop(opal_node, "opal-v4-le-entry-address", &opal.v4_le_entry, sizeof(u64)) < 0) + return; + opal.v4_le_entry = be64_to_cpu(opal.v4_le_entry); } int opal_console_init(void *devp, struct serial_console_data *scdp) diff --git a/arch/powerpc/platforms/powernv/opal-call.c b/arch/powerpc/platforms/powernv/opal-call.c index 506b1798081a..32857254d268 100644 --- a/arch/powerpc/platforms/powernv/opal-call.c +++ b/arch/powerpc/platforms/powernv/opal-call.c @@ -92,6 +92,18 @@ static s64 __opal_call_trace(s64 a0, s64 a1, s64 a2, s64 a3, #define DO_TRACE false #endif /* CONFIG_TRACEPOINTS */ +struct opal { + u64 base; + u64 entry; + u64 size; + u64 v4_le_entry; +}; +extern struct opal opal; + +typedef int64_t (*opal_v4_le_entry_fn)(uint64_t r3, uint64_t r4, uint64_t r5, + uint64_t r6, uint64_t r7, uint64_t r8, + uint64_t r9, uint64_t r10); + static int64_t opal_call(int64_t a0, int64_t a1, int64_t a2, int64_t a3, int64_t a4, int64_t a5, int64_t a6, int64_t a7, int64_t opcode) { @@ -99,6 +111,30 @@ static int64_t opal_call(int64_t a0, int64_t a1, int64_t a2, int64_t a3, unsigned long msr = mfmsr(); bool mmu = (msr & (MSR_IR|MSR_DR)); int64_t ret; + opal_v4_le_entry_fn fn; + + if (IS_ENABLED(CONFIG_CPU_LITTLE_ENDIAN)) + fn = (opal_v4_le_entry_fn)(opal.v4_le_entry); + + if (fn) { + if (!mmu) { + BUG_ON(msr & MSR_EE); + ret = fn(opcode, a0, a1, a2, a3, a4, a5, a6); + return ret; + } + + local_irq_save(flags); + hard_irq_disable(); /* XXX r13 */ + msr &= ~MSR_EE; + mtmsr(msr & ~(MSR_IR|MSR_DR)); + + ret = fn(opcode, a0, a1, a2, a3, a4, a5, a6); + + mtmsr(msr); + local_irq_restore(flags); + + return ret; + } msr &= ~MSR_EE; diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index a0e9808237b2..d00772d40680 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -46,13 +46,14 @@ static DEFINE_SPINLOCK(msg_list_lock); static LIST_HEAD(msg_list); /* /sys/firmware/opal */ -struct kobject *opal_kobj; +struct kobject *opal_kobj __read_mostly; struct opal { u64 base; u64 entry; u64 size; -} opal; + u64 v4_le_entry; +} opal __read_mostly; struct mcheck_recoverable_range { u64 start_addr; @@ -150,14 +151,15 @@ unsigned long arch_symbol_lookup_name(const char *name) int __init early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data) { - const void *basep, *entryp, *sizep; - int basesz, entrysz, runtimesz; + const void *basep, *entryp, *v4_le_entryp, *sizep; + int basesz, entrysz, v4_le_entrysz, runtimesz; if (depth != 1 || strcmp(uname, "ibm,opal") != 0) return 0; basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz); entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz); + v4_le_entryp = of_get_flat_dt_prop(node, "opal-v4-le-entry-address", &v4_le_entrysz); sizep = of_get_flat_dt_prop(node, "opal-runtime-size", &runtimesz); if (!basep || !entryp || !sizep) @@ -166,19 +168,25 @@ int __init early_init_dt_scan_opal(unsigned long node, opal.base = of_read_number(basep, basesz/4); opal.entry = of_read_number(entryp, entrysz/4); opal.size = of_read_number(sizep, runtimesz/4); + opal.v4_le_entry = of_read_number(v4_le_entryp, v4_le_entrysz/4); + + if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) { + powerpc_firmware_features |= FW_FEATURE_OPAL; + pr_debug("OPAL detected !\n"); + } else { + panic("OPAL v3 compatible firmware not detected, can not continue.\n"); + } pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%d)\n", opal.base, basep, basesz); - pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%d)\n", + pr_debug("OPAL Entry = 0x%llx (entryp=%p entrysz=%d)\n", opal.entry, entryp, entrysz); - pr_debug("OPAL Entry = 0x%llx (sizep=%p runtimesz=%d)\n", + pr_debug("OPAL Size = 0x%llx (sizep=%p runtimesz=%d)\n", opal.size, sizep, runtimesz); + if (IS_ENABLED(CONFIG_CPU_LITTLE_ENDIAN) && v4_le_entryp) { - if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) { - powerpc_firmware_features |= FW_FEATURE_OPAL; - pr_debug("OPAL detected !\n"); - } else { - panic("OPAL != V3 detected, no longer supported.\n"); + pr_debug("OPAL v4 Entry = 0x%llx (v4_le_entryp=%p v4_le_entrysz=%d)\n", + opal.v4_le_entry, v4_le_entryp, v4_le_entrysz); } return 1; -- 2.23.0