xref: /onnv-gate/usr/src/uts/common/io/tpm/tpm.c (revision 10346:9f0b25e42dc5)
19126SWyllys.Ingersoll@Sun.COM /*
29126SWyllys.Ingersoll@Sun.COM  * CDDL HEADER START
39126SWyllys.Ingersoll@Sun.COM  *
49126SWyllys.Ingersoll@Sun.COM  * The contents of this file are subject to the terms of the
59126SWyllys.Ingersoll@Sun.COM  * Common Development and Distribution License (the "License").
69126SWyllys.Ingersoll@Sun.COM  * You may not use this file except in compliance with the License.
79126SWyllys.Ingersoll@Sun.COM  *
89126SWyllys.Ingersoll@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99126SWyllys.Ingersoll@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109126SWyllys.Ingersoll@Sun.COM  * See the License for the specific language governing permissions
119126SWyllys.Ingersoll@Sun.COM  * and limitations under the License.
129126SWyllys.Ingersoll@Sun.COM  *
139126SWyllys.Ingersoll@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149126SWyllys.Ingersoll@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159126SWyllys.Ingersoll@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169126SWyllys.Ingersoll@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179126SWyllys.Ingersoll@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189126SWyllys.Ingersoll@Sun.COM  *
199126SWyllys.Ingersoll@Sun.COM  * CDDL HEADER END
209126SWyllys.Ingersoll@Sun.COM  */
219126SWyllys.Ingersoll@Sun.COM 
229126SWyllys.Ingersoll@Sun.COM /*
239126SWyllys.Ingersoll@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
249126SWyllys.Ingersoll@Sun.COM  * Use is subject to license terms.
259126SWyllys.Ingersoll@Sun.COM  */
269126SWyllys.Ingersoll@Sun.COM 
279126SWyllys.Ingersoll@Sun.COM /*
289126SWyllys.Ingersoll@Sun.COM  * TPM 1.2 Driver for the TPMs that follow TIS v1.2
299126SWyllys.Ingersoll@Sun.COM  */
309126SWyllys.Ingersoll@Sun.COM 
319126SWyllys.Ingersoll@Sun.COM #include <sys/devops.h>		/* used by dev_ops */
329126SWyllys.Ingersoll@Sun.COM #include <sys/conf.h>		/* used by dev_ops,cb_ops */
339126SWyllys.Ingersoll@Sun.COM #include <sys/modctl.h>		/* for _init,_info,_fini,mod_* */
349126SWyllys.Ingersoll@Sun.COM #include <sys/ddi.h>		/* used by all entry points */
359126SWyllys.Ingersoll@Sun.COM #include <sys/sunddi.h>		/* used by all entry points */
369126SWyllys.Ingersoll@Sun.COM #include <sys/cmn_err.h>	/* used for debug outputs */
379126SWyllys.Ingersoll@Sun.COM #include <sys/types.h>		/* used by prop_op, ddi_prop_op */
389126SWyllys.Ingersoll@Sun.COM 
399126SWyllys.Ingersoll@Sun.COM #include <sys/file.h>		/* used by open, close */
409126SWyllys.Ingersoll@Sun.COM #include <sys/errno.h>		/* used by open,close,read,write */
419126SWyllys.Ingersoll@Sun.COM #include <sys/open.h>		/* used by open,close,read,write */
429126SWyllys.Ingersoll@Sun.COM #include <sys/cred.h>		/* used by open,close,read */
439126SWyllys.Ingersoll@Sun.COM #include <sys/uio.h>		/* used by read */
449126SWyllys.Ingersoll@Sun.COM #include <sys/stat.h>		/* defines S_IFCHR */
459126SWyllys.Ingersoll@Sun.COM 
469126SWyllys.Ingersoll@Sun.COM #include <sys/byteorder.h>	/* for ntohs, ntohl, htons, htonl */
479126SWyllys.Ingersoll@Sun.COM 
48*10346Swyllys.ingersoll@sun.com #ifdef sun4v
49*10346Swyllys.ingersoll@sun.com #include <sys/hypervisor_api.h>
50*10346Swyllys.ingersoll@sun.com #include <sys/hsvc.h>
51*10346Swyllys.ingersoll@sun.com #endif
52*10346Swyllys.ingersoll@sun.com 
539126SWyllys.Ingersoll@Sun.COM #include <tss/platform.h> 	/* from SUNWtss */
549126SWyllys.Ingersoll@Sun.COM #include <tss/tpm.h> 		/* from SUNWtss */
559126SWyllys.Ingersoll@Sun.COM 
569126SWyllys.Ingersoll@Sun.COM #include "tpm_tis.h"
579126SWyllys.Ingersoll@Sun.COM #include "tpm_ddi.h"
589126SWyllys.Ingersoll@Sun.COM #include "tpm_duration.h"
599126SWyllys.Ingersoll@Sun.COM 
609126SWyllys.Ingersoll@Sun.COM #define	TPM_HEADER_SIZE 10
619126SWyllys.Ingersoll@Sun.COM typedef enum {
629126SWyllys.Ingersoll@Sun.COM 	TPM_TAG_OFFSET = 0,
639126SWyllys.Ingersoll@Sun.COM 	TPM_PARAMSIZE_OFFSET = 2,
649126SWyllys.Ingersoll@Sun.COM 	TPM_RETURN_OFFSET = 6,
659126SWyllys.Ingersoll@Sun.COM 	TPM_COMMAND_CODE_OFFSET = 6,
669126SWyllys.Ingersoll@Sun.COM } TPM_HEADER_OFFSET_T;
679126SWyllys.Ingersoll@Sun.COM 
689126SWyllys.Ingersoll@Sun.COM /*
699126SWyllys.Ingersoll@Sun.COM  * This is to address some TPMs that does not report the correct duration
709126SWyllys.Ingersoll@Sun.COM  * and timeouts.  In our experience with the production TPMs, we encountered
719126SWyllys.Ingersoll@Sun.COM  * time errors such as GetCapability command from TPM reporting the timeout
729126SWyllys.Ingersoll@Sun.COM  * and durations in milliseconds rather than microseconds.  Some other TPMs
739126SWyllys.Ingersoll@Sun.COM  * report the value 0's
749126SWyllys.Ingersoll@Sun.COM  *
759126SWyllys.Ingersoll@Sun.COM  * Short Duration is based on section 11.3.4 of TIS speciciation, that
769126SWyllys.Ingersoll@Sun.COM  * TPM_GetCapability (short duration) commands should not be longer than 750ms
779126SWyllys.Ingersoll@Sun.COM  * and that section 11.3.7 states that TPM_ContinueSelfTest (medium duration)
789126SWyllys.Ingersoll@Sun.COM  * should not be longer than 1 second.
799126SWyllys.Ingersoll@Sun.COM  */
809126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_SHORT_DURATION	750000
819126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_MEDIUM_DURATION	1000000
829126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_LONG_DURATION	300000000
839126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_TIMEOUT_A	750000
849126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_TIMEOUT_B	2000000
859126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_TIMEOUT_C	750000
869126SWyllys.Ingersoll@Sun.COM #define	DEFAULT_TIMEOUT_D	750000
879126SWyllys.Ingersoll@Sun.COM 
889126SWyllys.Ingersoll@Sun.COM /*
899126SWyllys.Ingersoll@Sun.COM  * In order to test the 'millisecond bug', we test if DURATIONS and TIMEOUTS
909126SWyllys.Ingersoll@Sun.COM  * are unreasonably low...such as 10 milliseconds (TPM isn't that fast).
919126SWyllys.Ingersoll@Sun.COM  * and 400 milliseconds for long duration
929126SWyllys.Ingersoll@Sun.COM  */
939126SWyllys.Ingersoll@Sun.COM #define	TEN_MILLISECONDS	10000	/* 10 milliseconds */
949126SWyllys.Ingersoll@Sun.COM #define	FOUR_HUNDRED_MILLISECONDS 400000	/* 4 hundred milliseconds */
959126SWyllys.Ingersoll@Sun.COM 
96*10346Swyllys.ingersoll@sun.com #define	DEFAULT_LOCALITY 0
979126SWyllys.Ingersoll@Sun.COM /*
989126SWyllys.Ingersoll@Sun.COM  * TPM input/output buffer offsets
999126SWyllys.Ingersoll@Sun.COM  */
1009126SWyllys.Ingersoll@Sun.COM 
1019126SWyllys.Ingersoll@Sun.COM typedef enum {
1029126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_RESPSIZE_OFFSET = 10,
1039126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_RESP_OFFSET = 14,
1049126SWyllys.Ingersoll@Sun.COM } TPM_CAP_RET_OFFSET_T;
1059126SWyllys.Ingersoll@Sun.COM 
1069126SWyllys.Ingersoll@Sun.COM typedef enum {
1079126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_TIMEOUT_A_OFFSET = 14,
1089126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_TIMEOUT_B_OFFSET = 18,
1099126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_TIMEOUT_C_OFFSET = 22,
1109126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_TIMEOUT_D_OFFSET = 26,
1119126SWyllys.Ingersoll@Sun.COM } TPM_CAP_TIMEOUT_OFFSET_T;
1129126SWyllys.Ingersoll@Sun.COM 
1139126SWyllys.Ingersoll@Sun.COM typedef enum {
1149126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_DUR_SHORT_OFFSET = 14,
1159126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_DUR_MEDIUM_OFFSET = 18,
1169126SWyllys.Ingersoll@Sun.COM 	TPM_CAP_DUR_LONG_OFFSET = 22,
1179126SWyllys.Ingersoll@Sun.COM } TPM_CAP_DURATION_OFFSET_T;
1189126SWyllys.Ingersoll@Sun.COM 
1199126SWyllys.Ingersoll@Sun.COM #define	TPM_CAP_VERSION_INFO_OFFSET	14
1209126SWyllys.Ingersoll@Sun.COM #define	TPM_CAP_VERSION_INFO_SIZE	15
1219126SWyllys.Ingersoll@Sun.COM 
1229126SWyllys.Ingersoll@Sun.COM /*
1239126SWyllys.Ingersoll@Sun.COM  * Internal TPM command functions
1249126SWyllys.Ingersoll@Sun.COM  */
1259126SWyllys.Ingersoll@Sun.COM static int itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz);
1269126SWyllys.Ingersoll@Sun.COM static int tpm_get_timeouts(tpm_state_t *tpm);
1279126SWyllys.Ingersoll@Sun.COM static int tpm_get_duration(tpm_state_t *tpm);
1289126SWyllys.Ingersoll@Sun.COM static int tpm_get_version(tpm_state_t *tpm);
1299126SWyllys.Ingersoll@Sun.COM static int tpm_continue_selftest(tpm_state_t *tpm);
1309126SWyllys.Ingersoll@Sun.COM 
1319126SWyllys.Ingersoll@Sun.COM /*
1329126SWyllys.Ingersoll@Sun.COM  * Internal TIS related functions
1339126SWyllys.Ingersoll@Sun.COM  */
1349126SWyllys.Ingersoll@Sun.COM static int tpm_wait_for_stat(tpm_state_t *, uint8_t, clock_t);
1359126SWyllys.Ingersoll@Sun.COM static clock_t tpm_get_ordinal_duration(tpm_state_t *, uint8_t);
1369126SWyllys.Ingersoll@Sun.COM static int tis_check_active_locality(tpm_state_t *, char);
1379126SWyllys.Ingersoll@Sun.COM static int tis_request_locality(tpm_state_t *, char);
1389126SWyllys.Ingersoll@Sun.COM static void tis_release_locality(tpm_state_t *, char, int);
1399126SWyllys.Ingersoll@Sun.COM static int tis_init(tpm_state_t *);
1409126SWyllys.Ingersoll@Sun.COM static uint8_t tis_get_status(tpm_state_t *);
1419126SWyllys.Ingersoll@Sun.COM static int tis_send_data(tpm_state_t *, uint8_t *, size_t);
1429126SWyllys.Ingersoll@Sun.COM static int tis_recv_data(tpm_state_t *, uint8_t *, size_t);
1439126SWyllys.Ingersoll@Sun.COM 
1449126SWyllys.Ingersoll@Sun.COM /* Auxilliary */
1459126SWyllys.Ingersoll@Sun.COM static int receive_data(tpm_state_t *, uint8_t *, size_t);
146*10346Swyllys.ingersoll@sun.com static inline int tpm_io_lock(tpm_state_t *);
1479126SWyllys.Ingersoll@Sun.COM static inline void tpm_unlock(tpm_state_t *);
1489126SWyllys.Ingersoll@Sun.COM static void tpm_cleanup(dev_info_t *, tpm_state_t *);
1499126SWyllys.Ingersoll@Sun.COM 
1509126SWyllys.Ingersoll@Sun.COM /*
1519126SWyllys.Ingersoll@Sun.COM  * Sun DDI/DDK entry points
1529126SWyllys.Ingersoll@Sun.COM  */
1539126SWyllys.Ingersoll@Sun.COM 
1549126SWyllys.Ingersoll@Sun.COM /* Declaration of autoconfig functions */
1559126SWyllys.Ingersoll@Sun.COM static int tpm_attach(dev_info_t *, ddi_attach_cmd_t);
1569126SWyllys.Ingersoll@Sun.COM static int tpm_detach(dev_info_t *, ddi_detach_cmd_t);
1579126SWyllys.Ingersoll@Sun.COM static int tpm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1589126SWyllys.Ingersoll@Sun.COM static int tpm_quiesce(dev_info_t *);
1599126SWyllys.Ingersoll@Sun.COM /* End of autoconfig functions */
1609126SWyllys.Ingersoll@Sun.COM 
1619126SWyllys.Ingersoll@Sun.COM /* Declaration of driver entry point functions */
1629126SWyllys.Ingersoll@Sun.COM static int tpm_open(dev_t *, int, int, cred_t *);
1639126SWyllys.Ingersoll@Sun.COM static int tpm_close(dev_t, int, int, cred_t *);
1649126SWyllys.Ingersoll@Sun.COM static int tpm_read(dev_t, struct uio *, cred_t *);
1659126SWyllys.Ingersoll@Sun.COM static int tpm_write(dev_t, struct uio *, cred_t *);
1669126SWyllys.Ingersoll@Sun.COM /* End of driver entry point functions */
1679126SWyllys.Ingersoll@Sun.COM 
1689126SWyllys.Ingersoll@Sun.COM /* cb_ops structure */
1699126SWyllys.Ingersoll@Sun.COM static struct cb_ops tpm_cb_ops = {
1709126SWyllys.Ingersoll@Sun.COM 	tpm_open,
1719126SWyllys.Ingersoll@Sun.COM 	tpm_close,
1729126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no strategy - nodev returns ENXIO */
1739126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no print */
1749126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no dump */
1759126SWyllys.Ingersoll@Sun.COM 	tpm_read,
1769126SWyllys.Ingersoll@Sun.COM 	tpm_write,
1779126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no ioctl */
1789126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no devmap */
1799126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no mmap */
1809126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no segmap */
1819126SWyllys.Ingersoll@Sun.COM 	nochpoll,	/* returns ENXIO for non-pollable devices */
1829126SWyllys.Ingersoll@Sun.COM 	ddi_prop_op,
1839126SWyllys.Ingersoll@Sun.COM 	NULL,		/* streamtab struc */
1849126SWyllys.Ingersoll@Sun.COM 	D_MP,		/* compatibility flags */
1859126SWyllys.Ingersoll@Sun.COM 	CB_REV,		/* cb_ops revision number */
1869126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no aread */
1879126SWyllys.Ingersoll@Sun.COM 	nodev		/* no awrite */
1889126SWyllys.Ingersoll@Sun.COM };
1899126SWyllys.Ingersoll@Sun.COM 
1909126SWyllys.Ingersoll@Sun.COM /* dev_ops structure */
1919126SWyllys.Ingersoll@Sun.COM static struct dev_ops tpm_dev_ops = {
1929126SWyllys.Ingersoll@Sun.COM 	DEVO_REV,
1939126SWyllys.Ingersoll@Sun.COM 	0,		/* reference count */
1949126SWyllys.Ingersoll@Sun.COM 	tpm_getinfo,
1959126SWyllys.Ingersoll@Sun.COM 	nulldev,	/* no identify - nulldev returns 0 */
1969126SWyllys.Ingersoll@Sun.COM 	nulldev,
1979126SWyllys.Ingersoll@Sun.COM 	tpm_attach,
1989126SWyllys.Ingersoll@Sun.COM 	tpm_detach,
1999126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no reset - nodev returns ENXIO */
2009126SWyllys.Ingersoll@Sun.COM 	&tpm_cb_ops,
2019126SWyllys.Ingersoll@Sun.COM 	(struct bus_ops *)NULL,
2029126SWyllys.Ingersoll@Sun.COM 	nodev,		/* no power */
2039126SWyllys.Ingersoll@Sun.COM 	tpm_quiesce
2049126SWyllys.Ingersoll@Sun.COM };
2059126SWyllys.Ingersoll@Sun.COM 
2069126SWyllys.Ingersoll@Sun.COM /* modldrv structure */
2079126SWyllys.Ingersoll@Sun.COM static struct modldrv modldrv = {
2089126SWyllys.Ingersoll@Sun.COM 	&mod_driverops,		/* Type: This is a driver */
2099126SWyllys.Ingersoll@Sun.COM 	"TPM 1.2 driver",	/* Name of the module. */
2109126SWyllys.Ingersoll@Sun.COM 	&tpm_dev_ops
2119126SWyllys.Ingersoll@Sun.COM };
2129126SWyllys.Ingersoll@Sun.COM 
2139126SWyllys.Ingersoll@Sun.COM /* modlinkage structure */
2149126SWyllys.Ingersoll@Sun.COM static struct modlinkage tpm_ml = {
2159126SWyllys.Ingersoll@Sun.COM 	MODREV_1,
2169126SWyllys.Ingersoll@Sun.COM 	&modldrv,
2179126SWyllys.Ingersoll@Sun.COM 	NULL
2189126SWyllys.Ingersoll@Sun.COM };
2199126SWyllys.Ingersoll@Sun.COM 
220*10346Swyllys.ingersoll@sun.com 
221*10346Swyllys.ingersoll@sun.com #ifdef KCF_TPM_RNG_PROVIDER
222*10346Swyllys.ingersoll@sun.com 
223*10346Swyllys.ingersoll@sun.com #define	IDENT_TPMRNG	"TPM Random Number Generator"
224*10346Swyllys.ingersoll@sun.com 
225*10346Swyllys.ingersoll@sun.com #include <sys/crypto/common.h>
226*10346Swyllys.ingersoll@sun.com #include <sys/crypto/impl.h>
227*10346Swyllys.ingersoll@sun.com #include <sys/crypto/spi.h>
228*10346Swyllys.ingersoll@sun.com /*
229*10346Swyllys.ingersoll@sun.com  * CSPI information (entry points, provider info, etc.)
230*10346Swyllys.ingersoll@sun.com  */
231*10346Swyllys.ingersoll@sun.com static void tpmrng_provider_status(crypto_provider_handle_t, uint_t *);
232*10346Swyllys.ingersoll@sun.com 
233*10346Swyllys.ingersoll@sun.com static crypto_control_ops_t tpmrng_control_ops = {
234*10346Swyllys.ingersoll@sun.com 	tpmrng_provider_status
235*10346Swyllys.ingersoll@sun.com };
236*10346Swyllys.ingersoll@sun.com 
237*10346Swyllys.ingersoll@sun.com static int tpmrng_seed_random(crypto_provider_handle_t, crypto_session_id_t,
238*10346Swyllys.ingersoll@sun.com     uchar_t *, size_t, uint_t, uint32_t, crypto_req_handle_t);
239*10346Swyllys.ingersoll@sun.com 
240*10346Swyllys.ingersoll@sun.com static int tpmrng_generate_random(crypto_provider_handle_t,
241*10346Swyllys.ingersoll@sun.com     crypto_session_id_t, uchar_t *, size_t, crypto_req_handle_t);
242*10346Swyllys.ingersoll@sun.com 
243*10346Swyllys.ingersoll@sun.com static crypto_random_number_ops_t tpmrng_random_number_ops = {
244*10346Swyllys.ingersoll@sun.com 	tpmrng_seed_random,
245*10346Swyllys.ingersoll@sun.com 	tpmrng_generate_random
246*10346Swyllys.ingersoll@sun.com };
247*10346Swyllys.ingersoll@sun.com 
248*10346Swyllys.ingersoll@sun.com static int tpmrng_ext_info(crypto_provider_handle_t,
249*10346Swyllys.ingersoll@sun.com 	crypto_provider_ext_info_t *,
250*10346Swyllys.ingersoll@sun.com 	crypto_req_handle_t);
251*10346Swyllys.ingersoll@sun.com 
252*10346Swyllys.ingersoll@sun.com static crypto_provider_management_ops_t tpmrng_extinfo_op = {
253*10346Swyllys.ingersoll@sun.com 	tpmrng_ext_info,
254*10346Swyllys.ingersoll@sun.com 	NULL,
255*10346Swyllys.ingersoll@sun.com 	NULL,
256*10346Swyllys.ingersoll@sun.com 	NULL
257*10346Swyllys.ingersoll@sun.com };
258*10346Swyllys.ingersoll@sun.com 
259*10346Swyllys.ingersoll@sun.com static int tpmrng_register(tpm_state_t *);
260*10346Swyllys.ingersoll@sun.com static int tpmrng_unregister(tpm_state_t *);
261*10346Swyllys.ingersoll@sun.com 
262*10346Swyllys.ingersoll@sun.com static crypto_ops_t tpmrng_crypto_ops = {
263*10346Swyllys.ingersoll@sun.com 	&tpmrng_control_ops,
264*10346Swyllys.ingersoll@sun.com 	NULL,
265*10346Swyllys.ingersoll@sun.com 	NULL,
266*10346Swyllys.ingersoll@sun.com 	NULL,
267*10346Swyllys.ingersoll@sun.com 	NULL,
268*10346Swyllys.ingersoll@sun.com 	NULL,
269*10346Swyllys.ingersoll@sun.com 	NULL,
270*10346Swyllys.ingersoll@sun.com 	NULL,
271*10346Swyllys.ingersoll@sun.com 	&tpmrng_random_number_ops,
272*10346Swyllys.ingersoll@sun.com 	NULL,
273*10346Swyllys.ingersoll@sun.com 	NULL,
274*10346Swyllys.ingersoll@sun.com 	NULL,
275*10346Swyllys.ingersoll@sun.com 	&tpmrng_extinfo_op,
276*10346Swyllys.ingersoll@sun.com 	NULL,
277*10346Swyllys.ingersoll@sun.com 	NULL
278*10346Swyllys.ingersoll@sun.com };
279*10346Swyllys.ingersoll@sun.com 
280*10346Swyllys.ingersoll@sun.com static crypto_provider_info_t tpmrng_prov_info = {
281*10346Swyllys.ingersoll@sun.com 	CRYPTO_SPI_VERSION_2,
282*10346Swyllys.ingersoll@sun.com 	"TPM Random Number Provider",
283*10346Swyllys.ingersoll@sun.com 	CRYPTO_HW_PROVIDER,
284*10346Swyllys.ingersoll@sun.com 	NULL,
285*10346Swyllys.ingersoll@sun.com 	NULL,
286*10346Swyllys.ingersoll@sun.com 	&tpmrng_crypto_ops,
287*10346Swyllys.ingersoll@sun.com 	0,
288*10346Swyllys.ingersoll@sun.com 	NULL,
289*10346Swyllys.ingersoll@sun.com 	0,
290*10346Swyllys.ingersoll@sun.com 	NULL
291*10346Swyllys.ingersoll@sun.com };
292*10346Swyllys.ingersoll@sun.com #endif /* KCF_TPM_RNG_PROVIDER */
293*10346Swyllys.ingersoll@sun.com 
2949126SWyllys.Ingersoll@Sun.COM static void *statep = NULL;
2959126SWyllys.Ingersoll@Sun.COM 
2969126SWyllys.Ingersoll@Sun.COM /*
297*10346Swyllys.ingersoll@sun.com  * Inline code to get exclusive lock on the TPM device and to make sure
298*10346Swyllys.ingersoll@sun.com  * the device is not suspended.  This grabs the primary TPM mutex (pm_mutex)
299*10346Swyllys.ingersoll@sun.com  * and then checks the suspend status.  If suspended, it will wait until
300*10346Swyllys.ingersoll@sun.com  * the device is "resumed" before releasing the pm_mutex and continuing.
301*10346Swyllys.ingersoll@sun.com  */
302*10346Swyllys.ingersoll@sun.com #define	TPM_EXCLUSIVE_LOCK(tpm)  { \
303*10346Swyllys.ingersoll@sun.com 	mutex_enter(&tpm->pm_mutex); \
304*10346Swyllys.ingersoll@sun.com 	while (tpm->suspended) \
305*10346Swyllys.ingersoll@sun.com 		cv_wait(&tpm->suspend_cv, &tpm->pm_mutex); \
306*10346Swyllys.ingersoll@sun.com 	mutex_exit(&tpm->pm_mutex); }
307*10346Swyllys.ingersoll@sun.com 
308*10346Swyllys.ingersoll@sun.com /*
309*10346Swyllys.ingersoll@sun.com  * TPM accessor functions
310*10346Swyllys.ingersoll@sun.com  */
311*10346Swyllys.ingersoll@sun.com #ifdef sun4v
312*10346Swyllys.ingersoll@sun.com 
313*10346Swyllys.ingersoll@sun.com extern uint64_t
314*10346Swyllys.ingersoll@sun.com hcall_tpm_get(uint64_t, uint64_t, uint64_t, uint64_t *);
315*10346Swyllys.ingersoll@sun.com 
316*10346Swyllys.ingersoll@sun.com extern uint64_t
317*10346Swyllys.ingersoll@sun.com hcall_tpm_put(uint64_t, uint64_t, uint64_t, uint64_t);
318*10346Swyllys.ingersoll@sun.com 
319*10346Swyllys.ingersoll@sun.com static inline uint8_t
320*10346Swyllys.ingersoll@sun.com tpm_get8(tpm_state_t *tpm, unsigned long offset)
321*10346Swyllys.ingersoll@sun.com {
322*10346Swyllys.ingersoll@sun.com 	uint64_t value;
323*10346Swyllys.ingersoll@sun.com 
324*10346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
325*10346Swyllys.ingersoll@sun.com 	(void) hcall_tpm_get(tpm->locality, offset, sizeof (uint8_t), &value);
326*10346Swyllys.ingersoll@sun.com 	return ((uint8_t)value);
327*10346Swyllys.ingersoll@sun.com }
328*10346Swyllys.ingersoll@sun.com 
329*10346Swyllys.ingersoll@sun.com static inline uint32_t
330*10346Swyllys.ingersoll@sun.com tpm_get32(tpm_state_t *tpm, unsigned long offset)
331*10346Swyllys.ingersoll@sun.com {
332*10346Swyllys.ingersoll@sun.com 	uint64_t value;
333*10346Swyllys.ingersoll@sun.com 
334*10346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
335*10346Swyllys.ingersoll@sun.com 	(void) hcall_tpm_get(tpm->locality, offset, sizeof (uint32_t), &value);
336*10346Swyllys.ingersoll@sun.com 	return ((uint32_t)value);
337*10346Swyllys.ingersoll@sun.com }
338*10346Swyllys.ingersoll@sun.com 
339*10346Swyllys.ingersoll@sun.com static inline void
340*10346Swyllys.ingersoll@sun.com tpm_put8(tpm_state_t *tpm, unsigned long offset, uint8_t value)
341*10346Swyllys.ingersoll@sun.com {
342*10346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
343*10346Swyllys.ingersoll@sun.com 	(void) hcall_tpm_put(tpm->locality, offset, sizeof (uint8_t), value);
344*10346Swyllys.ingersoll@sun.com }
345*10346Swyllys.ingersoll@sun.com 
346*10346Swyllys.ingersoll@sun.com #else
347*10346Swyllys.ingersoll@sun.com 
348*10346Swyllys.ingersoll@sun.com static inline uint8_t
349*10346Swyllys.ingersoll@sun.com tpm_get8(tpm_state_t *tpm, unsigned long offset)
350*10346Swyllys.ingersoll@sun.com {
351*10346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
352*10346Swyllys.ingersoll@sun.com 
353*10346Swyllys.ingersoll@sun.com 	return (ddi_get8(tpm->handle,
354*10346Swyllys.ingersoll@sun.com 	    (uint8_t *)(TPM_LOCALITY_OFFSET(tpm->locality) |
355*10346Swyllys.ingersoll@sun.com 	    (uintptr_t)tpm->addr + offset)));
356*10346Swyllys.ingersoll@sun.com }
357*10346Swyllys.ingersoll@sun.com 
358*10346Swyllys.ingersoll@sun.com static inline uint32_t
359*10346Swyllys.ingersoll@sun.com tpm_get32(tpm_state_t *tpm, unsigned long offset)
360*10346Swyllys.ingersoll@sun.com {
361*10346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
362*10346Swyllys.ingersoll@sun.com 	return (ddi_get32(tpm->handle,
363*10346Swyllys.ingersoll@sun.com 	    (uint32_t *)(TPM_LOCALITY_OFFSET(tpm->locality) |
364*10346Swyllys.ingersoll@sun.com 	    (uintptr_t)tpm->addr + offset)));
365*10346Swyllys.ingersoll@sun.com }
366*10346Swyllys.ingersoll@sun.com 
367*10346Swyllys.ingersoll@sun.com static inline void
368*10346Swyllys.ingersoll@sun.com tpm_put8(tpm_state_t *tpm, unsigned long offset, uint8_t value)
369*10346Swyllys.ingersoll@sun.com {
370*10346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
371*10346Swyllys.ingersoll@sun.com 	ddi_put8(tpm->handle,
372*10346Swyllys.ingersoll@sun.com 	    (uint8_t *)(TPM_LOCALITY_OFFSET(tpm->locality) |
373*10346Swyllys.ingersoll@sun.com 	    (uintptr_t)tpm->addr + offset), value);
374*10346Swyllys.ingersoll@sun.com }
375*10346Swyllys.ingersoll@sun.com 
376*10346Swyllys.ingersoll@sun.com #endif /* sun4v */
377*10346Swyllys.ingersoll@sun.com 
378*10346Swyllys.ingersoll@sun.com /*
3799126SWyllys.Ingersoll@Sun.COM  * TPM commands to get the TPM's properties, e.g.,timeout
3809126SWyllys.Ingersoll@Sun.COM  */
3819126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
3829126SWyllys.Ingersoll@Sun.COM static int
3839126SWyllys.Ingersoll@Sun.COM tpm_quiesce(dev_info_t *dip)
3849126SWyllys.Ingersoll@Sun.COM {
3859126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
3869126SWyllys.Ingersoll@Sun.COM }
3879126SWyllys.Ingersoll@Sun.COM 
3889126SWyllys.Ingersoll@Sun.COM static uint32_t
3899126SWyllys.Ingersoll@Sun.COM load32(uchar_t *ptr, uint32_t offset)
3909126SWyllys.Ingersoll@Sun.COM {
3919126SWyllys.Ingersoll@Sun.COM 	uint32_t val;
3929126SWyllys.Ingersoll@Sun.COM 	bcopy(ptr + offset, &val, sizeof (uint32_t));
3939126SWyllys.Ingersoll@Sun.COM 
3949126SWyllys.Ingersoll@Sun.COM 	return (ntohl(val));
3959126SWyllys.Ingersoll@Sun.COM }
3969126SWyllys.Ingersoll@Sun.COM 
3979126SWyllys.Ingersoll@Sun.COM /*
3989126SWyllys.Ingersoll@Sun.COM  * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
3999126SWyllys.Ingersoll@Sun.COM  * with the subcommand TPM_CAP_PROP_TIS_TIMEOUT
4009126SWyllys.Ingersoll@Sun.COM  * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
4019126SWyllys.Ingersoll@Sun.COM  */
4029126SWyllys.Ingersoll@Sun.COM static int
4039126SWyllys.Ingersoll@Sun.COM tpm_get_timeouts(tpm_state_t *tpm)
4049126SWyllys.Ingersoll@Sun.COM {
4059126SWyllys.Ingersoll@Sun.COM 	int ret;
4069126SWyllys.Ingersoll@Sun.COM 	uint32_t timeout;   /* in milliseconds */
4079126SWyllys.Ingersoll@Sun.COM 	uint32_t len;
4089126SWyllys.Ingersoll@Sun.COM 
4099126SWyllys.Ingersoll@Sun.COM 	/* The buffer size (30) needs room for 4 timeout values (uint32_t) */
4109126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[30] = {
4119126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU_COMMAND */
4129126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 22,	/* paramsize in bytes */
4139126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 101,	/* TPM_ORD_GetCapability */
4149126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 5,	/* TPM_CAP_Prop */
4159126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 4,	/* SUB_CAP size in bytes */
4169126SWyllys.Ingersoll@Sun.COM 		0, 0, 1, 21	/* TPM_CAP_PROP_TIS_TIMEOUT(0x115) */
4179126SWyllys.Ingersoll@Sun.COM 	};
4189126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_timeout";
4199126SWyllys.Ingersoll@Sun.COM 
4209126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
4219126SWyllys.Ingersoll@Sun.COM 
4229126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
4239126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
4249126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: itpm_command failed", myname);
4259126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
4269126SWyllys.Ingersoll@Sun.COM 	}
4279126SWyllys.Ingersoll@Sun.COM 
4289126SWyllys.Ingersoll@Sun.COM 	/*
4299126SWyllys.Ingersoll@Sun.COM 	 * Get the length of the returned buffer
4309126SWyllys.Ingersoll@Sun.COM 	 * Make sure that there are 4 timeout values returned
4319126SWyllys.Ingersoll@Sun.COM 	 * length of the capability response is stored in data[10-13]
4329126SWyllys.Ingersoll@Sun.COM 	 * Also the TPM is in network byte order
4339126SWyllys.Ingersoll@Sun.COM 	 */
4349126SWyllys.Ingersoll@Sun.COM 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
4359126SWyllys.Ingersoll@Sun.COM 	if (len != 4 * sizeof (uint32_t)) {
4369126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: capability response size should be %d"
4379126SWyllys.Ingersoll@Sun.COM 		    "instead it's %d",
4389126SWyllys.Ingersoll@Sun.COM 		    myname, (int)(4 * sizeof (uint32_t)), (int)len);
4399126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
4409126SWyllys.Ingersoll@Sun.COM 	}
4419126SWyllys.Ingersoll@Sun.COM 
4429126SWyllys.Ingersoll@Sun.COM 	/* Get the four timeout's: a,b,c,d (they are 4 bytes long each) */
4439126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_A_OFFSET);
4449126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
4459126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_A;
4469126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
4479126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
4489126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
4499126SWyllys.Ingersoll@Sun.COM 	}
4509126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_a = drv_usectohz(timeout);
4519126SWyllys.Ingersoll@Sun.COM 
4529126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_B_OFFSET);
4539126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
4549126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_B;
4559126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
4569126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
4579126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
4589126SWyllys.Ingersoll@Sun.COM 	}
4599126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_b = drv_usectohz(timeout);
4609126SWyllys.Ingersoll@Sun.COM 
4619126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_C_OFFSET);
4629126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
4639126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_C;
4649126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
4659126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
4669126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
4679126SWyllys.Ingersoll@Sun.COM 	}
4689126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_c = drv_usectohz(timeout);
4699126SWyllys.Ingersoll@Sun.COM 
4709126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_D_OFFSET);
4719126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
4729126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_D;
4739126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
4749126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
4759126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
4769126SWyllys.Ingersoll@Sun.COM 	}
4779126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_d = drv_usectohz(timeout);
4789126SWyllys.Ingersoll@Sun.COM 
4799126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
4809126SWyllys.Ingersoll@Sun.COM }
4819126SWyllys.Ingersoll@Sun.COM 
4829126SWyllys.Ingersoll@Sun.COM /*
4839126SWyllys.Ingersoll@Sun.COM  * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
4849126SWyllys.Ingersoll@Sun.COM  * with the subcommand TPM_CAP_PROP_TIS_DURATION
4859126SWyllys.Ingersoll@Sun.COM  * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
4869126SWyllys.Ingersoll@Sun.COM  */
4879126SWyllys.Ingersoll@Sun.COM static int
4889126SWyllys.Ingersoll@Sun.COM tpm_get_duration(tpm_state_t *tpm) {
4899126SWyllys.Ingersoll@Sun.COM 	int ret;
4909126SWyllys.Ingersoll@Sun.COM 	uint32_t duration;
4919126SWyllys.Ingersoll@Sun.COM 	uint32_t len;
4929126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[30] = {
4939126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU_COMMAND */
4949126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 22,	/* paramsize in bytes */
4959126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 101,	/* TPM_ORD_GetCapability */
4969126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 5,	/* TPM_CAP_Prop */
4979126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 4,	/* SUB_CAP size in bytes */
4989126SWyllys.Ingersoll@Sun.COM 		0, 0, 1, 32	/* TPM_CAP_PROP_TIS_DURATION(0x120) */
4999126SWyllys.Ingersoll@Sun.COM 	};
5009126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_duration";
5019126SWyllys.Ingersoll@Sun.COM 
5029126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
5039126SWyllys.Ingersoll@Sun.COM 
5049126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
5059126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
5069126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: itpm_command failed with ret code: 0x%x",
5079126SWyllys.Ingersoll@Sun.COM 			myname, ret);
5089126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
5099126SWyllys.Ingersoll@Sun.COM 	}
5109126SWyllys.Ingersoll@Sun.COM 
5119126SWyllys.Ingersoll@Sun.COM 	/*
5129126SWyllys.Ingersoll@Sun.COM 	 * Get the length of the returned buffer
5139126SWyllys.Ingersoll@Sun.COM 	 * Make sure that there are 3 duration values (S,M,L: in that order)
5149126SWyllys.Ingersoll@Sun.COM 	 * length of the capability response is stored in data[10-13]
5159126SWyllys.Ingersoll@Sun.COM 	 * Also the TPM is in network byte order
5169126SWyllys.Ingersoll@Sun.COM 	 */
5179126SWyllys.Ingersoll@Sun.COM 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
5189126SWyllys.Ingersoll@Sun.COM 	if (len != 3 * sizeof (uint32_t)) {
5199126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: capability response should be %d, "
5209126SWyllys.Ingersoll@Sun.COM 		    "instead, it's %d",
5219126SWyllys.Ingersoll@Sun.COM 		    myname, (int)(3 * sizeof (uint32_t)), (int)len);
5229126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
5239126SWyllys.Ingersoll@Sun.COM 	}
5249126SWyllys.Ingersoll@Sun.COM 
5259126SWyllys.Ingersoll@Sun.COM 	duration = load32(buf, TPM_CAP_DUR_SHORT_OFFSET);
5269126SWyllys.Ingersoll@Sun.COM 	if (duration == 0) {
5279126SWyllys.Ingersoll@Sun.COM 		duration = DEFAULT_SHORT_DURATION;
5289126SWyllys.Ingersoll@Sun.COM 	} else if (duration < TEN_MILLISECONDS) {
5299126SWyllys.Ingersoll@Sun.COM 		duration *= 1000;
5309126SWyllys.Ingersoll@Sun.COM 	}
5319126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_SHORT] = drv_usectohz(duration);
5329126SWyllys.Ingersoll@Sun.COM 
5339126SWyllys.Ingersoll@Sun.COM 	duration = load32(buf, TPM_CAP_DUR_MEDIUM_OFFSET);
5349126SWyllys.Ingersoll@Sun.COM 	if (duration == 0) {
5359126SWyllys.Ingersoll@Sun.COM 		duration = DEFAULT_MEDIUM_DURATION;
5369126SWyllys.Ingersoll@Sun.COM 	} else if (duration < TEN_MILLISECONDS) {
5379126SWyllys.Ingersoll@Sun.COM 		duration *= 1000;
5389126SWyllys.Ingersoll@Sun.COM 	}
5399126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_MEDIUM] = drv_usectohz(duration);
5409126SWyllys.Ingersoll@Sun.COM 
5419126SWyllys.Ingersoll@Sun.COM 	duration = load32(buf, TPM_CAP_DUR_LONG_OFFSET);
5429126SWyllys.Ingersoll@Sun.COM 	if (duration == 0) {
5439126SWyllys.Ingersoll@Sun.COM 		duration = DEFAULT_LONG_DURATION;
5449126SWyllys.Ingersoll@Sun.COM 	} else if (duration < FOUR_HUNDRED_MILLISECONDS) {
5459126SWyllys.Ingersoll@Sun.COM 		duration *= 1000;
5469126SWyllys.Ingersoll@Sun.COM 	}
5479126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_LONG] = drv_usectohz(duration);
5489126SWyllys.Ingersoll@Sun.COM 
5499126SWyllys.Ingersoll@Sun.COM 	/* Just make the undefined duration be the same as the LONG */
5509126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_UNDEFINED] = tpm->duration[TPM_LONG];
5519126SWyllys.Ingersoll@Sun.COM 
5529126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
5539126SWyllys.Ingersoll@Sun.COM }
5549126SWyllys.Ingersoll@Sun.COM 
5559126SWyllys.Ingersoll@Sun.COM /*
5569126SWyllys.Ingersoll@Sun.COM  * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
5579126SWyllys.Ingersoll@Sun.COM  * with the subcommand TPM_CAP_PROP_TIS_DURATION
5589126SWyllys.Ingersoll@Sun.COM  * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
5599126SWyllys.Ingersoll@Sun.COM  */
5609126SWyllys.Ingersoll@Sun.COM static int
5619126SWyllys.Ingersoll@Sun.COM tpm_get_version(tpm_state_t *tpm) {
5629126SWyllys.Ingersoll@Sun.COM 	int ret;
5639126SWyllys.Ingersoll@Sun.COM 	uint32_t len;
5649126SWyllys.Ingersoll@Sun.COM 	char vendorId[5];
5659126SWyllys.Ingersoll@Sun.COM 	/* If this buf is too small, the "vendor specific" data won't fit */
5669126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[64] = {
5679126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU_COMMAND */
5689126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 18,	/* paramsize in bytes */
5699126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 101,	/* TPM_ORD_GetCapability */
5709126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 0x1A,	/* TPM_CAP_VERSION_VAL */
5719126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 0,	/* SUB_CAP size in bytes */
5729126SWyllys.Ingersoll@Sun.COM 	};
5739126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_version";
5749126SWyllys.Ingersoll@Sun.COM 
5759126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
5769126SWyllys.Ingersoll@Sun.COM 
5779126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
5789126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
5799126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: itpm_command failed with ret code: 0x%x",
5809126SWyllys.Ingersoll@Sun.COM 			myname, ret);
5819126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
5829126SWyllys.Ingersoll@Sun.COM 	}
5839126SWyllys.Ingersoll@Sun.COM 
5849126SWyllys.Ingersoll@Sun.COM 	/*
5859126SWyllys.Ingersoll@Sun.COM 	 * Get the length of the returned buffer.
5869126SWyllys.Ingersoll@Sun.COM 	 */
5879126SWyllys.Ingersoll@Sun.COM 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
5889126SWyllys.Ingersoll@Sun.COM 	if (len < TPM_CAP_VERSION_INFO_SIZE) {
5899126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: capability response should be greater"
5909126SWyllys.Ingersoll@Sun.COM 		    " than %d, instead, it's %d",
5919126SWyllys.Ingersoll@Sun.COM 		    myname,
5929126SWyllys.Ingersoll@Sun.COM 		    TPM_CAP_VERSION_INFO_SIZE,
5939126SWyllys.Ingersoll@Sun.COM 		    len);
5949126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
5959126SWyllys.Ingersoll@Sun.COM 	}
5969126SWyllys.Ingersoll@Sun.COM 
5979126SWyllys.Ingersoll@Sun.COM 	bcopy(buf + TPM_CAP_VERSION_INFO_OFFSET, &tpm->vers_info,
5989126SWyllys.Ingersoll@Sun.COM 	    TPM_CAP_VERSION_INFO_SIZE);
5999126SWyllys.Ingersoll@Sun.COM 
6009126SWyllys.Ingersoll@Sun.COM 	bcopy(tpm->vers_info.tpmVendorID, vendorId,
6019126SWyllys.Ingersoll@Sun.COM 	    sizeof (tpm->vers_info.tpmVendorID));
6029126SWyllys.Ingersoll@Sun.COM 	vendorId[4] = '\0';
6039126SWyllys.Ingersoll@Sun.COM 
6049126SWyllys.Ingersoll@Sun.COM 	cmn_err(CE_NOTE, "!TPM found: Ver %d.%d, Rev %d.%d, "
6059126SWyllys.Ingersoll@Sun.COM 	    "SpecLevel %d, errataRev %d, VendorId '%s'",
6069126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.major,	/* Version */
6079126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.minor,
6089126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.revMajor,	/* Revision */
6099126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.revMinor,
6109126SWyllys.Ingersoll@Sun.COM 	    (int)ntohs(tpm->vers_info.specLevel),
6119126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.errataRev,
6129126SWyllys.Ingersoll@Sun.COM 	    vendorId);
6139126SWyllys.Ingersoll@Sun.COM 
6149126SWyllys.Ingersoll@Sun.COM 	/*
6159126SWyllys.Ingersoll@Sun.COM 	 * This driver only supports TPM Version 1.2
6169126SWyllys.Ingersoll@Sun.COM 	 */
6179126SWyllys.Ingersoll@Sun.COM 	if (tpm->vers_info.version.major != 1 &&
6189126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.minor != 2) {
6199126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: Unsupported TPM version (%d.%d)",
6209126SWyllys.Ingersoll@Sun.COM 		    myname,
6219126SWyllys.Ingersoll@Sun.COM 		    tpm->vers_info.version.major,		/* Version */
6229126SWyllys.Ingersoll@Sun.COM 		    tpm->vers_info.version.minor);
6239126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
6249126SWyllys.Ingersoll@Sun.COM 	}
6259126SWyllys.Ingersoll@Sun.COM 
6269126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
6279126SWyllys.Ingersoll@Sun.COM }
6289126SWyllys.Ingersoll@Sun.COM 
6299126SWyllys.Ingersoll@Sun.COM /*
6309126SWyllys.Ingersoll@Sun.COM  * To prevent the TPM from complaining that certain functions are not tested
6319126SWyllys.Ingersoll@Sun.COM  * we run this command when the driver attaches.
6329126SWyllys.Ingersoll@Sun.COM  * For details see Section 4.2 of TPM Main Part 3 Command Specification
6339126SWyllys.Ingersoll@Sun.COM  */
6349126SWyllys.Ingersoll@Sun.COM static int
6359126SWyllys.Ingersoll@Sun.COM tpm_continue_selftest(tpm_state_t *tpm) {
6369126SWyllys.Ingersoll@Sun.COM 	int ret;
6379126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[10] = {
6389126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU COMMAND */
6399126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 10,	/* paramsize in bytes */
6409126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 83	/* TPM_ORD_ContinueSelfTest */
6419126SWyllys.Ingersoll@Sun.COM 	};
6429126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_continue_selftest";
6439126SWyllys.Ingersoll@Sun.COM 
6449126SWyllys.Ingersoll@Sun.COM 	/* Need a longer timeout */
6459126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
6469126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
6479126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: itpm_command failed", myname);
6489126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
6499126SWyllys.Ingersoll@Sun.COM 	}
6509126SWyllys.Ingersoll@Sun.COM 
6519126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
6529126SWyllys.Ingersoll@Sun.COM }
6539126SWyllys.Ingersoll@Sun.COM /*
6549126SWyllys.Ingersoll@Sun.COM  * Auxilary Functions
6559126SWyllys.Ingersoll@Sun.COM  */
6569126SWyllys.Ingersoll@Sun.COM 
6579126SWyllys.Ingersoll@Sun.COM /*
6589126SWyllys.Ingersoll@Sun.COM  * Find out how long we should wait for the TPM command to complete a command
6599126SWyllys.Ingersoll@Sun.COM  */
6609126SWyllys.Ingersoll@Sun.COM static clock_t
6619126SWyllys.Ingersoll@Sun.COM tpm_get_ordinal_duration(tpm_state_t *tpm, uint8_t ordinal)
6629126SWyllys.Ingersoll@Sun.COM {
6639126SWyllys.Ingersoll@Sun.COM 	uint8_t index;
6649126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_ordinal_duration";
6659126SWyllys.Ingersoll@Sun.COM 
6669126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
6679126SWyllys.Ingersoll@Sun.COM 
6689126SWyllys.Ingersoll@Sun.COM 	/* Default and failure case for IFX */
6699126SWyllys.Ingersoll@Sun.COM 	/* Is it a TSC_ORDINAL? */
6709126SWyllys.Ingersoll@Sun.COM 	if (ordinal & TSC_ORDINAL_MASK) {
6719126SWyllys.Ingersoll@Sun.COM 		if (ordinal > TSC_ORDINAL_MAX) {
6729126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN,
6739126SWyllys.Ingersoll@Sun.COM 			    "%s: tsc ordinal: %d exceeds MAX: %d",
6749126SWyllys.Ingersoll@Sun.COM 			    myname, ordinal, TSC_ORDINAL_MAX);
6759126SWyllys.Ingersoll@Sun.COM 			return (0);
6769126SWyllys.Ingersoll@Sun.COM 		}
6779126SWyllys.Ingersoll@Sun.COM 		index = tsc_ords_duration[ordinal];
6789126SWyllys.Ingersoll@Sun.COM 	} else {
6799126SWyllys.Ingersoll@Sun.COM 		if (ordinal > TPM_ORDINAL_MAX) {
6809126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN,
6819126SWyllys.Ingersoll@Sun.COM 			    "%s: ordinal %d exceeds MAX: %d",
6829126SWyllys.Ingersoll@Sun.COM 			    myname, ordinal, TPM_ORDINAL_MAX);
6839126SWyllys.Ingersoll@Sun.COM 			return (0);
6849126SWyllys.Ingersoll@Sun.COM 		}
6859126SWyllys.Ingersoll@Sun.COM 		index = tpm_ords_duration[ordinal];
6869126SWyllys.Ingersoll@Sun.COM 	}
6879126SWyllys.Ingersoll@Sun.COM 
6889126SWyllys.Ingersoll@Sun.COM 	if (index > TPM_DURATION_MAX_IDX) {
6899126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: FATAL:index '%d' is out of bound",
6909126SWyllys.Ingersoll@Sun.COM 		    myname, index);
6919126SWyllys.Ingersoll@Sun.COM 		return (0);
6929126SWyllys.Ingersoll@Sun.COM 	}
6939126SWyllys.Ingersoll@Sun.COM 	return (tpm->duration[index]);
6949126SWyllys.Ingersoll@Sun.COM }
6959126SWyllys.Ingersoll@Sun.COM 
6969126SWyllys.Ingersoll@Sun.COM /*
6979126SWyllys.Ingersoll@Sun.COM  * Internal TPM Transmit Function:
6989126SWyllys.Ingersoll@Sun.COM  * Calls implementation specific sendto and receive
6999126SWyllys.Ingersoll@Sun.COM  * The code assumes that the buffer is in network byte order
7009126SWyllys.Ingersoll@Sun.COM  */
7019126SWyllys.Ingersoll@Sun.COM static int
7029126SWyllys.Ingersoll@Sun.COM itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz)
7039126SWyllys.Ingersoll@Sun.COM {
7049126SWyllys.Ingersoll@Sun.COM 	int ret;
7059126SWyllys.Ingersoll@Sun.COM 	uint32_t count;
7069126SWyllys.Ingersoll@Sun.COM 	char *myname = "itpm_command";
7079126SWyllys.Ingersoll@Sun.COM 
7089126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
7099126SWyllys.Ingersoll@Sun.COM 
7109126SWyllys.Ingersoll@Sun.COM 	/* The byte order is network byte order so convert it */
7119126SWyllys.Ingersoll@Sun.COM 	count = load32(buf, TPM_PARAMSIZE_OFFSET);
7129126SWyllys.Ingersoll@Sun.COM 
7139126SWyllys.Ingersoll@Sun.COM 	if (count == 0) {
7149126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: count=0, no data? %d", myname,
7159126SWyllys.Ingersoll@Sun.COM 		    (int)bufsiz);
7169126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
7179126SWyllys.Ingersoll@Sun.COM 	}
7189126SWyllys.Ingersoll@Sun.COM 	if (count > bufsiz) {
7199126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: invalid count value:count:%d > bufsiz %d",
7209126SWyllys.Ingersoll@Sun.COM 		    myname, (int)count, (int)bufsiz);
7219126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
7229126SWyllys.Ingersoll@Sun.COM 	}
7239126SWyllys.Ingersoll@Sun.COM 
7249126SWyllys.Ingersoll@Sun.COM 	/* Send the command */
7259126SWyllys.Ingersoll@Sun.COM 	ret = tis_send_data(tpm, buf, count);
7269126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
7279126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_send_data failed with error %x",
7289126SWyllys.Ingersoll@Sun.COM 		    myname, ret);
7299126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
7309126SWyllys.Ingersoll@Sun.COM 	}
7319126SWyllys.Ingersoll@Sun.COM 
7329126SWyllys.Ingersoll@Sun.COM 	/*
7339126SWyllys.Ingersoll@Sun.COM 	 * Now receive the data from the tpm
7349126SWyllys.Ingersoll@Sun.COM 	 * Should at least receive "the common" 10 bytes (TPM_HEADER_SIZE)
7359126SWyllys.Ingersoll@Sun.COM 	 */
7369126SWyllys.Ingersoll@Sun.COM 	ret = tis_recv_data(tpm, buf, bufsiz);
7379126SWyllys.Ingersoll@Sun.COM 	if (ret < TPM_HEADER_SIZE) {
7389126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_recv_data failed", myname);
7399126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
7409126SWyllys.Ingersoll@Sun.COM 	}
7419126SWyllys.Ingersoll@Sun.COM 
7429126SWyllys.Ingersoll@Sun.COM 	/* Check the return code */
7439126SWyllys.Ingersoll@Sun.COM 	ret = load32(buf, TPM_RETURN_OFFSET);
7449126SWyllys.Ingersoll@Sun.COM 	if (ret != TPM_SUCCESS) {
745*10346Swyllys.ingersoll@sun.com 		if (ret == TPM_E_DEACTIVATED)
746*10346Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "%s: TPM is deactivated", myname);
747*10346Swyllys.ingersoll@sun.com 		else if (ret == TPM_E_DISABLED)
748*10346Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "%s: TPM is disabled", myname);
749*10346Swyllys.ingersoll@sun.com 		else
750*10346Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "%s: TPM error code 0x%0x",
751*10346Swyllys.ingersoll@sun.com 			    myname, ret);
7529126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
7539126SWyllys.Ingersoll@Sun.COM 	}
7549126SWyllys.Ingersoll@Sun.COM 
7559126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
7569126SWyllys.Ingersoll@Sun.COM }
7579126SWyllys.Ingersoll@Sun.COM 
7589126SWyllys.Ingersoll@Sun.COM /*
7599126SWyllys.Ingersoll@Sun.COM  * Whenever the driver wants to write to the DATA_IO register, it must need
7609126SWyllys.Ingersoll@Sun.COM  * to figure out the burstcount.  This is the amount of bytes it can write
7619126SWyllys.Ingersoll@Sun.COM  * before having to wait for long LPC bus cycle
7629126SWyllys.Ingersoll@Sun.COM  *
7639126SWyllys.Ingersoll@Sun.COM  * Returns: 0 if error, burst count if sucess
7649126SWyllys.Ingersoll@Sun.COM  */
7659126SWyllys.Ingersoll@Sun.COM static uint16_t
7669126SWyllys.Ingersoll@Sun.COM tpm_get_burstcount(tpm_state_t *tpm) {
7679126SWyllys.Ingersoll@Sun.COM 	clock_t stop;
7689126SWyllys.Ingersoll@Sun.COM 	uint16_t burstcnt;
7699126SWyllys.Ingersoll@Sun.COM 
7709126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
7719126SWyllys.Ingersoll@Sun.COM 
7729126SWyllys.Ingersoll@Sun.COM 	/*
7739126SWyllys.Ingersoll@Sun.COM 	 * Spec says timeout should be TIMEOUT_D
7749126SWyllys.Ingersoll@Sun.COM 	 * burst count is TPM_STS bits 8..23
7759126SWyllys.Ingersoll@Sun.COM 	 */
7769126SWyllys.Ingersoll@Sun.COM 	stop = ddi_get_lbolt() + tpm->timeout_d;
7779126SWyllys.Ingersoll@Sun.COM 	do {
7789126SWyllys.Ingersoll@Sun.COM 		/*
7799126SWyllys.Ingersoll@Sun.COM 		 * burstcnt is stored as a little endian value
7809126SWyllys.Ingersoll@Sun.COM 		 * 'ntohs' doesn't work since the value is not word-aligned
7819126SWyllys.Ingersoll@Sun.COM 		 */
782*10346Swyllys.ingersoll@sun.com 		burstcnt = tpm_get8(tpm, TPM_STS + 1);
783*10346Swyllys.ingersoll@sun.com 		burstcnt += tpm_get8(tpm, TPM_STS + 2) << 8;
7849126SWyllys.Ingersoll@Sun.COM 
7859126SWyllys.Ingersoll@Sun.COM 		if (burstcnt)
7869126SWyllys.Ingersoll@Sun.COM 			return (burstcnt);
7879126SWyllys.Ingersoll@Sun.COM 
7889126SWyllys.Ingersoll@Sun.COM 		delay(tpm->timeout_poll);
7899126SWyllys.Ingersoll@Sun.COM 	} while (ddi_get_lbolt() < stop);
7909126SWyllys.Ingersoll@Sun.COM 
7919126SWyllys.Ingersoll@Sun.COM 	return (0);
7929126SWyllys.Ingersoll@Sun.COM }
7939126SWyllys.Ingersoll@Sun.COM 
7949126SWyllys.Ingersoll@Sun.COM /*
7959126SWyllys.Ingersoll@Sun.COM  * Writing 1 to TPM_STS_CMD_READY bit in TPM_STS will do the following:
7969126SWyllys.Ingersoll@Sun.COM  * 1. The TPM will clears IO buffers if any
7979126SWyllys.Ingersoll@Sun.COM  * 2. The TPM will enters either Idle or Ready state within TIMEOUT_B
7989126SWyllys.Ingersoll@Sun.COM  * (checked in the calling function)
7999126SWyllys.Ingersoll@Sun.COM  */
8009126SWyllys.Ingersoll@Sun.COM static void
8019126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm_state_t *tpm) {
802*10346Swyllys.ingersoll@sun.com 	tpm_put8(tpm, TPM_STS, TPM_STS_CMD_READY);
8039126SWyllys.Ingersoll@Sun.COM }
8049126SWyllys.Ingersoll@Sun.COM 
8059126SWyllys.Ingersoll@Sun.COM static int
8069126SWyllys.Ingersoll@Sun.COM receive_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
8079126SWyllys.Ingersoll@Sun.COM 	int size = 0;
8089126SWyllys.Ingersoll@Sun.COM 	int retried = 0;
8099126SWyllys.Ingersoll@Sun.COM 	uint8_t stsbits;
8109126SWyllys.Ingersoll@Sun.COM 
8119126SWyllys.Ingersoll@Sun.COM 	/* A number of consecutive bytes that can be written to TPM */
8129126SWyllys.Ingersoll@Sun.COM 	uint16_t burstcnt;
8139126SWyllys.Ingersoll@Sun.COM 
8149126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
8159126SWyllys.Ingersoll@Sun.COM retry:
8169126SWyllys.Ingersoll@Sun.COM 	while (size < bufsiz &&
8179126SWyllys.Ingersoll@Sun.COM 		(tpm_wait_for_stat(tpm,
8189126SWyllys.Ingersoll@Sun.COM 		    (TPM_STS_DATA_AVAIL|TPM_STS_VALID),
819*10346Swyllys.ingersoll@sun.com 		    tpm->timeout_c) == DDI_SUCCESS)) {
8209126SWyllys.Ingersoll@Sun.COM 		/*
8219126SWyllys.Ingersoll@Sun.COM 		 * Burstcount should be available within TIMEOUT_D
8229126SWyllys.Ingersoll@Sun.COM 		 * after STS is set to valid
8239126SWyllys.Ingersoll@Sun.COM 		 * burstcount is dynamic, so have to get it each time
8249126SWyllys.Ingersoll@Sun.COM 		 */
8259126SWyllys.Ingersoll@Sun.COM 		burstcnt = tpm_get_burstcount(tpm);
8269126SWyllys.Ingersoll@Sun.COM 		for (; burstcnt > 0 && size < bufsiz; burstcnt--) {
827*10346Swyllys.ingersoll@sun.com 			buf[size++] = tpm_get8(tpm, TPM_DATA_FIFO);
8289126SWyllys.Ingersoll@Sun.COM 		}
8299126SWyllys.Ingersoll@Sun.COM 	}
8309126SWyllys.Ingersoll@Sun.COM 	stsbits = tis_get_status(tpm);
8319126SWyllys.Ingersoll@Sun.COM 	/* check to see if we need to retry (just once) */
8329126SWyllys.Ingersoll@Sun.COM 	if (size < bufsiz && !(stsbits & TPM_STS_DATA_AVAIL) && retried == 0) {
8339126SWyllys.Ingersoll@Sun.COM 		/* issue responseRetry (TIS 1.2 pg 54) */
834*10346Swyllys.ingersoll@sun.com 		tpm_put8(tpm, TPM_STS, TPM_STS_RESPONSE_RETRY);
8359126SWyllys.Ingersoll@Sun.COM 		/* update the retry counter so we only retry once */
8369126SWyllys.Ingersoll@Sun.COM 		retried++;
8379126SWyllys.Ingersoll@Sun.COM 		/* reset the size to 0 and reread the entire response */
8389126SWyllys.Ingersoll@Sun.COM 		size = 0;
8399126SWyllys.Ingersoll@Sun.COM 		goto retry;
8409126SWyllys.Ingersoll@Sun.COM 	}
8419126SWyllys.Ingersoll@Sun.COM 	return (size);
8429126SWyllys.Ingersoll@Sun.COM }
8439126SWyllys.Ingersoll@Sun.COM 
8449126SWyllys.Ingersoll@Sun.COM /* Receive the data from the TPM */
8459126SWyllys.Ingersoll@Sun.COM static int
8469126SWyllys.Ingersoll@Sun.COM tis_recv_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
8479126SWyllys.Ingersoll@Sun.COM 	int ret;
8489126SWyllys.Ingersoll@Sun.COM 	int size = 0;
8499126SWyllys.Ingersoll@Sun.COM 	uint32_t expected, status;
8509126SWyllys.Ingersoll@Sun.COM 	uint32_t cmdresult;
8519126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_recv_data";
8529126SWyllys.Ingersoll@Sun.COM 
8539126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
8549126SWyllys.Ingersoll@Sun.COM 
8559126SWyllys.Ingersoll@Sun.COM 	if (bufsiz < TPM_HEADER_SIZE) {
8569126SWyllys.Ingersoll@Sun.COM 		/* There should be at least tag,paramsize,return code */
8579126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: received data should contain at least "
8589126SWyllys.Ingersoll@Sun.COM 		    "the header which is %d bytes long",
8599126SWyllys.Ingersoll@Sun.COM 		    myname, TPM_HEADER_SIZE);
8609126SWyllys.Ingersoll@Sun.COM 		goto OUT;
8619126SWyllys.Ingersoll@Sun.COM 	}
8629126SWyllys.Ingersoll@Sun.COM 
8639126SWyllys.Ingersoll@Sun.COM 	/* Read tag(2 bytes), paramsize(4), and result(4) */
8649126SWyllys.Ingersoll@Sun.COM 	size = receive_data(tpm, buf, TPM_HEADER_SIZE);
8659126SWyllys.Ingersoll@Sun.COM 	if (size < TPM_HEADER_SIZE) {
8669126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: getting the TPM_HEADER failed: size=%d",
8679126SWyllys.Ingersoll@Sun.COM 		    myname, size);
8689126SWyllys.Ingersoll@Sun.COM 		goto OUT;
8699126SWyllys.Ingersoll@Sun.COM 	}
8709126SWyllys.Ingersoll@Sun.COM 
8719126SWyllys.Ingersoll@Sun.COM 	cmdresult = load32(buf, TPM_RETURN_OFFSET);
8729126SWyllys.Ingersoll@Sun.COM 
8739126SWyllys.Ingersoll@Sun.COM 	/* Get 'paramsize'(4 bytes)--it includes tag and paramsize */
8749126SWyllys.Ingersoll@Sun.COM 	expected = load32(buf, TPM_PARAMSIZE_OFFSET);
8759126SWyllys.Ingersoll@Sun.COM 	if (expected > bufsiz) {
8769126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: paramSize is bigger "
8779126SWyllys.Ingersoll@Sun.COM 		    "than the requested size: paramSize=%d bufsiz=%d result=%d",
8789126SWyllys.Ingersoll@Sun.COM 		    myname, (int)expected, (int)bufsiz, cmdresult);
8799126SWyllys.Ingersoll@Sun.COM 		goto OUT;
8809126SWyllys.Ingersoll@Sun.COM 	}
8819126SWyllys.Ingersoll@Sun.COM 
8829126SWyllys.Ingersoll@Sun.COM 	/* Read in the rest of the data from the TPM */
8839126SWyllys.Ingersoll@Sun.COM 	size += receive_data(tpm, (uint8_t *)&buf[TPM_HEADER_SIZE],
8849126SWyllys.Ingersoll@Sun.COM 	    expected - TPM_HEADER_SIZE);
8859126SWyllys.Ingersoll@Sun.COM 	if (size < expected) {
8869126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: received data length=%d "
8879126SWyllys.Ingersoll@Sun.COM 		    "is less than expected = %d", myname, size, expected);
8889126SWyllys.Ingersoll@Sun.COM 		goto OUT;
8899126SWyllys.Ingersoll@Sun.COM 	}
8909126SWyllys.Ingersoll@Sun.COM 
8919126SWyllys.Ingersoll@Sun.COM 	/* The TPM MUST set the state to stsValid within TIMEOUT_C */
892*10346Swyllys.ingersoll@sun.com 	ret = tpm_wait_for_stat(tpm, TPM_STS_VALID, tpm->timeout_c);
8939126SWyllys.Ingersoll@Sun.COM 
8949126SWyllys.Ingersoll@Sun.COM 	status = tis_get_status(tpm);
8959126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
8969126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: TPM didn't set stsValid after its I/O: "
8979126SWyllys.Ingersoll@Sun.COM 		    "status = 0x%08X", myname, status);
8989126SWyllys.Ingersoll@Sun.COM 		goto OUT;
8999126SWyllys.Ingersoll@Sun.COM 	}
9009126SWyllys.Ingersoll@Sun.COM 
9019126SWyllys.Ingersoll@Sun.COM 	/* There is still more data? */
9029126SWyllys.Ingersoll@Sun.COM 	if (status & TPM_STS_DATA_AVAIL) {
9039126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: Status TPM_STS_DATA_AVAIL set:0x%08X",
9049126SWyllys.Ingersoll@Sun.COM 		    myname, status);
9059126SWyllys.Ingersoll@Sun.COM 		goto OUT;
9069126SWyllys.Ingersoll@Sun.COM 	}
9079126SWyllys.Ingersoll@Sun.COM 
9089126SWyllys.Ingersoll@Sun.COM 	/*
9099126SWyllys.Ingersoll@Sun.COM 	 * Release the control of the TPM after we are done with it
9109126SWyllys.Ingersoll@Sun.COM 	 * it...so others can also get a chance to send data
9119126SWyllys.Ingersoll@Sun.COM 	 */
9129126SWyllys.Ingersoll@Sun.COM 	tis_release_locality(tpm, tpm->locality, 0);
9139126SWyllys.Ingersoll@Sun.COM 
9149126SWyllys.Ingersoll@Sun.COM OUT:
9159126SWyllys.Ingersoll@Sun.COM 	tpm_set_ready(tpm);
9169126SWyllys.Ingersoll@Sun.COM 	tis_release_locality(tpm, tpm->locality, 0);
9179126SWyllys.Ingersoll@Sun.COM 	return (size);
9189126SWyllys.Ingersoll@Sun.COM }
9199126SWyllys.Ingersoll@Sun.COM 
9209126SWyllys.Ingersoll@Sun.COM /*
9219126SWyllys.Ingersoll@Sun.COM  * Send the data (TPM commands) to the Data IO register
9229126SWyllys.Ingersoll@Sun.COM  */
9239126SWyllys.Ingersoll@Sun.COM static int
9249126SWyllys.Ingersoll@Sun.COM tis_send_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
9259126SWyllys.Ingersoll@Sun.COM 	int ret;
9269126SWyllys.Ingersoll@Sun.COM 	uint8_t status;
9279126SWyllys.Ingersoll@Sun.COM 	uint16_t burstcnt;
9289126SWyllys.Ingersoll@Sun.COM 	uint32_t ordinal;
9299126SWyllys.Ingersoll@Sun.COM 	size_t count = 0;
9309126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_send_data";
9319126SWyllys.Ingersoll@Sun.COM 
9329126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
9339126SWyllys.Ingersoll@Sun.COM 
9349126SWyllys.Ingersoll@Sun.COM 	if (bufsiz == 0) {
9359126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: passed in argument bufsize is zero",
9369126SWyllys.Ingersoll@Sun.COM 		    myname);
9379126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
9389126SWyllys.Ingersoll@Sun.COM 	}
9399126SWyllys.Ingersoll@Sun.COM 
9409126SWyllys.Ingersoll@Sun.COM 	/* Put the TPM in ready state */
9419126SWyllys.Ingersoll@Sun.COM 	status = tis_get_status(tpm);
9429126SWyllys.Ingersoll@Sun.COM 
9439126SWyllys.Ingersoll@Sun.COM 	if (!(status & TPM_STS_CMD_READY)) {
9449126SWyllys.Ingersoll@Sun.COM 		tpm_set_ready(tpm);
945*10346Swyllys.ingersoll@sun.com 		ret = tpm_wait_for_stat(tpm, TPM_STS_CMD_READY, tpm->timeout_b);
9469126SWyllys.Ingersoll@Sun.COM 		if (ret != DDI_SUCCESS) {
9479126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: could not put the TPM "
9489126SWyllys.Ingersoll@Sun.COM 			    "in the command ready state:"
9499126SWyllys.Ingersoll@Sun.COM 			    "tpm_wait_for_stat returned error",
9509126SWyllys.Ingersoll@Sun.COM 			    myname);
9519126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
9529126SWyllys.Ingersoll@Sun.COM 		}
9539126SWyllys.Ingersoll@Sun.COM 	}
9549126SWyllys.Ingersoll@Sun.COM 
9559126SWyllys.Ingersoll@Sun.COM 	/*
9569126SWyllys.Ingersoll@Sun.COM 	 * Now we are ready to send command
9579126SWyllys.Ingersoll@Sun.COM 	 * TPM's burstcount dictates how many bytes we can write at a time
9589126SWyllys.Ingersoll@Sun.COM 	 * Burstcount is dynamic if INTF_CAPABILITY for static burstcount is
9599126SWyllys.Ingersoll@Sun.COM 	 * not set.
9609126SWyllys.Ingersoll@Sun.COM 	 */
9619126SWyllys.Ingersoll@Sun.COM 	while (count < bufsiz - 1) {
9629126SWyllys.Ingersoll@Sun.COM 		burstcnt = tpm_get_burstcount(tpm);
9639126SWyllys.Ingersoll@Sun.COM 		if (burstcnt == 0) {
9649126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: tpm_get_burstcnt returned error",
9659126SWyllys.Ingersoll@Sun.COM 			    myname);
9669126SWyllys.Ingersoll@Sun.COM 			ret = DDI_FAILURE;
9679126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
9689126SWyllys.Ingersoll@Sun.COM 		}
9699126SWyllys.Ingersoll@Sun.COM 
9709126SWyllys.Ingersoll@Sun.COM 		for (; burstcnt > 0 && count < bufsiz - 1; burstcnt--) {
971*10346Swyllys.ingersoll@sun.com 			tpm_put8(tpm, TPM_DATA_FIFO, buf[count]);
9729126SWyllys.Ingersoll@Sun.COM 			count++;
9739126SWyllys.Ingersoll@Sun.COM 		}
9749126SWyllys.Ingersoll@Sun.COM 		/* Wait for TPM to indicate that it is ready for more data */
9759126SWyllys.Ingersoll@Sun.COM 		ret = tpm_wait_for_stat(tpm,
976*10346Swyllys.ingersoll@sun.com 		    (TPM_STS_VALID | TPM_STS_DATA_EXPECT), tpm->timeout_c);
9779126SWyllys.Ingersoll@Sun.COM 		if (ret != DDI_SUCCESS) {
9789126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: TPM didn't enter stsvalid "
9799126SWyllys.Ingersoll@Sun.COM 			    "state after sending the data:", myname);
9809126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
9819126SWyllys.Ingersoll@Sun.COM 		}
9829126SWyllys.Ingersoll@Sun.COM 	}
9839126SWyllys.Ingersoll@Sun.COM 	/* We can't exit the loop above unless we wrote bufsiz-1 bytes */
9849126SWyllys.Ingersoll@Sun.COM 
9859126SWyllys.Ingersoll@Sun.COM 	/* Write last byte */
986*10346Swyllys.ingersoll@sun.com 	tpm_put8(tpm, TPM_DATA_FIFO, buf[count]);
9879126SWyllys.Ingersoll@Sun.COM 	count++;
9889126SWyllys.Ingersoll@Sun.COM 
9899126SWyllys.Ingersoll@Sun.COM 	/* Wait for the TPM to enter Valid State */
990*10346Swyllys.ingersoll@sun.com 	ret = tpm_wait_for_stat(tpm, TPM_STS_VALID, tpm->timeout_c);
9919126SWyllys.Ingersoll@Sun.COM 	if (ret == DDI_FAILURE) {
9929126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm didn't enter Valid state", myname);
9939126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
9949126SWyllys.Ingersoll@Sun.COM 	}
9959126SWyllys.Ingersoll@Sun.COM 
9969126SWyllys.Ingersoll@Sun.COM 	status = tis_get_status(tpm);
9979126SWyllys.Ingersoll@Sun.COM 	/* The TPM should NOT be expecing more data at this point */
9989126SWyllys.Ingersoll@Sun.COM 	if ((status & TPM_STS_DATA_EXPECT) != 0) {
9999126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: DATA_EXPECT is set (shouldn't be) after "
10009126SWyllys.Ingersoll@Sun.COM 		    "writing the last byte: status=0x%08X", myname, status);
10019126SWyllys.Ingersoll@Sun.COM 		ret = DDI_FAILURE;
10029126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
10039126SWyllys.Ingersoll@Sun.COM 	}
10049126SWyllys.Ingersoll@Sun.COM 
10059126SWyllys.Ingersoll@Sun.COM 	/*
10069126SWyllys.Ingersoll@Sun.COM 	 * Final step: Writing TPM_STS_GO to TPM_STS
10079126SWyllys.Ingersoll@Sun.COM 	 * register will actually send the command.
10089126SWyllys.Ingersoll@Sun.COM 	 */
1009*10346Swyllys.ingersoll@sun.com 	tpm_put8(tpm, TPM_STS, TPM_STS_GO);
10109126SWyllys.Ingersoll@Sun.COM 
10119126SWyllys.Ingersoll@Sun.COM 	/* Ordinal/Command_code is located in buf[6..9] */
10129126SWyllys.Ingersoll@Sun.COM 	ordinal = load32(buf, TPM_COMMAND_CODE_OFFSET);
10139126SWyllys.Ingersoll@Sun.COM 
10149126SWyllys.Ingersoll@Sun.COM 	ret = tpm_wait_for_stat(tpm, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
1015*10346Swyllys.ingersoll@sun.com 	    tpm_get_ordinal_duration(tpm, ordinal));
10169126SWyllys.Ingersoll@Sun.COM 	if (ret == DDI_FAILURE) {
10179126SWyllys.Ingersoll@Sun.COM 		status = tis_get_status(tpm);
10189126SWyllys.Ingersoll@Sun.COM 		if (!(status & TPM_STS_DATA_AVAIL) ||
10199126SWyllys.Ingersoll@Sun.COM 		    !(status & TPM_STS_VALID)) {
10209126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: TPM not ready or valid "
10219126SWyllys.Ingersoll@Sun.COM 			    "(ordinal = %d timeout = %ld)",
10229126SWyllys.Ingersoll@Sun.COM 			    myname, ordinal,
10239126SWyllys.Ingersoll@Sun.COM 			    tpm_get_ordinal_duration(tpm, ordinal));
10249126SWyllys.Ingersoll@Sun.COM 		} else {
10259126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: tpm_wait_for_stat "
10269126SWyllys.Ingersoll@Sun.COM 			    "(DATA_AVAIL | VALID) failed: STS = 0x%0X",
10279126SWyllys.Ingersoll@Sun.COM 			    myname, status);
10289126SWyllys.Ingersoll@Sun.COM 		}
10299126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
10309126SWyllys.Ingersoll@Sun.COM 	}
10319126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
10329126SWyllys.Ingersoll@Sun.COM 
10339126SWyllys.Ingersoll@Sun.COM FAIL:
10349126SWyllys.Ingersoll@Sun.COM 	tpm_set_ready(tpm);
10359126SWyllys.Ingersoll@Sun.COM 	tis_release_locality(tpm, tpm->locality, 0);
10369126SWyllys.Ingersoll@Sun.COM 	return (ret);
10379126SWyllys.Ingersoll@Sun.COM }
10389126SWyllys.Ingersoll@Sun.COM 
10399126SWyllys.Ingersoll@Sun.COM /*
10409126SWyllys.Ingersoll@Sun.COM  * Clear XrequestUse and Xactivelocality, where X is the current locality
10419126SWyllys.Ingersoll@Sun.COM  */
10429126SWyllys.Ingersoll@Sun.COM static void
10439126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm_state_t *tpm, char locality, int force) {
10449126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
10459126SWyllys.Ingersoll@Sun.COM 
10469126SWyllys.Ingersoll@Sun.COM 	if (force ||
1047*10346Swyllys.ingersoll@sun.com 	    (tpm_get8(tpm, TPM_ACCESS) &
1048*10346Swyllys.ingersoll@sun.com 	    (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
1049*10346Swyllys.ingersoll@sun.com 	    (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
10509126SWyllys.Ingersoll@Sun.COM 		/*
10519126SWyllys.Ingersoll@Sun.COM 		 * Writing 1 to active locality bit in TPM_ACCESS
10529126SWyllys.Ingersoll@Sun.COM 		 * register reliquishes the control of the locality
10539126SWyllys.Ingersoll@Sun.COM 		 */
1054*10346Swyllys.ingersoll@sun.com 		tpm_put8(tpm, TPM_ACCESS, TPM_ACCESS_ACTIVE_LOCALITY);
10559126SWyllys.Ingersoll@Sun.COM 	}
10569126SWyllys.Ingersoll@Sun.COM }
10579126SWyllys.Ingersoll@Sun.COM 
10589126SWyllys.Ingersoll@Sun.COM /*
10599126SWyllys.Ingersoll@Sun.COM  * Checks whether the given locality is active
10609126SWyllys.Ingersoll@Sun.COM  * Use TPM_ACCESS register and the masks TPM_ACCESS_VALID,TPM_ACTIVE_LOCALITY
10619126SWyllys.Ingersoll@Sun.COM  */
10629126SWyllys.Ingersoll@Sun.COM static int
10639126SWyllys.Ingersoll@Sun.COM tis_check_active_locality(tpm_state_t *tpm, char locality) {
10649126SWyllys.Ingersoll@Sun.COM 	uint8_t access_bits;
1065*10346Swyllys.ingersoll@sun.com 	uint8_t old_locality;
10669126SWyllys.Ingersoll@Sun.COM 
10679126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
10689126SWyllys.Ingersoll@Sun.COM 
1069*10346Swyllys.ingersoll@sun.com 	old_locality = tpm->locality;
1070*10346Swyllys.ingersoll@sun.com 	tpm->locality = locality;
1071*10346Swyllys.ingersoll@sun.com 
1072*10346Swyllys.ingersoll@sun.com 	/* Just check to see if the requested locality works */
1073*10346Swyllys.ingersoll@sun.com 	access_bits = tpm_get8(tpm, TPM_ACCESS);
10749126SWyllys.Ingersoll@Sun.COM 	access_bits &= (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID);
10759126SWyllys.Ingersoll@Sun.COM 
1076*10346Swyllys.ingersoll@sun.com 	/* this was just a check, not a request to switch */
1077*10346Swyllys.ingersoll@sun.com 	tpm->locality = old_locality;
1078*10346Swyllys.ingersoll@sun.com 
1079*10346Swyllys.ingersoll@sun.com 	if (access_bits == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
10809126SWyllys.Ingersoll@Sun.COM 		return (DDI_SUCCESS);
1081*10346Swyllys.ingersoll@sun.com 	} else {
10829126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
1083*10346Swyllys.ingersoll@sun.com 	}
10849126SWyllys.Ingersoll@Sun.COM }
10859126SWyllys.Ingersoll@Sun.COM 
10869126SWyllys.Ingersoll@Sun.COM /* Request the TPM to be in the given locality */
10879126SWyllys.Ingersoll@Sun.COM static int
10889126SWyllys.Ingersoll@Sun.COM tis_request_locality(tpm_state_t *tpm, char locality) {
10899126SWyllys.Ingersoll@Sun.COM 	clock_t timeout;
10909126SWyllys.Ingersoll@Sun.COM 	int ret;
10919126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_request_locality";
10929126SWyllys.Ingersoll@Sun.COM 
10939126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
10949126SWyllys.Ingersoll@Sun.COM 
10959126SWyllys.Ingersoll@Sun.COM 	ret = tis_check_active_locality(tpm, locality);
10969126SWyllys.Ingersoll@Sun.COM 
10979126SWyllys.Ingersoll@Sun.COM 	if (ret == DDI_SUCCESS) {
10989126SWyllys.Ingersoll@Sun.COM 		/* Locality is already active */
10999126SWyllys.Ingersoll@Sun.COM 		tpm->locality = locality;
11009126SWyllys.Ingersoll@Sun.COM 		return (DDI_SUCCESS);
11019126SWyllys.Ingersoll@Sun.COM 	}
11029126SWyllys.Ingersoll@Sun.COM 
1103*10346Swyllys.ingersoll@sun.com 	tpm_put8(tpm, TPM_ACCESS, TPM_ACCESS_REQUEST_USE);
11049126SWyllys.Ingersoll@Sun.COM 	timeout = ddi_get_lbolt() + tpm->timeout_a;
11059126SWyllys.Ingersoll@Sun.COM 
11069126SWyllys.Ingersoll@Sun.COM 	/* Using polling */
11079126SWyllys.Ingersoll@Sun.COM 	while (tis_check_active_locality(tpm, locality)
11089126SWyllys.Ingersoll@Sun.COM 		!= DDI_SUCCESS) {
11099126SWyllys.Ingersoll@Sun.COM 		if (ddi_get_lbolt() >= timeout) {
11109126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s (interrupt-disabled) "
1111*10346Swyllys.ingersoll@sun.com 			    "tis_request_locality timed out (timeout_a = %ld)",
1112*10346Swyllys.ingersoll@sun.com 			    myname, tpm->timeout_a);
11139126SWyllys.Ingersoll@Sun.COM 			return (DDI_FAILURE);
11149126SWyllys.Ingersoll@Sun.COM 		}
11159126SWyllys.Ingersoll@Sun.COM 		delay(tpm->timeout_poll);
11169126SWyllys.Ingersoll@Sun.COM 	}
11179126SWyllys.Ingersoll@Sun.COM 
11189126SWyllys.Ingersoll@Sun.COM 	tpm->locality = locality;
11199126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
11209126SWyllys.Ingersoll@Sun.COM }
11219126SWyllys.Ingersoll@Sun.COM 
11229126SWyllys.Ingersoll@Sun.COM /* Read the status register */
11239126SWyllys.Ingersoll@Sun.COM static uint8_t
11249126SWyllys.Ingersoll@Sun.COM tis_get_status(tpm_state_t *tpm) {
1125*10346Swyllys.ingersoll@sun.com 	return (tpm_get8(tpm, TPM_STS));
11269126SWyllys.Ingersoll@Sun.COM }
11279126SWyllys.Ingersoll@Sun.COM 
11289126SWyllys.Ingersoll@Sun.COM static int
1129*10346Swyllys.ingersoll@sun.com tpm_wait_for_stat(tpm_state_t *tpm, uint8_t mask, clock_t timeout) {
11309126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_wait_for_stat";
1131*10346Swyllys.ingersoll@sun.com 	clock_t absolute_timeout = ddi_get_lbolt() + timeout;
11329126SWyllys.Ingersoll@Sun.COM 
11339126SWyllys.Ingersoll@Sun.COM 	/* Using polling */
11349126SWyllys.Ingersoll@Sun.COM 	while ((tis_get_status(tpm) & mask) != mask) {
11359126SWyllys.Ingersoll@Sun.COM 		if (ddi_get_lbolt() >= absolute_timeout) {
11369126SWyllys.Ingersoll@Sun.COM 			/* Timeout reached */
11379126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN, "%s: using "
1138*10346Swyllys.ingersoll@sun.com 			    "polling - reached timeout (%ld usecs)",
1139*10346Swyllys.ingersoll@sun.com 			    myname, drv_hztousec(timeout));
11409126SWyllys.Ingersoll@Sun.COM 			return (DDI_FAILURE);
11419126SWyllys.Ingersoll@Sun.COM 		}
11429126SWyllys.Ingersoll@Sun.COM 		delay(tpm->timeout_poll);
11439126SWyllys.Ingersoll@Sun.COM 	}
11449126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
11459126SWyllys.Ingersoll@Sun.COM }
11469126SWyllys.Ingersoll@Sun.COM 
11479126SWyllys.Ingersoll@Sun.COM /*
11489126SWyllys.Ingersoll@Sun.COM  * Initialize TPM device
11499126SWyllys.Ingersoll@Sun.COM  * 1. Find out supported interrupt capabilities
11509126SWyllys.Ingersoll@Sun.COM  * 2. Set up interrupt handler if supported (some BIOSes don't support
11519126SWyllys.Ingersoll@Sun.COM  * interrupts for TPMS, in which case we set up polling)
11529126SWyllys.Ingersoll@Sun.COM  * 3. Determine timeouts and commands duration
11539126SWyllys.Ingersoll@Sun.COM  */
11549126SWyllys.Ingersoll@Sun.COM static int
11559126SWyllys.Ingersoll@Sun.COM tis_init(tpm_state_t *tpm) {
11569126SWyllys.Ingersoll@Sun.COM 	uint32_t intf_caps;
11579126SWyllys.Ingersoll@Sun.COM 	int ret;
11589126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_init";
11599126SWyllys.Ingersoll@Sun.COM 
11609126SWyllys.Ingersoll@Sun.COM 	/*
11619126SWyllys.Ingersoll@Sun.COM 	 * Temporarily set up timeouts before we get the real timeouts
11629126SWyllys.Ingersoll@Sun.COM 	 * by issuing TPM_CAP commands (but to issue TPM_CAP commands,
11639126SWyllys.Ingersoll@Sun.COM 	 * you need TIMEOUTs defined...chicken and egg problem here.
11649126SWyllys.Ingersoll@Sun.COM 	 * TPM timeouts: Convert the milliseconds to clock cycles
11659126SWyllys.Ingersoll@Sun.COM 	 */
11669126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_a = drv_usectohz(TIS_TIMEOUT_A);
11679126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_b = drv_usectohz(TIS_TIMEOUT_B);
11689126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_c = drv_usectohz(TIS_TIMEOUT_C);
11699126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_d = drv_usectohz(TIS_TIMEOUT_D);
11709126SWyllys.Ingersoll@Sun.COM 	/*
11719126SWyllys.Ingersoll@Sun.COM 	 * Do the same with the duration (real duration will be filled out
11729126SWyllys.Ingersoll@Sun.COM 	 * when we call TPM_GetCapability to get the duration values from
11739126SWyllys.Ingersoll@Sun.COM 	 * the TPM itself).
11749126SWyllys.Ingersoll@Sun.COM 	 */
11759126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_SHORT] = drv_usectohz(TPM_DEFAULT_DURATION);
11769126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_MEDIUM] = drv_usectohz(TPM_DEFAULT_DURATION);
11779126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_LONG] = drv_usectohz(TPM_DEFAULT_DURATION);
11789126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_UNDEFINED] = drv_usectohz(TPM_DEFAULT_DURATION);
11799126SWyllys.Ingersoll@Sun.COM 
11809126SWyllys.Ingersoll@Sun.COM 	/* Find out supported capabilities */
1181*10346Swyllys.ingersoll@sun.com 	intf_caps = tpm_get32(tpm, TPM_INTF_CAP);
11829126SWyllys.Ingersoll@Sun.COM 
11839126SWyllys.Ingersoll@Sun.COM 	/* Upper 3 bytes should always return 0 */
11849126SWyllys.Ingersoll@Sun.COM 	if (intf_caps & 0x7FFFFF00) {
11859126SWyllys.Ingersoll@Sun.COM #ifdef DEBUG
11869126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: bad intf_caps value 0x%0X",
11879126SWyllys.Ingersoll@Sun.COM 		    myname, intf_caps);
11889126SWyllys.Ingersoll@Sun.COM #endif
11899126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
11909126SWyllys.Ingersoll@Sun.COM 	}
11919126SWyllys.Ingersoll@Sun.COM 
11929126SWyllys.Ingersoll@Sun.COM 	/* These two interrupts are mandatory */
11939126SWyllys.Ingersoll@Sun.COM 	if (!(intf_caps & TPM_INTF_INT_LOCALITY_CHANGE_INT)) {
11949126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: Mandatory capability Locality Change Int "
11959126SWyllys.Ingersoll@Sun.COM 		    "not supported", myname);
11969126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
11979126SWyllys.Ingersoll@Sun.COM 	}
11989126SWyllys.Ingersoll@Sun.COM 	if (!(intf_caps & TPM_INTF_INT_DATA_AVAIL_INT)) {
11999126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: Mandatory capability Data Available Int "
12009126SWyllys.Ingersoll@Sun.COM 		    "not supported", myname);
12019126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
12029126SWyllys.Ingersoll@Sun.COM 	}
12039126SWyllys.Ingersoll@Sun.COM 
12049126SWyllys.Ingersoll@Sun.COM 	/*
12059126SWyllys.Ingersoll@Sun.COM 	 * Before we start writing anything to TPM's registers,
12069126SWyllys.Ingersoll@Sun.COM 	 * make sure we are in locality 0
12079126SWyllys.Ingersoll@Sun.COM 	 */
1208*10346Swyllys.ingersoll@sun.com 	ret = tis_request_locality(tpm, DEFAULT_LOCALITY);
12099126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1210*10346Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "%s: Unable to request locality %d", myname,
1211*10346Swyllys.ingersoll@sun.com 		    DEFAULT_LOCALITY);
12129126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
12139126SWyllys.Ingersoll@Sun.COM 	} /* Now we can refer to the locality as tpm->locality */
12149126SWyllys.Ingersoll@Sun.COM 
12159126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_poll = drv_usectohz(TPM_POLLING_TIMEOUT);
12169126SWyllys.Ingersoll@Sun.COM 	tpm->intr_enabled = 0;
12179126SWyllys.Ingersoll@Sun.COM 
12189126SWyllys.Ingersoll@Sun.COM 	/* Get the real timeouts from the TPM */
12199126SWyllys.Ingersoll@Sun.COM 	ret = tpm_get_timeouts(tpm);
12209126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
12219126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm_get_timeouts error", myname);
12229126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
12239126SWyllys.Ingersoll@Sun.COM 	}
12249126SWyllys.Ingersoll@Sun.COM 
12259126SWyllys.Ingersoll@Sun.COM 	ret = tpm_get_duration(tpm);
12269126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
12279126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm_get_duration error", myname);
12289126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
12299126SWyllys.Ingersoll@Sun.COM 	}
12309126SWyllys.Ingersoll@Sun.COM 
12319126SWyllys.Ingersoll@Sun.COM 	/* This gets the TPM version information */
12329126SWyllys.Ingersoll@Sun.COM 	ret = tpm_get_version(tpm);
12339126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
12349126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm_get_version error", myname);
12359126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
12369126SWyllys.Ingersoll@Sun.COM 	}
12379126SWyllys.Ingersoll@Sun.COM 
12389126SWyllys.Ingersoll@Sun.COM 	/*
12399126SWyllys.Ingersoll@Sun.COM 	 * Unless the TPM completes the test of its commands,
12409126SWyllys.Ingersoll@Sun.COM 	 * it can return an error when the untested commands are called
12419126SWyllys.Ingersoll@Sun.COM 	 */
12429126SWyllys.Ingersoll@Sun.COM 	ret = tpm_continue_selftest(tpm);
12439126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
12449126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tpm_continue_selftest error", myname);
12459126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
12469126SWyllys.Ingersoll@Sun.COM 	}
12479126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
12489126SWyllys.Ingersoll@Sun.COM }
12499126SWyllys.Ingersoll@Sun.COM 
12509126SWyllys.Ingersoll@Sun.COM /*
12519126SWyllys.Ingersoll@Sun.COM  * Module Entry points
12529126SWyllys.Ingersoll@Sun.COM  */
12539126SWyllys.Ingersoll@Sun.COM int
12549126SWyllys.Ingersoll@Sun.COM _init(void)
12559126SWyllys.Ingersoll@Sun.COM {
12569126SWyllys.Ingersoll@Sun.COM 	int ret;
12579126SWyllys.Ingersoll@Sun.COM 
12589126SWyllys.Ingersoll@Sun.COM 	ret = ddi_soft_state_init(&statep, sizeof (tpm_state_t), 1);
12599126SWyllys.Ingersoll@Sun.COM 	if (ret)
1260*10346Swyllys.ingersoll@sun.com {
1261*10346Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "ddi_soft_state_init failed: %d", ret);
12629126SWyllys.Ingersoll@Sun.COM 		return (ret);
1263*10346Swyllys.ingersoll@sun.com }
12649126SWyllys.Ingersoll@Sun.COM 	ret = mod_install(&tpm_ml);
12659126SWyllys.Ingersoll@Sun.COM 	if (ret != 0) {
12669126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "_init: mod_install returned non-zero");
12679126SWyllys.Ingersoll@Sun.COM 		ddi_soft_state_fini(&statep);
12689126SWyllys.Ingersoll@Sun.COM 		return (ret);
12699126SWyllys.Ingersoll@Sun.COM 	}
12709126SWyllys.Ingersoll@Sun.COM 
12719126SWyllys.Ingersoll@Sun.COM 	return (ret);
12729126SWyllys.Ingersoll@Sun.COM }
12739126SWyllys.Ingersoll@Sun.COM 
12749126SWyllys.Ingersoll@Sun.COM int
12759126SWyllys.Ingersoll@Sun.COM _info(struct modinfo *modinfop)
12769126SWyllys.Ingersoll@Sun.COM {
12779126SWyllys.Ingersoll@Sun.COM 	int ret;
12789126SWyllys.Ingersoll@Sun.COM 	ret = mod_info(&tpm_ml, modinfop);
12799126SWyllys.Ingersoll@Sun.COM 	if (ret == 0)
12809126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "mod_info failed: %d", ret);
12819126SWyllys.Ingersoll@Sun.COM 
12829126SWyllys.Ingersoll@Sun.COM 	return (ret);
12839126SWyllys.Ingersoll@Sun.COM }
12849126SWyllys.Ingersoll@Sun.COM 
12859126SWyllys.Ingersoll@Sun.COM int
12869126SWyllys.Ingersoll@Sun.COM _fini()
12879126SWyllys.Ingersoll@Sun.COM {
12889126SWyllys.Ingersoll@Sun.COM 	int ret;
1289*10346Swyllys.ingersoll@sun.com 
12909126SWyllys.Ingersoll@Sun.COM 	ret = mod_remove(&tpm_ml);
1291*10346Swyllys.ingersoll@sun.com 	if (ret != 0)
12929126SWyllys.Ingersoll@Sun.COM 		return (ret);
1293*10346Swyllys.ingersoll@sun.com 
12949126SWyllys.Ingersoll@Sun.COM 	ddi_soft_state_fini(&statep);
12959126SWyllys.Ingersoll@Sun.COM 
12969126SWyllys.Ingersoll@Sun.COM 	return (ret);
12979126SWyllys.Ingersoll@Sun.COM }
12989126SWyllys.Ingersoll@Sun.COM /* End of driver configuration functions */
12999126SWyllys.Ingersoll@Sun.COM 
13009126SWyllys.Ingersoll@Sun.COM static int
13019126SWyllys.Ingersoll@Sun.COM tpm_resume(tpm_state_t *tpm)
13029126SWyllys.Ingersoll@Sun.COM {
13039126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
13049126SWyllys.Ingersoll@Sun.COM 	if (!tpm->suspended) {
13059126SWyllys.Ingersoll@Sun.COM 		mutex_exit(&tpm->pm_mutex);
13069126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
13079126SWyllys.Ingersoll@Sun.COM 	}
13089126SWyllys.Ingersoll@Sun.COM 	tpm->suspended = 0;
13099126SWyllys.Ingersoll@Sun.COM 	cv_broadcast(&tpm->suspend_cv);
13109126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
13119126SWyllys.Ingersoll@Sun.COM 
13129126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
13139126SWyllys.Ingersoll@Sun.COM }
13149126SWyllys.Ingersoll@Sun.COM 
1315*10346Swyllys.ingersoll@sun.com #ifdef sun4v
1316*10346Swyllys.ingersoll@sun.com static uint64_t hsvc_tpm_minor = 0;
1317*10346Swyllys.ingersoll@sun.com static hsvc_info_t hsvc_tpm = {
1318*10346Swyllys.ingersoll@sun.com 	HSVC_REV_1, NULL, HSVC_GROUP_TPM, 1, 0, NULL
1319*10346Swyllys.ingersoll@sun.com };
1320*10346Swyllys.ingersoll@sun.com #endif
1321*10346Swyllys.ingersoll@sun.com 
13229126SWyllys.Ingersoll@Sun.COM /*
13239126SWyllys.Ingersoll@Sun.COM  * Sun DDI/DDK entry points
13249126SWyllys.Ingersoll@Sun.COM  */
13259126SWyllys.Ingersoll@Sun.COM static int
13269126SWyllys.Ingersoll@Sun.COM tpm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
13279126SWyllys.Ingersoll@Sun.COM {
1328*10346Swyllys.ingersoll@sun.com 	int ret;
13299126SWyllys.Ingersoll@Sun.COM 	int instance;
1330*10346Swyllys.ingersoll@sun.com #ifndef sun4v
1331*10346Swyllys.ingersoll@sun.com 	int idx, nregs;
1332*10346Swyllys.ingersoll@sun.com #endif
13339126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_attach";
13349126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm = NULL;
13359126SWyllys.Ingersoll@Sun.COM 
13369126SWyllys.Ingersoll@Sun.COM 	ASSERT(dip != NULL);
13379126SWyllys.Ingersoll@Sun.COM 
13389126SWyllys.Ingersoll@Sun.COM 	instance = ddi_get_instance(dip);
1339*10346Swyllys.ingersoll@sun.com 	if (instance < 0)
1340*10346Swyllys.ingersoll@sun.com 		return (DDI_FAILURE);
13419126SWyllys.Ingersoll@Sun.COM 
13429126SWyllys.Ingersoll@Sun.COM 	/* Nothing out of ordinary here */
13439126SWyllys.Ingersoll@Sun.COM 	switch (cmd) {
13449126SWyllys.Ingersoll@Sun.COM 	case DDI_ATTACH:
1345*10346Swyllys.ingersoll@sun.com 		if (ddi_soft_state_zalloc(statep, instance) == DDI_SUCCESS) {
1346*10346Swyllys.ingersoll@sun.com 			tpm = ddi_get_soft_state(statep, instance);
1347*10346Swyllys.ingersoll@sun.com 			if (tpm == NULL) {
1348*10346Swyllys.ingersoll@sun.com 				cmn_err(CE_WARN,
1349*10346Swyllys.ingersoll@sun.com 				    "%s: cannot get state information.",
1350*10346Swyllys.ingersoll@sun.com 				    myname);
1351*10346Swyllys.ingersoll@sun.com 				return (DDI_FAILURE);
1352*10346Swyllys.ingersoll@sun.com 			}
1353*10346Swyllys.ingersoll@sun.com 			tpm->dip = dip;
1354*10346Swyllys.ingersoll@sun.com 		} else {
1355*10346Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN,
1356*10346Swyllys.ingersoll@sun.com 			    "%s: cannot allocate state information.",
13579126SWyllys.Ingersoll@Sun.COM 			    myname);
1358*10346Swyllys.ingersoll@sun.com 			return (DDI_FAILURE);
13599126SWyllys.Ingersoll@Sun.COM 		}
13609126SWyllys.Ingersoll@Sun.COM 		break;
13619126SWyllys.Ingersoll@Sun.COM 	case DDI_RESUME:
13629126SWyllys.Ingersoll@Sun.COM 		tpm = ddi_get_soft_state(statep, instance);
13639126SWyllys.Ingersoll@Sun.COM 		if (tpm == NULL) {
1364*10346Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "%s: cannot get state information.",
13659126SWyllys.Ingersoll@Sun.COM 			    myname);
1366*10346Swyllys.ingersoll@sun.com 			return (DDI_FAILURE);
13679126SWyllys.Ingersoll@Sun.COM 		}
13689126SWyllys.Ingersoll@Sun.COM 		return (tpm_resume(tpm));
13699126SWyllys.Ingersoll@Sun.COM 	default:
13709126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: cmd %d is not implemented", myname, cmd);
13719126SWyllys.Ingersoll@Sun.COM 		ret = DDI_FAILURE;
13729126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
13739126SWyllys.Ingersoll@Sun.COM 	}
13749126SWyllys.Ingersoll@Sun.COM 
13759126SWyllys.Ingersoll@Sun.COM 	/* Zeroize the flag, which is used to keep track of what is allocated */
13769126SWyllys.Ingersoll@Sun.COM 	tpm->flags = 0;
13779126SWyllys.Ingersoll@Sun.COM 
1378*10346Swyllys.ingersoll@sun.com #ifdef sun4v
1379*10346Swyllys.ingersoll@sun.com 	ret = hsvc_register(&hsvc_tpm, &hsvc_tpm_minor);
1380*10346Swyllys.ingersoll@sun.com 	if (ret != 0) {
1381*10346Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "%s: failed to register with "
1382*10346Swyllys.ingersoll@sun.com 		    "hypervisor: 0x%0x", myname, ret);
1383*10346Swyllys.ingersoll@sun.com 		goto FAIL;
1384*10346Swyllys.ingersoll@sun.com 	}
1385*10346Swyllys.ingersoll@sun.com 	tpm->flags |= TPM_HSVC_REGISTERED;
1386*10346Swyllys.ingersoll@sun.com #else
13879126SWyllys.Ingersoll@Sun.COM 	tpm->accattr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
13889126SWyllys.Ingersoll@Sun.COM 	tpm->accattr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
13899126SWyllys.Ingersoll@Sun.COM 	tpm->accattr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
13909126SWyllys.Ingersoll@Sun.COM 
13919126SWyllys.Ingersoll@Sun.COM 	idx = 0;
13929126SWyllys.Ingersoll@Sun.COM 	ret = ddi_dev_nregs(tpm->dip, &nregs);
13939126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS)
13949126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
13959126SWyllys.Ingersoll@Sun.COM 
13969126SWyllys.Ingersoll@Sun.COM 	/*
13979126SWyllys.Ingersoll@Sun.COM 	 * TPM vendors put the TPM registers in different
13989126SWyllys.Ingersoll@Sun.COM 	 * slots in their register lists.  They are not always
13999126SWyllys.Ingersoll@Sun.COM 	 * the 1st set of registers, for instance.
14009126SWyllys.Ingersoll@Sun.COM 	 * Loop until we find the set that matches the expected
14019126SWyllys.Ingersoll@Sun.COM 	 * register size (0x5000).
14029126SWyllys.Ingersoll@Sun.COM 	 */
14039126SWyllys.Ingersoll@Sun.COM 	for (idx = 0; idx < nregs; idx++) {
14049126SWyllys.Ingersoll@Sun.COM 		off_t regsize;
14059126SWyllys.Ingersoll@Sun.COM 
14069126SWyllys.Ingersoll@Sun.COM 		if ((ret = ddi_dev_regsize(tpm->dip, idx, &regsize)) !=
14079126SWyllys.Ingersoll@Sun.COM 		    DDI_SUCCESS)
14089126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
14099126SWyllys.Ingersoll@Sun.COM 		/* The TIS spec says the TPM registers must be 0x5000 bytes */
14109126SWyllys.Ingersoll@Sun.COM 		if (regsize == 0x5000)
14119126SWyllys.Ingersoll@Sun.COM 			break;
14129126SWyllys.Ingersoll@Sun.COM 	}
14139382SWyllys.Ingersoll@Sun.COM 	if (idx == nregs)
14149382SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
14159382SWyllys.Ingersoll@Sun.COM 
14169126SWyllys.Ingersoll@Sun.COM 	ret = ddi_regs_map_setup(tpm->dip, idx, (caddr_t *)&tpm->addr,
14179126SWyllys.Ingersoll@Sun.COM 	    (offset_t)0, (offset_t)0x5000,
14189126SWyllys.Ingersoll@Sun.COM 	    &tpm->accattr, &tpm->handle);
14199126SWyllys.Ingersoll@Sun.COM 
14209126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
14219126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
14229126SWyllys.Ingersoll@Sun.COM 	}
14239126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DIDREGSMAP;
1424*10346Swyllys.ingersoll@sun.com #endif
14259126SWyllys.Ingersoll@Sun.COM 	/* Enable TPM device according to the TIS specification */
14269126SWyllys.Ingersoll@Sun.COM 	ret = tis_init(tpm);
14279126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1428*10346Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "%s: tis_init() failed with error %d",
14299126SWyllys.Ingersoll@Sun.COM 		    myname, ret);
14309126SWyllys.Ingersoll@Sun.COM 
14319126SWyllys.Ingersoll@Sun.COM 		/* We need to clean up the ddi_regs_map_setup call */
1432*10346Swyllys.ingersoll@sun.com 		if (tpm->flags & TPM_DIDREGSMAP) {
1433*10346Swyllys.ingersoll@sun.com 			ddi_regs_map_free(&tpm->handle);
1434*10346Swyllys.ingersoll@sun.com 			tpm->handle = NULL;
1435*10346Swyllys.ingersoll@sun.com 			tpm->flags &= ~TPM_DIDREGSMAP;
1436*10346Swyllys.ingersoll@sun.com 		}
14379126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
14389126SWyllys.Ingersoll@Sun.COM 	}
14399126SWyllys.Ingersoll@Sun.COM 
14409126SWyllys.Ingersoll@Sun.COM 	/* Initialize the inter-process lock */
14419126SWyllys.Ingersoll@Sun.COM 	mutex_init(&tpm->dev_lock, NULL, MUTEX_DRIVER, NULL);
14429126SWyllys.Ingersoll@Sun.COM 	mutex_init(&tpm->pm_mutex, NULL, MUTEX_DRIVER, NULL);
14439126SWyllys.Ingersoll@Sun.COM 	cv_init(&tpm->suspend_cv, NULL, CV_DRIVER, NULL);
14449126SWyllys.Ingersoll@Sun.COM 
14459126SWyllys.Ingersoll@Sun.COM 	/* Set the suspend/resume property */
14469126SWyllys.Ingersoll@Sun.COM 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
14479126SWyllys.Ingersoll@Sun.COM 	    "pm-hardware-state", "needs-suspend-resume");
14489126SWyllys.Ingersoll@Sun.COM 
14499126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
14509126SWyllys.Ingersoll@Sun.COM 	tpm->suspended = 0;
14519126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
14529126SWyllys.Ingersoll@Sun.COM 
14539126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_MUTEX;
14549126SWyllys.Ingersoll@Sun.COM 
14559126SWyllys.Ingersoll@Sun.COM 	/* Initialize the buffer and the lock associated with it */
14569126SWyllys.Ingersoll@Sun.COM 	tpm->bufsize = TPM_IO_BUF_SIZE;
14579126SWyllys.Ingersoll@Sun.COM 	tpm->iobuf = kmem_zalloc((sizeof (uint8_t))*(tpm->bufsize), KM_SLEEP);
14589126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_IO_ALLOC;
14599126SWyllys.Ingersoll@Sun.COM 
14609126SWyllys.Ingersoll@Sun.COM 	mutex_init(&tpm->iobuf_lock, NULL, MUTEX_DRIVER, NULL);
14619126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_IO_MUTEX;
14629126SWyllys.Ingersoll@Sun.COM 
14639126SWyllys.Ingersoll@Sun.COM 	cv_init(&tpm->iobuf_cv, NULL, CV_DRIVER, NULL);
14649126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_IO_CV;
14659126SWyllys.Ingersoll@Sun.COM 
14669126SWyllys.Ingersoll@Sun.COM 	/* Create minor node */
14679126SWyllys.Ingersoll@Sun.COM 	ret = ddi_create_minor_node(dip, "tpm", S_IFCHR, ddi_get_instance(dip),
14689126SWyllys.Ingersoll@Sun.COM 	    DDI_PSEUDO, 0);
14699126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
14709126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: ddi_create_minor_node failed", myname);
14719126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
14729126SWyllys.Ingersoll@Sun.COM 	}
14739126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DIDMINOR;
14749126SWyllys.Ingersoll@Sun.COM 
1475*10346Swyllys.ingersoll@sun.com #ifdef KCF_TPM_RNG_PROVIDER
1476*10346Swyllys.ingersoll@sun.com 	/* register RNG with kcf */
1477*10346Swyllys.ingersoll@sun.com 	if (tpmrng_register(tpm) != DDI_SUCCESS)
1478*10346Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "%s: tpm RNG failed to register with kcf",
1479*10346Swyllys.ingersoll@sun.com 		    myname);
1480*10346Swyllys.ingersoll@sun.com #endif
1481*10346Swyllys.ingersoll@sun.com 
14829126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
14839126SWyllys.Ingersoll@Sun.COM FAIL:
1484*10346Swyllys.ingersoll@sun.com 	cmn_err(CE_WARN, "%s: tpm failed to attach", myname);
14859126SWyllys.Ingersoll@Sun.COM 	if (tpm != NULL) {
14869126SWyllys.Ingersoll@Sun.COM 		tpm_cleanup(dip, tpm);
14879126SWyllys.Ingersoll@Sun.COM 		ddi_soft_state_free(statep, instance);
14889126SWyllys.Ingersoll@Sun.COM 		tpm = NULL;
14899126SWyllys.Ingersoll@Sun.COM 	}
14909126SWyllys.Ingersoll@Sun.COM 
14919126SWyllys.Ingersoll@Sun.COM 	return (DDI_FAILURE);
14929126SWyllys.Ingersoll@Sun.COM }
14939126SWyllys.Ingersoll@Sun.COM 
14949126SWyllys.Ingersoll@Sun.COM /*
14959126SWyllys.Ingersoll@Sun.COM  * Called by tpm_detach and tpm_attach (only on failure)
14969126SWyllys.Ingersoll@Sun.COM  * Free up the resources that are allocated
14979126SWyllys.Ingersoll@Sun.COM  */
14989126SWyllys.Ingersoll@Sun.COM static void
14999126SWyllys.Ingersoll@Sun.COM tpm_cleanup(dev_info_t *dip, tpm_state_t *tpm)
15009126SWyllys.Ingersoll@Sun.COM {
15019126SWyllys.Ingersoll@Sun.COM 	if (tpm == NULL)
15029126SWyllys.Ingersoll@Sun.COM 		return;
15039126SWyllys.Ingersoll@Sun.COM 
1504*10346Swyllys.ingersoll@sun.com #ifdef KCF_TPM_RNG_PROVIDER
1505*10346Swyllys.ingersoll@sun.com 	(void) tpmrng_unregister(tpm);
1506*10346Swyllys.ingersoll@sun.com #endif
1507*10346Swyllys.ingersoll@sun.com 
1508*10346Swyllys.ingersoll@sun.com #ifdef sun4v
1509*10346Swyllys.ingersoll@sun.com 	if (tpm->flags & TPM_HSVC_REGISTERED) {
1510*10346Swyllys.ingersoll@sun.com 		(void) hsvc_unregister(&hsvc_tpm);
1511*10346Swyllys.ingersoll@sun.com 		tpm->flags &= ~(TPM_HSVC_REGISTERED);
1512*10346Swyllys.ingersoll@sun.com 	}
1513*10346Swyllys.ingersoll@sun.com #endif
15149126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_MUTEX) {
15159126SWyllys.Ingersoll@Sun.COM 		mutex_destroy(&tpm->dev_lock);
15169126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_MUTEX);
15179126SWyllys.Ingersoll@Sun.COM 	}
15189126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_IO_ALLOC) {
15199126SWyllys.Ingersoll@Sun.COM 		ASSERT(tpm->iobuf != NULL);
15209126SWyllys.Ingersoll@Sun.COM 		kmem_free(tpm->iobuf, (sizeof (uint8_t))*(tpm->bufsize));
15219126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_IO_ALLOC);
15229126SWyllys.Ingersoll@Sun.COM 	}
15239126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_IO_MUTEX) {
15249126SWyllys.Ingersoll@Sun.COM 		mutex_destroy(&tpm->iobuf_lock);
15259126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_IO_MUTEX);
15269126SWyllys.Ingersoll@Sun.COM 	}
15279126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_IO_CV) {
15289126SWyllys.Ingersoll@Sun.COM 		cv_destroy(&tpm->iobuf_cv);
15299126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_IO_CV);
15309126SWyllys.Ingersoll@Sun.COM 	}
15319126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DIDREGSMAP) {
15329126SWyllys.Ingersoll@Sun.COM 		/* Free the mapped addresses */
15339126SWyllys.Ingersoll@Sun.COM 		if (tpm->handle != NULL)
15349126SWyllys.Ingersoll@Sun.COM 			ddi_regs_map_free(&tpm->handle);
15359126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DIDREGSMAP);
15369126SWyllys.Ingersoll@Sun.COM 	}
15379126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DIDMINOR) {
15389126SWyllys.Ingersoll@Sun.COM 		/* Remove minor node */
15399126SWyllys.Ingersoll@Sun.COM 		ddi_remove_minor_node(dip, NULL);
15409126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DIDMINOR);
15419126SWyllys.Ingersoll@Sun.COM 	}
15429126SWyllys.Ingersoll@Sun.COM }
15439126SWyllys.Ingersoll@Sun.COM 
15449126SWyllys.Ingersoll@Sun.COM static int
15459126SWyllys.Ingersoll@Sun.COM tpm_suspend(tpm_state_t *tpm)
15469126SWyllys.Ingersoll@Sun.COM {
15479126SWyllys.Ingersoll@Sun.COM 	if (tpm == NULL)
15489126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
15499126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
15509126SWyllys.Ingersoll@Sun.COM 	if (tpm->suspended) {
15519126SWyllys.Ingersoll@Sun.COM 		mutex_exit(&tpm->pm_mutex);
15529126SWyllys.Ingersoll@Sun.COM 		return (DDI_SUCCESS);
15539126SWyllys.Ingersoll@Sun.COM 	}
15549126SWyllys.Ingersoll@Sun.COM 	tpm->suspended = 1;
15559126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
15569126SWyllys.Ingersoll@Sun.COM 
15579126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
15589126SWyllys.Ingersoll@Sun.COM }
15599126SWyllys.Ingersoll@Sun.COM 
15609126SWyllys.Ingersoll@Sun.COM static int
15619126SWyllys.Ingersoll@Sun.COM tpm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
15629126SWyllys.Ingersoll@Sun.COM {
15639126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_detach";
15649126SWyllys.Ingersoll@Sun.COM 	int instance;
15659126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
15669126SWyllys.Ingersoll@Sun.COM 
15679126SWyllys.Ingersoll@Sun.COM 	ASSERT(dip != NULL);
15689126SWyllys.Ingersoll@Sun.COM 
15699126SWyllys.Ingersoll@Sun.COM 	instance = ddi_get_instance(dip);
1570*10346Swyllys.ingersoll@sun.com 	if (instance < 0)
1571*10346Swyllys.ingersoll@sun.com 		return (DDI_FAILURE);
1572*10346Swyllys.ingersoll@sun.com 
15739126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
15749126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
15759126SWyllys.Ingersoll@Sun.COM 		    myname);
15769126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
15779126SWyllys.Ingersoll@Sun.COM 	}
15789126SWyllys.Ingersoll@Sun.COM 
15799126SWyllys.Ingersoll@Sun.COM 	switch (cmd) {
15809126SWyllys.Ingersoll@Sun.COM 	case DDI_DETACH:
15819126SWyllys.Ingersoll@Sun.COM 		/* Body is after the switch stmt */
15829126SWyllys.Ingersoll@Sun.COM 		break;
15839126SWyllys.Ingersoll@Sun.COM 	case DDI_SUSPEND:
15849126SWyllys.Ingersoll@Sun.COM 		return (tpm_suspend(tpm));
15859126SWyllys.Ingersoll@Sun.COM 	default:
15869126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: case %d not implemented", myname, cmd);
15879126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
15889126SWyllys.Ingersoll@Sun.COM 	}
15899126SWyllys.Ingersoll@Sun.COM 
15909126SWyllys.Ingersoll@Sun.COM 	/* Since we are freeing tpm structure, we need to gain the lock */
15919126SWyllys.Ingersoll@Sun.COM 
15929126SWyllys.Ingersoll@Sun.COM 	tpm_cleanup(dip, tpm);
15939126SWyllys.Ingersoll@Sun.COM 
15949126SWyllys.Ingersoll@Sun.COM 	mutex_destroy(&tpm->pm_mutex);
15959126SWyllys.Ingersoll@Sun.COM 	cv_destroy(&tpm->suspend_cv);
15969126SWyllys.Ingersoll@Sun.COM 
15979126SWyllys.Ingersoll@Sun.COM 	/* Free the soft state */
15989126SWyllys.Ingersoll@Sun.COM 	ddi_soft_state_free(statep, instance);
15999126SWyllys.Ingersoll@Sun.COM 	tpm = NULL;
16009126SWyllys.Ingersoll@Sun.COM 
16019126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
16029126SWyllys.Ingersoll@Sun.COM }
16039126SWyllys.Ingersoll@Sun.COM 
16049126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
16059126SWyllys.Ingersoll@Sun.COM static int
16069126SWyllys.Ingersoll@Sun.COM tpm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
16079126SWyllys.Ingersoll@Sun.COM {
16089126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_getinfo";
16099126SWyllys.Ingersoll@Sun.COM 	int instance;
16109126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
16119126SWyllys.Ingersoll@Sun.COM 
16129126SWyllys.Ingersoll@Sun.COM 	instance = ddi_get_instance(dip);
16139126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
16149126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
16159126SWyllys.Ingersoll@Sun.COM 		    myname);
16169126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
16179126SWyllys.Ingersoll@Sun.COM 	}
16189126SWyllys.Ingersoll@Sun.COM 
16199126SWyllys.Ingersoll@Sun.COM 	switch (cmd) {
16209126SWyllys.Ingersoll@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
16219126SWyllys.Ingersoll@Sun.COM 		*resultp = tpm->dip;
16229126SWyllys.Ingersoll@Sun.COM 		break;
16239126SWyllys.Ingersoll@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
16249126SWyllys.Ingersoll@Sun.COM 		*resultp = 0;
16259126SWyllys.Ingersoll@Sun.COM 		break;
16269126SWyllys.Ingersoll@Sun.COM 	default:
16279126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: cmd %d is not implemented", myname, cmd);
16289126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
16299126SWyllys.Ingersoll@Sun.COM 	}
16309126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
16319126SWyllys.Ingersoll@Sun.COM }
16329126SWyllys.Ingersoll@Sun.COM 
16339126SWyllys.Ingersoll@Sun.COM /*
16349126SWyllys.Ingersoll@Sun.COM  * Driver entry points
16359126SWyllys.Ingersoll@Sun.COM  */
16369126SWyllys.Ingersoll@Sun.COM 
16379126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
16389126SWyllys.Ingersoll@Sun.COM static int
16399126SWyllys.Ingersoll@Sun.COM tpm_open(dev_t *devp, int flag, int otyp, cred_t *cred)
16409126SWyllys.Ingersoll@Sun.COM {
16419126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_open";
16429126SWyllys.Ingersoll@Sun.COM 	int instance;
16439126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
16449126SWyllys.Ingersoll@Sun.COM 
16459126SWyllys.Ingersoll@Sun.COM 	ASSERT(devp != NULL);
16469126SWyllys.Ingersoll@Sun.COM 
16479126SWyllys.Ingersoll@Sun.COM 	instance = getminor(*devp);
16489126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
16499126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
16509126SWyllys.Ingersoll@Sun.COM 		    myname);
16519126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
16529126SWyllys.Ingersoll@Sun.COM 	}
16539126SWyllys.Ingersoll@Sun.COM 	if (otyp != OTYP_CHR) {
16549126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: otyp(%d) != OTYP_CHR(%d)",
16559126SWyllys.Ingersoll@Sun.COM 		    myname, otyp, OTYP_CHR);
16569126SWyllys.Ingersoll@Sun.COM 		return (EINVAL);
16579126SWyllys.Ingersoll@Sun.COM 	}
1658*10346Swyllys.ingersoll@sun.com 	TPM_EXCLUSIVE_LOCK(tpm);
16599126SWyllys.Ingersoll@Sun.COM 
16609126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->dev_lock);
16619126SWyllys.Ingersoll@Sun.COM 	if (tpm->dev_held) {
16629126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: the device is already being used",
16639126SWyllys.Ingersoll@Sun.COM 		    myname);
16649126SWyllys.Ingersoll@Sun.COM 		mutex_exit(&tpm->dev_lock);
16659126SWyllys.Ingersoll@Sun.COM 		return (EBUSY);
16669126SWyllys.Ingersoll@Sun.COM 	}
16679126SWyllys.Ingersoll@Sun.COM 
16689126SWyllys.Ingersoll@Sun.COM 	/* The device is free so mark it busy */
16699126SWyllys.Ingersoll@Sun.COM 	tpm->dev_held = 1;
16709126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->dev_lock);
16719126SWyllys.Ingersoll@Sun.COM 
16729126SWyllys.Ingersoll@Sun.COM 	return (0);
16739126SWyllys.Ingersoll@Sun.COM }
16749126SWyllys.Ingersoll@Sun.COM 
16759126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
16769126SWyllys.Ingersoll@Sun.COM static int
16779126SWyllys.Ingersoll@Sun.COM tpm_close(dev_t dev, int flag, int otyp, cred_t *cred)
16789126SWyllys.Ingersoll@Sun.COM {
16799126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_close";
16809126SWyllys.Ingersoll@Sun.COM 	int instance;
16819126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
16829126SWyllys.Ingersoll@Sun.COM 
16839126SWyllys.Ingersoll@Sun.COM 	instance = getminor(dev);
16849126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
16859126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
16869126SWyllys.Ingersoll@Sun.COM 		    myname);
16879126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
16889126SWyllys.Ingersoll@Sun.COM 	}
16899126SWyllys.Ingersoll@Sun.COM 	if (otyp != OTYP_CHR) {
16909126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: otyp(%d) != OTYP_CHR(%d)",
16919126SWyllys.Ingersoll@Sun.COM 		    myname, otyp, OTYP_CHR);
16929126SWyllys.Ingersoll@Sun.COM 		return (EINVAL);
16939126SWyllys.Ingersoll@Sun.COM 	}
1694*10346Swyllys.ingersoll@sun.com 	TPM_EXCLUSIVE_LOCK(tpm);
16959126SWyllys.Ingersoll@Sun.COM 
16969126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm->dev_held);
16979126SWyllys.Ingersoll@Sun.COM 
16989126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->dev_lock);
16999126SWyllys.Ingersoll@Sun.COM 	ASSERT(mutex_owned(&tpm->dev_lock));
17009126SWyllys.Ingersoll@Sun.COM 	tpm->dev_held = 0;
17019126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->dev_lock);
17029126SWyllys.Ingersoll@Sun.COM 
17039126SWyllys.Ingersoll@Sun.COM 	return (0);
17049126SWyllys.Ingersoll@Sun.COM }
17059126SWyllys.Ingersoll@Sun.COM 
17069126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
17079126SWyllys.Ingersoll@Sun.COM static int
17089126SWyllys.Ingersoll@Sun.COM tpm_read(dev_t dev, struct uio *uiop, cred_t *credp)
17099126SWyllys.Ingersoll@Sun.COM {
17109126SWyllys.Ingersoll@Sun.COM 	int ret;
17119126SWyllys.Ingersoll@Sun.COM 	uint32_t size;
17129126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_read";
17139126SWyllys.Ingersoll@Sun.COM 	int instance;
17149126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
17159126SWyllys.Ingersoll@Sun.COM 
17169126SWyllys.Ingersoll@Sun.COM 	instance = getminor(dev);
17179126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
17189126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
17199126SWyllys.Ingersoll@Sun.COM 		    myname);
17209126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
17219126SWyllys.Ingersoll@Sun.COM 	}
17229126SWyllys.Ingersoll@Sun.COM 	if (uiop == NULL) {
17239126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: passed in uiop is NULL", myname);
17249126SWyllys.Ingersoll@Sun.COM 		return (EFAULT);
17259126SWyllys.Ingersoll@Sun.COM 	}
17269126SWyllys.Ingersoll@Sun.COM 
1727*10346Swyllys.ingersoll@sun.com 	TPM_EXCLUSIVE_LOCK(tpm);
17289126SWyllys.Ingersoll@Sun.COM 
17299126SWyllys.Ingersoll@Sun.COM 	/* Receive the data after requiring the lock */
1730*10346Swyllys.ingersoll@sun.com 	ret = tpm_io_lock(tpm);
17319126SWyllys.Ingersoll@Sun.COM 
17329126SWyllys.Ingersoll@Sun.COM 	/* Timeout reached */
1733*10346Swyllys.ingersoll@sun.com 	if (ret)
17349126SWyllys.Ingersoll@Sun.COM 		return (ret);
17359126SWyllys.Ingersoll@Sun.COM 
17369126SWyllys.Ingersoll@Sun.COM 	if (uiop->uio_resid > tpm->bufsize) {
17379126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: read_in data is bigger "
17389126SWyllys.Ingersoll@Sun.COM 		    "than tpm->bufsize:read in:%d, bufsiz:%d",
17399126SWyllys.Ingersoll@Sun.COM 		    myname, (int)uiop->uio_resid, (int)tpm->bufsize);
17409126SWyllys.Ingersoll@Sun.COM 		ret = EIO;
17419126SWyllys.Ingersoll@Sun.COM 		goto OUT;
17429126SWyllys.Ingersoll@Sun.COM 	}
17439126SWyllys.Ingersoll@Sun.COM 
17449126SWyllys.Ingersoll@Sun.COM 	ret = tis_recv_data(tpm, tpm->iobuf, tpm->bufsize);
17459126SWyllys.Ingersoll@Sun.COM 	if (ret < TPM_HEADER_SIZE) {
17469126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_recv_data returned error", myname);
17479126SWyllys.Ingersoll@Sun.COM 		ret = EIO;
17489126SWyllys.Ingersoll@Sun.COM 		goto OUT;
17499126SWyllys.Ingersoll@Sun.COM 	}
17509126SWyllys.Ingersoll@Sun.COM 
17519126SWyllys.Ingersoll@Sun.COM 	size = load32(tpm->iobuf, 2);
17529126SWyllys.Ingersoll@Sun.COM 	if (ret != size) {
17539126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_recv_data:"
17549126SWyllys.Ingersoll@Sun.COM 		    "expected size=%d, actually read=%d",
17559126SWyllys.Ingersoll@Sun.COM 		    myname, size, ret);
17569126SWyllys.Ingersoll@Sun.COM 		ret = EIO;
17579126SWyllys.Ingersoll@Sun.COM 		goto OUT;
17589126SWyllys.Ingersoll@Sun.COM 	}
17599126SWyllys.Ingersoll@Sun.COM 
17609126SWyllys.Ingersoll@Sun.COM 	/* Send the buffer from the kernel to the userspace */
17619126SWyllys.Ingersoll@Sun.COM 	ret = uiomove(tpm->iobuf, size, UIO_READ, uiop);
17629126SWyllys.Ingersoll@Sun.COM 	if (ret) {
17639126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: uiomove returned error", myname);
17649126SWyllys.Ingersoll@Sun.COM 		goto OUT;
17659126SWyllys.Ingersoll@Sun.COM 	}
17669126SWyllys.Ingersoll@Sun.COM 
17679126SWyllys.Ingersoll@Sun.COM 	/* Zeroize the buffer... */
17689126SWyllys.Ingersoll@Sun.COM 	bzero(tpm->iobuf, tpm->bufsize);
17699126SWyllys.Ingersoll@Sun.COM 	ret = DDI_SUCCESS;
17709126SWyllys.Ingersoll@Sun.COM OUT:
17719126SWyllys.Ingersoll@Sun.COM 	/* We are done now: wake up the waiting threads */
17729126SWyllys.Ingersoll@Sun.COM 	tpm_unlock(tpm);
17739126SWyllys.Ingersoll@Sun.COM 
17749126SWyllys.Ingersoll@Sun.COM 	return (ret);
17759126SWyllys.Ingersoll@Sun.COM }
17769126SWyllys.Ingersoll@Sun.COM 
17779126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
17789126SWyllys.Ingersoll@Sun.COM static int
17799126SWyllys.Ingersoll@Sun.COM tpm_write(dev_t dev, struct uio *uiop, cred_t *credp)
17809126SWyllys.Ingersoll@Sun.COM {
17819126SWyllys.Ingersoll@Sun.COM 	int ret;
17829126SWyllys.Ingersoll@Sun.COM 	size_t len;
17839126SWyllys.Ingersoll@Sun.COM 	uint32_t size;
17849126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_write";
17859126SWyllys.Ingersoll@Sun.COM 	int instance;
17869126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
17879126SWyllys.Ingersoll@Sun.COM 
17889126SWyllys.Ingersoll@Sun.COM 	instance = getminor(dev);
17899126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
17909126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL",
17919126SWyllys.Ingersoll@Sun.COM 		    myname);
17929126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
17939126SWyllys.Ingersoll@Sun.COM 	}
17949126SWyllys.Ingersoll@Sun.COM 
17959126SWyllys.Ingersoll@Sun.COM 	if (uiop == NULL) {
17969126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: passed in uiop is NULL", myname);
17979126SWyllys.Ingersoll@Sun.COM 		return (EFAULT);
17989126SWyllys.Ingersoll@Sun.COM 	}
17999126SWyllys.Ingersoll@Sun.COM 
1800*10346Swyllys.ingersoll@sun.com 	TPM_EXCLUSIVE_LOCK(tpm);
18019126SWyllys.Ingersoll@Sun.COM 
18029126SWyllys.Ingersoll@Sun.COM 	len = uiop->uio_resid;
18039126SWyllys.Ingersoll@Sun.COM 	if (len == 0) {
18049126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: requested read of len 0", myname);
18059126SWyllys.Ingersoll@Sun.COM 		return (0);
18069126SWyllys.Ingersoll@Sun.COM 	}
18079126SWyllys.Ingersoll@Sun.COM 
18089126SWyllys.Ingersoll@Sun.COM 	/* Get the lock for using iobuf */
1809*10346Swyllys.ingersoll@sun.com 	ret = tpm_io_lock(tpm);
18109126SWyllys.Ingersoll@Sun.COM 	/* Timeout Reached */
1811*10346Swyllys.ingersoll@sun.com 	if (ret)
18129126SWyllys.Ingersoll@Sun.COM 		return (ret);
18139126SWyllys.Ingersoll@Sun.COM 
18149126SWyllys.Ingersoll@Sun.COM 	/* Copy the header and parse the structure to find out the size... */
18159126SWyllys.Ingersoll@Sun.COM 	ret = uiomove(tpm->iobuf, TPM_HEADER_SIZE, UIO_WRITE, uiop);
18169126SWyllys.Ingersoll@Sun.COM 	if (ret) {
18179126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: uiomove returned error"
18189126SWyllys.Ingersoll@Sun.COM 		    "while getting the the header",
18199126SWyllys.Ingersoll@Sun.COM 		    myname);
18209126SWyllys.Ingersoll@Sun.COM 		goto OUT;
18219126SWyllys.Ingersoll@Sun.COM 	}
18229126SWyllys.Ingersoll@Sun.COM 
18239126SWyllys.Ingersoll@Sun.COM 	/* Get the buffersize from the command buffer structure */
18249126SWyllys.Ingersoll@Sun.COM 	size = load32(tpm->iobuf, TPM_PARAMSIZE_OFFSET);
18259126SWyllys.Ingersoll@Sun.COM 
18269126SWyllys.Ingersoll@Sun.COM 	/* Copy the command to the contiguous buffer */
18279126SWyllys.Ingersoll@Sun.COM 	if (size > tpm->bufsize) {
18289126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: size %d is greater than "
18299126SWyllys.Ingersoll@Sun.COM 		    "the tpm's input buffer size %d",
18309126SWyllys.Ingersoll@Sun.COM 		    myname, (int)size, (int)tpm->bufsize);
18319126SWyllys.Ingersoll@Sun.COM 		ret = ENXIO;
18329126SWyllys.Ingersoll@Sun.COM 		goto OUT;
18339126SWyllys.Ingersoll@Sun.COM 	}
18349126SWyllys.Ingersoll@Sun.COM 
18359126SWyllys.Ingersoll@Sun.COM 	/* Copy the buffer from the userspace to kernel */
18369126SWyllys.Ingersoll@Sun.COM 	ret = uiomove(tpm->iobuf+TPM_HEADER_SIZE, size-TPM_HEADER_SIZE,
18379126SWyllys.Ingersoll@Sun.COM 	    UIO_WRITE, uiop);
18389126SWyllys.Ingersoll@Sun.COM 
18399126SWyllys.Ingersoll@Sun.COM 	if (ret) {
18409126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: uiomove returned error"
18419126SWyllys.Ingersoll@Sun.COM 		    "while getting the rest of the command", myname);
18429126SWyllys.Ingersoll@Sun.COM 		goto OUT;
18439126SWyllys.Ingersoll@Sun.COM 	}
18449126SWyllys.Ingersoll@Sun.COM 
18459126SWyllys.Ingersoll@Sun.COM 	/* Send the command */
18469126SWyllys.Ingersoll@Sun.COM 	ret = tis_send_data(tpm, tpm->iobuf, size);
18479126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
18489126SWyllys.Ingersoll@Sun.COM 		cmn_err(CE_WARN, "%s: tis_send_data returned error", myname);
18499126SWyllys.Ingersoll@Sun.COM 		ret = EFAULT;
18509126SWyllys.Ingersoll@Sun.COM 		goto OUT;
18519126SWyllys.Ingersoll@Sun.COM 	}
18529126SWyllys.Ingersoll@Sun.COM 
18539126SWyllys.Ingersoll@Sun.COM 	/* Zeroize the buffer... */
18549126SWyllys.Ingersoll@Sun.COM 	bzero(tpm->iobuf, tpm->bufsize);
18559126SWyllys.Ingersoll@Sun.COM 	ret = DDI_SUCCESS;
18569126SWyllys.Ingersoll@Sun.COM OUT:
18579126SWyllys.Ingersoll@Sun.COM 	tpm_unlock(tpm);
18589126SWyllys.Ingersoll@Sun.COM 	return (ret);
18599126SWyllys.Ingersoll@Sun.COM }
18609126SWyllys.Ingersoll@Sun.COM 
18619126SWyllys.Ingersoll@Sun.COM /*
18629126SWyllys.Ingersoll@Sun.COM  * This is to deal with the contentions for the iobuf
18639126SWyllys.Ingersoll@Sun.COM  */
18649126SWyllys.Ingersoll@Sun.COM static inline int
1865*10346Swyllys.ingersoll@sun.com tpm_io_lock(tpm_state_t *tpm)
18669126SWyllys.Ingersoll@Sun.COM {
18679126SWyllys.Ingersoll@Sun.COM 	int ret;
18689126SWyllys.Ingersoll@Sun.COM 	clock_t timeout;
18699126SWyllys.Ingersoll@Sun.COM 
18709126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->iobuf_lock);
18719126SWyllys.Ingersoll@Sun.COM 	ASSERT(mutex_owned(&tpm->iobuf_lock));
18729126SWyllys.Ingersoll@Sun.COM 
18739126SWyllys.Ingersoll@Sun.COM 	timeout = ddi_get_lbolt() + drv_usectohz(TPM_IO_TIMEOUT);
18749126SWyllys.Ingersoll@Sun.COM 
18759126SWyllys.Ingersoll@Sun.COM 	/* Wait until the iobuf becomes free with the timeout */
18769126SWyllys.Ingersoll@Sun.COM 	while (tpm->iobuf_inuse) {
18779126SWyllys.Ingersoll@Sun.COM 		ret = cv_timedwait(&tpm->iobuf_cv, &tpm->iobuf_lock, timeout);
18789126SWyllys.Ingersoll@Sun.COM 		if (ret <= 0) {
18799126SWyllys.Ingersoll@Sun.COM 			/* Timeout reached */
18809126SWyllys.Ingersoll@Sun.COM 			mutex_exit(&tpm->iobuf_lock);
1881*10346Swyllys.ingersoll@sun.com #ifdef DEBUG
1882*10346Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "tpm_io_lock:iorequest timed out");
1883*10346Swyllys.ingersoll@sun.com #endif
188410335SJames.McPherson@Sun.COM 			return (ETIME);
18859126SWyllys.Ingersoll@Sun.COM 		}
18869126SWyllys.Ingersoll@Sun.COM 	}
18879126SWyllys.Ingersoll@Sun.COM 	tpm->iobuf_inuse = 1;
18889126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->iobuf_lock);
18899126SWyllys.Ingersoll@Sun.COM 	return (0);
18909126SWyllys.Ingersoll@Sun.COM }
18919126SWyllys.Ingersoll@Sun.COM 
18929126SWyllys.Ingersoll@Sun.COM /*
18939126SWyllys.Ingersoll@Sun.COM  * This is to deal with the contentions for the iobuf
18949126SWyllys.Ingersoll@Sun.COM  */
18959126SWyllys.Ingersoll@Sun.COM static inline void
18969126SWyllys.Ingersoll@Sun.COM tpm_unlock(tpm_state_t *tpm)
18979126SWyllys.Ingersoll@Sun.COM {
18989126SWyllys.Ingersoll@Sun.COM 	/* Wake up the waiting threads */
18999126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->iobuf_lock);
19009126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm->iobuf_inuse == 1 && mutex_owned(&tpm->iobuf_lock));
19019126SWyllys.Ingersoll@Sun.COM 	tpm->iobuf_inuse = 0;
19029126SWyllys.Ingersoll@Sun.COM 	cv_broadcast(&tpm->iobuf_cv);
19039126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->iobuf_lock);
19049126SWyllys.Ingersoll@Sun.COM }
1905*10346Swyllys.ingersoll@sun.com 
1906*10346Swyllys.ingersoll@sun.com #ifdef KCF_TPM_RNG_PROVIDER
1907*10346Swyllys.ingersoll@sun.com /*
1908*10346Swyllys.ingersoll@sun.com  * Random number generator entry points
1909*10346Swyllys.ingersoll@sun.com  */
1910*10346Swyllys.ingersoll@sun.com static void
1911*10346Swyllys.ingersoll@sun.com strncpy_spacepad(uchar_t *s1, char *s2, int n)
1912*10346Swyllys.ingersoll@sun.com {
1913*10346Swyllys.ingersoll@sun.com 	int s2len = strlen(s2);
1914*10346Swyllys.ingersoll@sun.com 	(void) strncpy((char *)s1, s2, n);
1915*10346Swyllys.ingersoll@sun.com 	if (s2len < n)
1916*10346Swyllys.ingersoll@sun.com 		(void) memset(s1 + s2len, ' ', n - s2len);
1917*10346Swyllys.ingersoll@sun.com }
1918*10346Swyllys.ingersoll@sun.com 
1919*10346Swyllys.ingersoll@sun.com /*ARGSUSED*/
1920*10346Swyllys.ingersoll@sun.com static int
1921*10346Swyllys.ingersoll@sun.com tpmrng_ext_info(crypto_provider_handle_t prov,
1922*10346Swyllys.ingersoll@sun.com 	crypto_provider_ext_info_t *ext_info,
1923*10346Swyllys.ingersoll@sun.com 	crypto_req_handle_t cfreq)
1924*10346Swyllys.ingersoll@sun.com {
1925*10346Swyllys.ingersoll@sun.com 	tpm_state_t *tpm = (tpm_state_t *)prov;
1926*10346Swyllys.ingersoll@sun.com 	char buf[64];
1927*10346Swyllys.ingersoll@sun.com 
1928*10346Swyllys.ingersoll@sun.com 	if (tpm == NULL)
1929*10346Swyllys.ingersoll@sun.com 		return (DDI_FAILURE);
1930*10346Swyllys.ingersoll@sun.com 
1931*10346Swyllys.ingersoll@sun.com 	strncpy_spacepad(ext_info->ei_manufacturerID,
1932*10346Swyllys.ingersoll@sun.com 	    (char *)tpm->vers_info.tpmVendorID,
1933*10346Swyllys.ingersoll@sun.com 	    sizeof (ext_info->ei_manufacturerID));
1934*10346Swyllys.ingersoll@sun.com 
1935*10346Swyllys.ingersoll@sun.com 	strncpy_spacepad(ext_info->ei_model, "0",
1936*10346Swyllys.ingersoll@sun.com 	    sizeof (ext_info->ei_model));
1937*10346Swyllys.ingersoll@sun.com 	strncpy_spacepad(ext_info->ei_serial_number, "0",
1938*10346Swyllys.ingersoll@sun.com 	    sizeof (ext_info->ei_serial_number));
1939*10346Swyllys.ingersoll@sun.com 
1940*10346Swyllys.ingersoll@sun.com 	ext_info->ei_flags = CRYPTO_EXTF_RNG | CRYPTO_EXTF_SO_PIN_LOCKED;
1941*10346Swyllys.ingersoll@sun.com 	ext_info->ei_max_session_count = CRYPTO_EFFECTIVELY_INFINITE;
1942*10346Swyllys.ingersoll@sun.com 	ext_info->ei_max_pin_len = 0;
1943*10346Swyllys.ingersoll@sun.com 	ext_info->ei_min_pin_len = 0;
1944*10346Swyllys.ingersoll@sun.com 	ext_info->ei_total_public_memory = CRYPTO_UNAVAILABLE_INFO;
1945*10346Swyllys.ingersoll@sun.com 	ext_info->ei_free_public_memory = CRYPTO_UNAVAILABLE_INFO;
1946*10346Swyllys.ingersoll@sun.com 	ext_info->ei_total_private_memory = CRYPTO_UNAVAILABLE_INFO;
1947*10346Swyllys.ingersoll@sun.com 	ext_info->ei_free_public_memory = CRYPTO_UNAVAILABLE_INFO;
1948*10346Swyllys.ingersoll@sun.com 	ext_info->ei_time[0] = 0;
1949*10346Swyllys.ingersoll@sun.com 
1950*10346Swyllys.ingersoll@sun.com 	ext_info->ei_hardware_version.cv_major = tpm->vers_info.version.major;
1951*10346Swyllys.ingersoll@sun.com 	ext_info->ei_hardware_version.cv_minor = tpm->vers_info.version.minor;
1952*10346Swyllys.ingersoll@sun.com 	ext_info->ei_firmware_version.cv_major =
1953*10346Swyllys.ingersoll@sun.com 	    tpm->vers_info.version.revMajor;
1954*10346Swyllys.ingersoll@sun.com 	ext_info->ei_firmware_version.cv_minor =
1955*10346Swyllys.ingersoll@sun.com 	    tpm->vers_info.version.revMinor;
1956*10346Swyllys.ingersoll@sun.com 
1957*10346Swyllys.ingersoll@sun.com 	(void) snprintf(buf, sizeof (buf), "tpmrng TPM RNG");
1958*10346Swyllys.ingersoll@sun.com 
1959*10346Swyllys.ingersoll@sun.com 	strncpy_spacepad(ext_info->ei_label, buf,
1960*10346Swyllys.ingersoll@sun.com 	    sizeof (ext_info->ei_label));
1961*10346Swyllys.ingersoll@sun.com #undef	BUFSZ
1962*10346Swyllys.ingersoll@sun.com 	return (CRYPTO_SUCCESS);
1963*10346Swyllys.ingersoll@sun.com 
1964*10346Swyllys.ingersoll@sun.com }
1965*10346Swyllys.ingersoll@sun.com 
1966*10346Swyllys.ingersoll@sun.com static int
1967*10346Swyllys.ingersoll@sun.com tpmrng_register(tpm_state_t *tpm)
1968*10346Swyllys.ingersoll@sun.com {
1969*10346Swyllys.ingersoll@sun.com 	int		ret;
1970*10346Swyllys.ingersoll@sun.com 	char 		ID[64];
1971*10346Swyllys.ingersoll@sun.com 	crypto_mech_name_t	*rngmech;
1972*10346Swyllys.ingersoll@sun.com 
1973*10346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
1974*10346Swyllys.ingersoll@sun.com 
1975*10346Swyllys.ingersoll@sun.com 	(void) snprintf(ID, sizeof (ID), "tpmrng %s", IDENT_TPMRNG);
1976*10346Swyllys.ingersoll@sun.com 
1977*10346Swyllys.ingersoll@sun.com 	tpmrng_prov_info.pi_provider_description = ID;
1978*10346Swyllys.ingersoll@sun.com 	tpmrng_prov_info.pi_provider_dev.pd_hw = tpm->dip;
1979*10346Swyllys.ingersoll@sun.com 	tpmrng_prov_info.pi_provider_handle = tpm;
1980*10346Swyllys.ingersoll@sun.com 
1981*10346Swyllys.ingersoll@sun.com 	ret = crypto_register_provider(&tpmrng_prov_info, &tpm->n_prov);
1982*10346Swyllys.ingersoll@sun.com 	if (ret != CRYPTO_SUCCESS) {
1983*10346Swyllys.ingersoll@sun.com 		tpm->n_prov = NULL;
1984*10346Swyllys.ingersoll@sun.com 		return (DDI_FAILURE);
1985*10346Swyllys.ingersoll@sun.com 	}
1986*10346Swyllys.ingersoll@sun.com 
1987*10346Swyllys.ingersoll@sun.com 	crypto_provider_notification(tpm->n_prov, CRYPTO_PROVIDER_READY);
1988*10346Swyllys.ingersoll@sun.com 
1989*10346Swyllys.ingersoll@sun.com 	rngmech = kmem_zalloc(strlen("random") + 1, KM_SLEEP);
1990*10346Swyllys.ingersoll@sun.com 	(void) memcpy(rngmech, "random", 6);
1991*10346Swyllys.ingersoll@sun.com 	ret = crypto_load_dev_disabled("tpm", ddi_get_instance(tpm->dip),
1992*10346Swyllys.ingersoll@sun.com 	    1, rngmech);
1993*10346Swyllys.ingersoll@sun.com 	if (ret != CRYPTO_SUCCESS) {
1994*10346Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "crypto_load_dev_disabled failed (%d)", ret);
1995*10346Swyllys.ingersoll@sun.com 	}
1996*10346Swyllys.ingersoll@sun.com 	return (DDI_SUCCESS);
1997*10346Swyllys.ingersoll@sun.com }
1998*10346Swyllys.ingersoll@sun.com 
1999*10346Swyllys.ingersoll@sun.com static int
2000*10346Swyllys.ingersoll@sun.com tpmrng_unregister(tpm_state_t *tpm)
2001*10346Swyllys.ingersoll@sun.com {
2002*10346Swyllys.ingersoll@sun.com 	int ret;
2003*10346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
2004*10346Swyllys.ingersoll@sun.com 	if (tpm->n_prov) {
2005*10346Swyllys.ingersoll@sun.com 		ret = crypto_unregister_provider(tpm->n_prov);
2006*10346Swyllys.ingersoll@sun.com 		tpm->n_prov = NULL;
2007*10346Swyllys.ingersoll@sun.com 		if (ret != CRYPTO_SUCCESS)
2008*10346Swyllys.ingersoll@sun.com 			return (DDI_FAILURE);
2009*10346Swyllys.ingersoll@sun.com 	}
2010*10346Swyllys.ingersoll@sun.com 	return (DDI_SUCCESS);
2011*10346Swyllys.ingersoll@sun.com }
2012*10346Swyllys.ingersoll@sun.com 
2013*10346Swyllys.ingersoll@sun.com /*ARGSUSED*/
2014*10346Swyllys.ingersoll@sun.com static void
2015*10346Swyllys.ingersoll@sun.com tpmrng_provider_status(crypto_provider_handle_t provider, uint_t *status)
2016*10346Swyllys.ingersoll@sun.com {
2017*10346Swyllys.ingersoll@sun.com 	*status = CRYPTO_PROVIDER_READY;
2018*10346Swyllys.ingersoll@sun.com }
2019*10346Swyllys.ingersoll@sun.com 
2020*10346Swyllys.ingersoll@sun.com /*ARGSUSED*/
2021*10346Swyllys.ingersoll@sun.com static int
2022*10346Swyllys.ingersoll@sun.com tpmrng_seed_random(crypto_provider_handle_t provider, crypto_session_id_t sid,
2023*10346Swyllys.ingersoll@sun.com     uchar_t *buf, size_t len, uint_t entropy_est, uint32_t flags,
2024*10346Swyllys.ingersoll@sun.com     crypto_req_handle_t req)
2025*10346Swyllys.ingersoll@sun.com {
2026*10346Swyllys.ingersoll@sun.com 	int ret;
2027*10346Swyllys.ingersoll@sun.com 	tpm_state_t *tpm;
2028*10346Swyllys.ingersoll@sun.com 	uint32_t len32;
2029*10346Swyllys.ingersoll@sun.com 	/* Max length of seed is 256 bytes, add 14 for header. */
2030*10346Swyllys.ingersoll@sun.com 	uint8_t cmdbuf[270] = {
2031*10346Swyllys.ingersoll@sun.com 		0, 193,		/* TPM_TAG_RQU COMMAND */
2032*10346Swyllys.ingersoll@sun.com 		0, 0, 0, 0x0A,	/* paramsize in bytes */
2033*10346Swyllys.ingersoll@sun.com 		0, 0, 0, TPM_ORD_StirRandom,
2034*10346Swyllys.ingersoll@sun.com 		0, 0, 0, 0 	/* number of input bytes (< 256) */
2035*10346Swyllys.ingersoll@sun.com 	};
2036*10346Swyllys.ingersoll@sun.com 	uint32_t buflen;
2037*10346Swyllys.ingersoll@sun.com 
2038*10346Swyllys.ingersoll@sun.com 	if (len == 0 || len > 255 || buf == NULL)
2039*10346Swyllys.ingersoll@sun.com 		return (CRYPTO_ARGUMENTS_BAD);
2040*10346Swyllys.ingersoll@sun.com 
2041*10346Swyllys.ingersoll@sun.com 	tpm = (tpm_state_t *)provider;
2042*10346Swyllys.ingersoll@sun.com 	if (tpm == NULL)
2043*10346Swyllys.ingersoll@sun.com 		return (CRYPTO_INVALID_CONTEXT);
2044*10346Swyllys.ingersoll@sun.com 
2045*10346Swyllys.ingersoll@sun.com 	/* Acquire lock for exclusive use of TPM */
2046*10346Swyllys.ingersoll@sun.com 	TPM_EXCLUSIVE_LOCK(tpm);
2047*10346Swyllys.ingersoll@sun.com 
2048*10346Swyllys.ingersoll@sun.com 	ret = tpm_io_lock(tpm);
2049*10346Swyllys.ingersoll@sun.com 	/* Timeout reached */
2050*10346Swyllys.ingersoll@sun.com 	if (ret)
2051*10346Swyllys.ingersoll@sun.com 		return (CRYPTO_BUSY);
2052*10346Swyllys.ingersoll@sun.com 
2053*10346Swyllys.ingersoll@sun.com 	/* TPM only handles 32 bit length, so truncate if too big. */
2054*10346Swyllys.ingersoll@sun.com 	len32 = (uint32_t)len;
2055*10346Swyllys.ingersoll@sun.com 	buflen = len32 + 14;
2056*10346Swyllys.ingersoll@sun.com 
2057*10346Swyllys.ingersoll@sun.com 	/* The length must be in network order */
2058*10346Swyllys.ingersoll@sun.com 	buflen = htonl(buflen);
2059*10346Swyllys.ingersoll@sun.com 	bcopy(&buflen, cmdbuf + 2, sizeof (uint32_t));
2060*10346Swyllys.ingersoll@sun.com 
2061*10346Swyllys.ingersoll@sun.com 	/* Convert it back */
2062*10346Swyllys.ingersoll@sun.com 	buflen = ntohl(buflen);
2063*10346Swyllys.ingersoll@sun.com 
2064*10346Swyllys.ingersoll@sun.com 	/* length must be in network order */
2065*10346Swyllys.ingersoll@sun.com 	len32 = htonl(len32);
2066*10346Swyllys.ingersoll@sun.com 	bcopy(&len32, cmdbuf + 10, sizeof (uint32_t));
2067*10346Swyllys.ingersoll@sun.com 
2068*10346Swyllys.ingersoll@sun.com 	/* convert it back */
2069*10346Swyllys.ingersoll@sun.com 	len32 = ntohl(len32);
2070*10346Swyllys.ingersoll@sun.com 
2071*10346Swyllys.ingersoll@sun.com 	bcopy(buf,  cmdbuf + 14, len32);
2072*10346Swyllys.ingersoll@sun.com 
2073*10346Swyllys.ingersoll@sun.com 	ret = itpm_command(tpm, cmdbuf, buflen);
2074*10346Swyllys.ingersoll@sun.com 	tpm_unlock(tpm);
2075*10346Swyllys.ingersoll@sun.com 
2076*10346Swyllys.ingersoll@sun.com 	if (ret != DDI_SUCCESS) {
2077*10346Swyllys.ingersoll@sun.com #ifdef DEBUG
2078*10346Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "tpmrng_seed_random failed");
2079*10346Swyllys.ingersoll@sun.com #endif
2080*10346Swyllys.ingersoll@sun.com 		return (CRYPTO_FAILED);
2081*10346Swyllys.ingersoll@sun.com 	}
2082*10346Swyllys.ingersoll@sun.com 
2083*10346Swyllys.ingersoll@sun.com 	return (CRYPTO_SUCCESS);
2084*10346Swyllys.ingersoll@sun.com }
2085*10346Swyllys.ingersoll@sun.com 
2086*10346Swyllys.ingersoll@sun.com /* ARGSUSED */
2087*10346Swyllys.ingersoll@sun.com static int
2088*10346Swyllys.ingersoll@sun.com tpmrng_generate_random(crypto_provider_handle_t provider,
2089*10346Swyllys.ingersoll@sun.com     crypto_session_id_t sid, uchar_t *buf, size_t len, crypto_req_handle_t req)
2090*10346Swyllys.ingersoll@sun.com {
2091*10346Swyllys.ingersoll@sun.com 	int ret;
2092*10346Swyllys.ingersoll@sun.com 	tpm_state_t *tpm;
2093*10346Swyllys.ingersoll@sun.com 	uint8_t hdr[14] = {
2094*10346Swyllys.ingersoll@sun.com 		0, 193,		/* TPM_TAG_RQU COMMAND */
2095*10346Swyllys.ingersoll@sun.com 		0, 0, 0, 14,	/* paramsize in bytes */
2096*10346Swyllys.ingersoll@sun.com 		0, 0, 0, TPM_ORD_GetRandom,
2097*10346Swyllys.ingersoll@sun.com 		0, 0, 0, 0
2098*10346Swyllys.ingersoll@sun.com 	};
2099*10346Swyllys.ingersoll@sun.com 	uint8_t *cmdbuf = NULL;
2100*10346Swyllys.ingersoll@sun.com 	uint32_t len32 = (uint32_t)len;
2101*10346Swyllys.ingersoll@sun.com 	uint32_t buflen = len32 + sizeof (hdr);
2102*10346Swyllys.ingersoll@sun.com 
2103*10346Swyllys.ingersoll@sun.com 	if (len == 0 || buf == NULL)
2104*10346Swyllys.ingersoll@sun.com 		return (CRYPTO_ARGUMENTS_BAD);
2105*10346Swyllys.ingersoll@sun.com 
2106*10346Swyllys.ingersoll@sun.com 	tpm = (tpm_state_t *)provider;
2107*10346Swyllys.ingersoll@sun.com 	if (tpm == NULL)
2108*10346Swyllys.ingersoll@sun.com 		return (CRYPTO_INVALID_CONTEXT);
2109*10346Swyllys.ingersoll@sun.com 
2110*10346Swyllys.ingersoll@sun.com 	TPM_EXCLUSIVE_LOCK(tpm);
2111*10346Swyllys.ingersoll@sun.com 
2112*10346Swyllys.ingersoll@sun.com 	ret = tpm_io_lock(tpm);
2113*10346Swyllys.ingersoll@sun.com 	/* Timeout reached */
2114*10346Swyllys.ingersoll@sun.com 	if (ret)
2115*10346Swyllys.ingersoll@sun.com 		return (CRYPTO_BUSY);
2116*10346Swyllys.ingersoll@sun.com 
2117*10346Swyllys.ingersoll@sun.com 	cmdbuf = kmem_zalloc(buflen, KM_SLEEP);
2118*10346Swyllys.ingersoll@sun.com 	bcopy(hdr, cmdbuf, sizeof (hdr));
2119*10346Swyllys.ingersoll@sun.com 
2120*10346Swyllys.ingersoll@sun.com 	/* Length is written in network byte order */
2121*10346Swyllys.ingersoll@sun.com 	len32 = htonl(len32);
2122*10346Swyllys.ingersoll@sun.com 	bcopy(&len32, cmdbuf + 10, sizeof (uint32_t));
2123*10346Swyllys.ingersoll@sun.com 
2124*10346Swyllys.ingersoll@sun.com 	ret = itpm_command(tpm, cmdbuf, buflen);
2125*10346Swyllys.ingersoll@sun.com 	if (ret != DDI_SUCCESS) {
2126*10346Swyllys.ingersoll@sun.com #ifdef DEBUG
2127*10346Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "tpmrng_generate_random failed");
2128*10346Swyllys.ingersoll@sun.com #endif
2129*10346Swyllys.ingersoll@sun.com 		kmem_free(cmdbuf, buflen);
2130*10346Swyllys.ingersoll@sun.com 		tpm_unlock(tpm);
2131*10346Swyllys.ingersoll@sun.com 		return (CRYPTO_FAILED);
2132*10346Swyllys.ingersoll@sun.com 	}
2133*10346Swyllys.ingersoll@sun.com 
2134*10346Swyllys.ingersoll@sun.com 	/* Find out how many bytes were really returned */
2135*10346Swyllys.ingersoll@sun.com 	len32 = load32(cmdbuf, 10);
2136*10346Swyllys.ingersoll@sun.com 
2137*10346Swyllys.ingersoll@sun.com 	/* Copy the random bytes back to the callers buffer */
2138*10346Swyllys.ingersoll@sun.com 	bcopy(cmdbuf + 14, buf, len32);
2139*10346Swyllys.ingersoll@sun.com 
2140*10346Swyllys.ingersoll@sun.com 	kmem_free(cmdbuf, buflen);
2141*10346Swyllys.ingersoll@sun.com 	tpm_unlock(tpm);
2142*10346Swyllys.ingersoll@sun.com 
2143*10346Swyllys.ingersoll@sun.com 	return (CRYPTO_SUCCESS);
2144*10346Swyllys.ingersoll@sun.com }
2145*10346Swyllys.ingersoll@sun.com #endif /* KCF_TPM_RNG_PROVIDER */
2146