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