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 48*10346Swyllys.ingersoll@sun.com #ifdef sun4v 49*10346Swyllys.ingersoll@sun.com #include <sys/hypervisor_api.h> 50*10346Swyllys.ingersoll@sun.com #include <sys/hsvc.h> 51*10346Swyllys.ingersoll@sun.com #endif 52*10346Swyllys.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 96*10346Swyllys.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); 146*10346Swyllys.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 220*10346Swyllys.ingersoll@sun.com 221*10346Swyllys.ingersoll@sun.com #ifdef KCF_TPM_RNG_PROVIDER 222*10346Swyllys.ingersoll@sun.com 223*10346Swyllys.ingersoll@sun.com #define IDENT_TPMRNG "TPM Random Number Generator" 224*10346Swyllys.ingersoll@sun.com 225*10346Swyllys.ingersoll@sun.com #include <sys/crypto/common.h> 226*10346Swyllys.ingersoll@sun.com #include <sys/crypto/impl.h> 227*10346Swyllys.ingersoll@sun.com #include <sys/crypto/spi.h> 228*10346Swyllys.ingersoll@sun.com /* 229*10346Swyllys.ingersoll@sun.com * CSPI information (entry points, provider info, etc.) 230*10346Swyllys.ingersoll@sun.com */ 231*10346Swyllys.ingersoll@sun.com static void tpmrng_provider_status(crypto_provider_handle_t, uint_t *); 232*10346Swyllys.ingersoll@sun.com 233*10346Swyllys.ingersoll@sun.com static crypto_control_ops_t tpmrng_control_ops = { 234*10346Swyllys.ingersoll@sun.com tpmrng_provider_status 235*10346Swyllys.ingersoll@sun.com }; 236*10346Swyllys.ingersoll@sun.com 237*10346Swyllys.ingersoll@sun.com static int tpmrng_seed_random(crypto_provider_handle_t, crypto_session_id_t, 238*10346Swyllys.ingersoll@sun.com uchar_t *, size_t, uint_t, uint32_t, crypto_req_handle_t); 239*10346Swyllys.ingersoll@sun.com 240*10346Swyllys.ingersoll@sun.com static int tpmrng_generate_random(crypto_provider_handle_t, 241*10346Swyllys.ingersoll@sun.com crypto_session_id_t, uchar_t *, size_t, crypto_req_handle_t); 242*10346Swyllys.ingersoll@sun.com 243*10346Swyllys.ingersoll@sun.com static crypto_random_number_ops_t tpmrng_random_number_ops = { 244*10346Swyllys.ingersoll@sun.com tpmrng_seed_random, 245*10346Swyllys.ingersoll@sun.com tpmrng_generate_random 246*10346Swyllys.ingersoll@sun.com }; 247*10346Swyllys.ingersoll@sun.com 248*10346Swyllys.ingersoll@sun.com static int tpmrng_ext_info(crypto_provider_handle_t, 249*10346Swyllys.ingersoll@sun.com crypto_provider_ext_info_t *, 250*10346Swyllys.ingersoll@sun.com crypto_req_handle_t); 251*10346Swyllys.ingersoll@sun.com 252*10346Swyllys.ingersoll@sun.com static crypto_provider_management_ops_t tpmrng_extinfo_op = { 253*10346Swyllys.ingersoll@sun.com tpmrng_ext_info, 254*10346Swyllys.ingersoll@sun.com NULL, 255*10346Swyllys.ingersoll@sun.com NULL, 256*10346Swyllys.ingersoll@sun.com NULL 257*10346Swyllys.ingersoll@sun.com }; 258*10346Swyllys.ingersoll@sun.com 259*10346Swyllys.ingersoll@sun.com static int tpmrng_register(tpm_state_t *); 260*10346Swyllys.ingersoll@sun.com static int tpmrng_unregister(tpm_state_t *); 261*10346Swyllys.ingersoll@sun.com 262*10346Swyllys.ingersoll@sun.com static crypto_ops_t tpmrng_crypto_ops = { 263*10346Swyllys.ingersoll@sun.com &tpmrng_control_ops, 264*10346Swyllys.ingersoll@sun.com NULL, 265*10346Swyllys.ingersoll@sun.com NULL, 266*10346Swyllys.ingersoll@sun.com NULL, 267*10346Swyllys.ingersoll@sun.com NULL, 268*10346Swyllys.ingersoll@sun.com NULL, 269*10346Swyllys.ingersoll@sun.com NULL, 270*10346Swyllys.ingersoll@sun.com NULL, 271*10346Swyllys.ingersoll@sun.com &tpmrng_random_number_ops, 272*10346Swyllys.ingersoll@sun.com NULL, 273*10346Swyllys.ingersoll@sun.com NULL, 274*10346Swyllys.ingersoll@sun.com NULL, 275*10346Swyllys.ingersoll@sun.com &tpmrng_extinfo_op, 276*10346Swyllys.ingersoll@sun.com NULL, 277*10346Swyllys.ingersoll@sun.com NULL 278*10346Swyllys.ingersoll@sun.com }; 279*10346Swyllys.ingersoll@sun.com 280*10346Swyllys.ingersoll@sun.com static crypto_provider_info_t tpmrng_prov_info = { 281*10346Swyllys.ingersoll@sun.com CRYPTO_SPI_VERSION_2, 282*10346Swyllys.ingersoll@sun.com "TPM Random Number Provider", 283*10346Swyllys.ingersoll@sun.com CRYPTO_HW_PROVIDER, 284*10346Swyllys.ingersoll@sun.com NULL, 285*10346Swyllys.ingersoll@sun.com NULL, 286*10346Swyllys.ingersoll@sun.com &tpmrng_crypto_ops, 287*10346Swyllys.ingersoll@sun.com 0, 288*10346Swyllys.ingersoll@sun.com NULL, 289*10346Swyllys.ingersoll@sun.com 0, 290*10346Swyllys.ingersoll@sun.com NULL 291*10346Swyllys.ingersoll@sun.com }; 292*10346Swyllys.ingersoll@sun.com #endif /* KCF_TPM_RNG_PROVIDER */ 293*10346Swyllys.ingersoll@sun.com 2949126SWyllys.Ingersoll@Sun.COM static void *statep = NULL; 2959126SWyllys.Ingersoll@Sun.COM 2969126SWyllys.Ingersoll@Sun.COM /* 297*10346Swyllys.ingersoll@sun.com * Inline code to get exclusive lock on the TPM device and to make sure 298*10346Swyllys.ingersoll@sun.com * the device is not suspended. This grabs the primary TPM mutex (pm_mutex) 299*10346Swyllys.ingersoll@sun.com * and then checks the suspend status. If suspended, it will wait until 300*10346Swyllys.ingersoll@sun.com * the device is "resumed" before releasing the pm_mutex and continuing. 301*10346Swyllys.ingersoll@sun.com */ 302*10346Swyllys.ingersoll@sun.com #define TPM_EXCLUSIVE_LOCK(tpm) { \ 303*10346Swyllys.ingersoll@sun.com mutex_enter(&tpm->pm_mutex); \ 304*10346Swyllys.ingersoll@sun.com while (tpm->suspended) \ 305*10346Swyllys.ingersoll@sun.com cv_wait(&tpm->suspend_cv, &tpm->pm_mutex); \ 306*10346Swyllys.ingersoll@sun.com mutex_exit(&tpm->pm_mutex); } 307*10346Swyllys.ingersoll@sun.com 308*10346Swyllys.ingersoll@sun.com /* 309*10346Swyllys.ingersoll@sun.com * TPM accessor functions 310*10346Swyllys.ingersoll@sun.com */ 311*10346Swyllys.ingersoll@sun.com #ifdef sun4v 312*10346Swyllys.ingersoll@sun.com 313*10346Swyllys.ingersoll@sun.com extern uint64_t 314*10346Swyllys.ingersoll@sun.com hcall_tpm_get(uint64_t, uint64_t, uint64_t, uint64_t *); 315*10346Swyllys.ingersoll@sun.com 316*10346Swyllys.ingersoll@sun.com extern uint64_t 317*10346Swyllys.ingersoll@sun.com hcall_tpm_put(uint64_t, uint64_t, uint64_t, uint64_t); 318*10346Swyllys.ingersoll@sun.com 319*10346Swyllys.ingersoll@sun.com static inline uint8_t 320*10346Swyllys.ingersoll@sun.com tpm_get8(tpm_state_t *tpm, unsigned long offset) 321*10346Swyllys.ingersoll@sun.com { 322*10346Swyllys.ingersoll@sun.com uint64_t value; 323*10346Swyllys.ingersoll@sun.com 324*10346Swyllys.ingersoll@sun.com ASSERT(tpm != NULL); 325*10346Swyllys.ingersoll@sun.com (void) hcall_tpm_get(tpm->locality, offset, sizeof (uint8_t), &value); 326*10346Swyllys.ingersoll@sun.com return ((uint8_t)value); 327*10346Swyllys.ingersoll@sun.com } 328*10346Swyllys.ingersoll@sun.com 329*10346Swyllys.ingersoll@sun.com static inline uint32_t 330*10346Swyllys.ingersoll@sun.com tpm_get32(tpm_state_t *tpm, unsigned long offset) 331*10346Swyllys.ingersoll@sun.com { 332*10346Swyllys.ingersoll@sun.com uint64_t value; 333*10346Swyllys.ingersoll@sun.com 334*10346Swyllys.ingersoll@sun.com ASSERT(tpm != NULL); 335*10346Swyllys.ingersoll@sun.com (void) hcall_tpm_get(tpm->locality, offset, sizeof (uint32_t), &value); 336*10346Swyllys.ingersoll@sun.com return ((uint32_t)value); 337*10346Swyllys.ingersoll@sun.com } 338*10346Swyllys.ingersoll@sun.com 339*10346Swyllys.ingersoll@sun.com static inline void 340*10346Swyllys.ingersoll@sun.com tpm_put8(tpm_state_t *tpm, unsigned long offset, uint8_t value) 341*10346Swyllys.ingersoll@sun.com { 342*10346Swyllys.ingersoll@sun.com ASSERT(tpm != NULL); 343*10346Swyllys.ingersoll@sun.com (void) hcall_tpm_put(tpm->locality, offset, sizeof (uint8_t), value); 344*10346Swyllys.ingersoll@sun.com } 345*10346Swyllys.ingersoll@sun.com 346*10346Swyllys.ingersoll@sun.com #else 347*10346Swyllys.ingersoll@sun.com 348*10346Swyllys.ingersoll@sun.com static inline uint8_t 349*10346Swyllys.ingersoll@sun.com tpm_get8(tpm_state_t *tpm, unsigned long offset) 350*10346Swyllys.ingersoll@sun.com { 351*10346Swyllys.ingersoll@sun.com ASSERT(tpm != NULL); 352*10346Swyllys.ingersoll@sun.com 353*10346Swyllys.ingersoll@sun.com return (ddi_get8(tpm->handle, 354*10346Swyllys.ingersoll@sun.com (uint8_t *)(TPM_LOCALITY_OFFSET(tpm->locality) | 355*10346Swyllys.ingersoll@sun.com (uintptr_t)tpm->addr + offset))); 356*10346Swyllys.ingersoll@sun.com } 357*10346Swyllys.ingersoll@sun.com 358*10346Swyllys.ingersoll@sun.com static inline uint32_t 359*10346Swyllys.ingersoll@sun.com tpm_get32(tpm_state_t *tpm, unsigned long offset) 360*10346Swyllys.ingersoll@sun.com { 361*10346Swyllys.ingersoll@sun.com ASSERT(tpm != NULL); 362*10346Swyllys.ingersoll@sun.com return (ddi_get32(tpm->handle, 363*10346Swyllys.ingersoll@sun.com (uint32_t *)(TPM_LOCALITY_OFFSET(tpm->locality) | 364*10346Swyllys.ingersoll@sun.com (uintptr_t)tpm->addr + offset))); 365*10346Swyllys.ingersoll@sun.com } 366*10346Swyllys.ingersoll@sun.com 367*10346Swyllys.ingersoll@sun.com static inline void 368*10346Swyllys.ingersoll@sun.com tpm_put8(tpm_state_t *tpm, unsigned long offset, uint8_t value) 369*10346Swyllys.ingersoll@sun.com { 370*10346Swyllys.ingersoll@sun.com ASSERT(tpm != NULL); 371*10346Swyllys.ingersoll@sun.com ddi_put8(tpm->handle, 372*10346Swyllys.ingersoll@sun.com (uint8_t *)(TPM_LOCALITY_OFFSET(tpm->locality) | 373*10346Swyllys.ingersoll@sun.com (uintptr_t)tpm->addr + offset), value); 374*10346Swyllys.ingersoll@sun.com } 375*10346Swyllys.ingersoll@sun.com 376*10346Swyllys.ingersoll@sun.com #endif /* sun4v */ 377*10346Swyllys.ingersoll@sun.com 378*10346Swyllys.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 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 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 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) { 4249126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: itpm_command failed", myname); 4259126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 4269126SWyllys.Ingersoll@Sun.COM } 4279126SWyllys.Ingersoll@Sun.COM 4289126SWyllys.Ingersoll@Sun.COM /* 4299126SWyllys.Ingersoll@Sun.COM * Get the length of the returned buffer 4309126SWyllys.Ingersoll@Sun.COM * Make sure that there are 4 timeout values returned 4319126SWyllys.Ingersoll@Sun.COM * length of the capability response is stored in data[10-13] 4329126SWyllys.Ingersoll@Sun.COM * Also the TPM is in network byte order 4339126SWyllys.Ingersoll@Sun.COM */ 4349126SWyllys.Ingersoll@Sun.COM len = load32(buf, TPM_CAP_RESPSIZE_OFFSET); 4359126SWyllys.Ingersoll@Sun.COM if (len != 4 * sizeof (uint32_t)) { 4369126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: capability response size should be %d" 4379126SWyllys.Ingersoll@Sun.COM "instead it's %d", 4389126SWyllys.Ingersoll@Sun.COM myname, (int)(4 * sizeof (uint32_t)), (int)len); 4399126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 4409126SWyllys.Ingersoll@Sun.COM } 4419126SWyllys.Ingersoll@Sun.COM 4429126SWyllys.Ingersoll@Sun.COM /* Get the four timeout's: a,b,c,d (they are 4 bytes long each) */ 4439126SWyllys.Ingersoll@Sun.COM timeout = load32(buf, TPM_CAP_TIMEOUT_A_OFFSET); 4449126SWyllys.Ingersoll@Sun.COM if (timeout == 0) { 4459126SWyllys.Ingersoll@Sun.COM timeout = DEFAULT_TIMEOUT_A; 4469126SWyllys.Ingersoll@Sun.COM } else if (timeout < TEN_MILLISECONDS) { 4479126SWyllys.Ingersoll@Sun.COM /* timeout is in millisecond range (should be microseconds) */ 4489126SWyllys.Ingersoll@Sun.COM timeout *= 1000; 4499126SWyllys.Ingersoll@Sun.COM } 4509126SWyllys.Ingersoll@Sun.COM tpm->timeout_a = drv_usectohz(timeout); 4519126SWyllys.Ingersoll@Sun.COM 4529126SWyllys.Ingersoll@Sun.COM timeout = load32(buf, TPM_CAP_TIMEOUT_B_OFFSET); 4539126SWyllys.Ingersoll@Sun.COM if (timeout == 0) { 4549126SWyllys.Ingersoll@Sun.COM timeout = DEFAULT_TIMEOUT_B; 4559126SWyllys.Ingersoll@Sun.COM } else if (timeout < TEN_MILLISECONDS) { 4569126SWyllys.Ingersoll@Sun.COM /* timeout is in millisecond range (should be microseconds) */ 4579126SWyllys.Ingersoll@Sun.COM timeout *= 1000; 4589126SWyllys.Ingersoll@Sun.COM } 4599126SWyllys.Ingersoll@Sun.COM tpm->timeout_b = drv_usectohz(timeout); 4609126SWyllys.Ingersoll@Sun.COM 4619126SWyllys.Ingersoll@Sun.COM timeout = load32(buf, TPM_CAP_TIMEOUT_C_OFFSET); 4629126SWyllys.Ingersoll@Sun.COM if (timeout == 0) { 4639126SWyllys.Ingersoll@Sun.COM timeout = DEFAULT_TIMEOUT_C; 4649126SWyllys.Ingersoll@Sun.COM } else if (timeout < TEN_MILLISECONDS) { 4659126SWyllys.Ingersoll@Sun.COM /* timeout is in millisecond range (should be microseconds) */ 4669126SWyllys.Ingersoll@Sun.COM timeout *= 1000; 4679126SWyllys.Ingersoll@Sun.COM } 4689126SWyllys.Ingersoll@Sun.COM tpm->timeout_c = drv_usectohz(timeout); 4699126SWyllys.Ingersoll@Sun.COM 4709126SWyllys.Ingersoll@Sun.COM timeout = load32(buf, TPM_CAP_TIMEOUT_D_OFFSET); 4719126SWyllys.Ingersoll@Sun.COM if (timeout == 0) { 4729126SWyllys.Ingersoll@Sun.COM timeout = DEFAULT_TIMEOUT_D; 4739126SWyllys.Ingersoll@Sun.COM } else if (timeout < TEN_MILLISECONDS) { 4749126SWyllys.Ingersoll@Sun.COM /* timeout is in millisecond range (should be microseconds) */ 4759126SWyllys.Ingersoll@Sun.COM timeout *= 1000; 4769126SWyllys.Ingersoll@Sun.COM } 4779126SWyllys.Ingersoll@Sun.COM tpm->timeout_d = drv_usectohz(timeout); 4789126SWyllys.Ingersoll@Sun.COM 4799126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 4809126SWyllys.Ingersoll@Sun.COM } 4819126SWyllys.Ingersoll@Sun.COM 4829126SWyllys.Ingersoll@Sun.COM /* 4839126SWyllys.Ingersoll@Sun.COM * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability 4849126SWyllys.Ingersoll@Sun.COM * with the subcommand TPM_CAP_PROP_TIS_DURATION 4859126SWyllys.Ingersoll@Sun.COM * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38) 4869126SWyllys.Ingersoll@Sun.COM */ 4879126SWyllys.Ingersoll@Sun.COM static int 4889126SWyllys.Ingersoll@Sun.COM tpm_get_duration(tpm_state_t *tpm) { 4899126SWyllys.Ingersoll@Sun.COM int ret; 4909126SWyllys.Ingersoll@Sun.COM uint32_t duration; 4919126SWyllys.Ingersoll@Sun.COM uint32_t len; 4929126SWyllys.Ingersoll@Sun.COM uint8_t buf[30] = { 4939126SWyllys.Ingersoll@Sun.COM 0, 193, /* TPM_TAG_RQU_COMMAND */ 4949126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 22, /* paramsize in bytes */ 4959126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 101, /* TPM_ORD_GetCapability */ 4969126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 5, /* TPM_CAP_Prop */ 4979126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 4, /* SUB_CAP size in bytes */ 4989126SWyllys.Ingersoll@Sun.COM 0, 0, 1, 32 /* TPM_CAP_PROP_TIS_DURATION(0x120) */ 4999126SWyllys.Ingersoll@Sun.COM }; 5009126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_get_duration"; 5019126SWyllys.Ingersoll@Sun.COM 5029126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 5039126SWyllys.Ingersoll@Sun.COM 5049126SWyllys.Ingersoll@Sun.COM ret = itpm_command(tpm, buf, sizeof (buf)); 5059126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 5069126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: itpm_command failed with ret code: 0x%x", 5079126SWyllys.Ingersoll@Sun.COM myname, ret); 5089126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 5099126SWyllys.Ingersoll@Sun.COM } 5109126SWyllys.Ingersoll@Sun.COM 5119126SWyllys.Ingersoll@Sun.COM /* 5129126SWyllys.Ingersoll@Sun.COM * Get the length of the returned buffer 5139126SWyllys.Ingersoll@Sun.COM * Make sure that there are 3 duration values (S,M,L: in that order) 5149126SWyllys.Ingersoll@Sun.COM * length of the capability response is stored in data[10-13] 5159126SWyllys.Ingersoll@Sun.COM * Also the TPM is in network byte order 5169126SWyllys.Ingersoll@Sun.COM */ 5179126SWyllys.Ingersoll@Sun.COM len = load32(buf, TPM_CAP_RESPSIZE_OFFSET); 5189126SWyllys.Ingersoll@Sun.COM if (len != 3 * sizeof (uint32_t)) { 5199126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: capability response should be %d, " 5209126SWyllys.Ingersoll@Sun.COM "instead, it's %d", 5219126SWyllys.Ingersoll@Sun.COM myname, (int)(3 * sizeof (uint32_t)), (int)len); 5229126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 5239126SWyllys.Ingersoll@Sun.COM } 5249126SWyllys.Ingersoll@Sun.COM 5259126SWyllys.Ingersoll@Sun.COM duration = load32(buf, TPM_CAP_DUR_SHORT_OFFSET); 5269126SWyllys.Ingersoll@Sun.COM if (duration == 0) { 5279126SWyllys.Ingersoll@Sun.COM duration = DEFAULT_SHORT_DURATION; 5289126SWyllys.Ingersoll@Sun.COM } else if (duration < TEN_MILLISECONDS) { 5299126SWyllys.Ingersoll@Sun.COM duration *= 1000; 5309126SWyllys.Ingersoll@Sun.COM } 5319126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_SHORT] = drv_usectohz(duration); 5329126SWyllys.Ingersoll@Sun.COM 5339126SWyllys.Ingersoll@Sun.COM duration = load32(buf, TPM_CAP_DUR_MEDIUM_OFFSET); 5349126SWyllys.Ingersoll@Sun.COM if (duration == 0) { 5359126SWyllys.Ingersoll@Sun.COM duration = DEFAULT_MEDIUM_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_MEDIUM] = drv_usectohz(duration); 5409126SWyllys.Ingersoll@Sun.COM 5419126SWyllys.Ingersoll@Sun.COM duration = load32(buf, TPM_CAP_DUR_LONG_OFFSET); 5429126SWyllys.Ingersoll@Sun.COM if (duration == 0) { 5439126SWyllys.Ingersoll@Sun.COM duration = DEFAULT_LONG_DURATION; 5449126SWyllys.Ingersoll@Sun.COM } else if (duration < FOUR_HUNDRED_MILLISECONDS) { 5459126SWyllys.Ingersoll@Sun.COM duration *= 1000; 5469126SWyllys.Ingersoll@Sun.COM } 5479126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_LONG] = drv_usectohz(duration); 5489126SWyllys.Ingersoll@Sun.COM 5499126SWyllys.Ingersoll@Sun.COM /* Just make the undefined duration be the same as the LONG */ 5509126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_UNDEFINED] = tpm->duration[TPM_LONG]; 5519126SWyllys.Ingersoll@Sun.COM 5529126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 5539126SWyllys.Ingersoll@Sun.COM } 5549126SWyllys.Ingersoll@Sun.COM 5559126SWyllys.Ingersoll@Sun.COM /* 5569126SWyllys.Ingersoll@Sun.COM * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability 5579126SWyllys.Ingersoll@Sun.COM * with the subcommand TPM_CAP_PROP_TIS_DURATION 5589126SWyllys.Ingersoll@Sun.COM * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38) 5599126SWyllys.Ingersoll@Sun.COM */ 5609126SWyllys.Ingersoll@Sun.COM static int 5619126SWyllys.Ingersoll@Sun.COM tpm_get_version(tpm_state_t *tpm) { 5629126SWyllys.Ingersoll@Sun.COM int ret; 5639126SWyllys.Ingersoll@Sun.COM uint32_t len; 5649126SWyllys.Ingersoll@Sun.COM char vendorId[5]; 5659126SWyllys.Ingersoll@Sun.COM /* If this buf is too small, the "vendor specific" data won't fit */ 5669126SWyllys.Ingersoll@Sun.COM uint8_t buf[64] = { 5679126SWyllys.Ingersoll@Sun.COM 0, 193, /* TPM_TAG_RQU_COMMAND */ 5689126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 18, /* paramsize in bytes */ 5699126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 101, /* TPM_ORD_GetCapability */ 5709126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 0x1A, /* TPM_CAP_VERSION_VAL */ 5719126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 0, /* SUB_CAP size in bytes */ 5729126SWyllys.Ingersoll@Sun.COM }; 5739126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_get_version"; 5749126SWyllys.Ingersoll@Sun.COM 5759126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 5769126SWyllys.Ingersoll@Sun.COM 5779126SWyllys.Ingersoll@Sun.COM ret = itpm_command(tpm, buf, sizeof (buf)); 5789126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 5799126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: itpm_command failed with ret code: 0x%x", 5809126SWyllys.Ingersoll@Sun.COM myname, ret); 5819126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 5829126SWyllys.Ingersoll@Sun.COM } 5839126SWyllys.Ingersoll@Sun.COM 5849126SWyllys.Ingersoll@Sun.COM /* 5859126SWyllys.Ingersoll@Sun.COM * Get the length of the returned buffer. 5869126SWyllys.Ingersoll@Sun.COM */ 5879126SWyllys.Ingersoll@Sun.COM len = load32(buf, TPM_CAP_RESPSIZE_OFFSET); 5889126SWyllys.Ingersoll@Sun.COM if (len < TPM_CAP_VERSION_INFO_SIZE) { 5899126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: capability response should be greater" 5909126SWyllys.Ingersoll@Sun.COM " than %d, instead, it's %d", 5919126SWyllys.Ingersoll@Sun.COM myname, 5929126SWyllys.Ingersoll@Sun.COM TPM_CAP_VERSION_INFO_SIZE, 5939126SWyllys.Ingersoll@Sun.COM len); 5949126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 5959126SWyllys.Ingersoll@Sun.COM } 5969126SWyllys.Ingersoll@Sun.COM 5979126SWyllys.Ingersoll@Sun.COM bcopy(buf + TPM_CAP_VERSION_INFO_OFFSET, &tpm->vers_info, 5989126SWyllys.Ingersoll@Sun.COM TPM_CAP_VERSION_INFO_SIZE); 5999126SWyllys.Ingersoll@Sun.COM 6009126SWyllys.Ingersoll@Sun.COM bcopy(tpm->vers_info.tpmVendorID, vendorId, 6019126SWyllys.Ingersoll@Sun.COM sizeof (tpm->vers_info.tpmVendorID)); 6029126SWyllys.Ingersoll@Sun.COM vendorId[4] = '\0'; 6039126SWyllys.Ingersoll@Sun.COM 6049126SWyllys.Ingersoll@Sun.COM cmn_err(CE_NOTE, "!TPM found: Ver %d.%d, Rev %d.%d, " 6059126SWyllys.Ingersoll@Sun.COM "SpecLevel %d, errataRev %d, VendorId '%s'", 6069126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.major, /* Version */ 6079126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.minor, 6089126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.revMajor, /* Revision */ 6099126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.revMinor, 6109126SWyllys.Ingersoll@Sun.COM (int)ntohs(tpm->vers_info.specLevel), 6119126SWyllys.Ingersoll@Sun.COM tpm->vers_info.errataRev, 6129126SWyllys.Ingersoll@Sun.COM vendorId); 6139126SWyllys.Ingersoll@Sun.COM 6149126SWyllys.Ingersoll@Sun.COM /* 6159126SWyllys.Ingersoll@Sun.COM * This driver only supports TPM Version 1.2 6169126SWyllys.Ingersoll@Sun.COM */ 6179126SWyllys.Ingersoll@Sun.COM if (tpm->vers_info.version.major != 1 && 6189126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.minor != 2) { 6199126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: Unsupported TPM version (%d.%d)", 6209126SWyllys.Ingersoll@Sun.COM myname, 6219126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.major, /* Version */ 6229126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.minor); 6239126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 6249126SWyllys.Ingersoll@Sun.COM } 6259126SWyllys.Ingersoll@Sun.COM 6269126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 6279126SWyllys.Ingersoll@Sun.COM } 6289126SWyllys.Ingersoll@Sun.COM 6299126SWyllys.Ingersoll@Sun.COM /* 6309126SWyllys.Ingersoll@Sun.COM * To prevent the TPM from complaining that certain functions are not tested 6319126SWyllys.Ingersoll@Sun.COM * we run this command when the driver attaches. 6329126SWyllys.Ingersoll@Sun.COM * For details see Section 4.2 of TPM Main Part 3 Command Specification 6339126SWyllys.Ingersoll@Sun.COM */ 6349126SWyllys.Ingersoll@Sun.COM static int 6359126SWyllys.Ingersoll@Sun.COM tpm_continue_selftest(tpm_state_t *tpm) { 6369126SWyllys.Ingersoll@Sun.COM int ret; 6379126SWyllys.Ingersoll@Sun.COM uint8_t buf[10] = { 6389126SWyllys.Ingersoll@Sun.COM 0, 193, /* TPM_TAG_RQU COMMAND */ 6399126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 10, /* paramsize in bytes */ 6409126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 83 /* TPM_ORD_ContinueSelfTest */ 6419126SWyllys.Ingersoll@Sun.COM }; 6429126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_continue_selftest"; 6439126SWyllys.Ingersoll@Sun.COM 6449126SWyllys.Ingersoll@Sun.COM /* Need a longer timeout */ 6459126SWyllys.Ingersoll@Sun.COM ret = itpm_command(tpm, buf, sizeof (buf)); 6469126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 6479126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: itpm_command failed", myname); 6489126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 6499126SWyllys.Ingersoll@Sun.COM } 6509126SWyllys.Ingersoll@Sun.COM 6519126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 6529126SWyllys.Ingersoll@Sun.COM } 6539126SWyllys.Ingersoll@Sun.COM /* 6549126SWyllys.Ingersoll@Sun.COM * Auxilary Functions 6559126SWyllys.Ingersoll@Sun.COM */ 6569126SWyllys.Ingersoll@Sun.COM 6579126SWyllys.Ingersoll@Sun.COM /* 6589126SWyllys.Ingersoll@Sun.COM * Find out how long we should wait for the TPM command to complete a command 6599126SWyllys.Ingersoll@Sun.COM */ 6609126SWyllys.Ingersoll@Sun.COM static clock_t 6619126SWyllys.Ingersoll@Sun.COM tpm_get_ordinal_duration(tpm_state_t *tpm, uint8_t ordinal) 6629126SWyllys.Ingersoll@Sun.COM { 6639126SWyllys.Ingersoll@Sun.COM uint8_t index; 6649126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_get_ordinal_duration"; 6659126SWyllys.Ingersoll@Sun.COM 6669126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 6679126SWyllys.Ingersoll@Sun.COM 6689126SWyllys.Ingersoll@Sun.COM /* Default and failure case for IFX */ 6699126SWyllys.Ingersoll@Sun.COM /* Is it a TSC_ORDINAL? */ 6709126SWyllys.Ingersoll@Sun.COM if (ordinal & TSC_ORDINAL_MASK) { 6719126SWyllys.Ingersoll@Sun.COM if (ordinal > TSC_ORDINAL_MAX) { 6729126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, 6739126SWyllys.Ingersoll@Sun.COM "%s: tsc ordinal: %d exceeds MAX: %d", 6749126SWyllys.Ingersoll@Sun.COM myname, ordinal, TSC_ORDINAL_MAX); 6759126SWyllys.Ingersoll@Sun.COM return (0); 6769126SWyllys.Ingersoll@Sun.COM } 6779126SWyllys.Ingersoll@Sun.COM index = tsc_ords_duration[ordinal]; 6789126SWyllys.Ingersoll@Sun.COM } else { 6799126SWyllys.Ingersoll@Sun.COM if (ordinal > TPM_ORDINAL_MAX) { 6809126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, 6819126SWyllys.Ingersoll@Sun.COM "%s: ordinal %d exceeds MAX: %d", 6829126SWyllys.Ingersoll@Sun.COM myname, ordinal, TPM_ORDINAL_MAX); 6839126SWyllys.Ingersoll@Sun.COM return (0); 6849126SWyllys.Ingersoll@Sun.COM } 6859126SWyllys.Ingersoll@Sun.COM index = tpm_ords_duration[ordinal]; 6869126SWyllys.Ingersoll@Sun.COM } 6879126SWyllys.Ingersoll@Sun.COM 6889126SWyllys.Ingersoll@Sun.COM if (index > TPM_DURATION_MAX_IDX) { 6899126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: FATAL:index '%d' is out of bound", 6909126SWyllys.Ingersoll@Sun.COM myname, index); 6919126SWyllys.Ingersoll@Sun.COM return (0); 6929126SWyllys.Ingersoll@Sun.COM } 6939126SWyllys.Ingersoll@Sun.COM return (tpm->duration[index]); 6949126SWyllys.Ingersoll@Sun.COM } 6959126SWyllys.Ingersoll@Sun.COM 6969126SWyllys.Ingersoll@Sun.COM /* 6979126SWyllys.Ingersoll@Sun.COM * Internal TPM Transmit Function: 6989126SWyllys.Ingersoll@Sun.COM * Calls implementation specific sendto and receive 6999126SWyllys.Ingersoll@Sun.COM * The code assumes that the buffer is in network byte order 7009126SWyllys.Ingersoll@Sun.COM */ 7019126SWyllys.Ingersoll@Sun.COM static int 7029126SWyllys.Ingersoll@Sun.COM itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) 7039126SWyllys.Ingersoll@Sun.COM { 7049126SWyllys.Ingersoll@Sun.COM int ret; 7059126SWyllys.Ingersoll@Sun.COM uint32_t count; 7069126SWyllys.Ingersoll@Sun.COM char *myname = "itpm_command"; 7079126SWyllys.Ingersoll@Sun.COM 7089126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && buf != NULL); 7099126SWyllys.Ingersoll@Sun.COM 7109126SWyllys.Ingersoll@Sun.COM /* The byte order is network byte order so convert it */ 7119126SWyllys.Ingersoll@Sun.COM count = load32(buf, TPM_PARAMSIZE_OFFSET); 7129126SWyllys.Ingersoll@Sun.COM 7139126SWyllys.Ingersoll@Sun.COM if (count == 0) { 7149126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: count=0, no data? %d", myname, 7159126SWyllys.Ingersoll@Sun.COM (int)bufsiz); 7169126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 7179126SWyllys.Ingersoll@Sun.COM } 7189126SWyllys.Ingersoll@Sun.COM if (count > bufsiz) { 7199126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: invalid count value:count:%d > bufsiz %d", 7209126SWyllys.Ingersoll@Sun.COM myname, (int)count, (int)bufsiz); 7219126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 7229126SWyllys.Ingersoll@Sun.COM } 7239126SWyllys.Ingersoll@Sun.COM 7249126SWyllys.Ingersoll@Sun.COM /* Send the command */ 7259126SWyllys.Ingersoll@Sun.COM ret = tis_send_data(tpm, buf, count); 7269126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 7279126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_send_data failed with error %x", 7289126SWyllys.Ingersoll@Sun.COM myname, ret); 7299126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 7309126SWyllys.Ingersoll@Sun.COM } 7319126SWyllys.Ingersoll@Sun.COM 7329126SWyllys.Ingersoll@Sun.COM /* 7339126SWyllys.Ingersoll@Sun.COM * Now receive the data from the tpm 7349126SWyllys.Ingersoll@Sun.COM * Should at least receive "the common" 10 bytes (TPM_HEADER_SIZE) 7359126SWyllys.Ingersoll@Sun.COM */ 7369126SWyllys.Ingersoll@Sun.COM ret = tis_recv_data(tpm, buf, bufsiz); 7379126SWyllys.Ingersoll@Sun.COM if (ret < TPM_HEADER_SIZE) { 7389126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_recv_data failed", myname); 7399126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 7409126SWyllys.Ingersoll@Sun.COM } 7419126SWyllys.Ingersoll@Sun.COM 7429126SWyllys.Ingersoll@Sun.COM /* Check the return code */ 7439126SWyllys.Ingersoll@Sun.COM ret = load32(buf, TPM_RETURN_OFFSET); 7449126SWyllys.Ingersoll@Sun.COM if (ret != TPM_SUCCESS) { 745*10346Swyllys.ingersoll@sun.com if (ret == TPM_E_DEACTIVATED) 746*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, "%s: TPM is deactivated", myname); 747*10346Swyllys.ingersoll@sun.com else if (ret == TPM_E_DISABLED) 748*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, "%s: TPM is disabled", myname); 749*10346Swyllys.ingersoll@sun.com else 750*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, "%s: TPM error code 0x%0x", 751*10346Swyllys.ingersoll@sun.com myname, ret); 7529126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 7539126SWyllys.Ingersoll@Sun.COM } 7549126SWyllys.Ingersoll@Sun.COM 7559126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 7569126SWyllys.Ingersoll@Sun.COM } 7579126SWyllys.Ingersoll@Sun.COM 7589126SWyllys.Ingersoll@Sun.COM /* 7599126SWyllys.Ingersoll@Sun.COM * Whenever the driver wants to write to the DATA_IO register, it must need 7609126SWyllys.Ingersoll@Sun.COM * to figure out the burstcount. This is the amount of bytes it can write 7619126SWyllys.Ingersoll@Sun.COM * before having to wait for long LPC bus cycle 7629126SWyllys.Ingersoll@Sun.COM * 7639126SWyllys.Ingersoll@Sun.COM * Returns: 0 if error, burst count if sucess 7649126SWyllys.Ingersoll@Sun.COM */ 7659126SWyllys.Ingersoll@Sun.COM static uint16_t 7669126SWyllys.Ingersoll@Sun.COM tpm_get_burstcount(tpm_state_t *tpm) { 7679126SWyllys.Ingersoll@Sun.COM clock_t stop; 7689126SWyllys.Ingersoll@Sun.COM uint16_t burstcnt; 7699126SWyllys.Ingersoll@Sun.COM 7709126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 7719126SWyllys.Ingersoll@Sun.COM 7729126SWyllys.Ingersoll@Sun.COM /* 7739126SWyllys.Ingersoll@Sun.COM * Spec says timeout should be TIMEOUT_D 7749126SWyllys.Ingersoll@Sun.COM * burst count is TPM_STS bits 8..23 7759126SWyllys.Ingersoll@Sun.COM */ 7769126SWyllys.Ingersoll@Sun.COM stop = ddi_get_lbolt() + tpm->timeout_d; 7779126SWyllys.Ingersoll@Sun.COM do { 7789126SWyllys.Ingersoll@Sun.COM /* 7799126SWyllys.Ingersoll@Sun.COM * burstcnt is stored as a little endian value 7809126SWyllys.Ingersoll@Sun.COM * 'ntohs' doesn't work since the value is not word-aligned 7819126SWyllys.Ingersoll@Sun.COM */ 782*10346Swyllys.ingersoll@sun.com burstcnt = tpm_get8(tpm, TPM_STS + 1); 783*10346Swyllys.ingersoll@sun.com burstcnt += tpm_get8(tpm, TPM_STS + 2) << 8; 7849126SWyllys.Ingersoll@Sun.COM 7859126SWyllys.Ingersoll@Sun.COM if (burstcnt) 7869126SWyllys.Ingersoll@Sun.COM return (burstcnt); 7879126SWyllys.Ingersoll@Sun.COM 7889126SWyllys.Ingersoll@Sun.COM delay(tpm->timeout_poll); 7899126SWyllys.Ingersoll@Sun.COM } while (ddi_get_lbolt() < stop); 7909126SWyllys.Ingersoll@Sun.COM 7919126SWyllys.Ingersoll@Sun.COM return (0); 7929126SWyllys.Ingersoll@Sun.COM } 7939126SWyllys.Ingersoll@Sun.COM 7949126SWyllys.Ingersoll@Sun.COM /* 7959126SWyllys.Ingersoll@Sun.COM * Writing 1 to TPM_STS_CMD_READY bit in TPM_STS will do the following: 7969126SWyllys.Ingersoll@Sun.COM * 1. The TPM will clears IO buffers if any 7979126SWyllys.Ingersoll@Sun.COM * 2. The TPM will enters either Idle or Ready state within TIMEOUT_B 7989126SWyllys.Ingersoll@Sun.COM * (checked in the calling function) 7999126SWyllys.Ingersoll@Sun.COM */ 8009126SWyllys.Ingersoll@Sun.COM static void 8019126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm_state_t *tpm) { 802*10346Swyllys.ingersoll@sun.com tpm_put8(tpm, TPM_STS, TPM_STS_CMD_READY); 8039126SWyllys.Ingersoll@Sun.COM } 8049126SWyllys.Ingersoll@Sun.COM 8059126SWyllys.Ingersoll@Sun.COM static int 8069126SWyllys.Ingersoll@Sun.COM receive_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) { 8079126SWyllys.Ingersoll@Sun.COM int size = 0; 8089126SWyllys.Ingersoll@Sun.COM int retried = 0; 8099126SWyllys.Ingersoll@Sun.COM uint8_t stsbits; 8109126SWyllys.Ingersoll@Sun.COM 8119126SWyllys.Ingersoll@Sun.COM /* A number of consecutive bytes that can be written to TPM */ 8129126SWyllys.Ingersoll@Sun.COM uint16_t burstcnt; 8139126SWyllys.Ingersoll@Sun.COM 8149126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && buf != NULL); 8159126SWyllys.Ingersoll@Sun.COM retry: 8169126SWyllys.Ingersoll@Sun.COM while (size < bufsiz && 8179126SWyllys.Ingersoll@Sun.COM (tpm_wait_for_stat(tpm, 8189126SWyllys.Ingersoll@Sun.COM (TPM_STS_DATA_AVAIL|TPM_STS_VALID), 819*10346Swyllys.ingersoll@sun.com tpm->timeout_c) == DDI_SUCCESS)) { 8209126SWyllys.Ingersoll@Sun.COM /* 8219126SWyllys.Ingersoll@Sun.COM * Burstcount should be available within TIMEOUT_D 8229126SWyllys.Ingersoll@Sun.COM * after STS is set to valid 8239126SWyllys.Ingersoll@Sun.COM * burstcount is dynamic, so have to get it each time 8249126SWyllys.Ingersoll@Sun.COM */ 8259126SWyllys.Ingersoll@Sun.COM burstcnt = tpm_get_burstcount(tpm); 8269126SWyllys.Ingersoll@Sun.COM for (; burstcnt > 0 && size < bufsiz; burstcnt--) { 827*10346Swyllys.ingersoll@sun.com buf[size++] = tpm_get8(tpm, TPM_DATA_FIFO); 8289126SWyllys.Ingersoll@Sun.COM } 8299126SWyllys.Ingersoll@Sun.COM } 8309126SWyllys.Ingersoll@Sun.COM stsbits = tis_get_status(tpm); 8319126SWyllys.Ingersoll@Sun.COM /* check to see if we need to retry (just once) */ 8329126SWyllys.Ingersoll@Sun.COM if (size < bufsiz && !(stsbits & TPM_STS_DATA_AVAIL) && retried == 0) { 8339126SWyllys.Ingersoll@Sun.COM /* issue responseRetry (TIS 1.2 pg 54) */ 834*10346Swyllys.ingersoll@sun.com tpm_put8(tpm, TPM_STS, TPM_STS_RESPONSE_RETRY); 8359126SWyllys.Ingersoll@Sun.COM /* update the retry counter so we only retry once */ 8369126SWyllys.Ingersoll@Sun.COM retried++; 8379126SWyllys.Ingersoll@Sun.COM /* reset the size to 0 and reread the entire response */ 8389126SWyllys.Ingersoll@Sun.COM size = 0; 8399126SWyllys.Ingersoll@Sun.COM goto retry; 8409126SWyllys.Ingersoll@Sun.COM } 8419126SWyllys.Ingersoll@Sun.COM return (size); 8429126SWyllys.Ingersoll@Sun.COM } 8439126SWyllys.Ingersoll@Sun.COM 8449126SWyllys.Ingersoll@Sun.COM /* Receive the data from the TPM */ 8459126SWyllys.Ingersoll@Sun.COM static int 8469126SWyllys.Ingersoll@Sun.COM tis_recv_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) { 8479126SWyllys.Ingersoll@Sun.COM int ret; 8489126SWyllys.Ingersoll@Sun.COM int size = 0; 8499126SWyllys.Ingersoll@Sun.COM uint32_t expected, status; 8509126SWyllys.Ingersoll@Sun.COM uint32_t cmdresult; 8519126SWyllys.Ingersoll@Sun.COM char *myname = "tis_recv_data"; 8529126SWyllys.Ingersoll@Sun.COM 8539126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && buf != NULL); 8549126SWyllys.Ingersoll@Sun.COM 8559126SWyllys.Ingersoll@Sun.COM if (bufsiz < TPM_HEADER_SIZE) { 8569126SWyllys.Ingersoll@Sun.COM /* There should be at least tag,paramsize,return code */ 8579126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: received data should contain at least " 8589126SWyllys.Ingersoll@Sun.COM "the header which is %d bytes long", 8599126SWyllys.Ingersoll@Sun.COM myname, TPM_HEADER_SIZE); 8609126SWyllys.Ingersoll@Sun.COM goto OUT; 8619126SWyllys.Ingersoll@Sun.COM } 8629126SWyllys.Ingersoll@Sun.COM 8639126SWyllys.Ingersoll@Sun.COM /* Read tag(2 bytes), paramsize(4), and result(4) */ 8649126SWyllys.Ingersoll@Sun.COM size = receive_data(tpm, buf, TPM_HEADER_SIZE); 8659126SWyllys.Ingersoll@Sun.COM if (size < TPM_HEADER_SIZE) { 8669126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: getting the TPM_HEADER failed: size=%d", 8679126SWyllys.Ingersoll@Sun.COM myname, size); 8689126SWyllys.Ingersoll@Sun.COM goto OUT; 8699126SWyllys.Ingersoll@Sun.COM } 8709126SWyllys.Ingersoll@Sun.COM 8719126SWyllys.Ingersoll@Sun.COM cmdresult = load32(buf, TPM_RETURN_OFFSET); 8729126SWyllys.Ingersoll@Sun.COM 8739126SWyllys.Ingersoll@Sun.COM /* Get 'paramsize'(4 bytes)--it includes tag and paramsize */ 8749126SWyllys.Ingersoll@Sun.COM expected = load32(buf, TPM_PARAMSIZE_OFFSET); 8759126SWyllys.Ingersoll@Sun.COM if (expected > bufsiz) { 8769126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: paramSize is bigger " 8779126SWyllys.Ingersoll@Sun.COM "than the requested size: paramSize=%d bufsiz=%d result=%d", 8789126SWyllys.Ingersoll@Sun.COM myname, (int)expected, (int)bufsiz, cmdresult); 8799126SWyllys.Ingersoll@Sun.COM goto OUT; 8809126SWyllys.Ingersoll@Sun.COM } 8819126SWyllys.Ingersoll@Sun.COM 8829126SWyllys.Ingersoll@Sun.COM /* Read in the rest of the data from the TPM */ 8839126SWyllys.Ingersoll@Sun.COM size += receive_data(tpm, (uint8_t *)&buf[TPM_HEADER_SIZE], 8849126SWyllys.Ingersoll@Sun.COM expected - TPM_HEADER_SIZE); 8859126SWyllys.Ingersoll@Sun.COM if (size < expected) { 8869126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: received data length=%d " 8879126SWyllys.Ingersoll@Sun.COM "is less than expected = %d", myname, size, expected); 8889126SWyllys.Ingersoll@Sun.COM goto OUT; 8899126SWyllys.Ingersoll@Sun.COM } 8909126SWyllys.Ingersoll@Sun.COM 8919126SWyllys.Ingersoll@Sun.COM /* The TPM MUST set the state to stsValid within TIMEOUT_C */ 892*10346Swyllys.ingersoll@sun.com ret = tpm_wait_for_stat(tpm, TPM_STS_VALID, tpm->timeout_c); 8939126SWyllys.Ingersoll@Sun.COM 8949126SWyllys.Ingersoll@Sun.COM status = tis_get_status(tpm); 8959126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 8969126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: TPM didn't set stsValid after its I/O: " 8979126SWyllys.Ingersoll@Sun.COM "status = 0x%08X", myname, status); 8989126SWyllys.Ingersoll@Sun.COM goto OUT; 8999126SWyllys.Ingersoll@Sun.COM } 9009126SWyllys.Ingersoll@Sun.COM 9019126SWyllys.Ingersoll@Sun.COM /* There is still more data? */ 9029126SWyllys.Ingersoll@Sun.COM if (status & TPM_STS_DATA_AVAIL) { 9039126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: Status TPM_STS_DATA_AVAIL set:0x%08X", 9049126SWyllys.Ingersoll@Sun.COM myname, status); 9059126SWyllys.Ingersoll@Sun.COM goto OUT; 9069126SWyllys.Ingersoll@Sun.COM } 9079126SWyllys.Ingersoll@Sun.COM 9089126SWyllys.Ingersoll@Sun.COM /* 9099126SWyllys.Ingersoll@Sun.COM * Release the control of the TPM after we are done with it 9109126SWyllys.Ingersoll@Sun.COM * it...so others can also get a chance to send data 9119126SWyllys.Ingersoll@Sun.COM */ 9129126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm, tpm->locality, 0); 9139126SWyllys.Ingersoll@Sun.COM 9149126SWyllys.Ingersoll@Sun.COM OUT: 9159126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm); 9169126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm, tpm->locality, 0); 9179126SWyllys.Ingersoll@Sun.COM return (size); 9189126SWyllys.Ingersoll@Sun.COM } 9199126SWyllys.Ingersoll@Sun.COM 9209126SWyllys.Ingersoll@Sun.COM /* 9219126SWyllys.Ingersoll@Sun.COM * Send the data (TPM commands) to the Data IO register 9229126SWyllys.Ingersoll@Sun.COM */ 9239126SWyllys.Ingersoll@Sun.COM static int 9249126SWyllys.Ingersoll@Sun.COM tis_send_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) { 9259126SWyllys.Ingersoll@Sun.COM int ret; 9269126SWyllys.Ingersoll@Sun.COM uint8_t status; 9279126SWyllys.Ingersoll@Sun.COM uint16_t burstcnt; 9289126SWyllys.Ingersoll@Sun.COM uint32_t ordinal; 9299126SWyllys.Ingersoll@Sun.COM size_t count = 0; 9309126SWyllys.Ingersoll@Sun.COM char *myname = "tis_send_data"; 9319126SWyllys.Ingersoll@Sun.COM 9329126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && buf != NULL); 9339126SWyllys.Ingersoll@Sun.COM 9349126SWyllys.Ingersoll@Sun.COM if (bufsiz == 0) { 9359126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: passed in argument bufsize is zero", 9369126SWyllys.Ingersoll@Sun.COM myname); 9379126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 9389126SWyllys.Ingersoll@Sun.COM } 9399126SWyllys.Ingersoll@Sun.COM 9409126SWyllys.Ingersoll@Sun.COM /* Put the TPM in ready state */ 9419126SWyllys.Ingersoll@Sun.COM status = tis_get_status(tpm); 9429126SWyllys.Ingersoll@Sun.COM 9439126SWyllys.Ingersoll@Sun.COM if (!(status & TPM_STS_CMD_READY)) { 9449126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm); 945*10346Swyllys.ingersoll@sun.com ret = tpm_wait_for_stat(tpm, TPM_STS_CMD_READY, tpm->timeout_b); 9469126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 9479126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: could not put the TPM " 9489126SWyllys.Ingersoll@Sun.COM "in the command ready state:" 9499126SWyllys.Ingersoll@Sun.COM "tpm_wait_for_stat returned error", 9509126SWyllys.Ingersoll@Sun.COM myname); 9519126SWyllys.Ingersoll@Sun.COM goto FAIL; 9529126SWyllys.Ingersoll@Sun.COM } 9539126SWyllys.Ingersoll@Sun.COM } 9549126SWyllys.Ingersoll@Sun.COM 9559126SWyllys.Ingersoll@Sun.COM /* 9569126SWyllys.Ingersoll@Sun.COM * Now we are ready to send command 9579126SWyllys.Ingersoll@Sun.COM * TPM's burstcount dictates how many bytes we can write at a time 9589126SWyllys.Ingersoll@Sun.COM * Burstcount is dynamic if INTF_CAPABILITY for static burstcount is 9599126SWyllys.Ingersoll@Sun.COM * not set. 9609126SWyllys.Ingersoll@Sun.COM */ 9619126SWyllys.Ingersoll@Sun.COM while (count < bufsiz - 1) { 9629126SWyllys.Ingersoll@Sun.COM burstcnt = tpm_get_burstcount(tpm); 9639126SWyllys.Ingersoll@Sun.COM if (burstcnt == 0) { 9649126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_get_burstcnt returned error", 9659126SWyllys.Ingersoll@Sun.COM myname); 9669126SWyllys.Ingersoll@Sun.COM ret = DDI_FAILURE; 9679126SWyllys.Ingersoll@Sun.COM goto FAIL; 9689126SWyllys.Ingersoll@Sun.COM } 9699126SWyllys.Ingersoll@Sun.COM 9709126SWyllys.Ingersoll@Sun.COM for (; burstcnt > 0 && count < bufsiz - 1; burstcnt--) { 971*10346Swyllys.ingersoll@sun.com tpm_put8(tpm, TPM_DATA_FIFO, buf[count]); 9729126SWyllys.Ingersoll@Sun.COM count++; 9739126SWyllys.Ingersoll@Sun.COM } 9749126SWyllys.Ingersoll@Sun.COM /* Wait for TPM to indicate that it is ready for more data */ 9759126SWyllys.Ingersoll@Sun.COM ret = tpm_wait_for_stat(tpm, 976*10346Swyllys.ingersoll@sun.com (TPM_STS_VALID | TPM_STS_DATA_EXPECT), tpm->timeout_c); 9779126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 9789126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: TPM didn't enter stsvalid " 9799126SWyllys.Ingersoll@Sun.COM "state after sending the data:", myname); 9809126SWyllys.Ingersoll@Sun.COM goto FAIL; 9819126SWyllys.Ingersoll@Sun.COM } 9829126SWyllys.Ingersoll@Sun.COM } 9839126SWyllys.Ingersoll@Sun.COM /* We can't exit the loop above unless we wrote bufsiz-1 bytes */ 9849126SWyllys.Ingersoll@Sun.COM 9859126SWyllys.Ingersoll@Sun.COM /* Write last byte */ 986*10346Swyllys.ingersoll@sun.com tpm_put8(tpm, TPM_DATA_FIFO, buf[count]); 9879126SWyllys.Ingersoll@Sun.COM count++; 9889126SWyllys.Ingersoll@Sun.COM 9899126SWyllys.Ingersoll@Sun.COM /* Wait for the TPM to enter Valid State */ 990*10346Swyllys.ingersoll@sun.com ret = tpm_wait_for_stat(tpm, TPM_STS_VALID, tpm->timeout_c); 9919126SWyllys.Ingersoll@Sun.COM if (ret == DDI_FAILURE) { 9929126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm didn't enter Valid state", myname); 9939126SWyllys.Ingersoll@Sun.COM goto FAIL; 9949126SWyllys.Ingersoll@Sun.COM } 9959126SWyllys.Ingersoll@Sun.COM 9969126SWyllys.Ingersoll@Sun.COM status = tis_get_status(tpm); 9979126SWyllys.Ingersoll@Sun.COM /* The TPM should NOT be expecing more data at this point */ 9989126SWyllys.Ingersoll@Sun.COM if ((status & TPM_STS_DATA_EXPECT) != 0) { 9999126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: DATA_EXPECT is set (shouldn't be) after " 10009126SWyllys.Ingersoll@Sun.COM "writing the last byte: status=0x%08X", myname, status); 10019126SWyllys.Ingersoll@Sun.COM ret = DDI_FAILURE; 10029126SWyllys.Ingersoll@Sun.COM goto FAIL; 10039126SWyllys.Ingersoll@Sun.COM } 10049126SWyllys.Ingersoll@Sun.COM 10059126SWyllys.Ingersoll@Sun.COM /* 10069126SWyllys.Ingersoll@Sun.COM * Final step: Writing TPM_STS_GO to TPM_STS 10079126SWyllys.Ingersoll@Sun.COM * register will actually send the command. 10089126SWyllys.Ingersoll@Sun.COM */ 1009*10346Swyllys.ingersoll@sun.com tpm_put8(tpm, TPM_STS, TPM_STS_GO); 10109126SWyllys.Ingersoll@Sun.COM 10119126SWyllys.Ingersoll@Sun.COM /* Ordinal/Command_code is located in buf[6..9] */ 10129126SWyllys.Ingersoll@Sun.COM ordinal = load32(buf, TPM_COMMAND_CODE_OFFSET); 10139126SWyllys.Ingersoll@Sun.COM 10149126SWyllys.Ingersoll@Sun.COM ret = tpm_wait_for_stat(tpm, TPM_STS_DATA_AVAIL | TPM_STS_VALID, 1015*10346Swyllys.ingersoll@sun.com tpm_get_ordinal_duration(tpm, ordinal)); 10169126SWyllys.Ingersoll@Sun.COM if (ret == DDI_FAILURE) { 10179126SWyllys.Ingersoll@Sun.COM status = tis_get_status(tpm); 10189126SWyllys.Ingersoll@Sun.COM if (!(status & TPM_STS_DATA_AVAIL) || 10199126SWyllys.Ingersoll@Sun.COM !(status & TPM_STS_VALID)) { 10209126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: TPM not ready or valid " 10219126SWyllys.Ingersoll@Sun.COM "(ordinal = %d timeout = %ld)", 10229126SWyllys.Ingersoll@Sun.COM myname, ordinal, 10239126SWyllys.Ingersoll@Sun.COM tpm_get_ordinal_duration(tpm, ordinal)); 10249126SWyllys.Ingersoll@Sun.COM } else { 10259126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_wait_for_stat " 10269126SWyllys.Ingersoll@Sun.COM "(DATA_AVAIL | VALID) failed: STS = 0x%0X", 10279126SWyllys.Ingersoll@Sun.COM myname, status); 10289126SWyllys.Ingersoll@Sun.COM } 10299126SWyllys.Ingersoll@Sun.COM goto FAIL; 10309126SWyllys.Ingersoll@Sun.COM } 10319126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 10329126SWyllys.Ingersoll@Sun.COM 10339126SWyllys.Ingersoll@Sun.COM FAIL: 10349126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm); 10359126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm, tpm->locality, 0); 10369126SWyllys.Ingersoll@Sun.COM return (ret); 10379126SWyllys.Ingersoll@Sun.COM } 10389126SWyllys.Ingersoll@Sun.COM 10399126SWyllys.Ingersoll@Sun.COM /* 10409126SWyllys.Ingersoll@Sun.COM * Clear XrequestUse and Xactivelocality, where X is the current locality 10419126SWyllys.Ingersoll@Sun.COM */ 10429126SWyllys.Ingersoll@Sun.COM static void 10439126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm_state_t *tpm, char locality, int force) { 10449126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && locality >= 0 && locality < 5); 10459126SWyllys.Ingersoll@Sun.COM 10469126SWyllys.Ingersoll@Sun.COM if (force || 1047*10346Swyllys.ingersoll@sun.com (tpm_get8(tpm, TPM_ACCESS) & 1048*10346Swyllys.ingersoll@sun.com (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == 1049*10346Swyllys.ingersoll@sun.com (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) { 10509126SWyllys.Ingersoll@Sun.COM /* 10519126SWyllys.Ingersoll@Sun.COM * Writing 1 to active locality bit in TPM_ACCESS 10529126SWyllys.Ingersoll@Sun.COM * register reliquishes the control of the locality 10539126SWyllys.Ingersoll@Sun.COM */ 1054*10346Swyllys.ingersoll@sun.com tpm_put8(tpm, TPM_ACCESS, TPM_ACCESS_ACTIVE_LOCALITY); 10559126SWyllys.Ingersoll@Sun.COM } 10569126SWyllys.Ingersoll@Sun.COM } 10579126SWyllys.Ingersoll@Sun.COM 10589126SWyllys.Ingersoll@Sun.COM /* 10599126SWyllys.Ingersoll@Sun.COM * Checks whether the given locality is active 10609126SWyllys.Ingersoll@Sun.COM * Use TPM_ACCESS register and the masks TPM_ACCESS_VALID,TPM_ACTIVE_LOCALITY 10619126SWyllys.Ingersoll@Sun.COM */ 10629126SWyllys.Ingersoll@Sun.COM static int 10639126SWyllys.Ingersoll@Sun.COM tis_check_active_locality(tpm_state_t *tpm, char locality) { 10649126SWyllys.Ingersoll@Sun.COM uint8_t access_bits; 1065*10346Swyllys.ingersoll@sun.com uint8_t old_locality; 10669126SWyllys.Ingersoll@Sun.COM 10679126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && locality >= 0 && locality < 5); 10689126SWyllys.Ingersoll@Sun.COM 1069*10346Swyllys.ingersoll@sun.com old_locality = tpm->locality; 1070*10346Swyllys.ingersoll@sun.com tpm->locality = locality; 1071*10346Swyllys.ingersoll@sun.com 1072*10346Swyllys.ingersoll@sun.com /* Just check to see if the requested locality works */ 1073*10346Swyllys.ingersoll@sun.com access_bits = tpm_get8(tpm, TPM_ACCESS); 10749126SWyllys.Ingersoll@Sun.COM access_bits &= (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID); 10759126SWyllys.Ingersoll@Sun.COM 1076*10346Swyllys.ingersoll@sun.com /* this was just a check, not a request to switch */ 1077*10346Swyllys.ingersoll@sun.com tpm->locality = old_locality; 1078*10346Swyllys.ingersoll@sun.com 1079*10346Swyllys.ingersoll@sun.com if (access_bits == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) { 10809126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 1081*10346Swyllys.ingersoll@sun.com } else { 10829126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1083*10346Swyllys.ingersoll@sun.com } 10849126SWyllys.Ingersoll@Sun.COM } 10859126SWyllys.Ingersoll@Sun.COM 10869126SWyllys.Ingersoll@Sun.COM /* Request the TPM to be in the given locality */ 10879126SWyllys.Ingersoll@Sun.COM static int 10889126SWyllys.Ingersoll@Sun.COM tis_request_locality(tpm_state_t *tpm, char locality) { 10899126SWyllys.Ingersoll@Sun.COM clock_t timeout; 10909126SWyllys.Ingersoll@Sun.COM int ret; 10919126SWyllys.Ingersoll@Sun.COM char *myname = "tis_request_locality"; 10929126SWyllys.Ingersoll@Sun.COM 10939126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && locality >= 0 && locality < 5); 10949126SWyllys.Ingersoll@Sun.COM 10959126SWyllys.Ingersoll@Sun.COM ret = tis_check_active_locality(tpm, locality); 10969126SWyllys.Ingersoll@Sun.COM 10979126SWyllys.Ingersoll@Sun.COM if (ret == DDI_SUCCESS) { 10989126SWyllys.Ingersoll@Sun.COM /* Locality is already active */ 10999126SWyllys.Ingersoll@Sun.COM tpm->locality = locality; 11009126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 11019126SWyllys.Ingersoll@Sun.COM } 11029126SWyllys.Ingersoll@Sun.COM 1103*10346Swyllys.ingersoll@sun.com tpm_put8(tpm, TPM_ACCESS, TPM_ACCESS_REQUEST_USE); 11049126SWyllys.Ingersoll@Sun.COM timeout = ddi_get_lbolt() + tpm->timeout_a; 11059126SWyllys.Ingersoll@Sun.COM 11069126SWyllys.Ingersoll@Sun.COM /* Using polling */ 11079126SWyllys.Ingersoll@Sun.COM while (tis_check_active_locality(tpm, locality) 11089126SWyllys.Ingersoll@Sun.COM != DDI_SUCCESS) { 11099126SWyllys.Ingersoll@Sun.COM if (ddi_get_lbolt() >= timeout) { 11109126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s (interrupt-disabled) " 1111*10346Swyllys.ingersoll@sun.com "tis_request_locality timed out (timeout_a = %ld)", 1112*10346Swyllys.ingersoll@sun.com myname, tpm->timeout_a); 11139126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 11149126SWyllys.Ingersoll@Sun.COM } 11159126SWyllys.Ingersoll@Sun.COM delay(tpm->timeout_poll); 11169126SWyllys.Ingersoll@Sun.COM } 11179126SWyllys.Ingersoll@Sun.COM 11189126SWyllys.Ingersoll@Sun.COM tpm->locality = locality; 11199126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 11209126SWyllys.Ingersoll@Sun.COM } 11219126SWyllys.Ingersoll@Sun.COM 11229126SWyllys.Ingersoll@Sun.COM /* Read the status register */ 11239126SWyllys.Ingersoll@Sun.COM static uint8_t 11249126SWyllys.Ingersoll@Sun.COM tis_get_status(tpm_state_t *tpm) { 1125*10346Swyllys.ingersoll@sun.com return (tpm_get8(tpm, TPM_STS)); 11269126SWyllys.Ingersoll@Sun.COM } 11279126SWyllys.Ingersoll@Sun.COM 11289126SWyllys.Ingersoll@Sun.COM static int 1129*10346Swyllys.ingersoll@sun.com tpm_wait_for_stat(tpm_state_t *tpm, uint8_t mask, clock_t timeout) { 11309126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_wait_for_stat"; 1131*10346Swyllys.ingersoll@sun.com clock_t absolute_timeout = ddi_get_lbolt() + timeout; 11329126SWyllys.Ingersoll@Sun.COM 11339126SWyllys.Ingersoll@Sun.COM /* Using polling */ 11349126SWyllys.Ingersoll@Sun.COM while ((tis_get_status(tpm) & mask) != mask) { 11359126SWyllys.Ingersoll@Sun.COM if (ddi_get_lbolt() >= absolute_timeout) { 11369126SWyllys.Ingersoll@Sun.COM /* Timeout reached */ 11379126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: using " 1138*10346Swyllys.ingersoll@sun.com "polling - reached timeout (%ld usecs)", 1139*10346Swyllys.ingersoll@sun.com myname, drv_hztousec(timeout)); 11409126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 11419126SWyllys.Ingersoll@Sun.COM } 11429126SWyllys.Ingersoll@Sun.COM delay(tpm->timeout_poll); 11439126SWyllys.Ingersoll@Sun.COM } 11449126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 11459126SWyllys.Ingersoll@Sun.COM } 11469126SWyllys.Ingersoll@Sun.COM 11479126SWyllys.Ingersoll@Sun.COM /* 11489126SWyllys.Ingersoll@Sun.COM * Initialize TPM device 11499126SWyllys.Ingersoll@Sun.COM * 1. Find out supported interrupt capabilities 11509126SWyllys.Ingersoll@Sun.COM * 2. Set up interrupt handler if supported (some BIOSes don't support 11519126SWyllys.Ingersoll@Sun.COM * interrupts for TPMS, in which case we set up polling) 11529126SWyllys.Ingersoll@Sun.COM * 3. Determine timeouts and commands duration 11539126SWyllys.Ingersoll@Sun.COM */ 11549126SWyllys.Ingersoll@Sun.COM static int 11559126SWyllys.Ingersoll@Sun.COM tis_init(tpm_state_t *tpm) { 11569126SWyllys.Ingersoll@Sun.COM uint32_t intf_caps; 11579126SWyllys.Ingersoll@Sun.COM int ret; 11589126SWyllys.Ingersoll@Sun.COM char *myname = "tis_init"; 11599126SWyllys.Ingersoll@Sun.COM 11609126SWyllys.Ingersoll@Sun.COM /* 11619126SWyllys.Ingersoll@Sun.COM * Temporarily set up timeouts before we get the real timeouts 11629126SWyllys.Ingersoll@Sun.COM * by issuing TPM_CAP commands (but to issue TPM_CAP commands, 11639126SWyllys.Ingersoll@Sun.COM * you need TIMEOUTs defined...chicken and egg problem here. 11649126SWyllys.Ingersoll@Sun.COM * TPM timeouts: Convert the milliseconds to clock cycles 11659126SWyllys.Ingersoll@Sun.COM */ 11669126SWyllys.Ingersoll@Sun.COM tpm->timeout_a = drv_usectohz(TIS_TIMEOUT_A); 11679126SWyllys.Ingersoll@Sun.COM tpm->timeout_b = drv_usectohz(TIS_TIMEOUT_B); 11689126SWyllys.Ingersoll@Sun.COM tpm->timeout_c = drv_usectohz(TIS_TIMEOUT_C); 11699126SWyllys.Ingersoll@Sun.COM tpm->timeout_d = drv_usectohz(TIS_TIMEOUT_D); 11709126SWyllys.Ingersoll@Sun.COM /* 11719126SWyllys.Ingersoll@Sun.COM * Do the same with the duration (real duration will be filled out 11729126SWyllys.Ingersoll@Sun.COM * when we call TPM_GetCapability to get the duration values from 11739126SWyllys.Ingersoll@Sun.COM * the TPM itself). 11749126SWyllys.Ingersoll@Sun.COM */ 11759126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_SHORT] = drv_usectohz(TPM_DEFAULT_DURATION); 11769126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_MEDIUM] = drv_usectohz(TPM_DEFAULT_DURATION); 11779126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_LONG] = drv_usectohz(TPM_DEFAULT_DURATION); 11789126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_UNDEFINED] = drv_usectohz(TPM_DEFAULT_DURATION); 11799126SWyllys.Ingersoll@Sun.COM 11809126SWyllys.Ingersoll@Sun.COM /* Find out supported capabilities */ 1181*10346Swyllys.ingersoll@sun.com intf_caps = tpm_get32(tpm, TPM_INTF_CAP); 11829126SWyllys.Ingersoll@Sun.COM 11839126SWyllys.Ingersoll@Sun.COM /* Upper 3 bytes should always return 0 */ 11849126SWyllys.Ingersoll@Sun.COM if (intf_caps & 0x7FFFFF00) { 11859126SWyllys.Ingersoll@Sun.COM #ifdef DEBUG 11869126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: bad intf_caps value 0x%0X", 11879126SWyllys.Ingersoll@Sun.COM myname, intf_caps); 11889126SWyllys.Ingersoll@Sun.COM #endif 11899126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 11909126SWyllys.Ingersoll@Sun.COM } 11919126SWyllys.Ingersoll@Sun.COM 11929126SWyllys.Ingersoll@Sun.COM /* These two interrupts are mandatory */ 11939126SWyllys.Ingersoll@Sun.COM if (!(intf_caps & TPM_INTF_INT_LOCALITY_CHANGE_INT)) { 11949126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: Mandatory capability Locality Change Int " 11959126SWyllys.Ingersoll@Sun.COM "not supported", myname); 11969126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 11979126SWyllys.Ingersoll@Sun.COM } 11989126SWyllys.Ingersoll@Sun.COM if (!(intf_caps & TPM_INTF_INT_DATA_AVAIL_INT)) { 11999126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: Mandatory capability Data Available Int " 12009126SWyllys.Ingersoll@Sun.COM "not supported", myname); 12019126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 12029126SWyllys.Ingersoll@Sun.COM } 12039126SWyllys.Ingersoll@Sun.COM 12049126SWyllys.Ingersoll@Sun.COM /* 12059126SWyllys.Ingersoll@Sun.COM * Before we start writing anything to TPM's registers, 12069126SWyllys.Ingersoll@Sun.COM * make sure we are in locality 0 12079126SWyllys.Ingersoll@Sun.COM */ 1208*10346Swyllys.ingersoll@sun.com ret = tis_request_locality(tpm, DEFAULT_LOCALITY); 12099126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 1210*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, "%s: Unable to request locality %d", myname, 1211*10346Swyllys.ingersoll@sun.com DEFAULT_LOCALITY); 12129126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 12139126SWyllys.Ingersoll@Sun.COM } /* Now we can refer to the locality as tpm->locality */ 12149126SWyllys.Ingersoll@Sun.COM 12159126SWyllys.Ingersoll@Sun.COM tpm->timeout_poll = drv_usectohz(TPM_POLLING_TIMEOUT); 12169126SWyllys.Ingersoll@Sun.COM tpm->intr_enabled = 0; 12179126SWyllys.Ingersoll@Sun.COM 12189126SWyllys.Ingersoll@Sun.COM /* Get the real timeouts from the TPM */ 12199126SWyllys.Ingersoll@Sun.COM ret = tpm_get_timeouts(tpm); 12209126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 12219126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_get_timeouts error", myname); 12229126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 12239126SWyllys.Ingersoll@Sun.COM } 12249126SWyllys.Ingersoll@Sun.COM 12259126SWyllys.Ingersoll@Sun.COM ret = tpm_get_duration(tpm); 12269126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 12279126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_get_duration error", myname); 12289126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 12299126SWyllys.Ingersoll@Sun.COM } 12309126SWyllys.Ingersoll@Sun.COM 12319126SWyllys.Ingersoll@Sun.COM /* This gets the TPM version information */ 12329126SWyllys.Ingersoll@Sun.COM ret = tpm_get_version(tpm); 12339126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 12349126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_get_version error", myname); 12359126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 12369126SWyllys.Ingersoll@Sun.COM } 12379126SWyllys.Ingersoll@Sun.COM 12389126SWyllys.Ingersoll@Sun.COM /* 12399126SWyllys.Ingersoll@Sun.COM * Unless the TPM completes the test of its commands, 12409126SWyllys.Ingersoll@Sun.COM * it can return an error when the untested commands are called 12419126SWyllys.Ingersoll@Sun.COM */ 12429126SWyllys.Ingersoll@Sun.COM ret = tpm_continue_selftest(tpm); 12439126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 12449126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_continue_selftest error", myname); 12459126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 12469126SWyllys.Ingersoll@Sun.COM } 12479126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 12489126SWyllys.Ingersoll@Sun.COM } 12499126SWyllys.Ingersoll@Sun.COM 12509126SWyllys.Ingersoll@Sun.COM /* 12519126SWyllys.Ingersoll@Sun.COM * Module Entry points 12529126SWyllys.Ingersoll@Sun.COM */ 12539126SWyllys.Ingersoll@Sun.COM int 12549126SWyllys.Ingersoll@Sun.COM _init(void) 12559126SWyllys.Ingersoll@Sun.COM { 12569126SWyllys.Ingersoll@Sun.COM int ret; 12579126SWyllys.Ingersoll@Sun.COM 12589126SWyllys.Ingersoll@Sun.COM ret = ddi_soft_state_init(&statep, sizeof (tpm_state_t), 1); 12599126SWyllys.Ingersoll@Sun.COM if (ret) 1260*10346Swyllys.ingersoll@sun.com { 1261*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, "ddi_soft_state_init failed: %d", ret); 12629126SWyllys.Ingersoll@Sun.COM return (ret); 1263*10346Swyllys.ingersoll@sun.com } 12649126SWyllys.Ingersoll@Sun.COM ret = mod_install(&tpm_ml); 12659126SWyllys.Ingersoll@Sun.COM if (ret != 0) { 12669126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "_init: mod_install returned non-zero"); 12679126SWyllys.Ingersoll@Sun.COM ddi_soft_state_fini(&statep); 12689126SWyllys.Ingersoll@Sun.COM return (ret); 12699126SWyllys.Ingersoll@Sun.COM } 12709126SWyllys.Ingersoll@Sun.COM 12719126SWyllys.Ingersoll@Sun.COM return (ret); 12729126SWyllys.Ingersoll@Sun.COM } 12739126SWyllys.Ingersoll@Sun.COM 12749126SWyllys.Ingersoll@Sun.COM int 12759126SWyllys.Ingersoll@Sun.COM _info(struct modinfo *modinfop) 12769126SWyllys.Ingersoll@Sun.COM { 12779126SWyllys.Ingersoll@Sun.COM int ret; 12789126SWyllys.Ingersoll@Sun.COM ret = mod_info(&tpm_ml, modinfop); 12799126SWyllys.Ingersoll@Sun.COM if (ret == 0) 12809126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "mod_info failed: %d", ret); 12819126SWyllys.Ingersoll@Sun.COM 12829126SWyllys.Ingersoll@Sun.COM return (ret); 12839126SWyllys.Ingersoll@Sun.COM } 12849126SWyllys.Ingersoll@Sun.COM 12859126SWyllys.Ingersoll@Sun.COM int 12869126SWyllys.Ingersoll@Sun.COM _fini() 12879126SWyllys.Ingersoll@Sun.COM { 12889126SWyllys.Ingersoll@Sun.COM int ret; 1289*10346Swyllys.ingersoll@sun.com 12909126SWyllys.Ingersoll@Sun.COM ret = mod_remove(&tpm_ml); 1291*10346Swyllys.ingersoll@sun.com if (ret != 0) 12929126SWyllys.Ingersoll@Sun.COM return (ret); 1293*10346Swyllys.ingersoll@sun.com 12949126SWyllys.Ingersoll@Sun.COM ddi_soft_state_fini(&statep); 12959126SWyllys.Ingersoll@Sun.COM 12969126SWyllys.Ingersoll@Sun.COM return (ret); 12979126SWyllys.Ingersoll@Sun.COM } 12989126SWyllys.Ingersoll@Sun.COM /* End of driver configuration functions */ 12999126SWyllys.Ingersoll@Sun.COM 13009126SWyllys.Ingersoll@Sun.COM static int 13019126SWyllys.Ingersoll@Sun.COM tpm_resume(tpm_state_t *tpm) 13029126SWyllys.Ingersoll@Sun.COM { 13039126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 13049126SWyllys.Ingersoll@Sun.COM if (!tpm->suspended) { 13059126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 13069126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 13079126SWyllys.Ingersoll@Sun.COM } 13089126SWyllys.Ingersoll@Sun.COM tpm->suspended = 0; 13099126SWyllys.Ingersoll@Sun.COM cv_broadcast(&tpm->suspend_cv); 13109126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 13119126SWyllys.Ingersoll@Sun.COM 13129126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 13139126SWyllys.Ingersoll@Sun.COM } 13149126SWyllys.Ingersoll@Sun.COM 1315*10346Swyllys.ingersoll@sun.com #ifdef sun4v 1316*10346Swyllys.ingersoll@sun.com static uint64_t hsvc_tpm_minor = 0; 1317*10346Swyllys.ingersoll@sun.com static hsvc_info_t hsvc_tpm = { 1318*10346Swyllys.ingersoll@sun.com HSVC_REV_1, NULL, HSVC_GROUP_TPM, 1, 0, NULL 1319*10346Swyllys.ingersoll@sun.com }; 1320*10346Swyllys.ingersoll@sun.com #endif 1321*10346Swyllys.ingersoll@sun.com 13229126SWyllys.Ingersoll@Sun.COM /* 13239126SWyllys.Ingersoll@Sun.COM * Sun DDI/DDK entry points 13249126SWyllys.Ingersoll@Sun.COM */ 13259126SWyllys.Ingersoll@Sun.COM static int 13269126SWyllys.Ingersoll@Sun.COM tpm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 13279126SWyllys.Ingersoll@Sun.COM { 1328*10346Swyllys.ingersoll@sun.com int ret; 13299126SWyllys.Ingersoll@Sun.COM int instance; 1330*10346Swyllys.ingersoll@sun.com #ifndef sun4v 1331*10346Swyllys.ingersoll@sun.com int idx, nregs; 1332*10346Swyllys.ingersoll@sun.com #endif 13339126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_attach"; 13349126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm = NULL; 13359126SWyllys.Ingersoll@Sun.COM 13369126SWyllys.Ingersoll@Sun.COM ASSERT(dip != NULL); 13379126SWyllys.Ingersoll@Sun.COM 13389126SWyllys.Ingersoll@Sun.COM instance = ddi_get_instance(dip); 1339*10346Swyllys.ingersoll@sun.com if (instance < 0) 1340*10346Swyllys.ingersoll@sun.com return (DDI_FAILURE); 13419126SWyllys.Ingersoll@Sun.COM 13429126SWyllys.Ingersoll@Sun.COM /* Nothing out of ordinary here */ 13439126SWyllys.Ingersoll@Sun.COM switch (cmd) { 13449126SWyllys.Ingersoll@Sun.COM case DDI_ATTACH: 1345*10346Swyllys.ingersoll@sun.com if (ddi_soft_state_zalloc(statep, instance) == DDI_SUCCESS) { 1346*10346Swyllys.ingersoll@sun.com tpm = ddi_get_soft_state(statep, instance); 1347*10346Swyllys.ingersoll@sun.com if (tpm == NULL) { 1348*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, 1349*10346Swyllys.ingersoll@sun.com "%s: cannot get state information.", 1350*10346Swyllys.ingersoll@sun.com myname); 1351*10346Swyllys.ingersoll@sun.com return (DDI_FAILURE); 1352*10346Swyllys.ingersoll@sun.com } 1353*10346Swyllys.ingersoll@sun.com tpm->dip = dip; 1354*10346Swyllys.ingersoll@sun.com } else { 1355*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, 1356*10346Swyllys.ingersoll@sun.com "%s: cannot allocate state information.", 13579126SWyllys.Ingersoll@Sun.COM myname); 1358*10346Swyllys.ingersoll@sun.com return (DDI_FAILURE); 13599126SWyllys.Ingersoll@Sun.COM } 13609126SWyllys.Ingersoll@Sun.COM break; 13619126SWyllys.Ingersoll@Sun.COM case DDI_RESUME: 13629126SWyllys.Ingersoll@Sun.COM tpm = ddi_get_soft_state(statep, instance); 13639126SWyllys.Ingersoll@Sun.COM if (tpm == NULL) { 1364*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, "%s: cannot get state information.", 13659126SWyllys.Ingersoll@Sun.COM myname); 1366*10346Swyllys.ingersoll@sun.com return (DDI_FAILURE); 13679126SWyllys.Ingersoll@Sun.COM } 13689126SWyllys.Ingersoll@Sun.COM return (tpm_resume(tpm)); 13699126SWyllys.Ingersoll@Sun.COM default: 13709126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: cmd %d is not implemented", myname, cmd); 13719126SWyllys.Ingersoll@Sun.COM ret = DDI_FAILURE; 13729126SWyllys.Ingersoll@Sun.COM goto FAIL; 13739126SWyllys.Ingersoll@Sun.COM } 13749126SWyllys.Ingersoll@Sun.COM 13759126SWyllys.Ingersoll@Sun.COM /* Zeroize the flag, which is used to keep track of what is allocated */ 13769126SWyllys.Ingersoll@Sun.COM tpm->flags = 0; 13779126SWyllys.Ingersoll@Sun.COM 1378*10346Swyllys.ingersoll@sun.com #ifdef sun4v 1379*10346Swyllys.ingersoll@sun.com ret = hsvc_register(&hsvc_tpm, &hsvc_tpm_minor); 1380*10346Swyllys.ingersoll@sun.com if (ret != 0) { 1381*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, "%s: failed to register with " 1382*10346Swyllys.ingersoll@sun.com "hypervisor: 0x%0x", myname, ret); 1383*10346Swyllys.ingersoll@sun.com goto FAIL; 1384*10346Swyllys.ingersoll@sun.com } 1385*10346Swyllys.ingersoll@sun.com tpm->flags |= TPM_HSVC_REGISTERED; 1386*10346Swyllys.ingersoll@sun.com #else 13879126SWyllys.Ingersoll@Sun.COM tpm->accattr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 13889126SWyllys.Ingersoll@Sun.COM tpm->accattr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; 13899126SWyllys.Ingersoll@Sun.COM tpm->accattr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 13909126SWyllys.Ingersoll@Sun.COM 13919126SWyllys.Ingersoll@Sun.COM idx = 0; 13929126SWyllys.Ingersoll@Sun.COM ret = ddi_dev_nregs(tpm->dip, &nregs); 13939126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) 13949126SWyllys.Ingersoll@Sun.COM goto FAIL; 13959126SWyllys.Ingersoll@Sun.COM 13969126SWyllys.Ingersoll@Sun.COM /* 13979126SWyllys.Ingersoll@Sun.COM * TPM vendors put the TPM registers in different 13989126SWyllys.Ingersoll@Sun.COM * slots in their register lists. They are not always 13999126SWyllys.Ingersoll@Sun.COM * the 1st set of registers, for instance. 14009126SWyllys.Ingersoll@Sun.COM * Loop until we find the set that matches the expected 14019126SWyllys.Ingersoll@Sun.COM * register size (0x5000). 14029126SWyllys.Ingersoll@Sun.COM */ 14039126SWyllys.Ingersoll@Sun.COM for (idx = 0; idx < nregs; idx++) { 14049126SWyllys.Ingersoll@Sun.COM off_t regsize; 14059126SWyllys.Ingersoll@Sun.COM 14069126SWyllys.Ingersoll@Sun.COM if ((ret = ddi_dev_regsize(tpm->dip, idx, ®size)) != 14079126SWyllys.Ingersoll@Sun.COM DDI_SUCCESS) 14089126SWyllys.Ingersoll@Sun.COM goto FAIL; 14099126SWyllys.Ingersoll@Sun.COM /* The TIS spec says the TPM registers must be 0x5000 bytes */ 14109126SWyllys.Ingersoll@Sun.COM if (regsize == 0x5000) 14119126SWyllys.Ingersoll@Sun.COM break; 14129126SWyllys.Ingersoll@Sun.COM } 14139382SWyllys.Ingersoll@Sun.COM if (idx == nregs) 14149382SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 14159382SWyllys.Ingersoll@Sun.COM 14169126SWyllys.Ingersoll@Sun.COM ret = ddi_regs_map_setup(tpm->dip, idx, (caddr_t *)&tpm->addr, 14179126SWyllys.Ingersoll@Sun.COM (offset_t)0, (offset_t)0x5000, 14189126SWyllys.Ingersoll@Sun.COM &tpm->accattr, &tpm->handle); 14199126SWyllys.Ingersoll@Sun.COM 14209126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 14219126SWyllys.Ingersoll@Sun.COM goto FAIL; 14229126SWyllys.Ingersoll@Sun.COM } 14239126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DIDREGSMAP; 1424*10346Swyllys.ingersoll@sun.com #endif 14259126SWyllys.Ingersoll@Sun.COM /* Enable TPM device according to the TIS specification */ 14269126SWyllys.Ingersoll@Sun.COM ret = tis_init(tpm); 14279126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 1428*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, "%s: tis_init() failed with error %d", 14299126SWyllys.Ingersoll@Sun.COM myname, ret); 14309126SWyllys.Ingersoll@Sun.COM 14319126SWyllys.Ingersoll@Sun.COM /* We need to clean up the ddi_regs_map_setup call */ 1432*10346Swyllys.ingersoll@sun.com if (tpm->flags & TPM_DIDREGSMAP) { 1433*10346Swyllys.ingersoll@sun.com ddi_regs_map_free(&tpm->handle); 1434*10346Swyllys.ingersoll@sun.com tpm->handle = NULL; 1435*10346Swyllys.ingersoll@sun.com tpm->flags &= ~TPM_DIDREGSMAP; 1436*10346Swyllys.ingersoll@sun.com } 14379126SWyllys.Ingersoll@Sun.COM goto FAIL; 14389126SWyllys.Ingersoll@Sun.COM } 14399126SWyllys.Ingersoll@Sun.COM 14409126SWyllys.Ingersoll@Sun.COM /* Initialize the inter-process lock */ 14419126SWyllys.Ingersoll@Sun.COM mutex_init(&tpm->dev_lock, NULL, MUTEX_DRIVER, NULL); 14429126SWyllys.Ingersoll@Sun.COM mutex_init(&tpm->pm_mutex, NULL, MUTEX_DRIVER, NULL); 14439126SWyllys.Ingersoll@Sun.COM cv_init(&tpm->suspend_cv, NULL, CV_DRIVER, NULL); 14449126SWyllys.Ingersoll@Sun.COM 14459126SWyllys.Ingersoll@Sun.COM /* Set the suspend/resume property */ 14469126SWyllys.Ingersoll@Sun.COM (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, 14479126SWyllys.Ingersoll@Sun.COM "pm-hardware-state", "needs-suspend-resume"); 14489126SWyllys.Ingersoll@Sun.COM 14499126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 14509126SWyllys.Ingersoll@Sun.COM tpm->suspended = 0; 14519126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 14529126SWyllys.Ingersoll@Sun.COM 14539126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DID_MUTEX; 14549126SWyllys.Ingersoll@Sun.COM 14559126SWyllys.Ingersoll@Sun.COM /* Initialize the buffer and the lock associated with it */ 14569126SWyllys.Ingersoll@Sun.COM tpm->bufsize = TPM_IO_BUF_SIZE; 14579126SWyllys.Ingersoll@Sun.COM tpm->iobuf = kmem_zalloc((sizeof (uint8_t))*(tpm->bufsize), KM_SLEEP); 14589126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DID_IO_ALLOC; 14599126SWyllys.Ingersoll@Sun.COM 14609126SWyllys.Ingersoll@Sun.COM mutex_init(&tpm->iobuf_lock, NULL, MUTEX_DRIVER, NULL); 14619126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DID_IO_MUTEX; 14629126SWyllys.Ingersoll@Sun.COM 14639126SWyllys.Ingersoll@Sun.COM cv_init(&tpm->iobuf_cv, NULL, CV_DRIVER, NULL); 14649126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DID_IO_CV; 14659126SWyllys.Ingersoll@Sun.COM 14669126SWyllys.Ingersoll@Sun.COM /* Create minor node */ 14679126SWyllys.Ingersoll@Sun.COM ret = ddi_create_minor_node(dip, "tpm", S_IFCHR, ddi_get_instance(dip), 14689126SWyllys.Ingersoll@Sun.COM DDI_PSEUDO, 0); 14699126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 14709126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: ddi_create_minor_node failed", myname); 14719126SWyllys.Ingersoll@Sun.COM goto FAIL; 14729126SWyllys.Ingersoll@Sun.COM } 14739126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DIDMINOR; 14749126SWyllys.Ingersoll@Sun.COM 1475*10346Swyllys.ingersoll@sun.com #ifdef KCF_TPM_RNG_PROVIDER 1476*10346Swyllys.ingersoll@sun.com /* register RNG with kcf */ 1477*10346Swyllys.ingersoll@sun.com if (tpmrng_register(tpm) != DDI_SUCCESS) 1478*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, "%s: tpm RNG failed to register with kcf", 1479*10346Swyllys.ingersoll@sun.com myname); 1480*10346Swyllys.ingersoll@sun.com #endif 1481*10346Swyllys.ingersoll@sun.com 14829126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 14839126SWyllys.Ingersoll@Sun.COM FAIL: 1484*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, "%s: tpm failed to attach", myname); 14859126SWyllys.Ingersoll@Sun.COM if (tpm != NULL) { 14869126SWyllys.Ingersoll@Sun.COM tpm_cleanup(dip, tpm); 14879126SWyllys.Ingersoll@Sun.COM ddi_soft_state_free(statep, instance); 14889126SWyllys.Ingersoll@Sun.COM tpm = NULL; 14899126SWyllys.Ingersoll@Sun.COM } 14909126SWyllys.Ingersoll@Sun.COM 14919126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 14929126SWyllys.Ingersoll@Sun.COM } 14939126SWyllys.Ingersoll@Sun.COM 14949126SWyllys.Ingersoll@Sun.COM /* 14959126SWyllys.Ingersoll@Sun.COM * Called by tpm_detach and tpm_attach (only on failure) 14969126SWyllys.Ingersoll@Sun.COM * Free up the resources that are allocated 14979126SWyllys.Ingersoll@Sun.COM */ 14989126SWyllys.Ingersoll@Sun.COM static void 14999126SWyllys.Ingersoll@Sun.COM tpm_cleanup(dev_info_t *dip, tpm_state_t *tpm) 15009126SWyllys.Ingersoll@Sun.COM { 15019126SWyllys.Ingersoll@Sun.COM if (tpm == NULL) 15029126SWyllys.Ingersoll@Sun.COM return; 15039126SWyllys.Ingersoll@Sun.COM 1504*10346Swyllys.ingersoll@sun.com #ifdef KCF_TPM_RNG_PROVIDER 1505*10346Swyllys.ingersoll@sun.com (void) tpmrng_unregister(tpm); 1506*10346Swyllys.ingersoll@sun.com #endif 1507*10346Swyllys.ingersoll@sun.com 1508*10346Swyllys.ingersoll@sun.com #ifdef sun4v 1509*10346Swyllys.ingersoll@sun.com if (tpm->flags & TPM_HSVC_REGISTERED) { 1510*10346Swyllys.ingersoll@sun.com (void) hsvc_unregister(&hsvc_tpm); 1511*10346Swyllys.ingersoll@sun.com tpm->flags &= ~(TPM_HSVC_REGISTERED); 1512*10346Swyllys.ingersoll@sun.com } 1513*10346Swyllys.ingersoll@sun.com #endif 15149126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DID_MUTEX) { 15159126SWyllys.Ingersoll@Sun.COM mutex_destroy(&tpm->dev_lock); 15169126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DID_MUTEX); 15179126SWyllys.Ingersoll@Sun.COM } 15189126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DID_IO_ALLOC) { 15199126SWyllys.Ingersoll@Sun.COM ASSERT(tpm->iobuf != NULL); 15209126SWyllys.Ingersoll@Sun.COM kmem_free(tpm->iobuf, (sizeof (uint8_t))*(tpm->bufsize)); 15219126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DID_IO_ALLOC); 15229126SWyllys.Ingersoll@Sun.COM } 15239126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DID_IO_MUTEX) { 15249126SWyllys.Ingersoll@Sun.COM mutex_destroy(&tpm->iobuf_lock); 15259126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DID_IO_MUTEX); 15269126SWyllys.Ingersoll@Sun.COM } 15279126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DID_IO_CV) { 15289126SWyllys.Ingersoll@Sun.COM cv_destroy(&tpm->iobuf_cv); 15299126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DID_IO_CV); 15309126SWyllys.Ingersoll@Sun.COM } 15319126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DIDREGSMAP) { 15329126SWyllys.Ingersoll@Sun.COM /* Free the mapped addresses */ 15339126SWyllys.Ingersoll@Sun.COM if (tpm->handle != NULL) 15349126SWyllys.Ingersoll@Sun.COM ddi_regs_map_free(&tpm->handle); 15359126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DIDREGSMAP); 15369126SWyllys.Ingersoll@Sun.COM } 15379126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DIDMINOR) { 15389126SWyllys.Ingersoll@Sun.COM /* Remove minor node */ 15399126SWyllys.Ingersoll@Sun.COM ddi_remove_minor_node(dip, NULL); 15409126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DIDMINOR); 15419126SWyllys.Ingersoll@Sun.COM } 15429126SWyllys.Ingersoll@Sun.COM } 15439126SWyllys.Ingersoll@Sun.COM 15449126SWyllys.Ingersoll@Sun.COM static int 15459126SWyllys.Ingersoll@Sun.COM tpm_suspend(tpm_state_t *tpm) 15469126SWyllys.Ingersoll@Sun.COM { 15479126SWyllys.Ingersoll@Sun.COM if (tpm == NULL) 15489126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 15499126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 15509126SWyllys.Ingersoll@Sun.COM if (tpm->suspended) { 15519126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 15529126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 15539126SWyllys.Ingersoll@Sun.COM } 15549126SWyllys.Ingersoll@Sun.COM tpm->suspended = 1; 15559126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 15569126SWyllys.Ingersoll@Sun.COM 15579126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 15589126SWyllys.Ingersoll@Sun.COM } 15599126SWyllys.Ingersoll@Sun.COM 15609126SWyllys.Ingersoll@Sun.COM static int 15619126SWyllys.Ingersoll@Sun.COM tpm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 15629126SWyllys.Ingersoll@Sun.COM { 15639126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_detach"; 15649126SWyllys.Ingersoll@Sun.COM int instance; 15659126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 15669126SWyllys.Ingersoll@Sun.COM 15679126SWyllys.Ingersoll@Sun.COM ASSERT(dip != NULL); 15689126SWyllys.Ingersoll@Sun.COM 15699126SWyllys.Ingersoll@Sun.COM instance = ddi_get_instance(dip); 1570*10346Swyllys.ingersoll@sun.com if (instance < 0) 1571*10346Swyllys.ingersoll@sun.com return (DDI_FAILURE); 1572*10346Swyllys.ingersoll@sun.com 15739126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 15749126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 15759126SWyllys.Ingersoll@Sun.COM myname); 15769126SWyllys.Ingersoll@Sun.COM return (ENXIO); 15779126SWyllys.Ingersoll@Sun.COM } 15789126SWyllys.Ingersoll@Sun.COM 15799126SWyllys.Ingersoll@Sun.COM switch (cmd) { 15809126SWyllys.Ingersoll@Sun.COM case DDI_DETACH: 15819126SWyllys.Ingersoll@Sun.COM /* Body is after the switch stmt */ 15829126SWyllys.Ingersoll@Sun.COM break; 15839126SWyllys.Ingersoll@Sun.COM case DDI_SUSPEND: 15849126SWyllys.Ingersoll@Sun.COM return (tpm_suspend(tpm)); 15859126SWyllys.Ingersoll@Sun.COM default: 15869126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: case %d not implemented", myname, cmd); 15879126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 15889126SWyllys.Ingersoll@Sun.COM } 15899126SWyllys.Ingersoll@Sun.COM 15909126SWyllys.Ingersoll@Sun.COM /* Since we are freeing tpm structure, we need to gain the lock */ 15919126SWyllys.Ingersoll@Sun.COM 15929126SWyllys.Ingersoll@Sun.COM tpm_cleanup(dip, tpm); 15939126SWyllys.Ingersoll@Sun.COM 15949126SWyllys.Ingersoll@Sun.COM mutex_destroy(&tpm->pm_mutex); 15959126SWyllys.Ingersoll@Sun.COM cv_destroy(&tpm->suspend_cv); 15969126SWyllys.Ingersoll@Sun.COM 15979126SWyllys.Ingersoll@Sun.COM /* Free the soft state */ 15989126SWyllys.Ingersoll@Sun.COM ddi_soft_state_free(statep, instance); 15999126SWyllys.Ingersoll@Sun.COM tpm = NULL; 16009126SWyllys.Ingersoll@Sun.COM 16019126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 16029126SWyllys.Ingersoll@Sun.COM } 16039126SWyllys.Ingersoll@Sun.COM 16049126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 16059126SWyllys.Ingersoll@Sun.COM static int 16069126SWyllys.Ingersoll@Sun.COM tpm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 16079126SWyllys.Ingersoll@Sun.COM { 16089126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_getinfo"; 16099126SWyllys.Ingersoll@Sun.COM int instance; 16109126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 16119126SWyllys.Ingersoll@Sun.COM 16129126SWyllys.Ingersoll@Sun.COM instance = ddi_get_instance(dip); 16139126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 16149126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 16159126SWyllys.Ingersoll@Sun.COM myname); 16169126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 16179126SWyllys.Ingersoll@Sun.COM } 16189126SWyllys.Ingersoll@Sun.COM 16199126SWyllys.Ingersoll@Sun.COM switch (cmd) { 16209126SWyllys.Ingersoll@Sun.COM case DDI_INFO_DEVT2DEVINFO: 16219126SWyllys.Ingersoll@Sun.COM *resultp = tpm->dip; 16229126SWyllys.Ingersoll@Sun.COM break; 16239126SWyllys.Ingersoll@Sun.COM case DDI_INFO_DEVT2INSTANCE: 16249126SWyllys.Ingersoll@Sun.COM *resultp = 0; 16259126SWyllys.Ingersoll@Sun.COM break; 16269126SWyllys.Ingersoll@Sun.COM default: 16279126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: cmd %d is not implemented", myname, cmd); 16289126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 16299126SWyllys.Ingersoll@Sun.COM } 16309126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 16319126SWyllys.Ingersoll@Sun.COM } 16329126SWyllys.Ingersoll@Sun.COM 16339126SWyllys.Ingersoll@Sun.COM /* 16349126SWyllys.Ingersoll@Sun.COM * Driver entry points 16359126SWyllys.Ingersoll@Sun.COM */ 16369126SWyllys.Ingersoll@Sun.COM 16379126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 16389126SWyllys.Ingersoll@Sun.COM static int 16399126SWyllys.Ingersoll@Sun.COM tpm_open(dev_t *devp, int flag, int otyp, cred_t *cred) 16409126SWyllys.Ingersoll@Sun.COM { 16419126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_open"; 16429126SWyllys.Ingersoll@Sun.COM int instance; 16439126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 16449126SWyllys.Ingersoll@Sun.COM 16459126SWyllys.Ingersoll@Sun.COM ASSERT(devp != NULL); 16469126SWyllys.Ingersoll@Sun.COM 16479126SWyllys.Ingersoll@Sun.COM instance = getminor(*devp); 16489126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 16499126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 16509126SWyllys.Ingersoll@Sun.COM myname); 16519126SWyllys.Ingersoll@Sun.COM return (ENXIO); 16529126SWyllys.Ingersoll@Sun.COM } 16539126SWyllys.Ingersoll@Sun.COM if (otyp != OTYP_CHR) { 16549126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: otyp(%d) != OTYP_CHR(%d)", 16559126SWyllys.Ingersoll@Sun.COM myname, otyp, OTYP_CHR); 16569126SWyllys.Ingersoll@Sun.COM return (EINVAL); 16579126SWyllys.Ingersoll@Sun.COM } 1658*10346Swyllys.ingersoll@sun.com TPM_EXCLUSIVE_LOCK(tpm); 16599126SWyllys.Ingersoll@Sun.COM 16609126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->dev_lock); 16619126SWyllys.Ingersoll@Sun.COM if (tpm->dev_held) { 16629126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: the device is already being used", 16639126SWyllys.Ingersoll@Sun.COM myname); 16649126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->dev_lock); 16659126SWyllys.Ingersoll@Sun.COM return (EBUSY); 16669126SWyllys.Ingersoll@Sun.COM } 16679126SWyllys.Ingersoll@Sun.COM 16689126SWyllys.Ingersoll@Sun.COM /* The device is free so mark it busy */ 16699126SWyllys.Ingersoll@Sun.COM tpm->dev_held = 1; 16709126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->dev_lock); 16719126SWyllys.Ingersoll@Sun.COM 16729126SWyllys.Ingersoll@Sun.COM return (0); 16739126SWyllys.Ingersoll@Sun.COM } 16749126SWyllys.Ingersoll@Sun.COM 16759126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 16769126SWyllys.Ingersoll@Sun.COM static int 16779126SWyllys.Ingersoll@Sun.COM tpm_close(dev_t dev, int flag, int otyp, cred_t *cred) 16789126SWyllys.Ingersoll@Sun.COM { 16799126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_close"; 16809126SWyllys.Ingersoll@Sun.COM int instance; 16819126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 16829126SWyllys.Ingersoll@Sun.COM 16839126SWyllys.Ingersoll@Sun.COM instance = getminor(dev); 16849126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 16859126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 16869126SWyllys.Ingersoll@Sun.COM myname); 16879126SWyllys.Ingersoll@Sun.COM return (ENXIO); 16889126SWyllys.Ingersoll@Sun.COM } 16899126SWyllys.Ingersoll@Sun.COM if (otyp != OTYP_CHR) { 16909126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: otyp(%d) != OTYP_CHR(%d)", 16919126SWyllys.Ingersoll@Sun.COM myname, otyp, OTYP_CHR); 16929126SWyllys.Ingersoll@Sun.COM return (EINVAL); 16939126SWyllys.Ingersoll@Sun.COM } 1694*10346Swyllys.ingersoll@sun.com TPM_EXCLUSIVE_LOCK(tpm); 16959126SWyllys.Ingersoll@Sun.COM 16969126SWyllys.Ingersoll@Sun.COM ASSERT(tpm->dev_held); 16979126SWyllys.Ingersoll@Sun.COM 16989126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->dev_lock); 16999126SWyllys.Ingersoll@Sun.COM ASSERT(mutex_owned(&tpm->dev_lock)); 17009126SWyllys.Ingersoll@Sun.COM tpm->dev_held = 0; 17019126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->dev_lock); 17029126SWyllys.Ingersoll@Sun.COM 17039126SWyllys.Ingersoll@Sun.COM return (0); 17049126SWyllys.Ingersoll@Sun.COM } 17059126SWyllys.Ingersoll@Sun.COM 17069126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 17079126SWyllys.Ingersoll@Sun.COM static int 17089126SWyllys.Ingersoll@Sun.COM tpm_read(dev_t dev, struct uio *uiop, cred_t *credp) 17099126SWyllys.Ingersoll@Sun.COM { 17109126SWyllys.Ingersoll@Sun.COM int ret; 17119126SWyllys.Ingersoll@Sun.COM uint32_t size; 17129126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_read"; 17139126SWyllys.Ingersoll@Sun.COM int instance; 17149126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 17159126SWyllys.Ingersoll@Sun.COM 17169126SWyllys.Ingersoll@Sun.COM instance = getminor(dev); 17179126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 17189126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 17199126SWyllys.Ingersoll@Sun.COM myname); 17209126SWyllys.Ingersoll@Sun.COM return (ENXIO); 17219126SWyllys.Ingersoll@Sun.COM } 17229126SWyllys.Ingersoll@Sun.COM if (uiop == NULL) { 17239126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: passed in uiop is NULL", myname); 17249126SWyllys.Ingersoll@Sun.COM return (EFAULT); 17259126SWyllys.Ingersoll@Sun.COM } 17269126SWyllys.Ingersoll@Sun.COM 1727*10346Swyllys.ingersoll@sun.com TPM_EXCLUSIVE_LOCK(tpm); 17289126SWyllys.Ingersoll@Sun.COM 17299126SWyllys.Ingersoll@Sun.COM /* Receive the data after requiring the lock */ 1730*10346Swyllys.ingersoll@sun.com ret = tpm_io_lock(tpm); 17319126SWyllys.Ingersoll@Sun.COM 17329126SWyllys.Ingersoll@Sun.COM /* Timeout reached */ 1733*10346Swyllys.ingersoll@sun.com if (ret) 17349126SWyllys.Ingersoll@Sun.COM return (ret); 17359126SWyllys.Ingersoll@Sun.COM 17369126SWyllys.Ingersoll@Sun.COM if (uiop->uio_resid > tpm->bufsize) { 17379126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: read_in data is bigger " 17389126SWyllys.Ingersoll@Sun.COM "than tpm->bufsize:read in:%d, bufsiz:%d", 17399126SWyllys.Ingersoll@Sun.COM myname, (int)uiop->uio_resid, (int)tpm->bufsize); 17409126SWyllys.Ingersoll@Sun.COM ret = EIO; 17419126SWyllys.Ingersoll@Sun.COM goto OUT; 17429126SWyllys.Ingersoll@Sun.COM } 17439126SWyllys.Ingersoll@Sun.COM 17449126SWyllys.Ingersoll@Sun.COM ret = tis_recv_data(tpm, tpm->iobuf, tpm->bufsize); 17459126SWyllys.Ingersoll@Sun.COM if (ret < TPM_HEADER_SIZE) { 17469126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_recv_data returned error", myname); 17479126SWyllys.Ingersoll@Sun.COM ret = EIO; 17489126SWyllys.Ingersoll@Sun.COM goto OUT; 17499126SWyllys.Ingersoll@Sun.COM } 17509126SWyllys.Ingersoll@Sun.COM 17519126SWyllys.Ingersoll@Sun.COM size = load32(tpm->iobuf, 2); 17529126SWyllys.Ingersoll@Sun.COM if (ret != size) { 17539126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_recv_data:" 17549126SWyllys.Ingersoll@Sun.COM "expected size=%d, actually read=%d", 17559126SWyllys.Ingersoll@Sun.COM myname, size, ret); 17569126SWyllys.Ingersoll@Sun.COM ret = EIO; 17579126SWyllys.Ingersoll@Sun.COM goto OUT; 17589126SWyllys.Ingersoll@Sun.COM } 17599126SWyllys.Ingersoll@Sun.COM 17609126SWyllys.Ingersoll@Sun.COM /* Send the buffer from the kernel to the userspace */ 17619126SWyllys.Ingersoll@Sun.COM ret = uiomove(tpm->iobuf, size, UIO_READ, uiop); 17629126SWyllys.Ingersoll@Sun.COM if (ret) { 17639126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: uiomove returned error", myname); 17649126SWyllys.Ingersoll@Sun.COM goto OUT; 17659126SWyllys.Ingersoll@Sun.COM } 17669126SWyllys.Ingersoll@Sun.COM 17679126SWyllys.Ingersoll@Sun.COM /* Zeroize the buffer... */ 17689126SWyllys.Ingersoll@Sun.COM bzero(tpm->iobuf, tpm->bufsize); 17699126SWyllys.Ingersoll@Sun.COM ret = DDI_SUCCESS; 17709126SWyllys.Ingersoll@Sun.COM OUT: 17719126SWyllys.Ingersoll@Sun.COM /* We are done now: wake up the waiting threads */ 17729126SWyllys.Ingersoll@Sun.COM tpm_unlock(tpm); 17739126SWyllys.Ingersoll@Sun.COM 17749126SWyllys.Ingersoll@Sun.COM return (ret); 17759126SWyllys.Ingersoll@Sun.COM } 17769126SWyllys.Ingersoll@Sun.COM 17779126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 17789126SWyllys.Ingersoll@Sun.COM static int 17799126SWyllys.Ingersoll@Sun.COM tpm_write(dev_t dev, struct uio *uiop, cred_t *credp) 17809126SWyllys.Ingersoll@Sun.COM { 17819126SWyllys.Ingersoll@Sun.COM int ret; 17829126SWyllys.Ingersoll@Sun.COM size_t len; 17839126SWyllys.Ingersoll@Sun.COM uint32_t size; 17849126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_write"; 17859126SWyllys.Ingersoll@Sun.COM int instance; 17869126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 17879126SWyllys.Ingersoll@Sun.COM 17889126SWyllys.Ingersoll@Sun.COM instance = getminor(dev); 17899126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 17909126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 17919126SWyllys.Ingersoll@Sun.COM myname); 17929126SWyllys.Ingersoll@Sun.COM return (ENXIO); 17939126SWyllys.Ingersoll@Sun.COM } 17949126SWyllys.Ingersoll@Sun.COM 17959126SWyllys.Ingersoll@Sun.COM if (uiop == NULL) { 17969126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: passed in uiop is NULL", myname); 17979126SWyllys.Ingersoll@Sun.COM return (EFAULT); 17989126SWyllys.Ingersoll@Sun.COM } 17999126SWyllys.Ingersoll@Sun.COM 1800*10346Swyllys.ingersoll@sun.com TPM_EXCLUSIVE_LOCK(tpm); 18019126SWyllys.Ingersoll@Sun.COM 18029126SWyllys.Ingersoll@Sun.COM len = uiop->uio_resid; 18039126SWyllys.Ingersoll@Sun.COM if (len == 0) { 18049126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: requested read of len 0", myname); 18059126SWyllys.Ingersoll@Sun.COM return (0); 18069126SWyllys.Ingersoll@Sun.COM } 18079126SWyllys.Ingersoll@Sun.COM 18089126SWyllys.Ingersoll@Sun.COM /* Get the lock for using iobuf */ 1809*10346Swyllys.ingersoll@sun.com ret = tpm_io_lock(tpm); 18109126SWyllys.Ingersoll@Sun.COM /* Timeout Reached */ 1811*10346Swyllys.ingersoll@sun.com if (ret) 18129126SWyllys.Ingersoll@Sun.COM return (ret); 18139126SWyllys.Ingersoll@Sun.COM 18149126SWyllys.Ingersoll@Sun.COM /* Copy the header and parse the structure to find out the size... */ 18159126SWyllys.Ingersoll@Sun.COM ret = uiomove(tpm->iobuf, TPM_HEADER_SIZE, UIO_WRITE, uiop); 18169126SWyllys.Ingersoll@Sun.COM if (ret) { 18179126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: uiomove returned error" 18189126SWyllys.Ingersoll@Sun.COM "while getting the the header", 18199126SWyllys.Ingersoll@Sun.COM myname); 18209126SWyllys.Ingersoll@Sun.COM goto OUT; 18219126SWyllys.Ingersoll@Sun.COM } 18229126SWyllys.Ingersoll@Sun.COM 18239126SWyllys.Ingersoll@Sun.COM /* Get the buffersize from the command buffer structure */ 18249126SWyllys.Ingersoll@Sun.COM size = load32(tpm->iobuf, TPM_PARAMSIZE_OFFSET); 18259126SWyllys.Ingersoll@Sun.COM 18269126SWyllys.Ingersoll@Sun.COM /* Copy the command to the contiguous buffer */ 18279126SWyllys.Ingersoll@Sun.COM if (size > tpm->bufsize) { 18289126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: size %d is greater than " 18299126SWyllys.Ingersoll@Sun.COM "the tpm's input buffer size %d", 18309126SWyllys.Ingersoll@Sun.COM myname, (int)size, (int)tpm->bufsize); 18319126SWyllys.Ingersoll@Sun.COM ret = ENXIO; 18329126SWyllys.Ingersoll@Sun.COM goto OUT; 18339126SWyllys.Ingersoll@Sun.COM } 18349126SWyllys.Ingersoll@Sun.COM 18359126SWyllys.Ingersoll@Sun.COM /* Copy the buffer from the userspace to kernel */ 18369126SWyllys.Ingersoll@Sun.COM ret = uiomove(tpm->iobuf+TPM_HEADER_SIZE, size-TPM_HEADER_SIZE, 18379126SWyllys.Ingersoll@Sun.COM UIO_WRITE, uiop); 18389126SWyllys.Ingersoll@Sun.COM 18399126SWyllys.Ingersoll@Sun.COM if (ret) { 18409126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: uiomove returned error" 18419126SWyllys.Ingersoll@Sun.COM "while getting the rest of the command", myname); 18429126SWyllys.Ingersoll@Sun.COM goto OUT; 18439126SWyllys.Ingersoll@Sun.COM } 18449126SWyllys.Ingersoll@Sun.COM 18459126SWyllys.Ingersoll@Sun.COM /* Send the command */ 18469126SWyllys.Ingersoll@Sun.COM ret = tis_send_data(tpm, tpm->iobuf, size); 18479126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 18489126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_send_data returned error", myname); 18499126SWyllys.Ingersoll@Sun.COM ret = EFAULT; 18509126SWyllys.Ingersoll@Sun.COM goto OUT; 18519126SWyllys.Ingersoll@Sun.COM } 18529126SWyllys.Ingersoll@Sun.COM 18539126SWyllys.Ingersoll@Sun.COM /* Zeroize the buffer... */ 18549126SWyllys.Ingersoll@Sun.COM bzero(tpm->iobuf, tpm->bufsize); 18559126SWyllys.Ingersoll@Sun.COM ret = DDI_SUCCESS; 18569126SWyllys.Ingersoll@Sun.COM OUT: 18579126SWyllys.Ingersoll@Sun.COM tpm_unlock(tpm); 18589126SWyllys.Ingersoll@Sun.COM return (ret); 18599126SWyllys.Ingersoll@Sun.COM } 18609126SWyllys.Ingersoll@Sun.COM 18619126SWyllys.Ingersoll@Sun.COM /* 18629126SWyllys.Ingersoll@Sun.COM * This is to deal with the contentions for the iobuf 18639126SWyllys.Ingersoll@Sun.COM */ 18649126SWyllys.Ingersoll@Sun.COM static inline int 1865*10346Swyllys.ingersoll@sun.com tpm_io_lock(tpm_state_t *tpm) 18669126SWyllys.Ingersoll@Sun.COM { 18679126SWyllys.Ingersoll@Sun.COM int ret; 18689126SWyllys.Ingersoll@Sun.COM clock_t timeout; 18699126SWyllys.Ingersoll@Sun.COM 18709126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->iobuf_lock); 18719126SWyllys.Ingersoll@Sun.COM ASSERT(mutex_owned(&tpm->iobuf_lock)); 18729126SWyllys.Ingersoll@Sun.COM 18739126SWyllys.Ingersoll@Sun.COM timeout = ddi_get_lbolt() + drv_usectohz(TPM_IO_TIMEOUT); 18749126SWyllys.Ingersoll@Sun.COM 18759126SWyllys.Ingersoll@Sun.COM /* Wait until the iobuf becomes free with the timeout */ 18769126SWyllys.Ingersoll@Sun.COM while (tpm->iobuf_inuse) { 18779126SWyllys.Ingersoll@Sun.COM ret = cv_timedwait(&tpm->iobuf_cv, &tpm->iobuf_lock, timeout); 18789126SWyllys.Ingersoll@Sun.COM if (ret <= 0) { 18799126SWyllys.Ingersoll@Sun.COM /* Timeout reached */ 18809126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->iobuf_lock); 1881*10346Swyllys.ingersoll@sun.com #ifdef DEBUG 1882*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, "tpm_io_lock:iorequest timed out"); 1883*10346Swyllys.ingersoll@sun.com #endif 188410335SJames.McPherson@Sun.COM return (ETIME); 18859126SWyllys.Ingersoll@Sun.COM } 18869126SWyllys.Ingersoll@Sun.COM } 18879126SWyllys.Ingersoll@Sun.COM tpm->iobuf_inuse = 1; 18889126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->iobuf_lock); 18899126SWyllys.Ingersoll@Sun.COM return (0); 18909126SWyllys.Ingersoll@Sun.COM } 18919126SWyllys.Ingersoll@Sun.COM 18929126SWyllys.Ingersoll@Sun.COM /* 18939126SWyllys.Ingersoll@Sun.COM * This is to deal with the contentions for the iobuf 18949126SWyllys.Ingersoll@Sun.COM */ 18959126SWyllys.Ingersoll@Sun.COM static inline void 18969126SWyllys.Ingersoll@Sun.COM tpm_unlock(tpm_state_t *tpm) 18979126SWyllys.Ingersoll@Sun.COM { 18989126SWyllys.Ingersoll@Sun.COM /* Wake up the waiting threads */ 18999126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->iobuf_lock); 19009126SWyllys.Ingersoll@Sun.COM ASSERT(tpm->iobuf_inuse == 1 && mutex_owned(&tpm->iobuf_lock)); 19019126SWyllys.Ingersoll@Sun.COM tpm->iobuf_inuse = 0; 19029126SWyllys.Ingersoll@Sun.COM cv_broadcast(&tpm->iobuf_cv); 19039126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->iobuf_lock); 19049126SWyllys.Ingersoll@Sun.COM } 1905*10346Swyllys.ingersoll@sun.com 1906*10346Swyllys.ingersoll@sun.com #ifdef KCF_TPM_RNG_PROVIDER 1907*10346Swyllys.ingersoll@sun.com /* 1908*10346Swyllys.ingersoll@sun.com * Random number generator entry points 1909*10346Swyllys.ingersoll@sun.com */ 1910*10346Swyllys.ingersoll@sun.com static void 1911*10346Swyllys.ingersoll@sun.com strncpy_spacepad(uchar_t *s1, char *s2, int n) 1912*10346Swyllys.ingersoll@sun.com { 1913*10346Swyllys.ingersoll@sun.com int s2len = strlen(s2); 1914*10346Swyllys.ingersoll@sun.com (void) strncpy((char *)s1, s2, n); 1915*10346Swyllys.ingersoll@sun.com if (s2len < n) 1916*10346Swyllys.ingersoll@sun.com (void) memset(s1 + s2len, ' ', n - s2len); 1917*10346Swyllys.ingersoll@sun.com } 1918*10346Swyllys.ingersoll@sun.com 1919*10346Swyllys.ingersoll@sun.com /*ARGSUSED*/ 1920*10346Swyllys.ingersoll@sun.com static int 1921*10346Swyllys.ingersoll@sun.com tpmrng_ext_info(crypto_provider_handle_t prov, 1922*10346Swyllys.ingersoll@sun.com crypto_provider_ext_info_t *ext_info, 1923*10346Swyllys.ingersoll@sun.com crypto_req_handle_t cfreq) 1924*10346Swyllys.ingersoll@sun.com { 1925*10346Swyllys.ingersoll@sun.com tpm_state_t *tpm = (tpm_state_t *)prov; 1926*10346Swyllys.ingersoll@sun.com char buf[64]; 1927*10346Swyllys.ingersoll@sun.com 1928*10346Swyllys.ingersoll@sun.com if (tpm == NULL) 1929*10346Swyllys.ingersoll@sun.com return (DDI_FAILURE); 1930*10346Swyllys.ingersoll@sun.com 1931*10346Swyllys.ingersoll@sun.com strncpy_spacepad(ext_info->ei_manufacturerID, 1932*10346Swyllys.ingersoll@sun.com (char *)tpm->vers_info.tpmVendorID, 1933*10346Swyllys.ingersoll@sun.com sizeof (ext_info->ei_manufacturerID)); 1934*10346Swyllys.ingersoll@sun.com 1935*10346Swyllys.ingersoll@sun.com strncpy_spacepad(ext_info->ei_model, "0", 1936*10346Swyllys.ingersoll@sun.com sizeof (ext_info->ei_model)); 1937*10346Swyllys.ingersoll@sun.com strncpy_spacepad(ext_info->ei_serial_number, "0", 1938*10346Swyllys.ingersoll@sun.com sizeof (ext_info->ei_serial_number)); 1939*10346Swyllys.ingersoll@sun.com 1940*10346Swyllys.ingersoll@sun.com ext_info->ei_flags = CRYPTO_EXTF_RNG | CRYPTO_EXTF_SO_PIN_LOCKED; 1941*10346Swyllys.ingersoll@sun.com ext_info->ei_max_session_count = CRYPTO_EFFECTIVELY_INFINITE; 1942*10346Swyllys.ingersoll@sun.com ext_info->ei_max_pin_len = 0; 1943*10346Swyllys.ingersoll@sun.com ext_info->ei_min_pin_len = 0; 1944*10346Swyllys.ingersoll@sun.com ext_info->ei_total_public_memory = CRYPTO_UNAVAILABLE_INFO; 1945*10346Swyllys.ingersoll@sun.com ext_info->ei_free_public_memory = CRYPTO_UNAVAILABLE_INFO; 1946*10346Swyllys.ingersoll@sun.com ext_info->ei_total_private_memory = CRYPTO_UNAVAILABLE_INFO; 1947*10346Swyllys.ingersoll@sun.com ext_info->ei_free_public_memory = CRYPTO_UNAVAILABLE_INFO; 1948*10346Swyllys.ingersoll@sun.com ext_info->ei_time[0] = 0; 1949*10346Swyllys.ingersoll@sun.com 1950*10346Swyllys.ingersoll@sun.com ext_info->ei_hardware_version.cv_major = tpm->vers_info.version.major; 1951*10346Swyllys.ingersoll@sun.com ext_info->ei_hardware_version.cv_minor = tpm->vers_info.version.minor; 1952*10346Swyllys.ingersoll@sun.com ext_info->ei_firmware_version.cv_major = 1953*10346Swyllys.ingersoll@sun.com tpm->vers_info.version.revMajor; 1954*10346Swyllys.ingersoll@sun.com ext_info->ei_firmware_version.cv_minor = 1955*10346Swyllys.ingersoll@sun.com tpm->vers_info.version.revMinor; 1956*10346Swyllys.ingersoll@sun.com 1957*10346Swyllys.ingersoll@sun.com (void) snprintf(buf, sizeof (buf), "tpmrng TPM RNG"); 1958*10346Swyllys.ingersoll@sun.com 1959*10346Swyllys.ingersoll@sun.com strncpy_spacepad(ext_info->ei_label, buf, 1960*10346Swyllys.ingersoll@sun.com sizeof (ext_info->ei_label)); 1961*10346Swyllys.ingersoll@sun.com #undef BUFSZ 1962*10346Swyllys.ingersoll@sun.com return (CRYPTO_SUCCESS); 1963*10346Swyllys.ingersoll@sun.com 1964*10346Swyllys.ingersoll@sun.com } 1965*10346Swyllys.ingersoll@sun.com 1966*10346Swyllys.ingersoll@sun.com static int 1967*10346Swyllys.ingersoll@sun.com tpmrng_register(tpm_state_t *tpm) 1968*10346Swyllys.ingersoll@sun.com { 1969*10346Swyllys.ingersoll@sun.com int ret; 1970*10346Swyllys.ingersoll@sun.com char ID[64]; 1971*10346Swyllys.ingersoll@sun.com crypto_mech_name_t *rngmech; 1972*10346Swyllys.ingersoll@sun.com 1973*10346Swyllys.ingersoll@sun.com ASSERT(tpm != NULL); 1974*10346Swyllys.ingersoll@sun.com 1975*10346Swyllys.ingersoll@sun.com (void) snprintf(ID, sizeof (ID), "tpmrng %s", IDENT_TPMRNG); 1976*10346Swyllys.ingersoll@sun.com 1977*10346Swyllys.ingersoll@sun.com tpmrng_prov_info.pi_provider_description = ID; 1978*10346Swyllys.ingersoll@sun.com tpmrng_prov_info.pi_provider_dev.pd_hw = tpm->dip; 1979*10346Swyllys.ingersoll@sun.com tpmrng_prov_info.pi_provider_handle = tpm; 1980*10346Swyllys.ingersoll@sun.com 1981*10346Swyllys.ingersoll@sun.com ret = crypto_register_provider(&tpmrng_prov_info, &tpm->n_prov); 1982*10346Swyllys.ingersoll@sun.com if (ret != CRYPTO_SUCCESS) { 1983*10346Swyllys.ingersoll@sun.com tpm->n_prov = NULL; 1984*10346Swyllys.ingersoll@sun.com return (DDI_FAILURE); 1985*10346Swyllys.ingersoll@sun.com } 1986*10346Swyllys.ingersoll@sun.com 1987*10346Swyllys.ingersoll@sun.com crypto_provider_notification(tpm->n_prov, CRYPTO_PROVIDER_READY); 1988*10346Swyllys.ingersoll@sun.com 1989*10346Swyllys.ingersoll@sun.com rngmech = kmem_zalloc(strlen("random") + 1, KM_SLEEP); 1990*10346Swyllys.ingersoll@sun.com (void) memcpy(rngmech, "random", 6); 1991*10346Swyllys.ingersoll@sun.com ret = crypto_load_dev_disabled("tpm", ddi_get_instance(tpm->dip), 1992*10346Swyllys.ingersoll@sun.com 1, rngmech); 1993*10346Swyllys.ingersoll@sun.com if (ret != CRYPTO_SUCCESS) { 1994*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, "crypto_load_dev_disabled failed (%d)", ret); 1995*10346Swyllys.ingersoll@sun.com } 1996*10346Swyllys.ingersoll@sun.com return (DDI_SUCCESS); 1997*10346Swyllys.ingersoll@sun.com } 1998*10346Swyllys.ingersoll@sun.com 1999*10346Swyllys.ingersoll@sun.com static int 2000*10346Swyllys.ingersoll@sun.com tpmrng_unregister(tpm_state_t *tpm) 2001*10346Swyllys.ingersoll@sun.com { 2002*10346Swyllys.ingersoll@sun.com int ret; 2003*10346Swyllys.ingersoll@sun.com ASSERT(tpm != NULL); 2004*10346Swyllys.ingersoll@sun.com if (tpm->n_prov) { 2005*10346Swyllys.ingersoll@sun.com ret = crypto_unregister_provider(tpm->n_prov); 2006*10346Swyllys.ingersoll@sun.com tpm->n_prov = NULL; 2007*10346Swyllys.ingersoll@sun.com if (ret != CRYPTO_SUCCESS) 2008*10346Swyllys.ingersoll@sun.com return (DDI_FAILURE); 2009*10346Swyllys.ingersoll@sun.com } 2010*10346Swyllys.ingersoll@sun.com return (DDI_SUCCESS); 2011*10346Swyllys.ingersoll@sun.com } 2012*10346Swyllys.ingersoll@sun.com 2013*10346Swyllys.ingersoll@sun.com /*ARGSUSED*/ 2014*10346Swyllys.ingersoll@sun.com static void 2015*10346Swyllys.ingersoll@sun.com tpmrng_provider_status(crypto_provider_handle_t provider, uint_t *status) 2016*10346Swyllys.ingersoll@sun.com { 2017*10346Swyllys.ingersoll@sun.com *status = CRYPTO_PROVIDER_READY; 2018*10346Swyllys.ingersoll@sun.com } 2019*10346Swyllys.ingersoll@sun.com 2020*10346Swyllys.ingersoll@sun.com /*ARGSUSED*/ 2021*10346Swyllys.ingersoll@sun.com static int 2022*10346Swyllys.ingersoll@sun.com tpmrng_seed_random(crypto_provider_handle_t provider, crypto_session_id_t sid, 2023*10346Swyllys.ingersoll@sun.com uchar_t *buf, size_t len, uint_t entropy_est, uint32_t flags, 2024*10346Swyllys.ingersoll@sun.com crypto_req_handle_t req) 2025*10346Swyllys.ingersoll@sun.com { 2026*10346Swyllys.ingersoll@sun.com int ret; 2027*10346Swyllys.ingersoll@sun.com tpm_state_t *tpm; 2028*10346Swyllys.ingersoll@sun.com uint32_t len32; 2029*10346Swyllys.ingersoll@sun.com /* Max length of seed is 256 bytes, add 14 for header. */ 2030*10346Swyllys.ingersoll@sun.com uint8_t cmdbuf[270] = { 2031*10346Swyllys.ingersoll@sun.com 0, 193, /* TPM_TAG_RQU COMMAND */ 2032*10346Swyllys.ingersoll@sun.com 0, 0, 0, 0x0A, /* paramsize in bytes */ 2033*10346Swyllys.ingersoll@sun.com 0, 0, 0, TPM_ORD_StirRandom, 2034*10346Swyllys.ingersoll@sun.com 0, 0, 0, 0 /* number of input bytes (< 256) */ 2035*10346Swyllys.ingersoll@sun.com }; 2036*10346Swyllys.ingersoll@sun.com uint32_t buflen; 2037*10346Swyllys.ingersoll@sun.com 2038*10346Swyllys.ingersoll@sun.com if (len == 0 || len > 255 || buf == NULL) 2039*10346Swyllys.ingersoll@sun.com return (CRYPTO_ARGUMENTS_BAD); 2040*10346Swyllys.ingersoll@sun.com 2041*10346Swyllys.ingersoll@sun.com tpm = (tpm_state_t *)provider; 2042*10346Swyllys.ingersoll@sun.com if (tpm == NULL) 2043*10346Swyllys.ingersoll@sun.com return (CRYPTO_INVALID_CONTEXT); 2044*10346Swyllys.ingersoll@sun.com 2045*10346Swyllys.ingersoll@sun.com /* Acquire lock for exclusive use of TPM */ 2046*10346Swyllys.ingersoll@sun.com TPM_EXCLUSIVE_LOCK(tpm); 2047*10346Swyllys.ingersoll@sun.com 2048*10346Swyllys.ingersoll@sun.com ret = tpm_io_lock(tpm); 2049*10346Swyllys.ingersoll@sun.com /* Timeout reached */ 2050*10346Swyllys.ingersoll@sun.com if (ret) 2051*10346Swyllys.ingersoll@sun.com return (CRYPTO_BUSY); 2052*10346Swyllys.ingersoll@sun.com 2053*10346Swyllys.ingersoll@sun.com /* TPM only handles 32 bit length, so truncate if too big. */ 2054*10346Swyllys.ingersoll@sun.com len32 = (uint32_t)len; 2055*10346Swyllys.ingersoll@sun.com buflen = len32 + 14; 2056*10346Swyllys.ingersoll@sun.com 2057*10346Swyllys.ingersoll@sun.com /* The length must be in network order */ 2058*10346Swyllys.ingersoll@sun.com buflen = htonl(buflen); 2059*10346Swyllys.ingersoll@sun.com bcopy(&buflen, cmdbuf + 2, sizeof (uint32_t)); 2060*10346Swyllys.ingersoll@sun.com 2061*10346Swyllys.ingersoll@sun.com /* Convert it back */ 2062*10346Swyllys.ingersoll@sun.com buflen = ntohl(buflen); 2063*10346Swyllys.ingersoll@sun.com 2064*10346Swyllys.ingersoll@sun.com /* length must be in network order */ 2065*10346Swyllys.ingersoll@sun.com len32 = htonl(len32); 2066*10346Swyllys.ingersoll@sun.com bcopy(&len32, cmdbuf + 10, sizeof (uint32_t)); 2067*10346Swyllys.ingersoll@sun.com 2068*10346Swyllys.ingersoll@sun.com /* convert it back */ 2069*10346Swyllys.ingersoll@sun.com len32 = ntohl(len32); 2070*10346Swyllys.ingersoll@sun.com 2071*10346Swyllys.ingersoll@sun.com bcopy(buf, cmdbuf + 14, len32); 2072*10346Swyllys.ingersoll@sun.com 2073*10346Swyllys.ingersoll@sun.com ret = itpm_command(tpm, cmdbuf, buflen); 2074*10346Swyllys.ingersoll@sun.com tpm_unlock(tpm); 2075*10346Swyllys.ingersoll@sun.com 2076*10346Swyllys.ingersoll@sun.com if (ret != DDI_SUCCESS) { 2077*10346Swyllys.ingersoll@sun.com #ifdef DEBUG 2078*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, "tpmrng_seed_random failed"); 2079*10346Swyllys.ingersoll@sun.com #endif 2080*10346Swyllys.ingersoll@sun.com return (CRYPTO_FAILED); 2081*10346Swyllys.ingersoll@sun.com } 2082*10346Swyllys.ingersoll@sun.com 2083*10346Swyllys.ingersoll@sun.com return (CRYPTO_SUCCESS); 2084*10346Swyllys.ingersoll@sun.com } 2085*10346Swyllys.ingersoll@sun.com 2086*10346Swyllys.ingersoll@sun.com /* ARGSUSED */ 2087*10346Swyllys.ingersoll@sun.com static int 2088*10346Swyllys.ingersoll@sun.com tpmrng_generate_random(crypto_provider_handle_t provider, 2089*10346Swyllys.ingersoll@sun.com crypto_session_id_t sid, uchar_t *buf, size_t len, crypto_req_handle_t req) 2090*10346Swyllys.ingersoll@sun.com { 2091*10346Swyllys.ingersoll@sun.com int ret; 2092*10346Swyllys.ingersoll@sun.com tpm_state_t *tpm; 2093*10346Swyllys.ingersoll@sun.com uint8_t hdr[14] = { 2094*10346Swyllys.ingersoll@sun.com 0, 193, /* TPM_TAG_RQU COMMAND */ 2095*10346Swyllys.ingersoll@sun.com 0, 0, 0, 14, /* paramsize in bytes */ 2096*10346Swyllys.ingersoll@sun.com 0, 0, 0, TPM_ORD_GetRandom, 2097*10346Swyllys.ingersoll@sun.com 0, 0, 0, 0 2098*10346Swyllys.ingersoll@sun.com }; 2099*10346Swyllys.ingersoll@sun.com uint8_t *cmdbuf = NULL; 2100*10346Swyllys.ingersoll@sun.com uint32_t len32 = (uint32_t)len; 2101*10346Swyllys.ingersoll@sun.com uint32_t buflen = len32 + sizeof (hdr); 2102*10346Swyllys.ingersoll@sun.com 2103*10346Swyllys.ingersoll@sun.com if (len == 0 || buf == NULL) 2104*10346Swyllys.ingersoll@sun.com return (CRYPTO_ARGUMENTS_BAD); 2105*10346Swyllys.ingersoll@sun.com 2106*10346Swyllys.ingersoll@sun.com tpm = (tpm_state_t *)provider; 2107*10346Swyllys.ingersoll@sun.com if (tpm == NULL) 2108*10346Swyllys.ingersoll@sun.com return (CRYPTO_INVALID_CONTEXT); 2109*10346Swyllys.ingersoll@sun.com 2110*10346Swyllys.ingersoll@sun.com TPM_EXCLUSIVE_LOCK(tpm); 2111*10346Swyllys.ingersoll@sun.com 2112*10346Swyllys.ingersoll@sun.com ret = tpm_io_lock(tpm); 2113*10346Swyllys.ingersoll@sun.com /* Timeout reached */ 2114*10346Swyllys.ingersoll@sun.com if (ret) 2115*10346Swyllys.ingersoll@sun.com return (CRYPTO_BUSY); 2116*10346Swyllys.ingersoll@sun.com 2117*10346Swyllys.ingersoll@sun.com cmdbuf = kmem_zalloc(buflen, KM_SLEEP); 2118*10346Swyllys.ingersoll@sun.com bcopy(hdr, cmdbuf, sizeof (hdr)); 2119*10346Swyllys.ingersoll@sun.com 2120*10346Swyllys.ingersoll@sun.com /* Length is written in network byte order */ 2121*10346Swyllys.ingersoll@sun.com len32 = htonl(len32); 2122*10346Swyllys.ingersoll@sun.com bcopy(&len32, cmdbuf + 10, sizeof (uint32_t)); 2123*10346Swyllys.ingersoll@sun.com 2124*10346Swyllys.ingersoll@sun.com ret = itpm_command(tpm, cmdbuf, buflen); 2125*10346Swyllys.ingersoll@sun.com if (ret != DDI_SUCCESS) { 2126*10346Swyllys.ingersoll@sun.com #ifdef DEBUG 2127*10346Swyllys.ingersoll@sun.com cmn_err(CE_WARN, "tpmrng_generate_random failed"); 2128*10346Swyllys.ingersoll@sun.com #endif 2129*10346Swyllys.ingersoll@sun.com kmem_free(cmdbuf, buflen); 2130*10346Swyllys.ingersoll@sun.com tpm_unlock(tpm); 2131*10346Swyllys.ingersoll@sun.com return (CRYPTO_FAILED); 2132*10346Swyllys.ingersoll@sun.com } 2133*10346Swyllys.ingersoll@sun.com 2134*10346Swyllys.ingersoll@sun.com /* Find out how many bytes were really returned */ 2135*10346Swyllys.ingersoll@sun.com len32 = load32(cmdbuf, 10); 2136*10346Swyllys.ingersoll@sun.com 2137*10346Swyllys.ingersoll@sun.com /* Copy the random bytes back to the callers buffer */ 2138*10346Swyllys.ingersoll@sun.com bcopy(cmdbuf + 14, buf, len32); 2139*10346Swyllys.ingersoll@sun.com 2140*10346Swyllys.ingersoll@sun.com kmem_free(cmdbuf, buflen); 2141*10346Swyllys.ingersoll@sun.com tpm_unlock(tpm); 2142*10346Swyllys.ingersoll@sun.com 2143*10346Swyllys.ingersoll@sun.com return (CRYPTO_SUCCESS); 2144*10346Swyllys.ingersoll@sun.com } 2145*10346Swyllys.ingersoll@sun.com #endif /* KCF_TPM_RNG_PROVIDER */ 2146