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.
2547e946e7SWyllys Ingersoll */
2647e946e7SWyllys Ingersoll
2747e946e7SWyllys Ingersoll /*
2847e946e7SWyllys Ingersoll * TPM 1.2 Driver for the TPMs that follow TIS v1.2
2947e946e7SWyllys Ingersoll */
3047e946e7SWyllys Ingersoll
3147e946e7SWyllys Ingersoll #include <sys/devops.h> /* used by dev_ops */
3247e946e7SWyllys Ingersoll #include <sys/conf.h> /* used by dev_ops,cb_ops */
3347e946e7SWyllys Ingersoll #include <sys/modctl.h> /* for _init,_info,_fini,mod_* */
3447e946e7SWyllys Ingersoll #include <sys/ddi.h> /* used by all entry points */
3547e946e7SWyllys Ingersoll #include <sys/sunddi.h> /* used by all entry points */
3647e946e7SWyllys Ingersoll #include <sys/cmn_err.h> /* used for debug outputs */
3747e946e7SWyllys Ingersoll #include <sys/types.h> /* used by prop_op, ddi_prop_op */
3847e946e7SWyllys Ingersoll
3947e946e7SWyllys Ingersoll #include <sys/file.h> /* used by open, close */
4047e946e7SWyllys Ingersoll #include <sys/errno.h> /* used by open,close,read,write */
4147e946e7SWyllys Ingersoll #include <sys/open.h> /* used by open,close,read,write */
4247e946e7SWyllys Ingersoll #include <sys/cred.h> /* used by open,close,read */
4347e946e7SWyllys Ingersoll #include <sys/uio.h> /* used by read */
4447e946e7SWyllys Ingersoll #include <sys/stat.h> /* defines S_IFCHR */
4547e946e7SWyllys Ingersoll
4647e946e7SWyllys Ingersoll #include <sys/byteorder.h> /* for ntohs, ntohl, htons, htonl */
4747e946e7SWyllys Ingersoll
488d26100cSWyllys Ingersoll #ifdef sun4v
498d26100cSWyllys Ingersoll #include <sys/hypervisor_api.h>
508d26100cSWyllys Ingersoll #include <sys/hsvc.h>
518d26100cSWyllys Ingersoll #endif
528d26100cSWyllys Ingersoll
5347e946e7SWyllys Ingersoll #include <tss/platform.h> /* from SUNWtss */
5447e946e7SWyllys Ingersoll #include <tss/tpm.h> /* from SUNWtss */
5547e946e7SWyllys Ingersoll
5647e946e7SWyllys Ingersoll #include "tpm_tis.h"
5747e946e7SWyllys Ingersoll #include "tpm_ddi.h"
5847e946e7SWyllys Ingersoll #include "tpm_duration.h"
5947e946e7SWyllys Ingersoll
6047e946e7SWyllys Ingersoll #define TPM_HEADER_SIZE 10
6147e946e7SWyllys Ingersoll typedef enum {
6247e946e7SWyllys Ingersoll TPM_TAG_OFFSET = 0,
6347e946e7SWyllys Ingersoll TPM_PARAMSIZE_OFFSET = 2,
6447e946e7SWyllys Ingersoll TPM_RETURN_OFFSET = 6,
6547e946e7SWyllys Ingersoll TPM_COMMAND_CODE_OFFSET = 6,
6647e946e7SWyllys Ingersoll } TPM_HEADER_OFFSET_T;
6747e946e7SWyllys Ingersoll
6847e946e7SWyllys Ingersoll /*
6947e946e7SWyllys Ingersoll * This is to address some TPMs that does not report the correct duration
7047e946e7SWyllys Ingersoll * and timeouts. In our experience with the production TPMs, we encountered
7147e946e7SWyllys Ingersoll * time errors such as GetCapability command from TPM reporting the timeout
7247e946e7SWyllys Ingersoll * and durations in milliseconds rather than microseconds. Some other TPMs
7347e946e7SWyllys Ingersoll * report the value 0's
7447e946e7SWyllys Ingersoll *
7547e946e7SWyllys Ingersoll * Short Duration is based on section 11.3.4 of TIS speciciation, that
7647e946e7SWyllys Ingersoll * TPM_GetCapability (short duration) commands should not be longer than 750ms
7747e946e7SWyllys Ingersoll * and that section 11.3.7 states that TPM_ContinueSelfTest (medium duration)
7847e946e7SWyllys Ingersoll * should not be longer than 1 second.
7947e946e7SWyllys Ingersoll */
8047e946e7SWyllys Ingersoll #define DEFAULT_SHORT_DURATION 750000
8147e946e7SWyllys Ingersoll #define DEFAULT_MEDIUM_DURATION 1000000
8247e946e7SWyllys Ingersoll #define DEFAULT_LONG_DURATION 300000000
8347e946e7SWyllys Ingersoll #define DEFAULT_TIMEOUT_A 750000
8447e946e7SWyllys Ingersoll #define DEFAULT_TIMEOUT_B 2000000
8547e946e7SWyllys Ingersoll #define DEFAULT_TIMEOUT_C 750000
8647e946e7SWyllys Ingersoll #define DEFAULT_TIMEOUT_D 750000
8747e946e7SWyllys Ingersoll
8847e946e7SWyllys Ingersoll /*
8947e946e7SWyllys Ingersoll * In order to test the 'millisecond bug', we test if DURATIONS and TIMEOUTS
9047e946e7SWyllys Ingersoll * are unreasonably low...such as 10 milliseconds (TPM isn't that fast).
9147e946e7SWyllys Ingersoll * and 400 milliseconds for long duration
9247e946e7SWyllys Ingersoll */
9347e946e7SWyllys Ingersoll #define TEN_MILLISECONDS 10000 /* 10 milliseconds */
9447e946e7SWyllys Ingersoll #define FOUR_HUNDRED_MILLISECONDS 400000 /* 4 hundred milliseconds */
9547e946e7SWyllys Ingersoll
968d26100cSWyllys Ingersoll #define DEFAULT_LOCALITY 0
9747e946e7SWyllys Ingersoll /*
9847e946e7SWyllys Ingersoll * TPM input/output buffer offsets
9947e946e7SWyllys Ingersoll */
10047e946e7SWyllys Ingersoll
10147e946e7SWyllys Ingersoll typedef enum {
10247e946e7SWyllys Ingersoll TPM_CAP_RESPSIZE_OFFSET = 10,
10347e946e7SWyllys Ingersoll TPM_CAP_RESP_OFFSET = 14,
10447e946e7SWyllys Ingersoll } TPM_CAP_RET_OFFSET_T;
10547e946e7SWyllys Ingersoll
10647e946e7SWyllys Ingersoll typedef enum {
10747e946e7SWyllys Ingersoll TPM_CAP_TIMEOUT_A_OFFSET = 14,
10847e946e7SWyllys Ingersoll TPM_CAP_TIMEOUT_B_OFFSET = 18,
10947e946e7SWyllys Ingersoll TPM_CAP_TIMEOUT_C_OFFSET = 22,
11047e946e7SWyllys Ingersoll TPM_CAP_TIMEOUT_D_OFFSET = 26,
11147e946e7SWyllys Ingersoll } TPM_CAP_TIMEOUT_OFFSET_T;
11247e946e7SWyllys Ingersoll
11347e946e7SWyllys Ingersoll typedef enum {
11447e946e7SWyllys Ingersoll TPM_CAP_DUR_SHORT_OFFSET = 14,
11547e946e7SWyllys Ingersoll TPM_CAP_DUR_MEDIUM_OFFSET = 18,
11647e946e7SWyllys Ingersoll TPM_CAP_DUR_LONG_OFFSET = 22,
11747e946e7SWyllys Ingersoll } TPM_CAP_DURATION_OFFSET_T;
11847e946e7SWyllys Ingersoll
11947e946e7SWyllys Ingersoll #define TPM_CAP_VERSION_INFO_OFFSET 14
12047e946e7SWyllys Ingersoll #define TPM_CAP_VERSION_INFO_SIZE 15
12147e946e7SWyllys Ingersoll
12247e946e7SWyllys Ingersoll /*
12347e946e7SWyllys Ingersoll * Internal TPM command functions
12447e946e7SWyllys Ingersoll */
12547e946e7SWyllys Ingersoll static int itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz);
12647e946e7SWyllys Ingersoll static int tpm_get_timeouts(tpm_state_t *tpm);
12747e946e7SWyllys Ingersoll static int tpm_get_duration(tpm_state_t *tpm);
12847e946e7SWyllys Ingersoll static int tpm_get_version(tpm_state_t *tpm);
12947e946e7SWyllys Ingersoll static int tpm_continue_selftest(tpm_state_t *tpm);
13047e946e7SWyllys Ingersoll
13147e946e7SWyllys Ingersoll /*
13247e946e7SWyllys Ingersoll * Internal TIS related functions
13347e946e7SWyllys Ingersoll */
13447e946e7SWyllys Ingersoll static int tpm_wait_for_stat(tpm_state_t *, uint8_t, clock_t);
13547e946e7SWyllys Ingersoll static clock_t tpm_get_ordinal_duration(tpm_state_t *, uint8_t);
13647e946e7SWyllys Ingersoll static int tis_check_active_locality(tpm_state_t *, char);
13747e946e7SWyllys Ingersoll static int tis_request_locality(tpm_state_t *, char);
13847e946e7SWyllys Ingersoll static void tis_release_locality(tpm_state_t *, char, int);
13947e946e7SWyllys Ingersoll static int tis_init(tpm_state_t *);
14047e946e7SWyllys Ingersoll static uint8_t tis_get_status(tpm_state_t *);
14147e946e7SWyllys Ingersoll static int tis_send_data(tpm_state_t *, uint8_t *, size_t);
14247e946e7SWyllys Ingersoll static int tis_recv_data(tpm_state_t *, uint8_t *, size_t);
14347e946e7SWyllys Ingersoll
14447e946e7SWyllys Ingersoll /* Auxilliary */
14547e946e7SWyllys Ingersoll static int receive_data(tpm_state_t *, uint8_t *, size_t);
1468d26100cSWyllys Ingersoll static inline int tpm_io_lock(tpm_state_t *);
14747e946e7SWyllys Ingersoll static inline void tpm_unlock(tpm_state_t *);
14847e946e7SWyllys Ingersoll static void tpm_cleanup(dev_info_t *, tpm_state_t *);
14947e946e7SWyllys Ingersoll
15047e946e7SWyllys Ingersoll /*
15147e946e7SWyllys Ingersoll * Sun DDI/DDK entry points
15247e946e7SWyllys Ingersoll */
15347e946e7SWyllys Ingersoll
15447e946e7SWyllys Ingersoll /* Declaration of autoconfig functions */
15547e946e7SWyllys Ingersoll static int tpm_attach(dev_info_t *, ddi_attach_cmd_t);
15647e946e7SWyllys Ingersoll static int tpm_detach(dev_info_t *, ddi_detach_cmd_t);
15747e946e7SWyllys Ingersoll static int tpm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
15847e946e7SWyllys Ingersoll static int tpm_quiesce(dev_info_t *);
15947e946e7SWyllys Ingersoll /* End of autoconfig functions */
16047e946e7SWyllys Ingersoll
16147e946e7SWyllys Ingersoll /* Declaration of driver entry point functions */
16247e946e7SWyllys Ingersoll static int tpm_open(dev_t *, int, int, cred_t *);
16347e946e7SWyllys Ingersoll static int tpm_close(dev_t, int, int, cred_t *);
16447e946e7SWyllys Ingersoll static int tpm_read(dev_t, struct uio *, cred_t *);
16547e946e7SWyllys Ingersoll static int tpm_write(dev_t, struct uio *, cred_t *);
16647e946e7SWyllys Ingersoll /* End of driver entry point functions */
16747e946e7SWyllys Ingersoll
16847e946e7SWyllys Ingersoll /* cb_ops structure */
16947e946e7SWyllys Ingersoll static struct cb_ops tpm_cb_ops = {
17047e946e7SWyllys Ingersoll tpm_open,
17147e946e7SWyllys Ingersoll tpm_close,
17247e946e7SWyllys Ingersoll nodev, /* no strategy - nodev returns ENXIO */
17347e946e7SWyllys Ingersoll nodev, /* no print */
17447e946e7SWyllys Ingersoll nodev, /* no dump */
17547e946e7SWyllys Ingersoll tpm_read,
17647e946e7SWyllys Ingersoll tpm_write,
17747e946e7SWyllys Ingersoll nodev, /* no ioctl */
17847e946e7SWyllys Ingersoll nodev, /* no devmap */
17947e946e7SWyllys Ingersoll nodev, /* no mmap */
18047e946e7SWyllys Ingersoll nodev, /* no segmap */
18147e946e7SWyllys Ingersoll nochpoll, /* returns ENXIO for non-pollable devices */
18247e946e7SWyllys Ingersoll ddi_prop_op,
18347e946e7SWyllys Ingersoll NULL, /* streamtab struc */
18447e946e7SWyllys Ingersoll D_MP, /* compatibility flags */
18547e946e7SWyllys Ingersoll CB_REV, /* cb_ops revision number */
18647e946e7SWyllys Ingersoll nodev, /* no aread */
18747e946e7SWyllys Ingersoll nodev /* no awrite */
18847e946e7SWyllys Ingersoll };
18947e946e7SWyllys Ingersoll
19047e946e7SWyllys Ingersoll /* dev_ops structure */
19147e946e7SWyllys Ingersoll static struct dev_ops tpm_dev_ops = {
19247e946e7SWyllys Ingersoll DEVO_REV,
19347e946e7SWyllys Ingersoll 0, /* reference count */
19447e946e7SWyllys Ingersoll tpm_getinfo,
19547e946e7SWyllys Ingersoll nulldev, /* no identify - nulldev returns 0 */
19647e946e7SWyllys Ingersoll nulldev,
19747e946e7SWyllys Ingersoll tpm_attach,
19847e946e7SWyllys Ingersoll tpm_detach,
19947e946e7SWyllys Ingersoll nodev, /* no reset - nodev returns ENXIO */
20047e946e7SWyllys Ingersoll &tpm_cb_ops,
20147e946e7SWyllys Ingersoll (struct bus_ops *)NULL,
20247e946e7SWyllys Ingersoll nodev, /* no power */
20347e946e7SWyllys Ingersoll tpm_quiesce
20447e946e7SWyllys Ingersoll };
20547e946e7SWyllys Ingersoll
20647e946e7SWyllys Ingersoll /* modldrv structure */
20747e946e7SWyllys Ingersoll static struct modldrv modldrv = {
20847e946e7SWyllys Ingersoll &mod_driverops, /* Type: This is a driver */
20947e946e7SWyllys Ingersoll "TPM 1.2 driver", /* Name of the module. */
21047e946e7SWyllys Ingersoll &tpm_dev_ops
21147e946e7SWyllys Ingersoll };
21247e946e7SWyllys Ingersoll
21347e946e7SWyllys Ingersoll /* modlinkage structure */
21447e946e7SWyllys Ingersoll static struct modlinkage tpm_ml = {
21547e946e7SWyllys Ingersoll MODREV_1,
21647e946e7SWyllys Ingersoll &modldrv,
21747e946e7SWyllys Ingersoll NULL
21847e946e7SWyllys Ingersoll };
21947e946e7SWyllys Ingersoll
2208d26100cSWyllys Ingersoll
2218d26100cSWyllys Ingersoll #ifdef KCF_TPM_RNG_PROVIDER
2228d26100cSWyllys Ingersoll
2238d26100cSWyllys Ingersoll #define IDENT_TPMRNG "TPM Random Number Generator"
2248d26100cSWyllys Ingersoll
2258d26100cSWyllys Ingersoll #include <sys/crypto/common.h>
2268d26100cSWyllys Ingersoll #include <sys/crypto/impl.h>
2278d26100cSWyllys Ingersoll #include <sys/crypto/spi.h>
2288d26100cSWyllys Ingersoll /*
2298d26100cSWyllys Ingersoll * CSPI information (entry points, provider info, etc.)
2308d26100cSWyllys Ingersoll */
2318d26100cSWyllys Ingersoll static void tpmrng_provider_status(crypto_provider_handle_t, uint_t *);
2328d26100cSWyllys Ingersoll
2338d26100cSWyllys Ingersoll static crypto_control_ops_t tpmrng_control_ops = {
2348d26100cSWyllys Ingersoll tpmrng_provider_status
2358d26100cSWyllys Ingersoll };
2368d26100cSWyllys Ingersoll
2378d26100cSWyllys Ingersoll static int tpmrng_seed_random(crypto_provider_handle_t, crypto_session_id_t,
2388d26100cSWyllys Ingersoll uchar_t *, size_t, uint_t, uint32_t, crypto_req_handle_t);
2398d26100cSWyllys Ingersoll
2408d26100cSWyllys Ingersoll static int tpmrng_generate_random(crypto_provider_handle_t,
2418d26100cSWyllys Ingersoll crypto_session_id_t, uchar_t *, size_t, crypto_req_handle_t);
2428d26100cSWyllys Ingersoll
2438d26100cSWyllys Ingersoll static crypto_random_number_ops_t tpmrng_random_number_ops = {
2448d26100cSWyllys Ingersoll tpmrng_seed_random,
2458d26100cSWyllys Ingersoll tpmrng_generate_random
2468d26100cSWyllys Ingersoll };
2478d26100cSWyllys Ingersoll
2488d26100cSWyllys Ingersoll static int tpmrng_ext_info(crypto_provider_handle_t,
2498d26100cSWyllys Ingersoll crypto_provider_ext_info_t *,
2508d26100cSWyllys Ingersoll crypto_req_handle_t);
2518d26100cSWyllys Ingersoll
2528d26100cSWyllys Ingersoll static crypto_provider_management_ops_t tpmrng_extinfo_op = {
2538d26100cSWyllys Ingersoll tpmrng_ext_info,
2548d26100cSWyllys Ingersoll NULL,
2558d26100cSWyllys Ingersoll NULL,
2568d26100cSWyllys Ingersoll NULL
2578d26100cSWyllys Ingersoll };
2588d26100cSWyllys Ingersoll
2598d26100cSWyllys Ingersoll static int tpmrng_register(tpm_state_t *);
2608d26100cSWyllys Ingersoll static int tpmrng_unregister(tpm_state_t *);
2618d26100cSWyllys Ingersoll
2628d26100cSWyllys Ingersoll static crypto_ops_t tpmrng_crypto_ops = {
2638d26100cSWyllys Ingersoll &tpmrng_control_ops,
2648d26100cSWyllys Ingersoll NULL,
2658d26100cSWyllys Ingersoll NULL,
2668d26100cSWyllys Ingersoll NULL,
2678d26100cSWyllys Ingersoll NULL,
2688d26100cSWyllys Ingersoll NULL,
2698d26100cSWyllys Ingersoll NULL,
2708d26100cSWyllys Ingersoll NULL,
2718d26100cSWyllys Ingersoll &tpmrng_random_number_ops,
2728d26100cSWyllys Ingersoll NULL,
2738d26100cSWyllys Ingersoll NULL,
2748d26100cSWyllys Ingersoll NULL,
2758d26100cSWyllys Ingersoll &tpmrng_extinfo_op,
2768d26100cSWyllys Ingersoll NULL,
2778d26100cSWyllys Ingersoll NULL
2788d26100cSWyllys Ingersoll };
2798d26100cSWyllys Ingersoll
2808d26100cSWyllys Ingersoll static crypto_provider_info_t tpmrng_prov_info = {
2818d26100cSWyllys Ingersoll CRYPTO_SPI_VERSION_2,
2828d26100cSWyllys Ingersoll "TPM Random Number Provider",
2838d26100cSWyllys Ingersoll CRYPTO_HW_PROVIDER,
2848d26100cSWyllys Ingersoll NULL,
2858d26100cSWyllys Ingersoll NULL,
2868d26100cSWyllys Ingersoll &tpmrng_crypto_ops,
2878d26100cSWyllys Ingersoll 0,
2888d26100cSWyllys Ingersoll NULL,
2898d26100cSWyllys Ingersoll 0,
2908d26100cSWyllys Ingersoll NULL
2918d26100cSWyllys Ingersoll };
2928d26100cSWyllys Ingersoll #endif /* KCF_TPM_RNG_PROVIDER */
2938d26100cSWyllys Ingersoll
29447e946e7SWyllys Ingersoll static void *statep = NULL;
29547e946e7SWyllys Ingersoll
29647e946e7SWyllys Ingersoll /*
2978d26100cSWyllys Ingersoll * Inline code to get exclusive lock on the TPM device and to make sure
2988d26100cSWyllys Ingersoll * the device is not suspended. This grabs the primary TPM mutex (pm_mutex)
2998d26100cSWyllys Ingersoll * and then checks the suspend status. If suspended, it will wait until
3008d26100cSWyllys Ingersoll * the device is "resumed" before releasing the pm_mutex and continuing.
3018d26100cSWyllys Ingersoll */
3028d26100cSWyllys Ingersoll #define TPM_EXCLUSIVE_LOCK(tpm) { \
3038d26100cSWyllys Ingersoll mutex_enter(&tpm->pm_mutex); \
3048d26100cSWyllys Ingersoll while (tpm->suspended) \
3058d26100cSWyllys Ingersoll cv_wait(&tpm->suspend_cv, &tpm->pm_mutex); \
3068d26100cSWyllys Ingersoll mutex_exit(&tpm->pm_mutex); }
3078d26100cSWyllys Ingersoll
3088d26100cSWyllys Ingersoll /*
3098d26100cSWyllys Ingersoll * TPM accessor functions
3108d26100cSWyllys Ingersoll */
3118d26100cSWyllys Ingersoll #ifdef sun4v
3128d26100cSWyllys Ingersoll
3138d26100cSWyllys Ingersoll extern uint64_t
3148d26100cSWyllys Ingersoll hcall_tpm_get(uint64_t, uint64_t, uint64_t, uint64_t *);
3158d26100cSWyllys Ingersoll
3168d26100cSWyllys Ingersoll extern uint64_t
3178d26100cSWyllys Ingersoll hcall_tpm_put(uint64_t, uint64_t, uint64_t, uint64_t);
3188d26100cSWyllys Ingersoll
3198d26100cSWyllys Ingersoll static inline uint8_t
tpm_get8(tpm_state_t * tpm,unsigned long offset)3208d26100cSWyllys Ingersoll tpm_get8(tpm_state_t *tpm, unsigned long offset)
3218d26100cSWyllys Ingersoll {
3228d26100cSWyllys Ingersoll uint64_t value;
3238d26100cSWyllys Ingersoll
3248d26100cSWyllys Ingersoll ASSERT(tpm != NULL);
3258d26100cSWyllys Ingersoll (void) hcall_tpm_get(tpm->locality, offset, sizeof (uint8_t), &value);
3268d26100cSWyllys Ingersoll return ((uint8_t)value);
3278d26100cSWyllys Ingersoll }
3288d26100cSWyllys Ingersoll
3298d26100cSWyllys Ingersoll static inline uint32_t
tpm_get32(tpm_state_t * tpm,unsigned long offset)3308d26100cSWyllys Ingersoll tpm_get32(tpm_state_t *tpm, unsigned long offset)
3318d26100cSWyllys Ingersoll {
3328d26100cSWyllys Ingersoll uint64_t value;
3338d26100cSWyllys Ingersoll
3348d26100cSWyllys Ingersoll ASSERT(tpm != NULL);
3358d26100cSWyllys Ingersoll (void) hcall_tpm_get(tpm->locality, offset, sizeof (uint32_t), &value);
3368d26100cSWyllys Ingersoll return ((uint32_t)value);
3378d26100cSWyllys Ingersoll }
3388d26100cSWyllys Ingersoll
3398d26100cSWyllys Ingersoll static inline void
tpm_put8(tpm_state_t * tpm,unsigned long offset,uint8_t value)3408d26100cSWyllys Ingersoll tpm_put8(tpm_state_t *tpm, unsigned long offset, uint8_t value)
3418d26100cSWyllys Ingersoll {
3428d26100cSWyllys Ingersoll ASSERT(tpm != NULL);
3438d26100cSWyllys Ingersoll (void) hcall_tpm_put(tpm->locality, offset, sizeof (uint8_t), value);
3448d26100cSWyllys Ingersoll }
3458d26100cSWyllys Ingersoll
3468d26100cSWyllys Ingersoll #else
3478d26100cSWyllys Ingersoll
3488d26100cSWyllys Ingersoll static inline uint8_t
tpm_get8(tpm_state_t * tpm,unsigned long offset)3498d26100cSWyllys Ingersoll tpm_get8(tpm_state_t *tpm, unsigned long offset)
3508d26100cSWyllys Ingersoll {
3518d26100cSWyllys Ingersoll ASSERT(tpm != NULL);
3528d26100cSWyllys Ingersoll
3538d26100cSWyllys Ingersoll return (ddi_get8(tpm->handle,
3548d26100cSWyllys Ingersoll (uint8_t *)(TPM_LOCALITY_OFFSET(tpm->locality) |
3558d26100cSWyllys Ingersoll (uintptr_t)tpm->addr + offset)));
3568d26100cSWyllys Ingersoll }
3578d26100cSWyllys Ingersoll
3588d26100cSWyllys Ingersoll static inline uint32_t
tpm_get32(tpm_state_t * tpm,unsigned long offset)3598d26100cSWyllys Ingersoll tpm_get32(tpm_state_t *tpm, unsigned long offset)
3608d26100cSWyllys Ingersoll {
3618d26100cSWyllys Ingersoll ASSERT(tpm != NULL);
3628d26100cSWyllys Ingersoll return (ddi_get32(tpm->handle,
3638d26100cSWyllys Ingersoll (uint32_t *)(TPM_LOCALITY_OFFSET(tpm->locality) |
3648d26100cSWyllys Ingersoll (uintptr_t)tpm->addr + offset)));
3658d26100cSWyllys Ingersoll }
3668d26100cSWyllys Ingersoll
3678d26100cSWyllys Ingersoll static inline void
tpm_put8(tpm_state_t * tpm,unsigned long offset,uint8_t value)3688d26100cSWyllys Ingersoll tpm_put8(tpm_state_t *tpm, unsigned long offset, uint8_t value)
3698d26100cSWyllys Ingersoll {
3708d26100cSWyllys Ingersoll ASSERT(tpm != NULL);
3718d26100cSWyllys Ingersoll ddi_put8(tpm->handle,
3728d26100cSWyllys Ingersoll (uint8_t *)(TPM_LOCALITY_OFFSET(tpm->locality) |
3738d26100cSWyllys Ingersoll (uintptr_t)tpm->addr + offset), value);
3748d26100cSWyllys Ingersoll }
3758d26100cSWyllys Ingersoll
3768d26100cSWyllys Ingersoll #endif /* sun4v */
3778d26100cSWyllys Ingersoll
3788d26100cSWyllys Ingersoll /*
37947e946e7SWyllys Ingersoll * TPM commands to get the TPM's properties, e.g.,timeout
38047e946e7SWyllys Ingersoll */
38147e946e7SWyllys Ingersoll /*ARGSUSED*/
38247e946e7SWyllys Ingersoll static int
tpm_quiesce(dev_info_t * dip)38347e946e7SWyllys Ingersoll tpm_quiesce(dev_info_t *dip)
38447e946e7SWyllys Ingersoll {
38547e946e7SWyllys Ingersoll return (DDI_SUCCESS);
38647e946e7SWyllys Ingersoll }
38747e946e7SWyllys Ingersoll
38847e946e7SWyllys Ingersoll static uint32_t
load32(uchar_t * ptr,uint32_t offset)38947e946e7SWyllys Ingersoll load32(uchar_t *ptr, uint32_t offset)
39047e946e7SWyllys Ingersoll {
39147e946e7SWyllys Ingersoll uint32_t val;
39247e946e7SWyllys Ingersoll bcopy(ptr + offset, &val, sizeof (uint32_t));
39347e946e7SWyllys Ingersoll
39447e946e7SWyllys Ingersoll return (ntohl(val));
39547e946e7SWyllys Ingersoll }
39647e946e7SWyllys Ingersoll
39747e946e7SWyllys Ingersoll /*
39847e946e7SWyllys Ingersoll * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability
39947e946e7SWyllys Ingersoll * with the subcommand TPM_CAP_PROP_TIS_TIMEOUT
40047e946e7SWyllys Ingersoll * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38)
40147e946e7SWyllys Ingersoll */
40247e946e7SWyllys Ingersoll static int
tpm_get_timeouts(tpm_state_t * tpm)40347e946e7SWyllys Ingersoll tpm_get_timeouts(tpm_state_t *tpm)
40447e946e7SWyllys Ingersoll {
40547e946e7SWyllys Ingersoll int ret;
40647e946e7SWyllys Ingersoll uint32_t timeout; /* in milliseconds */
40747e946e7SWyllys Ingersoll uint32_t len;
40847e946e7SWyllys Ingersoll
40947e946e7SWyllys Ingersoll /* The buffer size (30) needs room for 4 timeout values (uint32_t) */
41047e946e7SWyllys Ingersoll uint8_t buf[30] = {
41147e946e7SWyllys Ingersoll 0, 193, /* TPM_TAG_RQU_COMMAND */
41247e946e7SWyllys Ingersoll 0, 0, 0, 22, /* paramsize in bytes */
41347e946e7SWyllys Ingersoll 0, 0, 0, 101, /* TPM_ORD_GetCapability */
41447e946e7SWyllys Ingersoll 0, 0, 0, 5, /* TPM_CAP_Prop */
41547e946e7SWyllys Ingersoll 0, 0, 0, 4, /* SUB_CAP size in bytes */
41647e946e7SWyllys Ingersoll 0, 0, 1, 21 /* TPM_CAP_PROP_TIS_TIMEOUT(0x115) */
41747e946e7SWyllys Ingersoll };
41847e946e7SWyllys Ingersoll char *myname = "tpm_get_timeout";
41947e946e7SWyllys Ingersoll
42047e946e7SWyllys Ingersoll ASSERT(tpm != NULL);
42147e946e7SWyllys Ingersoll
42247e946e7SWyllys Ingersoll ret = itpm_command(tpm, buf, sizeof (buf));
42347e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS) {
424*ccf625adSWyllys Ingersoll #ifdef DEBUG
425*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: itpm_command failed", myname);
426*ccf625adSWyllys 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)) {
438*ccf625adSWyllys Ingersoll #ifdef DEBUG
439*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: capability response size should be %d"
440*ccf625adSWyllys Ingersoll "instead len = %d",
44147e946e7SWyllys Ingersoll myname, (int)(4 * sizeof (uint32_t)), (int)len);
442*ccf625adSWyllys 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)49247e946e7SWyllys Ingersoll tpm_get_duration(tpm_state_t *tpm) {
49347e946e7SWyllys Ingersoll int ret;
49447e946e7SWyllys Ingersoll uint32_t duration;
49547e946e7SWyllys Ingersoll uint32_t len;
49647e946e7SWyllys Ingersoll uint8_t buf[30] = {
49747e946e7SWyllys Ingersoll 0, 193, /* TPM_TAG_RQU_COMMAND */
49847e946e7SWyllys Ingersoll 0, 0, 0, 22, /* paramsize in bytes */
49947e946e7SWyllys Ingersoll 0, 0, 0, 101, /* TPM_ORD_GetCapability */
50047e946e7SWyllys Ingersoll 0, 0, 0, 5, /* TPM_CAP_Prop */
50147e946e7SWyllys Ingersoll 0, 0, 0, 4, /* SUB_CAP size in bytes */
50247e946e7SWyllys Ingersoll 0, 0, 1, 32 /* TPM_CAP_PROP_TIS_DURATION(0x120) */
50347e946e7SWyllys Ingersoll };
50447e946e7SWyllys Ingersoll char *myname = "tpm_get_duration";
50547e946e7SWyllys Ingersoll
50647e946e7SWyllys Ingersoll ASSERT(tpm != NULL);
50747e946e7SWyllys Ingersoll
50847e946e7SWyllys Ingersoll ret = itpm_command(tpm, buf, sizeof (buf));
50947e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS) {
510*ccf625adSWyllys Ingersoll #ifdef DEBUG
511*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: itpm_command failed with ret code: 0x%x",
51247e946e7SWyllys Ingersoll myname, ret);
513*ccf625adSWyllys 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)) {
525*ccf625adSWyllys Ingersoll #ifdef DEBUG
526*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: capability response should be %d, "
52747e946e7SWyllys Ingersoll "instead, it's %d",
52847e946e7SWyllys Ingersoll myname, (int)(3 * sizeof (uint32_t)), (int)len);
529*ccf625adSWyllys 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)56947e946e7SWyllys Ingersoll tpm_get_version(tpm_state_t *tpm) {
57047e946e7SWyllys Ingersoll int ret;
57147e946e7SWyllys Ingersoll uint32_t len;
57247e946e7SWyllys Ingersoll char vendorId[5];
57347e946e7SWyllys Ingersoll /* If this buf is too small, the "vendor specific" data won't fit */
57447e946e7SWyllys Ingersoll uint8_t buf[64] = {
57547e946e7SWyllys Ingersoll 0, 193, /* TPM_TAG_RQU_COMMAND */
57647e946e7SWyllys Ingersoll 0, 0, 0, 18, /* paramsize in bytes */
57747e946e7SWyllys Ingersoll 0, 0, 0, 101, /* TPM_ORD_GetCapability */
57847e946e7SWyllys Ingersoll 0, 0, 0, 0x1A, /* TPM_CAP_VERSION_VAL */
57947e946e7SWyllys Ingersoll 0, 0, 0, 0, /* SUB_CAP size in bytes */
58047e946e7SWyllys Ingersoll };
58147e946e7SWyllys Ingersoll char *myname = "tpm_get_version";
58247e946e7SWyllys Ingersoll
58347e946e7SWyllys Ingersoll ASSERT(tpm != NULL);
58447e946e7SWyllys Ingersoll
58547e946e7SWyllys Ingersoll ret = itpm_command(tpm, buf, sizeof (buf));
58647e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS) {
587*ccf625adSWyllys Ingersoll #ifdef DEBUG
588*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: itpm_command failed with ret code: 0x%x",
58947e946e7SWyllys Ingersoll myname, ret);
590*ccf625adSWyllys 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) {
599*ccf625adSWyllys Ingersoll #ifdef DEBUG
600*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: capability response should be greater"
60147e946e7SWyllys Ingersoll " than %d, instead, it's %d",
602*ccf625adSWyllys Ingersoll myname, TPM_CAP_VERSION_INFO_SIZE, len);
603*ccf625adSWyllys 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) {
629*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: Unsupported TPM version (%d.%d)",
63047e946e7SWyllys Ingersoll myname,
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)64547e946e7SWyllys Ingersoll tpm_continue_selftest(tpm_state_t *tpm) {
64647e946e7SWyllys Ingersoll int ret;
64747e946e7SWyllys Ingersoll uint8_t buf[10] = {
64847e946e7SWyllys Ingersoll 0, 193, /* TPM_TAG_RQU COMMAND */
64947e946e7SWyllys Ingersoll 0, 0, 0, 10, /* paramsize in bytes */
65047e946e7SWyllys Ingersoll 0, 0, 0, 83 /* TPM_ORD_ContinueSelfTest */
65147e946e7SWyllys Ingersoll };
65247e946e7SWyllys Ingersoll char *myname = "tpm_continue_selftest";
65347e946e7SWyllys Ingersoll
65447e946e7SWyllys Ingersoll /* Need a longer timeout */
65547e946e7SWyllys Ingersoll ret = itpm_command(tpm, buf, sizeof (buf));
65647e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS) {
657*ccf625adSWyllys Ingersoll #ifdef DEBUG
658*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: itpm_command failed", myname);
659*ccf625adSWyllys 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 char *myname = "tpm_get_ordinal_duration";
67747e946e7SWyllys Ingersoll
67847e946e7SWyllys Ingersoll ASSERT(tpm != NULL);
67947e946e7SWyllys Ingersoll
68047e946e7SWyllys Ingersoll /* Default and failure case for IFX */
68147e946e7SWyllys Ingersoll /* Is it a TSC_ORDINAL? */
68247e946e7SWyllys Ingersoll if (ordinal & TSC_ORDINAL_MASK) {
68347e946e7SWyllys Ingersoll if (ordinal > TSC_ORDINAL_MAX) {
684*ccf625adSWyllys Ingersoll #ifdef DEBUG
68547e946e7SWyllys Ingersoll cmn_err(CE_WARN,
686*ccf625adSWyllys Ingersoll "!%s: tsc ordinal: %d exceeds MAX: %d",
68747e946e7SWyllys Ingersoll myname, ordinal, TSC_ORDINAL_MAX);
688*ccf625adSWyllys Ingersoll #endif
68947e946e7SWyllys Ingersoll return (0);
69047e946e7SWyllys Ingersoll }
69147e946e7SWyllys Ingersoll index = tsc_ords_duration[ordinal];
69247e946e7SWyllys Ingersoll } else {
69347e946e7SWyllys Ingersoll if (ordinal > TPM_ORDINAL_MAX) {
694*ccf625adSWyllys Ingersoll #ifdef DEBUG
69547e946e7SWyllys Ingersoll cmn_err(CE_WARN,
696*ccf625adSWyllys Ingersoll "!%s: ordinal %d exceeds MAX: %d",
69747e946e7SWyllys Ingersoll myname, ordinal, TPM_ORDINAL_MAX);
698*ccf625adSWyllys Ingersoll #endif
69947e946e7SWyllys Ingersoll return (0);
70047e946e7SWyllys Ingersoll }
70147e946e7SWyllys Ingersoll index = tpm_ords_duration[ordinal];
70247e946e7SWyllys Ingersoll }
70347e946e7SWyllys Ingersoll
70447e946e7SWyllys Ingersoll if (index > TPM_DURATION_MAX_IDX) {
705*ccf625adSWyllys Ingersoll #ifdef DEBUG
706*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: duration index '%d' is out of bounds",
70747e946e7SWyllys Ingersoll myname, index);
708*ccf625adSWyllys Ingersoll #endif
70947e946e7SWyllys Ingersoll return (0);
71047e946e7SWyllys Ingersoll }
71147e946e7SWyllys Ingersoll return (tpm->duration[index]);
71247e946e7SWyllys Ingersoll }
71347e946e7SWyllys Ingersoll
71447e946e7SWyllys Ingersoll /*
71547e946e7SWyllys Ingersoll * Internal TPM Transmit Function:
71647e946e7SWyllys Ingersoll * Calls implementation specific sendto and receive
71747e946e7SWyllys Ingersoll * The code assumes that the buffer is in network byte order
71847e946e7SWyllys Ingersoll */
71947e946e7SWyllys Ingersoll static int
itpm_command(tpm_state_t * tpm,uint8_t * buf,size_t bufsiz)72047e946e7SWyllys Ingersoll itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz)
72147e946e7SWyllys Ingersoll {
72247e946e7SWyllys Ingersoll int ret;
72347e946e7SWyllys Ingersoll uint32_t count;
72447e946e7SWyllys Ingersoll char *myname = "itpm_command";
72547e946e7SWyllys Ingersoll
72647e946e7SWyllys Ingersoll ASSERT(tpm != NULL && buf != NULL);
72747e946e7SWyllys Ingersoll
72847e946e7SWyllys Ingersoll /* The byte order is network byte order so convert it */
72947e946e7SWyllys Ingersoll count = load32(buf, TPM_PARAMSIZE_OFFSET);
73047e946e7SWyllys Ingersoll
731*ccf625adSWyllys Ingersoll if (count == 0 || (count > bufsiz)) {
732*ccf625adSWyllys Ingersoll #ifdef DEBUG
733*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: invalid byte count value "
734*ccf625adSWyllys Ingersoll "(%d > bufsiz %d)", myname, (int)count, (int)bufsiz);
735*ccf625adSWyllys Ingersoll #endif
73647e946e7SWyllys Ingersoll return (DDI_FAILURE);
73747e946e7SWyllys Ingersoll }
73847e946e7SWyllys Ingersoll
73947e946e7SWyllys Ingersoll /* Send the command */
74047e946e7SWyllys Ingersoll ret = tis_send_data(tpm, buf, count);
74147e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS) {
742*ccf625adSWyllys Ingersoll #ifdef DEBUG
743*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: tis_send_data failed with error %x",
74447e946e7SWyllys Ingersoll myname, ret);
745*ccf625adSWyllys Ingersoll #endif
74647e946e7SWyllys Ingersoll return (DDI_FAILURE);
74747e946e7SWyllys Ingersoll }
74847e946e7SWyllys Ingersoll
74947e946e7SWyllys Ingersoll /*
75047e946e7SWyllys Ingersoll * Now receive the data from the tpm
75147e946e7SWyllys Ingersoll * Should at least receive "the common" 10 bytes (TPM_HEADER_SIZE)
75247e946e7SWyllys Ingersoll */
75347e946e7SWyllys Ingersoll ret = tis_recv_data(tpm, buf, bufsiz);
75447e946e7SWyllys Ingersoll if (ret < TPM_HEADER_SIZE) {
755*ccf625adSWyllys Ingersoll #ifdef DEBUG
756*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: tis_recv_data failed", myname);
757*ccf625adSWyllys Ingersoll #endif
75847e946e7SWyllys Ingersoll return (DDI_FAILURE);
75947e946e7SWyllys Ingersoll }
76047e946e7SWyllys Ingersoll
76147e946e7SWyllys Ingersoll /* Check the return code */
76247e946e7SWyllys Ingersoll ret = load32(buf, TPM_RETURN_OFFSET);
76347e946e7SWyllys Ingersoll if (ret != TPM_SUCCESS) {
7648d26100cSWyllys Ingersoll if (ret == TPM_E_DEACTIVATED)
765*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: TPM is deactivated", myname);
7668d26100cSWyllys Ingersoll else if (ret == TPM_E_DISABLED)
767*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: TPM is disabled", myname);
7688d26100cSWyllys Ingersoll else
769*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: TPM error code 0x%0x",
77047e946e7SWyllys Ingersoll myname, ret);
77147e946e7SWyllys Ingersoll return (DDI_FAILURE);
77247e946e7SWyllys Ingersoll }
77347e946e7SWyllys Ingersoll
77447e946e7SWyllys Ingersoll return (DDI_SUCCESS);
77547e946e7SWyllys Ingersoll }
77647e946e7SWyllys Ingersoll
77747e946e7SWyllys Ingersoll /*
77847e946e7SWyllys Ingersoll * Whenever the driver wants to write to the DATA_IO register, it must need
77947e946e7SWyllys Ingersoll * to figure out the burstcount. This is the amount of bytes it can write
78047e946e7SWyllys Ingersoll * before having to wait for long LPC bus cycle
78147e946e7SWyllys Ingersoll *
78247e946e7SWyllys Ingersoll * Returns: 0 if error, burst count if sucess
78347e946e7SWyllys Ingersoll */
78447e946e7SWyllys Ingersoll static uint16_t
tpm_get_burstcount(tpm_state_t * tpm)78547e946e7SWyllys Ingersoll tpm_get_burstcount(tpm_state_t *tpm) {
78647e946e7SWyllys Ingersoll clock_t stop;
78747e946e7SWyllys Ingersoll uint16_t burstcnt;
78847e946e7SWyllys Ingersoll
78947e946e7SWyllys Ingersoll ASSERT(tpm != NULL);
79047e946e7SWyllys Ingersoll
79147e946e7SWyllys Ingersoll /*
79247e946e7SWyllys Ingersoll * Spec says timeout should be TIMEOUT_D
79347e946e7SWyllys Ingersoll * burst count is TPM_STS bits 8..23
79447e946e7SWyllys Ingersoll */
79547e946e7SWyllys Ingersoll stop = ddi_get_lbolt() + tpm->timeout_d;
79647e946e7SWyllys Ingersoll do {
79747e946e7SWyllys Ingersoll /*
79847e946e7SWyllys Ingersoll * burstcnt is stored as a little endian value
79947e946e7SWyllys Ingersoll * 'ntohs' doesn't work since the value is not word-aligned
80047e946e7SWyllys Ingersoll */
8018d26100cSWyllys Ingersoll burstcnt = tpm_get8(tpm, TPM_STS + 1);
8028d26100cSWyllys Ingersoll burstcnt += tpm_get8(tpm, TPM_STS + 2) << 8;
80347e946e7SWyllys Ingersoll
80447e946e7SWyllys Ingersoll if (burstcnt)
80547e946e7SWyllys Ingersoll return (burstcnt);
80647e946e7SWyllys Ingersoll
80747e946e7SWyllys Ingersoll delay(tpm->timeout_poll);
80847e946e7SWyllys Ingersoll } while (ddi_get_lbolt() < stop);
80947e946e7SWyllys Ingersoll
81047e946e7SWyllys Ingersoll return (0);
81147e946e7SWyllys Ingersoll }
81247e946e7SWyllys Ingersoll
81347e946e7SWyllys Ingersoll /*
81447e946e7SWyllys Ingersoll * Writing 1 to TPM_STS_CMD_READY bit in TPM_STS will do the following:
81547e946e7SWyllys Ingersoll * 1. The TPM will clears IO buffers if any
81647e946e7SWyllys Ingersoll * 2. The TPM will enters either Idle or Ready state within TIMEOUT_B
81747e946e7SWyllys Ingersoll * (checked in the calling function)
81847e946e7SWyllys Ingersoll */
81947e946e7SWyllys Ingersoll static void
tpm_set_ready(tpm_state_t * tpm)82047e946e7SWyllys Ingersoll tpm_set_ready(tpm_state_t *tpm) {
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)82547e946e7SWyllys Ingersoll receive_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
82647e946e7SWyllys Ingersoll int size = 0;
82747e946e7SWyllys Ingersoll int retried = 0;
82847e946e7SWyllys Ingersoll uint8_t stsbits;
82947e946e7SWyllys Ingersoll
83047e946e7SWyllys Ingersoll /* A number of consecutive bytes that can be written to TPM */
83147e946e7SWyllys Ingersoll uint16_t burstcnt;
83247e946e7SWyllys Ingersoll
83347e946e7SWyllys Ingersoll ASSERT(tpm != NULL && buf != NULL);
83447e946e7SWyllys Ingersoll retry:
83547e946e7SWyllys Ingersoll while (size < bufsiz &&
83647e946e7SWyllys Ingersoll (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)86547e946e7SWyllys Ingersoll tis_recv_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
86647e946e7SWyllys Ingersoll int ret;
86747e946e7SWyllys Ingersoll int size = 0;
86847e946e7SWyllys Ingersoll uint32_t expected, status;
86947e946e7SWyllys Ingersoll uint32_t cmdresult;
87047e946e7SWyllys Ingersoll char *myname = "tis_recv_data";
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 */
876*ccf625adSWyllys Ingersoll #ifdef DEBUG
877*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: received data should contain at least "
87847e946e7SWyllys Ingersoll "the header which is %d bytes long",
87947e946e7SWyllys Ingersoll myname, TPM_HEADER_SIZE);
880*ccf625adSWyllys 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) {
887*ccf625adSWyllys Ingersoll #ifdef DEBUG
888*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: recv TPM_HEADER failed, size = %d",
88947e946e7SWyllys Ingersoll myname, size);
890*ccf625adSWyllys 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) {
899*ccf625adSWyllys Ingersoll #ifdef DEBUG
900*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: paramSize is bigger "
90147e946e7SWyllys Ingersoll "than the requested size: paramSize=%d bufsiz=%d result=%d",
90247e946e7SWyllys Ingersoll myname, (int)expected, (int)bufsiz, cmdresult);
903*ccf625adSWyllys 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) {
911*ccf625adSWyllys Ingersoll #ifdef DEBUG
912*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: received data length (%d) "
913*ccf625adSWyllys Ingersoll "is less than expected (%d)", myname, size, expected);
914*ccf625adSWyllys 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) {
923*ccf625adSWyllys Ingersoll #ifdef DEBUG
924*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: TPM didn't set stsValid after its I/O: "
92547e946e7SWyllys Ingersoll "status = 0x%08X", myname, status);
926*ccf625adSWyllys 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) {
932*ccf625adSWyllys Ingersoll #ifdef DEBUG
933*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: TPM_STS_DATA_AVAIL is set:0x%08X",
93447e946e7SWyllys Ingersoll myname, status);
935*ccf625adSWyllys 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)95547e946e7SWyllys Ingersoll tis_send_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) {
95647e946e7SWyllys Ingersoll int ret;
95747e946e7SWyllys Ingersoll uint8_t status;
95847e946e7SWyllys Ingersoll uint16_t burstcnt;
95947e946e7SWyllys Ingersoll uint32_t ordinal;
96047e946e7SWyllys Ingersoll size_t count = 0;
96147e946e7SWyllys Ingersoll char *myname = "tis_send_data";
96247e946e7SWyllys Ingersoll
96347e946e7SWyllys Ingersoll ASSERT(tpm != NULL && buf != NULL);
96447e946e7SWyllys Ingersoll
96547e946e7SWyllys Ingersoll if (bufsiz == 0) {
966*ccf625adSWyllys Ingersoll #ifdef DEBUG
967*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: bufsiz arg is zero", myname);
968*ccf625adSWyllys 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) {
979*ccf625adSWyllys Ingersoll #ifdef DEBUG
980*ccf625adSWyllys 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",
98347e946e7SWyllys Ingersoll myname);
984*ccf625adSWyllys 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) {
998*ccf625adSWyllys Ingersoll #ifdef DEBUG
999*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: tpm_get_burstcnt returned error",
100047e946e7SWyllys Ingersoll myname);
1001*ccf625adSWyllys 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) {
1014*ccf625adSWyllys Ingersoll #ifdef DEBUG
1015*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: TPM didn't enter STS_VALID "
1016*ccf625adSWyllys Ingersoll "state", myname);
1017*ccf625adSWyllys 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) {
1030*ccf625adSWyllys Ingersoll #ifdef DEBUG
1031*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: tpm didn't enter STS_VALID state",
1032*ccf625adSWyllys Ingersoll myname);
1033*ccf625adSWyllys 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) {
1040*ccf625adSWyllys Ingersoll #ifdef DEBUG
1041*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: DATA_EXPECT should not be set after "
104247e946e7SWyllys Ingersoll "writing the last byte: status=0x%08X", myname, status);
1043*ccf625adSWyllys 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) {
1060*ccf625adSWyllys Ingersoll #ifdef DEBUG
106147e946e7SWyllys Ingersoll status = tis_get_status(tpm);
106247e946e7SWyllys Ingersoll if (!(status & TPM_STS_DATA_AVAIL) ||
106347e946e7SWyllys Ingersoll !(status & TPM_STS_VALID)) {
1064*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: TPM not ready or valid "
1065*ccf625adSWyllys Ingersoll "(ordinal = %d timeout = %ld status = 0x%0x)",
106647e946e7SWyllys Ingersoll myname, ordinal,
1067*ccf625adSWyllys Ingersoll tpm_get_ordinal_duration(tpm, ordinal),
1068*ccf625adSWyllys Ingersoll status);
106947e946e7SWyllys Ingersoll } else {
1070*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: tpm_wait_for_stat "
1071*ccf625adSWyllys Ingersoll "(DATA_AVAIL | VALID) failed status = 0x%0X",
107247e946e7SWyllys Ingersoll myname, status);
107347e946e7SWyllys Ingersoll }
1074*ccf625adSWyllys 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)108947e946e7SWyllys Ingersoll tis_release_locality(tpm_state_t *tpm, char locality, int force) {
109047e946e7SWyllys Ingersoll ASSERT(tpm != NULL && locality >= 0 && locality < 5);
109147e946e7SWyllys Ingersoll
109247e946e7SWyllys Ingersoll if (force ||
10938d26100cSWyllys Ingersoll (tpm_get8(tpm, TPM_ACCESS) &
10948d26100cSWyllys Ingersoll (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
10958d26100cSWyllys Ingersoll (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
109647e946e7SWyllys Ingersoll /*
109747e946e7SWyllys Ingersoll * Writing 1 to active locality bit in TPM_ACCESS
109847e946e7SWyllys Ingersoll * register reliquishes the control of the locality
109947e946e7SWyllys Ingersoll */
11008d26100cSWyllys Ingersoll tpm_put8(tpm, TPM_ACCESS, TPM_ACCESS_ACTIVE_LOCALITY);
110147e946e7SWyllys Ingersoll }
110247e946e7SWyllys Ingersoll }
110347e946e7SWyllys Ingersoll
110447e946e7SWyllys Ingersoll /*
110547e946e7SWyllys Ingersoll * Checks whether the given locality is active
110647e946e7SWyllys Ingersoll * Use TPM_ACCESS register and the masks TPM_ACCESS_VALID,TPM_ACTIVE_LOCALITY
110747e946e7SWyllys Ingersoll */
110847e946e7SWyllys Ingersoll static int
tis_check_active_locality(tpm_state_t * tpm,char locality)110947e946e7SWyllys Ingersoll tis_check_active_locality(tpm_state_t *tpm, char locality) {
111047e946e7SWyllys Ingersoll uint8_t access_bits;
11118d26100cSWyllys Ingersoll uint8_t old_locality;
111247e946e7SWyllys Ingersoll
111347e946e7SWyllys Ingersoll ASSERT(tpm != NULL && locality >= 0 && locality < 5);
111447e946e7SWyllys Ingersoll
11158d26100cSWyllys Ingersoll old_locality = tpm->locality;
11168d26100cSWyllys Ingersoll tpm->locality = locality;
11178d26100cSWyllys Ingersoll
11188d26100cSWyllys Ingersoll /* Just check to see if the requested locality works */
11198d26100cSWyllys Ingersoll access_bits = tpm_get8(tpm, TPM_ACCESS);
112047e946e7SWyllys Ingersoll access_bits &= (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID);
112147e946e7SWyllys Ingersoll
11228d26100cSWyllys Ingersoll /* this was just a check, not a request to switch */
11238d26100cSWyllys Ingersoll tpm->locality = old_locality;
11248d26100cSWyllys Ingersoll
11258d26100cSWyllys Ingersoll if (access_bits == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
112647e946e7SWyllys Ingersoll return (DDI_SUCCESS);
11278d26100cSWyllys Ingersoll } else {
112847e946e7SWyllys Ingersoll return (DDI_FAILURE);
112947e946e7SWyllys Ingersoll }
11308d26100cSWyllys Ingersoll }
113147e946e7SWyllys Ingersoll
113247e946e7SWyllys Ingersoll /* Request the TPM to be in the given locality */
113347e946e7SWyllys Ingersoll static int
tis_request_locality(tpm_state_t * tpm,char locality)113447e946e7SWyllys Ingersoll tis_request_locality(tpm_state_t *tpm, char locality) {
113547e946e7SWyllys Ingersoll clock_t timeout;
113647e946e7SWyllys Ingersoll int ret;
113747e946e7SWyllys Ingersoll char *myname = "tis_request_locality";
113847e946e7SWyllys Ingersoll
113947e946e7SWyllys Ingersoll ASSERT(tpm != NULL && locality >= 0 && locality < 5);
114047e946e7SWyllys Ingersoll
114147e946e7SWyllys Ingersoll ret = tis_check_active_locality(tpm, locality);
114247e946e7SWyllys Ingersoll
114347e946e7SWyllys Ingersoll if (ret == DDI_SUCCESS) {
114447e946e7SWyllys Ingersoll /* Locality is already active */
114547e946e7SWyllys Ingersoll tpm->locality = locality;
114647e946e7SWyllys Ingersoll return (DDI_SUCCESS);
114747e946e7SWyllys Ingersoll }
114847e946e7SWyllys Ingersoll
11498d26100cSWyllys Ingersoll tpm_put8(tpm, TPM_ACCESS, TPM_ACCESS_REQUEST_USE);
115047e946e7SWyllys Ingersoll timeout = ddi_get_lbolt() + tpm->timeout_a;
115147e946e7SWyllys Ingersoll
115247e946e7SWyllys Ingersoll /* Using polling */
115347e946e7SWyllys Ingersoll while (tis_check_active_locality(tpm, locality)
115447e946e7SWyllys Ingersoll != DDI_SUCCESS) {
115547e946e7SWyllys Ingersoll if (ddi_get_lbolt() >= timeout) {
1156*ccf625adSWyllys Ingersoll #ifdef DEBUG
1157*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: (interrupt-disabled) "
11588d26100cSWyllys Ingersoll "tis_request_locality timed out (timeout_a = %ld)",
11598d26100cSWyllys Ingersoll myname, tpm->timeout_a);
1160*ccf625adSWyllys Ingersoll #endif
116147e946e7SWyllys Ingersoll return (DDI_FAILURE);
116247e946e7SWyllys Ingersoll }
116347e946e7SWyllys Ingersoll delay(tpm->timeout_poll);
116447e946e7SWyllys Ingersoll }
116547e946e7SWyllys Ingersoll
116647e946e7SWyllys Ingersoll tpm->locality = locality;
116747e946e7SWyllys Ingersoll return (DDI_SUCCESS);
116847e946e7SWyllys Ingersoll }
116947e946e7SWyllys Ingersoll
117047e946e7SWyllys Ingersoll /* Read the status register */
117147e946e7SWyllys Ingersoll static uint8_t
tis_get_status(tpm_state_t * tpm)117247e946e7SWyllys Ingersoll tis_get_status(tpm_state_t *tpm) {
11738d26100cSWyllys Ingersoll return (tpm_get8(tpm, TPM_STS));
117447e946e7SWyllys Ingersoll }
117547e946e7SWyllys Ingersoll
117647e946e7SWyllys Ingersoll static int
tpm_wait_for_stat(tpm_state_t * tpm,uint8_t mask,clock_t timeout)11778d26100cSWyllys Ingersoll tpm_wait_for_stat(tpm_state_t *tpm, uint8_t mask, clock_t timeout) {
117847e946e7SWyllys Ingersoll char *myname = "tpm_wait_for_stat";
11798d26100cSWyllys Ingersoll clock_t absolute_timeout = ddi_get_lbolt() + timeout;
118047e946e7SWyllys Ingersoll
118147e946e7SWyllys Ingersoll /* Using polling */
118247e946e7SWyllys Ingersoll while ((tis_get_status(tpm) & mask) != mask) {
118347e946e7SWyllys Ingersoll if (ddi_get_lbolt() >= absolute_timeout) {
118447e946e7SWyllys Ingersoll /* Timeout reached */
1185*ccf625adSWyllys Ingersoll #ifdef DEBUG
1186*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: using "
11878d26100cSWyllys Ingersoll "polling - reached timeout (%ld usecs)",
11888d26100cSWyllys Ingersoll myname, drv_hztousec(timeout));
1189*ccf625adSWyllys Ingersoll #endif
119047e946e7SWyllys Ingersoll return (DDI_FAILURE);
119147e946e7SWyllys Ingersoll }
119247e946e7SWyllys Ingersoll delay(tpm->timeout_poll);
119347e946e7SWyllys Ingersoll }
119447e946e7SWyllys Ingersoll return (DDI_SUCCESS);
119547e946e7SWyllys Ingersoll }
119647e946e7SWyllys Ingersoll
119747e946e7SWyllys Ingersoll /*
119847e946e7SWyllys Ingersoll * Initialize TPM device
119947e946e7SWyllys Ingersoll * 1. Find out supported interrupt capabilities
120047e946e7SWyllys Ingersoll * 2. Set up interrupt handler if supported (some BIOSes don't support
120147e946e7SWyllys Ingersoll * interrupts for TPMS, in which case we set up polling)
120247e946e7SWyllys Ingersoll * 3. Determine timeouts and commands duration
120347e946e7SWyllys Ingersoll */
120447e946e7SWyllys Ingersoll static int
tis_init(tpm_state_t * tpm)120547e946e7SWyllys Ingersoll tis_init(tpm_state_t *tpm) {
120647e946e7SWyllys Ingersoll uint32_t intf_caps;
120747e946e7SWyllys Ingersoll int ret;
120847e946e7SWyllys Ingersoll char *myname = "tis_init";
120947e946e7SWyllys Ingersoll
121047e946e7SWyllys Ingersoll /*
121147e946e7SWyllys Ingersoll * Temporarily set up timeouts before we get the real timeouts
121247e946e7SWyllys Ingersoll * by issuing TPM_CAP commands (but to issue TPM_CAP commands,
121347e946e7SWyllys Ingersoll * you need TIMEOUTs defined...chicken and egg problem here.
121447e946e7SWyllys Ingersoll * TPM timeouts: Convert the milliseconds to clock cycles
121547e946e7SWyllys Ingersoll */
121647e946e7SWyllys Ingersoll tpm->timeout_a = drv_usectohz(TIS_TIMEOUT_A);
121747e946e7SWyllys Ingersoll tpm->timeout_b = drv_usectohz(TIS_TIMEOUT_B);
121847e946e7SWyllys Ingersoll tpm->timeout_c = drv_usectohz(TIS_TIMEOUT_C);
121947e946e7SWyllys Ingersoll tpm->timeout_d = drv_usectohz(TIS_TIMEOUT_D);
122047e946e7SWyllys Ingersoll /*
122147e946e7SWyllys Ingersoll * Do the same with the duration (real duration will be filled out
122247e946e7SWyllys Ingersoll * when we call TPM_GetCapability to get the duration values from
122347e946e7SWyllys Ingersoll * the TPM itself).
122447e946e7SWyllys Ingersoll */
122547e946e7SWyllys Ingersoll tpm->duration[TPM_SHORT] = drv_usectohz(TPM_DEFAULT_DURATION);
122647e946e7SWyllys Ingersoll tpm->duration[TPM_MEDIUM] = drv_usectohz(TPM_DEFAULT_DURATION);
122747e946e7SWyllys Ingersoll tpm->duration[TPM_LONG] = drv_usectohz(TPM_DEFAULT_DURATION);
122847e946e7SWyllys Ingersoll tpm->duration[TPM_UNDEFINED] = drv_usectohz(TPM_DEFAULT_DURATION);
122947e946e7SWyllys Ingersoll
123047e946e7SWyllys Ingersoll /* Find out supported capabilities */
12318d26100cSWyllys Ingersoll intf_caps = tpm_get32(tpm, TPM_INTF_CAP);
123247e946e7SWyllys Ingersoll
123347e946e7SWyllys Ingersoll /* Upper 3 bytes should always return 0 */
123447e946e7SWyllys Ingersoll if (intf_caps & 0x7FFFFF00) {
1235*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: bad intf_caps value 0x%0X",
123647e946e7SWyllys Ingersoll myname, intf_caps);
123747e946e7SWyllys Ingersoll return (DDI_FAILURE);
123847e946e7SWyllys Ingersoll }
123947e946e7SWyllys Ingersoll
124047e946e7SWyllys Ingersoll /* These two interrupts are mandatory */
124147e946e7SWyllys Ingersoll if (!(intf_caps & TPM_INTF_INT_LOCALITY_CHANGE_INT)) {
1242*ccf625adSWyllys Ingersoll cmn_err(CE_WARN,
1243*ccf625adSWyllys Ingersoll "!%s: Mandatory capability Locality Change Int "
124447e946e7SWyllys Ingersoll "not supported", myname);
124547e946e7SWyllys Ingersoll return (DDI_FAILURE);
124647e946e7SWyllys Ingersoll }
124747e946e7SWyllys Ingersoll if (!(intf_caps & TPM_INTF_INT_DATA_AVAIL_INT)) {
1248*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: Mandatory capability Data Available Int "
1249*ccf625adSWyllys Ingersoll "not supported.", myname);
125047e946e7SWyllys Ingersoll return (DDI_FAILURE);
125147e946e7SWyllys Ingersoll }
125247e946e7SWyllys Ingersoll
125347e946e7SWyllys Ingersoll /*
125447e946e7SWyllys Ingersoll * Before we start writing anything to TPM's registers,
125547e946e7SWyllys Ingersoll * make sure we are in locality 0
125647e946e7SWyllys Ingersoll */
12578d26100cSWyllys Ingersoll ret = tis_request_locality(tpm, DEFAULT_LOCALITY);
125847e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS) {
1259*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: Unable to request locality %d", myname,
12608d26100cSWyllys Ingersoll DEFAULT_LOCALITY);
126147e946e7SWyllys Ingersoll return (DDI_FAILURE);
126247e946e7SWyllys Ingersoll } /* Now we can refer to the locality as tpm->locality */
126347e946e7SWyllys Ingersoll
126447e946e7SWyllys Ingersoll tpm->timeout_poll = drv_usectohz(TPM_POLLING_TIMEOUT);
126547e946e7SWyllys Ingersoll tpm->intr_enabled = 0;
126647e946e7SWyllys Ingersoll
126747e946e7SWyllys Ingersoll /* Get the real timeouts from the TPM */
126847e946e7SWyllys Ingersoll ret = tpm_get_timeouts(tpm);
126947e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS) {
1270*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: tpm_get_timeouts error", myname);
127147e946e7SWyllys Ingersoll return (DDI_FAILURE);
127247e946e7SWyllys Ingersoll }
127347e946e7SWyllys Ingersoll
127447e946e7SWyllys Ingersoll ret = tpm_get_duration(tpm);
127547e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS) {
1276*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: tpm_get_duration error", myname);
127747e946e7SWyllys Ingersoll return (DDI_FAILURE);
127847e946e7SWyllys Ingersoll }
127947e946e7SWyllys Ingersoll
128047e946e7SWyllys Ingersoll /* This gets the TPM version information */
128147e946e7SWyllys Ingersoll ret = tpm_get_version(tpm);
128247e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS) {
1283*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: tpm_get_version error", myname);
128447e946e7SWyllys Ingersoll return (DDI_FAILURE);
128547e946e7SWyllys Ingersoll }
128647e946e7SWyllys Ingersoll
128747e946e7SWyllys Ingersoll /*
128847e946e7SWyllys Ingersoll * Unless the TPM completes the test of its commands,
128947e946e7SWyllys Ingersoll * it can return an error when the untested commands are called
129047e946e7SWyllys Ingersoll */
129147e946e7SWyllys Ingersoll ret = tpm_continue_selftest(tpm);
129247e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS) {
1293*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: tpm_continue_selftest error", myname);
129447e946e7SWyllys Ingersoll return (DDI_FAILURE);
129547e946e7SWyllys Ingersoll }
129647e946e7SWyllys Ingersoll return (DDI_SUCCESS);
129747e946e7SWyllys Ingersoll }
129847e946e7SWyllys Ingersoll
129947e946e7SWyllys Ingersoll /*
130047e946e7SWyllys Ingersoll * Module Entry points
130147e946e7SWyllys Ingersoll */
130247e946e7SWyllys Ingersoll int
_init(void)130347e946e7SWyllys Ingersoll _init(void)
130447e946e7SWyllys Ingersoll {
130547e946e7SWyllys Ingersoll int ret;
130647e946e7SWyllys Ingersoll
130747e946e7SWyllys Ingersoll ret = ddi_soft_state_init(&statep, sizeof (tpm_state_t), 1);
1308*ccf625adSWyllys Ingersoll if (ret) {
1309*ccf625adSWyllys Ingersoll #ifdef DEBUG
1310*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!ddi_soft_state_init failed: %d", ret);
1311*ccf625adSWyllys Ingersoll #endif
131247e946e7SWyllys Ingersoll return (ret);
13138d26100cSWyllys Ingersoll }
131447e946e7SWyllys Ingersoll ret = mod_install(&tpm_ml);
131547e946e7SWyllys Ingersoll if (ret != 0) {
1316*ccf625adSWyllys Ingersoll #ifdef DEBUG
1317*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!_init: mod_install returned non-zero");
1318*ccf625adSWyllys Ingersoll #endif
131947e946e7SWyllys Ingersoll ddi_soft_state_fini(&statep);
132047e946e7SWyllys Ingersoll return (ret);
132147e946e7SWyllys Ingersoll }
132247e946e7SWyllys Ingersoll
132347e946e7SWyllys Ingersoll return (ret);
132447e946e7SWyllys Ingersoll }
132547e946e7SWyllys Ingersoll
132647e946e7SWyllys Ingersoll int
_info(struct modinfo * modinfop)132747e946e7SWyllys Ingersoll _info(struct modinfo *modinfop)
132847e946e7SWyllys Ingersoll {
132947e946e7SWyllys Ingersoll int ret;
133047e946e7SWyllys Ingersoll ret = mod_info(&tpm_ml, modinfop);
1331*ccf625adSWyllys Ingersoll #ifdef DEBUG
133247e946e7SWyllys Ingersoll if (ret == 0)
1333*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!mod_info failed: %d", ret);
1334*ccf625adSWyllys Ingersoll #endif
133547e946e7SWyllys Ingersoll
133647e946e7SWyllys Ingersoll return (ret);
133747e946e7SWyllys Ingersoll }
133847e946e7SWyllys Ingersoll
133947e946e7SWyllys Ingersoll int
_fini()134047e946e7SWyllys Ingersoll _fini()
134147e946e7SWyllys Ingersoll {
134247e946e7SWyllys Ingersoll int ret;
13438d26100cSWyllys Ingersoll
134447e946e7SWyllys Ingersoll ret = mod_remove(&tpm_ml);
13458d26100cSWyllys Ingersoll if (ret != 0)
134647e946e7SWyllys Ingersoll return (ret);
13478d26100cSWyllys Ingersoll
134847e946e7SWyllys Ingersoll ddi_soft_state_fini(&statep);
134947e946e7SWyllys Ingersoll
135047e946e7SWyllys Ingersoll return (ret);
135147e946e7SWyllys Ingersoll }
135247e946e7SWyllys Ingersoll /* End of driver configuration functions */
135347e946e7SWyllys Ingersoll
135447e946e7SWyllys Ingersoll static int
tpm_resume(tpm_state_t * tpm)135547e946e7SWyllys Ingersoll tpm_resume(tpm_state_t *tpm)
135647e946e7SWyllys Ingersoll {
135747e946e7SWyllys Ingersoll mutex_enter(&tpm->pm_mutex);
135847e946e7SWyllys Ingersoll if (!tpm->suspended) {
135947e946e7SWyllys Ingersoll mutex_exit(&tpm->pm_mutex);
136047e946e7SWyllys Ingersoll return (DDI_FAILURE);
136147e946e7SWyllys Ingersoll }
136247e946e7SWyllys Ingersoll tpm->suspended = 0;
136347e946e7SWyllys Ingersoll cv_broadcast(&tpm->suspend_cv);
136447e946e7SWyllys Ingersoll mutex_exit(&tpm->pm_mutex);
136547e946e7SWyllys Ingersoll
136647e946e7SWyllys Ingersoll return (DDI_SUCCESS);
136747e946e7SWyllys Ingersoll }
136847e946e7SWyllys Ingersoll
13698d26100cSWyllys Ingersoll #ifdef sun4v
13708d26100cSWyllys Ingersoll static uint64_t hsvc_tpm_minor = 0;
13718d26100cSWyllys Ingersoll static hsvc_info_t hsvc_tpm = {
13728d26100cSWyllys Ingersoll HSVC_REV_1, NULL, HSVC_GROUP_TPM, 1, 0, NULL
13738d26100cSWyllys Ingersoll };
13748d26100cSWyllys Ingersoll #endif
13758d26100cSWyllys Ingersoll
137647e946e7SWyllys Ingersoll /*
137747e946e7SWyllys Ingersoll * Sun DDI/DDK entry points
137847e946e7SWyllys Ingersoll */
137947e946e7SWyllys Ingersoll static int
tpm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)138047e946e7SWyllys Ingersoll tpm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
138147e946e7SWyllys Ingersoll {
13828d26100cSWyllys Ingersoll int ret;
138347e946e7SWyllys Ingersoll int instance;
13848d26100cSWyllys Ingersoll #ifndef sun4v
13858d26100cSWyllys Ingersoll int idx, nregs;
13868d26100cSWyllys Ingersoll #endif
138747e946e7SWyllys Ingersoll char *myname = "tpm_attach";
138847e946e7SWyllys Ingersoll tpm_state_t *tpm = NULL;
138947e946e7SWyllys Ingersoll
139047e946e7SWyllys Ingersoll ASSERT(dip != NULL);
139147e946e7SWyllys Ingersoll
139247e946e7SWyllys Ingersoll instance = ddi_get_instance(dip);
13938d26100cSWyllys Ingersoll if (instance < 0)
13948d26100cSWyllys Ingersoll return (DDI_FAILURE);
139547e946e7SWyllys Ingersoll
139647e946e7SWyllys Ingersoll /* Nothing out of ordinary here */
139747e946e7SWyllys Ingersoll switch (cmd) {
139847e946e7SWyllys Ingersoll case DDI_ATTACH:
13998d26100cSWyllys Ingersoll if (ddi_soft_state_zalloc(statep, instance) == DDI_SUCCESS) {
140047e946e7SWyllys Ingersoll tpm = ddi_get_soft_state(statep, instance);
14018d26100cSWyllys Ingersoll if (tpm == NULL) {
1402*ccf625adSWyllys Ingersoll #ifdef DEBUG
14038d26100cSWyllys Ingersoll cmn_err(CE_WARN,
1404*ccf625adSWyllys Ingersoll "!%s: cannot get state information.",
14058d26100cSWyllys Ingersoll myname);
1406*ccf625adSWyllys Ingersoll #endif
14078d26100cSWyllys Ingersoll return (DDI_FAILURE);
14088d26100cSWyllys Ingersoll }
140947e946e7SWyllys Ingersoll tpm->dip = dip;
14108d26100cSWyllys Ingersoll } else {
1411d25a0be9SWyllys Ingersoll #ifdef DEBUG
14128d26100cSWyllys Ingersoll cmn_err(CE_WARN,
1413*ccf625adSWyllys Ingersoll "!%s: cannot allocate state information.",
14148d26100cSWyllys Ingersoll myname);
1415d25a0be9SWyllys Ingersoll #endif
14168d26100cSWyllys Ingersoll return (DDI_FAILURE);
14178d26100cSWyllys Ingersoll }
141847e946e7SWyllys Ingersoll break;
141947e946e7SWyllys Ingersoll case DDI_RESUME:
142047e946e7SWyllys Ingersoll tpm = ddi_get_soft_state(statep, instance);
142147e946e7SWyllys Ingersoll if (tpm == NULL) {
1422*ccf625adSWyllys Ingersoll #ifdef DEBUG
1423*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: cannot get state information.",
142447e946e7SWyllys Ingersoll myname);
1425*ccf625adSWyllys Ingersoll #endif
14268d26100cSWyllys Ingersoll return (DDI_FAILURE);
142747e946e7SWyllys Ingersoll }
142847e946e7SWyllys Ingersoll return (tpm_resume(tpm));
142947e946e7SWyllys Ingersoll default:
1430*ccf625adSWyllys Ingersoll #ifdef DEBUG
1431*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: cmd %d is not implemented", myname, cmd);
1432*ccf625adSWyllys Ingersoll #endif
143347e946e7SWyllys Ingersoll ret = DDI_FAILURE;
143447e946e7SWyllys Ingersoll goto FAIL;
143547e946e7SWyllys Ingersoll }
143647e946e7SWyllys Ingersoll
143747e946e7SWyllys Ingersoll /* Zeroize the flag, which is used to keep track of what is allocated */
143847e946e7SWyllys Ingersoll tpm->flags = 0;
143947e946e7SWyllys Ingersoll
14408d26100cSWyllys Ingersoll #ifdef sun4v
14418d26100cSWyllys Ingersoll ret = hsvc_register(&hsvc_tpm, &hsvc_tpm_minor);
14428d26100cSWyllys Ingersoll if (ret != 0) {
1443*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: failed to register with "
14448d26100cSWyllys Ingersoll "hypervisor: 0x%0x", myname, ret);
14458d26100cSWyllys Ingersoll goto FAIL;
14468d26100cSWyllys Ingersoll }
14478d26100cSWyllys Ingersoll tpm->flags |= TPM_HSVC_REGISTERED;
14488d26100cSWyllys Ingersoll #else
144947e946e7SWyllys Ingersoll tpm->accattr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
145047e946e7SWyllys Ingersoll tpm->accattr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
145147e946e7SWyllys Ingersoll tpm->accattr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
145247e946e7SWyllys Ingersoll
145347e946e7SWyllys Ingersoll idx = 0;
145447e946e7SWyllys Ingersoll ret = ddi_dev_nregs(tpm->dip, &nregs);
145547e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS)
145647e946e7SWyllys Ingersoll goto FAIL;
145747e946e7SWyllys Ingersoll
145847e946e7SWyllys Ingersoll /*
145947e946e7SWyllys Ingersoll * TPM vendors put the TPM registers in different
146047e946e7SWyllys Ingersoll * slots in their register lists. They are not always
146147e946e7SWyllys Ingersoll * the 1st set of registers, for instance.
146247e946e7SWyllys Ingersoll * Loop until we find the set that matches the expected
146347e946e7SWyllys Ingersoll * register size (0x5000).
146447e946e7SWyllys Ingersoll */
146547e946e7SWyllys Ingersoll for (idx = 0; idx < nregs; idx++) {
146647e946e7SWyllys Ingersoll off_t regsize;
146747e946e7SWyllys Ingersoll
146847e946e7SWyllys Ingersoll if ((ret = ddi_dev_regsize(tpm->dip, idx, ®size)) !=
146947e946e7SWyllys Ingersoll DDI_SUCCESS)
147047e946e7SWyllys Ingersoll goto FAIL;
147147e946e7SWyllys Ingersoll /* The TIS spec says the TPM registers must be 0x5000 bytes */
147247e946e7SWyllys Ingersoll if (regsize == 0x5000)
147347e946e7SWyllys Ingersoll break;
147447e946e7SWyllys Ingersoll }
1475d25a0be9SWyllys Ingersoll if (idx == nregs) {
1476d25a0be9SWyllys Ingersoll ret = DDI_FAILURE;
1477d25a0be9SWyllys Ingersoll goto FAIL;
1478d25a0be9SWyllys Ingersoll }
1479459e772fSWyllys Ingersoll
148047e946e7SWyllys Ingersoll ret = ddi_regs_map_setup(tpm->dip, idx, (caddr_t *)&tpm->addr,
148147e946e7SWyllys Ingersoll (offset_t)0, (offset_t)0x5000,
148247e946e7SWyllys Ingersoll &tpm->accattr, &tpm->handle);
148347e946e7SWyllys Ingersoll
148447e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS) {
148547e946e7SWyllys Ingersoll goto FAIL;
148647e946e7SWyllys Ingersoll }
148747e946e7SWyllys Ingersoll tpm->flags |= TPM_DIDREGSMAP;
14888d26100cSWyllys Ingersoll #endif
148947e946e7SWyllys Ingersoll /* Enable TPM device according to the TIS specification */
149047e946e7SWyllys Ingersoll ret = tis_init(tpm);
149147e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS) {
1492*ccf625adSWyllys Ingersoll #ifdef DEBUG
1493*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: tis_init() failed with error %d",
149447e946e7SWyllys Ingersoll myname, ret);
1495*ccf625adSWyllys Ingersoll #endif
149647e946e7SWyllys Ingersoll
149747e946e7SWyllys Ingersoll /* We need to clean up the ddi_regs_map_setup call */
14988d26100cSWyllys Ingersoll if (tpm->flags & TPM_DIDREGSMAP) {
149947e946e7SWyllys Ingersoll ddi_regs_map_free(&tpm->handle);
150047e946e7SWyllys Ingersoll tpm->handle = NULL;
150147e946e7SWyllys Ingersoll tpm->flags &= ~TPM_DIDREGSMAP;
15028d26100cSWyllys Ingersoll }
150347e946e7SWyllys Ingersoll goto FAIL;
150447e946e7SWyllys Ingersoll }
150547e946e7SWyllys Ingersoll
150647e946e7SWyllys Ingersoll /* Initialize the inter-process lock */
150747e946e7SWyllys Ingersoll mutex_init(&tpm->dev_lock, NULL, MUTEX_DRIVER, NULL);
150847e946e7SWyllys Ingersoll mutex_init(&tpm->pm_mutex, NULL, MUTEX_DRIVER, NULL);
150947e946e7SWyllys Ingersoll cv_init(&tpm->suspend_cv, NULL, CV_DRIVER, NULL);
151047e946e7SWyllys Ingersoll
151147e946e7SWyllys Ingersoll /* Set the suspend/resume property */
151247e946e7SWyllys Ingersoll (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
151347e946e7SWyllys Ingersoll "pm-hardware-state", "needs-suspend-resume");
151447e946e7SWyllys Ingersoll
151547e946e7SWyllys Ingersoll mutex_enter(&tpm->pm_mutex);
151647e946e7SWyllys Ingersoll tpm->suspended = 0;
151747e946e7SWyllys Ingersoll mutex_exit(&tpm->pm_mutex);
151847e946e7SWyllys Ingersoll
151947e946e7SWyllys Ingersoll tpm->flags |= TPM_DID_MUTEX;
152047e946e7SWyllys Ingersoll
152147e946e7SWyllys Ingersoll /* Initialize the buffer and the lock associated with it */
152247e946e7SWyllys Ingersoll tpm->bufsize = TPM_IO_BUF_SIZE;
152347e946e7SWyllys Ingersoll tpm->iobuf = kmem_zalloc((sizeof (uint8_t))*(tpm->bufsize), KM_SLEEP);
152447e946e7SWyllys Ingersoll tpm->flags |= TPM_DID_IO_ALLOC;
152547e946e7SWyllys Ingersoll
152647e946e7SWyllys Ingersoll mutex_init(&tpm->iobuf_lock, NULL, MUTEX_DRIVER, NULL);
152747e946e7SWyllys Ingersoll tpm->flags |= TPM_DID_IO_MUTEX;
152847e946e7SWyllys Ingersoll
152947e946e7SWyllys Ingersoll cv_init(&tpm->iobuf_cv, NULL, CV_DRIVER, NULL);
153047e946e7SWyllys Ingersoll tpm->flags |= TPM_DID_IO_CV;
153147e946e7SWyllys Ingersoll
153247e946e7SWyllys Ingersoll /* Create minor node */
153347e946e7SWyllys Ingersoll ret = ddi_create_minor_node(dip, "tpm", S_IFCHR, ddi_get_instance(dip),
153447e946e7SWyllys Ingersoll DDI_PSEUDO, 0);
153547e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS) {
1536*ccf625adSWyllys Ingersoll #ifdef DEBUG
1537*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: ddi_create_minor_node failed", myname);
1538*ccf625adSWyllys Ingersoll #endif
153947e946e7SWyllys Ingersoll goto FAIL;
154047e946e7SWyllys Ingersoll }
154147e946e7SWyllys Ingersoll tpm->flags |= TPM_DIDMINOR;
154247e946e7SWyllys Ingersoll
15438d26100cSWyllys Ingersoll #ifdef KCF_TPM_RNG_PROVIDER
15448d26100cSWyllys Ingersoll /* register RNG with kcf */
15458d26100cSWyllys Ingersoll if (tpmrng_register(tpm) != DDI_SUCCESS)
1546*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: tpm RNG failed to register with kcf",
15478d26100cSWyllys Ingersoll myname);
15488d26100cSWyllys Ingersoll #endif
15498d26100cSWyllys Ingersoll
155047e946e7SWyllys Ingersoll return (DDI_SUCCESS);
155147e946e7SWyllys Ingersoll FAIL:
155247e946e7SWyllys Ingersoll if (tpm != NULL) {
155347e946e7SWyllys Ingersoll tpm_cleanup(dip, tpm);
155447e946e7SWyllys Ingersoll ddi_soft_state_free(statep, instance);
155547e946e7SWyllys Ingersoll tpm = NULL;
155647e946e7SWyllys Ingersoll }
155747e946e7SWyllys Ingersoll
155847e946e7SWyllys Ingersoll return (DDI_FAILURE);
155947e946e7SWyllys Ingersoll }
156047e946e7SWyllys Ingersoll
156147e946e7SWyllys Ingersoll /*
156247e946e7SWyllys Ingersoll * Called by tpm_detach and tpm_attach (only on failure)
156347e946e7SWyllys Ingersoll * Free up the resources that are allocated
156447e946e7SWyllys Ingersoll */
156547e946e7SWyllys Ingersoll static void
tpm_cleanup(dev_info_t * dip,tpm_state_t * tpm)156647e946e7SWyllys Ingersoll tpm_cleanup(dev_info_t *dip, tpm_state_t *tpm)
156747e946e7SWyllys Ingersoll {
156847e946e7SWyllys Ingersoll if (tpm == NULL)
156947e946e7SWyllys Ingersoll return;
157047e946e7SWyllys Ingersoll
15718d26100cSWyllys Ingersoll #ifdef KCF_TPM_RNG_PROVIDER
15728d26100cSWyllys Ingersoll (void) tpmrng_unregister(tpm);
15738d26100cSWyllys Ingersoll #endif
15748d26100cSWyllys Ingersoll
15758d26100cSWyllys Ingersoll #ifdef sun4v
15768d26100cSWyllys Ingersoll if (tpm->flags & TPM_HSVC_REGISTERED) {
15778d26100cSWyllys Ingersoll (void) hsvc_unregister(&hsvc_tpm);
15788d26100cSWyllys Ingersoll tpm->flags &= ~(TPM_HSVC_REGISTERED);
15798d26100cSWyllys Ingersoll }
15808d26100cSWyllys Ingersoll #endif
158147e946e7SWyllys Ingersoll if (tpm->flags & TPM_DID_MUTEX) {
158247e946e7SWyllys Ingersoll mutex_destroy(&tpm->dev_lock);
1583d25a0be9SWyllys Ingersoll mutex_destroy(&tpm->pm_mutex);
1584d25a0be9SWyllys Ingersoll cv_destroy(&tpm->suspend_cv);
158547e946e7SWyllys Ingersoll tpm->flags &= ~(TPM_DID_MUTEX);
158647e946e7SWyllys Ingersoll }
158747e946e7SWyllys Ingersoll if (tpm->flags & TPM_DID_IO_ALLOC) {
158847e946e7SWyllys Ingersoll ASSERT(tpm->iobuf != NULL);
158947e946e7SWyllys Ingersoll kmem_free(tpm->iobuf, (sizeof (uint8_t))*(tpm->bufsize));
159047e946e7SWyllys Ingersoll tpm->flags &= ~(TPM_DID_IO_ALLOC);
159147e946e7SWyllys Ingersoll }
159247e946e7SWyllys Ingersoll if (tpm->flags & TPM_DID_IO_MUTEX) {
159347e946e7SWyllys Ingersoll mutex_destroy(&tpm->iobuf_lock);
159447e946e7SWyllys Ingersoll tpm->flags &= ~(TPM_DID_IO_MUTEX);
159547e946e7SWyllys Ingersoll }
159647e946e7SWyllys Ingersoll if (tpm->flags & TPM_DID_IO_CV) {
159747e946e7SWyllys Ingersoll cv_destroy(&tpm->iobuf_cv);
159847e946e7SWyllys Ingersoll tpm->flags &= ~(TPM_DID_IO_CV);
159947e946e7SWyllys Ingersoll }
160047e946e7SWyllys Ingersoll if (tpm->flags & TPM_DIDREGSMAP) {
160147e946e7SWyllys Ingersoll /* Free the mapped addresses */
160247e946e7SWyllys Ingersoll if (tpm->handle != NULL)
160347e946e7SWyllys Ingersoll ddi_regs_map_free(&tpm->handle);
160447e946e7SWyllys Ingersoll tpm->flags &= ~(TPM_DIDREGSMAP);
160547e946e7SWyllys Ingersoll }
160647e946e7SWyllys Ingersoll if (tpm->flags & TPM_DIDMINOR) {
160747e946e7SWyllys Ingersoll /* Remove minor node */
160847e946e7SWyllys Ingersoll ddi_remove_minor_node(dip, NULL);
160947e946e7SWyllys Ingersoll tpm->flags &= ~(TPM_DIDMINOR);
161047e946e7SWyllys Ingersoll }
161147e946e7SWyllys Ingersoll }
161247e946e7SWyllys Ingersoll
161347e946e7SWyllys Ingersoll static int
tpm_suspend(tpm_state_t * tpm)161447e946e7SWyllys Ingersoll tpm_suspend(tpm_state_t *tpm)
161547e946e7SWyllys Ingersoll {
161647e946e7SWyllys Ingersoll if (tpm == NULL)
161747e946e7SWyllys Ingersoll return (DDI_FAILURE);
161847e946e7SWyllys Ingersoll mutex_enter(&tpm->pm_mutex);
161947e946e7SWyllys Ingersoll if (tpm->suspended) {
162047e946e7SWyllys Ingersoll mutex_exit(&tpm->pm_mutex);
162147e946e7SWyllys Ingersoll return (DDI_SUCCESS);
162247e946e7SWyllys Ingersoll }
162347e946e7SWyllys Ingersoll tpm->suspended = 1;
162447e946e7SWyllys Ingersoll mutex_exit(&tpm->pm_mutex);
162547e946e7SWyllys Ingersoll
162647e946e7SWyllys Ingersoll return (DDI_SUCCESS);
162747e946e7SWyllys Ingersoll }
162847e946e7SWyllys Ingersoll
162947e946e7SWyllys Ingersoll static int
tpm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)163047e946e7SWyllys Ingersoll tpm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
163147e946e7SWyllys Ingersoll {
163247e946e7SWyllys Ingersoll char *myname = "tpm_detach";
163347e946e7SWyllys Ingersoll int instance;
163447e946e7SWyllys Ingersoll tpm_state_t *tpm;
163547e946e7SWyllys Ingersoll
163647e946e7SWyllys Ingersoll ASSERT(dip != NULL);
163747e946e7SWyllys Ingersoll
163847e946e7SWyllys Ingersoll instance = ddi_get_instance(dip);
16398d26100cSWyllys Ingersoll if (instance < 0)
16408d26100cSWyllys Ingersoll return (DDI_FAILURE);
16418d26100cSWyllys Ingersoll
164247e946e7SWyllys Ingersoll if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1643*ccf625adSWyllys Ingersoll #ifdef DEBUG
1644*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
164547e946e7SWyllys Ingersoll myname);
1646*ccf625adSWyllys Ingersoll #endif
164747e946e7SWyllys Ingersoll return (ENXIO);
164847e946e7SWyllys Ingersoll }
164947e946e7SWyllys Ingersoll
165047e946e7SWyllys Ingersoll switch (cmd) {
165147e946e7SWyllys Ingersoll case DDI_DETACH:
165247e946e7SWyllys Ingersoll /* Body is after the switch stmt */
165347e946e7SWyllys Ingersoll break;
165447e946e7SWyllys Ingersoll case DDI_SUSPEND:
165547e946e7SWyllys Ingersoll return (tpm_suspend(tpm));
165647e946e7SWyllys Ingersoll default:
1657*ccf625adSWyllys Ingersoll #ifdef DEBUG
1658*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: case %d not implemented", myname, cmd);
1659*ccf625adSWyllys Ingersoll #endif
166047e946e7SWyllys Ingersoll return (DDI_FAILURE);
166147e946e7SWyllys Ingersoll }
166247e946e7SWyllys Ingersoll
166347e946e7SWyllys Ingersoll /* Since we are freeing tpm structure, we need to gain the lock */
166447e946e7SWyllys Ingersoll tpm_cleanup(dip, tpm);
166547e946e7SWyllys Ingersoll
166647e946e7SWyllys Ingersoll /* Free the soft state */
166747e946e7SWyllys Ingersoll ddi_soft_state_free(statep, instance);
166847e946e7SWyllys Ingersoll tpm = NULL;
166947e946e7SWyllys Ingersoll
167047e946e7SWyllys Ingersoll return (DDI_SUCCESS);
167147e946e7SWyllys Ingersoll }
167247e946e7SWyllys Ingersoll
167347e946e7SWyllys Ingersoll /*ARGSUSED*/
167447e946e7SWyllys Ingersoll static int
tpm_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)167547e946e7SWyllys Ingersoll tpm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
167647e946e7SWyllys Ingersoll {
167747e946e7SWyllys Ingersoll char *myname = "tpm_getinfo";
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) {
1683*ccf625adSWyllys Ingersoll #ifdef DEBUG
1684*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
168547e946e7SWyllys Ingersoll myname);
1686*ccf625adSWyllys 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:
1698*ccf625adSWyllys Ingersoll #ifdef DEBUG
1699*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: cmd %d is not implemented", myname, cmd);
1700*ccf625adSWyllys Ingersoll #endif
170147e946e7SWyllys Ingersoll return (DDI_FAILURE);
170247e946e7SWyllys Ingersoll }
170347e946e7SWyllys Ingersoll return (DDI_SUCCESS);
170447e946e7SWyllys Ingersoll }
170547e946e7SWyllys Ingersoll
170647e946e7SWyllys Ingersoll /*
170747e946e7SWyllys Ingersoll * Driver entry points
170847e946e7SWyllys Ingersoll */
170947e946e7SWyllys Ingersoll
171047e946e7SWyllys Ingersoll /*ARGSUSED*/
171147e946e7SWyllys Ingersoll static int
tpm_open(dev_t * devp,int flag,int otyp,cred_t * cred)171247e946e7SWyllys Ingersoll tpm_open(dev_t *devp, int flag, int otyp, cred_t *cred)
171347e946e7SWyllys Ingersoll {
171447e946e7SWyllys Ingersoll char *myname = "tpm_open";
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) {
1722*ccf625adSWyllys Ingersoll #ifdef DEBUG
1723*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
172447e946e7SWyllys Ingersoll myname);
1725*ccf625adSWyllys Ingersoll #endif
172647e946e7SWyllys Ingersoll return (ENXIO);
172747e946e7SWyllys Ingersoll }
172847e946e7SWyllys Ingersoll if (otyp != OTYP_CHR) {
1729*ccf625adSWyllys Ingersoll #ifdef DEBUG
1730*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: otyp(%d) != OTYP_CHR(%d)",
173147e946e7SWyllys Ingersoll myname, otyp, OTYP_CHR);
1732*ccf625adSWyllys 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) {
1739*ccf625adSWyllys Ingersoll #ifdef DEBUG
1740*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: the device is already being used",
174147e946e7SWyllys Ingersoll myname);
1742*ccf625adSWyllys 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 char *myname = "tpm_close";
175947e946e7SWyllys Ingersoll int instance;
176047e946e7SWyllys Ingersoll tpm_state_t *tpm;
176147e946e7SWyllys Ingersoll
176247e946e7SWyllys Ingersoll instance = getminor(dev);
176347e946e7SWyllys Ingersoll if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1764*ccf625adSWyllys Ingersoll #ifdef DEBUG
1765*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
176647e946e7SWyllys Ingersoll myname);
1767*ccf625adSWyllys Ingersoll #endif
176847e946e7SWyllys Ingersoll return (ENXIO);
176947e946e7SWyllys Ingersoll }
177047e946e7SWyllys Ingersoll if (otyp != OTYP_CHR) {
1771*ccf625adSWyllys Ingersoll #ifdef DEBUG
1772*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: otyp(%d) != OTYP_CHR(%d)",
177347e946e7SWyllys Ingersoll myname, otyp, OTYP_CHR);
1774*ccf625adSWyllys Ingersoll #endif
177547e946e7SWyllys Ingersoll return (EINVAL);
177647e946e7SWyllys Ingersoll }
17778d26100cSWyllys Ingersoll TPM_EXCLUSIVE_LOCK(tpm);
177847e946e7SWyllys Ingersoll
177947e946e7SWyllys Ingersoll ASSERT(tpm->dev_held);
178047e946e7SWyllys Ingersoll
178147e946e7SWyllys Ingersoll mutex_enter(&tpm->dev_lock);
178247e946e7SWyllys Ingersoll ASSERT(mutex_owned(&tpm->dev_lock));
178347e946e7SWyllys Ingersoll tpm->dev_held = 0;
178447e946e7SWyllys Ingersoll mutex_exit(&tpm->dev_lock);
178547e946e7SWyllys Ingersoll
178647e946e7SWyllys Ingersoll return (0);
178747e946e7SWyllys Ingersoll }
178847e946e7SWyllys Ingersoll
178947e946e7SWyllys Ingersoll /*ARGSUSED*/
179047e946e7SWyllys Ingersoll static int
tpm_read(dev_t dev,struct uio * uiop,cred_t * credp)179147e946e7SWyllys Ingersoll tpm_read(dev_t dev, struct uio *uiop, cred_t *credp)
179247e946e7SWyllys Ingersoll {
179347e946e7SWyllys Ingersoll int ret;
179447e946e7SWyllys Ingersoll uint32_t size;
179547e946e7SWyllys Ingersoll char *myname = "tpm_read";
179647e946e7SWyllys Ingersoll int instance;
179747e946e7SWyllys Ingersoll tpm_state_t *tpm;
179847e946e7SWyllys Ingersoll
179947e946e7SWyllys Ingersoll instance = getminor(dev);
180047e946e7SWyllys Ingersoll if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1801*ccf625adSWyllys Ingersoll #ifdef DEBUG
1802*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
180347e946e7SWyllys Ingersoll myname);
1804*ccf625adSWyllys Ingersoll #endif
180547e946e7SWyllys Ingersoll return (ENXIO);
180647e946e7SWyllys Ingersoll }
180747e946e7SWyllys Ingersoll if (uiop == NULL) {
1808*ccf625adSWyllys Ingersoll #ifdef DEBUG
1809*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: passed in uiop is NULL", myname);
1810*ccf625adSWyllys Ingersoll #endif
181147e946e7SWyllys Ingersoll return (EFAULT);
181247e946e7SWyllys Ingersoll }
181347e946e7SWyllys Ingersoll
18148d26100cSWyllys Ingersoll TPM_EXCLUSIVE_LOCK(tpm);
181547e946e7SWyllys Ingersoll
181647e946e7SWyllys Ingersoll /* Receive the data after requiring the lock */
18178d26100cSWyllys Ingersoll ret = tpm_io_lock(tpm);
181847e946e7SWyllys Ingersoll
181947e946e7SWyllys Ingersoll /* Timeout reached */
18208d26100cSWyllys Ingersoll if (ret)
182147e946e7SWyllys Ingersoll return (ret);
182247e946e7SWyllys Ingersoll
182347e946e7SWyllys Ingersoll if (uiop->uio_resid > tpm->bufsize) {
1824*ccf625adSWyllys Ingersoll #ifdef DEBUG
1825*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: read_in data is bigger "
182647e946e7SWyllys Ingersoll "than tpm->bufsize:read in:%d, bufsiz:%d",
182747e946e7SWyllys Ingersoll myname, (int)uiop->uio_resid, (int)tpm->bufsize);
1828*ccf625adSWyllys Ingersoll #endif
182947e946e7SWyllys Ingersoll ret = EIO;
183047e946e7SWyllys Ingersoll goto OUT;
183147e946e7SWyllys Ingersoll }
183247e946e7SWyllys Ingersoll
183347e946e7SWyllys Ingersoll ret = tis_recv_data(tpm, tpm->iobuf, tpm->bufsize);
183447e946e7SWyllys Ingersoll if (ret < TPM_HEADER_SIZE) {
1835*ccf625adSWyllys Ingersoll #ifdef DEBUG
1836*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: tis_recv_data returned error", myname);
1837*ccf625adSWyllys Ingersoll #endif
183847e946e7SWyllys Ingersoll ret = EIO;
183947e946e7SWyllys Ingersoll goto OUT;
184047e946e7SWyllys Ingersoll }
184147e946e7SWyllys Ingersoll
184247e946e7SWyllys Ingersoll size = load32(tpm->iobuf, 2);
184347e946e7SWyllys Ingersoll if (ret != size) {
1844*ccf625adSWyllys Ingersoll #ifdef DEBUG
1845*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: tis_recv_data:"
184647e946e7SWyllys Ingersoll "expected size=%d, actually read=%d",
184747e946e7SWyllys Ingersoll myname, size, ret);
1848*ccf625adSWyllys Ingersoll #endif
184947e946e7SWyllys Ingersoll ret = EIO;
185047e946e7SWyllys Ingersoll goto OUT;
185147e946e7SWyllys Ingersoll }
185247e946e7SWyllys Ingersoll
185347e946e7SWyllys Ingersoll /* Send the buffer from the kernel to the userspace */
185447e946e7SWyllys Ingersoll ret = uiomove(tpm->iobuf, size, UIO_READ, uiop);
185547e946e7SWyllys Ingersoll if (ret) {
1856*ccf625adSWyllys Ingersoll #ifdef DEBUG
1857*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: uiomove returned error", myname);
1858*ccf625adSWyllys Ingersoll #endif
185947e946e7SWyllys Ingersoll goto OUT;
186047e946e7SWyllys Ingersoll }
186147e946e7SWyllys Ingersoll
186247e946e7SWyllys Ingersoll /* Zeroize the buffer... */
186347e946e7SWyllys Ingersoll bzero(tpm->iobuf, tpm->bufsize);
186447e946e7SWyllys Ingersoll ret = DDI_SUCCESS;
186547e946e7SWyllys Ingersoll OUT:
186647e946e7SWyllys Ingersoll /* We are done now: wake up the waiting threads */
186747e946e7SWyllys Ingersoll tpm_unlock(tpm);
186847e946e7SWyllys Ingersoll
186947e946e7SWyllys Ingersoll return (ret);
187047e946e7SWyllys Ingersoll }
187147e946e7SWyllys Ingersoll
187247e946e7SWyllys Ingersoll /*ARGSUSED*/
187347e946e7SWyllys Ingersoll static int
tpm_write(dev_t dev,struct uio * uiop,cred_t * credp)187447e946e7SWyllys Ingersoll tpm_write(dev_t dev, struct uio *uiop, cred_t *credp)
187547e946e7SWyllys Ingersoll {
187647e946e7SWyllys Ingersoll int ret;
187747e946e7SWyllys Ingersoll size_t len;
187847e946e7SWyllys Ingersoll uint32_t size;
187947e946e7SWyllys Ingersoll char *myname = "tpm_write";
188047e946e7SWyllys Ingersoll int instance;
188147e946e7SWyllys Ingersoll tpm_state_t *tpm;
188247e946e7SWyllys Ingersoll
188347e946e7SWyllys Ingersoll instance = getminor(dev);
188447e946e7SWyllys Ingersoll if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) {
1885*ccf625adSWyllys Ingersoll #ifdef DEBUG
1886*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: stored pointer to tpm state is NULL",
188747e946e7SWyllys Ingersoll myname);
1888*ccf625adSWyllys Ingersoll #endif
188947e946e7SWyllys Ingersoll return (ENXIO);
189047e946e7SWyllys Ingersoll }
189147e946e7SWyllys Ingersoll
189247e946e7SWyllys Ingersoll if (uiop == NULL) {
1893*ccf625adSWyllys Ingersoll #ifdef DEBUG
1894*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: passed in uiop is NULL", myname);
1895*ccf625adSWyllys Ingersoll #endif
189647e946e7SWyllys Ingersoll return (EFAULT);
189747e946e7SWyllys Ingersoll }
189847e946e7SWyllys Ingersoll
18998d26100cSWyllys Ingersoll TPM_EXCLUSIVE_LOCK(tpm);
190047e946e7SWyllys Ingersoll
190147e946e7SWyllys Ingersoll len = uiop->uio_resid;
190247e946e7SWyllys Ingersoll if (len == 0) {
1903*ccf625adSWyllys Ingersoll #ifdef DEBUG
1904*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: requested read of len 0", myname);
1905*ccf625adSWyllys Ingersoll #endif
190647e946e7SWyllys Ingersoll return (0);
190747e946e7SWyllys Ingersoll }
190847e946e7SWyllys Ingersoll
190947e946e7SWyllys Ingersoll /* Get the lock for using iobuf */
19108d26100cSWyllys Ingersoll ret = tpm_io_lock(tpm);
191147e946e7SWyllys Ingersoll /* Timeout Reached */
19128d26100cSWyllys Ingersoll if (ret)
191347e946e7SWyllys Ingersoll return (ret);
191447e946e7SWyllys Ingersoll
191547e946e7SWyllys Ingersoll /* Copy the header and parse the structure to find out the size... */
191647e946e7SWyllys Ingersoll ret = uiomove(tpm->iobuf, TPM_HEADER_SIZE, UIO_WRITE, uiop);
191747e946e7SWyllys Ingersoll if (ret) {
1918*ccf625adSWyllys Ingersoll #ifdef DEBUG
1919*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: uiomove returned error"
192047e946e7SWyllys Ingersoll "while getting the the header",
192147e946e7SWyllys Ingersoll myname);
1922*ccf625adSWyllys Ingersoll #endif
192347e946e7SWyllys Ingersoll goto OUT;
192447e946e7SWyllys Ingersoll }
192547e946e7SWyllys Ingersoll
192647e946e7SWyllys Ingersoll /* Get the buffersize from the command buffer structure */
192747e946e7SWyllys Ingersoll size = load32(tpm->iobuf, TPM_PARAMSIZE_OFFSET);
192847e946e7SWyllys Ingersoll
192947e946e7SWyllys Ingersoll /* Copy the command to the contiguous buffer */
193047e946e7SWyllys Ingersoll if (size > tpm->bufsize) {
1931*ccf625adSWyllys Ingersoll #ifdef DEBUG
1932*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: size %d is greater than "
1933*ccf625adSWyllys Ingersoll "the tpm input buffer size %d",
193447e946e7SWyllys Ingersoll myname, (int)size, (int)tpm->bufsize);
1935*ccf625adSWyllys Ingersoll #endif
193647e946e7SWyllys Ingersoll ret = ENXIO;
193747e946e7SWyllys Ingersoll goto OUT;
193847e946e7SWyllys Ingersoll }
193947e946e7SWyllys Ingersoll
194047e946e7SWyllys Ingersoll /* Copy the buffer from the userspace to kernel */
194147e946e7SWyllys Ingersoll ret = uiomove(tpm->iobuf+TPM_HEADER_SIZE, size-TPM_HEADER_SIZE,
194247e946e7SWyllys Ingersoll UIO_WRITE, uiop);
194347e946e7SWyllys Ingersoll
194447e946e7SWyllys Ingersoll if (ret) {
1945*ccf625adSWyllys Ingersoll #ifdef DEBUG
1946*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: uiomove returned error"
194747e946e7SWyllys Ingersoll "while getting the rest of the command", myname);
1948*ccf625adSWyllys Ingersoll #endif
194947e946e7SWyllys Ingersoll goto OUT;
195047e946e7SWyllys Ingersoll }
195147e946e7SWyllys Ingersoll
195247e946e7SWyllys Ingersoll /* Send the command */
195347e946e7SWyllys Ingersoll ret = tis_send_data(tpm, tpm->iobuf, size);
195447e946e7SWyllys Ingersoll if (ret != DDI_SUCCESS) {
1955*ccf625adSWyllys Ingersoll #ifdef DEBUG
1956*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!%s: tis_send_data returned error", myname);
1957*ccf625adSWyllys Ingersoll #endif
195847e946e7SWyllys Ingersoll ret = EFAULT;
195947e946e7SWyllys Ingersoll goto OUT;
196047e946e7SWyllys Ingersoll }
196147e946e7SWyllys Ingersoll
196247e946e7SWyllys Ingersoll /* Zeroize the buffer... */
196347e946e7SWyllys Ingersoll bzero(tpm->iobuf, tpm->bufsize);
196447e946e7SWyllys Ingersoll ret = DDI_SUCCESS;
196547e946e7SWyllys Ingersoll OUT:
196647e946e7SWyllys Ingersoll tpm_unlock(tpm);
196747e946e7SWyllys Ingersoll return (ret);
196847e946e7SWyllys Ingersoll }
196947e946e7SWyllys Ingersoll
197047e946e7SWyllys Ingersoll /*
197147e946e7SWyllys Ingersoll * This is to deal with the contentions for the iobuf
197247e946e7SWyllys Ingersoll */
197347e946e7SWyllys Ingersoll static inline int
tpm_io_lock(tpm_state_t * tpm)19748d26100cSWyllys Ingersoll tpm_io_lock(tpm_state_t *tpm)
197547e946e7SWyllys Ingersoll {
197647e946e7SWyllys Ingersoll int ret;
197747e946e7SWyllys Ingersoll clock_t timeout;
197847e946e7SWyllys Ingersoll
197947e946e7SWyllys Ingersoll mutex_enter(&tpm->iobuf_lock);
198047e946e7SWyllys Ingersoll ASSERT(mutex_owned(&tpm->iobuf_lock));
198147e946e7SWyllys Ingersoll
198247e946e7SWyllys Ingersoll timeout = ddi_get_lbolt() + drv_usectohz(TPM_IO_TIMEOUT);
198347e946e7SWyllys Ingersoll
198447e946e7SWyllys Ingersoll /* Wait until the iobuf becomes free with the timeout */
198547e946e7SWyllys Ingersoll while (tpm->iobuf_inuse) {
198647e946e7SWyllys Ingersoll ret = cv_timedwait(&tpm->iobuf_cv, &tpm->iobuf_lock, timeout);
198747e946e7SWyllys Ingersoll if (ret <= 0) {
198847e946e7SWyllys Ingersoll /* Timeout reached */
198947e946e7SWyllys Ingersoll mutex_exit(&tpm->iobuf_lock);
19908d26100cSWyllys Ingersoll #ifdef DEBUG
1991*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!tpm_io_lock:iorequest timed out");
19928d26100cSWyllys Ingersoll #endif
1993b693132fSjmcp return (ETIME);
199447e946e7SWyllys Ingersoll }
199547e946e7SWyllys Ingersoll }
199647e946e7SWyllys Ingersoll tpm->iobuf_inuse = 1;
199747e946e7SWyllys Ingersoll mutex_exit(&tpm->iobuf_lock);
199847e946e7SWyllys Ingersoll return (0);
199947e946e7SWyllys Ingersoll }
200047e946e7SWyllys Ingersoll
200147e946e7SWyllys Ingersoll /*
200247e946e7SWyllys Ingersoll * This is to deal with the contentions for the iobuf
200347e946e7SWyllys Ingersoll */
200447e946e7SWyllys Ingersoll static inline void
tpm_unlock(tpm_state_t * tpm)200547e946e7SWyllys Ingersoll tpm_unlock(tpm_state_t *tpm)
200647e946e7SWyllys Ingersoll {
200747e946e7SWyllys Ingersoll /* Wake up the waiting threads */
200847e946e7SWyllys Ingersoll mutex_enter(&tpm->iobuf_lock);
200947e946e7SWyllys Ingersoll ASSERT(tpm->iobuf_inuse == 1 && mutex_owned(&tpm->iobuf_lock));
201047e946e7SWyllys Ingersoll tpm->iobuf_inuse = 0;
201147e946e7SWyllys Ingersoll cv_broadcast(&tpm->iobuf_cv);
201247e946e7SWyllys Ingersoll mutex_exit(&tpm->iobuf_lock);
201347e946e7SWyllys Ingersoll }
20148d26100cSWyllys Ingersoll
20158d26100cSWyllys Ingersoll #ifdef KCF_TPM_RNG_PROVIDER
20168d26100cSWyllys Ingersoll /*
20178d26100cSWyllys Ingersoll * Random number generator entry points
20188d26100cSWyllys Ingersoll */
20198d26100cSWyllys Ingersoll static void
strncpy_spacepad(uchar_t * s1,char * s2,int n)20208d26100cSWyllys Ingersoll strncpy_spacepad(uchar_t *s1, char *s2, int n)
20218d26100cSWyllys Ingersoll {
20228d26100cSWyllys Ingersoll int s2len = strlen(s2);
20238d26100cSWyllys Ingersoll (void) strncpy((char *)s1, s2, n);
20248d26100cSWyllys Ingersoll if (s2len < n)
20258d26100cSWyllys Ingersoll (void) memset(s1 + s2len, ' ', n - s2len);
20268d26100cSWyllys Ingersoll }
20278d26100cSWyllys Ingersoll
20288d26100cSWyllys Ingersoll /*ARGSUSED*/
20298d26100cSWyllys Ingersoll static int
tpmrng_ext_info(crypto_provider_handle_t prov,crypto_provider_ext_info_t * ext_info,crypto_req_handle_t cfreq)20308d26100cSWyllys Ingersoll tpmrng_ext_info(crypto_provider_handle_t prov,
20318d26100cSWyllys Ingersoll crypto_provider_ext_info_t *ext_info,
20328d26100cSWyllys Ingersoll crypto_req_handle_t cfreq)
20338d26100cSWyllys Ingersoll {
20348d26100cSWyllys Ingersoll tpm_state_t *tpm = (tpm_state_t *)prov;
20358d26100cSWyllys Ingersoll char buf[64];
20368d26100cSWyllys Ingersoll
20378d26100cSWyllys Ingersoll if (tpm == NULL)
20388d26100cSWyllys Ingersoll return (DDI_FAILURE);
20398d26100cSWyllys Ingersoll
20408d26100cSWyllys Ingersoll strncpy_spacepad(ext_info->ei_manufacturerID,
20418d26100cSWyllys Ingersoll (char *)tpm->vers_info.tpmVendorID,
20428d26100cSWyllys Ingersoll sizeof (ext_info->ei_manufacturerID));
20438d26100cSWyllys Ingersoll
20448d26100cSWyllys Ingersoll strncpy_spacepad(ext_info->ei_model, "0",
20458d26100cSWyllys Ingersoll sizeof (ext_info->ei_model));
20468d26100cSWyllys Ingersoll strncpy_spacepad(ext_info->ei_serial_number, "0",
20478d26100cSWyllys Ingersoll sizeof (ext_info->ei_serial_number));
20488d26100cSWyllys Ingersoll
20498d26100cSWyllys Ingersoll ext_info->ei_flags = CRYPTO_EXTF_RNG | CRYPTO_EXTF_SO_PIN_LOCKED;
20508d26100cSWyllys Ingersoll ext_info->ei_max_session_count = CRYPTO_EFFECTIVELY_INFINITE;
20518d26100cSWyllys Ingersoll ext_info->ei_max_pin_len = 0;
20528d26100cSWyllys Ingersoll ext_info->ei_min_pin_len = 0;
20538d26100cSWyllys Ingersoll ext_info->ei_total_public_memory = CRYPTO_UNAVAILABLE_INFO;
20548d26100cSWyllys Ingersoll ext_info->ei_free_public_memory = CRYPTO_UNAVAILABLE_INFO;
20558d26100cSWyllys Ingersoll ext_info->ei_total_private_memory = CRYPTO_UNAVAILABLE_INFO;
20568d26100cSWyllys Ingersoll ext_info->ei_free_public_memory = CRYPTO_UNAVAILABLE_INFO;
20578d26100cSWyllys Ingersoll ext_info->ei_time[0] = 0;
20588d26100cSWyllys Ingersoll
20598d26100cSWyllys Ingersoll ext_info->ei_hardware_version.cv_major = tpm->vers_info.version.major;
20608d26100cSWyllys Ingersoll ext_info->ei_hardware_version.cv_minor = tpm->vers_info.version.minor;
20618d26100cSWyllys Ingersoll ext_info->ei_firmware_version.cv_major =
20628d26100cSWyllys Ingersoll tpm->vers_info.version.revMajor;
20638d26100cSWyllys Ingersoll ext_info->ei_firmware_version.cv_minor =
20648d26100cSWyllys Ingersoll tpm->vers_info.version.revMinor;
20658d26100cSWyllys Ingersoll
20668d26100cSWyllys Ingersoll (void) snprintf(buf, sizeof (buf), "tpmrng TPM RNG");
20678d26100cSWyllys Ingersoll
20688d26100cSWyllys Ingersoll strncpy_spacepad(ext_info->ei_label, buf,
20698d26100cSWyllys Ingersoll sizeof (ext_info->ei_label));
20708d26100cSWyllys Ingersoll #undef BUFSZ
20718d26100cSWyllys Ingersoll return (CRYPTO_SUCCESS);
20728d26100cSWyllys Ingersoll
20738d26100cSWyllys Ingersoll }
20748d26100cSWyllys Ingersoll
20758d26100cSWyllys Ingersoll static int
tpmrng_register(tpm_state_t * tpm)20768d26100cSWyllys Ingersoll tpmrng_register(tpm_state_t *tpm)
20778d26100cSWyllys Ingersoll {
20788d26100cSWyllys Ingersoll int ret;
20798d26100cSWyllys Ingersoll char ID[64];
20808d26100cSWyllys Ingersoll crypto_mech_name_t *rngmech;
20818d26100cSWyllys Ingersoll
20828d26100cSWyllys Ingersoll ASSERT(tpm != NULL);
20838d26100cSWyllys Ingersoll
20848d26100cSWyllys Ingersoll (void) snprintf(ID, sizeof (ID), "tpmrng %s", IDENT_TPMRNG);
20858d26100cSWyllys Ingersoll
20868d26100cSWyllys Ingersoll tpmrng_prov_info.pi_provider_description = ID;
20878d26100cSWyllys Ingersoll tpmrng_prov_info.pi_provider_dev.pd_hw = tpm->dip;
20888d26100cSWyllys Ingersoll tpmrng_prov_info.pi_provider_handle = tpm;
20898d26100cSWyllys Ingersoll
20908d26100cSWyllys Ingersoll ret = crypto_register_provider(&tpmrng_prov_info, &tpm->n_prov);
20918d26100cSWyllys Ingersoll if (ret != CRYPTO_SUCCESS) {
20928d26100cSWyllys Ingersoll tpm->n_prov = NULL;
20938d26100cSWyllys Ingersoll return (DDI_FAILURE);
20948d26100cSWyllys Ingersoll }
20958d26100cSWyllys Ingersoll
20968d26100cSWyllys Ingersoll crypto_provider_notification(tpm->n_prov, CRYPTO_PROVIDER_READY);
20978d26100cSWyllys Ingersoll
20988d26100cSWyllys Ingersoll rngmech = kmem_zalloc(strlen("random") + 1, KM_SLEEP);
20998d26100cSWyllys Ingersoll (void) memcpy(rngmech, "random", 6);
21008d26100cSWyllys Ingersoll ret = crypto_load_dev_disabled("tpm", ddi_get_instance(tpm->dip),
21018d26100cSWyllys Ingersoll 1, rngmech);
2102*ccf625adSWyllys Ingersoll #ifdef DEBUG
2103*ccf625adSWyllys Ingersoll if (ret != CRYPTO_SUCCESS)
2104*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!crypto_load_dev_disabled failed (%d)", ret);
2105*ccf625adSWyllys Ingersoll #endif
21068d26100cSWyllys Ingersoll return (DDI_SUCCESS);
21078d26100cSWyllys Ingersoll }
21088d26100cSWyllys Ingersoll
21098d26100cSWyllys Ingersoll static int
tpmrng_unregister(tpm_state_t * tpm)21108d26100cSWyllys Ingersoll tpmrng_unregister(tpm_state_t *tpm)
21118d26100cSWyllys Ingersoll {
21128d26100cSWyllys Ingersoll int ret;
21138d26100cSWyllys Ingersoll ASSERT(tpm != NULL);
21148d26100cSWyllys Ingersoll if (tpm->n_prov) {
21158d26100cSWyllys Ingersoll ret = crypto_unregister_provider(tpm->n_prov);
21168d26100cSWyllys Ingersoll tpm->n_prov = NULL;
21178d26100cSWyllys Ingersoll if (ret != CRYPTO_SUCCESS)
21188d26100cSWyllys Ingersoll return (DDI_FAILURE);
21198d26100cSWyllys Ingersoll }
21208d26100cSWyllys Ingersoll return (DDI_SUCCESS);
21218d26100cSWyllys Ingersoll }
21228d26100cSWyllys Ingersoll
21238d26100cSWyllys Ingersoll /*ARGSUSED*/
21248d26100cSWyllys Ingersoll static void
tpmrng_provider_status(crypto_provider_handle_t provider,uint_t * status)21258d26100cSWyllys Ingersoll tpmrng_provider_status(crypto_provider_handle_t provider, uint_t *status)
21268d26100cSWyllys Ingersoll {
21278d26100cSWyllys Ingersoll *status = CRYPTO_PROVIDER_READY;
21288d26100cSWyllys Ingersoll }
21298d26100cSWyllys Ingersoll
21308d26100cSWyllys Ingersoll /*ARGSUSED*/
21318d26100cSWyllys 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)21328d26100cSWyllys Ingersoll tpmrng_seed_random(crypto_provider_handle_t provider, crypto_session_id_t sid,
21338d26100cSWyllys Ingersoll uchar_t *buf, size_t len, uint_t entropy_est, uint32_t flags,
21348d26100cSWyllys Ingersoll crypto_req_handle_t req)
21358d26100cSWyllys Ingersoll {
21368d26100cSWyllys Ingersoll int ret;
21378d26100cSWyllys Ingersoll tpm_state_t *tpm;
21388d26100cSWyllys Ingersoll uint32_t len32;
21398d26100cSWyllys Ingersoll /* Max length of seed is 256 bytes, add 14 for header. */
21408d26100cSWyllys Ingersoll uint8_t cmdbuf[270] = {
21418d26100cSWyllys Ingersoll 0, 193, /* TPM_TAG_RQU COMMAND */
21428d26100cSWyllys Ingersoll 0, 0, 0, 0x0A, /* paramsize in bytes */
21438d26100cSWyllys Ingersoll 0, 0, 0, TPM_ORD_StirRandom,
21448d26100cSWyllys Ingersoll 0, 0, 0, 0 /* number of input bytes (< 256) */
21458d26100cSWyllys Ingersoll };
21468d26100cSWyllys Ingersoll uint32_t buflen;
21478d26100cSWyllys Ingersoll
21488d26100cSWyllys Ingersoll if (len == 0 || len > 255 || buf == NULL)
21498d26100cSWyllys Ingersoll return (CRYPTO_ARGUMENTS_BAD);
21508d26100cSWyllys Ingersoll
21518d26100cSWyllys Ingersoll tpm = (tpm_state_t *)provider;
21528d26100cSWyllys Ingersoll if (tpm == NULL)
21538d26100cSWyllys Ingersoll return (CRYPTO_INVALID_CONTEXT);
21548d26100cSWyllys Ingersoll
21558d26100cSWyllys Ingersoll /* Acquire lock for exclusive use of TPM */
21568d26100cSWyllys Ingersoll TPM_EXCLUSIVE_LOCK(tpm);
21578d26100cSWyllys Ingersoll
21588d26100cSWyllys Ingersoll ret = tpm_io_lock(tpm);
21598d26100cSWyllys Ingersoll /* Timeout reached */
21608d26100cSWyllys Ingersoll if (ret)
21618d26100cSWyllys Ingersoll return (CRYPTO_BUSY);
21628d26100cSWyllys Ingersoll
21638d26100cSWyllys Ingersoll /* TPM only handles 32 bit length, so truncate if too big. */
21648d26100cSWyllys Ingersoll len32 = (uint32_t)len;
21658d26100cSWyllys Ingersoll buflen = len32 + 14;
21668d26100cSWyllys Ingersoll
21678d26100cSWyllys Ingersoll /* The length must be in network order */
21688d26100cSWyllys Ingersoll buflen = htonl(buflen);
21698d26100cSWyllys Ingersoll bcopy(&buflen, cmdbuf + 2, sizeof (uint32_t));
21708d26100cSWyllys Ingersoll
21718d26100cSWyllys Ingersoll /* Convert it back */
21728d26100cSWyllys Ingersoll buflen = ntohl(buflen);
21738d26100cSWyllys Ingersoll
21748d26100cSWyllys Ingersoll /* length must be in network order */
21758d26100cSWyllys Ingersoll len32 = htonl(len32);
21768d26100cSWyllys Ingersoll bcopy(&len32, cmdbuf + 10, sizeof (uint32_t));
21778d26100cSWyllys Ingersoll
21788d26100cSWyllys Ingersoll /* convert it back */
21798d26100cSWyllys Ingersoll len32 = ntohl(len32);
21808d26100cSWyllys Ingersoll
21818d26100cSWyllys Ingersoll bcopy(buf, cmdbuf + 14, len32);
21828d26100cSWyllys Ingersoll
21838d26100cSWyllys Ingersoll ret = itpm_command(tpm, cmdbuf, buflen);
21848d26100cSWyllys Ingersoll tpm_unlock(tpm);
21858d26100cSWyllys Ingersoll
21868d26100cSWyllys Ingersoll if (ret != DDI_SUCCESS) {
21878d26100cSWyllys Ingersoll #ifdef DEBUG
2188*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!tpmrng_seed_random failed");
21898d26100cSWyllys Ingersoll #endif
21908d26100cSWyllys Ingersoll return (CRYPTO_FAILED);
21918d26100cSWyllys Ingersoll }
21928d26100cSWyllys Ingersoll
21938d26100cSWyllys Ingersoll return (CRYPTO_SUCCESS);
21948d26100cSWyllys Ingersoll }
21958d26100cSWyllys Ingersoll
21968d26100cSWyllys Ingersoll /* ARGSUSED */
21978d26100cSWyllys 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)21988d26100cSWyllys Ingersoll tpmrng_generate_random(crypto_provider_handle_t provider,
21998d26100cSWyllys Ingersoll crypto_session_id_t sid, uchar_t *buf, size_t len, crypto_req_handle_t req)
22008d26100cSWyllys Ingersoll {
22018d26100cSWyllys Ingersoll int ret;
22028d26100cSWyllys Ingersoll tpm_state_t *tpm;
22038d26100cSWyllys Ingersoll uint8_t hdr[14] = {
22048d26100cSWyllys Ingersoll 0, 193, /* TPM_TAG_RQU COMMAND */
22058d26100cSWyllys Ingersoll 0, 0, 0, 14, /* paramsize in bytes */
22068d26100cSWyllys Ingersoll 0, 0, 0, TPM_ORD_GetRandom,
22078d26100cSWyllys Ingersoll 0, 0, 0, 0
22088d26100cSWyllys Ingersoll };
22098d26100cSWyllys Ingersoll uint8_t *cmdbuf = NULL;
22108d26100cSWyllys Ingersoll uint32_t len32 = (uint32_t)len;
22118d26100cSWyllys Ingersoll uint32_t buflen = len32 + sizeof (hdr);
22128d26100cSWyllys Ingersoll
22138d26100cSWyllys Ingersoll if (len == 0 || buf == NULL)
22148d26100cSWyllys Ingersoll return (CRYPTO_ARGUMENTS_BAD);
22158d26100cSWyllys Ingersoll
22168d26100cSWyllys Ingersoll tpm = (tpm_state_t *)provider;
22178d26100cSWyllys Ingersoll if (tpm == NULL)
22188d26100cSWyllys Ingersoll return (CRYPTO_INVALID_CONTEXT);
22198d26100cSWyllys Ingersoll
22208d26100cSWyllys Ingersoll TPM_EXCLUSIVE_LOCK(tpm);
22218d26100cSWyllys Ingersoll
22228d26100cSWyllys Ingersoll ret = tpm_io_lock(tpm);
22238d26100cSWyllys Ingersoll /* Timeout reached */
22248d26100cSWyllys Ingersoll if (ret)
22258d26100cSWyllys Ingersoll return (CRYPTO_BUSY);
22268d26100cSWyllys Ingersoll
22278d26100cSWyllys Ingersoll cmdbuf = kmem_zalloc(buflen, KM_SLEEP);
22288d26100cSWyllys Ingersoll bcopy(hdr, cmdbuf, sizeof (hdr));
22298d26100cSWyllys Ingersoll
22308d26100cSWyllys Ingersoll /* Length is written in network byte order */
22318d26100cSWyllys Ingersoll len32 = htonl(len32);
22328d26100cSWyllys Ingersoll bcopy(&len32, cmdbuf + 10, sizeof (uint32_t));
22338d26100cSWyllys Ingersoll
22348d26100cSWyllys Ingersoll ret = itpm_command(tpm, cmdbuf, buflen);
22358d26100cSWyllys Ingersoll if (ret != DDI_SUCCESS) {
22368d26100cSWyllys Ingersoll #ifdef DEBUG
2237*ccf625adSWyllys Ingersoll cmn_err(CE_WARN, "!tpmrng_generate_random failed");
22388d26100cSWyllys Ingersoll #endif
22398d26100cSWyllys Ingersoll kmem_free(cmdbuf, buflen);
22408d26100cSWyllys Ingersoll tpm_unlock(tpm);
22418d26100cSWyllys Ingersoll return (CRYPTO_FAILED);
22428d26100cSWyllys Ingersoll }
22438d26100cSWyllys Ingersoll
22448d26100cSWyllys Ingersoll /* Find out how many bytes were really returned */
22458d26100cSWyllys Ingersoll len32 = load32(cmdbuf, 10);
22468d26100cSWyllys Ingersoll
22478d26100cSWyllys Ingersoll /* Copy the random bytes back to the callers buffer */
22488d26100cSWyllys Ingersoll bcopy(cmdbuf + 14, buf, len32);
22498d26100cSWyllys Ingersoll
22508d26100cSWyllys Ingersoll kmem_free(cmdbuf, buflen);
22518d26100cSWyllys Ingersoll tpm_unlock(tpm);
22528d26100cSWyllys Ingersoll
22538d26100cSWyllys Ingersoll return (CRYPTO_SUCCESS);
22548d26100cSWyllys Ingersoll }
22558d26100cSWyllys Ingersoll #endif /* KCF_TPM_RNG_PROVIDER */
2256