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