diff --git a/src/device/irqchip/gicv3/gits.rs b/src/device/irqchip/gicv3/gits.rs index 9a44abe8..5b57551f 100644 --- a/src/device/irqchip/gicv3/gits.rs +++ b/src/device/irqchip/gicv3/gits.rs @@ -20,7 +20,7 @@ use spin::{mutex::Mutex, Once, RwLock}; use crate::{ consts::MAX_ZONE_NUM, cpu_data::this_zone, device::irqchip::gicv3::gicr::enable_one_lpi, - memory::Frame, + memory::Frame, pci::pci_struct::Bdf, }; use super::host_gits_base; @@ -45,6 +45,18 @@ pub const CMDQ_PAGES_NUM: usize = 16; // 16 pages, 64KB pub const PER_CMD_BYTES: usize = 0x20; pub const PER_CMD_QWORD: usize = PER_CMD_BYTES >> 3; +pub const ITS_CMD_MOVI: usize = 0x01; +pub const ITS_CMD_INT: usize = 0x03; +pub const ITS_CMD_CLEAR: usize = 0x04; +pub const ITS_CMD_SYNC: usize = 0x05; +pub const ITS_CMD_MAPD: usize = 0x08; +pub const ITS_CMD_MAPC: usize = 0x09; +pub const ITS_CMD_MAPTI: usize = 0x0a; +pub const ITS_CMD_MAPI: usize = 0x0b; +pub const ITS_CMD_INV: usize = 0x0c; +pub const ITS_CMD_INVALL: usize = 0x0d; +pub const ITS_CMD_DISCARD: usize = 0x0f; + fn ring_ptr_update(val: usize, page_num: usize) -> usize { let total_size = CMDQ_PAGE_SIZE * page_num; if val >= total_size { @@ -237,32 +249,65 @@ impl Cmdq { let binding = this_zone(); let zone = binding.read(); let cpuset_bitmap = zone.cpu_set.bitmap; + + let vicid_to_icid_checked = |vicid: u64| -> u64 { + vicid_to_icid(vicid, cpuset_bitmap) + .expect("vicid to icid failed, maybe logical_id out of range") + }; + let set_cmd2_icid = |cmd2: &mut u64, icid: u64| { + *cmd2 &= !0xffffu64; + *cmd2 |= icid & 0xffff; + }; + + // vbdf -> bdf + let id_32 = (value[0] >> 32) as u32; + let domain = (id_32 >> 16) as u8; + let vbus = ((id_32 >> 8) & 0xFF) as u8; + let vdevice = ((id_32 >> 3) & 0x1F) as u8; + let vfunction = (id_32 & 0x07) as u8; + + let vbdf = Bdf::new(domain, vbus, vdevice, vfunction); + let bdf = match zone.vpci_bus.get(&vbdf) { + Some(vdev) => vdev.read().get_bdf(), + None => Bdf { + domain, + bus: 0, + device: 0, + function: 0, + }, + }; + + let phys_id_32 = ((bdf.domain as u32) << 16) + | ((bdf.bus as u32) << 8) + | ((bdf.device as u32) << 3) + | (bdf.function as u32); + + // new_cmd[0] = cmd0_tmp + // CLEAR DISCARD INT INV MAPD MAPI MAPTI MOVI + let cmd0_tmp = (value[0] & 0xffffffff) | ((phys_id_32 as u64) << 32); + match code { - 0x0b => { - let id = value[0] & 0xffffffff00000000; + ITS_CMD_MAPI => { + new_cmd[0] = cmd0_tmp; + let event = value[1] & 0xffffffff; let vicid = value[2] & 0xffff; - let icid = vicid_to_icid(vicid, cpuset_bitmap) - .expect("vicid to icid failed, maybe logical_id out of range"); - new_cmd[2] &= !0xffffu64; - new_cmd[2] |= icid & 0xffff; + let icid = vicid_to_icid_checked(vicid); + set_cmd2_icid(&mut new_cmd[2], icid); enable_one_lpi((event - 8192) as _); debug!( - "MAPI cmd, for device {:#x}, event = intid = {:#x} -> vicid {:#x} (icid {:#x})", - id >> 32, + "MAPI cmd, for vbdf {:#x}:{:#x}:{:#x}:{:#x} -> {:#x}:{:#x}:{:#x}:{:#x}, event = intid = {:#x} -> vicid {:#x} (icid {:#x})", + domain, vbus, vdevice, vfunction, + bdf.domain, bdf.bus, bdf.device, bdf.function, event, vicid, icid ); } - 0x08 => { - let id = value[0] & 0xffffffff00000000; + ITS_CMD_MAPD => { + new_cmd[0] = cmd0_tmp; + let itt_base = value[2] & 0x000fffffffffff00; // the lowest 8 bits are zeros - debug!( - "MAPD cmd, for device {:#x}, itt base {:#x}", - id >> 32, - itt_base - ); let phys_itt_base = unsafe { this_zone() .read() @@ -274,69 +319,73 @@ impl Cmdq { new_cmd[2] &= !0x000fffffffffff00u64; new_cmd[2] |= phys_itt_base as u64; debug!( - "MAPD cmd, set ITT: {:#x} to device {:#x}", + "MAPD cmd, set ITT: {:#x} to vbdf {:#x}:{:#x}:{:#x}:{:#x} -> {:#x}:{:#x}:{:#x}:{:#x}", phys_itt_base, - id >> 32 + domain, vbus, vdevice, vfunction, + bdf.domain, bdf.bus, bdf.device, bdf.function ); } - 0x0a => { - let id = value[0] & 0xffffffff00000000; + ITS_CMD_MAPTI => { + new_cmd[0] = cmd0_tmp; + let event = value[1] & 0xffffffff; let intid = value[1] >> 32; let vicid = value[2] & 0xffff; - let icid = vicid_to_icid(vicid, cpuset_bitmap) - .expect("vicid to icid failed, maybe logical_id out of range"); - new_cmd[2] &= !0xffffu64; - new_cmd[2] |= icid & 0xffff; + let icid = vicid_to_icid_checked(vicid); + set_cmd2_icid(&mut new_cmd[2], icid); enable_one_lpi((intid - 8192) as _); debug!( - "MAPTI cmd, for device {:#x}, event {:#x} -> vicid {:#x} (icid {:#x}) + intid {:#x}", - id >> 32, + "MAPTI cmd, for vbdf {:#x}:{:#x}:{:#x}:{:#x} -> {:#x}:{:#x}:{:#x}:{:#x}, event {:#x} -> vicid {:#x} (icid {:#x}) + intid {:#x}", + domain, vbus, vdevice, vfunction, + bdf.domain, bdf.bus, bdf.device, bdf.function, event, vicid, icid, intid ); } - 0x09 => { + ITS_CMD_MAPC => { let vicid = value[2] & 0xffff; - let icid = vicid_to_icid(vicid, cpuset_bitmap) - .expect("vicid to icid failed, maybe logical_id out of range"); - new_cmd[2] &= !0xffffu64; - new_cmd[2] |= icid & 0xffff; + let icid = vicid_to_icid_checked(vicid); + set_cmd2_icid(&mut new_cmd[2], icid); let rd_base = (value[2] >> 16) & 0x7ffffffff; debug!( "MAPC cmd, vicid {:#x} (icid {:#x}) -> redist {:#x}", vicid, icid, rd_base ); } - 0x05 => { + ITS_CMD_SYNC => { debug!("SYNC cmd"); } - 0x04 => { + ITS_CMD_CLEAR => { + new_cmd[0] = cmd0_tmp; debug!("CLEAR cmd"); } - 0x0f => { + ITS_CMD_DISCARD => { + new_cmd[0] = cmd0_tmp; debug!("DISCARD cmd"); } - 0x03 => { + ITS_CMD_INT => { + new_cmd[0] = cmd0_tmp; debug!("INT cmd"); } - 0x0c => { + ITS_CMD_INV => { + new_cmd[0] = cmd0_tmp; debug!("INV cmd"); } - 0x0d => { + ITS_CMD_INVALL => { debug!("INVALL cmd"); } - 0x01 => { + ITS_CMD_MOVI => { + new_cmd[0] = cmd0_tmp; + let vicid = value[2] & 0xffff; - let icid = vicid_to_icid(vicid, cpuset_bitmap) - .expect("vicid to icid failed, maybe logical_id out of range"); - new_cmd[2] &= !0xffffu64; - new_cmd[2] |= icid & 0xffff; + let icid = vicid_to_icid_checked(vicid); + set_cmd2_icid(&mut new_cmd[2], icid); debug!( - "MOVI, for Device {:#x}, new vicid({}) -> icid({})", - value[0] >> 32, + "MOVI, for vbdf {:#x}:{:#x}:{:#x}:{:#x} -> {:#x}:{:#x}:{:#x}:{:#x}, new vicid({}) -> icid({})", + domain, vbus, vdevice, vfunction, + bdf.domain, bdf.bus, bdf.device, bdf.function, vicid, icid ); diff --git a/src/hypercall/mod.rs b/src/hypercall/mod.rs index 55a3b8e1..82489b11 100644 --- a/src/hypercall/mod.rs +++ b/src/hypercall/mod.rs @@ -22,6 +22,7 @@ use crate::consts::{INVALID_ADDRESS, MAX_CPU_NUM, MAX_WAIT_TIMES, PAGE_SIZE}; use crate::cpu_data::{get_cpu_data, PerCpu}; use crate::device::virtio_trampoline::{MAX_DEVS, VIRTIO_BRIDGE, VIRTIO_IRQS}; use crate::error::HvResult; +use crate::pci::pci_config::GLOBAL_PCIE_LIST; use crate::zone::{ add_zone, all_zones_info, find_zone, is_this_root_zone, remove_zone, zone_create, ZoneInfo, }; @@ -272,6 +273,16 @@ impl<'a> HyperCall<'a> { drop(zone_w); drop(zone); + + // Reset zone_id for all devices allocated to this zone + let pci_list = GLOBAL_PCIE_LIST.lock(); + for (_bdf, dev) in pci_list.iter() { + if dev.get_zone_id() == Some(zone_id as u32) { + dev.set_zone_id(None); + } + } + drop(pci_list); + remove_zone(zone_id as _); info!("zone {} has been shutdown", zone_id); HyperCallResult::Ok(0) diff --git a/src/pci/pci_config.rs b/src/pci/pci_config.rs index 34f881fc..777f6f06 100644 --- a/src/pci/pci_config.rs +++ b/src/pci/pci_config.rs @@ -44,12 +44,15 @@ use crate::pci::{ use crate::pci::{mem_alloc::BaseAllocator, pci_struct::RootComplex}; #[cfg(feature = "ecam_pcie")] -use crate::pci::pci_handler::mmio_vpci_handler; +use crate::pci::{config_accessors::ecam::EcamConfigAccessor, pci_handler::mmio_vpci_handler}; + #[cfg(feature = "dwc_pcie")] use crate::{ memory::mmio_generic_handler, pci::{ - config_accessors::{dwc::DwcConfigRegionBackend, dwc_atu::AtuConfig, PciRegionMmio}, + config_accessors::{ + dwc::DwcConfigAccessor, dwc::DwcConfigRegionBackend, dwc_atu::AtuConfig, PciRegionMmio, + }, pci_handler::{mmio_dwc_cfg_handler, mmio_dwc_io_handler, mmio_vpci_handler_dbi}, PciConfigAddress, }, @@ -57,7 +60,9 @@ use crate::{ }; #[cfg(feature = "loongarch64_pcie")] -use crate::pci::pci_handler::mmio_vpci_direct_handler; +use crate::pci::{ + config_accessors::loongarch64::LoongArchConfigAccessor, pci_handler::mmio_vpci_direct_handler, +}; pub static GLOBAL_PCIE_LIST: Lazy>> = Lazy::new(|| { @@ -172,7 +177,7 @@ impl Zone { pci_config: &[HvPciConfig], _num_pci_config: usize, ) -> HvResult { - let mut guard = GLOBAL_PCIE_LIST.lock(); + let guard = GLOBAL_PCIE_LIST.lock(); for target_pci_config in pci_config { // Skip empty config if target_pci_config.ecam_base == 0 { @@ -184,6 +189,46 @@ impl Zone { let target_domain = target_pci_config.domain; let bus_range_begin = target_pci_config.bus_range_begin as u8; + // Create accessor for VirtualRootComplex, similar to RootComplex + #[cfg(feature = "dwc_pcie")] + { + use alloc::sync::Arc; + let atu_config = platform::ROOT_DWC_ATU_CONFIG + .iter() + .find(|atu_cfg| atu_cfg.ecam_base == ecam_base); + + match atu_config { + Some(cfg) => { + let root_bus = bus_range_begin; + let accessor = Arc::new(DwcConfigAccessor::new(cfg, root_bus)); + self.vpci_bus.set_accessor(accessor); + } + None => { + warn!("No ATU config found for ecam_base 0x{:x}", ecam_base); + continue; + } + } + } + + #[cfg(feature = "loongarch64_pcie")] + { + use alloc::sync::Arc; + let root_bus = bus_range_begin; + let accessor = Arc::new(LoongArchConfigAccessor::new( + ecam_base, + target_pci_config.ecam_size, + root_bus, + )); + self.vpci_bus.set_accessor(accessor); + } + + #[cfg(feature = "ecam_pcie")] + { + use alloc::sync::Arc; + let accessor = Arc::new(EcamConfigAccessor::new(ecam_base)); + self.vpci_bus.set_accessor(accessor); + } + let mut filtered_devices: alloc::vec::Vec = alloc::vec::Vec::new(); for i in 0..num_pci_devs { let dev_config = alloc_pci_devs[i as usize]; @@ -207,6 +252,7 @@ impl Zone { let mut vbus_pre = bus_range_begin; let mut bus_pre = bus_range_begin; let mut device_pre = 0u8; + let mut vdevice_pre = 0u8; /* * To allow Linux to successfully recognize the devices we add, hvisor needs @@ -246,7 +292,21 @@ impl Zone { vbus_pre }; - let vbdf = Bdf::new(bdf.domain(), vbus, device, vfunction); + // Remap device number to be contiguous, starting from 0 + let vdevice = if bus != bus_pre || device != device_pre { + // New bus or new device, increment device counter + if bus != bus_pre { + vdevice_pre = 0; + } else { + vdevice_pre += 1; + } + vdevice_pre + } else { + // Same bus and device, keep the same virtual device number + vdevice_pre + }; + + let vbdf = Bdf::new(bdf.domain(), vbus, vdevice, vfunction); device_pre = device; bus_pre = bus; @@ -276,14 +336,23 @@ impl Zone { config_value.get_class().0 == 0x6 }) { - let mut vdev = dev.read().clone(); + let mut vdev = dev.read().config_space.clone(); vdev.set_vbdf(vbdf); self.vpci_bus.insert(vbdf, vdev); } else { - let vdev = guard.remove(&bdf).unwrap(); - let mut vdev_inner = vdev.read().clone(); - vdev_inner.set_vbdf(vbdf); - self.vpci_bus.insert(vbdf, vdev_inner); + // Check if device is already allocated to another zone + if dev.get_zone_id().is_none() { + dev.set_zone_id(Some(_zone_id as u32)); + let mut vdev_inner = dev.read().config_space.clone(); + vdev_inner.set_vbdf(vbdf); + self.vpci_bus.insert(vbdf, vdev_inner); + } else { + warn!( + "Device {:#?} is already allocated to zone {:?}", + bdf, + dev.get_zone_id() + ); + } } } else { // warn!("can not find dev {:#?}", bdf); diff --git a/src/pci/pci_struct.rs b/src/pci/pci_struct.rs index 13140269..c654edf6 100644 --- a/src/pci/pci_struct.rs +++ b/src/pci/pci_struct.rs @@ -368,68 +368,102 @@ pub struct VirtualPciConfigSpace { } #[derive(Clone)] -pub struct ArcRwLockVirtualPciConfigSpace(Arc>); +pub struct VirtualPciConfigSpaceWithZone { + pub zone_id: Option, + pub config_space: VirtualPciConfigSpace, +} + +impl core::ops::Deref for VirtualPciConfigSpaceWithZone { + type Target = VirtualPciConfigSpace; + + fn deref(&self) -> &Self::Target { + &self.config_space + } +} + +impl core::ops::DerefMut for VirtualPciConfigSpaceWithZone { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.config_space + } +} + +impl Debug for VirtualPciConfigSpaceWithZone { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "zone_id: {:?}, ", self.zone_id)?; + self.config_space.fmt(f) + } +} + +#[derive(Clone)] +pub struct ArcRwLockVirtualPciConfigSpace(Arc>); impl ArcRwLockVirtualPciConfigSpace { pub fn new(dev: VirtualPciConfigSpace) -> Self { - Self(Arc::new(RwLock::new(dev))) + Self(Arc::new(RwLock::new(VirtualPciConfigSpaceWithZone { + zone_id: None, + config_space: dev, + }))) } - pub fn inner(&self) -> &Arc> { - &self.0 + pub fn get_zone_id(&self) -> Option { + self.0.read().zone_id + } + + pub fn set_zone_id(&self, zone_id: Option) { + self.0.write().zone_id = zone_id; } pub fn access(&self, offset: PciConfigAddress, size: usize) -> bool { - self.0.read().access(offset, size) + self.read().access(offset, size) } pub fn get_bdf(&self) -> Bdf { - self.0.read().get_bdf() + self.read().get_bdf() } pub fn get_vbdf(&self) -> Bdf { - self.0.read().get_vbdf() + self.read().get_vbdf() } pub fn get_dev_type(&self) -> VpciDevType { - self.0.read().get_dev_type() + self.read().get_dev_type() } pub fn get_config_type(&self) -> HeaderType { - self.0.read().get_config_type() + self.read().get_config_type() } pub fn get_bararr(&self) -> Bar { - self.0.read().get_bararr() + self.read().get_bararr() } pub fn get_rom(&self) -> PciMem { - self.0.read().get_rom() + self.read().get_rom() } pub fn read_emu(&self, field: EndpointField) -> HvResult { - self.0.write().read_emu(field) + self.write().read_emu(field) } pub fn read_emu64(&self, field: EndpointField) -> HvResult { - self.0.write().read_emu64(field) + self.write().read_emu64(field) } pub fn write_emu(&self, field: EndpointField, value: usize) -> HvResult { - self.0.write().write_emu(field, value) + self.write().write_emu(field, value) } // Legacy method for backward compatibility // pub fn write_emu_legacy(&self, offset: PciConfigAddress, size: usize, value: usize) -> HvResult { - // self.0.write().write_emu_legacy(offset, size, value) + // self.write().write_emu_legacy(offset, size, value) // } pub fn read_hw(&self, offset: PciConfigAddress, size: usize) -> HvResult { - self.0.write().read_hw(offset, size) + self.write().read_hw(offset, size) } pub fn write_hw(&self, offset: PciConfigAddress, size: usize, value: usize) -> HvResult { - self.0.write().write_hw(offset, size, value) + self.write().write_hw(offset, size, value) } /// Execute a closure with a reference to the bar at the given slot @@ -437,7 +471,7 @@ impl ArcRwLockVirtualPciConfigSpace { where F: FnOnce(&PciMem) -> R, { - let guard = self.0.read(); + let guard = self.read(); let bar = guard.get_bar_ref(slot); f(bar) } @@ -447,8 +481,8 @@ impl ArcRwLockVirtualPciConfigSpace { where F: FnOnce(&mut PciMem) -> R, { - let mut guard = self.0.write(); - let bar = guard.get_bar_ref_mut(slot); + let mut inner = self.write(); + let bar = inner.get_bar_ref_mut(slot); f(bar) } @@ -457,8 +491,8 @@ impl ArcRwLockVirtualPciConfigSpace { where F: FnOnce(&ConfigValue) -> R, { - let guard = self.0.read(); - f(&guard.config_value) + let guard = self.read(); + f(guard.get_config_value()) } /// Execute a closure with a mutable reference to the config_value @@ -466,8 +500,8 @@ impl ArcRwLockVirtualPciConfigSpace { where F: FnOnce(&mut ConfigValue) -> R, { - let mut guard = self.0.write(); - f(&mut guard.config_value) + let mut inner = self.write(); + inner.with_config_value_mut(f) } /// Execute a closure with a reference to the rom @@ -475,7 +509,7 @@ impl ArcRwLockVirtualPciConfigSpace { where F: FnOnce(&PciMem) -> R, { - let guard = self.0.read(); + let guard = self.read(); let rom = &guard.rom; f(rom) } @@ -485,8 +519,8 @@ impl ArcRwLockVirtualPciConfigSpace { where F: FnOnce(&mut PciMem) -> R, { - let mut guard = self.0.write(); - let rom = &mut guard.rom; + let mut inner = self.write(); + let rom = &mut inner.rom; f(rom) } @@ -495,22 +529,24 @@ impl ArcRwLockVirtualPciConfigSpace { where F: FnOnce(&PciCapabilityList) -> R, { - let guard = self.0.read(); + let guard = self.read(); f(&guard.capabilities) } - pub fn read(&self) -> spin::RwLockReadGuard<'_, VirtualPciConfigSpace> { + pub fn read(&self) -> spin::RwLockReadGuard<'_, VirtualPciConfigSpaceWithZone> { self.0.read() } - pub fn write(&self) -> spin::RwLockWriteGuard<'_, VirtualPciConfigSpace> { + pub fn write(&self) -> spin::RwLockWriteGuard<'_, VirtualPciConfigSpaceWithZone> { self.0.write() } } impl Debug for ArcRwLockVirtualPciConfigSpace { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - self.0.read().fmt(f) + let guard = self.0.read(); + write!(f, "zone_id: {:?}, ", guard.zone_id)?; + guard.config_space.fmt(f) } } @@ -1434,6 +1470,7 @@ impl RootComplex { pub struct VirtualRootComplex { devs: BTreeMap, base_to_bdf: BTreeMap, + accessor: Option>, } impl VirtualRootComplex { @@ -1441,15 +1478,33 @@ impl VirtualRootComplex { Self { devs: BTreeMap::new(), base_to_bdf: BTreeMap::new(), + accessor: None, } } + pub fn set_accessor(&mut self, accessor: Arc) { + self.accessor = Some(accessor); + } + pub fn insert( &mut self, bdf: Bdf, dev: VirtualPciConfigSpace, ) -> Option { - let base = dev.get_base(); + let parent_bus = dev.parent_bdf.bus(); + let offset = 0; + let base = if let Some(accessor) = &self.accessor { + match accessor.get_physical_address(bdf, offset, parent_bus) { + Ok(addr) => addr, + Err(_) => { + warn!("can not get physical address for device {:#?}(vbdf), reset device base same to hardware", bdf); + dev.get_base() + } + } + } else { + warn!("can not found accessor for vpci bus, reset device base same to hardware"); + dev.get_base() + }; info!("pci insert base {:#x} to bdf {:#?}", base, bdf); self.base_to_bdf.insert(base, bdf); self.devs