From 7bfd21e828504e3b7dd923bea5474ffdf204f264 Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Tue, 30 Jun 2026 14:14:45 +0800 Subject: [PATCH 01/12] ACPI: APEI: GHES: Mark ghes_in_nmi_spool_from_list() as maybe unused When CONFIG_ACPI_APEI_SEA and CONFIG_HAVE_ACPI_APEI_NMI are both disabled, ghes_in_nmi_spool_from_list() becomes an unused static function and triggers -Werror=unused-function in some configs, for example riscv defconfig with APEI disabled. Mark it as __maybe_unused to silence the warning while keeping the code available for configurations that use SEA or APEI NMI. Reviewed-by: Breno Leitao Reviewed-by: Hanjun Guo Reviewed-by: Shuai Xue Signed-off-by: Rui Qi Link: https://patch.msgid.link/20260630061445.2191731-1-qirui.001@bytedance.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/ghes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 3236a3ce79d6..48f915ee5a0d 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -1397,8 +1397,8 @@ static int ghes_in_nmi_queue_one_entry(struct ghes *ghes, return rc; } -static int ghes_in_nmi_spool_from_list(struct list_head *rcu_list, - enum fixed_addresses fixmap_idx) +static int __maybe_unused ghes_in_nmi_spool_from_list(struct list_head *rcu_list, + enum fixed_addresses fixmap_idx) { int ret = -ENOENT; struct ghes *ghes; From 8a742141f7ab84975aa758b775567ef4740ef0cf Mon Sep 17 00:00:00 2001 From: Chen Pei Date: Wed, 15 Jul 2026 21:50:48 +0800 Subject: [PATCH 02/12] ACPI: PCI: Clear driver_data on all paths that free the acpi_pci_root acpi_pci_root_add() assigns the freshly allocated root to device->driver_data before dmar_device_add() and pci_acpi_scan_root(). Both failure paths reach the end: label where root is kfree()'d, but only the pci_acpi_scan_root() path clears driver_data first. When dmar_device_add() fails during a hot-add, root is freed while device->driver_data still points at it. The ACPI core does not clear driver_data on attach failure, so a later acpi_pci_find_root() call may dereference this dangling pointer. acpi_pci_root_remove() has the same problem: it frees root without clearing device->driver_data, leaving a dangling pointer behind after the root bridge is removed. Move the NULL assignment to the shared end: label so every error path in acpi_pci_root_add() clears driver_data before freeing root, and clear it in acpi_pci_root_remove() as well, so the object is never left reachable through driver_data after being freed. Fixes: db89b4f0dbab ("ACPI: catch calls of acpi_driver_data on pointer of wrong type") Reported-by: Sashiko AI review Link: https://sashiko.dev/#/patchset/20260526025118.38935-1-cp0613@linux.alibaba.com Link: https://sashiko.dev/#/patchset/20260707121258.11640-1-cp0613@linux.alibaba.com Signed-off-by: Chen Pei Link: https://patch.msgid.link/20260715135048.3278-1-cp0613@linux.alibaba.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pci_root.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 4c06c3ffd0cb..408ba12362a7 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -725,7 +725,6 @@ static int acpi_pci_root_add(struct acpi_device *device, dev_err(&device->dev, "Bus %04x:%02x not present in PCI namespace\n", root->segment, (unsigned int)root->secondary.start); - device->driver_data = NULL; result = -ENODEV; goto remove_dmar; } @@ -765,6 +764,7 @@ static int acpi_pci_root_add(struct acpi_device *device, if (hotadd) dmar_device_remove(handle); end: + device->driver_data = NULL; kfree(root); return result; } @@ -788,6 +788,7 @@ static void acpi_pci_root_remove(struct acpi_device *device) pci_unlock_rescan_remove(); + device->driver_data = NULL; kfree(root); } From 5d42a19dac27dc23b827b1a0ba88e76aa89be011 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 16 Jul 2026 16:04:43 +0200 Subject: [PATCH 03/12] ACPI: PCI: Use a mutex guard to simplify acpi_get_pci_dev() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a mutex guard in acpi_get_pci_dev() for the physical_node_lock locking and drop local variable pci_dev that becomes redundant after that change. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/3056272.e9J7NaK4W3@rafael.j.wysocki --- drivers/acpi/pci_root.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 408ba12362a7..85202f2f5394 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) "ACPI: " fmt +#include #include #include #include @@ -307,24 +308,20 @@ struct pci_dev *acpi_get_pci_dev(acpi_handle handle) { struct acpi_device *adev = acpi_fetch_acpi_dev(handle); struct acpi_device_physical_node *pn; - struct pci_dev *pci_dev = NULL; if (!adev) return NULL; - mutex_lock(&adev->physical_node_lock); + guard(mutex)(&adev->physical_node_lock); list_for_each_entry(pn, &adev->physical_node_list, node) { if (dev_is_pci(pn->dev)) { get_device(pn->dev); - pci_dev = to_pci_dev(pn->dev); - break; + return to_pci_dev(pn->dev); } } - mutex_unlock(&adev->physical_node_lock); - - return pci_dev; + return NULL; } EXPORT_SYMBOL_GPL(acpi_get_pci_dev); From 9d4f0ecb87fd939207682ec30dcaf128d43f07fb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 16 Jul 2026 16:04:53 +0200 Subject: [PATCH 04/12] ACPI: PCI: Introduce acpi_dev_get_pci_dev() Some acpi_get_pci_dev() callers already have a struct ACPI device for which they want to get the struct pci_dev pointer of the associated PCI device, so they don't need to look for one. For this reason, add acpi_dev_get_pci_dev() that will get a PCI device for a given ACPI one (if possible) and turn acpi_get_pci_dev() into a static inline helper passing the acpi_fetch_acpi_dev() return value directly to acpi_dev_get_pci_dev(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/3430928.44csPzL39Z@rafael.j.wysocki --- drivers/acpi/pci_root.c | 22 +++++++++++----------- include/acpi/acpi_drivers.h | 9 +++++++-- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 85202f2f5394..6f78f96332ea 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -293,20 +293,20 @@ struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle) EXPORT_SYMBOL_GPL(acpi_pci_find_root); /** - * acpi_get_pci_dev - convert ACPI CA handle to struct pci_dev - * @handle: the handle in question + * acpi_dev_get_pci_dev - Get a struct pci_dev for a given ACPI device + * @adev: Target ACPI device. * - * Given an ACPI CA handle, the desired PCI device is located in the - * list of PCI devices. + * Find the PCI device associated with @adev, if any, and bump up its reference + * counter. * - * If the device is found, its reference count is increased and this - * function returns a pointer to its data structure. The caller must - * decrement the reference count by calling pci_dev_put(). - * If no device is found, %NULL is returned. + * Callers are responsible for dropping the PCI device reference obtained by + * this function. + * + * Return: The struct pci_dev pointer of a reference-counted PCI device on + * success or NULL on failure. */ -struct pci_dev *acpi_get_pci_dev(acpi_handle handle) +struct pci_dev *acpi_dev_get_pci_dev(struct acpi_device *adev) { - struct acpi_device *adev = acpi_fetch_acpi_dev(handle); struct acpi_device_physical_node *pn; if (!adev) @@ -323,7 +323,7 @@ struct pci_dev *acpi_get_pci_dev(acpi_handle handle) return NULL; } -EXPORT_SYMBOL_GPL(acpi_get_pci_dev); +EXPORT_SYMBOL_GPL(acpi_dev_get_pci_dev); /** * acpi_pci_osc_control_set - Request control of PCI root _OSC features. diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index 402b97d12138..bff2ca035fcf 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -59,14 +59,19 @@ int acpi_pci_link_free_irq(acpi_handle handle); struct pci_bus; #ifdef CONFIG_PCI -struct pci_dev *acpi_get_pci_dev(acpi_handle); +struct pci_dev *acpi_dev_get_pci_dev(struct acpi_device *adev); #else -static inline struct pci_dev *acpi_get_pci_dev(acpi_handle handle) +static inline struct pci_dev *acpi_dev_get_pci_dev(struct acpi_device *adev) { return NULL; } #endif +static inline struct pci_dev *acpi_get_pci_dev(acpi_handle handle) +{ + return acpi_dev_get_pci_dev(acpi_fetch_acpi_dev(handle)); +} + /* Arch-defined function to add a bus to the system */ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root); From 2e8c155fe8bf552a4c55dc42f19da78ed2213e39 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 16 Jul 2026 16:05:04 +0200 Subject: [PATCH 05/12] ACPI: video: Drop backlight parent device reference later Update acpi_video_dev_register_backlight() to put the parent device after registering the backlight class device under it instead of attempting to register the backlight class device under a parent that (theoretically) may be gone at that point. Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/10885673.nUPlyArG6x@rafael.j.wysocki --- drivers/acpi/acpi_video.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index f93e877f87f6..a738265279cd 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1719,10 +1719,8 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device) if (ACPI_SUCCESS(acpi_get_parent(device->dev->handle, &acpi_parent))) { pdev = acpi_get_pci_dev(acpi_parent); - if (pdev) { + if (pdev) parent = &pdev->dev; - pci_dev_put(pdev); - } } memset(&props, 0, sizeof(struct backlight_properties)); @@ -1734,6 +1732,7 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device) device, &acpi_backlight_ops, &props); + put_device(parent); kfree(name); if (IS_ERR(device->backlight)) { device->backlight = NULL; From 1c24aa1bb2006d56d4783a9870bba260392edcf7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 16 Jul 2026 16:05:13 +0200 Subject: [PATCH 06/12] ACPI: video: Use acpi_dev_get_pci_dev() instead of acpi_get_pci_dev() In acpi_video_bus_check() and find_video(), simply replace acpi_get_pci_dev() with acpi_dev_get_pci_dev() that can be used in both places because the ACPI device needed to do the lookup is available. In acpi_video_dev_register_backlight(), instead of doing a parent ACPI handle lookup based on the handle of an ACPI device that is already available, pass that ACPI device to acpi_dev_parent() which is much more straightforward and pass the return value of the latter directly to acpi_dev_get_pci_dev() to get the PCI device associated with it. That allows local variable acpi_parent to be eliminated. Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/2047771.PYKUYFuaPT@rafael.j.wysocki --- drivers/acpi/acpi_video.c | 11 ++++------- drivers/acpi/video_detect.c | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index a738265279cd..4d6fd9f6e9ad 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1048,7 +1048,7 @@ static int acpi_video_bus_check(struct acpi_video_bus *video) if (!video) return -EINVAL; - dev = acpi_get_pci_dev(video->device->handle); + dev = acpi_dev_get_pci_dev(video->device); if (!dev) return -ENODEV; pci_dev_put(dev); @@ -1702,7 +1702,6 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device) { struct backlight_properties props; struct pci_dev *pdev; - acpi_handle acpi_parent; struct device *parent = NULL; int result; static int count; @@ -1717,11 +1716,9 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device) return; count++; - if (ACPI_SUCCESS(acpi_get_parent(device->dev->handle, &acpi_parent))) { - pdev = acpi_get_pci_dev(acpi_parent); - if (pdev) - parent = &pdev->dev; - } + pdev = acpi_dev_get_pci_dev(acpi_dev_parent(device->dev)); + if (pdev) + parent = &pdev->dev; memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_FIRMWARE; diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 458efa4fe9d4..c5b08c19e847 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -73,7 +73,7 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv) }; if (acpi_dev && !acpi_match_device_ids(acpi_dev, video_ids)) { - dev = acpi_get_pci_dev(handle); + dev = acpi_dev_get_pci_dev(acpi_dev); if (!dev) return AE_OK; pci_dev_put(dev); From a3df8bbe0a704fa5c1609b9666b594f350558fe0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 5 Aug 2026 20:53:32 +0200 Subject: [PATCH 07/12] ACPI: TAD: Add locking around AML evaluations In the ACPI TAD driver, there are hidden assumptions that the ACPI control methods used by it will not be evaluated concurrently due to ACPICA namespace and interpreter locking. However, that may not be the case since ACPICA may drop and re-acquire the namespace and interpreter locks during the evaluation of a given object in a few cases, including the one in which the AML in question sleeps causing acpi_ex_system_do_sleep() to be called. In that case, the evaluation of one control method may be started while the evaluation of another one is still in progress. For this reason, add a global lock to the ACPI TAD driver and acquire it every time before evaluating an ACPI control method, except for the initial evaluation of _GCP in acpi_tad_probe(). Fixes: 95c513ec84f7 ("ACPI: Add Time and Alarm Device (TAD) driver") Cc: All applicable Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/12951141.O9o76ZdvQC@rafael.j.wysocki --- drivers/acpi/acpi_tad.c | 46 +++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index fc43df083738..45459a4cafae 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,8 @@ static bool acpi_tad_rt_is_invalid(struct acpi_tad_rt *rt) rt->daylight > 3; } +static DEFINE_MUTEX(acpi_tad_aml_lock); + static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt) { acpi_handle handle = ACPI_HANDLE(dev); @@ -113,6 +116,8 @@ static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt) if (PM_RUNTIME_ACQUIRE_ERR(&pm)) return -ENXIO; + guard(mutex)(&acpi_tad_aml_lock); + status = acpi_evaluate_integer(handle, "_SRT", &arg_list, &retval); if (ACPI_FAILURE(status) || retval) return -EIO; @@ -124,30 +129,27 @@ static int acpi_tad_evaluate_grt(struct device *dev, struct acpi_tad_rt *rt) { acpi_handle handle = ACPI_HANDLE(dev); struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER }; - union acpi_object *out_obj; - struct acpi_tad_rt *data; acpi_status status; int ret = -EIO; + guard(mutex)(&acpi_tad_aml_lock); + status = acpi_evaluate_object(handle, "_GRT", NULL, &output); - if (ACPI_FAILURE(status)) - goto out_free; + if (ACPI_SUCCESS(status)) { + union acpi_object *out_obj; - out_obj = output.pointer; - if (out_obj->type != ACPI_TYPE_BUFFER) - goto out_free; + out_obj = output.pointer; + if (out_obj->type == ACPI_TYPE_BUFFER && + out_obj->buffer.length == sizeof(*rt)) { + struct acpi_tad_rt *data; - if (out_obj->buffer.length != sizeof(*rt)) - goto out_free; - - data = (struct acpi_tad_rt *)(out_obj->buffer.pointer); - if (!data->valid) - goto out_free; - - memcpy(rt, data, sizeof(*rt)); - ret = 0; - -out_free: + data = (struct acpi_tad_rt *)(out_obj->buffer.pointer); + if (data->valid) { + memcpy(rt, data, sizeof(*rt)); + ret = 0; + } + } + } ACPI_FREE(output.pointer); return ret; } @@ -193,6 +195,8 @@ static int __acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id, args[0].integer.value = timer_id; args[1].integer.value = value; + guard(mutex)(&acpi_tad_aml_lock); + status = acpi_evaluate_integer(handle, method, &arg_list, &retval); if (ACPI_FAILURE(status) || retval) return -EIO; @@ -215,6 +219,8 @@ static int __acpi_tad_wake_read(struct device *dev, char *method, u32 timer_id, args[0].integer.value = timer_id; + guard(mutex)(&acpi_tad_aml_lock); + status = acpi_evaluate_integer(handle, method, &arg_list, retval); if (ACPI_FAILURE(status)) return -EIO; @@ -416,6 +422,8 @@ static int acpi_tad_clear_status(struct device *dev, u32 timer_id) if (PM_RUNTIME_ACQUIRE_ERR(&pm)) return -ENXIO; + guard(mutex)(&acpi_tad_aml_lock); + status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval); if (ACPI_FAILURE(status) || retval) return -EIO; @@ -456,6 +464,8 @@ static ssize_t acpi_tad_status_read(struct device *dev, char *buf, u32 timer_id) if (PM_RUNTIME_ACQUIRE_ERR(&pm)) return -ENXIO; + guard(mutex)(&acpi_tad_aml_lock); + status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval); if (ACPI_FAILURE(status)) return -EIO; From fa608d2875b57fac792a9e1c11aa1da91737f04a Mon Sep 17 00:00:00 2001 From: Kazuma Kondo Date: Thu, 6 Aug 2026 11:21:11 +0000 Subject: [PATCH 08/12] ACPI: PCI: Avoid misleading _OSC messages for non-PCIe host bridges without _OSC After commit 7d703df7f4f5 ("ACPI: bus: Split _OSC evaluation out of acpi_run_osc()"), the _OSC evaluation path now returns AE_ERROR to negotiate_os_control() instead of propagating AE_NOT_FOUND from acpi_evaluate_object(). This has not caused any functional issues so far, but it produces additional misleading messages for non-PCIe host bridges without _OSC on some Intel servers: kernel: ACPI: Enabled 2 GPEs in block 00 to 7F kernel: ACPI: PCI Root Bridge [UNC0] (domain 0000 [bus fe]) kernel: acpi PNP0A03:00: _OSC: OS supports [ExtendedConfig ASPM ClockPM Segments MSI EDR HPX-Type3] + kernel: acpi PNP0A03:00: _OSC: OS requested [PCIeHotplug SHPCHotplug PME AER PCIeCapability LTR DPC] + kernel: acpi PNP0A03:00: _OSC: platform willing to grant [PCIeHotplug SHPCHotplug PME AER PCIeCapability LTR DPC] + kernel: acpi PNP0A03:00: _OSC: platform retains control of PCIe features (AE_ERROR) kernel: PCI host bridge to bus 0000:fe Previously, negotiate_os_control() silently ignored AE_NOT_FOUND for non-PCIe host bridges without _OSC, but after the above change, it no longer does so. As a result, negotiate_os_control() logs messages as if the OS had negotiated with the platform via _OSC, even though the non-PCIe host bridge has no _OSC method and no such negotiation actually occurs. Skip _OSC negotiation for non-PCIe host bridges that do not define an _OSC method before attempting to evaluate it. Fixes: 7d703df7f4f5 ("ACPI: bus: Split _OSC evaluation out of acpi_run_osc()") Link: https://lore.kernel.org/linux-acpi/d0be949d-0e21-472e-a44b-cedb1dd8695d@nec.com/ Suggested-by: Rafael J. Wysocki Signed-off-by: Kazuma Kondo [ rjw: Adjust white space, drop comment and tweak the new message ] Link: https://patch.msgid.link/20260806112052.857684-1-kazuma-kondo@nec.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pci_root.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 6f78f96332ea..88c65f34e305 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -571,6 +571,13 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm) return; } + if (!is_pcie(root) && !is_cxl(root) && !acpi_has_method(handle, "_OSC")) { + dev_dbg(&device->dev, "Non-PCIe host bridge without _OSC, skipping\n"); + + *no_aspm = 1; + return; + } + support = calculate_support(); decode_osc_support(root, "OS supports", support); @@ -612,10 +619,6 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm) */ *no_aspm = 1; - /* _OSC is optional for PCI host bridges */ - if (status == AE_NOT_FOUND && !is_pcie(root)) - return; - if (control) { decode_osc_control(root, "OS requested", requested); decode_osc_control(root, "platform willing to grant", control); From 903308ea40adf0577d82eab69882faf8836326ce Mon Sep 17 00:00:00 2001 From: TanZheng Date: Thu, 6 Aug 2026 09:09:44 +0800 Subject: [PATCH 09/12] ACPI: APEI: GHES: fix ARM section length accounting after header In ghes_handle_arm_hw_error(), after skipping the cper_sec_proc_arm header with (err + 1), the remaining length was reduced by sizeof(err) (pointer size) instead of sizeof(*err) (structure size). That overestimates the bytes left for cper_arm_err_info records and can let the parser read past the CPER section when err_info_num is large enough relative to error_data_length. Use sizeof(*err) so the length accounting matches the pointer advance and the earlier sizeof(*err) size check. Fixes: 87880af2d24e ("APEI/GHES: ARM processor Error: don't go past allocated memory") Cc: stable@vger.kernel.org Signed-off-by: TanZheng Reviewed-by: Shuai Xue Link: https://patch.msgid.link/20260806010944.32384-1-kensanya@163.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/ghes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 48f915ee5a0d..f245169c3026 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -576,7 +576,7 @@ static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, return false; p = (char *)(err + 1); - length -= sizeof(err); + length -= sizeof(*err); for (i = 0; i < err->err_info_num; i++) { struct cper_arm_err_info *err_info; From a685d8eea4a6899dc887e393927c16fa18ff5e9a Mon Sep 17 00:00:00 2001 From: Nirmoy Das Date: Tue, 21 Jul 2026 11:25:51 -0700 Subject: [PATCH 10/12] ACPI: APEI: Fix ERST timeout unit conversion The ACPI specification defines bits 63:32 returned by GET_EXECUTE_OPERATION_TIMINGS as the maximum execution time in microseconds. erst_get_timeout() instead multiplies the value by NSEC_PER_MSEC. Use NSEC_PER_USEC to express the firmware-provided microsecond timeout in the nanosecond units expected by erst_timedout(). Fixes: fac475aab70b ("ACPI: APEI: Use ERST timeout for slow devices") Cc: stable@vger.kernel.org Signed-off-by: Nirmoy Das Reviewed-by: Hanjun Guo Link: https://patch.msgid.link/20260721182551.2434933-1-nirmoyd@nvidia.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/erst.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index bf65e3461531..15ab797641cf 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -108,7 +108,7 @@ static inline u64 erst_get_timeout(void) if (erst_erange.attr & ERST_RANGE_SLOW) { timeout = ((erst_erange.timings & ERST_EXEC_TIMING_MAX_MASK) >> - ERST_EXEC_TIMING_MAX_SHIFT) * NSEC_PER_MSEC; + ERST_EXEC_TIMING_MAX_SHIFT) * NSEC_PER_USEC; if (timeout < FIRMWARE_TIMEOUT) timeout = FIRMWARE_TIMEOUT; } From e61487226b7df39e5f4683b036ee6aa7a5039d7b Mon Sep 17 00:00:00 2001 From: Junhao He Date: Wed, 27 May 2026 16:27:07 +0800 Subject: [PATCH 11/12] ACPI: APEI: Handle repeated SEA error storms When hardware memory corruption occurs and a user process accesses the corrupted page, the CPU triggers a Synchronous External Abort (SEA). The kernel invokes do_sea() to handle the exception, which calls memory_failure() to handle the faulty page. Scenario 1: Memory Error Interrupt First, then SEA The page is already poisoned by the memory error interrupt path. The subsequent SEA handler sends a SIGBUS to the task, which accesses the poisoned page. This flow is correct. Scenario 2: SEA first, then memory error interrupt (problematic scenario) If a user task directly accesses corrupted memory through a PFNMAP-style mapping (e.g., devmem), the page may still be in the free-buddy state when SEA is handled. In this case, memory_failure() will poison the page without invoking kill_accessing_process(), and then takes the free-buddy recovery path. After the CPU returns to the task context, the task re-enters the SEA handler due to the same access. However, ghes_estatus_cached() suppresses all subsequent entries during the 10-second window, preventing ghes_do_proc() from being called. This suppression blocks the MF_ACTION_REQUIRED-based SIGBUS delivery, causing the kernel to fail to kill the task immediately. Consequently, the process keeps re-entering the SEA handler, leading to an SEA storm. Later, the memory error interrupt path also cannot kill the task, leaving the system stuck in this repeated loop. The following error logs are explained using the devmem process: NOTICE: SEA Handle [Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 9 [Hardware Error]: event severity: recoverable [Hardware Error]: section_type: ARM processor error [Hardware Error]: physical fault address: 0x0000001000093c00 [T54990] Memory failure: 0x1000093: recovery action for free buddy page: Recovered [ T9955] EDAC MC0: 1 UE Multi-bit ECC on unknown memory (page:0x1000093 offset:0xc00 grain:1 - APEI location: ...) NOTICE: SEA Handle NOTICE: SEA Handle ... ... ---> SEA storm ... NOTICE: SEA Handle [ T9955] Memory failure: 0x1000093: already hardware poisoned ghes_print_estatus: 1 callbacks suppressed [Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 9 [Hardware Error]: event severity: recoverable [Hardware Error]: section_type: ARM processor error [Hardware Error]: physical fault address: 0x0000001000093c00 [T54990] Memory failure: 0x1000093: already hardware poisoned [T54990] 0x1000093: Sending SIGBUS to devmem:54990 due to hardware memory corruption To resolve this, return an error when encountering the same SEA again. The subsequent SEA handler invocation uses arm64_notify_die() to send a SIGBUS signal to the task, which terminates the process and prevents it from re-entering the handler loop. Signed-off-by: Junhao He Reviewed-by: Wupeng Ma Reviewed-by: Shuai Xue Reviewed-by: Tony Luck Link: https://patch.msgid.link/20260527082707.2013499-1-hejunhao3@h-partners.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/ghes.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index f245169c3026..bc1dfa3398b4 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -1383,8 +1383,16 @@ static int ghes_in_nmi_queue_one_entry(struct ghes *ghes, ghes_clear_estatus(ghes, &tmp_header, buf_paddr, fixmap_idx); /* This error has been reported before, don't process it again. */ - if (ghes_estatus_cached(estatus)) + if (ghes_estatus_cached(estatus)) { + /* + * Return failure on duplicate SEA entries so that the + * subsequent SEA handler invocation sends a SIGBUS signal to + * the task to prevent it from re-entering the handler loop. + */ + if (is_hest_sync_notify(ghes)) + rc = -ECANCELED; goto no_work; + } llist_add(&estatus_node->llnode, &ghes_estatus_llist); From f234fdaae1cad8c39265e7ca0a14633076ec7154 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 7 Aug 2026 12:22:37 +0200 Subject: [PATCH 12/12] ACPI: scan: Avoid registering platform devices with resource overlaps If acpi_dev_get_resources() returns overlapping I/O or memory resources, the subsequent registration of a platform device will fail with -EBUSY due to a resource conflict. This is reported to happen on Acer Aspire ES1-572 [1]. Avoid that by adjusting resources returned by acpi_dev_get_resources() to eliminate partial overlaps between them. This has not been regarded as necessary before because putting overlapping resources into the _CRS of one device is really pointless, but now that the issue has been reported to actually happen in the field, it needs to be done. Fixes: ab06eb920401 ("ACPI: scan: Register platform devices for fixed event buttons") Fixes: 48fe2cddc85c ("tpm_crb: Convert ACPI driver to a platform one") Reported-by: Julien Tested-by: Julien Reviewed-by: Paul Menzel Reviewed-by: Andy Shevchenko Closes: https://lore.kernel.org/linux-integrity/CAJOGg3z6LJPDsdPNBxajgy8_wQxfhYBRxe4EiurZf3kPU5A5Bw@mail.gmail.com/ [1] Cc: All applicable Signed-off-by: Rafael J. Wysocki [ rjw: Tweaked the new message ] Link: https://patch.msgid.link/12955541.O9o76ZdvQC@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_platform.c | 39 ++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index a09636a4168e..373c94de7590 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +72,36 @@ static struct notifier_block acpi_platform_notifier = { .notifier_call = acpi_platform_device_remove_notify, }; +static unsigned int acpi_platform_adjust_resources(struct acpi_device *adev, + struct resource *new_res, + struct resource *resources, + unsigned int count) +{ + unsigned int i; + + if (!(new_res->flags & (IORESOURCE_IO | IORESOURCE_MEM))) + return count; + + for (i = 0; i < count; ) { + struct resource *res = &resources[i]; + + if (resource_type(new_res) != resource_type(res) || + !resource_union(new_res, res, new_res)) { + i++; + continue; + } + + dev_info(&adev->dev, "%pR expanded due to overlap\n", new_res); + /* + * Eliminate the previously processed resource that overlapped + * with the new one because it is not necessary any more. + */ + memmove(res, res + 1, (--count - i) * sizeof(*res)); + } + + return count; +} + static void acpi_platform_fill_resource(struct acpi_device *adev, const struct resource *src, struct resource *dest) { @@ -151,10 +182,14 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, return ERR_PTR(-ENOMEM); } count = 0; - list_for_each_entry(rentry, &resource_list, node) + list_for_each_entry(rentry, &resource_list, node) { + count = acpi_platform_adjust_resources(adev, + rentry->res, + resources, + count); acpi_platform_fill_resource(adev, rentry->res, &resources[count++]); - + } acpi_dev_free_resource_list(&resource_list); } }