1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2012-2014 Intel Corporation |
4 | * |
5 | * Authors: |
6 | * Xiaoyan Zhang <xiaoyan.zhang@intel.com> |
7 | * Jiang Liu <jiang.liu@linux.intel.com> |
8 | * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> |
9 | * |
10 | * Maintained by: <tpmdd-devel@lists.sourceforge.net> |
11 | * |
12 | * This file contains implementation of the sysfs interface for PPI. |
13 | */ |
14 | |
15 | |
16 | #include <linux/acpi.h> |
17 | #include "tpm.h" |
18 | |
19 | #define TPM_PPI_REVISION_ID_1 1 |
20 | #define TPM_PPI_REVISION_ID_2 2 |
21 | #define TPM_PPI_FN_VERSION 1 |
22 | #define TPM_PPI_FN_SUBREQ 2 |
23 | #define TPM_PPI_FN_GETREQ 3 |
24 | #define TPM_PPI_FN_GETACT 4 |
25 | #define TPM_PPI_FN_GETRSP 5 |
26 | #define TPM_PPI_FN_SUBREQ2 7 |
27 | #define TPM_PPI_FN_GETOPR 8 |
28 | #define PPI_TPM_REQ_MAX 101 /* PPI 1.3 for TPM 2 */ |
29 | #define PPI_VS_REQ_START 128 |
30 | #define PPI_VS_REQ_END 255 |
31 | |
32 | static const guid_t tpm_ppi_guid = |
33 | GUID_INIT(0x3DDDFAA6, 0x361B, 0x4EB4, |
34 | 0xA4, 0x24, 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53); |
35 | |
36 | static bool tpm_ppi_req_has_parameter(u64 req) |
37 | { |
38 | return req == 23; |
39 | } |
40 | |
41 | static inline union acpi_object * |
42 | tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type, |
43 | union acpi_object *argv4, u64 rev) |
44 | { |
45 | BUG_ON(!ppi_handle); |
46 | return acpi_evaluate_dsm_typed(handle: ppi_handle, guid: &tpm_ppi_guid, |
47 | rev, func, argv4, type); |
48 | } |
49 | |
50 | static ssize_t tpm_show_ppi_version(struct device *dev, |
51 | struct device_attribute *attr, char *buf) |
52 | { |
53 | struct tpm_chip *chip = to_tpm_chip(dev); |
54 | |
55 | return scnprintf(buf, PAGE_SIZE, fmt: "%s\n" , chip->ppi_version); |
56 | } |
57 | |
58 | static ssize_t tpm_show_ppi_request(struct device *dev, |
59 | struct device_attribute *attr, char *buf) |
60 | { |
61 | ssize_t size = -EINVAL; |
62 | union acpi_object *obj; |
63 | struct tpm_chip *chip = to_tpm_chip(dev); |
64 | u64 rev = TPM_PPI_REVISION_ID_2; |
65 | u64 req; |
66 | |
67 | if (strcmp(chip->ppi_version, "1.2" ) < 0) |
68 | rev = TPM_PPI_REVISION_ID_1; |
69 | |
70 | obj = tpm_eval_dsm(ppi_handle: chip->acpi_dev_handle, TPM_PPI_FN_GETREQ, |
71 | ACPI_TYPE_PACKAGE, NULL, rev); |
72 | if (!obj) |
73 | return -ENXIO; |
74 | |
75 | /* |
76 | * output.pointer should be of package type, including two integers. |
77 | * The first is function return code, 0 means success and 1 means |
78 | * error. The second is pending TPM operation requested by the OS, 0 |
79 | * means none and >0 means operation value. |
80 | */ |
81 | if (obj->package.count == 3 && |
82 | obj->package.elements[0].type == ACPI_TYPE_INTEGER && |
83 | obj->package.elements[1].type == ACPI_TYPE_INTEGER && |
84 | obj->package.elements[2].type == ACPI_TYPE_INTEGER) { |
85 | if (obj->package.elements[0].integer.value) |
86 | size = -EFAULT; |
87 | else { |
88 | req = obj->package.elements[1].integer.value; |
89 | if (tpm_ppi_req_has_parameter(req)) |
90 | size = scnprintf(buf, PAGE_SIZE, |
91 | fmt: "%llu %llu\n" , req, |
92 | obj->package.elements[2].integer.value); |
93 | else |
94 | size = scnprintf(buf, PAGE_SIZE, |
95 | fmt: "%llu\n" , req); |
96 | } |
97 | } else if (obj->package.count == 2 && |
98 | obj->package.elements[0].type == ACPI_TYPE_INTEGER && |
99 | obj->package.elements[1].type == ACPI_TYPE_INTEGER) { |
100 | if (obj->package.elements[0].integer.value) |
101 | size = -EFAULT; |
102 | else |
103 | size = scnprintf(buf, PAGE_SIZE, fmt: "%llu\n" , |
104 | obj->package.elements[1].integer.value); |
105 | } |
106 | |
107 | ACPI_FREE(obj); |
108 | |
109 | return size; |
110 | } |
111 | |
112 | static ssize_t tpm_store_ppi_request(struct device *dev, |
113 | struct device_attribute *attr, |
114 | const char *buf, size_t count) |
115 | { |
116 | u32 req; |
117 | u64 ret; |
118 | int func = TPM_PPI_FN_SUBREQ; |
119 | union acpi_object *obj, tmp[2]; |
120 | union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(2, tmp); |
121 | struct tpm_chip *chip = to_tpm_chip(dev); |
122 | u64 rev = TPM_PPI_REVISION_ID_1; |
123 | |
124 | /* |
125 | * the function to submit TPM operation request to pre-os environment |
126 | * is updated with function index from SUBREQ to SUBREQ2 since PPI |
127 | * version 1.1 |
128 | */ |
129 | if (acpi_check_dsm(handle: chip->acpi_dev_handle, guid: &tpm_ppi_guid, |
130 | TPM_PPI_REVISION_ID_1, funcs: 1 << TPM_PPI_FN_SUBREQ2)) |
131 | func = TPM_PPI_FN_SUBREQ2; |
132 | |
133 | /* |
134 | * PPI spec defines params[3].type as ACPI_TYPE_PACKAGE. Some BIOS |
135 | * accept buffer/string/integer type, but some BIOS accept buffer/ |
136 | * string/package type. For PPI version 1.0 and 1.1, use buffer type |
137 | * for compatibility, and use package type since 1.2 according to spec. |
138 | */ |
139 | if (strcmp(chip->ppi_version, "1.3" ) == 0) { |
140 | if (sscanf(buf, "%llu %llu" , &tmp[0].integer.value, |
141 | &tmp[1].integer.value) != 2) |
142 | goto ppi12; |
143 | rev = TPM_PPI_REVISION_ID_2; |
144 | tmp[0].type = ACPI_TYPE_INTEGER; |
145 | tmp[1].type = ACPI_TYPE_INTEGER; |
146 | } else if (strcmp(chip->ppi_version, "1.2" ) < 0) { |
147 | if (sscanf(buf, "%d" , &req) != 1) |
148 | return -EINVAL; |
149 | argv4.type = ACPI_TYPE_BUFFER; |
150 | argv4.buffer.length = sizeof(req); |
151 | argv4.buffer.pointer = (u8 *)&req; |
152 | } else { |
153 | ppi12: |
154 | argv4.package.count = 1; |
155 | tmp[0].type = ACPI_TYPE_INTEGER; |
156 | if (sscanf(buf, "%llu" , &tmp[0].integer.value) != 1) |
157 | return -EINVAL; |
158 | } |
159 | |
160 | obj = tpm_eval_dsm(ppi_handle: chip->acpi_dev_handle, func, ACPI_TYPE_INTEGER, |
161 | argv4: &argv4, rev); |
162 | if (!obj) { |
163 | return -ENXIO; |
164 | } else { |
165 | ret = obj->integer.value; |
166 | ACPI_FREE(obj); |
167 | } |
168 | |
169 | if (ret == 0) |
170 | return (acpi_status)count; |
171 | |
172 | return (ret == 1) ? -EPERM : -EFAULT; |
173 | } |
174 | |
175 | static ssize_t tpm_show_ppi_transition_action(struct device *dev, |
176 | struct device_attribute *attr, |
177 | char *buf) |
178 | { |
179 | u32 ret; |
180 | acpi_status status; |
181 | union acpi_object *obj = NULL; |
182 | union acpi_object tmp = { |
183 | .buffer.type = ACPI_TYPE_BUFFER, |
184 | .buffer.length = 0, |
185 | .buffer.pointer = NULL |
186 | }; |
187 | struct tpm_chip *chip = to_tpm_chip(dev); |
188 | |
189 | static char *info[] = { |
190 | "None" , |
191 | "Shutdown" , |
192 | "Reboot" , |
193 | "OS Vendor-specific" , |
194 | "Error" , |
195 | }; |
196 | |
197 | /* |
198 | * PPI spec defines params[3].type as empty package, but some platforms |
199 | * (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for |
200 | * compatibility, define params[3].type as buffer, if PPI version < 1.2 |
201 | */ |
202 | if (strcmp(chip->ppi_version, "1.2" ) < 0) |
203 | obj = &tmp; |
204 | obj = tpm_eval_dsm(ppi_handle: chip->acpi_dev_handle, TPM_PPI_FN_GETACT, |
205 | ACPI_TYPE_INTEGER, argv4: obj, TPM_PPI_REVISION_ID_1); |
206 | if (!obj) { |
207 | return -ENXIO; |
208 | } else { |
209 | ret = obj->integer.value; |
210 | ACPI_FREE(obj); |
211 | } |
212 | |
213 | if (ret < ARRAY_SIZE(info) - 1) |
214 | status = scnprintf(buf, PAGE_SIZE, fmt: "%d: %s\n" , ret, info[ret]); |
215 | else |
216 | status = scnprintf(buf, PAGE_SIZE, fmt: "%d: %s\n" , ret, |
217 | info[ARRAY_SIZE(info)-1]); |
218 | return status; |
219 | } |
220 | |
221 | static ssize_t tpm_show_ppi_response(struct device *dev, |
222 | struct device_attribute *attr, |
223 | char *buf) |
224 | { |
225 | acpi_status status = -EINVAL; |
226 | union acpi_object *obj, *ret_obj; |
227 | u64 req, res; |
228 | struct tpm_chip *chip = to_tpm_chip(dev); |
229 | |
230 | obj = tpm_eval_dsm(ppi_handle: chip->acpi_dev_handle, TPM_PPI_FN_GETRSP, |
231 | ACPI_TYPE_PACKAGE, NULL, TPM_PPI_REVISION_ID_1); |
232 | if (!obj) |
233 | return -ENXIO; |
234 | |
235 | /* |
236 | * parameter output.pointer should be of package type, including |
237 | * 3 integers. The first means function return code, the second means |
238 | * most recent TPM operation request, and the last means response to |
239 | * the most recent TPM operation request. Only if the first is 0, and |
240 | * the second integer is not 0, the response makes sense. |
241 | */ |
242 | ret_obj = obj->package.elements; |
243 | if (obj->package.count < 3 || |
244 | ret_obj[0].type != ACPI_TYPE_INTEGER || |
245 | ret_obj[1].type != ACPI_TYPE_INTEGER || |
246 | ret_obj[2].type != ACPI_TYPE_INTEGER) |
247 | goto cleanup; |
248 | |
249 | if (ret_obj[0].integer.value) { |
250 | status = -EFAULT; |
251 | goto cleanup; |
252 | } |
253 | |
254 | req = ret_obj[1].integer.value; |
255 | res = ret_obj[2].integer.value; |
256 | if (req) { |
257 | if (res == 0) |
258 | status = scnprintf(buf, PAGE_SIZE, fmt: "%llu %s\n" , req, |
259 | "0: Success" ); |
260 | else if (res == 0xFFFFFFF0) |
261 | status = scnprintf(buf, PAGE_SIZE, fmt: "%llu %s\n" , req, |
262 | "0xFFFFFFF0: User Abort" ); |
263 | else if (res == 0xFFFFFFF1) |
264 | status = scnprintf(buf, PAGE_SIZE, fmt: "%llu %s\n" , req, |
265 | "0xFFFFFFF1: BIOS Failure" ); |
266 | else if (res >= 1 && res <= 0x00000FFF) |
267 | status = scnprintf(buf, PAGE_SIZE, fmt: "%llu %llu: %s\n" , |
268 | req, res, "Corresponding TPM error" ); |
269 | else |
270 | status = scnprintf(buf, PAGE_SIZE, fmt: "%llu %llu: %s\n" , |
271 | req, res, "Error" ); |
272 | } else { |
273 | status = scnprintf(buf, PAGE_SIZE, fmt: "%llu: %s\n" , |
274 | req, "No Recent Request" ); |
275 | } |
276 | |
277 | cleanup: |
278 | ACPI_FREE(obj); |
279 | return status; |
280 | } |
281 | |
282 | static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start, |
283 | u32 end) |
284 | { |
285 | int i; |
286 | u32 ret; |
287 | char *str = buf; |
288 | union acpi_object *obj, tmp; |
289 | union acpi_object argv = ACPI_INIT_DSM_ARGV4(1, &tmp); |
290 | |
291 | static char *info[] = { |
292 | "Not implemented" , |
293 | "BIOS only" , |
294 | "Blocked for OS by BIOS" , |
295 | "User required" , |
296 | "User not required" , |
297 | }; |
298 | |
299 | if (!acpi_check_dsm(handle: dev_handle, guid: &tpm_ppi_guid, TPM_PPI_REVISION_ID_1, |
300 | funcs: 1 << TPM_PPI_FN_GETOPR)) |
301 | return -EPERM; |
302 | |
303 | tmp.integer.type = ACPI_TYPE_INTEGER; |
304 | for (i = start; i <= end; i++) { |
305 | tmp.integer.value = i; |
306 | obj = tpm_eval_dsm(ppi_handle: dev_handle, TPM_PPI_FN_GETOPR, |
307 | ACPI_TYPE_INTEGER, argv4: &argv, |
308 | TPM_PPI_REVISION_ID_1); |
309 | if (!obj) { |
310 | return -ENOMEM; |
311 | } else { |
312 | ret = obj->integer.value; |
313 | ACPI_FREE(obj); |
314 | } |
315 | |
316 | if (ret > 0 && ret < ARRAY_SIZE(info)) |
317 | str += scnprintf(buf: str, PAGE_SIZE, fmt: "%d %d: %s\n" , |
318 | i, ret, info[ret]); |
319 | } |
320 | |
321 | return str - buf; |
322 | } |
323 | |
324 | static ssize_t tpm_show_ppi_tcg_operations(struct device *dev, |
325 | struct device_attribute *attr, |
326 | char *buf) |
327 | { |
328 | struct tpm_chip *chip = to_tpm_chip(dev); |
329 | |
330 | return show_ppi_operations(dev_handle: chip->acpi_dev_handle, buf, start: 0, |
331 | PPI_TPM_REQ_MAX); |
332 | } |
333 | |
334 | static ssize_t tpm_show_ppi_vs_operations(struct device *dev, |
335 | struct device_attribute *attr, |
336 | char *buf) |
337 | { |
338 | struct tpm_chip *chip = to_tpm_chip(dev); |
339 | |
340 | return show_ppi_operations(dev_handle: chip->acpi_dev_handle, buf, PPI_VS_REQ_START, |
341 | PPI_VS_REQ_END); |
342 | } |
343 | |
344 | static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL); |
345 | static DEVICE_ATTR(request, S_IRUGO | S_IWUSR | S_IWGRP, |
346 | tpm_show_ppi_request, tpm_store_ppi_request); |
347 | static DEVICE_ATTR(transition_action, S_IRUGO, |
348 | tpm_show_ppi_transition_action, NULL); |
349 | static DEVICE_ATTR(response, S_IRUGO, tpm_show_ppi_response, NULL); |
350 | static DEVICE_ATTR(tcg_operations, S_IRUGO, tpm_show_ppi_tcg_operations, NULL); |
351 | static DEVICE_ATTR(vs_operations, S_IRUGO, tpm_show_ppi_vs_operations, NULL); |
352 | |
353 | static struct attribute *ppi_attrs[] = { |
354 | &dev_attr_version.attr, |
355 | &dev_attr_request.attr, |
356 | &dev_attr_transition_action.attr, |
357 | &dev_attr_response.attr, |
358 | &dev_attr_tcg_operations.attr, |
359 | &dev_attr_vs_operations.attr, NULL, |
360 | }; |
361 | static const struct attribute_group ppi_attr_grp = { |
362 | .name = "ppi" , |
363 | .attrs = ppi_attrs |
364 | }; |
365 | |
366 | void tpm_add_ppi(struct tpm_chip *chip) |
367 | { |
368 | union acpi_object *obj; |
369 | |
370 | if (!chip->acpi_dev_handle) |
371 | return; |
372 | |
373 | if (!acpi_check_dsm(handle: chip->acpi_dev_handle, guid: &tpm_ppi_guid, |
374 | TPM_PPI_REVISION_ID_1, funcs: 1 << TPM_PPI_FN_VERSION)) |
375 | return; |
376 | |
377 | /* Cache PPI version string. */ |
378 | obj = acpi_evaluate_dsm_typed(handle: chip->acpi_dev_handle, guid: &tpm_ppi_guid, |
379 | TPM_PPI_REVISION_ID_1, |
380 | TPM_PPI_FN_VERSION, |
381 | NULL, ACPI_TYPE_STRING); |
382 | if (obj) { |
383 | strscpy(chip->ppi_version, obj->string.pointer, |
384 | sizeof(chip->ppi_version)); |
385 | ACPI_FREE(obj); |
386 | } |
387 | |
388 | chip->groups[chip->groups_cnt++] = &ppi_attr_grp; |
389 | } |
390 | |