sandbox: tpm: Enhance to support the latest Chromium OS
authorSimon Glass <sjg@chromium.org>
Mon, 1 Oct 2018 17:55:18 +0000 (11:55 -0600)
committerSimon Glass <sjg@chromium.org>
Tue, 9 Oct 2018 10:40:27 +0000 (04:40 -0600)
This driver was originally written against Chromium OS circa 2012. A few
new features have been added. Enhance the TPM driver to match. This mostly
includes a few new messages and properly modelling whether a particular
'space' is present or not.

Signed-off-by: Simon Glass <sjg@chromium.org>
drivers/tpm/tpm_tis_sandbox.c
include/tpm-v1.h

index c0b35a06c8a0f003fbc6105b3a20a2352ad88fd4..79517f015aff6081b324a4d8c0a0cc27d6db3c9c 100644 (file)
 /* TPM NVRAM location indices. */
 #define FIRMWARE_NV_INDEX              0x1007
 #define KERNEL_NV_INDEX                        0x1008
+#define BACKUP_NV_INDEX                 0x1009
+#define FWMP_NV_INDEX                   0x100a
+#define REC_HASH_NV_INDEX               0x100b
+#define REC_HASH_NV_SIZE                VB2_SHA256_DIGEST_SIZE
 
 #define NV_DATA_PUBLIC_PERMISSIONS_OFFSET      60
 
@@ -45,18 +49,28 @@ enum {
        NV_GLOBAL_LOCK,
        NV_SEQ_FIRMWARE,
        NV_SEQ_KERNEL,
+       NV_SEQ_BACKUP,
+       NV_SEQ_FWMP,
+       NV_SEQ_REC_HASH,
+
        NV_SEQ_COUNT,
 };
 
 /* Size of each non-volatile space */
 #define NV_DATA_SIZE           0x20
 
+struct nvdata_state {
+       bool present;
+       u8 data[NV_DATA_SIZE];
+};
+
 /*
  * Information about our TPM emulation. This is preserved in the sandbox
  * state file if enabled.
  */
 static struct tpm_state {
-       uint8_t nvdata[NV_SEQ_COUNT][NV_DATA_SIZE];
+       bool valid;
+       struct nvdata_state nvdata[NV_SEQ_COUNT];
 } g_state;
 
 /**
@@ -82,9 +96,12 @@ static int sandbox_tpm_read_state(const void *blob, int node)
 
                sprintf(prop_name, "nvdata%d", i);
                prop = fdt_getprop(blob, node, prop_name, &len);
-               if (prop && len == NV_DATA_SIZE)
-                       memcpy(g_state.nvdata[i], prop, NV_DATA_SIZE);
+               if (prop && len == NV_DATA_SIZE) {
+                       memcpy(g_state.nvdata[i].data, prop, NV_DATA_SIZE);
+                       g_state.nvdata[i].present = true;
+               }
        }
+       g_state.valid = true;
 
        return 0;
 }
@@ -110,9 +127,11 @@ static int sandbox_tpm_write_state(void *blob, int node)
        for (i = 0; i < NV_SEQ_COUNT; i++) {
                char prop_name[20];
 
-               sprintf(prop_name, "nvdata%d", i);
-               fdt_setprop(blob, node, prop_name, g_state.nvdata[i],
-                           NV_DATA_SIZE);
+               if (g_state.nvdata[i].present) {
+                       sprintf(prop_name, "nvdata%d", i);
+                       fdt_setprop(blob, node, prop_name,
+                                   g_state.nvdata[i].data, NV_DATA_SIZE);
+               }
        }
 
        return 0;
@@ -128,6 +147,12 @@ static int index_to_seq(uint32_t index)
                return NV_SEQ_FIRMWARE;
        case KERNEL_NV_INDEX:
                return NV_SEQ_KERNEL;
+       case BACKUP_NV_INDEX:
+               return NV_SEQ_BACKUP;
+       case FWMP_NV_INDEX:
+               return NV_SEQ_FWMP;
+       case REC_HASH_NV_INDEX:
+               return NV_SEQ_REC_HASH;
        case 0:
                return NV_GLOBAL_LOCK;
        }
@@ -136,6 +161,21 @@ static int index_to_seq(uint32_t index)
        return -1;
 }
 
+static void handle_cap_flag_space(u8 **datap, uint index)
+{
+       struct tpm_nv_data_public pub;
+
+       /* TPM_NV_PER_PPWRITE */
+       memset(&pub, '\0', sizeof(pub));
+       pub.nv_index = __cpu_to_be32(index);
+       pub.pcr_info_read.pcr_selection.size_of_select = __cpu_to_be16(
+               sizeof(pub.pcr_info_read.pcr_selection.pcr_select));
+       pub.permission.attributes = __cpu_to_be32(1);
+       pub.pcr_info_write = pub.pcr_info_read;
+       memcpy(*datap, &pub, sizeof(pub));
+       *datap += sizeof(pub);
+}
+
 static int sandbox_tpm_xfer(struct udevice *dev, const uint8_t *sendbuf,
                            size_t send_size, uint8_t *recvbuf,
                            size_t *recv_len)
@@ -159,19 +199,35 @@ static int sandbox_tpm_xfer(struct udevice *dev, const uint8_t *sendbuf,
                        printf("Get flags index %#02x\n", index);
                        *recv_len = 22;
                        memset(recvbuf, '\0', *recv_len);
-                       put_unaligned_be32(22, recvbuf +
-                                          TPM_RESPONSE_HEADER_LENGTH);
                        data = recvbuf + TPM_RESPONSE_HEADER_LENGTH +
                                        sizeof(uint32_t);
                        switch (index) {
                        case FIRMWARE_NV_INDEX:
                                break;
                        case KERNEL_NV_INDEX:
-                               /* TPM_NV_PER_PPWRITE */
-                               put_unaligned_be32(1, data +
-                                       NV_DATA_PUBLIC_PERMISSIONS_OFFSET);
+                               handle_cap_flag_space(&data, index);
+                               *recv_len = data - recvbuf -
+                                       TPM_RESPONSE_HEADER_LENGTH -
+                                       sizeof(uint32_t);
+                               break;
+                       case TPM_CAP_FLAG_PERMANENT: {
+                               struct tpm_permanent_flags *pflags;
+
+                               pflags = (struct tpm_permanent_flags *)data;
+                               memset(pflags, '\0', sizeof(*pflags));
+                               put_unaligned_be32(TPM_TAG_PERMANENT_FLAGS,
+                                                  &pflags->tag);
+                               *recv_len = TPM_HEADER_SIZE + 4 +
+                                               sizeof(*pflags);
                                break;
                        }
+                       default:
+                               printf("   ** Unknown flags index %x\n", index);
+                               return -ENOSYS;
+                       }
+                       put_unaligned_be32(*recv_len,
+                                          recvbuf +
+                                          TPM_RESPONSE_HEADER_LENGTH);
                        break;
                case TPM_CAP_NV_INDEX:
                        index = get_unaligned_be32(sendbuf + 18);
@@ -192,7 +248,8 @@ static int sandbox_tpm_xfer(struct udevice *dev, const uint8_t *sendbuf,
                if (seq < 0)
                        return -EINVAL;
                printf("tpm: nvwrite index=%#02x, len=%#02x\n", index, length);
-               memcpy(&tpm->nvdata[seq], sendbuf + 22, length);
+               memcpy(&tpm->nvdata[seq].data, sendbuf + 22, length);
+               tpm->nvdata[seq].present = true;
                *recv_len = 12;
                memset(recvbuf, '\0', *recv_len);
                break;
@@ -202,7 +259,8 @@ static int sandbox_tpm_xfer(struct udevice *dev, const uint8_t *sendbuf,
                seq = index_to_seq(index);
                if (seq < 0)
                        return -EINVAL;
-               printf("tpm: nvread index=%#02x, len=%#02x\n", index, length);
+               printf("tpm: nvread index=%#02x, len=%#02x, seq=%#02x\n", index,
+                      length, seq);
                *recv_len = TPM_RESPONSE_HEADER_LENGTH + sizeof(uint32_t) +
                                        length;
                memset(recvbuf, '\0', *recv_len);
@@ -220,17 +278,26 @@ static int sandbox_tpm_xfer(struct udevice *dev, const uint8_t *sendbuf,
                                        offsetof(struct rollback_space_kernel,
                                                 crc8));
                        memcpy(data, &rsk, sizeof(rsk));
+               } else if (!tpm->nvdata[seq].present) {
+                       put_unaligned_be32(TPM_BADINDEX, recvbuf +
+                                          sizeof(uint16_t) + sizeof(uint32_t));
                } else {
                        memcpy(recvbuf + TPM_RESPONSE_HEADER_LENGTH +
-                              sizeof(uint32_t), &tpm->nvdata[seq], length);
+                              sizeof(uint32_t), &tpm->nvdata[seq].data,
+                              length);
                }
                break;
-       case TPM_CMD_EXTEND: /* tpm extend */
+       case TPM_CMD_EXTEND:
+               *recv_len = 30;
+               memset(recvbuf, '\0', *recv_len);
+               break;
+       case TPM_CMD_NV_DEFINE_SPACE:
        case 0x15: /* pcr read */
        case 0x5d: /* force clear */
        case 0x6f: /* physical enable */
        case 0x72: /* physical set deactivated */
        case 0x99: /* startup */
+       case 0x50: /* self test full */
        case 0x4000000a:  /* assert physical presence */
                *recv_len = 12;
                memset(recvbuf, '\0', *recv_len);
index 29788b5390f7bde9f3f90d4bd6c56eb5f40eaf12..f9ffbb26561a81d7adb7541548e971944d8bd1cc 100644 (file)
@@ -245,6 +245,40 @@ struct tpm_permanent_flags {
        u8      disable_full_da_logic_info;
 } __packed;
 
+#define TPM_SHA1_160_HASH_LEN  0x14
+
+struct __packed tpm_composite_hash {
+       u8      digest[TPM_SHA1_160_HASH_LEN];
+};
+
+struct __packed tpm_pcr_selection {
+       __be16  size_of_select;
+       u8      pcr_select[3];  /* matches vboot's struct */
+};
+
+struct __packed tpm_pcr_info_short {
+       struct tpm_pcr_selection pcr_selection;
+       u8      locality_at_release;
+       struct tpm_composite_hash digest_at_release;
+};
+
+struct __packed tpm_nv_attributes {
+       __be16  tag;
+       __be32  attributes;
+};
+
+struct __packed tpm_nv_data_public {
+       __be16  tag;
+       __be32  nv_index;
+       struct tpm_pcr_info_short pcr_info_read;
+       struct tpm_pcr_info_short pcr_info_write;
+       struct tpm_nv_attributes permission;
+       u8      read_st_clear;
+       u8      write_st_clear;
+       u8      write_define;
+       __be32  data_size;
+};
+
 /**
  * Issue a TPM_Startup command.
  *