2014-12-12 11:46:34 -08: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 11:46:37 -08:00
# include <linux/major.h>
2014-12-12 11:46:34 -08:00
# include "tpm.h"
# include "tpm_eventlog.h"
static DECLARE_BITMAP ( dev_mask , TPM_NUM_DEVICES ) ;
static LIST_HEAD ( tpm_chip_list ) ;
static DEFINE_SPINLOCK ( driver_lock ) ;
2014-12-12 11:46:37 -08:00
struct class * tpm_class ;
dev_t tpm_devt ;
2014-12-12 11:46:34 -08:00
/*
* tpm_chip_find_get - return tpm_chip for a given chip number
* @ chip_num the device number for the chip
*/
struct tpm_chip * tpm_chip_find_get ( int chip_num )
{
struct tpm_chip * pos , * chip = NULL ;
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 ;
2014-12-12 11:46:36 -08:00
if ( try_module_get ( pos - > pdev - > driver - > owner ) ) {
2014-12-12 11:46:34 -08:00
chip = pos ;
break ;
}
}
rcu_read_unlock ( ) ;
return chip ;
}
/**
2014-12-12 11:46:37 -08:00
* tpm_dev_release ( ) - free chip memory and the device number
* @ dev : the character device for the TPM chip
2014-12-12 11:46:34 -08:00
*
2014-12-12 11:46:37 -08:00
* This is used as the release function for the character device .
2014-12-12 11:46:34 -08:00
*/
2014-12-12 11:46:37 -08:00
static void tpm_dev_release ( struct device * dev )
2014-12-12 11:46:34 -08:00
{
2014-12-12 11:46:37 -08:00
struct tpm_chip * chip = container_of ( dev , struct tpm_chip , dev ) ;
2014-12-12 11:46:34 -08:00
spin_lock ( & driver_lock ) ;
clear_bit ( chip - > dev_num , dev_mask ) ;
spin_unlock ( & driver_lock ) ;
kfree ( chip ) ;
}
/**
* tpmm_chip_alloc ( ) - allocate a new struct tpm_chip instance
* @ dev : device to which the chip is associated
* @ ops : struct tpm_class_ops instance
*
* Allocates a new struct tpm_chip instance and assigns a free
* device number for it . Caller does not have to worry about
* freeing the allocated resources . When the devices is removed
* devres calls tpmm_chip_remove ( ) to do the job .
*/
struct tpm_chip * tpmm_chip_alloc ( struct device * dev ,
const struct tpm_class_ops * ops )
{
struct tpm_chip * chip ;
2016-02-13 11:58:16 +02:00
int rc ;
2014-12-12 11:46:34 -08:00
chip = kzalloc ( sizeof ( * chip ) , GFP_KERNEL ) ;
if ( chip = = NULL )
return ERR_PTR ( - ENOMEM ) ;
mutex_init ( & chip - > tpm_mutex ) ;
INIT_LIST_HEAD ( & chip - > list ) ;
chip - > ops = ops ;
spin_lock ( & driver_lock ) ;
chip - > dev_num = find_first_zero_bit ( dev_mask , TPM_NUM_DEVICES ) ;
spin_unlock ( & driver_lock ) ;
if ( chip - > dev_num > = TPM_NUM_DEVICES ) {
dev_err ( dev , " No available tpm device numbers \n " ) ;
kfree ( chip ) ;
return ERR_PTR ( - ENOMEM ) ;
}
set_bit ( chip - > dev_num , dev_mask ) ;
scnprintf ( chip - > devname , sizeof ( chip - > devname ) , " tpm%d " , chip - > dev_num ) ;
2014-12-12 11:46:36 -08:00
chip - > pdev = dev ;
2014-12-12 11:46:37 -08:00
2014-12-12 11:46:34 -08:00
dev_set_drvdata ( dev , chip ) ;
2014-12-12 11:46:37 -08:00
chip - > dev . class = tpm_class ;
chip - > dev . release = tpm_dev_release ;
chip - > dev . parent = chip - > pdev ;
2015-04-14 17:56:48 +03:00
# ifdef CONFIG_ACPI
chip - > dev . groups = chip - > groups ;
# endif
2014-12-12 11:46:37 -08: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 ) ;
2015-01-20 12:03:35 +02:00
dev_set_name ( & chip - > dev , " %s " , chip - > devname ) ;
2014-12-12 11:46:37 -08:00
device_initialize ( & chip - > dev ) ;
cdev_init ( & chip - > cdev , & tpm_fops ) ;
2015-06-30 13:15:31 -06:00
chip - > cdev . owner = chip - > pdev - > driver - > owner ;
chip - > cdev . kobj . parent = & chip - > dev . kobj ;
2014-12-12 11:46:37 -08:00
2016-02-13 11:58:16 +02:00
rc = devm_add_action ( dev , ( void ( * ) ( void * ) ) put_device , & chip - > dev ) ;
if ( rc ) {
put_device ( & chip - > dev ) ;
return ERR_PTR ( rc ) ;
}
2016-02-08 22:31:08 +02:00
2014-12-12 11:46:34 -08:00
return chip ;
}
EXPORT_SYMBOL_GPL ( tpmm_chip_alloc ) ;
2016-01-29 09:47:22 -08:00
static int tpm_add_char_device ( struct tpm_chip * chip )
2014-12-12 11:46:37 -08:00
{
int rc ;
2015-03-01 23:55:47 +02:00
rc = cdev_add ( & chip - > cdev , chip - > dev . devt , 1 ) ;
2014-12-12 11:46:37 -08:00
if ( rc ) {
dev_err ( & chip - > dev ,
2015-03-01 23:55:47 +02:00
" unable to cdev_add() %s, major %d, minor %d, err=%d \n " ,
2014-12-12 11:46:37 -08:00
chip - > devname , MAJOR ( chip - > dev . devt ) ,
MINOR ( chip - > dev . devt ) , rc ) ;
return rc ;
}
2015-03-01 23:55:47 +02:00
rc = device_add ( & chip - > dev ) ;
2014-12-12 11:46:37 -08:00
if ( rc ) {
dev_err ( & chip - > dev ,
2015-03-01 23:55:47 +02:00
" unable to device_register() %s, major %d, minor %d, err=%d \n " ,
2014-12-12 11:46:37 -08:00
chip - > devname , MAJOR ( chip - > dev . devt ) ,
MINOR ( chip - > dev . devt ) , rc ) ;
2016-01-29 09:47:22 -08:00
cdev_del ( & chip - > cdev ) ;
2014-12-12 11:46:37 -08:00
return rc ;
}
return rc ;
}
2016-01-29 09:47:22 -08:00
static void tpm_del_char_device ( struct tpm_chip * chip )
2014-12-12 11:46:37 -08:00
{
cdev_del ( & chip - > cdev ) ;
2016-02-08 22:31:08 +02:00
device_del ( & chip - > dev ) ;
2014-12-12 11:46:37 -08:00
}
2015-03-18 08:17:14 +02:00
static int tpm1_chip_register ( struct tpm_chip * chip )
{
int rc ;
if ( chip - > flags & TPM_CHIP_FLAG_TPM2 )
return 0 ;
rc = tpm_sysfs_add_device ( chip ) ;
if ( rc )
return rc ;
chip - > bios_dir = tpm_bios_log_setup ( chip - > devname ) ;
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 ) ;
tpm_sysfs_del_device ( chip ) ;
}
2014-12-12 11:46:34 -08:00
/*
2014-12-12 11:46:37 -08:00
* tpm_chip_register ( ) - create a character device for the TPM chip
2014-12-12 11:46:34 -08:00
* @ chip : TPM chip to use .
*
2015-03-01 23:55:47 +02: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 11:46:34 -08:00
*
2015-03-01 23:55:47 +02:00
* This function should be only called after the chip initialization is
* complete .
2014-12-12 11:46:34 -08:00
*/
int tpm_chip_register ( struct tpm_chip * chip )
{
int rc ;
2015-03-18 08:17:14 +02:00
rc = tpm1_chip_register ( chip ) ;
if ( rc )
return rc ;
2014-12-12 11:46:34 -08:00
2015-04-14 17:56:48 +03:00
tpm_add_ppi ( chip ) ;
2016-01-29 09:47:22 -08:00
rc = tpm_add_char_device ( chip ) ;
2015-03-01 23:55:47 +02:00
if ( rc )
2015-03-18 08:17:14 +02:00
goto out_err ;
2015-03-01 23:55:47 +02:00
2014-12-12 11:46:34 -08:00
/* Make the chip available. */
spin_lock ( & driver_lock ) ;
2015-11-02 19:55:29 +02:00
list_add_tail_rcu ( & chip - > list , & tpm_chip_list ) ;
2014-12-12 11:46:34 -08:00
spin_unlock ( & driver_lock ) ;
chip - > flags | = TPM_CHIP_FLAG_REGISTERED ;
2015-11-07 13:33:25 +02:00
if ( ! ( chip - > flags & TPM_CHIP_FLAG_TPM2 ) ) {
rc = __compat_only_sysfs_link_entry_to_kobj ( & chip - > pdev - > kobj ,
& chip - > dev . kobj ,
" ppi " ) ;
if ( rc & & rc ! = - ENOENT ) {
tpm_chip_unregister ( chip ) ;
return rc ;
}
}
2014-12-12 11:46:34 -08:00
return 0 ;
2015-03-18 08:17:14 +02:00
out_err :
tpm1_chip_unregister ( chip ) ;
2014-12-12 11:46:34 -08:00
return rc ;
}
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 ( ) .
*
* 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 ;
spin_lock ( & driver_lock ) ;
list_del_rcu ( & chip - > list ) ;
spin_unlock ( & driver_lock ) ;
synchronize_rcu ( ) ;
2015-04-14 17:56:48 +03:00
if ( ! ( chip - > flags & TPM_CHIP_FLAG_TPM2 ) )
sysfs_remove_link ( & chip - > pdev - > kobj , " ppi " ) ;
2015-03-18 08:17:14 +02:00
tpm1_chip_unregister ( chip ) ;
2016-01-29 09:47:22 -08:00
tpm_del_char_device ( chip ) ;
2014-12-12 11:46:34 -08:00
}
EXPORT_SYMBOL_GPL ( tpm_chip_unregister ) ;