Skip to content

Commit dcdf945

Browse files
committed
[FROM-ML] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
Adds lwmi_is_attr_01_supported, and only creates the attribute subfolder if the attribute is supported by the hardware. Due to some poorly implemented BIOS, this is a multi-step sequence of events. This is because: - Some BIOS support getting the capability data from custom mode (0xff), while others only support it in no-mode (0x00). - Similarly, some BIOS support get/set for the current value from custom mode (0xff), while others only support it in no-mode (0x00). - Some BIOS report capability data for a method that is not fully implemented. - Some BIOS have methods fully implemented, but no complimentary capability data. To ensure we only expose fully implemented methods with corresponding capability data, we check each outcome before reporting that an attribute can be supported. Checking for lwmi_is_attr_01_supported during remove is not done to ensure that we don't attempt to call cd01 or send WMI events if one of the interfaces being removed was the cause of the driver unloading. While adding members to tunable_attr_01, remove unused capdata pointer and limit size of all ID's to the appropriate size. Reported-by: Kurt Borja <kuurtb@gmail.com> Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/ Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
1 parent e541a9c commit dcdf945

File tree

1 file changed

+104
-19
lines changed

1 file changed

+104
-19
lines changed

drivers/platform/x86/lenovo/wmi-other.c

Lines changed: 104 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -545,11 +545,12 @@ static void lwmi_om_fan_info_collect_cd_fan(struct device *dev, struct cd_list *
545545
/* ======== fw_attributes (component: lenovo-wmi-capdata 01) ======== */
546546

547547
struct tunable_attr_01 {
548-
struct capdata01 *capdata;
549548
struct device *dev;
550-
u32 feature_id;
551-
u32 device_id;
552-
u32 type_id;
549+
u8 feature_id;
550+
u8 device_id;
551+
u8 type_id;
552+
u8 cd_mode_id; /* mode arg for searching capdata */
553+
u8 cv_mode_id; /* mode arg for set/get current_value */
553554
};
554555

555556
static struct tunable_attr_01 ppt_pl1_spl = {
@@ -716,7 +717,7 @@ static ssize_t attr_capdata01_show(struct kobject *kobj,
716717
int value, ret;
717718

718719
attribute_id = LWMI_ATTR_ID(tunable_attr->device_id, tunable_attr->feature_id,
719-
LWMI_GZ_THERMAL_MODE_CUSTOM, tunable_attr->type_id);
720+
tunable_attr->cd_mode_id, tunable_attr->type_id);
720721

721722
ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
722723
if (ret)
@@ -771,7 +772,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
771772
struct wmi_method_args_32 args;
772773
struct capdata01 capdata;
773774
enum thermal_mode mode;
774-
u32 attribute_id;
775775
u32 value;
776776
int ret;
777777

@@ -782,10 +782,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
782782
if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
783783
return -EBUSY;
784784

785-
attribute_id = LWMI_ATTR_ID(tunable_attr->device_id, tunable_attr->feature_id,
786-
mode, tunable_attr->type_id);
785+
args.arg0 = LWMI_ATTR_ID(tunable_attr->device_id, tunable_attr->feature_id,
786+
tunable_attr->cd_mode_id, tunable_attr->type_id);
787787

788-
ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
788+
ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
789789
if (ret)
790790
return ret;
791791

@@ -796,7 +796,8 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
796796
if (value < capdata.min_value || value > capdata.max_value)
797797
return -EINVAL;
798798

799-
args.arg0 = attribute_id;
799+
args.arg0 = LWMI_ATTR_ID(tunable_attr->device_id, tunable_attr->feature_id,
800+
tunable_attr->cv_mode_id, tunable_attr->type_id);
800801
args.arg1 = value;
801802

802803
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
@@ -830,13 +831,16 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
830831
struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
831832
struct wmi_method_args_32 args;
832833
enum thermal_mode mode;
833-
int retval;
834-
int ret;
834+
int retval, ret;
835835

836836
ret = lwmi_om_notifier_call(&mode);
837837
if (ret)
838838
return ret;
839839

840+
/* If "no-mode" is the supported mode, ensure we never send current mode */
841+
if (tunable_attr->cv_mode_id == LWMI_GZ_THERMAL_MODE_NONE)
842+
mode = tunable_attr->cv_mode_id;
843+
840844
args.arg0 = LWMI_ATTR_ID(tunable_attr->device_id, tunable_attr->feature_id,
841845
mode, tunable_attr->type_id);
842846

@@ -849,6 +853,85 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
849853
return sysfs_emit(buf, "%d\n", retval);
850854
}
851855

856+
/**
857+
* lwmi_attr_01_is_supported() - Determine if the given attribute is supported.
858+
* @tunable_attr: The attribute to verify.
859+
*
860+
* First check if the attribute has a corresponding capdata01 table in the cd01
861+
* module under the "custom" mode (0xff). If that is not present then check if
862+
* there is a corresponding "no-mode" (0x00) entry. If either of those passes,
863+
* check capdata->supported for values > 0. If capdata is available, attempt to
864+
* determine the set/get mode for the current value property using a similar
865+
* pattern. If the value returned by either custom or no-mode is 0, or we get
866+
* an error, we assume that mode is not supported. If any of the above checks
867+
* fail then the attribute is not fully supported.
868+
*
869+
* The probed cd_mode_id/cv_mode_id are stored on the tunable_attr for later
870+
* reference.
871+
*
872+
* Return: Support level, or an error code.
873+
*/
874+
static int lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
875+
{
876+
struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
877+
u8 mode = LWMI_GZ_THERMAL_MODE_CUSTOM;
878+
struct wmi_method_args_32 args;
879+
struct capdata01 capdata;
880+
int retval, ret;
881+
882+
/* Determine tunable_attr->cd_mode_id */
883+
no_mode_fallback_1:
884+
args.arg0 = LWMI_ATTR_ID(tunable_attr->device_id, tunable_attr->feature_id,
885+
mode, tunable_attr->type_id);
886+
887+
ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
888+
if (ret && mode) {
889+
dev_dbg(tunable_attr->dev, "Attribute id %x not supported\n", args.arg0);
890+
mode = LWMI_GZ_THERMAL_MODE_NONE;
891+
goto no_mode_fallback_1;
892+
}
893+
if (ret)
894+
goto not_supported;
895+
if (!capdata.supported) {
896+
ret = -EOPNOTSUPP;
897+
goto not_supported;
898+
}
899+
900+
tunable_attr->cd_mode_id = mode;
901+
902+
/* Determine tunable_attr->cv_mode_id */
903+
mode = LWMI_GZ_THERMAL_MODE_CUSTOM;
904+
no_mode_fallback_2:
905+
args.arg0 = LWMI_ATTR_ID(tunable_attr->device_id, tunable_attr->feature_id,
906+
mode, tunable_attr->type_id);
907+
908+
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
909+
(unsigned char *)&args, sizeof(args),
910+
&retval);
911+
if ((ret && mode) || (!retval && mode)) {
912+
dev_dbg(tunable_attr->dev, "Attribute id %x not supported\n", args.arg0);
913+
mode = LWMI_GZ_THERMAL_MODE_NONE;
914+
goto no_mode_fallback_2;
915+
}
916+
if (ret)
917+
goto not_supported;
918+
if (retval == 0) {
919+
ret = -EOPNOTSUPP;
920+
goto not_supported;
921+
}
922+
923+
tunable_attr->cv_mode_id = mode;
924+
dev_dbg(tunable_attr->dev, "cd_mode_id: %02x%02x%02x%02x, cv_mode_id: %#08x attribute support level: %x\n",
925+
tunable_attr->device_id, tunable_attr->feature_id, tunable_attr->cd_mode_id,
926+
tunable_attr->type_id, args.arg0, capdata.supported);
927+
928+
return capdata.supported;
929+
930+
not_supported:
931+
dev_dbg(tunable_attr->dev, "Attribute id %x not supported\n", args.arg0);
932+
return ret;
933+
}
934+
852935
/* Lenovo WMI Other Mode Attribute macros */
853936
#define __LWMI_ATTR_RO(_func, _name) \
854937
{ \
@@ -972,19 +1055,21 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
9721055
}
9731056

9741057
for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) {
975-
err = sysfs_create_group(&priv->fw_attr_kset->kobj,
976-
cd01_attr_groups[i].attr_group);
977-
if (err)
978-
goto err_remove_groups;
979-
9801058
cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
1059+
if (lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr) > 0) {
1060+
err = sysfs_create_group(&priv->fw_attr_kset->kobj,
1061+
cd01_attr_groups[i].attr_group);
1062+
if (err)
1063+
goto err_remove_groups;
1064+
}
9811065
}
9821066
return 0;
9831067

9841068
err_remove_groups:
9851069
while (i--)
986-
sysfs_remove_group(&priv->fw_attr_kset->kobj,
987-
cd01_attr_groups[i].attr_group);
1070+
if (lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr) > 0)
1071+
sysfs_remove_group(&priv->fw_attr_kset->kobj,
1072+
cd01_attr_groups[i].attr_group);
9881073

9891074
kset_unregister(priv->fw_attr_kset);
9901075

0 commit comments

Comments
 (0)