Skip to content

rust: gpio: add support for registering irq chips with gpio chip. #565

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

Merged
merged 1 commit into from
Nov 27, 2021
Merged
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
28 changes: 28 additions & 0 deletions rust/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include <linux/security.h>
#include <asm/io.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/amba/bus.h>

__noreturn void rust_helper_BUG(void)
Expand Down Expand Up @@ -374,6 +376,32 @@ void *rust_helper_irq_data_get_irq_chip_data(struct irq_data *d)
}
EXPORT_SYMBOL_GPL(rust_helper_irq_data_get_irq_chip_data);

struct irq_chip *rust_helper_irq_desc_get_chip(struct irq_desc *desc)
{
return irq_desc_get_chip(desc);
}
EXPORT_SYMBOL_GPL(rust_helper_irq_desc_get_chip);

void *rust_helper_irq_desc_get_handler_data(struct irq_desc *desc)
{
return irq_desc_get_handler_data(desc);
}
EXPORT_SYMBOL_GPL(rust_helper_irq_desc_get_handler_data);

void rust_helper_chained_irq_enter(struct irq_chip *chip,
struct irq_desc *desc)
{
chained_irq_enter(chip, desc);
}
EXPORT_SYMBOL_GPL(rust_helper_chained_irq_enter);

void rust_helper_chained_irq_exit(struct irq_chip *chip,
struct irq_desc *desc)
{
chained_irq_exit(chip, desc);
}
EXPORT_SYMBOL_GPL(rust_helper_chained_irq_exit);

/* We use bindgen's --size_t-is-usize option to bind the C size_t type
* as the Rust usize type, so we can use it in contexts where Rust
* expects a usize like slice (array) indices. usize is defined to be
Expand Down
181 changes: 177 additions & 4 deletions rust/kernel/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ use core::{
pin::Pin,
};

#[cfg(CONFIG_GPIOLIB_IRQCHIP)]
pub use irqchip::{ChipWithIrqChip, RegistrationWithIrqChip};

/// The direction of a gpio line.
pub enum LineDirection {
/// Direction is input.
Expand Down Expand Up @@ -141,14 +144,13 @@ impl<T: Chip> Registration<T> {
parent: &dyn device::RawDevice,
data: T::Data,
) -> Result {
// SAFETY: We never move out of `this`.
let this = unsafe { self.get_unchecked_mut() };

if this.parent.is_some() {
if self.parent.is_some() {
// Already registered.
return Err(Error::EINVAL);
}

// SAFETY: We never move out of `this`.
let this = unsafe { self.get_unchecked_mut() };
{
let gc = this.gc.get_mut();

Expand Down Expand Up @@ -297,3 +299,174 @@ unsafe extern "C" fn set_callback<T: Chip>(
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc)) };
T::set(data, offset, value != 0);
}

#[cfg(CONFIG_GPIOLIB_IRQCHIP)]
mod irqchip {
use super::*;
use crate::irq;

/// A gpio chip that includes an irq chip.
pub trait ChipWithIrqChip: Chip {
/// Implements the irq flow for the gpio chip.
fn handle_irq_flow(
_data: <Self::Data as PointerWrapper>::Borrowed<'_>,
_desc: &irq::Descriptor,
_domain: &irq::Domain,
);
}

/// A registration of a gpio chip that includes an irq chip.
pub struct RegistrationWithIrqChip<T: ChipWithIrqChip> {
reg: Registration<T>,
irq_chip: UnsafeCell<bindings::irq_chip>,
parent_irq: u32,
}

impl<T: ChipWithIrqChip> RegistrationWithIrqChip<T> {
/// Creates a new [`RegistrationWithIrqChip`] but does not register it yet.
///
/// It is allowed to move.
pub fn new() -> Self {
Self {
reg: Registration::new(),
irq_chip: UnsafeCell::new(bindings::irq_chip::default()),
parent_irq: 0,
}
}

/// Registers a gpio chip and its irq chip with the rest of the kernel.
pub fn register<U: irq::Chip<Data = T::Data>>(
mut self: Pin<&mut Self>,
gpio_count: u16,
base: Option<i32>,
parent: &dyn device::RawDevice,
data: T::Data,
parent_irq: u32,
) -> Result {
if self.reg.parent.is_some() {
// Already registered.
return Err(Error::EINVAL);
}

// SAFETY: We never move out of `this`.
let this = unsafe { self.as_mut().get_unchecked_mut() };

// Initialise the irq_chip.
{
let irq_chip = this.irq_chip.get_mut();
irq_chip.name = parent.name().as_char_ptr();

// SAFETY: The gpio subsystem configures a pointer to `gpio_chip` as the irq chip
// data, so we use `IrqChipAdapter` to convert to the `T::Data`, which is the same
// as `irq::Chip::Data` per the bound above.
unsafe { irq::init_chip::<IrqChipAdapter<U>>(irq_chip) };
}

// Initialise gc irq state.
{
let girq = &mut this.reg.gc.get_mut().irq;
girq.chip = this.irq_chip.get();
// SAFETY: By leaving `parent_handler_data` set to `null`, the gpio subsystem
// initialises it to a pointer to the gpio chip, which is what `FlowHandler<T>`
// expects.
girq.parent_handler = unsafe { irq::new_flow_handler::<FlowHandler<T>>() };
girq.num_parents = 1;
girq.parents = &mut this.parent_irq;
this.parent_irq = parent_irq;
girq.default_type = bindings::IRQ_TYPE_NONE;
girq.handler = Some(bindings::handle_bad_irq);
}

// SAFETY: `reg` is pinned when `self` is.
let pinned = unsafe { self.map_unchecked_mut(|r| &mut r.reg) };
pinned.register(gpio_count, base, parent, data)
}
}

impl<T: ChipWithIrqChip> Default for RegistrationWithIrqChip<T> {
fn default() -> Self {
Self::new()
}
}

// SAFETY: `RegistrationWithIrqChip` doesn't offer any methods or access to fields when shared
// between threads or CPUs, so it is safe to share it.
unsafe impl<T: ChipWithIrqChip> Sync for RegistrationWithIrqChip<T> {}

// SAFETY: Registration with and unregistration from the gpio subsystem (including irq chips for
// them) can happen from any thread. Additionally, `T::Data` (which is dropped during
// unregistration) is `Send`, so it is ok to move `Registration` to different threads.
Copy link
Member

Choose a reason for hiding this comment

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

(Off-topic: I wonder if it would be useful in cases like this to assert safety conditions like T::Data is Send nearby where we rely on them too, specially in cases where it may be too far away etc.)

Copy link
Author

Choose a reason for hiding this comment

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

I like the idea of having the machine check some of our stated assumptions. I added a bound for this particular, though it is just repeating one that already exists.

It will help catch problems if in the future someone inadvertently removes the bounds from T::Data. On the other hand, we were asking the Rust folks to help us avoid repeating such bounds :)

Copy link
Member

Choose a reason for hiding this comment

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

It will help catch problems if in the future someone inadvertently removes the bounds from T::Data

Exactly -- I was thinking about the "SAFETY 'proofs' in both ends" discussion and while for "comments" it would likely be too annoying, for something that can be machine checked (& that can be asserted on this side), it seems worth to do.

unsafe impl<T: ChipWithIrqChip> Send for RegistrationWithIrqChip<T> where T::Data: Send {}

struct FlowHandler<T: ChipWithIrqChip>(PhantomData<T>);

impl<T: ChipWithIrqChip> irq::FlowHandler for FlowHandler<T> {
type Data = *mut bindings::gpio_chip;

fn handle_irq_flow(gc: *mut bindings::gpio_chip, desc: &irq::Descriptor) {
// SAFETY: `FlowHandler` is only used in gpio chips, and it is removed when the gpio is
// unregistered, so we know that `gc` must still be valid. We also know that the value
// stored as gpio data was returned by `T::Data::into_pointer` again because
// `FlowHandler` is a private structure only used in this way.
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc)) };

// SAFETY: `gc` is valid (see comment above), so we can dereference it.
let domain = unsafe { irq::Domain::from_ptr((*gc).irq.domain) };

T::handle_irq_flow(data, desc, &domain);
}
}

/// Adapter from an irq chip with `gpio_chip` pointer as context to one where the gpio chip
/// data is passed as context.
struct IrqChipAdapter<T: irq::Chip>(PhantomData<T>);

impl<T: irq::Chip> irq::Chip for IrqChipAdapter<T> {
type Data = *mut bindings::gpio_chip;
const TO_USE: irq::ToUse = T::TO_USE;

fn ack(gc: *mut bindings::gpio_chip, irq_data: &irq::IrqData) {
// SAFETY: `IrqChipAdapter` is a private struct, only used when the data stored in the
// gpio chip is known to come from `T::Data`, and only valid while the gpio chip is
// registered, so `gc` is valid.
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc as _)) };
T::ack(data, irq_data);
}

fn mask(gc: *mut bindings::gpio_chip, irq_data: &irq::IrqData) {
// SAFETY: `IrqChipAdapter` is a private struct, only used when the data stored in the
// gpio chip is known to come from `T::Data`, and only valid while the gpio chip is
// registered, so `gc` is valid.
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc as _)) };
T::mask(data, irq_data);
}

fn unmask(gc: *mut bindings::gpio_chip, irq_data: &irq::IrqData) {
// SAFETY: `IrqChipAdapter` is a private struct, only used when the data stored in the
// gpio chip is known to come from `T::Data`, and only valid while the gpio chip is
// registered, so `gc` is valid.
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc as _)) };
T::unmask(data, irq_data);
}

fn set_type(
gc: *mut bindings::gpio_chip,
irq_data: &mut irq::LockedIrqData,
flow_type: u32,
) -> Result<irq::ExtraResult> {
// SAFETY: `IrqChipAdapter` is a private struct, only used when the data stored in the
// gpio chip is known to come from `T::Data`, and only valid while the gpio chip is
// registered, so `gc` is valid.
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc as _)) };
T::set_type(data, irq_data, flow_type)
}

fn set_wake(gc: *mut bindings::gpio_chip, irq_data: &irq::IrqData, on: bool) -> Result {
// SAFETY: `IrqChipAdapter` is a private struct, only used when the data stored in the
// gpio chip is known to come from `T::Data`, and only valid while the gpio chip is
// registered, so `gc` is valid.
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc as _)) };
T::set_wake(data, irq_data, on)
}
}
}
116 changes: 116 additions & 0 deletions rust/kernel/irq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,119 @@ impl Type {
/// The interrupt is triggered while the signal is held low.
pub const LEVEL_LOW: u32 = bindings::IRQ_TYPE_LEVEL_LOW;
}

/// Wraps the kernel's `struct irq_desc`.
///
/// # Invariants
///
/// The pointer `Descriptor::ptr` is non-null and valid.
pub struct Descriptor {
pub(crate) ptr: *mut bindings::irq_desc,
}

impl Descriptor {
/// Constructs a new `struct irq_desc` wrapper.
///
/// # Safety
///
/// The pointer `ptr` must be non-null and valid for the lifetime of the returned object.
unsafe fn from_ptr(ptr: *mut bindings::irq_desc) -> Self {
// INVARIANT: The safety requirements ensure the invariant.
Self { ptr }
}

/// Calls `chained_irq_enter` and returns a guard that calls `chained_irq_exit` once dropped.
///
/// It is meant to be used by chained irq handlers to dispatch irqs to the next handlers.
pub fn enter_chained(&self) -> ChainedGuard<'_> {
// SAFETY: By the type invariants, `ptr` is always non-null and valid.
let irq_chip = unsafe { bindings::irq_desc_get_chip(self.ptr) };

// SAFETY: By the type invariants, `ptr` is always non-null and valid. `irq_chip` was just
// returned from `ptr`, so it is still valid too.
unsafe { bindings::chained_irq_enter(irq_chip, self.ptr) };
ChainedGuard {
desc: self,
irq_chip,
}
}
}

/// A guard to call `chained_irq_exit` after `chained_irq_enter` was called.
///
/// It is also used as evidence that a previous `chained_irq_enter` was called. So there are no
/// public constructors and it is only created after indeed calling `chained_irq_enter`.
pub struct ChainedGuard<'a> {
desc: &'a Descriptor,
irq_chip: *mut bindings::irq_chip,
}

impl Drop for ChainedGuard<'_> {
fn drop(&mut self) {
// SAFETY: The lifetime of `ChainedGuard` guarantees that `self.desc` remains valid, so it
// also guarantess `irq_chip` (which was returned from it) and `self.desc.ptr` (guaranteed
// by the type invariants).
unsafe { bindings::chained_irq_exit(self.irq_chip, self.desc.ptr) };
}
}

/// Wraps the kernel's `struct irq_domain`.
///
/// # Invariants
///
/// The pointer `Domain::ptr` is non-null and valid.
pub struct Domain {
ptr: *mut bindings::irq_domain,
}

impl Domain {
/// Constructs a new `struct irq_domain` wrapper.
///
/// # Safety
///
/// The pointer `ptr` must be non-null and valid for the lifetime of the returned object.
pub(crate) unsafe fn from_ptr(ptr: *mut bindings::irq_domain) -> Self {
// INVARIANT: The safety requirements ensure the invariant.
Self { ptr }
}

/// Invokes the chained handler of the given hw irq of the given domain.
///
/// It requires evidence that `chained_irq_enter` was called, which is done by passing a
/// `ChainedGuard` instance.
pub fn generic_handle_chained(&self, hwirq: u32, _guard: &ChainedGuard<'_>) {
// SAFETY: `ptr` is valid by the type invariants.
unsafe { bindings::generic_handle_domain_irq(self.ptr, hwirq) };
}
}

/// A high-level irq flow handler.
pub trait FlowHandler {
/// The data associated with the handler.
type Data: PointerWrapper;

/// Implements the irq flow for the given descriptor.
fn handle_irq_flow(data: <Self::Data as PointerWrapper>::Borrowed<'_>, desc: &Descriptor);
}

/// Returns the raw irq flow handler corresponding to the (high-level) one defined in `T`.
///
/// # Safety
///
/// The caller must ensure that the value stored in the irq handler data (as returned by
/// `irq_desc_get_handler_data`) is the result of calling [`PointerWrapper::into_pointer] for the
/// [`T::Data`] type.
pub(crate) unsafe fn new_flow_handler<T: FlowHandler>() -> bindings::irq_flow_handler_t {
Some(irq_flow_handler::<T>)
}

unsafe extern "C" fn irq_flow_handler<T: FlowHandler>(desc: *mut bindings::irq_desc) {
// SAFETY: By the safety requirements of `new_flow_handler`, we know that the value returned by
// `irq_desc_get_handler_data` comes from calling `T::Data::into_pointer`. `desc` is valid by
// the C API contract.
let data = unsafe { T::Data::borrow(bindings::irq_desc_get_handler_data(desc)) };

// SAFETY: The C API guarantees that `desc` is valid for the duration of this call, which
// outlives the lifetime returned by `from_desc`.
T::handle_irq_flow(data, &unsafe { Descriptor::from_ptr(desc) });
}