Skip to content

Commit e5ff74f

Browse files
pastaq1Naim
authored andcommitted
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). - 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. Fixes: edc4b18 ("platform/x86: Add Lenovo Other Mode WMI Driver") Reported-by: Kurt Borja <kuurtb@gmail.com> Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/ Cc: stable@vger.kernel.org Reviewed-by: Rong Zhang <i@rong.moe> Tested-by: Rong Zhang <i@rong.moe> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
1 parent 4823b95 commit e5ff74f

2 files changed

Lines changed: 98 additions & 17 deletions

File tree

drivers/platform/x86/lenovo/wmi-gamezone.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ enum gamezone_events_type {
1010
};
1111

1212
enum thermal_mode {
13+
LWMI_GZ_THERMAL_MODE_NONE = 0x00,
1314
LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
1415
LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
1516
LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,

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

Lines changed: 97 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,8 @@ struct tunable_attr_01 {
550550
u8 feature_id;
551551
u8 device_id;
552552
u8 type_id;
553+
u8 cd_mode_id; /* mode arg for searching capdata */
554+
u8 cv_mode_id; /* mode arg for set/get current_value */
553555
};
554556

555557
static struct tunable_attr_01 ppt_pl1_spl = {
@@ -775,7 +777,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
775777
struct wmi_method_args_32 args = {};
776778
struct capdata01 capdata;
777779
enum thermal_mode mode;
778-
u32 attribute_id;
779780
u32 value;
780781
int ret;
781782

@@ -786,13 +787,12 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
786787
if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
787788
return -EBUSY;
788789

789-
attribute_id =
790-
FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
791-
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
792-
FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
793-
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
790+
args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
791+
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
792+
FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cd_mode_id) |
793+
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
794794

795-
ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
795+
ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
796796
if (ret)
797797
return ret;
798798

@@ -803,7 +803,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
803803
if (value < capdata.min_value || value > capdata.max_value)
804804
return -EINVAL;
805805

806-
args.arg0 = attribute_id;
806+
args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
807+
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
808+
FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cv_mode_id) |
809+
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
807810
args.arg1 = value;
808811

809812
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
@@ -837,21 +840,21 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
837840
struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
838841
struct wmi_method_args_32 args = {};
839842
enum thermal_mode mode;
840-
u32 attribute_id;
841843
int retval;
842844
int ret;
843845

844846
ret = lwmi_om_notifier_call(&mode);
845847
if (ret)
846848
return ret;
847849

848-
attribute_id =
849-
FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
850-
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
851-
FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
852-
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
850+
/* If "no-mode" is the supported mode, ensure we never send current mode */
851+
if (tunable_attr->cv_mode_id == LWMI_GZ_THERMAL_MODE_NONE)
852+
mode = tunable_attr->cv_mode_id;
853853

854-
args.arg0 = attribute_id;
854+
args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
855+
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
856+
FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
857+
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
855858

856859
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
857860
(unsigned char *)&args, sizeof(args),
@@ -862,6 +865,81 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
862865
return sysfs_emit(buf, "%d\n", retval);
863866
}
864867

868+
/**
869+
* lwmi_attr_01_is_supported() - Determine if the given attribute is supported.
870+
* @tunable_attr: The attribute to verify.
871+
*
872+
* First check if the attribute has a corresponding capdata01 table in the cd01
873+
* module under the "custom" mode (0xff). If that is not present then check if
874+
* there is a corresponding "no-mode" (0x00) entry. If either of those passes,
875+
* check capdata->supported for values > 0. If capdata is available, attempt to
876+
* determine the set/get mode for the current value property using a similar
877+
* pattern. If the value returned by either custom or no-mode is 0, or we get
878+
* an error, we assume that mode is not supported. If any of the above checks
879+
* fail then the attribute is not fully supported.
880+
*
881+
* The probed cd_mode_id/cv_mode_id are stored on the tunable_attr for later
882+
* reference.
883+
*
884+
* Return: bool.
885+
*/
886+
static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
887+
{
888+
u8 modes[2] = { LWMI_GZ_THERMAL_MODE_CUSTOM, LWMI_GZ_THERMAL_MODE_NONE };
889+
struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
890+
struct wmi_method_args_32 args = { 0x0, 0x0 };
891+
bool cd_mode_found = false;
892+
bool cv_mode_found = false;
893+
struct capdata01 capdata;
894+
int retval, ret, i;
895+
896+
/* Determine tunable_attr->cd_mode_id*/
897+
for (i = 0; i < ARRAY_SIZE(modes); i++) {
898+
args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
899+
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
900+
FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, modes[i]) |
901+
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
902+
903+
ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
904+
if (ret || !capdata.supported)
905+
continue;
906+
tunable_attr->cd_mode_id = modes[i];
907+
cd_mode_found = true;
908+
break;
909+
}
910+
911+
if (!cd_mode_found)
912+
return cd_mode_found;
913+
914+
dev_dbg(tunable_attr->dev,
915+
"cd_mode_id: %#010x\n", args.arg0);
916+
917+
/* Determine tunable_attr->cv_mode_id, returns 1 if supported*/
918+
for (i = 0; i < ARRAY_SIZE(modes); i++) {
919+
args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
920+
FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
921+
FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, modes[i]) |
922+
FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
923+
924+
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
925+
(unsigned char *)&args, sizeof(args),
926+
&retval);
927+
if (ret || !retval)
928+
continue;
929+
tunable_attr->cv_mode_id = modes[i];
930+
cv_mode_found = true;
931+
break;
932+
}
933+
934+
if (!cv_mode_found)
935+
return cv_mode_found;
936+
937+
dev_dbg(tunable_attr->dev, "cv_mode_id: %#010x, attribute support level: %#010x\n",
938+
args.arg0, capdata.supported);
939+
940+
return capdata.supported > 0 ? true : false;
941+
}
942+
865943
/* Lenovo WMI Other Mode Attribute macros */
866944
#define __LWMI_ATTR_RO(_func, _name) \
867945
{ \
@@ -985,12 +1063,14 @@ static void lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
9851063
}
9861064

9871065
for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) {
1066+
cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
1067+
if (!lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr))
1068+
continue;
1069+
9881070
err = sysfs_create_group(&priv->fw_attr_kset->kobj,
9891071
cd01_attr_groups[i].attr_group);
9901072
if (err)
9911073
goto err_remove_groups;
992-
993-
cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
9941074
}
9951075
return;
9961076

0 commit comments

Comments
 (0)