1 #include <linux/acpi.h>
4 #define TPM_PPI_REVISION_ID 1
5 #define TPM_PPI_FN_VERSION 1
6 #define TPM_PPI_FN_SUBREQ 2
7 #define TPM_PPI_FN_GETREQ 3
8 #define TPM_PPI_FN_GETACT 4
9 #define TPM_PPI_FN_GETRSP 5
10 #define TPM_PPI_FN_SUBREQ2 7
11 #define TPM_PPI_FN_GETOPR 8
12 #define PPI_TPM_REQ_MAX 22
13 #define PPI_VS_REQ_START 128
14 #define PPI_VS_REQ_END 255
15 #define PPI_VERSION_LEN 3
17 static const u8 tpm_ppi_uuid
[] = {
18 0xA6, 0xFA, 0xDD, 0x3D,
22 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53
25 static char tpm_ppi_version
[PPI_VERSION_LEN
+ 1];
26 static acpi_handle tpm_ppi_handle
;
28 static acpi_status
ppi_callback(acpi_handle handle
, u32 level
, void *context
,
31 union acpi_object
*obj
;
33 if (!acpi_check_dsm(handle
, tpm_ppi_uuid
, TPM_PPI_REVISION_ID
,
34 1 << TPM_PPI_FN_VERSION
))
37 /* Cache version string */
38 obj
= acpi_evaluate_dsm_typed(handle
, tpm_ppi_uuid
,
39 TPM_PPI_REVISION_ID
, TPM_PPI_FN_VERSION
,
40 NULL
, ACPI_TYPE_STRING
);
42 strlcpy(tpm_ppi_version
, obj
->string
.pointer
,
47 *return_value
= handle
;
49 return AE_CTRL_TERMINATE
;
52 static inline union acpi_object
*
53 tpm_eval_dsm(int func
, acpi_object_type type
, union acpi_object
*argv4
)
55 BUG_ON(!tpm_ppi_handle
);
56 return acpi_evaluate_dsm_typed(tpm_ppi_handle
, tpm_ppi_uuid
,
57 TPM_PPI_REVISION_ID
, func
, argv4
, type
);
60 static ssize_t
tpm_show_ppi_version(struct device
*dev
,
61 struct device_attribute
*attr
, char *buf
)
63 return scnprintf(buf
, PAGE_SIZE
, "%s\n", tpm_ppi_version
);
66 static ssize_t
tpm_show_ppi_request(struct device
*dev
,
67 struct device_attribute
*attr
, char *buf
)
69 ssize_t size
= -EINVAL
;
70 union acpi_object
*obj
;
72 obj
= tpm_eval_dsm(TPM_PPI_FN_GETREQ
, ACPI_TYPE_PACKAGE
, NULL
);
77 * output.pointer should be of package type, including two integers.
78 * The first is function return code, 0 means success and 1 means
79 * error. The second is pending TPM operation requested by the OS, 0
80 * means none and >0 means operation value.
82 if (obj
->package
.count
== 2 &&
83 obj
->package
.elements
[0].type
== ACPI_TYPE_INTEGER
&&
84 obj
->package
.elements
[1].type
== ACPI_TYPE_INTEGER
) {
85 if (obj
->package
.elements
[0].integer
.value
)
88 size
= scnprintf(buf
, PAGE_SIZE
, "%llu\n",
89 obj
->package
.elements
[1].integer
.value
);
97 static ssize_t
tpm_store_ppi_request(struct device
*dev
,
98 struct device_attribute
*attr
,
99 const char *buf
, size_t count
)
103 int func
= TPM_PPI_FN_SUBREQ
;
104 union acpi_object
*obj
, tmp
;
105 union acpi_object argv4
= ACPI_INIT_DSM_ARGV4(1, &tmp
);
108 * the function to submit TPM operation request to pre-os environment
109 * is updated with function index from SUBREQ to SUBREQ2 since PPI
112 if (acpi_check_dsm(tpm_ppi_handle
, tpm_ppi_uuid
, TPM_PPI_REVISION_ID
,
113 1 << TPM_PPI_FN_SUBREQ2
))
114 func
= TPM_PPI_FN_SUBREQ2
;
117 * PPI spec defines params[3].type as ACPI_TYPE_PACKAGE. Some BIOS
118 * accept buffer/string/integer type, but some BIOS accept buffer/
119 * string/package type. For PPI version 1.0 and 1.1, use buffer type
120 * for compatibility, and use package type since 1.2 according to spec.
122 if (strcmp(tpm_ppi_version
, "1.2") < 0) {
123 if (sscanf(buf
, "%d", &req
) != 1)
125 argv4
.type
= ACPI_TYPE_BUFFER
;
126 argv4
.buffer
.length
= sizeof(req
);
127 argv4
.buffer
.pointer
= (u8
*)&req
;
129 tmp
.type
= ACPI_TYPE_INTEGER
;
130 if (sscanf(buf
, "%llu", &tmp
.integer
.value
) != 1)
134 obj
= tpm_eval_dsm(func
, ACPI_TYPE_INTEGER
, &argv4
);
138 ret
= obj
->integer
.value
;
143 return (acpi_status
)count
;
145 return (ret
== 1) ? -EPERM
: -EFAULT
;
148 static ssize_t
tpm_show_ppi_transition_action(struct device
*dev
,
149 struct device_attribute
*attr
,
154 union acpi_object
*obj
= NULL
;
155 union acpi_object tmp
= {
156 .buffer
.type
= ACPI_TYPE_BUFFER
,
158 .buffer
.pointer
= NULL
161 static char *info
[] = {
165 "OS Vendor-specific",
170 * PPI spec defines params[3].type as empty package, but some platforms
171 * (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for
172 * compatibility, define params[3].type as buffer, if PPI version < 1.2
174 if (strcmp(tpm_ppi_version
, "1.2") < 0)
176 obj
= tpm_eval_dsm(TPM_PPI_FN_GETACT
, ACPI_TYPE_INTEGER
, obj
);
180 ret
= obj
->integer
.value
;
184 if (ret
< ARRAY_SIZE(info
) - 1)
185 status
= scnprintf(buf
, PAGE_SIZE
, "%d: %s\n", ret
, info
[ret
]);
187 status
= scnprintf(buf
, PAGE_SIZE
, "%d: %s\n", ret
,
188 info
[ARRAY_SIZE(info
)-1]);
192 static ssize_t
tpm_show_ppi_response(struct device
*dev
,
193 struct device_attribute
*attr
,
196 acpi_status status
= -EINVAL
;
197 union acpi_object
*obj
, *ret_obj
;
200 obj
= tpm_eval_dsm(TPM_PPI_FN_GETRSP
, ACPI_TYPE_PACKAGE
, NULL
);
205 * parameter output.pointer should be of package type, including
206 * 3 integers. The first means function return code, the second means
207 * most recent TPM operation request, and the last means response to
208 * the most recent TPM operation request. Only if the first is 0, and
209 * the second integer is not 0, the response makes sense.
211 ret_obj
= obj
->package
.elements
;
212 if (obj
->package
.count
< 3 ||
213 ret_obj
[0].type
!= ACPI_TYPE_INTEGER
||
214 ret_obj
[1].type
!= ACPI_TYPE_INTEGER
||
215 ret_obj
[2].type
!= ACPI_TYPE_INTEGER
)
218 if (ret_obj
[0].integer
.value
) {
223 req
= ret_obj
[1].integer
.value
;
224 res
= ret_obj
[2].integer
.value
;
227 status
= scnprintf(buf
, PAGE_SIZE
, "%llu %s\n", req
,
229 else if (res
== 0xFFFFFFF0)
230 status
= scnprintf(buf
, PAGE_SIZE
, "%llu %s\n", req
,
231 "0xFFFFFFF0: User Abort");
232 else if (res
== 0xFFFFFFF1)
233 status
= scnprintf(buf
, PAGE_SIZE
, "%llu %s\n", req
,
234 "0xFFFFFFF1: BIOS Failure");
235 else if (res
>= 1 && res
<= 0x00000FFF)
236 status
= scnprintf(buf
, PAGE_SIZE
, "%llu %llu: %s\n",
237 req
, res
, "Corresponding TPM error");
239 status
= scnprintf(buf
, PAGE_SIZE
, "%llu %llu: %s\n",
242 status
= scnprintf(buf
, PAGE_SIZE
, "%llu: %s\n",
243 req
, "No Recent Request");
251 static ssize_t
show_ppi_operations(char *buf
, u32 start
, u32 end
)
256 union acpi_object
*obj
, tmp
;
257 union acpi_object argv
= ACPI_INIT_DSM_ARGV4(1, &tmp
);
259 static char *info
[] = {
262 "Blocked for OS by BIOS",
267 if (!acpi_check_dsm(tpm_ppi_handle
, tpm_ppi_uuid
, TPM_PPI_REVISION_ID
,
268 1 << TPM_PPI_FN_GETOPR
))
271 tmp
.integer
.type
= ACPI_TYPE_INTEGER
;
272 for (i
= start
; i
<= end
; i
++) {
273 tmp
.integer
.value
= i
;
274 obj
= tpm_eval_dsm(TPM_PPI_FN_GETOPR
, ACPI_TYPE_INTEGER
, &argv
);
278 ret
= obj
->integer
.value
;
282 if (ret
> 0 && ret
< ARRAY_SIZE(info
))
283 str
+= scnprintf(str
, PAGE_SIZE
, "%d %d: %s\n",
290 static ssize_t
tpm_show_ppi_tcg_operations(struct device
*dev
,
291 struct device_attribute
*attr
,
294 return show_ppi_operations(buf
, 0, PPI_TPM_REQ_MAX
);
297 static ssize_t
tpm_show_ppi_vs_operations(struct device
*dev
,
298 struct device_attribute
*attr
,
301 return show_ppi_operations(buf
, PPI_VS_REQ_START
, PPI_VS_REQ_END
);
304 static DEVICE_ATTR(version
, S_IRUGO
, tpm_show_ppi_version
, NULL
);
305 static DEVICE_ATTR(request
, S_IRUGO
| S_IWUSR
| S_IWGRP
,
306 tpm_show_ppi_request
, tpm_store_ppi_request
);
307 static DEVICE_ATTR(transition_action
, S_IRUGO
,
308 tpm_show_ppi_transition_action
, NULL
);
309 static DEVICE_ATTR(response
, S_IRUGO
, tpm_show_ppi_response
, NULL
);
310 static DEVICE_ATTR(tcg_operations
, S_IRUGO
, tpm_show_ppi_tcg_operations
, NULL
);
311 static DEVICE_ATTR(vs_operations
, S_IRUGO
, tpm_show_ppi_vs_operations
, NULL
);
313 static struct attribute
*ppi_attrs
[] = {
314 &dev_attr_version
.attr
,
315 &dev_attr_request
.attr
,
316 &dev_attr_transition_action
.attr
,
317 &dev_attr_response
.attr
,
318 &dev_attr_tcg_operations
.attr
,
319 &dev_attr_vs_operations
.attr
, NULL
,
321 static struct attribute_group ppi_attr_grp
= {
326 int tpm_add_ppi(struct kobject
*parent
)
328 /* Cache TPM ACPI handle and version string */
329 acpi_walk_namespace(ACPI_TYPE_DEVICE
, ACPI_ROOT_OBJECT
, ACPI_UINT32_MAX
,
330 ppi_callback
, NULL
, NULL
, &tpm_ppi_handle
);
331 if (tpm_ppi_handle
== NULL
)
334 return sysfs_create_group(parent
, &ppi_attr_grp
);
337 void tpm_remove_ppi(struct kobject
*parent
)
339 sysfs_remove_group(parent
, &ppi_attr_grp
);