virtio_spec/mmio.rs
1//! Definitions for Virtio over MMIO.
2
3use core::mem;
4
5use volatile::VolatilePtr;
6use volatile::access::{ReadOnly, ReadWrite, Readable, RestrictAccess, WriteOnly};
7
8pub use crate::driver_notifications::NotificationData;
9use crate::volatile::{OveralignedVolatilePtr, WideVolatilePtr};
10use crate::{DeviceConfigSpace, DeviceStatus, Id, le16, le32};
11
12/// The magic value for virtio-mmio.
13///
14/// See [`DeviceRegistersVolatileFieldAccess::magic_value`].
15///
16/// # Examples
17///
18/// ```
19/// # use virtio_spec as virtio;
20/// use virtio::le32;
21///
22/// assert_eq!(virtio::mmio::MAGIC_VALUE, le32::from_le_bytes(*b"virt"));
23/// ```
24pub const MAGIC_VALUE: le32 = le32::from_ne(0x74726976);
25
26/// MMIO Device Registers
27///
28/// Use [`DeviceRegistersVolatileFieldAccess`] and [`DeviceRegistersVolatileWideFieldAccess`] to work with this struct.
29#[repr(transparent)]
30pub struct DeviceRegisters([le32; 0x100 / mem::size_of::<le32>()]);
31
32macro_rules! field_fn {
33 (
34 $(#[doc = $doc:literal])*
35 $(#[doc(alias = $alias:literal)])*
36 #[access($Access:ty)]
37 $field:ident: le32,
38 ) => {
39 $(#[doc = $doc])*
40 $(#[doc(alias = $alias)])*
41 fn $field(self) -> VolatilePtr<'a, le32, A::Restricted>
42 where
43 A: RestrictAccess<$Access>;
44 };
45 (
46 $(#[doc = $doc:literal])*
47 $(#[doc(alias = $alias:literal)])*
48 #[access($Access:ty)]
49 $field:ident: (),
50 ) => {
51 $(#[doc = $doc])*
52 $(#[doc(alias = $alias)])*
53 fn $field(self) -> VolatilePtr<'a, (), A::Restricted>
54 where
55 A: RestrictAccess<$Access>;
56 };
57 (
58 $(#[doc = $doc:literal])*
59 $(#[doc(alias = $alias:literal)])*
60 #[access($Access:ty)]
61 $field:ident: $T:ty,
62 ) => {
63 $(#[doc = $doc])*
64 $(#[doc(alias = $alias)])*
65 fn $field(self) -> OveralignedVolatilePtr<'a, $T, le32, A::Restricted>
66 where
67 A: RestrictAccess<$Access>;
68 };
69}
70
71macro_rules! field_impl {
72 (
73 #[offset($offset:literal)]
74 #[access($Access:ty)]
75 $field:ident: le32,
76 ) => {
77 fn $field(self) -> VolatilePtr<'a, le32, A::Restricted>
78 where
79 A: RestrictAccess<$Access>,
80 {
81 unsafe {
82 self.map(|ptr| ptr.cast::<le32>().byte_add($offset))
83 .restrict()
84 }
85 }
86 };
87 (
88 #[offset($offset:literal)]
89 #[access($Access:ty)]
90 $field:ident: (),
91 ) => {
92 fn $field(self) -> VolatilePtr<'a, (), A::Restricted>
93 where
94 A: RestrictAccess<$Access>,
95 {
96 unsafe {
97 self.map(|ptr| ptr.cast::<()>().byte_add($offset))
98 .restrict()
99 }
100 }
101 };
102 (
103 #[offset($offset:literal)]
104 #[access($Access:ty)]
105 $field:ident: $T:ty,
106 ) => {
107 fn $field(self) -> OveralignedVolatilePtr<'a, $T, le32, A::Restricted>
108 where
109 A: RestrictAccess<$Access>,
110 {
111 let ptr = unsafe { self.map(|ptr| ptr.cast::<le32>().byte_add($offset)) };
112 OveralignedVolatilePtr::new(ptr.restrict())
113 }
114 };
115}
116
117macro_rules! device_register_impl {
118 (
119 $(#[doc = $outer_doc:literal])*
120 pub struct DeviceRegisters {
121 $(
122 $(#[doc = $doc:literal])*
123 $(#[doc(alias = $alias:literal)])*
124 #[offset($offset:literal)]
125 #[access($Access:ty)]
126 $field:ident: $T:tt,
127 )*
128 }
129 ) => {
130 $(#[doc = $outer_doc])*
131 pub trait DeviceRegistersVolatileFieldAccess<'a, A> {
132 $(
133 field_fn! {
134 $(#[doc = $doc])*
135 $(#[doc(alias = $alias)])*
136 #[access($Access)]
137 $field: $T,
138 }
139 )*
140 }
141
142 impl<'a, A> DeviceRegistersVolatileFieldAccess<'a, A> for VolatilePtr<'a, DeviceRegisters, A> {
143 $(
144 field_impl! {
145 #[offset($offset)]
146 #[access($Access)]
147 $field: $T,
148 }
149 )*
150 }
151 };
152}
153
154device_register_impl! {
155 /// MMIO Device Registers
156 pub struct DeviceRegisters {
157 /// Magic Value
158 ///
159 /// 0x74726976
160 /// (a Little Endian equivalent of the “virt” string).
161 ///
162 /// See [`MAGIC_VALUE`].
163 #[doc(alias = "MagicValue")]
164 #[offset(0x000)]
165 #[access(ReadOnly)]
166 magic_value: le32,
167
168 /// Device version number
169 ///
170 /// 0x2.
171 ///
172 /// <div class="warning">
173 ///
174 /// Legacy devices (see _Virtio Transport Options / Virtio Over MMIO / Legacy interface_) used 0x1.
175 ///
176 /// </div>
177 #[doc(alias = "Version")]
178 #[offset(0x004)]
179 #[access(ReadOnly)]
180 version: le32,
181
182 /// Virtio Subsystem Device ID
183 ///
184 /// See _Device Types_ for possible values.
185 /// Value zero (0x0) is used to
186 /// define a system memory map with placeholder devices at static,
187 /// well known addresses, assigning functions to them depending
188 /// on user's needs.
189 #[doc(alias = "DeviceID")]
190 #[offset(0x008)]
191 #[access(ReadOnly)]
192 device_id: Id,
193
194 /// Virtio Subsystem Vendor ID
195 #[doc(alias = "VendorID")]
196 #[offset(0x00c)]
197 #[access(ReadOnly)]
198 vendor_id: le32,
199
200 /// Flags representing features the device supports
201 ///
202 /// Reading from this register returns 32 consecutive flag bits,
203 /// the least significant bit depending on the last value written to
204 /// `DeviceFeaturesSel`. Access to this register returns
205 /// bits `DeviceFeaturesSel`*32 to (`DeviceFeaturesSel`*32)+31, eg.
206 /// feature bits 0 to 31 if `DeviceFeaturesSel` is set to 0 and
207 /// features bits 32 to 63 if `DeviceFeaturesSel` is set to 1.
208 /// Also see _Basic Facilities of a Virtio Device / Feature Bits_.
209 #[doc(alias = "DeviceFeatures")]
210 #[offset(0x010)]
211 #[access(ReadOnly)]
212 device_features: le32,
213
214 /// Device (host) features word selection.
215 ///
216 /// Writing to this register selects a set of 32 device feature bits
217 /// accessible by reading from `DeviceFeatures`.
218 #[doc(alias = "DeviceFeaturesSel")]
219 #[offset(0x014)]
220 #[access(WriteOnly)]
221 device_features_sel: le32,
222
223 /// Flags representing device features understood and activated by the driver
224 ///
225 /// Writing to this register sets 32 consecutive flag bits, the least significant
226 /// bit depending on the last value written to `DriverFeaturesSel`.
227 /// Access to this register sets bits `DriverFeaturesSel`*32
228 /// to (`DriverFeaturesSel`*32)+31, eg. feature bits 0 to 31 if
229 /// `DriverFeaturesSel` is set to 0 and features bits 32 to 63 if
230 /// `DriverFeaturesSel` is set to 1. Also see _Basic Facilities of a Virtio Device / Feature Bits_.
231 #[doc(alias = "DriverFeatures")]
232 #[offset(0x020)]
233 #[access(WriteOnly)]
234 driver_features: le32,
235
236 /// Activated (guest) features word selection
237 ///
238 /// Writing to this register selects a set of 32 activated feature
239 /// bits accessible by writing to `DriverFeatures`.
240 #[doc(alias = "DriverFeaturesSel")]
241 #[offset(0x024)]
242 #[access(WriteOnly)]
243 driver_features_sel: le32,
244
245 /// Virtual queue index
246 ///
247 /// Writing to this register selects the virtqueue that the
248 /// following operations on `QueueSizeMax`, `QueueSize`, `QueueReady`,
249 /// `QueueDescLow`, `QueueDescHigh`, `QueueDriverlLow`, `QueueDriverHigh`,
250 /// `QueueDeviceLow`, `QueueDeviceHigh` and `QueueReset` apply to.
251 #[doc(alias = "QueueSel")]
252 #[offset(0x030)]
253 #[access(WriteOnly)]
254 queue_sel: le16,
255
256 /// Maximum virtual queue size
257 ///
258 /// Reading from the register returns the maximum size (number of
259 /// elements) of the queue the device is ready to process or
260 /// zero (0x0) if the queue is not available. This applies to the
261 /// queue selected by writing to `QueueSel`.
262 ///
263 /// Note: `QueueSizeMax` was previously known as `QueueNumMax`.
264 #[doc(alias = "QueueSizeMax")]
265 #[doc(alias = "QueueNumMax")]
266 #[offset(0x034)]
267 #[access(ReadOnly)]
268 queue_size_max: le16,
269
270 /// Virtual queue size
271 ///
272 /// Queue size is the number of elements in the queue.
273 /// Writing to this register notifies the device what size of the
274 /// queue the driver will use. This applies to the queue selected by
275 /// writing to `QueueSel`.
276 ///
277 /// Note: `QueueSize` was previously known as `QueueNum`.
278 #[doc(alias = "QueueSize")]
279 #[doc(alias = "QueueNum")]
280 #[offset(0x038)]
281 #[access(WriteOnly)]
282 queue_size: le16,
283
284 /// Virtual queue ready bit
285 ///
286 /// Writing one (0x1) to this register notifies the device that it can
287 /// execute requests from this virtqueue. Reading from this register
288 /// returns the last value written to it. Both read and write
289 /// accesses apply to the queue selected by writing to `QueueSel`.
290 #[doc(alias = "QueueReady")]
291 #[offset(0x044)]
292 #[access(ReadWrite)]
293 queue_ready: bool,
294
295 /// Queue notifier
296 ///
297 /// Writing a value to this register notifies the device that
298 /// there are new buffers to process in a queue.
299 ///
300 /// When VIRTIO_F_NOTIFICATION_DATA has not been negotiated,
301 /// the value written is the queue index.
302 ///
303 /// When VIRTIO_F_NOTIFICATION_DATA has been negotiated,
304 /// the `Notification data` value has the following format:
305 ///
306 /// ```c
307 /// le32 {
308 /// vqn : 16;
309 /// next_off : 15;
310 /// next_wrap : 1;
311 /// };
312 /// ```
313 ///
314 /// See _Virtqueues / Driver notifications_
315 /// for the definition of the components.
316 #[doc(alias = "QueueNotify")]
317 #[offset(0x050)]
318 #[access(WriteOnly)]
319 queue_notify: le32,
320
321 /// Interrupt status
322 ///
323 /// Reading from this register returns a bit mask of events that
324 /// caused the device interrupt to be asserted.
325 #[doc(alias = "InterruptStatus")]
326 #[offset(0x060)]
327 #[access(ReadOnly)]
328 interrupt_status: InterruptStatus,
329
330 /// Interrupt acknowledge
331 ///
332 /// Writing a value with bits set as defined in `InterruptStatus`
333 /// to this register notifies the device that events causing
334 /// the interrupt have been handled.
335 #[doc(alias = "InterruptACK")]
336 #[offset(0x064)]
337 #[access(WriteOnly)]
338 interrupt_ack: InterruptStatus,
339
340 /// Device status
341 ///
342 /// Reading from this register returns the current device status
343 /// flags.
344 /// Writing non-zero values to this register sets the status flags,
345 /// indicating the driver progress. Writing zero (0x0) to this
346 /// register triggers a device reset.
347 /// See also p. _Virtio Transport Options / Virtio Over MMIO / MMIO-specific Initialization And Device Operation / Device Initialization_.
348 #[doc(alias = "Status")]
349 #[offset(0x070)]
350 #[access(ReadWrite)]
351 status: DeviceStatus,
352
353 /// Virtual queue's Descriptor Area 64 bit long physical address
354 ///
355 /// Writing to these two registers (lower 32 bits of the address
356 /// to `QueueDescLow`, higher 32 bits to `QueueDescHigh`) notifies
357 /// the device about location of the Descriptor Area of the queue
358 /// selected by writing to `QueueSel` register.
359 #[doc(alias = "QueueDescLow")]
360 #[offset(0x080)]
361 #[access(WriteOnly)]
362 queue_desc_low: le32,
363
364 /// Virtual queue's Descriptor Area 64 bit long physical address
365 ///
366 /// Writing to these two registers (lower 32 bits of the address
367 /// to `QueueDescLow`, higher 32 bits to `QueueDescHigh`) notifies
368 /// the device about location of the Descriptor Area of the queue
369 /// selected by writing to `QueueSel` register.
370 #[doc(alias = "QueueDescHigh")]
371 #[offset(0x084)]
372 #[access(WriteOnly)]
373 queue_desc_high: le32,
374
375 /// Virtual queue's Driver Area 64 bit long physical address
376 ///
377 /// Writing to these two registers (lower 32 bits of the address
378 /// to `QueueDriverLow`, higher 32 bits to `QueueDriverHigh`) notifies
379 /// the device about location of the Driver Area of the queue
380 /// selected by writing to `QueueSel`.
381 #[doc(alias = "QueueDriverLow")]
382 #[offset(0x090)]
383 #[access(WriteOnly)]
384 queue_driver_low: le32,
385
386 /// Virtual queue's Driver Area 64 bit long physical address
387 ///
388 /// Writing to these two registers (lower 32 bits of the address
389 /// to `QueueDriverLow`, higher 32 bits to `QueueDriverHigh`) notifies
390 /// the device about location of the Driver Area of the queue
391 /// selected by writing to `QueueSel`.
392 #[doc(alias = "QueueDriverHigh")]
393 #[offset(0x094)]
394 #[access(WriteOnly)]
395 queue_driver_high: le32,
396
397 /// Virtual queue's Device Area 64 bit long physical address
398 ///
399 /// Writing to these two registers (lower 32 bits of the address
400 /// to `QueueDeviceLow`, higher 32 bits to `QueueDeviceHigh`) notifies
401 /// the device about location of the Device Area of the queue
402 /// selected by writing to `QueueSel`.
403 #[doc(alias = "QueueDeviceLow")]
404 #[offset(0x0a0)]
405 #[access(WriteOnly)]
406 queue_device_low: le32,
407
408 /// Virtual queue's Device Area 64 bit long physical address
409 ///
410 /// Writing to these two registers (lower 32 bits of the address
411 /// to `QueueDeviceLow`, higher 32 bits to `QueueDeviceHigh`) notifies
412 /// the device about location of the Device Area of the queue
413 /// selected by writing to `QueueSel`.
414 #[doc(alias = "QueueDeviceHigh")]
415 #[offset(0x0a4)]
416 #[access(WriteOnly)]
417 queue_device_high: le32,
418
419 /// Shared memory id
420 ///
421 /// Writing to this register selects the shared memory region _Basic Facilities of a Virtio Device / Shared Memory Regions_
422 /// following operations on `SHMLenLow`, `SHMLenHigh`,
423 /// `SHMBaseLow` and `SHMBaseHigh` apply to.
424 #[doc(alias = "SHMSel")]
425 #[offset(0x0ac)]
426 #[access(WriteOnly)]
427 shm_sel: le32,
428
429 /// Shared memory region 64 bit long length
430 ///
431 /// These registers return the length of the shared memory
432 /// region in bytes, as defined by the device for the region selected by
433 /// the `SHMSel` register. The lower 32 bits of the length
434 /// are read from `SHMLenLow` and the higher 32 bits from
435 /// `SHMLenHigh`. Reading from a non-existent
436 /// region (i.e. where the ID written to `SHMSel` is unused)
437 /// results in a length of -1.
438 #[doc(alias = "SHMLenLow")]
439 #[offset(0x0b0)]
440 #[access(ReadOnly)]
441 shm_len_low: le32,
442
443 /// Shared memory region 64 bit long length
444 ///
445 /// These registers return the length of the shared memory
446 /// region in bytes, as defined by the device for the region selected by
447 /// the `SHMSel` register. The lower 32 bits of the length
448 /// are read from `SHMLenLow` and the higher 32 bits from
449 /// `SHMLenHigh`. Reading from a non-existent
450 /// region (i.e. where the ID written to `SHMSel` is unused)
451 /// results in a length of -1.
452 #[doc(alias = "SHMLenHigh")]
453 #[offset(0x0b4)]
454 #[access(ReadOnly)]
455 shm_len_high: le32,
456
457 /// Shared memory region 64 bit long physical address
458 ///
459 /// The driver reads these registers to discover the base address
460 /// of the region in physical address space. This address is
461 /// chosen by the device (or other part of the VMM).
462 /// The lower 32 bits of the address are read from `SHMBaseLow`
463 /// with the higher 32 bits from `SHMBaseHigh`. Reading
464 /// from a non-existent region (i.e. where the ID written to
465 /// `SHMSel` is unused) results in a base address of
466 /// 0xffffffffffffffff.
467 #[doc(alias = "SHMBaseLow")]
468 #[offset(0x0b8)]
469 #[access(ReadOnly)]
470 shm_base_low: le32,
471
472 /// Shared memory region 64 bit long physical address
473 ///
474 /// The driver reads these registers to discover the base address
475 /// of the region in physical address space. This address is
476 /// chosen by the device (or other part of the VMM).
477 /// The lower 32 bits of the address are read from `SHMBaseLow`
478 /// with the higher 32 bits from `SHMBaseHigh`. Reading
479 /// from a non-existent region (i.e. where the ID written to
480 /// `SHMSel` is unused) results in a base address of
481 /// 0xffffffffffffffff.
482 #[doc(alias = "SHMBaseHigh")]
483 #[offset(0x0bc)]
484 #[access(ReadOnly)]
485 shm_base_high: le32,
486
487 /// Virtual queue reset bit
488 ///
489 /// If VIRTIO_F_RING_RESET has been negotiated, writing one (0x1) to this
490 /// register selectively resets the queue. Both read and write accesses
491 /// apply to the queue selected by writing to `QueueSel`.
492 #[doc(alias = "QueueReset")]
493 #[offset(0x0c0)]
494 #[access(ReadWrite)]
495 queue_reset: le32,
496
497 /// Configuration atomicity value
498 ///
499 /// Reading from this register returns a value describing a version of the device-specific configuration space (see `Config`).
500 /// The driver can then access the configuration space and, when finished, read `ConfigGeneration` again.
501 /// If no part of the configuration space has changed between these two `ConfigGeneration` reads, the returned values are identical.
502 /// If the values are different, the configuration space accesses were not atomic and the driver has to perform the operations again.
503 /// See also _Basic Facilities of a Virtio Device / Device Configuration Space_.
504 #[doc(alias = "ConfigGeneration")]
505 #[offset(0x0fc)]
506 #[access(ReadOnly)]
507 config_generation: le32,
508
509 /// Configuration space
510 ///
511 /// Device-specific configuration space starts at the offset 0x100
512 /// and is accessed with byte alignment. Its meaning and size
513 /// depend on the device and the driver.
514 #[doc(alias = "Config")]
515 #[offset(0x100)]
516 #[access(ReadWrite)]
517 config: (),
518 }
519}
520
521impl_wide_field_access! {
522 /// MMIO Device Registers
523 pub trait DeviceRegistersVolatileWideFieldAccess<'a, A>: DeviceRegisters {
524 /// Virtual queue's Descriptor Area 64 bit long physical address
525 ///
526 /// Writing to these two registers (lower 32 bits of the address
527 /// to `QueueDescLow`, higher 32 bits to `QueueDescHigh`) notifies
528 /// the device about location of the Descriptor Area of the queue
529 /// selected by writing to `QueueSel` register.
530 #[doc(alias = "QueueDesc")]
531 #[access(WriteOnly)]
532 queue_desc: queue_desc_low, queue_desc_high;
533
534 /// Virtual queue's Driver Area 64 bit long physical address
535 ///
536 /// Writing to these two registers (lower 32 bits of the address
537 /// to `QueueDriverLow`, higher 32 bits to `QueueDriverHigh`) notifies
538 /// the device about location of the Driver Area of the queue
539 /// selected by writing to `QueueSel`.
540 #[doc(alias = "QueueDriver")]
541 #[access(WriteOnly)]
542 queue_driver: queue_driver_low, queue_driver_high;
543
544 /// Virtual queue's Device Area 64 bit long physical address
545 ///
546 /// Writing to these two registers (lower 32 bits of the address
547 /// to `QueueDeviceLow`, higher 32 bits to `QueueDeviceHigh`) notifies
548 /// the device about location of the Device Area of the queue
549 /// selected by writing to `QueueSel`.
550 #[doc(alias = "QueueDevice")]
551 #[access(WriteOnly)]
552 queue_device: queue_device_low, queue_device_high;
553
554 /// Shared memory region 64 bit long length
555 ///
556 /// These registers return the length of the shared memory
557 /// region in bytes, as defined by the device for the region selected by
558 /// the `SHMSel` register. The lower 32 bits of the length
559 /// are read from `SHMLenLow` and the higher 32 bits from
560 /// `SHMLenHigh`. Reading from a non-existent
561 /// region (i.e. where the ID written to `SHMSel` is unused)
562 /// results in a length of -1.
563 #[doc(alias = "SHMLen")]
564 #[access(ReadOnly)]
565 shm_len: shm_len_low, shm_len_high;
566
567 /// Shared memory region 64 bit long physical address
568 ///
569 /// The driver reads these registers to discover the base address
570 /// of the region in physical address space. This address is
571 /// chosen by the device (or other part of the VMM).
572 /// The lower 32 bits of the address are read from `SHMBaseLow`
573 /// with the higher 32 bits from `SHMBaseHigh`. Reading
574 /// from a non-existent region (i.e. where the ID written to
575 /// `SHMSel` is unused) results in a base address of
576 /// 0xffffffffffffffff.
577 #[doc(alias = "SHMBase")]
578 #[access(ReadOnly)]
579 shm_base: shm_base_low, shm_base_high;
580 }
581}
582
583impl<'a, A> DeviceConfigSpace for VolatilePtr<'a, DeviceRegisters, A>
584where
585 A: RestrictAccess<ReadOnly>,
586 A::Restricted: Readable,
587{
588 fn read_config_with<F, T>(self, f: F) -> T
589 where
590 F: FnMut() -> T,
591 {
592 let mut f = f;
593 loop {
594 let before = self.config_generation().read();
595 let read = f();
596 let after = self.config_generation().read();
597 if after == before {
598 break read;
599 }
600 }
601 }
602}
603
604virtio_bitflags! {
605 /// Interrupt Status
606 pub struct InterruptStatus: u8 {
607 /// Used Buffer Notification
608 ///
609 /// The interrupt was asserted because the device has used a buffer in at least one of the active virtqueues.
610 const USED_BUFFER_NOTIFICATION = 1 << 0;
611
612 /// Configuration Change Notification
613 ///
614 /// The interrupt was asserted because the configuration of the device has changed.
615 const CONFIGURATION_CHANGE_NOTIFICATION = 1 << 1;
616 }
617}