# header.rs - source
Source of the Rust file `/home/runner/.cargo/git/checkouts/virtio-drivers-4fdfaa862bcdc399/f30d426/src/header.rs`.
Keywords: rust, rustlang, rust-lang

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308

    use crate::PAGE_SIZE;
    use bitflags::*;
    use volatile::{ReadOnly, Volatile, WriteOnly};

    /// MMIO Device Legacy Register Interface.
    ///
    /// Ref: 4.2.4 Legacy interface
    #[repr(C)]
    pub struct VirtIOHeader {
        /// Magic value
        magic: ReadOnly<u32>,

        /// Device version number
        ///
        /// Legacy device returns value 0x1.
        version: ReadOnly<u32>,

        /// Virtio Subsystem Device ID
        device_id: ReadOnly<u32>,

        /// Virtio Subsystem Vendor ID
        vendor_id: ReadOnly<u32>,

        /// Flags representing features the device supports
        device_features: ReadOnly<u32>,

        /// Device (host) features word selection
        device_features_sel: WriteOnly<u32>,

        /// Reserved
        __r1: [ReadOnly<u32>; 2],

        /// Flags representing device features understood and activated by the driver
        driver_features: WriteOnly<u32>,

        /// Activated (guest) features word selection
        driver_features_sel: WriteOnly<u32>,

        /// Guest page size
        ///
        /// The driver writes the guest page size in bytes to the register during
        /// initialization, before any queues are used. This value should be a
        /// power of 2 and is used by the device to calculate the Guest address
        /// of the first queue page (see QueuePFN).
        guest_page_size: WriteOnly<u32>,

        /// Reserved
        __r2: ReadOnly<u32>,

        /// Virtual queue index
        ///
        /// Writing to this register selects the virtual queue that the following
        /// operations on the QueueNumMax, QueueNum, QueueAlign and QueuePFN
        /// registers apply to. The index number of the first queue is zero (0x0).
        queue_sel: WriteOnly<u32>,

        /// Maximum virtual queue size
        ///
        /// Reading from the register returns the maximum size of the queue the
        /// device is ready to process or zero (0x0) if the queue is not available.
        /// This applies to the queue selected by writing to QueueSel and is
        /// allowed only when QueuePFN is set to zero (0x0), so when the queue is
        /// not actively used.
        queue_num_max: ReadOnly<u32>,

        /// Virtual queue size
        ///
        /// Queue size is the number of elements in the queue. Writing to this
        /// register notifies the device what size of the queue the driver will use.
        /// This applies to the queue selected by writing to QueueSel.
        queue_num: WriteOnly<u32>,

        /// Used Ring alignment in the virtual queue
        ///
        /// Writing to this register notifies the device about alignment boundary
        /// of the Used Ring in bytes. This value should be a power of 2 and
        /// applies to the queue selected by writing to QueueSel.
        queue_align: WriteOnly<u32>,

        /// Guest physical page number of the virtual queue
        ///
        /// Writing to this register notifies the device about location of the
        /// virtual queue in the Guest’s physical address space. This value is
        /// the index number of a page starting with the queue Descriptor Table.
        /// Value zero (0x0) means physical address zero (0x00000000) and is illegal.
        /// When the driver stops using the queue it writes zero (0x0) to this
        /// register. Reading from this register returns the currently used page
        /// number of the queue, therefore a value other than zero (0x0) means that
        /// the queue is in use. Both read and write accesses apply to the queue
        /// selected by writing to QueueSel.
        queue_pfn: Volatile<u32>,

        /// new interface only
        queue_ready: Volatile<u32>,

        /// Reserved
        __r3: [ReadOnly<u32>; 2],

        /// Queue notifier
        queue_notify: WriteOnly<u32>,

        /// Reserved
        __r4: [ReadOnly<u32>; 3],

        /// Interrupt status
        interrupt_status: ReadOnly<u32>,

        /// Interrupt acknowledge
        interrupt_ack: WriteOnly<u32>,

        /// Reserved
        __r5: [ReadOnly<u32>; 2],

        /// Device status
        ///
        /// Reading from this register returns the current device status flags.
        /// Writing non-zero values to this register sets the status flags,
        /// indicating the OS/driver progress. Writing zero (0x0) to this register
        /// triggers a device reset. The device sets QueuePFN to zero (0x0) for
        /// all queues in the device. Also see 3.1 Device Initialization.
        status: Volatile<DeviceStatus>,

        /// Reserved
        __r6: [ReadOnly<u32>; 3],

        // new interface only since here
        queue_desc_low: WriteOnly<u32>,
        queue_desc_high: WriteOnly<u32>,

        /// Reserved
        __r7: [ReadOnly<u32>; 2],

        queue_avail_low: WriteOnly<u32>,
        queue_avail_high: WriteOnly<u32>,

        /// Reserved
        __r8: [ReadOnly<u32>; 2],

        queue_used_low: WriteOnly<u32>,
        queue_used_high: WriteOnly<u32>,

        /// Reserved
        __r9: [ReadOnly<u32>; 21],

        config_generation: ReadOnly<u32>,
    }

    impl VirtIOHeader {
        /// Verify a valid header.
        pub fn verify(&self) -> bool {
            self.magic.read() == 0x7472_6976 && self.version.read() == 1 && self.device_id.read() != 0
        }

        /// Get the device type.
        pub fn device_type(&self) -> DeviceType {
            match self.device_id.read() {
                x @ 1..=13 | x @ 16..=24 => unsafe { core::mem::transmute(x as u8) },
                _ => DeviceType::Invalid,
            }
        }

        /// Get the vendor ID.
        pub fn vendor_id(&self) -> u32 {
            self.vendor_id.read()
        }

        /// Begin initializing the device.
        ///
        /// Ref: virtio 3.1.1 Device Initialization
        pub fn begin_init(&mut self, negotiate_features: impl FnOnce(u64) -> u64) {
            self.status.write(DeviceStatus::ACKNOWLEDGE);
            self.status.write(DeviceStatus::DRIVER);

            let features = self.read_device_features();
            self.write_driver_features(negotiate_features(features));
            self.status.write(DeviceStatus::FEATURES_OK);

            self.guest_page_size.write(PAGE_SIZE as u32);
        }

        /// Finish initializing the device.
        pub fn finish_init(&mut self) {
            self.status.write(DeviceStatus::DRIVER_OK);
        }

        /// Read device features.
        fn read_device_features(&mut self) -> u64 {
            self.device_features_sel.write(0); // device features [0, 32)
            let mut device_features_bits = self.device_features.read().into();
            self.device_features_sel.write(1); // device features [32, 64)
            device_features_bits += (self.device_features.read() as u64) << 32;
            device_features_bits
        }

        /// Write device features.
        fn write_driver_features(&mut self, driver_features: u64) {
            self.driver_features_sel.write(0); // driver features [0, 32)
            self.driver_features.write(driver_features as u32);
            self.driver_features_sel.write(1); // driver features [32, 64)
            self.driver_features.write((driver_features >> 32) as u32);
        }

        /// Set queue.
        pub fn queue_set(&mut self, queue: u32, size: u32, align: u32, pfn: u32) {
            self.queue_sel.write(queue);
            self.queue_num.write(size);
            self.queue_align.write(align);
            self.queue_pfn.write(pfn);
        }

        /// Get guest physical page number of the virtual queue.
        pub fn queue_physical_page_number(&mut self, queue: u32) -> u32 {
            self.queue_sel.write(queue);
            self.queue_pfn.read()
        }

        /// Whether the queue is in used.
        pub fn queue_used(&mut self, queue: u32) -> bool {
            self.queue_physical_page_number(queue) != 0
        }

        /// Get the max size of queue.
        pub fn max_queue_size(&self) -> u32 {
            self.queue_num_max.read()
        }

        /// Notify device.
        pub fn notify(&mut self, queue: u32) {
            self.queue_notify.write(queue);
        }

        /// Acknowledge interrupt and return true if success.
        pub fn ack_interrupt(&mut self) -> bool {
            let interrupt = self.interrupt_status.read();
            if interrupt != 0 {
                self.interrupt_ack.write(interrupt);
                true
            } else {
                false
            }
        }

        /// Get the pointer to config space (at offset 0x100)
        pub fn config_space(&self) -> *mut u64 {
            (self as *const _ as usize + CONFIG_SPACE_OFFSET) as _
        }
    }

    bitflags! {
        /// The device status field.
        struct DeviceStatus: u32 {
            /// Indicates that the guest OS has found the device and recognized it
            /// as a valid virtio device.
            const ACKNOWLEDGE = 1;

            /// Indicates that the guest OS knows how to drive the device.
            const DRIVER = 2;

            /// Indicates that something went wrong in the guest, and it has given
            /// up on the device. This could be an internal error, or the driver
            /// didn’t like the device for some reason, or even a fatal error
            /// during device operation.
            const FAILED = 128;

            /// Indicates that the driver has acknowledged all the features it
            /// understands, and feature negotiation is complete.
            const FEATURES_OK = 8;

            /// Indicates that the driver is set up and ready to drive the device.
            const DRIVER_OK = 4;

            /// Indicates that the device has experienced an error from which it
            /// can’t recover.
            const DEVICE_NEEDS_RESET = 64;
        }
    }

    const CONFIG_SPACE_OFFSET: usize = 0x100;

    /// Types of virtio devices.
    #[repr(u8)]
    #[derive(Debug, Eq, PartialEq)]
    #[allow(missing_docs)]
    pub enum DeviceType {
        Invalid = 0,
        Network = 1,
        Block = 2,
        Console = 3,
        EntropySource = 4,
        MemoryBallooning = 5,
        IoMemory = 6,
        Rpmsg = 7,
        ScsiHost = 8,
        _9P = 9,
        Mac80211 = 10,
        RprocSerial = 11,
        VirtioCAIF = 12,
        MemoryBalloon = 13,
        GPU = 16,
        Timer = 17,
        Input = 18,
        Socket = 19,
        Crypto = 20,
        SignalDistributionModule = 21,
        Pstore = 22,
        IOMMU = 23,
        Memory = 24,
    }