xref: /openbmc/linux/drivers/char/tpm/tpm_ppi.c (revision f84fdff0fdcda7e509ce530e0ee612233a2104fb)
1*f84fdff0SXiaoyan Zhang #include <linux/acpi.h>
2*f84fdff0SXiaoyan Zhang #include <acpi/acpi_drivers.h>
3*f84fdff0SXiaoyan Zhang #include "tpm.h"
4*f84fdff0SXiaoyan Zhang 
5*f84fdff0SXiaoyan Zhang static const u8 tpm_ppi_uuid[] = {
6*f84fdff0SXiaoyan Zhang 	0xA6, 0xFA, 0xDD, 0x3D,
7*f84fdff0SXiaoyan Zhang 	0x1B, 0x36,
8*f84fdff0SXiaoyan Zhang 	0xB4, 0x4E,
9*f84fdff0SXiaoyan Zhang 	0xA4, 0x24,
10*f84fdff0SXiaoyan Zhang 	0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53
11*f84fdff0SXiaoyan Zhang };
12*f84fdff0SXiaoyan Zhang static char *tpm_device_name = "TPM";
13*f84fdff0SXiaoyan Zhang 
14*f84fdff0SXiaoyan Zhang #define TPM_PPI_REVISION_ID	1
15*f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_VERSION	1
16*f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_SUBREQ	2
17*f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETREQ	3
18*f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETACT	4
19*f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETRSP	5
20*f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_SUBREQ2	7
21*f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETOPR	8
22*f84fdff0SXiaoyan Zhang #define PPI_TPM_REQ_MAX		22
23*f84fdff0SXiaoyan Zhang #define PPI_VS_REQ_START	128
24*f84fdff0SXiaoyan Zhang #define PPI_VS_REQ_END		255
25*f84fdff0SXiaoyan Zhang #define PPI_VERSION_LEN		3
26*f84fdff0SXiaoyan Zhang 
27*f84fdff0SXiaoyan Zhang static acpi_status ppi_callback(acpi_handle handle, u32 level, void *context,
28*f84fdff0SXiaoyan Zhang 				void **return_value)
29*f84fdff0SXiaoyan Zhang {
30*f84fdff0SXiaoyan Zhang 	acpi_status status;
31*f84fdff0SXiaoyan Zhang 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
32*f84fdff0SXiaoyan Zhang 	status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
33*f84fdff0SXiaoyan Zhang 	if (strstr(buffer.pointer, context) != NULL) {
34*f84fdff0SXiaoyan Zhang 		*return_value = handle;
35*f84fdff0SXiaoyan Zhang 		kfree(buffer.pointer);
36*f84fdff0SXiaoyan Zhang 		return AE_CTRL_TERMINATE;
37*f84fdff0SXiaoyan Zhang 	}
38*f84fdff0SXiaoyan Zhang 	return AE_OK;
39*f84fdff0SXiaoyan Zhang }
40*f84fdff0SXiaoyan Zhang 
41*f84fdff0SXiaoyan Zhang static inline void ppi_assign_params(union acpi_object params[4],
42*f84fdff0SXiaoyan Zhang 				     u64 function_num)
43*f84fdff0SXiaoyan Zhang {
44*f84fdff0SXiaoyan Zhang 	params[0].type = ACPI_TYPE_BUFFER;
45*f84fdff0SXiaoyan Zhang 	params[0].buffer.length = sizeof(tpm_ppi_uuid);
46*f84fdff0SXiaoyan Zhang 	params[0].buffer.pointer = (char *)tpm_ppi_uuid;
47*f84fdff0SXiaoyan Zhang 	params[1].type = ACPI_TYPE_INTEGER;
48*f84fdff0SXiaoyan Zhang 	params[1].integer.value = TPM_PPI_REVISION_ID;
49*f84fdff0SXiaoyan Zhang 	params[2].type = ACPI_TYPE_INTEGER;
50*f84fdff0SXiaoyan Zhang 	params[2].integer.value = function_num;
51*f84fdff0SXiaoyan Zhang 	params[3].type = ACPI_TYPE_PACKAGE;
52*f84fdff0SXiaoyan Zhang 	params[3].package.count = 0;
53*f84fdff0SXiaoyan Zhang 	params[3].package.elements = NULL;
54*f84fdff0SXiaoyan Zhang }
55*f84fdff0SXiaoyan Zhang 
56*f84fdff0SXiaoyan Zhang ssize_t tpm_show_ppi_version(struct device *dev, struct device_attribute *attr,
57*f84fdff0SXiaoyan Zhang 			     char *buf)
58*f84fdff0SXiaoyan Zhang {
59*f84fdff0SXiaoyan Zhang 	acpi_handle handle;
60*f84fdff0SXiaoyan Zhang 	acpi_status status;
61*f84fdff0SXiaoyan Zhang 	struct acpi_object_list input;
62*f84fdff0SXiaoyan Zhang 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
63*f84fdff0SXiaoyan Zhang 	union acpi_object params[4];
64*f84fdff0SXiaoyan Zhang 	union acpi_object *obj;
65*f84fdff0SXiaoyan Zhang 
66*f84fdff0SXiaoyan Zhang 	input.count = 4;
67*f84fdff0SXiaoyan Zhang 	ppi_assign_params(params, TPM_PPI_FN_VERSION);
68*f84fdff0SXiaoyan Zhang 	input.pointer = params;
69*f84fdff0SXiaoyan Zhang 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
70*f84fdff0SXiaoyan Zhang 				     ACPI_UINT32_MAX, ppi_callback, NULL,
71*f84fdff0SXiaoyan Zhang 				     tpm_device_name, &handle);
72*f84fdff0SXiaoyan Zhang 	if (ACPI_FAILURE(status))
73*f84fdff0SXiaoyan Zhang 		return -ENXIO;
74*f84fdff0SXiaoyan Zhang 
75*f84fdff0SXiaoyan Zhang 	status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
76*f84fdff0SXiaoyan Zhang 					 ACPI_TYPE_STRING);
77*f84fdff0SXiaoyan Zhang 	if (ACPI_FAILURE(status))
78*f84fdff0SXiaoyan Zhang 		return -ENOMEM;
79*f84fdff0SXiaoyan Zhang 	obj = (union acpi_object *)output.pointer;
80*f84fdff0SXiaoyan Zhang 	status = scnprintf(buf, PAGE_SIZE, "%s\n", obj->string.pointer);
81*f84fdff0SXiaoyan Zhang 	kfree(output.pointer);
82*f84fdff0SXiaoyan Zhang 	return status;
83*f84fdff0SXiaoyan Zhang }
84*f84fdff0SXiaoyan Zhang 
85*f84fdff0SXiaoyan Zhang ssize_t tpm_show_ppi_request(struct device *dev,
86*f84fdff0SXiaoyan Zhang 			     struct device_attribute *attr,
87*f84fdff0SXiaoyan Zhang 			     char *buf)
88*f84fdff0SXiaoyan Zhang {
89*f84fdff0SXiaoyan Zhang 	acpi_handle handle;
90*f84fdff0SXiaoyan Zhang 	acpi_status status;
91*f84fdff0SXiaoyan Zhang 	struct acpi_object_list input;
92*f84fdff0SXiaoyan Zhang 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
93*f84fdff0SXiaoyan Zhang 	union acpi_object params[4];
94*f84fdff0SXiaoyan Zhang 	union acpi_object *ret_obj;
95*f84fdff0SXiaoyan Zhang 
96*f84fdff0SXiaoyan Zhang 	input.count = 4;
97*f84fdff0SXiaoyan Zhang 	ppi_assign_params(params, TPM_PPI_FN_GETREQ);
98*f84fdff0SXiaoyan Zhang 	input.pointer = params;
99*f84fdff0SXiaoyan Zhang 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
100*f84fdff0SXiaoyan Zhang 				     ACPI_UINT32_MAX, ppi_callback, NULL,
101*f84fdff0SXiaoyan Zhang 				     tpm_device_name, &handle);
102*f84fdff0SXiaoyan Zhang 	if (ACPI_FAILURE(status))
103*f84fdff0SXiaoyan Zhang 		return -ENXIO;
104*f84fdff0SXiaoyan Zhang 
105*f84fdff0SXiaoyan Zhang 	status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
106*f84fdff0SXiaoyan Zhang 					    ACPI_TYPE_PACKAGE);
107*f84fdff0SXiaoyan Zhang 	if (ACPI_FAILURE(status))
108*f84fdff0SXiaoyan Zhang 		return -ENOMEM;
109*f84fdff0SXiaoyan Zhang 	/*
110*f84fdff0SXiaoyan Zhang 	 * output.pointer should be of package type, including two integers.
111*f84fdff0SXiaoyan Zhang 	 * The first is function return code, 0 means success and 1 means
112*f84fdff0SXiaoyan Zhang 	 * error. The second is pending TPM operation requested by the OS, 0
113*f84fdff0SXiaoyan Zhang 	 * means none and >0 means operation value.
114*f84fdff0SXiaoyan Zhang 	 */
115*f84fdff0SXiaoyan Zhang 	ret_obj = ((union acpi_object *)output.pointer)->package.elements;
116*f84fdff0SXiaoyan Zhang 	if (ret_obj->type == ACPI_TYPE_INTEGER) {
117*f84fdff0SXiaoyan Zhang 		if (ret_obj->integer.value) {
118*f84fdff0SXiaoyan Zhang 			status = -EFAULT;
119*f84fdff0SXiaoyan Zhang 			goto cleanup;
120*f84fdff0SXiaoyan Zhang 		}
121*f84fdff0SXiaoyan Zhang 		ret_obj++;
122*f84fdff0SXiaoyan Zhang 		if (ret_obj->type == ACPI_TYPE_INTEGER)
123*f84fdff0SXiaoyan Zhang 			status = scnprintf(buf, PAGE_SIZE, "%llu\n",
124*f84fdff0SXiaoyan Zhang 					   ret_obj->integer.value);
125*f84fdff0SXiaoyan Zhang 		else
126*f84fdff0SXiaoyan Zhang 			status = -EINVAL;
127*f84fdff0SXiaoyan Zhang 	} else {
128*f84fdff0SXiaoyan Zhang 		status = -EINVAL;
129*f84fdff0SXiaoyan Zhang 	}
130*f84fdff0SXiaoyan Zhang cleanup:
131*f84fdff0SXiaoyan Zhang 	kfree(output.pointer);
132*f84fdff0SXiaoyan Zhang 	return status;
133*f84fdff0SXiaoyan Zhang }
134*f84fdff0SXiaoyan Zhang 
135*f84fdff0SXiaoyan Zhang ssize_t tpm_store_ppi_request(struct device *dev,
136*f84fdff0SXiaoyan Zhang 			      struct device_attribute *attr,
137*f84fdff0SXiaoyan Zhang 			      const char *buf, size_t count)
138*f84fdff0SXiaoyan Zhang {
139*f84fdff0SXiaoyan Zhang 	char version[PPI_VERSION_LEN + 1];
140*f84fdff0SXiaoyan Zhang 	acpi_handle handle;
141*f84fdff0SXiaoyan Zhang 	acpi_status status;
142*f84fdff0SXiaoyan Zhang 	struct acpi_object_list input;
143*f84fdff0SXiaoyan Zhang 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
144*f84fdff0SXiaoyan Zhang 	union acpi_object params[4];
145*f84fdff0SXiaoyan Zhang 	union acpi_object obj;
146*f84fdff0SXiaoyan Zhang 	u32 req;
147*f84fdff0SXiaoyan Zhang 	u64 ret;
148*f84fdff0SXiaoyan Zhang 
149*f84fdff0SXiaoyan Zhang 	input.count = 4;
150*f84fdff0SXiaoyan Zhang 	ppi_assign_params(params, TPM_PPI_FN_VERSION);
151*f84fdff0SXiaoyan Zhang 	input.pointer = params;
152*f84fdff0SXiaoyan Zhang 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
153*f84fdff0SXiaoyan Zhang 				     ACPI_UINT32_MAX, ppi_callback, NULL,
154*f84fdff0SXiaoyan Zhang 				     tpm_device_name, &handle);
155*f84fdff0SXiaoyan Zhang 	if (ACPI_FAILURE(status))
156*f84fdff0SXiaoyan Zhang 		return -ENXIO;
157*f84fdff0SXiaoyan Zhang 
158*f84fdff0SXiaoyan Zhang 	status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
159*f84fdff0SXiaoyan Zhang 					    ACPI_TYPE_STRING);
160*f84fdff0SXiaoyan Zhang 	if (ACPI_FAILURE(status))
161*f84fdff0SXiaoyan Zhang 		return -ENOMEM;
162*f84fdff0SXiaoyan Zhang 	strncpy(version,
163*f84fdff0SXiaoyan Zhang 		((union acpi_object *)output.pointer)->string.pointer,
164*f84fdff0SXiaoyan Zhang 		PPI_VERSION_LEN);
165*f84fdff0SXiaoyan Zhang 	kfree(output.pointer);
166*f84fdff0SXiaoyan Zhang 	output.length = ACPI_ALLOCATE_BUFFER;
167*f84fdff0SXiaoyan Zhang 	output.pointer = NULL;
168*f84fdff0SXiaoyan Zhang 	/*
169*f84fdff0SXiaoyan Zhang 	 * the function to submit TPM operation request to pre-os environment
170*f84fdff0SXiaoyan Zhang 	 * is updated with function index from SUBREQ to SUBREQ2 since PPI
171*f84fdff0SXiaoyan Zhang 	 * version 1.1
172*f84fdff0SXiaoyan Zhang 	 */
173*f84fdff0SXiaoyan Zhang 	if (strcmp(version, "1.1") == -1)
174*f84fdff0SXiaoyan Zhang 		params[2].integer.value = TPM_PPI_FN_SUBREQ;
175*f84fdff0SXiaoyan Zhang 	else
176*f84fdff0SXiaoyan Zhang 		params[2].integer.value = TPM_PPI_FN_SUBREQ2;
177*f84fdff0SXiaoyan Zhang 	/*
178*f84fdff0SXiaoyan Zhang 	 * PPI spec defines params[3].type as ACPI_TYPE_PACKAGE. Some BIOS
179*f84fdff0SXiaoyan Zhang 	 * accept buffer/string/integer type, but some BIOS accept buffer/
180*f84fdff0SXiaoyan Zhang 	 * string/package type. For PPI version 1.0 and 1.1, use buffer type
181*f84fdff0SXiaoyan Zhang 	 * for compatibility, and use package type since 1.2 according to spec.
182*f84fdff0SXiaoyan Zhang 	 */
183*f84fdff0SXiaoyan Zhang 	if (strcmp(version, "1.2") == -1) {
184*f84fdff0SXiaoyan Zhang 		params[3].type = ACPI_TYPE_BUFFER;
185*f84fdff0SXiaoyan Zhang 		params[3].buffer.length = sizeof(req);
186*f84fdff0SXiaoyan Zhang 		sscanf(buf, "%d", &req);
187*f84fdff0SXiaoyan Zhang 		params[3].buffer.pointer = (char *)&req;
188*f84fdff0SXiaoyan Zhang 	} else {
189*f84fdff0SXiaoyan Zhang 		params[3].package.count = 1;
190*f84fdff0SXiaoyan Zhang 		obj.type = ACPI_TYPE_INTEGER;
191*f84fdff0SXiaoyan Zhang 		sscanf(buf, "%llu", &obj.integer.value);
192*f84fdff0SXiaoyan Zhang 		params[3].package.elements = &obj;
193*f84fdff0SXiaoyan Zhang 	}
194*f84fdff0SXiaoyan Zhang 
195*f84fdff0SXiaoyan Zhang 	status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
196*f84fdff0SXiaoyan Zhang 					    ACPI_TYPE_INTEGER);
197*f84fdff0SXiaoyan Zhang 	if (ACPI_FAILURE(status))
198*f84fdff0SXiaoyan Zhang 		return -ENOMEM;
199*f84fdff0SXiaoyan Zhang 	ret = ((union acpi_object *)output.pointer)->integer.value;
200*f84fdff0SXiaoyan Zhang 	if (ret == 0)
201*f84fdff0SXiaoyan Zhang 		status = (acpi_status)count;
202*f84fdff0SXiaoyan Zhang 	else if (ret == 1)
203*f84fdff0SXiaoyan Zhang 		status = -EPERM;
204*f84fdff0SXiaoyan Zhang 	else
205*f84fdff0SXiaoyan Zhang 		status = -EFAULT;
206*f84fdff0SXiaoyan Zhang 	kfree(output.pointer);
207*f84fdff0SXiaoyan Zhang 	return status;
208*f84fdff0SXiaoyan Zhang }
209*f84fdff0SXiaoyan Zhang 
210*f84fdff0SXiaoyan Zhang ssize_t tpm_show_ppi_transition_action(struct device *dev,
211*f84fdff0SXiaoyan Zhang 				       struct device_attribute *attr,
212*f84fdff0SXiaoyan Zhang 				       char *buf)
213*f84fdff0SXiaoyan Zhang {
214*f84fdff0SXiaoyan Zhang 	char version[PPI_VERSION_LEN + 1];
215*f84fdff0SXiaoyan Zhang 	acpi_handle handle;
216*f84fdff0SXiaoyan Zhang 	acpi_status status;
217*f84fdff0SXiaoyan Zhang 	struct acpi_object_list input;
218*f84fdff0SXiaoyan Zhang 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
219*f84fdff0SXiaoyan Zhang 	union acpi_object params[4];
220*f84fdff0SXiaoyan Zhang 	u32 ret;
221*f84fdff0SXiaoyan Zhang 	char *info[] = {
222*f84fdff0SXiaoyan Zhang 		"None",
223*f84fdff0SXiaoyan Zhang 		"Shutdown",
224*f84fdff0SXiaoyan Zhang 		"Reboot",
225*f84fdff0SXiaoyan Zhang 		"OS Vendor-specific",
226*f84fdff0SXiaoyan Zhang 		"Error",
227*f84fdff0SXiaoyan Zhang 	};
228*f84fdff0SXiaoyan Zhang 	input.count = 4;
229*f84fdff0SXiaoyan Zhang 	ppi_assign_params(params, TPM_PPI_FN_VERSION);
230*f84fdff0SXiaoyan Zhang 	input.pointer = params;
231*f84fdff0SXiaoyan Zhang 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
232*f84fdff0SXiaoyan Zhang 				     ACPI_UINT32_MAX, ppi_callback, NULL,
233*f84fdff0SXiaoyan Zhang 				     tpm_device_name, &handle);
234*f84fdff0SXiaoyan Zhang 	if (ACPI_FAILURE(status))
235*f84fdff0SXiaoyan Zhang 		return -ENXIO;
236*f84fdff0SXiaoyan Zhang 
237*f84fdff0SXiaoyan Zhang 	status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
238*f84fdff0SXiaoyan Zhang 					    ACPI_TYPE_STRING);
239*f84fdff0SXiaoyan Zhang 	if (ACPI_FAILURE(status))
240*f84fdff0SXiaoyan Zhang 		return -ENOMEM;
241*f84fdff0SXiaoyan Zhang 	strncpy(version,
242*f84fdff0SXiaoyan Zhang 		((union acpi_object *)output.pointer)->string.pointer,
243*f84fdff0SXiaoyan Zhang 		PPI_VERSION_LEN);
244*f84fdff0SXiaoyan Zhang 	/*
245*f84fdff0SXiaoyan Zhang 	 * PPI spec defines params[3].type as empty package, but some platforms
246*f84fdff0SXiaoyan Zhang 	 * (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for
247*f84fdff0SXiaoyan Zhang 	 * compatibility, define params[3].type as buffer, if PPI version < 1.2
248*f84fdff0SXiaoyan Zhang 	 */
249*f84fdff0SXiaoyan Zhang 	if (strcmp(version, "1.2") == -1) {
250*f84fdff0SXiaoyan Zhang 		params[3].type = ACPI_TYPE_BUFFER;
251*f84fdff0SXiaoyan Zhang 		params[3].buffer.length =  0;
252*f84fdff0SXiaoyan Zhang 		params[3].buffer.pointer = NULL;
253*f84fdff0SXiaoyan Zhang 	}
254*f84fdff0SXiaoyan Zhang 	params[2].integer.value = TPM_PPI_FN_GETACT;
255*f84fdff0SXiaoyan Zhang 	kfree(output.pointer);
256*f84fdff0SXiaoyan Zhang 	output.length = ACPI_ALLOCATE_BUFFER;
257*f84fdff0SXiaoyan Zhang 	output.pointer = NULL;
258*f84fdff0SXiaoyan Zhang 	status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
259*f84fdff0SXiaoyan Zhang 					    ACPI_TYPE_INTEGER);
260*f84fdff0SXiaoyan Zhang 	if (ACPI_FAILURE(status))
261*f84fdff0SXiaoyan Zhang 		return -ENOMEM;
262*f84fdff0SXiaoyan Zhang 	ret = ((union acpi_object *)output.pointer)->integer.value;
263*f84fdff0SXiaoyan Zhang 	if (ret < ARRAY_SIZE(info) - 1)
264*f84fdff0SXiaoyan Zhang 		status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]);
265*f84fdff0SXiaoyan Zhang 	else
266*f84fdff0SXiaoyan Zhang 		status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret,
267*f84fdff0SXiaoyan Zhang 				   info[ARRAY_SIZE(info)-1]);
268*f84fdff0SXiaoyan Zhang 	kfree(output.pointer);
269*f84fdff0SXiaoyan Zhang 	return status;
270*f84fdff0SXiaoyan Zhang }
271*f84fdff0SXiaoyan Zhang 
272*f84fdff0SXiaoyan Zhang ssize_t tpm_show_ppi_response(struct device *dev,
273*f84fdff0SXiaoyan Zhang 			      struct device_attribute *attr,
274*f84fdff0SXiaoyan Zhang 			      char *buf)
275*f84fdff0SXiaoyan Zhang {
276*f84fdff0SXiaoyan Zhang 	acpi_handle handle;
277*f84fdff0SXiaoyan Zhang 	acpi_status status;
278*f84fdff0SXiaoyan Zhang 	struct acpi_object_list input;
279*f84fdff0SXiaoyan Zhang 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
280*f84fdff0SXiaoyan Zhang 	union acpi_object params[4];
281*f84fdff0SXiaoyan Zhang 	union acpi_object *ret_obj;
282*f84fdff0SXiaoyan Zhang 	u64 req;
283*f84fdff0SXiaoyan Zhang 
284*f84fdff0SXiaoyan Zhang 	input.count = 4;
285*f84fdff0SXiaoyan Zhang 	ppi_assign_params(params, TPM_PPI_FN_GETRSP);
286*f84fdff0SXiaoyan Zhang 	input.pointer = params;
287*f84fdff0SXiaoyan Zhang 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
288*f84fdff0SXiaoyan Zhang 				     ACPI_UINT32_MAX, ppi_callback, NULL,
289*f84fdff0SXiaoyan Zhang 				     tpm_device_name, &handle);
290*f84fdff0SXiaoyan Zhang 	if (ACPI_FAILURE(status))
291*f84fdff0SXiaoyan Zhang 		return -ENXIO;
292*f84fdff0SXiaoyan Zhang 
293*f84fdff0SXiaoyan Zhang 	status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
294*f84fdff0SXiaoyan Zhang 					    ACPI_TYPE_PACKAGE);
295*f84fdff0SXiaoyan Zhang 	if (ACPI_FAILURE(status))
296*f84fdff0SXiaoyan Zhang 		return -ENOMEM;
297*f84fdff0SXiaoyan Zhang 	/*
298*f84fdff0SXiaoyan Zhang 	 * parameter output.pointer should be of package type, including
299*f84fdff0SXiaoyan Zhang 	 * 3 integers. The first means function return code, the second means
300*f84fdff0SXiaoyan Zhang 	 * most recent TPM operation request, and the last means response to
301*f84fdff0SXiaoyan Zhang 	 * the most recent TPM operation request. Only if the first is 0, and
302*f84fdff0SXiaoyan Zhang 	 * the second integer is not 0, the response makes sense.
303*f84fdff0SXiaoyan Zhang 	 */
304*f84fdff0SXiaoyan Zhang 	ret_obj = ((union acpi_object *)output.pointer)->package.elements;
305*f84fdff0SXiaoyan Zhang 	if (ret_obj->type != ACPI_TYPE_INTEGER) {
306*f84fdff0SXiaoyan Zhang 		status = -EINVAL;
307*f84fdff0SXiaoyan Zhang 		goto cleanup;
308*f84fdff0SXiaoyan Zhang 	}
309*f84fdff0SXiaoyan Zhang 	if (ret_obj->integer.value) {
310*f84fdff0SXiaoyan Zhang 		status = -EFAULT;
311*f84fdff0SXiaoyan Zhang 		goto cleanup;
312*f84fdff0SXiaoyan Zhang 	}
313*f84fdff0SXiaoyan Zhang 	ret_obj++;
314*f84fdff0SXiaoyan Zhang 	if (ret_obj->type != ACPI_TYPE_INTEGER) {
315*f84fdff0SXiaoyan Zhang 		status = -EINVAL;
316*f84fdff0SXiaoyan Zhang 		goto cleanup;
317*f84fdff0SXiaoyan Zhang 	}
318*f84fdff0SXiaoyan Zhang 	if (ret_obj->integer.value) {
319*f84fdff0SXiaoyan Zhang 		req = ret_obj->integer.value;
320*f84fdff0SXiaoyan Zhang 		ret_obj++;
321*f84fdff0SXiaoyan Zhang 		if (ret_obj->type != ACPI_TYPE_INTEGER) {
322*f84fdff0SXiaoyan Zhang 			status = -EINVAL;
323*f84fdff0SXiaoyan Zhang 			goto cleanup;
324*f84fdff0SXiaoyan Zhang 		}
325*f84fdff0SXiaoyan Zhang 		if (ret_obj->integer.value == 0)
326*f84fdff0SXiaoyan Zhang 			status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
327*f84fdff0SXiaoyan Zhang 					   "0: Success");
328*f84fdff0SXiaoyan Zhang 		else if (ret_obj->integer.value == 0xFFFFFFF0)
329*f84fdff0SXiaoyan Zhang 			status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
330*f84fdff0SXiaoyan Zhang 					   "0xFFFFFFF0: User Abort");
331*f84fdff0SXiaoyan Zhang 		else if (ret_obj->integer.value == 0xFFFFFFF1)
332*f84fdff0SXiaoyan Zhang 			status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
333*f84fdff0SXiaoyan Zhang 					   "0xFFFFFFF1: BIOS Failure");
334*f84fdff0SXiaoyan Zhang 		else if (ret_obj->integer.value >= 1 &&
335*f84fdff0SXiaoyan Zhang 			 ret_obj->integer.value <= 0x00000FFF)
336*f84fdff0SXiaoyan Zhang 			status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
337*f84fdff0SXiaoyan Zhang 					   req, ret_obj->integer.value,
338*f84fdff0SXiaoyan Zhang 					   "Corresponding TPM error");
339*f84fdff0SXiaoyan Zhang 		else
340*f84fdff0SXiaoyan Zhang 			status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
341*f84fdff0SXiaoyan Zhang 					   req, ret_obj->integer.value,
342*f84fdff0SXiaoyan Zhang 					   "Error");
343*f84fdff0SXiaoyan Zhang 	} else {
344*f84fdff0SXiaoyan Zhang 		status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n",
345*f84fdff0SXiaoyan Zhang 				   ret_obj->integer.value, "No Recent Request");
346*f84fdff0SXiaoyan Zhang 	}
347*f84fdff0SXiaoyan Zhang cleanup:
348*f84fdff0SXiaoyan Zhang 	kfree(output.pointer);
349*f84fdff0SXiaoyan Zhang 	return status;
350*f84fdff0SXiaoyan Zhang }
351*f84fdff0SXiaoyan Zhang 
352*f84fdff0SXiaoyan Zhang static ssize_t show_ppi_operations(char *buf, u32 start, u32 end)
353*f84fdff0SXiaoyan Zhang {
354*f84fdff0SXiaoyan Zhang 	char *str = buf;
355*f84fdff0SXiaoyan Zhang 	char version[PPI_VERSION_LEN];
356*f84fdff0SXiaoyan Zhang 	acpi_handle handle;
357*f84fdff0SXiaoyan Zhang 	acpi_status status;
358*f84fdff0SXiaoyan Zhang 	struct acpi_object_list input;
359*f84fdff0SXiaoyan Zhang 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
360*f84fdff0SXiaoyan Zhang 	union acpi_object params[4];
361*f84fdff0SXiaoyan Zhang 	union acpi_object obj;
362*f84fdff0SXiaoyan Zhang 	int i;
363*f84fdff0SXiaoyan Zhang 	u32 ret;
364*f84fdff0SXiaoyan Zhang 	char *info[] = {
365*f84fdff0SXiaoyan Zhang 		"Not implemented",
366*f84fdff0SXiaoyan Zhang 		"BIOS only",
367*f84fdff0SXiaoyan Zhang 		"Blocked for OS by BIOS",
368*f84fdff0SXiaoyan Zhang 		"User required",
369*f84fdff0SXiaoyan Zhang 		"User not required",
370*f84fdff0SXiaoyan Zhang 	};
371*f84fdff0SXiaoyan Zhang 	input.count = 4;
372*f84fdff0SXiaoyan Zhang 	ppi_assign_params(params, TPM_PPI_FN_VERSION);
373*f84fdff0SXiaoyan Zhang 	input.pointer = params;
374*f84fdff0SXiaoyan Zhang 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
375*f84fdff0SXiaoyan Zhang 				     ACPI_UINT32_MAX, ppi_callback, NULL,
376*f84fdff0SXiaoyan Zhang 				     tpm_device_name, &handle);
377*f84fdff0SXiaoyan Zhang 	if (ACPI_FAILURE(status))
378*f84fdff0SXiaoyan Zhang 		return -ENXIO;
379*f84fdff0SXiaoyan Zhang 
380*f84fdff0SXiaoyan Zhang 	status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
381*f84fdff0SXiaoyan Zhang 					 ACPI_TYPE_STRING);
382*f84fdff0SXiaoyan Zhang 	if (ACPI_FAILURE(status))
383*f84fdff0SXiaoyan Zhang 		return -ENOMEM;
384*f84fdff0SXiaoyan Zhang 
385*f84fdff0SXiaoyan Zhang 	strncpy(version,
386*f84fdff0SXiaoyan Zhang 		((union acpi_object *)output.pointer)->string.pointer,
387*f84fdff0SXiaoyan Zhang 		PPI_VERSION_LEN);
388*f84fdff0SXiaoyan Zhang 	kfree(output.pointer);
389*f84fdff0SXiaoyan Zhang 	output.length = ACPI_ALLOCATE_BUFFER;
390*f84fdff0SXiaoyan Zhang 	output.pointer = NULL;
391*f84fdff0SXiaoyan Zhang 	if (strcmp(version, "1.2") == -1)
392*f84fdff0SXiaoyan Zhang 		return -EPERM;
393*f84fdff0SXiaoyan Zhang 
394*f84fdff0SXiaoyan Zhang 	params[2].integer.value = TPM_PPI_FN_GETOPR;
395*f84fdff0SXiaoyan Zhang 	params[3].package.count = 1;
396*f84fdff0SXiaoyan Zhang 	obj.type = ACPI_TYPE_INTEGER;
397*f84fdff0SXiaoyan Zhang 	params[3].package.elements = &obj;
398*f84fdff0SXiaoyan Zhang 	for (i = start; i <= end; i++) {
399*f84fdff0SXiaoyan Zhang 		obj.integer.value = i;
400*f84fdff0SXiaoyan Zhang 		status = acpi_evaluate_object_typed(handle, "_DSM",
401*f84fdff0SXiaoyan Zhang 			 &input, &output, ACPI_TYPE_INTEGER);
402*f84fdff0SXiaoyan Zhang 		if (ACPI_FAILURE(status))
403*f84fdff0SXiaoyan Zhang 			return -ENOMEM;
404*f84fdff0SXiaoyan Zhang 
405*f84fdff0SXiaoyan Zhang 		ret = ((union acpi_object *)output.pointer)->integer.value;
406*f84fdff0SXiaoyan Zhang 		if (ret > 0 && ret < ARRAY_SIZE(info))
407*f84fdff0SXiaoyan Zhang 			str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n",
408*f84fdff0SXiaoyan Zhang 					 i, ret, info[ret]);
409*f84fdff0SXiaoyan Zhang 		kfree(output.pointer);
410*f84fdff0SXiaoyan Zhang 		output.length = ACPI_ALLOCATE_BUFFER;
411*f84fdff0SXiaoyan Zhang 		output.pointer = NULL;
412*f84fdff0SXiaoyan Zhang 	}
413*f84fdff0SXiaoyan Zhang 	return str - buf;
414*f84fdff0SXiaoyan Zhang }
415*f84fdff0SXiaoyan Zhang 
416*f84fdff0SXiaoyan Zhang ssize_t tpm_show_ppi_tcg_operations(struct device *dev,
417*f84fdff0SXiaoyan Zhang 				   struct device_attribute *attr, char *buf)
418*f84fdff0SXiaoyan Zhang {
419*f84fdff0SXiaoyan Zhang 	return show_ppi_operations(buf, 0, PPI_TPM_REQ_MAX);
420*f84fdff0SXiaoyan Zhang }
421*f84fdff0SXiaoyan Zhang 
422*f84fdff0SXiaoyan Zhang ssize_t tpm_show_ppi_vs_operations(struct device *dev,
423*f84fdff0SXiaoyan Zhang 				  struct device_attribute *attr, char *buf)
424*f84fdff0SXiaoyan Zhang {
425*f84fdff0SXiaoyan Zhang 	return show_ppi_operations(buf, PPI_VS_REQ_START, PPI_VS_REQ_END);
426*f84fdff0SXiaoyan Zhang }
427*f84fdff0SXiaoyan Zhang 
428*f84fdff0SXiaoyan Zhang static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL);
429*f84fdff0SXiaoyan Zhang static DEVICE_ATTR(request, S_IRUGO | S_IWUSR | S_IWGRP,
430*f84fdff0SXiaoyan Zhang 		   tpm_show_ppi_request, tpm_store_ppi_request);
431*f84fdff0SXiaoyan Zhang static DEVICE_ATTR(transition_action, S_IRUGO,
432*f84fdff0SXiaoyan Zhang 		   tpm_show_ppi_transition_action, NULL);
433*f84fdff0SXiaoyan Zhang static DEVICE_ATTR(response, S_IRUGO, tpm_show_ppi_response, NULL);
434*f84fdff0SXiaoyan Zhang static DEVICE_ATTR(tcg_operations, S_IRUGO, tpm_show_ppi_tcg_operations, NULL);
435*f84fdff0SXiaoyan Zhang static DEVICE_ATTR(vs_operations, S_IRUGO, tpm_show_ppi_vs_operations, NULL);
436*f84fdff0SXiaoyan Zhang 
437*f84fdff0SXiaoyan Zhang static struct attribute *ppi_attrs[] = {
438*f84fdff0SXiaoyan Zhang 	&dev_attr_version.attr,
439*f84fdff0SXiaoyan Zhang 	&dev_attr_request.attr,
440*f84fdff0SXiaoyan Zhang 	&dev_attr_transition_action.attr,
441*f84fdff0SXiaoyan Zhang 	&dev_attr_response.attr,
442*f84fdff0SXiaoyan Zhang 	&dev_attr_tcg_operations.attr,
443*f84fdff0SXiaoyan Zhang 	&dev_attr_vs_operations.attr, NULL,
444*f84fdff0SXiaoyan Zhang };
445*f84fdff0SXiaoyan Zhang static struct attribute_group ppi_attr_grp = {
446*f84fdff0SXiaoyan Zhang 	.attrs = ppi_attrs
447*f84fdff0SXiaoyan Zhang };
448*f84fdff0SXiaoyan Zhang 
449*f84fdff0SXiaoyan Zhang ssize_t sys_add_ppi(struct kobject *parent)
450*f84fdff0SXiaoyan Zhang {
451*f84fdff0SXiaoyan Zhang 	struct kobject *ppi;
452*f84fdff0SXiaoyan Zhang 	ppi = kobject_create_and_add("ppi", parent);
453*f84fdff0SXiaoyan Zhang 	if (sysfs_create_group(ppi, &ppi_attr_grp))
454*f84fdff0SXiaoyan Zhang 		return -EFAULT;
455*f84fdff0SXiaoyan Zhang 	else
456*f84fdff0SXiaoyan Zhang 		return 0;
457*f84fdff0SXiaoyan Zhang }
458*f84fdff0SXiaoyan Zhang EXPORT_SYMBOL_GPL(sys_add_ppi);
459*f84fdff0SXiaoyan Zhang 
460*f84fdff0SXiaoyan Zhang MODULE_LICENSE("GPL");
461