2 * Copyright (c) 2018 Stormshield.
3 * Copyright (c) 2018 Semihalf.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
34 * CRB register space as defined in
35 * TCG_PC_Client_Platform_TPM_Profile_PTP_2.0_r1.03_v22
37 #define TPM_LOC_STATE 0x0
38 #define TPM_LOC_CTRL 0x8
39 #define TPM_LOC_STS 0xC
40 #define TPM_CRB_INTF_ID 0x30
41 #define TPM_CRB_CTRL_EXT 0x38
42 #define TPM_CRB_CTRL_REQ 0x40
43 #define TPM_CRB_CTRL_STS 0x44
44 #define TPM_CRB_CTRL_CANCEL 0x48
45 #define TPM_CRB_CTRL_START 0x4C
46 #define TPM_CRB_INT_ENABLE 0x50
47 #define TPM_CRB_INT_STS 0x54
48 #define TPM_CRB_CTRL_CMD_SIZE 0x58
49 #define TPM_CRB_CTRL_CMD_LADDR 0x5C
50 #define TPM_CRB_CTRL_CMD_HADDR 0x60
51 #define TPM_CRB_CTRL_RSP_SIZE 0x64
52 #define TPM_CRB_CTRL_RSP_ADDR 0x68
53 #define TPM_CRB_CTRL_RSP_HADDR 0x6c
54 #define TPM_CRB_DATA_BUFFER 0x80
56 #define TPM_LOC_STATE_ESTB BIT(0)
57 #define TPM_LOC_STATE_ASSIGNED BIT(1)
58 #define TPM_LOC_STATE_ACTIVE_MASK 0x9C
59 #define TPM_LOC_STATE_VALID BIT(7)
61 #define TPM_CRB_INTF_ID_TYPE_CRB 0x1
62 #define TPM_CRB_INTF_ID_TYPE 0x7
64 #define TPM_LOC_CTRL_REQUEST BIT(0)
65 #define TPM_LOC_CTRL_RELINQUISH BIT(1)
67 #define TPM_CRB_CTRL_REQ_GO_READY BIT(0)
68 #define TPM_CRB_CTRL_REQ_GO_IDLE BIT(1)
70 #define TPM_CRB_CTRL_STS_ERR_BIT BIT(0)
71 #define TPM_CRB_CTRL_STS_IDLE_BIT BIT(1)
73 #define TPM_CRB_CTRL_CANCEL_CMD 0x1
74 #define TPM_CRB_CTRL_CANCEL_CLEAR 0x0
76 #define TPM_CRB_CTRL_START_CMD BIT(0)
78 #define TPM_CRB_INT_ENABLE_BIT BIT(31)
88 int tpmcrb_transmit(struct tpm_sc *sc, size_t size);
90 static int tpmcrb_acpi_probe(device_t dev);
91 static int tpmcrb_attach(device_t dev);
92 static int tpmcrb_detach(device_t dev);
94 static ACPI_STATUS tpmcrb_fix_buff_offsets(ACPI_RESOURCE *res, void *arg);
96 static bool tpm_wait_for_u32(struct tpm_sc *sc, bus_size_t off,
97 uint32_t mask, uint32_t val, int32_t timeout);
98 static bool tpmcrb_request_locality(struct tpm_sc *sc, int locality);
99 static void tpmcrb_relinquish_locality(struct tpm_sc *sc);
100 static bool tpmcrb_cancel_cmd(struct tpm_sc *sc);
102 char *tpmcrb_ids[] = {"MSFT0101", NULL};
105 tpmcrb_acpi_probe(device_t dev)
108 ACPI_TABLE_TPM23 *tbl;
110 err = ACPI_ID_PROBE(device_get_parent(dev), dev, tpmcrb_ids, NULL);
114 status = AcpiGetTable(ACPI_SIG_TPM2, 1, (ACPI_TABLE_HEADER **) &tbl);
115 if(ACPI_FAILURE(status) ||
116 tbl->StartMethod != TPM2_START_METHOD_CRB)
119 device_set_desc(dev, "Trusted Platform Module 2.0, CRB mode");
124 tpmcrb_fix_buff_offsets(ACPI_RESOURCE *res, void *arg)
126 struct tpmcrb_sc *crb_sc;
130 crb_sc = (struct tpmcrb_sc *)arg;
132 if (res->Type != ACPI_RESOURCE_TYPE_FIXED_MEMORY32)
135 base_addr = res->Data.FixedMemory32.Address;
136 length = res->Data.FixedMemory32.AddressLength;
138 if (crb_sc->cmd_off > base_addr && crb_sc->cmd_off < base_addr + length)
139 crb_sc->cmd_off -= base_addr;
140 if (crb_sc->rsp_off > base_addr && crb_sc->rsp_off < base_addr + length)
141 crb_sc->rsp_off -= base_addr;
147 tpmcrb_attach(device_t dev)
149 struct tpmcrb_sc *crb_sc;
155 crb_sc = device_get_softc(dev);
157 handle = acpi_get_handle(dev);
160 sx_init(&sc->dev_lock, "TPM driver lock");
161 sc->buf = malloc(TPM_BUFSIZE, M_TPM20, M_WAITOK);
164 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
166 if (sc->mem_res == NULL) {
171 if(!tpmcrb_request_locality(sc, 0)) {
177 * Disable all interrupts for now, since I don't have a device that
178 * works in CRB mode and supports them.
180 AND4(sc, TPM_CRB_INT_ENABLE, ~TPM_CRB_INT_ENABLE_BIT);
181 sc->interrupts = false;
184 * Read addresses of Tx/Rx buffers and their sizes. Note that they
185 * can be implemented by a single buffer. Also for some reason CMD
186 * addr is stored in two 4 byte neighboring registers, whereas RSP is
187 * stored in a single 8 byte one.
190 crb_sc->rsp_off = RD8(sc, TPM_CRB_CTRL_RSP_ADDR);
192 crb_sc->rsp_off = RD4(sc, TPM_CRB_CTRL_RSP_ADDR);
193 crb_sc->rsp_off |= ((uint64_t) RD4(sc, TPM_CRB_CTRL_RSP_HADDR) << 32);
195 crb_sc->cmd_off = RD4(sc, TPM_CRB_CTRL_CMD_LADDR);
196 crb_sc->cmd_off |= ((uint64_t) RD4(sc, TPM_CRB_CTRL_CMD_HADDR) << 32);
197 crb_sc->cmd_buf_size = RD4(sc, TPM_CRB_CTRL_CMD_SIZE);
198 crb_sc->rsp_buf_size = RD4(sc, TPM_CRB_CTRL_RSP_SIZE);
200 tpmcrb_relinquish_locality(sc);
202 /* Emulator returns address in acpi space instead of an offset */
203 status = AcpiWalkResources(handle, "_CRS", tpmcrb_fix_buff_offsets,
205 if (ACPI_FAILURE(status)) {
210 if (crb_sc->rsp_off == crb_sc->cmd_off) {
212 * If Tx/Rx buffers are implemented as one they have to be of
215 if (crb_sc->cmd_buf_size != crb_sc->rsp_buf_size) {
216 device_printf(sc->dev,
217 "Overlapping Tx/Rx buffers have different sizes\n");
223 sc->transmit = tpmcrb_transmit;
225 result = tpm20_init(sc);
233 tpmcrb_detach(device_t dev)
237 sc = device_get_softc(dev);
240 if (sc->mem_res != NULL)
241 bus_release_resource(dev, SYS_RES_MEMORY,
242 sc->mem_rid, sc->mem_res);
248 tpm_wait_for_u32(struct tpm_sc *sc, bus_size_t off, uint32_t mask, uint32_t val,
252 /* Check for condition */
253 if ((RD4(sc, off) & mask) == val)
256 while (timeout > 0) {
257 if ((RD4(sc, off) & mask) == val)
260 pause("TPM in polling mode", 1);
267 tpmcrb_request_locality(struct tpm_sc *sc, int locality)
271 /* Currently we only support Locality 0 */
275 mask = TPM_LOC_STATE_VALID | TPM_LOC_STATE_ASSIGNED;
277 OR4(sc, TPM_LOC_CTRL, TPM_LOC_CTRL_REQUEST);
278 if (!tpm_wait_for_u32(sc, TPM_LOC_STATE, mask, mask, TPM_TIMEOUT_C))
285 tpmcrb_relinquish_locality(struct tpm_sc *sc)
288 OR4(sc, TPM_LOC_CTRL, TPM_LOC_CTRL_RELINQUISH);
292 tpmcrb_cancel_cmd(struct tpm_sc *sc)
296 WR4(sc, TPM_CRB_CTRL_CANCEL, TPM_CRB_CTRL_CANCEL_CMD);
297 if (!tpm_wait_for_u32(sc, TPM_CRB_CTRL_START,
298 mask, ~mask, TPM_TIMEOUT_B)) {
299 device_printf(sc->dev,
300 "Device failed to cancel command\n");
304 WR4(sc, TPM_CRB_CTRL_CANCEL, TPM_CRB_CTRL_CANCEL_CLEAR);
309 tpmcrb_transmit(struct tpm_sc *sc, size_t length)
311 struct tpmcrb_sc *crb_sc;
312 uint32_t mask, curr_cmd;
313 int timeout, bytes_available;
315 crb_sc = (struct tpmcrb_sc *)sc;
317 sx_assert(&sc->dev_lock, SA_XLOCKED);
319 if (length > crb_sc->cmd_buf_size) {
320 device_printf(sc->dev,
321 "Requested transfer is bigger than buffer size\n");
325 if (RD4(sc, TPM_CRB_CTRL_STS) & TPM_CRB_CTRL_STS_ERR_BIT) {
326 device_printf(sc->dev,
327 "Device has Error bit set\n");
330 if (!tpmcrb_request_locality(sc, 0)) {
331 device_printf(sc->dev,
332 "Failed to obtain locality\n");
335 /* Clear cancellation bit */
336 WR4(sc, TPM_CRB_CTRL_CANCEL, TPM_CRB_CTRL_CANCEL_CLEAR);
338 /* Switch device to idle state if necessary */
339 if (!(RD4(sc, TPM_CRB_CTRL_STS) & TPM_CRB_CTRL_STS_IDLE_BIT)) {
340 OR4(sc, TPM_CRB_CTRL_REQ, TPM_CRB_CTRL_REQ_GO_IDLE);
342 mask = TPM_CRB_CTRL_STS_IDLE_BIT;
343 if (!tpm_wait_for_u32(sc, TPM_CRB_CTRL_STS,
344 mask, mask, TPM_TIMEOUT_C)) {
345 device_printf(sc->dev,
346 "Failed to transition to idle state\n");
350 /* Switch to ready state */
351 OR4(sc, TPM_CRB_CTRL_REQ, TPM_CRB_CTRL_REQ_GO_READY);
353 mask = TPM_CRB_CTRL_REQ_GO_READY;
354 if (!tpm_wait_for_u32(sc, TPM_CRB_CTRL_STS,
355 mask, !mask, TPM_TIMEOUT_C)) {
356 device_printf(sc->dev,
357 "Failed to transition to ready state\n");
362 * Calculate timeout for current command.
363 * Command code is passed in bytes 6-10.
365 curr_cmd = be32toh(*(uint32_t *) (&sc->buf[6]));
366 timeout = tpm20_get_timeout(curr_cmd);
368 /* Send command and tell device to process it. */
369 bus_write_region_stream_1(sc->mem_res, crb_sc->cmd_off,
371 bus_barrier(sc->mem_res, crb_sc->cmd_off,
372 length, BUS_SPACE_BARRIER_WRITE);
374 WR4(sc, TPM_CRB_CTRL_START, TPM_CRB_CTRL_START_CMD);
375 bus_barrier(sc->mem_res, TPM_CRB_CTRL_START,
376 4, BUS_SPACE_BARRIER_WRITE);
379 if (!tpm_wait_for_u32(sc, TPM_CRB_CTRL_START, mask, ~mask, timeout)) {
380 device_printf(sc->dev,
381 "Timeout while waiting for device to process cmd\n");
382 if (!tpmcrb_cancel_cmd(sc))
386 /* Read response header. Length is passed in bytes 2 - 6. */
387 bus_read_region_stream_1(sc->mem_res, crb_sc->rsp_off,
388 sc->buf, TPM_HEADER_SIZE);
389 bytes_available = be32toh(*(uint32_t *) (&sc->buf[2]));
391 if (bytes_available > TPM_BUFSIZE || bytes_available < TPM_HEADER_SIZE) {
392 device_printf(sc->dev,
393 "Incorrect response size: %d\n",
398 bus_read_region_stream_1(sc->mem_res, crb_sc->rsp_off + TPM_HEADER_SIZE,
399 &sc->buf[TPM_HEADER_SIZE], bytes_available - TPM_HEADER_SIZE);
401 OR4(sc, TPM_CRB_CTRL_REQ, TPM_CRB_CTRL_REQ_GO_IDLE);
403 tpmcrb_relinquish_locality(sc);
404 sc->pending_data_length = bytes_available;
410 static device_method_t tpmcrb_methods[] = {
411 DEVMETHOD(device_probe, tpmcrb_acpi_probe),
412 DEVMETHOD(device_attach, tpmcrb_attach),
413 DEVMETHOD(device_detach, tpmcrb_detach),
414 DEVMETHOD(device_shutdown, tpm20_shutdown),
415 DEVMETHOD(device_suspend, tpm20_suspend),
418 static driver_t tpmcrb_driver = {
419 "tpmcrb", tpmcrb_methods, sizeof(struct tpmcrb_sc),
422 devclass_t tpmcrb_devclass;
423 DRIVER_MODULE(tpmcrb, acpi, tpmcrb_driver, tpmcrb_devclass, 0, 0);