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

platform/x86: think-lmi: Opcode support

Implement Opcode support.
This is available on ThinkCenter and ThinkStations platforms and
gives improved password setting capabilities

Add options to configure System, HDD & NVMe passwords.
HDD & NVMe passwords need a user level (user/master) along with
drive index.

Signed-off-by: Mark Pearson <markpearson@lenovo.com>
Link: https://lore.kernel.org/r/20211117184453.2476-2-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
640a5fa5 adca4b68

+311 -37
+284 -36
drivers/platform/x86/think-lmi.c
··· 128 128 */ 129 129 #define LENOVO_DEBUG_CMD_GUID "7FF47003-3B6C-4E5E-A227-E979824A85D1" 130 130 131 + /* 132 + * Name: 133 + * Lenovo_OpcodeIF 134 + * Description: 135 + * Opcode interface which provides the ability to set multiple 136 + * parameters and then trigger an action with a final command. 137 + * This is particularly useful for simplifying setting passwords. 138 + * With this support comes the ability to set System, HDD and NVMe 139 + * passwords. 140 + * This is currently available on ThinkCenter and ThinkStations platforms 141 + */ 142 + #define LENOVO_OPCODE_IF_GUID "DFDDEF2C-57D4-48ce-B196-0FB787D90836" 143 + 131 144 #define TLMI_POP_PWD (1 << 0) 132 145 #define TLMI_PAP_PWD (1 << 1) 146 + #define TLMI_HDD_PWD (1 << 2) 147 + #define TLMI_SYS_PWD (1 << 3) 133 148 #define to_tlmi_pwd_setting(kobj) container_of(kobj, struct tlmi_pwd_setting, kobj) 134 149 #define to_tlmi_attr_setting(kobj) container_of(kobj, struct tlmi_attr_setting, kobj) 135 150 ··· 159 144 static const char * const encoding_options[] = { 160 145 [TLMI_ENCODING_ASCII] = "ascii", 161 146 [TLMI_ENCODING_SCANCODE] = "scancode", 147 + }; 148 + static const char * const level_options[] = { 149 + [TLMI_LEVEL_USER] = "user", 150 + [TLMI_LEVEL_MASTER] = "master", 162 151 }; 163 152 static struct think_lmi tlmi_priv; 164 153 static struct class *fw_attr_class; ··· 252 233 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 253 234 const union acpi_object *obj; 254 235 acpi_status status; 236 + int copy_size; 255 237 256 238 if (!tlmi_priv.can_get_password_settings) 257 239 return -EOPNOTSUPP; ··· 273 253 * The size of thinkpad_wmi_pcfg on ThinkStation is larger than ThinkPad. 274 254 * To make the driver compatible on different brands, we permit it to get 275 255 * the data in below case. 256 + * Settings must have at minimum the core fields available 276 257 */ 277 - if (obj->buffer.length < sizeof(struct tlmi_pwdcfg)) { 258 + if (obj->buffer.length < sizeof(struct tlmi_pwdcfg_core)) { 278 259 pr_warn("Unknown pwdcfg buffer length %d\n", obj->buffer.length); 279 260 kfree(obj); 280 261 return -EIO; 281 262 } 282 - memcpy(pwdcfg, obj->buffer.pointer, sizeof(struct tlmi_pwdcfg)); 263 + 264 + copy_size = obj->buffer.length < sizeof(struct tlmi_pwdcfg) ? 265 + obj->buffer.length : sizeof(struct tlmi_pwdcfg); 266 + memcpy(pwdcfg, obj->buffer.pointer, copy_size); 283 267 kfree(obj); 268 + 269 + if (WARN_ON(pwdcfg->core.max_length >= TLMI_PWD_BUFSIZE)) 270 + pwdcfg->core.max_length = TLMI_PWD_BUFSIZE - 1; 284 271 return 0; 285 272 } 286 273 ··· 295 268 { 296 269 return tlmi_simple_call(LENOVO_SAVE_BIOS_SETTINGS_GUID, 297 270 password); 271 + } 272 + 273 + static int tlmi_opcode_setting(char *setting, const char *value) 274 + { 275 + char *opcode_str; 276 + int ret; 277 + 278 + opcode_str = kasprintf(GFP_KERNEL, "%s:%s;", setting, value); 279 + if (!opcode_str) 280 + return -ENOMEM; 281 + 282 + ret = tlmi_simple_call(LENOVO_OPCODE_IF_GUID, opcode_str); 283 + kfree(opcode_str); 284 + return ret; 298 285 } 299 286 300 287 static int tlmi_setting(int item, char **value, const char *guid_string) ··· 411 370 goto out; 412 371 } 413 372 414 - /* Format: 'PasswordType,CurrentPw,NewPw,Encoding,KbdLang;' */ 415 - auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s,%s,%s;", 416 - setting->pwd_type, setting->password, new_pwd, 417 - encoding_options[setting->encoding], setting->kbdlang); 418 - if (!auth_str) { 419 - ret = -ENOMEM; 420 - goto out; 373 + /* If opcode support is present use that interface */ 374 + if (tlmi_priv.opcode_support) { 375 + char pwd_type[8]; 376 + 377 + /* Special handling required for HDD and NVMe passwords */ 378 + if (setting == tlmi_priv.pwd_hdd) { 379 + if (setting->level == TLMI_LEVEL_USER) 380 + sprintf(pwd_type, "uhdp%d", setting->index); 381 + else 382 + sprintf(pwd_type, "mhdp%d", setting->index); 383 + } else if (setting == tlmi_priv.pwd_nvme) { 384 + if (setting->level == TLMI_LEVEL_USER) 385 + sprintf(pwd_type, "unvp%d", setting->index); 386 + else 387 + sprintf(pwd_type, "mnvp%d", setting->index); 388 + } else { 389 + sprintf(pwd_type, "%s", setting->pwd_type); 390 + } 391 + 392 + ret = tlmi_opcode_setting("WmiOpcodePasswordType", pwd_type); 393 + if (ret) 394 + goto out; 395 + 396 + if (tlmi_priv.pwd_admin->valid) { 397 + ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin", 398 + tlmi_priv.pwd_admin->password); 399 + if (ret) 400 + goto out; 401 + } 402 + ret = tlmi_opcode_setting("WmiOpcodePasswordCurrent01", setting->password); 403 + if (ret) 404 + goto out; 405 + ret = tlmi_opcode_setting("WmiOpcodePasswordNew01", new_pwd); 406 + if (ret) 407 + goto out; 408 + ret = tlmi_simple_call(LENOVO_OPCODE_IF_GUID, "WmiOpcodePasswordSetUpdate;"); 409 + } else { 410 + /* Format: 'PasswordType,CurrentPw,NewPw,Encoding,KbdLang;' */ 411 + auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s,%s,%s;", 412 + setting->pwd_type, setting->password, new_pwd, 413 + encoding_options[setting->encoding], setting->kbdlang); 414 + if (!auth_str) { 415 + ret = -ENOMEM; 416 + goto out; 417 + } 418 + ret = tlmi_simple_call(LENOVO_SET_BIOS_PASSWORD_GUID, auth_str); 419 + kfree(auth_str); 421 420 } 422 - ret = tlmi_simple_call(LENOVO_SET_BIOS_PASSWORD_GUID, auth_str); 423 - kfree(auth_str); 424 421 out: 425 422 kfree(new_pwd); 426 423 return ret ?: count; ··· 554 475 } 555 476 static struct kobj_attribute auth_role = __ATTR_RO(role); 556 477 478 + static ssize_t index_show(struct kobject *kobj, struct kobj_attribute *attr, 479 + char *buf) 480 + { 481 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 482 + 483 + return sysfs_emit(buf, "%d\n", setting->index); 484 + } 485 + 486 + static ssize_t index_store(struct kobject *kobj, 487 + struct kobj_attribute *attr, 488 + const char *buf, size_t count) 489 + { 490 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 491 + int err, val; 492 + 493 + err = kstrtoint(buf, 10, &val); 494 + if (err < 0) 495 + return err; 496 + 497 + if (val > TLMI_INDEX_MAX) 498 + return -EINVAL; 499 + 500 + setting->index = val; 501 + return count; 502 + } 503 + 504 + static struct kobj_attribute auth_index = __ATTR_RW(index); 505 + 506 + static ssize_t level_show(struct kobject *kobj, struct kobj_attribute *attr, 507 + char *buf) 508 + { 509 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 510 + 511 + return sysfs_emit(buf, "%s\n", level_options[setting->level]); 512 + } 513 + 514 + static ssize_t level_store(struct kobject *kobj, 515 + struct kobj_attribute *attr, 516 + const char *buf, size_t count) 517 + { 518 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 519 + int i; 520 + 521 + /* Scan for a matching profile */ 522 + i = sysfs_match_string(level_options, buf); 523 + if (i < 0) 524 + return -EINVAL; 525 + 526 + setting->level = i; 527 + return count; 528 + } 529 + 530 + static struct kobj_attribute auth_level = __ATTR_RW(level); 531 + 532 + static umode_t auth_attr_is_visible(struct kobject *kobj, 533 + struct attribute *attr, int n) 534 + { 535 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 536 + 537 + /*We only want to display level and index settings on HDD/NVMe */ 538 + if ((attr == (struct attribute *)&auth_index) || 539 + (attr == (struct attribute *)&auth_level)) { 540 + if ((setting == tlmi_priv.pwd_hdd) || (setting == tlmi_priv.pwd_nvme)) 541 + return attr->mode; 542 + return 0; 543 + } 544 + return attr->mode; 545 + } 546 + 557 547 static struct attribute *auth_attrs[] = { 558 548 &auth_is_pass_set.attr, 559 549 &auth_min_pass_length.attr, ··· 633 485 &auth_mechanism.attr, 634 486 &auth_encoding.attr, 635 487 &auth_kbdlang.attr, 488 + &auth_index.attr, 489 + &auth_level.attr, 636 490 NULL 637 491 }; 638 492 639 493 static const struct attribute_group auth_attr_group = { 494 + .is_visible = auth_attr_is_visible, 640 495 .attrs = auth_attrs, 641 496 }; 642 497 ··· 903 752 kobject_put(&tlmi_priv.pwd_admin->kobj); 904 753 sysfs_remove_group(&tlmi_priv.pwd_power->kobj, &auth_attr_group); 905 754 kobject_put(&tlmi_priv.pwd_power->kobj); 755 + 756 + if (tlmi_priv.opcode_support) { 757 + sysfs_remove_group(&tlmi_priv.pwd_system->kobj, &auth_attr_group); 758 + kobject_put(&tlmi_priv.pwd_system->kobj); 759 + sysfs_remove_group(&tlmi_priv.pwd_hdd->kobj, &auth_attr_group); 760 + kobject_put(&tlmi_priv.pwd_hdd->kobj); 761 + sysfs_remove_group(&tlmi_priv.pwd_nvme->kobj, &auth_attr_group); 762 + kobject_put(&tlmi_priv.pwd_nvme->kobj); 763 + } 764 + 906 765 kset_unregister(tlmi_priv.authentication_kset); 907 766 } 908 767 ··· 992 831 goto fail_create_attr; 993 832 994 833 tlmi_priv.pwd_power->kobj.kset = tlmi_priv.authentication_kset; 995 - ret = kobject_add(&tlmi_priv.pwd_power->kobj, NULL, "%s", "System"); 834 + ret = kobject_add(&tlmi_priv.pwd_power->kobj, NULL, "%s", "Power-on"); 996 835 if (ret) 997 836 goto fail_create_attr; 998 837 999 838 ret = sysfs_create_group(&tlmi_priv.pwd_power->kobj, &auth_attr_group); 1000 839 if (ret) 1001 840 goto fail_create_attr; 841 + 842 + if (tlmi_priv.opcode_support) { 843 + tlmi_priv.pwd_system->kobj.kset = tlmi_priv.authentication_kset; 844 + ret = kobject_add(&tlmi_priv.pwd_system->kobj, NULL, "%s", "System"); 845 + if (ret) 846 + goto fail_create_attr; 847 + 848 + ret = sysfs_create_group(&tlmi_priv.pwd_system->kobj, &auth_attr_group); 849 + if (ret) 850 + goto fail_create_attr; 851 + 852 + tlmi_priv.pwd_hdd->kobj.kset = tlmi_priv.authentication_kset; 853 + ret = kobject_add(&tlmi_priv.pwd_hdd->kobj, NULL, "%s", "HDD"); 854 + if (ret) 855 + goto fail_create_attr; 856 + 857 + ret = sysfs_create_group(&tlmi_priv.pwd_hdd->kobj, &auth_attr_group); 858 + if (ret) 859 + goto fail_create_attr; 860 + 861 + tlmi_priv.pwd_nvme->kobj.kset = tlmi_priv.authentication_kset; 862 + ret = kobject_add(&tlmi_priv.pwd_nvme->kobj, NULL, "%s", "NVMe"); 863 + if (ret) 864 + goto fail_create_attr; 865 + 866 + ret = sysfs_create_group(&tlmi_priv.pwd_nvme->kobj, &auth_attr_group); 867 + if (ret) 868 + goto fail_create_attr; 869 + } 1002 870 1003 871 return ret; 1004 872 ··· 1041 851 } 1042 852 1043 853 /* ---- Base Driver -------------------------------------------------------- */ 854 + static struct tlmi_pwd_setting *tlmi_create_auth(const char *pwd_type, 855 + const char *pwd_role) 856 + { 857 + struct tlmi_pwd_setting *new_pwd; 858 + 859 + new_pwd = kzalloc(sizeof(struct tlmi_pwd_setting), GFP_KERNEL); 860 + if (!new_pwd) 861 + return NULL; 862 + 863 + strscpy(new_pwd->kbdlang, "us", TLMI_LANG_MAXLEN); 864 + new_pwd->encoding = TLMI_ENCODING_ASCII; 865 + new_pwd->pwd_type = pwd_type; 866 + new_pwd->role = pwd_role; 867 + new_pwd->minlen = tlmi_priv.pwdcfg.core.min_length; 868 + new_pwd->maxlen = tlmi_priv.pwdcfg.core.max_length; 869 + new_pwd->index = 0; 870 + return new_pwd; 871 + } 872 + 1044 873 static int tlmi_analyze(void) 1045 874 { 1046 - struct tlmi_pwdcfg pwdcfg; 1047 875 acpi_status status; 1048 876 int i, ret; 1049 877 ··· 1080 872 1081 873 if (wmi_has_guid(LENOVO_DEBUG_CMD_GUID)) 1082 874 tlmi_priv.can_debug_cmd = true; 875 + 876 + if (wmi_has_guid(LENOVO_OPCODE_IF_GUID)) 877 + tlmi_priv.opcode_support = true; 1083 878 1084 879 /* 1085 880 * Try to find the number of valid settings of this machine ··· 1134 923 } 1135 924 1136 925 /* Create password setting structure */ 1137 - ret = tlmi_get_pwd_settings(&pwdcfg); 926 + ret = tlmi_get_pwd_settings(&tlmi_priv.pwdcfg); 1138 927 if (ret) 1139 928 goto fail_clear_attr; 1140 929 1141 - tlmi_priv.pwd_admin = kzalloc(sizeof(struct tlmi_pwd_setting), GFP_KERNEL); 930 + tlmi_priv.pwd_admin = tlmi_create_auth("pap", "bios-admin"); 1142 931 if (!tlmi_priv.pwd_admin) { 1143 932 ret = -ENOMEM; 1144 933 goto fail_clear_attr; 1145 934 } 1146 - strscpy(tlmi_priv.pwd_admin->kbdlang, "us", TLMI_LANG_MAXLEN); 1147 - tlmi_priv.pwd_admin->encoding = TLMI_ENCODING_ASCII; 1148 - tlmi_priv.pwd_admin->pwd_type = "pap"; 1149 - tlmi_priv.pwd_admin->role = "bios-admin"; 1150 - tlmi_priv.pwd_admin->minlen = pwdcfg.min_length; 1151 - if (WARN_ON(pwdcfg.max_length >= TLMI_PWD_BUFSIZE)) 1152 - pwdcfg.max_length = TLMI_PWD_BUFSIZE - 1; 1153 - tlmi_priv.pwd_admin->maxlen = pwdcfg.max_length; 1154 - if (pwdcfg.password_state & TLMI_PAP_PWD) 935 + if (tlmi_priv.pwdcfg.core.password_state & TLMI_PAP_PWD) 1155 936 tlmi_priv.pwd_admin->valid = true; 1156 937 1157 938 kobject_init(&tlmi_priv.pwd_admin->kobj, &tlmi_pwd_setting_ktype); 1158 939 1159 - tlmi_priv.pwd_power = kzalloc(sizeof(struct tlmi_pwd_setting), GFP_KERNEL); 940 + tlmi_priv.pwd_power = tlmi_create_auth("pop", "power-on"); 1160 941 if (!tlmi_priv.pwd_power) { 1161 942 ret = -ENOMEM; 1162 - goto fail_free_pwd_admin; 943 + goto fail_clear_attr; 1163 944 } 1164 - strscpy(tlmi_priv.pwd_power->kbdlang, "us", TLMI_LANG_MAXLEN); 1165 - tlmi_priv.pwd_power->encoding = TLMI_ENCODING_ASCII; 1166 - tlmi_priv.pwd_power->pwd_type = "pop"; 1167 - tlmi_priv.pwd_power->role = "power-on"; 1168 - tlmi_priv.pwd_power->minlen = pwdcfg.min_length; 1169 - tlmi_priv.pwd_power->maxlen = pwdcfg.max_length; 1170 - 1171 - if (pwdcfg.password_state & TLMI_POP_PWD) 945 + if (tlmi_priv.pwdcfg.core.password_state & TLMI_POP_PWD) 1172 946 tlmi_priv.pwd_power->valid = true; 1173 947 1174 948 kobject_init(&tlmi_priv.pwd_power->kobj, &tlmi_pwd_setting_ktype); 1175 949 950 + if (tlmi_priv.opcode_support) { 951 + tlmi_priv.pwd_system = tlmi_create_auth("sys", "system"); 952 + if (!tlmi_priv.pwd_system) { 953 + ret = -ENOMEM; 954 + goto fail_clear_attr; 955 + } 956 + if (tlmi_priv.pwdcfg.core.password_state & TLMI_SYS_PWD) 957 + tlmi_priv.pwd_system->valid = true; 958 + 959 + kobject_init(&tlmi_priv.pwd_system->kobj, &tlmi_pwd_setting_ktype); 960 + 961 + tlmi_priv.pwd_hdd = tlmi_create_auth("hdd", "hdd"); 962 + if (!tlmi_priv.pwd_hdd) { 963 + ret = -ENOMEM; 964 + goto fail_clear_attr; 965 + } 966 + kobject_init(&tlmi_priv.pwd_hdd->kobj, &tlmi_pwd_setting_ktype); 967 + 968 + tlmi_priv.pwd_nvme = tlmi_create_auth("nvm", "nvme"); 969 + if (!tlmi_priv.pwd_nvme) { 970 + ret = -ENOMEM; 971 + goto fail_clear_attr; 972 + } 973 + kobject_init(&tlmi_priv.pwd_nvme->kobj, &tlmi_pwd_setting_ktype); 974 + 975 + if (tlmi_priv.pwdcfg.core.password_state & TLMI_HDD_PWD) { 976 + /* Check if PWD is configured and set index to first drive found */ 977 + if (tlmi_priv.pwdcfg.ext.hdd_user_password || 978 + tlmi_priv.pwdcfg.ext.hdd_master_password) { 979 + tlmi_priv.pwd_hdd->valid = true; 980 + if (tlmi_priv.pwdcfg.ext.hdd_master_password) 981 + tlmi_priv.pwd_hdd->index = 982 + ffs(tlmi_priv.pwdcfg.ext.hdd_master_password) - 1; 983 + else 984 + tlmi_priv.pwd_hdd->index = 985 + ffs(tlmi_priv.pwdcfg.ext.hdd_user_password) - 1; 986 + } 987 + if (tlmi_priv.pwdcfg.ext.nvme_user_password || 988 + tlmi_priv.pwdcfg.ext.nvme_master_password) { 989 + tlmi_priv.pwd_nvme->valid = true; 990 + if (tlmi_priv.pwdcfg.ext.nvme_master_password) 991 + tlmi_priv.pwd_nvme->index = 992 + ffs(tlmi_priv.pwdcfg.ext.nvme_master_password) - 1; 993 + else 994 + tlmi_priv.pwd_nvme->index = 995 + ffs(tlmi_priv.pwdcfg.ext.nvme_user_password) - 1; 996 + } 997 + } 998 + } 1176 999 return 0; 1177 1000 1178 - fail_free_pwd_admin: 1179 - kfree(tlmi_priv.pwd_admin); 1180 1001 fail_clear_attr: 1181 1002 for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) { 1182 1003 if (tlmi_priv.setting[i]) { ··· 1216 973 kfree(tlmi_priv.setting[i]); 1217 974 } 1218 975 } 976 + kfree(tlmi_priv.pwd_admin); 977 + kfree(tlmi_priv.pwd_power); 978 + kfree(tlmi_priv.pwd_system); 979 + kfree(tlmi_priv.pwd_hdd); 980 + kfree(tlmi_priv.pwd_nvme); 1219 981 return ret; 1220 982 } 1221 983
+27 -1
drivers/platform/x86/think-lmi.h
··· 9 9 #define TLMI_SETTINGS_MAXLEN 512 10 10 #define TLMI_PWD_BUFSIZE 129 11 11 #define TLMI_LANG_MAXLEN 4 12 + #define TLMI_INDEX_MAX 32 12 13 13 14 /* Possible error values */ 14 15 struct tlmi_err_codes { ··· 22 21 TLMI_ENCODING_SCANCODE, 23 22 }; 24 23 24 + enum level_option { 25 + TLMI_LEVEL_USER, 26 + TLMI_LEVEL_MASTER, 27 + }; 28 + 25 29 /* password configuration details */ 26 - struct tlmi_pwdcfg { 30 + struct tlmi_pwdcfg_core { 27 31 uint32_t password_mode; 28 32 uint32_t password_state; 29 33 uint32_t min_length; 30 34 uint32_t max_length; 31 35 uint32_t supported_encodings; 32 36 uint32_t supported_keyboard; 37 + }; 38 + 39 + struct tlmi_pwdcfg_ext { 40 + uint32_t hdd_user_password; 41 + uint32_t hdd_master_password; 42 + uint32_t nvme_user_password; 43 + uint32_t nvme_master_password; 44 + }; 45 + 46 + struct tlmi_pwdcfg { 47 + struct tlmi_pwdcfg_core core; 48 + struct tlmi_pwdcfg_ext ext; 33 49 }; 34 50 35 51 /* password setting details */ ··· 60 42 int maxlen; 61 43 enum encoding_option encoding; 62 44 char kbdlang[TLMI_LANG_MAXLEN]; 45 + int index; /*Used for HDD and NVME auth */ 46 + enum level_option level; 63 47 }; 64 48 65 49 /* Attribute setting details */ ··· 81 61 bool can_get_password_settings; 82 62 bool pending_changes; 83 63 bool can_debug_cmd; 64 + bool opcode_support; 84 65 85 66 struct tlmi_attr_setting *setting[TLMI_SETTINGS_COUNT]; 86 67 struct device *class_dev; 87 68 struct kset *attribute_kset; 88 69 struct kset *authentication_kset; 70 + 71 + struct tlmi_pwdcfg pwdcfg; 89 72 struct tlmi_pwd_setting *pwd_admin; 90 73 struct tlmi_pwd_setting *pwd_power; 74 + struct tlmi_pwd_setting *pwd_system; 75 + struct tlmi_pwd_setting *pwd_hdd; 76 + struct tlmi_pwd_setting *pwd_nvme; 91 77 }; 92 78 93 79 #endif /* !_THINK_LMI_H_ */