Merge branch 'fix' of git://git.kernel.org/pub/scm/linux/kernel/git/ycmiao/pxa-linux-2.6
[pandora-kernel.git] / drivers / acpi / apei / hest.c
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, write to the Free Software
27  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28  */
29
30 #include <linux/kernel.h>
31 #include <linux/module.h>
32 #include <linux/init.h>
33 #include <linux/acpi.h>
34 #include <linux/kdebug.h>
35 #include <linux/highmem.h>
36 #include <linux/io.h>
37 #include <acpi/apei.h>
38
39 #include "apei-internal.h"
40
41 #define HEST_PFX "HEST: "
42
43 int hest_disable;
44 EXPORT_SYMBOL_GPL(hest_disable);
45
46 /* HEST table parsing */
47
48 static struct acpi_table_hest *hest_tab;
49
50 static int hest_void_parse(struct acpi_hest_header *hest_hdr, void *data)
51 {
52         return 0;
53 }
54
55 static int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = {
56         [ACPI_HEST_TYPE_IA32_CHECK] = -1,       /* need further calculation */
57         [ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1,
58         [ACPI_HEST_TYPE_IA32_NMI] = sizeof(struct acpi_hest_ia_nmi),
59         [ACPI_HEST_TYPE_AER_ROOT_PORT] = sizeof(struct acpi_hest_aer_root),
60         [ACPI_HEST_TYPE_AER_ENDPOINT] = sizeof(struct acpi_hest_aer),
61         [ACPI_HEST_TYPE_AER_BRIDGE] = sizeof(struct acpi_hest_aer_bridge),
62         [ACPI_HEST_TYPE_GENERIC_ERROR] = sizeof(struct acpi_hest_generic),
63 };
64
65 static int hest_esrc_len(struct acpi_hest_header *hest_hdr)
66 {
67         u16 hest_type = hest_hdr->type;
68         int len;
69
70         if (hest_type >= ACPI_HEST_TYPE_RESERVED)
71                 return 0;
72
73         len = hest_esrc_len_tab[hest_type];
74
75         if (hest_type == ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) {
76                 struct acpi_hest_ia_corrected *cmc;
77                 cmc = (struct acpi_hest_ia_corrected *)hest_hdr;
78                 len = sizeof(*cmc) + cmc->num_hardware_banks *
79                         sizeof(struct acpi_hest_ia_error_bank);
80         } else if (hest_type == ACPI_HEST_TYPE_IA32_CHECK) {
81                 struct acpi_hest_ia_machine_check *mc;
82                 mc = (struct acpi_hest_ia_machine_check *)hest_hdr;
83                 len = sizeof(*mc) + mc->num_hardware_banks *
84                         sizeof(struct acpi_hest_ia_error_bank);
85         }
86         BUG_ON(len == -1);
87
88         return len;
89 };
90
91 int apei_hest_parse(apei_hest_func_t func, void *data)
92 {
93         struct acpi_hest_header *hest_hdr;
94         int i, rc, len;
95
96         if (hest_disable)
97                 return -EINVAL;
98
99         hest_hdr = (struct acpi_hest_header *)(hest_tab + 1);
100         for (i = 0; i < hest_tab->error_source_count; i++) {
101                 len = hest_esrc_len(hest_hdr);
102                 if (!len) {
103                         pr_warning(FW_WARN HEST_PFX
104                                    "Unknown or unused hardware error source "
105                                    "type: %d for hardware error source: %d.\n",
106                                    hest_hdr->type, hest_hdr->source_id);
107                         return -EINVAL;
108                 }
109                 if ((void *)hest_hdr + len >
110                     (void *)hest_tab + hest_tab->header.length) {
111                         pr_warning(FW_BUG HEST_PFX
112                 "Table contents overflow for hardware error source: %d.\n",
113                                 hest_hdr->source_id);
114                         return -EINVAL;
115                 }
116
117                 rc = func(hest_hdr, data);
118                 if (rc)
119                         return rc;
120
121                 hest_hdr = (void *)hest_hdr + len;
122         }
123
124         return 0;
125 }
126 EXPORT_SYMBOL_GPL(apei_hest_parse);
127
128 static int __init setup_hest_disable(char *str)
129 {
130         hest_disable = 1;
131         return 0;
132 }
133
134 __setup("hest_disable", setup_hest_disable);
135
136 static int __init hest_init(void)
137 {
138         acpi_status status;
139         int rc = -ENODEV;
140
141         if (acpi_disabled)
142                 goto err;
143
144         if (hest_disable) {
145                 pr_info(HEST_PFX "HEST tabling parsing is disabled.\n");
146                 goto err;
147         }
148
149         status = acpi_get_table(ACPI_SIG_HEST, 0,
150                                 (struct acpi_table_header **)&hest_tab);
151         if (status == AE_NOT_FOUND) {
152                 pr_info(HEST_PFX "Table is not found!\n");
153                 goto err;
154         } else if (ACPI_FAILURE(status)) {
155                 const char *msg = acpi_format_exception(status);
156                 pr_err(HEST_PFX "Failed to get table, %s\n", msg);
157                 rc = -EINVAL;
158                 goto err;
159         }
160
161         rc = apei_hest_parse(hest_void_parse, NULL);
162         if (rc)
163                 goto err;
164
165         pr_info(HEST_PFX "HEST table parsing is initialized.\n");
166
167         return 0;
168 err:
169         hest_disable = 1;
170         return rc;
171 }
172
173 subsys_initcall(hest_init);