diff options
Diffstat (limited to 'drivers/platform/x86/think-lmi.c')
| -rw-r--r-- | drivers/platform/x86/think-lmi.c | 123 | 
1 files changed, 77 insertions, 46 deletions
diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index 86b33b74519b..1138f770149d 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -862,19 +862,18 @@ static umode_t auth_attr_is_visible(struct kobject *kobj,  	struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);  	/* We only want to display level and index settings on HDD/NVMe */ -	if ((attr == (struct attribute *)&auth_index) || -			(attr == (struct attribute *)&auth_level)) { +	if (attr == &auth_index.attr || attr == &auth_level.attr) {  		if ((setting == tlmi_priv.pwd_hdd) || (setting == tlmi_priv.pwd_nvme))  			return attr->mode;  		return 0;  	}  	/* We only display certificates on Admin account, if supported */ -	if ((attr == (struct attribute *)&auth_certificate) || -			(attr == (struct attribute *)&auth_signature) || -			(attr == (struct attribute *)&auth_save_signature) || -			(attr == (struct attribute *)&auth_cert_thumb) || -			(attr == (struct attribute *)&auth_cert_to_password)) { +	if (attr == &auth_certificate.attr || +	    attr == &auth_signature.attr || +	    attr == &auth_save_signature.attr || +	    attr == &auth_cert_thumb.attr || +	    attr == &auth_cert_to_password.attr) {  		if ((setting == tlmi_priv.pwd_admin) && tlmi_priv.certificate_support)  			return attr->mode;  		return 0; @@ -920,7 +919,7 @@ static ssize_t display_name_show(struct kobject *kobj, struct kobj_attribute *at  static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)  {  	struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); -	char *item, *value; +	char *item, *value, *p;  	int ret;  	ret = tlmi_setting(setting->index, &item, LENOVO_BIOS_SETTING_GUID); @@ -930,10 +929,15 @@ static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *a  	/* validate and split from `item,value` -> `value` */  	value = strpbrk(item, ",");  	if (!value || value == item || !strlen(value + 1)) -		return -EINVAL; - -	ret = sysfs_emit(buf, "%s\n", value + 1); +		ret = -EINVAL; +	else { +		/* On Workstations remove the Options part after the value */ +		p = strchrnul(value, ';'); +		*p = '\0'; +		ret = sysfs_emit(buf, "%s\n", value + 1); +	}  	kfree(item); +  	return ret;  } @@ -941,12 +945,23 @@ static ssize_t possible_values_show(struct kobject *kobj, struct kobj_attribute  {  	struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); -	if (!tlmi_priv.can_get_bios_selections) -		return -EOPNOTSUPP; -  	return sysfs_emit(buf, "%s\n", setting->possible_values);  } +static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, +		char *buf) +{ +	struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); + +	if (setting->possible_values) { +		/* Figure out what setting type is as BIOS does not return this */ +		if (strchr(setting->possible_values, ';')) +			return sysfs_emit(buf, "enumeration\n"); +	} +	/* Anything else is going to be a string */ +	return sysfs_emit(buf, "string\n"); +} +  static ssize_t current_value_store(struct kobject *kobj,  		struct kobj_attribute *attr,  		const char *buf, size_t count) @@ -1036,44 +1051,33 @@ static struct kobj_attribute attr_possible_values = __ATTR_RO(possible_values);  static struct kobj_attribute attr_current_val = __ATTR_RW_MODE(current_value, 0600); +static struct kobj_attribute attr_type = __ATTR_RO(type); + +static umode_t attr_is_visible(struct kobject *kobj, +					     struct attribute *attr, int n) +{ +	struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); + +	/* We don't want to display possible_values attributes if not available */ +	if ((attr == &attr_possible_values.attr) && (!setting->possible_values)) +		return 0; + +	return attr->mode; +} +  static struct attribute *tlmi_attrs[] = {  	&attr_displ_name.attr,  	&attr_current_val.attr,  	&attr_possible_values.attr, +	&attr_type.attr,  	NULL  };  static const struct attribute_group tlmi_attr_group = { +	.is_visible = attr_is_visible,  	.attrs = tlmi_attrs,  }; -static ssize_t tlmi_attr_show(struct kobject *kobj, struct attribute *attr, -				    char *buf) -{ -	struct kobj_attribute *kattr; - -	kattr = container_of(attr, struct kobj_attribute, attr); -	if (kattr->show) -		return kattr->show(kobj, kattr, buf); -	return -EIO; -} - -static ssize_t tlmi_attr_store(struct kobject *kobj, struct attribute *attr, -				     const char *buf, size_t count) -{ -	struct kobj_attribute *kattr; - -	kattr = container_of(attr, struct kobj_attribute, attr); -	if (kattr->store) -		return kattr->store(kobj, kattr, buf, count); -	return -EIO; -} - -static const struct sysfs_ops tlmi_kobj_sysfs_ops = { -	.show	= tlmi_attr_show, -	.store	= tlmi_attr_store, -}; -  static void tlmi_attr_setting_release(struct kobject *kobj)  {  	struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); @@ -1091,12 +1095,12 @@ static void tlmi_pwd_setting_release(struct kobject *kobj)  static const struct kobj_type tlmi_attr_setting_ktype = {  	.release        = &tlmi_attr_setting_release, -	.sysfs_ops	= &tlmi_kobj_sysfs_ops, +	.sysfs_ops	= &kobj_sysfs_ops,  };  static const struct kobj_type tlmi_pwd_setting_ktype = {  	.release        = &tlmi_pwd_setting_release, -	.sysfs_ops	= &tlmi_kobj_sysfs_ops, +	.sysfs_ops	= &kobj_sysfs_ops,  };  static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, @@ -1353,7 +1357,6 @@ static struct tlmi_pwd_setting *tlmi_create_auth(const char *pwd_type,  static int tlmi_analyze(void)  { -	acpi_status status;  	int i, ret;  	if (wmi_has_guid(LENOVO_SET_BIOS_SETTINGS_GUID) && @@ -1390,8 +1393,8 @@ static int tlmi_analyze(void)  		char *p;  		tlmi_priv.setting[i] = NULL; -		status = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID); -		if (ACPI_FAILURE(status)) +		ret = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID); +		if (ret)  			break;  		if (!item)  			break; @@ -1423,7 +1426,35 @@ static int tlmi_analyze(void)  			if (ret || !setting->possible_values)  				pr_info("Error retrieving possible values for %d : %s\n",  						i, setting->display_name); +		} else { +			/* +			 * Older Thinkstations don't support the bios_selections API. +			 * Instead they store this as a [Optional:Option1,Option2] section of the +			 * name string. +			 * Try and pull that out if it's available. +			 */ +			char *optitem, *optstart, *optend; + +			if (!tlmi_setting(setting->index, &optitem, LENOVO_BIOS_SETTING_GUID)) { +				optstart = strstr(optitem, "[Optional:"); +				if (optstart) { +					optstart += strlen("[Optional:"); +					optend = strstr(optstart, "]"); +					if (optend) +						setting->possible_values = +							kstrndup(optstart, optend - optstart, +									GFP_KERNEL); +				} +				kfree(optitem); +			}  		} +		/* +		 * firmware-attributes requires that possible_values are separated by ';' but +		 * Lenovo FW uses ','. Replace appropriately. +		 */ +		if (setting->possible_values) +			strreplace(setting->possible_values, ',', ';'); +  		kobject_init(&setting->kobj, &tlmi_attr_setting_ktype);  		tlmi_priv.setting[i] = setting;  		kfree(item);  |