From: dv Date: Sat, 23 Oct 2021 16:39:03 +0000 (+0000) Subject: tpm(4): add support for tpm2 CRB interface X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=306068a2945bf8b960d17abd9ecf9c7681ad638e;p=openbsd tpm(4): add support for tpm2 CRB interface Some modern tpm2 devices require or prefer drivers communicate via the CRB interface and not the TIS/fifo interface. This change adds basic support for detecting CRB start mode and using CRB to issue commands required for proper S4 hibernation. As a result, this also defines a new struct definition for the TPM2 acpi table required for start mode detection. This fixes recent S4 regressions on the Surface Go 2 caused by a change in firmware from Microsoft. Other CRB start methods may need implementing in the future to support additional hardware. tested by deraadt@ and many others, ok kettenis@ --- diff --git a/sys/dev/acpi/acpireg.h b/sys/dev/acpi/acpireg.h index f2ff1f02564..67f2a42f73b 100644 --- a/sys/dev/acpi/acpireg.h +++ b/sys/dev/acpi/acpireg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: acpireg.h,v 1.55 2021/03/23 09:41:12 patrick Exp $ */ +/* $OpenBSD: acpireg.h,v 1.56 2021/10/23 16:39:03 dv Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert * Copyright (c) 2005 Marco Peereboom @@ -466,6 +466,14 @@ struct acpi_facs { uint8_t reserved[31]; } __packed; +struct acpi_tpm2 { + struct acpi_table_header hdr; +#define TPM2_SIG "TPM2" + uint32_t reserved; + uint64_t control_addr; + uint32_t start_method; +} __packed; + /* * Intel ACPI DMA Remapping Entries */ diff --git a/sys/dev/acpi/tpm.c b/sys/dev/acpi/tpm.c index 6907c6a0d4e..6f3bafa0b76 100644 --- a/sys/dev/acpi/tpm.c +++ b/sys/dev/acpi/tpm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tpm.c,v 1.12 2021/09/11 23:22:38 deraadt Exp $ */ +/* $OpenBSD: tpm.c,v 1.13 2021/10/23 16:39:03 dv Exp $ */ /* * Minimal interface to Trusted Platform Module chips implementing the @@ -112,18 +112,73 @@ #define TPM_READ_TMO 120000 /* 2 minutes */ #define TPM_BURST_TMO 2000 /* 2sec */ +#define TPM2_START_METHOD_TIS 6 +#define TPM2_START_METHOD_CRB 7 + +#define TPM_CRB_LOC_STATE 0x0 +#define TPM_CRB_LOC_CTRL 0x8 +#define TPM_LOC_STS 0xC +#define TPM_CRB_INTF_ID 0x30 +#define TPM_CRB_CTRL_EXT 0x38 +#define TPM_CRB_CTRL_REQ 0x40 +#define TPM_CRB_CTRL_STS 0x44 +#define TPM_CRB_CTRL_CANCEL 0x48 +#define TPM_CRB_CTRL_START 0x4C +#define TPM_CRB_CTRL_CMD_SIZE 0x58 +#define TPM_CRB_CTRL_CMD_LADDR 0x5C +#define TPM_CRB_CTRL_CMD_HADDR 0x60 +#define TPM_CRB_CTRL_RSP_SIZE 0x64 +#define TPM_CRB_CTRL_RSP_LADDR 0x68 +#define TPM_CRB_CTRL_RSP_HADDR 0x6c +#define TPM_CRB_DATA_BUFFER 0x80 + +#define TPM_CRB_LOC_STATE_ESTB (1 << 0) +#define TPM_CRB_LOC_STATE_ASSIGNED (1 << 1) +#define TPM_CRB_LOC_ACTIVE_MASK 0x009c +#define TPM_CRB_LOC_VALID (1 << 7) + +#define TPM_CRB_LOC_REQUEST (1 << 0) +#define TPM_CRB_LOC_RELEASE (1 << 1) + +#define TPM_CRB_CTRL_REQ_GO_READY (1 << 0) +#define TPM_CRB_CTRL_REQ_GO_IDLE (1 << 1) + +#define TPM_CRB_CTRL_STS_ERR_BIT (1 << 0) +#define TPM_CRB_CTRL_STS_IDLE_BIT (1 << 1) + +#define TPM_CRB_CTRL_CANCEL_CMD 0x1 +#define TPM_CRB_CTRL_CANCEL_CLEAR 0x0 + +#define TPM_CRB_CTRL_START_CMD (1 << 0) +#define TPM_CRB_INT_ENABLED_BIT (1 << 31) + +#define TPM2_RC_SUCCESS 0x0000 +#define TPM2_RC_INITIALIZE 0x0100 +#define TPM2_RC_FAILURE 0x0101 +#define TPM2_RC_DISABLED 0x0120 +#define TPM2_RC_RETRY 0x0922 + struct tpm_softc { struct device sc_dev; bus_space_tag_t sc_bt; bus_space_handle_t sc_bh; + bus_size_t sc_bbase; struct acpi_softc *sc_acpi; struct aml_node *sc_devnode; uint32_t sc_devid; uint32_t sc_rev; + int sc_tpm20; + int sc_tpm_mode; +#define TPM_TIS 0 +#define TPM_CRB 1 + bus_size_t sc_cmd_off; + bus_size_t sc_rsp_off; + size_t sc_cmd_sz; + size_t sc_rsp_sz; int sc_enabled; }; @@ -148,18 +203,27 @@ void tpm_attach(struct device *, struct device *, void *); int tpm_activate(struct device *, int); int tpm_probe(bus_space_tag_t, bus_space_handle_t); -int tpm_init(struct tpm_softc *); -int tpm_read(struct tpm_softc *, void *, int, size_t *, int); -int tpm_write(struct tpm_softc *, void *, int); +int tpm_init_tis(struct tpm_softc *); +int tpm_init_crb(struct tpm_softc *); +int tpm_read_tis(struct tpm_softc *, void *, int, size_t *, int); +int tpm_read_crb(struct tpm_softc *, void *, int); +int tpm_write_tis(struct tpm_softc *, void *, int); +int tpm_write_crb(struct tpm_softc *, void *, int); int tpm_suspend(struct tpm_softc *); int tpm_resume(struct tpm_softc *); -int tpm_waitfor(struct tpm_softc *, uint8_t, int); -int tpm_request_locality(struct tpm_softc *, int); -void tpm_release_locality(struct tpm_softc *); -int tpm_getburst(struct tpm_softc *); +int tpm_waitfor(struct tpm_softc *, bus_space_handle_t, uint32_t, uint32_t, int); +int tpm_waitfor_status(struct tpm_softc *, uint8_t, int); +int tpm_request_locality_tis(struct tpm_softc *, int); +int tpm_request_locality_crb(struct tpm_softc *, int); +void tpm_release_locality_tis(struct tpm_softc *); +void tpm_release_locality_crb(struct tpm_softc *); +int tpm_getburst_tis(struct tpm_softc *); +int tpm_getburst_crb(struct tpm_softc *); uint8_t tpm_status(struct tpm_softc *); +uint32_t tpm2_start_method(struct acpi_softc *); + struct cfattach tpm_ca = { sizeof(struct tpm_softc), tpm_match, @@ -199,16 +263,35 @@ tpm_attach(struct device *parent, struct device *self, void *aux) struct tpm_softc *sc = (struct tpm_softc *)self; struct acpi_attach_args *aaa = aux; int64_t sta; + uint32_t start_method; sc->sc_acpi = (struct acpi_softc *)parent; sc->sc_devnode = aaa->aaa_node; sc->sc_enabled = 0; + sc->sc_tpm_mode = TPM_TIS; printf(" %s", sc->sc_devnode->name); if (strcmp(aaa->aaa_dev, "MSFT0101") == 0 || - strcmp(aaa->aaa_cdev, "MSFT0101") == 0) + strcmp(aaa->aaa_cdev, "MSFT0101") == 0) { sc->sc_tpm20 = 1; + /* Identify if using 1.2 TIS or 2.0's CRB methods */ + start_method = tpm2_start_method(sc->sc_acpi); + switch (start_method) { + case TPM2_START_METHOD_TIS: + /* Already default */ + break; + case TPM2_START_METHOD_CRB: + sc->sc_tpm_mode = TPM_CRB; + break; + default: + printf(": unsupported TPM2 start method\n"); + return; + } + } + + printf(" %s (%s)", sc->sc_tpm20 ? "2.0" : "1.2", + sc->sc_tpm_mode == TPM_TIS ? "TIS" : "CRB"); sta = acpi_getsta(sc->sc_acpi, sc->sc_devnode); if ((sta & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) != @@ -223,6 +306,7 @@ tpm_attach(struct device *parent, struct device *self, void *aux) } printf(" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]); + sc->sc_bbase = aaa->aaa_addr[0]; sc->sc_bt = aaa->aaa_bst[0]; if (bus_space_map(sc->sc_bt, aaa->aaa_addr[0], aaa->aaa_size[0], @@ -231,14 +315,21 @@ tpm_attach(struct device *parent, struct device *self, void *aux) return; } - if (!tpm_probe(sc->sc_bt, sc->sc_bh)) { - printf(": probe failed\n"); - return; - } + if (sc->sc_tpm_mode == TPM_TIS) { + if (!tpm_probe(sc->sc_bt, sc->sc_bh)) { + printf(": probe failed\n"); + return; + } - if (tpm_init(sc) != 0) { - printf(": init failed\n"); - return; + if (tpm_init_tis(sc) != 0) { + printf(": init failed\n"); + return; + } + } else { + if (tpm_init_crb(sc) != 0) { + printf(": init failed\n"); + return; + } } printf("\n"); @@ -305,9 +396,15 @@ tpm_suspend(struct tpm_softc *sc) * Tell the chip to save its state so the BIOS can then restore it upon * resume. */ - tpm_write(sc, command, commandlen); - tpm_read(sc, command, commandlen, NULL, TPM_HDRSIZE); - + if (sc->sc_tpm_mode == TPM_TIS) { + tpm_write_tis(sc, command, commandlen); + memset(command, 0, commandlen); + tpm_read_tis(sc, command, commandlen, NULL, TPM_HDRSIZE); + } else { + tpm_write_crb(sc, command, commandlen); + memset(command, 0, commandlen); + tpm_read_crb(sc, command, commandlen); + } return 0; } @@ -324,6 +421,28 @@ tpm_resume(struct tpm_softc *sc) return 0; } +uint32_t +tpm2_start_method(struct acpi_softc *sc) +{ + struct acpi_q *entry; + struct acpi_tpm2 *p_tpm2 = NULL; + + SIMPLEQ_FOREACH(entry, &sc->sc_tables, q_next) { + if (memcmp(entry->q_table, TPM2_SIG, + sizeof(TPM2_SIG) - 1) == 0) { + p_tpm2 = entry->q_table; + break; + } + } + + if (!p_tpm2) { + DPRINTF((", no TPM2 table")); + return 0; + } + + return p_tpm2->start_method; +} + int tpm_probe(bus_space_tag_t bt, bus_space_handle_t bh) { @@ -350,7 +469,7 @@ tpm_probe(bus_space_tag_t bt, bus_space_handle_t bh) } int -tpm_init(struct tpm_softc *sc) +tpm_init_tis(struct tpm_softc *sc) { uint32_t r, intmask; int i; @@ -369,7 +488,7 @@ tpm_init(struct tpm_softc *sc) intmask &= ~TPM_GLOBAL_INT_ENABLE; bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, intmask); - if (tpm_request_locality(sc, 0)) { + if (tpm_request_locality_tis(sc, 0)) { printf(", requesting locality failed\n"); return 1; } @@ -390,7 +509,66 @@ tpm_init(struct tpm_softc *sc) } int -tpm_request_locality(struct tpm_softc *sc, int l) +tpm_init_crb(struct tpm_softc *sc) +{ + uint32_t intmask; + int i; + + if (tpm_request_locality_crb(sc, 0)) { + printf(", request locality failed\n"); + return 1; + } + + /* ack and disable all interrupts, we'll be using polling only */ + intmask = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE); + intmask &= ~TPM_CRB_INT_ENABLED_BIT; + bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, intmask); + + /* Identify command and response registers and sizes */ + sc->sc_cmd_off = bus_space_read_4(sc->sc_bt, sc->sc_bh, + TPM_CRB_CTRL_CMD_LADDR); + sc->sc_cmd_off |= ((uint64_t) bus_space_read_4(sc->sc_bt, sc->sc_bh, + TPM_CRB_CTRL_CMD_HADDR) << 32); + sc->sc_cmd_sz = bus_space_read_4(sc->sc_bt, sc->sc_bh, + TPM_CRB_CTRL_CMD_SIZE); + + sc->sc_rsp_off = bus_space_read_4(sc->sc_bt, sc->sc_bh, + TPM_CRB_CTRL_RSP_LADDR); + sc->sc_rsp_off |= ((uint64_t) bus_space_read_4(sc->sc_bt, sc->sc_bh, + TPM_CRB_CTRL_RSP_HADDR) << 32); + sc->sc_rsp_sz = bus_space_read_4(sc->sc_bt, sc->sc_bh, + TPM_CRB_CTRL_RSP_SIZE); + + DPRINTF((", cmd @ 0x%lx, %ld, rsp @ 0x%lx, %ld", sc->sc_cmd_off, + sc->sc_cmd_sz, sc->sc_rsp_off, sc->sc_rsp_sz)); + + sc->sc_cmd_off = sc->sc_cmd_off - sc->sc_bbase; + sc->sc_rsp_off = sc->sc_rsp_off - sc->sc_bbase; + + tpm_release_locality_crb(sc); + + /* If it's a unified buffer, the sizes must be the same. */ + if (sc->sc_cmd_off == sc->sc_rsp_off) { + if (sc->sc_cmd_sz != sc->sc_rsp_sz) { + printf(", invalid buffer sizes\n"); + return 1; + } + } + + for (i = 0; tpm_devs[i].devid; i++) + if (tpm_devs[i].devid == sc->sc_devid) + break; + + if (tpm_devs[i].devid) + printf(", %s rev 0x%x", tpm_devs[i].name, sc->sc_rev); + else + printf(", device 0x%08x rev 0x%x", sc->sc_devid, sc->sc_rev); + + return 0; +} + +int +tpm_request_locality_tis(struct tpm_softc *sc, int l) { uint32_t r; int to; @@ -424,8 +602,38 @@ tpm_request_locality(struct tpm_softc *sc, int l) return 0; } +int +tpm_request_locality_crb(struct tpm_softc *sc, int l) +{ + uint32_t r, mask; + int to; + + if (l != 0) + return EINVAL; + + r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_LOC_CTRL); + bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_CRB_LOC_CTRL, + r | TPM_CRB_LOC_REQUEST); + + to = TPM_ACCESS_TMO * 200; + mask = TPM_CRB_LOC_STATE_ASSIGNED | TPM_CRB_LOC_VALID; + + r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_LOC_STATE); + while ((r & mask) != mask && to--) { + DELAY(10); + r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_LOC_STATE); + } + + if ((r & mask) != mask) { + printf(", CRB loc FAILED"); + return EBUSY; + } + + return 0; +} + void -tpm_release_locality(struct tpm_softc *sc) +tpm_release_locality_tis(struct tpm_softc *sc) { if ((bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) & (TPM_ACCESS_REQUEST_PENDING|TPM_ACCESS_VALID)) == @@ -436,6 +644,16 @@ tpm_release_locality(struct tpm_softc *sc) } } +void +tpm_release_locality_crb(struct tpm_softc *sc) +{ + uint32_t r; + + r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_LOC_CTRL); + bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_CRB_LOC_CTRL, + r | TPM_CRB_LOC_RELEASE); +} + int tpm_getburst(struct tpm_softc *sc) { @@ -474,7 +692,33 @@ tpm_status(struct tpm_softc *sc) } int -tpm_waitfor(struct tpm_softc *sc, uint8_t mask, int msecs) +tpm_waitfor(struct tpm_softc *sc, bus_size_t offset, uint32_t mask, + uint32_t val, int msecs) +{ + int usecs; + uint32_t r; + + usecs = msecs * 1000; + + r = bus_space_read_4(sc->sc_bt, sc->sc_bh, offset); + if ((r & mask) == val) + return 0; + + while (usecs > 0) { + r = bus_space_read_4(sc->sc_bt, sc->sc_bh, offset); + if ((r & mask) == val) + return 0; + DELAY(1); + usecs--; + } + + DPRINTF(("%s: %s: timed out, status 0x%x != 0x%x\n", + sc->sc_dev.dv_xname, __func__, r, mask)); + return ETIMEDOUT; +} + +int +tpm_waitfor_status(struct tpm_softc *sc, uint8_t mask, int msecs) { int usecs; uint8_t status; @@ -496,7 +740,7 @@ tpm_waitfor(struct tpm_softc *sc, uint8_t mask, int msecs) } int -tpm_read(struct tpm_softc *sc, void *buf, int len, size_t *count, +tpm_read_tis(struct tpm_softc *sc, void *buf, int len, size_t *count, int flags) { uint8_t *p = buf; @@ -508,8 +752,8 @@ tpm_read(struct tpm_softc *sc, void *buf, int len, size_t *count, cnt = 0; while (len > 0) { - if ((rv = tpm_waitfor(sc, TPM_STS_DATA_AVAIL | TPM_STS_VALID, - TPM_READ_TMO))) + if ((rv = tpm_waitfor_status(sc, + TPM_STS_DATA_AVAIL | TPM_STS_VALID, TPM_READ_TMO))) return rv; bcnt = tpm_getburst(sc); @@ -535,14 +779,76 @@ tpm_read(struct tpm_softc *sc, void *buf, int len, size_t *count, } int -tpm_write(struct tpm_softc *sc, void *buf, int len) +tpm_read_crb(struct tpm_softc *sc, void *buf, int len) +{ + uint8_t *p = buf; + uint32_t sz = 0, mask, rc; + size_t count = 0; + int r; + + DPRINTF(("%s: %s %d:", sc->sc_dev.dv_xname, __func__, len)); + + if (len < TPM_HDRSIZE) { + printf("%s: %s buf len too small\n", sc->sc_dev.dv_xname, + __func__); + return EINVAL; + } + + while (count < TPM_HDRSIZE) { + *p = bus_space_read_1(sc->sc_bt, sc->sc_bh, + sc->sc_rsp_off + count); + DPRINTF((" %02x", *p)); + count++; + p++; + } + DPRINTF(("\n")); + + /* Response length is bytes 2-5 in the response header. */ + p = buf; + sz = be32toh(*(uint32_t *) (p + 2)); + if (sz < TPM_HDRSIZE || sz > sc->sc_rsp_sz) { + printf("%s: invalid response size %d\n", + sc->sc_dev.dv_xname, sz); + return EIO; + } + if (sz > len) + printf("%s: response size too large, truncated to %d\n", + sc->sc_dev.dv_xname, len); + + /* Response code is bytes 6-9. */ + rc = be32toh(*(uint32_t *) (p + 6)); + if (rc != TPM2_RC_SUCCESS) { + printf("%s: command failed (0x%04x)\n", sc->sc_dev.dv_xname, + rc); + /* Nothing we can do on failure. Still try to idle the tpm. */ + } + + /* Tell the device to go idle. */ + r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_REQ); + bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_REQ, + r | TPM_CRB_CTRL_REQ_GO_IDLE); + + mask = TPM_CRB_CTRL_STS_IDLE_BIT; + if (tpm_waitfor(sc, TPM_CRB_CTRL_STS, mask, mask, 200)) { + printf("%s: failed to transition to idle state after read\n", + sc->sc_dev.dv_xname); + } + + tpm_release_locality_crb(sc); + + DPRINTF(("%s: %s completed\n", sc->sc_dev.dv_xname, __func__)); + return 0; +} + +int +tpm_write_tis(struct tpm_softc *sc, void *buf, int len) { uint8_t *p = buf; uint8_t status; size_t count = 0; int rv, r; - if ((rv = tpm_request_locality(sc, 0)) != 0) + if ((rv = tpm_request_locality_tis(sc, 0)) != 0) return rv; DPRINTF(("%s: %s %d:", sc->sc_dev.dv_xname, __func__, len)); @@ -556,7 +862,8 @@ tpm_write(struct tpm_softc *sc, void *buf, int len) /* abort! */ bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS, TPM_STS_CMD_READY); - if ((rv = tpm_waitfor(sc, TPM_STS_CMD_READY, TPM_READ_TMO))) { + if ((rv = tpm_waitfor_status(sc, TPM_STS_CMD_READY, + TPM_READ_TMO))) { DPRINTF(("%s: failed waiting for ready after abort " "(0x%x)\n", sc->sc_dev.dv_xname, rv)); return rv; @@ -570,7 +877,7 @@ tpm_write(struct tpm_softc *sc, void *buf, int len) bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_DATA, *p++); count++; } - if ((rv = tpm_waitfor(sc, TPM_STS_VALID | TPM_STS_DATA_EXPECT, + if ((rv = tpm_waitfor_status(sc, TPM_STS_VALID | TPM_STS_DATA_EXPECT, TPM_READ_TMO))) { DPRINTF(("%s: %s: failed waiting for next byte (%d)\n", sc->sc_dev.dv_xname, __func__, rv)); @@ -583,7 +890,7 @@ tpm_write(struct tpm_softc *sc, void *buf, int len) bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_DATA, *p); count++; - if ((rv = tpm_waitfor(sc, TPM_STS_VALID, TPM_READ_TMO))) { + if ((rv = tpm_waitfor_status(sc, TPM_STS_VALID, TPM_READ_TMO))) { DPRINTF(("%s: %s: failed after last byte (%d)\n", sc->sc_dev.dv_xname, __func__, rv)); return rv; @@ -603,3 +910,87 @@ tpm_write(struct tpm_softc *sc, void *buf, int len) return 0; } + +int +tpm_write_crb(struct tpm_softc *sc, void *buf, int len) +{ + uint8_t *p = buf; + size_t count = 0; + uint32_t r, mask; + + if (len > sc->sc_cmd_sz) { + printf("%s: requested write length larger than cmd buffer\n", + sc->sc_dev.dv_xname); + return EINVAL; + } + + if (bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_STS) + & TPM_CRB_CTRL_STS_ERR_BIT) { + printf("%s: device error bit set\n", sc->sc_dev.dv_xname); + return EIO; + } + + if (tpm_request_locality_crb(sc, 0)) { + printf("%s: failed to acquire locality\n", sc->sc_dev.dv_xname); + return EIO; + } + + /* Clear cancellation bit */ + bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_CANCEL, + TPM_CRB_CTRL_CANCEL_CLEAR); + + /* Toggle to idle state (if needed) and then to ready */ + r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_STS); + if(!(r & TPM_CRB_CTRL_STS_IDLE_BIT)) { + printf("%s: asking device to idle\n", sc->sc_dev.dv_xname); + r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_REQ); + bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_REQ, + r | TPM_CRB_CTRL_REQ_GO_IDLE); + + mask = TPM_CRB_CTRL_STS_IDLE_BIT; + if (tpm_waitfor(sc, TPM_CRB_CTRL_STS, mask, mask, 200)) { + printf("%s: failed to transition to idle state before " + "write\n", sc->sc_dev.dv_xname); + return EIO; + } + } + r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_REQ); + bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_REQ, + r | TPM_CRB_CTRL_REQ_GO_READY); + mask = TPM_CRB_CTRL_REQ_GO_READY; + if (tpm_waitfor(sc, TPM_CRB_CTRL_STS, mask, !mask, 200)) { + printf("%s: failed to transition to ready state\n", + sc->sc_dev.dv_xname); + return EIO; + } + + /* Write the command */ + DPRINTF(("%s: %s %d:", sc->sc_dev.dv_xname, __func__, len)); + while (count < len) { + DPRINTF((" %02x", (uint8_t)(*p))); + bus_space_write_1(sc->sc_bt, sc->sc_bh, sc->sc_cmd_off + count, + *p++); + count++; + } + DPRINTF(("\n")); + bus_space_barrier(sc->sc_bt, sc->sc_bh, sc->sc_cmd_off, len, + BUS_SPACE_BARRIER_WRITE); + DPRINTF(("%s: %s wrote %lu bytes\n", sc->sc_dev.dv_xname, __func__, + count)); + + /* Send the Start Command request */ + bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_START, + TPM_CRB_CTRL_START_CMD); + bus_space_barrier(sc->sc_bt, sc->sc_bh, TPM_CRB_CTRL_START, 4, + BUS_SPACE_BARRIER_WRITE); + + /* Check if command was processed */ + mask = ~0; + if (tpm_waitfor(sc, TPM_CRB_CTRL_START, mask, ~mask, 200)) { + printf("%s: timeout waiting for device to process command\n", + sc->sc_dev.dv_xname); + return EIO; + } + + return 0; +}