aboutsummaryrefslogtreecommitdiff
path: root/rust/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'rust/kernel')
-rw-r--r--rust/kernel/alloc/kbox.rs8
-rw-r--r--rust/kernel/auxiliary.rs2
-rw-r--r--rust/kernel/bitfield.rs2
-rw-r--r--rust/kernel/bug.rs34
-rw-r--r--rust/kernel/configfs.rs9
-rw-r--r--rust/kernel/dma.rs10
-rw-r--r--rust/kernel/drm/device.rs5
-rw-r--r--rust/kernel/drm/gem/mod.rs4
-rw-r--r--rust/kernel/drm/gpuvm/va.rs2
-rw-r--r--rust/kernel/drm/gpuvm/vm_bo.rs2
-rw-r--r--rust/kernel/error.rs102
-rw-r--r--rust/kernel/fmt.rs195
-rw-r--r--rust/kernel/i2c.rs2
-rw-r--r--rust/kernel/impl_flags.rs11
-rw-r--r--rust/kernel/init.rs6
-rw-r--r--rust/kernel/lib.rs81
-rw-r--r--rust/kernel/miscdevice.rs4
-rw-r--r--rust/kernel/module.rs80
-rw-r--r--rust/kernel/net/phy.rs6
-rw-r--r--rust/kernel/num.rs2
-rw-r--r--rust/kernel/num/bounded.rs36
-rw-r--r--rust/kernel/num/casts.rs298
-rw-r--r--rust/kernel/pci.rs2
-rw-r--r--rust/kernel/platform.rs2
-rw-r--r--rust/kernel/print.rs2
-rw-r--r--rust/kernel/pwm.rs2
-rw-r--r--rust/kernel/sync/arc.rs20
-rw-r--r--rust/kernel/sync/aref.rs50
-rw-r--r--rust/kernel/sync/rcu.rs20
-rw-r--r--rust/kernel/time.rs141
-rw-r--r--rust/kernel/types.rs8
-rw-r--r--rust/kernel/usb.rs2
32 files changed, 998 insertions, 152 deletions
diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs
index 35d1e015848d..c63d6acdbb6f 100644
--- a/rust/kernel/alloc/kbox.rs
+++ b/rust/kernel/alloc/kbox.rs
@@ -372,13 +372,13 @@ where
// - `ptr` is a valid pointer to uninitialized memory.
// - `ptr` is not used if an error is returned.
// - `ptr` won't be moved until it is dropped, i.e. it is pinned.
- unsafe { init(i).__pinned_init(ptr)? };
+ unsafe { pin_init::raw_try_init(ptr, init(i))? };
// SAFETY:
// - `i + 1 <= len`, hence we don't exceed the capacity, due to the call to
// `with_capacity()` above.
// - The new value at index buffer.len() + 1 is the only element being added here, and
- // it has been initialized above by `init(i).__pinned_init(ptr)`.
+ // it has been initialized above by `raw_try_init(ptr, i)`.
unsafe { buffer.inc_len(1) };
}
@@ -463,7 +463,7 @@ where
let slot = self.as_mut_ptr();
// SAFETY: When init errors/panics, slot will get deallocated but not dropped,
// slot is valid.
- unsafe { init.__init(slot)? };
+ unsafe { pin_init::raw_try_init(slot, init)? };
// SAFETY: All fields have been initialized.
Ok(unsafe { Box::assume_init(self) })
}
@@ -473,7 +473,7 @@ where
let slot = self.as_mut_ptr();
// SAFETY: When init errors/panics, slot will get deallocated but not dropped,
// slot is valid and will not be moved, because we pin it later.
- unsafe { init.__pinned_init(slot)? };
+ unsafe { pin_init::raw_try_init(slot, init)? };
// SAFETY: All fields have been initialized.
Ok(unsafe { Box::assume_init(self) }.into())
}
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index c42928d5a239..cc9745fbf179 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -69,7 +69,7 @@ unsafe impl<T: Driver> driver::RegistrationOps for Adapter<T> {
// SAFETY: `adrv` is guaranteed to be a valid `DriverType`.
to_result(unsafe {
- bindings::__auxiliary_driver_register(adrv.get(), module.0, name.as_char_ptr())
+ bindings::__auxiliary_driver_register(adrv.get(), module.as_ptr(), name.as_char_ptr())
})
}
diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
index 35ede53f2b8e..a0d089423f21 100644
--- a/rust/kernel/bitfield.rs
+++ b/rust/kernel/bitfield.rs
@@ -581,6 +581,7 @@ mod tests {
}
impl From<MemoryType> for Bounded<u64, 4> {
+ #[inline(always)]
fn from(mt: MemoryType) -> Bounded<u64, 4> {
Bounded::from_expr(mt as u64)
}
@@ -606,6 +607,7 @@ mod tests {
}
impl From<Priority> for Bounded<u16, 2> {
+ #[inline(always)]
fn from(p: Priority) -> Bounded<u16, 2> {
Bounded::from_expr(p as u16)
}
diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs
index ed943960f851..3566f0234ca4 100644
--- a/rust/kernel/bug.rs
+++ b/rust/kernel/bug.rs
@@ -8,6 +8,7 @@
#[macro_export]
#[doc(hidden)]
+#[cfg(not(testlib))]
#[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))]
#[cfg(CONFIG_DEBUG_BUGVERBOSE)]
macro_rules! warn_flags {
@@ -47,12 +48,17 @@ macro_rules! warn_flags {
#[macro_export]
#[doc(hidden)]
+#[cfg(not(testlib))]
#[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))]
#[cfg(not(CONFIG_DEBUG_BUGVERBOSE))]
macro_rules! warn_flags {
($file:expr, $flags:expr) => {
const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags;
+ if false {
+ _ = $file;
+ }
+
// SAFETY:
// - `flags` and `size` are all compile-time constants, preventing
// any invalid memory access.
@@ -73,14 +79,19 @@ macro_rules! warn_flags {
#[macro_export]
#[doc(hidden)]
+#[cfg(not(testlib))]
#[cfg(all(CONFIG_BUG, CONFIG_UML))]
macro_rules! warn_flags {
($file:expr, $flags:expr) => {
+ if false {
+ _ = $file;
+ }
+
// SAFETY: It is always safe to call `warn_slowpath_fmt()`
// with a valid null-terminated string.
unsafe {
$crate::bindings::warn_slowpath_fmt(
- $crate::c_str!(::core::file!()).as_char_ptr(),
+ $crate::str::CStrExt::as_char_ptr($crate::c_str!(::core::file!())),
line!() as $crate::ffi::c_int,
$flags as $crate::ffi::c_uint,
::core::ptr::null(),
@@ -91,9 +102,15 @@ macro_rules! warn_flags {
#[macro_export]
#[doc(hidden)]
+#[cfg(not(testlib))]
#[cfg(all(CONFIG_BUG, any(CONFIG_LOONGARCH, CONFIG_ARM)))]
macro_rules! warn_flags {
($file:expr, $flags:expr) => {
+ if false {
+ _ = $file;
+ _ = $flags;
+ }
+
// SAFETY: It is always safe to call `WARN_ON()`.
unsafe { $crate::bindings::WARN_ON(true) }
};
@@ -101,9 +118,14 @@ macro_rules! warn_flags {
#[macro_export]
#[doc(hidden)]
-#[cfg(not(CONFIG_BUG))]
+#[cfg(any(testlib, not(CONFIG_BUG)))]
macro_rules! warn_flags {
- ($file:expr, $flags:expr) => {};
+ ($file:expr, $flags:expr) => {
+ if false {
+ _ = $file;
+ _ = $flags;
+ }
+ };
}
#[doc(hidden)]
@@ -118,14 +140,14 @@ macro_rules! warn_on {
let cond = $cond;
#[cfg(CONFIG_DEBUG_BUGVERBOSE_DETAILED)]
- const _COND_STR: &str = concat!("[", stringify!($cond), "] ", file!());
+ const COND_STR: &str = concat!("[", stringify!($cond), "] ", file!());
#[cfg(not(CONFIG_DEBUG_BUGVERBOSE_DETAILED))]
- const _COND_STR: &str = file!();
+ const COND_STR: &str = file!();
if cond {
const WARN_ON_FLAGS: u32 = $crate::bug::bugflag_taint($crate::bindings::TAINT_WARN);
- $crate::warn_flags!(_COND_STR, WARN_ON_FLAGS);
+ $crate::warn_flags!(COND_STR, WARN_ON_FLAGS);
}
cond
}};
diff --git a/rust/kernel/configfs.rs b/rust/kernel/configfs.rs
index 2339c6467325..cd082b83e9e7 100644
--- a/rust/kernel/configfs.rs
+++ b/rust/kernel/configfs.rs
@@ -875,13 +875,14 @@ impl<Container, Data> ItemType<Container, Data> {
/// configfs::Subsystem<Configuration>,
/// Configuration
/// >::new_with_child_ctor::<N,Child>(
-/// &THIS_MODULE,
+/// ::kernel::module::this_module::<crate::LocalModule>(),
/// &CONFIGURATION_ATTRS
/// );
///
/// &CONFIGURATION_TPE
/// }
/// ```
+#[allow(clippy::crate_in_macro_def)]
#[macro_export]
macro_rules! configfs_attrs {
(
@@ -1021,7 +1022,8 @@ macro_rules! configfs_attrs {
static [< $data:upper _TPE >] : $crate::configfs::ItemType<$container, $data> =
$crate::configfs::ItemType::<$container, $data>::new::<N>(
- &THIS_MODULE, &[<$ data:upper _ATTRS >]
+ $crate::module::this_module::<crate::LocalModule>(),
+ &[<$ data:upper _ATTRS >]
);
)?
@@ -1030,7 +1032,8 @@ macro_rules! configfs_attrs {
$crate::configfs::ItemType<$container, $data> =
$crate::configfs::ItemType::<$container, $data>::
new_with_child_ctor::<N, $child>(
- &THIS_MODULE, &[<$ data:upper _ATTRS >]
+ $crate::module::this_module::<crate::LocalModule>(),
+ &[<$ data:upper _ATTRS >]
);
)?
diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
index 200def84fb69..8e36a4e7f514 100644
--- a/rust/kernel/dma.rs
+++ b/rust/kernel/dma.rs
@@ -449,7 +449,7 @@ impl<T: AsBytes + FromBytes> CoherentBox<[T]> {
// - `T: AsBytes + FromBytes` guarantees all bit patterns are valid, so partial writes on
// error cannot leave the element in an invalid state.
// - The DMA address has not been exposed yet, so there is no concurrent device access.
- unsafe { init.__init(ptr)? };
+ unsafe { pin_init::raw_try_init(ptr, init)? };
Ok(())
}
@@ -791,10 +791,10 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
// SAFETY:
// - `ptr` is valid, properly aligned, and points to exclusively owned memory.
- // - If `__init` fails, `self` is dropped, which safely frees the underlying `Coherent`'s
- // DMA memory. `T: AsBytes + FromBytes` ensures there are no complex `Drop` requirements
- // we are bypassing.
- unsafe { init.__init(ptr)? };
+ // - If `raw_try_init` fails, `self` is dropped, which safely frees the underlying
+ // `Coherent`'s DMA memory. `T: AsBytes + FromBytes` ensures there are no complex `Drop`
+ // requirements we are bypassing.
+ unsafe { pin_init::raw_try_init(ptr, init)? };
Ok(dmem)
}
diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
index 477cf771fb10..81f9f7e59817 100644
--- a/rust/kernel/drm/device.rs
+++ b/rust/kernel/drm/device.rs
@@ -203,7 +203,8 @@ impl<T: drm::Driver> UnregisteredDevice<T> {
fops: &Self::GEM_FOPS,
};
- const GEM_FOPS: bindings::file_operations = drm::gem::create_fops();
+ const GEM_FOPS: bindings::file_operations =
+ drm::gem::create_fops(crate::module::this_module::<T::OwnerModule>().as_ptr());
/// Create a new `UnregisteredDevice` for a `drm::Driver`.
///
@@ -244,7 +245,7 @@ impl<T: drm::Driver> UnregisteredDevice<T> {
// SAFETY:
// - `raw_data` is a valid pointer to uninitialized memory.
// - `raw_data` will not move until it is dropped.
- unsafe { data.__pinned_init(raw_data) }.inspect_err(|_| {
+ unsafe { pin_init::raw_try_init(raw_data, data) }.inspect_err(|_| {
// SAFETY: `__drm_dev_alloc()` was successful, hence `drm_dev` must be valid and the
// refcount must be non-zero.
unsafe { bindings::drm_dev_put(drm_dev) };
diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs
index c8b66d816871..a7ba1453d40b 100644
--- a/rust/kernel/drm/gem/mod.rs
+++ b/rust/kernel/drm/gem/mod.rs
@@ -387,10 +387,10 @@ impl<T: DriverObject, Ctx: DeviceContext> AllocImpl for Object<T, Ctx> {
};
}
-pub(super) const fn create_fops() -> bindings::file_operations {
+pub(super) const fn create_fops(owner: *mut bindings::module) -> bindings::file_operations {
let mut fops: bindings::file_operations = pin_init::zeroed();
- fops.owner = core::ptr::null_mut();
+ fops.owner = owner;
fops.open = Some(bindings::drm_open);
fops.release = Some(bindings::drm_release);
fops.unlocked_ioctl = Some(bindings::drm_ioctl);
diff --git a/rust/kernel/drm/gpuvm/va.rs b/rust/kernel/drm/gpuvm/va.rs
index 0b09fe44ab39..bf927b8e6fbb 100644
--- a/rust/kernel/drm/gpuvm/va.rs
+++ b/rust/kernel/drm/gpuvm/va.rs
@@ -116,7 +116,7 @@ impl<T: DriverGpuVm> GpuVaAlloc<T> {
pub(super) fn prepare(mut self, va_data: impl PinInit<T::VaData>) -> *mut bindings::drm_gpuva {
let va_ptr = MaybeUninit::as_mut_ptr(&mut self.0);
// SAFETY: The `data` field is pinned.
- let Ok(()) = unsafe { va_data.__pinned_init(&raw mut (*va_ptr).data) };
+ unsafe { pin_init::raw_init(&raw mut (*va_ptr).data, va_data) };
KBox::into_raw(self.0).cast()
}
}
diff --git a/rust/kernel/drm/gpuvm/vm_bo.rs b/rust/kernel/drm/gpuvm/vm_bo.rs
index c064ac63897b..ab12b710267e 100644
--- a/rust/kernel/drm/gpuvm/vm_bo.rs
+++ b/rust/kernel/drm/gpuvm/vm_bo.rs
@@ -181,7 +181,7 @@ impl<T: DriverGpuVm> GpuVmBoAlloc<T> {
};
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
// SAFETY: `ptr->data` is a valid pinned location.
- let Ok(()) = unsafe { value.__pinned_init(&raw mut (*raw_ptr).data) };
+ unsafe { pin_init::raw_init(&raw mut (*raw_ptr).data, value) };
// INVARIANTS: We just created the vm_bo so it's absent from lists, and the data is valid
// as we just initialized it.
Ok(GpuVmBoAlloc(ptr))
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index a56ba6309594..e52793f77196 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -30,6 +30,7 @@ pub mod code {
};
}
+ // From `include/uapi/asm-generic/errno-base.h`.
declare_err!(EPERM, "Operation not permitted.");
declare_err!(ENOENT, "No such file or directory.");
declare_err!(ESRCH, "No such process.");
@@ -64,9 +65,110 @@ pub mod code {
declare_err!(EPIPE, "Broken pipe.");
declare_err!(EDOM, "Math argument out of domain of func.");
declare_err!(ERANGE, "Math result not representable.");
+
+ // From `include/uapi/asm-generic/errno.h`.
+ declare_err!(EDEADLK, "Resource deadlock would occur.");
+ declare_err!(ENAMETOOLONG, "File name too long.");
+ declare_err!(ENOLCK, "No record locks available.");
+ declare_err!(ENOSYS, "Invalid system call number.");
+ declare_err!(ENOTEMPTY, "Directory not empty.");
+ declare_err!(ELOOP, "Too many symbolic links encountered.");
+ declare_err!(ENOMSG, "No message of desired type.");
+ declare_err!(EIDRM, "Identifier removed.");
+ declare_err!(ECHRNG, "Channel number out of range.");
+ declare_err!(EL2NSYNC, "Level 2 not synchronized.");
+ declare_err!(EL3HLT, "Level 3 halted.");
+ declare_err!(EL3RST, "Level 3 reset.");
+ declare_err!(ELNRNG, "Link number out of range.");
+ declare_err!(EUNATCH, "Protocol driver not attached.");
+ declare_err!(ENOCSI, "No CSI structure available.");
+ declare_err!(EL2HLT, "Level 2 halted.");
+ declare_err!(EBADE, "Invalid exchange.");
+ declare_err!(EBADR, "Invalid request descriptor.");
+ declare_err!(EXFULL, "Exchange full.");
+ declare_err!(ENOANO, "No anode.");
+ declare_err!(EBADRQC, "Invalid request code.");
+ declare_err!(EBADSLT, "Invalid slot.");
+ declare_err!(EBFONT, "Bad font file format.");
+ declare_err!(ENOSTR, "Device not a stream.");
+ declare_err!(ENODATA, "No data available.");
+ declare_err!(ETIME, "Timer expired.");
+ declare_err!(ENOSR, "Out of streams resources.");
+ declare_err!(ENONET, "Machine is not on the network.");
+ declare_err!(ENOPKG, "Package not installed.");
+ declare_err!(EREMOTE, "Object is remote.");
+ declare_err!(ENOLINK, "Link has been severed.");
+ declare_err!(EADV, "Advertise error.");
+ declare_err!(ESRMNT, "Srmount error.");
+ declare_err!(ECOMM, "Communication error on send.");
+ declare_err!(EPROTO, "Protocol error.");
+ declare_err!(EMULTIHOP, "Multihop attempted.");
+ declare_err!(EDOTDOT, "RFS specific error.");
+ declare_err!(EBADMSG, "Not a data message.");
+ declare_err!(EFSBADCRC, "Bad CRC detected.");
declare_err!(EOVERFLOW, "Value too large for defined data type.");
+ declare_err!(ENOTUNIQ, "Name not unique on network.");
+ declare_err!(EBADFD, "File descriptor in bad state.");
+ declare_err!(EREMCHG, "Remote address changed.");
+ declare_err!(ELIBACC, "Can not access a needed shared library.");
+ declare_err!(ELIBBAD, "Accessing a corrupted shared library.");
+ declare_err!(ELIBSCN, ".lib section in a.out corrupted.");
+ declare_err!(ELIBMAX, "Attempting to link in too many shared libraries.");
+ declare_err!(ELIBEXEC, "Cannot exec a shared library directly.");
+ declare_err!(EILSEQ, "Illegal byte sequence.");
+ declare_err!(ERESTART, "Interrupted system call should be restarted.");
+ declare_err!(ESTRPIPE, "Streams pipe error.");
+ declare_err!(EUSERS, "Too many users.");
+ declare_err!(ENOTSOCK, "Socket operation on non-socket.");
+ declare_err!(EDESTADDRREQ, "Destination address required.");
declare_err!(EMSGSIZE, "Message too long.");
+ declare_err!(EPROTOTYPE, "Protocol wrong type for socket.");
+ declare_err!(ENOPROTOOPT, "Protocol not available.");
+ declare_err!(EPROTONOSUPPORT, "Protocol not supported.");
+ declare_err!(ESOCKTNOSUPPORT, "Socket type not supported.");
+ declare_err!(EOPNOTSUPP, "Operation not supported on transport endpoint.");
+ declare_err!(EPFNOSUPPORT, "Protocol family not supported.");
+ declare_err!(EAFNOSUPPORT, "Address family not supported by protocol.");
+ declare_err!(EADDRINUSE, "Address already in use.");
+ declare_err!(EADDRNOTAVAIL, "Cannot assign requested address.");
+ declare_err!(ENETDOWN, "Network is down.");
+ declare_err!(ENETUNREACH, "Network is unreachable.");
+ declare_err!(ENETRESET, "Network dropped connection because of reset.");
+ declare_err!(ECONNABORTED, "Software caused connection abort.");
+ declare_err!(ECONNRESET, "Connection reset by peer.");
+ declare_err!(ENOBUFS, "No buffer space available.");
+ declare_err!(EISCONN, "Transport endpoint is already connected.");
+ declare_err!(ENOTCONN, "Transport endpoint is not connected.");
+ declare_err!(ESHUTDOWN, "Cannot send after transport endpoint shutdown.");
+ declare_err!(ETOOMANYREFS, "Too many references: cannot splice.");
declare_err!(ETIMEDOUT, "Connection timed out.");
+ declare_err!(ECONNREFUSED, "Connection refused.");
+ declare_err!(EHOSTDOWN, "Host is down.");
+ declare_err!(EHOSTUNREACH, "No route to host.");
+ declare_err!(EALREADY, "Operation already in progress.");
+ declare_err!(EINPROGRESS, "Operation now in progress.");
+ declare_err!(ESTALE, "Stale file handle.");
+ declare_err!(EUCLEAN, "Structure needs cleaning.");
+ declare_err!(EFSCORRUPTED, "Filesystem is corrupted.");
+ declare_err!(ENOTNAM, "Not a XENIX named type file.");
+ declare_err!(ENAVAIL, "No XENIX semaphores available.");
+ declare_err!(EISNAM, "Is a named type file.");
+ declare_err!(EREMOTEIO, "Remote I/O error.");
+ declare_err!(EDQUOT, "Quota exceeded.");
+ declare_err!(ENOMEDIUM, "No medium found.");
+ declare_err!(EMEDIUMTYPE, "Wrong medium type.");
+ declare_err!(ECANCELED, "Operation Canceled.");
+ declare_err!(ENOKEY, "Required key not available.");
+ declare_err!(EKEYEXPIRED, "Key has expired.");
+ declare_err!(EKEYREVOKED, "Key has been revoked.");
+ declare_err!(EKEYREJECTED, "Key was rejected by service.");
+ declare_err!(EOWNERDEAD, "Owner died.");
+ declare_err!(ENOTRECOVERABLE, "State not recoverable.");
+ declare_err!(ERFKILL, "Operation not possible due to RF-kill.");
+ declare_err!(EHWPOISON, "Memory page has hardware error.");
+ declare_err!(EFTYPE, "Wrong file type for the intended operation.");
+
+ // From `include/linux/errno.h`.
declare_err!(ERESTARTSYS, "Restart the system call.");
declare_err!(ERESTARTNOINTR, "System call was interrupted by a signal and will be restarted.");
declare_err!(ERESTARTNOHAND, "Restart if no handler.");
diff --git a/rust/kernel/fmt.rs b/rust/kernel/fmt.rs
index 73afbc51ba33..29582b053ab1 100644
--- a/rust/kernel/fmt.rs
+++ b/rust/kernel/fmt.rs
@@ -4,6 +4,8 @@
//!
//! This module is intended to be used in place of `core::fmt` in kernel code.
+use kernel::prelude::*;
+
pub use core::fmt::{
Arguments,
Debug,
@@ -39,11 +41,115 @@ use core::fmt::{
LowerExp,
LowerHex,
Octal,
- Pointer,
UpperExp,
UpperHex, //
};
-impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, Pointer, LowerExp, UpperExp);
+use core::ptr::NonNull;
+impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, LowerExp, UpperExp);
+
+/// A copy of [`core::fmt::Pointer`] that allows implementing pointer formatting for foreign types.
+///
+/// Together with the [`Adapter`] type and [`fmt!`] macro, it enables raw pointer formatting to be
+/// intercepted and routed to [`HashedPtr`] (kernel's `%p` hashed format), preventing kernel address
+/// leaks.
+///
+/// [`fmt!`]: crate::prelude::fmt!
+pub trait Pointer {
+ /// Same as [`core::fmt::Pointer::fmt`].
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result;
+}
+
+/// A wrapper for pointers that formats them using kernel's `%p` format specifier.
+///
+/// By default, `%p` prints a hashed representation of the pointer address to prevent kernel address
+/// leaks. When the `no_hash_pointers` kernel command-line parameter is enabled, the real address is
+/// printed instead (for debugging purposes).
+pub struct HashedPtr<T: ?Sized>(pub *const T);
+
+impl<T: ?Sized> Pointer for HashedPtr<T> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ use crate::str::CStrExt as _;
+
+ let mut buf = [0u8; 32];
+
+ // Use `%#0*p` for the `0x` prefix and zero-padding; `+2` compensates for
+ // the prefix counting toward the field width.
+ let default_width = (2 * size_of::<usize>() + 2) as c_int;
+ let width = match (f.sign_aware_zero_pad(), f.width()) {
+ (true, Some(w)) if w > 0 => w.min(buf.len() - 1) as c_int,
+ _ => default_width,
+ };
+
+ // SAFETY: `buf` is a valid, writable 32-byte buffer, sufficient for
+ // all architectures (max 19 bytes for 64-bit under the default width).
+ // The format string is null-terminated; `width` (c_int) and pointer
+ // match the `%*` and `%p` specifiers.
+ let len = unsafe {
+ crate::bindings::scnprintf(
+ buf.as_mut_ptr().cast(),
+ buf.len(),
+ c"%#0*p".as_char_ptr(),
+ width,
+ self.0.cast::<c_void>(),
+ )
+ };
+
+ // SAFETY: `%#0*p` produces only ASCII, which is valid UTF-8.
+ let s = unsafe { core::str::from_utf8_unchecked(&buf[..len as usize]) };
+
+ if f.sign_aware_zero_pad() {
+ // `scnprintf` already applied the width and zero-padding via `%#0*p`.
+ f.write_str(s)
+ } else {
+ f.pad(s)
+ }
+ }
+}
+
+// Raw pointers are formatted via `HashedPtr` (kernel `%p`: hashed by default, plain with
+// `no_hash_pointers`).
+impl<T: ?Sized> Pointer for *const T {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ Pointer::fmt(&HashedPtr(*self), f)
+ }
+}
+
+impl<T: ?Sized> Pointer for *mut T {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ Pointer::fmt(&HashedPtr(*self), f)
+ }
+}
+
+impl<T: ?Sized> Pointer for &T {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ Pointer::fmt(&HashedPtr(*self), f)
+ }
+}
+
+impl<T: ?Sized> Pointer for &mut T {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ Pointer::fmt(&HashedPtr(core::ptr::from_ref(*self)), f)
+ }
+}
+
+impl<T: ?Sized> Pointer for NonNull<T> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ Pointer::fmt(&HashedPtr(self.as_ptr()), f)
+ }
+}
+
+// `Adapter<&T>` bridges our `Pointer` trait to `core::fmt::Pointer`
+impl<T: Pointer> core::fmt::Pointer for Adapter<&T> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ Pointer::fmt(self.0, f)
+ }
+}
/// A copy of [`core::fmt::Display`] that allows us to implement it for foreign types.
///
@@ -105,3 +211,88 @@ impl_display_forward!(
{<T: ?Sized>} crate::sync::Arc<T> {where crate::sync::Arc<T>: core::fmt::Display},
{<T: ?Sized>} crate::sync::UniqueArc<T> {where crate::sync::UniqueArc<T>: core::fmt::Display},
);
+
+#[macros::kunit_tests(rust_kernel_fmt)]
+mod tests {
+ use crate::{
+ bindings,
+ prelude::fmt,
+ str::CString, //
+ };
+
+ #[cfg(CONFIG_64BIT)]
+ mod expected {
+ pub(super) const PTR_VALUE: usize = 0xffffffffdeadbeef;
+ pub(super) const PTR_VAL_NO_CRNG: &str = "(____ptrval____)";
+ pub(super) const HASHED_PREFIX: &str = "0x00000000";
+ pub(super) const RAW_POINTER: &str = "0xffffffffdeadbeef";
+ pub(super) const PADDED_RIGHT: &str = " 0xffffffffdeadbeef";
+ pub(super) const ZERO_PADDED: &str = "0x000000ffffffffdeadbeef";
+ pub(super) const HASHED_PADDED_RIGHT_PREFIX: &str = " ";
+ pub(super) const HASHED_ZERO_PADDED_PREFIX: &str = "0x00000000000000";
+ pub(super) const CLAMPED: &str = "0x0000000000000ffffffffdeadbeef";
+ }
+
+ #[cfg(not(CONFIG_64BIT))]
+ mod expected {
+ pub(super) const PTR_VALUE: usize = 0xdeadbeef;
+ pub(super) const PTR_VAL_NO_CRNG: &str = "(ptrval)";
+ pub(super) const HASHED_PREFIX: &str = "0x";
+ pub(super) const RAW_POINTER: &str = "0xdeadbeef";
+ pub(super) const PADDED_RIGHT: &str = " 0xdeadbeef";
+ pub(super) const ZERO_PADDED: &str = "0x00000000000000deadbeef";
+ pub(super) const HASHED_PADDED_RIGHT_PREFIX: &str = " ";
+ pub(super) const HASHED_ZERO_PADDED_PREFIX: &str = "0x00000000000000";
+ pub(super) const CLAMPED: &str = "0x0000000000000000000000deadbeef";
+ }
+
+ #[test]
+ fn test_ptr_formatting() -> core::result::Result<(), crate::error::Error> {
+ let ptr: *const u8 = core::ptr::without_provenance(expected::PTR_VALUE);
+
+ // SAFETY: `no_hash_pointers` is a global variable that is never concurrently modified —
+ // KUnit tests may run at boot (before `mark_readonly()`) or manually afterwards (when the
+ // variable is read-only). Reading is always safe.
+ let no_hash = unsafe { bindings::no_hash_pointers };
+
+ if no_hash {
+ let cstr = CString::try_from_fmt(fmt!("{:p}", ptr))?;
+ assert_eq!(cstr.to_str()?, expected::RAW_POINTER);
+
+ let cstr = CString::try_from_fmt(fmt!("{:>24p}", ptr))?;
+ assert_eq!(cstr.to_str()?, expected::PADDED_RIGHT);
+
+ let cstr = CString::try_from_fmt(fmt!("{:024p}", ptr))?;
+ assert_eq!(cstr.to_str()?, expected::ZERO_PADDED);
+
+ let cstr = CString::try_from_fmt(fmt!("{:0100p}", ptr))?;
+ assert_eq!(cstr.to_str()?, expected::CLAMPED);
+ } else {
+ let cstr = CString::try_from_fmt(fmt!("{:p}", ptr))?;
+ let formatted = cstr.to_str()?;
+ // If the RNG is not yet ready, `%p` falls back to a placeholder.
+ if formatted == expected::PTR_VAL_NO_CRNG {
+ return Ok(());
+ }
+ assert!(formatted.starts_with(expected::HASHED_PREFIX));
+ assert_ne!(formatted, expected::RAW_POINTER);
+
+ let cstr = CString::try_from_fmt(fmt!("{:>24p}", ptr))?;
+ assert!(cstr
+ .to_str()?
+ .starts_with(expected::HASHED_PADDED_RIGHT_PREFIX));
+
+ let cstr = CString::try_from_fmt(fmt!("{:024p}", ptr))?;
+ assert!(cstr
+ .to_str()?
+ .starts_with(expected::HASHED_ZERO_PADDED_PREFIX));
+
+ let cstr = CString::try_from_fmt(fmt!("{:0100p}", ptr))?;
+ let output = cstr.to_str()?;
+ assert!(output.starts_with("0x"));
+ assert!(!output[2..].chars().all(|c| c == '0'));
+ }
+
+ Ok(())
+ }
+}
diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index 624b971ca8b0..dd9271af5eb8 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -142,7 +142,7 @@ unsafe impl<T: Driver> driver::RegistrationOps for Adapter<T> {
}
// SAFETY: `idrv` is guaranteed to be a valid `DriverType`.
- to_result(unsafe { bindings::i2c_register_driver(module.0, idrv.get()) })
+ to_result(unsafe { bindings::i2c_register_driver(module.as_ptr(), idrv.get()) })
}
unsafe fn unregister(idrv: &Opaque<Self::DriverType>) {
diff --git a/rust/kernel/impl_flags.rs b/rust/kernel/impl_flags.rs
index e2bd7639da12..fdf44d5eea9c 100644
--- a/rust/kernel/impl_flags.rs
+++ b/rust/kernel/impl_flags.rs
@@ -19,7 +19,10 @@
/// # Examples
///
/// ```
-/// use kernel::impl_flags;
+/// use kernel::{
+/// bits::bit_u32,
+/// impl_flags, //
+/// };
///
/// impl_flags!(
/// /// Represents multiple permissions.
@@ -30,13 +33,13 @@
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// pub enum Permission {
/// /// Read permission.
-/// Read = 1 << 0,
+/// Read = bit_u32(0),
///
/// /// Write permission.
-/// Write = 1 << 1,
+/// Write = bit_u32(1),