summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/tpm_test/Makefile56
-rw-r--r--test/tpm_test/crypto_test.xml112
-rw-r--r--test/tpm_test/ftdi_spi_tpm.c448
-rw-r--r--test/tpm_test/ftdi_spi_tpm.h25
-rw-r--r--test/tpm_test/ftdi_spi_tpm.i50
-rw-r--r--test/tpm_test/mpsse.c723
-rw-r--r--test/tpm_test/mpsse.h45
-rw-r--r--test/tpm_test/support.c218
-rw-r--r--test/tpm_test/support.h87
-rwxr-xr-xtest/tpm_test/tpmtest.py364
10 files changed, 2128 insertions, 0 deletions
diff --git a/test/tpm_test/Makefile b/test/tpm_test/Makefile
new file mode 100644
index 0000000000..e68c0f1c76
--- /dev/null
+++ b/test/tpm_test/Makefile
@@ -0,0 +1,56 @@
+# Copyright 2015 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+ifeq ($(V),)
+Q := @
+else
+Q :=
+endif
+
+obj = ../../build/tpm_test
+src = .
+SWIG = /usr/bin/swig
+
+CFLAGS = -fPIC
+CFLAGS += -I /usr/include/python2.7
+CFLAGS += -DLIBFTDI1=1
+CFLAGS += -c
+
+TARGET = ftdi_spi_tpm
+.PRECIOUS: $(obj)/ftdi_spi_tpm_wrap.c
+
+$(obj)/_$(TARGET).so:
+
+OBJS = $(obj)/$(TARGET).o $(obj)/$(TARGET)_wrap.o $(obj)/mpsse.o \
+ $(obj)/support.o
+
+DEPS := $(OBJS:.o=.o.d)
+
+$(OBJS): | $(obj)
+
+$(obj)/%.o: $(obj)/%.c
+ @echo " CC $(notdir $@)"
+ $(Q)gcc $(CFLAGS) -o $@ $<
+
+$(obj)/%.o: $(src)/%.c
+ @echo " CC $(notdir $@)"
+ $(Q)gcc $(CFLAGS) -Wall -Werror -MMD -MF $@.d -o $@ $<
+
+$(obj)/_$(TARGET).so: $(OBJS) $(obj)/$(TARGET).py
+ @echo " LD $(notdir $@)"
+ $(Q)rm -f $@
+ $(Q)ld -shared $(OBJS) -lftdi1 -o $@
+
+$(obj)/%_wrap.c: $(src)/%.i
+ @echo " SWIG $(notdir $@)"
+ $(Q)swig -python -outdir $(obj) -o $@ $<
+
+clean:
+ @rm -rf $(obj)/
+
+$(obj):
+ @echo " MKDIR $(obj)"
+ $(Q)mkdir -p $(obj)
+
+-include $(DEPS)
diff --git a/test/tpm_test/crypto_test.xml b/test/tpm_test/crypto_test.xml
new file mode 100644
index 0000000000..6de62d8bbb
--- /dev/null
+++ b/test/tpm_test/crypto_test.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0"?>
+<!--
+Copyright 2015 The Chromium OS Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+
+This file describes test vectors for various encryption schemes.
+
+Each description is encapsulated in a 'crypto_test' element. This element must
+have the name property set. The name starts witht the encryption scheme's name
+(say AES or DES), delimited by a colon, and followed by a three character
+encryption submode, if necessary (say ECB for AES).
+
+The rest of the attributes are self explanatory. The default format for the
+clear_text element is ASCII, for the rest - hex. This default could be
+overridded using the 'format' property.
+
+The ascii strings are stripped of leading and trailing whitespace and then
+joined using space as a separator. Whitespace in hes strings is ignored.
+
+Hex values are interpreted as a set of 4 byte entities in network byte order.
+Many of the crypto_test elements were borrowed from NIST test vectors.
+-->
+<crypto_tests>
+ <crypto_test name="AES:ECB common">
+ <clear_text>
+ this is the text which will be encrypted if everything is going fine.
+ </clear_text>
+ <key>0123456789abcdef0123456789abcdef0123456789abcdef</key>
+ <cipher_text>
+ <!--
+ Cipher text matches the case of the clear text padded with zeros to
+ the nearest block size.
+ -->
+ f90fe23d ce62d9ee 57178af0 d08604c6
+ 7244ec3d 871879d8 6d81313f 10bb4c66
+ 9fe08dda ccb36763 bde8b464 c9a9b012
+ 9ff06d09 fbaee2a4 901cfe0d f0fee26c
+ 34b58f68 a9e27607 7bdd8e72 8b2b528b
+ </cipher_text>
+ </crypto_test>
+ <crypto_test name="AES:ECB128 1">
+ <clear_text format="hex">
+ 33221100 77665544 bbaa9988 ffeeddcc
+ </clear_text>
+ <key>03020100 07060504 0b0a0908 0f0e0d0c</key>
+ <cipher_text>
+ d8e0c469 30047b6a 80b7cdd8 5ac5b470
+ </cipher_text>
+ </crypto_test>
+ <crypto_test name="AES:ECB192 1">
+ <clear_text format="hex">
+ 00000000 00000000 00000000 00000000
+ </clear_text>
+ <key>
+ 6e0fd215 9f647ebc b1765bd9 badae607
+ 948a7c96 297f7984
+ </key>
+ <cipher_text>
+ 42184e8e 3d1a594e 76086f5b 94856ff1
+ </cipher_text>
+ </crypto_test>
+ <crypto_test name="AES:ECB256 1">
+ <clear_text format="hex">
+ 00000000 00000000 00000000 00000000
+ </clear_text>
+ <key>
+ 00000080 00000000 00000000 00000000
+ 00000000 00000000 00000000 00000000
+ </key>
+ <cipher_text>
+ cb6d5ae3 a001b219 8afabc1e 59572ba2
+ </cipher_text>
+ </crypto_test>
+ <crypto_test name="AES:ECB256 2">
+ <clear_text format="hex">
+ 45249ff6 179b4fdf 7b412bad 10376ce6
+ </clear_text>
+ <key>
+ 10eb3d60 be71ca15 f0ae732b 81777d85
+ 072c351f d708613b a310982d f4df1409
+ </key>
+ <cipher_text>
+ 7a4b3023 fff3f939 8f8d7d06 c7ec249e
+ </cipher_text>
+ </crypto_test>
+ <crypto_test name="AES:CTR128I 1">
+ <clear_text format="hex">
+ e2bec16b 969f402e 117e3de9 2a179373
+ </clear_text>
+ <key>
+ 16157e2b a6d2ae28 8815f7ab 3c4fcf09
+ </key>
+ <cipher_text>
+ 91614d87 26e320b6 6468ef1b ceb60d99
+ </cipher_text>
+ <iv>f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc</iv>
+ </crypto_test>
+ <crypto_test name="AES:CTR256I 1">
+ <clear_text format="hex">
+ 13c31e60 a5895777 04f5a7b7 28d2f3bb
+ </clear_text>
+ <key>
+ 10eb3d60 be71ca15 f0ae732b 81777d85
+ 072c351f d708613b a310982d f4df1409
+ </key>
+ <cipher_text>
+ e2bec16b 969f402e 117e3de9 2a179373
+ </cipher_text>
+ <iv>f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc</iv>
+ </crypto_test>
+</crypto_tests>
diff --git a/test/tpm_test/ftdi_spi_tpm.c b/test/tpm_test/ftdi_spi_tpm.c
new file mode 100644
index 0000000000..ac028ef325
--- /dev/null
+++ b/test/tpm_test/ftdi_spi_tpm.c
@@ -0,0 +1,448 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <endian.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "ftdi_spi_tpm.h"
+
+static struct mpsse_context *mpsse_;
+static unsigned locality_; /* Set at initialization. */
+static int ftdi_trace_enabled;
+
+/* Assorted TPM2 registers for interface type FIFO. */
+#define TPM_ACCESS_REG 0
+#define TPM_STS_REG 0x18
+#define TPM_DATA_FIFO_REG 0x24
+#define TPM_DID_VID_REG 0xf00
+#define TPM_RID_REG 0xf04
+
+static struct swig_string_data empty_string_data = (struct swig_string_data){
+ .size = 0, .data = NULL
+};
+
+/* Locality management bits (in TPM_ACCESS_REG). */
+enum TpmAccessBits {
+ tpmRegValidSts = (1 << 7),
+ activeLocality = (1 << 5),
+ requestUse = (1 << 1),
+ tpmEstablishment = (1 << 0),
+};
+
+enum TpmStsBits {
+ tpmFamilyShift = 26,
+ tpmFamilyMask = ((1 << 2) - 1), /* 2 bits wide. */
+ tpmFamilyTPM2 = 1,
+ resetEstablishmentBit = (1 << 25),
+ commandCancel = (1 << 24),
+ burstCountShift = 8,
+ burstCountMask = ((1 << 16) - 1), /* 16 bits wide. */
+ stsValid = (1 << 7),
+ commandReady = (1 << 6),
+ tpmGo = (1 << 5),
+ dataAvail = (1 << 4),
+ Expect = (1 << 3),
+ selfTestDone = (1 << 2),
+ responseRetry = (1 << 1),
+};
+
+enum {
+ false = 0,
+ true = 1
+};
+
+/*
+ * SPI frame header for TPM transactions is 4 bytes in size, it is described
+ * in section "6.4.6 Spi Bit Protocol" of the TCG issued "TPM Profile (PTP)
+ * Specification Revision 00.43.
+ */
+struct SpiFrameHeader {
+ unsigned char body[4];
+};
+
+void FtdiStop(void)
+{
+ if (mpsse_)
+ Close(mpsse_);
+
+ mpsse_ = NULL;
+}
+
+static void StartTransaction(int read_write, size_t bytes, unsigned addr)
+{
+ struct SpiFrameHeader header;
+ int i;
+ uint8_t flow_c;
+ char *transfer_data;
+
+ /*
+ * give it 10 ms. TODO(vbendeb): remove this once cr50 SPS TPM driver
+ * performance is fixed.
+ */
+ usleep(10000);
+
+ /*
+ * The first byte of the frame header encodes the transaction type
+ * (read or write) and size (set to length - 1).
+ */
+ header.body[0] = (read_write ? 0x80 : 0) | 0x40 | (bytes - 1);
+
+ /* The rest of the frame header is the internal address in the TPM. */
+ for (i = 0; i < 3; i++)
+ header.body[i + 1] = (addr >> (8 * (2 - i))) & 0xff;
+
+ Start(mpsse_);
+
+ transfer_data =
+ Transfer(mpsse_, (char *)header.body, sizeof(header.body));
+
+ /*
+ * The TCG TPM over SPI specification itroduces the notion of SPI flow
+ * control (Section "6.4.5 Flow Control" of the TCG issued "TPM
+ * Profile (PTP) Specification Revision 00.43).
+ *
+ * The slave (TPM device) expects each transaction to start with a 4
+ * byte header trasmitted by master. If the slave needs to stall the
+ * transaction, it sets the MOSI bit to 0 during the last clock of the
+ * 4 byte header. In this case the master is supposed to start polling
+ * the line, byte at time, until the last bit in the received byte
+ * (transferred during the last clock of the byte) is set to 1.
+ */
+ flow_c = transfer_data[3];
+ free(transfer_data);
+ while (!(flow_c & 1)) {
+ transfer_data = Read(mpsse_, 1);
+ flow_c = transfer_data[0];
+ free(transfer_data);
+ }
+}
+
+static void trace_dump(const char *prefix, unsigned reg, size_t bytes,
+ const uint8_t *buffer)
+{
+ if (!ftdi_trace_enabled)
+ return;
+ printf("%s %2.2x:", prefix, reg);
+ if (bytes == 4) {
+ printf(" %8.8x\n", *(const uint32_t *)buffer);
+ } else {
+ int i;
+
+ for (i = 0; i < bytes; i++)
+ printf(" %2.2x", buffer[i]);
+ printf("\n");
+ }
+}
+
+static int FtdiWriteReg(unsigned reg_number, size_t bytes, void *buffer)
+{
+ if (!mpsse_)
+ return false;
+
+ trace_dump("W", reg_number, bytes, buffer);
+ StartTransaction(false, bytes, reg_number + locality_ * 0x10000);
+ Write(mpsse_, buffer, bytes);
+ Stop(mpsse_);
+ return true;
+}
+
+static int FtdiReadReg(unsigned reg_number, size_t bytes, void *buffer)
+{
+ void *data;
+
+ if (!mpsse_)
+ return false;
+
+ StartTransaction(true, bytes, reg_number + locality_ * 0x10000);
+ data = Read(mpsse_, bytes);
+ if (data)
+ memcpy(buffer, data, bytes);
+ free(data);
+ Stop(mpsse_);
+ trace_dump("R", reg_number, bytes, buffer);
+ return true;
+}
+
+static int ReadTpmSts(uint32_t *status)
+{
+ return FtdiReadReg(TPM_STS_REG, sizeof(*status), status);
+}
+
+static int WriteTpmSts(uint32_t status)
+{
+ return FtdiWriteReg(TPM_STS_REG, sizeof(status), &status);
+}
+
+static uint32_t GetBurstCount(void)
+{
+ uint32_t status;
+
+ ReadTpmSts(&status);
+ return (status >> burstCountShift) & burstCountMask;
+}
+
+int FtdiSpiInit(uint32_t freq, int enable_debug)
+{
+ uint32_t did_vid, status;
+ uint8_t cmd;
+ uint16_t vid;
+
+ if (mpsse_)
+ return true;
+
+ ftdi_trace_enabled = enable_debug;
+
+ /* round frequency down to the closest 100KHz */
+ freq = (freq / (100 * 1000)) * 100 * 1000;
+
+ printf("Starting MPSSE at %d kHz\n", freq / 1000);
+ mpsse_ = MPSSE(freq, MSB, NULL);
+ if (!mpsse_)
+ return false;
+
+ /* Reset the TPM using GPIOL0, issue a 100 ms long pulse. */
+ PinLow(mpsse_, GPIOL0);
+ usleep(100000);
+ PinHigh(mpsse_, GPIOL0);
+
+ FtdiReadReg(TPM_DID_VID_REG, sizeof(did_vid), &did_vid);
+
+ vid = did_vid & 0xffff;
+ if ((vid != 0x15d1) && (vid != 0x1ae0)) {
+ fprintf(stderr, "unknown did_vid: %#x\n", did_vid);
+ return false;
+ }
+
+ /* Try claiming locality zero. */
+ FtdiReadReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
+ if ((cmd & (activeLocality & tpmRegValidSts)) ==
+ (activeLocality & tpmRegValidSts)) {
+ /*
+ * Locality active - maybe reset line is not connected?
+ * Release the locality and try again
+ */
+ cmd = activeLocality;
+ FtdiWriteReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
+ FtdiReadReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
+ }
+
+ /* tpmEstablishment can be either set or not. */
+ if ((cmd & ~tpmEstablishment) != tpmRegValidSts) {
+ fprintf(stderr, "invalid reset status: %#x\n", cmd);
+ return false;
+ }
+ cmd = requestUse;
+ FtdiWriteReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
+ FtdiReadReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
+ if ((cmd & ~tpmEstablishment) != (tpmRegValidSts | activeLocality)) {
+ fprintf(stderr, "failed to claim locality, status: %#x\n", cmd);
+ return false;
+ }
+
+ ReadTpmSts(&status);
+ if (((status >> tpmFamilyShift) & tpmFamilyMask) != tpmFamilyTPM2) {
+ fprintf(stderr, "unexpected TPM family value, status: %#x\n",
+ status);
+ return false;
+ }
+ FtdiReadReg(TPM_RID_REG, sizeof(cmd), &cmd);
+ printf("Connected to device vid:did:rid of %4.4x:%4.4x:%2.2x\n",
+ did_vid & 0xffff, did_vid >> 16, cmd);
+
+ return true;
+}
+
+/* This is in seconds. */
+#define MAX_STATUS_TIMEOUT 120
+static int WaitForStatus(uint32_t statusMask, uint32_t statusExpected)
+{
+ uint32_t status;
+ time_t target_time;
+ static unsigned max_timeout;
+
+ target_time = time(NULL) + MAX_STATUS_TIMEOUT;
+ do {
+ usleep(10000);
+ if (time(NULL) >= target_time) {
+ fprintf(stderr, "failed to get expected status %x\n",
+ statusExpected);
+ return false;
+ }
+ ReadTpmSts(&status);
+ } while ((status & statusMask) != statusExpected);
+
+ /* Calculate time spent waiting */
+ target_time = MAX_STATUS_TIMEOUT - target_time + time(NULL);
+ if (max_timeout < (unsigned)target_time) {
+ max_timeout = target_time;
+ printf("New max timeout: %d s\n", max_timeout);
+ }
+
+ return true;
+}
+
+static void SpinSpinner(void)
+{
+ static const char *spinner = "\\|/-";
+ static int index;
+
+ if (index > strlen(spinner))
+ index = 0;
+
+ fprintf(stdout, "%c[1D%c", 0x1b, spinner[index++]);
+ fflush(stdout);
+}
+
+#define MAX_RESPONSE_SIZE 4096
+#define HEADER_SIZE 6
+
+/* tpm_command points at a buffer 4096 bytes in size */
+struct swig_string_data FtdiSendCommandAndWait(char *tpm_command,
+ int command_size)
+{
+ uint32_t status;
+ uint32_t expected_status_bits;
+ size_t handled_so_far;
+ uint32_t payload_size;
+ char message[100];
+ int offset = 0;
+ uint8_t *response;
+
+ if (!mpsse_) {
+ fprintf(stderr, "attempt to use an uninitialized FTDI TPM!\n");
+ return empty_string_data;
+ }
+
+ response = malloc(MAX_RESPONSE_SIZE);
+ if (!response) {
+ fprintf(stderr, "attempt to use an uninitialized FTDI TPM!\n");
+ return empty_string_data;
+ }
+
+ handled_so_far = 0;
+
+ WriteTpmSts(commandReady);
+
+ memcpy(&payload_size, tpm_command + 2, sizeof(payload_size));
+ payload_size = be32toh(payload_size);
+ offset +=
+ snprintf(message, sizeof(message), "Message size %d", payload_size);
+
+ /*
+ * No need to wait for the sts.Expect bit to be set, at least with the
+ * 15d1:001b and 1ae0:0028 devices. Let's just write the command into
+ * FIFO, make sure not to exceed the burst count.
+ */
+ do {
+ uint32_t transaction_size;
+ uint32_t burst_count = GetBurstCount();
+
+ if (burst_count > 64)
+ burst_count = 64;
+
+ transaction_size = command_size - handled_so_far;
+ if (transaction_size > burst_count)
+ transaction_size = burst_count;
+
+ if (transaction_size) {
+ FtdiWriteReg(TPM_DATA_FIFO_REG, transaction_size,
+ tpm_command + handled_so_far);
+ handled_so_far += transaction_size;
+ }
+ } while (handled_so_far != command_size);
+
+ /* And tell the device it can start processing it. */
+ WriteTpmSts(tpmGo);
+
+ expected_status_bits = stsValid | dataAvail;
+ if (!WaitForStatus(expected_status_bits, expected_status_bits)) {
+ size_t i;
+
+ printf("Failed processing. %s:", message);
+ for (i = 0; i < command_size; i++) {
+ if (!(i % 16))
+ printf("\n");
+ printf(" %2.2x", tpm_command[i]);
+ }
+ printf("\n");
+ return empty_string_data;
+ }
+
+ /*
+ * The tpm_command is ready, let's read it.
+ *
+ * First we read the FIFO payload header, to see how much data to
+ * expect. The header size is fixed to six bytes, the total payload
+ * size is stored in network order in the last four bytes of the
+ * header.
+ */
+ FtdiReadReg(TPM_DATA_FIFO_REG, HEADER_SIZE, response);
+ handled_so_far = HEADER_SIZE;
+
+ /* Figure out the total payload size. */
+ memcpy(&payload_size, response + 2, sizeof(payload_size));
+ payload_size = be32toh(payload_size);
+
+ if (ftdi_trace_enabled)
+ printf("%s response size %d\n\n", message, payload_size);
+ else
+ SpinSpinner();
+
+ if (payload_size > MAX_RESPONSE_SIZE)
+ return empty_string_data;
+ /*
+ * Let's read all but the last byte in the FIFO to make sure the
+ * status register is showing correct flow control bits: 'more data'
+ * until the last byte and then 'no more data' once the last byte is
+ * read.
+ */
+ payload_size = payload_size - 1;
+ do {
+ uint32_t transaction_size;
+ uint32_t burst_count = GetBurstCount();
+
+ if (burst_count > 64)
+ burst_count = 64;
+
+ transaction_size = payload_size - handled_so_far;
+ if (transaction_size > burst_count)
+ transaction_size = burst_count;
+
+ if (transaction_size) {
+ FtdiReadReg(TPM_DATA_FIFO_REG, transaction_size,
+ response + handled_so_far);
+ handled_so_far += transaction_size;
+ }
+ } while (handled_so_far != payload_size);
+
+ /* Verify that there is still data to come. */
+ ReadTpmSts(&status);
+ if ((status & expected_status_bits) != expected_status_bits) {
+ fprintf(stderr, "unexpected status %#x\n", status);
+ return empty_string_data;
+ }
+
+ FtdiReadReg(TPM_DATA_FIFO_REG, 1, response + handled_so_far);
+
+ /* Verify that 'data available' is not asseretd any more. */
+ ReadTpmSts(&status);
+ if ((status & expected_status_bits) != stsValid) {
+ fprintf(stderr, "unexpected status %#x\n", status);
+ return empty_string_data;
+ }
+
+ /* Move the TPM back to idle state. */
+ WriteTpmSts(commandReady);
+
+ handled_so_far++;
+
+ return (struct swig_string_data) {
+ .size = handled_so_far, .data = response};
+}
diff --git a/test/tpm_test/ftdi_spi_tpm.h b/test/tpm_test/ftdi_spi_tpm.h
new file mode 100644
index 0000000000..5393d4cd86
--- /dev/null
+++ b/test/tpm_test/ftdi_spi_tpm.h
@@ -0,0 +1,25 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __EC_TEST_TPM_TEST_FTDI_SPI_TPM_H
+#define __EC_TEST_TPM_TEST_FTDI_SPI_TPM_H
+
+#include "mpsse.h"
+
+/*
+ * This structure allows to convert string representation between C and
+ * Python.
+ */
+struct swig_string_data {
+ int size;
+ uint8_t *data;
+};
+
+int FtdiSpiInit(uint32_t freq, int enable_debug);
+void FtdiStop(void);
+struct swig_string_data FtdiSendCommandAndWait(char *tpm_command,
+ int command_size);
+
+#endif /* ! __EC_TEST_TPM_TEST_FTDI_SPI_TPM_H */
diff --git a/test/tpm_test/ftdi_spi_tpm.i b/test/tpm_test/ftdi_spi_tpm.i
new file mode 100644
index 0000000000..c9cc2fc535
--- /dev/null
+++ b/test/tpm_test/ftdi_spi_tpm.i
@@ -0,0 +1,50 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+%module ftdi_spi_tpm
+typedef unsigned uint32_t;
+typedef unsigned char uint8_t;
+
+%{
+typedef struct swig_string_data
+{
+ int size;
+ char *data;
+} swig_string_data;
+
+extern int FtdiSpiInit(uint32_t freq, int enable_debug);
+extern void FtdiStop(void);
+extern swig_string_data FtdiSendCommandAndWait(char *tpm_command,
+ int command_size);
+%}
+
+%typemap(in) (char *tpm_command, int command_size)
+{
+ if(!PyString_Check($input))
+ {
+ PyErr_SetString(PyExc_ValueError, "String value required");
+ return NULL;
+ }
+
+ $1 = PyString_AsString($input);
+ $2 = PyString_Size($input);
+}
+
+%typemap(out) swig_string_data
+{
+ $result = PyString_FromStringAndSize($1.data, $1.size);
+ free($1.data);
+}
+
+typedef struct swig_string_data
+{
+ int size;
+ char *data;
+} swig_string_data;
+
+extern int FtdiSpiInit(uint32_t freq, int enable_debug);
+extern void FtdiStop(void);
+extern swig_string_data FtdiSendCommandAndWait(char *tpm_command,
+ int command_size);
diff --git a/test/tpm_test/mpsse.c b/test/tpm_test/mpsse.c
new file mode 100644
index 0000000000..9b8d97e8e3
--- /dev/null
+++ b/test/tpm_test/mpsse.c
@@ -0,0 +1,723 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Based on Craig Heffner's version of Dec 27 2011, published on
+ * https://github.com/devttys0/libmpsse
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if LIBFTDI1 == 1
+#include <libftdi1/ftdi.h>
+#else
+#include <ftdi.h>
+#endif
+
+#include "mpsse.h"
+#include "support.h"
+
+/* FTDI interfaces */
+enum interface {
+ IFACE_ANY = INTERFACE_ANY,
+ IFACE_A = INTERFACE_A,
+ IFACE_B = INTERFACE_B,
+ IFACE_C = INTERFACE_C,
+ IFACE_D = INTERFACE_D
+};
+
+enum mpsse_commands {
+ INVALID_COMMAND = 0xAB,
+ ENABLE_ADAPTIVE_CLOCK = 0x96,
+ DISABLE_ADAPTIVE_CLOCK = 0x97,
+ ENABLE_3_PHASE_CLOCK = 0x8C,
+ DISABLE_3_PHASE_CLOCK = 0x8D,
+ TCK_X5 = 0x8A,
+ TCK_D5 = 0x8B,
+ CLOCK_N_CYCLES = 0x8E,
+ CLOCK_N8_CYCLES = 0x8F,
+ PULSE_CLOCK_IO_HIGH = 0x94,
+ PULSE_CLOCK_IO_LOW = 0x95,
+ CLOCK_N8_CYCLES_IO_HIGH = 0x9C,
+ CLOCK_N8_CYCLES_IO_LOW = 0x9D,
+ TRISTATE_IO = 0x9E,
+};
+
+/* Common clock rates */
+enum clock_rates {
+ ONE_HUNDRED_KHZ = 100000,
+ FOUR_HUNDRED_KHZ = 400000,
+ ONE_MHZ = 1000000,
+ TWO_MHZ = 2000000,
+ FIVE_MHZ = 5000000,
+ SIX_MHZ = 6000000,
+ TEN_MHZ = 10000000,
+ TWELVE_MHZ = 12000000,
+ FIFTEEN_MHZ = 15000000,
+ THIRTY_MHZ = 30000000,
+ SIXTY_MHZ = 60000000
+};
+
+#define NULL_CONTEXT_ERROR_MSG "NULL MPSSE context pointer!"
+#define SPI_TRANSFER_SIZE 512
+#define SPI_RW_SIZE (63 * 1024)
+#define SETUP_DELAY 25000
+#define LATENCY_MS 2
+#define USB_TIMEOUT 120000
+#define CHUNK_SIZE 65535
+#define MAX_SETUP_COMMANDS 10
+
+/* SK and CS are high, GPIO1 is reset on the FPGA hookup, all others low */
+#define DEFAULT_PORT (SK | CS | GPIO1)
+/* SK/DO/CS and GPIOs are outputs, DI is an input */
+#define DEFAULT_TRIS (SK | DO | CS | GPIO0 | GPIO1 | GPIO2 | GPIO3)
+
+static struct vid_pid {
+ int vid;
+ int pid;
+ char *description;
+} supported_devices[] = {
+ {
+ 0x0403, 0x6010, "FT2232 Future Technology Devices International, Ltd"},
+ {
+ 0x0403, 0x6011, "FT4232 Future Technology Devices International, Ltd"},
+ {
+ 0x0403, 0x6014,
+ "FT232H Future Technology Devices International, Ltd"},
+ /* These devices are based on FT2232 chips, but have not been tested. */
+ {
+ 0x0403, 0x8878, "Bus Blaster v2 (channel A)"}, {
+ 0x0403, 0x8879, "Bus Blaster v2 (channel B)"}, {
+ 0x0403, 0xBDC8, "Turtelizer JTAG/RS232 Adapter A"}, {
+ 0x0403, 0xCFF8, "Amontec JTAGkey"}, {
+ 0x0403, 0x8A98, "TIAO Multi Protocol Adapter"}, {
+ 0x15BA, 0x0003, "Olimex Ltd. OpenOCD JTAG"}, {
+ 0x15BA, 0x0004, "Olimex Ltd. OpenOCD JTAG TINY"}, {
+ 0, 0, NULL}
+};
+
+/*
+ * Enables or disables flushing of the FTDI chip's RX buffers after each read
+ * operation. Flushing is disable by default.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @tf - Set to 1 to enable flushing, or 0 to disable flushing.
+ *
+ * Returns void.
+ */
+static void FlushAfterRead(struct mpsse_context *mpsse, int tf)
+{
+ mpsse->flush_after_read = tf;
+}
+
+/*
+ * Enable / disable internal loopback.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @enable - Zero to disable loopback, 1 to enable loopback.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+static int SetLoopback(struct mpsse_context *mpsse, int enable)
+{
+ unsigned char buf[1] = { 0 };
+ int retval = MPSSE_FAIL;
+
+ if (is_valid_context(mpsse)) {
+ if (enable)
+ buf[0] = LOOPBACK_START;
+ else
+ buf[0] = LOOPBACK_END;
+
+ retval = raw_write(mpsse, buf, 1);
+ }
+
+ return retval;
+}
+
+/*
+ * Sets the appropriate divisor for the desired clock frequency.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @freq - Desired clock frequency in hertz.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+static int SetClock(struct mpsse_context *mpsse, uint32_t freq)
+{
+ int retval = MPSSE_FAIL;
+ uint32_t system_clock = 0;
+ uint16_t divisor = 0;
+ unsigned char buf[CMD_SIZE] = { 0 };
+
+ /*
+ * Do not call is_valid_context() here, as the FTDI chip may not be
+ * completely configured when SetClock is called
+ */
+ if (!mpsse)
+ return retval;
+
+ if (freq > SIX_MHZ) {
+ buf[0] = TCK_X5;
+ system_clock = SIXTY_MHZ;
+ } else {
+ buf[0] = TCK_D5;
+ system_clock = TWELVE_MHZ;
+ }
+
+ if (raw_write(mpsse, buf, 1) == MPSSE_OK) {
+ if (freq <= 0)
+ divisor = 0xFFFF;
+ else
+ divisor = freq2div(system_clock, freq);
+
+ buf[0] = TCK_DIVISOR;
+ buf[1] = (divisor & 0xFF);
+ buf[2] = ((divisor >> 8) & 0xFF);
+
+ if (raw_write(mpsse, buf, 3) == MPSSE_OK) {
+ mpsse->clock = div2freq(system_clock, divisor);
+ retval = MPSSE_OK;
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * Sets the appropriate transmit and receive commands based on the requested
+ * mode and byte order.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @endianness - MPSSE_MSB or MPSSE_LSB.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+static int SetMode(struct mpsse_context *mpsse, int endianness)
+{
+ int retval = MPSSE_OK, i = 0, setup_commands_size = 0;
+ unsigned char buf[CMD_SIZE] = { 0 };
+ unsigned char setup_commands[CMD_SIZE * MAX_SETUP_COMMANDS] = { 0 };
+
+ /*
+ * Do not call is_valid_context() here, as the FTDI chip may not be
+ * completely configured when SetMode is called
+ */
+ if (!mpsse)
+ return MPSSE_FAIL;
+
+ /* Read and write commands need to include endianness */
+ mpsse->tx = MPSSE_DO_WRITE | endianness;
+ mpsse->rx = MPSSE_DO_READ | endianness;
+ mpsse->txrx = MPSSE_DO_WRITE | MPSSE_DO_READ | endianness;
+
+ /*
+ * Clock, data out, chip select pins are outputs; all others are
+ * inputs.
+ */
+ mpsse->tris = DEFAULT_TRIS;
+
+ /* Clock and chip select pins idle high; all others are low */
+ mpsse->pidle = mpsse->pstart = mpsse->pstop = DEFAULT_PORT;
+
+ /* During reads and writes the chip select pin is brought low */
+ mpsse->pstart &= ~CS;
+
+ /* Disable FTDI internal loopback */
+ SetLoopback(mpsse, 0);
+
+ /* Ensure adaptive clock is disabled */
+ setup_commands[setup_commands_size++] = DISABLE_ADAPTIVE_CLOCK;
+
+ switch (mpsse->mode) {
+ case SPI0:
+ /* SPI mode 0 clock idles low */
+ mpsse->pidle &= ~SK;
+ mpsse->pstart &= ~SK;
+ mpsse->pstop &= ~SK;
+
+ /*
+ * SPI mode 0 propogates data on the falling edge and read
+ * data on the rising edge of the clock
+ */
+ mpsse->tx |= MPSSE_WRITE_NEG;
+ mpsse->rx &= ~MPSSE_READ_NEG;
+ mpsse->txrx |= MPSSE_WRITE_NEG;
+ mpsse->txrx &= ~MPSSE_READ_NEG;
+ break;
+ default:
+ fprintf(stderr, "%s:%d attempt to set an unsupported mode %d\n",
+ __func__, __LINE__, mpsse->mode);
+ retval = MPSSE_FAIL;
+ }
+
+ /* Send any setup commands to the chip */
+ if ((retval == MPSSE_OK) && (setup_commands_size > 0))
+ retval = raw_write(mpsse, setup_commands, setup_commands_size);
+
+ if (retval == MPSSE_OK) {
+ /* Set the idle pin states */
+ set_bits_low(mpsse, mpsse->pidle);
+
+ /* All GPIO pins are outputs, set low */
+ mpsse->trish = 0xFF;
+ mpsse->gpioh = 0x00;
+
+ buf[i++] = SET_BITS_HIGH;
+ buf[i++] = mpsse->gpioh;
+ buf[i++] = mpsse->trish;
+
+ retval = raw_write(mpsse, buf, i);
+ }
+
+ return retval;
+}
+
+/*
+ * Open device by VID/PID/index
+ *
+ * @vid - Device vendor ID.
+ * @pid - Device product ID.
+ * @freq - Clock frequency to use for the specified mode.
+ * @endianness - Specifies how data is clocked in/out (MSB, LSB).
+ * @interface - FTDI interface to use (IFACE_A - IFACE_D).
+ * @description - Device product description (set to NULL if not needed).
+ * @serial - Device serial number (set to NULL if not needed).
+ * @index - Device index (set to 0 if not needed).
+ *
+ * Returns a pointer to an MPSSE context structure.
+ * On success, mpsse->open will be set to 1.
+ * On failure, mpsse->open will be set to 0.
+ */
+static struct mpsse_context *OpenIndex(int vid,
+ int pid,
+ int freq,
+ int endianness,
+ int interface,
+ const char *description,
+ const char *serial, int index)
+{
+ int status = 0;
+ struct mpsse_context *mpsse = NULL;
+ enum modes mode = SPI0; /* Let's use this mode at all times. */
+
+ mpsse = malloc(sizeof(struct mpsse_context));
+ if (!mpsse)
+ return NULL;
+
+ memset(mpsse, 0, sizeof(struct mpsse_context));
+
+ /* Legacy; flushing is no longer needed, so disable it by default. */
+ FlushAfterRead(mpsse, 0);
+
+ /* ftdilib initialization */
+ if (ftdi_init(&mpsse->ftdi)) {
+ fprintf(stderr, "%s:%d failed to initialize FTDI\n",
+ __func__, __LINE__);
+ free(mpsse);
+ return NULL;
+ }
+
+ mpsse->ftdi_initialized = 1;
+
+ /* Set the FTDI interface */
+ ftdi_set_interface(&mpsse->ftdi, interface);
+
+ /* Try opening the specified device */
+ if (ftdi_usb_open_desc_index
+ (&mpsse->ftdi, vid, pid, description, serial, index)) {
+ Close(mpsse);
+ return NULL;
+ }
+
+ mpsse->mode = mode;
+ mpsse->vid = vid;
+ mpsse->pid = pid;
+ mpsse->status = STOPPED;
+ mpsse->endianness = endianness;
+ mpsse->xsize = SPI_RW_SIZE;
+
+ status |= ftdi_usb_reset(&mpsse->ftdi);
+ status |= ftdi_set_latency_timer(&mpsse->ftdi, LATENCY_MS);
+ status |= ftdi_write_data_set_chunksize(&mpsse->ftdi, CHUNK_SIZE);
+ status |= ftdi_read_data_set_chunksize(&mpsse->ftdi, CHUNK_SIZE);
+ status |= ftdi_set_bitmode(&mpsse->ftdi, 0, BITMODE_RESET);
+
+ if (status) {
+ fprintf(stderr,
+ "%s:%d failed setting basic config for %4.4x:%4.4x\n",
+ __func__, __LINE__, vid, pid);
+ Close(mpsse);
+ return NULL;
+ }
+ /* Set the read and write timeout periods */
+ set_timeouts(mpsse, USB_TIMEOUT);
+
+ ftdi_set_bitmode(&mpsse->ftdi, 0, BITMODE_MPSSE);
+
+ if ((SetClock(mpsse, freq) != MPSSE_OK)
+ || (SetMode(mpsse, endianness) != MPSSE_OK)) {
+ fprintf(stderr,
+ "%s:%d failed setting clock/mode for %4.4x:%4.4x\n",
+ __func__, __LINE__, vid, pid);
+ Close(mpsse);
+ return NULL;
+ }
+
+ mpsse->open = 1;
+
+ /* Give the chip a few mS to initialize */
+ usleep(SETUP_DELAY);
+
+ /*
+ * Not all FTDI chips support all the commands that SetMode may have
+ * sent. This clears out any errors from unsupported commands that
+ * might have been sent during set up.
+ */
+ ftdi_usb_purge_buffers(&mpsse->ftdi);
+
+ return mpsse;
+}
+
+/*
+ * Opens and initializes the first FTDI device found.
+ *
+ * @freq - Clock frequency to use for the specified mode.
+ * @endianness - Specifies how data is clocked in/out (MSB, LSB).
+ * @serial - Serial number of the USB device (NULL if not needed).
+ *
+ * Returns a pointer to an MPSSE context structure.
+ * On success, mpsse->open will be set to 1.
+ * On failure, mpsse->open will be set to 0.
+ */
+struct mpsse_context *MPSSE(int freq, int endianness, const char *serial)
+{
+ int i = 0;
+ struct mpsse_context *mpsse = NULL;
+
+ for (i = 0; supported_devices[i].vid != 0; i++) {
+ mpsse = OpenIndex(supported_devices[i].vid,
+ supported_devices[i].pid, freq, endianness,
+ IFACE_A, NULL, serial, 0);
+ if (!mpsse)
+ continue;
+
+ if (mpsse->open) {
+ mpsse->description = supported_devices[i].description;
+ break;
+ }
+ /*
+ * If there is another device still left to try, free
+ * the context pointer and try again
+ */
+ if (supported_devices[i + 1].vid != 0) {
+ Close(mpsse);
+ mpsse = NULL;
+ }
+ }
+
+ return mpsse;
+}
+
+/*
+ * Closes the device, deinitializes libftdi, and frees the MPSSE context
+ * pointer.
+ *
+ * @mpsse - MPSSE context pointer.
+ *
+ * Returns void.
+ */
+
+void Close(struct mpsse_context *mpsse)
+{
+ if (!mpsse)
+ return;
+
+ if (mpsse->open) {
+ ftdi_usb_close(&mpsse->ftdi);
+ ftdi_set_bitmode(&mpsse->ftdi, 0, BITMODE_RESET);
+ }
+
+ if (mpsse->ftdi_initialized)
+ ftdi_deinit(&mpsse->ftdi);
+
+ free(mpsse);
+}
+
+/*
+ * Retrieves the last error string from libftdi.
+ *
+ * @mpsse - MPSSE context pointer.
+ *
+ * Returns a pointer to the last error string.
+ */
+const char *ErrorString(struct mpsse_context *mpsse)
+{
+ if (mpsse)
+ return ftdi_get_error_string(&mpsse->ftdi);
+
+ return NULL_CONTEXT_ERROR_MSG;
+}
+
+/*
+ * Send data start condition.
+ *
+ * @mpsse - MPSSE context pointer.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+int Start(struct mpsse_context *mpsse)
+{
+ int status;
+
+ if (!is_valid_context(mpsse)) {
+ mpsse->status = STOPPED;
+ return MPSSE_FAIL;
+ }
+
+ /* Set the start condition */
+ status = set_bits_low(mpsse, mpsse->pstart);
+
+ if (status == MPSSE_OK)
+ mpsse->status = STARTED;
+
+ return status;
+}
+
+/*
+ * Send data out via the selected serial protocol.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @data - Buffer of data to send.
+ * @size - Size of data.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+int Write(struct mpsse_context *mpsse, char *data, int size)
+{
+ int n = 0;
+
+ if (!is_valid_context(mpsse))
+ return MPSSE_FAIL;
+
+ if (!mpsse->mode)
+ return MPSSE_FAIL;
+
+ while (n < size) {
+ unsigned char *buf;
+ int retval, buf_size, txsize;
+
+ txsize = size - n;
+ if (txsize > mpsse->xsize)
+ txsize = mpsse->xsize;
+
+ buf = build_block_buffer(mpsse, mpsse->tx,
+ (unsigned char *)(data + n),
+ txsize, &buf_size);
+ if (!buf)
+ return MPSSE_FAIL;
+
+ retval = raw_write(mpsse, buf, buf_size);
+ n += txsize;
+ free(buf);
+
+ if (retval != MPSSE_OK)
+ return retval;
+
+ }
+
+ return MPSSE_OK;
+}
+
+/* Performs a read. For internal use only; see Read() and ReadBits(). */
+static char *InternalRead(struct mpsse_context *mpsse, int size)
+{
+ unsigned char *buf;
+ int n = 0;
+
+ if (!is_valid_context(mpsse))
+ return NULL;
+
+ if (!mpsse->mode)
+ return NULL;
+ buf = malloc(size);
+
+ if (!buf)
+ return NULL;
+
+ while (n < size) {
+ int rxsize, data_size, retval;
+ unsigned char *data;
+ unsigned char sbuf[SPI_RW_SIZE] = { 0 };
+
+ rxsize = size - n;
+ if (rxsize > mpsse->xsize)
+ rxsize = mpsse->xsize;
+
+ data = build_block_buffer(mpsse, mpsse->rx,
+ sbuf, rxsize, &data_size);
+ if (!data) {
+ free(buf);
+ return NULL;
+ }
+
+ retval = raw_write(mpsse, data, data_size);
+ free(data);
+
+ if (retval != MPSSE_OK) {
+ free(buf);
+ return NULL;
+ }
+ n += raw_read(mpsse, buf + n, rxsize);
+ }
+
+ return (char *)buf;
+}
+
+/*
+ * Reads data over the selected serial protocol.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @size - Number of bytes to read.
+ *
+ * Returns a pointer to the read data on success.
+ * Returns NULL on failure.
+ */
+char *Read(struct mpsse_context *mpsse, int size)
+{
+ char *buf = NULL;
+
+ buf = InternalRead(mpsse, size);
+ return buf;
+}
+
+/*
+ * Reads and writes data over the selected serial protocol (SPI only).
+ *
+ * @mpsse - MPSSE context pointer.
+ * @data - Buffer containing bytes to write.
+ * @size - Number of bytes to transfer.
+ *
+ * Returns a pointer to the read data on success.
+ * Returns NULL on failure.
+ */
+char *Transfer(struct mpsse_context *mpsse, char *data, int size)
+{
+ unsigned char *txdata = NULL, *buf = NULL;
+ int n = 0, data_size = 0, rxsize = 0, retval = MPSSE_OK;
+
+ if (!is_valid_context(mpsse))
+ return NULL;
+
+ buf = malloc(size);
+ if (!buf)
+ return NULL;
+
+ while (n < size) {
+ /*
+ * When sending and recieving, FTDI chips don't seem to like
+ * large data blocks. Limit the size of each block to
+ * SPI_TRANSFER_SIZE
+ */
+ rxsize = size - n;
+ if (rxsize > SPI_TRANSFER_SIZE)
+ rxsize = SPI_TRANSFER_SIZE;
+
+ txdata = build_block_buffer(mpsse, mpsse->txrx,
+ (unsigned char *)(data + n),
+ rxsize, &data_size);
+ if (!txdata) {
+ retval = MPSSE_FAIL;
+ break;
+ }
+ retval = raw_write(mpsse, txdata, data_size);
+ free(txdata);
+
+ if (retval != MPSSE_OK)
+ break;
+
+ n += raw_read(mpsse, (buf + n), rxsize);
+ }
+
+ if (retval != MPSSE_OK)
+ return NULL;
+
+ return (char *)buf;
+}
+
+/*
+ * Send data stop condition.
+ *
+ * @mpsse - MPSSE context pointer.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+int Stop(struct mpsse_context *mpsse)
+{
+ int retval = MPSSE_OK;
+
+ if (is_valid_context(mpsse)) {
+ /* Send the stop condition */
+ retval |= set_bits_low(mpsse, mpsse->pstop);
+
+ if (retval == MPSSE_OK) {
+ /* Restore the pins to their idle states */
+ retval |= set_bits_low(mpsse, mpsse->pidle);
+ }
+
+ mpsse->status = STOPPED;
+ } else {
+ retval = MPSSE_FAIL;
+ mpsse->status = STOPPED;
+ }
+
+ return retval;
+}
+
+/*
+ * Sets the specified pin high.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @pin - Pin number to set high.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+int PinHigh(struct mpsse_context *mpsse, int pin)
+{
+ int retval = MPSSE_FAIL;
+
+ if (is_valid_context(mpsse))
+ retval = gpio_write(mpsse, pin, HIGH);
+
+ return retval;
+}
+
+/*
+ * Sets the specified pin low.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @pin - Pin number to set low.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+int PinLow(struct mpsse_context *mpsse, int pin)
+{
+ int retval = MPSSE_FAIL;
+
+ if (is_valid_context(mpsse))
+ retval = gpio_write(mpsse, pin, LOW);
+
+ return retval;
+}
diff --git a/test/tpm_test/mpsse.h b/test/tpm_test/mpsse.h
new file mode 100644
index 0000000000..2925dfe27a
--- /dev/null
+++ b/test/tpm_test/mpsse.h
@@ -0,0 +1,45 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Based on Craig Heffner's version of Dec 27 2011, published on
+ * https://github.com/devttys0/libmpsse
+ */
+
+#ifndef __EC_TEST_TPM_TEST_MPSSE_H
+#define __EC_TEST_TPM_TEST_MPSSE_H
+
+#define MPSSE_OK 0
+#define MPSSE_FAIL -1
+
+#define MSB 0x00
+#define LSB 0x08
+
+enum gpio_pins {
+ GPIOL0 = 0,
+ GPIOL1 = 1,
+ GPIOL2 = 2,
+ GPIOL3 = 3,
+ GPIOH0 = 4,
+ GPIOH1 = 5,
+ GPIOH2 = 6,
+ GPIOH3 = 7,
+ GPIOH4 = 8,
+ GPIOH5 = 9,
+ GPIOH6 = 10,
+ GPIOH7 = 11
+};
+
+struct mpsse_context;
+
+int Write(struct mpsse_context *mpsse, char *data, int size);
+int Stop(struct mpsse_context *mpsse);
+char *Transfer(struct mpsse_context *mpsse, char *data, int size);
+char *Read(struct mpsse_context *mpsse, int size);
+struct mpsse_context *MPSSE(int freq, int endianness, const char *serial);
+void Close(struct mpsse_context *mpsse);
+int PinHigh(struct mpsse_context *mpsse, int pin);
+int PinLow(struct mpsse_context *mpsse, int pin);
+int Start(struct mpsse_context *mpsse);
+
+#endif /* ! __EC_TEST_TPM_TEST_MPSSE_H */
diff --git a/test/tpm_test/support.c b/test/tpm_test/support.c
new file mode 100644
index 0000000000..fbb4d0b6ab
--- /dev/null
+++ b/test/tpm_test/support.c
@@ -0,0 +1,218 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Based on Craig Heffner's version of Dec 27 2011, published on
+ * https://github.com/devttys0/libmpsse
+ *
+ * Internal functions used by libmpsse.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#if LIBFTDI1 == 1
+#include <libftdi1/ftdi.h>
+#else
+#include <ftdi.h>
+#endif
+
+#include "support.h"
+
+/* Write data to the FTDI chip */
+int raw_write(struct mpsse_context *mpsse, unsigned char *buf, int size)
+{
+ int retval = MPSSE_FAIL;
+
+ if (mpsse->mode && (ftdi_write_data(&mpsse->ftdi, buf, size) == size))
+ retval = MPSSE_OK;
+
+ return retval;
+}
+
+/* Read data from the FTDI chip */
+int raw_read(struct mpsse_context *mpsse, unsigned char *buf, int size)
+{
+ int n = 0, r = 0;
+
+ if (!mpsse->mode)
+ return 0;
+
+ while (n < size) {
+ r = ftdi_read_data(&mpsse->ftdi, buf, size);
+ if (r < 0)
+ break;
+ n += r;
+ }
+
+ if (mpsse->flush_after_read) {
+ /*
+ * Make sure the buffers are cleared after a read or
+ * subsequent reads may fail. Is this needed anymore?
+ * It slows down repetitive read operations by ~8%.
+ */
+ ftdi_usb_purge_rx_buffer(&mpsse->ftdi);
+ }
+
+ return n;
+}
+
+/* Sets the read and write timeout periods for bulk usb data transfers. */
+void set_timeouts(struct mpsse_context *mpsse, int timeout)
+{
+ if (mpsse->mode) {
+ mpsse->ftdi.usb_read_timeout = timeout;
+ mpsse->ftdi.usb_write_timeout = timeout;
+ }
+}
+
+/* Convert a frequency to a clock divisor */
+uint16_t freq2div(uint32_t system_clock, uint32_t freq)
+{
+ return (((system_clock / freq) / 2) - 1);
+}
+
+/* Convert a clock divisor to a frequency */
+uint32_t div2freq(uint32_t system_clock, uint16_t div)
+{
+ return (system_clock / ((1 + div) * 2));
+}
+
+/* Builds a buffer of commands + data blocks */
+unsigned char *build_block_buffer(struct mpsse_context *mpsse,
+ uint8_t cmd,
+ unsigned char *data, int size, int *buf_size)
+{
+ unsigned char *buf = NULL;
+ int i = 0, j = 0, k = 0, dsize = 0, num_blocks = 0, total_size =
+ 0, xfer_size = 0;
+ uint16_t rsize = 0;
+
+ *buf_size = 0;
+
+ /* Data block size is 1 in I2C, or when in bitmode */
+ if (mpsse->mode == I2C || (cmd & MPSSE_BITMODE))
+ xfer_size = 1;
+ else
+ xfer_size = mpsse->xsize;
+
+ num_blocks = (size / xfer_size);
+ if (size % xfer_size)
+ num_blocks++;
+
+ /*
+ * The total size of the data will be the data size + the write
+ * command
+ */
+ total_size = size + (CMD_SIZE * num_blocks);
+
+ buf = malloc(total_size);
+ if (!buf)
+ return NULL;
+
+ memset(buf, 0, total_size);
+
+ for (j = 0; j < num_blocks; j++) {
+ dsize = size - k;
+ if (dsize > xfer_size)
+ dsize = xfer_size;
+
+ /* The reported size of this block is block size - 1 */
+ rsize = dsize - 1;
+
+ /* Copy in the command for this block */
+ buf[i++] = cmd;
+ buf[i++] = (rsize & 0xFF);
+ if (!(cmd & MPSSE_BITMODE))
+ buf[i++] = ((rsize >> 8) & 0xFF);
+
+ /* On a write, copy the data to transmit after the command */
+ if (cmd == mpsse->tx || cmd == mpsse->txrx) {
+
+ memcpy(buf + i, data + k, dsize);
+
+ /* i == offset into buf */
+ i += dsize;
+ /* k == offset into data */
+ k += dsize;
+ }
+ }
+
+ *buf_size = i;
+
+ return buf;
+}
+
+/* Set the low bit pins high/low */
+int set_bits_low(struct mpsse_context *mpsse, int port)
+{
+ char buf[CMD_SIZE] = { 0 };
+
+ buf[0] = SET_BITS_LOW;
+ buf[1] = port;
+ buf[2] = mpsse->tris;
+
+ return raw_write(mpsse, (unsigned char *)&buf, sizeof(buf));
+}
+
+/* Set the high bit pins high/low */
+int set_bits_high(struct mpsse_context *mpsse, int port)
+{
+ char buf[CMD_SIZE] = { 0 };
+
+ buf[0] = SET_BITS_HIGH;
+ buf[1] = port;
+ buf[2] = mpsse->trish;
+
+ return raw_write(mpsse, (unsigned char *)&buf, sizeof(buf));
+}
+
+/* Set the GPIO pins high/low */
+int gpio_write(struct mpsse_context *mpsse, int pin, int direction)
+{
+ int retval = MPSSE_FAIL;
+
+ /*
+ * The first four pins can't be changed unless we are in a stopped
+ * status
+ */
+ if (pin < NUM_GPIOL_PINS && mpsse->status == STOPPED) {
+ /* Convert pin number (0-3) to the corresponding pin bit */
+ pin = (GPIO0 << pin);
+
+ if (direction == HIGH) {
+ mpsse->pstart |= pin;
+ mpsse->pidle |= pin;
+ mpsse->pstop |= pin;
+ } else {
+ mpsse->pstart &= ~pin;
+ mpsse->pidle &= ~pin;
+ mpsse->pstop &= ~pin;
+ }
+
+ retval = set_bits_low(mpsse, mpsse->pstop);
+ } else if (pin >= NUM_GPIOL_PINS && pin < NUM_GPIO_PINS) {
+ /* Convert pin number (4 - 11) to the corresponding pin bit */
+ pin -= NUM_GPIOL_PINS;
+
+ if (direction == HIGH)
+ mpsse->gpioh |= (1 << pin);
+ else
+ mpsse->gpioh &= ~(1 << pin);
+
+ retval = set_bits_high(mpsse, mpsse->gpioh);
+ }
+
+ return retval;
+}
+
+/* Checks if a given MPSSE context is valid. */
+int is_valid_context(struct mpsse_context *mpsse)
+{
+ int retval = 0;
+
+ if (mpsse != NULL && mpsse->open)
+ retval = 1;
+
+ return retval;
+}
diff --git a/test/tpm_test/support.h b/test/tpm_test/support.h
new file mode 100644
index 0000000000..77316582cc
--- /dev/null
+++ b/test/tpm_test/support.h
@@ -0,0 +1,87 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Based on Craig Heffner's version of Dec 27 2011, published on
+ * https://github.com/devttys0/libmpsse
+ */
+
+#ifndef __EC_TEST_TPM_TEST_SUPPORT_H
+#define __EC_TEST_TPM_TEST_SUPPORT_H
+
+#include "mpsse.h"
+
+#define CMD_SIZE 3
+#define NUM_GPIOL_PINS 4
+#define NUM_GPIO_PINS 12
+#define LOW 0
+#define HIGH 1
+
+/* Supported MPSSE modes */
+enum modes {
+ SPI0 = 1,
+ SPI1 = 2,
+ SPI2 = 3,
+ SPI3 = 4,
+ I2C = 5,
+ GPIO = 6,
+ BITBANG = 7,
+};
+
+enum low_bits_status {
+ STARTED,
+ STOPPED
+};
+
+enum pins {
+ SK = 1,
+ DO = 2,
+ DI = 4,
+ CS = 8,
+ GPIO0 = 16,
+ GPIO1 = 32,
+ GPIO2 = 64,
+ GPIO3 = 128
+};
+
+struct mpsse_context {
+ char *description;
+ struct ftdi_context ftdi;
+ enum modes mode;
+ enum low_bits_status status;
+ int flush_after_read;
+ int vid;
+ int pid;
+ int clock;
+ int xsize;
+ int open;
+ int ftdi_initialized;
+ int endianness;
+ uint8_t tris;
+ uint8_t pstart;
+ uint8_t pstop;
+ uint8_t pidle;
+ uint8_t gpioh;
+ uint8_t trish;
+ uint8_t bitbang;
+ uint8_t tx;
+ uint8_t rx;
+ uint8_t txrx;
+ uint8_t tack;
+ uint8_t rack;
+};
+
+int raw_write(struct mpsse_context *mpsse, unsigned char *buf, int size);
+int raw_read(struct mpsse_context *mpsse, unsigned char *buf, int size);
+void set_timeouts(struct mpsse_context *mpsse, int timeout);
+uint16_t freq2div(uint32_t system_clock, uint32_t freq);
+uint32_t div2freq(uint32_t system_clock, uint16_t div);
+unsigned char *build_block_buffer(struct mpsse_context *mpsse,
+ uint8_t cmd,
+ unsigned char *data, int size, int *buf_size);
+int set_bits_high(struct mpsse_context *mpsse, int port);
+int set_bits_low(struct mpsse_context *mpsse, int port);
+int gpio_write(struct mpsse_context *mpsse, int pin, int direction);
+int is_valid_context(struct mpsse_context *mpsse);
+
+#endif /* ! __EC_TEST_TPM_TEST_SUPPORT_H */
diff --git a/test/tpm_test/tpmtest.py b/test/tpm_test/tpmtest.py
new file mode 100755
index 0000000000..c2056c01c7
--- /dev/null
+++ b/test/tpm_test/tpmtest.py
@@ -0,0 +1,364 @@
+#!/usr/bin/python
+# Copyright 2015 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Module for testing TPM, using both conventional and extended commands."""
+
+from __future__ import print_function
+
+import os
+import struct
+import sys
+import traceback
+import xml.etree.ElementTree as ET
+
+# Suppressing pylint warning about an import not at the top of the file. The
+# path needs to be set *before* the last import.
+# pylint: disable=C6204
+root_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
+sys.path.append(os.path.join(root_dir, '..', '..', 'build', 'tpm_test'))
+import ftdi_spi_tpm
+
+# Basic crypto operations
+DECRYPT = 0
+ENCRYPT = 1
+
+# Extension command for dcypto testing
+EXT_CMD = 0xbaccd00a
+
+# Extension subcommands for encryption types
+AES = 0
+
+if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
+ cursor_back = '\x1b[1D' # Move one space to the left.
+else:
+ cursor_back = ''
+
+
+class TpmError(Exception):
+ pass
+
+
+class TPM(object):
+ """TPM accessor class.
+
+ Object of this class allows to send valid and extended TPM commands (using
+ the command() method. The wrap_command/unwrap_response methods provide a
+ means of encapsulating extended commands in proper TPM data packets, as well
+ as extracting extended command responses.
+
+ Attributes:
+ _handle: a ftdi_spi_tpm object, a USB/FTDI/SPI driver which allows
+ communicate with a TPM connected over USB dongle.
+ """
+
+ HEADER_FMT = '>H2IH'
+ STARTUP_CMD = '80 01 00 00 00 0c 00 00 01 44 00 00'
+ STARTUP_RSP = ('80 01 00 00 00 0a 00 00 00 00',
+ '80 01 00 00 00 0a 00 00 01 00')
+
+ def __init__(self, freq=800*1000, debug_mode=False):
+ self._handle = ftdi_spi_tpm
+ if not self._handle.FtdiSpiInit(freq, debug_mode):
+ raise TpmError()
+ response = self.command(''.join('%c' % int('0x%s' % x, 16)
+ for x in self.STARTUP_CMD.split()))
+ if ' '.join('%2.2x' % ord(x) for x in response) not in self.STARTUP_RSP:
+ raise TpmError('init failed')
+
+ def validate(self, data_blob, response_mode=False):
+ """Check if a data blob complies with TPM command/response header format."""
+ (tag, size, cmd_code, _) = struct.unpack_from(
+ self.HEADER_FMT, data_blob + ' ')
+ prefix = 'Misformatted blob: '
+ if tag not in (0x8001, 0x8002):
+ raise TpmError(prefix + 'bad tag value 0x%4.4x' % tag)
+ if size != len(data_blob):
+ raise TpmError(prefix + 'size mismatch: header %d, actual %d' %
+ (size, len(data_blob)))
+ if size > 4096:
+ raise TpmError(prefix + 'invalid size %d' % size)
+ if response_mode:
+ return
+ if cmd_code >= 0x11f and cmd_code <= 0x18f:
+ return # This is a valid command
+ if cmd_code == EXT_CMD:
+ return # This is an extension command
+
+ raise TpmError(prefix + 'invalid command code 0x%x' % cmd_code)
+
+ def command(self, cmd_data):
+ # Verify command header
+ self.validate(cmd_data)
+ response = self._handle.FtdiSendCommandAndWait(cmd_data)
+ self.validate(response, response_mode=True)
+ return response
+
+ def wrap_ext_command(self, cmd_code, subcmd_code, cmd_body):
+ return struct.pack(self.HEADER_FMT, 0x8001,
+ len(cmd_body) + struct.calcsize(self.HEADER_FMT),
+ cmd_code, subcmd_code) + cmd_body
+
+ def unwrap_ext_response(self, expected_cmd, expected_subcmd, response):
+ """Verify basic validity and strip off TPM extended command header.
+
+ Get the response generated by the device, as it came off the wire, verify
+ that header fields match expectations, then strip off the extension
+ command header and return the payload to the caller.
+
+ Args:
+ expected_cmd: an int, up to 32 bits, expected value in the
+ command/response field of the header. expected_subcmd, response
+ expected_subcmd: an int, up to 16 bits in size, the extension command
+ this response is supposed to be for.
+ response: a binary string, the actual response received over the wire.
+ Returns:
+ the binary string of the response payload, if validation succeeded.
+ Raises:
+ TpmError: in case there are any validation problems, the error message
+ describes the problem.
+ """
+ header_size = struct.calcsize(self.HEADER_FMT)
+ tag, size, cmd, subcmd = struct.unpack(self.HEADER_FMT,
+ response[:header_size])
+ if tag != 0x8001:
+ raise TpmError('Wrong response tag: %4.4x' % tag)
+ if cmd != expected_cmd:
+ raise TpmError('Unexpected response command field: %8.8x' % cmd)
+ if subcmd != expected_subcmd:
+ raise TpmError('Unexpected response subcommand field: %2.2x' %
+ subcmd)
+ if size != len(response):
+ raise TpmError('Size mismatch: header %d, actual %d' % (
+ size, len(response)))
+ return response[header_size:]
+
+
+def hex_dump(binstr):
+ """Convert string into its hex representation."""
+ dump_lines = ['',]
+ i = 0
+ while i < len(binstr):
+ strsize = min(16, len(binstr) - i)
+ hexstr = ' '.join('%2.2x' % ord(x) for x in binstr[i:i+strsize])
+ dump_lines.append(hexstr)
+ i += strsize
+ dump_lines.append('')
+ return '\n'.join(dump_lines)
+
+
+def get_attribute(tdesc, attr_name, required=True):
+ """Retrieve an attribute value from an XML node.
+
+ Args:
+
+ tdesc: an Element of the ElementTree, a test descriptor containing
+ necessary information to run a single encryption/description
+ session.
+ attr_name: a string, the name of the attribute to retrieve.
+ required: a Boolean, if True - the attribute must be present in the
+ descriptor, otherwise it is considered optional
+ Returns:
+ The attribute value as a string (ascii or binary)
+ Raises:
+ TpmError: on various format errors, or in case a required attribute is not
+ found, the error message describes the problem.
+
+ """
+ # Fields stored in hex format by default.
+ default_hex = ('cipher_text', 'iv', 'key')
+
+ data = tdesc.find(attr_name)
+ if data is None:
+ if required:
+ raise TpmError('node "%s" does not have attribute "%s"' %
+ (tdesc.get('name'), attr_name))
+ return ''
+
+ # Attribute is present, does it have to be decoded from hex?
+ cell_format = data.get('format')
+ if not cell_format:
+ if attr_name in default_hex:
+ cell_format = 'hex'
+ else:
+ cell_format = 'ascii'
+ elif cell_format not in ('hex', 'ascii'):
+ raise TpmError('%s:%s, unrecognizable format "%s"' %
+ (tdesc.get('name'), attr_name, cell_format))
+
+ text = ' '.join(x.strip() for x in data.text.splitlines() if x)
+ if cell_format == 'ascii':
+ return text
+
+ # Drop spaces from hex representation.
+ text = text.replace(' ', '')
+ if len(text) & 3:
+ raise TpmError('%s:%s %swrong hex number size' %
+ (tdesc.get('name'), attr_name, hex_dump(text)))
+ # Convert text to binary
+ value = ''
+ for x in range(len(text)/8):
+ try:
+ value += struct.pack('<I', int('0x%s' % text[8*x:8*(x+1)], 16))
+ except ValueError:
+ raise TpmError('%s:%s %swrong hex value' %
+ (tdesc.get('name'), attr_name, hex_dump(text)))
+ return value
+
+
+class CryptoD(object):
+ """A helper object to contain an encryption scheme description.
+
+ Attributes:
+ subcmd: a 16 bit max integer, the extension subcommand to be used with
+ this encryption scheme.
+ sumbodes: an optional dictionary, the keys are strings, names of the
+ encryption scheme submodes, the values are integers to be included in
+ the appropriate subcommand fields to communicat the submode to the
+ device.
+ """
+
+ def __init__(self, subcommand, submodes=None):
+ self.subcmd = subcommand
+ if not submodes:
+ submodes = {}
+ self.submodes = submodes
+
+SUPPORTED_MODES = {
+ 'AES': CryptoD(AES, {
+ 'ECB': 0,
+ 'CTR': 1,
+ 'CBC': 2,
+ 'GCM': 3
+ }),
+}
+
+
+def crypto_run(node_name, op_type, key, iv, in_text, out_text, tpm, debug_mode):
+ """Perform a basic operation(encrypt or decrypt).
+
+ This function creates an extended command with the requested parameters,
+ sends it to the device, and then compares the response to the expected
+ value.
+
+ Args:
+ node_name: a string, the name of the XML node this data comes from. The
+ format of the name is "<enc type>:<submode> ....", where <enc type> is
+ the major encryption mode (say AED or DES) and submode - a variant of
+ the major scheme, if exists.
+
+ op_type: an int, encodes the operation to perform (encrypt/decrypt), passed
+ directly to the device as a field in the extended command
+ key: a binary string
+ iv: a binary string, might be empty
+ in_text: a binary string, the input of the encrypt/decrypt operation
+ out_text: a binary string, might be empty, the expected output of the
+ operation. Note that it could be shorter than actual output (padded to
+ integer number of blocks), in which case only its length of bytes is
+ compared debug_mode: a Boolean, if True - enables tracing on the console
+ tpm: a TPM object to send extended commands to an initialized TPM
+ debug_mode: a Boolean, if True - this function and the FTDI driver
+ generate debug messated on the console.
+
+ Returns:
+ The actual binary string, result of the operation, if the
+ comparison with the expected value was successful.
+
+ Raises:
+ TpmError: in case there were problems parsing the node name, or verifying
+ the operation results.
+ """
+ mode_name, submode_name = node_name.split(':')
+ submode_name = submode_name[:3].upper()
+
+ mode = SUPPORTED_MODES.get(mode_name.upper())
+ if not mode:
+ raise TpmError('unrecognizable mode in node "%s"' % node_name)
+
+ submode = mode.submodes.get(submode_name, 0)
+ cmd = '%c' % op_type # Encrypt or decrypt
+ cmd += '%c' % submode # A particular type of a generic algorithm.
+ cmd += '%c' % len(key)
+ cmd += key
+ cmd += '%c' % len(iv)
+ if iv:
+ cmd += iv
+ cmd += struct.pack('>H', len(in_text))
+ cmd += in_text
+ if debug_mode:
+ print('%d:%d cmd size' % (op_type, mode.subcmd), len(cmd), hex_dump(cmd))
+ wrapped_response = tpm.command(tpm.wrap_ext_command(
+ EXT_CMD, mode.subcmd, cmd))
+ real_out_text = tpm.unwrap_ext_response(
+ EXT_CMD, mode.subcmd, wrapped_response)
+ if out_text:
+ if len(real_out_text) > len(out_text):
+ real_out_text = real_out_text[:len(out_text)] # Ignore padding
+ if real_out_text != out_text:
+ if debug_mode:
+ print('Out text mismatch in node %s:\n' % node_name)
+ else:
+ raise TpmError('Out text mismatch in node %s, operation %d:\n'
+ 'In text:%sExpected out text:%sReal out text:%s' % (
+ node_name, op_type,
+ hex_dump(in_text),
+ hex_dump(out_text),
+ hex_dump(real_out_text)))
+ return real_out_text
+
+
+def crypto_test(tdesc, tpm, debug_mode):
+ """Perform a single test described in the xml file.
+
+ The xml node contains all pertinent information about the test inputs and
+ outputs.
+
+ Args:
+ tdesc: an Element of the ElementTree, a test descriptor containing
+ necessary information to run a single encryption/description
+ session.
+ tpm: a TPM object to send extended commands to an initialized TPM
+ debug_mode: a Boolean, if True - this function and the FTDI driver
+ generate debug messated on the console.
+ Raises:
+ TpmError: on various execution errors, the details are included in the
+ error message.
+ """
+ node_name = tdesc.get('name')
+ key = get_attribute(tdesc, 'key')
+ if len(key) not in (16, 24, 32):
+ raise TpmError('wrong key size "%s:%s"' % (
+ node_name,
+ ''.join('%2.2x' % ord(x) for x in key)))
+ iv = get_attribute(tdesc, 'iv', required=False)
+ if iv and len(iv) != 16:
+ raise TpmError('wrong iv size "%s:%s"' % (
+ node_name,
+ ''.join('%2.2x' % ord(x) for x in iv)))
+ clear_text = get_attribute(tdesc, 'clear_text')
+ if debug_mode:
+ print('clear text size', len(clear_text))
+ cipher_text = get_attribute(tdesc, 'cipher_text', required=False)
+ real_cipher_text = crypto_run(node_name, ENCRYPT, key, iv,
+ clear_text, cipher_text, tpm, debug_mode)
+ crypto_run(node_name, DECRYPT, key, iv, real_cipher_text,
+ clear_text, tpm, debug_mode)
+ print(cursor_back + 'SUCCESS: %s' % node_name)
+
+
+if __name__ == '__main__':
+ tree = ET.parse(os.path.join(root_dir, 'crypto_test.xml'))
+ root = tree.getroot()
+ try:
+ debug_needed = len(sys.argv) == 2 and sys.argv[1] == '-d'
+ t = TPM(debug_mode=debug_needed)
+
+ for child in root:
+ crypto_test(child, t, debug_needed)
+ except TpmError as e:
+ print()
+ print(e)
+ if debug_needed:
+ traceback.print_exc()
+ sys.exit(1)