]>
Commit | Line | Data |
---|---|---|
f84fdff0 XZ |
1 | #include <linux/acpi.h> |
2 | #include <acpi/acpi_drivers.h> | |
3 | #include "tpm.h" | |
4 | ||
5 | static 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 | }; | |
12 | static 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 | ||
27 | static 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 | ||
44 | static 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 |
59 | static 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 |
88 | static 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 | } | |
132 | cleanup: | |
133 | kfree(output.pointer); | |
134 | return status; | |
135 | } | |
136 | ||
81198078 XZ |
137 | static 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 |
212 | static 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 |
274 | static 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 | } | |
349 | cleanup: | |
350 | kfree(output.pointer); | |
351 | return status; | |
352 | } | |
353 | ||
354 | static 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 |
418 | static 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 |
425 | static 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 | ||
432 | static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL); | |
433 | static DEVICE_ATTR(request, S_IRUGO | S_IWUSR | S_IWGRP, | |
434 | tpm_show_ppi_request, tpm_store_ppi_request); | |
435 | static DEVICE_ATTR(transition_action, S_IRUGO, | |
436 | tpm_show_ppi_transition_action, NULL); | |
437 | static DEVICE_ATTR(response, S_IRUGO, tpm_show_ppi_response, NULL); | |
438 | static DEVICE_ATTR(tcg_operations, S_IRUGO, tpm_show_ppi_tcg_operations, NULL); | |
439 | static DEVICE_ATTR(vs_operations, S_IRUGO, tpm_show_ppi_vs_operations, NULL); | |
440 | ||
441 | static 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 | }; | |
449 | static struct attribute_group ppi_attr_grp = { | |
1631cfb7 | 450 | .name = "ppi", |
f84fdff0 XZ |
451 | .attrs = ppi_attrs |
452 | }; | |
453 | ||
1631cfb7 | 454 | int tpm_add_ppi(struct kobject *parent) |
f84fdff0 | 455 | { |
1631cfb7 GW |
456 | return sysfs_create_group(parent, &ppi_attr_grp); |
457 | } | |
1631cfb7 GW |
458 | |
459 | void tpm_remove_ppi(struct kobject *parent) | |
460 | { | |
461 | sysfs_remove_group(parent, &ppi_attr_grp); | |
f84fdff0 | 462 | } |