18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PowerNV OPAL high level interfaces 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2011 IBM Corp. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "opal: " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/printk.h> 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/of_fdt.h> 148c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 158c2ecf20Sopenharmony_ci#include <linux/of_address.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/notifier.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/sched.h> 208c2ecf20Sopenharmony_ci#include <linux/kobject.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/memblock.h> 238c2ecf20Sopenharmony_ci#include <linux/kthread.h> 248c2ecf20Sopenharmony_ci#include <linux/freezer.h> 258c2ecf20Sopenharmony_ci#include <linux/kmsg_dump.h> 268c2ecf20Sopenharmony_ci#include <linux/console.h> 278c2ecf20Sopenharmony_ci#include <linux/sched/debug.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <asm/machdep.h> 308c2ecf20Sopenharmony_ci#include <asm/opal.h> 318c2ecf20Sopenharmony_ci#include <asm/firmware.h> 328c2ecf20Sopenharmony_ci#include <asm/mce.h> 338c2ecf20Sopenharmony_ci#include <asm/imc-pmu.h> 348c2ecf20Sopenharmony_ci#include <asm/bug.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include "powernv.h" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define OPAL_MSG_QUEUE_MAX 16 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct opal_msg_node { 418c2ecf20Sopenharmony_ci struct list_head list; 428c2ecf20Sopenharmony_ci struct opal_msg msg; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(msg_list_lock); 468c2ecf20Sopenharmony_cistatic LIST_HEAD(msg_list); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* /sys/firmware/opal */ 498c2ecf20Sopenharmony_cistruct kobject *opal_kobj; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct opal { 528c2ecf20Sopenharmony_ci u64 base; 538c2ecf20Sopenharmony_ci u64 entry; 548c2ecf20Sopenharmony_ci u64 size; 558c2ecf20Sopenharmony_ci} opal; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct mcheck_recoverable_range { 588c2ecf20Sopenharmony_ci u64 start_addr; 598c2ecf20Sopenharmony_ci u64 end_addr; 608c2ecf20Sopenharmony_ci u64 recover_addr; 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int msg_list_size; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic struct mcheck_recoverable_range *mc_recoverable_range; 668c2ecf20Sopenharmony_cistatic int mc_recoverable_range_len; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistruct device_node *opal_node; 698c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(opal_write_lock); 708c2ecf20Sopenharmony_cistatic struct atomic_notifier_head opal_msg_notifier_head[OPAL_MSG_TYPE_MAX]; 718c2ecf20Sopenharmony_cistatic uint32_t opal_heartbeat; 728c2ecf20Sopenharmony_cistatic struct task_struct *kopald_tsk; 738c2ecf20Sopenharmony_cistatic struct opal_msg *opal_msg; 748c2ecf20Sopenharmony_cistatic u32 opal_msg_size __ro_after_init; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_civoid opal_configure_cores(void) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci u64 reinit_flags = 0; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* Do the actual re-init, This will clobber all FPRs, VRs, etc... 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * It will preserve non volatile GPRs and HSPRG0/1. It will 838c2ecf20Sopenharmony_ci * also restore HIDs and other SPRs to their original value 848c2ecf20Sopenharmony_ci * but it might clobber a bunch. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN__ 878c2ecf20Sopenharmony_ci reinit_flags |= OPAL_REINIT_CPUS_HILE_BE; 888c2ecf20Sopenharmony_ci#else 898c2ecf20Sopenharmony_ci reinit_flags |= OPAL_REINIT_CPUS_HILE_LE; 908c2ecf20Sopenharmony_ci#endif 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* 938c2ecf20Sopenharmony_ci * POWER9 always support running hash: 948c2ecf20Sopenharmony_ci * ie. Host hash supports hash guests 958c2ecf20Sopenharmony_ci * Host radix supports hash/radix guests 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci if (early_cpu_has_feature(CPU_FTR_ARCH_300)) { 988c2ecf20Sopenharmony_ci reinit_flags |= OPAL_REINIT_CPUS_MMU_HASH; 998c2ecf20Sopenharmony_ci if (early_radix_enabled()) 1008c2ecf20Sopenharmony_ci reinit_flags |= OPAL_REINIT_CPUS_MMU_RADIX; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci opal_reinit_cpus(reinit_flags); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* Restore some bits */ 1068c2ecf20Sopenharmony_ci if (cur_cpu_spec->cpu_restore) 1078c2ecf20Sopenharmony_ci cur_cpu_spec->cpu_restore(); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ciint __init early_init_dt_scan_opal(unsigned long node, 1118c2ecf20Sopenharmony_ci const char *uname, int depth, void *data) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci const void *basep, *entryp, *sizep; 1148c2ecf20Sopenharmony_ci int basesz, entrysz, runtimesz; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (depth != 1 || strcmp(uname, "ibm,opal") != 0) 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz); 1208c2ecf20Sopenharmony_ci entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz); 1218c2ecf20Sopenharmony_ci sizep = of_get_flat_dt_prop(node, "opal-runtime-size", &runtimesz); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (!basep || !entryp || !sizep) 1248c2ecf20Sopenharmony_ci return 1; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci opal.base = of_read_number(basep, basesz/4); 1278c2ecf20Sopenharmony_ci opal.entry = of_read_number(entryp, entrysz/4); 1288c2ecf20Sopenharmony_ci opal.size = of_read_number(sizep, runtimesz/4); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%d)\n", 1318c2ecf20Sopenharmony_ci opal.base, basep, basesz); 1328c2ecf20Sopenharmony_ci pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%d)\n", 1338c2ecf20Sopenharmony_ci opal.entry, entryp, entrysz); 1348c2ecf20Sopenharmony_ci pr_debug("OPAL Entry = 0x%llx (sizep=%p runtimesz=%d)\n", 1358c2ecf20Sopenharmony_ci opal.size, sizep, runtimesz); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) { 1388c2ecf20Sopenharmony_ci powerpc_firmware_features |= FW_FEATURE_OPAL; 1398c2ecf20Sopenharmony_ci pr_debug("OPAL detected !\n"); 1408c2ecf20Sopenharmony_ci } else { 1418c2ecf20Sopenharmony_ci panic("OPAL != V3 detected, no longer supported.\n"); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return 1; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ciint __init early_init_dt_scan_recoverable_ranges(unsigned long node, 1488c2ecf20Sopenharmony_ci const char *uname, int depth, void *data) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int i, psize, size; 1518c2ecf20Sopenharmony_ci const __be32 *prop; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (depth != 1 || strcmp(uname, "ibm,opal") != 0) 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci prop = of_get_flat_dt_prop(node, "mcheck-recoverable-ranges", &psize); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (!prop) 1598c2ecf20Sopenharmony_ci return 1; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci pr_debug("Found machine check recoverable ranges.\n"); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* 1648c2ecf20Sopenharmony_ci * Calculate number of available entries. 1658c2ecf20Sopenharmony_ci * 1668c2ecf20Sopenharmony_ci * Each recoverable address range entry is (start address, len, 1678c2ecf20Sopenharmony_ci * recovery address), 2 cells each for start and recovery address, 1688c2ecf20Sopenharmony_ci * 1 cell for len, totalling 5 cells per entry. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci mc_recoverable_range_len = psize / (sizeof(*prop) * 5); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Sanity check */ 1738c2ecf20Sopenharmony_ci if (!mc_recoverable_range_len) 1748c2ecf20Sopenharmony_ci return 1; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* Size required to hold all the entries. */ 1778c2ecf20Sopenharmony_ci size = mc_recoverable_range_len * 1788c2ecf20Sopenharmony_ci sizeof(struct mcheck_recoverable_range); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* 1818c2ecf20Sopenharmony_ci * Allocate a buffer to hold the MC recoverable ranges. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_ci mc_recoverable_range = memblock_alloc(size, __alignof__(u64)); 1848c2ecf20Sopenharmony_ci if (!mc_recoverable_range) 1858c2ecf20Sopenharmony_ci panic("%s: Failed to allocate %u bytes align=0x%lx\n", 1868c2ecf20Sopenharmony_ci __func__, size, __alignof__(u64)); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci for (i = 0; i < mc_recoverable_range_len; i++) { 1898c2ecf20Sopenharmony_ci mc_recoverable_range[i].start_addr = 1908c2ecf20Sopenharmony_ci of_read_number(prop + (i * 5) + 0, 2); 1918c2ecf20Sopenharmony_ci mc_recoverable_range[i].end_addr = 1928c2ecf20Sopenharmony_ci mc_recoverable_range[i].start_addr + 1938c2ecf20Sopenharmony_ci of_read_number(prop + (i * 5) + 2, 1); 1948c2ecf20Sopenharmony_ci mc_recoverable_range[i].recover_addr = 1958c2ecf20Sopenharmony_ci of_read_number(prop + (i * 5) + 3, 2); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci pr_debug("Machine check recoverable range: %llx..%llx: %llx\n", 1988c2ecf20Sopenharmony_ci mc_recoverable_range[i].start_addr, 1998c2ecf20Sopenharmony_ci mc_recoverable_range[i].end_addr, 2008c2ecf20Sopenharmony_ci mc_recoverable_range[i].recover_addr); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci return 1; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int __init opal_register_exception_handlers(void) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN__ 2088c2ecf20Sopenharmony_ci u64 glue; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (!(powerpc_firmware_features & FW_FEATURE_OPAL)) 2118c2ecf20Sopenharmony_ci return -ENODEV; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* Hookup some exception handlers except machine check. We use the 2148c2ecf20Sopenharmony_ci * fwnmi area at 0x7000 to provide the glue space to OPAL 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci glue = 0x7000; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* 2198c2ecf20Sopenharmony_ci * Only ancient OPAL firmware requires this. 2208c2ecf20Sopenharmony_ci * Specifically, firmware from FW810.00 (released June 2014) 2218c2ecf20Sopenharmony_ci * through FW810.20 (Released October 2014). 2228c2ecf20Sopenharmony_ci * 2238c2ecf20Sopenharmony_ci * Check if we are running on newer (post Oct 2014) firmware that 2248c2ecf20Sopenharmony_ci * exports the OPAL_HANDLE_HMI token. If yes, then don't ask OPAL to 2258c2ecf20Sopenharmony_ci * patch the HMI interrupt and we catch it directly in Linux. 2268c2ecf20Sopenharmony_ci * 2278c2ecf20Sopenharmony_ci * For older firmware (i.e < FW810.20), we fallback to old behavior and 2288c2ecf20Sopenharmony_ci * let OPAL patch the HMI vector and handle it inside OPAL firmware. 2298c2ecf20Sopenharmony_ci * 2308c2ecf20Sopenharmony_ci * For newer firmware we catch/handle the HMI directly in Linux. 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_ci if (!opal_check_token(OPAL_HANDLE_HMI)) { 2338c2ecf20Sopenharmony_ci pr_info("Old firmware detected, OPAL handles HMIs.\n"); 2348c2ecf20Sopenharmony_ci opal_register_exception_handler( 2358c2ecf20Sopenharmony_ci OPAL_HYPERVISOR_MAINTENANCE_HANDLER, 2368c2ecf20Sopenharmony_ci 0, glue); 2378c2ecf20Sopenharmony_ci glue += 128; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* 2418c2ecf20Sopenharmony_ci * Only applicable to ancient firmware, all modern 2428c2ecf20Sopenharmony_ci * (post March 2015/skiboot 5.0) firmware will just return 2438c2ecf20Sopenharmony_ci * OPAL_UNSUPPORTED. 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue); 2468c2ecf20Sopenharmony_ci#endif 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_cimachine_early_initcall(powernv, opal_register_exception_handlers); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic void queue_replay_msg(void *msg) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct opal_msg_node *msg_node; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (msg_list_size < OPAL_MSG_QUEUE_MAX) { 2578c2ecf20Sopenharmony_ci msg_node = kzalloc(sizeof(*msg_node), GFP_ATOMIC); 2588c2ecf20Sopenharmony_ci if (msg_node) { 2598c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&msg_node->list); 2608c2ecf20Sopenharmony_ci memcpy(&msg_node->msg, msg, sizeof(struct opal_msg)); 2618c2ecf20Sopenharmony_ci list_add_tail(&msg_node->list, &msg_list); 2628c2ecf20Sopenharmony_ci msg_list_size++; 2638c2ecf20Sopenharmony_ci } else 2648c2ecf20Sopenharmony_ci pr_warn_once("message queue no memory\n"); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (msg_list_size >= OPAL_MSG_QUEUE_MAX) 2678c2ecf20Sopenharmony_ci pr_warn_once("message queue full\n"); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void dequeue_replay_msg(enum opal_msg_type msg_type) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct opal_msg_node *msg_node, *tmp; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci list_for_each_entry_safe(msg_node, tmp, &msg_list, list) { 2768c2ecf20Sopenharmony_ci if (be32_to_cpu(msg_node->msg.msg_type) != msg_type) 2778c2ecf20Sopenharmony_ci continue; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci atomic_notifier_call_chain(&opal_msg_notifier_head[msg_type], 2808c2ecf20Sopenharmony_ci msg_type, 2818c2ecf20Sopenharmony_ci &msg_node->msg); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci list_del(&msg_node->list); 2848c2ecf20Sopenharmony_ci kfree(msg_node); 2858c2ecf20Sopenharmony_ci msg_list_size--; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci/* 2908c2ecf20Sopenharmony_ci * Opal message notifier based on message type. Allow subscribers to get 2918c2ecf20Sopenharmony_ci * notified for specific messgae type. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_ciint opal_message_notifier_register(enum opal_msg_type msg_type, 2948c2ecf20Sopenharmony_ci struct notifier_block *nb) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci int ret; 2978c2ecf20Sopenharmony_ci unsigned long flags; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (!nb || msg_type >= OPAL_MSG_TYPE_MAX) { 3008c2ecf20Sopenharmony_ci pr_warn("%s: Invalid arguments, msg_type:%d\n", 3018c2ecf20Sopenharmony_ci __func__, msg_type); 3028c2ecf20Sopenharmony_ci return -EINVAL; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci spin_lock_irqsave(&msg_list_lock, flags); 3068c2ecf20Sopenharmony_ci ret = atomic_notifier_chain_register( 3078c2ecf20Sopenharmony_ci &opal_msg_notifier_head[msg_type], nb); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* 3108c2ecf20Sopenharmony_ci * If the registration succeeded, replay any queued messages that came 3118c2ecf20Sopenharmony_ci * in prior to the notifier chain registration. msg_list_lock held here 3128c2ecf20Sopenharmony_ci * to ensure they're delivered prior to any subsequent messages. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci if (ret == 0) 3158c2ecf20Sopenharmony_ci dequeue_replay_msg(msg_type); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&msg_list_lock, flags); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return ret; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_message_notifier_register); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ciint opal_message_notifier_unregister(enum opal_msg_type msg_type, 3248c2ecf20Sopenharmony_ci struct notifier_block *nb) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci return atomic_notifier_chain_unregister( 3278c2ecf20Sopenharmony_ci &opal_msg_notifier_head[msg_type], nb); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_message_notifier_unregister); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic void opal_message_do_notify(uint32_t msg_type, void *msg) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci unsigned long flags; 3348c2ecf20Sopenharmony_ci bool queued = false; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci spin_lock_irqsave(&msg_list_lock, flags); 3378c2ecf20Sopenharmony_ci if (opal_msg_notifier_head[msg_type].head == NULL) { 3388c2ecf20Sopenharmony_ci /* 3398c2ecf20Sopenharmony_ci * Queue up the msg since no notifiers have registered 3408c2ecf20Sopenharmony_ci * yet for this msg_type. 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_ci queue_replay_msg(msg); 3438c2ecf20Sopenharmony_ci queued = true; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&msg_list_lock, flags); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (queued) 3488c2ecf20Sopenharmony_ci return; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* notify subscribers */ 3518c2ecf20Sopenharmony_ci atomic_notifier_call_chain(&opal_msg_notifier_head[msg_type], 3528c2ecf20Sopenharmony_ci msg_type, msg); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic void opal_handle_message(void) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci s64 ret; 3588c2ecf20Sopenharmony_ci u32 type; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci ret = opal_get_msg(__pa(opal_msg), opal_msg_size); 3618c2ecf20Sopenharmony_ci /* No opal message pending. */ 3628c2ecf20Sopenharmony_ci if (ret == OPAL_RESOURCE) 3638c2ecf20Sopenharmony_ci return; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* check for errors. */ 3668c2ecf20Sopenharmony_ci if (ret) { 3678c2ecf20Sopenharmony_ci pr_warn("%s: Failed to retrieve opal message, err=%lld\n", 3688c2ecf20Sopenharmony_ci __func__, ret); 3698c2ecf20Sopenharmony_ci return; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci type = be32_to_cpu(opal_msg->msg_type); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* Sanity check */ 3758c2ecf20Sopenharmony_ci if (type >= OPAL_MSG_TYPE_MAX) { 3768c2ecf20Sopenharmony_ci pr_warn_once("%s: Unknown message type: %u\n", __func__, type); 3778c2ecf20Sopenharmony_ci return; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci opal_message_do_notify(type, (void *)opal_msg); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic irqreturn_t opal_message_notify(int irq, void *data) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci opal_handle_message(); 3858c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic int __init opal_message_init(struct device_node *opal_node) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci int ret, i, irq; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci ret = of_property_read_u32(opal_node, "opal-msg-size", &opal_msg_size); 3938c2ecf20Sopenharmony_ci if (ret) { 3948c2ecf20Sopenharmony_ci pr_notice("Failed to read opal-msg-size property\n"); 3958c2ecf20Sopenharmony_ci opal_msg_size = sizeof(struct opal_msg); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci opal_msg = kmalloc(opal_msg_size, GFP_KERNEL); 3998c2ecf20Sopenharmony_ci if (!opal_msg) { 4008c2ecf20Sopenharmony_ci opal_msg_size = sizeof(struct opal_msg); 4018c2ecf20Sopenharmony_ci /* Try to allocate fixed message size */ 4028c2ecf20Sopenharmony_ci opal_msg = kmalloc(opal_msg_size, GFP_KERNEL); 4038c2ecf20Sopenharmony_ci BUG_ON(opal_msg == NULL); 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci for (i = 0; i < OPAL_MSG_TYPE_MAX; i++) 4078c2ecf20Sopenharmony_ci ATOMIC_INIT_NOTIFIER_HEAD(&opal_msg_notifier_head[i]); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci irq = opal_event_request(ilog2(OPAL_EVENT_MSG_PENDING)); 4108c2ecf20Sopenharmony_ci if (!irq) { 4118c2ecf20Sopenharmony_ci pr_err("%s: Can't register OPAL event irq (%d)\n", 4128c2ecf20Sopenharmony_ci __func__, irq); 4138c2ecf20Sopenharmony_ci return irq; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci ret = request_irq(irq, opal_message_notify, 4178c2ecf20Sopenharmony_ci IRQ_TYPE_LEVEL_HIGH, "opal-msg", NULL); 4188c2ecf20Sopenharmony_ci if (ret) { 4198c2ecf20Sopenharmony_ci pr_err("%s: Can't request OPAL event irq (%d)\n", 4208c2ecf20Sopenharmony_ci __func__, ret); 4218c2ecf20Sopenharmony_ci return ret; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ciint opal_get_chars(uint32_t vtermno, char *buf, int count) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci s64 rc; 4308c2ecf20Sopenharmony_ci __be64 evt, len; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (!opal.entry) 4338c2ecf20Sopenharmony_ci return -ENODEV; 4348c2ecf20Sopenharmony_ci opal_poll_events(&evt); 4358c2ecf20Sopenharmony_ci if ((be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_INPUT) == 0) 4368c2ecf20Sopenharmony_ci return 0; 4378c2ecf20Sopenharmony_ci len = cpu_to_be64(count); 4388c2ecf20Sopenharmony_ci rc = opal_console_read(vtermno, &len, buf); 4398c2ecf20Sopenharmony_ci if (rc == OPAL_SUCCESS) 4408c2ecf20Sopenharmony_ci return be64_to_cpu(len); 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic int __opal_put_chars(uint32_t vtermno, const char *data, int total_len, bool atomic) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci unsigned long flags = 0 /* shut up gcc */; 4478c2ecf20Sopenharmony_ci int written; 4488c2ecf20Sopenharmony_ci __be64 olen; 4498c2ecf20Sopenharmony_ci s64 rc; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (!opal.entry) 4528c2ecf20Sopenharmony_ci return -ENODEV; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (atomic) 4558c2ecf20Sopenharmony_ci spin_lock_irqsave(&opal_write_lock, flags); 4568c2ecf20Sopenharmony_ci rc = opal_console_write_buffer_space(vtermno, &olen); 4578c2ecf20Sopenharmony_ci if (rc || be64_to_cpu(olen) < total_len) { 4588c2ecf20Sopenharmony_ci /* Closed -> drop characters */ 4598c2ecf20Sopenharmony_ci if (rc) 4608c2ecf20Sopenharmony_ci written = total_len; 4618c2ecf20Sopenharmony_ci else 4628c2ecf20Sopenharmony_ci written = -EAGAIN; 4638c2ecf20Sopenharmony_ci goto out; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* Should not get a partial write here because space is available. */ 4678c2ecf20Sopenharmony_ci olen = cpu_to_be64(total_len); 4688c2ecf20Sopenharmony_ci rc = opal_console_write(vtermno, &olen, data); 4698c2ecf20Sopenharmony_ci if (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { 4708c2ecf20Sopenharmony_ci if (rc == OPAL_BUSY_EVENT) 4718c2ecf20Sopenharmony_ci opal_poll_events(NULL); 4728c2ecf20Sopenharmony_ci written = -EAGAIN; 4738c2ecf20Sopenharmony_ci goto out; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* Closed or other error drop */ 4778c2ecf20Sopenharmony_ci if (rc != OPAL_SUCCESS) { 4788c2ecf20Sopenharmony_ci written = opal_error_code(rc); 4798c2ecf20Sopenharmony_ci goto out; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci written = be64_to_cpu(olen); 4838c2ecf20Sopenharmony_ci if (written < total_len) { 4848c2ecf20Sopenharmony_ci if (atomic) { 4858c2ecf20Sopenharmony_ci /* Should not happen */ 4868c2ecf20Sopenharmony_ci pr_warn("atomic console write returned partial " 4878c2ecf20Sopenharmony_ci "len=%d written=%d\n", total_len, written); 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci if (!written) 4908c2ecf20Sopenharmony_ci written = -EAGAIN; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ciout: 4948c2ecf20Sopenharmony_ci if (atomic) 4958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opal_write_lock, flags); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return written; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ciint opal_put_chars(uint32_t vtermno, const char *data, int total_len) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci return __opal_put_chars(vtermno, data, total_len, false); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/* 5068c2ecf20Sopenharmony_ci * opal_put_chars_atomic will not perform partial-writes. Data will be 5078c2ecf20Sopenharmony_ci * atomically written to the terminal or not at all. This is not strictly 5088c2ecf20Sopenharmony_ci * true at the moment because console space can race with OPAL's console 5098c2ecf20Sopenharmony_ci * writes. 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_ciint opal_put_chars_atomic(uint32_t vtermno, const char *data, int total_len) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci return __opal_put_chars(vtermno, data, total_len, true); 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic s64 __opal_flush_console(uint32_t vtermno) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci s64 rc; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (!opal_check_token(OPAL_CONSOLE_FLUSH)) { 5218c2ecf20Sopenharmony_ci __be64 evt; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* 5248c2ecf20Sopenharmony_ci * If OPAL_CONSOLE_FLUSH is not implemented in the firmware, 5258c2ecf20Sopenharmony_ci * the console can still be flushed by calling the polling 5268c2ecf20Sopenharmony_ci * function while it has OPAL_EVENT_CONSOLE_OUTPUT events. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_ci WARN_ONCE(1, "opal: OPAL_CONSOLE_FLUSH missing.\n"); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci opal_poll_events(&evt); 5318c2ecf20Sopenharmony_ci if (!(be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT)) 5328c2ecf20Sopenharmony_ci return OPAL_SUCCESS; 5338c2ecf20Sopenharmony_ci return OPAL_BUSY; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci } else { 5368c2ecf20Sopenharmony_ci rc = opal_console_flush(vtermno); 5378c2ecf20Sopenharmony_ci if (rc == OPAL_BUSY_EVENT) { 5388c2ecf20Sopenharmony_ci opal_poll_events(NULL); 5398c2ecf20Sopenharmony_ci rc = OPAL_BUSY; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci return rc; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci/* 5478c2ecf20Sopenharmony_ci * opal_flush_console spins until the console is flushed 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_ciint opal_flush_console(uint32_t vtermno) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci for (;;) { 5528c2ecf20Sopenharmony_ci s64 rc = __opal_flush_console(vtermno); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (rc == OPAL_BUSY || rc == OPAL_PARTIAL) { 5558c2ecf20Sopenharmony_ci mdelay(1); 5568c2ecf20Sopenharmony_ci continue; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci return opal_error_code(rc); 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci/* 5648c2ecf20Sopenharmony_ci * opal_flush_chars is an hvc interface that sleeps until the console is 5658c2ecf20Sopenharmony_ci * flushed if wait, otherwise it will return -EBUSY if the console has data, 5668c2ecf20Sopenharmony_ci * -EAGAIN if it has data and some of it was flushed. 5678c2ecf20Sopenharmony_ci */ 5688c2ecf20Sopenharmony_ciint opal_flush_chars(uint32_t vtermno, bool wait) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci for (;;) { 5718c2ecf20Sopenharmony_ci s64 rc = __opal_flush_console(vtermno); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (rc == OPAL_BUSY || rc == OPAL_PARTIAL) { 5748c2ecf20Sopenharmony_ci if (wait) { 5758c2ecf20Sopenharmony_ci msleep(OPAL_BUSY_DELAY_MS); 5768c2ecf20Sopenharmony_ci continue; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci if (rc == OPAL_PARTIAL) 5798c2ecf20Sopenharmony_ci return -EAGAIN; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return opal_error_code(rc); 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic int opal_recover_mce(struct pt_regs *regs, 5878c2ecf20Sopenharmony_ci struct machine_check_event *evt) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci int recovered = 0; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (!(regs->msr & MSR_RI)) { 5928c2ecf20Sopenharmony_ci /* If MSR_RI isn't set, we cannot recover */ 5938c2ecf20Sopenharmony_ci pr_err("Machine check interrupt unrecoverable: MSR(RI=0)\n"); 5948c2ecf20Sopenharmony_ci recovered = 0; 5958c2ecf20Sopenharmony_ci } else if (evt->disposition == MCE_DISPOSITION_RECOVERED) { 5968c2ecf20Sopenharmony_ci /* Platform corrected itself */ 5978c2ecf20Sopenharmony_ci recovered = 1; 5988c2ecf20Sopenharmony_ci } else if (evt->severity == MCE_SEV_FATAL) { 5998c2ecf20Sopenharmony_ci /* Fatal machine check */ 6008c2ecf20Sopenharmony_ci pr_err("Machine check interrupt is fatal\n"); 6018c2ecf20Sopenharmony_ci recovered = 0; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (!recovered && evt->sync_error) { 6058c2ecf20Sopenharmony_ci /* 6068c2ecf20Sopenharmony_ci * Try to kill processes if we get a synchronous machine check 6078c2ecf20Sopenharmony_ci * (e.g., one caused by execution of this instruction). This 6088c2ecf20Sopenharmony_ci * will devolve into a panic if we try to kill init or are in 6098c2ecf20Sopenharmony_ci * an interrupt etc. 6108c2ecf20Sopenharmony_ci * 6118c2ecf20Sopenharmony_ci * TODO: Queue up this address for hwpoisioning later. 6128c2ecf20Sopenharmony_ci * TODO: This is not quite right for d-side machine 6138c2ecf20Sopenharmony_ci * checks ->nip is not necessarily the important 6148c2ecf20Sopenharmony_ci * address. 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_ci if ((user_mode(regs))) { 6178c2ecf20Sopenharmony_ci _exception(SIGBUS, regs, BUS_MCEERR_AR, regs->nip); 6188c2ecf20Sopenharmony_ci recovered = 1; 6198c2ecf20Sopenharmony_ci } else if (die_will_crash()) { 6208c2ecf20Sopenharmony_ci /* 6218c2ecf20Sopenharmony_ci * die() would kill the kernel, so better to go via 6228c2ecf20Sopenharmony_ci * the platform reboot code that will log the 6238c2ecf20Sopenharmony_ci * machine check. 6248c2ecf20Sopenharmony_ci */ 6258c2ecf20Sopenharmony_ci recovered = 0; 6268c2ecf20Sopenharmony_ci } else { 6278c2ecf20Sopenharmony_ci die("Machine check", regs, SIGBUS); 6288c2ecf20Sopenharmony_ci recovered = 1; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return recovered; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_civoid __noreturn pnv_platform_error_reboot(struct pt_regs *regs, const char *msg) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci panic_flush_kmsg_start(); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci pr_emerg("Hardware platform error: %s\n", msg); 6408c2ecf20Sopenharmony_ci if (regs) 6418c2ecf20Sopenharmony_ci show_regs(regs); 6428c2ecf20Sopenharmony_ci smp_send_stop(); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci panic_flush_kmsg_end(); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci /* 6478c2ecf20Sopenharmony_ci * Don't bother to shut things down because this will 6488c2ecf20Sopenharmony_ci * xstop the system. 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_ci if (opal_cec_reboot2(OPAL_REBOOT_PLATFORM_ERROR, msg) 6518c2ecf20Sopenharmony_ci == OPAL_UNSUPPORTED) { 6528c2ecf20Sopenharmony_ci pr_emerg("Reboot type %d not supported for %s\n", 6538c2ecf20Sopenharmony_ci OPAL_REBOOT_PLATFORM_ERROR, msg); 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* 6578c2ecf20Sopenharmony_ci * We reached here. There can be three possibilities: 6588c2ecf20Sopenharmony_ci * 1. We are running on a firmware level that do not support 6598c2ecf20Sopenharmony_ci * opal_cec_reboot2() 6608c2ecf20Sopenharmony_ci * 2. We are running on a firmware level that do not support 6618c2ecf20Sopenharmony_ci * OPAL_REBOOT_PLATFORM_ERROR reboot type. 6628c2ecf20Sopenharmony_ci * 3. We are running on FSP based system that does not need 6638c2ecf20Sopenharmony_ci * opal to trigger checkstop explicitly for error analysis. 6648c2ecf20Sopenharmony_ci * The FSP PRD component would have already got notified 6658c2ecf20Sopenharmony_ci * about this error through other channels. 6668c2ecf20Sopenharmony_ci * 4. We are running on a newer skiboot that by default does 6678c2ecf20Sopenharmony_ci * not cause a checkstop, drops us back to the kernel to 6688c2ecf20Sopenharmony_ci * extract context and state at the time of the error. 6698c2ecf20Sopenharmony_ci */ 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci panic(msg); 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ciint opal_machine_check(struct pt_regs *regs) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci struct machine_check_event evt; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (!get_mce_event(&evt, MCE_EVENT_RELEASE)) 6798c2ecf20Sopenharmony_ci return 0; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci /* Print things out */ 6828c2ecf20Sopenharmony_ci if (evt.version != MCE_V1) { 6838c2ecf20Sopenharmony_ci pr_err("Machine Check Exception, Unknown event version %d !\n", 6848c2ecf20Sopenharmony_ci evt.version); 6858c2ecf20Sopenharmony_ci return 0; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci machine_check_print_event_info(&evt, user_mode(regs), false); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (opal_recover_mce(regs, &evt)) 6908c2ecf20Sopenharmony_ci return 1; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci pnv_platform_error_reboot(regs, "Unrecoverable Machine Check exception"); 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci/* Early hmi handler called in real mode. */ 6968c2ecf20Sopenharmony_ciint opal_hmi_exception_early(struct pt_regs *regs) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci s64 rc; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* 7018c2ecf20Sopenharmony_ci * call opal hmi handler. Pass paca address as token. 7028c2ecf20Sopenharmony_ci * The return value OPAL_SUCCESS is an indication that there is 7038c2ecf20Sopenharmony_ci * an HMI event generated waiting to pull by Linux. 7048c2ecf20Sopenharmony_ci */ 7058c2ecf20Sopenharmony_ci rc = opal_handle_hmi(); 7068c2ecf20Sopenharmony_ci if (rc == OPAL_SUCCESS) { 7078c2ecf20Sopenharmony_ci local_paca->hmi_event_available = 1; 7088c2ecf20Sopenharmony_ci return 1; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci return 0; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ciint opal_hmi_exception_early2(struct pt_regs *regs) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci s64 rc; 7168c2ecf20Sopenharmony_ci __be64 out_flags; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* 7198c2ecf20Sopenharmony_ci * call opal hmi handler. 7208c2ecf20Sopenharmony_ci * Check 64-bit flag mask to find out if an event was generated, 7218c2ecf20Sopenharmony_ci * and whether TB is still valid or not etc. 7228c2ecf20Sopenharmony_ci */ 7238c2ecf20Sopenharmony_ci rc = opal_handle_hmi2(&out_flags); 7248c2ecf20Sopenharmony_ci if (rc != OPAL_SUCCESS) 7258c2ecf20Sopenharmony_ci return 0; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (be64_to_cpu(out_flags) & OPAL_HMI_FLAGS_NEW_EVENT) 7288c2ecf20Sopenharmony_ci local_paca->hmi_event_available = 1; 7298c2ecf20Sopenharmony_ci if (be64_to_cpu(out_flags) & OPAL_HMI_FLAGS_TOD_TB_FAIL) 7308c2ecf20Sopenharmony_ci tb_invalid = true; 7318c2ecf20Sopenharmony_ci return 1; 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci/* HMI exception handler called in virtual mode when irqs are next enabled. */ 7358c2ecf20Sopenharmony_ciint opal_handle_hmi_exception(struct pt_regs *regs) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci /* 7388c2ecf20Sopenharmony_ci * Check if HMI event is available. 7398c2ecf20Sopenharmony_ci * if Yes, then wake kopald to process them. 7408c2ecf20Sopenharmony_ci */ 7418c2ecf20Sopenharmony_ci if (!local_paca->hmi_event_available) 7428c2ecf20Sopenharmony_ci return 0; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci local_paca->hmi_event_available = 0; 7458c2ecf20Sopenharmony_ci opal_wake_poller(); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci return 1; 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic uint64_t find_recovery_address(uint64_t nip) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci int i; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci for (i = 0; i < mc_recoverable_range_len; i++) 7558c2ecf20Sopenharmony_ci if ((nip >= mc_recoverable_range[i].start_addr) && 7568c2ecf20Sopenharmony_ci (nip < mc_recoverable_range[i].end_addr)) 7578c2ecf20Sopenharmony_ci return mc_recoverable_range[i].recover_addr; 7588c2ecf20Sopenharmony_ci return 0; 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cibool opal_mce_check_early_recovery(struct pt_regs *regs) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci uint64_t recover_addr = 0; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci if (!opal.base || !opal.size) 7668c2ecf20Sopenharmony_ci goto out; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if ((regs->nip >= opal.base) && 7698c2ecf20Sopenharmony_ci (regs->nip < (opal.base + opal.size))) 7708c2ecf20Sopenharmony_ci recover_addr = find_recovery_address(regs->nip); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* 7738c2ecf20Sopenharmony_ci * Setup regs->nip to rfi into fixup address. 7748c2ecf20Sopenharmony_ci */ 7758c2ecf20Sopenharmony_ci if (recover_addr) 7768c2ecf20Sopenharmony_ci regs->nip = recover_addr; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ciout: 7798c2ecf20Sopenharmony_ci return !!recover_addr; 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistatic int opal_sysfs_init(void) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci opal_kobj = kobject_create_and_add("opal", firmware_kobj); 7858c2ecf20Sopenharmony_ci if (!opal_kobj) { 7868c2ecf20Sopenharmony_ci pr_warn("kobject_create_and_add opal failed\n"); 7878c2ecf20Sopenharmony_ci return -ENOMEM; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci return 0; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_cistatic ssize_t export_attr_read(struct file *fp, struct kobject *kobj, 7948c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 7958c2ecf20Sopenharmony_ci loff_t off, size_t count) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci return memory_read_from_buffer(buf, count, &off, bin_attr->private, 7988c2ecf20Sopenharmony_ci bin_attr->size); 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic int opal_add_one_export(struct kobject *parent, const char *export_name, 8028c2ecf20Sopenharmony_ci struct device_node *np, const char *prop_name) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci struct bin_attribute *attr = NULL; 8058c2ecf20Sopenharmony_ci const char *name = NULL; 8068c2ecf20Sopenharmony_ci u64 vals[2]; 8078c2ecf20Sopenharmony_ci int rc; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci rc = of_property_read_u64_array(np, prop_name, &vals[0], 2); 8108c2ecf20Sopenharmony_ci if (rc) 8118c2ecf20Sopenharmony_ci goto out; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci attr = kzalloc(sizeof(*attr), GFP_KERNEL); 8148c2ecf20Sopenharmony_ci if (!attr) { 8158c2ecf20Sopenharmony_ci rc = -ENOMEM; 8168c2ecf20Sopenharmony_ci goto out; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci name = kstrdup(export_name, GFP_KERNEL); 8198c2ecf20Sopenharmony_ci if (!name) { 8208c2ecf20Sopenharmony_ci rc = -ENOMEM; 8218c2ecf20Sopenharmony_ci goto out; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci sysfs_bin_attr_init(attr); 8258c2ecf20Sopenharmony_ci attr->attr.name = name; 8268c2ecf20Sopenharmony_ci attr->attr.mode = 0400; 8278c2ecf20Sopenharmony_ci attr->read = export_attr_read; 8288c2ecf20Sopenharmony_ci attr->private = __va(vals[0]); 8298c2ecf20Sopenharmony_ci attr->size = vals[1]; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci rc = sysfs_create_bin_file(parent, attr); 8328c2ecf20Sopenharmony_ciout: 8338c2ecf20Sopenharmony_ci if (rc) { 8348c2ecf20Sopenharmony_ci kfree(name); 8358c2ecf20Sopenharmony_ci kfree(attr); 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci return rc; 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_cistatic void opal_add_exported_attrs(struct device_node *np, 8428c2ecf20Sopenharmony_ci struct kobject *kobj) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci struct device_node *child; 8458c2ecf20Sopenharmony_ci struct property *prop; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci for_each_property_of_node(np, prop) { 8488c2ecf20Sopenharmony_ci int rc; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (!strcmp(prop->name, "name") || 8518c2ecf20Sopenharmony_ci !strcmp(prop->name, "phandle")) 8528c2ecf20Sopenharmony_ci continue; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci rc = opal_add_one_export(kobj, prop->name, np, prop->name); 8558c2ecf20Sopenharmony_ci if (rc) { 8568c2ecf20Sopenharmony_ci pr_warn("Unable to add export %pOF/%s, rc = %d!\n", 8578c2ecf20Sopenharmony_ci np, prop->name, rc); 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci for_each_child_of_node(np, child) { 8628c2ecf20Sopenharmony_ci struct kobject *child_kobj; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci child_kobj = kobject_create_and_add(child->name, kobj); 8658c2ecf20Sopenharmony_ci if (!child_kobj) { 8668c2ecf20Sopenharmony_ci pr_err("Unable to create export dir for %pOF\n", child); 8678c2ecf20Sopenharmony_ci continue; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci opal_add_exported_attrs(child, child_kobj); 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci} 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci/* 8758c2ecf20Sopenharmony_ci * opal_export_attrs: creates a sysfs node for each property listed in 8768c2ecf20Sopenharmony_ci * the device-tree under /ibm,opal/firmware/exports/ 8778c2ecf20Sopenharmony_ci * All new sysfs nodes are created under /opal/exports/. 8788c2ecf20Sopenharmony_ci * This allows for reserved memory regions (e.g. HDAT) to be read. 8798c2ecf20Sopenharmony_ci * The new sysfs nodes are only readable by root. 8808c2ecf20Sopenharmony_ci */ 8818c2ecf20Sopenharmony_cistatic void opal_export_attrs(void) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci struct device_node *np; 8848c2ecf20Sopenharmony_ci struct kobject *kobj; 8858c2ecf20Sopenharmony_ci int rc; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci np = of_find_node_by_path("/ibm,opal/firmware/exports"); 8888c2ecf20Sopenharmony_ci if (!np) 8898c2ecf20Sopenharmony_ci return; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci /* Create new 'exports' directory - /sys/firmware/opal/exports */ 8928c2ecf20Sopenharmony_ci kobj = kobject_create_and_add("exports", opal_kobj); 8938c2ecf20Sopenharmony_ci if (!kobj) { 8948c2ecf20Sopenharmony_ci pr_warn("kobject_create_and_add() of exports failed\n"); 8958c2ecf20Sopenharmony_ci of_node_put(np); 8968c2ecf20Sopenharmony_ci return; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci opal_add_exported_attrs(np, kobj); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* 9028c2ecf20Sopenharmony_ci * NB: symbol_map existed before the generic export interface so it 9038c2ecf20Sopenharmony_ci * lives under the top level opal_kobj. 9048c2ecf20Sopenharmony_ci */ 9058c2ecf20Sopenharmony_ci rc = opal_add_one_export(opal_kobj, "symbol_map", 9068c2ecf20Sopenharmony_ci np->parent, "symbol-map"); 9078c2ecf20Sopenharmony_ci if (rc) 9088c2ecf20Sopenharmony_ci pr_warn("Error %d creating OPAL symbols file\n", rc); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci of_node_put(np); 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic void __init opal_dump_region_init(void) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci void *addr; 9168c2ecf20Sopenharmony_ci uint64_t size; 9178c2ecf20Sopenharmony_ci int rc; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci if (!opal_check_token(OPAL_REGISTER_DUMP_REGION)) 9208c2ecf20Sopenharmony_ci return; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci /* Register kernel log buffer */ 9238c2ecf20Sopenharmony_ci addr = log_buf_addr_get(); 9248c2ecf20Sopenharmony_ci if (addr == NULL) 9258c2ecf20Sopenharmony_ci return; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci size = log_buf_len_get(); 9288c2ecf20Sopenharmony_ci if (size == 0) 9298c2ecf20Sopenharmony_ci return; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci rc = opal_register_dump_region(OPAL_DUMP_REGION_LOG_BUF, 9328c2ecf20Sopenharmony_ci __pa(addr), size); 9338c2ecf20Sopenharmony_ci /* Don't warn if this is just an older OPAL that doesn't 9348c2ecf20Sopenharmony_ci * know about that call 9358c2ecf20Sopenharmony_ci */ 9368c2ecf20Sopenharmony_ci if (rc && rc != OPAL_UNSUPPORTED) 9378c2ecf20Sopenharmony_ci pr_warn("DUMP: Failed to register kernel log buffer. " 9388c2ecf20Sopenharmony_ci "rc = %d\n", rc); 9398c2ecf20Sopenharmony_ci} 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_cistatic void opal_pdev_init(const char *compatible) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci struct device_node *np; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci for_each_compatible_node(np, NULL, compatible) 9468c2ecf20Sopenharmony_ci of_platform_device_create(np, NULL, NULL); 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic void __init opal_imc_init_dev(void) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci struct device_node *np; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, IMC_DTB_COMPAT); 9548c2ecf20Sopenharmony_ci if (np) 9558c2ecf20Sopenharmony_ci of_platform_device_create(np, NULL, NULL); 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_cistatic int kopald(void *unused) 9598c2ecf20Sopenharmony_ci{ 9608c2ecf20Sopenharmony_ci unsigned long timeout = msecs_to_jiffies(opal_heartbeat) + 1; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci set_freezable(); 9638c2ecf20Sopenharmony_ci do { 9648c2ecf20Sopenharmony_ci try_to_freeze(); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci opal_handle_events(); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 9698c2ecf20Sopenharmony_ci if (opal_have_pending_events()) 9708c2ecf20Sopenharmony_ci __set_current_state(TASK_RUNNING); 9718c2ecf20Sopenharmony_ci else 9728c2ecf20Sopenharmony_ci schedule_timeout(timeout); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci } while (!kthread_should_stop()); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci return 0; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_civoid opal_wake_poller(void) 9808c2ecf20Sopenharmony_ci{ 9818c2ecf20Sopenharmony_ci if (kopald_tsk) 9828c2ecf20Sopenharmony_ci wake_up_process(kopald_tsk); 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic void opal_init_heartbeat(void) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci /* Old firwmware, we assume the HVC heartbeat is sufficient */ 9888c2ecf20Sopenharmony_ci if (of_property_read_u32(opal_node, "ibm,heartbeat-ms", 9898c2ecf20Sopenharmony_ci &opal_heartbeat) != 0) 9908c2ecf20Sopenharmony_ci opal_heartbeat = 0; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci if (opal_heartbeat) 9938c2ecf20Sopenharmony_ci kopald_tsk = kthread_run(kopald, NULL, "kopald"); 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic int __init opal_init(void) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci struct device_node *np, *consoles, *leds; 9998c2ecf20Sopenharmony_ci int rc; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci opal_node = of_find_node_by_path("/ibm,opal"); 10028c2ecf20Sopenharmony_ci if (!opal_node) { 10038c2ecf20Sopenharmony_ci pr_warn("Device node not found\n"); 10048c2ecf20Sopenharmony_ci return -ENODEV; 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci /* Register OPAL consoles if any ports */ 10088c2ecf20Sopenharmony_ci consoles = of_find_node_by_path("/ibm,opal/consoles"); 10098c2ecf20Sopenharmony_ci if (consoles) { 10108c2ecf20Sopenharmony_ci for_each_child_of_node(consoles, np) { 10118c2ecf20Sopenharmony_ci if (!of_node_name_eq(np, "serial")) 10128c2ecf20Sopenharmony_ci continue; 10138c2ecf20Sopenharmony_ci of_platform_device_create(np, NULL, NULL); 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci of_node_put(consoles); 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci /* Initialise OPAL messaging system */ 10198c2ecf20Sopenharmony_ci opal_message_init(opal_node); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci /* Initialise OPAL asynchronous completion interface */ 10228c2ecf20Sopenharmony_ci opal_async_comp_init(); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci /* Initialise OPAL sensor interface */ 10258c2ecf20Sopenharmony_ci opal_sensor_init(); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci /* Initialise OPAL hypervisor maintainence interrupt handling */ 10288c2ecf20Sopenharmony_ci opal_hmi_handler_init(); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* Create i2c platform devices */ 10318c2ecf20Sopenharmony_ci opal_pdev_init("ibm,opal-i2c"); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci /* Handle non-volatile memory devices */ 10348c2ecf20Sopenharmony_ci opal_pdev_init("pmem-region"); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci /* Setup a heatbeat thread if requested by OPAL */ 10378c2ecf20Sopenharmony_ci opal_init_heartbeat(); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci /* Detect In-Memory Collection counters and create devices*/ 10408c2ecf20Sopenharmony_ci opal_imc_init_dev(); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci /* Create leds platform devices */ 10438c2ecf20Sopenharmony_ci leds = of_find_node_by_path("/ibm,opal/leds"); 10448c2ecf20Sopenharmony_ci if (leds) { 10458c2ecf20Sopenharmony_ci of_platform_device_create(leds, "opal_leds", NULL); 10468c2ecf20Sopenharmony_ci of_node_put(leds); 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci /* Initialise OPAL message log interface */ 10508c2ecf20Sopenharmony_ci opal_msglog_init(); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci /* Create "opal" kobject under /sys/firmware */ 10538c2ecf20Sopenharmony_ci rc = opal_sysfs_init(); 10548c2ecf20Sopenharmony_ci if (rc == 0) { 10558c2ecf20Sopenharmony_ci /* Setup dump region interface */ 10568c2ecf20Sopenharmony_ci opal_dump_region_init(); 10578c2ecf20Sopenharmony_ci /* Setup error log interface */ 10588c2ecf20Sopenharmony_ci rc = opal_elog_init(); 10598c2ecf20Sopenharmony_ci /* Setup code update interface */ 10608c2ecf20Sopenharmony_ci opal_flash_update_init(); 10618c2ecf20Sopenharmony_ci /* Setup platform dump extract interface */ 10628c2ecf20Sopenharmony_ci opal_platform_dump_init(); 10638c2ecf20Sopenharmony_ci /* Setup system parameters interface */ 10648c2ecf20Sopenharmony_ci opal_sys_param_init(); 10658c2ecf20Sopenharmony_ci /* Setup message log sysfs interface. */ 10668c2ecf20Sopenharmony_ci opal_msglog_sysfs_init(); 10678c2ecf20Sopenharmony_ci /* Add all export properties*/ 10688c2ecf20Sopenharmony_ci opal_export_attrs(); 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci /* Initialize platform devices: IPMI backend, PRD & flash interface */ 10728c2ecf20Sopenharmony_ci opal_pdev_init("ibm,opal-ipmi"); 10738c2ecf20Sopenharmony_ci opal_pdev_init("ibm,opal-flash"); 10748c2ecf20Sopenharmony_ci opal_pdev_init("ibm,opal-prd"); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci /* Initialise platform device: oppanel interface */ 10778c2ecf20Sopenharmony_ci opal_pdev_init("ibm,opal-oppanel"); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci /* Initialise OPAL kmsg dumper for flushing console on panic */ 10808c2ecf20Sopenharmony_ci opal_kmsg_init(); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci /* Initialise OPAL powercap interface */ 10838c2ecf20Sopenharmony_ci opal_powercap_init(); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci /* Initialise OPAL Power-Shifting-Ratio interface */ 10868c2ecf20Sopenharmony_ci opal_psr_init(); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* Initialise OPAL sensor groups */ 10898c2ecf20Sopenharmony_ci opal_sensor_groups_init(); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci /* Initialise OPAL Power control interface */ 10928c2ecf20Sopenharmony_ci opal_power_control_init(); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci /* Initialize OPAL secure variables */ 10958c2ecf20Sopenharmony_ci opal_pdev_init("ibm,secvar-backend"); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci return 0; 10988c2ecf20Sopenharmony_ci} 10998c2ecf20Sopenharmony_cimachine_subsys_initcall(powernv, opal_init); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_civoid opal_shutdown(void) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci long rc = OPAL_BUSY; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci opal_event_shutdown(); 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci /* 11088c2ecf20Sopenharmony_ci * Then sync with OPAL which ensure anything that can 11098c2ecf20Sopenharmony_ci * potentially write to our memory has completed such 11108c2ecf20Sopenharmony_ci * as an ongoing dump retrieval 11118c2ecf20Sopenharmony_ci */ 11128c2ecf20Sopenharmony_ci while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { 11138c2ecf20Sopenharmony_ci rc = opal_sync_host_reboot(); 11148c2ecf20Sopenharmony_ci if (rc == OPAL_BUSY) 11158c2ecf20Sopenharmony_ci opal_poll_events(NULL); 11168c2ecf20Sopenharmony_ci else 11178c2ecf20Sopenharmony_ci mdelay(10); 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci /* Unregister memory dump region */ 11218c2ecf20Sopenharmony_ci if (opal_check_token(OPAL_UNREGISTER_DUMP_REGION)) 11228c2ecf20Sopenharmony_ci opal_unregister_dump_region(OPAL_DUMP_REGION_LOG_BUF); 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci/* Export this so that test modules can use it */ 11268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_invalid_call); 11278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_xscom_read); 11288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_xscom_write); 11298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_ipmi_send); 11308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_ipmi_recv); 11318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_flash_read); 11328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_flash_write); 11338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_flash_erase); 11348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_prd_msg); 11358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_check_token); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci/* Convert a region of vmalloc memory to an opal sg list */ 11388c2ecf20Sopenharmony_cistruct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr, 11398c2ecf20Sopenharmony_ci unsigned long vmalloc_size) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci struct opal_sg_list *sg, *first = NULL; 11428c2ecf20Sopenharmony_ci unsigned long i = 0; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci sg = kzalloc(PAGE_SIZE, GFP_KERNEL); 11458c2ecf20Sopenharmony_ci if (!sg) 11468c2ecf20Sopenharmony_ci goto nomem; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci first = sg; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci while (vmalloc_size > 0) { 11518c2ecf20Sopenharmony_ci uint64_t data = vmalloc_to_pfn(vmalloc_addr) << PAGE_SHIFT; 11528c2ecf20Sopenharmony_ci uint64_t length = min(vmalloc_size, PAGE_SIZE); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci sg->entry[i].data = cpu_to_be64(data); 11558c2ecf20Sopenharmony_ci sg->entry[i].length = cpu_to_be64(length); 11568c2ecf20Sopenharmony_ci i++; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci if (i >= SG_ENTRIES_PER_NODE) { 11598c2ecf20Sopenharmony_ci struct opal_sg_list *next; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci next = kzalloc(PAGE_SIZE, GFP_KERNEL); 11628c2ecf20Sopenharmony_ci if (!next) 11638c2ecf20Sopenharmony_ci goto nomem; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci sg->length = cpu_to_be64( 11668c2ecf20Sopenharmony_ci i * sizeof(struct opal_sg_entry) + 16); 11678c2ecf20Sopenharmony_ci i = 0; 11688c2ecf20Sopenharmony_ci sg->next = cpu_to_be64(__pa(next)); 11698c2ecf20Sopenharmony_ci sg = next; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci vmalloc_addr += length; 11738c2ecf20Sopenharmony_ci vmalloc_size -= length; 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci sg->length = cpu_to_be64(i * sizeof(struct opal_sg_entry) + 16); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci return first; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_cinomem: 11818c2ecf20Sopenharmony_ci pr_err("%s : Failed to allocate memory\n", __func__); 11828c2ecf20Sopenharmony_ci opal_free_sg_list(first); 11838c2ecf20Sopenharmony_ci return NULL; 11848c2ecf20Sopenharmony_ci} 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_civoid opal_free_sg_list(struct opal_sg_list *sg) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci while (sg) { 11898c2ecf20Sopenharmony_ci uint64_t next = be64_to_cpu(sg->next); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci kfree(sg); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci if (next) 11948c2ecf20Sopenharmony_ci sg = __va(next); 11958c2ecf20Sopenharmony_ci else 11968c2ecf20Sopenharmony_ci sg = NULL; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci} 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ciint opal_error_code(int rc) 12018c2ecf20Sopenharmony_ci{ 12028c2ecf20Sopenharmony_ci switch (rc) { 12038c2ecf20Sopenharmony_ci case OPAL_SUCCESS: return 0; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci case OPAL_PARAMETER: return -EINVAL; 12068c2ecf20Sopenharmony_ci case OPAL_ASYNC_COMPLETION: return -EINPROGRESS; 12078c2ecf20Sopenharmony_ci case OPAL_BUSY: 12088c2ecf20Sopenharmony_ci case OPAL_BUSY_EVENT: return -EBUSY; 12098c2ecf20Sopenharmony_ci case OPAL_NO_MEM: return -ENOMEM; 12108c2ecf20Sopenharmony_ci case OPAL_PERMISSION: return -EPERM; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci case OPAL_UNSUPPORTED: return -EIO; 12138c2ecf20Sopenharmony_ci case OPAL_HARDWARE: return -EIO; 12148c2ecf20Sopenharmony_ci case OPAL_INTERNAL_ERROR: return -EIO; 12158c2ecf20Sopenharmony_ci case OPAL_TIMEOUT: return -ETIMEDOUT; 12168c2ecf20Sopenharmony_ci default: 12178c2ecf20Sopenharmony_ci pr_err("%s: unexpected OPAL error %d\n", __func__, rc); 12188c2ecf20Sopenharmony_ci return -EIO; 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci} 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_civoid powernv_set_nmmu_ptcr(unsigned long ptcr) 12238c2ecf20Sopenharmony_ci{ 12248c2ecf20Sopenharmony_ci int rc; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci if (firmware_has_feature(FW_FEATURE_OPAL)) { 12278c2ecf20Sopenharmony_ci rc = opal_nmmu_set_ptcr(-1UL, ptcr); 12288c2ecf20Sopenharmony_ci if (rc != OPAL_SUCCESS && rc != OPAL_UNSUPPORTED) 12298c2ecf20Sopenharmony_ci pr_warn("%s: Unable to set nest mmu ptcr\n", __func__); 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci} 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_poll_events); 12348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_rtc_read); 12358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_rtc_write); 12368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_tpo_read); 12378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_tpo_write); 12388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_i2c_request); 12398c2ecf20Sopenharmony_ci/* Export these symbols for PowerNV LED class driver */ 12408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_leds_get_ind); 12418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_leds_set_ind); 12428c2ecf20Sopenharmony_ci/* Export this symbol for PowerNV Operator Panel class driver */ 12438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_write_oppanel_async); 12448c2ecf20Sopenharmony_ci/* Export this for KVM */ 12458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_int_set_mfrr); 12468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_int_eoi); 12478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_error_code); 12488c2ecf20Sopenharmony_ci/* Export the below symbol for NX compression */ 12498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(opal_nx_coproc_init); 1250