2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2017-11-01 14:25:31 -05:00
/*
* SMI methods for use with dell - smbios
*
* Copyright ( c ) Red Hat < mjg @ redhat . com >
* Copyright ( c ) 2014 Gabriele Mazzotta < gabriele . mzt @ gmail . com >
2020-04-10 14:34:00 -07:00
* Copyright ( c ) 2014 Pali Rohár < pali @ kernel . org >
2017-11-01 14:25:31 -05:00
* Copyright ( c ) 2017 Dell Inc .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/dmi.h>
# include <linux/gfp.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/platform_device.h>
2018-09-26 16:50:20 -05:00
# include "dcdbas.h"
2017-11-01 14:25:31 -05:00
# include "dell-smbios.h"
static int da_command_address ;
static int da_command_code ;
2022-03-18 16:09:50 +01:00
static struct smi_buffer smi_buf ;
2017-11-01 14:25:31 -05:00
static struct calling_interface_buffer * buffer ;
2018-06-21 19:15:24 +01:00
static struct platform_device * platform_device ;
2017-11-01 14:25:31 -05:00
static DEFINE_MUTEX ( smm_mutex ) ;
2018-02-27 12:23:04 -06:00
static void parse_da_table ( const struct dmi_header * dm )
2017-11-01 14:25:31 -05:00
{
struct calling_interface_structure * table =
container_of ( dm , struct calling_interface_structure , header ) ;
/* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
* 6 bytes of entry
*/
if ( dm - > length < 17 )
return ;
da_command_address = table - > cmdIOAddress ;
da_command_code = table - > cmdIOCode ;
}
2018-02-27 12:23:04 -06:00
static void find_cmd_address ( const struct dmi_header * dm , void * dummy )
2017-11-01 14:25:31 -05:00
{
switch ( dm - > type ) {
case 0xda : /* Calling interface */
parse_da_table ( dm ) ;
break ;
}
}
2018-06-21 19:15:24 +01:00
static int dell_smbios_smm_call ( struct calling_interface_buffer * input )
2017-11-01 14:25:31 -05:00
{
struct smi_cmd command ;
size_t size ;
size = sizeof ( struct calling_interface_buffer ) ;
command . magic = SMI_CMD_MAGIC ;
command . command_address = da_command_address ;
command . command_code = da_command_code ;
2022-03-18 16:09:50 +01:00
command . ebx = smi_buf . dma ;
2017-11-01 14:25:31 -05:00
command . ecx = 0x42534931 ;
mutex_lock ( & smm_mutex ) ;
memcpy ( buffer , input , size ) ;
dcdbas_smi_request ( & command ) ;
memcpy ( input , buffer , size ) ;
mutex_unlock ( & smm_mutex ) ;
return 0 ;
}
2017-11-01 14:25:33 -05:00
/* When enabled this indicates that SMM won't work */
static bool test_wsmt_enabled ( void )
{
struct calling_interface_token * wsmt ;
/* if token doesn't exist, SMM will work */
wsmt = dell_smbios_find_token ( WSMT_EN_TOKEN ) ;
if ( ! wsmt )
return false ;
/* If token exists, try to access over SMM but set a dummy return.
* - If WSMT disabled it will be overwritten by SMM
* - If WSMT enabled then dummy value will remain
*/
buffer - > cmd_class = CLASS_TOKEN_READ ;
buffer - > cmd_select = SELECT_TOKEN_STD ;
memset ( buffer , 0 , sizeof ( struct calling_interface_buffer ) ) ;
buffer - > input [ 0 ] = wsmt - > location ;
buffer - > output [ 0 ] = 99 ;
dell_smbios_smm_call ( buffer ) ;
if ( buffer - > output [ 0 ] = = 99 )
return true ;
return false ;
}
2018-02-27 12:23:04 -06:00
int init_dell_smbios_smm ( void )
2017-11-01 14:25:31 -05:00
{
int ret ;
/*
* Allocate buffer below 4 GB for SMI data - - only 32 - bit physical addr
* is passed to SMI handler .
*/
2022-03-18 16:09:50 +01:00
ret = dcdbas_smi_alloc ( & smi_buf , PAGE_SIZE ) ;
if ( ret )
return ret ;
buffer = ( void * ) smi_buf . virt ;
2017-11-01 14:25:31 -05:00
dmi_walk ( find_cmd_address , NULL ) ;
2017-11-01 14:25:33 -05:00
if ( test_wsmt_enabled ( ) ) {
pr_debug ( " Disabling due to WSMT enabled \n " ) ;
ret = - ENODEV ;
goto fail_wsmt ;
}
2017-11-01 14:25:31 -05:00
platform_device = platform_device_alloc ( " dell-smbios " , 1 ) ;
if ( ! platform_device ) {
ret = - ENOMEM ;
goto fail_platform_device_alloc ;
}
ret = platform_device_add ( platform_device ) ;
if ( ret )
goto fail_platform_device_add ;
ret = dell_smbios_register_device ( & platform_device - > dev ,
& dell_smbios_smm_call ) ;
if ( ret )
goto fail_register ;
return 0 ;
fail_register :
platform_device_del ( platform_device ) ;
fail_platform_device_add :
platform_device_put ( platform_device ) ;
2017-11-01 14:25:33 -05:00
fail_wsmt :
2017-11-01 14:25:31 -05:00
fail_platform_device_alloc :
2022-03-18 16:09:50 +01:00
dcdbas_smi_free ( & smi_buf ) ;
2017-11-01 14:25:31 -05:00
return ret ;
}
2018-02-27 12:23:04 -06:00
void exit_dell_smbios_smm ( void )
2017-11-01 14:25:31 -05:00
{
if ( platform_device ) {
dell_smbios_unregister_device ( & platform_device - > dev ) ;
platform_device_unregister ( platform_device ) ;
2022-03-18 16:09:50 +01:00
dcdbas_smi_free ( & smi_buf ) ;
2017-11-01 14:25:31 -05:00
}
}