| 1 | // Copyright 2024 Red Hat, Inc. |
| 2 | // Author(s): Paolo Bonzini <pbonzini@redhat.com> |
| 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | |
| 5 | //! Bindings for interrupt sources |
| 6 | |
| 7 | use std::{ |
| 8 | ffi::{c_int, CStr}, |
| 9 | marker::PhantomData, |
| 10 | ptr, |
| 11 | }; |
| 12 | |
| 13 | use bql::BqlCell; |
| 14 | use common::Opaque; |
| 15 | use qom::{prelude::*, ObjectClass}; |
| 16 | |
| 17 | use crate::bindings::{self, qemu_set_irq}; |
| 18 | |
| 19 | /// An opaque wrapper around [`bindings::IRQState`]. |
| 20 | #[repr(transparent)] |
| 21 | #[derive(Debug, common::Wrapper)] |
| 22 | pub struct IRQState(Opaque<bindings::IRQState>); |
| 23 | |
| 24 | /// Interrupt sources are used by devices to pass changes to a value (typically |
| 25 | /// a boolean). The interrupt sink is usually an interrupt controller or |
| 26 | /// GPIO controller. |
| 27 | /// |
| 28 | /// As far as devices are concerned, interrupt sources are always active-high: |
| 29 | /// for example, `InterruptSource<bool>`'s [`raise`](InterruptSource::raise) |
| 30 | /// method sends a `true` value to the sink. If the guest has to see a |
| 31 | /// different polarity, that change is performed by the board between the |
| 32 | /// device and the interrupt controller. |
| 33 | /// |
| 34 | /// Interrupts are implemented as a pointer to the interrupt "sink", which has |
| 35 | /// type [`IRQState`]. A device exposes its source as a QOM link property using |
| 36 | /// a function such as [`crate::DeviceMethods::init_gpio_out`], and |
| 37 | /// initially leaves the pointer to a NULL value, representing an unconnected |
| 38 | /// interrupt. To connect it, whoever creates the device fills the pointer with |
| 39 | /// the sink's `IRQState *`, for example using `qdev_connect_gpio_out`. Because |
| 40 | /// devices are generally shared objects, interrupt sources are an example of |
| 41 | /// the interior mutability pattern. |
| 42 | /// |
| 43 | /// Interrupt sources can only be triggered under the Big QEMU Lock; `BqlCell` |
| 44 | /// allows access from whatever thread has it. |
| 45 | #[derive(Debug)] |
| 46 | #[repr(transparent)] |
| 47 | pub struct InterruptSource<T = bool> |
| 48 | where |
| 49 | c_int: From<T>, |
| 50 | { |
| 51 | cell: BqlCell<*mut bindings::IRQState>, |
| 52 | _marker: PhantomData<T>, |
| 53 | } |
| 54 | |
| 55 | // SAFETY: the implementation asserts via `BqlCell` that the BQL is taken |
| 56 | unsafe impl<T> Sync for InterruptSource<T> where c_int: From<T> {} |
| 57 | |
| 58 | impl InterruptSource<bool> { |
| 59 | /// Send a low (`false`) value to the interrupt sink. |
| 60 | pub fn lower(&self) { |
| 61 | self.set(false); |
| 62 | } |
| 63 | |
| 64 | /// Send a high-low pulse to the interrupt sink. |
| 65 | pub fn pulse(&self) { |
| 66 | self.set(true); |
| 67 | self.set(false); |
| 68 | } |
| 69 | |
| 70 | /// Send a high (`true`) value to the interrupt sink. |
| 71 | pub fn raise(&self) { |
| 72 | self.set(true); |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | impl<T> InterruptSource<T> |
| 77 | where |
| 78 | c_int: From<T>, |
| 79 | { |
| 80 | /// Send `level` to the interrupt sink. |
| 81 | pub fn set(&self, level: T) { |
| 82 | let ptr = self.cell.get(); |
| 83 | // SAFETY: the pointer is retrieved under the BQL and remains valid |
| 84 | // until the BQL is released, which is after qemu_set_irq() is entered. |
| 85 | unsafe { |
| 86 | qemu_set_irq(ptr, level.into()); |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | pub const fn as_ptr(&self) -> *mut *mut bindings::IRQState { |
| 91 | self.cell.as_ptr() |
| 92 | } |
| 93 | |
| 94 | pub const fn slice_as_ptr(slice: &[Self]) -> *mut *mut bindings::IRQState { |
| 95 | assert!(!slice.is_empty()); |
| 96 | slice[0].as_ptr() |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | impl Default for InterruptSource { |
| 101 | fn default() -> Self { |
| 102 | InterruptSource { |
| 103 | cell: BqlCell::new(ptr::null_mut()), |
| 104 | _marker: PhantomData, |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | unsafe impl ObjectType for IRQState { |
| 110 | type Class = ObjectClass; |
| 111 | const TYPE_NAME: &'static CStr = |
| 112 | unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_IRQ) }; |
| 113 | } |
| 114 | |
| 115 | qom_isa!(IRQState: Object); |