x86: MCA support.

Signed-off-by: Christoph Egger <Christoph.Egger@amd.com>
This commit is contained in:
Keir Fraser committed 2008-07-04 16:27:44 +01:00
1 parent b12217e141
commit 355b0469a8
29 files changed
+2018 -87

No files matched your search

+3
View File
@@ -1,4 +1,7 @@
obj-y += amd_nonfatal.o
obj-y += k7.o
obj-y += amd_k8.o
obj-y += amd_f10.o
obj-y += mce.o
obj-y += non-fatal.o
obj-y += p4.o
+131
View File
@@ -0,0 +1,131 @@
/*
* MCA implementation for AMD Family10 CPUs
* Copyright (c) 2007 Advanced Micro Devices, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* K8 common MCA documentation published at
*
* AMD64 Architecture Programmer's Manual Volume 2:
* System Programming
* Publication # 24593 Revision: 3.12
* Issue Date: September 2006
*/
/* Family10 MCA documentation published at
*
* BIOS and Kernel Developer's Guide
* For AMD Family 10h Processors
* Publication # 31116 Revision: 1.08
* Isse Date: June 10, 2007
*/
#include <xen/init.h>
#include <xen/types.h>
#include <xen/kernel.h>
#include <xen/config.h>
#include <xen/smp.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/msr.h>
#include "mce.h"
#include "x86_mca.h"
static int amd_f10_handler(struct mc_info *mi, uint16_t bank, uint64_t status)
{
struct mcinfo_extended mc_ext;
/* Family 0x10 introduced additional MSR that belong to the
* northbridge bank (4). */
if (bank != 4)
return 0;
if (!(status & MCi_STATUS_VAL))
return 0;
if (!(status & MCi_STATUS_MISCV))
return 0;
memset(&mc_ext, 0, sizeof(mc_ext));
mc_ext.common.type = MC_TYPE_EXTENDED;
mc_ext.common.size = sizeof(mc_ext);
mc_ext.mc_msrs = 3;
mc_ext.mc_msr[0].reg = MSR_F10_MC4_MISC1;
mc_ext.mc_msr[1].reg = MSR_F10_MC4_MISC2;
mc_ext.mc_msr[2].reg = MSR_F10_MC4_MISC3;
rdmsrl(MSR_F10_MC4_MISC1, mc_ext.mc_msr[0].value);
rdmsrl(MSR_F10_MC4_MISC2, mc_ext.mc_msr[1].value);
rdmsrl(MSR_F10_MC4_MISC3, mc_ext.mc_msr[2].value);
x86_mcinfo_add(mi, &mc_ext);
return 1;
}
extern void k8_machine_check(struct cpu_user_regs *regs, long error_code);
/* AMD Family10 machine check */
void amd_f10_mcheck_init(struct cpuinfo_x86 *c)
{
uint64_t value;
uint32_t i;
int cpu_nr;
machine_check_vector = k8_machine_check;
mc_callback_bank_extended = amd_f10_handler;
cpu_nr = smp_processor_id();
wmb();
rdmsrl(MSR_IA32_MCG_CAP, value);
if (value & MCG_CTL_P) /* Control register present ? */
wrmsrl (MSR_IA32_MCG_CTL, 0xffffffffffffffffULL);
nr_mce_banks = value & MCG_CAP_COUNT;
for (i = 0; i < nr_mce_banks; i++) {
switch (i) {
case 4: /* Northbridge */
/* Enable error reporting of all errors,
* enable error checking and
* disable sync flooding */
wrmsrl(MSR_IA32_MC4_CTL, 0x02c3c008ffffffffULL);
wrmsrl(MSR_IA32_MC4_STATUS, 0x0ULL);
/* XXX: We should write the value 0x1087821UL into
* to register F3x180 here, which sits in
* the PCI extended configuration space.
* Since this is not possible here, we can only hope,
* Dom0 is doing that.
*/
break;
default:
/* Enable error reporting of all errors */
wrmsrl(MSR_IA32_MC0_CTL + 4 * i, 0xffffffffffffffffULL);
wrmsrl(MSR_IA32_MC0_STATUS + 4 * i, 0x0ULL);
break;
}
}
set_in_cr4(X86_CR4_MCE);
printk("CPU%i: AMD Family10h machine check reporting enabled.\n", cpu_nr);
}
+324
View File
@@ -0,0 +1,324 @@
/*
* MCA implementation for AMD K8 CPUs
* Copyright (c) 2007 Advanced Micro Devices, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* K8 common MCA documentation published at
*
* AMD64 Architecture Programmer's Manual Volume 2:
* System Programming
* Publication # 24593 Revision: 3.12
* Issue Date: September 2006
*
* URL:
* http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/24593.pdf
*/
/* The related documentation for K8 Revisions A - E is:
*
* BIOS and Kernel Developer's Guide for
* AMD Athlon 64 and AMD Opteron Processors
* Publication # 26094 Revision: 3.30
* Issue Date: February 2006
*
* URL:
* http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/26094.PDF
*/
/* The related documentation for K8 Revisions F - G is:
*
* BIOS and Kernel Developer's Guide for
* AMD NPT Family 0Fh Processors
* Publication # 32559 Revision: 3.04
* Issue Date: December 2006
*
* URL:
* http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/32559.pdf
*/
#include <xen/config.h>
#include <xen/init.h>
#include <xen/types.h>
#include <xen/kernel.h>
#include <xen/smp.h>
#include <xen/sched.h>
#include <xen/sched-if.h>
#include <xen/softirq.h>
#include <asm/processor.h>
#include <asm/shared.h>
#include <asm/system.h>
#include <asm/msr.h>
#include "mce.h"
#include "x86_mca.h"
/* Machine Check Handler for AMD K8 family series */
void k8_machine_check(struct cpu_user_regs *regs, long error_code)
{
struct vcpu *vcpu = current;
struct domain *curdom;
struct mc_info *mc_data;
struct mcinfo_global mc_global;
struct mcinfo_bank mc_info;
uint64_t status, addrv, miscv, uc;
uint32_t i;
unsigned int cpu_nr;
uint32_t xen_impacted = 0;
#define DOM_NORMAL 0
#define DOM0_TRAP 1
#define DOMU_TRAP 2
#define DOMU_KILLED 4
uint32_t dom_state = DOM_NORMAL;
/* This handler runs as interrupt gate. So IPIs from the
* polling service routine are defered until we finished.
*/
/* Disable interrupts for the _vcpu_. It may not re-scheduled to
* an other physical CPU or the impacted process in the guest
* continues running with corrupted data, otherwise. */
vcpu_schedule_lock_irq(vcpu);
mc_data = x86_mcinfo_getptr();
cpu_nr = smp_processor_id();
curdom = vcpu->domain;
memset(&mc_global, 0, sizeof(mc_global));
mc_global.common.type = MC_TYPE_GLOBAL;
mc_global.common.size = sizeof(mc_global);
mc_global.mc_domid = curdom->domain_id; /* impacted domain */
mc_global.mc_coreid = vcpu->processor; /* impacted physical cpu */
BUG_ON(cpu_nr != vcpu->processor);
mc_global.mc_core_threadid = 0;
mc_global.mc_vcpuid = vcpu->vcpu_id; /* impacted vcpu */
#if 0 /* TODO: on which socket is this physical core?
It's not clear to me how to figure this out. */
mc_global.mc_socketid = ???;
#endif
mc_global.mc_flags |= MC_FLAG_UNCORRECTABLE;
rdmsrl(MSR_IA32_MCG_STATUS, mc_global.mc_gstatus);
/* Quick check, who is impacted */
xen_impacted = is_idle_domain(curdom);
/* Dom0 */
x86_mcinfo_clear(mc_data);
x86_mcinfo_add(mc_data, &mc_global);
for (i = 0; i < nr_mce_banks; i++) {
struct domain *d;
rdmsrl(MSR_IA32_MC0_STATUS + 4 * i, status);
if (!(status & MCi_STATUS_VAL))
continue;
/* An error happened in this bank.
* This is expected to be an uncorrectable error,
* since correctable errors get polled.
*/
uc = status & MCi_STATUS_UC;
memset(&mc_info, 0, sizeof(mc_info));
mc_info.common.type = MC_TYPE_BANK;
mc_info.common.size = sizeof(mc_info);
mc_info.mc_bank = i;
mc_info.mc_status = status;
addrv = 0;
if (status & MCi_STATUS_ADDRV) {
rdmsrl(MSR_IA32_MC0_ADDR + 4 * i, addrv);
d = maddr_get_owner(addrv);
if (d != NULL)
mc_info.mc_domid = d->domain_id;
}
miscv = 0;
if (status & MCi_STATUS_MISCV)
rdmsrl(MSR_IA32_MC0_MISC + 4 * i, miscv);
mc_info.mc_addr = addrv;
mc_info.mc_misc = miscv;
x86_mcinfo_add(mc_data, &mc_info); /* Dom0 */
if (mc_callback_bank_extended)
mc_callback_bank_extended(mc_data, i, status);
/* clear status */
wrmsrl(MSR_IA32_MC0_STATUS + 4 * i, 0x0ULL);
wmb();
add_taint(TAINT_MACHINE_CHECK);
}
status = mc_global.mc_gstatus;
/* clear MCIP or cpu enters shutdown state
* in case another MCE occurs. */
status &= ~MCG_STATUS_MCIP;
wrmsrl(MSR_IA32_MCG_STATUS, status);
wmb();
/* For the details see the discussion "MCE/MCA concept" on xen-devel.
* The thread started here:
* http://lists.xensource.com/archives/html/xen-devel/2007-05/msg01015.html
*/
/* MCG_STATUS_RIPV:
* When this bit is not set, then the instruction pointer onto the stack
* to resume at is not valid. If xen is interrupted, then we panic anyway
* right below. Otherwise it is up to the guest to figure out if
* guest kernel or guest userland is affected and should kill either
* itself or the affected process.
*/
/* MCG_STATUS_EIPV:
* Evaluation of EIPV is the job of the guest.
*/
if (xen_impacted) {
/* Now we are going to panic anyway. Allow interrupts, so that
* printk on serial console can work. */
vcpu_schedule_unlock_irq(vcpu);
/* Uh, that means, machine check exception
* inside Xen occured. */
printk("Machine check exception occured in Xen.\n");
/* if MCG_STATUS_EIPV indicates, the IP on the stack is related
* to the error then it makes sense to print a stack trace.
* That can be useful for more detailed error analysis and/or
* error case studies to figure out, if we can clear
* xen_impacted and kill a DomU instead
* (i.e. if a guest only control structure is affected, but then
* we must ensure the bad pages are not re-used again).
*/
if (status & MCG_STATUS_EIPV) {
printk("MCE: Instruction Pointer is related to the error. "
"Therefore, print the execution state.\n");
show_execution_state(regs);
}
x86_mcinfo_dump(mc_data);
panic("End of MCE. Use mcelog to decode above error codes.\n");
}
/* If Dom0 registered a machine check handler, which is only possible
* with a PV MCA driver, then ... */
if ( guest_has_trap_callback(dom0, 0, TRAP_machine_check) ) {
dom_state = DOM0_TRAP;
/* ... deliver machine check trap to Dom0. */
send_guest_trap(dom0, 0, TRAP_machine_check);
/* Xen may tell Dom0 now to notify the DomU.
* But this will happen through a hypercall. */
} else
/* Dom0 did not register a machine check handler, but if DomU
* did so, then... */
if ( guest_has_trap_callback(curdom, vcpu->vcpu_id, TRAP_machine_check) ) {
dom_state = DOMU_TRAP;
/* ... deliver machine check trap to DomU */
send_guest_trap(curdom, vcpu->vcpu_id, TRAP_machine_check);
} else {
/* hmm... noone feels responsible to handle the error.
* So, do a quick check if a DomU is impacted or not.
*/
if (curdom == dom0) {
/* Dom0 is impacted. Since noone can't handle
* this error, panic! */
x86_mcinfo_dump(mc_data);
panic("MCE occured in Dom0, which it can't handle\n");
/* UNREACHED */
} else {
dom_state = DOMU_KILLED;
/* Enable interrupts. This basically results in
* calling sti on the *physical* cpu. But after
* domain_crash() the vcpu pointer is invalid.
* Therefore, we must unlock the irqs before killing
* it. */
vcpu_schedule_unlock_irq(vcpu);
/* DomU is impacted. Kill it and continue. */
domain_crash(curdom);
}
}
switch (dom_state) {
case DOM0_TRAP:
case DOMU_TRAP:
/* Enable interrupts. */
vcpu_schedule_unlock_irq(vcpu);
/* guest softirqs and event callbacks are scheduled
* immediately after this handler exits. */
break;
case DOMU_KILLED:
/* Nothing to do here. */
break;
default:
BUG();
}
}
/* AMD K8 machine check */
void amd_k8_mcheck_init(struct cpuinfo_x86 *c)
{
uint64_t value;
uint32_t i;
int cpu_nr;
machine_check_vector = k8_machine_check;
cpu_nr = smp_processor_id();
wmb();
rdmsrl(MSR_IA32_MCG_CAP, value);
if (value & MCG_CTL_P) /* Control register present ? */
wrmsrl (MSR_IA32_MCG_CTL, 0xffffffffffffffffULL);
nr_mce_banks = value & MCG_CAP_COUNT;
for (i = 0; i < nr_mce_banks; i++) {
switch (i) {
case 4: /* Northbridge */
/* Enable error reporting of all errors,
* enable error checking and
* disable sync flooding */
wrmsrl(MSR_IA32_MC4_CTL, 0x02c3c008ffffffffULL);
wrmsrl(MSR_IA32_MC4_STATUS, 0x0ULL);
break;
default:
/* Enable error reporting of all errors */
wrmsrl(MSR_IA32_MC0_CTL + 4 * i, 0xffffffffffffffffULL);
wrmsrl(MSR_IA32_MC0_STATUS + 4 * i, 0x0ULL);
break;
}
}
set_in_cr4(X86_CR4_MCE);
printk("CPU%i: AMD K8 machine check reporting enabled.\n", cpu_nr);
}
+303
View File
@@ -0,0 +1,303 @@
/*
* MCA implementation for AMD CPUs
* Copyright (c) 2007 Advanced Micro Devices, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* K8 common MCA documentation published at
*
* AMD64 Architecture Programmer's Manual Volume 2:
* System Programming
* Publication # 24593 Revision: 3.12
* Issue Date: September 2006
*
* URL:
* http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/24593.pdf
*/
/* The related documentation for K8 Revisions A - E is:
*
* BIOS and Kernel Developer's Guide for
* AMD Athlon 64 and AMD Opteron Processors
* Publication # 26094 Revision: 3.30
* Issue Date: February 2006
*
* URL:
* http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/26094.PDF
*/
/* The related documentation for K8 Revisions F - G is:
*
* BIOS and Kernel Developer's Guide for
* AMD NPT Family 0Fh Processors
* Publication # 32559 Revision: 3.04
* Issue Date: December 2006
*
* URL:
* http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/32559.pdf
*/
#include <xen/config.h>
#include <xen/init.h>
#include <xen/types.h>
#include <xen/kernel.h>
#include <xen/smp.h>
#include <xen/timer.h>
#include <xen/event.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/msr.h>
#include "mce.h"
#include "x86_mca.h"
static struct timer mce_timer;
#define MCE_PERIOD MILLISECS(15000)
#define MCE_MIN MILLISECS(2000)
#define MCE_MAX MILLISECS(30000)
static s_time_t period = MCE_PERIOD;
static int hw_threshold = 0;
static int adjust = 0;
/* The polling service routine:
* Collects information of correctable errors and notifies
* Dom0 via an event.
*/
void mce_amd_checkregs(void *info)
{
struct vcpu *vcpu = current;
struct mc_info *mc_data;
struct mcinfo_global mc_global;
struct mcinfo_bank mc_info;
uint64_t status, addrv, miscv;
unsigned int i;
unsigned int event_enabled;
unsigned int cpu_nr;
int error_found;
/* We don't need a slot yet. Only allocate one on error. */
mc_data = NULL;
cpu_nr = smp_processor_id();
event_enabled = guest_enabled_event(dom0->vcpu[0], VIRQ_MCA);
error_found = 0;
memset(&mc_global, 0, sizeof(mc_global));
mc_global.common.type = MC_TYPE_GLOBAL;
mc_global.common.size = sizeof(mc_global);
mc_global.mc_domid = vcpu->domain->domain_id; /* impacted domain */
mc_global.mc_coreid = vcpu->processor; /* impacted physical cpu */
BUG_ON(cpu_nr != vcpu->processor);
mc_global.mc_core_threadid = 0;
mc_global.mc_vcpuid = vcpu->vcpu_id; /* impacted vcpu */
#if 0 /* TODO: on which socket is this physical core?
It's not clear to me how to figure this out. */
mc_global.mc_socketid = ???;
#endif
mc_global.mc_flags |= MC_FLAG_CORRECTABLE;
rdmsrl(MSR_IA32_MCG_STATUS, mc_global.mc_gstatus);
for (i = 0; i < nr_mce_banks; i++) {
struct domain *d;
rdmsrl(MSR_IA32_MC0_STATUS + i * 4, status);
if (!(status & MCi_STATUS_VAL))
continue;
if (mc_data == NULL) {
/* Now we need a slot to fill in error telemetry. */
mc_data = x86_mcinfo_getptr();
BUG_ON(mc_data == NULL);
x86_mcinfo_clear(mc_data);
x86_mcinfo_add(mc_data, &mc_global);
}
memset(&mc_info, 0, sizeof(mc_info));
mc_info.common.type = MC_TYPE_BANK;
mc_info.common.size = sizeof(mc_info);
mc_info.mc_bank = i;
mc_info.mc_status = status;
/* Increase polling frequency */
error_found = 1;
addrv = 0;
if (status & MCi_STATUS_ADDRV) {
rdmsrl(MSR_IA32_MC0_ADDR + i * 4, addrv);
d = maddr_get_owner(addrv);
if (d != NULL)
mc_info.mc_domid = d->domain_id;
}
miscv = 0;
if (status & MCi_STATUS_MISCV)
rdmsrl(MSR_IA32_MC0_MISC + i * 4, miscv);
mc_info.mc_addr = addrv;
mc_info.mc_misc = miscv;
x86_mcinfo_add(mc_data, &mc_info);
if (mc_callback_bank_extended)
mc_callback_bank_extended(mc_data, i, status);
/* clear status */
wrmsrl(MSR_IA32_MC0_STATUS + i * 4, 0x0ULL);
wmb();
}
if (error_found > 0) {
/* If Dom0 enabled the VIRQ_MCA event, then ... */
if (event_enabled)
/* ... notify it. */
send_guest_global_virq(dom0, VIRQ_MCA);
else
/* ... or dump it */
x86_mcinfo_dump(mc_data);
}
adjust += error_found;
}
/* polling service routine invoker:
* Adjust poll frequency at runtime. No error means slow polling frequency,
* an error means higher polling frequency.
* It uses hw threshold register introduced in AMD K8 RevF to detect
* multiple correctable errors between two polls. In that case,
* increase polling frequency higher than normal.
*/
static void mce_amd_work_fn(void *data)
{
on_each_cpu(mce_amd_checkregs, data, 1, 1);
if (adjust > 0) {
if ( !guest_enabled_event(dom0->vcpu[0], VIRQ_MCA) ) {
/* Dom0 did not enable VIRQ_MCA, so Xen is reporting. */
printk("MCE: polling routine found correctable error. "
" Use mcelog to parse above error output.\n");
}
}
if (hw_threshold) {
uint64_t value;
uint32_t counter;
rdmsrl(MSR_IA32_MC4_MISC, value);
/* Only the error counter field is of interest
* Bit field is described in AMD K8 BKDG chapter 6.4.5.5
*/
counter = (value & 0xFFF00000000ULL) >> 32U;
/* HW does not count *all* kinds of correctable errors.
* Thus it is possible, that the polling routine finds an
* correctable error even if the HW reports nothing.
* However, the other way around is not possible (= BUG).
*/
if (counter > 0) {
/* HW reported correctable errors,
* the polling routine did not find...
*/
BUG_ON(adjust == 0);
/* subtract 1 to not double count the error
* from the polling service routine */
adjust += (counter - 1);
/* Restart counter */
/* No interrupt, reset counter value */
value &= ~(0x60FFF00000000ULL);
/* Counter enable */
value |= (1ULL << 51);
wrmsrl(MSR_IA32_MC4_MISC, value);
wmb();
}
}
if (adjust > 0) {
/* Increase polling frequency */
adjust++; /* adjust == 1 must have an effect */
period /= adjust;
} else {
/* Decrease polling frequency */
period *= 2;
}
if (period > MCE_MAX) {
/* limit: Poll at least every 30s */
period = MCE_MAX;
}
if (period < MCE_MIN) {
/* limit: Poll every 2s.
* When this is reached an uncorrectable error
* is expected to happen, if Dom0 does nothing.
*/
period = MCE_MIN;
}
set_timer(&mce_timer, NOW() + period);
adjust = 0;
}
void amd_nonfatal_mcheck_init(struct cpuinfo_x86 *c)
{
if (c->x86_vendor != X86_VENDOR_AMD)
return;
/* Assume we are on K8 or newer AMD CPU here */
/* The threshold bitfields in MSR_IA32_MC4_MISC has
* been introduced along with the SVME feature bit. */
if (cpu_has(c, X86_FEATURE_SVME)) {
uint64_t value;
/* hw threshold registers present */
hw_threshold = 1;
rdmsrl(MSR_IA32_MC4_MISC, value);
if (value & (1ULL << 61)) { /* Locked bit */
/* Locked by BIOS. Not available for use */
hw_threshold = 0;
}
if (!(value & (1ULL << 63))) { /* Valid bit */
/* No CtrP present */
hw_threshold = 0;
} else {
if (!(value & (1ULL << 62))) { /* Counter Bit */
/* No counter field present */
hw_threshold = 0;
}
}
if (hw_threshold) {
/* No interrupt, reset counter value */
value &= ~(0x60FFF00000000ULL);
/* Counter enable */
value |= (1ULL << 51);
wrmsrl(MSR_IA32_MC4_MISC, value);
/* serialize */
wmb();
printk(XENLOG_INFO "MCA: Use hw thresholding to adjust polling frequency\n");
}
}
init_timer(&mce_timer, mce_amd_work_fn, NULL, 0);
set_timer(&mce_timer, NOW() + period);
return;
}
+3 -4
View File
@@ -66,8 +66,8 @@ static fastcall void k7_machine_check(struct cpu_user_regs * regs, long error_co
}
/* AMD K7 machine check is Intel like */
void amd_mcheck_init(struct cpuinfo_x86 *c)
/* AMD K7 machine check */
void amd_k7_mcheck_init(struct cpuinfo_x86 *c)
{
u32 l, h;
int i;
@@ -75,7 +75,6 @@ void amd_mcheck_init(struct cpuinfo_x86 *c)
machine_check_vector = k7_machine_check;
wmb();
printk (KERN_INFO "Intel machine check architecture supported.\n");
rdmsr (MSR_IA32_MCG_CAP, l, h);
if (l & (1<<8)) /* Control register present ? */
wrmsr (MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff);
@@ -90,6 +89,6 @@ void amd_mcheck_init(struct cpuinfo_x86 *c)
}
set_in_cr4 (X86_CR4_MCE);
printk (KERN_INFO "Intel machine check reporting enabled on CPU#%d.\n",
printk (KERN_INFO "CPU%d: AMD K7 machine check reporting enabled.\n",
smp_processor_id());
}
+521 -35
View File
@@ -8,73 +8,151 @@
#include <xen/kernel.h>
#include <xen/config.h>
#include <xen/smp.h>
#include <xen/errno.h>
#include <asm/processor.h>
#include <asm/system.h>
#include "mce.h"
#include "x86_mca.h"
int mce_disabled = 0;
int nr_mce_banks;
unsigned int nr_mce_banks;
EXPORT_SYMBOL_GPL(nr_mce_banks); /* non-fatal.o */
/* XXX For now a fixed array is used. Later this should be changed
* to a dynamic allocated array with the size calculated in relation
* to physical cpus present in the machine.
* The more physical cpus are available, the more entries you need.
*/
#define MAX_MCINFO 10
struct mc_machine_notify {
struct mc_info mc;
uint32_t fetch_idx;
uint32_t valid;
};
struct mc_machine {
/* Array structure used for collecting machine check error telemetry. */
struct mc_info mc[MAX_MCINFO];
/* We handle multiple machine check reports lockless by
* iterating through the array using the producer/consumer concept.
*/
/* Producer array index to fill with machine check error data.
* Index must be increased atomically. */
uint32_t error_idx;
/* Consumer array index to fetch machine check error data from.
* Index must be increased atomically. */
uint32_t fetch_idx;
/* Integer array holding the indeces of the mc array that allows
* a Dom0 to notify a DomU to re-fetch the same machine check error
* data. The notification and refetch also uses its own
* producer/consumer mechanism, because Dom0 may decide to not report
* every error to the impacted DomU.
*/
struct mc_machine_notify notify[MAX_MCINFO];
/* Array index to get fetch_idx from.
* Index must be increased atomically. */
uint32_t notifyproducer_idx;
uint32_t notifyconsumer_idx;
};
/* Global variable with machine check information. */
struct mc_machine mc_data;
/* Handle unconfigured int18 (should never happen) */
static fastcall void unexpected_machine_check(struct cpu_user_regs * regs, long error_code)
static void unexpected_machine_check(struct cpu_user_regs *regs, long error_code)
{
printk(KERN_ERR "CPU#%d: Unexpected int18 (Machine Check).\n", smp_processor_id());
printk(XENLOG_ERR "CPU#%d: Unexpected int18 (Machine Check).\n",
smp_processor_id());
}
/* Call the installed machine check handler for this CPU setup. */
void fastcall (*machine_check_vector)(struct cpu_user_regs *, long error_code) = unexpected_machine_check;
void (*machine_check_vector)(struct cpu_user_regs *regs, long error_code) = unexpected_machine_check;
/* Init machine check callback handler
* It is used to collect additional information provided by newer
* CPU families/models without the need to duplicate the whole handler.
* This avoids having many handlers doing almost nearly the same and each
* with its own tweaks ands bugs. */
int (*mc_callback_bank_extended)(struct mc_info *, uint16_t, uint64_t) = NULL;
static void amd_mcheck_init(struct cpuinfo_x86 *ci)
{
switch (ci->x86) {
case 6:
amd_k7_mcheck_init(ci);
break;
case 0xf:
amd_k8_mcheck_init(ci);
break;
case 0x10:
amd_f10_mcheck_init(ci);
break;
default:
/* Assume that machine check support is available.
* The minimum provided support is at least the K8. */
amd_k8_mcheck_init(ci);
}
}
/* This has to be run for each processor */
void mcheck_init(struct cpuinfo_x86 *c)
{
if (mce_disabled==1)
if (mce_disabled == 1) {
printk(XENLOG_INFO "MCE support disabled by bootparam\n");
return;
}
if (!cpu_has(c, X86_FEATURE_MCE)) {
printk(XENLOG_INFO "CPU%i: No machine check support available\n",
smp_processor_id());
return;
}
memset(&mc_data, 0, sizeof(struct mc_machine));
switch (c->x86_vendor) {
case X86_VENDOR_AMD:
amd_mcheck_init(c);
break;
case X86_VENDOR_AMD:
amd_mcheck_init(c);
break;
case X86_VENDOR_INTEL:
case X86_VENDOR_INTEL:
#ifndef CONFIG_X86_64
if (c->x86==5)
intel_p5_mcheck_init(c);
if (c->x86==6)
intel_p6_mcheck_init(c);
if (c->x86==5)
intel_p5_mcheck_init(c);
if (c->x86==6)
intel_p6_mcheck_init(c);
#endif
if (c->x86==15)
intel_p4_mcheck_init(c);
break;
if (c->x86==15)
intel_p4_mcheck_init(c);
break;
#ifndef CONFIG_X86_64
case X86_VENDOR_CENTAUR:
if (c->x86==5)
winchip_mcheck_init(c);
break;
case X86_VENDOR_CENTAUR:
if (c->x86==5)
winchip_mcheck_init(c);
break;
#endif
default:
break;
default:
break;
}
}
static unsigned long old_cr4 __initdata;
void __init stop_mce(void)
{
old_cr4 = read_cr4();
clear_in_cr4(X86_CR4_MCE);
}
void __init restart_mce(void)
{
if (old_cr4 & X86_CR4_MCE)
set_in_cr4(X86_CR4_MCE);
}
static void __init mcheck_disable(char *str)
{
@@ -88,3 +166,411 @@ static void __init mcheck_enable(char *str)
custom_param("nomce", mcheck_disable);
custom_param("mce", mcheck_enable);
#include <xen/guest_access.h>
#include <asm/traps.h>
struct mc_info *x86_mcinfo_getptr(void)
{
struct mc_info *mi;
uint32_t entry, next;
for (;;) {
entry = mc_data.error_idx;
smp_rmb();
next = entry + 1;
if (cmpxchg(&mc_data.error_idx, entry, next) == entry)
break;
}
mi = &(mc_data.mc[(entry % MAX_MCINFO)]);
BUG_ON(mc_data.error_idx < mc_data.fetch_idx);
return mi;
}
static int x86_mcinfo_matches_guest(const struct mc_info *mi,
const struct domain *d, const struct vcpu *v)
{
struct mcinfo_common *mic;
struct mcinfo_global *mig;
x86_mcinfo_lookup(mic, mi, MC_TYPE_GLOBAL);
mig = (struct mcinfo_global *)mic;
if (mig == NULL)
return 0;
if (d->domain_id != mig->mc_domid)
return 0;
if (v->vcpu_id != mig->mc_vcpuid)
return 0;
return 1;
}
#define x86_mcinfo_mcdata(idx) (mc_data.mc[(idx % MAX_MCINFO)])
static struct mc_info *x86_mcinfo_getfetchptr(uint32_t *fetch_idx,
const struct domain *d, const struct vcpu *v)
{
struct mc_info *mi;
/* This function is called from the fetch hypercall with
* the mc_lock spinlock held. Thus, no need for locking here.
*/
mi = &(x86_mcinfo_mcdata(mc_data.fetch_idx));
if ((d != dom0) && !x86_mcinfo_matches_guest(mi, d, v)) {
/* Bogus domU command detected. */
*fetch_idx = 0;
return NULL;
}
*fetch_idx = mc_data.fetch_idx;
mc_data.fetch_idx++;
BUG_ON(mc_data.fetch_idx > mc_data.error_idx);
return mi;
}
static void x86_mcinfo_marknotified(struct xen_mc_notifydomain *mc_notifydomain)
{
struct mc_machine_notify *mn;
struct mcinfo_common *mic = NULL;
struct mcinfo_global *mig;
struct domain *d;
int i;
/* This function is called from the notifier hypercall with
* the mc_notify_lock spinlock held. Thus, no need for locking here.
*/
/* First invalidate entries for guests that disappeared after
* notification (e.g. shutdown/crash). This step prevents the
* notification array from filling up with stalling/leaking entries.
*/
for (i = mc_data.notifyconsumer_idx; i < mc_data.notifyproducer_idx; i++) {
mn = &(mc_data.notify[(i % MAX_MCINFO)]);
x86_mcinfo_lookup(mic, &mn->mc, MC_TYPE_GLOBAL);
BUG_ON(mic == NULL);
mig = (struct mcinfo_global *)mic;
d = get_domain_by_id(mig->mc_domid);
if (d == NULL) {
/* Domain does not exist. */
mn->valid = 0;
}
if ((!mn->valid) && (i == mc_data.notifyconsumer_idx))
mc_data.notifyconsumer_idx++;
}
/* Now put in the error telemetry. Since all error data fetchable
* by domUs are uncorrectable errors, they are very important.
* So we dump them before overriding them. When a guest takes that long,
* then we can assume something bad already happened (crash, hang, etc.)
*/
mn = &(mc_data.notify[(mc_data.notifyproducer_idx % MAX_MCINFO)]);
if (mn->valid) {
struct mcinfo_common *mic = NULL;
struct mcinfo_global *mig;
/* To not loose the information, we dump it. */
x86_mcinfo_lookup(mic, &mn->mc, MC_TYPE_GLOBAL);
BUG_ON(mic == NULL);
mig = (struct mcinfo_global *)mic;
printk(XENLOG_WARNING "Domain ID %u was notified by Dom0 to "
"fetch machine check error telemetry. But Domain ID "
"did not do that in time.\n",
mig->mc_domid);
x86_mcinfo_dump(&mn->mc);
}
memcpy(&mn->mc, &(x86_mcinfo_mcdata(mc_notifydomain->fetch_idx)),
sizeof(struct mc_info));
mn->fetch_idx = mc_notifydomain->fetch_idx;
mn->valid = 1;
mc_data.notifyproducer_idx++;
/* By design there can never be more notifies than machine check errors.
* If that ever happens, then we hit a bug. */
BUG_ON(mc_data.notifyproducer_idx > mc_data.fetch_idx);
BUG_ON(mc_data.notifyconsumer_idx > mc_data.notifyproducer_idx);
}
static struct mc_info *x86_mcinfo_getnotifiedptr(uint32_t *fetch_idx,
const struct domain *d, const struct vcpu *v)
{
struct mc_machine_notify *mn = NULL;
uint32_t i;
int found;
/* This function is called from the fetch hypercall with
* the mc_notify_lock spinlock held. Thus, no need for locking here.
*/
/* The notifier data is filled in the order guests get notified, but
* guests may fetch them in a different order. That's why we need
* the game with valid/invalid entries. */
found = 0;
for (i = mc_data.notifyconsumer_idx; i < mc_data.notifyproducer_idx; i++) {
mn = &(mc_data.notify[(i % MAX_MCINFO)]);
if (!mn->valid) {
if (i == mc_data.notifyconsumer_idx)
mc_data.notifyconsumer_idx++;
continue;
}
if (x86_mcinfo_matches_guest(&mn->mc, d, v)) {
found = 1;
break;
}
}
if (!found) {
/* This domain has never been notified. This must be
* a bogus domU command. */
*fetch_idx = 0;
return NULL;
}
BUG_ON(mn == NULL);
*fetch_idx = mn->fetch_idx;
mn->valid = 0;
BUG_ON(mc_data.notifyconsumer_idx > mc_data.notifyproducer_idx);
return &mn->mc;
}
void x86_mcinfo_clear(struct mc_info *mi)
{
memset(mi, 0, sizeof(struct mc_info));
x86_mcinfo_nentries(mi) = 0;
}
int x86_mcinfo_add(struct mc_info *mi, void *mcinfo)
{
int i;
unsigned long end1, end2;
struct mcinfo_common *mic, *mic_base, *mic_index;
mic = (struct mcinfo_common *)mcinfo;
mic_index = mic_base = x86_mcinfo_first(mi);
/* go to first free entry */
for (i = 0; i < x86_mcinfo_nentries(mi); i++) {
mic_index = x86_mcinfo_next(mic_index);
}
/* check if there is enough size */
end1 = (unsigned long)((uint8_t *)mic_base + sizeof(struct mc_info));
end2 = (unsigned long)((uint8_t *)mic_index + mic->size);
if (end1 < end2)
return -ENOSPC; /* No space. Can't add entry. */
/* there's enough space. add entry. */
memcpy(mic_index, mic, mic->size);
x86_mcinfo_nentries(mi)++;
return 0;
}
/* Dump machine check information in a format,
* mcelog can parse. This is used only when
* Dom0 does not take the notification. */
void x86_mcinfo_dump(struct mc_info *mi)
{
struct mcinfo_common *mic = NULL;
struct mcinfo_global *mc_global;
struct mcinfo_bank *mc_bank;
/* first print the global info */
x86_mcinfo_lookup(mic, mi, MC_TYPE_GLOBAL);
if (mic == NULL)
return;
mc_global = (struct mcinfo_global *)mic;
if (mc_global->mc_flags & MC_FLAG_UNCORRECTABLE) {
printk(XENLOG_WARNING
"CPU%d: Machine Check Exception: %16"PRIx64"\n",
mc_global->mc_coreid, mc_global->mc_gstatus);
} else {
printk(XENLOG_WARNING "MCE: The hardware reports a non "
"fatal, correctable incident occured on "
"CPU %d.\n",
mc_global->mc_coreid);
}
/* then the bank information */
x86_mcinfo_lookup(mic, mi, MC_TYPE_BANK); /* finds the first entry */
do {
if (mic == NULL)
return;
if (mic->type != MC_TYPE_BANK)
continue;
mc_bank = (struct mcinfo_bank *)mic;
printk(XENLOG_WARNING "Bank %d: %16"PRIx64,
mc_bank->mc_bank,
mc_bank->mc_status);
if (mc_bank->mc_status & MCi_STATUS_MISCV)
printk("[%16"PRIx64"]", mc_bank->mc_misc);
if (mc_bank->mc_status & MCi_STATUS_ADDRV)
printk(" at %16"PRIx64, mc_bank->mc_addr);
printk("\n");
mic = x86_mcinfo_next(mic); /* next entry */
if ((mic == NULL) || (mic->size == 0))
break;
} while (1);
}
/* Machine Check Architecture Hypercall */
long do_mca(XEN_GUEST_HANDLE(xen_mc_t) u_xen_mc)
{
long ret = 0;
struct xen_mc curop, *op = &curop;
struct vcpu *v = current;
struct domain *domU;
struct xen_mc_fetch *mc_fetch;
struct xen_mc_notifydomain *mc_notifydomain;
struct mc_info *mi;
uint32_t flags;
uint32_t fetch_idx;
uint16_t vcpuid;
/* Use a different lock for the notify hypercall in order to allow
* a DomU to fetch mc data while Dom0 notifies another DomU. */
static DEFINE_SPINLOCK(mc_lock);
static DEFINE_SPINLOCK(mc_notify_lock);
if ( copy_from_guest(op, u_xen_mc, 1) )
return -EFAULT;
if ( op->interface_version != XEN_MCA_INTERFACE_VERSION )
return -EACCES;
switch ( op->cmd ) {
case XEN_MC_fetch:
/* This hypercall is for any domain */
mc_fetch = &op->u.mc_fetch;
switch (mc_fetch->flags) {
case XEN_MC_CORRECTABLE:
/* But polling mode is Dom0 only, because
* correctable errors are reported to Dom0 only */
if ( !IS_PRIV(v->domain) )
return -EPERM;
break;
case XEN_MC_TRAP:
break;
default:
return -EFAULT;
}
flags = XEN_MC_OK;
spin_lock(&mc_lock);
if ( IS_PRIV(v->domain) ) {
/* this must be Dom0. So a notify hypercall
* can't have happened before. */
mi = x86_mcinfo_getfetchptr(&fetch_idx, dom0, v);
} else {
/* Hypercall comes from an unprivileged domain */
domU = v->domain;
if (guest_has_trap_callback(dom0, 0, TRAP_machine_check)) {
/* Dom0 must have notified this DomU before
* via the notify hypercall. */
mi = x86_mcinfo_getnotifiedptr(&fetch_idx, domU, v);
} else {
/* Xen notified the DomU. */
mi = x86_mcinfo_getfetchptr(&fetch_idx, domU, v);
}
}
if (mi) {
memcpy(&mc_fetch->mc_info, mi,
sizeof(struct mc_info));
} else {
/* There is no data for a bogus DomU command. */
flags |= XEN_MC_NODATA;
memset(&mc_fetch->mc_info, 0, sizeof(struct mc_info));
}
mc_fetch->flags = flags;
mc_fetch->fetch_idx = fetch_idx;
if ( copy_to_guest(u_xen_mc, op, 1) )
ret = -EFAULT;
spin_unlock(&mc_lock);
break;
case XEN_MC_notifydomain:
/* This hypercall is for Dom0 only */
if ( !IS_PRIV(v->domain) )
return -EPERM;
spin_lock(&mc_notify_lock);
mc_notifydomain = &op->u.mc_notifydomain;
domU = get_domain_by_id(mc_notifydomain->mc_domid);
vcpuid = mc_notifydomain->mc_vcpuid;
if ((domU == NULL) || (domU == dom0)) {
/* It's not possible to notify a non-existent domain
* or the dom0. */
spin_unlock(&mc_notify_lock);
return -EACCES;
}
if (vcpuid >= MAX_VIRT_CPUS) {
/* It's not possible to notify a vcpu, Xen can't
* assign to a domain. */
spin_unlock(&mc_notify_lock);
return -EACCES;
}
mc_notifydomain->flags = XEN_MC_OK;
mi = &(x86_mcinfo_mcdata(mc_notifydomain->fetch_idx));
if (!x86_mcinfo_matches_guest(mi, domU, domU->vcpu[vcpuid])) {
/* The error telemetry is not for the guest, Dom0
* wants to notify. */
mc_notifydomain->flags |= XEN_MC_NOMATCH;
} else if ( guest_has_trap_callback(domU, vcpuid,
TRAP_machine_check) )
{
/* Send notification */
if ( send_guest_trap(domU, vcpuid, TRAP_machine_check) )
mc_notifydomain->flags |= XEN_MC_NOTDELIVERED;
} else
mc_notifydomain->flags |= XEN_MC_CANNOTHANDLE;
#ifdef DEBUG
/* sanity check - these two flags are mutually exclusive */
if ((flags & XEN_MC_CANNOTHANDLE) && (flags & XEN_MC_NOTDELIVERED))
BUG();
#endif
if ( copy_to_guest(u_xen_mc, op, 1) )
ret = -EFAULT;
if (ret == 0) {
x86_mcinfo_marknotified(mc_notifydomain);
}
spin_unlock(&mc_notify_lock);
break;
}
return ret;
}
+21 -5
View File
@@ -1,14 +1,30 @@
#include <xen/init.h>
#include <asm/traps.h>
void amd_mcheck_init(struct cpuinfo_x86 *c);
/* Init functions */
void amd_nonfatal_mcheck_init(struct cpuinfo_x86 *c);
void amd_k7_mcheck_init(struct cpuinfo_x86 *c);
void amd_k8_mcheck_init(struct cpuinfo_x86 *c);
void amd_f10_mcheck_init(struct cpuinfo_x86 *c);
void intel_p4_mcheck_init(struct cpuinfo_x86 *c);
void intel_p5_mcheck_init(struct cpuinfo_x86 *c);
void intel_p6_mcheck_init(struct cpuinfo_x86 *c);
void winchip_mcheck_init(struct cpuinfo_x86 *c);
/* Call the installed machine check handler for this CPU setup. */
extern fastcall void (*machine_check_vector)(struct cpu_user_regs *, long error_code);
/* Function pointer used in the handlers to collect additional information
* provided by newer CPU families/models without the need to duplicate
* the whole handler resulting in various handlers each with its own
* tweaks and bugs */
extern int (*mc_callback_bank_extended)(struct mc_info *mi,
uint16_t bank, uint64_t status);
/* Helper functions used for collecting error telemetry */
struct mc_info *x86_mcinfo_getptr(void);
void x86_mcinfo_clear(struct mc_info *mi);
int x86_mcinfo_add(struct mc_info *mi, void *mcinfo);
void x86_mcinfo_dump(struct mc_info *mi);
/* Global variables */
extern int mce_disabled __initdata;
extern int nr_mce_banks;
extern unsigned int nr_mce_banks;
+20 -10
View File
@@ -68,19 +68,29 @@ static int __init init_nonfatal_mce_checker(void)
if (!cpu_has(c, X86_FEATURE_MCA))
return -ENODEV;
/* Some Athlons misbehave when we frob bank 0 */
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
boot_cpu_data.x86 == 6)
firstbank = 1;
else
firstbank = 0;
/*
* Check for non-fatal errors every MCE_RATE s
*/
init_timer(&mce_timer, mce_work_fn, NULL, 0);
set_timer(&mce_timer, NOW() + MCE_PERIOD);
printk(KERN_INFO "Machine check exception polling timer started.\n");
switch (c->x86_vendor) {
case X86_VENDOR_AMD:
if (c->x86 == 6) { /* K7 */
firstbank = 1;
init_timer(&mce_timer, mce_work_fn, NULL, 0);
set_timer(&mce_timer, NOW() + MCE_PERIOD);
break;
}
/* Assume we are on K8 or newer AMD CPU here */
amd_nonfatal_mcheck_init(c);
break;
case X86_VENDOR_INTEL:
init_timer(&mce_timer, mce_work_fn, NULL, 0);
set_timer(&mce_timer, NOW() + MCE_PERIOD);
break;
}
printk(KERN_INFO "MCA: Machine check polling timer started.\n");
return 0;
}
__initcall(init_nonfatal_mce_checker);
+72
View File
@@ -0,0 +1,72 @@
/*
* MCA implementation for AMD K7/K8 CPUs
* Copyright (c) 2007 Advanced Micro Devices, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* The MCA/MCE MSRs should not be used anywhere else.
* They are cpu family/model specific and are only for use
* in terms of machine check handling.
* So we define them here rather in <asm/msr.h>.
*/
/* Bitfield of the MSR_IA32_MCG_CAP register */
#define MCG_CAP_COUNT 0x00000000000000ffULL
#define MCG_CTL_P 0x0000000000000100ULL
/* Bits 9-63 are reserved */
/* Bitfield of the MSR_IA32_MCG_STATUS register */
#define MCG_STATUS_RIPV 0x0000000000000001ULL
#define MCG_STATUS_EIPV 0x0000000000000002ULL
#define MCG_STATUS_MCIP 0x0000000000000004ULL
/* Bits 3-63 are reserved */
/* Bitfield of MSR_K8_MCi_STATUS registers */
/* MCA error code */
#define MCi_STATUS_MCA 0x000000000000ffffULL
/* model-specific error code */
#define MCi_STATUS_MSEC 0x00000000ffff0000ULL
/* Other information */
#define MCi_STATUS_OTHER 0x01ffffff00000000ULL
/* processor context corrupt */
#define MCi_STATUS_PCC 0x0200000000000000ULL
/* MSR_K8_MCi_ADDR register valid */
#define MCi_STATUS_ADDRV 0x0400000000000000ULL
/* MSR_K8_MCi_MISC register valid */
#define MCi_STATUS_MISCV 0x0800000000000000ULL
/* error condition enabled */
#define MCi_STATUS_EN 0x1000000000000000ULL
/* uncorrected error */
#define MCi_STATUS_UC 0x2000000000000000ULL
/* status register overflow */
#define MCi_STATUS_OVER 0x4000000000000000ULL
/* valid */
#define MCi_STATUS_VAL 0x8000000000000000ULL
/* Bitfield of MSi_STATUS_OTHER field */
/* reserved bits */
#define MCi_STATUS_OTHER_RESERVED1 0x00001fff00000000ULL
/* uncorrectable ECC error */
#define MCi_STATUS_OTEHR_UC_ECC 0x0000200000000000ULL
/* correctable ECC error */
#define MCi_STATUS_OTHER_C_ECC 0x0000400000000000ULL
/* ECC syndrome of an ECC error */
#define MCi_STATUS_OTHER_ECC_SYNDROME 0x007f800000000000ULL
/* reserved bits */
#define MCi_STATUS_OTHER_RESERVED2 0x0180000000000000ULL
+2 -2
View File
@@ -457,10 +457,10 @@ static void do_nmi_stats(unsigned char key)
if ( ((d = dom0) == NULL) || ((v = d->vcpu[0]) == NULL) )
return;
if ( v->nmi_pending || v->nmi_masked )
if ( v->nmi_pending || (v->trap_priority >= VCPU_TRAP_NMI) )
printk("dom0 vpu0: NMI %s%s\n",
v->nmi_pending ? "pending " : "",
v->nmi_masked ? "masked " : "");
(v->trap_priority >= VCPU_TRAP_NMI) ? "masked " : "");
else
printk("dom0 vcpu0: NMI neither pending nor masked\n");
}
+113 -10
View File
@@ -61,6 +61,7 @@
#include <asm/msr.h>
#include <asm/shared.h>
#include <asm/x86_emulate.h>
#include <asm/traps.h>
#include <asm/hvm/vpt.h>
#include <public/arch-x86/cpuid.h>
@@ -485,6 +486,20 @@ static unsigned int check_guest_io_breakpoint(struct vcpu *v,
return match;
}
/*
* Called from asm to set up the MCE trapbounce info.
* Returns 0 if no callback is set up, else 1.
*/
asmlinkage int set_guest_machinecheck_trapbounce(void)
{
struct vcpu *v = current;
struct trap_bounce *tb = &v->arch.trap_bounce;
do_guest_trap(TRAP_machine_check, guest_cpu_user_regs(), 0);
tb->flags &= ~TBF_EXCEPTION; /* not needed for MCE delivery path */
return !null_trap_bounce(v, tb);
}
/*
* Called from asm to set up the NMI trapbounce info.
* Returns 0 if no callback is set up, else 1.
@@ -904,8 +919,6 @@ asmlinkage void do_int3(struct cpu_user_regs *regs)
asmlinkage void do_machine_check(struct cpu_user_regs *regs)
{
extern fastcall void (*machine_check_vector)(
struct cpu_user_regs *, long error_code);
machine_check_vector(regs, regs->error_code);
}
@@ -2678,25 +2691,51 @@ asmlinkage void do_general_protection(struct cpu_user_regs *regs)
panic("GENERAL PROTECTION FAULT\n[error_code=%04x]\n", regs->error_code);
}
static DEFINE_PER_CPU(struct softirq_trap, softirq_trap);
static void nmi_mce_softirq(void)
{
/* Only used to defer wakeup of dom0,vcpu0 to a safe (non-NMI) context. */
vcpu_kick(dom0->vcpu[0]);
int cpu = smp_processor_id();
struct softirq_trap *st = &per_cpu(softirq_trap, cpu);
cpumask_t affinity;
BUG_ON(st == NULL);
BUG_ON(st->vcpu == NULL);
/* Set the tmp value unconditionally, so that
* the check in the iret hypercall works. */
st->vcpu->cpu_affinity_tmp = st->vcpu->cpu_affinity;
if ((cpu != st->processor)
|| (st->processor != st->vcpu->processor))
{
/* We are on a different physical cpu.
* Make sure to wakeup the vcpu on the
* specified processor.
*/
cpus_clear(affinity);
cpu_set(st->processor, affinity);
vcpu_set_affinity(st->vcpu, &affinity);
/* Affinity is restored in the iret hypercall. */
}
/* Only used to defer wakeup of domain/vcpu to
* a safe (non-NMI/MCE) context.
*/
vcpu_kick(st->vcpu);
}
static void nmi_dom0_report(unsigned int reason_idx)
{
struct domain *d;
struct vcpu *v;
struct domain *d = dom0;
if ( ((d = dom0) == NULL) || ((v = d->vcpu[0]) == NULL) )
if ( (d == NULL) || (d->vcpu[0] == NULL) )
return;
set_bit(reason_idx, nmi_reason(d));
/* Not safe to wake a vcpu here, or even to schedule a tasklet! */
if ( !test_and_set_bool(v->nmi_pending) )
raise_softirq(NMI_MCE_SOFTIRQ);
send_guest_trap(d, 0, TRAP_nmi);
}
asmlinkage void mem_parity_error(struct cpu_user_regs *regs)
@@ -3010,6 +3049,70 @@ long unregister_guest_nmi_callback(void)
return 0;
}
int guest_has_trap_callback(struct domain *d, uint16_t vcpuid, unsigned int trap_nr)
{
struct vcpu *v;
struct trap_info *t;
BUG_ON(d == NULL);
BUG_ON(vcpuid >= MAX_VIRT_CPUS);
/* Sanity check - XXX should be more fine grained. */
BUG_ON(trap_nr > TRAP_syscall);
v = d->vcpu[vcpuid];
t = &v->arch.guest_context.trap_ctxt[trap_nr];
return (t->address != 0);
}
int send_guest_trap(struct domain *d, uint16_t vcpuid, unsigned int trap_nr)
{
struct vcpu *v;
struct softirq_trap *st;
BUG_ON(d == NULL);
BUG_ON(vcpuid >= MAX_VIRT_CPUS);
v = d->vcpu[vcpuid];
switch (trap_nr) {
case TRAP_nmi:
if ( !test_and_set_bool(v->nmi_pending) ) {
st = &per_cpu(softirq_trap, smp_processor_id());
st->domain = dom0;
st->vcpu = dom0->vcpu[0];
st->processor = st->vcpu->processor;
/* not safe to wake up a vcpu here */
raise_softirq(NMI_MCE_SOFTIRQ);
return 0;
}
break;
case TRAP_machine_check:
/* We are called by the machine check (exception or polling) handlers
* on the physical CPU that reported a machine check error. */
if ( !test_and_set_bool(v->mce_pending) ) {
st = &per_cpu(softirq_trap, smp_processor_id());
st->domain = d;
st->vcpu = v;
st->processor = v->processor;
/* not safe to wake up a vcpu here */
raise_softirq(NMI_MCE_SOFTIRQ);
return 0;
}
break;
}
/* delivery failed */
return -EIO;
}
long do_set_trap_table(XEN_GUEST_HANDLE(const_trap_info_t) traps)
{
struct trap_info cur;
+5 -1
View File
@@ -67,7 +67,11 @@ void __dummy__(void)
arch.guest_context.kernel_sp);
OFFSET(VCPU_guest_context_flags, struct vcpu, arch.guest_context.flags);
OFFSET(VCPU_nmi_pending, struct vcpu, nmi_pending);
OFFSET(VCPU_nmi_masked, struct vcpu, nmi_masked);
OFFSET(VCPU_mce_pending, struct vcpu, mce_pending);
OFFSET(VCPU_old_trap_priority, struct vcpu, old_trap_priority);
OFFSET(VCPU_trap_priority, struct vcpu, trap_priority);
DEFINE(VCPU_TRAP_NMI, VCPU_TRAP_NMI);
DEFINE(VCPU_TRAP_MCE, VCPU_TRAP_MCE);
DEFINE(_VGCF_failsafe_disables_events, _VGCF_failsafe_disables_events);
BLANK();
+33 -3
View File
@@ -229,6 +229,8 @@ test_all_events:
shl $IRQSTAT_shift,%eax
test %ecx,irq_stat(%eax,1)
jnz process_softirqs
testb $1,VCPU_mce_pending(%ebx)
jnz process_mce
testb $1,VCPU_nmi_pending(%ebx)
jnz process_nmi
test_guest_events:
@@ -255,15 +257,35 @@ process_softirqs:
jmp test_all_events
ALIGN
/* %ebx: struct vcpu */
process_mce:
cmpw $VCPU_TRAP_MCE,VCPU_trap_priority(%ebx)
jae test_guest_events
sti
movb $0,VCPU_mce_pending(%ebx)
call set_guest_machinecheck_trapbounce
test %eax,%eax
jz test_all_events
movw VCPU_trap_priority(%ebx),%dx # safe priority for the
movw %dx,VCPU_old_trap_priority(%ebx) # iret hypercall
movw $VCPU_TRAP_MCE,VCPU_trap_priority(%ebx)
jmp process_trap
ALIGN
/* %ebx: struct vcpu */
process_nmi:
testb $1,VCPU_nmi_masked(%ebx)
jnz test_guest_events
cmpw $VCPU_TRAP_NMI,VCPU_trap_priority(%ebx)
jae test_guest_events
sti
movb $0,VCPU_nmi_pending(%ebx)
call set_guest_nmi_trapbounce
test %eax,%eax
jz test_all_events
movb $1,VCPU_nmi_masked(%ebx)
movw VCPU_trap_priority(%ebx),%dx # safe priority for the
movw %dx,VCPU_old_trap_priority(%ebx) # iret hypercall
movw $VCPU_TRAP_NMI,VCPU_trap_priority(%ebx)
/* FALLTHROUGH */
process_trap:
leal VCPU_trap_bounce(%ebx),%edx
call create_bounce_frame
jmp test_all_events
@@ -681,6 +703,10 @@ ENTRY(hypercall_table)
.long do_sysctl /* 35 */
.long do_domctl
.long do_kexec_op
.rept __HYPERVISOR_arch_0-((.-hypercall_table)/4)
.long do_ni_hypercall
.endr
.long do_mca /* 48 */
.rept NR_hypercalls-((.-hypercall_table)/4)
.long do_ni_hypercall
.endr
@@ -724,6 +750,10 @@ ENTRY(hypercall_args_table)
.byte 1 /* do_sysctl */ /* 35 */
.byte 1 /* do_domctl */
.byte 2 /* do_kexec_op */
.rept __HYPERVISOR_arch_0-(.-hypercall_args_table)
.byte 0 /* do_ni_hypercall */
.endr
.byte 1 /* do_mca */ /* 48 */
.rept NR_hypercalls-(.-hypercall_args_table)
.byte 0 /* do_ni_hypercall */
.endr
+7 -2
View File
@@ -255,8 +255,13 @@ unsigned long do_iret(void)
goto exit_and_crash;
}
/* No longer in NMI context. */
v->nmi_masked = 0;
/* Restore affinity. */
if ((v->trap_priority >= VCPU_TRAP_NMI)
&& !cpus_equal(v->cpu_affinity_tmp, v->cpu_affinity))
vcpu_set_affinity(v, &v->cpu_affinity_tmp);
/* Restore previous trap priority */
v->trap_priority = v->old_trap_priority;
/* Restore upcall mask from supplied EFLAGS.IF. */
vcpu_info(v, evtchn_upcall_mask) = !(eflags & X86_EFLAGS_IF);
+5 -1
View File
@@ -92,7 +92,11 @@ void __dummy__(void)
OFFSET(VCPU_kernel_ss, struct vcpu, arch.guest_context.kernel_ss);
OFFSET(VCPU_guest_context_flags, struct vcpu, arch.guest_context.flags);
OFFSET(VCPU_nmi_pending, struct vcpu, nmi_pending);
OFFSET(VCPU_nmi_masked, struct vcpu, nmi_masked);
OFFSET(VCPU_mce_pending, struct vcpu, mce_pending);
OFFSET(VCPU_old_trap_priority, struct vcpu, old_trap_priority);
OFFSET(VCPU_trap_priority, struct vcpu, trap_priority);
DEFINE(VCPU_TRAP_NMI, VCPU_TRAP_NMI);
DEFINE(VCPU_TRAP_MCE, VCPU_TRAP_MCE);
DEFINE(_VGCF_failsafe_disables_events, _VGCF_failsafe_disables_events);
DEFINE(_VGCF_syscall_disables_events, _VGCF_syscall_disables_events);
BLANK();
+32 -3
View File
@@ -101,6 +101,8 @@ ENTRY(compat_test_all_events)
leaq irq_stat(%rip),%rcx
testl $~0,(%rcx,%rax,1)
jnz compat_process_softirqs
testb $1,VCPU_mce_pending(%rbx)
jnz compat_process_mce
testb $1,VCPU_nmi_pending(%rbx)
jnz compat_process_nmi
compat_test_guest_events:
@@ -129,15 +131,34 @@ compat_process_softirqs:
ALIGN
/* %rbx: struct vcpu */
compat_process_mce:
cmpw $VCPU_TRAP_MCE,VCPU_trap_priority(%rbx)
jae compat_test_guest_events
sti
movb $0,VCPU_mce_pending(%rbx)
call set_guest_machinecheck_trapbounce
testl %eax,%eax
jz compat_test_all_events
movw VCPU_trap_priority(%rbx),%dx # safe priority for the
movw %dx,VCPU_old_trap_priority(%rbx) # iret hypercall
movw $VCPU_TRAP_MCE,VCPU_trap_priority(%rbx)
jmp compat_process_trap
ALIGN
/* %rbx: struct vcpu */
compat_process_nmi:
testb $1,VCPU_nmi_masked(%rbx)
jnz compat_test_guest_events
cmpw $VCPU_TRAP_NMI,VCPU_trap_priority(%rbx)
jae compat_test_guest_events
sti
movb $0,VCPU_nmi_pending(%rbx)
call set_guest_nmi_trapbounce
testl %eax,%eax
jz compat_test_all_events
movb $1,VCPU_nmi_masked(%rbx)
movw VCPU_trap_priority(%rbx),%dx # safe priority for the
movw %dx,VCPU_old_trap_priority(%rbx) # iret hypercall
movw $VCPU_TRAP_NMI,VCPU_trap_priority(%rbx)
/* FALLTHROUGH */
compat_process_trap:
leaq VCPU_trap_bounce(%rbx),%rdx
call compat_create_bounce_frame
jmp compat_test_all_events
@@ -386,6 +407,10 @@ ENTRY(compat_hypercall_table)
.quad do_sysctl /* 35 */
.quad do_domctl
.quad compat_kexec_op
.rept __HYPERVISOR_arch_0-((.-compat_hypercall_table)/8)
.quad compat_ni_hypercall
.endr
.quad do_mca /* 48 */
.rept NR_hypercalls-((.-compat_hypercall_table)/8)
.quad compat_ni_hypercall
.endr
@@ -429,6 +454,10 @@ ENTRY(compat_hypercall_args_table)
.byte 1 /* do_sysctl */ /* 35 */
.byte 1 /* do_domctl */
.byte 2 /* compat_kexec_op */
.rept __HYPERVISOR_arch_0-(.-compat_hypercall_args_table)
.byte 0 /* compat_ni_hypercall */
.endr
.byte 1 /* do_mca */
.rept NR_hypercalls-(.-compat_hypercall_args_table)
.byte 0 /* compat_ni_hypercall */
.endr
+7 -2
View File
@@ -121,8 +121,13 @@ unsigned int compat_iret(void)
else
regs->_esp += 16;
/* No longer in NMI context. */
v->nmi_masked = 0;
/* Restore affinity. */
if ((v->trap_priority >= VCPU_TRAP_NMI)
&& !cpus_equal(v->cpu_affinity_tmp, v->cpu_affinity))
vcpu_set_affinity(v, &v->cpu_affinity_tmp);
/* Restore previous trap priority */
v->trap_priority = v->old_trap_priority;
/* Restore upcall mask from supplied EFLAGS.IF. */
vcpu_info(v, evtchn_upcall_mask) = !(eflags & X86_EFLAGS_IF);
+32 -3
View File
@@ -205,6 +205,8 @@ test_all_events:
leaq irq_stat(%rip),%rcx
testl $~0,(%rcx,%rax,1)
jnz process_softirqs
testb $1,VCPU_mce_pending(%rbx)
jnz process_mce
testb $1,VCPU_nmi_pending(%rbx)
jnz process_nmi
test_guest_events:
@@ -231,15 +233,34 @@ process_softirqs:
ALIGN
/* %rbx: struct vcpu */
process_mce:
cmpw $VCPU_TRAP_MCE,VCPU_trap_priority(%rbx)
jae test_guest_events
sti
movb $0,VCPU_mce_pending(%rbx)
call set_guest_machinecheck_trapbounce
test %eax,%eax
jz test_all_events
movw VCPU_trap_priority(%rbx),%dx # safe priority for the
movw %dx,VCPU_old_trap_priority(%rbx) # iret hypercall
movw $VCPU_TRAP_MCE,VCPU_trap_priority(%rbx)
jmp process_trap
ALIGN
/* %rbx: struct vcpu */
process_nmi:
testb $1,VCPU_nmi_masked(%rbx)
jnz test_guest_events
cmpw $VCPU_TRAP_NMI,VCPU_trap_priority(%rbx)
jae test_guest_events
sti
movb $0,VCPU_nmi_pending(%rbx)
call set_guest_nmi_trapbounce
test %eax,%eax
jz test_all_events
movb $1,VCPU_nmi_masked(%rbx)
movw VCPU_trap_priority(%rbx),%dx # safe priority for the
movw %dx,VCPU_old_trap_priority(%rbx) # iret hypercall
movw $VCPU_TRAP_NMI,VCPU_trap_priority(%rbx)
/* FALLTHROUGH */
process_trap:
leaq VCPU_trap_bounce(%rbx),%rdx
call create_bounce_frame
jmp test_all_events
@@ -671,6 +692,10 @@ ENTRY(hypercall_table)
.quad do_sysctl /* 35 */
.quad do_domctl
.quad do_kexec_op
.rept __HYPERVISOR_arch_0-((.-hypercall_table)/8)
.quad do_ni_hypercall
.endr
.quad do_mca /* 48 */
.rept NR_hypercalls-((.-hypercall_table)/8)
.quad do_ni_hypercall
.endr
@@ -715,6 +740,10 @@ ENTRY(hypercall_args_table)
.byte 1 /* do_domctl */
.byte 2 /* do_kexec */
.byte 1 /* do_xsm_op */
.rept __HYPERVISOR_arch_0-(.-hypercall_args_table)
.byte 0 /* do_ni_hypercall */
.endr
.byte 1 /* do_mca */ /* 48 */
.rept NR_hypercalls-(.-hypercall_args_table)
.byte 0 /* do_ni_hypercall */
.endr
+7 -2
View File
@@ -288,8 +288,13 @@ unsigned long do_iret(void)
regs->rcx = iret_saved.rcx;
}
/* No longer in NMI context. */
v->nmi_masked = 0;
/* Restore affinity. */
if ((v->trap_priority >= VCPU_TRAP_NMI)
&& !cpus_equal(v->cpu_affinity_tmp, v->cpu_affinity))
vcpu_set_affinity(v, &v->cpu_affinity_tmp);
/* Restore previous trap priority */
v->trap_priority = v->old_trap_priority;
/* Restore upcall mask from supplied EFLAGS.IF. */
vcpu_info(v, evtchn_upcall_mask) = !(iret_saved.rflags & EF_IE);
+3 -1
View File
@@ -654,7 +654,9 @@ void vcpu_reset(struct vcpu *v)
v->is_polling = 0;
v->is_initialised = 0;
v->nmi_pending = 0;
v->nmi_masked = 0;
v->mce_pending = 0;
v->old_trap_priority = VCPU_TRAP_NONE;
v->trap_priority = VCPU_TRAP_NONE;
clear_bit(_VPF_blocked, &v->pause_flags);
domain_unlock(v->domain);
+15
View File
@@ -587,6 +587,21 @@ void send_guest_vcpu_virq(struct vcpu *v, int virq)
evtchn_set_pending(v, port);
}
int guest_enabled_event(struct vcpu *v, int virq)
{
int port;
if ( unlikely(v == NULL) )
return 0;
port = v->virq_to_evtchn[virq];
if ( port == 0 )
return 0;
/* virq is in use */
return 1;
}
void send_guest_global_virq(struct domain *d, int virq)
{
int port;
+1
View File
@@ -20,6 +20,7 @@ headers-y := \
compat/xen.h \
compat/xencomm.h \
compat/xenoprof.h
headers-$(CONFIG_X86) += compat/arch-x86/xen-mca.h
headers-$(CONFIG_X86) += compat/arch-x86/xen.h
headers-$(CONFIG_X86) += compat/arch-x86/xen-$(compat-arch-y).h
headers-y += compat/arch-$(compat-arch-y).h compat/xlat.h
+6 -1
View File
@@ -69,7 +69,12 @@ static inline void local_event_delivery_enable(void)
/* No arch specific virq definition now. Default to global. */
static inline int arch_virq_is_global(int virq)
{
return 1;
switch (virq) {
case VIRQ_MCA:
return 1;
default:
return 1;
}
}
#endif
+3
View File
@@ -141,6 +141,9 @@ static inline u32 pickle_domptr(struct domain *domain)
#define page_get_owner(_p) (unpickle_domptr((_p)->u.inuse._domain))
#define page_set_owner(_p,_d) ((_p)->u.inuse._domain = pickle_domptr(_d))
#define maddr_get_owner(ma) (page_get_owner(maddr_to_page((ma))))
#define vaddr_get_owner(va) (page_get_owner(virt_to_page((va))))
#define XENSHARE_writable 0
#define XENSHARE_readonly 1
extern void share_xen_page_with_guest(
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2007, 2008 Advanced Micro Devices, Inc.
* Author: Christoph Egger <Christoph.Egger@amd.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef ASM_TRAP_H
#define ASM_TRAP_H
struct softirq_trap {
struct domain *domain; /* domain to inject trap */
struct vcpu *vcpu; /* vcpu to inject trap */
int processor; /* physical cpu to inject trap */
};
struct cpu_user_regs;
extern void (*machine_check_vector)(struct cpu_user_regs *regs, long error_code);
/**
* guest_has_trap_callback
*
* returns true (non-zero) if guest registered a trap handler
*/
extern int guest_has_trap_callback(struct domain *d, uint16_t vcpuid,
unsigned int trap_nr);
/**
* send_guest_trap
*
* delivers trap to guest analogous to send_guest_global_virq
* return 0 on successful delivery
*/
extern int send_guest_trap(struct domain *d, uint16_t vcpuid,
unsigned int trap_nr);
#endif /* ASM_TRAP_H */
+279
View File
@@ -0,0 +1,279 @@
/******************************************************************************
* arch-x86/mca.h
*
* Contributed by Advanced Micro Devices, Inc.
* Author: Christoph Egger <Christoph.Egger@amd.com>
*
* Guest OS machine check interface to x86 Xen.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/* Full MCA functionality has the following Usecases from the guest side:
*
* Must have's:
* 1. Dom0 and DomU register machine check trap callback handlers
* (already done via "set_trap_table" hypercall)
* 2. Dom0 registers machine check event callback handler
* (doable via EVTCHNOP_bind_virq)
* 3. Dom0 and DomU fetches machine check data
* 4. Dom0 wants Xen to notify a DomU
* 5. Dom0 gets DomU ID from physical address
* 6. Dom0 wants Xen to kill DomU (already done for "xm destroy")
*
* Nice to have's:
* 7. Dom0 wants Xen to deactivate a physical CPU
* This is better done as separate task, physical CPU hotplugging,
* and hypercall(s) should be sysctl's
* 8. Page migration proposed from Xen NUMA work, where Dom0 can tell Xen to
* move a DomU (or Dom0 itself) away from a malicious page
* producing correctable errors.
* 9. offlining physical page:
* Xen free's and never re-uses a certain physical page.
* 10. Testfacility: Allow Dom0 to write values into machine check MSR's
* and tell Xen to trigger a machine check
*/
#ifndef __XEN_PUBLIC_ARCH_X86_MCA_H__
#define __XEN_PUBLIC_ARCH_X86_MCA_H__
/* Hypercall */
#define __HYPERVISOR_mca __HYPERVISOR_arch_0
#define XEN_MCA_INTERFACE_VERSION 0x03000001
/* IN: Dom0 calls hypercall from MC event handler. */
#define XEN_MC_CORRECTABLE 0x0
/* IN: Dom0/DomU calls hypercall from MC trap handler. */
#define XEN_MC_TRAP 0x1
/* XEN_MC_CORRECTABLE and XEN_MC_TRAP are mutually exclusive. */
/* OUT: All is ok */
#define XEN_MC_OK 0x0
/* OUT: Domain could not fetch data. */
#define XEN_MC_FETCHFAILED 0x1
/* OUT: There was no machine check data to fetch. */
#define XEN_MC_NODATA 0x2
/* OUT: Between notification time and this hypercall an other
* (most likely) correctable error happened. The fetched data,
* does not match the original machine check data. */
#define XEN_MC_NOMATCH 0x4
/* OUT: DomU did not register MC NMI handler. Try something else. */
#define XEN_MC_CANNOTHANDLE 0x8
/* OUT: Notifying DomU failed. Retry later or try something else. */
#define XEN_MC_NOTDELIVERED 0x10
/* Note, XEN_MC_CANNOTHANDLE and XEN_MC_NOTDELIVERED are mutually exclusive. */
#ifndef __ASSEMBLY__
#define VIRQ_MCA VIRQ_ARCH_0 /* G. (DOM0) Machine Check Architecture */
/*
* Machine Check Architecure:
* structs are read-only and used to report all kinds of
* correctable and uncorrectable errors detected by the HW.
* Dom0 and DomU: register a handler to get notified.
* Dom0 only: Correctable errors are reported via VIRQ_MCA
* Dom0 and DomU: Uncorrectable errors are reported via nmi handlers
*/
#define MC_TYPE_GLOBAL 0
#define MC_TYPE_BANK 1
#define MC_TYPE_EXTENDED 2
struct mcinfo_common {
uint16_t type; /* structure type */
uint16_t size; /* size of this struct in bytes */
};
#define MC_FLAG_CORRECTABLE (1 << 0)
#define MC_FLAG_UNCORRECTABLE (1 << 1)
/* contains global x86 mc information */
struct mcinfo_global {
struct mcinfo_common common;
/* running domain at the time in error (most likely the impacted one) */
uint16_t mc_domid;
uint32_t mc_socketid; /* physical socket of the physical core */
uint16_t mc_coreid; /* physical impacted core */
uint16_t mc_core_threadid; /* core thread of physical core */
uint16_t mc_vcpuid; /* virtual cpu scheduled for mc_domid */
uint64_t mc_gstatus; /* global status */
uint32_t mc_flags;
};
/* contains bank local x86 mc information */
struct mcinfo_bank {
struct mcinfo_common common;
uint16_t mc_bank; /* bank nr */
uint16_t mc_domid; /* Usecase 5: domain referenced by mc_addr on dom0
* and if mc_addr is valid. Never valid on DomU. */
uint64_t mc_status; /* bank status */
uint64_t mc_addr; /* bank address, only valid
* if addr bit is set in mc_status */
uint64_t mc_misc;
};
struct mcinfo_msr {
uint64_t reg; /* MSR */
uint64_t value; /* MSR value */
};
/* contains mc information from other
* or additional mc MSRs */
struct mcinfo_extended {
struct mcinfo_common common;
/* You can fill up to five registers.
* If you need more, then use this structure
* multiple times. */
uint32_t mc_msrs; /* Number of msr with valid values. */
struct mcinfo_msr mc_msr[5];
};
#define MCINFO_HYPERCALLSIZE 1024
#define MCINFO_MAXSIZE 768
struct mc_info {
/* Number of mcinfo_* entries in mi_data */
uint32_t mi_nentries;
uint8_t mi_data[MCINFO_MAXSIZE - sizeof(uint32_t)];
};
typedef struct mc_info mc_info_t;
/*
* OS's should use these instead of writing their own lookup function
* each with its own bugs and drawbacks.
* We use macros instead of static inline functions to allow guests
* to include this header in assembly files (*.S).
*/
/* Prototype:
* uint32_t x86_mcinfo_nentries(struct mc_info *mi);
*/
#define x86_mcinfo_nentries(_mi) \
(_mi)->mi_nentries
/* Prototype:
* struct mcinfo_common *x86_mcinfo_first(struct mc_info *mi);
*/
#define x86_mcinfo_first(_mi) \
(struct mcinfo_common *)((_mi)->mi_data)
/* Prototype:
* struct mcinfo_common *x86_mcinfo_next(struct mcinfo_common *mic);
*/
#define x86_mcinfo_next(_mic) \
(struct mcinfo_common *)((uint8_t *)(_mic) + (_mic)->size)
/* Prototype:
* void x86_mcinfo_lookup(void *ret, struct mc_info *mi, uint16_t type);
*/
#define x86_mcinfo_lookup(_ret, _mi, _type) \
do { \
uint32_t found, i; \
struct mcinfo_common *_mic; \
\
found = 0; \
(_ret) = NULL; \
if (_mi == NULL) break; \
_mic = x86_mcinfo_first(_mi); \
for (i = 0; i < x86_mcinfo_nentries(_mi); i++) { \
if (_mic->type == (_type)) { \
found = 1; \
break; \
} \
_mic = x86_mcinfo_next(_mic); \
} \
(_ret) = found ? _mic : NULL; \
} while (0)
/* Usecase 1
* Register machine check trap callback handler
* (already done via "set_trap_table" hypercall)
*/
/* Usecase 2
* Dom0 registers machine check event callback handler
* done by EVTCHNOP_bind_virq
*/
/* Usecase 3
* Fetch machine check data from hypervisor.
* Note, this hypercall is special, because both Dom0 and DomU must use this.
*/
#define XEN_MC_fetch 1
struct xen_mc_fetch {
/* IN/OUT variables. */
uint32_t flags;
/* IN: XEN_MC_CORRECTABLE, XEN_MC_TRAP */
/* OUT: XEN_MC_OK, XEN_MC_FETCHFAILED, XEN_MC_NODATA, XEN_MC_NOMATCH */
/* OUT variables. */
uint32_t fetch_idx; /* only useful for Dom0 for the notify hypercall */
struct mc_info mc_info;
};
typedef struct xen_mc_fetch xen_mc_fetch_t;
DEFINE_XEN_GUEST_HANDLE(xen_mc_fetch_t);
/* Usecase 4
* This tells the hypervisor to notify a DomU about the machine check error
*/
#define XEN_MC_notifydomain 2
struct xen_mc_notifydomain {
/* IN variables. */
uint16_t mc_domid; /* The unprivileged domain to notify. */
uint16_t mc_vcpuid; /* The vcpu in mc_domid to notify.
* Usually echo'd value from the fetch hypercall. */
uint32_t fetch_idx; /* echo'd value from the fetch hypercall. */
/* IN/OUT variables. */
uint32_t flags;
/* IN: XEN_MC_CORRECTABLE, XEN_MC_TRAP */
/* OUT: XEN_MC_OK, XEN_MC_CANNOTHANDLE, XEN_MC_NOTDELIVERED, XEN_MC_NOMATCH */
};
typedef struct xen_mc_notifydomain xen_mc_notifydomain_t;
DEFINE_XEN_GUEST_HANDLE(xen_mc_notifydomain_t);
struct xen_mc {
uint32_t cmd;
uint32_t interface_version; /* XEN_MCA_INTERFACE_VERSION */
union {
struct xen_mc_fetch mc_fetch;
struct xen_mc_notifydomain mc_notifydomain;
uint8_t pad[MCINFO_HYPERCALLSIZE];
} u;
};
typedef struct xen_mc xen_mc_t;
DEFINE_XEN_GUEST_HANDLE(xen_mc_t);
#endif /* __ASSEMBLY__ */
#endif /* __XEN_PUBLIC_ARCH_X86_MCA_H__ */
+4
View File
@@ -76,6 +76,10 @@ typedef unsigned long xen_pfn_t;
/* Maximum number of virtual CPUs in multi-processor guests. */
#define MAX_VIRT_CPUS 32
/* Machine check support */
#include "xen-mca.h"
#ifndef __ASSEMBLY__
typedef unsigned long xen_ulong_t;
+3
View File
@@ -50,6 +50,9 @@ int alloc_unbound_xen_event_channel(
void free_xen_event_channel(
struct vcpu *local_vcpu, int port);
/* Query if event channel is in use by the guest */
int guest_enabled_event(struct vcpu *v, int virq);
/* Notify remote end of a Xen-attached event channel.*/
void notify_via_xen_event_channel(int lport);
+13 -2
View File
@@ -112,10 +112,21 @@ struct vcpu
bool_t is_initialised;
/* Currently running on a CPU? */
bool_t is_running;
/* MCE callback pending for this VCPU? */
bool_t mce_pending;
/* NMI callback pending for this VCPU? */
bool_t nmi_pending;
/* Avoid NMI reentry by allowing NMIs to be masked for short periods. */
bool_t nmi_masked;
/* Higher priorized traps may interrupt lower priorized traps,
* lower priorized traps wait until higher priorized traps finished.
* Note: This concept is known as "system priority level" (spl)
* in the UNIX world. */
uint16_t old_trap_priority;
uint16_t trap_priority;
#define VCPU_TRAP_NONE 0
#define VCPU_TRAP_NMI 1
#define VCPU_TRAP_MCE 2
/* Require shutdown to be deferred for some asynchronous operation? */
bool_t defer_shutdown;
/* VCPU is paused following shutdown request (d->is_shutting_down)? */
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.