/root/src/xen/xen/arch/x86/cpu/mcheck/mcaction.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include <xen/types.h> |
2 | | #include <xen/sched.h> |
3 | | #include "mcaction.h" |
4 | | #include "vmce.h" |
5 | | #include "mce.h" |
6 | | |
7 | | static struct mcinfo_recovery * |
8 | | mci_action_add_pageoffline(int bank, struct mc_info *mi, |
9 | | uint64_t mfn, uint32_t status) |
10 | 0 | { |
11 | 0 | struct mcinfo_recovery *rec; |
12 | 0 |
|
13 | 0 | if ( !mi ) |
14 | 0 | return NULL; |
15 | 0 |
|
16 | 0 | rec = x86_mcinfo_reserve(mi, sizeof(*rec), MC_TYPE_RECOVERY); |
17 | 0 | if ( !rec ) |
18 | 0 | { |
19 | 0 | mi->flags |= MCINFO_FLAGS_UNCOMPLETE; |
20 | 0 | return NULL; |
21 | 0 | } |
22 | 0 |
|
23 | 0 | rec->mc_bank = bank; |
24 | 0 | rec->action_types = MC_ACTION_PAGE_OFFLINE; |
25 | 0 | rec->action_info.page_retire.mfn = mfn; |
26 | 0 | rec->action_info.page_retire.status = status; |
27 | 0 | return rec; |
28 | 0 | } |
29 | | |
30 | | mce_check_addr_t mc_check_addr = NULL; |
31 | | |
32 | | void mce_register_addrcheck(mce_check_addr_t cbfunc) |
33 | 12 | { |
34 | 12 | mc_check_addr = cbfunc; |
35 | 12 | } |
36 | | |
37 | | void |
38 | | mc_memerr_dhandler(struct mca_binfo *binfo, |
39 | | enum mce_result *result, |
40 | | const struct cpu_user_regs *regs) |
41 | 0 | { |
42 | 0 | struct mcinfo_bank *bank = binfo->mib; |
43 | 0 | struct mcinfo_global *global = binfo->mig; |
44 | 0 | struct domain *d; |
45 | 0 | unsigned long mfn, gfn; |
46 | 0 | uint32_t status; |
47 | 0 | int vmce_vcpuid; |
48 | 0 | unsigned int mc_vcpuid; |
49 | 0 |
|
50 | 0 | if ( !mc_check_addr(bank->mc_status, bank->mc_misc, MC_ADDR_PHYSICAL) ) |
51 | 0 | { |
52 | 0 | dprintk(XENLOG_WARNING, |
53 | 0 | "No physical address provided for memory error\n"); |
54 | 0 | return; |
55 | 0 | } |
56 | 0 |
|
57 | 0 | mfn = bank->mc_addr >> PAGE_SHIFT; |
58 | 0 | if ( offline_page(mfn, 1, &status) ) |
59 | 0 | { |
60 | 0 | dprintk(XENLOG_WARNING, |
61 | 0 | "Failed to offline page %lx for MCE error\n", mfn); |
62 | 0 | return; |
63 | 0 | } |
64 | 0 |
|
65 | 0 | mci_action_add_pageoffline(binfo->bank, binfo->mi, mfn, status); |
66 | 0 |
|
67 | 0 | /* This is free page */ |
68 | 0 | if ( status & PG_OFFLINE_OFFLINED ) |
69 | 0 | *result = MCER_RECOVERED; |
70 | 0 | else if ( status & PG_OFFLINE_AGAIN ) |
71 | 0 | *result = MCER_CONTINUE; |
72 | 0 | else if ( status & PG_OFFLINE_PENDING ) |
73 | 0 | { |
74 | 0 | /* This page has owner */ |
75 | 0 | if ( status & PG_OFFLINE_OWNED ) |
76 | 0 | { |
77 | 0 | bank->mc_domid = status >> PG_OFFLINE_OWNER_SHIFT; |
78 | 0 | mce_printk(MCE_QUIET, "MCE: This error page is ownded" |
79 | 0 | " by DOM %d\n", bank->mc_domid); |
80 | 0 | /* |
81 | 0 | * XXX: Cannot handle shared pages yet |
82 | 0 | * (this should identify all domains and gfn mapping to |
83 | 0 | * the mfn in question) |
84 | 0 | */ |
85 | 0 | BUG_ON( bank->mc_domid == DOMID_COW ); |
86 | 0 | if ( bank->mc_domid != DOMID_XEN ) |
87 | 0 | { |
88 | 0 | d = get_domain_by_id(bank->mc_domid); |
89 | 0 | ASSERT(d); |
90 | 0 | gfn = get_gpfn_from_mfn((bank->mc_addr) >> PAGE_SHIFT); |
91 | 0 |
|
92 | 0 | if ( unmmap_broken_page(d, _mfn(mfn), gfn) ) |
93 | 0 | { |
94 | 0 | printk("Unmap broken memory %lx for DOM%d failed\n", |
95 | 0 | mfn, d->domain_id); |
96 | 0 | goto vmce_failed; |
97 | 0 | } |
98 | 0 |
|
99 | 0 | mc_vcpuid = global->mc_vcpuid; |
100 | 0 | if ( mc_vcpuid == XEN_MC_VCPUID_INVALID || |
101 | 0 | /* |
102 | 0 | * Because MC# may happen asynchronously with the actual |
103 | 0 | * operation that triggers the error, the domain ID as |
104 | 0 | * well as the vCPU ID collected in 'global' at MC# are |
105 | 0 | * not always precise. In that case, fallback to broadcast. |
106 | 0 | */ |
107 | 0 | global->mc_domid != bank->mc_domid || |
108 | 0 | (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && |
109 | 0 | (!(global->mc_gstatus & MCG_STATUS_LMCE) || |
110 | 0 | !(d->vcpu[mc_vcpuid]->arch.vmce.mcg_ext_ctl & |
111 | 0 | MCG_EXT_CTL_LMCE_EN))) ) |
112 | 0 | vmce_vcpuid = VMCE_INJECT_BROADCAST; |
113 | 0 | else |
114 | 0 | vmce_vcpuid = mc_vcpuid; |
115 | 0 |
|
116 | 0 | bank->mc_addr = gfn << PAGE_SHIFT | |
117 | 0 | (bank->mc_addr & (PAGE_SIZE - 1)); |
118 | 0 | if ( fill_vmsr_data(bank, d, global->mc_gstatus, vmce_vcpuid) ) |
119 | 0 | { |
120 | 0 | mce_printk(MCE_QUIET, "Fill vMCE# data for DOM%d " |
121 | 0 | "failed\n", bank->mc_domid); |
122 | 0 | goto vmce_failed; |
123 | 0 | } |
124 | 0 |
|
125 | 0 | /* We will inject vMCE to DOMU */ |
126 | 0 | if ( inject_vmce(d, vmce_vcpuid) < 0 ) |
127 | 0 | { |
128 | 0 | mce_printk(MCE_QUIET, "inject vMCE to DOM%d" |
129 | 0 | " failed\n", d->domain_id); |
130 | 0 | goto vmce_failed; |
131 | 0 | } |
132 | 0 |
|
133 | 0 | /* |
134 | 0 | * Impacted domain go on with domain's recovery job |
135 | 0 | * if the domain has its own MCA handler. |
136 | 0 | * For xen, it has contained the error and finished |
137 | 0 | * its own recovery job. |
138 | 0 | */ |
139 | 0 | *result = MCER_RECOVERED; |
140 | 0 | put_domain(d); |
141 | 0 |
|
142 | 0 | return; |
143 | 0 | vmce_failed: |
144 | 0 | put_domain(d); |
145 | 0 | domain_crash(d); |
146 | 0 | } |
147 | 0 | } |
148 | 0 | } |
149 | 0 | } |