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