xref: /illumos-gate/usr/src/uts/common/io/tpm/tpm.c (revision e9fe7b359091f8e565041c286948edcb4e1e96f6)
147e946e7SWyllys Ingersoll /*
247e946e7SWyllys Ingersoll  * CDDL HEADER START
347e946e7SWyllys Ingersoll  *
447e946e7SWyllys Ingersoll  * The contents of this file are subject to the terms of the
547e946e7SWyllys Ingersoll  * Common Development and Distribution License (the "License").
647e946e7SWyllys Ingersoll  * You may not use this file except in compliance with the License.
747e946e7SWyllys Ingersoll  *
847e946e7SWyllys Ingersoll  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
947e946e7SWyllys Ingersoll  * or http://www.opensolaris.org/os/licensing.
1047e946e7SWyllys Ingersoll  * See the License for the specific language governing permissions
1147e946e7SWyllys Ingersoll  * and limitations under the License.
1247e946e7SWyllys Ingersoll  *
1347e946e7SWyllys Ingersoll  * When distributing Covered Code, include this CDDL HEADER in each
1447e946e7SWyllys Ingersoll  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1547e946e7SWyllys Ingersoll  * If applicable, add the following below this CDDL HEADER, with the
1647e946e7SWyllys Ingersoll  * fields enclosed by brackets "[]" replaced with your own identifying
1747e946e7SWyllys Ingersoll  * information: Portions Copyright [yyyy] [name of copyright owner]
1847e946e7SWyllys Ingersoll  *
1947e946e7SWyllys Ingersoll  * CDDL HEADER END
2047e946e7SWyllys Ingersoll  */
2147e946e7SWyllys Ingersoll 
2247e946e7SWyllys Ingersoll /*
2347e946e7SWyllys Ingersoll  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2447e946e7SWyllys Ingersoll  * Use is subject to license terms.
25*e9fe7b35SJason King  * Copyright 2021 Jason King
2647e946e7SWyllys Ingersoll  */
2747e946e7SWyllys Ingersoll 
2847e946e7SWyllys Ingersoll /*
2947e946e7SWyllys Ingersoll  * TPM 1.2 Driver for the TPMs that follow TIS v1.2
3047e946e7SWyllys Ingersoll  */
3147e946e7SWyllys Ingersoll 
3247e946e7SWyllys Ingersoll #include <sys/devops.h>		/* used by dev_ops */
3347e946e7SWyllys Ingersoll #include <sys/conf.h>		/* used by dev_ops,cb_ops */
3447e946e7SWyllys Ingersoll #include <sys/modctl.h>		/* for _init,_info,_fini,mod_* */
3547e946e7SWyllys Ingersoll #include <sys/ddi.h>		/* used by all entry points */
3647e946e7SWyllys Ingersoll #include <sys/sunddi.h>		/* used by all entry points */
3747e946e7SWyllys Ingersoll #include <sys/cmn_err.h>	/* used for debug outputs */
3847e946e7SWyllys Ingersoll #include <sys/types.h>		/* used by prop_op, ddi_prop_op */
3947e946e7SWyllys Ingersoll 
4047e946e7SWyllys Ingersoll #include <sys/file.h>		/* used by open, close */
4147e946e7SWyllys Ingersoll #include <sys/errno.h>		/* used by open,close,read,write */
4247e946e7SWyllys Ingersoll #include <sys/open.h>		/* used by open,close,read,write */
4347e946e7SWyllys Ingersoll #include <sys/cred.h>		/* used by open,close,read */
4447e946e7SWyllys Ingersoll #include <sys/uio.h>		/* used by read */
4547e946e7SWyllys Ingersoll #include <sys/stat.h>		/* defines S_IFCHR */
4647e946e7SWyllys Ingersoll 
4747e946e7SWyllys Ingersoll #include <sys/byteorder.h>	/* for ntohs, ntohl, htons, htonl */
4847e946e7SWyllys Ingersoll 
498d26100cSWyllys Ingersoll #ifdef sun4v
508d26100cSWyllys Ingersoll #include <sys/hypervisor_api.h>
518d26100cSWyllys Ingersoll #include <sys/hsvc.h>
528d26100cSWyllys Ingersoll #endif
538d26100cSWyllys Ingersoll 
5447e946e7SWyllys Ingersoll #include <tss/platform.h>	/* from SUNWtss */
5547e946e7SWyllys Ingersoll #include <tss/tpm.h>		/* from SUNWtss */
5647e946e7SWyllys Ingersoll 
5747e946e7SWyllys Ingersoll #include "tpm_tis.h"
5847e946e7SWyllys Ingersoll #include "tpm_ddi.h"
5947e946e7SWyllys Ingersoll #include "tpm_duration.h"
6047e946e7SWyllys Ingersoll 
6147e946e7SWyllys Ingersoll #define	TPM_HEADER_SIZE 10
6247e946e7SWyllys Ingersoll typedef enum {
6347e946e7SWyllys Ingersoll 	TPM_TAG_OFFSET = 0,
6447e946e7SWyllys Ingersoll 	TPM_PARAMSIZE_OFFSET = 2,
6547e946e7SWyllys Ingersoll 	TPM_RETURN_OFFSET = 6,
6647e946e7SWyllys Ingersoll 	TPM_COMMAND_CODE_OFFSET = 6,
6747e946e7SWyllys Ingersoll } TPM_HEADER_OFFSET_T;
6847e946e7SWyllys Ingersoll 
6947e946e7SWyllys Ingersoll /*
7047e946e7SWyllys Ingersoll  * This is to address some TPMs that does not report the correct duration
7147e946e7SWyllys Ingersoll  * and timeouts.  In our experience with the production TPMs, we encountered
7247e946e7SWyllys Ingersoll  * time errors such as GetCapability command from TPM reporting the timeout
7347e946e7SWyllys Ingersoll  * and durations in milliseconds rather than microseconds.  Some other TPMs
7447e946e7SWyllys Ingersoll  * report the value 0's
7547e946e7SWyllys Ingersoll  *
7647e946e7SWyllys Ingersoll  * Short Duration is based on section 11.3.4 of TIS speciciation, that
7747e946e7SWyllys Ingersoll  * TPM_GetCapability (short duration) commands should not be longer than 750ms
7847e946e7SWyllys Ingersoll  * and that section 11.3.7 states that TPM_ContinueSelfTest (medium duration)
7947e946e7SWyllys Ingersoll  * should not be longer than 1 second.
8047e946e7SWyllys Ingersoll  */
8147e946e7SWyllys Ingersoll #define	DEFAULT_SHORT_DURATION	750000
8247e946e7SWyllys Ingersoll #define	DEFAULT_MEDIUM_DURATION	1000000
8347e946e7SWyllys Ingersoll #define	DEFAULT_LONG_DURATION	300000000
8447e946e7SWyllys Ingersoll #define	DEFAULT_TIMEOUT_A	750000
8547e946e7SWyllys Ingersoll #define	DEFAULT_TIMEOUT_B	2000000
8647e946e7SWyllys Ingersoll #define	DEFAULT_TIMEOUT_C	750000
8747e946e7SWyllys Ingersoll #define	DEFAULT_TIMEOUT_D	750000
8847e946e7SWyllys Ingersoll 
8947e946e7SWyllys Ingersoll /*
9047e946e7SWyllys Ingersoll  * In order to test the 'millisecond bug', we test if DURATIONS and TIMEOUTS
9147e946e7SWyllys Ingersoll  * are unreasonably low...such as 10 milliseconds (TPM isn't that fast).
9247e946e7SWyllys Ingersoll  * and 400 milliseconds for long duration
9347e946e7SWyllys Ingersoll  */
9447e946e7SWyllys Ingersoll #define	TEN_MILLISECONDS	10000	/* 10 milliseconds */
9547e946e7SWyllys Ingersoll #define	FOUR_HUNDRED_MILLISECONDS 400000	/* 4 hundred milliseconds */
9647e946e7SWyllys Ingersoll 
978d26100cSWyllys Ingersoll #define	DEFAULT_LOCALITY 0
9847e946e7SWyllys Ingersoll /*
9947e946e7SWyllys Ingersoll  * TPM input/output buffer offsets
10047e946e7SWyllys Ingersoll  */
10147e946e7SWyllys Ingersoll 
10247e946e7SWyllys Ingersoll typedef enum {
10347e946e7SWyllys Ingersoll 	TPM_CAP_RESPSIZE_OFFSET = 10,
10447e946e7SWyllys Ingersoll 	TPM_CAP_RESP_OFFSET = 14,
10547e946e7SWyllys Ingersoll } TPM_CAP_RET_OFFSET_T;
10647e946e7SWyllys Ingersoll 
10747e946e7SWyllys Ingersoll typedef enum {
10847e946e7SWyllys Ingersoll 	TPM_CAP_TIMEOUT_A_OFFSET = 14,
10947e946e7SWyllys Ingersoll 	TPM_CAP_TIMEOUT_B_OFFSET = 18,
11047e946e7SWyllys Ingersoll 	TPM_CAP_TIMEOUT_C_OFFSET = 22,
11147e946e7SWyllys Ingersoll 	TPM_CAP_TIMEOUT_D_OFFSET = 26,
11247e946e7SWyllys Ingersoll } TPM_CAP_TIMEOUT_OFFSET_T;
11347e946e7SWyllys Ingersoll 
11447e946e7SWyllys Ingersoll typedef enum {
11547e946e7SWyllys Ingersoll 	TPM_CAP_DUR_SHORT_OFFSET = 14,
11647e946e7SWyllys Ingersoll 	TPM_CAP_DUR_MEDIUM_OFFSET = 18,
11747e946e7SWyllys Ingersoll 	TPM_CAP_DUR_LONG_OFFSET = 22,
11847e946e7SWyllys Ingersoll } TPM_CAP_DURATION_OFFSET_T;
11947e946e7SWyllys Ingersoll 
12047e946e7SWyllys Ingersoll #define	TPM_CAP_VERSION_INFO_OFFSET	14
12147e946e7SWyllys Ingersoll #define	TPM_CAP_VERSION_INFO_SIZE	15
12247e946e7SWyllys Ingersoll 
12347e946e7SWyllys Ingersoll /*
12447e946e7SWyllys Ingersoll  * Internal TPM command functions
12547e946e7SWyllys Ingersoll  */
12647e946e7SWyllys Ingersoll static int itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz);
12747e946e7SWyllys Ingersoll static int tpm_get_timeouts(tpm_state_t *tpm);
12847e946e7SWyllys Ingersoll static int tpm_get_duration(tpm_state_t *tpm);
12947e946e7SWyllys Ingersoll static int tpm_get_version(tpm_state_t *tpm);
13047e946e7SWyllys Ingersoll static int tpm_continue_selftest(tpm_state_t *tpm);
13147e946e7SWyllys Ingersoll 
13247e946e7SWyllys Ingersoll /*
13347e946e7SWyllys Ingersoll  * Internal TIS related functions
13447e946e7SWyllys Ingersoll  */
13547e946e7SWyllys Ingersoll static int tpm_wait_for_stat(tpm_state_t *, uint8_t, clock_t);
13647e946e7SWyllys Ingersoll static clock_t tpm_get_ordinal_duration(tpm_state_t *, uint8_t);
13747e946e7SWyllys Ingersoll static int tis_check_active_locality(tpm_state_t *, char);
13847e946e7SWyllys Ingersoll static int tis_request_locality(tpm_state_t *, char);
13947e946e7SWyllys Ingersoll static void tis_release_locality(tpm_state_t *, char, int);
14047e946e7SWyllys Ingersoll static int tis_init(tpm_state_t *);
14147e946e7SWyllys Ingersoll static uint8_t tis_get_status(tpm_state_t *);
14247e946e7SWyllys Ingersoll static int tis_send_data(tpm_state_t *, uint8_t *, size_t);
14347e946e7SWyllys Ingersoll static int tis_recv_data(tpm_state_t *, uint8_t *, size_t);
14447e946e7SWyllys Ingersoll 
14547e946e7SWyllys Ingersoll /* Auxilliary */
14647e946e7SWyllys Ingersoll static int receive_data(tpm_state_t *, uint8_t *, size_t);
1478d26100cSWyllys Ingersoll static inline int tpm_io_lock(tpm_state_t *);
14847e946e7SWyllys Ingersoll static inline void tpm_unlock(tpm_state_t *);
14947e946e7SWyllys Ingersoll static void tpm_cleanup(dev_info_t *, tpm_state_t *);
15047e946e7SWyllys Ingersoll 
15147e946e7SWyllys Ingersoll /*
15247e946e7SWyllys Ingersoll  * Sun DDI/DDK entry points
15347e946e7SWyllys Ingersoll  */
15447e946e7SWyllys Ingersoll 
15547e946e7SWyllys Ingersoll /* Declaration of autoconfig functions */
15647e946e7SWyllys Ingersoll static int tpm_attach(dev_info_t *, ddi_attach_cmd_t);
15747e946e7SWyllys Ingersoll static int tpm_detach(dev_info_t *, ddi_detach_cmd_t);
15847e946e7SWyllys Ingersoll static int tpm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
15947e946e7SWyllys Ingersoll static int tpm_quiesce(dev_info_t *);
16047e946e7SWyllys Ingersoll /* End of autoconfig functions */
16147e946e7SWyllys Ingersoll 
16247e946e7SWyllys Ingersoll /* Declaration of driver entry point functions */
16347e946e7SWyllys Ingersoll static int tpm_open(dev_t *, int, int, cred_t *);
16447e946e7SWyllys Ingersoll static int tpm_close(dev_t, int, int, cred_t *);
16547e946e7SWyllys Ingersoll static int tpm_read(dev_t, struct uio *, cred_t *);
16647e946e7SWyllys Ingersoll static int tpm_write(dev_t, struct uio *, cred_t *);
16747e946e7SWyllys Ingersoll /* End of driver entry point functions */
16847e946e7SWyllys Ingersoll 
16947e946e7SWyllys Ingersoll /* cb_ops structure */
17047e946e7SWyllys Ingersoll static struct cb_ops tpm_cb_ops = {
17147e946e7SWyllys Ingersoll 	tpm_open,
17247e946e7SWyllys Ingersoll 	tpm_close,
17347e946e7SWyllys Ingersoll 	nodev,		/* no strategy - nodev returns ENXIO */
17447e946e7SWyllys Ingersoll 	nodev,		/* no print */
17547e946e7SWyllys Ingersoll 	nodev,		/* no dump */
17647e946e7SWyllys Ingersoll 	tpm_read,
17747e946e7SWyllys Ingersoll 	tpm_write,
17847e946e7SWyllys Ingersoll 	nodev,		/* no ioctl */
17947e946e7SWyllys Ingersoll 	nodev,		/* no devmap */
18047e946e7SWyllys Ingersoll 	nodev,		/* no mmap */
18147e946e7SWyllys Ingersoll 	nodev,		/* no segmap */
18247e946e7SWyllys Ingersoll 	nochpoll,	/* returns ENXIO for non-pollable devices */
18347e946e7SWyllys Ingersoll 	ddi_prop_op,
18447e946e7SWyllys Ingersoll 	NULL,		/* streamtab struc */
18547e946e7SWyllys Ingersoll 	D_MP,		/* compatibility flags */
18647e946e7SWyllys Ingersoll 	CB_REV,		/* cb_ops revision number */
18747e946e7SWyllys Ingersoll 	nodev,		/* no aread */
18847e946e7SWyllys Ingersoll 	nodev		/* no awrite */
18947e946e7SWyllys Ingersoll };
19047e946e7SWyllys Ingersoll 
19147e946e7SWyllys Ingersoll /* dev_ops structure */
19247e946e7SWyllys Ingersoll static struct dev_ops tpm_dev_ops = {
19347e946e7SWyllys Ingersoll 	DEVO_REV,
19447e946e7SWyllys Ingersoll 	0,		/* reference count */
19547e946e7SWyllys Ingersoll 	tpm_getinfo,
19647e946e7SWyllys Ingersoll 	nulldev,	/* no identify - nulldev returns 0 */
19747e946e7SWyllys Ingersoll 	nulldev,
19847e946e7SWyllys Ingersoll 	tpm_attach,
19947e946e7SWyllys Ingersoll 	tpm_detach,
20047e946e7SWyllys Ingersoll 	nodev,		/* no reset - nodev returns ENXIO */
20147e946e7SWyllys Ingersoll 	&tpm_cb_ops,
20247e946e7SWyllys Ingersoll 	(struct bus_ops *)NULL,
20347e946e7SWyllys Ingersoll 	nodev,		/* no power */
20447e946e7SWyllys Ingersoll 	tpm_quiesce
20547e946e7SWyllys Ingersoll };
20647e946e7SWyllys Ingersoll 
20747e946e7SWyllys Ingersoll /* modldrv structure */
20847e946e7SWyllys Ingersoll static struct modldrv modldrv = {
20947e946e7SWyllys Ingersoll 	&mod_driverops,		/* Type: This is a driver */
21047e946e7SWyllys Ingersoll 	"TPM 1.2 driver",	/* Name of the module. */
21147e946e7SWyllys Ingersoll 	&tpm_dev_ops
21247e946e7SWyllys Ingersoll };
21347e946e7SWyllys Ingersoll 
21447e946e7SWyllys Ingersoll /* modlinkage structure */
21547e946e7SWyllys Ingersoll static struct modlinkage tpm_ml = {
21647e946e7SWyllys Ingersoll 	MODREV_1,
21747e946e7SWyllys Ingersoll 	&modldrv,
21847e946e7SWyllys Ingersoll 	NULL
21947e946e7SWyllys Ingersoll };
22047e946e7SWyllys Ingersoll 
2218d26100cSWyllys Ingersoll 
2228d26100cSWyllys Ingersoll #ifdef KCF_TPM_RNG_PROVIDER
2238d26100cSWyllys Ingersoll 
2248d26100cSWyllys Ingersoll #define	IDENT_TPMRNG	"TPM Random Number Generator"
2258d26100cSWyllys Ingersoll 
2268d26100cSWyllys Ingersoll #include <sys/crypto/common.h>
2278d26100cSWyllys Ingersoll #include <sys/crypto/impl.h>
2288d26100cSWyllys Ingersoll #include <sys/crypto/spi.h>
2298d26100cSWyllys Ingersoll /*
2308d26100cSWyllys Ingersoll  * CSPI information (entry points, provider info, etc.)
2318d26100cSWyllys Ingersoll  */
2328d26100cSWyllys Ingersoll static void tpmrng_provider_status(crypto_provider_handle_t, uint_t *);
2338d26100cSWyllys Ingersoll 
2348d26100cSWyllys Ingersoll static crypto_control_ops_t tpmrng_control_ops = {
2358d26100cSWyllys Ingersoll 	tpmrng_provider_status
2368d26100cSWyllys Ingersoll };
2378d26100cSWyllys Ingersoll 
2388d26100cSWyllys Ingersoll static int tpmrng_seed_random(crypto_provider_handle_t, crypto_session_id_t,
2398d26100cSWyllys Ingersoll     uchar_t *, size_t, uint_t, uint32_t, crypto_req_handle_t);
2408d26100cSWyllys Ingersoll 
2418d26100cSWyllys Ingersoll static int tpmrng_generate_random(crypto_provider_handle_t,
2428d26100cSWyllys Ingersoll     crypto_session_id_t, uchar_t *, size_t, crypto_req_handle_t);
2438d26100cSWyllys Ingersoll 
2448d26100cSWyllys Ingersoll static crypto_random_number_ops_t tpmrng_random_number_ops = {
2458d26100cSWyllys Ingersoll 	tpmrng_seed_random,
2468d26100cSWyllys Ingersoll 	tpmrng_generate_random
2478d26100cSWyllys Ingersoll };
2488d26100cSWyllys Ingersoll 
2498d26100cSWyllys Ingersoll static int tpmrng_ext_info(crypto_provider_handle_t,
2508d26100cSWyllys Ingersoll 	crypto_provider_ext_info_t *,
2518d26100cSWyllys Ingersoll 	crypto_req_handle_t);
2528d26100cSWyllys Ingersoll 
2538d26100cSWyllys Ingersoll static crypto_provider_management_ops_t tpmrng_extinfo_op = {
2548d26100cSWyllys Ingersoll 	tpmrng_ext_info,
2558d26100cSWyllys Ingersoll 	NULL,
2568d26100cSWyllys Ingersoll 	NULL,
2578d26100cSWyllys Ingersoll 	NULL
2588d26100cSWyllys Ingersoll };
2598d26100cSWyllys Ingersoll 
2608d26100cSWyllys Ingersoll static int tpmrng_register(tpm_state_t *);
2618d26100cSWyllys Ingersoll static int tpmrng_unregister(tpm_state_t *);
2628d26100cSWyllys Ingersoll 
2638d26100cSWyllys Ingersoll static crypto_ops_t tpmrng_crypto_ops = {
2648d26100cSWyllys Ingersoll 	&tpmrng_control_ops,
2658d26100cSWyllys Ingersoll 	NULL,
2668d26100cSWyllys Ingersoll 	NULL,
2678d26100cSWyllys Ingersoll 	NULL,
2688d26100cSWyllys Ingersoll 	NULL,
2698d26100cSWyllys Ingersoll 	NULL,
2708d26100cSWyllys Ingersoll 	NULL,
2718d26100cSWyllys Ingersoll 	NULL,
2728d26100cSWyllys Ingersoll 	&tpmrng_random_number_ops,
2738d26100cSWyllys Ingersoll 	NULL,
2748d26100cSWyllys Ingersoll 	NULL,
2758d26100cSWyllys Ingersoll 	NULL,
2768d26100cSWyllys Ingersoll 	&tpmrng_extinfo_op,
2778d26100cSWyllys Ingersoll 	NULL,
2788d26100cSWyllys Ingersoll 	NULL
2798d26100cSWyllys Ingersoll };
2808d26100cSWyllys Ingersoll 
2818d26100cSWyllys Ingersoll static crypto_provider_info_t tpmrng_prov_info = {
2828d26100cSWyllys Ingersoll 	CRYPTO_SPI_VERSION_2,
2838d26100cSWyllys Ingersoll 	"TPM Random Number Provider",
2848d26100cSWyllys Ingersoll 	CRYPTO_HW_PROVIDER,
2858d26100cSWyllys Ingersoll 	NULL,
2868d26100cSWyllys Ingersoll 	NULL,
2878d26100cSWyllys Ingersoll 	&tpmrng_crypto_ops,
2888d26100cSWyllys Ingersoll 	0,
2898d26100cSWyllys Ingersoll 	NULL,
2908d26100cSWyllys Ingersoll 	0,
2918d26100cSWyllys Ingersoll 	NULL
2928d26100cSWyllys Ingersoll };
2938d26100cSWyllys Ingersoll #endif /* KCF_TPM_RNG_PROVIDER */
2948d26100cSWyllys Ingersoll 
29547e946e7SWyllys Ingersoll static void *statep = NULL;
29647e946e7SWyllys Ingersoll 
29747e946e7SWyllys Ingersoll /*
2988d26100cSWyllys Ingersoll  * Inline code to get exclusive lock on the TPM device and to make sure
2998d26100cSWyllys Ingersoll  * the device is not suspended.  This grabs the primary TPM mutex (pm_mutex)
3008d26100cSWyllys Ingersoll  * and then checks the suspend status.  If suspended, it will wait until
3018d26100cSWyllys Ingersoll  * the device is "resumed" before releasing the pm_mutex and continuing.
3028d26100cSWyllys Ingersoll  */
3038d26100cSWyllys Ingersoll #define	TPM_EXCLUSIVE_LOCK(tpm)  { \
3048d26100cSWyllys Ingersoll 	mutex_enter(&tpm->pm_mutex); \
3058d26100cSWyllys Ingersoll 	while (tpm->suspended) \
3068d26100cSWyllys Ingersoll 		cv_wait(&tpm->suspend_cv, &tpm->pm_mutex); \
3078d26100cSWyllys Ingersoll 	mutex_exit(&tpm->pm_mutex); }
3088d26100cSWyllys Ingersoll 
3098d26100cSWyllys Ingersoll /*
3108d26100cSWyllys Ingersoll  * TPM accessor functions
3118d26100cSWyllys Ingersoll  */
3128d26100cSWyllys Ingersoll #ifdef sun4v
3138d26100cSWyllys Ingersoll 
3148d26100cSWyllys Ingersoll extern uint64_t
3158d26100cSWyllys Ingersoll hcall_tpm_get(uint64_t, uint64_t, uint64_t, uint64_t *);
3168d26100cSWyllys Ingersoll 
3178d26100cSWyllys Ingersoll extern uint64_t
3188d26100cSWyllys Ingersoll hcall_tpm_put(uint64_t, uint64_t, uint64_t, uint64_t);
3198d26100cSWyllys Ingersoll 
3208d26100cSWyllys Ingersoll static inline uint8_t
tpm_get8(tpm_state_t * tpm,unsigned long offset)3218d26100cSWyllys Ingersoll tpm_get8(tpm_state_t *tpm, unsigned long offset)
3228d26100cSWyllys Ingersoll {
3238d26100cSWyllys Ingersoll 	uint64_t value;
3248d26100cSWyllys Ingersoll 
3258d26100cSWyllys Ingersoll 	ASSERT(tpm != NULL);
3268d26100cSWyllys Ingersoll 	(void) hcall_tpm_get(tpm->locality, offset, sizeof (uint8_t), &value);
3278d26100cSWyllys Ingersoll 	return ((uint8_t)value);
3288d26100cSWyllys Ingersoll }
3298d26100cSWyllys Ingersoll 
3308d26100cSWyllys Ingersoll static inline uint32_t
tpm_get32(tpm_state_t * tpm,unsigned long offset)3318d26100cSWyllys Ingersoll tpm_get32(tpm_state_t *tpm, unsigned long offset)
3328d26100cSWyllys Ingersoll {
3338d26100cSWyllys Ingersoll 	uint64_t value;
3348d26100cSWyllys Ingersoll 
3358d26100cSWyllys Ingersoll 	ASSERT(tpm != NULL);
3368d26100cSWyllys Ingersoll 	(void) hcall_tpm_get(tpm->locality, offset, sizeof (uint32_t), &value);
3378d26100cSWyllys Ingersoll 	return ((uint32_t)value);
3388d26100cSWyllys Ingersoll }
3398d26100cSWyllys Ingersoll 
3408d26100cSWyllys Ingersoll static inline void
tpm_put8(tpm_state_t * tpm,unsigned long offset,uint8_t value)3418d26100cSWyllys Ingersoll tpm_put8(tpm_state_t *tpm, unsigned long offset, uint8_t value)
3428d26100cSWyllys Ingersoll {
3438d26100cSWyllys Ingersoll 	ASSERT(tpm != NULL);
3448d26100cSWyllys Ingersoll 	(void) hcall_tpm_put(tpm->locality, offset, sizeof (uint8_t), value);
3458d26100cSWyllys Ingersoll }
3468d26100cSWyllys Ingersoll 
3478d26100cSWyllys Ingersoll #else
3488d26100cSWyllys Ingersoll 
3498d26100cSWyllys Ingersoll static inline uint8_t
tpm_get8(tpm_state_t * tpm,unsigned long offset)3508d26100cSWyllys Ingersoll tpm_get8(tpm_state_t *tpm, unsigned long offset)
3518d26100cSWyllys Ingersoll {
3528d26100cSWyllys Ingersoll 	ASSERT(tpm != NULL);
3538d26100cSWyllys Ingersoll 
3548d26100cSWyllys Ingersoll 	return (ddi_get8(tpm->handle,
3558d26100cSWyllys Ingersoll 	    (uint8_t *)(TPM_LOCALITY_OFFSET(tpm->locality) |
3568d26100cSWyllys Ingersoll 	    (uintptr_t)tpm->addr + offset)));
3578d26100cSWyllys Ingersoll }
3588d26100cSWyllys Ingersoll 
3598d26100cSWyllys Ingersoll static inline uint32_t
tpm_get32(tpm_state_t * tpm,unsigned long offset)3608d26100cSWyllys Ingersoll tpm_get32(tpm_state_t *tpm, unsigned long offset)
3618d26100cSWyllys Ingersoll {
3628d26100cSWyllys Ingersoll 	ASSERT(tpm != NULL);
3638d26100cSWyllys Ingersoll 	return (ddi_get32(tpm->handle,
3648d26100cSWyllys Ingersoll 	    (uint32_t *)(TPM_LOCALITY_OFFSET(tpm->locality) |
3658d26100cSWyllys Ingersoll 	    (uintptr_t)tpm->addr + offset)));
3668d26100cSWyllys Ingersoll }
3678d26100cSWyllys Ingersoll 
3688d26100cSWyllys Ingersoll static inline void
tpm_put8(tpm_state_t * tpm,unsigned long offset,uint8_t value)3698d26100cSWyllys Ingersoll tpm_put8(tpm_state_t *tpm, unsigned long offset, uint8_t value)
3708d26100cSWyllys Ingersoll {
3718d26100cSWyllys Ingersoll 	ASSERT(tpm != NULL);
3728d26100cSWyllys Ingersoll 	ddi_put8(tpm->handle,
3738d26100cSWyllys Ingersoll 	    (uint8_t *)(TPM_LOCALITY_OFFSET(tpm->locality) |
3748d26100cSWyllys Ingersoll 	    (uintptr_t)tpm->addr + offset), value);
3758d26100cSWyllys Ingersoll }
3768d26100cSWyllys Ingersoll 
3778d26100cSWyllys Ingersoll #endif /* sun4v */
3788d26100cSWyllys Ingersoll 
3798d26100cSWyllys Ingersoll /*
38047e946e7SWyllys Ingersoll  * TPM commands to get the TPM's properties, e.g.,timeout
38147e946e7SWyllys Ingersoll  */
38247e946e7SWyllys Ingersoll /*ARGSUSED*/
38347e946e7SWyllys Ingersoll static int
tpm_quiesce(dev_info_t * dip)38447e946e7SWyllys Ingersoll tpm_quiesce(dev_info_t *dip)
38547e946e7SWyllys Ingersoll {
38647e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
38747e946e7SWyllys Ingersoll }
38847e946e7SWyllys Ingersoll 
38947e946e7SWyllys Ingersoll static uint32_t
load32(uchar_t * ptr,uint32_t offset)39047e946e7SWyllys Ingersoll load32(uchar_t *ptr, uint32_t offset)
39147e946e7SWyllys Ingersoll {
39247e946e7SWyllys Ingersoll 	uint32_t val;
39347e946e7SWyllys Ingersoll 	bcopy(ptr + offset, &val, sizeof (uint32_t));
39447e946e7SWyllys Ingersoll 
39547e946e7SWyllys Ingersoll 	return (ntohl(val));
39647e946e7SWyllys Ingersoll }
39747e946e7SWyllys Ingersoll 
39847e946e7SWyllys Ingersoll /*
39947e946e7SWyllys Ingersoll  * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
40047e946e7SWyllys Ingersoll  * with the subcommand TPM_CAP_PROP_TIS_TIMEOUT
40147e946e7SWyllys Ingersoll  * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
40247e946e7SWyllys Ingersoll  */
40347e946e7SWyllys Ingersoll static int
tpm_get_timeouts(tpm_state_t * tpm)40447e946e7SWyllys Ingersoll tpm_get_timeouts(tpm_state_t *tpm)
40547e946e7SWyllys Ingersoll {
40647e946e7SWyllys Ingersoll 	int ret;
40747e946e7SWyllys Ingersoll 	uint32_t timeout;   /* in milliseconds */
40847e946e7SWyllys Ingersoll 	uint32_t len;
40947e946e7SWyllys Ingersoll 
41047e946e7SWyllys Ingersoll 	/* The buffer size (30) needs room for 4 timeout values (uint32_t) */
41147e946e7SWyllys Ingersoll 	uint8_t buf[30] = {
41247e946e7SWyllys Ingersoll 		0, 193,		/* TPM_TAG_RQU_COMMAND */
41347e946e7SWyllys Ingersoll 		0, 0, 0, 22,	/* paramsize in bytes */
41447e946e7SWyllys Ingersoll 		0, 0, 0, 101,	/* TPM_ORD_GetCapability */
41547e946e7SWyllys Ingersoll 		0, 0, 0, 5,	/* TPM_CAP_Prop */
41647e946e7SWyllys Ingersoll 		0, 0, 0, 4,	/* SUB_CAP size in bytes */
41747e946e7SWyllys Ingersoll 		0, 0, 1, 21	/* TPM_CAP_PROP_TIS_TIMEOUT(0x115) */
41847e946e7SWyllys Ingersoll 	};
41947e946e7SWyllys Ingersoll 
42047e946e7SWyllys Ingersoll 	ASSERT(tpm != NULL);
42147e946e7SWyllys Ingersoll 
42247e946e7SWyllys Ingersoll 	ret = itpm_command(tpm, buf, sizeof (buf));
42347e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
424ccf625adSWyllys Ingersoll #ifdef DEBUG
425*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: itpm_command failed", __func__);
426ccf625adSWyllys Ingersoll #endif
42747e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
42847e946e7SWyllys Ingersoll 	}
42947e946e7SWyllys Ingersoll 
43047e946e7SWyllys Ingersoll 	/*
43147e946e7SWyllys Ingersoll 	 * Get the length of the returned buffer
43247e946e7SWyllys Ingersoll 	 * Make sure that there are 4 timeout values returned
43347e946e7SWyllys Ingersoll 	 * length of the capability response is stored in data[10-13]
43447e946e7SWyllys Ingersoll 	 * Also the TPM is in network byte order
43547e946e7SWyllys Ingersoll 	 */
43647e946e7SWyllys Ingersoll 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
43747e946e7SWyllys Ingersoll 	if (len != 4 * sizeof (uint32_t)) {
438ccf625adSWyllys Ingersoll #ifdef DEBUG
439ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: capability response size should be %d"
440ccf625adSWyllys Ingersoll 		    "instead len = %d",
441*e9fe7b35SJason King 		    __func__, (int)(4 * sizeof (uint32_t)), (int)len);
442ccf625adSWyllys Ingersoll #endif
44347e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
44447e946e7SWyllys Ingersoll 	}
44547e946e7SWyllys Ingersoll 
44647e946e7SWyllys Ingersoll 	/* Get the four timeout's: a,b,c,d (they are 4 bytes long each) */
44747e946e7SWyllys Ingersoll 	timeout = load32(buf, TPM_CAP_TIMEOUT_A_OFFSET);
44847e946e7SWyllys Ingersoll 	if (timeout == 0) {
44947e946e7SWyllys Ingersoll 		timeout = DEFAULT_TIMEOUT_A;
45047e946e7SWyllys Ingersoll 	} else if (timeout < TEN_MILLISECONDS) {
45147e946e7SWyllys Ingersoll 		/* timeout is in millisecond range (should be microseconds) */
45247e946e7SWyllys Ingersoll 		timeout *= 1000;
45347e946e7SWyllys Ingersoll 	}
45447e946e7SWyllys Ingersoll 	tpm->timeout_a = drv_usectohz(timeout);
45547e946e7SWyllys Ingersoll 
45647e946e7SWyllys Ingersoll 	timeout = load32(buf, TPM_CAP_TIMEOUT_B_OFFSET);
45747e946e7SWyllys Ingersoll 	if (timeout == 0) {
45847e946e7SWyllys Ingersoll 		timeout = DEFAULT_TIMEOUT_B;
45947e946e7SWyllys Ingersoll 	} else if (timeout < TEN_MILLISECONDS) {
46047e946e7SWyllys Ingersoll 		/* timeout is in millisecond range (should be microseconds) */
46147e946e7SWyllys Ingersoll 		timeout *= 1000;
46247e946e7SWyllys Ingersoll 	}
46347e946e7SWyllys Ingersoll 	tpm->timeout_b = drv_usectohz(timeout);
46447e946e7SWyllys Ingersoll 
46547e946e7SWyllys Ingersoll 	timeout = load32(buf, TPM_CAP_TIMEOUT_C_OFFSET);
46647e946e7SWyllys Ingersoll 	if (timeout == 0) {
46747e946e7SWyllys Ingersoll 		timeout = DEFAULT_TIMEOUT_C;
46847e946e7SWyllys Ingersoll 	} else if (timeout < TEN_MILLISECONDS) {
46947e946e7SWyllys Ingersoll 		/* timeout is in millisecond range (should be microseconds) */
47047e946e7SWyllys Ingersoll 		timeout *= 1000;
47147e946e7SWyllys Ingersoll 	}
47247e946e7SWyllys Ingersoll 	tpm->timeout_c = drv_usectohz(timeout);
47347e946e7SWyllys Ingersoll 
47447e946e7SWyllys Ingersoll 	timeout = load32(buf, TPM_CAP_TIMEOUT_D_OFFSET);
47547e946e7SWyllys Ingersoll 	if (timeout == 0) {
47647e946e7SWyllys Ingersoll 		timeout = DEFAULT_TIMEOUT_D;
47747e946e7SWyllys Ingersoll 	} else if (timeout < TEN_MILLISECONDS) {
47847e946e7SWyllys Ingersoll 		/* timeout is in millisecond range (should be microseconds) */
47947e946e7SWyllys Ingersoll 		timeout *= 1000;
48047e946e7SWyllys Ingersoll 	}
48147e946e7SWyllys Ingersoll 	tpm->timeout_d = drv_usectohz(timeout);
48247e946e7SWyllys Ingersoll 
48347e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
48447e946e7SWyllys Ingersoll }
48547e946e7SWyllys Ingersoll 
48647e946e7SWyllys Ingersoll /*
48747e946e7SWyllys Ingersoll  * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
48847e946e7SWyllys Ingersoll  * with the subcommand TPM_CAP_PROP_TIS_DURATION
48947e946e7SWyllys Ingersoll  * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
49047e946e7SWyllys Ingersoll  */
49147e946e7SWyllys Ingersoll static int
tpm_get_duration(tpm_state_t * tpm)492*e9fe7b35SJason King tpm_get_duration(tpm_state_t *tpm)
493*e9fe7b35SJason King {
49447e946e7SWyllys Ingersoll 	int ret;
49547e946e7SWyllys Ingersoll 	uint32_t duration;
49647e946e7SWyllys Ingersoll 	uint32_t len;
49747e946e7SWyllys Ingersoll 	uint8_t buf[30] = {
49847e946e7SWyllys Ingersoll 		0, 193,		/* TPM_TAG_RQU_COMMAND */
49947e946e7SWyllys Ingersoll 		0, 0, 0, 22,	/* paramsize in bytes */
50047e946e7SWyllys Ingersoll 		0, 0, 0, 101,	/* TPM_ORD_GetCapability */
50147e946e7SWyllys Ingersoll 		0, 0, 0, 5,	/* TPM_CAP_Prop */
50247e946e7SWyllys Ingersoll 		0, 0, 0, 4,	/* SUB_CAP size in bytes */
50347e946e7SWyllys Ingersoll 		0, 0, 1, 32	/* TPM_CAP_PROP_TIS_DURATION(0x120) */
50447e946e7SWyllys Ingersoll 	};
50547e946e7SWyllys Ingersoll 
50647e946e7SWyllys Ingersoll 	ASSERT(tpm != NULL);
50747e946e7SWyllys Ingersoll 
50847e946e7SWyllys Ingersoll 	ret = itpm_command(tpm, buf, sizeof (buf));
50947e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
510ccf625adSWyllys Ingersoll #ifdef DEBUG
511ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: itpm_command failed with ret code: 0x%x",
512*e9fe7b35SJason King 		    __func__, ret);
513ccf625adSWyllys Ingersoll #endif
51447e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
51547e946e7SWyllys Ingersoll 	}
51647e946e7SWyllys Ingersoll 
51747e946e7SWyllys Ingersoll 	/*
51847e946e7SWyllys Ingersoll 	 * Get the length of the returned buffer
51947e946e7SWyllys Ingersoll 	 * Make sure that there are 3 duration values (S,M,L: in that order)
52047e946e7SWyllys Ingersoll 	 * length of the capability response is stored in data[10-13]
52147e946e7SWyllys Ingersoll 	 * Also the TPM is in network byte order
52247e946e7SWyllys Ingersoll 	 */
52347e946e7SWyllys Ingersoll 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
52447e946e7SWyllys Ingersoll 	if (len != 3 * sizeof (uint32_t)) {
525ccf625adSWyllys Ingersoll #ifdef DEBUG
526ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: capability response should be %d, "
52747e946e7SWyllys Ingersoll 		    "instead, it's %d",
528*e9fe7b35SJason King 		    __func__, (int)(3 * sizeof (uint32_t)), (int)len);
529ccf625adSWyllys Ingersoll #endif
53047e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
53147e946e7SWyllys Ingersoll 	}
53247e946e7SWyllys Ingersoll 
53347e946e7SWyllys Ingersoll 	duration = load32(buf, TPM_CAP_DUR_SHORT_OFFSET);
53447e946e7SWyllys Ingersoll 	if (duration == 0) {
53547e946e7SWyllys Ingersoll 		duration = DEFAULT_SHORT_DURATION;
53647e946e7SWyllys Ingersoll 	} else if (duration < TEN_MILLISECONDS) {
53747e946e7SWyllys Ingersoll 		duration *= 1000;
53847e946e7SWyllys Ingersoll 	}
53947e946e7SWyllys Ingersoll 	tpm->duration[TPM_SHORT] = drv_usectohz(duration);
54047e946e7SWyllys Ingersoll 
54147e946e7SWyllys Ingersoll 	duration = load32(buf, TPM_CAP_DUR_MEDIUM_OFFSET);
54247e946e7SWyllys Ingersoll 	if (duration == 0) {
54347e946e7SWyllys Ingersoll 		duration = DEFAULT_MEDIUM_DURATION;
54447e946e7SWyllys Ingersoll 	} else if (duration < TEN_MILLISECONDS) {
54547e946e7SWyllys Ingersoll 		duration *= 1000;
54647e946e7SWyllys Ingersoll 	}
54747e946e7SWyllys Ingersoll 	tpm->duration[TPM_MEDIUM] = drv_usectohz(duration);
54847e946e7SWyllys Ingersoll 
54947e946e7SWyllys Ingersoll 	duration = load32(buf, TPM_CAP_DUR_LONG_OFFSET);
55047e946e7SWyllys Ingersoll 	if (duration == 0) {
55147e946e7SWyllys Ingersoll 		duration = DEFAULT_LONG_DURATION;
55247e946e7SWyllys Ingersoll 	} else if (duration < FOUR_HUNDRED_MILLISECONDS) {
55347e946e7SWyllys Ingersoll 		duration *= 1000;
55447e946e7SWyllys Ingersoll 	}
55547e946e7SWyllys Ingersoll 	tpm->duration[TPM_LONG] = drv_usectohz(duration);
55647e946e7SWyllys Ingersoll 
55747e946e7SWyllys Ingersoll 	/* Just make the undefined duration be the same as the LONG */
55847e946e7SWyllys Ingersoll 	tpm->duration[TPM_UNDEFINED] = tpm->duration[TPM_LONG];
55947e946e7SWyllys Ingersoll 
56047e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
56147e946e7SWyllys Ingersoll }
56247e946e7SWyllys Ingersoll 
56347e946e7SWyllys Ingersoll /*
56447e946e7SWyllys Ingersoll  * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
56547e946e7SWyllys Ingersoll  * with the subcommand TPM_CAP_PROP_TIS_DURATION
56647e946e7SWyllys Ingersoll  * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
56747e946e7SWyllys Ingersoll  */
56847e946e7SWyllys Ingersoll static int
tpm_get_version(tpm_state_t * tpm)569*e9fe7b35SJason King tpm_get_version(tpm_state_t *tpm)
570*e9fe7b35SJason King {
57147e946e7SWyllys Ingersoll 	int ret;
57247e946e7SWyllys Ingersoll 	uint32_t len;
57347e946e7SWyllys Ingersoll 	char vendorId[5];
57447e946e7SWyllys Ingersoll 	/* If this buf is too small, the "vendor specific" data won't fit */
57547e946e7SWyllys Ingersoll 	uint8_t buf[64] = {
57647e946e7SWyllys Ingersoll 		0, 193,		/* TPM_TAG_RQU_COMMAND */
57747e946e7SWyllys Ingersoll 		0, 0, 0, 18,	/* paramsize in bytes */
57847e946e7SWyllys Ingersoll 		0, 0, 0, 101,	/* TPM_ORD_GetCapability */
57947e946e7SWyllys Ingersoll 		0, 0, 0, 0x1A,	/* TPM_CAP_VERSION_VAL */
58047e946e7SWyllys Ingersoll 		0, 0, 0, 0,	/* SUB_CAP size in bytes */
58147e946e7SWyllys Ingersoll 	};
58247e946e7SWyllys Ingersoll 
58347e946e7SWyllys Ingersoll 	ASSERT(tpm != NULL);
58447e946e7SWyllys Ingersoll 
58547e946e7SWyllys Ingersoll 	ret = itpm_command(tpm, buf, sizeof (buf));
58647e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
587ccf625adSWyllys Ingersoll #ifdef DEBUG
588ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: itpm_command failed with ret code: 0x%x",
589*e9fe7b35SJason King 		    __func__, ret);
590ccf625adSWyllys Ingersoll #endif
59147e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
59247e946e7SWyllys Ingersoll 	}
59347e946e7SWyllys Ingersoll 
59447e946e7SWyllys Ingersoll 	/*
59547e946e7SWyllys Ingersoll 	 * Get the length of the returned buffer.
59647e946e7SWyllys Ingersoll 	 */
59747e946e7SWyllys Ingersoll 	len = load32(buf, TPM_CAP_RESPSIZE_OFFSET);
59847e946e7SWyllys Ingersoll 	if (len < TPM_CAP_VERSION_INFO_SIZE) {
599ccf625adSWyllys Ingersoll #ifdef DEBUG
600ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: capability response should be greater"
60147e946e7SWyllys Ingersoll 		    " than %d, instead, it's %d",
602*e9fe7b35SJason King 		    __func__, TPM_CAP_VERSION_INFO_SIZE, len);
603ccf625adSWyllys Ingersoll #endif
60447e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
60547e946e7SWyllys Ingersoll 	}
60647e946e7SWyllys Ingersoll 
60747e946e7SWyllys Ingersoll 	bcopy(buf + TPM_CAP_VERSION_INFO_OFFSET, &tpm->vers_info,
60847e946e7SWyllys Ingersoll 	    TPM_CAP_VERSION_INFO_SIZE);
60947e946e7SWyllys Ingersoll 
61047e946e7SWyllys Ingersoll 	bcopy(tpm->vers_info.tpmVendorID, vendorId,
61147e946e7SWyllys Ingersoll 	    sizeof (tpm->vers_info.tpmVendorID));
61247e946e7SWyllys Ingersoll 	vendorId[4] = '\0';
61347e946e7SWyllys Ingersoll 
61447e946e7SWyllys Ingersoll 	cmn_err(CE_NOTE, "!TPM found: Ver %d.%d, Rev %d.%d, "
61547e946e7SWyllys Ingersoll 	    "SpecLevel %d, errataRev %d, VendorId '%s'",
61647e946e7SWyllys Ingersoll 	    tpm->vers_info.version.major,	/* Version */
61747e946e7SWyllys Ingersoll 	    tpm->vers_info.version.minor,
61847e946e7SWyllys Ingersoll 	    tpm->vers_info.version.revMajor,	/* Revision */
61947e946e7SWyllys Ingersoll 	    tpm->vers_info.version.revMinor,
62047e946e7SWyllys Ingersoll 	    (int)ntohs(tpm->vers_info.specLevel),
62147e946e7SWyllys Ingersoll 	    tpm->vers_info.errataRev,
62247e946e7SWyllys Ingersoll 	    vendorId);
62347e946e7SWyllys Ingersoll 
62447e946e7SWyllys Ingersoll 	/*
62547e946e7SWyllys Ingersoll 	 * This driver only supports TPM Version 1.2
62647e946e7SWyllys Ingersoll 	 */
62747e946e7SWyllys Ingersoll 	if (tpm->vers_info.version.major != 1 &&
62847e946e7SWyllys Ingersoll 	    tpm->vers_info.version.minor != 2) {
629ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: Unsupported TPM version (%d.%d)",
630*e9fe7b35SJason King 		    __func__,
63147e946e7SWyllys Ingersoll 		    tpm->vers_info.version.major,		/* Version */
63247e946e7SWyllys Ingersoll 		    tpm->vers_info.version.minor);
63347e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
63447e946e7SWyllys Ingersoll 	}
63547e946e7SWyllys Ingersoll 
63647e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
63747e946e7SWyllys Ingersoll }
63847e946e7SWyllys Ingersoll 
63947e946e7SWyllys Ingersoll /*
64047e946e7SWyllys Ingersoll  * To prevent the TPM from complaining that certain functions are not tested
64147e946e7SWyllys Ingersoll  * we run this command when the driver attaches.
64247e946e7SWyllys Ingersoll  * For details see Section 4.2 of TPM Main Part 3 Command Specification
64347e946e7SWyllys Ingersoll  */
64447e946e7SWyllys Ingersoll static int
tpm_continue_selftest(tpm_state_t * tpm)645*e9fe7b35SJason King tpm_continue_selftest(tpm_state_t *tpm)
646*e9fe7b35SJason King {
64747e946e7SWyllys Ingersoll 	int ret;
64847e946e7SWyllys Ingersoll 	uint8_t buf[10] = {
64947e946e7SWyllys Ingersoll 		0, 193,		/* TPM_TAG_RQU COMMAND */
65047e946e7SWyllys Ingersoll 		0, 0, 0, 10,	/* paramsize in bytes */
65147e946e7SWyllys Ingersoll 		0, 0, 0, 83	/* TPM_ORD_ContinueSelfTest */
65247e946e7SWyllys Ingersoll 	};
65347e946e7SWyllys Ingersoll 
65447e946e7SWyllys Ingersoll 	/* Need a longer timeout */
65547e946e7SWyllys Ingersoll 	ret = itpm_command(tpm, buf, sizeof (buf));
65647e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
657ccf625adSWyllys Ingersoll #ifdef DEBUG
658*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: itpm_command failed", __func__);
659ccf625adSWyllys Ingersoll #endif
66047e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
66147e946e7SWyllys Ingersoll 	}
66247e946e7SWyllys Ingersoll 
66347e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
66447e946e7SWyllys Ingersoll }
66547e946e7SWyllys Ingersoll /*
66647e946e7SWyllys Ingersoll  * Auxilary Functions
66747e946e7SWyllys Ingersoll  */
66847e946e7SWyllys Ingersoll 
66947e946e7SWyllys Ingersoll /*
67047e946e7SWyllys Ingersoll  * Find out how long we should wait for the TPM command to complete a command
67147e946e7SWyllys Ingersoll  */
67247e946e7SWyllys Ingersoll static clock_t
tpm_get_ordinal_duration(tpm_state_t * tpm,uint8_t ordinal)67347e946e7SWyllys Ingersoll tpm_get_ordinal_duration(tpm_state_t *tpm, uint8_t ordinal)
67447e946e7SWyllys Ingersoll {
67547e946e7SWyllys Ingersoll 	uint8_t index;
67647e946e7SWyllys Ingersoll 
67747e946e7SWyllys Ingersoll 	ASSERT(tpm != NULL);
67847e946e7SWyllys Ingersoll 
67947e946e7SWyllys Ingersoll 	/* Default and failure case for IFX */
68047e946e7SWyllys Ingersoll 	/* Is it a TSC_ORDINAL? */
68147e946e7SWyllys Ingersoll 	if (ordinal & TSC_ORDINAL_MASK) {
682*e9fe7b35SJason King 		if (ordinal >= TSC_ORDINAL_MAX) {
683ccf625adSWyllys Ingersoll #ifdef DEBUG
68447e946e7SWyllys Ingersoll 			cmn_err(CE_WARN,
685ccf625adSWyllys Ingersoll 			    "!%s: tsc ordinal: %d exceeds MAX: %d",
686*e9fe7b35SJason King 			    __func__, ordinal, TSC_ORDINAL_MAX);
687ccf625adSWyllys Ingersoll #endif
68847e946e7SWyllys Ingersoll 			return (0);
68947e946e7SWyllys Ingersoll 		}
69047e946e7SWyllys Ingersoll 		index = tsc_ords_duration[ordinal];
69147e946e7SWyllys Ingersoll 	} else {
692*e9fe7b35SJason King 		if (ordinal >= TPM_ORDINAL_MAX) {
693ccf625adSWyllys Ingersoll #ifdef DEBUG
69447e946e7SWyllys Ingersoll 			cmn_err(CE_WARN,
695ccf625adSWyllys Ingersoll 			    "!%s: ordinal %d exceeds MAX: %d",
696*e9fe7b35SJason King 			    __func__, ordinal, TPM_ORDINAL_MAX);
697ccf625adSWyllys Ingersoll #endif
69847e946e7SWyllys Ingersoll 			return (0);
69947e946e7SWyllys Ingersoll 		}
70047e946e7SWyllys Ingersoll 		index = tpm_ords_duration[ordinal];
70147e946e7SWyllys Ingersoll 	}
70247e946e7SWyllys Ingersoll 
70347e946e7SWyllys Ingersoll 	if (index > TPM_DURATION_MAX_IDX) {
704ccf625adSWyllys Ingersoll #ifdef DEBUG
705ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: duration index '%d' is out of bounds",
706*e9fe7b35SJason King 		    __func__, index);
707ccf625adSWyllys Ingersoll #endif
70847e946e7SWyllys Ingersoll 		return (0);
70947e946e7SWyllys Ingersoll 	}
71047e946e7SWyllys Ingersoll 	return (tpm->duration[index]);
71147e946e7SWyllys Ingersoll }
71247e946e7SWyllys Ingersoll 
71347e946e7SWyllys Ingersoll /*
71447e946e7SWyllys Ingersoll  * Internal TPM Transmit Function:
71547e946e7SWyllys Ingersoll  * Calls implementation specific sendto and receive
71647e946e7SWyllys Ingersoll  * The code assumes that the buffer is in network byte order
71747e946e7SWyllys Ingersoll  */
71847e946e7SWyllys Ingersoll static int
itpm_command(tpm_state_t * tpm,uint8_t * buf,size_t bufsiz)71947e946e7SWyllys Ingersoll itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz)
72047e946e7SWyllys Ingersoll {
72147e946e7SWyllys Ingersoll 	int ret;
72247e946e7SWyllys Ingersoll 	uint32_t count;
72347e946e7SWyllys Ingersoll 
72447e946e7SWyllys Ingersoll 	ASSERT(tpm != NULL && buf != NULL);
72547e946e7SWyllys Ingersoll 
72647e946e7SWyllys Ingersoll 	/* The byte order is network byte order so convert it */
72747e946e7SWyllys Ingersoll 	count = load32(buf, TPM_PARAMSIZE_OFFSET);
72847e946e7SWyllys Ingersoll 
729ccf625adSWyllys Ingersoll 	if (count == 0 || (count > bufsiz)) {
730ccf625adSWyllys Ingersoll #ifdef DEBUG
731ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: invalid byte count value "
732*e9fe7b35SJason King 		    "(%d > bufsiz %d)", __func__, (int)count, (int)bufsiz);
733ccf625adSWyllys Ingersoll #endif
73447e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
73547e946e7SWyllys Ingersoll 	}
73647e946e7SWyllys Ingersoll 
73747e946e7SWyllys Ingersoll 	/* Send the command */
73847e946e7SWyllys Ingersoll 	ret = tis_send_data(tpm, buf, count);
73947e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
740ccf625adSWyllys Ingersoll #ifdef DEBUG
741ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: tis_send_data failed with error %x",
742*e9fe7b35SJason King 		    __func__, ret);
743ccf625adSWyllys Ingersoll #endif
74447e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
74547e946e7SWyllys Ingersoll 	}
74647e946e7SWyllys Ingersoll 
74747e946e7SWyllys Ingersoll 	/*
74847e946e7SWyllys Ingersoll 	 * Now receive the data from the tpm
74947e946e7SWyllys Ingersoll 	 * Should at least receive "the common" 10 bytes (TPM_HEADER_SIZE)
75047e946e7SWyllys Ingersoll 	 */
75147e946e7SWyllys Ingersoll 	ret = tis_recv_data(tpm, buf, bufsiz);
75247e946e7SWyllys Ingersoll 	if (ret < TPM_HEADER_SIZE) {
753ccf625adSWyllys Ingersoll #ifdef DEBUG
754*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: tis_recv_data failed", __func__);
755ccf625adSWyllys Ingersoll #endif
75647e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
75747e946e7SWyllys Ingersoll 	}
75847e946e7SWyllys Ingersoll 
75947e946e7SWyllys Ingersoll 	/* Check the return code */
76047e946e7SWyllys Ingersoll 	ret = load32(buf, TPM_RETURN_OFFSET);
76147e946e7SWyllys Ingersoll 	if (ret != TPM_SUCCESS) {
7628d26100cSWyllys Ingersoll 		if (ret == TPM_E_DEACTIVATED)
763*e9fe7b35SJason King 			cmn_err(CE_WARN, "!%s: TPM is deactivated", __func__);
7648d26100cSWyllys Ingersoll 		else if (ret == TPM_E_DISABLED)
765*e9fe7b35SJason King 			cmn_err(CE_WARN, "!%s: TPM is disabled", __func__);
7668d26100cSWyllys Ingersoll 		else
767ccf625adSWyllys Ingersoll 			cmn_err(CE_WARN, "!%s: TPM error code 0x%0x",
768*e9fe7b35SJason King 			    __func__, ret);
76947e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
77047e946e7SWyllys Ingersoll 	}
77147e946e7SWyllys Ingersoll 
77247e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
77347e946e7SWyllys Ingersoll }
77447e946e7SWyllys Ingersoll 
77547e946e7SWyllys Ingersoll /*
77647e946e7SWyllys Ingersoll  * Whenever the driver wants to write to the DATA_IO register, it must need
77747e946e7SWyllys Ingersoll  * to figure out the burstcount.  This is the amount of bytes it can write
77847e946e7SWyllys Ingersoll  * before having to wait for long LPC bus cycle
77947e946e7SWyllys Ingersoll  *
78047e946e7SWyllys Ingersoll  * Returns: 0 if error, burst count if sucess
78147e946e7SWyllys Ingersoll  */
78247e946e7SWyllys Ingersoll static uint16_t
tpm_get_burstcount(tpm_state_t * tpm)783*e9fe7b35SJason King tpm_get_burstcount(tpm_state_t *tpm)
784*e9fe7b35SJason King {
78547e946e7SWyllys Ingersoll 	clock_t stop;
78647e946e7SWyllys Ingersoll 	uint16_t burstcnt;
78747e946e7SWyllys Ingersoll 
78847e946e7SWyllys Ingersoll 	ASSERT(tpm != NULL);
78947e946e7SWyllys Ingersoll 
79047e946e7SWyllys Ingersoll 	/*
79147e946e7SWyllys Ingersoll 	 * Spec says timeout should be TIMEOUT_D
79247e946e7SWyllys Ingersoll 	 * burst count is TPM_STS bits 8..23
79347e946e7SWyllys Ingersoll 	 */
79447e946e7SWyllys Ingersoll 	stop = ddi_get_lbolt() + tpm->timeout_d;
79547e946e7SWyllys Ingersoll 	do {
79647e946e7SWyllys Ingersoll 		/*
79747e946e7SWyllys Ingersoll 		 * burstcnt is stored as a little endian value
79847e946e7SWyllys Ingersoll 		 * 'ntohs' doesn't work since the value is not word-aligned
79947e946e7SWyllys Ingersoll 		 */
8008d26100cSWyllys Ingersoll 		burstcnt = tpm_get8(tpm, TPM_STS + 1);
8018d26100cSWyllys Ingersoll 		burstcnt += tpm_get8(tpm, TPM_STS + 2) << 8;
80247e946e7SWyllys Ingersoll 
80347e946e7SWyllys Ingersoll 		if (burstcnt)
80447e946e7SWyllys Ingersoll 			return (burstcnt);
80547e946e7SWyllys Ingersoll 
80647e946e7SWyllys Ingersoll 		delay(tpm->timeout_poll);
80747e946e7SWyllys Ingersoll 	} while (ddi_get_lbolt() < stop);
80847e946e7SWyllys Ingersoll 
80947e946e7SWyllys Ingersoll 	return (0);
81047e946e7SWyllys Ingersoll }
81147e946e7SWyllys Ingersoll 
81247e946e7SWyllys Ingersoll /*
81347e946e7SWyllys Ingersoll  * Writing 1 to TPM_STS_CMD_READY bit in TPM_STS will do the following:
81447e946e7SWyllys Ingersoll  * 1. The TPM will clears IO buffers if any
81547e946e7SWyllys Ingersoll  * 2. The TPM will enters either Idle or Ready state within TIMEOUT_B
81647e946e7SWyllys Ingersoll  * (checked in the calling function)
81747e946e7SWyllys Ingersoll  */
81847e946e7SWyllys Ingersoll static void
tpm_set_ready(tpm_state_t * tpm)819*e9fe7b35SJason King tpm_set_ready(tpm_state_t *tpm)
820*e9fe7b35SJason King {
8218d26100cSWyllys Ingersoll 	tpm_put8(tpm, TPM_STS, TPM_STS_CMD_READY);
82247e946e7SWyllys Ingersoll }
82347e946e7SWyllys Ingersoll 
82447e946e7SWyllys Ingersoll static int
receive_data(tpm_state_t * tpm,uint8_t * buf,size_t bufsiz)825*e9fe7b35SJason King receive_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz)
826*e9fe7b35SJason King {
82747e946e7SWyllys Ingersoll 	int size = 0;
82847e946e7SWyllys Ingersoll 	int retried = 0;
82947e946e7SWyllys Ingersoll 	uint8_t stsbits;
83047e946e7SWyllys Ingersoll 
83147e946e7SWyllys Ingersoll 	/* A number of consecutive bytes that can be written to TPM */
83247e946e7SWyllys Ingersoll 	uint16_t burstcnt;
83347e946e7SWyllys Ingersoll 
83447e946e7SWyllys Ingersoll 	ASSERT(tpm != NULL && buf != NULL);
83547e946e7SWyllys Ingersoll retry:
836*e9fe7b35SJason King 	while (size < bufsiz && (tpm_wait_for_stat(tpm,
83747e946e7SWyllys Ingersoll 	    (TPM_STS_DATA_AVAIL|TPM_STS_VALID),
8388d26100cSWyllys Ingersoll 	    tpm->timeout_c) == DDI_SUCCESS)) {
83947e946e7SWyllys Ingersoll 		/*
84047e946e7SWyllys Ingersoll 		 * Burstcount should be available within TIMEOUT_D
84147e946e7SWyllys Ingersoll 		 * after STS is set to valid
84247e946e7SWyllys Ingersoll 		 * burstcount is dynamic, so have to get it each time
84347e946e7SWyllys Ingersoll 		 */
84447e946e7SWyllys Ingersoll 		burstcnt = tpm_get_burstcount(tpm);
84547e946e7SWyllys Ingersoll 		for (; burstcnt > 0 && size < bufsiz; burstcnt--) {
8468d26100cSWyllys Ingersoll 			buf[size++] = tpm_get8(tpm, TPM_DATA_FIFO);
84747e946e7SWyllys Ingersoll 		}
84847e946e7SWyllys Ingersoll 	}
84947e946e7SWyllys Ingersoll 	stsbits = tis_get_status(tpm);
85047e946e7SWyllys Ingersoll 	/* check to see if we need to retry (just once) */
85147e946e7SWyllys Ingersoll 	if (size < bufsiz && !(stsbits & TPM_STS_DATA_AVAIL) && retried == 0) {
85247e946e7SWyllys Ingersoll 		/* issue responseRetry (TIS 1.2 pg 54) */
8538d26100cSWyllys Ingersoll 		tpm_put8(tpm, TPM_STS, TPM_STS_RESPONSE_RETRY);
85447e946e7SWyllys Ingersoll 		/* update the retry counter so we only retry once */
85547e946e7SWyllys Ingersoll 		retried++;
85647e946e7SWyllys Ingersoll 		/* reset the size to 0 and reread the entire response */
85747e946e7SWyllys Ingersoll 		size = 0;
85847e946e7SWyllys Ingersoll 		goto retry;
85947e946e7SWyllys Ingersoll 	}
86047e946e7SWyllys Ingersoll 	return (size);
86147e946e7SWyllys Ingersoll }
86247e946e7SWyllys Ingersoll 
86347e946e7SWyllys Ingersoll /* Receive the data from the TPM */
86447e946e7SWyllys Ingersoll static int
tis_recv_data(tpm_state_t * tpm,uint8_t * buf,size_t bufsiz)865*e9fe7b35SJason King tis_recv_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz)
866*e9fe7b35SJason King {
86747e946e7SWyllys Ingersoll 	int ret;
86847e946e7SWyllys Ingersoll 	int size = 0;
86947e946e7SWyllys Ingersoll 	uint32_t expected, status;
87047e946e7SWyllys Ingersoll 	uint32_t cmdresult;
87147e946e7SWyllys Ingersoll 
87247e946e7SWyllys Ingersoll 	ASSERT(tpm != NULL && buf != NULL);
87347e946e7SWyllys Ingersoll 
87447e946e7SWyllys Ingersoll 	if (bufsiz < TPM_HEADER_SIZE) {
87547e946e7SWyllys Ingersoll 		/* There should be at least tag, paramsize, return code */
876ccf625adSWyllys Ingersoll #ifdef DEBUG
877ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: received data should contain at least "
87847e946e7SWyllys Ingersoll 		    "the header which is %d bytes long",
879*e9fe7b35SJason King 		    __func__, TPM_HEADER_SIZE);
880ccf625adSWyllys Ingersoll #endif
88147e946e7SWyllys Ingersoll 		goto OUT;
88247e946e7SWyllys Ingersoll 	}
88347e946e7SWyllys Ingersoll 
88447e946e7SWyllys Ingersoll 	/* Read tag(2 bytes), paramsize(4), and result(4) */
88547e946e7SWyllys Ingersoll 	size = receive_data(tpm, buf, TPM_HEADER_SIZE);
88647e946e7SWyllys Ingersoll 	if (size < TPM_HEADER_SIZE) {
887ccf625adSWyllys Ingersoll #ifdef DEBUG
888ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: recv TPM_HEADER failed, size = %d",
889*e9fe7b35SJason King 		    __func__, size);
890ccf625adSWyllys Ingersoll #endif
89147e946e7SWyllys Ingersoll 		goto OUT;
89247e946e7SWyllys Ingersoll 	}
89347e946e7SWyllys Ingersoll 
89447e946e7SWyllys Ingersoll 	cmdresult = load32(buf, TPM_RETURN_OFFSET);
89547e946e7SWyllys Ingersoll 
89647e946e7SWyllys Ingersoll 	/* Get 'paramsize'(4 bytes)--it includes tag and paramsize */
89747e946e7SWyllys Ingersoll 	expected = load32(buf, TPM_PARAMSIZE_OFFSET);
89847e946e7SWyllys Ingersoll 	if (expected > bufsiz) {
899ccf625adSWyllys Ingersoll #ifdef DEBUG
900ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: paramSize is bigger "
90147e946e7SWyllys Ingersoll 		    "than the requested size: paramSize=%d bufsiz=%d result=%d",
902*e9fe7b35SJason King 		    __func__, (int)expected, (int)bufsiz, cmdresult);
903ccf625adSWyllys Ingersoll #endif
90447e946e7SWyllys Ingersoll 		goto OUT;
90547e946e7SWyllys Ingersoll 	}
90647e946e7SWyllys Ingersoll 
90747e946e7SWyllys Ingersoll 	/* Read in the rest of the data from the TPM */
90847e946e7SWyllys Ingersoll 	size += receive_data(tpm, (uint8_t *)&buf[TPM_HEADER_SIZE],
90947e946e7SWyllys Ingersoll 	    expected - TPM_HEADER_SIZE);
91047e946e7SWyllys Ingersoll 	if (size < expected) {
911ccf625adSWyllys Ingersoll #ifdef DEBUG
912ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: received data length (%d) "
913*e9fe7b35SJason King 		    "is less than expected (%d)", __func__, size, expected);
914ccf625adSWyllys Ingersoll #endif
91547e946e7SWyllys Ingersoll 		goto OUT;
91647e946e7SWyllys Ingersoll 	}
91747e946e7SWyllys Ingersoll 
91847e946e7SWyllys Ingersoll 	/* The TPM MUST set the state to stsValid within TIMEOUT_C */
9198d26100cSWyllys Ingersoll 	ret = tpm_wait_for_stat(tpm, TPM_STS_VALID, tpm->timeout_c);
92047e946e7SWyllys Ingersoll 
92147e946e7SWyllys Ingersoll 	status = tis_get_status(tpm);
92247e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
923ccf625adSWyllys Ingersoll #ifdef DEBUG
924ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: TPM didn't set stsValid after its I/O: "
925*e9fe7b35SJason King 		    "status = 0x%08X", __func__, status);
926ccf625adSWyllys Ingersoll #endif
92747e946e7SWyllys Ingersoll 		goto OUT;
92847e946e7SWyllys Ingersoll 	}
92947e946e7SWyllys Ingersoll 
93047e946e7SWyllys Ingersoll 	/* There is still more data? */
93147e946e7SWyllys Ingersoll 	if (status & TPM_STS_DATA_AVAIL) {
932ccf625adSWyllys Ingersoll #ifdef DEBUG
933ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: TPM_STS_DATA_AVAIL is set:0x%08X",
934*e9fe7b35SJason King 		    __func__, status);
935ccf625adSWyllys Ingersoll #endif
93647e946e7SWyllys Ingersoll 		goto OUT;
93747e946e7SWyllys Ingersoll 	}
93847e946e7SWyllys Ingersoll 
93947e946e7SWyllys Ingersoll 	/*
94047e946e7SWyllys Ingersoll 	 * Release the control of the TPM after we are done with it
94147e946e7SWyllys Ingersoll 	 * it...so others can also get a chance to send data
94247e946e7SWyllys Ingersoll 	 */
94347e946e7SWyllys Ingersoll 	tis_release_locality(tpm, tpm->locality, 0);
94447e946e7SWyllys Ingersoll 
94547e946e7SWyllys Ingersoll OUT:
94647e946e7SWyllys Ingersoll 	tpm_set_ready(tpm);
94747e946e7SWyllys Ingersoll 	tis_release_locality(tpm, tpm->locality, 0);
94847e946e7SWyllys Ingersoll 	return (size);
94947e946e7SWyllys Ingersoll }
95047e946e7SWyllys Ingersoll 
95147e946e7SWyllys Ingersoll /*
95247e946e7SWyllys Ingersoll  * Send the data (TPM commands) to the Data IO register
95347e946e7SWyllys Ingersoll  */
95447e946e7SWyllys Ingersoll static int
tis_send_data(tpm_state_t * tpm,uint8_t * buf,size_t bufsiz)955*e9fe7b35SJason King tis_send_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz)
956*e9fe7b35SJason King {
95747e946e7SWyllys Ingersoll 	int ret;
95847e946e7SWyllys Ingersoll 	uint8_t status;
95947e946e7SWyllys Ingersoll 	uint16_t burstcnt;
96047e946e7SWyllys Ingersoll 	uint32_t ordinal;
96147e946e7SWyllys Ingersoll 	size_t count = 0;
96247e946e7SWyllys Ingersoll 
96347e946e7SWyllys Ingersoll 	ASSERT(tpm != NULL && buf != NULL);
96447e946e7SWyllys Ingersoll 
96547e946e7SWyllys Ingersoll 	if (bufsiz == 0) {
966ccf625adSWyllys Ingersoll #ifdef DEBUG
967*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: bufsiz arg is zero", __func__);
968ccf625adSWyllys Ingersoll #endif
96947e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
97047e946e7SWyllys Ingersoll 	}
97147e946e7SWyllys Ingersoll 
97247e946e7SWyllys Ingersoll 	/* Put the TPM in ready state */
97347e946e7SWyllys Ingersoll 	status = tis_get_status(tpm);
97447e946e7SWyllys Ingersoll 
97547e946e7SWyllys Ingersoll 	if (!(status & TPM_STS_CMD_READY)) {
97647e946e7SWyllys Ingersoll 		tpm_set_ready(tpm);
9778d26100cSWyllys Ingersoll 		ret = tpm_wait_for_stat(tpm, TPM_STS_CMD_READY, tpm->timeout_b);
97847e946e7SWyllys Ingersoll 		if (ret != DDI_SUCCESS) {
979ccf625adSWyllys Ingersoll #ifdef DEBUG
980ccf625adSWyllys Ingersoll 			cmn_err(CE_WARN, "!%s: could not put the TPM "
98147e946e7SWyllys Ingersoll 			    "in the command ready state:"
98247e946e7SWyllys Ingersoll 			    "tpm_wait_for_stat returned error",
983*e9fe7b35SJason King 			    __func__);
984ccf625adSWyllys Ingersoll #endif
98547e946e7SWyllys Ingersoll 			goto FAIL;
98647e946e7SWyllys Ingersoll 		}
98747e946e7SWyllys Ingersoll 	}
98847e946e7SWyllys Ingersoll 
98947e946e7SWyllys Ingersoll 	/*
99047e946e7SWyllys Ingersoll 	 * Now we are ready to send command
99147e946e7SWyllys Ingersoll 	 * TPM's burstcount dictates how many bytes we can write at a time
99247e946e7SWyllys Ingersoll 	 * Burstcount is dynamic if INTF_CAPABILITY for static burstcount is
99347e946e7SWyllys Ingersoll 	 * not set.
99447e946e7SWyllys Ingersoll 	 */
99547e946e7SWyllys Ingersoll 	while (count < bufsiz - 1) {
99647e946e7SWyllys Ingersoll 		burstcnt = tpm_get_burstcount(tpm);
99747e946e7SWyllys Ingersoll 		if (burstcnt == 0) {
998ccf625adSWyllys Ingersoll #ifdef DEBUG
999ccf625adSWyllys Ingersoll 			cmn_err(CE_WARN, "!%s: tpm_get_burstcnt returned error",
1000*e9fe7b35SJason King 			    __func__);
1001ccf625adSWyllys Ingersoll #endif
100247e946e7SWyllys Ingersoll 			ret = DDI_FAILURE;
100347e946e7SWyllys Ingersoll 			goto FAIL;
100447e946e7SWyllys Ingersoll 		}
100547e946e7SWyllys Ingersoll 
100647e946e7SWyllys Ingersoll 		for (; burstcnt > 0 && count < bufsiz - 1; burstcnt--) {
10078d26100cSWyllys Ingersoll 			tpm_put8(tpm, TPM_DATA_FIFO, buf[count]);
100847e946e7SWyllys Ingersoll 			count++;
100947e946e7SWyllys Ingersoll 		}
101047e946e7SWyllys Ingersoll 		/* Wait for TPM to indicate that it is ready for more data */
101147e946e7SWyllys Ingersoll 		ret = tpm_wait_for_stat(tpm,
10128d26100cSWyllys Ingersoll 		    (TPM_STS_VALID | TPM_STS_DATA_EXPECT), tpm->timeout_c);
101347e946e7SWyllys Ingersoll 		if (ret != DDI_SUCCESS) {
1014ccf625adSWyllys Ingersoll #ifdef DEBUG
1015ccf625adSWyllys Ingersoll 			cmn_err(CE_WARN, "!%s: TPM didn't enter STS_VALID "
1016*e9fe7b35SJason King 			    "state", __func__);
1017ccf625adSWyllys Ingersoll #endif
101847e946e7SWyllys Ingersoll 			goto FAIL;
101947e946e7SWyllys Ingersoll 		}
102047e946e7SWyllys Ingersoll 	}
102147e946e7SWyllys Ingersoll 	/* We can't exit the loop above unless we wrote bufsiz-1 bytes */
102247e946e7SWyllys Ingersoll 
102347e946e7SWyllys Ingersoll 	/* Write last byte */
10248d26100cSWyllys Ingersoll 	tpm_put8(tpm, TPM_DATA_FIFO, buf[count]);
102547e946e7SWyllys Ingersoll 	count++;
102647e946e7SWyllys Ingersoll 
102747e946e7SWyllys Ingersoll 	/* Wait for the TPM to enter Valid State */
10288d26100cSWyllys Ingersoll 	ret = tpm_wait_for_stat(tpm, TPM_STS_VALID, tpm->timeout_c);
102947e946e7SWyllys Ingersoll 	if (ret == DDI_FAILURE) {
1030ccf625adSWyllys Ingersoll #ifdef DEBUG
1031ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: tpm didn't enter STS_VALID state",
1032*e9fe7b35SJason King 		    __func__);
1033ccf625adSWyllys Ingersoll #endif
103447e946e7SWyllys Ingersoll 		goto FAIL;
103547e946e7SWyllys Ingersoll 	}
103647e946e7SWyllys Ingersoll 
103747e946e7SWyllys Ingersoll 	status = tis_get_status(tpm);
103847e946e7SWyllys Ingersoll 	/* The TPM should NOT be expecing more data at this point */
103947e946e7SWyllys Ingersoll 	if ((status & TPM_STS_DATA_EXPECT) != 0) {
1040ccf625adSWyllys Ingersoll #ifdef DEBUG
1041ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: DATA_EXPECT should not be set after "
1042*e9fe7b35SJason King 		    "writing the last byte: status=0x%08X", __func__, status);
1043ccf625adSWyllys Ingersoll #endif
104447e946e7SWyllys Ingersoll 		ret = DDI_FAILURE;
104547e946e7SWyllys Ingersoll 		goto FAIL;
104647e946e7SWyllys Ingersoll 	}
104747e946e7SWyllys Ingersoll 
104847e946e7SWyllys Ingersoll 	/*
104947e946e7SWyllys Ingersoll 	 * Final step: Writing TPM_STS_GO to TPM_STS
105047e946e7SWyllys Ingersoll 	 * register will actually send the command.
105147e946e7SWyllys Ingersoll 	 */
10528d26100cSWyllys Ingersoll 	tpm_put8(tpm, TPM_STS, TPM_STS_GO);
105347e946e7SWyllys Ingersoll 
105447e946e7SWyllys Ingersoll 	/* Ordinal/Command_code is located in buf[6..9] */
105547e946e7SWyllys Ingersoll 	ordinal = load32(buf, TPM_COMMAND_CODE_OFFSET);
105647e946e7SWyllys Ingersoll 
105747e946e7SWyllys Ingersoll 	ret = tpm_wait_for_stat(tpm, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
10588d26100cSWyllys Ingersoll 	    tpm_get_ordinal_duration(tpm, ordinal));
105947e946e7SWyllys Ingersoll 	if (ret == DDI_FAILURE) {
1060ccf625adSWyllys Ingersoll #ifdef DEBUG
106147e946e7SWyllys Ingersoll 		status = tis_get_status(tpm);
106247e946e7SWyllys Ingersoll 		if (!(status & TPM_STS_DATA_AVAIL) ||
106347e946e7SWyllys Ingersoll 		    !(status & TPM_STS_VALID)) {
1064ccf625adSWyllys Ingersoll 			cmn_err(CE_WARN, "!%s: TPM not ready or valid "
1065ccf625adSWyllys Ingersoll 			    "(ordinal = %d timeout = %ld status = 0x%0x)",
1066*e9fe7b35SJason King 			    __func__, ordinal,
1067ccf625adSWyllys Ingersoll 			    tpm_get_ordinal_duration(tpm, ordinal),
1068ccf625adSWyllys Ingersoll 			    status);
106947e946e7SWyllys Ingersoll 		} else {
1070ccf625adSWyllys Ingersoll 			cmn_err(CE_WARN, "!%s: tpm_wait_for_stat "
1071ccf625adSWyllys Ingersoll 			    "(DATA_AVAIL | VALID) failed status = 0x%0X",
1072*e9fe7b35SJason King 			    __func__, status);
107347e946e7SWyllys Ingersoll 		}
1074ccf625adSWyllys Ingersoll #endif
107547e946e7SWyllys Ingersoll 		goto FAIL;
107647e946e7SWyllys Ingersoll 	}
107747e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
107847e946e7SWyllys Ingersoll 
107947e946e7SWyllys Ingersoll FAIL:
108047e946e7SWyllys Ingersoll 	tpm_set_ready(tpm);
108147e946e7SWyllys Ingersoll 	tis_release_locality(tpm, tpm->locality, 0);
108247e946e7SWyllys Ingersoll 	return (ret);
108347e946e7SWyllys Ingersoll }
108447e946e7SWyllys Ingersoll 
108547e946e7SWyllys Ingersoll /*
108647e946e7SWyllys Ingersoll  * Clear XrequestUse and Xactivelocality, where X is the current locality
108747e946e7SWyllys Ingersoll  */
108847e946e7SWyllys Ingersoll static void
tis_release_locality(tpm_state_t * tpm,char locality,int force)1089*e9fe7b35SJason King tis_release_locality(tpm_state_t *tpm, char locality, int force)
1090*e9fe7b35SJason King {
109147e946e7SWyllys Ingersoll 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
109247e946e7SWyllys Ingersoll 
109347e946e7SWyllys Ingersoll 	if (force ||
10948d26100cSWyllys Ingersoll 	    (tpm_get8(tpm, TPM_ACCESS) &
10958d26100cSWyllys Ingersoll 	    (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
10968d26100cSWyllys Ingersoll 	    (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
109747e946e7SWyllys Ingersoll 		/*
109847e946e7SWyllys Ingersoll 		 * Writing 1 to active locality bit in TPM_ACCESS
109947e946e7SWyllys Ingersoll 		 * register reliquishes the control of the locality
110047e946e7SWyllys Ingersoll 		 */
11018d26100cSWyllys Ingersoll 		tpm_put8(tpm, TPM_ACCESS, TPM_ACCESS_ACTIVE_LOCALITY);
110247e946e7SWyllys Ingersoll 	}
110347e946e7SWyllys Ingersoll }
110447e946e7SWyllys Ingersoll 
110547e946e7SWyllys Ingersoll /*
110647e946e7SWyllys Ingersoll  * Checks whether the given locality is active
110747e946e7SWyllys Ingersoll  * Use TPM_ACCESS register and the masks TPM_ACCESS_VALID,TPM_ACTIVE_LOCALITY
110847e946e7SWyllys Ingersoll  */
110947e946e7SWyllys Ingersoll static int
tis_check_active_locality(tpm_state_t * tpm,char locality)1110*e9fe7b35SJason King tis_check_active_locality(tpm_state_t *tpm, char locality)
1111*e9fe7b35SJason King {
111247e946e7SWyllys Ingersoll 	uint8_t access_bits;
11138d26100cSWyllys Ingersoll 	uint8_t old_locality;
111447e946e7SWyllys Ingersoll 
111547e946e7SWyllys Ingersoll 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
111647e946e7SWyllys Ingersoll 
11178d26100cSWyllys Ingersoll 	old_locality = tpm->locality;
11188d26100cSWyllys Ingersoll 	tpm->locality = locality;
11198d26100cSWyllys Ingersoll 
11208d26100cSWyllys Ingersoll 	/* Just check to see if the requested locality works */
11218d26100cSWyllys Ingersoll 	access_bits = tpm_get8(tpm, TPM_ACCESS);
112247e946e7SWyllys Ingersoll 	access_bits &= (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID);
112347e946e7SWyllys Ingersoll 
11248d26100cSWyllys Ingersoll 	/* this was just a check, not a request to switch */
11258d26100cSWyllys Ingersoll 	tpm->locality = old_locality;
11268d26100cSWyllys Ingersoll 
11278d26100cSWyllys Ingersoll 	if (access_bits == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
112847e946e7SWyllys Ingersoll 		return (DDI_SUCCESS);
11298d26100cSWyllys Ingersoll 	} else {
113047e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
113147e946e7SWyllys Ingersoll 	}
11328d26100cSWyllys Ingersoll }
113347e946e7SWyllys Ingersoll 
113447e946e7SWyllys Ingersoll /* Request the TPM to be in the given locality */
113547e946e7SWyllys Ingersoll static int
tis_request_locality(tpm_state_t * tpm,char locality)1136*e9fe7b35SJason King tis_request_locality(tpm_state_t *tpm, char locality)
1137*e9fe7b35SJason King {
113847e946e7SWyllys Ingersoll 	clock_t timeout;
113947e946e7SWyllys Ingersoll 	int ret;
114047e946e7SWyllys Ingersoll 
114147e946e7SWyllys Ingersoll 	ASSERT(tpm != NULL && locality >= 0 && locality < 5);
114247e946e7SWyllys Ingersoll 
114347e946e7SWyllys Ingersoll 	ret = tis_check_active_locality(tpm, locality);
114447e946e7SWyllys Ingersoll 
114547e946e7SWyllys Ingersoll 	if (ret == DDI_SUCCESS) {
114647e946e7SWyllys Ingersoll 		/* Locality is already active */
114747e946e7SWyllys Ingersoll 		tpm->locality = locality;
114847e946e7SWyllys Ingersoll 		return (DDI_SUCCESS);
114947e946e7SWyllys Ingersoll 	}
115047e946e7SWyllys Ingersoll 
11518d26100cSWyllys Ingersoll 	tpm_put8(tpm, TPM_ACCESS, TPM_ACCESS_REQUEST_USE);
115247e946e7SWyllys Ingersoll 	timeout = ddi_get_lbolt() + tpm->timeout_a;
115347e946e7SWyllys Ingersoll 
115447e946e7SWyllys Ingersoll 	/* Using polling */
1155*e9fe7b35SJason King 	while (tis_check_active_locality(tpm, locality) != DDI_SUCCESS) {
115647e946e7SWyllys Ingersoll 		if (ddi_get_lbolt() >= timeout) {
1157ccf625adSWyllys Ingersoll #ifdef DEBUG
1158ccf625adSWyllys Ingersoll 			cmn_err(CE_WARN, "!%s: (interrupt-disabled) "
11598d26100cSWyllys Ingersoll 			    "tis_request_locality timed out (timeout_a = %ld)",
1160*e9fe7b35SJason King 			    __func__, tpm->timeout_a);
1161ccf625adSWyllys Ingersoll #endif
116247e946e7SWyllys Ingersoll 			return (DDI_FAILURE);
116347e946e7SWyllys Ingersoll 		}
116447e946e7SWyllys Ingersoll 		delay(tpm->timeout_poll);
116547e946e7SWyllys Ingersoll 	}
116647e946e7SWyllys Ingersoll 
116747e946e7SWyllys Ingersoll 	tpm->locality = locality;
116847e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
116947e946e7SWyllys Ingersoll }
117047e946e7SWyllys Ingersoll 
117147e946e7SWyllys Ingersoll /* Read the status register */
117247e946e7SWyllys Ingersoll static uint8_t
tis_get_status(tpm_state_t * tpm)1173*e9fe7b35SJason King tis_get_status(tpm_state_t *tpm)
1174*e9fe7b35SJason King {
11758d26100cSWyllys Ingersoll 	return (tpm_get8(tpm, TPM_STS));
117647e946e7SWyllys Ingersoll }
117747e946e7SWyllys Ingersoll 
117847e946e7SWyllys Ingersoll static int
tpm_wait_for_stat(tpm_state_t * tpm,uint8_t mask,clock_t timeout)1179*e9fe7b35SJason King tpm_wait_for_stat(tpm_state_t *tpm, uint8_t mask, clock_t timeout)
1180*e9fe7b35SJason King {
11818d26100cSWyllys Ingersoll 	clock_t absolute_timeout = ddi_get_lbolt() + timeout;
118247e946e7SWyllys Ingersoll 
118347e946e7SWyllys Ingersoll 	/* Using polling */
118447e946e7SWyllys Ingersoll 	while ((tis_get_status(tpm) & mask) != mask) {
118547e946e7SWyllys Ingersoll 		if (ddi_get_lbolt() >= absolute_timeout) {
118647e946e7SWyllys Ingersoll 			/* Timeout reached */
1187ccf625adSWyllys Ingersoll #ifdef DEBUG
1188ccf625adSWyllys Ingersoll 			cmn_err(CE_WARN, "!%s: using "
11898d26100cSWyllys Ingersoll 			    "polling - reached timeout (%ld usecs)",
1190*e9fe7b35SJason King 			    __func__, drv_hztousec(timeout));
1191ccf625adSWyllys Ingersoll #endif
119247e946e7SWyllys Ingersoll 			return (DDI_FAILURE);
119347e946e7SWyllys Ingersoll 		}
119447e946e7SWyllys Ingersoll 		delay(tpm->timeout_poll);
119547e946e7SWyllys Ingersoll 	}
119647e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
119747e946e7SWyllys Ingersoll }
119847e946e7SWyllys Ingersoll 
119947e946e7SWyllys Ingersoll /*
120047e946e7SWyllys Ingersoll  * Initialize TPM device
120147e946e7SWyllys Ingersoll  * 1. Find out supported interrupt capabilities
120247e946e7SWyllys Ingersoll  * 2. Set up interrupt handler if supported (some BIOSes don't support
120347e946e7SWyllys Ingersoll  * interrupts for TPMS, in which case we set up polling)
120447e946e7SWyllys Ingersoll  * 3. Determine timeouts and commands duration
120547e946e7SWyllys Ingersoll  */
120647e946e7SWyllys Ingersoll static int
tis_init(tpm_state_t * tpm)1207*e9fe7b35SJason King tis_init(tpm_state_t *tpm)
1208*e9fe7b35SJason King {
120947e946e7SWyllys Ingersoll 	uint32_t intf_caps;
121047e946e7SWyllys Ingersoll 	int ret;
121147e946e7SWyllys Ingersoll 
121247e946e7SWyllys Ingersoll 	/*
121347e946e7SWyllys Ingersoll 	 * Temporarily set up timeouts before we get the real timeouts
121447e946e7SWyllys Ingersoll 	 * by issuing TPM_CAP commands (but to issue TPM_CAP commands,
121547e946e7SWyllys Ingersoll 	 * you need TIMEOUTs defined...chicken and egg problem here.
121647e946e7SWyllys Ingersoll 	 * TPM timeouts: Convert the milliseconds to clock cycles
121747e946e7SWyllys Ingersoll 	 */
121847e946e7SWyllys Ingersoll 	tpm->timeout_a = drv_usectohz(TIS_TIMEOUT_A);
121947e946e7SWyllys Ingersoll 	tpm->timeout_b = drv_usectohz(TIS_TIMEOUT_B);
122047e946e7SWyllys Ingersoll 	tpm->timeout_c = drv_usectohz(TIS_TIMEOUT_C);
122147e946e7SWyllys Ingersoll 	tpm->timeout_d = drv_usectohz(TIS_TIMEOUT_D);
122247e946e7SWyllys Ingersoll 	/*
122347e946e7SWyllys Ingersoll 	 * Do the same with the duration (real duration will be filled out
122447e946e7SWyllys Ingersoll 	 * when we call TPM_GetCapability to get the duration values from
122547e946e7SWyllys Ingersoll 	 * the TPM itself).
122647e946e7SWyllys Ingersoll 	 */
122747e946e7SWyllys Ingersoll 	tpm->duration[TPM_SHORT] = drv_usectohz(TPM_DEFAULT_DURATION);
122847e946e7SWyllys Ingersoll 	tpm->duration[TPM_MEDIUM] = drv_usectohz(TPM_DEFAULT_DURATION);
122947e946e7SWyllys Ingersoll 	tpm->duration[TPM_LONG] = drv_usectohz(TPM_DEFAULT_DURATION);
123047e946e7SWyllys Ingersoll 	tpm->duration[TPM_UNDEFINED] = drv_usectohz(TPM_DEFAULT_DURATION);
123147e946e7SWyllys Ingersoll 
123247e946e7SWyllys Ingersoll 	/* Find out supported capabilities */
12338d26100cSWyllys Ingersoll 	intf_caps = tpm_get32(tpm, TPM_INTF_CAP);
123447e946e7SWyllys Ingersoll 
123547e946e7SWyllys Ingersoll 	/* Upper 3 bytes should always return 0 */
123647e946e7SWyllys Ingersoll 	if (intf_caps & 0x7FFFFF00) {
1237ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: bad intf_caps value 0x%0X",
1238*e9fe7b35SJason King 		    __func__, intf_caps);
123947e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
124047e946e7SWyllys Ingersoll 	}
124147e946e7SWyllys Ingersoll 
124247e946e7SWyllys Ingersoll 	/* These two interrupts are mandatory */
124347e946e7SWyllys Ingersoll 	if (!(intf_caps & TPM_INTF_INT_LOCALITY_CHANGE_INT)) {
1244ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN,
1245ccf625adSWyllys Ingersoll 		    "!%s: Mandatory capability Locality Change Int "
1246*e9fe7b35SJason King 		    "not supported", __func__);
124747e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
124847e946e7SWyllys Ingersoll 	}
124947e946e7SWyllys Ingersoll 	if (!(intf_caps & TPM_INTF_INT_DATA_AVAIL_INT)) {
1250ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: Mandatory capability Data Available Int "
1251*e9fe7b35SJason King 		    "not supported.", __func__);
125247e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
125347e946e7SWyllys Ingersoll 	}
125447e946e7SWyllys Ingersoll 
125547e946e7SWyllys Ingersoll 	/*
125647e946e7SWyllys Ingersoll 	 * Before we start writing anything to TPM's registers,
125747e946e7SWyllys Ingersoll 	 * make sure we are in locality 0
125847e946e7SWyllys Ingersoll 	 */
12598d26100cSWyllys Ingersoll 	ret = tis_request_locality(tpm, DEFAULT_LOCALITY);
126047e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
1261*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: Unable to request locality %d", __func__,
12628d26100cSWyllys Ingersoll 		    DEFAULT_LOCALITY);
126347e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
126447e946e7SWyllys Ingersoll 	} /* Now we can refer to the locality as tpm->locality */
126547e946e7SWyllys Ingersoll 
126647e946e7SWyllys Ingersoll 	tpm->timeout_poll = drv_usectohz(TPM_POLLING_TIMEOUT);
126747e946e7SWyllys Ingersoll 	tpm->intr_enabled = 0;
126847e946e7SWyllys Ingersoll 
126947e946e7SWyllys Ingersoll 	/* Get the real timeouts from the TPM */
127047e946e7SWyllys Ingersoll 	ret = tpm_get_timeouts(tpm);
127147e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
1272*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: tpm_get_timeouts error", __func__);
127347e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
127447e946e7SWyllys Ingersoll 	}
127547e946e7SWyllys Ingersoll 
127647e946e7SWyllys Ingersoll 	ret = tpm_get_duration(tpm);
127747e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
1278*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: tpm_get_duration error", __func__);
127947e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
128047e946e7SWyllys Ingersoll 	}
128147e946e7SWyllys Ingersoll 
128247e946e7SWyllys Ingersoll 	/* This gets the TPM version information */
128347e946e7SWyllys Ingersoll 	ret = tpm_get_version(tpm);
128447e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
1285*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: tpm_get_version error", __func__);
128647e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
128747e946e7SWyllys Ingersoll 	}
128847e946e7SWyllys Ingersoll 
128947e946e7SWyllys Ingersoll 	/*
129047e946e7SWyllys Ingersoll 	 * Unless the TPM completes the test of its commands,
129147e946e7SWyllys Ingersoll 	 * it can return an error when the untested commands are called
129247e946e7SWyllys Ingersoll 	 */
129347e946e7SWyllys Ingersoll 	ret = tpm_continue_selftest(tpm);
129447e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
1295*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: tpm_continue_selftest error", __func__);
129647e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
129747e946e7SWyllys Ingersoll 	}
129847e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
129947e946e7SWyllys Ingersoll }
130047e946e7SWyllys Ingersoll 
130147e946e7SWyllys Ingersoll /*
130247e946e7SWyllys Ingersoll  * Module Entry points
130347e946e7SWyllys Ingersoll  */
130447e946e7SWyllys Ingersoll int
_init(void)130547e946e7SWyllys Ingersoll _init(void)
130647e946e7SWyllys Ingersoll {
130747e946e7SWyllys Ingersoll 	int ret;
130847e946e7SWyllys Ingersoll 
130947e946e7SWyllys Ingersoll 	ret = ddi_soft_state_init(&statep, sizeof (tpm_state_t), 1);
1310ccf625adSWyllys Ingersoll 	if (ret) {
1311ccf625adSWyllys Ingersoll #ifdef DEBUG
1312ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!ddi_soft_state_init failed: %d", ret);
1313ccf625adSWyllys Ingersoll #endif
131447e946e7SWyllys Ingersoll 		return (ret);
13158d26100cSWyllys Ingersoll 	}
131647e946e7SWyllys Ingersoll 	ret = mod_install(&tpm_ml);
131747e946e7SWyllys Ingersoll 	if (ret != 0) {
1318ccf625adSWyllys Ingersoll #ifdef DEBUG
1319ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!_init: mod_install returned non-zero");
1320ccf625adSWyllys Ingersoll #endif
132147e946e7SWyllys Ingersoll 		ddi_soft_state_fini(&statep);
132247e946e7SWyllys Ingersoll 		return (ret);
132347e946e7SWyllys Ingersoll 	}
132447e946e7SWyllys Ingersoll 
132547e946e7SWyllys Ingersoll 	return (ret);
132647e946e7SWyllys Ingersoll }
132747e946e7SWyllys Ingersoll 
132847e946e7SWyllys Ingersoll int
_info(struct modinfo * modinfop)132947e946e7SWyllys Ingersoll _info(struct modinfo *modinfop)
133047e946e7SWyllys Ingersoll {
133147e946e7SWyllys Ingersoll 	int ret;
133247e946e7SWyllys Ingersoll 	ret = mod_info(&tpm_ml, modinfop);
1333ccf625adSWyllys Ingersoll #ifdef DEBUG
133447e946e7SWyllys Ingersoll 	if (ret == 0)
1335ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!mod_info failed: %d", ret);
1336ccf625adSWyllys Ingersoll #endif
133747e946e7SWyllys Ingersoll 
133847e946e7SWyllys Ingersoll 	return (ret);
133947e946e7SWyllys Ingersoll }
134047e946e7SWyllys Ingersoll 
134147e946e7SWyllys Ingersoll int
_fini()134247e946e7SWyllys Ingersoll _fini()
134347e946e7SWyllys Ingersoll {
134447e946e7SWyllys Ingersoll 	int ret;
13458d26100cSWyllys Ingersoll 
134647e946e7SWyllys Ingersoll 	ret = mod_remove(&tpm_ml);
13478d26100cSWyllys Ingersoll 	if (ret != 0)
134847e946e7SWyllys Ingersoll 		return (ret);
13498d26100cSWyllys Ingersoll 
135047e946e7SWyllys Ingersoll 	ddi_soft_state_fini(&statep);
135147e946e7SWyllys Ingersoll 
135247e946e7SWyllys Ingersoll 	return (ret);
135347e946e7SWyllys Ingersoll }
135447e946e7SWyllys Ingersoll /* End of driver configuration functions */
135547e946e7SWyllys Ingersoll 
135647e946e7SWyllys Ingersoll static int
tpm_resume(tpm_state_t * tpm)135747e946e7SWyllys Ingersoll tpm_resume(tpm_state_t *tpm)
135847e946e7SWyllys Ingersoll {
135947e946e7SWyllys Ingersoll 	mutex_enter(&tpm->pm_mutex);
136047e946e7SWyllys Ingersoll 	if (!tpm->suspended) {
136147e946e7SWyllys Ingersoll 		mutex_exit(&tpm->pm_mutex);
136247e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
136347e946e7SWyllys Ingersoll 	}
136447e946e7SWyllys Ingersoll 	tpm->suspended = 0;
136547e946e7SWyllys Ingersoll 	cv_broadcast(&tpm->suspend_cv);
136647e946e7SWyllys Ingersoll 	mutex_exit(&tpm->pm_mutex);
136747e946e7SWyllys Ingersoll 
136847e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
136947e946e7SWyllys Ingersoll }
137047e946e7SWyllys Ingersoll 
13718d26100cSWyllys Ingersoll #ifdef sun4v
13728d26100cSWyllys Ingersoll static uint64_t hsvc_tpm_minor = 0;
13738d26100cSWyllys Ingersoll static hsvc_info_t hsvc_tpm = {
13748d26100cSWyllys Ingersoll 	HSVC_REV_1, NULL, HSVC_GROUP_TPM, 1, 0, NULL
13758d26100cSWyllys Ingersoll };
13768d26100cSWyllys Ingersoll #endif
13778d26100cSWyllys Ingersoll 
137847e946e7SWyllys Ingersoll /*
137947e946e7SWyllys Ingersoll  * Sun DDI/DDK entry points
138047e946e7SWyllys Ingersoll  */
138147e946e7SWyllys Ingersoll static int
tpm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)138247e946e7SWyllys Ingersoll tpm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
138347e946e7SWyllys Ingersoll {
13848d26100cSWyllys Ingersoll 	int ret;
138547e946e7SWyllys Ingersoll 	int instance;
13868d26100cSWyllys Ingersoll #ifndef sun4v
13878d26100cSWyllys Ingersoll 	int idx, nregs;
13888d26100cSWyllys Ingersoll #endif
138947e946e7SWyllys Ingersoll 	tpm_state_t *tpm = NULL;
139047e946e7SWyllys Ingersoll 
139147e946e7SWyllys Ingersoll 	ASSERT(dip != NULL);
139247e946e7SWyllys Ingersoll 
139347e946e7SWyllys Ingersoll 	instance = ddi_get_instance(dip);
13948d26100cSWyllys Ingersoll 	if (instance < 0)
13958d26100cSWyllys Ingersoll 		return (DDI_FAILURE);
139647e946e7SWyllys Ingersoll 
139747e946e7SWyllys Ingersoll 	/* Nothing out of ordinary here */
139847e946e7SWyllys Ingersoll 	switch (cmd) {
139947e946e7SWyllys Ingersoll 	case DDI_ATTACH:
14008d26100cSWyllys Ingersoll 		if (ddi_soft_state_zalloc(statep, instance) == DDI_SUCCESS) {
140147e946e7SWyllys Ingersoll 			tpm = ddi_get_soft_state(statep, instance);
14028d26100cSWyllys Ingersoll 			if (tpm == NULL) {
1403ccf625adSWyllys Ingersoll #ifdef DEBUG
14048d26100cSWyllys Ingersoll 				cmn_err(CE_WARN,
1405ccf625adSWyllys Ingersoll 				    "!%s: cannot get state information.",
1406*e9fe7b35SJason King 				    __func__);
1407ccf625adSWyllys Ingersoll #endif
14088d26100cSWyllys Ingersoll 				return (DDI_FAILURE);
14098d26100cSWyllys Ingersoll 			}
141047e946e7SWyllys Ingersoll 			tpm->dip = dip;
14118d26100cSWyllys Ingersoll 		} else {
1412d25a0be9SWyllys Ingersoll #ifdef DEBUG
14138d26100cSWyllys Ingersoll 			cmn_err(CE_WARN,
1414ccf625adSWyllys Ingersoll 			    "!%s: cannot allocate state information.",
1415*e9fe7b35SJason King 			    __func__);
1416d25a0be9SWyllys Ingersoll #endif
14178d26100cSWyllys Ingersoll 			return (DDI_FAILURE);
14188d26100cSWyllys Ingersoll 		}
141947e946e7SWyllys Ingersoll 		break;
142047e946e7SWyllys Ingersoll 	case DDI_RESUME:
142147e946e7SWyllys Ingersoll 		tpm = ddi_get_soft_state(statep, instance);
142247e946e7SWyllys Ingersoll 		if (tpm == NULL) {
1423ccf625adSWyllys Ingersoll #ifdef DEBUG
1424ccf625adSWyllys Ingersoll 			cmn_err(CE_WARN, "!%s: cannot get state information.",
1425*e9fe7b35SJason King 			    __func__);
1426ccf625adSWyllys Ingersoll #endif
14278d26100cSWyllys Ingersoll 			return (DDI_FAILURE);
142847e946e7SWyllys Ingersoll 		}
142947e946e7SWyllys Ingersoll 		return (tpm_resume(tpm));
143047e946e7SWyllys Ingersoll 	default:
1431ccf625adSWyllys Ingersoll #ifdef DEBUG
1432*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: cmd %d is not implemented", __func__,
1433*e9fe7b35SJason King 		    cmd);
1434ccf625adSWyllys Ingersoll #endif
143547e946e7SWyllys Ingersoll 		ret = DDI_FAILURE;
143647e946e7SWyllys Ingersoll 		goto FAIL;
143747e946e7SWyllys Ingersoll 	}
143847e946e7SWyllys Ingersoll 
143947e946e7SWyllys Ingersoll 	/* Zeroize the flag, which is used to keep track of what is allocated */
144047e946e7SWyllys Ingersoll 	tpm->flags = 0;
144147e946e7SWyllys Ingersoll 
14428d26100cSWyllys Ingersoll #ifdef sun4v
14438d26100cSWyllys Ingersoll 	ret = hsvc_register(&hsvc_tpm, &hsvc_tpm_minor);
14448d26100cSWyllys Ingersoll 	if (ret != 0) {
1445ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: failed to register with "
1446*e9fe7b35SJason King 		    "hypervisor: 0x%0x", __func__, ret);
14478d26100cSWyllys Ingersoll 		goto FAIL;
14488d26100cSWyllys Ingersoll 	}
14498d26100cSWyllys Ingersoll 	tpm->flags |= TPM_HSVC_REGISTERED;
14508d26100cSWyllys Ingersoll #else
145147e946e7SWyllys Ingersoll 	tpm->accattr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
145247e946e7SWyllys Ingersoll 	tpm->accattr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
145347e946e7SWyllys Ingersoll 	tpm->accattr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
145447e946e7SWyllys Ingersoll 
145547e946e7SWyllys Ingersoll 	idx = 0;
145647e946e7SWyllys Ingersoll 	ret = ddi_dev_nregs(tpm->dip, &nregs);
145747e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS)
145847e946e7SWyllys Ingersoll 		goto FAIL;
145947e946e7SWyllys Ingersoll 
146047e946e7SWyllys Ingersoll 	/*
146147e946e7SWyllys Ingersoll 	 * TPM vendors put the TPM registers in different
146247e946e7SWyllys Ingersoll 	 * slots in their register lists.  They are not always
146347e946e7SWyllys Ingersoll 	 * the 1st set of registers, for instance.
146447e946e7SWyllys Ingersoll 	 * Loop until we find the set that matches the expected
146547e946e7SWyllys Ingersoll 	 * register size (0x5000).
146647e946e7SWyllys Ingersoll 	 */
146747e946e7SWyllys Ingersoll 	for (idx = 0; idx < nregs; idx++) {
146847e946e7SWyllys Ingersoll 		off_t regsize;
146947e946e7SWyllys Ingersoll 
147047e946e7SWyllys Ingersoll 		if ((ret = ddi_dev_regsize(tpm->dip, idx, &regsize)) !=
147147e946e7SWyllys Ingersoll 		    DDI_SUCCESS)
147247e946e7SWyllys Ingersoll 			goto FAIL;
147347e946e7SWyllys Ingersoll 		/* The TIS spec says the TPM registers must be 0x5000 bytes */
147447e946e7SWyllys Ingersoll 		if (regsize == 0x5000)
147547e946e7SWyllys Ingersoll 			break;
147647e946e7SWyllys Ingersoll 	}
1477d25a0be9SWyllys Ingersoll 	if (idx == nregs) {
1478d25a0be9SWyllys Ingersoll 		ret = DDI_FAILURE;
1479d25a0be9SWyllys Ingersoll 		goto FAIL;
1480d25a0be9SWyllys Ingersoll 	}
1481459e772fSWyllys Ingersoll 
148247e946e7SWyllys Ingersoll 	ret = ddi_regs_map_setup(tpm->dip, idx, (caddr_t *)&tpm->addr,
148347e946e7SWyllys Ingersoll 	    (offset_t)0, (offset_t)0x5000,
148447e946e7SWyllys Ingersoll 	    &tpm->accattr, &tpm->handle);
148547e946e7SWyllys Ingersoll 
148647e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
148747e946e7SWyllys Ingersoll 		goto FAIL;
148847e946e7SWyllys Ingersoll 	}
148947e946e7SWyllys Ingersoll 	tpm->flags |= TPM_DIDREGSMAP;
14908d26100cSWyllys Ingersoll #endif
149147e946e7SWyllys Ingersoll 	/* Enable TPM device according to the TIS specification */
149247e946e7SWyllys Ingersoll 	ret = tis_init(tpm);
149347e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
1494ccf625adSWyllys Ingersoll #ifdef DEBUG
1495ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: tis_init() failed with error %d",
1496*e9fe7b35SJason King 		    __func__, ret);
1497ccf625adSWyllys Ingersoll #endif
149847e946e7SWyllys Ingersoll 
149947e946e7SWyllys Ingersoll 		/* We need to clean up the ddi_regs_map_setup call */
15008d26100cSWyllys Ingersoll 		if (tpm->flags & TPM_DIDREGSMAP) {
150147e946e7SWyllys Ingersoll 			ddi_regs_map_free(&tpm->handle);
150247e946e7SWyllys Ingersoll 			tpm->handle = NULL;
150347e946e7SWyllys Ingersoll 			tpm->flags &= ~TPM_DIDREGSMAP;
15048d26100cSWyllys Ingersoll 		}
150547e946e7SWyllys Ingersoll 		goto FAIL;
150647e946e7SWyllys Ingersoll 	}
150747e946e7SWyllys Ingersoll 
150847e946e7SWyllys Ingersoll 	/* Initialize the inter-process lock */
150947e946e7SWyllys Ingersoll 	mutex_init(&tpm->dev_lock, NULL, MUTEX_DRIVER, NULL);
151047e946e7SWyllys Ingersoll 	mutex_init(&tpm->pm_mutex, NULL, MUTEX_DRIVER, NULL);
151147e946e7SWyllys Ingersoll 	cv_init(&tpm->suspend_cv, NULL, CV_DRIVER, NULL);
151247e946e7SWyllys Ingersoll 
151347e946e7SWyllys Ingersoll 	/* Set the suspend/resume property */
151447e946e7SWyllys Ingersoll 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
151547e946e7SWyllys Ingersoll 	    "pm-hardware-state", "needs-suspend-resume");
151647e946e7SWyllys Ingersoll 
151747e946e7SWyllys Ingersoll 	mutex_enter(&tpm->pm_mutex);
151847e946e7SWyllys Ingersoll 	tpm->suspended = 0;
151947e946e7SWyllys Ingersoll 	mutex_exit(&tpm->pm_mutex);
152047e946e7SWyllys Ingersoll 
152147e946e7SWyllys Ingersoll 	tpm->flags |= TPM_DID_MUTEX;
152247e946e7SWyllys Ingersoll 
152347e946e7SWyllys Ingersoll 	/* Initialize the buffer and the lock associated with it */
152447e946e7SWyllys Ingersoll 	tpm->bufsize = TPM_IO_BUF_SIZE;
152547e946e7SWyllys Ingersoll 	tpm->iobuf = kmem_zalloc((sizeof (uint8_t))*(tpm->bufsize), KM_SLEEP);
152647e946e7SWyllys Ingersoll 	tpm->flags |= TPM_DID_IO_ALLOC;
152747e946e7SWyllys Ingersoll 
152847e946e7SWyllys Ingersoll 	mutex_init(&tpm->iobuf_lock, NULL, MUTEX_DRIVER, NULL);
152947e946e7SWyllys Ingersoll 	tpm->flags |= TPM_DID_IO_MUTEX;
153047e946e7SWyllys Ingersoll 
153147e946e7SWyllys Ingersoll 	cv_init(&tpm->iobuf_cv, NULL, CV_DRIVER, NULL);
153247e946e7SWyllys Ingersoll 	tpm->flags |= TPM_DID_IO_CV;
153347e946e7SWyllys Ingersoll 
153447e946e7SWyllys Ingersoll 	/* Create minor node */
153547e946e7SWyllys Ingersoll 	ret = ddi_create_minor_node(dip, "tpm", S_IFCHR, ddi_get_instance(dip),
153647e946e7SWyllys Ingersoll 	    DDI_PSEUDO, 0);
153747e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
1538ccf625adSWyllys Ingersoll #ifdef DEBUG
1539*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: ddi_create_minor_node failed", __func__);
1540ccf625adSWyllys Ingersoll #endif
154147e946e7SWyllys Ingersoll 		goto FAIL;
154247e946e7SWyllys Ingersoll 	}
154347e946e7SWyllys Ingersoll 	tpm->flags |= TPM_DIDMINOR;
154447e946e7SWyllys Ingersoll 
15458d26100cSWyllys Ingersoll #ifdef KCF_TPM_RNG_PROVIDER
15468d26100cSWyllys Ingersoll 	/* register RNG with kcf */
15478d26100cSWyllys Ingersoll 	if (tpmrng_register(tpm) != DDI_SUCCESS)
1548ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: tpm RNG failed to register with kcf",
1549*e9fe7b35SJason King 		    __func__);
15508d26100cSWyllys Ingersoll #endif
15518d26100cSWyllys Ingersoll 
155247e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
155347e946e7SWyllys Ingersoll FAIL:
155447e946e7SWyllys Ingersoll 	if (tpm != NULL) {
155547e946e7SWyllys Ingersoll 		tpm_cleanup(dip, tpm);
155647e946e7SWyllys Ingersoll 		ddi_soft_state_free(statep, instance);
155747e946e7SWyllys Ingersoll 		tpm = NULL;
155847e946e7SWyllys Ingersoll 	}
155947e946e7SWyllys Ingersoll 
156047e946e7SWyllys Ingersoll 	return (DDI_FAILURE);
156147e946e7SWyllys Ingersoll }
156247e946e7SWyllys Ingersoll 
156347e946e7SWyllys Ingersoll /*
156447e946e7SWyllys Ingersoll  * Called by tpm_detach and tpm_attach (only on failure)
156547e946e7SWyllys Ingersoll  * Free up the resources that are allocated
156647e946e7SWyllys Ingersoll  */
156747e946e7SWyllys Ingersoll static void
tpm_cleanup(dev_info_t * dip,tpm_state_t * tpm)156847e946e7SWyllys Ingersoll tpm_cleanup(dev_info_t *dip, tpm_state_t *tpm)
156947e946e7SWyllys Ingersoll {
157047e946e7SWyllys Ingersoll 	if (tpm == NULL)
157147e946e7SWyllys Ingersoll 		return;
157247e946e7SWyllys Ingersoll 
15738d26100cSWyllys Ingersoll #ifdef KCF_TPM_RNG_PROVIDER
15748d26100cSWyllys Ingersoll 	(void) tpmrng_unregister(tpm);
15758d26100cSWyllys Ingersoll #endif
15768d26100cSWyllys Ingersoll 
15778d26100cSWyllys Ingersoll #ifdef sun4v
15788d26100cSWyllys Ingersoll 	if (tpm->flags & TPM_HSVC_REGISTERED) {
15798d26100cSWyllys Ingersoll 		(void) hsvc_unregister(&hsvc_tpm);
15808d26100cSWyllys Ingersoll 		tpm->flags &= ~(TPM_HSVC_REGISTERED);
15818d26100cSWyllys Ingersoll 	}
15828d26100cSWyllys Ingersoll #endif
158347e946e7SWyllys Ingersoll 	if (tpm->flags & TPM_DID_MUTEX) {
158447e946e7SWyllys Ingersoll 		mutex_destroy(&tpm->dev_lock);
1585d25a0be9SWyllys Ingersoll 		mutex_destroy(&tpm->pm_mutex);
1586d25a0be9SWyllys Ingersoll 		cv_destroy(&tpm->suspend_cv);
158747e946e7SWyllys Ingersoll 		tpm->flags &= ~(TPM_DID_MUTEX);
158847e946e7SWyllys Ingersoll 	}
158947e946e7SWyllys Ingersoll 	if (tpm->flags & TPM_DID_IO_ALLOC) {
159047e946e7SWyllys Ingersoll 		ASSERT(tpm->iobuf != NULL);
159147e946e7SWyllys Ingersoll 		kmem_free(tpm->iobuf, (sizeof (uint8_t))*(tpm->bufsize));
159247e946e7SWyllys Ingersoll 		tpm->flags &= ~(TPM_DID_IO_ALLOC);
159347e946e7SWyllys Ingersoll 	}
159447e946e7SWyllys Ingersoll 	if (tpm->flags & TPM_DID_IO_MUTEX) {
159547e946e7SWyllys Ingersoll 		mutex_destroy(&tpm->iobuf_lock);
159647e946e7SWyllys Ingersoll 		tpm->flags &= ~(TPM_DID_IO_MUTEX);
159747e946e7SWyllys Ingersoll 	}
159847e946e7SWyllys Ingersoll 	if (tpm->flags & TPM_DID_IO_CV) {
159947e946e7SWyllys Ingersoll 		cv_destroy(&tpm->iobuf_cv);
160047e946e7SWyllys Ingersoll 		tpm->flags &= ~(TPM_DID_IO_CV);
160147e946e7SWyllys Ingersoll 	}
160247e946e7SWyllys Ingersoll 	if (tpm->flags & TPM_DIDREGSMAP) {
160347e946e7SWyllys Ingersoll 		/* Free the mapped addresses */
160447e946e7SWyllys Ingersoll 		if (tpm->handle != NULL)
160547e946e7SWyllys Ingersoll 			ddi_regs_map_free(&tpm->handle);
160647e946e7SWyllys Ingersoll 		tpm->flags &= ~(TPM_DIDREGSMAP);
160747e946e7SWyllys Ingersoll 	}
160847e946e7SWyllys Ingersoll 	if (tpm->flags & TPM_DIDMINOR) {
160947e946e7SWyllys Ingersoll 		/* Remove minor node */
161047e946e7SWyllys Ingersoll 		ddi_remove_minor_node(dip, NULL);
161147e946e7SWyllys Ingersoll 		tpm->flags &= ~(TPM_DIDMINOR);
161247e946e7SWyllys Ingersoll 	}
161347e946e7SWyllys Ingersoll }
161447e946e7SWyllys Ingersoll 
161547e946e7SWyllys Ingersoll static int
tpm_suspend(tpm_state_t * tpm)161647e946e7SWyllys Ingersoll tpm_suspend(tpm_state_t *tpm)
161747e946e7SWyllys Ingersoll {
161847e946e7SWyllys Ingersoll 	if (tpm == NULL)
161947e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
162047e946e7SWyllys Ingersoll 	mutex_enter(&tpm->pm_mutex);
162147e946e7SWyllys Ingersoll 	if (tpm->suspended) {
162247e946e7SWyllys Ingersoll 		mutex_exit(&tpm->pm_mutex);
162347e946e7SWyllys Ingersoll 		return (DDI_SUCCESS);
162447e946e7SWyllys Ingersoll 	}
162547e946e7SWyllys Ingersoll 	tpm->suspended = 1;
162647e946e7SWyllys Ingersoll 	mutex_exit(&tpm->pm_mutex);
162747e946e7SWyllys Ingersoll 
162847e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
162947e946e7SWyllys Ingersoll }
163047e946e7SWyllys Ingersoll 
163147e946e7SWyllys Ingersoll static int
tpm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)163247e946e7SWyllys Ingersoll tpm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
163347e946e7SWyllys Ingersoll {
163447e946e7SWyllys Ingersoll 	int instance;
163547e946e7SWyllys Ingersoll 	tpm_state_t *tpm;
163647e946e7SWyllys Ingersoll 
163747e946e7SWyllys Ingersoll 	ASSERT(dip != NULL);
163847e946e7SWyllys Ingersoll 
163947e946e7SWyllys Ingersoll 	instance = ddi_get_instance(dip);
16408d26100cSWyllys Ingersoll 	if (instance < 0)
16418d26100cSWyllys Ingersoll 		return (DDI_FAILURE);
16428d26100cSWyllys Ingersoll 
164347e946e7SWyllys Ingersoll 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1644ccf625adSWyllys Ingersoll #ifdef DEBUG
1645ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
1646*e9fe7b35SJason King 		    __func__);
1647ccf625adSWyllys Ingersoll #endif
164847e946e7SWyllys Ingersoll 		return (ENXIO);
164947e946e7SWyllys Ingersoll 	}
165047e946e7SWyllys Ingersoll 
165147e946e7SWyllys Ingersoll 	switch (cmd) {
165247e946e7SWyllys Ingersoll 	case DDI_DETACH:
165347e946e7SWyllys Ingersoll 		/* Body is after the switch stmt */
165447e946e7SWyllys Ingersoll 		break;
165547e946e7SWyllys Ingersoll 	case DDI_SUSPEND:
165647e946e7SWyllys Ingersoll 		return (tpm_suspend(tpm));
165747e946e7SWyllys Ingersoll 	default:
1658ccf625adSWyllys Ingersoll #ifdef DEBUG
1659*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: case %d not implemented", __func__, cmd);
1660ccf625adSWyllys Ingersoll #endif
166147e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
166247e946e7SWyllys Ingersoll 	}
166347e946e7SWyllys Ingersoll 
166447e946e7SWyllys Ingersoll 	/* Since we are freeing tpm structure, we need to gain the lock */
166547e946e7SWyllys Ingersoll 	tpm_cleanup(dip, tpm);
166647e946e7SWyllys Ingersoll 
166747e946e7SWyllys Ingersoll 	/* Free the soft state */
166847e946e7SWyllys Ingersoll 	ddi_soft_state_free(statep, instance);
166947e946e7SWyllys Ingersoll 	tpm = NULL;
167047e946e7SWyllys Ingersoll 
167147e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
167247e946e7SWyllys Ingersoll }
167347e946e7SWyllys Ingersoll 
167447e946e7SWyllys Ingersoll /*ARGSUSED*/
167547e946e7SWyllys Ingersoll static int
tpm_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)167647e946e7SWyllys Ingersoll tpm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
167747e946e7SWyllys Ingersoll {
167847e946e7SWyllys Ingersoll 	int instance;
167947e946e7SWyllys Ingersoll 	tpm_state_t *tpm;
168047e946e7SWyllys Ingersoll 
168147e946e7SWyllys Ingersoll 	instance = ddi_get_instance(dip);
168247e946e7SWyllys Ingersoll 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1683ccf625adSWyllys Ingersoll #ifdef DEBUG
1684ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
1685*e9fe7b35SJason King 		    __func__);
1686ccf625adSWyllys Ingersoll #endif
168747e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
168847e946e7SWyllys Ingersoll 	}
168947e946e7SWyllys Ingersoll 
169047e946e7SWyllys Ingersoll 	switch (cmd) {
169147e946e7SWyllys Ingersoll 	case DDI_INFO_DEVT2DEVINFO:
169247e946e7SWyllys Ingersoll 		*resultp = tpm->dip;
169347e946e7SWyllys Ingersoll 		break;
169447e946e7SWyllys Ingersoll 	case DDI_INFO_DEVT2INSTANCE:
169547e946e7SWyllys Ingersoll 		*resultp = 0;
169647e946e7SWyllys Ingersoll 		break;
169747e946e7SWyllys Ingersoll 	default:
1698ccf625adSWyllys Ingersoll #ifdef DEBUG
1699*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: cmd %d is not implemented", __func__,
1700*e9fe7b35SJason King 		    cmd);
1701ccf625adSWyllys Ingersoll #endif
170247e946e7SWyllys Ingersoll 		return (DDI_FAILURE);
170347e946e7SWyllys Ingersoll 	}
170447e946e7SWyllys Ingersoll 	return (DDI_SUCCESS);
170547e946e7SWyllys Ingersoll }
170647e946e7SWyllys Ingersoll 
170747e946e7SWyllys Ingersoll /*
170847e946e7SWyllys Ingersoll  * Driver entry points
170947e946e7SWyllys Ingersoll  */
171047e946e7SWyllys Ingersoll 
171147e946e7SWyllys Ingersoll /*ARGSUSED*/
171247e946e7SWyllys Ingersoll static int
tpm_open(dev_t * devp,int flag,int otyp,cred_t * cred)171347e946e7SWyllys Ingersoll tpm_open(dev_t *devp, int flag, int otyp, cred_t *cred)
171447e946e7SWyllys Ingersoll {
171547e946e7SWyllys Ingersoll 	int instance;
171647e946e7SWyllys Ingersoll 	tpm_state_t *tpm;
171747e946e7SWyllys Ingersoll 
171847e946e7SWyllys Ingersoll 	ASSERT(devp != NULL);
171947e946e7SWyllys Ingersoll 
172047e946e7SWyllys Ingersoll 	instance = getminor(*devp);
172147e946e7SWyllys Ingersoll 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1722ccf625adSWyllys Ingersoll #ifdef DEBUG
1723ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
1724*e9fe7b35SJason King 		    __func__);
1725ccf625adSWyllys Ingersoll #endif
172647e946e7SWyllys Ingersoll 		return (ENXIO);
172747e946e7SWyllys Ingersoll 	}
172847e946e7SWyllys Ingersoll 	if (otyp != OTYP_CHR) {
1729ccf625adSWyllys Ingersoll #ifdef DEBUG
1730ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: otyp(%d) != OTYP_CHR(%d)",
1731*e9fe7b35SJason King 		    __func__, otyp, OTYP_CHR);
1732ccf625adSWyllys Ingersoll #endif
173347e946e7SWyllys Ingersoll 		return (EINVAL);
173447e946e7SWyllys Ingersoll 	}
17358d26100cSWyllys Ingersoll 	TPM_EXCLUSIVE_LOCK(tpm);
173647e946e7SWyllys Ingersoll 
173747e946e7SWyllys Ingersoll 	mutex_enter(&tpm->dev_lock);
173847e946e7SWyllys Ingersoll 	if (tpm->dev_held) {
1739ccf625adSWyllys Ingersoll #ifdef DEBUG
1740ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: the device is already being used",
1741*e9fe7b35SJason King 		    __func__);
1742ccf625adSWyllys Ingersoll #endif
174347e946e7SWyllys Ingersoll 		mutex_exit(&tpm->dev_lock);
174447e946e7SWyllys Ingersoll 		return (EBUSY);
174547e946e7SWyllys Ingersoll 	}
174647e946e7SWyllys Ingersoll 
174747e946e7SWyllys Ingersoll 	/* The device is free so mark it busy */
174847e946e7SWyllys Ingersoll 	tpm->dev_held = 1;
174947e946e7SWyllys Ingersoll 	mutex_exit(&tpm->dev_lock);
175047e946e7SWyllys Ingersoll 
175147e946e7SWyllys Ingersoll 	return (0);
175247e946e7SWyllys Ingersoll }
175347e946e7SWyllys Ingersoll 
175447e946e7SWyllys Ingersoll /*ARGSUSED*/
175547e946e7SWyllys Ingersoll static int
tpm_close(dev_t dev,int flag,int otyp,cred_t * cred)175647e946e7SWyllys Ingersoll tpm_close(dev_t dev, int flag, int otyp, cred_t *cred)
175747e946e7SWyllys Ingersoll {
175847e946e7SWyllys Ingersoll 	int instance;
175947e946e7SWyllys Ingersoll 	tpm_state_t *tpm;
176047e946e7SWyllys Ingersoll 
176147e946e7SWyllys Ingersoll 	instance = getminor(dev);
176247e946e7SWyllys Ingersoll 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1763ccf625adSWyllys Ingersoll #ifdef DEBUG
1764ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
1765*e9fe7b35SJason King 		    __func__);
1766ccf625adSWyllys Ingersoll #endif
176747e946e7SWyllys Ingersoll 		return (ENXIO);
176847e946e7SWyllys Ingersoll 	}
176947e946e7SWyllys Ingersoll 	if (otyp != OTYP_CHR) {
1770ccf625adSWyllys Ingersoll #ifdef DEBUG
1771ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: otyp(%d) != OTYP_CHR(%d)",
1772*e9fe7b35SJason King 		    __func__, otyp, OTYP_CHR);
1773ccf625adSWyllys Ingersoll #endif
177447e946e7SWyllys Ingersoll 		return (EINVAL);
177547e946e7SWyllys Ingersoll 	}
17768d26100cSWyllys Ingersoll 	TPM_EXCLUSIVE_LOCK(tpm);
177747e946e7SWyllys Ingersoll 
177847e946e7SWyllys Ingersoll 	ASSERT(tpm->dev_held);
177947e946e7SWyllys Ingersoll 
178047e946e7SWyllys Ingersoll 	mutex_enter(&tpm->dev_lock);
178147e946e7SWyllys Ingersoll 	ASSERT(mutex_owned(&tpm->dev_lock));
178247e946e7SWyllys Ingersoll 	tpm->dev_held = 0;
178347e946e7SWyllys Ingersoll 	mutex_exit(&tpm->dev_lock);
178447e946e7SWyllys Ingersoll 
178547e946e7SWyllys Ingersoll 	return (0);
178647e946e7SWyllys Ingersoll }
178747e946e7SWyllys Ingersoll 
178847e946e7SWyllys Ingersoll /*ARGSUSED*/
178947e946e7SWyllys Ingersoll static int
tpm_read(dev_t dev,struct uio * uiop,cred_t * credp)179047e946e7SWyllys Ingersoll tpm_read(dev_t dev, struct uio *uiop, cred_t *credp)
179147e946e7SWyllys Ingersoll {
179247e946e7SWyllys Ingersoll 	int ret;
179347e946e7SWyllys Ingersoll 	uint32_t size;
179447e946e7SWyllys Ingersoll 	int instance;
179547e946e7SWyllys Ingersoll 	tpm_state_t *tpm;
179647e946e7SWyllys Ingersoll 
179747e946e7SWyllys Ingersoll 	instance = getminor(dev);
179847e946e7SWyllys Ingersoll 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1799ccf625adSWyllys Ingersoll #ifdef DEBUG
1800ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
1801*e9fe7b35SJason King 		    __func__);
1802ccf625adSWyllys Ingersoll #endif
180347e946e7SWyllys Ingersoll 		return (ENXIO);
180447e946e7SWyllys Ingersoll 	}
180547e946e7SWyllys Ingersoll 	if (uiop == NULL) {
1806ccf625adSWyllys Ingersoll #ifdef DEBUG
1807*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: passed in uiop is NULL", __func__);
1808ccf625adSWyllys Ingersoll #endif
180947e946e7SWyllys Ingersoll 		return (EFAULT);
181047e946e7SWyllys Ingersoll 	}
181147e946e7SWyllys Ingersoll 
18128d26100cSWyllys Ingersoll 	TPM_EXCLUSIVE_LOCK(tpm);
181347e946e7SWyllys Ingersoll 
181447e946e7SWyllys Ingersoll 	/* Receive the data after requiring the lock */
18158d26100cSWyllys Ingersoll 	ret = tpm_io_lock(tpm);
181647e946e7SWyllys Ingersoll 
181747e946e7SWyllys Ingersoll 	/* Timeout reached */
18188d26100cSWyllys Ingersoll 	if (ret)
181947e946e7SWyllys Ingersoll 		return (ret);
182047e946e7SWyllys Ingersoll 
182147e946e7SWyllys Ingersoll 	if (uiop->uio_resid > tpm->bufsize) {
1822ccf625adSWyllys Ingersoll #ifdef DEBUG
1823ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: read_in data is bigger "
182447e946e7SWyllys Ingersoll 		    "than tpm->bufsize:read in:%d, bufsiz:%d",
1825*e9fe7b35SJason King 		    __func__, (int)uiop->uio_resid, (int)tpm->bufsize);
1826ccf625adSWyllys Ingersoll #endif
182747e946e7SWyllys Ingersoll 		ret = EIO;
182847e946e7SWyllys Ingersoll 		goto OUT;
182947e946e7SWyllys Ingersoll 	}
183047e946e7SWyllys Ingersoll 
183147e946e7SWyllys Ingersoll 	ret = tis_recv_data(tpm, tpm->iobuf, tpm->bufsize);
183247e946e7SWyllys Ingersoll 	if (ret < TPM_HEADER_SIZE) {
1833ccf625adSWyllys Ingersoll #ifdef DEBUG
1834*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: tis_recv_data returned error", __func__);
1835ccf625adSWyllys Ingersoll #endif
183647e946e7SWyllys Ingersoll 		ret = EIO;
183747e946e7SWyllys Ingersoll 		goto OUT;
183847e946e7SWyllys Ingersoll 	}
183947e946e7SWyllys Ingersoll 
184047e946e7SWyllys Ingersoll 	size = load32(tpm->iobuf, 2);
184147e946e7SWyllys Ingersoll 	if (ret != size) {
1842ccf625adSWyllys Ingersoll #ifdef DEBUG
1843ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: tis_recv_data:"
184447e946e7SWyllys Ingersoll 		    "expected size=%d, actually read=%d",
1845*e9fe7b35SJason King 		    __func__, size, ret);
1846ccf625adSWyllys Ingersoll #endif
184747e946e7SWyllys Ingersoll 		ret = EIO;
184847e946e7SWyllys Ingersoll 		goto OUT;
184947e946e7SWyllys Ingersoll 	}
185047e946e7SWyllys Ingersoll 
185147e946e7SWyllys Ingersoll 	/* Send the buffer from the kernel to the userspace */
185247e946e7SWyllys Ingersoll 	ret = uiomove(tpm->iobuf, size, UIO_READ, uiop);
185347e946e7SWyllys Ingersoll 	if (ret) {
1854ccf625adSWyllys Ingersoll #ifdef DEBUG
1855*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: uiomove returned error", __func__);
1856ccf625adSWyllys Ingersoll #endif
185747e946e7SWyllys Ingersoll 		goto OUT;
185847e946e7SWyllys Ingersoll 	}
185947e946e7SWyllys Ingersoll 
186047e946e7SWyllys Ingersoll 	/* Zeroize the buffer... */
186147e946e7SWyllys Ingersoll 	bzero(tpm->iobuf, tpm->bufsize);
186247e946e7SWyllys Ingersoll 	ret = DDI_SUCCESS;
186347e946e7SWyllys Ingersoll OUT:
186447e946e7SWyllys Ingersoll 	/* We are done now: wake up the waiting threads */
186547e946e7SWyllys Ingersoll 	tpm_unlock(tpm);
186647e946e7SWyllys Ingersoll 
186747e946e7SWyllys Ingersoll 	return (ret);
186847e946e7SWyllys Ingersoll }
186947e946e7SWyllys Ingersoll 
187047e946e7SWyllys Ingersoll /*ARGSUSED*/
187147e946e7SWyllys Ingersoll static int
tpm_write(dev_t dev,struct uio * uiop,cred_t * credp)187247e946e7SWyllys Ingersoll tpm_write(dev_t dev, struct uio *uiop, cred_t *credp)
187347e946e7SWyllys Ingersoll {
187447e946e7SWyllys Ingersoll 	int ret;
187547e946e7SWyllys Ingersoll 	size_t len;
187647e946e7SWyllys Ingersoll 	uint32_t size;
187747e946e7SWyllys Ingersoll 	int instance;
187847e946e7SWyllys Ingersoll 	tpm_state_t *tpm;
187947e946e7SWyllys Ingersoll 
188047e946e7SWyllys Ingersoll 	instance = getminor(dev);
188147e946e7SWyllys Ingersoll 	if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1882ccf625adSWyllys Ingersoll #ifdef DEBUG
1883ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
1884*e9fe7b35SJason King 		    __func__);
1885ccf625adSWyllys Ingersoll #endif
188647e946e7SWyllys Ingersoll 		return (ENXIO);
188747e946e7SWyllys Ingersoll 	}
188847e946e7SWyllys Ingersoll 
188947e946e7SWyllys Ingersoll 	if (uiop == NULL) {
1890ccf625adSWyllys Ingersoll #ifdef DEBUG
1891*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: passed in uiop is NULL", __func__);
1892ccf625adSWyllys Ingersoll #endif
189347e946e7SWyllys Ingersoll 		return (EFAULT);
189447e946e7SWyllys Ingersoll 	}
189547e946e7SWyllys Ingersoll 
18968d26100cSWyllys Ingersoll 	TPM_EXCLUSIVE_LOCK(tpm);
189747e946e7SWyllys Ingersoll 
189847e946e7SWyllys Ingersoll 	len = uiop->uio_resid;
189947e946e7SWyllys Ingersoll 	if (len == 0) {
1900ccf625adSWyllys Ingersoll #ifdef DEBUG
1901*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: requested read of len 0", __func__);
1902ccf625adSWyllys Ingersoll #endif
190347e946e7SWyllys Ingersoll 		return (0);
190447e946e7SWyllys Ingersoll 	}
190547e946e7SWyllys Ingersoll 
190647e946e7SWyllys Ingersoll 	/* Get the lock for using iobuf */
19078d26100cSWyllys Ingersoll 	ret = tpm_io_lock(tpm);
190847e946e7SWyllys Ingersoll 	/* Timeout Reached */
19098d26100cSWyllys Ingersoll 	if (ret)
191047e946e7SWyllys Ingersoll 		return (ret);
191147e946e7SWyllys Ingersoll 
191247e946e7SWyllys Ingersoll 	/* Copy the header and parse the structure to find out the size... */
191347e946e7SWyllys Ingersoll 	ret = uiomove(tpm->iobuf, TPM_HEADER_SIZE, UIO_WRITE, uiop);
191447e946e7SWyllys Ingersoll 	if (ret) {
1915ccf625adSWyllys Ingersoll #ifdef DEBUG
1916ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: uiomove returned error"
191747e946e7SWyllys Ingersoll 		    "while getting the the header",
1918*e9fe7b35SJason King 		    __func__);
1919ccf625adSWyllys Ingersoll #endif
192047e946e7SWyllys Ingersoll 		goto OUT;
192147e946e7SWyllys Ingersoll 	}
192247e946e7SWyllys Ingersoll 
192347e946e7SWyllys Ingersoll 	/* Get the buffersize from the command buffer structure */
192447e946e7SWyllys Ingersoll 	size = load32(tpm->iobuf, TPM_PARAMSIZE_OFFSET);
192547e946e7SWyllys Ingersoll 
192647e946e7SWyllys Ingersoll 	/* Copy the command to the contiguous buffer */
192747e946e7SWyllys Ingersoll 	if (size > tpm->bufsize) {
1928ccf625adSWyllys Ingersoll #ifdef DEBUG
1929ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: size %d is greater than "
1930ccf625adSWyllys Ingersoll 		    "the tpm input buffer size %d",
1931*e9fe7b35SJason King 		    __func__, (int)size, (int)tpm->bufsize);
1932ccf625adSWyllys Ingersoll #endif
193347e946e7SWyllys Ingersoll 		ret = ENXIO;
193447e946e7SWyllys Ingersoll 		goto OUT;
193547e946e7SWyllys Ingersoll 	}
193647e946e7SWyllys Ingersoll 
193747e946e7SWyllys Ingersoll 	/* Copy the buffer from the userspace to kernel */
193847e946e7SWyllys Ingersoll 	ret = uiomove(tpm->iobuf+TPM_HEADER_SIZE, size-TPM_HEADER_SIZE,
193947e946e7SWyllys Ingersoll 	    UIO_WRITE, uiop);
194047e946e7SWyllys Ingersoll 
194147e946e7SWyllys Ingersoll 	if (ret) {
1942ccf625adSWyllys Ingersoll #ifdef DEBUG
1943ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!%s: uiomove returned error"
1944*e9fe7b35SJason King 		    "while getting the rest of the command", __func__);
1945ccf625adSWyllys Ingersoll #endif
194647e946e7SWyllys Ingersoll 		goto OUT;
194747e946e7SWyllys Ingersoll 	}
194847e946e7SWyllys Ingersoll 
194947e946e7SWyllys Ingersoll 	/* Send the command */
195047e946e7SWyllys Ingersoll 	ret = tis_send_data(tpm, tpm->iobuf, size);
195147e946e7SWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
1952ccf625adSWyllys Ingersoll #ifdef DEBUG
1953*e9fe7b35SJason King 		cmn_err(CE_WARN, "!%s: tis_send_data returned error", __func__);
1954ccf625adSWyllys Ingersoll #endif
195547e946e7SWyllys Ingersoll 		ret = EFAULT;
195647e946e7SWyllys Ingersoll 		goto OUT;
195747e946e7SWyllys Ingersoll 	}
195847e946e7SWyllys Ingersoll 
195947e946e7SWyllys Ingersoll 	/* Zeroize the buffer... */
196047e946e7SWyllys Ingersoll 	bzero(tpm->iobuf, tpm->bufsize);
196147e946e7SWyllys Ingersoll 	ret = DDI_SUCCESS;
196247e946e7SWyllys Ingersoll OUT:
196347e946e7SWyllys Ingersoll 	tpm_unlock(tpm);
196447e946e7SWyllys Ingersoll 	return (ret);
196547e946e7SWyllys Ingersoll }
196647e946e7SWyllys Ingersoll 
196747e946e7SWyllys Ingersoll /*
196847e946e7SWyllys Ingersoll  * This is to deal with the contentions for the iobuf
196947e946e7SWyllys Ingersoll  */
197047e946e7SWyllys Ingersoll static inline int
tpm_io_lock(tpm_state_t * tpm)19718d26100cSWyllys Ingersoll tpm_io_lock(tpm_state_t *tpm)
197247e946e7SWyllys Ingersoll {
197347e946e7SWyllys Ingersoll 	int ret;
197447e946e7SWyllys Ingersoll 	clock_t timeout;
197547e946e7SWyllys Ingersoll 
197647e946e7SWyllys Ingersoll 	mutex_enter(&tpm->iobuf_lock);
197747e946e7SWyllys Ingersoll 	ASSERT(mutex_owned(&tpm->iobuf_lock));
197847e946e7SWyllys Ingersoll 
197947e946e7SWyllys Ingersoll 	timeout = ddi_get_lbolt() + drv_usectohz(TPM_IO_TIMEOUT);
198047e946e7SWyllys Ingersoll 
198147e946e7SWyllys Ingersoll 	/* Wait until the iobuf becomes free with the timeout */
198247e946e7SWyllys Ingersoll 	while (tpm->iobuf_inuse) {
198347e946e7SWyllys Ingersoll 		ret = cv_timedwait(&tpm->iobuf_cv, &tpm->iobuf_lock, timeout);
198447e946e7SWyllys Ingersoll 		if (ret <= 0) {
198547e946e7SWyllys Ingersoll 			/* Timeout reached */
198647e946e7SWyllys Ingersoll 			mutex_exit(&tpm->iobuf_lock);
19878d26100cSWyllys Ingersoll #ifdef DEBUG
1988ccf625adSWyllys Ingersoll 			cmn_err(CE_WARN, "!tpm_io_lock:iorequest timed out");
19898d26100cSWyllys Ingersoll #endif
1990b693132fSjmcp 			return (ETIME);
199147e946e7SWyllys Ingersoll 		}
199247e946e7SWyllys Ingersoll 	}
199347e946e7SWyllys Ingersoll 	tpm->iobuf_inuse = 1;
199447e946e7SWyllys Ingersoll 	mutex_exit(&tpm->iobuf_lock);
199547e946e7SWyllys Ingersoll 	return (0);
199647e946e7SWyllys Ingersoll }
199747e946e7SWyllys Ingersoll 
199847e946e7SWyllys Ingersoll /*
199947e946e7SWyllys Ingersoll  * This is to deal with the contentions for the iobuf
200047e946e7SWyllys Ingersoll  */
200147e946e7SWyllys Ingersoll static inline void
tpm_unlock(tpm_state_t * tpm)200247e946e7SWyllys Ingersoll tpm_unlock(tpm_state_t *tpm)
200347e946e7SWyllys Ingersoll {
200447e946e7SWyllys Ingersoll 	/* Wake up the waiting threads */
200547e946e7SWyllys Ingersoll 	mutex_enter(&tpm->iobuf_lock);
200647e946e7SWyllys Ingersoll 	ASSERT(tpm->iobuf_inuse == 1 && mutex_owned(&tpm->iobuf_lock));
200747e946e7SWyllys Ingersoll 	tpm->iobuf_inuse = 0;
200847e946e7SWyllys Ingersoll 	cv_broadcast(&tpm->iobuf_cv);
200947e946e7SWyllys Ingersoll 	mutex_exit(&tpm->iobuf_lock);
201047e946e7SWyllys Ingersoll }
20118d26100cSWyllys Ingersoll 
20128d26100cSWyllys Ingersoll #ifdef KCF_TPM_RNG_PROVIDER
20138d26100cSWyllys Ingersoll /*
20148d26100cSWyllys Ingersoll  * Random number generator entry points
20158d26100cSWyllys Ingersoll  */
20168d26100cSWyllys Ingersoll static void
strncpy_spacepad(uchar_t * s1,char * s2,int n)20178d26100cSWyllys Ingersoll strncpy_spacepad(uchar_t *s1, char *s2, int n)
20188d26100cSWyllys Ingersoll {
20198d26100cSWyllys Ingersoll 	int s2len = strlen(s2);
20208d26100cSWyllys Ingersoll 	(void) strncpy((char *)s1, s2, n);
20218d26100cSWyllys Ingersoll 	if (s2len < n)
20228d26100cSWyllys Ingersoll 		(void) memset(s1 + s2len, ' ', n - s2len);
20238d26100cSWyllys Ingersoll }
20248d26100cSWyllys Ingersoll 
20258d26100cSWyllys Ingersoll /*ARGSUSED*/
20268d26100cSWyllys Ingersoll static int
tpmrng_ext_info(crypto_provider_handle_t prov,crypto_provider_ext_info_t * ext_info,crypto_req_handle_t cfreq)20278d26100cSWyllys Ingersoll tpmrng_ext_info(crypto_provider_handle_t prov,
20288d26100cSWyllys Ingersoll     crypto_provider_ext_info_t *ext_info,
20298d26100cSWyllys Ingersoll     crypto_req_handle_t cfreq)
20308d26100cSWyllys Ingersoll {
20318d26100cSWyllys Ingersoll 	tpm_state_t *tpm = (tpm_state_t *)prov;
20328d26100cSWyllys Ingersoll 	char buf[64];
20338d26100cSWyllys Ingersoll 
20348d26100cSWyllys Ingersoll 	if (tpm == NULL)
20358d26100cSWyllys Ingersoll 		return (DDI_FAILURE);
20368d26100cSWyllys Ingersoll 
20378d26100cSWyllys Ingersoll 	strncpy_spacepad(ext_info->ei_manufacturerID,
20388d26100cSWyllys Ingersoll 	    (char *)tpm->vers_info.tpmVendorID,
20398d26100cSWyllys Ingersoll 	    sizeof (ext_info->ei_manufacturerID));
20408d26100cSWyllys Ingersoll 
20418d26100cSWyllys Ingersoll 	strncpy_spacepad(ext_info->ei_model, "0",
20428d26100cSWyllys Ingersoll 	    sizeof (ext_info->ei_model));
20438d26100cSWyllys Ingersoll 	strncpy_spacepad(ext_info->ei_serial_number, "0",
20448d26100cSWyllys Ingersoll 	    sizeof (ext_info->ei_serial_number));
20458d26100cSWyllys Ingersoll 
20468d26100cSWyllys Ingersoll 	ext_info->ei_flags = CRYPTO_EXTF_RNG | CRYPTO_EXTF_SO_PIN_LOCKED;
20478d26100cSWyllys Ingersoll 	ext_info->ei_max_session_count = CRYPTO_EFFECTIVELY_INFINITE;
20488d26100cSWyllys Ingersoll 	ext_info->ei_max_pin_len = 0;
20498d26100cSWyllys Ingersoll 	ext_info->ei_min_pin_len = 0;
20508d26100cSWyllys Ingersoll 	ext_info->ei_total_public_memory = CRYPTO_UNAVAILABLE_INFO;
20518d26100cSWyllys Ingersoll 	ext_info->ei_free_public_memory = CRYPTO_UNAVAILABLE_INFO;
20528d26100cSWyllys Ingersoll 	ext_info->ei_total_private_memory = CRYPTO_UNAVAILABLE_INFO;
20538d26100cSWyllys Ingersoll 	ext_info->ei_free_public_memory = CRYPTO_UNAVAILABLE_INFO;
20548d26100cSWyllys Ingersoll 	ext_info->ei_time[0] = 0;
20558d26100cSWyllys Ingersoll 
20568d26100cSWyllys Ingersoll 	ext_info->ei_hardware_version.cv_major = tpm->vers_info.version.major;
20578d26100cSWyllys Ingersoll 	ext_info->ei_hardware_version.cv_minor = tpm->vers_info.version.minor;
20588d26100cSWyllys Ingersoll 	ext_info->ei_firmware_version.cv_major =
20598d26100cSWyllys Ingersoll 	    tpm->vers_info.version.revMajor;
20608d26100cSWyllys Ingersoll 	ext_info->ei_firmware_version.cv_minor =
20618d26100cSWyllys Ingersoll 	    tpm->vers_info.version.revMinor;
20628d26100cSWyllys Ingersoll 
20638d26100cSWyllys Ingersoll 	(void) snprintf(buf, sizeof (buf), "tpmrng TPM RNG");
20648d26100cSWyllys Ingersoll 
20658d26100cSWyllys Ingersoll 	strncpy_spacepad(ext_info->ei_label, buf,
20668d26100cSWyllys Ingersoll 	    sizeof (ext_info->ei_label));
20678d26100cSWyllys Ingersoll #undef	BUFSZ
20688d26100cSWyllys Ingersoll 	return (CRYPTO_SUCCESS);
20698d26100cSWyllys Ingersoll 
20708d26100cSWyllys Ingersoll }
20718d26100cSWyllys Ingersoll 
20728d26100cSWyllys Ingersoll static int
tpmrng_register(tpm_state_t * tpm)20738d26100cSWyllys Ingersoll tpmrng_register(tpm_state_t *tpm)
20748d26100cSWyllys Ingersoll {
20758d26100cSWyllys Ingersoll 	int		ret;
20768d26100cSWyllys Ingersoll 	char		ID[64];
20778d26100cSWyllys Ingersoll 	crypto_mech_name_t	*rngmech;
20788d26100cSWyllys Ingersoll 
20798d26100cSWyllys Ingersoll 	ASSERT(tpm != NULL);
20808d26100cSWyllys Ingersoll 
20818d26100cSWyllys Ingersoll 	(void) snprintf(ID, sizeof (ID), "tpmrng %s", IDENT_TPMRNG);
20828d26100cSWyllys Ingersoll 
20838d26100cSWyllys Ingersoll 	tpmrng_prov_info.pi_provider_description = ID;
20848d26100cSWyllys Ingersoll 	tpmrng_prov_info.pi_provider_dev.pd_hw = tpm->dip;
20858d26100cSWyllys Ingersoll 	tpmrng_prov_info.pi_provider_handle = tpm;
20868d26100cSWyllys Ingersoll 
20878d26100cSWyllys Ingersoll 	ret = crypto_register_provider(&tpmrng_prov_info, &tpm->n_prov);
20888d26100cSWyllys Ingersoll 	if (ret != CRYPTO_SUCCESS) {
20898d26100cSWyllys Ingersoll 		tpm->n_prov = NULL;
20908d26100cSWyllys Ingersoll 		return (DDI_FAILURE);
20918d26100cSWyllys Ingersoll 	}
20928d26100cSWyllys Ingersoll 
20938d26100cSWyllys Ingersoll 	crypto_provider_notification(tpm->n_prov, CRYPTO_PROVIDER_READY);
20948d26100cSWyllys Ingersoll 
20958d26100cSWyllys Ingersoll 	rngmech = kmem_zalloc(strlen("random") + 1, KM_SLEEP);
20968d26100cSWyllys Ingersoll 	(void) memcpy(rngmech, "random", 6);
20978d26100cSWyllys Ingersoll 	ret = crypto_load_dev_disabled("tpm", ddi_get_instance(tpm->dip),
20988d26100cSWyllys Ingersoll 	    1, rngmech);
2099ccf625adSWyllys Ingersoll #ifdef DEBUG
2100ccf625adSWyllys Ingersoll 	if (ret != CRYPTO_SUCCESS)
2101ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!crypto_load_dev_disabled failed (%d)", ret);
2102ccf625adSWyllys Ingersoll #endif
21038d26100cSWyllys Ingersoll 	return (DDI_SUCCESS);
21048d26100cSWyllys Ingersoll }
21058d26100cSWyllys Ingersoll 
21068d26100cSWyllys Ingersoll static int
tpmrng_unregister(tpm_state_t * tpm)21078d26100cSWyllys Ingersoll tpmrng_unregister(tpm_state_t *tpm)
21088d26100cSWyllys Ingersoll {
21098d26100cSWyllys Ingersoll 	int ret;
21108d26100cSWyllys Ingersoll 	ASSERT(tpm != NULL);
21118d26100cSWyllys Ingersoll 	if (tpm->n_prov) {
21128d26100cSWyllys Ingersoll 		ret = crypto_unregister_provider(tpm->n_prov);
21138d26100cSWyllys Ingersoll 		tpm->n_prov = NULL;
21148d26100cSWyllys Ingersoll 		if (ret != CRYPTO_SUCCESS)
21158d26100cSWyllys Ingersoll 			return (DDI_FAILURE);
21168d26100cSWyllys Ingersoll 	}
21178d26100cSWyllys Ingersoll 	return (DDI_SUCCESS);
21188d26100cSWyllys Ingersoll }
21198d26100cSWyllys Ingersoll 
21208d26100cSWyllys Ingersoll /*ARGSUSED*/
21218d26100cSWyllys Ingersoll static void
tpmrng_provider_status(crypto_provider_handle_t provider,uint_t * status)21228d26100cSWyllys Ingersoll tpmrng_provider_status(crypto_provider_handle_t provider, uint_t *status)
21238d26100cSWyllys Ingersoll {
21248d26100cSWyllys Ingersoll 	*status = CRYPTO_PROVIDER_READY;
21258d26100cSWyllys Ingersoll }
21268d26100cSWyllys Ingersoll 
21278d26100cSWyllys Ingersoll /*ARGSUSED*/
21288d26100cSWyllys Ingersoll 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)21298d26100cSWyllys Ingersoll tpmrng_seed_random(crypto_provider_handle_t provider, crypto_session_id_t sid,
21308d26100cSWyllys Ingersoll     uchar_t *buf, size_t len, uint_t entropy_est, uint32_t flags,
21318d26100cSWyllys Ingersoll     crypto_req_handle_t req)
21328d26100cSWyllys Ingersoll {
21338d26100cSWyllys Ingersoll 	int ret;
21348d26100cSWyllys Ingersoll 	tpm_state_t *tpm;
21358d26100cSWyllys Ingersoll 	uint32_t len32;
21368d26100cSWyllys Ingersoll 	/* Max length of seed is 256 bytes, add 14 for header. */
21378d26100cSWyllys Ingersoll 	uint8_t cmdbuf[270] = {
21388d26100cSWyllys Ingersoll 		0, 193,		/* TPM_TAG_RQU COMMAND */
21398d26100cSWyllys Ingersoll 		0, 0, 0, 0x0A,	/* paramsize in bytes */
21408d26100cSWyllys Ingersoll 		0, 0, 0, TPM_ORD_StirRandom,
21418d26100cSWyllys Ingersoll 		0, 0, 0, 0	/* number of input bytes (< 256) */
21428d26100cSWyllys Ingersoll 	};
21438d26100cSWyllys Ingersoll 	uint32_t buflen;
21448d26100cSWyllys Ingersoll 
21458d26100cSWyllys Ingersoll 	if (len == 0 || len > 255 || buf == NULL)
21468d26100cSWyllys Ingersoll 		return (CRYPTO_ARGUMENTS_BAD);
21478d26100cSWyllys Ingersoll 
21488d26100cSWyllys Ingersoll 	tpm = (tpm_state_t *)provider;
21498d26100cSWyllys Ingersoll 	if (tpm == NULL)
21508d26100cSWyllys Ingersoll 		return (CRYPTO_INVALID_CONTEXT);
21518d26100cSWyllys Ingersoll 
21528d26100cSWyllys Ingersoll 	/* Acquire lock for exclusive use of TPM */
21538d26100cSWyllys Ingersoll 	TPM_EXCLUSIVE_LOCK(tpm);
21548d26100cSWyllys Ingersoll 
21558d26100cSWyllys Ingersoll 	ret = tpm_io_lock(tpm);
21568d26100cSWyllys Ingersoll 	/* Timeout reached */
21578d26100cSWyllys Ingersoll 	if (ret)
21588d26100cSWyllys Ingersoll 		return (CRYPTO_BUSY);
21598d26100cSWyllys Ingersoll 
21608d26100cSWyllys Ingersoll 	/* TPM only handles 32 bit length, so truncate if too big. */
21618d26100cSWyllys Ingersoll 	len32 = (uint32_t)len;
21628d26100cSWyllys Ingersoll 	buflen = len32 + 14;
21638d26100cSWyllys Ingersoll 
21648d26100cSWyllys Ingersoll 	/* The length must be in network order */
21658d26100cSWyllys Ingersoll 	buflen = htonl(buflen);
21668d26100cSWyllys Ingersoll 	bcopy(&buflen, cmdbuf + 2, sizeof (uint32_t));
21678d26100cSWyllys Ingersoll 
21688d26100cSWyllys Ingersoll 	/* Convert it back */
21698d26100cSWyllys Ingersoll 	buflen = ntohl(buflen);
21708d26100cSWyllys Ingersoll 
21718d26100cSWyllys Ingersoll 	/* length must be in network order */
21728d26100cSWyllys Ingersoll 	len32 = htonl(len32);
21738d26100cSWyllys Ingersoll 	bcopy(&len32, cmdbuf + 10, sizeof (uint32_t));
21748d26100cSWyllys Ingersoll 
21758d26100cSWyllys Ingersoll 	/* convert it back */
21768d26100cSWyllys Ingersoll 	len32 = ntohl(len32);
21778d26100cSWyllys Ingersoll 
21788d26100cSWyllys Ingersoll 	bcopy(buf,  cmdbuf + 14, len32);
21798d26100cSWyllys Ingersoll 
21808d26100cSWyllys Ingersoll 	ret = itpm_command(tpm, cmdbuf, buflen);
21818d26100cSWyllys Ingersoll 	tpm_unlock(tpm);
21828d26100cSWyllys Ingersoll 
21838d26100cSWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
21848d26100cSWyllys Ingersoll #ifdef DEBUG
2185ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!tpmrng_seed_random failed");
21868d26100cSWyllys Ingersoll #endif
21878d26100cSWyllys Ingersoll 		return (CRYPTO_FAILED);
21888d26100cSWyllys Ingersoll 	}
21898d26100cSWyllys Ingersoll 
21908d26100cSWyllys Ingersoll 	return (CRYPTO_SUCCESS);
21918d26100cSWyllys Ingersoll }
21928d26100cSWyllys Ingersoll 
21938d26100cSWyllys Ingersoll /* ARGSUSED */
21948d26100cSWyllys Ingersoll 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)21958d26100cSWyllys Ingersoll tpmrng_generate_random(crypto_provider_handle_t provider,
21968d26100cSWyllys Ingersoll     crypto_session_id_t sid, uchar_t *buf, size_t len, crypto_req_handle_t req)
21978d26100cSWyllys Ingersoll {
21988d26100cSWyllys Ingersoll 	int ret;
21998d26100cSWyllys Ingersoll 	tpm_state_t *tpm;
22008d26100cSWyllys Ingersoll 	uint8_t hdr[14] = {
22018d26100cSWyllys Ingersoll 		0, 193,		/* TPM_TAG_RQU COMMAND */
22028d26100cSWyllys Ingersoll 		0, 0, 0, 14,	/* paramsize in bytes */
22038d26100cSWyllys Ingersoll 		0, 0, 0, TPM_ORD_GetRandom,
22048d26100cSWyllys Ingersoll 		0, 0, 0, 0
22058d26100cSWyllys Ingersoll 	};
22068d26100cSWyllys Ingersoll 	uint8_t *cmdbuf = NULL;
22078d26100cSWyllys Ingersoll 	uint32_t len32 = (uint32_t)len;
22088d26100cSWyllys Ingersoll 	uint32_t buflen = len32 + sizeof (hdr);
22098d26100cSWyllys Ingersoll 
22108d26100cSWyllys Ingersoll 	if (len == 0 || buf == NULL)
22118d26100cSWyllys Ingersoll 		return (CRYPTO_ARGUMENTS_BAD);
22128d26100cSWyllys Ingersoll 
22138d26100cSWyllys Ingersoll 	tpm = (tpm_state_t *)provider;
22148d26100cSWyllys Ingersoll 	if (tpm == NULL)
22158d26100cSWyllys Ingersoll 		return (CRYPTO_INVALID_CONTEXT);
22168d26100cSWyllys Ingersoll 
22178d26100cSWyllys Ingersoll 	TPM_EXCLUSIVE_LOCK(tpm);
22188d26100cSWyllys Ingersoll 
22198d26100cSWyllys Ingersoll 	ret = tpm_io_lock(tpm);
22208d26100cSWyllys Ingersoll 	/* Timeout reached */
22218d26100cSWyllys Ingersoll 	if (ret)
22228d26100cSWyllys Ingersoll 		return (CRYPTO_BUSY);
22238d26100cSWyllys Ingersoll 
22248d26100cSWyllys Ingersoll 	cmdbuf = kmem_zalloc(buflen, KM_SLEEP);
22258d26100cSWyllys Ingersoll 	bcopy(hdr, cmdbuf, sizeof (hdr));
22268d26100cSWyllys Ingersoll 
22278d26100cSWyllys Ingersoll 	/* Length is written in network byte order */
22288d26100cSWyllys Ingersoll 	len32 = htonl(len32);
22298d26100cSWyllys Ingersoll 	bcopy(&len32, cmdbuf + 10, sizeof (uint32_t));
22308d26100cSWyllys Ingersoll 
22318d26100cSWyllys Ingersoll 	ret = itpm_command(tpm, cmdbuf, buflen);
22328d26100cSWyllys Ingersoll 	if (ret != DDI_SUCCESS) {
22338d26100cSWyllys Ingersoll #ifdef DEBUG
2234ccf625adSWyllys Ingersoll 		cmn_err(CE_WARN, "!tpmrng_generate_random failed");
22358d26100cSWyllys Ingersoll #endif
22368d26100cSWyllys Ingersoll 		kmem_free(cmdbuf, buflen);
22378d26100cSWyllys Ingersoll 		tpm_unlock(tpm);
22388d26100cSWyllys Ingersoll 		return (CRYPTO_FAILED);
22398d26100cSWyllys Ingersoll 	}
22408d26100cSWyllys Ingersoll 
22418d26100cSWyllys Ingersoll 	/* Find out how many bytes were really returned */
22428d26100cSWyllys Ingersoll 	len32 = load32(cmdbuf, 10);
22438d26100cSWyllys Ingersoll 
22448d26100cSWyllys Ingersoll 	/* Copy the random bytes back to the callers buffer */
22458d26100cSWyllys Ingersoll 	bcopy(cmdbuf + 14, buf, len32);
22468d26100cSWyllys Ingersoll 
22478d26100cSWyllys Ingersoll 	kmem_free(cmdbuf, buflen);
22488d26100cSWyllys Ingersoll 	tpm_unlock(tpm);
22498d26100cSWyllys Ingersoll 
22508d26100cSWyllys Ingersoll 	return (CRYPTO_SUCCESS);
22518d26100cSWyllys Ingersoll }
22528d26100cSWyllys Ingersoll #endif /* KCF_TPM_RNG_PROVIDER */
2253