strat9_kernel/hardware/virtio/
common.rs1use super::{vring_flags, VirtqDesc};
10use crate::{
11 arch::x86_64::pci::{Bar, PciDevice},
12 memory::{self, PhysFrame},
13};
14use alloc::vec::Vec;
15use core::{
16 ptr::{read_volatile, write_volatile},
17 sync::atomic::{fence, AtomicU16, Ordering},
18};
19use endian_num::Le;
20
21pub mod features {
23 pub const VIRTIO_F_RING_INDIRECT_DESC: u64 = 1 << 28;
24 pub const VIRTIO_F_RING_EVENT_IDX: u64 = 1 << 29;
25 pub const VIRTIO_F_VERSION_1: u64 = 1 << 32;
26 pub const VIRTIO_F_ACCESS_PLATFORM: u64 = 1 << 33;
27 pub const VIRTIO_F_RING_PACKED: u64 = 1 << 34;
28 pub const VIRTIO_F_IN_ORDER: u64 = 1 << 35;
29 pub const VIRTIO_F_ORDER_PLATFORM: u64 = 1 << 36;
30 pub const VIRTIO_F_SR_IOV: u64 = 1 << 37;
31 pub const VIRTIO_F_NOTIFICATION_DATA: u64 = 1 << 38;
32}
33
34#[repr(C)]
36pub struct VirtqAvail {
37 pub flags: AtomicU16,
38 pub idx: AtomicU16,
39 }
42
43#[repr(C)]
45#[derive(Debug, Clone, Copy)]
46pub struct VirtqUsedElem {
47 pub id: u32,
49 pub len: u32,
51}
52
53#[repr(C)]
55pub struct VirtqUsed {
56 pub flags: AtomicU16,
57 pub idx: AtomicU16,
58 }
61
62pub struct Virtqueue {
67 queue_size: u16,
69
70 _ring_area: PhysFrame,
72
73 desc_area: u64,
75
76 avail_area: u64,
78
79 used_area: u64,
81
82 desc_ptr: *mut VirtqDesc,
84
85 avail_ptr: *mut VirtqAvail,
87
88 avail_ring_ptr: *mut u16,
90
91 used_ptr: *mut VirtqUsed,
93
94 used_ring_ptr: *mut VirtqUsedElem,
96
97 free_descriptors: Vec<u16>,
99
100 last_used_idx: u16,
102
103 next_avail_idx: u16,
105}
106
107unsafe impl Send for Virtqueue {}
109
110impl Virtqueue {
111 #[inline]
112 fn align_up(value: usize, align: usize) -> usize {
113 debug_assert!(align.is_power_of_two());
114 (value + align - 1) & !(align - 1)
115 }
116
117 pub unsafe fn new(queue_size: u16) -> Result<Self, &'static str> {
123 if !queue_size.is_power_of_two() {
124 return Err("Queue size must be power of 2");
125 }
126
127 let desc_size = queue_size as usize * core::mem::size_of::<VirtqDesc>();
128 let avail_size = 6 + queue_size as usize * 2;
129 let used_size = 6 + queue_size as usize * core::mem::size_of::<VirtqUsedElem>();
130 let avail_offset = Self::align_up(desc_size, 4096); let used_offset = Self::align_up(avail_offset + avail_size, 4096); let total_size = used_offset + used_size;
137
138 let ring_pages = (total_size + 4095) / 4096;
140 let ring_order = ring_pages.next_power_of_two().trailing_zeros() as u8;
141 let ring_area = crate::sync::with_irqs_disabled(|token| {
142 memory::allocate_phys_contiguous(token, ring_order)
143 })
144 .map_err(|_| "Failed to allocate virtqueue ring")?;
145 let ring_phys = ring_area.start_address.as_u64();
146 let desc_phys = ring_phys;
147 let avail_phys = ring_phys + avail_offset as u64;
148 let used_phys = ring_phys + used_offset as u64;
149
150 let desc_virt = crate::memory::phys_to_virt(desc_phys);
156 let avail_virt = crate::memory::phys_to_virt(avail_phys);
157 let used_virt = crate::memory::phys_to_virt(used_phys);
158
159 let desc_ptr = desc_virt as *mut VirtqDesc;
160 let avail_ptr = avail_virt as *mut VirtqAvail;
161 let avail_ring_ptr = (avail_virt + 4) as *mut u16;
162 let used_ptr = used_virt as *mut VirtqUsed;
163 let used_ring_ptr = (used_virt + 4) as *mut VirtqUsedElem;
164
165 core::ptr::write_bytes(desc_ptr as *mut u8, 0, total_size);
168
169 let mut free_descriptors = Vec::with_capacity(queue_size as usize);
171 for i in (0..queue_size).rev() {
172 free_descriptors.push(i);
173 }
174
175 Ok(Self {
176 queue_size,
177 _ring_area: ring_area,
178 desc_area: desc_phys,
179 avail_area: avail_phys,
180 used_area: used_phys,
181 desc_ptr,
182 avail_ptr,
183 avail_ring_ptr,
184 used_ptr,
185 used_ring_ptr,
186 free_descriptors,
187 last_used_idx: 0,
188 next_avail_idx: 0,
189 })
190 }
191
192 pub fn desc_area(&self) -> u64 {
194 self.desc_area
195 }
196
197 pub fn avail_area(&self) -> u64 {
199 self.avail_area
200 }
201
202 pub fn used_area(&self) -> u64 {
204 self.used_area
205 }
206
207 pub fn queue_size(&self) -> usize {
209 self.queue_size as usize
210 }
211
212 pub fn size(&self) -> u16 {
214 self.queue_size
215 }
216
217 pub fn alloc_descriptor(&mut self) -> Option<u16> {
221 self.free_descriptors.pop()
222 }
223
224 pub fn free_descriptor(&mut self, head: u16) {
228 let mut current = head;
229
230 loop {
231 let desc = unsafe { &*self.desc_ptr.add(current as usize) };
233 let has_next = desc.flags.to_ne() & vring_flags::NEXT != 0;
234 let next = desc.next.to_ne();
235
236 self.free_descriptors.push(current);
237
238 if !has_next {
239 break;
240 }
241 current = next;
242 }
243 }
244
245 pub fn add_buffer(&mut self, buffers: &[(u64, u32, bool)]) -> Result<u16, &'static str> {
252 if buffers.is_empty() {
253 return Err("Empty buffer list");
254 }
255
256 if buffers.len() > self.free_descriptors.len() {
257 return Err("Not enough free descriptors");
258 }
259
260 let head = self.alloc_descriptor().ok_or("No free descriptors")?;
262 let mut current = head;
263
264 for (i, &(addr, len, write)) in buffers.iter().enumerate() {
265 let is_last = i == buffers.len() - 1;
266
267 let desc = unsafe { &mut *self.desc_ptr.add(current as usize) };
269 desc.addr = Le::<u64>::from_ne(addr);
270 desc.len = Le::<u32>::from_ne(len);
271 desc.flags = Le::<u16>::from_ne(if write { vring_flags::WRITE } else { 0 });
272
273 if !is_last {
274 let next = self.alloc_descriptor().ok_or("No free descriptors")?;
275 desc.flags = Le::<u16>::from_ne(desc.flags.to_ne() | vring_flags::NEXT);
276 desc.next = Le::<u16>::from_ne(next);
277 current = next;
278 }
279 }
280
281 let avail_idx = unsafe { (*self.avail_ptr).idx.load(Ordering::Acquire) };
284 let ring_idx = (avail_idx % self.queue_size) as usize;
285
286 unsafe {
288 write_volatile(self.avail_ring_ptr.add(ring_idx), head);
289 }
290
291 fence(Ordering::Release);
293
294 unsafe {
297 (*self.avail_ptr)
298 .idx
299 .store(avail_idx.wrapping_add(1), Ordering::Release);
300 }
301
302 self.next_avail_idx = avail_idx.wrapping_add(1);
303
304 Ok(head)
305 }
306
307 pub fn has_used(&self) -> bool {
309 let used_idx = unsafe { (*self.used_ptr).idx.load(Ordering::Acquire) };
311 self.last_used_idx != used_idx
312 }
313
314 pub fn used_indices(&self) -> (u16, u16) {
316 let used_idx = unsafe { (*self.used_ptr).idx.load(Ordering::Acquire) };
318 (used_idx, self.last_used_idx)
319 }
320
321 pub fn get_used(&mut self) -> Option<(u16, u32)> {
325 let used_idx = unsafe { (*self.used_ptr).idx.load(Ordering::Acquire) };
327
328 if self.last_used_idx == used_idx {
329 return None;
330 }
331
332 let ring_idx = (self.last_used_idx % self.queue_size) as usize;
333
334 let elem = unsafe { read_volatile(self.used_ring_ptr.add(ring_idx)) };
336
337 self.last_used_idx = self.last_used_idx.wrapping_add(1);
338
339 self.free_descriptor(elem.id as u16);
346
347 Some((elem.id as u16, elem.len))
348 }
349
350 pub fn should_notify(&self) -> bool {
352 true
355 }
356}
357
358pub struct VirtioDevice {
362 pub pci_dev: PciDevice,
364
365 pub io_base: u16,
367}
368
369impl VirtioDevice {
370 pub unsafe fn new(pci_dev: PciDevice) -> Result<Self, &'static str> {
375 let bar0 = pci_dev.read_bar(0).ok_or("BAR0 not present")?;
377
378 let io_base = match bar0 {
379 Bar::Io { port } => port,
380 _ => return Err("BAR0 is not I/O space (legacy VirtIO required)"),
381 };
382
383 pci_dev.enable_io_space();
385 pci_dev.enable_bus_master();
386
387 Ok(Self { pci_dev, io_base })
388 }
389
390 pub fn read_reg_u8(&self, offset: u16) -> u8 {
392 unsafe { crate::arch::x86_64::io::inb(self.io_base + offset) }
394 }
395
396 pub fn read_reg_u16(&self, offset: u16) -> u16 {
398 unsafe { crate::arch::x86_64::io::inw(self.io_base + offset) }
400 }
401
402 pub fn read_reg_u32(&self, offset: u16) -> u32 {
404 unsafe { crate::arch::x86_64::io::inl(self.io_base + offset) }
406 }
407
408 pub fn write_reg_u8(&self, offset: u16, value: u8) {
410 unsafe { crate::arch::x86_64::io::outb(self.io_base + offset, value) }
412 }
413
414 pub fn write_reg_u16(&self, offset: u16, value: u16) {
416 unsafe { crate::arch::x86_64::io::outw(self.io_base + offset, value) }
418 }
419
420 pub fn write_reg_u32(&self, offset: u16, value: u32) {
422 unsafe { crate::arch::x86_64::io::outl(self.io_base + offset, value) }
424 }
425
426 pub fn read_device_features(&self) -> u32 {
428 self.read_reg_u32(0) }
430
431 pub fn write_guest_features(&self, features: u32) {
433 self.write_reg_u32(4, features); }
435
436 pub fn get_status(&self) -> u8 {
438 self.read_reg_u8(18) }
440
441 pub fn set_status(&self, status: u8) {
443 self.write_reg_u8(18, status); }
445
446 pub fn add_status(&self, status: u8) {
448 let current = self.get_status();
449 self.set_status(current | status);
450 }
451
452 pub fn reset(&self) {
454 self.set_status(0);
455 }
456
457 pub fn read_isr_status(&self) -> u8 {
459 self.read_reg_u8(19) }
461
462 pub fn ack_interrupt(&self) {
464 let _ = self.read_reg_u8(19); }
467
468 pub fn setup_queue(&self, queue_index: u16, queue: &Virtqueue) {
470 self.write_reg_u16(14, queue_index); let max = self.read_reg_u16(12); if max != 0 && (queue.queue_size() as u16) > max {
476 log::warn!(
477 "virtio: queue {} size {} > device max {}",
478 queue_index,
479 queue.queue_size(),
480 max,
481 );
482 }
483
484 let desc_pfn = (queue.desc_area() >> 12) as u32;
486 self.write_reg_u32(8, desc_pfn); log::info!(
489 "virtio: queue {} set up (size={}, pfn={:#x})",
490 queue_index,
491 queue.queue_size(),
492 desc_pfn,
493 );
494 }
495
496 pub fn queue_max_size(&self, queue_index: u16) -> u16 {
498 self.write_reg_u16(14, queue_index); self.read_reg_u16(12) }
501
502 pub fn notify_queue(&self, queue_index: u16) {
504 self.write_reg_u32(16, queue_index as u32); }
508}