162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Think LMI BIOS configuration driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright(C) 2019-2021 Lenovo 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Original code from Thinkpad-wmi project https://github.com/iksaif/thinkpad-wmi 862306a36Sopenharmony_ci * Copyright(C) 2017 Corentin Chary <corentin.chary@gmail.com> 962306a36Sopenharmony_ci * Distributed under the GPL-2.0 license 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/acpi.h> 1562306a36Sopenharmony_ci#include <linux/errno.h> 1662306a36Sopenharmony_ci#include <linux/fs.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/string.h> 1962306a36Sopenharmony_ci#include <linux/types.h> 2062306a36Sopenharmony_ci#include <linux/dmi.h> 2162306a36Sopenharmony_ci#include <linux/wmi.h> 2262306a36Sopenharmony_ci#include "firmware_attributes_class.h" 2362306a36Sopenharmony_ci#include "think-lmi.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic bool debug_support; 2662306a36Sopenharmony_cimodule_param(debug_support, bool, 0444); 2762306a36Sopenharmony_ciMODULE_PARM_DESC(debug_support, "Enable debug command support"); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * Name: BiosSetting 3162306a36Sopenharmony_ci * Description: Get item name and settings for current LMI instance. 3262306a36Sopenharmony_ci * Type: Query 3362306a36Sopenharmony_ci * Returns: "Item,Value" 3462306a36Sopenharmony_ci * Example: "WakeOnLAN,Enable" 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci#define LENOVO_BIOS_SETTING_GUID "51F5230E-9677-46CD-A1CF-C0B23EE34DB7" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * Name: SetBiosSetting 4062306a36Sopenharmony_ci * Description: Change the BIOS setting to the desired value using the SetBiosSetting 4162306a36Sopenharmony_ci * class. To save the settings, use the SaveBiosSetting class. 4262306a36Sopenharmony_ci * BIOS settings and values are case sensitive. 4362306a36Sopenharmony_ci * After making changes to the BIOS settings, you must reboot the computer 4462306a36Sopenharmony_ci * before the changes will take effect. 4562306a36Sopenharmony_ci * Type: Method 4662306a36Sopenharmony_ci * Arguments: "Item,Value,Password,Encoding,KbdLang;" 4762306a36Sopenharmony_ci * Example: "WakeOnLAN,Disable,pa55w0rd,ascii,us;" 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci#define LENOVO_SET_BIOS_SETTINGS_GUID "98479A64-33F5-4E33-A707-8E251EBBC3A1" 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * Name: SaveBiosSettings 5362306a36Sopenharmony_ci * Description: Save any pending changes in settings. 5462306a36Sopenharmony_ci * Type: Method 5562306a36Sopenharmony_ci * Arguments: "Password,Encoding,KbdLang;" 5662306a36Sopenharmony_ci * Example: "pa55w0rd,ascii,us;" 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci#define LENOVO_SAVE_BIOS_SETTINGS_GUID "6A4B54EF-A5ED-4D33-9455-B0D9B48DF4B3" 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * Name: BiosPasswordSettings 6262306a36Sopenharmony_ci * Description: Return BIOS Password settings 6362306a36Sopenharmony_ci * Type: Query 6462306a36Sopenharmony_ci * Returns: PasswordMode, PasswordState, MinLength, MaxLength, 6562306a36Sopenharmony_ci * SupportedEncoding, SupportedKeyboard 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci#define LENOVO_BIOS_PASSWORD_SETTINGS_GUID "8ADB159E-1E32-455C-BC93-308A7ED98246" 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Name: SetBiosPassword 7162306a36Sopenharmony_ci * Description: Change a specific password. 7262306a36Sopenharmony_ci * - BIOS settings cannot be changed at the same boot as power-on 7362306a36Sopenharmony_ci * passwords (POP) and hard disk passwords (HDP). If you want to change 7462306a36Sopenharmony_ci * BIOS settings and POP or HDP, you must reboot the system after changing 7562306a36Sopenharmony_ci * one of them. 7662306a36Sopenharmony_ci * - A password cannot be set using this method when one does not already 7762306a36Sopenharmony_ci * exist. Passwords can only be updated or cleared. 7862306a36Sopenharmony_ci * Type: Method 7962306a36Sopenharmony_ci * Arguments: "PasswordType,CurrentPassword,NewPassword,Encoding,KbdLang;" 8062306a36Sopenharmony_ci * Example: "pop,pa55w0rd,newpa55w0rd,ascii,us;” 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci#define LENOVO_SET_BIOS_PASSWORD_GUID "2651D9FD-911C-4B69-B94E-D0DED5963BD7" 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * Name: GetBiosSelections 8662306a36Sopenharmony_ci * Description: Return a list of valid settings for a given item. 8762306a36Sopenharmony_ci * Type: Method 8862306a36Sopenharmony_ci * Arguments: "Item" 8962306a36Sopenharmony_ci * Returns: "Value1,Value2,Value3,..." 9062306a36Sopenharmony_ci * Example: 9162306a36Sopenharmony_ci * -> "FlashOverLAN" 9262306a36Sopenharmony_ci * <- "Enabled,Disabled" 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci#define LENOVO_GET_BIOS_SELECTIONS_GUID "7364651A-132F-4FE7-ADAA-40C6C7EE2E3B" 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* 9762306a36Sopenharmony_ci * Name: DebugCmd 9862306a36Sopenharmony_ci * Description: Debug entry method for entering debug commands to the BIOS 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci#define LENOVO_DEBUG_CMD_GUID "7FF47003-3B6C-4E5E-A227-E979824A85D1" 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * Name: OpcodeIF 10462306a36Sopenharmony_ci * Description: Opcode interface which provides the ability to set multiple 10562306a36Sopenharmony_ci * parameters and then trigger an action with a final command. 10662306a36Sopenharmony_ci * This is particularly useful for simplifying setting passwords. 10762306a36Sopenharmony_ci * With this support comes the ability to set System, HDD and NVMe 10862306a36Sopenharmony_ci * passwords. 10962306a36Sopenharmony_ci * This is currently available on ThinkCenter and ThinkStations platforms 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci#define LENOVO_OPCODE_IF_GUID "DFDDEF2C-57D4-48ce-B196-0FB787D90836" 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* 11462306a36Sopenharmony_ci * Name: SetBiosCert 11562306a36Sopenharmony_ci * Description: Install BIOS certificate. 11662306a36Sopenharmony_ci * Type: Method 11762306a36Sopenharmony_ci * Arguments: "Certificate,Password" 11862306a36Sopenharmony_ci * You must reboot the computer before the changes will take effect. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ci#define LENOVO_SET_BIOS_CERT_GUID "26861C9F-47E9-44C4-BD8B-DFE7FA2610FE" 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* 12362306a36Sopenharmony_ci * Name: UpdateBiosCert 12462306a36Sopenharmony_ci * Description: Update BIOS certificate. 12562306a36Sopenharmony_ci * Type: Method 12662306a36Sopenharmony_ci * Format: "Certificate,Signature" 12762306a36Sopenharmony_ci * You must reboot the computer before the changes will take effect. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci#define LENOVO_UPDATE_BIOS_CERT_GUID "9AA3180A-9750-41F7-B9F7-D5D3B1BAC3CE" 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* 13262306a36Sopenharmony_ci * Name: ClearBiosCert 13362306a36Sopenharmony_ci * Description: Uninstall BIOS certificate. 13462306a36Sopenharmony_ci * Type: Method 13562306a36Sopenharmony_ci * Format: "Serial,Signature" 13662306a36Sopenharmony_ci * You must reboot the computer before the changes will take effect. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci#define LENOVO_CLEAR_BIOS_CERT_GUID "B2BC39A7-78DD-4D71-B059-A510DEC44890" 13962306a36Sopenharmony_ci/* 14062306a36Sopenharmony_ci * Name: CertToPassword 14162306a36Sopenharmony_ci * Description: Switch from certificate to password authentication. 14262306a36Sopenharmony_ci * Type: Method 14362306a36Sopenharmony_ci * Format: "Password,Signature" 14462306a36Sopenharmony_ci * You must reboot the computer before the changes will take effect. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ci#define LENOVO_CERT_TO_PASSWORD_GUID "0DE8590D-5510-4044-9621-77C227F5A70D" 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* 14962306a36Sopenharmony_ci * Name: SetBiosSettingCert 15062306a36Sopenharmony_ci * Description: Set attribute using certificate authentication. 15162306a36Sopenharmony_ci * Type: Method 15262306a36Sopenharmony_ci * Format: "Item,Value,Signature" 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci#define LENOVO_SET_BIOS_SETTING_CERT_GUID "34A008CC-D205-4B62-9E67-31DFA8B90003" 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* 15762306a36Sopenharmony_ci * Name: SaveBiosSettingCert 15862306a36Sopenharmony_ci * Description: Save any pending changes in settings. 15962306a36Sopenharmony_ci * Type: Method 16062306a36Sopenharmony_ci * Format: "Signature" 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci#define LENOVO_SAVE_BIOS_SETTING_CERT_GUID "C050FB9D-DF5F-4606-B066-9EFC401B2551" 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* 16562306a36Sopenharmony_ci * Name: CertThumbprint 16662306a36Sopenharmony_ci * Description: Display Certificate thumbprints 16762306a36Sopenharmony_ci * Type: Query 16862306a36Sopenharmony_ci * Returns: MD5, SHA1 & SHA256 thumbprints 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ci#define LENOVO_CERT_THUMBPRINT_GUID "C59119ED-1C0D-4806-A8E9-59AA318176C4" 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci#define TLMI_POP_PWD BIT(0) /* Supervisor */ 17362306a36Sopenharmony_ci#define TLMI_PAP_PWD BIT(1) /* Power-on */ 17462306a36Sopenharmony_ci#define TLMI_HDD_PWD BIT(2) /* HDD/NVME */ 17562306a36Sopenharmony_ci#define TLMI_SMP_PWD BIT(6) /* System Management */ 17662306a36Sopenharmony_ci#define TLMI_CERT BIT(7) /* Certificate Based */ 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci#define to_tlmi_pwd_setting(kobj) container_of(kobj, struct tlmi_pwd_setting, kobj) 17962306a36Sopenharmony_ci#define to_tlmi_attr_setting(kobj) container_of(kobj, struct tlmi_attr_setting, kobj) 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic const struct tlmi_err_codes tlmi_errs[] = { 18262306a36Sopenharmony_ci {"Success", 0}, 18362306a36Sopenharmony_ci {"Not Supported", -EOPNOTSUPP}, 18462306a36Sopenharmony_ci {"Invalid Parameter", -EINVAL}, 18562306a36Sopenharmony_ci {"Access Denied", -EACCES}, 18662306a36Sopenharmony_ci {"System Busy", -EBUSY}, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic const char * const encoding_options[] = { 19062306a36Sopenharmony_ci [TLMI_ENCODING_ASCII] = "ascii", 19162306a36Sopenharmony_ci [TLMI_ENCODING_SCANCODE] = "scancode", 19262306a36Sopenharmony_ci}; 19362306a36Sopenharmony_cistatic const char * const level_options[] = { 19462306a36Sopenharmony_ci [TLMI_LEVEL_USER] = "user", 19562306a36Sopenharmony_ci [TLMI_LEVEL_MASTER] = "master", 19662306a36Sopenharmony_ci}; 19762306a36Sopenharmony_cistatic struct think_lmi tlmi_priv; 19862306a36Sopenharmony_cistatic struct class *fw_attr_class; 19962306a36Sopenharmony_cistatic DEFINE_MUTEX(tlmi_mutex); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/* ------ Utility functions ------------*/ 20262306a36Sopenharmony_ci/* Strip out CR if one is present */ 20362306a36Sopenharmony_cistatic void strip_cr(char *str) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci char *p = strchrnul(str, '\n'); 20662306a36Sopenharmony_ci *p = '\0'; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/* Convert BIOS WMI error string to suitable error code */ 21062306a36Sopenharmony_cistatic int tlmi_errstr_to_err(const char *errstr) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci int i; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci for (i = 0; i < sizeof(tlmi_errs)/sizeof(struct tlmi_err_codes); i++) { 21562306a36Sopenharmony_ci if (!strcmp(tlmi_errs[i].err_str, errstr)) 21662306a36Sopenharmony_ci return tlmi_errs[i].err_code; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci return -EPERM; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/* Extract error string from WMI return buffer */ 22262306a36Sopenharmony_cistatic int tlmi_extract_error(const struct acpi_buffer *output) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci const union acpi_object *obj; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci obj = output->pointer; 22762306a36Sopenharmony_ci if (!obj) 22862306a36Sopenharmony_ci return -ENOMEM; 22962306a36Sopenharmony_ci if (obj->type != ACPI_TYPE_STRING || !obj->string.pointer) 23062306a36Sopenharmony_ci return -EIO; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return tlmi_errstr_to_err(obj->string.pointer); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* Utility function to execute WMI call to BIOS */ 23662306a36Sopenharmony_cistatic int tlmi_simple_call(const char *guid, const char *arg) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci const struct acpi_buffer input = { strlen(arg), (char *)arg }; 23962306a36Sopenharmony_ci struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 24062306a36Sopenharmony_ci acpi_status status; 24162306a36Sopenharmony_ci int i, err; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* 24462306a36Sopenharmony_ci * Duplicated call required to match BIOS workaround for behavior 24562306a36Sopenharmony_ci * seen when WMI accessed via scripting on other OS. 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 24862306a36Sopenharmony_ci /* (re)initialize output buffer to default state */ 24962306a36Sopenharmony_ci output.length = ACPI_ALLOCATE_BUFFER; 25062306a36Sopenharmony_ci output.pointer = NULL; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci status = wmi_evaluate_method(guid, 0, 0, &input, &output); 25362306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 25462306a36Sopenharmony_ci kfree(output.pointer); 25562306a36Sopenharmony_ci return -EIO; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci err = tlmi_extract_error(&output); 25862306a36Sopenharmony_ci kfree(output.pointer); 25962306a36Sopenharmony_ci if (err) 26062306a36Sopenharmony_ci return err; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/* Extract output string from WMI return buffer */ 26662306a36Sopenharmony_cistatic int tlmi_extract_output_string(const struct acpi_buffer *output, 26762306a36Sopenharmony_ci char **string) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci const union acpi_object *obj; 27062306a36Sopenharmony_ci char *s; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci obj = output->pointer; 27362306a36Sopenharmony_ci if (!obj) 27462306a36Sopenharmony_ci return -ENOMEM; 27562306a36Sopenharmony_ci if (obj->type != ACPI_TYPE_STRING || !obj->string.pointer) 27662306a36Sopenharmony_ci return -EIO; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci s = kstrdup(obj->string.pointer, GFP_KERNEL); 27962306a36Sopenharmony_ci if (!s) 28062306a36Sopenharmony_ci return -ENOMEM; 28162306a36Sopenharmony_ci *string = s; 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/* ------ Core interface functions ------------*/ 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci/* Get password settings from BIOS */ 28862306a36Sopenharmony_cistatic int tlmi_get_pwd_settings(struct tlmi_pwdcfg *pwdcfg) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 29162306a36Sopenharmony_ci const union acpi_object *obj; 29262306a36Sopenharmony_ci acpi_status status; 29362306a36Sopenharmony_ci int copy_size; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (!tlmi_priv.can_get_password_settings) 29662306a36Sopenharmony_ci return -EOPNOTSUPP; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci status = wmi_query_block(LENOVO_BIOS_PASSWORD_SETTINGS_GUID, 0, 29962306a36Sopenharmony_ci &output); 30062306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 30162306a36Sopenharmony_ci return -EIO; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci obj = output.pointer; 30462306a36Sopenharmony_ci if (!obj) 30562306a36Sopenharmony_ci return -ENOMEM; 30662306a36Sopenharmony_ci if (obj->type != ACPI_TYPE_BUFFER || !obj->buffer.pointer) { 30762306a36Sopenharmony_ci kfree(obj); 30862306a36Sopenharmony_ci return -EIO; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci /* 31162306a36Sopenharmony_ci * The size of thinkpad_wmi_pcfg on ThinkStation is larger than ThinkPad. 31262306a36Sopenharmony_ci * To make the driver compatible on different brands, we permit it to get 31362306a36Sopenharmony_ci * the data in below case. 31462306a36Sopenharmony_ci * Settings must have at minimum the core fields available 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci if (obj->buffer.length < sizeof(struct tlmi_pwdcfg_core)) { 31762306a36Sopenharmony_ci pr_warn("Unknown pwdcfg buffer length %d\n", obj->buffer.length); 31862306a36Sopenharmony_ci kfree(obj); 31962306a36Sopenharmony_ci return -EIO; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci copy_size = min_t(size_t, obj->buffer.length, sizeof(struct tlmi_pwdcfg)); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci memcpy(pwdcfg, obj->buffer.pointer, copy_size); 32562306a36Sopenharmony_ci kfree(obj); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (WARN_ON(pwdcfg->core.max_length >= TLMI_PWD_BUFSIZE)) 32862306a36Sopenharmony_ci pwdcfg->core.max_length = TLMI_PWD_BUFSIZE - 1; 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int tlmi_save_bios_settings(const char *password) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci return tlmi_simple_call(LENOVO_SAVE_BIOS_SETTINGS_GUID, 33562306a36Sopenharmony_ci password); 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int tlmi_opcode_setting(char *setting, const char *value) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci char *opcode_str; 34162306a36Sopenharmony_ci int ret; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci opcode_str = kasprintf(GFP_KERNEL, "%s:%s;", setting, value); 34462306a36Sopenharmony_ci if (!opcode_str) 34562306a36Sopenharmony_ci return -ENOMEM; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci ret = tlmi_simple_call(LENOVO_OPCODE_IF_GUID, opcode_str); 34862306a36Sopenharmony_ci kfree(opcode_str); 34962306a36Sopenharmony_ci return ret; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int tlmi_setting(int item, char **value, const char *guid_string) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 35562306a36Sopenharmony_ci acpi_status status; 35662306a36Sopenharmony_ci int ret; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci status = wmi_query_block(guid_string, item, &output); 35962306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 36062306a36Sopenharmony_ci kfree(output.pointer); 36162306a36Sopenharmony_ci return -EIO; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci ret = tlmi_extract_output_string(&output, value); 36562306a36Sopenharmony_ci kfree(output.pointer); 36662306a36Sopenharmony_ci return ret; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int tlmi_get_bios_selections(const char *item, char **value) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci const struct acpi_buffer input = { strlen(item), (char *)item }; 37262306a36Sopenharmony_ci struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 37362306a36Sopenharmony_ci acpi_status status; 37462306a36Sopenharmony_ci int ret; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci status = wmi_evaluate_method(LENOVO_GET_BIOS_SELECTIONS_GUID, 37762306a36Sopenharmony_ci 0, 0, &input, &output); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 38062306a36Sopenharmony_ci kfree(output.pointer); 38162306a36Sopenharmony_ci return -EIO; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci ret = tlmi_extract_output_string(&output, value); 38562306a36Sopenharmony_ci kfree(output.pointer); 38662306a36Sopenharmony_ci return ret; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/* ---- Authentication sysfs --------------------------------------------------------- */ 39062306a36Sopenharmony_cistatic ssize_t is_enabled_show(struct kobject *kobj, struct kobj_attribute *attr, 39162306a36Sopenharmony_ci char *buf) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", setting->valid); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic struct kobj_attribute auth_is_pass_set = __ATTR_RO(is_enabled); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic ssize_t current_password_store(struct kobject *kobj, 40162306a36Sopenharmony_ci struct kobj_attribute *attr, 40262306a36Sopenharmony_ci const char *buf, size_t count) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 40562306a36Sopenharmony_ci size_t pwdlen; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci pwdlen = strlen(buf); 40862306a36Sopenharmony_ci /* pwdlen == 0 is allowed to clear the password */ 40962306a36Sopenharmony_ci if (pwdlen && ((pwdlen < setting->minlen) || (pwdlen > setting->maxlen))) 41062306a36Sopenharmony_ci return -EINVAL; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci strscpy(setting->password, buf, setting->maxlen); 41362306a36Sopenharmony_ci /* Strip out CR if one is present, setting password won't work if it is present */ 41462306a36Sopenharmony_ci strip_cr(setting->password); 41562306a36Sopenharmony_ci return count; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic struct kobj_attribute auth_current_password = __ATTR_WO(current_password); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic ssize_t new_password_store(struct kobject *kobj, 42162306a36Sopenharmony_ci struct kobj_attribute *attr, 42262306a36Sopenharmony_ci const char *buf, size_t count) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 42562306a36Sopenharmony_ci char *auth_str, *new_pwd; 42662306a36Sopenharmony_ci size_t pwdlen; 42762306a36Sopenharmony_ci int ret; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 43062306a36Sopenharmony_ci return -EPERM; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!tlmi_priv.can_set_bios_password) 43362306a36Sopenharmony_ci return -EOPNOTSUPP; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci new_pwd = kstrdup(buf, GFP_KERNEL); 43662306a36Sopenharmony_ci if (!new_pwd) 43762306a36Sopenharmony_ci return -ENOMEM; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* Strip out CR if one is present, setting password won't work if it is present */ 44062306a36Sopenharmony_ci strip_cr(new_pwd); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Use lock in case multiple WMI operations needed */ 44362306a36Sopenharmony_ci mutex_lock(&tlmi_mutex); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci pwdlen = strlen(new_pwd); 44662306a36Sopenharmony_ci /* pwdlen == 0 is allowed to clear the password */ 44762306a36Sopenharmony_ci if (pwdlen && ((pwdlen < setting->minlen) || (pwdlen > setting->maxlen))) { 44862306a36Sopenharmony_ci ret = -EINVAL; 44962306a36Sopenharmony_ci goto out; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* If opcode support is present use that interface */ 45362306a36Sopenharmony_ci if (tlmi_priv.opcode_support) { 45462306a36Sopenharmony_ci char pwd_type[8]; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* Special handling required for HDD and NVMe passwords */ 45762306a36Sopenharmony_ci if (setting == tlmi_priv.pwd_hdd) { 45862306a36Sopenharmony_ci if (setting->level == TLMI_LEVEL_USER) 45962306a36Sopenharmony_ci sprintf(pwd_type, "uhdp%d", setting->index); 46062306a36Sopenharmony_ci else 46162306a36Sopenharmony_ci sprintf(pwd_type, "mhdp%d", setting->index); 46262306a36Sopenharmony_ci } else if (setting == tlmi_priv.pwd_nvme) { 46362306a36Sopenharmony_ci if (setting->level == TLMI_LEVEL_USER) 46462306a36Sopenharmony_ci sprintf(pwd_type, "udrp%d", setting->index); 46562306a36Sopenharmony_ci else 46662306a36Sopenharmony_ci sprintf(pwd_type, "adrp%d", setting->index); 46762306a36Sopenharmony_ci } else { 46862306a36Sopenharmony_ci sprintf(pwd_type, "%s", setting->pwd_type); 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci ret = tlmi_opcode_setting("WmiOpcodePasswordType", pwd_type); 47262306a36Sopenharmony_ci if (ret) 47362306a36Sopenharmony_ci goto out; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (tlmi_priv.pwd_admin->valid) { 47662306a36Sopenharmony_ci ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin", 47762306a36Sopenharmony_ci tlmi_priv.pwd_admin->password); 47862306a36Sopenharmony_ci if (ret) 47962306a36Sopenharmony_ci goto out; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci ret = tlmi_opcode_setting("WmiOpcodePasswordCurrent01", setting->password); 48262306a36Sopenharmony_ci if (ret) 48362306a36Sopenharmony_ci goto out; 48462306a36Sopenharmony_ci ret = tlmi_opcode_setting("WmiOpcodePasswordNew01", new_pwd); 48562306a36Sopenharmony_ci if (ret) 48662306a36Sopenharmony_ci goto out; 48762306a36Sopenharmony_ci ret = tlmi_simple_call(LENOVO_OPCODE_IF_GUID, "WmiOpcodePasswordSetUpdate;"); 48862306a36Sopenharmony_ci } else { 48962306a36Sopenharmony_ci /* Format: 'PasswordType,CurrentPw,NewPw,Encoding,KbdLang;' */ 49062306a36Sopenharmony_ci auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s,%s,%s;", 49162306a36Sopenharmony_ci setting->pwd_type, setting->password, new_pwd, 49262306a36Sopenharmony_ci encoding_options[setting->encoding], setting->kbdlang); 49362306a36Sopenharmony_ci if (!auth_str) { 49462306a36Sopenharmony_ci ret = -ENOMEM; 49562306a36Sopenharmony_ci goto out; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci ret = tlmi_simple_call(LENOVO_SET_BIOS_PASSWORD_GUID, auth_str); 49862306a36Sopenharmony_ci kfree(auth_str); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ciout: 50162306a36Sopenharmony_ci mutex_unlock(&tlmi_mutex); 50262306a36Sopenharmony_ci kfree(new_pwd); 50362306a36Sopenharmony_ci return ret ?: count; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic struct kobj_attribute auth_new_password = __ATTR_WO(new_password); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic ssize_t min_password_length_show(struct kobject *kobj, struct kobj_attribute *attr, 50962306a36Sopenharmony_ci char *buf) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", setting->minlen); 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic struct kobj_attribute auth_min_pass_length = __ATTR_RO(min_password_length); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic ssize_t max_password_length_show(struct kobject *kobj, struct kobj_attribute *attr, 51962306a36Sopenharmony_ci char *buf) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", setting->maxlen); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_cistatic struct kobj_attribute auth_max_pass_length = __ATTR_RO(max_password_length); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic ssize_t mechanism_show(struct kobject *kobj, struct kobj_attribute *attr, 52862306a36Sopenharmony_ci char *buf) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci return sysfs_emit(buf, "password\n"); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_cistatic struct kobj_attribute auth_mechanism = __ATTR_RO(mechanism); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic ssize_t encoding_show(struct kobject *kobj, struct kobj_attribute *attr, 53562306a36Sopenharmony_ci char *buf) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", encoding_options[setting->encoding]); 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic ssize_t encoding_store(struct kobject *kobj, 54362306a36Sopenharmony_ci struct kobj_attribute *attr, 54462306a36Sopenharmony_ci const char *buf, size_t count) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 54762306a36Sopenharmony_ci int i; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* Scan for a matching profile */ 55062306a36Sopenharmony_ci i = sysfs_match_string(encoding_options, buf); 55162306a36Sopenharmony_ci if (i < 0) 55262306a36Sopenharmony_ci return -EINVAL; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci setting->encoding = i; 55562306a36Sopenharmony_ci return count; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic struct kobj_attribute auth_encoding = __ATTR_RW(encoding); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic ssize_t kbdlang_show(struct kobject *kobj, struct kobj_attribute *attr, 56162306a36Sopenharmony_ci char *buf) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", setting->kbdlang); 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic ssize_t kbdlang_store(struct kobject *kobj, 56962306a36Sopenharmony_ci struct kobj_attribute *attr, 57062306a36Sopenharmony_ci const char *buf, size_t count) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 57362306a36Sopenharmony_ci int length; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* Calculate length till '\n' or terminating 0 */ 57662306a36Sopenharmony_ci length = strchrnul(buf, '\n') - buf; 57762306a36Sopenharmony_ci if (!length || length >= TLMI_LANG_MAXLEN) 57862306a36Sopenharmony_ci return -EINVAL; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci memcpy(setting->kbdlang, buf, length); 58162306a36Sopenharmony_ci setting->kbdlang[length] = '\0'; 58262306a36Sopenharmony_ci return count; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic struct kobj_attribute auth_kbdlang = __ATTR_RW(kbdlang); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic ssize_t role_show(struct kobject *kobj, struct kobj_attribute *attr, 58862306a36Sopenharmony_ci char *buf) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", setting->role); 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_cistatic struct kobj_attribute auth_role = __ATTR_RO(role); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic ssize_t index_show(struct kobject *kobj, struct kobj_attribute *attr, 59762306a36Sopenharmony_ci char *buf) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", setting->index); 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic ssize_t index_store(struct kobject *kobj, 60562306a36Sopenharmony_ci struct kobj_attribute *attr, 60662306a36Sopenharmony_ci const char *buf, size_t count) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 60962306a36Sopenharmony_ci int err, val; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci err = kstrtoint(buf, 10, &val); 61262306a36Sopenharmony_ci if (err < 0) 61362306a36Sopenharmony_ci return err; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (val < 0 || val > TLMI_INDEX_MAX) 61662306a36Sopenharmony_ci return -EINVAL; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci setting->index = val; 61962306a36Sopenharmony_ci return count; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic struct kobj_attribute auth_index = __ATTR_RW(index); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic ssize_t level_show(struct kobject *kobj, struct kobj_attribute *attr, 62562306a36Sopenharmony_ci char *buf) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", level_options[setting->level]); 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic ssize_t level_store(struct kobject *kobj, 63362306a36Sopenharmony_ci struct kobj_attribute *attr, 63462306a36Sopenharmony_ci const char *buf, size_t count) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 63762306a36Sopenharmony_ci int i; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* Scan for a matching profile */ 64062306a36Sopenharmony_ci i = sysfs_match_string(level_options, buf); 64162306a36Sopenharmony_ci if (i < 0) 64262306a36Sopenharmony_ci return -EINVAL; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci setting->level = i; 64562306a36Sopenharmony_ci return count; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic struct kobj_attribute auth_level = __ATTR_RW(level); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic ssize_t cert_thumbprint(char *buf, const char *arg, int count) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci const struct acpi_buffer input = { strlen(arg), (char *)arg }; 65362306a36Sopenharmony_ci struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 65462306a36Sopenharmony_ci const union acpi_object *obj; 65562306a36Sopenharmony_ci acpi_status status; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci status = wmi_evaluate_method(LENOVO_CERT_THUMBPRINT_GUID, 0, 0, &input, &output); 65862306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 65962306a36Sopenharmony_ci kfree(output.pointer); 66062306a36Sopenharmony_ci return -EIO; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci obj = output.pointer; 66362306a36Sopenharmony_ci if (!obj) 66462306a36Sopenharmony_ci return -ENOMEM; 66562306a36Sopenharmony_ci if (obj->type != ACPI_TYPE_STRING || !obj->string.pointer) { 66662306a36Sopenharmony_ci kfree(output.pointer); 66762306a36Sopenharmony_ci return -EIO; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci count += sysfs_emit_at(buf, count, "%s : %s\n", arg, (char *)obj->string.pointer); 67062306a36Sopenharmony_ci kfree(output.pointer); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci return count; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic ssize_t certificate_thumbprint_show(struct kobject *kobj, struct kobj_attribute *attr, 67662306a36Sopenharmony_ci char *buf) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 67962306a36Sopenharmony_ci int count = 0; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (!tlmi_priv.certificate_support || !setting->cert_installed) 68262306a36Sopenharmony_ci return -EOPNOTSUPP; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci count += cert_thumbprint(buf, "Md5", count); 68562306a36Sopenharmony_ci count += cert_thumbprint(buf, "Sha1", count); 68662306a36Sopenharmony_ci count += cert_thumbprint(buf, "Sha256", count); 68762306a36Sopenharmony_ci return count; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic struct kobj_attribute auth_cert_thumb = __ATTR_RO(certificate_thumbprint); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic ssize_t cert_to_password_store(struct kobject *kobj, 69362306a36Sopenharmony_ci struct kobj_attribute *attr, 69462306a36Sopenharmony_ci const char *buf, size_t count) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 69762306a36Sopenharmony_ci char *auth_str, *passwd; 69862306a36Sopenharmony_ci int ret; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 70162306a36Sopenharmony_ci return -EPERM; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (!tlmi_priv.certificate_support) 70462306a36Sopenharmony_ci return -EOPNOTSUPP; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (!setting->cert_installed) 70762306a36Sopenharmony_ci return -EINVAL; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (!setting->signature || !setting->signature[0]) 71062306a36Sopenharmony_ci return -EACCES; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci passwd = kstrdup(buf, GFP_KERNEL); 71362306a36Sopenharmony_ci if (!passwd) 71462306a36Sopenharmony_ci return -ENOMEM; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* Strip out CR if one is present */ 71762306a36Sopenharmony_ci strip_cr(passwd); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* Format: 'Password,Signature' */ 72062306a36Sopenharmony_ci auth_str = kasprintf(GFP_KERNEL, "%s,%s", passwd, setting->signature); 72162306a36Sopenharmony_ci if (!auth_str) { 72262306a36Sopenharmony_ci kfree_sensitive(passwd); 72362306a36Sopenharmony_ci return -ENOMEM; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci ret = tlmi_simple_call(LENOVO_CERT_TO_PASSWORD_GUID, auth_str); 72662306a36Sopenharmony_ci kfree(auth_str); 72762306a36Sopenharmony_ci kfree_sensitive(passwd); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci return ret ?: count; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic struct kobj_attribute auth_cert_to_password = __ATTR_WO(cert_to_password); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic ssize_t certificate_store(struct kobject *kobj, 73562306a36Sopenharmony_ci struct kobj_attribute *attr, 73662306a36Sopenharmony_ci const char *buf, size_t count) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 73962306a36Sopenharmony_ci char *auth_str, *new_cert; 74062306a36Sopenharmony_ci char *guid; 74162306a36Sopenharmony_ci int ret; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 74462306a36Sopenharmony_ci return -EPERM; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (!tlmi_priv.certificate_support) 74762306a36Sopenharmony_ci return -EOPNOTSUPP; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* If empty then clear installed certificate */ 75062306a36Sopenharmony_ci if ((buf[0] == '\0') || (buf[0] == '\n')) { /* Clear installed certificate */ 75162306a36Sopenharmony_ci /* Check that signature is set */ 75262306a36Sopenharmony_ci if (!setting->signature || !setting->signature[0]) 75362306a36Sopenharmony_ci return -EACCES; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* Format: 'serial#, signature' */ 75662306a36Sopenharmony_ci auth_str = kasprintf(GFP_KERNEL, "%s,%s", 75762306a36Sopenharmony_ci dmi_get_system_info(DMI_PRODUCT_SERIAL), 75862306a36Sopenharmony_ci setting->signature); 75962306a36Sopenharmony_ci if (!auth_str) 76062306a36Sopenharmony_ci return -ENOMEM; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci ret = tlmi_simple_call(LENOVO_CLEAR_BIOS_CERT_GUID, auth_str); 76362306a36Sopenharmony_ci kfree(auth_str); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return ret ?: count; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci new_cert = kstrdup(buf, GFP_KERNEL); 76962306a36Sopenharmony_ci if (!new_cert) 77062306a36Sopenharmony_ci return -ENOMEM; 77162306a36Sopenharmony_ci /* Strip out CR if one is present */ 77262306a36Sopenharmony_ci strip_cr(new_cert); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (setting->cert_installed) { 77562306a36Sopenharmony_ci /* Certificate is installed so this is an update */ 77662306a36Sopenharmony_ci if (!setting->signature || !setting->signature[0]) { 77762306a36Sopenharmony_ci kfree(new_cert); 77862306a36Sopenharmony_ci return -EACCES; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci guid = LENOVO_UPDATE_BIOS_CERT_GUID; 78162306a36Sopenharmony_ci /* Format: 'Certificate,Signature' */ 78262306a36Sopenharmony_ci auth_str = kasprintf(GFP_KERNEL, "%s,%s", 78362306a36Sopenharmony_ci new_cert, setting->signature); 78462306a36Sopenharmony_ci } else { 78562306a36Sopenharmony_ci /* This is a fresh install */ 78662306a36Sopenharmony_ci if (!setting->valid || !setting->password[0]) { 78762306a36Sopenharmony_ci kfree(new_cert); 78862306a36Sopenharmony_ci return -EACCES; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci guid = LENOVO_SET_BIOS_CERT_GUID; 79162306a36Sopenharmony_ci /* Format: 'Certificate,Admin-password' */ 79262306a36Sopenharmony_ci auth_str = kasprintf(GFP_KERNEL, "%s,%s", 79362306a36Sopenharmony_ci new_cert, setting->password); 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci kfree(new_cert); 79662306a36Sopenharmony_ci if (!auth_str) 79762306a36Sopenharmony_ci return -ENOMEM; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci ret = tlmi_simple_call(guid, auth_str); 80062306a36Sopenharmony_ci kfree(auth_str); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci return ret ?: count; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic struct kobj_attribute auth_certificate = __ATTR_WO(certificate); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic ssize_t signature_store(struct kobject *kobj, 80862306a36Sopenharmony_ci struct kobj_attribute *attr, 80962306a36Sopenharmony_ci const char *buf, size_t count) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 81262306a36Sopenharmony_ci char *new_signature; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 81562306a36Sopenharmony_ci return -EPERM; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (!tlmi_priv.certificate_support) 81862306a36Sopenharmony_ci return -EOPNOTSUPP; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci new_signature = kstrdup(buf, GFP_KERNEL); 82162306a36Sopenharmony_ci if (!new_signature) 82262306a36Sopenharmony_ci return -ENOMEM; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* Strip out CR if one is present */ 82562306a36Sopenharmony_ci strip_cr(new_signature); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* Free any previous signature */ 82862306a36Sopenharmony_ci kfree(setting->signature); 82962306a36Sopenharmony_ci setting->signature = new_signature; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return count; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic struct kobj_attribute auth_signature = __ATTR_WO(signature); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic ssize_t save_signature_store(struct kobject *kobj, 83762306a36Sopenharmony_ci struct kobj_attribute *attr, 83862306a36Sopenharmony_ci const char *buf, size_t count) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 84162306a36Sopenharmony_ci char *new_signature; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 84462306a36Sopenharmony_ci return -EPERM; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (!tlmi_priv.certificate_support) 84762306a36Sopenharmony_ci return -EOPNOTSUPP; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci new_signature = kstrdup(buf, GFP_KERNEL); 85062306a36Sopenharmony_ci if (!new_signature) 85162306a36Sopenharmony_ci return -ENOMEM; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci /* Strip out CR if one is present */ 85462306a36Sopenharmony_ci strip_cr(new_signature); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* Free any previous signature */ 85762306a36Sopenharmony_ci kfree(setting->save_signature); 85862306a36Sopenharmony_ci setting->save_signature = new_signature; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci return count; 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic struct kobj_attribute auth_save_signature = __ATTR_WO(save_signature); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic umode_t auth_attr_is_visible(struct kobject *kobj, 86662306a36Sopenharmony_ci struct attribute *attr, int n) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci /* We only want to display level and index settings on HDD/NVMe */ 87162306a36Sopenharmony_ci if (attr == &auth_index.attr || attr == &auth_level.attr) { 87262306a36Sopenharmony_ci if ((setting == tlmi_priv.pwd_hdd) || (setting == tlmi_priv.pwd_nvme)) 87362306a36Sopenharmony_ci return attr->mode; 87462306a36Sopenharmony_ci return 0; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* We only display certificates on Admin account, if supported */ 87862306a36Sopenharmony_ci if (attr == &auth_certificate.attr || 87962306a36Sopenharmony_ci attr == &auth_signature.attr || 88062306a36Sopenharmony_ci attr == &auth_save_signature.attr || 88162306a36Sopenharmony_ci attr == &auth_cert_thumb.attr || 88262306a36Sopenharmony_ci attr == &auth_cert_to_password.attr) { 88362306a36Sopenharmony_ci if ((setting == tlmi_priv.pwd_admin) && tlmi_priv.certificate_support) 88462306a36Sopenharmony_ci return attr->mode; 88562306a36Sopenharmony_ci return 0; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci /* Don't display un-needed settings if opcode available */ 88962306a36Sopenharmony_ci if ((attr == &auth_encoding.attr || attr == &auth_kbdlang.attr) && 89062306a36Sopenharmony_ci tlmi_priv.opcode_support) 89162306a36Sopenharmony_ci return 0; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci return attr->mode; 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic struct attribute *auth_attrs[] = { 89762306a36Sopenharmony_ci &auth_is_pass_set.attr, 89862306a36Sopenharmony_ci &auth_min_pass_length.attr, 89962306a36Sopenharmony_ci &auth_max_pass_length.attr, 90062306a36Sopenharmony_ci &auth_current_password.attr, 90162306a36Sopenharmony_ci &auth_new_password.attr, 90262306a36Sopenharmony_ci &auth_role.attr, 90362306a36Sopenharmony_ci &auth_mechanism.attr, 90462306a36Sopenharmony_ci &auth_encoding.attr, 90562306a36Sopenharmony_ci &auth_kbdlang.attr, 90662306a36Sopenharmony_ci &auth_index.attr, 90762306a36Sopenharmony_ci &auth_level.attr, 90862306a36Sopenharmony_ci &auth_certificate.attr, 90962306a36Sopenharmony_ci &auth_signature.attr, 91062306a36Sopenharmony_ci &auth_save_signature.attr, 91162306a36Sopenharmony_ci &auth_cert_thumb.attr, 91262306a36Sopenharmony_ci &auth_cert_to_password.attr, 91362306a36Sopenharmony_ci NULL 91462306a36Sopenharmony_ci}; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic const struct attribute_group auth_attr_group = { 91762306a36Sopenharmony_ci .is_visible = auth_attr_is_visible, 91862306a36Sopenharmony_ci .attrs = auth_attrs, 91962306a36Sopenharmony_ci}; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci/* ---- Attributes sysfs --------------------------------------------------------- */ 92262306a36Sopenharmony_cistatic ssize_t display_name_show(struct kobject *kobj, struct kobj_attribute *attr, 92362306a36Sopenharmony_ci char *buf) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", setting->display_name); 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 93362306a36Sopenharmony_ci char *item, *value, *p; 93462306a36Sopenharmony_ci int ret; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci ret = tlmi_setting(setting->index, &item, LENOVO_BIOS_SETTING_GUID); 93762306a36Sopenharmony_ci if (ret) 93862306a36Sopenharmony_ci return ret; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* validate and split from `item,value` -> `value` */ 94162306a36Sopenharmony_ci value = strpbrk(item, ","); 94262306a36Sopenharmony_ci if (!value || value == item || !strlen(value + 1)) 94362306a36Sopenharmony_ci ret = -EINVAL; 94462306a36Sopenharmony_ci else { 94562306a36Sopenharmony_ci /* On Workstations remove the Options part after the value */ 94662306a36Sopenharmony_ci p = strchrnul(value, ';'); 94762306a36Sopenharmony_ci *p = '\0'; 94862306a36Sopenharmony_ci ret = sysfs_emit(buf, "%s\n", value + 1); 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci kfree(item); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci return ret; 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic ssize_t possible_values_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", setting->possible_values); 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_cistatic ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, 96362306a36Sopenharmony_ci char *buf) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (setting->possible_values) { 96862306a36Sopenharmony_ci /* Figure out what setting type is as BIOS does not return this */ 96962306a36Sopenharmony_ci if (strchr(setting->possible_values, ';')) 97062306a36Sopenharmony_ci return sysfs_emit(buf, "enumeration\n"); 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci /* Anything else is going to be a string */ 97362306a36Sopenharmony_ci return sysfs_emit(buf, "string\n"); 97462306a36Sopenharmony_ci} 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic ssize_t current_value_store(struct kobject *kobj, 97762306a36Sopenharmony_ci struct kobj_attribute *attr, 97862306a36Sopenharmony_ci const char *buf, size_t count) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 98162306a36Sopenharmony_ci char *set_str = NULL, *new_setting = NULL; 98262306a36Sopenharmony_ci char *auth_str = NULL; 98362306a36Sopenharmony_ci int ret; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci if (!tlmi_priv.can_set_bios_settings) 98662306a36Sopenharmony_ci return -EOPNOTSUPP; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci new_setting = kstrdup(buf, GFP_KERNEL); 98962306a36Sopenharmony_ci if (!new_setting) 99062306a36Sopenharmony_ci return -ENOMEM; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* Strip out CR if one is present */ 99362306a36Sopenharmony_ci strip_cr(new_setting); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci /* Use lock in case multiple WMI operations needed */ 99662306a36Sopenharmony_ci mutex_lock(&tlmi_mutex); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* Check if certificate authentication is enabled and active */ 99962306a36Sopenharmony_ci if (tlmi_priv.certificate_support && tlmi_priv.pwd_admin->cert_installed) { 100062306a36Sopenharmony_ci if (!tlmi_priv.pwd_admin->signature || !tlmi_priv.pwd_admin->save_signature) { 100162306a36Sopenharmony_ci ret = -EINVAL; 100262306a36Sopenharmony_ci goto out; 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci set_str = kasprintf(GFP_KERNEL, "%s,%s,%s", setting->display_name, 100562306a36Sopenharmony_ci new_setting, tlmi_priv.pwd_admin->signature); 100662306a36Sopenharmony_ci if (!set_str) { 100762306a36Sopenharmony_ci ret = -ENOMEM; 100862306a36Sopenharmony_ci goto out; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTING_CERT_GUID, set_str); 101262306a36Sopenharmony_ci if (ret) 101362306a36Sopenharmony_ci goto out; 101462306a36Sopenharmony_ci ret = tlmi_simple_call(LENOVO_SAVE_BIOS_SETTING_CERT_GUID, 101562306a36Sopenharmony_ci tlmi_priv.pwd_admin->save_signature); 101662306a36Sopenharmony_ci if (ret) 101762306a36Sopenharmony_ci goto out; 101862306a36Sopenharmony_ci } else if (tlmi_priv.opcode_support) { 101962306a36Sopenharmony_ci /* 102062306a36Sopenharmony_ci * If opcode support is present use that interface. 102162306a36Sopenharmony_ci * Note - this sets the variable and then the password as separate 102262306a36Sopenharmony_ci * WMI calls. Function tlmi_save_bios_settings will error if the 102362306a36Sopenharmony_ci * password is incorrect. 102462306a36Sopenharmony_ci */ 102562306a36Sopenharmony_ci set_str = kasprintf(GFP_KERNEL, "%s,%s;", setting->display_name, 102662306a36Sopenharmony_ci new_setting); 102762306a36Sopenharmony_ci if (!set_str) { 102862306a36Sopenharmony_ci ret = -ENOMEM; 102962306a36Sopenharmony_ci goto out; 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTINGS_GUID, set_str); 103362306a36Sopenharmony_ci if (ret) 103462306a36Sopenharmony_ci goto out; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { 103762306a36Sopenharmony_ci ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin", 103862306a36Sopenharmony_ci tlmi_priv.pwd_admin->password); 103962306a36Sopenharmony_ci if (ret) 104062306a36Sopenharmony_ci goto out; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci ret = tlmi_save_bios_settings(""); 104462306a36Sopenharmony_ci } else { /* old non-opcode based authentication method (deprecated) */ 104562306a36Sopenharmony_ci if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { 104662306a36Sopenharmony_ci auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;", 104762306a36Sopenharmony_ci tlmi_priv.pwd_admin->password, 104862306a36Sopenharmony_ci encoding_options[tlmi_priv.pwd_admin->encoding], 104962306a36Sopenharmony_ci tlmi_priv.pwd_admin->kbdlang); 105062306a36Sopenharmony_ci if (!auth_str) { 105162306a36Sopenharmony_ci ret = -ENOMEM; 105262306a36Sopenharmony_ci goto out; 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci if (auth_str) 105762306a36Sopenharmony_ci set_str = kasprintf(GFP_KERNEL, "%s,%s,%s", setting->display_name, 105862306a36Sopenharmony_ci new_setting, auth_str); 105962306a36Sopenharmony_ci else 106062306a36Sopenharmony_ci set_str = kasprintf(GFP_KERNEL, "%s,%s;", setting->display_name, 106162306a36Sopenharmony_ci new_setting); 106262306a36Sopenharmony_ci if (!set_str) { 106362306a36Sopenharmony_ci ret = -ENOMEM; 106462306a36Sopenharmony_ci goto out; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTINGS_GUID, set_str); 106862306a36Sopenharmony_ci if (ret) 106962306a36Sopenharmony_ci goto out; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (auth_str) 107262306a36Sopenharmony_ci ret = tlmi_save_bios_settings(auth_str); 107362306a36Sopenharmony_ci else 107462306a36Sopenharmony_ci ret = tlmi_save_bios_settings(""); 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci if (!ret && !tlmi_priv.pending_changes) { 107762306a36Sopenharmony_ci tlmi_priv.pending_changes = true; 107862306a36Sopenharmony_ci /* let userland know it may need to check reboot pending again */ 107962306a36Sopenharmony_ci kobject_uevent(&tlmi_priv.class_dev->kobj, KOBJ_CHANGE); 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ciout: 108262306a36Sopenharmony_ci mutex_unlock(&tlmi_mutex); 108362306a36Sopenharmony_ci kfree(auth_str); 108462306a36Sopenharmony_ci kfree(set_str); 108562306a36Sopenharmony_ci kfree(new_setting); 108662306a36Sopenharmony_ci return ret ?: count; 108762306a36Sopenharmony_ci} 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_cistatic struct kobj_attribute attr_displ_name = __ATTR_RO(display_name); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_cistatic struct kobj_attribute attr_possible_values = __ATTR_RO(possible_values); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic struct kobj_attribute attr_current_val = __ATTR_RW_MODE(current_value, 0600); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cistatic struct kobj_attribute attr_type = __ATTR_RO(type); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic umode_t attr_is_visible(struct kobject *kobj, 109862306a36Sopenharmony_ci struct attribute *attr, int n) 109962306a36Sopenharmony_ci{ 110062306a36Sopenharmony_ci struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci /* We don't want to display possible_values attributes if not available */ 110362306a36Sopenharmony_ci if ((attr == &attr_possible_values.attr) && (!setting->possible_values)) 110462306a36Sopenharmony_ci return 0; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci return attr->mode; 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistatic struct attribute *tlmi_attrs[] = { 111062306a36Sopenharmony_ci &attr_displ_name.attr, 111162306a36Sopenharmony_ci &attr_current_val.attr, 111262306a36Sopenharmony_ci &attr_possible_values.attr, 111362306a36Sopenharmony_ci &attr_type.attr, 111462306a36Sopenharmony_ci NULL 111562306a36Sopenharmony_ci}; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_cistatic const struct attribute_group tlmi_attr_group = { 111862306a36Sopenharmony_ci .is_visible = attr_is_visible, 111962306a36Sopenharmony_ci .attrs = tlmi_attrs, 112062306a36Sopenharmony_ci}; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_cistatic void tlmi_attr_setting_release(struct kobject *kobj) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci kfree(setting->possible_values); 112762306a36Sopenharmony_ci kfree(setting); 112862306a36Sopenharmony_ci} 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic void tlmi_pwd_setting_release(struct kobject *kobj) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci kfree(setting); 113562306a36Sopenharmony_ci} 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_cistatic const struct kobj_type tlmi_attr_setting_ktype = { 113862306a36Sopenharmony_ci .release = &tlmi_attr_setting_release, 113962306a36Sopenharmony_ci .sysfs_ops = &kobj_sysfs_ops, 114062306a36Sopenharmony_ci}; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_cistatic const struct kobj_type tlmi_pwd_setting_ktype = { 114362306a36Sopenharmony_ci .release = &tlmi_pwd_setting_release, 114462306a36Sopenharmony_ci .sysfs_ops = &kobj_sysfs_ops, 114562306a36Sopenharmony_ci}; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_cistatic ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, 114862306a36Sopenharmony_ci char *buf) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci return sprintf(buf, "%d\n", tlmi_priv.pending_changes); 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci/* ---- Debug interface--------------------------------------------------------- */ 115662306a36Sopenharmony_cistatic ssize_t debug_cmd_store(struct kobject *kobj, struct kobj_attribute *attr, 115762306a36Sopenharmony_ci const char *buf, size_t count) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci char *set_str = NULL, *new_setting = NULL; 116062306a36Sopenharmony_ci char *auth_str = NULL; 116162306a36Sopenharmony_ci int ret; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci if (!tlmi_priv.can_debug_cmd) 116462306a36Sopenharmony_ci return -EOPNOTSUPP; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci new_setting = kstrdup(buf, GFP_KERNEL); 116762306a36Sopenharmony_ci if (!new_setting) 116862306a36Sopenharmony_ci return -ENOMEM; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci /* Strip out CR if one is present */ 117162306a36Sopenharmony_ci strip_cr(new_setting); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { 117462306a36Sopenharmony_ci auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;", 117562306a36Sopenharmony_ci tlmi_priv.pwd_admin->password, 117662306a36Sopenharmony_ci encoding_options[tlmi_priv.pwd_admin->encoding], 117762306a36Sopenharmony_ci tlmi_priv.pwd_admin->kbdlang); 117862306a36Sopenharmony_ci if (!auth_str) { 117962306a36Sopenharmony_ci ret = -ENOMEM; 118062306a36Sopenharmony_ci goto out; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci if (auth_str) 118562306a36Sopenharmony_ci set_str = kasprintf(GFP_KERNEL, "%s,%s", new_setting, auth_str); 118662306a36Sopenharmony_ci else 118762306a36Sopenharmony_ci set_str = kasprintf(GFP_KERNEL, "%s;", new_setting); 118862306a36Sopenharmony_ci if (!set_str) { 118962306a36Sopenharmony_ci ret = -ENOMEM; 119062306a36Sopenharmony_ci goto out; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci ret = tlmi_simple_call(LENOVO_DEBUG_CMD_GUID, set_str); 119462306a36Sopenharmony_ci if (ret) 119562306a36Sopenharmony_ci goto out; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci if (!ret && !tlmi_priv.pending_changes) { 119862306a36Sopenharmony_ci tlmi_priv.pending_changes = true; 119962306a36Sopenharmony_ci /* let userland know it may need to check reboot pending again */ 120062306a36Sopenharmony_ci kobject_uevent(&tlmi_priv.class_dev->kobj, KOBJ_CHANGE); 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ciout: 120362306a36Sopenharmony_ci kfree(auth_str); 120462306a36Sopenharmony_ci kfree(set_str); 120562306a36Sopenharmony_ci kfree(new_setting); 120662306a36Sopenharmony_ci return ret ?: count; 120762306a36Sopenharmony_ci} 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_cistatic struct kobj_attribute debug_cmd = __ATTR_WO(debug_cmd); 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci/* ---- Initialisation --------------------------------------------------------- */ 121262306a36Sopenharmony_cistatic void tlmi_release_attr(void) 121362306a36Sopenharmony_ci{ 121462306a36Sopenharmony_ci int i; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci /* Attribute structures */ 121762306a36Sopenharmony_ci for (i = 0; i < TLMI_SETTINGS_COUNT; i++) { 121862306a36Sopenharmony_ci if (tlmi_priv.setting[i]) { 121962306a36Sopenharmony_ci sysfs_remove_group(&tlmi_priv.setting[i]->kobj, &tlmi_attr_group); 122062306a36Sopenharmony_ci kobject_put(&tlmi_priv.setting[i]->kobj); 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci } 122362306a36Sopenharmony_ci sysfs_remove_file(&tlmi_priv.attribute_kset->kobj, &pending_reboot.attr); 122462306a36Sopenharmony_ci if (tlmi_priv.can_debug_cmd && debug_support) 122562306a36Sopenharmony_ci sysfs_remove_file(&tlmi_priv.attribute_kset->kobj, &debug_cmd.attr); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci kset_unregister(tlmi_priv.attribute_kset); 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci /* Free up any saved signatures */ 123062306a36Sopenharmony_ci kfree(tlmi_priv.pwd_admin->signature); 123162306a36Sopenharmony_ci kfree(tlmi_priv.pwd_admin->save_signature); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci /* Authentication structures */ 123462306a36Sopenharmony_ci sysfs_remove_group(&tlmi_priv.pwd_admin->kobj, &auth_attr_group); 123562306a36Sopenharmony_ci kobject_put(&tlmi_priv.pwd_admin->kobj); 123662306a36Sopenharmony_ci sysfs_remove_group(&tlmi_priv.pwd_power->kobj, &auth_attr_group); 123762306a36Sopenharmony_ci kobject_put(&tlmi_priv.pwd_power->kobj); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci if (tlmi_priv.opcode_support) { 124062306a36Sopenharmony_ci sysfs_remove_group(&tlmi_priv.pwd_system->kobj, &auth_attr_group); 124162306a36Sopenharmony_ci kobject_put(&tlmi_priv.pwd_system->kobj); 124262306a36Sopenharmony_ci sysfs_remove_group(&tlmi_priv.pwd_hdd->kobj, &auth_attr_group); 124362306a36Sopenharmony_ci kobject_put(&tlmi_priv.pwd_hdd->kobj); 124462306a36Sopenharmony_ci sysfs_remove_group(&tlmi_priv.pwd_nvme->kobj, &auth_attr_group); 124562306a36Sopenharmony_ci kobject_put(&tlmi_priv.pwd_nvme->kobj); 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci kset_unregister(tlmi_priv.authentication_kset); 124962306a36Sopenharmony_ci} 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_cistatic int tlmi_validate_setting_name(struct kset *attribute_kset, char *name) 125262306a36Sopenharmony_ci{ 125362306a36Sopenharmony_ci struct kobject *duplicate; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci if (!strcmp(name, "Reserved")) 125662306a36Sopenharmony_ci return -EINVAL; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci duplicate = kset_find_obj(attribute_kset, name); 125962306a36Sopenharmony_ci if (duplicate) { 126062306a36Sopenharmony_ci pr_debug("Duplicate attribute name found - %s\n", name); 126162306a36Sopenharmony_ci /* kset_find_obj() returns a reference */ 126262306a36Sopenharmony_ci kobject_put(duplicate); 126362306a36Sopenharmony_ci return -EBUSY; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci return 0; 126762306a36Sopenharmony_ci} 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_cistatic int tlmi_sysfs_init(void) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci int i, ret; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci ret = fw_attributes_class_get(&fw_attr_class); 127462306a36Sopenharmony_ci if (ret) 127562306a36Sopenharmony_ci return ret; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci tlmi_priv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0), 127862306a36Sopenharmony_ci NULL, "%s", "thinklmi"); 127962306a36Sopenharmony_ci if (IS_ERR(tlmi_priv.class_dev)) { 128062306a36Sopenharmony_ci ret = PTR_ERR(tlmi_priv.class_dev); 128162306a36Sopenharmony_ci goto fail_class_created; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci tlmi_priv.attribute_kset = kset_create_and_add("attributes", NULL, 128562306a36Sopenharmony_ci &tlmi_priv.class_dev->kobj); 128662306a36Sopenharmony_ci if (!tlmi_priv.attribute_kset) { 128762306a36Sopenharmony_ci ret = -ENOMEM; 128862306a36Sopenharmony_ci goto fail_device_created; 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci for (i = 0; i < TLMI_SETTINGS_COUNT; i++) { 129262306a36Sopenharmony_ci /* Check if index is a valid setting - skip if it isn't */ 129362306a36Sopenharmony_ci if (!tlmi_priv.setting[i]) 129462306a36Sopenharmony_ci continue; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci /* check for duplicate or reserved values */ 129762306a36Sopenharmony_ci if (tlmi_validate_setting_name(tlmi_priv.attribute_kset, 129862306a36Sopenharmony_ci tlmi_priv.setting[i]->display_name) < 0) { 129962306a36Sopenharmony_ci kfree(tlmi_priv.setting[i]->possible_values); 130062306a36Sopenharmony_ci kfree(tlmi_priv.setting[i]); 130162306a36Sopenharmony_ci tlmi_priv.setting[i] = NULL; 130262306a36Sopenharmony_ci continue; 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci /* Build attribute */ 130662306a36Sopenharmony_ci tlmi_priv.setting[i]->kobj.kset = tlmi_priv.attribute_kset; 130762306a36Sopenharmony_ci ret = kobject_add(&tlmi_priv.setting[i]->kobj, NULL, 130862306a36Sopenharmony_ci "%s", tlmi_priv.setting[i]->display_name); 130962306a36Sopenharmony_ci if (ret) 131062306a36Sopenharmony_ci goto fail_create_attr; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci ret = sysfs_create_group(&tlmi_priv.setting[i]->kobj, &tlmi_attr_group); 131362306a36Sopenharmony_ci if (ret) 131462306a36Sopenharmony_ci goto fail_create_attr; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci ret = sysfs_create_file(&tlmi_priv.attribute_kset->kobj, &pending_reboot.attr); 131862306a36Sopenharmony_ci if (ret) 131962306a36Sopenharmony_ci goto fail_create_attr; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci if (tlmi_priv.can_debug_cmd && debug_support) { 132262306a36Sopenharmony_ci ret = sysfs_create_file(&tlmi_priv.attribute_kset->kobj, &debug_cmd.attr); 132362306a36Sopenharmony_ci if (ret) 132462306a36Sopenharmony_ci goto fail_create_attr; 132562306a36Sopenharmony_ci } 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci /* Create authentication entries */ 132862306a36Sopenharmony_ci tlmi_priv.authentication_kset = kset_create_and_add("authentication", NULL, 132962306a36Sopenharmony_ci &tlmi_priv.class_dev->kobj); 133062306a36Sopenharmony_ci if (!tlmi_priv.authentication_kset) { 133162306a36Sopenharmony_ci ret = -ENOMEM; 133262306a36Sopenharmony_ci goto fail_create_attr; 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci tlmi_priv.pwd_admin->kobj.kset = tlmi_priv.authentication_kset; 133562306a36Sopenharmony_ci ret = kobject_add(&tlmi_priv.pwd_admin->kobj, NULL, "%s", "Admin"); 133662306a36Sopenharmony_ci if (ret) 133762306a36Sopenharmony_ci goto fail_create_attr; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci ret = sysfs_create_group(&tlmi_priv.pwd_admin->kobj, &auth_attr_group); 134062306a36Sopenharmony_ci if (ret) 134162306a36Sopenharmony_ci goto fail_create_attr; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci tlmi_priv.pwd_power->kobj.kset = tlmi_priv.authentication_kset; 134462306a36Sopenharmony_ci ret = kobject_add(&tlmi_priv.pwd_power->kobj, NULL, "%s", "Power-on"); 134562306a36Sopenharmony_ci if (ret) 134662306a36Sopenharmony_ci goto fail_create_attr; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci ret = sysfs_create_group(&tlmi_priv.pwd_power->kobj, &auth_attr_group); 134962306a36Sopenharmony_ci if (ret) 135062306a36Sopenharmony_ci goto fail_create_attr; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (tlmi_priv.opcode_support) { 135362306a36Sopenharmony_ci tlmi_priv.pwd_system->kobj.kset = tlmi_priv.authentication_kset; 135462306a36Sopenharmony_ci ret = kobject_add(&tlmi_priv.pwd_system->kobj, NULL, "%s", "System"); 135562306a36Sopenharmony_ci if (ret) 135662306a36Sopenharmony_ci goto fail_create_attr; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci ret = sysfs_create_group(&tlmi_priv.pwd_system->kobj, &auth_attr_group); 135962306a36Sopenharmony_ci if (ret) 136062306a36Sopenharmony_ci goto fail_create_attr; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci tlmi_priv.pwd_hdd->kobj.kset = tlmi_priv.authentication_kset; 136362306a36Sopenharmony_ci ret = kobject_add(&tlmi_priv.pwd_hdd->kobj, NULL, "%s", "HDD"); 136462306a36Sopenharmony_ci if (ret) 136562306a36Sopenharmony_ci goto fail_create_attr; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci ret = sysfs_create_group(&tlmi_priv.pwd_hdd->kobj, &auth_attr_group); 136862306a36Sopenharmony_ci if (ret) 136962306a36Sopenharmony_ci goto fail_create_attr; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci tlmi_priv.pwd_nvme->kobj.kset = tlmi_priv.authentication_kset; 137262306a36Sopenharmony_ci ret = kobject_add(&tlmi_priv.pwd_nvme->kobj, NULL, "%s", "NVMe"); 137362306a36Sopenharmony_ci if (ret) 137462306a36Sopenharmony_ci goto fail_create_attr; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci ret = sysfs_create_group(&tlmi_priv.pwd_nvme->kobj, &auth_attr_group); 137762306a36Sopenharmony_ci if (ret) 137862306a36Sopenharmony_ci goto fail_create_attr; 137962306a36Sopenharmony_ci } 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci return ret; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_cifail_create_attr: 138462306a36Sopenharmony_ci tlmi_release_attr(); 138562306a36Sopenharmony_cifail_device_created: 138662306a36Sopenharmony_ci device_destroy(fw_attr_class, MKDEV(0, 0)); 138762306a36Sopenharmony_cifail_class_created: 138862306a36Sopenharmony_ci fw_attributes_class_put(); 138962306a36Sopenharmony_ci return ret; 139062306a36Sopenharmony_ci} 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci/* ---- Base Driver -------------------------------------------------------- */ 139362306a36Sopenharmony_cistatic struct tlmi_pwd_setting *tlmi_create_auth(const char *pwd_type, 139462306a36Sopenharmony_ci const char *pwd_role) 139562306a36Sopenharmony_ci{ 139662306a36Sopenharmony_ci struct tlmi_pwd_setting *new_pwd; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci new_pwd = kzalloc(sizeof(struct tlmi_pwd_setting), GFP_KERNEL); 139962306a36Sopenharmony_ci if (!new_pwd) 140062306a36Sopenharmony_ci return NULL; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci strscpy(new_pwd->kbdlang, "us", TLMI_LANG_MAXLEN); 140362306a36Sopenharmony_ci new_pwd->encoding = TLMI_ENCODING_ASCII; 140462306a36Sopenharmony_ci new_pwd->pwd_type = pwd_type; 140562306a36Sopenharmony_ci new_pwd->role = pwd_role; 140662306a36Sopenharmony_ci new_pwd->minlen = tlmi_priv.pwdcfg.core.min_length; 140762306a36Sopenharmony_ci new_pwd->maxlen = tlmi_priv.pwdcfg.core.max_length; 140862306a36Sopenharmony_ci new_pwd->index = 0; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci kobject_init(&new_pwd->kobj, &tlmi_pwd_setting_ktype); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci return new_pwd; 141362306a36Sopenharmony_ci} 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_cistatic int tlmi_analyze(void) 141662306a36Sopenharmony_ci{ 141762306a36Sopenharmony_ci int i, ret; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci if (wmi_has_guid(LENOVO_SET_BIOS_SETTINGS_GUID) && 142062306a36Sopenharmony_ci wmi_has_guid(LENOVO_SAVE_BIOS_SETTINGS_GUID)) 142162306a36Sopenharmony_ci tlmi_priv.can_set_bios_settings = true; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci if (wmi_has_guid(LENOVO_GET_BIOS_SELECTIONS_GUID)) 142462306a36Sopenharmony_ci tlmi_priv.can_get_bios_selections = true; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci if (wmi_has_guid(LENOVO_SET_BIOS_PASSWORD_GUID)) 142762306a36Sopenharmony_ci tlmi_priv.can_set_bios_password = true; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci if (wmi_has_guid(LENOVO_BIOS_PASSWORD_SETTINGS_GUID)) 143062306a36Sopenharmony_ci tlmi_priv.can_get_password_settings = true; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci if (wmi_has_guid(LENOVO_DEBUG_CMD_GUID)) 143362306a36Sopenharmony_ci tlmi_priv.can_debug_cmd = true; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci if (wmi_has_guid(LENOVO_OPCODE_IF_GUID)) 143662306a36Sopenharmony_ci tlmi_priv.opcode_support = true; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci if (wmi_has_guid(LENOVO_SET_BIOS_CERT_GUID) && 143962306a36Sopenharmony_ci wmi_has_guid(LENOVO_SET_BIOS_SETTING_CERT_GUID) && 144062306a36Sopenharmony_ci wmi_has_guid(LENOVO_SAVE_BIOS_SETTING_CERT_GUID)) 144162306a36Sopenharmony_ci tlmi_priv.certificate_support = true; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci /* 144462306a36Sopenharmony_ci * Try to find the number of valid settings of this machine 144562306a36Sopenharmony_ci * and use it to create sysfs attributes. 144662306a36Sopenharmony_ci */ 144762306a36Sopenharmony_ci for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) { 144862306a36Sopenharmony_ci struct tlmi_attr_setting *setting; 144962306a36Sopenharmony_ci char *item = NULL; 145062306a36Sopenharmony_ci char *p; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci tlmi_priv.setting[i] = NULL; 145362306a36Sopenharmony_ci ret = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID); 145462306a36Sopenharmony_ci if (ret) 145562306a36Sopenharmony_ci break; 145662306a36Sopenharmony_ci if (!item) 145762306a36Sopenharmony_ci break; 145862306a36Sopenharmony_ci if (!*item) { 145962306a36Sopenharmony_ci kfree(item); 146062306a36Sopenharmony_ci continue; 146162306a36Sopenharmony_ci } 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci /* It is not allowed to have '/' for file name. Convert it into '\'. */ 146462306a36Sopenharmony_ci strreplace(item, '/', '\\'); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci /* Remove the value part */ 146762306a36Sopenharmony_ci p = strchrnul(item, ','); 146862306a36Sopenharmony_ci *p = '\0'; 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci /* Create a setting entry */ 147162306a36Sopenharmony_ci setting = kzalloc(sizeof(*setting), GFP_KERNEL); 147262306a36Sopenharmony_ci if (!setting) { 147362306a36Sopenharmony_ci ret = -ENOMEM; 147462306a36Sopenharmony_ci kfree(item); 147562306a36Sopenharmony_ci goto fail_clear_attr; 147662306a36Sopenharmony_ci } 147762306a36Sopenharmony_ci setting->index = i; 147862306a36Sopenharmony_ci strscpy(setting->display_name, item, TLMI_SETTINGS_MAXLEN); 147962306a36Sopenharmony_ci /* If BIOS selections supported, load those */ 148062306a36Sopenharmony_ci if (tlmi_priv.can_get_bios_selections) { 148162306a36Sopenharmony_ci ret = tlmi_get_bios_selections(setting->display_name, 148262306a36Sopenharmony_ci &setting->possible_values); 148362306a36Sopenharmony_ci if (ret || !setting->possible_values) 148462306a36Sopenharmony_ci pr_info("Error retrieving possible values for %d : %s\n", 148562306a36Sopenharmony_ci i, setting->display_name); 148662306a36Sopenharmony_ci } else { 148762306a36Sopenharmony_ci /* 148862306a36Sopenharmony_ci * Older Thinkstations don't support the bios_selections API. 148962306a36Sopenharmony_ci * Instead they store this as a [Optional:Option1,Option2] section of the 149062306a36Sopenharmony_ci * name string. 149162306a36Sopenharmony_ci * Try and pull that out if it's available. 149262306a36Sopenharmony_ci */ 149362306a36Sopenharmony_ci char *optitem, *optstart, *optend; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci if (!tlmi_setting(setting->index, &optitem, LENOVO_BIOS_SETTING_GUID)) { 149662306a36Sopenharmony_ci optstart = strstr(optitem, "[Optional:"); 149762306a36Sopenharmony_ci if (optstart) { 149862306a36Sopenharmony_ci optstart += strlen("[Optional:"); 149962306a36Sopenharmony_ci optend = strstr(optstart, "]"); 150062306a36Sopenharmony_ci if (optend) 150162306a36Sopenharmony_ci setting->possible_values = 150262306a36Sopenharmony_ci kstrndup(optstart, optend - optstart, 150362306a36Sopenharmony_ci GFP_KERNEL); 150462306a36Sopenharmony_ci } 150562306a36Sopenharmony_ci kfree(optitem); 150662306a36Sopenharmony_ci } 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci /* 150962306a36Sopenharmony_ci * firmware-attributes requires that possible_values are separated by ';' but 151062306a36Sopenharmony_ci * Lenovo FW uses ','. Replace appropriately. 151162306a36Sopenharmony_ci */ 151262306a36Sopenharmony_ci if (setting->possible_values) 151362306a36Sopenharmony_ci strreplace(setting->possible_values, ',', ';'); 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci kobject_init(&setting->kobj, &tlmi_attr_setting_ktype); 151662306a36Sopenharmony_ci tlmi_priv.setting[i] = setting; 151762306a36Sopenharmony_ci kfree(item); 151862306a36Sopenharmony_ci } 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci /* Create password setting structure */ 152162306a36Sopenharmony_ci ret = tlmi_get_pwd_settings(&tlmi_priv.pwdcfg); 152262306a36Sopenharmony_ci if (ret) 152362306a36Sopenharmony_ci goto fail_clear_attr; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci /* All failures below boil down to kmalloc failures */ 152662306a36Sopenharmony_ci ret = -ENOMEM; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci tlmi_priv.pwd_admin = tlmi_create_auth("pap", "bios-admin"); 152962306a36Sopenharmony_ci if (!tlmi_priv.pwd_admin) 153062306a36Sopenharmony_ci goto fail_clear_attr; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci if (tlmi_priv.pwdcfg.core.password_state & TLMI_PAP_PWD) 153362306a36Sopenharmony_ci tlmi_priv.pwd_admin->valid = true; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci tlmi_priv.pwd_power = tlmi_create_auth("pop", "power-on"); 153662306a36Sopenharmony_ci if (!tlmi_priv.pwd_power) 153762306a36Sopenharmony_ci goto fail_clear_attr; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci if (tlmi_priv.pwdcfg.core.password_state & TLMI_POP_PWD) 154062306a36Sopenharmony_ci tlmi_priv.pwd_power->valid = true; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci if (tlmi_priv.opcode_support) { 154362306a36Sopenharmony_ci tlmi_priv.pwd_system = tlmi_create_auth("smp", "system"); 154462306a36Sopenharmony_ci if (!tlmi_priv.pwd_system) 154562306a36Sopenharmony_ci goto fail_clear_attr; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci if (tlmi_priv.pwdcfg.core.password_state & TLMI_SMP_PWD) 154862306a36Sopenharmony_ci tlmi_priv.pwd_system->valid = true; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci tlmi_priv.pwd_hdd = tlmi_create_auth("hdd", "hdd"); 155162306a36Sopenharmony_ci if (!tlmi_priv.pwd_hdd) 155262306a36Sopenharmony_ci goto fail_clear_attr; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci tlmi_priv.pwd_nvme = tlmi_create_auth("nvm", "nvme"); 155562306a36Sopenharmony_ci if (!tlmi_priv.pwd_nvme) 155662306a36Sopenharmony_ci goto fail_clear_attr; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci /* Set default hdd/nvme index to 1 as there is no device 0 */ 155962306a36Sopenharmony_ci tlmi_priv.pwd_hdd->index = 1; 156062306a36Sopenharmony_ci tlmi_priv.pwd_nvme->index = 1; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci if (tlmi_priv.pwdcfg.core.password_state & TLMI_HDD_PWD) { 156362306a36Sopenharmony_ci /* Check if PWD is configured and set index to first drive found */ 156462306a36Sopenharmony_ci if (tlmi_priv.pwdcfg.ext.hdd_user_password || 156562306a36Sopenharmony_ci tlmi_priv.pwdcfg.ext.hdd_master_password) { 156662306a36Sopenharmony_ci tlmi_priv.pwd_hdd->valid = true; 156762306a36Sopenharmony_ci if (tlmi_priv.pwdcfg.ext.hdd_master_password) 156862306a36Sopenharmony_ci tlmi_priv.pwd_hdd->index = 156962306a36Sopenharmony_ci ffs(tlmi_priv.pwdcfg.ext.hdd_master_password) - 1; 157062306a36Sopenharmony_ci else 157162306a36Sopenharmony_ci tlmi_priv.pwd_hdd->index = 157262306a36Sopenharmony_ci ffs(tlmi_priv.pwdcfg.ext.hdd_user_password) - 1; 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci if (tlmi_priv.pwdcfg.ext.nvme_user_password || 157562306a36Sopenharmony_ci tlmi_priv.pwdcfg.ext.nvme_master_password) { 157662306a36Sopenharmony_ci tlmi_priv.pwd_nvme->valid = true; 157762306a36Sopenharmony_ci if (tlmi_priv.pwdcfg.ext.nvme_master_password) 157862306a36Sopenharmony_ci tlmi_priv.pwd_nvme->index = 157962306a36Sopenharmony_ci ffs(tlmi_priv.pwdcfg.ext.nvme_master_password) - 1; 158062306a36Sopenharmony_ci else 158162306a36Sopenharmony_ci tlmi_priv.pwd_nvme->index = 158262306a36Sopenharmony_ci ffs(tlmi_priv.pwdcfg.ext.nvme_user_password) - 1; 158362306a36Sopenharmony_ci } 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci } 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci if (tlmi_priv.certificate_support && 158862306a36Sopenharmony_ci (tlmi_priv.pwdcfg.core.password_state & TLMI_CERT)) 158962306a36Sopenharmony_ci tlmi_priv.pwd_admin->cert_installed = true; 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci return 0; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_cifail_clear_attr: 159462306a36Sopenharmony_ci for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) { 159562306a36Sopenharmony_ci if (tlmi_priv.setting[i]) { 159662306a36Sopenharmony_ci kfree(tlmi_priv.setting[i]->possible_values); 159762306a36Sopenharmony_ci kfree(tlmi_priv.setting[i]); 159862306a36Sopenharmony_ci } 159962306a36Sopenharmony_ci } 160062306a36Sopenharmony_ci kfree(tlmi_priv.pwd_admin); 160162306a36Sopenharmony_ci kfree(tlmi_priv.pwd_power); 160262306a36Sopenharmony_ci kfree(tlmi_priv.pwd_system); 160362306a36Sopenharmony_ci kfree(tlmi_priv.pwd_hdd); 160462306a36Sopenharmony_ci kfree(tlmi_priv.pwd_nvme); 160562306a36Sopenharmony_ci return ret; 160662306a36Sopenharmony_ci} 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_cistatic void tlmi_remove(struct wmi_device *wdev) 160962306a36Sopenharmony_ci{ 161062306a36Sopenharmony_ci tlmi_release_attr(); 161162306a36Sopenharmony_ci device_destroy(fw_attr_class, MKDEV(0, 0)); 161262306a36Sopenharmony_ci fw_attributes_class_put(); 161362306a36Sopenharmony_ci} 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_cistatic int tlmi_probe(struct wmi_device *wdev, const void *context) 161662306a36Sopenharmony_ci{ 161762306a36Sopenharmony_ci int ret; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci ret = tlmi_analyze(); 162062306a36Sopenharmony_ci if (ret) 162162306a36Sopenharmony_ci return ret; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci return tlmi_sysfs_init(); 162462306a36Sopenharmony_ci} 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_cistatic const struct wmi_device_id tlmi_id_table[] = { 162762306a36Sopenharmony_ci { .guid_string = LENOVO_BIOS_SETTING_GUID }, 162862306a36Sopenharmony_ci { } 162962306a36Sopenharmony_ci}; 163062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(wmi, tlmi_id_table); 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_cistatic struct wmi_driver tlmi_driver = { 163362306a36Sopenharmony_ci .driver = { 163462306a36Sopenharmony_ci .name = "think-lmi", 163562306a36Sopenharmony_ci }, 163662306a36Sopenharmony_ci .id_table = tlmi_id_table, 163762306a36Sopenharmony_ci .probe = tlmi_probe, 163862306a36Sopenharmony_ci .remove = tlmi_remove, 163962306a36Sopenharmony_ci}; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ciMODULE_AUTHOR("Sugumaran L <slacshiminar@lenovo.com>"); 164262306a36Sopenharmony_ciMODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>"); 164362306a36Sopenharmony_ciMODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>"); 164462306a36Sopenharmony_ciMODULE_DESCRIPTION("ThinkLMI Driver"); 164562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_cimodule_wmi_driver(tlmi_driver); 1648