From nobody Mon Dec 29 20:12:48 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 73C4FC61D85 for ; Thu, 23 Nov 2023 05:49:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344706AbjKWFtl (ORCPT ); Thu, 23 Nov 2023 00:49:41 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54052 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229994AbjKWFtg (ORCPT ); Thu, 23 Nov 2023 00:49:36 -0500 Received: from so254-32.mailgun.net (so254-32.mailgun.net [198.61.254.32]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4CD18191 for ; Wed, 22 Nov 2023 21:49:41 -0800 (PST) DKIM-Signature: a=rsa-sha256; v=1; c=relaxed/relaxed; d=equiv.tech; q=dns/txt; s=mx; t=1700718579; x=1700725779; h=Content-Transfer-Encoding: Content-Type: MIME-Version: Message-Id: Date: Subject: Subject: Cc: To: To: From: From: Sender: Sender; bh=I8wBwaBN4IEu0DBz273a5ua+d1ZYV86s0JP4ZasTt0o=; b=ro/tmnjkQxJ3xy3n//Hrut05lvc06FjHPNBSGzBOAmILUYbdlAlFQi8G1jPxP/wjqY/BnqkvxquzohO7LPLTrhIoUHs1EtvtTbNdkczT2qTW4uJPCs8QrVDjQtM9Cv/PUZ6bxZBTWO8pXq59wXtEpxLkbQ2CdAGsGzgtxBQX2uYctF4pULopOmb7gW20ctb9etk/12YIfuJfAy4ehMz+txwM74HifmglZgnYeOTMcjp07BThsRPBxCXDUzDWy9tAw1eY262jO9dRmq6rsu6kW8hTYT/Z+SQfFCrjv2f9nHfmnIVccG8u/FqYD8c2nsM9g6yP57VrgBVHXpPKHQmpAg== X-Mailgun-Sending-Ip: 198.61.254.32 X-Mailgun-Sid: WyI4ZWI3MiIsImxpbnV4LWtlcm5lbEB2Z2VyLmtlcm5lbC5vcmciLCI5M2Q1YWIiXQ== Received: from mail.equiv.tech (equiv.tech [142.93.28.83]) by 9691b5986cf9 with SMTP id 655ee7f3338167855b81ccf3 (version=TLS1.2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256); Thu, 23 Nov 2023 05:49:39 GMT Sender: james@equiv.tech From: James Seo To: Jean Delvare , Guenter Roeck Cc: James Seo , Lukasz Stelmach , Armin Wolf , linux-hwmon@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC v3] hwmon: (hp-wmi-sensors) Fix failure to load on EliteDesk 800 G6 Date: Wed, 22 Nov 2023 21:49:19 -0800 Message-Id: <20231123054918.157098-1-james@equiv.tech> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The EliteDesk 800 G6 stores a raw WMI string within the ACPI object in its BIOS corresponding to one instance of HPBIOS_PlatformEvents.Name. This is evidently a valid way of representing a WMI data item as far as the Microsoft ACPI-WMI mapper is concerned, but is preventing the driver from loading. This seems quite rare, but add support for such strings. Treating this as a quirk pretty much means adding that support anyway. Also clean up an oversight in update_numeric_sensor_from_wobj() in which the result of hp_wmi_strdup() was being used without error checking. Reported-by: Lukasz Stelmach Closes: https://lore.kernel.org/linux-hwmon/7850a0bd-60e7-88f8-1d6c-0bb0e32= 34fdc@roeck-us.net/ Tested-by: Lukasz Stelmach Signed-off-by: James Seo --- Changes v2->v3: * Fix checkpatch warnings - Line break commit message differently (no non-whitespace changes) - Use "unsigned int" instead of "unsigned" * Add Tested-by: from Lukasz Stelmach Changes v1->v2: * Remove DMI-based workaround logic * Add full support for raw WMI strings - Improve UTF-16 validation and conversion for the general case - Support such strings if they occur in HPBIOS_BIOSEvent objects * Only use the result of hp_wmi_strdup() in update_numeric_sensor_from_wobj= () if the call succeeded History: v2: https://lore.kernel.org/linux-hwmon/20231105192054.24833-1-james@equiv.= tech/ v1: https://lore.kernel.org/linux-hwmon/20231103181931.677796-1-james@equiv= .tech/ --- drivers/hwmon/hp-wmi-sensors.c | 127 ++++++++++++++++++++++++++++----- 1 file changed, 111 insertions(+), 16 deletions(-) diff --git a/drivers/hwmon/hp-wmi-sensors.c b/drivers/hwmon/hp-wmi-sensors.c index 17ae62f88bbf..b5325d0e72b9 100644 --- a/drivers/hwmon/hp-wmi-sensors.c +++ b/drivers/hwmon/hp-wmi-sensors.c @@ -17,6 +17,8 @@ * Available: https://github.com/linuxhw/ACPI * [4] P. Roh=C3=A1r, "bmfdec - Decompile binary MOF file (BMF) from WMI b= uffer", * 2017. [Online]. Available: https://github.com/pali/bmfdec + * [5] Microsoft Corporation, "Driver-Defined WMI Data Items", 2017. [Onli= ne]. + * Available: https://learn.microsoft.com/en-us/windows-hardware/drive= rs/kernel/driver-defined-wmi-data-items */ =20 #include @@ -24,6 +26,7 @@ #include #include #include +#include #include #include =20 @@ -395,6 +398,50 @@ struct hp_wmi_sensors { struct mutex lock; /* Lock polling WMI and driver state changes. */ }; =20 +static bool is_raw_wmi_string(const u8 *pointer, u32 length) +{ + const u16 *ptr; + u16 len; + + /* WMI strings are length-prefixed UTF-16 [5]. */ + if (length <=3D sizeof(*ptr)) + return false; + + length -=3D sizeof(*ptr); + ptr =3D (const u16 *)pointer; + len =3D *ptr; + + return len <=3D length && !(len & 1); +} + +static char *convert_raw_wmi_string(const u8 *buf) +{ + const wchar_t *src; + unsigned int cps; + unsigned int len; + char *dst; + int i; + + src =3D (const wchar_t *)buf; + + /* Count UTF-16 code points. Exclude trailing null padding. */ + cps =3D *src / sizeof(*src); + while (cps && !src[cps]) + cps--; + + /* Each code point becomes up to 3 UTF-8 characters. */ + len =3D min(cps * 3, HP_WMI_MAX_STR_SIZE - 1); + + dst =3D kmalloc((len + 1) * sizeof(*dst), GFP_KERNEL); + if (!dst) + return NULL; + + i =3D utf16s_to_utf8s(++src, cps, UTF16_LITTLE_ENDIAN, dst, len); + dst[i] =3D '\0'; + + return dst; +} + /* hp_wmi_strdup - devm_kstrdup, but length-limited */ static char *hp_wmi_strdup(struct device *dev, const char *src) { @@ -412,6 +459,23 @@ static char *hp_wmi_strdup(struct device *dev, const c= har *src) return dst; } =20 +/* hp_wmi_wstrdup - hp_wmi_strdup, but for a raw WMI string */ +static char *hp_wmi_wstrdup(struct device *dev, const u8 *buf) +{ + char *src; + char *dst; + + src =3D convert_raw_wmi_string(buf); + if (!src) + return NULL; + + dst =3D hp_wmi_strdup(dev, strim(src)); /* Note: Copy is trimmed. */ + + kfree(src); + + return dst; +} + /* * hp_wmi_get_wobj - poll WMI for a WMI object instance * @guid: WMI object GUID @@ -462,8 +526,14 @@ static int check_wobj(const union acpi_object *wobj, for (prop =3D 0; prop <=3D last_prop; prop++) { type =3D elements[prop].type; valid_type =3D property_map[prop]; - if (type !=3D valid_type) + if (type !=3D valid_type) { + if (type =3D=3D ACPI_TYPE_BUFFER && + valid_type =3D=3D ACPI_TYPE_STRING && + is_raw_wmi_string(elements[prop].buffer.pointer, + elements[prop].buffer.length)) + continue; return -EINVAL; + } } =20 return 0; @@ -480,7 +550,9 @@ static int extract_acpi_value(struct device *dev, break; =20 case ACPI_TYPE_STRING: - *out_string =3D hp_wmi_strdup(dev, strim(element->string.pointer)); + *out_string =3D element->type =3D=3D ACPI_TYPE_BUFFER ? + hp_wmi_wstrdup(dev, element->buffer.pointer) : + hp_wmi_strdup(dev, strim(element->string.pointer)); if (!*out_string) return -ENOMEM; break; @@ -861,7 +933,9 @@ update_numeric_sensor_from_wobj(struct device *dev, { const union acpi_object *elements; const union acpi_object *element; - const char *string; + const char *new_string; + char *trimmed; + char *string; bool is_new; int offset; u8 size; @@ -885,11 +959,21 @@ update_numeric_sensor_from_wobj(struct device *dev, offset =3D is_new ? size - 1 : -2; =20 element =3D &elements[HP_WMI_PROPERTY_CURRENT_STATE + offset]; - string =3D strim(element->string.pointer); - - if (strcmp(string, nsensor->current_state)) { - devm_kfree(dev, nsensor->current_state); - nsensor->current_state =3D hp_wmi_strdup(dev, string); + string =3D element->type =3D=3D ACPI_TYPE_BUFFER ? + convert_raw_wmi_string(element->buffer.pointer) : + element->string.pointer; + + if (string) { + trimmed =3D strim(string); + if (strcmp(trimmed, nsensor->current_state)) { + new_string =3D hp_wmi_strdup(dev, trimmed); + if (new_string) { + devm_kfree(dev, nsensor->current_state); + nsensor->current_state =3D new_string; + } + } + if (element->type =3D=3D ACPI_TYPE_BUFFER) + kfree(string); } =20 /* Old variant: -2 (not -1) because it lacks the Size property. */ @@ -996,11 +1080,15 @@ static int check_event_wobj(const union acpi_object = *wobj) HP_WMI_EVENT_PROPERTY_STATUS); } =20 -static int populate_event_from_wobj(struct hp_wmi_event *event, +static int populate_event_from_wobj(struct device *dev, + struct hp_wmi_event *event, union acpi_object *wobj) { int prop =3D HP_WMI_EVENT_PROPERTY_NAME; union acpi_object *element; + acpi_object_type type; + char *string; + u32 value; int err; =20 err =3D check_event_wobj(wobj); @@ -1009,20 +1097,24 @@ static int populate_event_from_wobj(struct hp_wmi_e= vent *event, =20 element =3D wobj->package.elements; =20 - /* Extracted strings are NOT device-managed copies. */ - for (; prop <=3D HP_WMI_EVENT_PROPERTY_CATEGORY; prop++, element++) { + type =3D hp_wmi_event_property_map[prop]; + + err =3D extract_acpi_value(dev, element, type, &value, &string); + if (err) + return err; + switch (prop) { case HP_WMI_EVENT_PROPERTY_NAME: - event->name =3D strim(element->string.pointer); + event->name =3D string; break; =20 case HP_WMI_EVENT_PROPERTY_DESCRIPTION: - event->description =3D strim(element->string.pointer); + event->description =3D string; break; =20 case HP_WMI_EVENT_PROPERTY_CATEGORY: - event->category =3D element->integer.value; + event->category =3D value; break; =20 default: @@ -1511,8 +1603,8 @@ static void hp_wmi_notify(u32 value, void *context) struct acpi_buffer out =3D { ACPI_ALLOCATE_BUFFER, NULL }; struct hp_wmi_sensors *state =3D context; struct device *dev =3D &state->wdev->dev; + struct hp_wmi_event event =3D {}; struct hp_wmi_info *fan_info; - struct hp_wmi_event event; union acpi_object *wobj; acpi_status err; int event_type; @@ -1546,7 +1638,7 @@ static void hp_wmi_notify(u32 value, void *context) =20 wobj =3D out.pointer; =20 - err =3D populate_event_from_wobj(&event, wobj); + err =3D populate_event_from_wobj(dev, &event, wobj); if (err) { dev_warn(dev, "Bad event data (ACPI type %d)\n", wobj->type); goto out_free_wobj; @@ -1577,6 +1669,9 @@ static void hp_wmi_notify(u32 value, void *context) out_free_wobj: kfree(wobj); =20 + devm_kfree(dev, event.name); + devm_kfree(dev, event.description); + out_unlock: mutex_unlock(&state->lock); } base-commit: c03cd01890c5e41138cc5709e37859bc917aab5d --=20 2.40.1