virtio_spec/
mmio.rs

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