qemu/hw/ppc/spapr_tpm_proxy.c
<<
>>
Prefs
   1/*
   2 * SPAPR TPM Proxy/Hypercall
   3 *
   4 * Copyright IBM Corp. 2019
   5 *
   6 * Authors:
   7 *  Michael Roth      <mdroth@linux.vnet.ibm.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10 * See the COPYING file in the top-level directory.
  11 */
  12
  13#include "qemu/osdep.h"
  14#include "qapi/error.h"
  15#include "qemu/error-report.h"
  16#include "sysemu/reset.h"
  17#include "hw/ppc/spapr.h"
  18#include "hw/qdev-properties.h"
  19#include "trace.h"
  20
  21#define TPM_SPAPR_BUFSIZE 4096
  22
  23enum {
  24    TPM_COMM_OP_EXECUTE = 1,
  25    TPM_COMM_OP_CLOSE_SESSION = 2,
  26};
  27
  28static void spapr_tpm_proxy_reset(void *opaque)
  29{
  30    SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(opaque);
  31
  32    if (tpm_proxy->host_fd != -1) {
  33        close(tpm_proxy->host_fd);
  34        tpm_proxy->host_fd = -1;
  35    }
  36}
  37
  38static ssize_t tpm_execute(SpaprTpmProxy *tpm_proxy, target_ulong *args)
  39{
  40    uint64_t data_in = ppc64_phys_to_real(args[1]);
  41    target_ulong data_in_size = args[2];
  42    uint64_t data_out = ppc64_phys_to_real(args[3]);
  43    target_ulong data_out_size = args[4];
  44    uint8_t buf_in[TPM_SPAPR_BUFSIZE];
  45    uint8_t buf_out[TPM_SPAPR_BUFSIZE];
  46    ssize_t ret;
  47
  48    trace_spapr_tpm_execute(data_in, data_in_size, data_out, data_out_size);
  49
  50    if (data_in_size > TPM_SPAPR_BUFSIZE) {
  51        error_report("invalid TPM input buffer size: " TARGET_FMT_lu,
  52                     data_in_size);
  53        return H_P3;
  54    }
  55
  56    if (data_out_size < TPM_SPAPR_BUFSIZE) {
  57        error_report("invalid TPM output buffer size: " TARGET_FMT_lu,
  58                     data_out_size);
  59        return H_P5;
  60    }
  61
  62    if (tpm_proxy->host_fd == -1) {
  63        tpm_proxy->host_fd = open(tpm_proxy->host_path, O_RDWR);
  64        if (tpm_proxy->host_fd == -1) {
  65            error_report("failed to open TPM device %s: %d",
  66                         tpm_proxy->host_path, errno);
  67            return H_RESOURCE;
  68        }
  69    }
  70
  71    cpu_physical_memory_read(data_in, buf_in, data_in_size);
  72
  73    do {
  74        ret = write(tpm_proxy->host_fd, buf_in, data_in_size);
  75        if (ret > 0) {
  76            data_in_size -= ret;
  77        }
  78    } while ((ret >= 0 && data_in_size > 0) || (ret == -1 && errno == EINTR));
  79
  80    if (ret == -1) {
  81        error_report("failed to write to TPM device %s: %d",
  82                     tpm_proxy->host_path, errno);
  83        return H_RESOURCE;
  84    }
  85
  86    do {
  87        ret = read(tpm_proxy->host_fd, buf_out, data_out_size);
  88    } while (ret == 0 || (ret == -1 && errno == EINTR));
  89
  90    if (ret == -1) {
  91        error_report("failed to read from TPM device %s: %d",
  92                     tpm_proxy->host_path, errno);
  93        return H_RESOURCE;
  94    }
  95
  96    cpu_physical_memory_write(data_out, buf_out, ret);
  97    args[0] = ret;
  98
  99    return H_SUCCESS;
 100}
 101
 102static target_ulong h_tpm_comm(PowerPCCPU *cpu,
 103                               SpaprMachineState *spapr,
 104                               target_ulong opcode,
 105                               target_ulong *args)
 106{
 107    target_ulong op = args[0];
 108    SpaprTpmProxy *tpm_proxy = spapr->tpm_proxy;
 109
 110    if (!tpm_proxy) {
 111        error_report("TPM proxy not available");
 112        return H_FUNCTION;
 113    }
 114
 115    trace_spapr_h_tpm_comm(tpm_proxy->host_path, op);
 116
 117    switch (op) {
 118    case TPM_COMM_OP_EXECUTE:
 119        return tpm_execute(tpm_proxy, args);
 120    case TPM_COMM_OP_CLOSE_SESSION:
 121        spapr_tpm_proxy_reset(tpm_proxy);
 122        return H_SUCCESS;
 123    default:
 124        return H_PARAMETER;
 125    }
 126}
 127
 128static void spapr_tpm_proxy_realize(DeviceState *d, Error **errp)
 129{
 130    SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(d);
 131
 132    if (tpm_proxy->host_path == NULL) {
 133        error_setg(errp, "must specify 'host-path' option for device");
 134        return;
 135    }
 136
 137    tpm_proxy->host_fd = -1;
 138    qemu_register_reset(spapr_tpm_proxy_reset, tpm_proxy);
 139}
 140
 141static void spapr_tpm_proxy_unrealize(DeviceState *d)
 142{
 143    SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(d);
 144
 145    qemu_unregister_reset(spapr_tpm_proxy_reset, tpm_proxy);
 146}
 147
 148static Property spapr_tpm_proxy_properties[] = {
 149    DEFINE_PROP_STRING("host-path", SpaprTpmProxy, host_path),
 150    DEFINE_PROP_END_OF_LIST(),
 151};
 152
 153static void spapr_tpm_proxy_class_init(ObjectClass *k, void *data)
 154{
 155    DeviceClass *dk = DEVICE_CLASS(k);
 156
 157    dk->realize = spapr_tpm_proxy_realize;
 158    dk->unrealize = spapr_tpm_proxy_unrealize;
 159    dk->user_creatable = true;
 160    device_class_set_props(dk, spapr_tpm_proxy_properties);
 161}
 162
 163static const TypeInfo spapr_tpm_proxy_info = {
 164    .name          = TYPE_SPAPR_TPM_PROXY,
 165    .parent        = TYPE_DEVICE,
 166    .instance_size = sizeof(SpaprTpmProxy),
 167    .class_init    = spapr_tpm_proxy_class_init,
 168};
 169
 170static void spapr_tpm_proxy_register_types(void)
 171{
 172    type_register_static(&spapr_tpm_proxy_info);
 173    spapr_register_hypercall(SVM_H_TPM_COMM, h_tpm_comm);
 174}
 175
 176type_init(spapr_tpm_proxy_register_types)
 177