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