Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+301
View File
@@ -0,0 +1,301 @@
// SPDX-License-Identifier: GPL-2.0
//! Generic memory-mapped IO.
use core::ops::Deref;
use crate::{
device::{
Bound,
Device, //
},
devres::Devres,
io::{
self,
resource::{
Region,
Resource, //
},
Mmio,
MmioRaw, //
},
prelude::*,
};
/// An IO request for a specific device and resource.
pub struct IoRequest<'a> {
device: &'a Device<Bound>,
resource: &'a Resource,
}
impl<'a> IoRequest<'a> {
/// Creates a new [`IoRequest`] instance.
///
/// # Safety
///
/// Callers must ensure that `resource` is valid for `device` during the
/// lifetime `'a`.
pub(crate) unsafe fn new(device: &'a Device<Bound>, resource: &'a Resource) -> Self {
IoRequest { device, resource }
}
/// Maps an [`IoRequest`] where the size is known at compile time.
///
/// This uses the [`ioremap()`] C API.
///
/// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
///
/// # Examples
///
/// The following example uses a [`kernel::platform::Device`] for
/// illustration purposes.
///
/// ```no_run
/// use kernel::{
/// bindings,
/// device::Core,
/// io::Io,
/// of,
/// platform,
/// };
/// struct SampleDriver;
///
/// impl platform::Driver for SampleDriver {
/// # type IdInfo = ();
/// # type Data<'bound> = Self;
///
/// fn probe<'bound>(
/// pdev: &'bound platform::Device<Core<'_>>,
/// info: Option<&'bound Self::IdInfo>,
/// ) -> impl PinInit<Self, Error> + 'bound {
/// let offset = 0; // Some offset.
///
/// // If the size is known at compile time, use [`Self::iomap_sized`].
/// //
/// // No runtime checks will apply when reading and writing.
/// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
/// let iomem = request.iomap_sized::<42>()?;
///
/// // Read and write a 32-bit value at `offset`.
/// let data = iomem.read32(offset);
///
/// iomem.write32(data, offset);
///
/// # Ok(SampleDriver)
/// }
/// }
/// ```
pub fn iomap_sized<const SIZE: usize>(self) -> Result<IoMem<'a, SIZE>> {
IoMem::ioremap(self.device, self.resource)
}
/// Same as [`Self::iomap_sized`] but with exclusive access to the
/// underlying region.
///
/// This uses the [`ioremap()`] C API.
///
/// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
pub fn iomap_exclusive_sized<const SIZE: usize>(self) -> Result<ExclusiveIoMem<'a, SIZE>> {
ExclusiveIoMem::ioremap(self.device, self.resource)
}
/// Maps an [`IoRequest`] where the size is not known at compile time,
///
/// This uses the [`ioremap()`] C API.
///
/// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
///
/// # Examples
///
/// The following example uses a [`kernel::platform::Device`] for
/// illustration purposes.
///
/// ```no_run
/// use kernel::{
/// bindings,
/// device::Core,
/// io::Io,
/// of,
/// platform,
/// };
/// struct SampleDriver;
///
/// impl platform::Driver for SampleDriver {
/// # type IdInfo = ();
/// # type Data<'bound> = Self;
///
/// fn probe<'bound>(
/// pdev: &'bound platform::Device<Core<'_>>,
/// info: Option<&'bound Self::IdInfo>,
/// ) -> impl PinInit<Self, Error> + 'bound {
/// let offset = 0; // Some offset.
///
/// // Unlike [`Self::iomap_sized`], here the size of the memory region
/// // is not known at compile time, so only the `try_read*` and `try_write*`
/// // family of functions should be used, leading to runtime checks on every
/// // access.
/// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
/// let iomem = request.iomap()?;
///
/// let data = iomem.try_read32(offset)?;
///
/// iomem.try_write32(data, offset)?;
///
/// # Ok(SampleDriver)
/// }
/// }
/// ```
pub fn iomap(self) -> Result<IoMem<'a>> {
self.iomap_sized::<0>()
}
/// Same as [`Self::iomap`] but with exclusive access to the underlying
/// region.
pub fn iomap_exclusive(self) -> Result<ExclusiveIoMem<'a, 0>> {
self.iomap_exclusive_sized::<0>()
}
}
/// An exclusive memory-mapped IO region.
///
/// # Invariants
///
/// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`].
pub struct ExclusiveIoMem<'a, const SIZE: usize> {
/// The underlying `IoMem` instance.
iomem: IoMem<'a, SIZE>,
/// The region abstraction. This represents exclusive access to the
/// range represented by the underlying `iomem`.
///
/// This field is needed for ownership of the region.
_region: Region,
}
impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> {
/// Creates a new `ExclusiveIoMem` instance.
fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> {
let start = resource.start();
let size = resource.size();
let name = resource.name().unwrap_or_default();
let region = resource
.request_region(
start,
size,
name.to_cstring()?,
io::resource::Flags::IORESOURCE_MEM,
)
.ok_or(EBUSY)?;
let iomem = IoMem::ioremap(dev, resource)?;
Ok(ExclusiveIoMem {
iomem,
_region: region,
})
}
/// Consume the `ExclusiveIoMem` and register it as a device-managed resource.
///
/// The returned `Devres<ExclusiveIoMem<'static, SIZE>>` can outlive the original lifetime
/// `'a`. Access to the I/O memory is revoked when the device is unbound.
pub fn into_devres(self) -> Result<Devres<ExclusiveIoMem<'static, SIZE>>> {
// SAFETY: Casting to `'static` is sound because `Devres` guarantees the
// `ExclusiveIoMem` does not actually outlive the device -- access is revoked and the
// resource is released when the device is unbound.
let iomem: ExclusiveIoMem<'static, SIZE> = unsafe { core::mem::transmute(self) };
let dev = iomem.iomem.dev;
Devres::new(dev, iomem)
}
}
impl<const SIZE: usize> Deref for ExclusiveIoMem<'_, SIZE> {
type Target = Mmio<SIZE>;
fn deref(&self) -> &Self::Target {
&self.iomem
}
}
/// A generic memory-mapped IO region.
///
/// Accesses to the underlying region is checked either at compile time, if the
/// region's size is known at that point, or at runtime otherwise.
///
/// # Invariants
///
/// [`IoMem`] always holds an [`MmioRaw`] instance that holds a valid pointer to the
/// start of the I/O memory mapped region.
pub struct IoMem<'a, const SIZE: usize = 0> {
dev: &'a Device<Bound>,
io: MmioRaw<SIZE>,
}
impl<'a, const SIZE: usize> IoMem<'a, SIZE> {
fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> {
// Note: Some ioremap() implementations use types that depend on the CPU
// word width rather than the bus address width.
//
// TODO: Properly address this in the C code to avoid this `try_into`.
let size = resource.size().try_into()?;
if size == 0 {
return Err(EINVAL);
}
let res_start = resource.start();
let addr = if resource
.flags()
.contains(io::resource::Flags::IORESOURCE_MEM_NONPOSTED)
{
// SAFETY:
// - `res_start` and `size` are read from a presumably valid `struct resource`.
// - `size` is known not to be zero at this point.
unsafe { bindings::ioremap_np(res_start, size) }
} else {
// SAFETY:
// - `res_start` and `size` are read from a presumably valid `struct resource`.
// - `size` is known not to be zero at this point.
unsafe { bindings::ioremap(res_start, size) }
};
if addr.is_null() {
return Err(ENOMEM);
}
let io = MmioRaw::new(addr as usize, size)?;
Ok(IoMem { dev, io })
}
/// Consume the `IoMem` and register it as a device-managed resource.
///
/// The returned `Devres<IoMem<'static, SIZE>>` can outlive the original
/// lifetime `'a`. Access to the I/O memory is revoked when the device
/// is unbound.
pub fn into_devres(self) -> Result<Devres<IoMem<'static, SIZE>>> {
// SAFETY: Casting to `'static` is sound because `Devres` guarantees the `IoMem` does not
// actually outlive the device -- access is revoked and the resource is released when the
// device is unbound.
let iomem: IoMem<'static, SIZE> = unsafe { core::mem::transmute(self) };
let dev = iomem.dev;
Devres::new(dev, iomem)
}
}
impl<const SIZE: usize> Drop for IoMem<'_, SIZE> {
fn drop(&mut self) {
// SAFETY: Safe as by the invariant of `Io`.
unsafe { bindings::iounmap(self.io.addr() as *mut c_void) }
}
}
impl<const SIZE: usize> Deref for IoMem<'_, SIZE> {
type Target = Mmio<SIZE>;
fn deref(&self) -> &Self::Target {
// SAFETY: Safe as by the invariant of `IoMem`.
unsafe { Mmio::from_raw(&self.io) }
}
}
+181
View File
@@ -0,0 +1,181 @@
// SPDX-License-Identifier: GPL-2.0
//! IO polling.
//!
//! C header: [`include/linux/iopoll.h`](srctree/include/linux/iopoll.h).
use crate::{
prelude::*,
processor::cpu_relax,
task::might_sleep,
time::{
delay::{
fsleep,
udelay, //
},
Delta,
Instant,
Monotonic, //
},
};
/// Polls periodically until a condition is met, an error occurs,
/// or the timeout is reached.
///
/// The function repeatedly executes the given operation `op` closure and
/// checks its result using the condition closure `cond`.
///
/// If `cond` returns `true`, the function returns successfully with
/// the result of `op`. Otherwise, it waits for a duration specified
/// by `sleep_delta` before executing `op` again.
///
/// This process continues until either `op` returns an error, `cond`
/// returns `true`, or the timeout specified by `timeout_delta` is
/// reached.
///
/// This function can only be used in a nonatomic context.
///
/// # Errors
///
/// If `op` returns an error, then that error is returned directly.
///
/// If the timeout specified by `timeout_delta` is reached, then
/// `Err(ETIMEDOUT)` is returned.
///
/// # Examples
///
/// ```no_run
/// use kernel::io::{
/// Io,
/// Mmio,
/// poll::read_poll_timeout, //
/// };
/// use kernel::time::Delta;
///
/// const HW_READY: u16 = 0x01;
///
/// fn wait_for_hardware<const SIZE: usize>(io: &Mmio<SIZE>) -> Result {
/// read_poll_timeout(
/// // The `op` closure reads the value of a specific status register.
/// || io.try_read16(0x1000),
/// // The `cond` closure takes a reference to the value returned by `op`
/// // and checks whether the hardware is ready.
/// |val: &u16| *val == HW_READY,
/// Delta::from_millis(50),
/// Delta::from_secs(3),
/// )?;
/// Ok(())
/// }
/// ```
#[track_caller]
pub fn read_poll_timeout<Op, Cond, T>(
mut op: Op,
mut cond: Cond,
sleep_delta: Delta,
timeout_delta: Delta,
) -> Result<T>
where
Op: FnMut() -> Result<T>,
Cond: FnMut(&T) -> bool,
{
let start: Instant<Monotonic> = Instant::now();
// Unlike the C version, we always call `might_sleep()` unconditionally,
// as conditional calls are error-prone. We clearly separate
// `read_poll_timeout()` and `read_poll_timeout_atomic()` to aid
// tools like klint.
might_sleep();
loop {
let val = op()?;
if cond(&val) {
// Unlike the C version, we immediately return.
// We know the condition is met so we don't need to check again.
return Ok(val);
}
if start.elapsed() > timeout_delta {
// Unlike the C version, we immediately return.
// We have just called `op()` so we don't need to call it again.
return Err(ETIMEDOUT);
}
if !sleep_delta.is_zero() {
fsleep(sleep_delta);
}
// `fsleep()` could be a busy-wait loop so we always call `cpu_relax()`.
cpu_relax();
}
}
/// Polls periodically until a condition is met, an error occurs,
/// or the attempt limit is reached.
///
/// The function repeatedly executes the given operation `op` closure and
/// checks its result using the condition closure `cond`.
///
/// If `cond` returns `true`, the function returns successfully with the result of `op`.
/// Otherwise, it performs a busy wait for a duration specified by `delay_delta`
/// before executing `op` again.
///
/// This process continues until either `op` returns an error, `cond`
/// returns `true`, or the attempt limit specified by `retry` is reached.
///
/// # Errors
///
/// If `op` returns an error, then that error is returned directly.
///
/// If the attempt limit specified by `retry` is reached, then
/// `Err(ETIMEDOUT)` is returned.
///
/// # Examples
///
/// ```no_run
/// use kernel::io::{
/// Io,
/// Mmio,
/// poll::read_poll_timeout_atomic, //
/// };
/// use kernel::time::Delta;
///
/// const HW_READY: u16 = 0x01;
///
/// fn wait_for_hardware<const SIZE: usize>(io: &Mmio<SIZE>) -> Result {
/// read_poll_timeout_atomic(
/// // The `op` closure reads the value of a specific status register.
/// || io.try_read16(0x1000),
/// // The `cond` closure takes a reference to the value returned by `op`
/// // and checks whether the hardware is ready.
/// |val: &u16| *val == HW_READY,
/// Delta::from_micros(50),
/// 1000,
/// )?;
/// Ok(())
/// }
/// ```
pub fn read_poll_timeout_atomic<Op, Cond, T>(
mut op: Op,
mut cond: Cond,
delay_delta: Delta,
retry: usize,
) -> Result<T>
where
Op: FnMut() -> Result<T>,
Cond: FnMut(&T) -> bool,
{
for _ in 0..retry {
let val = op()?;
if cond(&val) {
return Ok(val);
}
if !delay_delta.is_zero() {
udelay(delay_delta);
}
cpu_relax();
}
Err(ETIMEDOUT)
}
File diff suppressed because it is too large Load Diff
+235
View File
@@ -0,0 +1,235 @@
// SPDX-License-Identifier: GPL-2.0
//! Abstractions for [system
//! resources](https://docs.kernel.org/core-api/kernel-api.html#resources-management).
//!
//! C header: [`include/linux/ioport.h`](srctree/include/linux/ioport.h)
use core::{
ops::Deref,
ptr::NonNull, //
};
use crate::{
prelude::*,
str::CString,
types::Opaque, //
};
pub use super::{
PhysAddr,
ResourceSize, //
};
/// A region allocated from a parent [`Resource`].
///
/// # Invariants
///
/// - `self.0` points to a valid `bindings::resource` that was obtained through
/// `bindings::__request_region`.
pub struct Region {
/// The resource returned when the region was requested.
resource: NonNull<bindings::resource>,
/// The name that was passed in when the region was requested. We need to
/// store it for ownership reasons.
_name: CString,
}
impl Deref for Region {
type Target = Resource;
fn deref(&self) -> &Self::Target {
// SAFETY: Safe as per the invariant of `Region`.
unsafe { Resource::from_raw(self.resource.as_ptr()) }
}
}
impl Drop for Region {
fn drop(&mut self) {
let (flags, start, size) = {
let res = &**self;
(res.flags(), res.start(), res.size())
};
let release_fn = if flags.contains(Flags::IORESOURCE_MEM) {
bindings::release_mem_region
} else {
bindings::release_region
};
// SAFETY: Safe as per the invariant of `Region`.
unsafe { release_fn(start, size) };
}
}
// SAFETY: `Region` only holds a pointer to a C `struct resource`, which is safe to be used from
// any thread.
unsafe impl Send for Region {}
// SAFETY: `Region` only holds a pointer to a C `struct resource`, references to which are
// safe to be used from any thread.
unsafe impl Sync for Region {}
/// A resource abstraction.
///
/// # Invariants
///
/// [`Resource`] is a transparent wrapper around a valid `bindings::resource`.
#[repr(transparent)]
pub struct Resource(Opaque<bindings::resource>);
impl Resource {
/// Creates a reference to a [`Resource`] from a valid pointer.
///
/// # Safety
///
/// The caller must ensure that for the duration of 'a, the pointer will
/// point at a valid `bindings::resource`.
///
/// The caller must also ensure that the [`Resource`] is only accessed via the
/// returned reference for the duration of 'a.
pub(crate) const unsafe fn from_raw<'a>(ptr: *mut bindings::resource) -> &'a Self {
// SAFETY: Self is a transparent wrapper around `Opaque<bindings::resource>`.
unsafe { &*ptr.cast() }
}
/// Requests a resource region.
///
/// Exclusive access will be given and the region will be marked as busy.
/// Further calls to [`Self::request_region`] will return [`None`] if
/// the region, or a part of it, is already in use.
pub fn request_region(
&self,
start: PhysAddr,
size: ResourceSize,
name: CString,
flags: Flags,
) -> Option<Region> {
// SAFETY:
// - Safe as per the invariant of `Resource`.
// - `__request_region` will store a reference to the name, but that is
// safe as we own it and it will not be dropped until the `Region` is
// dropped.
let region = unsafe {
bindings::__request_region(
self.0.get(),
start,
size,
name.as_char_ptr(),
flags.0 as c_int,
)
};
Some(Region {
resource: NonNull::new(region)?,
_name: name,
})
}
/// Returns the size of the resource.
pub fn size(&self) -> ResourceSize {
let inner = self.0.get();
// SAFETY: Safe as per the invariants of `Resource`.
unsafe { bindings::resource_size(inner) }
}
/// Returns the start address of the resource.
pub fn start(&self) -> PhysAddr {
let inner = self.0.get();
// SAFETY: Safe as per the invariants of `Resource`.
unsafe { (*inner).start }
}
/// Returns the name of the resource.
pub fn name(&self) -> Option<&CStr> {
let inner = self.0.get();
// SAFETY: Safe as per the invariants of `Resource`.
let name = unsafe { (*inner).name };
if name.is_null() {
return None;
}
// SAFETY: In the C code, `resource::name` either contains a null
// pointer or points to a valid NUL-terminated C string, and at this
// point we know it is not null, so we can safely convert it to a
// `CStr`.
Some(unsafe { CStr::from_char_ptr(name) })
}
/// Returns the flags associated with the resource.
pub fn flags(&self) -> Flags {
let inner = self.0.get();
// SAFETY: Safe as per the invariants of `Resource`.
let flags = unsafe { (*inner).flags };
Flags(flags)
}
}
// SAFETY: `Resource` only holds a pointer to a C `struct resource`, which is
// safe to be used from any thread.
unsafe impl Send for Resource {}
// SAFETY: `Resource` only holds a pointer to a C `struct resource`, references
// to which are safe to be used from any thread.
unsafe impl Sync for Resource {}
/// Resource flags as stored in the C `struct resource::flags` field.
///
/// They can be combined with the operators `|`, `&`, and `!`.
///
/// Values can be used from the associated constants such as
/// [`Flags::IORESOURCE_IO`].
#[derive(Clone, Copy, PartialEq)]
pub struct Flags(c_ulong);
impl Flags {
/// Check whether `flags` is contained in `self`.
pub fn contains(self, flags: Flags) -> bool {
(self & flags) == flags
}
}
impl core::ops::BitOr for Flags {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl core::ops::BitAnd for Flags {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.0 & rhs.0)
}
}
impl core::ops::Not for Flags {
type Output = Self;
fn not(self) -> Self::Output {
Self(!self.0)
}
}
impl Flags {
/// PCI/ISA I/O ports.
pub const IORESOURCE_IO: Flags = Flags::new(bindings::IORESOURCE_IO);
/// Resource is software muxed.
pub const IORESOURCE_MUXED: Flags = Flags::new(bindings::IORESOURCE_MUXED);
/// Resource represents a memory region.
pub const IORESOURCE_MEM: Flags = Flags::new(bindings::IORESOURCE_MEM);
/// Resource represents a memory region that must be ioremaped using `ioremap_np`.
pub const IORESOURCE_MEM_NONPOSTED: Flags = Flags::new(bindings::IORESOURCE_MEM_NONPOSTED);
// Always inline to optimize out error path of `build_assert`.
#[inline(always)]
const fn new(value: u32) -> Self {
build_assert!(value as u64 <= c_ulong::MAX as u64);
Flags(value as c_ulong)
}
}