2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2004 IBM Corporation
*
* Authors :
* Leendert van Doorn < leendert @ watson . ibm . com >
* Dave Safford < safford @ watson . ibm . com >
* Reiner Sailer < sailer @ watson . ibm . com >
* Kylene Hall < kjhall @ us . ibm . com >
*
2007-08-23 01:01:04 +04:00
* Maintained by : < tpmdd - devel @ lists . sourceforge . net >
2005-04-17 02:20:36 +04:00
*
* Device driver for TCG / TCPA TPM ( trusted platform module ) .
* Specifications at www . trustedcomputinggroup . org
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation , version 2 of the
* License .
*
* Note , the TPM chip is not interrupt driven ( only polling )
* and can have very long timeouts ( minutes ! ) . Hence the unusual
2005-06-24 09:01:47 +04:00
* calls to msleep .
2005-04-17 02:20:36 +04:00
*
*/
# include <linux/poll.h>
2007-05-08 11:32:02 +04:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <linux/spinlock.h>
2007-05-08 11:32:02 +04:00
2005-04-17 02:20:36 +04:00
# include "tpm.h"
2005-06-24 09:01:48 +04:00
enum tpm_const {
TPM_MINOR = 224 , /* officially assigned */
2009-09-24 23:27:46 +04:00
TPM_BUFSIZE = 4096 ,
2005-06-24 09:01:48 +04:00
TPM_NUM_DEVICES = 256 ,
} ;
2005-04-17 02:20:36 +04:00
2006-04-22 13:37:38 +04:00
enum tpm_duration {
TPM_SHORT = 0 ,
TPM_MEDIUM = 1 ,
TPM_LONG = 2 ,
TPM_UNDEFINED ,
} ;
# define TPM_MAX_ORDINAL 243
# define TPM_MAX_PROTECTED_ORDINAL 12
# define TPM_PROTECTED_ORDINAL_MASK 0xFF
2005-04-17 02:20:36 +04:00
static LIST_HEAD ( tpm_chip_list ) ;
static DEFINE_SPINLOCK ( driver_lock ) ;
2006-04-22 13:38:32 +04:00
static DECLARE_BITMAP ( dev_mask , TPM_NUM_DEVICES ) ;
2005-04-17 02:20:36 +04:00
2006-04-22 13:37:38 +04:00
/*
* Array with one entry per ordinal defining the maximum amount
* of time the chip could take to return the result . The ordinal
* designation of short , medium or long is defined in a table in
* TCG Specification TPM Main Part 2 TPM Structures Section 17. The
* values of the SHORT , MEDIUM , and LONG durations are retrieved
* from the chip during initialization with a call to tpm_get_timeouts .
*/
static const u8 tpm_protected_ordinal_duration [ TPM_MAX_PROTECTED_ORDINAL ] = {
TPM_UNDEFINED , /* 0 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED , /* 5 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_SHORT , /* 10 */
TPM_SHORT ,
} ;
static const u8 tpm_ordinal_duration [ TPM_MAX_ORDINAL ] = {
TPM_UNDEFINED , /* 0 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED , /* 5 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_SHORT , /* 10 */
TPM_SHORT ,
TPM_MEDIUM ,
TPM_LONG ,
TPM_LONG ,
TPM_MEDIUM , /* 15 */
TPM_SHORT ,
TPM_SHORT ,
TPM_MEDIUM ,
TPM_LONG ,
TPM_SHORT , /* 20 */
TPM_SHORT ,
TPM_MEDIUM ,
TPM_MEDIUM ,
TPM_MEDIUM ,
TPM_SHORT , /* 25 */
TPM_SHORT ,
TPM_MEDIUM ,
TPM_SHORT ,
TPM_SHORT ,
TPM_MEDIUM , /* 30 */
TPM_LONG ,
TPM_MEDIUM ,
TPM_SHORT ,
TPM_SHORT ,
TPM_SHORT , /* 35 */
TPM_MEDIUM ,
TPM_MEDIUM ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_MEDIUM , /* 40 */
TPM_LONG ,
TPM_MEDIUM ,
TPM_SHORT ,
TPM_SHORT ,
TPM_SHORT , /* 45 */
TPM_SHORT ,
TPM_SHORT ,
TPM_SHORT ,
TPM_LONG ,
TPM_MEDIUM , /* 50 */
TPM_MEDIUM ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED , /* 55 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_MEDIUM , /* 60 */
TPM_MEDIUM ,
TPM_MEDIUM ,
TPM_SHORT ,
TPM_SHORT ,
TPM_MEDIUM , /* 65 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_SHORT , /* 70 */
TPM_SHORT ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED , /* 75 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_LONG , /* 80 */
TPM_UNDEFINED ,
TPM_MEDIUM ,
TPM_LONG ,
TPM_SHORT ,
TPM_UNDEFINED , /* 85 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_SHORT , /* 90 */
TPM_SHORT ,
TPM_SHORT ,
TPM_SHORT ,
TPM_SHORT ,
TPM_UNDEFINED , /* 95 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_MEDIUM , /* 100 */
TPM_SHORT ,
TPM_SHORT ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED , /* 105 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_SHORT , /* 110 */
TPM_SHORT ,
TPM_SHORT ,
TPM_SHORT ,
TPM_SHORT ,
TPM_SHORT , /* 115 */
TPM_SHORT ,
TPM_SHORT ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_LONG , /* 120 */
TPM_LONG ,
TPM_MEDIUM ,
TPM_UNDEFINED ,
TPM_SHORT ,
TPM_SHORT , /* 125 */
TPM_SHORT ,
TPM_LONG ,
TPM_SHORT ,
TPM_SHORT ,
TPM_SHORT , /* 130 */
TPM_MEDIUM ,
TPM_UNDEFINED ,
TPM_SHORT ,
TPM_MEDIUM ,
TPM_UNDEFINED , /* 135 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_SHORT , /* 140 */
TPM_SHORT ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED , /* 145 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_SHORT , /* 150 */
TPM_MEDIUM ,
TPM_MEDIUM ,
TPM_SHORT ,
TPM_SHORT ,
TPM_UNDEFINED , /* 155 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_SHORT , /* 160 */
TPM_SHORT ,
TPM_SHORT ,
TPM_SHORT ,
TPM_UNDEFINED ,
TPM_UNDEFINED , /* 165 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_LONG , /* 170 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED , /* 175 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_MEDIUM , /* 180 */
TPM_SHORT ,
TPM_MEDIUM ,
TPM_MEDIUM ,
TPM_MEDIUM ,
TPM_MEDIUM , /* 185 */
TPM_SHORT ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED , /* 190 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED , /* 195 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_SHORT , /* 200 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_SHORT ,
TPM_SHORT , /* 205 */
TPM_SHORT ,
TPM_SHORT ,
TPM_SHORT ,
TPM_SHORT ,
TPM_MEDIUM , /* 210 */
TPM_UNDEFINED ,
TPM_MEDIUM ,
TPM_MEDIUM ,
TPM_MEDIUM ,
TPM_UNDEFINED , /* 215 */
TPM_MEDIUM ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_SHORT ,
TPM_SHORT , /* 220 */
TPM_SHORT ,
TPM_SHORT ,
TPM_SHORT ,
TPM_SHORT ,
TPM_UNDEFINED , /* 225 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_SHORT , /* 230 */
TPM_LONG ,
TPM_MEDIUM ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED , /* 235 */
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_UNDEFINED ,
TPM_SHORT , /* 240 */
TPM_UNDEFINED ,
TPM_MEDIUM ,
} ;
2005-04-17 02:20:36 +04:00
static void user_reader_timeout ( unsigned long ptr )
{
struct tpm_chip * chip = ( struct tpm_chip * ) ptr ;
2005-11-14 03:07:43 +03:00
schedule_work ( & chip - > work ) ;
}
2006-11-22 17:57:56 +03:00
static void timeout_work ( struct work_struct * work )
2005-11-14 03:07:43 +03:00
{
2006-11-22 17:57:56 +03:00
struct tpm_chip * chip = container_of ( work , struct tpm_chip , work ) ;
2005-11-14 03:07:43 +03:00
2007-05-08 11:32:02 +04:00
mutex_lock ( & chip - > buffer_mutex ) ;
2005-04-17 02:20:36 +04:00
atomic_set ( & chip - > data_pending , 0 ) ;
memset ( chip - > data_buffer , 0 , TPM_BUFSIZE ) ;
2007-05-08 11:32:02 +04:00
mutex_unlock ( & chip - > buffer_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2006-04-22 13:37:38 +04:00
/*
* Returns max number of jiffies to wait
*/
unsigned long tpm_calc_ordinal_duration ( struct tpm_chip * chip ,
u32 ordinal )
{
int duration_idx = TPM_UNDEFINED ;
int duration = 0 ;
if ( ordinal < TPM_MAX_ORDINAL )
duration_idx = tpm_ordinal_duration [ ordinal ] ;
else if ( ( ordinal & TPM_PROTECTED_ORDINAL_MASK ) <
TPM_MAX_PROTECTED_ORDINAL )
duration_idx =
tpm_protected_ordinal_duration [ ordinal &
TPM_PROTECTED_ORDINAL_MASK ] ;
if ( duration_idx ! = TPM_UNDEFINED )
2006-04-22 13:38:19 +04:00
duration = chip - > vendor . duration [ duration_idx ] ;
2006-04-22 13:37:38 +04:00
if ( duration < = 0 )
return 2 * 60 * HZ ;
else
return duration ;
}
EXPORT_SYMBOL_GPL ( tpm_calc_ordinal_duration ) ;
2005-04-17 02:20:36 +04:00
/*
* Internal kernel interface to transmit TPM commands
*/
static ssize_t tpm_transmit ( struct tpm_chip * chip , const char * buf ,
size_t bufsiz )
{
2005-06-24 09:02:02 +04:00
ssize_t rc ;
2006-04-22 13:37:38 +04:00
u32 count , ordinal ;
2005-06-24 09:01:47 +04:00
unsigned long stop ;
2005-04-17 02:20:36 +04:00
2005-06-24 09:01:59 +04:00
count = be32_to_cpu ( * ( ( __be32 * ) ( buf + 2 ) ) ) ;
2006-04-22 13:37:38 +04:00
ordinal = be32_to_cpu ( * ( ( __be32 * ) ( buf + 6 ) ) ) ;
2005-04-17 02:20:36 +04:00
if ( count = = 0 )
return - ENODATA ;
if ( count > bufsiz ) {
2005-10-31 02:03:24 +03:00
dev_err ( chip - > dev ,
2005-04-26 18:43:41 +04:00
" invalid count value %x %zx \n " , count , bufsiz ) ;
2005-04-17 02:20:36 +04:00
return - E2BIG ;
}
2007-05-08 11:32:02 +04:00
mutex_lock ( & chip - > tpm_mutex ) ;
2005-04-17 02:20:36 +04:00
2006-04-22 13:37:15 +04:00
if ( ( rc = chip - > vendor . send ( chip , ( u8 * ) buf , count ) ) < 0 ) {
2005-10-31 02:03:24 +03:00
dev_err ( chip - > dev ,
2005-06-24 09:02:02 +04:00
" tpm_transmit: tpm_send: error %zd \n " , rc ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2006-04-22 13:38:03 +04:00
if ( chip - > vendor . irq )
goto out_recv ;
2006-04-22 13:37:38 +04:00
stop = jiffies + tpm_calc_ordinal_duration ( chip , ordinal ) ;
2005-04-17 02:20:36 +04:00
do {
2006-04-22 13:37:15 +04:00
u8 status = chip - > vendor . status ( chip ) ;
if ( ( status & chip - > vendor . req_complete_mask ) = =
chip - > vendor . req_complete_val )
2005-04-17 02:20:36 +04:00
goto out_recv ;
2005-06-24 09:02:02 +04:00
2006-04-22 13:37:15 +04:00
if ( ( status = = chip - > vendor . req_canceled ) ) {
2005-10-31 02:03:24 +03:00
dev_err ( chip - > dev , " Operation Canceled \n " ) ;
2005-06-24 09:02:02 +04:00
rc = - ECANCELED ;
goto out ;
}
msleep ( TPM_TIMEOUT ) ; /* CHECK */
2005-04-17 02:20:36 +04:00
rmb ( ) ;
2005-06-24 09:01:47 +04:00
} while ( time_before ( jiffies , stop ) ) ;
2005-04-17 02:20:36 +04:00
2006-04-22 13:37:15 +04:00
chip - > vendor . cancel ( chip ) ;
2005-10-31 02:03:24 +03:00
dev_err ( chip - > dev , " Operation Timed out \n " ) ;
2005-06-24 09:02:02 +04:00
rc = - ETIME ;
goto out ;
2005-04-17 02:20:36 +04:00
out_recv :
2006-04-22 13:37:15 +04:00
rc = chip - > vendor . recv ( chip , ( u8 * ) buf , bufsiz ) ;
2005-06-24 09:02:02 +04:00
if ( rc < 0 )
2005-10-31 02:03:24 +03:00
dev_err ( chip - > dev ,
2005-06-24 09:02:02 +04:00
" tpm_transmit: tpm_recv: error %zd \n " , rc ) ;
out :
2007-05-08 11:32:02 +04:00
mutex_unlock ( & chip - > tpm_mutex ) ;
2005-06-24 09:02:02 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
}
# define TPM_DIGEST_SIZE 20
2006-04-22 13:37:05 +04:00
# define TPM_ERROR_SIZE 10
# define TPM_RET_CODE_IDX 6
enum tpm_capabilities {
2009-02-02 20:23:43 +03:00
TPM_CAP_FLAG = cpu_to_be32 ( 4 ) ,
TPM_CAP_PROP = cpu_to_be32 ( 5 ) ,
CAP_VERSION_1_1 = cpu_to_be32 ( 0x06 ) ,
CAP_VERSION_1_2 = cpu_to_be32 ( 0x1A )
2006-04-22 13:37:05 +04:00
} ;
enum tpm_sub_capabilities {
2009-02-02 20:23:43 +03:00
TPM_CAP_PROP_PCR = cpu_to_be32 ( 0x101 ) ,
TPM_CAP_PROP_MANUFACTURER = cpu_to_be32 ( 0x103 ) ,
TPM_CAP_FLAG_PERM = cpu_to_be32 ( 0x108 ) ,
TPM_CAP_FLAG_VOL = cpu_to_be32 ( 0x109 ) ,
TPM_CAP_PROP_OWNER = cpu_to_be32 ( 0x111 ) ,
TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32 ( 0x115 ) ,
TPM_CAP_PROP_TIS_DURATION = cpu_to_be32 ( 0x120 ) ,
2006-04-22 13:37:05 +04:00
2005-04-17 02:20:36 +04:00
} ;
2009-02-02 20:23:43 +03:00
static ssize_t transmit_cmd ( struct tpm_chip * chip , struct tpm_cmd_t * cmd ,
int len , const char * desc )
2006-04-22 13:37:05 +04:00
{
int err ;
2009-02-02 20:23:43 +03:00
len = tpm_transmit ( chip , ( u8 * ) cmd , len ) ;
2006-04-22 13:37:05 +04:00
if ( len < 0 )
return len ;
if ( len = = TPM_ERROR_SIZE ) {
2009-02-02 20:23:43 +03:00
err = be32_to_cpu ( cmd - > header . out . return_code ) ;
2006-04-22 13:37:05 +04:00
dev_dbg ( chip - > dev , " A TPM error (%d) occurred %s \n " , err , desc ) ;
return err ;
}
return 0 ;
}
2009-02-02 20:23:43 +03:00
# define TPM_INTERNAL_RESULT_SIZE 200
# define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
# define TPM_ORD_GET_CAP cpu_to_be32(101)
static const struct tpm_input_header tpm_getcap_header = {
. tag = TPM_TAG_RQU_COMMAND ,
. length = cpu_to_be32 ( 22 ) ,
. ordinal = TPM_ORD_GET_CAP
} ;
ssize_t tpm_getcap ( struct device * dev , __be32 subcap_id , cap_t * cap ,
const char * desc )
{
struct tpm_cmd_t tpm_cmd ;
int rc ;
struct tpm_chip * chip = dev_get_drvdata ( dev ) ;
tpm_cmd . header . in = tpm_getcap_header ;
if ( subcap_id = = CAP_VERSION_1_1 | | subcap_id = = CAP_VERSION_1_2 ) {
tpm_cmd . params . getcap_in . cap = subcap_id ;
/*subcap field not necessary */
tpm_cmd . params . getcap_in . subcap_size = cpu_to_be32 ( 0 ) ;
tpm_cmd . header . in . length - = cpu_to_be32 ( sizeof ( __be32 ) ) ;
} else {
if ( subcap_id = = TPM_CAP_FLAG_PERM | |
subcap_id = = TPM_CAP_FLAG_VOL )
tpm_cmd . params . getcap_in . cap = TPM_CAP_FLAG ;
else
tpm_cmd . params . getcap_in . cap = TPM_CAP_PROP ;
tpm_cmd . params . getcap_in . subcap_size = cpu_to_be32 ( 4 ) ;
tpm_cmd . params . getcap_in . subcap = subcap_id ;
}
rc = transmit_cmd ( chip , & tpm_cmd , TPM_INTERNAL_RESULT_SIZE , desc ) ;
if ( ! rc )
* cap = tpm_cmd . params . getcap_out . cap ;
return rc ;
}
2006-04-22 13:37:50 +04:00
void tpm_gen_interrupt ( struct tpm_chip * chip )
{
2009-02-02 20:23:43 +03:00
struct tpm_cmd_t tpm_cmd ;
2006-04-22 13:37:50 +04:00
ssize_t rc ;
2009-02-02 20:23:43 +03:00
tpm_cmd . header . in = tpm_getcap_header ;
tpm_cmd . params . getcap_in . cap = TPM_CAP_PROP ;
tpm_cmd . params . getcap_in . subcap_size = cpu_to_be32 ( 4 ) ;
tpm_cmd . params . getcap_in . subcap = TPM_CAP_PROP_TIS_TIMEOUT ;
2006-04-22 13:37:50 +04:00
2009-02-02 20:23:43 +03:00
rc = transmit_cmd ( chip , & tpm_cmd , TPM_INTERNAL_RESULT_SIZE ,
2006-04-22 13:37:50 +04:00
" attempting to determine the timeouts " ) ;
}
EXPORT_SYMBOL_GPL ( tpm_gen_interrupt ) ;
void tpm_get_timeouts ( struct tpm_chip * chip )
{
2009-02-02 20:23:43 +03:00
struct tpm_cmd_t tpm_cmd ;
struct timeout_t * timeout_cap ;
struct duration_t * duration_cap ;
2006-04-22 13:37:50 +04:00
ssize_t rc ;
u32 timeout ;
2009-02-02 20:23:43 +03:00
tpm_cmd . header . in = tpm_getcap_header ;
tpm_cmd . params . getcap_in . cap = TPM_CAP_PROP ;
tpm_cmd . params . getcap_in . subcap_size = cpu_to_be32 ( 4 ) ;
tpm_cmd . params . getcap_in . subcap = TPM_CAP_PROP_TIS_TIMEOUT ;
2006-04-22 13:37:50 +04:00
2009-02-02 20:23:43 +03:00
rc = transmit_cmd ( chip , & tpm_cmd , TPM_INTERNAL_RESULT_SIZE ,
2006-04-22 13:37:50 +04:00
" attempting to determine the timeouts " ) ;
if ( rc )
goto duration ;
2009-02-02 20:23:43 +03:00
if ( be32_to_cpu ( tpm_cmd . header . out . length )
2006-04-22 13:37:50 +04:00
! = 4 * sizeof ( u32 ) )
goto duration ;
2009-02-02 20:23:43 +03:00
timeout_cap = & tpm_cmd . params . getcap_out . cap . timeout ;
2006-04-22 13:37:50 +04:00
/* Don't overwrite default if value is 0 */
2009-02-02 20:23:43 +03:00
timeout = be32_to_cpu ( timeout_cap - > a ) ;
2006-04-22 13:37:50 +04:00
if ( timeout )
2008-10-16 09:04:34 +04:00
chip - > vendor . timeout_a = usecs_to_jiffies ( timeout ) ;
2009-02-02 20:23:43 +03:00
timeout = be32_to_cpu ( timeout_cap - > b ) ;
2006-04-22 13:37:50 +04:00
if ( timeout )
2008-10-16 09:04:34 +04:00
chip - > vendor . timeout_b = usecs_to_jiffies ( timeout ) ;
2009-02-02 20:23:43 +03:00
timeout = be32_to_cpu ( timeout_cap - > c ) ;
2006-04-22 13:37:50 +04:00
if ( timeout )
2008-10-16 09:04:34 +04:00
chip - > vendor . timeout_c = usecs_to_jiffies ( timeout ) ;
2009-02-02 20:23:43 +03:00
timeout = be32_to_cpu ( timeout_cap - > d ) ;
2006-04-22 13:37:50 +04:00
if ( timeout )
2008-10-16 09:04:34 +04:00
chip - > vendor . timeout_d = usecs_to_jiffies ( timeout ) ;
2006-04-22 13:37:50 +04:00
duration :
2009-02-02 20:23:43 +03:00
tpm_cmd . header . in = tpm_getcap_header ;
tpm_cmd . params . getcap_in . cap = TPM_CAP_PROP ;
tpm_cmd . params . getcap_in . subcap_size = cpu_to_be32 ( 4 ) ;
tpm_cmd . params . getcap_in . subcap = TPM_CAP_PROP_TIS_DURATION ;
2006-04-22 13:37:50 +04:00
2009-02-02 20:23:43 +03:00
rc = transmit_cmd ( chip , & tpm_cmd , TPM_INTERNAL_RESULT_SIZE ,
2006-04-22 13:37:50 +04:00
" attempting to determine the durations " ) ;
if ( rc )
return ;
2009-02-02 20:23:43 +03:00
if ( be32_to_cpu ( tpm_cmd . header . out . return_code )
2006-04-22 13:37:50 +04:00
! = 3 * sizeof ( u32 ) )
return ;
2009-02-02 20:23:43 +03:00
duration_cap = & tpm_cmd . params . getcap_out . cap . duration ;
2006-04-22 13:37:50 +04:00
chip - > vendor . duration [ TPM_SHORT ] =
2009-02-02 20:23:43 +03:00
usecs_to_jiffies ( be32_to_cpu ( duration_cap - > tpm_short ) ) ;
2008-10-16 09:04:35 +04:00
/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
* value wrong and apparently reports msecs rather than usecs . So we
* fix up the resulting too - small TPM_SHORT value to make things work .
*/
if ( chip - > vendor . duration [ TPM_SHORT ] < ( HZ / 100 ) )
chip - > vendor . duration [ TPM_SHORT ] = HZ ;
2006-04-22 13:37:50 +04:00
chip - > vendor . duration [ TPM_MEDIUM ] =
2009-02-02 20:23:43 +03:00
usecs_to_jiffies ( be32_to_cpu ( duration_cap - > tpm_medium ) ) ;
2006-04-22 13:37:50 +04:00
chip - > vendor . duration [ TPM_LONG ] =
2009-02-02 20:23:43 +03:00
usecs_to_jiffies ( be32_to_cpu ( duration_cap - > tpm_long ) ) ;
2006-04-22 13:37:50 +04:00
}
EXPORT_SYMBOL_GPL ( tpm_get_timeouts ) ;
void tpm_continue_selftest ( struct tpm_chip * chip )
{
u8 data [ ] = {
0 , 193 , /* TPM_TAG_RQU_COMMAND */
0 , 0 , 0 , 10 , /* length */
0 , 0 , 0 , 83 , /* TPM_ORD_GetCapability */
} ;
tpm_transmit ( chip , data , sizeof ( data ) ) ;
}
EXPORT_SYMBOL_GPL ( tpm_continue_selftest ) ;
ssize_t tpm_show_enabled ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
2009-02-02 20:23:43 +03:00
cap_t cap ;
2006-04-22 13:37:50 +04:00
ssize_t rc ;
2009-02-02 20:23:43 +03:00
rc = tpm_getcap ( dev , TPM_CAP_FLAG_PERM , & cap ,
" attempting to determine the permanent enabled state " ) ;
if ( rc )
2006-04-22 13:37:50 +04:00
return 0 ;
2008-07-26 06:44:59 +04:00
2009-02-02 20:23:43 +03:00
rc = sprintf ( buf , " %d \n " , ! cap . perm_flags . disable ) ;
2008-07-26 06:44:59 +04:00
return rc ;
2006-04-22 13:37:50 +04:00
}
EXPORT_SYMBOL_GPL ( tpm_show_enabled ) ;
ssize_t tpm_show_active ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
2009-02-02 20:23:43 +03:00
cap_t cap ;
2006-04-22 13:37:50 +04:00
ssize_t rc ;
2009-02-02 20:23:43 +03:00
rc = tpm_getcap ( dev , TPM_CAP_FLAG_PERM , & cap ,
" attempting to determine the permanent active state " ) ;
if ( rc )
2006-04-22 13:37:50 +04:00
return 0 ;
2008-07-26 06:44:59 +04:00
2009-02-02 20:23:43 +03:00
rc = sprintf ( buf , " %d \n " , ! cap . perm_flags . deactivated ) ;
2008-07-26 06:44:59 +04:00
return rc ;
2006-04-22 13:37:50 +04:00
}
EXPORT_SYMBOL_GPL ( tpm_show_active ) ;
ssize_t tpm_show_owned ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
2009-02-02 20:23:43 +03:00
cap_t cap ;
2006-04-22 13:37:50 +04:00
ssize_t rc ;
2009-02-02 20:23:43 +03:00
rc = tpm_getcap ( dev , TPM_CAP_PROP_OWNER , & cap ,
" attempting to determine the owner state " ) ;
if ( rc )
2006-04-22 13:37:50 +04:00
return 0 ;
2008-07-26 06:44:59 +04:00
2009-02-02 20:23:43 +03:00
rc = sprintf ( buf , " %d \n " , cap . owned ) ;
2008-07-26 06:44:59 +04:00
return rc ;
2006-04-22 13:37:50 +04:00
}
EXPORT_SYMBOL_GPL ( tpm_show_owned ) ;
ssize_t tpm_show_temp_deactivated ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2009-02-02 20:23:43 +03:00
cap_t cap ;
2006-04-22 13:37:50 +04:00
ssize_t rc ;
2009-02-02 20:23:43 +03:00
rc = tpm_getcap ( dev , TPM_CAP_FLAG_VOL , & cap ,
" attempting to determine the temporary state " ) ;
if ( rc )
2006-04-22 13:37:50 +04:00
return 0 ;
2008-07-26 06:44:59 +04:00
2009-02-02 20:23:43 +03:00
rc = sprintf ( buf , " %d \n " , cap . stclear_flags . deactivated ) ;
2008-07-26 06:44:59 +04:00
return rc ;
2006-04-22 13:37:50 +04:00
}
EXPORT_SYMBOL_GPL ( tpm_show_temp_deactivated ) ;
2009-02-02 20:23:44 +03:00
/*
* tpm_chip_find_get - return tpm_chip for given chip number
*/
static struct tpm_chip * tpm_chip_find_get ( int chip_num )
{
2009-02-05 18:06:30 +03:00
struct tpm_chip * pos , * chip = NULL ;
2009-02-02 20:23:44 +03:00
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( pos , & tpm_chip_list , list ) {
if ( chip_num ! = TPM_ANY_NUM & & chip_num ! = pos - > dev_num )
continue ;
2009-02-05 18:06:30 +03:00
if ( try_module_get ( pos - > dev - > driver - > owner ) ) {
chip = pos ;
2009-02-02 20:23:44 +03:00
break ;
2009-02-05 18:06:30 +03:00
}
2009-02-02 20:23:44 +03:00
}
rcu_read_unlock ( ) ;
2009-02-05 18:06:30 +03:00
return chip ;
2009-02-02 20:23:44 +03:00
}
# define TPM_ORDINAL_PCRREAD cpu_to_be32(21)
# define READ_PCR_RESULT_SIZE 30
static struct tpm_input_header pcrread_header = {
. tag = TPM_TAG_RQU_COMMAND ,
. length = cpu_to_be32 ( 14 ) ,
. ordinal = TPM_ORDINAL_PCRREAD
2005-04-17 02:20:36 +04:00
} ;
2009-02-02 20:23:44 +03:00
int __tpm_pcr_read ( struct tpm_chip * chip , int pcr_idx , u8 * res_buf )
{
int rc ;
struct tpm_cmd_t cmd ;
cmd . header . in = pcrread_header ;
cmd . params . pcrread_in . pcr_idx = cpu_to_be32 ( pcr_idx ) ;
2009-09-30 19:26:55 +04:00
rc = transmit_cmd ( chip , & cmd , READ_PCR_RESULT_SIZE ,
2009-02-02 20:23:44 +03:00
" attempting to read a pcr value " ) ;
if ( rc = = 0 )
memcpy ( res_buf , cmd . params . pcrread_out . pcr_result ,
TPM_DIGEST_SIZE ) ;
return rc ;
}
/**
* tpm_pcr_read - read a pcr value
* @ chip_num : tpm idx # or ANY
* @ pcr_idx : pcr idx to retrieve
* @ res_buf : TPM_PCR value
* size of res_buf is 20 bytes ( or NULL if you don ' t care )
*
* The TPM driver should be built - in , but for whatever reason it
* isn ' t , protect against the chip disappearing , by incrementing
* the module usage count .
*/
int tpm_pcr_read ( u32 chip_num , int pcr_idx , u8 * res_buf )
{
struct tpm_chip * chip ;
int rc ;
chip = tpm_chip_find_get ( chip_num ) ;
if ( chip = = NULL )
return - ENODEV ;
rc = __tpm_pcr_read ( chip , pcr_idx , res_buf ) ;
module_put ( chip - > dev - > driver - > owner ) ;
return rc ;
}
EXPORT_SYMBOL_GPL ( tpm_pcr_read ) ;
/**
* tpm_pcr_extend - extend pcr value with hash
* @ chip_num : tpm idx # or AN &
* @ pcr_idx : pcr idx to extend
* @ hash : hash value used to extend pcr value
*
* The TPM driver should be built - in , but for whatever reason it
* isn ' t , protect against the chip disappearing , by incrementing
* the module usage count .
*/
# define TPM_ORD_PCR_EXTEND cpu_to_be32(20)
2009-09-18 23:54:24 +04:00
# define EXTEND_PCR_RESULT_SIZE 34
2009-02-02 20:23:44 +03:00
static struct tpm_input_header pcrextend_header = {
. tag = TPM_TAG_RQU_COMMAND ,
. length = cpu_to_be32 ( 34 ) ,
. ordinal = TPM_ORD_PCR_EXTEND
} ;
int tpm_pcr_extend ( u32 chip_num , int pcr_idx , const u8 * hash )
{
struct tpm_cmd_t cmd ;
int rc ;
struct tpm_chip * chip ;
chip = tpm_chip_find_get ( chip_num ) ;
if ( chip = = NULL )
return - ENODEV ;
cmd . header . in = pcrextend_header ;
cmd . params . pcrextend_in . pcr_idx = cpu_to_be32 ( pcr_idx ) ;
memcpy ( cmd . params . pcrextend_in . hash , hash , TPM_DIGEST_SIZE ) ;
2009-09-18 23:54:24 +04:00
rc = transmit_cmd ( chip , & cmd , EXTEND_PCR_RESULT_SIZE ,
2009-02-02 20:23:44 +03:00
" attempting extend a PCR value " ) ;
module_put ( chip - > dev - > driver - > owner ) ;
return rc ;
}
EXPORT_SYMBOL_GPL ( tpm_pcr_extend ) ;
2005-06-24 09:02:00 +04:00
ssize_t tpm_show_pcrs ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2009-02-02 20:23:43 +03:00
cap_t cap ;
2009-02-02 20:23:44 +03:00
u8 digest [ TPM_DIGEST_SIZE ] ;
2006-04-22 13:37:05 +04:00
ssize_t rc ;
2005-06-24 09:01:59 +04:00
int i , j , num_pcrs ;
2005-04-17 02:20:36 +04:00
char * str = buf ;
2005-10-31 02:03:24 +03:00
struct tpm_chip * chip = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2009-02-02 20:23:43 +03:00
rc = tpm_getcap ( dev , TPM_CAP_PROP_PCR , & cap ,
2006-04-22 13:37:05 +04:00
" attempting to determine the number of PCRS " ) ;
2009-02-02 20:23:43 +03:00
if ( rc )
2005-06-24 09:02:08 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2009-02-02 20:23:43 +03:00
num_pcrs = be32_to_cpu ( cap . num_pcrs ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < num_pcrs ; i + + ) {
2009-02-02 20:23:44 +03:00
rc = __tpm_pcr_read ( chip , i , digest ) ;
2006-04-22 13:37:05 +04:00
if ( rc )
2009-02-02 20:23:43 +03:00
break ;
2005-04-17 02:20:36 +04:00
str + = sprintf ( str , " PCR-%02d: " , i ) ;
for ( j = 0 ; j < TPM_DIGEST_SIZE ; j + + )
2009-02-02 20:23:44 +03:00
str + = sprintf ( str , " %02X " , digest [ j ] ) ;
2005-04-17 02:20:36 +04:00
str + = sprintf ( str , " \n " ) ;
}
return str - buf ;
}
2005-06-24 09:02:00 +04:00
EXPORT_SYMBOL_GPL ( tpm_show_pcrs ) ;
2005-04-17 02:20:36 +04:00
# define READ_PUBEK_RESULT_SIZE 314
2009-02-02 20:23:43 +03:00
# define TPM_ORD_READPUBEK cpu_to_be32(124)
struct tpm_input_header tpm_readpubek_header = {
. tag = TPM_TAG_RQU_COMMAND ,
. length = cpu_to_be32 ( 30 ) ,
. ordinal = TPM_ORD_READPUBEK
2005-04-17 02:20:36 +04:00
} ;
2005-06-24 09:02:00 +04:00
ssize_t tpm_show_pubek ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2005-06-24 09:01:54 +04:00
u8 * data ;
2009-02-02 20:23:43 +03:00
struct tpm_cmd_t tpm_cmd ;
2006-04-22 13:37:05 +04:00
ssize_t err ;
2005-06-24 09:01:59 +04:00
int i , rc ;
2005-04-17 02:20:36 +04:00
char * str = buf ;
2005-10-31 02:03:24 +03:00
struct tpm_chip * chip = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2009-02-02 20:23:43 +03:00
tpm_cmd . header . in = tpm_readpubek_header ;
err = transmit_cmd ( chip , & tpm_cmd , READ_PUBEK_RESULT_SIZE ,
2006-04-22 13:37:05 +04:00
" attempting to read the PUBEK " ) ;
if ( err )
2005-06-24 09:02:10 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
/*
ignore header 10 bytes
algorithm 32 bits ( 1 = = RSA )
encscheme 16 bits
sigscheme 16 bits
parameters ( RSA 12 - > bytes : keybit , # primes , expbit )
keylenbytes 32 bits
256 byte modulus
ignore checksum 20 bytes
*/
2009-02-02 20:23:43 +03:00
data = tpm_cmd . params . readpubek_out_buffer ;
2005-04-17 02:20:36 +04:00
str + =
sprintf ( str ,
" Algorithm: %02X %02X %02X %02X \n Encscheme: %02X %02X \n "
" Sigscheme: %02X %02X \n Parameters: %02X %02X %02X %02X "
" %02X %02X %02X %02X %02X %02X %02X %02X \n "
" Modulus length: %d \n Modulus: \n " ,
data [ 10 ] , data [ 11 ] , data [ 12 ] , data [ 13 ] , data [ 14 ] ,
data [ 15 ] , data [ 16 ] , data [ 17 ] , data [ 22 ] , data [ 23 ] ,
data [ 24 ] , data [ 25 ] , data [ 26 ] , data [ 27 ] , data [ 28 ] ,
data [ 29 ] , data [ 30 ] , data [ 31 ] , data [ 32 ] , data [ 33 ] ,
2005-06-26 01:55:40 +04:00
be32_to_cpu ( * ( ( __be32 * ) ( data + 34 ) ) ) ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 256 ; i + + ) {
2005-06-26 01:55:40 +04:00
str + = sprintf ( str , " %02X " , data [ i + 38 ] ) ;
2005-04-17 02:20:36 +04:00
if ( ( i + 1 ) % 16 = = 0 )
str + = sprintf ( str , " \n " ) ;
}
2005-06-24 09:02:10 +04:00
out :
2006-04-22 13:37:05 +04:00
rc = str - buf ;
2005-06-24 09:01:54 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
}
2005-06-24 09:02:00 +04:00
EXPORT_SYMBOL_GPL ( tpm_show_pubek ) ;
2005-04-17 02:20:36 +04:00
2005-06-24 09:02:00 +04:00
ssize_t tpm_show_caps ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2009-02-02 20:23:43 +03:00
cap_t cap ;
2006-04-22 13:37:05 +04:00
ssize_t rc ;
2005-04-17 02:20:36 +04:00
char * str = buf ;
2009-02-02 20:23:43 +03:00
rc = tpm_getcap ( dev , TPM_CAP_PROP_MANUFACTURER , & cap ,
2006-04-22 13:37:05 +04:00
" attempting to determine the manufacturer " ) ;
2009-02-02 20:23:43 +03:00
if ( rc )
2006-04-22 13:37:05 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
str + = sprintf ( str , " Manufacturer: 0x%x \n " ,
2009-02-02 20:23:43 +03:00
be32_to_cpu ( cap . manufacturer_id ) ) ;
2005-04-17 02:20:36 +04:00
2009-02-02 20:23:43 +03:00
rc = tpm_getcap ( dev , CAP_VERSION_1_1 , & cap ,
" attempting to determine the 1.1 version " ) ;
2006-04-22 13:37:05 +04:00
if ( rc )
2009-02-02 20:23:43 +03:00
return 0 ;
2006-04-22 13:37:05 +04:00
str + = sprintf ( str ,
" TCG version: %d.%d \n Firmware version: %d.%d \n " ,
2009-02-02 20:23:43 +03:00
cap . tpm_version . Major , cap . tpm_version . Minor ,
cap . tpm_version . revMajor , cap . tpm_version . revMinor ) ;
2005-04-17 02:20:36 +04:00
return str - buf ;
}
2005-06-24 09:02:00 +04:00
EXPORT_SYMBOL_GPL ( tpm_show_caps ) ;
2006-04-22 13:37:50 +04:00
ssize_t tpm_show_caps_1_2 ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2009-02-02 20:23:43 +03:00
cap_t cap ;
ssize_t rc ;
2006-04-22 13:37:50 +04:00
char * str = buf ;
2009-02-02 20:23:43 +03:00
rc = tpm_getcap ( dev , TPM_CAP_PROP_MANUFACTURER , & cap ,
" attempting to determine the manufacturer " ) ;
if ( rc )
2006-04-22 13:37:50 +04:00
return 0 ;
str + = sprintf ( str , " Manufacturer: 0x%x \n " ,
2009-02-02 20:23:43 +03:00
be32_to_cpu ( cap . manufacturer_id ) ) ;
rc = tpm_getcap ( dev , CAP_VERSION_1_2 , & cap ,
" attempting to determine the 1.2 version " ) ;
if ( rc )
return 0 ;
2006-04-22 13:37:50 +04:00
str + = sprintf ( str ,
" TCG version: %d.%d \n Firmware version: %d.%d \n " ,
2009-02-02 20:23:43 +03:00
cap . tpm_version_1_2 . Major , cap . tpm_version_1_2 . Minor ,
cap . tpm_version_1_2 . revMajor ,
cap . tpm_version_1_2 . revMinor ) ;
2006-04-22 13:37:50 +04:00
return str - buf ;
}
EXPORT_SYMBOL_GPL ( tpm_show_caps_1_2 ) ;
2005-06-24 09:02:00 +04:00
ssize_t tpm_store_cancel ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct tpm_chip * chip = dev_get_drvdata ( dev ) ;
if ( chip = = NULL )
return 0 ;
2006-04-22 13:37:15 +04:00
chip - > vendor . cancel ( chip ) ;
2005-06-24 09:02:00 +04:00
return count ;
}
EXPORT_SYMBOL_GPL ( tpm_store_cancel ) ;
2005-04-17 02:20:36 +04:00
/*
* Device file system interface to the TPM
2008-10-11 02:03:39 +04:00
*
* It ' s assured that the chip will be opened just once ,
* by the check of is_open variable , which is protected
* by driver_lock .
2005-04-17 02:20:36 +04:00
*/
int tpm_open ( struct inode * inode , struct file * file )
{
2008-10-11 02:04:23 +04:00
int minor = iminor ( inode ) ;
2005-04-17 02:20:36 +04:00
struct tpm_chip * chip = NULL , * pos ;
2008-10-11 02:04:23 +04:00
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( pos , & tpm_chip_list , list ) {
2006-04-22 13:37:15 +04:00
if ( pos - > vendor . miscdev . minor = = minor ) {
2005-04-17 02:20:36 +04:00
chip = pos ;
2008-10-11 02:04:23 +04:00
get_device ( chip - > dev ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
2008-10-11 02:04:23 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
2008-10-11 02:04:23 +04:00
if ( ! chip )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2008-10-11 02:04:02 +04:00
if ( test_and_set_bit ( 0 , & chip - > is_open ) ) {
2005-10-31 02:03:28 +03:00
dev_dbg ( chip - > dev , " Another process owns this TPM \n " ) ;
2008-10-11 02:04:23 +04:00
put_device ( chip - > dev ) ;
return - EBUSY ;
2005-04-17 02:20:36 +04:00
}
chip - > data_buffer = kmalloc ( TPM_BUFSIZE * sizeof ( u8 ) , GFP_KERNEL ) ;
if ( chip - > data_buffer = = NULL ) {
2008-10-11 02:04:02 +04:00
clear_bit ( 0 , & chip - > is_open ) ;
2005-10-31 02:03:24 +03:00
put_device ( chip - > dev ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
atomic_set ( & chip - > data_pending , 0 ) ;
file - > private_data = chip ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( tpm_open ) ;
2008-10-11 02:04:23 +04:00
/*
* Called on file close
*/
2005-04-17 02:20:36 +04:00
int tpm_release ( struct inode * inode , struct file * file )
{
struct tpm_chip * chip = file - > private_data ;
2008-10-11 02:05:20 +04:00
del_singleshot_timer_sync ( & chip - > user_read_timer ) ;
2007-05-08 11:31:09 +04:00
flush_scheduled_work ( ) ;
2005-06-24 09:02:03 +04:00
file - > private_data = NULL ;
2005-04-17 02:20:36 +04:00
atomic_set ( & chip - > data_pending , 0 ) ;
2008-10-11 02:04:23 +04:00
kfree ( chip - > data_buffer ) ;
2008-10-11 02:04:02 +04:00
clear_bit ( 0 , & chip - > is_open ) ;
2005-10-31 02:03:24 +03:00
put_device ( chip - > dev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( tpm_release ) ;
2005-10-31 02:03:28 +03:00
ssize_t tpm_write ( struct file * file , const char __user * buf ,
2006-04-22 13:36:56 +04:00
size_t size , loff_t * off )
2005-04-17 02:20:36 +04:00
{
struct tpm_chip * chip = file - > private_data ;
2008-07-26 06:45:00 +04:00
size_t in_size = size , out_size ;
2005-04-17 02:20:36 +04:00
/* cannot perform a write until the read has cleared
either via tpm_read or a user_read_timer timeout */
2005-06-24 09:01:47 +04:00
while ( atomic_read ( & chip - > data_pending ) ! = 0 )
msleep ( TPM_TIMEOUT ) ;
2005-04-17 02:20:36 +04:00
2007-05-08 11:32:02 +04:00
mutex_lock ( & chip - > buffer_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( in_size > TPM_BUFSIZE )
in_size = TPM_BUFSIZE ;
if ( copy_from_user
( chip - > data_buffer , ( void __user * ) buf , in_size ) ) {
2007-05-08 11:32:02 +04:00
mutex_unlock ( & chip - > buffer_mutex ) ;
2005-04-17 02:20:36 +04:00
return - EFAULT ;
}
/* atomic tpm command send and result receive */
out_size = tpm_transmit ( chip , chip - > data_buffer , TPM_BUFSIZE ) ;
atomic_set ( & chip - > data_pending , out_size ) ;
2007-05-08 11:32:02 +04:00
mutex_unlock ( & chip - > buffer_mutex ) ;
2005-04-17 02:20:36 +04:00
/* Set a timeout by which the reader must come claim the result */
2005-06-24 09:01:56 +04:00
mod_timer ( & chip - > user_read_timer , jiffies + ( 60 * HZ ) ) ;
2005-04-17 02:20:36 +04:00
return in_size ;
}
EXPORT_SYMBOL_GPL ( tpm_write ) ;
2006-04-22 13:36:56 +04:00
ssize_t tpm_read ( struct file * file , char __user * buf ,
size_t size , loff_t * off )
2005-04-17 02:20:36 +04:00
{
struct tpm_chip * chip = file - > private_data ;
2008-07-26 06:45:00 +04:00
ssize_t ret_size ;
2005-04-17 02:20:36 +04:00
2005-06-24 09:01:53 +04:00
del_singleshot_timer_sync ( & chip - > user_read_timer ) ;
2005-11-18 12:10:57 +03:00
flush_scheduled_work ( ) ;
2005-06-24 09:01:53 +04:00
ret_size = atomic_read ( & chip - > data_pending ) ;
atomic_set ( & chip - > data_pending , 0 ) ;
if ( ret_size > 0 ) { /* relay data */
if ( size < ret_size )
ret_size = size ;
2005-04-17 02:20:36 +04:00
2007-05-08 11:32:02 +04:00
mutex_lock ( & chip - > buffer_mutex ) ;
2005-11-14 03:07:42 +03:00
if ( copy_to_user ( buf , chip - > data_buffer , ret_size ) )
2005-06-24 09:01:53 +04:00
ret_size = - EFAULT ;
2007-05-08 11:32:02 +04:00
mutex_unlock ( & chip - > buffer_mutex ) ;
2005-04-17 02:20:36 +04:00
}
return ret_size ;
}
EXPORT_SYMBOL_GPL ( tpm_read ) ;
2005-10-31 02:03:24 +03:00
void tpm_remove_hardware ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2005-10-31 02:03:24 +03:00
struct tpm_chip * chip = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( chip = = NULL ) {
2005-10-31 02:03:24 +03:00
dev_err ( dev , " No device data found \n " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
spin_lock ( & driver_lock ) ;
2008-10-11 02:04:23 +04:00
list_del_rcu ( & chip - > list ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & driver_lock ) ;
2008-10-11 02:04:23 +04:00
synchronize_rcu ( ) ;
2005-04-17 02:20:36 +04:00
2006-04-22 13:37:15 +04:00
misc_deregister ( & chip - > vendor . miscdev ) ;
sysfs_remove_group ( & dev - > kobj , chip - > vendor . attr_group ) ;
2006-01-08 12:03:15 +03:00
tpm_bios_log_teardown ( chip - > bios_dir ) ;
2005-04-17 02:20:36 +04:00
2008-02-06 12:37:02 +03:00
/* write it this way to be explicit (chip->dev == dev) */
put_device ( chip - > dev ) ;
2005-04-17 02:20:36 +04:00
}
2005-10-31 02:03:24 +03:00
EXPORT_SYMBOL_GPL ( tpm_remove_hardware ) ;
2005-04-17 02:20:36 +04:00
/*
* We are about to suspend . Save the TPM state
* so that it can be restored .
*/
2005-10-31 02:03:25 +03:00
int tpm_pm_suspend ( struct device * dev , pm_message_t pm_state )
2005-04-17 02:20:36 +04:00
{
2005-10-31 02:03:25 +03:00
struct tpm_chip * chip = dev_get_drvdata ( dev ) ;
2008-01-14 11:55:12 +03:00
u8 savestate [ ] = {
0 , 193 , /* TPM_TAG_RQU_COMMAND */
0 , 0 , 0 , 10 , /* blob length (in bytes) */
0 , 0 , 0 , 152 /* TPM_ORD_SaveState */
} ;
2005-04-17 02:20:36 +04:00
if ( chip = = NULL )
return - ENODEV ;
tpm_transmit ( chip , savestate , sizeof ( savestate ) ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( tpm_pm_suspend ) ;
/*
* Resume from a power safe . The BIOS already restored
* the TPM state .
*/
2005-10-31 02:03:25 +03:00
int tpm_pm_resume ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2005-10-31 02:03:25 +03:00
struct tpm_chip * chip = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( chip = = NULL )
return - ENODEV ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( tpm_pm_resume ) ;
2008-10-11 02:04:39 +04:00
/* In case vendor provided release function, call it too.*/
void tpm_dev_vendor_release ( struct tpm_chip * chip )
{
if ( chip - > vendor . release )
chip - > vendor . release ( chip - > dev ) ;
clear_bit ( chip - > dev_num , dev_mask ) ;
kfree ( chip - > vendor . miscdev . name ) ;
}
EXPORT_SYMBOL_GPL ( tpm_dev_vendor_release ) ;
2008-02-06 12:37:02 +03:00
/*
* Once all references to platform device are down to 0 ,
* release all allocated structures .
*/
2008-10-19 07:25:46 +04:00
void tpm_dev_release ( struct device * dev )
2008-02-06 12:37:02 +03:00
{
struct tpm_chip * chip = dev_get_drvdata ( dev ) ;
2008-10-11 02:04:39 +04:00
tpm_dev_vendor_release ( chip ) ;
2008-02-06 12:37:02 +03:00
2008-10-11 02:04:39 +04:00
chip - > release ( dev ) ;
2008-02-06 12:37:02 +03:00
kfree ( chip ) ;
}
2008-10-11 02:04:39 +04:00
EXPORT_SYMBOL_GPL ( tpm_dev_release ) ;
2008-02-06 12:37:02 +03:00
2005-04-17 02:20:36 +04:00
/*
* Called from tpm_ < specific > . c probe function only for devices
* the driver has determined it should claim . Prior to calling
* this function the specific probe function has called pci_enable_device
* upon errant exit from this function specific probe function should call
* pci_disable_device
*/
2008-10-11 02:04:23 +04:00
struct tpm_chip * tpm_register_hardware ( struct device * dev ,
const struct tpm_vendor_specific * entry )
2005-04-17 02:20:36 +04:00
{
2005-06-26 01:55:41 +04:00
# define DEVNAME_SIZE 7
char * devname ;
2005-04-17 02:20:36 +04:00
struct tpm_chip * chip ;
/* Driver specific per-device data */
2005-10-31 02:03:28 +03:00
chip = kzalloc ( sizeof ( * chip ) , GFP_KERNEL ) ;
2007-05-08 11:31:09 +04:00
devname = kmalloc ( DEVNAME_SIZE , GFP_KERNEL ) ;
2008-10-16 09:04:37 +04:00
if ( chip = = NULL | | devname = = NULL )
goto out_free ;
2005-04-17 02:20:36 +04:00
2007-05-08 11:32:02 +04:00
mutex_init ( & chip - > buffer_mutex ) ;
mutex_init ( & chip - > tpm_mutex ) ;
2005-04-17 02:20:36 +04:00
INIT_LIST_HEAD ( & chip - > list ) ;
2006-11-22 17:57:56 +03:00
INIT_WORK ( & chip - > work , timeout_work ) ;
2005-11-14 03:07:43 +03:00
2007-02-12 11:52:31 +03:00
setup_timer ( & chip - > user_read_timer , user_reader_timeout ,
( unsigned long ) chip ) ;
2005-06-24 09:01:56 +04:00
2006-04-22 13:37:15 +04:00
memcpy ( & chip - > vendor , entry , sizeof ( struct tpm_vendor_specific ) ) ;
2005-04-17 02:20:36 +04:00
2006-04-22 13:38:32 +04:00
chip - > dev_num = find_first_zero_bit ( dev_mask , TPM_NUM_DEVICES ) ;
2005-04-17 02:20:36 +04:00
2006-04-22 13:38:32 +04:00
if ( chip - > dev_num > = TPM_NUM_DEVICES ) {
2005-10-31 02:03:28 +03:00
dev_err ( dev , " No available tpm device numbers \n " ) ;
2008-10-16 09:04:37 +04:00
goto out_free ;
2005-04-17 02:20:36 +04:00
} else if ( chip - > dev_num = = 0 )
2006-04-22 13:37:15 +04:00
chip - > vendor . miscdev . minor = TPM_MINOR ;
2005-04-17 02:20:36 +04:00
else
2006-04-22 13:37:15 +04:00
chip - > vendor . miscdev . minor = MISC_DYNAMIC_MINOR ;
2005-04-17 02:20:36 +04:00
2006-04-22 13:38:32 +04:00
set_bit ( chip - > dev_num , dev_mask ) ;
2005-06-26 01:55:41 +04:00
scnprintf ( devname , DEVNAME_SIZE , " %s%d " , " tpm " , chip - > dev_num ) ;
2006-04-22 13:37:15 +04:00
chip - > vendor . miscdev . name = devname ;
2005-04-17 02:20:36 +04:00
2006-07-28 03:16:04 +04:00
chip - > vendor . miscdev . parent = dev ;
2005-10-31 02:03:24 +03:00
chip - > dev = get_device ( dev ) ;
2008-02-06 12:37:02 +03:00
chip - > release = dev - > release ;
dev - > release = tpm_dev_release ;
dev_set_drvdata ( dev , chip ) ;
2005-04-17 02:20:36 +04:00
2006-04-22 13:37:15 +04:00
if ( misc_register ( & chip - > vendor . miscdev ) ) {
2005-10-31 02:03:24 +03:00
dev_err ( chip - > dev ,
2005-04-17 02:20:36 +04:00
" unable to misc_register %s, minor %d \n " ,
2006-04-22 13:37:15 +04:00
chip - > vendor . miscdev . name ,
chip - > vendor . miscdev . minor ) ;
2008-02-06 12:37:02 +03:00
put_device ( chip - > dev ) ;
2006-04-22 13:37:26 +04:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
2006-10-11 12:21:51 +04:00
if ( sysfs_create_group ( & dev - > kobj , chip - > vendor . attr_group ) ) {
2006-12-07 07:37:08 +03:00
misc_deregister ( & chip - > vendor . miscdev ) ;
2008-02-06 12:37:02 +03:00
put_device ( chip - > dev ) ;
2008-10-11 02:04:23 +04:00
2006-10-11 12:21:51 +04:00
return NULL ;
}
2005-04-17 02:20:36 +04:00
2006-01-08 12:03:15 +03:00
chip - > bios_dir = tpm_bios_log_setup ( devname ) ;
2008-10-11 02:04:23 +04:00
/* Make chip available */
spin_lock ( & driver_lock ) ;
list_add_rcu ( & chip - > list , & tpm_chip_list ) ;
spin_unlock ( & driver_lock ) ;
2006-04-22 13:37:26 +04:00
return chip ;
2008-10-16 09:04:37 +04:00
out_free :
kfree ( chip ) ;
kfree ( devname ) ;
return NULL ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL_GPL ( tpm_register_hardware ) ;
MODULE_AUTHOR ( " Leendert van Doorn (leendert@watson.ibm.com) " ) ;
MODULE_DESCRIPTION ( " TPM Driver " ) ;
MODULE_VERSION ( " 2.0 " ) ;
MODULE_LICENSE ( " GPL " ) ;