strat9_kernel/hardware/virtio/
common.rs1use super::{vring_flags, VirtqDesc};
10use crate::{
11 arch::pci::{Bar, PciDevice},
12 memory::{self, PhysFrame},
13};
14use core::{
15 ptr::{read_volatile, write_volatile},
16 sync::atomic::{fence, AtomicU16, Ordering},
17};
18use endian_num::Le;
19
20pub mod features {
22 pub const VIRTIO_F_RING_INDIRECT_DESC: u64 = 1 << 28;
23 pub const VIRTIO_F_RING_EVENT_IDX: u64 = 1 << 29;
24 pub const VIRTIO_F_VERSION_1: u64 = 1 << 32;
25 pub const VIRTIO_F_ACCESS_PLATFORM: u64 = 1 << 33;
26 pub const VIRTIO_F_RING_PACKED: u64 = 1 << 34;
27 pub const VIRTIO_F_IN_ORDER: u64 = 1 << 35;
28 pub const VIRTIO_F_ORDER_PLATFORM: u64 = 1 << 36;
29 pub const VIRTIO_F_SR_IOV: u64 = 1 << 37;
30 pub const VIRTIO_F_NOTIFICATION_DATA: u64 = 1 << 38;
31}
32
33#[repr(C)]
35pub struct VirtqAvail {
36 pub flags: AtomicU16,
37 pub idx: AtomicU16,
38 }
41
42#[repr(C)]
44#[derive(Debug, Clone, Copy)]
45pub struct VirtqUsedElem {
46 pub id: u32,
48 pub len: u32,
50}
51
52#[repr(C)]
54pub struct VirtqUsed {
55 pub flags: AtomicU16,
56 pub idx: AtomicU16,
57 }
60
61pub struct Virtqueue {
66 queue_size: u16,
68
69 _ring_area: PhysFrame,
71
72 desc_area: u64,
74
75 avail_area: u64,
77
78 used_area: u64,
80
81 desc_ptr: *mut VirtqDesc,
83
84 avail_ptr: *mut VirtqAvail,
86
87 avail_ring_ptr: *mut u16,
89
90 used_ptr: *mut VirtqUsed,
92
93 used_ring_ptr: *mut VirtqUsedElem,
95
96 first_free: u16,
100 free_count: u16,
102
103 last_used_idx: u16,
105
106 next_avail_idx: u16,
108}
109
110unsafe impl Send for Virtqueue {}
112
113impl Virtqueue {
114 #[inline]
115 fn align_up(value: usize, align: usize) -> usize {
116 debug_assert!(align.is_power_of_two());
117 (value + align - 1) & !(align - 1)
118 }
119
120 pub unsafe fn new(queue_size: u16) -> Result<Self, &'static str> {
126 if !queue_size.is_power_of_two() {
127 return Err("Queue size must be power of 2");
128 }
129
130 let desc_size = queue_size as usize * core::mem::size_of::<VirtqDesc>();
131 let avail_size = 6 + queue_size as usize * 2;
132 let used_size = 6 + queue_size as usize * core::mem::size_of::<VirtqUsedElem>();
133 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;
140
141 let ring_pages = (total_size + 4095) / 4096;
143 let ring_order = ring_pages.next_power_of_two().trailing_zeros() as u8;
144 let ring_area = crate::sync::with_irqs_disabled(|token| {
145 memory::allocate_phys_contiguous(token, ring_order)
146 })
147 .map_err(|_| "Failed to allocate virtqueue ring")?;
148 let ring_phys = ring_area.start_address.as_u64();
149 let desc_phys = ring_phys;
150 let avail_phys = ring_phys + avail_offset as u64;
151 let used_phys = ring_phys + used_offset as u64;
152
153 let desc_virt = crate::memory::phys_to_virt(desc_phys);
159 let avail_virt = crate::memory::phys_to_virt(avail_phys);
160 let used_virt = crate::memory::phys_to_virt(used_phys);
161
162 let desc_ptr = desc_virt as *mut VirtqDesc;
163 let avail_ptr = avail_virt as *mut VirtqAvail;
164 let avail_ring_ptr = (avail_virt + 4) as *mut u16;
165 let used_ptr = used_virt as *mut VirtqUsed;
166 let used_ring_ptr = (used_virt + 4) as *mut VirtqUsedElem;
167
168 core::ptr::write_bytes(desc_ptr as *mut u8, 0, total_size);
171
172 for i in 0..queue_size - 1 {
175 let desc = &mut *desc_ptr.add(i as usize);
176 desc.next = Le::<u16>::from_ne(i + 1);
177 }
178 let last_desc = &mut *desc_ptr.add((queue_size - 1) as usize);
180 last_desc.next = Le::<u16>::from_ne(0);
181 last_desc.flags = Le::<u16>::from_ne(0);
182
183 Ok(Self {
184 queue_size,
185 _ring_area: ring_area,
186 desc_area: desc_phys,
187 avail_area: avail_phys,
188 used_area: used_phys,
189 desc_ptr,
190 avail_ptr,
191 avail_ring_ptr,
192 used_ptr,
193 used_ring_ptr,
194 first_free: 0,
195 free_count: queue_size,
196 last_used_idx: 0,
197 next_avail_idx: 0,
198 })
199 }
200
201 pub fn desc_area(&self) -> u64 {
203 self.desc_area
204 }
205
206 pub fn avail_area(&self) -> u64 {
208 self.avail_area
209 }
210
211 pub fn used_area(&self) -> u64 {
213 self.used_area
214 }
215
216 pub fn queue_size(&self) -> usize {
218 self.queue_size as usize
219 }
220
221 pub fn alloc_descriptor(&mut self) -> Option<u16> {
225 if self.free_count == 0 {
226 return None;
227 }
228 let idx = self.first_free;
229 let desc = unsafe { &*self.desc_ptr.add(idx as usize) };
231 self.first_free = desc.next.to_ne(); self.free_count -= 1;
233 Some(idx)
234 }
235
236 pub fn free_descriptor(&mut self, head: u16) {
241 let mut current = head;
242
243 loop {
244 let desc = unsafe { &*self.desc_ptr.add(current as usize) };
246 let has_next = desc.flags.to_ne() & vring_flags::NEXT != 0;
247 let next = desc.next.to_ne();
248
249 let desc_mut = unsafe { &mut *self.desc_ptr.add(current as usize) };
251 desc_mut.next = Le::<u16>::from_ne(self.first_free);
252 self.first_free = current;
253 self.free_count += 1;
254
255 if !has_next {
256 break;
257 }
258 current = next;
259 }
260 }
261
262 pub fn add_buffer(&mut self, buffers: &[(u64, u32, bool)]) -> Result<u16, &'static str> {
269 if buffers.is_empty() {
270 return Err("Empty buffer list");
271 }
272
273 if (buffers.len() as u16) > self.free_count {
274 return Err("Not enough free descriptors");
275 }
276
277 let head = self.alloc_descriptor().ok_or("No free descriptors")?;
279 let mut current = head;
280
281 for (i, &(addr, len, write)) in buffers.iter().enumerate() {
282 let is_last = i == buffers.len() - 1;
283
284 let desc = unsafe { &mut *self.desc_ptr.add(current as usize) };
286 desc.addr = Le::<u64>::from_ne(addr);
287 desc.len = Le::<u32>::from_ne(len);
288 desc.flags = Le::<u16>::from_ne(if write { vring_flags::WRITE } else { 0 });
289
290 if !is_last {
291 let next = self.alloc_descriptor().ok_or("No free descriptors")?;
292 desc.flags = Le::<u16>::from_ne(desc.flags.to_ne() | vring_flags::NEXT);
293 desc.next = Le::<u16>::from_ne(next);
294 current = next;
295 }
296 }
297
298 let avail_idx = unsafe { (*self.avail_ptr).idx.load(Ordering::Acquire) };
301 let ring_idx = (avail_idx % self.queue_size) as usize;
302
303 unsafe {
305 write_volatile(self.avail_ring_ptr.add(ring_idx), head);
306 }
307
308 fence(Ordering::Release);
310
311 unsafe {
314 (*self.avail_ptr)
315 .idx
316 .store(avail_idx.wrapping_add(1), Ordering::Release);
317 }
318
319 self.next_avail_idx = avail_idx.wrapping_add(1);
320
321 Ok(head)
322 }
323
324 pub fn has_used(&self) -> bool {
326 let used_idx = unsafe { (*self.used_ptr).idx.load(Ordering::Acquire) };
328 self.last_used_idx != used_idx
329 }
330
331 pub fn used_indices(&self) -> (u16, u16) {
333 let used_idx = unsafe { (*self.used_ptr).idx.load(Ordering::Acquire) };
335 (used_idx, self.last_used_idx)
336 }
337
338 pub fn peek_used(&self) -> Option<(u16, u32)> {
345 let used_idx = unsafe { (*self.used_ptr).idx.load(Ordering::Acquire) };
346 if self.last_used_idx == used_idx {
347 return None;
348 }
349 let ring_idx = (self.last_used_idx % self.queue_size) as usize;
350 let elem = unsafe { read_volatile(self.used_ring_ptr.add(ring_idx)) };
351 Some((elem.id as u16, elem.len))
352 }
353
354 pub fn get_used(&mut self) -> Option<(u16, u32)> {
358 let used_idx = unsafe { (*self.used_ptr).idx.load(Ordering::Acquire) };
360
361 if self.last_used_idx == used_idx {
362 return None;
363 }
364
365 let ring_idx = (self.last_used_idx % self.queue_size) as usize;
366
367 let elem = unsafe { read_volatile(self.used_ring_ptr.add(ring_idx)) };
369
370 self.last_used_idx = self.last_used_idx.wrapping_add(1);
371
372 self.free_descriptor(elem.id as u16);
373
374 Some((elem.id as u16, elem.len))
375 }
376
377 pub fn should_notify(&self) -> bool {
383 let used_flags = unsafe { (*self.used_ptr).flags.load(Ordering::Acquire) };
385 (used_flags & 1) == 0
387 }
388}
389
390pub struct VirtioDevice {
394 pub pci_dev: PciDevice,
396
397 pub io_base: u16,
399}
400
401impl VirtioDevice {
402 pub unsafe fn new(pci_dev: PciDevice) -> Result<Self, &'static str> {
407 let bar0 = pci_dev.read_bar(0).ok_or("BAR0 not present")?;
409
410 let io_base = match bar0 {
411 Bar::Io { port } => port,
412 _ => return Err("BAR0 is not I/O space (legacy VirtIO required)"),
413 };
414
415 pci_dev.enable_io_space();
417 pci_dev.enable_bus_master();
418
419 Ok(Self { pci_dev, io_base })
420 }
421
422 pub fn read_reg_u8(&self, offset: u16) -> u8 {
424 unsafe { crate::arch::io::inb(self.io_base + offset) }
426 }
427
428 pub fn read_reg_u16(&self, offset: u16) -> u16 {
430 unsafe { crate::arch::io::inw(self.io_base + offset) }
432 }
433
434 pub fn read_reg_u32(&self, offset: u16) -> u32 {
436 unsafe { crate::arch::io::inl(self.io_base + offset) }
438 }
439
440 pub fn write_reg_u8(&self, offset: u16, value: u8) {
442 unsafe { crate::arch::io::outb(self.io_base + offset, value) }
444 }
445
446 pub fn write_reg_u16(&self, offset: u16, value: u16) {
448 unsafe { crate::arch::io::outw(self.io_base + offset, value) }
450 }
451
452 pub fn write_reg_u32(&self, offset: u16, value: u32) {
454 unsafe { crate::arch::io::outl(self.io_base + offset, value) }
456 }
457
458 pub fn read_device_features(&self) -> u32 {
460 self.read_reg_u32(0) }
462
463 pub fn write_guest_features(&self, features: u32) {
465 self.write_reg_u32(4, features); }
467
468 pub fn get_status(&self) -> u8 {
470 self.read_reg_u8(18) }
472
473 pub fn set_status(&self, status: u8) {
475 self.write_reg_u8(18, status); }
477
478 pub fn add_status(&self, status: u8) {
480 let current = self.get_status();
481 self.set_status(current | status);
482 }
483
484 pub fn reset(&self) {
486 self.set_status(0);
487 }
488
489 pub fn read_isr_status(&self) -> u8 {
491 self.read_reg_u8(19) }
493
494 pub fn ack_interrupt(&self) {
496 let _ = self.read_reg_u8(19); }
499
500 pub fn setup_queue(&self, queue_index: u16, queue: &Virtqueue) {
502 self.write_reg_u16(14, queue_index); let max = self.read_reg_u16(12); if max != 0 && (queue.queue_size() as u16) > max {
508 log::warn!(
509 "virtio: queue {} size {} > device max {}",
510 queue_index,
511 queue.queue_size(),
512 max,
513 );
514 }
515
516 let desc_pfn = (queue.desc_area() >> 12) as u32;
518 self.write_reg_u32(8, desc_pfn); log::info!(
521 "virtio: queue {} set up (size={}, pfn={:#x})",
522 queue_index,
523 queue.queue_size(),
524 desc_pfn,
525 );
526 }
527
528 pub fn queue_max_size(&self, queue_index: u16) -> u16 {
530 self.write_reg_u16(14, queue_index); self.read_reg_u16(12) }
533
534 pub fn notify_queue(&self, queue_index: u16) {
536 self.write_reg_u32(16, queue_index as u32); }
540}