📄 tpm_tis.c
字号:
/* raise an interrupt if allowed */static void tis_raise_irq(tpmState *s, uint8_t locty, uint32_t irqmask){ if (!s->irq_pending && (s->loc[locty].inte & INT_ENABLED) && (s->loc[locty].inte & irqmask)) { if ((irqmask & s->loc[locty].ints) == 0) {#ifdef DEBUG_TPM fprintf(logfile,"Raising IRQ for flag %08x\n",irqmask);#endif s->set_irq(s->irq_opaque, s->irq, 1); s->irq_pending = 1; s->loc[locty].ints |= irqmask; } }}/* abort execution of command */static void tis_abort(tpmState *s){ s->offset = 0; s->active_loc = s->next_locty; /* * Need to react differently depending on who's aborting now and * which locality will become active afterwards. */ if (s->aborting_locty == s->next_locty) { s->loc[s->aborting_locty].state = STATE_READY; s->loc[s->aborting_locty].sts = STS_COMMAND_READY; tis_raise_irq(s, s->aborting_locty, INT_COMMAND_READY); } /* locality after abort is another one than the current one */ if (s->aborting_locty != s->next_locty && s->next_locty != NO_LOCALITY) { s->loc[s->aborting_locty].access &= ~ACCESS_ACTIVE_LOCALITY; s->loc[s->next_locty].access |= ACCESS_ACTIVE_LOCALITY; tis_raise_irq(s, s->next_locty, INT_LOCALITY_CHANGED); } s->aborting_locty = NO_LOCALITY; /* nobody's aborting a command anymore */ qemu_del_timer(s->poll_timer);}/* abort current command */static void tis_prep_abort(tpmState *s, uint8_t locty, uint8_t newlocty){ s->aborting_locty = locty; /* current locality */ s->next_locty = newlocty; /* locality after successful abort */ /* * only abort a command using an interrupt if currently executing * a command AND if there's a valid connection to the vTPM. */ if (s->loc[locty].state == STATE_EXECUTION && IS_COMM_WITH_VTPM(s)) { /* start timer and inside the timer wait for the result */ s->poll_attempts = 0; tis_prep_next_interrupt(s); } else { tis_abort(s); }}/* * Try to receive a response from the vTPM */static void tis_attempt_receive(tpmState *s, uint8_t locty){ /* * Attempt to read from the vTPM here if * - not aborting a command * - command has been sent and state is 'EXECUTION' now * - no data are already available (data have already been read) * - there's a communication path to the vTPM established */ if (!IS_VALID_LOC(s->aborting_locty)) { if (s->loc[locty].state == STATE_EXECUTION) { if (0 == (s->loc[locty].sts & STS_DATA_AVAILABLE)){ if (IS_COMM_WITH_VTPM(s)) { int n = TPM_Receive(s, &s->buffer); if (n > 0) { s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE; s->loc[locty].state = STATE_COMPLETION; close_vtpm_channel(s, FORCE_CLOSE); tis_raise_irq(s, locty, INT_DATA_AVAILABLE); } } } } }}/* * Read a register of the TIS interface * See specs pages 33-63 for description of the registers */static uint32_t tis_mem_readl(void *opaque, target_phys_addr_t addr){ tpmState *s = (tpmState *)opaque; uint16_t offset = addr & 0xffc; uint8_t shift = (addr & 0x3) * 8; uint32_t val = 0; uint8_t locty = locality_from_addr(addr); if (offset == TPM_REG_ACCESS) { if (s->active_loc == locty) { s->loc[locty].access |= (1 << 5); } else { s->loc[locty].access &= ~(1 << 5); } val = s->loc[locty].access; } else if (offset == TPM_REG_INT_ENABLE) { val = s->loc[locty].inte; } else if (offset == TPM_REG_INT_VECTOR) { val = s->irq; } else if (offset == TPM_REG_INT_STATUS) { tis_attempt_receive(s, locty); val = s->loc[locty].ints; } else if (offset == TPM_REG_INTF_CAPABILITY) { val = CAPABILITIES_SUPPORTED; } else if (offset == TPM_REG_STS) { /* status register */ tis_attempt_receive(s, locty); val = (sizeof(s->buffer.buf) - s->offset) << 8 | s->loc[locty].sts; } else if (offset == TPM_REG_DATA_FIFO) { val = tpm_data_read(s, locty); } else if (offset == TPM_REG_DID_VID) { val = (TPM_DID << 16) | TPM_VID; } else if (offset == TPM_REG_RID) { val = TPM_RID; } if (shift) val >>= shift;#ifdef DEBUG_TPM fprintf(logfile," read(%08x) = %08x\n", (int)addr, val);#endif return val;}/* * Write a value to a register of the TIS interface * See specs pages 33-63 for description of the registers */static void tis_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val){ tpmState* s=(tpmState*)opaque; uint16_t off = addr & 0xfff; uint8_t locty = locality_from_addr(addr); int n, c; uint32_t len;#ifdef DEBUG_TPM fprintf(logfile,"write(%08x) = %08x\n", (int)addr, val);#endif if (off == TPM_REG_ACCESS) { if (val & ACCESS_ACTIVE_LOCALITY) { /* give up locality if currently owned */ if (s->active_loc == locty) { uint8_t newlocty = NO_LOCALITY; s->loc[locty].access &= ~(ACCESS_PENDING_REQUEST); /* anybody wants the locality ? */ for (c = NUM_LOCALITIES - 1; c >= 0; c--) { if (s->loc[c].access & ACCESS_REQUEST_USE) { s->loc[c].access |= ACCESS_TPM_REG_VALID_STS; s->loc[c].access &= ~ACCESS_REQUEST_USE; newlocty = c; break; } } tis_prep_abort(s, locty, newlocty); } } if (val & ACCESS_BEEN_SEIZED) { /* clear the flag */ s->loc[locty].access &= ~ACCESS_BEEN_SEIZED; } if (val & ACCESS_SEIZE) { if (locty > s->active_loc && IS_VALID_LOC(s->active_loc)) { s->loc[s->active_loc].access |= ACCESS_BEEN_SEIZED; s->loc[locty].access = ACCESS_TPM_REG_VALID_STS; tis_prep_abort(s, s->active_loc, locty); } } if (val & ACCESS_REQUEST_USE) { if (IS_VALID_LOC(s->active_loc)) { /* locality election */ s->loc[s->active_loc].access |= ACCESS_PENDING_REQUEST; } else { /* no locality active -> make this one active now */ s->loc[locty].access |= ACCESS_ACTIVE_LOCALITY; s->active_loc = locty; tis_raise_irq(s, locty, INT_LOCALITY_CHANGED); } } } else if (off == TPM_REG_INT_ENABLE) { s->loc[locty].inte = (val & (INT_ENABLED | (0x3 << 3) | INTERRUPTS_SUPPORTED)); } else if (off == TPM_REG_INT_STATUS) { /* clearing of interrupt flags */ if ((val & INTERRUPTS_SUPPORTED) && (s->loc[locty].ints & INTERRUPTS_SUPPORTED)) { s->set_irq(s->irq_opaque, s->irq, 0); s->irq_pending = 0; } s->loc[locty].ints &= ~(val & INTERRUPTS_SUPPORTED); } else if (off == TPM_REG_STS) { if (val & STS_COMMAND_READY) { if (s->loc[locty].state == STATE_IDLE) { s->loc[locty].sts = STS_COMMAND_READY; s->loc[locty].state = STATE_READY; tis_raise_irq(s, locty, INT_COMMAND_READY); } else if (s->loc[locty].state == STATE_COMPLETION || s->loc[locty].state == STATE_EXECUTION || s->loc[locty].state == STATE_RECEPTION) { /* abort currently running command */ tis_prep_abort(s, locty, locty); } } if (val & STS_TPM_GO) { n = TPM_Send(s, &s->buffer, locty, "tpm_data_write"); if (n > 0) { /* sending of data was successful */ s->offset = 0; s->loc[locty].state = STATE_EXECUTION; if (s->loc[locty].inte & (INT_ENABLED | INT_DATA_AVAILABLE)) { s->poll_attempts = 0; tis_prep_next_interrupt(s); } } } if (val & STS_RESPONSE_RETRY) { s->offset = 0; } } else if (off == TPM_REG_DATA_FIFO) { /* data fifo */ if (s->loc[locty].state == STATE_IDLE || s->loc[locty].state == STATE_EXECUTION || s->loc[locty].state == STATE_COMPLETION) { /* drop the byte */ } else {#ifdef TPM_DEBUG fprintf(logfile,"Byte to send to TPM: %02x\n", val);#endif s->loc[locty].state = STATE_RECEPTION; if (s->offset < sizeof(s->buffer.buf)) s->buffer.buf[s->offset++] = (uint8_t)val; if (s->offset > 5) { /* we have a packet length - see if we have all of it */ len = tpm_get_size_from_buffer(s->buffer.buf); if (len > s->offset) { s->loc[locty].sts = STS_EXPECT | STS_VALID; } else { s->loc[locty].sts = STS_VALID; } } } }}/* * Prepare the next interrupt for example after a command has * been sent out for the purpose of receiving the response. * Depending on how many interrupts (used for polling on the fd) have * already been schedule, this function determines the delta in time * to the next interrupt. This accomodates for commands that finish * quickly. */static void tis_prep_next_interrupt(tpmState *s){ int64_t expiration; int rate = 5; /* 5 times per second */ /* poll often at the beginning for quickly finished commands, then back off */ if (s->poll_attempts < 5) { rate = 20; } else if (s->poll_attempts < 10) { rate = 10; } expiration = qemu_get_clock(vm_clock) + (ticks_per_sec / rate); qemu_mod_timer(s->poll_timer, expiration); s->poll_attempts++;}/* * The polling routine called when the 'timer interrupt' fires. * Tries to receive a command from the vTPM. */static void tis_poll_timer(void *opaque){ tpmState* s=(tpmState*)opaque; uint8_t locty = s->active_loc; if (!IS_VALID_LOC(locty) || (!(s->loc[locty].inte & INT_ENABLED) && (s->aborting_locty != NO_LOCALITY)) || !IS_COMM_WITH_VTPM(s)) { /* no more interrupts requested, so no more polling needed */ qemu_del_timer(s->poll_timer); } if (!IS_COMM_WITH_VTPM(s)) { if (s->aborting_locty != NO_LOCALITY) { tis_abort(s); } return; } if (s->aborting_locty != NO_LOCALITY) { int n = TPM_Receive(s, &s->buffer);#ifdef DEBUG_TPM fprintf(logfile,"Receiving for abort.\n");#endif if (n > 0) { close_vtpm_channel(s, FORCE_CLOSE); tis_abort(s);#ifdef DEBUG_TPM fprintf(logfile,"Abort is complete.\n");#endif } else { tis_prep_next_interrupt(s); } } else if (IS_VALID_LOC(locty)) { if (s->loc[locty].state == STATE_EXECUTION) { /* poll for result */ int n = TPM_Receive(s, &s->buffer); if (n > 0) { s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE; s->loc[locty].state = STATE_COMPLETION; close_vtpm_channel(s, FORCE_CLOSE); tis_raise_irq(s, locty, INT_DATA_AVAILABLE); } else { /* nothing received */ tis_prep_next_interrupt(s); } } }}static CPUReadMemoryFunc *tis_readfn[3]={ tis_mem_readl, tis_mem_readl, tis_mem_readl};static CPUWriteMemoryFunc *tis_writefn[3]={ tis_mem_writel, tis_mem_writel, tis_mem_writel};/* * Save the internal state of this interface for later resumption.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -