Skip to content

Commit

Permalink
tpm: Support for TIS selftest done flag
Browse files Browse the repository at this point in the history
Extend the backend to check whether the TPM_ContinueSelfTest
finished successfully and provide a flag to the TIS front-end
if it successfully finished. The TIS then sets a flag in
all localities in the STS register and keeps it until the next
reset.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
  • Loading branch information
stefanberger authored and mstsirkin committed Mar 1, 2015
1 parent 2eae8c7 commit fd85908
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 19 deletions.
1 change: 1 addition & 0 deletions hw/tpm/tpm_int.h
Expand Up @@ -62,6 +62,7 @@ struct tpm_resp_hdr {

#define TPM_FAIL 9

#define TPM_ORD_ContinueSelfTest 0x53
#define TPM_ORD_GetTicks 0xf1

#endif /* TPM_TPM_INT_H */
37 changes: 32 additions & 5 deletions hw/tpm/tpm_passthrough.c
Expand Up @@ -112,14 +112,31 @@ static void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len)
}
}

static bool tpm_passthrough_is_selftest(const uint8_t *in, uint32_t in_len)
{
struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in;

if (in_len >= sizeof(*hdr)) {
return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest);
}

return false;
}

static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
const uint8_t *in, uint32_t in_len,
uint8_t *out, uint32_t out_len)
uint8_t *out, uint32_t out_len,
bool *selftest_done)
{
int ret;
bool is_selftest;
const struct tpm_resp_hdr *hdr;

tpm_pt->tpm_op_canceled = false;
tpm_pt->tpm_executing = true;
*selftest_done = false;

is_selftest = tpm_passthrough_is_selftest(in, in_len);

ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len);
if (ret != in_len) {
Expand Down Expand Up @@ -149,6 +166,11 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
"packet from TPM\n");
}

if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) {
hdr = (struct tpm_resp_hdr *)out;
*selftest_done = (be32_to_cpu(hdr->errcode) == 0);
}

err_exit:
if (ret < 0) {
tpm_write_fatal_error_response(out, out_len);
Expand All @@ -160,13 +182,15 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
}

static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt,
const TPMLocality *locty_data)
const TPMLocality *locty_data,
bool *selftest_done)
{
return tpm_passthrough_unix_tx_bufs(tpm_pt,
locty_data->w_buffer.buffer,
locty_data->w_offset,
locty_data->r_buffer.buffer,
locty_data->r_buffer.size);
locty_data->r_buffer.size,
selftest_done);
}

static void tpm_passthrough_worker_thread(gpointer data,
Expand All @@ -175,16 +199,19 @@ static void tpm_passthrough_worker_thread(gpointer data,
TPMPassthruThreadParams *thr_parms = user_data;
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb);
TPMBackendCmd cmd = (TPMBackendCmd)data;
bool selftest_done = false;

DPRINTF("tpm_passthrough: processing command type %d\n", cmd);

switch (cmd) {
case TPM_BACKEND_CMD_PROCESS_CMD:
tpm_passthrough_unix_transfer(tpm_pt,
thr_parms->tpm_state->locty_data);
thr_parms->tpm_state->locty_data,
&selftest_done);

thr_parms->recv_data_callback(thr_parms->tpm_state,
thr_parms->tpm_state->locty_number);
thr_parms->tpm_state->locty_number,
selftest_done);
break;
case TPM_BACKEND_CMD_INIT:
case TPM_BACKEND_CMD_END:
Expand Down
58 changes: 45 additions & 13 deletions hw/tpm/tpm_tis.c
Expand Up @@ -64,6 +64,7 @@
#define TPM_TIS_STS_TPM_GO (1 << 5)
#define TPM_TIS_STS_DATA_AVAILABLE (1 << 4)
#define TPM_TIS_STS_EXPECT (1 << 3)
#define TPM_TIS_STS_SELFTEST_DONE (1 << 2)
#define TPM_TIS_STS_RESPONSE_RETRY (1 << 1)

#define TPM_TIS_BURST_COUNT_SHIFT 8
Expand Down Expand Up @@ -146,6 +147,24 @@ static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
#endif
}

/*
* Set the given flags in the STS register by clearing the register but
* preserving the SELFTEST_DONE flag and then setting the new flags.
*
* The SELFTEST_DONE flag is acquired from the backend that determines it by
* peeking into TPM commands.
*
* A VM suspend/resume will preserve the flag by storing it into the VM
* device state, but the backend will not remember it when QEMU is started
* again. Therefore, we cache the flag here. Once set, it will not be unset
* except by a reset.
*/
static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags)
{
l->sts &= TPM_TIS_STS_SELFTEST_DONE;
l->sts |= flags;
}

/*
* Send a request to the TPM.
*/
Expand Down Expand Up @@ -257,7 +276,8 @@ static void tpm_tis_abort(TPMState *s, uint8_t locty)
*/
if (tis->aborting_locty == tis->next_locty) {
tis->loc[tis->aborting_locty].state = TPM_TIS_STATE_READY;
tis->loc[tis->aborting_locty].sts = TPM_TIS_STS_COMMAND_READY;
tpm_tis_sts_set(&tis->loc[tis->aborting_locty],
TPM_TIS_STS_COMMAND_READY);
tpm_tis_raise_irq(s, tis->aborting_locty, TPM_TIS_INT_COMMAND_READY);
}

Expand Down Expand Up @@ -302,7 +322,8 @@ static void tpm_tis_receive_bh(void *opaque)
TPMTISEmuState *tis = &s->s.tis;
uint8_t locty = s->locty_number;

tis->loc[locty].sts = TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE;
tpm_tis_sts_set(&tis->loc[locty],
TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
tis->loc[locty].state = TPM_TIS_STATE_COMPLETION;
tis->loc[locty].r_offset = 0;
tis->loc[locty].w_offset = 0;
Expand All @@ -322,12 +343,20 @@ static void tpm_tis_receive_bh(void *opaque)
/*
* Callback from the TPM to indicate that the response was received.
*/
static void tpm_tis_receive_cb(TPMState *s, uint8_t locty)
static void tpm_tis_receive_cb(TPMState *s, uint8_t locty,
bool is_selftest_done)
{
TPMTISEmuState *tis = &s->s.tis;
uint8_t l;

assert(s->locty_number == locty);

if (is_selftest_done) {
for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
tis->loc[locty].sts |= TPM_TIS_STS_SELFTEST_DONE;
}
}

qemu_bh_schedule(tis->bh);
}

Expand All @@ -346,7 +375,7 @@ static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++];
if (tis->loc[locty].r_offset >= len) {
/* got last byte */
tis->loc[locty].sts = TPM_TIS_STS_VALID;
tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID);
#ifdef RAISE_STS_IRQ
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
#endif
Expand Down Expand Up @@ -714,7 +743,7 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
break;

case TPM_TIS_STATE_IDLE:
tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY;
tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_COMMAND_READY);
tis->loc[locty].state = TPM_TIS_STATE_READY;
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
break;
Expand All @@ -733,7 +762,8 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
/* shortcut to ready state with C/R set */
tis->loc[locty].state = TPM_TIS_STATE_READY;
if (!(tis->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY;
tpm_tis_sts_set(&tis->loc[locty],
TPM_TIS_STS_COMMAND_READY);
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
}
tis->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE);
Expand All @@ -755,8 +785,9 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
switch (tis->loc[locty].state) {
case TPM_TIS_STATE_COMPLETION:
tis->loc[locty].r_offset = 0;
tis->loc[locty].sts = TPM_TIS_STS_VALID |
TPM_TIS_STS_DATA_AVAILABLE;
tpm_tis_sts_set(&tis->loc[locty],
TPM_TIS_STS_VALID|
TPM_TIS_STS_DATA_AVAILABLE);
break;
default:
/* ignore */
Expand All @@ -780,7 +811,8 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
val, size);
if (tis->loc[locty].state == TPM_TIS_STATE_READY) {
tis->loc[locty].state = TPM_TIS_STATE_RECEPTION;
tis->loc[locty].sts = TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID;
tpm_tis_sts_set(&tis->loc[locty],
TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
}

val >>= shift;
Expand All @@ -796,7 +828,7 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
val >>= 8;
size--;
} else {
tis->loc[locty].sts = TPM_TIS_STS_VALID;
tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID);
}
}

Expand All @@ -809,11 +841,11 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
#endif
len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer);
if (len > tis->loc[locty].w_offset) {
tis->loc[locty].sts = TPM_TIS_STS_EXPECT |
TPM_TIS_STS_VALID;
tpm_tis_sts_set(&tis->loc[locty],
TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
} else {
/* packet complete */
tis->loc[locty].sts = TPM_TIS_STS_VALID;
tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID);
}
#ifdef RAISE_STS_IRQ
if (needIrq) {
Expand Down
2 changes: 1 addition & 1 deletion include/sysemu/tpm_backend.h
Expand Up @@ -56,7 +56,7 @@ struct TPMBackend {
QLIST_ENTRY(TPMBackend) list;
};

typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty);
typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty, bool selftest_done);

typedef struct TPMSizedBuffer {
uint32_t size;
Expand Down

0 comments on commit fd85908

Please sign in to comment.