1use super::{DeviceStatus, DeviceType, Transport};
4use crate::{
5 align_up,
6 queue::Descriptor,
7 volatile::{volread, volwrite, ReadOnly, Volatile, WriteOnly},
8 Error, PhysAddr, PAGE_SIZE,
9};
10use core::{
11 convert::{TryFrom, TryInto},
12 fmt::{self, Display, Formatter},
13 mem::{align_of, size_of},
14 ptr::NonNull,
15};
16
17const MAGIC_VALUE: u32 = 0x7472_6976;
18pub(crate) const LEGACY_VERSION: u32 = 1;
19pub(crate) const MODERN_VERSION: u32 = 2;
20const CONFIG_SPACE_OFFSET: usize = 0x100;
21
22#[derive(Copy, Clone, Debug, Eq, PartialEq)]
24#[repr(u32)]
25pub enum MmioVersion {
26 Legacy = LEGACY_VERSION,
28 Modern = MODERN_VERSION,
30}
31
32impl TryFrom<u32> for MmioVersion {
33 type Error = MmioError;
34
35 fn try_from(version: u32) -> Result<Self, Self::Error> {
36 match version {
37 LEGACY_VERSION => Ok(Self::Legacy),
38 MODERN_VERSION => Ok(Self::Modern),
39 _ => Err(MmioError::UnsupportedVersion(version)),
40 }
41 }
42}
43
44impl From<MmioVersion> for u32 {
45 fn from(version: MmioVersion) -> Self {
46 match version {
47 MmioVersion::Legacy => LEGACY_VERSION,
48 MmioVersion::Modern => MODERN_VERSION,
49 }
50 }
51}
52
53#[derive(Clone, Debug, Eq, PartialEq)]
55pub enum MmioError {
56 BadMagic(u32),
58 UnsupportedVersion(u32),
60 ZeroDeviceId,
62}
63
64impl Display for MmioError {
65 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
66 match self {
67 Self::BadMagic(magic) => write!(
68 f,
69 "Invalid magic value {:#010x} (expected 0x74726976).",
70 magic
71 ),
72 Self::UnsupportedVersion(version) => {
73 write!(f, "Unsupported Virtio MMIO version {}.", version)
74 }
75 Self::ZeroDeviceId => write!(f, "Device ID was zero."),
76 }
77 }
78}
79
80#[repr(C)]
84pub struct VirtIOHeader {
85 magic: ReadOnly<u32>,
87
88 version: ReadOnly<u32>,
92
93 device_id: ReadOnly<u32>,
95
96 vendor_id: ReadOnly<u32>,
98
99 device_features: ReadOnly<u32>,
101
102 device_features_sel: WriteOnly<u32>,
104
105 __r1: [ReadOnly<u32>; 2],
107
108 driver_features: WriteOnly<u32>,
110
111 driver_features_sel: WriteOnly<u32>,
113
114 legacy_guest_page_size: WriteOnly<u32>,
121
122 __r2: ReadOnly<u32>,
124
125 queue_sel: WriteOnly<u32>,
131
132 queue_num_max: ReadOnly<u32>,
140
141 queue_num: WriteOnly<u32>,
147
148 legacy_queue_align: WriteOnly<u32>,
154
155 legacy_queue_pfn: Volatile<u32>,
167
168 queue_ready: Volatile<u32>,
170
171 __r3: [ReadOnly<u32>; 2],
173
174 queue_notify: WriteOnly<u32>,
176
177 __r4: [ReadOnly<u32>; 3],
179
180 interrupt_status: ReadOnly<u32>,
182
183 interrupt_ack: WriteOnly<u32>,
185
186 __r5: [ReadOnly<u32>; 2],
188
189 status: Volatile<DeviceStatus>,
197
198 __r6: [ReadOnly<u32>; 3],
200
201 queue_desc_low: WriteOnly<u32>,
203 queue_desc_high: WriteOnly<u32>,
204
205 __r7: [ReadOnly<u32>; 2],
207
208 queue_driver_low: WriteOnly<u32>,
209 queue_driver_high: WriteOnly<u32>,
210
211 __r8: [ReadOnly<u32>; 2],
213
214 queue_device_low: WriteOnly<u32>,
215 queue_device_high: WriteOnly<u32>,
216
217 __r9: [ReadOnly<u32>; 21],
219
220 config_generation: ReadOnly<u32>,
221}
222
223impl VirtIOHeader {
224 #[cfg(test)]
226 pub fn make_fake_header(
227 version: u32,
228 device_id: u32,
229 vendor_id: u32,
230 device_features: u32,
231 queue_num_max: u32,
232 ) -> Self {
233 Self {
234 magic: ReadOnly::new(MAGIC_VALUE),
235 version: ReadOnly::new(version),
236 device_id: ReadOnly::new(device_id),
237 vendor_id: ReadOnly::new(vendor_id),
238 device_features: ReadOnly::new(device_features),
239 device_features_sel: WriteOnly::default(),
240 __r1: Default::default(),
241 driver_features: Default::default(),
242 driver_features_sel: Default::default(),
243 legacy_guest_page_size: Default::default(),
244 __r2: Default::default(),
245 queue_sel: Default::default(),
246 queue_num_max: ReadOnly::new(queue_num_max),
247 queue_num: Default::default(),
248 legacy_queue_align: Default::default(),
249 legacy_queue_pfn: Default::default(),
250 queue_ready: Default::default(),
251 __r3: Default::default(),
252 queue_notify: Default::default(),
253 __r4: Default::default(),
254 interrupt_status: Default::default(),
255 interrupt_ack: Default::default(),
256 __r5: Default::default(),
257 status: Volatile::new(DeviceStatus::empty()),
258 __r6: Default::default(),
259 queue_desc_low: Default::default(),
260 queue_desc_high: Default::default(),
261 __r7: Default::default(),
262 queue_driver_low: Default::default(),
263 queue_driver_high: Default::default(),
264 __r8: Default::default(),
265 queue_device_low: Default::default(),
266 queue_device_high: Default::default(),
267 __r9: Default::default(),
268 config_generation: Default::default(),
269 }
270 }
271}
272
273#[derive(Debug)]
277pub struct MmioTransport {
278 header: NonNull<VirtIOHeader>,
279 version: MmioVersion,
280}
281
282impl MmioTransport {
283 pub unsafe fn new(header: NonNull<VirtIOHeader>) -> Result<Self, MmioError> {
290 let magic = volread!(header, magic);
291 if magic != MAGIC_VALUE {
292 return Err(MmioError::BadMagic(magic));
293 }
294 if volread!(header, device_id) == 0 {
295 return Err(MmioError::ZeroDeviceId);
296 }
297 let version = volread!(header, version).try_into()?;
298 Ok(Self { header, version })
299 }
300
301 pub fn version(&self) -> MmioVersion {
303 self.version
304 }
305
306 pub fn vendor_id(&self) -> u32 {
308 unsafe { volread!(self.header, vendor_id) }
310 }
311}
312
313unsafe impl Send for MmioTransport {}
315
316unsafe impl Sync for MmioTransport {}
319
320impl Transport for MmioTransport {
321 fn device_type(&self) -> DeviceType {
322 let device_id = unsafe { volread!(self.header, device_id) };
324 device_id.into()
325 }
326
327 fn read_device_features(&mut self) -> u64 {
328 unsafe {
330 volwrite!(self.header, device_features_sel, 0); let mut device_features_bits = volread!(self.header, device_features).into();
332 volwrite!(self.header, device_features_sel, 1); device_features_bits += (volread!(self.header, device_features) as u64) << 32;
334 device_features_bits
335 }
336 }
337
338 fn write_driver_features(&mut self, driver_features: u64) {
339 unsafe {
341 volwrite!(self.header, driver_features_sel, 0); volwrite!(self.header, driver_features, driver_features as u32);
343 volwrite!(self.header, driver_features_sel, 1); volwrite!(self.header, driver_features, (driver_features >> 32) as u32);
345 }
346 }
347
348 fn max_queue_size(&mut self, queue: u16) -> u32 {
349 unsafe {
351 volwrite!(self.header, queue_sel, queue.into());
352 volread!(self.header, queue_num_max)
353 }
354 }
355
356 fn notify(&mut self, queue: u16) {
357 unsafe {
359 volwrite!(self.header, queue_notify, queue.into());
360 }
361 }
362
363 fn get_status(&self) -> DeviceStatus {
364 unsafe { volread!(self.header, status) }
366 }
367
368 fn set_status(&mut self, status: DeviceStatus) {
369 unsafe {
371 volwrite!(self.header, status, status);
372 }
373 }
374
375 fn set_guest_page_size(&mut self, guest_page_size: u32) {
376 match self.version {
377 MmioVersion::Legacy => {
378 unsafe {
380 volwrite!(self.header, legacy_guest_page_size, guest_page_size);
381 }
382 }
383 MmioVersion::Modern => {
384 }
386 }
387 }
388
389 fn requires_legacy_layout(&self) -> bool {
390 match self.version {
391 MmioVersion::Legacy => true,
392 MmioVersion::Modern => false,
393 }
394 }
395
396 fn queue_set(
397 &mut self,
398 queue: u16,
399 size: u32,
400 descriptors: PhysAddr,
401 driver_area: PhysAddr,
402 device_area: PhysAddr,
403 ) {
404 match self.version {
405 MmioVersion::Legacy => {
406 assert_eq!(
407 driver_area - descriptors,
408 size_of::<Descriptor>() * size as usize
409 );
410 assert_eq!(
411 device_area - descriptors,
412 align_up(
413 size_of::<Descriptor>() * size as usize
414 + size_of::<u16>() * (size as usize + 3)
415 )
416 );
417 let align = PAGE_SIZE as u32;
418 let pfn = (descriptors / PAGE_SIZE) as u32;
419 assert_eq!(pfn as usize * PAGE_SIZE, descriptors);
420 unsafe {
422 volwrite!(self.header, queue_sel, queue.into());
423 volwrite!(self.header, queue_num, size);
424 volwrite!(self.header, legacy_queue_align, align);
425 volwrite!(self.header, legacy_queue_pfn, pfn);
426 }
427 }
428 MmioVersion::Modern => {
429 unsafe {
431 volwrite!(self.header, queue_sel, queue.into());
432 volwrite!(self.header, queue_num, size);
433 volwrite!(self.header, queue_desc_low, descriptors as u32);
434 volwrite!(self.header, queue_desc_high, (descriptors >> 32) as u32);
435 volwrite!(self.header, queue_driver_low, driver_area as u32);
436 volwrite!(self.header, queue_driver_high, (driver_area >> 32) as u32);
437 volwrite!(self.header, queue_device_low, device_area as u32);
438 volwrite!(self.header, queue_device_high, (device_area >> 32) as u32);
439 volwrite!(self.header, queue_ready, 1);
440 }
441 }
442 }
443 }
444
445 fn queue_unset(&mut self, queue: u16) {
446 match self.version {
447 MmioVersion::Legacy => {
448 unsafe {
450 volwrite!(self.header, queue_sel, queue.into());
451 volwrite!(self.header, queue_num, 0);
452 volwrite!(self.header, legacy_queue_align, 0);
453 volwrite!(self.header, legacy_queue_pfn, 0);
454 }
455 }
456 MmioVersion::Modern => {
457 unsafe {
459 volwrite!(self.header, queue_sel, queue.into());
460
461 volwrite!(self.header, queue_ready, 0);
462 while volread!(self.header, queue_ready) != 0 {}
464
465 volwrite!(self.header, queue_num, 0);
466 volwrite!(self.header, queue_desc_low, 0);
467 volwrite!(self.header, queue_desc_high, 0);
468 volwrite!(self.header, queue_driver_low, 0);
469 volwrite!(self.header, queue_driver_high, 0);
470 volwrite!(self.header, queue_device_low, 0);
471 volwrite!(self.header, queue_device_high, 0);
472 }
473 }
474 }
475 }
476
477 fn queue_used(&mut self, queue: u16) -> bool {
478 unsafe {
480 volwrite!(self.header, queue_sel, queue.into());
481 match self.version {
482 MmioVersion::Legacy => volread!(self.header, legacy_queue_pfn) != 0,
483 MmioVersion::Modern => volread!(self.header, queue_ready) != 0,
484 }
485 }
486 }
487
488 fn ack_interrupt(&mut self) -> bool {
489 unsafe {
491 let interrupt = volread!(self.header, interrupt_status);
492 if interrupt != 0 {
493 volwrite!(self.header, interrupt_ack, interrupt);
494 true
495 } else {
496 false
497 }
498 }
499 }
500
501 fn config_space<T>(&self) -> Result<NonNull<T>, Error> {
502 if align_of::<T>() > 4 {
503 panic!(
505 "Driver expected config space alignment of {} bytes, but VirtIO only guarantees 4 byte alignment.",
506 align_of::<T>()
507 );
508 }
509 Ok(NonNull::new((self.header.as_ptr() as usize + CONFIG_SPACE_OFFSET) as _).unwrap())
510 }
511}
512
513impl Drop for MmioTransport {
514 fn drop(&mut self) {
515 self.set_status(DeviceStatus::empty())
517 }
518}