]>
Commit | Line | Data |
---|---|---|
b886d83c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
0dc55365 JS |
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. | |
0dc55365 JS |
13 | */ |
14 | ||
15 | ||
f84fdff0 | 16 | #include <linux/acpi.h> |
f84fdff0 XZ |
17 | #include "tpm.h" |
18 | ||
09fe1b42 | 19 | #define TPM_PPI_REVISION_ID_1 1 |
8b60c79b | 20 | #define TPM_PPI_REVISION_ID_2 2 |
f84fdff0 XZ |
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 | |
9d4023ed | 28 | #define PPI_TPM_REQ_MAX 101 /* PPI 1.3 for TPM 2 */ |
f84fdff0 XZ |
29 | #define PPI_VS_REQ_START 128 |
30 | #define PPI_VS_REQ_END 255 | |
f84fdff0 | 31 | |
94116f81 AS |
32 | static const guid_t tpm_ppi_guid = |
33 | GUID_INIT(0x3DDDFAA6, 0x361B, 0x4EB4, | |
34 | 0xA4, 0x24, 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53); | |
84b1667d | 35 | |
8b60c79b SB |
36 | static bool tpm_ppi_req_has_parameter(u64 req) |
37 | { | |
38 | return req == 23; | |
39 | } | |
40 | ||
84b1667d | 41 | static inline union acpi_object * |
0dc55365 | 42 | tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type, |
587bad77 | 43 | union acpi_object *argv4, u64 rev) |
f84fdff0 | 44 | { |
0dc55365 | 45 | BUG_ON(!ppi_handle); |
94116f81 | 46 | return acpi_evaluate_dsm_typed(ppi_handle, &tpm_ppi_guid, |
587bad77 | 47 | rev, func, argv4, type); |
f84fdff0 XZ |
48 | } |
49 | ||
81198078 XZ |
50 | static ssize_t tpm_show_ppi_version(struct device *dev, |
51 | struct device_attribute *attr, char *buf) | |
f84fdff0 | 52 | { |
9b774d5c | 53 | struct tpm_chip *chip = to_tpm_chip(dev); |
0dc55365 JS |
54 | |
55 | return scnprintf(buf, PAGE_SIZE, "%s\n", chip->ppi_version); | |
f84fdff0 XZ |
56 | } |
57 | ||
81198078 XZ |
58 | static ssize_t tpm_show_ppi_request(struct device *dev, |
59 | struct device_attribute *attr, char *buf) | |
f84fdff0 | 60 | { |
84b1667d JL |
61 | ssize_t size = -EINVAL; |
62 | union acpi_object *obj; | |
9b774d5c | 63 | struct tpm_chip *chip = to_tpm_chip(dev); |
8b60c79b SB |
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; | |
84b1667d | 69 | |
0dc55365 | 70 | obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETREQ, |
8b60c79b | 71 | ACPI_TYPE_PACKAGE, NULL, rev); |
84b1667d | 72 | if (!obj) |
f84fdff0 XZ |
73 | return -ENXIO; |
74 | ||
f84fdff0 XZ |
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 | */ | |
8b60c79b SB |
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 | "%llu %llu\n", req, | |
92 | obj->package.elements[2].integer.value); | |
93 | else | |
94 | size = scnprintf(buf, PAGE_SIZE, | |
95 | "%llu\n", req); | |
96 | } | |
97 | } else if (obj->package.count == 2 && | |
84b1667d JL |
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; | |
f84fdff0 | 102 | else |
84b1667d JL |
103 | size = scnprintf(buf, PAGE_SIZE, "%llu\n", |
104 | obj->package.elements[1].integer.value); | |
f84fdff0 | 105 | } |
84b1667d JL |
106 | |
107 | ACPI_FREE(obj); | |
108 | ||
109 | return size; | |
f84fdff0 XZ |
110 | } |
111 | ||
81198078 XZ |
112 | static ssize_t tpm_store_ppi_request(struct device *dev, |
113 | struct device_attribute *attr, | |
114 | const char *buf, size_t count) | |
f84fdff0 | 115 | { |
f84fdff0 XZ |
116 | u32 req; |
117 | u64 ret; | |
84b1667d | 118 | int func = TPM_PPI_FN_SUBREQ; |
50a81b60 SB |
119 | union acpi_object *obj, tmp[2]; |
120 | union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(2, tmp); | |
9b774d5c | 121 | struct tpm_chip *chip = to_tpm_chip(dev); |
50a81b60 | 122 | u64 rev = TPM_PPI_REVISION_ID_1; |
f84fdff0 | 123 | |
f84fdff0 XZ |
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 | */ | |
94116f81 | 129 | if (acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid, |
09fe1b42 | 130 | TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_SUBREQ2)) |
84b1667d JL |
131 | func = TPM_PPI_FN_SUBREQ2; |
132 | ||
f84fdff0 XZ |
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 | */ | |
50a81b60 SB |
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) { | |
84b1667d JL |
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; | |
f84fdff0 | 152 | } else { |
50a81b60 SB |
153 | ppi12: |
154 | argv4.package.count = 1; | |
155 | tmp[0].type = ACPI_TYPE_INTEGER; | |
156 | if (sscanf(buf, "%llu", &tmp[0].integer.value) != 1) | |
84b1667d JL |
157 | return -EINVAL; |
158 | } | |
159 | ||
0dc55365 | 160 | obj = tpm_eval_dsm(chip->acpi_dev_handle, func, ACPI_TYPE_INTEGER, |
50a81b60 | 161 | &argv4, rev); |
84b1667d JL |
162 | if (!obj) { |
163 | return -ENXIO; | |
f84fdff0 | 164 | } else { |
84b1667d JL |
165 | ret = obj->integer.value; |
166 | ACPI_FREE(obj); | |
f84fdff0 XZ |
167 | } |
168 | ||
f84fdff0 | 169 | if (ret == 0) |
84b1667d JL |
170 | return (acpi_status)count; |
171 | ||
172 | return (ret == 1) ? -EPERM : -EFAULT; | |
f84fdff0 XZ |
173 | } |
174 | ||
81198078 XZ |
175 | static ssize_t tpm_show_ppi_transition_action(struct device *dev, |
176 | struct device_attribute *attr, | |
177 | char *buf) | |
f84fdff0 | 178 | { |
f84fdff0 | 179 | u32 ret; |
84b1667d JL |
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 | }; | |
9b774d5c | 187 | struct tpm_chip *chip = to_tpm_chip(dev); |
84b1667d JL |
188 | |
189 | static char *info[] = { | |
f84fdff0 XZ |
190 | "None", |
191 | "Shutdown", | |
192 | "Reboot", | |
193 | "OS Vendor-specific", | |
194 | "Error", | |
195 | }; | |
f84fdff0 | 196 | |
f84fdff0 XZ |
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 | */ | |
0dc55365 | 202 | if (strcmp(chip->ppi_version, "1.2") < 0) |
84b1667d | 203 | obj = &tmp; |
0dc55365 | 204 | obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETACT, |
09fe1b42 | 205 | ACPI_TYPE_INTEGER, obj, TPM_PPI_REVISION_ID_1); |
84b1667d JL |
206 | if (!obj) { |
207 | return -ENXIO; | |
208 | } else { | |
209 | ret = obj->integer.value; | |
210 | ACPI_FREE(obj); | |
f84fdff0 | 211 | } |
84b1667d | 212 | |
f84fdff0 XZ |
213 | if (ret < ARRAY_SIZE(info) - 1) |
214 | status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]); | |
215 | else | |
216 | status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, | |
217 | info[ARRAY_SIZE(info)-1]); | |
f84fdff0 XZ |
218 | return status; |
219 | } | |
220 | ||
81198078 XZ |
221 | static ssize_t tpm_show_ppi_response(struct device *dev, |
222 | struct device_attribute *attr, | |
223 | char *buf) | |
f84fdff0 | 224 | { |
84b1667d JL |
225 | acpi_status status = -EINVAL; |
226 | union acpi_object *obj, *ret_obj; | |
227 | u64 req, res; | |
9b774d5c | 228 | struct tpm_chip *chip = to_tpm_chip(dev); |
84b1667d | 229 | |
0dc55365 | 230 | obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETRSP, |
09fe1b42 | 231 | ACPI_TYPE_PACKAGE, NULL, TPM_PPI_REVISION_ID_1); |
84b1667d | 232 | if (!obj) |
f84fdff0 XZ |
233 | return -ENXIO; |
234 | ||
f84fdff0 XZ |
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 | */ | |
84b1667d JL |
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) | |
f84fdff0 | 247 | goto cleanup; |
84b1667d JL |
248 | |
249 | if (ret_obj[0].integer.value) { | |
f84fdff0 XZ |
250 | status = -EFAULT; |
251 | goto cleanup; | |
252 | } | |
84b1667d JL |
253 | |
254 | req = ret_obj[1].integer.value; | |
255 | res = ret_obj[2].integer.value; | |
256 | if (req) { | |
257 | if (res == 0) | |
f84fdff0 XZ |
258 | status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, |
259 | "0: Success"); | |
84b1667d | 260 | else if (res == 0xFFFFFFF0) |
f84fdff0 XZ |
261 | status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, |
262 | "0xFFFFFFF0: User Abort"); | |
84b1667d | 263 | else if (res == 0xFFFFFFF1) |
f84fdff0 XZ |
264 | status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, |
265 | "0xFFFFFFF1: BIOS Failure"); | |
84b1667d | 266 | else if (res >= 1 && res <= 0x00000FFF) |
f84fdff0 | 267 | status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", |
84b1667d | 268 | req, res, "Corresponding TPM error"); |
f84fdff0 XZ |
269 | else |
270 | status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", | |
84b1667d | 271 | req, res, "Error"); |
f84fdff0 XZ |
272 | } else { |
273 | status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n", | |
84b1667d | 274 | req, "No Recent Request"); |
f84fdff0 | 275 | } |
84b1667d | 276 | |
f84fdff0 | 277 | cleanup: |
84b1667d | 278 | ACPI_FREE(obj); |
f84fdff0 XZ |
279 | return status; |
280 | } | |
281 | ||
0dc55365 JS |
282 | static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start, |
283 | u32 end) | |
f84fdff0 | 284 | { |
f84fdff0 XZ |
285 | int i; |
286 | u32 ret; | |
84b1667d JL |
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[] = { | |
f84fdff0 XZ |
292 | "Not implemented", |
293 | "BIOS only", | |
294 | "Blocked for OS by BIOS", | |
295 | "User required", | |
296 | "User not required", | |
297 | }; | |
f84fdff0 | 298 | |
09fe1b42 | 299 | if (!acpi_check_dsm(dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID_1, |
1569a4c4 | 300 | 1 << TPM_PPI_FN_GETOPR)) |
f84fdff0 XZ |
301 | return -EPERM; |
302 | ||
84b1667d | 303 | tmp.integer.type = ACPI_TYPE_INTEGER; |
f84fdff0 | 304 | for (i = start; i <= end; i++) { |
84b1667d | 305 | tmp.integer.value = i; |
0dc55365 | 306 | obj = tpm_eval_dsm(dev_handle, TPM_PPI_FN_GETOPR, |
587bad77 | 307 | ACPI_TYPE_INTEGER, &argv, |
09fe1b42 | 308 | TPM_PPI_REVISION_ID_1); |
84b1667d | 309 | if (!obj) { |
f84fdff0 | 310 | return -ENOMEM; |
84b1667d JL |
311 | } else { |
312 | ret = obj->integer.value; | |
313 | ACPI_FREE(obj); | |
314 | } | |
f84fdff0 | 315 | |
f84fdff0 XZ |
316 | if (ret > 0 && ret < ARRAY_SIZE(info)) |
317 | str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n", | |
318 | i, ret, info[ret]); | |
f84fdff0 | 319 | } |
84b1667d | 320 | |
f84fdff0 XZ |
321 | return str - buf; |
322 | } | |
323 | ||
81198078 XZ |
324 | static ssize_t tpm_show_ppi_tcg_operations(struct device *dev, |
325 | struct device_attribute *attr, | |
326 | char *buf) | |
f84fdff0 | 327 | { |
9b774d5c | 328 | struct tpm_chip *chip = to_tpm_chip(dev); |
0dc55365 JS |
329 | |
330 | return show_ppi_operations(chip->acpi_dev_handle, buf, 0, | |
331 | PPI_TPM_REQ_MAX); | |
f84fdff0 XZ |
332 | } |
333 | ||
81198078 XZ |
334 | static ssize_t tpm_show_ppi_vs_operations(struct device *dev, |
335 | struct device_attribute *attr, | |
336 | char *buf) | |
f84fdff0 | 337 | { |
9b774d5c | 338 | struct tpm_chip *chip = to_tpm_chip(dev); |
0dc55365 JS |
339 | |
340 | return show_ppi_operations(chip->acpi_dev_handle, buf, PPI_VS_REQ_START, | |
341 | PPI_VS_REQ_END); | |
f84fdff0 XZ |
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 struct attribute_group ppi_attr_grp = { | |
1631cfb7 | 362 | .name = "ppi", |
f84fdff0 XZ |
363 | .attrs = ppi_attrs |
364 | }; | |
365 | ||
9b774d5c | 366 | void tpm_add_ppi(struct tpm_chip *chip) |
f84fdff0 | 367 | { |
0dc55365 | 368 | union acpi_object *obj; |
0dc55365 JS |
369 | |
370 | if (!chip->acpi_dev_handle) | |
9b774d5c | 371 | return; |
0dc55365 | 372 | |
94116f81 | 373 | if (!acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid, |
09fe1b42 | 374 | TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_VERSION)) |
9b774d5c | 375 | return; |
0dc55365 JS |
376 | |
377 | /* Cache PPI version string. */ | |
94116f81 | 378 | obj = acpi_evaluate_dsm_typed(chip->acpi_dev_handle, &tpm_ppi_guid, |
09fe1b42 SB |
379 | TPM_PPI_REVISION_ID_1, |
380 | TPM_PPI_FN_VERSION, | |
0dc55365 JS |
381 | NULL, ACPI_TYPE_STRING); |
382 | if (obj) { | |
383 | strlcpy(chip->ppi_version, obj->string.pointer, | |
384 | sizeof(chip->ppi_version)); | |
385 | ACPI_FREE(obj); | |
386 | } | |
387 | ||
9b774d5c | 388 | chip->groups[chip->groups_cnt++] = &ppi_attr_grp; |
f84fdff0 | 389 | } |