// Source size: 1208 + 1193 LOC
// Status: compact port — virtq descriptor walk + MMIO register
//         shell. Stripped of x86 PCI bits, async hooks, non-vsock
//         feature bits. ~500 LOC.
//
// Two layers:
//   queue::Queue  — descriptor / avail / used ring walking against
//                   guest memory accessed by raw pointer. The guest
//                   memory pointer comes from the same mmap that
//                   `vmm::vstate::MicroVm::ram_host` holds.
//   mmio::MmioVirtio — generic virtio-mmio register layout; wraps
//                      a Device trait.

pub mod balloon;
pub mod blk;
pub mod fs;
pub mod mmio;
pub mod queue;
pub mod rng;
pub mod vsock;

/// Virtio status bits (subset).
pub const STATUS_ACKNOWLEDGE: u32 = 1;
pub const STATUS_DRIVER: u32 = 2;
pub const STATUS_DRIVER_OK: u32 = 4;
pub const STATUS_FEATURES_OK: u32 = 8;
pub const STATUS_FAILED: u32 = 0x80;

/// Virtio device IDs (subset).
pub const VIRTIO_ID_BLOCK: u32 = 2;
pub const VIRTIO_ID_RNG: u32 = 4;
pub const VIRTIO_ID_VSOCK: u32 = 19;
/// virtio-fs (virtio spec §5.11). Two queues: hiprio (0) + request (1+).
/// Pairs with a virtio-mmio shared-memory region at SHMID 0 — the DAX
/// window. See `fs::VirtioFs`.
pub const VIRTIO_ID_FS: u32 = 26;

/// A virtio shared-memory region exposed by a device. virtio-fs uses
/// SHMID 0 as the DAX window: the device reserves a contiguous range
/// of guest physical address space, and SETUPMAPPING requests carve
/// host-file mappings into it via `hv_vm_map`.
///
/// The guest discovers each region by writing `id` to MMIO offset 0x0AC
/// (SHMSel) then reading `len` from 0x0B0/0x0B4 and `base` from
/// 0x0B8/0x0BC. A `len` of 0 means "no region with this id".
#[derive(Clone, Copy, Debug)]
pub struct ShmRegion {
    /// Region id (SHMID). 0 = DAX window for virtio-fs.
    pub id: u8,
    /// Guest physical address of the region. Must be 16 KiB aligned.
    pub gpa: u64,
    /// Length in bytes. Must be 16 KiB aligned.
    pub len: u64,
}

/// Generic virtio device trait. Implementations describe themselves
/// + handle queue notifications.
pub trait VirtioDevice: Send + Sync {
    fn device_id(&self) -> u32;
    fn vendor_id(&self) -> u32 {
        0x554d4551
    } // 'QEMU' — the conventional virtio-mmio vendor ID
    fn num_queues(&self) -> usize;
    fn queue_max_size(&self) -> u16 {
        256
    }
    /// Device-specific configuration space (read-only to the guest
    /// from the offset-0x100 MMIO window).
    fn config(&self) -> Vec<u8> {
        Vec::new()
    }
    /// Device feature bits offered to the driver.
    fn features(&self) -> u64 {
        1u64 << 32 /* VIRTIO_F_VERSION_1 */
    }
    /// Called when the driver kicks queue `q` (writes to QueueNotify).
    fn notify(&self, q: u16);
    /// Called when the driver transitions the device to DRIVER_OK.
    fn activate(&self, queues: Vec<queue::Queue>);
    /// Snapshot the device's CURRENT live queue state (with up-to-date
    /// last_avail_idx / next_used_idx cursors). Defaults to empty for
    /// pre-snapshot devices (caller falls back to the MmioVirtio
    /// mirror in that case). Required for snapshot/restore correctness:
    /// once `activate` clones queues into the device, the device
    /// bumps cursors in its own copy — the MmioVirtio's mirror goes
    /// stale.
    fn snapshot_queues(&self) -> Vec<queue::Queue> {
        Vec::new()
    }
    /// Guest writes to device-specific config space (offset >= 0x100,
    /// relative offset passed in). Default no-op. virtio-balloon's
    /// `actual` register at config-offset 0x004 is the only user.
    fn config_write(&self, _offset: usize, _value: u32) {}

    /// Shared-memory regions the device exposes via the SHMID
    /// mechanism. Default empty — only virtio-fs uses this today (for
    /// the DAX window). Returned regions are discovered by the guest
    /// via the MMIO SHMSel selector — see `ShmRegion` doc.
    fn shm_regions(&self) -> Vec<ShmRegion> {
        Vec::new()
    }
}
