1 #include <linux/acpi.h>
2 #include <acpi/acpi_drivers.h>
5 static const u8 tpm_ppi_uuid
[] = {
6 0xA6, 0xFA, 0xDD, 0x3D,
10 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53
12 static char *tpm_device_name
= "TPM";
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
27 static acpi_status
ppi_callback(acpi_handle handle
, u32 level
, void *context
,
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
;
41 static inline void ppi_assign_params(union acpi_object params
[4],
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
;
56 static ssize_t
tpm_show_ppi_version(struct device
*dev
,
57 struct device_attribute
*attr
, char *buf
)
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
;
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
))
75 status
= acpi_evaluate_object_typed(handle
, "_DSM", &input
, &output
,
77 if (ACPI_FAILURE(status
))
79 obj
= (union acpi_object
*)output
.pointer
;
80 status
= scnprintf(buf
, PAGE_SIZE
, "%s\n", obj
->string
.pointer
);
81 kfree(output
.pointer
);
85 static ssize_t
tpm_show_ppi_request(struct device
*dev
,
86 struct device_attribute
*attr
, char *buf
)
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
;
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
))
104 status
= acpi_evaluate_object_typed(handle
, "_DSM", &input
, &output
,
106 if (ACPI_FAILURE(status
))
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.
114 ret_obj
= ((union acpi_object
*)output
.pointer
)->package
.elements
;
115 if (ret_obj
->type
== ACPI_TYPE_INTEGER
) {
116 if (ret_obj
->integer
.value
) {
121 if (ret_obj
->type
== ACPI_TYPE_INTEGER
)
122 status
= scnprintf(buf
, PAGE_SIZE
, "%llu\n",
123 ret_obj
->integer
.value
);
130 kfree(output
.pointer
);
134 static ssize_t
tpm_store_ppi_request(struct device
*dev
,
135 struct device_attribute
*attr
,
136 const char *buf
, size_t count
)
138 char version
[PPI_VERSION_LEN
+ 1];
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
;
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
))
157 status
= acpi_evaluate_object_typed(handle
, "_DSM", &input
, &output
,
159 if (ACPI_FAILURE(status
))
162 ((union acpi_object
*)output
.pointer
)->string
.pointer
,
163 PPI_VERSION_LEN
+ 1);
164 kfree(output
.pointer
);
165 output
.length
= ACPI_ALLOCATE_BUFFER
;
166 output
.pointer
= NULL
;
168 * the function to submit TPM operation request to pre-os environment
169 * is updated with function index from SUBREQ to SUBREQ2 since PPI
172 if (strcmp(version
, "1.1") == -1)
173 params
[2].integer
.value
= TPM_PPI_FN_SUBREQ
;
175 params
[2].integer
.value
= TPM_PPI_FN_SUBREQ2
;
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.
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
;
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
;
194 status
= acpi_evaluate_object_typed(handle
, "_DSM", &input
, &output
,
196 if (ACPI_FAILURE(status
))
198 ret
= ((union acpi_object
*)output
.pointer
)->integer
.value
;
200 status
= (acpi_status
)count
;
205 kfree(output
.pointer
);
209 static ssize_t
tpm_show_ppi_transition_action(struct device
*dev
,
210 struct device_attribute
*attr
,
213 char version
[PPI_VERSION_LEN
+ 1];
216 struct acpi_object_list input
;
217 struct acpi_buffer output
= { ACPI_ALLOCATE_BUFFER
, NULL
};
218 union acpi_object params
[4];
224 "OS Vendor-specific",
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
))
236 status
= acpi_evaluate_object_typed(handle
, "_DSM", &input
, &output
,
238 if (ACPI_FAILURE(status
))
241 ((union acpi_object
*)output
.pointer
)->string
.pointer
,
242 PPI_VERSION_LEN
+ 1);
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
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
;
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
,
259 if (ACPI_FAILURE(status
))
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
]);
265 status
= scnprintf(buf
, PAGE_SIZE
, "%d: %s\n", ret
,
266 info
[ARRAY_SIZE(info
)-1]);
267 kfree(output
.pointer
);
271 static ssize_t
tpm_show_ppi_response(struct device
*dev
,
272 struct device_attribute
*attr
,
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
;
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
))
292 status
= acpi_evaluate_object_typed(handle
, "_DSM", &input
, &output
,
294 if (ACPI_FAILURE(status
))
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.
303 ret_obj
= ((union acpi_object
*)output
.pointer
)->package
.elements
;
304 if (ret_obj
->type
!= ACPI_TYPE_INTEGER
) {
308 if (ret_obj
->integer
.value
) {
313 if (ret_obj
->type
!= ACPI_TYPE_INTEGER
) {
317 if (ret_obj
->integer
.value
) {
318 req
= ret_obj
->integer
.value
;
320 if (ret_obj
->type
!= ACPI_TYPE_INTEGER
) {
324 if (ret_obj
->integer
.value
== 0)
325 status
= scnprintf(buf
, PAGE_SIZE
, "%llu %s\n", req
,
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");
339 status
= scnprintf(buf
, PAGE_SIZE
, "%llu %llu: %s\n",
340 req
, ret_obj
->integer
.value
,
343 status
= scnprintf(buf
, PAGE_SIZE
, "%llu: %s\n",
344 ret_obj
->integer
.value
, "No Recent Request");
347 kfree(output
.pointer
);
351 static ssize_t
show_ppi_operations(char *buf
, u32 start
, u32 end
)
354 char version
[PPI_VERSION_LEN
+ 1];
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
;
366 "Blocked for OS by BIOS",
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
))
379 status
= acpi_evaluate_object_typed(handle
, "_DSM", &input
, &output
,
381 if (ACPI_FAILURE(status
))
385 ((union acpi_object
*)output
.pointer
)->string
.pointer
,
386 PPI_VERSION_LEN
+ 1);
387 kfree(output
.pointer
);
388 output
.length
= ACPI_ALLOCATE_BUFFER
;
389 output
.pointer
= NULL
;
390 if (strcmp(version
, "1.2") == -1)
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
))
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",
408 kfree(output
.pointer
);
409 output
.length
= ACPI_ALLOCATE_BUFFER
;
410 output
.pointer
= NULL
;
415 static ssize_t
tpm_show_ppi_tcg_operations(struct device
*dev
,
416 struct device_attribute
*attr
,
419 return show_ppi_operations(buf
, 0, PPI_TPM_REQ_MAX
);
422 static ssize_t
tpm_show_ppi_vs_operations(struct device
*dev
,
423 struct device_attribute
*attr
,
426 return show_ppi_operations(buf
, PPI_VS_REQ_START
, PPI_VS_REQ_END
);
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
);
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
,
446 static struct attribute_group ppi_attr_grp
= {
451 int tpm_add_ppi(struct kobject
*parent
)
453 return sysfs_create_group(parent
, &ppi_attr_grp
);
455 EXPORT_SYMBOL_GPL(tpm_add_ppi
);
457 void tpm_remove_ppi(struct kobject
*parent
)
459 sysfs_remove_group(parent
, &ppi_attr_grp
);
461 EXPORT_SYMBOL_GPL(tpm_remove_ppi
);
463 MODULE_LICENSE("GPL");