10dc55365SJarkko Sakkinen /* 20dc55365SJarkko Sakkinen * Copyright (C) 2012-2014 Intel Corporation 30dc55365SJarkko Sakkinen * 40dc55365SJarkko Sakkinen * Authors: 50dc55365SJarkko Sakkinen * Xiaoyan Zhang <xiaoyan.zhang@intel.com> 60dc55365SJarkko Sakkinen * Jiang Liu <jiang.liu@linux.intel.com> 70dc55365SJarkko Sakkinen * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> 80dc55365SJarkko Sakkinen * 90dc55365SJarkko Sakkinen * Maintained by: <tpmdd-devel@lists.sourceforge.net> 100dc55365SJarkko Sakkinen * 110dc55365SJarkko Sakkinen * This file contains implementation of the sysfs interface for PPI. 120dc55365SJarkko Sakkinen * 130dc55365SJarkko Sakkinen * This program is free software; you can redistribute it and/or 140dc55365SJarkko Sakkinen * modify it under the terms of the GNU General Public License 150dc55365SJarkko Sakkinen * as published by the Free Software Foundation; version 2 160dc55365SJarkko Sakkinen * of the License. 170dc55365SJarkko Sakkinen */ 180dc55365SJarkko Sakkinen 190dc55365SJarkko Sakkinen 20f84fdff0SXiaoyan Zhang #include <linux/acpi.h> 21f84fdff0SXiaoyan Zhang #include "tpm.h" 22f84fdff0SXiaoyan Zhang 2309fe1b42SStefan Berger #define TPM_PPI_REVISION_ID_1 1 24*8b60c79bSStefan Berger #define TPM_PPI_REVISION_ID_2 2 25f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_VERSION 1 26f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_SUBREQ 2 27f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETREQ 3 28f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETACT 4 29f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETRSP 5 30f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_SUBREQ2 7 31f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETOPR 8 329d4023edSStefan Berger #define PPI_TPM_REQ_MAX 101 /* PPI 1.3 for TPM 2 */ 33f84fdff0SXiaoyan Zhang #define PPI_VS_REQ_START 128 34f84fdff0SXiaoyan Zhang #define PPI_VS_REQ_END 255 35f84fdff0SXiaoyan Zhang 3694116f81SAndy Shevchenko static const guid_t tpm_ppi_guid = 3794116f81SAndy Shevchenko GUID_INIT(0x3DDDFAA6, 0x361B, 0x4EB4, 3894116f81SAndy Shevchenko 0xA4, 0x24, 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53); 3984b1667dSJiang Liu 40*8b60c79bSStefan Berger static bool tpm_ppi_req_has_parameter(u64 req) 41*8b60c79bSStefan Berger { 42*8b60c79bSStefan Berger return req == 23; 43*8b60c79bSStefan Berger } 44*8b60c79bSStefan Berger 4584b1667dSJiang Liu static inline union acpi_object * 460dc55365SJarkko Sakkinen tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type, 47587bad77SStefan Berger union acpi_object *argv4, u64 rev) 48f84fdff0SXiaoyan Zhang { 490dc55365SJarkko Sakkinen BUG_ON(!ppi_handle); 5094116f81SAndy Shevchenko return acpi_evaluate_dsm_typed(ppi_handle, &tpm_ppi_guid, 51587bad77SStefan Berger rev, func, argv4, type); 52f84fdff0SXiaoyan Zhang } 53f84fdff0SXiaoyan Zhang 5481198078SXiaoyan Zhang static ssize_t tpm_show_ppi_version(struct device *dev, 5581198078SXiaoyan Zhang struct device_attribute *attr, char *buf) 56f84fdff0SXiaoyan Zhang { 579b774d5cSJarkko Sakkinen struct tpm_chip *chip = to_tpm_chip(dev); 580dc55365SJarkko Sakkinen 590dc55365SJarkko Sakkinen return scnprintf(buf, PAGE_SIZE, "%s\n", chip->ppi_version); 60f84fdff0SXiaoyan Zhang } 61f84fdff0SXiaoyan Zhang 6281198078SXiaoyan Zhang static ssize_t tpm_show_ppi_request(struct device *dev, 6381198078SXiaoyan Zhang struct device_attribute *attr, char *buf) 64f84fdff0SXiaoyan Zhang { 6584b1667dSJiang Liu ssize_t size = -EINVAL; 6684b1667dSJiang Liu union acpi_object *obj; 679b774d5cSJarkko Sakkinen struct tpm_chip *chip = to_tpm_chip(dev); 68*8b60c79bSStefan Berger u64 rev = TPM_PPI_REVISION_ID_2; 69*8b60c79bSStefan Berger u64 req; 70*8b60c79bSStefan Berger 71*8b60c79bSStefan Berger if (strcmp(chip->ppi_version, "1.2") < 0) 72*8b60c79bSStefan Berger rev = TPM_PPI_REVISION_ID_1; 73f84fdff0SXiaoyan Zhang 740dc55365SJarkko Sakkinen obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETREQ, 75*8b60c79bSStefan Berger ACPI_TYPE_PACKAGE, NULL, rev); 7684b1667dSJiang Liu if (!obj) 77f84fdff0SXiaoyan Zhang return -ENXIO; 78f84fdff0SXiaoyan Zhang 79f84fdff0SXiaoyan Zhang /* 80f84fdff0SXiaoyan Zhang * output.pointer should be of package type, including two integers. 81f84fdff0SXiaoyan Zhang * The first is function return code, 0 means success and 1 means 82f84fdff0SXiaoyan Zhang * error. The second is pending TPM operation requested by the OS, 0 83f84fdff0SXiaoyan Zhang * means none and >0 means operation value. 84f84fdff0SXiaoyan Zhang */ 85*8b60c79bSStefan Berger if (obj->package.count == 3 && 86*8b60c79bSStefan Berger obj->package.elements[0].type == ACPI_TYPE_INTEGER && 87*8b60c79bSStefan Berger obj->package.elements[1].type == ACPI_TYPE_INTEGER && 88*8b60c79bSStefan Berger obj->package.elements[2].type == ACPI_TYPE_INTEGER) { 89*8b60c79bSStefan Berger if (obj->package.elements[0].integer.value) 90*8b60c79bSStefan Berger size = -EFAULT; 91*8b60c79bSStefan Berger else { 92*8b60c79bSStefan Berger req = obj->package.elements[1].integer.value; 93*8b60c79bSStefan Berger if (tpm_ppi_req_has_parameter(req)) 94*8b60c79bSStefan Berger size = scnprintf(buf, PAGE_SIZE, 95*8b60c79bSStefan Berger "%llu %llu\n", req, 96*8b60c79bSStefan Berger obj->package.elements[2].integer.value); 97*8b60c79bSStefan Berger else 98*8b60c79bSStefan Berger size = scnprintf(buf, PAGE_SIZE, 99*8b60c79bSStefan Berger "%llu\n", req); 100*8b60c79bSStefan Berger } 101*8b60c79bSStefan Berger } else if (obj->package.count == 2 && 10284b1667dSJiang Liu obj->package.elements[0].type == ACPI_TYPE_INTEGER && 10384b1667dSJiang Liu obj->package.elements[1].type == ACPI_TYPE_INTEGER) { 10484b1667dSJiang Liu if (obj->package.elements[0].integer.value) 10584b1667dSJiang Liu size = -EFAULT; 106f84fdff0SXiaoyan Zhang else 10784b1667dSJiang Liu size = scnprintf(buf, PAGE_SIZE, "%llu\n", 10884b1667dSJiang Liu obj->package.elements[1].integer.value); 109f84fdff0SXiaoyan Zhang } 11084b1667dSJiang Liu 11184b1667dSJiang Liu ACPI_FREE(obj); 11284b1667dSJiang Liu 11384b1667dSJiang Liu return size; 114f84fdff0SXiaoyan Zhang } 115f84fdff0SXiaoyan Zhang 11681198078SXiaoyan Zhang static ssize_t tpm_store_ppi_request(struct device *dev, 117f84fdff0SXiaoyan Zhang struct device_attribute *attr, 118f84fdff0SXiaoyan Zhang const char *buf, size_t count) 119f84fdff0SXiaoyan Zhang { 120f84fdff0SXiaoyan Zhang u32 req; 121f84fdff0SXiaoyan Zhang u64 ret; 12284b1667dSJiang Liu int func = TPM_PPI_FN_SUBREQ; 12384b1667dSJiang Liu union acpi_object *obj, tmp; 12484b1667dSJiang Liu union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp); 1259b774d5cSJarkko Sakkinen struct tpm_chip *chip = to_tpm_chip(dev); 126f84fdff0SXiaoyan Zhang 127f84fdff0SXiaoyan Zhang /* 128f84fdff0SXiaoyan Zhang * the function to submit TPM operation request to pre-os environment 129f84fdff0SXiaoyan Zhang * is updated with function index from SUBREQ to SUBREQ2 since PPI 130f84fdff0SXiaoyan Zhang * version 1.1 131f84fdff0SXiaoyan Zhang */ 13294116f81SAndy Shevchenko if (acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid, 13309fe1b42SStefan Berger TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_SUBREQ2)) 13484b1667dSJiang Liu func = TPM_PPI_FN_SUBREQ2; 13584b1667dSJiang Liu 136f84fdff0SXiaoyan Zhang /* 137f84fdff0SXiaoyan Zhang * PPI spec defines params[3].type as ACPI_TYPE_PACKAGE. Some BIOS 138f84fdff0SXiaoyan Zhang * accept buffer/string/integer type, but some BIOS accept buffer/ 139f84fdff0SXiaoyan Zhang * string/package type. For PPI version 1.0 and 1.1, use buffer type 140f84fdff0SXiaoyan Zhang * for compatibility, and use package type since 1.2 according to spec. 141f84fdff0SXiaoyan Zhang */ 1420dc55365SJarkko Sakkinen if (strcmp(chip->ppi_version, "1.2") < 0) { 14384b1667dSJiang Liu if (sscanf(buf, "%d", &req) != 1) 14484b1667dSJiang Liu return -EINVAL; 14584b1667dSJiang Liu argv4.type = ACPI_TYPE_BUFFER; 14684b1667dSJiang Liu argv4.buffer.length = sizeof(req); 14784b1667dSJiang Liu argv4.buffer.pointer = (u8 *)&req; 148f84fdff0SXiaoyan Zhang } else { 14984b1667dSJiang Liu tmp.type = ACPI_TYPE_INTEGER; 15084b1667dSJiang Liu if (sscanf(buf, "%llu", &tmp.integer.value) != 1) 15184b1667dSJiang Liu return -EINVAL; 152f84fdff0SXiaoyan Zhang } 153f84fdff0SXiaoyan Zhang 1540dc55365SJarkko Sakkinen obj = tpm_eval_dsm(chip->acpi_dev_handle, func, ACPI_TYPE_INTEGER, 15509fe1b42SStefan Berger &argv4, TPM_PPI_REVISION_ID_1); 15684b1667dSJiang Liu if (!obj) { 15784b1667dSJiang Liu return -ENXIO; 15884b1667dSJiang Liu } else { 15984b1667dSJiang Liu ret = obj->integer.value; 16084b1667dSJiang Liu ACPI_FREE(obj); 16184b1667dSJiang Liu } 16284b1667dSJiang Liu 163f84fdff0SXiaoyan Zhang if (ret == 0) 16484b1667dSJiang Liu return (acpi_status)count; 16584b1667dSJiang Liu 16684b1667dSJiang Liu return (ret == 1) ? -EPERM : -EFAULT; 167f84fdff0SXiaoyan Zhang } 168f84fdff0SXiaoyan Zhang 16981198078SXiaoyan Zhang static ssize_t tpm_show_ppi_transition_action(struct device *dev, 170f84fdff0SXiaoyan Zhang struct device_attribute *attr, 171f84fdff0SXiaoyan Zhang char *buf) 172f84fdff0SXiaoyan Zhang { 173f84fdff0SXiaoyan Zhang u32 ret; 17484b1667dSJiang Liu acpi_status status; 17584b1667dSJiang Liu union acpi_object *obj = NULL; 17684b1667dSJiang Liu union acpi_object tmp = { 17784b1667dSJiang Liu .buffer.type = ACPI_TYPE_BUFFER, 17884b1667dSJiang Liu .buffer.length = 0, 17984b1667dSJiang Liu .buffer.pointer = NULL 18084b1667dSJiang Liu }; 1819b774d5cSJarkko Sakkinen struct tpm_chip *chip = to_tpm_chip(dev); 18284b1667dSJiang Liu 18384b1667dSJiang Liu static char *info[] = { 184f84fdff0SXiaoyan Zhang "None", 185f84fdff0SXiaoyan Zhang "Shutdown", 186f84fdff0SXiaoyan Zhang "Reboot", 187f84fdff0SXiaoyan Zhang "OS Vendor-specific", 188f84fdff0SXiaoyan Zhang "Error", 189f84fdff0SXiaoyan Zhang }; 190f84fdff0SXiaoyan Zhang 191f84fdff0SXiaoyan Zhang /* 192f84fdff0SXiaoyan Zhang * PPI spec defines params[3].type as empty package, but some platforms 193f84fdff0SXiaoyan Zhang * (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for 194f84fdff0SXiaoyan Zhang * compatibility, define params[3].type as buffer, if PPI version < 1.2 195f84fdff0SXiaoyan Zhang */ 1960dc55365SJarkko Sakkinen if (strcmp(chip->ppi_version, "1.2") < 0) 19784b1667dSJiang Liu obj = &tmp; 1980dc55365SJarkko Sakkinen obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETACT, 19909fe1b42SStefan Berger ACPI_TYPE_INTEGER, obj, TPM_PPI_REVISION_ID_1); 20084b1667dSJiang Liu if (!obj) { 20184b1667dSJiang Liu return -ENXIO; 20284b1667dSJiang Liu } else { 20384b1667dSJiang Liu ret = obj->integer.value; 20484b1667dSJiang Liu ACPI_FREE(obj); 205f84fdff0SXiaoyan Zhang } 20684b1667dSJiang Liu 207f84fdff0SXiaoyan Zhang if (ret < ARRAY_SIZE(info) - 1) 208f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]); 209f84fdff0SXiaoyan Zhang else 210f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, 211f84fdff0SXiaoyan Zhang info[ARRAY_SIZE(info)-1]); 212f84fdff0SXiaoyan Zhang return status; 213f84fdff0SXiaoyan Zhang } 214f84fdff0SXiaoyan Zhang 21581198078SXiaoyan Zhang static ssize_t tpm_show_ppi_response(struct device *dev, 216f84fdff0SXiaoyan Zhang struct device_attribute *attr, 217f84fdff0SXiaoyan Zhang char *buf) 218f84fdff0SXiaoyan Zhang { 21984b1667dSJiang Liu acpi_status status = -EINVAL; 22084b1667dSJiang Liu union acpi_object *obj, *ret_obj; 22184b1667dSJiang Liu u64 req, res; 2229b774d5cSJarkko Sakkinen struct tpm_chip *chip = to_tpm_chip(dev); 223f84fdff0SXiaoyan Zhang 2240dc55365SJarkko Sakkinen obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETRSP, 22509fe1b42SStefan Berger ACPI_TYPE_PACKAGE, NULL, TPM_PPI_REVISION_ID_1); 22684b1667dSJiang Liu if (!obj) 227f84fdff0SXiaoyan Zhang return -ENXIO; 228f84fdff0SXiaoyan Zhang 229f84fdff0SXiaoyan Zhang /* 230f84fdff0SXiaoyan Zhang * parameter output.pointer should be of package type, including 231f84fdff0SXiaoyan Zhang * 3 integers. The first means function return code, the second means 232f84fdff0SXiaoyan Zhang * most recent TPM operation request, and the last means response to 233f84fdff0SXiaoyan Zhang * the most recent TPM operation request. Only if the first is 0, and 234f84fdff0SXiaoyan Zhang * the second integer is not 0, the response makes sense. 235f84fdff0SXiaoyan Zhang */ 23684b1667dSJiang Liu ret_obj = obj->package.elements; 23784b1667dSJiang Liu if (obj->package.count < 3 || 23884b1667dSJiang Liu ret_obj[0].type != ACPI_TYPE_INTEGER || 23984b1667dSJiang Liu ret_obj[1].type != ACPI_TYPE_INTEGER || 24084b1667dSJiang Liu ret_obj[2].type != ACPI_TYPE_INTEGER) 241f84fdff0SXiaoyan Zhang goto cleanup; 24284b1667dSJiang Liu 24384b1667dSJiang Liu if (ret_obj[0].integer.value) { 244f84fdff0SXiaoyan Zhang status = -EFAULT; 245f84fdff0SXiaoyan Zhang goto cleanup; 246f84fdff0SXiaoyan Zhang } 24784b1667dSJiang Liu 24884b1667dSJiang Liu req = ret_obj[1].integer.value; 24984b1667dSJiang Liu res = ret_obj[2].integer.value; 25084b1667dSJiang Liu if (req) { 25184b1667dSJiang Liu if (res == 0) 252f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, 253f84fdff0SXiaoyan Zhang "0: Success"); 25484b1667dSJiang Liu else if (res == 0xFFFFFFF0) 255f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, 256f84fdff0SXiaoyan Zhang "0xFFFFFFF0: User Abort"); 25784b1667dSJiang Liu else if (res == 0xFFFFFFF1) 258f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, 259f84fdff0SXiaoyan Zhang "0xFFFFFFF1: BIOS Failure"); 26084b1667dSJiang Liu else if (res >= 1 && res <= 0x00000FFF) 261f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", 26284b1667dSJiang Liu req, res, "Corresponding TPM error"); 263f84fdff0SXiaoyan Zhang else 264f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", 26584b1667dSJiang Liu req, res, "Error"); 266f84fdff0SXiaoyan Zhang } else { 267f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n", 26884b1667dSJiang Liu req, "No Recent Request"); 269f84fdff0SXiaoyan Zhang } 27084b1667dSJiang Liu 271f84fdff0SXiaoyan Zhang cleanup: 27284b1667dSJiang Liu ACPI_FREE(obj); 273f84fdff0SXiaoyan Zhang return status; 274f84fdff0SXiaoyan Zhang } 275f84fdff0SXiaoyan Zhang 2760dc55365SJarkko Sakkinen static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start, 2770dc55365SJarkko Sakkinen u32 end) 278f84fdff0SXiaoyan Zhang { 279f84fdff0SXiaoyan Zhang int i; 280f84fdff0SXiaoyan Zhang u32 ret; 28184b1667dSJiang Liu char *str = buf; 28284b1667dSJiang Liu union acpi_object *obj, tmp; 28384b1667dSJiang Liu union acpi_object argv = ACPI_INIT_DSM_ARGV4(1, &tmp); 28484b1667dSJiang Liu 28584b1667dSJiang Liu static char *info[] = { 286f84fdff0SXiaoyan Zhang "Not implemented", 287f84fdff0SXiaoyan Zhang "BIOS only", 288f84fdff0SXiaoyan Zhang "Blocked for OS by BIOS", 289f84fdff0SXiaoyan Zhang "User required", 290f84fdff0SXiaoyan Zhang "User not required", 291f84fdff0SXiaoyan Zhang }; 292f84fdff0SXiaoyan Zhang 29309fe1b42SStefan Berger if (!acpi_check_dsm(dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID_1, 2941569a4c4SJiang Liu 1 << TPM_PPI_FN_GETOPR)) 295f84fdff0SXiaoyan Zhang return -EPERM; 296f84fdff0SXiaoyan Zhang 29784b1667dSJiang Liu tmp.integer.type = ACPI_TYPE_INTEGER; 298f84fdff0SXiaoyan Zhang for (i = start; i <= end; i++) { 29984b1667dSJiang Liu tmp.integer.value = i; 3000dc55365SJarkko Sakkinen obj = tpm_eval_dsm(dev_handle, TPM_PPI_FN_GETOPR, 301587bad77SStefan Berger ACPI_TYPE_INTEGER, &argv, 30209fe1b42SStefan Berger TPM_PPI_REVISION_ID_1); 30384b1667dSJiang Liu if (!obj) { 304f84fdff0SXiaoyan Zhang return -ENOMEM; 30584b1667dSJiang Liu } else { 30684b1667dSJiang Liu ret = obj->integer.value; 30784b1667dSJiang Liu ACPI_FREE(obj); 30884b1667dSJiang Liu } 309f84fdff0SXiaoyan Zhang 310f84fdff0SXiaoyan Zhang if (ret > 0 && ret < ARRAY_SIZE(info)) 311f84fdff0SXiaoyan Zhang str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n", 312f84fdff0SXiaoyan Zhang i, ret, info[ret]); 313f84fdff0SXiaoyan Zhang } 31484b1667dSJiang Liu 315f84fdff0SXiaoyan Zhang return str - buf; 316f84fdff0SXiaoyan Zhang } 317f84fdff0SXiaoyan Zhang 31881198078SXiaoyan Zhang static ssize_t tpm_show_ppi_tcg_operations(struct device *dev, 31981198078SXiaoyan Zhang struct device_attribute *attr, 32081198078SXiaoyan Zhang char *buf) 321f84fdff0SXiaoyan Zhang { 3229b774d5cSJarkko Sakkinen struct tpm_chip *chip = to_tpm_chip(dev); 3230dc55365SJarkko Sakkinen 3240dc55365SJarkko Sakkinen return show_ppi_operations(chip->acpi_dev_handle, buf, 0, 3250dc55365SJarkko Sakkinen PPI_TPM_REQ_MAX); 326f84fdff0SXiaoyan Zhang } 327f84fdff0SXiaoyan Zhang 32881198078SXiaoyan Zhang static ssize_t tpm_show_ppi_vs_operations(struct device *dev, 32981198078SXiaoyan Zhang struct device_attribute *attr, 33081198078SXiaoyan Zhang char *buf) 331f84fdff0SXiaoyan Zhang { 3329b774d5cSJarkko Sakkinen struct tpm_chip *chip = to_tpm_chip(dev); 3330dc55365SJarkko Sakkinen 3340dc55365SJarkko Sakkinen return show_ppi_operations(chip->acpi_dev_handle, buf, PPI_VS_REQ_START, 3350dc55365SJarkko Sakkinen PPI_VS_REQ_END); 336f84fdff0SXiaoyan Zhang } 337f84fdff0SXiaoyan Zhang 338f84fdff0SXiaoyan Zhang static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL); 339f84fdff0SXiaoyan Zhang static DEVICE_ATTR(request, S_IRUGO | S_IWUSR | S_IWGRP, 340f84fdff0SXiaoyan Zhang tpm_show_ppi_request, tpm_store_ppi_request); 341f84fdff0SXiaoyan Zhang static DEVICE_ATTR(transition_action, S_IRUGO, 342f84fdff0SXiaoyan Zhang tpm_show_ppi_transition_action, NULL); 343f84fdff0SXiaoyan Zhang static DEVICE_ATTR(response, S_IRUGO, tpm_show_ppi_response, NULL); 344f84fdff0SXiaoyan Zhang static DEVICE_ATTR(tcg_operations, S_IRUGO, tpm_show_ppi_tcg_operations, NULL); 345f84fdff0SXiaoyan Zhang static DEVICE_ATTR(vs_operations, S_IRUGO, tpm_show_ppi_vs_operations, NULL); 346f84fdff0SXiaoyan Zhang 347f84fdff0SXiaoyan Zhang static struct attribute *ppi_attrs[] = { 348f84fdff0SXiaoyan Zhang &dev_attr_version.attr, 349f84fdff0SXiaoyan Zhang &dev_attr_request.attr, 350f84fdff0SXiaoyan Zhang &dev_attr_transition_action.attr, 351f84fdff0SXiaoyan Zhang &dev_attr_response.attr, 352f84fdff0SXiaoyan Zhang &dev_attr_tcg_operations.attr, 353f84fdff0SXiaoyan Zhang &dev_attr_vs_operations.attr, NULL, 354f84fdff0SXiaoyan Zhang }; 355f84fdff0SXiaoyan Zhang static struct attribute_group ppi_attr_grp = { 3561631cfb7SGang Wei .name = "ppi", 357f84fdff0SXiaoyan Zhang .attrs = ppi_attrs 358f84fdff0SXiaoyan Zhang }; 359f84fdff0SXiaoyan Zhang 3609b774d5cSJarkko Sakkinen void tpm_add_ppi(struct tpm_chip *chip) 361f84fdff0SXiaoyan Zhang { 3620dc55365SJarkko Sakkinen union acpi_object *obj; 3630dc55365SJarkko Sakkinen 3640dc55365SJarkko Sakkinen if (!chip->acpi_dev_handle) 3659b774d5cSJarkko Sakkinen return; 3660dc55365SJarkko Sakkinen 36794116f81SAndy Shevchenko if (!acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid, 36809fe1b42SStefan Berger TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_VERSION)) 3699b774d5cSJarkko Sakkinen return; 3700dc55365SJarkko Sakkinen 3710dc55365SJarkko Sakkinen /* Cache PPI version string. */ 37294116f81SAndy Shevchenko obj = acpi_evaluate_dsm_typed(chip->acpi_dev_handle, &tpm_ppi_guid, 37309fe1b42SStefan Berger TPM_PPI_REVISION_ID_1, 37409fe1b42SStefan Berger TPM_PPI_FN_VERSION, 3750dc55365SJarkko Sakkinen NULL, ACPI_TYPE_STRING); 3760dc55365SJarkko Sakkinen if (obj) { 3770dc55365SJarkko Sakkinen strlcpy(chip->ppi_version, obj->string.pointer, 3780dc55365SJarkko Sakkinen sizeof(chip->ppi_version)); 3790dc55365SJarkko Sakkinen ACPI_FREE(obj); 380f84fdff0SXiaoyan Zhang } 3811631cfb7SGang Wei 3829b774d5cSJarkko Sakkinen chip->groups[chip->groups_cnt++] = &ppi_attr_grp; 3831631cfb7SGang Wei } 384