> Date: Wed, 3 Aug 2016 09:21:33 -0500 > From: joshua stein <j...@openbsd.org> > > Newer machines with TPM chips can have trouble suspending or > resuming, because the TPM chip is not instructed to save its state > properly before suspend. On ThinkPads this can prevent suspend from > working, and on my Chromebook Pixel it causes the machine to reboot > upon resume. > > This is a minimal TPM 1.2 driver with just enough functionality to > talk to the TPM chip and tell it to save state before a system > suspend, so that the BIOS restores it properly upon resume. > > Most of this code comes from the driver written by mickey and > hshoexer that landed in FreeBSD long ago, with lots of cleanup and > extraneous functionality ripped out. > > pirofti@ will be adding TPM 2.0 support later with some further > pruning.
Should we really add this as an isa(4) driver? That always brings probing risks. Isn't there an ACPI-based mechanism to discover the address of the TPM? > Index: sys/dev/isa/tpm.c > =================================================================== > RCS file: sys/dev/isa/tpm.c > diff -N sys/dev/isa/tpm.c > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ sys/dev/isa/tpm.c 3 Aug 2016 14:13:17 -0000 > @@ -0,0 +1,571 @@ > +/* $OpenBSD$ */ > + > +/* > + * Minimal interface to Trusted Platform Module chips implementing the > + * TPM Interface Spec 1.2, just enough to tell the TPM to save state before > + * a system suspend. > + * > + * Copyright (c) 2008, 2009 Michael Shalayeff > + * Copyright (c) 2009, 2010 Hans-Joerg Hoexer > + * Copyright (c) 2016 joshua stein <j...@openbsd.org> > + * All rights reserved. > + * > + * Permission to use, copy, modify, and distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN > + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT > + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include <sys/param.h> > +#include <sys/systm.h> > +#include <sys/device.h> > +#include <sys/malloc.h> > + > +#include <machine/bus.h> > +#include <machine/apmvar.h> > + > +#include <dev/isa/isareg.h> > +#include <dev/isa/isavar.h> > + > +/* #define TPM_DEBUG */ > + > +#ifdef TPM_DEBUG > +#define DPRINTF(x) printf x > +#else > +#define DPRINTF(x) > +#endif > + > +#define TPM_BUFSIZ 1024 > +#define TPM_HDRSIZE 10 > +#define TPM_PARAM_SIZE 0x0001 > + > +#define TPM_ACCESS 0x0000 /* access register */ > +#define TPM_ACCESS_ESTABLISHMENT 0x01 /* establishment */ > +#define TPM_ACCESS_REQUEST_USE 0x02 /* request using > locality */ > +#define TPM_ACCESS_REQUEST_PENDING 0x04 /* pending request */ > +#define TPM_ACCESS_SEIZE 0x08 /* request locality seize */ > +#define TPM_ACCESS_SEIZED 0x10 /* locality has been seized */ > +#define TPM_ACCESS_ACTIVE_LOCALITY 0x20 /* locality is active */ > +#define TPM_ACCESS_VALID 0x80 /* bits are valid */ > +#define TPM_ACCESS_BITS \ > + "\020\01EST\02REQ\03PEND\04SEIZE\05SEIZED\06ACT\010VALID" > + > +#define TPM_INTERRUPT_ENABLE 0x0008 > +#define TPM_GLOBAL_INT_ENABLE 0x80000000 /* enable ints */ > +#define TPM_CMD_READY_INT 0x00000080 /* cmd ready enable */ > +#define TPM_INT_EDGE_FALLING 0x00000018 > +#define TPM_INT_EDGE_RISING 0x00000010 > +#define TPM_INT_LEVEL_LOW 0x00000008 > +#define TPM_INT_LEVEL_HIGH 0x00000000 > +#define TPM_LOCALITY_CHANGE_INT 0x00000004 /* locality change > enable */ > +#define TPM_STS_VALID_INT 0x00000002 /* int on TPM_STS_VALID is > set */ > +#define TPM_DATA_AVAIL_INT 0x00000001 /* int on TPM_STS_DATA_AVAIL > is set */ > +#define TPM_INTERRUPT_ENABLE_BITS \ > + "\020\040ENA\010RDY\03LOCH\02STSV\01DRDY" > + > +#define TPM_INT_VECTOR 0x000c /* 8 bit reg for 4 bit > irq vector */ > +#define TPM_INT_STATUS 0x0010 /* bits are & 0x87 from > TPM_INTERRUPT_ENABLE */ > + > +#define TPM_INTF_CAPABILITIES 0x0014 /* capability register > */ > +#define TPM_INTF_BURST_COUNT_STATIC 0x0100 /* TPM_STS_BMASK static */ > +#define TPM_INTF_CMD_READY_INT 0x0080 /* int on ready > supported */ > +#define TPM_INTF_INT_EDGE_FALLING 0x0040 /* falling edge ints supported > */ > +#define TPM_INTF_INT_EDGE_RISING 0x0020 /* rising edge ints supported */ > +#define TPM_INTF_INT_LEVEL_LOW 0x0010 /* level-low ints > supported */ > +#define TPM_INTF_INT_LEVEL_HIGH 0x0008 /* level-high ints > supported */ > +#define TPM_INTF_LOCALITY_CHANGE_INT 0x0004 /* locality-change int (mb 1) */ > +#define TPM_INTF_STS_VALID_INT 0x0002 /* TPM_STS_VALID int > supported */ > +#define TPM_INTF_DATA_AVAIL_INT 0x0001 /* TPM_STS_DATA_AVAIL > int supported (mb 1) */ > +#define TPM_CAPSREQ \ > + > (TPM_INTF_DATA_AVAIL_INT|TPM_INTF_LOCALITY_CHANGE_INT|TPM_INTF_INT_LEVEL_LOW) > +#define TPM_CAPBITS \ > + > "\020\01IDRDY\02ISTSV\03ILOCH\04IHIGH\05ILOW\06IEDGE\07IFALL\010IRDY\011BCST" > + > +#define TPM_STS 0x0018 /* status register */ > +#define TPM_STS_MASK 0x000000ff /* status bits */ > +#define TPM_STS_BMASK 0x00ffff00 /* ro io burst size > */ > +#define TPM_STS_VALID 0x00000080 /* ro other bits are > valid */ > +#define TPM_STS_CMD_READY 0x00000040 /* rw chip/signal ready */ > +#define TPM_STS_GO 0x00000020 /* wo start the command */ > +#define TPM_STS_DATA_AVAIL 0x00000010 /* ro data available */ > +#define TPM_STS_DATA_EXPECT 0x00000008 /* ro more data to be > written */ > +#define TPM_STS_RESP_RETRY 0x00000002 /* wo resend the response */ > +#define TPM_STS_BITS "\020\010VALID\07RDY\06GO\05DRDY\04EXPECT\02RETRY" > + > +#define TPM_DATA 0x0024 > +#define TPM_ID 0x0f00 > +#define TPM_REV 0x0f04 > +#define TPM_SIZE 0x5000 /* five pages of the above */ > + > +#define TPM_ACCESS_TMO 2000 /* 2sec */ > +#define TPM_READY_TMO 2000 /* 2sec */ > +#define TPM_READ_TMO 120000 /* 2 minutes */ > +#define TPM_BURST_TMO 2000 /* 2sec */ > + > +struct tpm_softc { > + struct device sc_dev; > + > + bus_space_tag_t sc_bt; > + bus_space_handle_t sc_bh; > + > + uint32_t sc_devid; > + uint32_t sc_rev; > + > + int sc_enabled; > +}; > + > +const struct { > + uint32_t devid; > + char name[32]; > +} tpm_devs[] = { > + { 0x000615d1, "Infineon SLD9630 1.1" }, > + { 0x000b15d1, "Infineon SLB9635 1.2" }, > + { 0x100214e4, "Broadcom BCM0102" }, > + { 0x00fe1050, "WEC WPCT200" }, > + { 0x687119fa, "SNS SSX35" }, > + { 0x2e4d5453, "STM ST19WP18" }, > + { 0x32021114, "Atmel 97SC3203" }, > + { 0x10408086, "Intel INTC0102" }, > + { 0, "" }, > +}; > + > +int tpm_match(struct device *, void *, void *); > +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_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 *); > +uint8_t tpm_status(struct tpm_softc *); > +int tpm_tmotohz(int); > + > +struct cfattach tpm_ca = { > + sizeof(struct tpm_softc), > + tpm_match, > + tpm_attach, > + NULL, > + tpm_activate > +}; > + > +struct cfdriver tpm_cd = { > + NULL, "tpm", DV_DULL > +}; > + > +int > +tpm_match(struct device *parent, void *match, void *aux) > +{ > + struct isa_attach_args *ia = aux; > + bus_space_handle_t ioh; > + int ret; > + > + if (bus_space_map(ia->ia_memt, ia->ia_maddr, TPM_SIZE, 0, &ioh)) { > + DPRINTF(("tpm: map of 0x%x/0x%x failed\n", ia->ia_maddr, > + TPM_SIZE)); > + return (0); > + } > + > + ia->ia_iosize = 0; > + ia->ia_msize = TPM_SIZE; > + > + ret = tpm_probe(ia->ia_memt, ioh); > + bus_space_unmap(ia->ia_memt, ioh, TPM_SIZE); > + return ret; > +} > + > +void > +tpm_attach(struct device *parent, struct device *self, void *aux) > +{ > + struct tpm_softc *sc = (struct tpm_softc *)self; > + struct isa_attach_args *ia = aux; > + > + sc->sc_enabled = 0; > + sc->sc_bt = ia->ia_memt; > + > + if (bus_space_map(sc->sc_bt, ia->ia_maddr, TPM_SIZE, 0, &sc->sc_bh)) { > + printf(": failed mapping at 0x%x\n", ia->ia_maddr); > + return; > + } > + > + if (!tpm_probe(sc->sc_bt, sc->sc_bh)) { > + printf(": probe failed\n"); > + return; > + } > + > + if (tpm_init(sc) != 0) { > + printf(", init failed\n"); > + return; > + } > + > + sc->sc_enabled = 1; > +} > + > +int > +tpm_activate(struct device *self, int act) > +{ > + struct tpm_softc *sc = (struct tpm_softc *)self; > + > + switch (act) { > + case DVACT_SUSPEND: > + if (!sc->sc_enabled) { > + DPRINTF(("%s: suspend, but not enabled\n", > + sc->sc_dev.dv_xname)); > + return 0; > + } > + tpm_suspend(sc); > + break; > + > + case DVACT_WAKEUP: > + if (!sc->sc_enabled) { > + DPRINTF(("%s: wakeup, but not enabled\n", > + sc->sc_dev.dv_xname)); > + return 0; > + } > + tpm_resume(sc); > + break; > + } > + > + return 0; > +} > + > +int > +tpm_suspend(struct tpm_softc *sc) > +{ > + uint8_t command[] = { > + 0, 0xc1, /* TPM_TAG_RQU_COMMAND */ > + 0, 0, 0, 10, /* Length in bytes */ > + 0, 0, 0, 0x98 /* TPM_ORD_SaveStates */ > + }; > + > + DPRINTF(("%s: saving state preparing for suspend\n", > + sc->sc_dev.dv_xname)); > + > + /* > + * Tell the chip to save its state so the BIOS can then restore it upon > + * resume. > + */ > + tpm_write(sc, &command, sizeof(command)); > + tpm_read(sc, &command, sizeof(command), NULL, TPM_HDRSIZE); > + > + return 0; > +} > + > +int > +tpm_resume(struct tpm_softc *sc) > +{ > + DPRINTF(("%s: resume\n", sc->sc_dev.dv_xname)); > + > + /* > + * TODO: The BIOS should have restored the chip's state for us already, > + * but we should tell the chip to do a self-test here (according to the > + * Linux driver). > + */ > + > + return 0; > +} > + > +int > +tpm_probe(bus_space_tag_t bt, bus_space_handle_t bh) > +{ > + uint32_t r; > + int tries = 10000; > + > + /* wait for chip to settle */ > + while (tries--) { > + if (bus_space_read_1(bt, bh, TPM_ACCESS) & TPM_ACCESS_VALID) > + break; > + else if (!tries) { > + printf(": timed out waiting for validity\n"); > + return 1; > + } > + > + DELAY(10); > + } > + > + r = bus_space_read_4(bt, bh, TPM_INTF_CAPABILITIES); > + if (r == 0xffffffff) { > + DPRINTF(("tpm: no capabilities\n")); > + return 0; > + } > + > + DPRINTF(("tpm: capabilities: 0x%x\n", r)); > + > + return 1; > +} > + > +int > +tpm_init(struct tpm_softc *sc) > +{ > + uint32_t r, intmask; > + int i; > + > + r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTF_CAPABILITIES); > + if ((r & TPM_CAPSREQ) != TPM_CAPSREQ || > + !(r & (TPM_INTF_INT_EDGE_RISING | TPM_INTF_INT_LEVEL_LOW))) { > + DPRINTF((": caps too low (caps=%b)\n", r, TPM_CAPBITS)); > + return 0; > + } > + > + /* 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_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | > + TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT; > + 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)) { > + printf(", requesting locality failed\n"); > + return 1; > + } > + > + sc->sc_devid = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_ID); > + sc->sc_rev = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_REV); > + > + 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\n", tpm_devs[i].name, sc->sc_rev); > + else > + printf(": device 0x%08x rev 0x%x\n", sc->sc_devid, sc->sc_rev); > + > + return 0; > +} > + > +int > +tpm_request_locality(struct tpm_softc *sc, int l) > +{ > + uint32_t r; > + int to; > + > + if (l != 0) > + return EINVAL; > + > + if ((bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) & > + (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) == > + (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) > + return 0; > + > + bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS, > + TPM_ACCESS_REQUEST_USE); > + > + to = tpm_tmotohz(TPM_ACCESS_TMO); > + > + while ((r = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) & > + (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) != > + (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY) && to--) { > + DELAY(10); > + } > + > + if ((r & (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) != > + (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) { > + DPRINTF(("%s: %s: access %b\n", sc->sc_dev.dv_xname, __func__, > + r, TPM_ACCESS_BITS)); > + return EBUSY; > + } > + > + return 0; > +} > + > +void > +tpm_release_locality(struct tpm_softc *sc) > +{ > + if ((bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) & > + (TPM_ACCESS_REQUEST_PENDING|TPM_ACCESS_VALID)) == > + (TPM_ACCESS_REQUEST_PENDING|TPM_ACCESS_VALID)) { > + DPRINTF(("%s: releasing locality\n", sc->sc_dev.dv_xname)); > + bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS, > + TPM_ACCESS_ACTIVE_LOCALITY); > + } > +} > + > +int > +tpm_getburst(struct tpm_softc *sc) > +{ > + int burst, burst2, to; > + > + to = tpm_tmotohz(TPM_BURST_TMO); > + > + burst = 0; > + while (burst == 0 && to--) { > + /* > + * Burst count has to be read from bits 8 to 23 without > + * touching any other bits, eg. the actual status bits 0 to 7. > + */ > + burst = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_STS + 1); > + DPRINTF(("%s: %s: read1(0x%x): 0x%x\n", sc->sc_dev.dv_xname, > + __func__, TPM_STS + 1, burst)); > + burst2 = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_STS + 2); > + DPRINTF(("%s: %s: read1(0x%x): 0x%x\n", sc->sc_dev.dv_xname, > + __func__, TPM_STS + 2, burst2)); > + burst |= burst2 << 8; > + if (burst) > + return burst; > + > + DELAY(10); > + } > + > + DPRINTF(("%s: getburst timed out\n", sc->sc_dev.dv_xname)); > + > + return 0; > +} > + > +uint8_t > +tpm_status(struct tpm_softc *sc) > +{ > + return bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_STS) & TPM_STS_MASK; > +} > + > +int > +tpm_tmotohz(int tmo) > +{ > + struct timeval tv; > + > + tv.tv_sec = tmo / 1000; > + tv.tv_usec = 1000 * (tmo % 1000); > + > + return tvtohz(&tv); > +} > + > +int > +tpm_waitfor(struct tpm_softc *sc, uint8_t mask, int tries) > +{ > + uint8_t status; > + > + while (((status = tpm_status(sc)) & mask) != mask) { > + if (tries == 0) { > + DPRINTF(("%s: %s: timed out, status 0x%x != 0x%x\n", > + sc->sc_dev.dv_xname, __func__, status, mask)); > + return status; > + } > + > + tries--; > + DELAY(1); > + } > + > + return 0; > +} > + > +int > +tpm_read(struct tpm_softc *sc, void *buf, int len, size_t *count, > + int flags) > +{ > + uint8_t *p = buf; > + uint8_t c; > + size_t cnt; > + int rv, n, bcnt; > + > + DPRINTF(("%s: %s %d:", sc->sc_dev.dv_xname, __func__, len)); > + > + cnt = 0; > + while (len > 0) { > + if ((rv = tpm_waitfor(sc, TPM_STS_DATA_AVAIL | TPM_STS_VALID, > + TPM_READ_TMO))) > + return rv; > + > + bcnt = tpm_getburst(sc); > + n = MIN(len, bcnt); > + > + for (; n--; len--) { > + c = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_DATA); > + DPRINTF((" %02x", c)); > + *p++ = c; > + cnt++; > + } > + > + if ((flags & TPM_PARAM_SIZE) == 0 && cnt >= 6) > + break; > + } > + > + DPRINTF(("\n")); > + > + if (count) > + *count = cnt; > + > + return 0; > +} > + > +int > +tpm_write(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) > + return rv; > + > + DPRINTF(("%s: %s %d:", sc->sc_dev.dv_xname, __func__, len)); > + for (r = 0; r < len; r++) > + DPRINTF((" %02x", (uint8_t)(*(p + r)))); > + DPRINTF(("\n")); > + > + /* read status */ > + status = tpm_status(sc); > + if ((status & TPM_STS_CMD_READY) == 0) { > + /* 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))) { > + DPRINTF(("%s: failed waiting for ready after abort " > + "(0x%x)\n", sc->sc_dev.dv_xname, rv)); > + return rv; > + } > + } > + > + while (count < len - 1) { > + for (r = tpm_getburst(sc); r > 0 && count < len - 1; r--) { > + DPRINTF(("%s: %s: write1(0x%x, 0x%x)\n", > + sc->sc_dev.dv_xname, __func__, TPM_DATA, *p)); > + 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, > + TPM_READ_TMO))) { > + DPRINTF(("%s: %s: failed waiting for next byte (%d)\n", > + sc->sc_dev.dv_xname, __func__, rv)); > + return rv; > + } > + } > + > + DPRINTF(("%s: %s: write1(0x%x, 0x%x)\n", sc->sc_dev.dv_xname, __func__, > + TPM_DATA, *p)); > + 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))) { > + DPRINTF(("%s: %s: failed after last byte (%d)\n", > + sc->sc_dev.dv_xname, __func__, rv)); > + return rv; > + } > + > + if ((status = tpm_status(sc)) & TPM_STS_DATA_EXPECT) { > + DPRINTF(("%s: %s: final status still expecting data: %b\n", > + sc->sc_dev.dv_xname, __func__, status, TPM_STS_BITS)); > + return status; > + } > + > + DPRINTF(("%s: final status after write: %b\n", sc->sc_dev.dv_xname, > + status, TPM_STS_BITS)); > + > + /* XXX: are we ever sending non-command data? */ > + bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS, TPM_STS_GO); > + > + return 0; > +} > Index: sys/dev/isa/files.isa > =================================================================== > RCS file: /cvs/src/sys/dev/isa/files.isa,v > retrieving revision 1.119 > diff -u -p -u -p -r1.119 files.isa > --- sys/dev/isa/files.isa 30 Sep 2015 12:15:12 -0000 1.119 > +++ sys/dev/isa/files.isa 3 Aug 2016 14:13:17 -0000 > @@ -380,3 +380,8 @@ file dev/isa/isagpio.c isagpio > #file dev/isa/pcmcia_pcic.c pcic | pcicmaster > > #file dev/isa/pcmcia_isa.c pcmcia > + > +# Trusted Platform Module > +device tpm > +attach tpm at isa > +file dev/isa/tpm.c tpm > Index: sys/arch/amd64/conf/GENERIC > =================================================================== > RCS file: /cvs/src/sys/arch/amd64/conf/GENERIC,v > retrieving revision 1.426 > diff -u -p -u -p -r1.426 GENERIC > --- sys/arch/amd64/conf/GENERIC 30 Jul 2016 16:25:04 -0000 1.426 > +++ sys/arch/amd64/conf/GENERIC 3 Aug 2016 14:13:17 -0000 > @@ -120,6 +120,8 @@ uguru0 at isa? disable port 0xe0 # ABIT > aps0 at isa? port 0x1600 # ThinkPad Active Protection System > asmc0 at isa? port 0x300 # Apple SMC > > +tpm0 at isa? iomem 0xfed40000 # Trusted Platform Module > + > piixpm* at pci? # Intel PIIX PM > iic* at piixpm? > ichiic* at pci? # Intel ICH SMBus controller > >