xref: /cloud-hypervisor/pci/src/bus.rs (revision a3692144f0970f8d3f8ef3f8123d6a60ae982771)
1e8308dd1SSamuel Ortiz // Copyright 2018 The Chromium OS Authors. All rights reserved.
2e8308dd1SSamuel Ortiz // Use of this source code is governed by a BSD-style license that can be
3040ea543SSamuel Ortiz // found in the LICENSE-BSD-3-Clause file.
45e9886bbSRuslan Mstoi //
55e9886bbSRuslan Mstoi // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
6e8308dd1SSamuel Ortiz 
788a9f799SRob Bradford use std::any::Any;
888a9f799SRob Bradford use std::collections::HashMap;
988a9f799SRob Bradford use std::ops::DerefMut;
1088a9f799SRob Bradford use std::sync::{Arc, Barrier, Mutex};
1188a9f799SRob Bradford 
1288a9f799SRob Bradford use byteorder::{ByteOrder, LittleEndian};
13a007b750SPhilipp Schuster use thiserror::Error;
1488a9f799SRob Bradford use vm_device::{Bus, BusDevice, BusDeviceSync};
1588a9f799SRob Bradford 
1612681650SSebastien Boeuf use crate::configuration::{
1712681650SSebastien Boeuf     PciBarRegionType, PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType,
1812681650SSebastien Boeuf };
19cb52cf91SBo Chen use crate::device::{BarReprogrammingParams, DeviceRelocation, Error as PciDeviceError, PciDevice};
2089218b6dSSebastien Boeuf use crate::PciBarConfiguration;
21e8308dd1SSamuel Ortiz 
22e8308dd1SSamuel Ortiz const VENDOR_ID_INTEL: u16 = 0x8086;
23e8308dd1SSamuel Ortiz const DEVICE_ID_INTEL_VIRT_PCIE_HOST: u16 = 0x0d57;
24df71aaeeSSebastien Boeuf const NUM_DEVICE_IDS: usize = 32;
25e8308dd1SSamuel Ortiz 
26e8308dd1SSamuel Ortiz /// Errors for device manager.
27a007b750SPhilipp Schuster #[derive(Error, Debug)]
28e8308dd1SSamuel Ortiz pub enum PciRootError {
29e8308dd1SSamuel Ortiz     /// Could not allocate device address space for the device.
30*a3692144SPhilipp Schuster     #[error("Could not allocate device address space for the device")]
31a007b750SPhilipp Schuster     AllocateDeviceAddrs(#[source] PciDeviceError),
32e8308dd1SSamuel Ortiz     /// Could not allocate an IRQ number.
33a007b750SPhilipp Schuster     #[error("Could not allocate an IRQ number")]
34e8308dd1SSamuel Ortiz     AllocateIrq,
3512681650SSebastien Boeuf     /// Could not add a device to the port io bus.
36*a3692144SPhilipp Schuster     #[error("Could not add a device to the port io bus")]
37a007b750SPhilipp Schuster     PioInsert(#[source] vm_device::BusError),
38e8308dd1SSamuel Ortiz     /// Could not add a device to the mmio bus.
39*a3692144SPhilipp Schuster     #[error("Could not add a device to the mmio bus")]
40a007b750SPhilipp Schuster     MmioInsert(#[source] vm_device::BusError),
41df71aaeeSSebastien Boeuf     /// Could not find an available device slot on the PCI bus.
42a007b750SPhilipp Schuster     #[error("Could not find an available device slot on the PCI bus")]
43df71aaeeSSebastien Boeuf     NoPciDeviceSlotAvailable,
44b50cbe50SSebastien Boeuf     /// Invalid PCI device identifier provided.
45a007b750SPhilipp Schuster     #[error("Invalid PCI device identifier provided")]
46b50cbe50SSebastien Boeuf     InvalidPciDeviceSlot(usize),
471e0ebb76SSebastien Boeuf     /// Valid PCI device identifier but already used.
48a007b750SPhilipp Schuster     #[error("Valid PCI device identifier but already used")]
491e0ebb76SSebastien Boeuf     AlreadyInUsePciDeviceSlot(usize),
50e8308dd1SSamuel Ortiz }
51e8308dd1SSamuel Ortiz pub type Result<T> = std::result::Result<T, PciRootError>;
52e8308dd1SSamuel Ortiz 
532bb0b22cSJing Liu /// Emulates the PCI Root bridge device.
54e8308dd1SSamuel Ortiz pub struct PciRoot {
552bb0b22cSJing Liu     /// Configuration space.
562bb0b22cSJing Liu     config: PciConfiguration,
57e8308dd1SSamuel Ortiz }
58e8308dd1SSamuel Ortiz 
59e8308dd1SSamuel Ortiz impl PciRoot {
602bb0b22cSJing Liu     /// Create an empty PCI root bridge.
new(config: Option<PciConfiguration>) -> Self612bb0b22cSJing Liu     pub fn new(config: Option<PciConfiguration>) -> Self {
622bb0b22cSJing Liu         if let Some(config) = config {
632bb0b22cSJing Liu             PciRoot { config }
64e8308dd1SSamuel Ortiz         } else {
65e8308dd1SSamuel Ortiz             PciRoot {
662bb0b22cSJing Liu                 config: PciConfiguration::new(
67e8308dd1SSamuel Ortiz                     VENDOR_ID_INTEL,
68e8308dd1SSamuel Ortiz                     DEVICE_ID_INTEL_VIRT_PCIE_HOST,
699bd5ec89SRob Bradford                     0,
70e8308dd1SSamuel Ortiz                     PciClassCode::BridgeDevice,
71e8308dd1SSamuel Ortiz                     &PciBridgeSubclass::HostBridge,
72e8308dd1SSamuel Ortiz                     None,
732b2c31d2SSamuel Ortiz                     PciHeaderType::Device,
74e8308dd1SSamuel Ortiz                     0,
75e8308dd1SSamuel Ortiz                     0,
764d98dcb0SSebastien Boeuf                     None,
77eae80438SSebastien Boeuf                     None,
78e8308dd1SSamuel Ortiz                 ),
79e8308dd1SSamuel Ortiz             }
80e8308dd1SSamuel Ortiz         }
81e8308dd1SSamuel Ortiz     }
82e8308dd1SSamuel Ortiz }
83e8308dd1SSamuel Ortiz 
842bb0b22cSJing Liu impl BusDevice for PciRoot {}
852bb0b22cSJing Liu 
862bb0b22cSJing Liu impl PciDevice for PciRoot {
write_config_register( &mut self, reg_idx: usize, offset: u64, data: &[u8], ) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>)877cc729c7SRob Bradford     fn write_config_register(
887cc729c7SRob Bradford         &mut self,
897cc729c7SRob Bradford         reg_idx: usize,
907cc729c7SRob Bradford         offset: u64,
917cc729c7SRob Bradford         data: &[u8],
92aaf86ef2SBo Chen     ) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>) {
93cb52cf91SBo Chen         (
94cb52cf91SBo Chen             self.config.write_config_register(reg_idx, offset, data),
95cb52cf91SBo Chen             None,
96cb52cf91SBo Chen         )
972bb0b22cSJing Liu     }
982bb0b22cSJing Liu 
read_config_register(&mut self, reg_idx: usize) -> u3299db9f9b78SSebastien Boeuf     fn read_config_register(&mut self, reg_idx: usize) -> u32 {
1002bb0b22cSJing Liu         self.config.read_reg(reg_idx)
1012bb0b22cSJing Liu     }
102de21c9baSSebastien Boeuf 
as_any_mut(&mut self) -> &mut dyn Any103d99f2942SWei Liu     fn as_any_mut(&mut self) -> &mut dyn Any {
104de21c9baSSebastien Boeuf         self
105de21c9baSSebastien Boeuf     }
1065264d545SSebastien Boeuf 
id(&self) -> Option<String>1075264d545SSebastien Boeuf     fn id(&self) -> Option<String> {
1085264d545SSebastien Boeuf         None
1095264d545SSebastien Boeuf     }
1102bb0b22cSJing Liu }
1112bb0b22cSJing Liu 
112833a3d45SRob Bradford pub struct PciBus {
1132bb0b22cSJing Liu     /// Devices attached to this bus.
1142bb0b22cSJing Liu     /// Device 0 is host bridge.
1158d785bbdSSebastien Boeuf     devices: HashMap<u32, Arc<Mutex<dyn PciDevice>>>,
11649268bffSSebastien Boeuf     device_reloc: Arc<dyn DeviceRelocation>,
117df71aaeeSSebastien Boeuf     device_ids: Vec<bool>,
118e8308dd1SSamuel Ortiz }
119e8308dd1SSamuel Ortiz 
120833a3d45SRob Bradford impl PciBus {
new(pci_root: PciRoot, device_reloc: Arc<dyn DeviceRelocation>) -> Self12149268bffSSebastien Boeuf     pub fn new(pci_root: PciRoot, device_reloc: Arc<dyn DeviceRelocation>) -> Self {
1228d785bbdSSebastien Boeuf         let mut devices: HashMap<u32, Arc<Mutex<dyn PciDevice>>> = HashMap::new();
123df71aaeeSSebastien Boeuf         let mut device_ids: Vec<bool> = vec![false; NUM_DEVICE_IDS];
124833a3d45SRob Bradford 
1258d785bbdSSebastien Boeuf         devices.insert(0, Arc::new(Mutex::new(pci_root)));
126df71aaeeSSebastien Boeuf         device_ids[0] = true;
1272bb0b22cSJing Liu 
12804a449d3SSebastien Boeuf         PciBus {
12904a449d3SSebastien Boeuf             devices,
130149b61b2SSebastien Boeuf             device_reloc,
131df71aaeeSSebastien Boeuf             device_ids,
13204a449d3SSebastien Boeuf         }
133e8308dd1SSamuel Ortiz     }
134e8308dd1SSamuel Ortiz 
register_mapping( &self, dev: Arc<dyn BusDeviceSync>, io_bus: &Bus, mmio_bus: &Bus, bars: Vec<PciBarConfiguration>, ) -> Result<()>1352bb0b22cSJing Liu     pub fn register_mapping(
1362bb0b22cSJing Liu         &self,
137954f3dd0SYuanchu Xie         dev: Arc<dyn BusDeviceSync>,
13850bac169SAlyssa Ross         io_bus: &Bus,
13915025d71SRob Bradford         mmio_bus: &Bus,
14089218b6dSSebastien Boeuf         bars: Vec<PciBarConfiguration>,
1412bb0b22cSJing Liu     ) -> Result<()> {
14289218b6dSSebastien Boeuf         for bar in bars {
14389218b6dSSebastien Boeuf             match bar.region_type() {
144827229d8SRob Bradford                 PciBarRegionType::IoRegion => {
14512681650SSebastien Boeuf                     io_bus
14689218b6dSSebastien Boeuf                         .insert(dev.clone(), bar.addr(), bar.size())
14712681650SSebastien Boeuf                         .map_err(PciRootError::PioInsert)?;
14812681650SSebastien Boeuf                 }
14912681650SSebastien Boeuf                 PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => {
15012681650SSebastien Boeuf                     mmio_bus
15189218b6dSSebastien Boeuf                         .insert(dev.clone(), bar.addr(), bar.size())
1522bb0b22cSJing Liu                         .map_err(PciRootError::MmioInsert)?;
1532bb0b22cSJing Liu                 }
15412681650SSebastien Boeuf             }
15512681650SSebastien Boeuf         }
1562bb0b22cSJing Liu         Ok(())
1572bb0b22cSJing Liu     }
1582bb0b22cSJing Liu 
add_device(&mut self, device_id: u32, device: Arc<Mutex<dyn PciDevice>>) -> Result<()>159ae83e3b3SRob Bradford     pub fn add_device(&mut self, device_id: u32, device: Arc<Mutex<dyn PciDevice>>) -> Result<()> {
160ae83e3b3SRob Bradford         self.devices.insert(device_id, device);
1612bb0b22cSJing Liu         Ok(())
1622bb0b22cSJing Liu     }
163b918220bSSebastien Boeuf 
remove_by_device(&mut self, device: &Arc<Mutex<dyn PciDevice>>) -> Result<()>164f8e2008eSSebastien Boeuf     pub fn remove_by_device(&mut self, device: &Arc<Mutex<dyn PciDevice>>) -> Result<()> {
1658d785bbdSSebastien Boeuf         self.devices.retain(|_, dev| !Arc::ptr_eq(dev, device));
166f8e2008eSSebastien Boeuf         Ok(())
167f8e2008eSSebastien Boeuf     }
168f8e2008eSSebastien Boeuf 
next_device_id(&mut self) -> Result<u32>169df71aaeeSSebastien Boeuf     pub fn next_device_id(&mut self) -> Result<u32> {
170df71aaeeSSebastien Boeuf         for (idx, device_id) in self.device_ids.iter_mut().enumerate() {
171df71aaeeSSebastien Boeuf             if !(*device_id) {
172df71aaeeSSebastien Boeuf                 *device_id = true;
173df71aaeeSSebastien Boeuf                 return Ok(idx as u32);
174df71aaeeSSebastien Boeuf             }
175df71aaeeSSebastien Boeuf         }
176df71aaeeSSebastien Boeuf 
177df71aaeeSSebastien Boeuf         Err(PciRootError::NoPciDeviceSlotAvailable)
178b918220bSSebastien Boeuf     }
179b50cbe50SSebastien Boeuf 
get_device_id(&mut self, id: usize) -> Result<()>1801e0ebb76SSebastien Boeuf     pub fn get_device_id(&mut self, id: usize) -> Result<()> {
1811e0ebb76SSebastien Boeuf         if id < NUM_DEVICE_IDS {
1821e0ebb76SSebastien Boeuf             if !self.device_ids[id] {
1831e0ebb76SSebastien Boeuf                 self.device_ids[id] = true;
1841e0ebb76SSebastien Boeuf                 Ok(())
1851e0ebb76SSebastien Boeuf             } else {
1861e0ebb76SSebastien Boeuf                 Err(PciRootError::AlreadyInUsePciDeviceSlot(id))
1871e0ebb76SSebastien Boeuf             }
1881e0ebb76SSebastien Boeuf         } else {
1891e0ebb76SSebastien Boeuf             Err(PciRootError::InvalidPciDeviceSlot(id))
1901e0ebb76SSebastien Boeuf         }
1911e0ebb76SSebastien Boeuf     }
1921e0ebb76SSebastien Boeuf 
put_device_id(&mut self, id: usize) -> Result<()>193b50cbe50SSebastien Boeuf     pub fn put_device_id(&mut self, id: usize) -> Result<()> {
194b50cbe50SSebastien Boeuf         if id < NUM_DEVICE_IDS {
195b50cbe50SSebastien Boeuf             self.device_ids[id] = false;
196b50cbe50SSebastien Boeuf             Ok(())
197b50cbe50SSebastien Boeuf         } else {
198b50cbe50SSebastien Boeuf             Err(PciRootError::InvalidPciDeviceSlot(id))
199b50cbe50SSebastien Boeuf         }
200b50cbe50SSebastien Boeuf     }
201833a3d45SRob Bradford }
202833a3d45SRob Bradford 
203833a3d45SRob Bradford pub struct PciConfigIo {
204833a3d45SRob Bradford     /// Config space register.
205833a3d45SRob Bradford     config_address: u32,
2060eb78ab1SRob Bradford     pci_bus: Arc<Mutex<PciBus>>,
207833a3d45SRob Bradford }
208833a3d45SRob Bradford 
209833a3d45SRob Bradford impl PciConfigIo {
new(pci_bus: Arc<Mutex<PciBus>>) -> Self2100eb78ab1SRob Bradford     pub fn new(pci_bus: Arc<Mutex<PciBus>>) -> Self {
211833a3d45SRob Bradford         PciConfigIo {
212833a3d45SRob Bradford             config_address: 0,
2130eb78ab1SRob Bradford             pci_bus,
214833a3d45SRob Bradford         }
215833a3d45SRob Bradford     }
2162bb0b22cSJing Liu 
config_space_read(&self) -> u322172bb0b22cSJing Liu     pub fn config_space_read(&self) -> u32 {
218e8308dd1SSamuel Ortiz         let enabled = (self.config_address & 0x8000_0000) != 0;
219e8308dd1SSamuel Ortiz         if !enabled {
220e8308dd1SSamuel Ortiz             return 0xffff_ffff;
221e8308dd1SSamuel Ortiz         }
222e8308dd1SSamuel Ortiz 
223b6ae2ccdSSebastien Boeuf         let (bus, device, function, register) =
224f6b9445bSQiu Wenbo             parse_io_config_address(self.config_address & !0x8000_0000);
2252bb0b22cSJing Liu 
2262bb0b22cSJing Liu         // Only support one bus.
2272bb0b22cSJing Liu         if bus != 0 {
2282bb0b22cSJing Liu             return 0xffff_ffff;
229e8308dd1SSamuel Ortiz         }
230e8308dd1SSamuel Ortiz 
231b6ae2ccdSSebastien Boeuf         // Don't support multi-function devices.
232b6ae2ccdSSebastien Boeuf         if function > 0 {
233b6ae2ccdSSebastien Boeuf             return 0xffff_ffff;
234b6ae2ccdSSebastien Boeuf         }
235b6ae2ccdSSebastien Boeuf 
236833a3d45SRob Bradford         self.pci_bus
2370faa7afaSRob Bradford             .as_ref()
238833a3d45SRob Bradford             .lock()
239833a3d45SRob Bradford             .unwrap()
240833a3d45SRob Bradford             .devices
2418d785bbdSSebastien Boeuf             .get(&(device as u32))
242833a3d45SRob Bradford             .map_or(0xffff_ffff, |d| {
2432bb0b22cSJing Liu                 d.lock().unwrap().read_config_register(register)
2442bb0b22cSJing Liu             })
2452bb0b22cSJing Liu     }
2462bb0b22cSJing Liu 
config_space_write(&mut self, offset: u64, data: &[u8]) -> Option<Arc<Barrier>>2477cc729c7SRob Bradford     pub fn config_space_write(&mut self, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
2482bb0b22cSJing Liu         if offset as usize + data.len() > 4 {
2497cc729c7SRob Bradford             return None;
2502bb0b22cSJing Liu         }
2512bb0b22cSJing Liu 
252e8308dd1SSamuel Ortiz         let enabled = (self.config_address & 0x8000_0000) != 0;
253e8308dd1SSamuel Ortiz         if !enabled {
2547cc729c7SRob Bradford             return None;
255e8308dd1SSamuel Ortiz         }
256e8308dd1SSamuel Ortiz 
2572bb0b22cSJing Liu         let (bus, device, _function, register) =
258f6b9445bSQiu Wenbo             parse_io_config_address(self.config_address & !0x8000_0000);
2592bb0b22cSJing Liu 
2602bb0b22cSJing Liu         // Only support one bus.
2612bb0b22cSJing Liu         if bus != 0 {
2627cc729c7SRob Bradford             return None;
2632bb0b22cSJing Liu         }
2642bb0b22cSJing Liu 
2650eb78ab1SRob Bradford         let pci_bus = self.pci_bus.as_ref().lock().unwrap();
2668d785bbdSSebastien Boeuf         if let Some(d) = pci_bus.devices.get(&(device as u32)) {
267149b61b2SSebastien Boeuf             let mut device = d.lock().unwrap();
268149b61b2SSebastien Boeuf 
269cb52cf91SBo Chen             // Update the register value
270cb52cf91SBo Chen             let (bar_reprogram, ret) = device.write_config_register(register, offset, data);
271cb52cf91SBo Chen 
272cb52cf91SBo Chen             // Move the device's BAR if needed
273aaf86ef2SBo Chen             for params in &bar_reprogram {
27449268bffSSebastien Boeuf                 if let Err(e) = pci_bus.device_reloc.move_bar(
275149b61b2SSebastien Boeuf                     params.old_base,
276149b61b2SSebastien Boeuf                     params.new_base,
277149b61b2SSebastien Boeuf                     params.len,
278149b61b2SSebastien Boeuf                     device.deref_mut(),
279149b61b2SSebastien Boeuf                     params.region_type,
280d6c68e47SSebastien Boeuf                 ) {
28156207a03SRob Bradford                     error!(
28256207a03SRob Bradford                         "Failed moving device BAR: {}: 0x{:x}->0x{:x}(0x{:x})",
28356207a03SRob Bradford                         e, params.old_base, params.new_base, params.len
28456207a03SRob Bradford                     );
285d6c68e47SSebastien Boeuf                 }
286149b61b2SSebastien Boeuf             }
287c7cabc88SSebastien Boeuf 
288cb52cf91SBo Chen             ret
2897cc729c7SRob Bradford         } else {
2907cc729c7SRob Bradford             None
2912bb0b22cSJing Liu         }
292e8308dd1SSamuel Ortiz     }
293e8308dd1SSamuel Ortiz 
set_config_address(&mut self, offset: u64, data: &[u8])294e8308dd1SSamuel Ortiz     fn set_config_address(&mut self, offset: u64, data: &[u8]) {
295e8308dd1SSamuel Ortiz         if offset as usize + data.len() > 4 {
296e8308dd1SSamuel Ortiz             return;
297e8308dd1SSamuel Ortiz         }
298e8308dd1SSamuel Ortiz         let (mask, value): (u32, u32) = match data.len() {
299e8308dd1SSamuel Ortiz             1 => (
300e8308dd1SSamuel Ortiz                 0x0000_00ff << (offset * 8),
301e8308dd1SSamuel Ortiz                 u32::from(data[0]) << (offset * 8),
302e8308dd1SSamuel Ortiz             ),
303e8308dd1SSamuel Ortiz             2 => (
304e8308dd1SSamuel Ortiz                 0x0000_ffff << (offset * 16),
305b57cc3d7SRob Bradford                 ((u32::from(data[1]) << 8) | u32::from(data[0])) << (offset * 16),
306e8308dd1SSamuel Ortiz             ),
307e8308dd1SSamuel Ortiz             4 => (0xffff_ffff, LittleEndian::read_u32(data)),
308e8308dd1SSamuel Ortiz             _ => return,
309e8308dd1SSamuel Ortiz         };
310e8308dd1SSamuel Ortiz         self.config_address = (self.config_address & !mask) | value;
311e8308dd1SSamuel Ortiz     }
312e8308dd1SSamuel Ortiz }
313e8308dd1SSamuel Ortiz 
314e8308dd1SSamuel Ortiz impl BusDevice for PciConfigIo {
read(&mut self, _base: u64, offset: u64, data: &mut [u8])3158173e1ccSSamuel Ortiz     fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
316e8308dd1SSamuel Ortiz         // `offset` is relative to 0xcf8
317e8308dd1SSamuel Ortiz         let value = match offset {
318658c076eSSebastien Boeuf             0..=3 => self.config_address,
319658c076eSSebastien Boeuf             4..=7 => self.config_space_read(),
320e8308dd1SSamuel Ortiz             _ => 0xffff_ffff,
321e8308dd1SSamuel Ortiz         };
322e8308dd1SSamuel Ortiz 
323e8308dd1SSamuel Ortiz         // Only allow reads to the register boundary.
324e8308dd1SSamuel Ortiz         let start = offset as usize % 4;
325e8308dd1SSamuel Ortiz         let end = start + data.len();
326e8308dd1SSamuel Ortiz         if end <= 4 {
327e8308dd1SSamuel Ortiz             for i in start..end {
328e8308dd1SSamuel Ortiz                 data[i - start] = (value >> (i * 8)) as u8;
329e8308dd1SSamuel Ortiz             }
330e8308dd1SSamuel Ortiz         } else {
331e8308dd1SSamuel Ortiz             for d in data {
332e8308dd1SSamuel Ortiz                 *d = 0xff;
333e8308dd1SSamuel Ortiz             }
334e8308dd1SSamuel Ortiz         }
335e8308dd1SSamuel Ortiz     }
336e8308dd1SSamuel Ortiz 
write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>>3371fc6d50fSRob Bradford     fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
338e8308dd1SSamuel Ortiz         // `offset` is relative to 0xcf8
339e8308dd1SSamuel Ortiz         match offset {
3407cc729c7SRob Bradford             o @ 0..=3 => {
3417cc729c7SRob Bradford                 self.set_config_address(o, data);
3421fc6d50fSRob Bradford                 None
343e8308dd1SSamuel Ortiz             }
3447cc729c7SRob Bradford             o @ 4..=7 => self.config_space_write(o - 4, data),
3457cc729c7SRob Bradford             _ => None,
3467cc729c7SRob Bradford         }
3477cc729c7SRob Bradford     }
348e8308dd1SSamuel Ortiz }
349e8308dd1SSamuel Ortiz 
350e8308dd1SSamuel Ortiz /// Emulates PCI memory-mapped configuration access mechanism.
351e8308dd1SSamuel Ortiz pub struct PciConfigMmio {
352833a3d45SRob Bradford     pci_bus: Arc<Mutex<PciBus>>,
353e8308dd1SSamuel Ortiz }
354e8308dd1SSamuel Ortiz 
355e8308dd1SSamuel Ortiz impl PciConfigMmio {
new(pci_bus: Arc<Mutex<PciBus>>) -> Self356833a3d45SRob Bradford     pub fn new(pci_bus: Arc<Mutex<PciBus>>) -> Self {
357833a3d45SRob Bradford         PciConfigMmio { pci_bus }
358e8308dd1SSamuel Ortiz     }
359e8308dd1SSamuel Ortiz 
config_space_read(&self, config_address: u32) -> u32360e8308dd1SSamuel Ortiz     fn config_space_read(&self, config_address: u32) -> u32 {
361f6b9445bSQiu Wenbo         let (bus, device, _function, register) = parse_mmio_config_address(config_address);
3622bb0b22cSJing Liu 
3632bb0b22cSJing Liu         // Only support one bus.
3642bb0b22cSJing Liu         if bus != 0 {
3652bb0b22cSJing Liu             return 0xffff_ffff;
3662bb0b22cSJing Liu         }
3672bb0b22cSJing Liu 
368833a3d45SRob Bradford         self.pci_bus
369833a3d45SRob Bradford             .lock()
370833a3d45SRob Bradford             .unwrap()
371833a3d45SRob Bradford             .devices
3728d785bbdSSebastien Boeuf             .get(&(device as u32))
373833a3d45SRob Bradford             .map_or(0xffff_ffff, |d| {
3742bb0b22cSJing Liu                 d.lock().unwrap().read_config_register(register)
3752bb0b22cSJing Liu             })
376e8308dd1SSamuel Ortiz     }
377e8308dd1SSamuel Ortiz 
config_space_write(&mut self, config_address: u32, offset: u64, data: &[u8])378e8308dd1SSamuel Ortiz     fn config_space_write(&mut self, config_address: u32, offset: u64, data: &[u8]) {
3792bb0b22cSJing Liu         if offset as usize + data.len() > 4 {
3802bb0b22cSJing Liu             return;
3812bb0b22cSJing Liu         }
3822bb0b22cSJing Liu 
383f6b9445bSQiu Wenbo         let (bus, device, _function, register) = parse_mmio_config_address(config_address);
3842bb0b22cSJing Liu 
3852bb0b22cSJing Liu         // Only support one bus.
3862bb0b22cSJing Liu         if bus != 0 {
3872bb0b22cSJing Liu             return;
3882bb0b22cSJing Liu         }
3892bb0b22cSJing Liu 
390149b61b2SSebastien Boeuf         let pci_bus = self.pci_bus.lock().unwrap();
3918d785bbdSSebastien Boeuf         if let Some(d) = pci_bus.devices.get(&(device as u32)) {
392149b61b2SSebastien Boeuf             let mut device = d.lock().unwrap();
393149b61b2SSebastien Boeuf 
394cb52cf91SBo Chen             // Update the register value
395cb52cf91SBo Chen             let (bar_reprogram, _) = device.write_config_register(register, offset, data);
396cb52cf91SBo Chen 
397cb52cf91SBo Chen             // Move the device's BAR if needed
398aaf86ef2SBo Chen             for params in &bar_reprogram {
39949268bffSSebastien Boeuf                 if let Err(e) = pci_bus.device_reloc.move_bar(
400149b61b2SSebastien Boeuf                     params.old_base,
401149b61b2SSebastien Boeuf                     params.new_base,
402149b61b2SSebastien Boeuf                     params.len,
403149b61b2SSebastien Boeuf                     device.deref_mut(),
404149b61b2SSebastien Boeuf                     params.region_type,
405d6c68e47SSebastien Boeuf                 ) {
40656207a03SRob Bradford                     error!(
40756207a03SRob Bradford                         "Failed moving device BAR: {}: 0x{:x}->0x{:x}(0x{:x})",
40856207a03SRob Bradford                         e, params.old_base, params.new_base, params.len
40956207a03SRob Bradford                     );
410d6c68e47SSebastien Boeuf                 }
411149b61b2SSebastien Boeuf             }
4122bb0b22cSJing Liu         }
413e8308dd1SSamuel Ortiz     }
414e8308dd1SSamuel Ortiz }
415e8308dd1SSamuel Ortiz 
416e8308dd1SSamuel Ortiz impl BusDevice for PciConfigMmio {
read(&mut self, _base: u64, offset: u64, data: &mut [u8])4178173e1ccSSamuel Ortiz     fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
418e8308dd1SSamuel Ortiz         // Only allow reads to the register boundary.
419e8308dd1SSamuel Ortiz         let start = offset as usize % 4;
420e8308dd1SSamuel Ortiz         let end = start + data.len();
421f6cd3bd8SWei Liu         if end > 4 || offset > u64::from(u32::MAX) {
422e8308dd1SSamuel Ortiz             for d in data {
423e8308dd1SSamuel Ortiz                 *d = 0xff;
424e8308dd1SSamuel Ortiz             }
425e8308dd1SSamuel Ortiz             return;
426e8308dd1SSamuel Ortiz         }
427e8308dd1SSamuel Ortiz 
428e8308dd1SSamuel Ortiz         let value = self.config_space_read(offset as u32);
429e8308dd1SSamuel Ortiz         for i in start..end {
430e8308dd1SSamuel Ortiz             data[i - start] = (value >> (i * 8)) as u8;
431e8308dd1SSamuel Ortiz         }
432e8308dd1SSamuel Ortiz     }
433e8308dd1SSamuel Ortiz 
write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>>4341fc6d50fSRob Bradford     fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
435f6cd3bd8SWei Liu         if offset > u64::from(u32::MAX) {
4361fc6d50fSRob Bradford             return None;
437e8308dd1SSamuel Ortiz         }
4381fc6d50fSRob Bradford         self.config_space_write(offset as u32, offset % 4, data);
4391fc6d50fSRob Bradford 
4401fc6d50fSRob Bradford         None
441e8308dd1SSamuel Ortiz     }
442e8308dd1SSamuel Ortiz }
443e8308dd1SSamuel Ortiz 
shift_and_mask(value: u32, offset: usize, mask: u32) -> usize444f6b9445bSQiu Wenbo fn shift_and_mask(value: u32, offset: usize, mask: u32) -> usize {
445f6b9445bSQiu Wenbo     ((value >> offset) & mask) as usize
446f6b9445bSQiu Wenbo }
447f6b9445bSQiu Wenbo 
448f6b9445bSQiu Wenbo // Parse the MMIO address offset to a (bus, device, function, register) tuple.
449f6b9445bSQiu Wenbo // See section 7.2.2 PCI Express Enhanced Configuration Access Mechanism (ECAM)
450f6b9445bSQiu Wenbo // from the Pci Express Base Specification Revision 5.0 Version 1.0.
parse_mmio_config_address(config_address: u32) -> (usize, usize, usize, usize)451f6b9445bSQiu Wenbo fn parse_mmio_config_address(config_address: u32) -> (usize, usize, usize, usize) {
452f6b9445bSQiu Wenbo     const BUS_NUMBER_OFFSET: usize = 20;
453f6b9445bSQiu Wenbo     const BUS_NUMBER_MASK: u32 = 0x00ff;
454f6b9445bSQiu Wenbo     const DEVICE_NUMBER_OFFSET: usize = 15;
455f6b9445bSQiu Wenbo     const DEVICE_NUMBER_MASK: u32 = 0x1f;
456f6b9445bSQiu Wenbo     const FUNCTION_NUMBER_OFFSET: usize = 12;
457f6b9445bSQiu Wenbo     const FUNCTION_NUMBER_MASK: u32 = 0x07;
458f6b9445bSQiu Wenbo     const REGISTER_NUMBER_OFFSET: usize = 2;
459f6b9445bSQiu Wenbo     const REGISTER_NUMBER_MASK: u32 = 0x3ff;
460f6b9445bSQiu Wenbo 
461f6b9445bSQiu Wenbo     (
462f6b9445bSQiu Wenbo         shift_and_mask(config_address, BUS_NUMBER_OFFSET, BUS_NUMBER_MASK),
463f6b9445bSQiu Wenbo         shift_and_mask(config_address, DEVICE_NUMBER_OFFSET, DEVICE_NUMBER_MASK),
464f6b9445bSQiu Wenbo         shift_and_mask(config_address, FUNCTION_NUMBER_OFFSET, FUNCTION_NUMBER_MASK),
465f6b9445bSQiu Wenbo         shift_and_mask(config_address, REGISTER_NUMBER_OFFSET, REGISTER_NUMBER_MASK),
466f6b9445bSQiu Wenbo     )
467f6b9445bSQiu Wenbo }
468f6b9445bSQiu Wenbo 
469e8308dd1SSamuel Ortiz // Parse the CONFIG_ADDRESS register to a (bus, device, function, register) tuple.
parse_io_config_address(config_address: u32) -> (usize, usize, usize, usize)470f6b9445bSQiu Wenbo fn parse_io_config_address(config_address: u32) -> (usize, usize, usize, usize) {
471e8308dd1SSamuel Ortiz     const BUS_NUMBER_OFFSET: usize = 16;
472e8308dd1SSamuel Ortiz     const BUS_NUMBER_MASK: u32 = 0x00ff;
473e8308dd1SSamuel Ortiz     const DEVICE_NUMBER_OFFSET: usize = 11;
474e8308dd1SSamuel Ortiz     const DEVICE_NUMBER_MASK: u32 = 0x1f;
475e8308dd1SSamuel Ortiz     const FUNCTION_NUMBER_OFFSET: usize = 8;
476e8308dd1SSamuel Ortiz     const FUNCTION_NUMBER_MASK: u32 = 0x07;
477e8308dd1SSamuel Ortiz     const REGISTER_NUMBER_OFFSET: usize = 2;
478e8308dd1SSamuel Ortiz     const REGISTER_NUMBER_MASK: u32 = 0x3f;
479e8308dd1SSamuel Ortiz 
480f6b9445bSQiu Wenbo     (
481f6b9445bSQiu Wenbo         shift_and_mask(config_address, BUS_NUMBER_OFFSET, BUS_NUMBER_MASK),
482f6b9445bSQiu Wenbo         shift_and_mask(config_address, DEVICE_NUMBER_OFFSET, DEVICE_NUMBER_MASK),
483f6b9445bSQiu Wenbo         shift_and_mask(config_address, FUNCTION_NUMBER_OFFSET, FUNCTION_NUMBER_MASK),
484f6b9445bSQiu Wenbo         shift_and_mask(config_address, REGISTER_NUMBER_OFFSET, REGISTER_NUMBER_MASK),
485f6b9445bSQiu Wenbo     )
486e8308dd1SSamuel Ortiz }
487