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" 16*32cad1ffSPhilippe 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 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 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 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 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 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 DEFINE_PROP_END_OF_LIST(), 1510fb6bd07SMichael Roth }; 1520fb6bd07SMichael Roth 1530fb6bd07SMichael Roth static void spapr_tpm_proxy_class_init(ObjectClass *k, void *data) 1540fb6bd07SMichael Roth { 1550fb6bd07SMichael Roth DeviceClass *dk = DEVICE_CLASS(k); 1560fb6bd07SMichael Roth 1570fb6bd07SMichael Roth dk->realize = spapr_tpm_proxy_realize; 1580fb6bd07SMichael Roth dk->unrealize = spapr_tpm_proxy_unrealize; 1590fb6bd07SMichael Roth dk->user_creatable = true; 1604f67d30bSMarc-André Lureau device_class_set_props(dk, spapr_tpm_proxy_properties); 1610fb6bd07SMichael Roth } 1620fb6bd07SMichael Roth 1630fb6bd07SMichael Roth static const TypeInfo spapr_tpm_proxy_info = { 1640fb6bd07SMichael Roth .name = TYPE_SPAPR_TPM_PROXY, 1650fb6bd07SMichael Roth .parent = TYPE_DEVICE, 1660fb6bd07SMichael Roth .instance_size = sizeof(SpaprTpmProxy), 1670fb6bd07SMichael Roth .class_init = spapr_tpm_proxy_class_init, 1680fb6bd07SMichael Roth }; 1690fb6bd07SMichael Roth 1700fb6bd07SMichael Roth static void spapr_tpm_proxy_register_types(void) 1710fb6bd07SMichael Roth { 1720fb6bd07SMichael Roth type_register_static(&spapr_tpm_proxy_info); 1730fb6bd07SMichael Roth spapr_register_hypercall(SVM_H_TPM_COMM, h_tpm_comm); 1740fb6bd07SMichael Roth } 1750fb6bd07SMichael Roth 1760fb6bd07SMichael Roth type_init(spapr_tpm_proxy_register_types) 177