mmio.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. use super::{DeviceStatus, DeviceType, Transport};
  2. use crate::{align_up, queue::Descriptor, PhysAddr, PAGE_SIZE};
  3. use core::{
  4. convert::{TryFrom, TryInto},
  5. fmt::{self, Display, Formatter},
  6. mem::size_of,
  7. ptr::NonNull,
  8. };
  9. use volatile::{ReadOnly, Volatile, WriteOnly};
  10. const MAGIC_VALUE: u32 = 0x7472_6976;
  11. pub(crate) const LEGACY_VERSION: u32 = 1;
  12. pub(crate) const MODERN_VERSION: u32 = 2;
  13. const CONFIG_SPACE_OFFSET: usize = 0x100;
  14. /// The version of the VirtIO MMIO transport supported by a device.
  15. #[derive(Copy, Clone, Debug, Eq, PartialEq)]
  16. #[repr(u32)]
  17. pub enum MmioVersion {
  18. /// Legacy MMIO transport with page-based addressing.
  19. Legacy = LEGACY_VERSION,
  20. /// Modern MMIO transport.
  21. Modern = MODERN_VERSION,
  22. }
  23. impl TryFrom<u32> for MmioVersion {
  24. type Error = MmioError;
  25. fn try_from(version: u32) -> Result<Self, Self::Error> {
  26. match version {
  27. LEGACY_VERSION => Ok(Self::Legacy),
  28. MODERN_VERSION => Ok(Self::Modern),
  29. _ => Err(MmioError::UnsupportedVersion(version)),
  30. }
  31. }
  32. }
  33. impl From<MmioVersion> for u32 {
  34. fn from(version: MmioVersion) -> Self {
  35. match version {
  36. MmioVersion::Legacy => LEGACY_VERSION,
  37. MmioVersion::Modern => MODERN_VERSION,
  38. }
  39. }
  40. }
  41. /// An error encountered initialising a VirtIO MMIO transport.
  42. #[derive(Clone, Debug, Eq, PartialEq)]
  43. pub enum MmioError {
  44. /// The header doesn't start with the expected magic value 0x74726976.
  45. BadMagic(u32),
  46. /// The header reports a version number that is neither 1 (legacy) nor 2 (modern).
  47. UnsupportedVersion(u32),
  48. /// The header reports a device ID of 0.
  49. ZeroDeviceId,
  50. }
  51. impl Display for MmioError {
  52. fn fmt(&self, f: &mut Formatter) -> fmt::Result {
  53. match self {
  54. Self::BadMagic(magic) => write!(
  55. f,
  56. "Invalid magic value {:#010x} (expected 0x74726976).",
  57. magic
  58. ),
  59. Self::UnsupportedVersion(version) => {
  60. write!(f, "Unsupported Virtio MMIO version {}.", version)
  61. }
  62. Self::ZeroDeviceId => write!(f, "Device ID was zero."),
  63. }
  64. }
  65. }
  66. /// MMIO Device Register Interface, both legacy and modern.
  67. ///
  68. /// Ref: 4.2.2 MMIO Device Register Layout and 4.2.4 Legacy interface
  69. #[repr(C)]
  70. pub struct VirtIOHeader {
  71. /// Magic value
  72. magic: ReadOnly<u32>,
  73. /// Device version number
  74. ///
  75. /// Legacy device returns value 0x1.
  76. version: ReadOnly<u32>,
  77. /// Virtio Subsystem Device ID
  78. device_id: ReadOnly<u32>,
  79. /// Virtio Subsystem Vendor ID
  80. vendor_id: ReadOnly<u32>,
  81. /// Flags representing features the device supports
  82. device_features: ReadOnly<u32>,
  83. /// Device (host) features word selection
  84. device_features_sel: WriteOnly<u32>,
  85. /// Reserved
  86. __r1: [ReadOnly<u32>; 2],
  87. /// Flags representing device features understood and activated by the driver
  88. driver_features: WriteOnly<u32>,
  89. /// Activated (guest) features word selection
  90. driver_features_sel: WriteOnly<u32>,
  91. /// Guest page size
  92. ///
  93. /// The driver writes the guest page size in bytes to the register during
  94. /// initialization, before any queues are used. This value should be a
  95. /// power of 2 and is used by the device to calculate the Guest address
  96. /// of the first queue page (see QueuePFN).
  97. legacy_guest_page_size: WriteOnly<u32>,
  98. /// Reserved
  99. __r2: ReadOnly<u32>,
  100. /// Virtual queue index
  101. ///
  102. /// Writing to this register selects the virtual queue that the following
  103. /// operations on the QueueNumMax, QueueNum, QueueAlign and QueuePFN
  104. /// registers apply to. The index number of the first queue is zero (0x0).
  105. queue_sel: WriteOnly<u32>,
  106. /// Maximum virtual queue size
  107. ///
  108. /// Reading from the register returns the maximum size of the queue the
  109. /// device is ready to process or zero (0x0) if the queue is not available.
  110. /// This applies to the queue selected by writing to QueueSel and is
  111. /// allowed only when QueuePFN is set to zero (0x0), so when the queue is
  112. /// not actively used.
  113. queue_num_max: ReadOnly<u32>,
  114. /// Virtual queue size
  115. ///
  116. /// Queue size is the number of elements in the queue. Writing to this
  117. /// register notifies the device what size of the queue the driver will use.
  118. /// This applies to the queue selected by writing to QueueSel.
  119. queue_num: WriteOnly<u32>,
  120. /// Used Ring alignment in the virtual queue
  121. ///
  122. /// Writing to this register notifies the device about alignment boundary
  123. /// of the Used Ring in bytes. This value should be a power of 2 and
  124. /// applies to the queue selected by writing to QueueSel.
  125. legacy_queue_align: WriteOnly<u32>,
  126. /// Guest physical page number of the virtual queue
  127. ///
  128. /// Writing to this register notifies the device about location of the
  129. /// virtual queue in the Guest’s physical address space. This value is
  130. /// the index number of a page starting with the queue Descriptor Table.
  131. /// Value zero (0x0) means physical address zero (0x00000000) and is illegal.
  132. /// When the driver stops using the queue it writes zero (0x0) to this
  133. /// register. Reading from this register returns the currently used page
  134. /// number of the queue, therefore a value other than zero (0x0) means that
  135. /// the queue is in use. Both read and write accesses apply to the queue
  136. /// selected by writing to QueueSel.
  137. legacy_queue_pfn: Volatile<u32>,
  138. /// new interface only
  139. queue_ready: Volatile<u32>,
  140. /// Reserved
  141. __r3: [ReadOnly<u32>; 2],
  142. /// Queue notifier
  143. queue_notify: WriteOnly<u32>,
  144. /// Reserved
  145. __r4: [ReadOnly<u32>; 3],
  146. /// Interrupt status
  147. interrupt_status: ReadOnly<u32>,
  148. /// Interrupt acknowledge
  149. interrupt_ack: WriteOnly<u32>,
  150. /// Reserved
  151. __r5: [ReadOnly<u32>; 2],
  152. /// Device status
  153. ///
  154. /// Reading from this register returns the current device status flags.
  155. /// Writing non-zero values to this register sets the status flags,
  156. /// indicating the OS/driver progress. Writing zero (0x0) to this register
  157. /// triggers a device reset. The device sets QueuePFN to zero (0x0) for
  158. /// all queues in the device. Also see 3.1 Device Initialization.
  159. status: Volatile<DeviceStatus>,
  160. /// Reserved
  161. __r6: [ReadOnly<u32>; 3],
  162. // new interface only since here
  163. queue_desc_low: WriteOnly<u32>,
  164. queue_desc_high: WriteOnly<u32>,
  165. /// Reserved
  166. __r7: [ReadOnly<u32>; 2],
  167. queue_driver_low: WriteOnly<u32>,
  168. queue_driver_high: WriteOnly<u32>,
  169. /// Reserved
  170. __r8: [ReadOnly<u32>; 2],
  171. queue_device_low: WriteOnly<u32>,
  172. queue_device_high: WriteOnly<u32>,
  173. /// Reserved
  174. __r9: [ReadOnly<u32>; 21],
  175. config_generation: ReadOnly<u32>,
  176. }
  177. impl VirtIOHeader {
  178. /// Constructs a fake VirtIO header for use in unit tests.
  179. #[cfg(test)]
  180. pub fn make_fake_header(
  181. version: u32,
  182. device_id: u32,
  183. vendor_id: u32,
  184. device_features: u32,
  185. queue_num_max: u32,
  186. ) -> Self {
  187. Self {
  188. magic: ReadOnly::new(MAGIC_VALUE),
  189. version: ReadOnly::new(version),
  190. device_id: ReadOnly::new(device_id),
  191. vendor_id: ReadOnly::new(vendor_id),
  192. device_features: ReadOnly::new(device_features),
  193. device_features_sel: WriteOnly::default(),
  194. __r1: Default::default(),
  195. driver_features: Default::default(),
  196. driver_features_sel: Default::default(),
  197. legacy_guest_page_size: Default::default(),
  198. __r2: Default::default(),
  199. queue_sel: Default::default(),
  200. queue_num_max: ReadOnly::new(queue_num_max),
  201. queue_num: Default::default(),
  202. legacy_queue_align: Default::default(),
  203. legacy_queue_pfn: Default::default(),
  204. queue_ready: Default::default(),
  205. __r3: Default::default(),
  206. queue_notify: Default::default(),
  207. __r4: Default::default(),
  208. interrupt_status: Default::default(),
  209. interrupt_ack: Default::default(),
  210. __r5: Default::default(),
  211. status: Volatile::new(DeviceStatus::empty()),
  212. __r6: Default::default(),
  213. queue_desc_low: Default::default(),
  214. queue_desc_high: Default::default(),
  215. __r7: Default::default(),
  216. queue_driver_low: Default::default(),
  217. queue_driver_high: Default::default(),
  218. __r8: Default::default(),
  219. queue_device_low: Default::default(),
  220. queue_device_high: Default::default(),
  221. __r9: Default::default(),
  222. config_generation: Default::default(),
  223. }
  224. }
  225. }
  226. /// MMIO Device Register Interface.
  227. ///
  228. /// Ref: 4.2.2 MMIO Device Register Layout and 4.2.4 Legacy interface
  229. #[derive(Debug)]
  230. pub struct MmioTransport {
  231. header: NonNull<VirtIOHeader>,
  232. version: MmioVersion,
  233. }
  234. impl MmioTransport {
  235. /// Constructs a new VirtIO MMIO transport, or returns an error if the header reports an
  236. /// unsupported version.
  237. ///
  238. /// # Safety
  239. /// `header` must point to a properly aligned valid VirtIO MMIO region, which must remain valid
  240. /// for the lifetime of the transport that is returned.
  241. pub unsafe fn new(header: NonNull<VirtIOHeader>) -> Result<Self, MmioError> {
  242. let magic = header.as_ref().magic.read();
  243. if magic != MAGIC_VALUE {
  244. return Err(MmioError::BadMagic(magic));
  245. }
  246. if header.as_ref().device_id.read() == 0 {
  247. return Err(MmioError::ZeroDeviceId);
  248. }
  249. let version = header.as_ref().version.read().try_into()?;
  250. Ok(Self { header, version })
  251. }
  252. /// Gets the version of the VirtIO MMIO transport.
  253. pub fn version(&self) -> MmioVersion {
  254. self.version
  255. }
  256. /// Gets the vendor ID.
  257. pub fn vendor_id(&self) -> u32 {
  258. self.header().vendor_id.read()
  259. }
  260. fn header(&self) -> &VirtIOHeader {
  261. unsafe { self.header.as_ref() }
  262. }
  263. fn header_mut(&mut self) -> &mut VirtIOHeader {
  264. unsafe { self.header.as_mut() }
  265. }
  266. }
  267. impl Transport for MmioTransport {
  268. fn device_type(&self) -> DeviceType {
  269. match self.header().device_id.read() {
  270. x @ 1..=13 | x @ 16..=24 => unsafe { core::mem::transmute(x as u8) },
  271. _ => DeviceType::Invalid,
  272. }
  273. }
  274. fn read_device_features(&mut self) -> u64 {
  275. let header = self.header_mut();
  276. header.device_features_sel.write(0); // device features [0, 32)
  277. let mut device_features_bits = header.device_features.read().into();
  278. header.device_features_sel.write(1); // device features [32, 64)
  279. device_features_bits += (header.device_features.read() as u64) << 32;
  280. device_features_bits
  281. }
  282. fn write_driver_features(&mut self, driver_features: u64) {
  283. let header = self.header_mut();
  284. header.driver_features_sel.write(0); // driver features [0, 32)
  285. header.driver_features.write(driver_features as u32);
  286. header.driver_features_sel.write(1); // driver features [32, 64)
  287. header.driver_features.write((driver_features >> 32) as u32);
  288. }
  289. fn max_queue_size(&self) -> u32 {
  290. self.header().queue_num_max.read()
  291. }
  292. fn notify(&mut self, queue: u32) {
  293. self.header_mut().queue_notify.write(queue);
  294. }
  295. fn set_status(&mut self, status: DeviceStatus) {
  296. self.header_mut().status.write(status);
  297. }
  298. fn set_guest_page_size(&mut self, guest_page_size: u32) {
  299. match self.version {
  300. MmioVersion::Legacy => {
  301. self.header_mut()
  302. .legacy_guest_page_size
  303. .write(guest_page_size);
  304. }
  305. MmioVersion::Modern => {
  306. // No-op, modern devices don't care.
  307. }
  308. }
  309. }
  310. fn queue_set(
  311. &mut self,
  312. queue: u32,
  313. size: u32,
  314. descriptors: PhysAddr,
  315. driver_area: PhysAddr,
  316. device_area: PhysAddr,
  317. ) {
  318. match self.version {
  319. MmioVersion::Legacy => {
  320. assert_eq!(
  321. driver_area - descriptors,
  322. size_of::<Descriptor>() * size as usize
  323. );
  324. assert_eq!(
  325. device_area - descriptors,
  326. align_up(
  327. size_of::<Descriptor>() * size as usize
  328. + size_of::<u16>() * (size as usize + 3)
  329. )
  330. );
  331. let align = PAGE_SIZE as u32;
  332. let pfn = (descriptors / PAGE_SIZE) as u32;
  333. assert_eq!(pfn as usize * PAGE_SIZE, descriptors);
  334. self.header_mut().queue_sel.write(queue);
  335. self.header_mut().queue_num.write(size);
  336. self.header_mut().legacy_queue_align.write(align);
  337. self.header_mut().legacy_queue_pfn.write(pfn);
  338. }
  339. MmioVersion::Modern => {
  340. self.header_mut().queue_sel.write(queue);
  341. self.header_mut().queue_num.write(size);
  342. self.header_mut().queue_desc_low.write(descriptors as u32);
  343. self.header_mut()
  344. .queue_desc_high
  345. .write((descriptors >> 32) as u32);
  346. self.header_mut().queue_driver_low.write(driver_area as u32);
  347. self.header_mut()
  348. .queue_driver_high
  349. .write((driver_area >> 32) as u32);
  350. self.header_mut().queue_device_low.write(device_area as u32);
  351. self.header_mut()
  352. .queue_device_high
  353. .write((device_area >> 32) as u32);
  354. self.header_mut().queue_ready.write(1);
  355. }
  356. }
  357. }
  358. fn queue_used(&mut self, queue: u32) -> bool {
  359. self.header_mut().queue_sel.write(queue);
  360. match self.version {
  361. MmioVersion::Legacy => self.header().legacy_queue_pfn.read() != 0,
  362. MmioVersion::Modern => self.header().queue_ready.read() != 0,
  363. }
  364. }
  365. fn ack_interrupt(&mut self) -> bool {
  366. let header = self.header_mut();
  367. let interrupt = header.interrupt_status.read();
  368. if interrupt != 0 {
  369. header.interrupt_ack.write(interrupt);
  370. true
  371. } else {
  372. false
  373. }
  374. }
  375. fn config_space(&self) -> *mut u64 {
  376. (self.header.as_ptr() as usize + CONFIG_SPACE_OFFSET) as _
  377. }
  378. }