xref: /qemu/hw/ppc/spapr_tpm_proxy.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
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 "qapi/error.h"
150fb6bd07SMichael Roth #include "qemu/error-report.h"
1632cad1ffSPhilippe Mathieu-Daudé #include "system/reset.h"
170fb6bd07SMichael Roth #include "hw/ppc/spapr.h"
180fb6bd07SMichael Roth #include "hw/qdev-properties.h"
190fb6bd07SMichael Roth #include "trace.h"
200fb6bd07SMichael Roth 
210fb6bd07SMichael Roth #define TPM_SPAPR_BUFSIZE 4096
220fb6bd07SMichael Roth 
230fb6bd07SMichael Roth enum {
240fb6bd07SMichael Roth     TPM_COMM_OP_EXECUTE = 1,
250fb6bd07SMichael Roth     TPM_COMM_OP_CLOSE_SESSION = 2,
260fb6bd07SMichael Roth };
270fb6bd07SMichael Roth 
spapr_tpm_proxy_reset(void * opaque)280fb6bd07SMichael Roth static void spapr_tpm_proxy_reset(void *opaque)
290fb6bd07SMichael Roth {
300fb6bd07SMichael Roth     SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(opaque);
310fb6bd07SMichael Roth 
320fb6bd07SMichael Roth     if (tpm_proxy->host_fd != -1) {
330fb6bd07SMichael Roth         close(tpm_proxy->host_fd);
340fb6bd07SMichael Roth         tpm_proxy->host_fd = -1;
350fb6bd07SMichael Roth     }
360fb6bd07SMichael Roth }
370fb6bd07SMichael Roth 
tpm_execute(SpaprTpmProxy * tpm_proxy,target_ulong * args)380fb6bd07SMichael Roth static ssize_t tpm_execute(SpaprTpmProxy *tpm_proxy, target_ulong *args)
390fb6bd07SMichael Roth {
400fb6bd07SMichael Roth     uint64_t data_in = ppc64_phys_to_real(args[1]);
410fb6bd07SMichael Roth     target_ulong data_in_size = args[2];
420fb6bd07SMichael Roth     uint64_t data_out = ppc64_phys_to_real(args[3]);
430fb6bd07SMichael Roth     target_ulong data_out_size = args[4];
440fb6bd07SMichael Roth     uint8_t buf_in[TPM_SPAPR_BUFSIZE];
450fb6bd07SMichael Roth     uint8_t buf_out[TPM_SPAPR_BUFSIZE];
460fb6bd07SMichael Roth     ssize_t ret;
470fb6bd07SMichael Roth 
480fb6bd07SMichael Roth     trace_spapr_tpm_execute(data_in, data_in_size, data_out, data_out_size);
490fb6bd07SMichael Roth 
500fb6bd07SMichael Roth     if (data_in_size > TPM_SPAPR_BUFSIZE) {
510fb6bd07SMichael Roth         error_report("invalid TPM input buffer size: " TARGET_FMT_lu,
520fb6bd07SMichael Roth                      data_in_size);
530fb6bd07SMichael Roth         return H_P3;
540fb6bd07SMichael Roth     }
550fb6bd07SMichael Roth 
560fb6bd07SMichael Roth     if (data_out_size < TPM_SPAPR_BUFSIZE) {
570fb6bd07SMichael Roth         error_report("invalid TPM output buffer size: " TARGET_FMT_lu,
580fb6bd07SMichael Roth                      data_out_size);
590fb6bd07SMichael Roth         return H_P5;
600fb6bd07SMichael Roth     }
610fb6bd07SMichael Roth 
620fb6bd07SMichael Roth     if (tpm_proxy->host_fd == -1) {
630fb6bd07SMichael Roth         tpm_proxy->host_fd = open(tpm_proxy->host_path, O_RDWR);
640fb6bd07SMichael Roth         if (tpm_proxy->host_fd == -1) {
650fb6bd07SMichael Roth             error_report("failed to open TPM device %s: %d",
660fb6bd07SMichael Roth                          tpm_proxy->host_path, errno);
670fb6bd07SMichael Roth             return H_RESOURCE;
680fb6bd07SMichael Roth         }
690fb6bd07SMichael Roth     }
700fb6bd07SMichael Roth 
710fb6bd07SMichael Roth     cpu_physical_memory_read(data_in, buf_in, data_in_size);
720fb6bd07SMichael Roth 
730fb6bd07SMichael Roth     do {
740fb6bd07SMichael Roth         ret = write(tpm_proxy->host_fd, buf_in, data_in_size);
750fb6bd07SMichael Roth         if (ret > 0) {
760fb6bd07SMichael Roth             data_in_size -= ret;
770fb6bd07SMichael Roth         }
780fb6bd07SMichael Roth     } while ((ret >= 0 && data_in_size > 0) || (ret == -1 && errno == EINTR));
790fb6bd07SMichael Roth 
800fb6bd07SMichael Roth     if (ret == -1) {
810fb6bd07SMichael Roth         error_report("failed to write to TPM device %s: %d",
820fb6bd07SMichael Roth                      tpm_proxy->host_path, errno);
830fb6bd07SMichael Roth         return H_RESOURCE;
840fb6bd07SMichael Roth     }
850fb6bd07SMichael Roth 
860fb6bd07SMichael Roth     do {
870fb6bd07SMichael Roth         ret = read(tpm_proxy->host_fd, buf_out, data_out_size);
880fb6bd07SMichael Roth     } while (ret == 0 || (ret == -1 && errno == EINTR));
890fb6bd07SMichael Roth 
900fb6bd07SMichael Roth     if (ret == -1) {
910fb6bd07SMichael Roth         error_report("failed to read from TPM device %s: %d",
920fb6bd07SMichael Roth                      tpm_proxy->host_path, errno);
930fb6bd07SMichael Roth         return H_RESOURCE;
940fb6bd07SMichael Roth     }
950fb6bd07SMichael Roth 
960fb6bd07SMichael Roth     cpu_physical_memory_write(data_out, buf_out, ret);
970fb6bd07SMichael Roth     args[0] = ret;
980fb6bd07SMichael Roth 
990fb6bd07SMichael Roth     return H_SUCCESS;
1000fb6bd07SMichael Roth }
1010fb6bd07SMichael Roth 
h_tpm_comm(PowerPCCPU * cpu,SpaprMachineState * spapr,target_ulong opcode,target_ulong * args)1020fb6bd07SMichael Roth static target_ulong h_tpm_comm(PowerPCCPU *cpu,
1030fb6bd07SMichael Roth                                SpaprMachineState *spapr,
1040fb6bd07SMichael Roth                                target_ulong opcode,
1050fb6bd07SMichael Roth                                target_ulong *args)
1060fb6bd07SMichael Roth {
1070fb6bd07SMichael Roth     target_ulong op = args[0];
1080fb6bd07SMichael Roth     SpaprTpmProxy *tpm_proxy = spapr->tpm_proxy;
1090fb6bd07SMichael Roth 
1100fb6bd07SMichael Roth     if (!tpm_proxy) {
1110fb6bd07SMichael Roth         error_report("TPM proxy not available");
1120fb6bd07SMichael Roth         return H_FUNCTION;
1130fb6bd07SMichael Roth     }
1140fb6bd07SMichael Roth 
115226c9d15SGreg Kurz     trace_spapr_h_tpm_comm(tpm_proxy->host_path, op);
1160fb6bd07SMichael Roth 
1170fb6bd07SMichael Roth     switch (op) {
1180fb6bd07SMichael Roth     case TPM_COMM_OP_EXECUTE:
1190fb6bd07SMichael Roth         return tpm_execute(tpm_proxy, args);
1200fb6bd07SMichael Roth     case TPM_COMM_OP_CLOSE_SESSION:
1210fb6bd07SMichael Roth         spapr_tpm_proxy_reset(tpm_proxy);
1220fb6bd07SMichael Roth         return H_SUCCESS;
1230fb6bd07SMichael Roth     default:
1240fb6bd07SMichael Roth         return H_PARAMETER;
1250fb6bd07SMichael Roth     }
1260fb6bd07SMichael Roth }
1270fb6bd07SMichael Roth 
spapr_tpm_proxy_realize(DeviceState * d,Error ** errp)1280fb6bd07SMichael Roth static void spapr_tpm_proxy_realize(DeviceState *d, Error **errp)
1290fb6bd07SMichael Roth {
1300fb6bd07SMichael Roth     SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(d);
1310fb6bd07SMichael Roth 
1320fb6bd07SMichael Roth     if (tpm_proxy->host_path == NULL) {
1330fb6bd07SMichael Roth         error_setg(errp, "must specify 'host-path' option for device");
1340fb6bd07SMichael Roth         return;
1350fb6bd07SMichael Roth     }
1360fb6bd07SMichael Roth 
1370fb6bd07SMichael Roth     tpm_proxy->host_fd = -1;
1380fb6bd07SMichael Roth     qemu_register_reset(spapr_tpm_proxy_reset, tpm_proxy);
1390fb6bd07SMichael Roth }
1400fb6bd07SMichael Roth 
spapr_tpm_proxy_unrealize(DeviceState * d)141b69c3c21SMarkus Armbruster static void spapr_tpm_proxy_unrealize(DeviceState *d)
1420fb6bd07SMichael Roth {
1430fb6bd07SMichael Roth     SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(d);
1440fb6bd07SMichael Roth 
1450fb6bd07SMichael Roth     qemu_unregister_reset(spapr_tpm_proxy_reset, tpm_proxy);
1460fb6bd07SMichael Roth }
1470fb6bd07SMichael Roth 
14890f5755eSRichard Henderson static const Property spapr_tpm_proxy_properties[] = {
1490fb6bd07SMichael Roth     DEFINE_PROP_STRING("host-path", SpaprTpmProxy, host_path),
1500fb6bd07SMichael Roth };
1510fb6bd07SMichael Roth 
spapr_tpm_proxy_class_init(ObjectClass * k,const void * data)152*12d1a768SPhilippe Mathieu-Daudé static void spapr_tpm_proxy_class_init(ObjectClass *k, const void *data)
1530fb6bd07SMichael Roth {
1540fb6bd07SMichael Roth     DeviceClass *dk = DEVICE_CLASS(k);
1550fb6bd07SMichael Roth 
1560fb6bd07SMichael Roth     dk->realize = spapr_tpm_proxy_realize;
1570fb6bd07SMichael Roth     dk->unrealize = spapr_tpm_proxy_unrealize;
1580fb6bd07SMichael Roth     dk->user_creatable = true;
1594f67d30bSMarc-André Lureau     device_class_set_props(dk, spapr_tpm_proxy_properties);
1600fb6bd07SMichael Roth }
1610fb6bd07SMichael Roth 
1620fb6bd07SMichael Roth static const TypeInfo spapr_tpm_proxy_info = {
1630fb6bd07SMichael Roth     .name          = TYPE_SPAPR_TPM_PROXY,
1640fb6bd07SMichael Roth     .parent        = TYPE_DEVICE,
1650fb6bd07SMichael Roth     .instance_size = sizeof(SpaprTpmProxy),
1660fb6bd07SMichael Roth     .class_init    = spapr_tpm_proxy_class_init,
1670fb6bd07SMichael Roth };
1680fb6bd07SMichael Roth 
spapr_tpm_proxy_register_types(void)1690fb6bd07SMichael Roth static void spapr_tpm_proxy_register_types(void)
1700fb6bd07SMichael Roth {
1710fb6bd07SMichael Roth     type_register_static(&spapr_tpm_proxy_info);
1720fb6bd07SMichael Roth     spapr_register_hypercall(SVM_H_TPM_COMM, h_tpm_comm);
1730fb6bd07SMichael Roth }
1740fb6bd07SMichael Roth 
1750fb6bd07SMichael Roth type_init(spapr_tpm_proxy_register_types)
176