use std::collections::btree_map::Entry as BTreeMapEntry;
use std::collections::BTreeMap;
use std::convert::TryInto;
use std::ops::Bound::Included;
use std::ops::DerefMut;
use std::sync::Arc;
use std::sync::Weak;
use anyhow::Context;
use base::error;
use base::MemoryMapping;
use base::MemoryMappingBuilder;
use base::Protection;
use base::RawDescriptor;
use base::SendTube;
use base::SharedMemory;
use base::VmEventType;
use hypervisor::MemCacheType;
use hypervisor::Vm;
use resources::SystemAllocator;
use serde::Deserialize;
use serde::Serialize;
use snapshot::AnySnapshot;
use sync::Mutex;
use vm_memory::GuestAddress;
use crate::pci::pci_configuration::PciBarConfiguration;
use crate::pci::pci_configuration::PciBridgeSubclass;
use crate::pci::pci_configuration::PciClassCode;
use crate::pci::pci_configuration::PciConfiguration;
use crate::pci::pci_configuration::PciHeaderType;
use crate::pci::pci_configuration::HEADER_TYPE_MULTIFUNCTION_MASK;
use crate::pci::pci_configuration::HEADER_TYPE_REG;
use crate::pci::pci_configuration::HEADER_TYPE_REG_OFFSET;
use crate::pci::pci_device::Error;
use crate::pci::pci_device::PciBus;
use crate::pci::pci_device::PciDevice;
use crate::pci::PciAddress;
use crate::pci::PciBarIndex;
use crate::pci::PciId;
use crate::pci::PCI_VENDOR_ID_INTEL;
use crate::Bus;
use crate::BusAccessInfo;
use crate::BusDevice;
use crate::BusType;
use crate::DeviceId;
use crate::Suspendable;
struct PciRootConfiguration {
    config: PciConfiguration,
}
impl PciDevice for PciRootConfiguration {
    fn debug_label(&self) -> String {
        "pci root device".to_owned()
    }
    fn allocate_address(&mut self, _resources: &mut SystemAllocator) -> Result<PciAddress, Error> {
        Ok(PciAddress {
            bus: 0,
            dev: 0,
            func: 0,
        })
    }
    fn keep_rds(&self) -> Vec<RawDescriptor> {
        Vec::new()
    }
    fn read_config_register(&self, reg_idx: usize) -> u32 {
        self.config.read_reg(reg_idx)
    }
    fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
        self.config.write_reg(reg_idx, offset, data);
    }
    fn setup_pci_config_mapping(
        &mut self,
        shmem: &SharedMemory,
        base: usize,
        len: usize,
    ) -> Result<bool, Error> {
        self.config
            .setup_mapping(shmem, base, len)
            .map(|_| true)
            .map_err(Error::MmioSetup)
    }
    fn read_bar(&mut self, _bar_index: PciBarIndex, _offset: u64, _data: &mut [u8]) {}
    fn write_bar(&mut self, _bar_index: PciBarIndex, _offset: u64, _data: &[u8]) {}
    fn get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration> {
        self.config.get_bar_configuration(bar_num)
    }
}
impl Suspendable for PciRootConfiguration {
    fn sleep(&mut self) -> anyhow::Result<()> {
        Ok(())
    }
    fn wake(&mut self) -> anyhow::Result<()> {
        Ok(())
    }
    fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
        self.config
            .snapshot()
            .with_context(|| format!("failed to serialize {}", PciDevice::debug_label(self)))
    }
    fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
        self.config
            .restore(data)
            .context("failed to deserialize PciRootConfiguration")
    }
}
pub enum PciRootCommand {
    Add(PciAddress, Arc<Mutex<dyn BusDevice>>),
    AddBridge(Arc<Mutex<PciBus>>),
    Remove(PciAddress),
    Kill,
}
#[derive(Serialize)]
struct PciRootMmioState {
    #[serde(skip_serializing)]
    mappings: BTreeMap<u32, Option<(SharedMemory, MemoryMapping)>>,
    base: GuestAddress,
    register_bit_num: usize,
}
#[allow(dead_code)] pub struct PciRoot {
    mmio_bus: Weak<Bus>,
    io_bus: Weak<Bus>,
    root_bus: Arc<Mutex<PciBus>>,
    root_configuration: PciRootConfiguration,
    devices: BTreeMap<PciAddress, Arc<Mutex<dyn BusDevice>>>,
    pcie_cfg_mmio: Option<u64>,
    pci_mmio_state: PciRootMmioState,
}
const PCI_DEVICE_ID_INTEL_82441: u16 = 0x1237;
const PCIE_XBAR_BASE_ADDR: usize = 24;
#[derive(Serialize, Deserialize)]
struct PciRootSerializable {
    root_configuration: AnySnapshot,
    pcie_cfg_mmio: Option<u64>,
}
impl PciRoot {
    pub fn new(
        vm: &mut impl Vm,
        mmio_bus: Weak<Bus>,
        mmio_base: GuestAddress,
        mmio_register_bit_num: usize,
        io_bus: Weak<Bus>,
        root_bus: Arc<Mutex<PciBus>>,
    ) -> anyhow::Result<Self> {
        assert!(base::pagesize() >= (1 << mmio_register_bit_num));
        let mut root =
            Self::create_for_test(mmio_bus, mmio_base, mmio_register_bit_num, io_bus, root_bus);
        root.pci_mmio_state
            .setup_mapping(
                &PciAddress::new(0, 0, 0, 0).unwrap(),
                &mut root.root_configuration,
                vm,
            )
            .context("failed to set up root configuration mapping")?;
        Ok(root)
    }
    fn create_for_test(
        mmio_bus: Weak<Bus>,
        mmio_base: GuestAddress,
        mmio_register_bit_num: usize,
        io_bus: Weak<Bus>,
        root_bus: Arc<Mutex<PciBus>>,
    ) -> Self {
        PciRoot {
            mmio_bus,
            io_bus,
            root_bus,
            root_configuration: PciRootConfiguration {
                config: PciConfiguration::new(
                    PCI_VENDOR_ID_INTEL,
                    PCI_DEVICE_ID_INTEL_82441,
                    PciClassCode::BridgeDevice,
                    &PciBridgeSubclass::HostBridge,
                    None,
                    PciHeaderType::Device,
                    0,
                    0,
                    0,
                ),
            },
            devices: BTreeMap::new(),
            pcie_cfg_mmio: None,
            pci_mmio_state: PciRootMmioState {
                mappings: BTreeMap::new(),
                base: mmio_base,
                register_bit_num: mmio_register_bit_num,
            },
        }
    }
    pub fn get_root_bus(&self) -> Arc<Mutex<PciBus>> {
        self.root_bus.clone()
    }
    pub fn acpi_path(&self, address: &PciAddress) -> Option<String> {
        if let Some(device) = self.devices.get(address) {
            let path = self.root_bus.lock().path_to(address.bus);
            if path.is_empty() {
                None
            } else {
                Some(format!(
                    "_SB_.{}.{}",
                    path.iter()
                        .map(|x| format!("PC{:02X}", x))
                        .collect::<Vec<String>>()
                        .join("."),
                    match device.lock().is_bridge() {
                        Some(bus_no) => format!("PC{:02X}", bus_no),
                        None => format!("PE{:02X}", address.devfn()),
                    }
                ))
            }
        } else {
            None
        }
    }
    pub fn enable_pcie_cfg_mmio(&mut self, pcie_cfg_mmio: u64) {
        self.pcie_cfg_mmio = Some(pcie_cfg_mmio);
        self.root_configuration.config.set_reg(
            PCIE_XBAR_BASE_ADDR,
            self.pcie_cfg_mmio.unwrap() as u32 | 0x1,
            0xffff_ffff,
        );
        self.root_configuration.config.set_reg(
            PCIE_XBAR_BASE_ADDR + 1,
            (self.pcie_cfg_mmio.unwrap() >> 32) as u32,
            0xffff_ffff,
        );
    }
    pub fn add_device<T>(
        &mut self,
        address: PciAddress,
        device: Arc<Mutex<dyn BusDevice>>,
        mapper: &mut T,
    ) -> Result<(), Error>
    where
        T: PciMmioMapper,
    {
        if !address.is_root() {
            self.pci_mmio_state
                .setup_mapping(&address, device.lock().deref_mut(), mapper)
                .map_err(Error::MmioSetup)?;
            self.devices.insert(address, device);
            self.sync_multifunction_bit_to_mmio_mappings(&address, true);
        }
        self.root_bus.lock().add_child_device(address)
    }
    fn sync_multifunction_bit_to_mmio_mappings(&mut self, address: &PciAddress, on_add: bool) {
        let num_mfd = self.num_multifunction_device(address);
        let target_range = if (num_mfd == 1 && on_add) || (num_mfd == 0 && !on_add) {
            0..8
        } else if on_add && num_mfd > 0 {
            address.func..(address.func + 1)
        } else {
            return;
        };
        for i in target_range {
            self.pci_mmio_state.set_mfd_bit(
                &PciAddress {
                    func: i,
                    ..*address
                },
                num_mfd > 0,
            );
        }
    }
    pub fn add_bridge(&mut self, bridge_bus: Arc<Mutex<PciBus>>) -> Result<(), Error> {
        self.root_bus.lock().add_child_bus(bridge_bus)
    }
    pub fn remove_device(&mut self, address: PciAddress) {
        if let Some(d) = self.devices.remove(&address) {
            for (range, bus_type) in d.lock().get_ranges() {
                let bus_ptr = if bus_type == BusType::Mmio {
                    match self.mmio_bus.upgrade() {
                        Some(m) => m,
                        None => continue,
                    }
                } else {
                    match self.io_bus.upgrade() {
                        Some(i) => i,
                        None => continue,
                    }
                };
                let _ = bus_ptr.remove(range.base, range.len);
            }
            if let Some(bus_no) = d.lock().is_bridge() {
                let _ = self.root_bus.lock().remove_child_bus(bus_no);
            }
            d.lock().destroy_device();
            let _ = self.root_bus.lock().remove_child_device(address);
        }
        self.sync_multifunction_bit_to_mmio_mappings(&address, false);
    }
    pub fn config_space_read(&self, address: PciAddress, register: usize) -> u32 {
        if address.is_root() {
            if register == PCIE_XBAR_BASE_ADDR && self.pcie_cfg_mmio.is_some() {
                let pcie_mmio = self.pcie_cfg_mmio.unwrap() as u32;
                pcie_mmio | 0x1
            } else if register == (PCIE_XBAR_BASE_ADDR + 1) && self.pcie_cfg_mmio.is_some() {
                (self.pcie_cfg_mmio.unwrap() >> 32) as u32
            } else {
                self.root_configuration.config_register_read(register)
            }
        } else {
            let mut data = self
                .devices
                .get(&address)
                .map_or(0xffff_ffff, |d| d.lock().config_register_read(register));
            if register == HEADER_TYPE_REG {
                if self.num_multifunction_device(&address) != 0 {
                    data |= (HEADER_TYPE_MULTIFUNCTION_MASK as u32) << (HEADER_TYPE_REG_OFFSET * 8);
                }
            }
            data
        }
    }
    pub fn config_space_write(
        &mut self,
        address: PciAddress,
        register: usize,
        offset: u64,
        data: &[u8],
    ) {
        if offset as usize + data.len() > 4 {
            return;
        }
        if address.is_root() {
            self.root_configuration
                .config_register_write(register, offset, data);
        } else if let Some(d) = self.devices.get(&address) {
            let res = d.lock().config_register_write(register, offset, data);
            if !res.mmio_add.is_empty() || !res.mmio_remove.is_empty() {
                let mmio_bus = match self.mmio_bus.upgrade() {
                    Some(m) => m,
                    None => return,
                };
                for range in &res.mmio_remove {
                    let _ = mmio_bus.remove(range.base, range.len);
                }
                for range in &res.mmio_add {
                    let _ = mmio_bus.insert(d.clone(), range.base, range.len);
                }
            }
            if !res.io_add.is_empty() || !res.io_remove.is_empty() {
                let io_bus = match self.io_bus.upgrade() {
                    Some(i) => i,
                    None => return,
                };
                for range in &res.io_remove {
                    let _ = io_bus.remove(range.base, range.len);
                }
                for range in &res.io_add {
                    let _ = io_bus.insert(d.clone(), range.base, range.len);
                }
            }
            for remove_pci_device in res.removed_pci_devices.iter() {
                self.remove_device(*remove_pci_device);
            }
        }
    }
    pub fn virtual_config_space_read(&self, address: PciAddress, register: usize) -> u32 {
        if address.is_root() {
            0u32
        } else {
            self.devices
                .get(&address)
                .map_or(0u32, |d| d.lock().virtual_config_register_read(register))
        }
    }
    pub fn virtual_config_space_write(&mut self, address: PciAddress, register: usize, value: u32) {
        if !address.is_root() {
            if let Some(d) = self.devices.get(&address) {
                d.lock().virtual_config_register_write(register, value);
            }
        }
    }
    pub fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
        AnySnapshot::to_any(PciRootSerializable {
            root_configuration: self
                .root_configuration
                .snapshot()
                .context("failed to serialize PciRoot.root_configuration")?,
            pcie_cfg_mmio: self.pcie_cfg_mmio,
        })
        .context("failed to serialize PciRoot")
    }
    pub fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
        let deser: PciRootSerializable =
            AnySnapshot::from_any(data).context("failed to deserialize PciRoot")?;
        self.root_configuration.restore(deser.root_configuration)?;
        self.pcie_cfg_mmio = deser.pcie_cfg_mmio;
        Ok(())
    }
    fn num_multifunction_device(&self, address: &PciAddress) -> usize {
        self.devices
            .range((
                Included(&PciAddress {
                    func: 1,
                    ..*address
                }),
                Included(&PciAddress {
                    func: 7,
                    ..*address
                }),
            ))
            .count()
    }
}
impl PciRootMmioState {
    fn setup_mapping<T>(
        &mut self,
        address: &PciAddress,
        device: &mut dyn BusDevice,
        mapper: &mut T,
    ) -> anyhow::Result<()>
    where
        T: PciMmioMapper,
    {
        if cfg!(not(any(target_arch = "x86_64", target_arch = "aarch64"))) {
            return Ok(());
        }
        if !mapper.supports_readonly_mapping() {
            return Ok(());
        }
        let pagesize = base::pagesize();
        let offset = address.to_config_address(0, self.register_bit_num);
        let mmio_mapping_num = offset / pagesize as u32;
        let (shmem, new_entry) = match self.mappings.entry(mmio_mapping_num) {
            BTreeMapEntry::Vacant(e) => {
                let shmem = SharedMemory::new(
                    format!("{:04x}_pci_cfg_mapping", mmio_mapping_num),
                    pagesize as u64,
                )
                .context("failed to create shmem")?;
                let mapping = MemoryMappingBuilder::new(pagesize)
                    .from_shared_memory(&shmem)
                    .protection(Protection::read_write())
                    .build()
                    .context("failed to map shmem")?;
                let (shmem, _) = e.insert(Some((shmem, mapping))).as_ref().unwrap();
                (shmem, true)
            }
            BTreeMapEntry::Occupied(e) => {
                let Some((shmem, _)) = e.into_mut() else {
                    return Ok(());
                };
                (&*shmem, false)
            }
        };
        if device.init_pci_config_mapping(
            shmem,
            offset as usize % pagesize,
            1 << self.register_bit_num,
        ) {
            if new_entry {
                let mmio_address = self
                    .base
                    .unchecked_add(mmio_mapping_num as u64 * pagesize as u64);
                match mapper.add_mapping(mmio_address, shmem) {
                    Ok(_) => (),
                    Err(e) => error!("Failed to map mmio page; {:?}", e),
                }
            }
        } else {
            self.mappings.insert(mmio_mapping_num, None);
        }
        Ok(())
    }
    fn set_mfd_bit(&mut self, address: &PciAddress, is_mfd: bool) {
        let pagesize = base::pagesize();
        let offset = address.to_config_address(0, self.register_bit_num);
        let mapping_num = offset / pagesize as u32;
        if let Some(Some((_, mapping))) = self.mappings.get_mut(&mapping_num) {
            let mapping_base = offset as usize % pagesize;
            let reg_offset = mapping_base + (HEADER_TYPE_REG * 4) + HEADER_TYPE_REG_OFFSET;
            let mut val = mapping.read_obj::<u8>(reg_offset).expect("memcpy failed");
            val = if is_mfd {
                val | HEADER_TYPE_MULTIFUNCTION_MASK
            } else {
                val & !HEADER_TYPE_MULTIFUNCTION_MASK
            };
            mapping
                .write_obj_volatile(val, reg_offset)
                .expect("memcpy failed");
            if let Err(err) = mapping.flush_region(reg_offset, 4) {
                error!("failed to flush write to mfd bit: {}", err);
            }
        }
    }
}
pub trait PciMmioMapper {
    fn supports_readonly_mapping(&self) -> bool;
    fn add_mapping(&mut self, addr: GuestAddress, shmem: &SharedMemory) -> anyhow::Result<u32>;
}
impl<T: Vm> PciMmioMapper for T {
    fn supports_readonly_mapping(&self) -> bool {
        self.check_capability(hypervisor::VmCap::ReadOnlyMemoryRegion)
    }
    fn add_mapping(&mut self, addr: GuestAddress, shmem: &SharedMemory) -> anyhow::Result<u32> {
        let mapping = MemoryMappingBuilder::new(base::pagesize())
            .from_shared_memory(shmem)
            .protection(Protection::read())
            .build()
            .context("failed to map shmem")?;
        self.add_memory_region(
            addr,
            Box::new(mapping),
            true,
            false,
            MemCacheType::CacheCoherent,
        )
        .context("failed to create vm mapping")
    }
}
pub struct PciConfigIo {
    pci_root: Arc<Mutex<PciRoot>>,
    config_address: u32,
    break_linux_pci_config_io: bool,
    reset_evt_wrtube: SendTube,
}
#[derive(Serialize, Deserialize)]
struct PciConfigIoSerializable {
    pci_root: AnySnapshot,
    config_address: u32,
}
impl PciConfigIo {
    const REGISTER_BITS_NUM: usize = 8;
    pub fn new(
        pci_root: Arc<Mutex<PciRoot>>,
        break_linux_pci_config_io: bool,
        reset_evt_wrtube: SendTube,
    ) -> Self {
        PciConfigIo {
            pci_root,
            config_address: 0,
            break_linux_pci_config_io,
            reset_evt_wrtube,
        }
    }
    fn config_space_read(&self) -> u32 {
        let enabled = (self.config_address & 0x8000_0000) != 0;
        if !enabled {
            return 0xffff_ffff;
        }
        let (address, register) =
            PciAddress::from_config_address(self.config_address, Self::REGISTER_BITS_NUM);
        self.pci_root.lock().config_space_read(address, register)
    }
    fn config_space_write(&mut self, offset: u64, data: &[u8]) {
        let enabled = (self.config_address & 0x8000_0000) != 0;
        if !enabled {
            return;
        }
        let (address, register) =
            PciAddress::from_config_address(self.config_address, Self::REGISTER_BITS_NUM);
        self.pci_root
            .lock()
            .config_space_write(address, 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 * 8),
                u32::from(u16::from_le_bytes(data.try_into().unwrap())) << (offset * 8),
            ),
            4 => (0xffff_ffff, u32::from_le_bytes(data.try_into().unwrap())),
            _ => return,
        };
        self.config_address = (self.config_address & !mask) | value;
    }
}
const PCI_RESET_CPU_BIT: u8 = 1 << 2;
impl BusDevice for PciConfigIo {
    fn debug_label(&self) -> String {
        "pci config io-port".to_string()
    }
    fn device_id(&self) -> DeviceId {
        PciId::new(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441).into()
    }
    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
        if self.break_linux_pci_config_io {
            for d in data {
                *d = 0xff;
            }
            return;
        }
        let value = match info.offset {
            0..=3 => self.config_address,
            4..=7 => self.config_space_read(),
            _ => 0xffff_ffff,
        };
        let start = info.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, info: BusAccessInfo, data: &[u8]) {
        match info.offset {
            _o @ 1 if data.len() == 1 && data[0] & PCI_RESET_CPU_BIT != 0 => {
                if let Err(e) = self
                    .reset_evt_wrtube
                    .send::<VmEventType>(&VmEventType::Reset)
                {
                    error!("failed to trigger PCI 0xcf9 reset event: {}", e);
                }
            }
            o @ 0..=3 => self.set_config_address(o, data),
            o @ 4..=7 => self.config_space_write(o - 4, data),
            _ => (),
        };
    }
}
impl Suspendable for PciConfigIo {
    fn sleep(&mut self) -> anyhow::Result<()> {
        Ok(())
    }
    fn wake(&mut self) -> anyhow::Result<()> {
        Ok(())
    }
    fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
        AnySnapshot::to_any(PciConfigIoSerializable {
            pci_root: self
                .pci_root
                .lock()
                .snapshot()
                .context("failed to snapshot root")?,
            config_address: self.config_address,
        })
        .with_context(|| format!("failed to serialize {}", self.debug_label()))
    }
    fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
        let mut root = self.pci_root.lock();
        let deser: PciConfigIoSerializable = AnySnapshot::from_any(data)
            .with_context(|| format!("failed to deserialize {}", self.debug_label()))?;
        root.restore(deser.pci_root)?;
        self.config_address = deser.config_address;
        Ok(())
    }
}
pub struct PciConfigMmio {
    pci_root: Arc<Mutex<PciRoot>>,
    register_bit_num: usize,
}
#[derive(Serialize, Deserialize)]
struct PciConfigMmioSerializable {
    pci_root: AnySnapshot,
    register_bit_num: usize,
}
impl PciConfigMmio {
    pub fn new(pci_root: Arc<Mutex<PciRoot>>, register_bit_num: usize) -> Self {
        PciConfigMmio {
            pci_root,
            register_bit_num,
        }
    }
    fn config_space_read(&self, config_address: u32) -> u32 {
        let (address, register) =
            PciAddress::from_config_address(config_address, self.register_bit_num);
        self.pci_root.lock().config_space_read(address, register)
    }
    fn config_space_write(&mut self, config_address: u32, offset: u64, data: &[u8]) {
        let (address, register) =
            PciAddress::from_config_address(config_address, self.register_bit_num);
        self.pci_root
            .lock()
            .config_space_write(address, register, offset, data)
    }
}
impl BusDevice for PciConfigMmio {
    fn debug_label(&self) -> String {
        "pci config mmio".to_owned()
    }
    fn device_id(&self) -> DeviceId {
        PciId::new(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441).into()
    }
    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
        let start = info.offset as usize % 4;
        let end = start + data.len();
        if end > 4 || info.offset > u32::MAX as u64 {
            for d in data {
                *d = 0xff;
            }
            return;
        }
        let value = self.config_space_read(info.offset as u32);
        for i in start..end {
            data[i - start] = (value >> (i * 8)) as u8;
        }
    }
    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
        if info.offset > u32::MAX as u64 {
            return;
        }
        self.config_space_write(info.offset as u32, info.offset % 4, data)
    }
}
impl Suspendable for PciConfigMmio {
    fn sleep(&mut self) -> anyhow::Result<()> {
        Ok(())
    }
    fn wake(&mut self) -> anyhow::Result<()> {
        Ok(())
    }
    fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
        AnySnapshot::to_any(PciConfigMmioSerializable {
            pci_root: self
                .pci_root
                .lock()
                .snapshot()
                .context("failed to snapshot root")?,
            register_bit_num: self.register_bit_num,
        })
        .with_context(|| format!("failed to serialize {}", self.debug_label()))
    }
    fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
        let mut root = self.pci_root.lock();
        let deser: PciConfigMmioSerializable = AnySnapshot::from_any(data)
            .with_context(|| format!("failed to deserialize {}", self.debug_label()))?;
        root.restore(deser.pci_root)?;
        self.register_bit_num = deser.register_bit_num;
        Ok(())
    }
}
pub struct PciVirtualConfigMmio {
    pci_root: Arc<Mutex<PciRoot>>,
    register_bit_num: usize,
}
#[derive(Serialize, Deserialize)]
struct PciVirtualConfigMmioSerializable {
    pci_root: AnySnapshot,
    register_bit_num: usize,
}
impl PciVirtualConfigMmio {
    pub fn new(pci_root: Arc<Mutex<PciRoot>>, register_bit_num: usize) -> Self {
        PciVirtualConfigMmio {
            pci_root,
            register_bit_num,
        }
    }
}
impl BusDevice for PciVirtualConfigMmio {
    fn debug_label(&self) -> String {
        "pci virtual config mmio".to_owned()
    }
    fn device_id(&self) -> DeviceId {
        PciId::new(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441).into()
    }
    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
        let value = if info.offset % 4 != 0 || data.len() != 4 {
            error!(
                "{} unexpected read at offset = {}, len = {}",
                self.debug_label(),
                info.offset,
                data.len()
            );
            0u32
        } else {
            let (address, register) =
                PciAddress::from_config_address(info.offset as u32, self.register_bit_num);
            self.pci_root
                .lock()
                .virtual_config_space_read(address, register)
        };
        data[0..4].copy_from_slice(&value.to_le_bytes()[..]);
    }
    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
        if info.offset % 4 != 0 || data.len() != 4 {
            error!(
                "{} unexpected write at offset = {}, len = {}",
                self.debug_label(),
                info.offset,
                data.len()
            );
            return;
        }
        let value = u32::from_le_bytes(data.try_into().unwrap());
        let (address, register) =
            PciAddress::from_config_address(info.offset as u32, self.register_bit_num);
        self.pci_root
            .lock()
            .virtual_config_space_write(address, register, value)
    }
}
impl Suspendable for PciVirtualConfigMmio {
    fn sleep(&mut self) -> anyhow::Result<()> {
        Ok(())
    }
    fn wake(&mut self) -> anyhow::Result<()> {
        Ok(())
    }
    fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
        AnySnapshot::to_any(PciVirtualConfigMmioSerializable {
            pci_root: self
                .pci_root
                .lock()
                .snapshot()
                .context("failed to snapshot root")?,
            register_bit_num: self.register_bit_num,
        })
        .with_context(|| format!("failed to serialize {}", self.debug_label()))
    }
    fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
        let mut root = self.pci_root.lock();
        let deser: PciVirtualConfigMmioSerializable = AnySnapshot::from_any(data)
            .with_context(|| format!("failed to deserialize {}", self.debug_label()))?;
        root.restore(deser.pci_root)?;
        self.register_bit_num = deser.register_bit_num;
        Ok(())
    }
}
#[cfg(test)]
mod tests {
    use base::Tube;
    use super::*;
    use crate::suspendable_tests;
    fn create_pci_root() -> Arc<Mutex<PciRoot>> {
        let io_bus = Arc::new(Bus::new(BusType::Io));
        let mmio_bus = Arc::new(Bus::new(BusType::Mmio));
        let root_bus = Arc::new(Mutex::new(PciBus::new(0, 0, false)));
        Arc::new(Mutex::new(PciRoot::create_for_test(
            Arc::downgrade(&mmio_bus),
            GuestAddress(0),
            0,
            Arc::downgrade(&io_bus),
            root_bus,
        )))
    }
    fn create_pci_io_config(pci_root: Arc<Mutex<PciRoot>>) -> PciConfigIo {
        let (reset_evt_wrtube, _) = Tube::directional_pair().unwrap();
        PciConfigIo::new(pci_root, false, reset_evt_wrtube)
    }
    fn modify_pci_io_config(pci_config: &mut PciConfigIo) {
        pci_config.config_address += 1;
    }
    fn create_pci_mmio_config(pci_root: Arc<Mutex<PciRoot>>) -> PciConfigMmio {
        PciConfigMmio::new(pci_root, 0)
    }
    fn modify_pci_mmio_config(pci_config: &mut PciConfigMmio) {
        pci_config.register_bit_num += 1;
    }
    fn create_pci_virtual_config_mmio(pci_root: Arc<Mutex<PciRoot>>) -> PciVirtualConfigMmio {
        PciVirtualConfigMmio::new(pci_root, 0)
    }
    fn modify_pci_virtual_config_mmio(pci_config: &mut PciVirtualConfigMmio) {
        pci_config.register_bit_num += 1;
    }
    suspendable_tests!(
        pci_io_config,
        create_pci_io_config(create_pci_root()),
        modify_pci_io_config
    );
    suspendable_tests!(
        pcie_mmio_config,
        create_pci_mmio_config(create_pci_root()),
        modify_pci_mmio_config
    );
    suspendable_tests!(
        pci_virtual_config_mmio,
        create_pci_virtual_config_mmio(create_pci_root()),
        modify_pci_virtual_config_mmio
    );
    #[test]
    fn pci_set_config_address_word() {
        let mut pci_io_config = create_pci_io_config(create_pci_root());
        pci_io_config.write(
            BusAccessInfo {
                offset: 0,
                address: 0xCF8,
                id: 0,
            },
            &[0x44, 0x33, 0x22, 0x11],
        );
        pci_io_config.write(
            BusAccessInfo {
                offset: 2,
                address: 0xCFA,
                id: 0,
            },
            &[0xAA, 0x55],
        );
        let mut config_address = [0u8; 4];
        pci_io_config.read(
            BusAccessInfo {
                offset: 0,
                address: 0xCF8,
                id: 0,
            },
            &mut config_address,
        );
        assert_eq!(config_address, [0x44, 0x33, 0xAA, 0x55]);
    }
}