1*cef12ee5SLiu, Jinsong /****************************************************************************** 2*cef12ee5SLiu, Jinsong * mcelog.c 3*cef12ee5SLiu, Jinsong * Driver for receiving and transferring machine check error infomation 4*cef12ee5SLiu, Jinsong * 5*cef12ee5SLiu, Jinsong * Copyright (c) 2012 Intel Corporation 6*cef12ee5SLiu, Jinsong * Author: Liu, Jinsong <jinsong.liu@intel.com> 7*cef12ee5SLiu, Jinsong * Author: Jiang, Yunhong <yunhong.jiang@intel.com> 8*cef12ee5SLiu, Jinsong * Author: Ke, Liping <liping.ke@intel.com> 9*cef12ee5SLiu, Jinsong * 10*cef12ee5SLiu, Jinsong * This program is free software; you can redistribute it and/or 11*cef12ee5SLiu, Jinsong * modify it under the terms of the GNU General Public License version 2 12*cef12ee5SLiu, Jinsong * as published by the Free Software Foundation; or, when distributed 13*cef12ee5SLiu, Jinsong * separately from the Linux kernel or incorporated into other 14*cef12ee5SLiu, Jinsong * software packages, subject to the following license: 15*cef12ee5SLiu, Jinsong * 16*cef12ee5SLiu, Jinsong * Permission is hereby granted, free of charge, to any person obtaining a copy 17*cef12ee5SLiu, Jinsong * of this source file (the "Software"), to deal in the Software without 18*cef12ee5SLiu, Jinsong * restriction, including without limitation the rights to use, copy, modify, 19*cef12ee5SLiu, Jinsong * merge, publish, distribute, sublicense, and/or sell copies of the Software, 20*cef12ee5SLiu, Jinsong * and to permit persons to whom the Software is furnished to do so, subject to 21*cef12ee5SLiu, Jinsong * the following conditions: 22*cef12ee5SLiu, Jinsong * 23*cef12ee5SLiu, Jinsong * The above copyright notice and this permission notice shall be included in 24*cef12ee5SLiu, Jinsong * all copies or substantial portions of the Software. 25*cef12ee5SLiu, Jinsong * 26*cef12ee5SLiu, Jinsong * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27*cef12ee5SLiu, Jinsong * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28*cef12ee5SLiu, Jinsong * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29*cef12ee5SLiu, Jinsong * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30*cef12ee5SLiu, Jinsong * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 31*cef12ee5SLiu, Jinsong * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 32*cef12ee5SLiu, Jinsong * IN THE SOFTWARE. 33*cef12ee5SLiu, Jinsong */ 34*cef12ee5SLiu, Jinsong 35*cef12ee5SLiu, Jinsong #include <linux/init.h> 36*cef12ee5SLiu, Jinsong #include <linux/types.h> 37*cef12ee5SLiu, Jinsong #include <linux/kernel.h> 38*cef12ee5SLiu, Jinsong #include <linux/slab.h> 39*cef12ee5SLiu, Jinsong #include <linux/fs.h> 40*cef12ee5SLiu, Jinsong #include <linux/device.h> 41*cef12ee5SLiu, Jinsong #include <linux/miscdevice.h> 42*cef12ee5SLiu, Jinsong #include <linux/uaccess.h> 43*cef12ee5SLiu, Jinsong #include <linux/capability.h> 44*cef12ee5SLiu, Jinsong 45*cef12ee5SLiu, Jinsong #include <xen/interface/xen.h> 46*cef12ee5SLiu, Jinsong #include <xen/events.h> 47*cef12ee5SLiu, Jinsong #include <xen/interface/vcpu.h> 48*cef12ee5SLiu, Jinsong #include <xen/xen.h> 49*cef12ee5SLiu, Jinsong #include <asm/xen/hypercall.h> 50*cef12ee5SLiu, Jinsong #include <asm/xen/hypervisor.h> 51*cef12ee5SLiu, Jinsong 52*cef12ee5SLiu, Jinsong #define XEN_MCELOG "xen_mcelog: " 53*cef12ee5SLiu, Jinsong 54*cef12ee5SLiu, Jinsong static struct mc_info g_mi; 55*cef12ee5SLiu, Jinsong static struct mcinfo_logical_cpu *g_physinfo; 56*cef12ee5SLiu, Jinsong static uint32_t ncpus; 57*cef12ee5SLiu, Jinsong 58*cef12ee5SLiu, Jinsong static DEFINE_SPINLOCK(mcelog_lock); 59*cef12ee5SLiu, Jinsong 60*cef12ee5SLiu, Jinsong static struct xen_mce_log xen_mcelog = { 61*cef12ee5SLiu, Jinsong .signature = XEN_MCE_LOG_SIGNATURE, 62*cef12ee5SLiu, Jinsong .len = XEN_MCE_LOG_LEN, 63*cef12ee5SLiu, Jinsong .recordlen = sizeof(struct xen_mce), 64*cef12ee5SLiu, Jinsong }; 65*cef12ee5SLiu, Jinsong 66*cef12ee5SLiu, Jinsong static DEFINE_SPINLOCK(xen_mce_chrdev_state_lock); 67*cef12ee5SLiu, Jinsong static int xen_mce_chrdev_open_count; /* #times opened */ 68*cef12ee5SLiu, Jinsong static int xen_mce_chrdev_open_exclu; /* already open exclusive? */ 69*cef12ee5SLiu, Jinsong 70*cef12ee5SLiu, Jinsong static int xen_mce_chrdev_open(struct inode *inode, struct file *file) 71*cef12ee5SLiu, Jinsong { 72*cef12ee5SLiu, Jinsong spin_lock(&xen_mce_chrdev_state_lock); 73*cef12ee5SLiu, Jinsong 74*cef12ee5SLiu, Jinsong if (xen_mce_chrdev_open_exclu || 75*cef12ee5SLiu, Jinsong (xen_mce_chrdev_open_count && (file->f_flags & O_EXCL))) { 76*cef12ee5SLiu, Jinsong spin_unlock(&xen_mce_chrdev_state_lock); 77*cef12ee5SLiu, Jinsong 78*cef12ee5SLiu, Jinsong return -EBUSY; 79*cef12ee5SLiu, Jinsong } 80*cef12ee5SLiu, Jinsong 81*cef12ee5SLiu, Jinsong if (file->f_flags & O_EXCL) 82*cef12ee5SLiu, Jinsong xen_mce_chrdev_open_exclu = 1; 83*cef12ee5SLiu, Jinsong xen_mce_chrdev_open_count++; 84*cef12ee5SLiu, Jinsong 85*cef12ee5SLiu, Jinsong spin_unlock(&xen_mce_chrdev_state_lock); 86*cef12ee5SLiu, Jinsong 87*cef12ee5SLiu, Jinsong return nonseekable_open(inode, file); 88*cef12ee5SLiu, Jinsong } 89*cef12ee5SLiu, Jinsong 90*cef12ee5SLiu, Jinsong static int xen_mce_chrdev_release(struct inode *inode, struct file *file) 91*cef12ee5SLiu, Jinsong { 92*cef12ee5SLiu, Jinsong spin_lock(&xen_mce_chrdev_state_lock); 93*cef12ee5SLiu, Jinsong 94*cef12ee5SLiu, Jinsong xen_mce_chrdev_open_count--; 95*cef12ee5SLiu, Jinsong xen_mce_chrdev_open_exclu = 0; 96*cef12ee5SLiu, Jinsong 97*cef12ee5SLiu, Jinsong spin_unlock(&xen_mce_chrdev_state_lock); 98*cef12ee5SLiu, Jinsong 99*cef12ee5SLiu, Jinsong return 0; 100*cef12ee5SLiu, Jinsong } 101*cef12ee5SLiu, Jinsong 102*cef12ee5SLiu, Jinsong static ssize_t xen_mce_chrdev_read(struct file *filp, char __user *ubuf, 103*cef12ee5SLiu, Jinsong size_t usize, loff_t *off) 104*cef12ee5SLiu, Jinsong { 105*cef12ee5SLiu, Jinsong char __user *buf = ubuf; 106*cef12ee5SLiu, Jinsong unsigned num; 107*cef12ee5SLiu, Jinsong int i, err; 108*cef12ee5SLiu, Jinsong 109*cef12ee5SLiu, Jinsong spin_lock(&mcelog_lock); 110*cef12ee5SLiu, Jinsong 111*cef12ee5SLiu, Jinsong num = xen_mcelog.next; 112*cef12ee5SLiu, Jinsong 113*cef12ee5SLiu, Jinsong /* Only supports full reads right now */ 114*cef12ee5SLiu, Jinsong err = -EINVAL; 115*cef12ee5SLiu, Jinsong if (*off != 0 || usize < XEN_MCE_LOG_LEN*sizeof(struct xen_mce)) 116*cef12ee5SLiu, Jinsong goto out; 117*cef12ee5SLiu, Jinsong 118*cef12ee5SLiu, Jinsong err = 0; 119*cef12ee5SLiu, Jinsong for (i = 0; i < num; i++) { 120*cef12ee5SLiu, Jinsong struct xen_mce *m = &xen_mcelog.entry[i]; 121*cef12ee5SLiu, Jinsong 122*cef12ee5SLiu, Jinsong err |= copy_to_user(buf, m, sizeof(*m)); 123*cef12ee5SLiu, Jinsong buf += sizeof(*m); 124*cef12ee5SLiu, Jinsong } 125*cef12ee5SLiu, Jinsong 126*cef12ee5SLiu, Jinsong memset(xen_mcelog.entry, 0, num * sizeof(struct xen_mce)); 127*cef12ee5SLiu, Jinsong xen_mcelog.next = 0; 128*cef12ee5SLiu, Jinsong 129*cef12ee5SLiu, Jinsong if (err) 130*cef12ee5SLiu, Jinsong err = -EFAULT; 131*cef12ee5SLiu, Jinsong 132*cef12ee5SLiu, Jinsong out: 133*cef12ee5SLiu, Jinsong spin_unlock(&mcelog_lock); 134*cef12ee5SLiu, Jinsong 135*cef12ee5SLiu, Jinsong return err ? err : buf - ubuf; 136*cef12ee5SLiu, Jinsong } 137*cef12ee5SLiu, Jinsong 138*cef12ee5SLiu, Jinsong static long xen_mce_chrdev_ioctl(struct file *f, unsigned int cmd, 139*cef12ee5SLiu, Jinsong unsigned long arg) 140*cef12ee5SLiu, Jinsong { 141*cef12ee5SLiu, Jinsong int __user *p = (int __user *)arg; 142*cef12ee5SLiu, Jinsong 143*cef12ee5SLiu, Jinsong if (!capable(CAP_SYS_ADMIN)) 144*cef12ee5SLiu, Jinsong return -EPERM; 145*cef12ee5SLiu, Jinsong 146*cef12ee5SLiu, Jinsong switch (cmd) { 147*cef12ee5SLiu, Jinsong case MCE_GET_RECORD_LEN: 148*cef12ee5SLiu, Jinsong return put_user(sizeof(struct xen_mce), p); 149*cef12ee5SLiu, Jinsong case MCE_GET_LOG_LEN: 150*cef12ee5SLiu, Jinsong return put_user(XEN_MCE_LOG_LEN, p); 151*cef12ee5SLiu, Jinsong case MCE_GETCLEAR_FLAGS: { 152*cef12ee5SLiu, Jinsong unsigned flags; 153*cef12ee5SLiu, Jinsong 154*cef12ee5SLiu, Jinsong do { 155*cef12ee5SLiu, Jinsong flags = xen_mcelog.flags; 156*cef12ee5SLiu, Jinsong } while (cmpxchg(&xen_mcelog.flags, flags, 0) != flags); 157*cef12ee5SLiu, Jinsong 158*cef12ee5SLiu, Jinsong return put_user(flags, p); 159*cef12ee5SLiu, Jinsong } 160*cef12ee5SLiu, Jinsong default: 161*cef12ee5SLiu, Jinsong return -ENOTTY; 162*cef12ee5SLiu, Jinsong } 163*cef12ee5SLiu, Jinsong } 164*cef12ee5SLiu, Jinsong 165*cef12ee5SLiu, Jinsong static const struct file_operations xen_mce_chrdev_ops = { 166*cef12ee5SLiu, Jinsong .open = xen_mce_chrdev_open, 167*cef12ee5SLiu, Jinsong .release = xen_mce_chrdev_release, 168*cef12ee5SLiu, Jinsong .read = xen_mce_chrdev_read, 169*cef12ee5SLiu, Jinsong .unlocked_ioctl = xen_mce_chrdev_ioctl, 170*cef12ee5SLiu, Jinsong .llseek = no_llseek, 171*cef12ee5SLiu, Jinsong }; 172*cef12ee5SLiu, Jinsong 173*cef12ee5SLiu, Jinsong static struct miscdevice xen_mce_chrdev_device = { 174*cef12ee5SLiu, Jinsong MISC_MCELOG_MINOR, 175*cef12ee5SLiu, Jinsong "mcelog", 176*cef12ee5SLiu, Jinsong &xen_mce_chrdev_ops, 177*cef12ee5SLiu, Jinsong }; 178*cef12ee5SLiu, Jinsong 179*cef12ee5SLiu, Jinsong /* 180*cef12ee5SLiu, Jinsong * Caller should hold the mcelog_lock 181*cef12ee5SLiu, Jinsong */ 182*cef12ee5SLiu, Jinsong static void xen_mce_log(struct xen_mce *mce) 183*cef12ee5SLiu, Jinsong { 184*cef12ee5SLiu, Jinsong unsigned entry; 185*cef12ee5SLiu, Jinsong 186*cef12ee5SLiu, Jinsong entry = xen_mcelog.next; 187*cef12ee5SLiu, Jinsong 188*cef12ee5SLiu, Jinsong /* 189*cef12ee5SLiu, Jinsong * When the buffer fills up discard new entries. 190*cef12ee5SLiu, Jinsong * Assume that the earlier errors are the more 191*cef12ee5SLiu, Jinsong * interesting ones: 192*cef12ee5SLiu, Jinsong */ 193*cef12ee5SLiu, Jinsong if (entry >= XEN_MCE_LOG_LEN) { 194*cef12ee5SLiu, Jinsong set_bit(XEN_MCE_OVERFLOW, 195*cef12ee5SLiu, Jinsong (unsigned long *)&xen_mcelog.flags); 196*cef12ee5SLiu, Jinsong return; 197*cef12ee5SLiu, Jinsong } 198*cef12ee5SLiu, Jinsong 199*cef12ee5SLiu, Jinsong memcpy(xen_mcelog.entry + entry, mce, sizeof(struct xen_mce)); 200*cef12ee5SLiu, Jinsong 201*cef12ee5SLiu, Jinsong xen_mcelog.next++; 202*cef12ee5SLiu, Jinsong } 203*cef12ee5SLiu, Jinsong 204*cef12ee5SLiu, Jinsong static int convert_log(struct mc_info *mi) 205*cef12ee5SLiu, Jinsong { 206*cef12ee5SLiu, Jinsong struct mcinfo_common *mic; 207*cef12ee5SLiu, Jinsong struct mcinfo_global *mc_global; 208*cef12ee5SLiu, Jinsong struct mcinfo_bank *mc_bank; 209*cef12ee5SLiu, Jinsong struct xen_mce m; 210*cef12ee5SLiu, Jinsong uint32_t i; 211*cef12ee5SLiu, Jinsong 212*cef12ee5SLiu, Jinsong mic = NULL; 213*cef12ee5SLiu, Jinsong x86_mcinfo_lookup(&mic, mi, MC_TYPE_GLOBAL); 214*cef12ee5SLiu, Jinsong if (unlikely(!mic)) { 215*cef12ee5SLiu, Jinsong pr_warning(XEN_MCELOG "Failed to find global error info\n"); 216*cef12ee5SLiu, Jinsong return -ENODEV; 217*cef12ee5SLiu, Jinsong } 218*cef12ee5SLiu, Jinsong 219*cef12ee5SLiu, Jinsong memset(&m, 0, sizeof(struct xen_mce)); 220*cef12ee5SLiu, Jinsong 221*cef12ee5SLiu, Jinsong mc_global = (struct mcinfo_global *)mic; 222*cef12ee5SLiu, Jinsong m.mcgstatus = mc_global->mc_gstatus; 223*cef12ee5SLiu, Jinsong m.apicid = mc_global->mc_apicid; 224*cef12ee5SLiu, Jinsong 225*cef12ee5SLiu, Jinsong for (i = 0; i < ncpus; i++) 226*cef12ee5SLiu, Jinsong if (g_physinfo[i].mc_apicid == m.apicid) 227*cef12ee5SLiu, Jinsong break; 228*cef12ee5SLiu, Jinsong if (unlikely(i == ncpus)) { 229*cef12ee5SLiu, Jinsong pr_warning(XEN_MCELOG "Failed to match cpu with apicid %d\n", 230*cef12ee5SLiu, Jinsong m.apicid); 231*cef12ee5SLiu, Jinsong return -ENODEV; 232*cef12ee5SLiu, Jinsong } 233*cef12ee5SLiu, Jinsong 234*cef12ee5SLiu, Jinsong m.socketid = g_physinfo[i].mc_chipid; 235*cef12ee5SLiu, Jinsong m.cpu = m.extcpu = g_physinfo[i].mc_cpunr; 236*cef12ee5SLiu, Jinsong m.cpuvendor = (__u8)g_physinfo[i].mc_vendor; 237*cef12ee5SLiu, Jinsong m.mcgcap = g_physinfo[i].mc_msrvalues[__MC_MSR_MCGCAP].value; 238*cef12ee5SLiu, Jinsong 239*cef12ee5SLiu, Jinsong mic = NULL; 240*cef12ee5SLiu, Jinsong x86_mcinfo_lookup(&mic, mi, MC_TYPE_BANK); 241*cef12ee5SLiu, Jinsong if (unlikely(!mic)) { 242*cef12ee5SLiu, Jinsong pr_warning(XEN_MCELOG "Fail to find bank error info\n"); 243*cef12ee5SLiu, Jinsong return -ENODEV; 244*cef12ee5SLiu, Jinsong } 245*cef12ee5SLiu, Jinsong 246*cef12ee5SLiu, Jinsong do { 247*cef12ee5SLiu, Jinsong if ((!mic) || (mic->size == 0) || 248*cef12ee5SLiu, Jinsong (mic->type != MC_TYPE_GLOBAL && 249*cef12ee5SLiu, Jinsong mic->type != MC_TYPE_BANK && 250*cef12ee5SLiu, Jinsong mic->type != MC_TYPE_EXTENDED && 251*cef12ee5SLiu, Jinsong mic->type != MC_TYPE_RECOVERY)) 252*cef12ee5SLiu, Jinsong break; 253*cef12ee5SLiu, Jinsong 254*cef12ee5SLiu, Jinsong if (mic->type == MC_TYPE_BANK) { 255*cef12ee5SLiu, Jinsong mc_bank = (struct mcinfo_bank *)mic; 256*cef12ee5SLiu, Jinsong m.misc = mc_bank->mc_misc; 257*cef12ee5SLiu, Jinsong m.status = mc_bank->mc_status; 258*cef12ee5SLiu, Jinsong m.addr = mc_bank->mc_addr; 259*cef12ee5SLiu, Jinsong m.tsc = mc_bank->mc_tsc; 260*cef12ee5SLiu, Jinsong m.bank = mc_bank->mc_bank; 261*cef12ee5SLiu, Jinsong m.finished = 1; 262*cef12ee5SLiu, Jinsong /*log this record*/ 263*cef12ee5SLiu, Jinsong xen_mce_log(&m); 264*cef12ee5SLiu, Jinsong } 265*cef12ee5SLiu, Jinsong mic = x86_mcinfo_next(mic); 266*cef12ee5SLiu, Jinsong } while (1); 267*cef12ee5SLiu, Jinsong 268*cef12ee5SLiu, Jinsong return 0; 269*cef12ee5SLiu, Jinsong } 270*cef12ee5SLiu, Jinsong 271*cef12ee5SLiu, Jinsong static int mc_queue_handle(uint32_t flags) 272*cef12ee5SLiu, Jinsong { 273*cef12ee5SLiu, Jinsong struct xen_mc mc_op; 274*cef12ee5SLiu, Jinsong int ret = 0; 275*cef12ee5SLiu, Jinsong 276*cef12ee5SLiu, Jinsong mc_op.cmd = XEN_MC_fetch; 277*cef12ee5SLiu, Jinsong mc_op.interface_version = XEN_MCA_INTERFACE_VERSION; 278*cef12ee5SLiu, Jinsong set_xen_guest_handle(mc_op.u.mc_fetch.data, &g_mi); 279*cef12ee5SLiu, Jinsong do { 280*cef12ee5SLiu, Jinsong mc_op.u.mc_fetch.flags = flags; 281*cef12ee5SLiu, Jinsong ret = HYPERVISOR_mca(&mc_op); 282*cef12ee5SLiu, Jinsong if (ret) { 283*cef12ee5SLiu, Jinsong pr_err(XEN_MCELOG "Failed to fetch %s error log\n", 284*cef12ee5SLiu, Jinsong (flags == XEN_MC_URGENT) ? 285*cef12ee5SLiu, Jinsong "urgnet" : "nonurgent"); 286*cef12ee5SLiu, Jinsong break; 287*cef12ee5SLiu, Jinsong } 288*cef12ee5SLiu, Jinsong 289*cef12ee5SLiu, Jinsong if (mc_op.u.mc_fetch.flags & XEN_MC_NODATA || 290*cef12ee5SLiu, Jinsong mc_op.u.mc_fetch.flags & XEN_MC_FETCHFAILED) 291*cef12ee5SLiu, Jinsong break; 292*cef12ee5SLiu, Jinsong else { 293*cef12ee5SLiu, Jinsong ret = convert_log(&g_mi); 294*cef12ee5SLiu, Jinsong if (ret) 295*cef12ee5SLiu, Jinsong pr_warning(XEN_MCELOG 296*cef12ee5SLiu, Jinsong "Failed to convert this error log, " 297*cef12ee5SLiu, Jinsong "continue acking it anyway\n"); 298*cef12ee5SLiu, Jinsong 299*cef12ee5SLiu, Jinsong mc_op.u.mc_fetch.flags = flags | XEN_MC_ACK; 300*cef12ee5SLiu, Jinsong ret = HYPERVISOR_mca(&mc_op); 301*cef12ee5SLiu, Jinsong if (ret) { 302*cef12ee5SLiu, Jinsong pr_err(XEN_MCELOG 303*cef12ee5SLiu, Jinsong "Failed to ack previous error log\n"); 304*cef12ee5SLiu, Jinsong break; 305*cef12ee5SLiu, Jinsong } 306*cef12ee5SLiu, Jinsong } 307*cef12ee5SLiu, Jinsong } while (1); 308*cef12ee5SLiu, Jinsong 309*cef12ee5SLiu, Jinsong return ret; 310*cef12ee5SLiu, Jinsong } 311*cef12ee5SLiu, Jinsong 312*cef12ee5SLiu, Jinsong /* virq handler for machine check error info*/ 313*cef12ee5SLiu, Jinsong static irqreturn_t xen_mce_interrupt(int irq, void *dev_id) 314*cef12ee5SLiu, Jinsong { 315*cef12ee5SLiu, Jinsong int err; 316*cef12ee5SLiu, Jinsong unsigned long tmp; 317*cef12ee5SLiu, Jinsong 318*cef12ee5SLiu, Jinsong spin_lock_irqsave(&mcelog_lock, tmp); 319*cef12ee5SLiu, Jinsong 320*cef12ee5SLiu, Jinsong /* urgent mc_info */ 321*cef12ee5SLiu, Jinsong err = mc_queue_handle(XEN_MC_URGENT); 322*cef12ee5SLiu, Jinsong if (err) 323*cef12ee5SLiu, Jinsong pr_err(XEN_MCELOG 324*cef12ee5SLiu, Jinsong "Failed to handle urgent mc_info queue, " 325*cef12ee5SLiu, Jinsong "continue handling nonurgent mc_info queue anyway.\n"); 326*cef12ee5SLiu, Jinsong 327*cef12ee5SLiu, Jinsong /* nonurgent mc_info */ 328*cef12ee5SLiu, Jinsong err = mc_queue_handle(XEN_MC_NONURGENT); 329*cef12ee5SLiu, Jinsong if (err) 330*cef12ee5SLiu, Jinsong pr_err(XEN_MCELOG 331*cef12ee5SLiu, Jinsong "Failed to handle nonurgent mc_info queue.\n"); 332*cef12ee5SLiu, Jinsong 333*cef12ee5SLiu, Jinsong spin_unlock_irqrestore(&mcelog_lock, tmp); 334*cef12ee5SLiu, Jinsong 335*cef12ee5SLiu, Jinsong return IRQ_HANDLED; 336*cef12ee5SLiu, Jinsong } 337*cef12ee5SLiu, Jinsong 338*cef12ee5SLiu, Jinsong static int bind_virq_for_mce(void) 339*cef12ee5SLiu, Jinsong { 340*cef12ee5SLiu, Jinsong int ret; 341*cef12ee5SLiu, Jinsong struct xen_mc mc_op; 342*cef12ee5SLiu, Jinsong 343*cef12ee5SLiu, Jinsong memset(&mc_op, 0, sizeof(struct xen_mc)); 344*cef12ee5SLiu, Jinsong 345*cef12ee5SLiu, Jinsong /* Fetch physical CPU Numbers */ 346*cef12ee5SLiu, Jinsong mc_op.cmd = XEN_MC_physcpuinfo; 347*cef12ee5SLiu, Jinsong mc_op.interface_version = XEN_MCA_INTERFACE_VERSION; 348*cef12ee5SLiu, Jinsong set_xen_guest_handle(mc_op.u.mc_physcpuinfo.info, g_physinfo); 349*cef12ee5SLiu, Jinsong ret = HYPERVISOR_mca(&mc_op); 350*cef12ee5SLiu, Jinsong if (ret) { 351*cef12ee5SLiu, Jinsong pr_err(XEN_MCELOG "Failed to get CPU numbers\n"); 352*cef12ee5SLiu, Jinsong return ret; 353*cef12ee5SLiu, Jinsong } 354*cef12ee5SLiu, Jinsong 355*cef12ee5SLiu, Jinsong /* Fetch each CPU Physical Info for later reference*/ 356*cef12ee5SLiu, Jinsong ncpus = mc_op.u.mc_physcpuinfo.ncpus; 357*cef12ee5SLiu, Jinsong g_physinfo = kcalloc(ncpus, sizeof(struct mcinfo_logical_cpu), 358*cef12ee5SLiu, Jinsong GFP_KERNEL); 359*cef12ee5SLiu, Jinsong if (!g_physinfo) 360*cef12ee5SLiu, Jinsong return -ENOMEM; 361*cef12ee5SLiu, Jinsong set_xen_guest_handle(mc_op.u.mc_physcpuinfo.info, g_physinfo); 362*cef12ee5SLiu, Jinsong ret = HYPERVISOR_mca(&mc_op); 363*cef12ee5SLiu, Jinsong if (ret) { 364*cef12ee5SLiu, Jinsong pr_err(XEN_MCELOG "Failed to get CPU info\n"); 365*cef12ee5SLiu, Jinsong kfree(g_physinfo); 366*cef12ee5SLiu, Jinsong return ret; 367*cef12ee5SLiu, Jinsong } 368*cef12ee5SLiu, Jinsong 369*cef12ee5SLiu, Jinsong ret = bind_virq_to_irqhandler(VIRQ_MCA, 0, 370*cef12ee5SLiu, Jinsong xen_mce_interrupt, 0, "mce", NULL); 371*cef12ee5SLiu, Jinsong if (ret < 0) { 372*cef12ee5SLiu, Jinsong pr_err(XEN_MCELOG "Failed to bind virq\n"); 373*cef12ee5SLiu, Jinsong kfree(g_physinfo); 374*cef12ee5SLiu, Jinsong return ret; 375*cef12ee5SLiu, Jinsong } 376*cef12ee5SLiu, Jinsong 377*cef12ee5SLiu, Jinsong return 0; 378*cef12ee5SLiu, Jinsong } 379*cef12ee5SLiu, Jinsong 380*cef12ee5SLiu, Jinsong static int __init xen_late_init_mcelog(void) 381*cef12ee5SLiu, Jinsong { 382*cef12ee5SLiu, Jinsong /* Only DOM0 is responsible for MCE logging */ 383*cef12ee5SLiu, Jinsong if (xen_initial_domain()) { 384*cef12ee5SLiu, Jinsong /* register character device /dev/mcelog for xen mcelog */ 385*cef12ee5SLiu, Jinsong if (misc_register(&xen_mce_chrdev_device)) 386*cef12ee5SLiu, Jinsong return -ENODEV; 387*cef12ee5SLiu, Jinsong return bind_virq_for_mce(); 388*cef12ee5SLiu, Jinsong } 389*cef12ee5SLiu, Jinsong 390*cef12ee5SLiu, Jinsong return -ENODEV; 391*cef12ee5SLiu, Jinsong } 392*cef12ee5SLiu, Jinsong device_initcall(xen_late_init_mcelog); 393