ACPI / TPM: match node name instead of full path when searching for TPM device
[deliverable/linux.git] / drivers / char / tpm / tpm_ppi.c
CommitLineData
f84fdff0
XZ
1#include <linux/acpi.h>
2#include <acpi/acpi_drivers.h>
3#include "tpm.h"
4
5static const u8 tpm_ppi_uuid[] = {
6 0xA6, 0xFA, 0xDD, 0x3D,
7 0x1B, 0x36,
8 0xB4, 0x4E,
9 0xA4, 0x24,
10 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53
11};
12static char *tpm_device_name = "TPM";
13
14#define TPM_PPI_REVISION_ID 1
15#define TPM_PPI_FN_VERSION 1
16#define TPM_PPI_FN_SUBREQ 2
17#define TPM_PPI_FN_GETREQ 3
18#define TPM_PPI_FN_GETACT 4
19#define TPM_PPI_FN_GETRSP 5
20#define TPM_PPI_FN_SUBREQ2 7
21#define TPM_PPI_FN_GETOPR 8
22#define PPI_TPM_REQ_MAX 22
23#define PPI_VS_REQ_START 128
24#define PPI_VS_REQ_END 255
25#define PPI_VERSION_LEN 3
26
27static acpi_status ppi_callback(acpi_handle handle, u32 level, void *context,
28 void **return_value)
29{
df45c712 30 acpi_status status = AE_OK;
f84fdff0 31 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
df45c712 32
529139c9 33 if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer))) {
df45c712
JL
34 if (strstr(buffer.pointer, context) != NULL) {
35 *return_value = handle;
36 status = AE_CTRL_TERMINATE;
37 }
f84fdff0 38 kfree(buffer.pointer);
f84fdff0 39 }
df45c712
JL
40
41 return status;
f84fdff0
XZ
42}
43
44static inline void ppi_assign_params(union acpi_object params[4],
45 u64 function_num)
46{
47 params[0].type = ACPI_TYPE_BUFFER;
48 params[0].buffer.length = sizeof(tpm_ppi_uuid);
49 params[0].buffer.pointer = (char *)tpm_ppi_uuid;
50 params[1].type = ACPI_TYPE_INTEGER;
51 params[1].integer.value = TPM_PPI_REVISION_ID;
52 params[2].type = ACPI_TYPE_INTEGER;
53 params[2].integer.value = function_num;
54 params[3].type = ACPI_TYPE_PACKAGE;
55 params[3].package.count = 0;
56 params[3].package.elements = NULL;
57}
58
81198078
XZ
59static ssize_t tpm_show_ppi_version(struct device *dev,
60 struct device_attribute *attr, char *buf)
f84fdff0
XZ
61{
62 acpi_handle handle;
63 acpi_status status;
64 struct acpi_object_list input;
65 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
66 union acpi_object params[4];
67 union acpi_object *obj;
68
69 input.count = 4;
70 ppi_assign_params(params, TPM_PPI_FN_VERSION);
71 input.pointer = params;
72 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
73 ACPI_UINT32_MAX, ppi_callback, NULL,
74 tpm_device_name, &handle);
75 if (ACPI_FAILURE(status))
76 return -ENXIO;
77
78 status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
79 ACPI_TYPE_STRING);
80 if (ACPI_FAILURE(status))
81 return -ENOMEM;
82 obj = (union acpi_object *)output.pointer;
83 status = scnprintf(buf, PAGE_SIZE, "%s\n", obj->string.pointer);
84 kfree(output.pointer);
85 return status;
86}
87
81198078
XZ
88static ssize_t tpm_show_ppi_request(struct device *dev,
89 struct device_attribute *attr, char *buf)
f84fdff0
XZ
90{
91 acpi_handle handle;
92 acpi_status status;
93 struct acpi_object_list input;
94 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
95 union acpi_object params[4];
96 union acpi_object *ret_obj;
97
98 input.count = 4;
99 ppi_assign_params(params, TPM_PPI_FN_GETREQ);
100 input.pointer = params;
101 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
102 ACPI_UINT32_MAX, ppi_callback, NULL,
103 tpm_device_name, &handle);
104 if (ACPI_FAILURE(status))
105 return -ENXIO;
106
107 status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
108 ACPI_TYPE_PACKAGE);
109 if (ACPI_FAILURE(status))
110 return -ENOMEM;
111 /*
112 * output.pointer should be of package type, including two integers.
113 * The first is function return code, 0 means success and 1 means
114 * error. The second is pending TPM operation requested by the OS, 0
115 * means none and >0 means operation value.
116 */
117 ret_obj = ((union acpi_object *)output.pointer)->package.elements;
118 if (ret_obj->type == ACPI_TYPE_INTEGER) {
119 if (ret_obj->integer.value) {
120 status = -EFAULT;
121 goto cleanup;
122 }
123 ret_obj++;
124 if (ret_obj->type == ACPI_TYPE_INTEGER)
125 status = scnprintf(buf, PAGE_SIZE, "%llu\n",
126 ret_obj->integer.value);
127 else
128 status = -EINVAL;
129 } else {
130 status = -EINVAL;
131 }
132cleanup:
133 kfree(output.pointer);
134 return status;
135}
136
81198078
XZ
137static ssize_t tpm_store_ppi_request(struct device *dev,
138 struct device_attribute *attr,
139 const char *buf, size_t count)
f84fdff0
XZ
140{
141 char version[PPI_VERSION_LEN + 1];
142 acpi_handle handle;
143 acpi_status status;
144 struct acpi_object_list input;
145 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
146 union acpi_object params[4];
147 union acpi_object obj;
148 u32 req;
149 u64 ret;
150
151 input.count = 4;
152 ppi_assign_params(params, TPM_PPI_FN_VERSION);
153 input.pointer = params;
154 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
155 ACPI_UINT32_MAX, ppi_callback, NULL,
156 tpm_device_name, &handle);
157 if (ACPI_FAILURE(status))
158 return -ENXIO;
159
160 status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
161 ACPI_TYPE_STRING);
162 if (ACPI_FAILURE(status))
163 return -ENOMEM;
e361200b 164 strlcpy(version,
f84fdff0 165 ((union acpi_object *)output.pointer)->string.pointer,
e361200b 166 PPI_VERSION_LEN + 1);
f84fdff0
XZ
167 kfree(output.pointer);
168 output.length = ACPI_ALLOCATE_BUFFER;
169 output.pointer = NULL;
170 /*
171 * the function to submit TPM operation request to pre-os environment
172 * is updated with function index from SUBREQ to SUBREQ2 since PPI
173 * version 1.1
174 */
175 if (strcmp(version, "1.1") == -1)
176 params[2].integer.value = TPM_PPI_FN_SUBREQ;
177 else
178 params[2].integer.value = TPM_PPI_FN_SUBREQ2;
179 /*
180 * PPI spec defines params[3].type as ACPI_TYPE_PACKAGE. Some BIOS
181 * accept buffer/string/integer type, but some BIOS accept buffer/
182 * string/package type. For PPI version 1.0 and 1.1, use buffer type
183 * for compatibility, and use package type since 1.2 according to spec.
184 */
185 if (strcmp(version, "1.2") == -1) {
186 params[3].type = ACPI_TYPE_BUFFER;
187 params[3].buffer.length = sizeof(req);
188 sscanf(buf, "%d", &req);
189 params[3].buffer.pointer = (char *)&req;
190 } else {
191 params[3].package.count = 1;
192 obj.type = ACPI_TYPE_INTEGER;
193 sscanf(buf, "%llu", &obj.integer.value);
194 params[3].package.elements = &obj;
195 }
196
197 status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
198 ACPI_TYPE_INTEGER);
199 if (ACPI_FAILURE(status))
200 return -ENOMEM;
201 ret = ((union acpi_object *)output.pointer)->integer.value;
202 if (ret == 0)
203 status = (acpi_status)count;
204 else if (ret == 1)
205 status = -EPERM;
206 else
207 status = -EFAULT;
208 kfree(output.pointer);
209 return status;
210}
211
81198078
XZ
212static ssize_t tpm_show_ppi_transition_action(struct device *dev,
213 struct device_attribute *attr,
214 char *buf)
f84fdff0
XZ
215{
216 char version[PPI_VERSION_LEN + 1];
217 acpi_handle handle;
218 acpi_status status;
219 struct acpi_object_list input;
220 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
221 union acpi_object params[4];
222 u32 ret;
223 char *info[] = {
224 "None",
225 "Shutdown",
226 "Reboot",
227 "OS Vendor-specific",
228 "Error",
229 };
230 input.count = 4;
231 ppi_assign_params(params, TPM_PPI_FN_VERSION);
232 input.pointer = params;
233 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
234 ACPI_UINT32_MAX, ppi_callback, NULL,
235 tpm_device_name, &handle);
236 if (ACPI_FAILURE(status))
237 return -ENXIO;
238
239 status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
240 ACPI_TYPE_STRING);
241 if (ACPI_FAILURE(status))
242 return -ENOMEM;
e361200b 243 strlcpy(version,
f84fdff0 244 ((union acpi_object *)output.pointer)->string.pointer,
e361200b 245 PPI_VERSION_LEN + 1);
f84fdff0
XZ
246 /*
247 * PPI spec defines params[3].type as empty package, but some platforms
248 * (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for
249 * compatibility, define params[3].type as buffer, if PPI version < 1.2
250 */
251 if (strcmp(version, "1.2") == -1) {
252 params[3].type = ACPI_TYPE_BUFFER;
253 params[3].buffer.length = 0;
254 params[3].buffer.pointer = NULL;
255 }
256 params[2].integer.value = TPM_PPI_FN_GETACT;
257 kfree(output.pointer);
258 output.length = ACPI_ALLOCATE_BUFFER;
259 output.pointer = NULL;
260 status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
261 ACPI_TYPE_INTEGER);
262 if (ACPI_FAILURE(status))
263 return -ENOMEM;
264 ret = ((union acpi_object *)output.pointer)->integer.value;
265 if (ret < ARRAY_SIZE(info) - 1)
266 status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]);
267 else
268 status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret,
269 info[ARRAY_SIZE(info)-1]);
270 kfree(output.pointer);
271 return status;
272}
273
81198078
XZ
274static ssize_t tpm_show_ppi_response(struct device *dev,
275 struct device_attribute *attr,
276 char *buf)
f84fdff0
XZ
277{
278 acpi_handle handle;
279 acpi_status status;
280 struct acpi_object_list input;
281 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
282 union acpi_object params[4];
283 union acpi_object *ret_obj;
284 u64 req;
285
286 input.count = 4;
287 ppi_assign_params(params, TPM_PPI_FN_GETRSP);
288 input.pointer = params;
289 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
290 ACPI_UINT32_MAX, ppi_callback, NULL,
291 tpm_device_name, &handle);
292 if (ACPI_FAILURE(status))
293 return -ENXIO;
294
295 status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
296 ACPI_TYPE_PACKAGE);
297 if (ACPI_FAILURE(status))
298 return -ENOMEM;
299 /*
300 * parameter output.pointer should be of package type, including
301 * 3 integers. The first means function return code, the second means
302 * most recent TPM operation request, and the last means response to
303 * the most recent TPM operation request. Only if the first is 0, and
304 * the second integer is not 0, the response makes sense.
305 */
306 ret_obj = ((union acpi_object *)output.pointer)->package.elements;
307 if (ret_obj->type != ACPI_TYPE_INTEGER) {
308 status = -EINVAL;
309 goto cleanup;
310 }
311 if (ret_obj->integer.value) {
312 status = -EFAULT;
313 goto cleanup;
314 }
315 ret_obj++;
316 if (ret_obj->type != ACPI_TYPE_INTEGER) {
317 status = -EINVAL;
318 goto cleanup;
319 }
320 if (ret_obj->integer.value) {
321 req = ret_obj->integer.value;
322 ret_obj++;
323 if (ret_obj->type != ACPI_TYPE_INTEGER) {
324 status = -EINVAL;
325 goto cleanup;
326 }
327 if (ret_obj->integer.value == 0)
328 status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
329 "0: Success");
330 else if (ret_obj->integer.value == 0xFFFFFFF0)
331 status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
332 "0xFFFFFFF0: User Abort");
333 else if (ret_obj->integer.value == 0xFFFFFFF1)
334 status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
335 "0xFFFFFFF1: BIOS Failure");
336 else if (ret_obj->integer.value >= 1 &&
337 ret_obj->integer.value <= 0x00000FFF)
338 status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
339 req, ret_obj->integer.value,
340 "Corresponding TPM error");
341 else
342 status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
343 req, ret_obj->integer.value,
344 "Error");
345 } else {
346 status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n",
347 ret_obj->integer.value, "No Recent Request");
348 }
349cleanup:
350 kfree(output.pointer);
351 return status;
352}
353
354static ssize_t show_ppi_operations(char *buf, u32 start, u32 end)
355{
356 char *str = buf;
e361200b 357 char version[PPI_VERSION_LEN + 1];
f84fdff0
XZ
358 acpi_handle handle;
359 acpi_status status;
360 struct acpi_object_list input;
361 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
362 union acpi_object params[4];
363 union acpi_object obj;
364 int i;
365 u32 ret;
366 char *info[] = {
367 "Not implemented",
368 "BIOS only",
369 "Blocked for OS by BIOS",
370 "User required",
371 "User not required",
372 };
373 input.count = 4;
374 ppi_assign_params(params, TPM_PPI_FN_VERSION);
375 input.pointer = params;
376 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
377 ACPI_UINT32_MAX, ppi_callback, NULL,
378 tpm_device_name, &handle);
379 if (ACPI_FAILURE(status))
380 return -ENXIO;
381
382 status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
383 ACPI_TYPE_STRING);
384 if (ACPI_FAILURE(status))
385 return -ENOMEM;
386
e361200b 387 strlcpy(version,
f84fdff0 388 ((union acpi_object *)output.pointer)->string.pointer,
e361200b 389 PPI_VERSION_LEN + 1);
f84fdff0
XZ
390 kfree(output.pointer);
391 output.length = ACPI_ALLOCATE_BUFFER;
392 output.pointer = NULL;
393 if (strcmp(version, "1.2") == -1)
394 return -EPERM;
395
396 params[2].integer.value = TPM_PPI_FN_GETOPR;
397 params[3].package.count = 1;
398 obj.type = ACPI_TYPE_INTEGER;
399 params[3].package.elements = &obj;
400 for (i = start; i <= end; i++) {
401 obj.integer.value = i;
402 status = acpi_evaluate_object_typed(handle, "_DSM",
403 &input, &output, ACPI_TYPE_INTEGER);
404 if (ACPI_FAILURE(status))
405 return -ENOMEM;
406
407 ret = ((union acpi_object *)output.pointer)->integer.value;
408 if (ret > 0 && ret < ARRAY_SIZE(info))
409 str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n",
410 i, ret, info[ret]);
411 kfree(output.pointer);
412 output.length = ACPI_ALLOCATE_BUFFER;
413 output.pointer = NULL;
414 }
415 return str - buf;
416}
417
81198078
XZ
418static ssize_t tpm_show_ppi_tcg_operations(struct device *dev,
419 struct device_attribute *attr,
420 char *buf)
f84fdff0
XZ
421{
422 return show_ppi_operations(buf, 0, PPI_TPM_REQ_MAX);
423}
424
81198078
XZ
425static ssize_t tpm_show_ppi_vs_operations(struct device *dev,
426 struct device_attribute *attr,
427 char *buf)
f84fdff0
XZ
428{
429 return show_ppi_operations(buf, PPI_VS_REQ_START, PPI_VS_REQ_END);
430}
431
432static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL);
433static DEVICE_ATTR(request, S_IRUGO | S_IWUSR | S_IWGRP,
434 tpm_show_ppi_request, tpm_store_ppi_request);
435static DEVICE_ATTR(transition_action, S_IRUGO,
436 tpm_show_ppi_transition_action, NULL);
437static DEVICE_ATTR(response, S_IRUGO, tpm_show_ppi_response, NULL);
438static DEVICE_ATTR(tcg_operations, S_IRUGO, tpm_show_ppi_tcg_operations, NULL);
439static DEVICE_ATTR(vs_operations, S_IRUGO, tpm_show_ppi_vs_operations, NULL);
440
441static struct attribute *ppi_attrs[] = {
442 &dev_attr_version.attr,
443 &dev_attr_request.attr,
444 &dev_attr_transition_action.attr,
445 &dev_attr_response.attr,
446 &dev_attr_tcg_operations.attr,
447 &dev_attr_vs_operations.attr, NULL,
448};
449static struct attribute_group ppi_attr_grp = {
1631cfb7 450 .name = "ppi",
f84fdff0
XZ
451 .attrs = ppi_attrs
452};
453
1631cfb7 454int tpm_add_ppi(struct kobject *parent)
f84fdff0 455{
1631cfb7
GW
456 return sysfs_create_group(parent, &ppi_attr_grp);
457}
1631cfb7
GW
458
459void tpm_remove_ppi(struct kobject *parent)
460{
461 sysfs_remove_group(parent, &ppi_attr_grp);
f84fdff0 462}
This page took 0.098345 seconds and 5 git commands to generate.