Skip to main content

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}