The branch main has been updated by kevans:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=6af3031c951614f2a42dfd67ce2c3a48ee8314ae

commit 6af3031c951614f2a42dfd67ce2c3a48ee8314ae
Author:     Kyle Evans <[email protected]>
AuthorDate: 2026-09-09 09:00:28 +0000
Commit:     Kyle Evans <[email protected]>
CommitDate: 2026-09-09 09:00:28 +0000

    tpm: crb: make the Pluton startmethod more resilient
    
    The original implementation assumed that the start/reply doorbells
    lived within the device _CRS space, but that isn't always the case.  On
    my AMD Ryzen 7640U-based frame.work laptop, device memory runs from
    0xc0500000-0xc0500fff while the doorbells are up around 0xc0508000.
    
    Stop sanity checking the addresses and just map them in to work reliably
    whether they're within the device range or not.
    
    pluton_wait_reply is cribbed from tpm_wait_for_u32, but rewritten
    slightly to read in just one place and to read one last time before
    giving up at the end of the timeout, just in case.
    
    Reviewed by:    kbowling
    Differential Revision:  https://reviews.freebsd.org/D59327
---
 sys/dev/tpm/tpm_crb.c | 115 ++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 87 insertions(+), 28 deletions(-)

diff --git a/sys/dev/tpm/tpm_crb.c b/sys/dev/tpm/tpm_crb.c
index 3e567e215313..e5bbec5d46da 100644
--- a/sys/dev/tpm/tpm_crb.c
+++ b/sys/dev/tpm/tpm_crb.c
@@ -76,18 +76,21 @@
 #define        TPM_CRB_INT_ENABLE_BIT          BIT(31)
 
 struct tpmcrb_sc;
-/* Attach */
+/* Attach/detach callbacks */
 typedef bool (sm_attach_t)(struct tpmcrb_sc *, void *, size_t);
+typedef void (sm_detach_t)(struct tpmcrb_sc *);
 /* State change notification (timeout == 0 for 'no timeout') */
 typedef bool (sm_statechange_t)(struct tpmcrb_sc *, int);
 
 struct tpmcrb_sm_cfg {
        sm_attach_t             *sm_attach;
+       sm_detach_t             *sm_detach;
        sm_statechange_t        *sm_statechange;
        sm_statechange_t        *sm_cmdready;
 };
 
 static sm_attach_t             pluton_attach;
+static sm_detach_t             pluton_detach;
 static sm_statechange_t                pluton_doorbell;
 
 static const struct tpmcrb_sm_cfg_map {
@@ -105,6 +108,7 @@ static const struct tpmcrb_sm_cfg_map {
                .desc = "Trusted Platform Module 2.0, CRB mode (Pluton)",
                .sm_cfg = {
                        .sm_attach = &pluton_attach,
+                       .sm_detach = &pluton_detach,
                        .sm_statechange = &pluton_doorbell,
                        .sm_cmdready = &pluton_doorbell,
                },
@@ -115,10 +119,15 @@ struct tpmcrb_sc {
        struct tpm_sc   base;
        const struct tpmcrb_sm_cfg      *sm_cfg;
        union {
-               /* StartMethod data */
+               /*
+                * StartMethod data.  The Pluton start/reply mailbox
+                * registers may live outside of the _CRS window, so they get
+                * their own bus_space mapping.
+                */
                struct {
-                       uint64_t         start_reg;
-                       uint64_t         reply_reg;
+                       bus_space_tag_t         bst;
+                       bus_space_handle_t      start_bsh;
+                       bus_space_handle_t      reply_bsh;
                } pluton;
        };
        bus_size_t      cmd_off;
@@ -333,11 +342,16 @@ tpmcrb_attach(device_t dev)
 static int
 tpmcrb_detach(device_t dev)
 {
+       struct tpmcrb_sc *crb_sc;
        struct tpm_sc *sc;
 
-       sc = device_get_softc(dev);
+       crb_sc = device_get_softc(dev);
+       sc = &crb_sc->base;
        tpm20_release(sc);
 
+       if (crb_sc->sm_cfg != NULL && crb_sc->sm_cfg->sm_detach != NULL)
+               (*crb_sc->sm_cfg->sm_detach)(crb_sc);
+
        if (sc->mem_res != NULL)
                bus_release_resource(dev, SYS_RES_MEMORY,
                    sc->mem_rid, sc->mem_res);
@@ -618,7 +632,7 @@ pluton_attach(struct tpmcrb_sc *crb_sc, void *smdataregion, 
size_t datasz)
 {
        struct tpmcrb_startmethod_pluton *smdata;
        struct tpm_sc *sc;
-       rman_res_t base_addr, end_addr;
+       bus_space_tag_t bst;
 
        if (datasz < sizeof(*smdata))
                return (false);
@@ -626,38 +640,83 @@ pluton_attach(struct tpmcrb_sc *crb_sc, void 
*smdataregion, size_t datasz)
        smdata = smdataregion;
        sc = &crb_sc->base;
 
-       base_addr = rman_get_start(sc->mem_res);
-       end_addr = rman_get_end(sc->mem_res);
-       /* Sanity check */
-       if (smdata->sm_startaddr < base_addr ||
-           smdata->sm_startaddr > end_addr ||
-           smdata->sm_replyaddr < base_addr ||
-           smdata->sm_replyaddr > end_addr)
+       /*
+        * The start/reply mailbox registers are not necessarily part of
+        * the CRB register window described by _CRS, so we map them
+        * independently.
+        */
+       bst = rman_get_bustag(sc->mem_res);
+       if (bus_space_map(bst, smdata->sm_startaddr, sizeof(uint32_t), 0,
+           &crb_sc->pluton.start_bsh) != 0) {
+               device_printf(sc->dev,
+                   "Failed to map Pluton start register at %#jx\n",
+                   (uintmax_t)smdata->sm_startaddr);
+               return (false);
+       }
+
+       if (bus_space_map(bst, smdata->sm_replyaddr, sizeof(uint32_t), 0,
+           &crb_sc->pluton.reply_bsh) != 0) {
+               device_printf(sc->dev,
+                   "Failed to map Pluton reply register at %#jx\n",
+                   (uintmax_t)smdata->sm_replyaddr);
+               bus_space_unmap(bst, crb_sc->pluton.start_bsh,
+                   sizeof(uint32_t));
                return (false);
+       }
 
-       crb_sc->pluton.start_reg = smdata->sm_startaddr - base_addr;
-       crb_sc->pluton.reply_reg = smdata->sm_replyaddr - base_addr;
+       crb_sc->pluton.bst = bst;
        return (true);
 }
 
-static bool
-pluton_doorbell(struct tpmcrb_sc *crb_sc, int timeout)
+static void
+pluton_detach(struct tpmcrb_sc *crb_sc)
 {
-       struct tpm_sc *sc;
-       device_t dev;
 
-       sc = &crb_sc->base;
-       dev = sc->dev;
-       TPM_WRITE_4(dev, crb_sc->pluton.start_reg, 1);
-       TPM_WRITE_BARRIER(dev, crb_sc->pluton.start_reg, 4);
+       if (crb_sc->pluton.bst == 0)
+               return;
 
-       if (timeout > 0) {
-               if (!tpm_wait_for_u32(sc, crb_sc->pluton.reply_reg, ~0U, 1,
-                   timeout))
-                       return (false);
+       bus_space_unmap(crb_sc->pluton.bst, crb_sc->pluton.start_bsh,
+           sizeof(uint32_t));
+       bus_space_unmap(crb_sc->pluton.bst, crb_sc->pluton.reply_bsh,
+           sizeof(uint32_t));
+}
+
+static bool
+pluton_wait_reply(struct tpmcrb_sc *crb_sc, int32_t timeout)
+{
+       for (;;) {
+               /*
+                * Always read at least once, and try one more time after we hit
+                * the timeout.
+                */
+               if (bus_space_read_4(crb_sc->pluton.bst,
+                   crb_sc->pluton.reply_bsh, 0) == 1)
+                       return (true);
+               else if (timeout <= 0)
+                       break;
+
+               pause("TPM in polling mode", 1);
+               timeout -= tick;
        }
 
-       return (true);
+       return (false);
+}
+
+static bool
+pluton_doorbell(struct tpmcrb_sc *crb_sc, int timeout)
+{
+       bus_space_write_4(crb_sc->pluton.bst, crb_sc->pluton.start_bsh, 0, 1);
+       bus_space_barrier(crb_sc->pluton.bst, crb_sc->pluton.start_bsh, 0,
+           sizeof(uint32_t), BUS_SPACE_BARRIER_WRITE);
+
+       /*
+        * We assume at timeout == 0 that they're not really interested in a
+        * reply immediately.  Otherwise, we poll for a reply at least once,
+        * even with low timeouts.
+        */
+       if (timeout <= 0)
+               return (true);
+       return (pluton_wait_reply(crb_sc, timeout));
 }
 
 /* ACPI Driver */

Reply via email to