18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SGI NMI support routines 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) Copyright 2020 Hewlett Packard Enterprise Development LP 68c2ecf20Sopenharmony_ci * Copyright (C) 2007-2017 Silicon Graphics, Inc. All rights reserved. 78c2ecf20Sopenharmony_ci * Copyright (c) Mike Travis 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/cpu.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/kdb.h> 138c2ecf20Sopenharmony_ci#include <linux/kexec.h> 148c2ecf20Sopenharmony_ci#include <linux/kgdb.h> 158c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 168c2ecf20Sopenharmony_ci#include <linux/nmi.h> 178c2ecf20Sopenharmony_ci#include <linux/sched.h> 188c2ecf20Sopenharmony_ci#include <linux/sched/debug.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <asm/apic.h> 238c2ecf20Sopenharmony_ci#include <asm/current.h> 248c2ecf20Sopenharmony_ci#include <asm/kdebug.h> 258c2ecf20Sopenharmony_ci#include <asm/local64.h> 268c2ecf20Sopenharmony_ci#include <asm/nmi.h> 278c2ecf20Sopenharmony_ci#include <asm/traps.h> 288c2ecf20Sopenharmony_ci#include <asm/uv/uv.h> 298c2ecf20Sopenharmony_ci#include <asm/uv/uv_hub.h> 308c2ecf20Sopenharmony_ci#include <asm/uv/uv_mmrs.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * UV handler for NMI 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Handle system-wide NMI events generated by the global 'power nmi' command. 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * Basic operation is to field the NMI interrupt on each CPU and wait 388c2ecf20Sopenharmony_ci * until all CPU's have arrived into the nmi handler. If some CPU's do not 398c2ecf20Sopenharmony_ci * make it into the handler, try and force them in with the IPI(NMI) signal. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * We also have to lessen UV Hub MMR accesses as much as possible as this 428c2ecf20Sopenharmony_ci * disrupts the UV Hub's primary mission of directing NumaLink traffic and 438c2ecf20Sopenharmony_ci * can cause system problems to occur. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * To do this we register our primary NMI notifier on the NMI_UNKNOWN 468c2ecf20Sopenharmony_ci * chain. This reduces the number of false NMI calls when the perf 478c2ecf20Sopenharmony_ci * tools are running which generate an enormous number of NMIs per 488c2ecf20Sopenharmony_ci * second (~4M/s for 1024 CPU threads). Our secondary NMI handler is 498c2ecf20Sopenharmony_ci * very short as it only checks that if it has been "pinged" with the 508c2ecf20Sopenharmony_ci * IPI(NMI) signal as mentioned above, and does not read the UV Hub's MMR. 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic struct uv_hub_nmi_s **uv_hub_nmi_list; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ciDEFINE_PER_CPU(struct uv_cpu_nmi_s, uv_cpu_nmi); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* Newer SMM NMI handler, not present in all systems */ 598c2ecf20Sopenharmony_cistatic unsigned long uvh_nmi_mmrx; /* UVH_EVENT_OCCURRED0/1 */ 608c2ecf20Sopenharmony_cistatic unsigned long uvh_nmi_mmrx_clear; /* UVH_EVENT_OCCURRED0/1_ALIAS */ 618c2ecf20Sopenharmony_cistatic int uvh_nmi_mmrx_shift; /* UVH_EVENT_OCCURRED0/1_EXTIO_INT0_SHFT */ 628c2ecf20Sopenharmony_cistatic char *uvh_nmi_mmrx_type; /* "EXTIO_INT0" */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* Non-zero indicates newer SMM NMI handler present */ 658c2ecf20Sopenharmony_cistatic unsigned long uvh_nmi_mmrx_supported; /* UVH_EXTIO_INT0_BROADCAST */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Indicates to BIOS that we want to use the newer SMM NMI handler */ 688c2ecf20Sopenharmony_cistatic unsigned long uvh_nmi_mmrx_req; /* UVH_BIOS_KERNEL_MMR_ALIAS_2 */ 698c2ecf20Sopenharmony_cistatic int uvh_nmi_mmrx_req_shift; /* 62 */ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* UV hubless values */ 728c2ecf20Sopenharmony_ci#define NMI_CONTROL_PORT 0x70 738c2ecf20Sopenharmony_ci#define NMI_DUMMY_PORT 0x71 748c2ecf20Sopenharmony_ci#define PAD_OWN_GPP_D_0 0x2c 758c2ecf20Sopenharmony_ci#define GPI_NMI_STS_GPP_D_0 0x164 768c2ecf20Sopenharmony_ci#define GPI_NMI_ENA_GPP_D_0 0x174 778c2ecf20Sopenharmony_ci#define STS_GPP_D_0_MASK 0x1 788c2ecf20Sopenharmony_ci#define PAD_CFG_DW0_GPP_D_0 0x4c0 798c2ecf20Sopenharmony_ci#define GPIROUTNMI (1ul << 17) 808c2ecf20Sopenharmony_ci#define PCH_PCR_GPIO_1_BASE 0xfdae0000ul 818c2ecf20Sopenharmony_ci#define PCH_PCR_GPIO_ADDRESS(offset) (int *)((u64)(pch_base) | (u64)(offset)) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic u64 *pch_base; 848c2ecf20Sopenharmony_cistatic unsigned long nmi_mmr; 858c2ecf20Sopenharmony_cistatic unsigned long nmi_mmr_clear; 868c2ecf20Sopenharmony_cistatic unsigned long nmi_mmr_pending; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic atomic_t uv_in_nmi; 898c2ecf20Sopenharmony_cistatic atomic_t uv_nmi_cpu = ATOMIC_INIT(-1); 908c2ecf20Sopenharmony_cistatic atomic_t uv_nmi_cpus_in_nmi = ATOMIC_INIT(-1); 918c2ecf20Sopenharmony_cistatic atomic_t uv_nmi_slave_continue; 928c2ecf20Sopenharmony_cistatic cpumask_var_t uv_nmi_cpu_mask; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* Values for uv_nmi_slave_continue */ 958c2ecf20Sopenharmony_ci#define SLAVE_CLEAR 0 968c2ecf20Sopenharmony_ci#define SLAVE_CONTINUE 1 978c2ecf20Sopenharmony_ci#define SLAVE_EXIT 2 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * Default is all stack dumps go to the console and buffer. 1018c2ecf20Sopenharmony_ci * Lower level to send to log buffer only. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_cistatic int uv_nmi_loglevel = CONSOLE_LOGLEVEL_DEFAULT; 1048c2ecf20Sopenharmony_cimodule_param_named(dump_loglevel, uv_nmi_loglevel, int, 0644); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* 1078c2ecf20Sopenharmony_ci * The following values show statistics on how perf events are affecting 1088c2ecf20Sopenharmony_ci * this system. 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_cistatic int param_get_local64(char *buffer, const struct kernel_param *kp) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci return sprintf(buffer, "%lu\n", local64_read((local64_t *)kp->arg)); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int param_set_local64(const char *val, const struct kernel_param *kp) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci /* Clear on any write */ 1188c2ecf20Sopenharmony_ci local64_set((local64_t *)kp->arg, 0); 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic const struct kernel_param_ops param_ops_local64 = { 1238c2ecf20Sopenharmony_ci .get = param_get_local64, 1248c2ecf20Sopenharmony_ci .set = param_set_local64, 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci#define param_check_local64(name, p) __param_check(name, p, local64_t) 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic local64_t uv_nmi_count; 1298c2ecf20Sopenharmony_cimodule_param_named(nmi_count, uv_nmi_count, local64, 0644); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic local64_t uv_nmi_misses; 1328c2ecf20Sopenharmony_cimodule_param_named(nmi_misses, uv_nmi_misses, local64, 0644); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic local64_t uv_nmi_ping_count; 1358c2ecf20Sopenharmony_cimodule_param_named(ping_count, uv_nmi_ping_count, local64, 0644); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic local64_t uv_nmi_ping_misses; 1388c2ecf20Sopenharmony_cimodule_param_named(ping_misses, uv_nmi_ping_misses, local64, 0644); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* 1418c2ecf20Sopenharmony_ci * Following values allow tuning for large systems under heavy loading 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_cistatic int uv_nmi_initial_delay = 100; 1448c2ecf20Sopenharmony_cimodule_param_named(initial_delay, uv_nmi_initial_delay, int, 0644); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int uv_nmi_slave_delay = 100; 1478c2ecf20Sopenharmony_cimodule_param_named(slave_delay, uv_nmi_slave_delay, int, 0644); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int uv_nmi_loop_delay = 100; 1508c2ecf20Sopenharmony_cimodule_param_named(loop_delay, uv_nmi_loop_delay, int, 0644); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int uv_nmi_trigger_delay = 10000; 1538c2ecf20Sopenharmony_cimodule_param_named(trigger_delay, uv_nmi_trigger_delay, int, 0644); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int uv_nmi_wait_count = 100; 1568c2ecf20Sopenharmony_cimodule_param_named(wait_count, uv_nmi_wait_count, int, 0644); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int uv_nmi_retry_count = 500; 1598c2ecf20Sopenharmony_cimodule_param_named(retry_count, uv_nmi_retry_count, int, 0644); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic bool uv_pch_intr_enable = true; 1628c2ecf20Sopenharmony_cistatic bool uv_pch_intr_now_enabled; 1638c2ecf20Sopenharmony_cimodule_param_named(pch_intr_enable, uv_pch_intr_enable, bool, 0644); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic bool uv_pch_init_enable = true; 1668c2ecf20Sopenharmony_cimodule_param_named(pch_init_enable, uv_pch_init_enable, bool, 0644); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int uv_nmi_debug; 1698c2ecf20Sopenharmony_cimodule_param_named(debug, uv_nmi_debug, int, 0644); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci#define nmi_debug(fmt, ...) \ 1728c2ecf20Sopenharmony_ci do { \ 1738c2ecf20Sopenharmony_ci if (uv_nmi_debug) \ 1748c2ecf20Sopenharmony_ci pr_info(fmt, ##__VA_ARGS__); \ 1758c2ecf20Sopenharmony_ci } while (0) 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* Valid NMI Actions */ 1788c2ecf20Sopenharmony_ci#define ACTION_LEN 16 1798c2ecf20Sopenharmony_cistatic struct nmi_action { 1808c2ecf20Sopenharmony_ci char *action; 1818c2ecf20Sopenharmony_ci char *desc; 1828c2ecf20Sopenharmony_ci} valid_acts[] = { 1838c2ecf20Sopenharmony_ci { "kdump", "do kernel crash dump" }, 1848c2ecf20Sopenharmony_ci { "dump", "dump process stack for each cpu" }, 1858c2ecf20Sopenharmony_ci { "ips", "dump Inst Ptr info for each cpu" }, 1868c2ecf20Sopenharmony_ci { "kdb", "enter KDB (needs kgdboc= assignment)" }, 1878c2ecf20Sopenharmony_ci { "kgdb", "enter KGDB (needs gdb target remote)" }, 1888c2ecf20Sopenharmony_ci { "health", "check if CPUs respond to NMI" }, 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_citypedef char action_t[ACTION_LEN]; 1918c2ecf20Sopenharmony_cistatic action_t uv_nmi_action = { "dump" }; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int param_get_action(char *buffer, const struct kernel_param *kp) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci return sprintf(buffer, "%s\n", uv_nmi_action); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int param_set_action(const char *val, const struct kernel_param *kp) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci int i; 2018c2ecf20Sopenharmony_ci int n = ARRAY_SIZE(valid_acts); 2028c2ecf20Sopenharmony_ci char arg[ACTION_LEN], *p; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* (remove possible '\n') */ 2058c2ecf20Sopenharmony_ci strncpy(arg, val, ACTION_LEN - 1); 2068c2ecf20Sopenharmony_ci arg[ACTION_LEN - 1] = '\0'; 2078c2ecf20Sopenharmony_ci p = strchr(arg, '\n'); 2088c2ecf20Sopenharmony_ci if (p) 2098c2ecf20Sopenharmony_ci *p = '\0'; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 2128c2ecf20Sopenharmony_ci if (!strcmp(arg, valid_acts[i].action)) 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (i < n) { 2168c2ecf20Sopenharmony_ci strcpy(uv_nmi_action, arg); 2178c2ecf20Sopenharmony_ci pr_info("UV: New NMI action:%s\n", uv_nmi_action); 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci pr_err("UV: Invalid NMI action:%s, valid actions are:\n", arg); 2228c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 2238c2ecf20Sopenharmony_ci pr_err("UV: %-8s - %s\n", 2248c2ecf20Sopenharmony_ci valid_acts[i].action, valid_acts[i].desc); 2258c2ecf20Sopenharmony_ci return -EINVAL; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic const struct kernel_param_ops param_ops_action = { 2298c2ecf20Sopenharmony_ci .get = param_get_action, 2308c2ecf20Sopenharmony_ci .set = param_set_action, 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci#define param_check_action(name, p) __param_check(name, p, action_t) 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cimodule_param_named(action, uv_nmi_action, action, 0644); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic inline bool uv_nmi_action_is(const char *action) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci return (strncmp(uv_nmi_action, action, strlen(action)) == 0); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/* Setup which NMI support is present in system */ 2428c2ecf20Sopenharmony_cistatic void uv_nmi_setup_mmrs(void) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci /* First determine arch specific MMRs to handshake with BIOS */ 2458c2ecf20Sopenharmony_ci if (UVH_EVENT_OCCURRED0_EXTIO_INT0_MASK) { 2468c2ecf20Sopenharmony_ci uvh_nmi_mmrx = UVH_EVENT_OCCURRED0; 2478c2ecf20Sopenharmony_ci uvh_nmi_mmrx_clear = UVH_EVENT_OCCURRED0_ALIAS; 2488c2ecf20Sopenharmony_ci uvh_nmi_mmrx_shift = UVH_EVENT_OCCURRED0_EXTIO_INT0_SHFT; 2498c2ecf20Sopenharmony_ci uvh_nmi_mmrx_type = "OCRD0-EXTIO_INT0"; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci uvh_nmi_mmrx_supported = UVH_EXTIO_INT0_BROADCAST; 2528c2ecf20Sopenharmony_ci uvh_nmi_mmrx_req = UVH_BIOS_KERNEL_MMR_ALIAS_2; 2538c2ecf20Sopenharmony_ci uvh_nmi_mmrx_req_shift = 62; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci } else if (UVH_EVENT_OCCURRED1_EXTIO_INT0_MASK) { 2568c2ecf20Sopenharmony_ci uvh_nmi_mmrx = UVH_EVENT_OCCURRED1; 2578c2ecf20Sopenharmony_ci uvh_nmi_mmrx_clear = UVH_EVENT_OCCURRED1_ALIAS; 2588c2ecf20Sopenharmony_ci uvh_nmi_mmrx_shift = UVH_EVENT_OCCURRED1_EXTIO_INT0_SHFT; 2598c2ecf20Sopenharmony_ci uvh_nmi_mmrx_type = "OCRD1-EXTIO_INT0"; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci uvh_nmi_mmrx_supported = UVH_EXTIO_INT0_BROADCAST; 2628c2ecf20Sopenharmony_ci uvh_nmi_mmrx_req = UVH_BIOS_KERNEL_MMR_ALIAS_2; 2638c2ecf20Sopenharmony_ci uvh_nmi_mmrx_req_shift = 62; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci } else { 2668c2ecf20Sopenharmony_ci pr_err("UV:%s:cannot find EVENT_OCCURRED*_EXTIO_INT0\n", 2678c2ecf20Sopenharmony_ci __func__); 2688c2ecf20Sopenharmony_ci return; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* Then find out if new NMI is supported */ 2728c2ecf20Sopenharmony_ci if (likely(uv_read_local_mmr(uvh_nmi_mmrx_supported))) { 2738c2ecf20Sopenharmony_ci uv_write_local_mmr(uvh_nmi_mmrx_req, 2748c2ecf20Sopenharmony_ci 1UL << uvh_nmi_mmrx_req_shift); 2758c2ecf20Sopenharmony_ci nmi_mmr = uvh_nmi_mmrx; 2768c2ecf20Sopenharmony_ci nmi_mmr_clear = uvh_nmi_mmrx_clear; 2778c2ecf20Sopenharmony_ci nmi_mmr_pending = 1UL << uvh_nmi_mmrx_shift; 2788c2ecf20Sopenharmony_ci pr_info("UV: SMI NMI support: %s\n", uvh_nmi_mmrx_type); 2798c2ecf20Sopenharmony_ci } else { 2808c2ecf20Sopenharmony_ci nmi_mmr = UVH_NMI_MMR; 2818c2ecf20Sopenharmony_ci nmi_mmr_clear = UVH_NMI_MMR_CLEAR; 2828c2ecf20Sopenharmony_ci nmi_mmr_pending = 1UL << UVH_NMI_MMR_SHIFT; 2838c2ecf20Sopenharmony_ci pr_info("UV: SMI NMI support: %s\n", UVH_NMI_MMR_TYPE); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/* Read NMI MMR and check if NMI flag was set by BMC. */ 2888c2ecf20Sopenharmony_cistatic inline int uv_nmi_test_mmr(struct uv_hub_nmi_s *hub_nmi) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci hub_nmi->nmi_value = uv_read_local_mmr(nmi_mmr); 2918c2ecf20Sopenharmony_ci atomic_inc(&hub_nmi->read_mmr_count); 2928c2ecf20Sopenharmony_ci return !!(hub_nmi->nmi_value & nmi_mmr_pending); 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic inline void uv_local_mmr_clear_nmi(void) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci uv_write_local_mmr(nmi_mmr_clear, nmi_mmr_pending); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/* 3018c2ecf20Sopenharmony_ci * UV hubless NMI handler functions 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_cistatic inline void uv_reassert_nmi(void) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci /* (from arch/x86/include/asm/mach_traps.h) */ 3068c2ecf20Sopenharmony_ci outb(0x8f, NMI_CONTROL_PORT); 3078c2ecf20Sopenharmony_ci inb(NMI_DUMMY_PORT); /* dummy read */ 3088c2ecf20Sopenharmony_ci outb(0x0f, NMI_CONTROL_PORT); 3098c2ecf20Sopenharmony_ci inb(NMI_DUMMY_PORT); /* dummy read */ 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic void uv_init_hubless_pch_io(int offset, int mask, int data) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci int *addr = PCH_PCR_GPIO_ADDRESS(offset); 3158c2ecf20Sopenharmony_ci int readd = readl(addr); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (mask) { /* OR in new data */ 3188c2ecf20Sopenharmony_ci int writed = (readd & ~mask) | data; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci nmi_debug("UV:PCH: %p = %x & %x | %x (%x)\n", 3218c2ecf20Sopenharmony_ci addr, readd, ~mask, data, writed); 3228c2ecf20Sopenharmony_ci writel(writed, addr); 3238c2ecf20Sopenharmony_ci } else if (readd & data) { /* clear status bit */ 3248c2ecf20Sopenharmony_ci nmi_debug("UV:PCH: %p = %x\n", addr, data); 3258c2ecf20Sopenharmony_ci writel(data, addr); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci (void)readl(addr); /* flush write data */ 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic void uv_nmi_setup_hubless_intr(void) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci uv_pch_intr_now_enabled = uv_pch_intr_enable; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci uv_init_hubless_pch_io( 3368c2ecf20Sopenharmony_ci PAD_CFG_DW0_GPP_D_0, GPIROUTNMI, 3378c2ecf20Sopenharmony_ci uv_pch_intr_now_enabled ? GPIROUTNMI : 0); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci nmi_debug("UV:NMI: GPP_D_0 interrupt %s\n", 3408c2ecf20Sopenharmony_ci uv_pch_intr_now_enabled ? "enabled" : "disabled"); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic struct init_nmi { 3448c2ecf20Sopenharmony_ci unsigned int offset; 3458c2ecf20Sopenharmony_ci unsigned int mask; 3468c2ecf20Sopenharmony_ci unsigned int data; 3478c2ecf20Sopenharmony_ci} init_nmi[] = { 3488c2ecf20Sopenharmony_ci { /* HOSTSW_OWN_GPP_D_0 */ 3498c2ecf20Sopenharmony_ci .offset = 0x84, 3508c2ecf20Sopenharmony_ci .mask = 0x1, 3518c2ecf20Sopenharmony_ci .data = 0x0, /* ACPI Mode */ 3528c2ecf20Sopenharmony_ci }, 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci/* Clear status: */ 3558c2ecf20Sopenharmony_ci { /* GPI_INT_STS_GPP_D_0 */ 3568c2ecf20Sopenharmony_ci .offset = 0x104, 3578c2ecf20Sopenharmony_ci .mask = 0x0, 3588c2ecf20Sopenharmony_ci .data = 0x1, /* Clear Status */ 3598c2ecf20Sopenharmony_ci }, 3608c2ecf20Sopenharmony_ci { /* GPI_GPE_STS_GPP_D_0 */ 3618c2ecf20Sopenharmony_ci .offset = 0x124, 3628c2ecf20Sopenharmony_ci .mask = 0x0, 3638c2ecf20Sopenharmony_ci .data = 0x1, /* Clear Status */ 3648c2ecf20Sopenharmony_ci }, 3658c2ecf20Sopenharmony_ci { /* GPI_SMI_STS_GPP_D_0 */ 3668c2ecf20Sopenharmony_ci .offset = 0x144, 3678c2ecf20Sopenharmony_ci .mask = 0x0, 3688c2ecf20Sopenharmony_ci .data = 0x1, /* Clear Status */ 3698c2ecf20Sopenharmony_ci }, 3708c2ecf20Sopenharmony_ci { /* GPI_NMI_STS_GPP_D_0 */ 3718c2ecf20Sopenharmony_ci .offset = 0x164, 3728c2ecf20Sopenharmony_ci .mask = 0x0, 3738c2ecf20Sopenharmony_ci .data = 0x1, /* Clear Status */ 3748c2ecf20Sopenharmony_ci }, 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci/* Disable interrupts: */ 3778c2ecf20Sopenharmony_ci { /* GPI_INT_EN_GPP_D_0 */ 3788c2ecf20Sopenharmony_ci .offset = 0x114, 3798c2ecf20Sopenharmony_ci .mask = 0x1, 3808c2ecf20Sopenharmony_ci .data = 0x0, /* Disable interrupt generation */ 3818c2ecf20Sopenharmony_ci }, 3828c2ecf20Sopenharmony_ci { /* GPI_GPE_EN_GPP_D_0 */ 3838c2ecf20Sopenharmony_ci .offset = 0x134, 3848c2ecf20Sopenharmony_ci .mask = 0x1, 3858c2ecf20Sopenharmony_ci .data = 0x0, /* Disable interrupt generation */ 3868c2ecf20Sopenharmony_ci }, 3878c2ecf20Sopenharmony_ci { /* GPI_SMI_EN_GPP_D_0 */ 3888c2ecf20Sopenharmony_ci .offset = 0x154, 3898c2ecf20Sopenharmony_ci .mask = 0x1, 3908c2ecf20Sopenharmony_ci .data = 0x0, /* Disable interrupt generation */ 3918c2ecf20Sopenharmony_ci }, 3928c2ecf20Sopenharmony_ci { /* GPI_NMI_EN_GPP_D_0 */ 3938c2ecf20Sopenharmony_ci .offset = 0x174, 3948c2ecf20Sopenharmony_ci .mask = 0x1, 3958c2ecf20Sopenharmony_ci .data = 0x0, /* Disable interrupt generation */ 3968c2ecf20Sopenharmony_ci }, 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci/* Setup GPP_D_0 Pad Config: */ 3998c2ecf20Sopenharmony_ci { /* PAD_CFG_DW0_GPP_D_0 */ 4008c2ecf20Sopenharmony_ci .offset = 0x4c0, 4018c2ecf20Sopenharmony_ci .mask = 0xffffffff, 4028c2ecf20Sopenharmony_ci .data = 0x82020100, 4038c2ecf20Sopenharmony_ci/* 4048c2ecf20Sopenharmony_ci * 31:30 Pad Reset Config (PADRSTCFG): = 2h # PLTRST# (default) 4058c2ecf20Sopenharmony_ci * 4068c2ecf20Sopenharmony_ci * 29 RX Pad State Select (RXPADSTSEL): = 0 # Raw RX pad state directly 4078c2ecf20Sopenharmony_ci * from RX buffer (default) 4088c2ecf20Sopenharmony_ci * 4098c2ecf20Sopenharmony_ci * 28 RX Raw Override to '1' (RXRAW1): = 0 # No Override 4108c2ecf20Sopenharmony_ci * 4118c2ecf20Sopenharmony_ci * 26:25 RX Level/Edge Configuration (RXEVCFG): 4128c2ecf20Sopenharmony_ci * = 0h # Level 4138c2ecf20Sopenharmony_ci * = 1h # Edge 4148c2ecf20Sopenharmony_ci * 4158c2ecf20Sopenharmony_ci * 23 RX Invert (RXINV): = 0 # No Inversion (signal active high) 4168c2ecf20Sopenharmony_ci * 4178c2ecf20Sopenharmony_ci * 20 GPIO Input Route IOxAPIC (GPIROUTIOXAPIC): 4188c2ecf20Sopenharmony_ci * = 0 # Routing does not cause peripheral IRQ... 4198c2ecf20Sopenharmony_ci * # (we want an NMI not an IRQ) 4208c2ecf20Sopenharmony_ci * 4218c2ecf20Sopenharmony_ci * 19 GPIO Input Route SCI (GPIROUTSCI): = 0 # Routing does not cause SCI. 4228c2ecf20Sopenharmony_ci * 18 GPIO Input Route SMI (GPIROUTSMI): = 0 # Routing does not cause SMI. 4238c2ecf20Sopenharmony_ci * 17 GPIO Input Route NMI (GPIROUTNMI): = 1 # Routing can cause NMI. 4248c2ecf20Sopenharmony_ci * 4258c2ecf20Sopenharmony_ci * 11:10 Pad Mode (PMODE1/0): = 0h = GPIO control the Pad. 4268c2ecf20Sopenharmony_ci * 9 GPIO RX Disable (GPIORXDIS): 4278c2ecf20Sopenharmony_ci * = 0 # Enable the input buffer (active low enable) 4288c2ecf20Sopenharmony_ci * 4298c2ecf20Sopenharmony_ci * 8 GPIO TX Disable (GPIOTXDIS): 4308c2ecf20Sopenharmony_ci * = 1 # Disable the output buffer; i.e. Hi-Z 4318c2ecf20Sopenharmony_ci * 4328c2ecf20Sopenharmony_ci * 1 GPIO RX State (GPIORXSTATE): This is the current internal RX pad state.. 4338c2ecf20Sopenharmony_ci * 0 GPIO TX State (GPIOTXSTATE): 4348c2ecf20Sopenharmony_ci * = 0 # (Leave at default) 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_ci }, 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci/* Pad Config DW1 */ 4398c2ecf20Sopenharmony_ci { /* PAD_CFG_DW1_GPP_D_0 */ 4408c2ecf20Sopenharmony_ci .offset = 0x4c4, 4418c2ecf20Sopenharmony_ci .mask = 0x3c00, 4428c2ecf20Sopenharmony_ci .data = 0, /* Termination = none (default) */ 4438c2ecf20Sopenharmony_ci }, 4448c2ecf20Sopenharmony_ci}; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic void uv_init_hubless_pch_d0(void) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci int i, read; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci read = *PCH_PCR_GPIO_ADDRESS(PAD_OWN_GPP_D_0); 4518c2ecf20Sopenharmony_ci if (read != 0) { 4528c2ecf20Sopenharmony_ci pr_info("UV: Hubless NMI already configured\n"); 4538c2ecf20Sopenharmony_ci return; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci nmi_debug("UV: Initializing UV Hubless NMI on PCH\n"); 4578c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(init_nmi); i++) { 4588c2ecf20Sopenharmony_ci uv_init_hubless_pch_io(init_nmi[i].offset, 4598c2ecf20Sopenharmony_ci init_nmi[i].mask, 4608c2ecf20Sopenharmony_ci init_nmi[i].data); 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic int uv_nmi_test_hubless(struct uv_hub_nmi_s *hub_nmi) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci int *pstat = PCH_PCR_GPIO_ADDRESS(GPI_NMI_STS_GPP_D_0); 4678c2ecf20Sopenharmony_ci int status = *pstat; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci hub_nmi->nmi_value = status; 4708c2ecf20Sopenharmony_ci atomic_inc(&hub_nmi->read_mmr_count); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (!(status & STS_GPP_D_0_MASK)) /* Not a UV external NMI */ 4738c2ecf20Sopenharmony_ci return 0; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci *pstat = STS_GPP_D_0_MASK; /* Is a UV NMI: clear GPP_D_0 status */ 4768c2ecf20Sopenharmony_ci (void)*pstat; /* Flush write */ 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return 1; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic int uv_test_nmi(struct uv_hub_nmi_s *hub_nmi) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci if (hub_nmi->hub_present) 4848c2ecf20Sopenharmony_ci return uv_nmi_test_mmr(hub_nmi); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (hub_nmi->pch_owner) /* Only PCH owner can check status */ 4878c2ecf20Sopenharmony_ci return uv_nmi_test_hubless(hub_nmi); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return -1; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci/* 4938c2ecf20Sopenharmony_ci * If first CPU in on this hub, set hub_nmi "in_nmi" and "owner" values and 4948c2ecf20Sopenharmony_ci * return true. If first CPU in on the system, set global "in_nmi" flag. 4958c2ecf20Sopenharmony_ci */ 4968c2ecf20Sopenharmony_cistatic int uv_set_in_nmi(int cpu, struct uv_hub_nmi_s *hub_nmi) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci int first = atomic_add_unless(&hub_nmi->in_nmi, 1, 1); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (first) { 5018c2ecf20Sopenharmony_ci atomic_set(&hub_nmi->cpu_owner, cpu); 5028c2ecf20Sopenharmony_ci if (atomic_add_unless(&uv_in_nmi, 1, 1)) 5038c2ecf20Sopenharmony_ci atomic_set(&uv_nmi_cpu, cpu); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci atomic_inc(&hub_nmi->nmi_count); 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci return first; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci/* Check if this is a system NMI event */ 5118c2ecf20Sopenharmony_cistatic int uv_check_nmi(struct uv_hub_nmi_s *hub_nmi) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 5148c2ecf20Sopenharmony_ci int nmi = 0; 5158c2ecf20Sopenharmony_ci int nmi_detected = 0; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci local64_inc(&uv_nmi_count); 5188c2ecf20Sopenharmony_ci this_cpu_inc(uv_cpu_nmi.queries); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci do { 5218c2ecf20Sopenharmony_ci nmi = atomic_read(&hub_nmi->in_nmi); 5228c2ecf20Sopenharmony_ci if (nmi) 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (raw_spin_trylock(&hub_nmi->nmi_lock)) { 5268c2ecf20Sopenharmony_ci nmi_detected = uv_test_nmi(hub_nmi); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* Check flag for UV external NMI */ 5298c2ecf20Sopenharmony_ci if (nmi_detected > 0) { 5308c2ecf20Sopenharmony_ci uv_set_in_nmi(cpu, hub_nmi); 5318c2ecf20Sopenharmony_ci nmi = 1; 5328c2ecf20Sopenharmony_ci break; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* A non-PCH node in a hubless system waits for NMI */ 5368c2ecf20Sopenharmony_ci else if (nmi_detected < 0) 5378c2ecf20Sopenharmony_ci goto slave_wait; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* MMR/PCH NMI flag is clear */ 5408c2ecf20Sopenharmony_ci raw_spin_unlock(&hub_nmi->nmi_lock); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci } else { 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* Wait a moment for the HUB NMI locker to set flag */ 5458c2ecf20Sopenharmony_cislave_wait: cpu_relax(); 5468c2ecf20Sopenharmony_ci udelay(uv_nmi_slave_delay); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* Re-check hub in_nmi flag */ 5498c2ecf20Sopenharmony_ci nmi = atomic_read(&hub_nmi->in_nmi); 5508c2ecf20Sopenharmony_ci if (nmi) 5518c2ecf20Sopenharmony_ci break; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* 5558c2ecf20Sopenharmony_ci * Check if this BMC missed setting the MMR NMI flag (or) 5568c2ecf20Sopenharmony_ci * UV hubless system where only PCH owner can check flag 5578c2ecf20Sopenharmony_ci */ 5588c2ecf20Sopenharmony_ci if (!nmi) { 5598c2ecf20Sopenharmony_ci nmi = atomic_read(&uv_in_nmi); 5608c2ecf20Sopenharmony_ci if (nmi) 5618c2ecf20Sopenharmony_ci uv_set_in_nmi(cpu, hub_nmi); 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* If we're holding the hub lock, release it now */ 5658c2ecf20Sopenharmony_ci if (nmi_detected < 0) 5668c2ecf20Sopenharmony_ci raw_spin_unlock(&hub_nmi->nmi_lock); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci } while (0); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (!nmi) 5718c2ecf20Sopenharmony_ci local64_inc(&uv_nmi_misses); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci return nmi; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci/* Need to reset the NMI MMR register, but only once per hub. */ 5778c2ecf20Sopenharmony_cistatic inline void uv_clear_nmi(int cpu) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct uv_hub_nmi_s *hub_nmi = uv_hub_nmi; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (cpu == atomic_read(&hub_nmi->cpu_owner)) { 5828c2ecf20Sopenharmony_ci atomic_set(&hub_nmi->cpu_owner, -1); 5838c2ecf20Sopenharmony_ci atomic_set(&hub_nmi->in_nmi, 0); 5848c2ecf20Sopenharmony_ci if (hub_nmi->hub_present) 5858c2ecf20Sopenharmony_ci uv_local_mmr_clear_nmi(); 5868c2ecf20Sopenharmony_ci else 5878c2ecf20Sopenharmony_ci uv_reassert_nmi(); 5888c2ecf20Sopenharmony_ci raw_spin_unlock(&hub_nmi->nmi_lock); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci/* Ping non-responding CPU's attempting to force them into the NMI handler */ 5938c2ecf20Sopenharmony_cistatic void uv_nmi_nr_cpus_ping(void) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci int cpu; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci for_each_cpu(cpu, uv_nmi_cpu_mask) 5988c2ecf20Sopenharmony_ci uv_cpu_nmi_per(cpu).pinging = 1; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci apic->send_IPI_mask(uv_nmi_cpu_mask, APIC_DM_NMI); 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci/* Clean up flags for CPU's that ignored both NMI and ping */ 6048c2ecf20Sopenharmony_cistatic void uv_nmi_cleanup_mask(void) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci int cpu; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci for_each_cpu(cpu, uv_nmi_cpu_mask) { 6098c2ecf20Sopenharmony_ci uv_cpu_nmi_per(cpu).pinging = 0; 6108c2ecf20Sopenharmony_ci uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_OUT; 6118c2ecf20Sopenharmony_ci cpumask_clear_cpu(cpu, uv_nmi_cpu_mask); 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci/* Loop waiting as CPU's enter NMI handler */ 6168c2ecf20Sopenharmony_cistatic int uv_nmi_wait_cpus(int first) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci int i, j, k, n = num_online_cpus(); 6198c2ecf20Sopenharmony_ci int last_k = 0, waiting = 0; 6208c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (first) { 6238c2ecf20Sopenharmony_ci cpumask_copy(uv_nmi_cpu_mask, cpu_online_mask); 6248c2ecf20Sopenharmony_ci k = 0; 6258c2ecf20Sopenharmony_ci } else { 6268c2ecf20Sopenharmony_ci k = n - cpumask_weight(uv_nmi_cpu_mask); 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* PCH NMI causes only one CPU to respond */ 6308c2ecf20Sopenharmony_ci if (first && uv_pch_intr_now_enabled) { 6318c2ecf20Sopenharmony_ci cpumask_clear_cpu(cpu, uv_nmi_cpu_mask); 6328c2ecf20Sopenharmony_ci return n - k - 1; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci udelay(uv_nmi_initial_delay); 6368c2ecf20Sopenharmony_ci for (i = 0; i < uv_nmi_retry_count; i++) { 6378c2ecf20Sopenharmony_ci int loop_delay = uv_nmi_loop_delay; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci for_each_cpu(j, uv_nmi_cpu_mask) { 6408c2ecf20Sopenharmony_ci if (uv_cpu_nmi_per(j).state) { 6418c2ecf20Sopenharmony_ci cpumask_clear_cpu(j, uv_nmi_cpu_mask); 6428c2ecf20Sopenharmony_ci if (++k >= n) 6438c2ecf20Sopenharmony_ci break; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci if (k >= n) { /* all in? */ 6478c2ecf20Sopenharmony_ci k = n; 6488c2ecf20Sopenharmony_ci break; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci if (last_k != k) { /* abort if no new CPU's coming in */ 6518c2ecf20Sopenharmony_ci last_k = k; 6528c2ecf20Sopenharmony_ci waiting = 0; 6538c2ecf20Sopenharmony_ci } else if (++waiting > uv_nmi_wait_count) 6548c2ecf20Sopenharmony_ci break; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* Extend delay if waiting only for CPU 0: */ 6578c2ecf20Sopenharmony_ci if (waiting && (n - k) == 1 && 6588c2ecf20Sopenharmony_ci cpumask_test_cpu(0, uv_nmi_cpu_mask)) 6598c2ecf20Sopenharmony_ci loop_delay *= 100; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci udelay(loop_delay); 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci atomic_set(&uv_nmi_cpus_in_nmi, k); 6648c2ecf20Sopenharmony_ci return n - k; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci/* Wait until all slave CPU's have entered UV NMI handler */ 6688c2ecf20Sopenharmony_cistatic void uv_nmi_wait(int master) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci /* Indicate this CPU is in: */ 6718c2ecf20Sopenharmony_ci this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_IN); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci /* If not the first CPU in (the master), then we are a slave CPU */ 6748c2ecf20Sopenharmony_ci if (!master) 6758c2ecf20Sopenharmony_ci return; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci do { 6788c2ecf20Sopenharmony_ci /* Wait for all other CPU's to gather here */ 6798c2ecf20Sopenharmony_ci if (!uv_nmi_wait_cpus(1)) 6808c2ecf20Sopenharmony_ci break; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* If not all made it in, send IPI NMI to them */ 6838c2ecf20Sopenharmony_ci pr_alert("UV: Sending NMI IPI to %d CPUs: %*pbl\n", 6848c2ecf20Sopenharmony_ci cpumask_weight(uv_nmi_cpu_mask), 6858c2ecf20Sopenharmony_ci cpumask_pr_args(uv_nmi_cpu_mask)); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci uv_nmi_nr_cpus_ping(); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* If all CPU's are in, then done */ 6908c2ecf20Sopenharmony_ci if (!uv_nmi_wait_cpus(0)) 6918c2ecf20Sopenharmony_ci break; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci pr_alert("UV: %d CPUs not in NMI loop: %*pbl\n", 6948c2ecf20Sopenharmony_ci cpumask_weight(uv_nmi_cpu_mask), 6958c2ecf20Sopenharmony_ci cpumask_pr_args(uv_nmi_cpu_mask)); 6968c2ecf20Sopenharmony_ci } while (0); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci pr_alert("UV: %d of %d CPUs in NMI\n", 6998c2ecf20Sopenharmony_ci atomic_read(&uv_nmi_cpus_in_nmi), num_online_cpus()); 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci/* Dump Instruction Pointer header */ 7038c2ecf20Sopenharmony_cistatic void uv_nmi_dump_cpu_ip_hdr(void) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci pr_info("\nUV: %4s %6s %-32s %s (Note: PID 0 not listed)\n", 7068c2ecf20Sopenharmony_ci "CPU", "PID", "COMMAND", "IP"); 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci/* Dump Instruction Pointer info */ 7108c2ecf20Sopenharmony_cistatic void uv_nmi_dump_cpu_ip(int cpu, struct pt_regs *regs) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci pr_info("UV: %4d %6d %-32.32s %pS", 7138c2ecf20Sopenharmony_ci cpu, current->pid, current->comm, (void *)regs->ip); 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci/* 7178c2ecf20Sopenharmony_ci * Dump this CPU's state. If action was set to "kdump" and the crash_kexec 7188c2ecf20Sopenharmony_ci * failed, then we provide "dump" as an alternate action. Action "dump" now 7198c2ecf20Sopenharmony_ci * also includes the show "ips" (instruction pointers) action whereas the 7208c2ecf20Sopenharmony_ci * action "ips" only displays instruction pointers for the non-idle CPU's. 7218c2ecf20Sopenharmony_ci * This is an abbreviated form of the "ps" command. 7228c2ecf20Sopenharmony_ci */ 7238c2ecf20Sopenharmony_cistatic void uv_nmi_dump_state_cpu(int cpu, struct pt_regs *regs) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci const char *dots = " ................................. "; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (cpu == 0) 7288c2ecf20Sopenharmony_ci uv_nmi_dump_cpu_ip_hdr(); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (current->pid != 0 || !uv_nmi_action_is("ips")) 7318c2ecf20Sopenharmony_ci uv_nmi_dump_cpu_ip(cpu, regs); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (uv_nmi_action_is("dump")) { 7348c2ecf20Sopenharmony_ci pr_info("UV:%sNMI process trace for CPU %d\n", dots, cpu); 7358c2ecf20Sopenharmony_ci show_regs(regs); 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_DUMP_DONE); 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci/* Trigger a slave CPU to dump it's state */ 7428c2ecf20Sopenharmony_cistatic void uv_nmi_trigger_dump(int cpu) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci int retry = uv_nmi_trigger_delay; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci if (uv_cpu_nmi_per(cpu).state != UV_NMI_STATE_IN) 7478c2ecf20Sopenharmony_ci return; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_DUMP; 7508c2ecf20Sopenharmony_ci do { 7518c2ecf20Sopenharmony_ci cpu_relax(); 7528c2ecf20Sopenharmony_ci udelay(10); 7538c2ecf20Sopenharmony_ci if (uv_cpu_nmi_per(cpu).state 7548c2ecf20Sopenharmony_ci != UV_NMI_STATE_DUMP) 7558c2ecf20Sopenharmony_ci return; 7568c2ecf20Sopenharmony_ci } while (--retry > 0); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci pr_crit("UV: CPU %d stuck in process dump function\n", cpu); 7598c2ecf20Sopenharmony_ci uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_DUMP_DONE; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci/* Wait until all CPU's ready to exit */ 7638c2ecf20Sopenharmony_cistatic void uv_nmi_sync_exit(int master) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci atomic_dec(&uv_nmi_cpus_in_nmi); 7668c2ecf20Sopenharmony_ci if (master) { 7678c2ecf20Sopenharmony_ci while (atomic_read(&uv_nmi_cpus_in_nmi) > 0) 7688c2ecf20Sopenharmony_ci cpu_relax(); 7698c2ecf20Sopenharmony_ci atomic_set(&uv_nmi_slave_continue, SLAVE_CLEAR); 7708c2ecf20Sopenharmony_ci } else { 7718c2ecf20Sopenharmony_ci while (atomic_read(&uv_nmi_slave_continue)) 7728c2ecf20Sopenharmony_ci cpu_relax(); 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci/* Current "health" check is to check which CPU's are responsive */ 7778c2ecf20Sopenharmony_cistatic void uv_nmi_action_health(int cpu, struct pt_regs *regs, int master) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci if (master) { 7808c2ecf20Sopenharmony_ci int in = atomic_read(&uv_nmi_cpus_in_nmi); 7818c2ecf20Sopenharmony_ci int out = num_online_cpus() - in; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci pr_alert("UV: NMI CPU health check (non-responding:%d)\n", out); 7848c2ecf20Sopenharmony_ci atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT); 7858c2ecf20Sopenharmony_ci } else { 7868c2ecf20Sopenharmony_ci while (!atomic_read(&uv_nmi_slave_continue)) 7878c2ecf20Sopenharmony_ci cpu_relax(); 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci uv_nmi_sync_exit(master); 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci/* Walk through CPU list and dump state of each */ 7938c2ecf20Sopenharmony_cistatic void uv_nmi_dump_state(int cpu, struct pt_regs *regs, int master) 7948c2ecf20Sopenharmony_ci{ 7958c2ecf20Sopenharmony_ci if (master) { 7968c2ecf20Sopenharmony_ci int tcpu; 7978c2ecf20Sopenharmony_ci int ignored = 0; 7988c2ecf20Sopenharmony_ci int saved_console_loglevel = console_loglevel; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci pr_alert("UV: tracing %s for %d CPUs from CPU %d\n", 8018c2ecf20Sopenharmony_ci uv_nmi_action_is("ips") ? "IPs" : "processes", 8028c2ecf20Sopenharmony_ci atomic_read(&uv_nmi_cpus_in_nmi), cpu); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci console_loglevel = uv_nmi_loglevel; 8058c2ecf20Sopenharmony_ci atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT); 8068c2ecf20Sopenharmony_ci for_each_online_cpu(tcpu) { 8078c2ecf20Sopenharmony_ci if (cpumask_test_cpu(tcpu, uv_nmi_cpu_mask)) 8088c2ecf20Sopenharmony_ci ignored++; 8098c2ecf20Sopenharmony_ci else if (tcpu == cpu) 8108c2ecf20Sopenharmony_ci uv_nmi_dump_state_cpu(tcpu, regs); 8118c2ecf20Sopenharmony_ci else 8128c2ecf20Sopenharmony_ci uv_nmi_trigger_dump(tcpu); 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci if (ignored) 8158c2ecf20Sopenharmony_ci pr_alert("UV: %d CPUs ignored NMI\n", ignored); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci console_loglevel = saved_console_loglevel; 8188c2ecf20Sopenharmony_ci pr_alert("UV: process trace complete\n"); 8198c2ecf20Sopenharmony_ci } else { 8208c2ecf20Sopenharmony_ci while (!atomic_read(&uv_nmi_slave_continue)) 8218c2ecf20Sopenharmony_ci cpu_relax(); 8228c2ecf20Sopenharmony_ci while (this_cpu_read(uv_cpu_nmi.state) != UV_NMI_STATE_DUMP) 8238c2ecf20Sopenharmony_ci cpu_relax(); 8248c2ecf20Sopenharmony_ci uv_nmi_dump_state_cpu(cpu, regs); 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci uv_nmi_sync_exit(master); 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_cistatic void uv_nmi_touch_watchdogs(void) 8308c2ecf20Sopenharmony_ci{ 8318c2ecf20Sopenharmony_ci touch_softlockup_watchdog_sync(); 8328c2ecf20Sopenharmony_ci clocksource_touch_watchdog(); 8338c2ecf20Sopenharmony_ci rcu_cpu_stall_reset(); 8348c2ecf20Sopenharmony_ci touch_nmi_watchdog(); 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_cistatic atomic_t uv_nmi_kexec_failed; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci#if defined(CONFIG_KEXEC_CORE) 8408c2ecf20Sopenharmony_cistatic void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci /* Call crash to dump system state */ 8438c2ecf20Sopenharmony_ci if (master) { 8448c2ecf20Sopenharmony_ci pr_emerg("UV: NMI executing crash_kexec on CPU%d\n", cpu); 8458c2ecf20Sopenharmony_ci crash_kexec(regs); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci pr_emerg("UV: crash_kexec unexpectedly returned, "); 8488c2ecf20Sopenharmony_ci atomic_set(&uv_nmi_kexec_failed, 1); 8498c2ecf20Sopenharmony_ci if (!kexec_crash_image) { 8508c2ecf20Sopenharmony_ci pr_cont("crash kernel not loaded\n"); 8518c2ecf20Sopenharmony_ci return; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci pr_cont("kexec busy, stalling cpus while waiting\n"); 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci /* If crash exec fails the slaves should return, otherwise stall */ 8578c2ecf20Sopenharmony_ci while (atomic_read(&uv_nmi_kexec_failed) == 0) 8588c2ecf20Sopenharmony_ci mdelay(10); 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci#else /* !CONFIG_KEXEC_CORE */ 8628c2ecf20Sopenharmony_cistatic inline void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci if (master) 8658c2ecf20Sopenharmony_ci pr_err("UV: NMI kdump: KEXEC not supported in this kernel\n"); 8668c2ecf20Sopenharmony_ci atomic_set(&uv_nmi_kexec_failed, 1); 8678c2ecf20Sopenharmony_ci} 8688c2ecf20Sopenharmony_ci#endif /* !CONFIG_KEXEC_CORE */ 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci#ifdef CONFIG_KGDB 8718c2ecf20Sopenharmony_ci#ifdef CONFIG_KGDB_KDB 8728c2ecf20Sopenharmony_cistatic inline int uv_nmi_kdb_reason(void) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci return KDB_REASON_SYSTEM_NMI; 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci#else /* !CONFIG_KGDB_KDB */ 8778c2ecf20Sopenharmony_cistatic inline int uv_nmi_kdb_reason(void) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci /* Ensure user is expecting to attach gdb remote */ 8808c2ecf20Sopenharmony_ci if (uv_nmi_action_is("kgdb")) 8818c2ecf20Sopenharmony_ci return 0; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci pr_err("UV: NMI error: KDB is not enabled in this kernel\n"); 8848c2ecf20Sopenharmony_ci return -1; 8858c2ecf20Sopenharmony_ci} 8868c2ecf20Sopenharmony_ci#endif /* CONFIG_KGDB_KDB */ 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci/* 8898c2ecf20Sopenharmony_ci * Call KGDB/KDB from NMI handler 8908c2ecf20Sopenharmony_ci * 8918c2ecf20Sopenharmony_ci * Note that if both KGDB and KDB are configured, then the action of 'kgdb' or 8928c2ecf20Sopenharmony_ci * 'kdb' has no affect on which is used. See the KGDB documention for further 8938c2ecf20Sopenharmony_ci * information. 8948c2ecf20Sopenharmony_ci */ 8958c2ecf20Sopenharmony_cistatic void uv_call_kgdb_kdb(int cpu, struct pt_regs *regs, int master) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci if (master) { 8988c2ecf20Sopenharmony_ci int reason = uv_nmi_kdb_reason(); 8998c2ecf20Sopenharmony_ci int ret; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (reason < 0) 9028c2ecf20Sopenharmony_ci return; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci /* Call KGDB NMI handler as MASTER */ 9058c2ecf20Sopenharmony_ci ret = kgdb_nmicallin(cpu, X86_TRAP_NMI, regs, reason, 9068c2ecf20Sopenharmony_ci &uv_nmi_slave_continue); 9078c2ecf20Sopenharmony_ci if (ret) { 9088c2ecf20Sopenharmony_ci pr_alert("KGDB returned error, is kgdboc set?\n"); 9098c2ecf20Sopenharmony_ci atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT); 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci } else { 9128c2ecf20Sopenharmony_ci /* Wait for KGDB signal that it's ready for slaves to enter */ 9138c2ecf20Sopenharmony_ci int sig; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci do { 9168c2ecf20Sopenharmony_ci cpu_relax(); 9178c2ecf20Sopenharmony_ci sig = atomic_read(&uv_nmi_slave_continue); 9188c2ecf20Sopenharmony_ci } while (!sig); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci /* Call KGDB as slave */ 9218c2ecf20Sopenharmony_ci if (sig == SLAVE_CONTINUE) 9228c2ecf20Sopenharmony_ci kgdb_nmicallback(cpu, regs); 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci uv_nmi_sync_exit(master); 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci#else /* !CONFIG_KGDB */ 9288c2ecf20Sopenharmony_cistatic inline void uv_call_kgdb_kdb(int cpu, struct pt_regs *regs, int master) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci pr_err("UV: NMI error: KGDB is not enabled in this kernel\n"); 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci#endif /* !CONFIG_KGDB */ 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci/* 9358c2ecf20Sopenharmony_ci * UV NMI handler 9368c2ecf20Sopenharmony_ci */ 9378c2ecf20Sopenharmony_cistatic int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci struct uv_hub_nmi_s *hub_nmi = uv_hub_nmi; 9408c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 9418c2ecf20Sopenharmony_ci int master = 0; 9428c2ecf20Sopenharmony_ci unsigned long flags; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci local_irq_save(flags); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci /* If not a UV System NMI, ignore */ 9478c2ecf20Sopenharmony_ci if (!this_cpu_read(uv_cpu_nmi.pinging) && !uv_check_nmi(hub_nmi)) { 9488c2ecf20Sopenharmony_ci local_irq_restore(flags); 9498c2ecf20Sopenharmony_ci return NMI_DONE; 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci /* Indicate we are the first CPU into the NMI handler */ 9538c2ecf20Sopenharmony_ci master = (atomic_read(&uv_nmi_cpu) == cpu); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci /* If NMI action is "kdump", then attempt to do it */ 9568c2ecf20Sopenharmony_ci if (uv_nmi_action_is("kdump")) { 9578c2ecf20Sopenharmony_ci uv_nmi_kdump(cpu, master, regs); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci /* Unexpected return, revert action to "dump" */ 9608c2ecf20Sopenharmony_ci if (master) 9618c2ecf20Sopenharmony_ci strncpy(uv_nmi_action, "dump", strlen(uv_nmi_action)); 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci /* Pause as all CPU's enter the NMI handler */ 9658c2ecf20Sopenharmony_ci uv_nmi_wait(master); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci /* Process actions other than "kdump": */ 9688c2ecf20Sopenharmony_ci if (uv_nmi_action_is("health")) { 9698c2ecf20Sopenharmony_ci uv_nmi_action_health(cpu, regs, master); 9708c2ecf20Sopenharmony_ci } else if (uv_nmi_action_is("ips") || uv_nmi_action_is("dump")) { 9718c2ecf20Sopenharmony_ci uv_nmi_dump_state(cpu, regs, master); 9728c2ecf20Sopenharmony_ci } else if (uv_nmi_action_is("kdb") || uv_nmi_action_is("kgdb")) { 9738c2ecf20Sopenharmony_ci uv_call_kgdb_kdb(cpu, regs, master); 9748c2ecf20Sopenharmony_ci } else { 9758c2ecf20Sopenharmony_ci if (master) 9768c2ecf20Sopenharmony_ci pr_alert("UV: unknown NMI action: %s\n", uv_nmi_action); 9778c2ecf20Sopenharmony_ci uv_nmi_sync_exit(master); 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* Clear per_cpu "in_nmi" flag */ 9818c2ecf20Sopenharmony_ci this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_OUT); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci /* Clear MMR NMI flag on each hub */ 9848c2ecf20Sopenharmony_ci uv_clear_nmi(cpu); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci /* Clear global flags */ 9878c2ecf20Sopenharmony_ci if (master) { 9888c2ecf20Sopenharmony_ci if (cpumask_weight(uv_nmi_cpu_mask)) 9898c2ecf20Sopenharmony_ci uv_nmi_cleanup_mask(); 9908c2ecf20Sopenharmony_ci atomic_set(&uv_nmi_cpus_in_nmi, -1); 9918c2ecf20Sopenharmony_ci atomic_set(&uv_nmi_cpu, -1); 9928c2ecf20Sopenharmony_ci atomic_set(&uv_in_nmi, 0); 9938c2ecf20Sopenharmony_ci atomic_set(&uv_nmi_kexec_failed, 0); 9948c2ecf20Sopenharmony_ci atomic_set(&uv_nmi_slave_continue, SLAVE_CLEAR); 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci uv_nmi_touch_watchdogs(); 9988c2ecf20Sopenharmony_ci local_irq_restore(flags); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci return NMI_HANDLED; 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci/* 10048c2ecf20Sopenharmony_ci * NMI handler for pulling in CPU's when perf events are grabbing our NMI 10058c2ecf20Sopenharmony_ci */ 10068c2ecf20Sopenharmony_cistatic int uv_handle_nmi_ping(unsigned int reason, struct pt_regs *regs) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci int ret; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci this_cpu_inc(uv_cpu_nmi.queries); 10118c2ecf20Sopenharmony_ci if (!this_cpu_read(uv_cpu_nmi.pinging)) { 10128c2ecf20Sopenharmony_ci local64_inc(&uv_nmi_ping_misses); 10138c2ecf20Sopenharmony_ci return NMI_DONE; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci this_cpu_inc(uv_cpu_nmi.pings); 10178c2ecf20Sopenharmony_ci local64_inc(&uv_nmi_ping_count); 10188c2ecf20Sopenharmony_ci ret = uv_handle_nmi(reason, regs); 10198c2ecf20Sopenharmony_ci this_cpu_write(uv_cpu_nmi.pinging, 0); 10208c2ecf20Sopenharmony_ci return ret; 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_cistatic void uv_register_nmi_notifier(void) 10248c2ecf20Sopenharmony_ci{ 10258c2ecf20Sopenharmony_ci if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv")) 10268c2ecf20Sopenharmony_ci pr_warn("UV: NMI handler failed to register\n"); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (register_nmi_handler(NMI_LOCAL, uv_handle_nmi_ping, 0, "uvping")) 10298c2ecf20Sopenharmony_ci pr_warn("UV: PING NMI handler failed to register\n"); 10308c2ecf20Sopenharmony_ci} 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_civoid uv_nmi_init(void) 10338c2ecf20Sopenharmony_ci{ 10348c2ecf20Sopenharmony_ci unsigned int value; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci /* 10378c2ecf20Sopenharmony_ci * Unmask NMI on all CPU's 10388c2ecf20Sopenharmony_ci */ 10398c2ecf20Sopenharmony_ci value = apic_read(APIC_LVT1) | APIC_DM_NMI; 10408c2ecf20Sopenharmony_ci value &= ~APIC_LVT_MASKED; 10418c2ecf20Sopenharmony_ci apic_write(APIC_LVT1, value); 10428c2ecf20Sopenharmony_ci} 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci/* Setup HUB NMI info */ 10458c2ecf20Sopenharmony_cistatic void __init uv_nmi_setup_common(bool hubbed) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci int size = sizeof(void *) * (1 << NODES_SHIFT); 10488c2ecf20Sopenharmony_ci int cpu; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci uv_hub_nmi_list = kzalloc(size, GFP_KERNEL); 10518c2ecf20Sopenharmony_ci nmi_debug("UV: NMI hub list @ 0x%p (%d)\n", uv_hub_nmi_list, size); 10528c2ecf20Sopenharmony_ci BUG_ON(!uv_hub_nmi_list); 10538c2ecf20Sopenharmony_ci size = sizeof(struct uv_hub_nmi_s); 10548c2ecf20Sopenharmony_ci for_each_present_cpu(cpu) { 10558c2ecf20Sopenharmony_ci int nid = cpu_to_node(cpu); 10568c2ecf20Sopenharmony_ci if (uv_hub_nmi_list[nid] == NULL) { 10578c2ecf20Sopenharmony_ci uv_hub_nmi_list[nid] = kzalloc_node(size, 10588c2ecf20Sopenharmony_ci GFP_KERNEL, nid); 10598c2ecf20Sopenharmony_ci BUG_ON(!uv_hub_nmi_list[nid]); 10608c2ecf20Sopenharmony_ci raw_spin_lock_init(&(uv_hub_nmi_list[nid]->nmi_lock)); 10618c2ecf20Sopenharmony_ci atomic_set(&uv_hub_nmi_list[nid]->cpu_owner, -1); 10628c2ecf20Sopenharmony_ci uv_hub_nmi_list[nid]->hub_present = hubbed; 10638c2ecf20Sopenharmony_ci uv_hub_nmi_list[nid]->pch_owner = (nid == 0); 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci uv_hub_nmi_per(cpu) = uv_hub_nmi_list[nid]; 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci BUG_ON(!alloc_cpumask_var(&uv_nmi_cpu_mask, GFP_KERNEL)); 10688c2ecf20Sopenharmony_ci} 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci/* Setup for UV Hub systems */ 10718c2ecf20Sopenharmony_civoid __init uv_nmi_setup(void) 10728c2ecf20Sopenharmony_ci{ 10738c2ecf20Sopenharmony_ci uv_nmi_setup_mmrs(); 10748c2ecf20Sopenharmony_ci uv_nmi_setup_common(true); 10758c2ecf20Sopenharmony_ci uv_register_nmi_notifier(); 10768c2ecf20Sopenharmony_ci pr_info("UV: Hub NMI enabled\n"); 10778c2ecf20Sopenharmony_ci} 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci/* Setup for UV Hubless systems */ 10808c2ecf20Sopenharmony_civoid __init uv_nmi_setup_hubless(void) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci uv_nmi_setup_common(false); 10838c2ecf20Sopenharmony_ci pch_base = xlate_dev_mem_ptr(PCH_PCR_GPIO_1_BASE); 10848c2ecf20Sopenharmony_ci nmi_debug("UV: PCH base:%p from 0x%lx, GPP_D_0\n", 10858c2ecf20Sopenharmony_ci pch_base, PCH_PCR_GPIO_1_BASE); 10868c2ecf20Sopenharmony_ci if (uv_pch_init_enable) 10878c2ecf20Sopenharmony_ci uv_init_hubless_pch_d0(); 10888c2ecf20Sopenharmony_ci uv_init_hubless_pch_io(GPI_NMI_ENA_GPP_D_0, 10898c2ecf20Sopenharmony_ci STS_GPP_D_0_MASK, STS_GPP_D_0_MASK); 10908c2ecf20Sopenharmony_ci uv_nmi_setup_hubless_intr(); 10918c2ecf20Sopenharmony_ci /* Ensure NMI enabled in Processor Interface Reg: */ 10928c2ecf20Sopenharmony_ci uv_reassert_nmi(); 10938c2ecf20Sopenharmony_ci uv_register_nmi_notifier(); 10948c2ecf20Sopenharmony_ci pr_info("UV: PCH NMI enabled\n"); 10958c2ecf20Sopenharmony_ci} 1096