xref: /onnv-gate/usr/src/uts/common/io/tpm/tpm.c (revision 10561:bf86b7e90c69)
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 
4810346Swyllys.ingersoll@sun.com #ifdef sun4v
4910346Swyllys.ingersoll@sun.com #include <sys/hypervisor_api.h>
5010346Swyllys.ingersoll@sun.com #include <sys/hsvc.h>
5110346Swyllys.ingersoll@sun.com #endif
5210346Swyllys.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 
9610346Swyllys.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);
14610346Swyllys.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 
22010346Swyllys.ingersoll@sun.com 
22110346Swyllys.ingersoll@sun.com #ifdef KCF_TPM_RNG_PROVIDER
22210346Swyllys.ingersoll@sun.com 
22310346Swyllys.ingersoll@sun.com #define	IDENT_TPMRNG	"TPM Random Number Generator"
22410346Swyllys.ingersoll@sun.com 
22510346Swyllys.ingersoll@sun.com #include <sys/crypto/common.h>
22610346Swyllys.ingersoll@sun.com #include <sys/crypto/impl.h>
22710346Swyllys.ingersoll@sun.com #include <sys/crypto/spi.h>
22810346Swyllys.ingersoll@sun.com /*
22910346Swyllys.ingersoll@sun.com  * CSPI information (entry points, provider info, etc.)
23010346Swyllys.ingersoll@sun.com  */
23110346Swyllys.ingersoll@sun.com static void tpmrng_provider_status(crypto_provider_handle_t, uint_t *);
23210346Swyllys.ingersoll@sun.com 
23310346Swyllys.ingersoll@sun.com static crypto_control_ops_t tpmrng_control_ops = {
23410346Swyllys.ingersoll@sun.com 	tpmrng_provider_status
23510346Swyllys.ingersoll@sun.com };
23610346Swyllys.ingersoll@sun.com 
23710346Swyllys.ingersoll@sun.com static int tpmrng_seed_random(crypto_provider_handle_t, crypto_session_id_t,
23810346Swyllys.ingersoll@sun.com     uchar_t *, size_t, uint_t, uint32_t, crypto_req_handle_t);
23910346Swyllys.ingersoll@sun.com 
24010346Swyllys.ingersoll@sun.com static int tpmrng_generate_random(crypto_provider_handle_t,
24110346Swyllys.ingersoll@sun.com     crypto_session_id_t, uchar_t *, size_t, crypto_req_handle_t);
24210346Swyllys.ingersoll@sun.com 
24310346Swyllys.ingersoll@sun.com static crypto_random_number_ops_t tpmrng_random_number_ops = {
24410346Swyllys.ingersoll@sun.com 	tpmrng_seed_random,
24510346Swyllys.ingersoll@sun.com 	tpmrng_generate_random
24610346Swyllys.ingersoll@sun.com };
24710346Swyllys.ingersoll@sun.com 
24810346Swyllys.ingersoll@sun.com static int tpmrng_ext_info(crypto_provider_handle_t,
24910346Swyllys.ingersoll@sun.com 	crypto_provider_ext_info_t *,
25010346Swyllys.ingersoll@sun.com 	crypto_req_handle_t);
25110346Swyllys.ingersoll@sun.com 
25210346Swyllys.ingersoll@sun.com static crypto_provider_management_ops_t tpmrng_extinfo_op = {
25310346Swyllys.ingersoll@sun.com 	tpmrng_ext_info,
25410346Swyllys.ingersoll@sun.com 	NULL,
25510346Swyllys.ingersoll@sun.com 	NULL,
25610346Swyllys.ingersoll@sun.com 	NULL
25710346Swyllys.ingersoll@sun.com };
25810346Swyllys.ingersoll@sun.com 
25910346Swyllys.ingersoll@sun.com static int tpmrng_register(tpm_state_t *);
26010346Swyllys.ingersoll@sun.com static int tpmrng_unregister(tpm_state_t *);
26110346Swyllys.ingersoll@sun.com 
26210346Swyllys.ingersoll@sun.com static crypto_ops_t tpmrng_crypto_ops = {
26310346Swyllys.ingersoll@sun.com 	&tpmrng_control_ops,
26410346Swyllys.ingersoll@sun.com 	NULL,
26510346Swyllys.ingersoll@sun.com 	NULL,
26610346Swyllys.ingersoll@sun.com 	NULL,
26710346Swyllys.ingersoll@sun.com 	NULL,
26810346Swyllys.ingersoll@sun.com 	NULL,
26910346Swyllys.ingersoll@sun.com 	NULL,
27010346Swyllys.ingersoll@sun.com 	NULL,
27110346Swyllys.ingersoll@sun.com 	&tpmrng_random_number_ops,
27210346Swyllys.ingersoll@sun.com 	NULL,
27310346Swyllys.ingersoll@sun.com 	NULL,
27410346Swyllys.ingersoll@sun.com 	NULL,
27510346Swyllys.ingersoll@sun.com 	&tpmrng_extinfo_op,
27610346Swyllys.ingersoll@sun.com 	NULL,
27710346Swyllys.ingersoll@sun.com 	NULL
27810346Swyllys.ingersoll@sun.com };
27910346Swyllys.ingersoll@sun.com 
28010346Swyllys.ingersoll@sun.com static crypto_provider_info_t tpmrng_prov_info = {
28110346Swyllys.ingersoll@sun.com 	CRYPTO_SPI_VERSION_2,
28210346Swyllys.ingersoll@sun.com 	"TPM Random Number Provider",
28310346Swyllys.ingersoll@sun.com 	CRYPTO_HW_PROVIDER,
28410346Swyllys.ingersoll@sun.com 	NULL,
28510346Swyllys.ingersoll@sun.com 	NULL,
28610346Swyllys.ingersoll@sun.com 	&tpmrng_crypto_ops,
28710346Swyllys.ingersoll@sun.com 	0,
28810346Swyllys.ingersoll@sun.com 	NULL,
28910346Swyllys.ingersoll@sun.com 	0,
29010346Swyllys.ingersoll@sun.com 	NULL
29110346Swyllys.ingersoll@sun.com };
29210346Swyllys.ingersoll@sun.com #endif /* KCF_TPM_RNG_PROVIDER */
29310346Swyllys.ingersoll@sun.com 
2949126SWyllys.Ingersoll@Sun.COM static void *statep = NULL;
2959126SWyllys.Ingersoll@Sun.COM 
2969126SWyllys.Ingersoll@Sun.COM /*
29710346Swyllys.ingersoll@sun.com  * Inline code to get exclusive lock on the TPM device and to make sure
29810346Swyllys.ingersoll@sun.com  * the device is not suspended.  This grabs the primary TPM mutex (pm_mutex)
29910346Swyllys.ingersoll@sun.com  * and then checks the suspend status.  If suspended, it will wait until
30010346Swyllys.ingersoll@sun.com  * the device is "resumed" before releasing the pm_mutex and continuing.
30110346Swyllys.ingersoll@sun.com  */
30210346Swyllys.ingersoll@sun.com #define	TPM_EXCLUSIVE_LOCK(tpm)  { \
30310346Swyllys.ingersoll@sun.com 	mutex_enter(&tpm->pm_mutex); \
30410346Swyllys.ingersoll@sun.com 	while (tpm->suspended) \
30510346Swyllys.ingersoll@sun.com 		cv_wait(&tpm->suspend_cv, &tpm->pm_mutex); \
30610346Swyllys.ingersoll@sun.com 	mutex_exit(&tpm->pm_mutex); }
30710346Swyllys.ingersoll@sun.com 
30810346Swyllys.ingersoll@sun.com /*
30910346Swyllys.ingersoll@sun.com  * TPM accessor functions
31010346Swyllys.ingersoll@sun.com  */
31110346Swyllys.ingersoll@sun.com #ifdef sun4v
31210346Swyllys.ingersoll@sun.com 
31310346Swyllys.ingersoll@sun.com extern uint64_t
31410346Swyllys.ingersoll@sun.com hcall_tpm_get(uint64_t, uint64_t, uint64_t, uint64_t *);
31510346Swyllys.ingersoll@sun.com 
31610346Swyllys.ingersoll@sun.com extern uint64_t
31710346Swyllys.ingersoll@sun.com hcall_tpm_put(uint64_t, uint64_t, uint64_t, uint64_t);
31810346Swyllys.ingersoll@sun.com 
31910346Swyllys.ingersoll@sun.com static inline uint8_t
tpm_get8(tpm_state_t * tpm,unsigned long offset)32010346Swyllys.ingersoll@sun.com tpm_get8(tpm_state_t *tpm, unsigned long offset)
32110346Swyllys.ingersoll@sun.com {
32210346Swyllys.ingersoll@sun.com 	uint64_t value;
32310346Swyllys.ingersoll@sun.com 
32410346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
32510346Swyllys.ingersoll@sun.com 	(void) hcall_tpm_get(tpm->locality, offset, sizeof (uint8_t), &value);
32610346Swyllys.ingersoll@sun.com 	return ((uint8_t)value);
32710346Swyllys.ingersoll@sun.com }
32810346Swyllys.ingersoll@sun.com 
32910346Swyllys.ingersoll@sun.com static inline uint32_t
tpm_get32(tpm_state_t * tpm,unsigned long offset)33010346Swyllys.ingersoll@sun.com tpm_get32(tpm_state_t *tpm, unsigned long offset)
33110346Swyllys.ingersoll@sun.com {
33210346Swyllys.ingersoll@sun.com 	uint64_t value;
33310346Swyllys.ingersoll@sun.com 
33410346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
33510346Swyllys.ingersoll@sun.com 	(void) hcall_tpm_get(tpm->locality, offset, sizeof (uint32_t), &value);
33610346Swyllys.ingersoll@sun.com 	return ((uint32_t)value);
33710346Swyllys.ingersoll@sun.com }
33810346Swyllys.ingersoll@sun.com 
33910346Swyllys.ingersoll@sun.com static inline void
tpm_put8(tpm_state_t * tpm,unsigned long offset,uint8_t value)34010346Swyllys.ingersoll@sun.com tpm_put8(tpm_state_t *tpm, unsigned long offset, uint8_t value)
34110346Swyllys.ingersoll@sun.com {
34210346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
34310346Swyllys.ingersoll@sun.com 	(void) hcall_tpm_put(tpm->locality, offset, sizeof (uint8_t), value);
34410346Swyllys.ingersoll@sun.com }
34510346Swyllys.ingersoll@sun.com 
34610346Swyllys.ingersoll@sun.com #else
34710346Swyllys.ingersoll@sun.com 
34810346Swyllys.ingersoll@sun.com static inline uint8_t
tpm_get8(tpm_state_t * tpm,unsigned long offset)34910346Swyllys.ingersoll@sun.com tpm_get8(tpm_state_t *tpm, unsigned long offset)
35010346Swyllys.ingersoll@sun.com {
35110346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
35210346Swyllys.ingersoll@sun.com 
35310346Swyllys.ingersoll@sun.com 	return (ddi_get8(tpm->handle,
35410346Swyllys.ingersoll@sun.com 	    (uint8_t *)(TPM_LOCALITY_OFFSET(tpm->locality) |
35510346Swyllys.ingersoll@sun.com 	    (uintptr_t)tpm->addr + offset)));
35610346Swyllys.ingersoll@sun.com }
35710346Swyllys.ingersoll@sun.com 
35810346Swyllys.ingersoll@sun.com static inline uint32_t
tpm_get32(tpm_state_t * tpm,unsigned long offset)35910346Swyllys.ingersoll@sun.com tpm_get32(tpm_state_t *tpm, unsigned long offset)
36010346Swyllys.ingersoll@sun.com {
36110346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
36210346Swyllys.ingersoll@sun.com 	return (ddi_get32(tpm->handle,
36310346Swyllys.ingersoll@sun.com 	    (uint32_t *)(TPM_LOCALITY_OFFSET(tpm->locality) |
36410346Swyllys.ingersoll@sun.com 	    (uintptr_t)tpm->addr + offset)));
36510346Swyllys.ingersoll@sun.com }
36610346Swyllys.ingersoll@sun.com 
36710346Swyllys.ingersoll@sun.com static inline void
tpm_put8(tpm_state_t * tpm,unsigned long offset,uint8_t value)36810346Swyllys.ingersoll@sun.com tpm_put8(tpm_state_t *tpm, unsigned long offset, uint8_t value)
36910346Swyllys.ingersoll@sun.com {
37010346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
37110346Swyllys.ingersoll@sun.com 	ddi_put8(tpm->handle,
37210346Swyllys.ingersoll@sun.com 	    (uint8_t *)(TPM_LOCALITY_OFFSET(tpm->locality) |
37310346Swyllys.ingersoll@sun.com 	    (uintptr_t)tpm->addr + offset), value);
37410346Swyllys.ingersoll@sun.com }
37510346Swyllys.ingersoll@sun.com 
37610346Swyllys.ingersoll@sun.com #endif /* sun4v */
37710346Swyllys.ingersoll@sun.com 
37810346Swyllys.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
tpm_quiesce(dev_info_t * dip)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
load32(uchar_t * ptr,uint32_t offset)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
tpm_get_timeouts(tpm_state_t * tpm)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) {
424*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
425*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: itpm_command failed", myname);
426*10561Swyllys.ingersoll@sun.com #endif
4279126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
4289126SWyllys.Ingersoll@Sun.COM 	}
4299126SWyllys.Ingersoll@Sun.COM 
4309126SWyllys.Ingersoll@Sun.COM 	/*
4319126SWyllys.Ingersoll@Sun.COM 	 * Get the length of the returned buffer
4329126SWyllys.Ingersoll@Sun.COM 	 * Make sure that there are 4 timeout values returned
4339126SWyllys.Ingersoll@Sun.COM 	 * length of the capability response is stored in data[10-13]
4349126SWyllys.Ingersoll@Sun.COM 	 * Also the TPM is in network byte order
4359126SWyllys.Ingersoll@Sun.COM 	 */
4369126SWyllys.Ingersoll@Sun.COM 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
4379126SWyllys.Ingersoll@Sun.COM 	if (len != 4 * sizeof (uint32_t)) {
438*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
439*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: capability response size should be %d"
440*10561Swyllys.ingersoll@sun.com 		    "instead len = %d",
4419126SWyllys.Ingersoll@Sun.COM 		    myname, (int)(4 * sizeof (uint32_t)), (int)len);
442*10561Swyllys.ingersoll@sun.com #endif
4439126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
4449126SWyllys.Ingersoll@Sun.COM 	}
4459126SWyllys.Ingersoll@Sun.COM 
4469126SWyllys.Ingersoll@Sun.COM 	/* Get the four timeout's: a,b,c,d (they are 4 bytes long each) */
4479126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_A_OFFSET);
4489126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
4499126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_A;
4509126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
4519126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
4529126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
4539126SWyllys.Ingersoll@Sun.COM 	}
4549126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_a = drv_usectohz(timeout);
4559126SWyllys.Ingersoll@Sun.COM 
4569126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_B_OFFSET);
4579126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
4589126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_B;
4599126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
4609126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
4619126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
4629126SWyllys.Ingersoll@Sun.COM 	}
4639126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_b = drv_usectohz(timeout);
4649126SWyllys.Ingersoll@Sun.COM 
4659126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_C_OFFSET);
4669126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
4679126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_C;
4689126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
4699126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
4709126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
4719126SWyllys.Ingersoll@Sun.COM 	}
4729126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_c = drv_usectohz(timeout);
4739126SWyllys.Ingersoll@Sun.COM 
4749126SWyllys.Ingersoll@Sun.COM 	timeout = load32(buf, TPM_CAP_TIMEOUT_D_OFFSET);
4759126SWyllys.Ingersoll@Sun.COM 	if (timeout == 0) {
4769126SWyllys.Ingersoll@Sun.COM 		timeout = DEFAULT_TIMEOUT_D;
4779126SWyllys.Ingersoll@Sun.COM 	} else if (timeout < TEN_MILLISECONDS) {
4789126SWyllys.Ingersoll@Sun.COM 		/* timeout is in millisecond range (should be microseconds) */
4799126SWyllys.Ingersoll@Sun.COM 		timeout *= 1000;
4809126SWyllys.Ingersoll@Sun.COM 	}
4819126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_d = drv_usectohz(timeout);
4829126SWyllys.Ingersoll@Sun.COM 
4839126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
4849126SWyllys.Ingersoll@Sun.COM }
4859126SWyllys.Ingersoll@Sun.COM 
4869126SWyllys.Ingersoll@Sun.COM /*
4879126SWyllys.Ingersoll@Sun.COM  * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
4889126SWyllys.Ingersoll@Sun.COM  * with the subcommand TPM_CAP_PROP_TIS_DURATION
4899126SWyllys.Ingersoll@Sun.COM  * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
4909126SWyllys.Ingersoll@Sun.COM  */
4919126SWyllys.Ingersoll@Sun.COM static int
tpm_get_duration(tpm_state_t * tpm)4929126SWyllys.Ingersoll@Sun.COM tpm_get_duration(tpm_state_t *tpm) {
4939126SWyllys.Ingersoll@Sun.COM 	int ret;
4949126SWyllys.Ingersoll@Sun.COM 	uint32_t duration;
4959126SWyllys.Ingersoll@Sun.COM 	uint32_t len;
4969126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[30] = {
4979126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU_COMMAND */
4989126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 22,	/* paramsize in bytes */
4999126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 101,	/* TPM_ORD_GetCapability */
5009126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 5,	/* TPM_CAP_Prop */
5019126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 4,	/* SUB_CAP size in bytes */
5029126SWyllys.Ingersoll@Sun.COM 		0, 0, 1, 32	/* TPM_CAP_PROP_TIS_DURATION(0x120) */
5039126SWyllys.Ingersoll@Sun.COM 	};
5049126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_duration";
5059126SWyllys.Ingersoll@Sun.COM 
5069126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
5079126SWyllys.Ingersoll@Sun.COM 
5089126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
5099126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
510*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
511*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: itpm_command failed with ret code: 0x%x",
5129126SWyllys.Ingersoll@Sun.COM 			myname, ret);
513*10561Swyllys.ingersoll@sun.com #endif
5149126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
5159126SWyllys.Ingersoll@Sun.COM 	}
5169126SWyllys.Ingersoll@Sun.COM 
5179126SWyllys.Ingersoll@Sun.COM 	/*
5189126SWyllys.Ingersoll@Sun.COM 	 * Get the length of the returned buffer
5199126SWyllys.Ingersoll@Sun.COM 	 * Make sure that there are 3 duration values (S,M,L: in that order)
5209126SWyllys.Ingersoll@Sun.COM 	 * length of the capability response is stored in data[10-13]
5219126SWyllys.Ingersoll@Sun.COM 	 * Also the TPM is in network byte order
5229126SWyllys.Ingersoll@Sun.COM 	 */
5239126SWyllys.Ingersoll@Sun.COM 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
5249126SWyllys.Ingersoll@Sun.COM 	if (len != 3 * sizeof (uint32_t)) {
525*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
526*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: capability response should be %d, "
5279126SWyllys.Ingersoll@Sun.COM 		    "instead, it's %d",
5289126SWyllys.Ingersoll@Sun.COM 		    myname, (int)(3 * sizeof (uint32_t)), (int)len);
529*10561Swyllys.ingersoll@sun.com #endif
5309126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
5319126SWyllys.Ingersoll@Sun.COM 	}
5329126SWyllys.Ingersoll@Sun.COM 
5339126SWyllys.Ingersoll@Sun.COM 	duration = load32(buf, TPM_CAP_DUR_SHORT_OFFSET);
5349126SWyllys.Ingersoll@Sun.COM 	if (duration == 0) {
5359126SWyllys.Ingersoll@Sun.COM 		duration = DEFAULT_SHORT_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_SHORT] = drv_usectohz(duration);
5409126SWyllys.Ingersoll@Sun.COM 
5419126SWyllys.Ingersoll@Sun.COM 	duration = load32(buf, TPM_CAP_DUR_MEDIUM_OFFSET);
5429126SWyllys.Ingersoll@Sun.COM 	if (duration == 0) {
5439126SWyllys.Ingersoll@Sun.COM 		duration = DEFAULT_MEDIUM_DURATION;
5449126SWyllys.Ingersoll@Sun.COM 	} else if (duration < TEN_MILLISECONDS) {
5459126SWyllys.Ingersoll@Sun.COM 		duration *= 1000;
5469126SWyllys.Ingersoll@Sun.COM 	}
5479126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_MEDIUM] = drv_usectohz(duration);
5489126SWyllys.Ingersoll@Sun.COM 
5499126SWyllys.Ingersoll@Sun.COM 	duration = load32(buf, TPM_CAP_DUR_LONG_OFFSET);
5509126SWyllys.Ingersoll@Sun.COM 	if (duration == 0) {
5519126SWyllys.Ingersoll@Sun.COM 		duration = DEFAULT_LONG_DURATION;
5529126SWyllys.Ingersoll@Sun.COM 	} else if (duration < FOUR_HUNDRED_MILLISECONDS) {
5539126SWyllys.Ingersoll@Sun.COM 		duration *= 1000;
5549126SWyllys.Ingersoll@Sun.COM 	}
5559126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_LONG] = drv_usectohz(duration);
5569126SWyllys.Ingersoll@Sun.COM 
5579126SWyllys.Ingersoll@Sun.COM 	/* Just make the undefined duration be the same as the LONG */
5589126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_UNDEFINED] = tpm->duration[TPM_LONG];
5599126SWyllys.Ingersoll@Sun.COM 
5609126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
5619126SWyllys.Ingersoll@Sun.COM }
5629126SWyllys.Ingersoll@Sun.COM 
5639126SWyllys.Ingersoll@Sun.COM /*
5649126SWyllys.Ingersoll@Sun.COM  * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
5659126SWyllys.Ingersoll@Sun.COM  * with the subcommand TPM_CAP_PROP_TIS_DURATION
5669126SWyllys.Ingersoll@Sun.COM  * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
5679126SWyllys.Ingersoll@Sun.COM  */
5689126SWyllys.Ingersoll@Sun.COM static int
tpm_get_version(tpm_state_t * tpm)5699126SWyllys.Ingersoll@Sun.COM tpm_get_version(tpm_state_t *tpm) {
5709126SWyllys.Ingersoll@Sun.COM 	int ret;
5719126SWyllys.Ingersoll@Sun.COM 	uint32_t len;
5729126SWyllys.Ingersoll@Sun.COM 	char vendorId[5];
5739126SWyllys.Ingersoll@Sun.COM 	/* If this buf is too small, the "vendor specific" data won't fit */
5749126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[64] = {
5759126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU_COMMAND */
5769126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 18,	/* paramsize in bytes */
5779126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 101,	/* TPM_ORD_GetCapability */
5789126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 0x1A,	/* TPM_CAP_VERSION_VAL */
5799126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 0,	/* SUB_CAP size in bytes */
5809126SWyllys.Ingersoll@Sun.COM 	};
5819126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_version";
5829126SWyllys.Ingersoll@Sun.COM 
5839126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
5849126SWyllys.Ingersoll@Sun.COM 
5859126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
5869126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
587*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
588*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: itpm_command failed with ret code: 0x%x",
5899126SWyllys.Ingersoll@Sun.COM 			myname, ret);
590*10561Swyllys.ingersoll@sun.com #endif
5919126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
5929126SWyllys.Ingersoll@Sun.COM 	}
5939126SWyllys.Ingersoll@Sun.COM 
5949126SWyllys.Ingersoll@Sun.COM 	/*
5959126SWyllys.Ingersoll@Sun.COM 	 * Get the length of the returned buffer.
5969126SWyllys.Ingersoll@Sun.COM 	 */
5979126SWyllys.Ingersoll@Sun.COM 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
5989126SWyllys.Ingersoll@Sun.COM 	if (len < TPM_CAP_VERSION_INFO_SIZE) {
599*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
600*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: capability response should be greater"
6019126SWyllys.Ingersoll@Sun.COM 		    " than %d, instead, it's %d",
602*10561Swyllys.ingersoll@sun.com 		    myname, TPM_CAP_VERSION_INFO_SIZE, len);
603*10561Swyllys.ingersoll@sun.com #endif
6049126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
6059126SWyllys.Ingersoll@Sun.COM 	}
6069126SWyllys.Ingersoll@Sun.COM 
6079126SWyllys.Ingersoll@Sun.COM 	bcopy(buf + TPM_CAP_VERSION_INFO_OFFSET, &tpm->vers_info,
6089126SWyllys.Ingersoll@Sun.COM 	    TPM_CAP_VERSION_INFO_SIZE);
6099126SWyllys.Ingersoll@Sun.COM 
6109126SWyllys.Ingersoll@Sun.COM 	bcopy(tpm->vers_info.tpmVendorID, vendorId,
6119126SWyllys.Ingersoll@Sun.COM 	    sizeof (tpm->vers_info.tpmVendorID));
6129126SWyllys.Ingersoll@Sun.COM 	vendorId[4] = '\0';
6139126SWyllys.Ingersoll@Sun.COM 
6149126SWyllys.Ingersoll@Sun.COM 	cmn_err(CE_NOTE, "!TPM found: Ver %d.%d, Rev %d.%d, "
6159126SWyllys.Ingersoll@Sun.COM 	    "SpecLevel %d, errataRev %d, VendorId '%s'",
6169126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.major,	/* Version */
6179126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.minor,
6189126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.revMajor,	/* Revision */
6199126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.revMinor,
6209126SWyllys.Ingersoll@Sun.COM 	    (int)ntohs(tpm->vers_info.specLevel),
6219126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.errataRev,
6229126SWyllys.Ingersoll@Sun.COM 	    vendorId);
6239126SWyllys.Ingersoll@Sun.COM 
6249126SWyllys.Ingersoll@Sun.COM 	/*
6259126SWyllys.Ingersoll@Sun.COM 	 * This driver only supports TPM Version 1.2
6269126SWyllys.Ingersoll@Sun.COM 	 */
6279126SWyllys.Ingersoll@Sun.COM 	if (tpm->vers_info.version.major != 1 &&
6289126SWyllys.Ingersoll@Sun.COM 	    tpm->vers_info.version.minor != 2) {
629*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: Unsupported TPM version (%d.%d)",
6309126SWyllys.Ingersoll@Sun.COM 		    myname,
6319126SWyllys.Ingersoll@Sun.COM 		    tpm->vers_info.version.major,		/* Version */
6329126SWyllys.Ingersoll@Sun.COM 		    tpm->vers_info.version.minor);
6339126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
6349126SWyllys.Ingersoll@Sun.COM 	}
6359126SWyllys.Ingersoll@Sun.COM 
6369126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
6379126SWyllys.Ingersoll@Sun.COM }
6389126SWyllys.Ingersoll@Sun.COM 
6399126SWyllys.Ingersoll@Sun.COM /*
6409126SWyllys.Ingersoll@Sun.COM  * To prevent the TPM from complaining that certain functions are not tested
6419126SWyllys.Ingersoll@Sun.COM  * we run this command when the driver attaches.
6429126SWyllys.Ingersoll@Sun.COM  * For details see Section 4.2 of TPM Main Part 3 Command Specification
6439126SWyllys.Ingersoll@Sun.COM  */
6449126SWyllys.Ingersoll@Sun.COM static int
tpm_continue_selftest(tpm_state_t * tpm)6459126SWyllys.Ingersoll@Sun.COM tpm_continue_selftest(tpm_state_t *tpm) {
6469126SWyllys.Ingersoll@Sun.COM 	int ret;
6479126SWyllys.Ingersoll@Sun.COM 	uint8_t buf[10] = {
6489126SWyllys.Ingersoll@Sun.COM 		0, 193,		/* TPM_TAG_RQU COMMAND */
6499126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 10,	/* paramsize in bytes */
6509126SWyllys.Ingersoll@Sun.COM 		0, 0, 0, 83	/* TPM_ORD_ContinueSelfTest */
6519126SWyllys.Ingersoll@Sun.COM 	};
6529126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_continue_selftest";
6539126SWyllys.Ingersoll@Sun.COM 
6549126SWyllys.Ingersoll@Sun.COM 	/* Need a longer timeout */
6559126SWyllys.Ingersoll@Sun.COM 	ret = itpm_command(tpm, buf, sizeof (buf));
6569126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
657*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
658*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: itpm_command failed", myname);
659*10561Swyllys.ingersoll@sun.com #endif
6609126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
6619126SWyllys.Ingersoll@Sun.COM 	}
6629126SWyllys.Ingersoll@Sun.COM 
6639126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
6649126SWyllys.Ingersoll@Sun.COM }
6659126SWyllys.Ingersoll@Sun.COM /*
6669126SWyllys.Ingersoll@Sun.COM  * Auxilary Functions
6679126SWyllys.Ingersoll@Sun.COM  */
6689126SWyllys.Ingersoll@Sun.COM 
6699126SWyllys.Ingersoll@Sun.COM /*
6709126SWyllys.Ingersoll@Sun.COM  * Find out how long we should wait for the TPM command to complete a command
6719126SWyllys.Ingersoll@Sun.COM  */
6729126SWyllys.Ingersoll@Sun.COM static clock_t
tpm_get_ordinal_duration(tpm_state_t * tpm,uint8_t ordinal)6739126SWyllys.Ingersoll@Sun.COM tpm_get_ordinal_duration(tpm_state_t *tpm, uint8_t ordinal)
6749126SWyllys.Ingersoll@Sun.COM {
6759126SWyllys.Ingersoll@Sun.COM 	uint8_t index;
6769126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_get_ordinal_duration";
6779126SWyllys.Ingersoll@Sun.COM 
6789126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
6799126SWyllys.Ingersoll@Sun.COM 
6809126SWyllys.Ingersoll@Sun.COM 	/* Default and failure case for IFX */
6819126SWyllys.Ingersoll@Sun.COM 	/* Is it a TSC_ORDINAL? */
6829126SWyllys.Ingersoll@Sun.COM 	if (ordinal & TSC_ORDINAL_MASK) {
6839126SWyllys.Ingersoll@Sun.COM 		if (ordinal > TSC_ORDINAL_MAX) {
684*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
6859126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN,
686*10561Swyllys.ingersoll@sun.com 			    "!%s: tsc ordinal: %d exceeds MAX: %d",
6879126SWyllys.Ingersoll@Sun.COM 			    myname, ordinal, TSC_ORDINAL_MAX);
688*10561Swyllys.ingersoll@sun.com #endif
6899126SWyllys.Ingersoll@Sun.COM 			return (0);
6909126SWyllys.Ingersoll@Sun.COM 		}
6919126SWyllys.Ingersoll@Sun.COM 		index = tsc_ords_duration[ordinal];
6929126SWyllys.Ingersoll@Sun.COM 	} else {
6939126SWyllys.Ingersoll@Sun.COM 		if (ordinal > TPM_ORDINAL_MAX) {
694*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
6959126SWyllys.Ingersoll@Sun.COM 			cmn_err(CE_WARN,
696*10561Swyllys.ingersoll@sun.com 			    "!%s: ordinal %d exceeds MAX: %d",
6979126SWyllys.Ingersoll@Sun.COM 			    myname, ordinal, TPM_ORDINAL_MAX);
698*10561Swyllys.ingersoll@sun.com #endif
6999126SWyllys.Ingersoll@Sun.COM 			return (0);
7009126SWyllys.Ingersoll@Sun.COM 		}
7019126SWyllys.Ingersoll@Sun.COM 		index = tpm_ords_duration[ordinal];
7029126SWyllys.Ingersoll@Sun.COM 	}
7039126SWyllys.Ingersoll@Sun.COM 
7049126SWyllys.Ingersoll@Sun.COM 	if (index > TPM_DURATION_MAX_IDX) {
705*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
706*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: duration index '%d' is out of bounds",
7079126SWyllys.Ingersoll@Sun.COM 		    myname, index);
708*10561Swyllys.ingersoll@sun.com #endif
7099126SWyllys.Ingersoll@Sun.COM 		return (0);
7109126SWyllys.Ingersoll@Sun.COM 	}
7119126SWyllys.Ingersoll@Sun.COM 	return (tpm->duration[index]);
7129126SWyllys.Ingersoll@Sun.COM }
7139126SWyllys.Ingersoll@Sun.COM 
7149126SWyllys.Ingersoll@Sun.COM /*
7159126SWyllys.Ingersoll@Sun.COM  * Internal TPM Transmit Function:
7169126SWyllys.Ingersoll@Sun.COM  * Calls implementation specific sendto and receive
7179126SWyllys.Ingersoll@Sun.COM  * The code assumes that the buffer is in network byte order
7189126SWyllys.Ingersoll@Sun.COM  */
7199126SWyllys.Ingersoll@Sun.COM static int
itpm_command(tpm_state_t * tpm,uint8_t * buf,size_t bufsiz)7209126SWyllys.Ingersoll@Sun.COM itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz)
7219126SWyllys.Ingersoll@Sun.COM {
7229126SWyllys.Ingersoll@Sun.COM 	int ret;
7239126SWyllys.Ingersoll@Sun.COM 	uint32_t count;
7249126SWyllys.Ingersoll@Sun.COM 	char *myname = "itpm_command";
7259126SWyllys.Ingersoll@Sun.COM 
7269126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
7279126SWyllys.Ingersoll@Sun.COM 
7289126SWyllys.Ingersoll@Sun.COM 	/* The byte order is network byte order so convert it */
7299126SWyllys.Ingersoll@Sun.COM 	count = load32(buf, TPM_PARAMSIZE_OFFSET);
7309126SWyllys.Ingersoll@Sun.COM 
731*10561Swyllys.ingersoll@sun.com 	if (count == 0 || (count > bufsiz)) {
732*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
733*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: invalid byte count value "
734*10561Swyllys.ingersoll@sun.com 		    "(%d > bufsiz %d)", myname, (int)count, (int)bufsiz);
735*10561Swyllys.ingersoll@sun.com #endif
7369126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
7379126SWyllys.Ingersoll@Sun.COM 	}
7389126SWyllys.Ingersoll@Sun.COM 
7399126SWyllys.Ingersoll@Sun.COM 	/* Send the command */
7409126SWyllys.Ingersoll@Sun.COM 	ret = tis_send_data(tpm, buf, count);
7419126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
742*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
743*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: tis_send_data failed with error %x",
7449126SWyllys.Ingersoll@Sun.COM 		    myname, ret);
745*10561Swyllys.ingersoll@sun.com #endif
7469126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
7479126SWyllys.Ingersoll@Sun.COM 	}
7489126SWyllys.Ingersoll@Sun.COM 
7499126SWyllys.Ingersoll@Sun.COM 	/*
7509126SWyllys.Ingersoll@Sun.COM 	 * Now receive the data from the tpm
7519126SWyllys.Ingersoll@Sun.COM 	 * Should at least receive "the common" 10 bytes (TPM_HEADER_SIZE)
7529126SWyllys.Ingersoll@Sun.COM 	 */
7539126SWyllys.Ingersoll@Sun.COM 	ret = tis_recv_data(tpm, buf, bufsiz);
7549126SWyllys.Ingersoll@Sun.COM 	if (ret < TPM_HEADER_SIZE) {
755*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
756*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: tis_recv_data failed", myname);
757*10561Swyllys.ingersoll@sun.com #endif
7589126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
7599126SWyllys.Ingersoll@Sun.COM 	}
7609126SWyllys.Ingersoll@Sun.COM 
7619126SWyllys.Ingersoll@Sun.COM 	/* Check the return code */
7629126SWyllys.Ingersoll@Sun.COM 	ret = load32(buf, TPM_RETURN_OFFSET);
7639126SWyllys.Ingersoll@Sun.COM 	if (ret != TPM_SUCCESS) {
76410346Swyllys.ingersoll@sun.com 		if (ret == TPM_E_DEACTIVATED)
765*10561Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "!%s: TPM is deactivated", myname);
76610346Swyllys.ingersoll@sun.com 		else if (ret == TPM_E_DISABLED)
767*10561Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "!%s: TPM is disabled", myname);
76810346Swyllys.ingersoll@sun.com 		else
769*10561Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "!%s: TPM error code 0x%0x",
77010346Swyllys.ingersoll@sun.com 			    myname, ret);
7719126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
7729126SWyllys.Ingersoll@Sun.COM 	}
7739126SWyllys.Ingersoll@Sun.COM 
7749126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
7759126SWyllys.Ingersoll@Sun.COM }
7769126SWyllys.Ingersoll@Sun.COM 
7779126SWyllys.Ingersoll@Sun.COM /*
7789126SWyllys.Ingersoll@Sun.COM  * Whenever the driver wants to write to the DATA_IO register, it must need
7799126SWyllys.Ingersoll@Sun.COM  * to figure out the burstcount.  This is the amount of bytes it can write
7809126SWyllys.Ingersoll@Sun.COM  * before having to wait for long LPC bus cycle
7819126SWyllys.Ingersoll@Sun.COM  *
7829126SWyllys.Ingersoll@Sun.COM  * Returns: 0 if error, burst count if sucess
7839126SWyllys.Ingersoll@Sun.COM  */
7849126SWyllys.Ingersoll@Sun.COM static uint16_t
tpm_get_burstcount(tpm_state_t * tpm)7859126SWyllys.Ingersoll@Sun.COM tpm_get_burstcount(tpm_state_t *tpm) {
7869126SWyllys.Ingersoll@Sun.COM 	clock_t stop;
7879126SWyllys.Ingersoll@Sun.COM 	uint16_t burstcnt;
7889126SWyllys.Ingersoll@Sun.COM 
7899126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL);
7909126SWyllys.Ingersoll@Sun.COM 
7919126SWyllys.Ingersoll@Sun.COM 	/*
7929126SWyllys.Ingersoll@Sun.COM 	 * Spec says timeout should be TIMEOUT_D
7939126SWyllys.Ingersoll@Sun.COM 	 * burst count is TPM_STS bits 8..23
7949126SWyllys.Ingersoll@Sun.COM 	 */
7959126SWyllys.Ingersoll@Sun.COM 	stop = ddi_get_lbolt() + tpm->timeout_d;
7969126SWyllys.Ingersoll@Sun.COM 	do {
7979126SWyllys.Ingersoll@Sun.COM 		/*
7989126SWyllys.Ingersoll@Sun.COM 		 * burstcnt is stored as a little endian value
7999126SWyllys.Ingersoll@Sun.COM 		 * 'ntohs' doesn't work since the value is not word-aligned
8009126SWyllys.Ingersoll@Sun.COM 		 */
80110346Swyllys.ingersoll@sun.com 		burstcnt = tpm_get8(tpm, TPM_STS + 1);
80210346Swyllys.ingersoll@sun.com 		burstcnt += tpm_get8(tpm, TPM_STS + 2) << 8;
8039126SWyllys.Ingersoll@Sun.COM 
8049126SWyllys.Ingersoll@Sun.COM 		if (burstcnt)
8059126SWyllys.Ingersoll@Sun.COM 			return (burstcnt);
8069126SWyllys.Ingersoll@Sun.COM 
8079126SWyllys.Ingersoll@Sun.COM 		delay(tpm->timeout_poll);
8089126SWyllys.Ingersoll@Sun.COM 	} while (ddi_get_lbolt() < stop);
8099126SWyllys.Ingersoll@Sun.COM 
8109126SWyllys.Ingersoll@Sun.COM 	return (0);
8119126SWyllys.Ingersoll@Sun.COM }
8129126SWyllys.Ingersoll@Sun.COM 
8139126SWyllys.Ingersoll@Sun.COM /*
8149126SWyllys.Ingersoll@Sun.COM  * Writing 1 to TPM_STS_CMD_READY bit in TPM_STS will do the following:
8159126SWyllys.Ingersoll@Sun.COM  * 1. The TPM will clears IO buffers if any
8169126SWyllys.Ingersoll@Sun.COM  * 2. The TPM will enters either Idle or Ready state within TIMEOUT_B
8179126SWyllys.Ingersoll@Sun.COM  * (checked in the calling function)
8189126SWyllys.Ingersoll@Sun.COM  */
8199126SWyllys.Ingersoll@Sun.COM static void
tpm_set_ready(tpm_state_t * tpm)8209126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm_state_t *tpm) {
82110346Swyllys.ingersoll@sun.com 	tpm_put8(tpm, TPM_STS, TPM_STS_CMD_READY);
8229126SWyllys.Ingersoll@Sun.COM }
8239126SWyllys.Ingersoll@Sun.COM 
8249126SWyllys.Ingersoll@Sun.COM static int
receive_data(tpm_state_t * tpm,uint8_t * buf,size_t bufsiz)8259126SWyllys.Ingersoll@Sun.COM receive_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
8269126SWyllys.Ingersoll@Sun.COM 	int size = 0;
8279126SWyllys.Ingersoll@Sun.COM 	int retried = 0;
8289126SWyllys.Ingersoll@Sun.COM 	uint8_t stsbits;
8299126SWyllys.Ingersoll@Sun.COM 
8309126SWyllys.Ingersoll@Sun.COM 	/* A number of consecutive bytes that can be written to TPM */
8319126SWyllys.Ingersoll@Sun.COM 	uint16_t burstcnt;
8329126SWyllys.Ingersoll@Sun.COM 
8339126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
8349126SWyllys.Ingersoll@Sun.COM retry:
8359126SWyllys.Ingersoll@Sun.COM 	while (size < bufsiz &&
8369126SWyllys.Ingersoll@Sun.COM 		(tpm_wait_for_stat(tpm,
8379126SWyllys.Ingersoll@Sun.COM 		    (TPM_STS_DATA_AVAIL|TPM_STS_VALID),
83810346Swyllys.ingersoll@sun.com 		    tpm->timeout_c) == DDI_SUCCESS)) {
8399126SWyllys.Ingersoll@Sun.COM 		/*
8409126SWyllys.Ingersoll@Sun.COM 		 * Burstcount should be available within TIMEOUT_D
8419126SWyllys.Ingersoll@Sun.COM 		 * after STS is set to valid
8429126SWyllys.Ingersoll@Sun.COM 		 * burstcount is dynamic, so have to get it each time
8439126SWyllys.Ingersoll@Sun.COM 		 */
8449126SWyllys.Ingersoll@Sun.COM 		burstcnt = tpm_get_burstcount(tpm);
8459126SWyllys.Ingersoll@Sun.COM 		for (; burstcnt > 0 && size < bufsiz; burstcnt--) {
84610346Swyllys.ingersoll@sun.com 			buf[size++] = tpm_get8(tpm, TPM_DATA_FIFO);
8479126SWyllys.Ingersoll@Sun.COM 		}
8489126SWyllys.Ingersoll@Sun.COM 	}
8499126SWyllys.Ingersoll@Sun.COM 	stsbits = tis_get_status(tpm);
8509126SWyllys.Ingersoll@Sun.COM 	/* check to see if we need to retry (just once) */
8519126SWyllys.Ingersoll@Sun.COM 	if (size < bufsiz && !(stsbits & TPM_STS_DATA_AVAIL) && retried == 0) {
8529126SWyllys.Ingersoll@Sun.COM 		/* issue responseRetry (TIS 1.2 pg 54) */
85310346Swyllys.ingersoll@sun.com 		tpm_put8(tpm, TPM_STS, TPM_STS_RESPONSE_RETRY);
8549126SWyllys.Ingersoll@Sun.COM 		/* update the retry counter so we only retry once */
8559126SWyllys.Ingersoll@Sun.COM 		retried++;
8569126SWyllys.Ingersoll@Sun.COM 		/* reset the size to 0 and reread the entire response */
8579126SWyllys.Ingersoll@Sun.COM 		size = 0;
8589126SWyllys.Ingersoll@Sun.COM 		goto retry;
8599126SWyllys.Ingersoll@Sun.COM 	}
8609126SWyllys.Ingersoll@Sun.COM 	return (size);
8619126SWyllys.Ingersoll@Sun.COM }
8629126SWyllys.Ingersoll@Sun.COM 
8639126SWyllys.Ingersoll@Sun.COM /* Receive the data from the TPM */
8649126SWyllys.Ingersoll@Sun.COM static int
tis_recv_data(tpm_state_t * tpm,uint8_t * buf,size_t bufsiz)8659126SWyllys.Ingersoll@Sun.COM tis_recv_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
8669126SWyllys.Ingersoll@Sun.COM 	int ret;
8679126SWyllys.Ingersoll@Sun.COM 	int size = 0;
8689126SWyllys.Ingersoll@Sun.COM 	uint32_t expected, status;
8699126SWyllys.Ingersoll@Sun.COM 	uint32_t cmdresult;
8709126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_recv_data";
8719126SWyllys.Ingersoll@Sun.COM 
8729126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
8739126SWyllys.Ingersoll@Sun.COM 
8749126SWyllys.Ingersoll@Sun.COM 	if (bufsiz < TPM_HEADER_SIZE) {
875*10561Swyllys.ingersoll@sun.com 		/* There should be at least tag, paramsize, return code */
876*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
877*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: received data should contain at least "
8789126SWyllys.Ingersoll@Sun.COM 		    "the header which is %d bytes long",
8799126SWyllys.Ingersoll@Sun.COM 		    myname, TPM_HEADER_SIZE);
880*10561Swyllys.ingersoll@sun.com #endif
8819126SWyllys.Ingersoll@Sun.COM 		goto OUT;
8829126SWyllys.Ingersoll@Sun.COM 	}
8839126SWyllys.Ingersoll@Sun.COM 
8849126SWyllys.Ingersoll@Sun.COM 	/* Read tag(2 bytes), paramsize(4), and result(4) */
8859126SWyllys.Ingersoll@Sun.COM 	size = receive_data(tpm, buf, TPM_HEADER_SIZE);
8869126SWyllys.Ingersoll@Sun.COM 	if (size < TPM_HEADER_SIZE) {
887*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
888*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: recv TPM_HEADER failed, size = %d",
8899126SWyllys.Ingersoll@Sun.COM 		    myname, size);
890*10561Swyllys.ingersoll@sun.com #endif
8919126SWyllys.Ingersoll@Sun.COM 		goto OUT;
8929126SWyllys.Ingersoll@Sun.COM 	}
8939126SWyllys.Ingersoll@Sun.COM 
8949126SWyllys.Ingersoll@Sun.COM 	cmdresult = load32(buf, TPM_RETURN_OFFSET);
8959126SWyllys.Ingersoll@Sun.COM 
8969126SWyllys.Ingersoll@Sun.COM 	/* Get 'paramsize'(4 bytes)--it includes tag and paramsize */
8979126SWyllys.Ingersoll@Sun.COM 	expected = load32(buf, TPM_PARAMSIZE_OFFSET);
8989126SWyllys.Ingersoll@Sun.COM 	if (expected > bufsiz) {
899*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
900*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: paramSize is bigger "
9019126SWyllys.Ingersoll@Sun.COM 		    "than the requested size: paramSize=%d bufsiz=%d result=%d",
9029126SWyllys.Ingersoll@Sun.COM 		    myname, (int)expected, (int)bufsiz, cmdresult);
903*10561Swyllys.ingersoll@sun.com #endif
9049126SWyllys.Ingersoll@Sun.COM 		goto OUT;
9059126SWyllys.Ingersoll@Sun.COM 	}
9069126SWyllys.Ingersoll@Sun.COM 
9079126SWyllys.Ingersoll@Sun.COM 	/* Read in the rest of the data from the TPM */
9089126SWyllys.Ingersoll@Sun.COM 	size += receive_data(tpm, (uint8_t *)&buf[TPM_HEADER_SIZE],
9099126SWyllys.Ingersoll@Sun.COM 	    expected - TPM_HEADER_SIZE);
9109126SWyllys.Ingersoll@Sun.COM 	if (size < expected) {
911*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
912*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: received data length (%d) "
913*10561Swyllys.ingersoll@sun.com 		    "is less than expected (%d)", myname, size, expected);
914*10561Swyllys.ingersoll@sun.com #endif
9159126SWyllys.Ingersoll@Sun.COM 		goto OUT;
9169126SWyllys.Ingersoll@Sun.COM 	}
9179126SWyllys.Ingersoll@Sun.COM 
9189126SWyllys.Ingersoll@Sun.COM 	/* The TPM MUST set the state to stsValid within TIMEOUT_C */
91910346Swyllys.ingersoll@sun.com 	ret = tpm_wait_for_stat(tpm, TPM_STS_VALID, tpm->timeout_c);
9209126SWyllys.Ingersoll@Sun.COM 
9219126SWyllys.Ingersoll@Sun.COM 	status = tis_get_status(tpm);
9229126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
923*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
924*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: TPM didn't set stsValid after its I/O: "
9259126SWyllys.Ingersoll@Sun.COM 		    "status = 0x%08X", myname, status);
926*10561Swyllys.ingersoll@sun.com #endif
9279126SWyllys.Ingersoll@Sun.COM 		goto OUT;
9289126SWyllys.Ingersoll@Sun.COM 	}
9299126SWyllys.Ingersoll@Sun.COM 
9309126SWyllys.Ingersoll@Sun.COM 	/* There is still more data? */
9319126SWyllys.Ingersoll@Sun.COM 	if (status & TPM_STS_DATA_AVAIL) {
932*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
933*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: TPM_STS_DATA_AVAIL is set:0x%08X",
9349126SWyllys.Ingersoll@Sun.COM 		    myname, status);
935*10561Swyllys.ingersoll@sun.com #endif
9369126SWyllys.Ingersoll@Sun.COM 		goto OUT;
9379126SWyllys.Ingersoll@Sun.COM 	}
9389126SWyllys.Ingersoll@Sun.COM 
9399126SWyllys.Ingersoll@Sun.COM 	/*
9409126SWyllys.Ingersoll@Sun.COM 	 * Release the control of the TPM after we are done with it
9419126SWyllys.Ingersoll@Sun.COM 	 * it...so others can also get a chance to send data
9429126SWyllys.Ingersoll@Sun.COM 	 */
9439126SWyllys.Ingersoll@Sun.COM 	tis_release_locality(tpm, tpm->locality, 0);
9449126SWyllys.Ingersoll@Sun.COM 
9459126SWyllys.Ingersoll@Sun.COM OUT:
9469126SWyllys.Ingersoll@Sun.COM 	tpm_set_ready(tpm);
9479126SWyllys.Ingersoll@Sun.COM 	tis_release_locality(tpm, tpm->locality, 0);
9489126SWyllys.Ingersoll@Sun.COM 	return (size);
9499126SWyllys.Ingersoll@Sun.COM }
9509126SWyllys.Ingersoll@Sun.COM 
9519126SWyllys.Ingersoll@Sun.COM /*
9529126SWyllys.Ingersoll@Sun.COM  * Send the data (TPM commands) to the Data IO register
9539126SWyllys.Ingersoll@Sun.COM  */
9549126SWyllys.Ingersoll@Sun.COM static int
tis_send_data(tpm_state_t * tpm,uint8_t * buf,size_t bufsiz)9559126SWyllys.Ingersoll@Sun.COM tis_send_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
9569126SWyllys.Ingersoll@Sun.COM 	int ret;
9579126SWyllys.Ingersoll@Sun.COM 	uint8_t status;
9589126SWyllys.Ingersoll@Sun.COM 	uint16_t burstcnt;
9599126SWyllys.Ingersoll@Sun.COM 	uint32_t ordinal;
9609126SWyllys.Ingersoll@Sun.COM 	size_t count = 0;
9619126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_send_data";
9629126SWyllys.Ingersoll@Sun.COM 
9639126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && buf != NULL);
9649126SWyllys.Ingersoll@Sun.COM 
9659126SWyllys.Ingersoll@Sun.COM 	if (bufsiz == 0) {
966*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
967*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: bufsiz arg is zero", myname);
968*10561Swyllys.ingersoll@sun.com #endif
9699126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
9709126SWyllys.Ingersoll@Sun.COM 	}
9719126SWyllys.Ingersoll@Sun.COM 
9729126SWyllys.Ingersoll@Sun.COM 	/* Put the TPM in ready state */
9739126SWyllys.Ingersoll@Sun.COM 	status = tis_get_status(tpm);
9749126SWyllys.Ingersoll@Sun.COM 
9759126SWyllys.Ingersoll@Sun.COM 	if (!(status & TPM_STS_CMD_READY)) {
9769126SWyllys.Ingersoll@Sun.COM 		tpm_set_ready(tpm);
97710346Swyllys.ingersoll@sun.com 		ret = tpm_wait_for_stat(tpm, TPM_STS_CMD_READY, tpm->timeout_b);
9789126SWyllys.Ingersoll@Sun.COM 		if (ret != DDI_SUCCESS) {
979*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
980*10561Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "!%s: could not put the TPM "
9819126SWyllys.Ingersoll@Sun.COM 			    "in the command ready state:"
9829126SWyllys.Ingersoll@Sun.COM 			    "tpm_wait_for_stat returned error",
9839126SWyllys.Ingersoll@Sun.COM 			    myname);
984*10561Swyllys.ingersoll@sun.com #endif
9859126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
9869126SWyllys.Ingersoll@Sun.COM 		}
9879126SWyllys.Ingersoll@Sun.COM 	}
9889126SWyllys.Ingersoll@Sun.COM 
9899126SWyllys.Ingersoll@Sun.COM 	/*
9909126SWyllys.Ingersoll@Sun.COM 	 * Now we are ready to send command
9919126SWyllys.Ingersoll@Sun.COM 	 * TPM's burstcount dictates how many bytes we can write at a time
9929126SWyllys.Ingersoll@Sun.COM 	 * Burstcount is dynamic if INTF_CAPABILITY for static burstcount is
9939126SWyllys.Ingersoll@Sun.COM 	 * not set.
9949126SWyllys.Ingersoll@Sun.COM 	 */
9959126SWyllys.Ingersoll@Sun.COM 	while (count < bufsiz - 1) {
9969126SWyllys.Ingersoll@Sun.COM 		burstcnt = tpm_get_burstcount(tpm);
9979126SWyllys.Ingersoll@Sun.COM 		if (burstcnt == 0) {
998*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
999*10561Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "!%s: tpm_get_burstcnt returned error",
10009126SWyllys.Ingersoll@Sun.COM 			    myname);
1001*10561Swyllys.ingersoll@sun.com #endif
10029126SWyllys.Ingersoll@Sun.COM 			ret = DDI_FAILURE;
10039126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
10049126SWyllys.Ingersoll@Sun.COM 		}
10059126SWyllys.Ingersoll@Sun.COM 
10069126SWyllys.Ingersoll@Sun.COM 		for (; burstcnt > 0 && count < bufsiz - 1; burstcnt--) {
100710346Swyllys.ingersoll@sun.com 			tpm_put8(tpm, TPM_DATA_FIFO, buf[count]);
10089126SWyllys.Ingersoll@Sun.COM 			count++;
10099126SWyllys.Ingersoll@Sun.COM 		}
10109126SWyllys.Ingersoll@Sun.COM 		/* Wait for TPM to indicate that it is ready for more data */
10119126SWyllys.Ingersoll@Sun.COM 		ret = tpm_wait_for_stat(tpm,
101210346Swyllys.ingersoll@sun.com 		    (TPM_STS_VALID | TPM_STS_DATA_EXPECT), tpm->timeout_c);
10139126SWyllys.Ingersoll@Sun.COM 		if (ret != DDI_SUCCESS) {
1014*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1015*10561Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "!%s: TPM didn't enter STS_VALID "
1016*10561Swyllys.ingersoll@sun.com 			    "state", myname);
1017*10561Swyllys.ingersoll@sun.com #endif
10189126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
10199126SWyllys.Ingersoll@Sun.COM 		}
10209126SWyllys.Ingersoll@Sun.COM 	}
10219126SWyllys.Ingersoll@Sun.COM 	/* We can't exit the loop above unless we wrote bufsiz-1 bytes */
10229126SWyllys.Ingersoll@Sun.COM 
10239126SWyllys.Ingersoll@Sun.COM 	/* Write last byte */
102410346Swyllys.ingersoll@sun.com 	tpm_put8(tpm, TPM_DATA_FIFO, buf[count]);
10259126SWyllys.Ingersoll@Sun.COM 	count++;
10269126SWyllys.Ingersoll@Sun.COM 
10279126SWyllys.Ingersoll@Sun.COM 	/* Wait for the TPM to enter Valid State */
102810346Swyllys.ingersoll@sun.com 	ret = tpm_wait_for_stat(tpm, TPM_STS_VALID, tpm->timeout_c);
10299126SWyllys.Ingersoll@Sun.COM 	if (ret == DDI_FAILURE) {
1030*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1031*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: tpm didn't enter STS_VALID state",
1032*10561Swyllys.ingersoll@sun.com 		    myname);
1033*10561Swyllys.ingersoll@sun.com #endif
10349126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
10359126SWyllys.Ingersoll@Sun.COM 	}
10369126SWyllys.Ingersoll@Sun.COM 
10379126SWyllys.Ingersoll@Sun.COM 	status = tis_get_status(tpm);
10389126SWyllys.Ingersoll@Sun.COM 	/* The TPM should NOT be expecing more data at this point */
10399126SWyllys.Ingersoll@Sun.COM 	if ((status & TPM_STS_DATA_EXPECT) != 0) {
1040*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1041*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: DATA_EXPECT should not be set after "
10429126SWyllys.Ingersoll@Sun.COM 		    "writing the last byte: status=0x%08X", myname, status);
1043*10561Swyllys.ingersoll@sun.com #endif
10449126SWyllys.Ingersoll@Sun.COM 		ret = DDI_FAILURE;
10459126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
10469126SWyllys.Ingersoll@Sun.COM 	}
10479126SWyllys.Ingersoll@Sun.COM 
10489126SWyllys.Ingersoll@Sun.COM 	/*
10499126SWyllys.Ingersoll@Sun.COM 	 * Final step: Writing TPM_STS_GO to TPM_STS
10509126SWyllys.Ingersoll@Sun.COM 	 * register will actually send the command.
10519126SWyllys.Ingersoll@Sun.COM 	 */
105210346Swyllys.ingersoll@sun.com 	tpm_put8(tpm, TPM_STS, TPM_STS_GO);
10539126SWyllys.Ingersoll@Sun.COM 
10549126SWyllys.Ingersoll@Sun.COM 	/* Ordinal/Command_code is located in buf[6..9] */
10559126SWyllys.Ingersoll@Sun.COM 	ordinal = load32(buf, TPM_COMMAND_CODE_OFFSET);
10569126SWyllys.Ingersoll@Sun.COM 
10579126SWyllys.Ingersoll@Sun.COM 	ret = tpm_wait_for_stat(tpm, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
105810346Swyllys.ingersoll@sun.com 	    tpm_get_ordinal_duration(tpm, ordinal));
10599126SWyllys.Ingersoll@Sun.COM 	if (ret == DDI_FAILURE) {
1060*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
10619126SWyllys.Ingersoll@Sun.COM 		status = tis_get_status(tpm);
10629126SWyllys.Ingersoll@Sun.COM 		if (!(status & TPM_STS_DATA_AVAIL) ||
10639126SWyllys.Ingersoll@Sun.COM 		    !(status & TPM_STS_VALID)) {
1064*10561Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "!%s: TPM not ready or valid "
1065*10561Swyllys.ingersoll@sun.com 			    "(ordinal = %d timeout = %ld status = 0x%0x)",
10669126SWyllys.Ingersoll@Sun.COM 			    myname, ordinal,
1067*10561Swyllys.ingersoll@sun.com 			    tpm_get_ordinal_duration(tpm, ordinal),
1068*10561Swyllys.ingersoll@sun.com 			    status);
10699126SWyllys.Ingersoll@Sun.COM 		} else {
1070*10561Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "!%s: tpm_wait_for_stat "
1071*10561Swyllys.ingersoll@sun.com 			    "(DATA_AVAIL | VALID) failed status = 0x%0X",
10729126SWyllys.Ingersoll@Sun.COM 			    myname, status);
10739126SWyllys.Ingersoll@Sun.COM 		}
1074*10561Swyllys.ingersoll@sun.com #endif
10759126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
10769126SWyllys.Ingersoll@Sun.COM 	}
10779126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
10789126SWyllys.Ingersoll@Sun.COM 
10799126SWyllys.Ingersoll@Sun.COM FAIL:
10809126SWyllys.Ingersoll@Sun.COM 	tpm_set_ready(tpm);
10819126SWyllys.Ingersoll@Sun.COM 	tis_release_locality(tpm, tpm->locality, 0);
10829126SWyllys.Ingersoll@Sun.COM 	return (ret);
10839126SWyllys.Ingersoll@Sun.COM }
10849126SWyllys.Ingersoll@Sun.COM 
10859126SWyllys.Ingersoll@Sun.COM /*
10869126SWyllys.Ingersoll@Sun.COM  * Clear XrequestUse and Xactivelocality, where X is the current locality
10879126SWyllys.Ingersoll@Sun.COM  */
10889126SWyllys.Ingersoll@Sun.COM static void
tis_release_locality(tpm_state_t * tpm,char locality,int force)10899126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm_state_t *tpm, char locality, int force) {
10909126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
10919126SWyllys.Ingersoll@Sun.COM 
10929126SWyllys.Ingersoll@Sun.COM 	if (force ||
109310346Swyllys.ingersoll@sun.com 	    (tpm_get8(tpm, TPM_ACCESS) &
109410346Swyllys.ingersoll@sun.com 	    (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
109510346Swyllys.ingersoll@sun.com 	    (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
10969126SWyllys.Ingersoll@Sun.COM 		/*
10979126SWyllys.Ingersoll@Sun.COM 		 * Writing 1 to active locality bit in TPM_ACCESS
10989126SWyllys.Ingersoll@Sun.COM 		 * register reliquishes the control of the locality
10999126SWyllys.Ingersoll@Sun.COM 		 */
110010346Swyllys.ingersoll@sun.com 		tpm_put8(tpm, TPM_ACCESS, TPM_ACCESS_ACTIVE_LOCALITY);
11019126SWyllys.Ingersoll@Sun.COM 	}
11029126SWyllys.Ingersoll@Sun.COM }
11039126SWyllys.Ingersoll@Sun.COM 
11049126SWyllys.Ingersoll@Sun.COM /*
11059126SWyllys.Ingersoll@Sun.COM  * Checks whether the given locality is active
11069126SWyllys.Ingersoll@Sun.COM  * Use TPM_ACCESS register and the masks TPM_ACCESS_VALID,TPM_ACTIVE_LOCALITY
11079126SWyllys.Ingersoll@Sun.COM  */
11089126SWyllys.Ingersoll@Sun.COM static int
tis_check_active_locality(tpm_state_t * tpm,char locality)11099126SWyllys.Ingersoll@Sun.COM tis_check_active_locality(tpm_state_t *tpm, char locality) {
11109126SWyllys.Ingersoll@Sun.COM 	uint8_t access_bits;
111110346Swyllys.ingersoll@sun.com 	uint8_t old_locality;
11129126SWyllys.Ingersoll@Sun.COM 
11139126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
11149126SWyllys.Ingersoll@Sun.COM 
111510346Swyllys.ingersoll@sun.com 	old_locality = tpm->locality;
111610346Swyllys.ingersoll@sun.com 	tpm->locality = locality;
111710346Swyllys.ingersoll@sun.com 
111810346Swyllys.ingersoll@sun.com 	/* Just check to see if the requested locality works */
111910346Swyllys.ingersoll@sun.com 	access_bits = tpm_get8(tpm, TPM_ACCESS);
11209126SWyllys.Ingersoll@Sun.COM 	access_bits &= (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID);
11219126SWyllys.Ingersoll@Sun.COM 
112210346Swyllys.ingersoll@sun.com 	/* this was just a check, not a request to switch */
112310346Swyllys.ingersoll@sun.com 	tpm->locality = old_locality;
112410346Swyllys.ingersoll@sun.com 
112510346Swyllys.ingersoll@sun.com 	if (access_bits == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
11269126SWyllys.Ingersoll@Sun.COM 		return (DDI_SUCCESS);
112710346Swyllys.ingersoll@sun.com 	} else {
11289126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
112910346Swyllys.ingersoll@sun.com 	}
11309126SWyllys.Ingersoll@Sun.COM }
11319126SWyllys.Ingersoll@Sun.COM 
11329126SWyllys.Ingersoll@Sun.COM /* Request the TPM to be in the given locality */
11339126SWyllys.Ingersoll@Sun.COM static int
tis_request_locality(tpm_state_t * tpm,char locality)11349126SWyllys.Ingersoll@Sun.COM tis_request_locality(tpm_state_t *tpm, char locality) {
11359126SWyllys.Ingersoll@Sun.COM 	clock_t timeout;
11369126SWyllys.Ingersoll@Sun.COM 	int ret;
11379126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_request_locality";
11389126SWyllys.Ingersoll@Sun.COM 
11399126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
11409126SWyllys.Ingersoll@Sun.COM 
11419126SWyllys.Ingersoll@Sun.COM 	ret = tis_check_active_locality(tpm, locality);
11429126SWyllys.Ingersoll@Sun.COM 
11439126SWyllys.Ingersoll@Sun.COM 	if (ret == DDI_SUCCESS) {
11449126SWyllys.Ingersoll@Sun.COM 		/* Locality is already active */
11459126SWyllys.Ingersoll@Sun.COM 		tpm->locality = locality;
11469126SWyllys.Ingersoll@Sun.COM 		return (DDI_SUCCESS);
11479126SWyllys.Ingersoll@Sun.COM 	}
11489126SWyllys.Ingersoll@Sun.COM 
114910346Swyllys.ingersoll@sun.com 	tpm_put8(tpm, TPM_ACCESS, TPM_ACCESS_REQUEST_USE);
11509126SWyllys.Ingersoll@Sun.COM 	timeout = ddi_get_lbolt() + tpm->timeout_a;
11519126SWyllys.Ingersoll@Sun.COM 
11529126SWyllys.Ingersoll@Sun.COM 	/* Using polling */
11539126SWyllys.Ingersoll@Sun.COM 	while (tis_check_active_locality(tpm, locality)
11549126SWyllys.Ingersoll@Sun.COM 		!= DDI_SUCCESS) {
11559126SWyllys.Ingersoll@Sun.COM 		if (ddi_get_lbolt() >= timeout) {
1156*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1157*10561Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "!%s: (interrupt-disabled) "
115810346Swyllys.ingersoll@sun.com 			    "tis_request_locality timed out (timeout_a = %ld)",
115910346Swyllys.ingersoll@sun.com 			    myname, tpm->timeout_a);
1160*10561Swyllys.ingersoll@sun.com #endif
11619126SWyllys.Ingersoll@Sun.COM 			return (DDI_FAILURE);
11629126SWyllys.Ingersoll@Sun.COM 		}
11639126SWyllys.Ingersoll@Sun.COM 		delay(tpm->timeout_poll);
11649126SWyllys.Ingersoll@Sun.COM 	}
11659126SWyllys.Ingersoll@Sun.COM 
11669126SWyllys.Ingersoll@Sun.COM 	tpm->locality = locality;
11679126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
11689126SWyllys.Ingersoll@Sun.COM }
11699126SWyllys.Ingersoll@Sun.COM 
11709126SWyllys.Ingersoll@Sun.COM /* Read the status register */
11719126SWyllys.Ingersoll@Sun.COM static uint8_t
tis_get_status(tpm_state_t * tpm)11729126SWyllys.Ingersoll@Sun.COM tis_get_status(tpm_state_t *tpm) {
117310346Swyllys.ingersoll@sun.com 	return (tpm_get8(tpm, TPM_STS));
11749126SWyllys.Ingersoll@Sun.COM }
11759126SWyllys.Ingersoll@Sun.COM 
11769126SWyllys.Ingersoll@Sun.COM static int
tpm_wait_for_stat(tpm_state_t * tpm,uint8_t mask,clock_t timeout)117710346Swyllys.ingersoll@sun.com tpm_wait_for_stat(tpm_state_t *tpm, uint8_t mask, clock_t timeout) {
11789126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_wait_for_stat";
117910346Swyllys.ingersoll@sun.com 	clock_t absolute_timeout = ddi_get_lbolt() + timeout;
11809126SWyllys.Ingersoll@Sun.COM 
11819126SWyllys.Ingersoll@Sun.COM 	/* Using polling */
11829126SWyllys.Ingersoll@Sun.COM 	while ((tis_get_status(tpm) & mask) != mask) {
11839126SWyllys.Ingersoll@Sun.COM 		if (ddi_get_lbolt() >= absolute_timeout) {
11849126SWyllys.Ingersoll@Sun.COM 			/* Timeout reached */
1185*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1186*10561Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "!%s: using "
118710346Swyllys.ingersoll@sun.com 			    "polling - reached timeout (%ld usecs)",
118810346Swyllys.ingersoll@sun.com 			    myname, drv_hztousec(timeout));
1189*10561Swyllys.ingersoll@sun.com #endif
11909126SWyllys.Ingersoll@Sun.COM 			return (DDI_FAILURE);
11919126SWyllys.Ingersoll@Sun.COM 		}
11929126SWyllys.Ingersoll@Sun.COM 		delay(tpm->timeout_poll);
11939126SWyllys.Ingersoll@Sun.COM 	}
11949126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
11959126SWyllys.Ingersoll@Sun.COM }
11969126SWyllys.Ingersoll@Sun.COM 
11979126SWyllys.Ingersoll@Sun.COM /*
11989126SWyllys.Ingersoll@Sun.COM  * Initialize TPM device
11999126SWyllys.Ingersoll@Sun.COM  * 1. Find out supported interrupt capabilities
12009126SWyllys.Ingersoll@Sun.COM  * 2. Set up interrupt handler if supported (some BIOSes don't support
12019126SWyllys.Ingersoll@Sun.COM  * interrupts for TPMS, in which case we set up polling)
12029126SWyllys.Ingersoll@Sun.COM  * 3. Determine timeouts and commands duration
12039126SWyllys.Ingersoll@Sun.COM  */
12049126SWyllys.Ingersoll@Sun.COM static int
tis_init(tpm_state_t * tpm)12059126SWyllys.Ingersoll@Sun.COM tis_init(tpm_state_t *tpm) {
12069126SWyllys.Ingersoll@Sun.COM 	uint32_t intf_caps;
12079126SWyllys.Ingersoll@Sun.COM 	int ret;
12089126SWyllys.Ingersoll@Sun.COM 	char *myname = "tis_init";
12099126SWyllys.Ingersoll@Sun.COM 
12109126SWyllys.Ingersoll@Sun.COM 	/*
12119126SWyllys.Ingersoll@Sun.COM 	 * Temporarily set up timeouts before we get the real timeouts
12129126SWyllys.Ingersoll@Sun.COM 	 * by issuing TPM_CAP commands (but to issue TPM_CAP commands,
12139126SWyllys.Ingersoll@Sun.COM 	 * you need TIMEOUTs defined...chicken and egg problem here.
12149126SWyllys.Ingersoll@Sun.COM 	 * TPM timeouts: Convert the milliseconds to clock cycles
12159126SWyllys.Ingersoll@Sun.COM 	 */
12169126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_a = drv_usectohz(TIS_TIMEOUT_A);
12179126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_b = drv_usectohz(TIS_TIMEOUT_B);
12189126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_c = drv_usectohz(TIS_TIMEOUT_C);
12199126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_d = drv_usectohz(TIS_TIMEOUT_D);
12209126SWyllys.Ingersoll@Sun.COM 	/*
12219126SWyllys.Ingersoll@Sun.COM 	 * Do the same with the duration (real duration will be filled out
12229126SWyllys.Ingersoll@Sun.COM 	 * when we call TPM_GetCapability to get the duration values from
12239126SWyllys.Ingersoll@Sun.COM 	 * the TPM itself).
12249126SWyllys.Ingersoll@Sun.COM 	 */
12259126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_SHORT] = drv_usectohz(TPM_DEFAULT_DURATION);
12269126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_MEDIUM] = drv_usectohz(TPM_DEFAULT_DURATION);
12279126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_LONG] = drv_usectohz(TPM_DEFAULT_DURATION);
12289126SWyllys.Ingersoll@Sun.COM 	tpm->duration[TPM_UNDEFINED] = drv_usectohz(TPM_DEFAULT_DURATION);
12299126SWyllys.Ingersoll@Sun.COM 
12309126SWyllys.Ingersoll@Sun.COM 	/* Find out supported capabilities */
123110346Swyllys.ingersoll@sun.com 	intf_caps = tpm_get32(tpm, TPM_INTF_CAP);
12329126SWyllys.Ingersoll@Sun.COM 
12339126SWyllys.Ingersoll@Sun.COM 	/* Upper 3 bytes should always return 0 */
12349126SWyllys.Ingersoll@Sun.COM 	if (intf_caps & 0x7FFFFF00) {
1235*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: bad intf_caps value 0x%0X",
12369126SWyllys.Ingersoll@Sun.COM 		    myname, intf_caps);
12379126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
12389126SWyllys.Ingersoll@Sun.COM 	}
12399126SWyllys.Ingersoll@Sun.COM 
12409126SWyllys.Ingersoll@Sun.COM 	/* These two interrupts are mandatory */
12419126SWyllys.Ingersoll@Sun.COM 	if (!(intf_caps & TPM_INTF_INT_LOCALITY_CHANGE_INT)) {
1242*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN,
1243*10561Swyllys.ingersoll@sun.com 		    "!%s: Mandatory capability Locality Change Int "
12449126SWyllys.Ingersoll@Sun.COM 		    "not supported", myname);
12459126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
12469126SWyllys.Ingersoll@Sun.COM 	}
12479126SWyllys.Ingersoll@Sun.COM 	if (!(intf_caps & TPM_INTF_INT_DATA_AVAIL_INT)) {
1248*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: Mandatory capability Data Available Int "
1249*10561Swyllys.ingersoll@sun.com 		    "not supported.", myname);
12509126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
12519126SWyllys.Ingersoll@Sun.COM 	}
12529126SWyllys.Ingersoll@Sun.COM 
12539126SWyllys.Ingersoll@Sun.COM 	/*
12549126SWyllys.Ingersoll@Sun.COM 	 * Before we start writing anything to TPM's registers,
12559126SWyllys.Ingersoll@Sun.COM 	 * make sure we are in locality 0
12569126SWyllys.Ingersoll@Sun.COM 	 */
125710346Swyllys.ingersoll@sun.com 	ret = tis_request_locality(tpm, DEFAULT_LOCALITY);
12589126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1259*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: Unable to request locality %d", myname,
126010346Swyllys.ingersoll@sun.com 		    DEFAULT_LOCALITY);
12619126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
12629126SWyllys.Ingersoll@Sun.COM 	} /* Now we can refer to the locality as tpm->locality */
12639126SWyllys.Ingersoll@Sun.COM 
12649126SWyllys.Ingersoll@Sun.COM 	tpm->timeout_poll = drv_usectohz(TPM_POLLING_TIMEOUT);
12659126SWyllys.Ingersoll@Sun.COM 	tpm->intr_enabled = 0;
12669126SWyllys.Ingersoll@Sun.COM 
12679126SWyllys.Ingersoll@Sun.COM 	/* Get the real timeouts from the TPM */
12689126SWyllys.Ingersoll@Sun.COM 	ret = tpm_get_timeouts(tpm);
12699126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1270*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: tpm_get_timeouts error", myname);
12719126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
12729126SWyllys.Ingersoll@Sun.COM 	}
12739126SWyllys.Ingersoll@Sun.COM 
12749126SWyllys.Ingersoll@Sun.COM 	ret = tpm_get_duration(tpm);
12759126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1276*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: tpm_get_duration error", myname);
12779126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
12789126SWyllys.Ingersoll@Sun.COM 	}
12799126SWyllys.Ingersoll@Sun.COM 
12809126SWyllys.Ingersoll@Sun.COM 	/* This gets the TPM version information */
12819126SWyllys.Ingersoll@Sun.COM 	ret = tpm_get_version(tpm);
12829126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1283*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: tpm_get_version error", myname);
12849126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
12859126SWyllys.Ingersoll@Sun.COM 	}
12869126SWyllys.Ingersoll@Sun.COM 
12879126SWyllys.Ingersoll@Sun.COM 	/*
12889126SWyllys.Ingersoll@Sun.COM 	 * Unless the TPM completes the test of its commands,
12899126SWyllys.Ingersoll@Sun.COM 	 * it can return an error when the untested commands are called
12909126SWyllys.Ingersoll@Sun.COM 	 */
12919126SWyllys.Ingersoll@Sun.COM 	ret = tpm_continue_selftest(tpm);
12929126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1293*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: tpm_continue_selftest error", myname);
12949126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
12959126SWyllys.Ingersoll@Sun.COM 	}
12969126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
12979126SWyllys.Ingersoll@Sun.COM }
12989126SWyllys.Ingersoll@Sun.COM 
12999126SWyllys.Ingersoll@Sun.COM /*
13009126SWyllys.Ingersoll@Sun.COM  * Module Entry points
13019126SWyllys.Ingersoll@Sun.COM  */
13029126SWyllys.Ingersoll@Sun.COM int
_init(void)13039126SWyllys.Ingersoll@Sun.COM _init(void)
13049126SWyllys.Ingersoll@Sun.COM {
13059126SWyllys.Ingersoll@Sun.COM 	int ret;
13069126SWyllys.Ingersoll@Sun.COM 
13079126SWyllys.Ingersoll@Sun.COM 	ret = ddi_soft_state_init(&statep, sizeof (tpm_state_t), 1);
1308*10561Swyllys.ingersoll@sun.com 	if (ret) {
1309*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1310*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!ddi_soft_state_init failed: %d", ret);
1311*10561Swyllys.ingersoll@sun.com #endif
13129126SWyllys.Ingersoll@Sun.COM 		return (ret);
1313*10561Swyllys.ingersoll@sun.com 	}
13149126SWyllys.Ingersoll@Sun.COM 	ret = mod_install(&tpm_ml);
13159126SWyllys.Ingersoll@Sun.COM 	if (ret != 0) {
1316*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1317*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!_init: mod_install returned non-zero");
1318*10561Swyllys.ingersoll@sun.com #endif
13199126SWyllys.Ingersoll@Sun.COM 		ddi_soft_state_fini(&statep);
13209126SWyllys.Ingersoll@Sun.COM 		return (ret);
13219126SWyllys.Ingersoll@Sun.COM 	}
13229126SWyllys.Ingersoll@Sun.COM 
13239126SWyllys.Ingersoll@Sun.COM 	return (ret);
13249126SWyllys.Ingersoll@Sun.COM }
13259126SWyllys.Ingersoll@Sun.COM 
13269126SWyllys.Ingersoll@Sun.COM int
_info(struct modinfo * modinfop)13279126SWyllys.Ingersoll@Sun.COM _info(struct modinfo *modinfop)
13289126SWyllys.Ingersoll@Sun.COM {
13299126SWyllys.Ingersoll@Sun.COM 	int ret;
13309126SWyllys.Ingersoll@Sun.COM 	ret = mod_info(&tpm_ml, modinfop);
1331*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
13329126SWyllys.Ingersoll@Sun.COM 	if (ret == 0)
1333*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!mod_info failed: %d", ret);
1334*10561Swyllys.ingersoll@sun.com #endif
13359126SWyllys.Ingersoll@Sun.COM 
13369126SWyllys.Ingersoll@Sun.COM 	return (ret);
13379126SWyllys.Ingersoll@Sun.COM }
13389126SWyllys.Ingersoll@Sun.COM 
13399126SWyllys.Ingersoll@Sun.COM int
_fini()13409126SWyllys.Ingersoll@Sun.COM _fini()
13419126SWyllys.Ingersoll@Sun.COM {
13429126SWyllys.Ingersoll@Sun.COM 	int ret;
134310346Swyllys.ingersoll@sun.com 
13449126SWyllys.Ingersoll@Sun.COM 	ret = mod_remove(&tpm_ml);
134510346Swyllys.ingersoll@sun.com 	if (ret != 0)
13469126SWyllys.Ingersoll@Sun.COM 		return (ret);
134710346Swyllys.ingersoll@sun.com 
13489126SWyllys.Ingersoll@Sun.COM 	ddi_soft_state_fini(&statep);
13499126SWyllys.Ingersoll@Sun.COM 
13509126SWyllys.Ingersoll@Sun.COM 	return (ret);
13519126SWyllys.Ingersoll@Sun.COM }
13529126SWyllys.Ingersoll@Sun.COM /* End of driver configuration functions */
13539126SWyllys.Ingersoll@Sun.COM 
13549126SWyllys.Ingersoll@Sun.COM static int
tpm_resume(tpm_state_t * tpm)13559126SWyllys.Ingersoll@Sun.COM tpm_resume(tpm_state_t *tpm)
13569126SWyllys.Ingersoll@Sun.COM {
13579126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
13589126SWyllys.Ingersoll@Sun.COM 	if (!tpm->suspended) {
13599126SWyllys.Ingersoll@Sun.COM 		mutex_exit(&tpm->pm_mutex);
13609126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
13619126SWyllys.Ingersoll@Sun.COM 	}
13629126SWyllys.Ingersoll@Sun.COM 	tpm->suspended = 0;
13639126SWyllys.Ingersoll@Sun.COM 	cv_broadcast(&tpm->suspend_cv);
13649126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
13659126SWyllys.Ingersoll@Sun.COM 
13669126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
13679126SWyllys.Ingersoll@Sun.COM }
13689126SWyllys.Ingersoll@Sun.COM 
136910346Swyllys.ingersoll@sun.com #ifdef sun4v
137010346Swyllys.ingersoll@sun.com static uint64_t hsvc_tpm_minor = 0;
137110346Swyllys.ingersoll@sun.com static hsvc_info_t hsvc_tpm = {
137210346Swyllys.ingersoll@sun.com 	HSVC_REV_1, NULL, HSVC_GROUP_TPM, 1, 0, NULL
137310346Swyllys.ingersoll@sun.com };
137410346Swyllys.ingersoll@sun.com #endif
137510346Swyllys.ingersoll@sun.com 
13769126SWyllys.Ingersoll@Sun.COM /*
13779126SWyllys.Ingersoll@Sun.COM  * Sun DDI/DDK entry points
13789126SWyllys.Ingersoll@Sun.COM  */
13799126SWyllys.Ingersoll@Sun.COM static int
tpm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)13809126SWyllys.Ingersoll@Sun.COM tpm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
13819126SWyllys.Ingersoll@Sun.COM {
138210346Swyllys.ingersoll@sun.com 	int ret;
13839126SWyllys.Ingersoll@Sun.COM 	int instance;
138410346Swyllys.ingersoll@sun.com #ifndef sun4v
138510346Swyllys.ingersoll@sun.com 	int idx, nregs;
138610346Swyllys.ingersoll@sun.com #endif
13879126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_attach";
13889126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm = NULL;
13899126SWyllys.Ingersoll@Sun.COM 
13909126SWyllys.Ingersoll@Sun.COM 	ASSERT(dip != NULL);
13919126SWyllys.Ingersoll@Sun.COM 
13929126SWyllys.Ingersoll@Sun.COM 	instance = ddi_get_instance(dip);
139310346Swyllys.ingersoll@sun.com 	if (instance < 0)
139410346Swyllys.ingersoll@sun.com 		return (DDI_FAILURE);
13959126SWyllys.Ingersoll@Sun.COM 
13969126SWyllys.Ingersoll@Sun.COM 	/* Nothing out of ordinary here */
13979126SWyllys.Ingersoll@Sun.COM 	switch (cmd) {
13989126SWyllys.Ingersoll@Sun.COM 	case DDI_ATTACH:
139910346Swyllys.ingersoll@sun.com 		if (ddi_soft_state_zalloc(statep, instance) == DDI_SUCCESS) {
140010346Swyllys.ingersoll@sun.com 			tpm = ddi_get_soft_state(statep, instance);
140110346Swyllys.ingersoll@sun.com 			if (tpm == NULL) {
1402*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
140310346Swyllys.ingersoll@sun.com 				cmn_err(CE_WARN,
1404*10561Swyllys.ingersoll@sun.com 				    "!%s: cannot get state information.",
140510346Swyllys.ingersoll@sun.com 				    myname);
1406*10561Swyllys.ingersoll@sun.com #endif
140710346Swyllys.ingersoll@sun.com 				return (DDI_FAILURE);
140810346Swyllys.ingersoll@sun.com 			}
140910346Swyllys.ingersoll@sun.com 			tpm->dip = dip;
141010346Swyllys.ingersoll@sun.com 		} else {
141110458Swyllys.ingersoll@sun.com #ifdef DEBUG
141210346Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN,
1413*10561Swyllys.ingersoll@sun.com 			    "!%s: cannot allocate state information.",
14149126SWyllys.Ingersoll@Sun.COM 			    myname);
141510458Swyllys.ingersoll@sun.com #endif
141610346Swyllys.ingersoll@sun.com 			return (DDI_FAILURE);
14179126SWyllys.Ingersoll@Sun.COM 		}
14189126SWyllys.Ingersoll@Sun.COM 		break;
14199126SWyllys.Ingersoll@Sun.COM 	case DDI_RESUME:
14209126SWyllys.Ingersoll@Sun.COM 		tpm = ddi_get_soft_state(statep, instance);
14219126SWyllys.Ingersoll@Sun.COM 		if (tpm == NULL) {
1422*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1423*10561Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "!%s: cannot get state information.",
14249126SWyllys.Ingersoll@Sun.COM 			    myname);
1425*10561Swyllys.ingersoll@sun.com #endif
142610346Swyllys.ingersoll@sun.com 			return (DDI_FAILURE);
14279126SWyllys.Ingersoll@Sun.COM 		}
14289126SWyllys.Ingersoll@Sun.COM 		return (tpm_resume(tpm));
14299126SWyllys.Ingersoll@Sun.COM 	default:
1430*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1431*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: cmd %d is not implemented", myname, cmd);
1432*10561Swyllys.ingersoll@sun.com #endif
14339126SWyllys.Ingersoll@Sun.COM 		ret = DDI_FAILURE;
14349126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
14359126SWyllys.Ingersoll@Sun.COM 	}
14369126SWyllys.Ingersoll@Sun.COM 
14379126SWyllys.Ingersoll@Sun.COM 	/* Zeroize the flag, which is used to keep track of what is allocated */
14389126SWyllys.Ingersoll@Sun.COM 	tpm->flags = 0;
14399126SWyllys.Ingersoll@Sun.COM 
144010346Swyllys.ingersoll@sun.com #ifdef sun4v
144110346Swyllys.ingersoll@sun.com 	ret = hsvc_register(&hsvc_tpm, &hsvc_tpm_minor);
144210346Swyllys.ingersoll@sun.com 	if (ret != 0) {
1443*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: failed to register with "
144410346Swyllys.ingersoll@sun.com 		    "hypervisor: 0x%0x", myname, ret);
144510346Swyllys.ingersoll@sun.com 		goto FAIL;
144610346Swyllys.ingersoll@sun.com 	}
144710346Swyllys.ingersoll@sun.com 	tpm->flags |= TPM_HSVC_REGISTERED;
144810346Swyllys.ingersoll@sun.com #else
14499126SWyllys.Ingersoll@Sun.COM 	tpm->accattr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
14509126SWyllys.Ingersoll@Sun.COM 	tpm->accattr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
14519126SWyllys.Ingersoll@Sun.COM 	tpm->accattr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
14529126SWyllys.Ingersoll@Sun.COM 
14539126SWyllys.Ingersoll@Sun.COM 	idx = 0;
14549126SWyllys.Ingersoll@Sun.COM 	ret = ddi_dev_nregs(tpm->dip, &nregs);
14559126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS)
14569126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
14579126SWyllys.Ingersoll@Sun.COM 
14589126SWyllys.Ingersoll@Sun.COM 	/*
14599126SWyllys.Ingersoll@Sun.COM 	 * TPM vendors put the TPM registers in different
14609126SWyllys.Ingersoll@Sun.COM 	 * slots in their register lists.  They are not always
14619126SWyllys.Ingersoll@Sun.COM 	 * the 1st set of registers, for instance.
14629126SWyllys.Ingersoll@Sun.COM 	 * Loop until we find the set that matches the expected
14639126SWyllys.Ingersoll@Sun.COM 	 * register size (0x5000).
14649126SWyllys.Ingersoll@Sun.COM 	 */
14659126SWyllys.Ingersoll@Sun.COM 	for (idx = 0; idx < nregs; idx++) {
14669126SWyllys.Ingersoll@Sun.COM 		off_t regsize;
14679126SWyllys.Ingersoll@Sun.COM 
14689126SWyllys.Ingersoll@Sun.COM 		if ((ret = ddi_dev_regsize(tpm->dip, idx, &regsize)) !=
14699126SWyllys.Ingersoll@Sun.COM 		    DDI_SUCCESS)
14709126SWyllys.Ingersoll@Sun.COM 			goto FAIL;
14719126SWyllys.Ingersoll@Sun.COM 		/* The TIS spec says the TPM registers must be 0x5000 bytes */
14729126SWyllys.Ingersoll@Sun.COM 		if (regsize == 0x5000)
14739126SWyllys.Ingersoll@Sun.COM 			break;
14749126SWyllys.Ingersoll@Sun.COM 	}
147510458Swyllys.ingersoll@sun.com 	if (idx == nregs) {
147610458Swyllys.ingersoll@sun.com 		ret = DDI_FAILURE;
147710458Swyllys.ingersoll@sun.com 		goto FAIL;
147810458Swyllys.ingersoll@sun.com 	}
14799382SWyllys.Ingersoll@Sun.COM 
14809126SWyllys.Ingersoll@Sun.COM 	ret = ddi_regs_map_setup(tpm->dip, idx, (caddr_t *)&tpm->addr,
14819126SWyllys.Ingersoll@Sun.COM 	    (offset_t)0, (offset_t)0x5000,
14829126SWyllys.Ingersoll@Sun.COM 	    &tpm->accattr, &tpm->handle);
14839126SWyllys.Ingersoll@Sun.COM 
14849126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
14859126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
14869126SWyllys.Ingersoll@Sun.COM 	}
14879126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DIDREGSMAP;
148810346Swyllys.ingersoll@sun.com #endif
14899126SWyllys.Ingersoll@Sun.COM 	/* Enable TPM device according to the TIS specification */
14909126SWyllys.Ingersoll@Sun.COM 	ret = tis_init(tpm);
14919126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1492*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1493*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: tis_init() failed with error %d",
14949126SWyllys.Ingersoll@Sun.COM 		    myname, ret);
1495*10561Swyllys.ingersoll@sun.com #endif
14969126SWyllys.Ingersoll@Sun.COM 
14979126SWyllys.Ingersoll@Sun.COM 		/* We need to clean up the ddi_regs_map_setup call */
149810346Swyllys.ingersoll@sun.com 		if (tpm->flags & TPM_DIDREGSMAP) {
149910346Swyllys.ingersoll@sun.com 			ddi_regs_map_free(&tpm->handle);
150010346Swyllys.ingersoll@sun.com 			tpm->handle = NULL;
150110346Swyllys.ingersoll@sun.com 			tpm->flags &= ~TPM_DIDREGSMAP;
150210346Swyllys.ingersoll@sun.com 		}
15039126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
15049126SWyllys.Ingersoll@Sun.COM 	}
15059126SWyllys.Ingersoll@Sun.COM 
15069126SWyllys.Ingersoll@Sun.COM 	/* Initialize the inter-process lock */
15079126SWyllys.Ingersoll@Sun.COM 	mutex_init(&tpm->dev_lock, NULL, MUTEX_DRIVER, NULL);
15089126SWyllys.Ingersoll@Sun.COM 	mutex_init(&tpm->pm_mutex, NULL, MUTEX_DRIVER, NULL);
15099126SWyllys.Ingersoll@Sun.COM 	cv_init(&tpm->suspend_cv, NULL, CV_DRIVER, NULL);
15109126SWyllys.Ingersoll@Sun.COM 
15119126SWyllys.Ingersoll@Sun.COM 	/* Set the suspend/resume property */
15129126SWyllys.Ingersoll@Sun.COM 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
15139126SWyllys.Ingersoll@Sun.COM 	    "pm-hardware-state", "needs-suspend-resume");
15149126SWyllys.Ingersoll@Sun.COM 
15159126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
15169126SWyllys.Ingersoll@Sun.COM 	tpm->suspended = 0;
15179126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
15189126SWyllys.Ingersoll@Sun.COM 
15199126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_MUTEX;
15209126SWyllys.Ingersoll@Sun.COM 
15219126SWyllys.Ingersoll@Sun.COM 	/* Initialize the buffer and the lock associated with it */
15229126SWyllys.Ingersoll@Sun.COM 	tpm->bufsize = TPM_IO_BUF_SIZE;
15239126SWyllys.Ingersoll@Sun.COM 	tpm->iobuf = kmem_zalloc((sizeof (uint8_t))*(tpm->bufsize), KM_SLEEP);
15249126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_IO_ALLOC;
15259126SWyllys.Ingersoll@Sun.COM 
15269126SWyllys.Ingersoll@Sun.COM 	mutex_init(&tpm->iobuf_lock, NULL, MUTEX_DRIVER, NULL);
15279126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_IO_MUTEX;
15289126SWyllys.Ingersoll@Sun.COM 
15299126SWyllys.Ingersoll@Sun.COM 	cv_init(&tpm->iobuf_cv, NULL, CV_DRIVER, NULL);
15309126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DID_IO_CV;
15319126SWyllys.Ingersoll@Sun.COM 
15329126SWyllys.Ingersoll@Sun.COM 	/* Create minor node */
15339126SWyllys.Ingersoll@Sun.COM 	ret = ddi_create_minor_node(dip, "tpm", S_IFCHR, ddi_get_instance(dip),
15349126SWyllys.Ingersoll@Sun.COM 	    DDI_PSEUDO, 0);
15359126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1536*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1537*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: ddi_create_minor_node failed", myname);
1538*10561Swyllys.ingersoll@sun.com #endif
15399126SWyllys.Ingersoll@Sun.COM 		goto FAIL;
15409126SWyllys.Ingersoll@Sun.COM 	}
15419126SWyllys.Ingersoll@Sun.COM 	tpm->flags |= TPM_DIDMINOR;
15429126SWyllys.Ingersoll@Sun.COM 
154310346Swyllys.ingersoll@sun.com #ifdef KCF_TPM_RNG_PROVIDER
154410346Swyllys.ingersoll@sun.com 	/* register RNG with kcf */
154510346Swyllys.ingersoll@sun.com 	if (tpmrng_register(tpm) != DDI_SUCCESS)
1546*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: tpm RNG failed to register with kcf",
154710346Swyllys.ingersoll@sun.com 		    myname);
154810346Swyllys.ingersoll@sun.com #endif
154910346Swyllys.ingersoll@sun.com 
15509126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
15519126SWyllys.Ingersoll@Sun.COM FAIL:
15529126SWyllys.Ingersoll@Sun.COM 	if (tpm != NULL) {
15539126SWyllys.Ingersoll@Sun.COM 		tpm_cleanup(dip, tpm);
15549126SWyllys.Ingersoll@Sun.COM 		ddi_soft_state_free(statep, instance);
15559126SWyllys.Ingersoll@Sun.COM 		tpm = NULL;
15569126SWyllys.Ingersoll@Sun.COM 	}
15579126SWyllys.Ingersoll@Sun.COM 
15589126SWyllys.Ingersoll@Sun.COM 	return (DDI_FAILURE);
15599126SWyllys.Ingersoll@Sun.COM }
15609126SWyllys.Ingersoll@Sun.COM 
15619126SWyllys.Ingersoll@Sun.COM /*
15629126SWyllys.Ingersoll@Sun.COM  * Called by tpm_detach and tpm_attach (only on failure)
15639126SWyllys.Ingersoll@Sun.COM  * Free up the resources that are allocated
15649126SWyllys.Ingersoll@Sun.COM  */
15659126SWyllys.Ingersoll@Sun.COM static void
tpm_cleanup(dev_info_t * dip,tpm_state_t * tpm)15669126SWyllys.Ingersoll@Sun.COM tpm_cleanup(dev_info_t *dip, tpm_state_t *tpm)
15679126SWyllys.Ingersoll@Sun.COM {
15689126SWyllys.Ingersoll@Sun.COM 	if (tpm == NULL)
15699126SWyllys.Ingersoll@Sun.COM 		return;
15709126SWyllys.Ingersoll@Sun.COM 
157110346Swyllys.ingersoll@sun.com #ifdef KCF_TPM_RNG_PROVIDER
157210346Swyllys.ingersoll@sun.com 	(void) tpmrng_unregister(tpm);
157310346Swyllys.ingersoll@sun.com #endif
157410346Swyllys.ingersoll@sun.com 
157510346Swyllys.ingersoll@sun.com #ifdef sun4v
157610346Swyllys.ingersoll@sun.com 	if (tpm->flags & TPM_HSVC_REGISTERED) {
157710346Swyllys.ingersoll@sun.com 		(void) hsvc_unregister(&hsvc_tpm);
157810346Swyllys.ingersoll@sun.com 		tpm->flags &= ~(TPM_HSVC_REGISTERED);
157910346Swyllys.ingersoll@sun.com 	}
158010346Swyllys.ingersoll@sun.com #endif
15819126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_MUTEX) {
15829126SWyllys.Ingersoll@Sun.COM 		mutex_destroy(&tpm->dev_lock);
158310458Swyllys.ingersoll@sun.com 		mutex_destroy(&tpm->pm_mutex);
158410458Swyllys.ingersoll@sun.com 		cv_destroy(&tpm->suspend_cv);
15859126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_MUTEX);
15869126SWyllys.Ingersoll@Sun.COM 	}
15879126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_IO_ALLOC) {
15889126SWyllys.Ingersoll@Sun.COM 		ASSERT(tpm->iobuf != NULL);
15899126SWyllys.Ingersoll@Sun.COM 		kmem_free(tpm->iobuf, (sizeof (uint8_t))*(tpm->bufsize));
15909126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_IO_ALLOC);
15919126SWyllys.Ingersoll@Sun.COM 	}
15929126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_IO_MUTEX) {
15939126SWyllys.Ingersoll@Sun.COM 		mutex_destroy(&tpm->iobuf_lock);
15949126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_IO_MUTEX);
15959126SWyllys.Ingersoll@Sun.COM 	}
15969126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DID_IO_CV) {
15979126SWyllys.Ingersoll@Sun.COM 		cv_destroy(&tpm->iobuf_cv);
15989126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DID_IO_CV);
15999126SWyllys.Ingersoll@Sun.COM 	}
16009126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DIDREGSMAP) {
16019126SWyllys.Ingersoll@Sun.COM 		/* Free the mapped addresses */
16029126SWyllys.Ingersoll@Sun.COM 		if (tpm->handle != NULL)
16039126SWyllys.Ingersoll@Sun.COM 			ddi_regs_map_free(&tpm->handle);
16049126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DIDREGSMAP);
16059126SWyllys.Ingersoll@Sun.COM 	}
16069126SWyllys.Ingersoll@Sun.COM 	if (tpm->flags & TPM_DIDMINOR) {
16079126SWyllys.Ingersoll@Sun.COM 		/* Remove minor node */
16089126SWyllys.Ingersoll@Sun.COM 		ddi_remove_minor_node(dip, NULL);
16099126SWyllys.Ingersoll@Sun.COM 		tpm->flags &= ~(TPM_DIDMINOR);
16109126SWyllys.Ingersoll@Sun.COM 	}
16119126SWyllys.Ingersoll@Sun.COM }
16129126SWyllys.Ingersoll@Sun.COM 
16139126SWyllys.Ingersoll@Sun.COM static int
tpm_suspend(tpm_state_t * tpm)16149126SWyllys.Ingersoll@Sun.COM tpm_suspend(tpm_state_t *tpm)
16159126SWyllys.Ingersoll@Sun.COM {
16169126SWyllys.Ingersoll@Sun.COM 	if (tpm == NULL)
16179126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
16189126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->pm_mutex);
16199126SWyllys.Ingersoll@Sun.COM 	if (tpm->suspended) {
16209126SWyllys.Ingersoll@Sun.COM 		mutex_exit(&tpm->pm_mutex);
16219126SWyllys.Ingersoll@Sun.COM 		return (DDI_SUCCESS);
16229126SWyllys.Ingersoll@Sun.COM 	}
16239126SWyllys.Ingersoll@Sun.COM 	tpm->suspended = 1;
16249126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->pm_mutex);
16259126SWyllys.Ingersoll@Sun.COM 
16269126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
16279126SWyllys.Ingersoll@Sun.COM }
16289126SWyllys.Ingersoll@Sun.COM 
16299126SWyllys.Ingersoll@Sun.COM static int
tpm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)16309126SWyllys.Ingersoll@Sun.COM tpm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
16319126SWyllys.Ingersoll@Sun.COM {
16329126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_detach";
16339126SWyllys.Ingersoll@Sun.COM 	int instance;
16349126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
16359126SWyllys.Ingersoll@Sun.COM 
16369126SWyllys.Ingersoll@Sun.COM 	ASSERT(dip != NULL);
16379126SWyllys.Ingersoll@Sun.COM 
16389126SWyllys.Ingersoll@Sun.COM 	instance = ddi_get_instance(dip);
163910346Swyllys.ingersoll@sun.com 	if (instance < 0)
164010346Swyllys.ingersoll@sun.com 		return (DDI_FAILURE);
164110346Swyllys.ingersoll@sun.com 
16429126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1643*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1644*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
16459126SWyllys.Ingersoll@Sun.COM 		    myname);
1646*10561Swyllys.ingersoll@sun.com #endif
16479126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
16489126SWyllys.Ingersoll@Sun.COM 	}
16499126SWyllys.Ingersoll@Sun.COM 
16509126SWyllys.Ingersoll@Sun.COM 	switch (cmd) {
16519126SWyllys.Ingersoll@Sun.COM 	case DDI_DETACH:
16529126SWyllys.Ingersoll@Sun.COM 		/* Body is after the switch stmt */
16539126SWyllys.Ingersoll@Sun.COM 		break;
16549126SWyllys.Ingersoll@Sun.COM 	case DDI_SUSPEND:
16559126SWyllys.Ingersoll@Sun.COM 		return (tpm_suspend(tpm));
16569126SWyllys.Ingersoll@Sun.COM 	default:
1657*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1658*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: case %d not implemented", myname, cmd);
1659*10561Swyllys.ingersoll@sun.com #endif
16609126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
16619126SWyllys.Ingersoll@Sun.COM 	}
16629126SWyllys.Ingersoll@Sun.COM 
16639126SWyllys.Ingersoll@Sun.COM 	/* Since we are freeing tpm structure, we need to gain the lock */
16649126SWyllys.Ingersoll@Sun.COM 	tpm_cleanup(dip, tpm);
16659126SWyllys.Ingersoll@Sun.COM 
16669126SWyllys.Ingersoll@Sun.COM 	/* Free the soft state */
16679126SWyllys.Ingersoll@Sun.COM 	ddi_soft_state_free(statep, instance);
16689126SWyllys.Ingersoll@Sun.COM 	tpm = NULL;
16699126SWyllys.Ingersoll@Sun.COM 
16709126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
16719126SWyllys.Ingersoll@Sun.COM }
16729126SWyllys.Ingersoll@Sun.COM 
16739126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
16749126SWyllys.Ingersoll@Sun.COM static int
tpm_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)16759126SWyllys.Ingersoll@Sun.COM tpm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
16769126SWyllys.Ingersoll@Sun.COM {
16779126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_getinfo";
16789126SWyllys.Ingersoll@Sun.COM 	int instance;
16799126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
16809126SWyllys.Ingersoll@Sun.COM 
16819126SWyllys.Ingersoll@Sun.COM 	instance = ddi_get_instance(dip);
16829126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1683*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1684*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
16859126SWyllys.Ingersoll@Sun.COM 		    myname);
1686*10561Swyllys.ingersoll@sun.com #endif
16879126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
16889126SWyllys.Ingersoll@Sun.COM 	}
16899126SWyllys.Ingersoll@Sun.COM 
16909126SWyllys.Ingersoll@Sun.COM 	switch (cmd) {
16919126SWyllys.Ingersoll@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
16929126SWyllys.Ingersoll@Sun.COM 		*resultp = tpm->dip;
16939126SWyllys.Ingersoll@Sun.COM 		break;
16949126SWyllys.Ingersoll@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
16959126SWyllys.Ingersoll@Sun.COM 		*resultp = 0;
16969126SWyllys.Ingersoll@Sun.COM 		break;
16979126SWyllys.Ingersoll@Sun.COM 	default:
1698*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1699*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: cmd %d is not implemented", myname, cmd);
1700*10561Swyllys.ingersoll@sun.com #endif
17019126SWyllys.Ingersoll@Sun.COM 		return (DDI_FAILURE);
17029126SWyllys.Ingersoll@Sun.COM 	}
17039126SWyllys.Ingersoll@Sun.COM 	return (DDI_SUCCESS);
17049126SWyllys.Ingersoll@Sun.COM }
17059126SWyllys.Ingersoll@Sun.COM 
17069126SWyllys.Ingersoll@Sun.COM /*
17079126SWyllys.Ingersoll@Sun.COM  * Driver entry points
17089126SWyllys.Ingersoll@Sun.COM  */
17099126SWyllys.Ingersoll@Sun.COM 
17109126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
17119126SWyllys.Ingersoll@Sun.COM static int
tpm_open(dev_t * devp,int flag,int otyp,cred_t * cred)17129126SWyllys.Ingersoll@Sun.COM tpm_open(dev_t *devp, int flag, int otyp, cred_t *cred)
17139126SWyllys.Ingersoll@Sun.COM {
17149126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_open";
17159126SWyllys.Ingersoll@Sun.COM 	int instance;
17169126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
17179126SWyllys.Ingersoll@Sun.COM 
17189126SWyllys.Ingersoll@Sun.COM 	ASSERT(devp != NULL);
17199126SWyllys.Ingersoll@Sun.COM 
17209126SWyllys.Ingersoll@Sun.COM 	instance = getminor(*devp);
17219126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1722*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1723*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
17249126SWyllys.Ingersoll@Sun.COM 		    myname);
1725*10561Swyllys.ingersoll@sun.com #endif
17269126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
17279126SWyllys.Ingersoll@Sun.COM 	}
17289126SWyllys.Ingersoll@Sun.COM 	if (otyp != OTYP_CHR) {
1729*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1730*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: otyp(%d) != OTYP_CHR(%d)",
17319126SWyllys.Ingersoll@Sun.COM 		    myname, otyp, OTYP_CHR);
1732*10561Swyllys.ingersoll@sun.com #endif
17339126SWyllys.Ingersoll@Sun.COM 		return (EINVAL);
17349126SWyllys.Ingersoll@Sun.COM 	}
173510346Swyllys.ingersoll@sun.com 	TPM_EXCLUSIVE_LOCK(tpm);
17369126SWyllys.Ingersoll@Sun.COM 
17379126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->dev_lock);
17389126SWyllys.Ingersoll@Sun.COM 	if (tpm->dev_held) {
1739*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1740*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: the device is already being used",
17419126SWyllys.Ingersoll@Sun.COM 		    myname);
1742*10561Swyllys.ingersoll@sun.com #endif
17439126SWyllys.Ingersoll@Sun.COM 		mutex_exit(&tpm->dev_lock);
17449126SWyllys.Ingersoll@Sun.COM 		return (EBUSY);
17459126SWyllys.Ingersoll@Sun.COM 	}
17469126SWyllys.Ingersoll@Sun.COM 
17479126SWyllys.Ingersoll@Sun.COM 	/* The device is free so mark it busy */
17489126SWyllys.Ingersoll@Sun.COM 	tpm->dev_held = 1;
17499126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->dev_lock);
17509126SWyllys.Ingersoll@Sun.COM 
17519126SWyllys.Ingersoll@Sun.COM 	return (0);
17529126SWyllys.Ingersoll@Sun.COM }
17539126SWyllys.Ingersoll@Sun.COM 
17549126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
17559126SWyllys.Ingersoll@Sun.COM static int
tpm_close(dev_t dev,int flag,int otyp,cred_t * cred)17569126SWyllys.Ingersoll@Sun.COM tpm_close(dev_t dev, int flag, int otyp, cred_t *cred)
17579126SWyllys.Ingersoll@Sun.COM {
17589126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_close";
17599126SWyllys.Ingersoll@Sun.COM 	int instance;
17609126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
17619126SWyllys.Ingersoll@Sun.COM 
17629126SWyllys.Ingersoll@Sun.COM 	instance = getminor(dev);
17639126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1764*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1765*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
17669126SWyllys.Ingersoll@Sun.COM 		    myname);
1767*10561Swyllys.ingersoll@sun.com #endif
17689126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
17699126SWyllys.Ingersoll@Sun.COM 	}
17709126SWyllys.Ingersoll@Sun.COM 	if (otyp != OTYP_CHR) {
1771*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1772*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: otyp(%d) != OTYP_CHR(%d)",
17739126SWyllys.Ingersoll@Sun.COM 		    myname, otyp, OTYP_CHR);
1774*10561Swyllys.ingersoll@sun.com #endif
17759126SWyllys.Ingersoll@Sun.COM 		return (EINVAL);
17769126SWyllys.Ingersoll@Sun.COM 	}
177710346Swyllys.ingersoll@sun.com 	TPM_EXCLUSIVE_LOCK(tpm);
17789126SWyllys.Ingersoll@Sun.COM 
17799126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm->dev_held);
17809126SWyllys.Ingersoll@Sun.COM 
17819126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->dev_lock);
17829126SWyllys.Ingersoll@Sun.COM 	ASSERT(mutex_owned(&tpm->dev_lock));
17839126SWyllys.Ingersoll@Sun.COM 	tpm->dev_held = 0;
17849126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->dev_lock);
17859126SWyllys.Ingersoll@Sun.COM 
17869126SWyllys.Ingersoll@Sun.COM 	return (0);
17879126SWyllys.Ingersoll@Sun.COM }
17889126SWyllys.Ingersoll@Sun.COM 
17899126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
17909126SWyllys.Ingersoll@Sun.COM static int
tpm_read(dev_t dev,struct uio * uiop,cred_t * credp)17919126SWyllys.Ingersoll@Sun.COM tpm_read(dev_t dev, struct uio *uiop, cred_t *credp)
17929126SWyllys.Ingersoll@Sun.COM {
17939126SWyllys.Ingersoll@Sun.COM 	int ret;
17949126SWyllys.Ingersoll@Sun.COM 	uint32_t size;
17959126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_read";
17969126SWyllys.Ingersoll@Sun.COM 	int instance;
17979126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
17989126SWyllys.Ingersoll@Sun.COM 
17999126SWyllys.Ingersoll@Sun.COM 	instance = getminor(dev);
18009126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1801*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1802*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
18039126SWyllys.Ingersoll@Sun.COM 		    myname);
1804*10561Swyllys.ingersoll@sun.com #endif
18059126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
18069126SWyllys.Ingersoll@Sun.COM 	}
18079126SWyllys.Ingersoll@Sun.COM 	if (uiop == NULL) {
1808*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1809*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: passed in uiop is NULL", myname);
1810*10561Swyllys.ingersoll@sun.com #endif
18119126SWyllys.Ingersoll@Sun.COM 		return (EFAULT);
18129126SWyllys.Ingersoll@Sun.COM 	}
18139126SWyllys.Ingersoll@Sun.COM 
181410346Swyllys.ingersoll@sun.com 	TPM_EXCLUSIVE_LOCK(tpm);
18159126SWyllys.Ingersoll@Sun.COM 
18169126SWyllys.Ingersoll@Sun.COM 	/* Receive the data after requiring the lock */
181710346Swyllys.ingersoll@sun.com 	ret = tpm_io_lock(tpm);
18189126SWyllys.Ingersoll@Sun.COM 
18199126SWyllys.Ingersoll@Sun.COM 	/* Timeout reached */
182010346Swyllys.ingersoll@sun.com 	if (ret)
18219126SWyllys.Ingersoll@Sun.COM 		return (ret);
18229126SWyllys.Ingersoll@Sun.COM 
18239126SWyllys.Ingersoll@Sun.COM 	if (uiop->uio_resid > tpm->bufsize) {
1824*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1825*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: read_in data is bigger "
18269126SWyllys.Ingersoll@Sun.COM 		    "than tpm->bufsize:read in:%d, bufsiz:%d",
18279126SWyllys.Ingersoll@Sun.COM 		    myname, (int)uiop->uio_resid, (int)tpm->bufsize);
1828*10561Swyllys.ingersoll@sun.com #endif
18299126SWyllys.Ingersoll@Sun.COM 		ret = EIO;
18309126SWyllys.Ingersoll@Sun.COM 		goto OUT;
18319126SWyllys.Ingersoll@Sun.COM 	}
18329126SWyllys.Ingersoll@Sun.COM 
18339126SWyllys.Ingersoll@Sun.COM 	ret = tis_recv_data(tpm, tpm->iobuf, tpm->bufsize);
18349126SWyllys.Ingersoll@Sun.COM 	if (ret < TPM_HEADER_SIZE) {
1835*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1836*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: tis_recv_data returned error", myname);
1837*10561Swyllys.ingersoll@sun.com #endif
18389126SWyllys.Ingersoll@Sun.COM 		ret = EIO;
18399126SWyllys.Ingersoll@Sun.COM 		goto OUT;
18409126SWyllys.Ingersoll@Sun.COM 	}
18419126SWyllys.Ingersoll@Sun.COM 
18429126SWyllys.Ingersoll@Sun.COM 	size = load32(tpm->iobuf, 2);
18439126SWyllys.Ingersoll@Sun.COM 	if (ret != size) {
1844*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1845*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: tis_recv_data:"
18469126SWyllys.Ingersoll@Sun.COM 		    "expected size=%d, actually read=%d",
18479126SWyllys.Ingersoll@Sun.COM 		    myname, size, ret);
1848*10561Swyllys.ingersoll@sun.com #endif
18499126SWyllys.Ingersoll@Sun.COM 		ret = EIO;
18509126SWyllys.Ingersoll@Sun.COM 		goto OUT;
18519126SWyllys.Ingersoll@Sun.COM 	}
18529126SWyllys.Ingersoll@Sun.COM 
18539126SWyllys.Ingersoll@Sun.COM 	/* Send the buffer from the kernel to the userspace */
18549126SWyllys.Ingersoll@Sun.COM 	ret = uiomove(tpm->iobuf, size, UIO_READ, uiop);
18559126SWyllys.Ingersoll@Sun.COM 	if (ret) {
1856*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1857*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: uiomove returned error", myname);
1858*10561Swyllys.ingersoll@sun.com #endif
18599126SWyllys.Ingersoll@Sun.COM 		goto OUT;
18609126SWyllys.Ingersoll@Sun.COM 	}
18619126SWyllys.Ingersoll@Sun.COM 
18629126SWyllys.Ingersoll@Sun.COM 	/* Zeroize the buffer... */
18639126SWyllys.Ingersoll@Sun.COM 	bzero(tpm->iobuf, tpm->bufsize);
18649126SWyllys.Ingersoll@Sun.COM 	ret = DDI_SUCCESS;
18659126SWyllys.Ingersoll@Sun.COM OUT:
18669126SWyllys.Ingersoll@Sun.COM 	/* We are done now: wake up the waiting threads */
18679126SWyllys.Ingersoll@Sun.COM 	tpm_unlock(tpm);
18689126SWyllys.Ingersoll@Sun.COM 
18699126SWyllys.Ingersoll@Sun.COM 	return (ret);
18709126SWyllys.Ingersoll@Sun.COM }
18719126SWyllys.Ingersoll@Sun.COM 
18729126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/
18739126SWyllys.Ingersoll@Sun.COM static int
tpm_write(dev_t dev,struct uio * uiop,cred_t * credp)18749126SWyllys.Ingersoll@Sun.COM tpm_write(dev_t dev, struct uio *uiop, cred_t *credp)
18759126SWyllys.Ingersoll@Sun.COM {
18769126SWyllys.Ingersoll@Sun.COM 	int ret;
18779126SWyllys.Ingersoll@Sun.COM 	size_t len;
18789126SWyllys.Ingersoll@Sun.COM 	uint32_t size;
18799126SWyllys.Ingersoll@Sun.COM 	char *myname = "tpm_write";
18809126SWyllys.Ingersoll@Sun.COM 	int instance;
18819126SWyllys.Ingersoll@Sun.COM 	tpm_state_t *tpm;
18829126SWyllys.Ingersoll@Sun.COM 
18839126SWyllys.Ingersoll@Sun.COM 	instance = getminor(dev);
18849126SWyllys.Ingersoll@Sun.COM 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1885*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1886*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
18879126SWyllys.Ingersoll@Sun.COM 		    myname);
1888*10561Swyllys.ingersoll@sun.com #endif
18899126SWyllys.Ingersoll@Sun.COM 		return (ENXIO);
18909126SWyllys.Ingersoll@Sun.COM 	}
18919126SWyllys.Ingersoll@Sun.COM 
18929126SWyllys.Ingersoll@Sun.COM 	if (uiop == NULL) {
1893*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1894*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: passed in uiop is NULL", myname);
1895*10561Swyllys.ingersoll@sun.com #endif
18969126SWyllys.Ingersoll@Sun.COM 		return (EFAULT);
18979126SWyllys.Ingersoll@Sun.COM 	}
18989126SWyllys.Ingersoll@Sun.COM 
189910346Swyllys.ingersoll@sun.com 	TPM_EXCLUSIVE_LOCK(tpm);
19009126SWyllys.Ingersoll@Sun.COM 
19019126SWyllys.Ingersoll@Sun.COM 	len = uiop->uio_resid;
19029126SWyllys.Ingersoll@Sun.COM 	if (len == 0) {
1903*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1904*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: requested read of len 0", myname);
1905*10561Swyllys.ingersoll@sun.com #endif
19069126SWyllys.Ingersoll@Sun.COM 		return (0);
19079126SWyllys.Ingersoll@Sun.COM 	}
19089126SWyllys.Ingersoll@Sun.COM 
19099126SWyllys.Ingersoll@Sun.COM 	/* Get the lock for using iobuf */
191010346Swyllys.ingersoll@sun.com 	ret = tpm_io_lock(tpm);
19119126SWyllys.Ingersoll@Sun.COM 	/* Timeout Reached */
191210346Swyllys.ingersoll@sun.com 	if (ret)
19139126SWyllys.Ingersoll@Sun.COM 		return (ret);
19149126SWyllys.Ingersoll@Sun.COM 
19159126SWyllys.Ingersoll@Sun.COM 	/* Copy the header and parse the structure to find out the size... */
19169126SWyllys.Ingersoll@Sun.COM 	ret = uiomove(tpm->iobuf, TPM_HEADER_SIZE, UIO_WRITE, uiop);
19179126SWyllys.Ingersoll@Sun.COM 	if (ret) {
1918*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1919*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: uiomove returned error"
19209126SWyllys.Ingersoll@Sun.COM 		    "while getting the the header",
19219126SWyllys.Ingersoll@Sun.COM 		    myname);
1922*10561Swyllys.ingersoll@sun.com #endif
19239126SWyllys.Ingersoll@Sun.COM 		goto OUT;
19249126SWyllys.Ingersoll@Sun.COM 	}
19259126SWyllys.Ingersoll@Sun.COM 
19269126SWyllys.Ingersoll@Sun.COM 	/* Get the buffersize from the command buffer structure */
19279126SWyllys.Ingersoll@Sun.COM 	size = load32(tpm->iobuf, TPM_PARAMSIZE_OFFSET);
19289126SWyllys.Ingersoll@Sun.COM 
19299126SWyllys.Ingersoll@Sun.COM 	/* Copy the command to the contiguous buffer */
19309126SWyllys.Ingersoll@Sun.COM 	if (size > tpm->bufsize) {
1931*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1932*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: size %d is greater than "
1933*10561Swyllys.ingersoll@sun.com 		    "the tpm input buffer size %d",
19349126SWyllys.Ingersoll@Sun.COM 		    myname, (int)size, (int)tpm->bufsize);
1935*10561Swyllys.ingersoll@sun.com #endif
19369126SWyllys.Ingersoll@Sun.COM 		ret = ENXIO;
19379126SWyllys.Ingersoll@Sun.COM 		goto OUT;
19389126SWyllys.Ingersoll@Sun.COM 	}
19399126SWyllys.Ingersoll@Sun.COM 
19409126SWyllys.Ingersoll@Sun.COM 	/* Copy the buffer from the userspace to kernel */
19419126SWyllys.Ingersoll@Sun.COM 	ret = uiomove(tpm->iobuf+TPM_HEADER_SIZE, size-TPM_HEADER_SIZE,
19429126SWyllys.Ingersoll@Sun.COM 	    UIO_WRITE, uiop);
19439126SWyllys.Ingersoll@Sun.COM 
19449126SWyllys.Ingersoll@Sun.COM 	if (ret) {
1945*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1946*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: uiomove returned error"
19479126SWyllys.Ingersoll@Sun.COM 		    "while getting the rest of the command", myname);
1948*10561Swyllys.ingersoll@sun.com #endif
19499126SWyllys.Ingersoll@Sun.COM 		goto OUT;
19509126SWyllys.Ingersoll@Sun.COM 	}
19519126SWyllys.Ingersoll@Sun.COM 
19529126SWyllys.Ingersoll@Sun.COM 	/* Send the command */
19539126SWyllys.Ingersoll@Sun.COM 	ret = tis_send_data(tpm, tpm->iobuf, size);
19549126SWyllys.Ingersoll@Sun.COM 	if (ret != DDI_SUCCESS) {
1955*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
1956*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!%s: tis_send_data returned error", myname);
1957*10561Swyllys.ingersoll@sun.com #endif
19589126SWyllys.Ingersoll@Sun.COM 		ret = EFAULT;
19599126SWyllys.Ingersoll@Sun.COM 		goto OUT;
19609126SWyllys.Ingersoll@Sun.COM 	}
19619126SWyllys.Ingersoll@Sun.COM 
19629126SWyllys.Ingersoll@Sun.COM 	/* Zeroize the buffer... */
19639126SWyllys.Ingersoll@Sun.COM 	bzero(tpm->iobuf, tpm->bufsize);
19649126SWyllys.Ingersoll@Sun.COM 	ret = DDI_SUCCESS;
19659126SWyllys.Ingersoll@Sun.COM OUT:
19669126SWyllys.Ingersoll@Sun.COM 	tpm_unlock(tpm);
19679126SWyllys.Ingersoll@Sun.COM 	return (ret);
19689126SWyllys.Ingersoll@Sun.COM }
19699126SWyllys.Ingersoll@Sun.COM 
19709126SWyllys.Ingersoll@Sun.COM /*
19719126SWyllys.Ingersoll@Sun.COM  * This is to deal with the contentions for the iobuf
19729126SWyllys.Ingersoll@Sun.COM  */
19739126SWyllys.Ingersoll@Sun.COM static inline int
tpm_io_lock(tpm_state_t * tpm)197410346Swyllys.ingersoll@sun.com tpm_io_lock(tpm_state_t *tpm)
19759126SWyllys.Ingersoll@Sun.COM {
19769126SWyllys.Ingersoll@Sun.COM 	int ret;
19779126SWyllys.Ingersoll@Sun.COM 	clock_t timeout;
19789126SWyllys.Ingersoll@Sun.COM 
19799126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->iobuf_lock);
19809126SWyllys.Ingersoll@Sun.COM 	ASSERT(mutex_owned(&tpm->iobuf_lock));
19819126SWyllys.Ingersoll@Sun.COM 
19829126SWyllys.Ingersoll@Sun.COM 	timeout = ddi_get_lbolt() + drv_usectohz(TPM_IO_TIMEOUT);
19839126SWyllys.Ingersoll@Sun.COM 
19849126SWyllys.Ingersoll@Sun.COM 	/* Wait until the iobuf becomes free with the timeout */
19859126SWyllys.Ingersoll@Sun.COM 	while (tpm->iobuf_inuse) {
19869126SWyllys.Ingersoll@Sun.COM 		ret = cv_timedwait(&tpm->iobuf_cv, &tpm->iobuf_lock, timeout);
19879126SWyllys.Ingersoll@Sun.COM 		if (ret <= 0) {
19889126SWyllys.Ingersoll@Sun.COM 			/* Timeout reached */
19899126SWyllys.Ingersoll@Sun.COM 			mutex_exit(&tpm->iobuf_lock);
199010346Swyllys.ingersoll@sun.com #ifdef DEBUG
1991*10561Swyllys.ingersoll@sun.com 			cmn_err(CE_WARN, "!tpm_io_lock:iorequest timed out");
199210346Swyllys.ingersoll@sun.com #endif
199310335SJames.McPherson@Sun.COM 			return (ETIME);
19949126SWyllys.Ingersoll@Sun.COM 		}
19959126SWyllys.Ingersoll@Sun.COM 	}
19969126SWyllys.Ingersoll@Sun.COM 	tpm->iobuf_inuse = 1;
19979126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->iobuf_lock);
19989126SWyllys.Ingersoll@Sun.COM 	return (0);
19999126SWyllys.Ingersoll@Sun.COM }
20009126SWyllys.Ingersoll@Sun.COM 
20019126SWyllys.Ingersoll@Sun.COM /*
20029126SWyllys.Ingersoll@Sun.COM  * This is to deal with the contentions for the iobuf
20039126SWyllys.Ingersoll@Sun.COM  */
20049126SWyllys.Ingersoll@Sun.COM static inline void
tpm_unlock(tpm_state_t * tpm)20059126SWyllys.Ingersoll@Sun.COM tpm_unlock(tpm_state_t *tpm)
20069126SWyllys.Ingersoll@Sun.COM {
20079126SWyllys.Ingersoll@Sun.COM 	/* Wake up the waiting threads */
20089126SWyllys.Ingersoll@Sun.COM 	mutex_enter(&tpm->iobuf_lock);
20099126SWyllys.Ingersoll@Sun.COM 	ASSERT(tpm->iobuf_inuse == 1 && mutex_owned(&tpm->iobuf_lock));
20109126SWyllys.Ingersoll@Sun.COM 	tpm->iobuf_inuse = 0;
20119126SWyllys.Ingersoll@Sun.COM 	cv_broadcast(&tpm->iobuf_cv);
20129126SWyllys.Ingersoll@Sun.COM 	mutex_exit(&tpm->iobuf_lock);
20139126SWyllys.Ingersoll@Sun.COM }
201410346Swyllys.ingersoll@sun.com 
201510346Swyllys.ingersoll@sun.com #ifdef KCF_TPM_RNG_PROVIDER
201610346Swyllys.ingersoll@sun.com /*
201710346Swyllys.ingersoll@sun.com  * Random number generator entry points
201810346Swyllys.ingersoll@sun.com  */
201910346Swyllys.ingersoll@sun.com static void
strncpy_spacepad(uchar_t * s1,char * s2,int n)202010346Swyllys.ingersoll@sun.com strncpy_spacepad(uchar_t *s1, char *s2, int n)
202110346Swyllys.ingersoll@sun.com {
202210346Swyllys.ingersoll@sun.com 	int s2len = strlen(s2);
202310346Swyllys.ingersoll@sun.com 	(void) strncpy((char *)s1, s2, n);
202410346Swyllys.ingersoll@sun.com 	if (s2len < n)
202510346Swyllys.ingersoll@sun.com 		(void) memset(s1 + s2len, ' ', n - s2len);
202610346Swyllys.ingersoll@sun.com }
202710346Swyllys.ingersoll@sun.com 
202810346Swyllys.ingersoll@sun.com /*ARGSUSED*/
202910346Swyllys.ingersoll@sun.com static int
tpmrng_ext_info(crypto_provider_handle_t prov,crypto_provider_ext_info_t * ext_info,crypto_req_handle_t cfreq)203010346Swyllys.ingersoll@sun.com tpmrng_ext_info(crypto_provider_handle_t prov,
203110346Swyllys.ingersoll@sun.com 	crypto_provider_ext_info_t *ext_info,
203210346Swyllys.ingersoll@sun.com 	crypto_req_handle_t cfreq)
203310346Swyllys.ingersoll@sun.com {
203410346Swyllys.ingersoll@sun.com 	tpm_state_t *tpm = (tpm_state_t *)prov;
203510346Swyllys.ingersoll@sun.com 	char buf[64];
203610346Swyllys.ingersoll@sun.com 
203710346Swyllys.ingersoll@sun.com 	if (tpm == NULL)
203810346Swyllys.ingersoll@sun.com 		return (DDI_FAILURE);
203910346Swyllys.ingersoll@sun.com 
204010346Swyllys.ingersoll@sun.com 	strncpy_spacepad(ext_info->ei_manufacturerID,
204110346Swyllys.ingersoll@sun.com 	    (char *)tpm->vers_info.tpmVendorID,
204210346Swyllys.ingersoll@sun.com 	    sizeof (ext_info->ei_manufacturerID));
204310346Swyllys.ingersoll@sun.com 
204410346Swyllys.ingersoll@sun.com 	strncpy_spacepad(ext_info->ei_model, "0",
204510346Swyllys.ingersoll@sun.com 	    sizeof (ext_info->ei_model));
204610346Swyllys.ingersoll@sun.com 	strncpy_spacepad(ext_info->ei_serial_number, "0",
204710346Swyllys.ingersoll@sun.com 	    sizeof (ext_info->ei_serial_number));
204810346Swyllys.ingersoll@sun.com 
204910346Swyllys.ingersoll@sun.com 	ext_info->ei_flags = CRYPTO_EXTF_RNG | CRYPTO_EXTF_SO_PIN_LOCKED;
205010346Swyllys.ingersoll@sun.com 	ext_info->ei_max_session_count = CRYPTO_EFFECTIVELY_INFINITE;
205110346Swyllys.ingersoll@sun.com 	ext_info->ei_max_pin_len = 0;
205210346Swyllys.ingersoll@sun.com 	ext_info->ei_min_pin_len = 0;
205310346Swyllys.ingersoll@sun.com 	ext_info->ei_total_public_memory = CRYPTO_UNAVAILABLE_INFO;
205410346Swyllys.ingersoll@sun.com 	ext_info->ei_free_public_memory = CRYPTO_UNAVAILABLE_INFO;
205510346Swyllys.ingersoll@sun.com 	ext_info->ei_total_private_memory = CRYPTO_UNAVAILABLE_INFO;
205610346Swyllys.ingersoll@sun.com 	ext_info->ei_free_public_memory = CRYPTO_UNAVAILABLE_INFO;
205710346Swyllys.ingersoll@sun.com 	ext_info->ei_time[0] = 0;
205810346Swyllys.ingersoll@sun.com 
205910346Swyllys.ingersoll@sun.com 	ext_info->ei_hardware_version.cv_major = tpm->vers_info.version.major;
206010346Swyllys.ingersoll@sun.com 	ext_info->ei_hardware_version.cv_minor = tpm->vers_info.version.minor;
206110346Swyllys.ingersoll@sun.com 	ext_info->ei_firmware_version.cv_major =
206210346Swyllys.ingersoll@sun.com 	    tpm->vers_info.version.revMajor;
206310346Swyllys.ingersoll@sun.com 	ext_info->ei_firmware_version.cv_minor =
206410346Swyllys.ingersoll@sun.com 	    tpm->vers_info.version.revMinor;
206510346Swyllys.ingersoll@sun.com 
206610346Swyllys.ingersoll@sun.com 	(void) snprintf(buf, sizeof (buf), "tpmrng TPM RNG");
206710346Swyllys.ingersoll@sun.com 
206810346Swyllys.ingersoll@sun.com 	strncpy_spacepad(ext_info->ei_label, buf,
206910346Swyllys.ingersoll@sun.com 	    sizeof (ext_info->ei_label));
207010346Swyllys.ingersoll@sun.com #undef	BUFSZ
207110346Swyllys.ingersoll@sun.com 	return (CRYPTO_SUCCESS);
207210346Swyllys.ingersoll@sun.com 
207310346Swyllys.ingersoll@sun.com }
207410346Swyllys.ingersoll@sun.com 
207510346Swyllys.ingersoll@sun.com static int
tpmrng_register(tpm_state_t * tpm)207610346Swyllys.ingersoll@sun.com tpmrng_register(tpm_state_t *tpm)
207710346Swyllys.ingersoll@sun.com {
207810346Swyllys.ingersoll@sun.com 	int		ret;
207910346Swyllys.ingersoll@sun.com 	char 		ID[64];
208010346Swyllys.ingersoll@sun.com 	crypto_mech_name_t	*rngmech;
208110346Swyllys.ingersoll@sun.com 
208210346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
208310346Swyllys.ingersoll@sun.com 
208410346Swyllys.ingersoll@sun.com 	(void) snprintf(ID, sizeof (ID), "tpmrng %s", IDENT_TPMRNG);
208510346Swyllys.ingersoll@sun.com 
208610346Swyllys.ingersoll@sun.com 	tpmrng_prov_info.pi_provider_description = ID;
208710346Swyllys.ingersoll@sun.com 	tpmrng_prov_info.pi_provider_dev.pd_hw = tpm->dip;
208810346Swyllys.ingersoll@sun.com 	tpmrng_prov_info.pi_provider_handle = tpm;
208910346Swyllys.ingersoll@sun.com 
209010346Swyllys.ingersoll@sun.com 	ret = crypto_register_provider(&tpmrng_prov_info, &tpm->n_prov);
209110346Swyllys.ingersoll@sun.com 	if (ret != CRYPTO_SUCCESS) {
209210346Swyllys.ingersoll@sun.com 		tpm->n_prov = NULL;
209310346Swyllys.ingersoll@sun.com 		return (DDI_FAILURE);
209410346Swyllys.ingersoll@sun.com 	}
209510346Swyllys.ingersoll@sun.com 
209610346Swyllys.ingersoll@sun.com 	crypto_provider_notification(tpm->n_prov, CRYPTO_PROVIDER_READY);
209710346Swyllys.ingersoll@sun.com 
209810346Swyllys.ingersoll@sun.com 	rngmech = kmem_zalloc(strlen("random") + 1, KM_SLEEP);
209910346Swyllys.ingersoll@sun.com 	(void) memcpy(rngmech, "random", 6);
210010346Swyllys.ingersoll@sun.com 	ret = crypto_load_dev_disabled("tpm", ddi_get_instance(tpm->dip),
210110346Swyllys.ingersoll@sun.com 	    1, rngmech);
2102*10561Swyllys.ingersoll@sun.com #ifdef DEBUG
2103*10561Swyllys.ingersoll@sun.com 	if (ret != CRYPTO_SUCCESS)
2104*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!crypto_load_dev_disabled failed (%d)", ret);
2105*10561Swyllys.ingersoll@sun.com #endif
210610346Swyllys.ingersoll@sun.com 	return (DDI_SUCCESS);
210710346Swyllys.ingersoll@sun.com }
210810346Swyllys.ingersoll@sun.com 
210910346Swyllys.ingersoll@sun.com static int
tpmrng_unregister(tpm_state_t * tpm)211010346Swyllys.ingersoll@sun.com tpmrng_unregister(tpm_state_t *tpm)
211110346Swyllys.ingersoll@sun.com {
211210346Swyllys.ingersoll@sun.com 	int ret;
211310346Swyllys.ingersoll@sun.com 	ASSERT(tpm != NULL);
211410346Swyllys.ingersoll@sun.com 	if (tpm->n_prov) {
211510346Swyllys.ingersoll@sun.com 		ret = crypto_unregister_provider(tpm->n_prov);
211610346Swyllys.ingersoll@sun.com 		tpm->n_prov = NULL;
211710346Swyllys.ingersoll@sun.com 		if (ret != CRYPTO_SUCCESS)
211810346Swyllys.ingersoll@sun.com 			return (DDI_FAILURE);
211910346Swyllys.ingersoll@sun.com 	}
212010346Swyllys.ingersoll@sun.com 	return (DDI_SUCCESS);
212110346Swyllys.ingersoll@sun.com }
212210346Swyllys.ingersoll@sun.com 
212310346Swyllys.ingersoll@sun.com /*ARGSUSED*/
212410346Swyllys.ingersoll@sun.com static void
tpmrng_provider_status(crypto_provider_handle_t provider,uint_t * status)212510346Swyllys.ingersoll@sun.com tpmrng_provider_status(crypto_provider_handle_t provider, uint_t *status)
212610346Swyllys.ingersoll@sun.com {
212710346Swyllys.ingersoll@sun.com 	*status = CRYPTO_PROVIDER_READY;
212810346Swyllys.ingersoll@sun.com }
212910346Swyllys.ingersoll@sun.com 
213010346Swyllys.ingersoll@sun.com /*ARGSUSED*/
213110346Swyllys.ingersoll@sun.com static int
tpmrng_seed_random(crypto_provider_handle_t provider,crypto_session_id_t sid,uchar_t * buf,size_t len,uint_t entropy_est,uint32_t flags,crypto_req_handle_t req)213210346Swyllys.ingersoll@sun.com tpmrng_seed_random(crypto_provider_handle_t provider, crypto_session_id_t sid,
213310346Swyllys.ingersoll@sun.com     uchar_t *buf, size_t len, uint_t entropy_est, uint32_t flags,
213410346Swyllys.ingersoll@sun.com     crypto_req_handle_t req)
213510346Swyllys.ingersoll@sun.com {
213610346Swyllys.ingersoll@sun.com 	int ret;
213710346Swyllys.ingersoll@sun.com 	tpm_state_t *tpm;
213810346Swyllys.ingersoll@sun.com 	uint32_t len32;
213910346Swyllys.ingersoll@sun.com 	/* Max length of seed is 256 bytes, add 14 for header. */
214010346Swyllys.ingersoll@sun.com 	uint8_t cmdbuf[270] = {
214110346Swyllys.ingersoll@sun.com 		0, 193,		/* TPM_TAG_RQU COMMAND */
214210346Swyllys.ingersoll@sun.com 		0, 0, 0, 0x0A,	/* paramsize in bytes */
214310346Swyllys.ingersoll@sun.com 		0, 0, 0, TPM_ORD_StirRandom,
214410346Swyllys.ingersoll@sun.com 		0, 0, 0, 0 	/* number of input bytes (< 256) */
214510346Swyllys.ingersoll@sun.com 	};
214610346Swyllys.ingersoll@sun.com 	uint32_t buflen;
214710346Swyllys.ingersoll@sun.com 
214810346Swyllys.ingersoll@sun.com 	if (len == 0 || len > 255 || buf == NULL)
214910346Swyllys.ingersoll@sun.com 		return (CRYPTO_ARGUMENTS_BAD);
215010346Swyllys.ingersoll@sun.com 
215110346Swyllys.ingersoll@sun.com 	tpm = (tpm_state_t *)provider;
215210346Swyllys.ingersoll@sun.com 	if (tpm == NULL)
215310346Swyllys.ingersoll@sun.com 		return (CRYPTO_INVALID_CONTEXT);
215410346Swyllys.ingersoll@sun.com 
215510346Swyllys.ingersoll@sun.com 	/* Acquire lock for exclusive use of TPM */
215610346Swyllys.ingersoll@sun.com 	TPM_EXCLUSIVE_LOCK(tpm);
215710346Swyllys.ingersoll@sun.com 
215810346Swyllys.ingersoll@sun.com 	ret = tpm_io_lock(tpm);
215910346Swyllys.ingersoll@sun.com 	/* Timeout reached */
216010346Swyllys.ingersoll@sun.com 	if (ret)
216110346Swyllys.ingersoll@sun.com 		return (CRYPTO_BUSY);
216210346Swyllys.ingersoll@sun.com 
216310346Swyllys.ingersoll@sun.com 	/* TPM only handles 32 bit length, so truncate if too big. */
216410346Swyllys.ingersoll@sun.com 	len32 = (uint32_t)len;
216510346Swyllys.ingersoll@sun.com 	buflen = len32 + 14;
216610346Swyllys.ingersoll@sun.com 
216710346Swyllys.ingersoll@sun.com 	/* The length must be in network order */
216810346Swyllys.ingersoll@sun.com 	buflen = htonl(buflen);
216910346Swyllys.ingersoll@sun.com 	bcopy(&buflen, cmdbuf + 2, sizeof (uint32_t));
217010346Swyllys.ingersoll@sun.com 
217110346Swyllys.ingersoll@sun.com 	/* Convert it back */
217210346Swyllys.ingersoll@sun.com 	buflen = ntohl(buflen);
217310346Swyllys.ingersoll@sun.com 
217410346Swyllys.ingersoll@sun.com 	/* length must be in network order */
217510346Swyllys.ingersoll@sun.com 	len32 = htonl(len32);
217610346Swyllys.ingersoll@sun.com 	bcopy(&len32, cmdbuf + 10, sizeof (uint32_t));
217710346Swyllys.ingersoll@sun.com 
217810346Swyllys.ingersoll@sun.com 	/* convert it back */
217910346Swyllys.ingersoll@sun.com 	len32 = ntohl(len32);
218010346Swyllys.ingersoll@sun.com 
218110346Swyllys.ingersoll@sun.com 	bcopy(buf,  cmdbuf + 14, len32);
218210346Swyllys.ingersoll@sun.com 
218310346Swyllys.ingersoll@sun.com 	ret = itpm_command(tpm, cmdbuf, buflen);
218410346Swyllys.ingersoll@sun.com 	tpm_unlock(tpm);
218510346Swyllys.ingersoll@sun.com 
218610346Swyllys.ingersoll@sun.com 	if (ret != DDI_SUCCESS) {
218710346Swyllys.ingersoll@sun.com #ifdef DEBUG
2188*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!tpmrng_seed_random failed");
218910346Swyllys.ingersoll@sun.com #endif
219010346Swyllys.ingersoll@sun.com 		return (CRYPTO_FAILED);
219110346Swyllys.ingersoll@sun.com 	}
219210346Swyllys.ingersoll@sun.com 
219310346Swyllys.ingersoll@sun.com 	return (CRYPTO_SUCCESS);
219410346Swyllys.ingersoll@sun.com }
219510346Swyllys.ingersoll@sun.com 
219610346Swyllys.ingersoll@sun.com /* ARGSUSED */
219710346Swyllys.ingersoll@sun.com static int
tpmrng_generate_random(crypto_provider_handle_t provider,crypto_session_id_t sid,uchar_t * buf,size_t len,crypto_req_handle_t req)219810346Swyllys.ingersoll@sun.com tpmrng_generate_random(crypto_provider_handle_t provider,
219910346Swyllys.ingersoll@sun.com     crypto_session_id_t sid, uchar_t *buf, size_t len, crypto_req_handle_t req)
220010346Swyllys.ingersoll@sun.com {
220110346Swyllys.ingersoll@sun.com 	int ret;
220210346Swyllys.ingersoll@sun.com 	tpm_state_t *tpm;
220310346Swyllys.ingersoll@sun.com 	uint8_t hdr[14] = {
220410346Swyllys.ingersoll@sun.com 		0, 193,		/* TPM_TAG_RQU COMMAND */
220510346Swyllys.ingersoll@sun.com 		0, 0, 0, 14,	/* paramsize in bytes */
220610346Swyllys.ingersoll@sun.com 		0, 0, 0, TPM_ORD_GetRandom,
220710346Swyllys.ingersoll@sun.com 		0, 0, 0, 0
220810346Swyllys.ingersoll@sun.com 	};
220910346Swyllys.ingersoll@sun.com 	uint8_t *cmdbuf = NULL;
221010346Swyllys.ingersoll@sun.com 	uint32_t len32 = (uint32_t)len;
221110346Swyllys.ingersoll@sun.com 	uint32_t buflen = len32 + sizeof (hdr);
221210346Swyllys.ingersoll@sun.com 
221310346Swyllys.ingersoll@sun.com 	if (len == 0 || buf == NULL)
221410346Swyllys.ingersoll@sun.com 		return (CRYPTO_ARGUMENTS_BAD);
221510346Swyllys.ingersoll@sun.com 
221610346Swyllys.ingersoll@sun.com 	tpm = (tpm_state_t *)provider;
221710346Swyllys.ingersoll@sun.com 	if (tpm == NULL)
221810346Swyllys.ingersoll@sun.com 		return (CRYPTO_INVALID_CONTEXT);
221910346Swyllys.ingersoll@sun.com 
222010346Swyllys.ingersoll@sun.com 	TPM_EXCLUSIVE_LOCK(tpm);
222110346Swyllys.ingersoll@sun.com 
222210346Swyllys.ingersoll@sun.com 	ret = tpm_io_lock(tpm);
222310346Swyllys.ingersoll@sun.com 	/* Timeout reached */
222410346Swyllys.ingersoll@sun.com 	if (ret)
222510346Swyllys.ingersoll@sun.com 		return (CRYPTO_BUSY);
222610346Swyllys.ingersoll@sun.com 
222710346Swyllys.ingersoll@sun.com 	cmdbuf = kmem_zalloc(buflen, KM_SLEEP);
222810346Swyllys.ingersoll@sun.com 	bcopy(hdr, cmdbuf, sizeof (hdr));
222910346Swyllys.ingersoll@sun.com 
223010346Swyllys.ingersoll@sun.com 	/* Length is written in network byte order */
223110346Swyllys.ingersoll@sun.com 	len32 = htonl(len32);
223210346Swyllys.ingersoll@sun.com 	bcopy(&len32, cmdbuf + 10, sizeof (uint32_t));
223310346Swyllys.ingersoll@sun.com 
223410346Swyllys.ingersoll@sun.com 	ret = itpm_command(tpm, cmdbuf, buflen);
223510346Swyllys.ingersoll@sun.com 	if (ret != DDI_SUCCESS) {
223610346Swyllys.ingersoll@sun.com #ifdef DEBUG
2237*10561Swyllys.ingersoll@sun.com 		cmn_err(CE_WARN, "!tpmrng_generate_random failed");
223810346Swyllys.ingersoll@sun.com #endif
223910346Swyllys.ingersoll@sun.com 		kmem_free(cmdbuf, buflen);
224010346Swyllys.ingersoll@sun.com 		tpm_unlock(tpm);
224110346Swyllys.ingersoll@sun.com 		return (CRYPTO_FAILED);
224210346Swyllys.ingersoll@sun.com 	}
224310346Swyllys.ingersoll@sun.com 
224410346Swyllys.ingersoll@sun.com 	/* Find out how many bytes were really returned */
224510346Swyllys.ingersoll@sun.com 	len32 = load32(cmdbuf, 10);
224610346Swyllys.ingersoll@sun.com 
224710346Swyllys.ingersoll@sun.com 	/* Copy the random bytes back to the callers buffer */
224810346Swyllys.ingersoll@sun.com 	bcopy(cmdbuf + 14, buf, len32);
224910346Swyllys.ingersoll@sun.com 
225010346Swyllys.ingersoll@sun.com 	kmem_free(cmdbuf, buflen);
225110346Swyllys.ingersoll@sun.com 	tpm_unlock(tpm);
225210346Swyllys.ingersoll@sun.com 
225310346Swyllys.ingersoll@sun.com 	return (CRYPTO_SUCCESS);
225410346Swyllys.ingersoll@sun.com }
225510346Swyllys.ingersoll@sun.com #endif /* KCF_TPM_RNG_PROVIDER */
2256