git: f0ac0f73aea7 - main - tpm: crb: Improve ACPI control and buffer addresses
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 30 Oct 2026 22:07:23 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=f0ac0f73aea71fdcd70408c046cfca88d8a34076
commit f0ac0f73aea71fdcd70408c046cfca88d8a34076
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-09-18 06:35:27 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-10-30 22:06:53 +0000
tpm: crb: Improve ACPI control and buffer addresses
The TPM2 ControlAddress identifies the control area, not necessarily a
PTP locality block. Hyper-V exposes a start-method-7 control area at the
beginning of its ACPI memory resource, with command and response buffers
in separate reserved memory pages. Assuming control registers are at
resource + 0x40 instead writes CANCEL as LOC_CTRL and fails attach.
Map the control area using ControlAddress and map buffers using their
reported physical addresses. Reuse the resource mapping for contained
ranges and track ownership of independent mappings for cleanup. Request
locality only when the resource contains the preceding 64-byte PTP header.
Keep the existing Pluton notification callbacks and locality behavior for
that layout. Control, locality, and buffer accesses use their respective
mappings directly, without the tpm_bus accessor class.
Bound transfers by the mapped buffer sizes and TPM_BUFSIZE. Clamp an
overstated buffer that starts within the ACPI resource to its remaining
extent, while requiring room for a complete response header. Keep strict
control-area bounds and the shared-buffer size consistency check.
Write locality and state requests directly and wait for the corresponding
CTRL_REQ bit to clear. The old ready wait instead tested the fatal-status
bit in CTRL_STS. Enter ready state before reading buffer configuration,
and check fatal status again before reading a response, while locality
is still held. Retain common error cleanup and diagnose attach-time idle
transition failures without replacing an earlier error.
These layouts and register semantics are defined by TCG ACPI 1.5 Table 7
and PC Client PTP 1.03 sections 5.5.3.1, 5.5.3.4, and 5.5.3.5.
Validation: a Windows Server 2025 Hyper-V FreeBSD guest attached and
passed manufacturer queries, 700 GetRandom/PCR_Read pairs, concurrent
clients, five detach/reattach cycles, and device suspend/resume
with entropy harvesting active.
MFC after: 2 weeks
Sponsored by: BBOX.io
Differential Revision: https://reviews.freebsd.org/D59786
---
sys/dev/tpm/tpm_crb.c | 413 +++++++++++++++++++++++++++++++++-----------------
1 file changed, 276 insertions(+), 137 deletions(-)
diff --git a/sys/dev/tpm/tpm_crb.c b/sys/dev/tpm/tpm_crb.c
index f62d350948c8..ac472ce40eda 100644
--- a/sys/dev/tpm/tpm_crb.c
+++ b/sys/dev/tpm/tpm_crb.c
@@ -29,27 +29,29 @@
#include "tpm20.h"
/*
- * CRB register space as defined in
- * TCG_PC_Client_Platform_TPM_Profile_PTP_2.0_r1.03_v22
+ * PTP locality registers, relative to the start of the ACPI memory resource.
+ * TCG PC Client PTP 1.03, section 5.5.3.1, Table 23.
*/
#define TPM_LOC_STATE 0x0
#define TPM_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_INT_ENABLE 0x50
-#define TPM_CRB_INT_STS 0x54
-#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_ADDR 0x68
-#define TPM_CRB_CTRL_RSP_HADDR 0x6c
-#define TPM_CRB_DATA_BUFFER 0x80
+#define TPM_CRB_LOCALITY_SIZE 0x40
+
+/* Relative to the ControlAddress in the TPM2 ACPI table, not the resource. */
+#define TPM_CRB_CTRL_REQ 0x00
+#define TPM_CRB_CTRL_STS 0x04
+#define TPM_CRB_CTRL_CANCEL 0x08
+#define TPM_CRB_CTRL_START 0x0C
+#define TPM_CRB_INT_ENABLE 0x10
+#define TPM_CRB_INT_STS 0x14
+#define TPM_CRB_CTRL_CMD_SIZE 0x18
+#define TPM_CRB_CTRL_CMD_LADDR 0x1C
+#define TPM_CRB_CTRL_CMD_HADDR 0x20
+#define TPM_CRB_CTRL_RSP_SIZE 0x24
+#define TPM_CRB_CTRL_RSP_ADDR 0x28
+#define TPM_CRB_CTRL_SIZE 0x30
#define TPM_LOC_STATE_ESTB BIT(0)
#define TPM_LOC_STATE_ASSIGNED BIT(1)
@@ -115,6 +117,12 @@ static const struct tpmcrb_sm_cfg_map {
},
};
+struct tpmcrb_mapping {
+ bus_space_handle_t bsh;
+ bus_size_t size;
+ bool mapped; /* Owns a mapping outside mem_res. */
+};
+
struct tpmcrb_sc {
struct tpm_sc base;
const struct tpmcrb_sm_cfg *sm_cfg;
@@ -130,10 +138,11 @@ struct tpmcrb_sc {
bus_space_handle_t reply_bsh;
} pluton;
};
- bus_size_t cmd_off;
- bus_size_t rsp_off;
- size_t cmd_buf_size;
- size_t rsp_buf_size;
+ bus_space_tag_t bst;
+ struct tpmcrb_mapping ctrl;
+ struct tpmcrb_mapping cmd;
+ struct tpmcrb_mapping rsp;
+ bool has_locality;
};
int tpmcrb_transmit(device_t dev, struct tpm_priv *priv, size_t size);
@@ -142,13 +151,52 @@ static int tpmcrb_acpi_probe(device_t dev);
static int tpmcrb_attach(device_t dev);
static int tpmcrb_detach(device_t dev);
-static ACPI_STATUS tpmcrb_fix_buff_offsets(ACPI_RESOURCE *res, void *arg);
-
-static bool tpm_wait_for_u32(struct tpm_sc *sc, bus_size_t off,
+static bool tpmcrb_ctrl_wait(struct tpmcrb_sc *crb_sc, bus_size_t off,
uint32_t mask, uint32_t val, int32_t timeout);
-static bool tpmcrb_request_locality(struct tpm_sc *sc, int locality);
-static void tpmcrb_relinquish_locality(struct tpm_sc *sc);
-static bool tpmcrb_cancel_cmd(struct tpm_sc *sc);
+static bool tpmcrb_request_locality(struct tpmcrb_sc *crb_sc, int locality);
+static void tpmcrb_relinquish_locality(struct tpmcrb_sc *crb_sc);
+static bool tpmcrb_cancel_cmd(struct tpmcrb_sc *crb_sc);
+static bool tpmcrb_state_idle(struct tpmcrb_sc *crb_sc, bool wait);
+static bool tpmcrb_state_ready(struct tpmcrb_sc *crb_sc, bool wait);
+
+/* All control-register offsets are relative to the mapped control area. */
+static uint32_t
+tpmcrb_ctrl_read_4(struct tpmcrb_sc *crb_sc, bus_size_t off)
+{
+
+ return (bus_space_read_4(crb_sc->bst, crb_sc->ctrl.bsh, off));
+}
+
+static uint64_t
+tpmcrb_ctrl_read_8(struct tpmcrb_sc *crb_sc, bus_size_t off)
+{
+#ifdef __i386__
+ uint64_t val;
+
+ val = tpmcrb_ctrl_read_4(crb_sc, off);
+ val |= (uint64_t)tpmcrb_ctrl_read_4(crb_sc, off + 4) << 32;
+ return (val);
+#else
+
+ return (bus_space_read_8(crb_sc->bst, crb_sc->ctrl.bsh, off));
+#endif
+}
+
+static void
+tpmcrb_ctrl_write_4(struct tpmcrb_sc *crb_sc, bus_size_t off, uint32_t val)
+{
+
+ bus_space_write_4(crb_sc->bst, crb_sc->ctrl.bsh, off, val);
+}
+
+static void
+tpmcrb_ctrl_write_barrier(struct tpmcrb_sc *crb_sc, bus_size_t off,
+ bus_size_t length)
+{
+
+ bus_space_barrier(crb_sc->bst, crb_sc->ctrl.bsh, off, length,
+ BUS_SPACE_BARRIER_WRITE);
+}
char *tpmcrb_ids[] = {"MSFT0101", NULL};
@@ -182,6 +230,10 @@ tpmcrb_acpi_probe(device_t dev)
status = AcpiGetTable(ACPI_SIG_TPM2, 1, (ACPI_TABLE_HEADER **) &tbl);
if (ACPI_FAILURE(status))
return (ENXIO);
+ if (tbl->Header.Length < sizeof(*tbl)) {
+ AcpiPutTable((ACPI_TABLE_HEADER *)tbl);
+ return (ENXIO);
+ }
smethod = tbl->StartMethod;
AcpiPutTable((ACPI_TABLE_HEADER *)tbl);
@@ -194,31 +246,65 @@ tpmcrb_acpi_probe(device_t dev)
return (0);
}
-static ACPI_STATUS
-tpmcrb_fix_buff_offsets(ACPI_RESOURCE *res, void *arg)
+/* Reuse the resource mapping when possible; buffers may be in reserved RAM. */
+static int
+tpmcrb_map(struct tpmcrb_sc *crb_sc, struct tpmcrb_mapping *map,
+ uint64_t addr, bus_size_t size)
{
- struct tpmcrb_sc *crb_sc;
- size_t length;
- uint32_t base_addr;
-
- crb_sc = (struct tpmcrb_sc *)arg;
-
- if (res->Type != ACPI_RESOURCE_TYPE_FIXED_MEMORY32)
- return (AE_OK);
+ struct resource *res = crb_sc->base.mem_res;
+ rman_res_t start, end;
+ int error;
+
+ if (addr == 0 || size == 0 || addr > BUS_SPACE_MAXADDR ||
+ size - 1 > BUS_SPACE_MAXADDR - addr)
+ return (EINVAL);
+ start = rman_get_start(res);
+ end = rman_get_end(res);
+ if (addr <= end && addr + size - 1 >= start) {
+ if (addr < start || addr + size - 1 > end)
+ return (EINVAL);
+ error = bus_space_subregion(crb_sc->bst, rman_get_bushandle(res),
+ addr - start, size, &map->bsh);
+ } else {
+ error = bus_space_map(crb_sc->bst, addr, size, 0, &map->bsh);
+ if (error == 0)
+ map->mapped = true;
+ }
+ if (error == 0)
+ map->size = size;
+ return (error);
+}
- base_addr = res->Data.FixedMemory32.Address;
- length = res->Data.FixedMemory32.AddressLength;
+static void
+tpmcrb_unmap(struct tpmcrb_sc *crb_sc, struct tpmcrb_mapping *map)
+{
- if (crb_sc->cmd_off > base_addr && crb_sc->cmd_off < base_addr + length)
- crb_sc->cmd_off -= base_addr;
- if (crb_sc->rsp_off > base_addr && crb_sc->rsp_off < base_addr + length)
- crb_sc->rsp_off -= base_addr;
+ if (map->mapped)
+ bus_space_unmap(crb_sc->bst, map->bsh, map->size);
+ memset(map, 0, sizeof(*map));
+}
- return (AE_OK);
+static int
+tpmcrb_map_buffer(struct tpmcrb_sc *crb_sc, struct tpmcrb_mapping *map,
+ uint64_t addr, uint32_t size)
+{
+ struct resource *res = crb_sc->base.mem_res;
+ rman_res_t start, end;
+
+ /* The character device never transfers more than TPM_BUFSIZE bytes. */
+ size = MIN(size, TPM_BUFSIZE);
+ start = rman_get_start(res);
+ end = rman_get_end(res);
+ /* Firmware can report a buffer larger than its ACPI memory resource. */
+ if (addr >= start && addr <= end && end - addr + 1 < size)
+ size = end - addr + 1;
+ if (size < TPM_HEADER_SIZE)
+ return (EINVAL);
+ return (tpmcrb_map(crb_sc, map, addr, size));
}
static bool
-tpmcrb_attach_startmethod(struct tpmcrb_sc *crb_sc)
+tpmcrb_attach_acpi(struct tpmcrb_sc *crb_sc)
{
const struct tpmcrb_sm_cfg_map *sm_cfg_map;
const struct tpmcrb_sm_cfg *sm_cfg;
@@ -227,12 +313,30 @@ tpmcrb_attach_startmethod(struct tpmcrb_sc *crb_sc)
ACPI_STATUS status;
bool ret;
- /*
- * Grab what we need from the StartMethod.
- */
status = AcpiGetTable(ACPI_SIG_TPM2, 1, (ACPI_TABLE_HEADER **)(void **)&tbl);
if (ACPI_FAILURE(status))
return (false);
+ if (tbl->Header.Length < sizeof(*tbl)) {
+ AcpiPutTable((ACPI_TABLE_HEADER *)tbl);
+ return (false);
+ }
+
+ /*
+ * TCG ACPI 1.5, Table 7: ControlAddress points at CTRL_REQ. Only
+ * the PTP layout (PTP 1.03, Table 23) has locality registers in the
+ * preceding 64 bytes. A control-area-only interface has no such
+ * registers; do not interpret its Cancel register as LOC_CTRL.
+ */
+ if ((tbl->ControlAddress & 7) != 0 ||
+ tpmcrb_map(crb_sc, &crb_sc->ctrl, tbl->ControlAddress,
+ TPM_CRB_CTRL_SIZE) != 0) {
+ device_printf(crb_sc->base.dev, "Cannot map CRB control area\n");
+ AcpiPutTable((ACPI_TABLE_HEADER *)tbl);
+ return (false);
+ }
+ crb_sc->has_locality = tbl->ControlAddress >= TPM_CRB_LOCALITY_SIZE &&
+ tbl->ControlAddress - TPM_CRB_LOCALITY_SIZE ==
+ rman_get_start(crb_sc->base.mem_res);
sm_cfg_map = tpmcrb_acpi_startmethod_cfg(tbl->StartMethod);
MPASS(sm_cfg_map != NULL);
@@ -256,13 +360,12 @@ tpmcrb_attach(device_t dev)
{
struct tpmcrb_sc *crb_sc;
struct tpm_sc *sc;
- ACPI_HANDLE handle;
- ACPI_STATUS status;
+ uint64_t cmd_addr, rsp_addr;
+ uint32_t cmd_size, rsp_size;
int result;
crb_sc = device_get_softc(dev);
sc = &crb_sc->base;
- handle = acpi_get_handle(dev);
sc->dev = dev;
sx_init(&sc->dev_lock, "TPM driver lock");
@@ -274,65 +377,78 @@ tpmcrb_attach(device_t dev)
tpmcrb_detach(dev);
return (ENXIO);
}
+ crb_sc->bst = rman_get_bustag(sc->mem_res);
- if (!tpmcrb_attach_startmethod(crb_sc)) {
+ if (!tpmcrb_attach_acpi(crb_sc)) {
tpmcrb_detach(dev);
return (ENXIO);
}
- if(!tpmcrb_request_locality(sc, 0)) {
+ if (!tpmcrb_request_locality(crb_sc, 0)) {
+ device_printf(dev, "Failed to obtain locality\n");
tpmcrb_detach(dev);
return (ENXIO);
}
+ if (!tpmcrb_state_ready(crb_sc, true)) {
+ device_printf(dev, "Failed to transition to ready state\n");
+ result = ENXIO;
+ goto out;
+ }
/*
* Disable all interrupts for now, since I don't have a device that
* works in CRB mode and supports them.
*/
- AND4(sc, TPM_CRB_INT_ENABLE, ~TPM_CRB_INT_ENABLE_BIT);
+ tpmcrb_ctrl_write_4(crb_sc, TPM_CRB_INT_ENABLE,
+ tpmcrb_ctrl_read_4(crb_sc, TPM_CRB_INT_ENABLE) &
+ ~TPM_CRB_INT_ENABLE_BIT);
sc->interrupts = false;
/*
* Read addresses of Tx/Rx buffers and their sizes. Note that they
- * can be implemented by a single buffer. Also for some reason CMD
- * addr is stored in two 4 byte neighboring registers, whereas RSP is
- * stored in a single 8 byte one.
+ * can be implemented by a single buffer. PTP 1.03, section 5.5.3.1
+ * requires naturally aligned accesses: the command address needs two
+ * 32-bit reads, whereas the response address permits a 64-bit read.
*/
-#ifdef __amd64__
- crb_sc->rsp_off = TPM_READ_8(sc->dev, TPM_CRB_CTRL_RSP_ADDR);
-#else
- crb_sc->rsp_off = TPM_READ_4(sc->dev, TPM_CRB_CTRL_RSP_ADDR);
- crb_sc->rsp_off |= ((uint64_t) TPM_READ_4(sc->dev, TPM_CRB_CTRL_RSP_HADDR) << 32);
-#endif
- crb_sc->cmd_off = TPM_READ_4(sc->dev, TPM_CRB_CTRL_CMD_LADDR);
- crb_sc->cmd_off |= ((uint64_t) TPM_READ_4(sc->dev, TPM_CRB_CTRL_CMD_HADDR) << 32);
- crb_sc->cmd_buf_size = TPM_READ_4(sc->dev, TPM_CRB_CTRL_CMD_SIZE);
- crb_sc->rsp_buf_size = TPM_READ_4(sc->dev, TPM_CRB_CTRL_RSP_SIZE);
-
- tpmcrb_relinquish_locality(sc);
-
- /* Emulator returns address in acpi space instead of an offset */
- status = AcpiWalkResources(handle, "_CRS", tpmcrb_fix_buff_offsets,
- (void *)crb_sc);
- if (ACPI_FAILURE(status)) {
- tpmcrb_detach(dev);
- return (ENXIO);
+ rsp_addr = tpmcrb_ctrl_read_8(crb_sc, TPM_CRB_CTRL_RSP_ADDR);
+ cmd_addr = tpmcrb_ctrl_read_4(crb_sc, TPM_CRB_CTRL_CMD_LADDR);
+ cmd_addr |= (uint64_t)tpmcrb_ctrl_read_4(crb_sc,
+ TPM_CRB_CTRL_CMD_HADDR) << 32;
+ cmd_size = tpmcrb_ctrl_read_4(crb_sc, TPM_CRB_CTRL_CMD_SIZE);
+ rsp_size = tpmcrb_ctrl_read_4(crb_sc, TPM_CRB_CTRL_RSP_SIZE);
+
+ /* PTP 1.03, Table 23 requires equal sizes for a shared buffer. */
+ if (cmd_size < TPM_HEADER_SIZE || rsp_size < TPM_HEADER_SIZE ||
+ (cmd_addr == rsp_addr && cmd_size != rsp_size)) {
+ device_printf(dev, "Invalid CRB buffer sizes (%u, %u)\n",
+ cmd_size, rsp_size);
+ result = ENXIO;
+ goto out;
+ }
+ result = tpmcrb_map_buffer(crb_sc, &crb_sc->cmd, cmd_addr, cmd_size);
+ if (result != 0) {
+ device_printf(dev, "Cannot map CRB command buffer\n");
+ goto out;
+ }
+ if (cmd_addr == rsp_addr) {
+ crb_sc->rsp = crb_sc->cmd;
+ crb_sc->rsp.mapped = false;
+ } else {
+ result = tpmcrb_map_buffer(crb_sc, &crb_sc->rsp, rsp_addr, rsp_size);
+ if (result != 0)
+ device_printf(dev, "Cannot map CRB response buffer\n");
}
- if (crb_sc->rsp_off == crb_sc->cmd_off) {
- /*
- * If Tx/Rx buffers are implemented as one they have to be of
- * same size
- */
- if (crb_sc->cmd_buf_size != crb_sc->rsp_buf_size) {
- device_printf(sc->dev,
- "Overlapping Tx/Rx buffers have different sizes\n");
- tpmcrb_detach(dev);
- return (ENXIO);
- }
+out:
+ if (!tpmcrb_state_idle(crb_sc, true)) {
+ device_printf(dev, "Failed to transition to idle state\n");
+ if (result == 0)
+ result = ENXIO;
}
+ tpmcrb_relinquish_locality(crb_sc);
+ if (result == 0)
+ result = tpm20_init(sc);
- result = tpm20_init(sc);
if (result != 0)
tpmcrb_detach(dev);
@@ -351,25 +467,31 @@ tpmcrb_detach(device_t dev)
if (crb_sc->sm_cfg != NULL && crb_sc->sm_cfg->sm_detach != NULL)
(*crb_sc->sm_cfg->sm_detach)(crb_sc);
+ crb_sc->sm_cfg = NULL;
+ tpmcrb_unmap(crb_sc, &crb_sc->rsp);
+ tpmcrb_unmap(crb_sc, &crb_sc->cmd);
+ tpmcrb_unmap(crb_sc, &crb_sc->ctrl);
- if (sc->mem_res != NULL)
+ if (sc->mem_res != NULL) {
bus_release_resource(dev, SYS_RES_MEMORY,
sc->mem_rid, sc->mem_res);
+ sc->mem_res = NULL;
+ }
return (0);
}
static bool
-tpm_wait_for_u32(struct tpm_sc *sc, bus_size_t off, uint32_t mask, uint32_t val,
- int32_t timeout)
+tpmcrb_ctrl_wait(struct tpmcrb_sc *crb_sc, bus_size_t off, uint32_t mask,
+ uint32_t val, int32_t timeout)
{
/* Check for condition */
- if ((TPM_READ_4(sc->dev, off) & mask) == val)
+ if ((tpmcrb_ctrl_read_4(crb_sc, off) & mask) == val)
return (true);
while (timeout > 0) {
- if ((TPM_READ_4(sc->dev, off) & mask) == val)
+ if ((tpmcrb_ctrl_read_4(crb_sc, off) & mask) == val)
return (true);
pause("TPM in polling mode", 1);
@@ -379,44 +501,52 @@ tpm_wait_for_u32(struct tpm_sc *sc, bus_size_t off, uint32_t mask, uint32_t val,
}
static bool
-tpmcrb_request_locality(struct tpm_sc *sc, int locality)
+tpmcrb_request_locality(struct tpmcrb_sc *crb_sc, int locality)
{
+ struct resource *res = crb_sc->base.mem_res;
uint32_t mask;
+ int timeout;
/* Currently we only support Locality 0 */
if (locality != 0)
return (false);
+ if (!crb_sc->has_locality)
+ return (true);
mask = TPM_LOC_STATE_VALID | TPM_LOC_STATE_ASSIGNED;
- OR4(sc, TPM_LOC_CTRL, TPM_LOC_CTRL_REQUEST);
- if (!tpm_wait_for_u32(sc, TPM_LOC_STATE, mask, mask, TPM_TIMEOUT_C))
- return (false);
-
- return (true);
+ bus_write_4(res, TPM_LOC_CTRL, TPM_LOC_CTRL_REQUEST);
+ for (timeout = TPM_TIMEOUT_C; timeout > 0; timeout -= tick) {
+ if ((bus_read_4(res, TPM_LOC_STATE) & mask) == mask)
+ return (true);
+ pause("TPM locality", 1);
+ }
+ return (false);
}
static void
-tpmcrb_relinquish_locality(struct tpm_sc *sc)
+tpmcrb_relinquish_locality(struct tpmcrb_sc *crb_sc)
{
- OR4(sc, TPM_LOC_CTRL, TPM_LOC_CTRL_RELINQUISH);
+ if (crb_sc->has_locality)
+ bus_write_4(crb_sc->base.mem_res, TPM_LOC_CTRL,
+ TPM_LOC_CTRL_RELINQUISH);
}
static bool
-tpmcrb_cancel_cmd(struct tpm_sc *sc)
+tpmcrb_cancel_cmd(struct tpmcrb_sc *crb_sc)
{
uint32_t mask = ~0;
- TPM_WRITE_4(sc->dev, TPM_CRB_CTRL_CANCEL, TPM_CRB_CTRL_CANCEL_CMD);
- if (!tpm_wait_for_u32(sc, TPM_CRB_CTRL_START,
+ tpmcrb_ctrl_write_4(crb_sc, TPM_CRB_CTRL_CANCEL, TPM_CRB_CTRL_CANCEL_CMD);
+ if (!tpmcrb_ctrl_wait(crb_sc, TPM_CRB_CTRL_START,
mask, ~mask, TPM_TIMEOUT_B)) {
- device_printf(sc->dev,
+ device_printf(crb_sc->base.dev,
"Device failed to cancel command\n");
return (false);
}
- TPM_WRITE_4(sc->dev, TPM_CRB_CTRL_CANCEL, TPM_CRB_CTRL_CANCEL_CLEAR);
+ tpmcrb_ctrl_write_4(crb_sc, TPM_CRB_CTRL_CANCEL, TPM_CRB_CTRL_CANCEL_CLEAR);
return (true);
}
@@ -447,20 +577,19 @@ tpmcrb_notify_state_changing(struct tpmcrb_sc *crb_sc, int timeout)
static bool
tpmcrb_state_idle(struct tpmcrb_sc *crb_sc, bool wait)
{
- struct tpm_sc *sc;
int mask, timeout;
timeout = wait ? TPM_TIMEOUT_C : 0;
- sc = &crb_sc->base;
- OR4(sc, TPM_CRB_CTRL_REQ, TPM_CRB_CTRL_REQ_GO_IDLE);
+ tpmcrb_ctrl_write_4(crb_sc, TPM_CRB_CTRL_REQ, TPM_CRB_CTRL_REQ_GO_IDLE);
if (!tpmcrb_notify_state_changing(crb_sc, timeout))
return (false);
if (timeout > 0) {
- mask = TPM_CRB_CTRL_STS_IDLE_BIT;
- if (!tpm_wait_for_u32(sc, TPM_CRB_CTRL_STS, mask, mask,
+ /* PTP 1.03, 5.5.3.4: the TPM clears the request to acknowledge. */
+ mask = TPM_CRB_CTRL_REQ_GO_IDLE;
+ if (!tpmcrb_ctrl_wait(crb_sc, TPM_CRB_CTRL_REQ, mask, 0,
timeout))
return (false);
}
@@ -471,20 +600,18 @@ tpmcrb_state_idle(struct tpmcrb_sc *crb_sc, bool wait)
static bool
tpmcrb_state_ready(struct tpmcrb_sc *crb_sc, bool wait)
{
- struct tpm_sc *sc;
int mask, timeout;
timeout = wait ? TPM_TIMEOUT_C : 0;
- sc = &crb_sc->base;
- OR4(sc, TPM_CRB_CTRL_REQ, TPM_CRB_CTRL_REQ_GO_READY);
+ tpmcrb_ctrl_write_4(crb_sc, TPM_CRB_CTRL_REQ, TPM_CRB_CTRL_REQ_GO_READY);
if (!tpmcrb_notify_state_changing(crb_sc, timeout))
return (false);
if (timeout > 0) {
mask = TPM_CRB_CTRL_REQ_GO_READY;
- if (!tpm_wait_for_u32(sc, TPM_CRB_CTRL_STS, mask, !mask,
+ if (!tpmcrb_ctrl_wait(crb_sc, TPM_CRB_CTRL_REQ, mask, 0,
timeout))
return (false);
}
@@ -496,25 +623,23 @@ int
tpmcrb_transmit(device_t dev, struct tpm_priv *priv, size_t length)
{
struct tpmcrb_sc *crb_sc;
- struct tpm_sc *sc;
uint32_t mask, curr_cmd;
int bytes_available, error, timeout;
bool command_started, locality;
crb_sc = device_get_softc(dev);
- sc = &crb_sc->base;
- sx_assert(&sc->dev_lock, SA_XLOCKED);
+ sx_assert(&crb_sc->base.dev_lock, SA_XLOCKED);
command_started = false;
locality = false;
- if (length > crb_sc->cmd_buf_size) {
+ if (length > crb_sc->cmd.size) {
device_printf(dev,
"Requested transfer is bigger than buffer size\n");
return (E2BIG);
}
- if (!tpmcrb_request_locality(sc, 0)) {
+ if (!tpmcrb_request_locality(crb_sc, 0)) {
device_printf(dev,
"Failed to obtain locality\n");
return (EIO);
@@ -527,17 +652,19 @@ tpmcrb_transmit(device_t dev, struct tpm_priv *priv, size_t length)
* before then. An AMD Pluton fTPM using the plain CRB start method
* reads all-ones, which looks like a permanently stuck tpmSts.
*/
- if (TPM_READ_4(dev, TPM_CRB_CTRL_STS) & TPM_CRB_CTRL_STS_ERR_BIT) {
+ if (tpmcrb_ctrl_read_4(crb_sc, TPM_CRB_CTRL_STS) &
+ TPM_CRB_CTRL_STS_ERR_BIT) {
device_printf(dev,
"Device has Error bit set\n");
error = EIO;
goto out;
}
/* Clear cancellation bit */
- TPM_WRITE_4(dev, TPM_CRB_CTRL_CANCEL, TPM_CRB_CTRL_CANCEL_CLEAR);
+ tpmcrb_ctrl_write_4(crb_sc, TPM_CRB_CTRL_CANCEL, TPM_CRB_CTRL_CANCEL_CLEAR);
/* Switch device to idle state if necessary */
- if (!(TPM_READ_4(dev, TPM_CRB_CTRL_STS) & TPM_CRB_CTRL_STS_IDLE_BIT)) {
+ if (!(tpmcrb_ctrl_read_4(crb_sc, TPM_CRB_CTRL_STS) &
+ TPM_CRB_CTRL_STS_IDLE_BIT)) {
if (!tpmcrb_state_idle(crb_sc, true)) {
device_printf(dev,
"Failed to transition to idle state\n");
@@ -562,39 +689,51 @@ tpmcrb_transmit(device_t dev, struct tpm_priv *priv, size_t length)
timeout = tpm20_get_timeout(curr_cmd);
/* Send command and tell device to process it. */
- bus_write_region_stream_1(sc->mem_res, crb_sc->cmd_off,
+ bus_space_write_region_stream_1(crb_sc->bst, crb_sc->cmd.bsh, 0,
priv->buf, length);
- TPM_WRITE_BARRIER(dev, crb_sc->cmd_off, length);
+ bus_space_barrier(crb_sc->bst, crb_sc->cmd.bsh, 0, length,
+ BUS_SPACE_BARRIER_WRITE);
- TPM_WRITE_4(dev, TPM_CRB_CTRL_START, TPM_CRB_CTRL_START_CMD);
- TPM_WRITE_BARRIER(dev, TPM_CRB_CTRL_START, 4);
+ tpmcrb_ctrl_write_4(crb_sc, TPM_CRB_CTRL_START, TPM_CRB_CTRL_START_CMD);
+ tpmcrb_ctrl_write_barrier(crb_sc, TPM_CRB_CTRL_START, 4);
command_started = true;
if (!tpmcrb_notify_cmdready(crb_sc, timeout)) {
device_printf(dev,
"Timeout while waiting for device to ready\n");
- if (!tpmcrb_cancel_cmd(sc)) {
+ if (!tpmcrb_cancel_cmd(crb_sc)) {
error = EIO;
goto out;
}
}
mask = ~0;
- if (!tpm_wait_for_u32(sc, TPM_CRB_CTRL_START, mask, ~mask, timeout)) {
+ if (!tpmcrb_ctrl_wait(crb_sc, TPM_CRB_CTRL_START, mask, ~mask, timeout)) {
device_printf(dev,
"Timeout while waiting for device to process cmd\n");
- if (!tpmcrb_cancel_cmd(sc)) {
+ if (!tpmcrb_cancel_cmd(crb_sc)) {
error = EIO;
goto out;
}
}
+ /* A fatal error during execution may leave no valid response. */
+ if (tpmcrb_ctrl_read_4(crb_sc, TPM_CRB_CTRL_STS) &
+ TPM_CRB_CTRL_STS_ERR_BIT) {
+ device_printf(dev, "Device has Error bit set after command\n");
+ error = EIO;
+ goto out;
+ }
+
/* Read response header. Length is passed in bytes 2 - 6. */
- bus_read_region_stream_1(sc->mem_res, crb_sc->rsp_off,
+ bus_space_barrier(crb_sc->bst, crb_sc->rsp.bsh, 0, crb_sc->rsp.size,
+ BUS_SPACE_BARRIER_READ);
+ bus_space_read_region_stream_1(crb_sc->bst, crb_sc->rsp.bsh, 0,
priv->buf, TPM_HEADER_SIZE);
bytes_available = be32toh(*(uint32_t *) (&priv->buf[2]));
- if (bytes_available > TPM_BUFSIZE || bytes_available < TPM_HEADER_SIZE) {
+ if (bytes_available < TPM_HEADER_SIZE ||
+ bytes_available > crb_sc->rsp.size) {
device_printf(dev,
"Incorrect response size: %d\n",
bytes_available);
@@ -602,8 +741,9 @@ tpmcrb_transmit(device_t dev, struct tpm_priv *priv, size_t length)
goto out;
}
- bus_read_region_stream_1(sc->mem_res, crb_sc->rsp_off + TPM_HEADER_SIZE,
- &priv->buf[TPM_HEADER_SIZE], bytes_available - TPM_HEADER_SIZE);
+ bus_space_read_region_stream_1(crb_sc->bst, crb_sc->rsp.bsh,
+ TPM_HEADER_SIZE, &priv->buf[TPM_HEADER_SIZE],
+ bytes_available - TPM_HEADER_SIZE);
priv->offset = 0;
priv->len = bytes_available;
@@ -612,16 +752,16 @@ tpmcrb_transmit(device_t dev, struct tpm_priv *priv, size_t length)
out:
if (locality) {
if (error != 0 && command_started &&
- (TPM_READ_4(dev, TPM_CRB_CTRL_START) &
+ (tpmcrb_ctrl_read_4(crb_sc, TPM_CRB_CTRL_START) &
TPM_CRB_CTRL_START_CMD) != 0)
- (void)tpmcrb_cancel_cmd(sc);
+ (void)tpmcrb_cancel_cmd(crb_sc);
if (!tpmcrb_state_idle(crb_sc, false)) {
device_printf(dev,
"Failed to transition to idle state post-send\n");
if (error == 0)
error = EIO;
}
- tpmcrb_relinquish_locality(sc);
+ tpmcrb_relinquish_locality(crb_sc);
}
return (error);
@@ -739,7 +879,6 @@ static device_method_t tpmcrb_methods[] = {
{0, 0}
};
-DEFINE_CLASS_1(tpmcrb, tpmcrb_driver, tpmcrb_methods, sizeof(struct tpmcrb_sc),
- tpm_bus_driver);
+DEFINE_CLASS_0(tpmcrb, tpmcrb_driver, tpmcrb_methods, sizeof(struct tpmcrb_sc));
DRIVER_MODULE(tpmcrb, acpi, tpmcrb_driver, 0, 0);