// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

// TODO: Trim qualified paths in this crate, then drop this expectation.
#![expect(clippy::absolute_paths)]

use std::collections::HashMap;
use std::fs::File;
use std::io::{Read, Write, stdout};
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::panic::AssertUnwindSafe;
#[cfg(feature = "guest_debug")]
use std::path::PathBuf;
use std::sync::mpsc::{Receiver, RecvError, SendError, Sender};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use std::{io, mem, result, thread};

use anyhow::{Context, anyhow};
#[cfg(feature = "dbus_api")]
use api::dbus::{DBusApiOptions, DBusApiShutdownChannels};
use api::http::HttpApiHandle;
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
use arch::x86_64::MAX_SUPPORTED_CPUS_LEGACY;
use console_devices::{ConsoleInfo, pre_create_console_devices};
use event_monitor::event;
use landlock::LandlockError;
use libc::{EFD_NONBLOCK, SIGINT, SIGTERM, TCSANOW, tcsetattr, termios};
use log::{debug, error, info, trace, warn};
use memory_manager::MemoryManagerSnapshotData;
use pci::PciBdf;
use seccompiler::{SeccompAction, apply_filter};
use serde::ser::{SerializeStruct, Serializer};
use serde::{Deserialize, Serialize};
use signal_hook::iterator::{Handle, Signals};
use thiserror::Error;
use tracer::trace_scoped;
use vm_memory::GuestMemoryAtomic;
use vm_memory::bitmap::AtomicBitmap;
use vm_migration::protocol::*;
use vm_migration::{
    MemoryMigrationContext, Migratable, MigratableError, OngoingMigrationContext, Pausable,
    Snapshot, Snapshottable, Transportable,
};
use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::signal::unblock_signal;
use vmm_sys_util::sock_ctrl_msg::ScmSocket;

use crate::api::{
    ApiRequest, ApiResponse, RequestHandler, TimeoutStrategy, VmInfoResponse,
    VmReceiveMigrationData, VmSendMigrationData, VmmPingResponse,
};
use crate::config::{MemoryRestoreMode, RestoreConfig, add_to_config};
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
use crate::coredump::GuestDebuggable;
use crate::landlock::Landlock;
use crate::memory_manager::MemoryManager;
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
use crate::migration::get_vm_snapshot;
use crate::migration::{recv_vm_config, recv_vm_state};
use crate::migration_transport::{
    ReceiveAdditionalConnections, ReceiveListener, SendAdditionalConnections, SocketStream,
};
use crate::migration_worker::{MigrationWorker, MigrationWorkerHandle, MigrationWorkerResult};
use crate::seccomp_filters::{Thread, get_seccomp_filter};
use crate::vm::{Error as VmError, Vm, VmState};
use crate::vm_config::{
    DeviceConfig, DiskConfig, FsConfig, GenericVhostUserConfig, NetConfig, PmemConfig,
    UserDeviceConfig, VdpaConfig, VmConfig, VsockConfig,
};

mod acpi;
pub mod api;
mod clone3;
pub mod config;
pub mod console_devices;
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
mod coredump;
pub mod cpu;
pub mod device_manager;
pub mod device_tree;
#[cfg(feature = "guest_debug")]
mod gdb;
#[cfg(feature = "igvm")]
mod igvm;
pub mod interrupt;
pub mod landlock;
pub mod memory_manager;
pub mod migration;
pub mod migration_transport;
mod migration_worker;
mod pci_segment;
pub mod seccomp_filters;
mod serial_manager;
#[cfg(all(feature = "kvm", feature = "sev_snp", feature = "fw_cfg"))]
pub(crate) mod sev;
mod sigwinch_listener;
pub mod sparse;
mod sync_utils;
mod uffd;
mod userfaultfd;
pub mod vm;
pub mod vm_config;

type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
type GuestRegionMmap = vm_memory::GuestRegionMmap<AtomicBitmap>;

/// Errors associated with VMM management
#[derive(Debug, Error)]
pub enum Error {
    /// API request receive error
    #[error("Error receiving API request")]
    ApiRequestRecv(#[source] RecvError),

    /// API response send error
    #[error("Error sending API request")]
    ApiResponseSend(#[source] SendError<ApiResponse>),

    /// Cannot bind to the UNIX domain socket path
    #[error("Error binding to UNIX domain socket")]
    Bind(#[source] io::Error),

    /// Cannot clone EventFd.
    #[error("Error cloning EventFd")]
    EventFdClone(#[source] io::Error),

    /// Cannot create EventFd.
    #[error("Error creating EventFd")]
    EventFdCreate(#[source] io::Error),

    /// Cannot read from EventFd.
    #[error("Error reading from EventFd")]
    EventFdRead(#[source] io::Error),

    /// Cannot create epoll context.
    #[error("Error creating epoll context")]
    Epoll(#[source] io::Error),

    /// Cannot create HTTP thread
    #[error("Error spawning HTTP thread")]
    HttpThreadSpawn(#[source] io::Error),

    /// Cannot create D-Bus thread
    #[cfg(feature = "dbus_api")]
    #[error("Error spawning D-Bus thread")]
    DBusThreadSpawn(#[source] io::Error),

    /// Cannot start D-Bus session
    #[cfg(feature = "dbus_api")]
    #[error("Error starting D-Bus session")]
    CreateDBusSession(#[source] zbus::Error),

    /// Cannot create `event-monitor` thread
    #[error("Error spawning `event-monitor` thread")]
    EventMonitorThreadSpawn(#[source] io::Error),

    /// Cannot handle the VM STDIN stream
    #[error("Error handling VM stdin")]
    Stdin(#[source] VmError),

    /// Cannot handle the VM pty stream
    #[error("Error handling VM pty")]
    Pty(#[source] VmError),

    /// Cannot reboot the VM
    #[error("Error rebooting VM")]
    VmReboot(#[source] VmError),

    /// Cannot shut the VM down
    #[error("Error shutting down VM")]
    VmShutdown(#[source] VmError),

    /// Cannot create VMM thread
    #[error("Error spawning VMM thread")]
    VmmThreadSpawn(#[source] io::Error),

    /// Cannot shut the VMM down
    #[error("Error shutting down VMM")]
    VmmShutdown(#[source] VmError),

    /// Cannot create seccomp filter
    #[error("Error creating seccomp filter")]
    CreateSeccompFilter(#[source] seccompiler::Error),

    /// Cannot apply seccomp filter
    #[error("Error applying seccomp filter")]
    ApplySeccompFilter(#[source] seccompiler::Error),

    /// Error activating virtio devices
    #[error("Error activating virtio devices")]
    ActivateVirtioDevices(#[source] VmError),

    /// Error creating API server
    // TODO We should add #[source] here once the type implements Error.
    // Then we also can remove the `: {}` to align with the other errors.
    #[error("Error creating API server: {0}")]
    CreateApiServer(micro_http::ServerError),

    /// Error binding API server socket
    #[error("Error creation API server's socket")]
    CreateApiServerSocket(#[source] io::Error),

    /// The API server socket is already in use by another running instance
    #[error("API socket {0:?} is already in use by another running instance")]
    ApiSocketInUse(std::path::PathBuf),

    #[cfg(feature = "guest_debug")]
    #[error("Failed to start the GDB thread")]
    GdbThreadSpawn(#[source] io::Error),

    /// GDB request receive error
    #[cfg(feature = "guest_debug")]
    #[error("Error receiving GDB request")]
    GdbRequestRecv(#[source] RecvError),

    /// GDB response send error
    #[cfg(feature = "guest_debug")]
    #[error("Error sending GDB request")]
    GdbResponseSend(#[source] SendError<gdb::GdbResponse>),

    #[error("Cannot spawn a signal handler thread")]
    SignalHandlerSpawn(#[source] io::Error),

    #[error("Failed to join on threads: {0:?}")]
    ThreadCleanup(std::boxed::Box<dyn std::any::Any + std::marker::Send>),

    /// Cannot create Landlock object
    #[error("Error creating landlock object")]
    CreateLandlock(#[source] LandlockError),

    /// Cannot apply landlock based sandboxing
    #[error("Error applying landlock")]
    ApplyLandlock(#[source] LandlockError),
}

impl From<&VmConfig> for hypervisor::HypervisorVmConfig {
    fn from(_value: &VmConfig) -> Self {
        hypervisor::HypervisorVmConfig {
            #[cfg(feature = "tdx")]
            tdx_enabled: _value.platform.as_ref().is_some_and(|p| p.tdx),
            #[cfg(feature = "sev_snp")]
            sev_snp_enabled: _value.is_sev_snp_enabled(),
            #[cfg(feature = "sev_snp")]
            mem_size: _value.memory.total_size(),
            #[cfg(feature = "sev_snp")]
            vmsa_features: 0,
            nested: _value.cpus.nested,
            smt_enabled: _value
                .cpus
                .topology
                .as_ref()
                .is_some_and(|t| t.threads_per_core > 1),
        }
    }
}

pub type Result<T> = result::Result<T, Error>;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u64)]
pub enum EpollDispatch {
    Exit = 0,
    Reset = 1,
    Api = 2,
    ActivateVirtioDevices = 3,
    Debug = 4,
    GuestExit = 5,
    CheckMigration = 6,
    Unknown,
}

impl From<u64> for EpollDispatch {
    fn from(v: u64) -> Self {
        use EpollDispatch::*;
        match v {
            0 => Exit,
            1 => Reset,
            2 => Api,
            3 => ActivateVirtioDevices,
            4 => Debug,
            5 => GuestExit,
            6 => CheckMigration,
            _ => Unknown,
        }
    }
}

pub struct EpollContext {
    epoll_file: File,
}

impl EpollContext {
    pub fn new() -> result::Result<EpollContext, io::Error> {
        let epoll_fd = epoll::create(true)?;
        // Use 'File' to enforce closing on 'epoll_fd'
        // SAFETY: the epoll_fd returned by epoll::create is valid and owned by us.
        let epoll_file = unsafe { File::from_raw_fd(epoll_fd) };

        Ok(EpollContext { epoll_file })
    }

    pub fn add_event<T>(&mut self, fd: &T, token: EpollDispatch) -> result::Result<(), io::Error>
    where
        T: AsRawFd,
    {
        let dispatch_index = token as u64;
        epoll::ctl(
            self.epoll_file.as_raw_fd(),
            epoll::ControlOptions::EPOLL_CTL_ADD,
            fd.as_raw_fd(),
            epoll::Event::new(epoll::Events::EPOLLIN, dispatch_index),
        )?;

        Ok(())
    }

    #[cfg(fuzzing)]
    pub fn add_event_custom<T>(
        &mut self,
        fd: &T,
        id: u64,
        evts: epoll::Events,
    ) -> result::Result<(), io::Error>
    where
        T: AsRawFd,
    {
        epoll::ctl(
            self.epoll_file.as_raw_fd(),
            epoll::ControlOptions::EPOLL_CTL_ADD,
            fd.as_raw_fd(),
            epoll::Event::new(evts, id),
        )?;

        Ok(())
    }
}

impl AsRawFd for EpollContext {
    fn as_raw_fd(&self) -> RawFd {
        self.epoll_file.as_raw_fd()
    }
}

pub struct PciDeviceInfo {
    pub id: String,
    pub bdf: PciBdf,
}

impl Serialize for PciDeviceInfo {
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let bdf_str = self.bdf.to_string();

        // Serialize the structure.
        let mut state = serializer.serialize_struct("PciDeviceInfo", 2)?;
        state.serialize_field("id", &self.id)?;
        state.serialize_field("bdf", &bdf_str)?;
        state.end()
    }
}

pub fn feature_list() -> Vec<String> {
    vec![
        #[cfg(feature = "dbus_api")]
        "dbus_api".to_string(),
        #[cfg(feature = "dhat-heap")]
        "dhat-heap".to_string(),
        #[cfg(feature = "fw_cfg")]
        "fw_cfg".to_string(),
        #[cfg(feature = "guest_debug")]
        "guest_debug".to_string(),
        #[cfg(feature = "igvm")]
        "igvm".to_string(),
        #[cfg(feature = "io_uring")]
        "io_uring".to_string(),
        #[cfg(feature = "kvm")]
        "kvm".to_string(),
        #[cfg(feature = "mshv")]
        "mshv".to_string(),
        #[cfg(feature = "sev_snp")]
        "sev_snp".to_string(),
        #[cfg(feature = "tdx")]
        "tdx".to_string(),
        #[cfg(feature = "tracing")]
        "tracing".to_string(),
        #[cfg(feature = "ivshmem")]
        "ivshmem".to_string(),
    ]
}

pub fn start_event_monitor_thread(
    mut monitor: event_monitor::Monitor,
    seccomp_action: &SeccompAction,
    landlock_enable: bool,
    exit_event: EventFd,
) -> Result<thread::JoinHandle<Result<()>>> {
    // Retrieve seccomp filter
    let seccomp_filter = get_seccomp_filter(seccomp_action, Thread::EventMonitor, None)
        .map_err(Error::CreateSeccompFilter)?;

    thread::Builder::new()
        .name("event-monitor".to_owned())
        .spawn(move || {
            // Apply seccomp filter
            if !seccomp_filter.is_empty() {
                apply_filter(&seccomp_filter)
                    .map_err(Error::ApplySeccompFilter)
                    .inspect_err(|e| {
                        error!("Error applying seccomp filter: {e:?}");
                        exit_event.write(1).ok();
                    })?;
            }
            if landlock_enable {
                Landlock::new()
                    .map_err(Error::CreateLandlock)?
                    .restrict_self()
                    .map_err(Error::ApplyLandlock)
                    .inspect_err(|e| {
                        error!("Error applying landlock to event monitor thread: {e:?}");
                        exit_event.write(1).ok();
                    })?;
            }

            std::panic::catch_unwind(AssertUnwindSafe(move || {
                while let Ok(event) = monitor.rx.recv() {
                    let event = Arc::new(event);

                    if let Some(ref mut file) = monitor.file {
                        file.write_all(event.as_bytes().as_ref()).ok();
                        file.write_all(b"\n\n").ok();
                    }

                    for tx in monitor.broadcast.iter() {
                        tx.send(event.clone()).ok();
                    }
                }
            }))
            .map_err(|_| {
                error!("`event-monitor` thread panicked");
                exit_event.write(1).ok();
            })
            .ok();

            Ok(())
        })
        .map_err(Error::EventMonitorThreadSpawn)
}

#[expect(clippy::too_many_arguments)]
pub fn start_vmm_thread(
    vmm_version: VmmVersionInfo,
    http_path: &Option<String>,
    http_fd: Option<RawFd>,
    #[cfg(feature = "dbus_api")] dbus_options: Option<DBusApiOptions>,
    api_event: EventFd,
    api_sender: Sender<ApiRequest>,
    api_receiver: Receiver<ApiRequest>,
    #[cfg(feature = "guest_debug")] debug_path: Option<PathBuf>,
    #[cfg(feature = "guest_debug")] debug_event: EventFd,
    #[cfg(feature = "guest_debug")] vm_debug_event: EventFd,
    exit_event: EventFd,
    seccomp_action: &SeccompAction,
    hypervisor: Arc<dyn hypervisor::Hypervisor>,
    no_shutdown: bool,
    landlock_enable: bool,
) -> Result<VmmThreadHandle> {
    #[cfg(feature = "guest_debug")]
    let gdb_hw_breakpoints = hypervisor.get_guest_debug_hw_bps();
    #[cfg(feature = "guest_debug")]
    let (gdb_sender, gdb_receiver) = std::sync::mpsc::channel();
    #[cfg(feature = "guest_debug")]
    let gdb_debug_event = debug_event.try_clone().map_err(Error::EventFdClone)?;
    #[cfg(feature = "guest_debug")]
    let gdb_vm_debug_event = vm_debug_event.try_clone().map_err(Error::EventFdClone)?;

    let api_event_clone = api_event.try_clone().map_err(Error::EventFdClone)?;
    let hypervisor_type = hypervisor.hypervisor_type();

    // Retrieve seccomp filter
    let vmm_seccomp_filter = get_seccomp_filter(seccomp_action, Thread::Vmm, Some(hypervisor_type))
        .map_err(Error::CreateSeccompFilter)?;

    let vmm_seccomp_action = seccomp_action.clone();
    let thread = {
        let exit_event = exit_event.try_clone().map_err(Error::EventFdClone)?;
        thread::Builder::new()
            .name("vmm".to_string())
            .spawn(move || {
                // Apply seccomp filter for VMM thread.
                if !vmm_seccomp_filter.is_empty() {
                    apply_filter(&vmm_seccomp_filter).map_err(Error::ApplySeccompFilter)?;
                }

                let mut vmm = Vmm::new(
                    vmm_version,
                    api_event,
                    #[cfg(feature = "guest_debug")]
                    debug_event,
                    #[cfg(feature = "guest_debug")]
                    vm_debug_event,
                    vmm_seccomp_action,
                    hypervisor,
                    exit_event,
                    no_shutdown,
                )?;

                vmm.setup_signal_handler(landlock_enable)?;

                vmm.control_loop(
                    &api_receiver,
                    #[cfg(feature = "guest_debug")]
                    &gdb_receiver,
                )
            })
            .map_err(Error::VmmThreadSpawn)?
    };

    // The VMM thread is started, we can start the dbus thread
    // and start serving HTTP requests
    #[cfg(feature = "dbus_api")]
    let dbus_shutdown_chs = match dbus_options {
        Some(opts) => {
            let (_, chs) = api::start_dbus_thread(
                opts,
                api_event_clone.try_clone().map_err(Error::EventFdClone)?,
                api_sender.clone(),
                seccomp_action,
                exit_event.try_clone().map_err(Error::EventFdClone)?,
            )?;
            Some(chs)
        }
        None => None,
    };

    let http_api_handle = if let Some(http_path) = http_path {
        Some(api::start_http_path_thread(
            http_path,
            api_event_clone,
            api_sender,
            seccomp_action,
            exit_event,
            landlock_enable,
        )?)
    } else if let Some(http_fd) = http_fd {
        Some(api::start_http_fd_thread(
            http_fd,
            api_event_clone,
            api_sender,
            seccomp_action,
            exit_event,
            landlock_enable,
        )?)
    } else {
        None
    };

    #[cfg(feature = "guest_debug")]
    if let Some(debug_path) = debug_path {
        let target = gdb::GdbStub::new(
            gdb_sender,
            gdb_debug_event,
            gdb_vm_debug_event,
            gdb_hw_breakpoints,
        );
        thread::Builder::new()
            .name("gdb".to_owned())
            .spawn(move || gdb::gdb_thread(target, &debug_path))
            .map_err(Error::GdbThreadSpawn)?;
    }

    Ok(VmmThreadHandle {
        thread_handle: thread,
        #[cfg(feature = "dbus_api")]
        dbus_shutdown_chs,
        http_api_handle,
    })
}

/// Measures the time of the callback, in case it returns `Ok`.
fn measure_ok<T, E, F>(f: F) -> result::Result<(T, Duration), E>
where
    F: FnOnce() -> result::Result<T, E>,
{
    let begin = Instant::now();
    let value = f()?;
    let duration = begin.elapsed();
    Ok((value, duration))
}

#[derive(Clone, Deserialize, Serialize)]
pub struct VmMigrationConfig {
    vm_config: Arc<Mutex<VmConfig>>,
    #[cfg(all(feature = "kvm", target_arch = "x86_64"))]
    common_cpuid: Vec<hypervisor::arch::x86::CpuIdEntry>,
    memory_manager_data: MemoryManagerSnapshotData,
}

impl VmMigrationConfig {
    pub fn memory_manager_data(&self) -> &MemoryManagerSnapshotData {
        &self.memory_manager_data
    }
}

#[derive(Debug, Clone)]
pub struct VmmVersionInfo {
    pub build_version: String,
    pub version: String,
}

impl VmmVersionInfo {
    pub fn new(build_version: &str, version: &str) -> Self {
        Self {
            build_version: build_version.to_owned(),
            version: version.to_owned(),
        }
    }
}

pub struct VmmThreadHandle {
    pub thread_handle: thread::JoinHandle<Result<()>>,
    #[cfg(feature = "dbus_api")]
    pub dbus_shutdown_chs: Option<DBusApiShutdownChannels>,
    pub http_api_handle: Option<HttpApiHandle>,
}

/// Models the current ownership and associated state of the VM from the
/// perspective of the VMM.
pub enum VmOwnership {
    Owned(Vm),
    /// The VM is temporarily owned by an ongoing migration worker.
    Migration {
        migration_worker_handle: MigrationWorkerHandle,
        /// Snapshot returned while the VMM cannot inspect the worker-owned VM.
        vm_info_response: VmInfoResponse,
    },
    None,
}

impl VmOwnership {
    /// Returns a mutable reference to the underlying VM, if available.
    fn as_mut(&mut self) -> Option<&mut Vm> {
        match self {
            VmOwnership::Owned(vm) => Some(vm),
            _ => None,
        }
    }

    /// Takes the inner VM if it is currently owned.
    fn take_owned_or(&mut self, none_error: VmError) -> result::Result<Vm, VmError> {
        match mem::replace(self, VmOwnership::None) {
            VmOwnership::Owned(vm) => Ok(vm),
            old @ VmOwnership::Migration { .. } => {
                *self = old;
                Err(VmError::VmMigrating)
            }
            VmOwnership::None => Err(none_error),
        }
    }
}

pub struct Vmm {
    epoll: EpollContext,
    exit_evt: EventFd,
    reset_evt: EventFd,
    guest_exit_evt: EventFd,
    api_evt: EventFd,
    #[cfg(feature = "guest_debug")]
    debug_evt: EventFd,
    #[cfg(feature = "guest_debug")]
    vm_debug_evt: EventFd,
    version: VmmVersionInfo,
    vm: VmOwnership,
    vm_config: Option<Arc<Mutex<VmConfig>>>,
    seccomp_action: SeccompAction,
    hypervisor: Arc<dyn hypervisor::Hypervisor>,
    activate_evt: EventFd,
    signals: Option<Handle>,
    threads: Vec<thread::JoinHandle<()>>,
    original_termios_opt: Arc<Mutex<Option<termios>>>,
    console_resize_pipe: Option<Arc<File>>,
    console_info: Option<ConsoleInfo>,
    no_shutdown: bool,
    check_migration_evt: EventFd,
}

/// Just a wrapper for the data that goes into
/// [`ReceiveMigrationState::Configured`]
struct ReceiveMigrationConfiguredData {
    memory_manager: Arc<Mutex<MemoryManager>>,
    guest_memory: GuestMemoryAtomic<GuestMemoryMmap>,
    connections: ReceiveAdditionalConnections,
}
/// The receiver's state machine behind the migration protocol.
enum ReceiveMigrationState {
    /// The connection is established and we haven't received any commands yet.
    Established,

    /// We received the start command.
    Started,

    /// We received file descriptors for memory. This can only happen on UNIX domain sockets.
    MemoryFdsReceived(Vec<(u32, File)>),

    /// We received the VM configuration. We keep a direct reference to the guest memory
    /// around to populate it without having to acquire a lock (which we would have to do
    /// when accessing the memory through the memory manager).
    ///
    /// We keep the memory manager around to pass it into the next state. From this point
    /// on, the sender can start sending memory updates.
    Configured(ReceiveMigrationConfiguredData),

    /// Memory is populated and we received the state. The VM is ready to go.
    StateReceived {
        /// The timestamp where the VMM started to receive the final state.
        state_receive_begin: Instant,
    },

    /// The migration is successful.
    Completed,

    /// The migration couldn't complete, either due to an error or because the sender abandoned the migration.
    Aborted,
}

impl ReceiveMigrationState {
    fn variant_name(&self) -> &'static str {
        match self {
            ReceiveMigrationState::Established => "Established",
            ReceiveMigrationState::Started => "Started",
            ReceiveMigrationState::MemoryFdsReceived(_) => "MemoryFdsReceived",
            ReceiveMigrationState::Configured(_) => "Configured",
            ReceiveMigrationState::StateReceived { .. } => "StateReceived",
            ReceiveMigrationState::Completed => "Completed",
            ReceiveMigrationState::Aborted => "Aborted",
        }
    }

    fn finished(&self) -> bool {
        matches!(
            self,
            ReceiveMigrationState::Completed | ReceiveMigrationState::Aborted
        )
    }
}

impl Vmm {
    pub const HANDLED_SIGNALS: [i32; 2] = [SIGTERM, SIGINT];

    fn signal_handler(
        mut signals: Signals,
        original_termios_opt: &Mutex<Option<termios>>,
        exit_evt: &EventFd,
    ) {
        for sig in &Self::HANDLED_SIGNALS {
            unblock_signal(*sig).unwrap();
        }

        for signal in signals.forever() {
            match signal {
                #[expect(clippy::collapsible_match)]
                SIGTERM | SIGINT => {
                    if exit_evt.write(1).is_err() {
                        // Resetting the terminal is usually done as the VMM exits
                        if let Ok(lock) = original_termios_opt.lock() {
                            if let Some(termios) = *lock {
                                // SAFETY: FFI call
                                let _ = unsafe {
                                    tcsetattr(stdout().lock().as_raw_fd(), TCSANOW, &termios)
                                };
                            }
                        } else {
                            warn!("Failed to lock original termios");
                        }

                        std::process::exit(1);
                    }
                }
                _ => (),
            }
        }
    }

    fn setup_signal_handler(&mut self, landlock_enable: bool) -> Result<()> {
        let signals = Signals::new(Self::HANDLED_SIGNALS);
        match signals {
            Ok(signals) => {
                self.signals = Some(signals.handle());
                let exit_evt = self.exit_evt.try_clone().map_err(Error::EventFdClone)?;
                let original_termios_opt = Arc::clone(&self.original_termios_opt);

                let signal_handler_seccomp_filter =
                    get_seccomp_filter(&self.seccomp_action, Thread::SignalHandler, None)
                        .map_err(Error::CreateSeccompFilter)?;
                self.threads.push(
                    thread::Builder::new()
                        .name("vmm_signal_handler".to_string())
                        .spawn(move || {
                            if !signal_handler_seccomp_filter.is_empty() && let Err(e) = apply_filter(&signal_handler_seccomp_filter)
                                    .map_err(Error::ApplySeccompFilter)
                                {
                                    error!("Error applying seccomp filter: {e:?}");
                                    exit_evt.write(1).ok();
                                    return;
                                }

                            if landlock_enable{
                                match Landlock::new() {
                                    Ok(landlock) => {
                                        let _ = landlock.restrict_self().map_err(Error::ApplyLandlock).map_err(|e| {
                                            error!("Error applying Landlock to signal handler thread: {e:?}");
                                            exit_evt.write(1).ok();
                                        });
                                    }
                                    Err(e) => {
                                        error!("Error creating Landlock object: {e:?}");
                                        exit_evt.write(1).ok();
                                    }
                                }
                            }

                            std::panic::catch_unwind(AssertUnwindSafe(|| {
                                Vmm::signal_handler(signals, original_termios_opt.as_ref(), &exit_evt);
                            }))
                            .map_err(|_| {
                                error!("vmm signal_handler thread panicked");
                                exit_evt.write(1).ok()
                            })
                            .ok();
                        })
                        .map_err(Error::SignalHandlerSpawn)?,
                );
            }
            Err(e) => error!("Signal not found {e}"),
        }
        Ok(())
    }

    #[allow(clippy::too_many_arguments)]
    fn new(
        vmm_version: VmmVersionInfo,
        api_evt: EventFd,
        #[cfg(feature = "guest_debug")] debug_evt: EventFd,
        #[cfg(feature = "guest_debug")] vm_debug_evt: EventFd,
        seccomp_action: SeccompAction,
        hypervisor: Arc<dyn hypervisor::Hypervisor>,
        exit_evt: EventFd,
        no_shutdown: bool,
    ) -> Result<Self> {
        let mut epoll = EpollContext::new().map_err(Error::Epoll)?;
        let reset_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?;
        let guest_exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?;
        let activate_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?;
        let check_migration_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?;

        epoll
            .add_event(&exit_evt, EpollDispatch::Exit)
            .map_err(Error::Epoll)?;

        epoll
            .add_event(&reset_evt, EpollDispatch::Reset)
            .map_err(Error::Epoll)?;

        epoll
            .add_event(&guest_exit_evt, EpollDispatch::GuestExit)
            .map_err(Error::Epoll)?;

        epoll
            .add_event(&activate_evt, EpollDispatch::ActivateVirtioDevices)
            .map_err(Error::Epoll)?;

        epoll
            .add_event(&api_evt, EpollDispatch::Api)
            .map_err(Error::Epoll)?;

        #[cfg(feature = "guest_debug")]
        epoll
            .add_event(&debug_evt, EpollDispatch::Debug)
            .map_err(Error::Epoll)?;

        epoll
            .add_event(&check_migration_evt, EpollDispatch::CheckMigration)
            .map_err(Error::Epoll)?;

        Ok(Vmm {
            epoll,
            exit_evt,
            reset_evt,
            guest_exit_evt,
            api_evt,
            #[cfg(feature = "guest_debug")]
            debug_evt,
            #[cfg(feature = "guest_debug")]
            vm_debug_evt,
            version: vmm_version,
            vm: VmOwnership::None,
            vm_config: None,
            seccomp_action,
            hypervisor,
            activate_evt,
            signals: None,
            threads: vec![],
            original_termios_opt: Arc::new(Mutex::new(None)),
            console_resize_pipe: None,
            console_info: None,
            no_shutdown,
            check_migration_evt,
        })
    }

    /// Try to receive a file descriptor from a socket. Returns the slot number and the file descriptor.
    fn vm_receive_memory_fd(
        socket: &mut SocketStream,
    ) -> std::result::Result<(u32, File), MigratableError> {
        if let SocketStream::Unix(unix_socket) = socket {
            let mut buf = [0u8; 4];
            let (_, file) = unix_socket.recv_with_fd(&mut buf).map_err(|e| {
                MigratableError::MigrateReceive(anyhow!("Error receiving slot from socket: {e}"))
            })?;

            file.ok_or_else(|| MigratableError::MigrateReceive(anyhow!("Failed to receive socket")))
                .map(|file| (u32::from_le_bytes(buf), file))
        } else {
            Err(MigratableError::MigrateReceive(anyhow!(
                "Unsupported socket type"
            )))
        }
    }

    /// Handle a migration command and advance the protocol state machine.
    ///
    /// **Note**: This function is responsible for consuming any payloads! It also must
    /// _not_ write any response to the socket.
    fn vm_receive_migration_step(
        &mut self,
        socket: &mut SocketStream,
        listener: &ReceiveListener,
        state: ReceiveMigrationState,
        req: &Request,
        _receive_data_migration: &VmReceiveMigrationData,
    ) -> std::result::Result<ReceiveMigrationState, MigratableError> {
        use ReceiveMigrationState::*;

        let invalid_command = |state: &str, cmd: Command| {
            Err(MigratableError::MigrateReceive(anyhow!(
                "Can't handle command {cmd:?} in current receive state {state}"
            )))
        };

        let mut configure_vm =
            |socket: &mut SocketStream,
             memory_files: HashMap<u32, File>|
             -> std::result::Result<ReceiveMigrationConfiguredData, MigratableError> {
                let memory_manager = self.vm_receive_config(req, socket, memory_files)?;
                let guest_memory = memory_manager.lock().unwrap().guest_memory();
                // Create the additional-connection receiver even in the single-connection case.
                // At this point the receiver does not know whether the sender will use extra TCP
                // connections. If it does not, no worker connections are accepted and memory
                // requests continue to arrive on the main connection.
                let connections = listener
                    .try_clone()
                    .and_then(|l| ReceiveAdditionalConnections::new(l, guest_memory.clone()))?;
                Ok(ReceiveMigrationConfiguredData {
                    memory_manager,
                    guest_memory,
                    connections,
                })
            };

        let recv_memory_fd = |socket: &mut SocketStream,
                              mut memory_files: Vec<(u32, File)>|
         -> std::result::Result<Vec<(u32, File)>, MigratableError> {
            let (slot, file) = Self::vm_receive_memory_fd(socket)?;

            memory_files.push((slot, file));
            Ok(memory_files)
        };

        if req.command() == Command::Abandon {
            info!("Abandon Command Received");
            return Ok(Aborted);
        }

        let state_name = state.variant_name();
        match state {
            Established => match req.command() {
                Command::Start => {
                    let migration_protocol_version = req.sender_protocol_version()?;
                    debug!("Using migration protocol {migration_protocol_version}");
                    Ok(Started)
                }
                c => invalid_command(state_name, c),
            },
            Started => match req.command() {
                Command::MemoryFd => recv_memory_fd(socket, Vec::new()).map(MemoryFdsReceived),
                Command::Config => configure_vm(socket, Default::default()).map(Configured),
                c => invalid_command(state_name, c),
            },
            MemoryFdsReceived(memory_files) => match req.command() {
                Command::MemoryFd => recv_memory_fd(socket, memory_files).map(MemoryFdsReceived),
                Command::Config => {
                    configure_vm(socket, HashMap::from_iter(memory_files)).map(Configured)
                }
                c => invalid_command(state_name, c),
            },
            Configured(mut config_data) => match req.command() {
                // Memory commands use the main connection only in the single-connection case.
                // When multiple TCP connections are configured, the worker connections carry
                // all memory commands and the main connection is used only for control traffic.
                Command::Memory => {
                    migration_transport::receive_memory_ranges(
                        &config_data.guest_memory,
                        req,
                        socket,
                    )
                    .inspect_err(|_| {
                        // connections.cleanup() already logs all errors that occurred in one of the
                        // threads. Furthermore, this path is only taken in the single-connection case,
                        // thus we do not expect any errors during this cleanup. The warning should
                        // reflect that.
                        if let Err(e) = config_data.connections.cleanup() {
                            warn!(
                                "Unexpected error while cleaning up migration connections after a main-connection memory receive failure: {e}"
                            );
                        }
                    })?;
                    Ok(Configured(config_data))
                }
                Command::State => {
                    let state_receive_begin = Instant::now();
                    config_data.connections.cleanup()?;
                    let (recv_state_dur, restore_vm_dur) =
                        self.vm_receive_state(req, socket, config_data.memory_manager)?;
                    debug!(
                        "Migration (incoming): recv_snapshot:{}ms restore:{}ms",
                        recv_state_dur.as_millis(),
                        restore_vm_dur.as_millis(),
                    );
                    Ok(StateReceived {
                        state_receive_begin,
                    })
                }
                c => invalid_command(state_name, c),
            },
            StateReceived {
                state_receive_begin,
            } => match req.command() {
                Command::CompletePaused => {
                    debug!("Migration (incoming): Receiving final state of a paused VM");
                    Ok(Completed)
                }
                Command::Complete => {
                    let vm = self
                        .vm
                        .as_mut()
                        .expect("VM should have been created by now");
                    let (_, resume_duration) = measure_ok(|| vm.resume())?;
                    debug!(
                        "Migration (incoming): resume:{}ms",
                        resume_duration.as_millis()
                    );
                    // This logs the downtime without the final memory delta, so
                    // it does not reflect the actual downtime. While we could
                    // pass along the timestamp from when the VM was paused,
                    // that would rely on both VM hosts having synchronized
                    // clocks, which we cannot guarantee. For that reason, this
                    // is logged as debug! rather than info!.
                    debug!(
                        "Migration (incoming): Receiving final state and resuming the VM took {}ms",
                        state_receive_begin.elapsed().as_millis()
                    );
                    Ok(Completed)
                }
                c => invalid_command(state_name, c),
            },
            Completed | Aborted => {
                unreachable!("Performed a step on the finished state machine")
            }
        }
    }

    fn vm_receive_config<T>(
        &mut self,
        req: &Request,
        socket: &mut T,
        existing_memory_files: HashMap<u32, File>,
    ) -> std::result::Result<Arc<Mutex<MemoryManager>>, MigratableError>
    where
        T: Read,
    {
        // Read in config data along with memory manager data
        let mut data: Vec<u8> = Vec::new();
        data.resize_with(req.length() as usize, Default::default);
        socket
            .read_exact(&mut data)
            .map_err(MigratableError::MigrateSocket)?;

        let vm_migration_config: VmMigrationConfig =
            serde_json::from_slice(&data).map_err(|e| {
                MigratableError::MigrateReceive(anyhow!("Error deserialising config: {e}"))
            })?;

        #[cfg(all(feature = "kvm", target_arch = "x86_64"))]
        self.vm_check_cpuid_compatibility(
            &vm_migration_config.vm_config,
            &vm_migration_config.common_cpuid,
        )?;

        let config = vm_migration_config.vm_config.clone();
        self.vm_config = Some(vm_migration_config.vm_config);
        self.console_info = Some(pre_create_console_devices(self).map_err(|e| {
            MigratableError::MigrateReceive(anyhow!("Error creating console devices: {e:?}"))
        })?);

        if self
            .vm_config
            .as_ref()
            .unwrap()
            .lock()
            .unwrap()
            .landlock_enable
        {
            let mut config = self.vm_config.as_ref().unwrap().lock().unwrap();
            apply_landlock(&mut config).map_err(|e| {
                MigratableError::MigrateReceive(anyhow!("Error applying landlock: {e:?}"))
            })?;
        }

        let vm = Vm::create_hypervisor_vm(
            self.hypervisor.as_ref(),
            (&*self.vm_config.as_ref().unwrap().lock().unwrap()).into(),
        )
        .map_err(|e| {
            MigratableError::MigrateReceive(anyhow!(
                "Error creating hypervisor VM from snapshot: {e:?}"
            ))
        })?;

        #[cfg(all(feature = "kvm", target_arch = "x86_64"))]
        if config.lock().unwrap().max_apic_id() > MAX_SUPPORTED_CPUS_LEGACY {
            vm.enable_x2apic_api().unwrap();
        }

        let phys_bits = vm::physical_bits(
            self.hypervisor.as_ref(),
            config.lock().unwrap().cpus.max_phys_bits,
        );

        let memory_manager = MemoryManager::new(
            vm,
            &config.lock().unwrap().memory.clone(),
            None,
            phys_bits,
            #[cfg(feature = "tdx")]
            false,
            Some(&vm_migration_config.memory_manager_data),
            existing_memory_files,
        )
        .map_err(|e| {
            MigratableError::MigrateReceive(anyhow!(
                "Error creating MemoryManager from snapshot: {e:?}"
            ))
        })?;

        Ok(memory_manager)
    }

    /// Receives the final VM state (devices, vCPUs) and restores the VM.
    ///
    /// Measures the time for each step.
    fn vm_receive_state<T>(
        &mut self,
        req: &Request,
        socket: &mut T,
        mm: Arc<Mutex<MemoryManager>>,
    ) -> std::result::Result<
        (
            Duration, /* state receive + deserialize */
            Duration, /* restoring */
        ),
        MigratableError,
    >
    where
        T: Read,
    {
        let (snapshot, receive_duration): (Snapshot, Duration) = measure_ok(|| {
            let mut data: Vec<u8> = Vec::new();
            data.resize_with(req.length() as usize, Default::default);
            socket
                .read_exact(&mut data)
                .map_err(MigratableError::MigrateSocket)?;
            serde_json::from_slice(&data).map_err(|e| {
                MigratableError::MigrateReceive(anyhow!("Error deserialising snapshot: {e}"))
            })
        })?;

        let exit_evt = self.exit_evt.try_clone().map_err(|e| {
            MigratableError::MigrateReceive(anyhow!("Error cloning exit EventFd: {e}"))
        })?;
        let reset_evt = self.reset_evt.try_clone().map_err(|e| {
            MigratableError::MigrateReceive(anyhow!("Error cloning reset EventFd: {e}"))
        })?;
        let guest_exit_evt = self.guest_exit_evt.try_clone().map_err(|e| {
            MigratableError::MigrateReceive(anyhow!("Error cloning guest exit EventFd: {e}"))
        })?;
        #[cfg(feature = "guest_debug")]
        let debug_evt = self.vm_debug_evt.try_clone().map_err(|e| {
            MigratableError::MigrateReceive(anyhow!("Error cloning debug EventFd: {e}"))
        })?;
        let activate_evt = self.activate_evt.try_clone().map_err(|e| {
            MigratableError::MigrateReceive(anyhow!("Error cloning activate EventFd: {e}"))
        })?;

        let (vm, restore_duration) = measure_ok(|| {
            #[cfg(not(target_arch = "riscv64"))]
            let timestamp = Instant::now();
            let hypervisor_vm = mm.lock().unwrap().vm.clone();

            let mut vm = Vm::new_from_memory_manager(
                self.vm_config.clone().unwrap(),
                mm,
                hypervisor_vm,
                exit_evt,
                reset_evt,
                guest_exit_evt,
                #[cfg(feature = "guest_debug")]
                debug_evt,
                &self.seccomp_action,
                self.hypervisor.clone(),
                activate_evt,
                #[cfg(not(target_arch = "riscv64"))]
                timestamp,
                self.console_info.clone(),
                self.console_resize_pipe.clone(),
                Arc::clone(&self.original_termios_opt),
                Some(&snapshot),
                #[cfg(feature = "igvm")]
                None,
            )
            .map_err(|e| {
                MigratableError::MigrateReceive(anyhow!("Error creating VM from snapshot: {e:?}"))
            })?;

            // Create VM
            vm.restore().map_err(|e| {
                MigratableError::MigrateReceive(anyhow!("Failed restoring the Vm: {e}"))
            })?;

            Ok(vm)
        })?;

        self.vm = VmOwnership::Owned(vm);

        Ok((receive_duration, restore_duration))
    }

    /// Performs the initial memory transmission (iteration zero) plus a
    /// variable number of memory iterations with the goal to eventually migrate
    /// the VM in a reasonably small downtime.
    ///
    /// This returns as soon as the precopy migration indicates it is converged
    /// (e.g., reasonably small downtime) is reached.
    fn do_memory_iterations(
        vm: &mut Vm,
        socket: &mut SocketStream,
        ctx: &mut MemoryMigrationContext,
        is_converged: impl Fn(&MemoryMigrationContext) -> result::Result<bool, MigratableError>,
        mem_send: &mut SendAdditionalConnections,
    ) -> result::Result<MemoryRangeTable /* remaining */, MigratableError> {
        loop {
            let iteration_begin = Instant::now();

            let iteration_table = if ctx.iteration == 0 {
                vm.memory_range_table()?
            } else {
                // TODO do this in a thread #7816
                vm.dirty_log()?
            };

            ctx.update_metrics_before_transfer(iteration_begin, &iteration_table);
            if is_converged(ctx)? {
                debug!("Precopy converged: {ctx}");
                break Ok(iteration_table);
            }

            // Send the current dirty pages
            let transfer_begin = Instant::now();
            mem_send.send_memory(iteration_table, socket)?;
            let transfer_duration = transfer_begin.elapsed();
            ctx.update_metrics_after_transfer(transfer_begin, transfer_duration);

            // Log progress of the current iteration
            debug!("Precopy: {ctx}");

            // Enables management software (e.g., libvirt) to easily track forward progress.
            event!(
                "vm",
                "migration-memory-iteration",
                "id",
                ctx.iteration.to_string()
            );

            // Increment iteration last: This way we ensure that the logging
            // above matches the actual iteration.
            ctx.iteration += 1;
        }
    }

    /// Checks whether the precopy memory migration has converged and it is safe
    /// to proceed to the final (paused) memory iteration.
    ///
    /// Once this returns, the VM is expected to stop as soon as possible.
    ///
    /// Convergence is reached when any of the following criteria is met:
    ///
    /// 1. **No dirty pages remain** – the current iteration would transfer zero
    ///    bytes.
    /// 2. **Downtime budget is met** – the estimated downtime for the final
    ///    (paused) iteration is within the caller-specified
    ///    [`VmSendMigrationData::downtime`] budget.
    /// 3. **Timeout** – the precopy phase has been running for at least
    ///    [`VmSendMigrationData::timeout`]. The outcome depends on
    ///    [`VmSendMigrationData::timeout_strategy`]:
    ///    - [`TimeoutStrategy::Cancel`] – returns
    ///    - [`TimeoutStrategy::Ignore`] – the migration completes despite not
    ///      meeting the downtime budget.
    ///      [`MigratableError::MigrateSend`] so the caller can abort the
    ///      migration cleanly.
    ///
    /// # Returns
    ///
    /// * `Ok(true)` – convergence criterion met; the caller should stop precopy
    ///   iterations.
    /// * `Ok(false)` – not yet converged; the caller should run another
    ///   dirty-page iteration.
    /// * `Err(_)` – the timeout was reached and [`TimeoutStrategy::Cancel`]
    ///   is in effect.
    fn is_precopy_converged(
        ctx: &MemoryMigrationContext,
        send_data_migration: &VmSendMigrationData,
    ) -> result::Result<bool, MigratableError> {
        if ctx.current_iteration_total_bytes == 0 {
            debug!("Precopy: No more memory to transfer");
            return Ok(true);
        }

        // We currently ignore the time required to transfer the final
        // VM state (device state and vCPUs) and the time needed on the
        // receiver to create the VM and initialize its data structures
        // before execution can resume.
        //
        // Manual testing showed that migrating an idle VM on a modern
        // AMD CPU (CHV release build) adds ~5 ms of overhead when
        // scaling from 1 to 200 vCPUs. Given this small cost, we
        // deliberately avoid additional heuristics to estimate the
        // downtime more precisely - for now. Instead, we approximate
        // the downtime just by the transfer time of the final memory
        // delta.
        if let Some(memory_downtime) = ctx.estimated_downtime
            && memory_downtime <= send_data_migration.downtime()
        {
            debug!(
                "Precopy: Target downtime can be met: {}ms <= {}ms",
                memory_downtime.as_millis(),
                send_data_migration.downtime().as_millis()
            );
            return Ok(true);
        }

        // We check the beginning of the precopy migration and not the overall migration, and
        // this is fine: precopy takes the longest and the earlier steps are negligible.
        if ctx.migration_begin.elapsed() >= send_data_migration.timeout() {
            return match send_data_migration.timeout_strategy {
                TimeoutStrategy::Cancel => {
                    let msg = format!(
                        "Precopy: Timeout reached: {}s: migration didn't converge in time",
                        send_data_migration.timeout().as_secs()
                    );
                    Err(MigratableError::MigrateSend(anyhow!("{msg}")))
                }
                TimeoutStrategy::Ignore => {
                    info!(
                        "Precopy: Pausing VM, ignoring target downtime ({}ms) due to timeout ({}s): Estimated downtime: {}ms",
                        send_data_migration.downtime().as_millis(),
                        send_data_migration.timeout().as_secs(),
                        ctx.estimated_downtime
                            .unwrap_or(Duration::from_secs(0))
                            .as_millis()
                    );
                    Ok(true)
                }
            };
        }

        Ok(false)
    }

    /// Performs the memory migration including multiple iterations.
    ///
    /// This includes:
    /// - initial memory - VM is running
    /// - multiple memory delta transmissions - VM is running
    /// - final memory iteration - VM is paused
    ///
    /// Stores the [finalized] [`MemoryMigrationContext`] in the provided
    /// [`OngoingMigrationContext`].
    ///
    /// [finalized]: MemoryMigrationContext::finalize
    fn do_memory_migration(
        vm: &mut Vm,
        socket: &mut SocketStream,
        send_data_migration: &VmSendMigrationData,
        mem_send: &mut SendAdditionalConnections,
        ctx: &mut OngoingMigrationContext,
    ) -> result::Result<(), MigratableError> {
        let mut mem_ctx = MemoryMigrationContext::new();

        vm.start_dirty_log()?;
        let remaining = Self::do_memory_iterations(
            vm,
            socket,
            &mut mem_ctx,
            // We bind send_data_migration to the callback
            |ctx| Self::is_precopy_converged(ctx, send_data_migration),
            mem_send,
        )?;
        let downtime_begin = Instant::now();
        if vm.get_state() != VmState::Paused {
            vm.pause()?;
        }

        // Send last batch of dirty pages: final iteration
        {
            let iteration_begin = Instant::now();

            let mut final_table = vm.dirty_log()?;
            final_table.extend(remaining);

            mem_ctx.update_metrics_before_transfer(iteration_begin, &final_table);
            let transfer_begin = Instant::now();
            mem_send.send_memory(final_table, socket)?;
            let transfer_duration = transfer_begin.elapsed();
            mem_ctx.update_metrics_after_transfer(transfer_begin, transfer_duration);
            mem_ctx.iteration += 1;
        }
        mem_ctx.finalize();
        info!("Precopy complete: {mem_ctx}");
        ctx.set_vm_paused(downtime_begin, mem_ctx)
            .expect("migration context should transition to VmPaused after memory migration");

        Ok(())
    }

    /// Performs a migration.
    ///
    /// Runs after-migration cleanup only on success. Callers must handle failed
    /// migrations.
    fn send_migration(
        vm: &mut Vm,
        #[cfg(all(feature = "kvm", target_arch = "x86_64"))]
        hypervisor: &dyn hypervisor::Hypervisor,
        send_data_migration: &VmSendMigrationData,
        initial_vm_state: VmState,
    ) -> result::Result<(), MigratableError> {
        // State machine that is updated with more context as we progress.
        let mut ctx = OngoingMigrationContext::new();

        // Set up the socket connection
        let mut socket = migration_transport::send_migration_socket(
            &send_data_migration.destination_url,
            send_data_migration.tls_dir.as_deref(),
        )?;

        // Start the migration
        migration_transport::send_request_expect_ok(
            &mut socket,
            Request::start(),
            MigratableError::MigrateSend(anyhow!("Error starting migration")),
        )?;
        debug!("Using migration protocol {CURRENT_PROTOCOL_VERSION}");

        // Send config
        let vm_config = vm.get_config();
        #[cfg(all(feature = "kvm", target_arch = "x86_64"))]
        let common_cpuid = {
            #[cfg(feature = "tdx")]
            if vm_config.lock().unwrap().is_tdx_enabled() {
                return Err(MigratableError::MigrateSend(anyhow!(
                    "Live Migration is not supported when TDX is enabled"
                )));
            }

            let (amx, max_phys_bits, profile, kvm_hyperv) = {
                let guard = vm_config.lock().unwrap();
                (
                    guard.cpus.features.amx,
                    guard.cpus.max_phys_bits,
                    guard.cpus.profile,
                    guard.cpus.kvm_hyperv,
                )
            };

            let phys_bits = vm::physical_bits(hypervisor, max_phys_bits);

            arch::generate_common_cpuid(
                hypervisor,
                &arch::CpuidConfig {
                    phys_bits,
                    kvm_hyperv,
                    #[cfg(feature = "tdx")]
                    tdx: false,
                    amx,
                    profile,
                },
            )
            .map_err(|e| {
                MigratableError::MigrateSend(anyhow!("Error generating common cpuid': {e:?}"))
            })?
        };

        if send_data_migration.local {
            match &mut socket {
                SocketStream::Unix(unix_socket) => {
                    // Proceed with sending memory file descriptors over UNIX socket
                    vm.send_memory_fds(unix_socket)?;
                }
                _ => {
                    return Err(MigratableError::MigrateSend(anyhow!(
                        "--local option is only supported with UNIX sockets",
                    )));
                }
            }
        }

        let vm_migration_config = VmMigrationConfig {
            vm_config,
            #[cfg(all(feature = "kvm", target_arch = "x86_64"))]
            common_cpuid,
            memory_manager_data: vm.memory_manager_data(),
        };
        migration_transport::send_config(&mut socket, &vm_migration_config)?;

        // Let every Migratable object know about the migration being started.
        vm.start_migration()?;

        if send_data_migration.local {
            // Now pause VM (skip if already paused, e.g. migrating a paused VM)
            let downtime_begin = Instant::now();
            if vm.get_state() != VmState::Paused {
                vm.pause()?;
            }
            ctx.set_vm_paused(
                downtime_begin,
                // No memory was transferred
                MemoryMigrationContext::empty_finalized(),
            )
            .expect("migration context should transition to VmPaused for local migration");
        } else {
            let mut mem_send = migration_transport::SendAdditionalConnections::new(
                &send_data_migration.destination_url,
                send_data_migration.connections,
                send_data_migration.tls_dir.as_deref(),
                &vm.guest_memory(),
            )?;

            Self::do_memory_migration(
                vm,
                &mut socket,
                send_data_migration,
                &mut mem_send,
                &mut ctx,
            )
            .inspect_err(|_| {
                // Calling cleanup multiple times is fine, thus here we just make sure
                // that it is called.
                if let Err(e) = mem_send.cleanup() {
                    warn!("Error cleaning up migration connections: {e}");
                }
            })?;

            mem_send.cleanup()?;
        }

        // We release the locks early to enable locking them on the destination host.
        // The VM is already stopped.
        vm.release_disk_locks()
            .map_err(|e| MigratableError::UnlockError(anyhow!("{e}")))?;

        let (vm_snapshot, snapshot_duration) = measure_ok(|| {
            // Capture snapshot. This may have side effects, e.g. vhost-user backend inflight drain
            let snapshot = vm.snapshot()?;

            // One final memory iteration to handle side effects from snapshot.
            if !send_data_migration.local {
                let memory_ranges = vm.dirty_log()?;
                migration_transport::send_memory_ranges(
                    &vm.guest_memory(),
                    &memory_ranges,
                    &mut socket,
                )?;
            }
            Ok(snapshot)
        })?;

        let (_, send_snapshot_duration) =
            measure_ok(|| migration_transport::send_state(&mut socket, &vm_snapshot))?;

        // Complete the migration.
        // When this returns, we know the VM was resumed (if it was running
        // before the migration) and that the receiving VMM acquired disk
        // locks again.
        let complete_req = if initial_vm_state == VmState::Running {
            Request::complete()
        } else {
            Request::complete_paused()
        };
        let (_, complete_duration) = measure_ok(|| {
            migration_transport::send_request_expect_ok(
                &mut socket,
                complete_req,
                MigratableError::MigrateSend(anyhow!("Error completing migration")),
            )
        })?;

        let ctx = ctx
            .finalize(snapshot_duration, send_snapshot_duration, complete_duration)
            .expect("migration context should finalize after memory migration completed");

        info!(
            "Migration completed after {:.1}s with a downtime of {}ms (goal was {}ms)",
            ctx.migration_dur.as_secs_f32(),
            ctx.downtime_ctx.effective_downtime.as_millis(),
            send_data_migration.downtime().as_millis()
        );
        debug!("Downtime breakdown: {}", ctx.downtime_ctx);

        // Stop logging dirty pages
        if !send_data_migration.local {
            vm.stop_dirty_log()?;
        }

        // Let every Migratable object know about the migration being complete
        vm.complete_migration()
    }

    #[cfg(all(feature = "kvm", target_arch = "x86_64"))]
    fn vm_check_cpuid_compatibility(
        &self,
        src_vm_config: &Arc<Mutex<VmConfig>>,
        src_vm_cpuid: &[hypervisor::arch::x86::CpuIdEntry],
    ) -> result::Result<(), MigratableError> {
        #[cfg(feature = "tdx")]
        if src_vm_config.lock().unwrap().is_tdx_enabled() {
            return Err(MigratableError::MigrateReceive(anyhow!(
                "Live Migration is not supported when TDX is enabled"
            )));
        }

        // We check the `CPUID` compatibility of between the source vm and destination, which is
        // mostly about feature compatibility.
        let dest_cpuid = &{
            let vm_config = &src_vm_config.lock().unwrap();

            if vm_config.cpus.features.amx {
                // Need to enable AMX tile state components before generating common cpuid
                // as this affects what Hypervisor::get_supported_cpuid returns.
                self.hypervisor
                    .enable_amx_state_components()
                    .map_err(|e| MigratableError::MigrateReceive(e.into()))?;
            }

            let phys_bits =
                vm::physical_bits(self.hypervisor.as_ref(), vm_config.cpus.max_phys_bits);

            arch::generate_common_cpuid(
                self.hypervisor.as_ref(),
                &arch::CpuidConfig {
                    phys_bits,
                    kvm_hyperv: vm_config.cpus.kvm_hyperv,
                    #[cfg(feature = "tdx")]
                    tdx: false,
                    amx: vm_config.cpus.features.amx,
                    profile: vm_config.cpus.profile,
                },
            )
            .map_err(|e| {
                MigratableError::MigrateReceive(anyhow!("Error generating common cpuid: {e:?}"))
            })?
        };
        arch::CpuidFeatureEntry::check_cpuid_compatibility(src_vm_cpuid, dest_cpuid).map_err(|e| {
            MigratableError::MigrateReceive(anyhow!(
                "Error checking cpu feature compatibility': {e:?}"
            ))
        })
    }

    fn vm_restore(
        &mut self,
        source_url: &str,
        vm_config: Arc<Mutex<VmConfig>>,
        prefault: bool,
        memory_restore_mode: MemoryRestoreMode,
    ) -> std::result::Result<(), VmError> {
        match &self.vm {
            VmOwnership::Owned(_) => Err(VmError::VmAlreadyCreated),
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                let snapshot = recv_vm_state(source_url).map_err(VmError::Restore)?;
                #[cfg(all(feature = "kvm", target_arch = "x86_64"))]
                let vm_snapshot = get_vm_snapshot(&snapshot).map_err(VmError::Restore)?;

                #[cfg(all(feature = "kvm", target_arch = "x86_64"))]
                self.vm_check_cpuid_compatibility(&vm_config, &vm_snapshot.common_cpuid)
                    .map_err(VmError::Restore)?;

                self.vm_config = Some(Arc::clone(&vm_config));

                // Always re-populate the 'console_info' based on the new 'vm_config'
                self.console_info =
                    Some(pre_create_console_devices(self).map_err(VmError::CreateConsoleDevices)?);

                let exit_evt = self.exit_evt.try_clone().map_err(VmError::EventFdClone)?;
                let reset_evt = self.reset_evt.try_clone().map_err(VmError::EventFdClone)?;
                let guest_exit_evt = self
                    .guest_exit_evt
                    .try_clone()
                    .map_err(VmError::EventFdClone)?;
                #[cfg(feature = "guest_debug")]
                let debug_evt = self
                    .vm_debug_evt
                    .try_clone()
                    .map_err(VmError::EventFdClone)?;
                let activate_evt = self
                    .activate_evt
                    .try_clone()
                    .map_err(VmError::EventFdClone)?;

                let mut vm = Vm::new(
                    vm_config,
                    exit_evt,
                    reset_evt,
                    guest_exit_evt,
                    #[cfg(feature = "guest_debug")]
                    debug_evt,
                    &self.seccomp_action,
                    self.hypervisor.clone(),
                    activate_evt,
                    self.console_info.clone(),
                    self.console_resize_pipe.clone(),
                    Arc::clone(&self.original_termios_opt),
                    Some(&snapshot),
                    Some(source_url),
                    Some(prefault),
                    Some(memory_restore_mode),
                )?;

                if self
                    .vm_config
                    .as_ref()
                    .unwrap()
                    .lock()
                    .unwrap()
                    .landlock_enable
                {
                    let mut config = self.vm_config.as_ref().unwrap().lock().unwrap();
                    apply_landlock(&mut config).map_err(VmError::ApplyLandlock)?;
                }

                // Now we can restore the rest of the VM.
                // PANIC: won't panic, we just checked that the VM is there.
                vm.restore()?;
                self.vm = VmOwnership::Owned(vm);
                Ok(())
            }
        }
    }

    /// Handles the outcome of the migration worker thread.
    fn check_migration(&mut self) {
        let VmOwnership::Migration {
            migration_worker_handle,
            ..
        } = mem::replace(&mut self.vm, VmOwnership::None)
        else {
            panic!("Should only be called after a migration was started");
        };
        let MigrationWorkerResult {
            vm,
            migration_result: migration_res,
            initial_vm_state,
        } = migration_worker_handle.join();

        let mut try_resume_vm_after_failed_migration = |mut vm: Vm| {
            // A late failure may leave the VM paused.
            if initial_vm_state == VmState::Running && vm.get_state() == VmState::Paused {
                match vm.resume() {
                    Ok(_) => {
                        info!("Resumed VM successfully after failed migration");
                    }
                    Err(e) => {
                        error!("Failed resuming VM after failed migration: {e}");
                        self.exit_evt.write(1).unwrap();
                    }
                }
            }

            // Ensure full VM performance. The operation is idempotent.
            let _ = vm.stop_dirty_log().inspect_err(|e| {
                warn!("Failed stopping dirty log after resuming VM: {e} - VM performance might be slower than usual");
            });

            self.vm = VmOwnership::Owned(vm);
        };

        match migration_res {
            Ok(()) => {
                self.vm = VmOwnership::None;
                let mut vm = vm;

                // Since the VMM explicitly no longer owns the VM, the exit
                // event won't call the shutdown path automatically.
                if let Err(e) = vm.shutdown() {
                    error!("Failed shutting down the VM after migration: {e}");
                }

                if let Err(e) = self.exit_evt.write(1) {
                    error!("Failed exiting the VMM after migration: {e}");
                }
            }
            Err(e) => {
                error!("Migration failed: {e}");
                try_resume_vm_after_failed_migration(vm);
            }
        }
    }

    fn control_loop(
        &mut self,
        api_receiver: &Receiver<ApiRequest>,
        #[cfg(feature = "guest_debug")] gdb_receiver: &Receiver<gdb::GdbRequest>,
    ) -> Result<()> {
        const EPOLL_EVENTS_LEN: usize = 100;

        let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
        let epoll_fd = self.epoll.as_raw_fd();

        'outer: loop {
            let num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) {
                Ok(res) => res,
                Err(e) => {
                    if e.kind() == io::ErrorKind::Interrupted {
                        // It's well defined from the epoll_wait() syscall
                        // documentation that the epoll loop can be interrupted
                        // before any of the requested events occurred or the
                        // timeout expired. In both those cases, epoll_wait()
                        // returns an error of type EINTR, but this should not
                        // be considered as a regular error. Instead it is more
                        // appropriate to retry, by calling into epoll_wait().
                        continue;
                    }
                    return Err(Error::Epoll(e));
                }
            };

            for event in events.iter().take(num_events) {
                let dispatch_event: EpollDispatch = event.data.into();
                match dispatch_event {
                    EpollDispatch::Unknown => {
                        let event = event.data;
                        warn!("Unknown VMM loop event: {event}");
                    }
                    EpollDispatch::Exit => {
                        info!("VM exit event");
                        // Consume the event.
                        self.exit_evt.read().map_err(Error::EventFdRead)?;
                        // TODO: Future follow-up must resolve lifecycle handling while migrating.
                        self.vmm_shutdown().map_err(Error::VmmShutdown)?;

                        break 'outer;
                    }
                    EpollDispatch::Reset => {
                        info!("VM reset event");
                        // Consume the event.
                        self.reset_evt.read().map_err(Error::EventFdRead)?;
                        // TODO: Future follow-up must resolve lifecycle handling while migrating.
                        self.vm_reboot().map_err(Error::VmReboot)?;
                    }
                    EpollDispatch::GuestExit => {
                        info!("VM guest exit event");
                        self.guest_exit_evt.read().map_err(Error::EventFdRead)?;
                        // TODO: Future follow-up must resolve lifecycle handling while migrating.
                        if self.no_shutdown {
                            self.vm_shutdown().map_err(Error::VmShutdown)?;
                        } else {
                            self.vmm_shutdown().map_err(Error::VmmShutdown)?;
                            break 'outer;
                        }
                    }
                    EpollDispatch::ActivateVirtioDevices => {
                        // TODO: Future follow-up must resolve virtio activation handling while migrating.
                        let count = self.activate_evt.read().map_err(Error::EventFdRead)?;
                        if let VmOwnership::Owned(ref vm) = self.vm {
                            info!("Trying to activate pending virtio devices: count = {count}");
                            vm.activate_virtio_devices()
                                .map_err(Error::ActivateVirtioDevices)?;
                        }
                    }
                    EpollDispatch::Api => {
                        // Consume the events.
                        for _ in 0..self.api_evt.read().map_err(Error::EventFdRead)? {
                            // Read from the API receiver channel
                            let api_request = api_receiver.recv().map_err(Error::ApiRequestRecv)?;

                            if api_request(self)? {
                                break 'outer;
                            }
                        }
                    }
                    #[cfg(feature = "guest_debug")]
                    EpollDispatch::Debug => {
                        // Consume the events.
                        for _ in 0..self.debug_evt.read().map_err(Error::EventFdRead)? {
                            // Read from the API receiver channel
                            let gdb_request = gdb_receiver.recv().map_err(Error::GdbRequestRecv)?;

                            let response = match self.vm {
                                VmOwnership::Owned(ref mut vm) => {
                                    vm.debug_request(&gdb_request.payload, gdb_request.cpu_id)
                                }
                                VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
                                VmOwnership::None => Err(VmError::VmNotRunning),
                            }
                            .map_err(gdb::Error::Vm);

                            gdb_request
                                .sender
                                .send(response)
                                .map_err(Error::GdbResponseSend)?;
                        }
                    }
                    #[cfg(not(feature = "guest_debug"))]
                    EpollDispatch::Debug => {}
                    EpollDispatch::CheckMigration => {
                        info!("VM check migration event");
                        // Consume the event.
                        self.check_migration_evt
                            .read()
                            .map_err(Error::EventFdRead)?;
                        self.check_migration();
                    }
                }
            }
        }

        // Trigger the termination of the signal_handler thread
        if let Some(signals) = self.signals.take() {
            signals.close();
        }

        // Wait for all the threads to finish
        for thread in self.threads.drain(..) {
            thread.join().map_err(Error::ThreadCleanup)?;
        }

        Ok(())
    }
}

fn apply_landlock(vm_config: &mut VmConfig) -> result::Result<(), LandlockError> {
    vm_config.apply_landlock()?;
    Ok(())
}

impl RequestHandler for Vmm {
    fn vm_create(&mut self, config: Box<VmConfig>) -> result::Result<(), VmError> {
        match &self.vm {
            VmOwnership::Migration { .. } => return Err(VmError::VmMigrating),
            VmOwnership::Owned(_) | VmOwnership::None => {}
        }

        // We only store the passed VM config.
        // The VM will be created when being asked to boot it.
        if self.vm_config.is_some() {
            return Err(VmError::VmAlreadyCreated);
        }

        self.vm_config = Some(Arc::new(Mutex::new(*config)));
        self.console_info =
            Some(pre_create_console_devices(self).map_err(VmError::CreateConsoleDevices)?);

        if self
            .vm_config
            .as_ref()
            .is_some_and(|config| config.lock().unwrap().landlock_enable)
        {
            let mut config = self.vm_config.as_ref().unwrap().lock().unwrap();
            apply_landlock(&mut config).map_err(VmError::ApplyLandlock)?;
        }
        Ok(())
    }

    fn vm_boot(&mut self) -> result::Result<(), VmError> {
        match &self.vm {
            VmOwnership::Owned(_) => Err(VmError::VmAlreadyCreated),
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                tracer::start();
                info!("Booting VM");
                event!("vm", "booting");

                let r = (|| {
                    trace_scoped!("vm_boot");
                    // If we don't have a config, we cannot boot a VM.
                    if self.vm_config.is_none() {
                        return Err(VmError::VmMissingConfig);
                    }

                    // console_info is set to None in vm_shutdown. re-populate here if empty
                    if self.console_info.is_none() {
                        self.console_info = Some(
                            pre_create_console_devices(self)
                                .map_err(VmError::CreateConsoleDevices)?,
                        );
                    }

                    // Create a new VM if we don't have one yet.
                    let exit_evt = self.exit_evt.try_clone().map_err(VmError::EventFdClone)?;
                    let reset_evt = self.reset_evt.try_clone().map_err(VmError::EventFdClone)?;
                    let guest_exit_evt = self
                        .guest_exit_evt
                        .try_clone()
                        .map_err(VmError::EventFdClone)?;
                    #[cfg(feature = "guest_debug")]
                    let vm_debug_evt = self
                        .vm_debug_evt
                        .try_clone()
                        .map_err(VmError::EventFdClone)?;
                    let activate_evt = self
                        .activate_evt
                        .try_clone()
                        .map_err(VmError::EventFdClone)?;

                    if let Some(ref vm_config) = self.vm_config {
                        let mut vm = Vm::new(
                            Arc::clone(vm_config),
                            exit_evt,
                            reset_evt,
                            guest_exit_evt,
                            #[cfg(feature = "guest_debug")]
                            vm_debug_evt,
                            &self.seccomp_action,
                            self.hypervisor.clone(),
                            activate_evt,
                            self.console_info.clone(),
                            self.console_resize_pipe.clone(),
                            Arc::clone(&self.original_termios_opt),
                            None,
                            None,
                            None,
                            None,
                        )?;

                        let r = vm.boot();
                        self.vm = VmOwnership::Owned(vm);
                        r
                    } else {
                        Err(VmError::VmNotCreated)
                    }
                })();

                tracer::end();
                if r.is_ok() {
                    event!("vm", "booted");
                }
                r
            }
        }
    }

    fn vm_pause(&mut self) -> result::Result<(), VmError> {
        match self.vm {
            VmOwnership::Owned(ref mut vm) => vm.pause().map_err(VmError::Pause),
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => Err(VmError::VmNotRunning),
        }
    }

    fn vm_resume(&mut self) -> result::Result<(), VmError> {
        match self.vm {
            VmOwnership::Owned(ref mut vm) => vm.resume().map_err(VmError::Resume),
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => Err(VmError::VmNotRunning),
        }
    }

    fn vm_snapshot(&mut self, destination_url: &str) -> result::Result<(), VmError> {
        match self.vm {
            VmOwnership::Owned(ref mut vm) => {
                // Drain console_info so that FDs are not reused
                let _ = self.console_info.take();
                vm.snapshot()
                    .map_err(VmError::Snapshot)
                    .and_then(|snapshot| {
                        vm.send(&snapshot, destination_url)
                            .map_err(VmError::SnapshotSend)
                    })
            }
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => Err(VmError::VmNotRunning),
        }
    }

    fn vm_restore(&mut self, restore_cfg: RestoreConfig) -> result::Result<(), VmError> {
        match &self.vm {
            VmOwnership::Owned(_) => Err(VmError::VmAlreadyCreated),
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                if self.vm_config.is_some() {
                    return Err(VmError::VmAlreadyCreated);
                }

                let source_url = restore_cfg.source_url.as_path().to_str();
                if source_url.is_none() {
                    return Err(VmError::InvalidRestoreSourceUrl);
                }
                // Safe to unwrap as we checked it was Some(&str).
                let source_url = source_url.unwrap();

                let vm_config = Arc::new(Mutex::new(
                    recv_vm_config(source_url).map_err(VmError::Restore)?,
                ));
                restore_cfg
                    .validate(&vm_config.lock().unwrap().clone())
                    .map_err(VmError::ConfigValidation)?;

                // Update VM's net configurations with new fds received for restore operation
                if let (Some(restored_nets), Some(vm_net_configs)) =
                    (restore_cfg.net_fds, &mut vm_config.lock().unwrap().net)
                {
                    for net in restored_nets.iter() {
                        for net_config in vm_net_configs.iter_mut() {
                            // update only if the net dev is backed by FDs
                            if net_config.pci_common.id.as_ref() == Some(&net.id)
                                && net_config.fds.is_some()
                            {
                                net_config.fds.clone_from(&net.fds);
                            }
                        }
                    }
                }

                self.vm_restore(
                    source_url,
                    vm_config,
                    restore_cfg.prefault,
                    restore_cfg.memory_restore_mode,
                )
                .and_then(|()| {
                    if restore_cfg.resume {
                        self.vm_resume()
                    } else {
                        Ok(())
                    }
                })
                .map_err(|e| {
                    error!("VM Restore failed: {e:?}");
                    if let Err(e) = self.vm_delete() {
                        return e;
                    }
                    e
                })?;

                Ok(())
            }
        }
    }

    #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
    fn vm_coredump(&mut self, destination_url: &str) -> result::Result<(), VmError> {
        match self.vm {
            VmOwnership::Owned(ref mut vm) => {
                vm.coredump(destination_url).map_err(VmError::Coredump)
            }
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => Err(VmError::VmNotRunning),
        }
    }

    fn vm_shutdown(&mut self) -> result::Result<(), VmError> {
        let mut vm = self.vm.take_owned_or(VmError::VmNotRunning)?;
        // Drain console_info so that the FDs are not reused
        let _ = self.console_info.take();
        let r = vm.shutdown();

        if r.is_ok() {
            event!("vm", "shutdown");
        }

        r
    }

    fn vm_reboot(&mut self) -> result::Result<(), VmError> {
        event!("vm", "rebooting");

        // Drop VM early to release disk locks and free other resources before
        // we reboot.
        let config = {
            let mut vm = self.vm.take_owned_or(VmError::VmNotCreated)?;
            let config = vm.get_config();
            // First we stop the current VM
            vm.shutdown()?;
            config
        };

        // vm.shutdown() closes all the console devices, so set console_info to None
        // so that the closed FD #s are not reused.
        let _ = self.console_info.take();

        let exit_evt = self.exit_evt.try_clone().map_err(VmError::EventFdClone)?;
        let reset_evt = self.reset_evt.try_clone().map_err(VmError::EventFdClone)?;
        let guest_exit_evt = self
            .guest_exit_evt
            .try_clone()
            .map_err(VmError::EventFdClone)?;
        #[cfg(feature = "guest_debug")]
        let debug_evt = self
            .vm_debug_evt
            .try_clone()
            .map_err(VmError::EventFdClone)?;
        let activate_evt = self
            .activate_evt
            .try_clone()
            .map_err(VmError::EventFdClone)?;

        // The Linux kernel fires off an i8042 reset after doing the ACPI reset so there may be
        // an event sitting in the shared reset_evt. Without doing this we get very early reboots
        // during the boot process.
        if self.reset_evt.read().is_ok() {
            warn!("Spurious second reset event received. Ignoring.");
        }

        self.console_info =
            Some(pre_create_console_devices(self).map_err(VmError::CreateConsoleDevices)?);

        // Then we create the new VM
        let mut vm = Vm::new(
            config,
            exit_evt,
            reset_evt,
            guest_exit_evt,
            #[cfg(feature = "guest_debug")]
            debug_evt,
            &self.seccomp_action,
            self.hypervisor.clone(),
            activate_evt,
            self.console_info.clone(),
            self.console_resize_pipe.clone(),
            Arc::clone(&self.original_termios_opt),
            None,
            None,
            None,
            None,
        )?;

        // And we boot it
        vm.boot()?;

        self.vm = VmOwnership::Owned(vm);

        event!("vm", "rebooted");

        Ok(())
    }

    fn vm_info(&self) -> result::Result<VmInfoResponse, VmError> {
        // In case of a migration, we emit the old VM info, as the VM is
        // immutable during a migration.
        if let VmOwnership::Migration {
            vm_info_response, ..
        } = &self.vm
        {
            return Ok(vm_info_response.clone());
        }

        let vm_config = self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?;
        let vm_config = vm_config.lock().unwrap().clone();

        let state = match &self.vm {
            VmOwnership::Owned(vm) => vm.get_state(),
            VmOwnership::None => VmState::Created,
            VmOwnership::Migration { .. } => unreachable!("migration path is handled above"),
        };

        let base_memory_actual_size =
            vm_config.memory.total_size() - vm_config.memory.hotplugged_size();
        let (memory_actual_size, device_tree) = match &self.vm {
            VmOwnership::Owned(vm) => (
                base_memory_actual_size.saturating_sub(vm.balloon_size())
                    + vm.virtio_mem_plugged_size(),
                Some(vm.device_tree().lock().unwrap().clone()),
            ),
            VmOwnership::None => (base_memory_actual_size, None),
            VmOwnership::Migration { .. } => unreachable!("migration path is handled above"),
        };

        Ok(VmInfoResponse {
            config: Box::new(vm_config),
            state,
            memory_actual_size,
            device_tree,
        })
    }

    fn vmm_ping(&self) -> VmmPingResponse {
        let VmmVersionInfo {
            build_version,
            version,
        } = self.version.clone();

        VmmPingResponse {
            build_version,
            version,
            pid: std::process::id() as i64,
            features: feature_list(),
        }
    }

    fn vm_delete(&mut self) -> result::Result<(), VmError> {
        if self.vm_config.is_none() {
            return Ok(());
        }

        match &self.vm {
            VmOwnership::Owned(_vm) => {
                // If a VM is booted, we first try to shut it down.
                self.vm_shutdown()?;
            }
            VmOwnership::Migration { .. } => return Err(VmError::VmMigrating),
            VmOwnership::None => {}
        }

        self.vm_config = None;
        event!("vm", "deleted");

        Ok(())
    }

    fn vmm_shutdown(&mut self) -> result::Result<(), VmError> {
        self.vm_delete()?;
        event!("vmm", "shutdown");
        Ok(())
    }

    fn vm_resize(
        &mut self,
        desired_vcpus: Option<u32>,
        desired_ram: Option<u64>,
        desired_balloon: Option<u64>,
    ) -> result::Result<(), VmError> {
        self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?;

        match self.vm {
            VmOwnership::Owned(ref mut vm) => vm
                .resize(desired_vcpus, desired_ram, desired_balloon)
                .inspect_err(|e| error!("Error when resizing VM: {e:?}")),
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                let mut config = self.vm_config.as_ref().unwrap().lock().unwrap();
                if let Some(desired_vcpus) = desired_vcpus {
                    config.cpus.boot_vcpus = desired_vcpus;
                }
                if let Some(desired_ram) = desired_ram {
                    config.memory.size = desired_ram;
                }
                if let Some(desired_balloon) = desired_balloon
                    && let Some(balloon_config) = &mut config.balloon
                {
                    balloon_config.size = desired_balloon;
                }

                Ok(())
            }
        }
    }

    fn vm_resize_disk(&mut self, id: String, desired_size: u64) -> result::Result<(), VmError> {
        self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?;

        match self.vm {
            VmOwnership::Owned(ref mut vm) => vm.resize_disk(&id, desired_size),
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => Err(VmError::ResizeDisk),
        }
    }

    fn vm_resize_zone(&mut self, id: String, desired_ram: u64) -> result::Result<(), VmError> {
        self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?;

        match self.vm {
            VmOwnership::Owned(ref mut vm) => {
                vm.resize_zone(&id, desired_ram)
                    .inspect_err(|e| error!("Error when resizing zone: {e:?}"))?;
                Ok(())
            }
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                // Update VmConfig by setting the new desired ram.
                let memory_config = &mut self.vm_config.as_ref().unwrap().lock().unwrap().memory;

                if let Some(zones) = &mut memory_config.zones {
                    for zone in zones.iter_mut() {
                        if zone.id == id {
                            zone.size = desired_ram;
                            return Ok(());
                        }
                    }
                }

                error!("Could not find the memory zone {id} for the resize");
                Err(VmError::ResizeZone)
            }
        }
    }

    fn vm_add_device(
        &mut self,
        device_cfg: DeviceConfig,
    ) -> result::Result<Option<Vec<u8>>, VmError> {
        self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?;

        {
            // Validate the configuration change in a cloned configuration
            let mut config = self.vm_config.as_ref().unwrap().lock().unwrap().clone();
            add_to_config(&mut config.devices, device_cfg.clone());
            config.validate().map_err(VmError::ConfigValidation)?;
        }

        match self.vm {
            VmOwnership::Owned(ref mut vm) => {
                let info = vm.add_device(device_cfg).inspect_err(|e| {
                    error!("Error when adding new device to the VM: {e:?}");
                })?;
                serde_json::to_vec(&info)
                    .map(Some)
                    .map_err(VmError::SerializeJson)
            }
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                // Update VmConfig by adding the new device.
                let mut config = self.vm_config.as_ref().unwrap().lock().unwrap();
                add_to_config(&mut config.devices, device_cfg);
                Ok(None)
            }
        }
    }

    fn vm_add_user_device(
        &mut self,
        device_cfg: UserDeviceConfig,
    ) -> result::Result<Option<Vec<u8>>, VmError> {
        self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?;

        {
            // Validate the configuration change in a cloned configuration
            let mut config = self.vm_config.as_ref().unwrap().lock().unwrap().clone();
            add_to_config(&mut config.user_devices, device_cfg.clone());
            config.validate().map_err(VmError::ConfigValidation)?;
        }

        match self.vm {
            VmOwnership::Owned(ref mut vm) => {
                let info = vm.add_user_device(device_cfg).inspect_err(|e| {
                    error!("Error when adding new user device to the VM: {e:?}");
                })?;
                serde_json::to_vec(&info)
                    .map(Some)
                    .map_err(VmError::SerializeJson)
            }
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                // Update VmConfig by adding the new device.
                let mut config = self.vm_config.as_ref().unwrap().lock().unwrap();
                add_to_config(&mut config.user_devices, device_cfg);
                Ok(None)
            }
        }
    }

    fn vm_remove_device(&mut self, id: String) -> result::Result<(), VmError> {
        match self.vm {
            VmOwnership::Owned(ref mut vm) => {
                vm.remove_device(&id)
                    .inspect_err(|e| error!("Error when removing device from the VM: {e:?}"))?;
                Ok(())
            }
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                if let Some(ref config) = self.vm_config {
                    let mut config = config.lock().unwrap();
                    if config.remove_device(&id) {
                        Ok(())
                    } else {
                        Err(VmError::NoDeviceToRemove(id))
                    }
                } else {
                    Err(VmError::VmNotCreated)
                }
            }
        }
    }

    fn vm_add_disk(&mut self, disk_cfg: DiskConfig) -> result::Result<Option<Vec<u8>>, VmError> {
        self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?;

        {
            // Validate the configuration change in a cloned configuration
            let mut config = self.vm_config.as_ref().unwrap().lock().unwrap().clone();
            add_to_config(&mut config.disks, disk_cfg.clone());
            config.validate().map_err(VmError::ConfigValidation)?;
        }

        match self.vm {
            VmOwnership::Owned(ref mut vm) => {
                let info = vm.add_disk(disk_cfg).inspect_err(|e| {
                    error!("Error when adding new disk to the VM: {e:?}");
                })?;
                serde_json::to_vec(&info)
                    .map(Some)
                    .map_err(VmError::SerializeJson)
            }
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                // Update VmConfig by adding the new device.
                let mut config = self.vm_config.as_ref().unwrap().lock().unwrap();
                add_to_config(&mut config.disks, disk_cfg);
                Ok(None)
            }
        }
    }

    fn vm_add_fs(&mut self, fs_cfg: FsConfig) -> result::Result<Option<Vec<u8>>, VmError> {
        self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?;

        {
            // Validate the configuration change in a cloned configuration
            let mut config = self.vm_config.as_ref().unwrap().lock().unwrap().clone();
            add_to_config(&mut config.fs, fs_cfg.clone());
            config.validate().map_err(VmError::ConfigValidation)?;
        }

        match self.vm {
            VmOwnership::Owned(ref mut vm) => {
                let info = vm.add_fs(fs_cfg).inspect_err(|e| {
                    error!("Error when adding new fs to the VM: {e:?}");
                })?;
                serde_json::to_vec(&info)
                    .map(Some)
                    .map_err(VmError::SerializeJson)
            }
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                // Update VmConfig by adding the new device.
                let mut config = self.vm_config.as_ref().unwrap().lock().unwrap();
                add_to_config(&mut config.fs, fs_cfg);
                Ok(None)
            }
        }
    }

    fn vm_add_generic_vhost_user(
        &mut self,
        generic_vhost_user_cfg: GenericVhostUserConfig,
    ) -> result::Result<Option<Vec<u8>>, VmError> {
        self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?;

        {
            // Validate the configuration change in a cloned configuration
            let mut config = self.vm_config.as_ref().unwrap().lock().unwrap().clone();
            add_to_config(
                &mut config.generic_vhost_user,
                generic_vhost_user_cfg.clone(),
            );
            config.validate().map_err(VmError::ConfigValidation)?;
        }

        match self.vm {
            VmOwnership::Owned(ref mut vm) => {
                let info = vm
                    .add_generic_vhost_user(generic_vhost_user_cfg)
                    .inspect_err(|e| {
                        error!("Error when adding new generic vhost-user device to the VM: {e:?}");
                    })?;
                serde_json::to_vec(&info)
                    .map(Some)
                    .map_err(VmError::SerializeJson)
            }
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                // Update VmConfig by adding the new device.
                let mut config = self.vm_config.as_ref().unwrap().lock().unwrap();
                add_to_config(&mut config.generic_vhost_user, generic_vhost_user_cfg);
                Ok(None)
            }
        }
    }

    fn vm_add_pmem(&mut self, pmem_cfg: PmemConfig) -> result::Result<Option<Vec<u8>>, VmError> {
        self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?;

        {
            // Validate the configuration change in a cloned configuration
            let mut config = self.vm_config.as_ref().unwrap().lock().unwrap().clone();
            add_to_config(&mut config.pmem, pmem_cfg.clone());
            config.validate().map_err(VmError::ConfigValidation)?;
        }

        match self.vm {
            VmOwnership::Owned(ref mut vm) => {
                let info = vm.add_pmem(pmem_cfg).inspect_err(|e| {
                    error!("Error when adding new pmem device to the VM: {e:?}");
                })?;
                serde_json::to_vec(&info)
                    .map(Some)
                    .map_err(VmError::SerializeJson)
            }
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                // Update VmConfig by adding the new device.
                let mut config = self.vm_config.as_ref().unwrap().lock().unwrap();
                add_to_config(&mut config.pmem, pmem_cfg);
                Ok(None)
            }
        }
    }

    fn vm_add_net(&mut self, net_cfg: NetConfig) -> result::Result<Option<Vec<u8>>, VmError> {
        self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?;

        {
            // Validate the configuration change in a cloned configuration
            let mut config = self.vm_config.as_ref().unwrap().lock().unwrap().clone();
            add_to_config(&mut config.net, net_cfg.clone());
            config.validate().map_err(VmError::ConfigValidation)?;
        }

        match self.vm {
            VmOwnership::Owned(ref mut vm) => {
                let info = vm.add_net(net_cfg).inspect_err(|e| {
                    error!("Error when adding new network device to the VM: {e:?}");
                })?;
                serde_json::to_vec(&info)
                    .map(Some)
                    .map_err(VmError::SerializeJson)
            }
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                // Update VmConfig by adding the new device.
                let mut config = self.vm_config.as_ref().unwrap().lock().unwrap();
                add_to_config(&mut config.net, net_cfg);
                Ok(None)
            }
        }
    }

    fn vm_add_vdpa(&mut self, vdpa_cfg: VdpaConfig) -> result::Result<Option<Vec<u8>>, VmError> {
        self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?;

        {
            // Validate the configuration change in a cloned configuration
            let mut config = self.vm_config.as_ref().unwrap().lock().unwrap().clone();
            add_to_config(&mut config.vdpa, vdpa_cfg.clone());
            config.validate().map_err(VmError::ConfigValidation)?;
        }

        match self.vm {
            VmOwnership::Owned(ref mut vm) => {
                let info = vm.add_vdpa(vdpa_cfg).inspect_err(|e| {
                    error!("Error when adding new vDPA device to the VM: {e:?}");
                })?;
                serde_json::to_vec(&info)
                    .map(Some)
                    .map_err(VmError::SerializeJson)
            }
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                // Update VmConfig by adding the new device.
                let mut config = self.vm_config.as_ref().unwrap().lock().unwrap();
                add_to_config(&mut config.vdpa, vdpa_cfg);
                Ok(None)
            }
        }
    }

    fn vm_add_vsock(&mut self, vsock_cfg: VsockConfig) -> result::Result<Option<Vec<u8>>, VmError> {
        self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?;

        {
            // Validate the configuration change in a cloned configuration
            let mut config = self.vm_config.as_ref().unwrap().lock().unwrap().clone();

            if config.vsock.is_some() {
                return Err(VmError::TooManyVsockDevices);
            }

            config.vsock = Some(vsock_cfg.clone());
            config.validate().map_err(VmError::ConfigValidation)?;
        }

        match self.vm {
            VmOwnership::Owned(ref mut vm) => {
                let info = vm.add_vsock(vsock_cfg).inspect_err(|e| {
                    error!("Error when adding new vsock device to the VM: {e:?}");
                })?;
                serde_json::to_vec(&info)
                    .map(Some)
                    .map_err(VmError::SerializeJson)
            }
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => {
                // Update VmConfig by adding the new device.
                let mut config = self.vm_config.as_ref().unwrap().lock().unwrap();
                config.vsock = Some(vsock_cfg);
                Ok(None)
            }
        }
    }

    fn vm_counters(&mut self) -> result::Result<Option<Vec<u8>>, VmError> {
        match self.vm {
            VmOwnership::Owned(ref mut vm) => {
                let info = vm.counters().inspect_err(|e| {
                    error!("Error when getting counters from the VM: {e:?}");
                })?;
                serde_json::to_vec(&info)
                    .map(Some)
                    .map_err(VmError::SerializeJson)
            }
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => Err(VmError::VmNotRunning),
        }
    }

    fn vm_power_button(&mut self) -> result::Result<(), VmError> {
        match self.vm {
            VmOwnership::Owned(ref mut vm) => vm.power_button(),
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => Err(VmError::VmNotRunning),
        }
    }

    fn vm_nmi(&mut self) -> result::Result<(), VmError> {
        match self.vm {
            VmOwnership::Owned(ref mut vm) => vm.nmi(),
            VmOwnership::Migration { .. } => Err(VmError::VmMigrating),
            VmOwnership::None => Err(VmError::VmNotRunning),
        }
    }

    fn vm_receive_migration(
        &mut self,
        receive_data_migration: VmReceiveMigrationData,
    ) -> result::Result<(), MigratableError> {
        match &self.vm {
            VmOwnership::Owned(_vm) => {
                return Err(MigratableError::MigrateReceive(anyhow!(
                    "Can't receive a migration when a VM is already created"
                )));
            }
            VmOwnership::Migration { .. } => {
                return Err(MigratableError::MigrateReceive(anyhow!(
                    "There is already an ongoing migration"
                )));
            }
            VmOwnership::None => {}
        }

        receive_data_migration
            .validate()
            .context("Invalid receive migration configuration")
            .map_err(MigratableError::MigrateReceive)?;

        info!(
            "Receiving migration: receiver_url={},tls={}",
            receive_data_migration.receiver_url,
            receive_data_migration.tls_dir.is_some()
        );

        let mut listener = migration_transport::receive_migration_listener(
            &receive_data_migration.receiver_url,
            receive_data_migration.tls_dir.as_deref(),
        )?;

        if self.vm_config.is_some() {
            warn!("The existing VM config will be overwritten");
        }

        event!("vm", "migration-receive-ready");

        // Accept the connection and get the socket
        let mut socket = listener.accept()?;

        event!("vm", "migration-receive-started");

        let mut state = ReceiveMigrationState::Established;

        while !state.finished() {
            let req = Request::read_from(&mut socket)?;
            trace!("Command {:?} received", req.command());

            let (response, new_state) = match self.vm_receive_migration_step(
                &mut socket,
                &listener,
                state,
                &req,
                &receive_data_migration,
            ) {
                Ok(next_state) => (Response::ok(), next_state),
                Err(err) => {
                    warn!(
                        "Migration aborted as migration command {:?} failed: {}",
                        req.command(),
                        err
                    );
                    (Response::error(), ReceiveMigrationState::Aborted)
                }
            };

            state = new_state;
            assert_eq!(response.length(), 0);
            response.write_to(&mut socket)?;
        }

        if let ReceiveMigrationState::Aborted = state {
            event!("vm", "migration-receive-failed");
            self.vm = VmOwnership::None;
            self.vm_config = None;
        } else {
            event!("vm", "migration-receive-finished");
        }

        Ok(())
    }

    /// Dispatches a migration.
    ///
    /// Returns an error if the migration worker cannot be spawned. Once
    /// spawned, [`Vmm::check_migration`] will be called after the thread exits
    /// (on success, cancellation, or failure).
    fn vm_send_migration(
        &mut self,
        send_data_migration: VmSendMigrationData,
    ) -> result::Result<(), MigratableError> {
        match self.vm {
            VmOwnership::Owned(_) => (),
            VmOwnership::Migration { .. } => {
                return Err(MigratableError::MigrateSend(anyhow!(
                    "There is already an ongoing migration"
                )));
            }
            VmOwnership::None => {
                return Err(MigratableError::MigrateSend(anyhow!("VM is not running")));
            }
        }

        send_data_migration
            .validate()
            .context("Invalid send migration configuration")
            .map_err(MigratableError::MigrateSend)?;

        info!(
            "Sending migration: destination_url={},local={},tls={},downtime={}ms,timeout={}s,timeout_strategy={:?}",
            send_data_migration.destination_url,
            send_data_migration.local,
            send_data_migration.tls_dir.is_some(),
            send_data_migration.downtime().as_millis(),
            send_data_migration.timeout().as_secs(),
            send_data_migration.timeout_strategy
        );

        if !self
            .vm_config
            .as_ref()
            .unwrap()
            .lock()
            .unwrap()
            .backed_by_shared_memory()
            && send_data_migration.local
        {
            return Err(MigratableError::MigrateSend(anyhow!(
                "Local migration requires shared memory or hugepages enabled"
            )));
        }

        let vm = self
            .vm
            .as_mut()
            .ok_or_else(|| MigratableError::MigrateSend(anyhow!("VM is not running")))?;

        let initial_vm_state = vm.get_state();
        if initial_vm_state != VmState::Running && initial_vm_state != VmState::Paused {
            return Err(MigratableError::MigrateSend(anyhow!(
                "VM is not running or paused: {initial_vm_state:?}"
            )));
        }

        let vm_info_snapshot = self.vm_info().map_err(|e| {
            MigratableError::MigrateSend(anyhow!("Failed to query VM info snapshot: {e}"))
        })?;

        let check_migration_evt = self
            .check_migration_evt
            .try_clone()
            .with_context(|| "Failed to clone check_migration_evt FD")
            .map_err(MigratableError::MigrateSend)?;

        // Take VM ownership. This also means that API events can no longer
        // change the VM (e.g. net device hotplug).
        let vm = self
            .vm
            .take_owned_or(VmError::VmNotRunning)
            .expect("should have VM ownership as we just checked it");

        match MigrationWorker::spawn(
            vm,
            check_migration_evt,
            send_data_migration,
            #[cfg(all(feature = "kvm", target_arch = "x86_64"))]
            self.hypervisor.clone(),
            initial_vm_state,
        ) {
            Ok(handle) => {
                self.vm = VmOwnership::Migration {
                    migration_worker_handle: handle,
                    vm_info_response: vm_info_snapshot,
                };
                Ok(())
            }
            Err(e) => {
                self.vm = VmOwnership::Owned(e.vm);
                Err(MigratableError::MigrateSend(e.spawn_error.into()))
            }
        }
    }
}

const CPU_MANAGER_SNAPSHOT_ID: &str = "cpu-manager";
const MEMORY_MANAGER_SNAPSHOT_ID: &str = "memory-manager";
const DEVICE_MANAGER_SNAPSHOT_ID: &str = "device-manager";

#[cfg(test)]
mod unit_tests {
    use std::path::PathBuf;

    use arch::CpuProfile;

    use super::*;
    #[cfg(target_arch = "x86_64")]
    use crate::vm_config::DebugConsoleConfig;
    use crate::vm_config::{
        CommonConsoleConfig, ConsoleConfig, ConsoleOutputMode, CoreScheduling, CpuFeatures,
        CpusConfig, HotplugMethod, MemoryConfig, PayloadConfig, PciDeviceCommonConfig, RngConfig,
        SerialConfig,
    };

    fn create_dummy_vmm() -> Vmm {
        Vmm::new(
            VmmVersionInfo::new("dummy", "dummy"),
            EventFd::new(EFD_NONBLOCK).unwrap(),
            #[cfg(feature = "guest_debug")]
            EventFd::new(EFD_NONBLOCK).unwrap(),
            #[cfg(feature = "guest_debug")]
            EventFd::new(EFD_NONBLOCK).unwrap(),
            SeccompAction::Allow,
            hypervisor::new().unwrap(),
            EventFd::new(EFD_NONBLOCK).unwrap(),
            false,
        )
        .unwrap()
    }

    fn create_dummy_vm_config() -> Box<VmConfig> {
        Box::new(VmConfig {
            cpus: CpusConfig {
                boot_vcpus: 1,
                max_vcpus: 1,
                topology: None,
                kvm_hyperv: false,
                max_phys_bits: 46,
                affinity: None,
                features: CpuFeatures::default(),
                nested: true,
                core_scheduling: CoreScheduling::default(),
                profile: CpuProfile::default(),
            },
            memory: MemoryConfig {
                size: 536_870_912,
                mergeable: false,
                hotplug_method: HotplugMethod::Acpi,
                hotplug_size: None,
                hotplugged_size: None,
                shared: true,
                hugepages: false,
                hugepage_size: None,
                prefault: false,
                reserve: false,
                zones: None,
                thp: true,
            },
            payload: Some(PayloadConfig {
                kernel: Some(PathBuf::from("/path/to/kernel")),
                firmware: None,
                cmdline: None,
                initramfs: None,
                #[cfg(feature = "igvm")]
                igvm: None,
                #[cfg(feature = "sev_snp")]
                host_data: None,
                #[cfg(feature = "fw_cfg")]
                fw_cfg_config: None,
            }),
            rate_limit_groups: None,
            disks: None,
            net: None,
            rng: RngConfig {
                src: PathBuf::from("/dev/urandom"),
                pci_common: PciDeviceCommonConfig::default(),
            },
            balloon: None,
            fs: None,
            generic_vhost_user: None,
            pmem: None,
            serial: SerialConfig {
                common: CommonConsoleConfig {
                    file: None,
                    mode: ConsoleOutputMode::Null,
                    socket: None,
                },
            },
            console: ConsoleConfig {
                common: CommonConsoleConfig {
                    file: None,
                    // Caution: Don't use `Tty` to not mess with users terminal
                    mode: ConsoleOutputMode::Off,
                    socket: None,
                },
                pci_common: PciDeviceCommonConfig::default(),
            },
            #[cfg(target_arch = "x86_64")]
            debug_console: DebugConsoleConfig::default(),
            devices: None,
            user_devices: None,
            vdpa: None,
            vsock: None,
            #[cfg(feature = "pvmemcontrol")]
            pvmemcontrol: None,
            pvpanic: false,
            iommu: false,
            numa: None,
            watchdog: false,
            rtc: None,
            #[cfg(feature = "guest_debug")]
            gdb: false,
            pci_segments: None,
            platform: None,
            tpm: None,
            preserved_fds: None,
            landlock_enable: false,
            landlock_rules: None,
            #[cfg(feature = "ivshmem")]
            ivshmem: None,
        })
    }

    #[test]
    fn test_vmm_vm_create() {
        let mut vmm = create_dummy_vmm();
        let config = create_dummy_vm_config();

        assert!(matches!(vmm.vm_create(config.clone()), Ok(())));
        assert!(matches!(
            vmm.vm_create(config),
            Err(VmError::VmAlreadyCreated)
        ));
    }

    #[test]
    fn test_vmm_vm_cold_add_device() {
        let mut vmm = create_dummy_vmm();
        let device_config = DeviceConfig::parse("path=/path/to/device").unwrap();

        assert!(matches!(
            vmm.vm_add_device(device_config.clone()),
            Err(VmError::VmNotCreated)
        ));

        let _ = vmm.vm_create(create_dummy_vm_config());
        assert!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .devices
                .is_none()
        );

        assert!(vmm.vm_add_device(device_config.clone()).unwrap().is_none());
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .devices
                .clone()
                .unwrap()
                .len(),
            1
        );
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .devices
                .clone()
                .unwrap()[0],
            device_config
        );
    }

    #[test]
    fn test_vmm_vm_cold_add_user_device() {
        let mut vmm = create_dummy_vmm();
        let user_device_config = UserDeviceConfig::parse("socket=/path/to/socket,id=8").unwrap();

        assert!(matches!(
            vmm.vm_add_user_device(user_device_config.clone()),
            Err(VmError::VmNotCreated)
        ));

        let _ = vmm.vm_create(create_dummy_vm_config());
        assert!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .user_devices
                .is_none()
        );

        assert!(
            vmm.vm_add_user_device(user_device_config.clone())
                .unwrap()
                .is_none()
        );
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .user_devices
                .clone()
                .unwrap()
                .len(),
            1
        );
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .user_devices
                .clone()
                .unwrap()[0],
            user_device_config
        );
    }

    #[test]
    fn test_vmm_vm_cold_add_disk() {
        let mut vmm = create_dummy_vmm();
        let disk_config = DiskConfig::parse("path=/path/to_file").unwrap();

        assert!(matches!(
            vmm.vm_add_disk(disk_config.clone()),
            Err(VmError::VmNotCreated)
        ));

        let _ = vmm.vm_create(create_dummy_vm_config());
        assert!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .disks
                .is_none()
        );

        assert!(vmm.vm_add_disk(disk_config.clone()).unwrap().is_none());
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .disks
                .clone()
                .unwrap()
                .len(),
            1
        );
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .disks
                .clone()
                .unwrap()[0],
            disk_config
        );
    }

    #[test]
    fn test_vmm_vm_cold_add_fs() {
        let mut vmm = create_dummy_vmm();
        let fs_config = FsConfig::parse("tag=mytag,socket=/tmp/sock").unwrap();

        assert!(matches!(
            vmm.vm_add_fs(fs_config.clone()),
            Err(VmError::VmNotCreated)
        ));

        let _ = vmm.vm_create(create_dummy_vm_config());
        assert!(vmm.vm_config.as_ref().unwrap().lock().unwrap().fs.is_none());

        assert!(vmm.vm_add_fs(fs_config.clone()).unwrap().is_none());
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .fs
                .clone()
                .unwrap()
                .len(),
            1
        );
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .fs
                .clone()
                .unwrap()[0],
            fs_config
        );
    }

    #[test]
    fn test_vmm_vm_cold_add_generic_vhost_user() {
        let mut vmm = create_dummy_vmm();
        let generic_vhost_user_config =
            GenericVhostUserConfig::parse("virtio_id=26,socket=/tmp/sock,queue_sizes=[1024]")
                .unwrap();

        assert!(matches!(
            vmm.vm_add_generic_vhost_user(generic_vhost_user_config.clone()),
            Err(VmError::VmNotCreated)
        ));

        let _ = vmm.vm_create(create_dummy_vm_config());
        assert!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .generic_vhost_user
                .is_none()
        );

        assert!(
            vmm.vm_add_generic_vhost_user(generic_vhost_user_config.clone())
                .unwrap()
                .is_none()
        );
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .generic_vhost_user
                .clone()
                .unwrap()
                .len(),
            1
        );
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .generic_vhost_user
                .clone()
                .unwrap()[0],
            generic_vhost_user_config
        );
    }

    #[test]
    fn test_vmm_vm_cold_add_pmem() {
        let mut vmm = create_dummy_vmm();
        let pmem_config = PmemConfig::parse("file=/tmp/pmem,size=128M").unwrap();

        assert!(matches!(
            vmm.vm_add_pmem(pmem_config.clone()),
            Err(VmError::VmNotCreated)
        ));

        let _ = vmm.vm_create(create_dummy_vm_config());
        assert!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .pmem
                .is_none()
        );

        assert!(vmm.vm_add_pmem(pmem_config.clone()).unwrap().is_none());
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .pmem
                .clone()
                .unwrap()
                .len(),
            1
        );
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .pmem
                .clone()
                .unwrap()[0],
            pmem_config
        );
    }

    #[test]
    fn test_vmm_vm_cold_add_net() {
        let mut vmm = create_dummy_vmm();
        let net_config = NetConfig::parse(
            "mac=de:ad:be:ef:12:34,host_mac=12:34:de:ad:be:ef,vhost_user=true,socket=/tmp/sock",
        )
        .unwrap();

        assert!(matches!(
            vmm.vm_add_net(net_config.clone()),
            Err(VmError::VmNotCreated)
        ));

        let _ = vmm.vm_create(create_dummy_vm_config());
        assert!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .net
                .is_none()
        );

        assert!(vmm.vm_add_net(net_config.clone()).unwrap().is_none());
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .net
                .clone()
                .unwrap()
                .len(),
            1
        );
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .net
                .clone()
                .unwrap()[0],
            net_config
        );
    }

    #[test]
    fn test_vmm_vm_cold_add_vdpa() {
        let mut vmm = create_dummy_vmm();
        let vdpa_config = VdpaConfig::parse("path=/dev/vhost-vdpa,num_queues=2").unwrap();

        assert!(matches!(
            vmm.vm_add_vdpa(vdpa_config.clone()),
            Err(VmError::VmNotCreated)
        ));

        let _ = vmm.vm_create(create_dummy_vm_config());
        assert!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .vdpa
                .is_none()
        );

        assert!(vmm.vm_add_vdpa(vdpa_config.clone()).unwrap().is_none());
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .vdpa
                .clone()
                .unwrap()
                .len(),
            1
        );
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .vdpa
                .clone()
                .unwrap()[0],
            vdpa_config
        );
    }

    #[test]
    fn test_vmm_vm_cold_add_vsock() {
        let mut vmm = create_dummy_vmm();
        let vsock_config = VsockConfig::parse("socket=/tmp/sock,cid=3,iommu=on").unwrap();

        assert!(matches!(
            vmm.vm_add_vsock(vsock_config.clone()),
            Err(VmError::VmNotCreated)
        ));

        let _ = vmm.vm_create(create_dummy_vm_config());
        assert!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .vsock
                .is_none()
        );

        assert!(vmm.vm_add_vsock(vsock_config.clone()).unwrap().is_none());
        assert_eq!(
            vmm.vm_config
                .as_ref()
                .unwrap()
                .lock()
                .unwrap()
                .vsock
                .clone()
                .unwrap(),
            vsock_config
        );
    }
}
