| /* 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 implements the register interface for the TPM SPI Hardware Protocol. |
| * The master puts or gets between 1 and 64 bytes to a register designated by a |
| * 24-bit address. There is no provision for error reporting at this level. |
| */ |
| |
| #include "byteorder.h" |
| #include "console.h" |
| #include "extension.h" |
| #include "task.h" |
| #include "tpm_registers.h" |
| #include "util.h" |
| |
| /* TPM2 library includes. */ |
| #include "ExecCommand_fp.h" |
| #include "Platform.h" |
| #include "_TPM_Init_fp.h" |
| #include "Manufacture_fp.h" |
| |
| #define CPRINTS(format, args...) cprints(CC_TPM, format, ## args) |
| #define CPRINTF(format, args...) cprintf(CC_TPM, format, ## args) |
| |
| |
| /* Register addresses for FIFO mode. */ |
| #define TPM_ACCESS 0 |
| #define TPM_INT_ENABLE 8 |
| #define TPM_INT_VECTOR 0xC |
| #define TPM_INT_STATUS 0x10 |
| #define TPM_INTF_CAPABILITY 0x14 |
| #define TPM_STS 0x18 |
| #define TPM_DATA_FIFO 0x24 |
| #define TPM_INTERFACE_ID 0x30 |
| #define TPM_DID_VID 0xf00 |
| #define TPM_RID 0xf04 |
| |
| #define GOOGLE_VID 0x1ae0 |
| #define GOOGLE_DID 0x0028 |
| #define CR50_RID 0 /* No revision ID yet */ |
| |
| /* Tpm state machine states. */ |
| enum tpm_states { |
| tpm_state_idle, |
| tpm_state_ready, |
| tpm_state_receiving_cmd, |
| tpm_state_executing_cmd, |
| tpm_state_completing_cmd, |
| }; |
| |
| /* A preliminary interface capability register value, will be fine tuned. */ |
| #define IF_CAPABILITY_REG ((3 << 28) | /* TPM2.0 (interface 1.3) */ \ |
| (3 << 9) | /* up to 64 bytes transfers. */ \ |
| 0x15) /* Mandatory set to one. */ |
| |
| /* Volatile registers for FIFO mode */ |
| struct tpm_register_file { |
| uint8_t access; |
| uint32_t int_status; |
| uint32_t sts; |
| uint8_t data_fifo[2048]; /* this might have to be even deeper. */ |
| }; |
| |
| /* |
| * Tpm representation. This is a file scope variable, only one locality is |
| * supported. |
| */ |
| static struct { |
| enum tpm_states state; |
| uint32_t fifo_read_index; /* for read commands */ |
| uint32_t fifo_write_index; /* for write commands */ |
| struct tpm_register_file regs; |
| } tpm_; |
| |
| /* Bit definitions for some TPM registers. */ |
| enum tpm_access_bits { |
| tpm_reg_valid_sts = (1 << 7), |
| active_locality = (1 << 5), |
| request_use = (1 << 1), |
| tpm_establishment = (1 << 0), |
| }; |
| |
| enum tpm_sts_bits { |
| tpm_family_shift = 26, |
| tpm_family_mask = ((1 << 2) - 1), /* 2 bits wide */ |
| tpm_family_tpm2 = 1, |
| reset_establishment_bit = (1 << 25), |
| command_cancel = (1 << 24), |
| burst_count_shift = 8, |
| burst_count_mask = ((1 << 16) - 1), /* 16 bits wide */ |
| sts_valid = (1 << 7), |
| command_ready = (1 << 6), |
| tpm_go = (1 << 5), |
| data_avail = (1 << 4), |
| expect = (1 << 3), |
| self_test_done = (1 << 2), |
| response_retry = (1 << 1), |
| }; |
| |
| static void set_tpm_state(enum tpm_states state) |
| { |
| CPRINTF("state transition from %d to %d\n", tpm_.state, state); |
| tpm_.state = state; |
| |
| if (state == tpm_state_idle) { |
| /* Make sure FIFO is empty. */ |
| tpm_.fifo_read_index = 0; |
| tpm_.fifo_write_index = 0; |
| } |
| } |
| |
| /* |
| * Some TPM registers allow writing of only exactly one bit. This helper |
| * function allows to verify that a value is compliant with this |
| * requirement |
| */ |
| static int single_bit_set(uint32_t value) |
| { |
| return value && !(value & (value - 1)); |
| } |
| |
| /* |
| * NOTE: The put/get functions are called in interrupt context! Don't waste a |
| * lot of time here - just copy the data and wake up a task to deal with it |
| * later. Although if the implementation mandates a "busy" bit somewhere, you |
| * might want to set it now to avoid race conditions with back-to-back |
| * interrupts. |
| */ |
| |
| static void copy_bytes(uint8_t *dest, uint32_t data_size, uint32_t value) |
| { |
| unsigned i; |
| |
| data_size = MIN(data_size, 4); |
| |
| for (i = 0; i < data_size; i++) |
| dest[i] = (value >> (i * 8)) & 0xff; |
| } |
| |
| static void access_reg_write(uint8_t data) |
| { |
| if (!single_bit_set(data)) { |
| CPRINTF("%s: attempt to set acces reg to %02x\n", |
| __func__, data); |
| return; |
| } |
| |
| switch (data) { |
| case request_use: |
| /* |
| * No multiple localities supported, let's just always honor |
| * this request. |
| */ |
| tpm_.regs.access |= active_locality; |
| break; |
| |
| case active_locality: |
| switch (tpm_.state) { |
| case tpm_state_ready: |
| case tpm_state_idle: |
| break; |
| default: |
| /* |
| * TODO: need to decide what to do if there is a |
| * command in progress. |
| */ |
| CPRINTF("%s: locality release request in state %d\n", |
| __func__, tpm_.state); |
| break; |
| } |
| tpm_.regs.access &= ~active_locality; |
| /* No matter what we do, fall into idle state. */ |
| set_tpm_state(tpm_state_idle); |
| break; |
| |
| default: |
| CPRINTF("%s: attempt to set access reg to an unsupported value" |
| " of 0x%02x\n", __func__, data); |
| break; |
| } |
| } |
| |
| /* |
| * Process writes into the 'important' sts register bits. Actions on all |
| * depends on the current state of the device. |
| */ |
| static void sts_reg_write_cr(void) |
| { |
| switch (tpm_.state) { |
| case tpm_state_idle: |
| set_tpm_state(tpm_state_ready); |
| tpm_.regs.sts |= command_ready; |
| break; |
| case tpm_state_ready: |
| tpm_.regs.sts |= command_ready; |
| break; |
| case tpm_state_completing_cmd: |
| case tpm_state_executing_cmd: |
| case tpm_state_receiving_cmd: |
| set_tpm_state(tpm_state_idle); |
| tpm_.regs.sts &= ~command_ready; |
| break; |
| } |
| } |
| |
| static void sts_reg_write_tg(void) |
| { |
| switch (tpm_.state) { |
| case tpm_state_completing_cmd: |
| case tpm_state_executing_cmd: |
| case tpm_state_idle: |
| case tpm_state_ready: |
| break; /* Ignore setting this bit in these states. */ |
| case tpm_state_receiving_cmd: |
| if (!(tpm_.state & expect)) { |
| /* This should trigger actual command execution. */ |
| set_tpm_state(tpm_state_executing_cmd); |
| task_set_event(TASK_ID_TPM, TASK_EVENT_WAKE, 0); |
| } |
| break; |
| } |
| } |
| |
| static void sts_reg_write_rr(void) |
| { |
| switch (tpm_.state) { |
| case tpm_state_idle: |
| case tpm_state_ready: |
| case tpm_state_receiving_cmd: |
| case tpm_state_executing_cmd: |
| break; |
| case tpm_state_completing_cmd: |
| tpm_.fifo_read_index = 0; |
| break; |
| } |
| } |
| |
| /* |
| * TPM_STS register both reports current state machine state and controls some |
| * of state machine transitions. |
| */ |
| static void sts_reg_write(const uint8_t *data, uint32_t data_size) |
| { |
| uint32_t value; |
| |
| data_size = MIN(data_size, 4); |
| memcpy(&value, data, data_size); |
| |
| /* By definition only one bit can be set at a time. */ |
| if (!single_bit_set(value)) { |
| CPRINTF("%s: attempt to set status reg to %02x\n", |
| __func__, value); |
| return; |
| } |
| |
| switch (value) { |
| case command_ready: |
| sts_reg_write_cr(); |
| break; |
| case tpm_go: |
| sts_reg_write_tg(); |
| break; |
| case response_retry: |
| sts_reg_write_rr(); |
| break; |
| case command_cancel: |
| /* TODO: this also needs to be handled, fall through for now. */ |
| default: |
| CPRINTF("requested to write %08x to sts\n", value); |
| break; |
| } |
| } |
| |
| /* Collect received data in the local buffer and change state accordingly. */ |
| static void fifo_reg_write(const uint8_t *data, uint32_t data_size) |
| { |
| uint32_t packet_size; |
| struct tpm_cmd_header *tpmh; |
| |
| /* |
| * Make sure we are in the approriate sate, otherwise ignore this |
| * access. |
| */ |
| if ((tpm_.state == tpm_state_ready) && (tpm_.fifo_write_index == 0)) |
| set_tpm_state(tpm_state_receiving_cmd); |
| |
| if (tpm_.state != tpm_state_receiving_cmd) { |
| CPRINTF("%s: ignoring data in state %d\n", |
| __func__, tpm_.state); |
| return; |
| } |
| |
| if ((tpm_.fifo_write_index + data_size) > sizeof(tpm_.regs.data_fifo)) { |
| CPRINTF("%s: receive buffer overflow: %d in addition to %d\n", |
| __func__, data_size, tpm_.fifo_write_index); |
| tpm_.fifo_write_index = 0; |
| set_tpm_state(tpm_state_ready); |
| return; |
| } |
| |
| /* Copy data into the local buffer. */ |
| memcpy(tpm_.regs.data_fifo + tpm_.fifo_write_index, |
| data, data_size); |
| |
| tpm_.fifo_write_index += data_size; |
| |
| /* Verify that size in the header matches the block size */ |
| if (tpm_.fifo_write_index < 6) { |
| tpm_.regs.sts |= expect; /* More data is needed. */ |
| return; |
| } |
| |
| tpmh = (struct tpm_cmd_header *)tpm_.regs.data_fifo; |
| packet_size = be32toh(tpmh->size); |
| if (tpm_.fifo_write_index < packet_size) { |
| tpm_.regs.sts |= expect; /* More data is needed. */ |
| return; |
| } |
| |
| /* All data has been receved, Ready for the 'go' command. */ |
| tpm_.regs.sts &= ~expect; |
| } |
| |
| /* TODO: data_size is between 1 and 64, but is not trustworthy! Don't write |
| * past the end of any actual registers if data_size is larger than the spec |
| * allows. */ |
| void tpm_register_put(uint32_t regaddr, const uint8_t *data, uint32_t data_size) |
| { |
| uint32_t i; |
| uint32_t idata; |
| |
| memcpy(&idata, data, 4); |
| CPRINTF("%s(0x%03x, %d %x)\n", __func__, regaddr, data_size, idata); |
| |
| switch (regaddr) { |
| case TPM_ACCESS: |
| /* This is a one byte register, ignore extra data, if any */ |
| access_reg_write(data[0]); |
| break; |
| case TPM_STS: |
| sts_reg_write(data, data_size); |
| break; |
| case TPM_DATA_FIFO: |
| fifo_reg_write(data, data_size); |
| break; |
| default: |
| CPRINTF("%s(0x%06x, %d bytes:", __func__, regaddr, data_size); |
| for (i = 0; i < data_size; i++) |
| CPRINTF(", %02x", data[i]); |
| CPRINTF("\n"); |
| return; |
| } |
| |
| } |
| |
| void fifo_reg_read(uint8_t *dest, uint32_t data_size) |
| { |
| uint32_t still_in_fifo = tpm_.fifo_write_index - |
| tpm_.fifo_read_index; |
| |
| data_size = MIN(data_size, still_in_fifo); |
| memcpy(dest, |
| tpm_.regs.data_fifo + tpm_.fifo_read_index, |
| data_size); |
| |
| tpm_.fifo_read_index += data_size; |
| if (tpm_.fifo_write_index == tpm_.fifo_read_index) |
| tpm_.regs.sts &= ~(data_avail | command_ready); |
| } |
| |
| |
| /* TODO: data_size is between 1 and 64, but is not trustworthy! We must return |
| * that many bytes, but not leak any secrets if data_size is larger than |
| * it should be. Return 0x00 or 0xff or whatever the spec says instead. */ |
| void tpm_register_get(uint32_t regaddr, uint8_t *dest, uint32_t data_size) |
| { |
| CPRINTF("%s(0x%06x, %d)", __func__, regaddr, data_size); |
| switch (regaddr) { |
| case TPM_DID_VID: |
| copy_bytes(dest, data_size, (GOOGLE_DID << 16) | GOOGLE_VID); |
| break; |
| case TPM_RID: |
| copy_bytes(dest, data_size, CR50_RID); |
| break; |
| case TPM_INTF_CAPABILITY: |
| copy_bytes(dest, data_size, IF_CAPABILITY_REG); |
| break; |
| case TPM_ACCESS: |
| copy_bytes(dest, data_size, tpm_.regs.access); |
| break; |
| case TPM_STS: |
| CPRINTF(" %x", tpm_.regs.sts); |
| copy_bytes(dest, data_size, tpm_.regs.sts); |
| break; |
| case TPM_DATA_FIFO: |
| fifo_reg_read(dest, data_size); |
| break; |
| default: |
| CPRINTS("%s(0x%06x, %d) => ??", __func__, regaddr, data_size); |
| return; |
| } |
| CPRINTF("\n"); |
| } |
| |
| |
| static void tpm_init(void) |
| { |
| set_tpm_state(tpm_state_idle); |
| tpm_.regs.access = tpm_reg_valid_sts; |
| tpm_.regs.sts = (tpm_family_tpm2 << tpm_family_shift) | |
| (64 << burst_count_shift) | sts_valid; |
| |
| /* TPM2 library functions. */ |
| _plat__Signal_PowerOn(); |
| /* TODO(ngm): CRBUG/50115, initialize state expected by TPM2 |
| * compliance tests. */ |
| TPM_Manufacture(1); |
| _TPM_Init(); |
| _plat__SetNvAvail(); |
| } |
| |
| #ifdef CONFIG_EXTENSION_COMMAND |
| |
| static void call_extension_command(struct tpm_cmd_header *tpmh, |
| size_t *total_size) |
| { |
| size_t command_size = be32toh(tpmh->size); |
| |
| /* Verify there is room for at least the extension command header. */ |
| if (command_size >= sizeof(struct tpm_cmd_header)) { |
| uint16_t subcommand_code; |
| |
| /* The header takes room in the buffer. */ |
| *total_size -= sizeof(struct tpm_cmd_header); |
| |
| subcommand_code = be16toh(tpmh->subcommand_code); |
| extension_route_command(subcommand_code, |
| tpmh + 1, |
| command_size - |
| sizeof(struct tpm_cmd_header), |
| total_size); |
| /* Add the header size back. */ |
| *total_size += sizeof(struct tpm_cmd_header); |
| tpmh->size = htobe32(*total_size); |
| } else { |
| *total_size = command_size; |
| } |
| } |
| #endif |
| |
| void tpm_task(void) |
| { |
| tpm_init(); |
| sps_tpm_enable(); |
| while (1) { |
| uint8_t *response; |
| unsigned response_size; |
| uint32_t command_code; |
| struct tpm_cmd_header *tpmh; |
| |
| /* Wait for the next command event */ |
| task_wait_event(-1); |
| tpmh = (struct tpm_cmd_header *)tpm_.regs.data_fifo; |
| command_code = be32toh(tpmh->command_code); |
| CPRINTF("%s: received fifo command 0x%04x\n", |
| __func__, command_code); |
| |
| #ifdef CONFIG_EXTENSION_COMMAND |
| if (command_code == CONFIG_EXTENSION_COMMAND) { |
| response_size = sizeof(tpm_.regs.data_fifo); |
| call_extension_command(tpmh, &response_size); |
| } else |
| #endif |
| { |
| ExecuteCommand(tpm_.fifo_write_index, |
| tpm_.regs.data_fifo, |
| &response_size, |
| &response); |
| } |
| CPRINTF("got %d bytes in response\n", response_size); |
| if (response_size && |
| (response_size <= sizeof(tpm_.regs.data_fifo))) { |
| #ifdef CONFIG_EXTENSION_COMMAND |
| if (command_code != CONFIG_EXTENSION_COMMAND) |
| #endif |
| { |
| /* |
| * Extension commands reuse FIFO buffer, the |
| * rest need to copy. |
| */ |
| memcpy(tpm_.regs.data_fifo, |
| response, response_size); |
| } |
| tpm_.fifo_read_index = 0; |
| tpm_.fifo_write_index = response_size; |
| tpm_.regs.sts |= data_avail; |
| set_tpm_state(tpm_state_completing_cmd); |
| } |
| } |
| } |