From: Markus Probst Date: Mon, 27 Apr 2026 17:55:57 +0000 (+0000) Subject: rust: ACPI: fix missing match data for PRP0001 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2690d071584ed8f488f2336f93272817b6999484;p=thirdparty%2Fkernel%2Flinux.git rust: ACPI: fix missing match data for PRP0001 Export `acpi_of_match_device` function and use it to match the of device table against ACPI PRP0001 in Rust. This fixes id_info being None on ACPI PRP0001 devices. Using `device_get_match_data` is not possible, because Rust stores an index in the of device id instead of a data pointer. This was done this way to provide a convenient and obvious API for drivers, which can be evaluated in const context without the use of any unstable language features. Fixes: 7a718a1f26d1 ("rust: driver: implement `Adapter`") Signed-off-by: Markus Probst Acked-by: Rafael J. Wysocki (Intel) # ACPI Link: https://patch.msgid.link/20260427-rust_acpi_prp0001-v6-1-6119b2a66183@posteo.de Signed-off-by: Danilo Krummrich --- diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd163..4367a303e90e3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -291,6 +291,7 @@ F: include/linux/acpi.h F: include/linux/fwnode.h F: include/linux/fw_table.h F: lib/fw_table.c +F: rust/helpers/acpi.c F: rust/kernel/acpi.rs F: tools/power/acpi/ diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 2ec095e2009e4..554dbddb1c8cf 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -831,9 +831,9 @@ const struct acpi_device *acpi_companion_match(const struct device *dev) * identifiers and a _DSD object with the "compatible" property, use that * property to match against the given list of identifiers. */ -static bool acpi_of_match_device(const struct acpi_device *adev, - const struct of_device_id *of_match_table, - const struct of_device_id **of_id) +bool acpi_of_match_device(const struct acpi_device *adev, + const struct of_device_id *of_match_table, + const struct of_device_id **of_id) { const union acpi_object *of_compatible, *obj; int i, nval; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index b701b5f972cb4..c85f7d8f1632c 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -187,6 +187,10 @@ struct acpi_driver { * ----------- */ +bool acpi_of_match_device(const struct acpi_device *adev, + const struct of_device_id *of_match_table, + const struct of_device_id **of_id); + /* Status (_STA) */ struct acpi_device_status { @@ -992,6 +996,13 @@ int acpi_scan_add_dep(acpi_handle handle, struct acpi_handle_list *dep_devices); u32 arch_acpi_add_auto_dep(acpi_handle handle); #else /* CONFIG_ACPI */ +static inline bool acpi_of_match_device(const struct acpi_device *adev, + const struct of_device_id *of_match_table, + const struct of_device_id **of_id) +{ + return false; +} + static inline int register_acpi_bus_type(void *bus) { return 0; } static inline int unregister_acpi_bus_type(void *bus) { return 0; } diff --git a/rust/helpers/acpi.c b/rust/helpers/acpi.c new file mode 100644 index 0000000000000..e75c9807bbadc --- /dev/null +++ b/rust/helpers/acpi.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +__rust_helper bool rust_helper_acpi_of_match_device(const struct acpi_device *adev, + const struct of_device_id *of_match_table, + const struct of_device_id **of_id) +{ + return acpi_of_match_device(adev, of_match_table, of_id); +} + +__rust_helper struct acpi_device *rust_helper_to_acpi_device_node(struct fwnode_handle *fwnode) +{ + return to_acpi_device_node(fwnode); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 625921e27dfbc..38b34518eff14 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -38,6 +38,7 @@ #define __rust_helper __always_inline #endif +#include "acpi.c" #include "atomic.c" #include "atomic_ext.c" #include "auxiliary.c" diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index 36de8098754d0..93e5dd6ae3715 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -278,6 +278,26 @@ macro_rules! module_driver { } } +// Calling the FFI function directly from the `Adapter` impl may result in it being called +// directly from driver modules. This happens since the Rust compiler will use monomorphisation, so +// it might happen that functions are instantiated within the calling driver module. For now, work +// around this with `#[inline(never)]` helpers. +// +// TODO: Remove once a more generic solution has been implemented. For instance, we may be able to +// leverage `bindgen` to take care of this depending on whether a symbol is (already) exported. +#[inline(never)] +#[allow(clippy::missing_safety_doc)] +#[allow(dead_code)] +#[must_use] +unsafe fn acpi_of_match_device( + adev: *const bindings::acpi_device, + of_match_table: *const bindings::of_device_id, + of_id: *mut *const bindings::of_device_id, +) -> bool { + // SAFETY: Safety requirements are the same as `bindings::acpi_of_match_device`. + unsafe { bindings::acpi_of_match_device(adev, of_match_table, of_id) } +} + /// The bus independent adapter to match a drivers and a devices. /// /// This trait should be implemented by the bus specific adapter, which represents the connection @@ -329,35 +349,63 @@ pub trait Adapter { /// /// If this returns `None`, it means there is no match with an entry in the [`of::IdTable`]. fn of_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> { - #[cfg(not(CONFIG_OF))] + let table = Self::of_id_table()?; + + #[cfg(not(any(CONFIG_OF, CONFIG_ACPI)))] { - let _ = dev; - None + let _ = (dev, table); } #[cfg(CONFIG_OF)] { - let table = Self::of_id_table()?; - // SAFETY: // - `table` has static lifetime, hence it's valid for read, // - `dev` is guaranteed to be valid while it's alive, and so is `dev.as_raw()`. let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), dev.as_raw()) }; - if raw_id.is_null() { - None - } else { + if !raw_id.is_null() { // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct of_device_id` // and does not add additional invariants, so it's safe to transmute. let id = unsafe { &*raw_id.cast::() }; - Some( - table.info(::index( - id, - )), - ) + return Some(table.info( + ::index(id), + )); } } + + #[cfg(CONFIG_ACPI)] + { + use core::ptr; + use device::property::FwNode; + + let mut raw_id = ptr::null(); + + let fwnode = dev.fwnode().map_or(ptr::null_mut(), FwNode::as_raw); + + // SAFETY: `fwnode` is a pointer to a valid `fwnode_handle`. A null pointer will be + // passed through the function. + let adev = unsafe { bindings::to_acpi_device_node(fwnode) }; + + // SAFETY: + // - `adev` is a valid pointer to `acpi_device` or is null. It is guaranteed to be + // valid as long as `dev` is alive. + // - `table` has static lifetime, hence it's valid for read. + if unsafe { acpi_of_match_device(adev, table.as_ptr(), &raw mut raw_id) } { + // SAFETY: + // - the function returns true, therefore `raw_id` has been set to a pointer to a + // valid `of_device_id`. + // - `DeviceId` is a `#[repr(transparent)]` wrapper of `struct of_device_id` + // and does not add additional invariants, so it's safe to transmute. + let id = unsafe { &*raw_id.cast::() }; + + return Some(table.info( + ::index(id), + )); + } + } + + None } /// Returns the driver's private data from the matching entry of any of the ID tables, if any.