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