diff --git a/.gitignore b/.gitignore index 4cb7754b..ee35f79a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,8 @@ /hvisor.S /hvisor-elf.txt /images/* -/platform/*/*/image/kernel/* -/platform/*/*/image/virtdisk/* +/platform/*/*/image/kernel +/platform/*/*/image/virtdisk /tools/hvisor /tmp *.mod.[co] 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..9a739804 100644 --- a/platform/aarch64/qemu-gicv2/board.rs +++ b/platform/aarch64/qemu-gicv2/board.rs @@ -18,6 +18,8 @@ use crate::{ config::*, }; +use crate::pci_dev; + pub const BOARD_NAME: &str = "qemu-gicv2"; pub const BOARD_NCPUS: usize = 4; @@ -90,20 +92,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), + pci_dev!(0x0, 0x1, 0x0), + pci_dev!(0x0, 0x2, 0x0), +]; 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..107e9995 100644 --- a/platform/aarch64/qemu-gicv3/board.rs +++ b/platform/aarch64/qemu-gicv3/board.rs @@ -20,6 +20,9 @@ use crate::{ }, config::*, }; + +use crate::pci_dev; + pub const BOARD_NAME: &str = "qemu-gicv3"; pub const BOARD_NCPUS: usize = 4; @@ -88,7 +91,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 +103,13 @@ pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { 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), + pci_dev!(0x0, 0x1, 0x0), + pci_dev!(0x0, 0x2, 0x0), + // pci_dev!(0x0, 0x3, 0x0), +]; diff --git a/platform/aarch64/qemu-gicv3/configs/zone1-linux.json b/platform/aarch64/qemu-gicv3/configs/zone1-linux.json index 17cb55f2..a4fa325d 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", + "vbdf": "0x0" + }, + { + "bdf": "0x18", + "vbdf": "0x18" + } + ] } \ No newline at end of file diff --git a/platform/aarch64/rk3568/board.rs b/platform/aarch64/rk3568/board.rs index 84f07a71..c2f8c4e9 100644 --- a/platform/aarch64/rk3568/board.rs +++ b/platform/aarch64/rk3568/board.rs @@ -43,16 +43,17 @@ pub const BOARD_PHYSMEM_LIST: &[(u64, u64, MemoryType)] = &[ // ( start, end, type) ( 0x0, 0xf0000000, MemoryType::Normal), ( 0xf0000000, 0x100000000, MemoryType::Device), + ( 0x100000000, 0x3fc000000, MemoryType::Normal), ]; 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; 20] = [ // HvConfigMemoryRegion { // mem_type: MEM_TYPE_IO, // physical_start: 0xfd400000, @@ -65,12 +66,6 @@ pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 19] = [ // virtual_start: 0xfd460000, // size: 0xc0000, // }, // gic - HvConfigMemoryRegion { - mem_type: MEM_TYPE_IO, - physical_start: 0xfe000000, - virtual_start: 0xfe000000, - size: 0x4000, - }, // dwmmc HvConfigMemoryRegion { mem_type: MEM_TYPE_RAM, physical_start: 0x200000, @@ -83,30 +78,48 @@ 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: 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: 0x0, + virtual_start: 0x0, + size: 0x200000, + }, // ram + // 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 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, diff --git a/platform/aarch64/rk3568/image/dts/rk3568_limit_zone0.dts b/platform/aarch64/rk3568/image/dts/rk3568_limit_zone0.dts index d2d43a26..5b188f0c 100644 --- a/platform/aarch64/rk3568/image/dts/rk3568_limit_zone0.dts +++ b/platform/aarch64/rk3568/image/dts/rk3568_limit_zone0.dts @@ -13,6 +13,88 @@ serial2 = "/serial@fe660000"; sdmmc2 = "/dwmmc@fe000000"; serial4 = "/serial@fe680000"; + mmc1 = "/dwmmc@fe2b0000"; + mmc2 = "/dwmmc@fe2c0000"; + mmc3 = "/dwmmc@fe000000"; + }; + + 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"; + }; + + 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,9 +123,9 @@ ftrace-size = <0x00>; }; - hvisor@480000 { + hvisor@280000 { no-map; - reg = <0x00 0x480000 0x00 0x400000>; + reg = <0x00 0x280000 0x00 0x400000>; }; nonroot { @@ -1165,47 +1247,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 +1346,8 @@ }; 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"; 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/loongarch64/ls3a5000/board.rs b/platform/loongarch64/ls3a5000/board.rs index 184dc110..0be34690 100644 --- a/platform/loongarch64/ls3a5000/board.rs +++ b/platform/loongarch64/ls3a5000/board.rs @@ -15,6 +15,7 @@ // Yulong Han // use crate::{arch::zone::HvArchZoneConfig, config::*}; +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), // 00:00.0 + pci_dev!(0x0, 0x0, 0x1), // 00:00.1 + pci_dev!(0x0, 0x0, 0x2), // 00:00.2 + pci_dev!(0x0, 0x0, 0x3), // 00:00.3 + pci_dev!(0x0, 0x4, 0x0), // 00:04.0 + pci_dev!(0x0, 0x4, 0x1), // 00:04.1 + pci_dev!(0x0, 0x5, 0x0), // 00:05.0 + pci_dev!(0x0, 0x5, 0x1), // 00:05.1 + pci_dev!(0x0, 0x6, 0x0), // 00:06.0 + pci_dev!(0x0, 0x6, 0x1), // 00:06.1 + pci_dev!(0x0, 0x6, 0x2), // 00:06.2 + pci_dev!(0x0, 0x7, 0x0), // 00:07.0 + pci_dev!(0x0, 0x8, 0x0), // 00:08.0 + pci_dev!(0x0, 0x9, 0x0), // 00:09.0 + pci_dev!(0x0, 0xa, 0x0), // 00:0a.0 + pci_dev!(0x0, 0xb, 0x0), // 00:0b.0 + pci_dev!(0x0, 0xc, 0x0), // 00:0c.0 + pci_dev!(0x0, 0xd, 0x0), // 00:0d.0 + pci_dev!(0x0, 0xf, 0x0), // 00:0f.0 + pci_dev!(0x0, 0x10, 0x0), // 00:10.0 + pci_dev!(0x0, 0x13, 0x0), // 00:13.0 + pci_dev!(0x0, 0x16, 0x0), // 00:16.0 + pci_dev!(0x0, 0x19, 0x0), // 00:19.0 + pci_dev!(0x2, 0x0, 0x0), // 02:00.0 + pci_dev!(0x5, 0x0, 0x0), // 05:00.0 + pci_dev!(0x6, 0x0, 0x0), // 06:00.0 ]; + + // bus << 8 | dev << 5 | func << 3 // pub const ROOT_PCI_DEVS: [u64; 0] = []; 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..37b555f5 100644 --- a/platform/loongarch64/ls3a6000/board.rs +++ b/platform/loongarch64/ls3a6000/board.rs @@ -15,6 +15,7 @@ // Yulong Han // use crate::{arch::zone::HvArchZoneConfig, config::*}; +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,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), // 00:00.0 + pci_dev!(0x0, 0x0, 0x1), // 00:00.1 + pci_dev!(0x0, 0x0, 0x2), // 00:00.2 + pci_dev!(0x0, 0x0, 0x3), // 00:00.3 + pci_dev!(0x0, 0x4, 0x0), // 00:04.0 + pci_dev!(0x0, 0x4, 0x1), // 00:04.1 + pci_dev!(0x0, 0x5, 0x0), // 00:05.0 + pci_dev!(0x0, 0x5, 0x1), // 00:05.1 + pci_dev!(0x0, 0x6, 0x0), // 00:06.0 + pci_dev!(0x0, 0x6, 0x1), // 00:06.1 + pci_dev!(0x0, 0x6, 0x2), // 00:06.2 + pci_dev!(0x0, 0x7, 0x0), // 00:07.0 + pci_dev!(0x0, 0x8, 0x0), // 00:08.0 + pci_dev!(0x0, 0x9, 0x0), // 00:09.0 + pci_dev!(0x0, 0xa, 0x0), // 00:0a.0 + pci_dev!(0x0, 0xb, 0x0), // 00:0b.0 + pci_dev!(0x0, 0xc, 0x0), // 00:0c.0 + pci_dev!(0x0, 0xd, 0x0), // 00:0d.0 + pci_dev!(0x0, 0xf, 0x0), // 00:0f.0 + pci_dev!(0x0, 0x10, 0x0), // 00:10.0 + pci_dev!(0x0, 0x13, 0x0), // 00:13.0 + pci_dev!(0x0, 0x16, 0x0), // 00:16.0 + pci_dev!(0x0, 0x19, 0x0), // 00:19.0 + pci_dev!(0x2, 0x0, 0x0), // 02:00.0 + pci_dev!(0x5, 0x0, 0x0), // 05:00.0 + pci_dev!(0x6, 0x0, 0x0), // 06:00.0 ]; + // bus << 8 | dev << 5 | func << 3 // pub const ROOT_PCI_DEVS: [u64; 0] = []; 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/src/arch/aarch64/iommu.rs b/src/arch/aarch64/iommu.rs index 6773d2f7..9b1658c9 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/loongarch64/zone.rs b/src/arch/loongarch64/zone.rs index 419b29ef..c5a1cde3 100644 --- a/src/arch/loongarch64/zone.rs +++ b/src/arch/loongarch64/zone.rs @@ -684,14 +684,6 @@ impl Zone { } pub fn arch_zone_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(()) } } diff --git a/src/config.rs b/src/config.rs index 7d72f45d..49dc9303 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,6 +14,7 @@ // Authors: // use alloc::vec::Vec; +use core::fmt::Debug; use spin::Once; use crate::{arch::zone::HvArchZoneConfig, platform}; @@ -22,11 +23,12 @@ 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)] @@ -91,9 +93,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 +116,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 +137,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, @@ -192,3 +197,28 @@ pub struct HvIvcConfig { pub interrupt_num: u32, pub max_peers: u32, } + +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct HvPciDevConfig { + pub bdf: u64, + pub vbdf: u64, +} + +#[macro_export] +macro_rules! pci_dev { + ($bus:expr, $dev:expr, $func:expr) => { + HvPciDevConfig { + bdf: ($bus << 8) | ($dev << 3) | ($func), + vbdf: ($bus << 8) | ($dev << 3) | ($func), + } + }; +} + +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); + let vbdf = crate::pci::pci_struct::Bdf::from_address(self.vbdf); + write!(f, "bdf {:#?} vbdf {:#?}", bdf, vbdf) + } +} diff --git a/src/main.rs b/src/main.rs index 10f9ae27..36bb145a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,6 +71,7 @@ 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}; +use pci::pci_config::hvisor_pci_init; use percpu::PerCpu; use zone::{add_zone, zone_create}; @@ -133,9 +134,13 @@ fn primary_init_early() { iommu_init(); + let root_config = root_zone_config(); + + let _ = hvisor_pci_init(&root_config.pci_config); + #[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 10196366..04c1fe14 100644 --- a/src/memory/mm.rs +++ b/src/memory/mm.rs @@ -142,6 +142,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/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..02c3259e 100644 --- a/src/pci/mod.rs +++ b/src/pci/mod.rs @@ -13,96 +13,13 @@ // // Authors: // -use spin::Once; +pub mod mem_alloc; +pub mod pci_access; +pub mod pci_config; +pub mod pci_mem; +pub mod pci_struct; +// pub mod vpci_dtb; -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 ac0d5ec7..00000000 --- a/src/pci/pci.rs +++ /dev/null @@ -1,445 +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 core::{panic, ptr, usize}; - -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 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, - 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 = *reg_ptr; - *reg_ptr = 0xffffffffu32; - let new_val = *reg_ptr; - ep.bars_init(bar_id, origin_val, new_val); - *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 = *reg_ptr; - *reg_ptr = 0xffffffffu32; - let new_val = *reg_ptr; - bridge.bars_init(bar_id, origin_val, new_val); - *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 _); - if alloc_pci_devs[idx] != 0 { - iommu_add_device(self.id, alloc_pci_devs[idx] as _); - } - } - - 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(); - } - - 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.start = cpu_base + region.start - pci_base; - region.start = align_down(region.start); - - info!( - "pci bar region: type: {:?}, base: {:#x}, size:{:#x}", - region.bar_type, region.start, region.size - ); - - self.gpm - .insert(MemoryRegion::new_with_offset_mapper( - region.start as GuestPhysAddr, - region.start, - region.size, - MemFlags::READ | MemFlags::WRITE | MemFlags::IO, - )) - .ok(); - } - } -} - -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 { - 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); - } - } 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..d80bf10e --- /dev/null +++ b/src/pci/pci_access.rs @@ -0,0 +1,1145 @@ +// 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::{ + fmt::Debug, + ops::{Index, IndexMut}, +}; + +use alloc::string::String; +use bit_field::BitField; +use bitflags::bitflags; +use core::slice; + +use crate::{ + error::HvResult, + memory::{GuestPhysAddr, HostPhysAddr, MMIOAccess, MemFlags, MemoryRegion}, + percpu::this_zone, + zone::Zone, +}; + +use super::{ + pci_mem::{PciRegion, PciRegionMmio}, + pci_struct::Bdf, + PciConfigAddress, +}; + +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) -> Self { + Self { + bar_type: PciMemType::Io, + virtual_value: 0, + value, + size: 0, + 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 => !(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 + ) + } + _ => { + 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(0x0c).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() { + 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 => { + let size = { + let _ = self.write_bar(slot, 0xfffffff0); + let mut readback = self.read_bar(slot).unwrap(); + let _ = self.write_bar(slot, readback 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 => { + 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, 0xfffffff0); + let _ = self.write_bar(slot + 1, 0xfffffff0); + let mut readback_low = self.read_bar(slot).unwrap(); + let readback_high = self.read_bar(slot + 1).unwrap(); + let _ = self.write_bar(slot, readback_low as u32); + let _ = self.write_bar(slot + 1, readback_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) + } + }; + 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 { + bararr[slot as usize] = PciMem::new_io(value 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(PciRegionMmio); + +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: PciRegionMmio) -> 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 enum EndpointField { + ID, + Command, + Status, + RevisionIDAndClassCode, + CacheLineSize, + LatencyTime, + HeaderType, + Bist, + Bar, + CardCisPointer, + SubsystemVendorId, + SubsystemId, + ExpansionRomBar, + CapabilityPointer, + InterruptLine, + InterruptPin, + MinGnt, + MaxLat, + Unknown(usize), +} + +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(PciRegionMmio); + +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: PciRegionMmio) -> 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 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(PciRegionMmio); + +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: PciRegionMmio) -> Self { + PciBridgeHeader(region) + } +} + +impl PciBridgeHeader {} + +pub fn mmio_vpci_handler(mmio: &mut MMIOAccess, _base: usize) -> HvResult { + let offset = (mmio.address & 0xfff) as PciConfigAddress; + let vbdf = Bdf::from_address(mmio.address as u64); + let size = mmio.size; + let value = mmio.value; + + let zone = this_zone(); + let mut guard = zone.write(); + let (vbus, gpm) = { + let Zone { gpm, vpci_bus, .. } = &mut *guard; + (vpci_bus, gpm) + }; + + let mut dev = None; + for node in vbus.devs().iter_mut() { + if node.1.get_vbdf() == vbdf { + debug!("vbdf find {:#?}", vbdf); + dev = Some(node.1); + break; + } + } + + if let Some(dev) = dev { + match dev.access(offset, size) { + false => { + debug!( + "hw vbdf {:#?} reg 0x{:x} try {} {}", + vbdf, + offset, + if mmio.is_write { "write" } else { "read" }, + if mmio.is_write { + format!(" 0x{:x}", mmio.value) + } else { + String::new() + } + ); + if mmio.is_write { + dev.write_hw(offset, size, value)?; + } else { + mmio.value = dev.read_hw(offset, size).unwrap(); + } + } + true => { + debug!( + "emu vbdf {:#?} reg 0x{:x} try {} {}", + vbdf, + offset, + if mmio.is_write { "write" } else { "read" }, + if mmio.is_write { + format!(" 0x{:x}", mmio.value) + } else { + String::new() + } + ); + 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 mmio.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) + { + 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); + } + + gpm.insert(MemoryRegion::new_with_offset_mapper( + new_vaddr as GuestPhysAddr, + paddr as HostPhysAddr, + bar.get_size() as _, + MemFlags::READ | MemFlags::WRITE, + ))?; + /* after update gpm, mem barrier is needed + * TODO: for loongarch64 need ibar 0 dbar 0 + */ + #[cfg(target_arch = "aarch64")] + unsafe { + core::arch::asm!("isb"); + core::arch::asm!("tlbi vmalls12e1is"); + core::arch::asm!("dsb nsh"); + } + } + } + } 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 mmio.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!("unhanled pci type {:#?}", dev.get_config_type()); + } + } + } + } + } else { + debug!("not found dev"); + /* if the dev is None, just return 0xFFFF_FFFF when read ID */ + if !mmio.is_write { + match EndpointField::from(offset as usize, size) { + EndpointField::ID => { + mmio.value = 0xFFFF_FFFF; + } + _ => { + warn!("unhandled pci mmio read"); + mmio.value = 0; + } + } + } + } + + debug!( + "vbdf {:#?} reg 0x{:x} {} 0x{:x}", + vbdf, + offset, + if mmio.is_write { "write" } else { "read" }, + mmio.value + ); + + Ok(()) +} diff --git a/src/pci/pci_config.rs b/src/pci/pci_config.rs new file mode 100644 index 00000000..8838a770 --- /dev/null +++ b/src/pci/pci_config.rs @@ -0,0 +1,116 @@ +// Copyright (c) 2025 Syswonder +// hvisor is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +// FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +// +// Syswonder Website: +// https://www.syswonder.org +// +// Authors: +// +use alloc::collections::btree_map::BTreeMap; +use spin::{Lazy, Mutex}; + +use crate::{ + config::{HvPciConfig, HvPciDevConfig, CONFIG_MAX_PCI_DEV, CONFIG_PCI_BUS_MAXNUM}, + error::HvResult, + pci::{ + mem_alloc::BaseAllocator, + pci_access::mmio_vpci_handler, + pci_struct::{Bdf, VirtualPciConfigSpace}, + }, + zone::Zone, +}; + +use super::pci_struct::RootComplex; + +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_rootcomplex_config: &[HvPciConfig; CONFIG_PCI_BUS_MAXNUM]) -> HvResult { + for rootcomplex_config in pci_rootcomplex_config { + /* empty config */ + if rootcomplex_config.ecam_base == 0 { + 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, + ); + + let mut rootcomplex = RootComplex::new(rootcomplex_config.ecam_base); + for mut node in rootcomplex.enumerate(None, Some(allocator)) { + node.capability_enumerate(); + 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, + alloc_pci_devs: &[HvPciDevConfig; CONFIG_MAX_PCI_DEV], + num_pci_devs: 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::from_address(dev_config.vbdf << 12); + if bdf.is_host_bridge() { + if let Some(mut vdev) = guard.get(&bdf) { + let mut vdev = vdev.clone(); + vdev.set_vbdf(vbdf); + self.vpci_bus.insert(vbdf, vdev); + } else { + warn!("can not find host bridge {:#?}", bdf); + } + } else { + if let Some(mut vdev) = guard.remove(&bdf) { + vdev.set_vbdf(vbdf); + self.vpci_bus.insert(vbdf, vdev); + } else { + warn!("can not find dev {:#?}", 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_bus: u64, + ) { + for rootcomplex_config in pci_rootcomplex_config { + /* empty config */ + if rootcomplex_config.ecam_base == 0 { + continue; + } + self.mmio_region_register( + rootcomplex_config.ecam_base as usize, + rootcomplex_config.ecam_size as usize, + mmio_vpci_handler, + 0, + ); + } + } +} diff --git a/src/pci/pci_mem.rs b/src/pci/pci_mem.rs new file mode 100644 index 00000000..e2240d3f --- /dev/null +++ b/src/pci/pci_mem.rs @@ -0,0 +1,70 @@ +// 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::PciConfigAddress; + +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; +} + +/* in aarch64, config space just like a normal mem space */ +#[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 } + } + /* TODO: may here need check whether length exceeds*/ + 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(()) + } +} diff --git a/src/pci/pci_struct.rs b/src/pci/pci_struct.rs new file mode 100644 index 00000000..9f8be11d --- /dev/null +++ b/src/pci/pci_struct.rs @@ -0,0 +1,1034 @@ +// 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::{cmp::Ordering, fmt::Debug, hint::spin_loop, ops::Range, str::FromStr}; + +use alloc::{collections::btree_map::BTreeMap, sync::Arc, vec::Vec}; +use bit_field::BitField; +use bitvec::{array::BitArray, order::Lsb0, BitArr}; + +use crate::{ + error::{HvErrorNum, HvResult}, + pci::pci_access::{Bar, PciBarRW, PciHeaderRW, PciRomRW}, +}; + +use super::{ + mem_alloc::BarAllocator, + pci_access::{ + EndpointField, EndpointHeader, HeaderType, PciBridgeHeader, PciCommand, PciConfigHeader, + PciMem, PciMemType, PciRW, + }, + pci_mem::{PciRegion, PciRegionMmio}, + PciConfigAddress, +}; + +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; +const BIT_LENTH: usize = 256 * 2; + +#[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, + } + } + + #[allow(dead_code)] + pub fn is_zero(&self) -> bool { + if self.bus == 0 && self.device == 0 && self.function == 0 { + return true; + } + false + } + + pub fn from_address(address: PciConfigAddress) -> Self { + let bdf = address >> 12; + let function = (bdf & 0b111) as u8; + let device = ((bdf >> 3) & 0b11111) as u8; + let bus = (bdf >> 8) as u8; + Self { + bus, + device, + function, + } + } + + pub fn to_address(&self, offset: usize) -> PciConfigAddress { + let mut address = offset as PciConfigAddress; + address.set_bits(12..15, self.function as u64); + address.set_bits(15..20, self.device as u64); + address.set_bits(20..28, self.bus as u64); + address + } + + pub fn is_host_bridge(&self) -> bool { + if (self.device, self.function) == (0, 0) { + true + } else { + false + } + } +} + +impl Ord for Bdf { + fn cmp(&self, other: &Self) -> Ordering { + self.to_address(0).cmp(&other.to_address(0)) + } +} + +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, + } + } +} + +/* 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, + } + } +} + +/* 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 { + bdf: Bdf, + vbdf: Bdf, + config_type: HeaderType, + + space: [u8; BIT_LENTH], + control: VirtualPciConfigControl, + access: VirtualPciAccessBits, + + backend: Arc, + + bararr: Bar, + rom: PciMem, + capabilities: PciCapabilityList, +} + +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 + } + + // 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 vbdf {:#?}\n type {:#?}\n {:#?}", + self.bdf, self.vbdf, self.config_type, self.bararr + ) + } +} + +impl VirtualPciConfigSpace { + pub fn endpoint(bdf: Bdf, backend: Arc, bararr: Bar, rom: PciMem) -> Self { + Self { + bdf, + vbdf: Bdf::default(), + space: [0u8; BIT_LENTH], + control: VirtualPciConfigControl::endpoint(), + access: VirtualPciAccessBits::endpoint(), + config_type: HeaderType::Endpoint, + backend, + bararr, + rom, + capabilities: PciCapabilityList::new(), + } + } + + pub fn bridge(bdf: Bdf, backend: Arc, bararr: Bar) -> Self { + Self { + bdf, + vbdf: Bdf::default(), + space: [0u8; BIT_LENTH], + control: VirtualPciConfigControl::bridge(), + access: VirtualPciAccessBits::bridge(), + config_type: HeaderType::PciBridge, + backend, + bararr, + rom: PciMem::default(), + capabilities: PciCapabilityList::new(), + } + } + + pub fn unknown(bdf: Bdf, backend: Arc) -> Self { + Self { + bdf, + vbdf: Bdf::default(), + space: [0u8; BIT_LENTH], + control: VirtualPciConfigControl::endpoint(), + access: VirtualPciAccessBits::endpoint(), + config_type: HeaderType::Endpoint, + backend, + bararr: Bar::default(), + rom: PciMem::default(), + capabilities: PciCapabilityList::new(), + } + } + + pub fn host_bridge(bdf: Bdf, backend: Arc) -> Self { + Self { + bdf: bdf, + vbdf: bdf, + space: [0u8; BIT_LENTH], + control: VirtualPciConfigControl::host_bridge(), + access: VirtualPciAccessBits::host_bridge(), + config_type: HeaderType::Endpoint, + backend, + bararr: Bar::default(), + rom: PciMem::default(), + capabilities: PciCapabilityList::new(), + } + } + + 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; + } + + /* 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[offset..offset + 4].copy_from_slice(&bytes); + } + match self.config_type { + HeaderType::Endpoint => { + let bytes = self.rom.get_value().to_le_bytes(); + self.space[0x30..0x34].copy_from_slice(&bytes); + } + HeaderType::PciBridge => { + let bytes = self.rom.get_value().to_le_bytes(); + self.space[0x38..0x3c].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[offset as usize..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[offset as usize..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 = &mut self.space[offset as usize..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") + } + } +} + +pub struct PciIterator { + allocator: Option, + stack: Vec, + segment: PciConfigAddress, + bus_max: u8, + function: u8, + is_mulitple_function: bool, + is_finish: bool, +} + +impl PciIterator { + fn address(&self) -> PciConfigAddress { + let parent = self.stack.last().unwrap(); + let bus = parent.secondary_bus; + let device = parent.device; + + let mut address: PciConfigAddress = 0; + address.set_bits(12..15, self.function as PciConfigAddress); + address.set_bits(15..20, device as PciConfigAddress); + address.set_bits(20..28, bus as PciConfigAddress); + address += self.segment; + address + } + + fn get_node(&mut self) -> Option { + let address = self.address(); + + let region = PciRegionMmio::new(address, CONFIG_LENTH); + let pci_header = PciConfigHeader::new_with_region(region); + let (vender_id, _device_id) = pci_header.id(); + if vender_id == 0xffff { + return None; + } + + self.is_mulitple_function = pci_header.has_multiple_functions(); + + match pci_header.header_type() { + HeaderType::Endpoint => { + let mut ep = EndpointHeader::new_with_region(region); + let rom = ep.parse_rom(); + + let bararr = + Self::bar_mem_init(ep.bar_limit().into(), &mut self.allocator, &mut ep); + + let ep = Arc::new(ep); + let bdf = Bdf::from_address(address); + Some(VirtualPciConfigSpace::endpoint(bdf, ep, bararr, rom)) + } + HeaderType::PciBridge => { + warn!("bridge"); + let mut bridge = PciBridgeHeader::new_with_region(region); + + let bararr = + Self::bar_mem_init(bridge.bar_limit().into(), &mut self.allocator, &mut bridge); + + let bridge = Arc::new(bridge); + let bdf = Bdf::from_address(address); + Some(VirtualPciConfigSpace::bridge(bdf, bridge, bararr)) + } + _ => { + warn!("unknown type"); + let pci_header = Arc::new(pci_header); + let bdf = Bdf::from_address(address); + Some(VirtualPciConfigSpace::unknown(bdf, pci_header)) + } + } + } + + 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); + } + _ => {} + } + i += 1; + } + } + bararr + } + + fn get_bridge(&self) -> Bridge { + let a = self.stack.last(); + match a { + Some(bridge) => bridge.clone(), + None => { + unreachable!("get null stack") + } + } + } + + fn is_next_function_max(&mut self) -> bool { + if self.is_mulitple_function { + if self.function == MAX_FUNCTION { + self.function = 0; + true + } else { + self.function += 1; + false + } + } else { + self.function = 0; + true + } + } + + fn next_device_not_ok(&mut self) -> bool { + if let Some(parent) = self.stack.last_mut() { + if parent.device == MAX_DEVICE { + if let Some(mut parent) = self.stack.pop() { + self.is_finish = parent.subordinate_bus == self.bus_max; + + parent.update_bridge_bus(); + self.function = 0; + 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()); + + self.function = 0; + return; + } + + if self.is_next_function_max() { + while self.next_device_not_ok() { + spin_loop(); + } + } + } +} + +impl Iterator for PciIterator { + type Item = VirtualPciConfigSpace; + + fn next(&mut self) -> Option { + while !self.is_finish { + if let Some(mut node) = self.get_node() { + node.space_init(); + self.next(match node.config_type { + HeaderType::PciBridge => Some(self.get_bridge().next_bridge(self.address())), + _ => None, + }); + return Some(node); + } else { + self.next(None); + } + } + None + } +} + +#[derive(Debug, Clone)] +pub struct Bridge { + device: u8, + subordinate_bus: u8, + secondary_bus: u8, + primary_bus: u8, + mmio: PciRegionMmio, +} + +impl Bridge { + pub fn host_bridge(address: PciConfigAddress) -> Self { + Self { + device: 0, + subordinate_bus: 0, + secondary_bus: 0, + primary_bus: 0, + mmio: PciRegionMmio::new(address, CONFIG_LENTH), + } + } + + pub fn next_bridge(&self, address: PciConfigAddress) -> Self { + let mmio = PciRegionMmio::new(address, CONFIG_LENTH); + Self { + device: 0, + subordinate_bus: self.subordinate_bus + 1, + secondary_bus: self.subordinate_bus + 1, + primary_bus: self.secondary_bus, + mmio, + } + } + + pub fn update_bridge_bus(&mut self) { + let mut value = self.mmio.read_u32(0x18).unwrap(); + 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()); + let _ = self.mmio.write_u32(0x18, 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, +} + +impl RootComplex { + pub fn new(mmio_base: PciConfigAddress) -> Self { + Self { mmio_base } + } + + 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::host_bridge(mmio_base)], + segment: mmio_base, + bus_max: (range.end - 1) as _, + function: 0, + is_mulitple_function: false, + is_finish: false, + } + } + + pub fn enumerate( + &mut self, + range: Option>, + bar_alloc: Option, + ) -> PciIterator { + self.__enumerate(range, bar_alloc) + } +} + +#[derive(Debug)] +pub struct VirtualRootComplex { + devs: BTreeMap, +} + +impl VirtualRootComplex { + pub fn new() -> Self { + Self { + devs: BTreeMap::new(), + } + } + + pub fn insert( + &mut self, + bdf: Bdf, + dev: VirtualPciConfigSpace, + ) -> Option { + 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) + } +} + +#[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, 2).unwrap().get_bits(16..32) as u16 + } +} + +impl Iterator for CapabilityIterator { + type Item = PciCapability; + + fn next(&mut self) -> Option { + while self.get_offset() != 0 { + let cap = PciCapability::from_address(self.get_offset(), self.get_id(), self.get_extension()); + // warn!("cap value {:x}", self.backend.read(self.offset, 4).unwrap()); + 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(_) => {} + _ => {} + } + capabilities.insert(capability.get_offset(), capability); + } + info!("capability {:#?}", capabilities); + self.capabilities = capabilities; + } +} diff --git a/src/pci/pci_test.rs b/src/pci/pci_test.rs new file mode 100644 index 00000000..f0ec3c22 --- /dev/null +++ b/src/pci/pci_test.rs @@ -0,0 +1,120 @@ +// 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 crate::{ + memory::MMIOAccess, + pci::{pci_access::EndpointHeader, pci_mem::PciRegionMmio}, + percpu::this_zone, +}; + +use super::pci_struct::CONFIG_LENTH; +use super::{ + mem_alloc::BaseAllocator, + pci_access::mmio_vpci_handler, + pci_struct::{Bdf, RootComplex, VirtualPciConfigSpace}, +}; + +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(); + // warn!("address {}", bdf.to_address(0)); + let backend = EndpointHeader::new_with_region(PciRegionMmio::new( + bdf.to_address(0) + 0x4010000000, + CONFIG_LENTH, + )); + let dev = VirtualPciConfigSpace::host_bridge(bdf, Arc::new(backend)); + 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 pcie_guest_test() { + let mut mmio = MMIOAccess { + address: Bdf::from_str("0000:00:01.0").unwrap().to_address(0x24) 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"); +} diff --git a/src/pci/pcibar.rs b/src/pci/pcibar.rs deleted file mode 100644 index fd42bbc8..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)] -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; - } -} 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/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 5e6483e1..74246829 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,8 +38,8 @@ pub struct Zone { pub cpu_set: CpuSet, pub irq_bitmap: [u32; 1024 / 32], pub gpm: MemorySet, - pub pciroot: PciRoot, pub is_err: bool, + pub vpci_bus: VirtualRootComplex, } impl Zone { @@ -52,8 +52,8 @@ impl Zone { cpu_set: CpuSet::new(MAX_CPU_NUM as usize, 0), mmio: Vec::new(), irq_bitmap: [0; 1024 / 32], - pciroot: PciRoot::new(), is_err: false, + vpci_bus: VirtualRootComplex::new(), } } @@ -204,7 +204,9 @@ pub fn zone_create(config: &HvZoneConfig) -> HvResult>> { zone.pt_init(config.memory_regions()).unwrap(); zone.mmio_init(&config.arch_config); - zone.arch_zone_configuration(config)?; + let _ = zone.virtual_pci_mmio_init(&config.pci_config, config.num_pci_bus); + let _ = zone.guest_pci_init(&config.alloc_pci_devs, config.num_pci_devs); + // #[cfg(target_arch = "aarch64")] // zone.ivc_init(config.ivc_config()); @@ -212,16 +214,10 @@ pub fn zone_create(config: &HvZoneConfig) -> HvResult>> { /* 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.ecam_base as _, - // config.pci_config.ecam_size as _, + // config.pci_config[0].ecam_base as _, + // config.pci_config[0].ecam_size as _, // )?; - zone.pci_init( - &config.pci_config, - config.num_pci_devs as _, - &config.alloc_pci_devs, - ); - let mut cpu_num = 0; for cpu_id in config.cpus().iter() {