xref: /onnv-gate/usr/src/uts/common/io/tpm/tpm.c (revision 9382:8b998e70ebc6)
19126SWyllys.Ingersoll@Sun.COM /*
29126SWyllys.Ingersoll@Sun.COM  * CDDL HEADER START
39126SWyllys.Ingersoll@Sun.COM  *
49126SWyllys.Ingersoll@Sun.COM  * The contents of this file are subject to the terms of the
59126SWyllys.Ingersoll@Sun.COM  * Common Development and Distribution License (the "License").
69126SWyllys.Ingersoll@Sun.COM  * You may not use this file except in compliance with the License.
79126SWyllys.Ingersoll@Sun.COM  *
89126SWyllys.Ingersoll@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99126SWyllys.Ingersoll@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109126SWyllys.Ingersoll@Sun.COM  * See the License for the specific language governing permissions
119126SWyllys.Ingersoll@Sun.COM  * and limitations under the License.
129126SWyllys.Ingersoll@Sun.COM  *
139126SWyllys.Ingersoll@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149126SWyllys.Ingersoll@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159126SWyllys.Ingersoll@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169126SWyllys.Ingersoll@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179126SWyllys.Ingersoll@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189126SWyllys.Ingersoll@Sun.COM  *
199126SWyllys.Ingersoll@Sun.COM  * CDDL HEADER END
209126SWyllys.Ingersoll@Sun.COM  */
219126SWyllys.Ingersoll@Sun.COM 
229126SWyllys.Ingersoll@Sun.COM /*
239126SWyllys.Ingersoll@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
249126SWyllys.Ingersoll@Sun.COM  * Use is subject to license terms.
259126SWyllys.Ingersoll@Sun.COM  */
269126SWyllys.Ingersoll@Sun.COM 
279126SWyllys.Ingersoll@Sun.COM /*
289126SWyllys.Ingersoll@Sun.COM  * TPM 1.2 Driver for the TPMs that follow TIS v1.2
299126SWyllys.Ingersoll@Sun.COM  */
309126SWyllys.Ingersoll@Sun.COM 
319126SWyllys.Ingersoll@Sun.COM #include <sys/devops.h>		/* used by dev_ops */
329126SWyllys.Ingersoll@Sun.COM #include <sys/conf.h>		/* used by dev_ops,cb_ops */
339126SWyllys.Ingersoll@Sun.COM #include <sys/modctl.h>		/* for _init,_info,_fini,mod_* */
349126SWyllys.Ingersoll@Sun.COM #include <sys/ddi.h>		/* used by all entry points */
359126SWyllys.Ingersoll@Sun.COM #include <sys/sunddi.h>		/* used by all entry points */
369126SWyllys.Ingersoll@Sun.COM #include <sys/cmn_err.h>	/* used for debug outputs */
379126SWyllys.Ingersoll@Sun.COM #include <sys/types.h>		/* used by prop_op, ddi_prop_op */
389126SWyllys.Ingersoll@Sun.COM 
399126SWyllys.Ingersoll@Sun.COM #include <sys/file.h>		/* used by open, close */
409126SWyllys.Ingersoll@Sun.COM #include <sys/errno.h>		/* used by open,close,read,write */
419126SWyllys.Ingersoll@Sun.COM #include <sys/open.h>		/* used by open,close,read,write */
429126SWyllys.Ingersoll@Sun.COM #include <sys/cred.h>		/* used by open,close,read */
439126SWyllys.Ingersoll@Sun.COM #include <sys/uio.h>		/* used by read */
449126SWyllys.Ingersoll@Sun.COM #include <sys/stat.h>		/* defines S_IFCHR */
459126SWyllys.Ingersoll@Sun.COM 
469126SWyllys.Ingersoll@Sun.COM #include <sys/byteorder.h>	/* for ntohs, ntohl, htons, htonl */
479126SWyllys.Ingersoll@Sun.COM 
489126SWyllys.Ingersoll@Sun.COM #include <tss/platform.h> 	/* from SUNWtss */
499126SWyllys.Ingersoll@Sun.COM #include <tss/tpm.h> 		/* from SUNWtss */
509126SWyllys.Ingersoll@Sun.COM 
519126SWyllys.Ingersoll@Sun.COM #include "tpm_tis.h"
529126SWyllys.Ingersoll@Sun.COM #include "tpm_ddi.h"
539126SWyllys.Ingersoll@Sun.COM #include "tpm_duration.h"
549126SWyllys.Ingersoll@Sun.COM 
559126SWyllys.Ingersoll@Sun.COM #define	TPM_HEADER_SIZE 10
569126SWyllys.Ingersoll@Sun.COM typedef enum {
579126SWyllys.Ingersoll@Sun.COM 	TPM_TAG_OFFSET = 0,
589126SWyllys.Ingersoll@Sun.COM 	TPM_PARAMSIZE_OFFSET = 2,
599126SWyllys.Ingersoll@Sun.COM 	TPM_RETURN_OFFSET = 6,
609126SWyllys.Ingersoll@Sun.COM 	TPM_COMMAND_CODE_OFFSET = 6,
619126SWyllys.Ingersoll@Sun.COM } TPM_HEADER_OFFSET_T;
629126SWyllys.Ingersoll@Sun.COM 
639126SWyllys.Ingersoll@Sun.COM /*
649126SWyllys.Ingersoll@Sun.COM  * This is to address some TPMs that does not report the correct duration
659126SWyllys.Ingersoll@Sun.COM  * and timeouts.  In our experience with the production TPMs, we encountered
669126SWyllys.Ingersoll@Sun.COM  * time errors such as GetCapability command from TPM reporting the timeout
679126SWyllys.Ingersoll@Sun.COM  * and durations in milliseconds rather than microseconds.  Some other TPMs
689126SWyllys.Ingersoll@Sun.COM  * report the value 0's
699126SWyllys.Ingersoll@Sun.COM  *
709126SWyllys.Ingersoll@Sun.COM  * Short Duration is based on section 11.3.4 of TIS speciciation, that
719126SWyllys.Ingersoll@Sun.COM  * TPM_GetCapability (short duration) commands should not be longer than 750ms
729126SWyllys.Ingersoll@Sun.COM  * and that section 11.3.7 states that TPM_ContinueSelfTest (medium duration)
739126SWyllys.Ingersoll@Sun.COM  * should not be longer than 1 second.
749126SWyllys.Ingersoll@Sun.COM  */
759126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_SHORT_DURATION	750000
769126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_MEDIUM_DURATION	1000000
779126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_LONG_DURATION	300000000
789126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_TIMEOUT_A	750000
799126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_TIMEOUT_B	2000000
809126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_TIMEOUT_C	750000
819126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_TIMEOUT_D	750000
829126SWyllys.Ingersoll@Sun.COM 
839126SWyllys.Ingersoll@Sun.COM /*
849126SWyllys.Ingersoll@Sun.COM  * In order to test the 'millisecond bug', we test if DURATIONS and TIMEOUTS
859126SWyllys.Ingersoll@Sun.COM  * are unreasonably low...such as 10 milliseconds (TPM isn't that fast).
869126SWyllys.Ingersoll@Sun.COM  * and 400 milliseconds for long duration
879126SWyllys.Ingersoll@Sun.COM  */
889126SWyllys.Ingersoll@Sun.COM #define	TEN_MILLISECONDS	10000	/* 10 milliseconds */
899126SWyllys.Ingersoll@Sun.COM #define	FOUR_HUNDRED_MILLISECONDS 400000	/* 4 hundred milliseconds */
909126SWyllys.Ingersoll@Sun.COM 
919126SWyllys.Ingersoll@Sun.COM /*
929126SWyllys.Ingersoll@Sun.COM  * TPM input/output buffer offsets
939126SWyllys.Ingersoll@Sun.COM  */
949126SWyllys.Ingersoll@Sun.COM 
959126SWyllys.Ingersoll@Sun.COM typedef enum {
969126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_RESPSIZE_OFFSET = 10,
979126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_RESP_OFFSET = 14,
989126SWyllys.Ingersoll@Sun.COM } TPM_CAP_RET_OFFSET_T;
999126SWyllys.Ingersoll@Sun.COM 
1009126SWyllys.Ingersoll@Sun.COM typedef enum {
1019126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_TIMEOUT_A_OFFSET = 14,
1029126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_TIMEOUT_B_OFFSET = 18,
1039126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_TIMEOUT_C_OFFSET = 22,
1049126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_TIMEOUT_D_OFFSET = 26,
1059126SWyllys.Ingersoll@Sun.COM } TPM_CAP_TIMEOUT_OFFSET_T;
1069126SWyllys.Ingersoll@Sun.COM 
1079126SWyllys.Ingersoll@Sun.COM typedef enum {
1089126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_DUR_SHORT_OFFSET = 14,
1099126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_DUR_MEDIUM_OFFSET = 18,
1109126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_DUR_LONG_OFFSET = 22,
1119126SWyllys.Ingersoll@Sun.COM } TPM_CAP_DURATION_OFFSET_T;
1129126SWyllys.Ingersoll@Sun.COM 
1139126SWyllys.Ingersoll@Sun.COM #define	TPM_CAP_VERSION_INFO_OFFSET	14
1149126SWyllys.Ingersoll@Sun.COM #define	TPM_CAP_VERSION_INFO_SIZE	15
1159126SWyllys.Ingersoll@Sun.COM 
1169126SWyllys.Ingersoll@Sun.COM /*
1179126SWyllys.Ingersoll@Sun.COM  * Internal TPM command functions
1189126SWyllys.Ingersoll@Sun.COM  */
1199126SWyllys.Ingersoll@Sun.COM static int itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz);
1209126SWyllys.Ingersoll@Sun.COM static int tpm_get_timeouts(tpm_state_t *tpm);
1219126SWyllys.Ingersoll@Sun.COM static int tpm_get_duration(tpm_state_t *tpm);
1229126SWyllys.Ingersoll@Sun.COM static int tpm_get_version(tpm_state_t *tpm);
1239126SWyllys.Ingersoll@Sun.COM static int tpm_continue_selftest(tpm_state_t *tpm);
1249126SWyllys.Ingersoll@Sun.COM 
1259126SWyllys.Ingersoll@Sun.COM /*
1269126SWyllys.Ingersoll@Sun.COM  * Internal TIS related functions
1279126SWyllys.Ingersoll@Sun.COM  */
1289126SWyllys.Ingersoll@Sun.COM static int tpm_wait_for_stat(tpm_state_t *, uint8_t, clock_t);
1299126SWyllys.Ingersoll@Sun.COM static clock_t tpm_get_ordinal_duration(tpm_state_t *, uint8_t);
1309126SWyllys.Ingersoll@Sun.COM static int tis_check_active_locality(tpm_state_t *, char);
1319126SWyllys.Ingersoll@Sun.COM static int tis_request_locality(tpm_state_t *, char);
1329126SWyllys.Ingersoll@Sun.COM static void tis_release_locality(tpm_state_t *, char, int);
1339126SWyllys.Ingersoll@Sun.COM static int tis_init(tpm_state_t *);
1349126SWyllys.Ingersoll@Sun.COM static uint8_t tis_get_status(tpm_state_t *);
1359126SWyllys.Ingersoll@Sun.COM static int tis_send_data(tpm_state_t *, uint8_t *, size_t);
1369126SWyllys.Ingersoll@Sun.COM static int tis_recv_data(tpm_state_t *, uint8_t *, size_t);
1379126SWyllys.Ingersoll@Sun.COM 
1389126SWyllys.Ingersoll@Sun.COM /* Auxilliary */
1399126SWyllys.Ingersoll@Sun.COM static int receive_data(tpm_state_t *, uint8_t *, size_t);
1409126SWyllys.Ingersoll@Sun.COM static inline int tpm_lock(tpm_state_t *);
1419126SWyllys.Ingersoll@Sun.COM static inline void tpm_unlock(tpm_state_t *);
1429126SWyllys.Ingersoll@Sun.COM static void tpm_cleanup(dev_info_t *, tpm_state_t *);
1439126SWyllys.Ingersoll@Sun.COM 
1449126SWyllys.Ingersoll@Sun.COM /*
1459126SWyllys.Ingersoll@Sun.COM  * Sun DDI/DDK entry points
1469126SWyllys.Ingersoll@Sun.COM  */
1479126SWyllys.Ingersoll@Sun.COM 
1489126SWyllys.Ingersoll@Sun.COM /* Declaration of autoconfig functions */
1499126SWyllys.Ingersoll@Sun.COM static int tpm_attach(dev_info_t *, ddi_attach_cmd_t);
1509126SWyllys.Ingersoll@Sun.COM static int tpm_detach(dev_info_t *, ddi_detach_cmd_t);
1519126SWyllys.Ingersoll@Sun.COM static int tpm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1529126SWyllys.Ingersoll@Sun.COM static int tpm_quiesce(dev_info_t *);
1539126SWyllys.Ingersoll@Sun.COM /* End of autoconfig functions */
1549126SWyllys.Ingersoll@Sun.COM 
1559126SWyllys.Ingersoll@Sun.COM /* Declaration of driver entry point functions */
1569126SWyllys.Ingersoll@Sun.COM static int tpm_open(dev_t *, int, int, cred_t *);
1579126SWyllys.Ingersoll@Sun.COM static int tpm_close(dev_t, int, int, cred_t *);
1589126SWyllys.Ingersoll@Sun.COM static int tpm_read(dev_t, struct uio *, cred_t *);
1599126SWyllys.Ingersoll@Sun.COM static int tpm_write(dev_t, struct uio *, cred_t *);
1609126SWyllys.Ingersoll@Sun.COM /* End of driver entry point functions */
1619126SWyllys.Ingersoll@Sun.COM 
1629126SWyllys.Ingersoll@Sun.COM /* cb_ops structure */
1639126SWyllys.Ingersoll@Sun.COM static struct cb_ops tpm_cb_ops = {
1649126SWyllys.Ingersoll@Sun.COM 	tpm_open,
1659126SWyllys.Ingersoll@Sun.COM 	tpm_close,
1669126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no strategy - nodev returns ENXIO */
1679126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no print */
1689126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no dump */
1699126SWyllys.Ingersoll@Sun.COM 	tpm_read,
1709126SWyllys.Ingersoll@Sun.COM 	tpm_write,
1719126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no ioctl */
1729126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no devmap */
1739126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no mmap */
1749126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no segmap */
1759126SWyllys.Ingersoll@Sun.COM 	nochpoll,	/* returns ENXIO for non-pollable devices */
1769126SWyllys.Ingersoll@Sun.COM 	ddi_prop_op,
1779126SWyllys.Ingersoll@Sun.COM 	NULL,		/* streamtab struc */
1789126SWyllys.Ingersoll@Sun.COM 	D_MP,		/* compatibility flags */
1799126SWyllys.Ingersoll@Sun.COM 	CB_REV,		/* cb_ops revision number */
1809126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no aread */
1819126SWyllys.Ingersoll@Sun.COM 	nodev		/* no awrite */
1829126SWyllys.Ingersoll@Sun.COM };
1839126SWyllys.Ingersoll@Sun.COM 
1849126SWyllys.Ingersoll@Sun.COM /* dev_ops structure */
1859126SWyllys.Ingersoll@Sun.COM static struct dev_ops tpm_dev_ops = {
1869126SWyllys.Ingersoll@Sun.COM 	DEVO_REV,
1879126SWyllys.Ingersoll@Sun.COM 	0,		/* reference count */
1889126SWyllys.Ingersoll@Sun.COM 	tpm_getinfo,
1899126SWyllys.Ingersoll@Sun.COM 	nulldev,	/* no identify - nulldev returns 0 */
1909126SWyllys.Ingersoll@Sun.COM 	nulldev,
1919126SWyllys.Ingersoll@Sun.COM 	tpm_attach,
1929126SWyllys.Ingersoll@Sun.COM 	tpm_detach,
1939126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no reset - nodev returns ENXIO */
1949126SWyllys.Ingersoll@Sun.COM 	&tpm_cb_ops,
1959126SWyllys.Ingersoll@Sun.COM 	(struct bus_ops *)NULL,
1969126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no power */
1979126SWyllys.Ingersoll@Sun.COM 	tpm_quiesce
1989126SWyllys.Ingersoll@Sun.COM };
1999126SWyllys.Ingersoll@Sun.COM 
2009126SWyllys.Ingersoll@Sun.COM /* modldrv structure */
2019126SWyllys.Ingersoll@Sun.COM static struct modldrv modldrv = {
2029126SWyllys.Ingersoll@Sun.COM 	&mod_driverops,		/* Type: This is a driver */
2039126SWyllys.Ingersoll@Sun.COM 	"TPM 1.2 driver",	/* Name of the module. */
2049126SWyllys.Ingersoll@Sun.COM 	&tpm_dev_ops
2059126SWyllys.Ingersoll@Sun.COM };
2069126SWyllys.Ingersoll@Sun.COM 
2079126SWyllys.Ingersoll@Sun.COM /* modlinkage structure */
2089126SWyllys.Ingersoll@Sun.COM static struct modlinkage tpm_ml = {
2099126SWyllys.Ingersoll@Sun.COM 	MODREV_1,
2109126SWyllys.Ingersoll@Sun.COM 	&modldrv,
2119126SWyllys.Ingersoll@Sun.COM 	NULL
2129126SWyllys.Ingersoll@Sun.COM };
2139126SWyllys.Ingersoll@Sun.COM 
2149126SWyllys.Ingersoll@Sun.COM static void *statep = NULL;
2159126SWyllys.Ingersoll@Sun.COM 
2169126SWyllys.Ingersoll@Sun.COM /*
2179126SWyllys.Ingersoll@Sun.COM  * TPM commands to get the TPM's properties, e.g.,timeout
2189126SWyllys.Ingersoll@Sun.COM  */
2199126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
2209126SWyllys.Ingersoll@Sun.COM static int
2219126SWyllys.Ingersoll@Sun.COM tpm_quiesce(dev_info_t *dip)
2229126SWyllys.Ingersoll@Sun.COM {
2239126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
2249126SWyllys.Ingersoll@Sun.COM }
2259126SWyllys.Ingersoll@Sun.COM 
2269126SWyllys.Ingersoll@Sun.COM static uint32_t
2279126SWyllys.Ingersoll@Sun.COM load32(uchar_t *ptr, uint32_t offset)
2289126SWyllys.Ingersoll@Sun.COM {
2299126SWyllys.Ingersoll@Sun.COM 	uint32_t val;
2309126SWyllys.Ingersoll@Sun.COM 	bcopy(ptr + offset, &val, sizeof (uint32_t));
2319126SWyllys.Ingersoll@Sun.COM 
2329126SWyllys.Ingersoll@Sun.COM 	return (ntohl(val));
2339126SWyllys.Ingersoll@Sun.COM }
2349126SWyllys.Ingersoll@Sun.COM 
2359126SWyllys.Ingersoll@Sun.COM /*
2369126SWyllys.Ingersoll@Sun.COM  * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
2379126SWyllys.Ingersoll@Sun.COM  * with the subcommand TPM_CAP_PROP_TIS_TIMEOUT
2389126SWyllys.Ingersoll@Sun.COM  * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
2399126SWyllys.Ingersoll@Sun.COM  */
2409126SWyllys.Ingersoll@Sun.COM static int
2419126SWyllys.Ingersoll@Sun.COM tpm_get_timeouts(tpm_state_t *tpm)
2429126SWyllys.Ingersoll@Sun.COM {
2439126SWyllys.Ingersoll@Sun.COM 	int ret;
2449126SWyllys.Ingersoll@Sun.COM 	uint32_t timeout;   /* in milliseconds */
2459126SWyllys.Ingersoll@Sun.COM 	uint32_t len;
2469126SWyllys.Ingersoll@Sun.COM 
2479126SWyllys.Ingersoll@Sun.COM 	/* The buffer size (30) needs room for 4 timeout values (uint32_t) */
2489126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[30] = {
2499126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU_COMMAND */
2509126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 22,	/* paramsize in bytes */
2519126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 101,	/* TPM_ORD_GetCapability */
2529126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 5,	/* TPM_CAP_Prop */
2539126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 4,	/* SUB_CAP size in bytes */
2549126SWyllys.Ingersoll@Sun.COM 		0, 0, 1, 21	/* TPM_CAP_PROP_TIS_TIMEOUT(0x115) */
2559126SWyllys.Ingersoll@Sun.COM 	};
2569126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_timeout";
2579126SWyllys.Ingersoll@Sun.COM 
2589126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
2599126SWyllys.Ingersoll@Sun.COM 
2609126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
2619126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
2629126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: itpm_command failed", myname);
2639126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
2649126SWyllys.Ingersoll@Sun.COM 	}
2659126SWyllys.Ingersoll@Sun.COM 
2669126SWyllys.Ingersoll@Sun.COM 	/*
2679126SWyllys.Ingersoll@Sun.COM 	 * Get the length of the returned buffer
2689126SWyllys.Ingersoll@Sun.COM 	 * Make sure that there are 4 timeout values returned
2699126SWyllys.Ingersoll@Sun.COM 	 * length of the capability response is stored in data[10-13]
2709126SWyllys.Ingersoll@Sun.COM 	 * Also the TPM is in network byte order
2719126SWyllys.Ingersoll@Sun.COM 	 */
2729126SWyllys.Ingersoll@Sun.COM 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
2739126SWyllys.Ingersoll@Sun.COM 	if (len != 4 * sizeof (uint32_t)) {
2749126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: capability response size should be %d"
2759126SWyllys.Ingersoll@Sun.COM 		    "instead it's %d",
2769126SWyllys.Ingersoll@Sun.COM 		    myname, (int)(4 * sizeof (uint32_t)), (int)len);
2779126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
2789126SWyllys.Ingersoll@Sun.COM 	}
2799126SWyllys.Ingersoll@Sun.COM 
2809126SWyllys.Ingersoll@Sun.COM 	/* Get the four timeout's: a,b,c,d (they are 4 bytes long each) */
2819126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_A_OFFSET);
2829126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
2839126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_A;
2849126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
2859126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
2869126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
2879126SWyllys.Ingersoll@Sun.COM 	}
2889126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_a = drv_usectohz(timeout);
2899126SWyllys.Ingersoll@Sun.COM 
2909126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_B_OFFSET);
2919126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
2929126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_B;
2939126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
2949126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
2959126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
2969126SWyllys.Ingersoll@Sun.COM 	}
2979126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_b = drv_usectohz(timeout);
2989126SWyllys.Ingersoll@Sun.COM 
2999126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_C_OFFSET);
3009126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
3019126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_C;
3029126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
3039126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
3049126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
3059126SWyllys.Ingersoll@Sun.COM 	}
3069126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_c = drv_usectohz(timeout);
3079126SWyllys.Ingersoll@Sun.COM 
3089126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_D_OFFSET);
3099126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
3109126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_D;
3119126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
3129126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
3139126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
3149126SWyllys.Ingersoll@Sun.COM 	}
3159126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_d = drv_usectohz(timeout);
3169126SWyllys.Ingersoll@Sun.COM 
3179126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
3189126SWyllys.Ingersoll@Sun.COM }
3199126SWyllys.Ingersoll@Sun.COM 
3209126SWyllys.Ingersoll@Sun.COM /*
3219126SWyllys.Ingersoll@Sun.COM  * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
3229126SWyllys.Ingersoll@Sun.COM  * with the subcommand TPM_CAP_PROP_TIS_DURATION
3239126SWyllys.Ingersoll@Sun.COM  * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
3249126SWyllys.Ingersoll@Sun.COM  */
3259126SWyllys.Ingersoll@Sun.COM static int
3269126SWyllys.Ingersoll@Sun.COM tpm_get_duration(tpm_state_t *tpm) {
3279126SWyllys.Ingersoll@Sun.COM 	int ret;
3289126SWyllys.Ingersoll@Sun.COM 	uint32_t duration;
3299126SWyllys.Ingersoll@Sun.COM 	uint32_t len;
3309126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[30] = {
3319126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU_COMMAND */
3329126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 22,	/* paramsize in bytes */
3339126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 101,	/* TPM_ORD_GetCapability */
3349126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 5,	/* TPM_CAP_Prop */
3359126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 4,	/* SUB_CAP size in bytes */
3369126SWyllys.Ingersoll@Sun.COM 		0, 0, 1, 32	/* TPM_CAP_PROP_TIS_DURATION(0x120) */
3379126SWyllys.Ingersoll@Sun.COM 	};
3389126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_duration";
3399126SWyllys.Ingersoll@Sun.COM 
3409126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
3419126SWyllys.Ingersoll@Sun.COM 
3429126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
3439126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
3449126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: itpm_command failed with ret code: 0x%x",
3459126SWyllys.Ingersoll@Sun.COM 			myname, ret);
3469126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
3479126SWyllys.Ingersoll@Sun.COM 	}
3489126SWyllys.Ingersoll@Sun.COM 
3499126SWyllys.Ingersoll@Sun.COM 	/*
3509126SWyllys.Ingersoll@Sun.COM 	 * Get the length of the returned buffer
3519126SWyllys.Ingersoll@Sun.COM 	 * Make sure that there are 3 duration values (S,M,L: in that order)
3529126SWyllys.Ingersoll@Sun.COM 	 * length of the capability response is stored in data[10-13]
3539126SWyllys.Ingersoll@Sun.COM 	 * Also the TPM is in network byte order
3549126SWyllys.Ingersoll@Sun.COM 	 */
3559126SWyllys.Ingersoll@Sun.COM 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
3569126SWyllys.Ingersoll@Sun.COM 	if (len != 3 * sizeof (uint32_t)) {
3579126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: capability response should be %d, "
3589126SWyllys.Ingersoll@Sun.COM 		    "instead, it's %d",
3599126SWyllys.Ingersoll@Sun.COM 		    myname, (int)(3 * sizeof (uint32_t)), (int)len);
3609126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
3619126SWyllys.Ingersoll@Sun.COM 	}
3629126SWyllys.Ingersoll@Sun.COM 
3639126SWyllys.Ingersoll@Sun.COM 	duration = load32(buf, TPM_CAP_DUR_SHORT_OFFSET);
3649126SWyllys.Ingersoll@Sun.COM 	if (duration == 0) {
3659126SWyllys.Ingersoll@Sun.COM 		duration = DEFAULT_SHORT_DURATION;
3669126SWyllys.Ingersoll@Sun.COM 	} else if (duration < TEN_MILLISECONDS) {
3679126SWyllys.Ingersoll@Sun.COM 		duration *= 1000;
3689126SWyllys.Ingersoll@Sun.COM 	}
3699126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_SHORT] = drv_usectohz(duration);
3709126SWyllys.Ingersoll@Sun.COM 
3719126SWyllys.Ingersoll@Sun.COM 	duration = load32(buf, TPM_CAP_DUR_MEDIUM_OFFSET);
3729126SWyllys.Ingersoll@Sun.COM 	if (duration == 0) {
3739126SWyllys.Ingersoll@Sun.COM 		duration = DEFAULT_MEDIUM_DURATION;
3749126SWyllys.Ingersoll@Sun.COM 	} else if (duration < TEN_MILLISECONDS) {
3759126SWyllys.Ingersoll@Sun.COM 		duration *= 1000;
3769126SWyllys.Ingersoll@Sun.COM 	}
3779126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_MEDIUM] = drv_usectohz(duration);
3789126SWyllys.Ingersoll@Sun.COM 
3799126SWyllys.Ingersoll@Sun.COM 	duration = load32(buf, TPM_CAP_DUR_LONG_OFFSET);
3809126SWyllys.Ingersoll@Sun.COM 	if (duration == 0) {
3819126SWyllys.Ingersoll@Sun.COM 		duration = DEFAULT_LONG_DURATION;
3829126SWyllys.Ingersoll@Sun.COM 	} else if (duration < FOUR_HUNDRED_MILLISECONDS) {
3839126SWyllys.Ingersoll@Sun.COM 		duration *= 1000;
3849126SWyllys.Ingersoll@Sun.COM 	}
3859126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_LONG] = drv_usectohz(duration);
3869126SWyllys.Ingersoll@Sun.COM 
3879126SWyllys.Ingersoll@Sun.COM 	/* Just make the undefined duration be the same as the LONG */
3889126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_UNDEFINED] = tpm->duration[TPM_LONG];
3899126SWyllys.Ingersoll@Sun.COM 
3909126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
3919126SWyllys.Ingersoll@Sun.COM }
3929126SWyllys.Ingersoll@Sun.COM 
3939126SWyllys.Ingersoll@Sun.COM /*
3949126SWyllys.Ingersoll@Sun.COM  * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
3959126SWyllys.Ingersoll@Sun.COM  * with the subcommand TPM_CAP_PROP_TIS_DURATION
3969126SWyllys.Ingersoll@Sun.COM  * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
3979126SWyllys.Ingersoll@Sun.COM  */
3989126SWyllys.Ingersoll@Sun.COM static int
3999126SWyllys.Ingersoll@Sun.COM tpm_get_version(tpm_state_t *tpm) {
4009126SWyllys.Ingersoll@Sun.COM 	int ret;
4019126SWyllys.Ingersoll@Sun.COM 	uint32_t len;
4029126SWyllys.Ingersoll@Sun.COM 	char vendorId[5];
4039126SWyllys.Ingersoll@Sun.COM 	/* If this buf is too small, the "vendor specific" data won't fit */
4049126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[64] = {
4059126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU_COMMAND */
4069126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 18,	/* paramsize in bytes */
4079126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 101,	/* TPM_ORD_GetCapability */
4089126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 0x1A,	/* TPM_CAP_VERSION_VAL */
4099126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 0,	/* SUB_CAP size in bytes */
4109126SWyllys.Ingersoll@Sun.COM 	};
4119126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_version";
4129126SWyllys.Ingersoll@Sun.COM 
4139126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
4149126SWyllys.Ingersoll@Sun.COM 
4159126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
4169126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
4179126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: itpm_command failed with ret code: 0x%x",
4189126SWyllys.Ingersoll@Sun.COM 			myname, ret);
4199126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
4209126SWyllys.Ingersoll@Sun.COM 	}
4219126SWyllys.Ingersoll@Sun.COM 
4229126SWyllys.Ingersoll@Sun.COM 	/*
4239126SWyllys.Ingersoll@Sun.COM 	 * Get the length of the returned buffer.
4249126SWyllys.Ingersoll@Sun.COM 	 */
4259126SWyllys.Ingersoll@Sun.COM 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
4269126SWyllys.Ingersoll@Sun.COM 	if (len < TPM_CAP_VERSION_INFO_SIZE) {
4279126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: capability response should be greater"
4289126SWyllys.Ingersoll@Sun.COM 		    " than %d, instead, it's %d",
4299126SWyllys.Ingersoll@Sun.COM 		    myname,
4309126SWyllys.Ingersoll@Sun.COM 		    TPM_CAP_VERSION_INFO_SIZE,
4319126SWyllys.Ingersoll@Sun.COM 		    len);
4329126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
4339126SWyllys.Ingersoll@Sun.COM 	}
4349126SWyllys.Ingersoll@Sun.COM 
4359126SWyllys.Ingersoll@Sun.COM 	bcopy(buf + TPM_CAP_VERSION_INFO_OFFSET, &tpm->vers_info,
4369126SWyllys.Ingersoll@Sun.COM 	    TPM_CAP_VERSION_INFO_SIZE);
4379126SWyllys.Ingersoll@Sun.COM 
4389126SWyllys.Ingersoll@Sun.COM 	bcopy(tpm->vers_info.tpmVendorID, vendorId,
4399126SWyllys.Ingersoll@Sun.COM 	    sizeof (tpm->vers_info.tpmVendorID));
4409126SWyllys.Ingersoll@Sun.COM 	vendorId[4] = '\0';
4419126SWyllys.Ingersoll@Sun.COM 
4429126SWyllys.Ingersoll@Sun.COM 	cmn_err(CE_NOTE, "!TPM found: Ver %d.%d, Rev %d.%d, "
4439126SWyllys.Ingersoll@Sun.COM 	    "SpecLevel %d, errataRev %d, VendorId '%s'",
4449126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.major,	/* Version */
4459126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.minor,
4469126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.revMajor,	/* Revision */
4479126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.revMinor,
4489126SWyllys.Ingersoll@Sun.COM 	    (int)ntohs(tpm->vers_info.specLevel),
4499126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.errataRev,
4509126SWyllys.Ingersoll@Sun.COM 	    vendorId);
4519126SWyllys.Ingersoll@Sun.COM 
4529126SWyllys.Ingersoll@Sun.COM 	/*
4539126SWyllys.Ingersoll@Sun.COM 	 * This driver only supports TPM Version 1.2
4549126SWyllys.Ingersoll@Sun.COM 	 */
4559126SWyllys.Ingersoll@Sun.COM 	if (tpm->vers_info.version.major != 1 &&
4569126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.minor != 2) {
4579126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: Unsupported TPM version (%d.%d)",
4589126SWyllys.Ingersoll@Sun.COM 		    myname,
4599126SWyllys.Ingersoll@Sun.COM 		    tpm->vers_info.version.major,		/* Version */
4609126SWyllys.Ingersoll@Sun.COM 		    tpm->vers_info.version.minor);
4619126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
4629126SWyllys.Ingersoll@Sun.COM 	}
4639126SWyllys.Ingersoll@Sun.COM 
4649126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
4659126SWyllys.Ingersoll@Sun.COM }
4669126SWyllys.Ingersoll@Sun.COM 
4679126SWyllys.Ingersoll@Sun.COM /*
4689126SWyllys.Ingersoll@Sun.COM  * To prevent the TPM from complaining that certain functions are not tested
4699126SWyllys.Ingersoll@Sun.COM  * we run this command when the driver attaches.
4709126SWyllys.Ingersoll@Sun.COM  * For details see Section 4.2 of TPM Main Part 3 Command Specification
4719126SWyllys.Ingersoll@Sun.COM  */
4729126SWyllys.Ingersoll@Sun.COM static int
4739126SWyllys.Ingersoll@Sun.COM tpm_continue_selftest(tpm_state_t *tpm) {
4749126SWyllys.Ingersoll@Sun.COM 	int ret;
4759126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[10] = {
4769126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU COMMAND */
4779126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 10,	/* paramsize in bytes */
4789126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 83	/* TPM_ORD_ContinueSelfTest */
4799126SWyllys.Ingersoll@Sun.COM 	};
4809126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_continue_selftest";
4819126SWyllys.Ingersoll@Sun.COM 
4829126SWyllys.Ingersoll@Sun.COM 	/* Need a longer timeout */
4839126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
4849126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
4859126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: itpm_command failed", myname);
4869126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
4879126SWyllys.Ingersoll@Sun.COM 	}
4889126SWyllys.Ingersoll@Sun.COM 
4899126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
4909126SWyllys.Ingersoll@Sun.COM }
4919126SWyllys.Ingersoll@Sun.COM /*
4929126SWyllys.Ingersoll@Sun.COM  * Auxilary Functions
4939126SWyllys.Ingersoll@Sun.COM  */
4949126SWyllys.Ingersoll@Sun.COM 
4959126SWyllys.Ingersoll@Sun.COM /*
4969126SWyllys.Ingersoll@Sun.COM  * Find out how long we should wait for the TPM command to complete a command
4979126SWyllys.Ingersoll@Sun.COM  */
4989126SWyllys.Ingersoll@Sun.COM static clock_t
4999126SWyllys.Ingersoll@Sun.COM tpm_get_ordinal_duration(tpm_state_t *tpm, uint8_t ordinal)
5009126SWyllys.Ingersoll@Sun.COM {
5019126SWyllys.Ingersoll@Sun.COM 	uint8_t index;
5029126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_ordinal_duration";
5039126SWyllys.Ingersoll@Sun.COM 
5049126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
5059126SWyllys.Ingersoll@Sun.COM 
5069126SWyllys.Ingersoll@Sun.COM 	/* Default and failure case for IFX */
5079126SWyllys.Ingersoll@Sun.COM 	/* Is it a TSC_ORDINAL? */
5089126SWyllys.Ingersoll@Sun.COM 	if (ordinal & TSC_ORDINAL_MASK) {
5099126SWyllys.Ingersoll@Sun.COM 		if (ordinal > TSC_ORDINAL_MAX) {
5109126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN,
5119126SWyllys.Ingersoll@Sun.COM 			    "%s: tsc ordinal: %d exceeds MAX: %d",
5129126SWyllys.Ingersoll@Sun.COM 			    myname, ordinal, TSC_ORDINAL_MAX);
5139126SWyllys.Ingersoll@Sun.COM 			return (0);
5149126SWyllys.Ingersoll@Sun.COM 		}
5159126SWyllys.Ingersoll@Sun.COM 		index = tsc_ords_duration[ordinal];
5169126SWyllys.Ingersoll@Sun.COM 	} else {
5179126SWyllys.Ingersoll@Sun.COM 		if (ordinal > TPM_ORDINAL_MAX) {
5189126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN,
5199126SWyllys.Ingersoll@Sun.COM 			    "%s: ordinal %d exceeds MAX: %d",
5209126SWyllys.Ingersoll@Sun.COM 			    myname, ordinal, TPM_ORDINAL_MAX);
5219126SWyllys.Ingersoll@Sun.COM 			return (0);
5229126SWyllys.Ingersoll@Sun.COM 		}
5239126SWyllys.Ingersoll@Sun.COM 		index = tpm_ords_duration[ordinal];
5249126SWyllys.Ingersoll@Sun.COM 	}
5259126SWyllys.Ingersoll@Sun.COM 
5269126SWyllys.Ingersoll@Sun.COM 	if (index > TPM_DURATION_MAX_IDX) {
5279126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: FATAL:index '%d' is out of bound",
5289126SWyllys.Ingersoll@Sun.COM 		    myname, index);
5299126SWyllys.Ingersoll@Sun.COM 		return (0);
5309126SWyllys.Ingersoll@Sun.COM 	}
5319126SWyllys.Ingersoll@Sun.COM 	return (tpm->duration[index]);
5329126SWyllys.Ingersoll@Sun.COM }
5339126SWyllys.Ingersoll@Sun.COM 
5349126SWyllys.Ingersoll@Sun.COM /*
5359126SWyllys.Ingersoll@Sun.COM  * Internal TPM Transmit Function:
5369126SWyllys.Ingersoll@Sun.COM  * Calls implementation specific sendto and receive
5379126SWyllys.Ingersoll@Sun.COM  * The code assumes that the buffer is in network byte order
5389126SWyllys.Ingersoll@Sun.COM  */
5399126SWyllys.Ingersoll@Sun.COM static int
5409126SWyllys.Ingersoll@Sun.COM itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz)
5419126SWyllys.Ingersoll@Sun.COM {
5429126SWyllys.Ingersoll@Sun.COM 	int ret;
5439126SWyllys.Ingersoll@Sun.COM 	uint32_t count;
5449126SWyllys.Ingersoll@Sun.COM 	char *myname = "itpm_command";
5459126SWyllys.Ingersoll@Sun.COM 
5469126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
5479126SWyllys.Ingersoll@Sun.COM 
5489126SWyllys.Ingersoll@Sun.COM 	/* The byte order is network byte order so convert it */
5499126SWyllys.Ingersoll@Sun.COM 	count = load32(buf, TPM_PARAMSIZE_OFFSET);
5509126SWyllys.Ingersoll@Sun.COM 
5519126SWyllys.Ingersoll@Sun.COM 	if (count == 0) {
5529126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: count=0, no data? %d", myname,
5539126SWyllys.Ingersoll@Sun.COM 		    (int)bufsiz);
5549126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
5559126SWyllys.Ingersoll@Sun.COM 	}
5569126SWyllys.Ingersoll@Sun.COM 	if (count > bufsiz) {
5579126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: invalid count value:count:%d > bufsiz %d",
5589126SWyllys.Ingersoll@Sun.COM 		    myname, (int)count, (int)bufsiz);
5599126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
5609126SWyllys.Ingersoll@Sun.COM 	}
5619126SWyllys.Ingersoll@Sun.COM 
5629126SWyllys.Ingersoll@Sun.COM 	/* Send the command */
5639126SWyllys.Ingersoll@Sun.COM 	ret = tis_send_data(tpm, buf, count);
5649126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
5659126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_send_data failed with error %x",
5669126SWyllys.Ingersoll@Sun.COM 		    myname, ret);
5679126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
5689126SWyllys.Ingersoll@Sun.COM 	}
5699126SWyllys.Ingersoll@Sun.COM 
5709126SWyllys.Ingersoll@Sun.COM 	/*
5719126SWyllys.Ingersoll@Sun.COM 	 * Now receive the data from the tpm
5729126SWyllys.Ingersoll@Sun.COM 	 * Should at least receive "the common" 10 bytes (TPM_HEADER_SIZE)
5739126SWyllys.Ingersoll@Sun.COM 	 */
5749126SWyllys.Ingersoll@Sun.COM 	ret = tis_recv_data(tpm, buf, bufsiz);
5759126SWyllys.Ingersoll@Sun.COM 	if (ret < TPM_HEADER_SIZE) {
5769126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_recv_data failed", myname);
5779126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
5789126SWyllys.Ingersoll@Sun.COM 	}
5799126SWyllys.Ingersoll@Sun.COM 
5809126SWyllys.Ingersoll@Sun.COM 	/* Check the return code */
5819126SWyllys.Ingersoll@Sun.COM 	ret = load32(buf, TPM_RETURN_OFFSET);
5829126SWyllys.Ingersoll@Sun.COM 	if (ret != TPM_SUCCESS) {
5839126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: command failed with ret code: %x",
5849126SWyllys.Ingersoll@Sun.COM 		    myname, ret);
5859126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
5869126SWyllys.Ingersoll@Sun.COM 	}
5879126SWyllys.Ingersoll@Sun.COM 
5889126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
5899126SWyllys.Ingersoll@Sun.COM }
5909126SWyllys.Ingersoll@Sun.COM 
5919126SWyllys.Ingersoll@Sun.COM /*
5929126SWyllys.Ingersoll@Sun.COM  * Whenever the driver wants to write to the DATA_IO register, it must need
5939126SWyllys.Ingersoll@Sun.COM  * to figure out the burstcount.  This is the amount of bytes it can write
5949126SWyllys.Ingersoll@Sun.COM  * before having to wait for long LPC bus cycle
5959126SWyllys.Ingersoll@Sun.COM  *
5969126SWyllys.Ingersoll@Sun.COM  * Returns: 0 if error, burst count if sucess
5979126SWyllys.Ingersoll@Sun.COM  */
5989126SWyllys.Ingersoll@Sun.COM static uint16_t
5999126SWyllys.Ingersoll@Sun.COM tpm_get_burstcount(tpm_state_t *tpm) {
6009126SWyllys.Ingersoll@Sun.COM 	clock_t stop;
6019126SWyllys.Ingersoll@Sun.COM 	uint16_t burstcnt;
6029126SWyllys.Ingersoll@Sun.COM 
6039126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
6049126SWyllys.Ingersoll@Sun.COM 
6059126SWyllys.Ingersoll@Sun.COM 	/*
6069126SWyllys.Ingersoll@Sun.COM 	 * Spec says timeout should be TIMEOUT_D
6079126SWyllys.Ingersoll@Sun.COM 	 * burst count is TPM_STS bits 8..23
6089126SWyllys.Ingersoll@Sun.COM 	 */
6099126SWyllys.Ingersoll@Sun.COM 	stop = ddi_get_lbolt() + tpm->timeout_d;
6109126SWyllys.Ingersoll@Sun.COM 	do {
6119126SWyllys.Ingersoll@Sun.COM 		/*
6129126SWyllys.Ingersoll@Sun.COM 		 * burstcnt is stored as a little endian value
6139126SWyllys.Ingersoll@Sun.COM 		 * 'ntohs' doesn't work since the value is not word-aligned
6149126SWyllys.Ingersoll@Sun.COM 		 */
6159126SWyllys.Ingersoll@Sun.COM 		burstcnt = ddi_get8(tpm->handle,
6169126SWyllys.Ingersoll@Sun.COM 		    (uint8_t *)(tpm->addr+
6179126SWyllys.Ingersoll@Sun.COM 		    TPM_STS_(tpm->locality)+1));
6189126SWyllys.Ingersoll@Sun.COM 		burstcnt += ddi_get8(tpm->handle,
6199126SWyllys.Ingersoll@Sun.COM 		    (uint8_t *)(tpm->addr+
6209126SWyllys.Ingersoll@Sun.COM 		    TPM_STS_(tpm->locality)+2)) << 8;
6219126SWyllys.Ingersoll@Sun.COM 
6229126SWyllys.Ingersoll@Sun.COM 		if (burstcnt)
6239126SWyllys.Ingersoll@Sun.COM 			return (burstcnt);
6249126SWyllys.Ingersoll@Sun.COM 
6259126SWyllys.Ingersoll@Sun.COM 		delay(tpm->timeout_poll);
6269126SWyllys.Ingersoll@Sun.COM 	} while (ddi_get_lbolt() < stop);
6279126SWyllys.Ingersoll@Sun.COM 
6289126SWyllys.Ingersoll@Sun.COM 	return (0);
6299126SWyllys.Ingersoll@Sun.COM }
6309126SWyllys.Ingersoll@Sun.COM 
6319126SWyllys.Ingersoll@Sun.COM /*
6329126SWyllys.Ingersoll@Sun.COM  * Writing 1 to TPM_STS_CMD_READY bit in TPM_STS will do the following:
6339126SWyllys.Ingersoll@Sun.COM  * 1. The TPM will clears IO buffers if any
6349126SWyllys.Ingersoll@Sun.COM  * 2. The TPM will enters either Idle or Ready state within TIMEOUT_B
6359126SWyllys.Ingersoll@Sun.COM  * (checked in the calling function)
6369126SWyllys.Ingersoll@Sun.COM  */
6379126SWyllys.Ingersoll@Sun.COM static void
6389126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm_state_t *tpm) {
6399126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
6409126SWyllys.Ingersoll@Sun.COM 
6419126SWyllys.Ingersoll@Sun.COM 	ddi_put8(tpm->handle,
6429126SWyllys.Ingersoll@Sun.COM 	    (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality)),
6439126SWyllys.Ingersoll@Sun.COM 	    TPM_STS_CMD_READY);
6449126SWyllys.Ingersoll@Sun.COM }
6459126SWyllys.Ingersoll@Sun.COM 
6469126SWyllys.Ingersoll@Sun.COM static int
6479126SWyllys.Ingersoll@Sun.COM receive_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
6489126SWyllys.Ingersoll@Sun.COM 	int size = 0;
6499126SWyllys.Ingersoll@Sun.COM 	int retried = 0;
6509126SWyllys.Ingersoll@Sun.COM 	uint8_t stsbits;
6519126SWyllys.Ingersoll@Sun.COM 
6529126SWyllys.Ingersoll@Sun.COM 	/* A number of consecutive bytes that can be written to TPM */
6539126SWyllys.Ingersoll@Sun.COM 	uint16_t burstcnt;
6549126SWyllys.Ingersoll@Sun.COM 
6559126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
6569126SWyllys.Ingersoll@Sun.COM retry:
6579126SWyllys.Ingersoll@Sun.COM 	while (size < bufsiz &&
6589126SWyllys.Ingersoll@Sun.COM 		(tpm_wait_for_stat(tpm,
6599126SWyllys.Ingersoll@Sun.COM 		    (TPM_STS_DATA_AVAIL|TPM_STS_VALID),
6609126SWyllys.Ingersoll@Sun.COM 		    (ddi_get_lbolt() + tpm->timeout_c)) == DDI_SUCCESS)) {
6619126SWyllys.Ingersoll@Sun.COM 		/*
6629126SWyllys.Ingersoll@Sun.COM 		 * Burstcount should be available within TIMEOUT_D
6639126SWyllys.Ingersoll@Sun.COM 		 * after STS is set to valid
6649126SWyllys.Ingersoll@Sun.COM 		 * burstcount is dynamic, so have to get it each time
6659126SWyllys.Ingersoll@Sun.COM 		 */
6669126SWyllys.Ingersoll@Sun.COM 		burstcnt = tpm_get_burstcount(tpm);
6679126SWyllys.Ingersoll@Sun.COM 		for (; burstcnt > 0 && size < bufsiz; burstcnt--) {
6689126SWyllys.Ingersoll@Sun.COM 			buf[size++] = ddi_get8(tpm->handle,
6699126SWyllys.Ingersoll@Sun.COM 			    (uint8_t *)(tpm->addr +
6709126SWyllys.Ingersoll@Sun.COM 			    TPM_DATA_FIFO_(tpm->locality)));
6719126SWyllys.Ingersoll@Sun.COM 		}
6729126SWyllys.Ingersoll@Sun.COM 	}
6739126SWyllys.Ingersoll@Sun.COM 	stsbits = tis_get_status(tpm);
6749126SWyllys.Ingersoll@Sun.COM 	/* check to see if we need to retry (just once) */
6759126SWyllys.Ingersoll@Sun.COM 	if (size < bufsiz && !(stsbits & TPM_STS_DATA_AVAIL) && retried == 0) {
6769126SWyllys.Ingersoll@Sun.COM 		/* issue responseRetry (TIS 1.2 pg 54) */
6779126SWyllys.Ingersoll@Sun.COM 		ddi_put8(tpm->handle,
6789126SWyllys.Ingersoll@Sun.COM 		    (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality)),
6799126SWyllys.Ingersoll@Sun.COM 		    TPM_STS_RESPONSE_RETRY);
6809126SWyllys.Ingersoll@Sun.COM 		/* update the retry counter so we only retry once */
6819126SWyllys.Ingersoll@Sun.COM 		retried++;
6829126SWyllys.Ingersoll@Sun.COM 		/* reset the size to 0 and reread the entire response */
6839126SWyllys.Ingersoll@Sun.COM 		size = 0;
6849126SWyllys.Ingersoll@Sun.COM 		goto retry;
6859126SWyllys.Ingersoll@Sun.COM 	}
6869126SWyllys.Ingersoll@Sun.COM 	return (size);
6879126SWyllys.Ingersoll@Sun.COM }
6889126SWyllys.Ingersoll@Sun.COM 
6899126SWyllys.Ingersoll@Sun.COM /* Receive the data from the TPM */
6909126SWyllys.Ingersoll@Sun.COM static int
6919126SWyllys.Ingersoll@Sun.COM tis_recv_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
6929126SWyllys.Ingersoll@Sun.COM 	int ret;
6939126SWyllys.Ingersoll@Sun.COM 	int size = 0;
6949126SWyllys.Ingersoll@Sun.COM 	uint32_t expected, status;
6959126SWyllys.Ingersoll@Sun.COM 	uint32_t cmdresult;
6969126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_recv_data";
6979126SWyllys.Ingersoll@Sun.COM 
6989126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
6999126SWyllys.Ingersoll@Sun.COM 
7009126SWyllys.Ingersoll@Sun.COM 	if (bufsiz < TPM_HEADER_SIZE) {
7019126SWyllys.Ingersoll@Sun.COM 		/* There should be at least tag,paramsize,return code */
7029126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: received data should contain at least "
7039126SWyllys.Ingersoll@Sun.COM 		    "the header which is %d bytes long",
7049126SWyllys.Ingersoll@Sun.COM 		    myname, TPM_HEADER_SIZE);
7059126SWyllys.Ingersoll@Sun.COM 		goto OUT;
7069126SWyllys.Ingersoll@Sun.COM 	}
7079126SWyllys.Ingersoll@Sun.COM 
7089126SWyllys.Ingersoll@Sun.COM 	/* Read tag(2 bytes), paramsize(4), and result(4) */
7099126SWyllys.Ingersoll@Sun.COM 	size = receive_data(tpm, buf, TPM_HEADER_SIZE);
7109126SWyllys.Ingersoll@Sun.COM 	if (size < TPM_HEADER_SIZE) {
7119126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: getting the TPM_HEADER failed: size=%d",
7129126SWyllys.Ingersoll@Sun.COM 		    myname, size);
7139126SWyllys.Ingersoll@Sun.COM 		goto OUT;
7149126SWyllys.Ingersoll@Sun.COM 	}
7159126SWyllys.Ingersoll@Sun.COM 
7169126SWyllys.Ingersoll@Sun.COM 	cmdresult = load32(buf, TPM_RETURN_OFFSET);
7179126SWyllys.Ingersoll@Sun.COM 
7189126SWyllys.Ingersoll@Sun.COM 	/* Get 'paramsize'(4 bytes)--it includes tag and paramsize */
7199126SWyllys.Ingersoll@Sun.COM 	expected = load32(buf, TPM_PARAMSIZE_OFFSET);
7209126SWyllys.Ingersoll@Sun.COM 	if (expected > bufsiz) {
7219126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: paramSize is bigger "
7229126SWyllys.Ingersoll@Sun.COM 		    "than the requested size: paramSize=%d bufsiz=%d result=%d",
7239126SWyllys.Ingersoll@Sun.COM 		    myname, (int)expected, (int)bufsiz, cmdresult);
7249126SWyllys.Ingersoll@Sun.COM 		goto OUT;
7259126SWyllys.Ingersoll@Sun.COM 	}
7269126SWyllys.Ingersoll@Sun.COM 
7279126SWyllys.Ingersoll@Sun.COM 	/* Read in the rest of the data from the TPM */
7289126SWyllys.Ingersoll@Sun.COM 	size += receive_data(tpm, (uint8_t *)&buf[TPM_HEADER_SIZE],
7299126SWyllys.Ingersoll@Sun.COM 	    expected - TPM_HEADER_SIZE);
7309126SWyllys.Ingersoll@Sun.COM 	if (size < expected) {
7319126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: received data length=%d "
7329126SWyllys.Ingersoll@Sun.COM 		    "is less than expected = %d", myname, size, expected);
7339126SWyllys.Ingersoll@Sun.COM 		goto OUT;
7349126SWyllys.Ingersoll@Sun.COM 	}
7359126SWyllys.Ingersoll@Sun.COM 
7369126SWyllys.Ingersoll@Sun.COM 	/* The TPM MUST set the state to stsValid within TIMEOUT_C */
7379126SWyllys.Ingersoll@Sun.COM 	ret = tpm_wait_for_stat(tpm, TPM_STS_VALID,
7389126SWyllys.Ingersoll@Sun.COM 	    ddi_get_lbolt() + tpm->timeout_c);
7399126SWyllys.Ingersoll@Sun.COM 
7409126SWyllys.Ingersoll@Sun.COM 	status = tis_get_status(tpm);
7419126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
7429126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: TPM didn't set stsValid after its I/O: "
7439126SWyllys.Ingersoll@Sun.COM 		    "status = 0x%08X", myname, status);
7449126SWyllys.Ingersoll@Sun.COM 		goto OUT;
7459126SWyllys.Ingersoll@Sun.COM 	}
7469126SWyllys.Ingersoll@Sun.COM 
7479126SWyllys.Ingersoll@Sun.COM 	/* There is still more data? */
7489126SWyllys.Ingersoll@Sun.COM 	if (status & TPM_STS_DATA_AVAIL) {
7499126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: Status TPM_STS_DATA_AVAIL set:0x%08X",
7509126SWyllys.Ingersoll@Sun.COM 		    myname, status);
7519126SWyllys.Ingersoll@Sun.COM 		goto OUT;
7529126SWyllys.Ingersoll@Sun.COM 	}
7539126SWyllys.Ingersoll@Sun.COM 
7549126SWyllys.Ingersoll@Sun.COM 	/*
7559126SWyllys.Ingersoll@Sun.COM 	 * Release the control of the TPM after we are done with it
7569126SWyllys.Ingersoll@Sun.COM 	 * it...so others can also get a chance to send data
7579126SWyllys.Ingersoll@Sun.COM 	 */
7589126SWyllys.Ingersoll@Sun.COM 	tis_release_locality(tpm, tpm->locality, 0);
7599126SWyllys.Ingersoll@Sun.COM 
7609126SWyllys.Ingersoll@Sun.COM OUT:
7619126SWyllys.Ingersoll@Sun.COM 	tpm_set_ready(tpm);
7629126SWyllys.Ingersoll@Sun.COM 	tis_release_locality(tpm, tpm->locality, 0);
7639126SWyllys.Ingersoll@Sun.COM 	return (size);
7649126SWyllys.Ingersoll@Sun.COM }
7659126SWyllys.Ingersoll@Sun.COM 
7669126SWyllys.Ingersoll@Sun.COM /*
7679126SWyllys.Ingersoll@Sun.COM  * Send the data (TPM commands) to the Data IO register
7689126SWyllys.Ingersoll@Sun.COM  */
7699126SWyllys.Ingersoll@Sun.COM static int
7709126SWyllys.Ingersoll@Sun.COM tis_send_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
7719126SWyllys.Ingersoll@Sun.COM 	int ret;
7729126SWyllys.Ingersoll@Sun.COM 	uint8_t status;
7739126SWyllys.Ingersoll@Sun.COM 	uint16_t burstcnt;
7749126SWyllys.Ingersoll@Sun.COM 	uint32_t ordinal;
7759126SWyllys.Ingersoll@Sun.COM 	size_t count = 0;
7769126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_send_data";
7779126SWyllys.Ingersoll@Sun.COM 
7789126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
7799126SWyllys.Ingersoll@Sun.COM 
7809126SWyllys.Ingersoll@Sun.COM 	if (bufsiz == 0) {
7819126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: passed in argument bufsize is zero",
7829126SWyllys.Ingersoll@Sun.COM 		    myname);
7839126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
7849126SWyllys.Ingersoll@Sun.COM 	}
7859126SWyllys.Ingersoll@Sun.COM 
7869126SWyllys.Ingersoll@Sun.COM 	/* Be in the right locality (aren't we always in locality 0?) */
7879126SWyllys.Ingersoll@Sun.COM 	if (tis_request_locality(tpm, 0) != DDI_SUCCESS) {
7889126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_request_locality didn't enter "
7899126SWyllys.Ingersoll@Sun.COM 		    "locality 0", myname);
7909126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
7919126SWyllys.Ingersoll@Sun.COM 	}
7929126SWyllys.Ingersoll@Sun.COM 
7939126SWyllys.Ingersoll@Sun.COM 	/* Put the TPM in ready state */
7949126SWyllys.Ingersoll@Sun.COM 	status = tis_get_status(tpm);
7959126SWyllys.Ingersoll@Sun.COM 
7969126SWyllys.Ingersoll@Sun.COM 	if (!(status & TPM_STS_CMD_READY)) {
7979126SWyllys.Ingersoll@Sun.COM 		tpm_set_ready(tpm);
7989126SWyllys.Ingersoll@Sun.COM 		ret = tpm_wait_for_stat(tpm,
7999126SWyllys.Ingersoll@Sun.COM 		    TPM_STS_CMD_READY,
8009126SWyllys.Ingersoll@Sun.COM 		    (ddi_get_lbolt() + tpm->timeout_b));
8019126SWyllys.Ingersoll@Sun.COM 		if (ret != DDI_SUCCESS) {
8029126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: could not put the TPM "
8039126SWyllys.Ingersoll@Sun.COM 			    "in the command ready state:"
8049126SWyllys.Ingersoll@Sun.COM 			    "tpm_wait_for_stat returned error",
8059126SWyllys.Ingersoll@Sun.COM 			    myname);
8069126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
8079126SWyllys.Ingersoll@Sun.COM 		}
8089126SWyllys.Ingersoll@Sun.COM 	}
8099126SWyllys.Ingersoll@Sun.COM 
8109126SWyllys.Ingersoll@Sun.COM 	/*
8119126SWyllys.Ingersoll@Sun.COM 	 * Now we are ready to send command
8129126SWyllys.Ingersoll@Sun.COM 	 * TPM's burstcount dictates how many bytes we can write at a time
8139126SWyllys.Ingersoll@Sun.COM 	 * Burstcount is dynamic if INTF_CAPABILITY for static burstcount is
8149126SWyllys.Ingersoll@Sun.COM 	 * not set.
8159126SWyllys.Ingersoll@Sun.COM 	 */
8169126SWyllys.Ingersoll@Sun.COM 	while (count < bufsiz - 1) {
8179126SWyllys.Ingersoll@Sun.COM 		burstcnt = tpm_get_burstcount(tpm);
8189126SWyllys.Ingersoll@Sun.COM 		if (burstcnt == 0) {
8199126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: tpm_get_burstcnt returned error",
8209126SWyllys.Ingersoll@Sun.COM 			    myname);
8219126SWyllys.Ingersoll@Sun.COM 			ret = DDI_FAILURE;
8229126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
8239126SWyllys.Ingersoll@Sun.COM 		}
8249126SWyllys.Ingersoll@Sun.COM 
8259126SWyllys.Ingersoll@Sun.COM 		for (; burstcnt > 0 && count < bufsiz - 1; burstcnt--) {
8269126SWyllys.Ingersoll@Sun.COM 			ddi_put8(tpm->handle, (uint8_t *)(tpm->addr+
8279126SWyllys.Ingersoll@Sun.COM 			    TPM_DATA_FIFO_(tpm->locality)), buf[count]);
8289126SWyllys.Ingersoll@Sun.COM 			count++;
8299126SWyllys.Ingersoll@Sun.COM 		}
8309126SWyllys.Ingersoll@Sun.COM 		/* Wait for TPM to indicate that it is ready for more data */
8319126SWyllys.Ingersoll@Sun.COM 		ret = tpm_wait_for_stat(tpm,
8329126SWyllys.Ingersoll@Sun.COM 		    (TPM_STS_VALID | TPM_STS_DATA_EXPECT),
8339126SWyllys.Ingersoll@Sun.COM 		    (ddi_get_lbolt() + tpm->timeout_c));
8349126SWyllys.Ingersoll@Sun.COM 		if (ret != DDI_SUCCESS) {
8359126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: TPM didn't enter stsvalid "
8369126SWyllys.Ingersoll@Sun.COM 			    "state after sending the data:", myname);
8379126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
8389126SWyllys.Ingersoll@Sun.COM 		}
8399126SWyllys.Ingersoll@Sun.COM 	}
8409126SWyllys.Ingersoll@Sun.COM 	/* We can't exit the loop above unless we wrote bufsiz-1 bytes */
8419126SWyllys.Ingersoll@Sun.COM 
8429126SWyllys.Ingersoll@Sun.COM 	/* Write last byte */
8439126SWyllys.Ingersoll@Sun.COM 	ddi_put8(tpm->handle, (uint8_t *)(tpm->addr +
8449126SWyllys.Ingersoll@Sun.COM 	    TPM_DATA_FIFO_(tpm->locality)), buf[count]);
8459126SWyllys.Ingersoll@Sun.COM 	count++;
8469126SWyllys.Ingersoll@Sun.COM 
8479126SWyllys.Ingersoll@Sun.COM 	/* Wait for the TPM to enter Valid State */
8489126SWyllys.Ingersoll@Sun.COM 	ret = tpm_wait_for_stat(tpm,
8499126SWyllys.Ingersoll@Sun.COM 	    TPM_STS_VALID, (ddi_get_lbolt() + tpm->timeout_c));
8509126SWyllys.Ingersoll@Sun.COM 	if (ret == DDI_FAILURE) {
8519126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm didn't enter Valid state", myname);
8529126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
8539126SWyllys.Ingersoll@Sun.COM 	}
8549126SWyllys.Ingersoll@Sun.COM 
8559126SWyllys.Ingersoll@Sun.COM 	status = tis_get_status(tpm);
8569126SWyllys.Ingersoll@Sun.COM 	/* The TPM should NOT be expecing more data at this point */
8579126SWyllys.Ingersoll@Sun.COM 	if ((status & TPM_STS_DATA_EXPECT) != 0) {
8589126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: DATA_EXPECT is set (shouldn't be) after "
8599126SWyllys.Ingersoll@Sun.COM 		    "writing the last byte: status=0x%08X", myname, status);
8609126SWyllys.Ingersoll@Sun.COM 		ret = DDI_FAILURE;
8619126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
8629126SWyllys.Ingersoll@Sun.COM 	}
8639126SWyllys.Ingersoll@Sun.COM 
8649126SWyllys.Ingersoll@Sun.COM 	/*
8659126SWyllys.Ingersoll@Sun.COM 	 * Final step: Writing TPM_STS_GO to TPM_STS
8669126SWyllys.Ingersoll@Sun.COM 	 * register will actually send the command.
8679126SWyllys.Ingersoll@Sun.COM 	 */
8689126SWyllys.Ingersoll@Sun.COM 	ddi_put8(tpm->handle, (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality)),
8699126SWyllys.Ingersoll@Sun.COM 	    TPM_STS_GO);
8709126SWyllys.Ingersoll@Sun.COM 
8719126SWyllys.Ingersoll@Sun.COM 	/* Ordinal/Command_code is located in buf[6..9] */
8729126SWyllys.Ingersoll@Sun.COM 	ordinal = load32(buf, TPM_COMMAND_CODE_OFFSET);
8739126SWyllys.Ingersoll@Sun.COM 
8749126SWyllys.Ingersoll@Sun.COM 	ret = tpm_wait_for_stat(tpm, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
8759126SWyllys.Ingersoll@Sun.COM 	    ddi_get_lbolt() + tpm_get_ordinal_duration(tpm, ordinal));
8769126SWyllys.Ingersoll@Sun.COM 	if (ret == DDI_FAILURE) {
8779126SWyllys.Ingersoll@Sun.COM 		status = tis_get_status(tpm);
8789126SWyllys.Ingersoll@Sun.COM 		if (!(status & TPM_STS_DATA_AVAIL) ||
8799126SWyllys.Ingersoll@Sun.COM 		    !(status & TPM_STS_VALID)) {
8809126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: TPM not ready or valid "
8819126SWyllys.Ingersoll@Sun.COM 			    "(ordinal = %d timeout = %ld)",
8829126SWyllys.Ingersoll@Sun.COM 			    myname, ordinal,
8839126SWyllys.Ingersoll@Sun.COM 			    tpm_get_ordinal_duration(tpm, ordinal));
8849126SWyllys.Ingersoll@Sun.COM 		} else {
8859126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: tpm_wait_for_stat "
8869126SWyllys.Ingersoll@Sun.COM 			    "(DATA_AVAIL | VALID) failed: STS = 0x%0X",
8879126SWyllys.Ingersoll@Sun.COM 			    myname, status);
8889126SWyllys.Ingersoll@Sun.COM 		}
8899126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
8909126SWyllys.Ingersoll@Sun.COM 	}
8919126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
8929126SWyllys.Ingersoll@Sun.COM 
8939126SWyllys.Ingersoll@Sun.COM FAIL:
8949126SWyllys.Ingersoll@Sun.COM 	tpm_set_ready(tpm);
8959126SWyllys.Ingersoll@Sun.COM 	tis_release_locality(tpm, tpm->locality, 0);
8969126SWyllys.Ingersoll@Sun.COM 	return (ret);
8979126SWyllys.Ingersoll@Sun.COM }
8989126SWyllys.Ingersoll@Sun.COM 
8999126SWyllys.Ingersoll@Sun.COM /*
9009126SWyllys.Ingersoll@Sun.COM  * Clear XrequestUse and Xactivelocality, where X is the current locality
9019126SWyllys.Ingersoll@Sun.COM  */
9029126SWyllys.Ingersoll@Sun.COM static void
9039126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm_state_t *tpm, char locality, int force) {
9049126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
9059126SWyllys.Ingersoll@Sun.COM 
9069126SWyllys.Ingersoll@Sun.COM 	if (force ||
9079126SWyllys.Ingersoll@Sun.COM 	    (ddi_get8(tpm->handle,
9089126SWyllys.Ingersoll@Sun.COM 		(uchar_t *)(tpm->addr+TPM_ACCESS_(locality)))
9099126SWyllys.Ingersoll@Sun.COM 	    & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID))
9109126SWyllys.Ingersoll@Sun.COM 	    == (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
9119126SWyllys.Ingersoll@Sun.COM 		/*
9129126SWyllys.Ingersoll@Sun.COM 		 * Writing 1 to active locality bit in TPM_ACCESS
9139126SWyllys.Ingersoll@Sun.COM 		 * register reliquishes the control of the locality
9149126SWyllys.Ingersoll@Sun.COM 		 */
9159126SWyllys.Ingersoll@Sun.COM 		ddi_put8(tpm->handle,
9169126SWyllys.Ingersoll@Sun.COM 		    (uint8_t *)(tpm->addr+TPM_ACCESS_(locality)),
9179126SWyllys.Ingersoll@Sun.COM 		    TPM_ACCESS_ACTIVE_LOCALITY);
9189126SWyllys.Ingersoll@Sun.COM 	}
9199126SWyllys.Ingersoll@Sun.COM }
9209126SWyllys.Ingersoll@Sun.COM 
9219126SWyllys.Ingersoll@Sun.COM /*
9229126SWyllys.Ingersoll@Sun.COM  * Checks whether the given locality is active
9239126SWyllys.Ingersoll@Sun.COM  * Use TPM_ACCESS register and the masks TPM_ACCESS_VALID,TPM_ACTIVE_LOCALITY
9249126SWyllys.Ingersoll@Sun.COM  */
9259126SWyllys.Ingersoll@Sun.COM static int
9269126SWyllys.Ingersoll@Sun.COM tis_check_active_locality(tpm_state_t *tpm, char locality) {
9279126SWyllys.Ingersoll@Sun.COM 	uint8_t access_bits;
9289126SWyllys.Ingersoll@Sun.COM 
9299126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
9309126SWyllys.Ingersoll@Sun.COM 
9319126SWyllys.Ingersoll@Sun.COM 	access_bits = ddi_get8(tpm->handle,
9329126SWyllys.Ingersoll@Sun.COM 	    (uint8_t *)(tpm->addr+TPM_ACCESS_(locality)));
9339126SWyllys.Ingersoll@Sun.COM 	access_bits &= (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID);
9349126SWyllys.Ingersoll@Sun.COM 
9359126SWyllys.Ingersoll@Sun.COM 	if (access_bits == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
9369126SWyllys.Ingersoll@Sun.COM 		return (DDI_SUCCESS);
9379126SWyllys.Ingersoll@Sun.COM 	else
9389126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
9399126SWyllys.Ingersoll@Sun.COM }
9409126SWyllys.Ingersoll@Sun.COM 
9419126SWyllys.Ingersoll@Sun.COM /* Request the TPM to be in the given locality */
9429126SWyllys.Ingersoll@Sun.COM static int
9439126SWyllys.Ingersoll@Sun.COM tis_request_locality(tpm_state_t *tpm, char locality) {
9449126SWyllys.Ingersoll@Sun.COM 	clock_t timeout;
9459126SWyllys.Ingersoll@Sun.COM 	int ret;
9469126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_request_locality";
9479126SWyllys.Ingersoll@Sun.COM 
9489126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
9499126SWyllys.Ingersoll@Sun.COM 
9509126SWyllys.Ingersoll@Sun.COM 	ret = tis_check_active_locality(tpm, locality);
9519126SWyllys.Ingersoll@Sun.COM 
9529126SWyllys.Ingersoll@Sun.COM 	if (ret == DDI_SUCCESS) {
9539126SWyllys.Ingersoll@Sun.COM 		/* Locality is already active */
9549126SWyllys.Ingersoll@Sun.COM 		tpm->locality = locality;
9559126SWyllys.Ingersoll@Sun.COM 		return (DDI_SUCCESS);
9569126SWyllys.Ingersoll@Sun.COM 	}
9579126SWyllys.Ingersoll@Sun.COM 
9589126SWyllys.Ingersoll@Sun.COM 	ddi_put8(tpm->handle, tpm->addr+TPM_ACCESS_(locality),
9599126SWyllys.Ingersoll@Sun.COM 	    TPM_ACCESS_REQUEST_USE);
9609126SWyllys.Ingersoll@Sun.COM 	timeout = ddi_get_lbolt() + tpm->timeout_a;
9619126SWyllys.Ingersoll@Sun.COM 
9629126SWyllys.Ingersoll@Sun.COM 	/* Using polling */
9639126SWyllys.Ingersoll@Sun.COM 	while (tis_check_active_locality(tpm, locality)
9649126SWyllys.Ingersoll@Sun.COM 		!= DDI_SUCCESS) {
9659126SWyllys.Ingersoll@Sun.COM 		if (ddi_get_lbolt() >= timeout) {
9669126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s (interrupt-disabled) "
9679126SWyllys.Ingersoll@Sun.COM 			    "tis_request_locality timed out",
9689126SWyllys.Ingersoll@Sun.COM 			    myname);
9699126SWyllys.Ingersoll@Sun.COM 			return (DDI_FAILURE);
9709126SWyllys.Ingersoll@Sun.COM 		}
9719126SWyllys.Ingersoll@Sun.COM 		delay(tpm->timeout_poll);
9729126SWyllys.Ingersoll@Sun.COM 	}
9739126SWyllys.Ingersoll@Sun.COM 
9749126SWyllys.Ingersoll@Sun.COM 	tpm->locality = locality;
9759126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
9769126SWyllys.Ingersoll@Sun.COM }
9779126SWyllys.Ingersoll@Sun.COM 
9789126SWyllys.Ingersoll@Sun.COM /* Read the status register */
9799126SWyllys.Ingersoll@Sun.COM static uint8_t
9809126SWyllys.Ingersoll@Sun.COM tis_get_status(tpm_state_t *tpm) {
9819126SWyllys.Ingersoll@Sun.COM 	return (ddi_get8(tpm->handle,
9829126SWyllys.Ingersoll@Sun.COM 	    (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality))));
9839126SWyllys.Ingersoll@Sun.COM }
9849126SWyllys.Ingersoll@Sun.COM 
9859126SWyllys.Ingersoll@Sun.COM static int
9869126SWyllys.Ingersoll@Sun.COM tpm_wait_for_stat(tpm_state_t *tpm, uint8_t mask, clock_t absolute_timeout) {
9879126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_wait_for_stat";
9889126SWyllys.Ingersoll@Sun.COM 
9899126SWyllys.Ingersoll@Sun.COM 	/* Using polling */
9909126SWyllys.Ingersoll@Sun.COM 	while ((tis_get_status(tpm) & mask) != mask) {
9919126SWyllys.Ingersoll@Sun.COM 		if (ddi_get_lbolt() >= absolute_timeout) {
9929126SWyllys.Ingersoll@Sun.COM 			/* Timeout reached */
9939126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: using "
9949126SWyllys.Ingersoll@Sun.COM 			    "polling:reached timeout",
9959126SWyllys.Ingersoll@Sun.COM 			    myname);
9969126SWyllys.Ingersoll@Sun.COM 			return (DDI_FAILURE);
9979126SWyllys.Ingersoll@Sun.COM 		}
9989126SWyllys.Ingersoll@Sun.COM 		delay(tpm->timeout_poll);
9999126SWyllys.Ingersoll@Sun.COM 	}
10009126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
10019126SWyllys.Ingersoll@Sun.COM }
10029126SWyllys.Ingersoll@Sun.COM 
10039126SWyllys.Ingersoll@Sun.COM /*
10049126SWyllys.Ingersoll@Sun.COM  * Initialize TPM device
10059126SWyllys.Ingersoll@Sun.COM  * 1. Find out supported interrupt capabilities
10069126SWyllys.Ingersoll@Sun.COM  * 2. Set up interrupt handler if supported (some BIOSes don't support
10079126SWyllys.Ingersoll@Sun.COM  * interrupts for TPMS, in which case we set up polling)
10089126SWyllys.Ingersoll@Sun.COM  * 3. Determine timeouts and commands duration
10099126SWyllys.Ingersoll@Sun.COM  */
10109126SWyllys.Ingersoll@Sun.COM static int
10119126SWyllys.Ingersoll@Sun.COM tis_init(tpm_state_t *tpm) {
10129126SWyllys.Ingersoll@Sun.COM 	uint32_t intf_caps;
10139126SWyllys.Ingersoll@Sun.COM 	int ret;
10149126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_init";
10159126SWyllys.Ingersoll@Sun.COM 	uintptr_t aptr = (uintptr_t)tpm->addr;
10169126SWyllys.Ingersoll@Sun.COM 
10179126SWyllys.Ingersoll@Sun.COM 	/*
10189126SWyllys.Ingersoll@Sun.COM 	 * Temporarily set up timeouts before we get the real timeouts
10199126SWyllys.Ingersoll@Sun.COM 	 * by issuing TPM_CAP commands (but to issue TPM_CAP commands,
10209126SWyllys.Ingersoll@Sun.COM 	 * you need TIMEOUTs defined...chicken and egg problem here.
10219126SWyllys.Ingersoll@Sun.COM 	 * TPM timeouts: Convert the milliseconds to clock cycles
10229126SWyllys.Ingersoll@Sun.COM 	 */
10239126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_a = drv_usectohz(TIS_TIMEOUT_A);
10249126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_b = drv_usectohz(TIS_TIMEOUT_B);
10259126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_c = drv_usectohz(TIS_TIMEOUT_C);
10269126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_d = drv_usectohz(TIS_TIMEOUT_D);
10279126SWyllys.Ingersoll@Sun.COM 	/*
10289126SWyllys.Ingersoll@Sun.COM 	 * Do the same with the duration (real duration will be filled out
10299126SWyllys.Ingersoll@Sun.COM 	 * when we call TPM_GetCapability to get the duration values from
10309126SWyllys.Ingersoll@Sun.COM 	 * the TPM itself).
10319126SWyllys.Ingersoll@Sun.COM 	 */
10329126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_SHORT] = drv_usectohz(TPM_DEFAULT_DURATION);
10339126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_MEDIUM] = drv_usectohz(TPM_DEFAULT_DURATION);
10349126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_LONG] = drv_usectohz(TPM_DEFAULT_DURATION);
10359126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_UNDEFINED] = drv_usectohz(TPM_DEFAULT_DURATION);
10369126SWyllys.Ingersoll@Sun.COM 
10379126SWyllys.Ingersoll@Sun.COM 	/* Find out supported capabilities */
10389126SWyllys.Ingersoll@Sun.COM 	intf_caps = ddi_get32(tpm->handle,
10399126SWyllys.Ingersoll@Sun.COM 	    (uint32_t *)(aptr + TPM_INTF_CAP_(0)));
10409126SWyllys.Ingersoll@Sun.COM 
10419126SWyllys.Ingersoll@Sun.COM 	/* Upper 3 bytes should always return 0 */
10429126SWyllys.Ingersoll@Sun.COM 	if (intf_caps & 0x7FFFFF00) {
10439126SWyllys.Ingersoll@Sun.COM #ifdef DEBUG
10449126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: bad intf_caps value 0x%0X",
10459126SWyllys.Ingersoll@Sun.COM 		    myname, intf_caps);
10469126SWyllys.Ingersoll@Sun.COM #endif
10479126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
10489126SWyllys.Ingersoll@Sun.COM 	}
10499126SWyllys.Ingersoll@Sun.COM 
10509126SWyllys.Ingersoll@Sun.COM 	/* These two interrupts are mandatory */
10519126SWyllys.Ingersoll@Sun.COM 	if (!(intf_caps & TPM_INTF_INT_LOCALITY_CHANGE_INT)) {
10529126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: Mandatory capability Locality Change Int "
10539126SWyllys.Ingersoll@Sun.COM 		    "not supported", myname);
10549126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
10559126SWyllys.Ingersoll@Sun.COM 	}
10569126SWyllys.Ingersoll@Sun.COM 	if (!(intf_caps & TPM_INTF_INT_DATA_AVAIL_INT)) {
10579126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: Mandatory capability Data Available Int "
10589126SWyllys.Ingersoll@Sun.COM 		    "not supported", myname);
10599126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
10609126SWyllys.Ingersoll@Sun.COM 	}
10619126SWyllys.Ingersoll@Sun.COM 
10629126SWyllys.Ingersoll@Sun.COM 	/*
10639126SWyllys.Ingersoll@Sun.COM 	 * Before we start writing anything to TPM's registers,
10649126SWyllys.Ingersoll@Sun.COM 	 * make sure we are in locality 0
10659126SWyllys.Ingersoll@Sun.COM 	 */
10669126SWyllys.Ingersoll@Sun.COM 	ret = tis_request_locality(tpm, 0);
10679126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
10689126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: Unable to request locality 0", myname);
10699126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
10709126SWyllys.Ingersoll@Sun.COM 	} /* Now we can refer to the locality as tpm->locality */
10719126SWyllys.Ingersoll@Sun.COM 
10729126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_poll = drv_usectohz(TPM_POLLING_TIMEOUT);
10739126SWyllys.Ingersoll@Sun.COM 	tpm->intr_enabled = 0;
10749126SWyllys.Ingersoll@Sun.COM 
10759126SWyllys.Ingersoll@Sun.COM 	/* Get the real timeouts from the TPM */
10769126SWyllys.Ingersoll@Sun.COM 	ret = tpm_get_timeouts(tpm);
10779126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
10789126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm_get_timeouts error", myname);
10799126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
10809126SWyllys.Ingersoll@Sun.COM 	}
10819126SWyllys.Ingersoll@Sun.COM 
10829126SWyllys.Ingersoll@Sun.COM 	ret = tpm_get_duration(tpm);
10839126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
10849126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm_get_duration error", myname);
10859126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
10869126SWyllys.Ingersoll@Sun.COM 	}
10879126SWyllys.Ingersoll@Sun.COM 
10889126SWyllys.Ingersoll@Sun.COM 	/* This gets the TPM version information */
10899126SWyllys.Ingersoll@Sun.COM 	ret = tpm_get_version(tpm);
10909126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
10919126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm_get_version error", myname);
10929126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
10939126SWyllys.Ingersoll@Sun.COM 	}
10949126SWyllys.Ingersoll@Sun.COM 
10959126SWyllys.Ingersoll@Sun.COM 	/*
10969126SWyllys.Ingersoll@Sun.COM 	 * Unless the TPM completes the test of its commands,
10979126SWyllys.Ingersoll@Sun.COM 	 * it can return an error when the untested commands are called
10989126SWyllys.Ingersoll@Sun.COM 	 */
10999126SWyllys.Ingersoll@Sun.COM 	ret = tpm_continue_selftest(tpm);
11009126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
11019126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm_continue_selftest error", myname);
11029126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
11039126SWyllys.Ingersoll@Sun.COM 	}
11049126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
11059126SWyllys.Ingersoll@Sun.COM }
11069126SWyllys.Ingersoll@Sun.COM 
11079126SWyllys.Ingersoll@Sun.COM /*
11089126SWyllys.Ingersoll@Sun.COM  * Module Entry points
11099126SWyllys.Ingersoll@Sun.COM  */
11109126SWyllys.Ingersoll@Sun.COM int
11119126SWyllys.Ingersoll@Sun.COM _init(void)
11129126SWyllys.Ingersoll@Sun.COM {
11139126SWyllys.Ingersoll@Sun.COM 	int ret;
11149126SWyllys.Ingersoll@Sun.COM 
11159126SWyllys.Ingersoll@Sun.COM 	ret = ddi_soft_state_init(&statep, sizeof (tpm_state_t), 1);
11169126SWyllys.Ingersoll@Sun.COM 	if (ret)
11179126SWyllys.Ingersoll@Sun.COM 		return (ret);
11189126SWyllys.Ingersoll@Sun.COM 
11199126SWyllys.Ingersoll@Sun.COM 	ret = mod_install(&tpm_ml);
11209126SWyllys.Ingersoll@Sun.COM 	if (ret != 0) {
11219126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "_init: mod_install returned non-zero");
11229126SWyllys.Ingersoll@Sun.COM 		ddi_soft_state_fini(&statep);
11239126SWyllys.Ingersoll@Sun.COM 		return (ret);
11249126SWyllys.Ingersoll@Sun.COM 	}
11259126SWyllys.Ingersoll@Sun.COM 
11269126SWyllys.Ingersoll@Sun.COM 	return (ret);
11279126SWyllys.Ingersoll@Sun.COM }
11289126SWyllys.Ingersoll@Sun.COM 
11299126SWyllys.Ingersoll@Sun.COM int
11309126SWyllys.Ingersoll@Sun.COM _info(struct modinfo *modinfop)
11319126SWyllys.Ingersoll@Sun.COM {
11329126SWyllys.Ingersoll@Sun.COM 	int ret;
11339126SWyllys.Ingersoll@Sun.COM 	ret = mod_info(&tpm_ml, modinfop);
11349126SWyllys.Ingersoll@Sun.COM 	if (ret == 0)
11359126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "mod_info failed: %d", ret);
11369126SWyllys.Ingersoll@Sun.COM 
11379126SWyllys.Ingersoll@Sun.COM 	return (ret);
11389126SWyllys.Ingersoll@Sun.COM }
11399126SWyllys.Ingersoll@Sun.COM 
11409126SWyllys.Ingersoll@Sun.COM int
11419126SWyllys.Ingersoll@Sun.COM _fini()
11429126SWyllys.Ingersoll@Sun.COM {
11439126SWyllys.Ingersoll@Sun.COM 	int ret;
11449126SWyllys.Ingersoll@Sun.COM 	ret = mod_remove(&tpm_ml);
11459126SWyllys.Ingersoll@Sun.COM 	if (ret != 0) {
11469126SWyllys.Ingersoll@Sun.COM 		return (ret);
11479126SWyllys.Ingersoll@Sun.COM 	}
11489126SWyllys.Ingersoll@Sun.COM 	ddi_soft_state_fini(&statep);
11499126SWyllys.Ingersoll@Sun.COM 
11509126SWyllys.Ingersoll@Sun.COM 	return (ret);
11519126SWyllys.Ingersoll@Sun.COM }
11529126SWyllys.Ingersoll@Sun.COM /* End of driver configuration functions */
11539126SWyllys.Ingersoll@Sun.COM 
11549126SWyllys.Ingersoll@Sun.COM static int
11559126SWyllys.Ingersoll@Sun.COM tpm_resume(tpm_state_t *tpm)
11569126SWyllys.Ingersoll@Sun.COM {
11579126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
11589126SWyllys.Ingersoll@Sun.COM 	if (!tpm->suspended) {
11599126SWyllys.Ingersoll@Sun.COM 		mutex_exit(&tpm->pm_mutex);
11609126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
11619126SWyllys.Ingersoll@Sun.COM 	}
11629126SWyllys.Ingersoll@Sun.COM 	tpm->suspended = 0;
11639126SWyllys.Ingersoll@Sun.COM 	cv_broadcast(&tpm->suspend_cv);
11649126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
11659126SWyllys.Ingersoll@Sun.COM 
11669126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
11679126SWyllys.Ingersoll@Sun.COM }
11689126SWyllys.Ingersoll@Sun.COM 
11699126SWyllys.Ingersoll@Sun.COM /*
11709126SWyllys.Ingersoll@Sun.COM  * Sun DDI/DDK entry points
11719126SWyllys.Ingersoll@Sun.COM  */
11729126SWyllys.Ingersoll@Sun.COM static int
11739126SWyllys.Ingersoll@Sun.COM tpm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
11749126SWyllys.Ingersoll@Sun.COM {
11759126SWyllys.Ingersoll@Sun.COM 	int ret, idx;
11769126SWyllys.Ingersoll@Sun.COM 	int instance;
11779126SWyllys.Ingersoll@Sun.COM 	int nregs;
11789126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_attach";
11799126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm = NULL;
11809126SWyllys.Ingersoll@Sun.COM 
11819126SWyllys.Ingersoll@Sun.COM 	ASSERT(dip != NULL);
11829126SWyllys.Ingersoll@Sun.COM 
11839126SWyllys.Ingersoll@Sun.COM 	instance = ddi_get_instance(dip);
11849126SWyllys.Ingersoll@Sun.COM 
11859126SWyllys.Ingersoll@Sun.COM 	/* Nothing out of ordinary here */
11869126SWyllys.Ingersoll@Sun.COM 	switch (cmd) {
11879126SWyllys.Ingersoll@Sun.COM 	case DDI_ATTACH:
11889126SWyllys.Ingersoll@Sun.COM 		ret = ddi_soft_state_zalloc(statep, instance);
11899126SWyllys.Ingersoll@Sun.COM 		if (ret != DDI_SUCCESS) {
11909126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s could not allocate tpm_state_t",
11919126SWyllys.Ingersoll@Sun.COM 			    myname);
11929126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
11939126SWyllys.Ingersoll@Sun.COM 		}
11949126SWyllys.Ingersoll@Sun.COM 		tpm = ddi_get_soft_state(statep, instance);
11959126SWyllys.Ingersoll@Sun.COM 		tpm->dip = dip;
11969126SWyllys.Ingersoll@Sun.COM 		break;
11979126SWyllys.Ingersoll@Sun.COM 	case DDI_RESUME:
11989126SWyllys.Ingersoll@Sun.COM 		tpm = ddi_get_soft_state(statep, instance);
11999126SWyllys.Ingersoll@Sun.COM 		if (tpm == NULL) {
12009126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: tpm_state_t is NULL",
12019126SWyllys.Ingersoll@Sun.COM 			    myname);
12029126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
12039126SWyllys.Ingersoll@Sun.COM 		}
12049126SWyllys.Ingersoll@Sun.COM 		return (tpm_resume(tpm));
12059126SWyllys.Ingersoll@Sun.COM 	default:
12069126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: cmd %d is not implemented", myname, cmd);
12079126SWyllys.Ingersoll@Sun.COM 		ret = DDI_FAILURE;
12089126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
12099126SWyllys.Ingersoll@Sun.COM 	}
12109126SWyllys.Ingersoll@Sun.COM 
12119126SWyllys.Ingersoll@Sun.COM 	/* Zeroize the flag, which is used to keep track of what is allocated */
12129126SWyllys.Ingersoll@Sun.COM 	tpm->flags = 0;
12139126SWyllys.Ingersoll@Sun.COM 
12149126SWyllys.Ingersoll@Sun.COM 	tpm->accattr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
12159126SWyllys.Ingersoll@Sun.COM 	tpm->accattr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
12169126SWyllys.Ingersoll@Sun.COM 	tpm->accattr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
12179126SWyllys.Ingersoll@Sun.COM 
12189126SWyllys.Ingersoll@Sun.COM 	idx = 0;
12199126SWyllys.Ingersoll@Sun.COM 	ret = ddi_dev_nregs(tpm->dip, &nregs);
12209126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS)
12219126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
12229126SWyllys.Ingersoll@Sun.COM 
12239126SWyllys.Ingersoll@Sun.COM #ifdef DEBUG
12249126SWyllys.Ingersoll@Sun.COM 	cmn_err(CE_NOTE, "num registers = %d", nregs);
12259126SWyllys.Ingersoll@Sun.COM #endif
12269126SWyllys.Ingersoll@Sun.COM 	/*
12279126SWyllys.Ingersoll@Sun.COM 	 * TPM vendors put the TPM registers in different
12289126SWyllys.Ingersoll@Sun.COM 	 * slots in their register lists.  They are not always
12299126SWyllys.Ingersoll@Sun.COM 	 * the 1st set of registers, for instance.
12309126SWyllys.Ingersoll@Sun.COM 	 * Loop until we find the set that matches the expected
12319126SWyllys.Ingersoll@Sun.COM 	 * register size (0x5000).
12329126SWyllys.Ingersoll@Sun.COM 	 */
12339126SWyllys.Ingersoll@Sun.COM 	for (idx = 0; idx < nregs; idx++) {
12349126SWyllys.Ingersoll@Sun.COM 		off_t regsize;
12359126SWyllys.Ingersoll@Sun.COM 
12369126SWyllys.Ingersoll@Sun.COM 		if ((ret = ddi_dev_regsize(tpm->dip, idx, &regsize)) !=
12379126SWyllys.Ingersoll@Sun.COM 		    DDI_SUCCESS)
12389126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
12399126SWyllys.Ingersoll@Sun.COM #ifdef DEBUG
12409126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_NOTE, "register set #%d size = 0x%0lX", idx,
12419126SWyllys.Ingersoll@Sun.COM 		    regsize);
12429126SWyllys.Ingersoll@Sun.COM #endif
12439126SWyllys.Ingersoll@Sun.COM 		/* The TIS spec says the TPM registers must be 0x5000 bytes */
12449126SWyllys.Ingersoll@Sun.COM 		if (regsize == 0x5000)
12459126SWyllys.Ingersoll@Sun.COM 			break;
12469126SWyllys.Ingersoll@Sun.COM 	}
1247*9382SWyllys.Ingersoll@Sun.COM 	if (idx == nregs)
1248*9382SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1249*9382SWyllys.Ingersoll@Sun.COM 
12509126SWyllys.Ingersoll@Sun.COM 	ret = ddi_regs_map_setup(tpm->dip, idx, (caddr_t *)&tpm->addr,
12519126SWyllys.Ingersoll@Sun.COM 	    (offset_t)0, (offset_t)0x5000,
12529126SWyllys.Ingersoll@Sun.COM 	    &tpm->accattr, &tpm->handle);
12539126SWyllys.Ingersoll@Sun.COM 
12549126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
12559126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
12569126SWyllys.Ingersoll@Sun.COM 	}
12579126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DIDREGSMAP;
12589126SWyllys.Ingersoll@Sun.COM 
12599126SWyllys.Ingersoll@Sun.COM 	/* Enable TPM device according to the TIS specification */
12609126SWyllys.Ingersoll@Sun.COM 	ret = tis_init(tpm);
12619126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
12629126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_init() failed ret: %d",
12639126SWyllys.Ingersoll@Sun.COM 		    myname, ret);
12649126SWyllys.Ingersoll@Sun.COM 
12659126SWyllys.Ingersoll@Sun.COM 		/* We need to clean up the ddi_regs_map_setup call */
12669126SWyllys.Ingersoll@Sun.COM 		ddi_regs_map_free(&tpm->handle);
12679126SWyllys.Ingersoll@Sun.COM 		tpm->handle = NULL;
12689126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~TPM_DIDREGSMAP;
12699126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
12709126SWyllys.Ingersoll@Sun.COM 	}
12719126SWyllys.Ingersoll@Sun.COM 
12729126SWyllys.Ingersoll@Sun.COM 	/* Initialize the inter-process lock */
12739126SWyllys.Ingersoll@Sun.COM 	mutex_init(&tpm->dev_lock, NULL, MUTEX_DRIVER, NULL);
12749126SWyllys.Ingersoll@Sun.COM 	mutex_init(&tpm->pm_mutex, NULL, MUTEX_DRIVER, NULL);
12759126SWyllys.Ingersoll@Sun.COM 	cv_init(&tpm->suspend_cv, NULL, CV_DRIVER, NULL);
12769126SWyllys.Ingersoll@Sun.COM 
12779126SWyllys.Ingersoll@Sun.COM 	/* Set the suspend/resume property */
12789126SWyllys.Ingersoll@Sun.COM 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
12799126SWyllys.Ingersoll@Sun.COM 	    "pm-hardware-state", "needs-suspend-resume");
12809126SWyllys.Ingersoll@Sun.COM 
12819126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
12829126SWyllys.Ingersoll@Sun.COM 	tpm->suspended = 0;
12839126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
12849126SWyllys.Ingersoll@Sun.COM 
12859126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_MUTEX;
12869126SWyllys.Ingersoll@Sun.COM 
12879126SWyllys.Ingersoll@Sun.COM 	/* Initialize the buffer and the lock associated with it */
12889126SWyllys.Ingersoll@Sun.COM 	tpm->bufsize = TPM_IO_BUF_SIZE;
12899126SWyllys.Ingersoll@Sun.COM 	tpm->iobuf = kmem_zalloc((sizeof (uint8_t))*(tpm->bufsize), KM_SLEEP);
12909126SWyllys.Ingersoll@Sun.COM 	if (tpm->iobuf == NULL) {
12919126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: failed to allocate iobuf", myname);
12929126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
12939126SWyllys.Ingersoll@Sun.COM 	}
12949126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_IO_ALLOC;
12959126SWyllys.Ingersoll@Sun.COM 
12969126SWyllys.Ingersoll@Sun.COM 	mutex_init(&tpm->iobuf_lock, NULL, MUTEX_DRIVER, NULL);
12979126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_IO_MUTEX;
12989126SWyllys.Ingersoll@Sun.COM 
12999126SWyllys.Ingersoll@Sun.COM 	cv_init(&tpm->iobuf_cv, NULL, CV_DRIVER, NULL);
13009126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_IO_CV;
13019126SWyllys.Ingersoll@Sun.COM 
13029126SWyllys.Ingersoll@Sun.COM 	/* Create minor node */
13039126SWyllys.Ingersoll@Sun.COM 	ret = ddi_create_minor_node(dip, "tpm", S_IFCHR, ddi_get_instance(dip),
13049126SWyllys.Ingersoll@Sun.COM 	    DDI_PSEUDO, 0);
13059126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
13069126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: ddi_create_minor_node failed", myname);
13079126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
13089126SWyllys.Ingersoll@Sun.COM 	}
13099126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DIDMINOR;
13109126SWyllys.Ingersoll@Sun.COM 
13119126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
13129126SWyllys.Ingersoll@Sun.COM FAIL:
13139126SWyllys.Ingersoll@Sun.COM 	if (tpm != NULL) {
13149126SWyllys.Ingersoll@Sun.COM 		tpm_cleanup(dip, tpm);
13159126SWyllys.Ingersoll@Sun.COM 		ddi_soft_state_free(statep, instance);
13169126SWyllys.Ingersoll@Sun.COM 		tpm = NULL;
13179126SWyllys.Ingersoll@Sun.COM 	}
13189126SWyllys.Ingersoll@Sun.COM 
13199126SWyllys.Ingersoll@Sun.COM 	return (DDI_FAILURE);
13209126SWyllys.Ingersoll@Sun.COM }
13219126SWyllys.Ingersoll@Sun.COM 
13229126SWyllys.Ingersoll@Sun.COM /*
13239126SWyllys.Ingersoll@Sun.COM  * Called by tpm_detach and tpm_attach (only on failure)
13249126SWyllys.Ingersoll@Sun.COM  * Free up the resources that are allocated
13259126SWyllys.Ingersoll@Sun.COM  */
13269126SWyllys.Ingersoll@Sun.COM static void
13279126SWyllys.Ingersoll@Sun.COM tpm_cleanup(dev_info_t *dip, tpm_state_t *tpm)
13289126SWyllys.Ingersoll@Sun.COM {
13299126SWyllys.Ingersoll@Sun.COM 	if (tpm == NULL)
13309126SWyllys.Ingersoll@Sun.COM 		return;
13319126SWyllys.Ingersoll@Sun.COM 
13329126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_MUTEX) {
13339126SWyllys.Ingersoll@Sun.COM 		mutex_destroy(&tpm->dev_lock);
13349126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_MUTEX);
13359126SWyllys.Ingersoll@Sun.COM 	}
13369126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_IO_ALLOC) {
13379126SWyllys.Ingersoll@Sun.COM 		ASSERT(tpm->iobuf != NULL);
13389126SWyllys.Ingersoll@Sun.COM 		kmem_free(tpm->iobuf, (sizeof (uint8_t))*(tpm->bufsize));
13399126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_IO_ALLOC);
13409126SWyllys.Ingersoll@Sun.COM 	}
13419126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_IO_MUTEX) {
13429126SWyllys.Ingersoll@Sun.COM 		mutex_destroy(&tpm->iobuf_lock);
13439126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_IO_MUTEX);
13449126SWyllys.Ingersoll@Sun.COM 	}
13459126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_IO_CV) {
13469126SWyllys.Ingersoll@Sun.COM 		cv_destroy(&tpm->iobuf_cv);
13479126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_IO_CV);
13489126SWyllys.Ingersoll@Sun.COM 	}
13499126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DIDREGSMAP) {
13509126SWyllys.Ingersoll@Sun.COM 		/* Free the mapped addresses */
13519126SWyllys.Ingersoll@Sun.COM 		if (tpm->handle != NULL)
13529126SWyllys.Ingersoll@Sun.COM 			ddi_regs_map_free(&tpm->handle);
13539126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DIDREGSMAP);
13549126SWyllys.Ingersoll@Sun.COM 	}
13559126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DIDMINOR) {
13569126SWyllys.Ingersoll@Sun.COM 		/* Remove minor node */
13579126SWyllys.Ingersoll@Sun.COM 		ddi_remove_minor_node(dip, NULL);
13589126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DIDMINOR);
13599126SWyllys.Ingersoll@Sun.COM 	}
13609126SWyllys.Ingersoll@Sun.COM }
13619126SWyllys.Ingersoll@Sun.COM 
13629126SWyllys.Ingersoll@Sun.COM static int
13639126SWyllys.Ingersoll@Sun.COM tpm_suspend(tpm_state_t *tpm)
13649126SWyllys.Ingersoll@Sun.COM {
13659126SWyllys.Ingersoll@Sun.COM 	if (tpm == NULL)
13669126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
13679126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
13689126SWyllys.Ingersoll@Sun.COM 	if (tpm->suspended) {
13699126SWyllys.Ingersoll@Sun.COM 		mutex_exit(&tpm->pm_mutex);
13709126SWyllys.Ingersoll@Sun.COM 		return (DDI_SUCCESS);
13719126SWyllys.Ingersoll@Sun.COM 	}
13729126SWyllys.Ingersoll@Sun.COM 	tpm->suspended = 1;
13739126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
13749126SWyllys.Ingersoll@Sun.COM 
13759126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
13769126SWyllys.Ingersoll@Sun.COM }
13779126SWyllys.Ingersoll@Sun.COM 
13789126SWyllys.Ingersoll@Sun.COM static int
13799126SWyllys.Ingersoll@Sun.COM tpm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
13809126SWyllys.Ingersoll@Sun.COM {
13819126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_detach";
13829126SWyllys.Ingersoll@Sun.COM 	int instance;
13839126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
13849126SWyllys.Ingersoll@Sun.COM 
13859126SWyllys.Ingersoll@Sun.COM 	ASSERT(dip != NULL);
13869126SWyllys.Ingersoll@Sun.COM 
13879126SWyllys.Ingersoll@Sun.COM 	instance = ddi_get_instance(dip);
13889126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
13899126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
13909126SWyllys.Ingersoll@Sun.COM 		    myname);
13919126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
13929126SWyllys.Ingersoll@Sun.COM 	}
13939126SWyllys.Ingersoll@Sun.COM 
13949126SWyllys.Ingersoll@Sun.COM 	switch (cmd) {
13959126SWyllys.Ingersoll@Sun.COM 	case DDI_DETACH:
13969126SWyllys.Ingersoll@Sun.COM 		/* Body is after the switch stmt */
13979126SWyllys.Ingersoll@Sun.COM 		break;
13989126SWyllys.Ingersoll@Sun.COM 	case DDI_SUSPEND:
13999126SWyllys.Ingersoll@Sun.COM 		return (tpm_suspend(tpm));
14009126SWyllys.Ingersoll@Sun.COM 	default:
14019126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: case %d not implemented", myname, cmd);
14029126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
14039126SWyllys.Ingersoll@Sun.COM 	}
14049126SWyllys.Ingersoll@Sun.COM 
14059126SWyllys.Ingersoll@Sun.COM 	/* Since we are freeing tpm structure, we need to gain the lock */
14069126SWyllys.Ingersoll@Sun.COM 
14079126SWyllys.Ingersoll@Sun.COM 	tpm_cleanup(dip, tpm);
14089126SWyllys.Ingersoll@Sun.COM 
14099126SWyllys.Ingersoll@Sun.COM 	mutex_destroy(&tpm->pm_mutex);
14109126SWyllys.Ingersoll@Sun.COM 	cv_destroy(&tpm->suspend_cv);
14119126SWyllys.Ingersoll@Sun.COM 
14129126SWyllys.Ingersoll@Sun.COM 	/* Free the soft state */
14139126SWyllys.Ingersoll@Sun.COM 	ddi_soft_state_free(statep, instance);
14149126SWyllys.Ingersoll@Sun.COM 	tpm = NULL;
14159126SWyllys.Ingersoll@Sun.COM 
14169126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
14179126SWyllys.Ingersoll@Sun.COM }
14189126SWyllys.Ingersoll@Sun.COM 
14199126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
14209126SWyllys.Ingersoll@Sun.COM static int
14219126SWyllys.Ingersoll@Sun.COM tpm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
14229126SWyllys.Ingersoll@Sun.COM {
14239126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_getinfo";
14249126SWyllys.Ingersoll@Sun.COM 	int instance;
14259126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
14269126SWyllys.Ingersoll@Sun.COM 
14279126SWyllys.Ingersoll@Sun.COM 	instance = ddi_get_instance(dip);
14289126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
14299126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
14309126SWyllys.Ingersoll@Sun.COM 		    myname);
14319126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
14329126SWyllys.Ingersoll@Sun.COM 	}
14339126SWyllys.Ingersoll@Sun.COM 
14349126SWyllys.Ingersoll@Sun.COM 	switch (cmd) {
14359126SWyllys.Ingersoll@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
14369126SWyllys.Ingersoll@Sun.COM 		*resultp = tpm->dip;
14379126SWyllys.Ingersoll@Sun.COM 		break;
14389126SWyllys.Ingersoll@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
14399126SWyllys.Ingersoll@Sun.COM 		*resultp = 0;
14409126SWyllys.Ingersoll@Sun.COM 		break;
14419126SWyllys.Ingersoll@Sun.COM 	default:
14429126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: cmd %d is not implemented", myname, cmd);
14439126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
14449126SWyllys.Ingersoll@Sun.COM 	}
14459126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
14469126SWyllys.Ingersoll@Sun.COM }
14479126SWyllys.Ingersoll@Sun.COM 
14489126SWyllys.Ingersoll@Sun.COM /*
14499126SWyllys.Ingersoll@Sun.COM  * Driver entry points
14509126SWyllys.Ingersoll@Sun.COM  */
14519126SWyllys.Ingersoll@Sun.COM 
14529126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
14539126SWyllys.Ingersoll@Sun.COM static int
14549126SWyllys.Ingersoll@Sun.COM tpm_open(dev_t *devp, int flag, int otyp, cred_t *cred)
14559126SWyllys.Ingersoll@Sun.COM {
14569126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_open";
14579126SWyllys.Ingersoll@Sun.COM 	int instance;
14589126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
14599126SWyllys.Ingersoll@Sun.COM 
14609126SWyllys.Ingersoll@Sun.COM 	ASSERT(devp != NULL);
14619126SWyllys.Ingersoll@Sun.COM 
14629126SWyllys.Ingersoll@Sun.COM 	instance = getminor(*devp);
14639126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
14649126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
14659126SWyllys.Ingersoll@Sun.COM 		    myname);
14669126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
14679126SWyllys.Ingersoll@Sun.COM 	}
14689126SWyllys.Ingersoll@Sun.COM 	if (otyp != OTYP_CHR) {
14699126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: otyp(%d) != OTYP_CHR(%d)",
14709126SWyllys.Ingersoll@Sun.COM 		    myname, otyp, OTYP_CHR);
14719126SWyllys.Ingersoll@Sun.COM 		return (EINVAL);
14729126SWyllys.Ingersoll@Sun.COM 	}
14739126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
14749126SWyllys.Ingersoll@Sun.COM 	while (tpm->suspended)
14759126SWyllys.Ingersoll@Sun.COM 		cv_wait(&tpm->suspend_cv, &tpm->pm_mutex);
14769126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
14779126SWyllys.Ingersoll@Sun.COM 
14789126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->dev_lock);
14799126SWyllys.Ingersoll@Sun.COM 	if (tpm->dev_held) {
14809126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: the device is already being used",
14819126SWyllys.Ingersoll@Sun.COM 		    myname);
14829126SWyllys.Ingersoll@Sun.COM 		mutex_exit(&tpm->dev_lock);
14839126SWyllys.Ingersoll@Sun.COM 		return (EBUSY);
14849126SWyllys.Ingersoll@Sun.COM 	}
14859126SWyllys.Ingersoll@Sun.COM 
14869126SWyllys.Ingersoll@Sun.COM 	/* The device is free so mark it busy */
14879126SWyllys.Ingersoll@Sun.COM 	tpm->dev_held = 1;
14889126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->dev_lock);
14899126SWyllys.Ingersoll@Sun.COM 
14909126SWyllys.Ingersoll@Sun.COM 	return (0);
14919126SWyllys.Ingersoll@Sun.COM }
14929126SWyllys.Ingersoll@Sun.COM 
14939126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
14949126SWyllys.Ingersoll@Sun.COM static int
14959126SWyllys.Ingersoll@Sun.COM tpm_close(dev_t dev, int flag, int otyp, cred_t *cred)
14969126SWyllys.Ingersoll@Sun.COM {
14979126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_close";
14989126SWyllys.Ingersoll@Sun.COM 	int instance;
14999126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
15009126SWyllys.Ingersoll@Sun.COM 
15019126SWyllys.Ingersoll@Sun.COM 	instance = getminor(dev);
15029126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
15039126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
15049126SWyllys.Ingersoll@Sun.COM 		    myname);
15059126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
15069126SWyllys.Ingersoll@Sun.COM 	}
15079126SWyllys.Ingersoll@Sun.COM 	if (otyp != OTYP_CHR) {
15089126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: otyp(%d) != OTYP_CHR(%d)",
15099126SWyllys.Ingersoll@Sun.COM 		    myname, otyp, OTYP_CHR);
15109126SWyllys.Ingersoll@Sun.COM 		return (EINVAL);
15119126SWyllys.Ingersoll@Sun.COM 	}
15129126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
15139126SWyllys.Ingersoll@Sun.COM 	while (tpm->suspended)
15149126SWyllys.Ingersoll@Sun.COM 		cv_wait(&tpm->suspend_cv, &tpm->pm_mutex);
15159126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
15169126SWyllys.Ingersoll@Sun.COM 
15179126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm->dev_held);
15189126SWyllys.Ingersoll@Sun.COM 
15199126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->dev_lock);
15209126SWyllys.Ingersoll@Sun.COM 	ASSERT(mutex_owned(&tpm->dev_lock));
15219126SWyllys.Ingersoll@Sun.COM 	tpm->dev_held = 0;
15229126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->dev_lock);
15239126SWyllys.Ingersoll@Sun.COM 
15249126SWyllys.Ingersoll@Sun.COM 	return (0);
15259126SWyllys.Ingersoll@Sun.COM }
15269126SWyllys.Ingersoll@Sun.COM 
15279126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
15289126SWyllys.Ingersoll@Sun.COM static int
15299126SWyllys.Ingersoll@Sun.COM tpm_read(dev_t dev, struct uio *uiop, cred_t *credp)
15309126SWyllys.Ingersoll@Sun.COM {
15319126SWyllys.Ingersoll@Sun.COM 	int ret;
15329126SWyllys.Ingersoll@Sun.COM 	uint32_t size;
15339126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_read";
15349126SWyllys.Ingersoll@Sun.COM 	int instance;
15359126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
15369126SWyllys.Ingersoll@Sun.COM 
15379126SWyllys.Ingersoll@Sun.COM 	instance = getminor(dev);
15389126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
15399126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
15409126SWyllys.Ingersoll@Sun.COM 		    myname);
15419126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
15429126SWyllys.Ingersoll@Sun.COM 	}
15439126SWyllys.Ingersoll@Sun.COM 	if (uiop == NULL) {
15449126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: passed in uiop is NULL", myname);
15459126SWyllys.Ingersoll@Sun.COM 		return (EFAULT);
15469126SWyllys.Ingersoll@Sun.COM 	}
15479126SWyllys.Ingersoll@Sun.COM 
15489126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
15499126SWyllys.Ingersoll@Sun.COM 	while (tpm->suspended)
15509126SWyllys.Ingersoll@Sun.COM 		cv_wait(&tpm->suspend_cv, &tpm->pm_mutex);
15519126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
15529126SWyllys.Ingersoll@Sun.COM 
15539126SWyllys.Ingersoll@Sun.COM 	/* Receive the data after requiring the lock */
15549126SWyllys.Ingersoll@Sun.COM 	ret = tpm_lock(tpm);
15559126SWyllys.Ingersoll@Sun.COM 
15569126SWyllys.Ingersoll@Sun.COM 	/* Timeout reached */
15579126SWyllys.Ingersoll@Sun.COM 	if (ret == ETIME)
15589126SWyllys.Ingersoll@Sun.COM 		return (ret);
15599126SWyllys.Ingersoll@Sun.COM 
15609126SWyllys.Ingersoll@Sun.COM 	if (uiop->uio_resid > tpm->bufsize) {
15619126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: read_in data is bigger "
15629126SWyllys.Ingersoll@Sun.COM 		    "than tpm->bufsize:read in:%d, bufsiz:%d",
15639126SWyllys.Ingersoll@Sun.COM 		    myname, (int)uiop->uio_resid, (int)tpm->bufsize);
15649126SWyllys.Ingersoll@Sun.COM 		ret = EIO;
15659126SWyllys.Ingersoll@Sun.COM 		goto OUT;
15669126SWyllys.Ingersoll@Sun.COM 	}
15679126SWyllys.Ingersoll@Sun.COM 
15689126SWyllys.Ingersoll@Sun.COM 	ret = tis_recv_data(tpm, tpm->iobuf, tpm->bufsize);
15699126SWyllys.Ingersoll@Sun.COM 	if (ret < TPM_HEADER_SIZE) {
15709126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_recv_data returned error", myname);
15719126SWyllys.Ingersoll@Sun.COM 		ret = EIO;
15729126SWyllys.Ingersoll@Sun.COM 		goto OUT;
15739126SWyllys.Ingersoll@Sun.COM 	}
15749126SWyllys.Ingersoll@Sun.COM 
15759126SWyllys.Ingersoll@Sun.COM 	size = load32(tpm->iobuf, 2);
15769126SWyllys.Ingersoll@Sun.COM 	if (ret != size) {
15779126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_recv_data:"
15789126SWyllys.Ingersoll@Sun.COM 		    "expected size=%d, actually read=%d",
15799126SWyllys.Ingersoll@Sun.COM 		    myname, size, ret);
15809126SWyllys.Ingersoll@Sun.COM 		ret = EIO;
15819126SWyllys.Ingersoll@Sun.COM 		goto OUT;
15829126SWyllys.Ingersoll@Sun.COM 	}
15839126SWyllys.Ingersoll@Sun.COM 
15849126SWyllys.Ingersoll@Sun.COM 	/* Send the buffer from the kernel to the userspace */
15859126SWyllys.Ingersoll@Sun.COM 	ret = uiomove(tpm->iobuf, size, UIO_READ, uiop);
15869126SWyllys.Ingersoll@Sun.COM 	if (ret) {
15879126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: uiomove returned error", myname);
15889126SWyllys.Ingersoll@Sun.COM 		goto OUT;
15899126SWyllys.Ingersoll@Sun.COM 	}
15909126SWyllys.Ingersoll@Sun.COM 
15919126SWyllys.Ingersoll@Sun.COM 	/* Zeroize the buffer... */
15929126SWyllys.Ingersoll@Sun.COM 	bzero(tpm->iobuf, tpm->bufsize);
15939126SWyllys.Ingersoll@Sun.COM 	ret = DDI_SUCCESS;
15949126SWyllys.Ingersoll@Sun.COM OUT:
15959126SWyllys.Ingersoll@Sun.COM 	/* We are done now: wake up the waiting threads */
15969126SWyllys.Ingersoll@Sun.COM 	tpm_unlock(tpm);
15979126SWyllys.Ingersoll@Sun.COM 
15989126SWyllys.Ingersoll@Sun.COM 	return (ret);
15999126SWyllys.Ingersoll@Sun.COM }
16009126SWyllys.Ingersoll@Sun.COM 
16019126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
16029126SWyllys.Ingersoll@Sun.COM static int
16039126SWyllys.Ingersoll@Sun.COM tpm_write(dev_t dev, struct uio *uiop, cred_t *credp)
16049126SWyllys.Ingersoll@Sun.COM {
16059126SWyllys.Ingersoll@Sun.COM 	int ret;
16069126SWyllys.Ingersoll@Sun.COM 	size_t len;
16079126SWyllys.Ingersoll@Sun.COM 	uint32_t size;
16089126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_write";
16099126SWyllys.Ingersoll@Sun.COM 	int instance;
16109126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
16119126SWyllys.Ingersoll@Sun.COM 
16129126SWyllys.Ingersoll@Sun.COM 	instance = getminor(dev);
16139126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
16149126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
16159126SWyllys.Ingersoll@Sun.COM 		    myname);
16169126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
16179126SWyllys.Ingersoll@Sun.COM 	}
16189126SWyllys.Ingersoll@Sun.COM 
16199126SWyllys.Ingersoll@Sun.COM 	if (uiop == NULL) {
16209126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: passed in uiop is NULL", myname);
16219126SWyllys.Ingersoll@Sun.COM 		return (EFAULT);
16229126SWyllys.Ingersoll@Sun.COM 	}
16239126SWyllys.Ingersoll@Sun.COM 
16249126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
16259126SWyllys.Ingersoll@Sun.COM 	while (tpm->suspended)
16269126SWyllys.Ingersoll@Sun.COM 		cv_wait(&tpm->suspend_cv, &tpm->pm_mutex);
16279126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
16289126SWyllys.Ingersoll@Sun.COM 
16299126SWyllys.Ingersoll@Sun.COM 	len = uiop->uio_resid;
16309126SWyllys.Ingersoll@Sun.COM 	if (len == 0) {
16319126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: requested read of len 0", myname);
16329126SWyllys.Ingersoll@Sun.COM 		return (0);
16339126SWyllys.Ingersoll@Sun.COM 	}
16349126SWyllys.Ingersoll@Sun.COM 
16359126SWyllys.Ingersoll@Sun.COM 	/* Get the lock for using iobuf */
16369126SWyllys.Ingersoll@Sun.COM 	ret = tpm_lock(tpm);
16379126SWyllys.Ingersoll@Sun.COM 	/* Timeout Reached */
16389126SWyllys.Ingersoll@Sun.COM 	if (ret == ETIME)
16399126SWyllys.Ingersoll@Sun.COM 		return (ret);
16409126SWyllys.Ingersoll@Sun.COM 
16419126SWyllys.Ingersoll@Sun.COM 	/* Copy the header and parse the structure to find out the size... */
16429126SWyllys.Ingersoll@Sun.COM 	ret = uiomove(tpm->iobuf, TPM_HEADER_SIZE, UIO_WRITE, uiop);
16439126SWyllys.Ingersoll@Sun.COM 	if (ret) {
16449126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: uiomove returned error"
16459126SWyllys.Ingersoll@Sun.COM 		    "while getting the the header",
16469126SWyllys.Ingersoll@Sun.COM 		    myname);
16479126SWyllys.Ingersoll@Sun.COM 		goto OUT;
16489126SWyllys.Ingersoll@Sun.COM 	}
16499126SWyllys.Ingersoll@Sun.COM 
16509126SWyllys.Ingersoll@Sun.COM 	/* Get the buffersize from the command buffer structure */
16519126SWyllys.Ingersoll@Sun.COM 	size = load32(tpm->iobuf, TPM_PARAMSIZE_OFFSET);
16529126SWyllys.Ingersoll@Sun.COM 
16539126SWyllys.Ingersoll@Sun.COM 	/* Copy the command to the contiguous buffer */
16549126SWyllys.Ingersoll@Sun.COM 	if (size > tpm->bufsize) {
16559126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: size %d is greater than "
16569126SWyllys.Ingersoll@Sun.COM 		    "the tpm's input buffer size %d",
16579126SWyllys.Ingersoll@Sun.COM 		    myname, (int)size, (int)tpm->bufsize);
16589126SWyllys.Ingersoll@Sun.COM 		ret = ENXIO;
16599126SWyllys.Ingersoll@Sun.COM 		goto OUT;
16609126SWyllys.Ingersoll@Sun.COM 	}
16619126SWyllys.Ingersoll@Sun.COM 
16629126SWyllys.Ingersoll@Sun.COM 	/* Copy the buffer from the userspace to kernel */
16639126SWyllys.Ingersoll@Sun.COM 	ret = uiomove(tpm->iobuf+TPM_HEADER_SIZE, size-TPM_HEADER_SIZE,
16649126SWyllys.Ingersoll@Sun.COM 	    UIO_WRITE, uiop);
16659126SWyllys.Ingersoll@Sun.COM 
16669126SWyllys.Ingersoll@Sun.COM 	if (ret) {
16679126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: uiomove returned error"
16689126SWyllys.Ingersoll@Sun.COM 		    "while getting the rest of the command", myname);
16699126SWyllys.Ingersoll@Sun.COM 		goto OUT;
16709126SWyllys.Ingersoll@Sun.COM 	}
16719126SWyllys.Ingersoll@Sun.COM 
16729126SWyllys.Ingersoll@Sun.COM 	/* Send the command */
16739126SWyllys.Ingersoll@Sun.COM 	ret = tis_send_data(tpm, tpm->iobuf, size);
16749126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
16759126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_send_data returned error", myname);
16769126SWyllys.Ingersoll@Sun.COM 		ret = EFAULT;
16779126SWyllys.Ingersoll@Sun.COM 		goto OUT;
16789126SWyllys.Ingersoll@Sun.COM 	}
16799126SWyllys.Ingersoll@Sun.COM 
16809126SWyllys.Ingersoll@Sun.COM 	/* Zeroize the buffer... */
16819126SWyllys.Ingersoll@Sun.COM 	bzero(tpm->iobuf, tpm->bufsize);
16829126SWyllys.Ingersoll@Sun.COM 	ret = DDI_SUCCESS;
16839126SWyllys.Ingersoll@Sun.COM OUT:
16849126SWyllys.Ingersoll@Sun.COM 	tpm_unlock(tpm);
16859126SWyllys.Ingersoll@Sun.COM 	return (ret);
16869126SWyllys.Ingersoll@Sun.COM }
16879126SWyllys.Ingersoll@Sun.COM 
16889126SWyllys.Ingersoll@Sun.COM /*
16899126SWyllys.Ingersoll@Sun.COM  * This is to deal with the contentions for the iobuf
16909126SWyllys.Ingersoll@Sun.COM  */
16919126SWyllys.Ingersoll@Sun.COM static inline int
16929126SWyllys.Ingersoll@Sun.COM tpm_lock(tpm_state_t *tpm)
16939126SWyllys.Ingersoll@Sun.COM {
16949126SWyllys.Ingersoll@Sun.COM 	int ret;
16959126SWyllys.Ingersoll@Sun.COM 	clock_t timeout;
16969126SWyllys.Ingersoll@Sun.COM 
16979126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->iobuf_lock);
16989126SWyllys.Ingersoll@Sun.COM 	ASSERT(mutex_owned(&tpm->iobuf_lock));
16999126SWyllys.Ingersoll@Sun.COM 
17009126SWyllys.Ingersoll@Sun.COM 	timeout = ddi_get_lbolt() + drv_usectohz(TPM_IO_TIMEOUT);
17019126SWyllys.Ingersoll@Sun.COM 
17029126SWyllys.Ingersoll@Sun.COM 	/* Wait until the iobuf becomes free with the timeout */
17039126SWyllys.Ingersoll@Sun.COM 	while (tpm->iobuf_inuse) {
17049126SWyllys.Ingersoll@Sun.COM 		ret = cv_timedwait(&tpm->iobuf_cv, &tpm->iobuf_lock, timeout);
17059126SWyllys.Ingersoll@Sun.COM 		if (ret <= 0) {
17069126SWyllys.Ingersoll@Sun.COM 			/* Timeout reached */
17079126SWyllys.Ingersoll@Sun.COM 			mutex_exit(&tpm->iobuf_lock);
17089126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "tpm_lock:iorequest timed out");
17099126SWyllys.Ingersoll@Sun.COM 			return (ETIME);
17109126SWyllys.Ingersoll@Sun.COM 		}
17119126SWyllys.Ingersoll@Sun.COM 	}
17129126SWyllys.Ingersoll@Sun.COM 	tpm->iobuf_inuse = 1;
17139126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->iobuf_lock);
17149126SWyllys.Ingersoll@Sun.COM 	return (0);
17159126SWyllys.Ingersoll@Sun.COM }
17169126SWyllys.Ingersoll@Sun.COM 
17179126SWyllys.Ingersoll@Sun.COM /*
17189126SWyllys.Ingersoll@Sun.COM  * This is to deal with the contentions for the iobuf
17199126SWyllys.Ingersoll@Sun.COM  */
17209126SWyllys.Ingersoll@Sun.COM static inline void
17219126SWyllys.Ingersoll@Sun.COM tpm_unlock(tpm_state_t *tpm)
17229126SWyllys.Ingersoll@Sun.COM {
17239126SWyllys.Ingersoll@Sun.COM 	/* Wake up the waiting threads */
17249126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->iobuf_lock);
17259126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm->iobuf_inuse == 1 && mutex_owned(&tpm->iobuf_lock));
17269126SWyllys.Ingersoll@Sun.COM 	tpm->iobuf_inuse = 0;
17279126SWyllys.Ingersoll@Sun.COM 	cv_broadcast(&tpm->iobuf_cv);
17289126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->iobuf_lock);
17299126SWyllys.Ingersoll@Sun.COM }
1730