|
| 1 | +// Copyright (C) 2019 Alibaba Cloud. All rights reserved. |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | + |
| 4 | +//! Manage virtual device's legacy interrupts based on Linux KVM framework. |
| 5 | +//! |
| 6 | +//! On x86 platforms, legacy interrupts are those managed by the Master PIC, the slave PIC and |
| 7 | +//! IOAPICs. |
| 8 | +
|
| 9 | +use super::*; |
| 10 | +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| 11 | +use kvm_bindings::{ |
| 12 | + KVM_IRQCHIP_IOAPIC, KVM_IRQCHIP_PIC_MASTER, KVM_IRQCHIP_PIC_SLAVE, KVM_IRQ_ROUTING_IRQCHIP, |
| 13 | +}; |
| 14 | + |
| 15 | +pub(super) struct LegacyIrq { |
| 16 | + base: u32, |
| 17 | + vmfd: Arc<VmFd>, |
| 18 | + irqfd: EventFd, |
| 19 | +} |
| 20 | + |
| 21 | +impl LegacyIrq { |
| 22 | + #[allow(clippy::new_ret_no_self)] |
| 23 | + pub(super) fn new( |
| 24 | + base: InterruptIndex, |
| 25 | + count: InterruptIndex, |
| 26 | + vmfd: Arc<VmFd>, |
| 27 | + _routes: Arc<KvmIrqRouting>, |
| 28 | + ) -> Result<Self> { |
| 29 | + if count != 1 { |
| 30 | + return Err(std::io::Error::from_raw_os_error(libc::EINVAL)); |
| 31 | + } |
| 32 | + Ok(LegacyIrq { |
| 33 | + base, |
| 34 | + vmfd, |
| 35 | + irqfd: EventFd::new(0)?, |
| 36 | + }) |
| 37 | + } |
| 38 | + |
| 39 | + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| 40 | + fn add_legacy_entry( |
| 41 | + gsi: u32, |
| 42 | + chip: u32, |
| 43 | + pin: u32, |
| 44 | + routes: &mut HashMap<u64, kvm_irq_routing_entry>, |
| 45 | + ) -> Result<()> { |
| 46 | + let mut entry = kvm_irq_routing_entry { |
| 47 | + gsi, |
| 48 | + type_: KVM_IRQ_ROUTING_IRQCHIP, |
| 49 | + ..Default::default() |
| 50 | + }; |
| 51 | + // Safe because we are initializing all fields of the `irqchip` struct. |
| 52 | + entry.u.irqchip.irqchip = chip; |
| 53 | + entry.u.irqchip.pin = pin; |
| 54 | + routes.insert(hash_key(&entry), entry); |
| 55 | + |
| 56 | + Ok(()) |
| 57 | + } |
| 58 | + |
| 59 | + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| 60 | + /// Build routings for IRQs connected to the master PIC, the slave PIC or the first IOAPIC. |
| 61 | + pub(super) fn initialize_legacy( |
| 62 | + routes: &mut HashMap<u64, kvm_irq_routing_entry>, |
| 63 | + ) -> Result<()> { |
| 64 | + // Build routings for the master PIC |
| 65 | + for i in 0..8 { |
| 66 | + if i != 2 { |
| 67 | + Self::add_legacy_entry(i, KVM_IRQCHIP_PIC_MASTER, i, routes)?; |
| 68 | + } |
| 69 | + } |
| 70 | + |
| 71 | + // Build routings for the slave PIC |
| 72 | + for i in 8..16 { |
| 73 | + Self::add_legacy_entry(i, KVM_IRQCHIP_PIC_SLAVE, i - 8, routes)?; |
| 74 | + } |
| 75 | + |
| 76 | + // Build routings for the first IOAPIC |
| 77 | + for i in 0..24 { |
| 78 | + if i == 0 { |
| 79 | + Self::add_legacy_entry(i, KVM_IRQCHIP_IOAPIC, 2, routes)?; |
| 80 | + } else if i != 2 { |
| 81 | + Self::add_legacy_entry(i, KVM_IRQCHIP_IOAPIC, i, routes)?; |
| 82 | + }; |
| 83 | + } |
| 84 | + |
| 85 | + Ok(()) |
| 86 | + } |
| 87 | + |
| 88 | + #[cfg(any(target_arch = "aarch", target_arch = "aarch64"))] |
| 89 | + pub(super) fn initialize_legacy( |
| 90 | + _routes: &mut HashMap<u64, kvm_irq_routing_entry>, |
| 91 | + ) -> Result<()> { |
| 92 | + //TODO |
| 93 | + Ok(()) |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | +impl InterruptSourceGroup for LegacyIrq { |
| 98 | + fn interrupt_type(&self) -> InterruptSourceType { |
| 99 | + InterruptSourceType::LegacyIrq |
| 100 | + } |
| 101 | + |
| 102 | + fn len(&self) -> u32 { |
| 103 | + 1 |
| 104 | + } |
| 105 | + |
| 106 | + fn base(&self) -> u32 { |
| 107 | + self.base |
| 108 | + } |
| 109 | + |
| 110 | + fn enable(&self, configs: &[InterruptSourceConfig]) -> Result<()> { |
| 111 | + if configs.len() != 1 { |
| 112 | + return Err(std::io::Error::from_raw_os_error(libc::EINVAL)); |
| 113 | + } |
| 114 | + // The IRQ routings for legacy IRQs have been configured during |
| 115 | + // KvmIrqManager::initialize(), so only need to register irqfd to the KVM driver. |
| 116 | + self.vmfd |
| 117 | + .register_irqfd(&self.irqfd, self.base) |
| 118 | + .map_err(from_sys_util_errno) |
| 119 | + } |
| 120 | + |
| 121 | + fn disable(&self) -> Result<()> { |
| 122 | + self.vmfd |
| 123 | + .unregister_irqfd(&self.irqfd, self.base) |
| 124 | + .map_err(from_sys_util_errno) |
| 125 | + } |
| 126 | + |
| 127 | + fn update(&self, index: InterruptIndex, _config: &InterruptSourceConfig) -> Result<()> { |
| 128 | + // For legacy interrupts, the routing configuration is managed by the PIC/IOAPIC interrupt |
| 129 | + // controller drivers, so nothing to do here. |
| 130 | + if index != 0 { |
| 131 | + return Err(std::io::Error::from_raw_os_error(libc::EINVAL)); |
| 132 | + } |
| 133 | + Ok(()) |
| 134 | + } |
| 135 | + |
| 136 | + fn notifier(&self, index: InterruptIndex) -> Option<&EventFd> { |
| 137 | + if index != 0 { |
| 138 | + None |
| 139 | + } else { |
| 140 | + Some(&self.irqfd) |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + fn trigger(&self, index: InterruptIndex) -> Result<()> { |
| 145 | + if index != 0 { |
| 146 | + return Err(std::io::Error::from_raw_os_error(libc::EINVAL)); |
| 147 | + } |
| 148 | + self.irqfd.write(1) |
| 149 | + } |
| 150 | +} |
0 commit comments