diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 10:42:18 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 10:42:18 -0700 |
| commit | 59e6295fac26b8e85c1ea859cdd89fa1e47519d7 (patch) | |
| tree | 7c071f8bab440eddbb569e84e9f31c80f8a4e529 /rust/kernel | |
| parent | c6cf4441a3a05bb7273ed022f3e56c4fc591da08 (diff) | |
| parent | 3b5ea0f078e1b72276e99c237f3dfc2fd72938bb (diff) | |
Merge tag 'driver-core-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core
Pull driver core updates from Danilo Krummrich:
"container_of:
- Apply typeof_member(), remove the local __mptr variable to
eliminate variable shadowing warnings on nested container_of()
calls, and remove unnecessary parentheses
core:
- Add driver name to probe debug print for initcall_debug
- Avoid repeatedly printing the same 'Fixed dependency cycle' log
- Unwind device_add() on attribute creation failure in
attribute_container_add_class_device()
- Remove statistics group if encryption group creation fails in
transport_add_class_device()
debugfs:
- Fix lockdown check for mmap_prepare()
- Warn if file creation failed due to uninitialized debugfs
device property:
- Implement fw_devlink support for software nodes by adding
software_node_add_links(), which creates fwnode links from
DEV_PROP_REF properties to enable automatic probe ordering. Add
kunit-managed fwnode helpers and test coverage
- Fix infinite loop in fwnode_for_each_child_node() when the
secondary fwnode has more than one child. Add test cases
- Fix out-of-bounds access in software_node_get_reference_args() when
called with index -1 (UINT_MAX)
- Refactor to use RAII approach with __free()
- Add Bartosz Golaszewski as software node reviewer
firmware loader:
- Fix race where a sysfs fallback request can complete before being
queued as pending, leading to a use-after-free on the next fallback
request
- Reject 0-size built-in firmware and fail the build on empty
firmware files in CONFIG_EXTRA_FIRMWARE
kobject:
- Provide __KOBJ_ATTR() and __KOBJ_ATTR_RO/WO() initialization macros
and allow the constification of kobject attributes, enabling them
to reside in read-only memory
platform:
- Provide platform_device_set_of_node(), platform_device_set_fwnode(),
and platform_device_set_of_node_from_dev() helpers that encapsulate
firmware node reference counting for dynamically allocated platform
devices
Convert all in-tree users that manually assigned dev.of_node or
dev.fwnode, fixing a pre-existing refcount bug in powermac. Switch
to counting references of all firmware node types, not only OF
nodes
- Unify the release path for dynamically allocated platform devices
by removing platform_device_release_full(). Amend the fwnode setter
API contract to warn if a primary software node is overwritten. Add
KUnit tests for correct software node removal on device
unregistration
Rust:
- Auxiliary:
- Add registration_data_with() closure-based API for invariant
ForLt types
- Debugfs:
- Migrate BinaryWriter and BinaryReaderMut trait requirements
from kernel::transmute traits to zerocopy traits
- Device:
- Add BoundInternal device context and InternalBoundContext trait
for bus abstractions that need internal access to a bound
device.
- Make the lifetime on Core and CoreInternal invariant to prevent
coercion to shorter lifetimes
- Devres:
- Fix race between concurrent revokers where the losing revoker
could return before the winning revoker finished dropping the
inner data, causing use-after-free.
- Ensure revocation is complete before the device finishes
unbinding by making the synchronization bidirectional.
- Add DevresLt<F: ForLt>, a wrapper around Devres that shortens
'static back to the caller's borrow scope. Implement ForLt and
CovariantForLt for Bar, IoMem, and ExclusiveIoMem
- Driver:
- Switch from index-based to pointer-based device ID info lookup,
storing static references in driver_data. Centralize device ID
handling in device_id.rs, removing the open-coded ACPI/OF
matching logic and duplicate ID table from driver.rs
- I/O:
- Make I/O regions typed (with a dynamically-sized Region type
for the existing untyped case), create view types representing
subregions of a mapped I/O region, and add io_project!() for
safely creating subviews.
- Split Io into a base trait (IoBase) and an extension trait (Io)
with a blanket implementation, preventing implementers from
overriding provided methods that unsafe code relies on.
- Add a SysMem backend for shared system memory with volatile
access, and make Coherent implement Io via an I/O view type.
Add IoSysMap as sum type of Mmio and SysMem. Add copying
methods (memcpy_{from,to}io()) and read_val()/write_val() for
typed access.
- Replace dma_read!()/dma_write!() with io_read!()/io_write!()
for primitives and copying methods for aggregates; drop the old
macros. Convert nova-core to use I/O projection.
- Fix internal shortcut rule dispatch in the register!() macro,
remove unused rule arguments, and use path fragments for alias
destinations
- IRQ:
- Make irq::Registration compatible with lifetime-bound drivers
by removing the 'static bound on Handler/ThreadedHandler and
replacing Devres<RegistrationInner> with direct
request_irq()/free_irq() calls. Handlers can now directly own
lifetime-bound device resources
- PCI:
- Convert IrqVectorRegistration to a lifetime-annotated owning
type, giving drivers explicit control over the allocation
lifetime. IrqVector embeds a resolved IrqRequest, making the
conversion infallible. Remove the redundant
request_irq()/request_threaded_irq() wrappers from pci::Device.
- Add pci_irq_type() C helper and expose it via irq_type() on
IrqVectorRegistration and IrqVector, returning PCI_IRQ_MSIX,
PCI_IRQ_MSI, or PCI_IRQ_INTX.
- Mark pci::Device refcount methods inline
- Serdev:
- Add Rust abstractions for the serial device bus, including
serdev::Driver trait, serdev::Device wrapping struct
serdev_device, and serdev::Adapter implementing
RegistrationOps. Includes a sample driver. Markus Probst takes
over as serdev maintainer for both C and Rust code
- Misc:
- Split ForLt into a base trait (providing the Of<'a> GAT) and an
unsafe CovariantForLt subtrait guaranteeing covariance,
enabling invariant types (e.g. those containing Mutex<&'bound T>)
to participate in the ForLt abstraction.
- Fix Coherent read past EOF returning -ERANGE instead of zero.
- Fix firmware example UB by avoiding null-pointer ARef
misc:
- Avoid iattr allocation in kernfs listxattr by using
kernfs_iattrs_noalloc().
- Unregister SoC bus on early device registration failure.
- Remove unused DMA_FENCE_TRACE Kconfig symbol.
- Fix /sys/module path in comment.
- Refactor ISA bus init to remove nested blocks.
- Remove redundant nodemask clears in numa_init().
- Add kernel-doc for fwnode_operations and sys_soc.h, mark
internal property data as private for kernel-doc, and add
property.h/fwnode.h to driver-api infrastructure docs.
- Add MAINTAINERS entry for sys_soc.h"
* tag 'driver-core-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core: (129 commits)
rust: pci: expose the allocated interrupt type
PCI: Add pci_irq_type() to query the allocated interrupt type
rust: pci: remove request_irq() and request_threaded_irq() from Device
rust: pci: resolve IRQ in index() and embed IrqRequest in IrqVector
rust: pci: convert IrqVectorRegistration to a lifetime-managed owning type
kernfs: avoid iattr allocation in listxattr
rust: serdev: use ThisModule::as_ptr() instead of field access
ACPI/IORT: use platform_device_set_fwnode()
ACPI/APMT: use platform_device_set_fwnode()
firmware_loader: do not queue completed sysfs fallback requests
rust: pci: Mark Device refcount methods inline
rust: irq: make Registration compatible with lifetime-bound drivers
rust: net/phy: remove expansion from doc
rust: dma: return zero for Coherent reads past EOF
rust: io: register: use path fragment for alias destination
rust: io: register: remove unused rule arguments
rust: io: register: dispatch shortcut rules internally
MAINTAINERS: add sys_soc.h to DRIVER CORE
rust: debugfs: remove unsafe blocks from traits impl for Vec
rust: debugfs: migrate debugfs traits requirements to zerocopy
...
Diffstat (limited to 'rust/kernel')
| -rw-r--r-- | rust/kernel/acpi.rs | 14 | ||||
| -rw-r--r-- | rust/kernel/auxiliary.rs | 96 | ||||
| -rw-r--r-- | rust/kernel/debugfs/traits.rs | 41 | ||||
| -rw-r--r-- | rust/kernel/device.rs | 71 | ||||
| -rw-r--r-- | rust/kernel/device_id.rs | 196 | ||||
| -rw-r--r-- | rust/kernel/devres.rs | 151 | ||||
| -rw-r--r-- | rust/kernel/dma.rs | 287 | ||||
| -rw-r--r-- | rust/kernel/driver.rs | 137 | ||||
| -rw-r--r-- | rust/kernel/firmware.rs | 8 | ||||
| -rw-r--r-- | rust/kernel/i2c.rs | 26 | ||||
| -rw-r--r-- | rust/kernel/io.rs | 1519 | ||||
| -rw-r--r-- | rust/kernel/io/mem.rs | 94 | ||||
| -rw-r--r-- | rust/kernel/io/poll.rs | 6 | ||||
| -rw-r--r-- | rust/kernel/io/register.rs | 86 | ||||
| -rw-r--r-- | rust/kernel/irq.rs | 2 | ||||
| -rw-r--r-- | rust/kernel/irq/request.rs | 432 | ||||
| -rw-r--r-- | rust/kernel/lib.rs | 5 | ||||
| -rw-r--r-- | rust/kernel/net/phy.rs | 66 | ||||
| -rw-r--r-- | rust/kernel/of.rs | 14 | ||||
| -rw-r--r-- | rust/kernel/pci.rs | 32 | ||||
| -rw-r--r-- | rust/kernel/pci/io.rs | 203 | ||||
| -rw-r--r-- | rust/kernel/pci/irq.rs | 210 | ||||
| -rw-r--r-- | rust/kernel/platform.rs | 53 | ||||
| -rw-r--r-- | rust/kernel/ptr.rs | 12 | ||||
| -rw-r--r-- | rust/kernel/serdev.rs | 604 | ||||
| -rw-r--r-- | rust/kernel/types.rs | 5 | ||||
| -rw-r--r-- | rust/kernel/types/for_lt.rs | 103 | ||||
| -rw-r--r-- | rust/kernel/usb.rs | 24 |
28 files changed, 3054 insertions, 1443 deletions
diff --git a/rust/kernel/acpi.rs b/rust/kernel/acpi.rs index 9b8efa623130..ea2ce61ee393 100644 --- a/rust/kernel/acpi.rs +++ b/rust/kernel/acpi.rs @@ -25,10 +25,6 @@ unsafe impl RawDeviceId for DeviceId { // SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field. unsafe impl RawDeviceIdIndex for DeviceId { const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::acpi_device_id, driver_data); - - fn index(&self) -> usize { - self.0.driver_data - } } impl DeviceId { @@ -53,13 +49,7 @@ impl DeviceId { /// Create an ACPI `IdTable` with an "alias" for modpost. #[macro_export] macro_rules! acpi_device_table { - ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => { - const $table_name: $crate::device_id::IdArray< - $crate::acpi::DeviceId, - $id_info_type, - { $table_data.len() }, - > = $crate::device_id::IdArray::new($table_data); - - $crate::module_device_table!("acpi", $module_table_name, $table_name); + ($($tt:tt)*) => { + $crate::module_device_table!("acpi", $crate::acpi::DeviceId, $($tt)*); }; } diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index cc9745fbf179..60dfbec8f330 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -20,6 +20,7 @@ use crate::{ }, prelude::*, types::{ + CovariantForLt, ForLt, ForeignOwnable, Opaque, // @@ -93,7 +94,9 @@ impl<T: Driver> Adapter<T> { // SAFETY: `DeviceId` is a `#[repr(transparent)`] wrapper of `struct auxiliary_device_id` // and does not add additional invariants, so it's safe to transmute. let id = unsafe { &*id.cast::<DeviceId>() }; - let info = T::ID_TABLE.info(id.index()); + + // SAFETY: `id` comes from `T::ID_TABLE` which is of type `IdArray<_, T::IdInfo>`. + let info = unsafe { id.info_unchecked::<T::IdInfo>() }; from_result(|| { let data = T::probe(adev, info); @@ -169,10 +172,6 @@ unsafe impl RawDeviceId for DeviceId { unsafe impl RawDeviceIdIndex for DeviceId { const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::auxiliary_device_id, driver_data); - - fn index(&self) -> usize { - self.0.driver_data - } } /// IdTable type for auxiliary drivers. @@ -181,14 +180,8 @@ pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>; /// Create a auxiliary `IdTable` with its alias for modpost. #[macro_export] macro_rules! auxiliary_device_table { - ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => { - const $table_name: $crate::device_id::IdArray< - $crate::auxiliary::DeviceId, - $id_info_type, - { $table_data.len() }, - > = $crate::device_id::IdArray::new($table_data); - - $crate::module_device_table!("auxiliary", $module_table_name, $table_name); + ($($tt:tt)*) => { + $crate::module_device_table!("auxiliary", $crate::auxiliary::DeviceId, $($tt)*); }; } @@ -270,18 +263,15 @@ impl Device<device::Bound> { unsafe { parent.as_bound() } } - /// Returns a pinned reference to the registration data set by the registering (parent) driver. + /// Returns the stored registration data as a pinned reference. /// - /// `F` is the [`ForLt`](trait@ForLt) encoding of the data type. The returned - /// reference has its lifetime shortened from `'static` to `&self`'s borrow lifetime via - /// [`ForLt::cast_ref`]. + /// Performs null and [`TypeId`] checks, then borrows the stored [`KBox`]. /// - /// Returns [`EINVAL`] if `F` does not match the type used by the parent driver when calling - /// [`Registration::new()`]. + /// # Safety /// - /// Returns [`ENOENT`] if no registration data has been set, e.g. when the device was - /// registered by a C driver. - pub fn registration_data<F: ForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> { + /// Callers must ensure that the lifetime shortening from the original `'static` storage to + /// `'_` is sound, e.g. via an HRTB closure or [`CovariantForLt`] guarantee. + unsafe fn registration_data_pinned<F: ForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> { // SAFETY: By the type invariant, `self.as_raw()` is a valid `struct auxiliary_device`. let ptr = unsafe { (*self.as_raw()).registration_data_rust }; if ptr.is_null() { @@ -300,17 +290,59 @@ impl Device<device::Bound> { return Err(EINVAL); } - // SAFETY: The `TypeId` check above confirms that the stored type matches - // `F::Of<'static>`; `ptr` remains valid until `Registration::drop()` calls - // `from_foreign()`. - let wrapper = unsafe { Pin::<KBox<RegistrationData<F::Of<'static>>>>::borrow(ptr) }; + // SAFETY: The `TypeId` check above confirms that the stored type matches `F`'s + // encoding; lifetimes are erased at runtime, so borrowing as `F::Of<'_>` is + // layout-compatible with the stored `F::Of<'static>`. `ptr` remains valid until + // `Registration::drop()` calls `from_foreign()`. + let wrapper = unsafe { Pin::<KBox<RegistrationData<F::Of<'_>>>>::borrow(ptr) }; // SAFETY: `data` is a structurally pinned field of `RegistrationData`. - let pinned: Pin<&F::Of<'_>> = unsafe { wrapper.map_unchecked(|w| &w.data) }; + Ok(unsafe { wrapper.map_unchecked(|w| &w.data) }) + } + + /// Access the registration data set by the registering (parent) driver through a closure. + /// + /// `F` is the [`ForLt`](trait@ForLt) encoding of the data type. The closure receives a pinned + /// reference to the registration data. + /// + /// For covariant types that implement [`trait@CovariantForLt`], prefer + /// [`registration_data`](Self::registration_data) which returns a direct reference. + /// + /// Returns [`EINVAL`] if `F` does not match the type used by the parent driver when calling + /// [`Registration::new()`]. + /// + /// Returns [`ENOENT`] if no registration data has been set, e.g. when the device was + /// registered by a C driver. + #[inline] + pub fn registration_data_with<F: ForLt + 'static, R>( + &self, + f: impl for<'a> FnOnce(Pin<&'a F::Of<'a>>) -> R, + ) -> Result<R> { + // SAFETY: The HRTB closure prevents the caller from smuggling in references with a + // concrete short lifetime, making the round-trip from `'static` sound regardless of + // variance. + let pinned = unsafe { self.registration_data_pinned::<F>()? }; + + Ok(f(pinned)) + } - // SAFETY: The data was pinned when stored; `cast_ref` only shortens - // the lifetime, so the pinning guarantee is preserved. - Ok(unsafe { Pin::new_unchecked(F::cast_ref(pinned.get_ref())) }) + /// Returns a pinned reference to the registration data set by the registering (parent) driver. + /// + /// This method is only available when `F` implements [`trait@CovariantForLt`], which guarantees + /// that the lifetime shortening is sound. + /// + /// For non-covariant types, use the closure-based [`Self::registration_data_with`]. + /// + /// Returns [`EINVAL`] if `F` does not match the type used by the parent driver when calling + /// [`Registration::new()`]. + /// + /// Returns [`ENOENT`] if no registration data has been set, e.g. when the device was + /// registered by a C driver. + #[inline] + pub fn registration_data<F: CovariantForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> { + // SAFETY: `CovariantForLt` guarantees covariance, which makes the lifetime shortening + // from `'static` to `'_` performed by `registration_data_pinned` sound. + unsafe { self.registration_data_pinned::<F>() } } } @@ -401,7 +433,9 @@ struct RegistrationData<T> { /// /// The type parameter `F` is a [`ForLt`](trait@ForLt) encoding of the registration /// data type. For non-lifetime-parameterized types, use [`ForLt!(T)`](macro@ForLt). -/// The data can be accessed by the auxiliary driver through [`Device::registration_data()`]. +/// +/// The data can be accessed by the auxiliary driver through [`Device::registration_data()`] and +/// [`Device::registration_data_with()`]. /// /// # Invariants /// diff --git a/rust/kernel/debugfs/traits.rs b/rust/kernel/debugfs/traits.rs index 8c39524b6a99..b295f8420abd 100644 --- a/rust/kernel/debugfs/traits.rs +++ b/rust/kernel/debugfs/traits.rs @@ -18,10 +18,6 @@ use crate::{ Arc, Mutex, // }, - transmute::{ - AsBytes, - FromBytes, // - }, uaccess::{ UserSliceReader, UserSliceWriter, // @@ -36,6 +32,8 @@ use core::{ str::FromStr, }; +use zerocopy::Immutable; + /// A trait for types that can be written into a string. /// /// This works very similarly to `Debug`, and is automatically implemented if `Debug` is @@ -76,8 +74,8 @@ pub trait BinaryWriter { ) -> Result<usize>; } -// Base implementation for any `T: AsBytes`. -impl<T: AsBytes> BinaryWriter for T { +// Base implementation for any `T: Immutable + IntoBytes`. +impl<T: Immutable + IntoBytes> BinaryWriter for T { fn write_to_slice( &self, writer: &mut UserSliceWriter, @@ -147,7 +145,7 @@ where // Delegate for `Vec<T, A>`. impl<T, A> BinaryWriter for Vec<T, A> where - T: AsBytes, + T: Immutable + IntoBytes, A: Allocator, { fn write_to_slice( @@ -155,14 +153,7 @@ where writer: &mut UserSliceWriter, offset: &mut file::Offset, ) -> Result<usize> { - let slice = self.as_slice(); - - // SAFETY: `T: AsBytes` allows us to treat `&[T]` as `&[u8]`. - let buffer = unsafe { - core::slice::from_raw_parts(slice.as_ptr().cast(), core::mem::size_of_val(slice)) - }; - - writer.write_slice_file(buffer, offset) + writer.write_slice_file(self.as_bytes(), offset) } } @@ -230,14 +221,14 @@ pub trait BinaryReaderMut { ) -> Result<usize>; } -// Base implementation for any `T: AsBytes + FromBytes`. -impl<T: AsBytes + FromBytes> BinaryReaderMut for T { +// Base implementation for any `T: FromBytes + IntoBytes`. +impl<T: FromBytes + IntoBytes> BinaryReaderMut for T { fn read_from_slice_mut( &mut self, reader: &mut UserSliceReader, offset: &mut file::Offset, ) -> Result<usize> { - reader.read_slice_file(self.as_bytes_mut(), offset) + reader.read_slice_file(self.as_mut_bytes(), offset) } } @@ -255,7 +246,7 @@ impl<T: ?Sized + BinaryReaderMut, A: Allocator> BinaryReaderMut for Box<T, A> { // Delegate for `Vec<T, A>`: Support a `Vec<T, A>` with an outer lock. impl<T, A> BinaryReaderMut for Vec<T, A> where - T: AsBytes + FromBytes, + T: FromBytes + IntoBytes, A: Allocator, { fn read_from_slice_mut( @@ -263,17 +254,7 @@ where reader: &mut UserSliceReader, offset: &mut file::Offset, ) -> Result<usize> { - let slice = self.as_mut_slice(); - - // SAFETY: `T: AsBytes + FromBytes` allows us to treat `&mut [T]` as `&mut [u8]`. - let buffer = unsafe { - core::slice::from_raw_parts_mut( - slice.as_mut_ptr().cast(), - core::mem::size_of_val(slice), - ) - }; - - reader.read_slice_file(buffer, offset) + reader.read_slice_file(self.as_mut_bytes(), offset) } } diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 1a38b3bbdfb7..2291d85b6849 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -54,7 +54,8 @@ pub mod property; /// reference is valid in. For instance, the [`Bound`] context guarantees that the [`Device`] is /// bound to a driver for the entire duration of the existence of a [`Device<Bound>`] reference. /// -/// Other [`DeviceContext`] types besides [`Bound`] are [`Normal`], [`Core`] and [`CoreInternal`]. +/// Other [`DeviceContext`] types besides [`Bound`] are [`Normal`], [`Core`], [`CoreInternal`] and +/// [`BoundInternal`]. /// /// Unless selected otherwise [`Device`] defaults to the [`Normal`] [`DeviceContext`], which by /// itself has no additional requirements. @@ -235,7 +236,9 @@ impl<'a> Device<CoreInternal<'a>> { // in `into_foreign()`. Some(unsafe { Pin::<KBox<T>>::from_foreign(ptr.cast()) }) } +} +impl<Ctx: InternalBoundContext> Device<Ctx> { /// Borrow the driver's private data bound to this [`Device`]. /// /// # Safety @@ -245,22 +248,6 @@ impl<'a> Device<CoreInternal<'a>> { /// - The type `T` must match the type of the `ForeignOwnable` previously stored by /// [`Device::set_drvdata`]. pub unsafe fn drvdata_borrow<T>(&self) -> Pin<&T> { - // SAFETY: `drvdata_unchecked()` has the exact same safety requirements as the ones - // required by this method. - unsafe { self.drvdata_unchecked() } - } -} - -impl Device<Bound> { - /// Borrow the driver's private data bound to this [`Device`]. - /// - /// # Safety - /// - /// - Must only be called after a preceding call to [`Device::set_drvdata`] and before - /// the device is fully unbound. - /// - The type `T` must match the type of the `ForeignOwnable` previously stored by - /// [`Device::set_drvdata`]. - unsafe fn drvdata_unchecked<T>(&self) -> Pin<&T> { // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. let ptr = unsafe { bindings::dev_get_drvdata(self.as_raw()) }; @@ -476,7 +463,8 @@ unsafe impl Sync for Device<Bound> {} /// [`DeviceContext`] is a marker trait for types representing the context of a bus specific /// [`Device`]. /// -/// The specific device context types are: [`CoreInternal`], [`Core`], [`Bound`] and [`Normal`]. +/// The specific device context types are: [`CoreInternal`], [`Core`], [`BoundInternal`], [`Bound`] +/// and [`Normal`]. /// /// [`DeviceContext`] types are hierarchical, which means that there is a strict hierarchy that /// defines which [`DeviceContext`] type can be derived from another. For instance, any @@ -485,6 +473,11 @@ unsafe impl Sync for Device<Bound> {} /// The following enumeration illustrates the dereference hierarchy of [`DeviceContext`] types. /// /// - [`CoreInternal`] => [`Core`] => [`Bound`] => [`Normal`] +/// - [`BoundInternal`] => [`Bound`] => [`Normal`] +/// +/// Both [`CoreInternal`] and [`BoundInternal`] implement the [`InternalBoundContext`] trait, +/// which provides access to internal bus abstraction methods on [`Device`] that are not available +/// to drivers. /// /// Bus devices can automatically implement the dereference hierarchy by using /// [`impl_device_context_deref`]. @@ -511,7 +504,11 @@ pub struct Normal; /// callback it appears in. It is intended to be used for synchronization purposes. Bus device /// implementations can implement methods for [`Device<Core>`], such that they can only be called /// from bus callbacks. -pub struct Core<'a>(PhantomData<&'a ()>); +/// +/// The lifetime `'a` is for "lifetime branding" purpose. Callbacks need to polymorphic over this +/// lifetime so the `&'bound Device<Core<'_>>` provided to them cannot outlive the scope of the +/// function. For this reason, it needs to be invariant. +pub struct Core<'a>(PhantomData<fn(&'a ()) -> &'a ()>); /// Semantically the same as [`Core`], but reserved for internal usage of the corresponding bus /// abstraction. @@ -522,7 +519,21 @@ pub struct Core<'a>(PhantomData<&'a ()>); /// /// This context mainly exists to share generic [`Device`] infrastructure that should only be called /// from bus callbacks with bus abstractions, but without making them accessible for drivers. -pub struct CoreInternal<'a>(PhantomData<&'a ()>); +/// +/// Lifetime `'a` is invariant for the same reason as [`Core`]. +pub struct CoreInternal<'a>(PhantomData<fn(&'a ()) -> &'a ()>); + +/// Semantically the same as [`Bound`], but reserved for internal usage of the corresponding bus +/// abstraction. +/// +/// The internal bound context is intended to be used in exactly the same way as the [`Bound`] +/// context, with the difference that this [`DeviceContext`] is internal to the corresponding bus +/// abstraction. +/// +/// This context exists for cases where the bus abstraction needs access to internal device +/// infrastructure (such as [`Device::drvdata_borrow`]), where [`CoreInternal`] would not be +/// justified. +pub struct BoundInternal; /// The [`Bound`] context is the [`DeviceContext`] of a bus specific device when it is guaranteed to /// be bound to a driver. @@ -546,16 +557,28 @@ mod private { pub trait Sealed {} impl Sealed for super::Bound {} + impl Sealed for super::BoundInternal {} impl<'a> Sealed for super::Core<'a> {} impl<'a> Sealed for super::CoreInternal<'a> {} impl Sealed for super::Normal {} } impl DeviceContext for Bound {} +impl DeviceContext for BoundInternal {} impl<'a> DeviceContext for Core<'a> {} impl<'a> DeviceContext for CoreInternal<'a> {} impl DeviceContext for Normal {} +/// Marker trait for [`DeviceContext`] types that have internal bound-level access. +/// +/// This trait is implemented by [`CoreInternal`] and [`BoundInternal`], allowing methods that +/// require internal bus abstraction access to a bound device to be generic over both contexts. +/// +/// Methods bounded by this trait are available to bus abstractions but not to drivers. +pub trait InternalBoundContext: DeviceContext {} +impl<'a> InternalBoundContext for CoreInternal<'a> {} +impl InternalBoundContext for BoundInternal {} + impl<Ctx: DeviceContext> AsRef<Device<Ctx>> for Device<Ctx> { #[inline] fn as_ref(&self) -> &Device<Ctx> { @@ -665,6 +688,13 @@ macro_rules! impl_device_context_deref { // `__impl_device_context_deref!`. ::kernel::__impl_device_context_deref!(unsafe { $device, + $crate::device::BoundInternal => $crate::device::Bound + }); + + // SAFETY: This macro has the exact same safety requirement as + // `__impl_device_context_deref!`. + ::kernel::__impl_device_context_deref!(unsafe { + $device, $crate::device::Bound => $crate::device::Normal }); }; @@ -700,6 +730,7 @@ macro_rules! impl_device_context_into_aref { ::kernel::__impl_device_context_into_aref!( <'a> $crate::device::Core<'a>, $device ); + ::kernel::__impl_device_context_into_aref!($crate::device::BoundInternal, $device); ::kernel::__impl_device_context_into_aref!($crate::device::Bound, $device); }; } diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs index 8e9721446014..c81fca5b4986 100644 --- a/rust/kernel/device_id.rs +++ b/rust/kernel/device_id.rs @@ -5,7 +5,10 @@ //! Each bus / subsystem that matches device and driver through a bus / subsystem specific ID is //! expected to implement [`RawDeviceId`]. -use core::mem::MaybeUninit; +use core::{ + marker::PhantomData, + mem::MaybeUninit, // +}; /// Marker trait to indicate a Rust device ID type represents a corresponding C device ID type. /// @@ -47,112 +50,110 @@ pub unsafe trait RawDeviceIdIndex: RawDeviceId { /// The offset (in bytes) to the context/data field in the raw device ID. const DRIVER_DATA_OFFSET: usize; - /// The index stored at `DRIVER_DATA_OFFSET` of the implementor of the [`RawDeviceIdIndex`] - /// trait. - fn index(&self) -> usize; -} - -/// A zero-terminated device id array. -#[repr(C)] -pub struct RawIdArray<T: RawDeviceId, const N: usize> { - ids: [T::RawType; N], - sentinel: MaybeUninit<T::RawType>, -} + /// Obtain the data pointer stored inside the device ID. + /// + /// # Safety + /// + /// `&Self` must be stored inside a `IdArray<Self, U>`. + unsafe fn info_unchecked<U>(&self) -> &'static U { + // SAFETY: By safety requirement of the trait, this is `self.driver_data as *const U` and by + // the safety requirement of the function, this is stored in `IdArray<Self, U>` so is + // convertible to `&'static U`. + unsafe { + core::ptr::from_ref(self) + .byte_add(Self::DRIVER_DATA_OFFSET) + .cast::<&U>() + .read() + } + } -impl<T: RawDeviceId, const N: usize> RawIdArray<T, N> { - #[doc(hidden)] - pub const fn size(&self) -> usize { - core::mem::size_of::<Self>() + /// Obtain the data pointer stored inside the device ID. + /// + /// # Safety + /// + /// `&Self` must be stored inside a `IdArray<Self, U>`, or has NULL (or 0) as driver data. + unsafe fn info_unchecked_opt<U>(&self) -> Option<&'static U> { + // SAFETY: By safety requirement of the trait, this is `self.driver_data as *const U` and by + // the safety requirement of the function, if this is stored in `IdArray<Self, U>`, this is + // convertible to `Option<&'static U>`. Otherwise it is NULL which is `None` as + // `Option<&U>`. + unsafe { + core::ptr::from_ref(self) + .byte_add(Self::DRIVER_DATA_OFFSET) + .cast::<Option<&U>>() + .read() + } } } /// A zero-terminated device id array, followed by context data. #[repr(C)] -pub struct IdArray<T: RawDeviceId, U, const N: usize> { - raw_ids: RawIdArray<T, N>, - id_infos: [U; N], +pub struct IdArray<T: RawDeviceId, U: 'static, const N: usize> { + // This is `MaybeUninit<T::RawType>` so any bytes inside it can carry provenance in CTFE. + // If this were `T::RawType`, integer fields would not be able to contain pointers. + ids: [MaybeUninit<T::RawType>; N], + sentinel: MaybeUninit<T::RawType>, + phantom: PhantomData<&'static U>, } -impl<T: RawDeviceId, U, const N: usize> IdArray<T, U, N> { +// SAFETY: device ID is plain data plus a `&'static U` and can thus be sent between threads safely +// if `&U` can. +unsafe impl<T: RawDeviceId, U: Sync + 'static, const N: usize> Send for IdArray<T, U, N> {} + +// SAFETY: device ID is plain data plus a `&'static U` and can thus be shared between threads safely +// if `&U` can. +unsafe impl<T: RawDeviceId, U: Sync + 'static, const N: usize> Sync for IdArray<T, U, N> {} + +impl<T: RawDeviceId + RawDeviceIdIndex, U: 'static, const N: usize> IdArray<T, U, N> { /// Creates a new instance of the array. /// /// The contents are derived from the given identifiers and context information. - /// - /// # Safety - /// - /// `data_offset` as `None` is always safe. - /// If `data_offset` is `Some(data_offset)`, then: - /// - `data_offset` must be the correct offset (in bytes) to the context/data field - /// (e.g., the `driver_data` field) within the raw device ID structure. - /// - The field at `data_offset` must be correctly sized to hold a `usize`. - const unsafe fn build(ids: [(T, U); N], data_offset: Option<usize>) -> Self { + pub const fn new(ids: [(T, &'static U); N]) -> Self { let mut raw_ids = [const { MaybeUninit::<T::RawType>::uninit() }; N]; - let mut infos = [const { MaybeUninit::uninit() }; N]; let mut i = 0usize; while i < N { // SAFETY: by the safety requirement of |
