devices/virtio/
virtio_pci_common_config.rs

1// Copyright 2018 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::convert::TryInto;
6
7use base::warn;
8use serde::Deserialize;
9use serde::Serialize;
10use vm_memory::GuestAddress;
11
12use super::*;
13
14/// Contains the data for reading and writing the common configuration structure of a virtio PCI
15/// device.
16///
17/// * Registers:
18///   * About the whole device.
19///     * le32 device_feature_select;     // read-write
20///     * le32 device_feature;            // read-only for driver
21///     * le32 driver_feature_select;     // read-write
22///     * le32 driver_feature;            // read-write
23///     * le16 msix_config;               // read-write
24///     * le16 num_queues;                // read-only for driver
25///     * u8 device_status;               // read-write (driver_status)
26///     * u8 config_generation;           // read-only for driver
27///   * About a specific virtqueue.
28///     * le16 queue_select;              // read-write
29///     * le16 queue_size;                // read-write, power of 2, or 0.
30///     * le16 queue_msix_vector;         // read-write
31///     * le16 queue_enable;              // read-write (Ready)
32///     * le16 queue_notify_off;          // read-only for driver
33///     * le64 queue_desc;                // read-write
34///     * le64 queue_avail;               // read-write
35///     * le64 queue_used;                // read-write
36#[derive(Copy, Clone, Serialize, Deserialize)]
37pub struct VirtioPciCommonConfig {
38    pub driver_status: u8,
39    pub config_generation: u8,
40    pub device_feature_select: u32,
41    pub driver_feature_select: u32,
42    pub queue_select: u16,
43    pub msix_config: u16,
44}
45
46impl VirtioPciCommonConfig {
47    pub fn read(
48        &mut self,
49        offset: u64,
50        data: &mut [u8],
51        queues: &mut [QueueConfig],
52        device: &mut dyn VirtioDevice,
53    ) {
54        match data.len() {
55            1 => {
56                let v = self.read_common_config_byte(offset);
57                data[0] = v;
58            }
59            2 => {
60                let v = self.read_common_config_word(offset, queues);
61                data.copy_from_slice(&v.to_le_bytes());
62            }
63            4 => {
64                let v = self.read_common_config_dword(offset, device);
65                data.copy_from_slice(&v.to_le_bytes());
66            }
67            8 => {
68                let v = self.read_common_config_qword(offset);
69                data.copy_from_slice(&v.to_le_bytes());
70            }
71            _ => (),
72        }
73    }
74
75    pub fn write(
76        &mut self,
77        offset: u64,
78        data: &[u8],
79        queues: &mut [QueueConfig],
80        device: &mut dyn VirtioDevice,
81    ) {
82        match data.len() {
83            1 => self.write_common_config_byte(offset, data[0]),
84            2 => self.write_common_config_word(
85                offset,
86                // This unwrap (and those below) cannot fail since data.len() is checked.
87                u16::from_le_bytes(data.try_into().unwrap()),
88                queues,
89            ),
90            4 => self.write_common_config_dword(
91                offset,
92                u32::from_le_bytes(data.try_into().unwrap()),
93                queues,
94                device,
95            ),
96            8 => self.write_common_config_qword(
97                offset,
98                u64::from_le_bytes(data.try_into().unwrap()),
99                queues,
100            ),
101            _ => (),
102        }
103    }
104
105    fn read_common_config_byte(&self, offset: u64) -> u8 {
106        // The driver is only allowed to do aligned, properly sized access.
107        match offset {
108            0x14 => self.driver_status,
109            0x15 => self.config_generation,
110            _ => 0,
111        }
112    }
113
114    fn write_common_config_byte(&mut self, offset: u64, value: u8) {
115        match offset {
116            0x14 => self.driver_status = value,
117            _ => {
118                warn!("invalid virtio config byt access: 0x{:x}", offset);
119            }
120        }
121    }
122
123    fn read_common_config_word(&self, offset: u64, queues: &[QueueConfig]) -> u16 {
124        match offset {
125            0x10 => self.msix_config,
126            0x12 => queues.len() as u16, // num_queues
127            0x16 => self.queue_select,
128            0x18 => self.with_queue(queues, |q| q.size()).unwrap_or(0),
129            0x1a => self.with_queue(queues, |q| q.vector()).unwrap_or(0),
130            0x1c => self
131                .with_queue(queues, |q| q.ready())
132                .unwrap_or(false)
133                .into(),
134            0x1e => self.queue_select, // notify_off
135            _ => 0,
136        }
137    }
138
139    fn write_common_config_word(&mut self, offset: u64, value: u16, queues: &mut [QueueConfig]) {
140        match offset {
141            0x10 => self.msix_config = value,
142            0x16 => self.queue_select = value,
143            0x18 => self.with_queue_mut(queues, |q| q.set_size(value)),
144            0x1a => self.with_queue_mut(queues, |q| q.set_vector(value)),
145            0x1c => self.with_queue_mut(queues, |q| q.set_ready(value == 1)),
146            _ => {
147                warn!("invalid virtio register word write: 0x{:x}", offset);
148            }
149        }
150    }
151
152    fn read_common_config_dword(&self, offset: u64, device: &dyn VirtioDevice) -> u32 {
153        match offset {
154            0x00 => self.device_feature_select,
155            0x04 => {
156                // Only 64 bits of features (2 pages) are defined for now, so limit
157                // device_feature_select to avoid shifting by 64 or more bits.
158                if self.device_feature_select < 2 {
159                    (device.features() >> (self.device_feature_select * 32)) as u32
160                } else {
161                    0
162                }
163            }
164            0x08 => self.driver_feature_select,
165            _ => 0,
166        }
167    }
168
169    fn write_common_config_dword(
170        &mut self,
171        offset: u64,
172        value: u32,
173        queues: &mut [QueueConfig],
174        device: &mut dyn VirtioDevice,
175    ) {
176        macro_rules! hi {
177            ($q:expr, $get:ident, $set:ident, $x:expr) => {
178                $q.$set(($q.$get() & 0xffffffff) | (($x as u64) << 32))
179            };
180        }
181        macro_rules! lo {
182            ($q:expr, $get:ident, $set:ident, $x:expr) => {
183                $q.$set(($q.$get() & !0xffffffff) | ($x as u64))
184            };
185        }
186
187        match offset {
188            0x00 => self.device_feature_select = value,
189            0x08 => self.driver_feature_select = value,
190            0x0c => {
191                // Only 64 bits of features (2 pages) are defined for now, so limit
192                // device_feature_select to avoid shifting by 64 or more bits.
193                if self.driver_feature_select < 2 {
194                    let features: u64 = (value as u64) << (self.driver_feature_select * 32);
195                    device.ack_features(features);
196                    for queue in queues.iter_mut() {
197                        queue.ack_features(features);
198                    }
199                } else {
200                    // The guest might try to write features outside of the first
201                    // 64-bit in a second 64-bit (validly), but these should be zero
202                    if value != 0 {
203                        warn!(
204                            "invalid ack_features (page {}, value 0x{:x})",
205                            self.driver_feature_select, value
206                        );
207                    }
208                }
209            }
210            0x20 => self.with_queue_mut(queues, |q| lo!(q, desc_table, set_desc_table, value)),
211            0x24 => self.with_queue_mut(queues, |q| hi!(q, desc_table, set_desc_table, value)),
212            0x28 => self.with_queue_mut(queues, |q| lo!(q, avail_ring, set_avail_ring, value)),
213            0x2c => self.with_queue_mut(queues, |q| hi!(q, avail_ring, set_avail_ring, value)),
214            0x30 => self.with_queue_mut(queues, |q| lo!(q, used_ring, set_used_ring, value)),
215            0x34 => self.with_queue_mut(queues, |q| hi!(q, used_ring, set_used_ring, value)),
216            _ => {
217                warn!("invalid virtio register dword write: 0x{:x}", offset);
218            }
219        }
220    }
221
222    fn read_common_config_qword(&self, _offset: u64) -> u64 {
223        0 // Assume the guest has no reason to read write-only registers.
224    }
225
226    fn write_common_config_qword(&mut self, offset: u64, value: u64, queues: &mut [QueueConfig]) {
227        match offset {
228            0x20 => self.with_queue_mut(queues, |q| q.set_desc_table(GuestAddress(value))),
229            0x28 => self.with_queue_mut(queues, |q| q.set_avail_ring(GuestAddress(value))),
230            0x30 => self.with_queue_mut(queues, |q| q.set_used_ring(GuestAddress(value))),
231            _ => {
232                warn!("invalid virtio register qword write: 0x{:x}", offset);
233            }
234        }
235    }
236
237    fn with_queue<U, F>(&self, queues: &[QueueConfig], f: F) -> Option<U>
238    where
239        F: FnOnce(&QueueConfig) -> U,
240    {
241        queues.get(self.queue_select as usize).map(f)
242    }
243
244    fn with_queue_mut<F: FnOnce(&mut QueueConfig)>(&self, queues: &mut [QueueConfig], f: F) {
245        if let Some(queue) = queues.get_mut(self.queue_select as usize) {
246            f(queue);
247        }
248    }
249}
250
251#[cfg(test)]
252mod tests {
253    use std::collections::BTreeMap;
254
255    use base::RawDescriptor;
256    use vm_memory::GuestMemory;
257
258    use super::*;
259
260    struct DummyDevice(DeviceType);
261    const QUEUE_SIZE: u16 = 256;
262    const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
263    const DUMMY_FEATURES: u64 = 0x5555_aaaa;
264    impl VirtioDevice for DummyDevice {
265        fn keep_rds(&self) -> Vec<RawDescriptor> {
266            Vec::new()
267        }
268        fn device_type(&self) -> DeviceType {
269            self.0
270        }
271        fn queue_max_sizes(&self) -> &[u16] {
272            QUEUE_SIZES
273        }
274        fn activate(
275            &mut self,
276            _mem: GuestMemory,
277            _interrupt: Interrupt,
278            _queues: BTreeMap<usize, Queue>,
279        ) -> anyhow::Result<()> {
280            Ok(())
281        }
282        fn features(&self) -> u64 {
283            DUMMY_FEATURES
284        }
285    }
286
287    #[test]
288    fn write_base_regs() {
289        let mut regs = VirtioPciCommonConfig {
290            driver_status: 0xaa,
291            config_generation: 0x55,
292            device_feature_select: 0x0,
293            driver_feature_select: 0x0,
294            queue_select: 0xff,
295            msix_config: 0x00,
296        };
297
298        let dev = &mut DummyDevice(DeviceType::Rng) as &mut dyn VirtioDevice;
299        let mut queues = Vec::new();
300
301        // Can set all bits of driver_status.
302        regs.write(0x14, &[0x55], &mut queues, dev);
303        let mut read_back = vec![0x00];
304        regs.read(0x14, &mut read_back, &mut queues, dev);
305        assert_eq!(read_back[0], 0x55);
306
307        // The config generation register is read only.
308        regs.write(0x15, &[0xaa], &mut queues, dev);
309        let mut read_back = vec![0x00];
310        regs.read(0x15, &mut read_back, &mut queues, dev);
311        assert_eq!(read_back[0], 0x55);
312
313        // Device features is read-only and passed through from the device.
314        regs.write(0x04, &[0, 0, 0, 0], &mut queues, dev);
315        let mut read_back = [0u8; 4];
316        regs.read(0x04, &mut read_back, &mut queues, dev);
317        assert_eq!(u32::from_le_bytes(read_back), DUMMY_FEATURES as u32);
318
319        // Feature select registers are read/write.
320        regs.write(0x00, &[1, 2, 3, 4], &mut queues, dev);
321        let mut read_back = [0u8; 4];
322        regs.read(0x00, &mut read_back, &mut queues, dev);
323        assert_eq!(u32::from_le_bytes(read_back), 0x0403_0201);
324        regs.write(0x08, &[1, 2, 3, 4], &mut queues, dev);
325        let mut read_back = [0u8; 4];
326        regs.read(0x08, &mut read_back, &mut queues, dev);
327        assert_eq!(u32::from_le_bytes(read_back), 0x0403_0201);
328
329        // 'queue_select' can be read and written.
330        regs.write(0x16, &[0xaa, 0x55], &mut queues, dev);
331        let mut read_back = vec![0x00, 0x00];
332        regs.read(0x16, &mut read_back, &mut queues, dev);
333        assert_eq!(read_back[0], 0xaa);
334        assert_eq!(read_back[1], 0x55);
335    }
336}