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