Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

platform/x86: think-lmi: Add WMI interface support on Lenovo platforms

For Lenovo platforms that support a WMI interface to the BIOS add
support, using the firmware-attributes class, to allow users to access
and modify various BIOS related settings.

Signed-off-by: Mark Pearson <markpearson@lenovo.com>
Link: https://lore.kernel.org/r/20210530223111.25929-3-markpearson@lenovo.com
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>

authored by

Mark Pearson and committed by
Hans de Goede
a40cd7ef 8a1c379c

+1008 -1
+17 -1
Documentation/ABI/testing/sysfs-class-firmware-attributes
··· 197 197 Drivers may emit a CHANGE uevent when a password is set or unset 198 198 userspace may check it again. 199 199 200 - On Dell systems, if Admin password is set, then all BIOS attributes 200 + On Dell and Lenovo systems, if Admin password is set, then all BIOS attributes 201 201 require password validation. 202 + On Lenovo systems if you change the Admin password the new password is not active until 203 + the next boot. 204 + 205 + Lenovo specific class extensions 206 + ------------------------------ 207 + 208 + On Lenovo systems the following additional settings are available: 209 + 210 + lenovo_encoding: 211 + The encoding method that is used. This can be either "ascii" 212 + or "scancode". Default is set to "ascii" 213 + 214 + lenovo_kbdlang: 215 + The keyboard language method that is used. This is generally a 216 + two char code (e.g. "us", "fr", "gr") and may vary per platform. 217 + Default is set to "us" 202 218 203 219 What: /sys/class/firmware-attributes/*/attributes/pending_reboot 204 220 Date: February 2021
+7
MAINTAINERS
··· 18163 18163 T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git 18164 18164 F: drivers/platform/x86/thinkpad_acpi.c 18165 18165 18166 + THINKPAD LMI DRIVER 18167 + M: Mark Pearson <markpearson@lenovo.com> 18168 + L: platform-driver-x86@vger.kernel.org 18169 + S: Maintained 18170 + F: Documentation/ABI/testing/sysfs-class-firmware-attributes 18171 + F: drivers/platform/x86/think-lmi.? 18172 + 18166 18173 THUNDERBOLT DMA TRAFFIC TEST DRIVER 18167 18174 M: Isaac Hazan <isaac.hazan@intel.com> 18168 18175 L: linux-usb@vger.kernel.org
+11
drivers/platform/x86/Kconfig
··· 640 640 If you are not sure, say Y here. The driver enables polling only if 641 641 it is strictly necessary to do so. 642 642 643 + config THINKPAD_LMI 644 + tristate "Lenovo WMI-based systems management driver" 645 + depends on ACPI_WMI 646 + select FW_ATTR_CLASS 647 + help 648 + This driver allows changing BIOS settings on Lenovo machines whose 649 + BIOS support the WMI interface. 650 + 651 + To compile this driver as a module, choose M here: the module will 652 + be called think-lmi. 653 + 643 654 config INTEL_ATOMISP2_LED 644 655 tristate "Intel AtomISP2 camera LED driver" 645 656 depends on GPIOLIB && LEDS_GPIO
+1
drivers/platform/x86/Makefile
··· 63 63 obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o 64 64 obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o 65 65 obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o 66 + obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o 66 67 67 68 # Intel 68 69 obj-$(CONFIG_INTEL_ATOMISP2_LED) += intel_atomisp2_led.o
+891
drivers/platform/x86/think-lmi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Think LMI BIOS configuration driver 4 + * 5 + * Copyright(C) 2019-2021 Lenovo 6 + * 7 + * Original code from Thinkpad-wmi project https://github.com/iksaif/thinkpad-wmi 8 + * Copyright(C) 2017 Corentin Chary <corentin.chary@gmail.com> 9 + * Distributed under the GPL-2.0 license 10 + */ 11 + 12 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13 + 14 + #include <linux/acpi.h> 15 + #include <linux/errno.h> 16 + #include <linux/fs.h> 17 + #include <linux/string.h> 18 + #include <linux/types.h> 19 + #include <linux/wmi.h> 20 + #include "firmware_attributes_class.h" 21 + #include "think-lmi.h" 22 + 23 + /* 24 + * Name: 25 + * Lenovo_BiosSetting 26 + * Description: 27 + * Get item name and settings for current LMI instance. 28 + * Type: 29 + * Query 30 + * Returns: 31 + * "Item,Value" 32 + * Example: 33 + * "WakeOnLAN,Enable" 34 + */ 35 + #define LENOVO_BIOS_SETTING_GUID "51F5230E-9677-46CD-A1CF-C0B23EE34DB7" 36 + 37 + /* 38 + * Name: 39 + * Lenovo_SetBiosSetting 40 + * Description: 41 + * Change the BIOS setting to the desired value using the Lenovo_SetBiosSetting 42 + * class. To save the settings, use the Lenovo_SaveBiosSetting class. 43 + * BIOS settings and values are case sensitive. 44 + * After making changes to the BIOS settings, you must reboot the computer 45 + * before the changes will take effect. 46 + * Type: 47 + * Method 48 + * Arguments: 49 + * "Item,Value,Password,Encoding,KbdLang;" 50 + * Example: 51 + * "WakeOnLAN,Disable,pa55w0rd,ascii,us;" 52 + */ 53 + #define LENOVO_SET_BIOS_SETTINGS_GUID "98479A64-33F5-4E33-A707-8E251EBBC3A1" 54 + 55 + /* 56 + * Name: 57 + * Lenovo_SaveBiosSettings 58 + * Description: 59 + * Save any pending changes in settings. 60 + * Type: 61 + * Method 62 + * Arguments: 63 + * "Password,Encoding,KbdLang;" 64 + * Example: 65 + * "pa55w0rd,ascii,us;" 66 + */ 67 + #define LENOVO_SAVE_BIOS_SETTINGS_GUID "6A4B54EF-A5ED-4D33-9455-B0D9B48DF4B3" 68 + 69 + /* 70 + * Name: 71 + * Lenovo_BiosPasswordSettings 72 + * Description: 73 + * Return BIOS Password settings 74 + * Type: 75 + * Query 76 + * Returns: 77 + * PasswordMode, PasswordState, MinLength, MaxLength, 78 + * SupportedEncoding, SupportedKeyboard 79 + */ 80 + #define LENOVO_BIOS_PASSWORD_SETTINGS_GUID "8ADB159E-1E32-455C-BC93-308A7ED98246" 81 + 82 + /* 83 + * Name: 84 + * Lenovo_SetBiosPassword 85 + * Description: 86 + * Change a specific password. 87 + * - BIOS settings cannot be changed at the same boot as power-on 88 + * passwords (POP) and hard disk passwords (HDP). If you want to change 89 + * BIOS settings and POP or HDP, you must reboot the system after changing 90 + * one of them. 91 + * - A password cannot be set using this method when one does not already 92 + * exist. Passwords can only be updated or cleared. 93 + * Type: 94 + * Method 95 + * Arguments: 96 + * "PasswordType,CurrentPassword,NewPassword,Encoding,KbdLang;" 97 + * Example: 98 + * "pop,pa55w0rd,newpa55w0rd,ascii,us;” 99 + */ 100 + #define LENOVO_SET_BIOS_PASSWORD_GUID "2651D9FD-911C-4B69-B94E-D0DED5963BD7" 101 + 102 + /* 103 + * Name: 104 + * Lenovo_GetBiosSelections 105 + * Description: 106 + * Return a list of valid settings for a given item. 107 + * Type: 108 + * Method 109 + * Arguments: 110 + * "Item" 111 + * Returns: 112 + * "Value1,Value2,Value3,..." 113 + * Example: 114 + * -> "FlashOverLAN" 115 + * <- "Enabled,Disabled" 116 + */ 117 + #define LENOVO_GET_BIOS_SELECTIONS_GUID "7364651A-132F-4FE7-ADAA-40C6C7EE2E3B" 118 + 119 + #define TLMI_POP_PWD (1 << 0) 120 + #define TLMI_PAP_PWD (1 << 1) 121 + #define to_tlmi_pwd_setting(kobj) container_of(kobj, struct tlmi_pwd_setting, kobj) 122 + #define to_tlmi_attr_setting(kobj) container_of(kobj, struct tlmi_attr_setting, kobj) 123 + 124 + static const struct tlmi_err_codes tlmi_errs[] = { 125 + {"Success", 0}, 126 + {"Not Supported", -EOPNOTSUPP}, 127 + {"Invalid Parameter", -EINVAL}, 128 + {"Access Denied", -EACCES}, 129 + {"System Busy", -EBUSY}, 130 + }; 131 + 132 + static const char * const encoding_options[] = { 133 + [TLMI_ENCODING_ASCII] = "ascii", 134 + [TLMI_ENCODING_SCANCODE] = "scancode", 135 + }; 136 + static struct think_lmi tlmi_priv; 137 + struct class *fw_attr_class; 138 + 139 + /* ------ Utility functions ------------*/ 140 + /* Convert BIOS WMI error string to suitable error code */ 141 + static int tlmi_errstr_to_err(const char *errstr) 142 + { 143 + int i; 144 + 145 + for (i = 0; i < sizeof(tlmi_errs)/sizeof(struct tlmi_err_codes); i++) { 146 + if (!strcmp(tlmi_errs[i].err_str, errstr)) 147 + return tlmi_errs[i].err_code; 148 + } 149 + return -EPERM; 150 + } 151 + 152 + /* Extract error string from WMI return buffer */ 153 + static int tlmi_extract_error(const struct acpi_buffer *output) 154 + { 155 + const union acpi_object *obj; 156 + 157 + obj = output->pointer; 158 + if (!obj) 159 + return -ENOMEM; 160 + if (obj->type != ACPI_TYPE_STRING || !obj->string.pointer) 161 + return -EIO; 162 + 163 + return tlmi_errstr_to_err(obj->string.pointer); 164 + } 165 + 166 + /* Utility function to execute WMI call to BIOS */ 167 + static int tlmi_simple_call(const char *guid, const char *arg) 168 + { 169 + const struct acpi_buffer input = { strlen(arg), (char *)arg }; 170 + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 171 + acpi_status status; 172 + int i, err; 173 + 174 + /* 175 + * Duplicated call required to match BIOS workaround for behavior 176 + * seen when WMI accessed via scripting on other OS. 177 + */ 178 + for (i = 0; i < 2; i++) { 179 + /* (re)initialize output buffer to default state */ 180 + output.length = ACPI_ALLOCATE_BUFFER; 181 + output.pointer = NULL; 182 + 183 + status = wmi_evaluate_method(guid, 0, 0, &input, &output); 184 + if (ACPI_FAILURE(status)) { 185 + kfree(output.pointer); 186 + return -EIO; 187 + } 188 + err = tlmi_extract_error(&output); 189 + kfree(output.pointer); 190 + if (err) 191 + return err; 192 + } 193 + return 0; 194 + } 195 + 196 + /* Extract output string from WMI return buffer */ 197 + static int tlmi_extract_output_string(const struct acpi_buffer *output, 198 + char **string) 199 + { 200 + const union acpi_object *obj; 201 + char *s; 202 + 203 + obj = output->pointer; 204 + if (!obj) 205 + return -ENOMEM; 206 + if (obj->type != ACPI_TYPE_STRING || !obj->string.pointer) 207 + return -EIO; 208 + 209 + s = kstrdup(obj->string.pointer, GFP_KERNEL); 210 + if (!s) 211 + return -ENOMEM; 212 + *string = s; 213 + return 0; 214 + } 215 + 216 + /* ------ Core interface functions ------------*/ 217 + 218 + /* Get password settings from BIOS */ 219 + static int tlmi_get_pwd_settings(struct tlmi_pwdcfg *pwdcfg) 220 + { 221 + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 222 + const union acpi_object *obj; 223 + acpi_status status; 224 + 225 + if (!tlmi_priv.can_get_password_settings) 226 + return -EOPNOTSUPP; 227 + 228 + status = wmi_query_block(LENOVO_BIOS_PASSWORD_SETTINGS_GUID, 0, 229 + &output); 230 + if (ACPI_FAILURE(status)) 231 + return -EIO; 232 + 233 + obj = output.pointer; 234 + if (!obj) 235 + return -ENOMEM; 236 + if (obj->type != ACPI_TYPE_BUFFER || !obj->buffer.pointer) { 237 + kfree(obj); 238 + return -EIO; 239 + } 240 + /* 241 + * The size of thinkpad_wmi_pcfg on ThinkStation is larger than ThinkPad. 242 + * To make the driver compatible on different brands, we permit it to get 243 + * the data in below case. 244 + */ 245 + if (obj->buffer.length < sizeof(struct tlmi_pwdcfg)) { 246 + pr_warn("Unknown pwdcfg buffer length %d\n", obj->buffer.length); 247 + kfree(obj); 248 + return -EIO; 249 + } 250 + memcpy(pwdcfg, obj->buffer.pointer, sizeof(struct tlmi_pwdcfg)); 251 + kfree(obj); 252 + return 0; 253 + } 254 + 255 + static int tlmi_save_bios_settings(const char *password) 256 + { 257 + return tlmi_simple_call(LENOVO_SAVE_BIOS_SETTINGS_GUID, 258 + password); 259 + } 260 + 261 + static int tlmi_setting(int item, char **value, const char *guid_string) 262 + { 263 + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 264 + acpi_status status; 265 + int ret; 266 + 267 + status = wmi_query_block(guid_string, item, &output); 268 + if (ACPI_FAILURE(status)) { 269 + kfree(output.pointer); 270 + return -EIO; 271 + } 272 + 273 + ret = tlmi_extract_output_string(&output, value); 274 + kfree(output.pointer); 275 + return ret; 276 + } 277 + 278 + static int tlmi_get_bios_selections(const char *item, char **value) 279 + { 280 + const struct acpi_buffer input = { strlen(item), (char *)item }; 281 + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 282 + acpi_status status; 283 + int ret; 284 + 285 + status = wmi_evaluate_method(LENOVO_GET_BIOS_SELECTIONS_GUID, 286 + 0, 0, &input, &output); 287 + 288 + if (ACPI_FAILURE(status)) { 289 + kfree(output.pointer); 290 + return -EIO; 291 + } 292 + 293 + ret = tlmi_extract_output_string(&output, value); 294 + kfree(output.pointer); 295 + return ret; 296 + } 297 + 298 + /* ---- Authentication sysfs --------------------------------------------------------- */ 299 + static ssize_t is_enabled_show(struct kobject *kobj, struct kobj_attribute *attr, 300 + char *buf) 301 + { 302 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 303 + 304 + return sysfs_emit(buf, "%d\n", setting->valid); 305 + } 306 + 307 + static struct kobj_attribute auth_is_pass_set = __ATTR_RO(is_enabled); 308 + 309 + static ssize_t current_password_store(struct kobject *kobj, 310 + struct kobj_attribute *attr, 311 + const char *buf, size_t count) 312 + { 313 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 314 + size_t pwdlen; 315 + char *p; 316 + 317 + pwdlen = strlen(buf); 318 + /* pwdlen == 0 is allowed to clear the password */ 319 + if (pwdlen && ((pwdlen < setting->minlen) || (pwdlen > setting->maxlen))) 320 + return -EINVAL; 321 + 322 + strscpy(setting->password, buf, setting->maxlen); 323 + /* Strip out CR if one is present, setting password won't work if it is present */ 324 + p = strchrnul(setting->password, '\n'); 325 + *p = '\0'; 326 + return count; 327 + } 328 + 329 + static struct kobj_attribute auth_current_password = __ATTR_WO(current_password); 330 + 331 + static ssize_t new_password_store(struct kobject *kobj, 332 + struct kobj_attribute *attr, 333 + const char *buf, size_t count) 334 + { 335 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 336 + char *auth_str, *new_pwd, *p; 337 + size_t pwdlen; 338 + int ret; 339 + 340 + if (!capable(CAP_SYS_ADMIN)) 341 + return -EPERM; 342 + 343 + if (!tlmi_priv.can_set_bios_password) 344 + return -EOPNOTSUPP; 345 + 346 + new_pwd = kstrdup(buf, GFP_KERNEL); 347 + if (!new_pwd) 348 + return -ENOMEM; 349 + 350 + /* Strip out CR if one is present, setting password won't work if it is present */ 351 + p = strchrnul(new_pwd, '\n'); 352 + *p = '\0'; 353 + 354 + pwdlen = strlen(new_pwd); 355 + /* pwdlen == 0 is allowed to clear the password */ 356 + if (pwdlen && ((pwdlen < setting->minlen) || (pwdlen > setting->maxlen))) { 357 + ret = -EINVAL; 358 + goto out; 359 + } 360 + 361 + /* Format: 'PasswordType,CurrentPw,NewPw,Encoding,KbdLang;' */ 362 + auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s,%s,%s;", 363 + setting->pwd_type, setting->password, new_pwd, 364 + encoding_options[setting->encoding], setting->kbdlang); 365 + if (!auth_str) { 366 + ret = -ENOMEM; 367 + goto out; 368 + } 369 + ret = tlmi_simple_call(LENOVO_SET_BIOS_PASSWORD_GUID, auth_str); 370 + kfree(auth_str); 371 + out: 372 + kfree(new_pwd); 373 + return ret ?: count; 374 + } 375 + 376 + static struct kobj_attribute auth_new_password = __ATTR_WO(new_password); 377 + 378 + static ssize_t min_password_length_show(struct kobject *kobj, struct kobj_attribute *attr, 379 + char *buf) 380 + { 381 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 382 + 383 + return sysfs_emit(buf, "%d\n", setting->minlen); 384 + } 385 + 386 + static struct kobj_attribute auth_min_pass_length = __ATTR_RO(min_password_length); 387 + 388 + static ssize_t max_password_length_show(struct kobject *kobj, struct kobj_attribute *attr, 389 + char *buf) 390 + { 391 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 392 + 393 + return sysfs_emit(buf, "%d\n", setting->maxlen); 394 + } 395 + static struct kobj_attribute auth_max_pass_length = __ATTR_RO(max_password_length); 396 + 397 + static ssize_t mechanism_show(struct kobject *kobj, struct kobj_attribute *attr, 398 + char *buf) 399 + { 400 + return sysfs_emit(buf, "password\n"); 401 + } 402 + static struct kobj_attribute auth_mechanism = __ATTR_RO(mechanism); 403 + 404 + static ssize_t encoding_show(struct kobject *kobj, struct kobj_attribute *attr, 405 + char *buf) 406 + { 407 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 408 + 409 + return sysfs_emit(buf, "%s\n", encoding_options[setting->encoding]); 410 + } 411 + 412 + static ssize_t encoding_store(struct kobject *kobj, 413 + struct kobj_attribute *attr, 414 + const char *buf, size_t count) 415 + { 416 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 417 + int i; 418 + 419 + /* Scan for a matching profile */ 420 + i = sysfs_match_string(encoding_options, buf); 421 + if (i < 0) 422 + return -EINVAL; 423 + 424 + setting->encoding = i; 425 + return count; 426 + } 427 + 428 + static struct kobj_attribute auth_encoding = __ATTR_RW(encoding); 429 + 430 + static ssize_t kbdlang_show(struct kobject *kobj, struct kobj_attribute *attr, 431 + char *buf) 432 + { 433 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 434 + 435 + return sysfs_emit(buf, "%s\n", setting->kbdlang); 436 + } 437 + 438 + static ssize_t kbdlang_store(struct kobject *kobj, 439 + struct kobj_attribute *attr, 440 + const char *buf, size_t count) 441 + { 442 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 443 + int length; 444 + 445 + length = strlen(buf); 446 + if (buf[length-1] == '\n') 447 + length--; 448 + 449 + if (!length || (length >= TLMI_LANG_MAXLEN)) 450 + return -EINVAL; 451 + 452 + memcpy(setting->kbdlang, buf, length); 453 + setting->kbdlang[length] = '\0'; 454 + return count; 455 + } 456 + 457 + static struct kobj_attribute auth_kbdlang = __ATTR_RW(kbdlang); 458 + 459 + static ssize_t role_show(struct kobject *kobj, struct kobj_attribute *attr, 460 + char *buf) 461 + { 462 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 463 + 464 + return sysfs_emit(buf, "%s\n", setting->role); 465 + } 466 + static struct kobj_attribute auth_role = __ATTR_RO(role); 467 + 468 + static struct attribute *auth_attrs[] = { 469 + &auth_is_pass_set.attr, 470 + &auth_min_pass_length.attr, 471 + &auth_max_pass_length.attr, 472 + &auth_current_password.attr, 473 + &auth_new_password.attr, 474 + &auth_role.attr, 475 + &auth_mechanism.attr, 476 + &auth_encoding.attr, 477 + &auth_kbdlang.attr, 478 + NULL 479 + }; 480 + 481 + static const struct attribute_group auth_attr_group = { 482 + .attrs = auth_attrs, 483 + }; 484 + 485 + /* ---- Attributes sysfs --------------------------------------------------------- */ 486 + static ssize_t display_name_show(struct kobject *kobj, struct kobj_attribute *attr, 487 + char *buf) 488 + { 489 + struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 490 + 491 + return sysfs_emit(buf, "%s\n", setting->display_name); 492 + } 493 + 494 + static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 495 + { 496 + struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 497 + char *item; 498 + int ret; 499 + 500 + ret = tlmi_setting(setting->index, &item, LENOVO_BIOS_SETTING_GUID); 501 + if (ret) 502 + return ret; 503 + 504 + ret = sysfs_emit(buf, "%s\n", item); 505 + kfree(item); 506 + return ret; 507 + } 508 + 509 + static ssize_t possible_values_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 510 + { 511 + struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 512 + 513 + if (!tlmi_priv.can_get_bios_selections) 514 + return -EOPNOTSUPP; 515 + 516 + return sysfs_emit(buf, "%s\n", setting->possible_values); 517 + } 518 + 519 + static ssize_t current_value_store(struct kobject *kobj, 520 + struct kobj_attribute *attr, 521 + const char *buf, size_t count) 522 + { 523 + struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 524 + char *set_str = NULL, *new_setting = NULL; 525 + char *auth_str = NULL; 526 + char *p; 527 + int ret; 528 + 529 + if (!tlmi_priv.can_set_bios_settings) 530 + return -EOPNOTSUPP; 531 + 532 + new_setting = kstrdup(buf, GFP_KERNEL); 533 + if (!new_setting) 534 + return -ENOMEM; 535 + 536 + /* Strip out CR if one is present */ 537 + p = strchrnul(new_setting, '\n'); 538 + *p = '\0'; 539 + 540 + if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password) { 541 + auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;", 542 + tlmi_priv.pwd_admin->password, 543 + encoding_options[tlmi_priv.pwd_admin->encoding], 544 + tlmi_priv.pwd_admin->kbdlang); 545 + if (!auth_str) { 546 + ret = -ENOMEM; 547 + goto out; 548 + } 549 + } 550 + 551 + if (auth_str) 552 + set_str = kasprintf(GFP_KERNEL, "%s,%s,%s", setting->display_name, 553 + new_setting, auth_str); 554 + else 555 + set_str = kasprintf(GFP_KERNEL, "%s,%s;", setting->display_name, 556 + new_setting); 557 + if (!set_str) { 558 + ret = -ENOMEM; 559 + goto out; 560 + } 561 + 562 + ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTINGS_GUID, set_str); 563 + if (ret) 564 + goto out; 565 + 566 + if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password) 567 + ret = tlmi_save_bios_settings(auth_str); 568 + else 569 + ret = tlmi_save_bios_settings(""); 570 + 571 + out: 572 + kfree(auth_str); 573 + kfree(set_str); 574 + kfree(new_setting); 575 + return ret ?: count; 576 + } 577 + 578 + static struct kobj_attribute attr_displ_name = __ATTR_RO(display_name); 579 + 580 + static struct kobj_attribute attr_possible_values = __ATTR_RO(possible_values); 581 + 582 + static struct kobj_attribute attr_current_val = __ATTR_RW_MODE(current_value, 0600); 583 + 584 + static struct attribute *tlmi_attrs[] = { 585 + &attr_displ_name.attr, 586 + &attr_current_val.attr, 587 + &attr_possible_values.attr, 588 + NULL 589 + }; 590 + 591 + static const struct attribute_group tlmi_attr_group = { 592 + .attrs = tlmi_attrs, 593 + }; 594 + 595 + static ssize_t tlmi_attr_show(struct kobject *kobj, struct attribute *attr, 596 + char *buf) 597 + { 598 + struct kobj_attribute *kattr; 599 + 600 + kattr = container_of(attr, struct kobj_attribute, attr); 601 + if (kattr->show) 602 + return kattr->show(kobj, kattr, buf); 603 + return -EIO; 604 + } 605 + 606 + static ssize_t tlmi_attr_store(struct kobject *kobj, struct attribute *attr, 607 + const char *buf, size_t count) 608 + { 609 + struct kobj_attribute *kattr; 610 + 611 + kattr = container_of(attr, struct kobj_attribute, attr); 612 + if (kattr->store) 613 + return kattr->store(kobj, kattr, buf, count); 614 + return -EIO; 615 + } 616 + 617 + static const struct sysfs_ops tlmi_kobj_sysfs_ops = { 618 + .show = tlmi_attr_show, 619 + .store = tlmi_attr_store, 620 + }; 621 + 622 + static void tlmi_attr_setting_release(struct kobject *kobj) 623 + { 624 + struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 625 + 626 + kfree(setting); 627 + } 628 + 629 + static void tlmi_pwd_setting_release(struct kobject *kobj) 630 + { 631 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 632 + 633 + kfree(setting); 634 + } 635 + 636 + static struct kobj_type tlmi_attr_setting_ktype = { 637 + .release = &tlmi_attr_setting_release, 638 + .sysfs_ops = &tlmi_kobj_sysfs_ops, 639 + }; 640 + 641 + static struct kobj_type tlmi_pwd_setting_ktype = { 642 + .release = &tlmi_pwd_setting_release, 643 + .sysfs_ops = &tlmi_kobj_sysfs_ops, 644 + }; 645 + 646 + /* ---- Initialisation --------------------------------------------------------- */ 647 + static void tlmi_release_attr(void) 648 + { 649 + int i; 650 + 651 + /* Attribute structures */ 652 + for (i = 0; i < TLMI_SETTINGS_COUNT; i++) { 653 + if (tlmi_priv.setting[i]) { 654 + kfree(tlmi_priv.setting[i]->possible_values); 655 + sysfs_remove_group(&tlmi_priv.setting[i]->kobj, &tlmi_attr_group); 656 + kobject_put(&tlmi_priv.setting[i]->kobj); 657 + } 658 + } 659 + kset_unregister(tlmi_priv.attribute_kset); 660 + 661 + /* Authentication structures */ 662 + sysfs_remove_group(&tlmi_priv.pwd_admin->kobj, &auth_attr_group); 663 + kobject_put(&tlmi_priv.pwd_admin->kobj); 664 + sysfs_remove_group(&tlmi_priv.pwd_power->kobj, &auth_attr_group); 665 + kobject_put(&tlmi_priv.pwd_power->kobj); 666 + kset_unregister(tlmi_priv.authentication_kset); 667 + } 668 + 669 + static int tlmi_sysfs_init(void) 670 + { 671 + int i, ret; 672 + 673 + ret = fw_attributes_class_get(&fw_attr_class); 674 + if (ret) 675 + return ret; 676 + 677 + tlmi_priv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0), 678 + NULL, "%s", "thinklmi"); 679 + if (IS_ERR(tlmi_priv.class_dev)) { 680 + ret = PTR_ERR(tlmi_priv.class_dev); 681 + goto fail_class_created; 682 + } 683 + 684 + tlmi_priv.attribute_kset = kset_create_and_add("attributes", NULL, 685 + &tlmi_priv.class_dev->kobj); 686 + if (!tlmi_priv.attribute_kset) { 687 + ret = -ENOMEM; 688 + goto fail_device_created; 689 + } 690 + 691 + for (i = 0; i < TLMI_SETTINGS_COUNT; i++) { 692 + /* Check if index is a valid setting - skip if it isn't */ 693 + if (!tlmi_priv.setting[i]) 694 + continue; 695 + 696 + /* Build attribute */ 697 + tlmi_priv.setting[i]->kobj.kset = tlmi_priv.attribute_kset; 698 + ret = kobject_init_and_add(&tlmi_priv.setting[i]->kobj, &tlmi_attr_setting_ktype, 699 + NULL, "%s", tlmi_priv.setting[i]->display_name); 700 + if (ret) 701 + goto fail_create_attr; 702 + 703 + ret = sysfs_create_group(&tlmi_priv.setting[i]->kobj, &tlmi_attr_group); 704 + if (ret) 705 + goto fail_create_attr; 706 + } 707 + 708 + /* Create authentication entries */ 709 + tlmi_priv.authentication_kset = kset_create_and_add("authentication", NULL, 710 + &tlmi_priv.class_dev->kobj); 711 + if (!tlmi_priv.authentication_kset) { 712 + ret = -ENOMEM; 713 + goto fail_create_attr; 714 + } 715 + tlmi_priv.pwd_admin->kobj.kset = tlmi_priv.authentication_kset; 716 + ret = kobject_init_and_add(&tlmi_priv.pwd_admin->kobj, &tlmi_pwd_setting_ktype, 717 + NULL, "%s", "Admin"); 718 + if (ret) 719 + goto fail_create_attr; 720 + 721 + ret = sysfs_create_group(&tlmi_priv.pwd_admin->kobj, &auth_attr_group); 722 + if (ret) 723 + goto fail_create_attr; 724 + 725 + tlmi_priv.pwd_power->kobj.kset = tlmi_priv.authentication_kset; 726 + ret = kobject_init_and_add(&tlmi_priv.pwd_power->kobj, &tlmi_pwd_setting_ktype, 727 + NULL, "%s", "System"); 728 + if (ret) 729 + goto fail_create_attr; 730 + 731 + ret = sysfs_create_group(&tlmi_priv.pwd_power->kobj, &auth_attr_group); 732 + if (ret) 733 + goto fail_create_attr; 734 + 735 + return ret; 736 + 737 + fail_create_attr: 738 + tlmi_release_attr(); 739 + fail_device_created: 740 + device_destroy(fw_attr_class, MKDEV(0, 0)); 741 + fail_class_created: 742 + fw_attributes_class_put(); 743 + return ret; 744 + } 745 + 746 + /* ---- Base Driver -------------------------------------------------------- */ 747 + static int tlmi_analyze(void) 748 + { 749 + struct tlmi_pwdcfg pwdcfg; 750 + acpi_status status; 751 + int i, ret; 752 + 753 + if (wmi_has_guid(LENOVO_SET_BIOS_SETTINGS_GUID) && 754 + wmi_has_guid(LENOVO_SAVE_BIOS_SETTINGS_GUID)) 755 + tlmi_priv.can_set_bios_settings = true; 756 + 757 + if (wmi_has_guid(LENOVO_GET_BIOS_SELECTIONS_GUID)) 758 + tlmi_priv.can_get_bios_selections = true; 759 + 760 + if (wmi_has_guid(LENOVO_SET_BIOS_PASSWORD_GUID)) 761 + tlmi_priv.can_set_bios_password = true; 762 + 763 + if (wmi_has_guid(LENOVO_BIOS_PASSWORD_SETTINGS_GUID)) 764 + tlmi_priv.can_get_password_settings = true; 765 + 766 + /* 767 + * Try to find the number of valid settings of this machine 768 + * and use it to create sysfs attributes. 769 + */ 770 + for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) { 771 + struct tlmi_attr_setting *setting; 772 + char *item = NULL; 773 + char *p; 774 + 775 + tlmi_priv.setting[i] = NULL; 776 + status = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID); 777 + if (ACPI_FAILURE(status)) 778 + break; 779 + if (!item) 780 + break; 781 + if (!*item) 782 + continue; 783 + 784 + /* It is not allowed to have '/' for file name. Convert it into '\'. */ 785 + strreplace(item, '/', '\\'); 786 + 787 + /* Remove the value part */ 788 + p = strchrnul(item, ','); 789 + *p = '\0'; 790 + 791 + /* Create a setting entry */ 792 + setting = kzalloc(sizeof(*setting), GFP_KERNEL); 793 + if (!setting) { 794 + ret = -ENOMEM; 795 + goto fail_clear_attr; 796 + } 797 + setting->index = i; 798 + strscpy(setting->display_name, item, TLMI_SETTINGS_MAXLEN); 799 + /* If BIOS selections supported, load those */ 800 + if (tlmi_priv.can_get_bios_selections) { 801 + ret = tlmi_get_bios_selections(setting->display_name, 802 + &setting->possible_values); 803 + if (ret || !setting->possible_values) 804 + pr_info("Error retrieving possible values for %d : %s\n", 805 + i, setting->display_name); 806 + } 807 + tlmi_priv.setting[i] = setting; 808 + tlmi_priv.settings_count++; 809 + kfree(item); 810 + } 811 + 812 + /* Create password setting structure */ 813 + ret = tlmi_get_pwd_settings(&pwdcfg); 814 + if (ret) 815 + goto fail_clear_attr; 816 + 817 + tlmi_priv.pwd_admin = kzalloc(sizeof(struct tlmi_pwd_setting), GFP_KERNEL); 818 + if (!tlmi_priv.pwd_admin) { 819 + ret = -ENOMEM; 820 + goto fail_clear_attr; 821 + } 822 + strscpy(tlmi_priv.pwd_admin->display_name, "admin", TLMI_PWDTYPE_MAXLEN); 823 + strscpy(tlmi_priv.pwd_admin->kbdlang, "us", TLMI_LANG_MAXLEN); 824 + tlmi_priv.pwd_admin->encoding = TLMI_ENCODING_ASCII; 825 + tlmi_priv.pwd_admin->pwd_type = "pap"; 826 + tlmi_priv.pwd_admin->role = "bios-admin"; 827 + tlmi_priv.pwd_admin->minlen = pwdcfg.min_length; 828 + if (WARN_ON(pwdcfg.max_length >= TLMI_PWD_BUFSIZE)) 829 + pwdcfg.max_length = TLMI_PWD_BUFSIZE - 1; 830 + tlmi_priv.pwd_admin->maxlen = pwdcfg.max_length; 831 + if (pwdcfg.password_state & TLMI_PAP_PWD) 832 + tlmi_priv.pwd_admin->valid = true; 833 + 834 + tlmi_priv.pwd_power = kzalloc(sizeof(struct tlmi_pwd_setting), GFP_KERNEL); 835 + if (!tlmi_priv.pwd_power) { 836 + ret = -ENOMEM; 837 + goto fail_clear_attr; 838 + } 839 + strscpy(tlmi_priv.pwd_power->display_name, "power-on", TLMI_PWDTYPE_MAXLEN); 840 + strscpy(tlmi_priv.pwd_power->kbdlang, "us", TLMI_LANG_MAXLEN); 841 + tlmi_priv.pwd_power->encoding = TLMI_ENCODING_ASCII; 842 + tlmi_priv.pwd_power->pwd_type = "pop"; 843 + tlmi_priv.pwd_power->role = "power-on"; 844 + tlmi_priv.pwd_power->minlen = pwdcfg.min_length; 845 + tlmi_priv.pwd_power->maxlen = pwdcfg.max_length; 846 + 847 + if (pwdcfg.password_state & TLMI_POP_PWD) 848 + tlmi_priv.pwd_power->valid = true; 849 + 850 + return 0; 851 + 852 + fail_clear_attr: 853 + for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) 854 + kfree(tlmi_priv.setting[i]); 855 + return ret; 856 + } 857 + 858 + static void tlmi_remove(struct wmi_device *wdev) 859 + { 860 + tlmi_release_attr(); 861 + device_destroy(fw_attr_class, MKDEV(0, 0)); 862 + fw_attributes_class_put(); 863 + } 864 + 865 + static int tlmi_probe(struct wmi_device *wdev, const void *context) 866 + { 867 + tlmi_analyze(); 868 + return tlmi_sysfs_init(); 869 + } 870 + 871 + static const struct wmi_device_id tlmi_id_table[] = { 872 + { .guid_string = LENOVO_BIOS_SETTING_GUID }, 873 + { } 874 + }; 875 + 876 + static struct wmi_driver tlmi_driver = { 877 + .driver = { 878 + .name = "think-lmi", 879 + }, 880 + .id_table = tlmi_id_table, 881 + .probe = tlmi_probe, 882 + .remove = tlmi_remove, 883 + }; 884 + 885 + MODULE_AUTHOR("Sugumaran L <slacshiminar@lenovo.com>"); 886 + MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>"); 887 + MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>"); 888 + MODULE_DESCRIPTION("ThinkLMI Driver"); 889 + MODULE_LICENSE("GPL"); 890 + 891 + module_wmi_driver(tlmi_driver);
+81
drivers/platform/x86/think-lmi.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + #ifndef _THINK_LMI_H_ 4 + #define _THINK_LMI_H_ 5 + 6 + #include <linux/types.h> 7 + 8 + #define TLMI_SETTINGS_COUNT 256 9 + #define TLMI_SETTINGS_MAXLEN 512 10 + #define TLMI_PWD_BUFSIZE 129 11 + #define TLMI_PWDTYPE_MAXLEN 64 12 + #define TLMI_ENC_MAXLEN 64 13 + #define TLMI_LANG_MAXLEN 4 14 + #define TLMI_PWDTYPE_LEN 4 15 + /* 16 + * Longest string should be in the set command: allow size of BIOS 17 + * option and choice 18 + */ 19 + #define TLMI_GETSET_MAXLEN (TLMI_SETTINGS_MAXLEN + TLMI_SETTINGS_MAXLEN) 20 + 21 + /* Possible error values */ 22 + struct tlmi_err_codes { 23 + const char *err_str; 24 + int err_code; 25 + }; 26 + 27 + enum encoding_option { 28 + TLMI_ENCODING_ASCII, 29 + TLMI_ENCODING_SCANCODE, 30 + }; 31 + 32 + /* password configuration details */ 33 + struct tlmi_pwdcfg { 34 + uint32_t password_mode; 35 + uint32_t password_state; 36 + uint32_t min_length; 37 + uint32_t max_length; 38 + uint32_t supported_encodings; 39 + uint32_t supported_keyboard; 40 + }; 41 + 42 + /* password setting details */ 43 + struct tlmi_pwd_setting { 44 + struct kobject kobj; 45 + bool valid; 46 + char display_name[TLMI_PWDTYPE_MAXLEN]; 47 + char password[TLMI_PWD_BUFSIZE]; 48 + const char *pwd_type; 49 + const char *role; 50 + int minlen; 51 + int maxlen; 52 + enum encoding_option encoding; 53 + char kbdlang[TLMI_LANG_MAXLEN]; 54 + }; 55 + 56 + /* Attribute setting details */ 57 + struct tlmi_attr_setting { 58 + struct kobject kobj; 59 + int index; 60 + char display_name[TLMI_SETTINGS_MAXLEN]; 61 + char *possible_values; 62 + }; 63 + 64 + struct think_lmi { 65 + struct wmi_device *wmi_device; 66 + 67 + int settings_count; 68 + bool can_set_bios_settings; 69 + bool can_get_bios_selections; 70 + bool can_set_bios_password; 71 + bool can_get_password_settings; 72 + 73 + struct tlmi_attr_setting *setting[TLMI_SETTINGS_COUNT]; 74 + struct device *class_dev; 75 + struct kset *attribute_kset; 76 + struct kset *authentication_kset; 77 + struct tlmi_pwd_setting *pwd_admin; 78 + struct tlmi_pwd_setting *pwd_power; 79 + }; 80 + 81 + #endif /* !_THINK_LMI_H_ */