aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTony Luck <tony.luck@intel.com>2018-10-15 19:11:31 -0400
committerBorislav Petkov <bp@suse.de>2018-10-16 04:03:00 -0400
commit4cf841e398503990df640f7a7c5b2ea56f11c08c (patch)
tree9dc246175a02518b6dc87ef923486bedfdf5de64
parentd8c27ba86a2fd806d3957e5a9b30e66dfca2a61d (diff)
ACPI/ADXL: Add address translation interface using an ACPI DSM
Some new Intel servers provide an interface so that the OS can ask the BIOS to translate a system physical address to a memory address (socket, memory controller, channel, rank, dimm, etc.). This is useful for EDAC drivers that want to take the address of an error reported in a machine check bank and let the user know which DIMM may need to be replaced. Specification for this interface is available at: https://cdrdv2.intel.com/v1/dl/getContent/603354 [ Based on earlier code by Qiuxu Zhuo <qiuxu.zhuo@intel.com>. ] [ bp: Make the first pr_info() in adxl_init() pr_debug() so that it doesn't pollute every dmesg. ] Signed-off-by: Tony Luck <tony.luck@intel.com> Signed-off-by: Borislav Petkov <bp@suse.de> Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Tested-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com> CC: Len Brown <lenb@kernel.org> CC: linux-acpi@vger.kernel.org CC: linux-edac@vger.kernel.org Link: http://lkml.kernel.org/r/20181015202620.23610-1-tony.luck@intel.com
-rw-r--r--drivers/acpi/Kconfig3
-rw-r--r--drivers/acpi/Makefile3
-rw-r--r--drivers/acpi/acpi_adxl.c192
-rw-r--r--include/linux/adxl.h13
4 files changed, 211 insertions, 0 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index dd1eea90f67f..09991cc91b89 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -498,6 +498,9 @@ config ACPI_EXTLOG
498 driver adds support for that functionality with corresponding 498 driver adds support for that functionality with corresponding
499 tracepoint which carries that information to userspace. 499 tracepoint which carries that information to userspace.
500 500
501config ACPI_ADXL
502 bool
503
501menuconfig PMIC_OPREGION 504menuconfig PMIC_OPREGION
502 bool "PMIC (Power Management Integrated Circuit) operation region support" 505 bool "PMIC (Power Management Integrated Circuit) operation region support"
503 help 506 help
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 6d59aa109a91..edc039313cd6 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -61,6 +61,9 @@ acpi-$(CONFIG_ACPI_LPIT) += acpi_lpit.o
61acpi-$(CONFIG_ACPI_GENERIC_GSI) += irq.o 61acpi-$(CONFIG_ACPI_GENERIC_GSI) += irq.o
62acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o 62acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o
63 63
64# Address translation
65acpi-$(CONFIG_ACPI_ADXL) += acpi_adxl.o
66
64# These are (potentially) separate modules 67# These are (potentially) separate modules
65 68
66# IPMI may be used by other drivers, so it has to initialise before them 69# IPMI may be used by other drivers, so it has to initialise before them
diff --git a/drivers/acpi/acpi_adxl.c b/drivers/acpi/acpi_adxl.c
new file mode 100644
index 000000000000..13c8f7b50c46
--- /dev/null
+++ b/drivers/acpi/acpi_adxl.c
@@ -0,0 +1,192 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Address translation interface via ACPI DSM.
4 * Copyright (C) 2018 Intel Corporation
5 *
6 * Specification for this interface is available at:
7 *
8 * https://cdrdv2.intel.com/v1/dl/getContent/603354
9 */
10
11#include <linux/acpi.h>
12#include <linux/adxl.h>
13
14#define ADXL_REVISION 0x1
15#define ADXL_IDX_GET_ADDR_PARAMS 0x1
16#define ADXL_IDX_FORWARD_TRANSLATE 0x2
17#define ACPI_ADXL_PATH "\\_SB.ADXL"
18
19/*
20 * The specification doesn't provide a limit on how many
21 * components are in a memory address. But since we allocate
22 * memory based on the number the BIOS tells us, we should
23 * defend against insane values.
24 */
25#define ADXL_MAX_COMPONENTS 500
26
27#undef pr_fmt
28#define pr_fmt(fmt) "ADXL: " fmt
29
30static acpi_handle handle;
31static union acpi_object *params;
32static const guid_t adxl_guid =
33 GUID_INIT(0xAA3C050A, 0x7EA4, 0x4C1F,
34 0xAF, 0xDA, 0x12, 0x67, 0xDF, 0xD3, 0xD4, 0x8D);
35
36static int adxl_count;
37static char **adxl_component_names;
38
39static union acpi_object *adxl_dsm(int cmd, union acpi_object argv[])
40{
41 union acpi_object *obj, *o;
42
43 obj = acpi_evaluate_dsm_typed(handle, &adxl_guid, ADXL_REVISION,
44 cmd, argv, ACPI_TYPE_PACKAGE);
45 if (!obj) {
46 pr_info("DSM call failed for cmd=%d\n", cmd);
47 return NULL;
48 }
49
50 if (obj->package.count != 2) {
51 pr_info("Bad pkg count %d\n", obj->package.count);
52 goto err;
53 }
54
55 o = obj->package.elements;
56 if (o->type != ACPI_TYPE_INTEGER) {
57 pr_info("Bad 1st element type %d\n", o->type);
58 goto err;
59 }
60 if (o->integer.value) {
61 pr_info("Bad ret val %llu\n", o->integer.value);
62 goto err;
63 }
64
65 o = obj->package.elements + 1;
66 if (o->type != ACPI_TYPE_PACKAGE) {
67 pr_info("Bad 2nd element type %d\n", o->type);
68 goto err;
69 }
70 return obj;
71
72err:
73 ACPI_FREE(obj);
74 return NULL;
75}
76
77/**
78 * adxl_get_component_names - get list of memory component names
79 * Returns NULL terminated list of string names
80 *
81 * Give the caller a pointer to the list of memory component names
82 * e.g. { "SystemAddress", "ProcessorSocketId", "ChannelId", ... NULL }
83 * Caller should count how many strings in order to allocate a buffer
84 * for the return from adxl_decode().
85 */
86const char * const *adxl_get_component_names(void)
87{
88 return (const char * const *)adxl_component_names;
89}
90EXPORT_SYMBOL_GPL(adxl_get_component_names);
91
92/**
93 * adxl_decode - ask BIOS to decode a system address to memory address
94 * @addr: the address to decode
95 * @component_values: pointer to array of values for each component
96 * Returns 0 on success, negative error code otherwise
97 *
98 * The index of each value returned in the array matches the index of
99 * each component name returned by adxl_get_component_names().
100 * Components that are not defined for this address translation (e.g.
101 * mirror channel number for a non-mirrored address) are set to ~0ull.
102 */
103int adxl_decode(u64 addr, u64 component_values[])
104{
105 union acpi_object argv4[2], *results, *r;
106 int i, cnt;
107
108 if (!adxl_component_names)
109 return -EOPNOTSUPP;
110
111 argv4[0].type = ACPI_TYPE_PACKAGE;
112 argv4[0].package.count = 1;
113 argv4[0].package.elements = &argv4[1];
114 argv4[1].integer.type = ACPI_TYPE_INTEGER;
115 argv4[1].integer.value = addr;
116
117 results = adxl_dsm(ADXL_IDX_FORWARD_TRANSLATE, argv4);
118 if (!results)
119 return -EINVAL;
120
121 r = results->package.elements + 1;
122 cnt = r->package.count;
123 if (cnt != adxl_count) {
124 ACPI_FREE(results);
125 return -EINVAL;
126 }
127 r = r->package.elements;
128
129 for (i = 0; i < cnt; i++)
130 component_values[i] = r[i].integer.value;
131
132 ACPI_FREE(results);
133
134 return 0;
135}
136EXPORT_SYMBOL_GPL(adxl_decode);
137
138static int __init adxl_init(void)
139{
140 char *path = ACPI_ADXL_PATH;
141 union acpi_object *p;
142 acpi_status status;
143 int i;
144
145 status = acpi_get_handle(NULL, path, &handle);
146 if (ACPI_FAILURE(status)) {
147 pr_debug("No ACPI handle for path %s\n", path);
148 return -ENODEV;
149 }
150
151 if (!acpi_has_method(handle, "_DSM")) {
152 pr_info("No DSM method\n");
153 return -ENODEV;
154 }
155
156 if (!acpi_check_dsm(handle, &adxl_guid, ADXL_REVISION,
157 ADXL_IDX_GET_ADDR_PARAMS |
158 ADXL_IDX_FORWARD_TRANSLATE)) {
159 pr_info("DSM method does not support forward translate\n");
160 return -ENODEV;
161 }
162
163 params = adxl_dsm(ADXL_IDX_GET_ADDR_PARAMS, NULL);
164 if (!params) {
165 pr_info("Failed to get component names\n");
166 return -ENODEV;
167 }
168
169 p = params->package.elements + 1;
170 adxl_count = p->package.count;
171 if (adxl_count > ADXL_MAX_COMPONENTS) {
172 pr_info("Insane number of address component names %d\n", adxl_count);
173 ACPI_FREE(params);
174 return -ENODEV;
175 }
176 p = p->package.elements;
177
178 /*
179 * Allocate one extra for NULL termination.
180 */
181 adxl_component_names = kcalloc(adxl_count + 1, sizeof(char *), GFP_KERNEL);
182 if (!adxl_component_names) {
183 ACPI_FREE(params);
184 return -ENOMEM;
185 }
186
187 for (i = 0; i < adxl_count; i++)
188 adxl_component_names[i] = p[i].string.pointer;
189
190 return 0;
191}
192subsys_initcall(adxl_init);
diff --git a/include/linux/adxl.h b/include/linux/adxl.h
new file mode 100644
index 000000000000..2a629acb4c3f
--- /dev/null
+++ b/include/linux/adxl.h
@@ -0,0 +1,13 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Address translation interface via ACPI DSM.
4 * Copyright (C) 2018 Intel Corporation
5 */
6
7#ifndef _LINUX_ADXL_H
8#define _LINUX_ADXL_H
9
10const char * const *adxl_get_component_names(void);
11int adxl_decode(u64 addr, u64 component_values[]);
12
13#endif /* _LINUX_ADXL_H */