xref: /openbmc/linux/drivers/char/tpm/tpm_ppi.c (revision 84b1667dea233d2af29b5138bfd2d70417cfa720)
1f84fdff0SXiaoyan Zhang #include <linux/acpi.h>
2f84fdff0SXiaoyan Zhang #include <acpi/acpi_drivers.h>
3f84fdff0SXiaoyan Zhang #include "tpm.h"
4f84fdff0SXiaoyan Zhang 
5f84fdff0SXiaoyan Zhang #define TPM_PPI_REVISION_ID	1
6f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_VERSION	1
7f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_SUBREQ	2
8f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETREQ	3
9f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETACT	4
10f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETRSP	5
11f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_SUBREQ2	7
12f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETOPR	8
13f84fdff0SXiaoyan Zhang #define PPI_TPM_REQ_MAX		22
14f84fdff0SXiaoyan Zhang #define PPI_VS_REQ_START	128
15f84fdff0SXiaoyan Zhang #define PPI_VS_REQ_END		255
16f84fdff0SXiaoyan Zhang #define PPI_VERSION_LEN		3
17f84fdff0SXiaoyan Zhang 
18*84b1667dSJiang Liu static const u8 tpm_ppi_uuid[] = {
19*84b1667dSJiang Liu 	0xA6, 0xFA, 0xDD, 0x3D,
20*84b1667dSJiang Liu 	0x1B, 0x36,
21*84b1667dSJiang Liu 	0xB4, 0x4E,
22*84b1667dSJiang Liu 	0xA4, 0x24,
23*84b1667dSJiang Liu 	0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53
24*84b1667dSJiang Liu };
25*84b1667dSJiang Liu 
26*84b1667dSJiang Liu static char *tpm_device_name = "TPM";
27*84b1667dSJiang Liu static char tpm_ppi_version[PPI_VERSION_LEN + 1];
28*84b1667dSJiang Liu static acpi_handle tpm_ppi_handle;
29*84b1667dSJiang Liu 
30f84fdff0SXiaoyan Zhang static acpi_status ppi_callback(acpi_handle handle, u32 level, void *context,
31f84fdff0SXiaoyan Zhang 				void **return_value)
32f84fdff0SXiaoyan Zhang {
33df45c712SJiang Liu 	acpi_status status = AE_OK;
34f84fdff0SXiaoyan Zhang 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
35df45c712SJiang Liu 
36*84b1667dSJiang Liu 	status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
37*84b1667dSJiang Liu 	if (ACPI_FAILURE(status))
38*84b1667dSJiang Liu 		return AE_OK;
39*84b1667dSJiang Liu 
40f84fdff0SXiaoyan Zhang 	if (strstr(buffer.pointer, context) != NULL) {
41*84b1667dSJiang Liu 		union acpi_object *obj;
42*84b1667dSJiang Liu 
43*84b1667dSJiang Liu 		/* Cache version string */
44*84b1667dSJiang Liu 		obj = acpi_evaluate_dsm_typed(handle, tpm_ppi_uuid,
45*84b1667dSJiang Liu 				TPM_PPI_REVISION_ID, TPM_PPI_FN_VERSION,
46*84b1667dSJiang Liu 				NULL, ACPI_TYPE_STRING);
47*84b1667dSJiang Liu 		if (obj) {
48*84b1667dSJiang Liu 			strlcpy(tpm_ppi_version, obj->string.pointer,
49*84b1667dSJiang Liu 				PPI_VERSION_LEN + 1);
50*84b1667dSJiang Liu 			ACPI_FREE(obj);
51*84b1667dSJiang Liu 		}
52*84b1667dSJiang Liu 
53f84fdff0SXiaoyan Zhang 		*return_value = handle;
54df45c712SJiang Liu 		status = AE_CTRL_TERMINATE;
55f84fdff0SXiaoyan Zhang 	}
56df45c712SJiang Liu 	kfree(buffer.pointer);
57df45c712SJiang Liu 
58df45c712SJiang Liu 	return status;
59f84fdff0SXiaoyan Zhang }
60f84fdff0SXiaoyan Zhang 
61*84b1667dSJiang Liu static inline union acpi_object *
62*84b1667dSJiang Liu tpm_eval_dsm(int func, acpi_object_type type, union acpi_object *argv4)
63f84fdff0SXiaoyan Zhang {
64*84b1667dSJiang Liu 	BUG_ON(!tpm_ppi_handle);
65*84b1667dSJiang Liu 	return acpi_evaluate_dsm_typed(tpm_ppi_handle, tpm_ppi_uuid,
66*84b1667dSJiang Liu 				       TPM_PPI_REVISION_ID, func, argv4, type);
67f84fdff0SXiaoyan Zhang }
68f84fdff0SXiaoyan Zhang 
6981198078SXiaoyan Zhang static ssize_t tpm_show_ppi_version(struct device *dev,
7081198078SXiaoyan Zhang 				    struct device_attribute *attr, char *buf)
71f84fdff0SXiaoyan Zhang {
72*84b1667dSJiang Liu 	return scnprintf(buf, PAGE_SIZE, "%s\n", tpm_ppi_version);
73f84fdff0SXiaoyan Zhang }
74f84fdff0SXiaoyan Zhang 
7581198078SXiaoyan Zhang static ssize_t tpm_show_ppi_request(struct device *dev,
7681198078SXiaoyan Zhang 				    struct device_attribute *attr, char *buf)
77f84fdff0SXiaoyan Zhang {
78*84b1667dSJiang Liu 	ssize_t size = -EINVAL;
79*84b1667dSJiang Liu 	union acpi_object *obj;
80f84fdff0SXiaoyan Zhang 
81*84b1667dSJiang Liu 	obj = tpm_eval_dsm(TPM_PPI_FN_GETREQ, ACPI_TYPE_PACKAGE, NULL);
82*84b1667dSJiang Liu 	if (!obj)
83f84fdff0SXiaoyan Zhang 		return -ENXIO;
84f84fdff0SXiaoyan Zhang 
85f84fdff0SXiaoyan Zhang 	/*
86f84fdff0SXiaoyan Zhang 	 * output.pointer should be of package type, including two integers.
87f84fdff0SXiaoyan Zhang 	 * The first is function return code, 0 means success and 1 means
88f84fdff0SXiaoyan Zhang 	 * error. The second is pending TPM operation requested by the OS, 0
89f84fdff0SXiaoyan Zhang 	 * means none and >0 means operation value.
90f84fdff0SXiaoyan Zhang 	 */
91*84b1667dSJiang Liu 	if (obj->package.count == 2 &&
92*84b1667dSJiang Liu 	    obj->package.elements[0].type == ACPI_TYPE_INTEGER &&
93*84b1667dSJiang Liu 	    obj->package.elements[1].type == ACPI_TYPE_INTEGER) {
94*84b1667dSJiang Liu 		if (obj->package.elements[0].integer.value)
95*84b1667dSJiang Liu 			size = -EFAULT;
96f84fdff0SXiaoyan Zhang 		else
97*84b1667dSJiang Liu 			size = scnprintf(buf, PAGE_SIZE, "%llu\n",
98*84b1667dSJiang Liu 				 obj->package.elements[1].integer.value);
99f84fdff0SXiaoyan Zhang 	}
100*84b1667dSJiang Liu 
101*84b1667dSJiang Liu 	ACPI_FREE(obj);
102*84b1667dSJiang Liu 
103*84b1667dSJiang Liu 	return size;
104f84fdff0SXiaoyan Zhang }
105f84fdff0SXiaoyan Zhang 
10681198078SXiaoyan Zhang static ssize_t tpm_store_ppi_request(struct device *dev,
107f84fdff0SXiaoyan Zhang 				     struct device_attribute *attr,
108f84fdff0SXiaoyan Zhang 				     const char *buf, size_t count)
109f84fdff0SXiaoyan Zhang {
110f84fdff0SXiaoyan Zhang 	u32 req;
111f84fdff0SXiaoyan Zhang 	u64 ret;
112*84b1667dSJiang Liu 	int func = TPM_PPI_FN_SUBREQ;
113*84b1667dSJiang Liu 	union acpi_object *obj, tmp;
114*84b1667dSJiang Liu 	union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp);
115f84fdff0SXiaoyan Zhang 
116f84fdff0SXiaoyan Zhang 	/*
117f84fdff0SXiaoyan Zhang 	 * the function to submit TPM operation request to pre-os environment
118f84fdff0SXiaoyan Zhang 	 * is updated with function index from SUBREQ to SUBREQ2 since PPI
119f84fdff0SXiaoyan Zhang 	 * version 1.1
120f84fdff0SXiaoyan Zhang 	 */
121*84b1667dSJiang Liu 	if (strcmp(tpm_ppi_version, "1.1") >= 0)
122*84b1667dSJiang Liu 		func = TPM_PPI_FN_SUBREQ2;
123*84b1667dSJiang Liu 
124f84fdff0SXiaoyan Zhang 	/*
125f84fdff0SXiaoyan Zhang 	 * PPI spec defines params[3].type as ACPI_TYPE_PACKAGE. Some BIOS
126f84fdff0SXiaoyan Zhang 	 * accept buffer/string/integer type, but some BIOS accept buffer/
127f84fdff0SXiaoyan Zhang 	 * string/package type. For PPI version 1.0 and 1.1, use buffer type
128f84fdff0SXiaoyan Zhang 	 * for compatibility, and use package type since 1.2 according to spec.
129f84fdff0SXiaoyan Zhang 	 */
130*84b1667dSJiang Liu 	if (strcmp(tpm_ppi_version, "1.2") < 0) {
131*84b1667dSJiang Liu 		if (sscanf(buf, "%d", &req) != 1)
132*84b1667dSJiang Liu 			return -EINVAL;
133*84b1667dSJiang Liu 		argv4.type = ACPI_TYPE_BUFFER;
134*84b1667dSJiang Liu 		argv4.buffer.length = sizeof(req);
135*84b1667dSJiang Liu 		argv4.buffer.pointer = (u8 *)&req;
136f84fdff0SXiaoyan Zhang 	} else {
137*84b1667dSJiang Liu 		tmp.type = ACPI_TYPE_INTEGER;
138*84b1667dSJiang Liu 		if (sscanf(buf, "%llu", &tmp.integer.value) != 1)
139*84b1667dSJiang Liu 			return -EINVAL;
140f84fdff0SXiaoyan Zhang 	}
141f84fdff0SXiaoyan Zhang 
142*84b1667dSJiang Liu 	obj = tpm_eval_dsm(func, ACPI_TYPE_INTEGER, &argv4);
143*84b1667dSJiang Liu 	if (!obj) {
144*84b1667dSJiang Liu 		return -ENXIO;
145*84b1667dSJiang Liu 	} else {
146*84b1667dSJiang Liu 		ret = obj->integer.value;
147*84b1667dSJiang Liu 		ACPI_FREE(obj);
148*84b1667dSJiang Liu 	}
149*84b1667dSJiang Liu 
150f84fdff0SXiaoyan Zhang 	if (ret == 0)
151*84b1667dSJiang Liu 		return (acpi_status)count;
152*84b1667dSJiang Liu 
153*84b1667dSJiang Liu 	return (ret == 1) ? -EPERM : -EFAULT;
154f84fdff0SXiaoyan Zhang }
155f84fdff0SXiaoyan Zhang 
15681198078SXiaoyan Zhang static ssize_t tpm_show_ppi_transition_action(struct device *dev,
157f84fdff0SXiaoyan Zhang 					      struct device_attribute *attr,
158f84fdff0SXiaoyan Zhang 					      char *buf)
159f84fdff0SXiaoyan Zhang {
160f84fdff0SXiaoyan Zhang 	u32 ret;
161*84b1667dSJiang Liu 	acpi_status status;
162*84b1667dSJiang Liu 	union acpi_object *obj = NULL;
163*84b1667dSJiang Liu 	union acpi_object tmp = {
164*84b1667dSJiang Liu 		.buffer.type = ACPI_TYPE_BUFFER,
165*84b1667dSJiang Liu 		.buffer.length = 0,
166*84b1667dSJiang Liu 		.buffer.pointer = NULL
167*84b1667dSJiang Liu 	};
168*84b1667dSJiang Liu 
169*84b1667dSJiang Liu 	static char *info[] = {
170f84fdff0SXiaoyan Zhang 		"None",
171f84fdff0SXiaoyan Zhang 		"Shutdown",
172f84fdff0SXiaoyan Zhang 		"Reboot",
173f84fdff0SXiaoyan Zhang 		"OS Vendor-specific",
174f84fdff0SXiaoyan Zhang 		"Error",
175f84fdff0SXiaoyan Zhang 	};
176f84fdff0SXiaoyan Zhang 
177f84fdff0SXiaoyan Zhang 	/*
178f84fdff0SXiaoyan Zhang 	 * PPI spec defines params[3].type as empty package, but some platforms
179f84fdff0SXiaoyan Zhang 	 * (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for
180f84fdff0SXiaoyan Zhang 	 * compatibility, define params[3].type as buffer, if PPI version < 1.2
181f84fdff0SXiaoyan Zhang 	 */
182*84b1667dSJiang Liu 	if (strcmp(tpm_ppi_version, "1.2") < 0)
183*84b1667dSJiang Liu 		obj = &tmp;
184*84b1667dSJiang Liu 	obj = tpm_eval_dsm(TPM_PPI_FN_GETACT, ACPI_TYPE_INTEGER, obj);
185*84b1667dSJiang Liu 	if (!obj) {
186*84b1667dSJiang Liu 		return -ENXIO;
187*84b1667dSJiang Liu 	} else {
188*84b1667dSJiang Liu 		ret = obj->integer.value;
189*84b1667dSJiang Liu 		ACPI_FREE(obj);
190f84fdff0SXiaoyan Zhang 	}
191*84b1667dSJiang Liu 
192f84fdff0SXiaoyan Zhang 	if (ret < ARRAY_SIZE(info) - 1)
193f84fdff0SXiaoyan Zhang 		status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]);
194f84fdff0SXiaoyan Zhang 	else
195f84fdff0SXiaoyan Zhang 		status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret,
196f84fdff0SXiaoyan Zhang 				   info[ARRAY_SIZE(info)-1]);
197f84fdff0SXiaoyan Zhang 	return status;
198f84fdff0SXiaoyan Zhang }
199f84fdff0SXiaoyan Zhang 
20081198078SXiaoyan Zhang static ssize_t tpm_show_ppi_response(struct device *dev,
201f84fdff0SXiaoyan Zhang 				     struct device_attribute *attr,
202f84fdff0SXiaoyan Zhang 				     char *buf)
203f84fdff0SXiaoyan Zhang {
204*84b1667dSJiang Liu 	acpi_status status = -EINVAL;
205*84b1667dSJiang Liu 	union acpi_object *obj, *ret_obj;
206*84b1667dSJiang Liu 	u64 req, res;
207f84fdff0SXiaoyan Zhang 
208*84b1667dSJiang Liu 	obj = tpm_eval_dsm(TPM_PPI_FN_GETRSP, ACPI_TYPE_PACKAGE, NULL);
209*84b1667dSJiang Liu 	if (!obj)
210f84fdff0SXiaoyan Zhang 		return -ENXIO;
211f84fdff0SXiaoyan Zhang 
212f84fdff0SXiaoyan Zhang 	/*
213f84fdff0SXiaoyan Zhang 	 * parameter output.pointer should be of package type, including
214f84fdff0SXiaoyan Zhang 	 * 3 integers. The first means function return code, the second means
215f84fdff0SXiaoyan Zhang 	 * most recent TPM operation request, and the last means response to
216f84fdff0SXiaoyan Zhang 	 * the most recent TPM operation request. Only if the first is 0, and
217f84fdff0SXiaoyan Zhang 	 * the second integer is not 0, the response makes sense.
218f84fdff0SXiaoyan Zhang 	 */
219*84b1667dSJiang Liu 	ret_obj = obj->package.elements;
220*84b1667dSJiang Liu 	if (obj->package.count < 3 ||
221*84b1667dSJiang Liu 	    ret_obj[0].type != ACPI_TYPE_INTEGER ||
222*84b1667dSJiang Liu 	    ret_obj[1].type != ACPI_TYPE_INTEGER ||
223*84b1667dSJiang Liu 	    ret_obj[2].type != ACPI_TYPE_INTEGER)
224f84fdff0SXiaoyan Zhang 		goto cleanup;
225*84b1667dSJiang Liu 
226*84b1667dSJiang Liu 	if (ret_obj[0].integer.value) {
227f84fdff0SXiaoyan Zhang 		status = -EFAULT;
228f84fdff0SXiaoyan Zhang 		goto cleanup;
229f84fdff0SXiaoyan Zhang 	}
230*84b1667dSJiang Liu 
231*84b1667dSJiang Liu 	req = ret_obj[1].integer.value;
232*84b1667dSJiang Liu 	res = ret_obj[2].integer.value;
233*84b1667dSJiang Liu 	if (req) {
234*84b1667dSJiang Liu 		if (res == 0)
235f84fdff0SXiaoyan Zhang 			status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
236f84fdff0SXiaoyan Zhang 					   "0: Success");
237*84b1667dSJiang Liu 		else if (res == 0xFFFFFFF0)
238f84fdff0SXiaoyan Zhang 			status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
239f84fdff0SXiaoyan Zhang 					   "0xFFFFFFF0: User Abort");
240*84b1667dSJiang Liu 		else if (res == 0xFFFFFFF1)
241f84fdff0SXiaoyan Zhang 			status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
242f84fdff0SXiaoyan Zhang 					   "0xFFFFFFF1: BIOS Failure");
243*84b1667dSJiang Liu 		else if (res >= 1 && res <= 0x00000FFF)
244f84fdff0SXiaoyan Zhang 			status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
245*84b1667dSJiang Liu 					   req, res, "Corresponding TPM error");
246f84fdff0SXiaoyan Zhang 		else
247f84fdff0SXiaoyan Zhang 			status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
248*84b1667dSJiang Liu 					   req, res, "Error");
249f84fdff0SXiaoyan Zhang 	} else {
250f84fdff0SXiaoyan Zhang 		status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n",
251*84b1667dSJiang Liu 				   req, "No Recent Request");
252f84fdff0SXiaoyan Zhang 	}
253*84b1667dSJiang Liu 
254f84fdff0SXiaoyan Zhang cleanup:
255*84b1667dSJiang Liu 	ACPI_FREE(obj);
256f84fdff0SXiaoyan Zhang 	return status;
257f84fdff0SXiaoyan Zhang }
258f84fdff0SXiaoyan Zhang 
259f84fdff0SXiaoyan Zhang static ssize_t show_ppi_operations(char *buf, u32 start, u32 end)
260f84fdff0SXiaoyan Zhang {
261f84fdff0SXiaoyan Zhang 	int i;
262f84fdff0SXiaoyan Zhang 	u32 ret;
263*84b1667dSJiang Liu 	char *str = buf;
264*84b1667dSJiang Liu 	union acpi_object *obj, tmp;
265*84b1667dSJiang Liu 	union acpi_object argv = ACPI_INIT_DSM_ARGV4(1, &tmp);
266*84b1667dSJiang Liu 
267*84b1667dSJiang Liu 	static char *info[] = {
268f84fdff0SXiaoyan Zhang 		"Not implemented",
269f84fdff0SXiaoyan Zhang 		"BIOS only",
270f84fdff0SXiaoyan Zhang 		"Blocked for OS by BIOS",
271f84fdff0SXiaoyan Zhang 		"User required",
272f84fdff0SXiaoyan Zhang 		"User not required",
273f84fdff0SXiaoyan Zhang 	};
274f84fdff0SXiaoyan Zhang 
275*84b1667dSJiang Liu 	if (strcmp(tpm_ppi_version, "1.2") < 0)
276f84fdff0SXiaoyan Zhang 		return -EPERM;
277f84fdff0SXiaoyan Zhang 
278*84b1667dSJiang Liu 	tmp.integer.type = ACPI_TYPE_INTEGER;
279f84fdff0SXiaoyan Zhang 	for (i = start; i <= end; i++) {
280*84b1667dSJiang Liu 		tmp.integer.value = i;
281*84b1667dSJiang Liu 		obj = tpm_eval_dsm(TPM_PPI_FN_GETOPR, ACPI_TYPE_INTEGER, &argv);
282*84b1667dSJiang Liu 		if (!obj) {
283f84fdff0SXiaoyan Zhang 			return -ENOMEM;
284*84b1667dSJiang Liu 		} else {
285*84b1667dSJiang Liu 			ret = obj->integer.value;
286*84b1667dSJiang Liu 			ACPI_FREE(obj);
287*84b1667dSJiang Liu 		}
288f84fdff0SXiaoyan Zhang 
289f84fdff0SXiaoyan Zhang 		if (ret > 0 && ret < ARRAY_SIZE(info))
290f84fdff0SXiaoyan Zhang 			str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n",
291f84fdff0SXiaoyan Zhang 					 i, ret, info[ret]);
292f84fdff0SXiaoyan Zhang 	}
293*84b1667dSJiang Liu 
294f84fdff0SXiaoyan Zhang 	return str - buf;
295f84fdff0SXiaoyan Zhang }
296f84fdff0SXiaoyan Zhang 
29781198078SXiaoyan Zhang static ssize_t tpm_show_ppi_tcg_operations(struct device *dev,
29881198078SXiaoyan Zhang 					   struct device_attribute *attr,
29981198078SXiaoyan Zhang 					   char *buf)
300f84fdff0SXiaoyan Zhang {
301f84fdff0SXiaoyan Zhang 	return show_ppi_operations(buf, 0, PPI_TPM_REQ_MAX);
302f84fdff0SXiaoyan Zhang }
303f84fdff0SXiaoyan Zhang 
30481198078SXiaoyan Zhang static ssize_t tpm_show_ppi_vs_operations(struct device *dev,
30581198078SXiaoyan Zhang 					  struct device_attribute *attr,
30681198078SXiaoyan Zhang 					  char *buf)
307f84fdff0SXiaoyan Zhang {
308f84fdff0SXiaoyan Zhang 	return show_ppi_operations(buf, PPI_VS_REQ_START, PPI_VS_REQ_END);
309f84fdff0SXiaoyan Zhang }
310f84fdff0SXiaoyan Zhang 
311f84fdff0SXiaoyan Zhang static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL);
312f84fdff0SXiaoyan Zhang static DEVICE_ATTR(request, S_IRUGO | S_IWUSR | S_IWGRP,
313f84fdff0SXiaoyan Zhang 		   tpm_show_ppi_request, tpm_store_ppi_request);
314f84fdff0SXiaoyan Zhang static DEVICE_ATTR(transition_action, S_IRUGO,
315f84fdff0SXiaoyan Zhang 		   tpm_show_ppi_transition_action, NULL);
316f84fdff0SXiaoyan Zhang static DEVICE_ATTR(response, S_IRUGO, tpm_show_ppi_response, NULL);
317f84fdff0SXiaoyan Zhang static DEVICE_ATTR(tcg_operations, S_IRUGO, tpm_show_ppi_tcg_operations, NULL);
318f84fdff0SXiaoyan Zhang static DEVICE_ATTR(vs_operations, S_IRUGO, tpm_show_ppi_vs_operations, NULL);
319f84fdff0SXiaoyan Zhang 
320f84fdff0SXiaoyan Zhang static struct attribute *ppi_attrs[] = {
321f84fdff0SXiaoyan Zhang 	&dev_attr_version.attr,
322f84fdff0SXiaoyan Zhang 	&dev_attr_request.attr,
323f84fdff0SXiaoyan Zhang 	&dev_attr_transition_action.attr,
324f84fdff0SXiaoyan Zhang 	&dev_attr_response.attr,
325f84fdff0SXiaoyan Zhang 	&dev_attr_tcg_operations.attr,
326f84fdff0SXiaoyan Zhang 	&dev_attr_vs_operations.attr, NULL,
327f84fdff0SXiaoyan Zhang };
328f84fdff0SXiaoyan Zhang static struct attribute_group ppi_attr_grp = {
3291631cfb7SGang Wei 	.name = "ppi",
330f84fdff0SXiaoyan Zhang 	.attrs = ppi_attrs
331f84fdff0SXiaoyan Zhang };
332f84fdff0SXiaoyan Zhang 
3331631cfb7SGang Wei int tpm_add_ppi(struct kobject *parent)
334f84fdff0SXiaoyan Zhang {
335*84b1667dSJiang Liu 	/* Cache TPM ACPI handle and version string */
336*84b1667dSJiang Liu 	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
337*84b1667dSJiang Liu 			    ppi_callback, NULL,
338*84b1667dSJiang Liu 			    tpm_device_name, &tpm_ppi_handle);
339*84b1667dSJiang Liu 	if (tpm_ppi_handle == NULL)
340*84b1667dSJiang Liu 		return -ENODEV;
341*84b1667dSJiang Liu 
3421631cfb7SGang Wei 	return sysfs_create_group(parent, &ppi_attr_grp);
343f84fdff0SXiaoyan Zhang }
3441631cfb7SGang Wei 
3451631cfb7SGang Wei void tpm_remove_ppi(struct kobject *parent)
3461631cfb7SGang Wei {
3471631cfb7SGang Wei 	sysfs_remove_group(parent, &ppi_attr_grp);
3481631cfb7SGang Wei }
349