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                if self.driver_feature_select < 2 {
192                    let features: u64 = (value as u64) << (self.driver_feature_select * 32);
193                    device.ack_features(features);
194                    for queue in queues.iter_mut() {
195                        queue.ack_features(features);
196                    }
197                } else {
198                    warn!(
199                        "invalid ack_features (page {}, value 0x{:x})",
200                        self.driver_feature_select, value
201                    );
202                }
203            }
204            0x20 => self.with_queue_mut(queues, |q| lo!(q, desc_table, set_desc_table, value)),
205            0x24 => self.with_queue_mut(queues, |q| hi!(q, desc_table, set_desc_table, value)),
206            0x28 => self.with_queue_mut(queues, |q| lo!(q, avail_ring, set_avail_ring, value)),
207            0x2c => self.with_queue_mut(queues, |q| hi!(q, avail_ring, set_avail_ring, value)),
208            0x30 => self.with_queue_mut(queues, |q| lo!(q, used_ring, set_used_ring, value)),
209            0x34 => self.with_queue_mut(queues, |q| hi!(q, used_ring, set_used_ring, value)),
210            _ => {
211                warn!("invalid virtio register dword write: 0x{:x}", offset);
212            }
213        }
214    }
215
216    fn read_common_config_qword(&self, _offset: u64) -> u64 {
217        0 // Assume the guest has no reason to read write-only registers.
218    }
219
220    fn write_common_config_qword(&mut self, offset: u64, value: u64, queues: &mut [QueueConfig]) {
221        match offset {
222            0x20 => self.with_queue_mut(queues, |q| q.set_desc_table(GuestAddress(value))),
223            0x28 => self.with_queue_mut(queues, |q| q.set_avail_ring(GuestAddress(value))),
224            0x30 => self.with_queue_mut(queues, |q| q.set_used_ring(GuestAddress(value))),
225            _ => {
226                warn!("invalid virtio register qword write: 0x{:x}", offset);
227            }
228        }
229    }
230
231    fn with_queue<U, F>(&self, queues: &[QueueConfig], f: F) -> Option<U>
232    where
233        F: FnOnce(&QueueConfig) -> U,
234    {
235        queues.get(self.queue_select as usize).map(f)
236    }
237
238    fn with_queue_mut<F: FnOnce(&mut QueueConfig)>(&self, queues: &mut [QueueConfig], f: F) {
239        if let Some(queue) = queues.get_mut(self.queue_select as usize) {
240            f(queue);
241        }
242    }
243}
244
245#[cfg(test)]
246mod tests {
247    use std::collections::BTreeMap;
248
249    use base::RawDescriptor;
250    use vm_memory::GuestMemory;
251
252    use super::*;
253
254    struct DummyDevice(DeviceType);
255    const QUEUE_SIZE: u16 = 256;
256    const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
257    const DUMMY_FEATURES: u64 = 0x5555_aaaa;
258    impl VirtioDevice for DummyDevice {
259        fn keep_rds(&self) -> Vec<RawDescriptor> {
260            Vec::new()
261        }
262        fn device_type(&self) -> DeviceType {
263            self.0
264        }
265        fn queue_max_sizes(&self) -> &[u16] {
266            QUEUE_SIZES
267        }
268        fn activate(
269            &mut self,
270            _mem: GuestMemory,
271            _interrupt: Interrupt,
272            _queues: BTreeMap<usize, Queue>,
273        ) -> anyhow::Result<()> {
274            Ok(())
275        }
276        fn features(&self) -> u64 {
277            DUMMY_FEATURES
278        }
279    }
280
281    #[test]
282    fn write_base_regs() {
283        let mut regs = VirtioPciCommonConfig {
284            driver_status: 0xaa,
285            config_generation: 0x55,
286            device_feature_select: 0x0,
287            driver_feature_select: 0x0,
288            queue_select: 0xff,
289            msix_config: 0x00,
290        };
291
292        let dev = &mut DummyDevice(DeviceType::Rng) as &mut dyn VirtioDevice;
293        let mut queues = Vec::new();
294
295        // Can set all bits of driver_status.
296        regs.write(0x14, &[0x55], &mut queues, dev);
297        let mut read_back = vec![0x00];
298        regs.read(0x14, &mut read_back, &mut queues, dev);
299        assert_eq!(read_back[0], 0x55);
300
301        // The config generation register is read only.
302        regs.write(0x15, &[0xaa], &mut queues, dev);
303        let mut read_back = vec![0x00];
304        regs.read(0x15, &mut read_back, &mut queues, dev);
305        assert_eq!(read_back[0], 0x55);
306
307        // Device features is read-only and passed through from the device.
308        regs.write(0x04, &[0, 0, 0, 0], &mut queues, dev);
309        let mut read_back = [0u8; 4];
310        regs.read(0x04, &mut read_back, &mut queues, dev);
311        assert_eq!(u32::from_le_bytes(read_back), DUMMY_FEATURES as u32);
312
313        // Feature select registers are read/write.
314        regs.write(0x00, &[1, 2, 3, 4], &mut queues, dev);
315        let mut read_back = [0u8; 4];
316        regs.read(0x00, &mut read_back, &mut queues, dev);
317        assert_eq!(u32::from_le_bytes(read_back), 0x0403_0201);
318        regs.write(0x08, &[1, 2, 3, 4], &mut queues, dev);
319        let mut read_back = [0u8; 4];
320        regs.read(0x08, &mut read_back, &mut queues, dev);
321        assert_eq!(u32::from_le_bytes(read_back), 0x0403_0201);
322
323        // 'queue_select' can be read and written.
324        regs.write(0x16, &[0xaa, 0x55], &mut queues, dev);
325        let mut read_back = vec![0x00, 0x00];
326        regs.read(0x16, &mut read_back, &mut queues, dev);
327        assert_eq!(read_back[0], 0xaa);
328        assert_eq!(read_back[1], 0x55);
329    }
330}