diff --git a/Cargo.toml b/Cargo.toml index f7b0e8b2..e4b1320a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,12 @@ iommu = [] # supported by: aarch64 pci = [] # supported by: aarch64, loongarch64 print_timestamp = [] # print timestamp when logging +############# PCIe access mechanism ############## +ecam_pcie = [] # Standard ECAM mechanism (default for most platforms) +dwc_pcie = [] # DesignWare PCIe Core mechanism (CFG0/CFG1, used by RK3568) +loongarch64_pcie = [] # LoongArch PCIe mechanism (used by LoongArch platforms) +no_pcie_bar_realloc = [] + ############# aarch64 ############## # irqchip driver gicv2 = [] diff --git a/platform/aarch64/imx8mp/configs/zone1-ruxos.json b/platform/aarch64/imx8mp/configs/zone1-ruxos.json index b882694a..82335457 100644 --- a/platform/aarch64/imx8mp/configs/zone1-ruxos.json +++ b/platform/aarch64/imx8mp/configs/zone1-ruxos.json @@ -74,5 +74,5 @@ }, "num_pci_devs": 0, "alloc_pci_devs": [], - "pci_config": {} + "pci_config": [] } diff --git a/platform/aarch64/qemu-gicv2/board.rs b/platform/aarch64/qemu-gicv2/board.rs index c8e09dca..d7d58d5b 100644 --- a/platform/aarch64/qemu-gicv2/board.rs +++ b/platform/aarch64/qemu-gicv2/board.rs @@ -19,8 +19,11 @@ use crate::{ zone::{GicConfig, Gicv2Config, HvArchZoneConfig}, }, config::*, + pci::vpci_dev::VpciDevType, }; +use crate::pci_dev; + pub const BOARD_NAME: &str = "qemu-gicv2"; pub const BOARD_NCPUS: usize = 4; @@ -92,20 +95,30 @@ pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { }), }; -pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { - ecam_base: 0x4010000000, - ecam_size: 0x10000000, - io_base: 0x3eff0000, - io_size: 0x10000, - pci_io_base: 0x0, - mem32_base: 0x10000000, - mem32_size: 0x2eff0000, - pci_mem32_base: 0x10000000, - mem64_base: 0x8000000000, - mem64_size: 0x8000000000, - pci_mem64_base: 0x8000000000, -}; + +pub const ROOT_PCI_CONFIG: [HvPciConfig; 1] = [ + HvPciConfig { + ecam_base: 0x4010000000, + ecam_size: 0x10000000, + io_base: 0x3eff0000, + io_size: 0x10000, + pci_io_base: 0x0, + mem32_base: 0x10000000, + mem32_size: 0x2eff0000, + pci_mem32_base: 0x10000000, + mem64_base: 0x8000000000, + mem64_size: 0x8000000000, + pci_mem64_base: 0x8000000000, + bus_range_begin: 0x0, + bus_range_end: 0xff, + domain: 0x0, + } +]; pub const ROOT_ZONE_IVC_CONFIG: [HvIvcConfig; 0] = []; -pub const ROOT_PCI_DEVS: [u64; 2] = [0, 1 << 3]; +pub const ROOT_PCI_DEVS: [HvPciDevConfig; 3] = [ + pci_dev!(0x0, 0x0, 0x0, 0x0, VpciDevType::Physical), + pci_dev!(0x0, 0x0, 0x1, 0x0, VpciDevType::Physical), + pci_dev!(0x0, 0x0, 0x2, 0x0, VpciDevType::Physical), +]; diff --git a/platform/aarch64/qemu-gicv2/image/dts/zone0.dts b/platform/aarch64/qemu-gicv2/image/dts/zone0.dts index c17e8e7f..8cbcae70 100644 --- a/platform/aarch64/qemu-gicv2/image/dts/zone0.dts +++ b/platform/aarch64/qemu-gicv2/image/dts/zone0.dts @@ -85,6 +85,24 @@ compatible = "arm,armv8-timer\0arm,armv7-timer"; }; + pcie@10000000 { + interrupt-map-mask = <0x1800 0x00 0x00 0x07>; + interrupt-map = <0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x03 0x04 0x00 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x800 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x05 0x04 0x800 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x05 0x04 0x1000 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x06 0x04 0x1000 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x06 0x04 0x1800 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x03 0x04 0x1800 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x05 0x04>; + #interrupt-cells = <0x01>; + ranges = <0x1000000 0x00 0x00 0x00 0x3eff0000 0x00 0x10000 + 0x2000000 0x00 0x10000000 0x00 0x10000000 0x00 0x2eff0000 + 0x3000000 0x80 0x00 0x80 0x00 0x80 0x00>; + reg = <0x40 0x10000000 0x00 0x10000000>; + msi-map = <0x00 0x8006 0x00 0x10000>; + dma-coherent; + bus-range = <0x00 0xff>; + linux,pci-domain = <0x00>; + #size-cells = <0x02>; + #address-cells = <0x03>; + device_type = "pci"; + compatible = "pci-host-ecam-generic"; + }; + virtio_mmio@a003a00 { dma-coherent; interrupt-parent = <0x01>; diff --git a/platform/aarch64/qemu-gicv3/board.rs b/platform/aarch64/qemu-gicv3/board.rs index d60b4a8a..02f04aab 100644 --- a/platform/aarch64/qemu-gicv3/board.rs +++ b/platform/aarch64/qemu-gicv3/board.rs @@ -19,7 +19,11 @@ use crate::{ zone::{GicConfig, Gicv3Config, HvArchZoneConfig}, }, config::*, + pci::vpci_dev::VpciDevType, }; + +use crate::pci_dev; + pub const BOARD_NAME: &str = "qemu-gicv3"; pub const BOARD_NCPUS: usize = 4; @@ -51,7 +55,7 @@ pub const ROOT_ZONE_CPUS: u64 = (1 << 0) | (1 << 1); pub const ROOT_ZONE_NAME: &str = "root-linux"; -pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 3] = [ +pub const ROOT_ZONE_MEMORY_REGIONS: &[HvConfigMemoryRegion] = &[ HvConfigMemoryRegion { mem_type: MEM_TYPE_RAM, physical_start: 0x50000000, @@ -89,7 +93,7 @@ pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { }), }; -pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { +pub const ROOT_PCI_CONFIG: [HvPciConfig; 1] = [HvPciConfig { ecam_base: 0x4010000000, ecam_size: 0x10000000, io_base: 0x3eff0000, @@ -101,8 +105,16 @@ pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { mem64_base: 0x8000000000, mem64_size: 0x8000000000, pci_mem64_base: 0x8000000000, -}; + bus_range_begin: 0, + bus_range_end: 0xff, + domain: 0x0, +}]; pub const ROOT_ZONE_IVC_CONFIG: [HvIvcConfig; 0] = []; -pub const ROOT_PCI_DEVS: [u64; 2] = [0, 1 << 3]; +pub const ROOT_PCI_DEVS: &[HvPciDevConfig] = &[ + pci_dev!(0x0, 0x0, 0x0, 0x0, VpciDevType::Physical), + pci_dev!(0x0, 0x0, 0x1, 0x0, VpciDevType::Physical), + pci_dev!(0x0, 0x0, 0x2, 0x0, VpciDevType::Physical), + pci_dev!(0x0, 0x0, 0x5, 0x0, VpciDevType::StandardVdev), +]; diff --git a/platform/aarch64/qemu-gicv3/cargo/features b/platform/aarch64/qemu-gicv3/cargo/features index eb18579e..2092d180 100644 --- a/platform/aarch64/qemu-gicv3/cargo/features +++ b/platform/aarch64/qemu-gicv3/cargo/features @@ -2,3 +2,4 @@ gicv3 pl011 iommu pci +ecam_pcie \ No newline at end of file diff --git a/platform/aarch64/qemu-gicv3/configs/zone1-linux.json b/platform/aarch64/qemu-gicv3/configs/zone1-linux.json index 17cb55f2..e4aa8e37 100644 --- a/platform/aarch64/qemu-gicv3/configs/zone1-linux.json +++ b/platform/aarch64/qemu-gicv3/configs/zone1-linux.json @@ -1,5 +1,4 @@ { - "arch": "arm64", "name": "linux2", "zone_id": 1, "cpus": [2, 3], @@ -12,22 +11,15 @@ }, { "type": "virtio", - "physical_start": "0xa003800", - "virtual_start": "0xa003800", - "size": "0x200" - }, - { - "type": "virtio", - "physical_start": "0xa003c00", - "virtual_start": "0xa003c00", - "size": "0x200" + "physical_start": "0xa000000", + "virtual_start": "0xa000000", + "size": "0x4000" } ], "interrupts": [76, 78], "ivc_configs": [], - "kernel_filepath": "Image", - "kernel_args": "", - "dtb_filepath": "zone1-linux.dtb", + "kernel_filepath": "./Image", + "dtb_filepath": "./linux2.dtb", "kernel_load_paddr": "0x50400000", "dtb_load_paddr": "0x50000000", "entry_point": "0x50400000", @@ -38,9 +30,10 @@ "gicr_base": "0x80a0000", "gicr_size": "0xf60000", "gits_base": "0x8080000", - "gits_size": "0x20000" + "gits_size": "0x20000", + "is_aarch32": false }, - "pci_config": { + "pci_config": [{ "ecam_base": "0x4010000000", "ecam_size": "0x10000000", "io_base": "0x3eff0000", @@ -51,8 +44,25 @@ "pci_mem32_base": "0x10000000", "mem64_base": "0x8000000000", "mem64_size": "0x8000000000", - "pci_mem64_base": "0x8000000000" - }, + "pci_mem64_base": "0x8000000000", + "bus_range_begin": "0x0", + "bus_range_end": "0x1f" + }], "num_pci_devs": 2, - "alloc_pci_devs": [0, 16] + "alloc_pci_devs": [ + { + "domain": "0x0", + "bus": "0x0", + "device": "0x0", + "function": "0x0", + "dev_type": "0" + }, + { + "domain": "0x0", + "bus": "0x0", + "device": "0x1", + "function": "0x0", + "dev_type": "0" + } + ] } \ No newline at end of file diff --git a/platform/aarch64/rk3568/board.rs b/platform/aarch64/rk3568/board.rs index e590c189..d2238aef 100644 --- a/platform/aarch64/rk3568/board.rs +++ b/platform/aarch64/rk3568/board.rs @@ -20,7 +20,9 @@ use crate::{ zone::{GicConfig, Gicv3Config, HvArchZoneConfig}, }, config::*, + pci::vpci_dev::VpciDevType, }; +use crate::pci_dev; pub const BOARD_NAME: &str = "rk3568"; @@ -43,34 +45,90 @@ pub const BOARD_PHYSMEM_LIST: &[(u64, u64, MemoryType)] = &[ // ( start, end, type) ( 0x0, 0xf0000000, MemoryType::Normal), ( 0xf0000000, 0x100000000, MemoryType::Device), + ( 0x100000000, 0x3c0000000, MemoryType::Normal), + ( 0x3c0000000, 0x3d0000000, MemoryType::Device) ]; pub const ROOT_ZONE_DTB_ADDR: u64 = 0xa0000000; -pub const ROOT_ZONE_KERNEL_ADDR: u64 = 0x60080000; -pub const ROOT_ZONE_ENTRY: u64 = 0x60080000; +pub const ROOT_ZONE_KERNEL_ADDR: u64 = 0x00280000 ; +pub const ROOT_ZONE_ENTRY: u64 = 0x00280000 ; //pub const ROOT_ZONE_CPUS: u64 = (1 << 0) ; pub const ROOT_ZONE_CPUS: u64 = (1 << 0) | (1 << 1); pub const ROOT_ZONE_NAME: &str = "root-linux"; -pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 19] = [ +pub const ROOT_ZONE_MEMORY_REGIONS: &[HvConfigMemoryRegion] = &[ // HvConfigMemoryRegion { // mem_type: MEM_TYPE_IO, - // physical_start: 0xfd400000, - // virtual_start: 0xfd400000, - // size: 0x10000, - // }, // gic + // physical_start: 0x3c0400000, + // virtual_start: 0x3c0400000, + // size: 0x400000, + // }, //pcie // HvConfigMemoryRegion { // mem_type: MEM_TYPE_IO, - // physical_start: 0xfd460000, - // virtual_start: 0xfd460000, - // size: 0xc0000, - // }, // gic + // physical_start: 0xfe270000, + // virtual_start: 0xfe270000, + // size: 0x10000, + // }, //pcie HvConfigMemoryRegion { mem_type: MEM_TYPE_IO, - physical_start: 0xfe000000, - virtual_start: 0xfe000000, - size: 0x4000, - }, // dwmmc + physical_start: 0xf2000000, + virtual_start: 0xf2000000, + size: 0x100000, + }, //pcie + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0xf2100000, + virtual_start: 0xf2100000, + size: 0x100000, + }, //pcie + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0xf2200000, + virtual_start: 0xf2200000, + size: 0x1e00000, + }, //pcie + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x340000000, + virtual_start: 0x340000000, + size: 0x40000000, + }, //pcie + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0xfdcb8000, + virtual_start: 0xfdcb8000, + size: 0x10000, + }, //syscon pcie30_phy_grf + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0xfdc90000, + virtual_start: 0xfdc90000, + size: 0x10000, + }, //syscon pipe_phy_grf2 + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0xfdc50000, + virtual_start: 0xfdc50000, + size: 0x10000, + }, //syscon pipegrf + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0xfe8c0000, + virtual_start: 0xfe8c0000, + size: 0x20000, + }, // pcie30phy + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0xfdd90000, + virtual_start: 0xfdd90000, + size: 0x1000, + }, // power-management + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0xfe840000, + virtual_start: 0xfe840000, + size: 0x1000, + }, // combphy2_psq HvConfigMemoryRegion { mem_type: MEM_TYPE_RAM, physical_start: 0x200000, @@ -83,30 +141,54 @@ pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 19] = [ virtual_start: 0x9400000, size: 0xe6c00000, }, // memory + HvConfigMemoryRegion { + mem_type: MEM_TYPE_RAM, + physical_start: 0x0, + virtual_start: 0x0, + size: 0x200000, + }, // ram // HvConfigMemoryRegion { // mem_type: MEM_TYPE_RAM, - // physical_start: 0x0, - // virtual_start: 0x0, - // size: 0x200000, - // }, // ram + // physical_start: 0x110000, + // virtual_start: 0x110000, + // size: 0xf0000, + // }, // ramoops + // HvConfigMemoryRegion { + // mem_type: MEM_TYPE_IO, + // physical_start: 0x10f000, + // virtual_start: 0x10f000, + // size: 0x1000, + // }, //scmi-shmem HvConfigMemoryRegion { mem_type: MEM_TYPE_RAM, - physical_start: 0x110000, - virtual_start: 0x110000, - size: 0xf0000, - }, // ramoops - HvConfigMemoryRegion { - mem_type: MEM_TYPE_IO, - physical_start: 0x10f000, - virtual_start: 0x10f000, - size: 0x1000, - }, //scmi-shmem + physical_start: 0xfd440000, + virtual_start: 0xfd440000, + size: 0x20000, + }, // its HvConfigMemoryRegion { mem_type: MEM_TYPE_RAM, physical_start: 0x1f0000000, virtual_start: 0x1f0000000, size: 0x10000000, }, // memory + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0xfe2b0000, + virtual_start: 0xfe2b0000, + size: 0x4000, + }, //dwmmc mmc1 + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0xfe2c0000, + virtual_start: 0xfe2c0000, + size: 0x4000, + }, //dwmmc mmc2 + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0xfe000000, + virtual_start: 0xfe000000, + size: 0x4000, + }, //dwmmc mmc3 HvConfigMemoryRegion { mem_type: MEM_TYPE_IO, physical_start: 0xFE660000, @@ -199,11 +281,77 @@ pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { gicd_size: 0x10000, gicr_base: 0xfd460000, gicr_size: 0xc0000, - gits_base: 0, - gits_size: 0, + gits_base: 0xfd440000, + gits_size: 0x20000, }), }; +pub const ROOT_PCI_CONFIG: &[HvPciConfig] = &[ + // HvPciConfig { + // ecam_base: 0xfe260000, + // ecam_size: 0x400000, + // io_base: 0xf4100000, + // io_size: 0x100000, + // pci_io_base: 0xf4100000, + // mem32_base: 0xf4200000, + // mem32_size: 0x1e00000, + // pci_mem32_base: 0xf4200000, + // mem64_base: 0x300000000, + // mem64_size: 0x40000000, + // pci_mem64_base: 0x300000000, + // bus_range_begin: 0x0, + // bus_range_end: 0x10, + // domain: 0x0, + // }, + HvPciConfig { + ecam_base: 0x3c0400000, + ecam_size: 0x400000, + io_base: 0xf2100000, + io_size: 0x100000, + pci_io_base: 0xf2100000, + mem32_base: 0xf2200000, + mem32_size: 0x1e00000, + pci_mem32_base: 0xf2200000, + mem64_base: 0x340000000, + mem64_size: 0x40000000, + pci_mem64_base: 0x340000000, + bus_range_begin: 0x10, + bus_range_end: 0x1f, + domain: 0x1, + }, + // HvPciConfig { + // ecam_base: 0xfe280000, + // ecam_size: 0x400000, + // io_base: 0xf0100000, + // io_size: 0x100000, + // pci_io_base: 0xf0100000, + // mem32_base: 0xf0200000, + // mem32_size: 0x1e00000, + // pci_mem32_base: 0xf0200000, + // mem64_base: 0x380000000, + // mem64_size: 0x40000000, + // pci_mem64_base: 0x380000000, + // bus_range_begin: 0x20, + // bus_range_end: 0x2f, + // domain: 0x2, + // } +]; pub const ROOT_ZONE_IVC_CONFIG: [HvIvcConfig; 0] = []; -pub const ROOT_PCI_DEVS: [u64; 0] = []; +pub const ROOT_DWC_ATU_CONFIG: &[HvDwcAtuConfig] = &[ + HvDwcAtuConfig { + ecam_base: 0x3c0400000, + dbi_base: 0x3c0400000, + dbi_size: 0x10000, + apb_base: 0xfe270000, + apb_size: 0x10000, + cfg_base: 0xf2000000, + cfg_size: 0x80000*2, + io_cfg_atu_shared: 0, + }, +]; + +pub const ROOT_PCI_DEVS: [HvPciDevConfig; 2] = [ + pci_dev!(0x0, 0x10, 0x0, 0x0, VpciDevType::Physical), + pci_dev!(0x0, 0x11, 0x0, 0x0, VpciDevType::Physical), +]; \ No newline at end of file diff --git a/platform/aarch64/rk3568/cargo/features b/platform/aarch64/rk3568/cargo/features index afa59489..0b11e0fa 100644 --- a/platform/aarch64/rk3568/cargo/features +++ b/platform/aarch64/rk3568/cargo/features @@ -1,2 +1,5 @@ gicv3 uart_16550 +pci +dwc_pcie +no_pcie_bar_realloc \ No newline at end of file diff --git a/platform/aarch64/rk3568/image/dts/rk3568.dts b/platform/aarch64/rk3568/image/dts/rk3568.dts index 7cee4b82..8f4e912f 100644 --- a/platform/aarch64/rk3568/image/dts/rk3568.dts +++ b/platform/aarch64/rk3568/image/dts/rk3568.dts @@ -1648,48 +1648,48 @@ phandle = <0x155>; }; - // pcie@fe280000 { - // power-domains = <0x22 0x0f>; - // vpcie3v3-supply = <0xb0>; - // #address-cells = <0x03>; - // phy-names = "pcie-phy"; - // bus-range = <0x20 0x2f>; - // clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - // reg-names = "pcie-dbi\0pcie-apb"; - // num-ob-windows = <0x02>; - // resets = <0x20 0xc1>; - // interrupts = <0x00 0xa5 0x04 0x00 0xa4 0x04 0x00 0xa3 0x04 0x00 0xa2 0x04 0x00 0xa1 0x04>; - // clocks = <0x20 0x8f 0x20 0x90 0x20 0x91 0x20 0x92 0x20 0x93>; - // interrupt-map = <0x00 0x00 0x00 0x01 0xb1 0x00 0x00 0x00 0x00 0x02 0xb1 0x01 0x00 0x00 0x00 0x03 0xb1 0x02 0x00 0x00 0x00 0x04 0xb1 0x03>; - // #size-cells = <0x02>; - // max-link-speed = <0x03>; - // device_type = "pci"; - // interrupt-map-mask = <0x00 0x00 0x00 0x07>; - // reset-gpios = <0xb2 0x1e 0x00>; - // num-lanes = <0x02>; - // compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - // ranges = <0x800 0x00 0xf0000000 0x00 0xf0000000 0x00 0x100000 0x81000000 0x00 0xf0100000 0x00 0xf0100000 0x00 0x100000 0x82000000 0x00 0xf0200000 0x00 0xf0200000 0x00 0x1e00000 0xc3000000 0x03 0x80000000 0x03 0x80000000 0x00 0x40000000>; - // msi-map = <0x2000 0xad 0x2000 0x1000>; - // #interrupt-cells = <0x01>; - // status = "okay"; - // interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - // phys = <0xaf>; - // num-viewport = <0x08>; - // reg = <0x03 0xc0800000 0x00 0x400000 0x00 0xfe280000 0x00 0x10000>; - // linux,pci-domain = <0x02>; - // phandle = <0x19f>; - // reset-names = "pipe"; - // num-ib-windows = <0x06>; - - // legacy-interrupt-controller { - // #address-cells = <0x00>; - // interrupts = <0x00 0xa2 0x01>; - // interrupt-parent = <0x01>; - // #interrupt-cells = <0x01>; - // phandle = <0xb1>; - // interrupt-controller; - // }; - // }; + pcie@fe280000 { + power-domains = <0x22 0x0f>; + vpcie3v3-supply = <0xb0>; + #address-cells = <0x03>; + phy-names = "pcie-phy"; + bus-range = <0x20 0x2f>; + clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; + reg-names = "pcie-dbi\0pcie-apb"; + num-ob-windows = <0x02>; + resets = <0x20 0xc1>; + interrupts = <0x00 0xa5 0x04 0x00 0xa4 0x04 0x00 0xa3 0x04 0x00 0xa2 0x04 0x00 0xa1 0x04>; + clocks = <0x20 0x8f 0x20 0x90 0x20 0x91 0x20 0x92 0x20 0x93>; + interrupt-map = <0x00 0x00 0x00 0x01 0xb1 0x00 0x00 0x00 0x00 0x02 0xb1 0x01 0x00 0x00 0x00 0x03 0xb1 0x02 0x00 0x00 0x00 0x04 0xb1 0x03>; + #size-cells = <0x02>; + max-link-speed = <0x03>; + device_type = "pci"; + interrupt-map-mask = <0x00 0x00 0x00 0x07>; + reset-gpios = <0xb2 0x1e 0x00>; + num-lanes = <0x02>; + compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; + ranges = <0x800 0x00 0xf0000000 0x00 0xf0000000 0x00 0x100000 0x81000000 0x00 0xf0100000 0x00 0xf0100000 0x00 0x100000 0x82000000 0x00 0xf0200000 0x00 0xf0200000 0x00 0x1e00000 0xc3000000 0x03 0x80000000 0x03 0x80000000 0x00 0x40000000>; + msi-map = <0x2000 0xad 0x2000 0x1000>; + #interrupt-cells = <0x01>; + status = "okay"; + interrupt-names = "sys\0pmc\0msg\0legacy\0err"; + phys = <0xaf>; + num-viewport = <0x08>; + reg = <0x03 0xc0800000 0x00 0x400000 0x00 0xfe280000 0x00 0x10000>; + linux,pci-domain = <0x02>; + phandle = <0x19f>; + reset-names = "pipe"; + num-ib-windows = <0x06>; + + legacy-interrupt-controller { + #address-cells = <0x00>; + interrupts = <0x00 0xa2 0x01>; + interrupt-parent = <0x01>; + #interrupt-cells = <0x01>; + phandle = <0xb1>; + interrupt-controller; + }; + }; vcc2v5-ddr { regulator-max-microvolt = <0x2625a0>; @@ -3942,47 +3942,47 @@ reset-names = "can\0can-apb"; }; - // pcie@fe260000 { - // power-domains = <0x22 0x0f>; - // #address-cells = <0x03>; - // phy-names = "pcie-phy"; - // bus-range = <0x00 0x0f>; - // clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - // reg-names = "pcie-dbi\0pcie-apb"; - // num-ob-windows = <0x02>; - // resets = <0x20 0xa1>; - // interrupts = <0x00 0x4b 0x04 0x00 0x4a 0x04 0x00 0x49 0x04 0x00 0x48 0x04 0x00 0x47 0x04>; - // clocks = <0x20 0x81 0x20 0x82 0x20 0x83 0x20 0x84 0x20 0x85>; - // interrupt-map = <0x00 0x00 0x00 0x01 0xac 0x00 0x00 0x00 0x00 0x02 0xac 0x01 0x00 0x00 0x00 0x03 0xac 0x02 0x00 0x00 0x00 0x04 0xac 0x03>; - // #size-cells = <0x02>; - // max-link-speed = <0x02>; - // device_type = "pci"; - // interrupt-map-mask = <0x00 0x00 0x00 0x07>; - // reset-gpios = <0x83 0x11 0x00>; - // num-lanes = <0x01>; - // compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - // ranges = <0x800 0x00 0xf4000000 0x00 0xf4000000 0x00 0x100000 0x81000000 0x00 0xf4100000 0x00 0xf4100000 0x00 0x100000 0x82000000 0x00 0xf4200000 0x00 0xf4200000 0x00 0x1e00000 0xc3000000 0x03 0x00 0x03 0x00 0x00 0x40000000>; - // msi-map = <0x00 0xad 0x00 0x1000>; - // #interrupt-cells = <0x01>; - // status = "okay"; - // interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - // phys = <0x24 0x02>; - // num-viewport = <0x08>; - // reg = <0x03 0xc0000000 0x00 0x400000 0x00 0xfe260000 0x00 0x10000>; - // linux,pci-domain = <0x00>; - // phandle = <0x19d>; - // reset-names = "pipe"; - // num-ib-windows = <0x06>; - - // legacy-interrupt-controller { - // #address-cells = <0x00>; - // interrupts = <0x00 0x48 0x01>; - // interrupt-parent = <0x01>; - // #interrupt-cells = <0x01>; - // phandle = <0xac>; - // interrupt-controller; - // }; - // }; + pcie@fe260000 { + power-domains = <0x22 0x0f>; + #address-cells = <0x03>; + phy-names = "pcie-phy"; + bus-range = <0x00 0x0f>; + clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; + reg-names = "pcie-dbi\0pcie-apb"; + num-ob-windows = <0x02>; + resets = <0x20 0xa1>; + interrupts = <0x00 0x4b 0x04 0x00 0x4a 0x04 0x00 0x49 0x04 0x00 0x48 0x04 0x00 0x47 0x04>; + clocks = <0x20 0x81 0x20 0x82 0x20 0x83 0x20 0x84 0x20 0x85>; + interrupt-map = <0x00 0x00 0x00 0x01 0xac 0x00 0x00 0x00 0x00 0x02 0xac 0x01 0x00 0x00 0x00 0x03 0xac 0x02 0x00 0x00 0x00 0x04 0xac 0x03>; + #size-cells = <0x02>; + max-link-speed = <0x02>; + device_type = "pci"; + interrupt-map-mask = <0x00 0x00 0x00 0x07>; + reset-gpios = <0x83 0x11 0x00>; + num-lanes = <0x01>; + compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; + ranges = <0x800 0x00 0xf4000000 0x00 0xf4000000 0x00 0x100000 0x81000000 0x00 0xf4100000 0x00 0xf4100000 0x00 0x100000 0x82000000 0x00 0xf4200000 0x00 0xf4200000 0x00 0x1e00000 0xc3000000 0x03 0x00 0x03 0x00 0x00 0x40000000>; + msi-map = <0x00 0xad 0x00 0x1000>; + #interrupt-cells = <0x01>; + status = "okay"; + interrupt-names = "sys\0pmc\0msg\0legacy\0err"; + phys = <0x24 0x02>; + num-viewport = <0x08>; + reg = <0x03 0xc0000000 0x00 0x400000 0x00 0xfe260000 0x00 0x10000>; + linux,pci-domain = <0x00>; + phandle = <0x19d>; + reset-names = "pipe"; + num-ib-windows = <0x06>; + + legacy-interrupt-controller { + #address-cells = <0x00>; + interrupts = <0x00 0x48 0x01>; + interrupt-parent = <0x01>; + #interrupt-cells = <0x01>; + phandle = <0xac>; + interrupt-controller; + }; + }; i2c@fdd40000 { pinctrl-names = "default"; @@ -6104,49 +6104,49 @@ reset-names = "can\0can-apb"; }; - // pcie@fe270000 { - // power-domains = <0x22 0x0f>; - // vpcie3v3-supply = <0xb0>; - // #address-cells = <0x03>; - // phy-names = "pcie-phy"; - // bus-range = <0x10 0x1f>; - // clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - // reg-names = "pcie-dbi\0pcie-apb"; - // num-ob-windows = <0x02>; - // resets = <0x20 0xb1>; - // interrupts = <0x00 0xa0 0x04 0x00 0x9f 0x04 0x00 0x9e 0x04 0x00 0x9d 0x04 0x00 0x9c 0x04>; - // clocks = <0x20 0x88 0x20 0x89 0x20 0x8a 0x20 0x8b 0x20 0x8c>; - // interrupt-map = <0x00 0x00 0x00 0x01 0xae 0x00 0x00 0x00 0x00 0x02 0xae 0x01 0x00 0x00 0x00 0x03 0xae 0x02 0x00 0x00 0x00 0x04 0xae 0x03>; - // #size-cells = <0x02>; - // max-link-speed = <0x03>; - // device_type = "pci"; - // interrupt-map-mask = <0x00 0x00 0x00 0x07>; - // reset-gpios = <0x83 0x01 0x00>; - // num-lanes = <0x01>; - // compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - // ranges = <0x800 0x00 0xf2000000 0x00 0xf2000000 0x00 0x100000 0x81000000 0x00 0xf2100000 0x00 0xf2100000 0x00 0x100000 0x82000000 0x00 0xf2200000 0x00 0xf2200000 0x00 0x1e00000 0xc3000000 0x03 0x40000000 0x03 0x40000000 0x00 0x40000000>; - // msi-map = <0x1000 0xad 0x1000 0x1000>; - // #interrupt-cells = <0x01>; - // status = "okay"; - // rockchip,bifurcation; - // interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - // phys = <0xaf>; - // num-viewport = <0x08>; - // reg = <0x03 0xc0400000 0x00 0x400000 0x00 0xfe270000 0x00 0x10000>; - // linux,pci-domain = <0x01>; - // phandle = <0x19e>; - // reset-names = "pipe"; - // num-ib-windows = <0x06>; - - // legacy-interrupt-controller { - // #address-cells = <0x00>; - // interrupts = <0x00 0x9d 0x01>; - // interrupt-parent = <0x01>; - // #interrupt-cells = <0x01>; - // phandle = <0xae>; - // interrupt-controller; - // }; - // }; + pcie@fe270000 { + power-domains = <0x22 0x0f>; + vpcie3v3-supply = <0xb0>; + #address-cells = <0x03>; + phy-names = "pcie-phy"; + bus-range = <0x10 0x1f>; + clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; + reg-names = "pcie-dbi\0pcie-apb"; + num-ob-windows = <0x02>; + resets = <0x20 0xb1>; + interrupts = <0x00 0xa0 0x04 0x00 0x9f 0x04 0x00 0x9e 0x04 0x00 0x9d 0x04 0x00 0x9c 0x04>; + clocks = <0x20 0x88 0x20 0x89 0x20 0x8a 0x20 0x8b 0x20 0x8c>; + interrupt-map = <0x00 0x00 0x00 0x01 0xae 0x00 0x00 0x00 0x00 0x02 0xae 0x01 0x00 0x00 0x00 0x03 0xae 0x02 0x00 0x00 0x00 0x04 0xae 0x03>; + #size-cells = <0x02>; + max-link-speed = <0x03>; + device_type = "pci"; + interrupt-map-mask = <0x00 0x00 0x00 0x07>; + reset-gpios = <0x83 0x01 0x00>; + num-lanes = <0x01>; + compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; + ranges = <0x800 0x00 0xf2000000 0x00 0xf2000000 0x00 0x100000 0x81000000 0x00 0xf2100000 0x00 0xf2100000 0x00 0x100000 0x82000000 0x00 0xf2200000 0x00 0xf2200000 0x00 0x1e00000 0xc3000000 0x03 0x40000000 0x03 0x40000000 0x00 0x40000000>; + msi-map = <0x1000 0xad 0x1000 0x1000>; + #interrupt-cells = <0x01>; + status = "okay"; + rockchip,bifurcation; + interrupt-names = "sys\0pmc\0msg\0legacy\0err"; + phys = <0xaf>; + num-viewport = <0x08>; + reg = <0x03 0xc0400000 0x00 0x400000 0x00 0xfe270000 0x00 0x10000>; + linux,pci-domain = <0x01>; + phandle = <0x19e>; + reset-names = "pipe"; + num-ib-windows = <0x06>; + + legacy-interrupt-controller { + #address-cells = <0x00>; + interrupts = <0x00 0x9d 0x01>; + interrupt-parent = <0x01>; + #interrupt-cells = <0x01>; + phandle = <0xae>; + interrupt-controller; + }; + }; hvisor_virtio_device { compatible = "hvisor"; diff --git a/platform/aarch64/rk3568/image/dts/rk3568_limit_zone0.dts b/platform/aarch64/rk3568/image/dts/rk3568_limit_zone0.dts index d2d43a26..420426e8 100644 --- a/platform/aarch64/rk3568/image/dts/rk3568_limit_zone0.dts +++ b/platform/aarch64/rk3568/image/dts/rk3568_limit_zone0.dts @@ -13,6 +13,383 @@ serial2 = "/serial@fe660000"; sdmmc2 = "/dwmmc@fe000000"; serial4 = "/serial@fe680000"; + mmc1 = "/dwmmc@fe2b0000"; + mmc2 = "/dwmmc@fe2c0000"; + mmc3 = "/dwmmc@fe000000"; + }; + + syscon@fdc50000 { + compatible = "rockchip,rk3568-pipegrf\0syscon"; + reg = <0x00 0xfdc50000 0x00 0x1000>; + phandle = <0x10d>; + }; + + syscon@fdc90000 { + compatible = "rockchip,pipe-phy-grf\0syscon"; + reg = <0x00 0xfdc90000 0x00 0x1000>; + phandle = <0x110>; + }; + + phy@fe840000 { + rockchip,pipe-grf = <0x10d>; + clock-names = "refclk\0apbclk\0pipe_clk"; + assigned-clocks = <0x33 0x25>; + assigned-clock-rates = <0x5f5e100>; + resets = <0x20 0x1c8 0x20 0x1c9>; + clocks = <0x33 0x25 0x20 0x17e 0x20 0x7f>; + #phy-cells = <0x01>; + compatible = "rockchip,rk3568-naneng-combphy"; + status = "okay"; + rockchip,pipe-phy-grf = <0x110>; + reg = <0x00 0xfe840000 0x00 0x100>; + phandle = <0x24>; + reset-names = "combphy-apb\0combphy"; + }; + + power-management@fdd90000 { + compatible = "rockchip,rk3568-pmu\0syscon\0simple-mfd"; + reg = <0x00 0xfdd90000 0x00 0x1000>; + phandle = <0x177>; + + power-controller { + #address-cells = <0x01>; + #size-cells = <0x00>; + #power-domain-cells = <0x01>; + compatible = "rockchip,rk3568-power-controller"; + status = "okay"; + phandle = <0x22>; + + pd_pipe@15 { + clocks = <0x20 0x7f>; + reg = <0x0f>; + pm_qos = <0x5a 0x5b 0x5c 0x5d 0x5e 0x5f 0x60 0x61>; + }; + + pd_vi@8 { + clocks = <0x20 0xcc 0x20 0xcd>; + reg = <0x08>; + pm_qos = <0x49 0x4a 0x4b>; + }; + + pd_rkvenc@14 { + clocks = <0x20 0x102>; + reg = <0x0e>; + pm_qos = <0x57 0x58 0x59>; + }; + + pd_rga@10 { + clocks = <0x20 0xf1 0x20 0xf2>; + reg = <0x0a>; + pm_qos = <0x4f 0x50 0x51 0x52 0x53 0x54>; + }; + + pd_vpu@11 { + clocks = <0x20 0xed>; + reg = <0x0b>; + pm_qos = <0x55>; + }; + + pd_gpu@7 { + clocks = <0x20 0x19 0x20 0x1a>; + reg = <0x07>; + pm_qos = <0x48>; + }; + + pd_rkvdec@13 { + clocks = <0x20 0x107>; + reg = <0x0d>; + pm_qos = <0x56>; + }; + + pd_vo@9 { + clocks = <0x20 0xda 0x20 0xdb 0x20 0xdc>; + reg = <0x09>; + pm_qos = <0x4c 0x4d 0x4e>; + }; + + pd_npu@6 { + clocks = <0x20 0x27 0x20 0x25 0x20 0x26>; + reg = <0x06>; + pm_qos = <0x47>; + }; + }; + }; + + dc-12v { + regulator-max-microvolt = <0xb71b00>; + regulator-boot-on; + regulator-always-on; + regulator-min-microvolt = <0xb71b00>; + regulator-name = "dc_12v"; + compatible = "regulator-fixed"; + phandle = <0x131>; + }; + + vcc3v3_pcie2-regulator { + regulator-max-microvolt = <0x325aa0>; + regulator-boot-on; + gpio = <0x38 0x17 0x00>; + regulator-always-on; + enable-active-high; + regulator-min-microvolt = <0x325aa0>; + regulator-name = "vcc3v3_pcie2"; + startup-delay-us = <0x1388>; + compatible = "regulator-fixed"; + phandle = <0x1ea>; + vin-supply = <0x131>; + }; + + + // pcie@fe260000 { + // power-domains = <0x22 0x0f>; + // #address-cells = <0x03>; + // phy-names = "pcie-phy"; + // bus-range = <0x00 0x0f>; + // clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; + // reg-names = "pcie-dbi\0pcie-apb"; + // num-ob-windows = <0x02>; + // resets = <0x20 0xa1>; + // interrupts = <0x00 0x4b 0x04 0x00 0x4a 0x04 0x00 0x49 0x04 0x00 0x48 0x04 0x00 0x47 0x04>; + // clocks = <0x20 0x81 0x20 0x82 0x20 0x83 0x20 0x84 0x20 0x85>; + // interrupt-map = <0x00 0x00 0x00 0x01 0xac 0x00 0x00 0x00 0x00 0x02 0xac 0x01 0x00 0x00 0x00 0x03 0xac 0x02 0x00 0x00 0x00 0x04 0xac 0x03>; + // #size-cells = <0x02>; + // max-link-speed = <0x02>; + // device_type = "pci"; + // interrupt-map-mask = <0x00 0x00 0x00 0x07>; + // reset-gpios = <0x83 0x11 0x00>; + // num-lanes = <0x01>; + // compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; + // ranges = <0x800 0x00 0xf4000000 0x00 0xf4000000 0x00 0x100000 0x81000000 0x00 0xf4100000 0x00 0xf4100000 0x00 0x100000 0x82000000 0x00 0xf4200000 0x00 0xf4200000 0x00 0x1e00000 0xc3000000 0x03 0x00 0x03 0x00 0x00 0x40000000>; + // msi-map = <0x00 0xad 0x00 0x1000>; + // #interrupt-cells = <0x01>; + // status = "okay"; + // interrupt-names = "sys\0pmc\0msg\0legacy\0err"; + // phys = <0x24 0x02>; + // num-viewport = <0x08>; + // reg = <0x03 0xc0000000 0x00 0x400000 0x00 0xfe260000 0x00 0x10000>; + // linux,pci-domain = <0x00>; + // phandle = <0x19d>; + // reset-names = "pipe"; + // num-ib-windows = <0x06>; + + // legacy-interrupt-controller { + // #address-cells = <0x00>; + // interrupts = <0x00 0x48 0x01>; + // interrupt-parent = <0x01>; + // #interrupt-cells = <0x01>; + // phandle = <0xac>; + // interrupt-controller; + // }; + // }; + + syscon@fdcb8000 { + compatible = "rockchip,pcie30-phy-grf\0syscon"; + reg = <0x00 0xfdcb8000 0x00 0x10000>; + phandle = <0x116>; + }; + + phy@fe8c0000 { + clock-names = "refclk_m\0refclk_n\0pclk"; + resets = <0x20 0x1be>; + clocks = <0x33 0x26 0x33 0x27 0x20 0x177>; + #phy-cells = <0x00>; + compatible = "rockchip,rk3568-pcie3-phy"; + status = "okay"; + reg = <0x00 0xfe8c0000 0x00 0x20000>; + phandle = <0xaf>; + reset-names = "phy"; + rockchip,phy-grf = <0x116>; + }; + + vcc3v3_pcie-regulator { + regulator-max-microvolt = <0x325aa0>; + regulator-boot-on; + gpio = <0x38 0x1c 0x00>; + regulator-always-on; + enable-active-high; + regulator-min-microvolt = <0x325aa0>; + regulator-name = "vcc3v3_pcie"; + startup-delay-us = <0x3e8>; + compatible = "regulator-fixed"; + phandle = <0xb0>; + vin-supply = <0x131>; + }; + + pcie@fe270000 { + power-domains = <0x22 0x0f>; + vpcie3v3-supply = <0xb0>; + #address-cells = <0x03>; + phy-names = "pcie-phy"; + bus-range = <0x10 0x1f>; + clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; + reg-names = "pcie-dbi\0pcie-apb"; + num-ob-windows = <0x02>; + resets = <0x20 0xb1>; + interrupts = <0x00 0xa0 0x04 0x00 0x9f 0x04 0x00 0x9e 0x04 0x00 0x9d 0x04 0x00 0x9c 0x04>; + clocks = <0x20 0x88 0x20 0x89 0x20 0x8a 0x20 0x8b 0x20 0x8c>; + interrupt-map = <0x00 0x00 0x00 0x01 0xae 0x00 0x00 0x00 0x00 0x02 0xae 0x01 0x00 0x00 0x00 0x03 0xae 0x02 0x00 0x00 0x00 0x04 0xae 0x03>; + #size-cells = <0x02>; + max-link-speed = <0x03>; + device_type = "pci"; + interrupt-map-mask = <0x00 0x00 0x00 0x07>; + reset-gpios = <0x83 0x01 0x00>; + num-lanes = <0x01>; + compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; + ranges = <0x800 0x00 0xf2000000 0x00 0xf2000000 0x00 0x100000 0x81000000 0x00 0xf2100000 0x00 0xf2100000 0x00 0x100000 0x82000000 0x00 0xf2200000 0x00 0xf2200000 0x00 0x1e00000 0xc3000000 0x03 0x40000000 0x03 0x40000000 0x00 0x40000000>; + msi-map = <0x1000 0xad 0x1000 0x1000>; + #interrupt-cells = <0x01>; + status = "okay"; + rockchip,bifurcation; + interrupt-names = "sys\0pmc\0msg\0legacy\0err"; + phys = <0xaf>; + num-viewport = <0x08>; + reg = <0x03 0xc0400000 0x00 0x400000 0x00 0xfe270000 0x00 0x10000>; + linux,pci-domain = <0x01>; + phandle = <0x19e>; + reset-names = "pipe"; + num-ib-windows = <0x06>; + + legacy-interrupt-controller { + #address-cells = <0x00>; + interrupts = <0x00 0x9d 0x01>; + interrupt-parent = <0x01>; + #interrupt-cells = <0x01>; + phandle = <0xae>; + interrupt-controller; + }; + }; + + // pcie@fe280000 { + // power-domains = <0x22 0x0f>; + // vpcie3v3-supply = <0xb0>; + // #address-cells = <0x03>; + // phy-names = "pcie-phy"; + // bus-range = <0x20 0x2f>; + // clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; + // reg-names = "pcie-dbi\0pcie-apb"; + // num-ob-windows = <0x02>; + // resets = <0x20 0xc1>; + // interrupts = <0x00 0xa5 0x04 0x00 0xa4 0x04 0x00 0xa3 0x04 0x00 0xa2 0x04 0x00 0xa1 0x04>; + // clocks = <0x20 0x8f 0x20 0x90 0x20 0x91 0x20 0x92 0x20 0x93>; + // interrupt-map = <0x00 0x00 0x00 0x01 0xb1 0x00 0x00 0x00 0x00 0x02 0xb1 0x01 0x00 0x00 0x00 0x03 0xb1 0x02 0x00 0x00 0x00 0x04 0xb1 0x03>; + // #size-cells = <0x02>; + // max-link-speed = <0x03>; + // device_type = "pci"; + // interrupt-map-mask = <0x00 0x00 0x00 0x07>; + // reset-gpios = <0xb2 0x1e 0x00>; + // num-lanes = <0x02>; + // compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; + // ranges = <0x800 0x00 0xf0000000 0x00 0xf0000000 0x00 0x100000 0x81000000 0x00 0xf0100000 0x00 0xf0100000 0x00 0x100000 0x82000000 0x00 0xf0200000 0x00 0xf0200000 0x00 0x1e00000 0xc3000000 0x03 0x80000000 0x03 0x80000000 0x00 0x40000000>; + // msi-map = <0x2000 0xad 0x2000 0x1000>; + // #interrupt-cells = <0x01>; + // status = "okay"; + // interrupt-names = "sys\0pmc\0msg\0legacy\0err"; + // phys = <0xaf>; + // num-viewport = <0x08>; + // reg = <0x03 0xc0800000 0x00 0x400000 0x00 0xfe280000 0x00 0x10000>; + // linux,pci-domain = <0x02>; + // phandle = <0x19f>; + // reset-names = "pipe"; + // num-ib-windows = <0x06>; + + // legacy-interrupt-controller { + // #address-cells = <0x00>; + // interrupts = <0x00 0xa2 0x01>; + // interrupt-parent = <0x01>; + // #interrupt-cells = <0x01>; + // phandle = <0xb1>; + // interrupt-controller; + // }; + // }; + + sdhci@fe310000 { + clock-names = "core\0bus\0axi\0block\0timer"; + assigned-clocks = <0x20 0x7b 0x20 0x7d 0x20 0x7c>; + bus-width = <0x08>; + non-removable; + assigned-clock-rates = <0xbebc200 0x16e3600 0xbebc200>; + interrupts = <0x00 0x13 0x04>; + clocks = <0x20 0x7c 0x20 0x7a 0x20 0x79 0x20 0x7b 0x20 0x7d>; + compatible = "rockchip,dwcmshc-sdhci\0snps,dwcmshc-sdhci"; + status = "okay"; + reg = <0x00 0xfe310000 0x00 0x10000>; + phandle = <0x1a5>; + max-frequency = <0xbebc200>; + supports-emmc; + }; + + sdio-pwrseq { + pinctrl-names = "default"; + pinctrl-0 = <0x136>; + clock-names = "ext_clock"; + clocks = <0x135 0x01>; + reset-gpios = <0x83 0x1d 0x01>; + compatible = "mmc-pwrseq-simple"; + post-power-on-delay-ms = <0xc8>; + phandle = <0x9f>; + }; + + dwmmc@fe000000 { + fifo-depth = <0x100>; + pinctrl-names = "default"; + supports-sdio; + pinctrl-0 = <0xa0 0xa1 0xa2>; + clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; + cap-sd-highspeed; + bus-width = <0x04>; + non-removable; + resets = <0x20 0xeb>; + cap-sdio-irq; + interrupts = <0x00 0x64 0x04>; + clocks = <0x20 0xc1 0x20 0xc2 0x20 0x18e 0x20 0x18f>; + keep-power-in-suspend; + compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; + status = "okay"; + disable-wp; + mmc-pwrseq = <0x9f>; + reg = <0x00 0xfe000000 0x00 0x4000>; + phandle = <0x19a>; + sd-uhs-sdr104; + max-frequency = <0x8f0d180>; + reset-names = "reset"; + }; + + dwmmc@fe2b0000 { + supports-sd; + fifo-depth = <0x100>; + pinctrl-names = "default"; + pinctrl-0 = <0xbe 0xbf 0xc0 0xc1>; + clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; + cap-sd-highspeed; + vqmmc-supply = <0x2d>; + bus-width = <0x04>; + resets = <0x20 0xd4>; + interrupts = <0x00 0x62 0x04>; + clocks = <0x20 0xb0 0x20 0xb1 0x20 0x18a 0x20 0x18b>; + vmmc-supply = <0xbd>; + compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; + status = "okay"; + disable-wp; + reg = <0x00 0xfe2b0000 0x00 0x4000>; + phandle = <0x1a2>; + sd-uhs-sdr104; + max-frequency = <0x8f0d180>; + cap-mmc-highspeed; + reset-names = "reset"; + }; + + dwmmc@fe2c0000 { + fifo-depth = <0x100>; + clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; + resets = <0x20 0xd6>; + interrupts = <0x00 0x63 0x04>; + clocks = <0x20 0xb2 0x20 0xb3 0x20 0x18c 0x20 0x18d>; + compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; + status = "disabled"; + reg = <0x00 0xfe2c0000 0x00 0x4000>; + phandle = <0x1a3>; + max-frequency = <0x8f0d180>; + reset-names = "reset"; }; memory { @@ -41,10 +418,10 @@ ftrace-size = <0x00>; }; - hvisor@480000 { - no-map; - reg = <0x00 0x480000 0x00 0x400000>; - }; + // hvisor@0x2000000 { + // no-map; + // reg = <0x00 0x2000000 0x00 0x1000000>; + // }; nonroot { no-map; @@ -1165,47 +1542,6 @@ phandle = <0x34>; }; - sdhci@fe310000 { - clock-names = "core\0bus\0axi\0block\0timer"; - assigned-clocks = <0x20 0x7b 0x20 0x7d 0x20 0x7c>; - bus-width = <0x08>; - non-removable; - assigned-clock-rates = <0xbebc200 0x16e3600 0xbebc200>; - interrupts = <0x00 0x13 0x04>; - clocks = <0x20 0x7c 0x20 0x7a 0x20 0x79 0x20 0x7b 0x20 0x7d>; - compatible = "rockchip,dwcmshc-sdhci\0snps,dwcmshc-sdhci"; - status = "okay"; - reg = <0x00 0xfe310000 0x00 0x10000>; - phandle = <0x1a5>; - max-frequency = <0xbebc200>; - supports-emmc; - }; - - dwmmc@fe000000 { - fifo-depth = <0x100>; - pinctrl-names = "default"; - supports-sdio; - pinctrl-0 = <0xa0 0xa1 0xa2>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - cap-sd-highspeed; - bus-width = <0x04>; - non-removable; - resets = <0x20 0xeb>; - cap-sdio-irq; - interrupts = <0x00 0x64 0x04>; - clocks = <0x20 0xc1 0x20 0xc2 0x20 0x18e 0x20 0x18f>; - keep-power-in-suspend; - compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; - status = "okay"; - disable-wp; - mmc-pwrseq = <0x9f>; - reg = <0x00 0xfe000000 0x00 0x4000>; - phandle = <0x19a>; - sd-uhs-sdr104; - max-frequency = <0x8f0d180>; - reset-names = "reset"; - }; - scmi-shmem@10f000 { compatible = "arm,scmi-shmem"; reg = <0x00 0x10f000 0x00 0x100>; @@ -1305,7 +1641,9 @@ }; chosen { - bootargs = "storagemedia=emmc rw rootwait earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0 root=PARTUUID=614e0000-0000"; + // bootargs = "storagemedia=emmc rw rootwait earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0 root=PARTUUID=root=PARTUUID=614e0000-0000-4b53-8000-1d28000054a9"; + // bootargs = "storagemedia=emmc rw rootwait earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0 root=/dev/mmcblk0p5 systemd.mask=NetworkManager.service"; + bootargs = "storagemedia=emmc rw rootwait earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0 root=/dev/mmcblk0p5"; phandle = <0x1eb>; }; diff --git a/platform/aarch64/rk3568/image/dts/rk3568_limit_zone1.dts b/platform/aarch64/rk3568/image/dts/rk3568_limit_zone1.dts index 8775c052..97e392ab 100644 --- a/platform/aarch64/rk3568/image/dts/rk3568_limit_zone1.dts +++ b/platform/aarch64/rk3568/image/dts/rk3568_limit_zone1.dts @@ -25,9 +25,9 @@ ranges; phandle = <0x142>; - hvisor@480000 { + hvisor@280000 { no-map; - reg = <0x00 0x480000 0x00 0x400000>; + reg = <0x00 0x280000 0x00 0x400000>; }; }; diff --git a/platform/aarch64/rk3568/linker.ld b/platform/aarch64/rk3568/linker.ld index db030e7d..26e135cd 100644 --- a/platform/aarch64/rk3568/linker.ld +++ b/platform/aarch64/rk3568/linker.ld @@ -1,5 +1,5 @@ ENTRY(arch_entry) -BASE_ADDRESS = 0x00480000; +BASE_ADDRESS = 0x60080000; SECTIONS { diff --git a/platform/aarch64/rk3568/platform.mk b/platform/aarch64/rk3568/platform.mk index 51824073..7bf0b004 100644 --- a/platform/aarch64/rk3568/platform.mk +++ b/platform/aarch64/rk3568/platform.mk @@ -3,6 +3,6 @@ $(hvisor_bin): elf sudo apt update && sudo apt install u-boot-tools; \ fi && \ $(OBJCOPY) $(hvisor_elf) --strip-all -O binary $(hvisor_bin).tmp && \ - mkimage -n hvisor_img -A arm64 -O linux -C none -T kernel -a 0x00480000 \ - -e 0x00480000 -d $(hvisor_bin).tmp $(hvisor_bin) && \ + mkimage -n hvisor_img -A arm64 -O linux -C none -T kernel -a 0x60080000 \ + -e 0x60080000 -d $(hvisor_bin).tmp $(hvisor_bin) && \ rm -rf $(hvisor_bin).tmp \ No newline at end of file diff --git a/platform/aarch64/rk3588/board.rs b/platform/aarch64/rk3588/board.rs index 8ac239c3..afc6acff 100644 --- a/platform/aarch64/rk3588/board.rs +++ b/platform/aarch64/rk3588/board.rs @@ -19,8 +19,11 @@ use crate::{ zone::{GicConfig, Gicv3Config, HvArchZoneConfig}, }, config::*, + pci::vpci_dev::VpciDevType, }; +use crate::pci_dev; + // [ 17.796762] node 0: [mem 0x0000000000200000-0x000000000047ffff] // [ 17.797335] node 0: [mem 0x0000000000480000-0x000000000087ffff] // [ 17.797907] node 0: [mem 0x0000000000880000-0x00000000083fffff] @@ -188,6 +191,8 @@ pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { }; pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { + bus_range_begin: 0x0, + bus_range_end: 0x1f, ecam_base: 0x4010000000, ecam_size: 0x10000000, io_base: 0x3eff0000, @@ -199,8 +204,12 @@ pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { mem64_base: 0x8000000000, mem64_size: 0x8000000000, pci_mem64_base: 0x8000000000, + domain: 0x0, }; pub const ROOT_ZONE_IVC_CONFIG: [HvIvcConfig; 0] = []; -pub const ROOT_PCI_DEVS: [u64; 2] = [0, 1 << 3]; +pub const ROOT_PCI_DEVS: [HvPciDevConfig; 2] = [ + pci_dev!(0x0, 0x0, 0x0, 0x0, VpciDevType::Physical), + pci_dev!(0x0, 0x0, 0x1, 0x0, VpciDevType::Physical), +]; diff --git a/platform/loongarch64/ls3a5000/board.rs b/platform/loongarch64/ls3a5000/board.rs index 0ac53198..0d59ecae 100644 --- a/platform/loongarch64/ls3a5000/board.rs +++ b/platform/loongarch64/ls3a5000/board.rs @@ -14,7 +14,8 @@ // Authors: // Yulong Han // -use crate::{arch::zone::HvArchZoneConfig, config::*}; +use crate::{arch::zone::HvArchZoneConfig, config::*, pci::vpci_dev::VpciDevType}; +use crate::pci_dev; pub const BOARD_NAME: &str = "ls3a5000"; @@ -109,18 +110,18 @@ pub const ROOT_ZONE_MEMORY_REGIONS: &[HvConfigMemoryRegion] = &[ // virtual_start: 0xfe_0000_0000, // size: 0x20000000, // }, // pci config space (HT) - // HvConfigMemoryRegion { - // mem_type: MEM_TYPE_IO, - // physical_start: 0x18408000, - // virtual_start: 0x18408000, - // size: 0x00008000, - // }, // pci io resource - // HvConfigMemoryRegion { - // mem_type: MEM_TYPE_IO, - // physical_start: 0x60000000, - // virtual_start: 0x60000000, - // size: 0x20000000, - // }, // pci mem resource + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x18408000, + virtual_start: 0x18408000, + size: 0x00008000, + }, // pci io resource + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x60000000, + virtual_start: 0x60000000, + size: 0x20000000, + }, // pci mem resource HvConfigMemoryRegion { mem_type: MEM_TYPE_IO, physical_start: 0x1001_0000, @@ -154,19 +155,24 @@ pub const ROOT_ZONE_IRQS_BITMAP: &[BitmapWord] = &get_irqs_bitmap(&[]); pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { dummy: 0 }; pub const ROOT_ZONE_IVC_CONFIG: [HvIvcConfig; 0] = []; -pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { - ecam_base: 0xfe00000000, - ecam_size: 0x20000000, - io_base: 0x18408000, - io_size: 0x8000, - pci_io_base: 0x00008000, - mem32_base: 0x0, - mem32_size: 0x0, - pci_mem32_base: 0x0, - mem64_base: 0x60000000, - mem64_size: 0x20000000, - pci_mem64_base: 0x60000000, -}; +pub const ROOT_PCI_CONFIG: [HvPciConfig; 1] = [ + HvPciConfig { + ecam_base: 0xfe00000000, + ecam_size: 0x20000000, + io_base: 0x18408000, + io_size: 0x8000, + pci_io_base: 0x00008000, + mem32_base: 0x0, + mem32_size: 0x0, + pci_mem32_base: 0x0, + mem64_base: 0x60000000, + mem64_size: 0x30000000, + pci_mem64_base: 0x60000000, + bus_range_begin: 0, + bus_range_end: 0xff, + domain: 0x0, + } +]; /* 00:00.0, 00:00.1, 00:00.2, 00:00.3, 00:04.0, 00:04.1*/ /* 00:05.0, 00:05.1, 00:06.0, 00:06.1, 00:06.2 */ @@ -177,38 +183,37 @@ pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { /* 08:00.0, 08:00.1, 08:00.2, 08:00.3 net */ /* BUS 6 on X4 slot */ /* 06:00.0, 06:00.1, 06:00.2, 06:00.3 net */ -pub const ROOT_PCI_DEVS: [u64; 26] = [ - 0, - 1, - 2, - 3, - 4 << 3, - (4 << 3) + 1, - 5 << 3, - (5 << 3) + 1, - // 00:06.xx is VGA and Graphics card - (6 << 3), - (6 << 3) + 1, - (6 << 3) + 2, - 7 << 3, - 8 << 3, // bus 0 device 8: AHCI - 9 << 3, - 0xa << 3, - 0xb << 3, - 0xc << 3, - 0xd << 3, - 0xf << 3, - 0x10 << 3, - 0x13 << 3, - 0x16 << 3, - 0x19 << 3, - 2 << 8, - 5 << 8, - // bus 6 (x4 slot) is PCIe network card - // (8 << 8), // bus 8 net - (6 << 8), // bus 6 net +pub const ROOT_PCI_DEVS: &[HvPciDevConfig] = &[ + pci_dev!(0x0, 0x0, 0x0, 0x0, VpciDevType::Physical), // 00:00.0 + pci_dev!(0x0, 0x0, 0x0, 0x1, VpciDevType::Physical), // 00:00.1 + pci_dev!(0x0, 0x0, 0x0, 0x2, VpciDevType::Physical), // 00:00.2 + pci_dev!(0x0, 0x0, 0x0, 0x3, VpciDevType::Physical), // 00:00.3 + pci_dev!(0x0, 0x0, 0x4, 0x0, VpciDevType::Physical), // 00:04.0 + pci_dev!(0x0, 0x0, 0x4, 0x1, VpciDevType::Physical), // 00:04.1 + pci_dev!(0x0, 0x0, 0x5, 0x0, VpciDevType::Physical), // 00:05.0 + pci_dev!(0x0, 0x0, 0x5, 0x1, VpciDevType::Physical), // 00:05.1 + // pci_dev!(0x0, 0x0, 0x6, 0x0, VpciDevType::Physical), // 00:06.0 + // pci_dev!(0x0, 0x0, 0x6, 0x1, VpciDevType::Physical), // 00:06.1 + // pci_dev!(0x0, 0x0, 0x6, 0x2, VpciDevType::Physical), // 00:06.2 + pci_dev!(0x0, 0x0, 0x7, 0x0, VpciDevType::Physical), // 00:07.0 + pci_dev!(0x0, 0x0, 0x8, 0x0, VpciDevType::Physical), // 00:08.0 + pci_dev!(0x0, 0x0, 0x9, 0x0, VpciDevType::Physical), // 00:09.0 + pci_dev!(0x0, 0x0, 0xa, 0x0, VpciDevType::Physical), // 00:0a.0 + pci_dev!(0x0, 0x0, 0xb, 0x0, VpciDevType::Physical), // 00:0b.0 + pci_dev!(0x0, 0x0, 0xc, 0x0, VpciDevType::Physical), // 00:0c.0 + pci_dev!(0x0, 0x0, 0xd, 0x0, VpciDevType::Physical), // 00:0d.0 + pci_dev!(0x0, 0x0, 0xf, 0x0, VpciDevType::Physical), // 00:0f.0 + pci_dev!(0x0, 0x0, 0x10, 0x0, VpciDevType::Physical), // 00:10.0 + pci_dev!(0x0, 0x0, 0x13, 0x0, VpciDevType::Physical), // 00:13.0 + pci_dev!(0x0, 0x0, 0x16, 0x0, VpciDevType::Physical), // 00:16.0 + pci_dev!(0x0, 0x0, 0x19, 0x0, VpciDevType::Physical), // 00:19.0 + pci_dev!(0x0, 0x2, 0x0, 0x0, VpciDevType::Physical), // 02:00.0 + pci_dev!(0x0, 0x5, 0x0, 0x0, VpciDevType::Physical), // 05:00.0 + pci_dev!(0x0, 0x6, 0x0, 0x0, VpciDevType::Physical), // 06:00.0 ]; + + // bus << 8 | dev << 5 | func << 3 // pub const ROOT_PCI_DEVS: [u64; 0] = []; diff --git a/platform/loongarch64/ls3a5000/cargo/features b/platform/loongarch64/ls3a5000/cargo/features index a4727de5..442505f4 100644 --- a/platform/loongarch64/ls3a5000/cargo/features +++ b/platform/loongarch64/ls3a5000/cargo/features @@ -1,4 +1,7 @@ loongson_3a5000 loongson_7a2000 loongson_uart -pci \ No newline at end of file + +pci +loongarch64_pcie +no_pcie_bar_realloc \ No newline at end of file diff --git a/platform/loongarch64/ls3a5000/configs/zone1-linux.json b/platform/loongarch64/ls3a5000/configs/zone1-linux.json index e56d3424..995d42ab 100644 --- a/platform/loongarch64/ls3a5000/configs/zone1-linux.json +++ b/platform/loongarch64/ls3a5000/configs/zone1-linux.json @@ -111,7 +111,7 @@ "arch_config": { "dummy": "0x1234" }, - "pci_config": { + "pci_config": [{ "ecam_base": "0xfe00000000", "ecam_size": "0x20000000", "io_base": "0x18408000", @@ -123,7 +123,13 @@ "mem64_base": "0x60000000", "mem64_size": "0x20000000", "pci_mem64_base": "0x60000000" - }, + }], "num_pci_devs": 1, - "alloc_pci_devs": [1537] + "alloc_pci_devs": [{ + "domain": "0x0", + "bus": "0x6", + "device": "0x1", + "function": "0x0", + "dev_type": "0" + }] } \ No newline at end of file diff --git a/platform/loongarch64/ls3a5000/configs/zone2-linux.json b/platform/loongarch64/ls3a5000/configs/zone2-linux.json index 8255f01e..ef26e27e 100644 --- a/platform/loongarch64/ls3a5000/configs/zone2-linux.json +++ b/platform/loongarch64/ls3a5000/configs/zone2-linux.json @@ -99,7 +99,7 @@ "arch_config": { "dummy": "0x1234" }, - "pci_config": { + "pci_config": [{ "ecam_base": "0xfe00000000", "ecam_size": "0x20000000", "io_base": "0x18408000", @@ -111,7 +111,13 @@ "mem64_base": "0x60000000", "mem64_size": "0x20000000", "pci_mem64_base": "0x60000000" - }, + }], "num_pci_devs": 1, - "alloc_pci_devs": [1538] + "alloc_pci_devs": [{ + "domain": "0x0", + "bus": "0x6", + "device": "0x2", + "function": "0x0", + "dev_type": "0" + }] } \ No newline at end of file diff --git a/platform/loongarch64/ls3a5000/configs/zone3-linux.json b/platform/loongarch64/ls3a5000/configs/zone3-linux.json index fcb03239..a90f3b2e 100644 --- a/platform/loongarch64/ls3a5000/configs/zone3-linux.json +++ b/platform/loongarch64/ls3a5000/configs/zone3-linux.json @@ -99,7 +99,7 @@ "arch_config": { "dummy": "0x1234" }, - "pci_config": { + "pci_config": [{ "ecam_base": "0xfe00000000", "ecam_size": "0x20000000", "io_base": "0x18408000", @@ -111,7 +111,13 @@ "mem64_base": "0x60000000", "mem64_size": "0x20000000", "pci_mem64_base": "0x60000000" - }, + }], "num_pci_devs": 1, - "alloc_pci_devs": [1539] + "alloc_pci_devs": [{ + "domain": "0x0", + "bus": "0x6", + "device": "0x3", + "function": "0x0", + "dev_type": "0" + }] } \ No newline at end of file diff --git a/platform/loongarch64/ls3a6000/board.rs b/platform/loongarch64/ls3a6000/board.rs index 0ac53198..f78cfd1b 100644 --- a/platform/loongarch64/ls3a6000/board.rs +++ b/platform/loongarch64/ls3a6000/board.rs @@ -14,7 +14,8 @@ // Authors: // Yulong Han // -use crate::{arch::zone::HvArchZoneConfig, config::*}; +use crate::{arch::zone::HvArchZoneConfig, config::*, pci::vpci_dev::VpciDevType}; +use crate::pci_dev; pub const BOARD_NAME: &str = "ls3a5000"; @@ -154,19 +155,24 @@ pub const ROOT_ZONE_IRQS_BITMAP: &[BitmapWord] = &get_irqs_bitmap(&[]); pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { dummy: 0 }; pub const ROOT_ZONE_IVC_CONFIG: [HvIvcConfig; 0] = []; -pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { - ecam_base: 0xfe00000000, - ecam_size: 0x20000000, - io_base: 0x18408000, - io_size: 0x8000, - pci_io_base: 0x00008000, - mem32_base: 0x0, - mem32_size: 0x0, - pci_mem32_base: 0x0, - mem64_base: 0x60000000, - mem64_size: 0x20000000, - pci_mem64_base: 0x60000000, -}; +pub const ROOT_PCI_CONFIG: [HvPciConfig; 1] = [ + HvPciConfig { + bus_range_begin: 0x0, + bus_range_end: 0x1f, + ecam_base: 0xfe00000000, + ecam_size: 0x20000000, + io_base: 0x18408000, + io_size: 0x8000, + pci_io_base: 0x00008000, + mem32_base: 0x0, + mem32_size: 0x0, + pci_mem32_base: 0x0, + mem64_base: 0x60000000, + mem64_size: 0x20000000, + pci_mem64_base: 0x60000000, + domain: 0x0, + } +]; /* 00:00.0, 00:00.1, 00:00.2, 00:00.3, 00:04.0, 00:04.1*/ /* 00:05.0, 00:05.1, 00:06.0, 00:06.1, 00:06.2 */ @@ -177,38 +183,36 @@ pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { /* 08:00.0, 08:00.1, 08:00.2, 08:00.3 net */ /* BUS 6 on X4 slot */ /* 06:00.0, 06:00.1, 06:00.2, 06:00.3 net */ -pub const ROOT_PCI_DEVS: [u64; 26] = [ - 0, - 1, - 2, - 3, - 4 << 3, - (4 << 3) + 1, - 5 << 3, - (5 << 3) + 1, - // 00:06.xx is VGA and Graphics card - (6 << 3), - (6 << 3) + 1, - (6 << 3) + 2, - 7 << 3, - 8 << 3, // bus 0 device 8: AHCI - 9 << 3, - 0xa << 3, - 0xb << 3, - 0xc << 3, - 0xd << 3, - 0xf << 3, - 0x10 << 3, - 0x13 << 3, - 0x16 << 3, - 0x19 << 3, - 2 << 8, - 5 << 8, - // bus 6 (x4 slot) is PCIe network card - // (8 << 8), // bus 8 net - (6 << 8), // bus 6 net +pub const ROOT_PCI_DEVS: [HvPciDevConfig; 26] = [ + pci_dev!(0x0, 0x0, 0x0, 0x0, VpciDevType::Physical), // 00:00.0 + pci_dev!(0x0, 0x0, 0x0, 0x1, VpciDevType::Physical), // 00:00.1 + pci_dev!(0x0, 0x0, 0x0, 0x2, VpciDevType::Physical), // 00:00.2 + pci_dev!(0x0, 0x0, 0x0, 0x3, VpciDevType::Physical), // 00:00.3 + pci_dev!(0x0, 0x0, 0x4, 0x0, VpciDevType::Physical), // 00:04.0 + pci_dev!(0x0, 0x0, 0x4, 0x1, VpciDevType::Physical), // 00:04.1 + pci_dev!(0x0, 0x0, 0x5, 0x0, VpciDevType::Physical), // 00:05.0 + pci_dev!(0x0, 0x0, 0x5, 0x1, VpciDevType::Physical), // 00:05.1 + pci_dev!(0x0, 0x0, 0x6, 0x0, VpciDevType::Physical), // 00:06.0 + pci_dev!(0x0, 0x0, 0x6, 0x1, VpciDevType::Physical), // 00:06.1 + pci_dev!(0x0, 0x0, 0x6, 0x2, VpciDevType::Physical), // 00:06.2 + pci_dev!(0x0, 0x0, 0x7, 0x0, VpciDevType::Physical), // 00:07.0 + pci_dev!(0x0, 0x0, 0x8, 0x0, VpciDevType::Physical), // 00:08.0 + pci_dev!(0x0, 0x0, 0x9, 0x0, VpciDevType::Physical), // 00:09.0 + pci_dev!(0x0, 0x0, 0xa, 0x0, VpciDevType::Physical), // 00:0a.0 + pci_dev!(0x0, 0x0, 0xb, 0x0, VpciDevType::Physical), // 00:0b.0 + pci_dev!(0x0, 0x0, 0xc, 0x0, VpciDevType::Physical), // 00:0c.0 + pci_dev!(0x0, 0x0, 0xd, 0x0, VpciDevType::Physical), // 00:0d.0 + pci_dev!(0x0, 0x0, 0xf, 0x0, VpciDevType::Physical), // 00:0f.0 + pci_dev!(0x0, 0x0, 0x10, 0x0, VpciDevType::Physical), // 00:10.0 + pci_dev!(0x0, 0x0, 0x13, 0x0, VpciDevType::Physical), // 00:13.0 + pci_dev!(0x0, 0x0, 0x16, 0x0, VpciDevType::Physical), // 00:16.0 + pci_dev!(0x0, 0x0, 0x19, 0x0, VpciDevType::Physical), // 00:19.0 + pci_dev!(0x0, 0x2, 0x0, 0x0, VpciDevType::Physical), // 02:00.0 + pci_dev!(0x0, 0x5, 0x0, 0x0, VpciDevType::Physical), // 05:00.0 + pci_dev!(0x0, 0x6, 0x0, 0x0, VpciDevType::Physical), // 06:00.0 ]; + // bus << 8 | dev << 5 | func << 3 // pub const ROOT_PCI_DEVS: [u64; 0] = []; diff --git a/platform/loongarch64/ls3a6000/cargo/features b/platform/loongarch64/ls3a6000/cargo/features index e2e853ee..86cd7a5c 100644 --- a/platform/loongarch64/ls3a6000/cargo/features +++ b/platform/loongarch64/ls3a6000/cargo/features @@ -1,4 +1,7 @@ loongson_3a6000 loongson_7a2000 loongson_uart -pci \ No newline at end of file + +pci +loongarch64_pcie +no_pcie_bar_realloc \ No newline at end of file diff --git a/platform/loongarch64/ls3a6000/configs/zone1-linux.json b/platform/loongarch64/ls3a6000/configs/zone1-linux.json index e56d3424..995d42ab 100644 --- a/platform/loongarch64/ls3a6000/configs/zone1-linux.json +++ b/platform/loongarch64/ls3a6000/configs/zone1-linux.json @@ -111,7 +111,7 @@ "arch_config": { "dummy": "0x1234" }, - "pci_config": { + "pci_config": [{ "ecam_base": "0xfe00000000", "ecam_size": "0x20000000", "io_base": "0x18408000", @@ -123,7 +123,13 @@ "mem64_base": "0x60000000", "mem64_size": "0x20000000", "pci_mem64_base": "0x60000000" - }, + }], "num_pci_devs": 1, - "alloc_pci_devs": [1537] + "alloc_pci_devs": [{ + "domain": "0x0", + "bus": "0x6", + "device": "0x1", + "function": "0x0", + "dev_type": "0" + }] } \ No newline at end of file diff --git a/platform/loongarch64/ls3a6000/configs/zone2-linux.json b/platform/loongarch64/ls3a6000/configs/zone2-linux.json index 8255f01e..ef26e27e 100644 --- a/platform/loongarch64/ls3a6000/configs/zone2-linux.json +++ b/platform/loongarch64/ls3a6000/configs/zone2-linux.json @@ -99,7 +99,7 @@ "arch_config": { "dummy": "0x1234" }, - "pci_config": { + "pci_config": [{ "ecam_base": "0xfe00000000", "ecam_size": "0x20000000", "io_base": "0x18408000", @@ -111,7 +111,13 @@ "mem64_base": "0x60000000", "mem64_size": "0x20000000", "pci_mem64_base": "0x60000000" - }, + }], "num_pci_devs": 1, - "alloc_pci_devs": [1538] + "alloc_pci_devs": [{ + "domain": "0x0", + "bus": "0x6", + "device": "0x2", + "function": "0x0", + "dev_type": "0" + }] } \ No newline at end of file diff --git a/platform/loongarch64/ls3a6000/configs/zone3-linux.json b/platform/loongarch64/ls3a6000/configs/zone3-linux.json index fcb03239..a90f3b2e 100644 --- a/platform/loongarch64/ls3a6000/configs/zone3-linux.json +++ b/platform/loongarch64/ls3a6000/configs/zone3-linux.json @@ -99,7 +99,7 @@ "arch_config": { "dummy": "0x1234" }, - "pci_config": { + "pci_config": [{ "ecam_base": "0xfe00000000", "ecam_size": "0x20000000", "io_base": "0x18408000", @@ -111,7 +111,13 @@ "mem64_base": "0x60000000", "mem64_size": "0x20000000", "pci_mem64_base": "0x60000000" - }, + }], "num_pci_devs": 1, - "alloc_pci_devs": [1539] + "alloc_pci_devs": [{ + "domain": "0x0", + "bus": "0x6", + "device": "0x3", + "function": "0x0", + "dev_type": "0" + }] } \ No newline at end of file diff --git a/platform/riscv64/qemu-aia/configs/zone1-linux.json b/platform/riscv64/qemu-aia/configs/zone1-linux.json index 7dd2e0b8..c5d90065 100644 --- a/platform/riscv64/qemu-aia/configs/zone1-linux.json +++ b/platform/riscv64/qemu-aia/configs/zone1-linux.json @@ -36,7 +36,7 @@ "aplic_base": "0xd000000", "aplic_size": "0x8000" }, - "pci_config": { + "pci_config": [{ "ecam_base": "0x30000000", "ecam_size": "0x10000000", "io_base": "0x3000000", @@ -48,7 +48,16 @@ "mem64_base": "0x400000000", "mem64_size": "0x400000000", "pci_mem64_base": "0x400000000" - }, + }], "num_pci_devs": 2, - "alloc_pci_devs": [0, 16] + "alloc_pci_devs": [ + { + "bdf": "0x0", + "vbdf": "0x0" + }, + { + "bdf": "0x10", + "vbdf": "0x10" + } + ] } \ No newline at end of file diff --git a/platform/riscv64/qemu-plic/configs/zone1-linux-io.json b/platform/riscv64/qemu-plic/configs/zone1-linux-io.json index 3ca63476..710fec27 100644 --- a/platform/riscv64/qemu-plic/configs/zone1-linux-io.json +++ b/platform/riscv64/qemu-plic/configs/zone1-linux-io.json @@ -36,7 +36,7 @@ "aplic_base": "0xd000000", "aplic_size": "0x8000" }, - "pci_config": { + "pci_config": [{ "ecam_base": "0x30000000", "ecam_size": "0x10000000", "io_base": "0x3000000", @@ -48,7 +48,16 @@ "mem64_base": "0x400000000", "mem64_size": "0x400000000", "pci_mem64_base": "0x400000000" - }, + }], "num_pci_devs": 2, - "alloc_pci_devs": [0, 16] + "alloc_pci_devs": [ + { + "bdf": "0x0", + "vbdf": "0x0" + }, + { + "bdf": "0x10", + "vbdf": "0x10" + } + ] } \ No newline at end of file diff --git a/platform/riscv64/qemu-plic/configs/zone1-linux.json b/platform/riscv64/qemu-plic/configs/zone1-linux.json index 798e7be2..b3b77ac1 100644 --- a/platform/riscv64/qemu-plic/configs/zone1-linux.json +++ b/platform/riscv64/qemu-plic/configs/zone1-linux.json @@ -36,7 +36,7 @@ "aplic_base": "0xd000000", "aplic_size": "0x8000" }, - "pci_config": { + "pci_config": [{ "ecam_base": "0x30000000", "ecam_size": "0x10000000", "io_base": "0x3000000", @@ -48,7 +48,16 @@ "mem64_base": "0x400000000", "mem64_size": "0x400000000", "pci_mem64_base": "0x400000000" - }, + }], "num_pci_devs": 2, - "alloc_pci_devs": [0, 16] + "alloc_pci_devs": [ + { + "bdf": "0x0", + "vbdf": "0x0" + }, + { + "bdf": "0x10", + "vbdf": "0x10" + } + ] } \ No newline at end of file diff --git a/platform/x86_64/nuc14mnk/board.rs b/platform/x86_64/nuc14mnk/board.rs index cad0456c..c865b506 100644 --- a/platform/x86_64/nuc14mnk/board.rs +++ b/platform/x86_64/nuc14mnk/board.rs @@ -13,7 +13,8 @@ // // Authors: // -use crate::{arch::zone::HvArchZoneConfig, config::*, memory::GuestPhysAddr}; +use crate::pci_dev; +use crate::{arch::zone::HvArchZoneConfig, config::*, memory::GuestPhysAddr, pci::vpci_dev::VpciDevType}; pub const MEM_TYPE_RESERVED: u32 = 5; @@ -144,7 +145,7 @@ pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { screen_base: ROOT_ZONE_SCREEN_BASE_ADDR, }; -pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { +pub const ROOT_PCI_CONFIG: [HvPciConfig; 1] = [HvPciConfig { ecam_base: 0xc0000000, ecam_size: 0x300000, io_base: 0x0, @@ -156,11 +157,29 @@ pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { mem64_base: 0x0, mem64_size: 0x0, pci_mem64_base: 0x0, -}; +}]; -pub const ROOT_PCI_DEVS: [u64; 19] = [ - 0x0, 0x10, 0x20, 0x40, 0x50, 0x68, 0x90, 0xa0, 0xa2, 0xa3, 0xb0, 0xe0, 0xe8, 0xf8, 0xfb, 0xfc, - 0xfd, 0x100, 0x200, +pub const ROOT_PCI_MAX_BUS: usize = 2; +pub const ROOT_PCI_DEVS: [HvPciDevConfig; 18] = [ + pci_dev!(0x0, 0x0, 0x0, VpciDevType::Physical), // host bridge + pci_dev!(0x0, 0x2, 0x0, VpciDevType::Physical), // VGA controller + pci_dev!(0x0, 0x4, 0x0, VpciDevType::Physical), + pci_dev!(0x0, 0x8, 0x0, VpciDevType::Physical), + pci_dev!(0x0, 0xa, 0x0, VpciDevType::Physical), + // pci_dev!(0x0, 0xd, 0x0), // USB controller + pci_dev!(0x0, 0x12, 0x0, VpciDevType::Physical), // serial controller + pci_dev!(0x0, 0x14, 0x0, VpciDevType::Physical), // USB controller + pci_dev!(0x0, 0x14, 0x2, VpciDevType::Physical), // RAM memory + pci_dev!(0x0, 0x14, 0x3, VpciDevType::Physical), // network controller + pci_dev!(0x0, 0x16, 0x0, VpciDevType::Physical), // communication controller + pci_dev!(0x0, 0x1c, 0x0, VpciDevType::Physical), // PCI bridge + pci_dev!(0x0, 0x1d, 0x0, VpciDevType::Physical), // PCI bridge + pci_dev!(0x0, 0x1f, 0x0, VpciDevType::Physical), // ISA bridge + pci_dev!(0x0, 0x1f, 0x3, VpciDevType::Physical), // audio controller + pci_dev!(0x0, 0x1f, 0x4, VpciDevType::Physical), // SMBus + pci_dev!(0x0, 0x1f, 0x5, VpciDevType::Physical), // serial bus controller + pci_dev!(0x1, 0x0, 0x0, VpciDevType::Physical), // ethernet controller + pci_dev!(0x2, 0x0, 0x0, VpciDevType::Physical), // memory controller ]; #[cfg(all(feature = "graphics"))] diff --git a/platform/x86_64/nuc14mnk/cargo/features b/platform/x86_64/nuc14mnk/cargo/features index ac3b7f71..f8ee525a 100644 --- a/platform/x86_64/nuc14mnk/cargo/features +++ b/platform/x86_64/nuc14mnk/cargo/features @@ -1,2 +1,4 @@ pci +ecam_pcie +no_pcie_bar_realloc uart16550a \ No newline at end of file diff --git a/platform/x86_64/qemu/board.rs b/platform/x86_64/qemu/board.rs index 666ad306..ab42fc3a 100644 --- a/platform/x86_64/qemu/board.rs +++ b/platform/x86_64/qemu/board.rs @@ -13,7 +13,8 @@ // // Authors: // -use crate::{arch::zone::HvArchZoneConfig, config::*, memory::GuestPhysAddr}; +use crate::pci_dev; +use crate::{arch::zone::HvArchZoneConfig, config::*, memory::GuestPhysAddr, pci::vpci_dev::VpciDevType}; pub const MEM_TYPE_RESERVED: u32 = 5; @@ -90,7 +91,7 @@ pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 8] = [ const ROOT_ZONE_CMDLINE_ADDR: GuestPhysAddr = 0x9000; const ROOT_ZONE_SETUP_ADDR: GuestPhysAddr = 0xa000; const ROOT_ZONE_VMLINUX_ENTRY_ADDR: GuestPhysAddr = 0x10_0000; -const ROOT_ZONE_SCREEN_BASE_ADDR: GuestPhysAddr = 0x7000_0000; +const ROOT_ZONE_SCREEN_BASE_ADDR: GuestPhysAddr = 0; pub const ROOT_ZONE_IRQS_BITMAP: &[BitmapWord] = &get_irqs_bitmap(&[0; 32]); pub const ROOT_ZONE_IOAPIC_BASE: usize = 0xfec0_0000; @@ -108,8 +109,9 @@ pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { screen_base: ROOT_ZONE_SCREEN_BASE_ADDR, }; -// only need to fill in ecam_base and ecam_size in x86_64 -pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { +pub const ROOT_PCI_CONFIG: [HvPciConfig; 1] = [HvPciConfig { + bus_range_begin: 0x0, + bus_range_end: 0x1f, ecam_base: 0xe0000000, ecam_size: 0x200000, io_base: 0x0, @@ -121,9 +123,20 @@ pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { mem64_base: 0x0, mem64_size: 0x0, pci_mem64_base: 0x0, -}; + domain: 0x0, +}]; -pub const ROOT_PCI_DEVS: [u64; 8] = [0x0, 0x8, 0x10, 0x18, 0xf8, 0xfa, 0xfb, 0x100]; +pub const ROOT_PCI_MAX_BUS: usize = 1; +pub const ROOT_PCI_DEVS: [HvPciDevConfig; 8] = [ + pci_dev!(0x0, 0x0, 0x0, 0x0, VpciDevType::Physical), // host bridge + pci_dev!(0x0, 0x0, 0x1, 0x0, VpciDevType::Physical), // VGA controller + pci_dev!(0x0, 0x0, 0x2, 0x0, VpciDevType::Physical), // Ethernet controller + pci_dev!(0x0, 0x0, 0x3, 0x0, VpciDevType::Physical), // PCI bridge + pci_dev!(0x0, 0x0, 0x1f, 0x0, VpciDevType::Physical), // ISA bridge + pci_dev!(0x0, 0x0, 0x1f, 0x2, VpciDevType::Physical), // SATA controller + pci_dev!(0x0, 0x0, 0x1f, 0x3, VpciDevType::Physical), // SMBus + pci_dev!(0x0, 0x1, 0x0, 0x0, VpciDevType::Physical), // SCSI controller +]; #[cfg(all(feature = "graphics"))] pub const GRAPHICS_FONT: &[u8] = diff --git a/platform/x86_64/qemu/cargo/features b/platform/x86_64/qemu/cargo/features index ac3b7f71..f8ee525a 100644 --- a/platform/x86_64/qemu/cargo/features +++ b/platform/x86_64/qemu/cargo/features @@ -1,2 +1,4 @@ pci +ecam_pcie +no_pcie_bar_realloc uart16550a \ No newline at end of file diff --git a/src/arch/aarch64/consts.rs b/src/arch/aarch64/consts.rs index 5bfd0825..68e853a8 100644 --- a/src/arch/aarch64/consts.rs +++ b/src/arch/aarch64/consts.rs @@ -15,6 +15,6 @@ // ForeverYolo <2572131118@qq.com> // PCI constants -pub const HV_ADDR_PREFIX: u64 = 0; -pub const LOONG_HT_PREFIX: u64 = 0; -pub const BDF_SHIFT: usize = 12; +// pub const HV_ADDR_PREFIX: u64 = 0; +// pub const LOONG_HT_PREFIX: u64 = 0; +// pub const BDF_SHIFT: usize = 12; diff --git a/src/arch/aarch64/iommu.rs b/src/arch/aarch64/iommu.rs index a33d3f17..7e6336d4 100644 --- a/src/arch/aarch64/iommu.rs +++ b/src/arch/aarch64/iommu.rs @@ -13,6 +13,7 @@ // // Authors: // +#![allow(dead_code)] use crate::{ arch::mm::new_s2_memory_set, consts::{MAX_ZONE_NUM, PAGE_SIZE}, diff --git a/src/arch/aarch64/zone.rs b/src/arch/aarch64/zone.rs index b57fecae..207710fc 100644 --- a/src/arch/aarch64/zone.rs +++ b/src/arch/aarch64/zone.rs @@ -21,7 +21,6 @@ use crate::{ device::virtio_trampoline::mmio_virtio_handler, error::HvResult, memory::{GuestPhysAddr, HostPhysAddr, MemFlags, MemoryRegion, MemorySet}, - pci::pcibar::BarRegion, zone::Zone, }; @@ -162,19 +161,3 @@ pub struct Gicv3Config { pub gits_base: usize, pub gits_size: usize, } - -impl BarRegion { - pub fn arch_set_bar_region_start(&mut self, cpu_base: usize, pci_base: usize) { - self.start = crate::memory::addr::align_down(cpu_base + self.start - pci_base); - } - - pub fn arch_insert_bar_region(&self, gpm: &mut MemorySet, zone_id: usize) { - gpm.insert(MemoryRegion::new_with_offset_mapper( - self.start as GuestPhysAddr, - self.start, - self.size, - MemFlags::READ | MemFlags::WRITE | MemFlags::IO, - )) - .ok(); - } -} diff --git a/src/arch/loongarch64/zone.rs b/src/arch/loongarch64/zone.rs index b9dd8b70..86dfafbc 100644 --- a/src/arch/loongarch64/zone.rs +++ b/src/arch/loongarch64/zone.rs @@ -26,7 +26,6 @@ use crate::{ mmio_generic_handler, mmio_perform_access, GuestPhysAddr, HostPhysAddr, MMIOAccess, MemFlags, MemoryRegion, MemorySet, }, - pci::pcibar::BarRegion, zone::Zone, PHY_TO_DMW_UNCACHED, }; @@ -565,8 +564,8 @@ fn handle_extioi_mapping_mmio(mmio: &mut MMIOAccess, base_addr: usize, size: usi new_data |= (1 << target_cpu_id); let target_write_phyaddr = base_addr + target_ioi_number as usize; let target_write_value = new_data as u8; - info!( - "[[interrupt virtualization]] extioi[{}], node_selection={:#x}, irq_target={:#x}, changed irq routing to cpu {}, value={:#x}", + trace!( + "extioi[{}], node_selection={:#x}, irq_target={:#x}, changed irq routing to cpu {}, value={:#x}", target_ioi_number, target_ioi_node_selection, target_ioi_irq_target, target_cpu_id, target_write_value ); unsafe { @@ -681,38 +680,14 @@ impl Zone { size as _, MemFlags::READ | MemFlags::WRITE | MemFlags::IO, ))?; - self.gpm.delete(vaddr as GuestPhysAddr) + self.gpm.delete(vaddr as GuestPhysAddr, size) } pub fn arch_zone_pre_configuration(&mut self, config: &HvZoneConfig) -> HvResult { - let vaddr = config.pci_config.ecam_base; - let size = config.pci_config.ecam_size; - self.gpm.insert(MemoryRegion::new_with_offset_mapper( - vaddr as GuestPhysAddr, - vaddr as HostPhysAddr, - size as _, - MemFlags::READ | MemFlags::WRITE | MemFlags::IO, - ))?; - self.gpm.delete(vaddr as GuestPhysAddr) + Ok(()) } pub fn arch_zone_post_configuration(&mut self, config: &HvZoneConfig) -> HvResult { Ok(()) } } - -impl BarRegion { - pub fn arch_set_bar_region_start(&mut self, cpu_base: usize, pci_base: usize) { - self.start = crate::memory::addr::align_down(cpu_base + self.start - pci_base); - } - - pub fn arch_insert_bar_region(&self, gpm: &mut MemorySet, zone_id: usize) { - gpm.insert(MemoryRegion::new_with_offset_mapper( - self.start as GuestPhysAddr, - self.start, - self.size, - MemFlags::READ | MemFlags::WRITE | MemFlags::IO, - )) - .ok(); - } -} diff --git a/src/arch/riscv64/zone.rs b/src/arch/riscv64/zone.rs index fd67b580..81a02c5b 100644 --- a/src/arch/riscv64/zone.rs +++ b/src/arch/riscv64/zone.rs @@ -19,7 +19,6 @@ use crate::{ device::virtio_trampoline::{mmio_virtio_handler, VIRTIO_BRIDGE}, error::HvResult, memory::{addr::align_up, GuestPhysAddr, HostPhysAddr, MemFlags, MemoryRegion, MemorySet}, - pci::pcibar::BarRegion, percpu::get_cpu_data, zone::Zone, }; @@ -77,19 +76,3 @@ pub struct HvArchZoneConfig { pub aplic_base: usize, pub aplic_size: usize, } - -impl BarRegion { - pub fn arch_set_bar_region_start(&mut self, cpu_base: usize, pci_base: usize) { - self.start = crate::memory::addr::align_down(cpu_base + self.start - pci_base); - } - - pub fn arch_insert_bar_region(&self, gpm: &mut MemorySet, zone_id: usize) { - gpm.insert(MemoryRegion::new_with_offset_mapper( - self.start as GuestPhysAddr, - self.start, - self.size, - MemFlags::READ | MemFlags::WRITE | MemFlags::IO, - )) - .ok(); - } -} diff --git a/src/arch/x86_64/acpi.rs b/src/arch/x86_64/acpi.rs index b3c6636d..4bfdc530 100644 --- a/src/arch/x86_64/acpi.rs +++ b/src/arch/x86_64/acpi.rs @@ -15,10 +15,11 @@ // Solicey use crate::{ - arch::{boot, pci::probe_root_pci_devices}, + arch::boot, config::{HvConfigMemoryRegion, HvZoneConfig}, error::HvResult, percpu::{this_zone, CpuSet}, + platform::ROOT_PCI_MAX_BUS, }; use acpi::{ fadt::Fadt, @@ -273,13 +274,8 @@ pub struct RootAcpi { tables: BTreeMap, ssdts: BTreeMap, pointers: Vec, - devices: Vec, config_space_base: usize, config_space_size: usize, - /// key: data reg hpa, value: bdf - msi_data_reg_map: BTreeMap, - /// key: msi-x table bar, value: bdf - msix_bar_map: BTreeMap, /// key: apic id, value: cpu id (continuous) apic_id_to_cpu_id: BTreeMap, /// key: cpu id (continuous), value: apic id @@ -536,10 +532,7 @@ impl RootAcpi { // we only support segment group 0 println!("{:x?}", entry); - // we don't have such many buses, probe devices to get the max_bus we have - let (mut devices, mut msi_data_reg_map, mut msix_bar_map, _, max_bus) = - probe_root_pci_devices(entry.base_address as _); - + let max_bus = ROOT_PCI_MAX_BUS as u8; // update bus_number_end root_acpi .get_mut_table(Signature::MCFG) @@ -547,14 +540,9 @@ impl RootAcpi { .set_u8(max_bus, offset); offset += size_of::(); - root_acpi.devices.append(&mut devices); - root_acpi.config_space_base = entry.base_address as _; root_acpi.config_space_size = (((max_bus as u64 - entry.bus_number_start as u64) + 1) << 20) as usize; - - root_acpi.msi_data_reg_map.append(&mut msi_data_reg_map); - root_acpi.msix_bar_map.append(&mut msix_bar_map); } root_acpi.add_pointer(Signature::RSDT, rsdt_offset, Signature::MCFG, RSDT_PTR_SIZE); @@ -709,22 +697,6 @@ pub fn root_get_config_space_info() -> Option<(usize, usize)> { Some((acpi.config_space_base, acpi.config_space_size)) } -pub fn is_msi_data_reg(hpa: usize) -> Option { - if let Some(&bdf) = ROOT_ACPI.get().unwrap().msi_data_reg_map.get(&hpa) { - Some(bdf) - } else { - None - } -} - -pub fn is_msix_bar(hpa: usize) -> Option { - if let Some(&bdf) = ROOT_ACPI.get().unwrap().msix_bar_map.get(&hpa) { - Some(bdf) - } else { - None - } -} - fn contains_apic_id(apic_id: usize) -> bool { ROOT_ACPI .get() diff --git a/src/arch/x86_64/boot.rs b/src/arch/x86_64/boot.rs index 334990f2..14d92a08 100644 --- a/src/arch/x86_64/boot.rs +++ b/src/arch/x86_64/boot.rs @@ -245,8 +245,8 @@ impl BootParams { } self.e820_table[index] = BootE820Entry { - addr: config.pci_config.ecam_base as _, - size: config.pci_config.ecam_size as _, + addr: config.pci_config[0].ecam_base as _, + size: config.pci_config[0].ecam_size as _, _type: E820Type::E820_RESERVED, }; index += 1; diff --git a/src/arch/x86_64/cpu.rs b/src/arch/x86_64/cpu.rs index 07190eb2..25f3653d 100644 --- a/src/arch/x86_64/cpu.rs +++ b/src/arch/x86_64/cpu.rs @@ -39,7 +39,7 @@ use crate::{ PARKING_INST_PAGE, }, percpu::{this_cpu_data, this_zone}, - platform::{ROOT_ZONE_BOOT_STACK, ROOT_ZONE_CMDLINE}, + platform::ROOT_ZONE_BOOT_STACK, zone::{find_zone, this_zone_id}, }; use alloc::boxed::Box; @@ -254,6 +254,7 @@ impl ArchCpu { unsafe { PARKING_MEMORY_SET.get().unwrap().activate(); + info!("before vmx launch"); self.vmx_launch(); } } diff --git a/src/arch/x86_64/iommu.rs b/src/arch/x86_64/iommu.rs index 80eff95f..ede523e8 100644 --- a/src/arch/x86_64/iommu.rs +++ b/src/arch/x86_64/iommu.rs @@ -297,6 +297,12 @@ impl Vtd { self.invalid_iotlb(zone_id as _); } + fn flush(&mut self, zone_id: usize, bus: u8, dev_func: u8) { + let bdf: u16 = (bus as u16) << 8 | (dev_func as u16); + self.invalidate_context_cache(zone_id as _, bdf as _, 0); + self.invalid_iotlb(zone_id as _); + } + fn init(&mut self) { self.check_capability(); self.set_interrupt(); @@ -518,6 +524,10 @@ pub fn activate() { VTD.get().unwrap().lock().activate(); } +pub fn flush(zone_id: usize, bus: u8, dev_func: u8) { + VTD.get().unwrap().lock().flush(zone_id, bus, dev_func); +} + fn flush_cache_range(hpa: usize, size: usize) { let mut i = 0usize; while i < size { diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index d75140ed..023abdc3 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -35,6 +35,7 @@ pub mod pci; pub mod pio; pub mod s1pt; pub mod s2pt; +pub mod time; pub mod trap; pub mod vmcs; pub mod vmx; diff --git a/src/arch/x86_64/pci.rs b/src/arch/x86_64/pci.rs index 8383fccd..cc05d533 100644 --- a/src/arch/x86_64/pci.rs +++ b/src/arch/x86_64/pci.rs @@ -20,7 +20,6 @@ use crate::{ memory::{ mmio_generic_handler, mmio_handle_access, mmio_perform_access, GuestPhysAddr, MMIOAccess, }, - pci::pcibar::BarRegion, percpu::this_zone, zone::{this_zone_id, Zone}, }; @@ -38,162 +37,6 @@ use super::{ vmx::VmxIoExitInfo, }; -impl Zone { - pub fn pci_config_space_mmio_init(&mut self, arch: &HvArchZoneConfig) { - /*let bytes = acpi::root_get_table(&Signature::MCFG) - .unwrap() - .get_bytes() - .clone(); - let mcfg = unsafe { &*(bytes.as_ptr() as *const Mcfg) };*/ - - let bytes = acpi::root_get_table(&Signature::MCFG) - .unwrap() - .get_unpatched_src(); - let mcfg = unsafe { &*(bytes as *const Mcfg) }; - - for entry in mcfg.entries() { - let start = entry.base_address as usize; - let size = - ((entry.bus_number_end as usize - entry.bus_number_start as usize) + 1) << 20; - // info!("entry start: {:x} size: {:x}", start, size); - self.mmio_region_register(start, size, mmio_generic_handler, 0); - } - } -} - -pub fn probe_root_pci_devices( - config_base_hpa: usize, -) -> ( - Vec, - BTreeMap, - BTreeMap, - usize, - u8, -) { - let mut bdfs: Vec = Vec::new(); - // key: data reg hpa, value: bdf - let mut msi_data_reg_map: BTreeMap = BTreeMap::new(); - // key: msi-x table bar, value: bdf - let mut msix_bar_map: BTreeMap = BTreeMap::new(); - let mut config_space_size = 0usize; - - // info!("entry start: {:x} size: {:x}", start, size); - let mut buses: VecDeque = VecDeque::new(); - let mut max_bus: u8 = 0; - buses.push_back(max_bus); - - while !buses.is_empty() { - let bus = buses.pop_front().unwrap(); - let bus_config_hpa = (config_base_hpa as usize) + ((bus as usize) << 20); - let mut bus_empty: bool = true; - - for dev_func in 0u8..=255 { - let bdf = ((bus as u16) << 8) + (dev_func as u16); - let bdf_config_hpa = bus_config_hpa + ((dev_func as usize) << 12); - - let vendor_id = unsafe { *(bdf_config_hpa as *const u16) }; - if vendor_id == 0xffff { - continue; - } - - let device_id = unsafe { *((bdf_config_hpa + 0x2) as *const u16) }; - let header_type = unsafe { *((bdf_config_hpa + 0xe) as *const u8) }; - - println!( - "bdf: {:x}, bus: {:x}, dev_func: {:x}, vendor id: {:x}, device id: {:x}, header type: {:x}", - bdf, bus, dev_func, vendor_id, device_id, header_type - ); - - bdfs.push(bdf as _); - bus_empty = false; - - // pci bridge - if header_type.get_bits(0..7) == 0x1 { - let secondary_bus = unsafe { *((bdf_config_hpa + 0x19) as *const u8) }; - buses.push_back(secondary_bus); - } - - // probe msi/msi-x capability registers - let mut cap_pointer = unsafe { *((bdf_config_hpa + 0x34) as *const u8) } as usize; - while cap_pointer != 0 { - let cap_hpa = bdf_config_hpa + cap_pointer; - let cap_id = unsafe { *(cap_hpa as *const u8) }; - - if cap_id == 0x5 { - // msi capablility - let msg_ctrl_reg = unsafe { *((cap_hpa + 0x2) as *const u16) }; - let is_64b = msg_ctrl_reg.get_bit(7); - let per_vector_masking = msg_ctrl_reg.get_bit(8); - - let data_reg_hpa = match is_64b { - true => cap_hpa + 0xc, - false => cap_hpa + 0x8, - }; - msi_data_reg_map.insert(data_reg_hpa, bdf as _); - // println!("msi data reg hpa: {:x?}", data_reg_hpa); - println!("msi per vector masking: {:#x?}", per_vector_masking); - } else if cap_id == 0x11 { - // msi-x capability - let msg_ctrl_reg = unsafe { *((cap_hpa + 0x2) as *const u16) }; - let table_size = msg_ctrl_reg.get_bits(0..=10) as usize; - let table_bir = - unsafe { *((cap_hpa + 0x4) as *const u16) }.get_bits(0..=2) as usize; - - // find msi-x table bar - let bar_hpa = bdf_config_hpa + 0x10 + (table_bir) * size_of::(); - let mut bar = unsafe { *(bar_hpa as *const u32) } as usize; - assert!(!bar.get_bit(0)); // memory request - match bar.get_bits(1..=2) { - 0b00 => { - // 32-bit decoding - bar &= !(0xfff); - } - 0b10 => { - // 64-bit decoding - let bar_high = - unsafe { *((bar_hpa + size_of::()) as *const u32) } as usize; - bar = (bar_high << 6) + bar.get_bits(26..=31); - } - _ => { - panic!("MSI-X table BAR type error!"); - } - } - - /*println!( - "table size: {:x}, table bir: {:x}, bar: {:x}", - table_size, table_bir, bar - );*/ - msix_bar_map.insert(bar, bdf as _); - - for i in 0..=table_size { - let data_reg_hpa = bar + i * size_of::() + 2 * size_of::(); - msi_data_reg_map.insert(data_reg_hpa, bdf as _); - // println!("msi-x data reg hpa: {:x?}", data_reg_hpa); - } - } - - // println!("cap id: {:x}, hpa: {:x}", cap_id, cap_hpa); - cap_pointer = unsafe { *((cap_hpa + 1) as *const u8) } as usize; - } - } - - if !bus_empty && bus > max_bus { - max_bus = bus; - } - } - - config_space_size = ((max_bus as usize - 0usize) + 1) << 20; - // info!("config space size: {:x}", config_space_size); - - ( - bdfs, - msi_data_reg_map, - msix_bar_map, - config_space_size, - max_bus, - ) -} - fn get_pci_mmio_addr() -> Option { let addr = get_pio_bitmap(this_zone_id()).pci_config_addr as usize; let (base, _) = crate::arch::acpi::root_get_config_space_info().unwrap(); diff --git a/src/arch/x86_64/time.rs b/src/arch/x86_64/time.rs new file mode 100644 index 00000000..f8bf3b11 --- /dev/null +++ b/src/arch/x86_64/time.rs @@ -0,0 +1,21 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// Jingyu Liu +// + +/// Note: You should call this function once during initialization. +pub fn init_timebase() { + info!("Initializing x86_64 timebase not implemented yet."); +} diff --git a/src/arch/x86_64/zone.rs b/src/arch/x86_64/zone.rs index e4a55f82..f69f4eac 100644 --- a/src/arch/x86_64/zone.rs +++ b/src/arch/x86_64/zone.rs @@ -20,7 +20,6 @@ use crate::{ device::virtio_trampoline::mmio_virtio_handler, error::HvResult, memory::{GuestPhysAddr, HostPhysAddr, MemFlags, MemoryRegion, MemorySet}, - pci::pcibar::{BarRegion, BarType}, percpu::get_cpu_data, platform::MEM_TYPE_RESERVED, zone::Zone, @@ -118,7 +117,7 @@ impl Zone { } pub fn arch_zone_post_configuration(&mut self, config: &HvZoneConfig) -> HvResult { - let mut msix_bar_regions: Vec = Vec::new(); + /*let mut msix_bar_regions: Vec = Vec::new(); for region in self.pciroot.bar_regions.iter_mut() { // check whether this bar is msi-x table // if true, use msi-x table handler instead @@ -142,7 +141,7 @@ impl Zone { if self.id == 0 { self.pci_bars_register(&config.pci_config); - } + }*/ boot::BootParams::fill(&config, &mut self.gpm); acpi::copy_to_guest_memory_region(&config, &self.cpu_set); @@ -151,7 +150,7 @@ impl Zone { } } -impl BarRegion { +/*impl BarRegion { pub fn arch_set_bar_region_start(&mut self, cpu_base: usize, pci_base: usize) { self.start = cpu_base + self.start - pci_base; if self.bar_type != BarType::IO { @@ -175,4 +174,4 @@ impl BarRegion { ); } } -} +}*/ diff --git a/src/config.rs b/src/config.rs index e09876a3..7b4bad30 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,15 +14,16 @@ // Authors: // use alloc::vec::Vec; +use core::fmt::Debug; use spin::Once; -use crate::{arch::zone::HvArchZoneConfig, platform}; +use crate::{arch::zone::HvArchZoneConfig, pci::vpci_dev::VpciDevType, platform}; pub const MEM_TYPE_RAM: u32 = 0; pub const MEM_TYPE_IO: u32 = 1; pub const MEM_TYPE_VIRTIO: u32 = 2; -pub const CONFIG_MAGIC_VERSION: usize = 0x4; +pub const CONFIG_MAGIC_VERSION: usize = 0x5; pub const CONFIG_MAX_MEMORY_REGIONS: usize = 64; pub type BitmapWord = u32; @@ -31,6 +32,7 @@ pub const CONFIG_INTERRUPTS_BITMAP_BITS_PER_WORD: usize = 32; pub const CONFIG_NAME_MAXLEN: usize = 32; pub const CONFIG_MAX_IVC_CONFIGS: usize = 2; +pub const CONFIG_PCI_BUS_MAXNUM: usize = 4; pub const CONFIG_MAX_PCI_DEV: usize = 32; #[repr(C)] @@ -57,6 +59,9 @@ pub struct HvPciConfig { pub mem64_base: u64, pub mem64_size: u64, pub pci_mem64_base: u64, + pub bus_range_begin: u32, + pub bus_range_end: u32, + pub domain: u8, } impl HvPciConfig { @@ -73,6 +78,9 @@ impl HvPciConfig { mem64_base: 0, mem64_size: 0, pci_mem64_base: 0, + bus_range_begin: 0, + bus_range_end: 0, + domain: 0, } } } @@ -94,9 +102,10 @@ pub struct HvZoneConfig { pub dtb_size: u64, pub name: [u8; CONFIG_NAME_MAXLEN], pub arch_config: HvArchZoneConfig, - pub pci_config: HvPciConfig, + pub num_pci_bus: u64, + pub pci_config: [HvPciConfig; CONFIG_PCI_BUS_MAXNUM], pub num_pci_devs: u64, - pub alloc_pci_devs: [u64; CONFIG_MAX_PCI_DEV], + pub alloc_pci_devs: [HvPciDevConfig; CONFIG_MAX_PCI_DEV], } impl HvZoneConfig { @@ -116,9 +125,10 @@ impl HvZoneConfig { dtb_size: u64, name: [u8; CONFIG_NAME_MAXLEN], arch: HvArchZoneConfig, - pci: HvPciConfig, + num_pci_bus: u64, + pci: [HvPciConfig; CONFIG_PCI_BUS_MAXNUM], num_pci_devs: u64, - alloc_pci_devs: [u64; CONFIG_MAX_PCI_DEV], + alloc_pci_devs: [HvPciDevConfig; CONFIG_MAX_PCI_DEV], ) -> Self { Self { zone_id, @@ -135,6 +145,7 @@ impl HvZoneConfig { dtb_size, name, arch_config: arch, + num_pci_bus, pci_config: pci, num_pci_devs: num_pci_devs, alloc_pci_devs: alloc_pci_devs, @@ -163,6 +174,11 @@ impl HvZoneConfig { pub fn ivc_config(&self) -> &[HvIvcConfig] { &self.ivc_configs[..self.num_ivc_configs as usize] } + + #[allow(unused)] + pub fn pci_config(&self) -> &[HvPciConfig] { + &self.pci_config[..self.num_pci_bus as usize] + } } pub static mut HV_ROOT_ZONE_CONFIG: Once = Once::new(); @@ -195,6 +211,77 @@ pub struct HvIvcConfig { pub max_peers: u32, } +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct HvPciDevConfig { + pub domain: u8, + pub bus: u8, + pub device: u8, + pub function: u8, + pub dev_type: VpciDevType, +} + +#[macro_export] +macro_rules! pci_dev { + ($domain:expr, $bus:expr, $dev:expr, $func:expr, $dev_type:expr) => { + HvPciDevConfig { + domain: $domain, + bus: $bus, + device: $dev, + function: $func, + dev_type: $dev_type, + } + }; +} + +impl Debug for HvPciDevConfig { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let bdf = + crate::pci::pci_struct::Bdf::new(self.domain, self.bus, self.device, self.function); + write!(f, "bdf {:#?}", bdf) + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct HvDwcAtuConfig { + // ECAM (Enhanced Configuration Access Mechanism) base address + // This is used to match with HvPciConfig::ecam_base + pub ecam_base: u64, + pub dbi_base: u64, + pub dbi_size: u64, + pub apb_base: u64, + pub apb_size: u64, + pub cfg_base: u64, + pub cfg_size: u64, + // set 1 if io base use atu0, when hvisor need set mmio for io + // normally, when num-viewport less than 4, io_cfg_atu_shared is 1, otherwise is 0 + pub io_cfg_atu_shared: u64, +} + +impl HvDwcAtuConfig { + pub const fn new_empty() -> Self { + // Use ATU_UNUSED for ATU indices that are not used by default + // ATU_UNUSED is u32::MAX, cast to usize for consistency + // Default ATU types: CFG0=4, CFG1=5, MEM=0, IO=2 + Self { + ecam_base: 0, + dbi_base: 0, + dbi_size: 0, + apb_base: 0, + apb_size: 0, + cfg_base: 0, + cfg_size: 0, + io_cfg_atu_shared: 0, + } + } +} + +impl Default for HvDwcAtuConfig { + fn default() -> Self { + Self::new_empty() + } +} pub const fn get_irqs_bitmap( numbers: &[u32; N], ) -> [BitmapWord; CONFIG_MAX_INTERRUPTS / CONFIG_INTERRUPTS_BITMAP_BITS_PER_WORD] { diff --git a/src/main.rs b/src/main.rs index 28d02906..7565d5ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -72,6 +72,8 @@ use crate::consts::{hv_end, mem_pool_start, MAX_CPU_NUM}; use arch::{cpu::cpu_start, entry::arch_entry}; use config::root_zone_config; use core::sync::atomic::{AtomicI32, AtomicU32, Ordering}; +#[cfg(feature = "pci")] +use pci::pci_config::hvisor_pci_init; use percpu::PerCpu; use zone::{add_zone, zone_create}; @@ -132,13 +134,27 @@ fn primary_init_early() { device::irqchip::primary_init_early(); + #[cfg(feature = "iommu")] iommu_init(); + let root_config = root_zone_config(); + + #[cfg(feature = "pci")] + if root_config.num_pci_bus > 0 { + let num_pci_bus = root_config.num_pci_bus as usize; + let _ = hvisor_pci_init(&root_config.pci_config[..num_pci_bus]); + } + #[cfg(not(test))] { - let zone = zone_create(root_zone_config()).unwrap(); + let zone = zone_create(root_config).unwrap(); add_zone(zone); } + + // crate::pci::pci_test::pcie_test(); + // crate::pci::pci_test::pcie_guest_init(); + // crate::pci::pci_test::ecam_pcie_guest_test(); + INIT_EARLY_OK.store(1, Ordering::Release); } diff --git a/src/memory/mm.rs b/src/memory/mm.rs index 54a7c7a8..1edcbcef 100644 --- a/src/memory/mm.rs +++ b/src/memory/mm.rs @@ -109,7 +109,12 @@ where /// Add a memory region to this set. pub fn insert(&mut self, region: MemoryRegion) -> HvResult { - info!("region.start: {:#X}", region.start.into()); + info!( + "region.start: {:#X}, size: {:#X}, flags: {:#X}", + region.start.into(), + region.size, + region.flags + ); assert!(is_aligned(region.start.into())); assert!(is_aligned(region.size)); if region.size == 0 { @@ -127,10 +132,44 @@ where Ok(()) } - /// Find and remove memory region which starts from `start`. - pub fn delete(&mut self, start: PT::VA) -> HvResult { + pub fn try_insert(&mut self, region: MemoryRegion) -> HvResult { + if !self.test_free_area(®ion) { + warn!( + "try insert MemoryRegion overlapped in MemorySet: {:#x?}\n{:#x?}", + region, self + ); + return Ok(()); + } + + self.insert(region)?; + Ok(()) + } + + pub fn try_insert_quiet(&mut self, region: MemoryRegion) -> HvResult { + if !self.test_free_area(®ion) { + return Ok(()); + } + + self.insert(region)?; + Ok(()) + } + + /// Find and remove memory region which starts from `start` and `size` + pub fn delete(&mut self, start: PT::VA, size: usize) -> HvResult { if let Entry::Occupied(e) = self.regions.entry(start) { - self.pt.unmap(e.get())?; + let region = e.get(); + if region.size != size { + return hv_result_err!( + EINVAL, + format!( + "MemorySet::delete(): size mismatch at {:#x?}, expected {:#x?}, got {:#x?}", + start.into(), + size, + region.size + ) + ); + } + self.pt.unmap(region)?; e.remove(); Ok(()) } else { @@ -144,6 +183,25 @@ where } } + pub fn try_delete(&mut self, start: PT::VA, size: usize) -> HvResult { + if let Entry::Occupied(e) = self.regions.entry(start) { + let region = e.get(); + if region.size == size { + self.delete(start, size) + } else { + warn!( + "try delete MemoryRegion size mismatch at {:#x?}, expected {:#x?}, got {:#x?}", + start.into(), + size, + region.size + ); + Err(hv_err!(ENOMEM)) + } + } else { + Err(hv_err!(ENOMEM)) + } + } + pub fn clear(&mut self) { for region in self.regions.values() { self.pt.unmap(region).unwrap(); diff --git a/src/pci/bridge.rs b/src/pci/bridge.rs deleted file mode 100644 index 9bd67c2e..00000000 --- a/src/pci/bridge.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2025 Syswonder -// hvisor is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR -// FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. -// -// Syswonder Website: -// https://www.syswonder.org -// -// Authors: -// -use alloc::vec::Vec; - -use super::{ - pcibar::{BarRegion, PciBar, VirtPciBar}, - phantom_cfg::{PhantomCfg, PhantomCfgType}, - NUM_BAR_REGS_TYPE1, NUM_MAX_BARS, -}; - -#[derive(Debug)] -pub struct BridgeConfig { - bars: [PciBar; NUM_BAR_REGS_TYPE1], - pub bdf: usize, -} - -impl BridgeConfig { - pub fn new(bdf: usize) -> Self { - Self { - bars: [PciBar::default(); NUM_BAR_REGS_TYPE1], - bdf: bdf, - } - } - - pub fn bars_init(&mut self, bar_id: usize, origin_val: u32, val: u32) { - self.bars[bar_id].init(origin_val, val); - } - - pub fn get_regions(&self) -> Vec { - let mut regions: Vec = Vec::new(); - let mut bar_id = 0; - while bar_id < NUM_BAR_REGS_TYPE1 { - if self.bars[bar_id].is_mutable() { - if !self.bars[bar_id].mem_type_64() { - regions.push(self.bars[bar_id].get_32b_region()); - bar_id += 1; - } else { - regions.push( - self.bars[bar_id + 1].get_64b_region(self.bars[bar_id].get_32b_region()), - ); - bar_id += 2; - } - } else { - bar_id += 1; - } - } - regions - } - - // after we get bar regions, we should generate a virtual device instance that mirrors this device for use by other VMs - pub fn generate_vbridge(&self) -> PhantomCfg { - let mut v_bars: [VirtPciBar; NUM_MAX_BARS] = [VirtPciBar::default(); NUM_MAX_BARS]; - for i in 0..NUM_BAR_REGS_TYPE1 { - v_bars[i] = self.bars[i].generate_vbar(); - } - PhantomCfg::new(self.bdf, v_bars, PhantomCfgType::BRIDGE) - } -} diff --git a/src/pci/config_accessors/dwc.rs b/src/pci/config_accessors/dwc.rs new file mode 100644 index 00000000..8c609480 --- /dev/null +++ b/src/pci/config_accessors/dwc.rs @@ -0,0 +1,195 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// + +use alloc::sync::Arc; + +use super::{ + dwc_atu::{AtuConfig, AtuType, AtuUnroll, ATU_UNUSED}, + PciConfigAccessor, PciConfigMmio, PciRegion, PciRegionMmio, +}; + +use crate::{ + config::HvDwcAtuConfig, + error::HvResult, + pci::{ + pci_access::{PciRW, PciRWBase}, + pci_struct::{Bdf, RootComplex}, + PciConfigAddress, + }, +}; + +impl RootComplex { + pub fn new_dwc(ecam_base: u64, atu_config: &HvDwcAtuConfig, root_bus: u8) -> Self { + let accessor = Arc::new(DwcConfigAccessor::new(atu_config, root_bus)); + + Self { + mmio_base: ecam_base, + accessor, + } + } +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct DwcConfigRegion { + pub atu_index: usize, + pub atu_type: AtuType, + pub base: PciConfigAddress, + pub size: u64, +} + +#[derive(Debug)] +pub struct DwcConfigRegionBackend(PciRegionMmio); + +impl DwcConfigRegionBackend { + pub fn new(region: PciRegionMmio) -> Self { + Self(region) + } +} + +impl PciRWBase for DwcConfigRegionBackend { + fn backend(&self) -> &dyn PciRegion { + &self.0 + } +} +impl PciRW for DwcConfigRegionBackend {} + +#[derive(Debug)] +pub struct DwcConfigAccessor { + dbi_backend: Arc, + dbi: DwcConfigRegion, + cfg0: DwcConfigRegion, + cfg1: DwcConfigRegion, + root_bus: u8, +} + +impl DwcConfigAccessor { + pub fn new(atu_config: &HvDwcAtuConfig, root_bus: u8) -> Self { + let cfg_size_half = atu_config.cfg_size / 2; + let cfg0_base = atu_config.cfg_base; + let cfg1_base = atu_config.cfg_base + cfg_size_half; + + // Create DBI backend for ATU configuration + let dbi_base = atu_config.dbi_base as PciConfigAddress; + let dbi_size = atu_config.dbi_size; + let dbi_region = PciRegionMmio::new(dbi_base, dbi_size); + let dbi_backend = Arc::new(DwcConfigRegionBackend(dbi_region)); + + let dbi = DwcConfigRegion { + atu_index: ATU_UNUSED as usize, + atu_type: AtuType::Unused, + base: dbi_base, + size: dbi_size, + }; + + /* actually we only use atu0 to init dwc pcie */ + let cfg0 = DwcConfigRegion { + atu_index: 0, + atu_type: AtuType::Cfg0, + base: cfg0_base, + size: cfg_size_half, + }; + let cfg1 = DwcConfigRegion { + atu_index: 0, + atu_type: AtuType::Cfg1, + base: cfg1_base, + size: cfg_size_half, + }; + + Self { + dbi_backend, + dbi, + cfg0, + cfg1, + root_bus, + } + } +} + +impl PciConfigAccessor for DwcConfigAccessor { + fn get_pci_addr_base(&self, bdf: Bdf) -> HvResult { + let bus = bdf.bus() as PciConfigAddress; + let device = bdf.device() as PciConfigAddress; + let function = bdf.function() as PciConfigAddress; + + let pci_addr = (bus << 24) + (device << 19) + (function << 16); + // info!("pci_addr {:#x}", pci_addr); + let address = if bus == self.root_bus.into() { + // Root bus: use DBI directly, no ATU configuration needed + self.dbi.base + } else { + pci_addr + }; + + Ok(address) + } + + fn get_physical_address( + &self, + bdf: Bdf, + _offset: PciConfigAddress, + parent_bus: u8, + ) -> HvResult { + let bus = bdf.bus() as PciConfigAddress; + let device = bdf.device() as PciConfigAddress; + let function = bdf.function() as PciConfigAddress; + + // warn!("parent_bus {} self.root_bus {}", parent_bus, self.root_bus); + + let pci_addr = (bus << 24) + (device << 19) + (function << 16); + + let address = if bus == self.root_bus.into() { + // Root bus: use DBI directly, no ATU configuration needed + self.dbi.base + } else if parent_bus == self.root_bus.into() { + if self.cfg0.atu_index == ATU_UNUSED as usize { + return hv_result_err!(EINVAL, "CFG0 ATU is not configured"); + } + let atu_config = + AtuConfig::new_with_dwc_config_region(&self.cfg0, AtuType::Cfg0, pci_addr); + AtuUnroll::dw_pcie_prog_outbound_atu_unroll(self.dbi_backend.as_ref(), &atu_config)?; + + self.cfg0.base + } else { + if self.cfg1.atu_index == ATU_UNUSED as usize { + return hv_result_err!(EINVAL, "CFG1 ATU is not configured"); + } + let atu_config = + AtuConfig::new_with_dwc_config_region(&self.cfg1, AtuType::Cfg1, pci_addr); + AtuUnroll::dw_pcie_prog_outbound_atu_unroll(self.dbi_backend.as_ref(), &atu_config)?; + + self.cfg1.base + }; + + Ok(address) + } + + fn skip_device(&self, bdf: Bdf) -> bool { + // On root bus, only device 0 (slot 0) is valid, devices with dev > 0 should be skipped + // This matches Linux kernel's dw_pcie_valid_device() behavior + if bdf.bus() == self.root_bus && bdf.device() > 0 { + // warn!("skip_device {:#?}", bdf); + return true; + } + false + } +} + +impl PciConfigMmio { + pub(crate) fn access(&self, offset: PciConfigAddress) -> *mut T { + (self.base + offset) as *mut T + } +} diff --git a/src/pci/config_accessors/dwc_atu.rs b/src/pci/config_accessors/dwc_atu.rs new file mode 100644 index 00000000..f0353f3a --- /dev/null +++ b/src/pci/config_accessors/dwc_atu.rs @@ -0,0 +1,245 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// + +use super::dwc::DwcConfigRegion; +use core::{fmt, fmt::Debug}; + +use crate::{ + error::HvResult, + pci::{pci_access::PciRW, PciConfigAddress}, +}; + +// DWC PCIe ATU (Address Translation Unit) register offsets +pub const ATU_BASE: usize = 0x300000; +pub const ATU_REGION_SIZE: usize = 0x200; +pub const PCIE_ATU_UNR_REGION_CTRL1: usize = 0x00; +pub const PCIE_ATU_UNR_REGION_CTRL2: usize = 0x04; +pub const PCIE_ATU_UNR_LOWER_BASE: usize = 0x08; +pub const PCIE_ATU_UNR_UPPER_BASE: usize = 0x0C; +pub const PCIE_ATU_UNR_LIMIT: usize = 0x10; +pub const PCIE_ATU_UNR_LOWER_TARGET: usize = 0x14; +pub const PCIE_ATU_UNR_UPPER_TARGET: usize = 0x18; +pub const PCIE_ATU_UNR_UPPER_LIMIT: usize = 0x20; + +// ATU enable bit +pub const ATU_ENABLE_BIT: u32 = 0x80000000; + +// the flag is for dbi just +pub const ATU_UNUSED: u32 = u32::MAX; + +#[derive(Debug, Clone, Copy, Default)] +pub enum AtuType { + #[default] + Unused = 0xFF, + Cfg0 = 0x4, + Cfg1 = 0x5, + Mem = 0x0, + Io = 0x2, +} + +impl AtuType { + pub fn from_u8(value: u8) -> Self { + match value { + 0x0 => AtuType::Mem, + 0x2 => AtuType::Io, + 0x4 => AtuType::Cfg0, + 0x5 => AtuType::Cfg1, + 0xFF => AtuType::Unused, + _ => AtuType::Unused, + } + } +} + +#[derive(Clone, Copy, Default)] +pub struct AtuConfig { + index: usize, + atu_type: AtuType, + cpu_base: PciConfigAddress, + cpu_limit: PciConfigAddress, + pci_target: PciConfigAddress, + limit_hw_value: u32, + upper_limit_hw_value: u32, +} + +impl Debug for AtuConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "AtuConfig {{ index: {},\n atu_type: {:?},\n cpu_base: {:#x},\n cpu_limit: {:#x},\n pci_target: {:#x} }}", self.index(), self.atu_type(), self.cpu_base(), self.cpu_limit(), self.pci_target()) + } +} + +impl AtuConfig { + pub fn new( + index: usize, + atu_type: AtuType, + cpu_base: PciConfigAddress, + cpu_size: PciConfigAddress, + pci_target: PciConfigAddress, + ) -> Self { + let cpu_limit = cpu_base + cpu_size - 1; + Self { + index, + atu_type, + cpu_base, + cpu_limit, + pci_target, + limit_hw_value: 0, + upper_limit_hw_value: 0, + } + } + + pub fn new_with_dwc_config_region( + config_region: &DwcConfigRegion, + atu_type: AtuType, + pci_addr: PciConfigAddress, + ) -> Self { + Self::new( + config_region.atu_index, + atu_type, + config_region.base, + config_region.size, + pci_addr, + ) + } + + pub fn index(&self) -> usize { + self.index + } + + pub fn atu_type(&self) -> AtuType { + self.atu_type + } + + pub fn cpu_base(&self) -> PciConfigAddress { + self.cpu_base + } + + pub fn cpu_limit(&self) -> PciConfigAddress { + self.cpu_limit + } + + pub fn pci_target(&self) -> PciConfigAddress { + self.pci_target + } + + pub fn set_atu_type(&mut self, atu_type: AtuType) { + self.atu_type = atu_type; + } + + pub fn set_cpu_base(&mut self, cpu_base: PciConfigAddress) { + self.cpu_base = cpu_base; + } + + pub fn set_cpu_limit(&mut self, cpu_limit: PciConfigAddress) { + self.cpu_limit = cpu_limit; + } + + pub fn set_pci_target(&mut self, pci_target: PciConfigAddress) { + self.pci_target = pci_target; + } + + pub fn limit_hw_value(&self) -> u32 { + self.limit_hw_value + } + + pub fn upper_limit_hw_value(&self) -> u32 { + self.upper_limit_hw_value + } + + pub fn init_limit_hw_value(&mut self, dbi_backend: &dyn PciRW) -> HvResult { + let atu_base = (ATU_BASE + self.index() * ATU_REGION_SIZE) as PciConfigAddress; + + dbi_backend.write(atu_base + PCIE_ATU_UNR_LIMIT as PciConfigAddress, 4, 0)?; + self.limit_hw_value = + dbi_backend.read(atu_base + PCIE_ATU_UNR_LIMIT as PciConfigAddress, 4)? as u32; + + dbi_backend.write( + atu_base + PCIE_ATU_UNR_UPPER_LIMIT as PciConfigAddress, + 4, + 0xffffffff, + )?; + self.upper_limit_hw_value = + dbi_backend.read(atu_base + PCIE_ATU_UNR_UPPER_LIMIT as PciConfigAddress, 4)? as u32; + + Ok(()) + } +} + +// ATU unroll configuration functions +pub struct AtuUnroll; + +impl AtuUnroll { + // Configure ATU region with unroll registers + // Follows the same order as Linux kernel dw_pcie_prog_outbound_atu_unroll + pub fn dw_pcie_prog_outbound_atu_unroll( + dbi_backend: &dyn PciRW, + config: &AtuConfig, + ) -> HvResult { + let atu_base = (ATU_BASE + config.index() * ATU_REGION_SIZE) as PciConfigAddress; + + dbi_backend.write( + atu_base + PCIE_ATU_UNR_LOWER_BASE as PciConfigAddress, + 4, + (config.cpu_base() & 0xffffffff) as usize, + )?; + dbi_backend.write( + atu_base + PCIE_ATU_UNR_UPPER_BASE as PciConfigAddress, + 4, + (config.cpu_base() >> 32) as usize, + )?; + dbi_backend.write( + atu_base + PCIE_ATU_UNR_LIMIT as PciConfigAddress, + 4, + (config.cpu_limit() & 0xffffffff) as usize, + )?; + dbi_backend.write( + atu_base + PCIE_ATU_UNR_LOWER_TARGET as PciConfigAddress, + 4, + (config.pci_target() & 0xffffffff) as usize, + )?; + dbi_backend.write( + atu_base + PCIE_ATU_UNR_UPPER_TARGET as PciConfigAddress, + 4, + (config.pci_target() >> 32) as usize, + )?; + dbi_backend.write( + atu_base + PCIE_ATU_UNR_REGION_CTRL1 as PciConfigAddress, + 4, + config.atu_type() as usize, + )?; + dbi_backend.write( + atu_base + PCIE_ATU_UNR_REGION_CTRL2 as PciConfigAddress, + 4, + ATU_ENABLE_BIT as usize, + )?; + + // Verify that ATU enable takes effect before any subsequent config and I/O accesses + const MAX_RETRIES: usize = 5; + const RETRY_DELAY_ITERATIONS: usize = 10; + + for _ in 0..MAX_RETRIES { + let val = + dbi_backend.read(atu_base + PCIE_ATU_UNR_REGION_CTRL2 as PciConfigAddress, 4)?; + if (val as u32) & ATU_ENABLE_BIT != 0 { + return Ok(()); + } + for _ in 0..RETRY_DELAY_ITERATIONS { + core::hint::spin_loop(); + } + } + + hv_result_err!(EBUSY, "Outbound iATU is not being enabled") + } +} diff --git a/src/pci/config_accessors/ecam.rs b/src/pci/config_accessors/ecam.rs new file mode 100644 index 00000000..bbd7cb81 --- /dev/null +++ b/src/pci/config_accessors/ecam.rs @@ -0,0 +1,82 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// + +use alloc::sync::Arc; + +use super::{PciConfigAccessor, PciConfigMmio}; + +use crate::{ + error::HvResult, + pci::{ + pci_struct::{Bdf, RootComplex}, + PciConfigAddress, + }, +}; + +impl RootComplex { + pub fn new_ecam(mmio_base: PciConfigAddress) -> Self { + let accessor = Arc::new(EcamConfigAccessor::new(mmio_base)); + + Self { + mmio_base, + accessor, + } + } +} + +#[derive(Debug)] +pub struct EcamConfigAccessor { + ecam_base: PciConfigAddress, +} + +impl EcamConfigAccessor { + pub fn new(ecam_base: PciConfigAddress) -> Self { + Self { ecam_base } + } +} + +impl PciConfigAccessor for EcamConfigAccessor { + fn get_pci_addr_base(&self, bdf: Bdf) -> HvResult { + let bus = bdf.bus() as PciConfigAddress; + let device = bdf.device() as PciConfigAddress; + let function = bdf.function() as PciConfigAddress; + + let address = self.ecam_base + (bus << 20) + (device << 15) + (function << 12); + Ok(address) + } + + fn get_physical_address( + &self, + bdf: Bdf, + offset: PciConfigAddress, + _parent_bus: u8, + ) -> HvResult { + let bus = bdf.bus() as PciConfigAddress; + let device = bdf.device() as PciConfigAddress; + let function = bdf.function() as PciConfigAddress; + + // ECAM standard address calculation: + // base + (bus << 20) + (device << 15) + (function << 12) + offset + let address = self.ecam_base + (bus << 20) + (device << 15) + (function << 12) + offset; + Ok(address) + } +} + +impl PciConfigMmio { + pub(crate) fn access(&self, offset: PciConfigAddress) -> *mut T { + (self.base + offset) as *mut T + } +} diff --git a/src/pci/config_accessors/loongarch64.rs b/src/pci/config_accessors/loongarch64.rs new file mode 100644 index 00000000..cf60e437 --- /dev/null +++ b/src/pci/config_accessors/loongarch64.rs @@ -0,0 +1,152 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// + +use alloc::sync::Arc; + +use super::{PciConfigAccessor, PciConfigMmio}; + +use crate::{ + error::HvResult, + pci::{ + pci_struct::{Bdf, RootComplex}, + PciConfigAddress, + }, +}; + +impl RootComplex { + pub fn new_loongarch(mmio_base: PciConfigAddress, cfg_size: u64, root_bus: u8) -> Self { + let accessor = Arc::new(LoongArchConfigAccessor::new(mmio_base, cfg_size, root_bus)); + + Self { + mmio_base, + accessor, + } + } +} + +// LoongArch PCIe accessor implementation +#[derive(Debug)] +pub struct LoongArchConfigAccessor { + cfg0: PciConfigAddress, + cfg1: PciConfigAddress, + root_bus: u8, +} + +impl LoongArchConfigAccessor { + pub fn new(cfg_base: PciConfigAddress, cfg_size: u64, root_bus: u8) -> Self { + let cfg_size_half = cfg_size / 2; + let cfg0 = cfg_base; + let cfg1 = cfg_base + cfg_size_half; + Self { + cfg0, + cfg1, + root_bus, + } + } +} + +impl PciConfigAccessor for LoongArchConfigAccessor { + fn get_pci_addr_base(&self, bdf: Bdf) -> HvResult { + let bus = bdf.bus() as PciConfigAddress; + let device = bdf.device() as PciConfigAddress; + let function = bdf.function() as PciConfigAddress; + let offset = 0; + + // Extract Offset[11:8] (bits 11-8 of offset) for bits 31-28 + let offset_high = (offset >> 8) & 0xf; + // Extract Offset[7:0] (bits 7-0 of offset) for bits 7-0 + let offset_low = offset & 0xff; + + let address = if bus == self.root_bus as PciConfigAddress { + // Type 0 format (Root Bus): + // Bits 31-28: Offset[11:8] + // Bits 27-16: Reserved (0) + // Bits 15-11: Device Number + // Bits 10-8: Function Number + // Bits 7-0: Offset[7:0] + self.cfg0 + ((offset_high << 24) | (device << 11) | (function << 8) | offset_low) + } else { + // Type 1 format (Other Bus): + // Bits 31-28: Offset[11:8] + // Bits 27-16: Bus Number + // Bits 15-11: Device Number + // Bits 10-8: Function Number + // Bits 7-0: Offset[7:0] + self.cfg1 + + ((offset_high << 24) + | (bus << 16) + | (device << 11) + | (function << 8) + | offset_low) + }; + + Ok(address) + } + + fn get_physical_address( + &self, + bdf: Bdf, + offset: PciConfigAddress, + _parent_bus: u8, + ) -> HvResult { + let bus = bdf.bus() as PciConfigAddress; + let device = bdf.device() as PciConfigAddress; + let function = bdf.function() as PciConfigAddress; + + // Extract Offset[11:8] (bits 11-8 of offset) for bits 31-28 + let offset_high = (offset >> 8) & 0xf; + // Extract Offset[7:0] (bits 7-0 of offset) for bits 7-0 + let offset_low = offset & 0xff; + + let address = if bus == self.root_bus as PciConfigAddress { + // Type 0 format (Root Bus): + // Bits 31-28: Offset[11:8] + // Bits 27-16: Reserved (0) + // Bits 15-11: Device Number + // Bits 10-8: Function Number + // Bits 7-0: Offset[7:0] + self.cfg0 + ((offset_high << 24) | (device << 11) | (function << 8) | offset_low) + } else { + // Type 1 format (Other Bus): + // Bits 31-28: Offset[11:8] + // Bits 27-16: Bus Number + // Bits 15-11: Device Number + // Bits 10-8: Function Number + // Bits 7-0: Offset[7:0] + self.cfg1 + + ((offset_high << 24) + | (bus << 16) + | (device << 11) + | (function << 8) + | offset_low) + }; + + Ok(address) + } + + fn skip_device(&self, _bdf: Bdf) -> bool { + false + } +} + +pub const HV_ADDR_PREFIX: u64 = 0x8000_0000_0000_0000; +pub const LOONG_HT_PREFIX: u64 = 0xe00_0000_0000; + +impl PciConfigMmio { + pub(crate) fn access(&self, offset: PciConfigAddress) -> *mut T { + (self.base + offset | HV_ADDR_PREFIX | LOONG_HT_PREFIX) as *mut T + } +} diff --git a/src/pci/config_accessors/mod.rs b/src/pci/config_accessors/mod.rs new file mode 100644 index 00000000..544505a6 --- /dev/null +++ b/src/pci/config_accessors/mod.rs @@ -0,0 +1,159 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// + +use core::{any::Any, fmt::Debug}; + +use crate::error::HvResult; +use crate::pci::{pci_struct::Bdf, PciConfigAddress}; + +// PCIe region trait for memory-mapped I/O access +pub trait PciRegion: Debug + Sync + Send + Any { + fn read_u8(&self, offset: PciConfigAddress) -> HvResult; + fn write_u8(&self, offset: PciConfigAddress, value: u8) -> HvResult; + fn read_u16(&self, offset: PciConfigAddress) -> HvResult; + fn write_u16(&self, offset: PciConfigAddress, value: u16) -> HvResult; + fn read_u32(&self, offset: PciConfigAddress) -> HvResult; + fn write_u32(&self, offset: PciConfigAddress, value: u32) -> HvResult; +} + +#[derive(Debug, Clone, Copy)] +pub struct PciConfigMmio { + base: PciConfigAddress, + #[allow(dead_code)] + length: u64, +} + +impl PciConfigMmio { + pub fn new(base: PciConfigAddress, length: u64) -> Self { + Self { base, length } + } + + // placeholder is used only once in the beginning when hvisor init the pci bus + // defined that placeholder is a dummy mmio with base address 0 and length 0 + pub fn is_placeholder(&self) -> bool { + self.base == 0 && self.length == 0 + } +} + +#[derive(Debug, Clone, Copy)] +pub struct PciRegionMmio { + base: PciConfigAddress, + #[allow(dead_code)] + length: u64, +} + +impl PciRegionMmio { + pub fn new(base: PciConfigAddress, length: u64) -> Self { + Self { base, length } + } + + pub(crate) fn access(&self, offset: PciConfigAddress) -> *mut T { + (self.base + offset) as *mut T + } +} + +#[cfg(all( + not(feature = "ecam_pcie"), + not(feature = "dwc_pcie"), + not(feature = "loongarch64_pcie") +))] +impl PciConfigMmio { + pub fn access(&self, offset: PciConfigAddress) -> *mut T { + (self.base + offset) as *mut T + } +} + +impl PciRegion for PciRegionMmio { + fn read_u8(&self, offset: PciConfigAddress) -> HvResult { + unsafe { Ok(self.access::(offset).read_volatile() as u8) } + } + fn write_u8(&self, offset: PciConfigAddress, value: u8) -> HvResult { + unsafe { self.access::(offset).write_volatile(value) } + Ok(()) + } + fn read_u16(&self, offset: PciConfigAddress) -> HvResult { + unsafe { Ok(self.access::(offset).read_volatile() as u16) } + } + fn write_u16(&self, offset: PciConfigAddress, value: u16) -> HvResult { + unsafe { self.access::(offset).write_volatile(value) } + Ok(()) + } + fn read_u32(&self, offset: PciConfigAddress) -> HvResult { + unsafe { Ok(self.access::(offset).read_volatile() as u32) } + } + fn write_u32(&self, offset: PciConfigAddress, value: u32) -> HvResult { + unsafe { self.access::(offset).write_volatile(value) } + Ok(()) + } +} + +impl PciRegion for PciConfigMmio { + fn read_u8(&self, offset: PciConfigAddress) -> HvResult { + unsafe { Ok(self.access::(offset).read_volatile() as u8) } + } + fn write_u8(&self, offset: PciConfigAddress, value: u8) -> HvResult { + unsafe { self.access::(offset).write_volatile(value) } + Ok(()) + } + fn read_u16(&self, offset: PciConfigAddress) -> HvResult { + unsafe { Ok(self.access::(offset).read_volatile() as u16) } + } + fn write_u16(&self, offset: PciConfigAddress, value: u16) -> HvResult { + unsafe { self.access::(offset).write_volatile(value) } + Ok(()) + } + fn read_u32(&self, offset: PciConfigAddress) -> HvResult { + unsafe { Ok(self.access::(offset).read_volatile() as u32) } + } + fn write_u32(&self, offset: PciConfigAddress, value: u32) -> HvResult { + unsafe { self.access::(offset).write_volatile(value) } + Ok(()) + } +} + +pub trait PciConfigAccessor: Send + Sync + core::fmt::Debug { + fn get_pci_addr_base(&self, bdf: Bdf) -> HvResult; + + fn get_physical_address( + &self, + bdf: Bdf, + offset: PciConfigAddress, + _parent_bus: u8, + ) -> HvResult; + + fn skip_device(&self, _bdf: Bdf) -> bool { + false + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum PciAccessorType { + Ecam, + Dwc, + LoongArch, +} + +// Export accessor implementations +#[cfg(feature = "ecam_pcie")] +pub mod ecam; + +#[cfg(feature = "dwc_pcie")] +pub mod dwc; +#[cfg(feature = "dwc_pcie")] +pub mod dwc_atu; + +#[cfg(feature = "loongarch64_pcie")] +pub mod loongarch64; diff --git a/src/pci/endpoint.rs b/src/pci/endpoint.rs deleted file mode 100644 index d069afa6..00000000 --- a/src/pci/endpoint.rs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) 2025 Syswonder -// hvisor is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR -// FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. -// -// Syswonder Website: -// https://www.syswonder.org -// -// Authors: -// -use alloc::vec::Vec; -use core::ptr::read_volatile; - -use super::{ - cfg_base, cfg_reg_addr, - pcibar::{BarRegion, PciBar, VirtPciBar}, - phantom_cfg::{PhantomCfg, PhantomCfgType}, - CFG_CAP_PTR_OFF, CFG_EXT_CAP_ID, CFG_EXT_CAP_PTR_OFF, CFG_NEXT_EXT_CAP_OFF, CFG_SRIOV_CAP_ID, - NUM_BAR_REGS_TYPE0, NUM_MAX_BARS, -}; - -#[derive(Debug)] -pub struct EndpointConfig { - bars: [PciBar; NUM_BAR_REGS_TYPE0], - pub bdf: usize, - pub node_before_sriov: usize, - pub node_after_sriov: usize, -} - -impl EndpointConfig { - pub fn new(bdf: usize) -> Self { - let (bars, bdf) = { ([PciBar::default(); NUM_BAR_REGS_TYPE0], bdf) }; - let mut r = EndpointConfig { - bars, - bdf, - node_before_sriov: 0xfff, - node_after_sriov: 0xfff, - }; - if r.ext_cap_exists() { - r.find_sriov(); - } - r - } - - pub fn ext_cap_exists(&self) -> bool { - let cap_ptr_addr = cfg_reg_addr(self.bdf, CFG_CAP_PTR_OFF); - let mut cur_cap_ptr = unsafe { read_volatile(cap_ptr_addr as *const u8) } as usize; - - if cur_cap_ptr == 0 { - return false; - } - - while cur_cap_ptr != 0 { - let cap_addr = cfg_reg_addr(self.bdf, cur_cap_ptr); - let cap_val = unsafe { read_volatile(cap_addr as *const u16) }; - - let cap_id = (cap_val & 0xff) as u8; - let next_cap_ptr = ((cap_val >> 8) & 0xff) as usize; - - if (cap_id as usize) == CFG_EXT_CAP_ID { - info!( - "{:x}:{:x}.{:x} is a PCI Express device!", - self.bdf >> 8, - (self.bdf >> 3) & 0b11111, - self.bdf & 0b111 - ); - return true; - } - - cur_cap_ptr = next_cap_ptr; - } - - false - } - - pub fn find_sriov(&mut self) { - info!("finding sriov"); - - let mut prev_cap_ptr = 0; - let mut curr_cap_ptr = CFG_EXT_CAP_PTR_OFF; // start from 0x100 - - // init to invalid offset value, to check if we find sriov - self.node_before_sriov = 0xfff; - self.node_after_sriov = 0xfff; - - while curr_cap_ptr != 0 { - let cap_addr = cfg_reg_addr(self.bdf, curr_cap_ptr); - let cap_val = unsafe { read_volatile(cap_addr as *const u32) }; // each ext cap is 8 bytes - - let cap_id = (cap_val & 0xffff) as u16; - let next_cap_ptr = ((cap_val >> 20) & 0xfff) as usize; - - if (cap_id as usize) == CFG_SRIOV_CAP_ID { - self.node_before_sriov = prev_cap_ptr; - self.node_after_sriov = next_cap_ptr; - info!( - "{:x}:{:x}.{:x} SR-IOV off: {:#x}, prev_node_off: {:#x}, next_node_off: {:#x}", - self.bdf >> 8, - (self.bdf >> 3) & 0b11111, - self.bdf & 0b111, - curr_cap_ptr, - prev_cap_ptr, - next_cap_ptr - ); - break; - } - - prev_cap_ptr = curr_cap_ptr; - curr_cap_ptr = next_cap_ptr; - } - } - - pub fn skip_sriov(&self, cur_cap_hdr: usize) -> usize { - (cur_cap_hdr & 0x000fffff) | (self.node_after_sriov << CFG_NEXT_EXT_CAP_OFF) - } - - pub fn bars_init(&mut self, bar_id: usize, origin_val: u32, val: u32) { - self.bars[bar_id].init(origin_val, val); - } - - pub fn get_regions(&self) -> Vec { - let mut regions: Vec = Vec::new(); - let mut bar_id = 0; - while bar_id < NUM_BAR_REGS_TYPE0 { - if self.bars[bar_id].is_mutable() { - if !self.bars[bar_id].mem_type_64() { - regions.push(self.bars[bar_id].get_32b_region()); - bar_id += 1; - } else { - regions.push( - self.bars[bar_id + 1].get_64b_region(self.bars[bar_id].get_32b_region()), - ); - bar_id += 2; - } - } else { - bar_id += 1; - } - } - regions - } - - // after we get bar regions, we should generate a virtual device instance that mirrors this device for use by other VMs - pub fn generate_vep(&self) -> PhantomCfg { - let mut v_bars: [VirtPciBar; NUM_MAX_BARS] = [VirtPciBar::default(); NUM_MAX_BARS]; - for i in 0..NUM_BAR_REGS_TYPE0 { - v_bars[i] = self.bars[i].generate_vbar(); - } - PhantomCfg::new(self.bdf, v_bars, PhantomCfgType::ENDPOINT) - } -} diff --git a/src/pci/mem_alloc.rs b/src/pci/mem_alloc.rs new file mode 100644 index 00000000..c55fed80 --- /dev/null +++ b/src/pci/mem_alloc.rs @@ -0,0 +1,153 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// + +use core::{fmt::Debug, ops::Range}; + +pub type Mem32Address = u64; +pub type Mem64Address = u64; + +trait Algin { + fn align_up(self, align: Self) -> Self; +} + +impl Algin for Mem32Address { + fn align_up(self, align: Self) -> Self { + (self + align - 1) & !(align - 1) + } +} + +// impl Algin for Mem64Address { +// fn align_up(self, align: Self) -> Self { +// (self + align - 1) & !(align - 1) +// } +// } + +pub trait BarAllocator: Debug { + fn alloc_memory32(&mut self, size: Mem32Address) -> Option; + fn alloc_memory64(&mut self, size: Mem64Address) -> Option; + fn alloc_io(&mut self, size: Mem64Address) -> Option; +} + +#[derive(Default, Debug)] +pub struct BaseAllocator { + mem32: Range, + mem32_used: Mem32Address, + mem64: Range, + mem64_used: Mem64Address, + io: Range, + io_used: Mem64Address, +} + +impl BaseAllocator { + pub fn set_mem32(&mut self, start: Mem32Address, size: Mem32Address) { + self.mem32 = start..start + size; + self.mem32_used = start; + } + + pub fn set_mem64(&mut self, start: Mem64Address, size: Mem64Address) { + self.mem64 = start..start + size; + self.mem64_used = start; + } + + pub fn set_io(&mut self, start: Mem64Address, size: Mem64Address) { + self.io = start..start + size; + self.io_used = start; + } +} + +impl BarAllocator for BaseAllocator { + fn alloc_memory32(&mut self, size: Mem32Address) -> Option { + let ptr = self.mem32_used.align_up(size); + + if self.mem32.contains(&ptr) && ptr + size <= self.mem32.end { + self.mem32_used = ptr + size; + // debug!("alloc mem32 {:x} {}", ptr, size); + Some(ptr) + } else { + None + } + } + + fn alloc_memory64(&mut self, size: Mem64Address) -> Option { + let ptr = self.mem64_used.align_up(size); + if self.mem64.contains(&ptr) && ptr + size <= self.mem64.end { + self.mem64_used = ptr + size; + // debug!("alloc mem64 {:x} {}", ptr, size); + Some(ptr) + } else { + None + } + } + + fn alloc_io(&mut self, _size: Mem64Address) -> Option { + warn!("alloc io not supported"); + None + } +} + +// #[derive(Default, Debug)] +// pub struct LoongArchAllocator { +// mem: Range, +// mem_used: Mem64Address, +// io: Range, +// io_used: Mem64Address, +// } + +// impl LoongArchAllocator { +// pub fn set_mem(&mut self, start: Mem64Address, size: Mem64Address) { +// self.mem = start..start + size; +// self.mem_used = start; +// } + +// pub fn set_io(&mut self, start: Mem64Address, size: Mem64Address) { +// self.io = start..start + size; +// self.io_used = start; +// } +// } + +// impl BarAllocator for LoongArchAllocator { +// fn alloc_memory32(&mut self, size: Mem32Address) -> Option { +// let ptr = self.mem_used.align_up(size); +// if self.mem.contains(&ptr) && ptr + size <= self.mem.end { +// self.mem_used = ptr + size; +// // debug!("alloc mem64 {:x} {}", ptr, size); +// Some(ptr) +// } else { +// None +// } +// } + +// fn alloc_memory64(&mut self, size: Mem64Address) -> Option { +// let ptr = self.mem_used.align_up(size); +// if self.mem.contains(&ptr) && ptr + size <= self.mem.end { +// self.mem_used = ptr + size; +// // debug!("alloc mem64 {:x} {}", ptr, size); +// Some(ptr) +// } else { +// None +// } +// } + +// fn alloc_io(&mut self, size: Mem64Address) -> Option { +// let ptr = self.io_used.align_up(size); +// if self.io.contains(&ptr) && ptr + size <= self.io.end { +// self.io_used = ptr + size; +// Some(ptr) +// } else { +// None +// } +// } +// } diff --git a/src/pci/mod.rs b/src/pci/mod.rs index 203999af..7217cce5 100644 --- a/src/pci/mod.rs +++ b/src/pci/mod.rs @@ -13,96 +13,17 @@ // // Authors: // -use spin::Once; -pub mod bridge; -pub mod endpoint; -pub mod pci; -pub mod pcibar; -pub mod phantom_cfg; +#![allow(dead_code)] +pub mod config_accessors; +pub mod mem_alloc; +pub mod pci_access; +pub mod pci_config; +pub mod pci_handler; +pub mod pci_struct; +pub mod vpci_dev; -pub const CFG_CMD_OFF: usize = 0x4; //status -pub const CFG_CAP_PTR_OFF: usize = 0x34; // capabilities pointer -pub const CFG_EXT_CAP_PTR_OFF: usize = 0x100; // extended capabilities pointer -pub const CFG_NEXT_EXT_CAP_OFF: usize = 20; -pub const CFG_CLASS_CODE_OFF: usize = 0x8; // 4 bytes, include revision and class code -pub const CFG_SRIOV_CAP_ID: usize = 0x0010; -pub const CFG_EXT_CAP_ID: usize = 0x10; -pub const CFG_BAR0: usize = 0x10; -pub const CFG_BAR1: usize = 0x14; -pub const CFG_BAR2: usize = 0x18; -pub const CFG_BAR3: usize = 0x1c; -pub const CFG_BAR4: usize = 0x20; -pub const CFG_BAR5: usize = 0x24; -pub const CFG_PRIMARY_BUS: usize = 0x18; -pub const CFG_SECONDARY_BUS: usize = 0x19; -pub const CFG_IO_BASE: usize = 0x1c; -pub const CFG_IO_LIMIT: usize = 0x1d; -pub const CFG_MEM_BASE: usize = 0x20; -pub const CFG_MEM_LIMIT: usize = 0x22; -pub const CFG_PREF_MEM_BASE: usize = 0x24; -pub const CFG_PREF_MEM_LIMIT: usize = 0x26; -pub const CFG_PREF_BASE_UPPER32: usize = 0x28; -pub const CFG_PREF_LIMIT_UPPER32: usize = 0x2c; -pub const CFG_IO_BASE_UPPER16: usize = 0x30; -pub const CFG_IO_LIMIT_UPPER16: usize = 0x32; -pub const CFG_INT_LINE: usize = 0x3d; -pub const CFG_INT_PIN: usize = 0x3d; +#[cfg(test)] +pub mod pci_test; -pub const NUM_BAR_REGS_TYPE0: usize = 6; -pub const NUM_BAR_REGS_TYPE1: usize = 2; -pub const NUM_MAX_BARS: usize = 6; -pub const PHANTOM_DEV_HEADER: u32 = 0x77777777u32; - -pub static ECAM_BASE: Once = Once::new(); - -pub static BDF_SHIFT: Once = Once::new(); - -pub fn init_ecam_base(ecam_base: usize) { - ECAM_BASE.call_once(|| ecam_base); -} - -pub fn get_ecam_base() -> usize { - *ECAM_BASE.get().unwrap() as _ -} - -pub fn init_bdf_shift(bdf_shift: usize) { - BDF_SHIFT.call_once(|| bdf_shift); -} - -pub fn get_bdf_shift() -> usize { - *BDF_SHIFT.get().unwrap() as _ -} - -pub fn cfg_base(bdf: usize) -> usize { - let shift = get_bdf_shift(); - if cfg!(all(target_arch = "loongarch64", feature = "pci")) && ((bdf >> 8) != 0) { - get_ecam_base() + (bdf << shift) + 0x1000_0000 - } else { - get_ecam_base() + (bdf << shift) - } -} - -// generate addr with reg addr, example off = 0x123, shift = 0x8 -pub fn cfg_reg_addr(bdf: usize, off: usize) -> usize { - let base = cfg_base(bdf); - let shift = get_bdf_shift(); - let upper_off = off >> shift; // 0x1 - let lower_off = off & ((1 << shift) - 1); // 0x23 - let addr = (upper_off << (shift + 16)) + base + lower_off; - addr -} - -/// Extracts the PCI config space register offset, compatible with architectures where the offset layout is split (e.g., LoongArch). -/// Low bits are taken from address[0..bdf_shift), high bits from address[(bdf_shift + 16)..). -fn extract_reg_addr(addr: usize) -> usize { - let bdf_shift = get_bdf_shift(); - let low_mask = (1usize << bdf_shift) - 1; - let low_bits = addr & low_mask; - - let high_shift = bdf_shift + 16; - let high_mask = (1usize << (12 - bdf_shift)) - 1; - let high_bits = ((addr >> high_shift) & high_mask) << bdf_shift; - - high_bits | low_bits -} +pub type PciConfigAddress = u64; diff --git a/src/pci/pci.rs b/src/pci/pci.rs deleted file mode 100644 index 5ab53bbc..00000000 --- a/src/pci/pci.rs +++ /dev/null @@ -1,450 +0,0 @@ -// Copyright (c) 2025 Syswonder -// hvisor is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR -// FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. -// -// Syswonder Website: -// https://www.syswonder.org -// -// Authors: -// -use crate::config::{HvPciConfig, CONFIG_MAX_PCI_DEV}; -use crate::memory::addr::align_down; -use crate::memory::mmio_perform_access; -use crate::pci::pcibar::BarType; -use crate::pci::phantom_cfg::find_phantom_dev; -use crate::pci::{get_ecam_base, init_bdf_shift, init_ecam_base}; -use crate::percpu::this_zone; -use crate::{ - error::HvResult, - memory::MMIOAccess, - memory::{GuestPhysAddr, MemFlags, MemoryRegion}, - zone::Zone, -}; -use alloc::vec::Vec; -use core::ptr::{read_volatile, write_volatile}; -use core::{panic, ptr, usize}; - -use super::bridge::BridgeConfig; -use super::endpoint::EndpointConfig; -use super::pcibar::BarRegion; -use super::phantom_cfg::{add_phantom_devices, generate_vep_by_bdf, PhantomCfg}; -use super::{ - cfg_base, extract_reg_addr, get_bdf_shift, CFG_EXT_CAP_PTR_OFF, NUM_BAR_REGS_TYPE0, - NUM_BAR_REGS_TYPE1, -}; -use crate::arch::consts::{BDF_SHIFT, HV_ADDR_PREFIX, LOONG_HT_PREFIX}; -use crate::arch::iommu::iommu_add_device; -#[derive(Debug)] -pub struct PciRoot { - endpoints: Vec, - bridges: Vec, - alloc_devs: Vec, // include host bridge - phantom_devs: Vec, - pub bar_regions: Vec, -} -impl PciRoot { - pub fn new() -> Self { - let r = Self { - endpoints: Vec::new(), - bridges: Vec::new(), - alloc_devs: Vec::new(), - phantom_devs: Vec::new(), - bar_regions: Vec::new(), - }; - r - } - - pub fn is_assigned_device(&self, bdf: usize) -> bool { - if self.alloc_devs.contains(&bdf) { - true - } else { - false - } - } - - pub fn is_bridge(&self, bdf: usize) -> bool { - match self.bridges.iter().find(|&b| b.bdf == bdf) { - Some(b) => true, - None => false, - } - } - - pub fn bars_register(&mut self) { - self.ep_bars_init(); - self.bridge_bars_init(); - self.get_bars_regions(); - } - - pub fn generate_vdevs(&self) { - for ep in self.endpoints.iter() { - add_phantom_devices(ep.generate_vep()); - } - for bridge in self.bridges.iter() { - add_phantom_devices(bridge.generate_vbridge()); - } - } - - fn get_bars_regions(&mut self) { - for ep in self.endpoints.iter() { - let regions = ep.get_regions(); - for mut region in regions { - if region.size < 0x1000 { - // unnecessary unless you use qemu pci-test-dev - region.size = 0x1000; - } - self.bar_regions.push(region); - } - } - for bridge in self.bridges.iter() { - let regions = bridge.get_regions(); - for mut region in regions { - if region.size < 0x1000 { - region.size = 0x1000; - } - self.bar_regions.push(region); - } - } - info!("PCI BAR regions init done"); - } - - fn ep_bars_init(&mut self) { - for ep in self.endpoints.iter_mut() { - let cfg_base = cfg_base(ep.bdf); - let offsets: [usize; NUM_BAR_REGS_TYPE0] = [0x10, 0x14, 0x18, 0x1c, 0x20, 0x24]; - for bar_id in 0..NUM_BAR_REGS_TYPE0 { - unsafe { - let reg_ptr = (cfg_base + offsets[bar_id]) as *mut u32; - let origin_val = read_volatile(reg_ptr); - write_volatile(reg_ptr, 0xffffffffu32); - let new_val = read_volatile(reg_ptr); - ep.bars_init(bar_id, origin_val, new_val); - write_volatile(reg_ptr, origin_val); - } - } - } - } - - fn bridge_bars_init(&mut self) { - for bridge in self.bridges.iter_mut() { - let cfg_base = cfg_base(bridge.bdf); - let offsets: [usize; NUM_BAR_REGS_TYPE1] = [0x10, 0x14]; - for bar_id in 0..NUM_BAR_REGS_TYPE1 { - unsafe { - let reg_ptr = (cfg_base + offsets[bar_id]) as *mut u32; - let origin_val = read_volatile(reg_ptr); - write_volatile(reg_ptr, 0xffffffffu32); - let new_val = read_volatile(reg_ptr); - bridge.bars_init(bar_id, origin_val, new_val); - write_volatile(reg_ptr, origin_val); - } - } - } - } -} - -impl Zone { - pub fn pci_init( - &mut self, - pci_config: &HvPciConfig, - num_pci_devs: usize, - alloc_pci_devs: &[u64; CONFIG_MAX_PCI_DEV], - ) { - #[cfg(not(feature = "pci"))] - { - info!("PCIe feature is not enabled, skipping PCIe initialization."); - return; - } - - if num_pci_devs == 0 { - return; - } - info!("PCIe init!"); - - let hv_addr_prefix: u64 = HV_ADDR_PREFIX; - let loong_ht_prefix: u64 = LOONG_HT_PREFIX; - let bdf_shift: usize = BDF_SHIFT; - - init_bdf_shift(bdf_shift); - - init_ecam_base((pci_config.ecam_base + hv_addr_prefix + loong_ht_prefix) as _); - - info!("PCIe ECAM base: {:#x}", get_ecam_base()); - - for idx in 0..num_pci_devs { - info!( - "PCIe device assigned to zone {}: {:#x}:{:#x}.{:#x}", - self.id, - alloc_pci_devs[idx] >> 8, - (alloc_pci_devs[idx] >> 3) & 0b11111, - alloc_pci_devs[idx] & 0b111 - ); - self.pciroot.alloc_devs.push(alloc_pci_devs[idx] as _); - #[cfg(any( - all(feature = "iommu", target_arch = "aarch64"), - target_arch = "x86_64" - ))] - if alloc_pci_devs[idx] != 0 { - let iommu_pt_addr = if self.iommu_pt.is_some() { - self.iommu_pt.as_ref().unwrap().root_paddr() - } else { - 0 - }; - iommu_add_device(self.id, alloc_pci_devs[idx] as _, iommu_pt_addr); - } - } - - if self.id == 0 { - self.root_pci_init(pci_config, hv_addr_prefix, loong_ht_prefix); - } else { - self.virtual_pci_mmio_init(pci_config, hv_addr_prefix, loong_ht_prefix); - } - self.virtual_pci_device_init(pci_config); - } - - pub fn root_pci_init( - &mut self, - pci_config: &HvPciConfig, - hv_addr_prefix: u64, - loong_ht_prefix: u64, - ) { - // Virtual ECAM - - self.mmio_region_register( - pci_config.ecam_base as _, - pci_config.ecam_size as _, - mmio_pci_handler, - (pci_config.ecam_base + hv_addr_prefix + loong_ht_prefix) as _, - ); - - // self.gpm.insert(MemoryRegion::new_with_offset_mapper( - // pci_config.ecam_base as GuestPhysAddr, - // (pci_config.ecam_base + loong_ht_prefix) as _, - // pci_config.ecam_size as _, - // MemFlags::READ | MemFlags::WRITE | MemFlags::IO, - // )) - // .ok(); - - info!( - "pci handler args : {:#x}", - pci_config.ecam_base + hv_addr_prefix + loong_ht_prefix - ); - - if pci_config.io_size != 0 { - self.gpm - .insert(MemoryRegion::new_with_offset_mapper( - pci_config.io_base as GuestPhysAddr, - pci_config.io_base as _, - pci_config.io_size as _, - MemFlags::READ | MemFlags::WRITE | MemFlags::IO, - )) - .ok(); - } - - if pci_config.mem32_size != 0 { - self.gpm - .insert(MemoryRegion::new_with_offset_mapper( - pci_config.mem32_base as GuestPhysAddr, - pci_config.mem32_base as _, - pci_config.mem32_size as _, - MemFlags::READ | MemFlags::WRITE | MemFlags::IO, - )) - .ok(); - } - - if pci_config.mem64_size != 0 { - self.gpm - .insert(MemoryRegion::new_with_offset_mapper( - pci_config.mem64_base as GuestPhysAddr, - pci_config.mem64_base as _, - pci_config.mem64_size as _, - MemFlags::READ | MemFlags::WRITE | MemFlags::IO, - )) - .ok(); - } - } - - //probe pci mmio - pub fn virtual_pci_mmio_init( - &mut self, - pci_config: &HvPciConfig, - hv_addr_prefix: u64, - loong_ht_prefix: u64, - ) { - self.mmio_region_register( - pci_config.ecam_base as _, - pci_config.ecam_size as _, - mmio_pci_handler, - (pci_config.ecam_base + hv_addr_prefix + loong_ht_prefix) as _, - ); - - if pci_config.io_size != 0 { - self.mmio_region_register( - pci_config.io_base as _, - pci_config.io_size as _, - mmio_pci_bar_handler, - (pci_config.io_base + hv_addr_prefix) as _, - ); - } - - if pci_config.mem32_size != 0 { - self.mmio_region_register( - pci_config.mem32_base as _, - pci_config.mem32_size as _, - mmio_pci_bar_handler, - (pci_config.mem32_base + hv_addr_prefix) as _, - ); - } - - if pci_config.mem64_size != 0 { - self.mmio_region_register( - pci_config.mem64_base as _, - pci_config.mem64_size as _, - mmio_pci_bar_handler, - (pci_config.mem64_base + hv_addr_prefix) as _, - ); - } - - info!("PCIe MMIO init done!"); - } - - pub fn virtual_pci_device_init(&mut self, pci_config: &HvPciConfig) { - for bdf in self.pciroot.alloc_devs.clone() { - if bdf != 0 { - let base = cfg_base(bdf) + 0xe; - let header_val = unsafe { ptr::read_volatile(base as *mut u8) }; - match header_val & 0b1111111 { - 0b0 => self.pciroot.endpoints.push(EndpointConfig::new(bdf)), - 0b1 => self.pciroot.bridges.push(BridgeConfig::new(bdf)), - _ => error!( - "bdf {:#x} unsupported device type: {}!", - bdf, - header_val & 0b1111111 - ), - }; - } else { - // host bridge - self.pciroot.bridges.push(BridgeConfig::new(bdf)); - } - } - - trace!("pciroot = {:?}", self.pciroot); - self.pciroot.bars_register(); - if self.id != 0 { - self.pci_bars_register(pci_config); - } - self.pciroot.generate_vdevs(); - } - - pub fn pci_bars_register(&mut self, pci_config: &HvPciConfig) { - for region in self.pciroot.bar_regions.iter_mut() { - let (cpu_base, pci_base) = match region.bar_type { - BarType::IO => (pci_config.io_base as usize, pci_config.pci_io_base as usize), - BarType::Mem32 => ( - pci_config.mem32_base as usize, - pci_config.pci_mem32_base as usize, - ), - BarType::Mem64 => ( - pci_config.mem64_base as usize, - pci_config.pci_mem64_base as usize, - ), - _ => panic!("Unknown BAR type!"), - }; - - region.arch_set_bar_region_start(cpu_base, pci_base); - - info!( - "pci bar region: type: {:?}, base: {:#x}, size: {:#x}", - region.bar_type, region.start, region.size - ); - - region.arch_insert_bar_region(&mut self.gpm, self.id); - } - } -} - -pub fn mmio_pci_handler(mmio: &mut MMIOAccess, base: usize) -> HvResult { - // info!("mmio pci: {:#x}", mmio.address); - let zone = this_zone(); - let mut binding = zone.write(); - let zone_id = binding.id; - - let reg_addr = extract_reg_addr(mmio.address); - let bdf_shift = get_bdf_shift(); - let bdf = (mmio.address >> bdf_shift) & 0xffff; - let bus = (mmio.address >> 8) & 0xff; - let dev = (mmio.address >> 3) & 0x1f; - let func = mmio.address & 0x7; - - let is_assigned = binding.pciroot.is_assigned_device(bdf); - let is_bridge = binding.pciroot.is_bridge(bdf); - - match is_assigned { - true => { - mmio_perform_access(base, mmio); - if bus == 6 && reg_addr == 0x150 && !mmio.is_write { - // assume pcie network card is in bus 6(X4 slot in 3A6000 board), this will skip it's sriov - mmio.value = mmio.value & 0x00ffffff; - mmio.value += 0x1a000000; - } - // if (reg_addr >= CFG_EXT_CAP_PTR_OFF) && !is_bridge { - // mmio.value = match binding.pciroot.endpoints.iter().find(|&ep| ep.bdf == bdf) { - // Some(ep) => ep.skip_sriov(mmio.value), - // None => { - // error!("Endpoint {:x}:{:x}.{:x} doesn't exist!", bdf >> 8, (bdf >> 3) &0b11111, bdf & 0b111); - // mmio.value - // } - // } - // } - return Ok(()); - } - false => { - let header_addr = cfg_base(bdf); - let header_val = unsafe { ptr::read_volatile(header_addr as *mut u32) }; - if header_val == 0xffffffffu32 || header_val == 0 { - if reg_addr == 0 && mmio.is_write == false { - mmio.value = header_val as _; - return Ok(()); - } else { - #[cfg(not(target_arch = "x86_64"))] - panic!("invalid access to empty device {:x}:{:x}.{:x}, addr: {:#x}, reg_addr: {:#x}!", bdf >> 8, (bdf >> 3) & 0b11111, bdf & 0b111, mmio.address, reg_addr); - // in x86, linux will probe for pci devices automatically - #[cfg(target_arch = "x86_64")] - return Ok(()); - } - } else { - // device exists, so we try to get the phantom device - let pdev = match binding - .pciroot - .phantom_devs - .iter_mut() - .find(|dev| dev.bdf == bdf) - { - Some(dev) => dev, - None => { - let new_dev = find_phantom_dev(bdf); - binding.pciroot.phantom_devs.push(new_dev); - binding - .pciroot - .phantom_devs - .iter_mut() - .find(|dev| dev.bdf == bdf) - .unwrap() - } - }; - pdev.phantom_mmio_handler(mmio, base, zone_id) - } - } - } -} - -pub fn mmio_pci_bar_handler(mmio: &mut MMIOAccess, base: usize) -> HvResult { - panic!("mmio pci bar: {:#x}", mmio.address + base); - mmio_perform_access(base, mmio); - Ok(()) -} diff --git a/src/pci/pci_access.rs b/src/pci/pci_access.rs new file mode 100644 index 00000000..d2bff12c --- /dev/null +++ b/src/pci/pci_access.rs @@ -0,0 +1,1192 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// + +// #![allow(dead_code)] +use bit_field::BitField; +use bitflags::bitflags; +use core::{ + fmt::Debug, + ops::{Index, IndexMut}, + slice, +}; + +use super::{ + config_accessors::{PciConfigMmio, PciRegion}, + PciConfigAddress, +}; + +use crate::{ + error::HvResult +}; + +pub type VendorId = u16; +pub type DeviceId = u16; +pub type DeviceRevision = u8; +pub type BaseClass = u8; +pub type SubClass = u8; +pub type Interface = u8; +pub type SubsystemId = u16; +pub type SubsystemVendorId = u16; +pub type InterruptLine = u8; +pub type InterruptPin = u8; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HeaderType { + Endpoint, + PciBridge, + CardBusBridge, + Unknown(u8), +} + +bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct PciStatus: u16 { + const DETECTED_PARITY_ERROR = 1 << 15; + const SIGNALED_SYSTEM_ERROR = 1 << 14; + const RECEIVED_MASTER_ABORT = 1 << 13; + const RECEIVED_TARGET_ABORT = 1 << 12; + const SIGNALED_TARGET_ABORT = 1 << 11; + const DEVSEL_MASK = 0b11 << 9; + const MASTER_PARITY_ERROR = 1 << 8; + const FAST_BACK_TO_BACK = 1 << 7; + // resersed bit 6 + const CAP_66MHZ = 1 << 5; + const CAPABILITIES_LIST = 1 << 4; + const INTERRUPT_STATUS = 1 << 3; + // resersed bit 0-2 + const _ = !0; + } +} + +bitflags::bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct PciCommand: u16 { + const IO_ENABLE = 1 << 0; + const MEMORY_ENABLE = 1 << 1; + const BUS_MASTER_ENABLE = 1 << 2; + const SPECIAL_CYCLE_ENABLE = 1 << 3; + const MEMORY_WRITE_AND_INVALIDATE = 1 << 4; + const VGA_PALETTE_SNOOP = 1 << 5; + const PARITY_ERROR_RESPONSE = 1 << 6; + const IDSEL_STEP_WAIT_CYCLE_CONTROL = 1 << 7; + const SERR_ENABLE = 1 << 8; + const FAST_BACK_TO_BACK_ENABLE = 1 << 9; + const INTERRUPT_DISABLE = 1 << 10; + const _ = !0; + } +} + +#[derive(Default, Clone, Copy, PartialEq, Eq)] +pub enum PciMemType { + Mem32, + Mem64High, + Mem64Low, + Io, + Rom, + #[default] + Unused, +} + +impl Debug for PciMemType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + PciMemType::Mem32 => write!(f, "Mem32"), + PciMemType::Mem64High => write!(f, "Mem64High"), + PciMemType::Mem64Low => write!(f, "Mem64Low"), + PciMemType::Io => write!(f, "IO"), + PciMemType::Unused => write!(f, "Unused"), + PciMemType::Rom => write!(f, "Rom"), + } + } +} + +/* PciMem + * virtaul_value: the vaddr guset zone can rw, same with as the corresponding value in virtualconfigspace.space + * value: the paddr which hvisor and hw can rw, init when hvisor init the pci bus + * size: the size of mem region, when size_read is true return !(size - 1) + * + * size_read: if software write 0xffff_ffff to bar, size_read will set so next time hvisor can rerturn !(size - 1) indicating size to the software + * acutally size_read is not used now, we keep it just in case of possible special register handling in the future + */ +#[derive(Default, Clone, Copy)] +pub struct PciMem { + bar_type: PciMemType, + virtual_value: u64, + value: u64, + size: u64, + prefetchable: bool, + size_read: bool, +} + +impl PciMem { + pub fn new_bar(bar_type: PciMemType, value: u64, size: u64, prefetchable: bool) -> Self { + Self { + bar_type, + virtual_value: 0, + value, + size, + prefetchable, + size_read: false, + } + } + + pub fn new_io(value: u64, size: u64) -> Self { + Self { + bar_type: PciMemType::Io, + virtual_value: 0, + value, + size, + prefetchable: false, + size_read: false, + } + } + + pub fn init(value: u64, size: u64) -> Self { + Self { + bar_type: PciMemType::Unused, + virtual_value: 0, + value, + size, + prefetchable: false, + size_read: false, + } + } + + pub fn new_rom(value: u64, size: u64) -> Self { + Self { + bar_type: PciMemType::Rom, + virtual_value: 0, + value, + size, + prefetchable: false, + size_read: false, + } + } + + pub fn set_size(&mut self, size: u64) { + self.size = size; + } + + pub fn get_size(&self) -> u64 { + self.size + } + + pub fn get_size_with_flag(&self) -> u64 { + match self.bar_type { + PciMemType::Mem32 | PciMemType::Rom | PciMemType::Io => !(self.size - 1u64), + PciMemType::Mem64Low => { + let bar_size = !(self.size - 1); + bar_size.get_bits(0..32) + } + PciMemType::Mem64High => { + let bar_size = !(self.size - 1); + bar_size.get_bits(32..64) >> 32 + } + PciMemType::Unused => { + /* for unused bar, size is 0 + */ + 0 + } + // _ => { + // warn!("{:#?} not support size", self.bar_type); + // 0 + // } + } + } + + pub fn set_bar_type(&mut self, bar_type: PciMemType) { + self.bar_type = bar_type; + } + + pub fn set_prefetchable(&mut self, prefetchable: bool) { + self.prefetchable = prefetchable; + } + + pub fn config_init(&mut self, bar_type: PciMemType, prefetchable: bool, size: u64, value: u64) { + self.set_bar_type(bar_type); + self.set_prefetchable(prefetchable); + self.set_size(size); + self.set_value(value); + } + + pub fn is_enabled(&self) -> bool { + if self.bar_type == PciMemType::default() { + false + } else { + true + } + } + + /* the longest of mmio read is 32 */ + pub fn get_value(&self) -> u32 { + match self.bar_type { + PciMemType::Mem64High => (self.value >> 32) as u32, + _ => self.value as u32, + } + } + + /* when update map of bar region, + * need to read u64 to get whole address + * the virtual_value is same with value + */ + pub fn get_value64(&self) -> u64 { + self.value as u64 + } + + /* Automatically add flags */ + pub fn set_value(&mut self, address: u64) { + let mut val = address; + + match self.bar_type { + PciMemType::Io => { + // bit0 = 1 + val |= 0x1; + } + PciMemType::Mem32 => { + // bit1..2 = 00 + val &= !0x6; + if self.prefetchable { + val |= 0x8; + } + } + PciMemType::Mem64Low | PciMemType::Mem64High => { + // bit1..=2 = 0b10 + val &= !0x6; + val |= 0x4; + if self.prefetchable { + val |= 0x8; + } + } + PciMemType::Rom => {} + _ => { + warn!("please init bar first"); + } + } + + self.value = val; + } + + pub fn get_type(&self) -> PciMemType { + self.bar_type + } + + pub fn get_prefetchable(&self) -> bool { + self.prefetchable + } + + pub fn set_size_read(&mut self) { + self.size_read = true; + } + + pub fn clear_size_read(&mut self) { + self.size_read = false; + } + + pub fn get_size_read(&self) -> bool { + self.size_read + } + + pub fn get_virtual_value(&self) -> u32 { + match self.bar_type { + PciMemType::Mem64High => (self.virtual_value >> 32) as u32, + _ => self.virtual_value as u32, + } + } + + pub fn get_virtual_value64(&self) -> u64 { + self.virtual_value + } + + pub fn set_virtual_value(&mut self, address: u64) { + let mut val = address; + + match self.bar_type { + PciMemType::Io => { + // bit0 = 1 + val |= 0x1; + } + PciMemType::Mem32 => { + // bit1..2 = 00 + val &= !0x6; + if self.prefetchable { + val |= 0x8; + } + } + PciMemType::Mem64Low | PciMemType::Mem64High => { + // bit1..=2 = 0b10 + val &= !0x6; + val |= 0x4; + if self.prefetchable { + val |= 0x8; + } + } + PciMemType::Rom => {} + _ => { + warn!("unkown bar type: {:#?}", self.bar_type); + } + } + + self.virtual_value = val; + } +} + +impl Debug for PciMem { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self.bar_type { + PciMemType::Mem32 => { + let pre = if self.prefetchable { "pre" } else { "" }; + let paddr = self.value & !0xf; + let vaddr = self.virtual_value & !0xf; + let size = self.size; + write!( + f, + "[{:#?} 0x{:x}-0x{:x} {}] => [0x{:x}-0x{:x}]", + self.bar_type, + paddr, + paddr + size, + pre, + vaddr, + vaddr + size + ) + } + PciMemType::Mem64Low | PciMemType::Mem64High => { + let pre = if self.prefetchable { "pre" } else { "" }; + let paddr = self.value & !0xf; + let vaddr = self.virtual_value & !0xf; + let size = self.size; + write!( + f, + "[{:#?} 0x{:x} size 0x{:x} 64bit {}] => [0x{:x}-0x{:x}]", + self.bar_type, + paddr, + paddr + size, + pre, + vaddr, + vaddr + size + ) + } + PciMemType::Rom => { + let paddr = self.value & !0xf; + let vaddr = self.virtual_value & !0xf; + let size = self.size; + write!( + f, + "{:#?} [0x{:x}-0x{:x}] => [0x{:x}-0x{:x}]", + self.bar_type, + paddr, + paddr + size, + vaddr, + vaddr + size + ) + } + _ => { + write!(f, "[{:#?}]", self.bar_type) + } + } + } +} + +#[derive(Clone, Copy, Default)] +pub struct Bar { + bararr: [PciMem; 6], +} + +impl Index for Bar { + type Output = PciMem; + + fn index(&self, index: usize) -> &Self::Output { + &self.bararr[index] + } +} + +impl IndexMut for Bar { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.bararr[index] + } +} + +impl<'a> IntoIterator for &'a Bar { + type Item = &'a PciMem; + type IntoIter = slice::Iter<'a, PciMem>; + + fn into_iter(self) -> Self::IntoIter { + self.bararr.iter() + } +} + +impl<'a> IntoIterator for &'a mut Bar { + type Item = &'a mut PciMem; + type IntoIter = slice::IterMut<'a, PciMem>; + + fn into_iter(self) -> Self::IntoIter { + self.bararr.iter_mut() + } +} + +impl Debug for Bar { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "BARs [")?; + let mut i = 0; + let mut is_null = true; + while i < self.bararr.len() { + let bar = &self.bararr[i]; + let address = bar.value & !0xf; + // let address = bar.value; + match bar.bar_type { + PciMemType::Mem32 => { + is_null = false; + write!( + f, + "\n slot {} [mem 0x{:x}-0x{:x}", + i, + address, + address + bar.size + )?; + if bar.prefetchable { + write!(f, " pre")?; + } + write!(f, "]")?; + } + PciMemType::Mem64Low => { + is_null = false; + write!( + f, + "\n slot {} [mem 0x{:x}-0x{:x} 64bit", + i, + address, + address + bar.size + )?; + if bar.prefetchable { + write!(f, " pre")?; + } + write!(f, "]")?; + i += 1; + } + PciMemType::Io => { + writeln!(f, " IO @ 0x{:x}", bar.value)?; + } + _ => {} + } + i += 1; + } + if is_null { + writeln!(f, "]") + } else { + write!(f, "\n]") + } + } +} + +pub trait PciRWBase: Debug + Send + Sync { + fn backend(&self) -> &dyn PciRegion; +} + +pub trait PciRW: Debug + Send + Sync + PciRWBase { + fn read(&self, offset: PciConfigAddress, size: usize) -> HvResult { + match size { + 1 => self.backend().read_u8(offset).map(|v| v as usize), + 2 => self.backend().read_u16(offset).map(|v| v as usize), + 4 => self.backend().read_u32(offset).map(|v| v as usize), + _ => { + hv_result_err!(EFAULT, "pci: invalid mmio read size: {size}") + } + } + } + fn write(&self, offset: PciConfigAddress, size: usize, value: usize) -> HvResult { + match size { + 1 => self.backend().write_u8(offset, value as u8), + 2 => self.backend().write_u16(offset, value as u16), + 4 => self.backend().write_u32(offset, value as u32), + _ => { + hv_result_err!(EFAULT, "pci: invalid mmio write size: {size}") + } + } + } +} + +pub trait PciHeaderRW: PciRWBase { + fn id(&self) -> (DeviceId, VendorId) { + let id = self.backend().read_u32(0x00).unwrap(); + ( + id.get_bits(0..16) as VendorId, + id.get_bits(16..32) as DeviceId, + ) + } + + fn header_type(&self) -> HeaderType { + match self.backend().read_u8(0x0e).unwrap().get_bits(0..7) { + 0x00 => HeaderType::Endpoint, + 0x01 => HeaderType::PciBridge, + 0x02 => HeaderType::CardBusBridge, + v => HeaderType::Unknown(v as u8), + } + } + + fn has_multiple_functions(&self) -> bool { + self.backend().read_u8(0x0e).unwrap().get_bit(7) + } + + fn revision_and_class(&self) -> (BaseClass, SubClass, Interface, DeviceRevision) { + let value = self.backend().read_u32(0x08).unwrap(); + ( + value.get_bits(24..32) as BaseClass, + value.get_bits(16..24) as SubClass, + value.get_bits(8..16) as Interface, + value.get_bits(0..8) as DeviceRevision, + ) + } + + fn status(&self) -> PciStatus { + let status = self.backend().read_u16(0x06).unwrap(); + PciStatus::from_bits_truncate(status) + } + + fn command(&self) -> PciCommand { + let command = self.backend().read_u16(0x04).unwrap(); + PciCommand::from_bits_truncate(command) + } + + fn update_command(&mut self, f: F) + where + F: FnOnce(PciCommand) -> PciCommand, + { + let mut data = self.backend().read_u16(0x04).unwrap(); + let new_command = f(PciCommand::from_bits_retain(data.get_bits(0..16))); + data.set_bits(0..16, new_command.bits()); + let _ = self.backend().write_u16(0x04, data); + } +} + +pub trait PciBarRW: PciRWBase { + fn bar_limit(&self) -> u8; + + fn parse_bar(&self) -> Bar { + let mut bararr = Bar::default(); + + let mut slot = 0u8; + while slot < self.bar_limit() { + // info!("parse bar slot {}", slot); + let value = self.read_bar(slot).unwrap(); + + if !value.get_bit(0) { + let pre = value.get_bit(3); + + match value.get_bits(1..3) { + 0b00 => { + // 32-bit memory space + let size = { + let _ = self.write_bar(slot, 0xffffffff); + let mut readback = self.read_bar(slot).unwrap(); + let _ = self.write_bar(slot, value as u32); + + if readback == 0x0 { + // bar is null + slot += 1; + continue; + } + readback.set_bits(0..4, 0); + 1 << readback.trailing_zeros() + }; + bararr[slot as usize] = + PciMem::new_bar(PciMemType::Mem32, value as u64, size as u64, pre); + } + 0b10 => { + // 64-bit memory space + if slot == 5 { + warn!("read bar64 in last bar"); + break; + } + + let value_high = self.read_bar(slot + 1).unwrap(); + let size = { + let _ = self.write_bar(slot, 0xffffffff); + let _ = self.write_bar(slot + 1, 0xffffffff); + let mut readback_low = self.read_bar(slot).unwrap(); + let readback_high = self.read_bar(slot + 1).unwrap(); + let _ = self.write_bar(slot, value as u32); + let _ = self.write_bar(slot + 1, value_high as u32); + + readback_low.set_bits(0..4, 0); + + if readback_low != 0 { + (1 << readback_low.trailing_zeros()) as u64 + } else { + 1u64 << ((readback_high.trailing_zeros() + 32) as u64) + } + }; + // let value64 = (value as u64) | ((value_high as u64) << 32); + + bararr[slot as usize] = + PciMem::new_bar(PciMemType::Mem64Low, value as u64, size, pre); + bararr[(slot + 1) as usize] = + PciMem::new_bar(PciMemType::Mem64High, value_high as u64, size, pre); + slot += 1; // need extra add 1 + } + _ => { + warn!("unknown bar type"); + } + } + } else { + // IO space + let size = { + let _ = self.write_bar(slot, 0xffffffff); + let mut readback = self.read_bar(slot).unwrap(); + let _ = self.write_bar(slot, readback as u32); + + readback.set_bit(0, false); + if readback == 0x0 { + slot += 1; + continue; + } + + 1 << readback.trailing_zeros() + }; + bararr[slot as usize] = PciMem::new_io(value as u64, size as u64); + } + slot += 1; + } + bararr + } + + fn read_bar(&self, slot: u8) -> HvResult { + // println!("read bar slot {}", slot); + self.backend() + .read_u32((0x10 + (slot as u16) * 4) as PciConfigAddress) + .map(|r| r as usize) + } + + fn write_bar(&self, slot: u8, value: u32) -> HvResult { + // println!("write bar slot {} {}", slot, value); + self.backend() + .write_u32((0x10 + (slot as u16) * 4) as PciConfigAddress, value) + } +} + +pub trait PciRomRW: PciRWBase { + fn rom_offset(&self) -> u64; + fn parse_rom(&self) -> PciMem { + let offset = self.rom_offset(); + let value = self.backend().read_u32(offset).unwrap(); + + let size = { + let _ = self.backend().write_u32(offset, 0xfffff800); + let mut readback = self.backend().read_u32(offset).unwrap(); + let _ = self.backend().write_u32(offset, value); + if readback == 0x0 { + return PciMem::default(); + } + readback.set_bits(0..4, 0); + 1 << readback.trailing_zeros() + }; + PciMem::new_rom(value as u64, size) + } +} + +/* 32 16 0 + * +-----------------------------+------------------------------+ + * | Device ID | Vendor ID | 0x00 + * | | | + * +-----------------------------+------------------------------+ + * | Status | Command | 0x04 + * | | | + * +-----------------------------+---------------+--------------+ + * | Class Code | Revision | 0x08 + * | | ID | + * +--------------+--------------+---------------+--------------+ + * | BIST | Header | Latency | Cacheline | 0x0c + * | | type | timer | size | + * +--------------+--------------+---------------+--------------+ + */ +#[derive(Debug, Clone)] +pub struct PciConfigHeader(PciConfigMmio); + +impl PciRWBase for PciConfigHeader { + fn backend(&self) -> &dyn PciRegion { + &self.0 + } +} +impl PciRW for PciConfigHeader {} +impl PciHeaderRW for PciConfigHeader {} + +impl PciConfigHeader { + pub fn new_with_region(region: PciConfigMmio) -> Self { + PciConfigHeader(region) + } +} + +/* 32 16 0 + * +-----------------------------------------------------------+ 0x00 + * | | + * | Predefined region of header | + * | | + * | | + * +-----------------------------------------------------------+ + * | Base Address Register 0 | 0x10 + * | | + * +-----------------------------------------------------------+ + * | Base Address Register 1 | 0x14 + * | | + * +-----------------------------------------------------------+ + * | Base Address Register 2 | 0x18 + * | | + * +-----------------------------------------------------------+ + * | Base Address Register 3 | 0x1c + * | | + * +-----------------------------------------------------------+ + * | Base Address Register 4 | 0x20 + * | | + * +-----------------------------------------------------------+ + * | Base Address Register 5 | 0x24 + * | | + * +-----------------------------------------------------------+ + * | CardBus CIS Pointer | 0x28 + * | | + * +----------------------------+------------------------------+ + * | Subsystem ID | Subsystem vendor ID | 0x2c + * | | | + * +----------------------------+------------------------------+ + * | Expansion ROM Base Address | 0x30 + * | | + * +--------------------------------------------+--------------+ + * | Reserved | Capability | 0x34 + * | | Pointer | + * +--------------------------------------------+--------------+ + * | Reserved | 0x38 + * | | + * +--------------+--------------+--------------+--------------+ + * | Max_Lat | Min_Gnt | Interrupt | Interrupt | 0x3c + * | | | pin | line | + * +--------------+--------------+--------------+--------------+ + */ +pub trait PciField: Debug { + fn to_offset(&self) -> usize; + fn size(&self) -> usize; +} + +pub enum EndpointField { + ID, + Command, + Status, + RevisionIDAndClassCode, + CacheLineSize, + LatencyTime, + HeaderType, + Bist, + Bar(usize), + CardCisPointer, + SubsystemVendorId, + SubsystemId, + ExpansionRomBar, + CapabilityPointer, + InterruptLine, + InterruptPin, + MinGnt, + MaxLat, + Unknown(usize), +} + +impl Debug for EndpointField { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "EndpointField {{")?; + let _ = match self { + EndpointField::ID => write!(f, "ID"), + EndpointField::Command => write!(f, "Command"), + EndpointField::Status => write!(f, "Status"), + EndpointField::RevisionIDAndClassCode => write!(f, "RevisionIDAndClassCode"), + EndpointField::CacheLineSize => write!(f, "CacheLineSize"), + EndpointField::LatencyTime => write!(f, "LatencyTime"), + EndpointField::HeaderType => write!(f, "HeaderType"), + EndpointField::Bist => write!(f, "Bist"), + EndpointField::Bar(slot) => write!(f, "Bar({})", slot), + EndpointField::CardCisPointer => write!(f, "CardCisPointer"), + EndpointField::SubsystemVendorId => write!(f, "SubsystemVendorId"), + EndpointField::SubsystemId => write!(f, "SubsystemId"), + EndpointField::ExpansionRomBar => write!(f, "ExpansionRomBar"), + EndpointField::CapabilityPointer => write!(f, "CapabilityPointer"), + EndpointField::InterruptLine => write!(f, "InterruptLine"), + EndpointField::InterruptPin => write!(f, "InterruptPin"), + EndpointField::MinGnt => write!(f, "MinGnt"), + EndpointField::MaxLat => write!(f, "MaxLat"), + EndpointField::Unknown(offset) => write!(f, "Unknown({})", offset), + }; + write!(f, "}}") + } +} + +impl PciField for EndpointField { + fn to_offset(&self) -> usize { + match self { + EndpointField::ID => 0x00, + EndpointField::Command => 0x04, + EndpointField::Status => 0x06, + EndpointField::RevisionIDAndClassCode => 0x08, + EndpointField::CacheLineSize => 0x0c, + EndpointField::LatencyTime => 0x0d, + EndpointField::HeaderType => 0x0e, + EndpointField::Bist => 0x0f, + EndpointField::Bar(slot) => (0x10 + slot * 4) as usize, + EndpointField::CardCisPointer => 0x28, + EndpointField::SubsystemVendorId => 0x2c, + EndpointField::SubsystemId => 0x2e, + EndpointField::ExpansionRomBar => 0x30, + EndpointField::CapabilityPointer => 0x34, + EndpointField::InterruptLine => 0x3c, + EndpointField::InterruptPin => 0x3d, + EndpointField::MinGnt => 0x3e, + EndpointField::MaxLat => 0x3f, + EndpointField::Unknown(offset) => *offset, + } + } + + fn size(&self) -> usize { + match self { + EndpointField::ID => 4, + EndpointField::Command => 2, + EndpointField::Status => 2, + EndpointField::RevisionIDAndClassCode => 4, + EndpointField::CacheLineSize => 1, + EndpointField::LatencyTime => 1, + EndpointField::HeaderType => 1, + EndpointField::Bist => 1, + EndpointField::Bar(_) => 4, + EndpointField::CardCisPointer => 4, + EndpointField::SubsystemVendorId => 2, + EndpointField::SubsystemId => 2, + EndpointField::ExpansionRomBar => 4, + EndpointField::CapabilityPointer => 1, + EndpointField::InterruptLine => 1, + EndpointField::InterruptPin => 1, + EndpointField::MinGnt => 1, + EndpointField::MaxLat => 1, + EndpointField::Unknown(_) => 4, // Default to 4 bytes for unknown fields + } + } +} + +impl EndpointField { + pub fn from(offset: usize, size: usize) -> Self { + match (offset, size) { + (0x00, 4) => EndpointField::ID, + (0x04, 2) => EndpointField::Command, + (0x06, 2) => EndpointField::Status, + (0x08, 4) => EndpointField::RevisionIDAndClassCode, + (0x0c, 1) => EndpointField::CacheLineSize, + (0x0d, 1) => EndpointField::LatencyTime, + (0x0e, 1) => EndpointField::HeaderType, + (0x0f, 1) => EndpointField::Bist, + // (0x10, 4) | (0x14, 4) | (0x18, 4) | (0x1c, 4) | (0x20, 4) | (0x24, 4) => { + // EndpointField::Bar + // } + (0x10, 4) => EndpointField::Bar(0), + (0x14, 4) => EndpointField::Bar(1), + (0x18, 4) => EndpointField::Bar(2), + (0x1c, 4) => EndpointField::Bar(3), + (0x20, 4) => EndpointField::Bar(4), + (0x24, 4) => EndpointField::Bar(5), + (0x28, 4) => EndpointField::CardCisPointer, + (0x2c, 2) => EndpointField::SubsystemVendorId, + (0x2e, 2) => EndpointField::SubsystemId, + (0x30, 4) => EndpointField::ExpansionRomBar, + (0x34, 4) => EndpointField::CapabilityPointer, + (0x3c, 1) => EndpointField::InterruptLine, + (0x3d, 1) => EndpointField::InterruptPin, + (0x3e, 1) => EndpointField::MinGnt, + (0x3f, 1) => EndpointField::MaxLat, + (x, _) => EndpointField::Unknown(x), + } + } +} + +#[derive(Debug, Clone)] +pub struct EndpointHeader(PciConfigMmio); + +impl PciRWBase for EndpointHeader { + fn backend(&self) -> &dyn PciRegion { + &self.0 + } +} +impl PciRW for EndpointHeader {} +impl PciHeaderRW for EndpointHeader {} +impl PciBarRW for EndpointHeader { + fn bar_limit(&self) -> u8 { + 6 + } +} +impl PciRomRW for EndpointHeader { + fn rom_offset(&self) -> u64 { + 0x30 + } +} + +impl EndpointHeader { + pub fn new_with_region(region: PciConfigMmio) -> Self { + EndpointHeader(region) + } +} + +/* 32 16 0 + * +-----------------------------------------------------------+ 0x00 + * | | + * | Predefined region of header | + * | | + * | | + * +-----------------------------------------------------------+ + * | Base Address Register 0 | 0x10 + * | | + * +-----------------------------------------------------------+ + * | Base Address Register 1 | 0x14 + * | | + * +--------------+--------------+--------------+--------------+ + * | Secondary | Subordinate | Secondary | Primary Bus | 0x18 + * |Latency Timer | Bus Number | Bus Number | Number | + * +--------------+--------------+--------------+--------------+ + * | Secondary Status | I/O Limit | I/O Base | 0x1C + * | | | | + * +-----------------------------+--------------+--------------+ + * | Memory Limit | Memory Base | 0x20 + * | | | + * +-----------------------------+-----------------------------+ + * | Prefetchable Memory Limit | Prefetchable Memory Base | 0x24 + * | | | + * +-----------------------------+-----------------------------+ + * | Prefetchable Base Upper 32 Bits | 0x28 + * | | + * +-----------------------------------------------------------+ + * | Prefetchable Limit Upper 32 Bits | 0x2C + * | | + * +-----------------------------+-----------------------------+ + * | I/O Limit Upper 16 Bits | I/O Base Upper 16 Bits | 0x30 + * | | | + * +-----------------------------+--------------+--------------+ + * | Reserved | Capability | 0x34 + * | | Pointer | + * +--------------------------------------------+--------------+ + * | Expansion ROM base address | 0x38 + * | | + * +-----------------------------+--------------+--------------+ + * | Bridge Control | Interrupt | Interrupt | 0x3C + * | | PIN | Line | + * +-----------------------------+--------------+--------------+ + */ +pub enum BridgeField { + ID, + Command, + Status, + RevisionIDAndClassCode, + CacheLineSize, + LatencyTime, + HeaderType, + Bist, + Bar, + PrimaryBusNumber, + SecondaryBusNumber, + SubordinateBusNumber, + SecondaryLatencyTimer, + IOBase, + IOLimit, + SecondaryStatus, + MemoryBase, + MemoryLimit, + PrefetchableMemoryBase, + PrefetchableMemoryLimit, + PrefetchableBaseUpper32Bits, + PrefetchableLimitUpper32Bits, + UIBaseUpper16Bits, + IOLimitUpper16Bits, + CapabilityPointer, + ExpansionRomBar, + InterruptLine, + InterruptPin, + BridgeControl, + Unknown(usize), +} + +impl Debug for BridgeField { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "BridgeField {{")?; + let _ = match self { + BridgeField::ID => write!(f, "ID"), + BridgeField::Command => write!(f, "Command"), + BridgeField::Status => write!(f, "Status"), + BridgeField::RevisionIDAndClassCode => write!(f, "RevisionIDAndClassCode"), + BridgeField::CacheLineSize => write!(f, "CacheLineSize"), + BridgeField::LatencyTime => write!(f, "LatencyTime"), + BridgeField::HeaderType => write!(f, "HeaderType"), + BridgeField::Bist => write!(f, "Bist"), + BridgeField::Bar => write!(f, "Bar"), + BridgeField::PrimaryBusNumber => write!(f, "PrimaryBusNumber"), + BridgeField::SecondaryBusNumber => write!(f, "SecondaryBusNumber"), + BridgeField::SubordinateBusNumber => write!(f, "SubordinateBusNumber"), + BridgeField::SecondaryLatencyTimer => write!(f, "SecondaryLatencyTimer"), + BridgeField::IOBase => write!(f, "IOBase"), + BridgeField::IOLimit => write!(f, "IOLimit"), + BridgeField::SecondaryStatus => write!(f, "SecondaryStatus"), + BridgeField::MemoryBase => write!(f, "MemoryBase"), + BridgeField::MemoryLimit => write!(f, "MemoryLimit"), + BridgeField::PrefetchableMemoryBase => write!(f, "PrefetchableMemoryBase"), + BridgeField::PrefetchableMemoryLimit => write!(f, "PrefetchableMemoryLimit"), + BridgeField::PrefetchableBaseUpper32Bits => write!(f, "PrefetchableBaseUpper32Bits"), + BridgeField::PrefetchableLimitUpper32Bits => write!(f, "PrefetchableLimitUpper32Bits"), + BridgeField::UIBaseUpper16Bits => write!(f, "UIBaseUpper16Bits"), + BridgeField::IOLimitUpper16Bits => write!(f, "IOLimitUpper16Bits"), + BridgeField::CapabilityPointer => write!(f, "CapabilityPointer"), + BridgeField::ExpansionRomBar => write!(f, "ExpansionRomBar"), + BridgeField::InterruptLine => write!(f, "InterruptLine"), + BridgeField::InterruptPin => write!(f, "InterruptPin"), + BridgeField::BridgeControl => write!(f, "BridgeControl"), + BridgeField::Unknown(offset) => write!(f, "Unknown({})", offset), + }; + write!(f, "}}") + } +} + +impl PciField for BridgeField { + fn to_offset(&self) -> usize { + match self { + BridgeField::ID => 0x00, + BridgeField::Command => 0x04, + BridgeField::Status => 0x06, + BridgeField::RevisionIDAndClassCode => 0x08, + BridgeField::CacheLineSize => 0x0c, + BridgeField::LatencyTime => 0x0d, + BridgeField::HeaderType => 0x0e, + BridgeField::Bist => 0x0f, + BridgeField::Bar => 0x10, + BridgeField::PrimaryBusNumber => 0x18, + BridgeField::SecondaryBusNumber => 0x19, + BridgeField::SubordinateBusNumber => 0x1a, + BridgeField::SecondaryLatencyTimer => 0x1b, + BridgeField::IOBase => 0x1c, + BridgeField::IOLimit => 0x1d, + BridgeField::SecondaryStatus => 0x1e, + BridgeField::MemoryBase => 0x20, + BridgeField::MemoryLimit => 0x22, + BridgeField::PrefetchableMemoryBase => 0x24, + BridgeField::PrefetchableMemoryLimit => 0x26, + BridgeField::PrefetchableBaseUpper32Bits => 0x28, + BridgeField::PrefetchableLimitUpper32Bits => 0x2c, + BridgeField::UIBaseUpper16Bits => 0x30, + BridgeField::IOLimitUpper16Bits => 0x32, + BridgeField::CapabilityPointer => 0x34, + BridgeField::ExpansionRomBar => 0x38, + BridgeField::InterruptLine => 0x3c, + BridgeField::InterruptPin => 0x3d, + BridgeField::BridgeControl => 0x3e, + BridgeField::Unknown(offset) => *offset, + } + } + + fn size(&self) -> usize { + match self { + BridgeField::ID => 4, + BridgeField::Command => 2, + BridgeField::Status => 2, + BridgeField::RevisionIDAndClassCode => 4, + BridgeField::CacheLineSize => 1, + BridgeField::LatencyTime => 1, + BridgeField::HeaderType => 1, + BridgeField::Bist => 1, + BridgeField::Bar => 4, + BridgeField::PrimaryBusNumber => 1, + BridgeField::SecondaryBusNumber => 1, + BridgeField::SubordinateBusNumber => 1, + BridgeField::SecondaryLatencyTimer => 1, + BridgeField::IOBase => 1, + BridgeField::IOLimit => 1, + BridgeField::SecondaryStatus => 2, + BridgeField::MemoryBase => 2, + BridgeField::MemoryLimit => 2, + BridgeField::PrefetchableMemoryBase => 2, + BridgeField::PrefetchableMemoryLimit => 2, + BridgeField::PrefetchableBaseUpper32Bits => 4, + BridgeField::PrefetchableLimitUpper32Bits => 4, + BridgeField::UIBaseUpper16Bits => 2, + BridgeField::IOLimitUpper16Bits => 2, + BridgeField::CapabilityPointer => 1, + BridgeField::ExpansionRomBar => 4, + BridgeField::InterruptLine => 1, + BridgeField::InterruptPin => 1, + BridgeField::BridgeControl => 2, + BridgeField::Unknown(_) => 4, // Default to 4 bytes for unknown fields + } + } +} + +impl BridgeField { + pub fn from(offset: usize, size: usize) -> Self { + match (offset, size) { + (0x00, 4) => BridgeField::ID, + (0x04, 2) => BridgeField::Command, + (0x06, 2) => BridgeField::Status, + (0x08, 4) => BridgeField::RevisionIDAndClassCode, + (0x0c, 1) => BridgeField::CacheLineSize, + (0x0d, 1) => BridgeField::LatencyTime, + (0x0e, 1) => BridgeField::HeaderType, + (0x0f, 1) => BridgeField::Bist, + (0x10, 4) | (0x14, 4) => BridgeField::Bar, + (0x18, 1) => BridgeField::PrimaryBusNumber, + (0x19, 1) => BridgeField::SecondaryBusNumber, + (0x1a, 1) => BridgeField::SubordinateBusNumber, + (0x1b, 1) => BridgeField::SecondaryLatencyTimer, + (0x1c, 1) => BridgeField::IOBase, + (0x1d, 1) => BridgeField::IOLimit, + (0x1e, 2) => BridgeField::SecondaryStatus, + (0x20, 2) => BridgeField::MemoryBase, + (0x22, 2) => BridgeField::MemoryLimit, + (0x24, 2) => BridgeField::PrefetchableMemoryBase, + (0x26, 2) => BridgeField::PrefetchableMemoryLimit, + (0x28, 4) => BridgeField::PrefetchableBaseUpper32Bits, + (0x2c, 4) => BridgeField::PrefetchableLimitUpper32Bits, + (0x30, 2) => BridgeField::UIBaseUpper16Bits, + (0x32, 2) => BridgeField::IOLimitUpper16Bits, + (0x34, 1) => BridgeField::CapabilityPointer, + (0x38, 4) => BridgeField::ExpansionRomBar, + (0x3c, 1) => BridgeField::InterruptLine, + (0x3d, 1) => BridgeField::InterruptPin, + (0x3e, 2) => BridgeField::BridgeControl, + (x, _) => BridgeField::Unknown(x), + } + } +} + +#[derive(Debug, Clone)] +pub struct PciBridgeHeader(PciConfigMmio); + +impl PciRWBase for PciBridgeHeader { + fn backend(&self) -> &dyn PciRegion { + &self.0 + } +} +impl PciRW for PciBridgeHeader {} +impl PciHeaderRW for PciBridgeHeader {} +impl PciBarRW for PciBridgeHeader { + fn bar_limit(&self) -> u8 { + 2 + } +} +impl PciRomRW for PciBridgeHeader { + fn rom_offset(&self) -> u64 { + 0x38 + } +} + +impl PciBridgeHeader { + pub fn new_with_region(region: PciConfigMmio) -> Self { + PciBridgeHeader(region) + } +} + +impl PciBridgeHeader {} diff --git a/src/pci/pci_config.rs b/src/pci/pci_config.rs new file mode 100644 index 00000000..7eea48d1 --- /dev/null +++ b/src/pci/pci_config.rs @@ -0,0 +1,446 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// + +use alloc::collections::btree_map::BTreeMap; +use spin::{Lazy, Mutex}; + +use crate::{ + config::{HvPciConfig, HvPciDevConfig, CONFIG_MAX_PCI_DEV, CONFIG_PCI_BUS_MAXNUM}, + error::HvResult, + pci::pci_struct::{ArcRwLockVirtualPciConfigSpace, Bdf}, + zone::Zone, +}; + +#[cfg(any( + all(feature = "iommu", target_arch = "aarch64"), + target_arch = "x86_64" +))] +use crate::arch::iommu::iommu_add_device; + +#[cfg(feature = "ecam_pcie")] +use crate::pci::{ + pci_struct::VirtualPciConfigSpace, + vpci_dev::{get_handler, VpciDevType}, +}; + +#[cfg(any( + feature = "ecam_pcie", + feature = "dwc_pcie", + feature = "loongarch64_pcie" +))] +use crate::pci::{ + mem_alloc::BaseAllocator, pci_struct::RootComplex, +}; + +#[cfg(feature = "ecam_pcie")] +use crate::pci::pci_handler::mmio_vpci_handler; +#[cfg(feature = "dwc_pcie")] +use crate::{ + memory::mmio_generic_handler, + pci::{ + config_accessors::{ + dwc::DwcConfigRegionBackend, + dwc_atu::AtuConfig, + PciRegionMmio, + }, + pci_handler::{mmio_dwc_cfg_handler, mmio_dwc_io_handler, mmio_vpci_handler_dbi}, + PciConfigAddress, + }, + platform, +}; + +#[cfg(feature = "loongarch64_pcie")] +use crate::pci::pci_handler::mmio_vpci_direct_handler; + +pub static GLOBAL_PCIE_LIST: Lazy>> = + Lazy::new(|| { + let m = BTreeMap::new(); + Mutex::new(m) + }); + +/* add all dev to GLOBAL_PCIE_LIST */ +pub fn hvisor_pci_init(pci_config: &[HvPciConfig]) -> HvResult { + warn!("begin {:#?}", pci_config); + #[cfg(any( + feature = "ecam_pcie", + feature = "dwc_pcie", + feature = "loongarch64_pcie" + ))] + for (_index, rootcomplex_config) in pci_config.iter().enumerate() { + /* empty config */ + if rootcomplex_config.ecam_base == 0 { + warn!("empty pcie config"); + continue; + } + + let mut allocator = BaseAllocator::default(); + allocator.set_mem32(rootcomplex_config.mem32_base, rootcomplex_config.mem32_size); + allocator.set_mem64(rootcomplex_config.mem64_base, rootcomplex_config.mem64_size); + allocator.set_io( + rootcomplex_config.io_base as u64, + rootcomplex_config.io_size, + ); + + // TODO: refactor + // in x86, we do not take the initiative to reallocate BAR space + #[cfg(feature = "no_pcie_bar_realloc")] + let allocator_opt: Option = None; + #[cfg(not(feature = "no_pcie_bar_realloc"))] + let allocator_opt: Option = Some(allocator); + + // #[cfg(feature = "loongarch64_pcie")] + // let allocator_opt: Option = { + // let mut allocator = LoongArchAllocator::default(); + // allocator.set_mem( + // rootcomplex_config.mem64_base, + // rootcomplex_config.mem64_size, + // ); + // allocator.set_io( + // rootcomplex_config.io_base, + // rootcomplex_config.io_size, + // ); + // Some(allocator) + // }; + + let mut rootcomplex = { + #[cfg(feature = "dwc_pcie")] + { + warn!("dwc pcie"); + let ecam_base = rootcomplex_config.ecam_base; + let atu_config = platform::ROOT_DWC_ATU_CONFIG + .iter() + .find(|atu_cfg| atu_cfg.ecam_base == ecam_base); + + let atu_config = match atu_config { + Some(cfg) => cfg, + None => { + warn!("No ATU config found for ecam_base 0x{:x}", ecam_base); + return hv_result_err!(EINVAL, "No ATU config found for ecam_base"); + } + }; + + let root_bus = rootcomplex_config.bus_range_begin as u8; + + RootComplex::new_dwc(rootcomplex_config.ecam_base, atu_config, root_bus) + } + + #[cfg(feature = "loongarch64_pcie")] + { + let root_bus = rootcomplex_config.bus_range_begin as u8; + RootComplex::new_loongarch( + rootcomplex_config.ecam_base, + rootcomplex_config.ecam_size, + root_bus, + ) + } + + #[cfg(feature = "ecam_pcie")] + { + RootComplex::new_ecam(rootcomplex_config.ecam_base) + } + }; + let range = + rootcomplex_config.bus_range_begin as usize..rootcomplex_config.bus_range_end as usize; + + let domain = rootcomplex_config.domain; + let e = rootcomplex.enumerate(Some(range), domain, allocator_opt); + info!("begin enumerate {:#?}", e); + for node in e { + info!("node {:#?}", node); + GLOBAL_PCIE_LIST + .lock() + .insert(node.get_bdf(), ArcRwLockVirtualPciConfigSpace::new(node)); + } + } + info!("hvisor pci init done \n{:#?}", GLOBAL_PCIE_LIST); + Ok(()) +} + +impl Zone { + pub fn guest_pci_init( + &mut self, + _zone_id: usize, + alloc_pci_devs: &[HvPciDevConfig; CONFIG_MAX_PCI_DEV], + num_pci_devs: u64, + pci_config: &[HvPciConfig], + _num_pci_config: usize, + ) -> HvResult { + let mut guard = GLOBAL_PCIE_LIST.lock(); + for target_pci_config in pci_config { + // Skip empty config + if target_pci_config.ecam_base == 0 { + continue; + } + + #[allow(unused_variables)] + let ecam_base = target_pci_config.ecam_base; + let target_domain = target_pci_config.domain; + let bus_range_begin = target_pci_config.bus_range_begin as u8; + + 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]; + if dev_config.domain == target_domain { + filtered_devices.push(dev_config); + } + } + + // Skip if no devices for this domain + if filtered_devices.is_empty() { + continue; + } + + filtered_devices.sort_by(|a, b| { + a.bus + .cmp(&b.bus) + .then_with(|| a.device.cmp(&b.device)) + .then_with(|| a.function.cmp(&b.function)) + }); + + let mut vbus_pre = bus_range_begin; + let mut bus_pre = bus_range_begin; + let mut device_pre = 0u8; + + /* + * To allow Linux to successfully recognize the devices we add, hvisor needs + * to adjust the devices’ BDFs. Linux always assumes that the PCIe buses + * it discovers are contiguous, and that device function numbers always start from 0. + * + * 1. The bus number of a virtual BDF (vBDF) must start from range_begin and + * be contiguous. Once the physical bus number increases—regardless of + * how much it increases—the corresponding virtual bus number (vbus) + * can only increase by 1. + * + * 2. If the function number of a vBDF is not 0, and it is found that + * the device with function 0 of the same vBDF does not belong to the current zone, + * then the function number of the current vBDF should be set to 0. + */ + for dev_config in &filtered_devices { + let bdf = Bdf::new_from_config(*dev_config); + let bus = bdf.bus(); + let device = bdf.device(); + let function = bdf.function(); + + /* + * vfunction = if (bus != bus_pre || device != device_pre) && function != 0 + * In practice, remapping is performed only for new devices whose function is not 0; + * however, the check for function != 0 does not affect the final result. + */ + let vfunction = if bus != bus_pre || device != device_pre { + 0 + } else { + function + }; + + let vbus = if bus > bus_pre { + vbus_pre += 1; + vbus_pre + } else { + vbus_pre + }; + + let vbdf = Bdf::new(bdf.domain(), vbus, device, vfunction); + + device_pre = device; + bus_pre = bus; + + info!("set bdf {:#?} to vbdf {:#?}", bdf, vbdf); + + #[cfg(any( + all(feature = "iommu", target_arch = "aarch64"), + target_arch = "x86_64" + ))] + { + let iommu_pt_addr = if self.iommu_pt.is_some() { + self.iommu_pt.as_ref().unwrap().root_paddr() + } else { + 0 + }; + let device_id = (dev_config.bus as usize) << 8 + | (dev_config.device as usize) << 3 + | dev_config.function as usize; + iommu_add_device(_zone_id, device_id as _, iommu_pt_addr); + } + + // Insert device into vpci_bus with calculated vbdf + if let Some(dev) = guard.get(&bdf) { + if bdf.is_host_bridge(dev.read().get_host_bdf().bus()) + || dev.with_config_value(|config_value| -> bool { + config_value.get_class().0 == 0x6 + }) + { + let mut vdev = dev.read().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); + } + } else { + // warn!("can not find dev {:#?}", bdf); + #[cfg(feature = "ecam_pcie")] + { + let dev_type = dev_config.dev_type; + match dev_type { + VpciDevType::Physical => { + warn!("can not find dev {:#?}", bdf); + } + _ => { + if let Some(_handler) = get_handler(dev_type) { + let base = ecam_base + + ((bdf.bus() as u64) << 20) + + ((bdf.device() as u64) << 15) + + ((bdf.function() as u64) << 12); + let dev = VirtualPciConfigSpace::virt_dev(bdf, base, dev_type); + self.vpci_bus.insert(vbdf, dev); + } else { + warn!("can not find dev {:#?}, unknown device type", bdf); + } + } + } + } + } + } + } + info!("vpci bus init done\n {:#?}", self.vpci_bus); + Ok(()) + } + + pub fn virtual_pci_mmio_init( + &mut self, + pci_rootcomplex_config: &[HvPciConfig; CONFIG_PCI_BUS_MAXNUM], + _num_pci_config: usize, + ) { + for rootcomplex_config in pci_rootcomplex_config { + /* empty config */ + if rootcomplex_config.ecam_base == 0 { + continue; + } + #[cfg(feature = "ecam_pcie")] + { + // use crate::pci::pci_handler::mmio_vpci_direct_handler; + self.mmio_region_register( + rootcomplex_config.ecam_base as usize, + rootcomplex_config.ecam_size as usize, + mmio_vpci_handler, + // mmio_vpci_direct_handler, + rootcomplex_config.ecam_base as usize, + ); + } + #[cfg(feature = "dwc_pcie")] + { + self.mmio_region_register( + rootcomplex_config.ecam_base as usize, + rootcomplex_config.ecam_size as usize, + mmio_vpci_handler_dbi, + rootcomplex_config.ecam_base as usize, + ); + + let extend_config = platform::ROOT_DWC_ATU_CONFIG + .iter() + .find(|extend_cfg| extend_cfg.ecam_base == rootcomplex_config.ecam_base); + + if let Some(extend_config) = extend_config { + if extend_config.apb_base != 0 && extend_config.apb_size != 0 { + self.mmio_region_register( + extend_config.apb_base as usize, + extend_config.apb_size as usize, + mmio_generic_handler, + extend_config.apb_base as usize, + ); + } + + let cfg_size_half = extend_config.cfg_size / 2; + let cfg0_base = extend_config.cfg_base; + if cfg0_base != 0 && cfg_size_half != 0 { + self.mmio_region_register( + cfg0_base as usize, + cfg_size_half as usize, + mmio_dwc_cfg_handler, + cfg0_base as usize, + ); + } + + let cfg1_base = extend_config.cfg_base + cfg_size_half; + if cfg1_base != 0 && cfg_size_half != 0 { + self.mmio_region_register( + cfg1_base as usize, + cfg_size_half as usize, + mmio_dwc_cfg_handler, + cfg1_base as usize, + ); + } + + if extend_config.io_cfg_atu_shared != 0 { + self.mmio_region_register( + rootcomplex_config.io_base as usize, + rootcomplex_config.io_size as usize, + mmio_dwc_io_handler, + rootcomplex_config.io_base as usize, + ); + } + + let mut atu = AtuConfig::default(); + + let dbi_base = extend_config.dbi_base as PciConfigAddress; + let dbi_size = extend_config.dbi_size; + let dbi_region = PciRegionMmio::new(dbi_base, dbi_size); + let dbi_backend = DwcConfigRegionBackend::new(dbi_region); + if let Err(e) = atu.init_limit_hw_value(&dbi_backend) { + warn!("Failed to initialize ATU0 limit defaults: {:?}", e); + } + + self.atu_configs + .insert_atu(rootcomplex_config.ecam_base as usize, atu); + self.atu_configs.insert_cfg_base_mapping( + extend_config.cfg_base as PciConfigAddress, + rootcomplex_config.ecam_base as usize, + ); + self.atu_configs.insert_io_base_mapping( + rootcomplex_config.io_base as PciConfigAddress, + rootcomplex_config.ecam_base as usize, + ); + } + } + #[cfg(feature = "loongarch64_pcie")] + { + self.mmio_region_register( + rootcomplex_config.ecam_base as usize, + rootcomplex_config.ecam_size as usize, + mmio_vpci_direct_handler, + rootcomplex_config.ecam_base as usize, + ); + let _ = self.page_table_emergency( + rootcomplex_config.ecam_base as usize, + rootcomplex_config.ecam_size as usize, + ); + } + #[cfg(not(any( + feature = "ecam_pcie", + feature = "dwc_pcie", + feature = "loongarch64_pcie" + )))] + { + warn!( + "No extend config found for base 0x{:x}", + rootcomplex_config.ecam_base + ); + } + } + } +} diff --git a/src/pci/pci_handler.rs b/src/pci/pci_handler.rs new file mode 100644 index 00000000..960d15e1 --- /dev/null +++ b/src/pci/pci_handler.rs @@ -0,0 +1,997 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// + +use alloc::string::String; + +use crate::error::HvResult; +use crate::memory::MMIOAccess; +use crate::memory::{GuestPhysAddr, HostPhysAddr, MemFlags, MemoryRegion}; +use crate::percpu::this_zone; +use crate::zone::is_this_root_zone; + +use super::pci_access::{BridgeField, EndpointField, HeaderType, PciField, PciMemType}; +use super::pci_config::GLOBAL_PCIE_LIST; +use super::pci_struct::{ArcRwLockVirtualPciConfigSpace, BIT_LENTH}; +use super::vpci_dev::VpciDevType; +use super::PciConfigAddress; + +#[cfg(target_arch = "x86_64")] +use crate::zone::this_zone_id; + +#[cfg(feature = "dwc_pcie")] +use crate::{ + pci::config_accessors::{ + dwc::DwcConfigRegionBackend, + dwc_atu::{ + AtuType, AtuUnroll, ATU_BASE, ATU_ENABLE_BIT, ATU_REGION_SIZE, + PCIE_ATU_UNR_LIMIT, PCIE_ATU_UNR_LOWER_BASE, PCIE_ATU_UNR_LOWER_TARGET, + PCIE_ATU_UNR_REGION_CTRL1, PCIE_ATU_UNR_REGION_CTRL2, PCIE_ATU_UNR_UPPER_BASE, + PCIE_ATU_UNR_UPPER_LIMIT, PCIE_ATU_UNR_UPPER_TARGET, + }, + PciRegionMmio, + }, + memory::mmio_perform_access, +}; + +macro_rules! pci_log { + ($($arg:tt)*) => { + // info!($($arg)*); + // To switch to debug level, change the line above to: + debug!($($arg)*); + }; +} + +fn handle_virt_pci_request( + dev: ArcRwLockVirtualPciConfigSpace, + offset: PciConfigAddress, + size: usize, + value: usize, + is_write: bool, + dev_type: VpciDevType, +) -> HvResult> { + pci_log!( + "virt pci standard rw offset {:#x}, size {:#x}", + offset, + size + ); + + /* + * The capability is located in the upper part of the configuration space, + * and there is no other message. So the max cap_offset which is less than + * offset is the correct cap we need. + */ + let result = dev.with_cap(|capabilities| { + if let Some((cap_offset, cap)) = capabilities.range(..=offset).next_back() { + pci_log!( + "find cap at offset {:#x}, cap {:#?}", + cap_offset, + cap.get_type() + ); + let end = *cap_offset + cap.get_size() as u64; + if offset >= end { + return hv_result_err!( + ERANGE, + format!( + "virt pci cap rw offset {:#x} out of range [{:#x}..{:#x})", + offset, *cap_offset, end + ) + ); + } + let relative_offset = offset - *cap_offset; + + if is_write { + cap.with_region_mut(|region| { + match region.write(relative_offset, size, value as u32) { + Ok(()) => Ok(0), + Err(e) => { + warn!( + "Failed to write capability at offset 0x{:x}: {:?}", + offset, e + ); + Err(e) + } + } + }) + } else { + cap.with_region(|region| match region.read(relative_offset, size) { + Ok(val) => Ok(val), + Err(e) => { + warn!( + "Failed to read capability at offset 0x{:x}: {:?}", + offset, e + ); + Err(e) + } + }) + } + } else { + hv_result_err!(ENOENT) + } + }); + + match result { + Ok(val) => { + if !is_write { + Ok(Some(val as usize)) + } else { + Ok(None) + } + } + Err(_) => { + if is_write { + super::vpci_dev::vpci_dev_write_cfg(dev_type, dev.clone(), offset, size, value)?; + Ok(None) + } else { + Ok(Some(super::vpci_dev::vpci_dev_read_cfg( + dev_type, + dev.clone(), + offset, + size, + )?)) + } + } + } +} + +fn handle_endpoint_access( + dev: ArcRwLockVirtualPciConfigSpace, + field: EndpointField, + value: usize, + is_write: bool, + is_direct: bool, + is_root: bool, + is_dev_belong_to_zone: bool, +) -> HvResult> { + match field { + EndpointField::ID => { + if !is_write && is_dev_belong_to_zone { + Ok(Some(dev.read_emu(EndpointField::ID)?)) + } else if !is_write && is_direct && is_root { + /* just an id no one used now + * here let root allocate resources but not drive the device + */ + const ROOT_UNUSED_DEVICE_ID: usize = 0xFFFD_4106; + Ok(Some(ROOT_UNUSED_DEVICE_ID)) + } else { + // id is readonly (when is_write is true) + warn!( + "vbdf {:#?}: unhandled {:#?} {}", + dev.get_vbdf(), + field, + if is_write { "write" } else { "read" } + ); + Ok(None) + } + } + EndpointField::RevisionIDAndClassCode => { + if !is_write && is_dev_belong_to_zone { + Ok(Some(dev.read_emu(EndpointField::RevisionIDAndClassCode)?)) + } else if !is_write && is_direct && is_root { + const ROOT_DEFAULT_CLASS_AND_REVISION: usize = 0xff00_0000; + Ok(Some(ROOT_DEFAULT_CLASS_AND_REVISION)) + } else { + warn!( + "vbdf {:#?}: unhandled {:#?} {}", + dev.get_vbdf(), + field, + if is_write { "write" } else { "read" } + ); + Ok(None) + } + } + EndpointField::Bar(slot) => { + /* + * hw: the physical reg + * value: same with physical reg, the paddr for pt + * virt_value: the vaddr for pt + * config_value: the virtual reg for zone, directly rw + * + * The virt_value cache of vaddr is required because mem64 bar updates are + * split between mem64high and mem64low registers. The Hvisor must wait + * for both updates to complete before using old_vaddr for page table maintenance + * + * In typical operation, tmp_value maintains parity with virt_value; the sole exception occurs + * when exclusively updating mem64low while leaving mem64high unmodified, + * as previously described + */ + let bar_type = dev.with_bar_ref(slot, |bar| bar.get_type()); + if bar_type != PciMemType::default() { + if is_write { + if is_direct && is_root { + // direct mode and root zone, update resources directly + dev.with_config_value_mut(|configvalue| { + configvalue.set_bar_value(slot, value as u32); + }); + dev.write_hw(field.to_offset() as PciConfigAddress, field.size(), value)?; + if (bar_type == PciMemType::Mem32) + | (bar_type == PciMemType::Mem64High) + | (bar_type == PciMemType::Io) + { + let new_vaddr = { + if bar_type == PciMemType::Mem64High { + /* last 4bit is flag, not address and need ignore + * flag will auto add when set_value and set_virtual_value + * Read from config_value.bar_value cache instead of space + */ + let low_value = dev + .with_config_value(|cv| cv.get_bar_value(slot - 1)) + as u64; + let high_value = (value as u32 as u64) << 32; + (low_value | high_value) & !0xf + } else { + (value as u64) & !0xf + } + }; + + // set virt_value + dev.with_bar_ref_mut(slot, |bar| bar.set_virtual_value(new_vaddr)); + if bar_type == PciMemType::Mem64High { + dev.with_bar_ref_mut(slot - 1, |bar| { + bar.set_virtual_value(new_vaddr) + }); + } + + // set value + dev.with_bar_ref_mut(slot, |bar| bar.set_value(new_vaddr)); + if bar_type == PciMemType::Mem64High { + dev.with_bar_ref_mut(slot - 1, |bar| bar.set_value(new_vaddr)); + } + } + } else if is_dev_belong_to_zone { + // normal mod, update virt resources + dev.with_config_value_mut(|configvalue| { + configvalue.set_bar_value(slot, value as u32); + }); + + if (bar_type == PciMemType::Mem32) + | (bar_type == PciMemType::Mem64High) + | (bar_type == PciMemType::Io) + { + let old_vaddr = + dev.with_bar_ref(slot, |bar| bar.get_virtual_value64()) & !0xf; + let new_vaddr = { + if bar_type == PciMemType::Mem64High { + /* last 4bit is flag, not address and need ignore + * flag will auto add when set_value and set_virtual_value + * Read from config_value.bar_value cache instead of space + */ + let low_value = dev + .with_config_value(|cv| cv.get_bar_value(slot - 1)) + as u64; + let high_value = (value as u32 as u64) << 32; + (low_value | high_value) & !0xf + } else { + (value as u64) & !0xf + } + }; + + dev.with_bar_ref_mut(slot, |bar| bar.set_virtual_value(new_vaddr)); + if bar_type == PciMemType::Mem64High { + dev.with_bar_ref_mut(slot - 1, |bar| { + bar.set_virtual_value(new_vaddr) + }); + } + + let paddr = if is_root { + dev.with_bar_ref_mut(slot, |bar| bar.set_value(new_vaddr)); + if bar_type == PciMemType::Mem64High { + dev.with_bar_ref_mut(slot - 1, |bar| bar.set_value(new_vaddr)); + } + new_vaddr as HostPhysAddr + } else { + dev.with_bar_ref(slot, |bar| bar.get_value64()) as HostPhysAddr + }; + let bar_size = { + let size = dev.with_bar_ref(slot, |bar| bar.get_size()); + if crate::memory::addr::is_aligned(size as usize) { + size + } else { + crate::memory::PAGE_SIZE as u64 + } + }; + let new_vaddr = if !crate::memory::addr::is_aligned(new_vaddr as usize) + { + crate::memory::addr::align_up(new_vaddr as usize) as u64 + } else { + new_vaddr as u64 + }; + + let zone = this_zone(); + let mut guard = zone.write(); + let gpm = &mut guard.gpm; + + if !gpm + .try_delete(old_vaddr.try_into().unwrap(), bar_size as usize) + .is_ok() + { + // warn!("delete bar {}: can not found 0x{:x}", slot, old_vaddr); + } + gpm.try_insert_quiet(MemoryRegion::new_with_offset_mapper( + new_vaddr as GuestPhysAddr, + paddr as HostPhysAddr, + bar_size as _, + MemFlags::READ | MemFlags::WRITE, + ))?; + drop(guard); + /* after update gpm, mem barrier is needed + */ + #[cfg(target_arch = "aarch64")] + unsafe { + core::arch::asm!("isb"); + core::arch::asm!("tlbi vmalls12e1is"); + core::arch::asm!("dsb nsh"); + } + /* after update gpm, need to flush iommu table + * in x86_64 + */ + #[cfg(target_arch = "x86_64")] + { + let vbdf = dev.get_vbdf(); + crate::arch::iommu::flush( + this_zone_id(), + vbdf.bus, + (vbdf.device << 3) + vbdf.function, + ); + } + } + } + Ok(None) + } else { + // read bar + if (dev.with_config_value(|configvalue| configvalue.get_bar_value(slot)) + & 0xfffffff0) + == 0xfffffff0 + { + /* + * tmp_value being 0xFFFF_FFFF means that Linux is attempting to determine the BAR size. + * The value of tmp_value is used directly here because Linux will rewrite this register later, + * so the Hvisor does not need to preserve any additional state. + */ + Ok(Some( + dev.with_bar_ref(slot, |bar| bar.get_size_with_flag()) as usize + )) + } else { + Ok(Some( + dev.with_config_value(|configvalue| configvalue.get_bar_value(slot)) + as usize, + )) + } + } + } else { + Ok(None) + } + } + EndpointField::ExpansionRomBar => { + // rom is same with bar + let rom_type = dev.with_rom_ref(|rom| rom.get_type()); + if rom_type == PciMemType::Rom { + if is_write { + if is_direct && is_root { + dev.with_config_value_mut(|configvalue| { + configvalue.set_rom_value(value as u32); + }); + dev.write_hw(field.to_offset() as PciConfigAddress, field.size(), value)?; + + let new_vaddr = (value as u64) & !0xf; + + // set virt_value + dev.with_rom_ref_mut(|rom| rom.set_virtual_value(new_vaddr)); + + // set value + dev.with_rom_ref_mut(|rom| rom.set_value(new_vaddr)); + } else if is_dev_belong_to_zone { + // normal mode, update virt resources + dev.with_config_value_mut(|configvalue| { + configvalue.set_rom_value(value as u32); + }); + + let old_vaddr = dev.with_rom_ref(|rom| rom.get_virtual_value64()) & !0xf; + let new_vaddr = (value as u64) & !0xf; + + dev.with_rom_ref_mut(|rom| rom.set_virtual_value(new_vaddr)); + + let paddr = if is_root { + dev.with_rom_ref_mut(|rom| rom.set_value(new_vaddr)); + new_vaddr as HostPhysAddr + } else { + dev.with_rom_ref(|rom| rom.get_value64()) as HostPhysAddr + }; + + let rom_size = { + let size = dev.with_rom_ref(|rom| rom.get_size()); + if crate::memory::addr::is_aligned(size as usize) { + size + } else { + crate::memory::PAGE_SIZE as u64 + } + }; + let new_vaddr = if !crate::memory::addr::is_aligned(new_vaddr as usize) { + crate::memory::addr::align_up(new_vaddr as usize) as u64 + } else { + new_vaddr as u64 + }; + + let zone = this_zone(); + let mut guard = zone.write(); + let gpm = &mut guard.gpm; + + if !gpm + .try_delete(old_vaddr.try_into().unwrap(), rom_size as usize) + .is_ok() + { + // warn!("delete rom bar: can not found 0x{:x}", old_vaddr); + } + gpm.try_insert_quiet(MemoryRegion::new_with_offset_mapper( + new_vaddr as GuestPhysAddr, + paddr as HostPhysAddr, + rom_size as _, + MemFlags::READ | MemFlags::WRITE, + ))?; + drop(guard); + /* after update gpm, mem barrier is needed + */ + #[cfg(target_arch = "aarch64")] + unsafe { + core::arch::asm!("isb"); + core::arch::asm!("tlbi vmalls12e1is"); + core::arch::asm!("dsb nsh"); + } + /* after update gpm, need to flush iommu table + * in x86_64 + */ + #[cfg(target_arch = "x86_64")] + { + let vbdf = dev.get_vbdf(); + crate::arch::iommu::flush( + this_zone_id(), + vbdf.bus, + (vbdf.device << 3) + vbdf.function, + ); + } + } + Ok(None) + } else { + // read rom bar + if (dev.with_config_value(|configvalue| configvalue.get_rom_value())) + & 0xfffff800 + == 0xfffff800 + { + /* + * config_value being 0xFFFF_FFFF means that Linux is attempting to determine the ROM size. + * The value is used directly here because Linux will rewrite this register later, + * so the Hvisor does not need to preserve any additional state. + */ + Ok(Some( + dev.with_rom_ref(|rom| rom.get_size_with_flag()) as usize + )) + } else { + Ok(Some( + dev.with_config_value(|configvalue| configvalue.get_rom_value()) + as usize, + )) + } + } + } else { + Ok(None) + } + } + _ => Ok(None), + } +} + +fn handle_pci_bridge_access( + _dev: ArcRwLockVirtualPciConfigSpace, + _field: BridgeField, + _is_write: bool, +) -> HvResult> { + Ok(None) +} + +/* + * is_direct: if true, root can allocate resource for device belonging + * to ohter zone but can't drive it + * is_root: if the access is from the root zone + * is_dev_belong_to_zone: if the access is from the device that belongs to the zone + */ +fn handle_config_space_access( + dev: ArcRwLockVirtualPciConfigSpace, + mmio: &mut MMIOAccess, + offset: PciConfigAddress, + is_direct: bool, + is_root: bool, + is_dev_belong_to_zone: bool, +) -> HvResult { + let is_write = mmio.is_write; + + // the lenth of access and control bits are limited by BIT_LENTH + if (offset as usize) >= BIT_LENTH { + warn!("invalid pci offset {:#x}", offset); + if !is_write { + mmio.value = 0; + } + return Ok(()); + } + + let size = mmio.size; + let value = mmio.value; + + let vbdf = dev.get_bdf(); + let dev_type = dev.get_dev_type(); + + if is_root || is_dev_belong_to_zone { + match dev.access(offset, size) { + false => { + // Hardware access path + pci_log!( + "hw vbdf {:#?} reg 0x{:x} try {} {}", + vbdf, + offset, + if is_write { "write" } else { "read" }, + if is_write { + format!("0x{:x}", mmio.value) + } else { + String::new() + } + ); + if is_write { + dev.write_hw(offset, size, value)?; + } else { + mmio.value = dev.read_hw(offset, size).unwrap(); + } + } + true => { + // Emulation access path + pci_log!( + "emu vbdf {:#?} reg 0x{:x} try {} {}", + vbdf, + offset, + if is_write { "write" } else { "read" }, + if is_write { + format!(" 0x{:x}", mmio.value) + } else { + String::new() + } + ); + match dev_type { + VpciDevType::Physical => { + let config_type = dev.get_config_type(); + match config_type { + HeaderType::Endpoint => { + if let Some(val) = handle_endpoint_access( + dev, + EndpointField::from(offset as usize, size), + value, + is_write, + is_direct, + is_root, + is_dev_belong_to_zone, + )? { + mmio.value = val; + } + } + HeaderType::PciBridge => { + if let Some(val) = handle_pci_bridge_access( + dev, + BridgeField::from(offset as usize, size), + is_write, + )? { + mmio.value = val; + } + } + _ => { + mmio.value = 0; + } + } + } + _ => { + // virt pci dev + if let Some(val) = + handle_virt_pci_request(dev, offset, size, value, is_write, dev_type)? + { + mmio.value = val; + } + } + } + } + } + } + + pci_log!( + "vbdf {:#?} reg 0x{:x} {} 0x{:x}", + vbdf, + offset, + if is_write { "write" } else { "read" }, + mmio.value + ); + Ok(()) +} + +fn handle_device_not_found(mmio: &mut MMIOAccess, offset: PciConfigAddress) { + /* if the dev is None, just return 0xFFFF_FFFF when read ID */ + if !mmio.is_write { + match EndpointField::from(offset as usize, mmio.size) { + EndpointField::ID => { + mmio.value = 0xFFFF_FFFF; + } + _ => { + // warn!("unhandled pci mmio read, addr: {:#x?}", mmio.address); + mmio.value = 0; + } + } + } +} + +pub fn mmio_vpci_handler(mmio: &mut MMIOAccess, _base: usize) -> HvResult { + // info!("mmio_vpci_handler {:#x}", mmio.address); + let zone = this_zone(); + let offset = (mmio.address & 0xfff) as PciConfigAddress; + let base = mmio.address as PciConfigAddress - offset + _base as PciConfigAddress; + + let dev: Option = { + let mut guard = zone.write(); + let vbus = &mut guard.vpci_bus; + vbus.get_device_by_base(base) + }; + + let is_root = is_this_root_zone(); + + if let Some(dev) = dev { + handle_config_space_access(dev, mmio, offset, false, is_root, true)?; + } else { + handle_device_not_found(mmio, offset); + } + + Ok(()) +} + +#[cfg(feature = "dwc_pcie")] +pub fn mmio_dwc_io_handler(mmio: &mut MMIOAccess, _base: usize) -> HvResult { + { + let zone = this_zone(); + let guard = zone.read(); + + let atu_config = guard + .atu_configs + .get_atu_by_io_base(_base as PciConfigAddress) + .and_then(|atu| { + guard + .atu_configs + .get_ecam_by_io_base(_base as PciConfigAddress) + .map(|ecam| (*atu, ecam)) + }); + + drop(guard); + + if let Some((atu, ecam_base)) = atu_config { + use crate::platform; + if let Some(extend_config) = platform::ROOT_DWC_ATU_CONFIG + .iter() + .find(|cfg| cfg.ecam_base == ecam_base as u64) + { + // Create DBI backend + let dbi_base = extend_config.dbi_base as PciConfigAddress; + let dbi_size = extend_config.dbi_size; + let dbi_region = PciRegionMmio::new(dbi_base, dbi_size); + let dbi_backend = DwcConfigRegionBackend::new(dbi_region); + + // Call AtuUnroll to program the ATU + AtuUnroll::dw_pcie_prog_outbound_atu_unroll(&dbi_backend, &atu)?; + } + mmio_perform_access(atu.pci_target() as usize, mmio); + } else { + warn!("No ATU config yet, do nothing"); + } + } + Ok(()) +} + +#[cfg(feature = "dwc_pcie")] +pub fn mmio_dwc_cfg_handler(mmio: &mut MMIOAccess, _base: usize) -> HvResult { + info!("mmio_dwc_cfg_handler {:#x}", mmio.address + _base); + let zone = this_zone(); + let guard = zone.read(); + + let atu_config = guard + .atu_configs + .get_atu_by_cfg_base(_base as PciConfigAddress) + .and_then(|atu| { + guard + .atu_configs + .get_ecam_by_cfg_base(_base as PciConfigAddress) + .map(|ecam| (*atu, ecam)) + }); + + drop(guard); + + if let Some((atu, ecam_base)) = atu_config { + // Get dbi_base from platform config (usually dbi_base == ecam_base) + use crate::platform; + if let Some(extend_config) = platform::ROOT_DWC_ATU_CONFIG + .iter() + .find(|cfg| cfg.ecam_base == ecam_base as u64) + { + // Create DBI backend + let dbi_base = extend_config.dbi_base as PciConfigAddress; + let dbi_size = extend_config.dbi_size; + let dbi_region = PciRegionMmio::new(dbi_base, dbi_size); + let dbi_backend = DwcConfigRegionBackend::new(dbi_region); + + // warn!("atu config {:#?}", atu); + + // Call AtuUnroll to program the ATU + AtuUnroll::dw_pcie_prog_outbound_atu_unroll(&dbi_backend, &atu)?; + } + + let offset = (mmio.address & 0xfff) as PciConfigAddress; + let zone = this_zone(); + let mut is_dev_belong_to_zone = false; + + let base = mmio.address as PciConfigAddress - offset + atu.pci_target(); + + let dev: Option = { + let mut guard = zone.write(); + let vbus = &mut guard.vpci_bus; + if let Some(dev) = vbus.get_device_by_base(base) { + is_dev_belong_to_zone = true; + Some(dev) + } else { + drop(guard); + // Clone Arc first while holding GLOBAL_PCIE_LIST lock, then release it + // This avoids holding multiple locks simultaneously + let dev_clone = { + let global_pcie_list = GLOBAL_PCIE_LIST.lock(); + global_pcie_list + .values() + .find(|dev| { + let dev_guard = dev.read(); + dev_guard.get_base() == base + }) + .cloned() + }; + dev_clone + } + }; + + let dev = match dev { + Some(dev) => dev, + None => { + handle_device_not_found(mmio, offset); + return Ok(()); + } + }; + + let is_root = is_this_root_zone(); + let is_direct = true; // dwc_cfg_handler uses direct mode + + handle_config_space_access(dev, mmio, offset, is_direct, is_root, is_dev_belong_to_zone)?; + } else { + warn!("No ATU config yet, do nothing"); + } + Ok(()) +} + +#[cfg(feature = "dwc_pcie")] +pub fn mmio_vpci_handler_dbi(mmio: &mut MMIOAccess, _base: usize) -> HvResult { + // info!("mmio_vpci_handler_dbi {:#x}", mmio.address); + + /* 0x0-0x100 is outbound atu0 reg + * 0x100-0x200 is inbound atu0 reg just handle outbound right now + * so MAX is ATU_BASE + ATU_REGION_SIZE/2 + */ + if mmio.address >= ATU_BASE && mmio.address < ATU_BASE + ATU_REGION_SIZE / 2 { + let zone = this_zone(); + let mut guard = zone.write(); + let ecam_base = _base; + let atu_offset = mmio.address - ATU_BASE; + + // warn!("set atu0 register {:#X} value {:#X}", atu_offset, mmio.value); + + let atu = guard.atu_configs.get_atu_by_ecam_mut(ecam_base).unwrap(); + + // info!("atu config write {:#?}", atu); + + if mmio.is_write { + if mmio.size == 4 { + match atu_offset { + PCIE_ATU_UNR_REGION_CTRL1 => { + // info!("set atu0 region ctrl1 value {:#X}", mmio.value); + atu.set_atu_type(AtuType::from_u8((mmio.value & 0xff) as u8)); + } + PCIE_ATU_UNR_REGION_CTRL2 => { + // Enable bit is written here, but we just track it + // The actual enable is handled by the driver + } + PCIE_ATU_UNR_LOWER_BASE => { + // info!("set atu0 lower base value {:#X}", mmio.value); + atu.set_cpu_base( + (atu.cpu_base() & !0xffffffff) | (mmio.value as PciConfigAddress), + ); + } + PCIE_ATU_UNR_UPPER_BASE => { + // info!("set atu0 upper base value {:#X}", mmio.value); + atu.set_cpu_base( + (atu.cpu_base() & 0xffffffff) + | ((mmio.value as PciConfigAddress) << 32), + ); + } + PCIE_ATU_UNR_LIMIT => { + // info!("set atu0 limit value {:#X}", mmio.value); + atu.set_cpu_limit( + (atu.cpu_limit() & !0xffffffff) | (mmio.value as PciConfigAddress), + ); + } + PCIE_ATU_UNR_UPPER_LIMIT => { + // Update the upper 32 bits of cpu_limit + atu.set_cpu_limit( + (atu.cpu_limit() & 0xffffffff) + | ((mmio.value as PciConfigAddress) << 32), + ); + } + PCIE_ATU_UNR_LOWER_TARGET => { + // info!("set atu0 lower target value {:#X}", mmio.value); + atu.set_pci_target( + (atu.pci_target() & !0xffffffff) | (mmio.value as PciConfigAddress), + ); + } + PCIE_ATU_UNR_UPPER_TARGET => { + // info!("set atu0 upper target value {:#X}", mmio.value); + atu.set_pci_target( + (atu.pci_target() & 0xffffffff) + | ((mmio.value as PciConfigAddress) << 32), + ); + } + _ => { + warn!("invalid atu0 write {:#x} + {:#x}", atu_offset, mmio.size); + } + } + } else { + warn!("invalid atu0 read size {:#x}", mmio.size); + } + } else { + // Read from virtual ATU + // warn!("read atu0 {:#x}", atu_offset); + match atu_offset { + PCIE_ATU_UNR_REGION_CTRL1 => { + mmio.value = atu.atu_type() as usize; + } + PCIE_ATU_UNR_REGION_CTRL2 => { + mmio.value = ATU_ENABLE_BIT as usize; + } + PCIE_ATU_UNR_LOWER_BASE => { + mmio.value = (atu.cpu_base() & 0xffffffff) as usize; + } + PCIE_ATU_UNR_UPPER_BASE => { + mmio.value = ((atu.cpu_base() >> 32) & 0xffffffff) as usize; + } + PCIE_ATU_UNR_LIMIT => { + let limit_value = (atu.cpu_limit() & 0xffffffff) as usize; + mmio.value = if limit_value == 0 { + atu.limit_hw_value() as usize + } else { + limit_value + }; + } + PCIE_ATU_UNR_UPPER_LIMIT => { + let upper_limit = ((atu.cpu_limit() >> 32) & 0xffffffff) as usize; + mmio.value = if upper_limit == 0xffffffff { + atu.upper_limit_hw_value() as usize + } else { + upper_limit + }; + } + PCIE_ATU_UNR_LOWER_TARGET => { + mmio.value = (atu.pci_target() & 0xffffffff) as usize; + } + PCIE_ATU_UNR_UPPER_TARGET => { + mmio.value = ((atu.pci_target() >> 32) & 0xffffffff) as usize; + } + _ => { + warn!("invalid atu0 read {:#x}", atu_offset); + mmio_perform_access(_base, mmio); + } + } + } + } else if mmio.address > ATU_BASE + ATU_REGION_SIZE / 2 { + mmio_perform_access(_base, mmio); + } else if mmio.address >= BIT_LENTH { + // dbi read + mmio_perform_access(_base, mmio); + } else { + warn!("mmio_vpci_handler_dbi read {:#x}", mmio.address); + let offset = (mmio.address & 0xfff) as PciConfigAddress; + let zone = this_zone(); + let mut is_dev_belong_to_zone = false; + + let base = mmio.address as PciConfigAddress - offset + _base as PciConfigAddress; + + let dev: Option = { + let mut guard = zone.write(); + let vbus = &mut guard.vpci_bus; + if let Some(dev) = vbus.get_device_by_base(base) { + is_dev_belong_to_zone = true; + Some(dev) + } else { + drop(guard); + // Clone Arc first while holding GLOBAL_PCIE_LIST lock, then release it + // This avoids holding multiple locks simultaneously + let dev_clone = { + let global_pcie_list = GLOBAL_PCIE_LIST.lock(); + global_pcie_list + .values() + .find(|dev| { + let dev_guard = dev.read(); + dev_guard.get_base() == base + }) + .cloned() + }; + dev_clone + } + }; + + let dev = match dev { + Some(dev) => dev, + None => { + handle_device_not_found(mmio, offset); + return Ok(()); + } + }; + + let is_root = is_this_root_zone(); + let is_direct = true; // dbi handler uses direct mode + + handle_config_space_access(dev, mmio, offset, is_direct, is_root, is_dev_belong_to_zone)?; + } + + Ok(()) +} + +pub fn mmio_vpci_direct_handler(mmio: &mut MMIOAccess, _base: usize) -> HvResult { + let zone = this_zone(); + let offset = (mmio.address & 0xfff) as PciConfigAddress; + let base = mmio.address as PciConfigAddress - offset + _base as PciConfigAddress; + let mut is_dev_belong_to_zone = false; + + let dev: Option = { + let mut guard = zone.write(); + let vbus = &mut guard.vpci_bus; + if let Some(dev) = vbus.get_device_by_base(base) { + is_dev_belong_to_zone = true; + Some(dev) + } else { + drop(guard); + let global_pcie_list = GLOBAL_PCIE_LIST.lock(); + global_pcie_list + .values() + .find(|dev| dev.read().get_base() == base) + .cloned() + } + }; + + let dev = match dev { + Some(dev) => dev, + None => { + handle_device_not_found(mmio, offset); + return Ok(()); + } + }; + + let is_root = is_this_root_zone(); + let is_direct = true; // direct handler uses direct mode + + handle_config_space_access(dev, mmio, offset, is_direct, is_root, is_dev_belong_to_zone)?; + + Ok(()) +} diff --git a/src/pci/pci_struct.rs b/src/pci/pci_struct.rs new file mode 100644 index 00000000..9597693c --- /dev/null +++ b/src/pci/pci_struct.rs @@ -0,0 +1,1884 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// + +use alloc::{collections::btree_map::BTreeMap, sync::Arc, vec::Vec}; +use bit_field::BitField; +use bitvec::{array::BitArray, order::Lsb0, BitArr}; +use core::{ + cmp::Ordering, + fmt::Debug, + ops::{Deref, DerefMut, Range}, + str::FromStr, +}; +use spin::RwLock; + +use super::{ + config_accessors::{PciConfigAccessor, PciConfigMmio}, + mem_alloc::BarAllocator, + pci_access::{ + Bar, EndpointField, EndpointHeader, HeaderType, PciBarRW, PciBridgeHeader, PciCommand, + PciConfigHeader, PciField, PciHeaderRW, PciMem, PciMemType, PciRW, PciRomRW, + }, + pci_access::{BaseClass, DeviceId, DeviceRevision, Interface, SubClass, VendorId}, + PciConfigAddress, +}; + +use crate::{ + config::HvPciDevConfig, + error::{HvErrorNum, HvResult}, + pci::vpci_dev::VpciDevType, +}; + +type VirtualPciConfigBits = BitArr!(for BIT_LENTH, in u8, Lsb0); + +#[derive(Clone, Debug)] +pub struct ConfigValue { + id: (DeviceId, VendorId), + class_and_revision_id: (BaseClass, SubClass, Interface, DeviceRevision), + bar_value: [u32; 6], + rom_value: u32, +} + +impl Default for ConfigValue { + fn default() -> Self { + Self { + id: (0xFFFFu16, 0xFFFFu16), + class_and_revision_id: (0xFFu8, 0u8, 0u8, 0u8), + bar_value: [0; 6], + rom_value: 0, + } + } +} + +impl ConfigValue { + pub fn new( + id: (DeviceId, VendorId), + class_and_revision_id: (BaseClass, SubClass, Interface, DeviceRevision), + ) -> Self { + Self { + id, + class_and_revision_id, + bar_value: [0; 6], + rom_value: 0, + } + } + + pub fn get_id(&self) -> (DeviceId, VendorId) { + self.id + } + + pub fn set_id(&mut self, id: (DeviceId, VendorId)) { + self.id = id; + } + + pub fn get_class_and_revision_id(&self) -> (BaseClass, SubClass, Interface, DeviceRevision) { + self.class_and_revision_id + } + + pub fn get_class(&self) -> (BaseClass, SubClass, Interface) { + let (base, sub, interface, _) = self.class_and_revision_id; + (base, sub, interface) + } + + pub fn get_revision(&self) -> DeviceRevision { + self.class_and_revision_id.3 + } + + pub fn set_class_and_revision_id( + &mut self, + class_and_revision_id: (BaseClass, SubClass, Interface, DeviceRevision), + ) { + self.class_and_revision_id = class_and_revision_id; + } + + pub fn set_class(&mut self, class: (BaseClass, SubClass, Interface)) { + let (_, _, _, revision) = self.class_and_revision_id; + self.class_and_revision_id = (class.0, class.1, class.2, revision); + } + + pub fn get_bar_value(&self, slot: usize) -> u32 { + if slot < 6 { + self.bar_value[slot] + } else { + 0 + } + } + + pub fn set_bar_value(&mut self, slot: usize, value: u32) { + if slot < 6 { + self.bar_value[slot] = value; + } + } + + pub fn get_bar_value_ref(&self, slot: usize) -> &u32 { + &self.bar_value[slot] + } + + pub fn get_bar_value_ref_mut(&mut self, slot: usize) -> &mut u32 { + &mut self.bar_value[slot] + } + + pub fn get_rom_value(&self) -> u32 { + self.rom_value + } + + pub fn set_rom_value(&mut self, value: u32) { + self.rom_value = value; + } +} + +const MAX_DEVICE: u8 = 31; +const MAX_FUNCTION: u8 = 7; +pub const CONFIG_LENTH: u64 = 256; +pub const BIT_LENTH: usize = 128 * 8; + +// PCIe Device/Port Type values +const PCI_EXP_TYPE_ROOT_PORT: u16 = 4; +const PCI_EXP_TYPE_UPSTREAM: u16 = 5; +const PCI_EXP_TYPE_DOWNSTREAM: u16 = 6; +const PCI_EXP_TYPE_PCIE_BRIDGE: u16 = 8; + +#[derive(Clone, Copy, Eq, PartialEq, Default)] +pub struct Bdf { + pub domain: u8, + pub bus: u8, + pub device: u8, + pub function: u8, +} + +impl Bdf { + pub fn new(domain: u8, bus: u8, device: u8, function: u8) -> Self { + Self { + domain, + bus, + device, + function, + } + } + + pub fn new_from_config(config: HvPciDevConfig) -> Self { + Self::new(config.domain, config.bus, config.device, config.function) + } + + pub fn domain(&self) -> u8 { + self.domain + } + + pub fn bus(&self) -> u8 { + self.bus + } + + pub fn device(&self) -> u8 { + self.device + } + + pub fn function(&self) -> u8 { + self.function + } + + pub fn is_host_bridge(&self, bus_begin: u8) -> bool { + if (self.bus, self.device, self.function) == (bus_begin, 0, 0) { + true + } else { + false + } + } +} + +impl Ord for Bdf { + fn cmp(&self, other: &Self) -> Ordering { + self.bus + .cmp(&other.bus) + .then_with(|| self.device.cmp(&other.device)) + .then_with(|| self.function.cmp(&other.function)) + } +} + +impl PartialOrd for Bdf { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl FromStr for Bdf { + type Err = HvErrorNum; + + fn from_str(s: &str) -> Result { + // 0000:00:04.0 + let parts: Vec<&str> = s.split(':').collect(); + if parts.len() != 3 { + return Err(HvErrorNum::EINVAL); + } + + let domain = u8::from_str_radix(parts[0], 16) + .map_err(|_| HvErrorNum::EINVAL) + .unwrap(); + + let bus = u8::from_str_radix(parts[1], 16) + .map_err(|_| HvErrorNum::EINVAL) + .unwrap(); + let device_function: Vec<&str> = parts[2].split('.').collect(); + if device_function.len() != 2 { + panic!("Invalid device.function format"); + } + + let device = u8::from_str_radix(device_function[0], 16) + .map_err(|_| HvErrorNum::EINVAL) + .unwrap(); + let function = u8::from_str_radix(device_function[1], 10) + .map_err(|_| HvErrorNum::EINVAL) + .unwrap(); + + Ok(Bdf { + domain, + bus, + device, + function, + }) + } +} + +impl Debug for Bdf { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "{:04x}:{:02x}:{:02x}.{}", + 0, self.bus, self.device, self.function + ) + } +} + +/* 0: ro; + * 1: rw + */ +#[derive(Debug, Clone)] +pub struct VirtualPciConfigControl { + bits: VirtualPciConfigBits, +} + +impl VirtualPciConfigControl { + /* 0x0F, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x08, */ + pub fn endpoint() -> Self { + Self { + bits: !BitArray::ZERO, + } + } + + pub fn bridge() -> Self { + Self { + bits: !BitArray::ZERO, + } + } + + pub fn host_bridge() -> Self { + Self { + bits: !BitArray::ZERO, + } + } + + pub fn virt_dev() -> Self { + Self { + bits: !BitArray::ZERO, + } + } +} + +/* 0: read hw + * 1: read emu + */ +#[derive(Debug, Clone)] +pub struct VirtualPciAccessBits { + bits: VirtualPciConfigBits, +} + +impl VirtualPciAccessBits { + pub fn endpoint() -> Self { + let mut bits = BitArray::ZERO; + bits[0x0..0x4].fill(true); // ID + bits[0x08..0x0c].fill(true); // CLASS + bits[0x10..0x34].fill(true); //bar and rom + Self { bits } + } + + pub fn bridge() -> Self { + Self { + bits: BitArray::ZERO, + } + } + + pub fn host_bridge() -> Self { + Self { + bits: BitArray::ZERO, + } + } + + pub fn virt_dev() -> Self { + Self { + bits: !BitArray::ZERO, + } + } + + pub fn set_bits(&mut self, range: Range) { + self.bits[range].fill(true); + } +} + +/* VirtualPciConfigSpace + * bdf: the bdf hvisor seeing(same with the bdf without hvisor) + * vbdf: the bdf zone seeing, it can set just you like without sr-iov + * config_value: tmp value for config space + * control: control the satus of rw every bit in config space + * access: Determines whether the variable is read from config_value or hw + * backend: the hw rw interface + */ +#[derive(Clone)] +pub struct VirtualPciConfigSpace { + host_bdf: Bdf, + parent_bdf: Bdf, + bdf: Bdf, + vbdf: Bdf, + config_type: HeaderType, + + base: PciConfigAddress, + + config_value: ConfigValue, + control: VirtualPciConfigControl, + access: VirtualPciAccessBits, + + backend: Arc, + + bararr: Bar, + rom: PciMem, + capabilities: PciCapabilityList, + + dev_type: VpciDevType, +} + +#[derive(Clone)] +pub struct ArcRwLockVirtualPciConfigSpace(Arc>); + +impl ArcRwLockVirtualPciConfigSpace { + pub fn new(dev: VirtualPciConfigSpace) -> Self { + Self(Arc::new(RwLock::new(dev))) + } + + pub fn inner(&self) -> &Arc> { + &self.0 + } + + pub fn access(&self, offset: PciConfigAddress, size: usize) -> bool { + self.0.read().access(offset, size) + } + + pub fn get_bdf(&self) -> Bdf { + self.0.read().get_bdf() + } + + pub fn get_vbdf(&self) -> Bdf { + self.0.read().get_vbdf() + } + + pub fn get_dev_type(&self) -> VpciDevType { + self.0.read().get_dev_type() + } + + pub fn get_config_type(&self) -> HeaderType { + self.0.read().get_config_type() + } + + pub fn get_bararr(&self) -> Bar { + self.0.read().get_bararr() + } + + pub fn get_rom(&self) -> PciMem { + self.0.read().get_rom() + } + + pub fn read_emu(&self, field: EndpointField) -> HvResult { + self.0.write().read_emu(field) + } + + pub fn read_emu64(&self, field: EndpointField) -> HvResult { + self.0.write().read_emu64(field) + } + + pub fn write_emu(&self, field: EndpointField, value: usize) -> HvResult { + self.0.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) + // } + + pub fn read_hw(&self, offset: PciConfigAddress, size: usize) -> HvResult { + self.0.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) + } + + /// Execute a closure with a reference to the bar at the given slot + pub fn with_bar_ref(&self, slot: usize, f: F) -> R + where + F: FnOnce(&PciMem) -> R, + { + let guard = self.0.read(); + let bar = guard.get_bar_ref(slot); + f(bar) + } + + /// Execute a closure with a mutable reference to the bar at the given slot + pub fn with_bar_ref_mut(&self, slot: usize, f: F) -> R + where + F: FnOnce(&mut PciMem) -> R, + { + let mut guard = self.0.write(); + let bar = guard.get_bar_ref_mut(slot); + f(bar) + } + + /// Execute a closure with a reference to the config_value + pub fn with_config_value(&self, f: F) -> R + where + F: FnOnce(&ConfigValue) -> R, + { + let guard = self.0.read(); + f(&guard.config_value) + } + + /// Execute a closure with a mutable reference to the config_value + pub fn with_config_value_mut(&self, f: F) -> R + where + F: FnOnce(&mut ConfigValue) -> R, + { + let mut guard = self.0.write(); + f(&mut guard.config_value) + } + + /// Execute a closure with a reference to the rom + pub fn with_rom_ref(&self, f: F) -> R + where + F: FnOnce(&PciMem) -> R, + { + let guard = self.0.read(); + let rom = &guard.rom; + f(rom) + } + + /// Execute a closure with a mutable reference to the rom + pub fn with_rom_ref_mut(&self, f: F) -> R + where + F: FnOnce(&mut PciMem) -> R, + { + let mut guard = self.0.write(); + let rom = &mut guard.rom; + f(rom) + } + + /// Execute a closure with a reference to the capabilities list + pub fn with_cap(&self, f: F) -> R + where + F: FnOnce(&PciCapabilityList) -> R, + { + let guard = self.0.read(); + f(&guard.capabilities) + } + + pub fn read(&self) -> spin::RwLockReadGuard<'_, VirtualPciConfigSpace> { + self.0.read() + } + + pub fn write(&self) -> spin::RwLockWriteGuard<'_, VirtualPciConfigSpace> { + self.0.write() + } +} + +impl Debug for ArcRwLockVirtualPciConfigSpace { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.0.read().fmt(f) + } +} + +// impl core::ops::Deref for ArcRwLockVirtualPciConfigSpace { +// type Target = Arc>; + +// fn deref(&self) -> &Self::Target { +// &self.0 +// } +// } + +// impl From for ArcRwLockVirtualPciConfigSpace { +// fn from(dev: VirtualPciConfigSpace) -> Self { +// Self::new(dev) +// } +// } + +// impl From>> for ArcRwLockVirtualPciConfigSpace { +// fn from(arc: Arc>) -> Self { +// Self(arc) +// } +// } + +impl VirtualPciConfigSpace { + /* false: some bits ro */ + pub fn writable(&self, offset: PciConfigAddress, size: usize) -> bool { + self.control.bits[offset as usize..offset as usize + size] + .last_zero() + .is_none() + } + + /* false: some bits need read from hw */ + pub fn access(&self, offset: PciConfigAddress, size: usize) -> bool { + self.access.bits[offset as usize..offset as usize + size] + .last_zero() + .is_none() + } + + pub fn get_bararr(&self) -> Bar { + self.bararr + } + + pub fn get_bar_ref(&self, slot: usize) -> &PciMem { + &self.bararr[slot] + } + + pub fn get_bar_ref_mut(&mut self, slot: usize) -> &mut PciMem { + &mut self.bararr[slot] + } + + pub fn set_bar_size_read(&mut self, slot: usize) { + self.bararr[slot].set_size_read(); + } + + pub fn set_bar_virtual_value(&mut self, slot: usize, value: u64) { + self.bararr[slot].set_virtual_value(value); + } + + pub fn set_bar_physical_value(&mut self, slot: usize, value: u64) { + self.bararr[slot].set_value(value); + } + + pub fn clear_bar_size_read(&mut self, slot: usize) { + self.bararr[slot].clear_size_read(); + } + + pub fn get_rom(&self) -> PciMem { + self.rom + } + + pub fn get_dev_type(&self) -> VpciDevType { + self.dev_type + } + + pub fn get_config_value(&self) -> &ConfigValue { + &self.config_value + } + + /// Execute a closure with a mutable reference to the config_value + pub fn with_config_value_mut(&mut self, f: F) -> R + where + F: FnOnce(&mut ConfigValue) -> R, + { + f(&mut self.config_value) + } + + /// Execute a closure with a mutable reference to the bararr + pub fn with_bararr_mut(&mut self, f: F) -> R + where + F: FnOnce(&mut Bar) -> R, + { + f(&mut self.bararr) + } + + pub fn with_cap_mut(&mut self, f: F) -> R + where + F: FnOnce(&mut PciCapabilityList) -> R, + { + f(&mut self.capabilities) + } + + pub fn with_access_mut(&mut self, f: F) -> R + where + F: FnOnce(&mut VirtualPciAccessBits) -> R, + { + f(&mut self.access) + } + + // TODO: check whether need update config + pub fn update_config(&mut self, offset: PciConfigAddress, size: usize, _value: usize) { + match self.get_config_type() { + HeaderType::Endpoint => { + match EndpointField::from(offset as usize, size) { + EndpointField::Bar(_) => { + // Bar values are cached in config_value.bar_value, updated in write_emu + } + _ => {} + } + } + _ => {} + } + } +} + +impl Debug for VirtualPciConfigSpace { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "\n bdf {:#?}\n base {:#x}\n type {:#?}\n {:#?}\n {:#?}\n {:#?}", + self.bdf, self.base, self.config_type, self.bararr, self.rom, self.capabilities + ) + } +} + +impl VirtualPciConfigSpace { + pub(super) fn virt_dev_init_default( + bdf: Bdf, + base: PciConfigAddress, + dev_type: VpciDevType, + config_value: ConfigValue, + bararr: Bar, + ) -> Self { + Self { + host_bdf: Bdf::default(), + parent_bdf: Bdf::default(), + bdf, + vbdf: bdf, + config_type: HeaderType::Endpoint, + base, + config_value, + control: VirtualPciConfigControl::virt_dev(), + access: VirtualPciAccessBits::virt_dev(), + backend: Arc::new(EndpointHeader::new_with_region(PciConfigMmio::new( + base, + CONFIG_LENTH, + ))), + bararr, + rom: PciMem::default(), + capabilities: PciCapabilityList::new(), + dev_type, + } + } + + pub fn virt_dev(bdf: Bdf, base: PciConfigAddress, dev_type: VpciDevType) -> Self { + crate::pci::vpci_dev::virt_dev_init(bdf, base, dev_type) + } + pub fn endpoint( + bdf: Bdf, + base: PciConfigAddress, + backend: Arc, + bararr: Bar, + rom: PciMem, + class_and_revision_id: (DeviceRevision, BaseClass, SubClass, Interface), + id: (DeviceId, VendorId), + ) -> Self { + Self { + host_bdf: Bdf::default(), + parent_bdf: Bdf::default(), + bdf, + vbdf: Bdf::default(), + config_type: HeaderType::Endpoint, + base, + config_value: ConfigValue::new(id, class_and_revision_id), + control: VirtualPciConfigControl::endpoint(), + access: VirtualPciAccessBits::endpoint(), + backend, + bararr, + rom, + capabilities: PciCapabilityList::new(), + dev_type: VpciDevType::Physical, + } + } + + pub fn bridge( + bdf: Bdf, + base: PciConfigAddress, + backend: Arc, + bararr: Bar, + rom: PciMem, + class_and_revision_id: (DeviceRevision, BaseClass, SubClass, Interface), + id: (DeviceId, VendorId), + ) -> Self { + Self { + host_bdf: Bdf::default(), + parent_bdf: Bdf::default(), + bdf, + vbdf: Bdf::default(), + config_type: HeaderType::PciBridge, + base, + config_value: ConfigValue::new(id, class_and_revision_id), + control: VirtualPciConfigControl::bridge(), + access: VirtualPciAccessBits::bridge(), + backend, + bararr, + rom, + capabilities: PciCapabilityList::new(), + dev_type: VpciDevType::Physical, + } + } + + pub fn unknown( + bdf: Bdf, + base: PciConfigAddress, + backend: Arc, + id: (DeviceId, VendorId), + ) -> Self { + Self { + host_bdf: Bdf::default(), + parent_bdf: Bdf::default(), + bdf, + vbdf: Bdf::default(), + config_type: HeaderType::Endpoint, + base, + // Default class: base=0xFF, others 0, revision 0 + config_value: ConfigValue::new(id, (0xFFu8, 0u8, 0u8, 0u8)), + control: VirtualPciConfigControl::endpoint(), + access: VirtualPciAccessBits::endpoint(), + backend, + bararr: Bar::default(), + rom: PciMem::default(), + capabilities: PciCapabilityList::new(), + dev_type: VpciDevType::Physical, + } + } + + pub fn host_bridge( + bdf: Bdf, + base: PciConfigAddress, + backend: Arc, + class_and_revision_id: (DeviceRevision, BaseClass, SubClass, Interface), + ) -> Self { + Self { + host_bdf: bdf, + parent_bdf: bdf, + bdf: bdf, + vbdf: bdf, + config_type: HeaderType::Endpoint, + base, + config_value: ConfigValue::new((0xFFFFu16, 0xFFFFu16), class_and_revision_id), // Default ID for host bridge + control: VirtualPciConfigControl::host_bridge(), + access: VirtualPciAccessBits::host_bridge(), + backend, + bararr: Bar::default(), + rom: PciMem::default(), + capabilities: PciCapabilityList::new(), + dev_type: VpciDevType::Physical, + } + } + + pub fn set_host_bdf(&mut self, host_bdf: Bdf) { + self.host_bdf = host_bdf; + } + + pub fn get_host_bdf(&self) -> Bdf { + self.host_bdf + } + + pub fn set_parent_bdf(&mut self, parent_bdf: Bdf) { + self.parent_bdf = parent_bdf; + } + + pub fn get_bdf(&self) -> Bdf { + self.bdf + } + + pub fn get_vbdf(&self) -> Bdf { + self.vbdf + } + + pub fn get_config_type(&self) -> HeaderType { + self.config_type + } + + pub fn set_vbdf(&mut self, vbdf: Bdf) { + self.vbdf = vbdf; + } + + pub fn get_base(&self) -> PciConfigAddress { + self.base + } + + /* now the space_init just with bar + * Note: space field removed, bar values are cached in config_value.bar_value + */ + pub fn config_value_init(&mut self) { + // Initialize bar_value cache from bar values + for slot in 0..6 { + let bar_value = self.bararr[slot].get_value(); + self.config_value.set_bar_value(slot, bar_value as u32); + } + } +} + +impl VirtualPciConfigSpace { + pub fn read_hw(&mut self, offset: PciConfigAddress, size: usize) -> HvResult { + let r = self.backend.read(offset, size); + if let Ok(value) = r { + self.update_config(offset, size, value); + } + r + } + + pub fn write_hw(&mut self, offset: PciConfigAddress, size: usize, value: usize) -> HvResult { + if self.writable(offset, size) { + let r = self.backend.write(offset, size, value); + if r.is_ok() { + self.update_config(offset, size, value); + } + r + } else { + hv_result_err!(EPERM, "pci: invalid write to hw") + } + } + + pub fn read_emu(&mut self, field: EndpointField) -> HvResult { + let offset = field.to_offset() as PciConfigAddress; + let size = field.size(); + + match field { + EndpointField::ID => { + // Read ID from cached config_value.id field + let id = self.config_value.get_id(); + let id_value = ((id.0 as u32) << 16) | (id.1 as u32); + Ok(id_value as usize) + } + EndpointField::RevisionIDAndClassCode => { + let (base, sub, interface, revision) = + self.config_value.get_class_and_revision_id(); + let value = ((base as u32) << 24) + | ((sub as u32) << 16) + | ((interface as u32) << 8) + | (revision as u32); + Ok(value as usize) + } + EndpointField::Bar(slot) => { + // Read bar_value from cache + if slot < 6 { + Ok(self.config_value.get_bar_value(slot) as usize) + } else { + hv_result_err!(EFAULT, "pci: invalid bar slot: {slot}") + } + } + EndpointField::ExpansionRomBar => { + // Read rom_value from cache + Ok(self.config_value.get_rom_value() as usize) + } + _ => { + // For other fields, read from backend + warn!("read emu {:#?} failed, try read from hw", field); + self.backend.read(offset, size) + } + } + } + + pub fn read_emu64(&mut self, field: EndpointField) -> HvResult { + // Read 64-bit value (used for bar64) + // For Bar(slot), read from slot and slot+1 + match field { + EndpointField::Bar(slot) if slot < 5 => { + // Read from bar_value cache + let low = self.config_value.get_bar_value(slot) as u64; + let high = self.config_value.get_bar_value(slot + 1) as u64; + Ok(low | (high << 32)) + } + _ => { + // Fallback to backend read + warn!("read emu64 {:#?} failed", field); + Ok(0) + } + } + } + + pub fn write_emu(&mut self, field: EndpointField, value: usize) -> HvResult { + match field { + EndpointField::Bar(slot) => { + // Update bar_value cache when writing bar + if slot < 6 { + self.config_value.set_bar_value(slot, value as u32); + } + } + EndpointField::ExpansionRomBar => { + // Update rom_value cache when writing rom bar + self.config_value.set_rom_value(value as u32); + } + _ => { + // For other fields, write to backend + warn!("write emu {:#?} denied", field); + } + } + Ok(()) + } +} + +// Legacy method for backward compatibility - converts offset/size to EndpointField +// pub fn write_emu_legacy(&mut self, offset: PciConfigAddress, size: usize, value: usize) -> HvResult { +// let field = EndpointField::from(offset as usize, size); +// self.write_emu(field, value) +// } + +#[derive(Debug)] +pub struct PciIterator { + allocator: Option, + stack: Vec, + segment: PciConfigAddress, + bus_range: Range, + domain: u8, + function: u8, + is_mulitple_function: bool, + is_finish: bool, + accessor: Arc, +} + +impl PciIterator { + fn get_pci_addr_base(&self, bdf: Bdf) -> PciConfigAddress { + match self.accessor.get_pci_addr_base(bdf) { + Ok(addr) => addr, + Err(_) => 0x0, + } + } + + fn address(&self, parent_bus: u8, bdf: Bdf) -> PciConfigAddress { + let offset = 0; + + match self.accessor.get_physical_address(bdf, offset, parent_bus) { + Ok(addr) => addr, + Err(_) => 0x0, + } + } + + fn get_node(&mut self) -> Option { + // Handle placeholder: pop it first, use bus_range.start for initial scan + let was_placeholder = self + .stack + .last() + .map(|b| b.mmio.is_placeholder()) + .unwrap_or(false); + if was_placeholder { + self.stack.pop(); // Remove placeholder + } + + let (bus, device, function, parent_bus) = if let Some(parent) = self.stack.last() { + // device is already added in next() + (parent.bus, parent.device, self.function, parent.primary_bus) + } else { + // host bridge, so device is 0 + let bus_begin = self.bus_range.start as u8; + (bus_begin, 0, self.function, bus_begin) + }; + + let bdf = Bdf::new(self.domain, bus, device, function); + + let address = self.address(parent_bus, bdf); + let pci_addr_base = self.get_pci_addr_base(bdf); + // info!("get node {:x} {:#?}", address, bdf); + + let region = PciConfigMmio::new(address, CONFIG_LENTH); + let pci_header = PciConfigHeader::new_with_region(region); + let (vender_id, device_id) = pci_header.id(); + + // warn!("vender_id {:#x}", vender_id); + + // Check if device exists + if vender_id == 0xffff || self.accessor.skip_device(bdf) { + if function == 0 { + // Function 0 doesn't exist, so device doesn't exist at all + // Skip all functions and move to next device + // info!( + // "get none - device not present (vendor_id=0xffff) at {:#?}", + // bdf + // ); + self.function = 0; + self.is_mulitple_function = false; + } else { + // Function > 0 doesn't exist, but device might have other functions + // warn!( + // "get none - function not present (vendor_id=0xffff) at {:#?}", + // bdf + // ); + } + return None; + } + + // only check is_mulitple_function for function 0 + if self.function == 0 { + self.is_mulitple_function = pci_header.has_multiple_functions(); + } + + let class_and_revision = pci_header.revision_and_class(); + + match pci_header.header_type() { + HeaderType::Endpoint => { + // For endpoint: push host_bridge if we popped placeholder + if was_placeholder { + let bus_begin = self.bus_range.start as u8; + let host_bridge = Bridge::host_bridge( + self.segment, + bus_begin, + self.is_mulitple_function, + self.function, + ); + self.stack.push(host_bridge); + } + + let mut ep = EndpointHeader::new_with_region(region); + let rom = Self::rom_init(&mut ep); + + let bararr = + Self::bar_mem_init(ep.bar_limit().into(), &mut self.allocator, &mut ep); + + // info!("get node bar mem init end {:#?}", bararr); + + let ep = Arc::new(ep); + let mut node = VirtualPciConfigSpace::endpoint( + bdf, + pci_addr_base, + ep, + bararr, + rom, + class_and_revision, + (device_id, vender_id), + ); + + let _ = node.capability_enumerate(); + + Some(node) + } + HeaderType::PciBridge => { + // For bridge: don't push host_bridge, it will be handled in Iterator::next() + warn!("bridge"); + let mut bridge = PciBridgeHeader::new_with_region(region); + let rom = Self::rom_init(&mut bridge); + + let bararr = + Self::bar_mem_init(bridge.bar_limit().into(), &mut self.allocator, &mut bridge); + + let bridge = Arc::new(bridge); + let mut node = VirtualPciConfigSpace::bridge( + bdf, + pci_addr_base, + bridge, + bararr, + rom, + class_and_revision, + (device_id, vender_id), + ); + + let _ = node.capability_enumerate(); + + Some(node) + } + _ => { + warn!("unknown type"); + let pci_header = Arc::new(pci_header); + Some(VirtualPciConfigSpace::unknown( + bdf, + pci_addr_base, + pci_header, + (device_id, vender_id), + )) + } + } + } + + fn rom_init(dev: &mut D) -> PciMem { + let mut rom = dev.parse_rom(); + if rom.get_type() == PciMemType::Rom { + rom.set_value(rom.get_value() as u64); + rom.set_virtual_value(rom.get_value() as u64); + } + rom + } + + fn bar_mem_init( + bar_max: usize, + allocator: &mut Option, + dev: &mut D, + ) -> Bar { + let mut bararr = dev.parse_bar(); + + info!("{:#?}", bararr); + + if let Some(a) = allocator { + dev.update_command(|mut cmd| { + cmd.remove(PciCommand::IO_ENABLE); + cmd.remove(PciCommand::MEMORY_ENABLE); + cmd + }); + let mut i = 0; + while i < bar_max { + match bararr[i].get_type() { + PciMemType::Mem32 => { + let value = a.alloc_memory32(bararr[i].get_size() as u64).unwrap(); + bararr[i].set_value(value as u64); + bararr[i].set_virtual_value(value as u64); + let _ = dev.write_bar(i as u8, value as u32); + } + PciMemType::Mem64Low => { + let value = a.alloc_memory64(bararr[i].get_size()).unwrap(); + bararr[i].set_value(value); + bararr[i].set_virtual_value(value); + let _ = dev.write_bar(i as u8, value as u32); + i += 1; + bararr[i].set_value(value); + bararr[i].set_virtual_value(value); + let _ = dev.write_bar(i as u8, (value >> 32) as u32); + } + PciMemType::Io => { + //TODO: alloc io in hvisor, just set virt value for now + let value = bararr[i].get_value64(); + bararr[i].set_virtual_value(value); + let _ = dev.write_bar(i as u8, value as u32); + } + _ => {} + } + i += 1; + } + } else { + // use default bar address as virt bar address + let mut i = 0; + while i < bar_max { + match bararr[i].get_type() { + PciMemType::Mem32 => { + let value = bararr[i].get_value64(); + bararr[i].set_virtual_value(value as u64); + let _ = dev.write_bar(i as u8, value as u32); + } + PciMemType::Mem64Low => { + let value = bararr[i].get_value64(); + bararr[i].set_virtual_value(value); + let _ = dev.write_bar(i as u8, value as u32); + i += 1; + bararr[i].set_virtual_value(value); + let _ = dev.write_bar(i as u8, (value >> 32) as u32); + } + PciMemType::Io => { + let value = bararr[i].get_value64(); + bararr[i].set_virtual_value(value); + let _ = dev.write_bar(i as u8, value as u32); + } + _ => {} + } + i += 1; + } + } + bararr + } + + fn get_bridge(&self) -> Bridge { + let a = self.stack.last(); + match a { + Some(bridge) => bridge.clone(), + None => { + unreachable!("get bridge none"); + } + } + } + + fn next_device_not_ok(&mut self) -> bool { + if let Some(parent) = self.stack.last_mut() { + // only one child and skip this bus + if parent.has_secondary_link { + parent.device = MAX_DEVICE; + } + + if parent.device == MAX_DEVICE { + if let Some(mut parent) = self.stack.pop() { + self.is_finish = parent.subordinate_bus as usize == self.bus_range.end; + + parent.update_bridge_bus(); + self.function = parent.function; + self.is_mulitple_function = parent.is_mulitple_function; + return true; + } else { + self.is_finish = true; + } + } else { + parent.device += 1; + } + } else { + self.is_finish = true; + } + + false + } + + fn next(&mut self, current_bridge: Option) { + if let Some(bridge) = current_bridge { + for parent in &mut self.stack { + parent.subordinate_bus += 1; + } + + self.stack.push(bridge.clone()); + + if self.is_mulitple_function && self.function < MAX_FUNCTION { + // Device supports multiple functions and we haven't checked all functions yet + self.function += 1; + return; + } + + self.function = 0; + return; + } + + // Try next function first if device supports multiple functions + if self.is_mulitple_function && self.function < MAX_FUNCTION { + // Device supports multiple functions and we haven't checked all functions yet + self.function += 1; + return; + } + + // All functions of this device have been checked (or device doesn't support multiple functions) + // Move to next device + self.function = 0; + while self.next_device_not_ok() { + // Keep moving to next device until we find a valid one or finish + } + } +} + +impl Iterator for PciIterator { + type Item = VirtualPciConfigSpace; + + fn next(&mut self) -> Option { + while !self.is_finish { + if let Some(mut node) = self.get_node() { + node.config_value_init(); + let bus_begin = self.bus_range.start as u8; + let domain = self.domain; + /* + * when first time to enumerate, placeholder is pop in get_node + * the message of host bridge must be got after get_node() + * so we push host bridge to stack here + */ + if self.stack.is_empty() { + let host_bridge = Bridge::host_bridge( + self.segment, + bus_begin, + self.is_mulitple_function, + self.function, + ); + self.stack.push(host_bridge); + } + let parent = self.stack.last().unwrap(); // Safe because we just ensured it exists + let host_bdf = Bdf::new(domain, bus_begin, 0, 0); + let parent_bdf = Bdf::new(domain, parent.bus, parent.device, 0); + let parent_bus = parent.primary_bus; + node.set_host_bdf(host_bdf); + node.set_parent_bdf(parent_bdf); + self.next(match node.config_value.get_class().0 { + // class code 0x6 is bridge and class.1 0x0 is host bridge + 0x6 if node.config_value.get_class().1 != 0x0 => { + let bdf = Bdf::new(domain, parent.subordinate_bus + 1, 0, 0); + Some(self.get_bridge().next_bridge( + self.address(parent_bus, bdf), + node.has_secondary_link(), + self.is_mulitple_function, + self.function, + )) + } + _ => None, + }); + return Some(node); + } else { + self.next(None); + } + } + None + } +} + +#[derive(Debug, Clone)] +pub struct Bridge { + bus: u8, + device: u8, + function: u8, + subordinate_bus: u8, + secondary_bus: u8, + primary_bus: u8, + mmio: PciConfigMmio, + has_secondary_link: bool, + is_mulitple_function: bool, +} + +impl Bridge { + // Create a placeholder bridge for initializing the stack. + // This is not a real host bridge, just a placeholder to track bus hierarchy. + pub fn placeholder() -> Self { + Self { + bus: 0, + device: 0, + function: 0, + subordinate_bus: 0, + secondary_bus: 0, + primary_bus: 0, + mmio: PciConfigMmio::new(0, 0), // Dummy mmio for placeholder + has_secondary_link: false, + is_mulitple_function: false, + } + } + + pub fn host_bridge( + address: PciConfigAddress, + bus_begin: u8, + is_mulitple_function: bool, + function: u8, + ) -> Self { + Self { + bus: bus_begin, + device: 0, + function, + subordinate_bus: bus_begin, + secondary_bus: bus_begin, + primary_bus: bus_begin, + mmio: PciConfigMmio::new(address, CONFIG_LENTH), + has_secondary_link: false, + is_mulitple_function, + } + } + + pub fn next_bridge( + &self, + address: PciConfigAddress, + has_secondary_link: bool, + is_mulitple_function: bool, + function: u8, + ) -> Self { + let mmio = PciConfigMmio::new(address, CONFIG_LENTH); + Self { + bus: self.subordinate_bus + 1, + device: 0, + function, + subordinate_bus: self.subordinate_bus + 1, + secondary_bus: self.subordinate_bus + 1, + primary_bus: self.bus, + mmio, + has_secondary_link, + is_mulitple_function, + } + } + + pub fn update_bridge_bus(&mut self) { + // Skip update for placeholder bridges (they don't have real mmio) + if self.mmio.is_placeholder() { + return; + } + // we need to update the bridge bus number if we want linux not to update bus number + unsafe { + let ptr = self.mmio.access::(0x18); + let mut value = ptr.read_volatile(); + value.set_bits(16..24, self.subordinate_bus.into()); + value.set_bits(8..16, self.secondary_bus.into()); + value.set_bits(0..8, self.primary_bus.into()); + ptr.write_volatile(value); + } + } + + pub fn set_has_secondary_link(&mut self, value: bool) { + self.has_secondary_link = value; + } +} + +/* In fact, the size will be managed by the pci_mmio_handler, so only base is needed here */ +pub struct RootComplex { + pub mmio_base: PciConfigAddress, + pub accessor: Arc, // Unified accessor +} + +impl RootComplex { + fn __enumerate( + &mut self, + range: Option>, + domain: u8, + bar_alloc: Option, + ) -> PciIterator { + let mmio_base = self.mmio_base; + let range = range.unwrap_or_else(|| 0..0x100); + PciIterator { + allocator: bar_alloc, + stack: vec![Bridge::placeholder()], + segment: mmio_base, + bus_range: range, + domain, + function: 0, + is_mulitple_function: false, + is_finish: false, + accessor: self.accessor.clone(), // accessor to iterator + } + } + + pub fn enumerate( + &mut self, + range: Option>, + domain: u8, + bar_alloc: Option, + ) -> PciIterator { + self.__enumerate(range, domain, bar_alloc) + } +} + +#[derive(Debug)] +pub struct VirtualRootComplex { + devs: BTreeMap, + base_to_bdf: BTreeMap, +} + +impl VirtualRootComplex { + pub fn new() -> Self { + Self { + devs: BTreeMap::new(), + base_to_bdf: BTreeMap::new(), + } + } + + pub fn insert( + &mut self, + bdf: Bdf, + dev: VirtualPciConfigSpace, + ) -> Option { + let base = dev.get_base(); + info!("pci insert base {:#x} to bdf {:#?}", base, bdf); + self.base_to_bdf.insert(base, bdf); + self.devs + .insert(bdf, ArcRwLockVirtualPciConfigSpace::new(dev)) + } + + pub fn devs(&mut self) -> &mut BTreeMap { + &mut self.devs + } + + pub fn get(&self, bdf: &Bdf) -> Option<&ArcRwLockVirtualPciConfigSpace> { + self.devs.get(bdf) + } + + pub fn get_mut(&mut self, bdf: &Bdf) -> Option<&mut ArcRwLockVirtualPciConfigSpace> { + self.devs.get_mut(bdf) + } + + /* because the base of device may discontinuous,get device by base is simpler */ + pub fn get_device_by_base( + &mut self, + base: PciConfigAddress, + ) -> Option { + let bdf = self.base_to_bdf.get(&base).copied()?; + self.devs.get(&bdf).cloned() + } +} + +#[derive(Debug)] +pub struct CapabilityIterator { + backend: Arc, + offset: PciConfigAddress, +} + +impl CapabilityIterator { + pub fn get_offset(&self) -> PciConfigAddress { + self.offset + } + + pub fn get_next_cap(&mut self) -> HvResult { + let address = + self.backend.read(self.offset, 2).unwrap().get_bits(8..16) as PciConfigAddress; + self.offset = address; + Ok(()) + } + + pub fn get_id(&self) -> PciConfigAddress { + self.backend.read(self.offset, 2).unwrap().get_bits(0..8) as PciConfigAddress + } + + pub fn get_extension(&self) -> u16 { + self.backend.read(self.offset, 4).unwrap().get_bits(16..32) as u16 + } +} + +impl Iterator for CapabilityIterator { + type Item = PciCapability; + + fn next(&mut self) -> Option { + if self.offset == 0x34 { + let first_cap_offset = self.backend.read(0x34, 1).unwrap() as PciConfigAddress; + if first_cap_offset == 0 { + return None; + } + self.offset = first_cap_offset; + } + + while self.get_offset() != 0 { + debug!("get cap {:#x}", self.get_offset()); + // Get current capability before moving to next + let cap = + PciCapability::from_address(self.get_offset(), self.get_id(), self.backend.clone()); + // Move to next capability + let _ = self.get_next_cap(); + if let Some(cap) = cap { + return Some(cap); + } + } + None + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum CapabilityType { + // Power management capability, Cap ID = `0x01` + PowerManagement, + // Accelerated graphics port capability, Cap ID = `0x02` + AcceleratedGraphicsPort, + // Vital product data capability, Cap ID = `0x3` + VitalProductData, + // Slot identification capability, Cap ID = `0x04` + SlotIdentification, + // Message signalling interrupts capability, Cap ID = `0x05` + Msi, + // CompactPCI HotSwap capability, Cap ID = `0x06` + CompactPCIHotswap, + // PCI-X capability, Cap ID = `0x07` + PciX, + // HyperTransport capability, Cap ID = `0x08` + HyperTransport, + // Vendor-specific capability, Cap ID = `0x09` + Vendor, + // Debug port capability, Cap ID = `0x0A` + DebugPort, + // CompactPCI Central Resource Control capability, Cap ID = `0x0B` + CompactPCICentralResourceControl, + // PCI Standard Hot-Plug Controller capability, Cap ID = `0x0C` + PciHotPlugControl, + // Bridge subsystem vendor/device ID capability, Cap ID = `0x0D` + BridgeSubsystemVendorId, + // AGP Target PCI-PCI bridge capability, Cap ID = `0x0E` + AGP3, + // PCI Express capability, Cap ID = `0x10` + PciExpress, + // MSI-X capability, Cap ID = `0x11` + MsiX, + // Unknown capability + Unknown, +} + +impl Debug for CapabilityType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + CapabilityType::PowerManagement => write!(f, "PowerManagement(0x01)"), + CapabilityType::AcceleratedGraphicsPort => write!(f, "AcceleratedGraphicsPort(0x02)"), + CapabilityType::VitalProductData => write!(f, "VitalProductData(0x03)"), + CapabilityType::SlotIdentification => write!(f, "SlotIdentification(0x04)"), + CapabilityType::Msi => write!(f, "Msi(0x05)"), + CapabilityType::CompactPCIHotswap => write!(f, "CompactPCIHotswap(0x06)"), + CapabilityType::PciX => write!(f, "PciX(0x07)"), + CapabilityType::HyperTransport => write!(f, "HyperTransport(0x08)"), + CapabilityType::Vendor => write!(f, "Vendor(0x09)"), + CapabilityType::DebugPort => write!(f, "DebugPort(0x0A)"), + CapabilityType::CompactPCICentralResourceControl => { + write!(f, "CompactPCICentralResourceControl(0x0B)") + } + CapabilityType::PciHotPlugControl => write!(f, "PciHotPlugControl(0x0C)"), + CapabilityType::BridgeSubsystemVendorId => write!(f, "BridgeSubsystemVendorId(0x0D)"), + CapabilityType::AGP3 => write!(f, "AGP3(0x0E)"), + CapabilityType::PciExpress => write!(f, "PciExpress(0x10)"), + CapabilityType::MsiX => write!(f, "MsiX(0x11)"), + CapabilityType::Unknown => write!(f, "Unknown(0x00)"), + } + } +} + +impl CapabilityType { + fn from_id(id: PciConfigAddress) -> Self { + match id { + 0x01 => CapabilityType::PowerManagement, + 0x02 => CapabilityType::AcceleratedGraphicsPort, + 0x03 => CapabilityType::VitalProductData, + 0x04 => CapabilityType::SlotIdentification, + 0x05 => CapabilityType::Msi, + 0x06 => CapabilityType::CompactPCIHotswap, + 0x07 => CapabilityType::PciX, + 0x08 => CapabilityType::HyperTransport, + 0x09 => CapabilityType::Vendor, + 0x0A => CapabilityType::DebugPort, + 0x0B => CapabilityType::CompactPCICentralResourceControl, + 0x0C => CapabilityType::PciHotPlugControl, + 0x0D => CapabilityType::BridgeSubsystemVendorId, + 0x0E => CapabilityType::AGP3, + 0x10 => CapabilityType::PciExpress, + 0x11 => CapabilityType::MsiX, + _ => CapabilityType::Unknown, + } + } + + pub fn to_id(&self) -> PciConfigAddress { + match self { + CapabilityType::PowerManagement => 0x01, + CapabilityType::AcceleratedGraphicsPort => 0x02, + CapabilityType::VitalProductData => 0x03, + CapabilityType::SlotIdentification => 0x04, + CapabilityType::Msi => 0x05, + CapabilityType::CompactPCIHotswap => 0x06, + CapabilityType::PciX => 0x07, + CapabilityType::HyperTransport => 0x08, + CapabilityType::Vendor => 0x09, + CapabilityType::DebugPort => 0x0A, + CapabilityType::CompactPCICentralResourceControl => 0x0B, + CapabilityType::PciHotPlugControl => 0x0C, + CapabilityType::BridgeSubsystemVendorId => 0x0D, + CapabilityType::AGP3 => 0x0E, + CapabilityType::PciExpress => 0x10, + CapabilityType::MsiX => 0x11, + CapabilityType::Unknown => 0x00, + } + } +} + +#[derive(Clone)] +pub struct PciCapability { + cap_type: CapabilityType, + region: Arc>, +} + +impl PciCapability { + pub fn get_type(&self) -> CapabilityType { + self.cap_type + } + + /// Execute a closure with a read lock on the capability region + pub fn with_region(&self, f: F) -> R + where + F: FnOnce(&dyn PciCapabilityRegion) -> R, + { + let guard = self.region.read(); + f(&*guard) + } + + /// Execute a closure with a write lock on the capability region + pub fn with_region_mut(&self, f: F) -> R + where + F: FnOnce(&mut dyn PciCapabilityRegion) -> R, + { + let mut guard = self.region.write(); + f(&mut *guard) + } + + fn from_address( + offset: PciConfigAddress, + id: PciConfigAddress, + backend: Arc, + ) -> Option { + match CapabilityType::from_id(id) { + CapabilityType::Unknown => None, + CapabilityType::Msi => { + let region = Arc::new(RwLock::new(StandardPciCapabilityRegion::new( + offset, 32, backend, + ))); + return Some(PciCapability { + cap_type: CapabilityType::Msi, + region, + }); + } + _ => { + let region = Arc::new(RwLock::new(StandardPciCapabilityRegion::new( + offset, 32, backend, + ))); + Some(PciCapability { + cap_type: CapabilityType::from_id(id), + region, + }) + } + } + } + + pub fn new_virt( + cap_type: CapabilityType, + region: Arc>, + ) -> Self { + Self { cap_type, region } + } + + pub fn get_offset(&self) -> PciConfigAddress { + self.with_region(|region| region.get_offset()) + } + + pub fn get_size(&self) -> usize { + self.with_region(|region| region.get_size()) + } + + fn next_cap(&self) -> HvResult { + self.with_region(|region| region.next_cap()) + } +} + +impl Debug for PciCapability { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?}", self.cap_type) + } +} + +pub trait PciCapabilityRegion: Send + Sync { + /// Read from capability region at relative offset + /// offset: relative offset from capability start (0 = capability start) + fn read(&self, offset: PciConfigAddress, size: usize) -> HvResult; + + /// Write to capability region at relative offset + /// offset: relative offset from capability start (0 = capability start) + fn write(&mut self, offset: PciConfigAddress, size: usize, value: u32) -> HvResult; + + /// Get absolute offset of capability in config space + fn get_offset(&self) -> PciConfigAddress; + + /// Get size of capability + fn get_size(&self) -> usize; + + /// Get next capability offset by reading next pointer + /// Default implementation: read 2 bytes at offset 0 (capability start), extract bits(8..16) as next pointer + fn next_cap(&self) -> HvResult { + let value = self.read(0, 2)?; + let next_offset = (value as u16).get_bits(8..16) as PciConfigAddress; + Ok(next_offset) + } +} + +pub struct StandardPciCapabilityRegion { + offset: PciConfigAddress, + size: usize, + backend: Arc, +} + +impl StandardPciCapabilityRegion { + pub fn new(offset: PciConfigAddress, size: usize, backend: Arc) -> Self { + Self { + offset, + size, + backend, + } + } +} + +impl PciCapabilityRegion for StandardPciCapabilityRegion { + fn read(&self, offset: PciConfigAddress, size: usize) -> HvResult { + self.backend + .read(self.offset + offset, size) + .map(|v| v as u32) + } + + fn write(&mut self, offset: PciConfigAddress, size: usize, value: u32) -> HvResult { + self.backend + .write(self.offset + offset, size, value as usize) + } + + fn get_offset(&self) -> PciConfigAddress { + self.offset + } + + fn get_size(&self) -> usize { + self.size + } +} + +#[derive(Clone)] +pub struct PciCapabilityList(BTreeMap); + +impl PciCapabilityList { + pub fn new() -> Self { + Self(BTreeMap::new()) + } +} + +// impl Default for PciCapabilityList { +// fn default() -> Self { +// Self::new() +// } +// } + +impl Deref for PciCapabilityList { + type Target = BTreeMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for PciCapabilityList { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Debug for PciCapabilityList { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "PciCapabilityList {{\n")?; + for (offset, capability) in &self.0 { + write!(f, "0x{:x} {:?}\n", offset, capability)?; + } + write!(f, "}}")?; + Ok(()) + } +} + +impl VirtualPciConfigSpace { + fn _capability_enumerate(&self, backend: Arc) -> CapabilityIterator { + CapabilityIterator { + backend, + offset: 0x34, + } + } + + pub fn capability_enumerate(&mut self) { + let mut capabilities = PciCapabilityList::new(); + for capability in self._capability_enumerate(self.backend.clone()) { + match capability.get_type() { + CapabilityType::Msi => {} + CapabilityType::MsiX => {} + CapabilityType::PciExpress => {} + _ => {} + } + capabilities.insert(capability.get_offset(), capability); + } + info!("capability {:#?}", capabilities); + self.capabilities = capabilities; + } + + //TODO: check secondary link by read cap + pub fn has_secondary_link(&self) -> bool { + match self.config_type { + HeaderType::PciBridge => { + // Find PciExpress capability + // warn!("has_secondary_link {:#?}", self.capabilities); + // for (_, capability) in &self.capabilities { + // if capability.cap_type == CapabilityType::PciExpress { + // // Read PCIe Capability Register at offset + 0x00 + // // Bits 4:0 contain the Device/Port Type + // let offset = capability.get_offset(); + // if let Ok(cap_reg) = self.backend.read(offset, 2) { + // let type_val = (cap_reg as u16).get_bits(0..5); + // if type_val == PCI_EXP_TYPE_ROOT_PORT || type_val == PCI_EXP_TYPE_PCIE_BRIDGE { + // return true; + // } else if type_val == PCI_EXP_TYPE_UPSTREAM || type_val == PCI_EXP_TYPE_DOWNSTREAM { + // // Parent check is not implemented, set to false for now + // return false; + // } + // } + // break; + // } + // } + // false + // #[cfg(feature = "dwc_pcie")] + // return true; + // #[cfg(not(feature = "dwc_pcie"))] + return false; + } + _ => false, + } + } +} diff --git a/src/pci/pci_test.rs b/src/pci/pci_test.rs new file mode 100644 index 00000000..775ff605 --- /dev/null +++ b/src/pci/pci_test.rs @@ -0,0 +1,406 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// + +#![allow(dead_code)] +use core::str::FromStr; + +use alloc::{collections::btree_map::BTreeMap, sync::Arc}; +use spin::{lazy::Lazy, mutex::Mutex}; + +use super::{ + pci_handler::mmio_vpci_direct_handler, + pci_struct::{Bdf, VirtualPciConfigSpace, CONFIG_LENTH}, +}; + +#[cfg(feature = "ecam_pcie")] +use super::{ + mem_alloc::BaseAllocator, + pci_struct::RootComplex, +}; + +use crate::{ + memory::{mmio_perform_access, MMIOAccess}, + pci::{config_accessors::PciConfigMmio, pci_access::EndpointHeader}, + percpu::this_zone, +}; + +#[cfg(feature = "ecam_pcie")] +use crate::pci::config_accessors::ecam::EcamConfigAccessor; + +pub static GLOBAL_PCIE_LIST_TEST: Lazy>> = + Lazy::new(|| { + let m = BTreeMap::new(); + Mutex::new(m) + }); + +#[cfg(feature = "ecam_pcie")] +pub fn pcie_test() { + warn!("pcie test"); + let mut allocator = BaseAllocator::default(); + allocator.set_mem32(0x10000000, 0x2efeffff); + allocator.set_mem64(0x8000000000, 0xffffffffff - 0x8000000000); + + let mut root = RootComplex::new_ecam(0x4010000000); + for node in root.enumerate(None, 0, Some(allocator)) { + GLOBAL_PCIE_LIST_TEST.lock().insert(node.get_bdf(), node); + } + warn!("pcie guest init done"); + warn!("{:#?}", GLOBAL_PCIE_LIST_TEST); +} + +pub fn pcie_guest_init() { + let zone = this_zone(); + let vbus = &mut zone.write().vpci_bus; + + let mut guard = GLOBAL_PCIE_LIST_TEST.lock(); + + let vbdf = Bdf::from_str("0000:00:00.0").unwrap(); + let bdf = Bdf::from_str("0000:00:00.0").unwrap(); + let base = 0x4010000000; // Base address for test + let backend = EndpointHeader::new_with_region(PciConfigMmio::new(base, CONFIG_LENTH)); + let dev = + VirtualPciConfigSpace::host_bridge(bdf, base, Arc::new(backend), (0x6u8, 0u8, 0u8, 0x0)); + vbus.insert(vbdf, dev); + + let vbdf = Bdf::from_str("0000:00:01.0").unwrap(); + let bdf = Bdf::from_str("0000:00:01.0").unwrap(); + if let Some(mut dev) = guard.remove(&bdf) { + // let _ = dev.write_hw(0x20, 4, 0xffffffff); + // let value1 = dev.read_hw(0x20, 4).unwrap(); + // let _ = dev.write_hw(0x24, 4, 0xffffffff); + // let value2 = dev.read_hw(0x24, 4).unwrap(); + // info!("{:#?} bar64 {:x}, {:x}", bdf, (value1 as u64), ((value2 as u64) << 32u64)); + dev.set_vbdf(vbdf); + vbus.insert(vbdf, dev); + } else { + warn!("can not find dev"); + } + + let vbdf = Bdf::from_str("0000:00:02.0").unwrap(); + let bdf = Bdf::from_str("0000:00:02.0").unwrap(); + if let Some(mut dev) = guard.remove(&bdf) { + dev.set_vbdf(vbdf); + vbus.insert(vbdf, dev); + } else { + warn!("can not find dev"); + } + + let vbdf = Bdf::from_str("0000:00:03.0").unwrap(); + let bdf = Bdf::from_str("0000:00:03.0").unwrap(); + if let Some(mut dev) = guard.remove(&bdf) { + dev.set_vbdf(vbdf); + vbus.insert(vbdf, dev); + } else { + warn!("can not find dev"); + } + + info!("{:#?}", vbus); + info!("pcie guest init done"); +} + +pub fn ecam_pcie_guest_test() { + let zone = this_zone(); + let bdf = Bdf::from_str("0000:00:01.0").unwrap(); + // Get base from VirtualPciConfigSpace and add offset + // Use a block scope to ensure the read lock is released before calling mmio_vpci_direct_handler + let address = { + let vbus = &zone.read().vpci_bus; + if let Some(vdev) = vbus.get(&bdf) { + vdev.read().get_base() + } else { + warn!("can not find dev {:#?} for test", bdf); + 0 + } + }; + let value = 0; + let test_address = address + 0x14; + + let mut mmio = MMIOAccess { + address: test_address as _, + size: 4, + is_write: false, + value: value, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!( + "mmio offset {:x}, is_wirte {}, size {}, value 0x{:x}", + mmio.address, mmio.is_write, mmio.size, mmio.value + ); + + let mut mmio = MMIOAccess { + address: test_address as _, + size: 4, + is_write: true, + value: 0xFFFF_FFFF, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!( + "mmio offset {:x}, is_wirte {}, size {}, value 0x{:x}", + mmio.address, mmio.is_write, mmio.size, mmio.value + ); + + let mut mmio = MMIOAccess { + address: test_address as _, + size: 4, + is_write: false, + value: value, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!( + "mmio offset {:x}, is_wirte {}, size {}, value 0x{:x}", + mmio.address, mmio.is_write, mmio.size, mmio.value + ); + + let mut mmio = MMIOAccess { + address: test_address as _, + size: 4, + is_write: true, + value: 0x80000000, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!( + "mmio offset {:x}, is_wirte {}, size {}, value 0x{:x}", + mmio.address, mmio.is_write, mmio.size, mmio.value + ); + + let mut mmio = MMIOAccess { + address: test_address as _, + size: 4, + is_write: false, + value: value, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!( + "mmio offset {:x}, is_wirte {}, size {}, value 0x{:x}", + mmio.address, mmio.is_write, mmio.size, mmio.value + ); + + let mut mmio = MMIOAccess { + address: test_address as _, + size: 4, + is_write: true, + value: 0x70000000, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!( + "mmio offset {:x}, is_wirte {}, size {}, value 0x{:x}", + mmio.address, mmio.is_write, mmio.size, mmio.value + ); + + let mut mmio = MMIOAccess { + address: test_address as _, + size: 4, + is_write: false, + value: value, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!( + "mmio offset {:x}, is_wirte {}, size {}, value 0x{:x}", + mmio.address, mmio.is_write, mmio.size, mmio.value + ); + + info!("pcie guest test passed"); + + loop {} +} + +pub fn dwc_pcie_guest_test() { + info!("pcie dwc test begin"); + let mut mmio = MMIOAccess { + address: 0x3c0400000, + size: 4, + is_write: false, + value: 0x0, + }; + let ret = mmio_perform_access(0, &mut mmio); + info!("{:#?}", ret); + info!( + "mmio offset {:x}, is_wirte {}, size {}, value 0x{:x}", + mmio.address, mmio.is_write, mmio.size, mmio.value + ); + use core::ptr::read_volatile; + unsafe { + let a = read_volatile(0x3c0000900 as *const u32); + info!("{a}"); + }; + + info!("pcie dwc test passed"); +} + +pub fn ecam_pcie_guest_test64() { + let zone = this_zone(); + let bdf = Bdf::from_str("0000:00:02.0").unwrap(); + // Get base from VirtualPciConfigSpace and add offset + // Use a block scope to ensure the read lock is released before calling mmio_vpci_direct_handler + let address = { + let vbus = &zone.read().vpci_bus; + if let Some(vdev) = vbus.get(&bdf) { + vdev.read().get_base() + } else { + warn!("can not find dev {:#?} for test", bdf); + 0 + } + }; + + if address == 0 { + warn!("Failed to get device base address"); + return; + } + + // 64-bit BAR: slot 4 (Mem64Low) at offset 0x20, slot 5 (Mem64High) at offset 0x24 + let bar_low_offset = 0x20; // slot 4: 0x10 + 4 * 4 + let bar_high_offset = 0x24; // slot 5: 0x10 + 5 * 4 + + let test_address_low = address + bar_low_offset; + let test_address_high = address + bar_high_offset; + + info!("Testing 64-bit BAR for device {:#?}", bdf); + info!( + "Base address: 0x{:x}, BAR Low offset: 0x{:x}, BAR High offset: 0x{:x}", + address, bar_low_offset, bar_high_offset + ); + + let mut mmio = MMIOAccess { + address: test_address_low as _, + size: 4, + is_write: false, + value: 0, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!( + "Step 1 - Read Mem64Low (offset 0x{:x}): value 0x{:x}", + bar_low_offset, mmio.value + ); + + let mut mmio = MMIOAccess { + address: test_address_low as _, + size: 4, + is_write: true, + value: 0xFFFF_FFFF, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!("Step 2 - Write 0xFFFF_FFFF to Mem64Low (size probe)"); + + let mut mmio = MMIOAccess { + address: test_address_low as _, + size: 4, + is_write: false, + value: 0, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!( + "Step 3 - Read Mem64Low after size probe: value 0x{:x}", + mmio.value + ); + + let low_addr = 0x90000000; + let mut mmio = MMIOAccess { + address: test_address_low as _, + size: 4, + is_write: true, + value: low_addr, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!( + "Step 4 - Write low 32-bit address 0x{:x} to Mem64Low", + low_addr + ); + + let mut mmio = MMIOAccess { + address: test_address_low as _, + size: 4, + is_write: false, + value: 0, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + let read_low = mmio.value; + info!("Step 5 - Read Mem64Low: value 0x{:x}", read_low); + + let high_addr = 0x00000001; // High 32 bits of 64-bit address + let mut mmio = MMIOAccess { + address: test_address_high as _, + size: 4, + is_write: true, + value: high_addr, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!( + "Step 6 - Write high 32-bit address 0x{:x} to Mem64High", + high_addr + ); + + let mut mmio = MMIOAccess { + address: test_address_low as _, + size: 4, + is_write: false, + value: 0, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + let final_low = mmio.value; + + let mut mmio = MMIOAccess { + address: test_address_high as _, + size: 4, + is_write: false, + value: 0, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + let final_high = mmio.value; + + let full_64bit_addr = (final_low as u64) | ((final_high as u64) << 32); + info!( + "Step 7 - Full 64-bit address: Mem64Low=0x{:x}, Mem64High=0x{:x}, Combined=0x{:x}", + final_low, final_high, full_64bit_addr + ); + + let new_low_addr = 0x70000000; + let mut mmio = MMIOAccess { + address: test_address_low as _, + size: 4, + is_write: true, + value: new_low_addr, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!( + "Step 8 - Write new low address 0x{:x} to Mem64Low", + new_low_addr + ); + + let mut mmio = MMIOAccess { + address: test_address_low as _, + size: 4, + is_write: false, + value: 0, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!( + "Step 9 - Read Mem64Low after update: value 0x{:x}", + mmio.value + ); + + let mut mmio = MMIOAccess { + address: test_address_high as _, + size: 4, + is_write: false, + value: 0, + }; + let _ = mmio_vpci_direct_handler(&mut mmio, 0); + info!("Step 10 - Read Mem64High: value 0x{:x}", mmio.value); + + info!("pcie guest test64 passed"); + + loop {} +} diff --git a/src/pci/pcibar.rs b/src/pci/pcibar.rs deleted file mode 100644 index 4657fb75..00000000 --- a/src/pci/pcibar.rs +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2025 Syswonder -// hvisor is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR -// FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. -// -// Syswonder Website: -// https://www.syswonder.org -// -// Authors: -// -#[derive(Debug, Default, Clone, Copy)] -pub struct PciBar { - val: u32, - bar_type: BarType, - size: usize, -} - -#[derive(Debug, Copy, Clone)] -pub struct BarRegion { - pub start: usize, - pub size: usize, - pub bar_type: BarType, -} - -#[derive(Default, Debug, Copy, Clone, PartialEq)] -pub enum BarType { - Mem32, - Mem64, - IO, - #[default] - Unknown, -} - -impl PciBar { - // origin_val: the register value written by vm - // val: write !0u64 to the BAR to get the size this BAR need - pub fn init(&mut self, origin_val: u32, val: u32) { - self.val = origin_val; - - if let Some(fix_bit) = (0..32).rev().find(|&off| val & (1 << off) == 0) { - if fix_bit != 31 { - self.size = 1 << (fix_bit + 1); - } else { - // fix_bit == 31, indicates that all the bits are read-only - self.size = 0; - } - } else { - // all the bits are rw, indicates this BAR's value is the upper 32 bits of a region's address - // so the size depends on the next BAR, set self.size to 1, or the value will overflow - self.size = 1; - } - - self.bar_type = match self.val & 0b1 { - 0b1 => BarType::IO, - _ => match self.val & 0b110 { - 0b000 => BarType::Mem32, - 0b100 => BarType::Mem64, - _ => BarType::Unknown, - }, - }; - } - - pub fn is_mutable(&self) -> bool { - match self.size { - 0 => false, - _ => true, - } - } - - pub fn mem_type_64(&self) -> bool { - match self.bar_type { - BarType::Mem64 => true, - _ => false, - } - } - - pub fn get_32b_region(&self) -> BarRegion { - BarRegion { - start: (self.val & 0xfffffff0) as _, - size: self.size, - bar_type: self.bar_type, - } - } - - pub fn get_upper_mem64_32b(&self) -> BarRegion { - BarRegion { - start: self.val as _, // upper 32bits are all mutable - size: self.size, - bar_type: self.bar_type, - } - } - - pub fn get_64b_region(&self, lower_region: BarRegion) -> BarRegion { - let higher_region = self.get_upper_mem64_32b(); - // info!("mm64, high: {:#x}, low: {:#x}", higher_region.start, lower_region.start); - BarRegion { - start: (higher_region.start << 32) + lower_region.start, - size: higher_region.size * lower_region.size, - bar_type: BarType::Mem64, - } - } - - pub fn generate_vbar(&self) -> VirtPciBar { - match self.size { - 0 => VirtPciBar { - val: self.val, - mask: 0x0, - }, - 1 => VirtPciBar { - val: self.val, - mask: 0xffffffff, - }, - _ => VirtPciBar { - val: self.val, - mask: !((self.size - 1) as u64) as _, - }, - } - } -} - -#[derive(Default, Clone, Debug, Copy)] -pub struct VirtPciBar { - val: u32, - mask: u32, -} - -impl VirtPciBar { - pub fn new(val: u32, mask: u32) -> Self { - Self { - val: val, - mask: mask, - } - } - - pub fn read(&self) -> u32 { - self.val - } - - pub fn write(&mut self, new_val: u32) { - self.val = (new_val & self.mask) | (self.val & !self.mask); - } -} diff --git a/src/pci/phantom_cfg.rs b/src/pci/phantom_cfg.rs deleted file mode 100644 index b4d9252e..00000000 --- a/src/pci/phantom_cfg.rs +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright (c) 2025 Syswonder -// hvisor is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR -// FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. -// -// Syswonder Website: -// https://www.syswonder.org -// -// Authors: -// -use super::{ - cfg_base, endpoint::EndpointConfig, extract_reg_addr, pcibar::VirtPciBar, CFG_BAR0, CFG_BAR1, - CFG_BAR2, CFG_BAR3, CFG_BAR4, CFG_BAR5, CFG_CAP_PTR_OFF, CFG_CLASS_CODE_OFF, CFG_CMD_OFF, - CFG_EXT_CAP_PTR_OFF, CFG_INT_LINE, CFG_INT_PIN, CFG_IO_BASE, CFG_IO_BASE_UPPER16, CFG_IO_LIMIT, - CFG_IO_LIMIT_UPPER16, CFG_MEM_BASE, CFG_MEM_LIMIT, CFG_PREF_BASE_UPPER32, - CFG_PREF_LIMIT_UPPER32, CFG_PREF_MEM_BASE, CFG_PREF_MEM_LIMIT, CFG_PRIMARY_BUS, - CFG_SECONDARY_BUS, NUM_BAR_REGS_TYPE0, NUM_BAR_REGS_TYPE1, NUM_MAX_BARS, -}; -use crate::{ - error::HvResult, - memory::{mmio_perform_access, MMIOAccess}, - pci::PHANTOM_DEV_HEADER, - zone::this_zone_id, -}; -use alloc::collections::btree_map::BTreeMap; -use core::{ptr, usize}; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum PhantomCfgType { - ENDPOINT, - BRIDGE, -} - -#[derive(Debug, Copy, Clone)] -pub struct PhantomCfg { - pub bdf: usize, - command: u16, - status: u16, - int_line: u8, - v_bars: [VirtPciBar; NUM_MAX_BARS], - bar_num: usize, - cfg_type: PhantomCfgType, -} - -impl PhantomCfg { - pub fn new(bdf: usize, v_bars: [VirtPciBar; NUM_MAX_BARS], cfg_type: PhantomCfgType) -> Self { - Self { - bdf, - command: 0, - status: 0, - int_line: 0, - v_bars: v_bars, - bar_num: if cfg_type == PhantomCfgType::ENDPOINT { - NUM_BAR_REGS_TYPE0 - } else { - NUM_BAR_REGS_TYPE1 - }, - cfg_type: cfg_type, - } - } - - pub fn read_bar(&self, bar_id: usize) -> u32 { - if bar_id >= self.bar_num { - panic!("bar {} doesn't exists!", bar_id); - } - self.v_bars[bar_id].read() - } - pub fn write_bar(&mut self, bar_id: usize, val: u32) { - if bar_id >= self.bar_num { - panic!("bar {} doesn't exists!", bar_id); - } - self.v_bars[bar_id].write(val as _); - } - pub fn read_cmd(&self) -> u16 { - self.command - } - pub fn write_cmd(&mut self, command: u16) { - self.command = command; - } - pub fn read_stats(&self) -> u16 { - self.status - } - pub fn write_stats(&mut self, val: u16) { - self.status = val; - } - pub fn read_int_line(&self) -> u8 { - self.int_line - } - pub fn write_int_line(&mut self, val: u8) { - self.int_line = val; - } - - pub fn phantom_mmio_handler( - &mut self, - mmio: &mut MMIOAccess, - base: usize, - zone_id: usize, - ) -> HvResult { - match self.cfg_type { - PhantomCfgType::ENDPOINT => self.phantom_ep_handler(mmio, base, zone_id), - PhantomCfgType::BRIDGE => self.phantom_bridge_handler(mmio, base, zone_id), - } - } - - fn phantom_ep_handler( - &mut self, - mmio: &mut MMIOAccess, - base: usize, - zone_id: usize, - ) -> HvResult { - let reg_addr = extract_reg_addr(mmio.address); - match reg_addr { - 0 => { - // phantom device - let header_addr = base + mmio.address; - let bdf = self.bdf; - let function = bdf & 0x7; - let device = (bdf >> 3) & 0b11111; - let bus = bdf >> 8; - let header_val = unsafe { ptr::read_volatile(header_addr as *mut u32) }; - debug!( - "{:x}:{:x}.{:x} exists but we don't show it to vm {:x}:{:x}", - bus, - device, - function, - header_val & 0xffff, - (header_val >> 16) & 0xffff - ); - mmio.value = PHANTOM_DEV_HEADER as _; - } - CFG_CMD_OFF => { - if mmio.is_write { - self.write_cmd(mmio.value as _); - } else { - mmio.value = self.read_cmd() as _; - } - } - CFG_CAP_PTR_OFF => { - // can't see any capabilities - mmio.value = 0x0; - } - CFG_EXT_CAP_PTR_OFF => { - mmio.value = 0x0; - } - CFG_CLASS_CODE_OFF => { - mmio.value = 0x1f0000; - } - CFG_BAR0 => { - if zone_id == 0 { - mmio_perform_access(base, mmio); - } - if mmio.is_write { - self.write_bar(0, mmio.value as _); - } else { - mmio.value = self.read_bar(0) as _; - } - } - CFG_BAR1 => { - if zone_id == 0 { - mmio_perform_access(base, mmio); - } - if mmio.is_write { - self.write_bar(1, mmio.value as _); - } else { - mmio.value = self.read_bar(1) as _; - } - } - CFG_BAR2 => { - if zone_id == 0 { - mmio_perform_access(base, mmio); - } - if mmio.is_write { - self.write_bar(2, mmio.value as _); - } else { - mmio.value = self.read_bar(2) as _; - } - } - CFG_BAR3 => { - if zone_id == 0 { - mmio_perform_access(base, mmio); - } - if mmio.is_write { - self.write_bar(3, mmio.value as _); - } else { - mmio.value = self.read_bar(3) as _; - } - } - CFG_BAR4 => { - if zone_id == 0 { - mmio_perform_access(base, mmio); - } - if mmio.is_write { - self.write_bar(4, mmio.value as _); - } else { - mmio.value = self.read_bar(4) as _; - } - } - CFG_BAR5 => { - if zone_id == 0 { - mmio_perform_access(base, mmio); - } - if mmio.is_write { - self.write_bar(5, mmio.value as _); - } else { - mmio.value = self.read_bar(5) as _; - } - } - CFG_INT_PIN => { - mmio.value = 0; - } - CFG_INT_LINE => { - if mmio.is_write { - self.write_int_line(mmio.value as _); - } else { - mmio.value = self.read_int_line() as _; - } - } - _ => { - mmio_perform_access(base, mmio); - // if self.bdf >> 8 == 8 { - // info!( - // "{:x}:{:x}.{:x} access {:#x} {:?} {} -> {:#x}", - // self.bdf >> 8, - // (self.bdf >> 3) & 0b11111, - // self.bdf & 0b111, - // reg_addr, - // if mmio.is_write {"W"} else {"R"}, - // mmio.size, - // mmio.value - // ); - // } - } - } - Ok(()) - } - - fn phantom_bridge_handler( - &mut self, - mmio: &mut MMIOAccess, - base: usize, - _zone_id: usize, - ) -> HvResult { - let reg_addr = extract_reg_addr(mmio.address); - match reg_addr { - 0 => { - // phantom device - let header_addr = base + mmio.address; - let header_val = unsafe { ptr::read_volatile(header_addr as *mut u32) }; - let bdf = self.bdf; - let function = bdf & 0x7; - let device = (bdf >> 3) & 0b11111; - let bus = bdf >> 8; - debug!( - "{:x}:{:x}.{:x} exists but we don't show it to vm {:x}:{:x}", - bdf >> 8, - device, - function, - header_val & 0xffff, - (header_val >> 16) & 0xffff - ); - mmio.value = PHANTOM_DEV_HEADER as _; - } - CFG_CMD_OFF => { - if mmio.is_write { - self.write_cmd(mmio.value as _); - } else { - mmio.value = self.read_cmd() as _; - } - } - CFG_CAP_PTR_OFF => { - // can't see any capabilities - mmio.value = 0x0; - } - CFG_EXT_CAP_PTR_OFF => { - mmio.value = 0x0; - } - CFG_BAR0 => { - if mmio.is_write { - self.write_bar(0, mmio.value as _); - } else { - mmio.value = self.read_bar(0) as _; - } - } - CFG_BAR1 => { - if mmio.is_write { - self.write_bar(1, mmio.value as _); - } else { - mmio.value = self.read_bar(1) as _; - } - } - CFG_INT_PIN => { - mmio.value = 0; - } - CFG_INT_LINE => { - if mmio.is_write { - self.write_int_line(mmio.value as _); - } else { - mmio.value = self.read_int_line() as _; - } - } - _ => { - // if !mmio.is_write { - mmio_perform_access(base, mmio); - // } - } - } - Ok(()) - } -} - -pub static mut PHANTOM_DEVS: BTreeMap = BTreeMap::new(); - -pub fn add_phantom_devices(phantom_dev: PhantomCfg) { - unsafe { - let bdf = phantom_dev.bdf; - if !PHANTOM_DEVS.contains_key(&bdf) { - info!( - "Add a new virt pci device: {:x}:{:x}.{:x}", - &phantom_dev.bdf >> 8, - (&phantom_dev.bdf >> 3) & 0b11111, - &phantom_dev.bdf & 0b111 - ); - PHANTOM_DEVS.insert(bdf, phantom_dev); - } else { - warn!( - "Phantom device with BDF {:#x} already exists, skipping", - bdf - ); - } - } -} - -pub fn find_phantom_dev(bdf: usize) -> PhantomCfg { - unsafe { - match PHANTOM_DEVS.get(&bdf) { - Some(device) => device.clone(), - None => generate_vep_by_bdf(bdf), // root will generate all virt bridges so we don't need to actively generate vbridges - } - } -} - -pub fn generate_vep_by_bdf(bdf: usize) -> PhantomCfg { - let mut tmp_ep = EndpointConfig::new(bdf); - let cfg_base = cfg_base(bdf); - let offsets: [usize; NUM_BAR_REGS_TYPE0] = [0x10, 0x14, 0x18, 0x1c, 0x20, 0x24]; - for bar_id in 0..NUM_BAR_REGS_TYPE0 { - unsafe { - let reg_ptr = (cfg_base + offsets[bar_id]) as *mut u32; - let origin_val = *reg_ptr; - *reg_ptr = 0xffffffffu32; - let new_val = *reg_ptr; - tmp_ep.bars_init(bar_id, origin_val, new_val); - *reg_ptr = origin_val; - } - } - let pdev = tmp_ep.generate_vep(); - add_phantom_devices(pdev); - // info!("generate a pdev: {:#x?}", &pdev); - pdev -} diff --git a/src/pci/vpci_dev/mod.rs b/src/pci/vpci_dev/mod.rs new file mode 100644 index 00000000..091a402f --- /dev/null +++ b/src/pci/vpci_dev/mod.rs @@ -0,0 +1,400 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// + +use crate::error::HvResult; +use crate::pci::pci_access::{Bar, EndpointField}; +use crate::pci::pci_struct::{ + ArcRwLockVirtualPciConfigSpace, Bdf, CapabilityType, ConfigValue, PciCapabilityRegion, + VirtualPciConfigSpace, +}; +use crate::pci::PciConfigAddress; + +use bitvec::array::BitArray; +use bitvec::{order::Lsb0, BitArr}; + +macro_rules! pci_virt_log { + ($($arg:tt)*) => { + // info!($($arg)*); + // To switch to debug level, change the line above to: + debug!($($arg)*); + }; +} + +// Standard virtual device configuration space defaults +pub(crate) const STANDARD_CFG_SIZE: usize = 0x80; +pub(crate) const DEFAULT_CSPACE_U32: [u32; STANDARD_CFG_SIZE / 4] = { + const STANDARD_VENDOR_ID: u16 = 0x110a; + const STANDARD_DEVICE_ID: u16 = 0x4106; + const PCI_STS_CAPS: u16 = 0x10; + const PCI_DEV_CLASS_OTHER: u8 = 0xff; + const PCI_CFG_CAPS: usize = 0x34; + const PCI_CAP_ID_VNDR: u8 = 0x09; + const PCI_CAP_ID_MSIX: u8 = 0x11; + const STANDARD_CFG_VNDR_CAP: u8 = 0x40; + const STANDARD_CFG_VNDR_LEN: u8 = 0x20; + const STANDARD_CFG_MSIX_CAP: usize = 0x98; + const STANDARD_MSIX_VECTORS: u16 = 16; + + let mut arr = [0u32; STANDARD_CFG_SIZE / 4]; + arr[0x00 / 4] = (STANDARD_DEVICE_ID as u32) << 16 | STANDARD_VENDOR_ID as u32; + arr[0x04 / 4] = (PCI_STS_CAPS as u32) << 16; + arr[0x08 / 4] = (PCI_DEV_CLASS_OTHER as u32) << 24; + arr[0x2c / 4] = (STANDARD_DEVICE_ID as u32) << 16 | STANDARD_VENDOR_ID as u32; + // arr[PCI_CFG_CAPS / 4] = STANDARD_CFG_VNDR_CAP as u32; + // arr[STANDARD_CFG_VNDR_CAP as usize / 4] = (STANDARD_CFG_VNDR_LEN as u32) << 16 + // | (STANDARD_CFG_MSIX_CAP as u32) << 8 + // | PCI_CAP_ID_VNDR as u32; + // arr[STANDARD_CFG_MSIX_CAP / 4] = (0x00u32) << 8 | PCI_CAP_ID_MSIX as u32; + // arr[(STANDARD_CFG_MSIX_CAP + 0x4) / 4] = 1; + // arr[(STANDARD_CFG_MSIX_CAP + 0x8) / 4] = ((0x10 * STANDARD_MSIX_VECTORS) as u32) | 1; + arr +}; + +pub mod standard; + +/* + * PciConfigAccessStatus is used to return the result of the config space access + * Done(usize): the value is returned in usize + * Default: use default config space value + * Reject: the access is rejected + */ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum PciConfigAccessStatus { + Done(usize), + Default, + Reject, +} + +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C, align(4))] +pub enum VpciDevType { + #[default] + Physical = 0, + StandardVdev = 1, + // Add new device types here +} + +pub trait VpciDeviceHandler: Sync + Send { + fn read_cfg( + &self, + dev: ArcRwLockVirtualPciConfigSpace, + offset: PciConfigAddress, + size: usize, + ) -> HvResult; + fn write_cfg( + &self, + dev: ArcRwLockVirtualPciConfigSpace, + offset: PciConfigAddress, + size: usize, + value: usize, + ) -> HvResult; + fn vdev_init(&self, dev: VirtualPciConfigSpace) -> VirtualPciConfigSpace; +} + +/* + * Static handler instances for each device type (except Physical). + * To add a new device type: + * 1. Add the variant to VpciDevType enum above + * 2. Add the handler registration here: (&module::HANDLER, VpciDevType::YourType) + */ +static HANDLERS: &[(&dyn VpciDeviceHandler, VpciDevType)] = + &[(&standard::HANDLER, VpciDevType::StandardVdev)]; + +pub(crate) fn get_handler(dev_type: VpciDevType) -> Option<&'static dyn VpciDeviceHandler> { + HANDLERS + .iter() + .find(|(_, ty)| *ty == dev_type) + .map(|(handler, _)| *handler) +} + +pub(super) fn vpci_dev_read_cfg( + dev_type: VpciDevType, + node: ArcRwLockVirtualPciConfigSpace, + offset: PciConfigAddress, + size: usize, +) -> HvResult { + match dev_type { + VpciDevType::Physical => { + warn!("vpci_dev_read_cfg: physical device is not supported"); + Ok(0xFFFF_FFFF) + } + _ => { + if let Some(handler) = get_handler(dev_type) { + match handler.read_cfg(node.clone(), offset, size) { + Ok(status) => { + match status { + PciConfigAccessStatus::Done(value) => Ok(value), + PciConfigAccessStatus::Default => { + // If this is a standard virtual device, read from DEFAULT_CSPACE_U32 + pci_virt_log!("vpci_dev_read_cfg: default config space read, offset {:#x}, size {:#x}", offset, size); + if offset < STANDARD_CFG_SIZE as PciConfigAddress { + let u32_offset = (offset as usize) / 4; + let u32_value = DEFAULT_CSPACE_U32[u32_offset]; + + // Extract the appropriate bytes based on size and offset within the u32 + let byte_offset_in_u32 = (offset as usize) % 4; + let value = match size { + 1 => { + ((u32_value >> (byte_offset_in_u32 * 8)) & 0xFF) + as usize + } + 2 => { + ((u32_value >> (byte_offset_in_u32 * 8)) & 0xFFFF) + as usize + } + 4 => u32_value as usize, + _ => { + warn!("vpci_dev_read_cfg: invalid size {size}, try read from emu"); + let field = EndpointField::from(offset as usize, size); + return node.write().read_emu(field); + } + }; + return Ok(value); + } + // For other device types or out of range, use read_emu + let field = EndpointField::from(offset as usize, size); + let r = node.write().read_emu(field)?; + Ok(r) + } + PciConfigAccessStatus::Reject => { + // warn!("vpci_dev_read_cfg: operation rejected"); + Ok(0xFFFF_FFFF) + } + } + } + Err(e) => { + warn!("vpci_dev_read_cfg error: {:?}", e); + Ok(0xFFFF_FFFF) + } + } + } else { + warn!("vpci_dev_read_cfg: unknown device type"); + Ok(0xFFFF_FFFF) + } + } + } +} + +pub(super) fn vpci_dev_write_cfg( + dev_type: VpciDevType, + node: ArcRwLockVirtualPciConfigSpace, + offset: PciConfigAddress, + size: usize, + value: usize, +) -> HvResult { + match dev_type { + VpciDevType::Physical => { + warn!("vpci_dev_write_cfg: physical device is not supported"); + Ok(()) + } + _ => { + if let Some(handler) = get_handler(dev_type) { + match handler.write_cfg(node.clone(), offset, size, value) { + Ok(status) => match status { + PciConfigAccessStatus::Done(_) => Ok(()), + PciConfigAccessStatus::Default => { + warn!("vpci_dev_write_cfg: Default"); + Ok(()) + } + PciConfigAccessStatus::Reject => { + warn!("vpci_dev_write_cfg: operation rejected"); + Ok(()) + } + }, + Err(e) => { + warn!("vpci_dev_write_cfg error: {:?}", e); + Err(e) + } + } + } else { + warn!("vpci_dev_write_cfg: unknown device type"); + Ok(()) + } + } + } +} + +pub(super) fn virt_dev_init( + bdf: Bdf, + base: PciConfigAddress, + dev_type: VpciDevType, +) -> VirtualPciConfigSpace { + // Create initial VirtualPciConfigSpace with default values + let initial_dev = VirtualPciConfigSpace::virt_dev_init_default( + bdf, + base, + dev_type, + ConfigValue::default(), + Bar::default(), + ); + + match dev_type { + VpciDevType::Physical => { + // Physical devices use default values + warn!("virt_dev_init: physical device is not supported"); + initial_dev + } + _ => { + if let Some(handler) = get_handler(dev_type) { + // Let handler modify and return the device + handler.vdev_init(initial_dev) + } else { + warn!("virt_dev_init: unknown device type"); + initial_dev + } + } + } +} + +pub struct VirtMsiCap { + offset: PciConfigAddress, + next_cap_pointer: u16, + control_bits: BitArr!(for 16, in u16, Lsb0), + message_address_lower: u32, + message_address_upper: u32, + message_data: u16, +} + +impl PciCapabilityRegion for VirtMsiCap { + fn read(&self, offset: PciConfigAddress, size: usize) -> HvResult { + match (offset, size) { + (0x0, 2) => { + Ok((CapabilityType::Msi.to_id() as u32) | (self.next_cap_pointer << 8) as u32) + } + (0x2, 2) => Ok(self.control_bits.as_raw_slice()[0] as u32), + (0x4, 4) => Ok(self.message_address_lower), + (0x8, 4) => Ok(self.message_address_upper), + (0xC, 2) => Ok(self.message_data as u32), + _ => { + warn!( + "VirtMsiCap invalid read offset 0x{:x} size {}", + offset, size + ); + Ok(0) + } + } + } + fn write(&mut self, offset: PciConfigAddress, size: usize, value: u32) -> HvResult { + match (offset, size) { + (0x2, 2) => self.control_bits = BitArray::new([value as u16]), + (0x4, 4) => self.message_address_lower = value, + (0x8, 4) => self.message_address_upper = value, + (0xC, 2) => self.message_data = value as u16, + _ => { + warn!( + "VirtMsiCap invalid write offset 0x{:x} size {}", + offset, size + ); + } + } + Ok(()) + } + fn get_offset(&self) -> PciConfigAddress { + self.offset + } + fn get_size(&self) -> usize { + 128 + } +} + +impl VirtMsiCap { + pub fn new(offset: PciConfigAddress) -> Self { + let mut bits = BitArray::ZERO; + bits.set(0, true); + Self { + offset, + next_cap_pointer: 0, + control_bits: bits, + message_address_lower: 0, + message_address_upper: 0, + message_data: 0, + } + } + + pub fn set_next_cap_pointer(&mut self, next_cap_pointer: u16) { + self.next_cap_pointer = next_cap_pointer; + } +} + +pub struct VirtMsiXCap { + offset: PciConfigAddress, + next_cap_pointer: u16, + control_bits: BitArr!(for 16, in u16, Lsb0), + table: u32, + pba: u32, +} + +impl PciCapabilityRegion for VirtMsiXCap { + fn read(&self, offset: PciConfigAddress, size: usize) -> HvResult { + match (offset, size) { + (0x0, 4) => Ok(((CapabilityType::MsiX.to_id() as u32) + | (self.next_cap_pointer << 8) as u32) + | (((self.control_bits.as_raw_slice()[0] as u32) << 16) as u32)), + (0x0, 2) => { + Ok((CapabilityType::MsiX.to_id() as u32) | (self.next_cap_pointer << 8) as u32) + } + (0x2, 2) => Ok(self.control_bits.as_raw_slice()[0] as u32), + (0x4, 4) => Ok(self.table), + (0x8, 4) => Ok(self.pba), + _ => { + warn!( + "VirtMsiXCap invalid read offset 0x{:x} size {}", + offset, size + ); + Ok(0) + } + } + } + fn write(&mut self, offset: PciConfigAddress, size: usize, value: u32) -> HvResult { + match (offset, size) { + (0x2, 2) => self.control_bits = BitArray::new([value as u16]), + (0x4, 4) => self.table = value, + (0x8, 4) => self.pba = value, + _ => { + warn!( + "VirtMsiXCap invalid write offset 0x{:x} size {}", + offset, size + ); + } + } + + Ok(()) + } + + fn get_offset(&self) -> PciConfigAddress { + self.offset + } + fn get_size(&self) -> usize { + 96 + } +} + +impl VirtMsiXCap { + pub fn new(offset: PciConfigAddress) -> Self { + let bits = BitArray::ZERO; + Self { + offset, + next_cap_pointer: 0, + control_bits: bits, + table: 1, + pba: ((0x10 * 16) as u32) | 1, + } + } + + pub fn set_next_cap_pointer(&mut self, next_cap_pointer: u16) { + self.next_cap_pointer = next_cap_pointer; + } +} diff --git a/src/pci/vpci_dev/standard.rs b/src/pci/vpci_dev/standard.rs new file mode 100644 index 00000000..7446abc9 --- /dev/null +++ b/src/pci/vpci_dev/standard.rs @@ -0,0 +1,176 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// + +use super::{PciConfigAccessStatus, VpciDeviceHandler}; +use crate::error::HvResult; +use crate::pci::pci_access::{ + BaseClass, DeviceId, DeviceRevision, EndpointField, Interface, SubClass, VendorId, +}; +use crate::pci::pci_struct::{CapabilityType, PciCapability, VirtualPciConfigSpace}; +use crate::pci::PciConfigAddress; +// use crate::memory::frame::Frame; +use crate::memory::MMIOAccess; +use crate::pci::pci_access::PciMemType; +use crate::pci::pci_struct::ArcRwLockVirtualPciConfigSpace; +use crate::pci::pci_struct::PciCapabilityRegion; +use crate::pci::vpci_dev::VirtMsiXCap; +use crate::percpu::this_zone; +use alloc::sync::Arc; +use spin::RwLock; + +/// Handler for standard virtual PCI devices +pub struct StandardHandler; + +impl VpciDeviceHandler for StandardHandler { + fn read_cfg( + &self, + dev: ArcRwLockVirtualPciConfigSpace, + offset: PciConfigAddress, + size: usize, + ) -> HvResult { + pci_virt_log!( + "virt pci standard read_cfg, offset {:#x}, size {:#x}", + offset, + size + ); + match EndpointField::from(offset as usize, size) { + EndpointField::ID => { + let id = dev.with_config_value(|config_value| config_value.get_id()); + Ok(PciConfigAccessStatus::Done( + (((id.0 as u32) << 16) | (id.1 as u32)) as usize, + )) + } + EndpointField::Bar(0) => { + let slot = 0; + let size_read = dev.with_bar_ref(slot, |bar| bar.get_size_read()); + if size_read { + let value = dev.with_bar_ref(slot, |bar| bar.get_size_with_flag()); + dev.with_bar_ref_mut(slot, |bar| bar.clear_size_read()); + Ok(PciConfigAccessStatus::Done(value as usize)) + } else { + let value = dev.with_bar_ref(slot, |bar| bar.get_virtual_value()); + Ok(PciConfigAccessStatus::Done(value as usize)) + } + } + EndpointField::CapabilityPointer => Ok(PciConfigAccessStatus::Done(0x98)), + _ => Ok(PciConfigAccessStatus::Default), + } + } + + fn write_cfg( + &self, + dev: ArcRwLockVirtualPciConfigSpace, + offset: PciConfigAddress, + size: usize, + value: usize, + ) -> HvResult { + pci_virt_log!( + "virt pci standard write_cfg, offset {:#x}, size {:#x}, value {:#x}", + offset, + size, + value + ); + match EndpointField::from(offset as usize, size) { + EndpointField::Command => { + // Command field is written directly to backend, no need for space cache + Ok(PciConfigAccessStatus::Done(value)) + } + EndpointField::Bar(0) => { + let slot = 0; + let bar_size = dev.with_bar_ref(slot, |bar| bar.get_size()); + + if value == 0xFFFF_FFFF { + dev.with_bar_ref_mut(slot, |bar| bar.set_size_read()); + } else { + let zone = this_zone(); + let mut guard = zone.write(); + pci_virt_log!( + "virtual pci standard write_cfg, register mmio region {:#x}, size {:#x}", + value, + bar_size + ); + guard.mmio_region_register( + value as usize, + bar_size as usize, + mmio_vdev_standard_handler, + value, + ); + } + + Ok(PciConfigAccessStatus::Done(value)) + } + EndpointField::Bar(_) => Ok(PciConfigAccessStatus::Done(value)), + _ => { + warn!( + "virt pci standard write_cfg, invalid offset {:#x}, size {:#x}, value {:#x}", + offset, size, value + ); + Ok(PciConfigAccessStatus::Reject) + } + } + } + + fn vdev_init(&self, mut dev: VirtualPciConfigSpace) -> VirtualPciConfigSpace { + // Set config_value + let id: (DeviceId, VendorId) = (0x110a, 0x4106); + let revision: DeviceRevision = 0xFFu8; + let base_class: BaseClass = 0x0; + let sub_class: SubClass = 0x0; + let interface: Interface = 0x0; + dev.with_config_value_mut(|config_value| { + config_value.set_id(id); + config_value.set_class_and_revision_id((base_class, sub_class, interface, revision)); + }); + + // Set bararr + let your_addr = 0x0; + let size = 0x1000; + dev.with_bararr_mut(|bararr| { + bararr[0].config_init(PciMemType::Mem32, false, size as u64, your_addr); + }); + + // 0x98 is an arbitrary value, used here only for demonstration purposes + // please don't forget to set next cap pointer if next cap exists + let msi_cap_offset = 0x98; + let mut msi_cap = VirtMsiXCap::new(msi_cap_offset); + msi_cap.set_next_cap_pointer(0x00); + dev.with_access_mut(|access| { + access.set_bits( + (msi_cap_offset as usize)..(msi_cap_offset as usize + msi_cap.get_size()) as usize, + ); + }); + + dev.with_cap_mut(|capabilities| { + capabilities.insert( + msi_cap_offset, + PciCapability::new_virt(CapabilityType::MsiX, Arc::new(RwLock::new(msi_cap))), + ); + }); + + dev.with_access_mut(|access| { + access.set_bits(0x34..0x38); + }); + dev + } +} + +/// Static handler instance for standard virtual PCI devices +pub const HANDLER: StandardHandler = StandardHandler; + +pub fn mmio_vdev_standard_handler(mmio: &mut MMIOAccess, _base: usize) -> HvResult { + info!("mmio_vdev_standard_handler {:#x}", mmio.address); + Ok(()) +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index d5241d9b..d9dd18a6 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -15,9 +15,9 @@ // use crate::{ config::{ - HvConfigMemoryRegion, HvIvcConfig, HvPciConfig, HvZoneConfig, + HvConfigMemoryRegion, HvIvcConfig, HvPciConfig, HvPciDevConfig, HvZoneConfig, CONFIG_INTERRUPTS_BITMAP_BITS_PER_WORD, CONFIG_MAX_INTERRUPTS, CONFIG_MAX_IVC_CONFIGS, - CONFIG_MAX_MEMORY_REGIONS, CONFIG_MAX_PCI_DEV, CONFIG_NAME_MAXLEN, + CONFIG_MAX_MEMORY_REGIONS, CONFIG_MAX_PCI_DEV, CONFIG_NAME_MAXLEN, CONFIG_PCI_BUS_MAXNUM, }, consts::INVALID_ADDRESS, }; @@ -67,16 +67,23 @@ pub fn platform_root_zone_config() -> HvZoneConfig { check!(ROOT_ZONE_NAME.len(), CONFIG_NAME_MAXLEN, "ROOT_ZONE_NAME"); name[..ROOT_ZONE_NAME.len()].copy_from_slice(ROOT_ZONE_NAME.as_bytes()); - let mut pci_devs = [0; CONFIG_MAX_PCI_DEV]; - let mut _root_pci_cfg = HvPciConfig::new_empty(); + let mut pci_devs = [HvPciDevConfig::default(); CONFIG_MAX_PCI_DEV]; + let mut _root_pci_cfg = [HvPciConfig::new_empty(); CONFIG_PCI_BUS_MAXNUM]; let mut _num_pci_devs: u64 = 0; + let mut _num_pci_bus: u64 = 0; #[cfg(feature = "pci")] { check!(ROOT_PCI_DEVS.len(), CONFIG_MAX_PCI_DEV, "ROOT_PCI_DEVS"); pci_devs[..ROOT_PCI_DEVS.len()].copy_from_slice(&ROOT_PCI_DEVS); - _root_pci_cfg = ROOT_PCI_CONFIG; + check!( + ROOT_PCI_CONFIG.len(), + CONFIG_PCI_BUS_MAXNUM, + "ROOT_PCI_CONFIG" + ); + _root_pci_cfg[..ROOT_PCI_CONFIG.len()].copy_from_slice(&ROOT_PCI_CONFIG); _num_pci_devs = ROOT_PCI_DEVS.len() as _; + _num_pci_bus = ROOT_PCI_CONFIG.len() as _; } HvZoneConfig::new( @@ -94,6 +101,7 @@ pub fn platform_root_zone_config() -> HvZoneConfig { INVALID_ADDRESS as _, name, ROOT_ARCH_ZONE_CONFIG, + _num_pci_bus, _root_pci_cfg, _num_pci_devs, pci_devs, diff --git a/src/zone.rs b/src/zone.rs index 28c99a4a..9efb5232 100644 --- a/src/zone.rs +++ b/src/zone.rs @@ -17,9 +17,14 @@ use alloc::sync::Arc; use alloc::vec::Vec; // use psci::error::INVALID_ADDRESS; use crate::consts::{INVALID_ADDRESS, MAX_CPU_NUM}; -use crate::pci::pci::PciRoot; +use crate::pci::pci_struct::VirtualRootComplex; use spin::RwLock; +#[cfg(feature = "dwc_pcie")] +use crate::pci::{config_accessors::dwc_atu::AtuConfig, PciConfigAddress}; +#[cfg(feature = "dwc_pcie")] +use alloc::collections::btree_map::BTreeMap; + use crate::arch::mm::new_s2_memory_set; use crate::arch::s2pt::Stage2PageTable; use crate::config::{HvZoneConfig, CONFIG_NAME_MAXLEN}; @@ -30,6 +35,78 @@ use crate::memory::{MMIOConfig, MMIOHandler, MMIORegion, MemorySet}; use crate::percpu::{get_cpu_data, this_zone, CpuSet}; use core::panic; +#[cfg(feature = "dwc_pcie")] +#[derive(Debug)] +pub struct VirtualAtuConfigs { + ecam_to_atu: BTreeMap, + io_base_to_ecam: BTreeMap, + cfg_base_to_ecam: BTreeMap, +} + +#[cfg(feature = "dwc_pcie")] +impl VirtualAtuConfigs { + pub fn new() -> Self { + Self { + ecam_to_atu: BTreeMap::new(), + io_base_to_ecam: BTreeMap::new(), + cfg_base_to_ecam: BTreeMap::new(), + } + } + + pub fn get_atu_by_ecam(&self, ecam_base: usize) -> Option<&AtuConfig> { + self.ecam_to_atu.get(&ecam_base) + } + + pub fn get_atu_by_ecam_mut(&mut self, ecam_base: usize) -> Option<&mut AtuConfig> { + self.ecam_to_atu.get_mut(&ecam_base) + } + + pub fn insert_atu(&mut self, ecam_base: usize, atu: AtuConfig) -> Option { + self.ecam_to_atu.insert(ecam_base, atu) + } + + pub fn get_or_insert_atu(&mut self, ecam_base: usize, f: F) -> &mut AtuConfig + where + F: FnOnce() -> AtuConfig, + { + self.ecam_to_atu.entry(ecam_base).or_insert_with(f) + } + + pub fn get_atu_by_io_base(&self, io_base: PciConfigAddress) -> Option<&AtuConfig> { + let ecam = self.io_base_to_ecam.get(&io_base); + if let Some(ecam) = ecam { + self.get_atu_by_ecam(*ecam) + } else { + None + } + } + + pub fn get_ecam_by_io_base(&self, io_base: PciConfigAddress) -> Option { + self.io_base_to_ecam.get(&io_base).copied() + } + + pub fn insert_io_base_mapping(&mut self, io_base: PciConfigAddress, ecam_base: usize) { + self.io_base_to_ecam.insert(io_base, ecam_base); + } + + pub fn get_atu_by_cfg_base(&self, cfg_base: PciConfigAddress) -> Option<&AtuConfig> { + let ecam = self.cfg_base_to_ecam.get(&cfg_base); + if let Some(ecam) = ecam { + self.get_atu_by_ecam(*ecam) + } else { + None + } + } + + pub fn get_ecam_by_cfg_base(&self, cfg_base: PciConfigAddress) -> Option { + self.cfg_base_to_ecam.get(&cfg_base).copied() + } + + pub fn insert_cfg_base_mapping(&mut self, cfg_base: PciConfigAddress, ecam_base: usize) { + self.cfg_base_to_ecam.insert(cfg_base, ecam_base); + } +} + pub struct Zone { pub name: [u8; CONFIG_NAME_MAXLEN], pub id: usize, @@ -38,9 +115,11 @@ pub struct Zone { pub cpu_set: CpuSet, pub irq_bitmap: [u32; 1024 / 32], pub gpm: MemorySet, - pub pciroot: PciRoot, pub iommu_pt: Option>, pub is_err: bool, + pub vpci_bus: VirtualRootComplex, + #[cfg(feature = "dwc_pcie")] + pub atu_configs: VirtualAtuConfigs, } impl Zone { @@ -53,13 +132,15 @@ impl Zone { cpu_set: CpuSet::new(MAX_CPU_NUM as usize, 0), mmio: Vec::new(), irq_bitmap: [0; 1024 / 32], - pciroot: PciRoot::new(), iommu_pt: if cfg!(feature = "iommu") { Some(new_s2_memory_set()) } else { None }, is_err: false, + vpci_bus: VirtualRootComplex::new(), + #[cfg(feature = "dwc_pcie")] + atu_configs: VirtualAtuConfigs::new(), } } @@ -210,6 +291,29 @@ pub fn zone_create(config: &HvZoneConfig) -> HvResult>> { zone.pt_init(config.memory_regions()).unwrap(); zone.mmio_init(&config.arch_config); + #[cfg(feature = "pci")] + { + let _ = zone.virtual_pci_mmio_init(&config.pci_config, config.num_pci_bus as usize); + let _ = zone.guest_pci_init( + zone_id, + &config.alloc_pci_devs, + config.num_pci_devs, + &config.pci_config, + config.num_pci_bus as usize, + ); + } + + // #[cfg(target_arch = "aarch64")] + // zone.ivc_init(config.ivc_config()); + + /* loongarch page table emergency */ + /* Kai: Maybe unnecessary but i can't boot vms on my 3A6000 PC without this function. */ + // #[cfg(target_arch = "loongarch64")] + // zone.page_table_emergency( + // config.pci_config[0].ecam_base as _, + // config.pci_config[0].ecam_size as _, + // )?; + let mut cpu_num = 0; for cpu_id in config.cpus().iter() { if let Some(zone) = get_cpu_data(*cpu_id as _).zone.clone() { @@ -245,11 +349,11 @@ pub fn zone_create(config: &HvZoneConfig) -> HvResult>> { // config.pci_config.ecam_size as _, // )?; - zone.pci_init( + /*zone.pci_init( &config.pci_config, config.num_pci_devs as _, &config.alloc_pci_devs, - ); + );*/ zone.arch_zone_post_configuration(config)?;