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 device
164 /// initialization
165 ///
166 /// This can be used to query the VirtIO transport information (e.g. whether
167 /// it's a supported transport and the attached device).
168 ///
169 /// Returns `Ok(VirtIODeviceType)` if this MMIO device instance hosts a
170 /// known VirtIO device type, and `Err(device_id: u32)` with the raw device
171 /// ID otherwise. A device ID of `0` indicates that no active VirtIO device
172 /// is present at this MMIO address currently.
173 pub fn query(&self) -> Result<VirtIODeviceType, u32> {
174 // Verify that we are talking to a VirtIO MMIO device...
175 if self.regs.magic_value.get() != u32::from_le_bytes(VIRTIO_MAGIC_VALUE) {
176 panic!("Not a VirtIO MMIO device");
177 }
178
179 // with version 2
180 if self.regs.device_version.get() != 0x0002 {
181 panic!(
182 "Unknown VirtIO MMIO device version: {}",
183 self.regs.device_version.get()
184 );
185 }
186
187 // Extract the device type
188 let device_id = self.regs.device_id.get();
189
190 // Try to decode the device ID into a `VirtIODeviceType`, and otherwise
191 // return the raw device ID number in the `Err` variant.
192 VirtIODeviceType::from_device_id(device_id).ok_or(device_id)
193 }
194}
195
196impl VirtIOTransport for VirtIOMMIODevice {
197 fn initialize(
198 &self,
199 driver: &dyn VirtIODeviceDriver,
200 queues: &'static [&'static dyn Virtqueue],
201 ) -> Result<VirtIODeviceType, VirtIOInitializationError> {
202 // Initialization routine as per 4.2.3.1 MMIO-specific device
203 // initialization
204
205 // Verify that we are talking to a VirtIO MMIO device...
206 if self.regs.magic_value.get() != u32::from_le_bytes(VIRTIO_MAGIC_VALUE) {
207 return Err(VirtIOInitializationError::NotAVirtIODevice);
208 }
209
210 // with version 2
211 if self.regs.device_version.get() != 0x0002 {
212 return Err(VirtIOInitializationError::InvalidTransportVersion);
213 }
214
215 // Extract the device type, which will later function as an indicator
216 // for initialized
217 let device_id = self.regs.device_id.get();
218 let device_type = VirtIODeviceType::from_device_id(device_id)
219 .ok_or(VirtIOInitializationError::UnknownDeviceType(device_id))?;
220
221 if device_type != driver.device_type() {
222 return Err(VirtIOInitializationError::IncompatibleDriverDeviceType(
223 device_type,
224 ));
225 }
226
227 // All further initialization as per 3.1 Device Initialization
228
229 // 1. Reset the device (by writing 0x0 to the device status register)
230 self.regs.device_status.set(0x0000);
231
232 // 2. Set the ACKNOWLEDGE status bit: the guest OS has noticed the
233 // device
234 self.regs
235 .device_status
236 .modify(DeviceStatus::Acknowledge::SET);
237
238 // 3. Set the DRIVER status bit: the guest OS knows how to drive the
239 // device
240 //
241 // TODO: Maybe not always the case?
242 self.regs.device_status.modify(DeviceStatus::Driver::SET);
243
244 // 4. Read device feature bits, write the subset of feature bits
245 // understood by OS & driver to the device
246 //
247 // Feature bits 0-23 are for the driver, 24-37 for the transport &
248 // queue, 38 and above reserved. The caller may therefore only negotiate
249 // bits 0-23 using the supplied closure, others are possibly initialized
250 // by us.
251 //
252 // The features must be read 32 bits at a time, which are chosen using
253 // DeviceFeaturesSel.
254
255 // Read the virtual 64-bit register
256 //
257 // This is guaranteed to be consistent, the device MUST NOT change
258 // its features during operation
259 self.regs.device_features_sel.set(0);
260 let mut device_features_reg: u64 = self.regs.device_features.get() as u64;
261 self.regs.device_features_sel.set(1);
262 device_features_reg |= (self.regs.device_features.get() as u64) << 32;
263
264 // Negotiate the transport features
265 let offered_transport_features: InMemoryRegister<u64, TransportFeatures::Register> =
266 InMemoryRegister::new(device_features_reg);
267 let selected_transport_features: InMemoryRegister<u64, TransportFeatures::Register> =
268 InMemoryRegister::new(0x0000000000000000);
269
270 // Sanity check: Version1 must be offered AND accepted
271 if !offered_transport_features.is_set(TransportFeatures::Version1) {
272 return Err(VirtIOInitializationError::InvalidVirtIOVersion);
273 } else {
274 selected_transport_features.modify(TransportFeatures::Version1::SET);
275 }
276
277 // Negotiate the driver features. The driver can only select feature
278 // bits for the specific device type, which are assigned to be bits with
279 // indices in the range of 0 to 23.
280 let driver_negotiated =
281 if let Some(nf) = driver.negotiate_features(device_features_reg & 0xFFF) {
282 // Mask the driver's response by the device-specific feature bits.
283 nf & 0xFFF
284 } else {
285 // The driver does not like the offered features, indicate this
286 // failure to the device and report an error:
287 self.regs.device_status.modify(DeviceStatus::Failed::SET);
288 return Err(VirtIOInitializationError::FeatureNegotiationFailed {
289 offered: offered_transport_features.get(),
290 accepted: None,
291 });
292 };
293
294 let selected_features = selected_transport_features.get() | driver_negotiated;
295
296 // Write the virtual 64-bit register
297 self.regs.driver_features_sel.set(0);
298 self.regs
299 .driver_features
300 .set((selected_features & 0xFFFF) as u32);
301 self.regs.driver_features_sel.set(1);
302 self.regs
303 .driver_features
304 .set((selected_features >> 32 & 0xFFFF) as u32);
305
306 // 5. Set the FEATURES_OK status bit. We MUST NOT accept new feature
307 // bits after this step.
308 self.regs
309 .device_status
310 .modify(DeviceStatus::FeaturesOk::SET);
311
312 // 6. Re-read device status to ensure that FEATURES_OK is still set,
313 // otherwise the drive does not support the subset of features & is
314 // unusable.
315 if !self.regs.device_status.is_set(DeviceStatus::FeaturesOk) {
316 // The device does not like the accepted features, indicate
317 // this failure to the device and report an error:
318 self.regs.device_status.modify(DeviceStatus::Failed::SET);
319 return Err(VirtIOInitializationError::FeatureNegotiationFailed {
320 offered: offered_transport_features.get(),
321 accepted: Some(selected_features),
322 });
323 }
324
325 // 7. Perform device specific setup
326 //
327 // A device has a number of virtqueues it supports. We try to initialize
328 // all virtqueues passed in as the `queues` parameter, and ignore others
329 // potentially required by the device. If the `queues` parameter
330 // provides more queues than the device can take, abort and fail the
331 // configuration. The device should not use the queues until fully
332 // configured.
333 //
334 // Implementation of the algorithms of 4.2.3.2
335 for (index, queue) in queues.iter().enumerate() {
336 // Select the queue
337 self.regs.queue_sel.set(index as u32);
338
339 // Verify that the queue is not already in use (shouldn't be, since
340 // we've just reset)
341 if self.regs.queue_ready.get() != 0 {
342 self.regs.device_status.modify(DeviceStatus::Failed::SET);
343 return Err(VirtIOInitializationError::DeviceError);
344 }
345
346 // Read the maximum queue size (number of elements) from
347 // QueueNumMax. If the returned value is zero, the queue is not
348 // available
349 let queue_num_max = self.regs.queue_num_max.get() as usize;
350 if queue_num_max == 0 {
351 self.regs.device_status.modify(DeviceStatus::Failed::SET);
352 return Err(VirtIOInitializationError::VirtqueueNotAvailable(index));
353 }
354
355 // Negotiate the queue size, choosing a value fit for QueueNumMax
356 // and the buffer sizes of the passed in queue. This sets the
357 // negotiated value in the queue for later operation.
358 let queue_num = queue.negotiate_queue_size(queue_num_max);
359
360 // Zero the queue memory
361 queue.initialize(index as u32, queue_num);
362
363 // Notify the device about the queue size
364 self.regs.queue_num.set(queue_num as u32);
365
366 // Write the physical queue addresses
367 let addrs = queue.physical_addresses();
368 self.regs.queue_desc_low.set(addrs.descriptor_area as u32);
369 self.regs
370 .queue_desc_high
371 .set((addrs.descriptor_area >> 32) as u32);
372 self.regs.queue_driver_low.set(addrs.driver_area as u32);
373 self.regs
374 .queue_driver_high
375 .set((addrs.driver_area >> 32) as u32);
376 self.regs.queue_device_low.set(addrs.device_area as u32);
377 self.regs
378 .queue_device_high
379 .set((addrs.device_area >> 32) as u32);
380
381 // Set queue to ready
382 self.regs.queue_ready.set(0x0001);
383 }
384
385 // Store the queue references for later usage
386 self.queues.set(queues);
387
388 // Call the hook pre "device-initialization" (setting DRIVER_OK).
389 driver
390 .pre_device_initialization()
391 .map_err(VirtIOInitializationError::DriverPreInitializationError)?;
392
393 // 8. Set the DRIVER_OK status bit
394 self.regs.device_status.modify(DeviceStatus::DriverOk::SET);
395
396 // The device is now "live"
397 self.device_type.set(device_type);
398
399 driver.device_initialized().map_err(|err| {
400 VirtIOInitializationError::DriverInitializationError(device_type, err)
401 })?;
402
403 Ok(device_type)
404 }
405
406 fn queue_notify(&self, queue_id: u32) {
407 // TODO: better way to report an error here? This shouldn't usually be
408 // triggered.
409 assert!(
410 queue_id
411 < self
412 .queues
413 .get()
414 .expect("VirtIO transport not initialized")
415 .len() as u32
416 );
417
418 self.regs.queue_notify.set(queue_id);
419 }
420}