2012-08-22 18:47:22 +08:00
# include <linux/acpi.h>
# include <acpi/acpi_drivers.h>
# include "tpm.h"
static const u8 tpm_ppi_uuid [ ] = {
0xA6 , 0xFA , 0xDD , 0x3D ,
0x1B , 0x36 ,
0xB4 , 0x4E ,
0xA4 , 0x24 ,
0x8D , 0x10 , 0x08 , 0x9D , 0x16 , 0x53
} ;
static char * tpm_device_name = " TPM " ;
# define TPM_PPI_REVISION_ID 1
# define TPM_PPI_FN_VERSION 1
# define TPM_PPI_FN_SUBREQ 2
# define TPM_PPI_FN_GETREQ 3
# define TPM_PPI_FN_GETACT 4
# define TPM_PPI_FN_GETRSP 5
# define TPM_PPI_FN_SUBREQ2 7
# define TPM_PPI_FN_GETOPR 8
# define PPI_TPM_REQ_MAX 22
# define PPI_VS_REQ_START 128
# define PPI_VS_REQ_END 255
# define PPI_VERSION_LEN 3
static acpi_status ppi_callback ( acpi_handle handle , u32 level , void * context ,
void * * return_value )
{
acpi_status status ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
status = acpi_get_name ( handle , ACPI_FULL_PATHNAME , & buffer ) ;
if ( strstr ( buffer . pointer , context ) ! = NULL ) {
* return_value = handle ;
kfree ( buffer . pointer ) ;
return AE_CTRL_TERMINATE ;
}
return AE_OK ;
}
static inline void ppi_assign_params ( union acpi_object params [ 4 ] ,
u64 function_num )
{
params [ 0 ] . type = ACPI_TYPE_BUFFER ;
params [ 0 ] . buffer . length = sizeof ( tpm_ppi_uuid ) ;
params [ 0 ] . buffer . pointer = ( char * ) tpm_ppi_uuid ;
params [ 1 ] . type = ACPI_TYPE_INTEGER ;
params [ 1 ] . integer . value = TPM_PPI_REVISION_ID ;
params [ 2 ] . type = ACPI_TYPE_INTEGER ;
params [ 2 ] . integer . value = function_num ;
params [ 3 ] . type = ACPI_TYPE_PACKAGE ;
params [ 3 ] . package . count = 0 ;
params [ 3 ] . package . elements = NULL ;
}
2013-08-29 20:39:11 +08:00
static ssize_t tpm_show_ppi_version ( struct device * dev ,
struct device_attribute * attr , char * buf )
2012-08-22 18:47:22 +08:00
{
acpi_handle handle ;
acpi_status status ;
struct acpi_object_list input ;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object params [ 4 ] ;
union acpi_object * obj ;
input . count = 4 ;
ppi_assign_params ( params , TPM_PPI_FN_VERSION ) ;
input . pointer = params ;
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE , ACPI_ROOT_OBJECT ,
ACPI_UINT32_MAX , ppi_callback , NULL ,
tpm_device_name , & handle ) ;
if ( ACPI_FAILURE ( status ) )
return - ENXIO ;
status = acpi_evaluate_object_typed ( handle , " _DSM " , & input , & output ,
ACPI_TYPE_STRING ) ;
if ( ACPI_FAILURE ( status ) )
return - ENOMEM ;
obj = ( union acpi_object * ) output . pointer ;
status = scnprintf ( buf , PAGE_SIZE , " %s \n " , obj - > string . pointer ) ;
kfree ( output . pointer ) ;
return status ;
}
2013-08-29 20:39:11 +08:00
static ssize_t tpm_show_ppi_request ( struct device * dev ,
struct device_attribute * attr , char * buf )
2012-08-22 18:47:22 +08:00
{
acpi_handle handle ;
acpi_status status ;
struct acpi_object_list input ;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object params [ 4 ] ;
union acpi_object * ret_obj ;
input . count = 4 ;
ppi_assign_params ( params , TPM_PPI_FN_GETREQ ) ;
input . pointer = params ;
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE , ACPI_ROOT_OBJECT ,
ACPI_UINT32_MAX , ppi_callback , NULL ,
tpm_device_name , & handle ) ;
if ( ACPI_FAILURE ( status ) )
return - ENXIO ;
status = acpi_evaluate_object_typed ( handle , " _DSM " , & input , & output ,
ACPI_TYPE_PACKAGE ) ;
if ( ACPI_FAILURE ( status ) )
return - ENOMEM ;
/*
* output . pointer should be of package type , including two integers .
* The first is function return code , 0 means success and 1 means
* error . The second is pending TPM operation requested by the OS , 0
* means none and > 0 means operation value .
*/
ret_obj = ( ( union acpi_object * ) output . pointer ) - > package . elements ;
if ( ret_obj - > type = = ACPI_TYPE_INTEGER ) {
if ( ret_obj - > integer . value ) {
status = - EFAULT ;
goto cleanup ;
}
ret_obj + + ;
if ( ret_obj - > type = = ACPI_TYPE_INTEGER )
status = scnprintf ( buf , PAGE_SIZE , " %llu \n " ,
ret_obj - > integer . value ) ;
else
status = - EINVAL ;
} else {
status = - EINVAL ;
}
cleanup :
kfree ( output . pointer ) ;
return status ;
}
2013-08-29 20:39:11 +08:00
static ssize_t tpm_store_ppi_request ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
2012-08-22 18:47:22 +08:00
{
char version [ PPI_VERSION_LEN + 1 ] ;
acpi_handle handle ;
acpi_status status ;
struct acpi_object_list input ;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object params [ 4 ] ;
union acpi_object obj ;
u32 req ;
u64 ret ;
input . count = 4 ;
ppi_assign_params ( params , TPM_PPI_FN_VERSION ) ;
input . pointer = params ;
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE , ACPI_ROOT_OBJECT ,
ACPI_UINT32_MAX , ppi_callback , NULL ,
tpm_device_name , & handle ) ;
if ( ACPI_FAILURE ( status ) )
return - ENXIO ;
status = acpi_evaluate_object_typed ( handle , " _DSM " , & input , & output ,
ACPI_TYPE_STRING ) ;
if ( ACPI_FAILURE ( status ) )
return - ENOMEM ;
2013-02-28 16:24:50 -06:00
strlcpy ( version ,
2012-08-22 18:47:22 +08:00
( ( union acpi_object * ) output . pointer ) - > string . pointer ,
2013-02-28 16:24:50 -06:00
PPI_VERSION_LEN + 1 ) ;
2012-08-22 18:47:22 +08:00
kfree ( output . pointer ) ;
output . length = ACPI_ALLOCATE_BUFFER ;
output . pointer = NULL ;
/*
* the function to submit TPM operation request to pre - os environment
* is updated with function index from SUBREQ to SUBREQ2 since PPI
* version 1.1
*/
if ( strcmp ( version , " 1.1 " ) = = - 1 )
params [ 2 ] . integer . value = TPM_PPI_FN_SUBREQ ;
else
params [ 2 ] . integer . value = TPM_PPI_FN_SUBREQ2 ;
/*
* PPI spec defines params [ 3 ] . type as ACPI_TYPE_PACKAGE . Some BIOS
* accept buffer / string / integer type , but some BIOS accept buffer /
* string / package type . For PPI version 1.0 and 1.1 , use buffer type
* for compatibility , and use package type since 1.2 according to spec .
*/
if ( strcmp ( version , " 1.2 " ) = = - 1 ) {
params [ 3 ] . type = ACPI_TYPE_BUFFER ;
params [ 3 ] . buffer . length = sizeof ( req ) ;
sscanf ( buf , " %d " , & req ) ;
params [ 3 ] . buffer . pointer = ( char * ) & req ;
} else {
params [ 3 ] . package . count = 1 ;
obj . type = ACPI_TYPE_INTEGER ;
sscanf ( buf , " %llu " , & obj . integer . value ) ;
params [ 3 ] . package . elements = & obj ;
}
status = acpi_evaluate_object_typed ( handle , " _DSM " , & input , & output ,
ACPI_TYPE_INTEGER ) ;
if ( ACPI_FAILURE ( status ) )
return - ENOMEM ;
ret = ( ( union acpi_object * ) output . pointer ) - > integer . value ;
if ( ret = = 0 )
status = ( acpi_status ) count ;
else if ( ret = = 1 )
status = - EPERM ;
else
status = - EFAULT ;
kfree ( output . pointer ) ;
return status ;
}
2013-08-29 20:39:11 +08:00
static ssize_t tpm_show_ppi_transition_action ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
2012-08-22 18:47:22 +08:00
{
char version [ PPI_VERSION_LEN + 1 ] ;
acpi_handle handle ;
acpi_status status ;
struct acpi_object_list input ;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object params [ 4 ] ;
u32 ret ;
char * info [ ] = {
" None " ,
" Shutdown " ,
" Reboot " ,
" OS Vendor-specific " ,
" Error " ,
} ;
input . count = 4 ;
ppi_assign_params ( params , TPM_PPI_FN_VERSION ) ;
input . pointer = params ;
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE , ACPI_ROOT_OBJECT ,
ACPI_UINT32_MAX , ppi_callback , NULL ,
tpm_device_name , & handle ) ;
if ( ACPI_FAILURE ( status ) )
return - ENXIO ;
status = acpi_evaluate_object_typed ( handle , " _DSM " , & input , & output ,
ACPI_TYPE_STRING ) ;
if ( ACPI_FAILURE ( status ) )
return - ENOMEM ;
2013-02-28 16:24:50 -06:00
strlcpy ( version ,
2012-08-22 18:47:22 +08:00
( ( union acpi_object * ) output . pointer ) - > string . pointer ,
2013-02-28 16:24:50 -06:00
PPI_VERSION_LEN + 1 ) ;
2012-08-22 18:47:22 +08:00
/*
* PPI spec defines params [ 3 ] . type as empty package , but some platforms
* ( e . g . Capella with PPI 1.0 ) need integer / string / buffer type , so for
* compatibility , define params [ 3 ] . type as buffer , if PPI version < 1.2
*/
if ( strcmp ( version , " 1.2 " ) = = - 1 ) {
params [ 3 ] . type = ACPI_TYPE_BUFFER ;
params [ 3 ] . buffer . length = 0 ;
params [ 3 ] . buffer . pointer = NULL ;
}
params [ 2 ] . integer . value = TPM_PPI_FN_GETACT ;
kfree ( output . pointer ) ;
output . length = ACPI_ALLOCATE_BUFFER ;
output . pointer = NULL ;
status = acpi_evaluate_object_typed ( handle , " _DSM " , & input , & output ,
ACPI_TYPE_INTEGER ) ;
if ( ACPI_FAILURE ( status ) )
return - ENOMEM ;
ret = ( ( union acpi_object * ) output . pointer ) - > integer . value ;
if ( ret < ARRAY_SIZE ( info ) - 1 )
status = scnprintf ( buf , PAGE_SIZE , " %d: %s \n " , ret , info [ ret ] ) ;
else
status = scnprintf ( buf , PAGE_SIZE , " %d: %s \n " , ret ,
info [ ARRAY_SIZE ( info ) - 1 ] ) ;
kfree ( output . pointer ) ;
return status ;
}
2013-08-29 20:39:11 +08:00
static ssize_t tpm_show_ppi_response ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
2012-08-22 18:47:22 +08:00
{
acpi_handle handle ;
acpi_status status ;
struct acpi_object_list input ;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object params [ 4 ] ;
union acpi_object * ret_obj ;
u64 req ;
input . count = 4 ;
ppi_assign_params ( params , TPM_PPI_FN_GETRSP ) ;
input . pointer = params ;
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE , ACPI_ROOT_OBJECT ,
ACPI_UINT32_MAX , ppi_callback , NULL ,
tpm_device_name , & handle ) ;
if ( ACPI_FAILURE ( status ) )
return - ENXIO ;
status = acpi_evaluate_object_typed ( handle , " _DSM " , & input , & output ,
ACPI_TYPE_PACKAGE ) ;
if ( ACPI_FAILURE ( status ) )
return - ENOMEM ;
/*
* parameter output . pointer should be of package type , including
* 3 integers . The first means function return code , the second means
* most recent TPM operation request , and the last means response to
* the most recent TPM operation request . Only if the first is 0 , and
* the second integer is not 0 , the response makes sense .
*/
ret_obj = ( ( union acpi_object * ) output . pointer ) - > package . elements ;
if ( ret_obj - > type ! = ACPI_TYPE_INTEGER ) {
status = - EINVAL ;
goto cleanup ;
}
if ( ret_obj - > integer . value ) {
status = - EFAULT ;
goto cleanup ;
}
ret_obj + + ;
if ( ret_obj - > type ! = ACPI_TYPE_INTEGER ) {
status = - EINVAL ;
goto cleanup ;
}
if ( ret_obj - > integer . value ) {
req = ret_obj - > integer . value ;
ret_obj + + ;
if ( ret_obj - > type ! = ACPI_TYPE_INTEGER ) {
status = - EINVAL ;
goto cleanup ;
}
if ( ret_obj - > integer . value = = 0 )
status = scnprintf ( buf , PAGE_SIZE , " %llu %s \n " , req ,
" 0: Success " ) ;
else if ( ret_obj - > integer . value = = 0xFFFFFFF0 )
status = scnprintf ( buf , PAGE_SIZE , " %llu %s \n " , req ,
" 0xFFFFFFF0: User Abort " ) ;
else if ( ret_obj - > integer . value = = 0xFFFFFFF1 )
status = scnprintf ( buf , PAGE_SIZE , " %llu %s \n " , req ,
" 0xFFFFFFF1: BIOS Failure " ) ;
else if ( ret_obj - > integer . value > = 1 & &
ret_obj - > integer . value < = 0x00000FFF )
status = scnprintf ( buf , PAGE_SIZE , " %llu %llu: %s \n " ,
req , ret_obj - > integer . value ,
" Corresponding TPM error " ) ;
else
status = scnprintf ( buf , PAGE_SIZE , " %llu %llu: %s \n " ,
req , ret_obj - > integer . value ,
" Error " ) ;
} else {
status = scnprintf ( buf , PAGE_SIZE , " %llu: %s \n " ,
ret_obj - > integer . value , " No Recent Request " ) ;
}
cleanup :
kfree ( output . pointer ) ;
return status ;
}
static ssize_t show_ppi_operations ( char * buf , u32 start , u32 end )
{
char * str = buf ;
2013-02-28 16:24:50 -06:00
char version [ PPI_VERSION_LEN + 1 ] ;
2012-08-22 18:47:22 +08:00
acpi_handle handle ;
acpi_status status ;
struct acpi_object_list input ;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object params [ 4 ] ;
union acpi_object obj ;
int i ;
u32 ret ;
char * info [ ] = {
" Not implemented " ,
" BIOS only " ,
" Blocked for OS by BIOS " ,
" User required " ,
" User not required " ,
} ;
input . count = 4 ;
ppi_assign_params ( params , TPM_PPI_FN_VERSION ) ;
input . pointer = params ;
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE , ACPI_ROOT_OBJECT ,
ACPI_UINT32_MAX , ppi_callback , NULL ,
tpm_device_name , & handle ) ;
if ( ACPI_FAILURE ( status ) )
return - ENXIO ;
status = acpi_evaluate_object_typed ( handle , " _DSM " , & input , & output ,
ACPI_TYPE_STRING ) ;
if ( ACPI_FAILURE ( status ) )
return - ENOMEM ;
2013-02-28 16:24:50 -06:00
strlcpy ( version ,
2012-08-22 18:47:22 +08:00
( ( union acpi_object * ) output . pointer ) - > string . pointer ,
2013-02-28 16:24:50 -06:00
PPI_VERSION_LEN + 1 ) ;
2012-08-22 18:47:22 +08:00
kfree ( output . pointer ) ;
output . length = ACPI_ALLOCATE_BUFFER ;
output . pointer = NULL ;
if ( strcmp ( version , " 1.2 " ) = = - 1 )
return - EPERM ;
params [ 2 ] . integer . value = TPM_PPI_FN_GETOPR ;
params [ 3 ] . package . count = 1 ;
obj . type = ACPI_TYPE_INTEGER ;
params [ 3 ] . package . elements = & obj ;
for ( i = start ; i < = end ; i + + ) {
obj . integer . value = i ;
status = acpi_evaluate_object_typed ( handle , " _DSM " ,
& input , & output , ACPI_TYPE_INTEGER ) ;
if ( ACPI_FAILURE ( status ) )
return - ENOMEM ;
ret = ( ( union acpi_object * ) output . pointer ) - > integer . value ;
if ( ret > 0 & & ret < ARRAY_SIZE ( info ) )
str + = scnprintf ( str , PAGE_SIZE , " %d %d: %s \n " ,
i , ret , info [ ret ] ) ;
kfree ( output . pointer ) ;
output . length = ACPI_ALLOCATE_BUFFER ;
output . pointer = NULL ;
}
return str - buf ;
}
2013-08-29 20:39:11 +08:00
static ssize_t tpm_show_ppi_tcg_operations ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
2012-08-22 18:47:22 +08:00
{
return show_ppi_operations ( buf , 0 , PPI_TPM_REQ_MAX ) ;
}
2013-08-29 20:39:11 +08:00
static ssize_t tpm_show_ppi_vs_operations ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
2012-08-22 18:47:22 +08:00
{
return show_ppi_operations ( buf , PPI_VS_REQ_START , PPI_VS_REQ_END ) ;
}
static DEVICE_ATTR ( version , S_IRUGO , tpm_show_ppi_version , NULL ) ;
static DEVICE_ATTR ( request , S_IRUGO | S_IWUSR | S_IWGRP ,
tpm_show_ppi_request , tpm_store_ppi_request ) ;
static DEVICE_ATTR ( transition_action , S_IRUGO ,
tpm_show_ppi_transition_action , NULL ) ;
static DEVICE_ATTR ( response , S_IRUGO , tpm_show_ppi_response , NULL ) ;
static DEVICE_ATTR ( tcg_operations , S_IRUGO , tpm_show_ppi_tcg_operations , NULL ) ;
static DEVICE_ATTR ( vs_operations , S_IRUGO , tpm_show_ppi_vs_operations , NULL ) ;
static struct attribute * ppi_attrs [ ] = {
& dev_attr_version . attr ,
& dev_attr_request . attr ,
& dev_attr_transition_action . attr ,
& dev_attr_response . attr ,
& dev_attr_tcg_operations . attr ,
& dev_attr_vs_operations . attr , NULL ,
} ;
static struct attribute_group ppi_attr_grp = {
2012-10-09 17:35:22 +08:00
. name = " ppi " ,
2012-08-22 18:47:22 +08:00
. attrs = ppi_attrs
} ;
2012-10-09 17:35:22 +08:00
int tpm_add_ppi ( struct kobject * parent )
2012-08-22 18:47:22 +08:00
{
2012-10-09 17:35:22 +08:00
return sysfs_create_group ( parent , & ppi_attr_grp ) ;
}
EXPORT_SYMBOL_GPL ( tpm_add_ppi ) ;
void tpm_remove_ppi ( struct kobject * parent )
{
sysfs_remove_group ( parent , & ppi_attr_grp ) ;
2012-08-22 18:47:22 +08:00
}
2012-10-09 17:35:22 +08:00
EXPORT_SYMBOL_GPL ( tpm_remove_ppi ) ;
2012-08-22 18:47:22 +08:00
MODULE_LICENSE ( " GPL " ) ;