diff --git a/Makefile-test.am b/Makefile-test.am index 0cd3929c4..a64367201 100644 --- a/Makefile-test.am +++ b/Makefile-test.am @@ -158,6 +158,9 @@ endif if ENABLE_TCTI_SPI_LTT2GO TESTS_UNIT += test/unit/tcti-spi-ltt2go endif +if ENABLE_TCTI_SPIDEV +TESTS_UNIT += test/unit/tcti-spidev +endif if ENABLE_TCTI_SPI_FTDI TESTS_UNIT += test/unit/tcti-spi-ftdi endif @@ -570,6 +573,18 @@ test_unit_tcti_spi_ltt2go_SOURCES = test/unit/tcti-spi-ltt2go.c \ src/tss2-tcti/tcti-spi-ltt2go.c endif +if ENABLE_TCTI_SPIDEV +test_unit_tcti_spidev_CFLAGS = $(CMOCKA_CFLAGS) $(TESTS_CFLAGS) +test_unit_tcti_spidev_LDADD = $(CMOCKA_LIBS) $(libtss2_tcti_spi_helper) +test_unit_tcti_spidev_LDFLAGS = -Wl,--wrap=open \ + -Wl,--wrap=close \ + -Wl,--wrap=ioctl \ + -Wl,--wrap=select \ + -Wl,--wrap=gettimeofday +test_unit_tcti_spidev_SOURCES = test/unit/tcti-spidev.c \ + src/tss2-tcti/tcti-spidev.c +endif + if ENABLE_TCTI_SPI_FTDI test_unit_tcti_spi_ftdi_CFLAGS = $(CMOCKA_CFLAGS) $(TESTS_CFLAGS) test_unit_tcti_spi_ftdi_LDADD = $(CMOCKA_LIBS) $(libtss2_tcti_spi_helper) diff --git a/Makefile.am b/Makefile.am index 12de494fc..2f844cf65 100644 --- a/Makefile.am +++ b/Makefile.am @@ -450,6 +450,26 @@ endif # ENABLE_TCTI_SPI_LTT2GO EXTRA_DIST += lib/tss2-tcti-spi-ltt2go.map \ lib/tss2-tcti-spi-ltt2go.def +# tcti library for letstrust-tpm2go usb tpm +if ENABLE_TCTI_SPIDEV +libtss2_tcti_spidev = src/tss2-tcti/libtss2-tcti-spidev.la +tss2_HEADERS += $(srcdir)/include/tss2/tss2_tcti_spidev.h +lib_LTLIBRARIES += $(libtss2_tcti_spidev) +pkgconfig_DATA += lib/tss2-tcti-spidev.pc + +src_tss2_tcti_libtss2_tcti_spidev_la_LDFLAGS = + +if HAVE_LD_VERSION_SCRIPT +src_tss2_tcti_libtss2_tcti_spidev_la_LDFLAGS += -Wl,--version-script=$(srcdir)/lib/tss2-tcti-spidev.map +endif # HAVE_LD_VERSION_SCRIPT +src_tss2_tcti_libtss2_tcti_spidev_la_LIBADD = $(libutil) $(libtss2_mu) $(libtss2_tcti_spi_helper) +src_tss2_tcti_libtss2_tcti_spidev_la_SOURCES = \ + src/tss2-tcti/tcti-common.c \ + src/tss2-tcti/tcti-spidev.c +endif # ENABLE_TCTI_SPIDEV +EXTRA_DIST += lib/tss2-tcti-spidev.map \ + lib/tss2-tcti-spidev.def + # tcti library for ftdi connected tpm if ENABLE_TCTI_SPI_FTDI libtss2_tcti_spi_ftdi = src/tss2-tcti/libtss2-tcti-spi-ftdi.la @@ -907,6 +927,7 @@ man7_MANS = \ man/man7/tss2-tcti-swtpm.7 \ man/man7/tss2-tcti-mssim.7 \ man/man7/tss2-tcti-cmd.7 \ + man/man7/tss2-tcti-spidev.7 \ man/man7/tss2-tcti-spi-helper.7 \ man/man7/tss2-tcti-spi-ltt2go.7 \ man/man7/tss2-tcti-spi-ftdi.7 \ @@ -990,6 +1011,7 @@ EXTRA_DIST += \ man/tss2-tcti-swtpm.7.in \ man/tss2-tcti-mssim.7.in \ man/tss2-tcti-cmd.7.in \ + man/tss2-tcti-spidev.7.in \ man/tss2-tcti-spi-helper.7.in \ man/tss2-tcti-spi-ltt2go.7.in \ man/tss2-tcti-spi-ftdi.7.in \ diff --git a/configure.ac b/configure.ac index ebf8198c5..4bb0df6fb 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) #Backward compatible setti AC_CONFIG_HEADERS([config.h]) -AC_CONFIG_FILES([Makefile Doxyfile lib/tss2-sys.pc lib/tss2-esys.pc lib/tss2-mu.pc lib/tss2-tcti-device.pc lib/tss2-tcti-mssim.pc lib/tss2-tcti-swtpm.pc lib/tss2-tcti-pcap.pc lib/tss2-tcti-libtpms.pc lib/tss2-rc.pc lib/tss2-tctildr.pc lib/tss2-fapi.pc lib/tss2-tcti-cmd.pc lib/tss2-policy.pc lib/tss2-tcti-spi-helper.pc lib/tss2-tcti-spi-ltt2go.pc lib/tss2-tcti-spi-ftdi.pc lib/tss2-tcti-i2c-helper.pc lib/tss2-tcti-i2c-ftdi.pc]) +AC_CONFIG_FILES([Makefile Doxyfile lib/tss2-sys.pc lib/tss2-esys.pc lib/tss2-mu.pc lib/tss2-tcti-device.pc lib/tss2-tcti-mssim.pc lib/tss2-tcti-swtpm.pc lib/tss2-tcti-pcap.pc lib/tss2-tcti-libtpms.pc lib/tss2-rc.pc lib/tss2-tctildr.pc lib/tss2-fapi.pc lib/tss2-tcti-cmd.pc lib/tss2-policy.pc lib/tss2-tcti-spi-helper.pc lib/tss2-tcti-spi-ltt2go.pc lib/tss2-tcti-spidev.pc lib/tss2-tcti-spi-ftdi.pc lib/tss2-tcti-i2c-helper.pc lib/tss2-tcti-i2c-ftdi.pc]) # propagate configure arguments to distcheck AC_SUBST([DISTCHECK_CONFIGURE_FLAGS],[$ac_configure_args]) @@ -328,6 +328,18 @@ AM_CONDITIONAL([ENABLE_TCTI_SPI_LTT2GO], [test "x$enable_tcti_spi_ltt2go" != xno AS_IF([test "x$enable_tcti_spi_ltt2go" = "xyes"], AC_DEFINE([TCTI_SPI_LTT2GO],[1], [TCTI FOR USB BASED ACCESS TO LETSTRUST-TPM2GO])) +AC_ARG_ENABLE([tcti-spidev], + [AS_HELP_STRING([--disable-tcti-spidev], + [don't build the tcti-spidev module; Default: Auto])],, + [enable_tcti_spidev=auto]) +AS_IF([test "x$enable_tcti_spidev" = "xauto"], + AC_CHECK_HEADER(linux/ioctl.h, + [enable_tcti_spidev=yes], + [enable_tcti_spidev=no])) +AM_CONDITIONAL([ENABLE_TCTI_SPIDEV], [test "x$enable_tcti_spidev" != xno]) +AS_IF([test "x$enable_tcti_spidev" = "xyes"], + AC_DEFINE([TCTI_SPIDEV],[1], [TCTI FOR SPIDEV BASED ACCESS TO TPM])) + PKG_CHECK_MODULES([LIBFTDI], [libftdi], [AC_DEFINE(LIBFTDI_VERSION, [0], [libftdi version 0.x])] @@ -721,6 +733,7 @@ AC_MSG_RESULT([ sysmeasurements: $sysmeasurements imameasurements: $imameasurements tcti_spi_ltt2go $enable_tcti_spi_ltt2go + tcti_spidev $enable_tcti_spidev tcti_spi_ftdi $enable_tcti_spi_ftdi tcti_i2c_ftdi $enable_tcti_i2c_ftdi ]) diff --git a/doc/tcti.md b/doc/tcti.md index 90c029805..449c1a841 100644 --- a/doc/tcti.md +++ b/doc/tcti.md @@ -13,6 +13,7 @@ - [tcti-spi-ftdi](#tcti-spi-ftdi) - [tcti-i2c-ftdi](#tcti-i2c-ftdi) - [tcti-spi-ltt2go](#tcti-spi-ltt2go) +- [tcti-spidev](#tcti-spidev) - [TPM Simulator tctis](#tpm-simulator-tctis) - [tcti-libtpms](#tcti-libtpms) - [Parameters](#parameters-4) @@ -273,6 +274,12 @@ flowchart TD style tpm stroke-dasharray: 3, 3 ``` +## tcti-spidev + +The tcti-spidev is used for communicating to a TPM that is connected via +a spidev device. On a Raspberry Pi for example this happens when enabling +the device tree overlay `spi0-cs2`. + ## TPM Simulator tctis There are multiple tctis used for testing. diff --git a/include/tss2/tss2_tcti_spidev.h b/include/tss2/tss2_tcti_spidev.h new file mode 100644 index 000000000..78fb1245d --- /dev/null +++ b/include/tss2/tss2_tcti_spidev.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright 2023 Infineon Technologies AG + */ +#ifndef TSS2_TCTI_SPIDEV_H +#define TSS2_TCTI_SPIDEV_H + +#include +#include "tss2_tcti.h" + +#ifdef __cplusplus +extern "C" { +#endif + +TSS2_RC Tss2_Tcti_Spidev_Init ( + TSS2_TCTI_CONTEXT *tctiContext, + size_t *size, + const char *config); + + +#ifdef __cplusplus +} +#endif + +#endif /* TSS2_TCTI_SPIDEV_H */ diff --git a/lib/tss2-tcti-spidev.def b/lib/tss2-tcti-spidev.def new file mode 100644 index 000000000..ec337b3e1 --- /dev/null +++ b/lib/tss2-tcti-spidev.def @@ -0,0 +1,4 @@ +LIBRARY tss2-tcti-spidev +EXPORTS + Tss2_Tcti_Info + Tss2_Tcti_Spidev_Init diff --git a/lib/tss2-tcti-spidev.map b/lib/tss2-tcti-spidev.map new file mode 100644 index 000000000..a7b068aa7 --- /dev/null +++ b/lib/tss2-tcti-spidev.map @@ -0,0 +1,7 @@ +{ + global: + Tss2_Tcti_Info; + Tss2_Tcti_Spidev_Init; + local: + *; +}; diff --git a/lib/tss2-tcti-spidev.pc.in b/lib/tss2-tcti-spidev.pc.in new file mode 100644 index 000000000..c12e7d686 --- /dev/null +++ b/lib/tss2-tcti-spidev.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: tss2-tcti-spidev +Description: TCTI library for communicating with the TPM over spidev. +URL: https://github.com/tpm2-software/tpm2-tss +Version: @VERSION@ +Cflags: -I${includedir} -I${includedir}/tss +Libs: -ltss2-tcti-spi-helper -ltss2-tcti-spi-ltt2go -L${libdir} diff --git a/man/tss2-tcti-spidev.7.in b/man/tss2-tcti-spidev.7.in new file mode 100644 index 000000000..9d7c34596 --- /dev/null +++ b/man/tss2-tcti-spidev.7.in @@ -0,0 +1,16 @@ +.\" Process this file with +.\" groff -man -Tascii foo.1 +.\" +.TH TCTI-SPI 7 "NOVEMBER 2023" "TPM2 Software Stack" +.SH NAME +tcti-spidev \- TCTI library for accessing TPMs via spidev +.SH SYNOPSIS +A TPM Command Transmission Interface (TCTI) module for interaction with +a TPM that is attached to an spidev device node. +.SH DESCRIPTION +tcti-spidev is a library that abstracts the details of communication +with a TPM that is attached to an spidev device node.. The interface +exposed by this library is defined in the \*(lqTSS System Level API +and TPM Command Transmission Interface Specification\*(rq specification. +The configuration of this TCTI takes the filepath to the device node +(Default: /dev/spidev0.1). diff --git a/src/tss2-tcti/tcti-spidev.c b/src/tss2-tcti/tcti-spidev.c new file mode 100644 index 000000000..2e1d2b15f --- /dev/null +++ b/src/tss2-tcti/tcti-spidev.c @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright 2020 Peter Huewe + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tss2_tcti.h" +#include "tss2_tcti_spidev.h" +#include "tss2_tcti_spi_helper.h" +#include "tcti-common.h" +#include "tss2_mu.h" +#define LOGMODULE tcti +#include "util/log.h" + +typedef struct { + struct timeval timeout; + int fd; +} PLATFORM_USERDATA; + +TSS2_RC +platform_spi_acquire (void *user_data) +{ + PLATFORM_USERDATA *platform_data = (PLATFORM_USERDATA *) user_data; + struct spi_ioc_transfer tr = { + .delay_usecs = 0, + .speed_hz = 5000000, + .bits_per_word = 8, + .cs_change = 1, + .len = 0, + }; + + int ret = ioctl(platform_data->fd, SPI_IOC_MESSAGE(1), &tr); + if (ret < 0) { + LOG_ERROR("SPI acquire failed: %s", strerror(errno)); + return TSS2_TCTI_RC_IO_ERROR; + } + return TSS2_RC_SUCCESS; +} + +TSS2_RC +platform_spi_release (void *user_data) +{ + PLATFORM_USERDATA *platform_data = (PLATFORM_USERDATA *) user_data; + struct spi_ioc_transfer tr = { + .delay_usecs = 0, + .speed_hz = 5000000, + .bits_per_word = 8, + .cs_change = 0, + .len = 0, + }; + + int ret = ioctl(platform_data->fd, SPI_IOC_MESSAGE(1), &tr); + if (ret < 0) { + LOG_ERROR("SPI release failed: %s", strerror(errno)); + return TSS2_TCTI_RC_IO_ERROR; + } + return TSS2_RC_SUCCESS; +} + +TSS2_RC +platform_spi_transfer (void *user_data, const void *data_out, void *data_in, size_t cnt) +{ + LOGBLOB_DEBUG(data_out, cnt, "Transferring data over ioctl:"); + PLATFORM_USERDATA *platform_data = (PLATFORM_USERDATA *) user_data; + + struct spi_ioc_transfer tr = { + .delay_usecs = 0, + .speed_hz = 5000000, + .bits_per_word = 8, + .cs_change = 1, + }; + + tr.len = cnt; + tr.tx_buf = (unsigned long) data_out; + tr.rx_buf = (unsigned long) data_in; + int ret = ioctl(platform_data->fd, SPI_IOC_MESSAGE(1), &tr); + if (ret < 0) { + LOG_ERROR("SPI acquire failed: %s", strerror(errno)); + return TSS2_TCTI_RC_IO_ERROR; + } + LOGBLOB_DEBUG(data_in, cnt, "Received data over ioctl:"); + return TSS2_RC_SUCCESS; +} + +TSS2_RC +platform_sleep_ms (void *user_data, int32_t milliseconds) +{ + (void) user_data; + struct timeval tv = {milliseconds/1000, (milliseconds%1000)*1000}; + select (0, NULL, NULL, NULL, &tv); + + return TSS2_RC_SUCCESS; +} + +TSS2_RC +platform_start_timeout (void *user_data, int32_t milliseconds) +{ + PLATFORM_USERDATA *platform_data = (PLATFORM_USERDATA *) user_data; + + memset (&platform_data->timeout, 0, sizeof (struct timeval)); + + if (gettimeofday (&platform_data->timeout, NULL)) { + LOG_ERROR ("getimeofday failed with errno: %d.", errno); + return TSS2_TCTI_RC_GENERAL_FAILURE; + } + + platform_data->timeout.tv_sec += (milliseconds/1000); + platform_data->timeout.tv_usec += (milliseconds%1000)*1000; + if (platform_data->timeout.tv_usec > 999999) { + platform_data->timeout.tv_sec++; + platform_data->timeout.tv_usec -= 1000000; + } + + return TSS2_RC_SUCCESS; +} + +TSS2_RC +platform_timeout_expired (void *user_data, bool *is_timeout_expired) +{ + PLATFORM_USERDATA *platform_data = (PLATFORM_USERDATA *) user_data; + + struct timeval now; + if (gettimeofday (&now, NULL)) { + LOG_ERROR ("getimeofday failed with errno: %d.", errno); + return TSS2_TCTI_RC_GENERAL_FAILURE; + } + + if (now.tv_sec > platform_data->timeout.tv_sec) { + *is_timeout_expired = true; + } else if ((now.tv_sec == platform_data->timeout.tv_sec) + && (now.tv_usec > platform_data->timeout.tv_usec)) { + *is_timeout_expired = true; + } else { + *is_timeout_expired = false; + } + + return TSS2_RC_SUCCESS; +} + +void +platform_finalize(void *user_data) +{ + PLATFORM_USERDATA *platform_data = (PLATFORM_USERDATA *) user_data; + close(platform_data->fd); + free(platform_data); +} + +TSS2_RC +Tss2_Tcti_Spidev_Init (TSS2_TCTI_CONTEXT* tcti_context, size_t* size, const char* config) +{ + TSS2_TCTI_SPI_HELPER_PLATFORM platform = {0}; + + if (!config || config[0] == '\0') + config = "/dev/spidev0.1"; + + /* Check if context size is requested */ + if (tcti_context == NULL) { + return Tss2_Tcti_Spi_Helper_Init (NULL, size, NULL); + } + + /* Create required platform user data */ + PLATFORM_USERDATA *platform_data = calloc (1, sizeof (PLATFORM_USERDATA)); + if (platform_data == NULL) { + return TSS2_BASE_RC_MEMORY; + } + + platform_data->fd = open(config, O_RDWR); + if (!platform_data->fd) { + LOG_ERROR("%s cannot be opened: %s", config, strerror(errno)); + free(platform_data); + return TSS2_TCTI_RC_IO_ERROR; + } + + /* Create TCTI SPI platform struct with custom platform methods */ + platform.user_data = platform_data; + platform.sleep_ms = platform_sleep_ms; + platform.start_timeout = platform_start_timeout; + platform.timeout_expired = platform_timeout_expired; + platform.spi_acquire = platform_spi_acquire; + platform.spi_release = platform_spi_release; + platform.spi_transfer = platform_spi_transfer; + platform.finalize = platform_finalize; + + /* Initialize TCTI context */ + return Tss2_Tcti_Spi_Helper_Init (tcti_context, size, &platform); +} + +const TSS2_TCTI_INFO tss2_tcti_info = { + .version = TCTI_VERSION, + .name = "tcti-spidev", + .description = "TCTI for communicating with a TPM via spidev.", + .config_help = "Path to spidev (Default: /dev/spidev0.1).", + .init = Tss2_Tcti_Spidev_Init +}; + +const TSS2_TCTI_INFO * +Tss2_Tcti_Info (void) +{ + return &tss2_tcti_info; +} diff --git a/test/unit/tcti-spidev.c b/test/unit/tcti-spidev.c new file mode 100644 index 000000000..78436a660 --- /dev/null +++ b/test/unit/tcti-spidev.c @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/***********************************************************************; + * Copyright (c) 2022, Infineon Technologies AG + * All rights reserved. + ***********************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "tss2_tcti.h" +#include "tss2_tcti_spidev.h" +#include "tss2_tcti_spi_helper.h" + +#include "tss2-tcti/tcti-common.h" +#include "tss2-tcti/tcti-spi-helper.h" +#include "util/key-value-parse.h" + +typedef enum { + TPM_DID_VID_HEAD = 0, + TPM_DID_VID_DATA, + TPM_ACCESS_HEAD, + TPM_ACCESS_DATA, + TPM_STS_CMD_NOT_READY_HEAD, + TPM_STS_CMD_NOT_READY_DATA, + TPM_STS_CMD_READY_HEAD, + TPM_STS_CMD_READY_DATA, + TPM_RID_HEAD, + TPM_RID_DATA, +} tpm_state_t; + +// First 4 bytes are the request, the remainder is the response +static const unsigned char TPM_DID_VID_0[] = {0x83, 0xd4, 0x0f, 0x00, 0xd1, 0x15, 0x1b, 0x00}; +static const unsigned char TPM_ACCESS_0[] = {0x80, 0xd4, 0x00, 0x00, 0xa1}; +static const unsigned char TPM_STS_0_CMD_NOT_READY[] = {0x83, 0xd4, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00}; +static const unsigned char TPM_STS_0_CMD_READY[] = {0x83, 0xd4, 0x00, 0x18, 0x40, 0x00, 0x00, 0x00}; +static const unsigned char TPM_RID_0[] = {0x80, 0xd4, 0x0f, 0x04, 0x00}; + +/* + * Mock function select + */ +int __wrap_select (int nfds, fd_set *readfds, + fd_set *writefds, + fd_set *exceptfds, + struct timeval *timeout) +{ + + assert_int_equal (nfds, 0); + assert_null (readfds); + assert_null (writefds); + assert_null (exceptfds); + assert_non_null (timeout); + + return 0; +} + +/* + * Mock function gettimeofday + */ +int __wrap_gettimeofday (struct timeval *tv, + struct timezone *tz) +{ + assert_null (tz); + assert_non_null (tv); + + tv->tv_sec = 0; + tv->tv_usec = 0; + + return 0; +} + +int __real_open(const char *path, int flags); + +#define FD_NO 5 +int __wrap_open(const char *path, int flags) +{ + assert_ptr_not_equal(path, NULL); + if (!!strncmp(path, "/dev/spidev", sizeof("/dev/spidev") - 1)) + return __real_open(path, flags); + assert_int_equal(flags, O_RDWR); + return FD_NO; +} + +int __wrap_close(int fd) +{ + assert_int_equal(fd, FD_NO); + return 0; +} + +int __wrap_ioctl(int fd, unsigned long request, struct spi_ioc_transfer *tr) +{ + assert_int_equal(tr->delay_usecs, 0); + assert_int_equal(tr->bits_per_word, 8); + + size_t len = tr->len; + uint8_t *tx_buf = (uint8_t *) tr->tx_buf; + uint8_t *rx_buf = (uint8_t *) tr->rx_buf; + + static tpm_state_t tpm_state = TPM_DID_VID_HEAD; + + // Check for CS-acquire/-release which have no payload + if (len == 0) { + goto done; + } + + switch (tpm_state++) { + case TPM_DID_VID_HEAD: + assert_int_equal (len, 4); + assert_memory_equal(&tx_buf[0], TPM_DID_VID_0, 4); + rx_buf[3] = 0x01; // Set Waitstate OK + break; + case TPM_DID_VID_DATA: + assert_int_equal (len, sizeof (TPM_DID_VID_0) - 4); + memcpy (&rx_buf[0], &TPM_DID_VID_0[4], sizeof (TPM_DID_VID_0) - 4); + break; + case TPM_ACCESS_HEAD: + assert_int_equal (len, 4); + assert_memory_equal(&tx_buf[0], TPM_ACCESS_0, 4); + rx_buf[3] = 0x01; // Set Waitstate OK + break; + case TPM_ACCESS_DATA: + assert_int_equal (len, sizeof (TPM_ACCESS_0) - 4); + memcpy (&rx_buf[0], &TPM_ACCESS_0[4], sizeof (TPM_ACCESS_0) - 4); + break; + case TPM_STS_CMD_NOT_READY_HEAD: + assert_int_equal (len, 4); + assert_memory_equal(&tx_buf[0], TPM_STS_0_CMD_NOT_READY, 4); + rx_buf[3] = 0x01; // Set Waitstate OK + break; + case TPM_STS_CMD_NOT_READY_DATA: + assert_int_equal (len, sizeof (TPM_STS_0_CMD_NOT_READY) - 4); + memcpy (&rx_buf[0], &TPM_STS_0_CMD_NOT_READY[4], sizeof (TPM_STS_0_CMD_NOT_READY) - 4); + break; + case TPM_STS_CMD_READY_HEAD: + assert_int_equal (len, 4); + assert_memory_equal(&tx_buf[0], TPM_STS_0_CMD_READY, 4); + rx_buf[3] = 0x01; // Set Waitstate OK + break; + case TPM_STS_CMD_READY_DATA: + assert_int_equal (len, sizeof (TPM_STS_0_CMD_READY) - 4); + memcpy (&rx_buf[0], &TPM_STS_0_CMD_READY[4], sizeof (TPM_STS_0_CMD_READY) - 4); + break; + case TPM_RID_HEAD: + assert_int_equal (len, 4); + assert_memory_equal(&tx_buf[0], TPM_RID_0, 4); + rx_buf[3] = 0x01; // Set Waitstate OK + break; + case TPM_RID_DATA: + assert_int_equal (len, sizeof (TPM_RID_0) - 4); + memcpy (&rx_buf[0], &TPM_RID_0[4], sizeof (TPM_RID_0) - 4); + break; + default: + assert_true (false); + } + +done: + return 0; +} + +/* + * The test will invoke Tss2_Tcti_Spidev_Init() and subsequently + * it will start reading TPM_DID_VID, claim locality, read TPM_STS, + * and finally read TPM_RID before exiting the Init function. + * For testing purpose, the TPM responses are hardcoded. + */ +static void +tcti_spi_init_test (void **state) +{ + TSS2_RC rc; + size_t size; + TSS2_TCTI_CONTEXT* tcti_ctx; + + /* Get requested TCTI context size */ + rc = Tss2_Tcti_Spidev_Init (NULL, &size, NULL); + assert_int_equal (rc, TSS2_RC_SUCCESS); + + /* Allocate TCTI context size */ + tcti_ctx = (TSS2_TCTI_CONTEXT*) calloc (1, size); + assert_non_null (tcti_ctx); + + /* Initialize TCTI context */ + rc = Tss2_Tcti_Spidev_Init (tcti_ctx, &size, NULL); + assert_int_equal (rc, TSS2_RC_SUCCESS); + + TSS2_TCTI_SPI_HELPER_PLATFORM platform = ((TSS2_TCTI_SPI_HELPER_CONTEXT *) tcti_ctx)->platform; + free (platform.user_data); + free (tcti_ctx); +} + +int +main (int argc, + char *argv[]) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test (tcti_spi_init_test), + }; + + return cmocka_run_group_tests (tests, NULL, NULL); +}