linux/drivers/char/tpm/tpm_ppi.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2012-2014 Intel Corporation
   3 *
   4 * Authors:
   5 * Xiaoyan Zhang <xiaoyan.zhang@intel.com>
   6 * Jiang Liu <jiang.liu@linux.intel.com>
   7 * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
   8 *
   9 * Maintained by: <tpmdd-devel@lists.sourceforge.net>
  10 *
  11 * This file contains implementation of the sysfs interface for PPI.
  12 *
  13 * This program is free software; you can redistribute it and/or
  14 * modify it under the terms of the GNU General Public License
  15 * as published by the Free Software Foundation; version 2
  16 * of the License.
  17 */
  18
  19
  20#include <linux/acpi.h>
  21#include "tpm.h"
  22
  23#define TPM_PPI_REVISION_ID     1
  24#define TPM_PPI_FN_VERSION      1
  25#define TPM_PPI_FN_SUBREQ       2
  26#define TPM_PPI_FN_GETREQ       3
  27#define TPM_PPI_FN_GETACT       4
  28#define TPM_PPI_FN_GETRSP       5
  29#define TPM_PPI_FN_SUBREQ2      7
  30#define TPM_PPI_FN_GETOPR       8
  31#define PPI_TPM_REQ_MAX         22
  32#define PPI_VS_REQ_START        128
  33#define PPI_VS_REQ_END          255
  34
  35static const guid_t tpm_ppi_guid =
  36        GUID_INIT(0x3DDDFAA6, 0x361B, 0x4EB4,
  37                  0xA4, 0x24, 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53);
  38
  39static inline union acpi_object *
  40tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type,
  41             union acpi_object *argv4)
  42{
  43        BUG_ON(!ppi_handle);
  44        return acpi_evaluate_dsm_typed(ppi_handle, &tpm_ppi_guid,
  45                                       TPM_PPI_REVISION_ID,
  46                                       func, argv4, type);
  47}
  48
  49static ssize_t tpm_show_ppi_version(struct device *dev,
  50                                    struct device_attribute *attr, char *buf)
  51{
  52        struct tpm_chip *chip = to_tpm_chip(dev);
  53
  54        return scnprintf(buf, PAGE_SIZE, "%s\n", chip->ppi_version);
  55}
  56
  57static ssize_t tpm_show_ppi_request(struct device *dev,
  58                                    struct device_attribute *attr, char *buf)
  59{
  60        ssize_t size = -EINVAL;
  61        union acpi_object *obj;
  62        struct tpm_chip *chip = to_tpm_chip(dev);
  63
  64        obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETREQ,
  65                           ACPI_TYPE_PACKAGE, NULL);
  66        if (!obj)
  67                return -ENXIO;
  68
  69        /*
  70         * output.pointer should be of package type, including two integers.
  71         * The first is function return code, 0 means success and 1 means
  72         * error. The second is pending TPM operation requested by the OS, 0
  73         * means none and >0 means operation value.
  74         */
  75        if (obj->package.count == 2 &&
  76            obj->package.elements[0].type == ACPI_TYPE_INTEGER &&
  77            obj->package.elements[1].type == ACPI_TYPE_INTEGER) {
  78                if (obj->package.elements[0].integer.value)
  79                        size = -EFAULT;
  80                else
  81                        size = scnprintf(buf, PAGE_SIZE, "%llu\n",
  82                                 obj->package.elements[1].integer.value);
  83        }
  84
  85        ACPI_FREE(obj);
  86
  87        return size;
  88}
  89
  90static ssize_t tpm_store_ppi_request(struct device *dev,
  91                                     struct device_attribute *attr,
  92                                     const char *buf, size_t count)
  93{
  94        u32 req;
  95        u64 ret;
  96        int func = TPM_PPI_FN_SUBREQ;
  97        union acpi_object *obj, tmp;
  98        union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp);
  99        struct tpm_chip *chip = to_tpm_chip(dev);
 100
 101        /*
 102         * the function to submit TPM operation request to pre-os environment
 103         * is updated with function index from SUBREQ to SUBREQ2 since PPI
 104         * version 1.1
 105         */
 106        if (acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid,
 107                           TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_SUBREQ2))
 108                func = TPM_PPI_FN_SUBREQ2;
 109
 110        /*
 111         * PPI spec defines params[3].type as ACPI_TYPE_PACKAGE. Some BIOS
 112         * accept buffer/string/integer type, but some BIOS accept buffer/
 113         * string/package type. For PPI version 1.0 and 1.1, use buffer type
 114         * for compatibility, and use package type since 1.2 according to spec.
 115         */
 116        if (strcmp(chip->ppi_version, "1.2") < 0) {
 117                if (sscanf(buf, "%d", &req) != 1)
 118                        return -EINVAL;
 119                argv4.type = ACPI_TYPE_BUFFER;
 120                argv4.buffer.length = sizeof(req);
 121                argv4.buffer.pointer = (u8 *)&req;
 122        } else {
 123                tmp.type = ACPI_TYPE_INTEGER;
 124                if (sscanf(buf, "%llu", &tmp.integer.value) != 1)
 125                        return -EINVAL;
 126        }
 127
 128        obj = tpm_eval_dsm(chip->acpi_dev_handle, func, ACPI_TYPE_INTEGER,
 129                           &argv4);
 130        if (!obj) {
 131                return -ENXIO;
 132        } else {
 133                ret = obj->integer.value;
 134                ACPI_FREE(obj);
 135        }
 136
 137        if (ret == 0)
 138                return (acpi_status)count;
 139
 140        return (ret == 1) ? -EPERM : -EFAULT;
 141}
 142
 143static ssize_t tpm_show_ppi_transition_action(struct device *dev,
 144                                              struct device_attribute *attr,
 145                                              char *buf)
 146{
 147        u32 ret;
 148        acpi_status status;
 149        union acpi_object *obj = NULL;
 150        union acpi_object tmp = {
 151                .buffer.type = ACPI_TYPE_BUFFER,
 152                .buffer.length = 0,
 153                .buffer.pointer = NULL
 154        };
 155        struct tpm_chip *chip = to_tpm_chip(dev);
 156
 157        static char *info[] = {
 158                "None",
 159                "Shutdown",
 160                "Reboot",
 161                "OS Vendor-specific",
 162                "Error",
 163        };
 164
 165        /*
 166         * PPI spec defines params[3].type as empty package, but some platforms
 167         * (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for
 168         * compatibility, define params[3].type as buffer, if PPI version < 1.2
 169         */
 170        if (strcmp(chip->ppi_version, "1.2") < 0)
 171                obj = &tmp;
 172        obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETACT,
 173                           ACPI_TYPE_INTEGER, obj);
 174        if (!obj) {
 175                return -ENXIO;
 176        } else {
 177                ret = obj->integer.value;
 178                ACPI_FREE(obj);
 179        }
 180
 181        if (ret < ARRAY_SIZE(info) - 1)
 182                status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]);
 183        else
 184                status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret,
 185                                   info[ARRAY_SIZE(info)-1]);
 186        return status;
 187}
 188
 189static ssize_t tpm_show_ppi_response(struct device *dev,
 190                                     struct device_attribute *attr,
 191                                     char *buf)
 192{
 193        acpi_status status = -EINVAL;
 194        union acpi_object *obj, *ret_obj;
 195        u64 req, res;
 196        struct tpm_chip *chip = to_tpm_chip(dev);
 197
 198        obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETRSP,
 199                           ACPI_TYPE_PACKAGE, NULL);
 200        if (!obj)
 201                return -ENXIO;
 202
 203        /*
 204         * parameter output.pointer should be of package type, including
 205         * 3 integers. The first means function return code, the second means
 206         * most recent TPM operation request, and the last means response to
 207         * the most recent TPM operation request. Only if the first is 0, and
 208         * the second integer is not 0, the response makes sense.
 209         */
 210        ret_obj = obj->package.elements;
 211        if (obj->package.count < 3 ||
 212            ret_obj[0].type != ACPI_TYPE_INTEGER ||
 213            ret_obj[1].type != ACPI_TYPE_INTEGER ||
 214            ret_obj[2].type != ACPI_TYPE_INTEGER)
 215                goto cleanup;
 216
 217        if (ret_obj[0].integer.value) {
 218                status = -EFAULT;
 219                goto cleanup;
 220        }
 221
 222        req = ret_obj[1].integer.value;
 223        res = ret_obj[2].integer.value;
 224        if (req) {
 225                if (res == 0)
 226                        status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
 227                                           "0: Success");
 228                else if (res == 0xFFFFFFF0)
 229                        status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
 230                                           "0xFFFFFFF0: User Abort");
 231                else if (res == 0xFFFFFFF1)
 232                        status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
 233                                           "0xFFFFFFF1: BIOS Failure");
 234                else if (res >= 1 && res <= 0x00000FFF)
 235                        status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
 236                                           req, res, "Corresponding TPM error");
 237                else
 238                        status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
 239                                           req, res, "Error");
 240        } else {
 241                status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n",
 242                                   req, "No Recent Request");
 243        }
 244
 245cleanup:
 246        ACPI_FREE(obj);
 247        return status;
 248}
 249
 250static ssize_t show_ppi_operations(acpi_handle dev_handle, char *buf, u32 start,
 251                                   u32 end)
 252{
 253        int i;
 254        u32 ret;
 255        char *str = buf;
 256        union acpi_object *obj, tmp;
 257        union acpi_object argv = ACPI_INIT_DSM_ARGV4(1, &tmp);
 258
 259        static char *info[] = {
 260                "Not implemented",
 261                "BIOS only",
 262                "Blocked for OS by BIOS",
 263                "User required",
 264                "User not required",
 265        };
 266
 267        if (!acpi_check_dsm(dev_handle, &tpm_ppi_guid, TPM_PPI_REVISION_ID,
 268                            1 << TPM_PPI_FN_GETOPR))
 269                return -EPERM;
 270
 271        tmp.integer.type = ACPI_TYPE_INTEGER;
 272        for (i = start; i <= end; i++) {
 273                tmp.integer.value = i;
 274                obj = tpm_eval_dsm(dev_handle, TPM_PPI_FN_GETOPR,
 275                                   ACPI_TYPE_INTEGER, &argv);
 276                if (!obj) {
 277                        return -ENOMEM;
 278                } else {
 279                        ret = obj->integer.value;
 280                        ACPI_FREE(obj);
 281                }
 282
 283                if (ret > 0 && ret < ARRAY_SIZE(info))
 284                        str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n",
 285                                         i, ret, info[ret]);
 286        }
 287
 288        return str - buf;
 289}
 290
 291static ssize_t tpm_show_ppi_tcg_operations(struct device *dev,
 292                                           struct device_attribute *attr,
 293                                           char *buf)
 294{
 295        struct tpm_chip *chip = to_tpm_chip(dev);
 296
 297        return show_ppi_operations(chip->acpi_dev_handle, buf, 0,
 298                                   PPI_TPM_REQ_MAX);
 299}
 300
 301static ssize_t tpm_show_ppi_vs_operations(struct device *dev,
 302                                          struct device_attribute *attr,
 303                                          char *buf)
 304{
 305        struct tpm_chip *chip = to_tpm_chip(dev);
 306
 307        return show_ppi_operations(chip->acpi_dev_handle, buf, PPI_VS_REQ_START,
 308                                   PPI_VS_REQ_END);
 309}
 310
 311static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL);
 312static DEVICE_ATTR(request, S_IRUGO | S_IWUSR | S_IWGRP,
 313                   tpm_show_ppi_request, tpm_store_ppi_request);
 314static DEVICE_ATTR(transition_action, S_IRUGO,
 315                   tpm_show_ppi_transition_action, NULL);
 316static DEVICE_ATTR(response, S_IRUGO, tpm_show_ppi_response, NULL);
 317static DEVICE_ATTR(tcg_operations, S_IRUGO, tpm_show_ppi_tcg_operations, NULL);
 318static DEVICE_ATTR(vs_operations, S_IRUGO, tpm_show_ppi_vs_operations, NULL);
 319
 320static struct attribute *ppi_attrs[] = {
 321        &dev_attr_version.attr,
 322        &dev_attr_request.attr,
 323        &dev_attr_transition_action.attr,
 324        &dev_attr_response.attr,
 325        &dev_attr_tcg_operations.attr,
 326        &dev_attr_vs_operations.attr, NULL,
 327};
 328static struct attribute_group ppi_attr_grp = {
 329        .name = "ppi",
 330        .attrs = ppi_attrs
 331};
 332
 333void tpm_add_ppi(struct tpm_chip *chip)
 334{
 335        union acpi_object *obj;
 336
 337        if (!chip->acpi_dev_handle)
 338                return;
 339
 340        if (!acpi_check_dsm(chip->acpi_dev_handle, &tpm_ppi_guid,
 341                            TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_VERSION))
 342                return;
 343
 344        /* Cache PPI version string. */
 345        obj = acpi_evaluate_dsm_typed(chip->acpi_dev_handle, &tpm_ppi_guid,
 346                                      TPM_PPI_REVISION_ID, TPM_PPI_FN_VERSION,
 347                                      NULL, ACPI_TYPE_STRING);
 348        if (obj) {
 349                strlcpy(chip->ppi_version, obj->string.pointer,
 350                        sizeof(chip->ppi_version));
 351                ACPI_FREE(obj);
 352        }
 353
 354        chip->groups[chip->groups_cnt++] = &ppi_attr_grp;
 355}
 356