• 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::fmt::{self, Display};
6 
7 use base::{Event, RawDescriptor};
8 use hypervisor::Datamatch;
9 use resources::{Error as SystemAllocatorFaliure, SystemAllocator};
10 
11 use crate::bus::ConfigWriteResult;
12 use crate::pci::pci_configuration::{
13     self, COMMAND_REG, COMMAND_REG_IO_SPACE_MASK, COMMAND_REG_MEMORY_SPACE_MASK,
14 };
15 use crate::pci::{PciAddress, PciInterruptPin};
16 #[cfg(feature = "audio")]
17 use crate::virtio::snd::vios_backend::Error as VioSError;
18 use crate::{BusAccessInfo, BusDevice};
19 
20 #[derive(Debug)]
21 pub enum Error {
22     /// Setup of the device capabilities failed.
23     CapabilitiesSetup(pci_configuration::Error),
24     /// Allocating space for an IO BAR failed.
25     IoAllocationFailed(u64, SystemAllocatorFaliure),
26     /// Registering an IO BAR failed.
27     IoRegistrationFailed(u64, pci_configuration::Error),
28     /// Create cras client failed.
29     #[cfg(feature = "audio")]
30     CreateCrasClientFailed(libcras::Error),
31     /// Create VioS client failed.
32     #[cfg(feature = "audio")]
33     CreateViosClientFailed(VioSError),
34     /// PCI Address allocation failure.
35     PciAllocationFailed,
36     /// PCI Address is not allocated.
37     PciAddressMissing,
38 }
39 pub type Result<T> = std::result::Result<T, Error>;
40 
41 impl Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result42     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43         use self::Error::*;
44 
45         match self {
46             CapabilitiesSetup(e) => write!(f, "failed to add capability {}", e),
47             #[cfg(feature = "audio")]
48             CreateCrasClientFailed(e) => write!(f, "failed to create CRAS Client: {}", e),
49             #[cfg(feature = "audio")]
50             CreateViosClientFailed(e) => write!(f, "failed to create VioS Client: {}", e),
51             IoAllocationFailed(size, e) => write!(
52                 f,
53                 "failed to allocate space for an IO BAR, size={}: {}",
54                 size, e
55             ),
56             IoRegistrationFailed(addr, e) => {
57                 write!(f, "failed to register an IO BAR, addr={} err={}", addr, e)
58             }
59             PciAllocationFailed => write!(f, "failed to allocate PCI address"),
60             PciAddressMissing => write!(f, "PCI address is not allocated"),
61         }
62     }
63 }
64 
65 pub trait PciDevice: Send {
66     /// Returns a label suitable for debug output.
debug_label(&self) -> String67     fn debug_label(&self) -> String;
68     /// Allocate and return an unique bus, device and function number for this device.
allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress>69     fn allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress>;
70     /// A vector of device-specific file descriptors that must be kept open
71     /// after jailing. Must be called before the process is jailed.
keep_rds(&self) -> Vec<RawDescriptor>72     fn keep_rds(&self) -> Vec<RawDescriptor>;
73     /// Assign a legacy PCI IRQ to this device.
74     /// The device may write to `irq_evt` to trigger an interrupt.
75     /// When `irq_resample_evt` is signaled, the device should re-assert `irq_evt` if necessary.
assign_irq( &mut self, _irq_evt: Event, _irq_resample_evt: Event, _irq_num: u32, _irq_pin: PciInterruptPin, )76     fn assign_irq(
77         &mut self,
78         _irq_evt: Event,
79         _irq_resample_evt: Event,
80         _irq_num: u32,
81         _irq_pin: PciInterruptPin,
82     ) {
83     }
84     /// Allocates the needed IO BAR space using the `allocate` function which takes a size and
85     /// returns an address. Returns a Vec of (address, length) tuples.
allocate_io_bars(&mut self, _resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>>86     fn allocate_io_bars(&mut self, _resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> {
87         Ok(Vec::new())
88     }
89 
90     /// Allocates the needed device BAR space. Returns a Vec of (address, length) tuples.
91     /// Unlike MMIO BARs (see allocate_io_bars), device BARs are not expected to incur VM exits
92     /// - these BARs represent normal memory.
allocate_device_bars( &mut self, _resources: &mut SystemAllocator, ) -> Result<Vec<(u64, u64)>>93     fn allocate_device_bars(
94         &mut self,
95         _resources: &mut SystemAllocator,
96     ) -> Result<Vec<(u64, u64)>> {
97         Ok(Vec::new())
98     }
99 
100     /// Register any capabilties specified by the device.
register_device_capabilities(&mut self) -> Result<()>101     fn register_device_capabilities(&mut self) -> Result<()> {
102         Ok(())
103     }
104 
105     /// Gets a list of ioevents that should be registered with the running VM. The list is
106     /// returned as a Vec of (event, addr, datamatch) tuples.
ioevents(&self) -> Vec<(&Event, u64, Datamatch)>107     fn ioevents(&self) -> Vec<(&Event, u64, Datamatch)> {
108         Vec::new()
109     }
110 
111     /// Reads from a PCI configuration register.
112     /// * `reg_idx` - PCI register index (in units of 4 bytes).
read_config_register(&self, reg_idx: usize) -> u32113     fn read_config_register(&self, reg_idx: usize) -> u32;
114 
115     /// Writes to a PCI configuration register.
116     /// * `reg_idx` - PCI register index (in units of 4 bytes).
117     /// * `offset`  - byte offset within 4-byte register.
118     /// * `data`    - The data to write.
write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8])119     fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]);
120 
121     /// Reads from a BAR region mapped in to the device.
122     /// * `addr` - The guest address inside the BAR.
123     /// * `data` - Filled with the data from `addr`.
read_bar(&mut self, addr: u64, data: &mut [u8])124     fn read_bar(&mut self, addr: u64, data: &mut [u8]);
125     /// Writes to a BAR region mapped in to the device.
126     /// * `addr` - The guest address inside the BAR.
127     /// * `data` - The data to write.
write_bar(&mut self, addr: u64, data: &[u8])128     fn write_bar(&mut self, addr: u64, data: &[u8]);
129     /// Invoked when the device is sandboxed.
on_device_sandboxed(&mut self)130     fn on_device_sandboxed(&mut self) {}
131 }
132 
133 impl<T: PciDevice> BusDevice for T {
debug_label(&self) -> String134     fn debug_label(&self) -> String {
135         PciDevice::debug_label(self)
136     }
137 
read(&mut self, info: BusAccessInfo, data: &mut [u8])138     fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
139         self.read_bar(info.address, data)
140     }
141 
write(&mut self, info: BusAccessInfo, data: &[u8])142     fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
143         self.write_bar(info.address, data)
144     }
145 
config_register_write( &mut self, reg_idx: usize, offset: u64, data: &[u8], ) -> ConfigWriteResult146     fn config_register_write(
147         &mut self,
148         reg_idx: usize,
149         offset: u64,
150         data: &[u8],
151     ) -> ConfigWriteResult {
152         let mut result = ConfigWriteResult {
153             ..Default::default()
154         };
155         if offset as usize + data.len() > 4 {
156             return result;
157         }
158 
159         if reg_idx == COMMAND_REG {
160             let old_command_reg = self.read_config_register(COMMAND_REG);
161             self.write_config_register(reg_idx, offset, data);
162             let new_command_reg = self.read_config_register(COMMAND_REG);
163 
164             // Inform the caller of state changes.
165             if (old_command_reg ^ new_command_reg) & COMMAND_REG_MEMORY_SPACE_MASK != 0 {
166                 result.mem_bus_new_state =
167                     Some((new_command_reg & COMMAND_REG_MEMORY_SPACE_MASK) != 0);
168             }
169             if (old_command_reg ^ new_command_reg) & COMMAND_REG_IO_SPACE_MASK != 0 {
170                 result.io_bus_new_state = Some((new_command_reg & COMMAND_REG_IO_SPACE_MASK) != 0);
171             }
172         } else {
173             self.write_config_register(reg_idx, offset, data);
174         }
175 
176         result
177     }
178 
config_register_read(&self, reg_idx: usize) -> u32179     fn config_register_read(&self, reg_idx: usize) -> u32 {
180         self.read_config_register(reg_idx)
181     }
182 
on_sandboxed(&mut self)183     fn on_sandboxed(&mut self) {
184         self.on_device_sandboxed();
185     }
186 }
187 
188 impl<T: PciDevice + ?Sized> PciDevice for Box<T> {
189     /// Returns a label suitable for debug output.
debug_label(&self) -> String190     fn debug_label(&self) -> String {
191         (**self).debug_label()
192     }
allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress>193     fn allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress> {
194         (**self).allocate_address(resources)
195     }
keep_rds(&self) -> Vec<RawDescriptor>196     fn keep_rds(&self) -> Vec<RawDescriptor> {
197         (**self).keep_rds()
198     }
assign_irq( &mut self, irq_evt: Event, irq_resample_evt: Event, irq_num: u32, irq_pin: PciInterruptPin, )199     fn assign_irq(
200         &mut self,
201         irq_evt: Event,
202         irq_resample_evt: Event,
203         irq_num: u32,
204         irq_pin: PciInterruptPin,
205     ) {
206         (**self).assign_irq(irq_evt, irq_resample_evt, irq_num, irq_pin)
207     }
allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>>208     fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> {
209         (**self).allocate_io_bars(resources)
210     }
allocate_device_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>>211     fn allocate_device_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> {
212         (**self).allocate_device_bars(resources)
213     }
register_device_capabilities(&mut self) -> Result<()>214     fn register_device_capabilities(&mut self) -> Result<()> {
215         (**self).register_device_capabilities()
216     }
ioevents(&self) -> Vec<(&Event, u64, Datamatch)>217     fn ioevents(&self) -> Vec<(&Event, u64, Datamatch)> {
218         (**self).ioevents()
219     }
read_config_register(&self, reg_idx: usize) -> u32220     fn read_config_register(&self, reg_idx: usize) -> u32 {
221         (**self).read_config_register(reg_idx)
222     }
write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8])223     fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
224         (**self).write_config_register(reg_idx, offset, data)
225     }
read_bar(&mut self, addr: u64, data: &mut [u8])226     fn read_bar(&mut self, addr: u64, data: &mut [u8]) {
227         (**self).read_bar(addr, data)
228     }
write_bar(&mut self, addr: u64, data: &[u8])229     fn write_bar(&mut self, addr: u64, data: &[u8]) {
230         (**self).write_bar(addr, data)
231     }
232     /// Invoked when the device is sandboxed.
on_device_sandboxed(&mut self)233     fn on_device_sandboxed(&mut self) {
234         (**self).on_device_sandboxed()
235     }
236 }
237 
238 #[cfg(test)]
239 mod tests {
240     use super::*;
241     use pci_configuration::{PciClassCode, PciConfiguration, PciHeaderType, PciMultimediaSubclass};
242 
243     struct TestDev {
244         pub config_regs: PciConfiguration,
245     }
246 
247     impl PciDevice for TestDev {
debug_label(&self) -> String248         fn debug_label(&self) -> String {
249             "test".to_owned()
250         }
251 
keep_rds(&self) -> Vec<RawDescriptor>252         fn keep_rds(&self) -> Vec<RawDescriptor> {
253             Vec::new()
254         }
255 
read_config_register(&self, reg_idx: usize) -> u32256         fn read_config_register(&self, reg_idx: usize) -> u32 {
257             self.config_regs.read_reg(reg_idx)
258         }
259 
write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8])260         fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
261             (&mut self.config_regs).write_reg(reg_idx, offset, data)
262         }
263 
read_bar(&mut self, _addr: u64, _data: &mut [u8])264         fn read_bar(&mut self, _addr: u64, _data: &mut [u8]) {}
265 
write_bar(&mut self, _addr: u64, _data: &[u8])266         fn write_bar(&mut self, _addr: u64, _data: &[u8]) {}
267 
allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress>268         fn allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress> {
269             Err(Error::PciAllocationFailed)
270         }
271     }
272 
273     #[test]
config_write_result()274     fn config_write_result() {
275         let mut test_dev = TestDev {
276             config_regs: PciConfiguration::new(
277                 0x1234,
278                 0xABCD,
279                 PciClassCode::MultimediaController,
280                 &PciMultimediaSubclass::AudioDevice,
281                 None,
282                 PciHeaderType::Device,
283                 0x5678,
284                 0xEF01,
285                 0,
286             ),
287         };
288 
289         // Initialize command register to an all-zeroes value.
290         test_dev.config_register_write(COMMAND_REG, 0, &0u32.to_le_bytes());
291 
292         // Enable IO space access (bit 0 of command register).
293         assert_eq!(
294             test_dev.config_register_write(COMMAND_REG, 0, &1u32.to_le_bytes()),
295             ConfigWriteResult {
296                 mem_bus_new_state: None,
297                 io_bus_new_state: Some(true),
298             }
299         );
300 
301         // Enable memory space access (bit 1 of command register).
302         assert_eq!(
303             test_dev.config_register_write(COMMAND_REG, 0, &3u32.to_le_bytes()),
304             ConfigWriteResult {
305                 mem_bus_new_state: Some(true),
306                 io_bus_new_state: None,
307             }
308         );
309 
310         // Rewrite the same IO + mem value again (result should be no change).
311         assert_eq!(
312             test_dev.config_register_write(COMMAND_REG, 0, &3u32.to_le_bytes()),
313             ConfigWriteResult {
314                 mem_bus_new_state: None,
315                 io_bus_new_state: None,
316             }
317         );
318 
319         // Disable IO space access, leaving mem enabled.
320         assert_eq!(
321             test_dev.config_register_write(COMMAND_REG, 0, &2u32.to_le_bytes()),
322             ConfigWriteResult {
323                 mem_bus_new_state: None,
324                 io_bus_new_state: Some(false),
325             }
326         );
327 
328         // Re-enable IO space and disable mem simultaneously.
329         assert_eq!(
330             test_dev.config_register_write(COMMAND_REG, 0, &1u32.to_le_bytes()),
331             ConfigWriteResult {
332                 mem_bus_new_state: Some(false),
333                 io_bus_new_state: Some(true),
334             }
335         );
336     }
337 }
338