1*b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 20dc55365SJarkko Sakkinen /* 30dc55365SJarkko Sakkinen * Copyright (C) 2012-2014 Intel Corporation 40dc55365SJarkko Sakkinen * 50dc55365SJarkko Sakkinen * Authors: 60dc55365SJarkko Sakkinen * Xiaoyan Zhang <xiaoyan.zhang@intel.com> 70dc55365SJarkko Sakkinen * Jiang Liu <jiang.liu@linux.intel.com> 80dc55365SJarkko Sakkinen * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> 90dc55365SJarkko Sakkinen * 100dc55365SJarkko Sakkinen * Maintained by: <tpmdd-devel@lists.sourceforge.net> 110dc55365SJarkko Sakkinen * 120dc55365SJarkko Sakkinen * This file contains implementation of the sysfs interface for PPI. 130dc55365SJarkko Sakkinen */ 140dc55365SJarkko Sakkinen 150dc55365SJarkko Sakkinen 16f84fdff0SXiaoyan Zhang #include <linux/acpi.h> 17f84fdff0SXiaoyan Zhang #include "tpm.h" 18f84fdff0SXiaoyan Zhang 1909fe1b42SStefan Berger #define TPM_PPI_REVISION_ID_1 1 208b60c79bSStefan Berger #define TPM_PPI_REVISION_ID_2 2 21f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_VERSION 1 22f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_SUBREQ 2 23f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETREQ 3 24f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETACT 4 25f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETRSP 5 26f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_SUBREQ2 7 27f84fdff0SXiaoyan Zhang #define TPM_PPI_FN_GETOPR 8 289d4023edSStefan Berger #define PPI_TPM_REQ_MAX 101 /* PPI 1.3 for TPM 2 */ 29f84fdff0SXiaoyan Zhang #define PPI_VS_REQ_START 128 30f84fdff0SXiaoyan Zhang #define PPI_VS_REQ_END 255 31f84fdff0SXiaoyan Zhang 3294116f81SAndy Shevchenko static const guid_t tpm_ppi_guid = 3394116f81SAndy Shevchenko GUID_INIT(0x3DDDFAA6, 0x361B, 0x4EB4, 3494116f81SAndy Shevchenko 0xA4, 0x24, 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53); 3584b1667dSJiang Liu 368b60c79bSStefan Berger static bool tpm_ppi_req_has_parameter(u64 req) 378b60c79bSStefan Berger { 388b60c79bSStefan Berger return req == 23; 398b60c79bSStefan Berger } 408b60c79bSStefan Berger 4184b1667dSJiang Liu static inline union acpi_object * 420dc55365SJarkko Sakkinen tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type, 43587bad77SStefan Berger union acpi_object *argv4, u64 rev) 44f84fdff0SXiaoyan Zhang { 450dc55365SJarkko Sakkinen BUG_ON(!ppi_handle); 4694116f81SAndy Shevchenko return acpi_evaluate_dsm_typed(ppi_handle, &tpm_ppi_guid, 47587bad77SStefan Berger rev, func, argv4, type); 48f84fdff0SXiaoyan Zhang } 49f84fdff0SXiaoyan Zhang 5081198078SXiaoyan Zhang static ssize_t tpm_show_ppi_version(struct device *dev, 5181198078SXiaoyan Zhang struct device_attribute *attr, char *buf) 52f84fdff0SXiaoyan Zhang { 539b774d5cSJarkko Sakkinen struct tpm_chip *chip = to_tpm_chip(dev); 540dc55365SJarkko Sakkinen 550dc55365SJarkko Sakkinen return scnprintf(buf, PAGE_SIZE, "%s\n", chip->ppi_version); 56f84fdff0SXiaoyan Zhang } 57f84fdff0SXiaoyan Zhang 5881198078SXiaoyan Zhang static ssize_t tpm_show_ppi_request(struct device *dev, 5981198078SXiaoyan Zhang struct device_attribute *attr, char *buf) 60f84fdff0SXiaoyan Zhang { 6184b1667dSJiang Liu ssize_t size = -EINVAL; 6284b1667dSJiang Liu union acpi_object *obj; 639b774d5cSJarkko Sakkinen struct tpm_chip *chip = to_tpm_chip(dev); 648b60c79bSStefan Berger u64 rev = TPM_PPI_REVISION_ID_2; 658b60c79bSStefan Berger u64 req; 668b60c79bSStefan Berger 678b60c79bSStefan Berger if (strcmp(chip->ppi_version, "1.2") < 0) 688b60c79bSStefan Berger rev = TPM_PPI_REVISION_ID_1; 69f84fdff0SXiaoyan Zhang 700dc55365SJarkko Sakkinen obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETREQ, 718b60c79bSStefan Berger ACPI_TYPE_PACKAGE, NULL, rev); 7284b1667dSJiang Liu if (!obj) 73f84fdff0SXiaoyan Zhang return -ENXIO; 74f84fdff0SXiaoyan Zhang 75f84fdff0SXiaoyan Zhang /* 76f84fdff0SXiaoyan Zhang * output.pointer should be of package type, including two integers. 77f84fdff0SXiaoyan Zhang * The first is function return code, 0 means success and 1 means 78f84fdff0SXiaoyan Zhang * error. The second is pending TPM operation requested by the OS, 0 79f84fdff0SXiaoyan Zhang * means none and >0 means operation value. 80f84fdff0SXiaoyan Zhang */ 818b60c79bSStefan Berger if (obj->package.count == 3 && 828b60c79bSStefan Berger obj->package.elements[0].type == ACPI_TYPE_INTEGER && 838b60c79bSStefan Berger obj->package.elements[1].type == ACPI_TYPE_INTEGER && 848b60c79bSStefan Berger obj->package.elements[2].type == ACPI_TYPE_INTEGER) { 858b60c79bSStefan Berger if (obj->package.elements[0].integer.value) 868b60c79bSStefan Berger size = -EFAULT; 878b60c79bSStefan Berger else { 888b60c79bSStefan Berger req = obj->package.elements[1].integer.value; 898b60c79bSStefan Berger if (tpm_ppi_req_has_parameter(req)) 908b60c79bSStefan Berger size = scnprintf(buf, PAGE_SIZE, 918b60c79bSStefan Berger "%llu %llu\n", req, 928b60c79bSStefan Berger obj->package.elements[2].integer.value); 938b60c79bSStefan Berger else 948b60c79bSStefan Berger size = scnprintf(buf, PAGE_SIZE, 958b60c79bSStefan Berger "%llu\n", req); 968b60c79bSStefan Berger } 978b60c79bSStefan Berger } else if (obj->package.count == 2 && 9884b1667dSJiang Liu obj->package.elements[0].type == ACPI_TYPE_INTEGER && 9984b1667dSJiang Liu obj->package.elements[1].type == ACPI_TYPE_INTEGER) { 10084b1667dSJiang Liu if (obj->package.elements[0].integer.value) 10184b1667dSJiang Liu size = -EFAULT; 102f84fdff0SXiaoyan Zhang else 10384b1667dSJiang Liu size = scnprintf(buf, PAGE_SIZE, "%llu\n", 10484b1667dSJiang Liu obj->package.elements[1].integer.value); 105f84fdff0SXiaoyan Zhang } 10684b1667dSJiang Liu 10784b1667dSJiang Liu ACPI_FREE(obj); 10884b1667dSJiang Liu 10984b1667dSJiang Liu return size; 110f84fdff0SXiaoyan Zhang } 111f84fdff0SXiaoyan Zhang 11281198078SXiaoyan Zhang static ssize_t tpm_store_ppi_request(struct device *dev, 113f84fdff0SXiaoyan Zhang struct device_attribute *attr, 114f84fdff0SXiaoyan Zhang const char *buf, size_t count) 115f84fdff0SXiaoyan Zhang { 116f84fdff0SXiaoyan Zhang u32 req; 117f84fdff0SXiaoyan Zhang u64 ret; 11884b1667dSJiang Liu int func = TPM_PPI_FN_SUBREQ; 11950a81b60SStefan Berger union acpi_object *obj, tmp[2]; 12050a81b60SStefan Berger union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(2, tmp); 1219b774d5cSJarkko Sakkinen struct tpm_chip *chip = to_tpm_chip(dev); 12250a81b60SStefan Berger u64 rev = TPM_PPI_REVISION_ID_1; 123f84fdff0SXiaoyan Zhang 124f84fdff0SXiaoyan Zhang /* 125f84fdff0SXiaoyan Zhang * the function to submit TPM operation request to pre-os environment 126f84fdff0SXiaoyan Zhang * is updated with function index from SUBREQ to SUBREQ2 since PPI 127f84fdff0SXiaoyan Zhang * version 1.1 128f84fdff0SXiaoyan Zhang */ 12994116f81SAndy Shevchenko if (acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid, 13009fe1b42SStefan Berger TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_SUBREQ2)) 13184b1667dSJiang Liu func = TPM_PPI_FN_SUBREQ2; 13284b1667dSJiang Liu 133f84fdff0SXiaoyan Zhang /* 134f84fdff0SXiaoyan Zhang * PPI spec defines params[3].type as ACPI_TYPE_PACKAGE. Some BIOS 135f84fdff0SXiaoyan Zhang * accept buffer/string/integer type, but some BIOS accept buffer/ 136f84fdff0SXiaoyan Zhang * string/package type. For PPI version 1.0 and 1.1, use buffer type 137f84fdff0SXiaoyan Zhang * for compatibility, and use package type since 1.2 according to spec. 138f84fdff0SXiaoyan Zhang */ 13950a81b60SStefan Berger if (strcmp(chip->ppi_version, "1.3") == 0) { 14050a81b60SStefan Berger if (sscanf(buf, "%llu %llu", &tmp[0].integer.value, 14150a81b60SStefan Berger &tmp[1].integer.value) != 2) 14250a81b60SStefan Berger goto ppi12; 14350a81b60SStefan Berger rev = TPM_PPI_REVISION_ID_2; 14450a81b60SStefan Berger tmp[0].type = ACPI_TYPE_INTEGER; 14550a81b60SStefan Berger tmp[1].type = ACPI_TYPE_INTEGER; 14650a81b60SStefan Berger } else if (strcmp(chip->ppi_version, "1.2") < 0) { 14784b1667dSJiang Liu if (sscanf(buf, "%d", &req) != 1) 14884b1667dSJiang Liu return -EINVAL; 14984b1667dSJiang Liu argv4.type = ACPI_TYPE_BUFFER; 15084b1667dSJiang Liu argv4.buffer.length = sizeof(req); 15184b1667dSJiang Liu argv4.buffer.pointer = (u8 *)&req; 152f84fdff0SXiaoyan Zhang } else { 15350a81b60SStefan Berger ppi12: 15450a81b60SStefan Berger argv4.package.count = 1; 15550a81b60SStefan Berger tmp[0].type = ACPI_TYPE_INTEGER; 15650a81b60SStefan Berger if (sscanf(buf, "%llu", &tmp[0].integer.value) != 1) 15784b1667dSJiang Liu return -EINVAL; 158f84fdff0SXiaoyan Zhang } 159f84fdff0SXiaoyan Zhang 1600dc55365SJarkko Sakkinen obj = tpm_eval_dsm(chip->acpi_dev_handle, func, ACPI_TYPE_INTEGER, 16150a81b60SStefan Berger &argv4, rev); 16284b1667dSJiang Liu if (!obj) { 16384b1667dSJiang Liu return -ENXIO; 16484b1667dSJiang Liu } else { 16584b1667dSJiang Liu ret = obj->integer.value; 16684b1667dSJiang Liu ACPI_FREE(obj); 16784b1667dSJiang Liu } 16884b1667dSJiang Liu 169f84fdff0SXiaoyan Zhang if (ret == 0) 17084b1667dSJiang Liu return (acpi_status)count; 17184b1667dSJiang Liu 17284b1667dSJiang Liu return (ret == 1) ? -EPERM : -EFAULT; 173f84fdff0SXiaoyan Zhang } 174f84fdff0SXiaoyan Zhang 17581198078SXiaoyan Zhang static ssize_t tpm_show_ppi_transition_action(struct device *dev, 176f84fdff0SXiaoyan Zhang struct device_attribute *attr, 177f84fdff0SXiaoyan Zhang char *buf) 178f84fdff0SXiaoyan Zhang { 179f84fdff0SXiaoyan Zhang u32 ret; 18084b1667dSJiang Liu acpi_status status; 18184b1667dSJiang Liu union acpi_object *obj = NULL; 18284b1667dSJiang Liu union acpi_object tmp = { 18384b1667dSJiang Liu .buffer.type = ACPI_TYPE_BUFFER, 18484b1667dSJiang Liu .buffer.length = 0, 18584b1667dSJiang Liu .buffer.pointer = NULL 18684b1667dSJiang Liu }; 1879b774d5cSJarkko Sakkinen struct tpm_chip *chip = to_tpm_chip(dev); 18884b1667dSJiang Liu 18984b1667dSJiang Liu static char *info[] = { 190f84fdff0SXiaoyan Zhang "None", 191f84fdff0SXiaoyan Zhang "Shutdown", 192f84fdff0SXiaoyan Zhang "Reboot", 193f84fdff0SXiaoyan Zhang "OS Vendor-specific", 194f84fdff0SXiaoyan Zhang "Error", 195f84fdff0SXiaoyan Zhang }; 196f84fdff0SXiaoyan Zhang 197f84fdff0SXiaoyan Zhang /* 198f84fdff0SXiaoyan Zhang * PPI spec defines params[3].type as empty package, but some platforms 199f84fdff0SXiaoyan Zhang * (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for 200f84fdff0SXiaoyan Zhang * compatibility, define params[3].type as buffer, if PPI version < 1.2 201f84fdff0SXiaoyan Zhang */ 2020dc55365SJarkko Sakkinen if (strcmp(chip->ppi_version, "1.2") < 0) 20384b1667dSJiang Liu obj = &tmp; 2040dc55365SJarkko Sakkinen obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETACT, 20509fe1b42SStefan Berger ACPI_TYPE_INTEGER, obj, TPM_PPI_REVISION_ID_1); 20684b1667dSJiang Liu if (!obj) { 20784b1667dSJiang Liu return -ENXIO; 20884b1667dSJiang Liu } else { 20984b1667dSJiang Liu ret = obj->integer.value; 21084b1667dSJiang Liu ACPI_FREE(obj); 211f84fdff0SXiaoyan Zhang } 21284b1667dSJiang Liu 213f84fdff0SXiaoyan Zhang if (ret < ARRAY_SIZE(info) - 1) 214f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]); 215f84fdff0SXiaoyan Zhang else 216f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, 217f84fdff0SXiaoyan Zhang info[ARRAY_SIZE(info)-1]); 218f84fdff0SXiaoyan Zhang return status; 219f84fdff0SXiaoyan Zhang } 220f84fdff0SXiaoyan Zhang 22181198078SXiaoyan Zhang static ssize_t tpm_show_ppi_response(struct device *dev, 222f84fdff0SXiaoyan Zhang struct device_attribute *attr, 223f84fdff0SXiaoyan Zhang char *buf) 224f84fdff0SXiaoyan Zhang { 22584b1667dSJiang Liu acpi_status status = -EINVAL; 22684b1667dSJiang Liu union acpi_object *obj, *ret_obj; 22784b1667dSJiang Liu u64 req, res; 2289b774d5cSJarkko Sakkinen struct tpm_chip *chip = to_tpm_chip(dev); 229f84fdff0SXiaoyan Zhang 2300dc55365SJarkko Sakkinen obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETRSP, 23109fe1b42SStefan Berger ACPI_TYPE_PACKAGE, NULL, TPM_PPI_REVISION_ID_1); 23284b1667dSJiang Liu if (!obj) 233f84fdff0SXiaoyan Zhang return -ENXIO; 234f84fdff0SXiaoyan Zhang 235f84fdff0SXiaoyan Zhang /* 236f84fdff0SXiaoyan Zhang * parameter output.pointer should be of package type, including 237f84fdff0SXiaoyan Zhang * 3 integers. The first means function return code, the second means 238f84fdff0SXiaoyan Zhang * most recent TPM operation request, and the last means response to 239f84fdff0SXiaoyan Zhang * the most recent TPM operation request. Only if the first is 0, and 240f84fdff0SXiaoyan Zhang * the second integer is not 0, the response makes sense. 241f84fdff0SXiaoyan Zhang */ 24284b1667dSJiang Liu ret_obj = obj->package.elements; 24384b1667dSJiang Liu if (obj->package.count < 3 || 24484b1667dSJiang Liu ret_obj[0].type != ACPI_TYPE_INTEGER || 24584b1667dSJiang Liu ret_obj[1].type != ACPI_TYPE_INTEGER || 24684b1667dSJiang Liu ret_obj[2].type != ACPI_TYPE_INTEGER) 247f84fdff0SXiaoyan Zhang goto cleanup; 24884b1667dSJiang Liu 24984b1667dSJiang Liu if (ret_obj[0].integer.value) { 250f84fdff0SXiaoyan Zhang status = -EFAULT; 251f84fdff0SXiaoyan Zhang goto cleanup; 252f84fdff0SXiaoyan Zhang } 25384b1667dSJiang Liu 25484b1667dSJiang Liu req = ret_obj[1].integer.value; 25584b1667dSJiang Liu res = ret_obj[2].integer.value; 25684b1667dSJiang Liu if (req) { 25784b1667dSJiang Liu if (res == 0) 258f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, 259f84fdff0SXiaoyan Zhang "0: Success"); 26084b1667dSJiang Liu else if (res == 0xFFFFFFF0) 261f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, 262f84fdff0SXiaoyan Zhang "0xFFFFFFF0: User Abort"); 26384b1667dSJiang Liu else if (res == 0xFFFFFFF1) 264f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, 265f84fdff0SXiaoyan Zhang "0xFFFFFFF1: BIOS Failure"); 26684b1667dSJiang Liu else if (res >= 1 && res <= 0x00000FFF) 267f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", 26884b1667dSJiang Liu req, res, "Corresponding TPM error"); 269f84fdff0SXiaoyan Zhang else 270f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", 27184b1667dSJiang Liu req, res, "Error"); 272f84fdff0SXiaoyan Zhang } else { 273f84fdff0SXiaoyan Zhang status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n", 27484b1667dSJiang Liu req, "No Recent Request"); 275f84fdff0SXiaoyan Zhang } 27684b1667dSJiang Liu 277f84fdff0SXiaoyan Zhang cleanup: 27884b1667dSJiang Liu ACPI_FREE(obj); 279f84fdff0SXiaoyan Zhang return status; 280f84fdff0SXiaoyan Zhang } 281f84fdff0SXiaoyan Zhang 2820dc55365SJarkko Sakkinen static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start, 2830dc55365SJarkko Sakkinen u32 end) 284f84fdff0SXiaoyan Zhang { 285f84fdff0SXiaoyan Zhang int i; 286f84fdff0SXiaoyan Zhang u32 ret; 28784b1667dSJiang Liu char *str = buf; 28884b1667dSJiang Liu union acpi_object *obj, tmp; 28984b1667dSJiang Liu union acpi_object argv = ACPI_INIT_DSM_ARGV4(1, &tmp); 29084b1667dSJiang Liu 29184b1667dSJiang Liu static char *info[] = { 292f84fdff0SXiaoyan Zhang "Not implemented", 293f84fdff0SXiaoyan Zhang "BIOS only", 294f84fdff0SXiaoyan Zhang "Blocked for OS by BIOS", 295f84fdff0SXiaoyan Zhang "User required", 296f84fdff0SXiaoyan Zhang "User not required", 297f84fdff0SXiaoyan Zhang }; 298f84fdff0SXiaoyan Zhang 29909fe1b42SStefan Berger if (!acpi_check_dsm(dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID_1, 3001569a4c4SJiang Liu 1 << TPM_PPI_FN_GETOPR)) 301f84fdff0SXiaoyan Zhang return -EPERM; 302f84fdff0SXiaoyan Zhang 30384b1667dSJiang Liu tmp.integer.type = ACPI_TYPE_INTEGER; 304f84fdff0SXiaoyan Zhang for (i = start; i <= end; i++) { 30584b1667dSJiang Liu tmp.integer.value = i; 3060dc55365SJarkko Sakkinen obj = tpm_eval_dsm(dev_handle, TPM_PPI_FN_GETOPR, 307587bad77SStefan Berger ACPI_TYPE_INTEGER, &argv, 30809fe1b42SStefan Berger TPM_PPI_REVISION_ID_1); 30984b1667dSJiang Liu if (!obj) { 310f84fdff0SXiaoyan Zhang return -ENOMEM; 31184b1667dSJiang Liu } else { 31284b1667dSJiang Liu ret = obj->integer.value; 31384b1667dSJiang Liu ACPI_FREE(obj); 31484b1667dSJiang Liu } 315f84fdff0SXiaoyan Zhang 316f84fdff0SXiaoyan Zhang if (ret > 0 && ret < ARRAY_SIZE(info)) 317f84fdff0SXiaoyan Zhang str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n", 318f84fdff0SXiaoyan Zhang i, ret, info[ret]); 319f84fdff0SXiaoyan Zhang } 32084b1667dSJiang Liu 321f84fdff0SXiaoyan Zhang return str - buf; 322f84fdff0SXiaoyan Zhang } 323f84fdff0SXiaoyan Zhang 32481198078SXiaoyan Zhang static ssize_t tpm_show_ppi_tcg_operations(struct device *dev, 32581198078SXiaoyan Zhang struct device_attribute *attr, 32681198078SXiaoyan Zhang char *buf) 327f84fdff0SXiaoyan Zhang { 3289b774d5cSJarkko Sakkinen struct tpm_chip *chip = to_tpm_chip(dev); 3290dc55365SJarkko Sakkinen 3300dc55365SJarkko Sakkinen return show_ppi_operations(chip->acpi_dev_handle, buf, 0, 3310dc55365SJarkko Sakkinen PPI_TPM_REQ_MAX); 332f84fdff0SXiaoyan Zhang } 333f84fdff0SXiaoyan Zhang 33481198078SXiaoyan Zhang static ssize_t tpm_show_ppi_vs_operations(struct device *dev, 33581198078SXiaoyan Zhang struct device_attribute *attr, 33681198078SXiaoyan Zhang char *buf) 337f84fdff0SXiaoyan Zhang { 3389b774d5cSJarkko Sakkinen struct tpm_chip *chip = to_tpm_chip(dev); 3390dc55365SJarkko Sakkinen 3400dc55365SJarkko Sakkinen return show_ppi_operations(chip->acpi_dev_handle, buf, PPI_VS_REQ_START, 3410dc55365SJarkko Sakkinen PPI_VS_REQ_END); 342f84fdff0SXiaoyan Zhang } 343f84fdff0SXiaoyan Zhang 344f84fdff0SXiaoyan Zhang static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL); 345f84fdff0SXiaoyan Zhang static DEVICE_ATTR(request, S_IRUGO | S_IWUSR | S_IWGRP, 346f84fdff0SXiaoyan Zhang tpm_show_ppi_request, tpm_store_ppi_request); 347f84fdff0SXiaoyan Zhang static DEVICE_ATTR(transition_action, S_IRUGO, 348f84fdff0SXiaoyan Zhang tpm_show_ppi_transition_action, NULL); 349f84fdff0SXiaoyan Zhang static DEVICE_ATTR(response, S_IRUGO, tpm_show_ppi_response, NULL); 350f84fdff0SXiaoyan Zhang static DEVICE_ATTR(tcg_operations, S_IRUGO, tpm_show_ppi_tcg_operations, NULL); 351f84fdff0SXiaoyan Zhang static DEVICE_ATTR(vs_operations, S_IRUGO, tpm_show_ppi_vs_operations, NULL); 352f84fdff0SXiaoyan Zhang 353f84fdff0SXiaoyan Zhang static struct attribute *ppi_attrs[] = { 354f84fdff0SXiaoyan Zhang &dev_attr_version.attr, 355f84fdff0SXiaoyan Zhang &dev_attr_request.attr, 356f84fdff0SXiaoyan Zhang &dev_attr_transition_action.attr, 357f84fdff0SXiaoyan Zhang &dev_attr_response.attr, 358f84fdff0SXiaoyan Zhang &dev_attr_tcg_operations.attr, 359f84fdff0SXiaoyan Zhang &dev_attr_vs_operations.attr, NULL, 360f84fdff0SXiaoyan Zhang }; 361f84fdff0SXiaoyan Zhang static struct attribute_group ppi_attr_grp = { 3621631cfb7SGang Wei .name = "ppi", 363f84fdff0SXiaoyan Zhang .attrs = ppi_attrs 364f84fdff0SXiaoyan Zhang }; 365f84fdff0SXiaoyan Zhang 3669b774d5cSJarkko Sakkinen void tpm_add_ppi(struct tpm_chip *chip) 367f84fdff0SXiaoyan Zhang { 3680dc55365SJarkko Sakkinen union acpi_object *obj; 3690dc55365SJarkko Sakkinen 3700dc55365SJarkko Sakkinen if (!chip->acpi_dev_handle) 3719b774d5cSJarkko Sakkinen return; 3720dc55365SJarkko Sakkinen 37394116f81SAndy Shevchenko if (!acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid, 37409fe1b42SStefan Berger TPM_PPI_REVISION_ID_1, 1 << TPM_PPI_FN_VERSION)) 3759b774d5cSJarkko Sakkinen return; 3760dc55365SJarkko Sakkinen 3770dc55365SJarkko Sakkinen /* Cache PPI version string. */ 37894116f81SAndy Shevchenko obj = acpi_evaluate_dsm_typed(chip->acpi_dev_handle, &tpm_ppi_guid, 37909fe1b42SStefan Berger TPM_PPI_REVISION_ID_1, 38009fe1b42SStefan Berger TPM_PPI_FN_VERSION, 3810dc55365SJarkko Sakkinen NULL, ACPI_TYPE_STRING); 3820dc55365SJarkko Sakkinen if (obj) { 3830dc55365SJarkko Sakkinen strlcpy(chip->ppi_version, obj->string.pointer, 3840dc55365SJarkko Sakkinen sizeof(chip->ppi_version)); 3850dc55365SJarkko Sakkinen ACPI_FREE(obj); 386f84fdff0SXiaoyan Zhang } 3871631cfb7SGang Wei 3889b774d5cSJarkko Sakkinen chip->groups[chip->groups_cnt++] = &ppi_attr_grp; 3891631cfb7SGang Wei } 390