dbs_interrupt/
manager.rs

1// Copyright (C) 2019-2020 Alibaba Cloud. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Interrupt manager to manage and switch device interrupt modes.
5//!
6//! A device may support multiple interrupt modes. For example, a PCI device may support legacy, PCI
7//! MSI and PCI MSIx interrupts. This interrupt manager helps a device backend driver to manage its
8//! interrupts and provides interfaces to switch interrupt working modes.
9use std::io::{Error, Result};
10use std::sync::atomic::{AtomicU32, Ordering};
11use std::sync::Arc;
12
13use dbs_device::resources::DeviceResources;
14
15#[cfg(feature = "legacy-irq")]
16use super::LegacyIrqSourceConfig;
17#[cfg(feature = "msi-irq")]
18use super::MsiIrqSourceConfig;
19use super::{InterruptManager, InterruptSourceConfig, InterruptSourceGroup, InterruptSourceType};
20
21/// Defines the offset when device_id is recorded to msi.
22///
23/// For the origin of this value, please refer to the comment of set_msi_device_id function.
24pub const MSI_DEVICE_ID_SHIFT: u8 = 3;
25
26#[cfg(feature = "legacy-irq")]
27const LEGACY_CONFIGS: [InterruptSourceConfig; 1] =
28    [InterruptSourceConfig::LegacyIrq(LegacyIrqSourceConfig {})];
29
30#[cfg(feature = "msi-irq")]
31const MSI_INT_MASK_BIT: u8 = 0;
32#[cfg(feature = "msi-irq")]
33const MSI_INT_MASK: u32 = (1 << MSI_INT_MASK_BIT) as u32;
34
35/// Device interrupt working modes.
36#[derive(Copy, Clone, Debug, Eq, PartialEq)]
37pub enum DeviceInterruptMode {
38    /// The device interrupt manager has been disabled.
39    Disabled = 0,
40    /// The device interrupt manager works in legacy irq mode.
41    LegacyIrq = 1,
42    /// The device interrupt manager works in generic MSI mode.
43    GenericMsiIrq = 2,
44    /// The device interrupt manager works in PCI MSI mode.
45    PciMsiIrq = 3,
46    /// The device interrupt manager works in PCI MSI-x mode.
47    PciMsixIrq = 4,
48}
49
50/// A struct to manage interrupts and interrupt modes for a device.
51///
52/// The interrupt manager may support multiple working mode. For example, an interrupt manager for a
53/// PCI device may work in legacy mode, PCI MSI mode or PCI MSIx mode. Under certain conditions, the
54/// interrupt manager may switch between interrupt working modes. To simplify implementation,
55/// switching working mode is only supported at configuration stage and will be disabled at runtime
56/// stage. The DeviceInterruptManager::enable() switches the interrupt manager from configuration
57/// stage into runtime stage. And DeviceInterruptManager::reset() switches from runtime stage back
58/// to initial configuration stage.
59pub struct DeviceInterruptManager<T: InterruptManager> {
60    mode: DeviceInterruptMode,
61    activated: bool,
62    current_idx: usize,
63    mode2idx: [usize; 5],
64    intr_mgr: T,
65    intr_groups: Vec<Arc<Box<dyn InterruptSourceGroup>>>,
66    #[cfg(feature = "msi-irq")]
67    msi_config: Vec<InterruptSourceConfig>,
68    /// Device id indicate the device who triggers msi irq.
69    device_id: Option<u32>,
70}
71
72impl<T: InterruptManager> DeviceInterruptManager<T> {
73    /// Create an interrupt manager for a device.
74    ///
75    /// # Arguments
76    /// * `intr_mgr`: underline interrupt manager to allocate/free interrupt groups.
77    /// * `resources`: resources assigned to the device, including assigned interrupt resources.
78    pub fn new(intr_mgr: T, resources: &DeviceResources) -> Result<Self> {
79        let mut mgr = DeviceInterruptManager {
80            mode: DeviceInterruptMode::Disabled,
81            activated: false,
82            current_idx: usize::MAX,
83            mode2idx: [usize::MAX; 5],
84            intr_mgr,
85            intr_groups: Vec::new(),
86            #[cfg(feature = "msi-irq")]
87            msi_config: Vec::new(),
88            device_id: None,
89        };
90
91        #[cfg(feature = "legacy-irq")]
92        {
93            if let Some(irq) = resources.get_legacy_irq() {
94                let group = mgr
95                    .intr_mgr
96                    .create_group(InterruptSourceType::LegacyIrq, irq, 1)?;
97                mgr.mode2idx[DeviceInterruptMode::LegacyIrq as usize] = mgr.intr_groups.len();
98                mgr.intr_groups.push(group);
99            }
100        }
101
102        #[cfg(feature = "msi-irq")]
103        {
104            if let Some(msi) = resources.get_generic_msi_irqs() {
105                let group = mgr
106                    .intr_mgr
107                    .create_group(InterruptSourceType::MsiIrq, msi.0, msi.1)?;
108                mgr.resize_msi_config_space(group.len());
109                mgr.mode2idx[DeviceInterruptMode::GenericMsiIrq as usize] = mgr.intr_groups.len();
110                mgr.intr_groups.push(group);
111            }
112
113            if let Some(msi) = resources.get_pci_msi_irqs() {
114                let group = mgr
115                    .intr_mgr
116                    .create_group(InterruptSourceType::MsiIrq, msi.0, msi.1)?;
117                mgr.resize_msi_config_space(group.len());
118                mgr.mode2idx[DeviceInterruptMode::PciMsiIrq as usize] = mgr.intr_groups.len();
119                mgr.intr_groups.push(group);
120            }
121
122            if let Some(msi) = resources.get_pci_msix_irqs() {
123                let group = mgr
124                    .intr_mgr
125                    .create_group(InterruptSourceType::MsiIrq, msi.0, msi.1)?;
126                mgr.resize_msi_config_space(group.len());
127                mgr.mode2idx[DeviceInterruptMode::PciMsixIrq as usize] = mgr.intr_groups.len();
128                mgr.intr_groups.push(group);
129            }
130        }
131
132        Ok(mgr)
133    }
134
135    /// Set device_id for MSI routing
136    pub fn set_device_id(&mut self, device_id: Option<u32>) {
137        self.device_id = device_id;
138    }
139
140    /// Check whether the interrupt manager has been activated.
141    pub fn is_enabled(&self) -> bool {
142        self.activated
143    }
144
145    /// Switch the interrupt manager from configuration stage into runtime stage.
146    ///
147    /// The working mode could only be changed at configuration stage, and all requests to change
148    /// working mode at runtime stage will be rejected.
149    ///
150    /// If the interrupt manager is still in DISABLED mode when DeviceInterruptManager::enable() is
151    /// called, it will be put into LEGACY mode if LEGACY mode is supported.
152    pub fn enable(&mut self) -> Result<()> {
153        if self.activated {
154            return Ok(());
155        }
156
157        // Enter Legacy mode by default if Legacy mode is supported.
158        if self.mode == DeviceInterruptMode::Disabled
159            && self.mode2idx[DeviceInterruptMode::LegacyIrq as usize] != usize::MAX
160        {
161            self.set_working_mode(DeviceInterruptMode::LegacyIrq)?;
162        }
163        if self.mode == DeviceInterruptMode::Disabled {
164            return Err(Error::from_raw_os_error(libc::EINVAL));
165        }
166
167        self.intr_groups[self.current_idx].enable(self.get_configs(self.mode))?;
168        self.activated = true;
169
170        Ok(())
171    }
172
173    /// Switch the interrupt manager from runtime stage back into initial configuration stage.
174    ///
175    /// Currently we doesn't track the usage of interrupt group object given out by `get_group()`,
176    /// so the the caller needs to take the responsibility to release all interrupt group object
177    /// reference before calling DeviceInterruptManager::reset().
178    pub fn reset(&mut self) -> Result<()> {
179        if self.activated {
180            self.activated = false;
181            self.intr_groups[self.current_idx].disable()?;
182        }
183        self.set_working_mode(DeviceInterruptMode::Disabled)?;
184
185        Ok(())
186    }
187
188    /// Get the current interrupt working mode.
189    pub fn get_working_mode(&mut self) -> DeviceInterruptMode {
190        self.mode
191    }
192
193    /// Switch interrupt working mode.
194    ///
195    /// Currently switching working mode is only supported during device configuration stage and
196    /// will always return failure if called during device runtime stage. The device switches from
197    /// configuration stage to runtime stage by invoking `DeviceInterruptManager::enable()`. With
198    /// this constraint, the device drivers may call `DeviceInterruptManager::get_group()` to get
199    /// the underline active interrupt group object, and directly calls the interrupt group object's
200    /// methods to trigger/acknowledge interrupts.
201    ///
202    /// This is a key design decision for optimizing performance. Though the DeviceInterruptManager
203    /// object itself is not multi-thread safe and must be protected from concurrent access by the
204    /// caller, the interrupt source group object is multi-thread safe and could be called
205    /// concurrently to trigger/acknowledge interrupts. This design may help to improve performance
206    /// for MSI interrupts.
207    ///
208    /// # Arguments
209    /// * `mode`: target working mode.
210    pub fn set_working_mode(&mut self, mode: DeviceInterruptMode) -> Result<()> {
211        // Can't switch mode agian once enabled.
212        if self.activated {
213            return Err(Error::from_raw_os_error(libc::EINVAL));
214        }
215
216        if mode != self.mode {
217            // Supported state transitions:
218            // - other state -> DISABLED
219            // - DISABLED -> other
220            // - non-legacy -> legacy
221            // - legacy -> non-legacy
222            if self.mode != DeviceInterruptMode::Disabled
223                && self.mode != DeviceInterruptMode::LegacyIrq
224                && mode != DeviceInterruptMode::LegacyIrq
225                && mode != DeviceInterruptMode::Disabled
226            {
227                return Err(Error::from_raw_os_error(libc::EINVAL));
228            }
229
230            // Then enter new state
231            if mode != DeviceInterruptMode::Disabled {
232                self.current_idx = self.mode2idx[mode as usize];
233            } else {
234                // We should reset irq configs when disable interrupt
235                self.reset_configs(mode);
236            }
237            self.mode = mode;
238        }
239
240        Ok(())
241    }
242
243    /// Get the underline interrupt source group object, so the device driver could concurrently
244    /// trigger/acknowledge interrupts by using the returned group object.
245    pub fn get_group(&self) -> Option<Arc<Box<dyn InterruptSourceGroup>>> {
246        if !self.activated || self.mode == DeviceInterruptMode::Disabled {
247            None
248        } else {
249            Some(self.intr_groups[self.current_idx].clone())
250        }
251    }
252
253    /// Get the underline interrupt source group object, ignore the mode
254    pub fn get_group_unchecked(&self) -> Arc<Box<dyn InterruptSourceGroup>> {
255        self.intr_groups[self.current_idx].clone()
256    }
257
258    /// Reconfigure a specific interrupt in current working mode at configuration or runtime stage.
259    ///
260    /// It's mainly used to reconfigure Generic MSI/PCI MSI/PCI MSIx interrupts. Actually legacy
261    /// interrupts don't support reconfiguration yet.
262    #[allow(unused_variables)]
263    pub fn update(&mut self, index: u32) -> Result<()> {
264        if !self.activated {
265            return Err(Error::from_raw_os_error(libc::EINVAL));
266        }
267
268        match self.mode {
269            #[cfg(feature = "msi-irq")]
270            DeviceInterruptMode::GenericMsiIrq
271            | DeviceInterruptMode::PciMsiIrq
272            | DeviceInterruptMode::PciMsixIrq => {
273                let group = &self.intr_groups[self.current_idx];
274                if index >= group.len() || index >= self.msi_config.len() as u32 {
275                    return Err(Error::from_raw_os_error(libc::EINVAL));
276                }
277                group.update(index, &self.msi_config[index as usize])?;
278                Ok(())
279            }
280            _ => Err(Error::from_raw_os_error(libc::EINVAL)),
281        }
282    }
283
284    fn get_configs(&self, mode: DeviceInterruptMode) -> &[InterruptSourceConfig] {
285        match mode {
286            #[cfg(feature = "legacy-irq")]
287            DeviceInterruptMode::LegacyIrq => &LEGACY_CONFIGS[..],
288            #[cfg(feature = "msi-irq")]
289            DeviceInterruptMode::GenericMsiIrq
290            | DeviceInterruptMode::PciMsiIrq
291            | DeviceInterruptMode::PciMsixIrq => {
292                let idx = self.mode2idx[mode as usize];
293                let group_len = self.intr_groups[idx].len() as usize;
294                &self.msi_config[0..group_len]
295            }
296            _ => panic!("unhandled interrupt type in get_configs()"),
297        }
298    }
299
300    fn reset_configs(&mut self, mode: DeviceInterruptMode) {
301        match mode {
302            #[cfg(feature = "msi-irq")]
303            DeviceInterruptMode::GenericMsiIrq
304            | DeviceInterruptMode::PciMsiIrq
305            | DeviceInterruptMode::PciMsixIrq => {
306                self.msi_config = vec![
307                    InterruptSourceConfig::MsiIrq(MsiIrqSourceConfig::default());
308                    self.msi_config.len()
309                ];
310            }
311            _ => {}
312        }
313    }
314}
315
316#[cfg(feature = "msi-irq")]
317impl<T: InterruptManager> DeviceInterruptManager<T> {
318    /// Set the high address for a MSI message.
319    #[allow(irrefutable_let_patterns)]
320    pub fn set_msi_high_address(&mut self, index: u32, data: u32) -> Result<()> {
321        if (index as usize) < self.msi_config.len() {
322            if let InterruptSourceConfig::MsiIrq(ref mut msi) = self.msi_config[index as usize] {
323                msi.high_addr = data;
324                return Ok(());
325            }
326        }
327        Err(Error::from_raw_os_error(libc::EINVAL))
328    }
329
330    /// Set the low address for a MSI message.
331    #[allow(irrefutable_let_patterns)]
332    pub fn set_msi_low_address(&mut self, index: u32, data: u32) -> Result<()> {
333        if (index as usize) < self.msi_config.len() {
334            if let InterruptSourceConfig::MsiIrq(ref mut msi) = self.msi_config[index as usize] {
335                msi.low_addr = data;
336                return Ok(());
337            }
338        }
339        Err(Error::from_raw_os_error(libc::EINVAL))
340    }
341
342    /// Set the data for a MSI message.
343    #[allow(irrefutable_let_patterns)]
344    pub fn set_msi_data(&mut self, index: u32, data: u32) -> Result<()> {
345        if (index as usize) < self.msi_config.len() {
346            if let InterruptSourceConfig::MsiIrq(ref mut msi) = self.msi_config[index as usize] {
347                msi.data = data;
348                return Ok(());
349            }
350        }
351        Err(Error::from_raw_os_error(libc::EINVAL))
352    }
353
354    /// Set msi irq MASK bit
355    #[allow(irrefutable_let_patterns)]
356    pub fn set_msi_mask(&mut self, index: u32, mask: bool) -> Result<()> {
357        if (index as usize) < self.msi_config.len() {
358            if let InterruptSourceConfig::MsiIrq(ref mut msi) = self.msi_config[index as usize] {
359                let mut msg_ctl = msi.msg_ctl;
360                msg_ctl &= !MSI_INT_MASK;
361                if mask {
362                    msg_ctl |= MSI_INT_MASK;
363                }
364                msi.msg_ctl = msg_ctl;
365                return Ok(());
366            }
367        }
368        Err(Error::from_raw_os_error(libc::EINVAL))
369    }
370
371    /// Get msi irq MASK state
372    #[allow(irrefutable_let_patterns)]
373    pub fn get_msi_mask(&self, index: u32) -> Result<bool> {
374        if (index as usize) < self.msi_config.len() {
375            if let InterruptSourceConfig::MsiIrq(ref msi) = self.msi_config[index as usize] {
376                return Ok((msi.msg_ctl & MSI_INT_MASK) == MSI_INT_MASK);
377            }
378        }
379        Err(Error::from_raw_os_error(libc::EINVAL))
380    }
381
382    #[cfg(target_arch = "aarch64")]
383    /// Set the device id for a MSI irq
384    pub fn set_msi_device_id(&mut self, index: u32) -> Result<()> {
385        if (index as usize) < self.msi_config.len() {
386            if let InterruptSourceConfig::MsiIrq(ref mut msi) = self.msi_config[index as usize] {
387                msi.device_id = self.device_id.map(|dev_id| {
388                    // An pci device attach to ITS will have a new device id which is use for msi
389                    // irq routing.  It is calculated according to kernel function PCI_DEVID(),
390                    // new_dev_id = (bus << 8) | devfn. In addition, devfn = device_id << 3,
391                    // according to pci-host-ecam-generic's spec, and we implement bus = 0.
392                    dev_id << MSI_DEVICE_ID_SHIFT
393                });
394                return Ok(());
395            }
396        }
397        Err(Error::from_raw_os_error(libc::EINVAL))
398    }
399
400    fn resize_msi_config_space(&mut self, size: u32) {
401        if self.msi_config.len() < size as usize {
402            self.msi_config =
403                vec![InterruptSourceConfig::MsiIrq(MsiIrqSourceConfig::default()); size as usize];
404        }
405    }
406}
407
408/// Struct to implement a 32-bit interrupt status register.
409#[derive(Default, Debug)]
410pub struct InterruptStatusRegister32 {
411    status: AtomicU32,
412}
413
414impl InterruptStatusRegister32 {
415    /// Create a status register instance.
416    pub fn new() -> Self {
417        InterruptStatusRegister32 {
418            status: AtomicU32::new(0),
419        }
420    }
421
422    /// Read current value of the status register.
423    pub fn read(&self) -> u32 {
424        self.status.load(Ordering::SeqCst)
425    }
426
427    /// Write value to the status register.
428    pub fn write(&self, value: u32) {
429        self.status.store(value, Ordering::SeqCst);
430    }
431
432    /// Read current value and reset the status register to 0.
433    pub fn read_and_clear(&self) -> u32 {
434        self.status.swap(0, Ordering::SeqCst)
435    }
436
437    /// Set bits into `value`.
438    pub fn set_bits(&self, value: u32) {
439        self.status.fetch_or(value, Ordering::SeqCst);
440    }
441
442    /// Clear bits present in `value`.
443    pub fn clear_bits(&self, value: u32) {
444        self.status.fetch_and(!value, Ordering::SeqCst);
445    }
446}
447
448#[cfg(all(test, feature = "kvm-legacy-irq", feature = "kvm-msi-irq"))]
449pub(crate) mod tests {
450    use std::sync::Arc;
451
452    use dbs_device::resources::{DeviceResources, MsiIrqType, Resource};
453    use kvm_ioctls::{Kvm, VmFd};
454
455    use super::*;
456    use crate::KvmIrqManager;
457
458    pub(crate) fn create_vm_fd() -> VmFd {
459        let kvm = Kvm::new().unwrap();
460        kvm.create_vm().unwrap()
461    }
462
463    fn create_init_resources() -> DeviceResources {
464        let mut resources = DeviceResources::new();
465
466        resources.append(Resource::MmioAddressRange {
467            base: 0xd000_0000,
468            size: 0x10_0000,
469        });
470        resources.append(Resource::LegacyIrq(0));
471        resources.append(Resource::MsiIrq {
472            ty: MsiIrqType::GenericMsi,
473            base: 0x200,
474            size: 0x10,
475        });
476        resources.append(Resource::MsiIrq {
477            ty: MsiIrqType::PciMsi,
478            base: 0x100,
479            size: 0x20,
480        });
481        resources.append(Resource::MsiIrq {
482            ty: MsiIrqType::PciMsix,
483            base: 0x300,
484            size: 0x30,
485        });
486
487        resources
488    }
489
490    fn create_interrupt_manager() -> DeviceInterruptManager<Arc<KvmIrqManager>> {
491        let vmfd = Arc::new(create_vm_fd());
492        #[cfg(target_arch = "x86_64")]
493        vmfd.create_irq_chip().unwrap();
494        #[cfg(target_arch = "aarch64")]
495        let _ = dbs_arch::gic::create_gic(&vmfd, 1);
496        let intr_mgr = Arc::new(KvmIrqManager::new(vmfd));
497
498        let resource = create_init_resources();
499        intr_mgr.initialize().unwrap();
500        DeviceInterruptManager::new(intr_mgr, &resource).unwrap()
501    }
502
503    #[test]
504    fn test_create_device_interrupt_manager() {
505        let mut mgr = create_interrupt_manager();
506
507        assert_eq!(mgr.mode, DeviceInterruptMode::Disabled);
508        assert!(!mgr.activated);
509        assert_eq!(mgr.current_idx, usize::MAX);
510        assert_eq!(mgr.intr_groups.len(), 4);
511        assert!(!mgr.is_enabled());
512        assert!(mgr.get_group().is_none());
513
514        // Enter legacy mode by default
515        mgr.enable().unwrap();
516        assert!(mgr.is_enabled());
517        assert_eq!(
518            mgr.mode2idx[DeviceInterruptMode::LegacyIrq as usize],
519            mgr.current_idx
520        );
521        assert!(mgr.get_group().is_some());
522        assert_eq!(
523            mgr.get_group_unchecked().interrupt_type(),
524            InterruptSourceType::LegacyIrq
525        );
526
527        // Disable interrupt manager
528        mgr.reset().unwrap();
529        assert!(!mgr.is_enabled());
530        assert_eq!(
531            mgr.mode2idx[DeviceInterruptMode::LegacyIrq as usize],
532            mgr.current_idx
533        );
534        assert_eq!(mgr.get_working_mode(), DeviceInterruptMode::Disabled);
535        assert!(mgr.get_group().is_none());
536    }
537
538    #[test]
539    fn test_device_interrupt_manager_switch_mode() {
540        let mut mgr = create_interrupt_manager();
541
542        // Can't switch working mode in enabled state.
543        mgr.enable().unwrap();
544        mgr.set_working_mode(DeviceInterruptMode::PciMsiIrq)
545            .unwrap_err();
546        mgr.set_working_mode(DeviceInterruptMode::PciMsixIrq)
547            .unwrap_err();
548        mgr.set_working_mode(DeviceInterruptMode::GenericMsiIrq)
549            .unwrap_err();
550        mgr.reset().unwrap();
551
552        // Switch from LEGACY to PciMsi mode
553        mgr.set_working_mode(DeviceInterruptMode::LegacyIrq)
554            .unwrap();
555        mgr.set_working_mode(DeviceInterruptMode::LegacyIrq)
556            .unwrap();
557        mgr.set_working_mode(DeviceInterruptMode::PciMsiIrq)
558            .unwrap();
559        mgr.set_working_mode(DeviceInterruptMode::PciMsiIrq)
560            .unwrap();
561        mgr.set_working_mode(DeviceInterruptMode::PciMsixIrq)
562            .unwrap_err();
563        mgr.set_working_mode(DeviceInterruptMode::GenericMsiIrq)
564            .unwrap_err();
565
566        // Switch from LEGACY to PciMsix mode
567        mgr.set_working_mode(DeviceInterruptMode::LegacyIrq)
568            .unwrap();
569        mgr.set_working_mode(DeviceInterruptMode::PciMsixIrq)
570            .unwrap();
571        mgr.set_working_mode(DeviceInterruptMode::PciMsixIrq)
572            .unwrap();
573        mgr.set_working_mode(DeviceInterruptMode::PciMsiIrq)
574            .unwrap_err();
575        mgr.set_working_mode(DeviceInterruptMode::GenericMsiIrq)
576            .unwrap_err();
577
578        // Switch from LEGACY to GenericMsi mode
579        mgr.set_working_mode(DeviceInterruptMode::LegacyIrq)
580            .unwrap();
581        mgr.set_working_mode(DeviceInterruptMode::GenericMsiIrq)
582            .unwrap();
583        mgr.set_working_mode(DeviceInterruptMode::GenericMsiIrq)
584            .unwrap();
585        mgr.set_working_mode(DeviceInterruptMode::PciMsiIrq)
586            .unwrap_err();
587        mgr.set_working_mode(DeviceInterruptMode::PciMsixIrq)
588            .unwrap_err();
589
590        // Switch from DISABLED to PciMsi mode
591        mgr.set_working_mode(DeviceInterruptMode::Disabled).unwrap();
592        mgr.set_working_mode(DeviceInterruptMode::Disabled).unwrap();
593        mgr.set_working_mode(DeviceInterruptMode::PciMsiIrq)
594            .unwrap();
595        mgr.set_working_mode(DeviceInterruptMode::PciMsixIrq)
596            .unwrap_err();
597        mgr.set_working_mode(DeviceInterruptMode::GenericMsiIrq)
598            .unwrap_err();
599
600        // Switch from DISABLED to PciMsix mode
601        mgr.set_working_mode(DeviceInterruptMode::Disabled).unwrap();
602        mgr.set_working_mode(DeviceInterruptMode::PciMsixIrq)
603            .unwrap();
604        mgr.set_working_mode(DeviceInterruptMode::PciMsiIrq)
605            .unwrap_err();
606        mgr.set_working_mode(DeviceInterruptMode::GenericMsiIrq)
607            .unwrap_err();
608
609        // Switch from DISABLED to GenericMsi mode
610        mgr.set_working_mode(DeviceInterruptMode::Disabled).unwrap();
611        mgr.set_working_mode(DeviceInterruptMode::GenericMsiIrq)
612            .unwrap();
613        mgr.set_working_mode(DeviceInterruptMode::PciMsiIrq)
614            .unwrap_err();
615        mgr.set_working_mode(DeviceInterruptMode::PciMsixIrq)
616            .unwrap_err();
617
618        mgr.set_working_mode(DeviceInterruptMode::Disabled).unwrap();
619        mgr.set_working_mode(DeviceInterruptMode::Disabled).unwrap();
620    }
621
622    #[test]
623    fn test_msi_config() {
624        let mut interrupt_manager = create_interrupt_manager();
625
626        assert!(interrupt_manager.set_msi_data(512, 0).is_err());
627        interrupt_manager.set_msi_data(0, 0).unwrap();
628        assert!(interrupt_manager.set_msi_high_address(512, 0).is_err());
629        interrupt_manager.set_msi_high_address(0, 0).unwrap();
630        assert!(interrupt_manager.set_msi_low_address(512, 0).is_err());
631        interrupt_manager.set_msi_low_address(0, 0).unwrap();
632        assert!(interrupt_manager.get_msi_mask(512).is_err());
633        assert!(!interrupt_manager.get_msi_mask(0).unwrap());
634        assert!(interrupt_manager.set_msi_mask(512, true).is_err());
635        interrupt_manager.set_msi_mask(0, true).unwrap();
636        assert!(interrupt_manager.get_msi_mask(0).unwrap());
637    }
638
639    #[test]
640    fn test_set_working_mode_after_activated() {
641        let mut interrupt_manager = create_interrupt_manager();
642        interrupt_manager.activated = true;
643        assert!(interrupt_manager
644            .set_working_mode(DeviceInterruptMode::Disabled)
645            .is_err());
646        assert!(interrupt_manager
647            .set_working_mode(DeviceInterruptMode::GenericMsiIrq)
648            .is_err());
649        assert!(interrupt_manager
650            .set_working_mode(DeviceInterruptMode::LegacyIrq)
651            .is_err());
652        assert!(interrupt_manager
653            .set_working_mode(DeviceInterruptMode::PciMsiIrq)
654            .is_err());
655        assert!(interrupt_manager
656            .set_working_mode(DeviceInterruptMode::PciMsixIrq)
657            .is_err());
658    }
659
660    #[test]
661    fn test_disable2legacy() {
662        let mut interrupt_manager = create_interrupt_manager();
663        interrupt_manager.activated = false;
664        interrupt_manager.mode = DeviceInterruptMode::Disabled;
665        interrupt_manager
666            .set_working_mode(DeviceInterruptMode::LegacyIrq)
667            .unwrap();
668    }
669
670    #[test]
671    fn test_disable2nonlegacy() {
672        let mut interrupt_manager = create_interrupt_manager();
673        interrupt_manager.activated = false;
674        interrupt_manager.mode = DeviceInterruptMode::Disabled;
675        interrupt_manager
676            .set_working_mode(DeviceInterruptMode::GenericMsiIrq)
677            .unwrap();
678    }
679
680    #[test]
681    fn test_legacy2nonlegacy() {
682        let mut interrupt_manager = create_interrupt_manager();
683        interrupt_manager.activated = false;
684        interrupt_manager.mode = DeviceInterruptMode::Disabled;
685        interrupt_manager
686            .set_working_mode(DeviceInterruptMode::LegacyIrq)
687            .unwrap();
688        interrupt_manager
689            .set_working_mode(DeviceInterruptMode::GenericMsiIrq)
690            .unwrap();
691    }
692
693    #[test]
694    fn test_nonlegacy2legacy() {
695        let mut interrupt_manager = create_interrupt_manager();
696        interrupt_manager.activated = false;
697        interrupt_manager.mode = DeviceInterruptMode::Disabled;
698        interrupt_manager
699            .set_working_mode(DeviceInterruptMode::GenericMsiIrq)
700            .unwrap();
701        interrupt_manager
702            .set_working_mode(DeviceInterruptMode::LegacyIrq)
703            .unwrap();
704    }
705
706    #[test]
707    fn test_update() {
708        let mut interrupt_manager = create_interrupt_manager();
709        interrupt_manager
710            .set_working_mode(DeviceInterruptMode::GenericMsiIrq)
711            .unwrap();
712        interrupt_manager.enable().unwrap();
713        assert!(interrupt_manager.update(0x10).is_err());
714        interrupt_manager.update(0x01).unwrap();
715        interrupt_manager.reset().unwrap();
716        interrupt_manager
717            .set_working_mode(DeviceInterruptMode::LegacyIrq)
718            .unwrap();
719        assert!(interrupt_manager.update(0x10).is_err());
720    }
721
722    #[test]
723    fn test_get_configs() {
724        // legacy irq config
725        {
726            let interrupt_manager = create_interrupt_manager();
727
728            let legacy_config = interrupt_manager.get_configs(DeviceInterruptMode::LegacyIrq);
729            assert_eq!(legacy_config, LEGACY_CONFIGS);
730        }
731
732        // generic irq config
733        {
734            let mut interrupt_manager = create_interrupt_manager();
735            interrupt_manager
736                .set_working_mode(DeviceInterruptMode::GenericMsiIrq)
737                .unwrap();
738            let msi_config = interrupt_manager.get_configs(DeviceInterruptMode::GenericMsiIrq);
739            assert_eq!(msi_config.len(), 0x10);
740        }
741
742        // msi irq config
743        {
744            let mut interrupt_manager = create_interrupt_manager();
745            interrupt_manager
746                .set_working_mode(DeviceInterruptMode::PciMsiIrq)
747                .unwrap();
748            let msi_config = interrupt_manager.get_configs(DeviceInterruptMode::PciMsiIrq);
749            assert_eq!(msi_config.len(), 0x20);
750        }
751
752        // msix irq config
753        {
754            let mut interrupt_manager = create_interrupt_manager();
755            interrupt_manager
756                .set_working_mode(DeviceInterruptMode::PciMsixIrq)
757                .unwrap();
758            let msi_config = interrupt_manager.get_configs(DeviceInterruptMode::PciMsixIrq);
759            assert_eq!(msi_config.len(), 0x30);
760        }
761    }
762
763    #[test]
764    fn test_reset_configs() {
765        let mut interrupt_manager = create_interrupt_manager();
766
767        interrupt_manager.reset_configs(DeviceInterruptMode::LegacyIrq);
768        interrupt_manager.reset_configs(DeviceInterruptMode::LegacyIrq);
769
770        interrupt_manager.set_msi_data(0, 100).unwrap();
771        interrupt_manager.set_msi_high_address(0, 200).unwrap();
772        interrupt_manager.set_msi_low_address(0, 300).unwrap();
773
774        interrupt_manager.reset_configs(DeviceInterruptMode::GenericMsiIrq);
775        assert_eq!(
776            interrupt_manager.msi_config[0],
777            InterruptSourceConfig::MsiIrq(MsiIrqSourceConfig::default())
778        );
779    }
780
781    #[test]
782    fn test_interrupt_status_register() {
783        let status = InterruptStatusRegister32::new();
784
785        assert_eq!(status.read(), 0);
786        status.write(0x13);
787        assert_eq!(status.read(), 0x13);
788        status.clear_bits(0x11);
789        assert_eq!(status.read(), 0x2);
790        status.set_bits(0x100);
791        assert_eq!(status.read_and_clear(), 0x102);
792        assert_eq!(status.read(), 0);
793    }
794}