diff options
author | Xiaoyan Zhang <xiaoyan.zhang@intel.com> | 2012-08-22 06:47:22 -0400 |
---|---|---|
committer | Kent Yoder <key@linux.vnet.ibm.com> | 2012-08-22 17:23:42 -0400 |
commit | f84fdff0fdcda7e509ce530e0ee612233a2104fb (patch) | |
tree | 6af900776de08b9610f383eb741681366e800e9a /drivers/char | |
parent | 1f862f0f96abdf8b030bda84d6b66d676f31785f (diff) |
driver: add PPI support in tpm driver
The Physical Presence Interface enables the OS and the BIOS to cooperate and
provides a simple and straightforward platform user experience for
administering the TPM without sacrificing security.
V2: separate the patch out in a separate source file,
add #ifdef CONFIG_ACPI so it compiles out on ppc,
use standard error instead of ACPI error as return code of show/store fns.
V3: move #ifdef CONFIG_ACPI from .c file to .h file.
V4: move tpm_ppi code from tpm module to tpm_bios module.
V5: modify sys_add_ppi() so that ppi_attr_grp doesn't need to be exported
Signed-off-by: Xiaoyan Zhang <xiaoyan.zhang@intel.com>
Signed-off-by: Kent Yoder <key@linux.vnet.ibm.com>
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/tpm/Makefile | 2 | ||||
-rw-r--r-- | drivers/char/tpm/tpm.c | 5 | ||||
-rw-r--r-- | drivers/char/tpm/tpm.h | 9 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_ppi.c | 460 |
4 files changed, 475 insertions, 1 deletions
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 9080cc44e3c4..5b3fc8bc6c13 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile | |||
@@ -4,7 +4,7 @@ | |||
4 | obj-$(CONFIG_TCG_TPM) += tpm.o | 4 | obj-$(CONFIG_TCG_TPM) += tpm.o |
5 | ifdef CONFIG_ACPI | 5 | ifdef CONFIG_ACPI |
6 | obj-$(CONFIG_TCG_TPM) += tpm_bios.o | 6 | obj-$(CONFIG_TCG_TPM) += tpm_bios.o |
7 | tpm_bios-objs += tpm_eventlog.o tpm_acpi.o | 7 | tpm_bios-objs += tpm_eventlog.o tpm_acpi.o tpm_ppi.o |
8 | else | 8 | else |
9 | ifdef CONFIG_TCG_IBMVTPM | 9 | ifdef CONFIG_TCG_IBMVTPM |
10 | obj-$(CONFIG_TCG_TPM) += tpm_bios.o | 10 | obj-$(CONFIG_TCG_TPM) += tpm_bios.o |
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 0a75638e3e56..39526c053158 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c | |||
@@ -1476,6 +1476,11 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, | |||
1476 | goto put_device; | 1476 | goto put_device; |
1477 | } | 1477 | } |
1478 | 1478 | ||
1479 | if (sys_add_ppi(&dev->kobj)) { | ||
1480 | misc_deregister(&chip->vendor.miscdev); | ||
1481 | goto put_device; | ||
1482 | } | ||
1483 | |||
1479 | chip->bios_dir = tpm_bios_log_setup(devname); | 1484 | chip->bios_dir = tpm_bios_log_setup(devname); |
1480 | 1485 | ||
1481 | /* Make chip available */ | 1486 | /* Make chip available */ |
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index f1af73821101..02c266aa2bf7 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h | |||
@@ -327,3 +327,12 @@ extern int tpm_pm_suspend(struct device *); | |||
327 | extern int tpm_pm_resume(struct device *); | 327 | extern int tpm_pm_resume(struct device *); |
328 | extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long, | 328 | extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long, |
329 | wait_queue_head_t *); | 329 | wait_queue_head_t *); |
330 | |||
331 | #ifdef CONFIG_ACPI | ||
332 | extern ssize_t sys_add_ppi(struct kobject *parent); | ||
333 | #else | ||
334 | static inline ssize_t sys_add_ppi(struct kobject *parent) | ||
335 | { | ||
336 | return 0; | ||
337 | } | ||
338 | #endif | ||
diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c new file mode 100644 index 000000000000..440fa1c2d301 --- /dev/null +++ b/drivers/char/tpm/tpm_ppi.c | |||
@@ -0,0 +1,460 @@ | |||
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 | |||
56 | ssize_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 | |||
85 | ssize_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 | } | ||
130 | cleanup: | ||
131 | kfree(output.pointer); | ||
132 | return status; | ||
133 | } | ||
134 | |||
135 | ssize_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 | |||
210 | ssize_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 | |||
272 | ssize_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 | } | ||
347 | cleanup: | ||
348 | kfree(output.pointer); | ||
349 | return status; | ||
350 | } | ||
351 | |||
352 | static 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 | |||
416 | ssize_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 | |||
422 | ssize_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 | |||
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 = { | ||
446 | .attrs = ppi_attrs | ||
447 | }; | ||
448 | |||
449 | ssize_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 | } | ||
458 | EXPORT_SYMBOL_GPL(sys_add_ppi); | ||
459 | |||
460 | MODULE_LICENSE("GPL"); | ||