Re: [PATCH v2 3/3] platform/x86: think-lmi: Add WMI interface support on Lenovo platforms

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sun, May 9, 2021 at 5:02 AM Mark Pearson <markpearson@xxxxxxxxxx> wrote:
>
> For Lenovo platforms that support a WMI interface to the BIOS add
> support, using the firmware-attributes class, to allow users to access
> and modify various BIOS related settings.

Few comments below.

...

> +/* Convert BIOS WMI error string to suitable error code */

Can it be rather the mapping structure? In that case you move string
literals, error codes and comments to its entries.
Here will be something like for-loop.

...

> +static int tlmi_extract_error(const struct acpi_buffer *output)
> +{
> +       const union acpi_object *obj;
> +       int ret;
> +
> +       obj = output->pointer;
> +       if (!obj || obj->type != ACPI_TYPE_STRING || !obj->string.pointer) {

I would split !obj check, but I don't know if it is even a useful
check. Can it be the case?

> +               kfree(obj);

Basically you double check for NULL (see above)

Same for other similar places in the code.

> +               return -EIO;
> +       }
> +
> +       ret = tlmi_errstr_to_err(obj->string.pointer);
> +       kfree(obj);
> +       return ret;

What I really do not like here is that you are freeing something out
of the scope. Please, free it where it was acquired.

> +}

...

> +       /*
> +        * duplicated call required to match bios workaround for behavior
> +        * seen when WMI accessed via scripting on other OS

/*
 * Multi-line comments
 * should have this kind of
 * style. Pay attention to the details.
 */

> +        */

...

> +       *string = kstrdup(obj->string.pointer, GFP_KERNEL);
> +       kfree(obj);
> +       return *string ? 0 : -ENOMEM;

This breaks the principle "don't touch the output in error case".

...

> +       if (setting->valid)
> +               return sysfs_emit(buf, "1\n");
> +       else
> +               return sysfs_emit(buf, "0\n");

sysfs_emit(buf, "%d\n", !!setting->valid);
?

...

> +static ssize_t current_password_store(struct kobject *kobj,
> +                                     struct kobj_attribute *attr,
> +                                     const char *buf, size_t count)
> +{
> +       struct tlmi_pwd_setting *setting = container_of(kobj, struct tlmi_pwd_setting, kobj);
> +       int length;

> +       length = strlen(buf);
> +       if (buf[length-1] == '\n')
> +               length--;

This will prevent you from using \n in the password. Why?

> +       if (length >= TLMI_PWD_MAXLEN)
> +               return -EINVAL;

I guess password *string* length is wrong per se. count seems the
proper one which one should use.

> +
> +       memcpy(setting->password, buf, length);

> +       setting->password[length] = '\0';

Why is the password a *string*? From where that assumption comes from?

> +       return count;
> +}
> +
> +static struct kobj_attribute auth_current_password = __ATTR_WO(current_password);

...

> +       p = strchr(new_pwd, '\n');
> +       if (p)
> +               *p = '\0';

strtrim(). But see above.

> +       if (strlen(new_pwd) > setting->maxlen) {
> +               ret = -EINVAL;
> +               goto out;
> +       }

...

> +       /* Format: 'PasswordType,CurrentPw,NewPw,Encoding,KbdLang;' */
> +       len = strlen(setting->password) + strlen(encoding_options[setting->encoding])
> +               + strlen(setting->kbdlang) + 3 /* type */
> +               + strlen(new_pwd) + 6 /* punctuation and terminator*/;
> +       auth_str = kzalloc(len, GFP_KERNEL);
> +       snprintf(auth_str, len, "%s,%s,%s,%s,%s;",
> +                setting->pwd_type, setting->password, new_pwd,
> +                encoding_options[setting->encoding], setting->kbdlang);

NIH of kasprintf()

...

> +       return ret ? ret : count;

      return ret ?: count;

is shorter.

...

> +       if (strcmp(kobj->name, "Admin") == 0)
> +               return sprintf(buf, "bios-admin\n");
> +       else if (strcmp(kobj->name, "System") == 0)

Redundant 'else'

> +               return sprintf(buf, "power-on\n");
> +       return -EIO;

...

> +static struct attribute *auth_attrs[] = {
> +       &auth_is_pass_set.attr,
> +       &auth_min_pass_length.attr,
> +       &auth_max_pass_length.attr,
> +       &auth_current_password.attr,
> +       &auth_new_password.attr,
> +       &auth_role.attr,
> +       &auth_mechanism.attr,
> +       &auth_encoding.attr,
> +       &auth_kbdlang.attr,
> +       NULL,

The terminator line doesn't need a comma.

> +};

...

> +       new_setting = kstrdup(buf, GFP_KERNEL);

strtrim(buf) ?

> +       if (!new_setting)
> +               return -ENOMEM;

> +       p = strchr(new_setting, '\n');
> +       if (p)
> +               *p = '\0';

See above.

> +       alloc_len = strlen(setting->display_name) + 3 + strlen(new_setting);
> +
> +       if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password) {
> +               auth_len += strlen(tlmi_priv.pwd_admin->password) + 1
> +                       + strlen(encoding_options[tlmi_priv.pwd_admin->encoding]) + 1
> +                       + strlen(tlmi_priv.pwd_admin->kbdlang) + 1 + 1;
> +               auth_str = kmalloc(auth_len, GFP_KERNEL);
> +               if (!auth_str) {
> +                       ret = -ENOMEM;
> +                       goto out;
> +               }
> +
> +               sprintf(auth_str, "%s,%s,%s;",
> +                               tlmi_priv.pwd_admin->password,
> +                               encoding_options[tlmi_priv.pwd_admin->encoding],
> +                               tlmi_priv.pwd_admin->kbdlang);
> +               alloc_len += auth_len;

HIN of kasprintf()

> +       }
> +
> +       set_str = kmalloc(alloc_len, GFP_KERNEL);
> +       if (!set_str) {
> +               ret = -ENOMEM;
> +               goto out;
> +       }
> +
> +       str_ix += sprintf(set_str, "%s,%s", setting->display_name, new_setting);

Ditto,

> +       if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password)
> +               sprintf(set_str + str_ix, ",%s", auth_str);
> +       else
> +               sprintf(set_str + str_ix, ";");
> +
> +       ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTINGS_GUID, set_str);
> +       if (ret)
> +               goto out;
> +
> +       if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password)
> +               ret = tlmi_save_bios_settings(auth_str);
> +       else
> +               ret = tlmi_save_bios_settings("");

> +       if (ret)
> +               goto out;

Useless.

> +out:
> +       kfree(auth_str);
> +       kfree(set_str);
> +       kfree(new_setting);
> +       return ret ? ret : count;

Can be shorter.

> +}

> +static struct kobj_attribute attr_displ_name =
> +               __ATTR_RO(display_name);

One line (and so on for all of them).

...

> +static struct attribute *tlmi_attrs[] = {
> +       &attr_displ_name.attr,
> +       &attr_current_val.attr,
> +       &attr_possible_values.attr,
> +       NULL,

No comma.

> +};

...

> +       struct kobj_attribute *kattr;

> +       ssize_t ret = -EIO;

Useless. Use direct return.

> +
> +       kattr = container_of(attr, struct kobj_attribute, attr);
> +       if (kattr->show)
> +               ret = kattr->show(kobj, kattr, buf);
> +       return ret;

...

> +       struct kobj_attribute *kattr;
> +       ssize_t ret = -EIO;
> +
> +       kattr = container_of(attr, struct kobj_attribute, attr);
> +       if (kattr->store)
> +               ret = kattr->store(kobj, kattr, buf, count);
> +       return ret;

As above.

...

> +       for (i = 0; i < TLMI_SETTINGS_COUNT; i++) {
> +               /*Check if index is a valid setting - skip if it isn't */

Missed space.

> +               if (!tlmi_priv.setting[i])
> +                       continue;
> +
> +               /* build attribute */
> +               tlmi_priv.setting[i]->kobj.kset = tlmi_priv.attribute_kset;
> +               ret = kobject_init_and_add(&tlmi_priv.setting[i]->kobj, &attr_name_ktype,
> +                               NULL, "%s", tlmi_priv.setting[i]->display_name);
> +               if (ret)
> +                       goto fail_create_attr;
> +
> +               ret = sysfs_create_group(&tlmi_priv.setting[i]->kobj, &tlmi_attr_group);
> +               if (ret)
> +                       goto fail_create_attr;
> +       }

...

> +       int i = 0;

Why assignment?

> +       for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) {
> +               char *item = NULL;
> +               int spleng = 0;
> +               int num = 0;
> +               char *p;
> +               struct tlmi_attr_setting *setting;
> +
> +               tlmi_priv.setting[i] = NULL;
> +               status = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID);
> +               if (ACPI_FAILURE(status))
> +                       break;
> +               if (!item)
> +                       break;
> +               if (!*item)
> +                       continue;
> +
> +               /* It is not allowed to have '/' for file name. Convert it into '\'. */
> +               spleng = strlen(item);
> +               for (num = 0; num < spleng; num++) {
> +                       if (item[num] == '/')
> +                               item[num] = '\\';
> +               }

NIH of strreplace(), but please check its name.

> +
> +               /* Remove the value part */
> +               p = strchr(item, ',');
> +               if (p)
> +                       *p = '\0';
> +
> +               /* Create a setting entry */
> +               setting = kzalloc(sizeof(struct tlmi_attr_setting), GFP_KERNEL);
> +               if (!setting) {
> +                       ret = -ENOMEM;
> +                       goto fail_clear_attr;
> +               }
> +               setting->index = i;
> +               strscpy(setting->display_name, item, TLMI_SETTINGS_MAXLEN);
> +               /* If BIOS selections supported, load those */
> +               if (tlmi_priv.can_get_bios_selections) {
> +                       ret = tlmi_get_bios_selections(setting->display_name,
> +                                       &setting->possible_values);
> +                       if (ret || !setting->possible_values)
> +                               pr_info("Error retrieving possible values for %d : %s\n",
> +                                               i, setting->display_name);
> +               }
> +               tlmi_priv.setting[i] = setting;
> +               tlmi_priv.settings_count++;
> +               kfree(item);
> +       }

I stopped here because it's full of such small issues.

Basically the summary
 - try to find the existing APIs for routines that quite often in use
(usually string operations)
 - try to make your code compact with the same level of readability


-- 
With Best Regards,
Andy Shevchenko



[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux