1*9126SWyllys.Ingersoll@Sun.COM /* 2*9126SWyllys.Ingersoll@Sun.COM * CDDL HEADER START 3*9126SWyllys.Ingersoll@Sun.COM * 4*9126SWyllys.Ingersoll@Sun.COM * The contents of this file are subject to the terms of the 5*9126SWyllys.Ingersoll@Sun.COM * Common Development and Distribution License (the "License"). 6*9126SWyllys.Ingersoll@Sun.COM * You may not use this file except in compliance with the License. 7*9126SWyllys.Ingersoll@Sun.COM * 8*9126SWyllys.Ingersoll@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*9126SWyllys.Ingersoll@Sun.COM * or http://www.opensolaris.org/os/licensing. 10*9126SWyllys.Ingersoll@Sun.COM * See the License for the specific language governing permissions 11*9126SWyllys.Ingersoll@Sun.COM * and limitations under the License. 12*9126SWyllys.Ingersoll@Sun.COM * 13*9126SWyllys.Ingersoll@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 14*9126SWyllys.Ingersoll@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*9126SWyllys.Ingersoll@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 16*9126SWyllys.Ingersoll@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 17*9126SWyllys.Ingersoll@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 18*9126SWyllys.Ingersoll@Sun.COM * 19*9126SWyllys.Ingersoll@Sun.COM * CDDL HEADER END 20*9126SWyllys.Ingersoll@Sun.COM */ 21*9126SWyllys.Ingersoll@Sun.COM 22*9126SWyllys.Ingersoll@Sun.COM /* 23*9126SWyllys.Ingersoll@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24*9126SWyllys.Ingersoll@Sun.COM * Use is subject to license terms. 25*9126SWyllys.Ingersoll@Sun.COM */ 26*9126SWyllys.Ingersoll@Sun.COM 27*9126SWyllys.Ingersoll@Sun.COM /* 28*9126SWyllys.Ingersoll@Sun.COM * TPM 1.2 Driver for the TPMs that follow TIS v1.2 29*9126SWyllys.Ingersoll@Sun.COM */ 30*9126SWyllys.Ingersoll@Sun.COM 31*9126SWyllys.Ingersoll@Sun.COM #include <sys/devops.h> /* used by dev_ops */ 32*9126SWyllys.Ingersoll@Sun.COM #include <sys/conf.h> /* used by dev_ops,cb_ops */ 33*9126SWyllys.Ingersoll@Sun.COM #include <sys/modctl.h> /* for _init,_info,_fini,mod_* */ 34*9126SWyllys.Ingersoll@Sun.COM #include <sys/ddi.h> /* used by all entry points */ 35*9126SWyllys.Ingersoll@Sun.COM #include <sys/sunddi.h> /* used by all entry points */ 36*9126SWyllys.Ingersoll@Sun.COM #include <sys/cmn_err.h> /* used for debug outputs */ 37*9126SWyllys.Ingersoll@Sun.COM #include <sys/types.h> /* used by prop_op, ddi_prop_op */ 38*9126SWyllys.Ingersoll@Sun.COM 39*9126SWyllys.Ingersoll@Sun.COM #include <sys/file.h> /* used by open, close */ 40*9126SWyllys.Ingersoll@Sun.COM #include <sys/errno.h> /* used by open,close,read,write */ 41*9126SWyllys.Ingersoll@Sun.COM #include <sys/open.h> /* used by open,close,read,write */ 42*9126SWyllys.Ingersoll@Sun.COM #include <sys/cred.h> /* used by open,close,read */ 43*9126SWyllys.Ingersoll@Sun.COM #include <sys/uio.h> /* used by read */ 44*9126SWyllys.Ingersoll@Sun.COM #include <sys/stat.h> /* defines S_IFCHR */ 45*9126SWyllys.Ingersoll@Sun.COM 46*9126SWyllys.Ingersoll@Sun.COM #include <sys/byteorder.h> /* for ntohs, ntohl, htons, htonl */ 47*9126SWyllys.Ingersoll@Sun.COM 48*9126SWyllys.Ingersoll@Sun.COM #include <tss/platform.h> /* from SUNWtss */ 49*9126SWyllys.Ingersoll@Sun.COM #include <tss/tpm.h> /* from SUNWtss */ 50*9126SWyllys.Ingersoll@Sun.COM 51*9126SWyllys.Ingersoll@Sun.COM #include "tpm_tis.h" 52*9126SWyllys.Ingersoll@Sun.COM #include "tpm_ddi.h" 53*9126SWyllys.Ingersoll@Sun.COM #include "tpm_duration.h" 54*9126SWyllys.Ingersoll@Sun.COM 55*9126SWyllys.Ingersoll@Sun.COM #define TPM_HEADER_SIZE 10 56*9126SWyllys.Ingersoll@Sun.COM typedef enum { 57*9126SWyllys.Ingersoll@Sun.COM TPM_TAG_OFFSET = 0, 58*9126SWyllys.Ingersoll@Sun.COM TPM_PARAMSIZE_OFFSET = 2, 59*9126SWyllys.Ingersoll@Sun.COM TPM_RETURN_OFFSET = 6, 60*9126SWyllys.Ingersoll@Sun.COM TPM_COMMAND_CODE_OFFSET = 6, 61*9126SWyllys.Ingersoll@Sun.COM } TPM_HEADER_OFFSET_T; 62*9126SWyllys.Ingersoll@Sun.COM 63*9126SWyllys.Ingersoll@Sun.COM /* 64*9126SWyllys.Ingersoll@Sun.COM * This is to address some TPMs that does not report the correct duration 65*9126SWyllys.Ingersoll@Sun.COM * and timeouts. In our experience with the production TPMs, we encountered 66*9126SWyllys.Ingersoll@Sun.COM * time errors such as GetCapability command from TPM reporting the timeout 67*9126SWyllys.Ingersoll@Sun.COM * and durations in milliseconds rather than microseconds. Some other TPMs 68*9126SWyllys.Ingersoll@Sun.COM * report the value 0's 69*9126SWyllys.Ingersoll@Sun.COM * 70*9126SWyllys.Ingersoll@Sun.COM * Short Duration is based on section 11.3.4 of TIS speciciation, that 71*9126SWyllys.Ingersoll@Sun.COM * TPM_GetCapability (short duration) commands should not be longer than 750ms 72*9126SWyllys.Ingersoll@Sun.COM * and that section 11.3.7 states that TPM_ContinueSelfTest (medium duration) 73*9126SWyllys.Ingersoll@Sun.COM * should not be longer than 1 second. 74*9126SWyllys.Ingersoll@Sun.COM */ 75*9126SWyllys.Ingersoll@Sun.COM #define DEFAULT_SHORT_DURATION 750000 76*9126SWyllys.Ingersoll@Sun.COM #define DEFAULT_MEDIUM_DURATION 1000000 77*9126SWyllys.Ingersoll@Sun.COM #define DEFAULT_LONG_DURATION 300000000 78*9126SWyllys.Ingersoll@Sun.COM #define DEFAULT_TIMEOUT_A 750000 79*9126SWyllys.Ingersoll@Sun.COM #define DEFAULT_TIMEOUT_B 2000000 80*9126SWyllys.Ingersoll@Sun.COM #define DEFAULT_TIMEOUT_C 750000 81*9126SWyllys.Ingersoll@Sun.COM #define DEFAULT_TIMEOUT_D 750000 82*9126SWyllys.Ingersoll@Sun.COM 83*9126SWyllys.Ingersoll@Sun.COM /* 84*9126SWyllys.Ingersoll@Sun.COM * In order to test the 'millisecond bug', we test if DURATIONS and TIMEOUTS 85*9126SWyllys.Ingersoll@Sun.COM * are unreasonably low...such as 10 milliseconds (TPM isn't that fast). 86*9126SWyllys.Ingersoll@Sun.COM * and 400 milliseconds for long duration 87*9126SWyllys.Ingersoll@Sun.COM */ 88*9126SWyllys.Ingersoll@Sun.COM #define TEN_MILLISECONDS 10000 /* 10 milliseconds */ 89*9126SWyllys.Ingersoll@Sun.COM #define FOUR_HUNDRED_MILLISECONDS 400000 /* 4 hundred milliseconds */ 90*9126SWyllys.Ingersoll@Sun.COM 91*9126SWyllys.Ingersoll@Sun.COM /* 92*9126SWyllys.Ingersoll@Sun.COM * TPM input/output buffer offsets 93*9126SWyllys.Ingersoll@Sun.COM */ 94*9126SWyllys.Ingersoll@Sun.COM 95*9126SWyllys.Ingersoll@Sun.COM typedef enum { 96*9126SWyllys.Ingersoll@Sun.COM TPM_CAP_RESPSIZE_OFFSET = 10, 97*9126SWyllys.Ingersoll@Sun.COM TPM_CAP_RESP_OFFSET = 14, 98*9126SWyllys.Ingersoll@Sun.COM } TPM_CAP_RET_OFFSET_T; 99*9126SWyllys.Ingersoll@Sun.COM 100*9126SWyllys.Ingersoll@Sun.COM typedef enum { 101*9126SWyllys.Ingersoll@Sun.COM TPM_CAP_TIMEOUT_A_OFFSET = 14, 102*9126SWyllys.Ingersoll@Sun.COM TPM_CAP_TIMEOUT_B_OFFSET = 18, 103*9126SWyllys.Ingersoll@Sun.COM TPM_CAP_TIMEOUT_C_OFFSET = 22, 104*9126SWyllys.Ingersoll@Sun.COM TPM_CAP_TIMEOUT_D_OFFSET = 26, 105*9126SWyllys.Ingersoll@Sun.COM } TPM_CAP_TIMEOUT_OFFSET_T; 106*9126SWyllys.Ingersoll@Sun.COM 107*9126SWyllys.Ingersoll@Sun.COM typedef enum { 108*9126SWyllys.Ingersoll@Sun.COM TPM_CAP_DUR_SHORT_OFFSET = 14, 109*9126SWyllys.Ingersoll@Sun.COM TPM_CAP_DUR_MEDIUM_OFFSET = 18, 110*9126SWyllys.Ingersoll@Sun.COM TPM_CAP_DUR_LONG_OFFSET = 22, 111*9126SWyllys.Ingersoll@Sun.COM } TPM_CAP_DURATION_OFFSET_T; 112*9126SWyllys.Ingersoll@Sun.COM 113*9126SWyllys.Ingersoll@Sun.COM #define TPM_CAP_VERSION_INFO_OFFSET 14 114*9126SWyllys.Ingersoll@Sun.COM #define TPM_CAP_VERSION_INFO_SIZE 15 115*9126SWyllys.Ingersoll@Sun.COM 116*9126SWyllys.Ingersoll@Sun.COM /* 117*9126SWyllys.Ingersoll@Sun.COM * Internal TPM command functions 118*9126SWyllys.Ingersoll@Sun.COM */ 119*9126SWyllys.Ingersoll@Sun.COM static int itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz); 120*9126SWyllys.Ingersoll@Sun.COM static int tpm_get_timeouts(tpm_state_t *tpm); 121*9126SWyllys.Ingersoll@Sun.COM static int tpm_get_duration(tpm_state_t *tpm); 122*9126SWyllys.Ingersoll@Sun.COM static int tpm_get_version(tpm_state_t *tpm); 123*9126SWyllys.Ingersoll@Sun.COM static int tpm_continue_selftest(tpm_state_t *tpm); 124*9126SWyllys.Ingersoll@Sun.COM 125*9126SWyllys.Ingersoll@Sun.COM /* 126*9126SWyllys.Ingersoll@Sun.COM * Internal TIS related functions 127*9126SWyllys.Ingersoll@Sun.COM */ 128*9126SWyllys.Ingersoll@Sun.COM static int tpm_wait_for_stat(tpm_state_t *, uint8_t, clock_t); 129*9126SWyllys.Ingersoll@Sun.COM static clock_t tpm_get_ordinal_duration(tpm_state_t *, uint8_t); 130*9126SWyllys.Ingersoll@Sun.COM static int tis_check_active_locality(tpm_state_t *, char); 131*9126SWyllys.Ingersoll@Sun.COM static int tis_request_locality(tpm_state_t *, char); 132*9126SWyllys.Ingersoll@Sun.COM static void tis_release_locality(tpm_state_t *, char, int); 133*9126SWyllys.Ingersoll@Sun.COM static int tis_init(tpm_state_t *); 134*9126SWyllys.Ingersoll@Sun.COM static uint8_t tis_get_status(tpm_state_t *); 135*9126SWyllys.Ingersoll@Sun.COM static int tis_send_data(tpm_state_t *, uint8_t *, size_t); 136*9126SWyllys.Ingersoll@Sun.COM static int tis_recv_data(tpm_state_t *, uint8_t *, size_t); 137*9126SWyllys.Ingersoll@Sun.COM 138*9126SWyllys.Ingersoll@Sun.COM /* Auxilliary */ 139*9126SWyllys.Ingersoll@Sun.COM static int receive_data(tpm_state_t *, uint8_t *, size_t); 140*9126SWyllys.Ingersoll@Sun.COM static inline int tpm_lock(tpm_state_t *); 141*9126SWyllys.Ingersoll@Sun.COM static inline void tpm_unlock(tpm_state_t *); 142*9126SWyllys.Ingersoll@Sun.COM static void tpm_cleanup(dev_info_t *, tpm_state_t *); 143*9126SWyllys.Ingersoll@Sun.COM 144*9126SWyllys.Ingersoll@Sun.COM /* 145*9126SWyllys.Ingersoll@Sun.COM * Sun DDI/DDK entry points 146*9126SWyllys.Ingersoll@Sun.COM */ 147*9126SWyllys.Ingersoll@Sun.COM 148*9126SWyllys.Ingersoll@Sun.COM /* Declaration of autoconfig functions */ 149*9126SWyllys.Ingersoll@Sun.COM static int tpm_attach(dev_info_t *, ddi_attach_cmd_t); 150*9126SWyllys.Ingersoll@Sun.COM static int tpm_detach(dev_info_t *, ddi_detach_cmd_t); 151*9126SWyllys.Ingersoll@Sun.COM static int tpm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 152*9126SWyllys.Ingersoll@Sun.COM static int tpm_quiesce(dev_info_t *); 153*9126SWyllys.Ingersoll@Sun.COM /* End of autoconfig functions */ 154*9126SWyllys.Ingersoll@Sun.COM 155*9126SWyllys.Ingersoll@Sun.COM /* Declaration of driver entry point functions */ 156*9126SWyllys.Ingersoll@Sun.COM static int tpm_open(dev_t *, int, int, cred_t *); 157*9126SWyllys.Ingersoll@Sun.COM static int tpm_close(dev_t, int, int, cred_t *); 158*9126SWyllys.Ingersoll@Sun.COM static int tpm_read(dev_t, struct uio *, cred_t *); 159*9126SWyllys.Ingersoll@Sun.COM static int tpm_write(dev_t, struct uio *, cred_t *); 160*9126SWyllys.Ingersoll@Sun.COM /* End of driver entry point functions */ 161*9126SWyllys.Ingersoll@Sun.COM 162*9126SWyllys.Ingersoll@Sun.COM /* cb_ops structure */ 163*9126SWyllys.Ingersoll@Sun.COM static struct cb_ops tpm_cb_ops = { 164*9126SWyllys.Ingersoll@Sun.COM tpm_open, 165*9126SWyllys.Ingersoll@Sun.COM tpm_close, 166*9126SWyllys.Ingersoll@Sun.COM nodev, /* no strategy - nodev returns ENXIO */ 167*9126SWyllys.Ingersoll@Sun.COM nodev, /* no print */ 168*9126SWyllys.Ingersoll@Sun.COM nodev, /* no dump */ 169*9126SWyllys.Ingersoll@Sun.COM tpm_read, 170*9126SWyllys.Ingersoll@Sun.COM tpm_write, 171*9126SWyllys.Ingersoll@Sun.COM nodev, /* no ioctl */ 172*9126SWyllys.Ingersoll@Sun.COM nodev, /* no devmap */ 173*9126SWyllys.Ingersoll@Sun.COM nodev, /* no mmap */ 174*9126SWyllys.Ingersoll@Sun.COM nodev, /* no segmap */ 175*9126SWyllys.Ingersoll@Sun.COM nochpoll, /* returns ENXIO for non-pollable devices */ 176*9126SWyllys.Ingersoll@Sun.COM ddi_prop_op, 177*9126SWyllys.Ingersoll@Sun.COM NULL, /* streamtab struc */ 178*9126SWyllys.Ingersoll@Sun.COM D_MP, /* compatibility flags */ 179*9126SWyllys.Ingersoll@Sun.COM CB_REV, /* cb_ops revision number */ 180*9126SWyllys.Ingersoll@Sun.COM nodev, /* no aread */ 181*9126SWyllys.Ingersoll@Sun.COM nodev /* no awrite */ 182*9126SWyllys.Ingersoll@Sun.COM }; 183*9126SWyllys.Ingersoll@Sun.COM 184*9126SWyllys.Ingersoll@Sun.COM /* dev_ops structure */ 185*9126SWyllys.Ingersoll@Sun.COM static struct dev_ops tpm_dev_ops = { 186*9126SWyllys.Ingersoll@Sun.COM DEVO_REV, 187*9126SWyllys.Ingersoll@Sun.COM 0, /* reference count */ 188*9126SWyllys.Ingersoll@Sun.COM tpm_getinfo, 189*9126SWyllys.Ingersoll@Sun.COM nulldev, /* no identify - nulldev returns 0 */ 190*9126SWyllys.Ingersoll@Sun.COM nulldev, 191*9126SWyllys.Ingersoll@Sun.COM tpm_attach, 192*9126SWyllys.Ingersoll@Sun.COM tpm_detach, 193*9126SWyllys.Ingersoll@Sun.COM nodev, /* no reset - nodev returns ENXIO */ 194*9126SWyllys.Ingersoll@Sun.COM &tpm_cb_ops, 195*9126SWyllys.Ingersoll@Sun.COM (struct bus_ops *)NULL, 196*9126SWyllys.Ingersoll@Sun.COM nodev, /* no power */ 197*9126SWyllys.Ingersoll@Sun.COM tpm_quiesce 198*9126SWyllys.Ingersoll@Sun.COM }; 199*9126SWyllys.Ingersoll@Sun.COM 200*9126SWyllys.Ingersoll@Sun.COM /* modldrv structure */ 201*9126SWyllys.Ingersoll@Sun.COM static struct modldrv modldrv = { 202*9126SWyllys.Ingersoll@Sun.COM &mod_driverops, /* Type: This is a driver */ 203*9126SWyllys.Ingersoll@Sun.COM "TPM 1.2 driver", /* Name of the module. */ 204*9126SWyllys.Ingersoll@Sun.COM &tpm_dev_ops 205*9126SWyllys.Ingersoll@Sun.COM }; 206*9126SWyllys.Ingersoll@Sun.COM 207*9126SWyllys.Ingersoll@Sun.COM /* modlinkage structure */ 208*9126SWyllys.Ingersoll@Sun.COM static struct modlinkage tpm_ml = { 209*9126SWyllys.Ingersoll@Sun.COM MODREV_1, 210*9126SWyllys.Ingersoll@Sun.COM &modldrv, 211*9126SWyllys.Ingersoll@Sun.COM NULL 212*9126SWyllys.Ingersoll@Sun.COM }; 213*9126SWyllys.Ingersoll@Sun.COM 214*9126SWyllys.Ingersoll@Sun.COM static void *statep = NULL; 215*9126SWyllys.Ingersoll@Sun.COM 216*9126SWyllys.Ingersoll@Sun.COM /* 217*9126SWyllys.Ingersoll@Sun.COM * TPM commands to get the TPM's properties, e.g.,timeout 218*9126SWyllys.Ingersoll@Sun.COM */ 219*9126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 220*9126SWyllys.Ingersoll@Sun.COM static int 221*9126SWyllys.Ingersoll@Sun.COM tpm_quiesce(dev_info_t *dip) 222*9126SWyllys.Ingersoll@Sun.COM { 223*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 224*9126SWyllys.Ingersoll@Sun.COM } 225*9126SWyllys.Ingersoll@Sun.COM 226*9126SWyllys.Ingersoll@Sun.COM static uint32_t 227*9126SWyllys.Ingersoll@Sun.COM load32(uchar_t *ptr, uint32_t offset) 228*9126SWyllys.Ingersoll@Sun.COM { 229*9126SWyllys.Ingersoll@Sun.COM uint32_t val; 230*9126SWyllys.Ingersoll@Sun.COM bcopy(ptr + offset, &val, sizeof (uint32_t)); 231*9126SWyllys.Ingersoll@Sun.COM 232*9126SWyllys.Ingersoll@Sun.COM return (ntohl(val)); 233*9126SWyllys.Ingersoll@Sun.COM } 234*9126SWyllys.Ingersoll@Sun.COM 235*9126SWyllys.Ingersoll@Sun.COM /* 236*9126SWyllys.Ingersoll@Sun.COM * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability 237*9126SWyllys.Ingersoll@Sun.COM * with the subcommand TPM_CAP_PROP_TIS_TIMEOUT 238*9126SWyllys.Ingersoll@Sun.COM * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38) 239*9126SWyllys.Ingersoll@Sun.COM */ 240*9126SWyllys.Ingersoll@Sun.COM static int 241*9126SWyllys.Ingersoll@Sun.COM tpm_get_timeouts(tpm_state_t *tpm) 242*9126SWyllys.Ingersoll@Sun.COM { 243*9126SWyllys.Ingersoll@Sun.COM int ret; 244*9126SWyllys.Ingersoll@Sun.COM uint32_t timeout; /* in milliseconds */ 245*9126SWyllys.Ingersoll@Sun.COM uint32_t len; 246*9126SWyllys.Ingersoll@Sun.COM 247*9126SWyllys.Ingersoll@Sun.COM /* The buffer size (30) needs room for 4 timeout values (uint32_t) */ 248*9126SWyllys.Ingersoll@Sun.COM uint8_t buf[30] = { 249*9126SWyllys.Ingersoll@Sun.COM 0, 193, /* TPM_TAG_RQU_COMMAND */ 250*9126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 22, /* paramsize in bytes */ 251*9126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 101, /* TPM_ORD_GetCapability */ 252*9126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 5, /* TPM_CAP_Prop */ 253*9126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 4, /* SUB_CAP size in bytes */ 254*9126SWyllys.Ingersoll@Sun.COM 0, 0, 1, 21 /* TPM_CAP_PROP_TIS_TIMEOUT(0x115) */ 255*9126SWyllys.Ingersoll@Sun.COM }; 256*9126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_get_timeout"; 257*9126SWyllys.Ingersoll@Sun.COM 258*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 259*9126SWyllys.Ingersoll@Sun.COM 260*9126SWyllys.Ingersoll@Sun.COM ret = itpm_command(tpm, buf, sizeof (buf)); 261*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 262*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: itpm_command failed", myname); 263*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 264*9126SWyllys.Ingersoll@Sun.COM } 265*9126SWyllys.Ingersoll@Sun.COM 266*9126SWyllys.Ingersoll@Sun.COM /* 267*9126SWyllys.Ingersoll@Sun.COM * Get the length of the returned buffer 268*9126SWyllys.Ingersoll@Sun.COM * Make sure that there are 4 timeout values returned 269*9126SWyllys.Ingersoll@Sun.COM * length of the capability response is stored in data[10-13] 270*9126SWyllys.Ingersoll@Sun.COM * Also the TPM is in network byte order 271*9126SWyllys.Ingersoll@Sun.COM */ 272*9126SWyllys.Ingersoll@Sun.COM len = load32(buf, TPM_CAP_RESPSIZE_OFFSET); 273*9126SWyllys.Ingersoll@Sun.COM if (len != 4 * sizeof (uint32_t)) { 274*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: capability response size should be %d" 275*9126SWyllys.Ingersoll@Sun.COM "instead it's %d", 276*9126SWyllys.Ingersoll@Sun.COM myname, (int)(4 * sizeof (uint32_t)), (int)len); 277*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 278*9126SWyllys.Ingersoll@Sun.COM } 279*9126SWyllys.Ingersoll@Sun.COM 280*9126SWyllys.Ingersoll@Sun.COM /* Get the four timeout's: a,b,c,d (they are 4 bytes long each) */ 281*9126SWyllys.Ingersoll@Sun.COM timeout = load32(buf, TPM_CAP_TIMEOUT_A_OFFSET); 282*9126SWyllys.Ingersoll@Sun.COM if (timeout == 0) { 283*9126SWyllys.Ingersoll@Sun.COM timeout = DEFAULT_TIMEOUT_A; 284*9126SWyllys.Ingersoll@Sun.COM } else if (timeout < TEN_MILLISECONDS) { 285*9126SWyllys.Ingersoll@Sun.COM /* timeout is in millisecond range (should be microseconds) */ 286*9126SWyllys.Ingersoll@Sun.COM timeout *= 1000; 287*9126SWyllys.Ingersoll@Sun.COM } 288*9126SWyllys.Ingersoll@Sun.COM tpm->timeout_a = drv_usectohz(timeout); 289*9126SWyllys.Ingersoll@Sun.COM 290*9126SWyllys.Ingersoll@Sun.COM timeout = load32(buf, TPM_CAP_TIMEOUT_B_OFFSET); 291*9126SWyllys.Ingersoll@Sun.COM if (timeout == 0) { 292*9126SWyllys.Ingersoll@Sun.COM timeout = DEFAULT_TIMEOUT_B; 293*9126SWyllys.Ingersoll@Sun.COM } else if (timeout < TEN_MILLISECONDS) { 294*9126SWyllys.Ingersoll@Sun.COM /* timeout is in millisecond range (should be microseconds) */ 295*9126SWyllys.Ingersoll@Sun.COM timeout *= 1000; 296*9126SWyllys.Ingersoll@Sun.COM } 297*9126SWyllys.Ingersoll@Sun.COM tpm->timeout_b = drv_usectohz(timeout); 298*9126SWyllys.Ingersoll@Sun.COM 299*9126SWyllys.Ingersoll@Sun.COM timeout = load32(buf, TPM_CAP_TIMEOUT_C_OFFSET); 300*9126SWyllys.Ingersoll@Sun.COM if (timeout == 0) { 301*9126SWyllys.Ingersoll@Sun.COM timeout = DEFAULT_TIMEOUT_C; 302*9126SWyllys.Ingersoll@Sun.COM } else if (timeout < TEN_MILLISECONDS) { 303*9126SWyllys.Ingersoll@Sun.COM /* timeout is in millisecond range (should be microseconds) */ 304*9126SWyllys.Ingersoll@Sun.COM timeout *= 1000; 305*9126SWyllys.Ingersoll@Sun.COM } 306*9126SWyllys.Ingersoll@Sun.COM tpm->timeout_c = drv_usectohz(timeout); 307*9126SWyllys.Ingersoll@Sun.COM 308*9126SWyllys.Ingersoll@Sun.COM timeout = load32(buf, TPM_CAP_TIMEOUT_D_OFFSET); 309*9126SWyllys.Ingersoll@Sun.COM if (timeout == 0) { 310*9126SWyllys.Ingersoll@Sun.COM timeout = DEFAULT_TIMEOUT_D; 311*9126SWyllys.Ingersoll@Sun.COM } else if (timeout < TEN_MILLISECONDS) { 312*9126SWyllys.Ingersoll@Sun.COM /* timeout is in millisecond range (should be microseconds) */ 313*9126SWyllys.Ingersoll@Sun.COM timeout *= 1000; 314*9126SWyllys.Ingersoll@Sun.COM } 315*9126SWyllys.Ingersoll@Sun.COM tpm->timeout_d = drv_usectohz(timeout); 316*9126SWyllys.Ingersoll@Sun.COM 317*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 318*9126SWyllys.Ingersoll@Sun.COM } 319*9126SWyllys.Ingersoll@Sun.COM 320*9126SWyllys.Ingersoll@Sun.COM /* 321*9126SWyllys.Ingersoll@Sun.COM * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability 322*9126SWyllys.Ingersoll@Sun.COM * with the subcommand TPM_CAP_PROP_TIS_DURATION 323*9126SWyllys.Ingersoll@Sun.COM * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38) 324*9126SWyllys.Ingersoll@Sun.COM */ 325*9126SWyllys.Ingersoll@Sun.COM static int 326*9126SWyllys.Ingersoll@Sun.COM tpm_get_duration(tpm_state_t *tpm) { 327*9126SWyllys.Ingersoll@Sun.COM int ret; 328*9126SWyllys.Ingersoll@Sun.COM uint32_t duration; 329*9126SWyllys.Ingersoll@Sun.COM uint32_t len; 330*9126SWyllys.Ingersoll@Sun.COM uint8_t buf[30] = { 331*9126SWyllys.Ingersoll@Sun.COM 0, 193, /* TPM_TAG_RQU_COMMAND */ 332*9126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 22, /* paramsize in bytes */ 333*9126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 101, /* TPM_ORD_GetCapability */ 334*9126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 5, /* TPM_CAP_Prop */ 335*9126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 4, /* SUB_CAP size in bytes */ 336*9126SWyllys.Ingersoll@Sun.COM 0, 0, 1, 32 /* TPM_CAP_PROP_TIS_DURATION(0x120) */ 337*9126SWyllys.Ingersoll@Sun.COM }; 338*9126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_get_duration"; 339*9126SWyllys.Ingersoll@Sun.COM 340*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 341*9126SWyllys.Ingersoll@Sun.COM 342*9126SWyllys.Ingersoll@Sun.COM ret = itpm_command(tpm, buf, sizeof (buf)); 343*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 344*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: itpm_command failed with ret code: 0x%x", 345*9126SWyllys.Ingersoll@Sun.COM myname, ret); 346*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 347*9126SWyllys.Ingersoll@Sun.COM } 348*9126SWyllys.Ingersoll@Sun.COM 349*9126SWyllys.Ingersoll@Sun.COM /* 350*9126SWyllys.Ingersoll@Sun.COM * Get the length of the returned buffer 351*9126SWyllys.Ingersoll@Sun.COM * Make sure that there are 3 duration values (S,M,L: in that order) 352*9126SWyllys.Ingersoll@Sun.COM * length of the capability response is stored in data[10-13] 353*9126SWyllys.Ingersoll@Sun.COM * Also the TPM is in network byte order 354*9126SWyllys.Ingersoll@Sun.COM */ 355*9126SWyllys.Ingersoll@Sun.COM len = load32(buf, TPM_CAP_RESPSIZE_OFFSET); 356*9126SWyllys.Ingersoll@Sun.COM if (len != 3 * sizeof (uint32_t)) { 357*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: capability response should be %d, " 358*9126SWyllys.Ingersoll@Sun.COM "instead, it's %d", 359*9126SWyllys.Ingersoll@Sun.COM myname, (int)(3 * sizeof (uint32_t)), (int)len); 360*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 361*9126SWyllys.Ingersoll@Sun.COM } 362*9126SWyllys.Ingersoll@Sun.COM 363*9126SWyllys.Ingersoll@Sun.COM duration = load32(buf, TPM_CAP_DUR_SHORT_OFFSET); 364*9126SWyllys.Ingersoll@Sun.COM if (duration == 0) { 365*9126SWyllys.Ingersoll@Sun.COM duration = DEFAULT_SHORT_DURATION; 366*9126SWyllys.Ingersoll@Sun.COM } else if (duration < TEN_MILLISECONDS) { 367*9126SWyllys.Ingersoll@Sun.COM duration *= 1000; 368*9126SWyllys.Ingersoll@Sun.COM } 369*9126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_SHORT] = drv_usectohz(duration); 370*9126SWyllys.Ingersoll@Sun.COM 371*9126SWyllys.Ingersoll@Sun.COM duration = load32(buf, TPM_CAP_DUR_MEDIUM_OFFSET); 372*9126SWyllys.Ingersoll@Sun.COM if (duration == 0) { 373*9126SWyllys.Ingersoll@Sun.COM duration = DEFAULT_MEDIUM_DURATION; 374*9126SWyllys.Ingersoll@Sun.COM } else if (duration < TEN_MILLISECONDS) { 375*9126SWyllys.Ingersoll@Sun.COM duration *= 1000; 376*9126SWyllys.Ingersoll@Sun.COM } 377*9126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_MEDIUM] = drv_usectohz(duration); 378*9126SWyllys.Ingersoll@Sun.COM 379*9126SWyllys.Ingersoll@Sun.COM duration = load32(buf, TPM_CAP_DUR_LONG_OFFSET); 380*9126SWyllys.Ingersoll@Sun.COM if (duration == 0) { 381*9126SWyllys.Ingersoll@Sun.COM duration = DEFAULT_LONG_DURATION; 382*9126SWyllys.Ingersoll@Sun.COM } else if (duration < FOUR_HUNDRED_MILLISECONDS) { 383*9126SWyllys.Ingersoll@Sun.COM duration *= 1000; 384*9126SWyllys.Ingersoll@Sun.COM } 385*9126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_LONG] = drv_usectohz(duration); 386*9126SWyllys.Ingersoll@Sun.COM 387*9126SWyllys.Ingersoll@Sun.COM /* Just make the undefined duration be the same as the LONG */ 388*9126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_UNDEFINED] = tpm->duration[TPM_LONG]; 389*9126SWyllys.Ingersoll@Sun.COM 390*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 391*9126SWyllys.Ingersoll@Sun.COM } 392*9126SWyllys.Ingersoll@Sun.COM 393*9126SWyllys.Ingersoll@Sun.COM /* 394*9126SWyllys.Ingersoll@Sun.COM * Get the actual timeouts supported by the TPM by issuing TPM_GetCapability 395*9126SWyllys.Ingersoll@Sun.COM * with the subcommand TPM_CAP_PROP_TIS_DURATION 396*9126SWyllys.Ingersoll@Sun.COM * TPM_GetCapability (TPM Main Part 3 Rev. 94, pg.38) 397*9126SWyllys.Ingersoll@Sun.COM */ 398*9126SWyllys.Ingersoll@Sun.COM static int 399*9126SWyllys.Ingersoll@Sun.COM tpm_get_version(tpm_state_t *tpm) { 400*9126SWyllys.Ingersoll@Sun.COM int ret; 401*9126SWyllys.Ingersoll@Sun.COM uint32_t len; 402*9126SWyllys.Ingersoll@Sun.COM char vendorId[5]; 403*9126SWyllys.Ingersoll@Sun.COM /* If this buf is too small, the "vendor specific" data won't fit */ 404*9126SWyllys.Ingersoll@Sun.COM uint8_t buf[64] = { 405*9126SWyllys.Ingersoll@Sun.COM 0, 193, /* TPM_TAG_RQU_COMMAND */ 406*9126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 18, /* paramsize in bytes */ 407*9126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 101, /* TPM_ORD_GetCapability */ 408*9126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 0x1A, /* TPM_CAP_VERSION_VAL */ 409*9126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 0, /* SUB_CAP size in bytes */ 410*9126SWyllys.Ingersoll@Sun.COM }; 411*9126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_get_version"; 412*9126SWyllys.Ingersoll@Sun.COM 413*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 414*9126SWyllys.Ingersoll@Sun.COM 415*9126SWyllys.Ingersoll@Sun.COM ret = itpm_command(tpm, buf, sizeof (buf)); 416*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 417*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: itpm_command failed with ret code: 0x%x", 418*9126SWyllys.Ingersoll@Sun.COM myname, ret); 419*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 420*9126SWyllys.Ingersoll@Sun.COM } 421*9126SWyllys.Ingersoll@Sun.COM 422*9126SWyllys.Ingersoll@Sun.COM /* 423*9126SWyllys.Ingersoll@Sun.COM * Get the length of the returned buffer. 424*9126SWyllys.Ingersoll@Sun.COM */ 425*9126SWyllys.Ingersoll@Sun.COM len = load32(buf, TPM_CAP_RESPSIZE_OFFSET); 426*9126SWyllys.Ingersoll@Sun.COM if (len < TPM_CAP_VERSION_INFO_SIZE) { 427*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: capability response should be greater" 428*9126SWyllys.Ingersoll@Sun.COM " than %d, instead, it's %d", 429*9126SWyllys.Ingersoll@Sun.COM myname, 430*9126SWyllys.Ingersoll@Sun.COM TPM_CAP_VERSION_INFO_SIZE, 431*9126SWyllys.Ingersoll@Sun.COM len); 432*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 433*9126SWyllys.Ingersoll@Sun.COM } 434*9126SWyllys.Ingersoll@Sun.COM 435*9126SWyllys.Ingersoll@Sun.COM bcopy(buf + TPM_CAP_VERSION_INFO_OFFSET, &tpm->vers_info, 436*9126SWyllys.Ingersoll@Sun.COM TPM_CAP_VERSION_INFO_SIZE); 437*9126SWyllys.Ingersoll@Sun.COM 438*9126SWyllys.Ingersoll@Sun.COM bcopy(tpm->vers_info.tpmVendorID, vendorId, 439*9126SWyllys.Ingersoll@Sun.COM sizeof (tpm->vers_info.tpmVendorID)); 440*9126SWyllys.Ingersoll@Sun.COM vendorId[4] = '\0'; 441*9126SWyllys.Ingersoll@Sun.COM 442*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_NOTE, "!TPM found: Ver %d.%d, Rev %d.%d, " 443*9126SWyllys.Ingersoll@Sun.COM "SpecLevel %d, errataRev %d, VendorId '%s'", 444*9126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.major, /* Version */ 445*9126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.minor, 446*9126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.revMajor, /* Revision */ 447*9126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.revMinor, 448*9126SWyllys.Ingersoll@Sun.COM (int)ntohs(tpm->vers_info.specLevel), 449*9126SWyllys.Ingersoll@Sun.COM tpm->vers_info.errataRev, 450*9126SWyllys.Ingersoll@Sun.COM vendorId); 451*9126SWyllys.Ingersoll@Sun.COM 452*9126SWyllys.Ingersoll@Sun.COM /* 453*9126SWyllys.Ingersoll@Sun.COM * This driver only supports TPM Version 1.2 454*9126SWyllys.Ingersoll@Sun.COM */ 455*9126SWyllys.Ingersoll@Sun.COM if (tpm->vers_info.version.major != 1 && 456*9126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.minor != 2) { 457*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: Unsupported TPM version (%d.%d)", 458*9126SWyllys.Ingersoll@Sun.COM myname, 459*9126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.major, /* Version */ 460*9126SWyllys.Ingersoll@Sun.COM tpm->vers_info.version.minor); 461*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 462*9126SWyllys.Ingersoll@Sun.COM } 463*9126SWyllys.Ingersoll@Sun.COM 464*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 465*9126SWyllys.Ingersoll@Sun.COM } 466*9126SWyllys.Ingersoll@Sun.COM 467*9126SWyllys.Ingersoll@Sun.COM /* 468*9126SWyllys.Ingersoll@Sun.COM * To prevent the TPM from complaining that certain functions are not tested 469*9126SWyllys.Ingersoll@Sun.COM * we run this command when the driver attaches. 470*9126SWyllys.Ingersoll@Sun.COM * For details see Section 4.2 of TPM Main Part 3 Command Specification 471*9126SWyllys.Ingersoll@Sun.COM */ 472*9126SWyllys.Ingersoll@Sun.COM static int 473*9126SWyllys.Ingersoll@Sun.COM tpm_continue_selftest(tpm_state_t *tpm) { 474*9126SWyllys.Ingersoll@Sun.COM int ret; 475*9126SWyllys.Ingersoll@Sun.COM uint8_t buf[10] = { 476*9126SWyllys.Ingersoll@Sun.COM 0, 193, /* TPM_TAG_RQU COMMAND */ 477*9126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 10, /* paramsize in bytes */ 478*9126SWyllys.Ingersoll@Sun.COM 0, 0, 0, 83 /* TPM_ORD_ContinueSelfTest */ 479*9126SWyllys.Ingersoll@Sun.COM }; 480*9126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_continue_selftest"; 481*9126SWyllys.Ingersoll@Sun.COM 482*9126SWyllys.Ingersoll@Sun.COM /* Need a longer timeout */ 483*9126SWyllys.Ingersoll@Sun.COM ret = itpm_command(tpm, buf, sizeof (buf)); 484*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 485*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: itpm_command failed", myname); 486*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 487*9126SWyllys.Ingersoll@Sun.COM } 488*9126SWyllys.Ingersoll@Sun.COM 489*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 490*9126SWyllys.Ingersoll@Sun.COM } 491*9126SWyllys.Ingersoll@Sun.COM /* 492*9126SWyllys.Ingersoll@Sun.COM * Auxilary Functions 493*9126SWyllys.Ingersoll@Sun.COM */ 494*9126SWyllys.Ingersoll@Sun.COM 495*9126SWyllys.Ingersoll@Sun.COM /* 496*9126SWyllys.Ingersoll@Sun.COM * Find out how long we should wait for the TPM command to complete a command 497*9126SWyllys.Ingersoll@Sun.COM */ 498*9126SWyllys.Ingersoll@Sun.COM static clock_t 499*9126SWyllys.Ingersoll@Sun.COM tpm_get_ordinal_duration(tpm_state_t *tpm, uint8_t ordinal) 500*9126SWyllys.Ingersoll@Sun.COM { 501*9126SWyllys.Ingersoll@Sun.COM uint8_t index; 502*9126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_get_ordinal_duration"; 503*9126SWyllys.Ingersoll@Sun.COM 504*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 505*9126SWyllys.Ingersoll@Sun.COM 506*9126SWyllys.Ingersoll@Sun.COM /* Default and failure case for IFX */ 507*9126SWyllys.Ingersoll@Sun.COM /* Is it a TSC_ORDINAL? */ 508*9126SWyllys.Ingersoll@Sun.COM if (ordinal & TSC_ORDINAL_MASK) { 509*9126SWyllys.Ingersoll@Sun.COM if (ordinal > TSC_ORDINAL_MAX) { 510*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, 511*9126SWyllys.Ingersoll@Sun.COM "%s: tsc ordinal: %d exceeds MAX: %d", 512*9126SWyllys.Ingersoll@Sun.COM myname, ordinal, TSC_ORDINAL_MAX); 513*9126SWyllys.Ingersoll@Sun.COM return (0); 514*9126SWyllys.Ingersoll@Sun.COM } 515*9126SWyllys.Ingersoll@Sun.COM index = tsc_ords_duration[ordinal]; 516*9126SWyllys.Ingersoll@Sun.COM } else { 517*9126SWyllys.Ingersoll@Sun.COM if (ordinal > TPM_ORDINAL_MAX) { 518*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, 519*9126SWyllys.Ingersoll@Sun.COM "%s: ordinal %d exceeds MAX: %d", 520*9126SWyllys.Ingersoll@Sun.COM myname, ordinal, TPM_ORDINAL_MAX); 521*9126SWyllys.Ingersoll@Sun.COM return (0); 522*9126SWyllys.Ingersoll@Sun.COM } 523*9126SWyllys.Ingersoll@Sun.COM index = tpm_ords_duration[ordinal]; 524*9126SWyllys.Ingersoll@Sun.COM } 525*9126SWyllys.Ingersoll@Sun.COM 526*9126SWyllys.Ingersoll@Sun.COM if (index > TPM_DURATION_MAX_IDX) { 527*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: FATAL:index '%d' is out of bound", 528*9126SWyllys.Ingersoll@Sun.COM myname, index); 529*9126SWyllys.Ingersoll@Sun.COM return (0); 530*9126SWyllys.Ingersoll@Sun.COM } 531*9126SWyllys.Ingersoll@Sun.COM return (tpm->duration[index]); 532*9126SWyllys.Ingersoll@Sun.COM } 533*9126SWyllys.Ingersoll@Sun.COM 534*9126SWyllys.Ingersoll@Sun.COM /* 535*9126SWyllys.Ingersoll@Sun.COM * Internal TPM Transmit Function: 536*9126SWyllys.Ingersoll@Sun.COM * Calls implementation specific sendto and receive 537*9126SWyllys.Ingersoll@Sun.COM * The code assumes that the buffer is in network byte order 538*9126SWyllys.Ingersoll@Sun.COM */ 539*9126SWyllys.Ingersoll@Sun.COM static int 540*9126SWyllys.Ingersoll@Sun.COM itpm_command(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) 541*9126SWyllys.Ingersoll@Sun.COM { 542*9126SWyllys.Ingersoll@Sun.COM int ret; 543*9126SWyllys.Ingersoll@Sun.COM uint32_t count; 544*9126SWyllys.Ingersoll@Sun.COM char *myname = "itpm_command"; 545*9126SWyllys.Ingersoll@Sun.COM 546*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && buf != NULL); 547*9126SWyllys.Ingersoll@Sun.COM 548*9126SWyllys.Ingersoll@Sun.COM /* The byte order is network byte order so convert it */ 549*9126SWyllys.Ingersoll@Sun.COM count = load32(buf, TPM_PARAMSIZE_OFFSET); 550*9126SWyllys.Ingersoll@Sun.COM 551*9126SWyllys.Ingersoll@Sun.COM if (count == 0) { 552*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: count=0, no data? %d", myname, 553*9126SWyllys.Ingersoll@Sun.COM (int)bufsiz); 554*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 555*9126SWyllys.Ingersoll@Sun.COM } 556*9126SWyllys.Ingersoll@Sun.COM if (count > bufsiz) { 557*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: invalid count value:count:%d > bufsiz %d", 558*9126SWyllys.Ingersoll@Sun.COM myname, (int)count, (int)bufsiz); 559*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 560*9126SWyllys.Ingersoll@Sun.COM } 561*9126SWyllys.Ingersoll@Sun.COM 562*9126SWyllys.Ingersoll@Sun.COM /* Send the command */ 563*9126SWyllys.Ingersoll@Sun.COM ret = tis_send_data(tpm, buf, count); 564*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 565*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_send_data failed with error %x", 566*9126SWyllys.Ingersoll@Sun.COM myname, ret); 567*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 568*9126SWyllys.Ingersoll@Sun.COM } 569*9126SWyllys.Ingersoll@Sun.COM 570*9126SWyllys.Ingersoll@Sun.COM /* 571*9126SWyllys.Ingersoll@Sun.COM * Now receive the data from the tpm 572*9126SWyllys.Ingersoll@Sun.COM * Should at least receive "the common" 10 bytes (TPM_HEADER_SIZE) 573*9126SWyllys.Ingersoll@Sun.COM */ 574*9126SWyllys.Ingersoll@Sun.COM ret = tis_recv_data(tpm, buf, bufsiz); 575*9126SWyllys.Ingersoll@Sun.COM if (ret < TPM_HEADER_SIZE) { 576*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_recv_data failed", myname); 577*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 578*9126SWyllys.Ingersoll@Sun.COM } 579*9126SWyllys.Ingersoll@Sun.COM 580*9126SWyllys.Ingersoll@Sun.COM /* Check the return code */ 581*9126SWyllys.Ingersoll@Sun.COM ret = load32(buf, TPM_RETURN_OFFSET); 582*9126SWyllys.Ingersoll@Sun.COM if (ret != TPM_SUCCESS) { 583*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: command failed with ret code: %x", 584*9126SWyllys.Ingersoll@Sun.COM myname, ret); 585*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 586*9126SWyllys.Ingersoll@Sun.COM } 587*9126SWyllys.Ingersoll@Sun.COM 588*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 589*9126SWyllys.Ingersoll@Sun.COM } 590*9126SWyllys.Ingersoll@Sun.COM 591*9126SWyllys.Ingersoll@Sun.COM /* 592*9126SWyllys.Ingersoll@Sun.COM * Whenever the driver wants to write to the DATA_IO register, it must need 593*9126SWyllys.Ingersoll@Sun.COM * to figure out the burstcount. This is the amount of bytes it can write 594*9126SWyllys.Ingersoll@Sun.COM * before having to wait for long LPC bus cycle 595*9126SWyllys.Ingersoll@Sun.COM * 596*9126SWyllys.Ingersoll@Sun.COM * Returns: 0 if error, burst count if sucess 597*9126SWyllys.Ingersoll@Sun.COM */ 598*9126SWyllys.Ingersoll@Sun.COM static uint16_t 599*9126SWyllys.Ingersoll@Sun.COM tpm_get_burstcount(tpm_state_t *tpm) { 600*9126SWyllys.Ingersoll@Sun.COM clock_t stop; 601*9126SWyllys.Ingersoll@Sun.COM uint16_t burstcnt; 602*9126SWyllys.Ingersoll@Sun.COM 603*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 604*9126SWyllys.Ingersoll@Sun.COM 605*9126SWyllys.Ingersoll@Sun.COM /* 606*9126SWyllys.Ingersoll@Sun.COM * Spec says timeout should be TIMEOUT_D 607*9126SWyllys.Ingersoll@Sun.COM * burst count is TPM_STS bits 8..23 608*9126SWyllys.Ingersoll@Sun.COM */ 609*9126SWyllys.Ingersoll@Sun.COM stop = ddi_get_lbolt() + tpm->timeout_d; 610*9126SWyllys.Ingersoll@Sun.COM do { 611*9126SWyllys.Ingersoll@Sun.COM /* 612*9126SWyllys.Ingersoll@Sun.COM * burstcnt is stored as a little endian value 613*9126SWyllys.Ingersoll@Sun.COM * 'ntohs' doesn't work since the value is not word-aligned 614*9126SWyllys.Ingersoll@Sun.COM */ 615*9126SWyllys.Ingersoll@Sun.COM burstcnt = ddi_get8(tpm->handle, 616*9126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr+ 617*9126SWyllys.Ingersoll@Sun.COM TPM_STS_(tpm->locality)+1)); 618*9126SWyllys.Ingersoll@Sun.COM burstcnt += ddi_get8(tpm->handle, 619*9126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr+ 620*9126SWyllys.Ingersoll@Sun.COM TPM_STS_(tpm->locality)+2)) << 8; 621*9126SWyllys.Ingersoll@Sun.COM 622*9126SWyllys.Ingersoll@Sun.COM if (burstcnt) 623*9126SWyllys.Ingersoll@Sun.COM return (burstcnt); 624*9126SWyllys.Ingersoll@Sun.COM 625*9126SWyllys.Ingersoll@Sun.COM delay(tpm->timeout_poll); 626*9126SWyllys.Ingersoll@Sun.COM } while (ddi_get_lbolt() < stop); 627*9126SWyllys.Ingersoll@Sun.COM 628*9126SWyllys.Ingersoll@Sun.COM return (0); 629*9126SWyllys.Ingersoll@Sun.COM } 630*9126SWyllys.Ingersoll@Sun.COM 631*9126SWyllys.Ingersoll@Sun.COM /* 632*9126SWyllys.Ingersoll@Sun.COM * Writing 1 to TPM_STS_CMD_READY bit in TPM_STS will do the following: 633*9126SWyllys.Ingersoll@Sun.COM * 1. The TPM will clears IO buffers if any 634*9126SWyllys.Ingersoll@Sun.COM * 2. The TPM will enters either Idle or Ready state within TIMEOUT_B 635*9126SWyllys.Ingersoll@Sun.COM * (checked in the calling function) 636*9126SWyllys.Ingersoll@Sun.COM */ 637*9126SWyllys.Ingersoll@Sun.COM static void 638*9126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm_state_t *tpm) { 639*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL); 640*9126SWyllys.Ingersoll@Sun.COM 641*9126SWyllys.Ingersoll@Sun.COM ddi_put8(tpm->handle, 642*9126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality)), 643*9126SWyllys.Ingersoll@Sun.COM TPM_STS_CMD_READY); 644*9126SWyllys.Ingersoll@Sun.COM } 645*9126SWyllys.Ingersoll@Sun.COM 646*9126SWyllys.Ingersoll@Sun.COM static int 647*9126SWyllys.Ingersoll@Sun.COM receive_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) { 648*9126SWyllys.Ingersoll@Sun.COM int size = 0; 649*9126SWyllys.Ingersoll@Sun.COM int retried = 0; 650*9126SWyllys.Ingersoll@Sun.COM uint8_t stsbits; 651*9126SWyllys.Ingersoll@Sun.COM 652*9126SWyllys.Ingersoll@Sun.COM /* A number of consecutive bytes that can be written to TPM */ 653*9126SWyllys.Ingersoll@Sun.COM uint16_t burstcnt; 654*9126SWyllys.Ingersoll@Sun.COM 655*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && buf != NULL); 656*9126SWyllys.Ingersoll@Sun.COM retry: 657*9126SWyllys.Ingersoll@Sun.COM while (size < bufsiz && 658*9126SWyllys.Ingersoll@Sun.COM (tpm_wait_for_stat(tpm, 659*9126SWyllys.Ingersoll@Sun.COM (TPM_STS_DATA_AVAIL|TPM_STS_VALID), 660*9126SWyllys.Ingersoll@Sun.COM (ddi_get_lbolt() + tpm->timeout_c)) == DDI_SUCCESS)) { 661*9126SWyllys.Ingersoll@Sun.COM /* 662*9126SWyllys.Ingersoll@Sun.COM * Burstcount should be available within TIMEOUT_D 663*9126SWyllys.Ingersoll@Sun.COM * after STS is set to valid 664*9126SWyllys.Ingersoll@Sun.COM * burstcount is dynamic, so have to get it each time 665*9126SWyllys.Ingersoll@Sun.COM */ 666*9126SWyllys.Ingersoll@Sun.COM burstcnt = tpm_get_burstcount(tpm); 667*9126SWyllys.Ingersoll@Sun.COM for (; burstcnt > 0 && size < bufsiz; burstcnt--) { 668*9126SWyllys.Ingersoll@Sun.COM buf[size++] = ddi_get8(tpm->handle, 669*9126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr + 670*9126SWyllys.Ingersoll@Sun.COM TPM_DATA_FIFO_(tpm->locality))); 671*9126SWyllys.Ingersoll@Sun.COM } 672*9126SWyllys.Ingersoll@Sun.COM } 673*9126SWyllys.Ingersoll@Sun.COM stsbits = tis_get_status(tpm); 674*9126SWyllys.Ingersoll@Sun.COM /* check to see if we need to retry (just once) */ 675*9126SWyllys.Ingersoll@Sun.COM if (size < bufsiz && !(stsbits & TPM_STS_DATA_AVAIL) && retried == 0) { 676*9126SWyllys.Ingersoll@Sun.COM /* issue responseRetry (TIS 1.2 pg 54) */ 677*9126SWyllys.Ingersoll@Sun.COM ddi_put8(tpm->handle, 678*9126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality)), 679*9126SWyllys.Ingersoll@Sun.COM TPM_STS_RESPONSE_RETRY); 680*9126SWyllys.Ingersoll@Sun.COM /* update the retry counter so we only retry once */ 681*9126SWyllys.Ingersoll@Sun.COM retried++; 682*9126SWyllys.Ingersoll@Sun.COM /* reset the size to 0 and reread the entire response */ 683*9126SWyllys.Ingersoll@Sun.COM size = 0; 684*9126SWyllys.Ingersoll@Sun.COM goto retry; 685*9126SWyllys.Ingersoll@Sun.COM } 686*9126SWyllys.Ingersoll@Sun.COM return (size); 687*9126SWyllys.Ingersoll@Sun.COM } 688*9126SWyllys.Ingersoll@Sun.COM 689*9126SWyllys.Ingersoll@Sun.COM /* Receive the data from the TPM */ 690*9126SWyllys.Ingersoll@Sun.COM static int 691*9126SWyllys.Ingersoll@Sun.COM tis_recv_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) { 692*9126SWyllys.Ingersoll@Sun.COM int ret; 693*9126SWyllys.Ingersoll@Sun.COM int size = 0; 694*9126SWyllys.Ingersoll@Sun.COM uint32_t expected, status; 695*9126SWyllys.Ingersoll@Sun.COM uint32_t cmdresult; 696*9126SWyllys.Ingersoll@Sun.COM char *myname = "tis_recv_data"; 697*9126SWyllys.Ingersoll@Sun.COM 698*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && buf != NULL); 699*9126SWyllys.Ingersoll@Sun.COM 700*9126SWyllys.Ingersoll@Sun.COM if (bufsiz < TPM_HEADER_SIZE) { 701*9126SWyllys.Ingersoll@Sun.COM /* There should be at least tag,paramsize,return code */ 702*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: received data should contain at least " 703*9126SWyllys.Ingersoll@Sun.COM "the header which is %d bytes long", 704*9126SWyllys.Ingersoll@Sun.COM myname, TPM_HEADER_SIZE); 705*9126SWyllys.Ingersoll@Sun.COM goto OUT; 706*9126SWyllys.Ingersoll@Sun.COM } 707*9126SWyllys.Ingersoll@Sun.COM 708*9126SWyllys.Ingersoll@Sun.COM /* Read tag(2 bytes), paramsize(4), and result(4) */ 709*9126SWyllys.Ingersoll@Sun.COM size = receive_data(tpm, buf, TPM_HEADER_SIZE); 710*9126SWyllys.Ingersoll@Sun.COM if (size < TPM_HEADER_SIZE) { 711*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: getting the TPM_HEADER failed: size=%d", 712*9126SWyllys.Ingersoll@Sun.COM myname, size); 713*9126SWyllys.Ingersoll@Sun.COM goto OUT; 714*9126SWyllys.Ingersoll@Sun.COM } 715*9126SWyllys.Ingersoll@Sun.COM 716*9126SWyllys.Ingersoll@Sun.COM cmdresult = load32(buf, TPM_RETURN_OFFSET); 717*9126SWyllys.Ingersoll@Sun.COM 718*9126SWyllys.Ingersoll@Sun.COM /* Get 'paramsize'(4 bytes)--it includes tag and paramsize */ 719*9126SWyllys.Ingersoll@Sun.COM expected = load32(buf, TPM_PARAMSIZE_OFFSET); 720*9126SWyllys.Ingersoll@Sun.COM if (expected > bufsiz) { 721*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: paramSize is bigger " 722*9126SWyllys.Ingersoll@Sun.COM "than the requested size: paramSize=%d bufsiz=%d result=%d", 723*9126SWyllys.Ingersoll@Sun.COM myname, (int)expected, (int)bufsiz, cmdresult); 724*9126SWyllys.Ingersoll@Sun.COM goto OUT; 725*9126SWyllys.Ingersoll@Sun.COM } 726*9126SWyllys.Ingersoll@Sun.COM 727*9126SWyllys.Ingersoll@Sun.COM /* Read in the rest of the data from the TPM */ 728*9126SWyllys.Ingersoll@Sun.COM size += receive_data(tpm, (uint8_t *)&buf[TPM_HEADER_SIZE], 729*9126SWyllys.Ingersoll@Sun.COM expected - TPM_HEADER_SIZE); 730*9126SWyllys.Ingersoll@Sun.COM if (size < expected) { 731*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: received data length=%d " 732*9126SWyllys.Ingersoll@Sun.COM "is less than expected = %d", myname, size, expected); 733*9126SWyllys.Ingersoll@Sun.COM goto OUT; 734*9126SWyllys.Ingersoll@Sun.COM } 735*9126SWyllys.Ingersoll@Sun.COM 736*9126SWyllys.Ingersoll@Sun.COM /* The TPM MUST set the state to stsValid within TIMEOUT_C */ 737*9126SWyllys.Ingersoll@Sun.COM ret = tpm_wait_for_stat(tpm, TPM_STS_VALID, 738*9126SWyllys.Ingersoll@Sun.COM ddi_get_lbolt() + tpm->timeout_c); 739*9126SWyllys.Ingersoll@Sun.COM 740*9126SWyllys.Ingersoll@Sun.COM status = tis_get_status(tpm); 741*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 742*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: TPM didn't set stsValid after its I/O: " 743*9126SWyllys.Ingersoll@Sun.COM "status = 0x%08X", myname, status); 744*9126SWyllys.Ingersoll@Sun.COM goto OUT; 745*9126SWyllys.Ingersoll@Sun.COM } 746*9126SWyllys.Ingersoll@Sun.COM 747*9126SWyllys.Ingersoll@Sun.COM /* There is still more data? */ 748*9126SWyllys.Ingersoll@Sun.COM if (status & TPM_STS_DATA_AVAIL) { 749*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: Status TPM_STS_DATA_AVAIL set:0x%08X", 750*9126SWyllys.Ingersoll@Sun.COM myname, status); 751*9126SWyllys.Ingersoll@Sun.COM goto OUT; 752*9126SWyllys.Ingersoll@Sun.COM } 753*9126SWyllys.Ingersoll@Sun.COM 754*9126SWyllys.Ingersoll@Sun.COM /* 755*9126SWyllys.Ingersoll@Sun.COM * Release the control of the TPM after we are done with it 756*9126SWyllys.Ingersoll@Sun.COM * it...so others can also get a chance to send data 757*9126SWyllys.Ingersoll@Sun.COM */ 758*9126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm, tpm->locality, 0); 759*9126SWyllys.Ingersoll@Sun.COM 760*9126SWyllys.Ingersoll@Sun.COM OUT: 761*9126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm); 762*9126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm, tpm->locality, 0); 763*9126SWyllys.Ingersoll@Sun.COM return (size); 764*9126SWyllys.Ingersoll@Sun.COM } 765*9126SWyllys.Ingersoll@Sun.COM 766*9126SWyllys.Ingersoll@Sun.COM /* 767*9126SWyllys.Ingersoll@Sun.COM * Send the data (TPM commands) to the Data IO register 768*9126SWyllys.Ingersoll@Sun.COM */ 769*9126SWyllys.Ingersoll@Sun.COM static int 770*9126SWyllys.Ingersoll@Sun.COM tis_send_data(tpm_state_t *tpm, uint8_t *buf, size_t bufsiz) { 771*9126SWyllys.Ingersoll@Sun.COM int ret; 772*9126SWyllys.Ingersoll@Sun.COM uint8_t status; 773*9126SWyllys.Ingersoll@Sun.COM uint16_t burstcnt; 774*9126SWyllys.Ingersoll@Sun.COM uint32_t ordinal; 775*9126SWyllys.Ingersoll@Sun.COM size_t count = 0; 776*9126SWyllys.Ingersoll@Sun.COM char *myname = "tis_send_data"; 777*9126SWyllys.Ingersoll@Sun.COM 778*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && buf != NULL); 779*9126SWyllys.Ingersoll@Sun.COM 780*9126SWyllys.Ingersoll@Sun.COM if (bufsiz == 0) { 781*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: passed in argument bufsize is zero", 782*9126SWyllys.Ingersoll@Sun.COM myname); 783*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 784*9126SWyllys.Ingersoll@Sun.COM } 785*9126SWyllys.Ingersoll@Sun.COM 786*9126SWyllys.Ingersoll@Sun.COM /* Be in the right locality (aren't we always in locality 0?) */ 787*9126SWyllys.Ingersoll@Sun.COM if (tis_request_locality(tpm, 0) != DDI_SUCCESS) { 788*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_request_locality didn't enter " 789*9126SWyllys.Ingersoll@Sun.COM "locality 0", myname); 790*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 791*9126SWyllys.Ingersoll@Sun.COM } 792*9126SWyllys.Ingersoll@Sun.COM 793*9126SWyllys.Ingersoll@Sun.COM /* Put the TPM in ready state */ 794*9126SWyllys.Ingersoll@Sun.COM status = tis_get_status(tpm); 795*9126SWyllys.Ingersoll@Sun.COM 796*9126SWyllys.Ingersoll@Sun.COM if (!(status & TPM_STS_CMD_READY)) { 797*9126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm); 798*9126SWyllys.Ingersoll@Sun.COM ret = tpm_wait_for_stat(tpm, 799*9126SWyllys.Ingersoll@Sun.COM TPM_STS_CMD_READY, 800*9126SWyllys.Ingersoll@Sun.COM (ddi_get_lbolt() + tpm->timeout_b)); 801*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 802*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: could not put the TPM " 803*9126SWyllys.Ingersoll@Sun.COM "in the command ready state:" 804*9126SWyllys.Ingersoll@Sun.COM "tpm_wait_for_stat returned error", 805*9126SWyllys.Ingersoll@Sun.COM myname); 806*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 807*9126SWyllys.Ingersoll@Sun.COM } 808*9126SWyllys.Ingersoll@Sun.COM } 809*9126SWyllys.Ingersoll@Sun.COM 810*9126SWyllys.Ingersoll@Sun.COM /* 811*9126SWyllys.Ingersoll@Sun.COM * Now we are ready to send command 812*9126SWyllys.Ingersoll@Sun.COM * TPM's burstcount dictates how many bytes we can write at a time 813*9126SWyllys.Ingersoll@Sun.COM * Burstcount is dynamic if INTF_CAPABILITY for static burstcount is 814*9126SWyllys.Ingersoll@Sun.COM * not set. 815*9126SWyllys.Ingersoll@Sun.COM */ 816*9126SWyllys.Ingersoll@Sun.COM while (count < bufsiz - 1) { 817*9126SWyllys.Ingersoll@Sun.COM burstcnt = tpm_get_burstcount(tpm); 818*9126SWyllys.Ingersoll@Sun.COM if (burstcnt == 0) { 819*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_get_burstcnt returned error", 820*9126SWyllys.Ingersoll@Sun.COM myname); 821*9126SWyllys.Ingersoll@Sun.COM ret = DDI_FAILURE; 822*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 823*9126SWyllys.Ingersoll@Sun.COM } 824*9126SWyllys.Ingersoll@Sun.COM 825*9126SWyllys.Ingersoll@Sun.COM for (; burstcnt > 0 && count < bufsiz - 1; burstcnt--) { 826*9126SWyllys.Ingersoll@Sun.COM ddi_put8(tpm->handle, (uint8_t *)(tpm->addr+ 827*9126SWyllys.Ingersoll@Sun.COM TPM_DATA_FIFO_(tpm->locality)), buf[count]); 828*9126SWyllys.Ingersoll@Sun.COM count++; 829*9126SWyllys.Ingersoll@Sun.COM } 830*9126SWyllys.Ingersoll@Sun.COM /* Wait for TPM to indicate that it is ready for more data */ 831*9126SWyllys.Ingersoll@Sun.COM ret = tpm_wait_for_stat(tpm, 832*9126SWyllys.Ingersoll@Sun.COM (TPM_STS_VALID | TPM_STS_DATA_EXPECT), 833*9126SWyllys.Ingersoll@Sun.COM (ddi_get_lbolt() + tpm->timeout_c)); 834*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 835*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: TPM didn't enter stsvalid " 836*9126SWyllys.Ingersoll@Sun.COM "state after sending the data:", myname); 837*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 838*9126SWyllys.Ingersoll@Sun.COM } 839*9126SWyllys.Ingersoll@Sun.COM } 840*9126SWyllys.Ingersoll@Sun.COM /* We can't exit the loop above unless we wrote bufsiz-1 bytes */ 841*9126SWyllys.Ingersoll@Sun.COM 842*9126SWyllys.Ingersoll@Sun.COM /* Write last byte */ 843*9126SWyllys.Ingersoll@Sun.COM ddi_put8(tpm->handle, (uint8_t *)(tpm->addr + 844*9126SWyllys.Ingersoll@Sun.COM TPM_DATA_FIFO_(tpm->locality)), buf[count]); 845*9126SWyllys.Ingersoll@Sun.COM count++; 846*9126SWyllys.Ingersoll@Sun.COM 847*9126SWyllys.Ingersoll@Sun.COM /* Wait for the TPM to enter Valid State */ 848*9126SWyllys.Ingersoll@Sun.COM ret = tpm_wait_for_stat(tpm, 849*9126SWyllys.Ingersoll@Sun.COM TPM_STS_VALID, (ddi_get_lbolt() + tpm->timeout_c)); 850*9126SWyllys.Ingersoll@Sun.COM if (ret == DDI_FAILURE) { 851*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm didn't enter Valid state", myname); 852*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 853*9126SWyllys.Ingersoll@Sun.COM } 854*9126SWyllys.Ingersoll@Sun.COM 855*9126SWyllys.Ingersoll@Sun.COM status = tis_get_status(tpm); 856*9126SWyllys.Ingersoll@Sun.COM /* The TPM should NOT be expecing more data at this point */ 857*9126SWyllys.Ingersoll@Sun.COM if ((status & TPM_STS_DATA_EXPECT) != 0) { 858*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: DATA_EXPECT is set (shouldn't be) after " 859*9126SWyllys.Ingersoll@Sun.COM "writing the last byte: status=0x%08X", myname, status); 860*9126SWyllys.Ingersoll@Sun.COM ret = DDI_FAILURE; 861*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 862*9126SWyllys.Ingersoll@Sun.COM } 863*9126SWyllys.Ingersoll@Sun.COM 864*9126SWyllys.Ingersoll@Sun.COM /* 865*9126SWyllys.Ingersoll@Sun.COM * Final step: Writing TPM_STS_GO to TPM_STS 866*9126SWyllys.Ingersoll@Sun.COM * register will actually send the command. 867*9126SWyllys.Ingersoll@Sun.COM */ 868*9126SWyllys.Ingersoll@Sun.COM ddi_put8(tpm->handle, (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality)), 869*9126SWyllys.Ingersoll@Sun.COM TPM_STS_GO); 870*9126SWyllys.Ingersoll@Sun.COM 871*9126SWyllys.Ingersoll@Sun.COM /* Ordinal/Command_code is located in buf[6..9] */ 872*9126SWyllys.Ingersoll@Sun.COM ordinal = load32(buf, TPM_COMMAND_CODE_OFFSET); 873*9126SWyllys.Ingersoll@Sun.COM 874*9126SWyllys.Ingersoll@Sun.COM ret = tpm_wait_for_stat(tpm, TPM_STS_DATA_AVAIL | TPM_STS_VALID, 875*9126SWyllys.Ingersoll@Sun.COM ddi_get_lbolt() + tpm_get_ordinal_duration(tpm, ordinal)); 876*9126SWyllys.Ingersoll@Sun.COM if (ret == DDI_FAILURE) { 877*9126SWyllys.Ingersoll@Sun.COM status = tis_get_status(tpm); 878*9126SWyllys.Ingersoll@Sun.COM if (!(status & TPM_STS_DATA_AVAIL) || 879*9126SWyllys.Ingersoll@Sun.COM !(status & TPM_STS_VALID)) { 880*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: TPM not ready or valid " 881*9126SWyllys.Ingersoll@Sun.COM "(ordinal = %d timeout = %ld)", 882*9126SWyllys.Ingersoll@Sun.COM myname, ordinal, 883*9126SWyllys.Ingersoll@Sun.COM tpm_get_ordinal_duration(tpm, ordinal)); 884*9126SWyllys.Ingersoll@Sun.COM } else { 885*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_wait_for_stat " 886*9126SWyllys.Ingersoll@Sun.COM "(DATA_AVAIL | VALID) failed: STS = 0x%0X", 887*9126SWyllys.Ingersoll@Sun.COM myname, status); 888*9126SWyllys.Ingersoll@Sun.COM } 889*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 890*9126SWyllys.Ingersoll@Sun.COM } 891*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 892*9126SWyllys.Ingersoll@Sun.COM 893*9126SWyllys.Ingersoll@Sun.COM FAIL: 894*9126SWyllys.Ingersoll@Sun.COM tpm_set_ready(tpm); 895*9126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm, tpm->locality, 0); 896*9126SWyllys.Ingersoll@Sun.COM return (ret); 897*9126SWyllys.Ingersoll@Sun.COM } 898*9126SWyllys.Ingersoll@Sun.COM 899*9126SWyllys.Ingersoll@Sun.COM /* 900*9126SWyllys.Ingersoll@Sun.COM * Clear XrequestUse and Xactivelocality, where X is the current locality 901*9126SWyllys.Ingersoll@Sun.COM */ 902*9126SWyllys.Ingersoll@Sun.COM static void 903*9126SWyllys.Ingersoll@Sun.COM tis_release_locality(tpm_state_t *tpm, char locality, int force) { 904*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && locality >= 0 && locality < 5); 905*9126SWyllys.Ingersoll@Sun.COM 906*9126SWyllys.Ingersoll@Sun.COM if (force || 907*9126SWyllys.Ingersoll@Sun.COM (ddi_get8(tpm->handle, 908*9126SWyllys.Ingersoll@Sun.COM (uchar_t *)(tpm->addr+TPM_ACCESS_(locality))) 909*9126SWyllys.Ingersoll@Sun.COM & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) 910*9126SWyllys.Ingersoll@Sun.COM == (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) { 911*9126SWyllys.Ingersoll@Sun.COM /* 912*9126SWyllys.Ingersoll@Sun.COM * Writing 1 to active locality bit in TPM_ACCESS 913*9126SWyllys.Ingersoll@Sun.COM * register reliquishes the control of the locality 914*9126SWyllys.Ingersoll@Sun.COM */ 915*9126SWyllys.Ingersoll@Sun.COM ddi_put8(tpm->handle, 916*9126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr+TPM_ACCESS_(locality)), 917*9126SWyllys.Ingersoll@Sun.COM TPM_ACCESS_ACTIVE_LOCALITY); 918*9126SWyllys.Ingersoll@Sun.COM } 919*9126SWyllys.Ingersoll@Sun.COM } 920*9126SWyllys.Ingersoll@Sun.COM 921*9126SWyllys.Ingersoll@Sun.COM /* 922*9126SWyllys.Ingersoll@Sun.COM * Checks whether the given locality is active 923*9126SWyllys.Ingersoll@Sun.COM * Use TPM_ACCESS register and the masks TPM_ACCESS_VALID,TPM_ACTIVE_LOCALITY 924*9126SWyllys.Ingersoll@Sun.COM */ 925*9126SWyllys.Ingersoll@Sun.COM static int 926*9126SWyllys.Ingersoll@Sun.COM tis_check_active_locality(tpm_state_t *tpm, char locality) { 927*9126SWyllys.Ingersoll@Sun.COM uint8_t access_bits; 928*9126SWyllys.Ingersoll@Sun.COM 929*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && locality >= 0 && locality < 5); 930*9126SWyllys.Ingersoll@Sun.COM 931*9126SWyllys.Ingersoll@Sun.COM access_bits = ddi_get8(tpm->handle, 932*9126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr+TPM_ACCESS_(locality))); 933*9126SWyllys.Ingersoll@Sun.COM access_bits &= (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID); 934*9126SWyllys.Ingersoll@Sun.COM 935*9126SWyllys.Ingersoll@Sun.COM if (access_bits == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) 936*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 937*9126SWyllys.Ingersoll@Sun.COM else 938*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 939*9126SWyllys.Ingersoll@Sun.COM } 940*9126SWyllys.Ingersoll@Sun.COM 941*9126SWyllys.Ingersoll@Sun.COM /* Request the TPM to be in the given locality */ 942*9126SWyllys.Ingersoll@Sun.COM static int 943*9126SWyllys.Ingersoll@Sun.COM tis_request_locality(tpm_state_t *tpm, char locality) { 944*9126SWyllys.Ingersoll@Sun.COM clock_t timeout; 945*9126SWyllys.Ingersoll@Sun.COM int ret; 946*9126SWyllys.Ingersoll@Sun.COM char *myname = "tis_request_locality"; 947*9126SWyllys.Ingersoll@Sun.COM 948*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm != NULL && locality >= 0 && locality < 5); 949*9126SWyllys.Ingersoll@Sun.COM 950*9126SWyllys.Ingersoll@Sun.COM ret = tis_check_active_locality(tpm, locality); 951*9126SWyllys.Ingersoll@Sun.COM 952*9126SWyllys.Ingersoll@Sun.COM if (ret == DDI_SUCCESS) { 953*9126SWyllys.Ingersoll@Sun.COM /* Locality is already active */ 954*9126SWyllys.Ingersoll@Sun.COM tpm->locality = locality; 955*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 956*9126SWyllys.Ingersoll@Sun.COM } 957*9126SWyllys.Ingersoll@Sun.COM 958*9126SWyllys.Ingersoll@Sun.COM ddi_put8(tpm->handle, tpm->addr+TPM_ACCESS_(locality), 959*9126SWyllys.Ingersoll@Sun.COM TPM_ACCESS_REQUEST_USE); 960*9126SWyllys.Ingersoll@Sun.COM timeout = ddi_get_lbolt() + tpm->timeout_a; 961*9126SWyllys.Ingersoll@Sun.COM 962*9126SWyllys.Ingersoll@Sun.COM /* Using polling */ 963*9126SWyllys.Ingersoll@Sun.COM while (tis_check_active_locality(tpm, locality) 964*9126SWyllys.Ingersoll@Sun.COM != DDI_SUCCESS) { 965*9126SWyllys.Ingersoll@Sun.COM if (ddi_get_lbolt() >= timeout) { 966*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s (interrupt-disabled) " 967*9126SWyllys.Ingersoll@Sun.COM "tis_request_locality timed out", 968*9126SWyllys.Ingersoll@Sun.COM myname); 969*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 970*9126SWyllys.Ingersoll@Sun.COM } 971*9126SWyllys.Ingersoll@Sun.COM delay(tpm->timeout_poll); 972*9126SWyllys.Ingersoll@Sun.COM } 973*9126SWyllys.Ingersoll@Sun.COM 974*9126SWyllys.Ingersoll@Sun.COM tpm->locality = locality; 975*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 976*9126SWyllys.Ingersoll@Sun.COM } 977*9126SWyllys.Ingersoll@Sun.COM 978*9126SWyllys.Ingersoll@Sun.COM /* Read the status register */ 979*9126SWyllys.Ingersoll@Sun.COM static uint8_t 980*9126SWyllys.Ingersoll@Sun.COM tis_get_status(tpm_state_t *tpm) { 981*9126SWyllys.Ingersoll@Sun.COM return (ddi_get8(tpm->handle, 982*9126SWyllys.Ingersoll@Sun.COM (uint8_t *)(tpm->addr+TPM_STS_(tpm->locality)))); 983*9126SWyllys.Ingersoll@Sun.COM } 984*9126SWyllys.Ingersoll@Sun.COM 985*9126SWyllys.Ingersoll@Sun.COM static int 986*9126SWyllys.Ingersoll@Sun.COM tpm_wait_for_stat(tpm_state_t *tpm, uint8_t mask, clock_t absolute_timeout) { 987*9126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_wait_for_stat"; 988*9126SWyllys.Ingersoll@Sun.COM 989*9126SWyllys.Ingersoll@Sun.COM /* Using polling */ 990*9126SWyllys.Ingersoll@Sun.COM while ((tis_get_status(tpm) & mask) != mask) { 991*9126SWyllys.Ingersoll@Sun.COM if (ddi_get_lbolt() >= absolute_timeout) { 992*9126SWyllys.Ingersoll@Sun.COM /* Timeout reached */ 993*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: using " 994*9126SWyllys.Ingersoll@Sun.COM "polling:reached timeout", 995*9126SWyllys.Ingersoll@Sun.COM myname); 996*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 997*9126SWyllys.Ingersoll@Sun.COM } 998*9126SWyllys.Ingersoll@Sun.COM delay(tpm->timeout_poll); 999*9126SWyllys.Ingersoll@Sun.COM } 1000*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 1001*9126SWyllys.Ingersoll@Sun.COM } 1002*9126SWyllys.Ingersoll@Sun.COM 1003*9126SWyllys.Ingersoll@Sun.COM /* 1004*9126SWyllys.Ingersoll@Sun.COM * Initialize TPM device 1005*9126SWyllys.Ingersoll@Sun.COM * 1. Find out supported interrupt capabilities 1006*9126SWyllys.Ingersoll@Sun.COM * 2. Set up interrupt handler if supported (some BIOSes don't support 1007*9126SWyllys.Ingersoll@Sun.COM * interrupts for TPMS, in which case we set up polling) 1008*9126SWyllys.Ingersoll@Sun.COM * 3. Determine timeouts and commands duration 1009*9126SWyllys.Ingersoll@Sun.COM */ 1010*9126SWyllys.Ingersoll@Sun.COM static int 1011*9126SWyllys.Ingersoll@Sun.COM tis_init(tpm_state_t *tpm) { 1012*9126SWyllys.Ingersoll@Sun.COM uint32_t intf_caps; 1013*9126SWyllys.Ingersoll@Sun.COM int ret; 1014*9126SWyllys.Ingersoll@Sun.COM char *myname = "tis_init"; 1015*9126SWyllys.Ingersoll@Sun.COM uintptr_t aptr = (uintptr_t)tpm->addr; 1016*9126SWyllys.Ingersoll@Sun.COM 1017*9126SWyllys.Ingersoll@Sun.COM /* 1018*9126SWyllys.Ingersoll@Sun.COM * Temporarily set up timeouts before we get the real timeouts 1019*9126SWyllys.Ingersoll@Sun.COM * by issuing TPM_CAP commands (but to issue TPM_CAP commands, 1020*9126SWyllys.Ingersoll@Sun.COM * you need TIMEOUTs defined...chicken and egg problem here. 1021*9126SWyllys.Ingersoll@Sun.COM * TPM timeouts: Convert the milliseconds to clock cycles 1022*9126SWyllys.Ingersoll@Sun.COM */ 1023*9126SWyllys.Ingersoll@Sun.COM tpm->timeout_a = drv_usectohz(TIS_TIMEOUT_A); 1024*9126SWyllys.Ingersoll@Sun.COM tpm->timeout_b = drv_usectohz(TIS_TIMEOUT_B); 1025*9126SWyllys.Ingersoll@Sun.COM tpm->timeout_c = drv_usectohz(TIS_TIMEOUT_C); 1026*9126SWyllys.Ingersoll@Sun.COM tpm->timeout_d = drv_usectohz(TIS_TIMEOUT_D); 1027*9126SWyllys.Ingersoll@Sun.COM /* 1028*9126SWyllys.Ingersoll@Sun.COM * Do the same with the duration (real duration will be filled out 1029*9126SWyllys.Ingersoll@Sun.COM * when we call TPM_GetCapability to get the duration values from 1030*9126SWyllys.Ingersoll@Sun.COM * the TPM itself). 1031*9126SWyllys.Ingersoll@Sun.COM */ 1032*9126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_SHORT] = drv_usectohz(TPM_DEFAULT_DURATION); 1033*9126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_MEDIUM] = drv_usectohz(TPM_DEFAULT_DURATION); 1034*9126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_LONG] = drv_usectohz(TPM_DEFAULT_DURATION); 1035*9126SWyllys.Ingersoll@Sun.COM tpm->duration[TPM_UNDEFINED] = drv_usectohz(TPM_DEFAULT_DURATION); 1036*9126SWyllys.Ingersoll@Sun.COM 1037*9126SWyllys.Ingersoll@Sun.COM /* Find out supported capabilities */ 1038*9126SWyllys.Ingersoll@Sun.COM intf_caps = ddi_get32(tpm->handle, 1039*9126SWyllys.Ingersoll@Sun.COM (uint32_t *)(aptr + TPM_INTF_CAP_(0))); 1040*9126SWyllys.Ingersoll@Sun.COM 1041*9126SWyllys.Ingersoll@Sun.COM /* Upper 3 bytes should always return 0 */ 1042*9126SWyllys.Ingersoll@Sun.COM if (intf_caps & 0x7FFFFF00) { 1043*9126SWyllys.Ingersoll@Sun.COM #ifdef DEBUG 1044*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: bad intf_caps value 0x%0X", 1045*9126SWyllys.Ingersoll@Sun.COM myname, intf_caps); 1046*9126SWyllys.Ingersoll@Sun.COM #endif 1047*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1048*9126SWyllys.Ingersoll@Sun.COM } 1049*9126SWyllys.Ingersoll@Sun.COM 1050*9126SWyllys.Ingersoll@Sun.COM /* These two interrupts are mandatory */ 1051*9126SWyllys.Ingersoll@Sun.COM if (!(intf_caps & TPM_INTF_INT_LOCALITY_CHANGE_INT)) { 1052*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: Mandatory capability Locality Change Int " 1053*9126SWyllys.Ingersoll@Sun.COM "not supported", myname); 1054*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1055*9126SWyllys.Ingersoll@Sun.COM } 1056*9126SWyllys.Ingersoll@Sun.COM if (!(intf_caps & TPM_INTF_INT_DATA_AVAIL_INT)) { 1057*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: Mandatory capability Data Available Int " 1058*9126SWyllys.Ingersoll@Sun.COM "not supported", myname); 1059*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1060*9126SWyllys.Ingersoll@Sun.COM } 1061*9126SWyllys.Ingersoll@Sun.COM 1062*9126SWyllys.Ingersoll@Sun.COM /* 1063*9126SWyllys.Ingersoll@Sun.COM * Before we start writing anything to TPM's registers, 1064*9126SWyllys.Ingersoll@Sun.COM * make sure we are in locality 0 1065*9126SWyllys.Ingersoll@Sun.COM */ 1066*9126SWyllys.Ingersoll@Sun.COM ret = tis_request_locality(tpm, 0); 1067*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 1068*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: Unable to request locality 0", myname); 1069*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1070*9126SWyllys.Ingersoll@Sun.COM } /* Now we can refer to the locality as tpm->locality */ 1071*9126SWyllys.Ingersoll@Sun.COM 1072*9126SWyllys.Ingersoll@Sun.COM tpm->timeout_poll = drv_usectohz(TPM_POLLING_TIMEOUT); 1073*9126SWyllys.Ingersoll@Sun.COM tpm->intr_enabled = 0; 1074*9126SWyllys.Ingersoll@Sun.COM 1075*9126SWyllys.Ingersoll@Sun.COM /* Get the real timeouts from the TPM */ 1076*9126SWyllys.Ingersoll@Sun.COM ret = tpm_get_timeouts(tpm); 1077*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 1078*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_get_timeouts error", myname); 1079*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1080*9126SWyllys.Ingersoll@Sun.COM } 1081*9126SWyllys.Ingersoll@Sun.COM 1082*9126SWyllys.Ingersoll@Sun.COM ret = tpm_get_duration(tpm); 1083*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 1084*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_get_duration error", myname); 1085*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1086*9126SWyllys.Ingersoll@Sun.COM } 1087*9126SWyllys.Ingersoll@Sun.COM 1088*9126SWyllys.Ingersoll@Sun.COM /* This gets the TPM version information */ 1089*9126SWyllys.Ingersoll@Sun.COM ret = tpm_get_version(tpm); 1090*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 1091*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_get_version error", myname); 1092*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1093*9126SWyllys.Ingersoll@Sun.COM } 1094*9126SWyllys.Ingersoll@Sun.COM 1095*9126SWyllys.Ingersoll@Sun.COM /* 1096*9126SWyllys.Ingersoll@Sun.COM * Unless the TPM completes the test of its commands, 1097*9126SWyllys.Ingersoll@Sun.COM * it can return an error when the untested commands are called 1098*9126SWyllys.Ingersoll@Sun.COM */ 1099*9126SWyllys.Ingersoll@Sun.COM ret = tpm_continue_selftest(tpm); 1100*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 1101*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_continue_selftest error", myname); 1102*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1103*9126SWyllys.Ingersoll@Sun.COM } 1104*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 1105*9126SWyllys.Ingersoll@Sun.COM } 1106*9126SWyllys.Ingersoll@Sun.COM 1107*9126SWyllys.Ingersoll@Sun.COM /* 1108*9126SWyllys.Ingersoll@Sun.COM * Module Entry points 1109*9126SWyllys.Ingersoll@Sun.COM */ 1110*9126SWyllys.Ingersoll@Sun.COM int 1111*9126SWyllys.Ingersoll@Sun.COM _init(void) 1112*9126SWyllys.Ingersoll@Sun.COM { 1113*9126SWyllys.Ingersoll@Sun.COM int ret; 1114*9126SWyllys.Ingersoll@Sun.COM 1115*9126SWyllys.Ingersoll@Sun.COM ret = ddi_soft_state_init(&statep, sizeof (tpm_state_t), 1); 1116*9126SWyllys.Ingersoll@Sun.COM if (ret) 1117*9126SWyllys.Ingersoll@Sun.COM return (ret); 1118*9126SWyllys.Ingersoll@Sun.COM 1119*9126SWyllys.Ingersoll@Sun.COM ret = mod_install(&tpm_ml); 1120*9126SWyllys.Ingersoll@Sun.COM if (ret != 0) { 1121*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "_init: mod_install returned non-zero"); 1122*9126SWyllys.Ingersoll@Sun.COM ddi_soft_state_fini(&statep); 1123*9126SWyllys.Ingersoll@Sun.COM return (ret); 1124*9126SWyllys.Ingersoll@Sun.COM } 1125*9126SWyllys.Ingersoll@Sun.COM 1126*9126SWyllys.Ingersoll@Sun.COM return (ret); 1127*9126SWyllys.Ingersoll@Sun.COM } 1128*9126SWyllys.Ingersoll@Sun.COM 1129*9126SWyllys.Ingersoll@Sun.COM int 1130*9126SWyllys.Ingersoll@Sun.COM _info(struct modinfo *modinfop) 1131*9126SWyllys.Ingersoll@Sun.COM { 1132*9126SWyllys.Ingersoll@Sun.COM int ret; 1133*9126SWyllys.Ingersoll@Sun.COM ret = mod_info(&tpm_ml, modinfop); 1134*9126SWyllys.Ingersoll@Sun.COM if (ret == 0) 1135*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "mod_info failed: %d", ret); 1136*9126SWyllys.Ingersoll@Sun.COM 1137*9126SWyllys.Ingersoll@Sun.COM return (ret); 1138*9126SWyllys.Ingersoll@Sun.COM } 1139*9126SWyllys.Ingersoll@Sun.COM 1140*9126SWyllys.Ingersoll@Sun.COM int 1141*9126SWyllys.Ingersoll@Sun.COM _fini() 1142*9126SWyllys.Ingersoll@Sun.COM { 1143*9126SWyllys.Ingersoll@Sun.COM int ret; 1144*9126SWyllys.Ingersoll@Sun.COM ret = mod_remove(&tpm_ml); 1145*9126SWyllys.Ingersoll@Sun.COM if (ret != 0) { 1146*9126SWyllys.Ingersoll@Sun.COM return (ret); 1147*9126SWyllys.Ingersoll@Sun.COM } 1148*9126SWyllys.Ingersoll@Sun.COM ddi_soft_state_fini(&statep); 1149*9126SWyllys.Ingersoll@Sun.COM 1150*9126SWyllys.Ingersoll@Sun.COM return (ret); 1151*9126SWyllys.Ingersoll@Sun.COM } 1152*9126SWyllys.Ingersoll@Sun.COM /* End of driver configuration functions */ 1153*9126SWyllys.Ingersoll@Sun.COM 1154*9126SWyllys.Ingersoll@Sun.COM static int 1155*9126SWyllys.Ingersoll@Sun.COM tpm_resume(tpm_state_t *tpm) 1156*9126SWyllys.Ingersoll@Sun.COM { 1157*9126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 1158*9126SWyllys.Ingersoll@Sun.COM if (!tpm->suspended) { 1159*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 1160*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1161*9126SWyllys.Ingersoll@Sun.COM } 1162*9126SWyllys.Ingersoll@Sun.COM tpm->suspended = 0; 1163*9126SWyllys.Ingersoll@Sun.COM cv_broadcast(&tpm->suspend_cv); 1164*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 1165*9126SWyllys.Ingersoll@Sun.COM 1166*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 1167*9126SWyllys.Ingersoll@Sun.COM } 1168*9126SWyllys.Ingersoll@Sun.COM 1169*9126SWyllys.Ingersoll@Sun.COM /* 1170*9126SWyllys.Ingersoll@Sun.COM * Sun DDI/DDK entry points 1171*9126SWyllys.Ingersoll@Sun.COM */ 1172*9126SWyllys.Ingersoll@Sun.COM static int 1173*9126SWyllys.Ingersoll@Sun.COM tpm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 1174*9126SWyllys.Ingersoll@Sun.COM { 1175*9126SWyllys.Ingersoll@Sun.COM int ret, idx; 1176*9126SWyllys.Ingersoll@Sun.COM int instance; 1177*9126SWyllys.Ingersoll@Sun.COM int nregs; 1178*9126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_attach"; 1179*9126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm = NULL; 1180*9126SWyllys.Ingersoll@Sun.COM 1181*9126SWyllys.Ingersoll@Sun.COM ASSERT(dip != NULL); 1182*9126SWyllys.Ingersoll@Sun.COM 1183*9126SWyllys.Ingersoll@Sun.COM instance = ddi_get_instance(dip); 1184*9126SWyllys.Ingersoll@Sun.COM 1185*9126SWyllys.Ingersoll@Sun.COM /* Nothing out of ordinary here */ 1186*9126SWyllys.Ingersoll@Sun.COM switch (cmd) { 1187*9126SWyllys.Ingersoll@Sun.COM case DDI_ATTACH: 1188*9126SWyllys.Ingersoll@Sun.COM ret = ddi_soft_state_zalloc(statep, instance); 1189*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 1190*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s could not allocate tpm_state_t", 1191*9126SWyllys.Ingersoll@Sun.COM myname); 1192*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 1193*9126SWyllys.Ingersoll@Sun.COM } 1194*9126SWyllys.Ingersoll@Sun.COM tpm = ddi_get_soft_state(statep, instance); 1195*9126SWyllys.Ingersoll@Sun.COM tpm->dip = dip; 1196*9126SWyllys.Ingersoll@Sun.COM break; 1197*9126SWyllys.Ingersoll@Sun.COM case DDI_RESUME: 1198*9126SWyllys.Ingersoll@Sun.COM tpm = ddi_get_soft_state(statep, instance); 1199*9126SWyllys.Ingersoll@Sun.COM if (tpm == NULL) { 1200*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tpm_state_t is NULL", 1201*9126SWyllys.Ingersoll@Sun.COM myname); 1202*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 1203*9126SWyllys.Ingersoll@Sun.COM } 1204*9126SWyllys.Ingersoll@Sun.COM return (tpm_resume(tpm)); 1205*9126SWyllys.Ingersoll@Sun.COM default: 1206*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: cmd %d is not implemented", myname, cmd); 1207*9126SWyllys.Ingersoll@Sun.COM ret = DDI_FAILURE; 1208*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 1209*9126SWyllys.Ingersoll@Sun.COM } 1210*9126SWyllys.Ingersoll@Sun.COM 1211*9126SWyllys.Ingersoll@Sun.COM /* Zeroize the flag, which is used to keep track of what is allocated */ 1212*9126SWyllys.Ingersoll@Sun.COM tpm->flags = 0; 1213*9126SWyllys.Ingersoll@Sun.COM 1214*9126SWyllys.Ingersoll@Sun.COM tpm->accattr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 1215*9126SWyllys.Ingersoll@Sun.COM tpm->accattr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; 1216*9126SWyllys.Ingersoll@Sun.COM tpm->accattr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 1217*9126SWyllys.Ingersoll@Sun.COM 1218*9126SWyllys.Ingersoll@Sun.COM idx = 0; 1219*9126SWyllys.Ingersoll@Sun.COM ret = ddi_dev_nregs(tpm->dip, &nregs); 1220*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) 1221*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 1222*9126SWyllys.Ingersoll@Sun.COM 1223*9126SWyllys.Ingersoll@Sun.COM #ifdef DEBUG 1224*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_NOTE, "num registers = %d", nregs); 1225*9126SWyllys.Ingersoll@Sun.COM #endif 1226*9126SWyllys.Ingersoll@Sun.COM /* 1227*9126SWyllys.Ingersoll@Sun.COM * TPM vendors put the TPM registers in different 1228*9126SWyllys.Ingersoll@Sun.COM * slots in their register lists. They are not always 1229*9126SWyllys.Ingersoll@Sun.COM * the 1st set of registers, for instance. 1230*9126SWyllys.Ingersoll@Sun.COM * Loop until we find the set that matches the expected 1231*9126SWyllys.Ingersoll@Sun.COM * register size (0x5000). 1232*9126SWyllys.Ingersoll@Sun.COM */ 1233*9126SWyllys.Ingersoll@Sun.COM for (idx = 0; idx < nregs; idx++) { 1234*9126SWyllys.Ingersoll@Sun.COM off_t regsize; 1235*9126SWyllys.Ingersoll@Sun.COM 1236*9126SWyllys.Ingersoll@Sun.COM if ((ret = ddi_dev_regsize(tpm->dip, idx, ®size)) != 1237*9126SWyllys.Ingersoll@Sun.COM DDI_SUCCESS) 1238*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 1239*9126SWyllys.Ingersoll@Sun.COM #ifdef DEBUG 1240*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_NOTE, "register set #%d size = 0x%0lX", idx, 1241*9126SWyllys.Ingersoll@Sun.COM regsize); 1242*9126SWyllys.Ingersoll@Sun.COM #endif 1243*9126SWyllys.Ingersoll@Sun.COM /* The TIS spec says the TPM registers must be 0x5000 bytes */ 1244*9126SWyllys.Ingersoll@Sun.COM if (regsize == 0x5000) 1245*9126SWyllys.Ingersoll@Sun.COM break; 1246*9126SWyllys.Ingersoll@Sun.COM } 1247*9126SWyllys.Ingersoll@Sun.COM ret = ddi_regs_map_setup(tpm->dip, idx, (caddr_t *)&tpm->addr, 1248*9126SWyllys.Ingersoll@Sun.COM (offset_t)0, (offset_t)0x5000, 1249*9126SWyllys.Ingersoll@Sun.COM &tpm->accattr, &tpm->handle); 1250*9126SWyllys.Ingersoll@Sun.COM 1251*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 1252*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, 1253*9126SWyllys.Ingersoll@Sun.COM "%s:ddi_regs_map_setup failed ret: %d", 1254*9126SWyllys.Ingersoll@Sun.COM myname, ret); 1255*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 1256*9126SWyllys.Ingersoll@Sun.COM } 1257*9126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DIDREGSMAP; 1258*9126SWyllys.Ingersoll@Sun.COM 1259*9126SWyllys.Ingersoll@Sun.COM /* Enable TPM device according to the TIS specification */ 1260*9126SWyllys.Ingersoll@Sun.COM ret = tis_init(tpm); 1261*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 1262*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_init() failed ret: %d", 1263*9126SWyllys.Ingersoll@Sun.COM myname, ret); 1264*9126SWyllys.Ingersoll@Sun.COM 1265*9126SWyllys.Ingersoll@Sun.COM /* We need to clean up the ddi_regs_map_setup call */ 1266*9126SWyllys.Ingersoll@Sun.COM ddi_regs_map_free(&tpm->handle); 1267*9126SWyllys.Ingersoll@Sun.COM tpm->handle = NULL; 1268*9126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~TPM_DIDREGSMAP; 1269*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 1270*9126SWyllys.Ingersoll@Sun.COM } 1271*9126SWyllys.Ingersoll@Sun.COM 1272*9126SWyllys.Ingersoll@Sun.COM /* Initialize the inter-process lock */ 1273*9126SWyllys.Ingersoll@Sun.COM mutex_init(&tpm->dev_lock, NULL, MUTEX_DRIVER, NULL); 1274*9126SWyllys.Ingersoll@Sun.COM mutex_init(&tpm->pm_mutex, NULL, MUTEX_DRIVER, NULL); 1275*9126SWyllys.Ingersoll@Sun.COM cv_init(&tpm->suspend_cv, NULL, CV_DRIVER, NULL); 1276*9126SWyllys.Ingersoll@Sun.COM 1277*9126SWyllys.Ingersoll@Sun.COM /* Set the suspend/resume property */ 1278*9126SWyllys.Ingersoll@Sun.COM (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, 1279*9126SWyllys.Ingersoll@Sun.COM "pm-hardware-state", "needs-suspend-resume"); 1280*9126SWyllys.Ingersoll@Sun.COM 1281*9126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 1282*9126SWyllys.Ingersoll@Sun.COM tpm->suspended = 0; 1283*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 1284*9126SWyllys.Ingersoll@Sun.COM 1285*9126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DID_MUTEX; 1286*9126SWyllys.Ingersoll@Sun.COM 1287*9126SWyllys.Ingersoll@Sun.COM /* Initialize the buffer and the lock associated with it */ 1288*9126SWyllys.Ingersoll@Sun.COM tpm->bufsize = TPM_IO_BUF_SIZE; 1289*9126SWyllys.Ingersoll@Sun.COM tpm->iobuf = kmem_zalloc((sizeof (uint8_t))*(tpm->bufsize), KM_SLEEP); 1290*9126SWyllys.Ingersoll@Sun.COM if (tpm->iobuf == NULL) { 1291*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: failed to allocate iobuf", myname); 1292*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 1293*9126SWyllys.Ingersoll@Sun.COM } 1294*9126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DID_IO_ALLOC; 1295*9126SWyllys.Ingersoll@Sun.COM 1296*9126SWyllys.Ingersoll@Sun.COM mutex_init(&tpm->iobuf_lock, NULL, MUTEX_DRIVER, NULL); 1297*9126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DID_IO_MUTEX; 1298*9126SWyllys.Ingersoll@Sun.COM 1299*9126SWyllys.Ingersoll@Sun.COM cv_init(&tpm->iobuf_cv, NULL, CV_DRIVER, NULL); 1300*9126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DID_IO_CV; 1301*9126SWyllys.Ingersoll@Sun.COM 1302*9126SWyllys.Ingersoll@Sun.COM /* Create minor node */ 1303*9126SWyllys.Ingersoll@Sun.COM ret = ddi_create_minor_node(dip, "tpm", S_IFCHR, ddi_get_instance(dip), 1304*9126SWyllys.Ingersoll@Sun.COM DDI_PSEUDO, 0); 1305*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 1306*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: ddi_create_minor_node failed", myname); 1307*9126SWyllys.Ingersoll@Sun.COM goto FAIL; 1308*9126SWyllys.Ingersoll@Sun.COM } 1309*9126SWyllys.Ingersoll@Sun.COM tpm->flags |= TPM_DIDMINOR; 1310*9126SWyllys.Ingersoll@Sun.COM 1311*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 1312*9126SWyllys.Ingersoll@Sun.COM FAIL: 1313*9126SWyllys.Ingersoll@Sun.COM if (tpm != NULL) { 1314*9126SWyllys.Ingersoll@Sun.COM tpm_cleanup(dip, tpm); 1315*9126SWyllys.Ingersoll@Sun.COM ddi_soft_state_free(statep, instance); 1316*9126SWyllys.Ingersoll@Sun.COM tpm = NULL; 1317*9126SWyllys.Ingersoll@Sun.COM } 1318*9126SWyllys.Ingersoll@Sun.COM 1319*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1320*9126SWyllys.Ingersoll@Sun.COM } 1321*9126SWyllys.Ingersoll@Sun.COM 1322*9126SWyllys.Ingersoll@Sun.COM /* 1323*9126SWyllys.Ingersoll@Sun.COM * Called by tpm_detach and tpm_attach (only on failure) 1324*9126SWyllys.Ingersoll@Sun.COM * Free up the resources that are allocated 1325*9126SWyllys.Ingersoll@Sun.COM */ 1326*9126SWyllys.Ingersoll@Sun.COM static void 1327*9126SWyllys.Ingersoll@Sun.COM tpm_cleanup(dev_info_t *dip, tpm_state_t *tpm) 1328*9126SWyllys.Ingersoll@Sun.COM { 1329*9126SWyllys.Ingersoll@Sun.COM if (tpm == NULL) 1330*9126SWyllys.Ingersoll@Sun.COM return; 1331*9126SWyllys.Ingersoll@Sun.COM 1332*9126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DID_MUTEX) { 1333*9126SWyllys.Ingersoll@Sun.COM mutex_destroy(&tpm->dev_lock); 1334*9126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DID_MUTEX); 1335*9126SWyllys.Ingersoll@Sun.COM } 1336*9126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DID_IO_ALLOC) { 1337*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm->iobuf != NULL); 1338*9126SWyllys.Ingersoll@Sun.COM kmem_free(tpm->iobuf, (sizeof (uint8_t))*(tpm->bufsize)); 1339*9126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DID_IO_ALLOC); 1340*9126SWyllys.Ingersoll@Sun.COM } 1341*9126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DID_IO_MUTEX) { 1342*9126SWyllys.Ingersoll@Sun.COM mutex_destroy(&tpm->iobuf_lock); 1343*9126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DID_IO_MUTEX); 1344*9126SWyllys.Ingersoll@Sun.COM } 1345*9126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DID_IO_CV) { 1346*9126SWyllys.Ingersoll@Sun.COM cv_destroy(&tpm->iobuf_cv); 1347*9126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DID_IO_CV); 1348*9126SWyllys.Ingersoll@Sun.COM } 1349*9126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DIDREGSMAP) { 1350*9126SWyllys.Ingersoll@Sun.COM /* Free the mapped addresses */ 1351*9126SWyllys.Ingersoll@Sun.COM if (tpm->handle != NULL) 1352*9126SWyllys.Ingersoll@Sun.COM ddi_regs_map_free(&tpm->handle); 1353*9126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DIDREGSMAP); 1354*9126SWyllys.Ingersoll@Sun.COM } 1355*9126SWyllys.Ingersoll@Sun.COM if (tpm->flags & TPM_DIDMINOR) { 1356*9126SWyllys.Ingersoll@Sun.COM /* Remove minor node */ 1357*9126SWyllys.Ingersoll@Sun.COM ddi_remove_minor_node(dip, NULL); 1358*9126SWyllys.Ingersoll@Sun.COM tpm->flags &= ~(TPM_DIDMINOR); 1359*9126SWyllys.Ingersoll@Sun.COM } 1360*9126SWyllys.Ingersoll@Sun.COM } 1361*9126SWyllys.Ingersoll@Sun.COM 1362*9126SWyllys.Ingersoll@Sun.COM static int 1363*9126SWyllys.Ingersoll@Sun.COM tpm_suspend(tpm_state_t *tpm) 1364*9126SWyllys.Ingersoll@Sun.COM { 1365*9126SWyllys.Ingersoll@Sun.COM if (tpm == NULL) 1366*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1367*9126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 1368*9126SWyllys.Ingersoll@Sun.COM if (tpm->suspended) { 1369*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 1370*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 1371*9126SWyllys.Ingersoll@Sun.COM } 1372*9126SWyllys.Ingersoll@Sun.COM tpm->suspended = 1; 1373*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 1374*9126SWyllys.Ingersoll@Sun.COM 1375*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 1376*9126SWyllys.Ingersoll@Sun.COM } 1377*9126SWyllys.Ingersoll@Sun.COM 1378*9126SWyllys.Ingersoll@Sun.COM static int 1379*9126SWyllys.Ingersoll@Sun.COM tpm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 1380*9126SWyllys.Ingersoll@Sun.COM { 1381*9126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_detach"; 1382*9126SWyllys.Ingersoll@Sun.COM int instance; 1383*9126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 1384*9126SWyllys.Ingersoll@Sun.COM 1385*9126SWyllys.Ingersoll@Sun.COM ASSERT(dip != NULL); 1386*9126SWyllys.Ingersoll@Sun.COM 1387*9126SWyllys.Ingersoll@Sun.COM instance = ddi_get_instance(dip); 1388*9126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 1389*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 1390*9126SWyllys.Ingersoll@Sun.COM myname); 1391*9126SWyllys.Ingersoll@Sun.COM return (ENXIO); 1392*9126SWyllys.Ingersoll@Sun.COM } 1393*9126SWyllys.Ingersoll@Sun.COM 1394*9126SWyllys.Ingersoll@Sun.COM switch (cmd) { 1395*9126SWyllys.Ingersoll@Sun.COM case DDI_DETACH: 1396*9126SWyllys.Ingersoll@Sun.COM /* Body is after the switch stmt */ 1397*9126SWyllys.Ingersoll@Sun.COM break; 1398*9126SWyllys.Ingersoll@Sun.COM case DDI_SUSPEND: 1399*9126SWyllys.Ingersoll@Sun.COM return (tpm_suspend(tpm)); 1400*9126SWyllys.Ingersoll@Sun.COM default: 1401*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: case %d not implemented", myname, cmd); 1402*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1403*9126SWyllys.Ingersoll@Sun.COM } 1404*9126SWyllys.Ingersoll@Sun.COM 1405*9126SWyllys.Ingersoll@Sun.COM /* Since we are freeing tpm structure, we need to gain the lock */ 1406*9126SWyllys.Ingersoll@Sun.COM 1407*9126SWyllys.Ingersoll@Sun.COM tpm_cleanup(dip, tpm); 1408*9126SWyllys.Ingersoll@Sun.COM 1409*9126SWyllys.Ingersoll@Sun.COM mutex_destroy(&tpm->pm_mutex); 1410*9126SWyllys.Ingersoll@Sun.COM cv_destroy(&tpm->suspend_cv); 1411*9126SWyllys.Ingersoll@Sun.COM 1412*9126SWyllys.Ingersoll@Sun.COM /* Free the soft state */ 1413*9126SWyllys.Ingersoll@Sun.COM ddi_soft_state_free(statep, instance); 1414*9126SWyllys.Ingersoll@Sun.COM tpm = NULL; 1415*9126SWyllys.Ingersoll@Sun.COM 1416*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 1417*9126SWyllys.Ingersoll@Sun.COM } 1418*9126SWyllys.Ingersoll@Sun.COM 1419*9126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 1420*9126SWyllys.Ingersoll@Sun.COM static int 1421*9126SWyllys.Ingersoll@Sun.COM tpm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 1422*9126SWyllys.Ingersoll@Sun.COM { 1423*9126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_getinfo"; 1424*9126SWyllys.Ingersoll@Sun.COM int instance; 1425*9126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 1426*9126SWyllys.Ingersoll@Sun.COM 1427*9126SWyllys.Ingersoll@Sun.COM instance = ddi_get_instance(dip); 1428*9126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 1429*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 1430*9126SWyllys.Ingersoll@Sun.COM myname); 1431*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1432*9126SWyllys.Ingersoll@Sun.COM } 1433*9126SWyllys.Ingersoll@Sun.COM 1434*9126SWyllys.Ingersoll@Sun.COM switch (cmd) { 1435*9126SWyllys.Ingersoll@Sun.COM case DDI_INFO_DEVT2DEVINFO: 1436*9126SWyllys.Ingersoll@Sun.COM *resultp = tpm->dip; 1437*9126SWyllys.Ingersoll@Sun.COM break; 1438*9126SWyllys.Ingersoll@Sun.COM case DDI_INFO_DEVT2INSTANCE: 1439*9126SWyllys.Ingersoll@Sun.COM *resultp = 0; 1440*9126SWyllys.Ingersoll@Sun.COM break; 1441*9126SWyllys.Ingersoll@Sun.COM default: 1442*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: cmd %d is not implemented", myname, cmd); 1443*9126SWyllys.Ingersoll@Sun.COM return (DDI_FAILURE); 1444*9126SWyllys.Ingersoll@Sun.COM } 1445*9126SWyllys.Ingersoll@Sun.COM return (DDI_SUCCESS); 1446*9126SWyllys.Ingersoll@Sun.COM } 1447*9126SWyllys.Ingersoll@Sun.COM 1448*9126SWyllys.Ingersoll@Sun.COM /* 1449*9126SWyllys.Ingersoll@Sun.COM * Driver entry points 1450*9126SWyllys.Ingersoll@Sun.COM */ 1451*9126SWyllys.Ingersoll@Sun.COM 1452*9126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 1453*9126SWyllys.Ingersoll@Sun.COM static int 1454*9126SWyllys.Ingersoll@Sun.COM tpm_open(dev_t *devp, int flag, int otyp, cred_t *cred) 1455*9126SWyllys.Ingersoll@Sun.COM { 1456*9126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_open"; 1457*9126SWyllys.Ingersoll@Sun.COM int instance; 1458*9126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 1459*9126SWyllys.Ingersoll@Sun.COM 1460*9126SWyllys.Ingersoll@Sun.COM ASSERT(devp != NULL); 1461*9126SWyllys.Ingersoll@Sun.COM 1462*9126SWyllys.Ingersoll@Sun.COM instance = getminor(*devp); 1463*9126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 1464*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 1465*9126SWyllys.Ingersoll@Sun.COM myname); 1466*9126SWyllys.Ingersoll@Sun.COM return (ENXIO); 1467*9126SWyllys.Ingersoll@Sun.COM } 1468*9126SWyllys.Ingersoll@Sun.COM if (otyp != OTYP_CHR) { 1469*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: otyp(%d) != OTYP_CHR(%d)", 1470*9126SWyllys.Ingersoll@Sun.COM myname, otyp, OTYP_CHR); 1471*9126SWyllys.Ingersoll@Sun.COM return (EINVAL); 1472*9126SWyllys.Ingersoll@Sun.COM } 1473*9126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 1474*9126SWyllys.Ingersoll@Sun.COM while (tpm->suspended) 1475*9126SWyllys.Ingersoll@Sun.COM cv_wait(&tpm->suspend_cv, &tpm->pm_mutex); 1476*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 1477*9126SWyllys.Ingersoll@Sun.COM 1478*9126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->dev_lock); 1479*9126SWyllys.Ingersoll@Sun.COM if (tpm->dev_held) { 1480*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: the device is already being used", 1481*9126SWyllys.Ingersoll@Sun.COM myname); 1482*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->dev_lock); 1483*9126SWyllys.Ingersoll@Sun.COM return (EBUSY); 1484*9126SWyllys.Ingersoll@Sun.COM } 1485*9126SWyllys.Ingersoll@Sun.COM 1486*9126SWyllys.Ingersoll@Sun.COM /* The device is free so mark it busy */ 1487*9126SWyllys.Ingersoll@Sun.COM tpm->dev_held = 1; 1488*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->dev_lock); 1489*9126SWyllys.Ingersoll@Sun.COM 1490*9126SWyllys.Ingersoll@Sun.COM return (0); 1491*9126SWyllys.Ingersoll@Sun.COM } 1492*9126SWyllys.Ingersoll@Sun.COM 1493*9126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 1494*9126SWyllys.Ingersoll@Sun.COM static int 1495*9126SWyllys.Ingersoll@Sun.COM tpm_close(dev_t dev, int flag, int otyp, cred_t *cred) 1496*9126SWyllys.Ingersoll@Sun.COM { 1497*9126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_close"; 1498*9126SWyllys.Ingersoll@Sun.COM int instance; 1499*9126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 1500*9126SWyllys.Ingersoll@Sun.COM 1501*9126SWyllys.Ingersoll@Sun.COM instance = getminor(dev); 1502*9126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 1503*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 1504*9126SWyllys.Ingersoll@Sun.COM myname); 1505*9126SWyllys.Ingersoll@Sun.COM return (ENXIO); 1506*9126SWyllys.Ingersoll@Sun.COM } 1507*9126SWyllys.Ingersoll@Sun.COM if (otyp != OTYP_CHR) { 1508*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: otyp(%d) != OTYP_CHR(%d)", 1509*9126SWyllys.Ingersoll@Sun.COM myname, otyp, OTYP_CHR); 1510*9126SWyllys.Ingersoll@Sun.COM return (EINVAL); 1511*9126SWyllys.Ingersoll@Sun.COM } 1512*9126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 1513*9126SWyllys.Ingersoll@Sun.COM while (tpm->suspended) 1514*9126SWyllys.Ingersoll@Sun.COM cv_wait(&tpm->suspend_cv, &tpm->pm_mutex); 1515*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 1516*9126SWyllys.Ingersoll@Sun.COM 1517*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm->dev_held); 1518*9126SWyllys.Ingersoll@Sun.COM 1519*9126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->dev_lock); 1520*9126SWyllys.Ingersoll@Sun.COM ASSERT(mutex_owned(&tpm->dev_lock)); 1521*9126SWyllys.Ingersoll@Sun.COM tpm->dev_held = 0; 1522*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->dev_lock); 1523*9126SWyllys.Ingersoll@Sun.COM 1524*9126SWyllys.Ingersoll@Sun.COM return (0); 1525*9126SWyllys.Ingersoll@Sun.COM } 1526*9126SWyllys.Ingersoll@Sun.COM 1527*9126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 1528*9126SWyllys.Ingersoll@Sun.COM static int 1529*9126SWyllys.Ingersoll@Sun.COM tpm_read(dev_t dev, struct uio *uiop, cred_t *credp) 1530*9126SWyllys.Ingersoll@Sun.COM { 1531*9126SWyllys.Ingersoll@Sun.COM int ret; 1532*9126SWyllys.Ingersoll@Sun.COM uint32_t size; 1533*9126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_read"; 1534*9126SWyllys.Ingersoll@Sun.COM int instance; 1535*9126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 1536*9126SWyllys.Ingersoll@Sun.COM 1537*9126SWyllys.Ingersoll@Sun.COM instance = getminor(dev); 1538*9126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 1539*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 1540*9126SWyllys.Ingersoll@Sun.COM myname); 1541*9126SWyllys.Ingersoll@Sun.COM return (ENXIO); 1542*9126SWyllys.Ingersoll@Sun.COM } 1543*9126SWyllys.Ingersoll@Sun.COM if (uiop == NULL) { 1544*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: passed in uiop is NULL", myname); 1545*9126SWyllys.Ingersoll@Sun.COM return (EFAULT); 1546*9126SWyllys.Ingersoll@Sun.COM } 1547*9126SWyllys.Ingersoll@Sun.COM 1548*9126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 1549*9126SWyllys.Ingersoll@Sun.COM while (tpm->suspended) 1550*9126SWyllys.Ingersoll@Sun.COM cv_wait(&tpm->suspend_cv, &tpm->pm_mutex); 1551*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 1552*9126SWyllys.Ingersoll@Sun.COM 1553*9126SWyllys.Ingersoll@Sun.COM /* Receive the data after requiring the lock */ 1554*9126SWyllys.Ingersoll@Sun.COM ret = tpm_lock(tpm); 1555*9126SWyllys.Ingersoll@Sun.COM 1556*9126SWyllys.Ingersoll@Sun.COM /* Timeout reached */ 1557*9126SWyllys.Ingersoll@Sun.COM if (ret == ETIME) 1558*9126SWyllys.Ingersoll@Sun.COM return (ret); 1559*9126SWyllys.Ingersoll@Sun.COM 1560*9126SWyllys.Ingersoll@Sun.COM if (uiop->uio_resid > tpm->bufsize) { 1561*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: read_in data is bigger " 1562*9126SWyllys.Ingersoll@Sun.COM "than tpm->bufsize:read in:%d, bufsiz:%d", 1563*9126SWyllys.Ingersoll@Sun.COM myname, (int)uiop->uio_resid, (int)tpm->bufsize); 1564*9126SWyllys.Ingersoll@Sun.COM ret = EIO; 1565*9126SWyllys.Ingersoll@Sun.COM goto OUT; 1566*9126SWyllys.Ingersoll@Sun.COM } 1567*9126SWyllys.Ingersoll@Sun.COM 1568*9126SWyllys.Ingersoll@Sun.COM ret = tis_recv_data(tpm, tpm->iobuf, tpm->bufsize); 1569*9126SWyllys.Ingersoll@Sun.COM if (ret < TPM_HEADER_SIZE) { 1570*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_recv_data returned error", myname); 1571*9126SWyllys.Ingersoll@Sun.COM ret = EIO; 1572*9126SWyllys.Ingersoll@Sun.COM goto OUT; 1573*9126SWyllys.Ingersoll@Sun.COM } 1574*9126SWyllys.Ingersoll@Sun.COM 1575*9126SWyllys.Ingersoll@Sun.COM size = load32(tpm->iobuf, 2); 1576*9126SWyllys.Ingersoll@Sun.COM if (ret != size) { 1577*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_recv_data:" 1578*9126SWyllys.Ingersoll@Sun.COM "expected size=%d, actually read=%d", 1579*9126SWyllys.Ingersoll@Sun.COM myname, size, ret); 1580*9126SWyllys.Ingersoll@Sun.COM ret = EIO; 1581*9126SWyllys.Ingersoll@Sun.COM goto OUT; 1582*9126SWyllys.Ingersoll@Sun.COM } 1583*9126SWyllys.Ingersoll@Sun.COM 1584*9126SWyllys.Ingersoll@Sun.COM /* Send the buffer from the kernel to the userspace */ 1585*9126SWyllys.Ingersoll@Sun.COM ret = uiomove(tpm->iobuf, size, UIO_READ, uiop); 1586*9126SWyllys.Ingersoll@Sun.COM if (ret) { 1587*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: uiomove returned error", myname); 1588*9126SWyllys.Ingersoll@Sun.COM goto OUT; 1589*9126SWyllys.Ingersoll@Sun.COM } 1590*9126SWyllys.Ingersoll@Sun.COM 1591*9126SWyllys.Ingersoll@Sun.COM /* Zeroize the buffer... */ 1592*9126SWyllys.Ingersoll@Sun.COM bzero(tpm->iobuf, tpm->bufsize); 1593*9126SWyllys.Ingersoll@Sun.COM ret = DDI_SUCCESS; 1594*9126SWyllys.Ingersoll@Sun.COM OUT: 1595*9126SWyllys.Ingersoll@Sun.COM /* We are done now: wake up the waiting threads */ 1596*9126SWyllys.Ingersoll@Sun.COM tpm_unlock(tpm); 1597*9126SWyllys.Ingersoll@Sun.COM 1598*9126SWyllys.Ingersoll@Sun.COM return (ret); 1599*9126SWyllys.Ingersoll@Sun.COM } 1600*9126SWyllys.Ingersoll@Sun.COM 1601*9126SWyllys.Ingersoll@Sun.COM /*ARGSUSED*/ 1602*9126SWyllys.Ingersoll@Sun.COM static int 1603*9126SWyllys.Ingersoll@Sun.COM tpm_write(dev_t dev, struct uio *uiop, cred_t *credp) 1604*9126SWyllys.Ingersoll@Sun.COM { 1605*9126SWyllys.Ingersoll@Sun.COM int ret; 1606*9126SWyllys.Ingersoll@Sun.COM size_t len; 1607*9126SWyllys.Ingersoll@Sun.COM uint32_t size; 1608*9126SWyllys.Ingersoll@Sun.COM char *myname = "tpm_write"; 1609*9126SWyllys.Ingersoll@Sun.COM int instance; 1610*9126SWyllys.Ingersoll@Sun.COM tpm_state_t *tpm; 1611*9126SWyllys.Ingersoll@Sun.COM 1612*9126SWyllys.Ingersoll@Sun.COM instance = getminor(dev); 1613*9126SWyllys.Ingersoll@Sun.COM if ((tpm = ddi_get_soft_state(statep, instance)) == NULL) { 1614*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: stored pointer to tpm state is NULL", 1615*9126SWyllys.Ingersoll@Sun.COM myname); 1616*9126SWyllys.Ingersoll@Sun.COM return (ENXIO); 1617*9126SWyllys.Ingersoll@Sun.COM } 1618*9126SWyllys.Ingersoll@Sun.COM 1619*9126SWyllys.Ingersoll@Sun.COM if (uiop == NULL) { 1620*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: passed in uiop is NULL", myname); 1621*9126SWyllys.Ingersoll@Sun.COM return (EFAULT); 1622*9126SWyllys.Ingersoll@Sun.COM } 1623*9126SWyllys.Ingersoll@Sun.COM 1624*9126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->pm_mutex); 1625*9126SWyllys.Ingersoll@Sun.COM while (tpm->suspended) 1626*9126SWyllys.Ingersoll@Sun.COM cv_wait(&tpm->suspend_cv, &tpm->pm_mutex); 1627*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->pm_mutex); 1628*9126SWyllys.Ingersoll@Sun.COM 1629*9126SWyllys.Ingersoll@Sun.COM len = uiop->uio_resid; 1630*9126SWyllys.Ingersoll@Sun.COM if (len == 0) { 1631*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: requested read of len 0", myname); 1632*9126SWyllys.Ingersoll@Sun.COM return (0); 1633*9126SWyllys.Ingersoll@Sun.COM } 1634*9126SWyllys.Ingersoll@Sun.COM 1635*9126SWyllys.Ingersoll@Sun.COM /* Get the lock for using iobuf */ 1636*9126SWyllys.Ingersoll@Sun.COM ret = tpm_lock(tpm); 1637*9126SWyllys.Ingersoll@Sun.COM /* Timeout Reached */ 1638*9126SWyllys.Ingersoll@Sun.COM if (ret == ETIME) 1639*9126SWyllys.Ingersoll@Sun.COM return (ret); 1640*9126SWyllys.Ingersoll@Sun.COM 1641*9126SWyllys.Ingersoll@Sun.COM /* Copy the header and parse the structure to find out the size... */ 1642*9126SWyllys.Ingersoll@Sun.COM ret = uiomove(tpm->iobuf, TPM_HEADER_SIZE, UIO_WRITE, uiop); 1643*9126SWyllys.Ingersoll@Sun.COM if (ret) { 1644*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: uiomove returned error" 1645*9126SWyllys.Ingersoll@Sun.COM "while getting the the header", 1646*9126SWyllys.Ingersoll@Sun.COM myname); 1647*9126SWyllys.Ingersoll@Sun.COM goto OUT; 1648*9126SWyllys.Ingersoll@Sun.COM } 1649*9126SWyllys.Ingersoll@Sun.COM 1650*9126SWyllys.Ingersoll@Sun.COM /* Get the buffersize from the command buffer structure */ 1651*9126SWyllys.Ingersoll@Sun.COM size = load32(tpm->iobuf, TPM_PARAMSIZE_OFFSET); 1652*9126SWyllys.Ingersoll@Sun.COM 1653*9126SWyllys.Ingersoll@Sun.COM /* Copy the command to the contiguous buffer */ 1654*9126SWyllys.Ingersoll@Sun.COM if (size > tpm->bufsize) { 1655*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: size %d is greater than " 1656*9126SWyllys.Ingersoll@Sun.COM "the tpm's input buffer size %d", 1657*9126SWyllys.Ingersoll@Sun.COM myname, (int)size, (int)tpm->bufsize); 1658*9126SWyllys.Ingersoll@Sun.COM ret = ENXIO; 1659*9126SWyllys.Ingersoll@Sun.COM goto OUT; 1660*9126SWyllys.Ingersoll@Sun.COM } 1661*9126SWyllys.Ingersoll@Sun.COM 1662*9126SWyllys.Ingersoll@Sun.COM /* Copy the buffer from the userspace to kernel */ 1663*9126SWyllys.Ingersoll@Sun.COM ret = uiomove(tpm->iobuf+TPM_HEADER_SIZE, size-TPM_HEADER_SIZE, 1664*9126SWyllys.Ingersoll@Sun.COM UIO_WRITE, uiop); 1665*9126SWyllys.Ingersoll@Sun.COM 1666*9126SWyllys.Ingersoll@Sun.COM if (ret) { 1667*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: uiomove returned error" 1668*9126SWyllys.Ingersoll@Sun.COM "while getting the rest of the command", myname); 1669*9126SWyllys.Ingersoll@Sun.COM goto OUT; 1670*9126SWyllys.Ingersoll@Sun.COM } 1671*9126SWyllys.Ingersoll@Sun.COM 1672*9126SWyllys.Ingersoll@Sun.COM /* Send the command */ 1673*9126SWyllys.Ingersoll@Sun.COM ret = tis_send_data(tpm, tpm->iobuf, size); 1674*9126SWyllys.Ingersoll@Sun.COM if (ret != DDI_SUCCESS) { 1675*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "%s: tis_send_data returned error", myname); 1676*9126SWyllys.Ingersoll@Sun.COM ret = EFAULT; 1677*9126SWyllys.Ingersoll@Sun.COM goto OUT; 1678*9126SWyllys.Ingersoll@Sun.COM } 1679*9126SWyllys.Ingersoll@Sun.COM 1680*9126SWyllys.Ingersoll@Sun.COM /* Zeroize the buffer... */ 1681*9126SWyllys.Ingersoll@Sun.COM bzero(tpm->iobuf, tpm->bufsize); 1682*9126SWyllys.Ingersoll@Sun.COM ret = DDI_SUCCESS; 1683*9126SWyllys.Ingersoll@Sun.COM OUT: 1684*9126SWyllys.Ingersoll@Sun.COM tpm_unlock(tpm); 1685*9126SWyllys.Ingersoll@Sun.COM return (ret); 1686*9126SWyllys.Ingersoll@Sun.COM } 1687*9126SWyllys.Ingersoll@Sun.COM 1688*9126SWyllys.Ingersoll@Sun.COM /* 1689*9126SWyllys.Ingersoll@Sun.COM * This is to deal with the contentions for the iobuf 1690*9126SWyllys.Ingersoll@Sun.COM */ 1691*9126SWyllys.Ingersoll@Sun.COM static inline int 1692*9126SWyllys.Ingersoll@Sun.COM tpm_lock(tpm_state_t *tpm) 1693*9126SWyllys.Ingersoll@Sun.COM { 1694*9126SWyllys.Ingersoll@Sun.COM int ret; 1695*9126SWyllys.Ingersoll@Sun.COM clock_t timeout; 1696*9126SWyllys.Ingersoll@Sun.COM 1697*9126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->iobuf_lock); 1698*9126SWyllys.Ingersoll@Sun.COM ASSERT(mutex_owned(&tpm->iobuf_lock)); 1699*9126SWyllys.Ingersoll@Sun.COM 1700*9126SWyllys.Ingersoll@Sun.COM timeout = ddi_get_lbolt() + drv_usectohz(TPM_IO_TIMEOUT); 1701*9126SWyllys.Ingersoll@Sun.COM 1702*9126SWyllys.Ingersoll@Sun.COM /* Wait until the iobuf becomes free with the timeout */ 1703*9126SWyllys.Ingersoll@Sun.COM while (tpm->iobuf_inuse) { 1704*9126SWyllys.Ingersoll@Sun.COM ret = cv_timedwait(&tpm->iobuf_cv, &tpm->iobuf_lock, timeout); 1705*9126SWyllys.Ingersoll@Sun.COM if (ret <= 0) { 1706*9126SWyllys.Ingersoll@Sun.COM /* Timeout reached */ 1707*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->iobuf_lock); 1708*9126SWyllys.Ingersoll@Sun.COM cmn_err(CE_WARN, "tpm_lock:iorequest timed out"); 1709*9126SWyllys.Ingersoll@Sun.COM return (ETIME); 1710*9126SWyllys.Ingersoll@Sun.COM } 1711*9126SWyllys.Ingersoll@Sun.COM } 1712*9126SWyllys.Ingersoll@Sun.COM tpm->iobuf_inuse = 1; 1713*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->iobuf_lock); 1714*9126SWyllys.Ingersoll@Sun.COM return (0); 1715*9126SWyllys.Ingersoll@Sun.COM } 1716*9126SWyllys.Ingersoll@Sun.COM 1717*9126SWyllys.Ingersoll@Sun.COM /* 1718*9126SWyllys.Ingersoll@Sun.COM * This is to deal with the contentions for the iobuf 1719*9126SWyllys.Ingersoll@Sun.COM */ 1720*9126SWyllys.Ingersoll@Sun.COM static inline void 1721*9126SWyllys.Ingersoll@Sun.COM tpm_unlock(tpm_state_t *tpm) 1722*9126SWyllys.Ingersoll@Sun.COM { 1723*9126SWyllys.Ingersoll@Sun.COM /* Wake up the waiting threads */ 1724*9126SWyllys.Ingersoll@Sun.COM mutex_enter(&tpm->iobuf_lock); 1725*9126SWyllys.Ingersoll@Sun.COM ASSERT(tpm->iobuf_inuse == 1 && mutex_owned(&tpm->iobuf_lock)); 1726*9126SWyllys.Ingersoll@Sun.COM tpm->iobuf_inuse = 0; 1727*9126SWyllys.Ingersoll@Sun.COM cv_broadcast(&tpm->iobuf_cv); 1728*9126SWyllys.Ingersoll@Sun.COM mutex_exit(&tpm->iobuf_lock); 1729*9126SWyllys.Ingersoll@Sun.COM } 1730