1 // Copyright 2023 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use base::Event; 6 use base::Result; 7 use hypervisor::gunyah::GunyahVm; 8 use hypervisor::DeviceKind; 9 use hypervisor::IrqRoute; 10 use hypervisor::MPState; 11 use hypervisor::Vcpu; 12 13 use crate::IrqChip; 14 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] 15 use crate::IrqChipAArch64; 16 use crate::IrqChipCap; 17 use crate::IrqEdgeEvent; 18 use crate::IrqEventIndex; 19 use crate::IrqEventSource; 20 use crate::IrqLevelEvent; 21 use crate::VcpuRunState; 22 23 pub struct GunyahIrqChip { 24 vm: GunyahVm, 25 } 26 27 impl GunyahIrqChip { new(vm: GunyahVm) -> Result<GunyahIrqChip>28 pub fn new(vm: GunyahVm) -> Result<GunyahIrqChip> { 29 // NOTE: Unlike the other hypervisors supported by crosvm, the Gunyah IRQ chip is not 30 // explicitly configured here. Instead, Gunyah uses the information in the FDT generated by 31 // crosvm and to determine where and how to setup the IRQ chip. 32 Ok(GunyahIrqChip { vm }) 33 } 34 } 35 36 impl IrqChip for GunyahIrqChip { 37 // GunyahIrqChip doesn't need to track VCPUs. add_vcpu(&mut self, _vcpu_id: usize, _vcpu: &dyn Vcpu) -> Result<()>38 fn add_vcpu(&mut self, _vcpu_id: usize, _vcpu: &dyn Vcpu) -> Result<()> { 39 Ok(()) 40 } 41 register_edge_irq_event( &mut self, irq: u32, irq_event: &IrqEdgeEvent, _source: IrqEventSource, ) -> Result<Option<IrqEventIndex>>42 fn register_edge_irq_event( 43 &mut self, 44 irq: u32, 45 irq_event: &IrqEdgeEvent, 46 _source: IrqEventSource, 47 ) -> Result<Option<IrqEventIndex>> { 48 self.vm 49 .register_irqfd(irq, irq_event.get_trigger(), false)?; 50 Ok(None) 51 } 52 unregister_edge_irq_event(&mut self, irq: u32, irq_event: &IrqEdgeEvent) -> Result<()>53 fn unregister_edge_irq_event(&mut self, irq: u32, irq_event: &IrqEdgeEvent) -> Result<()> { 54 self.vm.unregister_irqfd(irq, irq_event.get_trigger())?; 55 Ok(()) 56 } 57 register_level_irq_event( &mut self, irq: u32, irq_event: &IrqLevelEvent, _source: IrqEventSource, ) -> Result<Option<IrqEventIndex>>58 fn register_level_irq_event( 59 &mut self, 60 irq: u32, 61 irq_event: &IrqLevelEvent, 62 _source: IrqEventSource, 63 ) -> Result<Option<IrqEventIndex>> { 64 self.vm.register_irqfd(irq, irq_event.get_trigger(), true)?; 65 Ok(None) 66 } 67 unregister_level_irq_event(&mut self, irq: u32, irq_event: &IrqLevelEvent) -> Result<()>68 fn unregister_level_irq_event(&mut self, irq: u32, irq_event: &IrqLevelEvent) -> Result<()> { 69 self.vm.unregister_irqfd(irq, irq_event.get_trigger())?; 70 Ok(()) 71 } 72 route_irq(&mut self, _route: IrqRoute) -> Result<()>73 fn route_irq(&mut self, _route: IrqRoute) -> Result<()> { 74 unimplemented!() 75 } 76 set_irq_routes(&mut self, _routes: &[IrqRoute]) -> Result<()>77 fn set_irq_routes(&mut self, _routes: &[IrqRoute]) -> Result<()> { 78 unimplemented!() 79 } 80 81 /// Return a vector of all registered irq numbers and their associated events and event 82 /// indices. These should be used by the main thread to wait for irq events. 83 /// For the GunyahIrqChip, the kernel handles listening to irq events being triggered by 84 /// devices, so this function always returns an empty Vec. irq_event_tokens(&self) -> Result<Vec<(IrqEventIndex, IrqEventSource, Event)>>85 fn irq_event_tokens(&self) -> Result<Vec<(IrqEventIndex, IrqEventSource, Event)>> { 86 Ok(Vec::new()) 87 } 88 service_irq(&mut self, _irq: u32, _level: bool) -> Result<()>89 fn service_irq(&mut self, _irq: u32, _level: bool) -> Result<()> { 90 unimplemented!() 91 } 92 93 /// Service an IRQ event by asserting then deasserting an IRQ line. The associated Event 94 /// that triggered the irq event will be read from. If the irq is associated with a resample 95 /// Event, then the deassert will only happen after an EOI is broadcast for a vector 96 /// associated with the irq line. 97 /// This function should never be called on GunyahIrqChip. service_irq_event(&mut self, _event_index: IrqEventIndex) -> Result<()>98 fn service_irq_event(&mut self, _event_index: IrqEventIndex) -> Result<()> { 99 unreachable!(); 100 } 101 102 /// Broadcast an end of interrupt. 103 /// This should never be called on a GunyahIrqChip because a Gunyah vcpu should never exit 104 /// with VcpuExit::IoapicEoi. broadcast_eoi(&self, _vector: u8) -> Result<()>105 fn broadcast_eoi(&self, _vector: u8) -> Result<()> { 106 unreachable!(); 107 } 108 109 /// Injects any pending interrupts for `vcpu`. 110 /// For GunyahIrqChip this is a no-op because Gunyah is responsible for injecting all 111 /// interrupts. inject_interrupts(&self, _vcpu: &dyn Vcpu) -> Result<()>112 fn inject_interrupts(&self, _vcpu: &dyn Vcpu) -> Result<()> { 113 Ok(()) 114 } 115 116 /// Notifies the irq chip that the specified VCPU has executed a halt instruction. 117 /// For GunyahIrqChip this is a no-op because Gunyah handles VCPU blocking. halted(&self, _vcpu_id: usize)118 fn halted(&self, _vcpu_id: usize) {} 119 wait_until_runnable(&self, _vcpu: &dyn Vcpu) -> Result<VcpuRunState>120 fn wait_until_runnable(&self, _vcpu: &dyn Vcpu) -> Result<VcpuRunState> { 121 // Gunyah handles vCPU blocking. From userspace perspective, vCPU is always runnable. 122 Ok(VcpuRunState::Runnable) 123 } 124 125 /// Makes unrunnable VCPUs return immediately from `wait_until_runnable`. 126 /// For GunyahIrqChip this is a no-op because Gunyah handles VCPU blocking. kick_halted_vcpus(&self)127 fn kick_halted_vcpus(&self) {} 128 get_mp_state(&self, _vcpu_id: usize) -> Result<MPState>129 fn get_mp_state(&self, _vcpu_id: usize) -> Result<MPState> { 130 unimplemented!() 131 } 132 set_mp_state(&mut self, _vcpu_id: usize, _state: &MPState) -> Result<()>133 fn set_mp_state(&mut self, _vcpu_id: usize, _state: &MPState) -> Result<()> { 134 unimplemented!() 135 } 136 try_clone(&self) -> Result<Self> where Self: Sized,137 fn try_clone(&self) -> Result<Self> 138 where 139 Self: Sized, 140 { 141 Ok(Self { 142 vm: self.vm.try_clone()?, 143 }) 144 } 145 finalize_devices( &mut self, _resources: &mut resources::SystemAllocator, _io_bus: &crate::Bus, _mmio_bus: &crate::Bus, ) -> Result<()>146 fn finalize_devices( 147 &mut self, 148 _resources: &mut resources::SystemAllocator, 149 _io_bus: &crate::Bus, 150 _mmio_bus: &crate::Bus, 151 ) -> Result<()> { 152 Ok(()) 153 } 154 155 /// The GunyahIrqChip doesn't process irq events itself so this function does nothing. process_delayed_irq_events(&mut self) -> Result<()>156 fn process_delayed_irq_events(&mut self) -> Result<()> { 157 Ok(()) 158 } 159 irq_delayed_event_token(&self) -> Result<Option<Event>>160 fn irq_delayed_event_token(&self) -> Result<Option<Event>> { 161 Ok(None) 162 } 163 check_capability(&self, _c: IrqChipCap) -> bool164 fn check_capability(&self, _c: IrqChipCap) -> bool { 165 false 166 } 167 } 168 169 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] 170 impl IrqChipAArch64 for GunyahIrqChip { try_box_clone(&self) -> Result<Box<dyn IrqChipAArch64>>171 fn try_box_clone(&self) -> Result<Box<dyn IrqChipAArch64>> { 172 Ok(Box::new(self.try_clone()?)) 173 } 174 as_irq_chip(&self) -> &dyn IrqChip175 fn as_irq_chip(&self) -> &dyn IrqChip { 176 self 177 } 178 as_irq_chip_mut(&mut self) -> &mut dyn IrqChip179 fn as_irq_chip_mut(&mut self) -> &mut dyn IrqChip { 180 self 181 } 182 get_vgic_version(&self) -> DeviceKind183 fn get_vgic_version(&self) -> DeviceKind { 184 DeviceKind::ArmVgicV3 185 } 186 finalize(&self) -> Result<()>187 fn finalize(&self) -> Result<()> { 188 Ok(()) 189 } 190 } 191