xref: /aosp_15_r20/external/crosvm/devices/src/virtio/virtio_pci_common_config.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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 
5 use std::convert::TryInto;
6 
7 use base::warn;
8 use serde::Deserialize;
9 use serde::Serialize;
10 use vm_memory::GuestAddress;
11 
12 use 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)]
37 pub 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 
46 impl VirtioPciCommonConfig {
read( &mut self, offset: u64, data: &mut [u8], queues: &mut [QueueConfig], device: &mut dyn VirtioDevice, )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 
write( &mut self, offset: u64, data: &[u8], queues: &mut [QueueConfig], device: &mut dyn VirtioDevice, )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 
read_common_config_byte(&self, offset: u64) -> u8105     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 
write_common_config_byte(&mut self, offset: u64, value: u8)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 
read_common_config_word(&self, offset: u64, queues: &[QueueConfig]) -> u16123     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 
write_common_config_word(&mut self, offset: u64, value: u16, queues: &mut [QueueConfig])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 
read_common_config_dword(&self, offset: u64, device: &dyn VirtioDevice) -> u32152     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 
write_common_config_dword( &mut self, offset: u64, value: u32, queues: &mut [QueueConfig], device: &mut dyn VirtioDevice, )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 
read_common_config_qword(&self, _offset: u64) -> u64216     fn read_common_config_qword(&self, _offset: u64) -> u64 {
217         0 // Assume the guest has no reason to read write-only registers.
218     }
219 
write_common_config_qword(&mut self, offset: u64, value: u64, queues: &mut [QueueConfig])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 
with_queue<U, F>(&self, queues: &[QueueConfig], f: F) -> Option<U> where F: FnOnce(&QueueConfig) -> U,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 
with_queue_mut<F: FnOnce(&mut QueueConfig)>(&self, queues: &mut [QueueConfig], f: F)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)]
246 mod 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 {
keep_rds(&self) -> Vec<RawDescriptor>259         fn keep_rds(&self) -> Vec<RawDescriptor> {
260             Vec::new()
261         }
device_type(&self) -> DeviceType262         fn device_type(&self) -> DeviceType {
263             self.0
264         }
queue_max_sizes(&self) -> &[u16]265         fn queue_max_sizes(&self) -> &[u16] {
266             QUEUE_SIZES
267         }
activate( &mut self, _mem: GuestMemory, _interrupt: Interrupt, _queues: BTreeMap<usize, Queue>, ) -> anyhow::Result<()>268         fn activate(
269             &mut self,
270             _mem: GuestMemory,
271             _interrupt: Interrupt,
272             _queues: BTreeMap<usize, Queue>,
273         ) -> anyhow::Result<()> {
274             Ok(())
275         }
features(&self) -> u64276         fn features(&self) -> u64 {
277             DUMMY_FEATURES
278         }
279     }
280 
281     #[test]
write_base_regs()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 }
331