Merge tpm 2018/02/21 v2
-----BEGIN PGP SIGNATURE----- iQEcBAABAgAGBQJalWIkAAoJEHWtZYAqC0IRULsH/1qaCCDcR91PmBENdiYlsqvp XyThlT1VtHhq66CJvH4RVwPhkSFGeMsElXhAk5KoQYha50LvJsqw5+RyXPPeCAk7 1k8nfUC2ELRsLofjee2XkENXA3f1EX0qz+D4ozWjoRt/bI0GtiuW+M1SlGZEiOWN PbcB3XIESVdD1CXL+qlFQK6zHAG4mxVDaosYxACNJzsWIDoTOlyInCoNlkqknxsI PffdbyZE5HZ6ANYkuKgOpXm9a5TmeqZl4MW+ikGG1D2S1QpUnRlTHKS3oDEA7NNb 9Z7jfZxOXCX5PxY04p1Bo0bgksDiQMqFCaIvYyKQ7aMjugvf56qnFJPx1Qx+0WE= =Unmb -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/stefanberger/tags/pull-tpm-2018-02-21-2' into staging Merge tpm 2018/02/21 v2 # gpg: Signature made Tue 27 Feb 2018 13:50:28 GMT # gpg: using RSA key 75AD65802A0B4211 # gpg: Good signature from "Stefan Berger <stefanb@linux.vnet.ibm.com>" # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: B818 B9CA DF90 89C2 D5CE C66B 75AD 6580 2A0B 4211 * remotes/stefanberger/tags/pull-tpm-2018-02-21-2: tests: add test for TPM TIS device tests: Move common TPM test code into tpm-emu.c Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
		
						commit
						5116c331c6
					
				| @ -1639,6 +1639,7 @@ F: include/hw/acpi/tpm.h | ||||
| F: include/sysemu/tpm* | ||||
| F: qapi/tpm.json | ||||
| F: backends/tpm.c | ||||
| F: tests/*tpm* | ||||
| T: git git://github.com/stefanberger/qemu-tpm.git tpm-next | ||||
| 
 | ||||
| Checkpatch | ||||
|  | ||||
							
								
								
									
										101
									
								
								hw/tpm/tpm_tis.c
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								hw/tpm/tpm_tis.c
									
									
									
									
									
								
							| @ -92,107 +92,6 @@ typedef struct TPMState { | ||||
|     } \ | ||||
| } while (0) | ||||
| 
 | ||||
| /* tis registers */ | ||||
| #define TPM_TIS_REG_ACCESS                0x00 | ||||
| #define TPM_TIS_REG_INT_ENABLE            0x08 | ||||
| #define TPM_TIS_REG_INT_VECTOR            0x0c | ||||
| #define TPM_TIS_REG_INT_STATUS            0x10 | ||||
| #define TPM_TIS_REG_INTF_CAPABILITY       0x14 | ||||
| #define TPM_TIS_REG_STS                   0x18 | ||||
| #define TPM_TIS_REG_DATA_FIFO             0x24 | ||||
| #define TPM_TIS_REG_INTERFACE_ID          0x30 | ||||
| #define TPM_TIS_REG_DATA_XFIFO            0x80 | ||||
| #define TPM_TIS_REG_DATA_XFIFO_END        0xbc | ||||
| #define TPM_TIS_REG_DID_VID               0xf00 | ||||
| #define TPM_TIS_REG_RID                   0xf04 | ||||
| 
 | ||||
| /* vendor-specific registers */ | ||||
| #define TPM_TIS_REG_DEBUG                 0xf90 | ||||
| 
 | ||||
| #define TPM_TIS_STS_TPM_FAMILY_MASK         (0x3 << 26)/* TPM 2.0 */ | ||||
| #define TPM_TIS_STS_TPM_FAMILY1_2           (0 << 26)  /* TPM 2.0 */ | ||||
| #define TPM_TIS_STS_TPM_FAMILY2_0           (1 << 26)  /* TPM 2.0 */ | ||||
| #define TPM_TIS_STS_RESET_ESTABLISHMENT_BIT (1 << 25)  /* TPM 2.0 */ | ||||
| #define TPM_TIS_STS_COMMAND_CANCEL          (1 << 24)  /* TPM 2.0 */ | ||||
| 
 | ||||
| #define TPM_TIS_STS_VALID                 (1 << 7) | ||||
| #define TPM_TIS_STS_COMMAND_READY         (1 << 6) | ||||
| #define TPM_TIS_STS_TPM_GO                (1 << 5) | ||||
| #define TPM_TIS_STS_DATA_AVAILABLE        (1 << 4) | ||||
| #define TPM_TIS_STS_EXPECT                (1 << 3) | ||||
| #define TPM_TIS_STS_SELFTEST_DONE         (1 << 2) | ||||
| #define TPM_TIS_STS_RESPONSE_RETRY        (1 << 1) | ||||
| 
 | ||||
| #define TPM_TIS_BURST_COUNT_SHIFT         8 | ||||
| #define TPM_TIS_BURST_COUNT(X) \ | ||||
|     ((X) << TPM_TIS_BURST_COUNT_SHIFT) | ||||
| 
 | ||||
| #define TPM_TIS_ACCESS_TPM_REG_VALID_STS  (1 << 7) | ||||
| #define TPM_TIS_ACCESS_ACTIVE_LOCALITY    (1 << 5) | ||||
| #define TPM_TIS_ACCESS_BEEN_SEIZED        (1 << 4) | ||||
| #define TPM_TIS_ACCESS_SEIZE              (1 << 3) | ||||
| #define TPM_TIS_ACCESS_PENDING_REQUEST    (1 << 2) | ||||
| #define TPM_TIS_ACCESS_REQUEST_USE        (1 << 1) | ||||
| #define TPM_TIS_ACCESS_TPM_ESTABLISHMENT  (1 << 0) | ||||
| 
 | ||||
| #define TPM_TIS_INT_ENABLED               (1 << 31) | ||||
| #define TPM_TIS_INT_DATA_AVAILABLE        (1 << 0) | ||||
| #define TPM_TIS_INT_STS_VALID             (1 << 1) | ||||
| #define TPM_TIS_INT_LOCALITY_CHANGED      (1 << 2) | ||||
| #define TPM_TIS_INT_COMMAND_READY         (1 << 7) | ||||
| 
 | ||||
| #define TPM_TIS_INT_POLARITY_MASK         (3 << 3) | ||||
| #define TPM_TIS_INT_POLARITY_LOW_LEVEL    (1 << 3) | ||||
| 
 | ||||
| #define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \ | ||||
|                                       TPM_TIS_INT_DATA_AVAILABLE   | \ | ||||
|                                       TPM_TIS_INT_STS_VALID | \ | ||||
|                                       TPM_TIS_INT_COMMAND_READY) | ||||
| 
 | ||||
| #define TPM_TIS_CAP_INTERFACE_VERSION1_3 (2 << 28) | ||||
| #define TPM_TIS_CAP_INTERFACE_VERSION1_3_FOR_TPM2_0 (3 << 28) | ||||
| #define TPM_TIS_CAP_DATA_TRANSFER_64B    (3 << 9) | ||||
| #define TPM_TIS_CAP_DATA_TRANSFER_LEGACY (0 << 9) | ||||
| #define TPM_TIS_CAP_BURST_COUNT_DYNAMIC  (0 << 8) | ||||
| #define TPM_TIS_CAP_INTERRUPT_LOW_LEVEL  (1 << 4) /* support is mandatory */ | ||||
| #define TPM_TIS_CAPABILITIES_SUPPORTED1_3 \ | ||||
|     (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \ | ||||
|      TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \ | ||||
|      TPM_TIS_CAP_DATA_TRANSFER_64B | \ | ||||
|      TPM_TIS_CAP_INTERFACE_VERSION1_3 | \ | ||||
|      TPM_TIS_INTERRUPTS_SUPPORTED) | ||||
| 
 | ||||
| #define TPM_TIS_CAPABILITIES_SUPPORTED2_0 \ | ||||
|     (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \ | ||||
|      TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \ | ||||
|      TPM_TIS_CAP_DATA_TRANSFER_64B | \ | ||||
|      TPM_TIS_CAP_INTERFACE_VERSION1_3_FOR_TPM2_0 | \ | ||||
|      TPM_TIS_INTERRUPTS_SUPPORTED) | ||||
| 
 | ||||
| #define TPM_TIS_IFACE_ID_INTERFACE_TIS1_3   (0xf)     /* TPM 2.0 */ | ||||
| #define TPM_TIS_IFACE_ID_INTERFACE_FIFO     (0x0)     /* TPM 2.0 */ | ||||
| #define TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO (0 << 4)  /* TPM 2.0 */ | ||||
| #define TPM_TIS_IFACE_ID_CAP_5_LOCALITIES   (1 << 8)  /* TPM 2.0 */ | ||||
| #define TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED  (1 << 13) /* TPM 2.0 */ | ||||
| #define TPM_TIS_IFACE_ID_INT_SEL_LOCK       (1 << 19) /* TPM 2.0 */ | ||||
| 
 | ||||
| #define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3 \ | ||||
|     (TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 | \ | ||||
|      (~0u << 4)/* all of it is don't care */) | ||||
| 
 | ||||
| /* if backend was a TPM 2.0: */ | ||||
| #define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0 \ | ||||
|     (TPM_TIS_IFACE_ID_INTERFACE_FIFO | \ | ||||
|      TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO | \ | ||||
|      TPM_TIS_IFACE_ID_CAP_5_LOCALITIES | \ | ||||
|      TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED) | ||||
| 
 | ||||
| #define TPM_TIS_TPM_DID       0x0001 | ||||
| #define TPM_TIS_TPM_VID       PCI_VENDOR_ID_IBM | ||||
| #define TPM_TIS_TPM_RID       0x0001 | ||||
| 
 | ||||
| #define TPM_TIS_NO_DATA_BYTE  0xff | ||||
| 
 | ||||
| /* local prototypes */ | ||||
| 
 | ||||
| static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, | ||||
|  | ||||
| @ -23,6 +23,111 @@ | ||||
| 
 | ||||
| #define TPM_TIS_IRQ                 5 | ||||
| 
 | ||||
| #define TPM_TIS_NUM_LOCALITIES      5     /* per spec */ | ||||
| #define TPM_TIS_LOCALITY_SHIFT      12 | ||||
| 
 | ||||
| /* tis registers */ | ||||
| #define TPM_TIS_REG_ACCESS                0x00 | ||||
| #define TPM_TIS_REG_INT_ENABLE            0x08 | ||||
| #define TPM_TIS_REG_INT_VECTOR            0x0c | ||||
| #define TPM_TIS_REG_INT_STATUS            0x10 | ||||
| #define TPM_TIS_REG_INTF_CAPABILITY       0x14 | ||||
| #define TPM_TIS_REG_STS                   0x18 | ||||
| #define TPM_TIS_REG_DATA_FIFO             0x24 | ||||
| #define TPM_TIS_REG_INTERFACE_ID          0x30 | ||||
| #define TPM_TIS_REG_DATA_XFIFO            0x80 | ||||
| #define TPM_TIS_REG_DATA_XFIFO_END        0xbc | ||||
| #define TPM_TIS_REG_DID_VID               0xf00 | ||||
| #define TPM_TIS_REG_RID                   0xf04 | ||||
| 
 | ||||
| /* vendor-specific registers */ | ||||
| #define TPM_TIS_REG_DEBUG                 0xf90 | ||||
| 
 | ||||
| #define TPM_TIS_STS_TPM_FAMILY_MASK         (0x3 << 26)/* TPM 2.0 */ | ||||
| #define TPM_TIS_STS_TPM_FAMILY1_2           (0 << 26)  /* TPM 2.0 */ | ||||
| #define TPM_TIS_STS_TPM_FAMILY2_0           (1 << 26)  /* TPM 2.0 */ | ||||
| #define TPM_TIS_STS_RESET_ESTABLISHMENT_BIT (1 << 25)  /* TPM 2.0 */ | ||||
| #define TPM_TIS_STS_COMMAND_CANCEL          (1 << 24)  /* TPM 2.0 */ | ||||
| 
 | ||||
| #define TPM_TIS_STS_VALID                 (1 << 7) | ||||
| #define TPM_TIS_STS_COMMAND_READY         (1 << 6) | ||||
| #define TPM_TIS_STS_TPM_GO                (1 << 5) | ||||
| #define TPM_TIS_STS_DATA_AVAILABLE        (1 << 4) | ||||
| #define TPM_TIS_STS_EXPECT                (1 << 3) | ||||
| #define TPM_TIS_STS_SELFTEST_DONE         (1 << 2) | ||||
| #define TPM_TIS_STS_RESPONSE_RETRY        (1 << 1) | ||||
| 
 | ||||
| #define TPM_TIS_BURST_COUNT_SHIFT         8 | ||||
| #define TPM_TIS_BURST_COUNT(X) \ | ||||
|     ((X) << TPM_TIS_BURST_COUNT_SHIFT) | ||||
| 
 | ||||
| #define TPM_TIS_ACCESS_TPM_REG_VALID_STS  (1 << 7) | ||||
| #define TPM_TIS_ACCESS_ACTIVE_LOCALITY    (1 << 5) | ||||
| #define TPM_TIS_ACCESS_BEEN_SEIZED        (1 << 4) | ||||
| #define TPM_TIS_ACCESS_SEIZE              (1 << 3) | ||||
| #define TPM_TIS_ACCESS_PENDING_REQUEST    (1 << 2) | ||||
| #define TPM_TIS_ACCESS_REQUEST_USE        (1 << 1) | ||||
| #define TPM_TIS_ACCESS_TPM_ESTABLISHMENT  (1 << 0) | ||||
| 
 | ||||
| #define TPM_TIS_INT_ENABLED               (1 << 31) | ||||
| #define TPM_TIS_INT_DATA_AVAILABLE        (1 << 0) | ||||
| #define TPM_TIS_INT_STS_VALID             (1 << 1) | ||||
| #define TPM_TIS_INT_LOCALITY_CHANGED      (1 << 2) | ||||
| #define TPM_TIS_INT_COMMAND_READY         (1 << 7) | ||||
| 
 | ||||
| #define TPM_TIS_INT_POLARITY_MASK         (3 << 3) | ||||
| #define TPM_TIS_INT_POLARITY_LOW_LEVEL    (1 << 3) | ||||
| 
 | ||||
| #define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \ | ||||
|                                       TPM_TIS_INT_DATA_AVAILABLE   | \ | ||||
|                                       TPM_TIS_INT_STS_VALID | \ | ||||
|                                       TPM_TIS_INT_COMMAND_READY) | ||||
| 
 | ||||
| #define TPM_TIS_CAP_INTERFACE_VERSION1_3 (2 << 28) | ||||
| #define TPM_TIS_CAP_INTERFACE_VERSION1_3_FOR_TPM2_0 (3 << 28) | ||||
| #define TPM_TIS_CAP_DATA_TRANSFER_64B    (3 << 9) | ||||
| #define TPM_TIS_CAP_DATA_TRANSFER_LEGACY (0 << 9) | ||||
| #define TPM_TIS_CAP_BURST_COUNT_DYNAMIC  (0 << 8) | ||||
| #define TPM_TIS_CAP_INTERRUPT_LOW_LEVEL  (1 << 4) /* support is mandatory */ | ||||
| #define TPM_TIS_CAPABILITIES_SUPPORTED1_3 \ | ||||
|     (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \ | ||||
|      TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \ | ||||
|      TPM_TIS_CAP_DATA_TRANSFER_64B | \ | ||||
|      TPM_TIS_CAP_INTERFACE_VERSION1_3 | \ | ||||
|      TPM_TIS_INTERRUPTS_SUPPORTED) | ||||
| 
 | ||||
| #define TPM_TIS_CAPABILITIES_SUPPORTED2_0 \ | ||||
|     (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \ | ||||
|      TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \ | ||||
|      TPM_TIS_CAP_DATA_TRANSFER_64B | \ | ||||
|      TPM_TIS_CAP_INTERFACE_VERSION1_3_FOR_TPM2_0 | \ | ||||
|      TPM_TIS_INTERRUPTS_SUPPORTED) | ||||
| 
 | ||||
| #define TPM_TIS_IFACE_ID_INTERFACE_TIS1_3   (0xf)     /* TPM 2.0 */ | ||||
| #define TPM_TIS_IFACE_ID_INTERFACE_FIFO     (0x0)     /* TPM 2.0 */ | ||||
| #define TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO (0 << 4)  /* TPM 2.0 */ | ||||
| #define TPM_TIS_IFACE_ID_CAP_5_LOCALITIES   (1 << 8)  /* TPM 2.0 */ | ||||
| #define TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED  (1 << 13) /* TPM 2.0 */ | ||||
| #define TPM_TIS_IFACE_ID_INT_SEL_LOCK       (1 << 19) /* TPM 2.0 */ | ||||
| 
 | ||||
| #define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3 \ | ||||
|     (TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 | \ | ||||
|      (~0u << 4)/* all of it is don't care */) | ||||
| 
 | ||||
| /* if backend was a TPM 2.0: */ | ||||
| #define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0 \ | ||||
|     (TPM_TIS_IFACE_ID_INTERFACE_FIFO | \ | ||||
|      TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO | \ | ||||
|      TPM_TIS_IFACE_ID_CAP_5_LOCALITIES | \ | ||||
|      TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED) | ||||
| 
 | ||||
| #define TPM_TIS_TPM_DID       0x0001 | ||||
| #define TPM_TIS_TPM_VID       PCI_VENDOR_ID_IBM | ||||
| #define TPM_TIS_TPM_RID       0x0001 | ||||
| 
 | ||||
| #define TPM_TIS_NO_DATA_BYTE  0xff | ||||
| 
 | ||||
| 
 | ||||
| REG32(CRB_LOC_STATE, 0x00) | ||||
|   FIELD(CRB_LOC_STATE, tpmEstablished, 0, 1) | ||||
|   FIELD(CRB_LOC_STATE, locAssigned, 1, 1) | ||||
|  | ||||
| @ -287,6 +287,7 @@ ifeq ($(CONFIG_VHOST_USER_NET_TEST_i386),) | ||||
| check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF) | ||||
| endif | ||||
| check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-test$(EXESUF) | ||||
| check-qtest-i386-$(CONFIG_TPM) += tests/tpm-tis-test$(EXESUF) | ||||
| check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) | ||||
| check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) | ||||
| check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF) | ||||
| @ -714,7 +715,8 @@ tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \ | ||||
| tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) | ||||
| tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ | ||||
|         tests/io-channel-helpers.o $(test-io-obj-y) | ||||
| tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o $(test-io-obj-y) | ||||
| tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o tests/tpm-emu.o $(test-io-obj-y) | ||||
| tests/tpm-tis-test$(EXESUF): tests/tpm-tis-test.o tests/tpm-emu.o $(test-io-obj-y) | ||||
| tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ | ||||
|         tests/io-channel-helpers.o $(test-io-obj-y) | ||||
| tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \ | ||||
|  | ||||
| @ -14,177 +14,9 @@ | ||||
| #include <glib/gstdio.h> | ||||
| 
 | ||||
| #include "hw/acpi/tpm.h" | ||||
| #include "hw/tpm/tpm_ioctl.h" | ||||
| #include "io/channel-socket.h" | ||||
| #include "libqtest.h" | ||||
| #include "qapi/error.h" | ||||
| 
 | ||||
| #define TPM_RC_FAILURE 0x101 | ||||
| #define TPM2_ST_NO_SESSIONS 0x8001 | ||||
| 
 | ||||
| struct tpm_hdr { | ||||
|     uint16_t tag; | ||||
|     uint32_t len; | ||||
|     uint32_t code; /*ordinal/error */ | ||||
|     char buffer[]; | ||||
| } QEMU_PACKED; | ||||
| 
 | ||||
| typedef struct TestState { | ||||
|     CompatGMutex data_mutex; | ||||
|     CompatGCond data_cond; | ||||
|     SocketAddress *addr; | ||||
|     QIOChannel *tpm_ioc; | ||||
|     GThread *emu_tpm_thread; | ||||
|     struct tpm_hdr *tpm_msg; | ||||
| } TestState; | ||||
| 
 | ||||
| static void test_wait_cond(TestState *s) | ||||
| { | ||||
|     gint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; | ||||
| 
 | ||||
|     g_mutex_lock(&s->data_mutex); | ||||
|     if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { | ||||
|         g_assert_not_reached(); | ||||
|     } | ||||
|     g_mutex_unlock(&s->data_mutex); | ||||
| } | ||||
| 
 | ||||
| static void *emu_tpm_thread(void *data) | ||||
| { | ||||
|     TestState *s = data; | ||||
|     QIOChannel *ioc = s->tpm_ioc; | ||||
| 
 | ||||
|     s->tpm_msg = g_new(struct tpm_hdr, 1); | ||||
|     while (true) { | ||||
|         int minhlen = sizeof(s->tpm_msg->tag) + sizeof(s->tpm_msg->len); | ||||
| 
 | ||||
|         if (!qio_channel_read(ioc, (char *)s->tpm_msg, minhlen, &error_abort)) { | ||||
|             break; | ||||
|         } | ||||
|         s->tpm_msg->tag = be16_to_cpu(s->tpm_msg->tag); | ||||
|         s->tpm_msg->len = be32_to_cpu(s->tpm_msg->len); | ||||
|         g_assert_cmpint(s->tpm_msg->len, >=, minhlen); | ||||
|         g_assert_cmpint(s->tpm_msg->tag, ==, TPM2_ST_NO_SESSIONS); | ||||
| 
 | ||||
|         s->tpm_msg = g_realloc(s->tpm_msg, s->tpm_msg->len); | ||||
|         qio_channel_read(ioc, (char *)&s->tpm_msg->code, | ||||
|                          s->tpm_msg->len - minhlen, &error_abort); | ||||
|         s->tpm_msg->code = be32_to_cpu(s->tpm_msg->code); | ||||
| 
 | ||||
|         /* reply error */ | ||||
|         s->tpm_msg->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); | ||||
|         s->tpm_msg->len = cpu_to_be32(sizeof(struct tpm_hdr)); | ||||
|         s->tpm_msg->code = cpu_to_be32(TPM_RC_FAILURE); | ||||
|         qio_channel_write(ioc, (char *)s->tpm_msg, be32_to_cpu(s->tpm_msg->len), | ||||
|                           &error_abort); | ||||
|     } | ||||
| 
 | ||||
|     g_free(s->tpm_msg); | ||||
|     s->tpm_msg = NULL; | ||||
|     object_unref(OBJECT(s->tpm_ioc)); | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| static void *emu_ctrl_thread(void *data) | ||||
| { | ||||
|     TestState *s = data; | ||||
|     QIOChannelSocket *lioc = qio_channel_socket_new(); | ||||
|     QIOChannel *ioc; | ||||
| 
 | ||||
|     qio_channel_socket_listen_sync(lioc, s->addr, &error_abort); | ||||
|     g_cond_signal(&s->data_cond); | ||||
| 
 | ||||
|     qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); | ||||
|     ioc = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); | ||||
|     g_assert(ioc); | ||||
| 
 | ||||
|     { | ||||
|         uint32_t cmd = 0; | ||||
|         struct iovec iov = { .iov_base = &cmd, .iov_len = sizeof(cmd) }; | ||||
|         int *pfd = NULL; | ||||
|         size_t nfd = 0; | ||||
| 
 | ||||
|         qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, &error_abort); | ||||
|         cmd = be32_to_cpu(cmd); | ||||
|         g_assert_cmpint(cmd, ==, CMD_SET_DATAFD); | ||||
|         g_assert_cmpint(nfd, ==, 1); | ||||
|         s->tpm_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(*pfd, &error_abort)); | ||||
|         g_free(pfd); | ||||
| 
 | ||||
|         cmd = 0; | ||||
|         qio_channel_write(ioc, (char *)&cmd, sizeof(cmd), &error_abort); | ||||
| 
 | ||||
|         s->emu_tpm_thread = g_thread_new(NULL, emu_tpm_thread, s); | ||||
|     } | ||||
| 
 | ||||
|     while (true) { | ||||
|         uint32_t cmd; | ||||
|         ssize_t ret; | ||||
| 
 | ||||
|         ret = qio_channel_read(ioc, (char *)&cmd, sizeof(cmd), NULL); | ||||
|         if (ret <= 0) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         cmd = be32_to_cpu(cmd); | ||||
|         switch (cmd) { | ||||
|         case CMD_GET_CAPABILITY: { | ||||
|             ptm_cap cap = cpu_to_be64(0x3fff); | ||||
|             qio_channel_write(ioc, (char *)&cap, sizeof(cap), &error_abort); | ||||
|             break; | ||||
|         } | ||||
|         case CMD_INIT: { | ||||
|             ptm_init init; | ||||
|             qio_channel_read(ioc, (char *)&init.u.req, sizeof(init.u.req), | ||||
|                               &error_abort); | ||||
|             init.u.resp.tpm_result = 0; | ||||
|             qio_channel_write(ioc, (char *)&init.u.resp, sizeof(init.u.resp), | ||||
|                               &error_abort); | ||||
|             break; | ||||
|         } | ||||
|         case CMD_SHUTDOWN: { | ||||
|             ptm_res res = 0; | ||||
|             qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); | ||||
|             qio_channel_close(s->tpm_ioc, &error_abort); | ||||
|             g_thread_join(s->emu_tpm_thread); | ||||
|             break; | ||||
|         } | ||||
|         case CMD_STOP: { | ||||
|             ptm_res res = 0; | ||||
|             qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); | ||||
|             break; | ||||
|         } | ||||
|         case CMD_SET_BUFFERSIZE: { | ||||
|             ptm_setbuffersize sbs; | ||||
|             qio_channel_read(ioc, (char *)&sbs.u.req, sizeof(sbs.u.req), | ||||
|                              &error_abort); | ||||
|             sbs.u.resp.buffersize = sbs.u.req.buffersize ?: cpu_to_be32(4096); | ||||
|             sbs.u.resp.tpm_result = 0; | ||||
|             sbs.u.resp.minsize = cpu_to_be32(128); | ||||
|             sbs.u.resp.maxsize = cpu_to_be32(4096); | ||||
|             qio_channel_write(ioc, (char *)&sbs.u.resp, sizeof(sbs.u.resp), | ||||
|                               &error_abort); | ||||
|             break; | ||||
|         } | ||||
|         case CMD_SET_LOCALITY: { | ||||
|             ptm_loc loc; | ||||
|             /* Note: this time it's not u.req / u.resp... */ | ||||
|             qio_channel_read(ioc, (char *)&loc, sizeof(loc), &error_abort); | ||||
|             g_assert_cmpint(loc.u.req.loc, ==, 0); | ||||
|             loc.u.resp.tpm_result = 0; | ||||
|             qio_channel_write(ioc, (char *)&loc, sizeof(loc), &error_abort); | ||||
|             break; | ||||
|         } | ||||
|         default: | ||||
|             g_debug("unimplemented %u", cmd); | ||||
|             g_assert_not_reached(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     object_unref(OBJECT(ioc)); | ||||
|     object_unref(OBJECT(lioc)); | ||||
|     return NULL; | ||||
| } | ||||
| #include "tpm-emu.h" | ||||
| 
 | ||||
| #define TPM_CMD "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00" | ||||
| 
 | ||||
| @ -250,8 +82,8 @@ int main(int argc, char **argv) | ||||
|     g_mutex_init(&test.data_mutex); | ||||
|     g_cond_init(&test.data_cond); | ||||
| 
 | ||||
|     thread = g_thread_new(NULL, emu_ctrl_thread, &test); | ||||
|     test_wait_cond(&test); | ||||
|     thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test); | ||||
|     tpm_emu_test_wait_cond(&test); | ||||
| 
 | ||||
|     args = g_strdup_printf( | ||||
|         "-chardev socket,id=chr,path=%s " | ||||
|  | ||||
							
								
								
									
										174
									
								
								tests/tpm-emu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								tests/tpm-emu.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,174 @@ | ||||
| /*
 | ||||
|  * Minimal TPM emulator for TPM test cases | ||||
|  * | ||||
|  * Copyright (c) 2018 Red Hat, Inc. | ||||
|  * | ||||
|  * Authors: | ||||
|  *   Marc-André Lureau <marcandre.lureau@redhat.com> | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  */ | ||||
| 
 | ||||
| #include "qemu/osdep.h" | ||||
| #include <glib/gstdio.h> | ||||
| 
 | ||||
| #include "hw/tpm/tpm_ioctl.h" | ||||
| #include "io/channel-socket.h" | ||||
| #include "qapi/error.h" | ||||
| #include "tpm-emu.h" | ||||
| 
 | ||||
| void tpm_emu_test_wait_cond(TestState *s) | ||||
| { | ||||
|     gint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; | ||||
| 
 | ||||
|     g_mutex_lock(&s->data_mutex); | ||||
|     if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { | ||||
|         g_assert_not_reached(); | ||||
|     } | ||||
|     g_mutex_unlock(&s->data_mutex); | ||||
| } | ||||
| 
 | ||||
| static void *tpm_emu_tpm_thread(void *data) | ||||
| { | ||||
|     TestState *s = data; | ||||
|     QIOChannel *ioc = s->tpm_ioc; | ||||
| 
 | ||||
|     s->tpm_msg = g_new(struct tpm_hdr, 1); | ||||
|     while (true) { | ||||
|         int minhlen = sizeof(s->tpm_msg->tag) + sizeof(s->tpm_msg->len); | ||||
| 
 | ||||
|         if (!qio_channel_read(ioc, (char *)s->tpm_msg, minhlen, &error_abort)) { | ||||
|             break; | ||||
|         } | ||||
|         s->tpm_msg->tag = be16_to_cpu(s->tpm_msg->tag); | ||||
|         s->tpm_msg->len = be32_to_cpu(s->tpm_msg->len); | ||||
|         g_assert_cmpint(s->tpm_msg->len, >=, minhlen); | ||||
|         g_assert_cmpint(s->tpm_msg->tag, ==, TPM2_ST_NO_SESSIONS); | ||||
| 
 | ||||
|         s->tpm_msg = g_realloc(s->tpm_msg, s->tpm_msg->len); | ||||
|         qio_channel_read(ioc, (char *)&s->tpm_msg->code, | ||||
|                          s->tpm_msg->len - minhlen, &error_abort); | ||||
|         s->tpm_msg->code = be32_to_cpu(s->tpm_msg->code); | ||||
| 
 | ||||
|         /* reply error */ | ||||
|         s->tpm_msg->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); | ||||
|         s->tpm_msg->len = cpu_to_be32(sizeof(struct tpm_hdr)); | ||||
|         s->tpm_msg->code = cpu_to_be32(TPM_RC_FAILURE); | ||||
|         qio_channel_write(ioc, (char *)s->tpm_msg, be32_to_cpu(s->tpm_msg->len), | ||||
|                           &error_abort); | ||||
|     } | ||||
| 
 | ||||
|     g_free(s->tpm_msg); | ||||
|     s->tpm_msg = NULL; | ||||
|     object_unref(OBJECT(s->tpm_ioc)); | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| void *tpm_emu_ctrl_thread(void *data) | ||||
| { | ||||
|     TestState *s = data; | ||||
|     QIOChannelSocket *lioc = qio_channel_socket_new(); | ||||
|     QIOChannel *ioc; | ||||
| 
 | ||||
|     qio_channel_socket_listen_sync(lioc, s->addr, &error_abort); | ||||
|     g_cond_signal(&s->data_cond); | ||||
| 
 | ||||
|     qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); | ||||
|     ioc = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); | ||||
|     g_assert(ioc); | ||||
| 
 | ||||
|     { | ||||
|         uint32_t cmd = 0; | ||||
|         struct iovec iov = { .iov_base = &cmd, .iov_len = sizeof(cmd) }; | ||||
|         int *pfd = NULL; | ||||
|         size_t nfd = 0; | ||||
| 
 | ||||
|         qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, &error_abort); | ||||
|         cmd = be32_to_cpu(cmd); | ||||
|         g_assert_cmpint(cmd, ==, CMD_SET_DATAFD); | ||||
|         g_assert_cmpint(nfd, ==, 1); | ||||
|         s->tpm_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(*pfd, &error_abort)); | ||||
|         g_free(pfd); | ||||
| 
 | ||||
|         cmd = 0; | ||||
|         qio_channel_write(ioc, (char *)&cmd, sizeof(cmd), &error_abort); | ||||
| 
 | ||||
|         s->emu_tpm_thread = g_thread_new(NULL, tpm_emu_tpm_thread, s); | ||||
|     } | ||||
| 
 | ||||
|     while (true) { | ||||
|         uint32_t cmd; | ||||
|         ssize_t ret; | ||||
| 
 | ||||
|         ret = qio_channel_read(ioc, (char *)&cmd, sizeof(cmd), NULL); | ||||
|         if (ret <= 0) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         cmd = be32_to_cpu(cmd); | ||||
|         switch (cmd) { | ||||
|         case CMD_GET_CAPABILITY: { | ||||
|             ptm_cap cap = cpu_to_be64(0x3fff); | ||||
|             qio_channel_write(ioc, (char *)&cap, sizeof(cap), &error_abort); | ||||
|             break; | ||||
|         } | ||||
|         case CMD_INIT: { | ||||
|             ptm_init init; | ||||
|             qio_channel_read(ioc, (char *)&init.u.req, sizeof(init.u.req), | ||||
|                               &error_abort); | ||||
|             init.u.resp.tpm_result = 0; | ||||
|             qio_channel_write(ioc, (char *)&init.u.resp, sizeof(init.u.resp), | ||||
|                               &error_abort); | ||||
|             break; | ||||
|         } | ||||
|         case CMD_SHUTDOWN: { | ||||
|             ptm_res res = 0; | ||||
|             qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); | ||||
|             qio_channel_close(s->tpm_ioc, &error_abort); | ||||
|             g_thread_join(s->emu_tpm_thread); | ||||
|             break; | ||||
|         } | ||||
|         case CMD_STOP: { | ||||
|             ptm_res res = 0; | ||||
|             qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); | ||||
|             break; | ||||
|         } | ||||
|         case CMD_SET_BUFFERSIZE: { | ||||
|             ptm_setbuffersize sbs; | ||||
|             qio_channel_read(ioc, (char *)&sbs.u.req, sizeof(sbs.u.req), | ||||
|                              &error_abort); | ||||
|             sbs.u.resp.buffersize = sbs.u.req.buffersize ?: cpu_to_be32(4096); | ||||
|             sbs.u.resp.tpm_result = 0; | ||||
|             sbs.u.resp.minsize = cpu_to_be32(128); | ||||
|             sbs.u.resp.maxsize = cpu_to_be32(4096); | ||||
|             qio_channel_write(ioc, (char *)&sbs.u.resp, sizeof(sbs.u.resp), | ||||
|                               &error_abort); | ||||
|             break; | ||||
|         } | ||||
|         case CMD_SET_LOCALITY: { | ||||
|             ptm_loc loc; | ||||
|             /* Note: this time it's not u.req / u.resp... */ | ||||
|             qio_channel_read(ioc, (char *)&loc, sizeof(loc), &error_abort); | ||||
|             g_assert_cmpint(loc.u.req.loc, ==, 0); | ||||
|             loc.u.resp.tpm_result = 0; | ||||
|             qio_channel_write(ioc, (char *)&loc, sizeof(loc), &error_abort); | ||||
|             break; | ||||
|         } | ||||
|         case CMD_GET_TPMESTABLISHED: { | ||||
|             ptm_est est = { | ||||
|                 .u.resp.bit = 0, | ||||
|             }; | ||||
|             qio_channel_write(ioc, (char *)&est, sizeof(est), &error_abort); | ||||
|             break; | ||||
|         } | ||||
|         default: | ||||
|             g_debug("unimplemented %u", cmd); | ||||
|             g_assert_not_reached(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     object_unref(OBJECT(ioc)); | ||||
|     object_unref(OBJECT(lioc)); | ||||
|     return NULL; | ||||
| } | ||||
							
								
								
									
										38
									
								
								tests/tpm-emu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								tests/tpm-emu.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| /*
 | ||||
|  * Minimal TPM emulator for TPM test cases | ||||
|  * | ||||
|  * Copyright (c) 2018 Red Hat, Inc. | ||||
|  * | ||||
|  * Authors: | ||||
|  *   Marc-André Lureau <marcandre.lureau@redhat.com> | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef TESTS_TPM_EMU_H | ||||
| #define TESTS_TPM_EMU_H | ||||
| 
 | ||||
| #define TPM_RC_FAILURE 0x101 | ||||
| #define TPM2_ST_NO_SESSIONS 0x8001 | ||||
| 
 | ||||
| struct tpm_hdr { | ||||
|     uint16_t tag; | ||||
|     uint32_t len; | ||||
|     uint32_t code; /*ordinal/error */ | ||||
|     char buffer[]; | ||||
| } QEMU_PACKED; | ||||
| 
 | ||||
| typedef struct TestState { | ||||
|     CompatGMutex data_mutex; | ||||
|     CompatGCond data_cond; | ||||
|     SocketAddress *addr; | ||||
|     QIOChannel *tpm_ioc; | ||||
|     GThread *emu_tpm_thread; | ||||
|     struct tpm_hdr *tpm_msg; | ||||
| } TestState; | ||||
| 
 | ||||
| void tpm_emu_test_wait_cond(TestState *s); | ||||
| void *tpm_emu_ctrl_thread(void *data); | ||||
| 
 | ||||
| #endif /* TEST_TPM_EMU_H */ | ||||
							
								
								
									
										486
									
								
								tests/tpm-tis-test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										486
									
								
								tests/tpm-tis-test.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,486 @@ | ||||
| /*
 | ||||
|  * QTest testcase for TPM TIS | ||||
|  * | ||||
|  * Copyright (c) 2018 Red Hat, Inc. | ||||
|  * Copyright (c) 2018 IBM Corporation | ||||
|  * | ||||
|  * Authors: | ||||
|  *   Marc-André Lureau <marcandre.lureau@redhat.com> | ||||
|  *   Stefan Berger <stefanb@linux.vnet.ibm.com> | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  */ | ||||
| 
 | ||||
| #include "qemu/osdep.h" | ||||
| #include <glib/gstdio.h> | ||||
| 
 | ||||
| #include "hw/acpi/tpm.h" | ||||
| #include "io/channel-socket.h" | ||||
| #include "libqtest.h" | ||||
| #include "tpm-emu.h" | ||||
| 
 | ||||
| #define TIS_REG(LOCTY, REG) \ | ||||
|     (TPM_TIS_ADDR_BASE + ((LOCTY) << 12) + REG) | ||||
| 
 | ||||
| #define DEBUG_TIS_TEST 0 | ||||
| 
 | ||||
| #define DPRINTF(fmt, ...) do { \ | ||||
|     if (DEBUG_TIS_TEST) { \ | ||||
|         printf(fmt, ## __VA_ARGS__); \ | ||||
|     } \ | ||||
| } while (0) | ||||
| 
 | ||||
| #define DPRINTF_ACCESS \ | ||||
|     DPRINTF("%s: %d: locty=%d l=%d access=0x%02x pending_request_flag=0x%x\n", \ | ||||
|             __func__, __LINE__, locty, l, access, pending_request_flag) | ||||
| 
 | ||||
| #define DPRINTF_STS \ | ||||
|     DPRINTF("%s: %d: sts = 0x%08x\n", __func__, __LINE__, sts) | ||||
| 
 | ||||
| static const uint8_t TPM_CMD[12] = | ||||
|     "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"; | ||||
| 
 | ||||
| static void tpm_tis_test_check_localities(const void *data) | ||||
| { | ||||
|     uint8_t locty; | ||||
|     uint8_t access; | ||||
|     uint32_t ifaceid; | ||||
|     uint32_t capability; | ||||
|     uint32_t didvid; | ||||
|     uint32_t rid; | ||||
| 
 | ||||
|     for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES; locty++) { | ||||
|         access = readb(TIS_REG(0, TPM_TIS_REG_ACCESS)); | ||||
|         g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                     TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|         capability = readl(TIS_REG(locty, TPM_TIS_REG_INTF_CAPABILITY)); | ||||
|         g_assert_cmpint(capability, ==, TPM_TIS_CAPABILITIES_SUPPORTED2_0); | ||||
| 
 | ||||
|         ifaceid = readl(TIS_REG(locty, TPM_TIS_REG_INTERFACE_ID)); | ||||
|         g_assert_cmpint(ifaceid, ==, TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0); | ||||
| 
 | ||||
|         didvid = readl(TIS_REG(locty, TPM_TIS_REG_DID_VID)); | ||||
|         g_assert_cmpint(didvid, !=, 0); | ||||
|         g_assert_cmpint(didvid, !=, 0xffffffff); | ||||
| 
 | ||||
|         rid = readl(TIS_REG(locty, TPM_TIS_REG_RID)); | ||||
|         g_assert_cmpint(rid, !=, 0); | ||||
|         g_assert_cmpint(rid, !=, 0xffffffff); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void tpm_tis_test_check_access_reg(const void *data) | ||||
| { | ||||
|     uint8_t locty; | ||||
|     uint8_t access; | ||||
| 
 | ||||
|     /* do not test locality 4 (hw only) */ | ||||
|     for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES - 1; locty++) { | ||||
|         access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); | ||||
|         g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                     TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|         /* request use of locality */ | ||||
|         writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); | ||||
| 
 | ||||
|         access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); | ||||
|         g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                     TPM_TIS_ACCESS_ACTIVE_LOCALITY | | ||||
|                                     TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|         /* release access */ | ||||
|         writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), | ||||
|                TPM_TIS_ACCESS_ACTIVE_LOCALITY); | ||||
|         access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); | ||||
|         g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                     TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Test case for seizing access by a higher number locality | ||||
|  */ | ||||
| static void tpm_tis_test_check_access_reg_seize(const void *data) | ||||
| { | ||||
|     int locty, l; | ||||
|     uint8_t access; | ||||
|     uint8_t pending_request_flag; | ||||
| 
 | ||||
|     /* do not test locality 4 (hw only) */ | ||||
|     for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES - 1; locty++) { | ||||
|         pending_request_flag = 0; | ||||
| 
 | ||||
|         access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); | ||||
|         g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                     TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|         /* request use of locality */ | ||||
|         writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); | ||||
|         access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); | ||||
|         g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                     TPM_TIS_ACCESS_ACTIVE_LOCALITY | | ||||
|                                     TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|         /* lower localities cannot seize access */ | ||||
|         for (l = 0; l < locty; l++) { | ||||
|             /* lower locality is not active */ | ||||
|             access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); | ||||
|             DPRINTF_ACCESS; | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         pending_request_flag | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|             /* try to request use from 'l' */ | ||||
|             writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); | ||||
| 
 | ||||
|             /* requesting use from 'l' was not possible;
 | ||||
|                we must see REQUEST_USE and possibly PENDING_REQUEST */ | ||||
|             access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); | ||||
|             DPRINTF_ACCESS; | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         TPM_TIS_ACCESS_REQUEST_USE | | ||||
|                                         pending_request_flag | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|             /* locality 'locty' must be unchanged;
 | ||||
|                we must see PENDING_REQUEST */ | ||||
|             access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         TPM_TIS_ACCESS_ACTIVE_LOCALITY | | ||||
|                                         TPM_TIS_ACCESS_PENDING_REQUEST | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|             /* try to seize from 'l' */ | ||||
|             writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_SEIZE); | ||||
|             /* seize from 'l' was not possible */ | ||||
|             access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); | ||||
|             DPRINTF_ACCESS; | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         TPM_TIS_ACCESS_REQUEST_USE | | ||||
|                                         pending_request_flag | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|             /* locality 'locty' must be unchanged */ | ||||
|             access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         TPM_TIS_ACCESS_ACTIVE_LOCALITY | | ||||
|                                         TPM_TIS_ACCESS_PENDING_REQUEST | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|             /* on the next loop we will have a PENDING_REQUEST flag
 | ||||
|                set for locality 'l' */ | ||||
|             pending_request_flag = TPM_TIS_ACCESS_PENDING_REQUEST; | ||||
|         } | ||||
| 
 | ||||
|         /* higher localities can 'seize' access but not 'request use';
 | ||||
|            note: this will activate first l+1, then l+2 etc. */ | ||||
|         for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES - 1; l++) { | ||||
|             /* try to 'request use' from 'l' */ | ||||
|             writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); | ||||
| 
 | ||||
|             /* requesting use from 'l' was not possible; we should see
 | ||||
|                REQUEST_USE and may see PENDING_REQUEST */ | ||||
|             access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); | ||||
|             DPRINTF_ACCESS; | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         TPM_TIS_ACCESS_REQUEST_USE | | ||||
|                                         pending_request_flag | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|             /* locality 'l-1' must be unchanged; we should always
 | ||||
|                see PENDING_REQUEST from 'l' requesting access */ | ||||
|             access = readb(TIS_REG(l - 1, TPM_TIS_REG_ACCESS)); | ||||
|             DPRINTF_ACCESS; | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         TPM_TIS_ACCESS_ACTIVE_LOCALITY | | ||||
|                                         TPM_TIS_ACCESS_PENDING_REQUEST | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|             /* try to seize from 'l' */ | ||||
|             writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_SEIZE); | ||||
| 
 | ||||
|             /* seize from 'l' was possible */ | ||||
|             access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); | ||||
|             DPRINTF_ACCESS; | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         TPM_TIS_ACCESS_ACTIVE_LOCALITY | | ||||
|                                         pending_request_flag | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|             /* l - 1 should show that it has BEEN_SEIZED */ | ||||
|             access = readb(TIS_REG(l - 1, TPM_TIS_REG_ACCESS)); | ||||
|             DPRINTF_ACCESS; | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         TPM_TIS_ACCESS_BEEN_SEIZED | | ||||
|                                         pending_request_flag | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|             /* clear the BEEN_SEIZED flag and make sure it's gone */ | ||||
|             writeb(TIS_REG(l - 1, TPM_TIS_REG_ACCESS), | ||||
|                    TPM_TIS_ACCESS_BEEN_SEIZED); | ||||
| 
 | ||||
|             access = readb(TIS_REG(l - 1, TPM_TIS_REG_ACCESS)); | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         pending_request_flag | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
|         } | ||||
| 
 | ||||
|         /* PENDING_REQUEST will not be set if locty = 0 since all localities
 | ||||
|            were active; in case of locty = 1, locality 0 will be active | ||||
|            but no PENDING_REQUEST anywhere */ | ||||
|         if (locty <= 1) { | ||||
|             pending_request_flag = 0; | ||||
|         } | ||||
| 
 | ||||
|         /* release access from l - 1; this activates locty - 1 */ | ||||
|         l--; | ||||
| 
 | ||||
|         access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); | ||||
|         DPRINTF_ACCESS; | ||||
| 
 | ||||
|         DPRINTF("%s: %d: relinquishing control on l = %d\n", | ||||
|                 __func__, __LINE__, l); | ||||
|         writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), | ||||
|                TPM_TIS_ACCESS_ACTIVE_LOCALITY); | ||||
| 
 | ||||
|         access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); | ||||
|         DPRINTF_ACCESS; | ||||
|         g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                     pending_request_flag | | ||||
|                                     TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|         for (l = locty - 1; l >= 0; l--) { | ||||
|             access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); | ||||
|             DPRINTF_ACCESS; | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         TPM_TIS_ACCESS_ACTIVE_LOCALITY | | ||||
|                                         pending_request_flag | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|             /* release this locality */ | ||||
|             writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), | ||||
|                    TPM_TIS_ACCESS_ACTIVE_LOCALITY); | ||||
| 
 | ||||
|             if (l == 1) { | ||||
|                 pending_request_flag = 0; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /* no locality may be active now */ | ||||
|         for (l = 0; l < TPM_TIS_NUM_LOCALITIES - 1; l++) { | ||||
|             access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); | ||||
|             DPRINTF_ACCESS; | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Test case for getting access when higher number locality relinquishes access | ||||
|  */ | ||||
| static void tpm_tis_test_check_access_reg_release(const void *data) | ||||
| { | ||||
|     int locty, l; | ||||
|     uint8_t access; | ||||
|     uint8_t pending_request_flag; | ||||
| 
 | ||||
|     /* do not test locality 4 (hw only) */ | ||||
|     for (locty = TPM_TIS_NUM_LOCALITIES - 2; locty >= 0; locty--) { | ||||
|         pending_request_flag = 0; | ||||
| 
 | ||||
|         access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); | ||||
|         g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                     TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|         /* request use of locality */ | ||||
|         writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); | ||||
|         access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); | ||||
|         g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                     TPM_TIS_ACCESS_ACTIVE_LOCALITY | | ||||
|                                     TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|         /* request use of all other localities */ | ||||
|         for (l = 0; l < TPM_TIS_NUM_LOCALITIES - 1; l++) { | ||||
|             if (l == locty) { | ||||
|                 continue; | ||||
|             } | ||||
|             /* request use of locality 'l' -- we MUST see REQUEST USE and
 | ||||
|                may see PENDING_REQUEST */ | ||||
|             writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); | ||||
|             access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); | ||||
|             DPRINTF_ACCESS; | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         TPM_TIS_ACCESS_REQUEST_USE | | ||||
|                                         pending_request_flag | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
|             pending_request_flag = TPM_TIS_ACCESS_PENDING_REQUEST; | ||||
|         } | ||||
|         /* release locality 'locty' */ | ||||
|         writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), | ||||
|                TPM_TIS_ACCESS_ACTIVE_LOCALITY); | ||||
|         /* highest locality should now be active; release it and make sure the
 | ||||
|            next higest locality is active afterwards */ | ||||
|         for (l = TPM_TIS_NUM_LOCALITIES - 2; l >= 0; l--) { | ||||
|             if (l == locty) { | ||||
|                 continue; | ||||
|             } | ||||
|             /* 'l' should be active now */ | ||||
|             access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); | ||||
|             DPRINTF_ACCESS; | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         TPM_TIS_ACCESS_ACTIVE_LOCALITY | | ||||
|                                         pending_request_flag | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
|             /* 'l' relinquishes access */ | ||||
|             writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), | ||||
|                    TPM_TIS_ACCESS_ACTIVE_LOCALITY); | ||||
|             access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); | ||||
|             DPRINTF_ACCESS; | ||||
|             if (l == 1 || (locty <= 1 && l == 2)) { | ||||
|                 pending_request_flag = 0; | ||||
|             } | ||||
|             g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                         pending_request_flag | | ||||
|                                         TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Test case for transmitting packets | ||||
|  */ | ||||
| static void tpm_tis_test_check_transmit(const void *data) | ||||
| { | ||||
|     const TestState *s = data; | ||||
|     uint8_t access; | ||||
|     uint32_t sts; | ||||
|     uint16_t bcount; | ||||
|     size_t i; | ||||
| 
 | ||||
|     /* request use of locality 0 */ | ||||
|     writeb(TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); | ||||
|     access = readb(TIS_REG(0, TPM_TIS_REG_ACCESS)); | ||||
|     g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | | ||||
|                                 TPM_TIS_ACCESS_ACTIVE_LOCALITY | | ||||
|                                 TPM_TIS_ACCESS_TPM_ESTABLISHMENT); | ||||
| 
 | ||||
|     sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); | ||||
|     DPRINTF_STS; | ||||
| 
 | ||||
|     g_assert_cmpint(sts & 0xff, ==, 0); | ||||
|     g_assert_cmpint(sts & TPM_TIS_STS_TPM_FAMILY_MASK, ==, | ||||
|                     TPM_TIS_STS_TPM_FAMILY2_0); | ||||
| 
 | ||||
|     bcount = (sts >> 8) & 0xffff; | ||||
|     g_assert_cmpint(bcount, >=, 128); | ||||
| 
 | ||||
|     writel(TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_COMMAND_READY); | ||||
|     sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); | ||||
|     DPRINTF_STS; | ||||
|     g_assert_cmpint(sts & 0xff, ==, TPM_TIS_STS_COMMAND_READY); | ||||
| 
 | ||||
|     /* transmit command */ | ||||
|     for (i = 0; i < sizeof(TPM_CMD); i++) { | ||||
|         writeb(TIS_REG(0, TPM_TIS_REG_DATA_FIFO), TPM_CMD[i]); | ||||
|         sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); | ||||
|         DPRINTF_STS; | ||||
|         if (i < sizeof(TPM_CMD) - 1) { | ||||
|             g_assert_cmpint(sts & 0xff, ==, | ||||
|                             TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); | ||||
|         } else { | ||||
|             g_assert_cmpint(sts & 0xff, ==, TPM_TIS_STS_VALID); | ||||
|         } | ||||
|         g_assert_cmpint((sts >> 8) & 0xffff, ==, --bcount); | ||||
|     } | ||||
|     /* start processing */ | ||||
|     writeb(TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_TPM_GO); | ||||
| 
 | ||||
|     uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND; | ||||
|     do { | ||||
|         sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); | ||||
|         if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) { | ||||
|             break; | ||||
|         } | ||||
|     } while (g_get_monotonic_time() < end_time); | ||||
| 
 | ||||
|     sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); | ||||
|     DPRINTF_STS; | ||||
|     g_assert_cmpint(sts & 0xff, == , | ||||
|                     TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); | ||||
|     bcount = (sts >> 8) & 0xffff; | ||||
| 
 | ||||
|     /* read response */ | ||||
|     uint8_t tpm_msg[sizeof(struct tpm_hdr)]; | ||||
|     g_assert_cmpint(sizeof(tpm_msg), ==, bcount); | ||||
| 
 | ||||
|     for (i = 0; i < sizeof(tpm_msg); i++) { | ||||
|         tpm_msg[i] = readb(TIS_REG(0, TPM_TIS_REG_DATA_FIFO)); | ||||
|         sts = readl(TIS_REG(0, TPM_TIS_REG_STS)); | ||||
|         DPRINTF_STS; | ||||
|         if (sts & TPM_TIS_STS_DATA_AVAILABLE) { | ||||
|             g_assert_cmpint((sts >> 8) & 0xffff, ==, --bcount); | ||||
|         } | ||||
|     } | ||||
|     g_assert_cmpmem(tpm_msg, sizeof(tpm_msg), s->tpm_msg, sizeof(*s->tpm_msg)); | ||||
| 
 | ||||
|     /* relinquish use of locality 0 */ | ||||
|     writeb(TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_ACTIVE_LOCALITY); | ||||
|     access = readb(TIS_REG(0, TPM_TIS_REG_ACCESS)); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
|     int ret; | ||||
|     char *args, *tmp_path = g_dir_make_tmp("qemu-tpm-tis-test.XXXXXX", NULL); | ||||
|     GThread *thread; | ||||
|     TestState test; | ||||
| 
 | ||||
|     module_call_init(MODULE_INIT_QOM); | ||||
|     g_test_init(&argc, &argv, NULL); | ||||
| 
 | ||||
|     test.addr = g_new0(SocketAddress, 1); | ||||
|     test.addr->type = SOCKET_ADDRESS_TYPE_UNIX; | ||||
|     test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL); | ||||
|     g_mutex_init(&test.data_mutex); | ||||
|     g_cond_init(&test.data_cond); | ||||
| 
 | ||||
|     thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test); | ||||
|     tpm_emu_test_wait_cond(&test); | ||||
| 
 | ||||
|     args = g_strdup_printf( | ||||
|         "-chardev socket,id=chr,path=%s " | ||||
|         "-tpmdev emulator,id=dev,chardev=chr " | ||||
|         "-device tpm-tis,tpmdev=dev", | ||||
|         test.addr->u.q_unix.path); | ||||
|     qtest_start(args); | ||||
| 
 | ||||
|     qtest_add_data_func("/tpm-tis/test_check_localities", &test, | ||||
|                         tpm_tis_test_check_localities); | ||||
| 
 | ||||
|     qtest_add_data_func("/tpm-tis/test_check_access_reg", &test, | ||||
|                         tpm_tis_test_check_access_reg); | ||||
| 
 | ||||
|     qtest_add_data_func("/tpm-tis/test_check_access_reg_seize", &test, | ||||
|                         tpm_tis_test_check_access_reg_seize); | ||||
| 
 | ||||
|     qtest_add_data_func("/tpm-tis/test_check_access_reg_release", &test, | ||||
|                         tpm_tis_test_check_access_reg_release); | ||||
| 
 | ||||
|     qtest_add_data_func("/tpm-tis/test_check_transmit", &test, | ||||
|                         tpm_tis_test_check_transmit); | ||||
| 
 | ||||
|     ret = g_test_run(); | ||||
| 
 | ||||
|     qtest_end(); | ||||
| 
 | ||||
|     g_thread_join(thread); | ||||
|     g_unlink(test.addr->u.q_unix.path); | ||||
|     qapi_free_SocketAddress(test.addr); | ||||
|     g_rmdir(tmp_path); | ||||
|     g_free(tmp_path); | ||||
|     g_free(args); | ||||
|     return ret; | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Peter Maydell
						Peter Maydell