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