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, guid: &adxl_guid, ADXL_REVISION, |
44 | func: cmd, argv4: 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, argv: 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, pathname: path, ret_handle: &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, name: "_DSM" )) { |
152 | pr_info("No DSM method\n" ); |
153 | return -ENODEV; |
154 | } |
155 | |
156 | if (!acpi_check_dsm(handle, guid: &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(n: adxl_count + 1, size: 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); |
193 | |