From nobody Thu Apr 2 21:49:47 2026 Received: from mail-dy1-f181.google.com (mail-dy1-f181.google.com [74.125.82.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0D5C131AA83 for ; Fri, 13 Feb 2026 08:12:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770970375; cv=none; b=iFI/NZcgWMnb6YydJuYuW6BXeyuDxIFPLa8zZNspMXmRsKMZZzrK0GfVHmX0nXhkr/0W0OkJ/A04J6MYLc7ZGFPwaMs/1NX197FHIuhCB5vJEMbLz1QYk2lLrcq1WmhRj8Yzjt2kgTMCBDDtn1RIzqkQrBEz4L/xWca4ZEodIms= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770970375; c=relaxed/simple; bh=KdPkHUHHIgp5cMPFy718m0SBxTLs26+2jU02FqTmmpE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Z/y0ieF255EBVWqfzt9QE6QjS8+1WV2v39X+ulHXnlg7HhS51emsmhC87K7lR6sWzcqG3eOu6Te7U/RcTk7g4M52c5REbwlN/HPcQAKk4JWTwcGZSFJBEb896Q85uPgYDQvXokTvvNbpiA5qBGbVLX5InRrsD7ngRZMBWLV1zkc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=d7GfOVNT; arc=none smtp.client-ip=74.125.82.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="d7GfOVNT" Received: by mail-dy1-f181.google.com with SMTP id 5a478bee46e88-2ba9c484e5eso700945eec.1 for ; Fri, 13 Feb 2026 00:12:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770970373; x=1771575173; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=WNnTNGaQauNGyuAdgf6YmwFPKlR5RGFWSx3LoZcGKDU=; b=d7GfOVNTY8N6hBdcEMRxLBWReh8Qf/9YmFjd1zbmqo0YmDIDkRnOniHtK1TUA4LW27 Bb+5BuAuZspj01zO82fllQDN0fODktnzct6Sy+y0EC5eGkMc9DgUSGUgERvDi8xa7nDN gMoNpOMGmHjn+XTuId8KShkdhH1pb9qgrZ/2v7fgIlxFylfn9UxYy09mnA2fzhKeiFVt F5YHEeiWAg4Lm1l4j+ju+921FneLaXnn4akpSz5llfRR/Ze3dCFxX1Ejc6ilxJnH+oNn MNF/iQbBAfYxQ6ohFoWl2JC6Qfsdd7qT3d90JD8HjecPtxOo3bUeE0ZtKEocUgLAKCMQ 8yLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770970373; x=1771575173; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=WNnTNGaQauNGyuAdgf6YmwFPKlR5RGFWSx3LoZcGKDU=; b=QI/uuxs8fVoGyhRoJ1htW9ffv0e477aEYnmuo+WmmyT/67q/1kHo1JjdmBO7Z4q8It foswE6G/NNimlfQdaOzlDH/DOqJSRNAxUvhDAhFGE9Xd5Y50RRZsJxMewkHHeqgEQAyG ez7LaUbav+cMNGeLa4fyjgEincaYbVRlN3M6ZF6UlEkVUFjMjIGJBY9fRkNXVWjTfyCB RxBXssdpYgDuPYPNpdHwFYxIMqB0o9+UItucJHV20loT70JfmZCSWokBTJv+GCvRfy4q clMviN9AocvPkZ0rKZ648yL41ju+dWVPRoemMlylBFjvCVSnhile6VkTpdt5ZenwxJ33 e1SA== X-Forwarded-Encrypted: i=1; AJvYcCWSrXXOS7eFPXEh7HEPh+B1NPidMcXGGOFK4NR8h3L/7CeYeZ/Ldf7CikrM1y1xzBqGUnDKDZn7k/DY0Kc=@vger.kernel.org X-Gm-Message-State: AOJu0YyciH90uzoswTIL8wLUYypzWpSn80psAb68LtK3oLUbn4fWjwJg iggo8dtBjNRKIkTIP0nNHdlVsWkukytVMFOPP7XC7KjVVcNAJqifch7e X-Gm-Gg: AZuq6aLf4uripfTY/8YIxJKjS7q4e6w6ItN3caK9OzXdv74j8Uk+ikiFL81bTOlqvWG JAWuvgqVgQ8v++yScuLx5NvsA8WShWellNKE0YJhp2uhoeN1cszERlnjt1Q/ajP8ChOj3iSXAt6 HqzYHN90kpDgAg1YaBdwDyp7CvQxQvioJm0XM2OAx57z6nGExY6mZIhBEIOfDFuZ1mg/H43yUvM kFq3O4iul/PzjOGiA5oLZEW1qcNAyctIIruqgh6/Cu5dzQOBSXupZiaAf2BRDxjHTl9sUy9YqB8 V963XMbE82A6hKBdcYCmeGgCT3mrt0uG+Ty3Xx7jmh2JgIqdQrETzQrmizG9X7weHoDatAuOoR2 2IzFlkyiEbH40Tt2WC52XDuCvY7u6R270The+iTCZOAY9aRJPKpv6Q4eNxU9eQc1UHqx9txz+4i sothtBaFi4DtOhFP1asoYjPmrPDk6TK0UuKmw7rqTrAsD9qyTRQp1jy1ELjGrWyvZsjNbnYgIsd 2A= X-Received: by 2002:a05:7300:1495:b0:2ba:64c6:2cd7 with SMTP id 5a478bee46e88-2babc4a9bc1mr298161eec.32.1770970373133; Fri, 13 Feb 2026 00:12:53 -0800 (PST) Received: from lappy (108-228-232-20.lightspeed.sndgca.sbcglobal.net. [108.228.232.20]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2ba9dd0a04csm4975052eec.33.2026.02.13.00.12.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 13 Feb 2026 00:12:52 -0800 (PST) From: "Derek J. Clark" To: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Hans de Goede Cc: Mark Pearson , Armin Wolf , Jonathan Corbet , Rong Zhang , Kurt Borja , "Derek J . Clark" , platform-driver-x86@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/5] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices Date: Fri, 13 Feb 2026 08:02:54 +0000 Message-ID: <20260213081243.794288-3-derekjohn.clark@gmail.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260213081243.794288-1-derekjohn.clark@gmail.com> References: <20260213081243.794288-1-derekjohn.clark@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7= XRC@gmail.com/ Signed-off-by: Derek J. Clark --- drivers/platform/x86/lenovo/wmi-other.c | 123 ++++++++++++++++++++---- 1 file changed, 104 insertions(+), 19 deletions(-) diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86= /lenovo/wmi-other.c index 559feb49c9f11..f3f12303e3798 100644 --- a/drivers/platform/x86/lenovo/wmi-other.c +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -545,11 +545,12 @@ static void lwmi_om_fan_info_collect_cd_fan(struct de= vice *dev, struct cd_list * /* =3D=3D=3D=3D=3D=3D=3D=3D fw_attributes (component: lenovo-wmi-capdata 0= 1) =3D=3D=3D=3D=3D=3D=3D=3D */ =20 struct tunable_attr_01 { - struct capdata01 *capdata; struct device *dev; - u32 feature_id; - u32 device_id; - u32 type_id; + u8 feature_id; + u8 device_id; + u8 type_id; + u8 cd_mode_id; /* mode arg for searching capdata */ + u8 cv_mode_id; /* mode arg for set/get current_value */ }; =20 static struct tunable_attr_01 ppt_pl1_spl =3D { @@ -716,7 +717,7 @@ static ssize_t attr_capdata01_show(struct kobject *kobj, int value, ret; =20 attribute_id =3D LWMI_ATTR_ID(tunable_attr->device_id, tunable_attr->feat= ure_id, - LWMI_GZ_THERMAL_MODE_CUSTOM, tunable_attr->type_id); + tunable_attr->cd_mode_id, tunable_attr->type_id); =20 ret =3D lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata); if (ret) @@ -771,7 +772,6 @@ static ssize_t attr_current_value_store(struct kobject = *kobj, struct wmi_method_args_32 args; struct capdata01 capdata; enum thermal_mode mode; - u32 attribute_id; u32 value; int ret; =20 @@ -782,10 +782,10 @@ static ssize_t attr_current_value_store(struct kobjec= t *kobj, if (mode !=3D LWMI_GZ_THERMAL_MODE_CUSTOM) return -EBUSY; =20 - attribute_id =3D LWMI_ATTR_ID(tunable_attr->device_id, tunable_attr->feat= ure_id, - mode, tunable_attr->type_id); + args.arg0 =3D LWMI_ATTR_ID(tunable_attr->device_id, tunable_attr->feature= _id, + tunable_attr->cd_mode_id, tunable_attr->type_id); =20 - ret =3D lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata); + ret =3D lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata); if (ret) return ret; =20 @@ -796,7 +796,8 @@ static ssize_t attr_current_value_store(struct kobject = *kobj, if (value < capdata.min_value || value > capdata.max_value) return -EINVAL; =20 - args.arg0 =3D attribute_id; + args.arg0 =3D LWMI_ATTR_ID(tunable_attr->device_id, tunable_attr->feature= _id, + tunable_attr->cv_mode_id, tunable_attr->type_id); args.arg1 =3D value; =20 ret =3D 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, struct lwmi_om_priv *priv =3D dev_get_drvdata(tunable_attr->dev); struct wmi_method_args_32 args; enum thermal_mode mode; - int retval; - int ret; + int retval, ret; =20 ret =3D lwmi_om_notifier_call(&mode); if (ret) return ret; =20 + /* If "no-mode" is the supported mode, ensure we never send current mode = */ + if (tunable_attr->cv_mode_id =3D=3D LWMI_GZ_THERMAL_MODE_NONE) + mode =3D tunable_attr->cv_mode_id; + args.arg0 =3D LWMI_ATTR_ID(tunable_attr->device_id, tunable_attr->feature= _id, mode, tunable_attr->type_id); =20 @@ -849,6 +853,85 @@ static ssize_t attr_current_value_show(struct kobject = *kobj, return sysfs_emit(buf, "%d\n", retval); } =20 +/** + * lwmi_attr_01_is_supported() - Determine if the given attribute is suppo= rted. + * @tunable_attr: The attribute to verify. + * + * First check if the attribute has a corresponding capdata01 table in the= cd01 + * module under the "custom" mode (0xff). If that is not present then chec= k if + * there is a corresponding "no-mode" (0x00) entry. If either of those pas= ses, + * check capdata->supported for values > 0. If capdata is available, attem= pt to + * determine the set/get mode for the current value property using a simil= ar + * pattern. If the value returned by either custom or no-mode is 0, or we = get + * an error, we assume that mode is not supported. If any of the above che= cks + * fail then the attribute is not fully supported. + * + * The probed cd_mode_id/cv_mode_id are stored on the tunable_attr for lat= er + * reference. + * + * Return: Support level, or an error code. + */ +static int lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr) +{ + struct lwmi_om_priv *priv =3D dev_get_drvdata(tunable_attr->dev); + u8 mode =3D LWMI_GZ_THERMAL_MODE_CUSTOM; + struct wmi_method_args_32 args; + struct capdata01 capdata; + int retval, ret; + + /* Determine tunable_attr->cd_mode_id */ +no_mode_fallback_1: + args.arg0 =3D LWMI_ATTR_ID(tunable_attr->device_id, tunable_attr->feature= _id, + mode, tunable_attr->type_id); + + ret =3D lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata); + if (ret && mode) { + dev_dbg(tunable_attr->dev, "Attribute id %x not supported\n", args.arg0); + mode =3D LWMI_GZ_THERMAL_MODE_NONE; + goto no_mode_fallback_1; + } + if (ret) + goto not_supported; + if (!capdata.supported) { + ret =3D -EOPNOTSUPP; + goto not_supported; + } + + tunable_attr->cd_mode_id =3D mode; + + /* Determine tunable_attr->cv_mode_id */ + mode =3D LWMI_GZ_THERMAL_MODE_CUSTOM; +no_mode_fallback_2: + args.arg0 =3D LWMI_ATTR_ID(tunable_attr->device_id, tunable_attr->feature= _id, + mode, tunable_attr->type_id); + + ret =3D lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET, + (unsigned char *)&args, sizeof(args), + &retval); + if ((ret && mode) || (!retval && mode)) { + dev_dbg(tunable_attr->dev, "Attribute id %x not supported\n", args.arg0); + mode =3D LWMI_GZ_THERMAL_MODE_NONE; + goto no_mode_fallback_2; + } + if (ret) + goto not_supported; + if (retval =3D=3D 0) { + ret =3D -EOPNOTSUPP; + goto not_supported; + } + + tunable_attr->cv_mode_id =3D mode; + dev_dbg(tunable_attr->dev, "cd_mode_id: %02x%02x%02x%02x, cv_mode_id: %#0= 8x attribute support level: %x\n", + tunable_attr->device_id, tunable_attr->feature_id, tunable_attr->cd_mode= _id, + tunable_attr->type_id, args.arg0, capdata.supported); + + return capdata.supported; + +not_supported: + dev_dbg(tunable_attr->dev, "Attribute id %x not supported\n", args.arg0); + return ret; +} + /* Lenovo WMI Other Mode Attribute macros */ #define __LWMI_ATTR_RO(_func, _name) \ { \ @@ -972,19 +1055,21 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *= priv) } =20 for (i =3D 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) { - err =3D sysfs_create_group(&priv->fw_attr_kset->kobj, - cd01_attr_groups[i].attr_group); - if (err) - goto err_remove_groups; - cd01_attr_groups[i].tunable_attr->dev =3D &priv->wdev->dev; + if (lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr) > 0) { + err =3D sysfs_create_group(&priv->fw_attr_kset->kobj, + cd01_attr_groups[i].attr_group); + if (err) + goto err_remove_groups; + } } return 0; =20 err_remove_groups: while (i--) - sysfs_remove_group(&priv->fw_attr_kset->kobj, - cd01_attr_groups[i].attr_group); + if (lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr) > 0) + sysfs_remove_group(&priv->fw_attr_kset->kobj, + cd01_attr_groups[i].attr_group); =20 kset_unregister(priv->fw_attr_kset); =20 --=20 2.52.0