2014-12-12 22:46:39 +03:00
/*
* Copyright ( C ) 2014 Intel Corporation
*
* Authors :
* Jarkko Sakkinen < jarkko . sakkinen @ linux . intel . com >
*
* Maintained by : < tpmdd - devel @ lists . sourceforge . net >
*
* This device driver implements the TPM interface as defined in
* the TCG CRB 2.0 TPM specification .
*
* 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/acpi.h>
# include <linux/highmem.h>
# include <linux/rculist.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include "tpm.h"
# define ACPI_SIG_TPM2 "TPM2"
static const u8 CRB_ACPI_START_UUID [ ] = {
/* 0000 */ 0xAB , 0x6C , 0xBF , 0x6B , 0x63 , 0x54 , 0x14 , 0x47 ,
/* 0008 */ 0xB7 , 0xCD , 0xF0 , 0x20 , 0x3C , 0x03 , 0x68 , 0xD4
} ;
enum crb_defaults {
CRB_ACPI_START_REVISION_ID = 1 ,
CRB_ACPI_START_INDEX = 1 ,
} ;
enum crb_start_method {
CRB_SM_ACPI_START = 2 ,
CRB_SM_CRB = 7 ,
CRB_SM_CRB_WITH_ACPI_START = 8 ,
} ;
struct acpi_tpm2 {
struct acpi_table_header hdr ;
u16 platform_class ;
u16 reserved ;
u64 control_area_pa ;
u32 start_method ;
} __packed ;
enum crb_ca_request {
CRB_CA_REQ_GO_IDLE = BIT ( 0 ) ,
CRB_CA_REQ_CMD_READY = BIT ( 1 ) ,
} ;
enum crb_ca_status {
CRB_CA_STS_ERROR = BIT ( 0 ) ,
CRB_CA_STS_TPM_IDLE = BIT ( 1 ) ,
} ;
enum crb_start {
CRB_START_INVOKE = BIT ( 0 ) ,
} ;
enum crb_cancel {
CRB_CANCEL_INVOKE = BIT ( 0 ) ,
} ;
struct crb_control_area {
u32 req ;
u32 sts ;
u32 cancel ;
u32 start ;
u32 int_enable ;
u32 int_sts ;
u32 cmd_size ;
u64 cmd_pa ;
u32 rsp_size ;
u64 rsp_pa ;
} __packed ;
enum crb_status {
CRB_STS_COMPLETE = BIT ( 0 ) ,
} ;
enum crb_flags {
CRB_FL_ACPI_START = BIT ( 0 ) ,
CRB_FL_CRB_START = BIT ( 1 ) ,
} ;
struct crb_priv {
unsigned int flags ;
struct crb_control_area __iomem * cca ;
u8 __iomem * cmd ;
u8 __iomem * rsp ;
} ;
2015-01-29 08:43:47 +03:00
static SIMPLE_DEV_PM_OPS ( crb_pm , tpm_pm_suspend , tpm_pm_resume ) ;
2014-12-12 22:46:39 +03:00
static u8 crb_status ( struct tpm_chip * chip )
{
struct crb_priv * priv = chip - > vendor . priv ;
u8 sts = 0 ;
if ( ( le32_to_cpu ( ioread32 ( & priv - > cca - > start ) ) & CRB_START_INVOKE ) ! =
CRB_START_INVOKE )
sts | = CRB_STS_COMPLETE ;
return sts ;
}
static int crb_recv ( struct tpm_chip * chip , u8 * buf , size_t count )
{
struct crb_priv * priv = chip - > vendor . priv ;
unsigned int expected ;
/* sanity check */
if ( count < 6 )
return - EIO ;
if ( le32_to_cpu ( ioread32 ( & priv - > cca - > sts ) ) & CRB_CA_STS_ERROR )
return - EIO ;
memcpy_fromio ( buf , priv - > rsp , 6 ) ;
expected = be32_to_cpup ( ( __be32 * ) & buf [ 2 ] ) ;
if ( expected > count )
return - EIO ;
memcpy_fromio ( & buf [ 6 ] , & priv - > rsp [ 6 ] , expected - 6 ) ;
return expected ;
}
static int crb_do_acpi_start ( struct tpm_chip * chip )
{
union acpi_object * obj ;
int rc ;
obj = acpi_evaluate_dsm ( chip - > acpi_dev_handle ,
CRB_ACPI_START_UUID ,
CRB_ACPI_START_REVISION_ID ,
CRB_ACPI_START_INDEX ,
NULL ) ;
if ( ! obj )
return - ENXIO ;
rc = obj - > integer . value = = 0 ? 0 : - ENXIO ;
ACPI_FREE ( obj ) ;
return rc ;
}
static int crb_send ( struct tpm_chip * chip , u8 * buf , size_t len )
{
struct crb_priv * priv = chip - > vendor . priv ;
int rc = 0 ;
if ( len > le32_to_cpu ( ioread32 ( & priv - > cca - > cmd_size ) ) ) {
dev_err ( & chip - > dev ,
" invalid command count value %x %zx \n " ,
( unsigned int ) len ,
( size_t ) le32_to_cpu ( ioread32 ( & priv - > cca - > cmd_size ) ) ) ;
return - E2BIG ;
}
memcpy_toio ( priv - > cmd , buf , len ) ;
/* Make sure that cmd is populated before issuing start. */
wmb ( ) ;
if ( priv - > flags & CRB_FL_CRB_START )
iowrite32 ( cpu_to_le32 ( CRB_START_INVOKE ) , & priv - > cca - > start ) ;
if ( priv - > flags & CRB_FL_ACPI_START )
rc = crb_do_acpi_start ( chip ) ;
return rc ;
}
static void crb_cancel ( struct tpm_chip * chip )
{
struct crb_priv * priv = chip - > vendor . priv ;
iowrite32 ( cpu_to_le32 ( CRB_CANCEL_INVOKE ) , & priv - > cca - > cancel ) ;
/* Make sure that cmd is populated before issuing cancel. */
wmb ( ) ;
if ( ( priv - > flags & CRB_FL_ACPI_START ) & & crb_do_acpi_start ( chip ) )
dev_err ( & chip - > dev , " ACPI Start failed \n " ) ;
iowrite32 ( 0 , & priv - > cca - > cancel ) ;
}
static bool crb_req_canceled ( struct tpm_chip * chip , u8 status )
{
struct crb_priv * priv = chip - > vendor . priv ;
u32 cancel = le32_to_cpu ( ioread32 ( & priv - > cca - > cancel ) ) ;
return ( cancel & CRB_CANCEL_INVOKE ) = = CRB_CANCEL_INVOKE ;
}
static const struct tpm_class_ops tpm_crb = {
. status = crb_status ,
. recv = crb_recv ,
. send = crb_send ,
. cancel = crb_cancel ,
. req_canceled = crb_req_canceled ,
. req_complete_mask = CRB_STS_COMPLETE ,
. req_complete_val = CRB_STS_COMPLETE ,
} ;
static int crb_acpi_add ( struct acpi_device * device )
{
struct tpm_chip * chip ;
struct acpi_tpm2 * buf ;
struct crb_priv * priv ;
struct device * dev = & device - > dev ;
acpi_status status ;
u32 sm ;
u64 pa ;
int rc ;
chip = tpmm_chip_alloc ( dev , & tpm_crb ) ;
if ( IS_ERR ( chip ) )
return PTR_ERR ( chip ) ;
chip - > flags = TPM_CHIP_FLAG_TPM2 ;
status = acpi_get_table ( ACPI_SIG_TPM2 , 1 ,
( struct acpi_table_header * * ) & buf ) ;
if ( ACPI_FAILURE ( status ) ) {
dev_err ( dev , " failed to get TPM2 ACPI table \n " ) ;
return - ENODEV ;
}
2015-06-24 17:14:55 +03:00
/* At least some versions of AMI BIOS have a bug that TPM2 table has
* zero address for the control area and therefore we must fail .
*/
if ( ! buf - > control_area_pa ) {
dev_err ( dev , " TPM2 ACPI table has a zero address for the control area \n " ) ;
return - EINVAL ;
}
2014-12-12 22:46:39 +03:00
if ( buf - > hdr . length < sizeof ( struct acpi_tpm2 ) ) {
dev_err ( dev , " TPM2 ACPI table has wrong size " ) ;
return - EINVAL ;
}
priv = ( struct crb_priv * ) devm_kzalloc ( dev , sizeof ( struct crb_priv ) ,
GFP_KERNEL ) ;
if ( ! priv ) {
dev_err ( dev , " failed to devm_kzalloc for private data \n " ) ;
return - ENOMEM ;
}
sm = le32_to_cpu ( buf - > start_method ) ;
/* The reason for the extra quirk is that the PTT in 4th Gen Core CPUs
* report only ACPI start but in practice seems to require both
* ACPI start and CRB start .
*/
if ( sm = = CRB_SM_CRB | | sm = = CRB_SM_CRB_WITH_ACPI_START | |
! strcmp ( acpi_device_hid ( device ) , " MSFT0101 " ) )
priv - > flags | = CRB_FL_CRB_START ;
if ( sm = = CRB_SM_ACPI_START | | sm = = CRB_SM_CRB_WITH_ACPI_START )
priv - > flags | = CRB_FL_ACPI_START ;
priv - > cca = ( struct crb_control_area __iomem * )
devm_ioremap_nocache ( dev , buf - > control_area_pa , 0x1000 ) ;
if ( ! priv - > cca ) {
dev_err ( dev , " ioremap of the control area failed \n " ) ;
return - ENOMEM ;
}
memcpy_fromio ( & pa , & priv - > cca - > cmd_pa , 8 ) ;
pa = le64_to_cpu ( pa ) ;
2015-06-09 15:07:59 +03:00
priv - > cmd = devm_ioremap_nocache ( dev , pa ,
2014-12-12 22:46:39 +03:00
ioread32 ( & priv - > cca - > cmd_size ) ) ;
if ( ! priv - > cmd ) {
dev_err ( dev , " ioremap of the command buffer failed \n " ) ;
return - ENOMEM ;
}
memcpy_fromio ( & pa , & priv - > cca - > rsp_pa , 8 ) ;
pa = le64_to_cpu ( pa ) ;
2015-06-09 15:07:59 +03:00
priv - > rsp = devm_ioremap_nocache ( dev , pa ,
2014-12-12 22:46:39 +03:00
ioread32 ( & priv - > cca - > rsp_size ) ) ;
if ( ! priv - > rsp ) {
dev_err ( dev , " ioremap of the response buffer failed \n " ) ;
return - ENOMEM ;
}
chip - > vendor . priv = priv ;
/* Default timeouts and durations */
chip - > vendor . timeout_a = msecs_to_jiffies ( TPM2_TIMEOUT_A ) ;
chip - > vendor . timeout_b = msecs_to_jiffies ( TPM2_TIMEOUT_B ) ;
chip - > vendor . timeout_c = msecs_to_jiffies ( TPM2_TIMEOUT_C ) ;
chip - > vendor . timeout_d = msecs_to_jiffies ( TPM2_TIMEOUT_D ) ;
chip - > vendor . duration [ TPM_SHORT ] =
msecs_to_jiffies ( TPM2_DURATION_SHORT ) ;
chip - > vendor . duration [ TPM_MEDIUM ] =
msecs_to_jiffies ( TPM2_DURATION_MEDIUM ) ;
chip - > vendor . duration [ TPM_LONG ] =
msecs_to_jiffies ( TPM2_DURATION_LONG ) ;
chip - > acpi_dev_handle = device - > handle ;
rc = tpm2_do_selftest ( chip ) ;
if ( rc )
return rc ;
return tpm_chip_register ( chip ) ;
}
static int crb_acpi_remove ( struct acpi_device * device )
{
struct device * dev = & device - > dev ;
struct tpm_chip * chip = dev_get_drvdata ( dev ) ;
tpm_chip_unregister ( chip ) ;
2015-01-29 08:43:47 +03:00
if ( chip - > flags & TPM_CHIP_FLAG_TPM2 )
tpm2_shutdown ( chip , TPM2_SU_CLEAR ) ;
2014-12-12 22:46:39 +03:00
return 0 ;
}
static struct acpi_device_id crb_device_ids [ ] = {
{ " MSFT0101 " , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , crb_device_ids ) ;
static struct acpi_driver crb_acpi_driver = {
. name = " tpm_crb " ,
. ids = crb_device_ids ,
. ops = {
. add = crb_acpi_add ,
. remove = crb_acpi_remove ,
} ,
. drv = {
. pm = & crb_pm ,
} ,
} ;
module_acpi_driver ( crb_acpi_driver ) ;
MODULE_AUTHOR ( " Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> " ) ;
MODULE_DESCRIPTION ( " TPM2 Driver " ) ;
MODULE_VERSION ( " 0.1 " ) ;
MODULE_LICENSE ( " GPL " ) ;