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 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 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 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 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 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 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 38347e946e7SWyllys Ingersoll tpm_quiesce(dev_info_t *dip) 38447e946e7SWyllys Ingersoll { 38547e946e7SWyllys Ingersoll return (DDI_SUCCESS); 38647e946e7SWyllys Ingersoll } 38747e946e7SWyllys Ingersoll 38847e946e7SWyllys Ingersoll static uint32_t 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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