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