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 489126SWyllys.Ingersoll@Sun.COM #include <tss/platform.h> /* from SUNWtss */ 499126SWyllys.Ingersoll@Sun.COM #include <tss/tpm.h> /* from SUNWtss */ 509126SWyllys.Ingersoll@Sun.COM 519126SWyllys.Ingersoll@Sun.COM #include "tpm_tis.h" 529126SWyllys.Ingersoll@Sun.COM #include "tpm_ddi.h" 539126SWyllys.Ingersoll@Sun.COM #include "tpm_duration.h" 549126SWyllys.Ingersoll@Sun.COM 559126SWyllys.Ingersoll@Sun.COM #define TPM_HEADER_SIZE 10 569126SWyllys.Ingersoll@Sun.COM typedef enum { 579126SWyllys.Ingersoll@Sun.COM TPM_TAG_OFFSET = 0, 589126SWyllys.Ingersoll@Sun.COM TPM_PARAMSIZE_OFFSET = 2, 599126SWyllys.Ingersoll@Sun.COM TPM_RETURN_OFFSET = 6, 609126SWyllys.Ingersoll@Sun.COM TPM_COMMAND_CODE_OFFSET = 6, 619126SWyllys.Ingersoll@Sun.COM } TPM_HEADER_OFFSET_T; 629126SWyllys.Ingersoll@Sun.COM 639126SWyllys.Ingersoll@Sun.COM /* 649126SWyllys.Ingersoll@Sun.COM * This is to address some TPMs that does not report the correct duration 659126SWyllys.Ingersoll@Sun.COM * and timeouts. In our experience with the production TPMs, we encountered 669126SWyllys.Ingersoll@Sun.COM * time errors such as GetCapability command from TPM reporting the timeout 679126SWyllys.Ingersoll@Sun.COM * and durations in milliseconds rather than microseconds. Some other TPMs 689126SWyllys.Ingersoll@Sun.COM * report the value 0's 699126SWyllys.Ingersoll@Sun.COM * 709126SWyllys.Ingersoll@Sun.COM * Short Duration is based on section 11.3.4 of TIS speciciation, that 719126SWyllys.Ingersoll@Sun.COM * TPM_GetCapability (short duration) commands should not be longer than 750ms 729126SWyllys.Ingersoll@Sun.COM * and that section 11.3.7 states that TPM_ContinueSelfTest (medium duration) 739126SWyllys.Ingersoll@Sun.COM * should not be longer than 1 second. 749126SWyllys.Ingersoll@Sun.COM */ 759126SWyllys.Ingersoll@Sun.COM #define DEFAULT_SHORT_DURATION 750000 769126SWyllys.Ingersoll@Sun.COM #define DEFAULT_MEDIUM_DURATION 1000000 779126SWyllys.Ingersoll@Sun.COM #define DEFAULT_LONG_DURATION 300000000 789126SWyllys.Ingersoll@Sun.COM #define DEFAULT_TIMEOUT_A 750000 799126SWyllys.Ingersoll@Sun.COM #define DEFAULT_TIMEOUT_B 2000000 809126SWyllys.Ingersoll@Sun.COM #define DEFAULT_TIMEOUT_C 750000 819126SWyllys.Ingersoll@Sun.COM #define DEFAULT_TIMEOUT_D 750000 829126SWyllys.Ingersoll@Sun.COM 839126SWyllys.Ingersoll@Sun.COM /* 849126SWyllys.Ingersoll@Sun.COM * In order to test the 'millisecond bug', we test if DURATIONS and TIMEOUTS 859126SWyllys.Ingersoll@Sun.COM * are unreasonably low...such as 10 milliseconds (TPM isn't that fast). 869126SWyllys.Ingersoll@Sun.COM * and 400 milliseconds for long duration 879126SWyllys.Ingersoll@Sun.COM */ 889126SWyllys.Ingersoll@Sun.COM #define TEN_MILLISECONDS 10000 /* 10 milliseconds */ 899126SWyllys.Ingersoll@Sun.COM #define FOUR_HUNDRED_MILLISECONDS 400000 /* 4 hundred milliseconds */ 909126SWyllys.Ingersoll@Sun.COM 919126SWyllys.Ingersoll@Sun.COM /* 929126SWyllys.Ingersoll@Sun.COM * TPM input/output buffer offsets 939126SWyllys.Ingersoll@Sun.COM */ 949126SWyllys.Ingersoll@Sun.COM 959126SWyllys.Ingersoll@Sun.COM typedef enum { 969126SWyllys.Ingersoll@Sun.COM TPM_CAP_RESPSIZE_OFFSET = 10, 979126SWyllys.Ingersoll@Sun.COM TPM_CAP_RESP_OFFSET = 14, 989126SWyllys.Ingersoll@Sun.COM } TPM_CAP_RET_OFFSET_T; 999126SWyllys.Ingersoll@Sun.COM 1009126SWyllys.Ingersoll@Sun.COM typedef enum { 1019126SWyllys.Ingersoll@Sun.COM TPM_CAP_TIMEOUT_A_OFFSET = 14, 1029126SWyllys.Ingersoll@Sun.COM TPM_CAP_TIMEOUT_B_OFFSET = 18, 1039126SWyllys.Ingersoll@Sun.COM TPM_CAP_TIMEOUT_C_OFFSET = 22, 1049126SWyllys.Ingersoll@Sun.COM TPM_CAP_TIMEOUT_D_OFFSET = 26, 1059126SWyllys.Ingersoll@Sun.COM } TPM_CAP_TIMEOUT_OFFSET_T; 1069126SWyllys.Ingersoll@Sun.COM 1079126SWyllys.Ingersoll@Sun.COM typedef enum { 1089126SWyllys.Ingersoll@Sun.COM TPM_CAP_DUR_SHORT_OFFSET = 14, 1099126SWyllys.Ingersoll@Sun.COM TPM_CAP_DUR_MEDIUM_OFFSET = 18, 1109126SWyllys.Ingersoll@Sun.COM TPM_CAP_DUR_LONG_OFFSET = 22, 1119126SWyllys.Ingersoll@Sun.COM } TPM_CAP_DURATION_OFFSET_T; 1129126SWyllys.Ingersoll@Sun.COM 1139126SWyllys.Ingersoll@Sun.COM #define TPM_CAP_VERSION_INFO_OFFSET 14 1149126SWyllys.Ingersoll@Sun.COM #define TPM_CAP_VERSION_INFO_SIZE 15 1159126SWyllys.Ingersoll@Sun.COM 1169126SWyllys.Ingersoll@Sun.COM /* 1179126SWyllys.Ingersoll@Sun.COM * Internal TPM command functions 1189126SWyllys.Ingersoll@Sun.COM */ 1199126SWyllys.Ingersoll@Sun.COM static int itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz); 1209126SWyllys.Ingersoll@Sun.COM static int tpm_get_timeouts(tpm_state_t *tpm); 1219126SWyllys.Ingersoll@Sun.COM static int tpm_get_duration(tpm_state_t *tpm); 1229126SWyllys.Ingersoll@Sun.COM static int tpm_get_version(tpm_state_t *tpm); 1239126SWyllys.Ingersoll@Sun.COM static int tpm_continue_selftest(tpm_state_t *tpm); 1249126SWyllys.Ingersoll@Sun.COM 1259126SWyllys.Ingersoll@Sun.COM /* 1269126SWyllys.Ingersoll@Sun.COM * Internal TIS related functions 1279126SWyllys.Ingersoll@Sun.COM */ 1289126SWyllys.Ingersoll@Sun.COM static int tpm_wait_for_stat(tpm_state_t *, uint8_t, clock_t); 1299126SWyllys.Ingersoll@Sun.COM static clock_t tpm_get_ordinal_duration(tpm_state_t *, uint8_t); 1309126SWyllys.Ingersoll@Sun.COM static int tis_check_active_locality(tpm_state_t *, char); 1319126SWyllys.Ingersoll@Sun.COM static int tis_request_locality(tpm_state_t *, char); 1329126SWyllys.Ingersoll@Sun.COM static void tis_release_locality(tpm_state_t *, char, int); 1339126SWyllys.Ingersoll@Sun.COM static int tis_init(tpm_state_t *); 1349126SWyllys.Ingersoll@Sun.COM static uint8_t tis_get_status(tpm_state_t *); 1359126SWyllys.Ingersoll@Sun.COM static int tis_send_data(tpm_state_t *, uint8_t *, size_t); 1369126SWyllys.Ingersoll@Sun.COM static int tis_recv_data(tpm_state_t *, uint8_t *, size_t); 1379126SWyllys.Ingersoll@Sun.COM 1389126SWyllys.Ingersoll@Sun.COM /* Auxilliary */ 1399126SWyllys.Ingersoll@Sun.COM static int receive_data(tpm_state_t *, uint8_t *, size_t); 1409126SWyllys.Ingersoll@Sun.COM static inline int tpm_lock(tpm_state_t *); 1419126SWyllys.Ingersoll@Sun.COM static inline void tpm_unlock(tpm_state_t *); 1429126SWyllys.Ingersoll@Sun.COM static void tpm_cleanup(dev_info_t *, tpm_state_t *); 1439126SWyllys.Ingersoll@Sun.COM 1449126SWyllys.Ingersoll@Sun.COM /* 1459126SWyllys.Ingersoll@Sun.COM * Sun DDI/DDK entry points 1469126SWyllys.Ingersoll@Sun.COM */ 1479126SWyllys.Ingersoll@Sun.COM 1489126SWyllys.Ingersoll@Sun.COM /* Declaration of autoconfig functions */ 1499126SWyllys.Ingersoll@Sun.COM static int tpm_attach(dev_info_t *, ddi_attach_cmd_t); 1509126SWyllys.Ingersoll@Sun.COM static int tpm_detach(dev_info_t *, ddi_detach_cmd_t); 1519126SWyllys.Ingersoll@Sun.COM static int tpm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 1529126SWyllys.Ingersoll@Sun.COM static int tpm_quiesce(dev_info_t *); 1539126SWyllys.Ingersoll@Sun.COM /* End of autoconfig functions */ 1549126SWyllys.Ingersoll@Sun.COM 1559126SWyllys.Ingersoll@Sun.COM /* Declaration of driver entry point functions */ 1569126SWyllys.Ingersoll@Sun.COM static int tpm_open(dev_t *, int, int, cred_t *); 1579126SWyllys.Ingersoll@Sun.COM static int tpm_close(dev_t, int, int, cred_t *); 1589126SWyllys.Ingersoll@Sun.COM static int tpm_read(dev_t, struct uio *, cred_t *); 1599126SWyllys.Ingersoll@Sun.COM static int tpm_write(dev_t, struct uio *, cred_t *); 1609126SWyllys.Ingersoll@Sun.COM /* End of driver entry point functions */ 1619126SWyllys.Ingersoll@Sun.COM 1629126SWyllys.Ingersoll@Sun.COM /* cb_ops structure */ 1639126SWyllys.Ingersoll@Sun.COM static struct cb_ops tpm_cb_ops = { 1649126SWyllys.Ingersoll@Sun.COM tpm_open, 1659126SWyllys.Ingersoll@Sun.COM tpm_close, 1669126SWyllys.Ingersoll@Sun.COM nodev, /* no strategy - nodev returns ENXIO */ 1679126SWyllys.Ingersoll@Sun.COM nodev, /* no print */ 1689126SWyllys.Ingersoll@Sun.COM nodev, /* no dump */ 1699126SWyllys.Ingersoll@Sun.COM tpm_read, 1709126SWyllys.Ingersoll@Sun.COM tpm_write, 1719126SWyllys.Ingersoll@Sun.COM nodev, /* no ioctl */ 1729126SWyllys.Ingersoll@Sun.COM nodev, /* no devmap */ 1739126SWyllys.Ingersoll@Sun.COM nodev, /* no mmap */ 1749126SWyllys.Ingersoll@Sun.COM nodev, /* no segmap */ 1759126SWyllys.Ingersoll@Sun.COM nochpoll, /* returns ENXIO for non-pollable devices */ 1769126SWyllys.Ingersoll@Sun.COM ddi_prop_op, 1779126SWyllys.Ingersoll@Sun.COM NULL, /* streamtab struc */ 1789126SWyllys.Ingersoll@Sun.COM D_MP, /* compatibility flags */ 1799126SWyllys.Ingersoll@Sun.COM CB_REV, /* cb_ops revision number */ 1809126SWyllys.Ingersoll@Sun.COM nodev, /* no aread */ 1819126SWyllys.Ingersoll@Sun.COM nodev /* no awrite */ 1829126SWyllys.Ingersoll@Sun.COM }; 1839126SWyllys.Ingersoll@Sun.COM 1849126SWyllys.Ingersoll@Sun.COM /* dev_ops structure */ 1859126SWyllys.Ingersoll@Sun.COM static struct dev_ops tpm_dev_ops = { 1869126SWyllys.Ingersoll@Sun.COM DEVO_REV, 1879126SWyllys.Ingersoll@Sun.COM 0, /* reference count */ 1889126SWyllys.Ingersoll@Sun.COM tpm_getinfo, 1899126SWyllys.Ingersoll@Sun.COM nulldev, /* no identify - nulldev returns 0 */ 1909126SWyllys.Ingersoll@Sun.COM nulldev, 1919126SWyllys.Ingersoll@Sun.COM tpm_attach, 1929126SWyllys.Ingersoll@Sun.COM tpm_detach, 1939126SWyllys.Ingersoll@Sun.COM nodev, /* no reset - nodev returns ENXIO */ 1949126SWyllys.Ingersoll@Sun.COM &tpm_cb_ops, 1959126SWyllys.Ingersoll@Sun.COM (struct bus_ops *)NULL, 1969126SWyllys.Ingersoll@Sun.COM nodev, /* no power */ 1979126SWyllys.Ingersoll@Sun.COM tpm_quiesce 1989126SWyllys.Ingersoll@Sun.COM }; 1999126SWyllys.Ingersoll@Sun.COM 2009126SWyllys.Ingersoll@Sun.COM /* modldrv structure */ 2019126SWyllys.Ingersoll@Sun.COM static struct modldrv modldrv = { 2029126SWyllys.Ingersoll@Sun.COM &mod_driverops, /* Type: This is a driver */ 2039126SWyllys.Ingersoll@Sun.COM "TPM 1.2 driver", /* Name of the module. */ 2049126SWyllys.Ingersoll@Sun.COM &tpm_dev_ops 2059126SWyllys.Ingersoll@Sun.COM }; 2069126SWyllys.Ingersoll@Sun.COM 2079126SWyllys.Ingersoll@Sun.COM /* modlinkage structure */ 2089126SWyllys.Ingersoll@Sun.COM static struct modlinkage tpm_ml = { 2099126SWyllys.Ingersoll@Sun.COM MODREV_1, 2109126SWyllys.Ingersoll@Sun.COM &modldrv, 2119126SWyllys.Ingersoll@Sun.COM NULL 2129126SWyllys.Ingersoll@Sun.COM }; 2139126SWyllys.Ingersoll@Sun.COM 2149126SWyllys.Ingersoll@Sun.COM static void *statep = NULL; 2159126SWyllys.Ingersoll@Sun.COM 2169126SWyllys.Ingersoll@Sun.COM /* 2179126SWyllys.Ingersoll@Sun.COM * TPM commands to get the TPM's properties, e.g.,timeout 2189126SWyllys.Ingersoll@Sun.COM */ 2199126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 2209126SWyllys.Ingersoll@Sun.COM static int 2219126SWyllys.Ingersoll@Sun.COM tpm_quiesce(dev_info_t *dip) 2229126SWyllys.Ingersoll@Sun.COM { 2239126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 2249126SWyllys.Ingersoll@Sun.COM } 2259126SWyllys.Ingersoll@Sun.COM 2269126SWyllys.Ingersoll@Sun.COM static uint32_t 2279126SWyllys.Ingersoll@Sun.COM load32(uchar_t *ptr, uint32_t offset) 2289126SWyllys.Ingersoll@Sun.COM { 2299126SWyllys.Ingersoll@Sun.COM uint32_t val; 2309126SWyllys.Ingersoll@Sun.COM bcopy(ptr + offset, &val, sizeof (uint32_t)); 2319126SWyllys.Ingersoll@Sun.COM 2329126SWyllys.Ingersoll@Sun.COM return (ntohl(val)); 2339126SWyllys.Ingersoll@Sun.COM } 2349126SWyllys.Ingersoll@Sun.COM 2359126SWyllys.Ingersoll@Sun.COM /* 2369126SWyllys.Ingersoll@Sun.COM * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability 2379126SWyllys.Ingersoll@Sun.COM * with the subcommand TPM_CAP_PROP_TIS_TIMEOUT 2389126SWyllys.Ingersoll@Sun.COM * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38) 2399126SWyllys.Ingersoll@Sun.COM */ 2409126SWyllys.Ingersoll@Sun.COM static int 2419126SWyllys.Ingersoll@Sun.COM tpm_get_timeouts(tpm_state_t *tpm) 2429126SWyllys.Ingersoll@Sun.COM { 2439126SWyllys.Ingersoll@Sun.COM int ret; 2449126SWyllys.Ingersoll@Sun.COM uint32_t timeout; /* in milliseconds */ 2459126SWyllys.Ingersoll@Sun.COM uint32_t len; 2469126SWyllys.Ingersoll@Sun.COM 2479126SWyllys.Ingersoll@Sun.COM /* The buffer size (30) needs room for 4 timeout values (uint32_t) */ 2489126SWyllys.Ingersoll@Sun.COM uint8_t buf[30] = { 2499126SWyllys.Ingersoll@Sun.COM 0, 193, /* TPM_TAG_RQU_COMMAND */ 2509126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 22, /* paramsize in bytes */ 2519126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 101, /* TPM_ORD_GetCapability */ 2529126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 5, /* TPM_CAP_Prop */ 2539126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 4, /* SUB_CAP size in bytes */ 2549126SWyllys.Ingersoll@Sun.COM 0, 0, 1, 21 /* TPM_CAP_PROP_TIS_TIMEOUT(0x115) */ 2559126SWyllys.Ingersoll@Sun.COM }; 2569126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_get_timeout"; 2579126SWyllys.Ingersoll@Sun.COM 2589126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 2599126SWyllys.Ingersoll@Sun.COM 2609126SWyllys.Ingersoll@Sun.COM ret = itpm_command(tpm, buf, sizeof (buf)); 2619126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 2629126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: itpm_command failed", myname); 2639126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 2649126SWyllys.Ingersoll@Sun.COM } 2659126SWyllys.Ingersoll@Sun.COM 2669126SWyllys.Ingersoll@Sun.COM /* 2679126SWyllys.Ingersoll@Sun.COM * Get the length of the returned buffer 2689126SWyllys.Ingersoll@Sun.COM * Make sure that there are 4 timeout values returned 2699126SWyllys.Ingersoll@Sun.COM * length of the capability response is stored in data[10-13] 2709126SWyllys.Ingersoll@Sun.COM * Also the TPM is in network byte order 2719126SWyllys.Ingersoll@Sun.COM */ 2729126SWyllys.Ingersoll@Sun.COM len = load32(buf, TPM_CAP_RESPSIZE_OFFSET); 2739126SWyllys.Ingersoll@Sun.COM if (len != 4 * sizeof (uint32_t)) { 2749126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: capability response size should be %d" 2759126SWyllys.Ingersoll@Sun.COM "instead it's %d", 2769126SWyllys.Ingersoll@Sun.COM myname, (int)(4 * sizeof (uint32_t)), (int)len); 2779126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 2789126SWyllys.Ingersoll@Sun.COM } 2799126SWyllys.Ingersoll@Sun.COM 2809126SWyllys.Ingersoll@Sun.COM /* Get the four timeout's: a,b,c,d (they are 4 bytes long each) */ 2819126SWyllys.Ingersoll@Sun.COM timeout = load32(buf, TPM_CAP_TIMEOUT_A_OFFSET); 2829126SWyllys.Ingersoll@Sun.COM if (timeout == 0) { 2839126SWyllys.Ingersoll@Sun.COM timeout = DEFAULT_TIMEOUT_A; 2849126SWyllys.Ingersoll@Sun.COM } else if (timeout < TEN_MILLISECONDS) { 2859126SWyllys.Ingersoll@Sun.COM /* timeout is in millisecond range (should be microseconds) */ 2869126SWyllys.Ingersoll@Sun.COM timeout *= 1000; 2879126SWyllys.Ingersoll@Sun.COM } 2889126SWyllys.Ingersoll@Sun.COM tpm->timeout_a = drv_usectohz(timeout); 2899126SWyllys.Ingersoll@Sun.COM 2909126SWyllys.Ingersoll@Sun.COM timeout = load32(buf, TPM_CAP_TIMEOUT_B_OFFSET); 2919126SWyllys.Ingersoll@Sun.COM if (timeout == 0) { 2929126SWyllys.Ingersoll@Sun.COM timeout = DEFAULT_TIMEOUT_B; 2939126SWyllys.Ingersoll@Sun.COM } else if (timeout < TEN_MILLISECONDS) { 2949126SWyllys.Ingersoll@Sun.COM /* timeout is in millisecond range (should be microseconds) */ 2959126SWyllys.Ingersoll@Sun.COM timeout *= 1000; 2969126SWyllys.Ingersoll@Sun.COM } 2979126SWyllys.Ingersoll@Sun.COM tpm->timeout_b = drv_usectohz(timeout); 2989126SWyllys.Ingersoll@Sun.COM 2999126SWyllys.Ingersoll@Sun.COM timeout = load32(buf, TPM_CAP_TIMEOUT_C_OFFSET); 3009126SWyllys.Ingersoll@Sun.COM if (timeout == 0) { 3019126SWyllys.Ingersoll@Sun.COM timeout = DEFAULT_TIMEOUT_C; 3029126SWyllys.Ingersoll@Sun.COM } else if (timeout < TEN_MILLISECONDS) { 3039126SWyllys.Ingersoll@Sun.COM /* timeout is in millisecond range (should be microseconds) */ 3049126SWyllys.Ingersoll@Sun.COM timeout *= 1000; 3059126SWyllys.Ingersoll@Sun.COM } 3069126SWyllys.Ingersoll@Sun.COM tpm->timeout_c = drv_usectohz(timeout); 3079126SWyllys.Ingersoll@Sun.COM 3089126SWyllys.Ingersoll@Sun.COM timeout = load32(buf, TPM_CAP_TIMEOUT_D_OFFSET); 3099126SWyllys.Ingersoll@Sun.COM if (timeout == 0) { 3109126SWyllys.Ingersoll@Sun.COM timeout = DEFAULT_TIMEOUT_D; 3119126SWyllys.Ingersoll@Sun.COM } else if (timeout < TEN_MILLISECONDS) { 3129126SWyllys.Ingersoll@Sun.COM /* timeout is in millisecond range (should be microseconds) */ 3139126SWyllys.Ingersoll@Sun.COM timeout *= 1000; 3149126SWyllys.Ingersoll@Sun.COM } 3159126SWyllys.Ingersoll@Sun.COM tpm->timeout_d = drv_usectohz(timeout); 3169126SWyllys.Ingersoll@Sun.COM 3179126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 3189126SWyllys.Ingersoll@Sun.COM } 3199126SWyllys.Ingersoll@Sun.COM 3209126SWyllys.Ingersoll@Sun.COM /* 3219126SWyllys.Ingersoll@Sun.COM * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability 3229126SWyllys.Ingersoll@Sun.COM * with the subcommand TPM_CAP_PROP_TIS_DURATION 3239126SWyllys.Ingersoll@Sun.COM * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38) 3249126SWyllys.Ingersoll@Sun.COM */ 3259126SWyllys.Ingersoll@Sun.COM static int 3269126SWyllys.Ingersoll@Sun.COM tpm_get_duration(tpm_state_t *tpm) { 3279126SWyllys.Ingersoll@Sun.COM int ret; 3289126SWyllys.Ingersoll@Sun.COM uint32_t duration; 3299126SWyllys.Ingersoll@Sun.COM uint32_t len; 3309126SWyllys.Ingersoll@Sun.COM uint8_t buf[30] = { 3319126SWyllys.Ingersoll@Sun.COM 0, 193, /* TPM_TAG_RQU_COMMAND */ 3329126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 22, /* paramsize in bytes */ 3339126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 101, /* TPM_ORD_GetCapability */ 3349126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 5, /* TPM_CAP_Prop */ 3359126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 4, /* SUB_CAP size in bytes */ 3369126SWyllys.Ingersoll@Sun.COM 0, 0, 1, 32 /* TPM_CAP_PROP_TIS_DURATION(0x120) */ 3379126SWyllys.Ingersoll@Sun.COM }; 3389126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_get_duration"; 3399126SWyllys.Ingersoll@Sun.COM 3409126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 3419126SWyllys.Ingersoll@Sun.COM 3429126SWyllys.Ingersoll@Sun.COM ret = itpm_command(tpm, buf, sizeof (buf)); 3439126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 3449126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: itpm_command failed with ret code: 0x%x", 3459126SWyllys.Ingersoll@Sun.COM myname, ret); 3469126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 3479126SWyllys.Ingersoll@Sun.COM } 3489126SWyllys.Ingersoll@Sun.COM 3499126SWyllys.Ingersoll@Sun.COM /* 3509126SWyllys.Ingersoll@Sun.COM * Get the length of the returned buffer 3519126SWyllys.Ingersoll@Sun.COM * Make sure that there are 3 duration values (S,M,L: in that order) 3529126SWyllys.Ingersoll@Sun.COM * length of the capability response is stored in data[10-13] 3539126SWyllys.Ingersoll@Sun.COM * Also the TPM is in network byte order 3549126SWyllys.Ingersoll@Sun.COM */ 3559126SWyllys.Ingersoll@Sun.COM len = load32(buf, TPM_CAP_RESPSIZE_OFFSET); 3569126SWyllys.Ingersoll@Sun.COM if (len != 3 * sizeof (uint32_t)) { 3579126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: capability response should be %d, " 3589126SWyllys.Ingersoll@Sun.COM "instead, it's %d", 3599126SWyllys.Ingersoll@Sun.COM myname, (int)(3 * sizeof (uint32_t)), (int)len); 3609126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 3619126SWyllys.Ingersoll@Sun.COM } 3629126SWyllys.Ingersoll@Sun.COM 3639126SWyllys.Ingersoll@Sun.COM duration = load32(buf, TPM_CAP_DUR_SHORT_OFFSET); 3649126SWyllys.Ingersoll@Sun.COM if (duration == 0) { 3659126SWyllys.Ingersoll@Sun.COM duration = DEFAULT_SHORT_DURATION; 3669126SWyllys.Ingersoll@Sun.COM } else if (duration < TEN_MILLISECONDS) { 3679126SWyllys.Ingersoll@Sun.COM duration *= 1000; 3689126SWyllys.Ingersoll@Sun.COM } 3699126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_SHORT] = drv_usectohz(duration); 3709126SWyllys.Ingersoll@Sun.COM 3719126SWyllys.Ingersoll@Sun.COM duration = load32(buf, TPM_CAP_DUR_MEDIUM_OFFSET); 3729126SWyllys.Ingersoll@Sun.COM if (duration == 0) { 3739126SWyllys.Ingersoll@Sun.COM duration = DEFAULT_MEDIUM_DURATION; 3749126SWyllys.Ingersoll@Sun.COM } else if (duration < TEN_MILLISECONDS) { 3759126SWyllys.Ingersoll@Sun.COM duration *= 1000; 3769126SWyllys.Ingersoll@Sun.COM } 3779126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_MEDIUM] = drv_usectohz(duration); 3789126SWyllys.Ingersoll@Sun.COM 3799126SWyllys.Ingersoll@Sun.COM duration = load32(buf, TPM_CAP_DUR_LONG_OFFSET); 3809126SWyllys.Ingersoll@Sun.COM if (duration == 0) { 3819126SWyllys.Ingersoll@Sun.COM duration = DEFAULT_LONG_DURATION; 3829126SWyllys.Ingersoll@Sun.COM } else if (duration < FOUR_HUNDRED_MILLISECONDS) { 3839126SWyllys.Ingersoll@Sun.COM duration *= 1000; 3849126SWyllys.Ingersoll@Sun.COM } 3859126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_LONG] = drv_usectohz(duration); 3869126SWyllys.Ingersoll@Sun.COM 3879126SWyllys.Ingersoll@Sun.COM /* Just make the undefined duration be the same as the LONG */ 3889126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_UNDEFINED] = tpm->duration[TPM_LONG]; 3899126SWyllys.Ingersoll@Sun.COM 3909126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 3919126SWyllys.Ingersoll@Sun.COM } 3929126SWyllys.Ingersoll@Sun.COM 3939126SWyllys.Ingersoll@Sun.COM /* 3949126SWyllys.Ingersoll@Sun.COM * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability 3959126SWyllys.Ingersoll@Sun.COM * with the subcommand TPM_CAP_PROP_TIS_DURATION 3969126SWyllys.Ingersoll@Sun.COM * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38) 3979126SWyllys.Ingersoll@Sun.COM */ 3989126SWyllys.Ingersoll@Sun.COM static int 3999126SWyllys.Ingersoll@Sun.COM tpm_get_version(tpm_state_t *tpm) { 4009126SWyllys.Ingersoll@Sun.COM int ret; 4019126SWyllys.Ingersoll@Sun.COM uint32_t len; 4029126SWyllys.Ingersoll@Sun.COM char vendorId[5]; 4039126SWyllys.Ingersoll@Sun.COM /* If this buf is too small, the "vendor specific" data won't fit */ 4049126SWyllys.Ingersoll@Sun.COM uint8_t buf[64] = { 4059126SWyllys.Ingersoll@Sun.COM 0, 193, /* TPM_TAG_RQU_COMMAND */ 4069126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 18, /* paramsize in bytes */ 4079126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 101, /* TPM_ORD_GetCapability */ 4089126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 0x1A, /* TPM_CAP_VERSION_VAL */ 4099126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 0, /* SUB_CAP size in bytes */ 4109126SWyllys.Ingersoll@Sun.COM }; 4119126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_get_version"; 4129126SWyllys.Ingersoll@Sun.COM 4139126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 4149126SWyllys.Ingersoll@Sun.COM 4159126SWyllys.Ingersoll@Sun.COM ret = itpm_command(tpm, buf, sizeof (buf)); 4169126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 4179126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: itpm_command failed with ret code: 0x%x", 4189126SWyllys.Ingersoll@Sun.COM myname, ret); 4199126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 4209126SWyllys.Ingersoll@Sun.COM } 4219126SWyllys.Ingersoll@Sun.COM 4229126SWyllys.Ingersoll@Sun.COM /* 4239126SWyllys.Ingersoll@Sun.COM * Get the length of the returned buffer. 4249126SWyllys.Ingersoll@Sun.COM */ 4259126SWyllys.Ingersoll@Sun.COM len = load32(buf, TPM_CAP_RESPSIZE_OFFSET); 4269126SWyllys.Ingersoll@Sun.COM if (len < TPM_CAP_VERSION_INFO_SIZE) { 4279126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: capability response should be greater" 4289126SWyllys.Ingersoll@Sun.COM " than %d, instead, it's %d", 4299126SWyllys.Ingersoll@Sun.COM myname, 4309126SWyllys.Ingersoll@Sun.COM TPM_CAP_VERSION_INFO_SIZE, 4319126SWyllys.Ingersoll@Sun.COM len); 4329126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 4339126SWyllys.Ingersoll@Sun.COM } 4349126SWyllys.Ingersoll@Sun.COM 4359126SWyllys.Ingersoll@Sun.COM bcopy(buf + TPM_CAP_VERSION_INFO_OFFSET, &tpm->vers_info, 4369126SWyllys.Ingersoll@Sun.COM TPM_CAP_VERSION_INFO_SIZE); 4379126SWyllys.Ingersoll@Sun.COM 4389126SWyllys.Ingersoll@Sun.COM bcopy(tpm->vers_info.tpmVendorID, vendorId, 4399126SWyllys.Ingersoll@Sun.COM sizeof (tpm->vers_info.tpmVendorID)); 4409126SWyllys.Ingersoll@Sun.COM vendorId[4] = '\0'; 4419126SWyllys.Ingersoll@Sun.COM 4429126SWyllys.Ingersoll@Sun.COM cmn_err(CE_NOTE, "!TPM found: Ver %d.%d, Rev %d.%d, " 4439126SWyllys.Ingersoll@Sun.COM "SpecLevel %d, errataRev %d, VendorId '%s'", 4449126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.major, /* Version */ 4459126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.minor, 4469126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.revMajor, /* Revision */ 4479126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.revMinor, 4489126SWyllys.Ingersoll@Sun.COM (int)ntohs(tpm->vers_info.specLevel), 4499126SWyllys.Ingersoll@Sun.COM tpm->vers_info.errataRev, 4509126SWyllys.Ingersoll@Sun.COM vendorId); 4519126SWyllys.Ingersoll@Sun.COM 4529126SWyllys.Ingersoll@Sun.COM /* 4539126SWyllys.Ingersoll@Sun.COM * This driver only supports TPM Version 1.2 4549126SWyllys.Ingersoll@Sun.COM */ 4559126SWyllys.Ingersoll@Sun.COM if (tpm->vers_info.version.major != 1 && 4569126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.minor != 2) { 4579126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: Unsupported TPM version (%d.%d)", 4589126SWyllys.Ingersoll@Sun.COM myname, 4599126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.major, /* Version */ 4609126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.minor); 4619126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 4629126SWyllys.Ingersoll@Sun.COM } 4639126SWyllys.Ingersoll@Sun.COM 4649126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 4659126SWyllys.Ingersoll@Sun.COM } 4669126SWyllys.Ingersoll@Sun.COM 4679126SWyllys.Ingersoll@Sun.COM /* 4689126SWyllys.Ingersoll@Sun.COM * To prevent the TPM from complaining that certain functions are not tested 4699126SWyllys.Ingersoll@Sun.COM * we run this command when the driver attaches. 4709126SWyllys.Ingersoll@Sun.COM * For details see Section 4.2 of TPM Main Part 3 Command Specification 4719126SWyllys.Ingersoll@Sun.COM */ 4729126SWyllys.Ingersoll@Sun.COM static int 4739126SWyllys.Ingersoll@Sun.COM tpm_continue_selftest(tpm_state_t *tpm) { 4749126SWyllys.Ingersoll@Sun.COM int ret; 4759126SWyllys.Ingersoll@Sun.COM uint8_t buf[10] = { 4769126SWyllys.Ingersoll@Sun.COM 0, 193, /* TPM_TAG_RQU COMMAND */ 4779126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 10, /* paramsize in bytes */ 4789126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 83 /* TPM_ORD_ContinueSelfTest */ 4799126SWyllys.Ingersoll@Sun.COM }; 4809126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_continue_selftest"; 4819126SWyllys.Ingersoll@Sun.COM 4829126SWyllys.Ingersoll@Sun.COM /* Need a longer timeout */ 4839126SWyllys.Ingersoll@Sun.COM ret = itpm_command(tpm, buf, sizeof (buf)); 4849126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 4859126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: itpm_command failed", myname); 4869126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 4879126SWyllys.Ingersoll@Sun.COM } 4889126SWyllys.Ingersoll@Sun.COM 4899126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 4909126SWyllys.Ingersoll@Sun.COM } 4919126SWyllys.Ingersoll@Sun.COM /* 4929126SWyllys.Ingersoll@Sun.COM * Auxilary Functions 4939126SWyllys.Ingersoll@Sun.COM */ 4949126SWyllys.Ingersoll@Sun.COM 4959126SWyllys.Ingersoll@Sun.COM /* 4969126SWyllys.Ingersoll@Sun.COM * Find out how long we should wait for the TPM command to complete a command 4979126SWyllys.Ingersoll@Sun.COM */ 4989126SWyllys.Ingersoll@Sun.COM static clock_t 4999126SWyllys.Ingersoll@Sun.COM tpm_get_ordinal_duration(tpm_state_t *tpm, uint8_t ordinal) 5009126SWyllys.Ingersoll@Sun.COM { 5019126SWyllys.Ingersoll@Sun.COM uint8_t index; 5029126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_get_ordinal_duration"; 5039126SWyllys.Ingersoll@Sun.COM 5049126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 5059126SWyllys.Ingersoll@Sun.COM 5069126SWyllys.Ingersoll@Sun.COM /* Default and failure case for IFX */ 5079126SWyllys.Ingersoll@Sun.COM /* Is it a TSC_ORDINAL? */ 5089126SWyllys.Ingersoll@Sun.COM if (ordinal & TSC_ORDINAL_MASK) { 5099126SWyllys.Ingersoll@Sun.COM if (ordinal > TSC_ORDINAL_MAX) { 5109126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, 5119126SWyllys.Ingersoll@Sun.COM "%s: tsc ordinal: %d exceeds MAX: %d", 5129126SWyllys.Ingersoll@Sun.COM myname, ordinal, TSC_ORDINAL_MAX); 5139126SWyllys.Ingersoll@Sun.COM return (0); 5149126SWyllys.Ingersoll@Sun.COM } 5159126SWyllys.Ingersoll@Sun.COM index = tsc_ords_duration[ordinal]; 5169126SWyllys.Ingersoll@Sun.COM } else { 5179126SWyllys.Ingersoll@Sun.COM if (ordinal > TPM_ORDINAL_MAX) { 5189126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, 5199126SWyllys.Ingersoll@Sun.COM "%s: ordinal %d exceeds MAX: %d", 5209126SWyllys.Ingersoll@Sun.COM myname, ordinal, TPM_ORDINAL_MAX); 5219126SWyllys.Ingersoll@Sun.COM return (0); 5229126SWyllys.Ingersoll@Sun.COM } 5239126SWyllys.Ingersoll@Sun.COM index = tpm_ords_duration[ordinal]; 5249126SWyllys.Ingersoll@Sun.COM } 5259126SWyllys.Ingersoll@Sun.COM 5269126SWyllys.Ingersoll@Sun.COM if (index > TPM_DURATION_MAX_IDX) { 5279126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: FATAL:index '%d' is out of bound", 5289126SWyllys.Ingersoll@Sun.COM myname, index); 5299126SWyllys.Ingersoll@Sun.COM return (0); 5309126SWyllys.Ingersoll@Sun.COM } 5319126SWyllys.Ingersoll@Sun.COM return (tpm->duration[index]); 5329126SWyllys.Ingersoll@Sun.COM } 5339126SWyllys.Ingersoll@Sun.COM 5349126SWyllys.Ingersoll@Sun.COM /* 5359126SWyllys.Ingersoll@Sun.COM * Internal TPM Transmit Function: 5369126SWyllys.Ingersoll@Sun.COM * Calls implementation specific sendto and receive 5379126SWyllys.Ingersoll@Sun.COM * The code assumes that the buffer is in network byte order 5389126SWyllys.Ingersoll@Sun.COM */ 5399126SWyllys.Ingersoll@Sun.COM static int 5409126SWyllys.Ingersoll@Sun.COM itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) 5419126SWyllys.Ingersoll@Sun.COM { 5429126SWyllys.Ingersoll@Sun.COM int ret; 5439126SWyllys.Ingersoll@Sun.COM uint32_t count; 5449126SWyllys.Ingersoll@Sun.COM char *myname = "itpm_command"; 5459126SWyllys.Ingersoll@Sun.COM 5469126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && buf != NULL); 5479126SWyllys.Ingersoll@Sun.COM 5489126SWyllys.Ingersoll@Sun.COM /* The byte order is network byte order so convert it */ 5499126SWyllys.Ingersoll@Sun.COM count = load32(buf, TPM_PARAMSIZE_OFFSET); 5509126SWyllys.Ingersoll@Sun.COM 5519126SWyllys.Ingersoll@Sun.COM if (count == 0) { 5529126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: count=0, no data? %d", myname, 5539126SWyllys.Ingersoll@Sun.COM (int)bufsiz); 5549126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 5559126SWyllys.Ingersoll@Sun.COM } 5569126SWyllys.Ingersoll@Sun.COM if (count > bufsiz) { 5579126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: invalid count value:count:%d > bufsiz %d", 5589126SWyllys.Ingersoll@Sun.COM myname, (int)count, (int)bufsiz); 5599126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 5609126SWyllys.Ingersoll@Sun.COM } 5619126SWyllys.Ingersoll@Sun.COM 5629126SWyllys.Ingersoll@Sun.COM /* Send the command */ 5639126SWyllys.Ingersoll@Sun.COM ret = tis_send_data(tpm, buf, count); 5649126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 5659126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_send_data failed with error %x", 5669126SWyllys.Ingersoll@Sun.COM myname, ret); 5679126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 5689126SWyllys.Ingersoll@Sun.COM } 5699126SWyllys.Ingersoll@Sun.COM 5709126SWyllys.Ingersoll@Sun.COM /* 5719126SWyllys.Ingersoll@Sun.COM * Now receive the data from the tpm 5729126SWyllys.Ingersoll@Sun.COM * Should at least receive "the common" 10 bytes (TPM_HEADER_SIZE) 5739126SWyllys.Ingersoll@Sun.COM */ 5749126SWyllys.Ingersoll@Sun.COM ret = tis_recv_data(tpm, buf, bufsiz); 5759126SWyllys.Ingersoll@Sun.COM if (ret < TPM_HEADER_SIZE) { 5769126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_recv_data failed", myname); 5779126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 5789126SWyllys.Ingersoll@Sun.COM } 5799126SWyllys.Ingersoll@Sun.COM 5809126SWyllys.Ingersoll@Sun.COM /* Check the return code */ 5819126SWyllys.Ingersoll@Sun.COM ret = load32(buf, TPM_RETURN_OFFSET); 5829126SWyllys.Ingersoll@Sun.COM if (ret != TPM_SUCCESS) { 5839126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: command failed with ret code: %x", 5849126SWyllys.Ingersoll@Sun.COM myname, ret); 5859126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 5869126SWyllys.Ingersoll@Sun.COM } 5879126SWyllys.Ingersoll@Sun.COM 5889126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 5899126SWyllys.Ingersoll@Sun.COM } 5909126SWyllys.Ingersoll@Sun.COM 5919126SWyllys.Ingersoll@Sun.COM /* 5929126SWyllys.Ingersoll@Sun.COM * Whenever the driver wants to write to the DATA_IO register, it must need 5939126SWyllys.Ingersoll@Sun.COM * to figure out the burstcount. This is the amount of bytes it can write 5949126SWyllys.Ingersoll@Sun.COM * before having to wait for long LPC bus cycle 5959126SWyllys.Ingersoll@Sun.COM * 5969126SWyllys.Ingersoll@Sun.COM * Returns: 0 if error, burst count if sucess 5979126SWyllys.Ingersoll@Sun.COM */ 5989126SWyllys.Ingersoll@Sun.COM static uint16_t 5999126SWyllys.Ingersoll@Sun.COM tpm_get_burstcount(tpm_state_t *tpm) { 6009126SWyllys.Ingersoll@Sun.COM clock_t stop; 6019126SWyllys.Ingersoll@Sun.COM uint16_t burstcnt; 6029126SWyllys.Ingersoll@Sun.COM 6039126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 6049126SWyllys.Ingersoll@Sun.COM 6059126SWyllys.Ingersoll@Sun.COM /* 6069126SWyllys.Ingersoll@Sun.COM * Spec says timeout should be TIMEOUT_D 6079126SWyllys.Ingersoll@Sun.COM * burst count is TPM_STS bits 8..23 6089126SWyllys.Ingersoll@Sun.COM */ 6099126SWyllys.Ingersoll@Sun.COM stop = ddi_get_lbolt() + tpm->timeout_d; 6109126SWyllys.Ingersoll@Sun.COM do { 6119126SWyllys.Ingersoll@Sun.COM /* 6129126SWyllys.Ingersoll@Sun.COM * burstcnt is stored as a little endian value 6139126SWyllys.Ingersoll@Sun.COM * 'ntohs' doesn't work since the value is not word-aligned 6149126SWyllys.Ingersoll@Sun.COM */ 6159126SWyllys.Ingersoll@Sun.COM burstcnt = ddi_get8(tpm->handle, 6169126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr+ 6179126SWyllys.Ingersoll@Sun.COM TPM_STS_(tpm->locality)+1)); 6189126SWyllys.Ingersoll@Sun.COM burstcnt += ddi_get8(tpm->handle, 6199126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr+ 6209126SWyllys.Ingersoll@Sun.COM TPM_STS_(tpm->locality)+2)) << 8; 6219126SWyllys.Ingersoll@Sun.COM 6229126SWyllys.Ingersoll@Sun.COM if (burstcnt) 6239126SWyllys.Ingersoll@Sun.COM return (burstcnt); 6249126SWyllys.Ingersoll@Sun.COM 6259126SWyllys.Ingersoll@Sun.COM delay(tpm->timeout_poll); 6269126SWyllys.Ingersoll@Sun.COM } while (ddi_get_lbolt() < stop); 6279126SWyllys.Ingersoll@Sun.COM 6289126SWyllys.Ingersoll@Sun.COM return (0); 6299126SWyllys.Ingersoll@Sun.COM } 6309126SWyllys.Ingersoll@Sun.COM 6319126SWyllys.Ingersoll@Sun.COM /* 6329126SWyllys.Ingersoll@Sun.COM * Writing 1 to TPM_STS_CMD_READY bit in TPM_STS will do the following: 6339126SWyllys.Ingersoll@Sun.COM * 1. The TPM will clears IO buffers if any 6349126SWyllys.Ingersoll@Sun.COM * 2. The TPM will enters either Idle or Ready state within TIMEOUT_B 6359126SWyllys.Ingersoll@Sun.COM * (checked in the calling function) 6369126SWyllys.Ingersoll@Sun.COM */ 6379126SWyllys.Ingersoll@Sun.COM static void 6389126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm_state_t *tpm) { 6399126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 6409126SWyllys.Ingersoll@Sun.COM 6419126SWyllys.Ingersoll@Sun.COM ddi_put8(tpm->handle, 6429126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality)), 6439126SWyllys.Ingersoll@Sun.COM TPM_STS_CMD_READY); 6449126SWyllys.Ingersoll@Sun.COM } 6459126SWyllys.Ingersoll@Sun.COM 6469126SWyllys.Ingersoll@Sun.COM static int 6479126SWyllys.Ingersoll@Sun.COM receive_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) { 6489126SWyllys.Ingersoll@Sun.COM int size = 0; 6499126SWyllys.Ingersoll@Sun.COM int retried = 0; 6509126SWyllys.Ingersoll@Sun.COM uint8_t stsbits; 6519126SWyllys.Ingersoll@Sun.COM 6529126SWyllys.Ingersoll@Sun.COM /* A number of consecutive bytes that can be written to TPM */ 6539126SWyllys.Ingersoll@Sun.COM uint16_t burstcnt; 6549126SWyllys.Ingersoll@Sun.COM 6559126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && buf != NULL); 6569126SWyllys.Ingersoll@Sun.COM retry: 6579126SWyllys.Ingersoll@Sun.COM while (size < bufsiz && 6589126SWyllys.Ingersoll@Sun.COM (tpm_wait_for_stat(tpm, 6599126SWyllys.Ingersoll@Sun.COM (TPM_STS_DATA_AVAIL|TPM_STS_VALID), 6609126SWyllys.Ingersoll@Sun.COM (ddi_get_lbolt() + tpm->timeout_c)) == DDI_SUCCESS)) { 6619126SWyllys.Ingersoll@Sun.COM /* 6629126SWyllys.Ingersoll@Sun.COM * Burstcount should be available within TIMEOUT_D 6639126SWyllys.Ingersoll@Sun.COM * after STS is set to valid 6649126SWyllys.Ingersoll@Sun.COM * burstcount is dynamic, so have to get it each time 6659126SWyllys.Ingersoll@Sun.COM */ 6669126SWyllys.Ingersoll@Sun.COM burstcnt = tpm_get_burstcount(tpm); 6679126SWyllys.Ingersoll@Sun.COM for (; burstcnt > 0 && size < bufsiz; burstcnt--) { 6689126SWyllys.Ingersoll@Sun.COM buf[size++] = ddi_get8(tpm->handle, 6699126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr + 6709126SWyllys.Ingersoll@Sun.COM TPM_DATA_FIFO_(tpm->locality))); 6719126SWyllys.Ingersoll@Sun.COM } 6729126SWyllys.Ingersoll@Sun.COM } 6739126SWyllys.Ingersoll@Sun.COM stsbits = tis_get_status(tpm); 6749126SWyllys.Ingersoll@Sun.COM /* check to see if we need to retry (just once) */ 6759126SWyllys.Ingersoll@Sun.COM if (size < bufsiz && !(stsbits & TPM_STS_DATA_AVAIL) && retried == 0) { 6769126SWyllys.Ingersoll@Sun.COM /* issue responseRetry (TIS 1.2 pg 54) */ 6779126SWyllys.Ingersoll@Sun.COM ddi_put8(tpm->handle, 6789126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality)), 6799126SWyllys.Ingersoll@Sun.COM TPM_STS_RESPONSE_RETRY); 6809126SWyllys.Ingersoll@Sun.COM /* update the retry counter so we only retry once */ 6819126SWyllys.Ingersoll@Sun.COM retried++; 6829126SWyllys.Ingersoll@Sun.COM /* reset the size to 0 and reread the entire response */ 6839126SWyllys.Ingersoll@Sun.COM size = 0; 6849126SWyllys.Ingersoll@Sun.COM goto retry; 6859126SWyllys.Ingersoll@Sun.COM } 6869126SWyllys.Ingersoll@Sun.COM return (size); 6879126SWyllys.Ingersoll@Sun.COM } 6889126SWyllys.Ingersoll@Sun.COM 6899126SWyllys.Ingersoll@Sun.COM /* Receive the data from the TPM */ 6909126SWyllys.Ingersoll@Sun.COM static int 6919126SWyllys.Ingersoll@Sun.COM tis_recv_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) { 6929126SWyllys.Ingersoll@Sun.COM int ret; 6939126SWyllys.Ingersoll@Sun.COM int size = 0; 6949126SWyllys.Ingersoll@Sun.COM uint32_t expected, status; 6959126SWyllys.Ingersoll@Sun.COM uint32_t cmdresult; 6969126SWyllys.Ingersoll@Sun.COM char *myname = "tis_recv_data"; 6979126SWyllys.Ingersoll@Sun.COM 6989126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && buf != NULL); 6999126SWyllys.Ingersoll@Sun.COM 7009126SWyllys.Ingersoll@Sun.COM if (bufsiz < TPM_HEADER_SIZE) { 7019126SWyllys.Ingersoll@Sun.COM /* There should be at least tag,paramsize,return code */ 7029126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: received data should contain at least " 7039126SWyllys.Ingersoll@Sun.COM "the header which is %d bytes long", 7049126SWyllys.Ingersoll@Sun.COM myname, TPM_HEADER_SIZE); 7059126SWyllys.Ingersoll@Sun.COM goto OUT; 7069126SWyllys.Ingersoll@Sun.COM } 7079126SWyllys.Ingersoll@Sun.COM 7089126SWyllys.Ingersoll@Sun.COM /* Read tag(2 bytes), paramsize(4), and result(4) */ 7099126SWyllys.Ingersoll@Sun.COM size = receive_data(tpm, buf, TPM_HEADER_SIZE); 7109126SWyllys.Ingersoll@Sun.COM if (size < TPM_HEADER_SIZE) { 7119126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: getting the TPM_HEADER failed: size=%d", 7129126SWyllys.Ingersoll@Sun.COM myname, size); 7139126SWyllys.Ingersoll@Sun.COM goto OUT; 7149126SWyllys.Ingersoll@Sun.COM } 7159126SWyllys.Ingersoll@Sun.COM 7169126SWyllys.Ingersoll@Sun.COM cmdresult = load32(buf, TPM_RETURN_OFFSET); 7179126SWyllys.Ingersoll@Sun.COM 7189126SWyllys.Ingersoll@Sun.COM /* Get 'paramsize'(4 bytes)--it includes tag and paramsize */ 7199126SWyllys.Ingersoll@Sun.COM expected = load32(buf, TPM_PARAMSIZE_OFFSET); 7209126SWyllys.Ingersoll@Sun.COM if (expected > bufsiz) { 7219126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: paramSize is bigger " 7229126SWyllys.Ingersoll@Sun.COM "than the requested size: paramSize=%d bufsiz=%d result=%d", 7239126SWyllys.Ingersoll@Sun.COM myname, (int)expected, (int)bufsiz, cmdresult); 7249126SWyllys.Ingersoll@Sun.COM goto OUT; 7259126SWyllys.Ingersoll@Sun.COM } 7269126SWyllys.Ingersoll@Sun.COM 7279126SWyllys.Ingersoll@Sun.COM /* Read in the rest of the data from the TPM */ 7289126SWyllys.Ingersoll@Sun.COM size += receive_data(tpm, (uint8_t *)&buf[TPM_HEADER_SIZE], 7299126SWyllys.Ingersoll@Sun.COM expected - TPM_HEADER_SIZE); 7309126SWyllys.Ingersoll@Sun.COM if (size < expected) { 7319126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: received data length=%d " 7329126SWyllys.Ingersoll@Sun.COM "is less than expected = %d", myname, size, expected); 7339126SWyllys.Ingersoll@Sun.COM goto OUT; 7349126SWyllys.Ingersoll@Sun.COM } 7359126SWyllys.Ingersoll@Sun.COM 7369126SWyllys.Ingersoll@Sun.COM /* The TPM MUST set the state to stsValid within TIMEOUT_C */ 7379126SWyllys.Ingersoll@Sun.COM ret = tpm_wait_for_stat(tpm, TPM_STS_VALID, 7389126SWyllys.Ingersoll@Sun.COM ddi_get_lbolt() + tpm->timeout_c); 7399126SWyllys.Ingersoll@Sun.COM 7409126SWyllys.Ingersoll@Sun.COM status = tis_get_status(tpm); 7419126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 7429126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: TPM didn't set stsValid after its I/O: " 7439126SWyllys.Ingersoll@Sun.COM "status = 0x%08X", myname, status); 7449126SWyllys.Ingersoll@Sun.COM goto OUT; 7459126SWyllys.Ingersoll@Sun.COM } 7469126SWyllys.Ingersoll@Sun.COM 7479126SWyllys.Ingersoll@Sun.COM /* There is still more data? */ 7489126SWyllys.Ingersoll@Sun.COM if (status & TPM_STS_DATA_AVAIL) { 7499126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: Status TPM_STS_DATA_AVAIL set:0x%08X", 7509126SWyllys.Ingersoll@Sun.COM myname, status); 7519126SWyllys.Ingersoll@Sun.COM goto OUT; 7529126SWyllys.Ingersoll@Sun.COM } 7539126SWyllys.Ingersoll@Sun.COM 7549126SWyllys.Ingersoll@Sun.COM /* 7559126SWyllys.Ingersoll@Sun.COM * Release the control of the TPM after we are done with it 7569126SWyllys.Ingersoll@Sun.COM * it...so others can also get a chance to send data 7579126SWyllys.Ingersoll@Sun.COM */ 7589126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm, tpm->locality, 0); 7599126SWyllys.Ingersoll@Sun.COM 7609126SWyllys.Ingersoll@Sun.COM OUT: 7619126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm); 7629126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm, tpm->locality, 0); 7639126SWyllys.Ingersoll@Sun.COM return (size); 7649126SWyllys.Ingersoll@Sun.COM } 7659126SWyllys.Ingersoll@Sun.COM 7669126SWyllys.Ingersoll@Sun.COM /* 7679126SWyllys.Ingersoll@Sun.COM * Send the data (TPM commands) to the Data IO register 7689126SWyllys.Ingersoll@Sun.COM */ 7699126SWyllys.Ingersoll@Sun.COM static int 7709126SWyllys.Ingersoll@Sun.COM tis_send_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) { 7719126SWyllys.Ingersoll@Sun.COM int ret; 7729126SWyllys.Ingersoll@Sun.COM uint8_t status; 7739126SWyllys.Ingersoll@Sun.COM uint16_t burstcnt; 7749126SWyllys.Ingersoll@Sun.COM uint32_t ordinal; 7759126SWyllys.Ingersoll@Sun.COM size_t count = 0; 7769126SWyllys.Ingersoll@Sun.COM char *myname = "tis_send_data"; 7779126SWyllys.Ingersoll@Sun.COM 7789126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && buf != NULL); 7799126SWyllys.Ingersoll@Sun.COM 7809126SWyllys.Ingersoll@Sun.COM if (bufsiz == 0) { 7819126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: passed in argument bufsize is zero", 7829126SWyllys.Ingersoll@Sun.COM myname); 7839126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 7849126SWyllys.Ingersoll@Sun.COM } 7859126SWyllys.Ingersoll@Sun.COM 7869126SWyllys.Ingersoll@Sun.COM /* Be in the right locality (aren't we always in locality 0?) */ 7879126SWyllys.Ingersoll@Sun.COM if (tis_request_locality(tpm, 0) != DDI_SUCCESS) { 7889126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_request_locality didn't enter " 7899126SWyllys.Ingersoll@Sun.COM "locality 0", myname); 7909126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 7919126SWyllys.Ingersoll@Sun.COM } 7929126SWyllys.Ingersoll@Sun.COM 7939126SWyllys.Ingersoll@Sun.COM /* Put the TPM in ready state */ 7949126SWyllys.Ingersoll@Sun.COM status = tis_get_status(tpm); 7959126SWyllys.Ingersoll@Sun.COM 7969126SWyllys.Ingersoll@Sun.COM if (!(status & TPM_STS_CMD_READY)) { 7979126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm); 7989126SWyllys.Ingersoll@Sun.COM ret = tpm_wait_for_stat(tpm, 7999126SWyllys.Ingersoll@Sun.COM TPM_STS_CMD_READY, 8009126SWyllys.Ingersoll@Sun.COM (ddi_get_lbolt() + tpm->timeout_b)); 8019126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 8029126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: could not put the TPM " 8039126SWyllys.Ingersoll@Sun.COM "in the command ready state:" 8049126SWyllys.Ingersoll@Sun.COM "tpm_wait_for_stat returned error", 8059126SWyllys.Ingersoll@Sun.COM myname); 8069126SWyllys.Ingersoll@Sun.COM goto FAIL; 8079126SWyllys.Ingersoll@Sun.COM } 8089126SWyllys.Ingersoll@Sun.COM } 8099126SWyllys.Ingersoll@Sun.COM 8109126SWyllys.Ingersoll@Sun.COM /* 8119126SWyllys.Ingersoll@Sun.COM * Now we are ready to send command 8129126SWyllys.Ingersoll@Sun.COM * TPM's burstcount dictates how many bytes we can write at a time 8139126SWyllys.Ingersoll@Sun.COM * Burstcount is dynamic if INTF_CAPABILITY for static burstcount is 8149126SWyllys.Ingersoll@Sun.COM * not set. 8159126SWyllys.Ingersoll@Sun.COM */ 8169126SWyllys.Ingersoll@Sun.COM while (count < bufsiz - 1) { 8179126SWyllys.Ingersoll@Sun.COM burstcnt = tpm_get_burstcount(tpm); 8189126SWyllys.Ingersoll@Sun.COM if (burstcnt == 0) { 8199126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_get_burstcnt returned error", 8209126SWyllys.Ingersoll@Sun.COM myname); 8219126SWyllys.Ingersoll@Sun.COM ret = DDI_FAILURE; 8229126SWyllys.Ingersoll@Sun.COM goto FAIL; 8239126SWyllys.Ingersoll@Sun.COM } 8249126SWyllys.Ingersoll@Sun.COM 8259126SWyllys.Ingersoll@Sun.COM for (; burstcnt > 0 && count < bufsiz - 1; burstcnt--) { 8269126SWyllys.Ingersoll@Sun.COM ddi_put8(tpm->handle, (uint8_t *)(tpm->addr+ 8279126SWyllys.Ingersoll@Sun.COM TPM_DATA_FIFO_(tpm->locality)), buf[count]); 8289126SWyllys.Ingersoll@Sun.COM count++; 8299126SWyllys.Ingersoll@Sun.COM } 8309126SWyllys.Ingersoll@Sun.COM /* Wait for TPM to indicate that it is ready for more data */ 8319126SWyllys.Ingersoll@Sun.COM ret = tpm_wait_for_stat(tpm, 8329126SWyllys.Ingersoll@Sun.COM (TPM_STS_VALID | TPM_STS_DATA_EXPECT), 8339126SWyllys.Ingersoll@Sun.COM (ddi_get_lbolt() + tpm->timeout_c)); 8349126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 8359126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: TPM didn't enter stsvalid " 8369126SWyllys.Ingersoll@Sun.COM "state after sending the data:", myname); 8379126SWyllys.Ingersoll@Sun.COM goto FAIL; 8389126SWyllys.Ingersoll@Sun.COM } 8399126SWyllys.Ingersoll@Sun.COM } 8409126SWyllys.Ingersoll@Sun.COM /* We can't exit the loop above unless we wrote bufsiz-1 bytes */ 8419126SWyllys.Ingersoll@Sun.COM 8429126SWyllys.Ingersoll@Sun.COM /* Write last byte */ 8439126SWyllys.Ingersoll@Sun.COM ddi_put8(tpm->handle, (uint8_t *)(tpm->addr + 8449126SWyllys.Ingersoll@Sun.COM TPM_DATA_FIFO_(tpm->locality)), buf[count]); 8459126SWyllys.Ingersoll@Sun.COM count++; 8469126SWyllys.Ingersoll@Sun.COM 8479126SWyllys.Ingersoll@Sun.COM /* Wait for the TPM to enter Valid State */ 8489126SWyllys.Ingersoll@Sun.COM ret = tpm_wait_for_stat(tpm, 8499126SWyllys.Ingersoll@Sun.COM TPM_STS_VALID, (ddi_get_lbolt() + tpm->timeout_c)); 8509126SWyllys.Ingersoll@Sun.COM if (ret == DDI_FAILURE) { 8519126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm didn't enter Valid state", myname); 8529126SWyllys.Ingersoll@Sun.COM goto FAIL; 8539126SWyllys.Ingersoll@Sun.COM } 8549126SWyllys.Ingersoll@Sun.COM 8559126SWyllys.Ingersoll@Sun.COM status = tis_get_status(tpm); 8569126SWyllys.Ingersoll@Sun.COM /* The TPM should NOT be expecing more data at this point */ 8579126SWyllys.Ingersoll@Sun.COM if ((status & TPM_STS_DATA_EXPECT) != 0) { 8589126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: DATA_EXPECT is set (shouldn't be) after " 8599126SWyllys.Ingersoll@Sun.COM "writing the last byte: status=0x%08X", myname, status); 8609126SWyllys.Ingersoll@Sun.COM ret = DDI_FAILURE; 8619126SWyllys.Ingersoll@Sun.COM goto FAIL; 8629126SWyllys.Ingersoll@Sun.COM } 8639126SWyllys.Ingersoll@Sun.COM 8649126SWyllys.Ingersoll@Sun.COM /* 8659126SWyllys.Ingersoll@Sun.COM * Final step: Writing TPM_STS_GO to TPM_STS 8669126SWyllys.Ingersoll@Sun.COM * register will actually send the command. 8679126SWyllys.Ingersoll@Sun.COM */ 8689126SWyllys.Ingersoll@Sun.COM ddi_put8(tpm->handle, (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality)), 8699126SWyllys.Ingersoll@Sun.COM TPM_STS_GO); 8709126SWyllys.Ingersoll@Sun.COM 8719126SWyllys.Ingersoll@Sun.COM /* Ordinal/Command_code is located in buf[6..9] */ 8729126SWyllys.Ingersoll@Sun.COM ordinal = load32(buf, TPM_COMMAND_CODE_OFFSET); 8739126SWyllys.Ingersoll@Sun.COM 8749126SWyllys.Ingersoll@Sun.COM ret = tpm_wait_for_stat(tpm, TPM_STS_DATA_AVAIL | TPM_STS_VALID, 8759126SWyllys.Ingersoll@Sun.COM ddi_get_lbolt() + tpm_get_ordinal_duration(tpm, ordinal)); 8769126SWyllys.Ingersoll@Sun.COM if (ret == DDI_FAILURE) { 8779126SWyllys.Ingersoll@Sun.COM status = tis_get_status(tpm); 8789126SWyllys.Ingersoll@Sun.COM if (!(status & TPM_STS_DATA_AVAIL) || 8799126SWyllys.Ingersoll@Sun.COM !(status & TPM_STS_VALID)) { 8809126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: TPM not ready or valid " 8819126SWyllys.Ingersoll@Sun.COM "(ordinal = %d timeout = %ld)", 8829126SWyllys.Ingersoll@Sun.COM myname, ordinal, 8839126SWyllys.Ingersoll@Sun.COM tpm_get_ordinal_duration(tpm, ordinal)); 8849126SWyllys.Ingersoll@Sun.COM } else { 8859126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_wait_for_stat " 8869126SWyllys.Ingersoll@Sun.COM "(DATA_AVAIL | VALID) failed: STS = 0x%0X", 8879126SWyllys.Ingersoll@Sun.COM myname, status); 8889126SWyllys.Ingersoll@Sun.COM } 8899126SWyllys.Ingersoll@Sun.COM goto FAIL; 8909126SWyllys.Ingersoll@Sun.COM } 8919126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 8929126SWyllys.Ingersoll@Sun.COM 8939126SWyllys.Ingersoll@Sun.COM FAIL: 8949126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm); 8959126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm, tpm->locality, 0); 8969126SWyllys.Ingersoll@Sun.COM return (ret); 8979126SWyllys.Ingersoll@Sun.COM } 8989126SWyllys.Ingersoll@Sun.COM 8999126SWyllys.Ingersoll@Sun.COM /* 9009126SWyllys.Ingersoll@Sun.COM * Clear XrequestUse and Xactivelocality, where X is the current locality 9019126SWyllys.Ingersoll@Sun.COM */ 9029126SWyllys.Ingersoll@Sun.COM static void 9039126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm_state_t *tpm, char locality, int force) { 9049126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && locality >= 0 && locality < 5); 9059126SWyllys.Ingersoll@Sun.COM 9069126SWyllys.Ingersoll@Sun.COM if (force || 9079126SWyllys.Ingersoll@Sun.COM (ddi_get8(tpm->handle, 9089126SWyllys.Ingersoll@Sun.COM (uchar_t *)(tpm->addr+TPM_ACCESS_(locality))) 9099126SWyllys.Ingersoll@Sun.COM & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) 9109126SWyllys.Ingersoll@Sun.COM == (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) { 9119126SWyllys.Ingersoll@Sun.COM /* 9129126SWyllys.Ingersoll@Sun.COM * Writing 1 to active locality bit in TPM_ACCESS 9139126SWyllys.Ingersoll@Sun.COM * register reliquishes the control of the locality 9149126SWyllys.Ingersoll@Sun.COM */ 9159126SWyllys.Ingersoll@Sun.COM ddi_put8(tpm->handle, 9169126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr+TPM_ACCESS_(locality)), 9179126SWyllys.Ingersoll@Sun.COM TPM_ACCESS_ACTIVE_LOCALITY); 9189126SWyllys.Ingersoll@Sun.COM } 9199126SWyllys.Ingersoll@Sun.COM } 9209126SWyllys.Ingersoll@Sun.COM 9219126SWyllys.Ingersoll@Sun.COM /* 9229126SWyllys.Ingersoll@Sun.COM * Checks whether the given locality is active 9239126SWyllys.Ingersoll@Sun.COM * Use TPM_ACCESS register and the masks TPM_ACCESS_VALID,TPM_ACTIVE_LOCALITY 9249126SWyllys.Ingersoll@Sun.COM */ 9259126SWyllys.Ingersoll@Sun.COM static int 9269126SWyllys.Ingersoll@Sun.COM tis_check_active_locality(tpm_state_t *tpm, char locality) { 9279126SWyllys.Ingersoll@Sun.COM uint8_t access_bits; 9289126SWyllys.Ingersoll@Sun.COM 9299126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && locality >= 0 && locality < 5); 9309126SWyllys.Ingersoll@Sun.COM 9319126SWyllys.Ingersoll@Sun.COM access_bits = ddi_get8(tpm->handle, 9329126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr+TPM_ACCESS_(locality))); 9339126SWyllys.Ingersoll@Sun.COM access_bits &= (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID); 9349126SWyllys.Ingersoll@Sun.COM 9359126SWyllys.Ingersoll@Sun.COM if (access_bits == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) 9369126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 9379126SWyllys.Ingersoll@Sun.COM else 9389126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 9399126SWyllys.Ingersoll@Sun.COM } 9409126SWyllys.Ingersoll@Sun.COM 9419126SWyllys.Ingersoll@Sun.COM /* Request the TPM to be in the given locality */ 9429126SWyllys.Ingersoll@Sun.COM static int 9439126SWyllys.Ingersoll@Sun.COM tis_request_locality(tpm_state_t *tpm, char locality) { 9449126SWyllys.Ingersoll@Sun.COM clock_t timeout; 9459126SWyllys.Ingersoll@Sun.COM int ret; 9469126SWyllys.Ingersoll@Sun.COM char *myname = "tis_request_locality"; 9479126SWyllys.Ingersoll@Sun.COM 9489126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && locality >= 0 && locality < 5); 9499126SWyllys.Ingersoll@Sun.COM 9509126SWyllys.Ingersoll@Sun.COM ret = tis_check_active_locality(tpm, locality); 9519126SWyllys.Ingersoll@Sun.COM 9529126SWyllys.Ingersoll@Sun.COM if (ret == DDI_SUCCESS) { 9539126SWyllys.Ingersoll@Sun.COM /* Locality is already active */ 9549126SWyllys.Ingersoll@Sun.COM tpm->locality = locality; 9559126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 9569126SWyllys.Ingersoll@Sun.COM } 9579126SWyllys.Ingersoll@Sun.COM 9589126SWyllys.Ingersoll@Sun.COM ddi_put8(tpm->handle, tpm->addr+TPM_ACCESS_(locality), 9599126SWyllys.Ingersoll@Sun.COM TPM_ACCESS_REQUEST_USE); 9609126SWyllys.Ingersoll@Sun.COM timeout = ddi_get_lbolt() + tpm->timeout_a; 9619126SWyllys.Ingersoll@Sun.COM 9629126SWyllys.Ingersoll@Sun.COM /* Using polling */ 9639126SWyllys.Ingersoll@Sun.COM while (tis_check_active_locality(tpm, locality) 9649126SWyllys.Ingersoll@Sun.COM != DDI_SUCCESS) { 9659126SWyllys.Ingersoll@Sun.COM if (ddi_get_lbolt() >= timeout) { 9669126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s (interrupt-disabled) " 9679126SWyllys.Ingersoll@Sun.COM "tis_request_locality timed out", 9689126SWyllys.Ingersoll@Sun.COM myname); 9699126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 9709126SWyllys.Ingersoll@Sun.COM } 9719126SWyllys.Ingersoll@Sun.COM delay(tpm->timeout_poll); 9729126SWyllys.Ingersoll@Sun.COM } 9739126SWyllys.Ingersoll@Sun.COM 9749126SWyllys.Ingersoll@Sun.COM tpm->locality = locality; 9759126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 9769126SWyllys.Ingersoll@Sun.COM } 9779126SWyllys.Ingersoll@Sun.COM 9789126SWyllys.Ingersoll@Sun.COM /* Read the status register */ 9799126SWyllys.Ingersoll@Sun.COM static uint8_t 9809126SWyllys.Ingersoll@Sun.COM tis_get_status(tpm_state_t *tpm) { 9819126SWyllys.Ingersoll@Sun.COM return (ddi_get8(tpm->handle, 9829126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality)))); 9839126SWyllys.Ingersoll@Sun.COM } 9849126SWyllys.Ingersoll@Sun.COM 9859126SWyllys.Ingersoll@Sun.COM static int 9869126SWyllys.Ingersoll@Sun.COM tpm_wait_for_stat(tpm_state_t *tpm, uint8_t mask, clock_t absolute_timeout) { 9879126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_wait_for_stat"; 9889126SWyllys.Ingersoll@Sun.COM 9899126SWyllys.Ingersoll@Sun.COM /* Using polling */ 9909126SWyllys.Ingersoll@Sun.COM while ((tis_get_status(tpm) & mask) != mask) { 9919126SWyllys.Ingersoll@Sun.COM if (ddi_get_lbolt() >= absolute_timeout) { 9929126SWyllys.Ingersoll@Sun.COM /* Timeout reached */ 9939126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: using " 9949126SWyllys.Ingersoll@Sun.COM "polling:reached timeout", 9959126SWyllys.Ingersoll@Sun.COM myname); 9969126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 9979126SWyllys.Ingersoll@Sun.COM } 9989126SWyllys.Ingersoll@Sun.COM delay(tpm->timeout_poll); 9999126SWyllys.Ingersoll@Sun.COM } 10009126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 10019126SWyllys.Ingersoll@Sun.COM } 10029126SWyllys.Ingersoll@Sun.COM 10039126SWyllys.Ingersoll@Sun.COM /* 10049126SWyllys.Ingersoll@Sun.COM * Initialize TPM device 10059126SWyllys.Ingersoll@Sun.COM * 1. Find out supported interrupt capabilities 10069126SWyllys.Ingersoll@Sun.COM * 2. Set up interrupt handler if supported (some BIOSes don't support 10079126SWyllys.Ingersoll@Sun.COM * interrupts for TPMS, in which case we set up polling) 10089126SWyllys.Ingersoll@Sun.COM * 3. Determine timeouts and commands duration 10099126SWyllys.Ingersoll@Sun.COM */ 10109126SWyllys.Ingersoll@Sun.COM static int 10119126SWyllys.Ingersoll@Sun.COM tis_init(tpm_state_t *tpm) { 10129126SWyllys.Ingersoll@Sun.COM uint32_t intf_caps; 10139126SWyllys.Ingersoll@Sun.COM int ret; 10149126SWyllys.Ingersoll@Sun.COM char *myname = "tis_init"; 10159126SWyllys.Ingersoll@Sun.COM uintptr_t aptr = (uintptr_t)tpm->addr; 10169126SWyllys.Ingersoll@Sun.COM 10179126SWyllys.Ingersoll@Sun.COM /* 10189126SWyllys.Ingersoll@Sun.COM * Temporarily set up timeouts before we get the real timeouts 10199126SWyllys.Ingersoll@Sun.COM * by issuing TPM_CAP commands (but to issue TPM_CAP commands, 10209126SWyllys.Ingersoll@Sun.COM * you need TIMEOUTs defined...chicken and egg problem here. 10219126SWyllys.Ingersoll@Sun.COM * TPM timeouts: Convert the milliseconds to clock cycles 10229126SWyllys.Ingersoll@Sun.COM */ 10239126SWyllys.Ingersoll@Sun.COM tpm->timeout_a = drv_usectohz(TIS_TIMEOUT_A); 10249126SWyllys.Ingersoll@Sun.COM tpm->timeout_b = drv_usectohz(TIS_TIMEOUT_B); 10259126SWyllys.Ingersoll@Sun.COM tpm->timeout_c = drv_usectohz(TIS_TIMEOUT_C); 10269126SWyllys.Ingersoll@Sun.COM tpm->timeout_d = drv_usectohz(TIS_TIMEOUT_D); 10279126SWyllys.Ingersoll@Sun.COM /* 10289126SWyllys.Ingersoll@Sun.COM * Do the same with the duration (real duration will be filled out 10299126SWyllys.Ingersoll@Sun.COM * when we call TPM_GetCapability to get the duration values from 10309126SWyllys.Ingersoll@Sun.COM * the TPM itself). 10319126SWyllys.Ingersoll@Sun.COM */ 10329126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_SHORT] = drv_usectohz(TPM_DEFAULT_DURATION); 10339126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_MEDIUM] = drv_usectohz(TPM_DEFAULT_DURATION); 10349126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_LONG] = drv_usectohz(TPM_DEFAULT_DURATION); 10359126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_UNDEFINED] = drv_usectohz(TPM_DEFAULT_DURATION); 10369126SWyllys.Ingersoll@Sun.COM 10379126SWyllys.Ingersoll@Sun.COM /* Find out supported capabilities */ 10389126SWyllys.Ingersoll@Sun.COM intf_caps = ddi_get32(tpm->handle, 10399126SWyllys.Ingersoll@Sun.COM (uint32_t *)(aptr + TPM_INTF_CAP_(0))); 10409126SWyllys.Ingersoll@Sun.COM 10419126SWyllys.Ingersoll@Sun.COM /* Upper 3 bytes should always return 0 */ 10429126SWyllys.Ingersoll@Sun.COM if (intf_caps & 0x7FFFFF00) { 10439126SWyllys.Ingersoll@Sun.COM #ifdef DEBUG 10449126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: bad intf_caps value 0x%0X", 10459126SWyllys.Ingersoll@Sun.COM myname, intf_caps); 10469126SWyllys.Ingersoll@Sun.COM #endif 10479126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 10489126SWyllys.Ingersoll@Sun.COM } 10499126SWyllys.Ingersoll@Sun.COM 10509126SWyllys.Ingersoll@Sun.COM /* These two interrupts are mandatory */ 10519126SWyllys.Ingersoll@Sun.COM if (!(intf_caps & TPM_INTF_INT_LOCALITY_CHANGE_INT)) { 10529126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: Mandatory capability Locality Change Int " 10539126SWyllys.Ingersoll@Sun.COM "not supported", myname); 10549126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 10559126SWyllys.Ingersoll@Sun.COM } 10569126SWyllys.Ingersoll@Sun.COM if (!(intf_caps & TPM_INTF_INT_DATA_AVAIL_INT)) { 10579126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: Mandatory capability Data Available Int " 10589126SWyllys.Ingersoll@Sun.COM "not supported", myname); 10599126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 10609126SWyllys.Ingersoll@Sun.COM } 10619126SWyllys.Ingersoll@Sun.COM 10629126SWyllys.Ingersoll@Sun.COM /* 10639126SWyllys.Ingersoll@Sun.COM * Before we start writing anything to TPM's registers, 10649126SWyllys.Ingersoll@Sun.COM * make sure we are in locality 0 10659126SWyllys.Ingersoll@Sun.COM */ 10669126SWyllys.Ingersoll@Sun.COM ret = tis_request_locality(tpm, 0); 10679126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 10689126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: Unable to request locality 0", myname); 10699126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 10709126SWyllys.Ingersoll@Sun.COM } /* Now we can refer to the locality as tpm->locality */ 10719126SWyllys.Ingersoll@Sun.COM 10729126SWyllys.Ingersoll@Sun.COM tpm->timeout_poll = drv_usectohz(TPM_POLLING_TIMEOUT); 10739126SWyllys.Ingersoll@Sun.COM tpm->intr_enabled = 0; 10749126SWyllys.Ingersoll@Sun.COM 10759126SWyllys.Ingersoll@Sun.COM /* Get the real timeouts from the TPM */ 10769126SWyllys.Ingersoll@Sun.COM ret = tpm_get_timeouts(tpm); 10779126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 10789126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_get_timeouts error", myname); 10799126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 10809126SWyllys.Ingersoll@Sun.COM } 10819126SWyllys.Ingersoll@Sun.COM 10829126SWyllys.Ingersoll@Sun.COM ret = tpm_get_duration(tpm); 10839126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 10849126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_get_duration error", myname); 10859126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 10869126SWyllys.Ingersoll@Sun.COM } 10879126SWyllys.Ingersoll@Sun.COM 10889126SWyllys.Ingersoll@Sun.COM /* This gets the TPM version information */ 10899126SWyllys.Ingersoll@Sun.COM ret = tpm_get_version(tpm); 10909126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 10919126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_get_version error", myname); 10929126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 10939126SWyllys.Ingersoll@Sun.COM } 10949126SWyllys.Ingersoll@Sun.COM 10959126SWyllys.Ingersoll@Sun.COM /* 10969126SWyllys.Ingersoll@Sun.COM * Unless the TPM completes the test of its commands, 10979126SWyllys.Ingersoll@Sun.COM * it can return an error when the untested commands are called 10989126SWyllys.Ingersoll@Sun.COM */ 10999126SWyllys.Ingersoll@Sun.COM ret = tpm_continue_selftest(tpm); 11009126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 11019126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_continue_selftest error", myname); 11029126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 11039126SWyllys.Ingersoll@Sun.COM } 11049126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 11059126SWyllys.Ingersoll@Sun.COM } 11069126SWyllys.Ingersoll@Sun.COM 11079126SWyllys.Ingersoll@Sun.COM /* 11089126SWyllys.Ingersoll@Sun.COM * Module Entry points 11099126SWyllys.Ingersoll@Sun.COM */ 11109126SWyllys.Ingersoll@Sun.COM int 11119126SWyllys.Ingersoll@Sun.COM _init(void) 11129126SWyllys.Ingersoll@Sun.COM { 11139126SWyllys.Ingersoll@Sun.COM int ret; 11149126SWyllys.Ingersoll@Sun.COM 11159126SWyllys.Ingersoll@Sun.COM ret = ddi_soft_state_init(&statep, sizeof (tpm_state_t), 1); 11169126SWyllys.Ingersoll@Sun.COM if (ret) 11179126SWyllys.Ingersoll@Sun.COM return (ret); 11189126SWyllys.Ingersoll@Sun.COM 11199126SWyllys.Ingersoll@Sun.COM ret = mod_install(&tpm_ml); 11209126SWyllys.Ingersoll@Sun.COM if (ret != 0) { 11219126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "_init: mod_install returned non-zero"); 11229126SWyllys.Ingersoll@Sun.COM ddi_soft_state_fini(&statep); 11239126SWyllys.Ingersoll@Sun.COM return (ret); 11249126SWyllys.Ingersoll@Sun.COM } 11259126SWyllys.Ingersoll@Sun.COM 11269126SWyllys.Ingersoll@Sun.COM return (ret); 11279126SWyllys.Ingersoll@Sun.COM } 11289126SWyllys.Ingersoll@Sun.COM 11299126SWyllys.Ingersoll@Sun.COM int 11309126SWyllys.Ingersoll@Sun.COM _info(struct modinfo *modinfop) 11319126SWyllys.Ingersoll@Sun.COM { 11329126SWyllys.Ingersoll@Sun.COM int ret; 11339126SWyllys.Ingersoll@Sun.COM ret = mod_info(&tpm_ml, modinfop); 11349126SWyllys.Ingersoll@Sun.COM if (ret == 0) 11359126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "mod_info failed: %d", ret); 11369126SWyllys.Ingersoll@Sun.COM 11379126SWyllys.Ingersoll@Sun.COM return (ret); 11389126SWyllys.Ingersoll@Sun.COM } 11399126SWyllys.Ingersoll@Sun.COM 11409126SWyllys.Ingersoll@Sun.COM int 11419126SWyllys.Ingersoll@Sun.COM _fini() 11429126SWyllys.Ingersoll@Sun.COM { 11439126SWyllys.Ingersoll@Sun.COM int ret; 11449126SWyllys.Ingersoll@Sun.COM ret = mod_remove(&tpm_ml); 11459126SWyllys.Ingersoll@Sun.COM if (ret != 0) { 11469126SWyllys.Ingersoll@Sun.COM return (ret); 11479126SWyllys.Ingersoll@Sun.COM } 11489126SWyllys.Ingersoll@Sun.COM ddi_soft_state_fini(&statep); 11499126SWyllys.Ingersoll@Sun.COM 11509126SWyllys.Ingersoll@Sun.COM return (ret); 11519126SWyllys.Ingersoll@Sun.COM } 11529126SWyllys.Ingersoll@Sun.COM /* End of driver configuration functions */ 11539126SWyllys.Ingersoll@Sun.COM 11549126SWyllys.Ingersoll@Sun.COM static int 11559126SWyllys.Ingersoll@Sun.COM tpm_resume(tpm_state_t *tpm) 11569126SWyllys.Ingersoll@Sun.COM { 11579126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 11589126SWyllys.Ingersoll@Sun.COM if (!tpm->suspended) { 11599126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 11609126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 11619126SWyllys.Ingersoll@Sun.COM } 11629126SWyllys.Ingersoll@Sun.COM tpm->suspended = 0; 11639126SWyllys.Ingersoll@Sun.COM cv_broadcast(&tpm->suspend_cv); 11649126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 11659126SWyllys.Ingersoll@Sun.COM 11669126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 11679126SWyllys.Ingersoll@Sun.COM } 11689126SWyllys.Ingersoll@Sun.COM 11699126SWyllys.Ingersoll@Sun.COM /* 11709126SWyllys.Ingersoll@Sun.COM * Sun DDI/DDK entry points 11719126SWyllys.Ingersoll@Sun.COM */ 11729126SWyllys.Ingersoll@Sun.COM static int 11739126SWyllys.Ingersoll@Sun.COM tpm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 11749126SWyllys.Ingersoll@Sun.COM { 11759126SWyllys.Ingersoll@Sun.COM int ret, idx; 11769126SWyllys.Ingersoll@Sun.COM int instance; 11779126SWyllys.Ingersoll@Sun.COM int nregs; 11789126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_attach"; 11799126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm = NULL; 11809126SWyllys.Ingersoll@Sun.COM 11819126SWyllys.Ingersoll@Sun.COM ASSERT(dip != NULL); 11829126SWyllys.Ingersoll@Sun.COM 11839126SWyllys.Ingersoll@Sun.COM instance = ddi_get_instance(dip); 11849126SWyllys.Ingersoll@Sun.COM 11859126SWyllys.Ingersoll@Sun.COM /* Nothing out of ordinary here */ 11869126SWyllys.Ingersoll@Sun.COM switch (cmd) { 11879126SWyllys.Ingersoll@Sun.COM case DDI_ATTACH: 11889126SWyllys.Ingersoll@Sun.COM ret = ddi_soft_state_zalloc(statep, instance); 11899126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 11909126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s could not allocate tpm_state_t", 11919126SWyllys.Ingersoll@Sun.COM myname); 11929126SWyllys.Ingersoll@Sun.COM goto FAIL; 11939126SWyllys.Ingersoll@Sun.COM } 11949126SWyllys.Ingersoll@Sun.COM tpm = ddi_get_soft_state(statep, instance); 11959126SWyllys.Ingersoll@Sun.COM tpm->dip = dip; 11969126SWyllys.Ingersoll@Sun.COM break; 11979126SWyllys.Ingersoll@Sun.COM case DDI_RESUME: 11989126SWyllys.Ingersoll@Sun.COM tpm = ddi_get_soft_state(statep, instance); 11999126SWyllys.Ingersoll@Sun.COM if (tpm == NULL) { 12009126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_state_t is NULL", 12019126SWyllys.Ingersoll@Sun.COM myname); 12029126SWyllys.Ingersoll@Sun.COM goto FAIL; 12039126SWyllys.Ingersoll@Sun.COM } 12049126SWyllys.Ingersoll@Sun.COM return (tpm_resume(tpm)); 12059126SWyllys.Ingersoll@Sun.COM default: 12069126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: cmd %d is not implemented", myname, cmd); 12079126SWyllys.Ingersoll@Sun.COM ret = DDI_FAILURE; 12089126SWyllys.Ingersoll@Sun.COM goto FAIL; 12099126SWyllys.Ingersoll@Sun.COM } 12109126SWyllys.Ingersoll@Sun.COM 12119126SWyllys.Ingersoll@Sun.COM /* Zeroize the flag, which is used to keep track of what is allocated */ 12129126SWyllys.Ingersoll@Sun.COM tpm->flags = 0; 12139126SWyllys.Ingersoll@Sun.COM 12149126SWyllys.Ingersoll@Sun.COM tpm->accattr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 12159126SWyllys.Ingersoll@Sun.COM tpm->accattr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; 12169126SWyllys.Ingersoll@Sun.COM tpm->accattr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 12179126SWyllys.Ingersoll@Sun.COM 12189126SWyllys.Ingersoll@Sun.COM idx = 0; 12199126SWyllys.Ingersoll@Sun.COM ret = ddi_dev_nregs(tpm->dip, &nregs); 12209126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) 12219126SWyllys.Ingersoll@Sun.COM goto FAIL; 12229126SWyllys.Ingersoll@Sun.COM 12239126SWyllys.Ingersoll@Sun.COM #ifdef DEBUG 12249126SWyllys.Ingersoll@Sun.COM cmn_err(CE_NOTE, "num registers = %d", nregs); 12259126SWyllys.Ingersoll@Sun.COM #endif 12269126SWyllys.Ingersoll@Sun.COM /* 12279126SWyllys.Ingersoll@Sun.COM * TPM vendors put the TPM registers in different 12289126SWyllys.Ingersoll@Sun.COM * slots in their register lists. They are not always 12299126SWyllys.Ingersoll@Sun.COM * the 1st set of registers, for instance. 12309126SWyllys.Ingersoll@Sun.COM * Loop until we find the set that matches the expected 12319126SWyllys.Ingersoll@Sun.COM * register size (0x5000). 12329126SWyllys.Ingersoll@Sun.COM */ 12339126SWyllys.Ingersoll@Sun.COM for (idx = 0; idx < nregs; idx++) { 12349126SWyllys.Ingersoll@Sun.COM off_t regsize; 12359126SWyllys.Ingersoll@Sun.COM 12369126SWyllys.Ingersoll@Sun.COM if ((ret = ddi_dev_regsize(tpm->dip, idx, ®size)) != 12379126SWyllys.Ingersoll@Sun.COM DDI_SUCCESS) 12389126SWyllys.Ingersoll@Sun.COM goto FAIL; 12399126SWyllys.Ingersoll@Sun.COM #ifdef DEBUG 12409126SWyllys.Ingersoll@Sun.COM cmn_err(CE_NOTE, "register set #%d size = 0x%0lX", idx, 12419126SWyllys.Ingersoll@Sun.COM regsize); 12429126SWyllys.Ingersoll@Sun.COM #endif 12439126SWyllys.Ingersoll@Sun.COM /* The TIS spec says the TPM registers must be 0x5000 bytes */ 12449126SWyllys.Ingersoll@Sun.COM if (regsize == 0x5000) 12459126SWyllys.Ingersoll@Sun.COM break; 12469126SWyllys.Ingersoll@Sun.COM } 1247*9382SWyllys.Ingersoll@Sun.COM if (idx == nregs) 1248*9382SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1249*9382SWyllys.Ingersoll@Sun.COM 12509126SWyllys.Ingersoll@Sun.COM ret = ddi_regs_map_setup(tpm->dip, idx, (caddr_t *)&tpm->addr, 12519126SWyllys.Ingersoll@Sun.COM (offset_t)0, (offset_t)0x5000, 12529126SWyllys.Ingersoll@Sun.COM &tpm->accattr, &tpm->handle); 12539126SWyllys.Ingersoll@Sun.COM 12549126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 12559126SWyllys.Ingersoll@Sun.COM goto FAIL; 12569126SWyllys.Ingersoll@Sun.COM } 12579126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DIDREGSMAP; 12589126SWyllys.Ingersoll@Sun.COM 12599126SWyllys.Ingersoll@Sun.COM /* Enable TPM device according to the TIS specification */ 12609126SWyllys.Ingersoll@Sun.COM ret = tis_init(tpm); 12619126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 12629126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_init() failed ret: %d", 12639126SWyllys.Ingersoll@Sun.COM myname, ret); 12649126SWyllys.Ingersoll@Sun.COM 12659126SWyllys.Ingersoll@Sun.COM /* We need to clean up the ddi_regs_map_setup call */ 12669126SWyllys.Ingersoll@Sun.COM ddi_regs_map_free(&tpm->handle); 12679126SWyllys.Ingersoll@Sun.COM tpm->handle = NULL; 12689126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~TPM_DIDREGSMAP; 12699126SWyllys.Ingersoll@Sun.COM goto FAIL; 12709126SWyllys.Ingersoll@Sun.COM } 12719126SWyllys.Ingersoll@Sun.COM 12729126SWyllys.Ingersoll@Sun.COM /* Initialize the inter-process lock */ 12739126SWyllys.Ingersoll@Sun.COM mutex_init(&tpm->dev_lock, NULL, MUTEX_DRIVER, NULL); 12749126SWyllys.Ingersoll@Sun.COM mutex_init(&tpm->pm_mutex, NULL, MUTEX_DRIVER, NULL); 12759126SWyllys.Ingersoll@Sun.COM cv_init(&tpm->suspend_cv, NULL, CV_DRIVER, NULL); 12769126SWyllys.Ingersoll@Sun.COM 12779126SWyllys.Ingersoll@Sun.COM /* Set the suspend/resume property */ 12789126SWyllys.Ingersoll@Sun.COM (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, 12799126SWyllys.Ingersoll@Sun.COM "pm-hardware-state", "needs-suspend-resume"); 12809126SWyllys.Ingersoll@Sun.COM 12819126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 12829126SWyllys.Ingersoll@Sun.COM tpm->suspended = 0; 12839126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 12849126SWyllys.Ingersoll@Sun.COM 12859126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DID_MUTEX; 12869126SWyllys.Ingersoll@Sun.COM 12879126SWyllys.Ingersoll@Sun.COM /* Initialize the buffer and the lock associated with it */ 12889126SWyllys.Ingersoll@Sun.COM tpm->bufsize = TPM_IO_BUF_SIZE; 12899126SWyllys.Ingersoll@Sun.COM tpm->iobuf = kmem_zalloc((sizeof (uint8_t))*(tpm->bufsize), KM_SLEEP); 12909126SWyllys.Ingersoll@Sun.COM if (tpm->iobuf == NULL) { 12919126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: failed to allocate iobuf", myname); 12929126SWyllys.Ingersoll@Sun.COM goto FAIL; 12939126SWyllys.Ingersoll@Sun.COM } 12949126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DID_IO_ALLOC; 12959126SWyllys.Ingersoll@Sun.COM 12969126SWyllys.Ingersoll@Sun.COM mutex_init(&tpm->iobuf_lock, NULL, MUTEX_DRIVER, NULL); 12979126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DID_IO_MUTEX; 12989126SWyllys.Ingersoll@Sun.COM 12999126SWyllys.Ingersoll@Sun.COM cv_init(&tpm->iobuf_cv, NULL, CV_DRIVER, NULL); 13009126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DID_IO_CV; 13019126SWyllys.Ingersoll@Sun.COM 13029126SWyllys.Ingersoll@Sun.COM /* Create minor node */ 13039126SWyllys.Ingersoll@Sun.COM ret = ddi_create_minor_node(dip, "tpm", S_IFCHR, ddi_get_instance(dip), 13049126SWyllys.Ingersoll@Sun.COM DDI_PSEUDO, 0); 13059126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 13069126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: ddi_create_minor_node failed", myname); 13079126SWyllys.Ingersoll@Sun.COM goto FAIL; 13089126SWyllys.Ingersoll@Sun.COM } 13099126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DIDMINOR; 13109126SWyllys.Ingersoll@Sun.COM 13119126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 13129126SWyllys.Ingersoll@Sun.COM FAIL: 13139126SWyllys.Ingersoll@Sun.COM if (tpm != NULL) { 13149126SWyllys.Ingersoll@Sun.COM tpm_cleanup(dip, tpm); 13159126SWyllys.Ingersoll@Sun.COM ddi_soft_state_free(statep, instance); 13169126SWyllys.Ingersoll@Sun.COM tpm = NULL; 13179126SWyllys.Ingersoll@Sun.COM } 13189126SWyllys.Ingersoll@Sun.COM 13199126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 13209126SWyllys.Ingersoll@Sun.COM } 13219126SWyllys.Ingersoll@Sun.COM 13229126SWyllys.Ingersoll@Sun.COM /* 13239126SWyllys.Ingersoll@Sun.COM * Called by tpm_detach and tpm_attach (only on failure) 13249126SWyllys.Ingersoll@Sun.COM * Free up the resources that are allocated 13259126SWyllys.Ingersoll@Sun.COM */ 13269126SWyllys.Ingersoll@Sun.COM static void 13279126SWyllys.Ingersoll@Sun.COM tpm_cleanup(dev_info_t *dip, tpm_state_t *tpm) 13289126SWyllys.Ingersoll@Sun.COM { 13299126SWyllys.Ingersoll@Sun.COM if (tpm == NULL) 13309126SWyllys.Ingersoll@Sun.COM return; 13319126SWyllys.Ingersoll@Sun.COM 13329126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DID_MUTEX) { 13339126SWyllys.Ingersoll@Sun.COM mutex_destroy(&tpm->dev_lock); 13349126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DID_MUTEX); 13359126SWyllys.Ingersoll@Sun.COM } 13369126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DID_IO_ALLOC) { 13379126SWyllys.Ingersoll@Sun.COM ASSERT(tpm->iobuf != NULL); 13389126SWyllys.Ingersoll@Sun.COM kmem_free(tpm->iobuf, (sizeof (uint8_t))*(tpm->bufsize)); 13399126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DID_IO_ALLOC); 13409126SWyllys.Ingersoll@Sun.COM } 13419126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DID_IO_MUTEX) { 13429126SWyllys.Ingersoll@Sun.COM mutex_destroy(&tpm->iobuf_lock); 13439126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DID_IO_MUTEX); 13449126SWyllys.Ingersoll@Sun.COM } 13459126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DID_IO_CV) { 13469126SWyllys.Ingersoll@Sun.COM cv_destroy(&tpm->iobuf_cv); 13479126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DID_IO_CV); 13489126SWyllys.Ingersoll@Sun.COM } 13499126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DIDREGSMAP) { 13509126SWyllys.Ingersoll@Sun.COM /* Free the mapped addresses */ 13519126SWyllys.Ingersoll@Sun.COM if (tpm->handle != NULL) 13529126SWyllys.Ingersoll@Sun.COM ddi_regs_map_free(&tpm->handle); 13539126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DIDREGSMAP); 13549126SWyllys.Ingersoll@Sun.COM } 13559126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DIDMINOR) { 13569126SWyllys.Ingersoll@Sun.COM /* Remove minor node */ 13579126SWyllys.Ingersoll@Sun.COM ddi_remove_minor_node(dip, NULL); 13589126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DIDMINOR); 13599126SWyllys.Ingersoll@Sun.COM } 13609126SWyllys.Ingersoll@Sun.COM } 13619126SWyllys.Ingersoll@Sun.COM 13629126SWyllys.Ingersoll@Sun.COM static int 13639126SWyllys.Ingersoll@Sun.COM tpm_suspend(tpm_state_t *tpm) 13649126SWyllys.Ingersoll@Sun.COM { 13659126SWyllys.Ingersoll@Sun.COM if (tpm == NULL) 13669126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 13679126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 13689126SWyllys.Ingersoll@Sun.COM if (tpm->suspended) { 13699126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 13709126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 13719126SWyllys.Ingersoll@Sun.COM } 13729126SWyllys.Ingersoll@Sun.COM tpm->suspended = 1; 13739126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 13749126SWyllys.Ingersoll@Sun.COM 13759126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 13769126SWyllys.Ingersoll@Sun.COM } 13779126SWyllys.Ingersoll@Sun.COM 13789126SWyllys.Ingersoll@Sun.COM static int 13799126SWyllys.Ingersoll@Sun.COM tpm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 13809126SWyllys.Ingersoll@Sun.COM { 13819126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_detach"; 13829126SWyllys.Ingersoll@Sun.COM int instance; 13839126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 13849126SWyllys.Ingersoll@Sun.COM 13859126SWyllys.Ingersoll@Sun.COM ASSERT(dip != NULL); 13869126SWyllys.Ingersoll@Sun.COM 13879126SWyllys.Ingersoll@Sun.COM instance = ddi_get_instance(dip); 13889126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 13899126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 13909126SWyllys.Ingersoll@Sun.COM myname); 13919126SWyllys.Ingersoll@Sun.COM return (ENXIO); 13929126SWyllys.Ingersoll@Sun.COM } 13939126SWyllys.Ingersoll@Sun.COM 13949126SWyllys.Ingersoll@Sun.COM switch (cmd) { 13959126SWyllys.Ingersoll@Sun.COM case DDI_DETACH: 13969126SWyllys.Ingersoll@Sun.COM /* Body is after the switch stmt */ 13979126SWyllys.Ingersoll@Sun.COM break; 13989126SWyllys.Ingersoll@Sun.COM case DDI_SUSPEND: 13999126SWyllys.Ingersoll@Sun.COM return (tpm_suspend(tpm)); 14009126SWyllys.Ingersoll@Sun.COM default: 14019126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: case %d not implemented", myname, cmd); 14029126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 14039126SWyllys.Ingersoll@Sun.COM } 14049126SWyllys.Ingersoll@Sun.COM 14059126SWyllys.Ingersoll@Sun.COM /* Since we are freeing tpm structure, we need to gain the lock */ 14069126SWyllys.Ingersoll@Sun.COM 14079126SWyllys.Ingersoll@Sun.COM tpm_cleanup(dip, tpm); 14089126SWyllys.Ingersoll@Sun.COM 14099126SWyllys.Ingersoll@Sun.COM mutex_destroy(&tpm->pm_mutex); 14109126SWyllys.Ingersoll@Sun.COM cv_destroy(&tpm->suspend_cv); 14119126SWyllys.Ingersoll@Sun.COM 14129126SWyllys.Ingersoll@Sun.COM /* Free the soft state */ 14139126SWyllys.Ingersoll@Sun.COM ddi_soft_state_free(statep, instance); 14149126SWyllys.Ingersoll@Sun.COM tpm = NULL; 14159126SWyllys.Ingersoll@Sun.COM 14169126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 14179126SWyllys.Ingersoll@Sun.COM } 14189126SWyllys.Ingersoll@Sun.COM 14199126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 14209126SWyllys.Ingersoll@Sun.COM static int 14219126SWyllys.Ingersoll@Sun.COM tpm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 14229126SWyllys.Ingersoll@Sun.COM { 14239126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_getinfo"; 14249126SWyllys.Ingersoll@Sun.COM int instance; 14259126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 14269126SWyllys.Ingersoll@Sun.COM 14279126SWyllys.Ingersoll@Sun.COM instance = ddi_get_instance(dip); 14289126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 14299126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 14309126SWyllys.Ingersoll@Sun.COM myname); 14319126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 14329126SWyllys.Ingersoll@Sun.COM } 14339126SWyllys.Ingersoll@Sun.COM 14349126SWyllys.Ingersoll@Sun.COM switch (cmd) { 14359126SWyllys.Ingersoll@Sun.COM case DDI_INFO_DEVT2DEVINFO: 14369126SWyllys.Ingersoll@Sun.COM *resultp = tpm->dip; 14379126SWyllys.Ingersoll@Sun.COM break; 14389126SWyllys.Ingersoll@Sun.COM case DDI_INFO_DEVT2INSTANCE: 14399126SWyllys.Ingersoll@Sun.COM *resultp = 0; 14409126SWyllys.Ingersoll@Sun.COM break; 14419126SWyllys.Ingersoll@Sun.COM default: 14429126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: cmd %d is not implemented", myname, cmd); 14439126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 14449126SWyllys.Ingersoll@Sun.COM } 14459126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 14469126SWyllys.Ingersoll@Sun.COM } 14479126SWyllys.Ingersoll@Sun.COM 14489126SWyllys.Ingersoll@Sun.COM /* 14499126SWyllys.Ingersoll@Sun.COM * Driver entry points 14509126SWyllys.Ingersoll@Sun.COM */ 14519126SWyllys.Ingersoll@Sun.COM 14529126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 14539126SWyllys.Ingersoll@Sun.COM static int 14549126SWyllys.Ingersoll@Sun.COM tpm_open(dev_t *devp, int flag, int otyp, cred_t *cred) 14559126SWyllys.Ingersoll@Sun.COM { 14569126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_open"; 14579126SWyllys.Ingersoll@Sun.COM int instance; 14589126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 14599126SWyllys.Ingersoll@Sun.COM 14609126SWyllys.Ingersoll@Sun.COM ASSERT(devp != NULL); 14619126SWyllys.Ingersoll@Sun.COM 14629126SWyllys.Ingersoll@Sun.COM instance = getminor(*devp); 14639126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 14649126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 14659126SWyllys.Ingersoll@Sun.COM myname); 14669126SWyllys.Ingersoll@Sun.COM return (ENXIO); 14679126SWyllys.Ingersoll@Sun.COM } 14689126SWyllys.Ingersoll@Sun.COM if (otyp != OTYP_CHR) { 14699126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: otyp(%d) != OTYP_CHR(%d)", 14709126SWyllys.Ingersoll@Sun.COM myname, otyp, OTYP_CHR); 14719126SWyllys.Ingersoll@Sun.COM return (EINVAL); 14729126SWyllys.Ingersoll@Sun.COM } 14739126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 14749126SWyllys.Ingersoll@Sun.COM while (tpm->suspended) 14759126SWyllys.Ingersoll@Sun.COM cv_wait(&tpm->suspend_cv, &tpm->pm_mutex); 14769126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 14779126SWyllys.Ingersoll@Sun.COM 14789126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->dev_lock); 14799126SWyllys.Ingersoll@Sun.COM if (tpm->dev_held) { 14809126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: the device is already being used", 14819126SWyllys.Ingersoll@Sun.COM myname); 14829126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->dev_lock); 14839126SWyllys.Ingersoll@Sun.COM return (EBUSY); 14849126SWyllys.Ingersoll@Sun.COM } 14859126SWyllys.Ingersoll@Sun.COM 14869126SWyllys.Ingersoll@Sun.COM /* The device is free so mark it busy */ 14879126SWyllys.Ingersoll@Sun.COM tpm->dev_held = 1; 14889126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->dev_lock); 14899126SWyllys.Ingersoll@Sun.COM 14909126SWyllys.Ingersoll@Sun.COM return (0); 14919126SWyllys.Ingersoll@Sun.COM } 14929126SWyllys.Ingersoll@Sun.COM 14939126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 14949126SWyllys.Ingersoll@Sun.COM static int 14959126SWyllys.Ingersoll@Sun.COM tpm_close(dev_t dev, int flag, int otyp, cred_t *cred) 14969126SWyllys.Ingersoll@Sun.COM { 14979126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_close"; 14989126SWyllys.Ingersoll@Sun.COM int instance; 14999126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 15009126SWyllys.Ingersoll@Sun.COM 15019126SWyllys.Ingersoll@Sun.COM instance = getminor(dev); 15029126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 15039126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 15049126SWyllys.Ingersoll@Sun.COM myname); 15059126SWyllys.Ingersoll@Sun.COM return (ENXIO); 15069126SWyllys.Ingersoll@Sun.COM } 15079126SWyllys.Ingersoll@Sun.COM if (otyp != OTYP_CHR) { 15089126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: otyp(%d) != OTYP_CHR(%d)", 15099126SWyllys.Ingersoll@Sun.COM myname, otyp, OTYP_CHR); 15109126SWyllys.Ingersoll@Sun.COM return (EINVAL); 15119126SWyllys.Ingersoll@Sun.COM } 15129126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 15139126SWyllys.Ingersoll@Sun.COM while (tpm->suspended) 15149126SWyllys.Ingersoll@Sun.COM cv_wait(&tpm->suspend_cv, &tpm->pm_mutex); 15159126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 15169126SWyllys.Ingersoll@Sun.COM 15179126SWyllys.Ingersoll@Sun.COM ASSERT(tpm->dev_held); 15189126SWyllys.Ingersoll@Sun.COM 15199126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->dev_lock); 15209126SWyllys.Ingersoll@Sun.COM ASSERT(mutex_owned(&tpm->dev_lock)); 15219126SWyllys.Ingersoll@Sun.COM tpm->dev_held = 0; 15229126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->dev_lock); 15239126SWyllys.Ingersoll@Sun.COM 15249126SWyllys.Ingersoll@Sun.COM return (0); 15259126SWyllys.Ingersoll@Sun.COM } 15269126SWyllys.Ingersoll@Sun.COM 15279126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 15289126SWyllys.Ingersoll@Sun.COM static int 15299126SWyllys.Ingersoll@Sun.COM tpm_read(dev_t dev, struct uio *uiop, cred_t *credp) 15309126SWyllys.Ingersoll@Sun.COM { 15319126SWyllys.Ingersoll@Sun.COM int ret; 15329126SWyllys.Ingersoll@Sun.COM uint32_t size; 15339126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_read"; 15349126SWyllys.Ingersoll@Sun.COM int instance; 15359126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 15369126SWyllys.Ingersoll@Sun.COM 15379126SWyllys.Ingersoll@Sun.COM instance = getminor(dev); 15389126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 15399126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 15409126SWyllys.Ingersoll@Sun.COM myname); 15419126SWyllys.Ingersoll@Sun.COM return (ENXIO); 15429126SWyllys.Ingersoll@Sun.COM } 15439126SWyllys.Ingersoll@Sun.COM if (uiop == NULL) { 15449126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: passed in uiop is NULL", myname); 15459126SWyllys.Ingersoll@Sun.COM return (EFAULT); 15469126SWyllys.Ingersoll@Sun.COM } 15479126SWyllys.Ingersoll@Sun.COM 15489126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 15499126SWyllys.Ingersoll@Sun.COM while (tpm->suspended) 15509126SWyllys.Ingersoll@Sun.COM cv_wait(&tpm->suspend_cv, &tpm->pm_mutex); 15519126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 15529126SWyllys.Ingersoll@Sun.COM 15539126SWyllys.Ingersoll@Sun.COM /* Receive the data after requiring the lock */ 15549126SWyllys.Ingersoll@Sun.COM ret = tpm_lock(tpm); 15559126SWyllys.Ingersoll@Sun.COM 15569126SWyllys.Ingersoll@Sun.COM /* Timeout reached */ 15579126SWyllys.Ingersoll@Sun.COM if (ret == ETIME) 15589126SWyllys.Ingersoll@Sun.COM return (ret); 15599126SWyllys.Ingersoll@Sun.COM 15609126SWyllys.Ingersoll@Sun.COM if (uiop->uio_resid > tpm->bufsize) { 15619126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: read_in data is bigger " 15629126SWyllys.Ingersoll@Sun.COM "than tpm->bufsize:read in:%d, bufsiz:%d", 15639126SWyllys.Ingersoll@Sun.COM myname, (int)uiop->uio_resid, (int)tpm->bufsize); 15649126SWyllys.Ingersoll@Sun.COM ret = EIO; 15659126SWyllys.Ingersoll@Sun.COM goto OUT; 15669126SWyllys.Ingersoll@Sun.COM } 15679126SWyllys.Ingersoll@Sun.COM 15689126SWyllys.Ingersoll@Sun.COM ret = tis_recv_data(tpm, tpm->iobuf, tpm->bufsize); 15699126SWyllys.Ingersoll@Sun.COM if (ret < TPM_HEADER_SIZE) { 15709126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_recv_data returned error", myname); 15719126SWyllys.Ingersoll@Sun.COM ret = EIO; 15729126SWyllys.Ingersoll@Sun.COM goto OUT; 15739126SWyllys.Ingersoll@Sun.COM } 15749126SWyllys.Ingersoll@Sun.COM 15759126SWyllys.Ingersoll@Sun.COM size = load32(tpm->iobuf, 2); 15769126SWyllys.Ingersoll@Sun.COM if (ret != size) { 15779126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_recv_data:" 15789126SWyllys.Ingersoll@Sun.COM "expected size=%d, actually read=%d", 15799126SWyllys.Ingersoll@Sun.COM myname, size, ret); 15809126SWyllys.Ingersoll@Sun.COM ret = EIO; 15819126SWyllys.Ingersoll@Sun.COM goto OUT; 15829126SWyllys.Ingersoll@Sun.COM } 15839126SWyllys.Ingersoll@Sun.COM 15849126SWyllys.Ingersoll@Sun.COM /* Send the buffer from the kernel to the userspace */ 15859126SWyllys.Ingersoll@Sun.COM ret = uiomove(tpm->iobuf, size, UIO_READ, uiop); 15869126SWyllys.Ingersoll@Sun.COM if (ret) { 15879126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: uiomove returned error", myname); 15889126SWyllys.Ingersoll@Sun.COM goto OUT; 15899126SWyllys.Ingersoll@Sun.COM } 15909126SWyllys.Ingersoll@Sun.COM 15919126SWyllys.Ingersoll@Sun.COM /* Zeroize the buffer... */ 15929126SWyllys.Ingersoll@Sun.COM bzero(tpm->iobuf, tpm->bufsize); 15939126SWyllys.Ingersoll@Sun.COM ret = DDI_SUCCESS; 15949126SWyllys.Ingersoll@Sun.COM OUT: 15959126SWyllys.Ingersoll@Sun.COM /* We are done now: wake up the waiting threads */ 15969126SWyllys.Ingersoll@Sun.COM tpm_unlock(tpm); 15979126SWyllys.Ingersoll@Sun.COM 15989126SWyllys.Ingersoll@Sun.COM return (ret); 15999126SWyllys.Ingersoll@Sun.COM } 16009126SWyllys.Ingersoll@Sun.COM 16019126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 16029126SWyllys.Ingersoll@Sun.COM static int 16039126SWyllys.Ingersoll@Sun.COM tpm_write(dev_t dev, struct uio *uiop, cred_t *credp) 16049126SWyllys.Ingersoll@Sun.COM { 16059126SWyllys.Ingersoll@Sun.COM int ret; 16069126SWyllys.Ingersoll@Sun.COM size_t len; 16079126SWyllys.Ingersoll@Sun.COM uint32_t size; 16089126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_write"; 16099126SWyllys.Ingersoll@Sun.COM int instance; 16109126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 16119126SWyllys.Ingersoll@Sun.COM 16129126SWyllys.Ingersoll@Sun.COM instance = getminor(dev); 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 (ENXIO); 16179126SWyllys.Ingersoll@Sun.COM } 16189126SWyllys.Ingersoll@Sun.COM 16199126SWyllys.Ingersoll@Sun.COM if (uiop == NULL) { 16209126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: passed in uiop is NULL", myname); 16219126SWyllys.Ingersoll@Sun.COM return (EFAULT); 16229126SWyllys.Ingersoll@Sun.COM } 16239126SWyllys.Ingersoll@Sun.COM 16249126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 16259126SWyllys.Ingersoll@Sun.COM while (tpm->suspended) 16269126SWyllys.Ingersoll@Sun.COM cv_wait(&tpm->suspend_cv, &tpm->pm_mutex); 16279126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 16289126SWyllys.Ingersoll@Sun.COM 16299126SWyllys.Ingersoll@Sun.COM len = uiop->uio_resid; 16309126SWyllys.Ingersoll@Sun.COM if (len == 0) { 16319126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: requested read of len 0", myname); 16329126SWyllys.Ingersoll@Sun.COM return (0); 16339126SWyllys.Ingersoll@Sun.COM } 16349126SWyllys.Ingersoll@Sun.COM 16359126SWyllys.Ingersoll@Sun.COM /* Get the lock for using iobuf */ 16369126SWyllys.Ingersoll@Sun.COM ret = tpm_lock(tpm); 16379126SWyllys.Ingersoll@Sun.COM /* Timeout Reached */ 16389126SWyllys.Ingersoll@Sun.COM if (ret == ETIME) 16399126SWyllys.Ingersoll@Sun.COM return (ret); 16409126SWyllys.Ingersoll@Sun.COM 16419126SWyllys.Ingersoll@Sun.COM /* Copy the header and parse the structure to find out the size... */ 16429126SWyllys.Ingersoll@Sun.COM ret = uiomove(tpm->iobuf, TPM_HEADER_SIZE, UIO_WRITE, uiop); 16439126SWyllys.Ingersoll@Sun.COM if (ret) { 16449126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: uiomove returned error" 16459126SWyllys.Ingersoll@Sun.COM "while getting the the header", 16469126SWyllys.Ingersoll@Sun.COM myname); 16479126SWyllys.Ingersoll@Sun.COM goto OUT; 16489126SWyllys.Ingersoll@Sun.COM } 16499126SWyllys.Ingersoll@Sun.COM 16509126SWyllys.Ingersoll@Sun.COM /* Get the buffersize from the command buffer structure */ 16519126SWyllys.Ingersoll@Sun.COM size = load32(tpm->iobuf, TPM_PARAMSIZE_OFFSET); 16529126SWyllys.Ingersoll@Sun.COM 16539126SWyllys.Ingersoll@Sun.COM /* Copy the command to the contiguous buffer */ 16549126SWyllys.Ingersoll@Sun.COM if (size > tpm->bufsize) { 16559126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: size %d is greater than " 16569126SWyllys.Ingersoll@Sun.COM "the tpm's input buffer size %d", 16579126SWyllys.Ingersoll@Sun.COM myname, (int)size, (int)tpm->bufsize); 16589126SWyllys.Ingersoll@Sun.COM ret = ENXIO; 16599126SWyllys.Ingersoll@Sun.COM goto OUT; 16609126SWyllys.Ingersoll@Sun.COM } 16619126SWyllys.Ingersoll@Sun.COM 16629126SWyllys.Ingersoll@Sun.COM /* Copy the buffer from the userspace to kernel */ 16639126SWyllys.Ingersoll@Sun.COM ret = uiomove(tpm->iobuf+TPM_HEADER_SIZE, size-TPM_HEADER_SIZE, 16649126SWyllys.Ingersoll@Sun.COM UIO_WRITE, uiop); 16659126SWyllys.Ingersoll@Sun.COM 16669126SWyllys.Ingersoll@Sun.COM if (ret) { 16679126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: uiomove returned error" 16689126SWyllys.Ingersoll@Sun.COM "while getting the rest of the command", myname); 16699126SWyllys.Ingersoll@Sun.COM goto OUT; 16709126SWyllys.Ingersoll@Sun.COM } 16719126SWyllys.Ingersoll@Sun.COM 16729126SWyllys.Ingersoll@Sun.COM /* Send the command */ 16739126SWyllys.Ingersoll@Sun.COM ret = tis_send_data(tpm, tpm->iobuf, size); 16749126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 16759126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_send_data returned error", myname); 16769126SWyllys.Ingersoll@Sun.COM ret = EFAULT; 16779126SWyllys.Ingersoll@Sun.COM goto OUT; 16789126SWyllys.Ingersoll@Sun.COM } 16799126SWyllys.Ingersoll@Sun.COM 16809126SWyllys.Ingersoll@Sun.COM /* Zeroize the buffer... */ 16819126SWyllys.Ingersoll@Sun.COM bzero(tpm->iobuf, tpm->bufsize); 16829126SWyllys.Ingersoll@Sun.COM ret = DDI_SUCCESS; 16839126SWyllys.Ingersoll@Sun.COM OUT: 16849126SWyllys.Ingersoll@Sun.COM tpm_unlock(tpm); 16859126SWyllys.Ingersoll@Sun.COM return (ret); 16869126SWyllys.Ingersoll@Sun.COM } 16879126SWyllys.Ingersoll@Sun.COM 16889126SWyllys.Ingersoll@Sun.COM /* 16899126SWyllys.Ingersoll@Sun.COM * This is to deal with the contentions for the iobuf 16909126SWyllys.Ingersoll@Sun.COM */ 16919126SWyllys.Ingersoll@Sun.COM static inline int 16929126SWyllys.Ingersoll@Sun.COM tpm_lock(tpm_state_t *tpm) 16939126SWyllys.Ingersoll@Sun.COM { 16949126SWyllys.Ingersoll@Sun.COM int ret; 16959126SWyllys.Ingersoll@Sun.COM clock_t timeout; 16969126SWyllys.Ingersoll@Sun.COM 16979126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->iobuf_lock); 16989126SWyllys.Ingersoll@Sun.COM ASSERT(mutex_owned(&tpm->iobuf_lock)); 16999126SWyllys.Ingersoll@Sun.COM 17009126SWyllys.Ingersoll@Sun.COM timeout = ddi_get_lbolt() + drv_usectohz(TPM_IO_TIMEOUT); 17019126SWyllys.Ingersoll@Sun.COM 17029126SWyllys.Ingersoll@Sun.COM /* Wait until the iobuf becomes free with the timeout */ 17039126SWyllys.Ingersoll@Sun.COM while (tpm->iobuf_inuse) { 17049126SWyllys.Ingersoll@Sun.COM ret = cv_timedwait(&tpm->iobuf_cv, &tpm->iobuf_lock, timeout); 17059126SWyllys.Ingersoll@Sun.COM if (ret <= 0) { 17069126SWyllys.Ingersoll@Sun.COM /* Timeout reached */ 17079126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->iobuf_lock); 17089126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "tpm_lock:iorequest timed out"); 17099126SWyllys.Ingersoll@Sun.COM return (ETIME); 17109126SWyllys.Ingersoll@Sun.COM } 17119126SWyllys.Ingersoll@Sun.COM } 17129126SWyllys.Ingersoll@Sun.COM tpm->iobuf_inuse = 1; 17139126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->iobuf_lock); 17149126SWyllys.Ingersoll@Sun.COM return (0); 17159126SWyllys.Ingersoll@Sun.COM } 17169126SWyllys.Ingersoll@Sun.COM 17179126SWyllys.Ingersoll@Sun.COM /* 17189126SWyllys.Ingersoll@Sun.COM * This is to deal with the contentions for the iobuf 17199126SWyllys.Ingersoll@Sun.COM */ 17209126SWyllys.Ingersoll@Sun.COM static inline void 17219126SWyllys.Ingersoll@Sun.COM tpm_unlock(tpm_state_t *tpm) 17229126SWyllys.Ingersoll@Sun.COM { 17239126SWyllys.Ingersoll@Sun.COM /* Wake up the waiting threads */ 17249126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->iobuf_lock); 17259126SWyllys.Ingersoll@Sun.COM ASSERT(tpm->iobuf_inuse == 1 && mutex_owned(&tpm->iobuf_lock)); 17269126SWyllys.Ingersoll@Sun.COM tpm->iobuf_inuse = 0; 17279126SWyllys.Ingersoll@Sun.COM cv_broadcast(&tpm->iobuf_cv); 17289126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->iobuf_lock); 17299126SWyllys.Ingersoll@Sun.COM } 1730