arcbox_hypervisor/linux/vm/
virtio.rs1use std::os::unix::io::RawFd;
2use std::sync::atomic::Ordering;
3
4use crate::{error::HypervisorError, types::VirtioDeviceType};
5
6use super::KvmVm;
7
8pub(super) const VIRTIO_MMIO_BASE: u64 = 0x0a00_0000;
11
12pub(super) const VIRTIO_MMIO_SIZE: u64 = 0x200;
14
15pub(super) const VIRTIO_MMIO_GAP: u64 = 0x200;
17
18pub(super) const VIRTIO_MMIO_QUEUE_NOTIFY: u64 = 0x50;
20
21#[cfg(target_arch = "aarch64")]
25const VIRTIO_IRQ_BASE: u32 = 32;
26
27#[cfg(target_arch = "x86_64")]
28const VIRTIO_IRQ_BASE: u32 = 5;
29
30const MAX_VIRTIO_DEVICES: usize = 32;
32
33#[derive(Debug)]
35pub struct VirtioDeviceInfo {
36 pub device_type: VirtioDeviceType,
38 pub mmio_base: u64,
40 pub mmio_size: u64,
42 pub irq: u32,
44 pub irq_fd: RawFd,
46 pub notify_fd: RawFd,
48}
49
50impl KvmVm {
51 pub(super) fn allocate_mmio_region(&self) -> Result<u64, HypervisorError> {
55 let devices = self
56 .virtio_devices
57 .read()
58 .map_err(|_| HypervisorError::DeviceError("Lock poisoned".to_string()))?;
59
60 if devices.len() >= MAX_VIRTIO_DEVICES {
61 return Err(HypervisorError::DeviceError(
62 "Maximum number of VirtIO devices reached".to_string(),
63 ));
64 }
65
66 let offset = devices.len() as u64 * (VIRTIO_MMIO_SIZE + VIRTIO_MMIO_GAP);
68 Ok(VIRTIO_MMIO_BASE + offset)
69 }
70
71 pub(super) fn allocate_irq(&self) -> u32 {
73 let offset = self.next_virtio_irq.fetch_add(1, Ordering::SeqCst);
74 VIRTIO_IRQ_BASE + offset
75 }
76
77 pub(super) fn create_eventfd() -> Result<RawFd, HypervisorError> {
79 let fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) };
80 if fd < 0 {
81 return Err(HypervisorError::DeviceError(format!(
82 "Failed to create eventfd: {}",
83 std::io::Error::last_os_error()
84 )));
85 }
86 Ok(fd)
87 }
88
89 pub(super) fn setup_ioeventfd(&self, mmio_base: u64) -> Result<RawFd, HypervisorError> {
94 let notify_fd = Self::create_eventfd()?;
95
96 let notify_addr = mmio_base + VIRTIO_MMIO_QUEUE_NOTIFY;
99
100 self.vm_fd
101 .register_ioeventfd(notify_addr, 4, notify_fd, None)
102 .map_err(|e| {
103 unsafe { libc::close(notify_fd) };
105 HypervisorError::DeviceError(format!("Failed to register IOEVENTFD: {}", e))
106 })?;
107
108 tracing::debug!(
109 "Registered IOEVENTFD at {:#x} with fd={}",
110 notify_addr,
111 notify_fd
112 );
113
114 Ok(notify_fd)
115 }
116
117 pub fn virtio_devices(&self) -> Result<Vec<VirtioDeviceInfo>, HypervisorError> {
119 let devices = self
120 .virtio_devices
121 .read()
122 .map_err(|_| HypervisorError::DeviceError("Lock poisoned".to_string()))?;
123
124 Ok(devices
125 .iter()
126 .map(|d| VirtioDeviceInfo {
127 device_type: d.device_type.clone(),
128 mmio_base: d.mmio_base,
129 mmio_size: d.mmio_size,
130 irq: d.irq,
131 irq_fd: d.irq_fd,
132 notify_fd: d.notify_fd,
133 })
134 .collect())
135 }
136}
137
138pub(super) fn bincode_serialize_device_config(device: &VirtioDeviceInfo) -> Vec<u8> {
140 let mut bytes = Vec::new();
143
144 let type_byte = match device.device_type {
146 VirtioDeviceType::Block => 0u8,
147 VirtioDeviceType::Net => 1,
148 VirtioDeviceType::Console => 2,
149 VirtioDeviceType::Rng => 3,
150 VirtioDeviceType::Balloon => 4,
151 VirtioDeviceType::Fs => 5,
152 VirtioDeviceType::Vsock => 6,
153 VirtioDeviceType::Gpu => 7,
154 };
155 bytes.push(type_byte);
156
157 bytes.extend_from_slice(&device.mmio_base.to_le_bytes());
159 bytes.extend_from_slice(&device.mmio_size.to_le_bytes());
160
161 bytes.extend_from_slice(&device.irq.to_le_bytes());
163
164 bytes
165}