diff --git a/xen/arch/x86/include/asm/tpm.h b/xen/arch/x86/include/asm/tpm.h index c5b9ecec88..e53e8cf133 100644 --- a/xen/arch/x86/include/asm/tpm.h +++ b/xen/arch/x86/include/asm/tpm.h @@ -9,8 +9,8 @@ #include -#define TPM_TIS_BASE 0xfed40000U -#define TPM_TIS_SIZE 0x00010000U +#define TPM_BASE 0xfed40000U +#define TPM_SIZE 0x00010000U /* All fields of the following structs are big endian. */ diff --git a/xen/arch/x86/slaunch.c b/xen/arch/x86/slaunch.c index cf92f768de..d3dbc46e60 100644 --- a/xen/arch/x86/slaunch.c +++ b/xen/arch/x86/slaunch.c @@ -104,7 +104,7 @@ void __init slaunch_map_mem_regions(void) paddr_t evt_log_addr; uint32_t evt_log_size; - rc = slaunch_map_l2(TPM_TIS_BASE, TPM_TIS_SIZE); + rc = slaunch_map_l2(TPM_BASE, TPM_SIZE); BUG_ON(rc != 0); /* Vendor-specific part. */ diff --git a/xen/arch/x86/tpm.c b/xen/arch/x86/tpm.c index 089c7d12a5..95f07e0a30 100644 --- a/xen/arch/x86/tpm.c +++ b/xen/arch/x86/tpm.c @@ -85,63 +85,115 @@ static bool is_amd_cpu(void) #define TPM_LOC_REG(loc, reg) (0x1000 * (loc) + (reg)) -#define TPM_ACCESS_(x) TPM_LOC_REG(x, 0x00) -#define ACCESS_REQUEST_USE (1 << 1) -#define ACCESS_ACTIVE_LOCALITY (1 << 5) -#define TPM_INTF_CAPABILITY_(x) TPM_LOC_REG(x, 0x14) -#define INTF_VERSION_MASK 0x70000000 -#define TPM_STS_(x) TPM_LOC_REG(x, 0x18) -#define TPM_FAMILY_MASK 0x0C000000 -#define STS_EXPECT_DATA (1 << 3) -#define STS_DATA_AVAIL (1 << 4) -#define STS_TPM_GO (1 << 5) -#define STS_COMMAND_READY (1 << 6) -#define STS_VALID (1 << 7) -#define TPM_BURST_COUNT_(x) TPM_LOC_REG(x, 0x19) -#define TPM_DATA_FIFO_(x) TPM_LOC_REG(x, 0x24) - #define swap16(x) __builtin_bswap16(x) #define swap32(x) __builtin_bswap32(x) -static inline volatile uint32_t tis_read32(unsigned reg) +/******************************** MMIO helpers ********************************/ + +static inline uint32_t tpm_read32(unsigned reg) { - return *(volatile uint32_t *)__va(TPM_TIS_BASE + reg); + return *(volatile uint32_t *)__va(TPM_BASE + reg); } -static inline volatile uint16_t tis_read16(unsigned reg) +static inline uint16_t tpm_read16(unsigned reg) { - return *(volatile uint16_t *)__va(TPM_TIS_BASE + reg); + return *(volatile uint16_t *)__va(TPM_BASE + reg); } -static inline volatile uint8_t tis_read8(unsigned reg) +static inline uint8_t tpm_read8(unsigned reg) { - return *(volatile uint8_t *)__va(TPM_TIS_BASE + reg); + return *(volatile uint8_t *)__va(TPM_BASE + reg); } -static inline void tis_write8(unsigned reg, uint8_t val) +static inline void tpm_write32(unsigned reg, uint32_t val) { - *(volatile uint8_t *)__va(TPM_TIS_BASE + reg) = val; + *(volatile uint32_t *)__va(TPM_BASE + reg) = val; } -static inline void request_locality(unsigned loc) +static inline void tpm_write8(unsigned reg, uint8_t val) +{ + *(volatile uint8_t *)__va(TPM_BASE + reg) = val; +} + +/************************** Interface detection *******************************/ + +#define TPM_INTF_ID_(x) TPM_LOC_REG(x, 0x30) +#define INTF_TYPE_MASK 0x0000000fU +#define INTF_TYPE_TIS 0x00 +#define INTF_TYPE_CRB 0x01 + +/* + * No static caching: the early 32-bit binary (tpm_early.bin) is built with + * "objcopy -j .text", which omits .bss/.data. + */ +static inline bool tpm_is_crb(void) +{ + return (tpm_read32(TPM_INTF_ID_(0)) & INTF_TYPE_MASK) == INTF_TYPE_CRB; +} + +/************************** TIS register definitions **************************/ + +#define TIS_ACCESS_(x) TPM_LOC_REG(x, 0x00) +#define ACCESS_REQUEST_USE (1 << 1) +#define ACCESS_ACTIVE_LOCALITY (1 << 5) +#define TIS_INTF_CAPABILITY_(x) TPM_LOC_REG(x, 0x14) +#define INTF_VERSION_MASK 0x70000000 +#define TIS_STS_(x) TPM_LOC_REG(x, 0x18) +#define STS_FAMILY_MASK 0x0C000000 +#define STS_EXPECT_DATA (1 << 3) +#define STS_DATA_AVAIL (1 << 4) +#define STS_TPM_GO (1 << 5) +#define STS_COMMAND_READY (1 << 6) +#define STS_VALID (1 << 7) +#define TIS_BURST_COUNT_(x) TPM_LOC_REG(x, 0x19) +#define TIS_DATA_FIFO_(x) TPM_LOC_REG(x, 0x24) + +/************************** CRB register definitions **************************/ + +#define CRB_LOC_STATE_(x) TPM_LOC_REG(x, 0x00) +#define CRB_LOC_STATE_LOC_ASSIGNED (1 << 1) +#define CRB_LOC_STATE_REG_VALID_STS (1 << 7) +#define CRB_LOC_CTRL_(x) TPM_LOC_REG(x, 0x08) +#define CRB_LOC_CTRL_REQUEST_ACCESS (1 << 0) +#define CRB_LOC_CTRL_RELINQUISH (1 << 1) +#define CRB_CTRL_REQ_(x) TPM_LOC_REG(x, 0x40) +#define CRB_CTRL_REQ_CMD_READY (1 << 0) +#define CRB_CTRL_REQ_GO_IDLE (1 << 1) +#define CRB_CTRL_STS_(x) TPM_LOC_REG(x, 0x44) +#define CRB_CTRL_STS_ERROR (1 << 0) +#define CRB_CTRL_CANCEL_(x) TPM_LOC_REG(x, 0x48) +#define CRB_CTRL_CANCEL_INVOKE (1 << 0) +#define CRB_CTRL_START_(x) TPM_LOC_REG(x, 0x4C) +#define CRB_CTRL_START_INVOKE (1 << 0) +#define CRB_CTRL_CMD_SIZE_(x) TPM_LOC_REG(x, 0x58) +#define CRB_CTRL_CMD_LADDR_(x) TPM_LOC_REG(x, 0x5C) +#define CRB_CTRL_CMD_HADDR_(x) TPM_LOC_REG(x, 0x60) +#define CRB_CTRL_RSP_SIZE_(x) TPM_LOC_REG(x, 0x64) +#define CRB_CTRL_RSP_ADDR_(x) TPM_LOC_REG(x, 0x68) +#define CRB_DATA_BUFFER_(x) TPM_LOC_REG(x, 0x80) +#define CRB_DATA_BUFFER_SIZE 0x0F80 + +/************************** TIS locality & command ****************************/ + +static inline void tis_request_locality(unsigned loc) { - tis_write8(TPM_ACCESS_(loc), ACCESS_REQUEST_USE); + tpm_write8(TIS_ACCESS_(loc), ACCESS_REQUEST_USE); /* Check that locality was actually activated. */ - while ( (tis_read8(TPM_ACCESS_(loc)) & ACCESS_ACTIVE_LOCALITY) == 0 ); + while ( (tpm_read8(TIS_ACCESS_(loc)) & ACCESS_ACTIVE_LOCALITY) == 0 ); } -static inline void relinquish_locality(unsigned loc) +static inline void tis_relinquish_locality(unsigned loc) { - tis_write8(TPM_ACCESS_(loc), ACCESS_ACTIVE_LOCALITY); + tpm_write8(TIS_ACCESS_(loc), ACCESS_ACTIVE_LOCALITY); } -static inline uint16_t get_burst_count(unsigned loc) +static inline uint16_t tis_get_burst_count(unsigned loc) { - return tis_read16(TPM_BURST_COUNT_(loc)); + return tpm_read16(TIS_BURST_COUNT_(loc)); } -static void send_cmd(unsigned loc, uint8_t *buf, unsigned i_size, - unsigned *o_size) +static void tis_send_cmd(unsigned loc, uint8_t *buf, unsigned i_size, + unsigned *o_size) { /* * Values of "expect data" and "data available" bits counts only when @@ -154,54 +206,160 @@ static void send_cmd(unsigned loc, uint8_t *buf, unsigned i_size, unsigned burst_count; /* Make sure TPM can accept a command. */ - if ( (tis_read8(TPM_STS_(loc)) & STS_COMMAND_READY) == 0 ) + if ( (tpm_read8(TIS_STS_(loc)) & STS_COMMAND_READY) == 0 ) { /* Abort current command. */ - tis_write8(TPM_STS_(loc), STS_COMMAND_READY); + tpm_write8(TIS_STS_(loc), STS_COMMAND_READY); /* Wait until TPM is ready for a new one. */ - while ( (tis_read8(TPM_STS_(loc)) & STS_COMMAND_READY) == 0 ); + while ( (tpm_read8(TIS_STS_(loc)) & STS_COMMAND_READY) == 0 ); } i = 0; while ( i < i_size ) { do - burst_count = get_burst_count(loc); + burst_count = tis_get_burst_count(loc); while ( burst_count == 0 ); while ( burst_count-- > 0 && i < i_size ) - tis_write8(TPM_DATA_FIFO_(loc), buf[i++]); + tpm_write8(TIS_DATA_FIFO_(loc), buf[i++]); if ( i < i_size ) - while ( (tis_read8(TPM_STS_(loc)) & expect_data) != expect_data ); + while ( (tpm_read8(TIS_STS_(loc)) & expect_data) != expect_data ); } - tis_write8(TPM_STS_(loc), STS_TPM_GO); + tpm_write8(TIS_STS_(loc), STS_TPM_GO); /* Wait for the first byte of response. */ - while ( (tis_read8(TPM_STS_(loc)) & data_avail) != data_avail ); + while ( (tpm_read8(TIS_STS_(loc)) & data_avail) != data_avail ); i = 0; do { do - burst_count = get_burst_count(loc); + burst_count = tis_get_burst_count(loc); while ( burst_count == 0 ); while ( burst_count-- > 0 && i < *o_size) - buf[i++] = tis_read8(TPM_DATA_FIFO_(loc)); + buf[i++] = tpm_read8(TIS_DATA_FIFO_(loc)); - while ( (tis_read8(TPM_STS_(loc)) & STS_VALID) == 0 ); + while ( (tpm_read8(TIS_STS_(loc)) & STS_VALID) == 0 ); } while ( i < *o_size && - (tis_read8(TPM_STS_(loc)) & data_avail) == data_avail ); + (tpm_read8(TIS_STS_(loc)) & data_avail) == data_avail ); if ( i < *o_size ) *o_size = i; - tis_write8(TPM_STS_(loc), STS_COMMAND_READY); + tpm_write8(TIS_STS_(loc), STS_COMMAND_READY); +} + +/************************** CRB locality & command ****************************/ + +static void crb_request_locality(unsigned loc) +{ + const uint32_t mask = CRB_LOC_STATE_LOC_ASSIGNED | + CRB_LOC_STATE_REG_VALID_STS; + + tpm_write32(CRB_LOC_CTRL_(loc), CRB_LOC_CTRL_REQUEST_ACCESS); + while ( (tpm_read32(CRB_LOC_STATE_(loc)) & mask) != mask ); +} + +static void crb_relinquish_locality(unsigned loc) +{ + tpm_write32(CRB_LOC_CTRL_(loc), CRB_LOC_CTRL_RELINQUISH); + while ( tpm_read32(CRB_LOC_STATE_(loc)) & CRB_LOC_STATE_LOC_ASSIGNED ); +} + +static void crb_cmd_ready(unsigned loc) +{ + tpm_write32(CRB_CTRL_REQ_(loc), CRB_CTRL_REQ_CMD_READY); + while ( tpm_read32(CRB_CTRL_REQ_(loc)) & CRB_CTRL_REQ_CMD_READY ); +} + +static void crb_go_idle(unsigned loc) +{ + tpm_write32(CRB_CTRL_REQ_(loc), CRB_CTRL_REQ_GO_IDLE); + while ( tpm_read32(CRB_CTRL_REQ_(loc)) & CRB_CTRL_REQ_GO_IDLE ); +} + +static void crb_send_cmd(unsigned loc, uint8_t *buf, unsigned i_size, + unsigned *o_size) +{ + uint32_t data_buf_pa = TPM_BASE + CRB_DATA_BUFFER_(loc); + unsigned expected; + + crb_cmd_ready(loc); + + tpm_write32(CRB_CTRL_CANCEL_(loc), 0); + + tpm_write32(CRB_CTRL_CMD_LADDR_(loc), data_buf_pa); + tpm_write32(CRB_CTRL_CMD_HADDR_(loc), 0); + tpm_write32(CRB_CTRL_CMD_SIZE_(loc), CRB_DATA_BUFFER_SIZE); + tpm_write32(CRB_CTRL_RSP_SIZE_(loc), CRB_DATA_BUFFER_SIZE); + /* RSP_ADDR is 64-bit. */ + tpm_write32(CRB_CTRL_RSP_ADDR_(loc), data_buf_pa); + tpm_write32(CRB_CTRL_RSP_ADDR_(loc) + 4, 0); + + memcpy(__va(data_buf_pa), buf, i_size); + + tpm_write32(CRB_CTRL_START_(loc), CRB_CTRL_START_INVOKE); + while ( tpm_read32(CRB_CTRL_START_(loc)) & CRB_CTRL_START_INVOKE ); + + if ( tpm_read32(CRB_CTRL_STS_(loc)) & CRB_CTRL_STS_ERROR ) + { + *o_size = 0; + crb_go_idle(loc); + return; + } + + /* Read header to learn the response length. */ + memcpy(buf, __va(data_buf_pa), sizeof(struct tpm_rsp_hdr)); + expected = swap32(((struct tpm_rsp_hdr *)buf)->paramSize); + if ( expected > *o_size ) + expected = *o_size; + if ( expected > CRB_DATA_BUFFER_SIZE ) + expected = CRB_DATA_BUFFER_SIZE; + + memcpy(buf, __va(data_buf_pa), expected); + + *o_size = expected; + crb_go_idle(loc); +} + +/************************** Interface dispatch ********************************/ + +static inline void request_locality(unsigned loc) +{ + if ( tpm_is_crb() ) + crb_request_locality(loc); + else + tis_request_locality(loc); +} + +static inline void relinquish_locality(unsigned loc) +{ + if ( tpm_is_crb() ) + crb_relinquish_locality(loc); + else + tis_relinquish_locality(loc); +} + +static void send_cmd(unsigned loc, uint8_t *buf, unsigned i_size, + unsigned *o_size) +{ + if ( tpm_is_crb() ) + crb_send_cmd(loc, buf, i_size, o_size); + else + tis_send_cmd(loc, buf, i_size, o_size); } static inline bool is_tpm12(void) { + uint32_t intf_version; + + /* CRB interface is always TPM 2.0. */ + if ( tpm_is_crb() ) + return false; + /* * If one of these conditions is true: * - INTF_CAPABILITY_x.interfaceVersion is 0 (TIS <= 1.21) @@ -209,10 +367,9 @@ static inline bool is_tpm12(void) * - STS_x.tpmFamily is 0 * we're dealing with TPM1.2. */ - uint32_t intf_version = tis_read32(TPM_INTF_CAPABILITY_(0)) - & INTF_VERSION_MASK; + intf_version = tpm_read32(TIS_INTF_CAPABILITY_(0)) & INTF_VERSION_MASK; return (intf_version == 0x00000000 || intf_version == 0x20000000 || - (tis_read32(TPM_STS_(0)) & TPM_FAMILY_MASK) == 0); + (tpm_read32(TIS_STS_(0)) & STS_FAMILY_MASK) == 0); } /****************************** TPM1.2 & TPM2.0 *******************************/ @@ -460,13 +617,14 @@ static uint32_t tpm2_hash_extend(unsigned loc, const uint8_t *buf, o_size = sizeof(cmd_rsp); send_cmd(loc, cmd_rsp.b, swap32(cmd_rsp.c.paramSize), &o_size); - if ( cmd_rsp.r.tag == swap16(TPM_ST_NO_SESSIONS) && - cmd_rsp.r.paramSize == swap32(10) ) + if ( o_size < sizeof(struct tpm_rsp_hdr) ) { - rc = swap32(cmd_rsp.r.returnCode); - if ( rc != 0 ) - goto error; + rc = 1; + goto error; } + rc = swap32(cmd_rsp.r.returnCode); + if ( rc != 0 ) + goto error; seq_handle = swap32(cmd_rsp.start_r.sequenceHandle); @@ -490,13 +648,14 @@ static uint32_t tpm2_hash_extend(unsigned loc, const uint8_t *buf, o_size = sizeof(cmd_rsp); send_cmd(loc, cmd_rsp.b, swap32(cmd_rsp.c.paramSize), &o_size); - if ( cmd_rsp.r.tag == swap16(TPM_ST_NO_SESSIONS) && - cmd_rsp.r.paramSize == swap32(10) ) + if ( o_size < sizeof(struct tpm_rsp_hdr) ) { - rc = swap32(cmd_rsp.r.returnCode); - if ( rc != 0 ) - goto error; + rc = 1; + goto error; } + rc = swap32(cmd_rsp.r.returnCode); + if ( rc != 0 ) + goto error; size -= max_bytes; buf += max_bytes; @@ -519,12 +678,24 @@ static uint32_t tpm2_hash_extend(unsigned loc, const uint8_t *buf, o_size = sizeof(cmd_rsp); send_cmd(loc, cmd_rsp.b, swap32(cmd_rsp.c.paramSize), &o_size); - if ( cmd_rsp.r.tag == swap16(TPM_ST_NO_SESSIONS) && - cmd_rsp.r.paramSize == swap32(10) ) + if ( o_size < sizeof(struct tpm_rsp_hdr) ) { - rc = swap32(cmd_rsp.r.returnCode); - if ( rc != 0 ) - goto error; + rc = 1; + goto error; + } + rc = swap32(cmd_rsp.r.returnCode); + if ( rc != 0 ) + goto error; + + /* + * EventSequenceComplete response (with sessions): + * header (10) | parameterSize (4) | TPML_DIGEST_VALUES | authArea + * finish_r overlays: h (10) | paramSize (4) | hashCount (4) | hashes[] + */ + if ( o_size < sizeof(cmd_rsp.finish_r) + sizeof(uint32_t) ) + { + rc = 1; + goto error; } p = cmd_rsp.finish_r.hashes;