mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-12-21 21:25:19 +00:00
vmm: support injecting NMI
Inject NMI interrupt when needed, by call ioctl KVM_NMI. Signed-off-by: Yi Wang <foxywang@tencent.com>
This commit is contained in:
parent
f40dd4a993
commit
c72bf0b32d
@ -278,6 +278,11 @@ pub enum HypervisorCpuError {
|
||||
#[cfg(feature = "sev_snp")]
|
||||
#[error("Failed to set sev control register: {0}")]
|
||||
SetSevControlRegister(#[source] anyhow::Error),
|
||||
|
||||
/// Error injecting NMI
|
||||
///
|
||||
#[error("Failed to inject NMI")]
|
||||
Nmi(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -505,4 +510,12 @@ pub trait Vcpu: Send + Sync {
|
||||
fn set_sev_control_register(&self, _reg: u64) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Trigger NMI interrupt
|
||||
///
|
||||
fn nmi(&self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
@ -105,6 +105,15 @@ pub use {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const KVM_CAP_SGX_ATTRIBUTE: u32 = 196;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use vmm_sys_util::ioctl_io_nr;
|
||||
|
||||
#[cfg(all(not(feature = "tdx"), target_arch = "x86_64"))]
|
||||
use vmm_sys_util::ioctl_ioc_nr;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
ioctl_io_nr!(KVM_NMI, kvm_bindings::KVMIO, 0x9a);
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
const KVM_EXIT_TDX: u32 = 50;
|
||||
#[cfg(feature = "tdx")]
|
||||
@ -2312,6 +2321,23 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
Ok(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Trigger NMI interrupt
|
||||
///
|
||||
fn nmi(&self) -> cpu::Result<()> {
|
||||
match self.fd.nmi() {
|
||||
Err(e) => {
|
||||
if e.errno() == libc::EIO {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(cpu::HypervisorCpuError::Nmi(e.into()))
|
||||
}
|
||||
}
|
||||
Ok(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KvmVcpu {
|
||||
|
@ -405,6 +405,13 @@ paths:
|
||||
405:
|
||||
description: The VM instance could not be coredumped because it is not booted.
|
||||
|
||||
/vmm.nmi:
|
||||
put:
|
||||
summary: Inject an NMI.
|
||||
responses:
|
||||
204:
|
||||
description: The NMI successfully injected.
|
||||
|
||||
/vm.restore:
|
||||
put:
|
||||
summary: Restore a VM from a snapshot.
|
||||
|
@ -188,6 +188,10 @@ pub enum Error {
|
||||
#[cfg(feature = "sev_snp")]
|
||||
#[error("Failed to set sev control register: {0}")]
|
||||
SetSevControlRegister(#[source] hypervisor::HypervisorCpuError),
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[error("Failed to inject NMI")]
|
||||
NmiError(hypervisor::HypervisorCpuError),
|
||||
}
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@ -477,6 +481,7 @@ pub struct CpuManager {
|
||||
vm: Arc<dyn hypervisor::Vm>,
|
||||
vcpus_kill_signalled: Arc<AtomicBool>,
|
||||
vcpus_pause_signalled: Arc<AtomicBool>,
|
||||
vcpus_kick_signalled: Arc<AtomicBool>,
|
||||
exit_evt: EventFd,
|
||||
#[cfg_attr(target_arch = "aarch64", allow(dead_code))]
|
||||
reset_evt: EventFd,
|
||||
@ -721,6 +726,7 @@ impl CpuManager {
|
||||
vm,
|
||||
vcpus_kill_signalled: Arc::new(AtomicBool::new(false)),
|
||||
vcpus_pause_signalled: Arc::new(AtomicBool::new(false)),
|
||||
vcpus_kick_signalled: Arc::new(AtomicBool::new(false)),
|
||||
vcpu_states,
|
||||
exit_evt,
|
||||
reset_evt,
|
||||
@ -934,6 +940,7 @@ impl CpuManager {
|
||||
let panic_exit_evt = self.exit_evt.try_clone().unwrap();
|
||||
let vcpu_kill_signalled = self.vcpus_kill_signalled.clone();
|
||||
let vcpu_pause_signalled = self.vcpus_pause_signalled.clone();
|
||||
let vcpu_kick_signalled = self.vcpus_kick_signalled.clone();
|
||||
|
||||
let vcpu_kill = self.vcpu_states[usize::from(vcpu_id)].kill.clone();
|
||||
let vcpu_run_interrupted = self.vcpu_states[usize::from(vcpu_id)]
|
||||
@ -1058,6 +1065,18 @@ impl CpuManager {
|
||||
vcpu_run_interrupted.store(false, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
if vcpu_kick_signalled.load(Ordering::SeqCst) {
|
||||
vcpu_run_interrupted.store(true, Ordering::SeqCst);
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
match vcpu.lock().as_ref().unwrap().vcpu.nmi() {
|
||||
Ok(()) => {},
|
||||
Err(e) => {
|
||||
error!("Error when inject nmi {}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We've been told to terminate
|
||||
if vcpu_kill_signalled.load(Ordering::SeqCst)
|
||||
|| vcpu_kill.load(Ordering::SeqCst)
|
||||
@ -1848,6 +1867,18 @@ impl CpuManager {
|
||||
pub(crate) fn sev_snp_enabled(&self) -> bool {
|
||||
self.sev_snp_enabled
|
||||
}
|
||||
|
||||
pub(crate) fn nmi(&self) -> Result<()> {
|
||||
self.vcpus_kick_signalled.store(true, Ordering::SeqCst);
|
||||
|
||||
for state in self.vcpu_states.iter() {
|
||||
state.signal_thread();
|
||||
}
|
||||
|
||||
self.vcpus_kick_signalled.store(false, Ordering::SeqCst);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Cpu {
|
||||
|
@ -1842,8 +1842,7 @@ impl RequestHandler for Vmm {
|
||||
|
||||
fn vm_nmi(&mut self) -> result::Result<(), VmError> {
|
||||
if let Some(ref mut vm) = self.vm {
|
||||
info!("nmi");
|
||||
vm.power_button()
|
||||
vm.nmi()
|
||||
} else {
|
||||
Err(VmError::VmNotRunning)
|
||||
}
|
||||
|
@ -145,6 +145,7 @@ mod kvm {
|
||||
pub const KVM_CREATE_DEVICE: u64 = 0xc00c_aee0;
|
||||
pub const KVM_GET_REG_LIST: u64 = 0xc008_aeb0;
|
||||
pub const KVM_MEMORY_ENCRYPT_OP: u64 = 0xc008_aeba;
|
||||
pub const KVM_NMI: u64 = 0xae9a;
|
||||
}
|
||||
|
||||
#[cfg(feature = "kvm")]
|
||||
@ -275,6 +276,7 @@ fn create_vmm_ioctl_seccomp_rule_common_kvm() -> Result<Vec<SeccompRule>, Backen
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_SET_REGS)?],
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_SET_USER_MEMORY_REGION,)?],
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_SET_VCPU_EVENTS,)?],
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_NMI)?],
|
||||
])
|
||||
}
|
||||
|
||||
@ -688,6 +690,7 @@ fn create_vcpu_ioctl_seccomp_rule_kvm() -> Result<Vec<SeccompRule>, BackendError
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_SET_GSI_ROUTING,)?],
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_SET_USER_MEMORY_REGION,)?],
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_RUN,)?],
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_NMI)?],
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -316,6 +316,9 @@ pub enum Error {
|
||||
#[cfg(feature = "igvm")]
|
||||
#[error("Cannot load the igvm into memory: {0}")]
|
||||
IgvmLoad(#[source] igvm_loader::Error),
|
||||
|
||||
#[error("Error injecting NMI")]
|
||||
ErrorNmi,
|
||||
}
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@ -2446,6 +2449,15 @@ impl Vm {
|
||||
+ note_size as u64
|
||||
+ size_of::<elf::Elf64_Phdr>() as u64 * phdr_num as u64
|
||||
}
|
||||
|
||||
pub fn nmi(&self) -> Result<()> {
|
||||
return self
|
||||
.cpu_manager
|
||||
.lock()
|
||||
.unwrap()
|
||||
.nmi()
|
||||
.map_err(|_| Error::ErrorNmi);
|
||||
}
|
||||
}
|
||||
|
||||
impl Pausable for Vm {
|
||||
|
Loading…
Reference in New Issue
Block a user