From nobody Tue Apr 7 04:33:31 2026 Received: from mail-dl1-f46.google.com (mail-dl1-f46.google.com [74.125.82.46]) (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 3B01839C64C for ; Mon, 6 Apr 2026 20:14:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775506454; cv=none; b=JaUoVa9wuUF5AunPKGaJfC4D35ZxvtSaC8ZKOg6yT1YP4iAzs+rBwtD+Yi/MRhKHQceaHSx6TLslkI/FLrqtzYhyLidVwEakSGJhUZi4vI96youqSoryulEAdvqmvuhvzzcQiTlVvIQ7e0tB7opoUqa9dcIx406HKWsedYeFiog= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775506454; c=relaxed/simple; bh=G35PK13KIPRFYTqXY6BjmGuXO4n6S4cgmcfM+gl3eHg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Zb/N+ptD7FH6zQfg5Fye4+ojPMxnacaNtizVKbT2/zJflS8v8RVJc8DrMJYvv+E86scmAYuLZs+2sBYT5CwtOwrrgx0cHp7i2sUQiwTdkjncf+Wh/h+Obf5tIle+1hcFpvcrodNm239ITnJ3pce5WZ2HKoboGyLAb7BZbKqDnBc= 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=fZL/QJuG; arc=none smtp.client-ip=74.125.82.46 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="fZL/QJuG" Received: by mail-dl1-f46.google.com with SMTP id a92af1059eb24-126ea4e9694so10169799c88.1 for ; Mon, 06 Apr 2026 13:14:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1775506447; x=1776111247; 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=bjy/Htk6Ygl2OJNIllJv8e1Fqpi7jxNP7rb3Pje1SZA=; b=fZL/QJuGkRKgbt8ouSOGXDvSP6eyTbeTu6xV4igkvod63WmlYAU2x69jlOcaH6Hddr 8YHwT1t2Dd7PsJxc7kKGuoIDiu8BwXRCDaOgMl0sYrerR+4HoBRtQrpqEb6c2NlvpgnM EiawcqokjAN+hxvrFcCGYS1OO4JcUexRz9LVNfTwwAyMjSFH/6zEHRSxn5SF/obhkFAV oR4T4+WcAmlpxdVftgmajIaPVAB9oTrydfDm1et449oQrt378LX4K8a4BLnNP+eawHB9 MGgsh5NoNSkkeP3AerVu8+K25KrEVtubiwyqhB3RWICgjJPDpp5CZg18FAWlyBsdQQBM 5Dxg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775506447; x=1776111247; 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=bjy/Htk6Ygl2OJNIllJv8e1Fqpi7jxNP7rb3Pje1SZA=; b=C7bbuqkPDQa3wWHG9jwU9bRdwdSrRjIrz6k4IPqfxqqDebne9qnQUJwMaNpfl2PFfB Av3NxM/C2slYATC3aBijxYosqYjPYX4hW7xCIPlADqY9TAiah785zzDgC1thEuggRslo 3PJC3WmB0Zs2C/udSh9RuT/Q6m3JOmnce3cZ9uSW5Wwo5lzkqEhs0CDQT97eroej7omH 1gqiftgLYfd80ze4+CIwZ4Oal5Hju+xZz31D+JCGTi/9aEP4SnDdItolXbewYqEu65jo Wj2wQI1z38IyVLe6oPMdz2i9BAq4aSk0KwVXuB/4zZc991p+a0G6DhMmxa77KfynldSW D3UQ== X-Forwarded-Encrypted: i=1; AJvYcCXvyJ2O6VN8CKzUgxOehQfLSRQ6Tlm4byiVC/pWHglcy/X+CjdRJv+X0qGrPwVq2b16Dd7NAg+nJgh8Vz8=@vger.kernel.org X-Gm-Message-State: AOJu0YyQW2YEtQNrlj7DbCFmcwQHcdkpf31W+gxUx/cZ4qruReHN0Nts lA9MPhLylLrdZuysHFDpyh/ikuN2KtLgoG5i0q887jLJFTTzBENnT4VQ X-Gm-Gg: AeBDievUpgW14o3OzOVI9DROIZcSwEN9li2Vf++XaHLgy14nV9TsbC5cx6jzDgNgnjC C0pHrjpMnGZJmmskYrgkTbspqrXjjkBaYCmcqPDtENuvcpB27a0i77V64+AYRamSWaee6nIAgPX naZF3f6F/vgH4DI3TMDT1sPLH3cJU2KVCSpMsFY24x8xmQugAcKf+R4XJZrYrR/XsNaRbM4X5B1 K5LLp01vxHDQWFSVVvFqWqgHLgevtMOs2mAqOIXjS9njOTZUloiDu0q4ztDDXQhRNuEOtHoLuBp XwF/xAOyhSSG+TwuJ7OMGEZa0QeDRs5AnYEgCEexbLCPk/HHH0LeDl7i3+seSBZfOkAn4Xw3JiI Flly1Jd20xYavwyHOLnd4gxfpPMq2TYnAOKc/XdTym9OUu37ao87rsZ4164rWAMe8gzYz1z6OfV 234qIUG4oT9MmBttha1Gx767iAxiyHHJnn1ZtcQPJ5aqCjCeAk/kBLsTycb9xzoIhKsaZmWKuO/ iix X-Received: by 2002:a05:7022:90e:b0:128:d714:3ca2 with SMTP id a92af1059eb24-12bfb6eb3d8mr6404214c88.2.1775506447066; Mon, 06 Apr 2026 13:14:07 -0700 (PDT) Received: from lappy (108-228-232-20.lightspeed.sndgca.sbcglobal.net. [108.228.232.20]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-12bed93f861sm17022333c88.0.2026.04.06.13.14.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 06 Apr 2026 13:14:06 -0700 (PDT) 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, stable@vger.kernel.org Subject: [PATCH v8 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices Date: Mon, 6 Apr 2026 20:13:50 +0000 Message-ID: <20260406201400.438221-7-derekjohn.clark@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260406201400.438221-1-derekjohn.clark@gmail.com> References: <20260406201400.438221-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). - 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: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver") Reported-by: Kurt Borja Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7= XRC@gmail.com/ Cc: stable@vger.kernel.org Reviewed-by: Rong Zhang Tested-by: Rong Zhang Reviewed-by: Mark Pearson Signed-off-by: Derek J. Clark --- v7: - Move earlier in the series. This required dropping the use of lwmi_attr_id as it will be added later. - Add missing switch between cd_mode_id and cv_mode_id in current_value_store. v6: - Zero initialize args in lwmi_is_attr_01_supported. - Fix formatting. v5: - Move cv/cd_mode_id refrences from path 3/4. - Add missing import for ARRAY_SIZE. - Make lwmi_is_attr_01_supported return bool instead of u32. - Various formatting fixes. v4: - Use for loop instead of backtrace gotos for checking if an attribute is supported. - Add include for dev_printk. - Wrap dev_dbg in lwmi_is_attr_01_supported earlier. - Don't use symmetric cleanup of attributes in error states. --- drivers/platform/x86/lenovo/wmi-gamezone.h | 1 + drivers/platform/x86/lenovo/wmi-other.c | 114 ++++++++++++++++++--- 2 files changed, 98 insertions(+), 17 deletions(-) diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/= x86/lenovo/wmi-gamezone.h index 6b163a5eeb95..ddb919cf6c36 100644 --- a/drivers/platform/x86/lenovo/wmi-gamezone.h +++ b/drivers/platform/x86/lenovo/wmi-gamezone.h @@ -10,6 +10,7 @@ enum gamezone_events_type { }; =20 enum thermal_mode { + LWMI_GZ_THERMAL_MODE_NONE =3D 0x00, LWMI_GZ_THERMAL_MODE_QUIET =3D 0x01, LWMI_GZ_THERMAL_MODE_BALANCED =3D 0x02, LWMI_GZ_THERMAL_MODE_PERFORMANCE =3D 0x03, diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86= /lenovo/wmi-other.c index 0e8a69309ec4..728681766b6d 100644 --- a/drivers/platform/x86/lenovo/wmi-other.c +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -550,6 +550,8 @@ struct tunable_attr_01 { 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 { @@ -775,7 +777,6 @@ static ssize_t attr_current_value_store(struct kobject = *kobj, struct wmi_method_args_32 args =3D {}; struct capdata01 capdata; enum thermal_mode mode; - u32 attribute_id; u32 value; int ret; =20 @@ -786,13 +787,12 @@ 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 - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) | - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) | - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id); + args.arg0 =3D FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) | + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cd_mode_id) | + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, 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 @@ -803,7 +803,10 @@ 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 FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) | + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cv_mode_id) | + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id); args.arg1 =3D value; =20 ret =3D lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET, @@ -837,7 +840,6 @@ 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 =3D {}; enum thermal_mode mode; - u32 attribute_id; int retval; int ret; =20 @@ -845,13 +847,14 @@ static ssize_t attr_current_value_show(struct kobject= *kobj, if (ret) return ret; =20 - attribute_id =3D - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) | - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) | - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id); + /* 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; =20 - args.arg0 =3D attribute_id; + args.arg0 =3D FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) | + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) | + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id); =20 ret =3D lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET, (unsigned char *)&args, sizeof(args), @@ -862,6 +865,81 @@ 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: bool. + */ +static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr) +{ + u8 modes[2] =3D { LWMI_GZ_THERMAL_MODE_CUSTOM, LWMI_GZ_THERMAL_MODE_NONE = }; + struct lwmi_om_priv *priv =3D dev_get_drvdata(tunable_attr->dev); + struct wmi_method_args_32 args =3D {}; + bool cd_mode_found =3D false; + bool cv_mode_found =3D false; + struct capdata01 capdata; + int retval, ret, i; + + /* Determine tunable_attr->cd_mode_id*/ + for (i =3D 0; i < ARRAY_SIZE(modes); i++) { + args.arg0 =3D FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id)= | + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, modes[i]) | + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id); + + ret =3D lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata); + if (ret || !capdata.supported) + continue; + tunable_attr->cd_mode_id =3D modes[i]; + cd_mode_found =3D true; + break; + } + + if (!cd_mode_found) + return cd_mode_found; + + dev_dbg(tunable_attr->dev, + "cd_mode_id: %#010x\n", args.arg0); + + /* Determine tunable_attr->cv_mode_id, returns 1 if supported*/ + for (i =3D 0; i < ARRAY_SIZE(modes); i++) { + args.arg0 =3D FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id)= | + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, modes[i]) | + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, 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 || !retval) + continue; + tunable_attr->cv_mode_id =3D modes[i]; + cv_mode_found =3D true; + break; + } + + if (!cv_mode_found) + return cv_mode_found; + + dev_dbg(tunable_attr->dev, "cv_mode_id: %#010x, attribute support level: = %#010x\n", + args.arg0, capdata.supported); + + return capdata.supported > 0 ? true : false; +} + /* Lenovo WMI Other Mode Attribute macros */ #define __LWMI_ATTR_RO(_func, _name) \ { \ @@ -985,12 +1063,14 @@ static void lwmi_om_fw_attr_add(struct lwmi_om_priv = *priv) } =20 for (i =3D 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) { + cd01_attr_groups[i].tunable_attr->dev =3D &priv->wdev->dev; + if (!lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr)) + continue; + 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; } return; =20 --=20 2.53.0