From nobody Tue Dec 2 02:52:28 2025 Received: from out-183.mta1.migadu.com (out-183.mta1.migadu.com [95.215.58.183]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 867022E0B58 for ; Tue, 18 Nov 2025 00:58:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.183 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763427498; cv=none; b=D2OI1QxnapFwU7qPu6lrK+A4s/nzlcHtQHqWAL2I0ZgMNBzTe0LFVj1h99/Zw71Au/00wkQ6n517E9TFGDTO9RyWUpYcTWyBZ/4gx9DIpQGvXuvDFG2a1xBHpHnNM718u3NBQKqeHzpI9e4GLTRcyNEWcn/1rkh4T+vTIrfwVXk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763427498; c=relaxed/simple; bh=VKJDWUKr4rf+xeuei+j2+OdwvR9yeKkH1RHTprq/aM8=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=pwrD6hZwOKFA3Vy+tNqrm9k2KoGyjHUFWmcIZSv9NaQYz37MQRNdxAVq+IzPsselFk++68hJtl0ATmdagUyc9tD8zRqK2+2qQNiV4OO8EReg2UH0XHQGFkcRUgkLFjnR2Llpt7b0hIXfM4z4PmUnWfDyEcwTYspUJWu3jI1Sn18= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=sa8JlcoH; arc=none smtp.client-ip=95.215.58.183 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="sa8JlcoH" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1763427492; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=TwYLw+Es83x+aNI+ML0MmQD5GtV7lPKU8Tjfiq23IPI=; b=sa8JlcoHUpO8CXaE73HmGVffe2sCpQVAcQvKXYmHoBwGqrU2KhwR7Lei/ZUzadAR1mEyZ4 fWOjK4hm03rNpigqHm1H7TDCrTVInQtJERAfD5jR4UaBhr2Z1BUW8YcGGpNkjVyvtXGjNT eyFR9dnWnL1Cd36s3jqWDG8PH/a6INs= From: Denis Benato To: linux-kernel@vger.kernel.org Cc: platform-driver-x86@vger.kernel.org, "Hans de Goede" , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , "Limonciello, Mario" , "Luke D . Jones" , "Alok Tiwari" , "Derek John Clark" , "Mateusz Schyboll" , porfet828@gmail.com, "Denis Benato" , Denis Benato Subject: [PATCH] platform/x86: asus-armoury: make CPU cores interface readonly Date: Tue, 18 Nov 2025 01:57:48 +0100 Message-ID: <20251118005748.538726-1-denis.benato@linux.dev> 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 X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" The CPU cores interface is inconsistent between AMD and Intel systems, leading to probe failure: solve the problem taking following steps: - make the interface read-only (avoid the possibility of bricks) - do not fail if the interface returns unexpected data - show interface errors at either info or debug level Links: https://lore.kernel.org/all/20251114185337.578959-1-denis.benato@linux.dev/ https://lore.kernel.org/all/20251115145158.1172210-1-denis.benato@linux.dev/ Suggested-by: Luke D. Jones Signed-off-by: Denis Benato --- drivers/platform/x86/asus-armoury.c | 104 ++++++++++++++++++++++++---- drivers/platform/x86/asus-armoury.h | 12 +++- 2 files changed, 99 insertions(+), 17 deletions(-) diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asu= s-armoury.c index 9f67218ecd14..abbbcd62d0eb 100644 --- a/drivers/platform/x86/asus-armoury.c +++ b/drivers/platform/x86/asus-armoury.c @@ -118,11 +118,14 @@ struct asus_armoury_priv { */ struct mutex egpu_mutex; =20 +#if IS_REACHABLE(CONFIG_ASUS_ARMOURY_CPU_CORES_RW) /* * Mutex to prevent big/little core count changes writing to same * endpoint at the same time. Must lock during attr store. */ struct mutex cpu_core_mutex; +#endif /* CONFIG_ASUS_ARMOURY_CPU_CORES_RW */ + struct cpu_cores *cpu_cores; bool cpu_cores_changeable; =20 @@ -136,7 +139,9 @@ struct asus_armoury_priv { static struct asus_armoury_priv asus_armoury =3D { .egpu_mutex =3D __MUTEX_INITIALIZER(asus_armoury.egpu_mutex), =20 +#if IS_REACHABLE(CONFIG_ASUS_ARMOURY_CPU_CORES_RW) .cpu_core_mutex =3D __MUTEX_INITIALIZER(asus_armoury.cpu_core_mutex), +#endif /* CONFIG_ASUS_ARMOURY_CPU_CORES_RW */ }; =20 struct fw_attrs_group { @@ -285,6 +290,12 @@ static int armoury_set_devstate(struct kobj_attribute = *attr, return -EINVAL; } break; + case ASUS_WMI_DEVID_CORES_MAX: + /* + * CPU cores max is a read-only property on supported devices. + */ + pr_err("Refusing to write to readonly devstate of CPU cores interface\n"= ); + return -EINVAL; default: /* No known problems are known for this dev_id */ break; @@ -803,6 +814,7 @@ static struct cpu_cores *init_cpu_cores_ctrl(void) return ERR_PTR(-ENODEV); } =20 + pr_debug("CPU cores control interface max cores read 0%x.\n", cores); cores_p->max_power_cores =3D FIELD_GET(ASUS_POWER_CORE_MASK, cores); cores_p->max_perf_cores =3D FIELD_GET(ASUS_PERF_CORE_MASK, cores); =20 @@ -812,16 +824,30 @@ static struct cpu_cores *init_cpu_cores_ctrl(void) return ERR_PTR(-EIO); } =20 + pr_debug("CPU cores control interface active cores read 0%x.\n", cores); cores_p->cur_power_cores =3D FIELD_GET(ASUS_POWER_CORE_MASK, cores); cores_p->cur_perf_cores =3D FIELD_GET(ASUS_PERF_CORE_MASK, cores); =20 cores_p->min_power_cores =3D CPU_POWR_CORE_COUNT_MIN; cores_p->min_perf_cores =3D CPU_PERF_CORE_COUNT_MIN; =20 + if (cores_p->min_perf_cores > cores_p->max_perf_cores) { + pr_info("Invalid CPU performance cores count detected: min: %u, max: %u,= current: %u\n", + cores_p->min_perf_cores, + cores_p->max_perf_cores, + cores_p->cur_perf_cores + ); + return ERR_PTR(-EINVAL); + } + if ((cores_p->min_perf_cores > cores_p->max_perf_cores) || (cores_p->min_power_cores > cores_p->max_power_cores) ) { - pr_err("Invalid CPU cores count detected: interface is not safe to be us= ed.\n"); + pr_info("Invalid CPU efficiency cores count detected: min: %u, max: %u, = current: %u\n", + cores_p->min_power_cores, + cores_p->max_power_cores, + cores_p->cur_power_cores + ); return ERR_PTR(-EINVAL); } =20 @@ -836,6 +862,24 @@ static struct cpu_cores *init_cpu_cores_ctrl(void) return no_free_ptr(cores_p); } =20 +/** + * cores_value_show() - Get the core count for the specified core type. + * @kobj: The kobject associated to caller. + * @attr: The kobj_attribute associated to caller. + * @buf: The buffer that will be used to sysfs_emit. + * @core_type: The core type (performance or efficiency). + * @core_value: min, max or current count for the specified cores type. + * + * Intended usage is from sysfs attribute reading a CPU core count. + * + * This function assumes asus_armoury.cpu_cores is already initialized, + * therefore the compatibility of the interface has already been checked. + * + * Returns: + * * %-EINVAL - invalid core value type. + * * %0 - successful and buf is filled by sysfs_emit. + * * %other - error from sysfs_emit. + */ static ssize_t cores_value_show(struct kobject *kobj, struct kobj_attribut= e *attr, char *buf, enum cpu_core_type core_type, enum cpu_core_value core_value) { @@ -865,6 +909,7 @@ static ssize_t cores_value_show(struct kobject *kobj, s= truct kobj_attribute *att return sysfs_emit(buf, "%u\n", cpu_core_value); } =20 +#if IS_REACHABLE(CONFIG_ASUS_ARMOURY_CPU_CORES_RW) static ssize_t cores_current_value_store(struct kobject *kobj, struct kobj= _attribute *attr, const char *buf, enum cpu_core_type core_type) { @@ -919,6 +964,7 @@ static ssize_t cores_current_value_store(struct kobject= *kobj, struct kobj_attri =20 return 0; } +#endif /* CONFIG_ASUS_ARMOURY_CPU_CORES_RW */ =20 static ssize_t cores_performance_min_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -944,6 +990,7 @@ static ssize_t cores_performance_current_value_show(str= uct kobject *kobj, return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_CURRENT); } =20 +#if IS_REACHABLE(CONFIG_ASUS_ARMOURY_CPU_CORES_RW) static ssize_t cores_performance_current_value_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) @@ -956,8 +1003,9 @@ static ssize_t cores_performance_current_value_store(s= truct kobject *kobj, =20 return count; } -ASUS_ATTR_GROUP_CORES_RW(cores_performance, "cores_performance", - "Set the max available performance cores"); +#endif /* CONFIG_ASUS_ARMOURY_CPU_CORES_RW */ +ASUS_ATTR_GROUP_CORES(cores_performance, "cores_performance", + "Get available performance cores"); =20 /* Define helper to access the current power mode tunable values */ static inline struct rog_tunables *get_current_tunables(void) @@ -992,6 +1040,7 @@ static ssize_t cores_efficiency_current_value_show(str= uct kobject *kobj, return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_CURRENT= ); } =20 +#if IS_REACHABLE(CONFIG_ASUS_ARMOURY_CPU_CORES_RW) static ssize_t cores_efficiency_current_value_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) @@ -1004,8 +1053,9 @@ static ssize_t cores_efficiency_current_value_store(s= truct kobject *kobj, =20 return count; } -ASUS_ATTR_GROUP_CORES_RW(cores_efficiency, "cores_efficiency", - "Set the max available efficiency cores"); +#endif /* CONFIG_ASUS_ARMOURY_CPU_CORES_RW */ +ASUS_ATTR_GROUP_CORES(cores_efficiency, "cores_efficiency", + "Get available efficiency cores"); =20 /* Simple attribute creation */ ASUS_ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHA= RGE_MODE, "0;1;2\n", @@ -1048,8 +1098,6 @@ static const struct asus_attr_group armoury_attr_grou= ps[] =3D { { &egpu_enable_attr_group, ASUS_WMI_DEVID_EGPU }, { &dgpu_disable_attr_group, ASUS_WMI_DEVID_DGPU }, { &apu_mem_attr_group, ASUS_WMI_DEVID_APU_MEM }, - { &cores_efficiency_attr_group, ASUS_WMI_DEVID_CORES_MAX }, - { &cores_performance_attr_group, ASUS_WMI_DEVID_CORES_MAX }, =20 { &ppt_pl1_spl_attr_group, ASUS_WMI_DEVID_PPT_PL1_SPL }, { &ppt_pl2_sppt_attr_group, ASUS_WMI_DEVID_PPT_PL2_SPPT }, @@ -1191,6 +1239,22 @@ static int asus_fw_attr_add(void) } } =20 + if (asus_armoury.cpu_cores !=3D NULL) { + err =3D sysfs_create_group(&asus_armoury.fw_attr_kset->kobj, + &cores_efficiency_attr_group); + if (err) { + pr_err("Failed to create sysfs-group for cpu efficiency cores: %d\n", e= rr); + goto err_remove_cores_efficiency_group; + } + + err =3D sysfs_create_group(&asus_armoury.fw_attr_kset->kobj, + &cores_performance_attr_group); + if (err) { + pr_err("Failed to create sysfs-group for cpu performance cores: %d\n", = err); + goto err_remove_cores_performance_group; + } + } + for (i =3D 0; i < ARRAY_SIZE(armoury_attr_groups); i++) { if (!armoury_has_devstate(armoury_attr_groups[i].wmi_devid)) continue; @@ -1230,6 +1294,12 @@ static int asus_fw_attr_add(void) } if (asus_armoury.gpu_mux_dev_id) sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &gpu_mux_mode_attr_= group); +err_remove_cores_performance_group: + if (asus_armoury.cpu_cores !=3D NULL) + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &cores_performance_= attr_group); +err_remove_cores_efficiency_group: + if (asus_armoury.cpu_cores !=3D NULL) + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &cores_efficiency_a= ttr_group); err_remove_mini_led_group: if (asus_armoury.mini_led_dev_id) sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr= _group); @@ -1375,7 +1445,6 @@ static int __init asus_fw_init(void) { char *wmi_uid; struct cpu_cores *cpu_cores_ctrl; - int err; =20 wmi_uid =3D wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID); if (!wmi_uid) @@ -1389,16 +1458,14 @@ static int __init asus_fw_init(void) return -ENODEV; =20 asus_armoury.cpu_cores_changeable =3D false; + asus_armoury.cpu_cores =3D NULL; if (armoury_has_devstate(ASUS_WMI_DEVID_CORES_MAX)) { cpu_cores_ctrl =3D init_cpu_cores_ctrl(); - if (IS_ERR(cpu_cores_ctrl)) { - err =3D PTR_ERR(cpu_cores_ctrl); - pr_err("Could not initialise CPU core control: %d\n", err); - return err; + if (!IS_ERR(cpu_cores_ctrl)) { + pr_debug("CPU cores control available.\n"); + asus_armoury.cpu_cores =3D cpu_cores_ctrl; + asus_armoury.cpu_cores_changeable =3D true; } - - asus_armoury.cpu_cores =3D cpu_cores_ctrl; - asus_armoury.cpu_cores_changeable =3D true; } =20 init_rog_tunables(); @@ -1417,6 +1484,13 @@ static void __exit asus_fw_exit(void) armoury_attr_groups[i].attr_group); } =20 + if (asus_armoury.cpu_cores !=3D NULL) { + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, + &cores_performance_attr_group); + sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, + &cores_efficiency_attr_group); + } + if (asus_armoury.gpu_mux_dev_id) sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &gpu_mux_mode_attr_= group); =20 diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asu= s-armoury.h index 2f05a2e0cab3..6b2bfe763d23 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -198,12 +198,20 @@ ssize_t armoury_attr_uint_show(struct kobject *kobj, = struct kobj_attribute *attr .name =3D _fsname, .attrs =3D _attrname##_attrs \ } =20 +#if IS_REACHABLE(CONFIG_ASUS_ARMOURY_CPU_CORES_RW) + #define __ASUS_ATTR_CPU_CORES(_attrname, __attrval) \ + __ASUS_ATTR_RW(_attrname, __attrval) +#else + #define __ASUS_ATTR_CPU_CORES(_attrname, __attrval) \ + __ASUS_ATTR_RO(_attrname, __attrval) +#endif /* CONFIG_ASUS_ARMOURY_CPU_CORES_RW */ + /* CPU core attributes need a little different in setup */ -#define ASUS_ATTR_GROUP_CORES_RW(_attrname, _fsname, _dispname) \ +#define ASUS_ATTR_GROUP_CORES(_attrname, _fsname, _dispname) \ __ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", 1); \ __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ static struct kobj_attribute attr_##_attrname##_current_value =3D \ - __ASUS_ATTR_RW(_attrname, current_value); \ + __ASUS_ATTR_CPU_CORES(_attrname, current_value); \ static struct kobj_attribute attr_##_attrname##_default_value =3D \ __ASUS_ATTR_RO(_attrname, default_value); \ static struct kobj_attribute attr_##_attrname##_min_value =3D \ --=20 2.51.2