Coverage Report

Created: 2017-10-25 09:10

/root/src/xen/xen/arch/x86/cpu/mcheck/amd_nonfatal.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * MCA implementation for AMD CPUs
3
 * Copyright (c) 2007 Advanced Micro Devices, Inc.
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; If not, see <http://www.gnu.org/licenses/>.
17
 */
18
19
20
/* K8 common MCA documentation published at
21
 *
22
 * AMD64 Architecture Programmer's Manual Volume 2:
23
 * System Programming
24
 * Publication # 24593 Revision: 3.12
25
 * Issue Date: September 2006
26
 *
27
 * URL:
28
 * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/24593.pdf
29
 */
30
31
/* The related documentation for K8 Revisions A - E is:
32
 *
33
 * BIOS and Kernel Developer's Guide for
34
 * AMD Athlon 64 and AMD Opteron Processors
35
 * Publication # 26094 Revision: 3.30
36
 * Issue Date: February 2006
37
 *
38
 * URL:
39
 * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/26094.PDF
40
 */
41
42
/* The related documentation for K8 Revisions F - G is:
43
 *
44
 * BIOS and Kernel Developer's Guide for
45
 * AMD NPT Family 0Fh Processors
46
 * Publication # 32559 Revision: 3.04
47
 * Issue Date: December 2006
48
 *
49
 * URL:
50
 * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/32559.pdf
51
 */
52
53
#include <xen/init.h>
54
#include <xen/types.h>
55
#include <xen/kernel.h>
56
#include <xen/smp.h>
57
#include <xen/timer.h>
58
#include <xen/event.h>
59
60
#include <asm/processor.h>
61
#include <asm/system.h>
62
#include <asm/msr.h>
63
64
#include "mce.h"
65
#include "vmce.h"
66
67
static struct timer mce_timer;
68
69
#define MCE_PERIOD MILLISECS(10000)
70
0
#define MCE_MIN    MILLISECS(2000)
71
0
#define MCE_MAX    MILLISECS(30000)
72
73
static s_time_t period = MCE_PERIOD;
74
static int hw_threshold = 0;
75
static int adjust = 0;
76
static int variable_period = 1;
77
78
/* The polling service routine:
79
 * Collects information of correctable errors and notifies
80
 * Dom0 via an event.
81
 */
82
static void mce_amd_checkregs(void *info)
83
0
{
84
0
  mctelem_cookie_t mctc;
85
0
  struct mca_summary bs;
86
0
87
0
  mctc = mcheck_mca_logout(MCA_POLLER, mca_allbanks, &bs, NULL);
88
0
89
0
  if (bs.errcnt && mctc != NULL) {
90
0
    static uint64_t dumpcount = 0;
91
0
92
0
    /* If Dom0 enabled the VIRQ_MCA event, then notify it.
93
0
     * Otherwise, if dom0 has had plenty of time to register
94
0
     * the virq handler but still hasn't then dump telemetry
95
0
     * to the Xen console.  The call count may be incremented
96
0
     * on multiple cpus at once and is indicative only - just
97
0
     * a simple-minded attempt to avoid spamming the console
98
0
     * for corrected errors in early startup. */
99
0
100
0
    if (dom0_vmce_enabled()) {
101
0
      mctelem_commit(mctc);
102
0
      send_global_virq(VIRQ_MCA);
103
0
    } else if (++dumpcount >= 10) {
104
0
      x86_mcinfo_dump((struct mc_info *)mctelem_dataptr(mctc));
105
0
      mctelem_dismiss(mctc);
106
0
    } else {
107
0
      mctelem_dismiss(mctc);
108
0
    }
109
0
110
0
  } else if (mctc != NULL) {
111
0
    mctelem_dismiss(mctc);
112
0
  }
113
0
114
0
  /* adjust is global and all cpus may attempt to increment it without
115
0
   * synchronisation, so they race and the final adjust count
116
0
   * (number of cpus seeing any error) is approximate.  We can
117
0
   * guarantee that if any cpu observes an error that the
118
0
   * adjust count is at least 1. */
119
0
  if (bs.errcnt)
120
0
    adjust++;
121
0
}
122
123
/* polling service routine invoker:
124
 * Adjust poll frequency at runtime. No error means slow polling frequency,
125
 * an error means higher polling frequency.
126
 * It uses hw threshold register introduced in AMD K8 RevF to detect
127
 * multiple correctable errors between two polls. In that case,
128
 * increase polling frequency higher than normal.
129
 */
130
static void mce_amd_work_fn(void *data)
131
0
{
132
0
  on_each_cpu(mce_amd_checkregs, data, 1);
133
0
134
0
  if (adjust > 0) {
135
0
    if (!dom0_vmce_enabled()) {
136
0
      /* Dom0 did not enable VIRQ_MCA, so Xen is reporting. */
137
0
      printk("MCE: polling routine found correctable error. "
138
0
        " Use mcelog to parse above error output.\n");
139
0
    }
140
0
  }
141
0
142
0
  if (hw_threshold) {
143
0
    uint64_t value;
144
0
    uint32_t counter;
145
0
146
0
    value = mca_rdmsr(MSR_IA32_MCx_MISC(4));
147
0
    /* Only the error counter field is of interest
148
0
     * Bit field is described in AMD K8 BKDG chapter 6.4.5.5
149
0
     */
150
0
    counter = (value & 0xFFF00000000ULL) >> 32U;
151
0
152
0
    /* HW does not count *all* kinds of correctable errors.
153
0
     * Thus it is possible, that the polling routine finds an
154
0
     * correctable error even if the HW reports nothing. */
155
0
    if (counter > 0) {
156
0
      /* HW reported correctable errors,
157
0
       * the polling routine did not find...
158
0
       */
159
0
      if (adjust == 0) {
160
0
        printk("CPU counter reports %"PRIu32
161
0
          " correctable hardware error%s that %s"
162
0
          " not reported by the status MSRs\n",
163
0
          counter,
164
0
          (counter == 1 ? "" : "s"),
165
0
          (counter == 1 ? "was" : "were"));
166
0
      }
167
0
      /* subtract 1 to not double count the error
168
0
       * from the polling service routine */
169
0
      adjust += (counter - 1);
170
0
171
0
      /* Restart counter */
172
0
      /* No interrupt, reset counter value */
173
0
      value &= ~(0x60FFF00000000ULL);
174
0
      /* Counter enable */
175
0
      value |= (1ULL << 51);
176
0
      mca_wrmsr(MSR_IA32_MCx_MISC(4), value);
177
0
    }
178
0
  }
179
0
180
0
  if (variable_period && adjust > 0) {
181
0
    /* Increase polling frequency */
182
0
    adjust++; /* adjust == 1 must have an effect */
183
0
    period /= adjust;
184
0
  } else if (variable_period) {
185
0
    /* Decrease polling frequency */
186
0
    period *= 2;
187
0
  }
188
0
  if (variable_period && period > MCE_MAX) {
189
0
    /* limit: Poll at least every 30s */
190
0
    period = MCE_MAX;
191
0
  }
192
0
  if (variable_period && period < MCE_MIN) {
193
0
    /* limit: Poll every 2s.
194
0
     * When this is reached an uncorrectable error
195
0
     * is expected to happen, if Dom0 does nothing.
196
0
     */
197
0
    period = MCE_MIN;
198
0
  }
199
0
200
0
  set_timer(&mce_timer, NOW() + period);
201
0
  adjust = 0;
202
0
}
203
204
void __init amd_nonfatal_mcheck_init(struct cpuinfo_x86 *c)
205
0
{
206
0
  if (c->x86_vendor != X86_VENDOR_AMD)
207
0
    return;
208
0
209
0
  /* Assume we are on K8 or newer AMD CPU here */
210
0
211
0
  /* The threshold bitfields in MSR_IA32_MC4_MISC has
212
0
   * been introduced along with the SVME feature bit. */
213
0
  if (variable_period && cpu_has(c, X86_FEATURE_SVM)) {
214
0
    uint64_t value;
215
0
216
0
    /* hw threshold registers present */
217
0
    hw_threshold = 1;
218
0
    rdmsrl(MSR_IA32_MCx_MISC(4), value);
219
0
220
0
    if (value & (1ULL << 61)) { /* Locked bit */
221
0
      /* Locked by BIOS. Not available for use */
222
0
      hw_threshold = 0;
223
0
    }
224
0
    if (!(value & (1ULL << 63))) { /* Valid bit */
225
0
      /* No CtrP present */
226
0
      hw_threshold = 0;
227
0
    } else {
228
0
      if (!(value & (1ULL << 62))) { /* Counter Bit */
229
0
        /* No counter field present */
230
0
        hw_threshold = 0;
231
0
      }
232
0
    }
233
0
234
0
    if (hw_threshold) {
235
0
      /* No interrupt, reset counter value */
236
0
      value &= ~(0x60FFF00000000ULL);
237
0
      /* Counter enable */
238
0
      value |= (1ULL << 51);
239
0
      wrmsrl(MSR_IA32_MCx_MISC(4), value);
240
0
      printk(XENLOG_INFO "MCA: Use hw thresholding to adjust polling frequency\n");
241
0
    }
242
0
  }
243
0
244
0
  init_timer(&mce_timer, mce_amd_work_fn, NULL, 0);
245
0
  set_timer(&mce_timer, NOW() + period);
246
0
}