diff options
author | Alyssa Ross <hi@alyssa.is> | 2020-05-08 15:27:56 +0000 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2020-05-10 02:39:28 +0000 |
commit | 2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b (patch) | |
tree | fefaf2c13796f8f2fa9a13b99b09c3b40ab5966b /devices | |
parent | 00c41c28bbc44b37fc8dcf5d2a5b4679f2aa4297 (diff) | |
parent | 03a54abf852984f696e7a101ff9590f05ebcba5b (diff) | |
download | crosvm-2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b.tar crosvm-2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b.tar.gz crosvm-2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b.tar.bz2 crosvm-2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b.tar.lz crosvm-2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b.tar.xz crosvm-2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b.tar.zst crosvm-2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b.zip |
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'devices')
34 files changed, 363 insertions, 477 deletions
diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 9ed8417..01c2b46 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -17,6 +17,7 @@ mod proxy; mod register_space; pub mod acpi; mod serial; +mod serial_device; pub mod split_irqchip_common; pub mod usb; mod utils; @@ -30,18 +31,16 @@ pub use self::cmos::Cmos; pub use self::i8042::I8042Device; pub use self::ioapic::{Ioapic, IOAPIC_BASE_ADDRESS, IOAPIC_MEM_LENGTH_BYTES}; pub use self::pci::{ - Ac97Backend, Ac97Dev, Ac97Parameters, PciConfigIo, PciConfigMmio, PciDevice, PciDeviceError, - PciInterruptPin, PciRoot, VfioPciDevice, + Ac97Backend, Ac97Dev, Ac97Parameters, PciAddress, PciConfigIo, PciConfigMmio, PciDevice, + PciDeviceError, PciInterruptPin, PciRoot, VfioPciDevice, }; pub use self::pic::Pic; pub use self::pit::{Pit, PitError}; pub use self::pl030::Pl030; pub use self::proxy::Error as ProxyError; pub use self::proxy::ProxyDevice; -pub use self::serial::Error as SerialError; -pub use self::serial::{ - get_serial_tty_string, Serial, SerialParameters, SerialType, DEFAULT_SERIAL_PARAMS, SERIAL_ADDR, -}; +pub use self::serial::Serial; +pub use self::serial_device::SerialDevice; pub use self::usb::host_backend::host_backend_device_provider::HostBackendDeviceProvider; pub use self::usb::xhci::xhci_controller::XhciController; pub use self::vfio::{VfioContainer, VfioDevice}; diff --git a/devices/src/pci/ac97.rs b/devices/src/pci/ac97.rs index d061746..8a6748d 100644 --- a/devices/src/pci/ac97.rs +++ b/devices/src/pci/ac97.rs @@ -12,7 +12,7 @@ use audio_streams::{ shm_streams::{NullShmStreamSource, ShmStreamSource}, StreamEffect, }; -use libcras::{CrasClient, CrasClientType}; +use libcras::{CrasClient, CrasClientType, CrasSocketType}; use resources::{Alloc, MmioType, SystemAllocator}; use sys_util::{error, EventFd, GuestMemory}; @@ -23,7 +23,7 @@ use crate::pci::pci_configuration::{ PciBarConfiguration, PciClassCode, PciConfiguration, PciHeaderType, PciMultimediaSubclass, }; use crate::pci::pci_device::{self, PciDevice, Result}; -use crate::pci::PciInterruptPin; +use crate::pci::{PciAddress, PciInterruptPin}; // Use 82801AA because it's what qemu does. const PCI_DEVICE_ID_INTEL_82801AA_5: u16 = 0x2415; @@ -82,7 +82,7 @@ pub struct Ac97Parameters { pub struct Ac97Dev { config_regs: PciConfiguration, - pci_bus_dev: Option<(u8, u8)>, + pci_address: Option<PciAddress>, // The irq events are temporarily saved here. They need to be passed to the device after the // jail forks. This happens when the bus is first written. irq_evt: Option<EventFd>, @@ -108,7 +108,7 @@ impl Ac97Dev { Ac97Dev { config_regs, - pci_bus_dev: None, + pci_address: None, irq_evt: None, irq_resample_evt: None, bus_master: Ac97BusMaster::new(mem, audio_server), @@ -117,8 +117,10 @@ impl Ac97Dev { } fn create_cras_audio_device(params: Ac97Parameters, mem: GuestMemory) -> Result<Ac97Dev> { - let mut server = - Box::new(CrasClient::new().map_err(|e| pci_device::Error::CreateCrasClientFailed(e))?); + let mut server = Box::new( + CrasClient::with_type(CrasSocketType::Unified) + .map_err(|e| pci_device::Error::CreateCrasClientFailed(e))?, + ); server.set_client_type(CrasClientType::CRAS_CLIENT_TYPE_CROSVM); if params.capture { server.enable_cras_capture(); @@ -214,8 +216,8 @@ impl PciDevice for Ac97Dev { "AC97".to_owned() } - fn assign_bus_dev(&mut self, bus: u8, device: u8) { - self.pci_bus_dev = Some((bus, device)); + fn assign_address(&mut self, address: PciAddress) { + self.pci_address = Some(address); } fn assign_irq( @@ -231,15 +233,20 @@ impl PciDevice for Ac97Dev { } fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> { - let (bus, dev) = self - .pci_bus_dev - .expect("assign_bus_dev must be called prior to allocate_io_bars"); + let address = self + .pci_address + .expect("assign_address must be called prior to allocate_io_bars"); let mut ranges = Vec::new(); let mixer_regs_addr = resources .mmio_allocator(MmioType::Low) .allocate_with_align( MIXER_REGS_SIZE, - Alloc::PciBar { bus, dev, bar: 0 }, + Alloc::PciBar { + bus: address.bus, + dev: address.dev, + func: address.func, + bar: 0, + }, "ac97-mixer_regs".to_string(), MIXER_REGS_SIZE, ) @@ -257,7 +264,12 @@ impl PciDevice for Ac97Dev { .mmio_allocator(MmioType::Low) .allocate_with_align( MASTER_REGS_SIZE, - Alloc::PciBar { bus, dev, bar: 1 }, + Alloc::PciBar { + bus: address.bus, + dev: address.dev, + func: address.func, + bar: 1, + }, "ac97-master_regs".to_string(), MASTER_REGS_SIZE, ) @@ -336,7 +348,11 @@ mod tests { .add_high_mmio_addresses(0x3000_0000, 0x1000_0000) .create_allocator(5, false) .unwrap(); - ac97_dev.assign_bus_dev(0, 0); + ac97_dev.assign_address(PciAddress { + bus: 0, + dev: 0, + func: 0, + }); assert!(ac97_dev.allocate_io_bars(&mut allocator).is_ok()); } } diff --git a/devices/src/pci/ac97_bus_master.rs b/devices/src/pci/ac97_bus_master.rs index 809f31f..22f3c92 100644 --- a/devices/src/pci/ac97_bus_master.rs +++ b/devices/src/pci/ac97_bus_master.rs @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use std; use std::collections::VecDeque; use std::convert::AsRef; use std::convert::TryInto; diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index b9ebc19..0edc3b2 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -23,7 +23,7 @@ pub use self::pci_configuration::{ }; pub use self::pci_device::Error as PciDeviceError; pub use self::pci_device::PciDevice; -pub use self::pci_root::{PciConfigIo, PciConfigMmio, PciRoot}; +pub use self::pci_root::{PciAddress, PciConfigIo, PciConfigMmio, PciRoot}; pub use self::vfio_pci::VfioPciDevice; /// PCI has four interrupt pins A->D. diff --git a/devices/src/pci/pci_device.rs b/devices/src/pci/pci_device.rs index a1a3cca..244edbd 100644 --- a/devices/src/pci/pci_device.rs +++ b/devices/src/pci/pci_device.rs @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use std; use std::fmt::{self, Display}; use std::os::unix::io::RawFd; @@ -11,7 +10,7 @@ use resources::{Error as SystemAllocatorFaliure, SystemAllocator}; use sys_util::EventFd; use crate::pci::pci_configuration; -use crate::pci::PciInterruptPin; +use crate::pci::{PciAddress, PciInterruptPin}; use crate::BusDevice; #[derive(Debug)] @@ -49,8 +48,8 @@ impl Display for Error { pub trait PciDevice: Send { /// Returns a label suitable for debug output. fn debug_label(&self) -> String; - /// Assign a unique bus and device number to this device. - fn assign_bus_dev(&mut self, _bus: u8, _device: u8 /*u5*/) {} + /// Assign a unique bus, device and function number to this device. + fn assign_address(&mut self, _address: PciAddress) {} /// A vector of device-specific file descriptors that must be kept open /// after jailing. Must be called before the process is jailed. fn keep_fds(&self) -> Vec<RawFd>; @@ -149,8 +148,8 @@ impl<T: PciDevice + ?Sized> PciDevice for Box<T> { fn debug_label(&self) -> String { (**self).debug_label() } - fn assign_bus_dev(&mut self, bus: u8, device: u8 /*u5*/) { - (**self).assign_bus_dev(bus, device) + fn assign_address(&mut self, address: PciAddress) { + (**self).assign_address(address) } fn keep_fds(&self) -> Vec<RawFd> { (**self).keep_fds() diff --git a/devices/src/pci/pci_root.rs b/devices/src/pci/pci_root.rs index eef642e..91d6094 100644 --- a/devices/src/pci/pci_root.rs +++ b/devices/src/pci/pci_root.rs @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +use std::collections::BTreeMap; use std::convert::TryInto; +use std::fmt::{self, Display}; use std::os::unix::io::RawFd; use std::sync::Arc; @@ -39,12 +41,67 @@ impl PciDevice for PciRootConfiguration { fn write_bar(&mut self, _addr: u64, _data: &[u8]) {} } +/// PCI Device Address, AKA Bus:Device.Function +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct PciAddress { + pub bus: u8, + pub dev: u8, /* u5 */ + pub func: u8, /* u3 */ +} + +impl Display for PciAddress { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:04x}:{:02x}.{:0x}", self.bus, self.dev, self.func) + } +} + +impl PciAddress { + const BUS_OFFSET: usize = 16; + const BUS_MASK: u32 = 0x00ff; + const DEVICE_OFFSET: usize = 11; + const DEVICE_MASK: u32 = 0x1f; + const FUNCTION_OFFSET: usize = 8; + const FUNCTION_MASK: u32 = 0x07; + const REGISTER_OFFSET: usize = 2; + const REGISTER_MASK: u32 = 0x3f; + + /// Construct PciAddress and register tuple from CONFIG_ADDRESS value. + pub fn from_config_address(config_address: u32) -> (Self, usize) { + let bus = ((config_address >> Self::BUS_OFFSET) & Self::BUS_MASK) as u8; + let dev = ((config_address >> Self::DEVICE_OFFSET) & Self::DEVICE_MASK) as u8; + let func = ((config_address >> Self::FUNCTION_OFFSET) & Self::FUNCTION_MASK) as u8; + let register = ((config_address >> Self::REGISTER_OFFSET) & Self::REGISTER_MASK) as usize; + + (PciAddress { bus, dev, func }, register) + } + + /// Encode PciAddress into CONFIG_ADDRESS value. + pub fn to_config_address(&self, register: usize) -> u32 { + ((Self::BUS_MASK & self.bus as u32) << Self::BUS_OFFSET) + | ((Self::DEVICE_MASK & self.dev as u32) << Self::DEVICE_OFFSET) + | ((Self::FUNCTION_MASK & self.func as u32) << Self::FUNCTION_OFFSET) + | ((Self::REGISTER_MASK & register as u32) << Self::REGISTER_OFFSET) + } + + /// Returns true if the address points to PCI root host-bridge. + fn is_root(&self) -> bool { + matches!( + &self, + PciAddress { + bus: 0, + dev: 0, + func: 0 + } + ) + } +} + /// Emulates the PCI Root bridge. pub struct PciRoot { /// Bus configuration for the root device. root_configuration: PciRootConfiguration, /// Devices attached to this bridge. - devices: Vec<Arc<Mutex<dyn BusDevice>>>, + devices: BTreeMap<PciAddress, Arc<Mutex<dyn BusDevice>>>, } const PCI_VENDOR_ID_INTEL: u16 = 0x8086; @@ -66,44 +123,31 @@ impl PciRoot { 0, ), }, - devices: Vec::new(), + devices: BTreeMap::new(), } } /// Add a `device` to this root PCI bus. - pub fn add_device(&mut self, device: Arc<Mutex<dyn BusDevice>>) { - self.devices.push(device); - } - - pub fn config_space_read( - &self, - bus: usize, - device: usize, - _function: usize, - register: usize, - ) -> u32 { - // Only support one bus. - if bus != 0 { - return 0xffff_ffff; + pub fn add_device(&mut self, address: PciAddress, device: Arc<Mutex<dyn BusDevice>>) { + // Ignore attempt to replace PCI Root host bridge. + if !address.is_root() { + self.devices.insert(address, device); } + } - match device { - 0 => { - // If bus and device are both zero, then read from the root config. - self.root_configuration.config_register_read(register) - } - dev_num => self - .devices - .get(dev_num - 1) - .map_or(0xffff_ffff, |d| d.lock().config_register_read(register)), + pub fn config_space_read(&self, address: PciAddress, register: usize) -> u32 { + if address.is_root() { + self.root_configuration.config_register_read(register) + } else { + self.devices + .get(&address) + .map_or(0xffff_ffff, |d| d.lock().config_register_read(register)) } } pub fn config_space_write( &mut self, - bus: usize, - device: usize, - _function: usize, + address: PciAddress, register: usize, offset: u64, data: &[u8], @@ -111,24 +155,11 @@ impl PciRoot { if offset as usize + data.len() > 4 { return; } - - // Only support one bus. - if bus != 0 { - return; - } - - match device { - 0 => { - // If bus and device are both zero, then read from the root config. - self.root_configuration - .config_register_write(register, offset, data); - } - dev_num => { - // dev_num is 1-indexed here. - if let Some(d) = self.devices.get(dev_num - 1) { - d.lock().config_register_write(register, offset, data); - } - } + if address.is_root() { + self.root_configuration + .config_register_write(register, offset, data); + } else if let Some(d) = self.devices.get(&address) { + d.lock().config_register_write(register, offset, data); } } } @@ -155,10 +186,8 @@ impl PciConfigIo { return 0xffff_ffff; } - let (bus, device, function, register) = - parse_config_address(self.config_address & !0x8000_0000); - self.pci_root - .config_space_read(bus, device, function, register) + let (address, register) = PciAddress::from_config_address(self.config_address); + self.pci_root.config_space_read(address, register) } fn config_space_write(&mut self, offset: u64, data: &[u8]) { @@ -167,10 +196,9 @@ impl PciConfigIo { return; } - let (bus, device, function, register) = - parse_config_address(self.config_address & !0x8000_0000); + let (address, register) = PciAddress::from_config_address(self.config_address); self.pci_root - .config_space_write(bus, device, function, register, offset, data) + .config_space_write(address, register, offset, data) } fn set_config_address(&mut self, offset: u64, data: &[u8]) { @@ -242,15 +270,14 @@ impl PciConfigMmio { } fn config_space_read(&self, config_address: u32) -> u32 { - let (bus, device, function, register) = parse_config_address(config_address); - self.pci_root - .config_space_read(bus, device, function, register) + let (address, register) = PciAddress::from_config_address(config_address); + self.pci_root.config_space_read(address, register) } fn config_space_write(&mut self, config_address: u32, offset: u64, data: &[u8]) { - let (bus, device, function, register) = parse_config_address(config_address); + let (address, register) = PciAddress::from_config_address(config_address); self.pci_root - .config_space_write(bus, device, function, register, offset, data) + .config_space_write(address, register, offset, data) } } @@ -283,24 +310,3 @@ impl BusDevice for PciConfigMmio { self.config_space_write(offset as u32, offset % 4, data) } } - -// Parse the CONFIG_ADDRESS register to a (bus, device, function, register) tuple. -fn parse_config_address(config_address: u32) -> (usize, usize, usize, usize) { - const BUS_NUMBER_OFFSET: usize = 16; - const BUS_NUMBER_MASK: u32 = 0x00ff; - const DEVICE_NUMBER_OFFSET: usize = 11; - const DEVICE_NUMBER_MASK: u32 = 0x1f; - const FUNCTION_NUMBER_OFFSET: usize = 8; - const FUNCTION_NUMBER_MASK: u32 = 0x07; - const REGISTER_NUMBER_OFFSET: usize = 2; - const REGISTER_NUMBER_MASK: u32 = 0x3f; - - let bus_number = ((config_address >> BUS_NUMBER_OFFSET) & BUS_NUMBER_MASK) as usize; - let device_number = ((config_address >> DEVICE_NUMBER_OFFSET) & DEVICE_NUMBER_MASK) as usize; - let function_number = - ((config_address >> FUNCTION_NUMBER_OFFSET) & FUNCTION_NUMBER_MASK) as usize; - let register_number = - ((config_address >> REGISTER_NUMBER_OFFSET) & REGISTER_NUMBER_MASK) as usize; - - (bus_number, device_number, function_number, register_number) -} diff --git a/devices/src/pci/vfio_pci.rs b/devices/src/pci/vfio_pci.rs index 5c13d28..671e8cd 100644 --- a/devices/src/pci/vfio_pci.rs +++ b/devices/src/pci/vfio_pci.rs @@ -22,7 +22,7 @@ use crate::pci::msix::{ }; use crate::pci::pci_device::{Error as PciDeviceError, PciDevice}; -use crate::pci::{PciClassCode, PciInterruptPin}; +use crate::pci::{PciAddress, PciClassCode, PciInterruptPin}; use crate::vfio::{VfioDevice, VfioIrqType}; @@ -457,7 +457,7 @@ enum DeviceData { pub struct VfioPciDevice { device: Arc<VfioDevice>, config: VfioPciConfig, - pci_bus_dev: Option<(u8, u8)>, + pci_address: Option<PciAddress>, interrupt_evt: Option<EventFd>, interrupt_resample_evt: Option<EventFd>, mmio_regions: Vec<MmioInfo>, @@ -522,7 +522,7 @@ impl VfioPciDevice { VfioPciDevice { device: dev, config, - pci_bus_dev: None, + pci_address: None, interrupt_evt: None, interrupt_resample_evt: None, mmio_regions: Vec::new(), @@ -776,8 +776,8 @@ impl PciDevice for VfioPciDevice { "vfio pci device".to_string() } - fn assign_bus_dev(&mut self, bus: u8, device: u8) { - self.pci_bus_dev = Some((bus, device)); + fn assign_address(&mut self, address: PciAddress) { + self.pci_address = Some(address); } fn keep_fds(&self) -> Vec<RawFd> { @@ -816,9 +816,9 @@ impl PciDevice for VfioPciDevice { ) -> Result<Vec<(u64, u64)>, PciDeviceError> { let mut ranges = Vec::new(); let mut i = VFIO_PCI_BAR0_REGION_INDEX; - let (bus, dev) = self - .pci_bus_dev - .expect("assign_bus_dev must be called prior to allocate_io_bars"); + let address = self + .pci_address + .expect("assign_address must be called prior to allocate_io_bars"); while i <= VFIO_PCI_ROM_REGION_INDEX { let mut low: u32 = 0xffffffff; @@ -854,8 +854,9 @@ impl PciDevice for VfioPciDevice { .allocate_with_align( size, Alloc::PciBar { - bus, - dev, + bus: address.bus, + dev: address.dev, + func: address.func, bar: i as u8, }, "vfio_bar".to_string(), @@ -914,16 +915,17 @@ impl PciDevice for VfioPciDevice { VFIO_REGION_TYPE_PCI_VENDOR_TYPE | (INTEL_VENDOR_ID as u32), VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, ) { - let (bus, dev) = self - .pci_bus_dev - .expect("assign_bus_dev must be called prior to allocate_device_bars"); + let address = self + .pci_address + .expect("assign_address must be called prior to allocate_device_bars"); let bar_addr = resources .mmio_allocator(MmioType::Low) .allocate( size, Alloc::PciBar { - bus, - dev, + bus: address.bus, + dev: address.dev, + func: address.func, bar: (index * 4) as u8, }, "vfio_bar".to_string(), diff --git a/devices/src/register_space/register.rs b/devices/src/register_space/register.rs index c2f5184..2a4f551 100644 --- a/devices/src/register_space/register.rs +++ b/devices/src/register_space/register.rs @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use std; use std::boxed::Box; use std::cmp::{max, min, Ord, Ordering, PartialOrd}; use std::mem::size_of; diff --git a/devices/src/serial.rs b/devices/src/serial.rs index 6629d7e..2af988e 100644 --- a/devices/src/serial.rs +++ b/devices/src/serial.rs @@ -3,20 +3,16 @@ // found in the LICENSE file. use std::collections::VecDeque; -use std::fmt::{self, Display}; -use std::fs::File; -use std::io::{self, stdin, stdout, Read, Write}; -use std::os::unix::io::{AsRawFd, RawFd}; -use std::path::PathBuf; -use std::str::FromStr; +use std::io::{self, Write}; +use std::os::unix::io::RawFd; use std::sync::atomic::{AtomicU8, Ordering}; use std::sync::mpsc::{channel, Receiver, TryRecvError}; use std::sync::Arc; use std::thread::{self}; -use sys_util::{error, read_raw_stdin, syslog, EventFd, Result}; +use sys_util::{error, EventFd, Result}; -use crate::BusDevice; +use crate::{BusDevice, SerialDevice}; const LOOP_SIZE: usize = 0x40; @@ -62,187 +58,6 @@ const DEFAULT_MODEM_CONTROL: u8 = MCR_OUT2_BIT; const DEFAULT_MODEM_STATUS: u8 = MSR_DSR_BIT | MSR_CTS_BIT | MSR_DCD_BIT; const DEFAULT_BAUD_DIVISOR: u16 = 12; // 9600 bps -#[derive(Debug)] -pub enum Error { - CloneEventFd(sys_util::Error), - InvalidSerialType(String), - PathRequired, - FileError(std::io::Error), - Unimplemented(SerialType), -} - -impl Display for Error { - #[remain::check] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::Error::*; - - #[sorted] - match self { - CloneEventFd(e) => write!(f, "unable to clone an EventFd: {}", e), - FileError(e) => write!(f, "Unable to open/create file: {}", e), - InvalidSerialType(e) => write!(f, "invalid serial type: {}", e), - PathRequired => write!(f, "serial device type file requires a path"), - Unimplemented(e) => write!(f, "serial device type {} not implemented", e.to_string()), - } - } -} - -/// Enum for possible type of serial devices -#[derive(Debug)] -pub enum SerialType { - File, - Stdout, - Sink, - Syslog, - UnixSocket, // NOT IMPLEMENTED -} - -impl Display for SerialType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match &self { - SerialType::File => "File".to_string(), - SerialType::Stdout => "Stdout".to_string(), - SerialType::Sink => "Sink".to_string(), - SerialType::Syslog => "Syslog".to_string(), - SerialType::UnixSocket => "UnixSocket".to_string(), - }; - - write!(f, "{}", s) - } -} - -impl FromStr for SerialType { - type Err = Error; - fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { - match s { - "file" | "File" => Ok(SerialType::File), - "stdout" | "Stdout" => Ok(SerialType::Stdout), - "sink" | "Sink" => Ok(SerialType::Sink), - "syslog" | "Syslog" => Ok(SerialType::Syslog), - "unix" | "UnixSocket" => Ok(SerialType::UnixSocket), - _ => Err(Error::InvalidSerialType(s.to_string())), - } - } -} - -/// Holds the parameters for a serial device -#[derive(Debug)] -pub struct SerialParameters { - pub type_: SerialType, - pub path: Option<PathBuf>, - pub num: u8, - pub console: bool, - pub stdin: bool, -} - -impl SerialParameters { - /// Helper function to create a serial device from the defined parameters. - /// - /// # Arguments - /// * `evt_fd` - eventfd used for interrupt events - /// * `keep_fds` - Vector of FDs required by this device if it were sandboxed in a child - /// process. `evt_fd` will always be added to this vector by this function. - pub fn create_serial_device( - &self, - evt_fd: &EventFd, - keep_fds: &mut Vec<RawFd>, - ) -> std::result::Result<Serial, Error> { - let evt_fd = evt_fd.try_clone().map_err(Error::CloneEventFd)?; - keep_fds.push(evt_fd.as_raw_fd()); - let input: Option<Box<dyn io::Read + Send>> = if self.stdin { - keep_fds.push(stdin().as_raw_fd()); - // This wrapper is used in place of the libstd native version because we don't want - // buffering for stdin. - struct StdinWrapper; - impl io::Read for StdinWrapper { - fn read(&mut self, out: &mut [u8]) -> io::Result<usize> { - read_raw_stdin(out).map_err(|e| e.into()) - } - } - Some(Box::new(StdinWrapper)) - } else { - None - }; - match self.type_ { - SerialType::Stdout => { - keep_fds.push(stdout().as_raw_fd()); - Ok(Serial::new(evt_fd, input, Some(Box::new(stdout())))) - } - SerialType::Sink => Ok(Serial::new(evt_fd, input, None)), - SerialType::Syslog => { - syslog::push_fds(keep_fds); - Ok(Serial::new( - evt_fd, - input, - Some(Box::new(syslog::Syslogger::new( - syslog::Priority::Info, - syslog::Facility::Daemon, - ))), - )) - } - SerialType::File => match &self.path { - None => Err(Error::PathRequired), - Some(path) => { - let file = File::create(path.as_path()).map_err(Error::FileError)?; - keep_fds.push(file.as_raw_fd()); - Ok(Serial::new(evt_fd, input, Some(Box::new(file)))) - } - }, - SerialType::UnixSocket => Err(Error::Unimplemented(SerialType::UnixSocket)), - } - } -} - -// Structure for holding the default configuration of the serial devices. -pub const DEFAULT_SERIAL_PARAMS: [SerialParameters; 4] = [ - SerialParameters { - type_: SerialType::Stdout, - path: None, - num: 1, - console: true, - stdin: true, - }, - SerialParameters { - type_: SerialType::Sink, - path: None, - num: 2, - console: false, - stdin: false, - }, - SerialParameters { - type_: SerialType::Sink, - path: None, - num: 3, - console: false, - stdin: false, - }, - SerialParameters { - type_: SerialType::Sink, - path: None, - num: 4, - console: false, - stdin: false, - }, -]; - -/// Address for Serial ports in x86 -pub const SERIAL_ADDR: [u64; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8]; - -/// String representations of serial devices -pub const SERIAL_TTY_STRINGS: [&str; 4] = ["ttyS0", "ttyS1", "ttyS2", "ttyS3"]; - -/// Helper function to get the tty string of a serial device based on the port number. Will default -/// to ttyS0 if an invalid number is given. -pub fn get_serial_tty_string(stdio_serial_num: u8) -> String { - match stdio_serial_num { - 1 => SERIAL_TTY_STRINGS[0].to_string(), - 2 => SERIAL_TTY_STRINGS[1].to_string(), - 3 => SERIAL_TTY_STRINGS[2].to_string(), - 4 => SERIAL_TTY_STRINGS[3].to_string(), - _ => SERIAL_TTY_STRINGS[0].to_string(), - } -} - /// Emulates serial COM ports commonly seen on x86 I/O ports 0x3f8/0x2f8/0x3e8/0x2e8. /// /// This can optionally write the guest's output to a Write trait object. To send input to the @@ -267,11 +82,12 @@ pub struct Serial { out: Option<Box<dyn io::Write + Send>>, } -impl Serial { +impl SerialDevice for Serial { fn new( interrupt_evt: EventFd, input: Option<Box<dyn io::Read + Send>>, out: Option<Box<dyn io::Write + Send>>, + _keep_fds: Vec<RawFd>, ) -> Serial { Serial { interrupt_enable: Default::default(), @@ -289,28 +105,9 @@ impl Serial { out, } } +} - /// Constructs a Serial port ready for input and output. - /// - /// The stream `input` should not block, instead returning 0 bytes if are no bytes available. - pub fn new_in_out( - interrupt_evt: EventFd, - input: Box<dyn io::Read + Send>, - out: Box<dyn io::Write + Send>, - ) -> Serial { - Self::new(interrupt_evt, Some(input), Some(out)) - } - - /// Constructs a Serial port ready for output but not input. - pub fn new_out(interrupt_evt: EventFd, out: Box<dyn io::Write + Send>) -> Serial { - Self::new(interrupt_evt, None, Some(out)) - } - - /// Constructs a Serial port with no connected input or output. - pub fn new_sink(interrupt_evt: EventFd) -> Serial { - Self::new(interrupt_evt, None, None) - } - +impl Serial { /// Queues raw bytes for the guest to read and signals the interrupt if the line status would /// change. These bytes will be read by the guest before any bytes from the input stream that /// have not already been queued. @@ -611,7 +408,12 @@ mod tests { let intr_evt = EventFd::new().unwrap(); let serial_out = SharedBuffer::new(); - let mut serial = Serial::new_out(intr_evt, Box::new(serial_out.clone())); + let mut serial = Serial::new( + intr_evt, + None, + Some(Box::new(serial_out.clone())), + Vec::new(), + ); serial.write(DATA as u64, &['a' as u8]); serial.write(DATA as u64, &['b' as u8]); @@ -627,8 +429,12 @@ mod tests { let intr_evt = EventFd::new().unwrap(); let serial_out = SharedBuffer::new(); - let mut serial = - Serial::new_out(intr_evt.try_clone().unwrap(), Box::new(serial_out.clone())); + let mut serial = Serial::new( + intr_evt.try_clone().unwrap(), + None, + Some(Box::new(serial_out.clone())), + Vec::new(), + ); serial.write(IER as u64, &[IER_RECV_BIT]); serial diff --git a/devices/src/serial_device.rs b/devices/src/serial_device.rs new file mode 100644 index 0000000..9377556 --- /dev/null +++ b/devices/src/serial_device.rs @@ -0,0 +1,19 @@ +// Copyright 2020 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::io; +use std::os::unix::io::RawFd; + +use sys_util::EventFd; + +/// Abstraction over serial-like devices that can be created given an event and optional input and +/// output streams. +pub trait SerialDevice { + fn new( + interrupt_evt: EventFd, + input: Option<Box<dyn io::Read + Send>>, + output: Option<Box<dyn io::Write + Send>>, + keep_fds: Vec<RawFd>, + ) -> Self; +} diff --git a/devices/src/usb/host_backend/host_device.rs b/devices/src/usb/host_backend/host_device.rs index 196fa50..8f8723d 100644 --- a/devices/src/usb/host_backend/host_device.rs +++ b/devices/src/usb/host_backend/host_device.rs @@ -140,30 +140,21 @@ impl HostDevice { return Ok(()); } - // Default buffer size for control data transfer. - const CONTROL_DATA_BUFFER_SIZE: usize = 1024; - - // Buffer type for control transfer. The first 8 bytes is a UsbRequestSetup struct. - #[derive(Copy, Clone)] - #[repr(C, packed)] - struct ControlTransferBuffer { - pub setup: UsbRequestSetup, - pub data: [u8; CONTROL_DATA_BUFFER_SIZE], - } - - // Safe because it only has data and has no implicit padding. - unsafe impl DataInit for ControlTransferBuffer {} + // Allocate a buffer for the control transfer. + // This buffer will hold a UsbRequestSetup struct followed by the data. + let control_buffer_len = + mem::size_of::<UsbRequestSetup>() + self.control_request_setup.length as usize; + let mut control_buffer = vec![0u8; control_buffer_len]; - let mut control_request = ControlTransferBuffer { - setup: self.control_request_setup, - data: [0; CONTROL_DATA_BUFFER_SIZE], - }; + // Copy the control request header. + control_buffer[..mem::size_of::<UsbRequestSetup>()] + .copy_from_slice(self.control_request_setup.as_slice()); let direction = self.control_request_setup.get_direction(); let buffer = if direction == ControlRequestDataPhaseTransferDirection::HostToDevice { if let Some(buffer) = buffer { buffer - .read(&mut control_request.data) + .read(&mut control_buffer[mem::size_of::<UsbRequestSetup>()..]) .map_err(Error::ReadBuffer)?; } // buffer is consumed here for HostToDevice transfers. @@ -173,8 +164,6 @@ impl HostDevice { buffer }; - let control_buffer = control_request.as_slice().to_vec(); - let mut control_transfer = Transfer::new_control(control_buffer).map_err(Error::CreateTransfer)?; diff --git a/devices/src/usb/xhci/event_ring.rs b/devices/src/usb/xhci/event_ring.rs index fcaff23..1742c77 100644 --- a/devices/src/usb/xhci/event_ring.rs +++ b/devices/src/usb/xhci/event_ring.rs @@ -3,7 +3,6 @@ // found in the LICENSE file. use data_model::DataInit; -use std; use std::fmt::{self, Display}; use std::mem::size_of; use std::sync::atomic::{fence, Ordering}; diff --git a/devices/src/usb/xhci/xhci_abi.rs b/devices/src/usb/xhci/xhci_abi.rs index 5a8748e..e9be3c3 100644 --- a/devices/src/usb/xhci/xhci_abi.rs +++ b/devices/src/usb/xhci/xhci_abi.rs @@ -8,8 +8,6 @@ use data_model::DataInit; use std::fmt::{self, Display}; use sys_util::GuestAddress; -use std; - #[derive(Debug)] pub enum Error { UnknownTrbType(BitFieldError), diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index b77a73a..e357ae3 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -3,8 +3,8 @@ // found in the LICENSE file. use crate::pci::{ - PciBarConfiguration, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, - PciInterruptPin, PciProgrammingInterface, PciSerialBusSubClass, + PciAddress, PciBarConfiguration, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, + PciHeaderType, PciInterruptPin, PciProgrammingInterface, PciSerialBusSubClass, }; use crate::register_space::{Register, RegisterSpace}; use crate::usb::host_backend::host_backend_device_provider::HostBackendDeviceProvider; @@ -94,7 +94,7 @@ enum XhciControllerState { /// xHCI PCI interface implementation. pub struct XhciController { config_regs: PciConfiguration, - pci_bus_dev: Option<(u8, u8)>, + pci_address: Option<PciAddress>, mem: GuestMemory, state: XhciControllerState, } @@ -114,7 +114,7 @@ impl XhciController { ); XhciController { config_regs, - pci_bus_dev: None, + pci_address: None, mem, state: XhciControllerState::Created { device_provider: usb_provider, @@ -166,8 +166,8 @@ impl PciDevice for XhciController { "xhci controller".to_owned() } - fn assign_bus_dev(&mut self, bus: u8, device: u8) { - self.pci_bus_dev = Some((bus, device)); + fn assign_address(&mut self, address: PciAddress) { + self.pci_address = Some(address); } fn keep_fds(&self) -> Vec<RawFd> { @@ -206,15 +206,20 @@ impl PciDevice for XhciController { &mut self, resources: &mut SystemAllocator, ) -> std::result::Result<Vec<(u64, u64)>, PciDeviceError> { - let (bus, dev) = self - .pci_bus_dev - .expect("assign_bus_dev must be called prior to allocate_io_bars"); + let address = self + .pci_address + .expect("assign_address must be called prior to allocate_io_bars"); // xHCI spec 5.2.1. let bar0_addr = resources .mmio_allocator(MmioType::Low) .allocate_with_align( XHCI_BAR0_SIZE, - Alloc::PciBar { bus, dev, bar: 0 }, + Alloc::PciBar { + bus: address.bus, + dev: address.dev, + func: address.func, + bar: 0, + }, "xhci_bar0".to_string(), XHCI_BAR0_SIZE, ) diff --git a/devices/src/virtio/balloon.rs b/devices/src/virtio/balloon.rs index 3e6d602..b549258 100644 --- a/devices/src/virtio/balloon.rs +++ b/devices/src/virtio/balloon.rs @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use std; use std::fmt::{self, Display}; use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/devices/src/virtio/console.rs b/devices/src/virtio/console.rs index 38f5bf1..50a5a76 100644 --- a/devices/src/virtio/console.rs +++ b/devices/src/virtio/console.rs @@ -13,6 +13,7 @@ use sys_util::{error, EventFd, GuestMemory, PollContext, PollToken}; use super::{ copy_config, Interrupt, Queue, Reader, VirtioDevice, Writer, TYPE_CONSOLE, VIRTIO_F_VERSION_1, }; +use crate::SerialDevice; const QUEUE_SIZE: u16 = 256; @@ -303,8 +304,9 @@ pub struct Console { keep_fds: Vec<RawFd>, } -impl Console { +impl SerialDevice for Console { fn new( + _evt_fd: EventFd, input: Option<Box<dyn io::Read + Send>>, output: Option<Box<dyn io::Write + Send>>, keep_fds: Vec<RawFd>, @@ -317,25 +319,6 @@ impl Console { keep_fds, } } - - /// Constructs a console with input and output streams. - pub fn new_in_out( - input: Box<dyn io::Read + Send>, - out: Box<dyn io::Write + Send>, - keep_fds: Vec<RawFd>, - ) -> Console { - Self::new(Some(input), Some(out), keep_fds) - } - - /// Constructs a console with an output stream but no input. - pub fn new_out(out: Box<dyn io::Write + Send>, keep_fds: Vec<RawFd>) -> Console { - Self::new(None, Some(out), keep_fds) - } - - /// Constructs a console with no connected input or output. - pub fn new_sink() -> Console { - Self::new(None, None, Vec::new()) - } } impl Drop for Console { diff --git a/devices/src/virtio/descriptor_utils.rs b/devices/src/virtio/descriptor_utils.rs index 990f147..f90264a 100644 --- a/devices/src/virtio/descriptor_utils.rs +++ b/devices/src/virtio/descriptor_utils.rs @@ -467,9 +467,17 @@ impl<'a> Writer<'a> { self.write_all(val.as_slice()) } + /// Writes all objects produced by `iter` into the descriptor chain buffer. Unlike `consume`, + /// this doesn't require the values to be stored in an intermediate collection first. It also + /// allows callers to choose which elements in a collection to write, for example by using the + /// `filter` or `take` methods of the `Iterator` trait. + pub fn write_iter<T: DataInit, I: Iterator<Item = T>>(&mut self, iter: I) -> io::Result<()> { + iter.map(|v| self.write_obj(v)).collect() + } + /// Writes a collection of objects into the descriptor chain buffer. pub fn consume<T: DataInit, C: IntoIterator<Item = T>>(&mut self, vals: C) -> io::Result<()> { - vals.into_iter().map(|v| self.write_obj(v)).collect() + self.write_iter(vals.into_iter()) } /// Returns number of bytes available for writing. May return an error if the combined diff --git a/devices/src/virtio/fs/filesystem.rs b/devices/src/virtio/fs/filesystem.rs index eb9726c..474a278 100644 --- a/devices/src/virtio/fs/filesystem.rs +++ b/devices/src/virtio/fs/filesystem.rs @@ -9,8 +9,6 @@ use std::io; use std::mem; use std::time::Duration; -use libc; - use crate::virtio::fs::fuse; pub use fuse::{FsOptions, IoctlFlags, IoctlIovec, OpenOptions, SetattrValid, ROOT_ID}; @@ -77,7 +75,7 @@ pub struct DirEntry<'a> { /// The name of this directory entry. There are no requirements for the contents of this field /// and any sequence of bytes is considered valid. - pub name: &'a [u8], + pub name: &'a CStr, } /// A reply to a `getxattr` method call. diff --git a/devices/src/virtio/fs/fuse.rs b/devices/src/virtio/fs/fuse.rs index 5c531cf..981596b 100644 --- a/devices/src/virtio/fs/fuse.rs +++ b/devices/src/virtio/fs/fuse.rs @@ -7,7 +7,6 @@ use std::mem; use bitflags::bitflags; use data_model::DataInit; use enumn::N; -use libc; /// Version number of this interface. pub const KERNEL_VERSION: u32 = 7; diff --git a/devices/src/virtio/fs/passthrough.rs b/devices/src/virtio/fs/passthrough.rs index d2034ba..bcc7c5d 100644 --- a/devices/src/virtio/fs/passthrough.rs +++ b/devices/src/virtio/fs/passthrough.rs @@ -15,7 +15,6 @@ use std::sync::Arc; use std::time::Duration; use data_model::DataInit; -use libc; use sync::Mutex; use sys_util::{error, ioctl_ior_nr, ioctl_iow_nr, ioctl_with_mut_ptr, ioctl_with_ptr}; @@ -26,8 +25,6 @@ use crate::virtio::fs::filesystem::{ use crate::virtio::fs::fuse; use crate::virtio::fs::multikey::MultikeyBTreeMap; -const CURRENT_DIR_CSTR: &[u8] = b".\0"; -const PARENT_DIR_CSTR: &[u8] = b"..\0"; const EMPTY_CSTR: &[u8] = b"\0"; const ROOT_CSTR: &[u8] = b"/\0"; const PROC_CSTR: &[u8] = b"/proc\0"; @@ -199,6 +196,20 @@ fn stat(f: &File) -> io::Result<libc::stat64> { } } +// Like `CStr::from_bytes_with_nul` but strips any bytes after the first '\0'-byte. Panics if `b` +// doesn't contain any '\0' bytes. +fn strip_padding(b: &[u8]) -> &CStr { + // It would be nice if we could use memchr here but that's locked behind an unstable gate. + let pos = b + .iter() + .position(|&c| c == 0) + .expect("`b` doesn't contain any nul bytes"); + + // Safe because we are creating this string with the first nul-byte we found so we can + // guarantee that it is nul-terminated and doesn't contain any interior nuls. + unsafe { CStr::from_bytes_with_nul_unchecked(&b[..pos + 1]) } +} + /// The caching policy that the file system should report to the FUSE client. By default the FUSE /// protocol uses close-to-open consistency. This means that any cached contents of the file are /// invalidated the next time that file is opened. @@ -576,19 +587,15 @@ impl PassthroughFs { let namelen = dirent64.d_reclen as usize - size_of::<LinuxDirent64>(); debug_assert!(namelen <= back.len(), "back is smaller than `namelen`"); - let name = &back[..namelen]; - let res = if name.starts_with(CURRENT_DIR_CSTR) || name.starts_with(PARENT_DIR_CSTR) { - // We don't want to report the "." and ".." entries. However, returning `Ok(0)` will - // break the loop so return `Ok` with a non-zero value instead. - Ok(1) - } else { - add_entry(DirEntry { - ino: dirent64.d_ino, - offset: dirent64.d_off as u64, - type_: dirent64.d_ty as u32, - name, - }) - }; + // The kernel will pad the name with additional nul bytes until it is 8-byte aligned so + // we need to strip those off here. + let name = strip_padding(&back[..namelen]); + let res = add_entry(DirEntry { + ino: dirent64.d_ino, + offset: dirent64.d_off as u64, + type_: dirent64.d_ty as u32, + name, + }); debug_assert!( rem.len() >= dirent64.d_reclen as usize, @@ -806,7 +813,8 @@ impl FileSystem for PassthroughFs { }), ); - let mut opts = FsOptions::DO_READDIRPLUS | FsOptions::READDIRPLUS_AUTO; + let mut opts = + FsOptions::DO_READDIRPLUS | FsOptions::READDIRPLUS_AUTO | FsOptions::EXPORT_SUPPORT; if self.cfg.writeback && capable.contains(FsOptions::WRITEBACK_CACHE) { opts |= FsOptions::WRITEBACK_CACHE; self.writeback.store(true, Ordering::Relaxed); @@ -933,16 +941,38 @@ impl FileSystem for PassthroughFs { F: FnMut(DirEntry, Entry) -> io::Result<usize>, { self.do_readdir(inode, handle, size, offset, |dir_entry| { - // Safe because the kernel guarantees that the buffer is nul-terminated. Additionally, - // the kernel will pad the name with '\0' bytes up to 8-byte alignment and there's no - // way for us to know exactly how many padding bytes there are. This would cause - // `CStr::from_bytes_with_nul` to return an error because it would think there are - // interior '\0' bytes. We trust the kernel to provide us with properly formatted data - // so we'll just skip the checks here. - let name = unsafe { CStr::from_bytes_with_nul_unchecked(dir_entry.name) }; - let entry = self.do_lookup(inode, name)?; - - add_entry(dir_entry, entry) + let name = dir_entry.name.to_bytes(); + let entry = if name == b"." || name == b".." { + // Don't do lookups on the current directory or the parent directory. Safe because + // this only contains integer fields and any value is valid. + let mut attr = unsafe { MaybeUninit::<libc::stat64>::zeroed().assume_init() }; + attr.st_ino = dir_entry.ino; + attr.st_mode = dir_entry.type_; + + // We use 0 for the inode value to indicate a negative entry. + Entry { + inode: 0, + generation: 0, + attr, + attr_timeout: Duration::from_secs(0), + entry_timeout: Duration::from_secs(0), + } + } else { + self.do_lookup(inode, dir_entry.name)? + }; + + let entry_inode = entry.inode; + add_entry(dir_entry, entry).map_err(|e| { + if entry_inode != 0 { + // Undo the `do_lookup` for this inode since we aren't going to report it to + // the kernel. If `entry_inode` was 0 then that means this was the "." or + // ".." entry and there wasn't a lookup in the first place. + let mut inodes = self.inodes.lock(); + forget_one(&mut inodes, entry_inode, 1); + } + + e + }) }) } @@ -1747,3 +1777,29 @@ impl FileSystem for PassthroughFs { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn padded_cstrings() { + assert_eq!(strip_padding(b".\0\0\0\0\0\0\0").to_bytes(), b"."); + assert_eq!(strip_padding(b"..\0\0\0\0\0\0").to_bytes(), b".."); + assert_eq!( + strip_padding(b"normal cstring\0").to_bytes(), + b"normal cstring" + ); + assert_eq!(strip_padding(b"\0\0\0\0").to_bytes(), b""); + assert_eq!( + strip_padding(b"interior\0nul bytes\0\0\0").to_bytes(), + b"interior" + ); + } + + #[test] + #[should_panic(expected = "`b` doesn't contain any nul bytes")] + fn no_nul_byte() { + strip_padding(b"no nul bytes in string"); + } +} diff --git a/devices/src/virtio/fs/server.rs b/devices/src/virtio/fs/server.rs index c9025f2..33b7c98 100644 --- a/devices/src/virtio/fs/server.rs +++ b/devices/src/virtio/fs/server.rs @@ -8,7 +8,6 @@ use std::io::{self, Read, Write}; use std::mem::size_of; use data_model::DataInit; -use libc; use sys_util::error; use crate::virtio::fs::filesystem::{ @@ -19,7 +18,7 @@ use crate::virtio::fs::fuse::*; use crate::virtio::fs::{Error, Result}; use crate::virtio::{Reader, Writer}; -const MAX_BUFFER_SIZE: u32 = (1 << 20); +const MAX_BUFFER_SIZE: u32 = 1 << 20; const DIRENT_PADDING: [u8; 8] = [0; 8]; struct ZCReader<'a>(Reader<'a>); @@ -1369,12 +1368,14 @@ fn add_dirent( d: DirEntry, entry: Option<Entry>, ) -> io::Result<usize> { - if d.name.len() > ::std::u32::MAX as usize { + // Strip the trailing '\0'. + let name = d.name.to_bytes(); + if name.len() > ::std::u32::MAX as usize { return Err(io::Error::from_raw_os_error(libc::EOVERFLOW)); } let dirent_len = size_of::<Dirent>() - .checked_add(d.name.len()) + .checked_add(name.len()) .ok_or_else(|| io::Error::from_raw_os_error(libc::EOVERFLOW))?; // Directory entries must be padded to 8-byte alignment. If adding 7 causes @@ -1402,12 +1403,12 @@ fn add_dirent( let dirent = Dirent { ino: d.ino, off: d.offset, - namelen: d.name.len() as u32, + namelen: name.len() as u32, type_: d.type_, }; cursor.write_all(dirent.as_slice())?; - cursor.write_all(d.name)?; + cursor.write_all(name)?; // We know that `dirent_len` <= `padded_dirent_len` due to the check above // so there's no need for checked arithmetic. diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs index fec2904..b305089 100644 --- a/devices/src/virtio/gpu/mod.rs +++ b/devices/src/virtio/gpu/mod.rs @@ -42,7 +42,9 @@ use self::virtio_2d_backend::Virtio2DBackend; use self::virtio_3d_backend::Virtio3DBackend; #[cfg(feature = "gfxstream")] use self::virtio_gfxstream_backend::VirtioGfxStreamBackend; -use crate::pci::{PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability}; +use crate::pci::{ + PciAddress, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability, +}; use vm_control::VmMemoryControlRequestSocket; @@ -1203,10 +1205,11 @@ impl VirtioDevice for Gpu { } // Require 1 BAR for mapping 3D buffers - fn get_device_bars(&mut self, bus: u8, dev: u8) -> Vec<PciBarConfiguration> { + fn get_device_bars(&mut self, address: PciAddress) -> Vec<PciBarConfiguration> { self.pci_bar = Some(Alloc::PciBar { - bus, - dev, + bus: address.bus, + dev: address.dev, + func: address.func, bar: GPU_BAR_NUM, }); vec![PciBarConfiguration::new( diff --git a/devices/src/virtio/gpu/protocol.rs b/devices/src/virtio/gpu/protocol.rs index 3df6975..5605dd3 100644 --- a/devices/src/virtio/gpu/protocol.rs +++ b/devices/src/virtio/gpu/protocol.rs @@ -137,7 +137,7 @@ pub fn virtio_gpu_cmd_str(cmd: u32) -> &'static str { } } -pub const VIRTIO_GPU_FLAG_FENCE: u32 = (1 << 0); +pub const VIRTIO_GPU_FLAG_FENCE: u32 = 1 << 0; #[derive(Copy, Clone, Debug, Default)] #[repr(C)] @@ -336,7 +336,7 @@ pub struct virtio_gpu_transfer_host_3d { unsafe impl DataInit for virtio_gpu_transfer_host_3d {} /* VIRTIO_GPU_CMD_RESOURCE_CREATE_3D */ -pub const VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP: u32 = (1 << 0); +pub const VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP: u32 = 1 << 0; #[derive(Copy, Clone, Debug, Default)] #[repr(C)] pub struct virtio_gpu_resource_create_3d { diff --git a/devices/src/virtio/input/constants.rs b/devices/src/virtio/input/constants.rs index 84799b2..7973312 100644 --- a/devices/src/virtio/input/constants.rs +++ b/devices/src/virtio/input/constants.rs @@ -12,7 +12,7 @@ pub const INPUT_PROP_POINTING_STICK: u16 = 0x05; pub const INPUT_PROP_ACCELEROMETER: u16 = 0x06; pub const INPUT_PROP_MAX: u16 = 0x1f; -pub const INPUT_PROP_CNT: u16 = (INPUT_PROP_MAX + 1); +pub const INPUT_PROP_CNT: u16 = INPUT_PROP_MAX + 1; pub const EV_SYN: u16 = 0x00; pub const EV_KEY: u16 = 0x01; @@ -638,7 +638,7 @@ pub const BTN_TRIGGER_HAPPY40: u16 = 0x2e7; pub const KEY_MIN_INTERESTING: u16 = KEY_MUTE; pub const KEY_MAX: u16 = 0x2ff; -pub const KEY_CNT: u16 = (KEY_MAX + 1); +pub const KEY_CNT: u16 = KEY_MAX + 1; pub const REL_X: u16 = 0x00; pub const REL_Y: u16 = 0x01; @@ -651,7 +651,7 @@ pub const REL_DIAL: u16 = 0x07; pub const REL_WHEEL: u16 = 0x08; pub const REL_MISC: u16 = 0x09; pub const REL_MAX: u16 = 0x0f; -pub const REL_CNT: u16 = (REL_MAX + 1); +pub const REL_CNT: u16 = REL_MAX + 1; pub const ABS_X: u16 = 0x00; pub const ABS_Y: u16 = 0x01; @@ -699,7 +699,7 @@ pub const ABS_MT_TOOL_X: u16 = 0x3c; pub const ABS_MT_TOOL_Y: u16 = 0x3d; pub const ABS_MAX: u16 = 0x3f; -pub const ABS_CNT: u16 = (ABS_MAX + 1); +pub const ABS_CNT: u16 = ABS_MAX + 1; pub const MSC_SERIAL: u16 = 0x00; pub const MSC_PULSELED: u16 = 0x01; @@ -708,7 +708,7 @@ pub const MSC_RAW: u16 = 0x03; pub const MSC_SCAN: u16 = 0x04; pub const MSC_TIMESTAMP: u16 = 0x05; pub const MSC_MAX: u16 = 0x07; -pub const MSC_CNT: u16 = (MSC_MAX + 1); +pub const MSC_CNT: u16 = MSC_MAX + 1; pub const LED_NUML: u16 = 0x00; pub const LED_CAPSL: u16 = 0x01; @@ -722,12 +722,12 @@ pub const LED_MISC: u16 = 0x08; pub const LED_MAIL: u16 = 0x09; pub const LED_CHARGING: u16 = 0x0a; pub const LED_MAX: u16 = 0x0f; -pub const LED_CNT: u16 = (LED_MAX + 1); +pub const LED_CNT: u16 = LED_MAX + 1; pub const REP_DELAY: u16 = 0x00; pub const REP_PERIOD: u16 = 0x01; pub const REP_MAX: u16 = 0x01; -pub const REP_CNT: u16 = (REP_MAX + 1); +pub const REP_CNT: u16 = REP_MAX + 1; // Should match linux/virtio_input.h pub const VIRTIO_INPUT_CFG_UNSET: u8 = 0x00; diff --git a/devices/src/virtio/net.rs b/devices/src/virtio/net.rs index a15ab03..8e9aa9e 100644 --- a/devices/src/virtio/net.rs +++ b/devices/src/virtio/net.rs @@ -13,7 +13,6 @@ use std::sync::Arc; use std::thread; use data_model::{DataInit, Le16, Le64}; -use net_sys; use net_util::{Error as TapError, MacAddress, TapT}; use sys_util::Error as SysError; use sys_util::{error, warn, EventFd, GuestMemory, PollContext, PollToken, WatchingEvents}; diff --git a/devices/src/virtio/p9.rs b/devices/src/virtio/p9.rs index 5e483c3..7854c39 100644 --- a/devices/src/virtio/p9.rs +++ b/devices/src/virtio/p9.rs @@ -10,7 +10,6 @@ use std::path::{Path, PathBuf}; use std::result; use std::thread; -use p9; use sys_util::{error, warn, Error as SysError, EventFd, GuestMemory, PollContext, PollToken}; use virtio_sys::vhost::VIRTIO_F_VERSION_1; diff --git a/devices/src/virtio/pmem.rs b/devices/src/virtio/pmem.rs index 499e110..cbeecc3 100644 --- a/devices/src/virtio/pmem.rs +++ b/devices/src/virtio/pmem.rs @@ -8,8 +8,8 @@ use std::io; use std::os::unix::io::{AsRawFd, RawFd}; use std::thread; -use sys_util::Result as SysResult; use sys_util::{error, EventFd, GuestAddress, GuestMemory, PollContext, PollToken}; +use sys_util::{Error as SysError, Result as SysResult}; use data_model::{DataInit, Le32, Le64}; @@ -89,6 +89,7 @@ struct Worker { memory: GuestMemory, pmem_device_socket: VmMsyncRequestSocket, mapping_arena_slot: u32, + mapping_size: usize, } impl Worker { @@ -98,6 +99,7 @@ impl Worker { let request = VmMsyncRequest::MsyncArena { slot: self.mapping_arena_slot, offset: 0, // The pmem backing file is always at offset 0 in the arena. + size: self.mapping_size, }; if let Err(e) = self.pmem_device_socket.send(&request) { @@ -235,6 +237,10 @@ impl Pmem { mapping_size: u64, pmem_device_socket: Option<VmMsyncRequestSocket>, ) -> SysResult<Pmem> { + if mapping_size > usize::max_value() as u64 { + return Err(SysError::new(libc::EOVERFLOW)); + } + Ok(Pmem { kill_event: None, worker_thread: None, @@ -308,6 +314,8 @@ impl VirtioDevice for Pmem { let queue_event = queue_events.remove(0); let mapping_arena_slot = self.mapping_arena_slot; + // We checked that this fits in a usize in `Pmem::new`. + let mapping_size = self.mapping_size as usize; if let Some(pmem_device_socket) = self.pmem_device_socket.take() { let (self_kill_event, kill_event) = @@ -329,6 +337,7 @@ impl VirtioDevice for Pmem { queue, pmem_device_socket, mapping_arena_slot, + mapping_size, }; worker.run(queue_event, kill_event); }); diff --git a/devices/src/virtio/rng.rs b/devices/src/virtio/rng.rs index 9ce8dae..3897ee3 100644 --- a/devices/src/virtio/rng.rs +++ b/devices/src/virtio/rng.rs @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use std; use std::fmt::{self, Display}; use std::fs::File; use std::io; diff --git a/devices/src/virtio/tpm.rs b/devices/src/virtio/tpm.rs index 95f7092..727c634 100644 --- a/devices/src/virtio/tpm.rs +++ b/devices/src/virtio/tpm.rs @@ -12,7 +12,6 @@ use std::path::PathBuf; use std::thread; use sys_util::{error, EventFd, GuestMemory, PollContext, PollToken}; -use tpm2; use super::{ DescriptorChain, DescriptorError, Interrupt, Queue, Reader, VirtioDevice, Writer, TYPE_TPM, diff --git a/devices/src/virtio/vhost/mod.rs b/devices/src/virtio/vhost/mod.rs index 86ed81e..4d7623f 100644 --- a/devices/src/virtio/vhost/mod.rs +++ b/devices/src/virtio/vhost/mod.rs @@ -4,7 +4,6 @@ //! Implements vhost-based virtio devices. -use std; use std::fmt::{self, Display}; use net_util::Error as TapError; diff --git a/devices/src/virtio/vhost/net.rs b/devices/src/virtio/vhost/net.rs index 681cfe8..ce13226 100644 --- a/devices/src/virtio/vhost/net.rs +++ b/devices/src/virtio/vhost/net.rs @@ -7,7 +7,6 @@ use std::net::Ipv4Addr; use std::os::unix::io::{AsRawFd, RawFd}; use std::thread; -use net_sys; use net_util::{MacAddress, TapT}; use sys_util::{error, warn, EventFd, GuestMemory}; diff --git a/devices/src/virtio/virtio_device.rs b/devices/src/virtio/virtio_device.rs index 6eb5548..7c07651 100644 --- a/devices/src/virtio/virtio_device.rs +++ b/devices/src/virtio/virtio_device.rs @@ -7,7 +7,7 @@ use std::os::unix::io::RawFd; use sys_util::{EventFd, GuestMemory}; use super::*; -use crate::pci::{MsixStatus, PciBarConfiguration, PciCapability}; +use crate::pci::{MsixStatus, PciAddress, PciBarConfiguration, PciCapability}; /// Trait for virtio devices to be driven by a virtio transport. /// @@ -75,7 +75,7 @@ pub trait VirtioDevice: Send { } /// Returns any additional BAR configuration required by the device. - fn get_device_bars(&mut self, _bus: u8, _dev: u8) -> Vec<PciBarConfiguration> { + fn get_device_bars(&mut self, _address: PciAddress) -> Vec<PciBarConfiguration> { Vec::new() } diff --git a/devices/src/virtio/virtio_pci_device.rs b/devices/src/virtio/virtio_pci_device.rs index e63abe9..eb91e58 100644 --- a/devices/src/virtio/virtio_pci_device.rs +++ b/devices/src/virtio/virtio_pci_device.rs @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use std; use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -16,10 +15,10 @@ use sys_util::{warn, EventFd, GuestMemory, Result}; use super::*; use crate::pci::{ - MsixCap, MsixConfig, PciBarConfiguration, PciCapability, PciCapabilityID, PciClassCode, - PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciInterruptPin, PciSubclass, + MsixCap, MsixConfig, PciAddress, PciBarConfiguration, PciCapability, PciCapabilityID, + PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciInterruptPin, + PciSubclass, }; - use vm_control::VmIrqRequestSocket; use self::virtio_pci_common_config::VirtioPciCommonConfig; @@ -212,7 +211,7 @@ const VIRTIO_PCI_DEVICE_ID_BASE: u16 = 0x1040; // Add to device type to get devi /// transport for virtio devices. pub struct VirtioPciDevice { config_regs: PciConfiguration, - pci_bus_dev: Option<(u8, u8)>, + pci_address: Option<PciAddress>, device: Box<dyn VirtioDevice>, device_activated: bool, @@ -270,7 +269,7 @@ impl VirtioPciDevice { Ok(VirtioPciDevice { config_regs, - pci_bus_dev: None, + pci_address: None, device, device_activated: false, interrupt_status: Arc::new(AtomicUsize::new(0)), @@ -393,8 +392,8 @@ impl PciDevice for VirtioPciDevice { format!("virtio-pci ({})", self.device.debug_label()) } - fn assign_bus_dev(&mut self, bus: u8, device: u8) { - self.pci_bus_dev = Some((bus, device)); + fn assign_address(&mut self, address: PciAddress) { + self.pci_address = Some(address); } fn keep_fds(&self) -> Vec<RawFd> { @@ -426,16 +425,21 @@ impl PciDevice for VirtioPciDevice { &mut self, resources: &mut SystemAllocator, ) -> std::result::Result<Vec<(u64, u64)>, PciDeviceError> { - let (bus, dev) = self - .pci_bus_dev - .expect("assign_bus_dev must be called prior to allocate_io_bars"); + let address = self + .pci_address + .expect("assign_address must be called prior to allocate_io_bars"); // Allocate one bar for the structures pointed to by the capability structures. let mut ranges = Vec::new(); let settings_config_addr = resources .mmio_allocator(MmioType::Low) .allocate_with_align( CAPABILITY_BAR_SIZE, - Alloc::PciBar { bus, dev, bar: 0 }, + Alloc::PciBar { + bus: address.bus, + dev: address.dev, + func: address.func, + bar: 0, + }, format!( "virtio-{}-cap_bar", type_to_str(self.device.device_type()).unwrap_or("?") @@ -464,18 +468,19 @@ impl PciDevice for VirtioPciDevice { &mut self, resources: &mut SystemAllocator, ) -> std::result::Result<Vec<(u64, u64)>, PciDeviceError> { - let (bus, dev) = self - .pci_bus_dev - .expect("assign_bus_dev must be called prior to allocate_device_bars"); + let address = self + .pci_address + .expect("assign_address must be called prior to allocate_device_bars"); let mut ranges = Vec::new(); - for config in self.device.get_device_bars(bus, dev) { + for config in self.device.get_device_bars(address) { let device_addr = resources .mmio_allocator(MmioType::High) .allocate_with_align( config.get_size(), Alloc::PciBar { - bus, - dev, + bus: address.bus, + dev: address.dev, + func: address.func, bar: config.get_register_index() as u8, }, format!( diff --git a/devices/src/virtio/wl.rs b/devices/src/virtio/wl.rs index 95075b4..517a22e 100644 --- a/devices/src/virtio/wl.rs +++ b/devices/src/virtio/wl.rs @@ -47,7 +47,7 @@ use std::thread; use std::time::Duration; #[cfg(feature = "wl-dmabuf")] -use libc::{dup, EBADF, EINVAL}; +use libc::{EBADF, EINVAL}; use data_model::VolatileMemoryError; use data_model::*; @@ -543,9 +543,6 @@ impl fmt::Debug for WlVfd { impl WlVfd { fn connect<P: AsRef<Path>>(path: P) -> WlResult<WlVfd> { let socket = UnixStream::connect(path).map_err(WlError::SocketConnect)?; - socket - .set_nonblocking(true) - .map_err(WlError::SocketNonBlock)?; let mut vfd = WlVfd::default(); vfd.socket = Some(socket); Ok(vfd) @@ -587,15 +584,13 @@ impl WlVfd { })?; match allocate_and_register_gpu_memory_response { VmMemoryResponse::AllocateAndRegisterGpuMemory { - fd, + fd: MaybeOwnedFd::Owned(file), pfn, slot, desc, } => { let mut vfd = WlVfd::default(); - // Duplicate FD for shared memory instance. - let raw_fd = unsafe { File::from_raw_fd(dup(fd.as_raw_fd())) }; - let vfd_shm = SharedMemory::from_raw_fd(raw_fd).map_err(WlError::NewAlloc)?; + let vfd_shm = SharedMemory::from_file(file).map_err(WlError::NewAlloc)?; vfd.guest_shared_memory = Some((vfd_shm.size(), vfd_shm.into())); vfd.slot = Some((slot, pfn, vm)); vfd.is_dmabuf = true; |