2014-12-12 22:46:34 +03:00
/*
* Copyright ( C ) 2004 IBM Corporation
* Copyright ( C ) 2014 Intel Corporation
*
* Authors :
* Jarkko Sakkinen < jarkko . sakkinen @ linux . intel . com >
* 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 >
*
* Maintained by : < tpmdd - devel @ lists . sourceforge . net >
*
* TPM chip management routines .
*
* 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 .
*
*/
# include <linux/poll.h>
# include <linux/slab.h>
# include <linux/mutex.h>
# include <linux/spinlock.h>
# include <linux/freezer.h>
2014-12-12 22:46:37 +03:00
# include <linux/major.h>
2014-12-12 22:46:34 +03:00
# include "tpm.h"
# include "tpm_eventlog.h"
2016-02-29 16:53:02 +03:00
DEFINE_IDR ( dev_nums_idr ) ;
static DEFINE_MUTEX ( idr_lock ) ;
2014-12-12 22:46:34 +03:00
2014-12-12 22:46:37 +03:00
struct class * tpm_class ;
dev_t tpm_devt ;
2016-02-13 06:29:53 +03:00
/**
* tpm_try_get_ops ( ) - Get a ref to the tpm_chip
* @ chip : Chip to ref
*
* The caller must already have some kind of locking to ensure that chip is
* valid . This function will lock the chip so that the ops member can be
* accessed safely . The locking prevents tpm_chip_unregister from
* completing , so it should not be held for long periods .
*
* Returns - ERRNO if the chip could not be got .
2014-12-12 22:46:34 +03:00
*/
2016-02-13 06:29:53 +03:00
int tpm_try_get_ops ( struct tpm_chip * chip )
{
int rc = - EIO ;
get_device ( & chip - > dev ) ;
down_read ( & chip - > ops_sem ) ;
if ( ! chip - > ops )
goto out_lock ;
return 0 ;
out_lock :
up_read ( & chip - > ops_sem ) ;
put_device ( & chip - > dev ) ;
return rc ;
}
EXPORT_SYMBOL_GPL ( tpm_try_get_ops ) ;
/**
* tpm_put_ops ( ) - Release a ref to the tpm_chip
* @ chip : Chip to put
*
* This is the opposite pair to tpm_try_get_ops ( ) . After this returns chip may
* be kfree ' d .
*/
void tpm_put_ops ( struct tpm_chip * chip )
{
up_read ( & chip - > ops_sem ) ;
put_device ( & chip - > dev ) ;
}
EXPORT_SYMBOL_GPL ( tpm_put_ops ) ;
/**
* tpm_chip_find_get ( ) - return tpm_chip for a given chip number
* @ chip_num : id to find
*
* The return ' d chip has been tpm_try_get_ops ' d and must be released via
* tpm_put_ops
*/
2014-12-12 22:46:34 +03:00
struct tpm_chip * tpm_chip_find_get ( int chip_num )
{
2016-02-29 16:53:02 +03:00
struct tpm_chip * chip , * res = NULL ;
int chip_prev ;
mutex_lock ( & idr_lock ) ;
if ( chip_num = = TPM_ANY_NUM ) {
chip_num = 0 ;
do {
chip_prev = chip_num ;
chip = idr_get_next ( & dev_nums_idr , & chip_num ) ;
if ( chip & & ! tpm_try_get_ops ( chip ) ) {
res = chip ;
break ;
}
} while ( chip_prev ! = chip_num ) ;
} else {
chip = idr_find_slowpath ( & dev_nums_idr , chip_num ) ;
if ( chip & & ! tpm_try_get_ops ( chip ) )
res = chip ;
}
2014-12-12 22:46:34 +03:00
2016-02-29 16:53:02 +03:00
mutex_unlock ( & idr_lock ) ;
2014-12-12 22:46:34 +03:00
2016-02-29 16:53:02 +03:00
return res ;
2014-12-12 22:46:34 +03:00
}
/**
2014-12-12 22:46:37 +03:00
* tpm_dev_release ( ) - free chip memory and the device number
* @ dev : the character device for the TPM chip
2014-12-12 22:46:34 +03:00
*
2014-12-12 22:46:37 +03:00
* This is used as the release function for the character device .
2014-12-12 22:46:34 +03:00
*/
2014-12-12 22:46:37 +03:00
static void tpm_dev_release ( struct device * dev )
2014-12-12 22:46:34 +03:00
{
2014-12-12 22:46:37 +03:00
struct tpm_chip * chip = container_of ( dev , struct tpm_chip , dev ) ;
2014-12-12 22:46:34 +03:00
2016-02-29 16:53:02 +03:00
mutex_lock ( & idr_lock ) ;
idr_remove ( & dev_nums_idr , chip - > dev_num ) ;
mutex_unlock ( & idr_lock ) ;
2014-12-12 22:46:34 +03:00
kfree ( chip ) ;
}
/**
2016-02-11 22:45:48 +03:00
* tpm_chip_alloc ( ) - allocate a new struct tpm_chip instance
* @ pdev : device to which the chip is associated
* At this point pdev mst be initialized , but does not have to
* be registered
2014-12-12 22:46:34 +03:00
* @ ops : struct tpm_class_ops instance
*
* Allocates a new struct tpm_chip instance and assigns a free
2016-02-11 22:45:48 +03:00
* device number for it . Must be paired with put_device ( & chip - > dev ) .
2014-12-12 22:46:34 +03:00
*/
2016-02-11 22:45:48 +03:00
struct tpm_chip * tpm_chip_alloc ( struct device * dev ,
const struct tpm_class_ops * ops )
2014-12-12 22:46:34 +03:00
{
struct tpm_chip * chip ;
2016-02-13 12:58:16 +03:00
int rc ;
2014-12-12 22:46:34 +03:00
chip = kzalloc ( sizeof ( * chip ) , GFP_KERNEL ) ;
if ( chip = = NULL )
return ERR_PTR ( - ENOMEM ) ;
mutex_init ( & chip - > tpm_mutex ) ;
2016-02-13 06:29:53 +03:00
init_rwsem ( & chip - > ops_sem ) ;
2014-12-12 22:46:34 +03:00
chip - > ops = ops ;
2016-02-29 16:53:02 +03:00
mutex_lock ( & idr_lock ) ;
rc = idr_alloc ( & dev_nums_idr , NULL , 0 , TPM_NUM_DEVICES , GFP_KERNEL ) ;
mutex_unlock ( & idr_lock ) ;
if ( rc < 0 ) {
2014-12-12 22:46:34 +03:00
dev_err ( dev , " No available tpm device numbers \n " ) ;
kfree ( chip ) ;
2016-02-29 16:53:02 +03:00
return ERR_PTR ( rc ) ;
2014-12-12 22:46:34 +03:00
}
2016-02-29 16:53:02 +03:00
chip - > dev_num = rc ;
2014-12-12 22:46:34 +03:00
2016-02-29 20:29:48 +03:00
device_initialize ( & chip - > dev ) ;
2014-12-12 22:46:34 +03:00
2014-12-12 22:46:37 +03:00
chip - > dev . class = tpm_class ;
chip - > dev . release = tpm_dev_release ;
2016-02-29 20:29:47 +03:00
chip - > dev . parent = dev ;
2015-04-14 17:56:48 +03:00
chip - > dev . groups = chip - > groups ;
2014-12-12 22:46:37 +03:00
if ( chip - > dev_num = = 0 )
chip - > dev . devt = MKDEV ( MISC_MAJOR , TPM_MINOR ) ;
else
chip - > dev . devt = MKDEV ( MAJOR ( tpm_devt ) , chip - > dev_num ) ;
2016-02-29 20:29:48 +03:00
rc = dev_set_name ( & chip - > dev , " tpm%d " , chip - > dev_num ) ;
if ( rc )
goto out ;
2014-12-12 22:46:37 +03:00
2016-04-18 20:26:14 +03:00
if ( ! dev )
chip - > flags | = TPM_CHIP_FLAG_VIRTUAL ;
2014-12-12 22:46:37 +03:00
cdev_init ( & chip - > cdev , & tpm_fops ) ;
2016-02-29 16:53:01 +03:00
chip - > cdev . owner = THIS_MODULE ;
2015-06-30 22:15:31 +03:00
chip - > cdev . kobj . parent = & chip - > dev . kobj ;
2014-12-12 22:46:37 +03:00
2016-02-11 22:45:48 +03:00
return chip ;
out :
put_device ( & chip - > dev ) ;
return ERR_PTR ( rc ) ;
}
EXPORT_SYMBOL_GPL ( tpm_chip_alloc ) ;
/**
* tpmm_chip_alloc ( ) - allocate a new struct tpm_chip instance
* @ pdev : parent device to which the chip is associated
* @ ops : struct tpm_class_ops instance
*
* Same as tpm_chip_alloc except devm is used to do the put_device
*/
struct tpm_chip * tpmm_chip_alloc ( struct device * pdev ,
const struct tpm_class_ops * ops )
{
struct tpm_chip * chip ;
int rc ;
chip = tpm_chip_alloc ( pdev , ops ) ;
if ( IS_ERR ( chip ) )
return chip ;
2016-06-12 17:05:29 +03:00
rc = devm_add_action_or_reset ( pdev ,
( void ( * ) ( void * ) ) put_device ,
& chip - > dev ) ;
if ( rc )
2016-02-13 12:58:16 +03:00
return ERR_PTR ( rc ) ;
2016-02-08 23:31:08 +03:00
2016-02-11 22:45:48 +03:00
dev_set_drvdata ( pdev , chip ) ;
2016-02-29 20:29:48 +03:00
2016-02-11 22:45:48 +03:00
return chip ;
2014-12-12 22:46:34 +03:00
}
EXPORT_SYMBOL_GPL ( tpmm_chip_alloc ) ;
2016-01-29 20:47:22 +03:00
static int tpm_add_char_device ( struct tpm_chip * chip )
2014-12-12 22:46:37 +03:00
{
int rc ;
2015-03-02 00:55:47 +03:00
rc = cdev_add ( & chip - > cdev , chip - > dev . devt , 1 ) ;
2014-12-12 22:46:37 +03:00
if ( rc ) {
dev_err ( & chip - > dev ,
2015-03-02 00:55:47 +03:00
" unable to cdev_add() %s, major %d, minor %d, err=%d \n " ,
2016-02-29 20:29:48 +03:00
dev_name ( & chip - > dev ) , MAJOR ( chip - > dev . devt ) ,
2014-12-12 22:46:37 +03:00
MINOR ( chip - > dev . devt ) , rc ) ;
return rc ;
}
2015-03-02 00:55:47 +03:00
rc = device_add ( & chip - > dev ) ;
2014-12-12 22:46:37 +03:00
if ( rc ) {
dev_err ( & chip - > dev ,
2015-03-02 00:55:47 +03:00
" unable to device_register() %s, major %d, minor %d, err=%d \n " ,
2016-02-29 20:29:48 +03:00
dev_name ( & chip - > dev ) , MAJOR ( chip - > dev . devt ) ,
2014-12-12 22:46:37 +03:00
MINOR ( chip - > dev . devt ) , rc ) ;
2016-01-29 20:47:22 +03:00
cdev_del ( & chip - > cdev ) ;
2014-12-12 22:46:37 +03:00
return rc ;
}
2016-02-29 16:53:02 +03:00
/* Make the chip available. */
mutex_lock ( & idr_lock ) ;
idr_replace ( & dev_nums_idr , chip , chip - > dev_num ) ;
mutex_unlock ( & idr_lock ) ;
2014-12-12 22:46:37 +03:00
return rc ;
}
2016-01-29 20:47:22 +03:00
static void tpm_del_char_device ( struct tpm_chip * chip )
2014-12-12 22:46:37 +03:00
{
cdev_del ( & chip - > cdev ) ;
2016-02-29 16:53:02 +03:00
device_del ( & chip - > dev ) ;
/* Make the chip unavailable. */
mutex_lock ( & idr_lock ) ;
idr_replace ( & dev_nums_idr , NULL , chip - > dev_num ) ;
mutex_unlock ( & idr_lock ) ;
2016-02-13 06:29:53 +03:00
/* Make the driver uncallable. */
down_write ( & chip - > ops_sem ) ;
2016-04-25 12:20:07 +03:00
if ( chip - > flags & TPM_CHIP_FLAG_TPM2 )
tpm2_shutdown ( chip , TPM2_SU_CLEAR ) ;
2016-02-13 06:29:53 +03:00
chip - > ops = NULL ;
up_write ( & chip - > ops_sem ) ;
2014-12-12 22:46:37 +03:00
}
2015-03-18 09:17:14 +03:00
static int tpm1_chip_register ( struct tpm_chip * chip )
{
if ( chip - > flags & TPM_CHIP_FLAG_TPM2 )
return 0 ;
2016-04-18 20:26:13 +03:00
tpm_sysfs_add_device ( chip ) ;
2015-03-18 09:17:14 +03:00
2016-02-29 20:29:48 +03:00
chip - > bios_dir = tpm_bios_log_setup ( dev_name ( & chip - > dev ) ) ;
2015-03-18 09:17:14 +03:00
return 0 ;
}
static void tpm1_chip_unregister ( struct tpm_chip * chip )
{
if ( chip - > flags & TPM_CHIP_FLAG_TPM2 )
return ;
if ( chip - > bios_dir )
tpm_bios_log_teardown ( chip - > bios_dir ) ;
2016-04-18 20:26:13 +03:00
}
static void tpm_del_legacy_sysfs ( struct tpm_chip * chip )
{
struct attribute * * i ;
2016-04-18 20:26:14 +03:00
if ( chip - > flags & ( TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL ) )
2016-04-18 20:26:13 +03:00
return ;
2015-03-18 09:17:14 +03:00
2016-04-18 20:26:13 +03:00
sysfs_remove_link ( & chip - > dev . parent - > kobj , " ppi " ) ;
for ( i = chip - > groups [ 0 ] - > attrs ; * i ! = NULL ; + + i )
sysfs_remove_link ( & chip - > dev . parent - > kobj , ( * i ) - > name ) ;
2015-03-18 09:17:14 +03:00
}
2016-04-18 20:26:13 +03:00
/* For compatibility with legacy sysfs paths we provide symlinks from the
* parent dev directory to selected names within the tpm chip directory . Old
* kernel versions created these files directly under the parent .
*/
static int tpm_add_legacy_sysfs ( struct tpm_chip * chip )
{
struct attribute * * i ;
int rc ;
2016-04-18 20:26:14 +03:00
if ( chip - > flags & ( TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL ) )
2016-04-18 20:26:13 +03:00
return 0 ;
rc = __compat_only_sysfs_link_entry_to_kobj (
& chip - > dev . parent - > kobj , & chip - > dev . kobj , " ppi " ) ;
if ( rc & & rc ! = - ENOENT )
return rc ;
/* All the names from tpm-sysfs */
for ( i = chip - > groups [ 0 ] - > attrs ; * i ! = NULL ; + + i ) {
rc = __compat_only_sysfs_link_entry_to_kobj (
& chip - > dev . parent - > kobj , & chip - > dev . kobj , ( * i ) - > name ) ;
if ( rc ) {
tpm_del_legacy_sysfs ( chip ) ;
return rc ;
}
}
return 0 ;
}
2014-12-12 22:46:34 +03:00
/*
2014-12-12 22:46:37 +03:00
* tpm_chip_register ( ) - create a character device for the TPM chip
2014-12-12 22:46:34 +03:00
* @ chip : TPM chip to use .
*
2015-03-02 00:55:47 +03:00
* Creates a character device for the TPM chip and adds sysfs attributes for
* the device . As the last step this function adds the chip to the list of TPM
* chips available for in - kernel use .
2014-12-12 22:46:34 +03:00
*
2015-03-02 00:55:47 +03:00
* This function should be only called after the chip initialization is
* complete .
2014-12-12 22:46:34 +03:00
*/
int tpm_chip_register ( struct tpm_chip * chip )
{
int rc ;
2015-03-18 09:17:14 +03:00
rc = tpm1_chip_register ( chip ) ;
if ( rc )
return rc ;
2014-12-12 22:46:34 +03:00
2015-04-14 17:56:48 +03:00
tpm_add_ppi ( chip ) ;
2016-01-29 20:47:22 +03:00
rc = tpm_add_char_device ( chip ) ;
2016-04-18 20:26:13 +03:00
if ( rc ) {
tpm1_chip_unregister ( chip ) ;
return rc ;
}
2015-03-02 00:55:47 +03:00
2014-12-12 22:46:34 +03:00
chip - > flags | = TPM_CHIP_FLAG_REGISTERED ;
2016-04-18 20:26:13 +03:00
rc = tpm_add_legacy_sysfs ( chip ) ;
if ( rc ) {
tpm_chip_unregister ( chip ) ;
return rc ;
2015-11-07 14:33:25 +03:00
}
2014-12-12 22:46:34 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( tpm_chip_register ) ;
/*
* tpm_chip_unregister ( ) - release the TPM driver
* @ chip : TPM chip to use .
*
* Takes the chip first away from the list of available TPM chips and then
* cleans up all the resources reserved by tpm_chip_register ( ) .
*
2016-02-13 06:29:53 +03:00
* Once this function returns the driver call backs in ' op ' s will not be
* running and will no longer start .
*
2014-12-12 22:46:34 +03:00
* NOTE : This function should be only called before deinitializing chip
* resources .
*/
void tpm_chip_unregister ( struct tpm_chip * chip )
{
if ( ! ( chip - > flags & TPM_CHIP_FLAG_REGISTERED ) )
return ;
2016-04-18 20:26:13 +03:00
tpm_del_legacy_sysfs ( chip ) ;
2015-04-14 17:56:48 +03:00
2015-03-18 09:17:14 +03:00
tpm1_chip_unregister ( chip ) ;
2016-01-29 20:47:22 +03:00
tpm_del_char_device ( chip ) ;
2014-12-12 22:46:34 +03:00
}
EXPORT_SYMBOL_GPL ( tpm_chip_unregister ) ;