2021-05-31 01:31:11 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Think LMI BIOS configuration driver
*
* Copyright ( C ) 2019 - 2021 Lenovo
*
* Original code from Thinkpad - wmi project https : //github.com/iksaif/thinkpad-wmi
* Copyright ( C ) 2017 Corentin Chary < corentin . chary @ gmail . com >
* Distributed under the GPL - 2.0 license
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/acpi.h>
# include <linux/errno.h>
# include <linux/fs.h>
2023-06-01 23:05:45 +03:00
# include <linux/mutex.h>
2023-09-18 16:51:15 +03:00
# include <linux/string_helpers.h>
2021-05-31 01:31:11 +03:00
# include <linux/types.h>
2022-03-18 00:40:08 +03:00
# include <linux/dmi.h>
2021-05-31 01:31:11 +03:00
# include <linux/wmi.h>
# include "firmware_attributes_class.h"
# include "think-lmi.h"
2021-08-17 03:15:01 +03:00
static bool debug_support ;
module_param ( debug_support , bool , 0444 ) ;
MODULE_PARM_DESC ( debug_support , " Enable debug command support " ) ;
2021-05-31 01:31:11 +03:00
/*
2022-03-18 00:40:08 +03:00
* Name : BiosSetting
* Description : Get item name and settings for current LMI instance .
* Type : Query
* Returns : " Item,Value "
* Example : " WakeOnLAN,Enable "
2021-05-31 01:31:11 +03:00
*/
# define LENOVO_BIOS_SETTING_GUID "51F5230E-9677-46CD-A1CF-C0B23EE34DB7"
/*
2022-03-18 00:40:08 +03:00
* Name : SetBiosSetting
* Description : Change the BIOS setting to the desired value using the SetBiosSetting
* class . To save the settings , use the SaveBiosSetting class .
2021-05-31 01:31:11 +03:00
* BIOS settings and values are case sensitive .
* After making changes to the BIOS settings , you must reboot the computer
* before the changes will take effect .
2022-03-18 00:40:08 +03:00
* Type : Method
* Arguments : " Item,Value,Password,Encoding,KbdLang; "
* Example : " WakeOnLAN,Disable,pa55w0rd,ascii,us; "
2021-05-31 01:31:11 +03:00
*/
# define LENOVO_SET_BIOS_SETTINGS_GUID "98479A64-33F5-4E33-A707-8E251EBBC3A1"
/*
2022-03-18 00:40:08 +03:00
* Name : SaveBiosSettings
* Description : Save any pending changes in settings .
* Type : Method
* Arguments : " Password,Encoding,KbdLang; "
* Example : " pa55w0rd,ascii,us; "
2021-05-31 01:31:11 +03:00
*/
# define LENOVO_SAVE_BIOS_SETTINGS_GUID "6A4B54EF-A5ED-4D33-9455-B0D9B48DF4B3"
/*
2022-03-18 00:40:08 +03:00
* Name : BiosPasswordSettings
* Description : Return BIOS Password settings
* Type : Query
* Returns : PasswordMode , PasswordState , MinLength , MaxLength ,
2021-05-31 01:31:11 +03:00
* SupportedEncoding , SupportedKeyboard
*/
# define LENOVO_BIOS_PASSWORD_SETTINGS_GUID "8ADB159E-1E32-455C-BC93-308A7ED98246"
/*
2022-03-18 00:40:08 +03:00
* Name : SetBiosPassword
* Description : Change a specific password .
2021-05-31 01:31:11 +03:00
* - BIOS settings cannot be changed at the same boot as power - on
* passwords ( POP ) and hard disk passwords ( HDP ) . If you want to change
* BIOS settings and POP or HDP , you must reboot the system after changing
* one of them .
* - A password cannot be set using this method when one does not already
* exist . Passwords can only be updated or cleared .
2022-03-18 00:40:08 +03:00
* Type : Method
* Arguments : " PasswordType,CurrentPassword,NewPassword,Encoding,KbdLang; "
* Example : " pop,pa55w0rd,newpa55w0rd,ascii,us;”
2021-05-31 01:31:11 +03:00
*/
# define LENOVO_SET_BIOS_PASSWORD_GUID "2651D9FD-911C-4B69-B94E-D0DED5963BD7"
/*
2022-03-18 00:40:08 +03:00
* Name : GetBiosSelections
* Description : Return a list of valid settings for a given item .
* Type : Method
* Arguments : " Item "
* Returns : " Value1,Value2,Value3,... "
2021-05-31 01:31:11 +03:00
* Example :
* - > " FlashOverLAN "
* < - " Enabled,Disabled "
*/
# define LENOVO_GET_BIOS_SELECTIONS_GUID "7364651A-132F-4FE7-ADAA-40C6C7EE2E3B"
2021-08-17 03:15:01 +03:00
/*
2022-03-18 00:40:08 +03:00
* Name : DebugCmd
* Description : Debug entry method for entering debug commands to the BIOS
2021-08-17 03:15:01 +03:00
*/
# define LENOVO_DEBUG_CMD_GUID "7FF47003-3B6C-4E5E-A227-E979824A85D1"
2021-11-17 21:44:53 +03:00
/*
2022-03-18 00:40:08 +03:00
* Name : OpcodeIF
* Description : Opcode interface which provides the ability to set multiple
2021-11-17 21:44:53 +03:00
* parameters and then trigger an action with a final command .
* This is particularly useful for simplifying setting passwords .
* With this support comes the ability to set System , HDD and NVMe
* passwords .
* This is currently available on ThinkCenter and ThinkStations platforms
*/
# define LENOVO_OPCODE_IF_GUID "DFDDEF2C-57D4-48ce-B196-0FB787D90836"
2022-03-18 00:40:08 +03:00
/*
* Name : SetBiosCert
* Description : Install BIOS certificate .
* Type : Method
* Arguments : " Certificate,Password "
* You must reboot the computer before the changes will take effect .
*/
# define LENOVO_SET_BIOS_CERT_GUID "26861C9F-47E9-44C4-BD8B-DFE7FA2610FE"
/*
* Name : UpdateBiosCert
* Description : Update BIOS certificate .
* Type : Method
* Format : " Certificate,Signature "
* You must reboot the computer before the changes will take effect .
*/
# define LENOVO_UPDATE_BIOS_CERT_GUID "9AA3180A-9750-41F7-B9F7-D5D3B1BAC3CE"
/*
* Name : ClearBiosCert
* Description : Uninstall BIOS certificate .
* Type : Method
* Format : " Serial,Signature "
* You must reboot the computer before the changes will take effect .
*/
# define LENOVO_CLEAR_BIOS_CERT_GUID "B2BC39A7-78DD-4D71-B059-A510DEC44890"
/*
* Name : CertToPassword
* Description : Switch from certificate to password authentication .
* Type : Method
* Format : " Password,Signature "
* You must reboot the computer before the changes will take effect .
*/
# define LENOVO_CERT_TO_PASSWORD_GUID "0DE8590D-5510-4044-9621-77C227F5A70D"
/*
* Name : SetBiosSettingCert
* Description : Set attribute using certificate authentication .
* Type : Method
* Format : " Item,Value,Signature "
*/
# define LENOVO_SET_BIOS_SETTING_CERT_GUID "34A008CC-D205-4B62-9E67-31DFA8B90003"
/*
* Name : SaveBiosSettingCert
* Description : Save any pending changes in settings .
* Type : Method
* Format : " Signature "
*/
# define LENOVO_SAVE_BIOS_SETTING_CERT_GUID "C050FB9D-DF5F-4606-B066-9EFC401B2551"
/*
* Name : CertThumbprint
* Description : Display Certificate thumbprints
* Type : Query
* Returns : MD5 , SHA1 & SHA256 thumbprints
*/
# define LENOVO_CERT_THUMBPRINT_GUID "C59119ED-1C0D-4806-A8E9-59AA318176C4"
2023-06-01 23:05:49 +03:00
# define TLMI_POP_PWD BIT(0) /* Supervisor */
# define TLMI_PAP_PWD BIT(1) /* Power-on */
# define TLMI_HDD_PWD BIT(2) /* HDD/NVME */
# define TLMI_SMP_PWD BIT(6) /* System Management */
# define TLMI_CERT BIT(7) /* Certificate Based */
2022-03-18 00:40:08 +03:00
2021-05-31 01:31:11 +03:00
# define to_tlmi_pwd_setting(kobj) container_of(kobj, struct tlmi_pwd_setting, kobj)
# define to_tlmi_attr_setting(kobj) container_of(kobj, struct tlmi_attr_setting, kobj)
static const struct tlmi_err_codes tlmi_errs [ ] = {
{ " Success " , 0 } ,
{ " Not Supported " , - EOPNOTSUPP } ,
{ " Invalid Parameter " , - EINVAL } ,
{ " Access Denied " , - EACCES } ,
{ " System Busy " , - EBUSY } ,
} ;
static const char * const encoding_options [ ] = {
[ TLMI_ENCODING_ASCII ] = " ascii " ,
[ TLMI_ENCODING_SCANCODE ] = " scancode " ,
} ;
2021-11-17 21:44:53 +03:00
static const char * const level_options [ ] = {
[ TLMI_LEVEL_USER ] = " user " ,
[ TLMI_LEVEL_MASTER ] = " master " ,
} ;
2021-05-31 01:31:11 +03:00
static struct think_lmi tlmi_priv ;
2021-06-09 17:59:52 +03:00
static struct class * fw_attr_class ;
2023-06-01 23:05:45 +03:00
static DEFINE_MUTEX ( tlmi_mutex ) ;
2021-05-31 01:31:11 +03:00
/* Convert BIOS WMI error string to suitable error code */
static int tlmi_errstr_to_err ( const char * errstr )
{
int i ;
for ( i = 0 ; i < sizeof ( tlmi_errs ) / sizeof ( struct tlmi_err_codes ) ; i + + ) {
if ( ! strcmp ( tlmi_errs [ i ] . err_str , errstr ) )
return tlmi_errs [ i ] . err_code ;
}
return - EPERM ;
}
/* Extract error string from WMI return buffer */
static int tlmi_extract_error ( const struct acpi_buffer * output )
{
const union acpi_object * obj ;
obj = output - > pointer ;
if ( ! obj )
return - ENOMEM ;
if ( obj - > type ! = ACPI_TYPE_STRING | | ! obj - > string . pointer )
return - EIO ;
return tlmi_errstr_to_err ( obj - > string . pointer ) ;
}
/* Utility function to execute WMI call to BIOS */
static int tlmi_simple_call ( const char * guid , const char * arg )
{
const struct acpi_buffer input = { strlen ( arg ) , ( char * ) arg } ;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
acpi_status status ;
int i , err ;
/*
* Duplicated call required to match BIOS workaround for behavior
* seen when WMI accessed via scripting on other OS .
*/
for ( i = 0 ; i < 2 ; i + + ) {
/* (re)initialize output buffer to default state */
output . length = ACPI_ALLOCATE_BUFFER ;
output . pointer = NULL ;
status = wmi_evaluate_method ( guid , 0 , 0 , & input , & output ) ;
if ( ACPI_FAILURE ( status ) ) {
kfree ( output . pointer ) ;
return - EIO ;
}
err = tlmi_extract_error ( & output ) ;
kfree ( output . pointer ) ;
if ( err )
return err ;
}
return 0 ;
}
/* Extract output string from WMI return buffer */
static int tlmi_extract_output_string ( const struct acpi_buffer * output ,
char * * string )
{
const union acpi_object * obj ;
char * s ;
obj = output - > pointer ;
if ( ! obj )
return - ENOMEM ;
if ( obj - > type ! = ACPI_TYPE_STRING | | ! obj - > string . pointer )
return - EIO ;
s = kstrdup ( obj - > string . pointer , GFP_KERNEL ) ;
if ( ! s )
return - ENOMEM ;
* string = s ;
return 0 ;
}
/* ------ Core interface functions ------------*/
/* Get password settings from BIOS */
static int tlmi_get_pwd_settings ( struct tlmi_pwdcfg * pwdcfg )
{
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
const union acpi_object * obj ;
acpi_status status ;
2021-11-17 21:44:53 +03:00
int copy_size ;
2021-05-31 01:31:11 +03:00
if ( ! tlmi_priv . can_get_password_settings )
return - EOPNOTSUPP ;
status = wmi_query_block ( LENOVO_BIOS_PASSWORD_SETTINGS_GUID , 0 ,
& output ) ;
if ( ACPI_FAILURE ( status ) )
return - EIO ;
obj = output . pointer ;
if ( ! obj )
return - ENOMEM ;
if ( obj - > type ! = ACPI_TYPE_BUFFER | | ! obj - > buffer . pointer ) {
kfree ( obj ) ;
return - EIO ;
}
/*
* The size of thinkpad_wmi_pcfg on ThinkStation is larger than ThinkPad .
* To make the driver compatible on different brands , we permit it to get
* the data in below case .
2021-11-17 21:44:53 +03:00
* Settings must have at minimum the core fields available
2021-05-31 01:31:11 +03:00
*/
2021-11-17 21:44:53 +03:00
if ( obj - > buffer . length < sizeof ( struct tlmi_pwdcfg_core ) ) {
2021-05-31 01:31:11 +03:00
pr_warn ( " Unknown pwdcfg buffer length %d \n " , obj - > buffer . length ) ;
kfree ( obj ) ;
return - EIO ;
}
2021-11-17 21:44:53 +03:00
2023-01-27 23:05:56 +03:00
copy_size = min_t ( size_t , obj - > buffer . length , sizeof ( struct tlmi_pwdcfg ) ) ;
2021-11-17 21:44:53 +03:00
memcpy ( pwdcfg , obj - > buffer . pointer , copy_size ) ;
2021-05-31 01:31:11 +03:00
kfree ( obj ) ;
2021-11-17 21:44:53 +03:00
if ( WARN_ON ( pwdcfg - > core . max_length > = TLMI_PWD_BUFSIZE ) )
pwdcfg - > core . max_length = TLMI_PWD_BUFSIZE - 1 ;
2021-05-31 01:31:11 +03:00
return 0 ;
}
static int tlmi_save_bios_settings ( const char * password )
{
return tlmi_simple_call ( LENOVO_SAVE_BIOS_SETTINGS_GUID ,
password ) ;
}
2021-11-17 21:44:53 +03:00
static int tlmi_opcode_setting ( char * setting , const char * value )
{
char * opcode_str ;
int ret ;
opcode_str = kasprintf ( GFP_KERNEL , " %s:%s; " , setting , value ) ;
if ( ! opcode_str )
return - ENOMEM ;
ret = tlmi_simple_call ( LENOVO_OPCODE_IF_GUID , opcode_str ) ;
kfree ( opcode_str ) ;
return ret ;
}
2021-05-31 01:31:11 +03:00
static int tlmi_setting ( int item , char * * value , const char * guid_string )
{
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
acpi_status status ;
int ret ;
status = wmi_query_block ( guid_string , item , & output ) ;
if ( ACPI_FAILURE ( status ) ) {
kfree ( output . pointer ) ;
return - EIO ;
}
ret = tlmi_extract_output_string ( & output , value ) ;
kfree ( output . pointer ) ;
return ret ;
}
static int tlmi_get_bios_selections ( const char * item , char * * value )
{
const struct acpi_buffer input = { strlen ( item ) , ( char * ) item } ;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
acpi_status status ;
int ret ;
status = wmi_evaluate_method ( LENOVO_GET_BIOS_SELECTIONS_GUID ,
0 , 0 , & input , & output ) ;
if ( ACPI_FAILURE ( status ) ) {
kfree ( output . pointer ) ;
return - EIO ;
}
ret = tlmi_extract_output_string ( & output , value ) ;
kfree ( output . pointer ) ;
return ret ;
}
/* ---- Authentication sysfs --------------------------------------------------------- */
static ssize_t is_enabled_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
return sysfs_emit ( buf , " %d \n " , setting - > valid ) ;
}
static struct kobj_attribute auth_is_pass_set = __ATTR_RO ( is_enabled ) ;
static ssize_t current_password_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
size_t pwdlen ;
pwdlen = strlen ( buf ) ;
/* pwdlen == 0 is allowed to clear the password */
if ( pwdlen & & ( ( pwdlen < setting - > minlen ) | | ( pwdlen > setting - > maxlen ) ) )
return - EINVAL ;
strscpy ( setting - > password , buf , setting - > maxlen ) ;
/* Strip out CR if one is present, setting password won't work if it is present */
2023-09-18 16:51:16 +03:00
strreplace ( setting - > password , ' \n ' , ' \0 ' ) ;
2021-05-31 01:31:11 +03:00
return count ;
}
static struct kobj_attribute auth_current_password = __ATTR_WO ( current_password ) ;
static ssize_t new_password_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
2022-03-18 00:40:08 +03:00
char * auth_str , * new_pwd ;
2021-05-31 01:31:11 +03:00
size_t pwdlen ;
int ret ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( ! tlmi_priv . can_set_bios_password )
return - EOPNOTSUPP ;
2023-09-18 16:51:15 +03:00
/* Strip out CR if one is present, setting password won't work if it is present */
new_pwd = kstrdup_and_replace ( buf , ' \n ' , ' \0 ' , GFP_KERNEL ) ;
2021-05-31 01:31:11 +03:00
if ( ! new_pwd )
return - ENOMEM ;
2023-06-01 23:05:45 +03:00
/* Use lock in case multiple WMI operations needed */
mutex_lock ( & tlmi_mutex ) ;
2021-05-31 01:31:11 +03:00
pwdlen = strlen ( new_pwd ) ;
/* pwdlen == 0 is allowed to clear the password */
if ( pwdlen & & ( ( pwdlen < setting - > minlen ) | | ( pwdlen > setting - > maxlen ) ) ) {
ret = - EINVAL ;
goto out ;
}
2021-11-17 21:44:53 +03:00
/* If opcode support is present use that interface */
if ( tlmi_priv . opcode_support ) {
char pwd_type [ 8 ] ;
/* Special handling required for HDD and NVMe passwords */
if ( setting = = tlmi_priv . pwd_hdd ) {
if ( setting - > level = = TLMI_LEVEL_USER )
sprintf ( pwd_type , " uhdp%d " , setting - > index ) ;
else
sprintf ( pwd_type , " mhdp%d " , setting - > index ) ;
} else if ( setting = = tlmi_priv . pwd_nvme ) {
if ( setting - > level = = TLMI_LEVEL_USER )
2023-06-01 23:05:50 +03:00
sprintf ( pwd_type , " udrp%d " , setting - > index ) ;
2021-11-17 21:44:53 +03:00
else
2023-06-01 23:05:50 +03:00
sprintf ( pwd_type , " adrp%d " , setting - > index ) ;
2021-11-17 21:44:53 +03:00
} else {
sprintf ( pwd_type , " %s " , setting - > pwd_type ) ;
}
ret = tlmi_opcode_setting ( " WmiOpcodePasswordType " , pwd_type ) ;
if ( ret )
goto out ;
if ( tlmi_priv . pwd_admin - > valid ) {
ret = tlmi_opcode_setting ( " WmiOpcodePasswordAdmin " ,
tlmi_priv . pwd_admin - > password ) ;
if ( ret )
goto out ;
}
ret = tlmi_opcode_setting ( " WmiOpcodePasswordCurrent01 " , setting - > password ) ;
if ( ret )
goto out ;
ret = tlmi_opcode_setting ( " WmiOpcodePasswordNew01 " , new_pwd ) ;
if ( ret )
goto out ;
ret = tlmi_simple_call ( LENOVO_OPCODE_IF_GUID , " WmiOpcodePasswordSetUpdate; " ) ;
} else {
/* Format: 'PasswordType,CurrentPw,NewPw,Encoding,KbdLang;' */
auth_str = kasprintf ( GFP_KERNEL , " %s,%s,%s,%s,%s; " ,
setting - > pwd_type , setting - > password , new_pwd ,
encoding_options [ setting - > encoding ] , setting - > kbdlang ) ;
if ( ! auth_str ) {
ret = - ENOMEM ;
goto out ;
}
ret = tlmi_simple_call ( LENOVO_SET_BIOS_PASSWORD_GUID , auth_str ) ;
kfree ( auth_str ) ;
2021-05-31 01:31:11 +03:00
}
out :
2023-06-01 23:05:45 +03:00
mutex_unlock ( & tlmi_mutex ) ;
2021-05-31 01:31:11 +03:00
kfree ( new_pwd ) ;
return ret ? : count ;
}
static struct kobj_attribute auth_new_password = __ATTR_WO ( new_password ) ;
static ssize_t min_password_length_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
return sysfs_emit ( buf , " %d \n " , setting - > minlen ) ;
}
static struct kobj_attribute auth_min_pass_length = __ATTR_RO ( min_password_length ) ;
static ssize_t max_password_length_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
return sysfs_emit ( buf , " %d \n " , setting - > maxlen ) ;
}
static struct kobj_attribute auth_max_pass_length = __ATTR_RO ( max_password_length ) ;
static ssize_t mechanism_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
return sysfs_emit ( buf , " password \n " ) ;
}
static struct kobj_attribute auth_mechanism = __ATTR_RO ( mechanism ) ;
static ssize_t encoding_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
return sysfs_emit ( buf , " %s \n " , encoding_options [ setting - > encoding ] ) ;
}
static ssize_t encoding_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
int i ;
/* Scan for a matching profile */
i = sysfs_match_string ( encoding_options , buf ) ;
if ( i < 0 )
return - EINVAL ;
setting - > encoding = i ;
return count ;
}
static struct kobj_attribute auth_encoding = __ATTR_RW ( encoding ) ;
static ssize_t kbdlang_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
return sysfs_emit ( buf , " %s \n " , setting - > kbdlang ) ;
}
static ssize_t kbdlang_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
int length ;
2021-06-21 22:36:48 +03:00
/* Calculate length till '\n' or terminating 0 */
length = strchrnul ( buf , ' \n ' ) - buf ;
if ( ! length | | length > = TLMI_LANG_MAXLEN )
2021-05-31 01:31:11 +03:00
return - EINVAL ;
memcpy ( setting - > kbdlang , buf , length ) ;
setting - > kbdlang [ length ] = ' \0 ' ;
return count ;
}
static struct kobj_attribute auth_kbdlang = __ATTR_RW ( kbdlang ) ;
static ssize_t role_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
return sysfs_emit ( buf , " %s \n " , setting - > role ) ;
}
static struct kobj_attribute auth_role = __ATTR_RO ( role ) ;
2021-11-17 21:44:53 +03:00
static ssize_t index_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
return sysfs_emit ( buf , " %d \n " , setting - > index ) ;
}
static ssize_t index_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
int err , val ;
err = kstrtoint ( buf , 10 , & val ) ;
if ( err < 0 )
return err ;
2021-12-17 10:12:09 +03:00
if ( val < 0 | | val > TLMI_INDEX_MAX )
2021-11-17 21:44:53 +03:00
return - EINVAL ;
setting - > index = val ;
return count ;
}
static struct kobj_attribute auth_index = __ATTR_RW ( index ) ;
static ssize_t level_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
return sysfs_emit ( buf , " %s \n " , level_options [ setting - > level ] ) ;
}
static ssize_t level_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
int i ;
/* Scan for a matching profile */
i = sysfs_match_string ( level_options , buf ) ;
if ( i < 0 )
return - EINVAL ;
setting - > level = i ;
return count ;
}
static struct kobj_attribute auth_level = __ATTR_RW ( level ) ;
2022-03-18 00:40:08 +03:00
static ssize_t cert_thumbprint ( char * buf , const char * arg , int count )
{
const struct acpi_buffer input = { strlen ( arg ) , ( char * ) arg } ;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER , NULL } ;
const union acpi_object * obj ;
acpi_status status ;
status = wmi_evaluate_method ( LENOVO_CERT_THUMBPRINT_GUID , 0 , 0 , & input , & output ) ;
if ( ACPI_FAILURE ( status ) ) {
kfree ( output . pointer ) ;
return - EIO ;
}
obj = output . pointer ;
if ( ! obj )
return - ENOMEM ;
if ( obj - > type ! = ACPI_TYPE_STRING | | ! obj - > string . pointer ) {
kfree ( output . pointer ) ;
return - EIO ;
}
count + = sysfs_emit_at ( buf , count , " %s : %s \n " , arg , ( char * ) obj - > string . pointer ) ;
kfree ( output . pointer ) ;
return count ;
}
static ssize_t certificate_thumbprint_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
int count = 0 ;
if ( ! tlmi_priv . certificate_support | | ! setting - > cert_installed )
return - EOPNOTSUPP ;
count + = cert_thumbprint ( buf , " Md5 " , count ) ;
count + = cert_thumbprint ( buf , " Sha1 " , count ) ;
count + = cert_thumbprint ( buf , " Sha256 " , count ) ;
return count ;
}
static struct kobj_attribute auth_cert_thumb = __ATTR_RO ( certificate_thumbprint ) ;
static ssize_t cert_to_password_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
char * auth_str , * passwd ;
int ret ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( ! tlmi_priv . certificate_support )
return - EOPNOTSUPP ;
if ( ! setting - > cert_installed )
return - EINVAL ;
if ( ! setting - > signature | | ! setting - > signature [ 0 ] )
return - EACCES ;
2023-09-18 16:51:15 +03:00
/* Strip out CR if one is present */
passwd = kstrdup_and_replace ( buf , ' \n ' , ' \0 ' , GFP_KERNEL ) ;
2022-03-18 00:40:08 +03:00
if ( ! passwd )
return - ENOMEM ;
/* Format: 'Password,Signature' */
auth_str = kasprintf ( GFP_KERNEL , " %s,%s " , passwd , setting - > signature ) ;
if ( ! auth_str ) {
2023-07-17 13:11:02 +03:00
kfree_sensitive ( passwd ) ;
2022-03-18 00:40:08 +03:00
return - ENOMEM ;
}
ret = tlmi_simple_call ( LENOVO_CERT_TO_PASSWORD_GUID , auth_str ) ;
kfree ( auth_str ) ;
2023-07-17 13:11:02 +03:00
kfree_sensitive ( passwd ) ;
2022-03-18 00:40:08 +03:00
return ret ? : count ;
}
static struct kobj_attribute auth_cert_to_password = __ATTR_WO ( cert_to_password ) ;
static ssize_t certificate_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
char * auth_str , * new_cert ;
char * guid ;
int ret ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( ! tlmi_priv . certificate_support )
return - EOPNOTSUPP ;
/* If empty then clear installed certificate */
2022-03-21 21:06:24 +03:00
if ( ( buf [ 0 ] = = ' \0 ' ) | | ( buf [ 0 ] = = ' \n ' ) ) { /* Clear installed certificate */
2022-03-18 00:40:08 +03:00
/* Check that signature is set */
if ( ! setting - > signature | | ! setting - > signature [ 0 ] )
return - EACCES ;
/* Format: 'serial#, signature' */
auth_str = kasprintf ( GFP_KERNEL , " %s,%s " ,
dmi_get_system_info ( DMI_PRODUCT_SERIAL ) ,
setting - > signature ) ;
if ( ! auth_str )
return - ENOMEM ;
ret = tlmi_simple_call ( LENOVO_CLEAR_BIOS_CERT_GUID , auth_str ) ;
kfree ( auth_str ) ;
2022-03-21 21:06:24 +03:00
return ret ? : count ;
2022-03-18 00:40:08 +03:00
}
2023-09-18 16:51:15 +03:00
/* Strip out CR if one is present */
new_cert = kstrdup_and_replace ( buf , ' \n ' , ' \0 ' , GFP_KERNEL ) ;
2022-03-21 21:06:24 +03:00
if ( ! new_cert )
return - ENOMEM ;
2022-03-18 00:40:08 +03:00
if ( setting - > cert_installed ) {
/* Certificate is installed so this is an update */
if ( ! setting - > signature | | ! setting - > signature [ 0 ] ) {
kfree ( new_cert ) ;
return - EACCES ;
}
guid = LENOVO_UPDATE_BIOS_CERT_GUID ;
/* Format: 'Certificate,Signature' */
auth_str = kasprintf ( GFP_KERNEL , " %s,%s " ,
new_cert , setting - > signature ) ;
} else {
/* This is a fresh install */
if ( ! setting - > valid | | ! setting - > password [ 0 ] ) {
kfree ( new_cert ) ;
return - EACCES ;
}
guid = LENOVO_SET_BIOS_CERT_GUID ;
/* Format: 'Certificate,Admin-password' */
auth_str = kasprintf ( GFP_KERNEL , " %s,%s " ,
new_cert , setting - > password ) ;
}
2022-03-21 21:06:24 +03:00
kfree ( new_cert ) ;
if ( ! auth_str )
2022-03-18 00:40:08 +03:00
return - ENOMEM ;
ret = tlmi_simple_call ( guid , auth_str ) ;
kfree ( auth_str ) ;
2022-03-21 21:06:24 +03:00
return ret ? : count ;
2022-03-18 00:40:08 +03:00
}
static struct kobj_attribute auth_certificate = __ATTR_WO ( certificate ) ;
static ssize_t signature_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
char * new_signature ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( ! tlmi_priv . certificate_support )
return - EOPNOTSUPP ;
2023-09-18 16:51:15 +03:00
/* Strip out CR if one is present */
new_signature = kstrdup_and_replace ( buf , ' \n ' , ' \0 ' , GFP_KERNEL ) ;
2022-03-18 00:40:08 +03:00
if ( ! new_signature )
return - ENOMEM ;
/* Free any previous signature */
kfree ( setting - > signature ) ;
setting - > signature = new_signature ;
return count ;
}
static struct kobj_attribute auth_signature = __ATTR_WO ( signature ) ;
static ssize_t save_signature_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
char * new_signature ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( ! tlmi_priv . certificate_support )
return - EOPNOTSUPP ;
2023-09-18 16:51:15 +03:00
/* Strip out CR if one is present */
new_signature = kstrdup_and_replace ( buf , ' \n ' , ' \0 ' , GFP_KERNEL ) ;
2022-03-18 00:40:08 +03:00
if ( ! new_signature )
return - ENOMEM ;
/* Free any previous signature */
kfree ( setting - > save_signature ) ;
setting - > save_signature = new_signature ;
return count ;
}
static struct kobj_attribute auth_save_signature = __ATTR_WO ( save_signature ) ;
2021-11-17 21:44:53 +03:00
static umode_t auth_attr_is_visible ( struct kobject * kobj ,
struct attribute * attr , int n )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
2022-03-18 00:40:08 +03:00
/* We only want to display level and index settings on HDD/NVMe */
2023-03-29 07:50:25 +03:00
if ( attr = = & auth_index . attr | | attr = = & auth_level . attr ) {
2021-11-17 21:44:53 +03:00
if ( ( setting = = tlmi_priv . pwd_hdd ) | | ( setting = = tlmi_priv . pwd_nvme ) )
return attr - > mode ;
return 0 ;
}
2022-03-18 00:40:08 +03:00
/* We only display certificates on Admin account, if supported */
2023-03-29 07:50:25 +03:00
if ( attr = = & auth_certificate . attr | |
attr = = & auth_signature . attr | |
attr = = & auth_save_signature . attr | |
attr = = & auth_cert_thumb . attr | |
attr = = & auth_cert_to_password . attr ) {
2022-03-18 00:40:08 +03:00
if ( ( setting = = tlmi_priv . pwd_admin ) & & tlmi_priv . certificate_support )
return attr - > mode ;
return 0 ;
}
2023-06-01 23:05:52 +03:00
/* Don't display un-needed settings if opcode available */
if ( ( attr = = & auth_encoding . attr | | attr = = & auth_kbdlang . attr ) & &
tlmi_priv . opcode_support )
return 0 ;
2021-11-17 21:44:53 +03:00
return attr - > mode ;
}
2021-05-31 01:31:11 +03:00
static struct attribute * auth_attrs [ ] = {
& auth_is_pass_set . attr ,
& auth_min_pass_length . attr ,
& auth_max_pass_length . attr ,
& auth_current_password . attr ,
& auth_new_password . attr ,
& auth_role . attr ,
& auth_mechanism . attr ,
& auth_encoding . attr ,
& auth_kbdlang . attr ,
2021-11-17 21:44:53 +03:00
& auth_index . attr ,
& auth_level . attr ,
2022-03-18 00:40:08 +03:00
& auth_certificate . attr ,
& auth_signature . attr ,
& auth_save_signature . attr ,
& auth_cert_thumb . attr ,
& auth_cert_to_password . attr ,
2021-05-31 01:31:11 +03:00
NULL
} ;
static const struct attribute_group auth_attr_group = {
2021-11-17 21:44:53 +03:00
. is_visible = auth_attr_is_visible ,
2021-05-31 01:31:11 +03:00
. attrs = auth_attrs ,
} ;
/* ---- Attributes sysfs --------------------------------------------------------- */
static ssize_t display_name_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct tlmi_attr_setting * setting = to_tlmi_attr_setting ( kobj ) ;
return sysfs_emit ( buf , " %s \n " , setting - > display_name ) ;
}
static ssize_t current_value_show ( struct kobject * kobj , struct kobj_attribute * attr , char * buf )
{
struct tlmi_attr_setting * setting = to_tlmi_attr_setting ( kobj ) ;
2023-09-18 16:51:16 +03:00
char * item , * value ;
2021-05-31 01:31:11 +03:00
int ret ;
ret = tlmi_setting ( setting - > index , & item , LENOVO_BIOS_SETTING_GUID ) ;
if ( ret )
return ret ;
2021-06-22 23:07:55 +03:00
/* validate and split from `item,value` -> `value` */
value = strpbrk ( item , " , " ) ;
if ( ! value | | value = = item | | ! strlen ( value + 1 ) )
2023-04-01 00:33:19 +03:00
ret = - EINVAL ;
2023-04-03 04:31:20 +03:00
else {
/* On Workstations remove the Options part after the value */
2023-09-18 16:51:16 +03:00
strreplace ( value , ' ; ' , ' \0 ' ) ;
2023-04-01 00:33:19 +03:00
ret = sysfs_emit ( buf , " %s \n " , value + 1 ) ;
2023-04-03 04:31:20 +03:00
}
2021-05-31 01:31:11 +03:00
kfree ( item ) ;
2023-04-01 00:33:19 +03:00
2021-05-31 01:31:11 +03:00
return ret ;
}
static ssize_t possible_values_show ( struct kobject * kobj , struct kobj_attribute * attr , char * buf )
{
struct tlmi_attr_setting * setting = to_tlmi_attr_setting ( kobj ) ;
return sysfs_emit ( buf , " %s \n " , setting - > possible_values ) ;
}
2023-03-20 03:32:18 +03:00
static ssize_t type_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
struct tlmi_attr_setting * setting = to_tlmi_attr_setting ( kobj ) ;
if ( setting - > possible_values ) {
/* Figure out what setting type is as BIOS does not return this */
2023-03-20 03:32:19 +03:00
if ( strchr ( setting - > possible_values , ' ; ' ) )
2023-03-20 03:32:18 +03:00
return sysfs_emit ( buf , " enumeration \n " ) ;
}
/* Anything else is going to be a string */
return sysfs_emit ( buf , " string \n " ) ;
}
2021-05-31 01:31:11 +03:00
static ssize_t current_value_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct tlmi_attr_setting * setting = to_tlmi_attr_setting ( kobj ) ;
char * set_str = NULL , * new_setting = NULL ;
char * auth_str = NULL ;
int ret ;
if ( ! tlmi_priv . can_set_bios_settings )
return - EOPNOTSUPP ;
2023-09-19 17:15:26 +03:00
/*
* If we are using bulk saves a reboot should be done once save has
* been called
*/
if ( tlmi_priv . save_mode = = TLMI_SAVE_BULK & & tlmi_priv . reboot_required )
return - EPERM ;
2021-05-31 01:31:11 +03:00
/* Strip out CR if one is present */
2023-09-18 16:51:15 +03:00
new_setting = kstrdup_and_replace ( buf , ' \n ' , ' \0 ' , GFP_KERNEL ) ;
2021-05-31 01:31:11 +03:00
if ( ! new_setting )
return - ENOMEM ;
2023-06-01 23:05:45 +03:00
/* Use lock in case multiple WMI operations needed */
mutex_lock ( & tlmi_mutex ) ;
2022-03-18 00:40:08 +03:00
/* Check if certificate authentication is enabled and active */
if ( tlmi_priv . certificate_support & & tlmi_priv . pwd_admin - > cert_installed ) {
if ( ! tlmi_priv . pwd_admin - > signature | | ! tlmi_priv . pwd_admin - > save_signature ) {
ret = - EINVAL ;
goto out ;
}
set_str = kasprintf ( GFP_KERNEL , " %s,%s,%s " , setting - > display_name ,
new_setting , tlmi_priv . pwd_admin - > signature ) ;
if ( ! set_str ) {
2021-05-31 01:31:11 +03:00
ret = - ENOMEM ;
goto out ;
}
2022-03-18 00:40:08 +03:00
ret = tlmi_simple_call ( LENOVO_SET_BIOS_SETTING_CERT_GUID , set_str ) ;
if ( ret )
goto out ;
2023-09-19 17:15:26 +03:00
if ( tlmi_priv . save_mode = = TLMI_SAVE_BULK )
tlmi_priv . save_required = true ;
else
ret = tlmi_simple_call ( LENOVO_SAVE_BIOS_SETTING_CERT_GUID ,
tlmi_priv . pwd_admin - > save_signature ) ;
2023-06-01 23:05:46 +03:00
} else if ( tlmi_priv . opcode_support ) {
/*
* If opcode support is present use that interface .
* Note - this sets the variable and then the password as separate
* WMI calls . Function tlmi_save_bios_settings will error if the
* password is incorrect .
*/
set_str = kasprintf ( GFP_KERNEL , " %s,%s; " , setting - > display_name ,
new_setting ) ;
if ( ! set_str ) {
ret = - ENOMEM ;
goto out ;
}
ret = tlmi_simple_call ( LENOVO_SET_BIOS_SETTINGS_GUID , set_str ) ;
if ( ret )
goto out ;
2023-09-19 17:15:26 +03:00
if ( tlmi_priv . save_mode = = TLMI_SAVE_BULK ) {
tlmi_priv . save_required = true ;
} else {
if ( tlmi_priv . pwd_admin - > valid & & tlmi_priv . pwd_admin - > password [ 0 ] ) {
ret = tlmi_opcode_setting ( " WmiOpcodePasswordAdmin " ,
tlmi_priv . pwd_admin - > password ) ;
if ( ret )
goto out ;
}
ret = tlmi_save_bios_settings ( " " ) ;
2023-06-01 23:05:46 +03:00
}
} else { /* old non-opcode based authentication method (deprecated) */
2022-03-18 00:40:08 +03:00
if ( tlmi_priv . pwd_admin - > valid & & tlmi_priv . pwd_admin - > password [ 0 ] ) {
auth_str = kasprintf ( GFP_KERNEL , " %s,%s,%s; " ,
tlmi_priv . pwd_admin - > password ,
encoding_options [ tlmi_priv . pwd_admin - > encoding ] ,
tlmi_priv . pwd_admin - > kbdlang ) ;
if ( ! auth_str ) {
ret = - ENOMEM ;
goto out ;
}
}
2021-05-31 01:31:11 +03:00
2022-03-18 00:40:08 +03:00
if ( auth_str )
set_str = kasprintf ( GFP_KERNEL , " %s,%s,%s " , setting - > display_name ,
new_setting , auth_str ) ;
else
set_str = kasprintf ( GFP_KERNEL , " %s,%s; " , setting - > display_name ,
new_setting ) ;
if ( ! set_str ) {
ret = - ENOMEM ;
goto out ;
}
2021-05-31 01:31:11 +03:00
2022-03-18 00:40:08 +03:00
ret = tlmi_simple_call ( LENOVO_SET_BIOS_SETTINGS_GUID , set_str ) ;
if ( ret )
goto out ;
2021-05-31 01:31:11 +03:00
2023-09-19 17:15:26 +03:00
if ( tlmi_priv . save_mode = = TLMI_SAVE_BULK ) {
tlmi_priv . save_required = true ;
} else {
if ( auth_str )
ret = tlmi_save_bios_settings ( auth_str ) ;
else
ret = tlmi_save_bios_settings ( " " ) ;
}
2022-03-18 00:40:08 +03:00
}
2021-06-29 01:28:46 +03:00
if ( ! ret & & ! tlmi_priv . pending_changes ) {
tlmi_priv . pending_changes = true ;
/* let userland know it may need to check reboot pending again */
kobject_uevent ( & tlmi_priv . class_dev - > kobj , KOBJ_CHANGE ) ;
}
2021-05-31 01:31:11 +03:00
out :
2023-06-01 23:05:45 +03:00
mutex_unlock ( & tlmi_mutex ) ;
2021-05-31 01:31:11 +03:00
kfree ( auth_str ) ;
kfree ( set_str ) ;
kfree ( new_setting ) ;
return ret ? : count ;
}
static struct kobj_attribute attr_displ_name = __ATTR_RO ( display_name ) ;
static struct kobj_attribute attr_possible_values = __ATTR_RO ( possible_values ) ;
static struct kobj_attribute attr_current_val = __ATTR_RW_MODE ( current_value , 0600 ) ;
2023-03-20 03:32:18 +03:00
static struct kobj_attribute attr_type = __ATTR_RO ( type ) ;
2023-03-20 03:32:20 +03:00
static umode_t attr_is_visible ( struct kobject * kobj ,
struct attribute * attr , int n )
{
struct tlmi_attr_setting * setting = to_tlmi_attr_setting ( kobj ) ;
/* We don't want to display possible_values attributes if not available */
if ( ( attr = = & attr_possible_values . attr ) & & ( ! setting - > possible_values ) )
return 0 ;
return attr - > mode ;
}
2021-05-31 01:31:11 +03:00
static struct attribute * tlmi_attrs [ ] = {
& attr_displ_name . attr ,
& attr_current_val . attr ,
& attr_possible_values . attr ,
2023-03-20 03:32:18 +03:00
& attr_type . attr ,
2021-05-31 01:31:11 +03:00
NULL
} ;
static const struct attribute_group tlmi_attr_group = {
2023-03-20 03:32:20 +03:00
. is_visible = attr_is_visible ,
2021-05-31 01:31:11 +03:00
. attrs = tlmi_attrs ,
} ;
static void tlmi_attr_setting_release ( struct kobject * kobj )
{
struct tlmi_attr_setting * setting = to_tlmi_attr_setting ( kobj ) ;
2021-06-28 12:36:00 +03:00
kfree ( setting - > possible_values ) ;
2021-05-31 01:31:11 +03:00
kfree ( setting ) ;
}
static void tlmi_pwd_setting_release ( struct kobject * kobj )
{
struct tlmi_pwd_setting * setting = to_tlmi_pwd_setting ( kobj ) ;
kfree ( setting ) ;
}
2023-02-07 19:40:45 +03:00
static const struct kobj_type tlmi_attr_setting_ktype = {
2021-05-31 01:31:11 +03:00
. release = & tlmi_attr_setting_release ,
2023-03-14 03:07:52 +03:00
. sysfs_ops = & kobj_sysfs_ops ,
2021-05-31 01:31:11 +03:00
} ;
2023-02-07 19:40:45 +03:00
static const struct kobj_type tlmi_pwd_setting_ktype = {
2021-05-31 01:31:11 +03:00
. release = & tlmi_pwd_setting_release ,
2023-03-14 03:07:52 +03:00
. sysfs_ops = & kobj_sysfs_ops ,
2021-05-31 01:31:11 +03:00
} ;
2021-06-29 01:28:46 +03:00
static ssize_t pending_reboot_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
return sprintf ( buf , " %d \n " , tlmi_priv . pending_changes ) ;
}
static struct kobj_attribute pending_reboot = __ATTR_RO ( pending_reboot ) ;
2023-09-19 17:15:26 +03:00
static const char * const save_mode_strings [ ] = {
[ TLMI_SAVE_SINGLE ] = " single " ,
[ TLMI_SAVE_BULK ] = " bulk " ,
[ TLMI_SAVE_SAVE ] = " save "
} ;
static ssize_t save_settings_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * buf )
{
/* Check that setting is valid */
if ( WARN_ON ( tlmi_priv . save_mode < TLMI_SAVE_SINGLE | |
tlmi_priv . save_mode > TLMI_SAVE_BULK ) )
return - EIO ;
return sysfs_emit ( buf , " %s \n " , save_mode_strings [ tlmi_priv . save_mode ] ) ;
}
static ssize_t save_settings_store ( struct kobject * kobj , struct kobj_attribute * attr ,
const char * buf , size_t count )
{
char * auth_str = NULL ;
int ret = 0 ;
int cmd ;
cmd = sysfs_match_string ( save_mode_strings , buf ) ;
if ( cmd < 0 )
return cmd ;
/* Use lock in case multiple WMI operations needed */
mutex_lock ( & tlmi_mutex ) ;
switch ( cmd ) {
case TLMI_SAVE_SINGLE :
case TLMI_SAVE_BULK :
tlmi_priv . save_mode = cmd ;
goto out ;
case TLMI_SAVE_SAVE :
/* Check if supported*/
if ( ! tlmi_priv . can_set_bios_settings | |
tlmi_priv . save_mode = = TLMI_SAVE_SINGLE ) {
ret = - EOPNOTSUPP ;
goto out ;
}
/* Check there is actually something to save */
if ( ! tlmi_priv . save_required ) {
ret = - ENOENT ;
goto out ;
}
/* Check if certificate authentication is enabled and active */
if ( tlmi_priv . certificate_support & & tlmi_priv . pwd_admin - > cert_installed ) {
if ( ! tlmi_priv . pwd_admin - > signature | |
! tlmi_priv . pwd_admin - > save_signature ) {
ret = - EINVAL ;
goto out ;
}
ret = tlmi_simple_call ( LENOVO_SAVE_BIOS_SETTING_CERT_GUID ,
tlmi_priv . pwd_admin - > save_signature ) ;
if ( ret )
goto out ;
} else if ( tlmi_priv . opcode_support ) {
if ( tlmi_priv . pwd_admin - > valid & & tlmi_priv . pwd_admin - > password [ 0 ] ) {
ret = tlmi_opcode_setting ( " WmiOpcodePasswordAdmin " ,
tlmi_priv . pwd_admin - > password ) ;
if ( ret )
goto out ;
}
ret = tlmi_save_bios_settings ( " " ) ;
} else { /* old non-opcode based authentication method (deprecated) */
if ( tlmi_priv . pwd_admin - > valid & & tlmi_priv . pwd_admin - > password [ 0 ] ) {
auth_str = kasprintf ( GFP_KERNEL , " %s,%s,%s; " ,
tlmi_priv . pwd_admin - > password ,
encoding_options [ tlmi_priv . pwd_admin - > encoding ] ,
tlmi_priv . pwd_admin - > kbdlang ) ;
if ( ! auth_str ) {
ret = - ENOMEM ;
goto out ;
}
}
if ( auth_str )
ret = tlmi_save_bios_settings ( auth_str ) ;
else
ret = tlmi_save_bios_settings ( " " ) ;
}
tlmi_priv . save_required = false ;
tlmi_priv . reboot_required = true ;
if ( ! ret & & ! tlmi_priv . pending_changes ) {
tlmi_priv . pending_changes = true ;
/* let userland know it may need to check reboot pending again */
kobject_uevent ( & tlmi_priv . class_dev - > kobj , KOBJ_CHANGE ) ;
}
break ;
}
out :
mutex_unlock ( & tlmi_mutex ) ;
kfree ( auth_str ) ;
return ret ? : count ;
}
static struct kobj_attribute save_settings = __ATTR_RW ( save_settings ) ;
2021-08-17 03:15:01 +03:00
/* ---- Debug interface--------------------------------------------------------- */
static ssize_t debug_cmd_store ( struct kobject * kobj , struct kobj_attribute * attr ,
const char * buf , size_t count )
{
char * set_str = NULL , * new_setting = NULL ;
char * auth_str = NULL ;
int ret ;
if ( ! tlmi_priv . can_debug_cmd )
return - EOPNOTSUPP ;
2023-09-18 16:51:15 +03:00
/* Strip out CR if one is present */
new_setting = kstrdup_and_replace ( buf , ' \n ' , ' \0 ' , GFP_KERNEL ) ;
2021-08-17 03:15:01 +03:00
if ( ! new_setting )
return - ENOMEM ;
if ( tlmi_priv . pwd_admin - > valid & & tlmi_priv . pwd_admin - > password [ 0 ] ) {
auth_str = kasprintf ( GFP_KERNEL , " %s,%s,%s; " ,
tlmi_priv . pwd_admin - > password ,
encoding_options [ tlmi_priv . pwd_admin - > encoding ] ,
tlmi_priv . pwd_admin - > kbdlang ) ;
if ( ! auth_str ) {
ret = - ENOMEM ;
goto out ;
}
}
if ( auth_str )
set_str = kasprintf ( GFP_KERNEL , " %s,%s " , new_setting , auth_str ) ;
else
set_str = kasprintf ( GFP_KERNEL , " %s; " , new_setting ) ;
if ( ! set_str ) {
ret = - ENOMEM ;
goto out ;
}
ret = tlmi_simple_call ( LENOVO_DEBUG_CMD_GUID , set_str ) ;
if ( ret )
goto out ;
if ( ! ret & & ! tlmi_priv . pending_changes ) {
tlmi_priv . pending_changes = true ;
/* let userland know it may need to check reboot pending again */
kobject_uevent ( & tlmi_priv . class_dev - > kobj , KOBJ_CHANGE ) ;
}
out :
kfree ( auth_str ) ;
kfree ( set_str ) ;
kfree ( new_setting ) ;
return ret ? : count ;
}
static struct kobj_attribute debug_cmd = __ATTR_WO ( debug_cmd ) ;
2021-05-31 01:31:11 +03:00
/* ---- Initialisation --------------------------------------------------------- */
static void tlmi_release_attr ( void )
{
int i ;
/* Attribute structures */
for ( i = 0 ; i < TLMI_SETTINGS_COUNT ; i + + ) {
if ( tlmi_priv . setting [ i ] ) {
sysfs_remove_group ( & tlmi_priv . setting [ i ] - > kobj , & tlmi_attr_group ) ;
kobject_put ( & tlmi_priv . setting [ i ] - > kobj ) ;
}
}
2021-07-17 17:36:05 +03:00
sysfs_remove_file ( & tlmi_priv . attribute_kset - > kobj , & pending_reboot . attr ) ;
2023-09-19 17:15:26 +03:00
sysfs_remove_file ( & tlmi_priv . attribute_kset - > kobj , & save_settings . attr ) ;
2021-08-17 03:15:01 +03:00
if ( tlmi_priv . can_debug_cmd & & debug_support )
sysfs_remove_file ( & tlmi_priv . attribute_kset - > kobj , & debug_cmd . attr ) ;
2022-03-18 00:40:08 +03:00
2021-05-31 01:31:11 +03:00
kset_unregister ( tlmi_priv . attribute_kset ) ;
2022-03-21 21:06:24 +03:00
/* Free up any saved signatures */
kfree ( tlmi_priv . pwd_admin - > signature ) ;
kfree ( tlmi_priv . pwd_admin - > save_signature ) ;
2021-05-31 01:31:11 +03:00
/* Authentication structures */
sysfs_remove_group ( & tlmi_priv . pwd_admin - > kobj , & auth_attr_group ) ;
kobject_put ( & tlmi_priv . pwd_admin - > kobj ) ;
sysfs_remove_group ( & tlmi_priv . pwd_power - > kobj , & auth_attr_group ) ;
kobject_put ( & tlmi_priv . pwd_power - > kobj ) ;
2021-11-17 21:44:53 +03:00
if ( tlmi_priv . opcode_support ) {
sysfs_remove_group ( & tlmi_priv . pwd_system - > kobj , & auth_attr_group ) ;
kobject_put ( & tlmi_priv . pwd_system - > kobj ) ;
sysfs_remove_group ( & tlmi_priv . pwd_hdd - > kobj , & auth_attr_group ) ;
kobject_put ( & tlmi_priv . pwd_hdd - > kobj ) ;
sysfs_remove_group ( & tlmi_priv . pwd_nvme - > kobj , & auth_attr_group ) ;
kobject_put ( & tlmi_priv . pwd_nvme - > kobj ) ;
}
2021-05-31 01:31:11 +03:00
kset_unregister ( tlmi_priv . authentication_kset ) ;
}
2023-09-25 17:28:18 +03:00
static int tlmi_validate_setting_name ( struct kset * attribute_kset , char * name )
{
struct kobject * duplicate ;
if ( ! strcmp ( name , " Reserved " ) )
return - EINVAL ;
duplicate = kset_find_obj ( attribute_kset , name ) ;
if ( duplicate ) {
pr_debug ( " Duplicate attribute name found - %s \n " , name ) ;
/* kset_find_obj() returns a reference */
kobject_put ( duplicate ) ;
return - EBUSY ;
}
return 0 ;
}
2021-05-31 01:31:11 +03:00
static int tlmi_sysfs_init ( void )
{
int i , ret ;
ret = fw_attributes_class_get ( & fw_attr_class ) ;
if ( ret )
return ret ;
tlmi_priv . class_dev = device_create ( fw_attr_class , NULL , MKDEV ( 0 , 0 ) ,
NULL , " %s " , " thinklmi " ) ;
if ( IS_ERR ( tlmi_priv . class_dev ) ) {
ret = PTR_ERR ( tlmi_priv . class_dev ) ;
goto fail_class_created ;
}
tlmi_priv . attribute_kset = kset_create_and_add ( " attributes " , NULL ,
& tlmi_priv . class_dev - > kobj ) ;
if ( ! tlmi_priv . attribute_kset ) {
ret = - ENOMEM ;
goto fail_device_created ;
}
for ( i = 0 ; i < TLMI_SETTINGS_COUNT ; i + + ) {
/* Check if index is a valid setting - skip if it isn't */
if ( ! tlmi_priv . setting [ i ] )
continue ;
2021-06-22 23:07:54 +03:00
/* check for duplicate or reserved values */
2023-09-25 17:28:18 +03:00
if ( tlmi_validate_setting_name ( tlmi_priv . attribute_kset ,
tlmi_priv . setting [ i ] - > display_name ) < 0 ) {
2021-06-22 23:07:54 +03:00
kfree ( tlmi_priv . setting [ i ] - > possible_values ) ;
kfree ( tlmi_priv . setting [ i ] ) ;
tlmi_priv . setting [ i ] = NULL ;
continue ;
}
2021-05-31 01:31:11 +03:00
/* Build attribute */
tlmi_priv . setting [ i ] - > kobj . kset = tlmi_priv . attribute_kset ;
2021-07-17 17:36:06 +03:00
ret = kobject_add ( & tlmi_priv . setting [ i ] - > kobj , NULL ,
" %s " , tlmi_priv . setting [ i ] - > display_name ) ;
2021-05-31 01:31:11 +03:00
if ( ret )
goto fail_create_attr ;
ret = sysfs_create_group ( & tlmi_priv . setting [ i ] - > kobj , & tlmi_attr_group ) ;
if ( ret )
goto fail_create_attr ;
}
2021-07-17 17:36:05 +03:00
ret = sysfs_create_file ( & tlmi_priv . attribute_kset - > kobj , & pending_reboot . attr ) ;
if ( ret )
goto fail_create_attr ;
2023-09-19 17:15:26 +03:00
ret = sysfs_create_file ( & tlmi_priv . attribute_kset - > kobj , & save_settings . attr ) ;
if ( ret )
goto fail_create_attr ;
2021-08-17 03:15:01 +03:00
if ( tlmi_priv . can_debug_cmd & & debug_support ) {
ret = sysfs_create_file ( & tlmi_priv . attribute_kset - > kobj , & debug_cmd . attr ) ;
if ( ret )
goto fail_create_attr ;
}
2022-03-18 00:40:08 +03:00
2021-05-31 01:31:11 +03:00
/* Create authentication entries */
tlmi_priv . authentication_kset = kset_create_and_add ( " authentication " , NULL ,
& tlmi_priv . class_dev - > kobj ) ;
if ( ! tlmi_priv . authentication_kset ) {
ret = - ENOMEM ;
goto fail_create_attr ;
}
tlmi_priv . pwd_admin - > kobj . kset = tlmi_priv . authentication_kset ;
2021-07-17 17:36:06 +03:00
ret = kobject_add ( & tlmi_priv . pwd_admin - > kobj , NULL , " %s " , " Admin " ) ;
2021-05-31 01:31:11 +03:00
if ( ret )
goto fail_create_attr ;
ret = sysfs_create_group ( & tlmi_priv . pwd_admin - > kobj , & auth_attr_group ) ;
if ( ret )
goto fail_create_attr ;
tlmi_priv . pwd_power - > kobj . kset = tlmi_priv . authentication_kset ;
2021-11-17 21:44:53 +03:00
ret = kobject_add ( & tlmi_priv . pwd_power - > kobj , NULL , " %s " , " Power-on " ) ;
2021-05-31 01:31:11 +03:00
if ( ret )
goto fail_create_attr ;
ret = sysfs_create_group ( & tlmi_priv . pwd_power - > kobj , & auth_attr_group ) ;
if ( ret )
goto fail_create_attr ;
2021-11-17 21:44:53 +03:00
if ( tlmi_priv . opcode_support ) {
tlmi_priv . pwd_system - > kobj . kset = tlmi_priv . authentication_kset ;
ret = kobject_add ( & tlmi_priv . pwd_system - > kobj , NULL , " %s " , " System " ) ;
if ( ret )
goto fail_create_attr ;
ret = sysfs_create_group ( & tlmi_priv . pwd_system - > kobj , & auth_attr_group ) ;
if ( ret )
goto fail_create_attr ;
tlmi_priv . pwd_hdd - > kobj . kset = tlmi_priv . authentication_kset ;
ret = kobject_add ( & tlmi_priv . pwd_hdd - > kobj , NULL , " %s " , " HDD " ) ;
if ( ret )
goto fail_create_attr ;
ret = sysfs_create_group ( & tlmi_priv . pwd_hdd - > kobj , & auth_attr_group ) ;
if ( ret )
goto fail_create_attr ;
tlmi_priv . pwd_nvme - > kobj . kset = tlmi_priv . authentication_kset ;
ret = kobject_add ( & tlmi_priv . pwd_nvme - > kobj , NULL , " %s " , " NVMe " ) ;
if ( ret )
goto fail_create_attr ;
ret = sysfs_create_group ( & tlmi_priv . pwd_nvme - > kobj , & auth_attr_group ) ;
if ( ret )
goto fail_create_attr ;
}
2021-05-31 01:31:11 +03:00
return ret ;
fail_create_attr :
tlmi_release_attr ( ) ;
fail_device_created :
device_destroy ( fw_attr_class , MKDEV ( 0 , 0 ) ) ;
fail_class_created :
fw_attributes_class_put ( ) ;
return ret ;
}
/* ---- Base Driver -------------------------------------------------------- */
2021-11-17 21:44:53 +03:00
static struct tlmi_pwd_setting * tlmi_create_auth ( const char * pwd_type ,
const char * pwd_role )
{
struct tlmi_pwd_setting * new_pwd ;
new_pwd = kzalloc ( sizeof ( struct tlmi_pwd_setting ) , GFP_KERNEL ) ;
if ( ! new_pwd )
return NULL ;
strscpy ( new_pwd - > kbdlang , " us " , TLMI_LANG_MAXLEN ) ;
new_pwd - > encoding = TLMI_ENCODING_ASCII ;
new_pwd - > pwd_type = pwd_type ;
new_pwd - > role = pwd_role ;
new_pwd - > minlen = tlmi_priv . pwdcfg . core . min_length ;
new_pwd - > maxlen = tlmi_priv . pwdcfg . core . max_length ;
new_pwd - > index = 0 ;
2021-11-18 14:41:49 +03:00
kobject_init ( & new_pwd - > kobj , & tlmi_pwd_setting_ktype ) ;
2021-11-17 21:44:53 +03:00
return new_pwd ;
}
2021-05-31 01:31:11 +03:00
static int tlmi_analyze ( void )
{
int i , ret ;
if ( wmi_has_guid ( LENOVO_SET_BIOS_SETTINGS_GUID ) & &
wmi_has_guid ( LENOVO_SAVE_BIOS_SETTINGS_GUID ) )
tlmi_priv . can_set_bios_settings = true ;
if ( wmi_has_guid ( LENOVO_GET_BIOS_SELECTIONS_GUID ) )
tlmi_priv . can_get_bios_selections = true ;
if ( wmi_has_guid ( LENOVO_SET_BIOS_PASSWORD_GUID ) )
tlmi_priv . can_set_bios_password = true ;
if ( wmi_has_guid ( LENOVO_BIOS_PASSWORD_SETTINGS_GUID ) )
tlmi_priv . can_get_password_settings = true ;
2021-08-17 03:15:01 +03:00
if ( wmi_has_guid ( LENOVO_DEBUG_CMD_GUID ) )
tlmi_priv . can_debug_cmd = true ;
2021-11-17 21:44:53 +03:00
if ( wmi_has_guid ( LENOVO_OPCODE_IF_GUID ) )
tlmi_priv . opcode_support = true ;
2022-03-18 00:40:08 +03:00
if ( wmi_has_guid ( LENOVO_SET_BIOS_CERT_GUID ) & &
wmi_has_guid ( LENOVO_SET_BIOS_SETTING_CERT_GUID ) & &
wmi_has_guid ( LENOVO_SAVE_BIOS_SETTING_CERT_GUID ) )
tlmi_priv . certificate_support = true ;
2021-05-31 01:31:11 +03:00
/*
* Try to find the number of valid settings of this machine
* and use it to create sysfs attributes .
*/
for ( i = 0 ; i < TLMI_SETTINGS_COUNT ; + + i ) {
struct tlmi_attr_setting * setting ;
char * item = NULL ;
tlmi_priv . setting [ i ] = NULL ;
2023-03-12 06:47:57 +03:00
ret = tlmi_setting ( i , & item , LENOVO_BIOS_SETTING_GUID ) ;
if ( ret )
2021-05-31 01:31:11 +03:00
break ;
if ( ! item )
break ;
2021-11-08 21:03:57 +03:00
if ( ! * item ) {
kfree ( item ) ;
2021-05-31 01:31:11 +03:00
continue ;
2021-11-08 21:03:57 +03:00
}
2021-05-31 01:31:11 +03:00
/* It is not allowed to have '/' for file name. Convert it into '\'. */
strreplace ( item , ' / ' , ' \\ ' ) ;
/* Remove the value part */
2023-09-18 16:51:16 +03:00
strreplace ( item , ' , ' , ' \0 ' ) ;
2021-05-31 01:31:11 +03:00
/* Create a setting entry */
setting = kzalloc ( sizeof ( * setting ) , GFP_KERNEL ) ;
if ( ! setting ) {
ret = - ENOMEM ;
2021-11-08 21:03:57 +03:00
kfree ( item ) ;
2021-05-31 01:31:11 +03:00
goto fail_clear_attr ;
}
setting - > index = i ;
strscpy ( setting - > display_name , item , TLMI_SETTINGS_MAXLEN ) ;
/* If BIOS selections supported, load those */
if ( tlmi_priv . can_get_bios_selections ) {
ret = tlmi_get_bios_selections ( setting - > display_name ,
& setting - > possible_values ) ;
if ( ret | | ! setting - > possible_values )
pr_info ( " Error retrieving possible values for %d : %s \n " ,
i , setting - > display_name ) ;
2023-03-20 03:32:21 +03:00
} else {
/*
* Older Thinkstations don ' t support the bios_selections API .
* Instead they store this as a [ Optional : Option1 , Option2 ] section of the
* name string .
* Try and pull that out if it ' s available .
*/
2023-04-03 04:31:19 +03:00
char * optitem , * optstart , * optend ;
2023-03-20 03:32:21 +03:00
2023-04-03 04:31:19 +03:00
if ( ! tlmi_setting ( setting - > index , & optitem , LENOVO_BIOS_SETTING_GUID ) ) {
optstart = strstr ( optitem , " [Optional: " ) ;
2023-03-20 03:32:21 +03:00
if ( optstart ) {
optstart + = strlen ( " [Optional: " ) ;
optend = strstr ( optstart , " ] " ) ;
if ( optend )
setting - > possible_values =
kstrndup ( optstart , optend - optstart ,
GFP_KERNEL ) ;
}
2023-04-03 04:31:19 +03:00
kfree ( optitem ) ;
2023-03-20 03:32:21 +03:00
}
2021-05-31 01:31:11 +03:00
}
2023-03-20 03:32:19 +03:00
/*
* firmware - attributes requires that possible_values are separated by ' ; ' but
* Lenovo FW uses ' , ' . Replace appropriately .
*/
if ( setting - > possible_values )
strreplace ( setting - > possible_values , ' , ' , ' ; ' ) ;
2021-07-17 17:36:06 +03:00
kobject_init ( & setting - > kobj , & tlmi_attr_setting_ktype ) ;
2021-05-31 01:31:11 +03:00
tlmi_priv . setting [ i ] = setting ;
kfree ( item ) ;
}
/* Create password setting structure */
2021-11-17 21:44:53 +03:00
ret = tlmi_get_pwd_settings ( & tlmi_priv . pwdcfg ) ;
2021-05-31 01:31:11 +03:00
if ( ret )
goto fail_clear_attr ;
2021-11-18 14:41:50 +03:00
/* All failures below boil down to kmalloc failures */
ret = - ENOMEM ;
2021-11-17 21:44:53 +03:00
tlmi_priv . pwd_admin = tlmi_create_auth ( " pap " , " bios-admin " ) ;
2021-11-18 14:41:50 +03:00
if ( ! tlmi_priv . pwd_admin )
2021-05-31 01:31:11 +03:00
goto fail_clear_attr ;
2021-11-18 14:41:50 +03:00
2021-11-17 21:44:53 +03:00
if ( tlmi_priv . pwdcfg . core . password_state & TLMI_PAP_PWD )
2021-05-31 01:31:11 +03:00
tlmi_priv . pwd_admin - > valid = true ;
2021-11-17 21:44:53 +03:00
tlmi_priv . pwd_power = tlmi_create_auth ( " pop " , " power-on " ) ;
2021-11-18 14:41:50 +03:00
if ( ! tlmi_priv . pwd_power )
2021-11-17 21:44:53 +03:00
goto fail_clear_attr ;
2021-11-18 14:41:50 +03:00
2021-11-17 21:44:53 +03:00
if ( tlmi_priv . pwdcfg . core . password_state & TLMI_POP_PWD )
2021-05-31 01:31:11 +03:00
tlmi_priv . pwd_power - > valid = true ;
2021-11-17 21:44:53 +03:00
if ( tlmi_priv . opcode_support ) {
2023-06-01 23:05:47 +03:00
tlmi_priv . pwd_system = tlmi_create_auth ( " smp " , " system " ) ;
2021-11-18 14:41:50 +03:00
if ( ! tlmi_priv . pwd_system )
2021-11-17 21:44:53 +03:00
goto fail_clear_attr ;
2021-11-18 14:41:50 +03:00
2023-06-01 23:05:47 +03:00
if ( tlmi_priv . pwdcfg . core . password_state & TLMI_SMP_PWD )
2021-11-17 21:44:53 +03:00
tlmi_priv . pwd_system - > valid = true ;
tlmi_priv . pwd_hdd = tlmi_create_auth ( " hdd " , " hdd " ) ;
2021-11-18 14:41:50 +03:00
if ( ! tlmi_priv . pwd_hdd )
2021-11-17 21:44:53 +03:00
goto fail_clear_attr ;
tlmi_priv . pwd_nvme = tlmi_create_auth ( " nvm " , " nvme " ) ;
2021-11-18 14:41:50 +03:00
if ( ! tlmi_priv . pwd_nvme )
2021-11-17 21:44:53 +03:00
goto fail_clear_attr ;
2023-06-01 23:05:51 +03:00
/* Set default hdd/nvme index to 1 as there is no device 0 */
tlmi_priv . pwd_hdd - > index = 1 ;
tlmi_priv . pwd_nvme - > index = 1 ;
2021-11-17 21:44:53 +03:00
if ( tlmi_priv . pwdcfg . core . password_state & TLMI_HDD_PWD ) {
/* Check if PWD is configured and set index to first drive found */
if ( tlmi_priv . pwdcfg . ext . hdd_user_password | |
tlmi_priv . pwdcfg . ext . hdd_master_password ) {
tlmi_priv . pwd_hdd - > valid = true ;
if ( tlmi_priv . pwdcfg . ext . hdd_master_password )
tlmi_priv . pwd_hdd - > index =
ffs ( tlmi_priv . pwdcfg . ext . hdd_master_password ) - 1 ;
else
tlmi_priv . pwd_hdd - > index =
ffs ( tlmi_priv . pwdcfg . ext . hdd_user_password ) - 1 ;
}
if ( tlmi_priv . pwdcfg . ext . nvme_user_password | |
tlmi_priv . pwdcfg . ext . nvme_master_password ) {
tlmi_priv . pwd_nvme - > valid = true ;
if ( tlmi_priv . pwdcfg . ext . nvme_master_password )
tlmi_priv . pwd_nvme - > index =
ffs ( tlmi_priv . pwdcfg . ext . nvme_master_password ) - 1 ;
else
tlmi_priv . pwd_nvme - > index =
ffs ( tlmi_priv . pwdcfg . ext . nvme_user_password ) - 1 ;
}
}
}
2022-03-18 00:40:08 +03:00
if ( tlmi_priv . certificate_support & &
( tlmi_priv . pwdcfg . core . password_state & TLMI_CERT ) )
tlmi_priv . pwd_admin - > cert_installed = true ;
2021-05-31 01:31:11 +03:00
return 0 ;
fail_clear_attr :
2021-07-17 17:36:07 +03:00
for ( i = 0 ; i < TLMI_SETTINGS_COUNT ; + + i ) {
if ( tlmi_priv . setting [ i ] ) {
kfree ( tlmi_priv . setting [ i ] - > possible_values ) ;
kfree ( tlmi_priv . setting [ i ] ) ;
}
}
2021-11-17 21:44:53 +03:00
kfree ( tlmi_priv . pwd_admin ) ;
kfree ( tlmi_priv . pwd_power ) ;
kfree ( tlmi_priv . pwd_system ) ;
kfree ( tlmi_priv . pwd_hdd ) ;
kfree ( tlmi_priv . pwd_nvme ) ;
2021-05-31 01:31:11 +03:00
return ret ;
}
static void tlmi_remove ( struct wmi_device * wdev )
{
tlmi_release_attr ( ) ;
device_destroy ( fw_attr_class , MKDEV ( 0 , 0 ) ) ;
fw_attributes_class_put ( ) ;
}
static int tlmi_probe ( struct wmi_device * wdev , const void * context )
{
2021-11-08 21:03:57 +03:00
int ret ;
ret = tlmi_analyze ( ) ;
if ( ret )
return ret ;
2021-05-31 01:31:11 +03:00
return tlmi_sysfs_init ( ) ;
}
static const struct wmi_device_id tlmi_id_table [ ] = {
{ . guid_string = LENOVO_BIOS_SETTING_GUID } ,
{ }
} ;
2021-06-16 05:50:09 +03:00
MODULE_DEVICE_TABLE ( wmi , tlmi_id_table ) ;
2021-05-31 01:31:11 +03:00
static struct wmi_driver tlmi_driver = {
. driver = {
. name = " think-lmi " ,
} ,
. id_table = tlmi_id_table ,
. probe = tlmi_probe ,
. remove = tlmi_remove ,
} ;
MODULE_AUTHOR ( " Sugumaran L <slacshiminar@lenovo.com> " ) ;
MODULE_AUTHOR ( " Mark Pearson <markpearson@lenovo.com> " ) ;
MODULE_AUTHOR ( " Corentin Chary <corentin.chary@gmail.com> " ) ;
MODULE_DESCRIPTION ( " ThinkLMI Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_wmi_driver ( tlmi_driver ) ;