From 3ad5df2391040d1596ce888c042f99b7c1d7870a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 15 Dec 2025 14:34:06 +0100 Subject: [PATCH 01/18] ACPI: PNP: Drop PNP0C01 and PNP0C02 from acpi_pnp_device_ids[] There is a long-standing problem with ACPI device enumeration that if the given device has a compatible ID which is one of the generic system resource device IDs (PNP0C01 and PNP0C02), it will be claimed by the PNP scan handler and it will not be represented as a platform device, so it cannot be handled by a platform driver. Drivers have been working around this issue by "manually" creating platform devices that they can bind to (see the Intel HID driver for one example) or adding their device IDs to acpi_nonpnp_device_ids[]. None of the above is particularly clean though and the only reason why the PNP0C01 and PNP0C02 device IDs are present in acpi_pnp_device_ids[] is to allow the legacy PNP system driver to bind to those devices and reserve their resources so they are not used going forward. Obviously, to address this problem PNP0C01 and PNP0C02 need to be dropped from acpi_pnp_device_ids[], but doing so without making any other changes would be problematic because the ACPI core would then create platform devices for the generic system resource device objects and that would not work on all systems for two reasons. First, the PNP system driver explicitly avoids reserving I/O resources below the "standard PC hardware" boundary, 0x100, to avoid conflicts in that range (one possible case when this may happen is when the CMOS RTC driver is involved), but the platform device creation code does not do that. Second, there may be resource conflicts between the "system" devices and the other devices in the system, possibly including conflicts with PCI BARs. Registering the PNP system driver via fs_initcall() helps to manage those conflicts, even though it does not make them go away. Resource conflicts during the registration of "motherboard resources" that occur after PCI has claimed BARs are harmless as a rule and do not need to be addressed in any specific way. To overcome the issues mentioned above, use the observation that it is not actually necessary to create any device objects in addition to struct acpi_device ones in order to reserve the "system" device resources because that can be done directly in the ACPI device enumeration code. Namely, modify acpi_default_enumeration() to add the given ACPI device object to a special "system devices" list if its _HID is either PNP0C01 or PNP0C02 without creating a platform device for it. Next, add a new special acpi_scan_claim_resources() function that will be run via fs_initcall() and will walk that list and reserve resources for each device in it along the lines of what the PNP system driver does. Having made the above changes, drop PNP0C01 and PNP0C02 from acpi_pnp_device_ids[] which will allow platform devices to be created for ACPI device objects whose _CID lists contain PNP0C01 or PNP0C02, but the _HID is not in acpi_pnp_device_ids[]. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mario Limonciello (AMD) [ rjw: Drop a leftover comment and add a new one elsewhere ] Link: https://patch.msgid.link/9550709.CDJkKcVGEf@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_pnp.c | 3 - drivers/acpi/scan.c | 120 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 116 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c index 4ad88187dc7a..17b2219a361e 100644 --- a/drivers/acpi/acpi_pnp.c +++ b/drivers/acpi/acpi_pnp.c @@ -125,9 +125,6 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = { {"PNP0401"}, /* ECP Printer Port */ /* apple-gmux */ {"APP000B"}, - /* system */ - {"PNP0c02"}, /* General ID for reserving resources */ - {"PNP0c01"}, /* memory controller */ /* rtc_cmos */ {"PNP0b00"}, {"PNP0b01"}, diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 416d87f9bd10..8a895d377e21 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -42,6 +42,7 @@ static LIST_HEAD(acpi_scan_handlers_list); DEFINE_MUTEX(acpi_device_lock); LIST_HEAD(acpi_wakeup_device_list); static DEFINE_MUTEX(acpi_hp_context_lock); +static LIST_HEAD(acpi_scan_system_dev_list); /* * The UART device described by the SPCR table is the only object which needs @@ -2203,19 +2204,48 @@ static acpi_status acpi_bus_check_add_2(acpi_handle handle, u32 lvl_not_used, return acpi_bus_check_add(handle, false, (struct acpi_device **)ret_p); } +struct acpi_scan_system_dev { + struct list_head node; + struct acpi_device *adev; +}; + +static const char * const acpi_system_dev_ids[] = { + "PNP0C01", /* Memory controller */ + "PNP0C02", /* Motherboard resource */ + NULL +}; + static void acpi_default_enumeration(struct acpi_device *device) { /* * Do not enumerate devices with enumeration_by_parent flag set as * they will be enumerated by their respective parents. */ - if (!device->flags.enumeration_by_parent) { - acpi_create_platform_device(device, NULL); - acpi_device_set_enumerated(device); - } else { + if (device->flags.enumeration_by_parent) { blocking_notifier_call_chain(&acpi_reconfig_chain, ACPI_RECONFIG_DEVICE_ADD, device); + return; } + if (match_string(acpi_system_dev_ids, -1, acpi_device_hid(device)) >= 0) { + struct acpi_scan_system_dev *sd; + + /* + * This is a generic system device, so there is no need to + * create a platform device for it, but its resources need to be + * reserved. However, that needs to be done after all of the + * other device objects have been processed and PCI has claimed + * BARs in case there are resource conflicts. + */ + sd = kmalloc(sizeof(*sd), GFP_KERNEL); + if (sd) { + sd->adev = device; + list_add_tail(&sd->node, &acpi_scan_system_dev_list); + } + } else { + /* For a regular device object, create a platform device. */ + acpi_create_platform_device(device, NULL); + } + acpi_device_set_enumerated(device); } static const struct acpi_device_id generic_device_ids[] = { @@ -2571,6 +2601,88 @@ static void acpi_scan_postponed(void) mutex_unlock(&acpi_dep_list_lock); } +static void acpi_scan_claim_resources(struct acpi_device *adev) +{ + struct list_head resource_list = LIST_HEAD_INIT(resource_list); + struct resource_entry *rentry; + unsigned int count = 0; + const char *regionid; + + if (acpi_dev_get_resources(adev, &resource_list, NULL, NULL) <= 0) + return; + + regionid = kstrdup(dev_name(&adev->dev), GFP_KERNEL); + if (!regionid) + goto exit; + + list_for_each_entry(rentry, &resource_list, node) { + struct resource *res = rentry->res; + struct resource *r; + + /* Skip disabled and invalid resources. */ + if ((res->flags & IORESOURCE_DISABLED) || res->end < res->start) + continue; + + if (res->flags & IORESOURCE_IO) { + /* + * Follow the PNP system driver and on x86 skip I/O + * resources that start below 0x100 (the "standard PC + * hardware" boundary). + */ + if (IS_ENABLED(CONFIG_X86) && res->start < 0x100) { + dev_info(&adev->dev, "Skipped %pR\n", res); + continue; + } + r = request_region(res->start, resource_size(res), regionid); + } else if (res->flags & IORESOURCE_MEM) { + r = request_mem_region(res->start, resource_size(res), regionid); + } else { + continue; + } + + if (r) { + r->flags &= ~IORESOURCE_BUSY; + dev_info(&adev->dev, "Reserved %pR\n", r); + count++; + } else { + /* + * Failures at this point are usually harmless. PCI + * quirks, for example, reserve resources they know + * about too, so there may well be double reservations. + */ + dev_info(&adev->dev, "Could not reserve %pR\n", res); + } + } + + if (!count) + kfree(regionid); + +exit: + acpi_dev_free_resource_list(&resource_list); +} + + +static int __init acpi_reserve_motherboard_resources(void) +{ + struct acpi_scan_system_dev *sd, *tmp; + + guard(mutex)(&acpi_scan_lock); + + list_for_each_entry_safe(sd, tmp, &acpi_scan_system_dev_list, node) { + acpi_scan_claim_resources(sd->adev); + list_del(&sd->node); + kfree(sd); + } + + return 0; +} + +/* + * Reserve motherboard resources after PCI claims BARs, but before PCI assigns + * resources for uninitialized PCI devices. + */ +fs_initcall(acpi_reserve_motherboard_resources); + /** * acpi_bus_scan - Add ACPI device node objects in a given namespace scope. * @handle: Root of the namespace scope to scan. From 61ddc929a162c68f2fedb32767cf8afe54198b56 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 15 Dec 2025 14:35:05 +0100 Subject: [PATCH 02/18] platform/x86/intel/hid: Stop creating a platform device Now that "system" devices are represented as platform devices, they are not claimed by the PNP ACPI scan handler any more and the Intel HID platform devices should be created by the ACPI core, so the driver does not need to attempt to create a platform device by itself. Accordingly, make it stop doing so. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/6115868.MhkbZ0Pkbq@rafael.j.wysocki --- drivers/platform/x86/intel/hid.c | 41 +------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index 560cc063198e..0f8684f4464b 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -779,43 +779,4 @@ static struct platform_driver intel_hid_pl_driver = { .remove = intel_hid_remove, }; -/* - * Unfortunately, some laptops provide a _HID="INT33D5" device with - * _CID="PNP0C02". This causes the pnpacpi scan driver to claim the - * ACPI node, so no platform device will be created. The pnpacpi - * driver rejects this device in subsequent processing, so no physical - * node is created at all. - * - * As a workaround until the ACPI core figures out how to handle - * this corner case, manually ask the ACPI platform device code to - * claim the ACPI node. - */ -static acpi_status __init -check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - const struct acpi_device_id *ids = context; - struct acpi_device *dev = acpi_fetch_acpi_dev(handle); - - if (dev && acpi_match_device_ids(dev, ids) == 0) - if (!IS_ERR_OR_NULL(acpi_create_platform_device(dev, NULL))) - dev_info(&dev->dev, - "intel-hid: created platform device\n"); - - return AE_OK; -} - -static int __init intel_hid_init(void) -{ - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, check_acpi_dev, NULL, - (void *)intel_hid_ids, NULL); - - return platform_driver_register(&intel_hid_pl_driver); -} -module_init(intel_hid_init); - -static void __exit intel_hid_exit(void) -{ - platform_driver_unregister(&intel_hid_pl_driver); -} -module_exit(intel_hid_exit); +module_platform_driver(intel_hid_pl_driver); From 686e905aeea524b957a8584c8c1e5060111ad4da Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 15 Dec 2025 14:35:44 +0100 Subject: [PATCH 03/18] platform/x86/intel/vbtn: Stop creating a platform device Now that "system" devices are represented as platform devices, they are not claimed by the PNP ACPI scan handler any more and the Intel virtual button array platform devices should be created by the ACPI core, so the driver does not need to attempt to create a platform device by itself. Accordingly, make it stop doing so. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/8661724.NyiUUSuA9g@rafael.j.wysocki --- drivers/platform/x86/intel/vbtn.c | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c index 232cd12e3c9f..9ca87e707582 100644 --- a/drivers/platform/x86/intel/vbtn.c +++ b/drivers/platform/x86/intel/vbtn.c @@ -390,32 +390,4 @@ static struct platform_driver intel_vbtn_pl_driver = { .remove = intel_vbtn_remove, }; -static acpi_status __init -check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - const struct acpi_device_id *ids = context; - struct acpi_device *dev = acpi_fetch_acpi_dev(handle); - - if (dev && acpi_match_device_ids(dev, ids) == 0) - if (!IS_ERR_OR_NULL(acpi_create_platform_device(dev, NULL))) - dev_info(&dev->dev, - "intel-vbtn: created platform device\n"); - - return AE_OK; -} - -static int __init intel_vbtn_init(void) -{ - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, check_acpi_dev, NULL, - (void *)intel_vbtn_ids, NULL); - - return platform_driver_register(&intel_vbtn_pl_driver); -} -module_init(intel_vbtn_init); - -static void __exit intel_vbtn_exit(void) -{ - platform_driver_unregister(&intel_vbtn_pl_driver); -} -module_exit(intel_vbtn_exit); +module_platform_driver(intel_vbtn_pl_driver); From dd2fc7b85744e8da5a9fd630e5c030b70eebd293 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 15 Dec 2025 14:36:49 +0100 Subject: [PATCH 04/18] ACPI: PNP: Drop acpi_nonpnp_device_ids[] Now that "system" devices are represented as platform devices, they are not claimed by the PNP ACPI scan handler any more and platform devices can be created for ACPI device objects listing "system" device IDs as their compatible device IDs. Accordingly, it should not be necessary any more to add device IDs to acpi_nonpnp_device_ids[], so drop it. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/3587570.QJadu78ljV@rafael.j.wysocki --- drivers/acpi/acpi_pnp.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c index 17b2219a361e..85d9f78619a2 100644 --- a/drivers/acpi/acpi_pnp.c +++ b/drivers/acpi/acpi_pnp.c @@ -343,24 +343,10 @@ static bool acpi_pnp_match(const char *idstr, const struct acpi_device_id **matc return false; } -/* - * If one of the device IDs below is present in the list of device IDs of a - * given ACPI device object, the PNP scan handler will not attach to that - * object, because there is a proper non-PNP driver in the kernel for the - * device represented by it. - */ -static const struct acpi_device_id acpi_nonpnp_device_ids[] = { - {"INT3F0D"}, - {"INTC1080"}, - {"INTC1081"}, - {"INTC1099"}, - {""}, -}; - static int acpi_pnp_attach(struct acpi_device *adev, const struct acpi_device_id *id) { - return !!acpi_match_device_ids(adev, acpi_nonpnp_device_ids); + return true; } static struct acpi_scan_handler acpi_pnp_handler = { From bb203a649c26bbcb39c1d93d020cad77e87c518e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 22 Dec 2025 20:05:44 +0100 Subject: [PATCH 05/18] ACPI: bus: Fix handling of _OSC errors in acpi_run_osc() The handling of _OSC errors in acpi_run_osc() is inconsistent and arguably not compliant with the _OSC definition (cf. Section 6.2.12 of ACPI 6.6 [1]). Namely, if OSC_QUERY_ENABLE is not set in the capabilities buffer and any of the error bits are set in the _OSC return buffer, acpi_run_osc() returns an error code and the _OSC return buffer is discarded. However, in that case, depending on what error bits are set, the return buffer may contain acknowledged bits for features that need to be controlled by the kernel going forward. If the OSC_INVALID_UUID_ERROR bit is set, the request could not be processed at all and so in that particular case discarding the _OSC return buffer and returning an error is the right thing to do regardless of whether or not OSC_QUERY_ENABLE is set in the capabilities buffer. If OSC_QUERY_ENABLE is set in the capabilities buffer and the OSC_REQUEST_ERROR or OSC_INVALID_REVISION_ERROR bits are set in the return buffer, acpi_run_osc() may return an error and discard the _OSC return buffer because in that case the platform configuration does not change. However, if any of them is set in the return buffer when OSC_QUERY_ENABLE is not set in the capabilities buffer, the feature mask in the _OSC return buffer still represents a set of acknowleded features as per the _OSC definition: The platform acknowledges the Capabilities Buffer by returning a buffer of DWORDs of the same length. Set bits indicate acknowledgment that OSPM may take control of the capability and cleared bits indicate that the platform either does not support the capability or that OSPM may not assume control. which is not conditional on the error bits being clear, so in that case, discarding the _OSC return buffer is questionable. There is also no reason to return an error and discard the _OSC return buffer if the OSC_CAPABILITIES_MASK_ERROR bit is set in it, but printing diagnostic messages is appropriate when that happens with OSC_QUERY_ENABLE clear in the capabilities buffer. Adress this issue by making acpi_run_osc() follow the rules outlined above. Moreover, make acpi_run_osc() only take the defined _OSC error bits into account when checking _OSC errors. Link: https://uefi.org/specs/ACPI/6.6/06_Device_Configuration.html#osc-operating-system-capabilities [1] Signed-off-by: Rafael J. Wysocki Reviewed-by: Jonathan Cameron [ rjw: Corrected typo in the changelog ] Link: https://patch.msgid.link/3042649.e9J7NaK4W3@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 52 +++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index a984ccd4a2a0..3b12fb59749d 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -194,14 +194,18 @@ static void acpi_print_osc_error(acpi_handle handle, pr_debug("\n"); } +#define OSC_ERROR_MASK (OSC_REQUEST_ERROR | OSC_INVALID_UUID_ERROR | \ + OSC_INVALID_REVISION_ERROR | \ + OSC_CAPABILITIES_MASK_ERROR) + acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) { + u32 errors, *capbuf = context->cap.pointer; acpi_status status; struct acpi_object_list input; union acpi_object in_params[4]; union acpi_object *out_obj; guid_t guid; - u32 errors; struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; if (!context) @@ -240,29 +244,49 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) status = AE_TYPE; goto out_kfree; } - /* Need to ignore the bit0 in result code */ - errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0); + /* Only take defined error bits into account. */ + errors = *((u32 *)out_obj->buffer.pointer) & OSC_ERROR_MASK; + /* + * If OSC_QUERY_ENABLE is set, ignore the "capabilities masked" + * bit because it merely means that some features have not been + * acknowledged which is not unexpected. + */ + if (capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE) + errors &= ~OSC_CAPABILITIES_MASK_ERROR; + if (errors) { + /* + * As a rule, fail only if OSC_QUERY_ENABLE is set because + * otherwise the acknowledged features need to be controlled. + */ + bool fail = !!(capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE); + + if (errors & OSC_INVALID_UUID_ERROR) { + acpi_print_osc_error(handle, context, + "_OSC invalid UUID"); + /* + * Always fail if this bit is set because it means that + * the request could not be processed. + */ + fail = true; + goto out_kfree; + } if (errors & OSC_REQUEST_ERROR) acpi_print_osc_error(handle, context, "_OSC request failed"); - if (errors & OSC_INVALID_UUID_ERROR) - acpi_print_osc_error(handle, context, - "_OSC invalid UUID"); if (errors & OSC_INVALID_REVISION_ERROR) acpi_print_osc_error(handle, context, "_OSC invalid revision"); - if (errors & OSC_CAPABILITIES_MASK_ERROR) { - if (((u32 *)context->cap.pointer)[OSC_QUERY_DWORD] - & OSC_QUERY_ENABLE) - goto out_success; - status = AE_SUPPORT; + if (errors & OSC_CAPABILITIES_MASK_ERROR) + acpi_print_osc_error(handle, context, + "_OSC capability bits masked"); + + if (fail) { + status = AE_ERROR; goto out_kfree; } - status = AE_ERROR; - goto out_kfree; } -out_success: + context->ret.length = out_obj->buffer.length; context->ret.pointer = kmemdup(out_obj->buffer.pointer, context->ret.length, GFP_KERNEL); From 06bf78f82f45514416b1d2193f7a45b6c6c1995e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 22 Dec 2025 20:11:08 +0100 Subject: [PATCH 06/18] ACPI: bus: Rework printing debug messages on _OSC errors Instead of using one function, acpi_print_osc_error(), for printing a debug message and dumping the _OSC request data in one go, use acpi_handle_debug() directly for printing messages and a separate function called acpi_dump_osc_data() for dumping the _OSC request data before printing one or more of them. This avoids * dumping _OSC request data multiple times when there are multiple error bits set in the return buffer, * wrapping message lines on terminals with 80 character line width, * mixing up unrelated messages by printing full lines only, and generally helps to make the messages easier to parse. Also, use %pUL for UUID printing instead of printing UUIDs as strings and include the revision number into the dumped _OSC request data. This is how the debug printout looks like when the OSC_REQUEST_ERROR and OSC_INVALID_REVISION_ERROR bits are set in the return buffer before the change: ACPI: \_SB_: ACPI: (0811B06E-4A27-44F9-8D60-3CBBC22E7B48): _OSC request failed ACPI: _OSC request data: ACPI: 1 ACPI: 2e7eff ACPI: ACPI: \_SB_: ACPI: (0811B06E-4A27-44F9-8D60-3CBBC22E7B48): _OSC invalid revision ACPI: _OSC request data: ACPI: 1 ACPI: 2e7eff ACPI: and this is how it is going to look like afterward: ACPI: \_SB_: ACPI: _OSC: UUID: 0811B06E-4A27-44F9-8D60-3CBBC22E7B48, rev: 1 ACPI: \_SB_: ACPI: _OSC: capabilities DWORD 0: [00000001] ACPI: \_SB_: ACPI: _OSC: capabilities DWORD 1: [002e7eff] ACPI: \_SB_: ACPI: _OSC: request failed ACPI: \_SB_: ACPI: _OSC: invalid revision Signed-off-by: Rafael J. Wysocki Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/10794028.nUPlyArG6x@rafael.j.wysocki --- drivers/acpi/bus.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 3b12fb59749d..658c2f532dfb 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -180,18 +180,16 @@ void acpi_bus_detach_private_data(acpi_handle handle) } EXPORT_SYMBOL_GPL(acpi_bus_detach_private_data); -static void acpi_print_osc_error(acpi_handle handle, - struct acpi_osc_context *context, char *error) +static void acpi_dump_osc_data(acpi_handle handle, const guid_t *guid, int rev, + struct acpi_buffer *cap) { + u32 *capbuf = cap->pointer; int i; - acpi_handle_debug(handle, "(%s): %s\n", context->uuid_str, error); - - pr_debug("_OSC request data:"); - for (i = 0; i < context->cap.length; i += sizeof(u32)) - pr_debug(" %x", *((u32 *)(context->cap.pointer + i))); - - pr_debug("\n"); + acpi_handle_debug(handle, "_OSC: UUID: %pUL, rev: %d\n", guid, rev); + for (i = 0; i < cap->length / sizeof(u32); i++) + acpi_handle_debug(handle, "_OSC: capabilities DWORD %i: [%08x]\n", + i, capbuf[i]); } #define OSC_ERROR_MASK (OSC_REQUEST_ERROR | OSC_INVALID_UUID_ERROR | \ @@ -239,8 +237,8 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) out_obj = output.pointer; if (out_obj->type != ACPI_TYPE_BUFFER || out_obj->buffer.length != context->cap.length) { - acpi_print_osc_error(handle, context, - "_OSC evaluation returned wrong type"); + acpi_dump_osc_data(handle, &guid, context->rev, &context->cap); + acpi_handle_debug(handle, "_OSC: evaluation returned wrong type"); status = AE_TYPE; goto out_kfree; } @@ -261,9 +259,9 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) */ bool fail = !!(capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE); + acpi_dump_osc_data(handle, &guid, context->rev, &context->cap); if (errors & OSC_INVALID_UUID_ERROR) { - acpi_print_osc_error(handle, context, - "_OSC invalid UUID"); + acpi_handle_debug(handle, "_OSC: invalid UUID"); /* * Always fail if this bit is set because it means that * the request could not be processed. @@ -272,14 +270,13 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) goto out_kfree; } if (errors & OSC_REQUEST_ERROR) - acpi_print_osc_error(handle, context, - "_OSC request failed"); + acpi_handle_debug(handle, "_OSC: request failed"); + if (errors & OSC_INVALID_REVISION_ERROR) - acpi_print_osc_error(handle, context, - "_OSC invalid revision"); + acpi_handle_debug(handle, "_OSC: invalid revision"); + if (errors & OSC_CAPABILITIES_MASK_ERROR) - acpi_print_osc_error(handle, context, - "_OSC capability bits masked"); + acpi_handle_debug(handle, "_OSC: capability bits masked"); if (fail) { status = AE_ERROR; From 7d703df7f4f5646d9d9f866a930843f838c9979b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 22 Dec 2025 20:14:16 +0100 Subject: [PATCH 07/18] ACPI: bus: Split _OSC evaluation out of acpi_run_osc() Split a function for evaluating _OSC called acpi_eval_osc() out of acpi_run_osc() to facilitate subsequent changes and add some more parameters sanity checks to the latter. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/22963770.EfDdHjke4D@rafael.j.wysocki --- drivers/acpi/bus.c | 91 +++++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 658c2f532dfb..ad828dcd0657 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -196,52 +196,67 @@ static void acpi_dump_osc_data(acpi_handle handle, const guid_t *guid, int rev, OSC_INVALID_REVISION_ERROR | \ OSC_CAPABILITIES_MASK_ERROR) +static int acpi_eval_osc(acpi_handle handle, guid_t *guid, int rev, + struct acpi_buffer *cap, + union acpi_object in_params[at_least 4], + struct acpi_buffer *output) +{ + struct acpi_object_list input; + union acpi_object *out_obj; + acpi_status status; + + in_params[0].type = ACPI_TYPE_BUFFER; + in_params[0].buffer.length = sizeof(*guid); + in_params[0].buffer.pointer = (u8 *)guid; + in_params[1].type = ACPI_TYPE_INTEGER; + in_params[1].integer.value = rev; + in_params[2].type = ACPI_TYPE_INTEGER; + in_params[2].integer.value = cap->length / sizeof(u32); + in_params[3].type = ACPI_TYPE_BUFFER; + in_params[3].buffer.length = cap->length; + in_params[3].buffer.pointer = cap->pointer; + input.pointer = in_params; + input.count = 4; + + output->length = ACPI_ALLOCATE_BUFFER; + output->pointer = NULL; + + status = acpi_evaluate_object(handle, "_OSC", &input, output); + if (ACPI_FAILURE(status) || !output->length) + return -ENODATA; + + out_obj = output->pointer; + if (out_obj->type != ACPI_TYPE_BUFFER || + out_obj->buffer.length != cap->length) { + acpi_handle_debug(handle, "Invalid _OSC return buffer\n"); + acpi_dump_osc_data(handle, guid, rev, cap); + ACPI_FREE(out_obj); + return -ENODATA; + } + + return 0; +} + acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) { u32 errors, *capbuf = context->cap.pointer; - acpi_status status; - struct acpi_object_list input; - union acpi_object in_params[4]; - union acpi_object *out_obj; + union acpi_object in_params[4], *out_obj; + struct acpi_buffer output; + acpi_status status = AE_OK; guid_t guid; - struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + int ret; - if (!context) + if (!context || !context->cap.pointer || + context->cap.length < 2 * sizeof(32) || + guid_parse(context->uuid_str, &guid)) + return AE_BAD_PARAMETER; + + ret = acpi_eval_osc(handle, &guid, context->rev, &context->cap, + in_params, &output); + if (ret) return AE_ERROR; - if (guid_parse(context->uuid_str, &guid)) - return AE_ERROR; - context->ret.length = ACPI_ALLOCATE_BUFFER; - context->ret.pointer = NULL; - - /* Setting up input parameters */ - input.count = 4; - input.pointer = in_params; - in_params[0].type = ACPI_TYPE_BUFFER; - in_params[0].buffer.length = 16; - in_params[0].buffer.pointer = (u8 *)&guid; - in_params[1].type = ACPI_TYPE_INTEGER; - in_params[1].integer.value = context->rev; - in_params[2].type = ACPI_TYPE_INTEGER; - in_params[2].integer.value = context->cap.length/sizeof(u32); - in_params[3].type = ACPI_TYPE_BUFFER; - in_params[3].buffer.length = context->cap.length; - in_params[3].buffer.pointer = context->cap.pointer; - - status = acpi_evaluate_object(handle, "_OSC", &input, &output); - if (ACPI_FAILURE(status)) - return status; - - if (!output.length) - return AE_NULL_OBJECT; out_obj = output.pointer; - if (out_obj->type != ACPI_TYPE_BUFFER - || out_obj->buffer.length != context->cap.length) { - acpi_dump_osc_data(handle, &guid, context->rev, &context->cap); - acpi_handle_debug(handle, "_OSC: evaluation returned wrong type"); - status = AE_TYPE; - goto out_kfree; - } /* Only take defined error bits into account. */ errors = *((u32 *)out_obj->buffer.pointer) & OSC_ERROR_MASK; /* From d179ae1f06ae1b3c328013f7224ab9ebfd210640 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 22 Dec 2025 20:17:29 +0100 Subject: [PATCH 08/18] ACPI: bus: Split _OSC error processing out of acpi_run_osc() Split a function for processing _OSC errors called acpi_osc_error_check() out of acpi_run_osc() to facilitate subsequent changes. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/6135328.MhkbZ0Pkbq@rafael.j.wysocki --- drivers/acpi/bus.c | 95 ++++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index ad828dcd0657..13ac46a70adb 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -237,13 +237,60 @@ static int acpi_eval_osc(acpi_handle handle, guid_t *guid, int rev, return 0; } +static bool acpi_osc_error_check(acpi_handle handle, guid_t *guid, int rev, + struct acpi_buffer *cap, u32 *retbuf) +{ + /* Only take defined error bits into account. */ + u32 errors = retbuf[OSC_QUERY_DWORD] & OSC_ERROR_MASK; + u32 *capbuf = cap->pointer; + bool fail; + + /* + * If OSC_QUERY_ENABLE is set, ignore the "capabilities masked" + * bit because it merely means that some features have not been + * acknowledged which is not unexpected. + */ + if (capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE) + errors &= ~OSC_CAPABILITIES_MASK_ERROR; + + if (!errors) + return false; + + acpi_dump_osc_data(handle, guid, rev, cap); + /* + * As a rule, fail only if OSC_QUERY_ENABLE is set because otherwise the + * acknowledged features need to be controlled. + */ + fail = !!(capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE); + + if (errors & OSC_REQUEST_ERROR) + acpi_handle_debug(handle, "_OSC: request failed\n"); + + if (errors & OSC_INVALID_UUID_ERROR) { + acpi_handle_debug(handle, "_OSC: invalid UUID\n"); + /* + * Always fail if this bit is set because it means that the + * request could not be processed. + */ + fail = true; + } + + if (errors & OSC_INVALID_REVISION_ERROR) + acpi_handle_debug(handle, "_OSC: invalid revision\n"); + + if (errors & OSC_CAPABILITIES_MASK_ERROR) + acpi_handle_debug(handle, "_OSC: capability bits masked\n"); + + return fail; +} + acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) { - u32 errors, *capbuf = context->cap.pointer; union acpi_object in_params[4], *out_obj; struct acpi_buffer output; acpi_status status = AE_OK; guid_t guid; + u32 *retbuf; int ret; if (!context || !context->cap.pointer || @@ -257,51 +304,15 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) return AE_ERROR; out_obj = output.pointer; - /* Only take defined error bits into account. */ - errors = *((u32 *)out_obj->buffer.pointer) & OSC_ERROR_MASK; - /* - * If OSC_QUERY_ENABLE is set, ignore the "capabilities masked" - * bit because it merely means that some features have not been - * acknowledged which is not unexpected. - */ - if (capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE) - errors &= ~OSC_CAPABILITIES_MASK_ERROR; + retbuf = (u32 *)out_obj->buffer.pointer; - if (errors) { - /* - * As a rule, fail only if OSC_QUERY_ENABLE is set because - * otherwise the acknowledged features need to be controlled. - */ - bool fail = !!(capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE); - - acpi_dump_osc_data(handle, &guid, context->rev, &context->cap); - if (errors & OSC_INVALID_UUID_ERROR) { - acpi_handle_debug(handle, "_OSC: invalid UUID"); - /* - * Always fail if this bit is set because it means that - * the request could not be processed. - */ - fail = true; - goto out_kfree; - } - if (errors & OSC_REQUEST_ERROR) - acpi_handle_debug(handle, "_OSC: request failed"); - - if (errors & OSC_INVALID_REVISION_ERROR) - acpi_handle_debug(handle, "_OSC: invalid revision"); - - if (errors & OSC_CAPABILITIES_MASK_ERROR) - acpi_handle_debug(handle, "_OSC: capability bits masked"); - - if (fail) { - status = AE_ERROR; - goto out_kfree; - } + if (acpi_osc_error_check(handle, &guid, context->rev, &context->cap, retbuf)) { + status = AE_ERROR; + goto out_kfree; } context->ret.length = out_obj->buffer.length; - context->ret.pointer = kmemdup(out_obj->buffer.pointer, - context->ret.length, GFP_KERNEL); + context->ret.pointer = kmemdup(retbuf, context->ret.length, GFP_KERNEL); if (!context->ret.pointer) { status = AE_NO_MEMORY; goto out_kfree; From 5ada805104d4fd89504206216f297c112382bfd1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 22 Dec 2025 20:18:40 +0100 Subject: [PATCH 09/18] ACPI: bus: Rename label and use ACPI_FREE() in acpi_run_osc() Use ACPI_FREE() for freeing an object coming from acpi_eval_osc() and rename the "out_free" to "out" because it does not involve kfree() any more. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/8682086.NyiUUSuA9g@rafael.j.wysocki --- drivers/acpi/bus.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 13ac46a70adb..2232cb872a94 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -308,19 +308,19 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) if (acpi_osc_error_check(handle, &guid, context->rev, &context->cap, retbuf)) { status = AE_ERROR; - goto out_kfree; + goto out; } context->ret.length = out_obj->buffer.length; context->ret.pointer = kmemdup(retbuf, context->ret.length, GFP_KERNEL); if (!context->ret.pointer) { status = AE_NO_MEMORY; - goto out_kfree; + goto out; } status = AE_OK; -out_kfree: - kfree(output.pointer); +out: + ACPI_FREE(out_obj); return status; } EXPORT_SYMBOL(acpi_run_osc); From e5322888e6bf4ec17964a93638c9b14433a2f6f1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 22 Dec 2025 20:21:19 +0100 Subject: [PATCH 10/18] ACPI: bus: Rework the handling of \_SB._OSC platform features Both acpi_bus_osc_negotiate_platform_control() and acpi_bus_osc_negotiate_usb_control() first call acpi_run_osc() to evaluate _OSC in "query mode", with OSC_QUERY_ENABLE set in the capabilities buffer, and then use the resultant feature mask as the input buffer for requesting control of those features by calling acpi_run_osc() to evaluate _OSC with OSC_QUERY_ENABLE clear. This involves some code duplication and unnecessary memory allocations, so introduce a new helper function carrying out an _OSC handshake along the lines of the above description in a simpler way and update acpi_bus_osc_negotiate_platform_control() to use it. Signed-off-by: Rafael J. Wysocki Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/1966378.CQOukoFCf9@rafael.j.wysocki --- drivers/acpi/bus.c | 141 ++++++++++++++++++++++++++++++++------------- 1 file changed, 101 insertions(+), 40 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 2232cb872a94..15168196a6a5 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -325,6 +325,92 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) } EXPORT_SYMBOL(acpi_run_osc); +static int acpi_osc_handshake(acpi_handle handle, const char *uuid_str, + int rev, struct acpi_buffer *cap) +{ + union acpi_object in_params[4], *out_obj; + size_t bufsize = cap->length / sizeof(u32); + struct acpi_object_list input; + struct acpi_buffer output; + u32 *capbuf, *retbuf, test; + guid_t guid; + int ret, i; + + if (!cap || cap->length < 2 * sizeof(32) || guid_parse(uuid_str, &guid)) + return -EINVAL; + + /* First evaluate _OSC with OSC_QUERY_ENABLE set. */ + capbuf = cap->pointer; + capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; + + ret = acpi_eval_osc(handle, &guid, rev, cap, in_params, &output); + if (ret) + return ret; + + out_obj = output.pointer; + retbuf = (u32 *)out_obj->buffer.pointer; + + if (acpi_osc_error_check(handle, &guid, rev, cap, retbuf)) { + ret = -ENODATA; + goto out; + } + + /* + * Clear the feature bits in the capabilities buffer that have not been + * acknowledged and clear the return buffer. + */ + for (i = OSC_QUERY_DWORD + 1, test = 0; i < bufsize; i++) { + capbuf[i] &= retbuf[i]; + test |= capbuf[i]; + retbuf[i] = 0; + } + /* + * If none of the feature bits have been acknowledged, there's nothing + * more to do. capbuf[] contains a feature mask of all zeros. + */ + if (!test) + goto out; + + retbuf[OSC_QUERY_DWORD] = 0; + /* + * Now evaluate _OSC again (directly) with OSC_QUERY_ENABLE clear and + * the updated input and output buffers used before. Since the feature + * bits that were clear in the return buffer from the previous _OSC + * evaluation are also clear in the capabilities buffer now, this _OSC + * evaluation is not expected to fail. + */ + capbuf[OSC_QUERY_DWORD] = 0; + /* Reuse in_params[] populated by acpi_eval_osc(). */ + input.pointer = in_params; + input.count = 4; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, "_OSC", &input, &output))) { + ret = -ENODATA; + goto out; + } + + /* + * Clear the feature bits in capbuf[] that have not been acknowledged. + * After that, capbuf[] contains the resultant feature mask. + */ + for (i = OSC_QUERY_DWORD + 1; i < bufsize; i++) + capbuf[i] &= retbuf[i]; + + if (retbuf[OSC_QUERY_DWORD] & OSC_ERROR_MASK) { + /* + * Complain about the unexpected errors and print diagnostic + * information related to them. + */ + acpi_handle_err(handle, "_OSC: errors while processing control request\n"); + acpi_handle_err(handle, "_OSC: some features may be missing\n"); + acpi_osc_error_check(handle, &guid, rev, cap, retbuf); + } + +out: + ACPI_FREE(out_obj); + return ret; +} + bool osc_sb_apei_support_acked; /* @@ -356,19 +442,16 @@ EXPORT_SYMBOL_GPL(osc_sb_native_usb4_support_confirmed); bool osc_sb_cppc2_support_acked; -static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48"; static void acpi_bus_osc_negotiate_platform_control(void) { - u32 capbuf[2], *capbuf_ret; - struct acpi_osc_context context = { - .uuid_str = sb_uuid_str, - .rev = 1, - .cap.length = 8, - .cap.pointer = capbuf, + static const u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48"; + u32 capbuf[2]; + struct acpi_buffer cap = { + .pointer = capbuf, + .length = sizeof(capbuf), }; acpi_handle handle; - capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; capbuf[OSC_SUPPORT_DWORD] = OSC_SB_PR3_SUPPORT; /* _PR3 is in use */ if (IS_ENABLED(CONFIG_ACPI_PROCESSOR_AGGREGATOR)) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PAD_SUPPORT; @@ -414,43 +497,21 @@ static void acpi_bus_osc_negotiate_platform_control(void) if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) return; - if (ACPI_FAILURE(acpi_run_osc(handle, &context))) + if (acpi_osc_handshake(handle, sb_uuid_str, 1, &cap)) return; - capbuf_ret = context.ret.pointer; - if (context.ret.length <= OSC_SUPPORT_DWORD) { - kfree(context.ret.pointer); - return; - } - - /* - * Now run _OSC again with query flag clear and with the caps - * supported by both the OS and the platform. - */ - capbuf[OSC_QUERY_DWORD] = 0; - capbuf[OSC_SUPPORT_DWORD] = capbuf_ret[OSC_SUPPORT_DWORD]; - kfree(context.ret.pointer); - - if (ACPI_FAILURE(acpi_run_osc(handle, &context))) - return; - - capbuf_ret = context.ret.pointer; - if (context.ret.length > OSC_SUPPORT_DWORD) { #ifdef CONFIG_ACPI_CPPC_LIB - osc_sb_cppc2_support_acked = capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_CPCV2_SUPPORT; + osc_sb_cppc2_support_acked = capbuf[OSC_SUPPORT_DWORD] & OSC_SB_CPCV2_SUPPORT; #endif - osc_sb_apei_support_acked = - capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_APEI_SUPPORT; - osc_pc_lpi_support_confirmed = - capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_PCLPI_SUPPORT; - osc_sb_native_usb4_support_confirmed = - capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_NATIVE_USB4_SUPPORT; - osc_cpc_flexible_adr_space_confirmed = - capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_CPC_FLEXIBLE_ADR_SPACE; - } - - kfree(context.ret.pointer); + osc_sb_apei_support_acked = + capbuf[OSC_SUPPORT_DWORD] & OSC_SB_APEI_SUPPORT; + osc_pc_lpi_support_confirmed = + capbuf[OSC_SUPPORT_DWORD] & OSC_SB_PCLPI_SUPPORT; + osc_sb_native_usb4_support_confirmed = + capbuf[OSC_SUPPORT_DWORD] & OSC_SB_NATIVE_USB4_SUPPORT; + osc_cpc_flexible_adr_space_confirmed = + capbuf[OSC_SUPPORT_DWORD] & OSC_SB_CPC_FLEXIBLE_ADR_SPACE; } /* From 6485059361923236735f763af3fc9cbb909fc6dd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 22 Dec 2025 20:23:29 +0100 Subject: [PATCH 11/18] ACPI: bus: Adjust feature mask creation for \_SB._OSC The feature mask creation for \_SB._OSC is messy and hard to follow, so clean it up and make all of the CPPC-related features depend on CONFIG_ACPI_CPPC_LIB as they will not work if it is not set anyway. Also make acpi_bus_osc_negotiate_platform_control() print a message including a bit mask representig the features for which control has been granted. Signed-off-by: Rafael J. Wysocki Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/4495088.ejJDZkT8p0@rafael.j.wysocki --- drivers/acpi/bus.c | 83 ++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 43 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 15168196a6a5..3a6ce53aef47 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -445,73 +445,70 @@ bool osc_sb_cppc2_support_acked; static void acpi_bus_osc_negotiate_platform_control(void) { static const u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48"; - u32 capbuf[2]; + u32 capbuf[2], feature_mask; struct acpi_buffer cap = { .pointer = capbuf, .length = sizeof(capbuf), }; acpi_handle handle; - capbuf[OSC_SUPPORT_DWORD] = OSC_SB_PR3_SUPPORT; /* _PR3 is in use */ + feature_mask = OSC_SB_PR3_SUPPORT | OSC_SB_HOTPLUG_OST_SUPPORT | + OSC_SB_PCLPI_SUPPORT | OSC_SB_OVER_16_PSTATES_SUPPORT | + OSC_SB_GED_SUPPORT | OSC_SB_IRQ_RESOURCE_SOURCE_SUPPORT; + + if (IS_ENABLED(CONFIG_ARM64) || IS_ENABLED(CONFIG_X86)) + feature_mask |= OSC_SB_GENERIC_INITIATOR_SUPPORT; + + if (IS_ENABLED(CONFIG_ACPI_CPPC_LIB)) { + feature_mask |= OSC_SB_CPC_SUPPORT | OSC_SB_CPCV2_SUPPORT | + OSC_SB_CPC_FLEXIBLE_ADR_SPACE; + if (IS_ENABLED(CONFIG_SCHED_MC_PRIO)) + feature_mask |= OSC_SB_CPC_DIVERSE_HIGH_SUPPORT; + } + if (IS_ENABLED(CONFIG_ACPI_PROCESSOR_AGGREGATOR)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PAD_SUPPORT; + feature_mask |= OSC_SB_PAD_SUPPORT; + if (IS_ENABLED(CONFIG_ACPI_PROCESSOR)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PPC_OST_SUPPORT; + feature_mask |= OSC_SB_PPC_OST_SUPPORT; + if (IS_ENABLED(CONFIG_ACPI_THERMAL)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_FAST_THERMAL_SAMPLING_SUPPORT; + feature_mask |= OSC_SB_FAST_THERMAL_SAMPLING_SUPPORT; + if (IS_ENABLED(CONFIG_ACPI_BATTERY)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_BATTERY_CHARGE_LIMITING_SUPPORT; + feature_mask |= OSC_SB_BATTERY_CHARGE_LIMITING_SUPPORT; - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_OST_SUPPORT; - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PCLPI_SUPPORT; - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_OVER_16_PSTATES_SUPPORT; - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_GED_SUPPORT; - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_IRQ_RESOURCE_SOURCE_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_PRMT)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PRM_SUPPORT; + feature_mask |= OSC_SB_PRM_SUPPORT; + if (IS_ENABLED(CONFIG_ACPI_FFH)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_FFH_OPR_SUPPORT; - -#ifdef CONFIG_ARM64 - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_GENERIC_INITIATOR_SUPPORT; -#endif -#ifdef CONFIG_X86 - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_GENERIC_INITIATOR_SUPPORT; -#endif - -#ifdef CONFIG_ACPI_CPPC_LIB - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_CPC_SUPPORT; - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_CPCV2_SUPPORT; -#endif - - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_CPC_FLEXIBLE_ADR_SPACE; - - if (IS_ENABLED(CONFIG_SCHED_MC_PRIO)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_CPC_DIVERSE_HIGH_SUPPORT; + feature_mask |= OSC_SB_FFH_OPR_SUPPORT; if (IS_ENABLED(CONFIG_USB4)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_NATIVE_USB4_SUPPORT; + feature_mask |= OSC_SB_NATIVE_USB4_SUPPORT; if (!ghes_disable) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_APEI_SUPPORT; + feature_mask |= OSC_SB_APEI_SUPPORT; + if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) return; + capbuf[OSC_SUPPORT_DWORD] = feature_mask; + + acpi_handle_info(handle, "platform _OSC: OS support mask [%08x]\n", feature_mask); + if (acpi_osc_handshake(handle, sb_uuid_str, 1, &cap)) return; -#ifdef CONFIG_ACPI_CPPC_LIB - osc_sb_cppc2_support_acked = capbuf[OSC_SUPPORT_DWORD] & OSC_SB_CPCV2_SUPPORT; -#endif + feature_mask = capbuf[OSC_SUPPORT_DWORD]; - osc_sb_apei_support_acked = - capbuf[OSC_SUPPORT_DWORD] & OSC_SB_APEI_SUPPORT; - osc_pc_lpi_support_confirmed = - capbuf[OSC_SUPPORT_DWORD] & OSC_SB_PCLPI_SUPPORT; - osc_sb_native_usb4_support_confirmed = - capbuf[OSC_SUPPORT_DWORD] & OSC_SB_NATIVE_USB4_SUPPORT; - osc_cpc_flexible_adr_space_confirmed = - capbuf[OSC_SUPPORT_DWORD] & OSC_SB_CPC_FLEXIBLE_ADR_SPACE; + acpi_handle_info(handle, "platform _OSC: OS control mask [%08x]\n", feature_mask); + + osc_sb_cppc2_support_acked = feature_mask & OSC_SB_CPCV2_SUPPORT; + osc_sb_apei_support_acked = feature_mask & OSC_SB_APEI_SUPPORT; + osc_pc_lpi_support_confirmed = feature_mask & OSC_SB_PCLPI_SUPPORT; + osc_sb_native_usb4_support_confirmed = feature_mask & OSC_SB_NATIVE_USB4_SUPPORT; + osc_cpc_flexible_adr_space_confirmed = feature_mask & OSC_SB_CPC_FLEXIBLE_ADR_SPACE; } /* From d9239fdc14bcf69fd153ca5daae22c12bd3f8164 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 22 Dec 2025 20:26:36 +0100 Subject: [PATCH 12/18] ACPI: bus: Rework the handling of \_SB._OSC USB4 features Use acpi_osc_handshake() introduced previously for implementing the \_SB._OSC USB4 features control negotiation. Signed-off-by: Rafael J. Wysocki Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/3879947.MHq7AAxBmi@rafael.j.wysocki --- drivers/acpi/bus.c | 58 +++++++--------------------------------------- 1 file changed, 8 insertions(+), 50 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 3a6ce53aef47..d0cb47c77446 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -528,19 +528,15 @@ static void acpi_bus_decode_usb_osc(const char *msg, u32 bits) (bits & OSC_USB_XDOMAIN) ? '+' : '-'); } -static u8 sb_usb_uuid_str[] = "23A0D13A-26AB-486C-9C5F-0FFA525A575A"; static void acpi_bus_osc_negotiate_usb_control(void) { - u32 capbuf[3], *capbuf_ret; - struct acpi_osc_context context = { - .uuid_str = sb_usb_uuid_str, - .rev = 1, - .cap.length = sizeof(capbuf), - .cap.pointer = capbuf, + static const u8 sb_usb_uuid_str[] = "23A0D13A-26AB-486C-9C5F-0FFA525A575A"; + u32 capbuf[3], control; + struct acpi_buffer cap = { + .pointer = capbuf, + .length = sizeof(capbuf), }; acpi_handle handle; - acpi_status status; - u32 control; if (!osc_sb_native_usb4_support_confirmed) return; @@ -551,54 +547,16 @@ static void acpi_bus_osc_negotiate_usb_control(void) control = OSC_USB_USB3_TUNNELING | OSC_USB_DP_TUNNELING | OSC_USB_PCIE_TUNNELING | OSC_USB_XDOMAIN; - /* - * Run _OSC first with query bit set, trying to get control over - * all tunneling. The platform can then clear out bits in the - * control dword that it does not want to grant to the OS. - */ - capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; capbuf[OSC_SUPPORT_DWORD] = 0; capbuf[OSC_CONTROL_DWORD] = control; - status = acpi_run_osc(handle, &context); - if (ACPI_FAILURE(status)) + if (acpi_osc_handshake(handle, sb_usb_uuid_str, 1, &cap)) return; - if (context.ret.length != sizeof(capbuf)) { - pr_info("USB4 _OSC: returned invalid length buffer\n"); - goto out_free; - } - - /* - * Run _OSC again now with query bit clear and the control dword - * matching what the platform granted (which may not have all - * the control bits set). - */ - capbuf_ret = context.ret.pointer; - - capbuf[OSC_QUERY_DWORD] = 0; - capbuf[OSC_CONTROL_DWORD] = capbuf_ret[OSC_CONTROL_DWORD]; - - kfree(context.ret.pointer); - - status = acpi_run_osc(handle, &context); - if (ACPI_FAILURE(status)) - return; - - if (context.ret.length != sizeof(capbuf)) { - pr_info("USB4 _OSC: returned invalid length buffer\n"); - goto out_free; - } - - osc_sb_native_usb4_control = - control & acpi_osc_ctx_get_pci_control(&context); + osc_sb_native_usb4_control = capbuf[OSC_CONTROL_DWORD]; acpi_bus_decode_usb_osc("USB4 _OSC: OS supports", control); - acpi_bus_decode_usb_osc("USB4 _OSC: OS controls", - osc_sb_native_usb4_control); - -out_free: - kfree(context.ret.pointer); + acpi_bus_decode_usb_osc("USB4 _OSC: OS controls", osc_sb_native_usb4_control); } /* -------------------------------------------------------------------------- From 06a17f2beab8c7e9eb9e63c7f89a62a4575cd95b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 26 Dec 2025 14:48:45 +0100 Subject: [PATCH 13/18] ACPI: bus: Adjust acpi_osc_handshake() parameter list For the sake of interface cleanliness, it is better to avoid using ACPICA data types in the parameter lists of helper functions that don't belong to ACPICA, so adjust the parameter list of recently introduced acpi_osc_handshake() to take a capabilities buffer pointer and the size of the buffer (in u32 size units) as parameters directly instead of a struct acpi_buffer pointer. This is also somewhat more straightforward on the caller side because they won't need to create struct acpi_buffer objects themselves to pass them to the helper function and it guarantees that the size of the buffer in bytes will always be a multiple of 4 (the size of u32). Moreover, it addresses a premature cap pointer dereference and eliminates a sizeof(32) that should have been sizeof(u32) [1]. Fixes: e5322888e6bf ("ACPI: bus: Rework the handling of \_SB._OSC platform features") Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-acpi/202512242052.W4GhDauV-lkp@intel.com/ Signed-off-by: Rafael J. Wysocki Reviewed-by: Jonathan Cameron [ rjw: Fixed typo under sizeof(), used ARRAY_SIZE() in two places ] Link: https://patch.msgid.link/12833187.O9o76ZdvQC@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index d0cb47c77446..4e04b5946364 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -326,31 +326,33 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) EXPORT_SYMBOL(acpi_run_osc); static int acpi_osc_handshake(acpi_handle handle, const char *uuid_str, - int rev, struct acpi_buffer *cap) + int rev, u32 *capbuf, size_t bufsize) { union acpi_object in_params[4], *out_obj; - size_t bufsize = cap->length / sizeof(u32); struct acpi_object_list input; + struct acpi_buffer cap = { + .pointer = capbuf, + .length = bufsize * sizeof(u32), + }; struct acpi_buffer output; - u32 *capbuf, *retbuf, test; + u32 *retbuf, test; guid_t guid; int ret, i; - if (!cap || cap->length < 2 * sizeof(32) || guid_parse(uuid_str, &guid)) + if (!capbuf || bufsize < 2 || guid_parse(uuid_str, &guid)) return -EINVAL; /* First evaluate _OSC with OSC_QUERY_ENABLE set. */ - capbuf = cap->pointer; capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; - ret = acpi_eval_osc(handle, &guid, rev, cap, in_params, &output); + ret = acpi_eval_osc(handle, &guid, rev, &cap, in_params, &output); if (ret) return ret; out_obj = output.pointer; retbuf = (u32 *)out_obj->buffer.pointer; - if (acpi_osc_error_check(handle, &guid, rev, cap, retbuf)) { + if (acpi_osc_error_check(handle, &guid, rev, &cap, retbuf)) { ret = -ENODATA; goto out; } @@ -403,7 +405,7 @@ static int acpi_osc_handshake(acpi_handle handle, const char *uuid_str, */ acpi_handle_err(handle, "_OSC: errors while processing control request\n"); acpi_handle_err(handle, "_OSC: some features may be missing\n"); - acpi_osc_error_check(handle, &guid, rev, cap, retbuf); + acpi_osc_error_check(handle, &guid, rev, &cap, retbuf); } out: @@ -446,10 +448,6 @@ static void acpi_bus_osc_negotiate_platform_control(void) { static const u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48"; u32 capbuf[2], feature_mask; - struct acpi_buffer cap = { - .pointer = capbuf, - .length = sizeof(capbuf), - }; acpi_handle handle; feature_mask = OSC_SB_PR3_SUPPORT | OSC_SB_HOTPLUG_OST_SUPPORT | @@ -497,7 +495,7 @@ static void acpi_bus_osc_negotiate_platform_control(void) acpi_handle_info(handle, "platform _OSC: OS support mask [%08x]\n", feature_mask); - if (acpi_osc_handshake(handle, sb_uuid_str, 1, &cap)) + if (acpi_osc_handshake(handle, sb_uuid_str, 1, capbuf, ARRAY_SIZE(capbuf))) return; feature_mask = capbuf[OSC_SUPPORT_DWORD]; @@ -532,10 +530,6 @@ static void acpi_bus_osc_negotiate_usb_control(void) { static const u8 sb_usb_uuid_str[] = "23A0D13A-26AB-486C-9C5F-0FFA525A575A"; u32 capbuf[3], control; - struct acpi_buffer cap = { - .pointer = capbuf, - .length = sizeof(capbuf), - }; acpi_handle handle; if (!osc_sb_native_usb4_support_confirmed) @@ -550,7 +544,7 @@ static void acpi_bus_osc_negotiate_usb_control(void) capbuf[OSC_SUPPORT_DWORD] = 0; capbuf[OSC_CONTROL_DWORD] = control; - if (acpi_osc_handshake(handle, sb_usb_uuid_str, 1, &cap)) + if (acpi_osc_handshake(handle, sb_usb_uuid_str, 1, capbuf, ARRAY_SIZE(capbuf))) return; osc_sb_native_usb4_control = capbuf[OSC_CONTROL_DWORD]; From eed8f21a94f86e1b25d945fabbd50f822781e41d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 29 Dec 2025 14:27:46 +0100 Subject: [PATCH 14/18] ACPI: bus: Fix typo under sizeof() in acpi_run_osc() The sizeof(32) in acpi_run_osc() should be sizeof(u32), so fix it. Fixes: e5322888e6bf ("ACPI: bus: Rework the handling of \_SB._OSC platform features") Signed-off-by: Rafael J. Wysocki Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/2817106.mvXUDI8C0e@rafael.j.wysocki --- drivers/acpi/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 4e04b5946364..06b51886bef8 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -294,7 +294,7 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) int ret; if (!context || !context->cap.pointer || - context->cap.length < 2 * sizeof(32) || + context->cap.length < 2 * sizeof(u32) || guid_parse(context->uuid_str, &guid)) return AE_BAD_PARAMETER; From ff8f624860e1cd31f32d2c18de291ca727f8ddcd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 30 Dec 2025 10:57:34 +0100 Subject: [PATCH 15/18] ACPI: scan: Use resource_type() for resource type checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To follow a well-established existing pattern, use resource_type() for resource type checking in acpi_scan_claim_resources(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Reviewed-by: Ilpo Järvinen Reviewed-by: Jonathan Cameron Link: https://patch.msgid.link/12814730.O9o76ZdvQC@rafael.j.wysocki --- drivers/acpi/scan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8a895d377e21..e9b09a3040f0 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2623,7 +2623,7 @@ static void acpi_scan_claim_resources(struct acpi_device *adev) if ((res->flags & IORESOURCE_DISABLED) || res->end < res->start) continue; - if (res->flags & IORESOURCE_IO) { + if (resource_type(res) == IORESOURCE_IO) { /* * Follow the PNP system driver and on x86 skip I/O * resources that start below 0x100 (the "standard PC @@ -2634,7 +2634,7 @@ static void acpi_scan_claim_resources(struct acpi_device *adev) continue; } r = request_region(res->start, resource_size(res), regionid); - } else if (res->flags & IORESOURCE_MEM) { + } else if (resource_type(res) == IORESOURCE_MEM) { r = request_mem_region(res->start, resource_size(res), regionid); } else { continue; From b2f90ef5ded439cb01fc076a6f66c5cde5e633c5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 30 Dec 2025 17:06:44 +0100 Subject: [PATCH 16/18] ACPI: scan: Drop outdated comment regarding removed function The function acpi_video_get_capabilities() was removed from drivers/acpi/video_detect.c by commit 87521e16a7ab ("acpi-video-detect: Rewrite backlight interface selection logic") in 2015. At the time, comments about this function were just removed, and no replacement seemed to be proposed. Drop the reference to acpi_video_get_capabilities() here as well. Signed-off-by: Julia Lawall [ rjw: Subject adjustment, changelog edits ] Link: https://patch.msgid.link/20251230160644.100439-1-Julia.Lawall@inria.fr Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index e9b09a3040f0..26ea73abe39f 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1295,8 +1295,6 @@ acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context, * The device will get a Linux specific CID added in scan.c to * identify the device as an ACPI graphics device * Be aware that the graphics device may not be physically present - * Use acpi_video_get_capabilities() to detect general ACPI video - * capabilities of present cards */ long acpi_is_video_device(acpi_handle handle) { From 8567b5733715e959474851dbec666aafcdba86e8 Mon Sep 17 00:00:00 2001 From: Kartik Rajput Date: Wed, 14 Jan 2026 13:53:06 +0530 Subject: [PATCH 17/18] ACPI: bus: Align acpi_device_get_match_data() with driver match order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During pre-production development, drivers may provide both ACPI and OF match tables while a formal ACPI HID for the device is not yet allocated. Such devices are enumerated via PRP0001. In this case, acpi_device_get_match_data() consults only the driver’s ACPI match table and returns NULL, even though the device was successfully matched via PRP0001. This behavior also risks breaking existing PRP0001 setups if a driver later gains an ACPI HID, as the presence of an ACPI match table changes the match-data lookup path. Make acpi_device_get_match_data() use the same precedence as driver matching by using __acpi_match_device(). Return match data from the acpi_id or of_id that was actually matched. Remove now-unused acpi_of_device_get_match_data(). Signed-off-by: Kartik Rajput Reviewed-by: Andy Shevchenko Reviewed-by: Sakari Ailus Link: https://patch.msgid.link/20260114082306.48119-1-kkartik@nvidia.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 06b51886bef8..a616e62c1ab0 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -1013,30 +1013,24 @@ const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, } EXPORT_SYMBOL_GPL(acpi_match_device); -static const void *acpi_of_device_get_match_data(const struct device *dev) -{ - struct acpi_device *adev = ACPI_COMPANION(dev); - const struct of_device_id *match = NULL; - - if (!acpi_of_match_device(adev, dev->driver->of_match_table, &match)) - return NULL; - - return match->data; -} - const void *acpi_device_get_match_data(const struct device *dev) { const struct acpi_device_id *acpi_ids = dev->driver->acpi_match_table; - const struct acpi_device_id *match; + const struct of_device_id *of_ids = dev->driver->of_match_table; + const struct acpi_device *adev = acpi_companion_match(dev); + const struct acpi_device_id *acpi_id = NULL; + const struct of_device_id *of_id = NULL; - if (!acpi_ids) - return acpi_of_device_get_match_data(dev); - - match = acpi_match_device(acpi_ids, dev); - if (!match) + if (!__acpi_match_device(adev, acpi_ids, of_ids, &acpi_id, &of_id)) return NULL; - return (const void *)match->driver_data; + if (acpi_id) + return (const void *)acpi_id->driver_data; + + if (of_id) + return of_id->data; + + return NULL; } EXPORT_SYMBOL_GPL(acpi_device_get_match_data); From 7cf28b3797a81b616bb7eb3e90cf131afc452919 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Wed, 28 Jan 2026 21:28:47 +0800 Subject: [PATCH 18/18] ACPI: scan: Use async schedule function in acpi_scan_clear_dep_fn() The device object rescan in acpi_scan_clear_dep_fn() is scheduled on a system workqueue which is not guaranteed to be finished before entering userspace. This may cause some key devices to be missing when userspace init task tries to find them. Two issues observed on RISCV platforms: - Kernel panic due to userspace init cannot have an opened console. The console device scanning is queued by acpi_scan_clear_dep_queue() and not finished by the time userspace init process running, thus by the time userspace init runs, no console is present. - Entering rescue shell due to the lack of root devices (PCIe nvme in our case). Same reason as above, the PCIe host bridge scanning is queued on a system workqueue and finished after init process runs. The reason is because both devices (console, PCIe host bridge) depend on riscv-aplic irqchip to serve their interrupts (console's wired interrupt and PCI's INTx interrupts). In order to keep the dependency, these devices are scanned and created after initializing riscv-aplic. The riscv-aplic is initialized in device_initcall() and a device scan work is queued via acpi_scan_clear_dep_queue(), which is close to the time userspace init process is run. Since system_dfl_wq is used in acpi_scan_clear_dep_queue() with no synchronization, the issues will happen if userspace init runs before these devices are ready. The solution is to wait for the queued work to complete before entering userspace init. One possible way would be to use a dedicated workqueue instead of system_dfl_wq, and explicitly flush it somewhere in the initcall stage before entering userspace. Another way is to use async_schedule_dev_nocall() for scanning these devices. It's designed for asynchronous initialization and will work in the same way as before because it's using a dedicated unbound workqueue as well, but the kernel init code calls async_synchronize_full() right before entering userspace init which will wait for the work to complete. Compared to a dedicated workqueue, the second approach is simpler because the async schedule framework takes care of all of the details. The ACPI code only needs to focus on its job. A dedicated workqueue for this could also be redundant because some platforms don't need acpi_scan_clear_dep_queue() for their device scanning. Signed-off-by: Yicong Yang [ rjw: Subject adjustment, changelog edits ] Link: https://patch.msgid.link/20260128132848.93638-1-yang.yicong@picoheart.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 41 +++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 26ea73abe39f..fc40dcfa5ffc 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) "ACPI: " fmt +#include #include #include #include @@ -2388,46 +2389,34 @@ static int acpi_dev_get_next_consumer_dev_cb(struct acpi_dep_data *dep, void *da return 0; } -struct acpi_scan_clear_dep_work { - struct work_struct work; - struct acpi_device *adev; -}; - -static void acpi_scan_clear_dep_fn(struct work_struct *work) +static void acpi_scan_clear_dep_fn(void *dev, async_cookie_t cookie) { - struct acpi_scan_clear_dep_work *cdw; - - cdw = container_of(work, struct acpi_scan_clear_dep_work, work); + struct acpi_device *adev = to_acpi_device(dev); acpi_scan_lock_acquire(); - acpi_bus_attach(cdw->adev, (void *)true); + acpi_bus_attach(adev, (void *)true); acpi_scan_lock_release(); - acpi_dev_put(cdw->adev); - kfree(cdw); + acpi_dev_put(adev); } static bool acpi_scan_clear_dep_queue(struct acpi_device *adev) { - struct acpi_scan_clear_dep_work *cdw; - if (adev->dep_unmet) return false; - cdw = kmalloc(sizeof(*cdw), GFP_KERNEL); - if (!cdw) - return false; - - cdw->adev = adev; - INIT_WORK(&cdw->work, acpi_scan_clear_dep_fn); /* - * Since the work function may block on the lock until the entire - * initial enumeration of devices is complete, put it into the unbound - * workqueue. + * Async schedule the deferred acpi_scan_clear_dep_fn() since: + * - acpi_bus_attach() needs to hold acpi_scan_lock which cannot + * be acquired under acpi_dep_list_lock (held here) + * - the deferred work at boot stage is ensured to be finished + * before userspace init task by the async_synchronize_full() + * barrier + * + * Use _nocall variant since it'll return on failure instead of + * run the function synchronously. */ - queue_work(system_dfl_wq, &cdw->work); - - return true; + return async_schedule_dev_nocall(acpi_scan_clear_dep_fn, &adev->dev); } static void acpi_scan_delete_dep_data(struct acpi_dep_data *dep)