squib_virtio/
interrupt.rs1use std::sync::{
19 Arc,
20 atomic::{AtomicU32, Ordering},
21};
22
23use squib_arch::IntId;
24use squib_gic::{Gic, GicError};
25
26pub const INT_VRING: u32 = 0x01;
29pub const INT_CONFIG: u32 = 0x02;
31
32#[derive(Clone)]
37pub struct IrqLine {
38 gic: Arc<dyn Gic + Send + Sync>,
39 intid: IntId,
40 status: Arc<AtomicU32>,
41}
42
43impl std::fmt::Debug for IrqLine {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 f.debug_struct("IrqLine")
48 .field("intid", &self.intid.as_raw())
49 .field("status", &self.status.load(Ordering::SeqCst))
50 .finish_non_exhaustive()
51 }
52}
53
54impl IrqLine {
55 #[must_use]
57 pub fn new(gic: Arc<dyn Gic + Send + Sync>, intid: IntId) -> Self {
58 Self {
59 gic,
60 intid,
61 status: Arc::new(AtomicU32::new(0)),
62 }
63 }
64
65 #[must_use]
69 pub fn status(&self) -> Arc<AtomicU32> {
70 Arc::clone(&self.status)
71 }
72
73 pub fn trigger_queue(&self) -> Result<(), GicError> {
78 self.status.fetch_or(INT_VRING, Ordering::SeqCst);
79 self.gic.pulse_spi(self.intid)
80 }
81
82 pub fn trigger_config(&self) -> Result<(), GicError> {
87 self.status.fetch_or(INT_CONFIG, Ordering::SeqCst);
88 self.gic.pulse_spi(self.intid)
89 }
90
91 pub fn ack(&self, mask: u32) {
93 self.status.fetch_and(!mask, Ordering::SeqCst);
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use parking_lot::Mutex;
100
101 use super::*;
102
103 #[derive(Debug, Default)]
104 struct StubGic {
105 pulses: Mutex<Vec<u32>>,
106 }
107
108 impl Gic for StubGic {
109 fn pulse_spi(&self, intid: IntId) -> Result<(), GicError> {
110 self.pulses.lock().push(intid.as_raw());
111 Ok(())
112 }
113 fn set_spi_level(&self, _intid: IntId, _level: bool) -> Result<(), GicError> {
114 Ok(())
115 }
116 fn save_state(&self) -> Result<Vec<u8>, GicError> {
117 Ok(Vec::new())
118 }
119 fn restore_state(&self, _data: &[u8]) -> Result<(), GicError> {
120 Ok(())
121 }
122 }
123
124 fn line() -> (IrqLine, Arc<StubGic>) {
125 let gic = Arc::new(StubGic::default());
126 let line = IrqLine::new(gic.clone(), IntId::from_spi_cell(0).expect("intid 0"));
127 (line, gic)
128 }
129
130 #[test]
131 fn test_should_set_vring_bit_and_pulse_on_trigger_queue() {
132 let (line, gic) = line();
133 line.trigger_queue().unwrap();
134 assert_eq!(line.status().load(Ordering::SeqCst), INT_VRING);
135 assert_eq!(gic.pulses.lock().len(), 1);
136 }
137
138 #[test]
139 fn test_should_set_config_bit_and_pulse_on_trigger_config() {
140 let (line, _gic) = line();
141 line.trigger_config().unwrap();
142 assert_eq!(line.status().load(Ordering::SeqCst), INT_CONFIG);
143 }
144
145 #[test]
146 fn test_should_clear_only_acked_bits() {
147 let (line, _) = line();
148 line.trigger_queue().unwrap();
149 line.trigger_config().unwrap();
150 assert_eq!(line.status().load(Ordering::SeqCst), INT_VRING | INT_CONFIG);
151 line.ack(INT_VRING);
152 assert_eq!(line.status().load(Ordering::SeqCst), INT_CONFIG);
153 }
154}