1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Common methods for use with dell-wmi-sysman 4 * 5 * Copyright (c) 2020 Dell Inc. 6 */ 7 8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 10 #include <linux/align.h> 11 #include <linux/fs.h> 12 #include <linux/dmi.h> 13 #include <linux/module.h> 14 #include <linux/kernel.h> 15 #include <linux/string.h> 16 #include <linux/sysfs.h> 17 #include <linux/wmi.h> 18 #include "dell-wmi-sysman.h" 19 #include "../../firmware_attributes_class.h" 20 21 #define MAX_TYPES 4 22 #include <linux/nls.h> 23 24 struct wmi_sysman_priv wmi_priv = { 25 .mutex = __MUTEX_INITIALIZER(wmi_priv.mutex), 26 }; 27 28 /* reset bios to defaults */ 29 static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"}; 30 static int reset_option = -1; 31 32 33 /** 34 * populate_string_buffer() - populates a string buffer 35 * @buffer: the start of the destination buffer 36 * @buffer_len: length of the destination buffer 37 * @str: the string to insert into buffer 38 */ 39 ssize_t populate_string_buffer(char *buffer, size_t buffer_len, const char *str) 40 { 41 u16 *length = (u16 *)buffer; 42 u16 *target = length + 1; 43 int ret; 44 45 ret = utf8s_to_utf16s(str, strlen(str), UTF16_HOST_ENDIAN, 46 target, buffer_len - sizeof(u16)); 47 if (ret < 0) { 48 dev_err(wmi_priv.class_dev, "UTF16 conversion failed\n"); 49 return ret; 50 } 51 52 if ((ret * sizeof(u16)) > U16_MAX) { 53 dev_err(wmi_priv.class_dev, "Error string too long\n"); 54 return -ERANGE; 55 } 56 57 *length = ret * sizeof(u16); 58 return sizeof(u16) + *length; 59 } 60 61 /** 62 * calculate_string_buffer() - determines size of string buffer for use with BIOS communication 63 * @str: the string to calculate based upon 64 * 65 */ 66 size_t calculate_string_buffer(const char *str) 67 { 68 /* u16 length field + one UTF16 char for each input char */ 69 return sizeof(u16) + strlen(str) * sizeof(u16); 70 } 71 72 /** 73 * calculate_security_buffer() - determines size of security buffer for authentication scheme 74 * @authentication: the authentication content 75 * 76 * Currently only supported type is Admin password 77 */ 78 size_t calculate_security_buffer(const char *authentication) 79 { 80 return sizeof(u32) * 2 + ALIGN(strlen(authentication), 2); 81 } 82 83 /** 84 * populate_security_buffer() - builds a security buffer for authentication scheme 85 * @buffer: the buffer to populate 86 * @authentication: the authentication content 87 * 88 * Currently only supported type is PLAIN TEXT 89 */ 90 void populate_security_buffer(char *buffer, const char *authentication) 91 { 92 size_t seclen = strlen(authentication); 93 char *auth = buffer + sizeof(u32) * 2; 94 u32 *sectype = (u32 *) buffer; 95 u32 *seclenp = sectype + 1; 96 97 *sectype = !!seclen; 98 *seclenp = seclen; 99 100 /* plain text */ 101 memcpy(auth, authentication, seclen); 102 } 103 104 /** 105 * map_wmi_error() - map errors from WMI methods to kernel error codes 106 * @error_code: integer error code returned from Dell's firmware 107 */ 108 int map_wmi_error(int error_code) 109 { 110 switch (error_code) { 111 case 0: 112 /* success */ 113 return 0; 114 case 1: 115 /* failed */ 116 return -EIO; 117 case 2: 118 /* invalid parameter */ 119 return -EINVAL; 120 case 3: 121 /* access denied */ 122 return -EACCES; 123 case 4: 124 /* not supported */ 125 return -EOPNOTSUPP; 126 case 5: 127 /* memory error */ 128 return -ENOMEM; 129 case 6: 130 /* protocol error */ 131 return -EPROTO; 132 } 133 /* unspecified error */ 134 return -EIO; 135 } 136 137 /** 138 * reset_bios_show() - sysfs implementaton for read reset_bios 139 * @kobj: Kernel object for this attribute 140 * @attr: Kernel object attribute 141 * @buf: The buffer to display to userspace 142 */ 143 static ssize_t reset_bios_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 144 { 145 ssize_t len = 0; 146 int i; 147 148 for (i = 0; i < MAX_TYPES; i++) { 149 if (i == reset_option) 150 len += sysfs_emit_at(buf, len, "[%s] ", reset_types[i]); 151 else 152 len += sysfs_emit_at(buf, len, "%s ", reset_types[i]); 153 } 154 len += sysfs_emit_at(buf, len, "\n"); 155 return len; 156 } 157 158 /** 159 * reset_bios_store() - sysfs implementaton for write reset_bios 160 * @kobj: Kernel object for this attribute 161 * @attr: Kernel object attribute 162 * @buf: The buffer from userspace 163 * @count: the size of the buffer from userspace 164 */ 165 static ssize_t reset_bios_store(struct kobject *kobj, 166 struct kobj_attribute *attr, const char *buf, size_t count) 167 { 168 int type = sysfs_match_string(reset_types, buf); 169 int ret; 170 171 if (type < 0) 172 return type; 173 174 ret = set_bios_defaults(type); 175 pr_debug("reset all attributes request type %d: %d\n", type, ret); 176 if (!ret) { 177 reset_option = type; 178 ret = count; 179 } 180 181 return ret; 182 } 183 184 /** 185 * pending_reboot_show() - sysfs implementaton for read pending_reboot 186 * @kobj: Kernel object for this attribute 187 * @attr: Kernel object attribute 188 * @buf: The buffer to display to userspace 189 * 190 * Stores default value as 0 191 * When current_value is changed this attribute is set to 1 to notify reboot may be required 192 */ 193 static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, 194 char *buf) 195 { 196 return sysfs_emit(buf, "%d\n", wmi_priv.pending_changes); 197 } 198 199 static struct kobj_attribute reset_bios = __ATTR_RW(reset_bios); 200 static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot); 201 202 203 /** 204 * create_attributes_level_sysfs_files() - Creates reset_bios and 205 * pending_reboot attributes 206 */ 207 static int create_attributes_level_sysfs_files(void) 208 { 209 int ret; 210 211 ret = sysfs_create_file(&wmi_priv.main_dir_kset->kobj, &reset_bios.attr); 212 if (ret) 213 return ret; 214 215 ret = sysfs_create_file(&wmi_priv.main_dir_kset->kobj, &pending_reboot.attr); 216 if (ret) 217 return ret; 218 219 return 0; 220 } 221 222 static void attr_name_release(struct kobject *kobj) 223 { 224 kfree(kobj); 225 } 226 227 static const struct kobj_type attr_name_ktype = { 228 .release = attr_name_release, 229 .sysfs_ops = &kobj_sysfs_ops, 230 }; 231 232 /** 233 * strlcpy_attr - Copy a length-limited, NULL-terminated string with bound checks 234 * @dest: Where to copy the string to 235 * @src: Where to copy the string from 236 */ 237 void strlcpy_attr(char *dest, char *src) 238 { 239 size_t len = strlen(src) + 1; 240 241 if (len > 1 && len <= MAX_BUFF) 242 strscpy(dest, src, len); 243 244 /*len can be zero because any property not-applicable to attribute can 245 * be empty so check only for too long buffers and log error 246 */ 247 if (len > MAX_BUFF) 248 pr_err("Source string returned from BIOS is out of bound!\n"); 249 } 250 251 /** 252 * get_wmiobj_pointer() - Get Content of WMI block for particular instance 253 * @instance_id: WMI instance ID 254 * @guid_string: WMI GUID (in str form) 255 * 256 * Fetches the content for WMI block (instance_id) under GUID (guid_string) 257 * Caller must kfree the return 258 */ 259 union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string) 260 { 261 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; 262 acpi_status status; 263 264 status = wmi_query_block(guid_string, instance_id, &out); 265 266 return ACPI_SUCCESS(status) ? (union acpi_object *)out.pointer : NULL; 267 } 268 269 /** 270 * get_instance_count() - Compute total number of instances under guid_string 271 * @guid_string: WMI GUID (in string form) 272 */ 273 int get_instance_count(const char *guid_string) 274 { 275 int ret; 276 277 ret = wmi_instance_count(guid_string); 278 if (ret < 0) 279 return 0; 280 281 return ret; 282 } 283 284 /** 285 * alloc_attributes_data() - Allocate attributes data for a particular type 286 * @attr_type: Attribute type to allocate 287 */ 288 static int alloc_attributes_data(int attr_type) 289 { 290 int retval = 0; 291 292 switch (attr_type) { 293 case ENUM: 294 retval = alloc_enum_data(); 295 break; 296 case INT: 297 retval = alloc_int_data(); 298 break; 299 case STR: 300 retval = alloc_str_data(); 301 break; 302 case PO: 303 retval = alloc_po_data(); 304 break; 305 default: 306 break; 307 } 308 309 return retval; 310 } 311 312 /** 313 * destroy_attribute_objs() - Free a kset of kobjects 314 * @kset: The kset to destroy 315 * 316 * Frees kobjects created for each attribute_name under attribute type kset. 317 */ 318 static void destroy_attribute_objs(struct kset *kset) 319 { 320 struct kobject *pos, *next; 321 322 list_for_each_entry_safe(pos, next, &kset->list, entry) { 323 kobject_put(pos); 324 } 325 } 326 327 /** 328 * release_attributes_data() - Clean-up all sysfs directories and files created 329 */ 330 static void release_attributes_data(void) 331 { 332 mutex_lock(&wmi_priv.mutex); 333 exit_enum_attributes(); 334 exit_int_attributes(); 335 exit_str_attributes(); 336 exit_po_attributes(); 337 if (wmi_priv.authentication_dir_kset) { 338 destroy_attribute_objs(wmi_priv.authentication_dir_kset); 339 kset_unregister(wmi_priv.authentication_dir_kset); 340 wmi_priv.authentication_dir_kset = NULL; 341 } 342 if (wmi_priv.main_dir_kset) { 343 sysfs_remove_file(&wmi_priv.main_dir_kset->kobj, &reset_bios.attr); 344 sysfs_remove_file(&wmi_priv.main_dir_kset->kobj, &pending_reboot.attr); 345 destroy_attribute_objs(wmi_priv.main_dir_kset); 346 kset_unregister(wmi_priv.main_dir_kset); 347 wmi_priv.main_dir_kset = NULL; 348 } 349 mutex_unlock(&wmi_priv.mutex); 350 } 351 352 /** 353 * init_bios_attributes() - Initialize all attributes for a type 354 * @attr_type: The attribute type to initialize 355 * @guid: The WMI GUID associated with this type to initialize 356 * 357 * Initialiaze all 4 types of attributes enumeration, integer, string and password object. 358 * Populates each attrbute typ's respective properties under sysfs files 359 */ 360 static int init_bios_attributes(int attr_type, const char *guid) 361 { 362 struct kobject *attr_name_kobj; //individual attribute names 363 union acpi_object *obj = NULL; 364 union acpi_object *elements; 365 struct kobject *duplicate; 366 struct kset *tmp_set; 367 int min_elements; 368 369 /* instance_id needs to be reset for each type GUID 370 * also, instance IDs are unique within GUID but not across 371 */ 372 int instance_id = 0; 373 int retval = 0; 374 375 retval = alloc_attributes_data(attr_type); 376 if (retval) 377 return retval; 378 379 switch (attr_type) { 380 case ENUM: min_elements = ENUM_MIN_ELEMENTS; break; 381 case INT: min_elements = INT_MIN_ELEMENTS; break; 382 case STR: min_elements = STR_MIN_ELEMENTS; break; 383 case PO: min_elements = PO_MIN_ELEMENTS; break; 384 default: 385 pr_err("Error: Unknown attr_type: %d\n", attr_type); 386 return -EINVAL; 387 } 388 389 /* need to use specific instance_id and guid combination to get right data */ 390 obj = get_wmiobj_pointer(instance_id, guid); 391 if (!obj) 392 return -ENODEV; 393 394 mutex_lock(&wmi_priv.mutex); 395 while (obj) { 396 if (obj->type != ACPI_TYPE_PACKAGE) { 397 pr_err("Error: Expected ACPI-package type, got: %d\n", obj->type); 398 retval = -EIO; 399 goto err_attr_init; 400 } 401 402 if (obj->package.count < min_elements) { 403 pr_err("Error: ACPI-package does not have enough elements: %d < %d\n", 404 obj->package.count, min_elements); 405 goto nextobj; 406 } 407 408 elements = obj->package.elements; 409 410 /* sanity checking */ 411 if (elements[ATTR_NAME].type != ACPI_TYPE_STRING) { 412 pr_debug("incorrect element type\n"); 413 goto nextobj; 414 } 415 if (strlen(elements[ATTR_NAME].string.pointer) == 0) { 416 pr_debug("empty attribute found\n"); 417 goto nextobj; 418 } 419 if (attr_type == PO) 420 tmp_set = wmi_priv.authentication_dir_kset; 421 else 422 tmp_set = wmi_priv.main_dir_kset; 423 424 duplicate = kset_find_obj(tmp_set, elements[ATTR_NAME].string.pointer); 425 if (duplicate) { 426 pr_debug("Duplicate attribute name found - %s\n", 427 elements[ATTR_NAME].string.pointer); 428 kobject_put(duplicate); 429 goto nextobj; 430 } 431 432 /* build attribute */ 433 attr_name_kobj = kzalloc_obj(*attr_name_kobj); 434 if (!attr_name_kobj) { 435 retval = -ENOMEM; 436 goto err_attr_init; 437 } 438 439 attr_name_kobj->kset = tmp_set; 440 441 retval = kobject_init_and_add(attr_name_kobj, &attr_name_ktype, NULL, "%s", 442 elements[ATTR_NAME].string.pointer); 443 if (retval) { 444 kobject_put(attr_name_kobj); 445 goto err_attr_init; 446 } 447 448 /* enumerate all of this attribute */ 449 switch (attr_type) { 450 case ENUM: 451 retval = populate_enum_data(elements, instance_id, attr_name_kobj, 452 obj->package.count); 453 break; 454 case INT: 455 retval = populate_int_data(elements, instance_id, attr_name_kobj); 456 break; 457 case STR: 458 retval = populate_str_data(elements, instance_id, attr_name_kobj); 459 break; 460 case PO: 461 retval = populate_po_data(elements, instance_id, attr_name_kobj); 462 break; 463 default: 464 break; 465 } 466 467 if (retval) { 468 pr_debug("failed to populate %s\n", 469 elements[ATTR_NAME].string.pointer); 470 goto err_attr_init; 471 } 472 473 nextobj: 474 kfree(obj); 475 instance_id++; 476 obj = get_wmiobj_pointer(instance_id, guid); 477 } 478 479 mutex_unlock(&wmi_priv.mutex); 480 return 0; 481 482 err_attr_init: 483 mutex_unlock(&wmi_priv.mutex); 484 kfree(obj); 485 return retval; 486 } 487 488 static int __init sysman_init(void) 489 { 490 int ret = 0; 491 492 if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL) && 493 !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Alienware", NULL) && 494 !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "www.dell.com", NULL)) { 495 pr_err("Unable to run on non-Dell system\n"); 496 return -ENODEV; 497 } 498 499 ret = init_bios_attr_set_interface(); 500 if (ret) 501 return ret; 502 503 ret = init_bios_attr_pass_interface(); 504 if (ret) 505 goto err_exit_bios_attr_set_interface; 506 507 if (!wmi_priv.bios_attr_wdev || !wmi_priv.password_attr_wdev) { 508 pr_debug("failed to find set or pass interface\n"); 509 ret = -ENODEV; 510 goto err_exit_bios_attr_pass_interface; 511 } 512 513 wmi_priv.class_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0), 514 NULL, "%s", DRIVER_NAME); 515 if (IS_ERR(wmi_priv.class_dev)) { 516 ret = PTR_ERR(wmi_priv.class_dev); 517 goto err_exit_bios_attr_pass_interface; 518 } 519 520 wmi_priv.main_dir_kset = kset_create_and_add("attributes", NULL, 521 &wmi_priv.class_dev->kobj); 522 if (!wmi_priv.main_dir_kset) { 523 ret = -ENOMEM; 524 goto err_destroy_classdev; 525 } 526 527 wmi_priv.authentication_dir_kset = kset_create_and_add("authentication", NULL, 528 &wmi_priv.class_dev->kobj); 529 if (!wmi_priv.authentication_dir_kset) { 530 ret = -ENOMEM; 531 goto err_release_attributes_data; 532 } 533 534 ret = create_attributes_level_sysfs_files(); 535 if (ret) { 536 pr_debug("could not create reset BIOS attribute\n"); 537 goto err_release_attributes_data; 538 } 539 540 ret = init_bios_attributes(ENUM, DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID); 541 if (ret) { 542 pr_debug("failed to populate enumeration type attributes\n"); 543 goto err_release_attributes_data; 544 } 545 546 ret = init_bios_attributes(INT, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); 547 if (ret) { 548 pr_debug("failed to populate integer type attributes\n"); 549 goto err_release_attributes_data; 550 } 551 552 ret = init_bios_attributes(STR, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); 553 if (ret) { 554 pr_debug("failed to populate string type attributes\n"); 555 goto err_release_attributes_data; 556 } 557 558 ret = init_bios_attributes(PO, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID); 559 if (ret) { 560 pr_debug("failed to populate pass object type attributes\n"); 561 goto err_release_attributes_data; 562 } 563 564 return 0; 565 566 err_release_attributes_data: 567 release_attributes_data(); 568 569 err_destroy_classdev: 570 device_unregister(wmi_priv.class_dev); 571 572 err_exit_bios_attr_pass_interface: 573 exit_bios_attr_pass_interface(); 574 575 err_exit_bios_attr_set_interface: 576 exit_bios_attr_set_interface(); 577 578 return ret; 579 } 580 581 static void __exit sysman_exit(void) 582 { 583 release_attributes_data(); 584 device_unregister(wmi_priv.class_dev); 585 exit_bios_attr_set_interface(); 586 exit_bios_attr_pass_interface(); 587 } 588 589 module_init(sysman_init); 590 module_exit(sysman_exit); 591 592 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>"); 593 MODULE_AUTHOR("Prasanth Ksr <prasanth.ksr@dell.com>"); 594 MODULE_AUTHOR("Divya Bharathi <divya.bharathi@dell.com>"); 595 MODULE_DESCRIPTION("Dell platform setting control interface"); 596 MODULE_LICENSE("GPL"); 597