Commit | Line | Data |
---|---|---|
4cf841e3 TL |
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 | ||
30 | static acpi_handle handle; | |
31 | static union acpi_object *params; | |
32 | static const guid_t adxl_guid = | |
33 | GUID_INIT(0xAA3C050A, 0x7EA4, 0x4C1F, | |
34 | 0xAF, 0xDA, 0x12, 0x67, 0xDF, 0xD3, 0xD4, 0x8D); | |
35 | ||
36 | static int adxl_count; | |
37 | static char **adxl_component_names; | |
38 | ||
39 | static 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 | ||
72 | err: | |
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 | */ | |
86 | const char * const *adxl_get_component_names(void) | |
87 | { | |
88 | return (const char * const *)adxl_component_names; | |
89 | } | |
90 | EXPORT_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 | */ | |
103 | int 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 | } | |
136 | EXPORT_SYMBOL_GPL(adxl_decode); | |
137 | ||
138 | static 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 | } | |
192 | subsys_initcall(adxl_init); |