Path: blob/main/devices/src/virtio/virtio_pci_common_config.rs
5394 views
// Copyright 2018 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use std::convert::TryInto;56use base::warn;7use serde::Deserialize;8use serde::Serialize;9use vm_memory::GuestAddress;1011use super::*;1213/// Contains the data for reading and writing the common configuration structure of a virtio PCI14/// device.15///16/// * Registers:17/// * About the whole device.18/// * le32 device_feature_select; // read-write19/// * le32 device_feature; // read-only for driver20/// * le32 driver_feature_select; // read-write21/// * le32 driver_feature; // read-write22/// * le16 msix_config; // read-write23/// * le16 num_queues; // read-only for driver24/// * u8 device_status; // read-write (driver_status)25/// * u8 config_generation; // read-only for driver26/// * About a specific virtqueue.27/// * le16 queue_select; // read-write28/// * le16 queue_size; // read-write, power of 2, or 0.29/// * le16 queue_msix_vector; // read-write30/// * le16 queue_enable; // read-write (Ready)31/// * le16 queue_notify_off; // read-only for driver32/// * le64 queue_desc; // read-write33/// * le64 queue_avail; // read-write34/// * le64 queue_used; // read-write35#[derive(Copy, Clone, Serialize, Deserialize)]36pub struct VirtioPciCommonConfig {37pub driver_status: u8,38pub config_generation: u8,39pub device_feature_select: u32,40pub driver_feature_select: u32,41pub queue_select: u16,42pub msix_config: u16,43}4445impl VirtioPciCommonConfig {46pub fn read(47&mut self,48offset: u64,49data: &mut [u8],50queues: &mut [QueueConfig],51device: &mut dyn VirtioDevice,52) {53match data.len() {541 => {55let v = self.read_common_config_byte(offset);56data[0] = v;57}582 => {59let v = self.read_common_config_word(offset, queues);60data.copy_from_slice(&v.to_le_bytes());61}624 => {63let v = self.read_common_config_dword(offset, device);64data.copy_from_slice(&v.to_le_bytes());65}668 => {67let v = self.read_common_config_qword(offset);68data.copy_from_slice(&v.to_le_bytes());69}70_ => (),71}72}7374pub fn write(75&mut self,76offset: u64,77data: &[u8],78queues: &mut [QueueConfig],79device: &mut dyn VirtioDevice,80) {81match data.len() {821 => self.write_common_config_byte(offset, data[0]),832 => self.write_common_config_word(84offset,85// This unwrap (and those below) cannot fail since data.len() is checked.86u16::from_le_bytes(data.try_into().unwrap()),87queues,88),894 => self.write_common_config_dword(90offset,91u32::from_le_bytes(data.try_into().unwrap()),92queues,93device,94),958 => self.write_common_config_qword(96offset,97u64::from_le_bytes(data.try_into().unwrap()),98queues,99),100_ => (),101}102}103104fn read_common_config_byte(&self, offset: u64) -> u8 {105// The driver is only allowed to do aligned, properly sized access.106match offset {1070x14 => self.driver_status,1080x15 => self.config_generation,109_ => 0,110}111}112113fn write_common_config_byte(&mut self, offset: u64, value: u8) {114match offset {1150x14 => self.driver_status = value,116_ => {117warn!("invalid virtio config byt access: 0x{:x}", offset);118}119}120}121122fn read_common_config_word(&self, offset: u64, queues: &[QueueConfig]) -> u16 {123match offset {1240x10 => self.msix_config,1250x12 => queues.len() as u16, // num_queues1260x16 => self.queue_select,1270x18 => self.with_queue(queues, |q| q.size()).unwrap_or(0),1280x1a => self.with_queue(queues, |q| q.vector()).unwrap_or(0),1290x1c => self130.with_queue(queues, |q| q.ready())131.unwrap_or(false)132.into(),1330x1e => self.queue_select, // notify_off134_ => 0,135}136}137138fn write_common_config_word(&mut self, offset: u64, value: u16, queues: &mut [QueueConfig]) {139match offset {1400x10 => self.msix_config = value,1410x16 => self.queue_select = value,1420x18 => self.with_queue_mut(queues, |q| q.set_size(value)),1430x1a => self.with_queue_mut(queues, |q| q.set_vector(value)),1440x1c => self.with_queue_mut(queues, |q| q.set_ready(value == 1)),145_ => {146warn!("invalid virtio register word write: 0x{:x}", offset);147}148}149}150151fn read_common_config_dword(&self, offset: u64, device: &dyn VirtioDevice) -> u32 {152match offset {1530x00 => self.device_feature_select,1540x04 => {155// Only 64 bits of features (2 pages) are defined for now, so limit156// device_feature_select to avoid shifting by 64 or more bits.157if self.device_feature_select < 2 {158(device.features() >> (self.device_feature_select * 32)) as u32159} else {1600161}162}1630x08 => self.driver_feature_select,164_ => 0,165}166}167168fn write_common_config_dword(169&mut self,170offset: u64,171value: u32,172queues: &mut [QueueConfig],173device: &mut dyn VirtioDevice,174) {175macro_rules! hi {176($q:expr, $get:ident, $set:ident, $x:expr) => {177$q.$set(($q.$get() & 0xffffffff) | (($x as u64) << 32))178};179}180macro_rules! lo {181($q:expr, $get:ident, $set:ident, $x:expr) => {182$q.$set(($q.$get() & !0xffffffff) | ($x as u64))183};184}185186match offset {1870x00 => self.device_feature_select = value,1880x08 => self.driver_feature_select = value,1890x0c => {190if self.driver_feature_select < 2 {191let features: u64 = (value as u64) << (self.driver_feature_select * 32);192device.ack_features(features);193for queue in queues.iter_mut() {194queue.ack_features(features);195}196} else {197warn!(198"invalid ack_features (page {}, value 0x{:x})",199self.driver_feature_select, value200);201}202}2030x20 => self.with_queue_mut(queues, |q| lo!(q, desc_table, set_desc_table, value)),2040x24 => self.with_queue_mut(queues, |q| hi!(q, desc_table, set_desc_table, value)),2050x28 => self.with_queue_mut(queues, |q| lo!(q, avail_ring, set_avail_ring, value)),2060x2c => self.with_queue_mut(queues, |q| hi!(q, avail_ring, set_avail_ring, value)),2070x30 => self.with_queue_mut(queues, |q| lo!(q, used_ring, set_used_ring, value)),2080x34 => self.with_queue_mut(queues, |q| hi!(q, used_ring, set_used_ring, value)),209_ => {210warn!("invalid virtio register dword write: 0x{:x}", offset);211}212}213}214215fn read_common_config_qword(&self, _offset: u64) -> u64 {2160 // Assume the guest has no reason to read write-only registers.217}218219fn write_common_config_qword(&mut self, offset: u64, value: u64, queues: &mut [QueueConfig]) {220match offset {2210x20 => self.with_queue_mut(queues, |q| q.set_desc_table(GuestAddress(value))),2220x28 => self.with_queue_mut(queues, |q| q.set_avail_ring(GuestAddress(value))),2230x30 => self.with_queue_mut(queues, |q| q.set_used_ring(GuestAddress(value))),224_ => {225warn!("invalid virtio register qword write: 0x{:x}", offset);226}227}228}229230fn with_queue<U, F>(&self, queues: &[QueueConfig], f: F) -> Option<U>231where232F: FnOnce(&QueueConfig) -> U,233{234queues.get(self.queue_select as usize).map(f)235}236237fn with_queue_mut<F: FnOnce(&mut QueueConfig)>(&self, queues: &mut [QueueConfig], f: F) {238if let Some(queue) = queues.get_mut(self.queue_select as usize) {239f(queue);240}241}242}243244#[cfg(test)]245mod tests {246use std::collections::BTreeMap;247248use base::RawDescriptor;249use vm_memory::GuestMemory;250251use super::*;252253struct DummyDevice(DeviceType);254const QUEUE_SIZE: u16 = 256;255const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];256const DUMMY_FEATURES: u64 = 0x5555_aaaa;257impl VirtioDevice for DummyDevice {258fn keep_rds(&self) -> Vec<RawDescriptor> {259Vec::new()260}261fn device_type(&self) -> DeviceType {262self.0263}264fn queue_max_sizes(&self) -> &[u16] {265QUEUE_SIZES266}267fn activate(268&mut self,269_mem: GuestMemory,270_interrupt: Interrupt,271_queues: BTreeMap<usize, Queue>,272) -> anyhow::Result<()> {273Ok(())274}275fn features(&self) -> u64 {276DUMMY_FEATURES277}278}279280#[test]281fn write_base_regs() {282let mut regs = VirtioPciCommonConfig {283driver_status: 0xaa,284config_generation: 0x55,285device_feature_select: 0x0,286driver_feature_select: 0x0,287queue_select: 0xff,288msix_config: 0x00,289};290291let dev = &mut DummyDevice(DeviceType::Rng) as &mut dyn VirtioDevice;292let mut queues = Vec::new();293294// Can set all bits of driver_status.295regs.write(0x14, &[0x55], &mut queues, dev);296let mut read_back = vec![0x00];297regs.read(0x14, &mut read_back, &mut queues, dev);298assert_eq!(read_back[0], 0x55);299300// The config generation register is read only.301regs.write(0x15, &[0xaa], &mut queues, dev);302let mut read_back = vec![0x00];303regs.read(0x15, &mut read_back, &mut queues, dev);304assert_eq!(read_back[0], 0x55);305306// Device features is read-only and passed through from the device.307regs.write(0x04, &[0, 0, 0, 0], &mut queues, dev);308let mut read_back = [0u8; 4];309regs.read(0x04, &mut read_back, &mut queues, dev);310assert_eq!(u32::from_le_bytes(read_back), DUMMY_FEATURES as u32);311312// Feature select registers are read/write.313regs.write(0x00, &[1, 2, 3, 4], &mut queues, dev);314let mut read_back = [0u8; 4];315regs.read(0x00, &mut read_back, &mut queues, dev);316assert_eq!(u32::from_le_bytes(read_back), 0x0403_0201);317regs.write(0x08, &[1, 2, 3, 4], &mut queues, dev);318let mut read_back = [0u8; 4];319regs.read(0x08, &mut read_back, &mut queues, dev);320assert_eq!(u32::from_le_bytes(read_back), 0x0403_0201);321322// 'queue_select' can be read and written.323regs.write(0x16, &[0xaa, 0x55], &mut queues, dev);324let mut read_back = vec![0x00, 0x00];325regs.read(0x16, &mut read_back, &mut queues, dev);326assert_eq!(read_back[0], 0xaa);327assert_eq!(read_back[1], 0x55);328}329}330331332