162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PowerNV OPAL high level interfaces 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2011 IBM Corp. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) "opal: " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/printk.h> 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/of_fdt.h> 1462306a36Sopenharmony_ci#include <linux/of_platform.h> 1562306a36Sopenharmony_ci#include <linux/of_address.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/notifier.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/sched.h> 2062306a36Sopenharmony_ci#include <linux/kobject.h> 2162306a36Sopenharmony_ci#include <linux/delay.h> 2262306a36Sopenharmony_ci#include <linux/memblock.h> 2362306a36Sopenharmony_ci#include <linux/kthread.h> 2462306a36Sopenharmony_ci#include <linux/freezer.h> 2562306a36Sopenharmony_ci#include <linux/kmsg_dump.h> 2662306a36Sopenharmony_ci#include <linux/console.h> 2762306a36Sopenharmony_ci#include <linux/sched/debug.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <asm/machdep.h> 3062306a36Sopenharmony_ci#include <asm/opal.h> 3162306a36Sopenharmony_ci#include <asm/firmware.h> 3262306a36Sopenharmony_ci#include <asm/mce.h> 3362306a36Sopenharmony_ci#include <asm/imc-pmu.h> 3462306a36Sopenharmony_ci#include <asm/bug.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include "powernv.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define OPAL_MSG_QUEUE_MAX 16 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct opal_msg_node { 4162306a36Sopenharmony_ci struct list_head list; 4262306a36Sopenharmony_ci struct opal_msg msg; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(msg_list_lock); 4662306a36Sopenharmony_cistatic LIST_HEAD(msg_list); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* /sys/firmware/opal */ 4962306a36Sopenharmony_cistruct kobject *opal_kobj; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct opal { 5262306a36Sopenharmony_ci u64 base; 5362306a36Sopenharmony_ci u64 entry; 5462306a36Sopenharmony_ci u64 size; 5562306a36Sopenharmony_ci} opal; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct mcheck_recoverable_range { 5862306a36Sopenharmony_ci u64 start_addr; 5962306a36Sopenharmony_ci u64 end_addr; 6062306a36Sopenharmony_ci u64 recover_addr; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int msg_list_size; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic struct mcheck_recoverable_range *mc_recoverable_range; 6662306a36Sopenharmony_cistatic int mc_recoverable_range_len; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct device_node *opal_node; 6962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(opal_write_lock); 7062306a36Sopenharmony_cistatic struct atomic_notifier_head opal_msg_notifier_head[OPAL_MSG_TYPE_MAX]; 7162306a36Sopenharmony_cistatic uint32_t opal_heartbeat; 7262306a36Sopenharmony_cistatic struct task_struct *kopald_tsk; 7362306a36Sopenharmony_cistatic struct opal_msg *opal_msg; 7462306a36Sopenharmony_cistatic u32 opal_msg_size __ro_after_init; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_civoid __init opal_configure_cores(void) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci u64 reinit_flags = 0; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* Do the actual re-init, This will clobber all FPRs, VRs, etc... 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * It will preserve non volatile GPRs and HSPRG0/1. It will 8362306a36Sopenharmony_ci * also restore HIDs and other SPRs to their original value 8462306a36Sopenharmony_ci * but it might clobber a bunch. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci#ifdef __BIG_ENDIAN__ 8762306a36Sopenharmony_ci reinit_flags |= OPAL_REINIT_CPUS_HILE_BE; 8862306a36Sopenharmony_ci#else 8962306a36Sopenharmony_ci reinit_flags |= OPAL_REINIT_CPUS_HILE_LE; 9062306a36Sopenharmony_ci#endif 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* 9362306a36Sopenharmony_ci * POWER9 always support running hash: 9462306a36Sopenharmony_ci * ie. Host hash supports hash guests 9562306a36Sopenharmony_ci * Host radix supports hash/radix guests 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci if (early_cpu_has_feature(CPU_FTR_ARCH_300)) { 9862306a36Sopenharmony_ci reinit_flags |= OPAL_REINIT_CPUS_MMU_HASH; 9962306a36Sopenharmony_ci if (early_radix_enabled()) 10062306a36Sopenharmony_ci reinit_flags |= OPAL_REINIT_CPUS_MMU_RADIX; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci opal_reinit_cpus(reinit_flags); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* Restore some bits */ 10662306a36Sopenharmony_ci if (cur_cpu_spec->cpu_restore) 10762306a36Sopenharmony_ci cur_cpu_spec->cpu_restore(); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ciint __init early_init_dt_scan_opal(unsigned long node, 11162306a36Sopenharmony_ci const char *uname, int depth, void *data) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci const void *basep, *entryp, *sizep; 11462306a36Sopenharmony_ci int basesz, entrysz, runtimesz; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (depth != 1 || strcmp(uname, "ibm,opal") != 0) 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz); 12062306a36Sopenharmony_ci entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz); 12162306a36Sopenharmony_ci sizep = of_get_flat_dt_prop(node, "opal-runtime-size", &runtimesz); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (!basep || !entryp || !sizep) 12462306a36Sopenharmony_ci return 1; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci opal.base = of_read_number(basep, basesz/4); 12762306a36Sopenharmony_ci opal.entry = of_read_number(entryp, entrysz/4); 12862306a36Sopenharmony_ci opal.size = of_read_number(sizep, runtimesz/4); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%d)\n", 13162306a36Sopenharmony_ci opal.base, basep, basesz); 13262306a36Sopenharmony_ci pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%d)\n", 13362306a36Sopenharmony_ci opal.entry, entryp, entrysz); 13462306a36Sopenharmony_ci pr_debug("OPAL Entry = 0x%llx (sizep=%p runtimesz=%d)\n", 13562306a36Sopenharmony_ci opal.size, sizep, runtimesz); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) { 13862306a36Sopenharmony_ci powerpc_firmware_features |= FW_FEATURE_OPAL; 13962306a36Sopenharmony_ci pr_debug("OPAL detected !\n"); 14062306a36Sopenharmony_ci } else { 14162306a36Sopenharmony_ci panic("OPAL != V3 detected, no longer supported.\n"); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return 1; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ciint __init early_init_dt_scan_recoverable_ranges(unsigned long node, 14862306a36Sopenharmony_ci const char *uname, int depth, void *data) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci int i, psize, size; 15162306a36Sopenharmony_ci const __be32 *prop; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (depth != 1 || strcmp(uname, "ibm,opal") != 0) 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "mcheck-recoverable-ranges", &psize); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (!prop) 15962306a36Sopenharmony_ci return 1; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci pr_debug("Found machine check recoverable ranges.\n"); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* 16462306a36Sopenharmony_ci * Calculate number of available entries. 16562306a36Sopenharmony_ci * 16662306a36Sopenharmony_ci * Each recoverable address range entry is (start address, len, 16762306a36Sopenharmony_ci * recovery address), 2 cells each for start and recovery address, 16862306a36Sopenharmony_ci * 1 cell for len, totalling 5 cells per entry. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ci mc_recoverable_range_len = psize / (sizeof(*prop) * 5); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* Sanity check */ 17362306a36Sopenharmony_ci if (!mc_recoverable_range_len) 17462306a36Sopenharmony_ci return 1; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Size required to hold all the entries. */ 17762306a36Sopenharmony_ci size = mc_recoverable_range_len * 17862306a36Sopenharmony_ci sizeof(struct mcheck_recoverable_range); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* 18162306a36Sopenharmony_ci * Allocate a buffer to hold the MC recoverable ranges. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci mc_recoverable_range = memblock_alloc(size, __alignof__(u64)); 18462306a36Sopenharmony_ci if (!mc_recoverable_range) 18562306a36Sopenharmony_ci panic("%s: Failed to allocate %u bytes align=0x%lx\n", 18662306a36Sopenharmony_ci __func__, size, __alignof__(u64)); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci for (i = 0; i < mc_recoverable_range_len; i++) { 18962306a36Sopenharmony_ci mc_recoverable_range[i].start_addr = 19062306a36Sopenharmony_ci of_read_number(prop + (i * 5) + 0, 2); 19162306a36Sopenharmony_ci mc_recoverable_range[i].end_addr = 19262306a36Sopenharmony_ci mc_recoverable_range[i].start_addr + 19362306a36Sopenharmony_ci of_read_number(prop + (i * 5) + 2, 1); 19462306a36Sopenharmony_ci mc_recoverable_range[i].recover_addr = 19562306a36Sopenharmony_ci of_read_number(prop + (i * 5) + 3, 2); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci pr_debug("Machine check recoverable range: %llx..%llx: %llx\n", 19862306a36Sopenharmony_ci mc_recoverable_range[i].start_addr, 19962306a36Sopenharmony_ci mc_recoverable_range[i].end_addr, 20062306a36Sopenharmony_ci mc_recoverable_range[i].recover_addr); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci return 1; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int __init opal_register_exception_handlers(void) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci#ifdef __BIG_ENDIAN__ 20862306a36Sopenharmony_ci u64 glue; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (!(powerpc_firmware_features & FW_FEATURE_OPAL)) 21162306a36Sopenharmony_ci return -ENODEV; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* Hookup some exception handlers except machine check. We use the 21462306a36Sopenharmony_ci * fwnmi area at 0x7000 to provide the glue space to OPAL 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci glue = 0x7000; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* 21962306a36Sopenharmony_ci * Only ancient OPAL firmware requires this. 22062306a36Sopenharmony_ci * Specifically, firmware from FW810.00 (released June 2014) 22162306a36Sopenharmony_ci * through FW810.20 (Released October 2014). 22262306a36Sopenharmony_ci * 22362306a36Sopenharmony_ci * Check if we are running on newer (post Oct 2014) firmware that 22462306a36Sopenharmony_ci * exports the OPAL_HANDLE_HMI token. If yes, then don't ask OPAL to 22562306a36Sopenharmony_ci * patch the HMI interrupt and we catch it directly in Linux. 22662306a36Sopenharmony_ci * 22762306a36Sopenharmony_ci * For older firmware (i.e < FW810.20), we fallback to old behavior and 22862306a36Sopenharmony_ci * let OPAL patch the HMI vector and handle it inside OPAL firmware. 22962306a36Sopenharmony_ci * 23062306a36Sopenharmony_ci * For newer firmware we catch/handle the HMI directly in Linux. 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_ci if (!opal_check_token(OPAL_HANDLE_HMI)) { 23362306a36Sopenharmony_ci pr_info("Old firmware detected, OPAL handles HMIs.\n"); 23462306a36Sopenharmony_ci opal_register_exception_handler( 23562306a36Sopenharmony_ci OPAL_HYPERVISOR_MAINTENANCE_HANDLER, 23662306a36Sopenharmony_ci 0, glue); 23762306a36Sopenharmony_ci glue += 128; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* 24162306a36Sopenharmony_ci * Only applicable to ancient firmware, all modern 24262306a36Sopenharmony_ci * (post March 2015/skiboot 5.0) firmware will just return 24362306a36Sopenharmony_ci * OPAL_UNSUPPORTED. 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ci opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue); 24662306a36Sopenharmony_ci#endif 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_cimachine_early_initcall(powernv, opal_register_exception_handlers); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void queue_replay_msg(void *msg) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct opal_msg_node *msg_node; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (msg_list_size < OPAL_MSG_QUEUE_MAX) { 25762306a36Sopenharmony_ci msg_node = kzalloc(sizeof(*msg_node), GFP_ATOMIC); 25862306a36Sopenharmony_ci if (msg_node) { 25962306a36Sopenharmony_ci INIT_LIST_HEAD(&msg_node->list); 26062306a36Sopenharmony_ci memcpy(&msg_node->msg, msg, sizeof(struct opal_msg)); 26162306a36Sopenharmony_ci list_add_tail(&msg_node->list, &msg_list); 26262306a36Sopenharmony_ci msg_list_size++; 26362306a36Sopenharmony_ci } else 26462306a36Sopenharmony_ci pr_warn_once("message queue no memory\n"); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (msg_list_size >= OPAL_MSG_QUEUE_MAX) 26762306a36Sopenharmony_ci pr_warn_once("message queue full\n"); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic void dequeue_replay_msg(enum opal_msg_type msg_type) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct opal_msg_node *msg_node, *tmp; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci list_for_each_entry_safe(msg_node, tmp, &msg_list, list) { 27662306a36Sopenharmony_ci if (be32_to_cpu(msg_node->msg.msg_type) != msg_type) 27762306a36Sopenharmony_ci continue; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci atomic_notifier_call_chain(&opal_msg_notifier_head[msg_type], 28062306a36Sopenharmony_ci msg_type, 28162306a36Sopenharmony_ci &msg_node->msg); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci list_del(&msg_node->list); 28462306a36Sopenharmony_ci kfree(msg_node); 28562306a36Sopenharmony_ci msg_list_size--; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/* 29062306a36Sopenharmony_ci * Opal message notifier based on message type. Allow subscribers to get 29162306a36Sopenharmony_ci * notified for specific messgae type. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ciint opal_message_notifier_register(enum opal_msg_type msg_type, 29462306a36Sopenharmony_ci struct notifier_block *nb) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci int ret; 29762306a36Sopenharmony_ci unsigned long flags; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (!nb || msg_type >= OPAL_MSG_TYPE_MAX) { 30062306a36Sopenharmony_ci pr_warn("%s: Invalid arguments, msg_type:%d\n", 30162306a36Sopenharmony_ci __func__, msg_type); 30262306a36Sopenharmony_ci return -EINVAL; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci spin_lock_irqsave(&msg_list_lock, flags); 30662306a36Sopenharmony_ci ret = atomic_notifier_chain_register( 30762306a36Sopenharmony_ci &opal_msg_notifier_head[msg_type], nb); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* 31062306a36Sopenharmony_ci * If the registration succeeded, replay any queued messages that came 31162306a36Sopenharmony_ci * in prior to the notifier chain registration. msg_list_lock held here 31262306a36Sopenharmony_ci * to ensure they're delivered prior to any subsequent messages. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci if (ret == 0) 31562306a36Sopenharmony_ci dequeue_replay_msg(msg_type); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci spin_unlock_irqrestore(&msg_list_lock, flags); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return ret; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_message_notifier_register); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ciint opal_message_notifier_unregister(enum opal_msg_type msg_type, 32462306a36Sopenharmony_ci struct notifier_block *nb) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci return atomic_notifier_chain_unregister( 32762306a36Sopenharmony_ci &opal_msg_notifier_head[msg_type], nb); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_message_notifier_unregister); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic void opal_message_do_notify(uint32_t msg_type, void *msg) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci unsigned long flags; 33462306a36Sopenharmony_ci bool queued = false; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci spin_lock_irqsave(&msg_list_lock, flags); 33762306a36Sopenharmony_ci if (opal_msg_notifier_head[msg_type].head == NULL) { 33862306a36Sopenharmony_ci /* 33962306a36Sopenharmony_ci * Queue up the msg since no notifiers have registered 34062306a36Sopenharmony_ci * yet for this msg_type. 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_ci queue_replay_msg(msg); 34362306a36Sopenharmony_ci queued = true; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci spin_unlock_irqrestore(&msg_list_lock, flags); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (queued) 34862306a36Sopenharmony_ci return; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* notify subscribers */ 35162306a36Sopenharmony_ci atomic_notifier_call_chain(&opal_msg_notifier_head[msg_type], 35262306a36Sopenharmony_ci msg_type, msg); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic void opal_handle_message(void) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci s64 ret; 35862306a36Sopenharmony_ci u32 type; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci ret = opal_get_msg(__pa(opal_msg), opal_msg_size); 36162306a36Sopenharmony_ci /* No opal message pending. */ 36262306a36Sopenharmony_ci if (ret == OPAL_RESOURCE) 36362306a36Sopenharmony_ci return; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* check for errors. */ 36662306a36Sopenharmony_ci if (ret) { 36762306a36Sopenharmony_ci pr_warn("%s: Failed to retrieve opal message, err=%lld\n", 36862306a36Sopenharmony_ci __func__, ret); 36962306a36Sopenharmony_ci return; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci type = be32_to_cpu(opal_msg->msg_type); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* Sanity check */ 37562306a36Sopenharmony_ci if (type >= OPAL_MSG_TYPE_MAX) { 37662306a36Sopenharmony_ci pr_warn_once("%s: Unknown message type: %u\n", __func__, type); 37762306a36Sopenharmony_ci return; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci opal_message_do_notify(type, (void *)opal_msg); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic irqreturn_t opal_message_notify(int irq, void *data) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci opal_handle_message(); 38562306a36Sopenharmony_ci return IRQ_HANDLED; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int __init opal_message_init(struct device_node *opal_node) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci int ret, i, irq; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ret = of_property_read_u32(opal_node, "opal-msg-size", &opal_msg_size); 39362306a36Sopenharmony_ci if (ret) { 39462306a36Sopenharmony_ci pr_notice("Failed to read opal-msg-size property\n"); 39562306a36Sopenharmony_ci opal_msg_size = sizeof(struct opal_msg); 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci opal_msg = kmalloc(opal_msg_size, GFP_KERNEL); 39962306a36Sopenharmony_ci if (!opal_msg) { 40062306a36Sopenharmony_ci opal_msg_size = sizeof(struct opal_msg); 40162306a36Sopenharmony_ci /* Try to allocate fixed message size */ 40262306a36Sopenharmony_ci opal_msg = kmalloc(opal_msg_size, GFP_KERNEL); 40362306a36Sopenharmony_ci BUG_ON(opal_msg == NULL); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci for (i = 0; i < OPAL_MSG_TYPE_MAX; i++) 40762306a36Sopenharmony_ci ATOMIC_INIT_NOTIFIER_HEAD(&opal_msg_notifier_head[i]); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci irq = opal_event_request(ilog2(OPAL_EVENT_MSG_PENDING)); 41062306a36Sopenharmony_ci if (!irq) { 41162306a36Sopenharmony_ci pr_err("%s: Can't register OPAL event irq (%d)\n", 41262306a36Sopenharmony_ci __func__, irq); 41362306a36Sopenharmony_ci return irq; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci ret = request_irq(irq, opal_message_notify, 41762306a36Sopenharmony_ci IRQ_TYPE_LEVEL_HIGH, "opal-msg", NULL); 41862306a36Sopenharmony_ci if (ret) { 41962306a36Sopenharmony_ci pr_err("%s: Can't request OPAL event irq (%d)\n", 42062306a36Sopenharmony_ci __func__, ret); 42162306a36Sopenharmony_ci return ret; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ciint opal_get_chars(uint32_t vtermno, char *buf, int count) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci s64 rc; 43062306a36Sopenharmony_ci __be64 evt, len; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!opal.entry) 43362306a36Sopenharmony_ci return -ENODEV; 43462306a36Sopenharmony_ci opal_poll_events(&evt); 43562306a36Sopenharmony_ci if ((be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_INPUT) == 0) 43662306a36Sopenharmony_ci return 0; 43762306a36Sopenharmony_ci len = cpu_to_be64(count); 43862306a36Sopenharmony_ci rc = opal_console_read(vtermno, &len, buf); 43962306a36Sopenharmony_ci if (rc == OPAL_SUCCESS) 44062306a36Sopenharmony_ci return be64_to_cpu(len); 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int __opal_put_chars(uint32_t vtermno, const char *data, int total_len, bool atomic) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci unsigned long flags = 0 /* shut up gcc */; 44762306a36Sopenharmony_ci int written; 44862306a36Sopenharmony_ci __be64 olen; 44962306a36Sopenharmony_ci s64 rc; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (!opal.entry) 45262306a36Sopenharmony_ci return -ENODEV; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (atomic) 45562306a36Sopenharmony_ci spin_lock_irqsave(&opal_write_lock, flags); 45662306a36Sopenharmony_ci rc = opal_console_write_buffer_space(vtermno, &olen); 45762306a36Sopenharmony_ci if (rc || be64_to_cpu(olen) < total_len) { 45862306a36Sopenharmony_ci /* Closed -> drop characters */ 45962306a36Sopenharmony_ci if (rc) 46062306a36Sopenharmony_ci written = total_len; 46162306a36Sopenharmony_ci else 46262306a36Sopenharmony_ci written = -EAGAIN; 46362306a36Sopenharmony_ci goto out; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* Should not get a partial write here because space is available. */ 46762306a36Sopenharmony_ci olen = cpu_to_be64(total_len); 46862306a36Sopenharmony_ci rc = opal_console_write(vtermno, &olen, data); 46962306a36Sopenharmony_ci if (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { 47062306a36Sopenharmony_ci if (rc == OPAL_BUSY_EVENT) 47162306a36Sopenharmony_ci opal_poll_events(NULL); 47262306a36Sopenharmony_ci written = -EAGAIN; 47362306a36Sopenharmony_ci goto out; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* Closed or other error drop */ 47762306a36Sopenharmony_ci if (rc != OPAL_SUCCESS) { 47862306a36Sopenharmony_ci written = opal_error_code(rc); 47962306a36Sopenharmony_ci goto out; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci written = be64_to_cpu(olen); 48362306a36Sopenharmony_ci if (written < total_len) { 48462306a36Sopenharmony_ci if (atomic) { 48562306a36Sopenharmony_ci /* Should not happen */ 48662306a36Sopenharmony_ci pr_warn("atomic console write returned partial " 48762306a36Sopenharmony_ci "len=%d written=%d\n", total_len, written); 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci if (!written) 49062306a36Sopenharmony_ci written = -EAGAIN; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ciout: 49462306a36Sopenharmony_ci if (atomic) 49562306a36Sopenharmony_ci spin_unlock_irqrestore(&opal_write_lock, flags); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return written; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ciint opal_put_chars(uint32_t vtermno, const char *data, int total_len) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci return __opal_put_chars(vtermno, data, total_len, false); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci/* 50662306a36Sopenharmony_ci * opal_put_chars_atomic will not perform partial-writes. Data will be 50762306a36Sopenharmony_ci * atomically written to the terminal or not at all. This is not strictly 50862306a36Sopenharmony_ci * true at the moment because console space can race with OPAL's console 50962306a36Sopenharmony_ci * writes. 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_ciint opal_put_chars_atomic(uint32_t vtermno, const char *data, int total_len) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci return __opal_put_chars(vtermno, data, total_len, true); 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic s64 __opal_flush_console(uint32_t vtermno) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci s64 rc; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (!opal_check_token(OPAL_CONSOLE_FLUSH)) { 52162306a36Sopenharmony_ci __be64 evt; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* 52462306a36Sopenharmony_ci * If OPAL_CONSOLE_FLUSH is not implemented in the firmware, 52562306a36Sopenharmony_ci * the console can still be flushed by calling the polling 52662306a36Sopenharmony_ci * function while it has OPAL_EVENT_CONSOLE_OUTPUT events. 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_ci WARN_ONCE(1, "opal: OPAL_CONSOLE_FLUSH missing.\n"); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci opal_poll_events(&evt); 53162306a36Sopenharmony_ci if (!(be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT)) 53262306a36Sopenharmony_ci return OPAL_SUCCESS; 53362306a36Sopenharmony_ci return OPAL_BUSY; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci } else { 53662306a36Sopenharmony_ci rc = opal_console_flush(vtermno); 53762306a36Sopenharmony_ci if (rc == OPAL_BUSY_EVENT) { 53862306a36Sopenharmony_ci opal_poll_events(NULL); 53962306a36Sopenharmony_ci rc = OPAL_BUSY; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci return rc; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci/* 54762306a36Sopenharmony_ci * opal_flush_console spins until the console is flushed 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_ciint opal_flush_console(uint32_t vtermno) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci for (;;) { 55262306a36Sopenharmony_ci s64 rc = __opal_flush_console(vtermno); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (rc == OPAL_BUSY || rc == OPAL_PARTIAL) { 55562306a36Sopenharmony_ci mdelay(1); 55662306a36Sopenharmony_ci continue; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return opal_error_code(rc); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci/* 56462306a36Sopenharmony_ci * opal_flush_chars is an hvc interface that sleeps until the console is 56562306a36Sopenharmony_ci * flushed if wait, otherwise it will return -EBUSY if the console has data, 56662306a36Sopenharmony_ci * -EAGAIN if it has data and some of it was flushed. 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_ciint opal_flush_chars(uint32_t vtermno, bool wait) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci for (;;) { 57162306a36Sopenharmony_ci s64 rc = __opal_flush_console(vtermno); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (rc == OPAL_BUSY || rc == OPAL_PARTIAL) { 57462306a36Sopenharmony_ci if (wait) { 57562306a36Sopenharmony_ci msleep(OPAL_BUSY_DELAY_MS); 57662306a36Sopenharmony_ci continue; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci if (rc == OPAL_PARTIAL) 57962306a36Sopenharmony_ci return -EAGAIN; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci return opal_error_code(rc); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic int opal_recover_mce(struct pt_regs *regs, 58762306a36Sopenharmony_ci struct machine_check_event *evt) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci int recovered = 0; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (regs_is_unrecoverable(regs)) { 59262306a36Sopenharmony_ci /* If MSR_RI isn't set, we cannot recover */ 59362306a36Sopenharmony_ci pr_err("Machine check interrupt unrecoverable: MSR(RI=0)\n"); 59462306a36Sopenharmony_ci recovered = 0; 59562306a36Sopenharmony_ci } else if (evt->disposition == MCE_DISPOSITION_RECOVERED) { 59662306a36Sopenharmony_ci /* Platform corrected itself */ 59762306a36Sopenharmony_ci recovered = 1; 59862306a36Sopenharmony_ci } else if (evt->severity == MCE_SEV_FATAL) { 59962306a36Sopenharmony_ci /* Fatal machine check */ 60062306a36Sopenharmony_ci pr_err("Machine check interrupt is fatal\n"); 60162306a36Sopenharmony_ci recovered = 0; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (!recovered && evt->sync_error) { 60562306a36Sopenharmony_ci /* 60662306a36Sopenharmony_ci * Try to kill processes if we get a synchronous machine check 60762306a36Sopenharmony_ci * (e.g., one caused by execution of this instruction). This 60862306a36Sopenharmony_ci * will devolve into a panic if we try to kill init or are in 60962306a36Sopenharmony_ci * an interrupt etc. 61062306a36Sopenharmony_ci * 61162306a36Sopenharmony_ci * TODO: Queue up this address for hwpoisioning later. 61262306a36Sopenharmony_ci * TODO: This is not quite right for d-side machine 61362306a36Sopenharmony_ci * checks ->nip is not necessarily the important 61462306a36Sopenharmony_ci * address. 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_ci if ((user_mode(regs))) { 61762306a36Sopenharmony_ci _exception(SIGBUS, regs, BUS_MCEERR_AR, regs->nip); 61862306a36Sopenharmony_ci recovered = 1; 61962306a36Sopenharmony_ci } else if (die_will_crash()) { 62062306a36Sopenharmony_ci /* 62162306a36Sopenharmony_ci * die() would kill the kernel, so better to go via 62262306a36Sopenharmony_ci * the platform reboot code that will log the 62362306a36Sopenharmony_ci * machine check. 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_ci recovered = 0; 62662306a36Sopenharmony_ci } else { 62762306a36Sopenharmony_ci die_mce("Machine check", regs, SIGBUS); 62862306a36Sopenharmony_ci recovered = 1; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci return recovered; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_civoid __noreturn pnv_platform_error_reboot(struct pt_regs *regs, const char *msg) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci panic_flush_kmsg_start(); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci pr_emerg("Hardware platform error: %s\n", msg); 64062306a36Sopenharmony_ci if (regs) 64162306a36Sopenharmony_ci show_regs(regs); 64262306a36Sopenharmony_ci smp_send_stop(); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci panic_flush_kmsg_end(); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* 64762306a36Sopenharmony_ci * Don't bother to shut things down because this will 64862306a36Sopenharmony_ci * xstop the system. 64962306a36Sopenharmony_ci */ 65062306a36Sopenharmony_ci if (opal_cec_reboot2(OPAL_REBOOT_PLATFORM_ERROR, msg) 65162306a36Sopenharmony_ci == OPAL_UNSUPPORTED) { 65262306a36Sopenharmony_ci pr_emerg("Reboot type %d not supported for %s\n", 65362306a36Sopenharmony_ci OPAL_REBOOT_PLATFORM_ERROR, msg); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* 65762306a36Sopenharmony_ci * We reached here. There can be three possibilities: 65862306a36Sopenharmony_ci * 1. We are running on a firmware level that do not support 65962306a36Sopenharmony_ci * opal_cec_reboot2() 66062306a36Sopenharmony_ci * 2. We are running on a firmware level that do not support 66162306a36Sopenharmony_ci * OPAL_REBOOT_PLATFORM_ERROR reboot type. 66262306a36Sopenharmony_ci * 3. We are running on FSP based system that does not need 66362306a36Sopenharmony_ci * opal to trigger checkstop explicitly for error analysis. 66462306a36Sopenharmony_ci * The FSP PRD component would have already got notified 66562306a36Sopenharmony_ci * about this error through other channels. 66662306a36Sopenharmony_ci * 4. We are running on a newer skiboot that by default does 66762306a36Sopenharmony_ci * not cause a checkstop, drops us back to the kernel to 66862306a36Sopenharmony_ci * extract context and state at the time of the error. 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci panic(msg); 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ciint opal_machine_check(struct pt_regs *regs) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct machine_check_event evt; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (!get_mce_event(&evt, MCE_EVENT_RELEASE)) 67962306a36Sopenharmony_ci return 0; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* Print things out */ 68262306a36Sopenharmony_ci if (evt.version != MCE_V1) { 68362306a36Sopenharmony_ci pr_err("Machine Check Exception, Unknown event version %d !\n", 68462306a36Sopenharmony_ci evt.version); 68562306a36Sopenharmony_ci return 0; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci machine_check_print_event_info(&evt, user_mode(regs), false); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (opal_recover_mce(regs, &evt)) 69062306a36Sopenharmony_ci return 1; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci pnv_platform_error_reboot(regs, "Unrecoverable Machine Check exception"); 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci/* Early hmi handler called in real mode. */ 69662306a36Sopenharmony_ciint opal_hmi_exception_early(struct pt_regs *regs) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci s64 rc; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci /* 70162306a36Sopenharmony_ci * call opal hmi handler. Pass paca address as token. 70262306a36Sopenharmony_ci * The return value OPAL_SUCCESS is an indication that there is 70362306a36Sopenharmony_ci * an HMI event generated waiting to pull by Linux. 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_ci rc = opal_handle_hmi(); 70662306a36Sopenharmony_ci if (rc == OPAL_SUCCESS) { 70762306a36Sopenharmony_ci local_paca->hmi_event_available = 1; 70862306a36Sopenharmony_ci return 1; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ciint opal_hmi_exception_early2(struct pt_regs *regs) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci s64 rc; 71662306a36Sopenharmony_ci __be64 out_flags; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* 71962306a36Sopenharmony_ci * call opal hmi handler. 72062306a36Sopenharmony_ci * Check 64-bit flag mask to find out if an event was generated, 72162306a36Sopenharmony_ci * and whether TB is still valid or not etc. 72262306a36Sopenharmony_ci */ 72362306a36Sopenharmony_ci rc = opal_handle_hmi2(&out_flags); 72462306a36Sopenharmony_ci if (rc != OPAL_SUCCESS) 72562306a36Sopenharmony_ci return 0; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (be64_to_cpu(out_flags) & OPAL_HMI_FLAGS_NEW_EVENT) 72862306a36Sopenharmony_ci local_paca->hmi_event_available = 1; 72962306a36Sopenharmony_ci if (be64_to_cpu(out_flags) & OPAL_HMI_FLAGS_TOD_TB_FAIL) 73062306a36Sopenharmony_ci tb_invalid = true; 73162306a36Sopenharmony_ci return 1; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci/* HMI exception handler called in virtual mode when irqs are next enabled. */ 73562306a36Sopenharmony_ciint opal_handle_hmi_exception(struct pt_regs *regs) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci /* 73862306a36Sopenharmony_ci * Check if HMI event is available. 73962306a36Sopenharmony_ci * if Yes, then wake kopald to process them. 74062306a36Sopenharmony_ci */ 74162306a36Sopenharmony_ci if (!local_paca->hmi_event_available) 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci local_paca->hmi_event_available = 0; 74562306a36Sopenharmony_ci opal_wake_poller(); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci return 1; 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic uint64_t find_recovery_address(uint64_t nip) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci int i; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci for (i = 0; i < mc_recoverable_range_len; i++) 75562306a36Sopenharmony_ci if ((nip >= mc_recoverable_range[i].start_addr) && 75662306a36Sopenharmony_ci (nip < mc_recoverable_range[i].end_addr)) 75762306a36Sopenharmony_ci return mc_recoverable_range[i].recover_addr; 75862306a36Sopenharmony_ci return 0; 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cibool opal_mce_check_early_recovery(struct pt_regs *regs) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci uint64_t recover_addr = 0; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (!opal.base || !opal.size) 76662306a36Sopenharmony_ci goto out; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if ((regs->nip >= opal.base) && 76962306a36Sopenharmony_ci (regs->nip < (opal.base + opal.size))) 77062306a36Sopenharmony_ci recover_addr = find_recovery_address(regs->nip); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* 77362306a36Sopenharmony_ci * Setup regs->nip to rfi into fixup address. 77462306a36Sopenharmony_ci */ 77562306a36Sopenharmony_ci if (recover_addr) 77662306a36Sopenharmony_ci regs_set_return_ip(regs, recover_addr); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ciout: 77962306a36Sopenharmony_ci return !!recover_addr; 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_cistatic int __init opal_sysfs_init(void) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci opal_kobj = kobject_create_and_add("opal", firmware_kobj); 78562306a36Sopenharmony_ci if (!opal_kobj) { 78662306a36Sopenharmony_ci pr_warn("kobject_create_and_add opal failed\n"); 78762306a36Sopenharmony_ci return -ENOMEM; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci return 0; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic ssize_t export_attr_read(struct file *fp, struct kobject *kobj, 79462306a36Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 79562306a36Sopenharmony_ci loff_t off, size_t count) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci return memory_read_from_buffer(buf, count, &off, bin_attr->private, 79862306a36Sopenharmony_ci bin_attr->size); 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic int opal_add_one_export(struct kobject *parent, const char *export_name, 80262306a36Sopenharmony_ci struct device_node *np, const char *prop_name) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci struct bin_attribute *attr = NULL; 80562306a36Sopenharmony_ci const char *name = NULL; 80662306a36Sopenharmony_ci u64 vals[2]; 80762306a36Sopenharmony_ci int rc; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci rc = of_property_read_u64_array(np, prop_name, &vals[0], 2); 81062306a36Sopenharmony_ci if (rc) 81162306a36Sopenharmony_ci goto out; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci attr = kzalloc(sizeof(*attr), GFP_KERNEL); 81462306a36Sopenharmony_ci if (!attr) { 81562306a36Sopenharmony_ci rc = -ENOMEM; 81662306a36Sopenharmony_ci goto out; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci name = kstrdup(export_name, GFP_KERNEL); 81962306a36Sopenharmony_ci if (!name) { 82062306a36Sopenharmony_ci rc = -ENOMEM; 82162306a36Sopenharmony_ci goto out; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci sysfs_bin_attr_init(attr); 82562306a36Sopenharmony_ci attr->attr.name = name; 82662306a36Sopenharmony_ci attr->attr.mode = 0400; 82762306a36Sopenharmony_ci attr->read = export_attr_read; 82862306a36Sopenharmony_ci attr->private = __va(vals[0]); 82962306a36Sopenharmony_ci attr->size = vals[1]; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci rc = sysfs_create_bin_file(parent, attr); 83262306a36Sopenharmony_ciout: 83362306a36Sopenharmony_ci if (rc) { 83462306a36Sopenharmony_ci kfree(name); 83562306a36Sopenharmony_ci kfree(attr); 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci return rc; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic void opal_add_exported_attrs(struct device_node *np, 84262306a36Sopenharmony_ci struct kobject *kobj) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct device_node *child; 84562306a36Sopenharmony_ci struct property *prop; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci for_each_property_of_node(np, prop) { 84862306a36Sopenharmony_ci int rc; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (!strcmp(prop->name, "name") || 85162306a36Sopenharmony_ci !strcmp(prop->name, "phandle")) 85262306a36Sopenharmony_ci continue; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci rc = opal_add_one_export(kobj, prop->name, np, prop->name); 85562306a36Sopenharmony_ci if (rc) { 85662306a36Sopenharmony_ci pr_warn("Unable to add export %pOF/%s, rc = %d!\n", 85762306a36Sopenharmony_ci np, prop->name, rc); 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci for_each_child_of_node(np, child) { 86262306a36Sopenharmony_ci struct kobject *child_kobj; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci child_kobj = kobject_create_and_add(child->name, kobj); 86562306a36Sopenharmony_ci if (!child_kobj) { 86662306a36Sopenharmony_ci pr_err("Unable to create export dir for %pOF\n", child); 86762306a36Sopenharmony_ci continue; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci opal_add_exported_attrs(child, child_kobj); 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci/* 87562306a36Sopenharmony_ci * opal_export_attrs: creates a sysfs node for each property listed in 87662306a36Sopenharmony_ci * the device-tree under /ibm,opal/firmware/exports/ 87762306a36Sopenharmony_ci * All new sysfs nodes are created under /opal/exports/. 87862306a36Sopenharmony_ci * This allows for reserved memory regions (e.g. HDAT) to be read. 87962306a36Sopenharmony_ci * The new sysfs nodes are only readable by root. 88062306a36Sopenharmony_ci */ 88162306a36Sopenharmony_cistatic void opal_export_attrs(void) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci struct device_node *np; 88462306a36Sopenharmony_ci struct kobject *kobj; 88562306a36Sopenharmony_ci int rc; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci np = of_find_node_by_path("/ibm,opal/firmware/exports"); 88862306a36Sopenharmony_ci if (!np) 88962306a36Sopenharmony_ci return; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* Create new 'exports' directory - /sys/firmware/opal/exports */ 89262306a36Sopenharmony_ci kobj = kobject_create_and_add("exports", opal_kobj); 89362306a36Sopenharmony_ci if (!kobj) { 89462306a36Sopenharmony_ci pr_warn("kobject_create_and_add() of exports failed\n"); 89562306a36Sopenharmony_ci of_node_put(np); 89662306a36Sopenharmony_ci return; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci opal_add_exported_attrs(np, kobj); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* 90262306a36Sopenharmony_ci * NB: symbol_map existed before the generic export interface so it 90362306a36Sopenharmony_ci * lives under the top level opal_kobj. 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_ci rc = opal_add_one_export(opal_kobj, "symbol_map", 90662306a36Sopenharmony_ci np->parent, "symbol-map"); 90762306a36Sopenharmony_ci if (rc) 90862306a36Sopenharmony_ci pr_warn("Error %d creating OPAL symbols file\n", rc); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci of_node_put(np); 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic void __init opal_dump_region_init(void) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci void *addr; 91662306a36Sopenharmony_ci uint64_t size; 91762306a36Sopenharmony_ci int rc; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci if (!opal_check_token(OPAL_REGISTER_DUMP_REGION)) 92062306a36Sopenharmony_ci return; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci /* Register kernel log buffer */ 92362306a36Sopenharmony_ci addr = log_buf_addr_get(); 92462306a36Sopenharmony_ci if (addr == NULL) 92562306a36Sopenharmony_ci return; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci size = log_buf_len_get(); 92862306a36Sopenharmony_ci if (size == 0) 92962306a36Sopenharmony_ci return; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci rc = opal_register_dump_region(OPAL_DUMP_REGION_LOG_BUF, 93262306a36Sopenharmony_ci __pa(addr), size); 93362306a36Sopenharmony_ci /* Don't warn if this is just an older OPAL that doesn't 93462306a36Sopenharmony_ci * know about that call 93562306a36Sopenharmony_ci */ 93662306a36Sopenharmony_ci if (rc && rc != OPAL_UNSUPPORTED) 93762306a36Sopenharmony_ci pr_warn("DUMP: Failed to register kernel log buffer. " 93862306a36Sopenharmony_ci "rc = %d\n", rc); 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic void __init opal_pdev_init(const char *compatible) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci struct device_node *np; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci for_each_compatible_node(np, NULL, compatible) 94662306a36Sopenharmony_ci of_platform_device_create(np, NULL, NULL); 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic void __init opal_imc_init_dev(void) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci struct device_node *np; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, IMC_DTB_COMPAT); 95462306a36Sopenharmony_ci if (np) 95562306a36Sopenharmony_ci of_platform_device_create(np, NULL, NULL); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci of_node_put(np); 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic int kopald(void *unused) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci unsigned long timeout = msecs_to_jiffies(opal_heartbeat) + 1; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci set_freezable(); 96562306a36Sopenharmony_ci do { 96662306a36Sopenharmony_ci try_to_freeze(); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci opal_handle_events(); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 97162306a36Sopenharmony_ci if (opal_have_pending_events()) 97262306a36Sopenharmony_ci __set_current_state(TASK_RUNNING); 97362306a36Sopenharmony_ci else 97462306a36Sopenharmony_ci schedule_timeout(timeout); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci } while (!kthread_should_stop()); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci return 0; 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_civoid opal_wake_poller(void) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci if (kopald_tsk) 98462306a36Sopenharmony_ci wake_up_process(kopald_tsk); 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic void __init opal_init_heartbeat(void) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci /* Old firwmware, we assume the HVC heartbeat is sufficient */ 99062306a36Sopenharmony_ci if (of_property_read_u32(opal_node, "ibm,heartbeat-ms", 99162306a36Sopenharmony_ci &opal_heartbeat) != 0) 99262306a36Sopenharmony_ci opal_heartbeat = 0; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (opal_heartbeat) 99562306a36Sopenharmony_ci kopald_tsk = kthread_run(kopald, NULL, "kopald"); 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic int __init opal_init(void) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct device_node *np, *consoles, *leds; 100162306a36Sopenharmony_ci int rc; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci opal_node = of_find_node_by_path("/ibm,opal"); 100462306a36Sopenharmony_ci if (!opal_node) { 100562306a36Sopenharmony_ci pr_warn("Device node not found\n"); 100662306a36Sopenharmony_ci return -ENODEV; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci /* Register OPAL consoles if any ports */ 101062306a36Sopenharmony_ci consoles = of_find_node_by_path("/ibm,opal/consoles"); 101162306a36Sopenharmony_ci if (consoles) { 101262306a36Sopenharmony_ci for_each_child_of_node(consoles, np) { 101362306a36Sopenharmony_ci if (!of_node_name_eq(np, "serial")) 101462306a36Sopenharmony_ci continue; 101562306a36Sopenharmony_ci of_platform_device_create(np, NULL, NULL); 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci of_node_put(consoles); 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci /* Initialise OPAL messaging system */ 102162306a36Sopenharmony_ci opal_message_init(opal_node); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci /* Initialise OPAL asynchronous completion interface */ 102462306a36Sopenharmony_ci opal_async_comp_init(); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci /* Initialise OPAL sensor interface */ 102762306a36Sopenharmony_ci opal_sensor_init(); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci /* Initialise OPAL hypervisor maintainence interrupt handling */ 103062306a36Sopenharmony_ci opal_hmi_handler_init(); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci /* Create i2c platform devices */ 103362306a36Sopenharmony_ci opal_pdev_init("ibm,opal-i2c"); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci /* Handle non-volatile memory devices */ 103662306a36Sopenharmony_ci opal_pdev_init("pmem-region"); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci /* Setup a heatbeat thread if requested by OPAL */ 103962306a36Sopenharmony_ci opal_init_heartbeat(); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci /* Detect In-Memory Collection counters and create devices*/ 104262306a36Sopenharmony_ci opal_imc_init_dev(); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci /* Create leds platform devices */ 104562306a36Sopenharmony_ci leds = of_find_node_by_path("/ibm,opal/leds"); 104662306a36Sopenharmony_ci if (leds) { 104762306a36Sopenharmony_ci of_platform_device_create(leds, "opal_leds", NULL); 104862306a36Sopenharmony_ci of_node_put(leds); 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci /* Initialise OPAL message log interface */ 105262306a36Sopenharmony_ci opal_msglog_init(); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci /* Create "opal" kobject under /sys/firmware */ 105562306a36Sopenharmony_ci rc = opal_sysfs_init(); 105662306a36Sopenharmony_ci if (rc == 0) { 105762306a36Sopenharmony_ci /* Setup dump region interface */ 105862306a36Sopenharmony_ci opal_dump_region_init(); 105962306a36Sopenharmony_ci /* Setup error log interface */ 106062306a36Sopenharmony_ci rc = opal_elog_init(); 106162306a36Sopenharmony_ci /* Setup code update interface */ 106262306a36Sopenharmony_ci opal_flash_update_init(); 106362306a36Sopenharmony_ci /* Setup platform dump extract interface */ 106462306a36Sopenharmony_ci opal_platform_dump_init(); 106562306a36Sopenharmony_ci /* Setup system parameters interface */ 106662306a36Sopenharmony_ci opal_sys_param_init(); 106762306a36Sopenharmony_ci /* Setup message log sysfs interface. */ 106862306a36Sopenharmony_ci opal_msglog_sysfs_init(); 106962306a36Sopenharmony_ci /* Add all export properties*/ 107062306a36Sopenharmony_ci opal_export_attrs(); 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* Initialize platform devices: IPMI backend, PRD & flash interface */ 107462306a36Sopenharmony_ci opal_pdev_init("ibm,opal-ipmi"); 107562306a36Sopenharmony_ci opal_pdev_init("ibm,opal-flash"); 107662306a36Sopenharmony_ci opal_pdev_init("ibm,opal-prd"); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci /* Initialise platform device: oppanel interface */ 107962306a36Sopenharmony_ci opal_pdev_init("ibm,opal-oppanel"); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* Initialise OPAL kmsg dumper for flushing console on panic */ 108262306a36Sopenharmony_ci opal_kmsg_init(); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci /* Initialise OPAL powercap interface */ 108562306a36Sopenharmony_ci opal_powercap_init(); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci /* Initialise OPAL Power-Shifting-Ratio interface */ 108862306a36Sopenharmony_ci opal_psr_init(); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci /* Initialise OPAL sensor groups */ 109162306a36Sopenharmony_ci opal_sensor_groups_init(); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* Initialise OPAL Power control interface */ 109462306a36Sopenharmony_ci opal_power_control_init(); 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci /* Initialize OPAL secure variables */ 109762306a36Sopenharmony_ci opal_pdev_init("ibm,secvar-backend"); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci return 0; 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_cimachine_subsys_initcall(powernv, opal_init); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_civoid opal_shutdown(void) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci long rc = OPAL_BUSY; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci opal_event_shutdown(); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci /* 111062306a36Sopenharmony_ci * Then sync with OPAL which ensure anything that can 111162306a36Sopenharmony_ci * potentially write to our memory has completed such 111262306a36Sopenharmony_ci * as an ongoing dump retrieval 111362306a36Sopenharmony_ci */ 111462306a36Sopenharmony_ci while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { 111562306a36Sopenharmony_ci rc = opal_sync_host_reboot(); 111662306a36Sopenharmony_ci if (rc == OPAL_BUSY) 111762306a36Sopenharmony_ci opal_poll_events(NULL); 111862306a36Sopenharmony_ci else 111962306a36Sopenharmony_ci mdelay(10); 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci /* Unregister memory dump region */ 112362306a36Sopenharmony_ci if (opal_check_token(OPAL_UNREGISTER_DUMP_REGION)) 112462306a36Sopenharmony_ci opal_unregister_dump_region(OPAL_DUMP_REGION_LOG_BUF); 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci/* Export this so that test modules can use it */ 112862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_invalid_call); 112962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_xscom_read); 113062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_xscom_write); 113162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_ipmi_send); 113262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_ipmi_recv); 113362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_flash_read); 113462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_flash_write); 113562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_flash_erase); 113662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_prd_msg); 113762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_check_token); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci/* Convert a region of vmalloc memory to an opal sg list */ 114062306a36Sopenharmony_cistruct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr, 114162306a36Sopenharmony_ci unsigned long vmalloc_size) 114262306a36Sopenharmony_ci{ 114362306a36Sopenharmony_ci struct opal_sg_list *sg, *first = NULL; 114462306a36Sopenharmony_ci unsigned long i = 0; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci sg = kzalloc(PAGE_SIZE, GFP_KERNEL); 114762306a36Sopenharmony_ci if (!sg) 114862306a36Sopenharmony_ci goto nomem; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci first = sg; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci while (vmalloc_size > 0) { 115362306a36Sopenharmony_ci uint64_t data = vmalloc_to_pfn(vmalloc_addr) << PAGE_SHIFT; 115462306a36Sopenharmony_ci uint64_t length = min(vmalloc_size, PAGE_SIZE); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci sg->entry[i].data = cpu_to_be64(data); 115762306a36Sopenharmony_ci sg->entry[i].length = cpu_to_be64(length); 115862306a36Sopenharmony_ci i++; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci if (i >= SG_ENTRIES_PER_NODE) { 116162306a36Sopenharmony_ci struct opal_sg_list *next; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci next = kzalloc(PAGE_SIZE, GFP_KERNEL); 116462306a36Sopenharmony_ci if (!next) 116562306a36Sopenharmony_ci goto nomem; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci sg->length = cpu_to_be64( 116862306a36Sopenharmony_ci i * sizeof(struct opal_sg_entry) + 16); 116962306a36Sopenharmony_ci i = 0; 117062306a36Sopenharmony_ci sg->next = cpu_to_be64(__pa(next)); 117162306a36Sopenharmony_ci sg = next; 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci vmalloc_addr += length; 117562306a36Sopenharmony_ci vmalloc_size -= length; 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci sg->length = cpu_to_be64(i * sizeof(struct opal_sg_entry) + 16); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci return first; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_cinomem: 118362306a36Sopenharmony_ci pr_err("%s : Failed to allocate memory\n", __func__); 118462306a36Sopenharmony_ci opal_free_sg_list(first); 118562306a36Sopenharmony_ci return NULL; 118662306a36Sopenharmony_ci} 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_civoid opal_free_sg_list(struct opal_sg_list *sg) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci while (sg) { 119162306a36Sopenharmony_ci uint64_t next = be64_to_cpu(sg->next); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci kfree(sg); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci if (next) 119662306a36Sopenharmony_ci sg = __va(next); 119762306a36Sopenharmony_ci else 119862306a36Sopenharmony_ci sg = NULL; 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ciint opal_error_code(int rc) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci switch (rc) { 120562306a36Sopenharmony_ci case OPAL_SUCCESS: return 0; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci case OPAL_PARAMETER: return -EINVAL; 120862306a36Sopenharmony_ci case OPAL_ASYNC_COMPLETION: return -EINPROGRESS; 120962306a36Sopenharmony_ci case OPAL_BUSY: 121062306a36Sopenharmony_ci case OPAL_BUSY_EVENT: return -EBUSY; 121162306a36Sopenharmony_ci case OPAL_NO_MEM: return -ENOMEM; 121262306a36Sopenharmony_ci case OPAL_PERMISSION: return -EPERM; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci case OPAL_UNSUPPORTED: return -EIO; 121562306a36Sopenharmony_ci case OPAL_HARDWARE: return -EIO; 121662306a36Sopenharmony_ci case OPAL_INTERNAL_ERROR: return -EIO; 121762306a36Sopenharmony_ci case OPAL_TIMEOUT: return -ETIMEDOUT; 121862306a36Sopenharmony_ci default: 121962306a36Sopenharmony_ci pr_err("%s: unexpected OPAL error %d\n", __func__, rc); 122062306a36Sopenharmony_ci return -EIO; 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci} 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_civoid powernv_set_nmmu_ptcr(unsigned long ptcr) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci int rc; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci if (firmware_has_feature(FW_FEATURE_OPAL)) { 122962306a36Sopenharmony_ci rc = opal_nmmu_set_ptcr(-1UL, ptcr); 123062306a36Sopenharmony_ci if (rc != OPAL_SUCCESS && rc != OPAL_UNSUPPORTED) 123162306a36Sopenharmony_ci pr_warn("%s: Unable to set nest mmu ptcr\n", __func__); 123262306a36Sopenharmony_ci } 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_poll_events); 123662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_rtc_read); 123762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_rtc_write); 123862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_tpo_read); 123962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_tpo_write); 124062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_i2c_request); 124162306a36Sopenharmony_ci/* Export these symbols for PowerNV LED class driver */ 124262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_leds_get_ind); 124362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_leds_set_ind); 124462306a36Sopenharmony_ci/* Export this symbol for PowerNV Operator Panel class driver */ 124562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_write_oppanel_async); 124662306a36Sopenharmony_ci/* Export this for KVM */ 124762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_int_set_mfrr); 124862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_int_eoi); 124962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_error_code); 125062306a36Sopenharmony_ci/* Export the below symbol for NX compression */ 125162306a36Sopenharmony_ciEXPORT_SYMBOL(opal_nx_coproc_init); 1252