1 //! MMIO transport for VirtIO. 2 3 use super::{DeviceStatus, DeviceType, Transport}; 4 use crate::{ 5 align_up, 6 queue::Descriptor, 7 volatile::{volread, volwrite, ReadOnly, Volatile, WriteOnly}, 8 Error, PhysAddr, PAGE_SIZE, 9 }; 10 use core::{ 11 convert::{TryFrom, TryInto}, 12 fmt::{self, Display, Formatter}, 13 mem::{align_of, size_of}, 14 ptr::NonNull, 15 }; 16 17 const MAGIC_VALUE: u32 = 0x7472_6976; 18 pub(crate) const LEGACY_VERSION: u32 = 1; 19 pub(crate) const MODERN_VERSION: u32 = 2; 20 const CONFIG_SPACE_OFFSET: usize = 0x100; 21 22 /// The version of the VirtIO MMIO transport supported by a device. 23 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 24 #[repr(u32)] 25 pub enum MmioVersion { 26 /// Legacy MMIO transport with page-based addressing. 27 Legacy = LEGACY_VERSION, 28 /// Modern MMIO transport. 29 Modern = MODERN_VERSION, 30 } 31 32 impl TryFrom<u32> for MmioVersion { 33 type Error = MmioError; 34 try_from(version: u32) -> Result<Self, Self::Error>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 44 impl From<MmioVersion> for u32 { from(version: MmioVersion) -> Self45 fn from(version: MmioVersion) -> Self { 46 match version { 47 MmioVersion::Legacy => LEGACY_VERSION, 48 MmioVersion::Modern => MODERN_VERSION, 49 } 50 } 51 } 52 53 /// An error encountered initialising a VirtIO MMIO transport. 54 #[derive(Clone, Debug, Eq, PartialEq)] 55 pub enum MmioError { 56 /// The header doesn't start with the expected magic value 0x74726976. 57 BadMagic(u32), 58 /// The header reports a version number that is neither 1 (legacy) nor 2 (modern). 59 UnsupportedVersion(u32), 60 /// The header reports a device ID of 0. 61 ZeroDeviceId, 62 } 63 64 impl Display for MmioError { fmt(&self, f: &mut Formatter) -> fmt::Result65 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 /// MMIO Device Register Interface, both legacy and modern. 81 /// 82 /// Ref: 4.2.2 MMIO Device Register Layout and 4.2.4 Legacy interface 83 #[repr(C)] 84 pub struct VirtIOHeader { 85 /// Magic value 86 magic: ReadOnly<u32>, 87 88 /// Device version number 89 /// 90 /// Legacy device returns value 0x1. 91 version: ReadOnly<u32>, 92 93 /// Virtio Subsystem Device ID 94 device_id: ReadOnly<u32>, 95 96 /// Virtio Subsystem Vendor ID 97 vendor_id: ReadOnly<u32>, 98 99 /// Flags representing features the device supports 100 device_features: ReadOnly<u32>, 101 102 /// Device (host) features word selection 103 device_features_sel: WriteOnly<u32>, 104 105 /// Reserved 106 __r1: [ReadOnly<u32>; 2], 107 108 /// Flags representing device features understood and activated by the driver 109 driver_features: WriteOnly<u32>, 110 111 /// Activated (guest) features word selection 112 driver_features_sel: WriteOnly<u32>, 113 114 /// Guest page size 115 /// 116 /// The driver writes the guest page size in bytes to the register during 117 /// initialization, before any queues are used. This value should be a 118 /// power of 2 and is used by the device to calculate the Guest address 119 /// of the first queue page (see QueuePFN). 120 legacy_guest_page_size: WriteOnly<u32>, 121 122 /// Reserved 123 __r2: ReadOnly<u32>, 124 125 /// Virtual queue index 126 /// 127 /// Writing to this register selects the virtual queue that the following 128 /// operations on the QueueNumMax, QueueNum, QueueAlign and QueuePFN 129 /// registers apply to. The index number of the first queue is zero (0x0). 130 queue_sel: WriteOnly<u32>, 131 132 /// Maximum virtual queue size 133 /// 134 /// Reading from the register returns the maximum size of the queue the 135 /// device is ready to process or zero (0x0) if the queue is not available. 136 /// This applies to the queue selected by writing to QueueSel and is 137 /// allowed only when QueuePFN is set to zero (0x0), so when the queue is 138 /// not actively used. 139 queue_num_max: ReadOnly<u32>, 140 141 /// Virtual queue size 142 /// 143 /// Queue size is the number of elements in the queue. Writing to this 144 /// register notifies the device what size of the queue the driver will use. 145 /// This applies to the queue selected by writing to QueueSel. 146 queue_num: WriteOnly<u32>, 147 148 /// Used Ring alignment in the virtual queue 149 /// 150 /// Writing to this register notifies the device about alignment boundary 151 /// of the Used Ring in bytes. This value should be a power of 2 and 152 /// applies to the queue selected by writing to QueueSel. 153 legacy_queue_align: WriteOnly<u32>, 154 155 /// Guest physical page number of the virtual queue 156 /// 157 /// Writing to this register notifies the device about location of the 158 /// virtual queue in the Guest’s physical address space. This value is 159 /// the index number of a page starting with the queue Descriptor Table. 160 /// Value zero (0x0) means physical address zero (0x00000000) and is illegal. 161 /// When the driver stops using the queue it writes zero (0x0) to this 162 /// register. Reading from this register returns the currently used page 163 /// number of the queue, therefore a value other than zero (0x0) means that 164 /// the queue is in use. Both read and write accesses apply to the queue 165 /// selected by writing to QueueSel. 166 legacy_queue_pfn: Volatile<u32>, 167 168 /// new interface only 169 queue_ready: Volatile<u32>, 170 171 /// Reserved 172 __r3: [ReadOnly<u32>; 2], 173 174 /// Queue notifier 175 queue_notify: WriteOnly<u32>, 176 177 /// Reserved 178 __r4: [ReadOnly<u32>; 3], 179 180 /// Interrupt status 181 interrupt_status: ReadOnly<u32>, 182 183 /// Interrupt acknowledge 184 interrupt_ack: WriteOnly<u32>, 185 186 /// Reserved 187 __r5: [ReadOnly<u32>; 2], 188 189 /// Device status 190 /// 191 /// Reading from this register returns the current device status flags. 192 /// Writing non-zero values to this register sets the status flags, 193 /// indicating the OS/driver progress. Writing zero (0x0) to this register 194 /// triggers a device reset. The device sets QueuePFN to zero (0x0) for 195 /// all queues in the device. Also see 3.1 Device Initialization. 196 status: Volatile<DeviceStatus>, 197 198 /// Reserved 199 __r6: [ReadOnly<u32>; 3], 200 201 // new interface only since here 202 queue_desc_low: WriteOnly<u32>, 203 queue_desc_high: WriteOnly<u32>, 204 205 /// Reserved 206 __r7: [ReadOnly<u32>; 2], 207 208 queue_driver_low: WriteOnly<u32>, 209 queue_driver_high: WriteOnly<u32>, 210 211 /// Reserved 212 __r8: [ReadOnly<u32>; 2], 213 214 queue_device_low: WriteOnly<u32>, 215 queue_device_high: WriteOnly<u32>, 216 217 /// Reserved 218 __r9: [ReadOnly<u32>; 21], 219 220 config_generation: ReadOnly<u32>, 221 } 222 223 impl VirtIOHeader { 224 /// Constructs a fake VirtIO header for use in unit tests. 225 #[cfg(test)] make_fake_header( version: u32, device_id: u32, vendor_id: u32, device_features: u32, queue_num_max: u32, ) -> Self226 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 /// MMIO Device Register Interface. 274 /// 275 /// Ref: 4.2.2 MMIO Device Register Layout and 4.2.4 Legacy interface 276 #[derive(Debug)] 277 pub struct MmioTransport { 278 header: NonNull<VirtIOHeader>, 279 version: MmioVersion, 280 } 281 282 impl MmioTransport { 283 /// Constructs a new VirtIO MMIO transport, or returns an error if the header reports an 284 /// unsupported version. 285 /// 286 /// # Safety 287 /// `header` must point to a properly aligned valid VirtIO MMIO region, which must remain valid 288 /// for the lifetime of the transport that is returned. new(header: NonNull<VirtIOHeader>) -> Result<Self, MmioError>289 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 /// Gets the version of the VirtIO MMIO transport. version(&self) -> MmioVersion302 pub fn version(&self) -> MmioVersion { 303 self.version 304 } 305 306 /// Gets the vendor ID. vendor_id(&self) -> u32307 pub fn vendor_id(&self) -> u32 { 308 // Safe because self.header points to a valid VirtIO MMIO region. 309 unsafe { volread!(self.header, vendor_id) } 310 } 311 } 312 313 impl Transport for MmioTransport { device_type(&self) -> DeviceType314 fn device_type(&self) -> DeviceType { 315 // Safe because self.header points to a valid VirtIO MMIO region. 316 let device_id = unsafe { volread!(self.header, device_id) }; 317 device_id.into() 318 } 319 read_device_features(&mut self) -> u64320 fn read_device_features(&mut self) -> u64 { 321 // Safe because self.header points to a valid VirtIO MMIO region. 322 unsafe { 323 volwrite!(self.header, device_features_sel, 0); // device features [0, 32) 324 let mut device_features_bits = volread!(self.header, device_features).into(); 325 volwrite!(self.header, device_features_sel, 1); // device features [32, 64) 326 device_features_bits += (volread!(self.header, device_features) as u64) << 32; 327 device_features_bits 328 } 329 } 330 write_driver_features(&mut self, driver_features: u64)331 fn write_driver_features(&mut self, driver_features: u64) { 332 // Safe because self.header points to a valid VirtIO MMIO region. 333 unsafe { 334 volwrite!(self.header, driver_features_sel, 0); // driver features [0, 32) 335 volwrite!(self.header, driver_features, driver_features as u32); 336 volwrite!(self.header, driver_features_sel, 1); // driver features [32, 64) 337 volwrite!(self.header, driver_features, (driver_features >> 32) as u32); 338 } 339 } 340 max_queue_size(&self) -> u32341 fn max_queue_size(&self) -> u32 { 342 // Safe because self.header points to a valid VirtIO MMIO region. 343 unsafe { volread!(self.header, queue_num_max) } 344 } 345 notify(&mut self, queue: u16)346 fn notify(&mut self, queue: u16) { 347 // Safe because self.header points to a valid VirtIO MMIO region. 348 unsafe { 349 volwrite!(self.header, queue_notify, queue.into()); 350 } 351 } 352 get_status(&self) -> DeviceStatus353 fn get_status(&self) -> DeviceStatus { 354 // Safe because self.header points to a valid VirtIO MMIO region. 355 unsafe { volread!(self.header, status) } 356 } 357 set_status(&mut self, status: DeviceStatus)358 fn set_status(&mut self, status: DeviceStatus) { 359 // Safe because self.header points to a valid VirtIO MMIO region. 360 unsafe { 361 volwrite!(self.header, status, status); 362 } 363 } 364 set_guest_page_size(&mut self, guest_page_size: u32)365 fn set_guest_page_size(&mut self, guest_page_size: u32) { 366 match self.version { 367 MmioVersion::Legacy => { 368 // Safe because self.header points to a valid VirtIO MMIO region. 369 unsafe { 370 volwrite!(self.header, legacy_guest_page_size, guest_page_size); 371 } 372 } 373 MmioVersion::Modern => { 374 // No-op, modern devices don't care. 375 } 376 } 377 } 378 requires_legacy_layout(&self) -> bool379 fn requires_legacy_layout(&self) -> bool { 380 match self.version { 381 MmioVersion::Legacy => true, 382 MmioVersion::Modern => false, 383 } 384 } 385 queue_set( &mut self, queue: u16, size: u32, descriptors: PhysAddr, driver_area: PhysAddr, device_area: PhysAddr, )386 fn queue_set( 387 &mut self, 388 queue: u16, 389 size: u32, 390 descriptors: PhysAddr, 391 driver_area: PhysAddr, 392 device_area: PhysAddr, 393 ) { 394 match self.version { 395 MmioVersion::Legacy => { 396 assert_eq!( 397 driver_area - descriptors, 398 size_of::<Descriptor>() * size as usize 399 ); 400 assert_eq!( 401 device_area - descriptors, 402 align_up( 403 size_of::<Descriptor>() * size as usize 404 + size_of::<u16>() * (size as usize + 3) 405 ) 406 ); 407 let align = PAGE_SIZE as u32; 408 let pfn = (descriptors / PAGE_SIZE) as u32; 409 assert_eq!(pfn as usize * PAGE_SIZE, descriptors); 410 // Safe because self.header points to a valid VirtIO MMIO region. 411 unsafe { 412 volwrite!(self.header, queue_sel, queue.into()); 413 volwrite!(self.header, queue_num, size); 414 volwrite!(self.header, legacy_queue_align, align); 415 volwrite!(self.header, legacy_queue_pfn, pfn); 416 } 417 } 418 MmioVersion::Modern => { 419 // Safe because self.header points to a valid VirtIO MMIO region. 420 unsafe { 421 volwrite!(self.header, queue_sel, queue.into()); 422 volwrite!(self.header, queue_num, size); 423 volwrite!(self.header, queue_desc_low, descriptors as u32); 424 volwrite!(self.header, queue_desc_high, (descriptors >> 32) as u32); 425 volwrite!(self.header, queue_driver_low, driver_area as u32); 426 volwrite!(self.header, queue_driver_high, (driver_area >> 32) as u32); 427 volwrite!(self.header, queue_device_low, device_area as u32); 428 volwrite!(self.header, queue_device_high, (device_area >> 32) as u32); 429 volwrite!(self.header, queue_ready, 1); 430 } 431 } 432 } 433 } 434 queue_unset(&mut self, queue: u16)435 fn queue_unset(&mut self, queue: u16) { 436 match self.version { 437 MmioVersion::Legacy => { 438 // Safe because self.header points to a valid VirtIO MMIO region. 439 unsafe { 440 volwrite!(self.header, queue_sel, queue.into()); 441 volwrite!(self.header, queue_num, 0); 442 volwrite!(self.header, legacy_queue_align, 0); 443 volwrite!(self.header, legacy_queue_pfn, 0); 444 } 445 } 446 MmioVersion::Modern => { 447 // Safe because self.header points to a valid VirtIO MMIO region. 448 unsafe { 449 volwrite!(self.header, queue_sel, queue.into()); 450 451 volwrite!(self.header, queue_ready, 0); 452 // Wait until we read the same value back, to ensure synchronisation (see 4.2.2.2). 453 while volread!(self.header, queue_ready) != 0 {} 454 455 volwrite!(self.header, queue_num, 0); 456 volwrite!(self.header, queue_desc_low, 0); 457 volwrite!(self.header, queue_desc_high, 0); 458 volwrite!(self.header, queue_driver_low, 0); 459 volwrite!(self.header, queue_driver_high, 0); 460 volwrite!(self.header, queue_device_low, 0); 461 volwrite!(self.header, queue_device_high, 0); 462 } 463 } 464 } 465 } 466 queue_used(&mut self, queue: u16) -> bool467 fn queue_used(&mut self, queue: u16) -> bool { 468 // Safe because self.header points to a valid VirtIO MMIO region. 469 unsafe { 470 volwrite!(self.header, queue_sel, queue.into()); 471 match self.version { 472 MmioVersion::Legacy => volread!(self.header, legacy_queue_pfn) != 0, 473 MmioVersion::Modern => volread!(self.header, queue_ready) != 0, 474 } 475 } 476 } 477 ack_interrupt(&mut self) -> bool478 fn ack_interrupt(&mut self) -> bool { 479 // Safe because self.header points to a valid VirtIO MMIO region. 480 unsafe { 481 let interrupt = volread!(self.header, interrupt_status); 482 if interrupt != 0 { 483 volwrite!(self.header, interrupt_ack, interrupt); 484 true 485 } else { 486 false 487 } 488 } 489 } 490 config_space<T>(&self) -> Result<NonNull<T>, Error>491 fn config_space<T>(&self) -> Result<NonNull<T>, Error> { 492 if align_of::<T>() > 4 { 493 // Panic as this should only happen if the driver is written incorrectly. 494 panic!( 495 "Driver expected config space alignment of {} bytes, but VirtIO only guarantees 4 byte alignment.", 496 align_of::<T>() 497 ); 498 } 499 Ok(NonNull::new((self.header.as_ptr() as usize + CONFIG_SPACE_OFFSET) as _).unwrap()) 500 } 501 } 502 503 impl Drop for MmioTransport { drop(&mut self)504 fn drop(&mut self) { 505 // Reset the device when the transport is dropped. 506 self.set_status(DeviceStatus::empty()) 507 } 508 } 509