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}