virtio/transports/
mmio.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! VirtIO memory mapped device driver
6
7use kernel::utilities::cells::OptionalCell;
8use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
9use kernel::utilities::registers::{
10    register_bitfields, InMemoryRegister, ReadOnly, ReadWrite, WriteOnly,
11};
12use kernel::utilities::StaticRef;
13
14use super::super::devices::{VirtIODeviceDriver, VirtIODeviceType};
15use super::super::queues::Virtqueue;
16use super::super::transports::{VirtIOInitializationError, VirtIOTransport};
17
18// Magic string "virt" every device has to expose
19const VIRTIO_MAGIC_VALUE: [u8; 4] = [0x76, 0x69, 0x72, 0x74];
20
21#[repr(C)]
22pub struct VirtIOMMIODeviceRegisters {
23    /// 0x000 Magic string "virt" for identification
24    magic_value: ReadOnly<u32>,
25    /// 0x004 Device version number
26    device_version: ReadOnly<u32>,
27    /// 0x008 VirtIO Subsystem Device ID
28    device_id: ReadOnly<u32>,
29    /// 0x00C VirtIO Subsystem Vendor ID
30    vendor_id: ReadOnly<u32>,
31    /// 0x010 Flags representing features the device supports
32    device_features: ReadOnly<u32, DeviceFeatures::Register>,
33    /// 0x014 Device (host) features word selection
34    device_features_sel: WriteOnly<u32, DeviceFeatures::Register>,
35    // 0x018 - 0x01C: reserved
36    _reversed0: [u32; 2],
37    /// 0x020 Flags representing features understood and activated by the driver
38    driver_features: WriteOnly<u32>,
39    /// 0x024 Activated (guest) features word selection
40    driver_features_sel: WriteOnly<u32>,
41    // 0x028 - 0x02C: reserved
42    _reserved1: [u32; 2],
43    /// 0x030 Virtual queue index
44    queue_sel: WriteOnly<u32>,
45    /// 0x034 Maximum virtual queue size
46    queue_num_max: ReadOnly<u32>,
47    /// 0x038 Virtual queue size
48    queue_num: WriteOnly<u32>,
49    // 0x03C - 0x40: reserved
50    _reserved2: [u32; 2],
51    /// 0x044 Virtual queue ready bit
52    queue_ready: ReadWrite<u32>,
53    // 0x048 - 0x04C: reserved
54    _reserved3: [u32; 2],
55    /// 0x050 Queue notifier
56    queue_notify: WriteOnly<u32>,
57    // 0x054 - 0x05C: reserved
58    _reserved4: [u32; 3],
59    /// 0x060 Interrupt status
60    interrupt_status: ReadOnly<u32, InterruptStatus::Register>,
61    /// 0x064 Interrupt acknowledge
62    interrupt_ack: WriteOnly<u32, InterruptStatus::Register>,
63    // 0x068 - 0x06C: reserved
64    _reserved5: [u32; 2],
65    /// 0x070 Device status
66    device_status: ReadWrite<u32, DeviceStatus::Register>,
67    // 0x074 - 0x07C: reserved
68    _reserved6: [u32; 3],
69    /// 0x080 - 0x084 Virtual queue's Descriptor Area 64-bit long physical address
70    queue_desc_low: WriteOnly<u32>,
71    queue_desc_high: WriteOnly<u32>,
72    // 0x088 - 0x08C: reserved
73    _reserved7: [u32; 2],
74    /// 0x090 - 0x094 Virtual queue's Driver Area 64-bit long physical address
75    queue_driver_low: WriteOnly<u32>,
76    queue_driver_high: WriteOnly<u32>,
77    // 0x098 - 0x09C: reserved
78    _reserved8: [u32; 2],
79    /// 0x0A0 - 0x0A4 Virtual queue's Device Area 64-bit long physical address
80    queue_device_low: WriteOnly<u32>,
81    queue_device_high: WriteOnly<u32>,
82    // 0x0A8 - 0x0AC: reserved
83    _reserved9: [u32; 21],
84    /// 0x0FC Configuration atomicity value
85    config_generation: ReadOnly<u32>,
86    /// 0x100 - 0x19C device configuration space
87    ///
88    /// This is individually defined per device, with a variable
89    /// size. TODO: How to address this properly? Just hand around
90    /// addresses to this?
91    config: [u32; 40],
92}
93
94register_bitfields![u32,
95    DeviceStatus [
96        Acknowledge OFFSET(0) NUMBITS(1) [],
97        Driver OFFSET(1) NUMBITS(1) [],
98        Failed OFFSET(7) NUMBITS(1) [],
99        FeaturesOk OFFSET(3) NUMBITS(1) [],
100        DriverOk OFFSET(2) NUMBITS(1) [],
101        DeviceNeedsReset OFFSET(6) NUMBITS(1) []
102    ],
103    DeviceFeatures [
104        // TODO
105        Dummy OFFSET(0) NUMBITS(1) []
106    ],
107    InterruptStatus [
108        UsedBuffer OFFSET(0) NUMBITS(1) [],
109        ConfigChange OFFSET(1) NUMBITS(1) []
110    ]
111];
112
113register_bitfields![u64,
114    TransportFeatures [
115        RingIndirectDesc OFFSET(28) NUMBITS(1) [],
116        RingEventIdx OFFSET(29) NUMBITS(1) [],
117        Version1 OFFSET(32) NUMBITS(1) [],
118        AccessPlatform OFFSET(33) NUMBITS(1) [],
119        RingPacked OFFSET(34) NUMBITS(1) [],
120        InOrder OFFSET(35) NUMBITS(1) [],
121        OrderPlatform OFFSET(36) NUMBITS(1) [],
122        SRIOV OFFSET(37) NUMBITS(1) []
123    ]
124];
125
126pub struct VirtIOMMIODevice {
127    regs: StaticRef<VirtIOMMIODeviceRegisters>,
128    device_type: OptionalCell<VirtIODeviceType>,
129    queues: OptionalCell<&'static [&'static dyn Virtqueue]>,
130}
131
132impl VirtIOMMIODevice {
133    pub const fn new(regs: StaticRef<VirtIOMMIODeviceRegisters>) -> VirtIOMMIODevice {
134        VirtIOMMIODevice {
135            regs,
136            device_type: OptionalCell::empty(),
137            queues: OptionalCell::empty(),
138        }
139    }
140
141    pub fn handle_interrupt(&self) {
142        assert!(self.queues.is_some());
143
144        let isr = self.regs.interrupt_status.extract();
145        // Acknowledge all interrupts immediately so that the interrupts is deasserted
146        self.regs.interrupt_ack.set(isr.get());
147
148        if isr.is_set(InterruptStatus::UsedBuffer) {
149            // Iterate over all queues, checking for new buffers in
150            // the used ring
151            self.queues.map(|queues| {
152                for queue in queues.iter() {
153                    queue.used_interrupt();
154                }
155            });
156        }
157
158        if isr.is_set(InterruptStatus::ConfigChange) {
159            // TODO: this should probably be handled?
160        }
161    }
162
163    /// Partial initialization routine as per 4.2.3.1 MMIO-specific
164    /// device initialization
165    ///
166    /// This can be used to query the VirtIO transport information
167    /// (e.g. whether it's a supported transport and the attached
168    /// device)
169    pub fn query(&self) -> Option<VirtIODeviceType> {
170        // Verify that we are talking to a VirtIO MMIO device...
171        if self.regs.magic_value.get() != u32::from_le_bytes(VIRTIO_MAGIC_VALUE) {
172            panic!("Not a VirtIO MMIO device");
173        }
174
175        // with version 2
176        if self.regs.device_version.get() != 0x0002 {
177            panic!(
178                "Unknown VirtIO MMIO device version: {}",
179                self.regs.device_version.get()
180            );
181        }
182
183        // Extract the device type
184        VirtIODeviceType::from_device_id(self.regs.device_id.get())
185    }
186}
187
188impl VirtIOTransport for VirtIOMMIODevice {
189    fn initialize(
190        &self,
191        driver: &dyn VirtIODeviceDriver,
192        queues: &'static [&'static dyn Virtqueue],
193    ) -> Result<VirtIODeviceType, VirtIOInitializationError> {
194        // Initialization routine as per 4.2.3.1 MMIO-specific device
195        // initialization
196
197        // Verify that we are talking to a VirtIO MMIO device...
198        if self.regs.magic_value.get() != u32::from_le_bytes(VIRTIO_MAGIC_VALUE) {
199            return Err(VirtIOInitializationError::NotAVirtIODevice);
200        }
201
202        // with version 2
203        if self.regs.device_version.get() != 0x0002 {
204            return Err(VirtIOInitializationError::InvalidTransportVersion);
205        }
206
207        // Extract the device type, which will later function as an indicator
208        // for initialized
209        let device_id = self.regs.device_id.get();
210        let device_type = VirtIODeviceType::from_device_id(device_id)
211            .ok_or(VirtIOInitializationError::UnknownDeviceType(device_id))?;
212
213        if device_type != driver.device_type() {
214            return Err(VirtIOInitializationError::IncompatibleDriverDeviceType(
215                device_type,
216            ));
217        }
218
219        // All further initialization as per 3.1 Device Initialization
220
221        // 1. Reset the device (by writing 0x0 to the device status register)
222        self.regs.device_status.set(0x0000);
223
224        // 2. Set the ACKNOWLEDGE status bit: the guest OS has noticed the
225        // device
226        self.regs
227            .device_status
228            .modify(DeviceStatus::Acknowledge::SET);
229
230        // 3. Set the DRIVER status bit: the guest OS knows how to drive the
231        // device
232        //
233        // TODO: Maybe not always the case?
234        self.regs.device_status.modify(DeviceStatus::Driver::SET);
235
236        // 4. Read device feature bits, write the subset of feature bits
237        // understood by OS & driver to the device
238        //
239        // Feature bits 0-23 are for the driver, 24-37 for the transport &
240        // queue, 38 and above reserved. The caller may therefore only negotiate
241        // bits 0-23 using the supplied closure, others are possibly initialized
242        // by us.
243        //
244        // The features must be read 32 bits at a time, which are chosen using
245        // DeviceFeaturesSel.
246
247        // Read the virtual 64-bit register
248        //
249        // This is guaranteed to be consistent, the device MUST NOT change
250        // its features during operation
251        self.regs.device_features_sel.set(0);
252        let mut device_features_reg: u64 = self.regs.device_features.get() as u64;
253        self.regs.device_features_sel.set(1);
254        device_features_reg |= (self.regs.device_features.get() as u64) << 32;
255
256        // Negotiate the transport features
257        let offered_transport_features: InMemoryRegister<u64, TransportFeatures::Register> =
258            InMemoryRegister::new(device_features_reg);
259        let selected_transport_features: InMemoryRegister<u64, TransportFeatures::Register> =
260            InMemoryRegister::new(0x0000000000000000);
261
262        // Sanity check: Version1 must be offered AND accepted
263        if !offered_transport_features.is_set(TransportFeatures::Version1) {
264            return Err(VirtIOInitializationError::InvalidVirtIOVersion);
265        } else {
266            selected_transport_features.modify(TransportFeatures::Version1::SET);
267        }
268
269        // Negotiate the driver features. The driver can only select feature
270        // bits for the specific device type, which are assigned to be bits with
271        // indices in the range of 0 to 23.
272        let driver_negotiated =
273            if let Some(nf) = driver.negotiate_features(device_features_reg & 0xFFF) {
274                // Mask the driver's response by the device-specific feature bits.
275                nf & 0xFFF
276            } else {
277                // The driver does not like the offered features, indicate this
278                // failure to the device and report an error:
279                self.regs.device_status.modify(DeviceStatus::Failed::SET);
280                return Err(VirtIOInitializationError::FeatureNegotiationFailed {
281                    offered: offered_transport_features.get(),
282                    accepted: None,
283                });
284            };
285
286        let selected_features = selected_transport_features.get() | driver_negotiated;
287
288        // Write the virtual 64-bit register
289        self.regs.driver_features_sel.set(0);
290        self.regs
291            .driver_features
292            .set((selected_features & 0xFFFF) as u32);
293        self.regs.driver_features_sel.set(1);
294        self.regs
295            .driver_features
296            .set((selected_features >> 32 & 0xFFFF) as u32);
297
298        // 5. Set the FEATURES_OK status bit. We MUST NOT accept new feature
299        // bits after this step.
300        self.regs
301            .device_status
302            .modify(DeviceStatus::FeaturesOk::SET);
303
304        // 6. Re-read device status to ensure that FEATURES_OK is still set,
305        // otherwise the drive does not support the subset of features & is
306        // unusable.
307        if !self.regs.device_status.is_set(DeviceStatus::FeaturesOk) {
308            // The device does not like the accepted features, indicate
309            // this failure to the device and report an error:
310            self.regs.device_status.modify(DeviceStatus::Failed::SET);
311            return Err(VirtIOInitializationError::FeatureNegotiationFailed {
312                offered: offered_transport_features.get(),
313                accepted: Some(selected_features),
314            });
315        }
316
317        // 7. Perform device specific setup
318        //
319        // A device has a number of virtqueues it supports. We try to initialize
320        // all virtqueues passed in as the `queues` parameter, and ignore others
321        // potentially required by the device. If the `queues` parameter
322        // provides more queues than the device can take, abort and fail the
323        // configuration. The device should not use the queues until fully
324        // configured.
325        //
326        // Implementation of the algorithms of 4.2.3.2
327        for (index, queue) in queues.iter().enumerate() {
328            // Select the queue
329            self.regs.queue_sel.set(index as u32);
330
331            // Verify that the queue is not already in use (shouldn't be, since
332            // we've just reset)
333            if self.regs.queue_ready.get() != 0 {
334                self.regs.device_status.modify(DeviceStatus::Failed::SET);
335                return Err(VirtIOInitializationError::DeviceError);
336            }
337
338            // Read the maximum queue size (number of elements) from
339            // QueueNumMax. If the returned value is zero, the queue is not
340            // available
341            let queue_num_max = self.regs.queue_num_max.get() as usize;
342            if queue_num_max == 0 {
343                self.regs.device_status.modify(DeviceStatus::Failed::SET);
344                return Err(VirtIOInitializationError::VirtqueueNotAvailable(index));
345            }
346
347            // Negotiate the queue size, choosing a value fit for QueueNumMax
348            // and the buffer sizes of the passed in queue. This sets the
349            // negotiated value in the queue for later operation.
350            let queue_num = queue.negotiate_queue_size(queue_num_max);
351
352            // Zero the queue memory
353            queue.initialize(index as u32, queue_num);
354
355            // Notify the device about the queue size
356            self.regs.queue_num.set(queue_num as u32);
357
358            // Write the physical queue addresses
359            let addrs = queue.physical_addresses();
360            self.regs.queue_desc_low.set(addrs.descriptor_area as u32);
361            self.regs
362                .queue_desc_high
363                .set((addrs.descriptor_area >> 32) as u32);
364            self.regs.queue_driver_low.set(addrs.driver_area as u32);
365            self.regs
366                .queue_driver_high
367                .set((addrs.driver_area >> 32) as u32);
368            self.regs.queue_device_low.set(addrs.device_area as u32);
369            self.regs
370                .queue_device_high
371                .set((addrs.device_area >> 32) as u32);
372
373            // Set queue to ready
374            self.regs.queue_ready.set(0x0001);
375        }
376
377        // Store the queue references for later usage
378        self.queues.set(queues);
379
380        // Call the hook pre "device-initialization" (setting DRIVER_OK).
381        driver
382            .pre_device_initialization()
383            .map_err(VirtIOInitializationError::DriverPreInitializationError)?;
384
385        // 8. Set the DRIVER_OK status bit
386        self.regs.device_status.modify(DeviceStatus::DriverOk::SET);
387
388        // The device is now "live"
389        self.device_type.set(device_type);
390
391        driver.device_initialized().map_err(|err| {
392            VirtIOInitializationError::DriverInitializationError(device_type, err)
393        })?;
394
395        Ok(device_type)
396    }
397
398    fn queue_notify(&self, queue_id: u32) {
399        // TODO: better way to report an error here? This shouldn't usually be
400        // triggered.
401        assert!(
402            queue_id
403                < self
404                    .queues
405                    .get()
406                    .expect("VirtIO transport not initialized")
407                    .len() as u32
408        );
409
410        self.regs.queue_notify.set(queue_id);
411    }
412}