// Copyright 2020 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use base::error;5use base::Error;6use base::Event;7use base::Result;8#[cfg(target_arch = "x86_64")]9use hypervisor::kvm::KvmCap;10use hypervisor::kvm::KvmVcpu;11use hypervisor::IrqRoute;12use hypervisor::MPState;13use hypervisor::Vcpu;14use kvm_sys::kvm_mp_state;15use resources::SystemAllocator;1617use crate::Bus;18use crate::IrqEdgeEvent;19use crate::IrqEventSource;20use crate::IrqLevelEvent;2122#[cfg(target_arch = "x86_64")]23mod x86_64;24#[cfg(target_arch = "x86_64")]25pub use x86_64::*;2627#[cfg(target_arch = "aarch64")]28mod aarch64;29#[cfg(target_arch = "aarch64")]30pub use aarch64::*;3132#[cfg(target_arch = "riscv64")]33mod riscv64;34#[cfg(target_arch = "riscv64")]35pub use riscv64::*;3637use crate::IrqChip;38use crate::IrqChipCap;39use crate::IrqEventIndex;40use crate::VcpuRunState;4142/// This IrqChip only works with Kvm so we only implement it for KvmVcpu.43impl IrqChip for KvmKernelIrqChip {44/// Add a vcpu to the irq chip.45fn add_vcpu(&mut self, vcpu_id: usize, vcpu: &dyn Vcpu) -> Result<()> {46let vcpu: &KvmVcpu = vcpu47.downcast_ref()48.expect("KvmKernelIrqChip::add_vcpu called with non-KvmVcpu");49self.vcpus.lock()[vcpu_id] = Some(vcpu.try_clone()?);50Ok(())51}5253/// Register an event with edge-trigger semantic that can trigger an interrupt54/// for a particular GSI.55fn register_edge_irq_event(56&mut self,57irq: u32,58irq_event: &IrqEdgeEvent,59_source: IrqEventSource,60) -> Result<Option<IrqEventIndex>> {61self.vm.register_irqfd(irq, irq_event.get_trigger(), None)?;62Ok(None)63}6465/// Unregister an event with edge-trigger semantic for a particular GSI.66fn unregister_edge_irq_event(&mut self, irq: u32, irq_event: &IrqEdgeEvent) -> Result<()> {67self.vm.unregister_irqfd(irq, irq_event.get_trigger())68}6970/// Register an event with level-trigger semantic that can trigger an interrupt71/// for a particular GSI.72fn register_level_irq_event(73&mut self,74irq: u32,75irq_event: &IrqLevelEvent,76_source: IrqEventSource,77) -> Result<Option<IrqEventIndex>> {78self.vm79.register_irqfd(irq, irq_event.get_trigger(), Some(irq_event.get_resample()))?;80Ok(None)81}8283/// Unregister an event with level-trigger semantic for a particular GSI.84fn unregister_level_irq_event(&mut self, irq: u32, irq_event: &IrqLevelEvent) -> Result<()> {85self.vm.unregister_irqfd(irq, irq_event.get_trigger())86}8788/// Route an IRQ line to an interrupt controller, or to a particular MSI vector.89fn route_irq(&mut self, route: IrqRoute) -> Result<()> {90let mut routes = self.routes.lock();91routes.retain(|r| r.gsi != route.gsi);9293routes.push(route);9495self.vm.set_gsi_routing(&routes)96}9798/// Replace all irq routes with the supplied routes99fn set_irq_routes(&mut self, routes: &[IrqRoute]) -> Result<()> {100let mut current_routes = self.routes.lock();101*current_routes = routes.to_vec();102103self.vm.set_gsi_routing(¤t_routes)104}105106/// Return a vector of all registered irq numbers and their associated events and event107/// indices. These should be used by the main thread to wait for irq events.108/// For the KvmKernelIrqChip, the kernel handles listening to irq events being triggered by109/// devices, so this function always returns an empty Vec.110fn irq_event_tokens(&self) -> Result<Vec<(IrqEventIndex, IrqEventSource, Event)>> {111Ok(Vec::new())112}113114/// Either assert or deassert an IRQ line. Sends to either an interrupt controller, or does115/// a send_msi if the irq is associated with an MSI.116/// For the KvmKernelIrqChip this simply calls the KVM_SET_IRQ_LINE ioctl.117fn service_irq(&mut self, irq: u32, level: bool) -> Result<()> {118self.vm.set_irq_line(irq, level)119}120121/// Service an IRQ event by asserting then deasserting an IRQ line. The associated Event122/// that triggered the irq event will be read from. If the irq is associated with a resample123/// Event, then the deassert will only happen after an EOI is broadcast for a vector124/// associated with the irq line.125/// This function should never be called on KvmKernelIrqChip.126fn service_irq_event(&mut self, _event_index: IrqEventIndex) -> Result<()> {127error!("service_irq_event should never be called for KvmKernelIrqChip");128Ok(())129}130131/// Broadcast an end of interrupt.132/// This should never be called on a KvmKernelIrqChip because a KVM vcpu should never exit133/// with the KVM_EXIT_EOI_BROADCAST reason when an in-kernel irqchip exists.134fn broadcast_eoi(&self, _vector: u8) -> Result<()> {135error!("broadcast_eoi should never be called for KvmKernelIrqChip");136Ok(())137}138139/// Injects any pending interrupts for `vcpu`.140/// For KvmKernelIrqChip this is a no-op because KVM is responsible for injecting all141/// interrupts.142fn inject_interrupts(&self, _vcpu: &dyn Vcpu) -> Result<()> {143Ok(())144}145146/// Notifies the irq chip that the specified VCPU has executed a halt instruction.147/// For KvmKernelIrqChip this is a no-op because KVM handles VCPU blocking.148fn halted(&self, _vcpu_id: usize) {}149150/// Blocks until `vcpu` is in a runnable state or until interrupted by151/// `IrqChip::kick_halted_vcpus`. Returns `VcpuRunState::Runnable if vcpu is runnable, or152/// `VcpuRunState::Interrupted` if the wait was interrupted.153/// For KvmKernelIrqChip this is a no-op and always returns Runnable because KVM handles VCPU154/// blocking.155fn wait_until_runnable(&self, _vcpu: &dyn Vcpu) -> Result<VcpuRunState> {156Ok(VcpuRunState::Runnable)157}158159/// Makes unrunnable VCPUs return immediately from `wait_until_runnable`.160/// For KvmKernelIrqChip this is a no-op because KVM handles VCPU blocking.161fn kick_halted_vcpus(&self) {}162163/// Get the current MP state of the specified VCPU.164fn get_mp_state(&self, vcpu_id: usize) -> Result<MPState> {165match self.vcpus.lock().get(vcpu_id) {166Some(Some(vcpu)) => Ok(MPState::from(&vcpu.get_mp_state()?)),167_ => Err(Error::new(libc::ENOENT)),168}169}170171/// Set the current MP state of the specified VCPU.172fn set_mp_state(&mut self, vcpu_id: usize, state: &MPState) -> Result<()> {173match self.vcpus.lock().get(vcpu_id) {174Some(Some(vcpu)) => vcpu.set_mp_state(&kvm_mp_state::from(state)),175_ => Err(Error::new(libc::ENOENT)),176}177}178179/// Attempt to clone this IrqChip instance.180fn try_clone(&self) -> Result<Self> {181// Because the KvmKernelIrqchip struct contains arch-specific fields we leave the182// cloning to arch-specific implementations183self.arch_try_clone()184}185186/// Finalize irqchip setup. Should be called once all devices have registered irq events and187/// been added to the io_bus and mmio_bus.188/// KvmKernelIrqChip does not need to do anything here.189fn finalize_devices(190&mut self,191_resources: &mut SystemAllocator,192_io_bus: &Bus,193_mmio_bus: &Bus,194) -> Result<()> {195Ok(())196}197198/// The KvmKernelIrqChip doesn't process irq events itself so this function does nothing.199fn process_delayed_irq_events(&mut self) -> Result<()> {200Ok(())201}202203fn irq_delayed_event_token(&self) -> Result<Option<Event>> {204Ok(None)205}206207fn check_capability(&self, c: IrqChipCap) -> bool {208match c {209#[cfg(target_arch = "x86_64")]210IrqChipCap::TscDeadlineTimer => self.vm.check_raw_capability(KvmCap::TscDeadlineTimer),211#[cfg(target_arch = "x86_64")]212IrqChipCap::X2Apic => true,213IrqChipCap::MpStateGetSet => true,214}215}216}217218219