From 9e409f1dff7841634e4b101111d6427f979c0aac Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Sun, 9 Aug 2026 07:43:55 +0800 Subject: [PATCH 1/5] ACPI: battery: Protect all properties with a separated mutex The acpi_battery_get_property() callback calls acpi_battery_get_state() without any lock held. On some devices, it happens that the property cache has expired before a uevent reaches userspace, triggering simultaneous attempts to evaluate _BST. See [1] for an analysis to sysrq stacktraces on one of the these devices. In a few cases, including when the AML is sleeping or acquiring a mutex, ACPICA drops the namespace and interpreter locks and allows the evaluation of _BST to start while another task is still evaluating it. This could somehow confuse the interpreter and lead to chaos in AML mutexes on some devices, see [2] for an example. Not holding the lock is also prone to race conditions, for example: CPU0 | CPU1 acpi_battery_get_property() | acpi_battery_get_state() | [update_time expired] | extract_package() | acpi_battery_get_property() battery->update_time = jiffies | acpi_battery_get_state() kfree() | [up to date] | [read capacity_now] [fix capacity_now due to quirk] | where CPU1 gets raw capacity_now before CPU0 fixes it to a meaningful value. The existing mutex update_lock is not applicapable for acpi_battery_get_property(), as some code path could call or wait for acpi_battery_get_property() while holding update_lock. Therefore, introduce a mutex called property_lock to protect all accesses to battery properties, so that acpi_battery_get_property() can take the advantage of the mutex and synchronize itself. With the mutex, acpi_battery_get_state() are synchronized in all code paths calling it, and its cache mechanism can always clamp the frequency of _BST evaluations according to cache_time. The helper function acpi_battery_handle_discharging() for quirky devices has to be inlined due to the change, as the mutex must be unlocked before calling the expensive power_supply_is_system_supplied() helper function. Fixes: 86bfd21a0baf ("ACPI: battery: Drop redundant locking") Reported-by: Rick Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221065#c85 [1] Reported-by: Avraham Hollander Tested-by: Avraham Hollander Closes: https://lore.kernel.org/linux-acpi/CAP1mzZReJCn6df5DwEPu-JCQUyr=Pu1cg5xKCMttWZkHCQtVmQ@mail.gmail.com [2] Signed-off-by: Rong Zhang Cc: All applicable Link: https://patch.msgid.link/20260809-b4-acpi-battery-notification-v5-1-788d54fa2e35@rong.moe Signed-off-by: Rafael J. Wysocki --- drivers/acpi/battery.c | 147 ++++++++++++++++++++++++++++------------- 1 file changed, 101 insertions(+), 46 deletions(-) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 0084f308b790..670853ec3a4d 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -105,6 +106,9 @@ struct acpi_battery { struct delayed_work acpi_notif_dwork; struct notifier_block pm_nb; struct list_head list; + unsigned long flags; + + struct mutex property_lock; /* Protects properties below. */ unsigned long update_time; int revision; int rate_now; @@ -131,7 +135,6 @@ struct acpi_battery { char oem_info[MAX_STRING_LENGTH]; int state; int power_unit; - unsigned long flags; }; #define to_acpi_battery(x) power_supply_get_drvdata(x) @@ -189,20 +192,6 @@ static bool acpi_battery_is_degraded(struct acpi_battery *battery) battery->full_charge_capacity < battery->design_capacity; } -static int acpi_battery_handle_discharging(struct acpi_battery *battery) -{ - /* - * Some devices wrongly report discharging if the battery's charge level - * was above the device's start charging threshold atm the AC adapter - * was plugged in and the device thus did not start a new charge cycle. - */ - if ((battery_ac_is_broken || power_supply_is_system_supplied()) && - battery->rate_now == 0) - return POWER_SUPPLY_STATUS_NOT_CHARGING; - - return POWER_SUPPLY_STATUS_DISCHARGING; -} - static int acpi_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -210,15 +199,41 @@ static int acpi_battery_get_property(struct power_supply *psy, int full_capacity = ACPI_BATTERY_VALUE_UNKNOWN, ret = 0; struct acpi_battery *battery = to_acpi_battery(psy); - if (acpi_battery_present(battery)) { - /* run battery update only if it is present */ - acpi_battery_get_state(battery); - } else if (psp != POWER_SUPPLY_PROP_PRESENT) - return -ENODEV; + /* run battery update only if it is present */ + if (!acpi_battery_present(battery)) { + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 0; + return 0; + default: + return -ENODEV; + } + } + + mutex_lock(&battery->property_lock); + + acpi_battery_get_state(battery); + switch (psp) { case POWER_SUPPLY_PROP_STATUS: + /* + * Some devices wrongly report discharging if the battery's charge level + * was above the device's start charging threshold atm the AC adapter + * was plugged in and the device thus did not start a new charge cycle. + */ if (battery->state & ACPI_BATTERY_STATE_DISCHARGING) - val->intval = acpi_battery_handle_discharging(battery); + if (battery->rate_now != 0) { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + } else if (battery_ac_is_broken) { + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + mutex_unlock(&battery->property_lock); + + val->intval = power_supply_is_system_supplied() + ? POWER_SUPPLY_STATUS_NOT_CHARGING + : POWER_SUPPLY_STATUS_DISCHARGING; + return 0; + } else if (battery->state & ACPI_BATTERY_STATE_CHARGING) /* Check the rate and capacity to validate the status. */ if (!acpi_battery_is_full(battery) || @@ -321,6 +336,8 @@ static int acpi_battery_get_property(struct power_supply *psy, default: ret = -EINVAL; } + + mutex_unlock(&battery->property_lock); return ret; } @@ -556,6 +573,8 @@ static int acpi_battery_get_info(struct acpi_battery *battery) int use_bix; int result = -ENODEV; + lockdep_assert_held(&battery->property_lock); + if (!acpi_battery_present(battery)) return 0; @@ -595,6 +614,8 @@ static int acpi_battery_get_state(struct acpi_battery *battery) acpi_status status = 0; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + lockdep_assert_held(&battery->property_lock); + if (!acpi_battery_present(battery)) return 0; @@ -648,6 +669,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery) { acpi_status status = 0; + lockdep_assert_held(&battery->property_lock); + if (!acpi_battery_present(battery) || !test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags)) return -ENODEV; @@ -665,6 +688,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery) static int acpi_battery_init_alarm(struct acpi_battery *battery) { + lockdep_assert_held(&battery->property_lock); + /* See if alarms are supported, and if so, set default */ if (!acpi_has_method(battery->device->handle, "_BTP")) { clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags); @@ -682,6 +707,8 @@ static ssize_t acpi_battery_alarm_show(struct device *dev, { struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev)); + guard(mutex)(&battery->property_lock); + return sysfs_emit(buf, "%d\n", battery->alarm * 1000); } @@ -697,6 +724,8 @@ static ssize_t acpi_battery_alarm_store(struct device *dev, if (err) return err; + guard(mutex)(&battery->property_lock); + battery->alarm = x / 1000; if (acpi_battery_present(battery)) acpi_battery_set_alarm(battery); @@ -881,12 +910,17 @@ static int sysfs_add_battery(struct acpi_battery *battery) .no_wakeup_source = true, }; bool full_cap_broken = false; + int power_unit; - if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) && - !ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity)) - full_cap_broken = true; + scoped_guard(mutex, &battery->property_lock) { + power_unit = battery->power_unit; - if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) { + if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) && + !ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity)) + full_cap_broken = true; + } + + if (power_unit == ACPI_BATTERY_POWER_UNIT_MA) { if (full_cap_broken) { battery->bat_desc.properties = charge_battery_full_cap_broken_props; @@ -940,6 +974,9 @@ static void sysfs_remove_battery(struct acpi_battery *battery) static void find_battery(const struct dmi_header *dm, void *private) { struct acpi_battery *battery = (struct acpi_battery *)private; + + lockdep_assert_held(&battery->property_lock); + /* Note: the hardcoded offsets below have been extracted from * the source code of dmidecode. */ @@ -971,6 +1008,8 @@ static void find_battery(const struct dmi_header *dm, void *private) */ static void acpi_battery_quirks(struct acpi_battery *battery) { + lockdep_assert_held(&battery->property_lock); + if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)) return; @@ -1023,30 +1062,38 @@ static void acpi_battery_quirks(struct acpi_battery *battery) static int acpi_battery_update(struct acpi_battery *battery, bool resume) { int result = acpi_battery_get_status(battery); + bool wakeup; if (result) return result; if (!acpi_battery_present(battery)) { sysfs_remove_battery(battery); - battery->update_time = 0; + scoped_guard(mutex, &battery->property_lock) + battery->update_time = 0; return 0; } if (resume) return 0; - if (!battery->update_time) { - result = acpi_battery_get_info(battery); + scoped_guard(mutex, &battery->property_lock) { + if (!battery->update_time) { + result = acpi_battery_get_info(battery); + if (result) + return result; + acpi_battery_init_alarm(battery); + } + + result = acpi_battery_get_state(battery); if (result) return result; - acpi_battery_init_alarm(battery); - } + acpi_battery_quirks(battery); - result = acpi_battery_get_state(battery); - if (result) - return result; - acpi_battery_quirks(battery); + wakeup = ((battery->state & ACPI_BATTERY_STATE_CRITICAL) || + (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) && + (battery->capacity_now <= battery->alarm))); + } if (!battery->bat) { result = sysfs_add_battery(battery); @@ -1058,9 +1105,7 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume) * Wakeup the system if battery is critical low * or lower than the alarm level */ - if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) || - (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) && - (battery->capacity_now <= battery->alarm))) + if (wakeup) acpi_pm_wakeup_event(battery->phys_dev); return result; @@ -1073,12 +1118,14 @@ static void acpi_battery_refresh(struct acpi_battery *battery) if (!battery->bat) return; - power_unit = battery->power_unit; + scoped_guard(mutex, &battery->property_lock) { + power_unit = battery->power_unit; - acpi_battery_get_info(battery); + acpi_battery_get_info(battery); - if (power_unit == battery->power_unit) - return; + if (power_unit == battery->power_unit) + return; + } /* The battery has changed its reporting units. */ sysfs_remove_battery(battery); @@ -1170,17 +1217,21 @@ static int battery_notify(struct notifier_block *nb, } else { int result; - result = acpi_battery_get_info(battery); - if (result) - return result; + scoped_guard(mutex, &battery->property_lock) { + result = acpi_battery_get_info(battery); + if (result) + return result; + } result = sysfs_add_battery(battery); if (result) return result; } - acpi_battery_init_alarm(battery); - acpi_battery_get_state(battery); + scoped_guard(mutex, &battery->property_lock) { + acpi_battery_init_alarm(battery); + acpi_battery_get_state(battery); + } } return 0; @@ -1345,6 +1396,10 @@ static int acpi_battery_probe(struct platform_device *pdev) if (result) return result; + result = devm_mutex_init(&pdev->dev, &battery->property_lock); + if (result) + return result; + if (acpi_has_method(battery->device->handle, "_BIX")) set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags); From 4226911d72960b24ec427c61d795916e027f528e Mon Sep 17 00:00:00 2001 From: Hongnan Li Date: Thu, 13 Aug 2026 14:30:04 +0800 Subject: [PATCH 2/5] ACPI: APD: Convert fixed clock rates to use HZ_PER_MHZ Use HZ_PER_MHZ multiplier for fixed_clk_rate values to improve readability. Signed-off-by: Hongnan Li Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260813063005.42925-1-clarke.li@hj-micro.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_apd.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index 008bd0552cb7..275027ebd01f 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "internal.h" @@ -110,17 +111,17 @@ static int fch_misc_setup(struct apd_private_data *pdata) static const struct apd_device_desc cz_i2c_desc = { .setup = acpi_apd_setup, - .fixed_clk_rate = 133000000, + .fixed_clk_rate = 133 * HZ_PER_MHZ, }; static const struct apd_device_desc wt_i2c_desc = { .setup = acpi_apd_setup, - .fixed_clk_rate = 150000000, + .fixed_clk_rate = 150 * HZ_PER_MHZ, }; static const struct apd_device_desc wt_i3c_desc = { .setup = acpi_apd_setup, - .fixed_clk_rate = 125000000, + .fixed_clk_rate = 125 * HZ_PER_MHZ, }; static struct property_entry uart_properties[] = { @@ -132,7 +133,7 @@ static struct property_entry uart_properties[] = { static const struct apd_device_desc cz_uart_desc = { .setup = acpi_apd_setup, - .fixed_clk_rate = 48000000, + .fixed_clk_rate = 48 * HZ_PER_MHZ, .properties = uart_properties, }; @@ -144,52 +145,52 @@ static const struct apd_device_desc fch_misc_desc = { #ifdef CONFIG_ARM64 static const struct apd_device_desc xgene_i2c_desc = { .setup = acpi_apd_setup, - .fixed_clk_rate = 100000000, + .fixed_clk_rate = 100 * HZ_PER_MHZ, }; static const struct apd_device_desc vulcan_spi_desc = { .setup = acpi_apd_setup, - .fixed_clk_rate = 133000000, + .fixed_clk_rate = 133 * HZ_PER_MHZ, }; static const struct apd_device_desc hip07_i2c_desc = { .setup = acpi_apd_setup, - .fixed_clk_rate = 200000000, + .fixed_clk_rate = 200 * HZ_PER_MHZ, }; static const struct apd_device_desc hip08_i2c_desc = { .setup = acpi_apd_setup, - .fixed_clk_rate = 250000000, + .fixed_clk_rate = 250 * HZ_PER_MHZ, }; static const struct apd_device_desc hip08_lite_i2c_desc = { .setup = acpi_apd_setup, - .fixed_clk_rate = 125000000, + .fixed_clk_rate = 125 * HZ_PER_MHZ, }; static const struct apd_device_desc thunderx2_i2c_desc = { .setup = acpi_apd_setup, - .fixed_clk_rate = 125000000, + .fixed_clk_rate = 125 * HZ_PER_MHZ, }; static const struct apd_device_desc nxp_i2c_desc = { .setup = acpi_apd_setup, - .fixed_clk_rate = 350000000, + .fixed_clk_rate = 350 * HZ_PER_MHZ, }; static const struct apd_device_desc hip08_spi_desc = { .setup = acpi_apd_setup, - .fixed_clk_rate = 250000000, + .fixed_clk_rate = 250 * HZ_PER_MHZ, }; static const struct apd_device_desc leca_spi_desc = { .setup = acpi_apd_setup, - .fixed_clk_rate = 400000000, + .fixed_clk_rate = 400 * HZ_PER_MHZ, }; static const struct apd_device_desc leca_i2c_desc = { .setup = acpi_apd_setup, - .fixed_clk_rate = 250000000, + .fixed_clk_rate = 250 * HZ_PER_MHZ, }; #endif /* CONFIG_ARM64 */ From 6d2d4627485595c3d09c77eeee94972abf362e8b Mon Sep 17 00:00:00 2001 From: Xiangyang Yu Date: Thu, 13 Aug 2026 14:40:25 +0800 Subject: [PATCH 3/5] ACPI: APD: Add clock frequency for HJMC01 I2C controller I2C clock frequency for HJMC01 is 200MHz, define a new ACPI HID for it. Signed-off-by: Xiangyang Yu Signed-off-by: Hongnan Li Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260813064025.45242-1-clarke.li@hj-micro.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_apd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index 275027ebd01f..e7366fcb76ee 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -193,6 +193,11 @@ static const struct apd_device_desc leca_i2c_desc = { .fixed_clk_rate = 250 * HZ_PER_MHZ, }; +static const struct apd_device_desc hjmc_i2c_desc = { + .setup = acpi_apd_setup, + .fixed_clk_rate = 200 * HZ_PER_MHZ, +}; + #endif /* CONFIG_ARM64 */ #endif @@ -263,6 +268,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = { { "HISI02A2", APD_ADDR(hip08_i2c_desc) }, { "HISI02A3", APD_ADDR(hip08_lite_i2c_desc) }, { "HISI0173", APD_ADDR(hip08_spi_desc) }, + { "HJMC3001", APD_ADDR(hjmc_i2c_desc) }, { "LECA0002", APD_ADDR(leca_spi_desc) }, { "LECA0003", APD_ADDR(leca_i2c_desc) }, { "NXP0001", APD_ADDR(nxp_i2c_desc) }, From ced45be0073a8a31b30b4a7f68cd3a15734515de Mon Sep 17 00:00:00 2001 From: Anirudh Prasad Date: Sat, 15 Aug 2026 01:36:23 +0530 Subject: [PATCH 4/5] ACPI: pfr_update: fix stack buffer overflow in query_capability() query_capability() copies four ACPI buffer objects returned by the firmware _DSM into fixed-size u8[16] fields in struct pfru_update_cap_info using memcpy with the firmware-supplied length: memcpy(&cap_hdr->code_type, elements[CAP_CODE_TYPE_IDX].buffer.pointer, elements[CAP_CODE_TYPE_IDX].buffer.length); The same pattern repeats for drv_type, platform_id, and oem_id. If the firmware returns buffer.length > 16 for any of these fields, memcpy writes past the destination array. struct pfru_update_cap_info is stack-allocated in pfru_ioctl(). Confirmed with KASAN on 7.2-rc6: three stack-out-of-bounds reports are generated when a DSM returns 64-byte buffers, with writes reaching 44 bytes past the end of cap_hdr's [64, 156) frame window into adjacent stack redzones. Introduce a helper pointer to out_obj->package.elements and use it to validate each buffer length against its destination field size before copying, returning -EINVAL if the firmware supplies an oversized buffer. Fixes: 0db89fa243e5 ("ACPI: Introduce Platform Firmware Runtime Update device driver") Cc: All applicable Signed-off-by: Anirudh Prasad Link: https://patch.msgid.link/1a001e1fee9.637da6dc3533246.238498880682901704@a0rg.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pfr_update.c | 45 +++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c index 6283105bb0e8..9afd2c52fdbd 100644 --- a/drivers/acpi/pfr_update.c +++ b/drivers/acpi/pfr_update.c @@ -120,7 +120,7 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr, struct pfru_device *pfru_dev) { acpi_handle handle = ACPI_HANDLE(pfru_dev->parent_dev); - union acpi_object *out_obj; + union acpi_object *out_obj, *elem; int ret = -EINVAL; out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid, @@ -150,7 +150,9 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr, goto free_acpi_buffer; } - cap_hdr->status = out_obj->package.elements[CAP_STATUS_IDX].integer.value; + elem = out_obj->package.elements; + + cap_hdr->status = elem[CAP_STATUS_IDX].integer.value; if (cap_hdr->status != DSM_SUCCEED) { ret = -EBUSY; dev_dbg(pfru_dev->parent_dev, "Query cap Error Status:%d\n", @@ -158,29 +160,30 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr, goto free_acpi_buffer; } - cap_hdr->update_cap = out_obj->package.elements[CAP_UPDATE_IDX].integer.value; + if (elem[CAP_CODE_TYPE_IDX].buffer.length > sizeof(cap_hdr->code_type) || + elem[CAP_DRV_TYPE_IDX].buffer.length > sizeof(cap_hdr->drv_type) || + elem[CAP_PLAT_ID_IDX].buffer.length > sizeof(cap_hdr->platform_id) || + elem[CAP_OEM_ID_IDX].buffer.length > sizeof(cap_hdr->oem_id)) + goto free_acpi_buffer; + + cap_hdr->update_cap = elem[CAP_UPDATE_IDX].integer.value; memcpy(&cap_hdr->code_type, - out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.pointer, - out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.length); - cap_hdr->fw_version = - out_obj->package.elements[CAP_FW_VER_IDX].integer.value; - cap_hdr->code_rt_version = - out_obj->package.elements[CAP_CODE_RT_VER_IDX].integer.value; + elem[CAP_CODE_TYPE_IDX].buffer.pointer, + elem[CAP_CODE_TYPE_IDX].buffer.length); + cap_hdr->fw_version = elem[CAP_FW_VER_IDX].integer.value; + cap_hdr->code_rt_version = elem[CAP_CODE_RT_VER_IDX].integer.value; memcpy(&cap_hdr->drv_type, - out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.pointer, - out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.length); - cap_hdr->drv_rt_version = - out_obj->package.elements[CAP_DRV_RT_VER_IDX].integer.value; - cap_hdr->drv_svn = - out_obj->package.elements[CAP_DRV_SVN_IDX].integer.value; + elem[CAP_DRV_TYPE_IDX].buffer.pointer, + elem[CAP_DRV_TYPE_IDX].buffer.length); + cap_hdr->drv_rt_version = elem[CAP_DRV_RT_VER_IDX].integer.value; + cap_hdr->drv_svn = elem[CAP_DRV_SVN_IDX].integer.value; memcpy(&cap_hdr->platform_id, - out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.pointer, - out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.length); + elem[CAP_PLAT_ID_IDX].buffer.pointer, + elem[CAP_PLAT_ID_IDX].buffer.length); memcpy(&cap_hdr->oem_id, - out_obj->package.elements[CAP_OEM_ID_IDX].buffer.pointer, - out_obj->package.elements[CAP_OEM_ID_IDX].buffer.length); - cap_hdr->oem_info_len = - out_obj->package.elements[CAP_OEM_INFO_IDX].buffer.length; + elem[CAP_OEM_ID_IDX].buffer.pointer, + elem[CAP_OEM_ID_IDX].buffer.length); + cap_hdr->oem_info_len = elem[CAP_OEM_INFO_IDX].buffer.length; ret = 0; From 415125669c2ddc773c579a60df108f75712dfa83 Mon Sep 17 00:00:00 2001 From: Robin Everaars Date: Mon, 17 Aug 2026 14:14:17 +0000 Subject: [PATCH 5/5] ACPI: button: Add DMI quirk for Razer Blade Pro 17 early 2020 lid switch The lid switch reports "close" but can miss the matching "open", leaving _LID closed after resume. systemd-logind then suspends the system again roughly every 35 seconds. Reading the embedded controller's PSTA byte while _LID is stale shows that bit 0x04 is set, which the DSDT treats as open. The DSDT returns the cached LIDS byte from _LID. Its wake path aborts in RTEC on an unhandled SystemCMOS region before copying PSTA to LIDS. Initialize the lid state to open on resume, matching the existing quirk for the Razer Blade Stealth 13 late 2019. With button.lid_init_state=open, a physical close suspended once and resume reported open without another suspend. Signed-off-by: Robin Everaars Link: https://patch.msgid.link/20260817141414.213075-1-robineveraars@pm.me Signed-off-by: Rafael J. Wysocki --- drivers/acpi/button.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 3836ee75dd66..cdbb1023a8ee 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -133,6 +133,17 @@ static const struct dmi_system_id dmi_lid_quirks[] = { }, .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN, }, + { + /* + * Razer Blade Pro 17 early 2020, notification of the LID device + * only happens on close, not on open and _LID keeps returning closed. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Razer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Blade Pro 17 (Early 2020) - RZ09-0329"), + }, + .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN, + }, { /* * Samsung galaxybook2 ,initial _LID device notification returns