Coverage Report

Created: 2017-10-25 09:10

/root/src/xen/xen/drivers/acpi/apei/hest.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * APEI Hardware Error Souce Table support
3
 *
4
 * HEST describes error sources in detail; communicates operational
5
 * parameters (i.e. severity levels, masking bits, and threshold
6
 * values) to Linux as necessary. It also allows the BIOS to report
7
 * non-standard error sources to Linux (for example, chipset-specific
8
 * error registers).
9
 *
10
 * For more information about HEST, please refer to ACPI Specification
11
 * version 4.0, section 17.3.2.
12
 *
13
 * Copyright 2009 Intel Corp.
14
 *   Author: Huang Ying <ying.huang@intel.com>
15
 *
16
 * This program is free software; you can redistribute it and/or
17
 * modify it under the terms of the GNU General Public License version
18
 * 2 as published by the Free Software Foundation;
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program; If not, see <http://www.gnu.org/licenses/>.
27
 */
28
29
#include <xen/errno.h>
30
#include <xen/init.h>
31
#include <xen/kernel.h>
32
#include <xen/mm.h>
33
#include <xen/pfn.h>
34
#include <acpi/acpi.h>
35
#include <acpi/apei.h>
36
37
#include "apei-internal.h"
38
39
#define HEST_PFX "HEST: "
40
41
static bool_t hest_disable;
42
boolean_param("hest_disable", hest_disable);
43
44
/* HEST table parsing */
45
46
static struct acpi_table_hest *__read_mostly hest_tab;
47
48
static const int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = {
49
  [ACPI_HEST_TYPE_IA32_CHECK] = -1, /* need further calculation */
50
  [ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1,
51
  [ACPI_HEST_TYPE_IA32_NMI] = sizeof(struct acpi_hest_ia_nmi),
52
  [ACPI_HEST_TYPE_AER_ROOT_PORT] = sizeof(struct acpi_hest_aer_root),
53
  [ACPI_HEST_TYPE_AER_ENDPOINT] = sizeof(struct acpi_hest_aer),
54
  [ACPI_HEST_TYPE_AER_BRIDGE] = sizeof(struct acpi_hest_aer_bridge),
55
  [ACPI_HEST_TYPE_GENERIC_ERROR] = sizeof(struct acpi_hest_generic),
56
};
57
58
static int hest_esrc_len(const struct acpi_hest_header *hest_hdr)
59
0
{
60
0
  u16 hest_type = hest_hdr->type;
61
0
  int len;
62
0
63
0
  if (hest_type >= ACPI_HEST_TYPE_RESERVED)
64
0
    return 0;
65
0
66
0
  len = hest_esrc_len_tab[hest_type];
67
0
68
0
  if (hest_type == ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) {
69
0
    const struct acpi_hest_ia_corrected *cmc =
70
0
      container_of(hest_hdr,
71
0
             const struct acpi_hest_ia_corrected,
72
0
             header);
73
0
74
0
    len = sizeof(*cmc) + cmc->num_hardware_banks *
75
0
          sizeof(struct acpi_hest_ia_error_bank);
76
0
  } else if (hest_type == ACPI_HEST_TYPE_IA32_CHECK) {
77
0
    const struct acpi_hest_ia_machine_check *mc =
78
0
      container_of(hest_hdr,
79
0
             const struct acpi_hest_ia_machine_check,
80
0
             header);
81
0
82
0
    len = sizeof(*mc) + mc->num_hardware_banks *
83
0
          sizeof(struct acpi_hest_ia_error_bank);
84
0
  }
85
0
  BUG_ON(len == -1);
86
0
87
0
  return len;
88
0
};
89
90
int apei_hest_parse(apei_hest_func_t func, void *data)
91
0
{
92
0
  struct acpi_hest_header *hest_hdr;
93
0
  int i, rc, len;
94
0
95
0
  if (hest_disable || !hest_tab)
96
0
    return -EINVAL;
97
0
98
0
  hest_hdr = (struct acpi_hest_header *)(hest_tab + 1);
99
0
  for (i = 0; i < hest_tab->error_source_count; i++) {
100
0
    len = hest_esrc_len(hest_hdr);
101
0
    if (!len) {
102
0
      printk(XENLOG_WARNING HEST_PFX
103
0
             "Unknown or unused hardware error source "
104
0
             "type: %d for hardware error source: %d\n",
105
0
             hest_hdr->type, hest_hdr->source_id);
106
0
      return -EINVAL;
107
0
    }
108
0
    if ((void *)hest_hdr + len >
109
0
        (void *)hest_tab + hest_tab->header.length) {
110
0
      printk(XENLOG_WARNING HEST_PFX
111
0
             "Table contents overflow for hardware error source: %d\n",
112
0
             hest_hdr->source_id);
113
0
      return -EINVAL;
114
0
    }
115
0
116
0
    rc = func(hest_hdr, data);
117
0
    if (rc)
118
0
      return rc;
119
0
120
0
    hest_hdr = (void *)hest_hdr + len;
121
0
  }
122
0
123
0
  return 0;
124
0
}
125
126
/*
127
 * Check if firmware advertises firmware first mode. We need FF bit to be set
128
 * along with a set of MC banks which work in FF mode.
129
 */
130
static int __init hest_parse_cmc(const struct acpi_hest_header *hest_hdr,
131
         void *data)
132
0
{
133
0
#ifdef CONFIG_X86_MCE
134
0
  unsigned int i;
135
0
  const struct acpi_hest_ia_corrected *cmc;
136
0
  const struct acpi_hest_ia_error_bank *mc_bank;
137
0
138
0
  if (hest_hdr->type != ACPI_HEST_TYPE_IA32_CORRECTED_CHECK)
139
0
    return 0;
140
0
141
0
  cmc = container_of(hest_hdr, const struct acpi_hest_ia_corrected, header);
142
0
  if (!cmc->enabled)
143
0
    return 0;
144
0
145
0
  /*
146
0
   * We expect HEST to provide a list of MC banks that report errors
147
0
   * in firmware first mode. Otherwise, return non-zero value to
148
0
   * indicate that we are done parsing HEST.
149
0
   */
150
0
  if (!(cmc->flags & ACPI_HEST_FIRMWARE_FIRST) || !cmc->num_hardware_banks)
151
0
    return 1;
152
0
153
0
  printk(XENLOG_INFO HEST_PFX "Enabling Firmware First mode for corrected errors.\n");
154
0
155
0
  mc_bank = (const struct acpi_hest_ia_error_bank *)(cmc + 1);
156
0
  for (i = 0; i < cmc->num_hardware_banks; i++, mc_bank++)
157
0
    mce_disable_bank(mc_bank->bank_number);
158
0
#else
159
1
# define acpi_disable_cmcff 1
160
0
#endif
161
0
162
0
  return 1;
163
0
}
164
165
void __init acpi_hest_init(void)
166
1
{
167
1
  acpi_status status;
168
1
  acpi_physical_address hest_addr;
169
1
  acpi_native_uint hest_len;
170
1
171
1
  if (acpi_disabled)
172
0
    return;
173
1
174
1
  if (hest_disable) {
175
0
    printk(XENLOG_INFO HEST_PFX "Table parsing disabled.\n");
176
0
    return;
177
0
  }
178
1
179
1
  status = acpi_get_table_phys(ACPI_SIG_HEST, 0, &hest_addr, &hest_len);
180
1
  if (status == AE_NOT_FOUND)
181
0
    goto err;
182
1
  if (ACPI_FAILURE(status)) {
183
0
    printk(XENLOG_ERR HEST_PFX "Failed to get table, %s\n",
184
0
           acpi_format_exception(status));
185
0
    goto err;
186
0
  }
187
1
  map_pages_to_xen((unsigned long)__va(hest_addr), PFN_DOWN(hest_addr),
188
1
       PFN_UP(hest_addr + hest_len) - PFN_DOWN(hest_addr),
189
1
       PAGE_HYPERVISOR);
190
1
  hest_tab = __va(hest_addr);
191
1
192
1
  if (!acpi_disable_cmcff)
193
0
    apei_hest_parse(hest_parse_cmc, NULL);
194
1
195
1
  printk(XENLOG_INFO HEST_PFX "Table parsing has been initialized\n");
196
1
  return;
197
0
err:
198
0
  hest_disable = 1;
199
0
}