Re: [External] 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]

 



Hi Andy,

Many thanks for the review

On 2021-05-22 7:04 a.m., Andy Shevchenko wrote:
> 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.
> 
Yes, I can do this
> ...
> 
>> +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?
I wasn't certain on if obj could ever be null, I suspect not but as I
wasn't 100% sure I put the check in just in case.
> 
>> +               kfree(obj);
> 
> Basically you double check for NULL (see above)
> 
> Same for other similar places in the code.
> 
OK - understood. Thanks.

>> +               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.
Ack
> 
>> +}
> 
> ...
> 
>> +       /*
>> +        * 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.
>  */
Ack (and I'm assuming here the concern is the 'D' and missing '.', let
me know if there's anything else that is important)

> 
>> +        */
> 
> ...
> 
>> +       *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".

But I'm not changing *string in an error case here - I'm not
understanding the issue here.
Happy to rewrite it to make it clearer though if that would help.

> 
> ...
> 
>> +       if (setting->valid)
>> +               return sysfs_emit(buf, "1\n");
>> +       else
>> +               return sysfs_emit(buf, "0\n");
> 
> sysfs_emit(buf, "%d\n", !!setting->valid);
> ?
Yeah - this was picked up on in an earlier review. Apologies.
> 
> ...
> 
>> +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?
The BIOS doesn't like it - so we strip it out :)

> 
>> +       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.
Yes, this is being cleaned up after review from Hans.

> 
>> +
>> +       memcpy(setting->password, buf, length);
> 
>> +       setting->password[length] = '\0';
> 
> Why is the password a *string*? From where that assumption comes from?
Sorry, I'm not understanding the question here. It's what our BIOS is
expecting. I'm missing something here
> 
>> +       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.
ack.
> 
>> +       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()
Not sure what NIH is - but I'm assuming I should be using kasprintf
instead of snprinf :)
I wasn't aware of it - thank you.
> 
> ...
> 
>> +       return ret ? ret : count;
> 
>       return ret ?: count;
> 
> is shorter.
Ack
> 
> ...
> 
>> +       if (strcmp(kobj->name, "Admin") == 0)
>> +               return sprintf(buf, "bios-admin\n");
>> +       else if (strcmp(kobj->name, "System") == 0)
> 
> Redundant 'else'
Ack
> 
>> +               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.
Argh. I always get this wrong as to when it is required and when it isn't.
I'll fix
> 
>> +};
> 
> ...
> 
>> +       new_setting = kstrdup(buf, GFP_KERNEL);
> 
> strtrim(buf) ?
ack
> 
>> +       if (!new_setting)
>> +               return -ENOMEM;
> 
>> +       p = strchr(new_setting, '\n');
>> +       if (p)
>> +               *p = '\0';
> 
> See above.
Thanks
> 
>> +       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()
ack
> 
>> +       }
>> +
>> +       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,
ack
> 
>> +       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.
Ack (and oops)
> 
>> +out:
>> +       kfree(auth_str);
>> +       kfree(set_str);
>> +       kfree(new_setting);
>> +       return ret ? ret : count;
> 
> Can be shorter.
Ack
> 
>> +}
> 
>> +static struct kobj_attribute attr_displ_name =
>> +               __ATTR_RO(display_name);
> 
> One line (and so on for all of them).
Ack
> 
> ...
> 
>> +static struct attribute *tlmi_attrs[] = {
>> +       &attr_displ_name.attr,
>> +       &attr_current_val.attr,
>> +       &attr_possible_values.attr,
>> +       NULL,
> 
> No comma.
Ack
> 
>> +};
> 
> ...
> 
>> +       struct kobj_attribute *kattr;
> 
>> +       ssize_t ret = -EIO;
> 
> Useless. Use direct return.
Ack
> 
>> +
>> +       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.
Ack
> 
> ...
> 
>> +       for (i = 0; i < TLMI_SETTINGS_COUNT; i++) {
>> +               /*Check if index is a valid setting - skip if it isn't */
> 
> Missed space.
Ack
> 
>> +               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?
Left over from earlier iteration. Should be removed.

> 
>> +       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.
Will do. Thanks!

> 
>> +
>> +               /* 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.
Ouch.
Many thanks for the review. I'll go through the rest and see if I can
clean it up more to make it less painful.
> 
> 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
> 
> 
I really had tried to do that - many thanks for your patience and review
details.

Mark



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

  Powered by Linux