// Copyright 2018 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::sync::{Arc, Mutex}; use byteorder::{ByteOrder, LittleEndian}; use BusDevice; use ProxyDevice; use pci::pci_configuration::{PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType}; use pci::pci_device::PciDevice; // A PciDevice that holds the root hub's configuration. struct PciRootConfiguration { config: PciConfiguration, } impl PciDevice for PciRootConfiguration { fn config_registers(&self) -> &PciConfiguration { &self.config } fn config_registers_mut(&mut self) -> &mut PciConfiguration { &mut self.config } fn read_bar(&mut self, _addr: u64, _data: &mut [u8]) {} fn write_bar(&mut self, _addr: u64, _data: &[u8]) {} } /// Emulates the PCI Root bridge. pub struct PciRoot { /// Bus configuration for the root device. root_configuration: PciRootConfiguration, /// Current address to read/write from (0xcf8 register, litte endian). config_address: u32, /// Devices attached to this bridge. devices: Vec>>, } impl PciRoot { /// Create an empty PCI root bus. pub fn new() -> Self { PciRoot { root_configuration: PciRootConfiguration { config: PciConfiguration::new( 0, 0, PciClassCode::BridgeDevice, &PciBridgeSubclass::HostBridge, PciHeaderType::Bridge, 0, 0, ), }, config_address: 0, devices: Vec::new(), } } /// Add a `device` to this root PCI bus. pub fn add_device(&mut self, device: Arc>) { self.devices.push(device); } fn config_space_read(&self) -> u32 { let (enabled, bus, device, _, register) = parse_config_address(self.config_address); // Only support one bus. if !enabled || bus != 0 { return 0xffff_ffff; } 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().unwrap().config_register_read(register) }), } } fn config_space_write(&mut self, offset: u64, data: &[u8]) { if offset as usize + data.len() > 4 { return; } let (enabled, bus, device, _, register) = parse_config_address(self.config_address); // Only support one bus. if !enabled || 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().unwrap().config_register_write(register, offset, data); } } } } fn set_config_address(&mut self, offset: u64, data: &[u8]) { if offset as usize + data.len() > 4 { return; } let (mask, value): (u32, u32) = match data.len() { 1 => ( 0x0000_00ff << (offset * 8), (data[0] as u32) << (offset * 8), ), 2 => ( 0x0000_ffff << (offset * 16), ((data[1] as u32) << 8 | data[0] as u32) << (offset * 16), ), 4 => (0xffff_ffff, LittleEndian::read_u32(data)), _ => return, }; self.config_address = (self.config_address & !mask) | value; } } impl BusDevice for PciRoot { fn read(&mut self, offset: u64, data: &mut [u8]) { // `offset` is relative to 0xcf8 let value = match offset { 0...3 => self.config_address, 4...7 => self.config_space_read(), _ => 0xffff_ffff, }; // Only allow reads to the register boundary. let start = offset as usize % 4; let end = start + data.len(); if end <= 4 { for i in start..end { data[i - start] = (value >> (i * 8)) as u8; } } else { for d in data { *d = 0xff; } } } fn write(&mut self, offset: u64, data: &[u8]) { // `offset` is relative to 0xcf8 match offset { o @ 0...3 => self.set_config_address(o, data), o @ 4...7 => self.config_space_write(o - 4, data), _ => (), }; } } // Parse the CONFIG_ADDRESS register to a (enabled, bus, device, function, register) tuple. fn parse_config_address(config_address: u32) -> (bool, 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 enabled = (config_address & 0x8000_0000) != 0; 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; ( enabled, bus_number, device_number, function_number, register_number, ) }