diff --git a/Cargo.toml b/Cargo.toml index 55a4d9a7..20fc9ef3 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 98cae2b2..772b5085 100644 --- a/platform/aarch64/qemu-gicv2/board.rs +++ b/platform/aarch64/qemu-gicv2/board.rs @@ -16,8 +16,11 @@ use crate::{ arch::{mmu::MemoryType, zone::{HvArchZoneConfig,GicConfig,Gicv2Config}}, config::*, + pci::vpci_dev::VpciDevType, }; +use crate::pci_dev; + pub const BOARD_NAME: &str = "qemu-gicv2"; pub const BOARD_NCPUS: usize = 4; @@ -90,20 +93,26 @@ 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, + } +]; 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, VpciDevType::Physical), + pci_dev!(0x0, 0x1, 0x0, VpciDevType::Physical), + pci_dev!(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 124b9dca..c24cf0fc 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; @@ -88,7 +92,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, @@ -100,8 +104,15 @@ 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, +}]; 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, VpciDevType::Physical), + pci_dev!(0x0, 0x1, 0x0, VpciDevType::Physical), + pci_dev!(0x0, 0x2, 0x0, VpciDevType::Physical), + pci_dev!(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..9bef1db5 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", @@ -52,7 +45,16 @@ "mem64_base": "0x8000000000", "mem64_size": "0x8000000000", "pci_mem64_base": "0x8000000000" - }, + }], "num_pci_devs": 2, - "alloc_pci_devs": [0, 16] + "alloc_pci_devs": [ + { + "bdf": "0x0", + "dev_type": "0" + }, + { + "bdf": "0x18", + "dev_type": "0" + } + ] } \ No newline at end of file diff --git a/platform/aarch64/rk3568/board.rs b/platform/aarch64/rk3568/board.rs index 84f07a71..a1682d3c 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, + // physical_start: 0x3c0400000, + // virtual_start: 0x3c0400000, + // size: 0x400000, + // }, //pcie + // HvConfigMemoryRegion { + // mem_type: MEM_TYPE_IO, + // physical_start: 0xfe270000, + // virtual_start: 0xfe270000, // size: 0x10000, - // }, // gic + // }, //pcie + // HvConfigMemoryRegion { + // mem_type: MEM_TYPE_IO, + // physical_start: 0xf2000000, + // virtual_start: 0xf2000000, + // size: 0x100000, + // }, //pcie // HvConfigMemoryRegion { // mem_type: MEM_TYPE_IO, - // physical_start: 0xfd460000, - // virtual_start: 0xfd460000, - // size: 0xc0000, - // }, // gic + // 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: 0xfe000000, - virtual_start: 0xfe000000, - size: 0x4000, - }, // dwmmc + 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, @@ -197,11 +279,83 @@ 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, + // }, + 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, + }, + // 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, + // } +]; 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, + cfg0_atu_index: 0, + cfg0_atu_type: 4, + cfg1_atu_index: 0, + cfg1_atu_type: 4, + mem32_atu_index: 1, + mem32_atu_type: 0, + mem64_atu_index: 2, + mem64_atu_type: 0, + io_atu_index: 3, + io_atu_type: 2, + }, +]; + +pub const ROOT_PCI_DEVS: [HvPciDevConfig; 2] = [ + pci_dev!(0x10, 0x0, 0x0, VpciDevType::Physical), + pci_dev!(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 947cb35c..b561212a 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 b12c6ec1..eb44da23 100644 --- a/platform/aarch64/rk3588/board.rs +++ b/platform/aarch64/rk3588/board.rs @@ -188,6 +188,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, diff --git a/platform/loongarch64/ls3a5000/board.rs b/platform/loongarch64/ls3a5000/board.rs index 184dc110..f91d83eb 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"; @@ -154,19 +155,21 @@ pub const ROOT_ZONE_IRQS: [u32; 0] = []; 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: 0x20000000, + pci_mem64_base: 0x60000000, + } +]; /* 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 +180,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; 26] = [ + pci_dev!(0x0, 0x0, 0x0, VpciDevType::Physical), // 00:00.0 + pci_dev!(0x0, 0x0, 0x1, VpciDevType::Physical), // 00:00.1 + pci_dev!(0x0, 0x0, 0x2, VpciDevType::Physical), // 00:00.2 + pci_dev!(0x0, 0x0, 0x3, VpciDevType::Physical), // 00:00.3 + pci_dev!(0x0, 0x4, 0x0, VpciDevType::Physical), // 00:04.0 + pci_dev!(0x0, 0x4, 0x1, VpciDevType::Physical), // 00:04.1 + pci_dev!(0x0, 0x5, 0x0, VpciDevType::Physical), // 00:05.0 + pci_dev!(0x0, 0x5, 0x1, VpciDevType::Physical), // 00:05.1 + pci_dev!(0x0, 0x6, 0x0, VpciDevType::Physical), // 00:06.0 + pci_dev!(0x0, 0x6, 0x1, VpciDevType::Physical), // 00:06.1 + pci_dev!(0x0, 0x6, 0x2, VpciDevType::Physical), // 00:06.2 + pci_dev!(0x0, 0x7, 0x0, VpciDevType::Physical), // 00:07.0 + pci_dev!(0x0, 0x8, 0x0, VpciDevType::Physical), // 00:08.0 + pci_dev!(0x0, 0x9, 0x0, VpciDevType::Physical), // 00:09.0 + pci_dev!(0x0, 0xa, 0x0, VpciDevType::Physical), // 00:0a.0 + pci_dev!(0x0, 0xb, 0x0, VpciDevType::Physical), // 00:0b.0 + pci_dev!(0x0, 0xc, 0x0, VpciDevType::Physical), // 00:0c.0 + pci_dev!(0x0, 0xd, 0x0, VpciDevType::Physical), // 00:0d.0 + pci_dev!(0x0, 0xf, 0x0, VpciDevType::Physical), // 00:0f.0 + pci_dev!(0x0, 0x10, 0x0, VpciDevType::Physical), // 00:10.0 + pci_dev!(0x0, 0x13, 0x0, VpciDevType::Physical), // 00:13.0 + pci_dev!(0x0, 0x16, 0x0, VpciDevType::Physical), // 00:16.0 + pci_dev!(0x0, 0x19, 0x0, VpciDevType::Physical), // 00:19.0 + pci_dev!(0x2, 0x0, 0x0, VpciDevType::Physical), // 02:00.0 + pci_dev!(0x5, 0x0, 0x0, VpciDevType::Physical), // 05:00.0 + pci_dev!(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..0b5fb081 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,10 @@ "mem64_base": "0x60000000", "mem64_size": "0x20000000", "pci_mem64_base": "0x60000000" - }, + }], "num_pci_devs": 1, - "alloc_pci_devs": [1537] + "alloc_pci_devs": [{ + "bdf": "0x601", + "vbdf": "0x601" + }] } \ 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..be533e7d 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,10 @@ "mem64_base": "0x60000000", "mem64_size": "0x20000000", "pci_mem64_base": "0x60000000" - }, + }], "num_pci_devs": 1, - "alloc_pci_devs": [1538] + "alloc_pci_devs": [{ + "bdf": "0x602", + "vbdf": "0x602" + }] } \ 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..9d5d235f 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,10 @@ "mem64_base": "0x60000000", "mem64_size": "0x20000000", "pci_mem64_base": "0x60000000" - }, + }], "num_pci_devs": 1, - "alloc_pci_devs": [1539] + "alloc_pci_devs": [{ + "bdf": "0x603", + "vbdf": "0x603" + }] } \ No newline at end of file diff --git a/platform/loongarch64/ls3a6000/board.rs b/platform/loongarch64/ls3a6000/board.rs index 184dc110..8c51e758 100644 --- a/platform/loongarch64/ls3a6000/board.rs +++ b/platform/loongarch64/ls3a6000/board.rs @@ -14,7 +14,9 @@ // Authors: // Yulong Han // -use crate::{arch::zone::HvArchZoneConfig, config::*}; +use crate::{arch::zone::HvArchZoneConfig, config::*, pci::vpci_dev::VpciDevType}; +use crate::pci_dev; +use crate::pci::vpci_dev::VpciDevType; pub const BOARD_NAME: &str = "ls3a5000"; @@ -154,19 +156,23 @@ pub const ROOT_ZONE_IRQS: [u32; 0] = []; 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, + } +]; /* 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, VpciDevType::Physical), // 00:00.0 + pci_dev!(0x0, 0x0, 0x1, VpciDevType::Physical), // 00:00.1 + pci_dev!(0x0, 0x0, 0x2, VpciDevType::Physical), // 00:00.2 + pci_dev!(0x0, 0x0, 0x3, VpciDevType::Physical), // 00:00.3 + pci_dev!(0x0, 0x4, 0x0, VpciDevType::Physical), // 00:04.0 + pci_dev!(0x0, 0x4, 0x1, VpciDevType::Physical), // 00:04.1 + pci_dev!(0x0, 0x5, 0x0, VpciDevType::Physical), // 00:05.0 + pci_dev!(0x0, 0x5, 0x1, VpciDevType::Physical), // 00:05.1 + pci_dev!(0x0, 0x6, 0x0, VpciDevType::Physical), // 00:06.0 + pci_dev!(0x0, 0x6, 0x1, VpciDevType::Physical), // 00:06.1 + pci_dev!(0x0, 0x6, 0x2, VpciDevType::Physical), // 00:06.2 + pci_dev!(0x0, 0x7, 0x0, VpciDevType::Physical), // 00:07.0 + pci_dev!(0x0, 0x8, 0x0, VpciDevType::Physical), // 00:08.0 + pci_dev!(0x0, 0x9, 0x0, VpciDevType::Physical), // 00:09.0 + pci_dev!(0x0, 0xa, 0x0, VpciDevType::Physical), // 00:0a.0 + pci_dev!(0x0, 0xb, 0x0, VpciDevType::Physical), // 00:0b.0 + pci_dev!(0x0, 0xc, 0x0, VpciDevType::Physical), // 00:0c.0 + pci_dev!(0x0, 0xd, 0x0, VpciDevType::Physical), // 00:0d.0 + pci_dev!(0x0, 0xf, 0x0, VpciDevType::Physical), // 00:0f.0 + pci_dev!(0x0, 0x10, 0x0, VpciDevType::Physical), // 00:10.0 + pci_dev!(0x0, 0x13, 0x0, VpciDevType::Physical), // 00:13.0 + pci_dev!(0x0, 0x16, 0x0, VpciDevType::Physical), // 00:16.0 + pci_dev!(0x0, 0x19, 0x0, VpciDevType::Physical), // 00:19.0 + pci_dev!(0x2, 0x0, 0x0, VpciDevType::Physical), // 02:00.0 + pci_dev!(0x5, 0x0, 0x0, VpciDevType::Physical), // 05:00.0 + pci_dev!(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..0b5fb081 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,10 @@ "mem64_base": "0x60000000", "mem64_size": "0x20000000", "pci_mem64_base": "0x60000000" - }, + }], "num_pci_devs": 1, - "alloc_pci_devs": [1537] + "alloc_pci_devs": [{ + "bdf": "0x601", + "vbdf": "0x601" + }] } \ 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..be533e7d 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,10 @@ "mem64_base": "0x60000000", "mem64_size": "0x20000000", "pci_mem64_base": "0x60000000" - }, + }], "num_pci_devs": 1, - "alloc_pci_devs": [1538] + "alloc_pci_devs": [{ + "bdf": "0x602", + "vbdf": "0x602" + }] } \ 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..9d5d235f 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,10 @@ "mem64_base": "0x60000000", "mem64_size": "0x20000000", "pci_mem64_base": "0x60000000" - }, + }], "num_pci_devs": 1, - "alloc_pci_devs": [1539] + "alloc_pci_devs": [{ + "bdf": "0x603", + "vbdf": "0x603" + }] } \ 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 d5f001e9..4054dd49 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 342bac03..1150cfd4 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: [u32; 32] = [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,19 @@ pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { mem64_base: 0x0, mem64_size: 0x0, pci_mem64_base: 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, VpciDevType::Physical), // host bridge + pci_dev!(0x0, 0x1, 0x0, VpciDevType::Physical), // VGA controller + pci_dev!(0x0, 0x2, 0x0, VpciDevType::Physical), // Ethernet controller + pci_dev!(0x0, 0x3, 0x0, VpciDevType::Physical), // PCI bridge + pci_dev!(0x0, 0x1f, 0x0, VpciDevType::Physical), // ISA bridge + pci_dev!(0x0, 0x1f, 0x2, VpciDevType::Physical), // SATA controller + pci_dev!(0x0, 0x1f, 0x3, VpciDevType::Physical), // SMBus + pci_dev!(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/iommu.rs b/src/arch/aarch64/iommu.rs index 1b7110d5..acf06fd8 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 96ac8883..3bc460f9 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, }; @@ -685,34 +684,10 @@ impl Zone { } 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 f70e3665..e645eebc 100644 --- a/src/arch/riscv64/zone.rs +++ b/src/arch/riscv64/zone.rs @@ -18,8 +18,8 @@ use crate::{ config::*, device::virtio_trampoline::mmio_virtio_handler, error::HvResult, - memory::{GuestPhysAddr, HostPhysAddr, MemFlags, MemoryRegion, MemorySet}, - pci::pcibar::BarRegion, + memory::{addr::align_up, GuestPhysAddr, HostPhysAddr, MemFlags, MemoryRegion, MemorySet}, + percpu::get_cpu_data, zone::Zone, }; impl Zone { @@ -76,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 75e7c43d..f4ccba7b 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 7d72f45d..a2b253a6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,19 +14,21 @@ // 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 = 0x3; +pub const CONFIG_MAGIC_VERSION: usize = 0x4; pub const CONFIG_MAX_MEMORY_REGIONS: usize = 64; pub const CONFIG_MAX_INTERRUPTS: 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)] @@ -53,6 +55,8 @@ 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, } impl HvPciConfig { @@ -69,6 +73,8 @@ impl HvPciConfig { mem64_base: 0, mem64_size: 0, pci_mem64_base: 0, + bus_range_begin: 0, + bus_range_end: 0, } } } @@ -91,9 +97,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 { @@ -113,9 +120,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, @@ -133,6 +141,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, @@ -161,6 +170,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(); @@ -192,3 +206,84 @@ pub struct HvIvcConfig { pub interrupt_num: u32, pub max_peers: u32, } + +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct HvPciDevConfig { + pub bdf: u64, + pub dev_type: VpciDevType, +} + +#[macro_export] +macro_rules! pci_dev { + ($bus:expr, $dev:expr, $func:expr, $dev_type:expr) => { + HvPciDevConfig { + bdf: ($bus << 8) | ($dev << 3) | ($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::from_address(self.bdf); + 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, + pub cfg0_atu_index: usize, + pub cfg0_atu_type: u32, + pub cfg1_atu_index: usize, + pub cfg1_atu_type: u32, + pub mem32_atu_index: usize, + pub mem32_atu_type: u32, + pub mem64_atu_index: usize, + pub mem64_atu_type: u32, + pub io_atu_index: usize, + pub io_atu_type: u32, +} + +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, + cfg0_atu_index: u32::MAX as usize, + cfg0_atu_type: 4, // ATU_TYPE_CFG0 + cfg1_atu_index: u32::MAX as usize, + cfg1_atu_type: 5, // ATU_TYPE_CFG1 + mem32_atu_index: u32::MAX as usize, + mem32_atu_type: 0, // ATU_TYPE_MEM + mem64_atu_index: u32::MAX as usize, + mem64_atu_type: 0, // ATU_TYPE_MEM + io_atu_index: u32::MAX as usize, + io_atu_type: 2, // ATU_TYPE_IO + } + } +} + +impl Default for HvDwcAtuConfig { + fn default() -> Self { + Self::new_empty() + } +} diff --git a/src/main.rs b/src/main.rs index 28d02906..7e231c67 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,11 +134,20 @@ 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); } INIT_EARLY_OK.store(1, Ordering::Release); diff --git a/src/memory/mm.rs b/src/memory/mm.rs index 54a7c7a8..e1449599 100644 --- a/src/memory/mm.rs +++ b/src/memory/mm.rs @@ -144,6 +144,16 @@ where } } + pub fn try_delete(&mut self, start: PT::VA) -> HvResult { + if let Entry::Occupied(e) = self.regions.entry(start) { + self.pt.unmap(e.get())?; + e.remove(); + Ok(()) + } 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..3681bd91 --- /dev/null +++ b/src/pci/config_accessors/dwc.rs @@ -0,0 +1,172 @@ +// 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 bit_field::BitField; + +use super::{ + dwc_atu::{AtuConfig, AtuUnroll, ATU_UNUSED}, + PciConfigAccessor, PciConfigMmio, PciRegion, PciRegionMmio, +}; + +use crate::{ + config::HvDwcAtuConfig, + error::{HvErrorNum::*, 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: u32, + pub base: PciConfigAddress, + pub size: u64, +} + +#[derive(Debug)] +pub struct DwcConfigRegionBackend(PciRegionMmio); + +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: 0, + base: dbi_base, + size: dbi_size, + }; + let cfg0 = DwcConfigRegion { + atu_index: atu_config.cfg0_atu_index, + atu_type: atu_config.cfg0_atu_type, + base: cfg0_base, + size: cfg_size_half, + }; + let cfg1 = DwcConfigRegion { + atu_index: atu_config.cfg1_atu_index, + atu_type: atu_config.cfg1_atu_type, + base: cfg1_base, + size: cfg_size_half, + }; + + Self { + dbi_backend, + dbi, + cfg0, + cfg1, + root_bus, + } + } +} + +impl PciConfigAccessor for DwcConfigAccessor { + fn get_physical_address( + &self, + bdf: Bdf, + offset: PciConfigAddress, + parent_bus: u8, + ) -> HvResult { + let bus = bdf.bus(); + let device = bdf.device() as PciConfigAddress; + let function = bdf.function() as PciConfigAddress; + + warn!("parent_bus {} self.root_bus {}", parent_bus, self.root_bus); + + // Calculate address without bus field (bus is handled by different config regions) + // Address format: (device << 15) + (function << 12) + offset + let offset_without_bus = (device << 15) + (function << 12) + offset; + + let address = if bus == self.root_bus { + // Root bus: use DBI directly, no ATU configuration needed + self.dbi.base + offset_without_bus + } else if parent_bus == self.root_bus { + 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); + AtuUnroll::dw_pcie_prog_outbound_atu_unroll(self.dbi_backend.as_ref(), &atu_config)?; + + self.cfg0.base + offset_without_bus + } else { + //TODO: cfg1 not implemented yet because it's not used in the current board + 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); + AtuUnroll::dw_pcie_prog_outbound_atu_unroll(self.dbi_backend.as_ref(), &atu_config)?; + + self.cfg1.base + offset_without_bus + }; + + 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..b3951b25 --- /dev/null +++ b/src/pci/config_accessors/dwc_atu.rs @@ -0,0 +1,150 @@ +// 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 crate::{ + error::{HvErrorNum::*, 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; + +// ATU region type constants +pub const ATU_TYPE_CFG0: u32 = 0x4; // CFG0 Type +pub const ATU_TYPE_CFG1: u32 = 0x5; // CFG1 Type +pub const ATU_TYPE_MEM: u32 = 0x0; // Memory Type +pub const ATU_TYPE_IO: u32 = 0x2; // IO Type + +// 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)] +pub struct AtuConfig { + pub index: usize, + pub atu_type: u32, + pub cpu_base: PciConfigAddress, + pub cpu_limit: PciConfigAddress, + pub pci_target: PciConfigAddress, +} + +impl AtuConfig { + pub fn new( + index: usize, + atu_type: u32, + 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, + } + } + + pub fn new_with_dwc_config_region(config_region: &DwcConfigRegion) -> Self { + Self::new( + config_region.atu_index, + config_region.atu_type, + config_region.base, + config_region.size, + config_region.base, + ) + } +} + +// 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..76794958 --- /dev/null +++ b/src/pci/config_accessors/ecam.rs @@ -0,0 +1,74 @@ +// 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 bit_field::BitField; + +use super::{PciConfigAccessor, PciConfigMmio, PciRegion}; + +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_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..7516d7f6 --- /dev/null +++ b/src/pci/config_accessors/loongarch64.rs @@ -0,0 +1,113 @@ +// 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 bit_field::BitField; + +use super::{PciConfigAccessor, PciConfigMmio, PciRegion}; + +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_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..5b3ee5ef --- /dev/null +++ b/src/pci/config_accessors/mod.rs @@ -0,0 +1,157 @@ +// 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_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..7e4533fe --- /dev/null +++ b/src/pci/mem_alloc.rs @@ -0,0 +1,85 @@ +// 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 = u32; +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; +} + +#[derive(Default, Debug)] +pub struct BaseAllocator { + mem32: Range, + mem32_used: Mem32Address, + mem64: Range, + mem64_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; + } +} + +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 + } + } +} diff --git a/src/pci/mod.rs b/src/pci/mod.rs index 203999af..f4f53512 100644 --- a/src/pci/mod.rs +++ b/src/pci/mod.rs @@ -13,96 +13,14 @@ // // Authors: // -use spin::Once; +#![allow(dead_code)] +pub mod config_accessors; +pub mod mem_alloc; +pub mod pci_access; +pub mod pci_config; +pub mod pci_struct; +pub mod vpci_dev; -pub mod bridge; -pub mod endpoint; -pub mod pci; -pub mod pcibar; -pub mod phantom_cfg; +pub mod pci_test; -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; - -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..2791058d --- /dev/null +++ b/src/pci/pci_access.rs @@ -0,0 +1,1394 @@ +// 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 alloc::string::String; +use bit_field::BitField; +use bitflags::bitflags; +use core::{ + fmt::Debug, + ops::{Index, IndexMut}, + slice, +}; + +use super::{ + config_accessors::{PciConfigMmio, PciRegion}, + pci_struct::VirtualPciConfigSpace, + PciConfigAddress, +}; + +use crate::{ + error::HvResult, + memory::{ + mmio_perform_access, GuestPhysAddr, HostPhysAddr, MMIOAccess, MemFlags, MemoryRegion, + MemorySet, + }, + pci::pci_struct::BIT_LENTH, + percpu::this_zone, + zone::{this_zone_id, Zone}, +}; + +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 + */ +#[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 get_size(&self) -> u64 { + self.size + } + + pub fn get_size_with_flag(&mut 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 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; + } + } + _ => {} + } + + 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; + } + } + _ => {} + } + + 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) -> (DeviceRevision, BaseClass, SubClass, Interface) { + let value = self.backend().read_u32(0x08).unwrap(); + ( + value.get_bits(0..8) as DeviceRevision, + value.get_bits(24..32) as BaseClass, + value.get_bits(16..24) as SubClass, + value.get_bits(8..16) as Interface, + ) + } + + 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() { + warn!("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 { + fn to_offset(&self) -> usize; + fn size(&self) -> usize; +} + +pub enum EndpointField { + ID, + Command, + Status, + RevisionIDAndClassCode, + CacheLineSize, + LatencyTime, + HeaderType, + Bist, + Bar, + CardCisPointer, + SubsystemVendorId, + SubsystemId, + ExpansionRomBar, + CapabilityPointer, + InterruptLine, + InterruptPin, + MinGnt, + MaxLat, + Unknown(usize), +} + +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 => 0x10, + 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 + } + (0x28, 4) => EndpointField::CardCisPointer, + (0x2c, 2) => EndpointField::SubsystemVendorId, + (0x2e, 2) => EndpointField::SubsystemId, + (0x30, 4) => EndpointField::ExpansionRomBar, + (0x34, 1) => 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 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 {} + +fn handle_config_space_access( + dev: &mut VirtualPciConfigSpace, + mmio: &mut MMIOAccess, + offset: PciConfigAddress, + gpm: &mut MemorySet, + zone_id: usize, +) -> HvResult { + let size = mmio.size; + let value = mmio.value; + let is_write = mmio.is_write; + let vbdf = dev.get_vbdf(); + + if vbdf.bus == 0 && vbdf.device == 5 && vbdf.function == 0 { + info!("virt pci standard access, vbdf {:#?}, offset {:#x}, size {:#x}, is_write {:#?}", vbdf, offset, size, is_write); + } + + if (offset as usize) >= BIT_LENTH { + warn!("invalid pci offset {:#x}", offset); + if !is_write { + mmio.value = 0; + } + return Ok(()); + } + + match dev.access(offset, size) { + false => { + debug!( + "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 => { + info!( + "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.get_dev_type() { + super::vpci_dev::VpciDevType::Physical => { + match dev.get_config_type() { + HeaderType::Endpoint => { + match EndpointField::from(offset as usize, size) { + EndpointField::Bar => { + let slot = ((offset - 0x10) / 4) as usize; + /* the write of bar needs to start from dev, + * where the bar variable here is just a copy + */ + let bar = &mut dev.get_bararr()[slot]; + let bar_type = bar.get_type(); + if bar_type != PciMemType::default() { + if is_write { + if (value & 0xfffffff0) == 0xfffffff0 { + dev.set_bar_size_read(slot); + } else { + let _ = dev.write_emu(offset, size, value); + /* for mem64, Mem64High always write after Mem64Low, + * so update bar when write Mem64High + */ + if (bar_type == PciMemType::Mem32) + | (bar_type == PciMemType::Mem64High) + | (bar_type == PciMemType::Io) + { + let old_vaddr = 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 + */ + dev.read_emu64(offset - 0x4).unwrap() & !0xf + } else { + (value as u64) & !0xf + } + }; + /* Linux traverses the PCI bus twice. During the first traversal, + * it does not assign addresses to the BARs; it simply writes back the same + * values. In the second traversal, it reorders the BARs and assigns + * addresses to them. Each time the guest writes to a BAR, + * it attempts to remove the previous mapping and add a new one. + * However, on the first access there is no prior mapping, so a single warning + * is normal. Subsequent warnings should be treated with caution. + * + * TODO: When adding a new device or removing an old one, reloading + * the PCIe bus, will the newly written BAR address overlap with + * the old BAR addresses, potentially causing the update to fail? + */ + if !gpm + .try_delete(old_vaddr.try_into().unwrap()) + .is_ok() + { + /* The first delete from the guest will fail + * because the region has not yet been inserted + */ + warn!( + "delete bar {}: can not found 0x{:x}", + slot, old_vaddr + ); + } + let paddr = bar.get_value64(); + debug!( + "old_vaddr {:x} new_vaddr {:x} paddr {:x}", + old_vaddr, new_vaddr, paddr + ); + + dev.set_bar_virtual_value(slot, new_vaddr); + if bar_type == PciMemType::Mem64High { + dev.set_bar_virtual_value(slot - 1, new_vaddr); + } + + let bar_size = if crate::memory::addr::is_aligned( + bar.get_size() as usize, + ) { + bar.get_size() + } else { + crate::memory::PAGE_SIZE as u64 + }; + + gpm.insert(MemoryRegion::new_with_offset_mapper( + new_vaddr as GuestPhysAddr, + paddr as HostPhysAddr, + bar_size as _, + MemFlags::READ | MemFlags::WRITE, + ))?; + /* 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")] + crate::arch::iommu::flush( + zone_id, + vbdf.bus, + (vbdf.device << 3) + vbdf.function, + ); + } + } + } else { + mmio.value = if bar.get_size_read() { + let r = bar.get_size_with_flag().try_into().unwrap(); + dev.clear_bar_size_read(slot); + r + } else { + bar.get_virtual_value().try_into().unwrap() + }; + } + } else { + mmio.value = 0; + } + } + EndpointField::ExpansionRomBar => { + let mut rom = dev.get_rom(); + if is_write { + if (mmio.value & 0xfffff800) == 0xfffff800 { + rom.set_size_read(); + } else { + // let old_vaddr = dev.read_emu(offset, size).unwrap() as u64; + let _ = dev.write_emu(offset, size, value); + // TODO: add gpm change for rom + } + } else { + mmio.value = if rom.get_size_read() { + dev.read_emu(offset, size).unwrap() + } else { + rom.get_size_with_flag().try_into().unwrap() + }; + } + } + _ => {} + } + } + HeaderType::PciBridge => { + // TODO: add emu for bridge, actually it is same with endpoint + warn!("bridge emu rw"); + } + _ => { + warn!("unhanled pci type {:#?}", dev.get_config_type()); + } + } + } + _ => { + if mmio.is_write { + super::vpci_dev::vpci_dev_write_cfg(dev.get_dev_type(), dev, offset, size, value).unwrap(); + } else { + mmio.value = super::vpci_dev::vpci_dev_read_cfg(dev.get_dev_type(), dev, offset, size).unwrap() as usize; + } + } + } + } + } + + debug!( + "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_id = this_zone_id(); + let zone = this_zone(); + let mut guard = zone.write(); + let (vbus, gpm) = { + let Zone { gpm, vpci_bus, .. } = &mut *guard; + (vpci_bus, gpm) + }; + + let offset = (mmio.address & 0xfff) as PciConfigAddress; + let base = mmio.address as PciConfigAddress - offset + _base as PciConfigAddress; + + if let Some(dev) = vbus.get_device_by_base(base) { + handle_config_space_access(dev, mmio, offset, gpm, zone_id)?; + } else { + handle_device_not_found(mmio, offset); + } + + Ok(()) +} + +pub fn mmio_vpci_handler_dbi(mmio: &mut MMIOAccess, _base: usize) -> HvResult { + // info!("mmio_vpci_handler_dbi {:#x}", mmio.address); + + if mmio.address >= 0x300000 + /* ATU base */ + { + mmio_perform_access(_base, mmio); + } else if mmio.address >= BIT_LENTH { + // dbi read + mmio_perform_access(_base, mmio); + } else { + let offset = (mmio.address & 0xfff) as PciConfigAddress; + let zone_id = this_zone_id(); + let zone = this_zone(); + let mut guard = zone.write(); + let (vbus, gpm) = { + let Zone { gpm, vpci_bus, .. } = &mut *guard; + (vpci_bus, gpm) + }; + + let base = mmio.address as PciConfigAddress - offset + _base as PciConfigAddress; + + if let Some(dev) = vbus.get_device_by_base(base) { + handle_config_space_access(dev, mmio, offset, gpm, zone_id)?; + } else { + handle_device_not_found(mmio, offset); + } + } + + Ok(()) +} diff --git a/src/pci/pci_config.rs b/src/pci/pci_config.rs new file mode 100644 index 00000000..8c422015 --- /dev/null +++ b/src/pci/pci_config.rs @@ -0,0 +1,268 @@ +// 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::{ + arch::iommu::iommu_add_device, + config::{HvPciConfig, HvPciDevConfig, CONFIG_MAX_PCI_DEV, CONFIG_PCI_BUS_MAXNUM}, + error::HvResult, + pci::pci_struct::{Bdf, VirtualPciConfigSpace}, + zone::Zone, +}; + +#[cfg(feature = "ecam_pcie")] +use crate::pci::vpci_dev::{VpciDevType, get_handler}; + +#[cfg(any( + feature = "ecam_pcie", + feature = "dwc_pcie", + feature = "loongarch64_pcie" +))] +use crate::pci::{mem_alloc::BaseAllocator, pci_struct::RootComplex}; + +#[cfg(any(feature = "ecam_pcie", feature = "dwc_pcie"))] +use crate::pci::pci_access::mmio_vpci_handler; + +#[cfg(feature = "dwc_pcie")] +use crate::{memory::mmio_generic_handler, pci::pci_access::mmio_vpci_handler_dbi, platform}; + +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.pci_mem32_base as u32, + rootcomplex_config.mem32_size as u32, + ); + allocator.set_mem64( + rootcomplex_config.pci_mem64_base, + rootcomplex_config.mem64_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); + + 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 e = rootcomplex.enumerate(Some(range), allocator_opt); + info!("begin enumerate {:#?}", e); + for node in e { + info!("node {:#?}", node); + GLOBAL_PCIE_LIST.lock().insert(node.get_bdf(), 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, + //TODO: set vpci dev base in other way + _ecam_base: u64, + ) -> HvResult { + let mut guard = GLOBAL_PCIE_LIST.lock(); + let mut i = 0; + while i < num_pci_devs { + let dev_config = alloc_pci_devs[i as usize]; + let bdf = Bdf::from_address(dev_config.bdf << 12); + let vbdf = bdf.clone(); + #[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 + }; + iommu_add_device(zone_id, dev_config.bdf as _, iommu_pt_addr); + } + if let Some(dev) = guard.get(&bdf) { + if bdf.is_host_bridge(dev.get_host_bdf().bus()) { + let mut vdev = dev.clone(); + vdev.set_vbdf(vbdf); + self.vpci_bus.insert(vbdf, vdev); + } else { + let mut vdev = guard.remove(&bdf).unwrap(); + vdev.set_vbdf(vbdf); + self.vpci_bus.insert(vbdf, vdev); + } + } 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); + self.vpci_bus.insert(vbdf, dev); + } else { + warn!("can not find dev {:#?}, unknown device type", bdf); + } + } + } + } + } + i += 1; + } + 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")] + { + self.mmio_region_register( + rootcomplex_config.ecam_base as usize, + rootcomplex_config.ecam_size as usize, + mmio_vpci_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_vpci_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_vpci_handler, + cfg1_base as usize, + ); + } + } else { + warn!( + "No extend config found for base 0x{:x}", + rootcomplex_config.ecam_base + ); + } + } + } + } +} diff --git a/src/pci/pci_struct.rs b/src/pci/pci_struct.rs new file mode 100644 index 00000000..18c13c42 --- /dev/null +++ b/src/pci/pci_struct.rs @@ -0,0 +1,1419 @@ +// 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::Range, str::FromStr}; + +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, + }, + PciConfigAddress, + pci_access::{BaseClass, SubClass, Interface}, +}; + +use crate::{error::{HvErrorNum, HvResult}, pci::vpci_dev::VpciDevType}; + +type VirtualPciConfigBits = BitArr!(for BIT_LENTH, in u8, Lsb0); + +const MAX_DEVICE: u8 = 31; +const MAX_FUNCTION: u8 = 7; +pub const CONFIG_LENTH: u64 = 256; +pub const BIT_LENTH: usize = 128 * 5; + +// 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 bus: u8, + pub device: u8, + pub function: u8, +} + +impl Bdf { + #[allow(dead_code)] + pub fn new(bus: u8, device: u8, function: u8) -> Self { + Self { + bus, + device, + function, + } + } + + pub fn bus(&self) -> u8 { + self.bus + } + + pub fn device(&self) -> u8 { + self.device + } + + pub fn function(&self) -> u8 { + self.function + } + + pub fn from_address(address: PciConfigAddress) -> Bdf { + let bdf = address >> 12; + let function = (bdf & 0b111) as u8; + let device = ((bdf >> 3) & 0b11111) as u8; + let bus = (bdf >> 8) as u8; + Bdf { + bus, + device, + 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 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 { + 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[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, + } + } +} + +#[derive(Clone, Debug)] +pub struct PciConfigSpace { + data: [u8; BIT_LENTH], +} + +impl PciConfigSpace { + pub fn new() -> Self { + Self { + data: [0u8; BIT_LENTH], + } + } + + pub fn as_slice(&self) -> &[u8] { + &self.data + } + + pub fn as_mut_slice(&mut self) -> &mut [u8] { + &mut self.data + } + + pub fn get_range(&self, offset: usize, size: usize) -> &[u8] { + &self.data[offset..offset + size] + } + + pub fn get_range_mut(&mut self, offset: usize, size: usize) -> &mut [u8] { + &mut self.data[offset..offset + size] + } + + pub fn set(&mut self, field: F, value: u32) { + let offset = field.to_offset(); + let size = field.size(); + match size { + 1 => { + self.get_range_mut(offset, 1)[0] = value as u8; + } + 2 => { + self.get_range_mut(offset, 2).copy_from_slice(&(value as u16).to_le_bytes()); + } + 4 => { + self.get_range_mut(offset, 4).copy_from_slice(&value.to_le_bytes()); + } + _ => { + // For other sizes, write as many bytes as needed + let bytes = value.to_le_bytes(); + self.get_range_mut(offset, size.min(bytes.len())).copy_from_slice(&bytes[..size.min(bytes.len())]); + } + } + } + + pub fn init_with_type(dev_type: VpciDevType) -> Self { + crate::pci::vpci_dev::init_config_space_with_type(dev_type) + } +} + +impl Default for PciConfigSpace { + fn default() -> Self { + Self::new() + } +} + +/* 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 + * space: the space where emulate the config space + * control: control the satus of rw every bit in config space + * access: Determines whether the variable is read from space 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, + class: (BaseClass, SubClass, Interface), + + base: PciConfigAddress, + + pub space: PciConfigSpace, + control: VirtualPciConfigControl, + access: VirtualPciAccessBits, + + backend: Arc, + + bararr: Bar, + rom: PciMem, + capabilities: PciCapabilityList, + + dev_type: VpciDevType, +} + +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 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 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 + } + + // TODO: update sapce when first time read value from hw, and next read will more quick + pub fn update_space(&mut self, offset: PciConfigAddress, size: usize, _value: usize) { + match self.get_config_type() { + HeaderType::Endpoint => { + match EndpointField::from(offset as usize, size) { + EndpointField::Bar => { + // let updating_range = offset as usize..offset as usize+ size; + // let bytes = &value.to_le_bytes()[..size]; + // info!("[{:x}-{:x}] bytes {:#?} \n{:x}", updating_range.start, updating_range.end, bytes, value); + // self.space[updating_range.clone()].copy_from_slice(bytes); + // self.access.bits[updating_range].fill(true); + } + _ => {} + } + } + _ => { + // warn!("TODO updating space"); + } + } + } +} + +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 {:#?} {:#?}", + self.bdf, self.base, self.config_type, self.bararr, self.rom + ) + } +} + +impl VirtualPciConfigSpace { + pub fn virt_dev( + bdf: Bdf, + base: PciConfigAddress, + ) -> Self { + Self { + host_bdf: Bdf::default(), + parent_bdf: Bdf::default(), + bdf, + vbdf: Bdf::default(), + config_type: HeaderType::Endpoint, + class: (0u8,0u8,0u8), + base, + space: PciConfigSpace::init_with_type(VpciDevType::StandardVdev), + control: VirtualPciConfigControl::virt_dev(), + access: VirtualPciAccessBits::virt_dev(), + backend: Arc::new(EndpointHeader::new_with_region(PciConfigMmio::new(base, CONFIG_LENTH))), + bararr: Bar::default(), + rom: PciMem::default(), + capabilities: PciCapabilityList::new(), + dev_type: VpciDevType::StandardVdev, + } + } + pub fn endpoint( + bdf: Bdf, + base: PciConfigAddress, + backend: Arc, + bararr: Bar, + rom: PciMem, + class: (BaseClass, SubClass, Interface), + ) -> Self { + Self { + host_bdf: Bdf::default(), + parent_bdf: Bdf::default(), + bdf, + vbdf: Bdf::default(), + config_type: HeaderType::Endpoint, + class, + base, + space: PciConfigSpace::new(), + 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: (BaseClass, SubClass, Interface), + ) -> Self { + Self { + host_bdf: Bdf::default(), + parent_bdf: Bdf::default(), + bdf, + vbdf: Bdf::default(), + config_type: HeaderType::PciBridge, + class, + base, + space: PciConfigSpace::new(), + 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) -> Self { + Self { + host_bdf: Bdf::default(), + parent_bdf: Bdf::default(), + bdf, + vbdf: Bdf::default(), + config_type: HeaderType::Endpoint, + class: (0u8,0u8,0u8), + base, + space: PciConfigSpace::new(), + 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: (BaseClass, SubClass, Interface)) -> Self { + Self { + host_bdf: bdf, + parent_bdf: bdf, + bdf: bdf, + vbdf: bdf, + config_type: HeaderType::Endpoint, + class, + base, + space: PciConfigSpace::new(), + 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 + */ + pub fn space_init(&mut self) { + for (slot, bar) in self.bararr.into_iter().enumerate() { + let offset = 0x10 + slot * 4; + let bytes = bar.get_value().to_le_bytes(); + self.space.get_range_mut(offset, 4).copy_from_slice(&bytes); + } + match self.config_type { + HeaderType::Endpoint => { + let bytes = self.rom.get_value().to_le_bytes(); + self.space.get_range_mut(0x30, 4).copy_from_slice(&bytes); + } + HeaderType::PciBridge => { + let bytes = self.rom.get_value().to_le_bytes(); + self.space.get_range_mut(0x38, 4).copy_from_slice(&bytes); + } + _ => {} + } + } +} + +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_space(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_space(offset, size, value); + } + r + } else { + hv_result_err!(EPERM, "pci: invalid write to hw") + } + } + + pub fn read_emu(&mut self, offset: PciConfigAddress, size: usize) -> HvResult { + match size { + 1 | 2 | 4 => { + let slice = self.space.get_range(offset as usize, size); + let value = match size { + 1 => slice[0] as usize, + 2 => u16::from_le_bytes(slice.try_into().unwrap()) as usize, + 4 => u32::from_le_bytes(slice.try_into().unwrap()) as usize, + _ => unreachable!(), + }; + Ok(value) + } + _ => { + hv_result_err!(EFAULT, "pci: invalid virtual mmio read size: {size}") + } + } + } + + pub fn read_emu64(&mut self, offset: PciConfigAddress) -> HvResult { + let slice = self.space.get_range(offset as usize, 8); + let value = u64::from_le_bytes(slice.try_into().unwrap()) as u64; + Ok(value) + } + + pub fn write_emu(&mut self, offset: PciConfigAddress, size: usize, value: usize) -> HvResult { + if self.writable(offset, size) { + match size { + 1 | 2 | 4 => { + let slice = self.space.get_range_mut(offset as usize, size); + match size { + 1 => slice[0] = value as u8, + 2 => slice.copy_from_slice(&u16::to_le_bytes(value as u16)), + 4 => slice.copy_from_slice(&u32::to_le_bytes(value as u32)), + _ => unreachable!(), + } + Ok(()) + } + _ => { + hv_result_err!(EFAULT, "pci: invalid virtual mmio write size: {size}") + } + } + } else { + hv_result_err!(EPERM, "pci: invalid write to hw") + } + } +} + +#[derive(Debug)] +pub struct PciIterator { + allocator: Option, + stack: Vec, + segment: PciConfigAddress, + bus_range: Range, + function: u8, + is_mulitple_function: bool, + is_finish: bool, + accessor: Arc, +} + +impl PciIterator { + 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(bus, device, function); + + let address = self.address(parent_bus, 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 + warn!( + "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 (_, base_class, sub_class, interface) = pci_header.revision_and_class(); + let class = (base_class, sub_class, interface); + + 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, address, ep, bararr, rom, class); + + 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, address, bridge, bararr, rom, class); + + let _ = node.capability_enumerate(); + + Some(node) + } + _ => { + warn!("unknown type"); + let pci_header = Arc::new(pci_header); + Some(VirtualPciConfigSpace::unknown(bdf, address, pci_header)) + } + } + } + + fn rom_init(dev: &mut D) -> PciMem { + let mut rom = dev.parse_rom(); + 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 u32).unwrap(); + bararr[i].set_value(value as u64); + bararr[i].set_virtual_value(value as u64); + let _ = dev.write_bar(i as u8, value); + } + 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 { + info!("pci dev next"); + while !self.is_finish { + if let Some(mut node) = self.get_node() { + node.space_init(); + let bus_begin = self.bus_range.start as u8; + /* + * 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(bus_begin, 0, 0); + let parent_bdf = Bdf::new(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.class.0 { + 0x6 if node.class.1 != 0x0 => { + let bdf = Bdf::new(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>, + 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, + function: 0, + is_mulitple_function: false, + is_finish: false, + accessor: self.accessor.clone(), // accessor to iterator + } + } + + pub fn enumerate( + &mut self, + range: Option>, + bar_alloc: Option, + ) -> PciIterator { + self.__enumerate(range, 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(); + self.base_to_bdf.insert(base, bdf); + self.devs.insert(bdf, dev) + } + + pub fn devs(&mut self) -> &mut BTreeMap { + &mut self.devs + } + + pub fn get(&self, bdf: &Bdf) -> Option<&VirtualPciConfigSpace> { + self.devs.get(bdf) + } + + pub fn get_mut(&mut self, bdf: &Bdf) -> Option<&mut VirtualPciConfigSpace> { + 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<&mut VirtualPciConfigSpace> { + let bdf = self.base_to_bdf.get(&base).copied()?; + self.devs.get_mut(&bdf) + } +} + +#[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.get_extension()); + // Move to next capability + let _ = self.get_next_cap(); + if let Some(cap) = cap { + return Some(cap); + } + } + None + } +} + +#[derive(Clone)] +pub enum PciCapability { + // Power management capability, Cap ID = `0x01` + PowerManagement(PciCapabilityRegion), + // Accelerated graphics port capability, Cap ID = `0x02` + AcceleratedGraphicsPort(PciCapabilityRegion), + // Vital product data capability, Cap ID = `0x3` + VitalProductData(PciCapabilityRegion), + // Slot identification capability, Cap ID = `0x04` + SlotIdentification(PciCapabilityRegion), + // Message signalling interrupts capability, Cap ID = `0x05` + Msi(PciCapabilityRegion), + // CompactPCI HotSwap capability, Cap ID = `0x06` + CompactPCIHotswap(PciCapabilityRegion), + // PCI-X capability, Cap ID = `0x07` + PciX(PciCapabilityRegion), + // HyperTransport capability, Cap ID = `0x08` + HyperTransport(PciCapabilityRegion), + // Vendor-specific capability, Cap ID = `0x09` + Vendor(PciCapabilityRegion), + // Debug port capability, Cap ID = `0x0A` + DebugPort(PciCapabilityRegion), + // CompactPCI Central Resource Control capability, Cap ID = `0x0B` + CompactPCICentralResourceControl(PciCapabilityRegion), + // PCI Standard Hot-Plug Controller capability, Cap ID = `0x0C` + PciHotPlugControl(PciCapabilityRegion), + // Bridge subsystem vendor/device ID capability, Cap ID = `0x0D` + BridgeSubsystemVendorId(PciCapabilityRegion), + // AGP Target PCI-PCI bridge capability, Cap ID = `0x0E` + AGP3(PciCapabilityRegion), + // PCI Express capability, Cap ID = `0x10` + PciExpress(PciCapabilityRegion), + // MSI-X capability, Cap ID = `0x11` + MsiX(PciCapabilityRegion), + // Unknown capability + Unknown(PciCapabilityRegion), +} + +impl PciCapability { + fn from_address( + offset: PciConfigAddress, + id: PciConfigAddress, + extension: u16, + ) -> Option { + match id { + 0x00 => None, + 0x01 => Some(PciCapability::PowerManagement(PciCapabilityRegion::new( + offset, extension, + ))), + 0x02 => Some(PciCapability::AcceleratedGraphicsPort( + PciCapabilityRegion::new(offset, extension), + )), + 0x03 => Some(PciCapability::VitalProductData(PciCapabilityRegion::new( + offset, extension, + ))), + 0x04 => Some(PciCapability::SlotIdentification(PciCapabilityRegion::new( + offset, extension, + ))), + 0x05 => Some(PciCapability::Msi(PciCapabilityRegion::new( + offset, extension, + ))), + 0x06 => Some(PciCapability::CompactPCIHotswap(PciCapabilityRegion::new( + offset, extension, + ))), + 0x07 => Some(PciCapability::PciX(PciCapabilityRegion::new( + offset, extension, + ))), + 0x08 => Some(PciCapability::HyperTransport(PciCapabilityRegion::new( + offset, extension, + ))), + 0x09 => Some(PciCapability::Vendor(PciCapabilityRegion::new( + offset, extension, + ))), + 0x0A => Some(PciCapability::DebugPort(PciCapabilityRegion::new( + offset, extension, + ))), + 0x0B => Some(PciCapability::CompactPCICentralResourceControl( + PciCapabilityRegion::new(offset, extension), + )), + 0x0C => Some(PciCapability::PciHotPlugControl(PciCapabilityRegion::new( + offset, extension, + ))), + 0x0D => Some(PciCapability::BridgeSubsystemVendorId( + PciCapabilityRegion::new(offset, extension), + )), + 0x0E => Some(PciCapability::AGP3(PciCapabilityRegion::new( + offset, extension, + ))), + 0x10 => Some(PciCapability::PciExpress(PciCapabilityRegion::new( + offset, extension, + ))), + 0x11 => Some(PciCapability::MsiX(PciCapabilityRegion::new( + offset, extension, + ))), + _ => Some(PciCapability::Unknown(PciCapabilityRegion::new( + offset, extension, + ))), + } + } + + fn get_offset(&self) -> PciConfigAddress { + match *self { + PciCapability::PowerManagement(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::AcceleratedGraphicsPort(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::VitalProductData(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::SlotIdentification(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::Msi(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::CompactPCIHotswap(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::PciX(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::HyperTransport(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::Vendor(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::DebugPort(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::CompactPCICentralResourceControl(PciCapabilityRegion { + offset, .. + }) => offset, + PciCapability::PciHotPlugControl(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::BridgeSubsystemVendorId(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::AGP3(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::PciExpress(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::MsiX(PciCapabilityRegion { offset, .. }) => offset, + PciCapability::Unknown(PciCapabilityRegion { offset, .. }) => offset, + } + } +} + +impl Debug for PciCapability { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match *self { + PciCapability::PowerManagement(PciCapabilityRegion { offset, .. }) => { + write!(f, "PowerManagement {:x}", offset) + } + PciCapability::AcceleratedGraphicsPort(PciCapabilityRegion { offset, .. }) => { + write!(f, "AcceleratedGraphicsPort {:x}", offset) + } + PciCapability::VitalProductData(PciCapabilityRegion { offset, .. }) => { + write!(f, "VitalProductData {:x}", offset) + } + PciCapability::SlotIdentification(PciCapabilityRegion { offset, .. }) => { + write!(f, "SlotIdentification {:x}", offset) + } + PciCapability::Msi(PciCapabilityRegion { offset, .. }) => write!(f, "Msi {:x}", offset), + PciCapability::CompactPCIHotswap(PciCapabilityRegion { offset, .. }) => { + write!(f, "CompactPCIHotswap {:x}", offset) + } + PciCapability::PciX(PciCapabilityRegion { offset, .. }) => { + write!(f, "PciX {:x}", offset) + } + PciCapability::HyperTransport(PciCapabilityRegion { offset, .. }) => { + write!(f, "HyperTransport {:x}", offset) + } + PciCapability::Vendor(PciCapabilityRegion { offset, .. }) => { + write!(f, "Vendor {:x}", offset) + } + PciCapability::DebugPort(PciCapabilityRegion { offset, .. }) => { + write!(f, "DebugPort {:x}", offset) + } + PciCapability::CompactPCICentralResourceControl(PciCapabilityRegion { + offset, .. + }) => write!(f, "CompactPCICentralResourceControl {:x}", offset), + PciCapability::PciHotPlugControl(PciCapabilityRegion { offset, .. }) => { + write!(f, "PciHotPlugControl {:x}", offset) + } + PciCapability::BridgeSubsystemVendorId(PciCapabilityRegion { offset, .. }) => { + write!(f, "BridgeSubsystemVendorId {:x}", offset) + } + PciCapability::AGP3(PciCapabilityRegion { offset, .. }) => { + write!(f, "AGP3 {:x}", offset) + } + PciCapability::PciExpress(PciCapabilityRegion { offset, .. }) => { + write!(f, "PciExpress {:x}", offset) + } + PciCapability::MsiX(PciCapabilityRegion { offset, .. }) => { + write!(f, "MsiX {:x}", offset) + } + PciCapability::Unknown(PciCapabilityRegion { offset, .. }) => { + write!(f, "Unknown {:x}", offset) + } + } + } +} + +#[derive(Clone)] +pub struct PciCapabilityRegion { + offset: PciConfigAddress, + extension: u16, +} + +impl PciCapabilityRegion { + pub fn new(offset: PciConfigAddress, extension: u16) -> Self { + Self { offset, extension } + } +} + +pub type PciCapabilityList = BTreeMap; + +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 { + PciCapability::Msi(_) => {} + PciCapability::MsiX(_) => {} + PciCapability::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 + // for (_, capability) in &self.capabilities { + // if let PciCapability::PciExpress(PciCapabilityRegion { offset, .. }) = capability { + // // Read PCIe Capability Register at offset + 0x00 + // // Bits 4:0 contain the Device/Port Type + // 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..a61bb521 --- /dev/null +++ b/src/pci/pci_test.rs @@ -0,0 +1,149 @@ +// 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::{ + mem_alloc::BaseAllocator, + pci_access::mmio_vpci_handler, + pci_struct::{Bdf, VirtualPciConfigSpace, CONFIG_LENTH}, +}; + +use crate::{ + memory::{mmio_perform_access, MMIOAccess}, + pci::{config_accessors::PciConfigMmio, pci_access::EndpointHeader}, + percpu::this_zone, +}; + +pub static GLOBAL_PCIE_LIST_TEST: Lazy>> = + Lazy::new(|| { + let m = BTreeMap::new(); + Mutex::new(m) + }); + +pub fn pcie_test() { + info!("pcie test"); + let mut allocator = BaseAllocator::default(); + allocator.set_mem32(0x10000000, 0x2efeffff); + allocator.set_mem64(0x8000000000, 0xffffffffff - 0x8000000000); + + // let mut root = RootComplex::new(0x4010000000); + // for node in root.enumerate(None, Some(allocator)) { + // GLOBAL_PCIE_LIST_TEST.lock().insert(node.get_bdf(), node); + // } +} + +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,0x0u8,0u8)); + 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 vbus = &zone.read().vpci_bus; + let bdf = Bdf::from_str("0000:00:01.0").unwrap(); + // Get base from VirtualPciConfigSpace and add offset + let address = if let Some(vdev) = vbus.get(&bdf) { + vdev.get_base() + 0x24 + } else { + warn!("can not find dev {:#?} for test", bdf); + 0 + }; + let mut mmio = MMIOAccess { + address: address as _, + size: 4, + is_write: false, + value: 0x0, + }; + let ret = mmio_vpci_handler(&mut mmio, 0); + info!("{:#?}", ret); + info!( + "mmio offset {:x}, is_wirte {}, size {}, value 0x{:x}", + mmio.address, mmio.is_write, mmio.size, mmio.value + ); + + info!("pcie guest test passed"); +} + +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"); +} 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..afc79bc3 --- /dev/null +++ b/src/pci/vpci_dev/mod.rs @@ -0,0 +1,148 @@ +use crate::pci::pci_struct::{PciConfigSpace, VirtualPciConfigSpace}; +use crate::pci::PciConfigAddress; +use crate::error::HvResult; + +pub mod standard; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum PciConfigAccessStatus { + Done, + Perform, + Reject, +} + +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum VpciDevType { + #[default] + Physical=0, + StandardVdev=1, + // Add new device types here +} + +pub trait VpciDeviceHandler: Sync + Send { + fn read_cfg(&self) -> HvResult; + fn write_cfg(&self) -> HvResult; + fn init_config_space(&self) -> PciConfigSpace; +} + +/* + * 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: &mut VirtualPciConfigSpace, + 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() { + Ok(status) => { + match status { + PciConfigAccessStatus::Done => { + Ok(status as usize) + } + PciConfigAccessStatus::Perform => { + warn!("vpci_dev_read_cfg: perform, offset {:#x}, size {:#x}", offset, size); + // warn!("vpci_dev_read_cfg: node {:#?}", node.space); + let r = node.read_emu(offset, size).unwrap(); + warn!("vpci_dev_read_cfg: perform result {:#x}", r); + 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: &mut VirtualPciConfigSpace, + 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() { + Ok(status) => { + match status { + PciConfigAccessStatus::Done => { + Ok(()) + } + PciConfigAccessStatus::Perform => { + warn!("vpci_dev_write_cfg: perform"); + node.write_emu(offset, size, value) + } + 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 init_config_space_with_type(dev_type: VpciDevType) -> PciConfigSpace { + match dev_type { + VpciDevType::Physical => { + // Physical devices use default (all zeros) space + PciConfigSpace::new() + } + _ => { + if let Some(handler) = get_handler(dev_type) { + handler.init_config_space() + } else { + warn!("init_config_space_with_type: unknown device type"); + PciConfigSpace::new() + } + } + } +} \ No newline at end of file diff --git a/src/pci/vpci_dev/standard.rs b/src/pci/vpci_dev/standard.rs new file mode 100644 index 00000000..2636245f --- /dev/null +++ b/src/pci/vpci_dev/standard.rs @@ -0,0 +1,66 @@ +use crate::error::HvResult; +use crate::pci::pci_struct::PciConfigSpace; +use crate::pci::pci_access::EndpointField; +use super::{PciConfigAccessStatus, VpciDeviceHandler}; + +const STANDARD_VENDOR_ID: u16 = 0x110a; +const STANDARD_DEVICE_ID: u16 = 0x4106; +const PCI_STS_CAPS: u16 = 0x10; // bit 4 +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 = 0x60; // VNDR_CAP + VNDR_LEN +const STANDARD_MSIX_VECTORS: u16 = 16; +const STANDARD_CFG_SIZE: usize = 0x80; + +pub(crate) const DEFAULT_CSPACE_U32: [u32; STANDARD_CFG_SIZE / 4] = { + 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 +}; + +/// Handler for standard virtual PCI devices +pub struct StandardHandler; + +impl VpciDeviceHandler for StandardHandler { + fn read_cfg(&self) -> HvResult { + info!("virt pci standard read_cfg"); + Ok(PciConfigAccessStatus::Perform) + } + + fn write_cfg(&self) -> HvResult { + info!("virt pci standard write_cfg"); + Ok(PciConfigAccessStatus::Perform) + } + + fn init_config_space(&self) -> PciConfigSpace { + let mut space = PciConfigSpace::new(); + let default_cspace = DEFAULT_CSPACE_U32; + let mut offset = 0; + for &value in &default_cspace { + space.get_range_mut(offset, 4).copy_from_slice(&value.to_le_bytes()); + offset += 4; + } + + // Example: update vendor ID + space.set(EndpointField::ID, 0x12345678); + + space + } +} + +/// Static handler instance for standard virtual PCI devices +pub const HANDLER: StandardHandler = StandardHandler; \ No newline at end of file diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 8dc1c493..95fc4a88 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -15,8 +15,9 @@ // use crate::{ config::{ - HvConfigMemoryRegion, HvIvcConfig, HvPciConfig, HvZoneConfig, CONFIG_MAX_INTERRUPTS, - CONFIG_MAX_IVC_CONFIGS, CONFIG_MAX_MEMORY_REGIONS, CONFIG_MAX_PCI_DEV, CONFIG_NAME_MAXLEN, + HvConfigMemoryRegion, HvIvcConfig, HvPciConfig, HvPciDevConfig, HvZoneConfig, + CONFIG_MAX_INTERRUPTS, CONFIG_MAX_IVC_CONFIGS, CONFIG_MAX_MEMORY_REGIONS, + CONFIG_MAX_PCI_DEV, CONFIG_NAME_MAXLEN, CONFIG_PCI_BUS_MAXNUM, }, consts::INVALID_ADDRESS, }; @@ -71,16 +72,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( @@ -99,6 +107,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 58bf6ec4..39089846 100644 --- a/src/zone.rs +++ b/src/zone.rs @@ -17,7 +17,7 @@ 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; use crate::arch::mm::new_s2_memory_set; @@ -38,9 +38,9 @@ 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, } impl Zone { @@ -53,13 +53,13 @@ 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(), } } @@ -210,6 +210,23 @@ 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[0].ecam_base as u64); + } + + // #[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 +262,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)?;