xref: /onnv-gate/usr/src/uts/common/io/tpm/tpm.c (revision 9126:6acea8ac53c8)
1*9126SWyllys.Ingersoll@Sun.COM /*
2*9126SWyllys.Ingersoll@Sun.COM  * CDDL HEADER START
3*9126SWyllys.Ingersoll@Sun.COM  *
4*9126SWyllys.Ingersoll@Sun.COM  * The contents of this file are subject to the terms of the
5*9126SWyllys.Ingersoll@Sun.COM  * Common Development and Distribution License (the "License").
6*9126SWyllys.Ingersoll@Sun.COM  * You may not use this file except in compliance with the License.
7*9126SWyllys.Ingersoll@Sun.COM  *
8*9126SWyllys.Ingersoll@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*9126SWyllys.Ingersoll@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*9126SWyllys.Ingersoll@Sun.COM  * See the License for the specific language governing permissions
11*9126SWyllys.Ingersoll@Sun.COM  * and limitations under the License.
12*9126SWyllys.Ingersoll@Sun.COM  *
13*9126SWyllys.Ingersoll@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*9126SWyllys.Ingersoll@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*9126SWyllys.Ingersoll@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*9126SWyllys.Ingersoll@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*9126SWyllys.Ingersoll@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*9126SWyllys.Ingersoll@Sun.COM  *
19*9126SWyllys.Ingersoll@Sun.COM  * CDDL HEADER END
20*9126SWyllys.Ingersoll@Sun.COM  */
21*9126SWyllys.Ingersoll@Sun.COM 
22*9126SWyllys.Ingersoll@Sun.COM /*
23*9126SWyllys.Ingersoll@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*9126SWyllys.Ingersoll@Sun.COM  * Use is subject to license terms.
25*9126SWyllys.Ingersoll@Sun.COM  */
26*9126SWyllys.Ingersoll@Sun.COM 
27*9126SWyllys.Ingersoll@Sun.COM /*
28*9126SWyllys.Ingersoll@Sun.COM  * TPM 1.2 Driver for the TPMs that follow TIS v1.2
29*9126SWyllys.Ingersoll@Sun.COM  */
30*9126SWyllys.Ingersoll@Sun.COM 
31*9126SWyllys.Ingersoll@Sun.COM #include <sys/devops.h>		/* used by dev_ops */
32*9126SWyllys.Ingersoll@Sun.COM #include <sys/conf.h>		/* used by dev_ops,cb_ops */
33*9126SWyllys.Ingersoll@Sun.COM #include <sys/modctl.h>		/* for _init,_info,_fini,mod_* */
34*9126SWyllys.Ingersoll@Sun.COM #include <sys/ddi.h>		/* used by all entry points */
35*9126SWyllys.Ingersoll@Sun.COM #include <sys/sunddi.h>		/* used by all entry points */
36*9126SWyllys.Ingersoll@Sun.COM #include <sys/cmn_err.h>	/* used for debug outputs */
37*9126SWyllys.Ingersoll@Sun.COM #include <sys/types.h>		/* used by prop_op, ddi_prop_op */
38*9126SWyllys.Ingersoll@Sun.COM 
39*9126SWyllys.Ingersoll@Sun.COM #include <sys/file.h>		/* used by open, close */
40*9126SWyllys.Ingersoll@Sun.COM #include <sys/errno.h>		/* used by open,close,read,write */
41*9126SWyllys.Ingersoll@Sun.COM #include <sys/open.h>		/* used by open,close,read,write */
42*9126SWyllys.Ingersoll@Sun.COM #include <sys/cred.h>		/* used by open,close,read */
43*9126SWyllys.Ingersoll@Sun.COM #include <sys/uio.h>		/* used by read */
44*9126SWyllys.Ingersoll@Sun.COM #include <sys/stat.h>		/* defines S_IFCHR */
45*9126SWyllys.Ingersoll@Sun.COM 
46*9126SWyllys.Ingersoll@Sun.COM #include <sys/byteorder.h>	/* for ntohs, ntohl, htons, htonl */
47*9126SWyllys.Ingersoll@Sun.COM 
48*9126SWyllys.Ingersoll@Sun.COM #include <tss/platform.h> 	/* from SUNWtss */
49*9126SWyllys.Ingersoll@Sun.COM #include <tss/tpm.h> 		/* from SUNWtss */
50*9126SWyllys.Ingersoll@Sun.COM 
51*9126SWyllys.Ingersoll@Sun.COM #include "tpm_tis.h"
52*9126SWyllys.Ingersoll@Sun.COM #include "tpm_ddi.h"
53*9126SWyllys.Ingersoll@Sun.COM #include "tpm_duration.h"
54*9126SWyllys.Ingersoll@Sun.COM 
55*9126SWyllys.Ingersoll@Sun.COM #define	TPM_HEADER_SIZE 10
56*9126SWyllys.Ingersoll@Sun.COM typedef enum {
57*9126SWyllys.Ingersoll@Sun.COM 	TPM_TAG_OFFSET = 0,
58*9126SWyllys.Ingersoll@Sun.COM 	TPM_PARAMSIZE_OFFSET = 2,
59*9126SWyllys.Ingersoll@Sun.COM 	TPM_RETURN_OFFSET = 6,
60*9126SWyllys.Ingersoll@Sun.COM 	TPM_COMMAND_CODE_OFFSET = 6,
61*9126SWyllys.Ingersoll@Sun.COM } TPM_HEADER_OFFSET_T;
62*9126SWyllys.Ingersoll@Sun.COM 
63*9126SWyllys.Ingersoll@Sun.COM /*
64*9126SWyllys.Ingersoll@Sun.COM  * This is to address some TPMs that does not report the correct duration
65*9126SWyllys.Ingersoll@Sun.COM  * and timeouts.  In our experience with the production TPMs, we encountered
66*9126SWyllys.Ingersoll@Sun.COM  * time errors such as GetCapability command from TPM reporting the timeout
67*9126SWyllys.Ingersoll@Sun.COM  * and durations in milliseconds rather than microseconds.  Some other TPMs
68*9126SWyllys.Ingersoll@Sun.COM  * report the value 0's
69*9126SWyllys.Ingersoll@Sun.COM  *
70*9126SWyllys.Ingersoll@Sun.COM  * Short Duration is based on section 11.3.4 of TIS speciciation, that
71*9126SWyllys.Ingersoll@Sun.COM  * TPM_GetCapability (short duration) commands should not be longer than 750ms
72*9126SWyllys.Ingersoll@Sun.COM  * and that section 11.3.7 states that TPM_ContinueSelfTest (medium duration)
73*9126SWyllys.Ingersoll@Sun.COM  * should not be longer than 1 second.
74*9126SWyllys.Ingersoll@Sun.COM  */
75*9126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_SHORT_DURATION	750000
76*9126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_MEDIUM_DURATION	1000000
77*9126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_LONG_DURATION	300000000
78*9126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_TIMEOUT_A	750000
79*9126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_TIMEOUT_B	2000000
80*9126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_TIMEOUT_C	750000
81*9126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_TIMEOUT_D	750000
82*9126SWyllys.Ingersoll@Sun.COM 
83*9126SWyllys.Ingersoll@Sun.COM /*
84*9126SWyllys.Ingersoll@Sun.COM  * In order to test the 'millisecond bug', we test if DURATIONS and TIMEOUTS
85*9126SWyllys.Ingersoll@Sun.COM  * are unreasonably low...such as 10 milliseconds (TPM isn't that fast).
86*9126SWyllys.Ingersoll@Sun.COM  * and 400 milliseconds for long duration
87*9126SWyllys.Ingersoll@Sun.COM  */
88*9126SWyllys.Ingersoll@Sun.COM #define	TEN_MILLISECONDS	10000	/* 10 milliseconds */
89*9126SWyllys.Ingersoll@Sun.COM #define	FOUR_HUNDRED_MILLISECONDS 400000	/* 4 hundred milliseconds */
90*9126SWyllys.Ingersoll@Sun.COM 
91*9126SWyllys.Ingersoll@Sun.COM /*
92*9126SWyllys.Ingersoll@Sun.COM  * TPM input/output buffer offsets
93*9126SWyllys.Ingersoll@Sun.COM  */
94*9126SWyllys.Ingersoll@Sun.COM 
95*9126SWyllys.Ingersoll@Sun.COM typedef enum {
96*9126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_RESPSIZE_OFFSET = 10,
97*9126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_RESP_OFFSET = 14,
98*9126SWyllys.Ingersoll@Sun.COM } TPM_CAP_RET_OFFSET_T;
99*9126SWyllys.Ingersoll@Sun.COM 
100*9126SWyllys.Ingersoll@Sun.COM typedef enum {
101*9126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_TIMEOUT_A_OFFSET = 14,
102*9126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_TIMEOUT_B_OFFSET = 18,
103*9126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_TIMEOUT_C_OFFSET = 22,
104*9126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_TIMEOUT_D_OFFSET = 26,
105*9126SWyllys.Ingersoll@Sun.COM } TPM_CAP_TIMEOUT_OFFSET_T;
106*9126SWyllys.Ingersoll@Sun.COM 
107*9126SWyllys.Ingersoll@Sun.COM typedef enum {
108*9126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_DUR_SHORT_OFFSET = 14,
109*9126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_DUR_MEDIUM_OFFSET = 18,
110*9126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_DUR_LONG_OFFSET = 22,
111*9126SWyllys.Ingersoll@Sun.COM } TPM_CAP_DURATION_OFFSET_T;
112*9126SWyllys.Ingersoll@Sun.COM 
113*9126SWyllys.Ingersoll@Sun.COM #define	TPM_CAP_VERSION_INFO_OFFSET	14
114*9126SWyllys.Ingersoll@Sun.COM #define	TPM_CAP_VERSION_INFO_SIZE	15
115*9126SWyllys.Ingersoll@Sun.COM 
116*9126SWyllys.Ingersoll@Sun.COM /*
117*9126SWyllys.Ingersoll@Sun.COM  * Internal TPM command functions
118*9126SWyllys.Ingersoll@Sun.COM  */
119*9126SWyllys.Ingersoll@Sun.COM static int itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz);
120*9126SWyllys.Ingersoll@Sun.COM static int tpm_get_timeouts(tpm_state_t *tpm);
121*9126SWyllys.Ingersoll@Sun.COM static int tpm_get_duration(tpm_state_t *tpm);
122*9126SWyllys.Ingersoll@Sun.COM static int tpm_get_version(tpm_state_t *tpm);
123*9126SWyllys.Ingersoll@Sun.COM static int tpm_continue_selftest(tpm_state_t *tpm);
124*9126SWyllys.Ingersoll@Sun.COM 
125*9126SWyllys.Ingersoll@Sun.COM /*
126*9126SWyllys.Ingersoll@Sun.COM  * Internal TIS related functions
127*9126SWyllys.Ingersoll@Sun.COM  */
128*9126SWyllys.Ingersoll@Sun.COM static int tpm_wait_for_stat(tpm_state_t *, uint8_t, clock_t);
129*9126SWyllys.Ingersoll@Sun.COM static clock_t tpm_get_ordinal_duration(tpm_state_t *, uint8_t);
130*9126SWyllys.Ingersoll@Sun.COM static int tis_check_active_locality(tpm_state_t *, char);
131*9126SWyllys.Ingersoll@Sun.COM static int tis_request_locality(tpm_state_t *, char);
132*9126SWyllys.Ingersoll@Sun.COM static void tis_release_locality(tpm_state_t *, char, int);
133*9126SWyllys.Ingersoll@Sun.COM static int tis_init(tpm_state_t *);
134*9126SWyllys.Ingersoll@Sun.COM static uint8_t tis_get_status(tpm_state_t *);
135*9126SWyllys.Ingersoll@Sun.COM static int tis_send_data(tpm_state_t *, uint8_t *, size_t);
136*9126SWyllys.Ingersoll@Sun.COM static int tis_recv_data(tpm_state_t *, uint8_t *, size_t);
137*9126SWyllys.Ingersoll@Sun.COM 
138*9126SWyllys.Ingersoll@Sun.COM /* Auxilliary */
139*9126SWyllys.Ingersoll@Sun.COM static int receive_data(tpm_state_t *, uint8_t *, size_t);
140*9126SWyllys.Ingersoll@Sun.COM static inline int tpm_lock(tpm_state_t *);
141*9126SWyllys.Ingersoll@Sun.COM static inline void tpm_unlock(tpm_state_t *);
142*9126SWyllys.Ingersoll@Sun.COM static void tpm_cleanup(dev_info_t *, tpm_state_t *);
143*9126SWyllys.Ingersoll@Sun.COM 
144*9126SWyllys.Ingersoll@Sun.COM /*
145*9126SWyllys.Ingersoll@Sun.COM  * Sun DDI/DDK entry points
146*9126SWyllys.Ingersoll@Sun.COM  */
147*9126SWyllys.Ingersoll@Sun.COM 
148*9126SWyllys.Ingersoll@Sun.COM /* Declaration of autoconfig functions */
149*9126SWyllys.Ingersoll@Sun.COM static int tpm_attach(dev_info_t *, ddi_attach_cmd_t);
150*9126SWyllys.Ingersoll@Sun.COM static int tpm_detach(dev_info_t *, ddi_detach_cmd_t);
151*9126SWyllys.Ingersoll@Sun.COM static int tpm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
152*9126SWyllys.Ingersoll@Sun.COM static int tpm_quiesce(dev_info_t *);
153*9126SWyllys.Ingersoll@Sun.COM /* End of autoconfig functions */
154*9126SWyllys.Ingersoll@Sun.COM 
155*9126SWyllys.Ingersoll@Sun.COM /* Declaration of driver entry point functions */
156*9126SWyllys.Ingersoll@Sun.COM static int tpm_open(dev_t *, int, int, cred_t *);
157*9126SWyllys.Ingersoll@Sun.COM static int tpm_close(dev_t, int, int, cred_t *);
158*9126SWyllys.Ingersoll@Sun.COM static int tpm_read(dev_t, struct uio *, cred_t *);
159*9126SWyllys.Ingersoll@Sun.COM static int tpm_write(dev_t, struct uio *, cred_t *);
160*9126SWyllys.Ingersoll@Sun.COM /* End of driver entry point functions */
161*9126SWyllys.Ingersoll@Sun.COM 
162*9126SWyllys.Ingersoll@Sun.COM /* cb_ops structure */
163*9126SWyllys.Ingersoll@Sun.COM static struct cb_ops tpm_cb_ops = {
164*9126SWyllys.Ingersoll@Sun.COM 	tpm_open,
165*9126SWyllys.Ingersoll@Sun.COM 	tpm_close,
166*9126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no strategy - nodev returns ENXIO */
167*9126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no print */
168*9126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no dump */
169*9126SWyllys.Ingersoll@Sun.COM 	tpm_read,
170*9126SWyllys.Ingersoll@Sun.COM 	tpm_write,
171*9126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no ioctl */
172*9126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no devmap */
173*9126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no mmap */
174*9126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no segmap */
175*9126SWyllys.Ingersoll@Sun.COM 	nochpoll,	/* returns ENXIO for non-pollable devices */
176*9126SWyllys.Ingersoll@Sun.COM 	ddi_prop_op,
177*9126SWyllys.Ingersoll@Sun.COM 	NULL,		/* streamtab struc */
178*9126SWyllys.Ingersoll@Sun.COM 	D_MP,		/* compatibility flags */
179*9126SWyllys.Ingersoll@Sun.COM 	CB_REV,		/* cb_ops revision number */
180*9126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no aread */
181*9126SWyllys.Ingersoll@Sun.COM 	nodev		/* no awrite */
182*9126SWyllys.Ingersoll@Sun.COM };
183*9126SWyllys.Ingersoll@Sun.COM 
184*9126SWyllys.Ingersoll@Sun.COM /* dev_ops structure */
185*9126SWyllys.Ingersoll@Sun.COM static struct dev_ops tpm_dev_ops = {
186*9126SWyllys.Ingersoll@Sun.COM 	DEVO_REV,
187*9126SWyllys.Ingersoll@Sun.COM 	0,		/* reference count */
188*9126SWyllys.Ingersoll@Sun.COM 	tpm_getinfo,
189*9126SWyllys.Ingersoll@Sun.COM 	nulldev,	/* no identify - nulldev returns 0 */
190*9126SWyllys.Ingersoll@Sun.COM 	nulldev,
191*9126SWyllys.Ingersoll@Sun.COM 	tpm_attach,
192*9126SWyllys.Ingersoll@Sun.COM 	tpm_detach,
193*9126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no reset - nodev returns ENXIO */
194*9126SWyllys.Ingersoll@Sun.COM 	&tpm_cb_ops,
195*9126SWyllys.Ingersoll@Sun.COM 	(struct bus_ops *)NULL,
196*9126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no power */
197*9126SWyllys.Ingersoll@Sun.COM 	tpm_quiesce
198*9126SWyllys.Ingersoll@Sun.COM };
199*9126SWyllys.Ingersoll@Sun.COM 
200*9126SWyllys.Ingersoll@Sun.COM /* modldrv structure */
201*9126SWyllys.Ingersoll@Sun.COM static struct modldrv modldrv = {
202*9126SWyllys.Ingersoll@Sun.COM 	&mod_driverops,		/* Type: This is a driver */
203*9126SWyllys.Ingersoll@Sun.COM 	"TPM 1.2 driver",	/* Name of the module. */
204*9126SWyllys.Ingersoll@Sun.COM 	&tpm_dev_ops
205*9126SWyllys.Ingersoll@Sun.COM };
206*9126SWyllys.Ingersoll@Sun.COM 
207*9126SWyllys.Ingersoll@Sun.COM /* modlinkage structure */
208*9126SWyllys.Ingersoll@Sun.COM static struct modlinkage tpm_ml = {
209*9126SWyllys.Ingersoll@Sun.COM 	MODREV_1,
210*9126SWyllys.Ingersoll@Sun.COM 	&modldrv,
211*9126SWyllys.Ingersoll@Sun.COM 	NULL
212*9126SWyllys.Ingersoll@Sun.COM };
213*9126SWyllys.Ingersoll@Sun.COM 
214*9126SWyllys.Ingersoll@Sun.COM static void *statep = NULL;
215*9126SWyllys.Ingersoll@Sun.COM 
216*9126SWyllys.Ingersoll@Sun.COM /*
217*9126SWyllys.Ingersoll@Sun.COM  * TPM commands to get the TPM's properties, e.g.,timeout
218*9126SWyllys.Ingersoll@Sun.COM  */
219*9126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
220*9126SWyllys.Ingersoll@Sun.COM static int
221*9126SWyllys.Ingersoll@Sun.COM tpm_quiesce(dev_info_t *dip)
222*9126SWyllys.Ingersoll@Sun.COM {
223*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
224*9126SWyllys.Ingersoll@Sun.COM }
225*9126SWyllys.Ingersoll@Sun.COM 
226*9126SWyllys.Ingersoll@Sun.COM static uint32_t
227*9126SWyllys.Ingersoll@Sun.COM load32(uchar_t *ptr, uint32_t offset)
228*9126SWyllys.Ingersoll@Sun.COM {
229*9126SWyllys.Ingersoll@Sun.COM 	uint32_t val;
230*9126SWyllys.Ingersoll@Sun.COM 	bcopy(ptr + offset, &val, sizeof (uint32_t));
231*9126SWyllys.Ingersoll@Sun.COM 
232*9126SWyllys.Ingersoll@Sun.COM 	return (ntohl(val));
233*9126SWyllys.Ingersoll@Sun.COM }
234*9126SWyllys.Ingersoll@Sun.COM 
235*9126SWyllys.Ingersoll@Sun.COM /*
236*9126SWyllys.Ingersoll@Sun.COM  * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
237*9126SWyllys.Ingersoll@Sun.COM  * with the subcommand TPM_CAP_PROP_TIS_TIMEOUT
238*9126SWyllys.Ingersoll@Sun.COM  * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
239*9126SWyllys.Ingersoll@Sun.COM  */
240*9126SWyllys.Ingersoll@Sun.COM static int
241*9126SWyllys.Ingersoll@Sun.COM tpm_get_timeouts(tpm_state_t *tpm)
242*9126SWyllys.Ingersoll@Sun.COM {
243*9126SWyllys.Ingersoll@Sun.COM 	int ret;
244*9126SWyllys.Ingersoll@Sun.COM 	uint32_t timeout;   /* in milliseconds */
245*9126SWyllys.Ingersoll@Sun.COM 	uint32_t len;
246*9126SWyllys.Ingersoll@Sun.COM 
247*9126SWyllys.Ingersoll@Sun.COM 	/* The buffer size (30) needs room for 4 timeout values (uint32_t) */
248*9126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[30] = {
249*9126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU_COMMAND */
250*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 22,	/* paramsize in bytes */
251*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 101,	/* TPM_ORD_GetCapability */
252*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 5,	/* TPM_CAP_Prop */
253*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 4,	/* SUB_CAP size in bytes */
254*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 1, 21	/* TPM_CAP_PROP_TIS_TIMEOUT(0x115) */
255*9126SWyllys.Ingersoll@Sun.COM 	};
256*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_timeout";
257*9126SWyllys.Ingersoll@Sun.COM 
258*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
259*9126SWyllys.Ingersoll@Sun.COM 
260*9126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
261*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
262*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: itpm_command failed", myname);
263*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
264*9126SWyllys.Ingersoll@Sun.COM 	}
265*9126SWyllys.Ingersoll@Sun.COM 
266*9126SWyllys.Ingersoll@Sun.COM 	/*
267*9126SWyllys.Ingersoll@Sun.COM 	 * Get the length of the returned buffer
268*9126SWyllys.Ingersoll@Sun.COM 	 * Make sure that there are 4 timeout values returned
269*9126SWyllys.Ingersoll@Sun.COM 	 * length of the capability response is stored in data[10-13]
270*9126SWyllys.Ingersoll@Sun.COM 	 * Also the TPM is in network byte order
271*9126SWyllys.Ingersoll@Sun.COM 	 */
272*9126SWyllys.Ingersoll@Sun.COM 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
273*9126SWyllys.Ingersoll@Sun.COM 	if (len != 4 * sizeof (uint32_t)) {
274*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: capability response size should be %d"
275*9126SWyllys.Ingersoll@Sun.COM 		    "instead it's %d",
276*9126SWyllys.Ingersoll@Sun.COM 		    myname, (int)(4 * sizeof (uint32_t)), (int)len);
277*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
278*9126SWyllys.Ingersoll@Sun.COM 	}
279*9126SWyllys.Ingersoll@Sun.COM 
280*9126SWyllys.Ingersoll@Sun.COM 	/* Get the four timeout's: a,b,c,d (they are 4 bytes long each) */
281*9126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_A_OFFSET);
282*9126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
283*9126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_A;
284*9126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
285*9126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
286*9126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
287*9126SWyllys.Ingersoll@Sun.COM 	}
288*9126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_a = drv_usectohz(timeout);
289*9126SWyllys.Ingersoll@Sun.COM 
290*9126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_B_OFFSET);
291*9126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
292*9126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_B;
293*9126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
294*9126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
295*9126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
296*9126SWyllys.Ingersoll@Sun.COM 	}
297*9126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_b = drv_usectohz(timeout);
298*9126SWyllys.Ingersoll@Sun.COM 
299*9126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_C_OFFSET);
300*9126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
301*9126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_C;
302*9126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
303*9126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
304*9126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
305*9126SWyllys.Ingersoll@Sun.COM 	}
306*9126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_c = drv_usectohz(timeout);
307*9126SWyllys.Ingersoll@Sun.COM 
308*9126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_D_OFFSET);
309*9126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
310*9126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_D;
311*9126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
312*9126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
313*9126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
314*9126SWyllys.Ingersoll@Sun.COM 	}
315*9126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_d = drv_usectohz(timeout);
316*9126SWyllys.Ingersoll@Sun.COM 
317*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
318*9126SWyllys.Ingersoll@Sun.COM }
319*9126SWyllys.Ingersoll@Sun.COM 
320*9126SWyllys.Ingersoll@Sun.COM /*
321*9126SWyllys.Ingersoll@Sun.COM  * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
322*9126SWyllys.Ingersoll@Sun.COM  * with the subcommand TPM_CAP_PROP_TIS_DURATION
323*9126SWyllys.Ingersoll@Sun.COM  * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
324*9126SWyllys.Ingersoll@Sun.COM  */
325*9126SWyllys.Ingersoll@Sun.COM static int
326*9126SWyllys.Ingersoll@Sun.COM tpm_get_duration(tpm_state_t *tpm) {
327*9126SWyllys.Ingersoll@Sun.COM 	int ret;
328*9126SWyllys.Ingersoll@Sun.COM 	uint32_t duration;
329*9126SWyllys.Ingersoll@Sun.COM 	uint32_t len;
330*9126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[30] = {
331*9126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU_COMMAND */
332*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 22,	/* paramsize in bytes */
333*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 101,	/* TPM_ORD_GetCapability */
334*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 5,	/* TPM_CAP_Prop */
335*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 4,	/* SUB_CAP size in bytes */
336*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 1, 32	/* TPM_CAP_PROP_TIS_DURATION(0x120) */
337*9126SWyllys.Ingersoll@Sun.COM 	};
338*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_duration";
339*9126SWyllys.Ingersoll@Sun.COM 
340*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
341*9126SWyllys.Ingersoll@Sun.COM 
342*9126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
343*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
344*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: itpm_command failed with ret code: 0x%x",
345*9126SWyllys.Ingersoll@Sun.COM 			myname, ret);
346*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
347*9126SWyllys.Ingersoll@Sun.COM 	}
348*9126SWyllys.Ingersoll@Sun.COM 
349*9126SWyllys.Ingersoll@Sun.COM 	/*
350*9126SWyllys.Ingersoll@Sun.COM 	 * Get the length of the returned buffer
351*9126SWyllys.Ingersoll@Sun.COM 	 * Make sure that there are 3 duration values (S,M,L: in that order)
352*9126SWyllys.Ingersoll@Sun.COM 	 * length of the capability response is stored in data[10-13]
353*9126SWyllys.Ingersoll@Sun.COM 	 * Also the TPM is in network byte order
354*9126SWyllys.Ingersoll@Sun.COM 	 */
355*9126SWyllys.Ingersoll@Sun.COM 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
356*9126SWyllys.Ingersoll@Sun.COM 	if (len != 3 * sizeof (uint32_t)) {
357*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: capability response should be %d, "
358*9126SWyllys.Ingersoll@Sun.COM 		    "instead, it's %d",
359*9126SWyllys.Ingersoll@Sun.COM 		    myname, (int)(3 * sizeof (uint32_t)), (int)len);
360*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
361*9126SWyllys.Ingersoll@Sun.COM 	}
362*9126SWyllys.Ingersoll@Sun.COM 
363*9126SWyllys.Ingersoll@Sun.COM 	duration = load32(buf, TPM_CAP_DUR_SHORT_OFFSET);
364*9126SWyllys.Ingersoll@Sun.COM 	if (duration == 0) {
365*9126SWyllys.Ingersoll@Sun.COM 		duration = DEFAULT_SHORT_DURATION;
366*9126SWyllys.Ingersoll@Sun.COM 	} else if (duration < TEN_MILLISECONDS) {
367*9126SWyllys.Ingersoll@Sun.COM 		duration *= 1000;
368*9126SWyllys.Ingersoll@Sun.COM 	}
369*9126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_SHORT] = drv_usectohz(duration);
370*9126SWyllys.Ingersoll@Sun.COM 
371*9126SWyllys.Ingersoll@Sun.COM 	duration = load32(buf, TPM_CAP_DUR_MEDIUM_OFFSET);
372*9126SWyllys.Ingersoll@Sun.COM 	if (duration == 0) {
373*9126SWyllys.Ingersoll@Sun.COM 		duration = DEFAULT_MEDIUM_DURATION;
374*9126SWyllys.Ingersoll@Sun.COM 	} else if (duration < TEN_MILLISECONDS) {
375*9126SWyllys.Ingersoll@Sun.COM 		duration *= 1000;
376*9126SWyllys.Ingersoll@Sun.COM 	}
377*9126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_MEDIUM] = drv_usectohz(duration);
378*9126SWyllys.Ingersoll@Sun.COM 
379*9126SWyllys.Ingersoll@Sun.COM 	duration = load32(buf, TPM_CAP_DUR_LONG_OFFSET);
380*9126SWyllys.Ingersoll@Sun.COM 	if (duration == 0) {
381*9126SWyllys.Ingersoll@Sun.COM 		duration = DEFAULT_LONG_DURATION;
382*9126SWyllys.Ingersoll@Sun.COM 	} else if (duration < FOUR_HUNDRED_MILLISECONDS) {
383*9126SWyllys.Ingersoll@Sun.COM 		duration *= 1000;
384*9126SWyllys.Ingersoll@Sun.COM 	}
385*9126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_LONG] = drv_usectohz(duration);
386*9126SWyllys.Ingersoll@Sun.COM 
387*9126SWyllys.Ingersoll@Sun.COM 	/* Just make the undefined duration be the same as the LONG */
388*9126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_UNDEFINED] = tpm->duration[TPM_LONG];
389*9126SWyllys.Ingersoll@Sun.COM 
390*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
391*9126SWyllys.Ingersoll@Sun.COM }
392*9126SWyllys.Ingersoll@Sun.COM 
393*9126SWyllys.Ingersoll@Sun.COM /*
394*9126SWyllys.Ingersoll@Sun.COM  * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
395*9126SWyllys.Ingersoll@Sun.COM  * with the subcommand TPM_CAP_PROP_TIS_DURATION
396*9126SWyllys.Ingersoll@Sun.COM  * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
397*9126SWyllys.Ingersoll@Sun.COM  */
398*9126SWyllys.Ingersoll@Sun.COM static int
399*9126SWyllys.Ingersoll@Sun.COM tpm_get_version(tpm_state_t *tpm) {
400*9126SWyllys.Ingersoll@Sun.COM 	int ret;
401*9126SWyllys.Ingersoll@Sun.COM 	uint32_t len;
402*9126SWyllys.Ingersoll@Sun.COM 	char vendorId[5];
403*9126SWyllys.Ingersoll@Sun.COM 	/* If this buf is too small, the "vendor specific" data won't fit */
404*9126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[64] = {
405*9126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU_COMMAND */
406*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 18,	/* paramsize in bytes */
407*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 101,	/* TPM_ORD_GetCapability */
408*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 0x1A,	/* TPM_CAP_VERSION_VAL */
409*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 0,	/* SUB_CAP size in bytes */
410*9126SWyllys.Ingersoll@Sun.COM 	};
411*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_version";
412*9126SWyllys.Ingersoll@Sun.COM 
413*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
414*9126SWyllys.Ingersoll@Sun.COM 
415*9126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
416*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
417*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: itpm_command failed with ret code: 0x%x",
418*9126SWyllys.Ingersoll@Sun.COM 			myname, ret);
419*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
420*9126SWyllys.Ingersoll@Sun.COM 	}
421*9126SWyllys.Ingersoll@Sun.COM 
422*9126SWyllys.Ingersoll@Sun.COM 	/*
423*9126SWyllys.Ingersoll@Sun.COM 	 * Get the length of the returned buffer.
424*9126SWyllys.Ingersoll@Sun.COM 	 */
425*9126SWyllys.Ingersoll@Sun.COM 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
426*9126SWyllys.Ingersoll@Sun.COM 	if (len < TPM_CAP_VERSION_INFO_SIZE) {
427*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: capability response should be greater"
428*9126SWyllys.Ingersoll@Sun.COM 		    " than %d, instead, it's %d",
429*9126SWyllys.Ingersoll@Sun.COM 		    myname,
430*9126SWyllys.Ingersoll@Sun.COM 		    TPM_CAP_VERSION_INFO_SIZE,
431*9126SWyllys.Ingersoll@Sun.COM 		    len);
432*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
433*9126SWyllys.Ingersoll@Sun.COM 	}
434*9126SWyllys.Ingersoll@Sun.COM 
435*9126SWyllys.Ingersoll@Sun.COM 	bcopy(buf + TPM_CAP_VERSION_INFO_OFFSET, &tpm->vers_info,
436*9126SWyllys.Ingersoll@Sun.COM 	    TPM_CAP_VERSION_INFO_SIZE);
437*9126SWyllys.Ingersoll@Sun.COM 
438*9126SWyllys.Ingersoll@Sun.COM 	bcopy(tpm->vers_info.tpmVendorID, vendorId,
439*9126SWyllys.Ingersoll@Sun.COM 	    sizeof (tpm->vers_info.tpmVendorID));
440*9126SWyllys.Ingersoll@Sun.COM 	vendorId[4] = '\0';
441*9126SWyllys.Ingersoll@Sun.COM 
442*9126SWyllys.Ingersoll@Sun.COM 	cmn_err(CE_NOTE, "!TPM found: Ver %d.%d, Rev %d.%d, "
443*9126SWyllys.Ingersoll@Sun.COM 	    "SpecLevel %d, errataRev %d, VendorId '%s'",
444*9126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.major,	/* Version */
445*9126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.minor,
446*9126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.revMajor,	/* Revision */
447*9126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.revMinor,
448*9126SWyllys.Ingersoll@Sun.COM 	    (int)ntohs(tpm->vers_info.specLevel),
449*9126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.errataRev,
450*9126SWyllys.Ingersoll@Sun.COM 	    vendorId);
451*9126SWyllys.Ingersoll@Sun.COM 
452*9126SWyllys.Ingersoll@Sun.COM 	/*
453*9126SWyllys.Ingersoll@Sun.COM 	 * This driver only supports TPM Version 1.2
454*9126SWyllys.Ingersoll@Sun.COM 	 */
455*9126SWyllys.Ingersoll@Sun.COM 	if (tpm->vers_info.version.major != 1 &&
456*9126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.minor != 2) {
457*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: Unsupported TPM version (%d.%d)",
458*9126SWyllys.Ingersoll@Sun.COM 		    myname,
459*9126SWyllys.Ingersoll@Sun.COM 		    tpm->vers_info.version.major,		/* Version */
460*9126SWyllys.Ingersoll@Sun.COM 		    tpm->vers_info.version.minor);
461*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
462*9126SWyllys.Ingersoll@Sun.COM 	}
463*9126SWyllys.Ingersoll@Sun.COM 
464*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
465*9126SWyllys.Ingersoll@Sun.COM }
466*9126SWyllys.Ingersoll@Sun.COM 
467*9126SWyllys.Ingersoll@Sun.COM /*
468*9126SWyllys.Ingersoll@Sun.COM  * To prevent the TPM from complaining that certain functions are not tested
469*9126SWyllys.Ingersoll@Sun.COM  * we run this command when the driver attaches.
470*9126SWyllys.Ingersoll@Sun.COM  * For details see Section 4.2 of TPM Main Part 3 Command Specification
471*9126SWyllys.Ingersoll@Sun.COM  */
472*9126SWyllys.Ingersoll@Sun.COM static int
473*9126SWyllys.Ingersoll@Sun.COM tpm_continue_selftest(tpm_state_t *tpm) {
474*9126SWyllys.Ingersoll@Sun.COM 	int ret;
475*9126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[10] = {
476*9126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU COMMAND */
477*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 10,	/* paramsize in bytes */
478*9126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 83	/* TPM_ORD_ContinueSelfTest */
479*9126SWyllys.Ingersoll@Sun.COM 	};
480*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_continue_selftest";
481*9126SWyllys.Ingersoll@Sun.COM 
482*9126SWyllys.Ingersoll@Sun.COM 	/* Need a longer timeout */
483*9126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
484*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
485*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: itpm_command failed", myname);
486*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
487*9126SWyllys.Ingersoll@Sun.COM 	}
488*9126SWyllys.Ingersoll@Sun.COM 
489*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
490*9126SWyllys.Ingersoll@Sun.COM }
491*9126SWyllys.Ingersoll@Sun.COM /*
492*9126SWyllys.Ingersoll@Sun.COM  * Auxilary Functions
493*9126SWyllys.Ingersoll@Sun.COM  */
494*9126SWyllys.Ingersoll@Sun.COM 
495*9126SWyllys.Ingersoll@Sun.COM /*
496*9126SWyllys.Ingersoll@Sun.COM  * Find out how long we should wait for the TPM command to complete a command
497*9126SWyllys.Ingersoll@Sun.COM  */
498*9126SWyllys.Ingersoll@Sun.COM static clock_t
499*9126SWyllys.Ingersoll@Sun.COM tpm_get_ordinal_duration(tpm_state_t *tpm, uint8_t ordinal)
500*9126SWyllys.Ingersoll@Sun.COM {
501*9126SWyllys.Ingersoll@Sun.COM 	uint8_t index;
502*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_ordinal_duration";
503*9126SWyllys.Ingersoll@Sun.COM 
504*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
505*9126SWyllys.Ingersoll@Sun.COM 
506*9126SWyllys.Ingersoll@Sun.COM 	/* Default and failure case for IFX */
507*9126SWyllys.Ingersoll@Sun.COM 	/* Is it a TSC_ORDINAL? */
508*9126SWyllys.Ingersoll@Sun.COM 	if (ordinal & TSC_ORDINAL_MASK) {
509*9126SWyllys.Ingersoll@Sun.COM 		if (ordinal > TSC_ORDINAL_MAX) {
510*9126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN,
511*9126SWyllys.Ingersoll@Sun.COM 			    "%s: tsc ordinal: %d exceeds MAX: %d",
512*9126SWyllys.Ingersoll@Sun.COM 			    myname, ordinal, TSC_ORDINAL_MAX);
513*9126SWyllys.Ingersoll@Sun.COM 			return (0);
514*9126SWyllys.Ingersoll@Sun.COM 		}
515*9126SWyllys.Ingersoll@Sun.COM 		index = tsc_ords_duration[ordinal];
516*9126SWyllys.Ingersoll@Sun.COM 	} else {
517*9126SWyllys.Ingersoll@Sun.COM 		if (ordinal > TPM_ORDINAL_MAX) {
518*9126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN,
519*9126SWyllys.Ingersoll@Sun.COM 			    "%s: ordinal %d exceeds MAX: %d",
520*9126SWyllys.Ingersoll@Sun.COM 			    myname, ordinal, TPM_ORDINAL_MAX);
521*9126SWyllys.Ingersoll@Sun.COM 			return (0);
522*9126SWyllys.Ingersoll@Sun.COM 		}
523*9126SWyllys.Ingersoll@Sun.COM 		index = tpm_ords_duration[ordinal];
524*9126SWyllys.Ingersoll@Sun.COM 	}
525*9126SWyllys.Ingersoll@Sun.COM 
526*9126SWyllys.Ingersoll@Sun.COM 	if (index > TPM_DURATION_MAX_IDX) {
527*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: FATAL:index '%d' is out of bound",
528*9126SWyllys.Ingersoll@Sun.COM 		    myname, index);
529*9126SWyllys.Ingersoll@Sun.COM 		return (0);
530*9126SWyllys.Ingersoll@Sun.COM 	}
531*9126SWyllys.Ingersoll@Sun.COM 	return (tpm->duration[index]);
532*9126SWyllys.Ingersoll@Sun.COM }
533*9126SWyllys.Ingersoll@Sun.COM 
534*9126SWyllys.Ingersoll@Sun.COM /*
535*9126SWyllys.Ingersoll@Sun.COM  * Internal TPM Transmit Function:
536*9126SWyllys.Ingersoll@Sun.COM  * Calls implementation specific sendto and receive
537*9126SWyllys.Ingersoll@Sun.COM  * The code assumes that the buffer is in network byte order
538*9126SWyllys.Ingersoll@Sun.COM  */
539*9126SWyllys.Ingersoll@Sun.COM static int
540*9126SWyllys.Ingersoll@Sun.COM itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz)
541*9126SWyllys.Ingersoll@Sun.COM {
542*9126SWyllys.Ingersoll@Sun.COM 	int ret;
543*9126SWyllys.Ingersoll@Sun.COM 	uint32_t count;
544*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "itpm_command";
545*9126SWyllys.Ingersoll@Sun.COM 
546*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
547*9126SWyllys.Ingersoll@Sun.COM 
548*9126SWyllys.Ingersoll@Sun.COM 	/* The byte order is network byte order so convert it */
549*9126SWyllys.Ingersoll@Sun.COM 	count = load32(buf, TPM_PARAMSIZE_OFFSET);
550*9126SWyllys.Ingersoll@Sun.COM 
551*9126SWyllys.Ingersoll@Sun.COM 	if (count == 0) {
552*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: count=0, no data? %d", myname,
553*9126SWyllys.Ingersoll@Sun.COM 		    (int)bufsiz);
554*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
555*9126SWyllys.Ingersoll@Sun.COM 	}
556*9126SWyllys.Ingersoll@Sun.COM 	if (count > bufsiz) {
557*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: invalid count value:count:%d > bufsiz %d",
558*9126SWyllys.Ingersoll@Sun.COM 		    myname, (int)count, (int)bufsiz);
559*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
560*9126SWyllys.Ingersoll@Sun.COM 	}
561*9126SWyllys.Ingersoll@Sun.COM 
562*9126SWyllys.Ingersoll@Sun.COM 	/* Send the command */
563*9126SWyllys.Ingersoll@Sun.COM 	ret = tis_send_data(tpm, buf, count);
564*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
565*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_send_data failed with error %x",
566*9126SWyllys.Ingersoll@Sun.COM 		    myname, ret);
567*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
568*9126SWyllys.Ingersoll@Sun.COM 	}
569*9126SWyllys.Ingersoll@Sun.COM 
570*9126SWyllys.Ingersoll@Sun.COM 	/*
571*9126SWyllys.Ingersoll@Sun.COM 	 * Now receive the data from the tpm
572*9126SWyllys.Ingersoll@Sun.COM 	 * Should at least receive "the common" 10 bytes (TPM_HEADER_SIZE)
573*9126SWyllys.Ingersoll@Sun.COM 	 */
574*9126SWyllys.Ingersoll@Sun.COM 	ret = tis_recv_data(tpm, buf, bufsiz);
575*9126SWyllys.Ingersoll@Sun.COM 	if (ret < TPM_HEADER_SIZE) {
576*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_recv_data failed", myname);
577*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
578*9126SWyllys.Ingersoll@Sun.COM 	}
579*9126SWyllys.Ingersoll@Sun.COM 
580*9126SWyllys.Ingersoll@Sun.COM 	/* Check the return code */
581*9126SWyllys.Ingersoll@Sun.COM 	ret = load32(buf, TPM_RETURN_OFFSET);
582*9126SWyllys.Ingersoll@Sun.COM 	if (ret != TPM_SUCCESS) {
583*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: command failed with ret code: %x",
584*9126SWyllys.Ingersoll@Sun.COM 		    myname, ret);
585*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
586*9126SWyllys.Ingersoll@Sun.COM 	}
587*9126SWyllys.Ingersoll@Sun.COM 
588*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
589*9126SWyllys.Ingersoll@Sun.COM }
590*9126SWyllys.Ingersoll@Sun.COM 
591*9126SWyllys.Ingersoll@Sun.COM /*
592*9126SWyllys.Ingersoll@Sun.COM  * Whenever the driver wants to write to the DATA_IO register, it must need
593*9126SWyllys.Ingersoll@Sun.COM  * to figure out the burstcount.  This is the amount of bytes it can write
594*9126SWyllys.Ingersoll@Sun.COM  * before having to wait for long LPC bus cycle
595*9126SWyllys.Ingersoll@Sun.COM  *
596*9126SWyllys.Ingersoll@Sun.COM  * Returns: 0 if error, burst count if sucess
597*9126SWyllys.Ingersoll@Sun.COM  */
598*9126SWyllys.Ingersoll@Sun.COM static uint16_t
599*9126SWyllys.Ingersoll@Sun.COM tpm_get_burstcount(tpm_state_t *tpm) {
600*9126SWyllys.Ingersoll@Sun.COM 	clock_t stop;
601*9126SWyllys.Ingersoll@Sun.COM 	uint16_t burstcnt;
602*9126SWyllys.Ingersoll@Sun.COM 
603*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
604*9126SWyllys.Ingersoll@Sun.COM 
605*9126SWyllys.Ingersoll@Sun.COM 	/*
606*9126SWyllys.Ingersoll@Sun.COM 	 * Spec says timeout should be TIMEOUT_D
607*9126SWyllys.Ingersoll@Sun.COM 	 * burst count is TPM_STS bits 8..23
608*9126SWyllys.Ingersoll@Sun.COM 	 */
609*9126SWyllys.Ingersoll@Sun.COM 	stop = ddi_get_lbolt() + tpm->timeout_d;
610*9126SWyllys.Ingersoll@Sun.COM 	do {
611*9126SWyllys.Ingersoll@Sun.COM 		/*
612*9126SWyllys.Ingersoll@Sun.COM 		 * burstcnt is stored as a little endian value
613*9126SWyllys.Ingersoll@Sun.COM 		 * 'ntohs' doesn't work since the value is not word-aligned
614*9126SWyllys.Ingersoll@Sun.COM 		 */
615*9126SWyllys.Ingersoll@Sun.COM 		burstcnt = ddi_get8(tpm->handle,
616*9126SWyllys.Ingersoll@Sun.COM 		    (uint8_t *)(tpm->addr+
617*9126SWyllys.Ingersoll@Sun.COM 		    TPM_STS_(tpm->locality)+1));
618*9126SWyllys.Ingersoll@Sun.COM 		burstcnt += ddi_get8(tpm->handle,
619*9126SWyllys.Ingersoll@Sun.COM 		    (uint8_t *)(tpm->addr+
620*9126SWyllys.Ingersoll@Sun.COM 		    TPM_STS_(tpm->locality)+2)) << 8;
621*9126SWyllys.Ingersoll@Sun.COM 
622*9126SWyllys.Ingersoll@Sun.COM 		if (burstcnt)
623*9126SWyllys.Ingersoll@Sun.COM 			return (burstcnt);
624*9126SWyllys.Ingersoll@Sun.COM 
625*9126SWyllys.Ingersoll@Sun.COM 		delay(tpm->timeout_poll);
626*9126SWyllys.Ingersoll@Sun.COM 	} while (ddi_get_lbolt() < stop);
627*9126SWyllys.Ingersoll@Sun.COM 
628*9126SWyllys.Ingersoll@Sun.COM 	return (0);
629*9126SWyllys.Ingersoll@Sun.COM }
630*9126SWyllys.Ingersoll@Sun.COM 
631*9126SWyllys.Ingersoll@Sun.COM /*
632*9126SWyllys.Ingersoll@Sun.COM  * Writing 1 to TPM_STS_CMD_READY bit in TPM_STS will do the following:
633*9126SWyllys.Ingersoll@Sun.COM  * 1. The TPM will clears IO buffers if any
634*9126SWyllys.Ingersoll@Sun.COM  * 2. The TPM will enters either Idle or Ready state within TIMEOUT_B
635*9126SWyllys.Ingersoll@Sun.COM  * (checked in the calling function)
636*9126SWyllys.Ingersoll@Sun.COM  */
637*9126SWyllys.Ingersoll@Sun.COM static void
638*9126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm_state_t *tpm) {
639*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
640*9126SWyllys.Ingersoll@Sun.COM 
641*9126SWyllys.Ingersoll@Sun.COM 	ddi_put8(tpm->handle,
642*9126SWyllys.Ingersoll@Sun.COM 	    (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality)),
643*9126SWyllys.Ingersoll@Sun.COM 	    TPM_STS_CMD_READY);
644*9126SWyllys.Ingersoll@Sun.COM }
645*9126SWyllys.Ingersoll@Sun.COM 
646*9126SWyllys.Ingersoll@Sun.COM static int
647*9126SWyllys.Ingersoll@Sun.COM receive_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
648*9126SWyllys.Ingersoll@Sun.COM 	int size = 0;
649*9126SWyllys.Ingersoll@Sun.COM 	int retried = 0;
650*9126SWyllys.Ingersoll@Sun.COM 	uint8_t stsbits;
651*9126SWyllys.Ingersoll@Sun.COM 
652*9126SWyllys.Ingersoll@Sun.COM 	/* A number of consecutive bytes that can be written to TPM */
653*9126SWyllys.Ingersoll@Sun.COM 	uint16_t burstcnt;
654*9126SWyllys.Ingersoll@Sun.COM 
655*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
656*9126SWyllys.Ingersoll@Sun.COM retry:
657*9126SWyllys.Ingersoll@Sun.COM 	while (size < bufsiz &&
658*9126SWyllys.Ingersoll@Sun.COM 		(tpm_wait_for_stat(tpm,
659*9126SWyllys.Ingersoll@Sun.COM 		    (TPM_STS_DATA_AVAIL|TPM_STS_VALID),
660*9126SWyllys.Ingersoll@Sun.COM 		    (ddi_get_lbolt() + tpm->timeout_c)) == DDI_SUCCESS)) {
661*9126SWyllys.Ingersoll@Sun.COM 		/*
662*9126SWyllys.Ingersoll@Sun.COM 		 * Burstcount should be available within TIMEOUT_D
663*9126SWyllys.Ingersoll@Sun.COM 		 * after STS is set to valid
664*9126SWyllys.Ingersoll@Sun.COM 		 * burstcount is dynamic, so have to get it each time
665*9126SWyllys.Ingersoll@Sun.COM 		 */
666*9126SWyllys.Ingersoll@Sun.COM 		burstcnt = tpm_get_burstcount(tpm);
667*9126SWyllys.Ingersoll@Sun.COM 		for (; burstcnt > 0 && size < bufsiz; burstcnt--) {
668*9126SWyllys.Ingersoll@Sun.COM 			buf[size++] = ddi_get8(tpm->handle,
669*9126SWyllys.Ingersoll@Sun.COM 			    (uint8_t *)(tpm->addr +
670*9126SWyllys.Ingersoll@Sun.COM 			    TPM_DATA_FIFO_(tpm->locality)));
671*9126SWyllys.Ingersoll@Sun.COM 		}
672*9126SWyllys.Ingersoll@Sun.COM 	}
673*9126SWyllys.Ingersoll@Sun.COM 	stsbits = tis_get_status(tpm);
674*9126SWyllys.Ingersoll@Sun.COM 	/* check to see if we need to retry (just once) */
675*9126SWyllys.Ingersoll@Sun.COM 	if (size < bufsiz && !(stsbits & TPM_STS_DATA_AVAIL) && retried == 0) {
676*9126SWyllys.Ingersoll@Sun.COM 		/* issue responseRetry (TIS 1.2 pg 54) */
677*9126SWyllys.Ingersoll@Sun.COM 		ddi_put8(tpm->handle,
678*9126SWyllys.Ingersoll@Sun.COM 		    (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality)),
679*9126SWyllys.Ingersoll@Sun.COM 		    TPM_STS_RESPONSE_RETRY);
680*9126SWyllys.Ingersoll@Sun.COM 		/* update the retry counter so we only retry once */
681*9126SWyllys.Ingersoll@Sun.COM 		retried++;
682*9126SWyllys.Ingersoll@Sun.COM 		/* reset the size to 0 and reread the entire response */
683*9126SWyllys.Ingersoll@Sun.COM 		size = 0;
684*9126SWyllys.Ingersoll@Sun.COM 		goto retry;
685*9126SWyllys.Ingersoll@Sun.COM 	}
686*9126SWyllys.Ingersoll@Sun.COM 	return (size);
687*9126SWyllys.Ingersoll@Sun.COM }
688*9126SWyllys.Ingersoll@Sun.COM 
689*9126SWyllys.Ingersoll@Sun.COM /* Receive the data from the TPM */
690*9126SWyllys.Ingersoll@Sun.COM static int
691*9126SWyllys.Ingersoll@Sun.COM tis_recv_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
692*9126SWyllys.Ingersoll@Sun.COM 	int ret;
693*9126SWyllys.Ingersoll@Sun.COM 	int size = 0;
694*9126SWyllys.Ingersoll@Sun.COM 	uint32_t expected, status;
695*9126SWyllys.Ingersoll@Sun.COM 	uint32_t cmdresult;
696*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_recv_data";
697*9126SWyllys.Ingersoll@Sun.COM 
698*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
699*9126SWyllys.Ingersoll@Sun.COM 
700*9126SWyllys.Ingersoll@Sun.COM 	if (bufsiz < TPM_HEADER_SIZE) {
701*9126SWyllys.Ingersoll@Sun.COM 		/* There should be at least tag,paramsize,return code */
702*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: received data should contain at least "
703*9126SWyllys.Ingersoll@Sun.COM 		    "the header which is %d bytes long",
704*9126SWyllys.Ingersoll@Sun.COM 		    myname, TPM_HEADER_SIZE);
705*9126SWyllys.Ingersoll@Sun.COM 		goto OUT;
706*9126SWyllys.Ingersoll@Sun.COM 	}
707*9126SWyllys.Ingersoll@Sun.COM 
708*9126SWyllys.Ingersoll@Sun.COM 	/* Read tag(2 bytes), paramsize(4), and result(4) */
709*9126SWyllys.Ingersoll@Sun.COM 	size = receive_data(tpm, buf, TPM_HEADER_SIZE);
710*9126SWyllys.Ingersoll@Sun.COM 	if (size < TPM_HEADER_SIZE) {
711*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: getting the TPM_HEADER failed: size=%d",
712*9126SWyllys.Ingersoll@Sun.COM 		    myname, size);
713*9126SWyllys.Ingersoll@Sun.COM 		goto OUT;
714*9126SWyllys.Ingersoll@Sun.COM 	}
715*9126SWyllys.Ingersoll@Sun.COM 
716*9126SWyllys.Ingersoll@Sun.COM 	cmdresult = load32(buf, TPM_RETURN_OFFSET);
717*9126SWyllys.Ingersoll@Sun.COM 
718*9126SWyllys.Ingersoll@Sun.COM 	/* Get 'paramsize'(4 bytes)--it includes tag and paramsize */
719*9126SWyllys.Ingersoll@Sun.COM 	expected = load32(buf, TPM_PARAMSIZE_OFFSET);
720*9126SWyllys.Ingersoll@Sun.COM 	if (expected > bufsiz) {
721*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: paramSize is bigger "
722*9126SWyllys.Ingersoll@Sun.COM 		    "than the requested size: paramSize=%d bufsiz=%d result=%d",
723*9126SWyllys.Ingersoll@Sun.COM 		    myname, (int)expected, (int)bufsiz, cmdresult);
724*9126SWyllys.Ingersoll@Sun.COM 		goto OUT;
725*9126SWyllys.Ingersoll@Sun.COM 	}
726*9126SWyllys.Ingersoll@Sun.COM 
727*9126SWyllys.Ingersoll@Sun.COM 	/* Read in the rest of the data from the TPM */
728*9126SWyllys.Ingersoll@Sun.COM 	size += receive_data(tpm, (uint8_t *)&buf[TPM_HEADER_SIZE],
729*9126SWyllys.Ingersoll@Sun.COM 	    expected - TPM_HEADER_SIZE);
730*9126SWyllys.Ingersoll@Sun.COM 	if (size < expected) {
731*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: received data length=%d "
732*9126SWyllys.Ingersoll@Sun.COM 		    "is less than expected = %d", myname, size, expected);
733*9126SWyllys.Ingersoll@Sun.COM 		goto OUT;
734*9126SWyllys.Ingersoll@Sun.COM 	}
735*9126SWyllys.Ingersoll@Sun.COM 
736*9126SWyllys.Ingersoll@Sun.COM 	/* The TPM MUST set the state to stsValid within TIMEOUT_C */
737*9126SWyllys.Ingersoll@Sun.COM 	ret = tpm_wait_for_stat(tpm, TPM_STS_VALID,
738*9126SWyllys.Ingersoll@Sun.COM 	    ddi_get_lbolt() + tpm->timeout_c);
739*9126SWyllys.Ingersoll@Sun.COM 
740*9126SWyllys.Ingersoll@Sun.COM 	status = tis_get_status(tpm);
741*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
742*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: TPM didn't set stsValid after its I/O: "
743*9126SWyllys.Ingersoll@Sun.COM 		    "status = 0x%08X", myname, status);
744*9126SWyllys.Ingersoll@Sun.COM 		goto OUT;
745*9126SWyllys.Ingersoll@Sun.COM 	}
746*9126SWyllys.Ingersoll@Sun.COM 
747*9126SWyllys.Ingersoll@Sun.COM 	/* There is still more data? */
748*9126SWyllys.Ingersoll@Sun.COM 	if (status & TPM_STS_DATA_AVAIL) {
749*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: Status TPM_STS_DATA_AVAIL set:0x%08X",
750*9126SWyllys.Ingersoll@Sun.COM 		    myname, status);
751*9126SWyllys.Ingersoll@Sun.COM 		goto OUT;
752*9126SWyllys.Ingersoll@Sun.COM 	}
753*9126SWyllys.Ingersoll@Sun.COM 
754*9126SWyllys.Ingersoll@Sun.COM 	/*
755*9126SWyllys.Ingersoll@Sun.COM 	 * Release the control of the TPM after we are done with it
756*9126SWyllys.Ingersoll@Sun.COM 	 * it...so others can also get a chance to send data
757*9126SWyllys.Ingersoll@Sun.COM 	 */
758*9126SWyllys.Ingersoll@Sun.COM 	tis_release_locality(tpm, tpm->locality, 0);
759*9126SWyllys.Ingersoll@Sun.COM 
760*9126SWyllys.Ingersoll@Sun.COM OUT:
761*9126SWyllys.Ingersoll@Sun.COM 	tpm_set_ready(tpm);
762*9126SWyllys.Ingersoll@Sun.COM 	tis_release_locality(tpm, tpm->locality, 0);
763*9126SWyllys.Ingersoll@Sun.COM 	return (size);
764*9126SWyllys.Ingersoll@Sun.COM }
765*9126SWyllys.Ingersoll@Sun.COM 
766*9126SWyllys.Ingersoll@Sun.COM /*
767*9126SWyllys.Ingersoll@Sun.COM  * Send the data (TPM commands) to the Data IO register
768*9126SWyllys.Ingersoll@Sun.COM  */
769*9126SWyllys.Ingersoll@Sun.COM static int
770*9126SWyllys.Ingersoll@Sun.COM tis_send_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
771*9126SWyllys.Ingersoll@Sun.COM 	int ret;
772*9126SWyllys.Ingersoll@Sun.COM 	uint8_t status;
773*9126SWyllys.Ingersoll@Sun.COM 	uint16_t burstcnt;
774*9126SWyllys.Ingersoll@Sun.COM 	uint32_t ordinal;
775*9126SWyllys.Ingersoll@Sun.COM 	size_t count = 0;
776*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_send_data";
777*9126SWyllys.Ingersoll@Sun.COM 
778*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
779*9126SWyllys.Ingersoll@Sun.COM 
780*9126SWyllys.Ingersoll@Sun.COM 	if (bufsiz == 0) {
781*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: passed in argument bufsize is zero",
782*9126SWyllys.Ingersoll@Sun.COM 		    myname);
783*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
784*9126SWyllys.Ingersoll@Sun.COM 	}
785*9126SWyllys.Ingersoll@Sun.COM 
786*9126SWyllys.Ingersoll@Sun.COM 	/* Be in the right locality (aren't we always in locality 0?) */
787*9126SWyllys.Ingersoll@Sun.COM 	if (tis_request_locality(tpm, 0) != DDI_SUCCESS) {
788*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_request_locality didn't enter "
789*9126SWyllys.Ingersoll@Sun.COM 		    "locality 0", myname);
790*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
791*9126SWyllys.Ingersoll@Sun.COM 	}
792*9126SWyllys.Ingersoll@Sun.COM 
793*9126SWyllys.Ingersoll@Sun.COM 	/* Put the TPM in ready state */
794*9126SWyllys.Ingersoll@Sun.COM 	status = tis_get_status(tpm);
795*9126SWyllys.Ingersoll@Sun.COM 
796*9126SWyllys.Ingersoll@Sun.COM 	if (!(status & TPM_STS_CMD_READY)) {
797*9126SWyllys.Ingersoll@Sun.COM 		tpm_set_ready(tpm);
798*9126SWyllys.Ingersoll@Sun.COM 		ret = tpm_wait_for_stat(tpm,
799*9126SWyllys.Ingersoll@Sun.COM 		    TPM_STS_CMD_READY,
800*9126SWyllys.Ingersoll@Sun.COM 		    (ddi_get_lbolt() + tpm->timeout_b));
801*9126SWyllys.Ingersoll@Sun.COM 		if (ret != DDI_SUCCESS) {
802*9126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: could not put the TPM "
803*9126SWyllys.Ingersoll@Sun.COM 			    "in the command ready state:"
804*9126SWyllys.Ingersoll@Sun.COM 			    "tpm_wait_for_stat returned error",
805*9126SWyllys.Ingersoll@Sun.COM 			    myname);
806*9126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
807*9126SWyllys.Ingersoll@Sun.COM 		}
808*9126SWyllys.Ingersoll@Sun.COM 	}
809*9126SWyllys.Ingersoll@Sun.COM 
810*9126SWyllys.Ingersoll@Sun.COM 	/*
811*9126SWyllys.Ingersoll@Sun.COM 	 * Now we are ready to send command
812*9126SWyllys.Ingersoll@Sun.COM 	 * TPM's burstcount dictates how many bytes we can write at a time
813*9126SWyllys.Ingersoll@Sun.COM 	 * Burstcount is dynamic if INTF_CAPABILITY for static burstcount is
814*9126SWyllys.Ingersoll@Sun.COM 	 * not set.
815*9126SWyllys.Ingersoll@Sun.COM 	 */
816*9126SWyllys.Ingersoll@Sun.COM 	while (count < bufsiz - 1) {
817*9126SWyllys.Ingersoll@Sun.COM 		burstcnt = tpm_get_burstcount(tpm);
818*9126SWyllys.Ingersoll@Sun.COM 		if (burstcnt == 0) {
819*9126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: tpm_get_burstcnt returned error",
820*9126SWyllys.Ingersoll@Sun.COM 			    myname);
821*9126SWyllys.Ingersoll@Sun.COM 			ret = DDI_FAILURE;
822*9126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
823*9126SWyllys.Ingersoll@Sun.COM 		}
824*9126SWyllys.Ingersoll@Sun.COM 
825*9126SWyllys.Ingersoll@Sun.COM 		for (; burstcnt > 0 && count < bufsiz - 1; burstcnt--) {
826*9126SWyllys.Ingersoll@Sun.COM 			ddi_put8(tpm->handle, (uint8_t *)(tpm->addr+
827*9126SWyllys.Ingersoll@Sun.COM 			    TPM_DATA_FIFO_(tpm->locality)), buf[count]);
828*9126SWyllys.Ingersoll@Sun.COM 			count++;
829*9126SWyllys.Ingersoll@Sun.COM 		}
830*9126SWyllys.Ingersoll@Sun.COM 		/* Wait for TPM to indicate that it is ready for more data */
831*9126SWyllys.Ingersoll@Sun.COM 		ret = tpm_wait_for_stat(tpm,
832*9126SWyllys.Ingersoll@Sun.COM 		    (TPM_STS_VALID | TPM_STS_DATA_EXPECT),
833*9126SWyllys.Ingersoll@Sun.COM 		    (ddi_get_lbolt() + tpm->timeout_c));
834*9126SWyllys.Ingersoll@Sun.COM 		if (ret != DDI_SUCCESS) {
835*9126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: TPM didn't enter stsvalid "
836*9126SWyllys.Ingersoll@Sun.COM 			    "state after sending the data:", myname);
837*9126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
838*9126SWyllys.Ingersoll@Sun.COM 		}
839*9126SWyllys.Ingersoll@Sun.COM 	}
840*9126SWyllys.Ingersoll@Sun.COM 	/* We can't exit the loop above unless we wrote bufsiz-1 bytes */
841*9126SWyllys.Ingersoll@Sun.COM 
842*9126SWyllys.Ingersoll@Sun.COM 	/* Write last byte */
843*9126SWyllys.Ingersoll@Sun.COM 	ddi_put8(tpm->handle, (uint8_t *)(tpm->addr +
844*9126SWyllys.Ingersoll@Sun.COM 	    TPM_DATA_FIFO_(tpm->locality)), buf[count]);
845*9126SWyllys.Ingersoll@Sun.COM 	count++;
846*9126SWyllys.Ingersoll@Sun.COM 
847*9126SWyllys.Ingersoll@Sun.COM 	/* Wait for the TPM to enter Valid State */
848*9126SWyllys.Ingersoll@Sun.COM 	ret = tpm_wait_for_stat(tpm,
849*9126SWyllys.Ingersoll@Sun.COM 	    TPM_STS_VALID, (ddi_get_lbolt() + tpm->timeout_c));
850*9126SWyllys.Ingersoll@Sun.COM 	if (ret == DDI_FAILURE) {
851*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm didn't enter Valid state", myname);
852*9126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
853*9126SWyllys.Ingersoll@Sun.COM 	}
854*9126SWyllys.Ingersoll@Sun.COM 
855*9126SWyllys.Ingersoll@Sun.COM 	status = tis_get_status(tpm);
856*9126SWyllys.Ingersoll@Sun.COM 	/* The TPM should NOT be expecing more data at this point */
857*9126SWyllys.Ingersoll@Sun.COM 	if ((status & TPM_STS_DATA_EXPECT) != 0) {
858*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: DATA_EXPECT is set (shouldn't be) after "
859*9126SWyllys.Ingersoll@Sun.COM 		    "writing the last byte: status=0x%08X", myname, status);
860*9126SWyllys.Ingersoll@Sun.COM 		ret = DDI_FAILURE;
861*9126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
862*9126SWyllys.Ingersoll@Sun.COM 	}
863*9126SWyllys.Ingersoll@Sun.COM 
864*9126SWyllys.Ingersoll@Sun.COM 	/*
865*9126SWyllys.Ingersoll@Sun.COM 	 * Final step: Writing TPM_STS_GO to TPM_STS
866*9126SWyllys.Ingersoll@Sun.COM 	 * register will actually send the command.
867*9126SWyllys.Ingersoll@Sun.COM 	 */
868*9126SWyllys.Ingersoll@Sun.COM 	ddi_put8(tpm->handle, (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality)),
869*9126SWyllys.Ingersoll@Sun.COM 	    TPM_STS_GO);
870*9126SWyllys.Ingersoll@Sun.COM 
871*9126SWyllys.Ingersoll@Sun.COM 	/* Ordinal/Command_code is located in buf[6..9] */
872*9126SWyllys.Ingersoll@Sun.COM 	ordinal = load32(buf, TPM_COMMAND_CODE_OFFSET);
873*9126SWyllys.Ingersoll@Sun.COM 
874*9126SWyllys.Ingersoll@Sun.COM 	ret = tpm_wait_for_stat(tpm, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
875*9126SWyllys.Ingersoll@Sun.COM 	    ddi_get_lbolt() + tpm_get_ordinal_duration(tpm, ordinal));
876*9126SWyllys.Ingersoll@Sun.COM 	if (ret == DDI_FAILURE) {
877*9126SWyllys.Ingersoll@Sun.COM 		status = tis_get_status(tpm);
878*9126SWyllys.Ingersoll@Sun.COM 		if (!(status & TPM_STS_DATA_AVAIL) ||
879*9126SWyllys.Ingersoll@Sun.COM 		    !(status & TPM_STS_VALID)) {
880*9126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: TPM not ready or valid "
881*9126SWyllys.Ingersoll@Sun.COM 			    "(ordinal = %d timeout = %ld)",
882*9126SWyllys.Ingersoll@Sun.COM 			    myname, ordinal,
883*9126SWyllys.Ingersoll@Sun.COM 			    tpm_get_ordinal_duration(tpm, ordinal));
884*9126SWyllys.Ingersoll@Sun.COM 		} else {
885*9126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: tpm_wait_for_stat "
886*9126SWyllys.Ingersoll@Sun.COM 			    "(DATA_AVAIL | VALID) failed: STS = 0x%0X",
887*9126SWyllys.Ingersoll@Sun.COM 			    myname, status);
888*9126SWyllys.Ingersoll@Sun.COM 		}
889*9126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
890*9126SWyllys.Ingersoll@Sun.COM 	}
891*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
892*9126SWyllys.Ingersoll@Sun.COM 
893*9126SWyllys.Ingersoll@Sun.COM FAIL:
894*9126SWyllys.Ingersoll@Sun.COM 	tpm_set_ready(tpm);
895*9126SWyllys.Ingersoll@Sun.COM 	tis_release_locality(tpm, tpm->locality, 0);
896*9126SWyllys.Ingersoll@Sun.COM 	return (ret);
897*9126SWyllys.Ingersoll@Sun.COM }
898*9126SWyllys.Ingersoll@Sun.COM 
899*9126SWyllys.Ingersoll@Sun.COM /*
900*9126SWyllys.Ingersoll@Sun.COM  * Clear XrequestUse and Xactivelocality, where X is the current locality
901*9126SWyllys.Ingersoll@Sun.COM  */
902*9126SWyllys.Ingersoll@Sun.COM static void
903*9126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm_state_t *tpm, char locality, int force) {
904*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
905*9126SWyllys.Ingersoll@Sun.COM 
906*9126SWyllys.Ingersoll@Sun.COM 	if (force ||
907*9126SWyllys.Ingersoll@Sun.COM 	    (ddi_get8(tpm->handle,
908*9126SWyllys.Ingersoll@Sun.COM 		(uchar_t *)(tpm->addr+TPM_ACCESS_(locality)))
909*9126SWyllys.Ingersoll@Sun.COM 	    & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID))
910*9126SWyllys.Ingersoll@Sun.COM 	    == (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
911*9126SWyllys.Ingersoll@Sun.COM 		/*
912*9126SWyllys.Ingersoll@Sun.COM 		 * Writing 1 to active locality bit in TPM_ACCESS
913*9126SWyllys.Ingersoll@Sun.COM 		 * register reliquishes the control of the locality
914*9126SWyllys.Ingersoll@Sun.COM 		 */
915*9126SWyllys.Ingersoll@Sun.COM 		ddi_put8(tpm->handle,
916*9126SWyllys.Ingersoll@Sun.COM 		    (uint8_t *)(tpm->addr+TPM_ACCESS_(locality)),
917*9126SWyllys.Ingersoll@Sun.COM 		    TPM_ACCESS_ACTIVE_LOCALITY);
918*9126SWyllys.Ingersoll@Sun.COM 	}
919*9126SWyllys.Ingersoll@Sun.COM }
920*9126SWyllys.Ingersoll@Sun.COM 
921*9126SWyllys.Ingersoll@Sun.COM /*
922*9126SWyllys.Ingersoll@Sun.COM  * Checks whether the given locality is active
923*9126SWyllys.Ingersoll@Sun.COM  * Use TPM_ACCESS register and the masks TPM_ACCESS_VALID,TPM_ACTIVE_LOCALITY
924*9126SWyllys.Ingersoll@Sun.COM  */
925*9126SWyllys.Ingersoll@Sun.COM static int
926*9126SWyllys.Ingersoll@Sun.COM tis_check_active_locality(tpm_state_t *tpm, char locality) {
927*9126SWyllys.Ingersoll@Sun.COM 	uint8_t access_bits;
928*9126SWyllys.Ingersoll@Sun.COM 
929*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
930*9126SWyllys.Ingersoll@Sun.COM 
931*9126SWyllys.Ingersoll@Sun.COM 	access_bits = ddi_get8(tpm->handle,
932*9126SWyllys.Ingersoll@Sun.COM 	    (uint8_t *)(tpm->addr+TPM_ACCESS_(locality)));
933*9126SWyllys.Ingersoll@Sun.COM 	access_bits &= (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID);
934*9126SWyllys.Ingersoll@Sun.COM 
935*9126SWyllys.Ingersoll@Sun.COM 	if (access_bits == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
936*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_SUCCESS);
937*9126SWyllys.Ingersoll@Sun.COM 	else
938*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
939*9126SWyllys.Ingersoll@Sun.COM }
940*9126SWyllys.Ingersoll@Sun.COM 
941*9126SWyllys.Ingersoll@Sun.COM /* Request the TPM to be in the given locality */
942*9126SWyllys.Ingersoll@Sun.COM static int
943*9126SWyllys.Ingersoll@Sun.COM tis_request_locality(tpm_state_t *tpm, char locality) {
944*9126SWyllys.Ingersoll@Sun.COM 	clock_t timeout;
945*9126SWyllys.Ingersoll@Sun.COM 	int ret;
946*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_request_locality";
947*9126SWyllys.Ingersoll@Sun.COM 
948*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
949*9126SWyllys.Ingersoll@Sun.COM 
950*9126SWyllys.Ingersoll@Sun.COM 	ret = tis_check_active_locality(tpm, locality);
951*9126SWyllys.Ingersoll@Sun.COM 
952*9126SWyllys.Ingersoll@Sun.COM 	if (ret == DDI_SUCCESS) {
953*9126SWyllys.Ingersoll@Sun.COM 		/* Locality is already active */
954*9126SWyllys.Ingersoll@Sun.COM 		tpm->locality = locality;
955*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_SUCCESS);
956*9126SWyllys.Ingersoll@Sun.COM 	}
957*9126SWyllys.Ingersoll@Sun.COM 
958*9126SWyllys.Ingersoll@Sun.COM 	ddi_put8(tpm->handle, tpm->addr+TPM_ACCESS_(locality),
959*9126SWyllys.Ingersoll@Sun.COM 	    TPM_ACCESS_REQUEST_USE);
960*9126SWyllys.Ingersoll@Sun.COM 	timeout = ddi_get_lbolt() + tpm->timeout_a;
961*9126SWyllys.Ingersoll@Sun.COM 
962*9126SWyllys.Ingersoll@Sun.COM 	/* Using polling */
963*9126SWyllys.Ingersoll@Sun.COM 	while (tis_check_active_locality(tpm, locality)
964*9126SWyllys.Ingersoll@Sun.COM 		!= DDI_SUCCESS) {
965*9126SWyllys.Ingersoll@Sun.COM 		if (ddi_get_lbolt() >= timeout) {
966*9126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s (interrupt-disabled) "
967*9126SWyllys.Ingersoll@Sun.COM 			    "tis_request_locality timed out",
968*9126SWyllys.Ingersoll@Sun.COM 			    myname);
969*9126SWyllys.Ingersoll@Sun.COM 			return (DDI_FAILURE);
970*9126SWyllys.Ingersoll@Sun.COM 		}
971*9126SWyllys.Ingersoll@Sun.COM 		delay(tpm->timeout_poll);
972*9126SWyllys.Ingersoll@Sun.COM 	}
973*9126SWyllys.Ingersoll@Sun.COM 
974*9126SWyllys.Ingersoll@Sun.COM 	tpm->locality = locality;
975*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
976*9126SWyllys.Ingersoll@Sun.COM }
977*9126SWyllys.Ingersoll@Sun.COM 
978*9126SWyllys.Ingersoll@Sun.COM /* Read the status register */
979*9126SWyllys.Ingersoll@Sun.COM static uint8_t
980*9126SWyllys.Ingersoll@Sun.COM tis_get_status(tpm_state_t *tpm) {
981*9126SWyllys.Ingersoll@Sun.COM 	return (ddi_get8(tpm->handle,
982*9126SWyllys.Ingersoll@Sun.COM 	    (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality))));
983*9126SWyllys.Ingersoll@Sun.COM }
984*9126SWyllys.Ingersoll@Sun.COM 
985*9126SWyllys.Ingersoll@Sun.COM static int
986*9126SWyllys.Ingersoll@Sun.COM tpm_wait_for_stat(tpm_state_t *tpm, uint8_t mask, clock_t absolute_timeout) {
987*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_wait_for_stat";
988*9126SWyllys.Ingersoll@Sun.COM 
989*9126SWyllys.Ingersoll@Sun.COM 	/* Using polling */
990*9126SWyllys.Ingersoll@Sun.COM 	while ((tis_get_status(tpm) & mask) != mask) {
991*9126SWyllys.Ingersoll@Sun.COM 		if (ddi_get_lbolt() >= absolute_timeout) {
992*9126SWyllys.Ingersoll@Sun.COM 			/* Timeout reached */
993*9126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: using "
994*9126SWyllys.Ingersoll@Sun.COM 			    "polling:reached timeout",
995*9126SWyllys.Ingersoll@Sun.COM 			    myname);
996*9126SWyllys.Ingersoll@Sun.COM 			return (DDI_FAILURE);
997*9126SWyllys.Ingersoll@Sun.COM 		}
998*9126SWyllys.Ingersoll@Sun.COM 		delay(tpm->timeout_poll);
999*9126SWyllys.Ingersoll@Sun.COM 	}
1000*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
1001*9126SWyllys.Ingersoll@Sun.COM }
1002*9126SWyllys.Ingersoll@Sun.COM 
1003*9126SWyllys.Ingersoll@Sun.COM /*
1004*9126SWyllys.Ingersoll@Sun.COM  * Initialize TPM device
1005*9126SWyllys.Ingersoll@Sun.COM  * 1. Find out supported interrupt capabilities
1006*9126SWyllys.Ingersoll@Sun.COM  * 2. Set up interrupt handler if supported (some BIOSes don't support
1007*9126SWyllys.Ingersoll@Sun.COM  * interrupts for TPMS, in which case we set up polling)
1008*9126SWyllys.Ingersoll@Sun.COM  * 3. Determine timeouts and commands duration
1009*9126SWyllys.Ingersoll@Sun.COM  */
1010*9126SWyllys.Ingersoll@Sun.COM static int
1011*9126SWyllys.Ingersoll@Sun.COM tis_init(tpm_state_t *tpm) {
1012*9126SWyllys.Ingersoll@Sun.COM 	uint32_t intf_caps;
1013*9126SWyllys.Ingersoll@Sun.COM 	int ret;
1014*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_init";
1015*9126SWyllys.Ingersoll@Sun.COM 	uintptr_t aptr = (uintptr_t)tpm->addr;
1016*9126SWyllys.Ingersoll@Sun.COM 
1017*9126SWyllys.Ingersoll@Sun.COM 	/*
1018*9126SWyllys.Ingersoll@Sun.COM 	 * Temporarily set up timeouts before we get the real timeouts
1019*9126SWyllys.Ingersoll@Sun.COM 	 * by issuing TPM_CAP commands (but to issue TPM_CAP commands,
1020*9126SWyllys.Ingersoll@Sun.COM 	 * you need TIMEOUTs defined...chicken and egg problem here.
1021*9126SWyllys.Ingersoll@Sun.COM 	 * TPM timeouts: Convert the milliseconds to clock cycles
1022*9126SWyllys.Ingersoll@Sun.COM 	 */
1023*9126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_a = drv_usectohz(TIS_TIMEOUT_A);
1024*9126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_b = drv_usectohz(TIS_TIMEOUT_B);
1025*9126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_c = drv_usectohz(TIS_TIMEOUT_C);
1026*9126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_d = drv_usectohz(TIS_TIMEOUT_D);
1027*9126SWyllys.Ingersoll@Sun.COM 	/*
1028*9126SWyllys.Ingersoll@Sun.COM 	 * Do the same with the duration (real duration will be filled out
1029*9126SWyllys.Ingersoll@Sun.COM 	 * when we call TPM_GetCapability to get the duration values from
1030*9126SWyllys.Ingersoll@Sun.COM 	 * the TPM itself).
1031*9126SWyllys.Ingersoll@Sun.COM 	 */
1032*9126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_SHORT] = drv_usectohz(TPM_DEFAULT_DURATION);
1033*9126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_MEDIUM] = drv_usectohz(TPM_DEFAULT_DURATION);
1034*9126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_LONG] = drv_usectohz(TPM_DEFAULT_DURATION);
1035*9126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_UNDEFINED] = drv_usectohz(TPM_DEFAULT_DURATION);
1036*9126SWyllys.Ingersoll@Sun.COM 
1037*9126SWyllys.Ingersoll@Sun.COM 	/* Find out supported capabilities */
1038*9126SWyllys.Ingersoll@Sun.COM 	intf_caps = ddi_get32(tpm->handle,
1039*9126SWyllys.Ingersoll@Sun.COM 	    (uint32_t *)(aptr + TPM_INTF_CAP_(0)));
1040*9126SWyllys.Ingersoll@Sun.COM 
1041*9126SWyllys.Ingersoll@Sun.COM 	/* Upper 3 bytes should always return 0 */
1042*9126SWyllys.Ingersoll@Sun.COM 	if (intf_caps & 0x7FFFFF00) {
1043*9126SWyllys.Ingersoll@Sun.COM #ifdef DEBUG
1044*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: bad intf_caps value 0x%0X",
1045*9126SWyllys.Ingersoll@Sun.COM 		    myname, intf_caps);
1046*9126SWyllys.Ingersoll@Sun.COM #endif
1047*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1048*9126SWyllys.Ingersoll@Sun.COM 	}
1049*9126SWyllys.Ingersoll@Sun.COM 
1050*9126SWyllys.Ingersoll@Sun.COM 	/* These two interrupts are mandatory */
1051*9126SWyllys.Ingersoll@Sun.COM 	if (!(intf_caps & TPM_INTF_INT_LOCALITY_CHANGE_INT)) {
1052*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: Mandatory capability Locality Change Int "
1053*9126SWyllys.Ingersoll@Sun.COM 		    "not supported", myname);
1054*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1055*9126SWyllys.Ingersoll@Sun.COM 	}
1056*9126SWyllys.Ingersoll@Sun.COM 	if (!(intf_caps & TPM_INTF_INT_DATA_AVAIL_INT)) {
1057*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: Mandatory capability Data Available Int "
1058*9126SWyllys.Ingersoll@Sun.COM 		    "not supported", myname);
1059*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1060*9126SWyllys.Ingersoll@Sun.COM 	}
1061*9126SWyllys.Ingersoll@Sun.COM 
1062*9126SWyllys.Ingersoll@Sun.COM 	/*
1063*9126SWyllys.Ingersoll@Sun.COM 	 * Before we start writing anything to TPM's registers,
1064*9126SWyllys.Ingersoll@Sun.COM 	 * make sure we are in locality 0
1065*9126SWyllys.Ingersoll@Sun.COM 	 */
1066*9126SWyllys.Ingersoll@Sun.COM 	ret = tis_request_locality(tpm, 0);
1067*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1068*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: Unable to request locality 0", myname);
1069*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1070*9126SWyllys.Ingersoll@Sun.COM 	} /* Now we can refer to the locality as tpm->locality */
1071*9126SWyllys.Ingersoll@Sun.COM 
1072*9126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_poll = drv_usectohz(TPM_POLLING_TIMEOUT);
1073*9126SWyllys.Ingersoll@Sun.COM 	tpm->intr_enabled = 0;
1074*9126SWyllys.Ingersoll@Sun.COM 
1075*9126SWyllys.Ingersoll@Sun.COM 	/* Get the real timeouts from the TPM */
1076*9126SWyllys.Ingersoll@Sun.COM 	ret = tpm_get_timeouts(tpm);
1077*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1078*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm_get_timeouts error", myname);
1079*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1080*9126SWyllys.Ingersoll@Sun.COM 	}
1081*9126SWyllys.Ingersoll@Sun.COM 
1082*9126SWyllys.Ingersoll@Sun.COM 	ret = tpm_get_duration(tpm);
1083*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1084*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm_get_duration error", myname);
1085*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1086*9126SWyllys.Ingersoll@Sun.COM 	}
1087*9126SWyllys.Ingersoll@Sun.COM 
1088*9126SWyllys.Ingersoll@Sun.COM 	/* This gets the TPM version information */
1089*9126SWyllys.Ingersoll@Sun.COM 	ret = tpm_get_version(tpm);
1090*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1091*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm_get_version error", myname);
1092*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1093*9126SWyllys.Ingersoll@Sun.COM 	}
1094*9126SWyllys.Ingersoll@Sun.COM 
1095*9126SWyllys.Ingersoll@Sun.COM 	/*
1096*9126SWyllys.Ingersoll@Sun.COM 	 * Unless the TPM completes the test of its commands,
1097*9126SWyllys.Ingersoll@Sun.COM 	 * it can return an error when the untested commands are called
1098*9126SWyllys.Ingersoll@Sun.COM 	 */
1099*9126SWyllys.Ingersoll@Sun.COM 	ret = tpm_continue_selftest(tpm);
1100*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1101*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm_continue_selftest error", myname);
1102*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1103*9126SWyllys.Ingersoll@Sun.COM 	}
1104*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
1105*9126SWyllys.Ingersoll@Sun.COM }
1106*9126SWyllys.Ingersoll@Sun.COM 
1107*9126SWyllys.Ingersoll@Sun.COM /*
1108*9126SWyllys.Ingersoll@Sun.COM  * Module Entry points
1109*9126SWyllys.Ingersoll@Sun.COM  */
1110*9126SWyllys.Ingersoll@Sun.COM int
1111*9126SWyllys.Ingersoll@Sun.COM _init(void)
1112*9126SWyllys.Ingersoll@Sun.COM {
1113*9126SWyllys.Ingersoll@Sun.COM 	int ret;
1114*9126SWyllys.Ingersoll@Sun.COM 
1115*9126SWyllys.Ingersoll@Sun.COM 	ret = ddi_soft_state_init(&statep, sizeof (tpm_state_t), 1);
1116*9126SWyllys.Ingersoll@Sun.COM 	if (ret)
1117*9126SWyllys.Ingersoll@Sun.COM 		return (ret);
1118*9126SWyllys.Ingersoll@Sun.COM 
1119*9126SWyllys.Ingersoll@Sun.COM 	ret = mod_install(&tpm_ml);
1120*9126SWyllys.Ingersoll@Sun.COM 	if (ret != 0) {
1121*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "_init: mod_install returned non-zero");
1122*9126SWyllys.Ingersoll@Sun.COM 		ddi_soft_state_fini(&statep);
1123*9126SWyllys.Ingersoll@Sun.COM 		return (ret);
1124*9126SWyllys.Ingersoll@Sun.COM 	}
1125*9126SWyllys.Ingersoll@Sun.COM 
1126*9126SWyllys.Ingersoll@Sun.COM 	return (ret);
1127*9126SWyllys.Ingersoll@Sun.COM }
1128*9126SWyllys.Ingersoll@Sun.COM 
1129*9126SWyllys.Ingersoll@Sun.COM int
1130*9126SWyllys.Ingersoll@Sun.COM _info(struct modinfo *modinfop)
1131*9126SWyllys.Ingersoll@Sun.COM {
1132*9126SWyllys.Ingersoll@Sun.COM 	int ret;
1133*9126SWyllys.Ingersoll@Sun.COM 	ret = mod_info(&tpm_ml, modinfop);
1134*9126SWyllys.Ingersoll@Sun.COM 	if (ret == 0)
1135*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "mod_info failed: %d", ret);
1136*9126SWyllys.Ingersoll@Sun.COM 
1137*9126SWyllys.Ingersoll@Sun.COM 	return (ret);
1138*9126SWyllys.Ingersoll@Sun.COM }
1139*9126SWyllys.Ingersoll@Sun.COM 
1140*9126SWyllys.Ingersoll@Sun.COM int
1141*9126SWyllys.Ingersoll@Sun.COM _fini()
1142*9126SWyllys.Ingersoll@Sun.COM {
1143*9126SWyllys.Ingersoll@Sun.COM 	int ret;
1144*9126SWyllys.Ingersoll@Sun.COM 	ret = mod_remove(&tpm_ml);
1145*9126SWyllys.Ingersoll@Sun.COM 	if (ret != 0) {
1146*9126SWyllys.Ingersoll@Sun.COM 		return (ret);
1147*9126SWyllys.Ingersoll@Sun.COM 	}
1148*9126SWyllys.Ingersoll@Sun.COM 	ddi_soft_state_fini(&statep);
1149*9126SWyllys.Ingersoll@Sun.COM 
1150*9126SWyllys.Ingersoll@Sun.COM 	return (ret);
1151*9126SWyllys.Ingersoll@Sun.COM }
1152*9126SWyllys.Ingersoll@Sun.COM /* End of driver configuration functions */
1153*9126SWyllys.Ingersoll@Sun.COM 
1154*9126SWyllys.Ingersoll@Sun.COM static int
1155*9126SWyllys.Ingersoll@Sun.COM tpm_resume(tpm_state_t *tpm)
1156*9126SWyllys.Ingersoll@Sun.COM {
1157*9126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
1158*9126SWyllys.Ingersoll@Sun.COM 	if (!tpm->suspended) {
1159*9126SWyllys.Ingersoll@Sun.COM 		mutex_exit(&tpm->pm_mutex);
1160*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1161*9126SWyllys.Ingersoll@Sun.COM 	}
1162*9126SWyllys.Ingersoll@Sun.COM 	tpm->suspended = 0;
1163*9126SWyllys.Ingersoll@Sun.COM 	cv_broadcast(&tpm->suspend_cv);
1164*9126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
1165*9126SWyllys.Ingersoll@Sun.COM 
1166*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
1167*9126SWyllys.Ingersoll@Sun.COM }
1168*9126SWyllys.Ingersoll@Sun.COM 
1169*9126SWyllys.Ingersoll@Sun.COM /*
1170*9126SWyllys.Ingersoll@Sun.COM  * Sun DDI/DDK entry points
1171*9126SWyllys.Ingersoll@Sun.COM  */
1172*9126SWyllys.Ingersoll@Sun.COM static int
1173*9126SWyllys.Ingersoll@Sun.COM tpm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1174*9126SWyllys.Ingersoll@Sun.COM {
1175*9126SWyllys.Ingersoll@Sun.COM 	int ret, idx;
1176*9126SWyllys.Ingersoll@Sun.COM 	int instance;
1177*9126SWyllys.Ingersoll@Sun.COM 	int nregs;
1178*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_attach";
1179*9126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm = NULL;
1180*9126SWyllys.Ingersoll@Sun.COM 
1181*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(dip != NULL);
1182*9126SWyllys.Ingersoll@Sun.COM 
1183*9126SWyllys.Ingersoll@Sun.COM 	instance = ddi_get_instance(dip);
1184*9126SWyllys.Ingersoll@Sun.COM 
1185*9126SWyllys.Ingersoll@Sun.COM 	/* Nothing out of ordinary here */
1186*9126SWyllys.Ingersoll@Sun.COM 	switch (cmd) {
1187*9126SWyllys.Ingersoll@Sun.COM 	case DDI_ATTACH:
1188*9126SWyllys.Ingersoll@Sun.COM 		ret = ddi_soft_state_zalloc(statep, instance);
1189*9126SWyllys.Ingersoll@Sun.COM 		if (ret != DDI_SUCCESS) {
1190*9126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s could not allocate tpm_state_t",
1191*9126SWyllys.Ingersoll@Sun.COM 			    myname);
1192*9126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
1193*9126SWyllys.Ingersoll@Sun.COM 		}
1194*9126SWyllys.Ingersoll@Sun.COM 		tpm = ddi_get_soft_state(statep, instance);
1195*9126SWyllys.Ingersoll@Sun.COM 		tpm->dip = dip;
1196*9126SWyllys.Ingersoll@Sun.COM 		break;
1197*9126SWyllys.Ingersoll@Sun.COM 	case DDI_RESUME:
1198*9126SWyllys.Ingersoll@Sun.COM 		tpm = ddi_get_soft_state(statep, instance);
1199*9126SWyllys.Ingersoll@Sun.COM 		if (tpm == NULL) {
1200*9126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: tpm_state_t is NULL",
1201*9126SWyllys.Ingersoll@Sun.COM 			    myname);
1202*9126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
1203*9126SWyllys.Ingersoll@Sun.COM 		}
1204*9126SWyllys.Ingersoll@Sun.COM 		return (tpm_resume(tpm));
1205*9126SWyllys.Ingersoll@Sun.COM 	default:
1206*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: cmd %d is not implemented", myname, cmd);
1207*9126SWyllys.Ingersoll@Sun.COM 		ret = DDI_FAILURE;
1208*9126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
1209*9126SWyllys.Ingersoll@Sun.COM 	}
1210*9126SWyllys.Ingersoll@Sun.COM 
1211*9126SWyllys.Ingersoll@Sun.COM 	/* Zeroize the flag, which is used to keep track of what is allocated */
1212*9126SWyllys.Ingersoll@Sun.COM 	tpm->flags = 0;
1213*9126SWyllys.Ingersoll@Sun.COM 
1214*9126SWyllys.Ingersoll@Sun.COM 	tpm->accattr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1215*9126SWyllys.Ingersoll@Sun.COM 	tpm->accattr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
1216*9126SWyllys.Ingersoll@Sun.COM 	tpm->accattr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1217*9126SWyllys.Ingersoll@Sun.COM 
1218*9126SWyllys.Ingersoll@Sun.COM 	idx = 0;
1219*9126SWyllys.Ingersoll@Sun.COM 	ret = ddi_dev_nregs(tpm->dip, &nregs);
1220*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS)
1221*9126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
1222*9126SWyllys.Ingersoll@Sun.COM 
1223*9126SWyllys.Ingersoll@Sun.COM #ifdef DEBUG
1224*9126SWyllys.Ingersoll@Sun.COM 	cmn_err(CE_NOTE, "num registers = %d", nregs);
1225*9126SWyllys.Ingersoll@Sun.COM #endif
1226*9126SWyllys.Ingersoll@Sun.COM 	/*
1227*9126SWyllys.Ingersoll@Sun.COM 	 * TPM vendors put the TPM registers in different
1228*9126SWyllys.Ingersoll@Sun.COM 	 * slots in their register lists.  They are not always
1229*9126SWyllys.Ingersoll@Sun.COM 	 * the 1st set of registers, for instance.
1230*9126SWyllys.Ingersoll@Sun.COM 	 * Loop until we find the set that matches the expected
1231*9126SWyllys.Ingersoll@Sun.COM 	 * register size (0x5000).
1232*9126SWyllys.Ingersoll@Sun.COM 	 */
1233*9126SWyllys.Ingersoll@Sun.COM 	for (idx = 0; idx < nregs; idx++) {
1234*9126SWyllys.Ingersoll@Sun.COM 		off_t regsize;
1235*9126SWyllys.Ingersoll@Sun.COM 
1236*9126SWyllys.Ingersoll@Sun.COM 		if ((ret = ddi_dev_regsize(tpm->dip, idx, &regsize)) !=
1237*9126SWyllys.Ingersoll@Sun.COM 		    DDI_SUCCESS)
1238*9126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
1239*9126SWyllys.Ingersoll@Sun.COM #ifdef DEBUG
1240*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_NOTE, "register set #%d size = 0x%0lX", idx,
1241*9126SWyllys.Ingersoll@Sun.COM 		    regsize);
1242*9126SWyllys.Ingersoll@Sun.COM #endif
1243*9126SWyllys.Ingersoll@Sun.COM 		/* The TIS spec says the TPM registers must be 0x5000 bytes */
1244*9126SWyllys.Ingersoll@Sun.COM 		if (regsize == 0x5000)
1245*9126SWyllys.Ingersoll@Sun.COM 			break;
1246*9126SWyllys.Ingersoll@Sun.COM 	}
1247*9126SWyllys.Ingersoll@Sun.COM 	ret = ddi_regs_map_setup(tpm->dip, idx, (caddr_t *)&tpm->addr,
1248*9126SWyllys.Ingersoll@Sun.COM 	    (offset_t)0, (offset_t)0x5000,
1249*9126SWyllys.Ingersoll@Sun.COM 	    &tpm->accattr, &tpm->handle);
1250*9126SWyllys.Ingersoll@Sun.COM 
1251*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1252*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN,
1253*9126SWyllys.Ingersoll@Sun.COM 		    "%s:ddi_regs_map_setup failed ret: %d",
1254*9126SWyllys.Ingersoll@Sun.COM 		    myname, ret);
1255*9126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
1256*9126SWyllys.Ingersoll@Sun.COM 	}
1257*9126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DIDREGSMAP;
1258*9126SWyllys.Ingersoll@Sun.COM 
1259*9126SWyllys.Ingersoll@Sun.COM 	/* Enable TPM device according to the TIS specification */
1260*9126SWyllys.Ingersoll@Sun.COM 	ret = tis_init(tpm);
1261*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1262*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_init() failed ret: %d",
1263*9126SWyllys.Ingersoll@Sun.COM 		    myname, ret);
1264*9126SWyllys.Ingersoll@Sun.COM 
1265*9126SWyllys.Ingersoll@Sun.COM 		/* We need to clean up the ddi_regs_map_setup call */
1266*9126SWyllys.Ingersoll@Sun.COM 		ddi_regs_map_free(&tpm->handle);
1267*9126SWyllys.Ingersoll@Sun.COM 		tpm->handle = NULL;
1268*9126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~TPM_DIDREGSMAP;
1269*9126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
1270*9126SWyllys.Ingersoll@Sun.COM 	}
1271*9126SWyllys.Ingersoll@Sun.COM 
1272*9126SWyllys.Ingersoll@Sun.COM 	/* Initialize the inter-process lock */
1273*9126SWyllys.Ingersoll@Sun.COM 	mutex_init(&tpm->dev_lock, NULL, MUTEX_DRIVER, NULL);
1274*9126SWyllys.Ingersoll@Sun.COM 	mutex_init(&tpm->pm_mutex, NULL, MUTEX_DRIVER, NULL);
1275*9126SWyllys.Ingersoll@Sun.COM 	cv_init(&tpm->suspend_cv, NULL, CV_DRIVER, NULL);
1276*9126SWyllys.Ingersoll@Sun.COM 
1277*9126SWyllys.Ingersoll@Sun.COM 	/* Set the suspend/resume property */
1278*9126SWyllys.Ingersoll@Sun.COM 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
1279*9126SWyllys.Ingersoll@Sun.COM 	    "pm-hardware-state", "needs-suspend-resume");
1280*9126SWyllys.Ingersoll@Sun.COM 
1281*9126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
1282*9126SWyllys.Ingersoll@Sun.COM 	tpm->suspended = 0;
1283*9126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
1284*9126SWyllys.Ingersoll@Sun.COM 
1285*9126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_MUTEX;
1286*9126SWyllys.Ingersoll@Sun.COM 
1287*9126SWyllys.Ingersoll@Sun.COM 	/* Initialize the buffer and the lock associated with it */
1288*9126SWyllys.Ingersoll@Sun.COM 	tpm->bufsize = TPM_IO_BUF_SIZE;
1289*9126SWyllys.Ingersoll@Sun.COM 	tpm->iobuf = kmem_zalloc((sizeof (uint8_t))*(tpm->bufsize), KM_SLEEP);
1290*9126SWyllys.Ingersoll@Sun.COM 	if (tpm->iobuf == NULL) {
1291*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: failed to allocate iobuf", myname);
1292*9126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
1293*9126SWyllys.Ingersoll@Sun.COM 	}
1294*9126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_IO_ALLOC;
1295*9126SWyllys.Ingersoll@Sun.COM 
1296*9126SWyllys.Ingersoll@Sun.COM 	mutex_init(&tpm->iobuf_lock, NULL, MUTEX_DRIVER, NULL);
1297*9126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_IO_MUTEX;
1298*9126SWyllys.Ingersoll@Sun.COM 
1299*9126SWyllys.Ingersoll@Sun.COM 	cv_init(&tpm->iobuf_cv, NULL, CV_DRIVER, NULL);
1300*9126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_IO_CV;
1301*9126SWyllys.Ingersoll@Sun.COM 
1302*9126SWyllys.Ingersoll@Sun.COM 	/* Create minor node */
1303*9126SWyllys.Ingersoll@Sun.COM 	ret = ddi_create_minor_node(dip, "tpm", S_IFCHR, ddi_get_instance(dip),
1304*9126SWyllys.Ingersoll@Sun.COM 	    DDI_PSEUDO, 0);
1305*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1306*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: ddi_create_minor_node failed", myname);
1307*9126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
1308*9126SWyllys.Ingersoll@Sun.COM 	}
1309*9126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DIDMINOR;
1310*9126SWyllys.Ingersoll@Sun.COM 
1311*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
1312*9126SWyllys.Ingersoll@Sun.COM FAIL:
1313*9126SWyllys.Ingersoll@Sun.COM 	if (tpm != NULL) {
1314*9126SWyllys.Ingersoll@Sun.COM 		tpm_cleanup(dip, tpm);
1315*9126SWyllys.Ingersoll@Sun.COM 		ddi_soft_state_free(statep, instance);
1316*9126SWyllys.Ingersoll@Sun.COM 		tpm = NULL;
1317*9126SWyllys.Ingersoll@Sun.COM 	}
1318*9126SWyllys.Ingersoll@Sun.COM 
1319*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_FAILURE);
1320*9126SWyllys.Ingersoll@Sun.COM }
1321*9126SWyllys.Ingersoll@Sun.COM 
1322*9126SWyllys.Ingersoll@Sun.COM /*
1323*9126SWyllys.Ingersoll@Sun.COM  * Called by tpm_detach and tpm_attach (only on failure)
1324*9126SWyllys.Ingersoll@Sun.COM  * Free up the resources that are allocated
1325*9126SWyllys.Ingersoll@Sun.COM  */
1326*9126SWyllys.Ingersoll@Sun.COM static void
1327*9126SWyllys.Ingersoll@Sun.COM tpm_cleanup(dev_info_t *dip, tpm_state_t *tpm)
1328*9126SWyllys.Ingersoll@Sun.COM {
1329*9126SWyllys.Ingersoll@Sun.COM 	if (tpm == NULL)
1330*9126SWyllys.Ingersoll@Sun.COM 		return;
1331*9126SWyllys.Ingersoll@Sun.COM 
1332*9126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_MUTEX) {
1333*9126SWyllys.Ingersoll@Sun.COM 		mutex_destroy(&tpm->dev_lock);
1334*9126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_MUTEX);
1335*9126SWyllys.Ingersoll@Sun.COM 	}
1336*9126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_IO_ALLOC) {
1337*9126SWyllys.Ingersoll@Sun.COM 		ASSERT(tpm->iobuf != NULL);
1338*9126SWyllys.Ingersoll@Sun.COM 		kmem_free(tpm->iobuf, (sizeof (uint8_t))*(tpm->bufsize));
1339*9126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_IO_ALLOC);
1340*9126SWyllys.Ingersoll@Sun.COM 	}
1341*9126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_IO_MUTEX) {
1342*9126SWyllys.Ingersoll@Sun.COM 		mutex_destroy(&tpm->iobuf_lock);
1343*9126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_IO_MUTEX);
1344*9126SWyllys.Ingersoll@Sun.COM 	}
1345*9126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_IO_CV) {
1346*9126SWyllys.Ingersoll@Sun.COM 		cv_destroy(&tpm->iobuf_cv);
1347*9126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_IO_CV);
1348*9126SWyllys.Ingersoll@Sun.COM 	}
1349*9126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DIDREGSMAP) {
1350*9126SWyllys.Ingersoll@Sun.COM 		/* Free the mapped addresses */
1351*9126SWyllys.Ingersoll@Sun.COM 		if (tpm->handle != NULL)
1352*9126SWyllys.Ingersoll@Sun.COM 			ddi_regs_map_free(&tpm->handle);
1353*9126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DIDREGSMAP);
1354*9126SWyllys.Ingersoll@Sun.COM 	}
1355*9126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DIDMINOR) {
1356*9126SWyllys.Ingersoll@Sun.COM 		/* Remove minor node */
1357*9126SWyllys.Ingersoll@Sun.COM 		ddi_remove_minor_node(dip, NULL);
1358*9126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DIDMINOR);
1359*9126SWyllys.Ingersoll@Sun.COM 	}
1360*9126SWyllys.Ingersoll@Sun.COM }
1361*9126SWyllys.Ingersoll@Sun.COM 
1362*9126SWyllys.Ingersoll@Sun.COM static int
1363*9126SWyllys.Ingersoll@Sun.COM tpm_suspend(tpm_state_t *tpm)
1364*9126SWyllys.Ingersoll@Sun.COM {
1365*9126SWyllys.Ingersoll@Sun.COM 	if (tpm == NULL)
1366*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1367*9126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
1368*9126SWyllys.Ingersoll@Sun.COM 	if (tpm->suspended) {
1369*9126SWyllys.Ingersoll@Sun.COM 		mutex_exit(&tpm->pm_mutex);
1370*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_SUCCESS);
1371*9126SWyllys.Ingersoll@Sun.COM 	}
1372*9126SWyllys.Ingersoll@Sun.COM 	tpm->suspended = 1;
1373*9126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
1374*9126SWyllys.Ingersoll@Sun.COM 
1375*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
1376*9126SWyllys.Ingersoll@Sun.COM }
1377*9126SWyllys.Ingersoll@Sun.COM 
1378*9126SWyllys.Ingersoll@Sun.COM static int
1379*9126SWyllys.Ingersoll@Sun.COM tpm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1380*9126SWyllys.Ingersoll@Sun.COM {
1381*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_detach";
1382*9126SWyllys.Ingersoll@Sun.COM 	int instance;
1383*9126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
1384*9126SWyllys.Ingersoll@Sun.COM 
1385*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(dip != NULL);
1386*9126SWyllys.Ingersoll@Sun.COM 
1387*9126SWyllys.Ingersoll@Sun.COM 	instance = ddi_get_instance(dip);
1388*9126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1389*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
1390*9126SWyllys.Ingersoll@Sun.COM 		    myname);
1391*9126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
1392*9126SWyllys.Ingersoll@Sun.COM 	}
1393*9126SWyllys.Ingersoll@Sun.COM 
1394*9126SWyllys.Ingersoll@Sun.COM 	switch (cmd) {
1395*9126SWyllys.Ingersoll@Sun.COM 	case DDI_DETACH:
1396*9126SWyllys.Ingersoll@Sun.COM 		/* Body is after the switch stmt */
1397*9126SWyllys.Ingersoll@Sun.COM 		break;
1398*9126SWyllys.Ingersoll@Sun.COM 	case DDI_SUSPEND:
1399*9126SWyllys.Ingersoll@Sun.COM 		return (tpm_suspend(tpm));
1400*9126SWyllys.Ingersoll@Sun.COM 	default:
1401*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: case %d not implemented", myname, cmd);
1402*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1403*9126SWyllys.Ingersoll@Sun.COM 	}
1404*9126SWyllys.Ingersoll@Sun.COM 
1405*9126SWyllys.Ingersoll@Sun.COM 	/* Since we are freeing tpm structure, we need to gain the lock */
1406*9126SWyllys.Ingersoll@Sun.COM 
1407*9126SWyllys.Ingersoll@Sun.COM 	tpm_cleanup(dip, tpm);
1408*9126SWyllys.Ingersoll@Sun.COM 
1409*9126SWyllys.Ingersoll@Sun.COM 	mutex_destroy(&tpm->pm_mutex);
1410*9126SWyllys.Ingersoll@Sun.COM 	cv_destroy(&tpm->suspend_cv);
1411*9126SWyllys.Ingersoll@Sun.COM 
1412*9126SWyllys.Ingersoll@Sun.COM 	/* Free the soft state */
1413*9126SWyllys.Ingersoll@Sun.COM 	ddi_soft_state_free(statep, instance);
1414*9126SWyllys.Ingersoll@Sun.COM 	tpm = NULL;
1415*9126SWyllys.Ingersoll@Sun.COM 
1416*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
1417*9126SWyllys.Ingersoll@Sun.COM }
1418*9126SWyllys.Ingersoll@Sun.COM 
1419*9126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
1420*9126SWyllys.Ingersoll@Sun.COM static int
1421*9126SWyllys.Ingersoll@Sun.COM tpm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
1422*9126SWyllys.Ingersoll@Sun.COM {
1423*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_getinfo";
1424*9126SWyllys.Ingersoll@Sun.COM 	int instance;
1425*9126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
1426*9126SWyllys.Ingersoll@Sun.COM 
1427*9126SWyllys.Ingersoll@Sun.COM 	instance = ddi_get_instance(dip);
1428*9126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1429*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
1430*9126SWyllys.Ingersoll@Sun.COM 		    myname);
1431*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1432*9126SWyllys.Ingersoll@Sun.COM 	}
1433*9126SWyllys.Ingersoll@Sun.COM 
1434*9126SWyllys.Ingersoll@Sun.COM 	switch (cmd) {
1435*9126SWyllys.Ingersoll@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
1436*9126SWyllys.Ingersoll@Sun.COM 		*resultp = tpm->dip;
1437*9126SWyllys.Ingersoll@Sun.COM 		break;
1438*9126SWyllys.Ingersoll@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
1439*9126SWyllys.Ingersoll@Sun.COM 		*resultp = 0;
1440*9126SWyllys.Ingersoll@Sun.COM 		break;
1441*9126SWyllys.Ingersoll@Sun.COM 	default:
1442*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: cmd %d is not implemented", myname, cmd);
1443*9126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1444*9126SWyllys.Ingersoll@Sun.COM 	}
1445*9126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
1446*9126SWyllys.Ingersoll@Sun.COM }
1447*9126SWyllys.Ingersoll@Sun.COM 
1448*9126SWyllys.Ingersoll@Sun.COM /*
1449*9126SWyllys.Ingersoll@Sun.COM  * Driver entry points
1450*9126SWyllys.Ingersoll@Sun.COM  */
1451*9126SWyllys.Ingersoll@Sun.COM 
1452*9126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
1453*9126SWyllys.Ingersoll@Sun.COM static int
1454*9126SWyllys.Ingersoll@Sun.COM tpm_open(dev_t *devp, int flag, int otyp, cred_t *cred)
1455*9126SWyllys.Ingersoll@Sun.COM {
1456*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_open";
1457*9126SWyllys.Ingersoll@Sun.COM 	int instance;
1458*9126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
1459*9126SWyllys.Ingersoll@Sun.COM 
1460*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(devp != NULL);
1461*9126SWyllys.Ingersoll@Sun.COM 
1462*9126SWyllys.Ingersoll@Sun.COM 	instance = getminor(*devp);
1463*9126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1464*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
1465*9126SWyllys.Ingersoll@Sun.COM 		    myname);
1466*9126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
1467*9126SWyllys.Ingersoll@Sun.COM 	}
1468*9126SWyllys.Ingersoll@Sun.COM 	if (otyp != OTYP_CHR) {
1469*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: otyp(%d) != OTYP_CHR(%d)",
1470*9126SWyllys.Ingersoll@Sun.COM 		    myname, otyp, OTYP_CHR);
1471*9126SWyllys.Ingersoll@Sun.COM 		return (EINVAL);
1472*9126SWyllys.Ingersoll@Sun.COM 	}
1473*9126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
1474*9126SWyllys.Ingersoll@Sun.COM 	while (tpm->suspended)
1475*9126SWyllys.Ingersoll@Sun.COM 		cv_wait(&tpm->suspend_cv, &tpm->pm_mutex);
1476*9126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
1477*9126SWyllys.Ingersoll@Sun.COM 
1478*9126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->dev_lock);
1479*9126SWyllys.Ingersoll@Sun.COM 	if (tpm->dev_held) {
1480*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: the device is already being used",
1481*9126SWyllys.Ingersoll@Sun.COM 		    myname);
1482*9126SWyllys.Ingersoll@Sun.COM 		mutex_exit(&tpm->dev_lock);
1483*9126SWyllys.Ingersoll@Sun.COM 		return (EBUSY);
1484*9126SWyllys.Ingersoll@Sun.COM 	}
1485*9126SWyllys.Ingersoll@Sun.COM 
1486*9126SWyllys.Ingersoll@Sun.COM 	/* The device is free so mark it busy */
1487*9126SWyllys.Ingersoll@Sun.COM 	tpm->dev_held = 1;
1488*9126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->dev_lock);
1489*9126SWyllys.Ingersoll@Sun.COM 
1490*9126SWyllys.Ingersoll@Sun.COM 	return (0);
1491*9126SWyllys.Ingersoll@Sun.COM }
1492*9126SWyllys.Ingersoll@Sun.COM 
1493*9126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
1494*9126SWyllys.Ingersoll@Sun.COM static int
1495*9126SWyllys.Ingersoll@Sun.COM tpm_close(dev_t dev, int flag, int otyp, cred_t *cred)
1496*9126SWyllys.Ingersoll@Sun.COM {
1497*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_close";
1498*9126SWyllys.Ingersoll@Sun.COM 	int instance;
1499*9126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
1500*9126SWyllys.Ingersoll@Sun.COM 
1501*9126SWyllys.Ingersoll@Sun.COM 	instance = getminor(dev);
1502*9126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1503*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
1504*9126SWyllys.Ingersoll@Sun.COM 		    myname);
1505*9126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
1506*9126SWyllys.Ingersoll@Sun.COM 	}
1507*9126SWyllys.Ingersoll@Sun.COM 	if (otyp != OTYP_CHR) {
1508*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: otyp(%d) != OTYP_CHR(%d)",
1509*9126SWyllys.Ingersoll@Sun.COM 		    myname, otyp, OTYP_CHR);
1510*9126SWyllys.Ingersoll@Sun.COM 		return (EINVAL);
1511*9126SWyllys.Ingersoll@Sun.COM 	}
1512*9126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
1513*9126SWyllys.Ingersoll@Sun.COM 	while (tpm->suspended)
1514*9126SWyllys.Ingersoll@Sun.COM 		cv_wait(&tpm->suspend_cv, &tpm->pm_mutex);
1515*9126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
1516*9126SWyllys.Ingersoll@Sun.COM 
1517*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm->dev_held);
1518*9126SWyllys.Ingersoll@Sun.COM 
1519*9126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->dev_lock);
1520*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(mutex_owned(&tpm->dev_lock));
1521*9126SWyllys.Ingersoll@Sun.COM 	tpm->dev_held = 0;
1522*9126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->dev_lock);
1523*9126SWyllys.Ingersoll@Sun.COM 
1524*9126SWyllys.Ingersoll@Sun.COM 	return (0);
1525*9126SWyllys.Ingersoll@Sun.COM }
1526*9126SWyllys.Ingersoll@Sun.COM 
1527*9126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
1528*9126SWyllys.Ingersoll@Sun.COM static int
1529*9126SWyllys.Ingersoll@Sun.COM tpm_read(dev_t dev, struct uio *uiop, cred_t *credp)
1530*9126SWyllys.Ingersoll@Sun.COM {
1531*9126SWyllys.Ingersoll@Sun.COM 	int ret;
1532*9126SWyllys.Ingersoll@Sun.COM 	uint32_t size;
1533*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_read";
1534*9126SWyllys.Ingersoll@Sun.COM 	int instance;
1535*9126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
1536*9126SWyllys.Ingersoll@Sun.COM 
1537*9126SWyllys.Ingersoll@Sun.COM 	instance = getminor(dev);
1538*9126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1539*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
1540*9126SWyllys.Ingersoll@Sun.COM 		    myname);
1541*9126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
1542*9126SWyllys.Ingersoll@Sun.COM 	}
1543*9126SWyllys.Ingersoll@Sun.COM 	if (uiop == NULL) {
1544*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: passed in uiop is NULL", myname);
1545*9126SWyllys.Ingersoll@Sun.COM 		return (EFAULT);
1546*9126SWyllys.Ingersoll@Sun.COM 	}
1547*9126SWyllys.Ingersoll@Sun.COM 
1548*9126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
1549*9126SWyllys.Ingersoll@Sun.COM 	while (tpm->suspended)
1550*9126SWyllys.Ingersoll@Sun.COM 		cv_wait(&tpm->suspend_cv, &tpm->pm_mutex);
1551*9126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
1552*9126SWyllys.Ingersoll@Sun.COM 
1553*9126SWyllys.Ingersoll@Sun.COM 	/* Receive the data after requiring the lock */
1554*9126SWyllys.Ingersoll@Sun.COM 	ret = tpm_lock(tpm);
1555*9126SWyllys.Ingersoll@Sun.COM 
1556*9126SWyllys.Ingersoll@Sun.COM 	/* Timeout reached */
1557*9126SWyllys.Ingersoll@Sun.COM 	if (ret == ETIME)
1558*9126SWyllys.Ingersoll@Sun.COM 		return (ret);
1559*9126SWyllys.Ingersoll@Sun.COM 
1560*9126SWyllys.Ingersoll@Sun.COM 	if (uiop->uio_resid > tpm->bufsize) {
1561*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: read_in data is bigger "
1562*9126SWyllys.Ingersoll@Sun.COM 		    "than tpm->bufsize:read in:%d, bufsiz:%d",
1563*9126SWyllys.Ingersoll@Sun.COM 		    myname, (int)uiop->uio_resid, (int)tpm->bufsize);
1564*9126SWyllys.Ingersoll@Sun.COM 		ret = EIO;
1565*9126SWyllys.Ingersoll@Sun.COM 		goto OUT;
1566*9126SWyllys.Ingersoll@Sun.COM 	}
1567*9126SWyllys.Ingersoll@Sun.COM 
1568*9126SWyllys.Ingersoll@Sun.COM 	ret = tis_recv_data(tpm, tpm->iobuf, tpm->bufsize);
1569*9126SWyllys.Ingersoll@Sun.COM 	if (ret < TPM_HEADER_SIZE) {
1570*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_recv_data returned error", myname);
1571*9126SWyllys.Ingersoll@Sun.COM 		ret = EIO;
1572*9126SWyllys.Ingersoll@Sun.COM 		goto OUT;
1573*9126SWyllys.Ingersoll@Sun.COM 	}
1574*9126SWyllys.Ingersoll@Sun.COM 
1575*9126SWyllys.Ingersoll@Sun.COM 	size = load32(tpm->iobuf, 2);
1576*9126SWyllys.Ingersoll@Sun.COM 	if (ret != size) {
1577*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_recv_data:"
1578*9126SWyllys.Ingersoll@Sun.COM 		    "expected size=%d, actually read=%d",
1579*9126SWyllys.Ingersoll@Sun.COM 		    myname, size, ret);
1580*9126SWyllys.Ingersoll@Sun.COM 		ret = EIO;
1581*9126SWyllys.Ingersoll@Sun.COM 		goto OUT;
1582*9126SWyllys.Ingersoll@Sun.COM 	}
1583*9126SWyllys.Ingersoll@Sun.COM 
1584*9126SWyllys.Ingersoll@Sun.COM 	/* Send the buffer from the kernel to the userspace */
1585*9126SWyllys.Ingersoll@Sun.COM 	ret = uiomove(tpm->iobuf, size, UIO_READ, uiop);
1586*9126SWyllys.Ingersoll@Sun.COM 	if (ret) {
1587*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: uiomove returned error", myname);
1588*9126SWyllys.Ingersoll@Sun.COM 		goto OUT;
1589*9126SWyllys.Ingersoll@Sun.COM 	}
1590*9126SWyllys.Ingersoll@Sun.COM 
1591*9126SWyllys.Ingersoll@Sun.COM 	/* Zeroize the buffer... */
1592*9126SWyllys.Ingersoll@Sun.COM 	bzero(tpm->iobuf, tpm->bufsize);
1593*9126SWyllys.Ingersoll@Sun.COM 	ret = DDI_SUCCESS;
1594*9126SWyllys.Ingersoll@Sun.COM OUT:
1595*9126SWyllys.Ingersoll@Sun.COM 	/* We are done now: wake up the waiting threads */
1596*9126SWyllys.Ingersoll@Sun.COM 	tpm_unlock(tpm);
1597*9126SWyllys.Ingersoll@Sun.COM 
1598*9126SWyllys.Ingersoll@Sun.COM 	return (ret);
1599*9126SWyllys.Ingersoll@Sun.COM }
1600*9126SWyllys.Ingersoll@Sun.COM 
1601*9126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
1602*9126SWyllys.Ingersoll@Sun.COM static int
1603*9126SWyllys.Ingersoll@Sun.COM tpm_write(dev_t dev, struct uio *uiop, cred_t *credp)
1604*9126SWyllys.Ingersoll@Sun.COM {
1605*9126SWyllys.Ingersoll@Sun.COM 	int ret;
1606*9126SWyllys.Ingersoll@Sun.COM 	size_t len;
1607*9126SWyllys.Ingersoll@Sun.COM 	uint32_t size;
1608*9126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_write";
1609*9126SWyllys.Ingersoll@Sun.COM 	int instance;
1610*9126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
1611*9126SWyllys.Ingersoll@Sun.COM 
1612*9126SWyllys.Ingersoll@Sun.COM 	instance = getminor(dev);
1613*9126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1614*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
1615*9126SWyllys.Ingersoll@Sun.COM 		    myname);
1616*9126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
1617*9126SWyllys.Ingersoll@Sun.COM 	}
1618*9126SWyllys.Ingersoll@Sun.COM 
1619*9126SWyllys.Ingersoll@Sun.COM 	if (uiop == NULL) {
1620*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: passed in uiop is NULL", myname);
1621*9126SWyllys.Ingersoll@Sun.COM 		return (EFAULT);
1622*9126SWyllys.Ingersoll@Sun.COM 	}
1623*9126SWyllys.Ingersoll@Sun.COM 
1624*9126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
1625*9126SWyllys.Ingersoll@Sun.COM 	while (tpm->suspended)
1626*9126SWyllys.Ingersoll@Sun.COM 		cv_wait(&tpm->suspend_cv, &tpm->pm_mutex);
1627*9126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
1628*9126SWyllys.Ingersoll@Sun.COM 
1629*9126SWyllys.Ingersoll@Sun.COM 	len = uiop->uio_resid;
1630*9126SWyllys.Ingersoll@Sun.COM 	if (len == 0) {
1631*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: requested read of len 0", myname);
1632*9126SWyllys.Ingersoll@Sun.COM 		return (0);
1633*9126SWyllys.Ingersoll@Sun.COM 	}
1634*9126SWyllys.Ingersoll@Sun.COM 
1635*9126SWyllys.Ingersoll@Sun.COM 	/* Get the lock for using iobuf */
1636*9126SWyllys.Ingersoll@Sun.COM 	ret = tpm_lock(tpm);
1637*9126SWyllys.Ingersoll@Sun.COM 	/* Timeout Reached */
1638*9126SWyllys.Ingersoll@Sun.COM 	if (ret == ETIME)
1639*9126SWyllys.Ingersoll@Sun.COM 		return (ret);
1640*9126SWyllys.Ingersoll@Sun.COM 
1641*9126SWyllys.Ingersoll@Sun.COM 	/* Copy the header and parse the structure to find out the size... */
1642*9126SWyllys.Ingersoll@Sun.COM 	ret = uiomove(tpm->iobuf, TPM_HEADER_SIZE, UIO_WRITE, uiop);
1643*9126SWyllys.Ingersoll@Sun.COM 	if (ret) {
1644*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: uiomove returned error"
1645*9126SWyllys.Ingersoll@Sun.COM 		    "while getting the the header",
1646*9126SWyllys.Ingersoll@Sun.COM 		    myname);
1647*9126SWyllys.Ingersoll@Sun.COM 		goto OUT;
1648*9126SWyllys.Ingersoll@Sun.COM 	}
1649*9126SWyllys.Ingersoll@Sun.COM 
1650*9126SWyllys.Ingersoll@Sun.COM 	/* Get the buffersize from the command buffer structure */
1651*9126SWyllys.Ingersoll@Sun.COM 	size = load32(tpm->iobuf, TPM_PARAMSIZE_OFFSET);
1652*9126SWyllys.Ingersoll@Sun.COM 
1653*9126SWyllys.Ingersoll@Sun.COM 	/* Copy the command to the contiguous buffer */
1654*9126SWyllys.Ingersoll@Sun.COM 	if (size > tpm->bufsize) {
1655*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: size %d is greater than "
1656*9126SWyllys.Ingersoll@Sun.COM 		    "the tpm's input buffer size %d",
1657*9126SWyllys.Ingersoll@Sun.COM 		    myname, (int)size, (int)tpm->bufsize);
1658*9126SWyllys.Ingersoll@Sun.COM 		ret = ENXIO;
1659*9126SWyllys.Ingersoll@Sun.COM 		goto OUT;
1660*9126SWyllys.Ingersoll@Sun.COM 	}
1661*9126SWyllys.Ingersoll@Sun.COM 
1662*9126SWyllys.Ingersoll@Sun.COM 	/* Copy the buffer from the userspace to kernel */
1663*9126SWyllys.Ingersoll@Sun.COM 	ret = uiomove(tpm->iobuf+TPM_HEADER_SIZE, size-TPM_HEADER_SIZE,
1664*9126SWyllys.Ingersoll@Sun.COM 	    UIO_WRITE, uiop);
1665*9126SWyllys.Ingersoll@Sun.COM 
1666*9126SWyllys.Ingersoll@Sun.COM 	if (ret) {
1667*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: uiomove returned error"
1668*9126SWyllys.Ingersoll@Sun.COM 		    "while getting the rest of the command", myname);
1669*9126SWyllys.Ingersoll@Sun.COM 		goto OUT;
1670*9126SWyllys.Ingersoll@Sun.COM 	}
1671*9126SWyllys.Ingersoll@Sun.COM 
1672*9126SWyllys.Ingersoll@Sun.COM 	/* Send the command */
1673*9126SWyllys.Ingersoll@Sun.COM 	ret = tis_send_data(tpm, tpm->iobuf, size);
1674*9126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1675*9126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_send_data returned error", myname);
1676*9126SWyllys.Ingersoll@Sun.COM 		ret = EFAULT;
1677*9126SWyllys.Ingersoll@Sun.COM 		goto OUT;
1678*9126SWyllys.Ingersoll@Sun.COM 	}
1679*9126SWyllys.Ingersoll@Sun.COM 
1680*9126SWyllys.Ingersoll@Sun.COM 	/* Zeroize the buffer... */
1681*9126SWyllys.Ingersoll@Sun.COM 	bzero(tpm->iobuf, tpm->bufsize);
1682*9126SWyllys.Ingersoll@Sun.COM 	ret = DDI_SUCCESS;
1683*9126SWyllys.Ingersoll@Sun.COM OUT:
1684*9126SWyllys.Ingersoll@Sun.COM 	tpm_unlock(tpm);
1685*9126SWyllys.Ingersoll@Sun.COM 	return (ret);
1686*9126SWyllys.Ingersoll@Sun.COM }
1687*9126SWyllys.Ingersoll@Sun.COM 
1688*9126SWyllys.Ingersoll@Sun.COM /*
1689*9126SWyllys.Ingersoll@Sun.COM  * This is to deal with the contentions for the iobuf
1690*9126SWyllys.Ingersoll@Sun.COM  */
1691*9126SWyllys.Ingersoll@Sun.COM static inline int
1692*9126SWyllys.Ingersoll@Sun.COM tpm_lock(tpm_state_t *tpm)
1693*9126SWyllys.Ingersoll@Sun.COM {
1694*9126SWyllys.Ingersoll@Sun.COM 	int ret;
1695*9126SWyllys.Ingersoll@Sun.COM 	clock_t timeout;
1696*9126SWyllys.Ingersoll@Sun.COM 
1697*9126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->iobuf_lock);
1698*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(mutex_owned(&tpm->iobuf_lock));
1699*9126SWyllys.Ingersoll@Sun.COM 
1700*9126SWyllys.Ingersoll@Sun.COM 	timeout = ddi_get_lbolt() + drv_usectohz(TPM_IO_TIMEOUT);
1701*9126SWyllys.Ingersoll@Sun.COM 
1702*9126SWyllys.Ingersoll@Sun.COM 	/* Wait until the iobuf becomes free with the timeout */
1703*9126SWyllys.Ingersoll@Sun.COM 	while (tpm->iobuf_inuse) {
1704*9126SWyllys.Ingersoll@Sun.COM 		ret = cv_timedwait(&tpm->iobuf_cv, &tpm->iobuf_lock, timeout);
1705*9126SWyllys.Ingersoll@Sun.COM 		if (ret <= 0) {
1706*9126SWyllys.Ingersoll@Sun.COM 			/* Timeout reached */
1707*9126SWyllys.Ingersoll@Sun.COM 			mutex_exit(&tpm->iobuf_lock);
1708*9126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "tpm_lock:iorequest timed out");
1709*9126SWyllys.Ingersoll@Sun.COM 			return (ETIME);
1710*9126SWyllys.Ingersoll@Sun.COM 		}
1711*9126SWyllys.Ingersoll@Sun.COM 	}
1712*9126SWyllys.Ingersoll@Sun.COM 	tpm->iobuf_inuse = 1;
1713*9126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->iobuf_lock);
1714*9126SWyllys.Ingersoll@Sun.COM 	return (0);
1715*9126SWyllys.Ingersoll@Sun.COM }
1716*9126SWyllys.Ingersoll@Sun.COM 
1717*9126SWyllys.Ingersoll@Sun.COM /*
1718*9126SWyllys.Ingersoll@Sun.COM  * This is to deal with the contentions for the iobuf
1719*9126SWyllys.Ingersoll@Sun.COM  */
1720*9126SWyllys.Ingersoll@Sun.COM static inline void
1721*9126SWyllys.Ingersoll@Sun.COM tpm_unlock(tpm_state_t *tpm)
1722*9126SWyllys.Ingersoll@Sun.COM {
1723*9126SWyllys.Ingersoll@Sun.COM 	/* Wake up the waiting threads */
1724*9126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->iobuf_lock);
1725*9126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm->iobuf_inuse == 1 && mutex_owned(&tpm->iobuf_lock));
1726*9126SWyllys.Ingersoll@Sun.COM 	tpm->iobuf_inuse = 0;
1727*9126SWyllys.Ingersoll@Sun.COM 	cv_broadcast(&tpm->iobuf_cv);
1728*9126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->iobuf_lock);
1729*9126SWyllys.Ingersoll@Sun.COM }
1730