 68a5b02a46
			
		
	
	
		68a5b02a46
		
	
	
	
	
		
			
			Linking of qemu-system-ppc64 fails on macOS with dtrace enabled:
  error: probe tpm_spapr_show_buffer doesn't exist
  error: Could not register probes
  ld: error creating dtrace DOF section for architecture x86_64
The failure is explained in 8c8ed03850208e4 ("net/colo: Match is-enabled
probe to tracepoint"). In short, is-enabled probe can't be used without
a matching trace probe. And for this particular case
tpm_util_show_buffer probe should be enabled to print TPM buffer.
Signed-off-by: Roman Bolshakov <r.bolshakov@yadro.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
		
	
			
		
			
				
	
	
		
			431 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			431 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
 | |
|  *
 | |
|  * PAPR Virtual TPM
 | |
|  *
 | |
|  * Copyright (c) 2015, 2017, 2019 IBM Corporation.
 | |
|  *
 | |
|  * Authors:
 | |
|  *    Stefan Berger <stefanb@linux.vnet.ibm.com>
 | |
|  *
 | |
|  * This code is licensed under the GPL version 2 or later. See the
 | |
|  * COPYING file in the top-level directory.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "qemu/error-report.h"
 | |
| #include "qapi/error.h"
 | |
| #include "hw/qdev-properties.h"
 | |
| #include "migration/vmstate.h"
 | |
| 
 | |
| #include "sysemu/tpm_backend.h"
 | |
| #include "sysemu/tpm_util.h"
 | |
| #include "tpm_prop.h"
 | |
| 
 | |
| #include "hw/ppc/spapr.h"
 | |
| #include "hw/ppc/spapr_vio.h"
 | |
| #include "trace.h"
 | |
| #include "qom/object.h"
 | |
| 
 | |
| #define DEBUG_SPAPR 0
 | |
| 
 | |
| typedef struct SpaprTpmState SpaprTpmState;
 | |
| DECLARE_INSTANCE_CHECKER(SpaprTpmState, VIO_SPAPR_VTPM,
 | |
|                          TYPE_TPM_SPAPR)
 | |
| 
 | |
| typedef struct TpmCrq {
 | |
|     uint8_t valid;  /* 0x80: cmd; 0xc0: init crq */
 | |
|                     /* 0x81-0x83: CRQ message response */
 | |
|     uint8_t msg;    /* see below */
 | |
|     uint16_t len;   /* len of TPM request; len of TPM response */
 | |
|     uint32_t data;  /* rtce_dma_handle when sending TPM request */
 | |
|     uint64_t reserved;
 | |
| } TpmCrq;
 | |
| 
 | |
| #define SPAPR_VTPM_VALID_INIT_CRQ_COMMAND  0xC0
 | |
| #define SPAPR_VTPM_VALID_COMMAND           0x80
 | |
| #define SPAPR_VTPM_MSG_RESULT              0x80
 | |
| 
 | |
| /* msg types for valid = SPAPR_VTPM_VALID_INIT_CRQ */
 | |
| #define SPAPR_VTPM_INIT_CRQ_RESULT           0x1
 | |
| #define SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT  0x2
 | |
| 
 | |
| /* msg types for valid = SPAPR_VTPM_VALID_CMD */
 | |
| #define SPAPR_VTPM_GET_VERSION               0x1
 | |
| #define SPAPR_VTPM_TPM_COMMAND               0x2
 | |
| #define SPAPR_VTPM_GET_RTCE_BUFFER_SIZE      0x3
 | |
| #define SPAPR_VTPM_PREPARE_TO_SUSPEND        0x4
 | |
| 
 | |
| /* response error messages */
 | |
| #define SPAPR_VTPM_VTPM_ERROR                0xff
 | |
| 
 | |
| /* error codes */
 | |
| #define SPAPR_VTPM_ERR_COPY_IN_FAILED        0x3
 | |
| #define SPAPR_VTPM_ERR_COPY_OUT_FAILED       0x4
 | |
| 
 | |
| #define TPM_SPAPR_BUFFER_MAX                 4096
 | |
| 
 | |
| struct SpaprTpmState {
 | |
|     SpaprVioDevice vdev;
 | |
| 
 | |
|     TpmCrq crq; /* track single TPM command */
 | |
| 
 | |
|     uint8_t state;
 | |
| #define SPAPR_VTPM_STATE_NONE         0
 | |
| #define SPAPR_VTPM_STATE_EXECUTION    1
 | |
| #define SPAPR_VTPM_STATE_COMPLETION   2
 | |
| 
 | |
|     unsigned char *buffer;
 | |
| 
 | |
|     uint32_t numbytes; /* number of bytes to deliver on resume */
 | |
| 
 | |
|     TPMBackendCmd cmd;
 | |
| 
 | |
|     TPMBackend *be_driver;
 | |
|     TPMVersion be_tpm_version;
 | |
| 
 | |
|     size_t be_buffer_size;
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * Send a request to the TPM.
 | |
|  */
 | |
| static void tpm_spapr_tpm_send(SpaprTpmState *s)
 | |
| {
 | |
|     tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM");
 | |
| 
 | |
|     s->state = SPAPR_VTPM_STATE_EXECUTION;
 | |
|     s->cmd = (TPMBackendCmd) {
 | |
|         .locty = 0,
 | |
|         .in = s->buffer,
 | |
|         .in_len = MIN(tpm_cmd_get_size(s->buffer), s->be_buffer_size),
 | |
|         .out = s->buffer,
 | |
|         .out_len = s->be_buffer_size,
 | |
|     };
 | |
| 
 | |
|     tpm_backend_deliver_request(s->be_driver, &s->cmd);
 | |
| }
 | |
| 
 | |
| static int tpm_spapr_process_cmd(SpaprTpmState *s, uint64_t dataptr)
 | |
| {
 | |
|     long rc;
 | |
| 
 | |
|     /* a max. of be_buffer_size bytes can be transported */
 | |
|     rc = spapr_vio_dma_read(&s->vdev, dataptr,
 | |
|                             s->buffer, s->be_buffer_size);
 | |
|     if (rc) {
 | |
|         error_report("tpm_spapr_got_payload: DMA read failure");
 | |
|     }
 | |
|     /* let vTPM handle any malformed request */
 | |
|     tpm_spapr_tpm_send(s);
 | |
| 
 | |
|     return rc;
 | |
| }
 | |
| 
 | |
| static inline int spapr_tpm_send_crq(struct SpaprVioDevice *dev, TpmCrq *crq)
 | |
| {
 | |
|     return spapr_vio_send_crq(dev, (uint8_t *)crq);
 | |
| }
 | |
| 
 | |
| static int tpm_spapr_do_crq(struct SpaprVioDevice *dev, uint8_t *crq_data)
 | |
| {
 | |
|     SpaprTpmState *s = VIO_SPAPR_VTPM(dev);
 | |
|     TpmCrq local_crq;
 | |
|     TpmCrq *crq = &s->crq; /* requests only */
 | |
|     int rc;
 | |
|     uint8_t valid = crq_data[0];
 | |
|     uint8_t msg = crq_data[1];
 | |
| 
 | |
|     trace_tpm_spapr_do_crq(valid, msg);
 | |
| 
 | |
|     switch (valid) {
 | |
|     case SPAPR_VTPM_VALID_INIT_CRQ_COMMAND: /* Init command/response */
 | |
| 
 | |
|         /* Respond to initialization request */
 | |
|         switch (msg) {
 | |
|         case SPAPR_VTPM_INIT_CRQ_RESULT:
 | |
|             trace_tpm_spapr_do_crq_crq_result();
 | |
|             memset(&local_crq, 0, sizeof(local_crq));
 | |
|             local_crq.valid = SPAPR_VTPM_VALID_INIT_CRQ_COMMAND;
 | |
|             local_crq.msg = SPAPR_VTPM_INIT_CRQ_RESULT;
 | |
|             spapr_tpm_send_crq(dev, &local_crq);
 | |
|             break;
 | |
| 
 | |
|         case SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT:
 | |
|             trace_tpm_spapr_do_crq_crq_complete_result();
 | |
|             memset(&local_crq, 0, sizeof(local_crq));
 | |
|             local_crq.valid = SPAPR_VTPM_VALID_INIT_CRQ_COMMAND;
 | |
|             local_crq.msg = SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT;
 | |
|             spapr_tpm_send_crq(dev, &local_crq);
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         break;
 | |
|     case SPAPR_VTPM_VALID_COMMAND: /* Payloads */
 | |
|         switch (msg) {
 | |
|         case SPAPR_VTPM_TPM_COMMAND:
 | |
|             trace_tpm_spapr_do_crq_tpm_command();
 | |
|             if (s->state == SPAPR_VTPM_STATE_EXECUTION) {
 | |
|                 return H_BUSY;
 | |
|             }
 | |
|             memcpy(crq, crq_data, sizeof(*crq));
 | |
| 
 | |
|             rc = tpm_spapr_process_cmd(s, be32_to_cpu(crq->data));
 | |
| 
 | |
|             if (rc == H_SUCCESS) {
 | |
|                 crq->valid = be16_to_cpu(0);
 | |
|             } else {
 | |
|                 local_crq.valid = SPAPR_VTPM_MSG_RESULT;
 | |
|                 local_crq.msg = SPAPR_VTPM_VTPM_ERROR;
 | |
|                 local_crq.len = cpu_to_be16(0);
 | |
|                 local_crq.data = cpu_to_be32(SPAPR_VTPM_ERR_COPY_IN_FAILED);
 | |
|                 spapr_tpm_send_crq(dev, &local_crq);
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         case SPAPR_VTPM_GET_RTCE_BUFFER_SIZE:
 | |
|             trace_tpm_spapr_do_crq_tpm_get_rtce_buffer_size(s->be_buffer_size);
 | |
|             local_crq.valid = SPAPR_VTPM_VALID_COMMAND;
 | |
|             local_crq.msg = SPAPR_VTPM_GET_RTCE_BUFFER_SIZE |
 | |
|                             SPAPR_VTPM_MSG_RESULT;
 | |
|             local_crq.len = cpu_to_be16(s->be_buffer_size);
 | |
|             spapr_tpm_send_crq(dev, &local_crq);
 | |
|             break;
 | |
| 
 | |
|         case SPAPR_VTPM_GET_VERSION:
 | |
|             local_crq.valid = SPAPR_VTPM_VALID_COMMAND;
 | |
|             local_crq.msg = SPAPR_VTPM_GET_VERSION | SPAPR_VTPM_MSG_RESULT;
 | |
|             local_crq.len = cpu_to_be16(0);
 | |
|             switch (s->be_tpm_version) {
 | |
|             case TPM_VERSION_1_2:
 | |
|                 local_crq.data = cpu_to_be32(1);
 | |
|                 break;
 | |
|             case TPM_VERSION_2_0:
 | |
|                 local_crq.data = cpu_to_be32(2);
 | |
|                 break;
 | |
|             default:
 | |
|                 g_assert_not_reached();
 | |
|                 break;
 | |
|             }
 | |
|             trace_tpm_spapr_do_crq_get_version(be32_to_cpu(local_crq.data));
 | |
|             spapr_tpm_send_crq(dev, &local_crq);
 | |
|             break;
 | |
| 
 | |
|         case SPAPR_VTPM_PREPARE_TO_SUSPEND:
 | |
|             trace_tpm_spapr_do_crq_prepare_to_suspend();
 | |
|             local_crq.valid = SPAPR_VTPM_VALID_COMMAND;
 | |
|             local_crq.msg = SPAPR_VTPM_PREPARE_TO_SUSPEND |
 | |
|                             SPAPR_VTPM_MSG_RESULT;
 | |
|             spapr_tpm_send_crq(dev, &local_crq);
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             trace_tpm_spapr_do_crq_unknown_msg_type(crq->msg);
 | |
|         }
 | |
|         break;
 | |
|     default:
 | |
|         trace_tpm_spapr_do_crq_unknown_crq(valid, msg);
 | |
|     };
 | |
| 
 | |
|     return H_SUCCESS;
 | |
| }
 | |
| 
 | |
| static void tpm_spapr_request_completed(TPMIf *ti, int ret)
 | |
| {
 | |
|     SpaprTpmState *s = VIO_SPAPR_VTPM(ti);
 | |
|     TpmCrq *crq = &s->crq;
 | |
|     uint32_t len;
 | |
|     int rc;
 | |
| 
 | |
|     s->state = SPAPR_VTPM_STATE_COMPLETION;
 | |
| 
 | |
|     /* a max. of be_buffer_size bytes can be transported */
 | |
|     len = MIN(tpm_cmd_get_size(s->buffer), s->be_buffer_size);
 | |
| 
 | |
|     if (runstate_check(RUN_STATE_FINISH_MIGRATE)) {
 | |
|         trace_tpm_spapr_caught_response(len);
 | |
|         /* defer delivery of response until .post_load */
 | |
|         s->numbytes = len;
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     rc = spapr_vio_dma_write(&s->vdev, be32_to_cpu(crq->data),
 | |
|                              s->buffer, len);
 | |
| 
 | |
|     tpm_util_show_buffer(s->buffer, len, "From TPM");
 | |
| 
 | |
|     crq->valid = SPAPR_VTPM_MSG_RESULT;
 | |
|     if (rc == H_SUCCESS) {
 | |
|         crq->msg = SPAPR_VTPM_TPM_COMMAND | SPAPR_VTPM_MSG_RESULT;
 | |
|         crq->len = cpu_to_be16(len);
 | |
|     } else {
 | |
|         error_report("%s: DMA write failure", __func__);
 | |
|         crq->msg = SPAPR_VTPM_VTPM_ERROR;
 | |
|         crq->len = cpu_to_be16(0);
 | |
|         crq->data = cpu_to_be32(SPAPR_VTPM_ERR_COPY_OUT_FAILED);
 | |
|     }
 | |
| 
 | |
|     rc = spapr_tpm_send_crq(&s->vdev, crq);
 | |
|     if (rc) {
 | |
|         error_report("%s: Error sending response", __func__);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int tpm_spapr_do_startup_tpm(SpaprTpmState *s, size_t buffersize)
 | |
| {
 | |
|     return tpm_backend_startup_tpm(s->be_driver, buffersize);
 | |
| }
 | |
| 
 | |
| static const char *tpm_spapr_get_dt_compatible(SpaprVioDevice *dev)
 | |
| {
 | |
|     SpaprTpmState *s = VIO_SPAPR_VTPM(dev);
 | |
| 
 | |
|     switch (s->be_tpm_version) {
 | |
|     case TPM_VERSION_1_2:
 | |
|         return "IBM,vtpm";
 | |
|     case TPM_VERSION_2_0:
 | |
|         return "IBM,vtpm20";
 | |
|     default:
 | |
|         g_assert_not_reached();
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void tpm_spapr_reset(SpaprVioDevice *dev)
 | |
| {
 | |
|     SpaprTpmState *s = VIO_SPAPR_VTPM(dev);
 | |
| 
 | |
|     s->state = SPAPR_VTPM_STATE_NONE;
 | |
|     s->numbytes = 0;
 | |
| 
 | |
|     s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
 | |
| 
 | |
|     s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver),
 | |
|                             TPM_SPAPR_BUFFER_MAX);
 | |
| 
 | |
|     tpm_backend_reset(s->be_driver);
 | |
| 
 | |
|     if (tpm_spapr_do_startup_tpm(s, s->be_buffer_size) < 0) {
 | |
|         exit(1);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static enum TPMVersion tpm_spapr_get_version(TPMIf *ti)
 | |
| {
 | |
|     SpaprTpmState *s = VIO_SPAPR_VTPM(ti);
 | |
| 
 | |
|     if (tpm_backend_had_startup_error(s->be_driver)) {
 | |
|         return TPM_VERSION_UNSPEC;
 | |
|     }
 | |
| 
 | |
|     return tpm_backend_get_tpm_version(s->be_driver);
 | |
| }
 | |
| 
 | |
| /* persistent state handling */
 | |
| 
 | |
| static int tpm_spapr_pre_save(void *opaque)
 | |
| {
 | |
|     SpaprTpmState *s = opaque;
 | |
| 
 | |
|     tpm_backend_finish_sync(s->be_driver);
 | |
|     /*
 | |
|      * we cannot deliver the results to the VM since DMA would touch VM memory
 | |
|      */
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int tpm_spapr_post_load(void *opaque, int version_id)
 | |
| {
 | |
|     SpaprTpmState *s = opaque;
 | |
| 
 | |
|     if (s->numbytes) {
 | |
|         trace_tpm_spapr_post_load();
 | |
|         /* deliver the results to the VM via DMA */
 | |
|         tpm_spapr_request_completed(TPM_IF(s), 0);
 | |
|         s->numbytes = 0;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static const VMStateDescription vmstate_spapr_vtpm = {
 | |
|     .name = "tpm-spapr",
 | |
|     .pre_save = tpm_spapr_pre_save,
 | |
|     .post_load = tpm_spapr_post_load,
 | |
|     .fields = (VMStateField[]) {
 | |
|         VMSTATE_SPAPR_VIO(vdev, SpaprTpmState),
 | |
| 
 | |
|         VMSTATE_UINT8(state, SpaprTpmState),
 | |
|         VMSTATE_UINT32(numbytes, SpaprTpmState),
 | |
|         VMSTATE_VBUFFER_UINT32(buffer, SpaprTpmState, 0, NULL, numbytes),
 | |
|         /* remember DMA address */
 | |
|         VMSTATE_UINT32(crq.data, SpaprTpmState),
 | |
|         VMSTATE_END_OF_LIST(),
 | |
|     }
 | |
| };
 | |
| 
 | |
| static Property tpm_spapr_properties[] = {
 | |
|     DEFINE_SPAPR_PROPERTIES(SpaprTpmState, vdev),
 | |
|     DEFINE_PROP_TPMBE("tpmdev", SpaprTpmState, be_driver),
 | |
|     DEFINE_PROP_END_OF_LIST(),
 | |
| };
 | |
| 
 | |
| static void tpm_spapr_realizefn(SpaprVioDevice *dev, Error **errp)
 | |
| {
 | |
|     SpaprTpmState *s = VIO_SPAPR_VTPM(dev);
 | |
| 
 | |
|     if (!tpm_find()) {
 | |
|         error_setg(errp, "at most one TPM device is permitted");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     dev->crq.SendFunc = tpm_spapr_do_crq;
 | |
| 
 | |
|     if (!s->be_driver) {
 | |
|         error_setg(errp, "'tpmdev' property is required");
 | |
|         return;
 | |
|     }
 | |
|     s->buffer = g_malloc(TPM_SPAPR_BUFFER_MAX);
 | |
| }
 | |
| 
 | |
| static void tpm_spapr_class_init(ObjectClass *klass, void *data)
 | |
| {
 | |
|     DeviceClass *dc = DEVICE_CLASS(klass);
 | |
|     SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass);
 | |
|     TPMIfClass *tc = TPM_IF_CLASS(klass);
 | |
| 
 | |
|     k->realize = tpm_spapr_realizefn;
 | |
|     k->reset = tpm_spapr_reset;
 | |
|     k->dt_name = "vtpm";
 | |
|     k->dt_type = "IBM,vtpm";
 | |
|     k->get_dt_compatible = tpm_spapr_get_dt_compatible;
 | |
|     k->signal_mask = 0x00000001;
 | |
|     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
 | |
|     device_class_set_props(dc, tpm_spapr_properties);
 | |
|     k->rtce_window_size = 0x10000000;
 | |
|     dc->vmsd = &vmstate_spapr_vtpm;
 | |
| 
 | |
|     tc->model = TPM_MODEL_TPM_SPAPR;
 | |
|     tc->get_version = tpm_spapr_get_version;
 | |
|     tc->request_completed = tpm_spapr_request_completed;
 | |
| }
 | |
| 
 | |
| static const TypeInfo tpm_spapr_info = {
 | |
|     .name          = TYPE_TPM_SPAPR,
 | |
|     .parent        = TYPE_VIO_SPAPR_DEVICE,
 | |
|     .instance_size = sizeof(SpaprTpmState),
 | |
|     .class_init    = tpm_spapr_class_init,
 | |
|     .interfaces = (InterfaceInfo[]) {
 | |
|         { TYPE_TPM_IF },
 | |
|         { }
 | |
|     }
 | |
| };
 | |
| 
 | |
| static void tpm_spapr_register_types(void)
 | |
| {
 | |
|     type_register_static(&tpm_spapr_info);
 | |
| }
 | |
| 
 | |
| type_init(tpm_spapr_register_types)
 |