xref: /qemu/hw/ppc/spapr_tpm_proxy.c (revision 4f67d30b5e74e060b8dbe10528829b47345cd6e8)
10fb6bd07SMichael Roth /*
20fb6bd07SMichael Roth  * SPAPR TPM Proxy/Hypercall
30fb6bd07SMichael Roth  *
40fb6bd07SMichael Roth  * Copyright IBM Corp. 2019
50fb6bd07SMichael Roth  *
60fb6bd07SMichael Roth  * Authors:
70fb6bd07SMichael Roth  *  Michael Roth      <mdroth@linux.vnet.ibm.com>
80fb6bd07SMichael Roth  *
90fb6bd07SMichael Roth  * This work is licensed under the terms of the GNU GPL, version 2 or later.
100fb6bd07SMichael Roth  * See the COPYING file in the top-level directory.
110fb6bd07SMichael Roth  */
120fb6bd07SMichael Roth 
130fb6bd07SMichael Roth #include "qemu/osdep.h"
140fb6bd07SMichael Roth #include "qemu-common.h"
150fb6bd07SMichael Roth #include "qapi/error.h"
160fb6bd07SMichael Roth #include "qemu/error-report.h"
170fb6bd07SMichael Roth #include "sysemu/reset.h"
180fb6bd07SMichael Roth #include "cpu.h"
190fb6bd07SMichael Roth #include "hw/ppc/spapr.h"
200fb6bd07SMichael Roth #include "hw/qdev-properties.h"
210fb6bd07SMichael Roth #include "trace.h"
220fb6bd07SMichael Roth 
230fb6bd07SMichael Roth #define TPM_SPAPR_BUFSIZE 4096
240fb6bd07SMichael Roth 
250fb6bd07SMichael Roth enum {
260fb6bd07SMichael Roth     TPM_COMM_OP_EXECUTE = 1,
270fb6bd07SMichael Roth     TPM_COMM_OP_CLOSE_SESSION = 2,
280fb6bd07SMichael Roth };
290fb6bd07SMichael Roth 
300fb6bd07SMichael Roth static void spapr_tpm_proxy_reset(void *opaque)
310fb6bd07SMichael Roth {
320fb6bd07SMichael Roth     SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(opaque);
330fb6bd07SMichael Roth 
340fb6bd07SMichael Roth     if (tpm_proxy->host_fd != -1) {
350fb6bd07SMichael Roth         close(tpm_proxy->host_fd);
360fb6bd07SMichael Roth         tpm_proxy->host_fd = -1;
370fb6bd07SMichael Roth     }
380fb6bd07SMichael Roth }
390fb6bd07SMichael Roth 
400fb6bd07SMichael Roth static ssize_t tpm_execute(SpaprTpmProxy *tpm_proxy, target_ulong *args)
410fb6bd07SMichael Roth {
420fb6bd07SMichael Roth     uint64_t data_in = ppc64_phys_to_real(args[1]);
430fb6bd07SMichael Roth     target_ulong data_in_size = args[2];
440fb6bd07SMichael Roth     uint64_t data_out = ppc64_phys_to_real(args[3]);
450fb6bd07SMichael Roth     target_ulong data_out_size = args[4];
460fb6bd07SMichael Roth     uint8_t buf_in[TPM_SPAPR_BUFSIZE];
470fb6bd07SMichael Roth     uint8_t buf_out[TPM_SPAPR_BUFSIZE];
480fb6bd07SMichael Roth     ssize_t ret;
490fb6bd07SMichael Roth 
500fb6bd07SMichael Roth     trace_spapr_tpm_execute(data_in, data_in_size, data_out, data_out_size);
510fb6bd07SMichael Roth 
520fb6bd07SMichael Roth     if (data_in_size > TPM_SPAPR_BUFSIZE) {
530fb6bd07SMichael Roth         error_report("invalid TPM input buffer size: " TARGET_FMT_lu,
540fb6bd07SMichael Roth                      data_in_size);
550fb6bd07SMichael Roth         return H_P3;
560fb6bd07SMichael Roth     }
570fb6bd07SMichael Roth 
580fb6bd07SMichael Roth     if (data_out_size < TPM_SPAPR_BUFSIZE) {
590fb6bd07SMichael Roth         error_report("invalid TPM output buffer size: " TARGET_FMT_lu,
600fb6bd07SMichael Roth                      data_out_size);
610fb6bd07SMichael Roth         return H_P5;
620fb6bd07SMichael Roth     }
630fb6bd07SMichael Roth 
640fb6bd07SMichael Roth     if (tpm_proxy->host_fd == -1) {
650fb6bd07SMichael Roth         tpm_proxy->host_fd = open(tpm_proxy->host_path, O_RDWR);
660fb6bd07SMichael Roth         if (tpm_proxy->host_fd == -1) {
670fb6bd07SMichael Roth             error_report("failed to open TPM device %s: %d",
680fb6bd07SMichael Roth                          tpm_proxy->host_path, errno);
690fb6bd07SMichael Roth             return H_RESOURCE;
700fb6bd07SMichael Roth         }
710fb6bd07SMichael Roth     }
720fb6bd07SMichael Roth 
730fb6bd07SMichael Roth     cpu_physical_memory_read(data_in, buf_in, data_in_size);
740fb6bd07SMichael Roth 
750fb6bd07SMichael Roth     do {
760fb6bd07SMichael Roth         ret = write(tpm_proxy->host_fd, buf_in, data_in_size);
770fb6bd07SMichael Roth         if (ret > 0) {
780fb6bd07SMichael Roth             data_in_size -= ret;
790fb6bd07SMichael Roth         }
800fb6bd07SMichael Roth     } while ((ret >= 0 && data_in_size > 0) || (ret == -1 && errno == EINTR));
810fb6bd07SMichael Roth 
820fb6bd07SMichael Roth     if (ret == -1) {
830fb6bd07SMichael Roth         error_report("failed to write to TPM device %s: %d",
840fb6bd07SMichael Roth                      tpm_proxy->host_path, errno);
850fb6bd07SMichael Roth         return H_RESOURCE;
860fb6bd07SMichael Roth     }
870fb6bd07SMichael Roth 
880fb6bd07SMichael Roth     do {
890fb6bd07SMichael Roth         ret = read(tpm_proxy->host_fd, buf_out, data_out_size);
900fb6bd07SMichael Roth     } while (ret == 0 || (ret == -1 && errno == EINTR));
910fb6bd07SMichael Roth 
920fb6bd07SMichael Roth     if (ret == -1) {
930fb6bd07SMichael Roth         error_report("failed to read from TPM device %s: %d",
940fb6bd07SMichael Roth                      tpm_proxy->host_path, errno);
950fb6bd07SMichael Roth         return H_RESOURCE;
960fb6bd07SMichael Roth     }
970fb6bd07SMichael Roth 
980fb6bd07SMichael Roth     cpu_physical_memory_write(data_out, buf_out, ret);
990fb6bd07SMichael Roth     args[0] = ret;
1000fb6bd07SMichael Roth 
1010fb6bd07SMichael Roth     return H_SUCCESS;
1020fb6bd07SMichael Roth }
1030fb6bd07SMichael Roth 
1040fb6bd07SMichael Roth static target_ulong h_tpm_comm(PowerPCCPU *cpu,
1050fb6bd07SMichael Roth                                SpaprMachineState *spapr,
1060fb6bd07SMichael Roth                                target_ulong opcode,
1070fb6bd07SMichael Roth                                target_ulong *args)
1080fb6bd07SMichael Roth {
1090fb6bd07SMichael Roth     target_ulong op = args[0];
1100fb6bd07SMichael Roth     SpaprTpmProxy *tpm_proxy = spapr->tpm_proxy;
1110fb6bd07SMichael Roth 
1120fb6bd07SMichael Roth     if (!tpm_proxy) {
1130fb6bd07SMichael Roth         error_report("TPM proxy not available");
1140fb6bd07SMichael Roth         return H_FUNCTION;
1150fb6bd07SMichael Roth     }
1160fb6bd07SMichael Roth 
117226c9d15SGreg Kurz     trace_spapr_h_tpm_comm(tpm_proxy->host_path, op);
1180fb6bd07SMichael Roth 
1190fb6bd07SMichael Roth     switch (op) {
1200fb6bd07SMichael Roth     case TPM_COMM_OP_EXECUTE:
1210fb6bd07SMichael Roth         return tpm_execute(tpm_proxy, args);
1220fb6bd07SMichael Roth     case TPM_COMM_OP_CLOSE_SESSION:
1230fb6bd07SMichael Roth         spapr_tpm_proxy_reset(tpm_proxy);
1240fb6bd07SMichael Roth         return H_SUCCESS;
1250fb6bd07SMichael Roth     default:
1260fb6bd07SMichael Roth         return H_PARAMETER;
1270fb6bd07SMichael Roth     }
1280fb6bd07SMichael Roth }
1290fb6bd07SMichael Roth 
1300fb6bd07SMichael Roth static void spapr_tpm_proxy_realize(DeviceState *d, Error **errp)
1310fb6bd07SMichael Roth {
1320fb6bd07SMichael Roth     SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(d);
1330fb6bd07SMichael Roth 
1340fb6bd07SMichael Roth     if (tpm_proxy->host_path == NULL) {
1350fb6bd07SMichael Roth         error_setg(errp, "must specify 'host-path' option for device");
1360fb6bd07SMichael Roth         return;
1370fb6bd07SMichael Roth     }
1380fb6bd07SMichael Roth 
1390fb6bd07SMichael Roth     tpm_proxy->host_fd = -1;
1400fb6bd07SMichael Roth     qemu_register_reset(spapr_tpm_proxy_reset, tpm_proxy);
1410fb6bd07SMichael Roth }
1420fb6bd07SMichael Roth 
1430fb6bd07SMichael Roth static void spapr_tpm_proxy_unrealize(DeviceState *d, Error **errp)
1440fb6bd07SMichael Roth {
1450fb6bd07SMichael Roth     SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(d);
1460fb6bd07SMichael Roth 
1470fb6bd07SMichael Roth     qemu_unregister_reset(spapr_tpm_proxy_reset, tpm_proxy);
1480fb6bd07SMichael Roth }
1490fb6bd07SMichael Roth 
1500fb6bd07SMichael Roth static Property spapr_tpm_proxy_properties[] = {
1510fb6bd07SMichael Roth     DEFINE_PROP_STRING("host-path", SpaprTpmProxy, host_path),
1520fb6bd07SMichael Roth     DEFINE_PROP_END_OF_LIST(),
1530fb6bd07SMichael Roth };
1540fb6bd07SMichael Roth 
1550fb6bd07SMichael Roth static void spapr_tpm_proxy_class_init(ObjectClass *k, void *data)
1560fb6bd07SMichael Roth {
1570fb6bd07SMichael Roth     DeviceClass *dk = DEVICE_CLASS(k);
1580fb6bd07SMichael Roth 
1590fb6bd07SMichael Roth     dk->realize = spapr_tpm_proxy_realize;
1600fb6bd07SMichael Roth     dk->unrealize = spapr_tpm_proxy_unrealize;
1610fb6bd07SMichael Roth     dk->user_creatable = true;
162*4f67d30bSMarc-André Lureau     device_class_set_props(dk, spapr_tpm_proxy_properties);
1630fb6bd07SMichael Roth }
1640fb6bd07SMichael Roth 
1650fb6bd07SMichael Roth static const TypeInfo spapr_tpm_proxy_info = {
1660fb6bd07SMichael Roth     .name          = TYPE_SPAPR_TPM_PROXY,
1670fb6bd07SMichael Roth     .parent        = TYPE_DEVICE,
1680fb6bd07SMichael Roth     .instance_size = sizeof(SpaprTpmProxy),
1690fb6bd07SMichael Roth     .class_init    = spapr_tpm_proxy_class_init,
1700fb6bd07SMichael Roth };
1710fb6bd07SMichael Roth 
1720fb6bd07SMichael Roth static void spapr_tpm_proxy_register_types(void)
1730fb6bd07SMichael Roth {
1740fb6bd07SMichael Roth     type_register_static(&spapr_tpm_proxy_info);
1750fb6bd07SMichael Roth     spapr_register_hypercall(SVM_H_TPM_COMM, h_tpm_comm);
1760fb6bd07SMichael Roth }
1770fb6bd07SMichael Roth 
1780fb6bd07SMichael Roth type_init(spapr_tpm_proxy_register_types)
179