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