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/mutex.h> |
18 | #include <linux/string_helpers.h> |
19 | #include <linux/types.h> |
20 | #include <linux/dmi.h> |
21 | #include <linux/wmi.h> |
22 | #include "firmware_attributes_class.h" |
23 | #include "think-lmi.h" |
24 | |
25 | static bool debug_support; |
26 | module_param(debug_support, bool, 0444); |
27 | MODULE_PARM_DESC(debug_support, "Enable debug command support" ); |
28 | |
29 | /* |
30 | * Name: BiosSetting |
31 | * Description: Get item name and settings for current LMI instance. |
32 | * Type: Query |
33 | * Returns: "Item,Value" |
34 | * Example: "WakeOnLAN,Enable" |
35 | */ |
36 | #define LENOVO_BIOS_SETTING_GUID "51F5230E-9677-46CD-A1CF-C0B23EE34DB7" |
37 | |
38 | /* |
39 | * Name: SetBiosSetting |
40 | * Description: Change the BIOS setting to the desired value using the SetBiosSetting |
41 | * class. To save the settings, use the SaveBiosSetting class. |
42 | * BIOS settings and values are case sensitive. |
43 | * After making changes to the BIOS settings, you must reboot the computer |
44 | * before the changes will take effect. |
45 | * Type: Method |
46 | * Arguments: "Item,Value,Password,Encoding,KbdLang;" |
47 | * Example: "WakeOnLAN,Disable,pa55w0rd,ascii,us;" |
48 | */ |
49 | #define LENOVO_SET_BIOS_SETTINGS_GUID "98479A64-33F5-4E33-A707-8E251EBBC3A1" |
50 | |
51 | /* |
52 | * Name: SaveBiosSettings |
53 | * Description: Save any pending changes in settings. |
54 | * Type: Method |
55 | * Arguments: "Password,Encoding,KbdLang;" |
56 | * Example: "pa55w0rd,ascii,us;" |
57 | */ |
58 | #define LENOVO_SAVE_BIOS_SETTINGS_GUID "6A4B54EF-A5ED-4D33-9455-B0D9B48DF4B3" |
59 | |
60 | /* |
61 | * Name: BiosPasswordSettings |
62 | * Description: Return BIOS Password settings |
63 | * Type: Query |
64 | * Returns: PasswordMode, PasswordState, MinLength, MaxLength, |
65 | * SupportedEncoding, SupportedKeyboard |
66 | */ |
67 | #define LENOVO_BIOS_PASSWORD_SETTINGS_GUID "8ADB159E-1E32-455C-BC93-308A7ED98246" |
68 | |
69 | /* |
70 | * Name: SetBiosPassword |
71 | * Description: Change a specific password. |
72 | * - BIOS settings cannot be changed at the same boot as power-on |
73 | * passwords (POP) and hard disk passwords (HDP). If you want to change |
74 | * BIOS settings and POP or HDP, you must reboot the system after changing |
75 | * one of them. |
76 | * - A password cannot be set using this method when one does not already |
77 | * exist. Passwords can only be updated or cleared. |
78 | * Type: Method |
79 | * Arguments: "PasswordType,CurrentPassword,NewPassword,Encoding,KbdLang;" |
80 | * Example: "pop,pa55w0rd,newpa55w0rd,ascii,us;” |
81 | */ |
82 | #define LENOVO_SET_BIOS_PASSWORD_GUID "2651D9FD-911C-4B69-B94E-D0DED5963BD7" |
83 | |
84 | /* |
85 | * Name: GetBiosSelections |
86 | * Description: Return a list of valid settings for a given item. |
87 | * Type: Method |
88 | * Arguments: "Item" |
89 | * Returns: "Value1,Value2,Value3,..." |
90 | * Example: |
91 | * -> "FlashOverLAN" |
92 | * <- "Enabled,Disabled" |
93 | */ |
94 | #define LENOVO_GET_BIOS_SELECTIONS_GUID "7364651A-132F-4FE7-ADAA-40C6C7EE2E3B" |
95 | |
96 | /* |
97 | * Name: DebugCmd |
98 | * Description: Debug entry method for entering debug commands to the BIOS |
99 | */ |
100 | #define LENOVO_DEBUG_CMD_GUID "7FF47003-3B6C-4E5E-A227-E979824A85D1" |
101 | |
102 | /* |
103 | * Name: OpcodeIF |
104 | * Description: Opcode interface which provides the ability to set multiple |
105 | * parameters and then trigger an action with a final command. |
106 | * This is particularly useful for simplifying setting passwords. |
107 | * With this support comes the ability to set System, HDD and NVMe |
108 | * passwords. |
109 | * This is currently available on ThinkCenter and ThinkStations platforms |
110 | */ |
111 | #define LENOVO_OPCODE_IF_GUID "DFDDEF2C-57D4-48ce-B196-0FB787D90836" |
112 | |
113 | /* |
114 | * Name: SetBiosCert |
115 | * Description: Install BIOS certificate. |
116 | * Type: Method |
117 | * Arguments: "Certificate,Password" |
118 | * You must reboot the computer before the changes will take effect. |
119 | */ |
120 | #define LENOVO_SET_BIOS_CERT_GUID "26861C9F-47E9-44C4-BD8B-DFE7FA2610FE" |
121 | |
122 | /* |
123 | * Name: UpdateBiosCert |
124 | * Description: Update BIOS certificate. |
125 | * Type: Method |
126 | * Format: "Certificate,Signature" |
127 | * You must reboot the computer before the changes will take effect. |
128 | */ |
129 | #define LENOVO_UPDATE_BIOS_CERT_GUID "9AA3180A-9750-41F7-B9F7-D5D3B1BAC3CE" |
130 | |
131 | /* |
132 | * Name: ClearBiosCert |
133 | * Description: Uninstall BIOS certificate. |
134 | * Type: Method |
135 | * Format: "Serial,Signature" |
136 | * You must reboot the computer before the changes will take effect. |
137 | */ |
138 | #define LENOVO_CLEAR_BIOS_CERT_GUID "B2BC39A7-78DD-4D71-B059-A510DEC44890" |
139 | /* |
140 | * Name: CertToPassword |
141 | * Description: Switch from certificate to password authentication. |
142 | * Type: Method |
143 | * Format: "Password,Signature" |
144 | * You must reboot the computer before the changes will take effect. |
145 | */ |
146 | #define LENOVO_CERT_TO_PASSWORD_GUID "0DE8590D-5510-4044-9621-77C227F5A70D" |
147 | |
148 | /* |
149 | * Name: SetBiosSettingCert |
150 | * Description: Set attribute using certificate authentication. |
151 | * Type: Method |
152 | * Format: "Item,Value,Signature" |
153 | */ |
154 | #define LENOVO_SET_BIOS_SETTING_CERT_GUID "34A008CC-D205-4B62-9E67-31DFA8B90003" |
155 | |
156 | /* |
157 | * Name: SaveBiosSettingCert |
158 | * Description: Save any pending changes in settings. |
159 | * Type: Method |
160 | * Format: "Signature" |
161 | */ |
162 | #define LENOVO_SAVE_BIOS_SETTING_CERT_GUID "C050FB9D-DF5F-4606-B066-9EFC401B2551" |
163 | |
164 | /* |
165 | * Name: CertThumbprint |
166 | * Description: Display Certificate thumbprints |
167 | * Type: Query |
168 | * Returns: MD5, SHA1 & SHA256 thumbprints |
169 | */ |
170 | #define LENOVO_CERT_THUMBPRINT_GUID "C59119ED-1C0D-4806-A8E9-59AA318176C4" |
171 | |
172 | #define TLMI_POP_PWD BIT(0) /* Supervisor */ |
173 | #define TLMI_PAP_PWD BIT(1) /* Power-on */ |
174 | #define TLMI_HDD_PWD BIT(2) /* HDD/NVME */ |
175 | #define TLMI_SMP_PWD BIT(6) /* System Management */ |
176 | #define TLMI_CERT BIT(7) /* Certificate Based */ |
177 | |
178 | #define to_tlmi_pwd_setting(kobj) container_of(kobj, struct tlmi_pwd_setting, kobj) |
179 | #define to_tlmi_attr_setting(kobj) container_of(kobj, struct tlmi_attr_setting, kobj) |
180 | |
181 | static const struct tlmi_err_codes tlmi_errs[] = { |
182 | {"Success" , 0}, |
183 | {"Not Supported" , -EOPNOTSUPP}, |
184 | {"Invalid Parameter" , -EINVAL}, |
185 | {"Access Denied" , -EACCES}, |
186 | {"System Busy" , -EBUSY}, |
187 | }; |
188 | |
189 | static const char * const encoding_options[] = { |
190 | [TLMI_ENCODING_ASCII] = "ascii" , |
191 | [TLMI_ENCODING_SCANCODE] = "scancode" , |
192 | }; |
193 | static const char * const level_options[] = { |
194 | [TLMI_LEVEL_USER] = "user" , |
195 | [TLMI_LEVEL_MASTER] = "master" , |
196 | }; |
197 | static struct think_lmi tlmi_priv; |
198 | static const struct class *fw_attr_class; |
199 | static DEFINE_MUTEX(tlmi_mutex); |
200 | |
201 | /* Convert BIOS WMI error string to suitable error code */ |
202 | static int tlmi_errstr_to_err(const char *errstr) |
203 | { |
204 | int i; |
205 | |
206 | for (i = 0; i < sizeof(tlmi_errs)/sizeof(struct tlmi_err_codes); i++) { |
207 | if (!strcmp(tlmi_errs[i].err_str, errstr)) |
208 | return tlmi_errs[i].err_code; |
209 | } |
210 | return -EPERM; |
211 | } |
212 | |
213 | /* Extract error string from WMI return buffer */ |
214 | static int (const struct acpi_buffer *output) |
215 | { |
216 | const union acpi_object *obj; |
217 | |
218 | obj = output->pointer; |
219 | if (!obj) |
220 | return -ENOMEM; |
221 | if (obj->type != ACPI_TYPE_STRING || !obj->string.pointer) |
222 | return -EIO; |
223 | |
224 | return tlmi_errstr_to_err(errstr: obj->string.pointer); |
225 | } |
226 | |
227 | /* Utility function to execute WMI call to BIOS */ |
228 | static int tlmi_simple_call(const char *guid, const char *arg) |
229 | { |
230 | const struct acpi_buffer input = { strlen(arg), (char *)arg }; |
231 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
232 | acpi_status status; |
233 | int i, err; |
234 | |
235 | /* |
236 | * Duplicated call required to match BIOS workaround for behavior |
237 | * seen when WMI accessed via scripting on other OS. |
238 | */ |
239 | for (i = 0; i < 2; i++) { |
240 | /* (re)initialize output buffer to default state */ |
241 | output.length = ACPI_ALLOCATE_BUFFER; |
242 | output.pointer = NULL; |
243 | |
244 | status = wmi_evaluate_method(guid, instance: 0, method_id: 0, in: &input, out: &output); |
245 | if (ACPI_FAILURE(status)) { |
246 | kfree(objp: output.pointer); |
247 | return -EIO; |
248 | } |
249 | err = tlmi_extract_error(output: &output); |
250 | kfree(objp: output.pointer); |
251 | if (err) |
252 | return err; |
253 | } |
254 | return 0; |
255 | } |
256 | |
257 | /* Extract output string from WMI return buffer */ |
258 | static int (const struct acpi_buffer *output, |
259 | char **string) |
260 | { |
261 | const union acpi_object *obj; |
262 | char *s; |
263 | |
264 | obj = output->pointer; |
265 | if (!obj) |
266 | return -ENOMEM; |
267 | if (obj->type != ACPI_TYPE_STRING || !obj->string.pointer) |
268 | return -EIO; |
269 | |
270 | s = kstrdup(s: obj->string.pointer, GFP_KERNEL); |
271 | if (!s) |
272 | return -ENOMEM; |
273 | *string = s; |
274 | return 0; |
275 | } |
276 | |
277 | /* ------ Core interface functions ------------*/ |
278 | |
279 | /* Get password settings from BIOS */ |
280 | static int tlmi_get_pwd_settings(struct tlmi_pwdcfg *pwdcfg) |
281 | { |
282 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
283 | const union acpi_object *obj; |
284 | acpi_status status; |
285 | int copy_size; |
286 | |
287 | if (!tlmi_priv.can_get_password_settings) |
288 | return -EOPNOTSUPP; |
289 | |
290 | status = wmi_query_block(LENOVO_BIOS_PASSWORD_SETTINGS_GUID, instance: 0, |
291 | out: &output); |
292 | if (ACPI_FAILURE(status)) |
293 | return -EIO; |
294 | |
295 | obj = output.pointer; |
296 | if (!obj) |
297 | return -ENOMEM; |
298 | if (obj->type != ACPI_TYPE_BUFFER || !obj->buffer.pointer) { |
299 | kfree(objp: obj); |
300 | return -EIO; |
301 | } |
302 | /* |
303 | * The size of thinkpad_wmi_pcfg on ThinkStation is larger than ThinkPad. |
304 | * To make the driver compatible on different brands, we permit it to get |
305 | * the data in below case. |
306 | * Settings must have at minimum the core fields available |
307 | */ |
308 | if (obj->buffer.length < sizeof(struct tlmi_pwdcfg_core)) { |
309 | pr_warn("Unknown pwdcfg buffer length %d\n" , obj->buffer.length); |
310 | kfree(objp: obj); |
311 | return -EIO; |
312 | } |
313 | |
314 | copy_size = min_t(size_t, obj->buffer.length, sizeof(struct tlmi_pwdcfg)); |
315 | |
316 | memcpy(pwdcfg, obj->buffer.pointer, copy_size); |
317 | kfree(objp: obj); |
318 | |
319 | if (WARN_ON(pwdcfg->core.max_length >= TLMI_PWD_BUFSIZE)) |
320 | pwdcfg->core.max_length = TLMI_PWD_BUFSIZE - 1; |
321 | return 0; |
322 | } |
323 | |
324 | static int tlmi_save_bios_settings(const char *password) |
325 | { |
326 | return tlmi_simple_call(LENOVO_SAVE_BIOS_SETTINGS_GUID, |
327 | arg: password); |
328 | } |
329 | |
330 | static int tlmi_opcode_setting(char *setting, const char *value) |
331 | { |
332 | char *opcode_str; |
333 | int ret; |
334 | |
335 | opcode_str = kasprintf(GFP_KERNEL, fmt: "%s:%s;" , setting, value); |
336 | if (!opcode_str) |
337 | return -ENOMEM; |
338 | |
339 | ret = tlmi_simple_call(LENOVO_OPCODE_IF_GUID, arg: opcode_str); |
340 | kfree(objp: opcode_str); |
341 | return ret; |
342 | } |
343 | |
344 | static int tlmi_setting(int item, char **value, const char *guid_string) |
345 | { |
346 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
347 | acpi_status status; |
348 | int ret; |
349 | |
350 | status = wmi_query_block(guid: guid_string, instance: item, out: &output); |
351 | if (ACPI_FAILURE(status)) { |
352 | kfree(objp: output.pointer); |
353 | return -EIO; |
354 | } |
355 | |
356 | ret = tlmi_extract_output_string(output: &output, string: value); |
357 | kfree(objp: output.pointer); |
358 | return ret; |
359 | } |
360 | |
361 | static int tlmi_get_bios_selections(const char *item, char **value) |
362 | { |
363 | const struct acpi_buffer input = { strlen(item), (char *)item }; |
364 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
365 | acpi_status status; |
366 | int ret; |
367 | |
368 | status = wmi_evaluate_method(LENOVO_GET_BIOS_SELECTIONS_GUID, |
369 | instance: 0, method_id: 0, in: &input, out: &output); |
370 | |
371 | if (ACPI_FAILURE(status)) { |
372 | kfree(objp: output.pointer); |
373 | return -EIO; |
374 | } |
375 | |
376 | ret = tlmi_extract_output_string(output: &output, string: value); |
377 | kfree(objp: output.pointer); |
378 | return ret; |
379 | } |
380 | |
381 | /* ---- Authentication sysfs --------------------------------------------------------- */ |
382 | static ssize_t is_enabled_show(struct kobject *kobj, struct kobj_attribute *attr, |
383 | char *buf) |
384 | { |
385 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
386 | |
387 | return sysfs_emit(buf, fmt: "%d\n" , setting->valid); |
388 | } |
389 | |
390 | static struct kobj_attribute auth_is_pass_set = __ATTR_RO(is_enabled); |
391 | |
392 | static ssize_t current_password_store(struct kobject *kobj, |
393 | struct kobj_attribute *attr, |
394 | const char *buf, size_t count) |
395 | { |
396 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
397 | size_t pwdlen; |
398 | |
399 | pwdlen = strlen(buf); |
400 | /* pwdlen == 0 is allowed to clear the password */ |
401 | if (pwdlen && ((pwdlen < setting->minlen) || (pwdlen > setting->maxlen))) |
402 | return -EINVAL; |
403 | |
404 | strscpy(setting->password, buf, setting->maxlen); |
405 | /* Strip out CR if one is present, setting password won't work if it is present */ |
406 | strreplace(str: setting->password, old: '\n', new: '\0'); |
407 | return count; |
408 | } |
409 | |
410 | static struct kobj_attribute auth_current_password = __ATTR_WO(current_password); |
411 | |
412 | static ssize_t new_password_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 | char *auth_str, *new_pwd; |
418 | size_t pwdlen; |
419 | int ret; |
420 | |
421 | if (!capable(CAP_SYS_ADMIN)) |
422 | return -EPERM; |
423 | |
424 | if (!tlmi_priv.can_set_bios_password) |
425 | return -EOPNOTSUPP; |
426 | |
427 | /* Strip out CR if one is present, setting password won't work if it is present */ |
428 | new_pwd = kstrdup_and_replace(src: buf, old: '\n', new: '\0', GFP_KERNEL); |
429 | if (!new_pwd) |
430 | return -ENOMEM; |
431 | |
432 | /* Use lock in case multiple WMI operations needed */ |
433 | mutex_lock(&tlmi_mutex); |
434 | |
435 | pwdlen = strlen(new_pwd); |
436 | /* pwdlen == 0 is allowed to clear the password */ |
437 | if (pwdlen && ((pwdlen < setting->minlen) || (pwdlen > setting->maxlen))) { |
438 | ret = -EINVAL; |
439 | goto out; |
440 | } |
441 | |
442 | /* If opcode support is present use that interface */ |
443 | if (tlmi_priv.opcode_support) { |
444 | char pwd_type[8]; |
445 | |
446 | /* Special handling required for HDD and NVMe passwords */ |
447 | if (setting == tlmi_priv.pwd_hdd) { |
448 | if (setting->level == TLMI_LEVEL_USER) |
449 | sprintf(buf: pwd_type, fmt: "uhdp%d" , setting->index); |
450 | else |
451 | sprintf(buf: pwd_type, fmt: "mhdp%d" , setting->index); |
452 | } else if (setting == tlmi_priv.pwd_nvme) { |
453 | if (setting->level == TLMI_LEVEL_USER) |
454 | sprintf(buf: pwd_type, fmt: "udrp%d" , setting->index); |
455 | else |
456 | sprintf(buf: pwd_type, fmt: "adrp%d" , setting->index); |
457 | } else { |
458 | sprintf(buf: pwd_type, fmt: "%s" , setting->pwd_type); |
459 | } |
460 | |
461 | ret = tlmi_opcode_setting(setting: "WmiOpcodePasswordType" , value: pwd_type); |
462 | if (ret) |
463 | goto out; |
464 | |
465 | if (tlmi_priv.pwd_admin->valid) { |
466 | ret = tlmi_opcode_setting(setting: "WmiOpcodePasswordAdmin" , |
467 | value: tlmi_priv.pwd_admin->password); |
468 | if (ret) |
469 | goto out; |
470 | } |
471 | ret = tlmi_opcode_setting(setting: "WmiOpcodePasswordCurrent01" , value: setting->password); |
472 | if (ret) |
473 | goto out; |
474 | ret = tlmi_opcode_setting(setting: "WmiOpcodePasswordNew01" , value: new_pwd); |
475 | if (ret) |
476 | goto out; |
477 | ret = tlmi_simple_call(LENOVO_OPCODE_IF_GUID, arg: "WmiOpcodePasswordSetUpdate;" ); |
478 | } else { |
479 | /* Format: 'PasswordType,CurrentPw,NewPw,Encoding,KbdLang;' */ |
480 | auth_str = kasprintf(GFP_KERNEL, fmt: "%s,%s,%s,%s,%s;" , |
481 | setting->pwd_type, setting->password, new_pwd, |
482 | encoding_options[setting->encoding], setting->kbdlang); |
483 | if (!auth_str) { |
484 | ret = -ENOMEM; |
485 | goto out; |
486 | } |
487 | ret = tlmi_simple_call(LENOVO_SET_BIOS_PASSWORD_GUID, arg: auth_str); |
488 | kfree(objp: auth_str); |
489 | } |
490 | out: |
491 | mutex_unlock(lock: &tlmi_mutex); |
492 | kfree(objp: new_pwd); |
493 | return ret ?: count; |
494 | } |
495 | |
496 | static struct kobj_attribute auth_new_password = __ATTR_WO(new_password); |
497 | |
498 | static ssize_t min_password_length_show(struct kobject *kobj, struct kobj_attribute *attr, |
499 | char *buf) |
500 | { |
501 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
502 | |
503 | return sysfs_emit(buf, fmt: "%d\n" , setting->minlen); |
504 | } |
505 | |
506 | static struct kobj_attribute auth_min_pass_length = __ATTR_RO(min_password_length); |
507 | |
508 | static ssize_t max_password_length_show(struct kobject *kobj, struct kobj_attribute *attr, |
509 | char *buf) |
510 | { |
511 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
512 | |
513 | return sysfs_emit(buf, fmt: "%d\n" , setting->maxlen); |
514 | } |
515 | static struct kobj_attribute auth_max_pass_length = __ATTR_RO(max_password_length); |
516 | |
517 | static ssize_t mechanism_show(struct kobject *kobj, struct kobj_attribute *attr, |
518 | char *buf) |
519 | { |
520 | return sysfs_emit(buf, fmt: "password\n" ); |
521 | } |
522 | static struct kobj_attribute auth_mechanism = __ATTR_RO(mechanism); |
523 | |
524 | static ssize_t encoding_show(struct kobject *kobj, struct kobj_attribute *attr, |
525 | char *buf) |
526 | { |
527 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
528 | |
529 | return sysfs_emit(buf, fmt: "%s\n" , encoding_options[setting->encoding]); |
530 | } |
531 | |
532 | static ssize_t encoding_store(struct kobject *kobj, |
533 | struct kobj_attribute *attr, |
534 | const char *buf, size_t count) |
535 | { |
536 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
537 | int i; |
538 | |
539 | /* Scan for a matching profile */ |
540 | i = sysfs_match_string(encoding_options, buf); |
541 | if (i < 0) |
542 | return -EINVAL; |
543 | |
544 | setting->encoding = i; |
545 | return count; |
546 | } |
547 | |
548 | static struct kobj_attribute auth_encoding = __ATTR_RW(encoding); |
549 | |
550 | static ssize_t kbdlang_show(struct kobject *kobj, struct kobj_attribute *attr, |
551 | char *buf) |
552 | { |
553 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
554 | |
555 | return sysfs_emit(buf, fmt: "%s\n" , setting->kbdlang); |
556 | } |
557 | |
558 | static ssize_t kbdlang_store(struct kobject *kobj, |
559 | struct kobj_attribute *attr, |
560 | const char *buf, size_t count) |
561 | { |
562 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
563 | int length; |
564 | |
565 | /* Calculate length till '\n' or terminating 0 */ |
566 | length = strchrnul(buf, '\n') - buf; |
567 | if (!length || length >= TLMI_LANG_MAXLEN) |
568 | return -EINVAL; |
569 | |
570 | memcpy(setting->kbdlang, buf, length); |
571 | setting->kbdlang[length] = '\0'; |
572 | return count; |
573 | } |
574 | |
575 | static struct kobj_attribute auth_kbdlang = __ATTR_RW(kbdlang); |
576 | |
577 | static ssize_t role_show(struct kobject *kobj, struct kobj_attribute *attr, |
578 | char *buf) |
579 | { |
580 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
581 | |
582 | return sysfs_emit(buf, fmt: "%s\n" , setting->role); |
583 | } |
584 | static struct kobj_attribute auth_role = __ATTR_RO(role); |
585 | |
586 | static ssize_t index_show(struct kobject *kobj, struct kobj_attribute *attr, |
587 | char *buf) |
588 | { |
589 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
590 | |
591 | return sysfs_emit(buf, fmt: "%d\n" , setting->index); |
592 | } |
593 | |
594 | static ssize_t index_store(struct kobject *kobj, |
595 | struct kobj_attribute *attr, |
596 | const char *buf, size_t count) |
597 | { |
598 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
599 | int err, val; |
600 | |
601 | err = kstrtoint(s: buf, base: 10, res: &val); |
602 | if (err < 0) |
603 | return err; |
604 | |
605 | if (val < 0 || val > TLMI_INDEX_MAX) |
606 | return -EINVAL; |
607 | |
608 | setting->index = val; |
609 | return count; |
610 | } |
611 | |
612 | static struct kobj_attribute auth_index = __ATTR_RW(index); |
613 | |
614 | static ssize_t level_show(struct kobject *kobj, struct kobj_attribute *attr, |
615 | char *buf) |
616 | { |
617 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
618 | |
619 | return sysfs_emit(buf, fmt: "%s\n" , level_options[setting->level]); |
620 | } |
621 | |
622 | static ssize_t level_store(struct kobject *kobj, |
623 | struct kobj_attribute *attr, |
624 | const char *buf, size_t count) |
625 | { |
626 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
627 | int i; |
628 | |
629 | /* Scan for a matching profile */ |
630 | i = sysfs_match_string(level_options, buf); |
631 | if (i < 0) |
632 | return -EINVAL; |
633 | |
634 | setting->level = i; |
635 | return count; |
636 | } |
637 | |
638 | static struct kobj_attribute auth_level = __ATTR_RW(level); |
639 | |
640 | static ssize_t cert_thumbprint(char *buf, const char *arg, int count) |
641 | { |
642 | const struct acpi_buffer input = { strlen(arg), (char *)arg }; |
643 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
644 | const union acpi_object *obj; |
645 | acpi_status status; |
646 | |
647 | status = wmi_evaluate_method(LENOVO_CERT_THUMBPRINT_GUID, instance: 0, method_id: 0, in: &input, out: &output); |
648 | if (ACPI_FAILURE(status)) { |
649 | kfree(objp: output.pointer); |
650 | return -EIO; |
651 | } |
652 | obj = output.pointer; |
653 | if (!obj) |
654 | return -ENOMEM; |
655 | if (obj->type != ACPI_TYPE_STRING || !obj->string.pointer) { |
656 | kfree(objp: output.pointer); |
657 | return -EIO; |
658 | } |
659 | count += sysfs_emit_at(buf, at: count, fmt: "%s : %s\n" , arg, (char *)obj->string.pointer); |
660 | kfree(objp: output.pointer); |
661 | |
662 | return count; |
663 | } |
664 | |
665 | static ssize_t certificate_thumbprint_show(struct kobject *kobj, struct kobj_attribute *attr, |
666 | char *buf) |
667 | { |
668 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
669 | int count = 0; |
670 | |
671 | if (!tlmi_priv.certificate_support || !setting->cert_installed) |
672 | return -EOPNOTSUPP; |
673 | |
674 | count += cert_thumbprint(buf, arg: "Md5" , count); |
675 | count += cert_thumbprint(buf, arg: "Sha1" , count); |
676 | count += cert_thumbprint(buf, arg: "Sha256" , count); |
677 | return count; |
678 | } |
679 | |
680 | static struct kobj_attribute auth_cert_thumb = __ATTR_RO(certificate_thumbprint); |
681 | |
682 | static ssize_t cert_to_password_store(struct kobject *kobj, |
683 | struct kobj_attribute *attr, |
684 | const char *buf, size_t count) |
685 | { |
686 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
687 | char *auth_str, *passwd; |
688 | int ret; |
689 | |
690 | if (!capable(CAP_SYS_ADMIN)) |
691 | return -EPERM; |
692 | |
693 | if (!tlmi_priv.certificate_support) |
694 | return -EOPNOTSUPP; |
695 | |
696 | if (!setting->cert_installed) |
697 | return -EINVAL; |
698 | |
699 | if (!setting->signature || !setting->signature[0]) |
700 | return -EACCES; |
701 | |
702 | /* Strip out CR if one is present */ |
703 | passwd = kstrdup_and_replace(src: buf, old: '\n', new: '\0', GFP_KERNEL); |
704 | if (!passwd) |
705 | return -ENOMEM; |
706 | |
707 | /* Format: 'Password,Signature' */ |
708 | auth_str = kasprintf(GFP_KERNEL, fmt: "%s,%s" , passwd, setting->signature); |
709 | if (!auth_str) { |
710 | kfree_sensitive(objp: passwd); |
711 | return -ENOMEM; |
712 | } |
713 | ret = tlmi_simple_call(LENOVO_CERT_TO_PASSWORD_GUID, arg: auth_str); |
714 | kfree(objp: auth_str); |
715 | kfree_sensitive(objp: passwd); |
716 | |
717 | return ret ?: count; |
718 | } |
719 | |
720 | static struct kobj_attribute auth_cert_to_password = __ATTR_WO(cert_to_password); |
721 | |
722 | static ssize_t certificate_store(struct kobject *kobj, |
723 | struct kobj_attribute *attr, |
724 | const char *buf, size_t count) |
725 | { |
726 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
727 | char *auth_str, *new_cert; |
728 | char *guid; |
729 | int ret; |
730 | |
731 | if (!capable(CAP_SYS_ADMIN)) |
732 | return -EPERM; |
733 | |
734 | if (!tlmi_priv.certificate_support) |
735 | return -EOPNOTSUPP; |
736 | |
737 | /* If empty then clear installed certificate */ |
738 | if ((buf[0] == '\0') || (buf[0] == '\n')) { /* Clear installed certificate */ |
739 | /* Check that signature is set */ |
740 | if (!setting->signature || !setting->signature[0]) |
741 | return -EACCES; |
742 | |
743 | /* Format: 'serial#, signature' */ |
744 | auth_str = kasprintf(GFP_KERNEL, fmt: "%s,%s" , |
745 | dmi_get_system_info(field: DMI_PRODUCT_SERIAL), |
746 | setting->signature); |
747 | if (!auth_str) |
748 | return -ENOMEM; |
749 | |
750 | ret = tlmi_simple_call(LENOVO_CLEAR_BIOS_CERT_GUID, arg: auth_str); |
751 | kfree(objp: auth_str); |
752 | |
753 | return ret ?: count; |
754 | } |
755 | |
756 | /* Strip out CR if one is present */ |
757 | new_cert = kstrdup_and_replace(src: buf, old: '\n', new: '\0', GFP_KERNEL); |
758 | if (!new_cert) |
759 | return -ENOMEM; |
760 | |
761 | if (setting->cert_installed) { |
762 | /* Certificate is installed so this is an update */ |
763 | if (!setting->signature || !setting->signature[0]) { |
764 | kfree(objp: new_cert); |
765 | return -EACCES; |
766 | } |
767 | guid = LENOVO_UPDATE_BIOS_CERT_GUID; |
768 | /* Format: 'Certificate,Signature' */ |
769 | auth_str = kasprintf(GFP_KERNEL, fmt: "%s,%s" , |
770 | new_cert, setting->signature); |
771 | } else { |
772 | /* This is a fresh install */ |
773 | if (!setting->valid || !setting->password[0]) { |
774 | kfree(objp: new_cert); |
775 | return -EACCES; |
776 | } |
777 | guid = LENOVO_SET_BIOS_CERT_GUID; |
778 | /* Format: 'Certificate,Admin-password' */ |
779 | auth_str = kasprintf(GFP_KERNEL, fmt: "%s,%s" , |
780 | new_cert, setting->password); |
781 | } |
782 | kfree(objp: new_cert); |
783 | if (!auth_str) |
784 | return -ENOMEM; |
785 | |
786 | ret = tlmi_simple_call(guid, arg: auth_str); |
787 | kfree(objp: auth_str); |
788 | |
789 | return ret ?: count; |
790 | } |
791 | |
792 | static struct kobj_attribute auth_certificate = __ATTR_WO(certificate); |
793 | |
794 | static ssize_t signature_store(struct kobject *kobj, |
795 | struct kobj_attribute *attr, |
796 | const char *buf, size_t count) |
797 | { |
798 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
799 | char *new_signature; |
800 | |
801 | if (!capable(CAP_SYS_ADMIN)) |
802 | return -EPERM; |
803 | |
804 | if (!tlmi_priv.certificate_support) |
805 | return -EOPNOTSUPP; |
806 | |
807 | /* Strip out CR if one is present */ |
808 | new_signature = kstrdup_and_replace(src: buf, old: '\n', new: '\0', GFP_KERNEL); |
809 | if (!new_signature) |
810 | return -ENOMEM; |
811 | |
812 | /* Free any previous signature */ |
813 | kfree(objp: setting->signature); |
814 | setting->signature = new_signature; |
815 | |
816 | return count; |
817 | } |
818 | |
819 | static struct kobj_attribute auth_signature = __ATTR_WO(signature); |
820 | |
821 | static ssize_t save_signature_store(struct kobject *kobj, |
822 | struct kobj_attribute *attr, |
823 | const char *buf, size_t count) |
824 | { |
825 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
826 | char *new_signature; |
827 | |
828 | if (!capable(CAP_SYS_ADMIN)) |
829 | return -EPERM; |
830 | |
831 | if (!tlmi_priv.certificate_support) |
832 | return -EOPNOTSUPP; |
833 | |
834 | /* Strip out CR if one is present */ |
835 | new_signature = kstrdup_and_replace(src: buf, old: '\n', new: '\0', GFP_KERNEL); |
836 | if (!new_signature) |
837 | return -ENOMEM; |
838 | |
839 | /* Free any previous signature */ |
840 | kfree(objp: setting->save_signature); |
841 | setting->save_signature = new_signature; |
842 | |
843 | return count; |
844 | } |
845 | |
846 | static struct kobj_attribute auth_save_signature = __ATTR_WO(save_signature); |
847 | |
848 | static umode_t auth_attr_is_visible(struct kobject *kobj, |
849 | struct attribute *attr, int n) |
850 | { |
851 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
852 | |
853 | /* We only want to display level and index settings on HDD/NVMe */ |
854 | if (attr == &auth_index.attr || attr == &auth_level.attr) { |
855 | if ((setting == tlmi_priv.pwd_hdd) || (setting == tlmi_priv.pwd_nvme)) |
856 | return attr->mode; |
857 | return 0; |
858 | } |
859 | |
860 | /* We only display certificates on Admin account, if supported */ |
861 | if (attr == &auth_certificate.attr || |
862 | attr == &auth_signature.attr || |
863 | attr == &auth_save_signature.attr || |
864 | attr == &auth_cert_thumb.attr || |
865 | attr == &auth_cert_to_password.attr) { |
866 | if ((setting == tlmi_priv.pwd_admin) && tlmi_priv.certificate_support) |
867 | return attr->mode; |
868 | return 0; |
869 | } |
870 | |
871 | /* Don't display un-needed settings if opcode available */ |
872 | if ((attr == &auth_encoding.attr || attr == &auth_kbdlang.attr) && |
873 | tlmi_priv.opcode_support) |
874 | return 0; |
875 | |
876 | return attr->mode; |
877 | } |
878 | |
879 | static struct attribute *auth_attrs[] = { |
880 | &auth_is_pass_set.attr, |
881 | &auth_min_pass_length.attr, |
882 | &auth_max_pass_length.attr, |
883 | &auth_current_password.attr, |
884 | &auth_new_password.attr, |
885 | &auth_role.attr, |
886 | &auth_mechanism.attr, |
887 | &auth_encoding.attr, |
888 | &auth_kbdlang.attr, |
889 | &auth_index.attr, |
890 | &auth_level.attr, |
891 | &auth_certificate.attr, |
892 | &auth_signature.attr, |
893 | &auth_save_signature.attr, |
894 | &auth_cert_thumb.attr, |
895 | &auth_cert_to_password.attr, |
896 | NULL |
897 | }; |
898 | |
899 | static const struct attribute_group auth_attr_group = { |
900 | .is_visible = auth_attr_is_visible, |
901 | .attrs = auth_attrs, |
902 | }; |
903 | |
904 | /* ---- Attributes sysfs --------------------------------------------------------- */ |
905 | static ssize_t display_name_show(struct kobject *kobj, struct kobj_attribute *attr, |
906 | char *buf) |
907 | { |
908 | struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); |
909 | |
910 | return sysfs_emit(buf, fmt: "%s\n" , setting->display_name); |
911 | } |
912 | |
913 | static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) |
914 | { |
915 | struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); |
916 | char *item, *value; |
917 | int ret; |
918 | |
919 | ret = tlmi_setting(item: setting->index, value: &item, LENOVO_BIOS_SETTING_GUID); |
920 | if (ret) |
921 | return ret; |
922 | |
923 | /* validate and split from `item,value` -> `value` */ |
924 | value = strpbrk(item, "," ); |
925 | if (!value || value == item || !strlen(value + 1)) |
926 | ret = -EINVAL; |
927 | else { |
928 | /* On Workstations remove the Options part after the value */ |
929 | strreplace(str: value, old: ';', new: '\0'); |
930 | ret = sysfs_emit(buf, fmt: "%s\n" , value + 1); |
931 | } |
932 | kfree(objp: item); |
933 | |
934 | return ret; |
935 | } |
936 | |
937 | static ssize_t possible_values_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) |
938 | { |
939 | struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); |
940 | |
941 | return sysfs_emit(buf, fmt: "%s\n" , setting->possible_values); |
942 | } |
943 | |
944 | static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, |
945 | char *buf) |
946 | { |
947 | struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); |
948 | |
949 | if (setting->possible_values) { |
950 | /* Figure out what setting type is as BIOS does not return this */ |
951 | if (strchr(setting->possible_values, ';')) |
952 | return sysfs_emit(buf, fmt: "enumeration\n" ); |
953 | } |
954 | /* Anything else is going to be a string */ |
955 | return sysfs_emit(buf, fmt: "string\n" ); |
956 | } |
957 | |
958 | static ssize_t current_value_store(struct kobject *kobj, |
959 | struct kobj_attribute *attr, |
960 | const char *buf, size_t count) |
961 | { |
962 | struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); |
963 | char *set_str = NULL, *new_setting = NULL; |
964 | char *auth_str = NULL; |
965 | int ret; |
966 | |
967 | if (!tlmi_priv.can_set_bios_settings) |
968 | return -EOPNOTSUPP; |
969 | |
970 | /* |
971 | * If we are using bulk saves a reboot should be done once save has |
972 | * been called |
973 | */ |
974 | if (tlmi_priv.save_mode == TLMI_SAVE_BULK && tlmi_priv.reboot_required) |
975 | return -EPERM; |
976 | |
977 | /* Strip out CR if one is present */ |
978 | new_setting = kstrdup_and_replace(src: buf, old: '\n', new: '\0', GFP_KERNEL); |
979 | if (!new_setting) |
980 | return -ENOMEM; |
981 | |
982 | /* Use lock in case multiple WMI operations needed */ |
983 | mutex_lock(&tlmi_mutex); |
984 | |
985 | /* Check if certificate authentication is enabled and active */ |
986 | if (tlmi_priv.certificate_support && tlmi_priv.pwd_admin->cert_installed) { |
987 | if (!tlmi_priv.pwd_admin->signature || !tlmi_priv.pwd_admin->save_signature) { |
988 | ret = -EINVAL; |
989 | goto out; |
990 | } |
991 | set_str = kasprintf(GFP_KERNEL, fmt: "%s,%s,%s" , setting->display_name, |
992 | new_setting, tlmi_priv.pwd_admin->signature); |
993 | if (!set_str) { |
994 | ret = -ENOMEM; |
995 | goto out; |
996 | } |
997 | |
998 | ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTING_CERT_GUID, arg: set_str); |
999 | if (ret) |
1000 | goto out; |
1001 | if (tlmi_priv.save_mode == TLMI_SAVE_BULK) |
1002 | tlmi_priv.save_required = true; |
1003 | else |
1004 | ret = tlmi_simple_call(LENOVO_SAVE_BIOS_SETTING_CERT_GUID, |
1005 | arg: tlmi_priv.pwd_admin->save_signature); |
1006 | } else if (tlmi_priv.opcode_support) { |
1007 | /* |
1008 | * If opcode support is present use that interface. |
1009 | * Note - this sets the variable and then the password as separate |
1010 | * WMI calls. Function tlmi_save_bios_settings will error if the |
1011 | * password is incorrect. |
1012 | * Workstation's require the opcode to be set before changing the |
1013 | * attribute. |
1014 | */ |
1015 | if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { |
1016 | ret = tlmi_opcode_setting(setting: "WmiOpcodePasswordAdmin" , |
1017 | value: tlmi_priv.pwd_admin->password); |
1018 | if (ret) |
1019 | goto out; |
1020 | } |
1021 | |
1022 | set_str = kasprintf(GFP_KERNEL, fmt: "%s,%s;" , setting->display_name, |
1023 | new_setting); |
1024 | if (!set_str) { |
1025 | ret = -ENOMEM; |
1026 | goto out; |
1027 | } |
1028 | |
1029 | ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTINGS_GUID, arg: set_str); |
1030 | if (ret) |
1031 | goto out; |
1032 | |
1033 | if (tlmi_priv.save_mode == TLMI_SAVE_BULK) |
1034 | tlmi_priv.save_required = true; |
1035 | else |
1036 | ret = tlmi_save_bios_settings(password: "" ); |
1037 | } else { /* old non-opcode based authentication method (deprecated) */ |
1038 | if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { |
1039 | auth_str = kasprintf(GFP_KERNEL, fmt: "%s,%s,%s;" , |
1040 | tlmi_priv.pwd_admin->password, |
1041 | encoding_options[tlmi_priv.pwd_admin->encoding], |
1042 | tlmi_priv.pwd_admin->kbdlang); |
1043 | if (!auth_str) { |
1044 | ret = -ENOMEM; |
1045 | goto out; |
1046 | } |
1047 | } |
1048 | |
1049 | if (auth_str) |
1050 | set_str = kasprintf(GFP_KERNEL, fmt: "%s,%s,%s" , setting->display_name, |
1051 | new_setting, auth_str); |
1052 | else |
1053 | set_str = kasprintf(GFP_KERNEL, fmt: "%s,%s;" , setting->display_name, |
1054 | new_setting); |
1055 | if (!set_str) { |
1056 | ret = -ENOMEM; |
1057 | goto out; |
1058 | } |
1059 | |
1060 | ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTINGS_GUID, arg: set_str); |
1061 | if (ret) |
1062 | goto out; |
1063 | |
1064 | if (tlmi_priv.save_mode == TLMI_SAVE_BULK) { |
1065 | tlmi_priv.save_required = true; |
1066 | } else { |
1067 | if (auth_str) |
1068 | ret = tlmi_save_bios_settings(password: auth_str); |
1069 | else |
1070 | ret = tlmi_save_bios_settings(password: "" ); |
1071 | } |
1072 | } |
1073 | if (!ret && !tlmi_priv.pending_changes) { |
1074 | tlmi_priv.pending_changes = true; |
1075 | /* let userland know it may need to check reboot pending again */ |
1076 | kobject_uevent(kobj: &tlmi_priv.class_dev->kobj, action: KOBJ_CHANGE); |
1077 | } |
1078 | out: |
1079 | mutex_unlock(lock: &tlmi_mutex); |
1080 | kfree(objp: auth_str); |
1081 | kfree(objp: set_str); |
1082 | kfree(objp: new_setting); |
1083 | return ret ?: count; |
1084 | } |
1085 | |
1086 | static struct kobj_attribute attr_displ_name = __ATTR_RO(display_name); |
1087 | |
1088 | static struct kobj_attribute attr_possible_values = __ATTR_RO(possible_values); |
1089 | |
1090 | static struct kobj_attribute attr_current_val = __ATTR_RW_MODE(current_value, 0600); |
1091 | |
1092 | static struct kobj_attribute attr_type = __ATTR_RO(type); |
1093 | |
1094 | static umode_t attr_is_visible(struct kobject *kobj, |
1095 | struct attribute *attr, int n) |
1096 | { |
1097 | struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); |
1098 | |
1099 | /* We don't want to display possible_values attributes if not available */ |
1100 | if ((attr == &attr_possible_values.attr) && (!setting->possible_values)) |
1101 | return 0; |
1102 | |
1103 | return attr->mode; |
1104 | } |
1105 | |
1106 | static struct attribute *tlmi_attrs[] = { |
1107 | &attr_displ_name.attr, |
1108 | &attr_current_val.attr, |
1109 | &attr_possible_values.attr, |
1110 | &attr_type.attr, |
1111 | NULL |
1112 | }; |
1113 | |
1114 | static const struct attribute_group tlmi_attr_group = { |
1115 | .is_visible = attr_is_visible, |
1116 | .attrs = tlmi_attrs, |
1117 | }; |
1118 | |
1119 | static void tlmi_attr_setting_release(struct kobject *kobj) |
1120 | { |
1121 | struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); |
1122 | |
1123 | kfree(objp: setting->possible_values); |
1124 | kfree(objp: setting); |
1125 | } |
1126 | |
1127 | static void tlmi_pwd_setting_release(struct kobject *kobj) |
1128 | { |
1129 | struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); |
1130 | |
1131 | kfree(objp: setting); |
1132 | } |
1133 | |
1134 | static const struct kobj_type tlmi_attr_setting_ktype = { |
1135 | .release = &tlmi_attr_setting_release, |
1136 | .sysfs_ops = &kobj_sysfs_ops, |
1137 | }; |
1138 | |
1139 | static const struct kobj_type tlmi_pwd_setting_ktype = { |
1140 | .release = &tlmi_pwd_setting_release, |
1141 | .sysfs_ops = &kobj_sysfs_ops, |
1142 | }; |
1143 | |
1144 | static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, |
1145 | char *buf) |
1146 | { |
1147 | return sprintf(buf, fmt: "%d\n" , tlmi_priv.pending_changes); |
1148 | } |
1149 | |
1150 | static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot); |
1151 | |
1152 | static const char * const save_mode_strings[] = { |
1153 | [TLMI_SAVE_SINGLE] = "single" , |
1154 | [TLMI_SAVE_BULK] = "bulk" , |
1155 | [TLMI_SAVE_SAVE] = "save" |
1156 | }; |
1157 | |
1158 | static ssize_t save_settings_show(struct kobject *kobj, struct kobj_attribute *attr, |
1159 | char *buf) |
1160 | { |
1161 | /* Check that setting is valid */ |
1162 | if (WARN_ON(tlmi_priv.save_mode < TLMI_SAVE_SINGLE || |
1163 | tlmi_priv.save_mode > TLMI_SAVE_BULK)) |
1164 | return -EIO; |
1165 | return sysfs_emit(buf, fmt: "%s\n" , save_mode_strings[tlmi_priv.save_mode]); |
1166 | } |
1167 | |
1168 | static ssize_t save_settings_store(struct kobject *kobj, struct kobj_attribute *attr, |
1169 | const char *buf, size_t count) |
1170 | { |
1171 | char *auth_str = NULL; |
1172 | int ret = 0; |
1173 | int cmd; |
1174 | |
1175 | cmd = sysfs_match_string(save_mode_strings, buf); |
1176 | if (cmd < 0) |
1177 | return cmd; |
1178 | |
1179 | /* Use lock in case multiple WMI operations needed */ |
1180 | mutex_lock(&tlmi_mutex); |
1181 | |
1182 | switch (cmd) { |
1183 | case TLMI_SAVE_SINGLE: |
1184 | case TLMI_SAVE_BULK: |
1185 | tlmi_priv.save_mode = cmd; |
1186 | goto out; |
1187 | case TLMI_SAVE_SAVE: |
1188 | /* Check if supported*/ |
1189 | if (!tlmi_priv.can_set_bios_settings || |
1190 | tlmi_priv.save_mode == TLMI_SAVE_SINGLE) { |
1191 | ret = -EOPNOTSUPP; |
1192 | goto out; |
1193 | } |
1194 | /* Check there is actually something to save */ |
1195 | if (!tlmi_priv.save_required) { |
1196 | ret = -ENOENT; |
1197 | goto out; |
1198 | } |
1199 | /* Check if certificate authentication is enabled and active */ |
1200 | if (tlmi_priv.certificate_support && tlmi_priv.pwd_admin->cert_installed) { |
1201 | if (!tlmi_priv.pwd_admin->signature || |
1202 | !tlmi_priv.pwd_admin->save_signature) { |
1203 | ret = -EINVAL; |
1204 | goto out; |
1205 | } |
1206 | ret = tlmi_simple_call(LENOVO_SAVE_BIOS_SETTING_CERT_GUID, |
1207 | arg: tlmi_priv.pwd_admin->save_signature); |
1208 | if (ret) |
1209 | goto out; |
1210 | } else if (tlmi_priv.opcode_support) { |
1211 | if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { |
1212 | ret = tlmi_opcode_setting(setting: "WmiOpcodePasswordAdmin" , |
1213 | value: tlmi_priv.pwd_admin->password); |
1214 | if (ret) |
1215 | goto out; |
1216 | } |
1217 | ret = tlmi_save_bios_settings(password: "" ); |
1218 | } else { /* old non-opcode based authentication method (deprecated) */ |
1219 | if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { |
1220 | auth_str = kasprintf(GFP_KERNEL, fmt: "%s,%s,%s;" , |
1221 | tlmi_priv.pwd_admin->password, |
1222 | encoding_options[tlmi_priv.pwd_admin->encoding], |
1223 | tlmi_priv.pwd_admin->kbdlang); |
1224 | if (!auth_str) { |
1225 | ret = -ENOMEM; |
1226 | goto out; |
1227 | } |
1228 | } |
1229 | |
1230 | if (auth_str) |
1231 | ret = tlmi_save_bios_settings(password: auth_str); |
1232 | else |
1233 | ret = tlmi_save_bios_settings(password: "" ); |
1234 | } |
1235 | tlmi_priv.save_required = false; |
1236 | tlmi_priv.reboot_required = true; |
1237 | |
1238 | if (!ret && !tlmi_priv.pending_changes) { |
1239 | tlmi_priv.pending_changes = true; |
1240 | /* let userland know it may need to check reboot pending again */ |
1241 | kobject_uevent(kobj: &tlmi_priv.class_dev->kobj, action: KOBJ_CHANGE); |
1242 | } |
1243 | break; |
1244 | } |
1245 | out: |
1246 | mutex_unlock(lock: &tlmi_mutex); |
1247 | kfree(objp: auth_str); |
1248 | return ret ?: count; |
1249 | } |
1250 | |
1251 | static struct kobj_attribute save_settings = __ATTR_RW(save_settings); |
1252 | |
1253 | /* ---- Debug interface--------------------------------------------------------- */ |
1254 | static ssize_t debug_cmd_store(struct kobject *kobj, struct kobj_attribute *attr, |
1255 | const char *buf, size_t count) |
1256 | { |
1257 | char *set_str = NULL, *new_setting = NULL; |
1258 | char *auth_str = NULL; |
1259 | int ret; |
1260 | |
1261 | if (!tlmi_priv.can_debug_cmd) |
1262 | return -EOPNOTSUPP; |
1263 | |
1264 | /* Strip out CR if one is present */ |
1265 | new_setting = kstrdup_and_replace(src: buf, old: '\n', new: '\0', GFP_KERNEL); |
1266 | if (!new_setting) |
1267 | return -ENOMEM; |
1268 | |
1269 | if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { |
1270 | auth_str = kasprintf(GFP_KERNEL, fmt: "%s,%s,%s;" , |
1271 | tlmi_priv.pwd_admin->password, |
1272 | encoding_options[tlmi_priv.pwd_admin->encoding], |
1273 | tlmi_priv.pwd_admin->kbdlang); |
1274 | if (!auth_str) { |
1275 | ret = -ENOMEM; |
1276 | goto out; |
1277 | } |
1278 | } |
1279 | |
1280 | if (auth_str) |
1281 | set_str = kasprintf(GFP_KERNEL, fmt: "%s,%s" , new_setting, auth_str); |
1282 | else |
1283 | set_str = kasprintf(GFP_KERNEL, fmt: "%s;" , new_setting); |
1284 | if (!set_str) { |
1285 | ret = -ENOMEM; |
1286 | goto out; |
1287 | } |
1288 | |
1289 | ret = tlmi_simple_call(LENOVO_DEBUG_CMD_GUID, arg: set_str); |
1290 | if (ret) |
1291 | goto out; |
1292 | |
1293 | if (!ret && !tlmi_priv.pending_changes) { |
1294 | tlmi_priv.pending_changes = true; |
1295 | /* let userland know it may need to check reboot pending again */ |
1296 | kobject_uevent(kobj: &tlmi_priv.class_dev->kobj, action: KOBJ_CHANGE); |
1297 | } |
1298 | out: |
1299 | kfree(objp: auth_str); |
1300 | kfree(objp: set_str); |
1301 | kfree(objp: new_setting); |
1302 | return ret ?: count; |
1303 | } |
1304 | |
1305 | static struct kobj_attribute debug_cmd = __ATTR_WO(debug_cmd); |
1306 | |
1307 | /* ---- Initialisation --------------------------------------------------------- */ |
1308 | static void tlmi_release_attr(void) |
1309 | { |
1310 | int i; |
1311 | |
1312 | /* Attribute structures */ |
1313 | for (i = 0; i < TLMI_SETTINGS_COUNT; i++) { |
1314 | if (tlmi_priv.setting[i]) { |
1315 | sysfs_remove_group(kobj: &tlmi_priv.setting[i]->kobj, grp: &tlmi_attr_group); |
1316 | kobject_put(kobj: &tlmi_priv.setting[i]->kobj); |
1317 | } |
1318 | } |
1319 | sysfs_remove_file(kobj: &tlmi_priv.attribute_kset->kobj, attr: &pending_reboot.attr); |
1320 | sysfs_remove_file(kobj: &tlmi_priv.attribute_kset->kobj, attr: &save_settings.attr); |
1321 | |
1322 | if (tlmi_priv.can_debug_cmd && debug_support) |
1323 | sysfs_remove_file(kobj: &tlmi_priv.attribute_kset->kobj, attr: &debug_cmd.attr); |
1324 | |
1325 | kset_unregister(kset: tlmi_priv.attribute_kset); |
1326 | |
1327 | /* Free up any saved signatures */ |
1328 | kfree(objp: tlmi_priv.pwd_admin->signature); |
1329 | kfree(objp: tlmi_priv.pwd_admin->save_signature); |
1330 | |
1331 | /* Authentication structures */ |
1332 | sysfs_remove_group(kobj: &tlmi_priv.pwd_admin->kobj, grp: &auth_attr_group); |
1333 | kobject_put(kobj: &tlmi_priv.pwd_admin->kobj); |
1334 | sysfs_remove_group(kobj: &tlmi_priv.pwd_power->kobj, grp: &auth_attr_group); |
1335 | kobject_put(kobj: &tlmi_priv.pwd_power->kobj); |
1336 | |
1337 | if (tlmi_priv.opcode_support) { |
1338 | sysfs_remove_group(kobj: &tlmi_priv.pwd_system->kobj, grp: &auth_attr_group); |
1339 | kobject_put(kobj: &tlmi_priv.pwd_system->kobj); |
1340 | sysfs_remove_group(kobj: &tlmi_priv.pwd_hdd->kobj, grp: &auth_attr_group); |
1341 | kobject_put(kobj: &tlmi_priv.pwd_hdd->kobj); |
1342 | sysfs_remove_group(kobj: &tlmi_priv.pwd_nvme->kobj, grp: &auth_attr_group); |
1343 | kobject_put(kobj: &tlmi_priv.pwd_nvme->kobj); |
1344 | } |
1345 | |
1346 | kset_unregister(kset: tlmi_priv.authentication_kset); |
1347 | } |
1348 | |
1349 | static int tlmi_validate_setting_name(struct kset *attribute_kset, char *name) |
1350 | { |
1351 | struct kobject *duplicate; |
1352 | |
1353 | if (!strcmp(name, "Reserved" )) |
1354 | return -EINVAL; |
1355 | |
1356 | duplicate = kset_find_obj(attribute_kset, name); |
1357 | if (duplicate) { |
1358 | pr_debug("Duplicate attribute name found - %s\n" , name); |
1359 | /* kset_find_obj() returns a reference */ |
1360 | kobject_put(kobj: duplicate); |
1361 | return -EBUSY; |
1362 | } |
1363 | |
1364 | return 0; |
1365 | } |
1366 | |
1367 | static int tlmi_sysfs_init(void) |
1368 | { |
1369 | int i, ret; |
1370 | |
1371 | ret = fw_attributes_class_get(fw_attr_class: &fw_attr_class); |
1372 | if (ret) |
1373 | return ret; |
1374 | |
1375 | tlmi_priv.class_dev = device_create(cls: fw_attr_class, NULL, MKDEV(0, 0), |
1376 | NULL, fmt: "%s" , "thinklmi" ); |
1377 | if (IS_ERR(ptr: tlmi_priv.class_dev)) { |
1378 | ret = PTR_ERR(ptr: tlmi_priv.class_dev); |
1379 | goto fail_class_created; |
1380 | } |
1381 | |
1382 | tlmi_priv.attribute_kset = kset_create_and_add(name: "attributes" , NULL, |
1383 | parent_kobj: &tlmi_priv.class_dev->kobj); |
1384 | if (!tlmi_priv.attribute_kset) { |
1385 | ret = -ENOMEM; |
1386 | goto fail_device_created; |
1387 | } |
1388 | |
1389 | for (i = 0; i < TLMI_SETTINGS_COUNT; i++) { |
1390 | /* Check if index is a valid setting - skip if it isn't */ |
1391 | if (!tlmi_priv.setting[i]) |
1392 | continue; |
1393 | |
1394 | /* check for duplicate or reserved values */ |
1395 | if (tlmi_validate_setting_name(attribute_kset: tlmi_priv.attribute_kset, |
1396 | name: tlmi_priv.setting[i]->display_name) < 0) { |
1397 | kfree(objp: tlmi_priv.setting[i]->possible_values); |
1398 | kfree(objp: tlmi_priv.setting[i]); |
1399 | tlmi_priv.setting[i] = NULL; |
1400 | continue; |
1401 | } |
1402 | |
1403 | /* Build attribute */ |
1404 | tlmi_priv.setting[i]->kobj.kset = tlmi_priv.attribute_kset; |
1405 | ret = kobject_add(kobj: &tlmi_priv.setting[i]->kobj, NULL, |
1406 | fmt: "%s" , tlmi_priv.setting[i]->display_name); |
1407 | if (ret) |
1408 | goto fail_create_attr; |
1409 | |
1410 | ret = sysfs_create_group(kobj: &tlmi_priv.setting[i]->kobj, grp: &tlmi_attr_group); |
1411 | if (ret) |
1412 | goto fail_create_attr; |
1413 | } |
1414 | |
1415 | ret = sysfs_create_file(kobj: &tlmi_priv.attribute_kset->kobj, attr: &pending_reboot.attr); |
1416 | if (ret) |
1417 | goto fail_create_attr; |
1418 | |
1419 | ret = sysfs_create_file(kobj: &tlmi_priv.attribute_kset->kobj, attr: &save_settings.attr); |
1420 | if (ret) |
1421 | goto fail_create_attr; |
1422 | |
1423 | if (tlmi_priv.can_debug_cmd && debug_support) { |
1424 | ret = sysfs_create_file(kobj: &tlmi_priv.attribute_kset->kobj, attr: &debug_cmd.attr); |
1425 | if (ret) |
1426 | goto fail_create_attr; |
1427 | } |
1428 | |
1429 | /* Create authentication entries */ |
1430 | tlmi_priv.authentication_kset = kset_create_and_add(name: "authentication" , NULL, |
1431 | parent_kobj: &tlmi_priv.class_dev->kobj); |
1432 | if (!tlmi_priv.authentication_kset) { |
1433 | ret = -ENOMEM; |
1434 | goto fail_create_attr; |
1435 | } |
1436 | tlmi_priv.pwd_admin->kobj.kset = tlmi_priv.authentication_kset; |
1437 | ret = kobject_add(kobj: &tlmi_priv.pwd_admin->kobj, NULL, fmt: "%s" , "Admin" ); |
1438 | if (ret) |
1439 | goto fail_create_attr; |
1440 | |
1441 | ret = sysfs_create_group(kobj: &tlmi_priv.pwd_admin->kobj, grp: &auth_attr_group); |
1442 | if (ret) |
1443 | goto fail_create_attr; |
1444 | |
1445 | tlmi_priv.pwd_power->kobj.kset = tlmi_priv.authentication_kset; |
1446 | ret = kobject_add(kobj: &tlmi_priv.pwd_power->kobj, NULL, fmt: "%s" , "Power-on" ); |
1447 | if (ret) |
1448 | goto fail_create_attr; |
1449 | |
1450 | ret = sysfs_create_group(kobj: &tlmi_priv.pwd_power->kobj, grp: &auth_attr_group); |
1451 | if (ret) |
1452 | goto fail_create_attr; |
1453 | |
1454 | if (tlmi_priv.opcode_support) { |
1455 | tlmi_priv.pwd_system->kobj.kset = tlmi_priv.authentication_kset; |
1456 | ret = kobject_add(kobj: &tlmi_priv.pwd_system->kobj, NULL, fmt: "%s" , "System" ); |
1457 | if (ret) |
1458 | goto fail_create_attr; |
1459 | |
1460 | ret = sysfs_create_group(kobj: &tlmi_priv.pwd_system->kobj, grp: &auth_attr_group); |
1461 | if (ret) |
1462 | goto fail_create_attr; |
1463 | |
1464 | tlmi_priv.pwd_hdd->kobj.kset = tlmi_priv.authentication_kset; |
1465 | ret = kobject_add(kobj: &tlmi_priv.pwd_hdd->kobj, NULL, fmt: "%s" , "HDD" ); |
1466 | if (ret) |
1467 | goto fail_create_attr; |
1468 | |
1469 | ret = sysfs_create_group(kobj: &tlmi_priv.pwd_hdd->kobj, grp: &auth_attr_group); |
1470 | if (ret) |
1471 | goto fail_create_attr; |
1472 | |
1473 | tlmi_priv.pwd_nvme->kobj.kset = tlmi_priv.authentication_kset; |
1474 | ret = kobject_add(kobj: &tlmi_priv.pwd_nvme->kobj, NULL, fmt: "%s" , "NVMe" ); |
1475 | if (ret) |
1476 | goto fail_create_attr; |
1477 | |
1478 | ret = sysfs_create_group(kobj: &tlmi_priv.pwd_nvme->kobj, grp: &auth_attr_group); |
1479 | if (ret) |
1480 | goto fail_create_attr; |
1481 | } |
1482 | |
1483 | return ret; |
1484 | |
1485 | fail_create_attr: |
1486 | tlmi_release_attr(); |
1487 | fail_device_created: |
1488 | device_destroy(cls: fw_attr_class, MKDEV(0, 0)); |
1489 | fail_class_created: |
1490 | fw_attributes_class_put(); |
1491 | return ret; |
1492 | } |
1493 | |
1494 | /* ---- Base Driver -------------------------------------------------------- */ |
1495 | static struct tlmi_pwd_setting *tlmi_create_auth(const char *pwd_type, |
1496 | const char *pwd_role) |
1497 | { |
1498 | struct tlmi_pwd_setting *new_pwd; |
1499 | |
1500 | new_pwd = kzalloc(size: sizeof(struct tlmi_pwd_setting), GFP_KERNEL); |
1501 | if (!new_pwd) |
1502 | return NULL; |
1503 | |
1504 | strscpy(new_pwd->kbdlang, "us" , TLMI_LANG_MAXLEN); |
1505 | new_pwd->encoding = TLMI_ENCODING_ASCII; |
1506 | new_pwd->pwd_type = pwd_type; |
1507 | new_pwd->role = pwd_role; |
1508 | new_pwd->minlen = tlmi_priv.pwdcfg.core.min_length; |
1509 | new_pwd->maxlen = tlmi_priv.pwdcfg.core.max_length; |
1510 | new_pwd->index = 0; |
1511 | |
1512 | kobject_init(kobj: &new_pwd->kobj, ktype: &tlmi_pwd_setting_ktype); |
1513 | |
1514 | return new_pwd; |
1515 | } |
1516 | |
1517 | static int tlmi_analyze(void) |
1518 | { |
1519 | int i, ret; |
1520 | |
1521 | if (wmi_has_guid(LENOVO_SET_BIOS_SETTINGS_GUID) && |
1522 | wmi_has_guid(LENOVO_SAVE_BIOS_SETTINGS_GUID)) |
1523 | tlmi_priv.can_set_bios_settings = true; |
1524 | |
1525 | if (wmi_has_guid(LENOVO_GET_BIOS_SELECTIONS_GUID)) |
1526 | tlmi_priv.can_get_bios_selections = true; |
1527 | |
1528 | if (wmi_has_guid(LENOVO_SET_BIOS_PASSWORD_GUID)) |
1529 | tlmi_priv.can_set_bios_password = true; |
1530 | |
1531 | if (wmi_has_guid(LENOVO_BIOS_PASSWORD_SETTINGS_GUID)) |
1532 | tlmi_priv.can_get_password_settings = true; |
1533 | |
1534 | if (wmi_has_guid(LENOVO_DEBUG_CMD_GUID)) |
1535 | tlmi_priv.can_debug_cmd = true; |
1536 | |
1537 | if (wmi_has_guid(LENOVO_OPCODE_IF_GUID)) |
1538 | tlmi_priv.opcode_support = true; |
1539 | |
1540 | if (wmi_has_guid(LENOVO_SET_BIOS_CERT_GUID) && |
1541 | wmi_has_guid(LENOVO_SET_BIOS_SETTING_CERT_GUID) && |
1542 | wmi_has_guid(LENOVO_SAVE_BIOS_SETTING_CERT_GUID)) |
1543 | tlmi_priv.certificate_support = true; |
1544 | |
1545 | /* |
1546 | * Try to find the number of valid settings of this machine |
1547 | * and use it to create sysfs attributes. |
1548 | */ |
1549 | for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) { |
1550 | struct tlmi_attr_setting *setting; |
1551 | char *item = NULL; |
1552 | |
1553 | tlmi_priv.setting[i] = NULL; |
1554 | ret = tlmi_setting(item: i, value: &item, LENOVO_BIOS_SETTING_GUID); |
1555 | if (ret) |
1556 | break; |
1557 | if (!item) |
1558 | break; |
1559 | if (!*item) { |
1560 | kfree(objp: item); |
1561 | continue; |
1562 | } |
1563 | |
1564 | /* It is not allowed to have '/' for file name. Convert it into '\'. */ |
1565 | strreplace(str: item, old: '/', new: '\\'); |
1566 | |
1567 | /* Remove the value part */ |
1568 | strreplace(str: item, old: ',', new: '\0'); |
1569 | |
1570 | /* Create a setting entry */ |
1571 | setting = kzalloc(size: sizeof(*setting), GFP_KERNEL); |
1572 | if (!setting) { |
1573 | ret = -ENOMEM; |
1574 | kfree(objp: item); |
1575 | goto fail_clear_attr; |
1576 | } |
1577 | setting->index = i; |
1578 | strscpy(setting->display_name, item, TLMI_SETTINGS_MAXLEN); |
1579 | /* If BIOS selections supported, load those */ |
1580 | if (tlmi_priv.can_get_bios_selections) { |
1581 | ret = tlmi_get_bios_selections(item: setting->display_name, |
1582 | value: &setting->possible_values); |
1583 | if (ret || !setting->possible_values) |
1584 | pr_info("Error retrieving possible values for %d : %s\n" , |
1585 | i, setting->display_name); |
1586 | } else { |
1587 | /* |
1588 | * Older Thinkstations don't support the bios_selections API. |
1589 | * Instead they store this as a [Optional:Option1,Option2] section of the |
1590 | * name string. |
1591 | * Try and pull that out if it's available. |
1592 | */ |
1593 | char *optitem, *optstart, *optend; |
1594 | |
1595 | if (!tlmi_setting(item: setting->index, value: &optitem, LENOVO_BIOS_SETTING_GUID)) { |
1596 | optstart = strstr(optitem, "[Optional:" ); |
1597 | if (optstart) { |
1598 | optstart += strlen("[Optional:" ); |
1599 | optend = strstr(optstart, "]" ); |
1600 | if (optend) |
1601 | setting->possible_values = |
1602 | kstrndup(s: optstart, len: optend - optstart, |
1603 | GFP_KERNEL); |
1604 | } |
1605 | kfree(objp: optitem); |
1606 | } |
1607 | } |
1608 | /* |
1609 | * firmware-attributes requires that possible_values are separated by ';' but |
1610 | * Lenovo FW uses ','. Replace appropriately. |
1611 | */ |
1612 | if (setting->possible_values) |
1613 | strreplace(str: setting->possible_values, old: ',', new: ';'); |
1614 | |
1615 | kobject_init(kobj: &setting->kobj, ktype: &tlmi_attr_setting_ktype); |
1616 | tlmi_priv.setting[i] = setting; |
1617 | kfree(objp: item); |
1618 | } |
1619 | |
1620 | /* Create password setting structure */ |
1621 | ret = tlmi_get_pwd_settings(pwdcfg: &tlmi_priv.pwdcfg); |
1622 | if (ret) |
1623 | goto fail_clear_attr; |
1624 | |
1625 | /* All failures below boil down to kmalloc failures */ |
1626 | ret = -ENOMEM; |
1627 | |
1628 | tlmi_priv.pwd_admin = tlmi_create_auth(pwd_type: "pap" , pwd_role: "bios-admin" ); |
1629 | if (!tlmi_priv.pwd_admin) |
1630 | goto fail_clear_attr; |
1631 | |
1632 | if (tlmi_priv.pwdcfg.core.password_state & TLMI_PAP_PWD) |
1633 | tlmi_priv.pwd_admin->valid = true; |
1634 | |
1635 | tlmi_priv.pwd_power = tlmi_create_auth(pwd_type: "pop" , pwd_role: "power-on" ); |
1636 | if (!tlmi_priv.pwd_power) |
1637 | goto fail_clear_attr; |
1638 | |
1639 | if (tlmi_priv.pwdcfg.core.password_state & TLMI_POP_PWD) |
1640 | tlmi_priv.pwd_power->valid = true; |
1641 | |
1642 | if (tlmi_priv.opcode_support) { |
1643 | tlmi_priv.pwd_system = tlmi_create_auth(pwd_type: "smp" , pwd_role: "system" ); |
1644 | if (!tlmi_priv.pwd_system) |
1645 | goto fail_clear_attr; |
1646 | |
1647 | if (tlmi_priv.pwdcfg.core.password_state & TLMI_SMP_PWD) |
1648 | tlmi_priv.pwd_system->valid = true; |
1649 | |
1650 | tlmi_priv.pwd_hdd = tlmi_create_auth(pwd_type: "hdd" , pwd_role: "hdd" ); |
1651 | if (!tlmi_priv.pwd_hdd) |
1652 | goto fail_clear_attr; |
1653 | |
1654 | tlmi_priv.pwd_nvme = tlmi_create_auth(pwd_type: "nvm" , pwd_role: "nvme" ); |
1655 | if (!tlmi_priv.pwd_nvme) |
1656 | goto fail_clear_attr; |
1657 | |
1658 | /* Set default hdd/nvme index to 1 as there is no device 0 */ |
1659 | tlmi_priv.pwd_hdd->index = 1; |
1660 | tlmi_priv.pwd_nvme->index = 1; |
1661 | |
1662 | if (tlmi_priv.pwdcfg.core.password_state & TLMI_HDD_PWD) { |
1663 | /* Check if PWD is configured and set index to first drive found */ |
1664 | if (tlmi_priv.pwdcfg.ext.hdd_user_password || |
1665 | tlmi_priv.pwdcfg.ext.hdd_master_password) { |
1666 | tlmi_priv.pwd_hdd->valid = true; |
1667 | if (tlmi_priv.pwdcfg.ext.hdd_master_password) |
1668 | tlmi_priv.pwd_hdd->index = |
1669 | ffs(tlmi_priv.pwdcfg.ext.hdd_master_password) - 1; |
1670 | else |
1671 | tlmi_priv.pwd_hdd->index = |
1672 | ffs(tlmi_priv.pwdcfg.ext.hdd_user_password) - 1; |
1673 | } |
1674 | if (tlmi_priv.pwdcfg.ext.nvme_user_password || |
1675 | tlmi_priv.pwdcfg.ext.nvme_master_password) { |
1676 | tlmi_priv.pwd_nvme->valid = true; |
1677 | if (tlmi_priv.pwdcfg.ext.nvme_master_password) |
1678 | tlmi_priv.pwd_nvme->index = |
1679 | ffs(tlmi_priv.pwdcfg.ext.nvme_master_password) - 1; |
1680 | else |
1681 | tlmi_priv.pwd_nvme->index = |
1682 | ffs(tlmi_priv.pwdcfg.ext.nvme_user_password) - 1; |
1683 | } |
1684 | } |
1685 | } |
1686 | |
1687 | if (tlmi_priv.certificate_support && |
1688 | (tlmi_priv.pwdcfg.core.password_state & TLMI_CERT)) |
1689 | tlmi_priv.pwd_admin->cert_installed = true; |
1690 | |
1691 | return 0; |
1692 | |
1693 | fail_clear_attr: |
1694 | for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) { |
1695 | if (tlmi_priv.setting[i]) { |
1696 | kfree(objp: tlmi_priv.setting[i]->possible_values); |
1697 | kfree(objp: tlmi_priv.setting[i]); |
1698 | } |
1699 | } |
1700 | kfree(objp: tlmi_priv.pwd_admin); |
1701 | kfree(objp: tlmi_priv.pwd_power); |
1702 | kfree(objp: tlmi_priv.pwd_system); |
1703 | kfree(objp: tlmi_priv.pwd_hdd); |
1704 | kfree(objp: tlmi_priv.pwd_nvme); |
1705 | return ret; |
1706 | } |
1707 | |
1708 | static void tlmi_remove(struct wmi_device *wdev) |
1709 | { |
1710 | tlmi_release_attr(); |
1711 | device_destroy(cls: fw_attr_class, MKDEV(0, 0)); |
1712 | fw_attributes_class_put(); |
1713 | } |
1714 | |
1715 | static int tlmi_probe(struct wmi_device *wdev, const void *context) |
1716 | { |
1717 | int ret; |
1718 | |
1719 | ret = tlmi_analyze(); |
1720 | if (ret) |
1721 | return ret; |
1722 | |
1723 | return tlmi_sysfs_init(); |
1724 | } |
1725 | |
1726 | static const struct wmi_device_id tlmi_id_table[] = { |
1727 | { .guid_string = LENOVO_BIOS_SETTING_GUID }, |
1728 | { } |
1729 | }; |
1730 | MODULE_DEVICE_TABLE(wmi, tlmi_id_table); |
1731 | |
1732 | static struct wmi_driver tlmi_driver = { |
1733 | .driver = { |
1734 | .name = "think-lmi" , |
1735 | }, |
1736 | .id_table = tlmi_id_table, |
1737 | .probe = tlmi_probe, |
1738 | .remove = tlmi_remove, |
1739 | }; |
1740 | |
1741 | MODULE_AUTHOR("Sugumaran L <slacshiminar@lenovo.com>" ); |
1742 | MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>" ); |
1743 | MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>" ); |
1744 | MODULE_DESCRIPTION("ThinkLMI Driver" ); |
1745 | MODULE_LICENSE("GPL" ); |
1746 | |
1747 | module_wmi_driver(tlmi_driver); |
1748 | |