Skip to content

rust: platform: add ioremap_resource and get_resource methods #682

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: rust
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions rust/kernel/io_mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
use crate::{bindings, error::code::*, Result};
use core::convert::TryInto;

/// The type of `Resource`.
pub enum IoResource {
/// i/o memory
Mem = bindings::IORESOURCE_MEM as _,
}

/// Represents a memory resource.
pub struct Resource {
offset: bindings::resource_size_t,
Expand Down
64 changes: 60 additions & 4 deletions rust/kernel/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
//! C header: [`include/linux/platform_device.h`](../../../../include/linux/platform_device.h)

use crate::{
bindings, c_types,
bindings, bit, c_types,
device::{self, RawDevice},
driver,
error::{from_kernel_result, Result},
error::{code::*, from_kernel_result},
io_mem::{IoMem, IoResource, Resource},
of,
str::CStr,
to_result,
types::PointerWrapper,
ThisModule,
Result, ThisModule,
};

/// A registration of a platform driver.
Expand Down Expand Up @@ -161,6 +162,7 @@ pub trait Driver {
/// The field `ptr` is non-null and valid for the lifetime of the object.
pub struct Device {
ptr: *mut bindings::platform_device,
used_resource: u64,
}

impl Device {
Expand All @@ -172,14 +174,68 @@ impl Device {
/// instance.
unsafe fn from_ptr(ptr: *mut bindings::platform_device) -> Self {
// INVARIANT: The safety requirements of the function ensure the lifetime invariant.
Self { ptr }
Self {
ptr,
used_resource: 0,
}
}

/// Returns id of the platform device.
pub fn id(&self) -> i32 {
// SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid.
unsafe { (*self.ptr).id }
}

/// Gets a system resources of a platform device.
pub fn get_resource(&mut self, rtype: IoResource, num: usize) -> Result<Resource> {
// SAFETY: `self.ptr` is valid by the type invariant.
let res = unsafe { bindings::platform_get_resource(self.ptr, rtype as _, num as _) };
if res.is_null() {
return Err(EINVAL);
}

// Get the position of the found resource in the array.
// SAFETY:
// - `self.ptr` is valid by the type invariant.
// - `res` is a displaced pointer to one of the array's elements,
// and `resource` is its base pointer.
let index = unsafe { res.offset_from((*self.ptr).resource) } as usize;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This happens to be how this is implemented today, but isn't necessarily going to always be this way. So I suggest we explicitly test here that the pointer is between self.ptr.resource and self.ptr.resource + 64. If it isn't, we should fail the request.


// Make sure that the index does not exceed the 64-bit mask.
assert!(index < 64);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't necessarily true, is it?

Instead of an assert this, should just be a check. If it's greater than or equal to 64, we fail the request.


if self.used_resource >> index & bit(0) == 1 {
return Err(EBUSY);
}
self.used_resource |= bit(index);

// SAFETY: The pointer `res` is returned from `bindings::platform_get_resource`
// above and checked if it is not a NULL.
unsafe { Resource::new((*res).start, (*res).end) }.ok_or(EINVAL)
}

/// Ioremaps resources of a platform device.
///
/// # Safety
///
/// Callers must ensure that either (a) the resulting interface cannot be used to initiate DMA
/// operations, or (b) that DMA operations initiated via the returned interface use DMA handles
/// allocated through the `dma` module.
pub unsafe fn ioremap_resource<const SIZE: usize>(
&mut self,
index: usize,
) -> Result<IoMem<SIZE>> {
let mask = self.used_resource;
let res = self.get_resource(IoResource::Mem, index)?;

// SAFETY: Valid by the safety contract.
let iomem = unsafe { IoMem::<SIZE>::try_new(res) };
// If remapping fails, the given resource won't be used, so restore the old mask.
if iomem.is_err() {
self.used_resource = mask;
}
iomem
}
}

// SAFETY: The device returned by `raw_device` is the raw platform device.
Expand Down