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