Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7107c03488 |
Generated
+1
-269
@@ -38,12 +38,6 @@ dependencies = [
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "accessor"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd8b2abd55bf1f9cffbf00fd594566c51a9d31402553284920c1309ca8351086"
|
||||
|
||||
[[package]]
|
||||
name = "acpi"
|
||||
version = "4.1.1"
|
||||
@@ -649,7 +643,7 @@ checksum = "581d3afdd654deb68c19fcbe4bc411910cc64067d4a13d8637bda7722cb9c2ea"
|
||||
dependencies = [
|
||||
"endian-type-rs",
|
||||
"fallible-iterator",
|
||||
"memoffset 0.5.6",
|
||||
"memoffset",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"rustc_version 0.2.3",
|
||||
@@ -1419,15 +1413,6 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.4"
|
||||
@@ -1512,12 +1497,6 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
@@ -2561,18 +2540,6 @@ dependencies = [
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xhci"
|
||||
version = "0.9.2"
|
||||
source = "git+https://github.com/rust-osdev/xhci.git#f2254c86d5ba12754259bf0ae483c6c8dc821aad"
|
||||
dependencies = [
|
||||
"accessor",
|
||||
"bit_field",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.1.0"
|
||||
@@ -2618,90 +2585,6 @@ dependencies = [
|
||||
"rsdp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_ahci"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"futures-util",
|
||||
"kernel-fs",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"memoffset 0.9.1",
|
||||
"static_assertions",
|
||||
"tock-registers",
|
||||
"ygg_driver_pci",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_bsp_arm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_bsp_bcm283x"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"futures-util",
|
||||
"kernel-arch-aarch64",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_bsp_jh7110"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"futures-util",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_bsp_riscv"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"kernel-arch-riscv64",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_fat32"
|
||||
version = "0.1.0"
|
||||
@@ -2744,21 +2627,6 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_net_igbe"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"device-api",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"ygg_driver_net_core",
|
||||
"ygg_driver_pci",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_net_loopback"
|
||||
version = "0.1.0"
|
||||
@@ -2772,22 +2640,6 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_net_rtl81xx"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"device-api",
|
||||
"futures-util",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"ygg_driver_net_core",
|
||||
"ygg_driver_pci",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_net_stmmac"
|
||||
version = "0.1.0"
|
||||
@@ -2805,26 +2657,6 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_nvme"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"futures-util",
|
||||
"kernel-arch",
|
||||
"kernel-fs",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"static_assertions",
|
||||
"tock-registers",
|
||||
"ygg_driver_pci",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_pci"
|
||||
version = "0.1.0"
|
||||
@@ -2872,94 +2704,6 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_usb_xhci"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"atomic_enum",
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"futures-util",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"xhci",
|
||||
"ygg_driver_pci",
|
||||
"ygg_driver_usb",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_virtio_blk"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"ygg_driver_pci",
|
||||
"ygg_driver_virtio_core",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_virtio_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"device-api",
|
||||
"kernel-arch-hosted",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"ygg_driver_pci",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_virtio_gpu"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"ygg_driver_pci",
|
||||
"ygg_driver_virtio_core",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_virtio_net"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"futures-util",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"ygg_driver_net_core",
|
||||
"ygg_driver_pci",
|
||||
"ygg_driver_virtio_core",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yggdrasil-abi"
|
||||
version = "0.1.0"
|
||||
@@ -3014,25 +2758,13 @@ dependencies = [
|
||||
"vmalloc",
|
||||
"yboot-proto",
|
||||
"ygg_driver_acpi",
|
||||
"ygg_driver_ahci",
|
||||
"ygg_driver_bsp_arm",
|
||||
"ygg_driver_bsp_bcm283x",
|
||||
"ygg_driver_bsp_jh7110",
|
||||
"ygg_driver_bsp_riscv",
|
||||
"ygg_driver_fat32",
|
||||
"ygg_driver_input",
|
||||
"ygg_driver_net_core",
|
||||
"ygg_driver_net_igbe",
|
||||
"ygg_driver_net_loopback",
|
||||
"ygg_driver_net_rtl81xx",
|
||||
"ygg_driver_net_stmmac",
|
||||
"ygg_driver_nvme",
|
||||
"ygg_driver_pci",
|
||||
"ygg_driver_usb",
|
||||
"ygg_driver_usb_xhci",
|
||||
"ygg_driver_virtio_blk",
|
||||
"ygg_driver_virtio_gpu",
|
||||
"ygg_driver_virtio_net",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
|
||||
@@ -43,56 +43,3 @@ $ booti ${loadaddr} ${initrd_addr_r}:<SIZE-PRINTED-WHEN-LOADING-INITRD> ${fdt_ad
|
||||
###### (FIXME when initrd gets larger than 64MiB)
|
||||
|
||||
env set ipaddr 13.0.0.2; env set fdt_addr_r 0x11000000; env set initrd_addr_r 0x04000000; tftpboot ${initrd_addr_r} 13.0.0.1:initrd.tar; tftpboot ${loadaddr} 13.0.0.1:kernel.bin; load mmc 0:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:67108864 ${fdt_addr_r}
|
||||
|
||||
dhcp;
|
||||
env set initrd_addr_r 0x20000000; tftpboot ${initrd_addr_r} 192.168.88.10:initrd.img; tftpboot ${loadaddr} 192.168.88.10:yggdrasil-kernel.bin; load mmc 1:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:0x4000000 ${fdt_addr_r}
|
||||
|
||||
|
||||
Missing drivers:
|
||||
|
||||
No driver for Some("hvs@7e400000") ("brcm,bcm2711-hvs")
|
||||
No driver for Some("i2c@7e804000") ("brcm,bcm2711-i2c")
|
||||
also "brcm,bcm2835-i2c"
|
||||
No driver for Some("usb@7e980000") ("brcm,bcm2835-usb")
|
||||
No driver for Some("local_intc@40000000") ("brcm,bcm2836-l1-intc")
|
||||
: avs-monitor@7d5d2000: probed
|
||||
No driver for Some("thermal") ("brcm,bcm2711-thermal")
|
||||
No driver for Some("dma@7e007000") ("brcm,bcm2835-dma")
|
||||
No driver for Some("watchdog@7e100000") ("brcm,bcm2835-pm")
|
||||
also "brcm,bcm2835-pm-wdt"
|
||||
No driver for Some("rng@7e104000") ("brcm,bcm2711-rng200")
|
||||
No driver for Some("pixelvalve@7e206000") ("brcm,bcm2711-pixelvalve0")
|
||||
No driver for Some("pixelvalve@7e207000") ("brcm,bcm2711-pixelvalve1")
|
||||
No driver for Some("pixelvalve@7e20a000") ("brcm,bcm2711-pixelvalve2")
|
||||
No driver for Some("pwm@7e20c800") ("brcm,bcm2835-pwm")
|
||||
No driver for Some("pixelvalve@7e216000") ("brcm,bcm2711-pixelvalve4")
|
||||
No driver for Some("clock@7ef00000") ("brcm,brcm2711-dvp")
|
||||
No driver for Some("interrupt-controller@7ef00100") ("brcm,bcm2711-l2-intc")
|
||||
also "brcm,l2-intc"
|
||||
No driver for Some("hdmi@7ef00700") ("brcm,bcm2711-hdmi0")
|
||||
No driver for Some("i2c@7ef04500") ("brcm,bcm2711-hdmi-i2c")
|
||||
No driver for Some("hdmi@7ef05700") ("brcm,bcm2711-hdmi1")
|
||||
No driver for Some("i2c@7ef09500") ("brcm,bcm2711-hdmi-i2c")
|
||||
No driver for Some("firmware") ("raspberrypi,bcm2835-firmware")
|
||||
also "simple-mfd"
|
||||
No driver for Some("clocks") ("raspberrypi,firmware-clocks")
|
||||
No driver for Some("gpio") ("raspberrypi,firmware-gpio")
|
||||
No driver for Some("reset") ("raspberrypi,firmware-reset")
|
||||
No driver for Some("power") ("raspberrypi,bcm2835-power")
|
||||
No driver for Some("mailbox@7e00b840") ("brcm,bcm2835-vchiq")
|
||||
No driver for Some("phy") ("usb-nop-xceiv")
|
||||
No driver for Some("gpu") ("brcm,bcm2711-vc5")
|
||||
No driver for Some("mmc@7e340000") ("brcm,bcm2711-emmc2")
|
||||
No driver for Some("arm-pmu") ("arm,cortex-a72-pmu")
|
||||
also "arm,armv8-pmuv3"
|
||||
No driver for Some("cpu@0") ("arm,cortex-a72")
|
||||
No driver for Some("cpu@1") ("arm,cortex-a72")
|
||||
No driver for Some("cpu@2") ("arm,cortex-a72")
|
||||
No driver for Some("cpu@3") ("arm,cortex-a72")
|
||||
No driver for Some("pcie@7d500000") ("brcm,bcm2711-pcie")
|
||||
No driver for Some("ethernet@7d580000") ("brcm,bcm2711-genet-v5")
|
||||
No driver for Some("mdio@e14") ("brcm,genet-mdio-v5")
|
||||
No driver for Some("leds") ("gpio-leds")
|
||||
No driver for Some("wifi-pwrseq") ("mmc-pwrseq-simple")
|
||||
No driver for Some("sd_io_1v8_reg") ("regulator-gpio")
|
||||
No driver for Some("sd_vcc_reg") ("regulator-fixed")
|
||||
|
||||
@@ -37,94 +37,3 @@ env set ipaddr 13.0.0.2; env set initrd_addr_r 0x70000000; tftpboot ${initrd_add
|
||||
|
||||
dhcp
|
||||
dhcp; env set initrd_addr_r 0x70000000; tftpboot ${initrd_addr_r} 192.168.88.10:initrd.img; tftpboot ${loadaddr} 192.168.88.10:yggdrasil-kernel.bin; load mmc 1:3 ${fdt_addr_r} dtbs/6.6.20-starfive/starfive/${fdtfile}; fdt resize; booti ${loadaddr} ${initrd_addr_r}:60000000 ${fdt_addr_r}
|
||||
|
||||
|
||||
Missing drivers:
|
||||
|
||||
Clock/reset/pin:
|
||||
No driver for Some("pinctrl@17020000") ("starfive,jh7110-aon-pinctrl")
|
||||
No driver for Some("clock-controller@19810000") ("starfive,jh7110-ispcrg")
|
||||
No driver for Some("clock-controller@295c0000") ("starfive,jh7110-voutcrg")
|
||||
|
||||
Power/reg/GPIO:
|
||||
No driver for Some("opp-table-0") ("operating-points-v2")
|
||||
No driver for Some("pmic@36") ("x-powers,axp15060")
|
||||
No driver for Some("power-controller@17030000") ("starfive,jh7110-pmu")
|
||||
No driver for Some("leds") ("gpio-leds")
|
||||
No driver for Some("gpio-restart") ("gpio-restart")
|
||||
|
||||
Serial:
|
||||
No driver for Some("i2c@10030000") ("snps,designware-i2c")
|
||||
No driver for Some("i2c@10050000") ("snps,designware-i2c")
|
||||
No driver for Some("i2c@12050000") ("snps,designware-i2c")
|
||||
No driver for Some("i2c@12060000") ("snps,designware-i2c")
|
||||
No driver for Some("spi@10060000") ("arm,pl022")
|
||||
also "arm,primecell"
|
||||
|
||||
Bus:
|
||||
No driver for Some("usb@10100000") ("starfive,jh7110-usb")
|
||||
No driver for Some("usb@0") ("cdns,usb3")
|
||||
No driver for Some("phy@10200000") ("starfive,jh7110-usb-phy")
|
||||
No driver for Some("phy@10210000") ("starfive,jh7110-pcie-phy")
|
||||
No driver for Some("phy@10220000") ("starfive,jh7110-pcie-phy")
|
||||
No driver for Some("pcie@940000000") ("starfive,jh7110-pcie")
|
||||
No driver for Some("pcie@9c0000000") ("starfive,jh7110-pcie")
|
||||
|
||||
Interrupt:
|
||||
No driver for Some("interrupt-controller") ("riscv,cpu-intc")
|
||||
No driver for Some("timer@2000000") ("starfive,jh7110-clint")
|
||||
also "sifive,clint0"
|
||||
|
||||
Display/GPU subsystem:
|
||||
No driver for Some("display-subsystem") ("starfive,jh7110-display")
|
||||
also "verisilicon,display-subsystem"
|
||||
No driver for Some("dsi-output") ("starfive,jh7110-display-encoder")
|
||||
also "verisilicon,dsi-encoder"
|
||||
No driver for Some("jpu@13090000") ("starfive,jpu")
|
||||
No driver for Some("vpu_dec@130a0000") ("starfive,vdec")
|
||||
No driver for Some("vpu_enc@130b0000") ("starfive,venc")
|
||||
No driver for Some("gpu@18000000") ("img-gpu")
|
||||
No driver for Some("vin_sysctl@19800000") ("starfive,jh7110-vin")
|
||||
No driver for Some("phy@19820000") ("starfive,jh7110-dphy-rx")
|
||||
No driver for Some("dc8200@29400000") ("starfive,jh7110-dc8200")
|
||||
also "verisilicon,dc8200"
|
||||
No driver for Some("hdmi@29590000") ("starfive,jh7110-hdmi")
|
||||
also "inno,hdmi"
|
||||
No driver for Some("mipi@295d0000") ("starfive,jh7110-mipi_dsi")
|
||||
also "cdns,dsi"
|
||||
No driver for Some("mipi-dphy@295e0000") ("starfive,jh7110-mipi-dphy-tx")
|
||||
also "m31,mipi-dphy-tx"
|
||||
|
||||
Misc:
|
||||
No driver for Some("mailbox_client") ("starfive,mailbox-test")
|
||||
No driver for Some("cache-controller@2010000") ("starfive,jh7110-ccache")
|
||||
also "sifive,ccache0"
|
||||
also "cache"
|
||||
No driver for Some("pwm@120d0000") ("starfive,jh7110-pwm")
|
||||
also "opencores,pwm-v1"
|
||||
No driver for Some("temperature-sensor@120e0000") ("starfive,jh7110-temp")
|
||||
No driver for Some("timer@13050000") ("starfive,jh7110-timer")
|
||||
No driver for Some("mailbox@13060000") ("starfive,mail_box")
|
||||
No driver for Some("watchdog@13070000") ("starfive,jh7110-wdt")
|
||||
No driver for Some("crypto@16000000") ("starfive,jh7110-crypto")
|
||||
No driver for Some("rng@1600c000") ("starfive,jh7110-trng")
|
||||
No driver for Some("mdio") ("snps,dwmac-mdio")
|
||||
No driver for Some("mdio") ("snps,dwmac-mdio")
|
||||
No driver for Some("dma-controller@16050000") ("starfive,jh7110-axi-dma")
|
||||
No driver for Some("dma-controller@16008000") ("arm,pl080")
|
||||
also "arm,primecell"
|
||||
No driver for Some("rtc@17040000") ("starfive,jh7110-rtc")
|
||||
No driver for Some("e24@6e210000") ("starfive,e24")
|
||||
No driver for Some("linux,cma") ("shared-dma-pool")
|
||||
|
||||
Storage:
|
||||
No driver for Some("spi@13010000") ("starfive,jh7110-qspi")
|
||||
also "cdns,qspi-nor"
|
||||
No driver for Some("flash@0") ("jedec,spi-nor")
|
||||
No driver for Some("partitions") ("fixed-partitions")
|
||||
No driver for Some("mmc@16010000") ("starfive,jh7110-mmc")
|
||||
No driver for Some("mmc@16020000") ("starfive,jh7110-mmc")
|
||||
|
||||
Audio:
|
||||
No driver for Some("pwmdac@100b0000") ("starfive,jh7110-pwmdac")
|
||||
No driver for Some("i2s@120b0000") ("starfive,jh7110-i2stx0")
|
||||
|
||||
+9
-18
@@ -23,18 +23,18 @@ vmalloc.workspace = true
|
||||
kernel-arch.workspace = true
|
||||
|
||||
# Drivers
|
||||
ygg_driver_input = { path = "driver/input" }
|
||||
ygg_driver_pci = { path = "driver/bus/pci" }
|
||||
ygg_driver_usb = { path = "driver/bus/usb" }
|
||||
ygg_driver_net_core = { path = "driver/net/core" }
|
||||
ygg_driver_net_loopback = { path = "driver/net/loopback" }
|
||||
ygg_driver_virtio_net = { path = "driver/virtio/net", features = ["pci"] }
|
||||
ygg_driver_virtio_gpu = { path = "driver/virtio/gpu", features = ["pci"] }
|
||||
ygg_driver_virtio_blk = { path = "driver/virtio/blk", features = ["pci"] }
|
||||
ygg_driver_nvme = { path = "driver/block/nvme" }
|
||||
ygg_driver_ahci = { path = "driver/block/ahci" }
|
||||
ygg_driver_input = { path = "driver/input" }
|
||||
ygg_driver_usb_xhci.path = "driver/usb/xhci"
|
||||
ygg_driver_net_rtl81xx.path = "driver/net/rtl81xx"
|
||||
# ygg_driver_virtio_net = { path = "driver/virtio/net", features = ["pci"] }
|
||||
# ygg_driver_virtio_gpu = { path = "driver/virtio/gpu", features = ["pci"] }
|
||||
# ygg_driver_virtio_blk = { path = "driver/virtio/blk", features = ["pci"] }
|
||||
# ygg_driver_nvme = { path = "driver/block/nvme" }
|
||||
# ygg_driver_ahci = { path = "driver/block/ahci" }
|
||||
# ygg_driver_usb_xhci.path = "driver/usb/xhci"
|
||||
# ygg_driver_net_rtl81xx.path = "driver/net/rtl81xx"
|
||||
|
||||
memfs = { path = "driver/fs/memfs" }
|
||||
ext2 = { path = "driver/fs/ext2" }
|
||||
@@ -56,16 +56,11 @@ git-version = "0.3.9"
|
||||
aarch64-cpu.workspace = true
|
||||
device-tree.workspace = true
|
||||
kernel-arch-aarch64.workspace = true
|
||||
ygg_driver_bsp_arm.path = "driver/bsp/arm"
|
||||
ygg_driver_bsp_bcm283x.path = "driver/bsp/bcm283x"
|
||||
|
||||
[target.'cfg(target_arch = "riscv64")'.dependencies]
|
||||
device-tree.workspace = true
|
||||
kernel-arch-riscv64.workspace = true
|
||||
ygg_driver_bsp_arm.path = "driver/bsp/arm" # PrimeCell components
|
||||
ygg_driver_bsp_riscv.path = "driver/bsp/riscv"
|
||||
ygg_driver_net_stmmac.path = "driver/net/stmmac"
|
||||
ygg_driver_bsp_jh7110.path = "driver/bsp/jh7110"
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||
yboot-proto.workspace = true
|
||||
@@ -73,7 +68,7 @@ kernel-arch-x86_64.workspace = true
|
||||
kernel-arch-x86.workspace = true
|
||||
|
||||
ygg_driver_acpi.path = "driver/acpi"
|
||||
ygg_driver_net_igbe.path = "driver/net/igbe"
|
||||
# ygg_driver_net_igbe.path = "driver/net/igbe"
|
||||
|
||||
acpi.workspace = true
|
||||
|
||||
@@ -92,11 +87,7 @@ kernel-arch-aarch64.workspace = true
|
||||
kernel-arch-riscv64.workspace = true
|
||||
|
||||
ygg_driver_acpi.path = "driver/acpi"
|
||||
ygg_driver_bsp_arm.path = "driver/bsp/arm"
|
||||
ygg_driver_bsp_riscv.path = "driver/bsp/riscv"
|
||||
ygg_driver_net_stmmac.path = "driver/net/stmmac"
|
||||
ygg_driver_bsp_bcm283x.path = "driver/bsp/bcm283x"
|
||||
ygg_driver_bsp_jh7110.path = "driver/bsp/jh7110"
|
||||
|
||||
[features]
|
||||
default = ["fb_console"]
|
||||
|
||||
@@ -44,7 +44,6 @@ pub struct PerCpuData {
|
||||
}
|
||||
|
||||
pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(1);
|
||||
pub static mut BOOT_HART_ID: u64 = 0;
|
||||
static IPI_QUEUES: OneTimeInit<Vec<IpiQueue<ArchitectureImpl>>> = OneTimeInit::new();
|
||||
static HART_TO_QUEUE: IrqSafeSpinlock<ArchitectureImpl, BTreeMap<u32, usize>> =
|
||||
IrqSafeSpinlock::new(BTreeMap::new());
|
||||
@@ -61,11 +60,6 @@ impl CpuData for PerCpuData {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the ID of the bootstrap HART
|
||||
pub fn boot_hart_id() -> u64 {
|
||||
unsafe { BOOT_HART_ID }
|
||||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn idle_task(_: usize) -> ! {
|
||||
core::arch::naked_asm!("1: nop; j 1b");
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
[package]
|
||||
name = "ygg_driver_bsp_arm"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
device-tree.workspace = true
|
||||
device-api.workspace = true
|
||||
yggdrasil-abi.workspace = true
|
||||
libk-mm.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk.workspace = true
|
||||
|
||||
tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
bytemuck.workspace = true
|
||||
@@ -1,7 +0,0 @@
|
||||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod pl011;
|
||||
mod pl061;
|
||||
@@ -1,233 +0,0 @@
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::{
|
||||
GpioController, GpioInterruptEvent, GpioInterruptMode, GpioPinLevel, PinHandle,
|
||||
SinglePinDirection,
|
||||
},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
};
|
||||
use device_tree::{
|
||||
driver::{
|
||||
device_tree_driver, util::generic_gpio_config, DeviceTreeGpioPins, DeviceTreePinController,
|
||||
Node, ProbeContext,
|
||||
},
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
use libk::{device::external_interrupt_controller, event::signal_gpio_event};
|
||||
use libk_mm::device::DeviceMemoryIo;
|
||||
use libk_util::{bit::BitField, sync::IrqSafeSpinlock};
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x0000 => GPIODATA: [ReadWrite<u32>; 256]),
|
||||
(0x0400 => GPIODIR: ReadWrite<u32>),
|
||||
(0x0404 => GPIOIS: ReadWrite<u32>),
|
||||
(0x0408 => GPIOIBE: ReadWrite<u32>),
|
||||
(0x040C => GPIOIEV: ReadWrite<u32>),
|
||||
(0x0410 => GPIOIE: ReadWrite<u32>),
|
||||
(0x0414 => GPIORIS: ReadOnly<u32>),
|
||||
(0x0418 => GPIOMIS: ReadOnly<u32>),
|
||||
(0x041C => GPIOIC: WriteOnly<u32>),
|
||||
(0x0420 => GPIOAFSEL: ReadWrite<u32>),
|
||||
(0x0424 => _0),
|
||||
(0x0FE0 => GPIOPERIPHID: [ReadOnly<u32>; 4]),
|
||||
(0x0FF0 => GPIOPCELLID: [ReadOnly<u32>; 4]),
|
||||
(0x1000 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct Pl061 {
|
||||
#[allow(unused)]
|
||||
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
irq: FullIrq,
|
||||
clocks: Vec<ClockHandle>,
|
||||
resets: Vec<ResetHandle>,
|
||||
gpio_events: [AtomicU64; 8],
|
||||
}
|
||||
|
||||
impl Device for Pl061 {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
for clock in self.clocks.iter() {
|
||||
clock.enable()?;
|
||||
}
|
||||
for reset in self.resets.iter() {
|
||||
reset.deassert()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
let intc = external_interrupt_controller()?;
|
||||
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
|
||||
intc.enable_irq(self.irq.irq)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"PL061 GPIO Controller"
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for Pl061 {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
let status = {
|
||||
let lock = self.regs.lock();
|
||||
let val = lock.GPIOMIS.get();
|
||||
lock.GPIOIC.set(0xFF);
|
||||
val
|
||||
};
|
||||
|
||||
for bit in 0..8 {
|
||||
let ev = self.gpio_events[bit].load(Ordering::Acquire);
|
||||
if ev != 0 {
|
||||
signal_gpio_event(ev);
|
||||
}
|
||||
}
|
||||
|
||||
status != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl GpioController for Pl061 {
|
||||
fn write_gpio(&self, _pin: &PinHandle, _value: bool) -> Result<(), Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn setup_gpio(&self, pin: &PinHandle, event: Option<GpioInterruptEvent>) -> Result<(), Error> {
|
||||
let regs = self.regs.lock();
|
||||
|
||||
let direction = pin
|
||||
.config
|
||||
.force_single_direction()
|
||||
.ok_or(Error::InvalidArgument)
|
||||
.inspect_err(|_| {
|
||||
log::warn!(
|
||||
"pl061: gpio #{} has invalid direction input={}, output={}",
|
||||
pin.index,
|
||||
pin.config.input,
|
||||
pin.config.output
|
||||
)
|
||||
})?;
|
||||
|
||||
// Enable software control
|
||||
regs.GPIOAFSEL.set(regs.GPIOAFSEL.get() & !(1 << pin.index));
|
||||
|
||||
match direction {
|
||||
SinglePinDirection::Output => {
|
||||
log::info!("pl061: gpio #{} set as output", pin.index);
|
||||
regs.GPIODIR.set(regs.GPIODIR.get() | (1 << pin.index));
|
||||
// Disable interrupt
|
||||
regs.GPIOIE.set(regs.GPIOIE.get() & !(1 << pin.index));
|
||||
|
||||
let level = match pin.config.initial_level {
|
||||
GpioPinLevel::Low => 0,
|
||||
GpioPinLevel::High => 1,
|
||||
};
|
||||
let mut val = regs.GPIODATA[0].get();
|
||||
val &= !(1 << pin.index);
|
||||
val |= level << pin.index;
|
||||
regs.GPIODATA[0].set(val);
|
||||
}
|
||||
SinglePinDirection::Input => {
|
||||
log::info!("pl061: gpio #{} set as input", pin.index);
|
||||
regs.GPIODIR.set(regs.GPIODIR.get() & !(1 << pin.index));
|
||||
|
||||
if let Some(event) = event {
|
||||
let ibe = event.mode == GpioInterruptMode::BothEdges;
|
||||
let is = event.mode == GpioInterruptMode::HighLevel
|
||||
|| event.mode == GpioInterruptMode::LowLevel;
|
||||
let iev = event.mode == GpioInterruptMode::HighLevel
|
||||
|| event.mode == GpioInterruptMode::RisingEdge;
|
||||
|
||||
self.gpio_events[pin.index as usize].store(event.event, Ordering::Release);
|
||||
|
||||
regs.GPIOIS
|
||||
.set(regs.GPIOIS.get().modify_bit(pin.index as usize, is));
|
||||
regs.GPIOIBE
|
||||
.set(regs.GPIOIBE.get().modify_bit(pin.index as usize, ibe));
|
||||
regs.GPIOIEV
|
||||
.set(regs.GPIOIEV.get().modify_bit(pin.index as usize, iev));
|
||||
regs.GPIOIE.set(regs.GPIOIE.get() | (1 << pin.index));
|
||||
regs.GPIOIC.set(1 << pin.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTreePinController for Pl061 {
|
||||
fn configure_pin_group(self: Arc<Self>, _pins: &Arc<Node>) -> Result<(), Error> {
|
||||
// TODO implement this when I get some board with this pinctrl
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn map_gpio(
|
||||
self: Arc<Self>,
|
||||
property: &TProp,
|
||||
offset: usize,
|
||||
info: &DeviceTreeGpioPins,
|
||||
) -> Option<(PinHandle, usize)> {
|
||||
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
|
||||
let config = generic_gpio_config(options as u32, info);
|
||||
Some((
|
||||
PinHandle {
|
||||
index: pin as u32,
|
||||
config,
|
||||
parent: self,
|
||||
},
|
||||
2,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["arm,pl061"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let clocks = if let Some(clocks) = node.clocks() {
|
||||
clocks.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let resets = if let Some(resets) = node.resets() {
|
||||
resets.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let irq = node.interrupt(0)?;
|
||||
|
||||
let base = node.map_base(context, 0)?;
|
||||
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
|
||||
|
||||
let pinctrl = Arc::new(Pl061 {
|
||||
regs: IrqSafeSpinlock::new(regs),
|
||||
irq,
|
||||
clocks,
|
||||
resets,
|
||||
gpio_events: [const { AtomicU64::new(0) }; 8]
|
||||
});
|
||||
|
||||
node.make_pin_controller(pinctrl.clone());
|
||||
|
||||
Some(pinctrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
[package]
|
||||
name = "ygg_driver_bsp_bcm283x"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
device-tree.workspace = true
|
||||
device-api.workspace = true
|
||||
yggdrasil-abi.workspace = true
|
||||
libk-mm.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk.workspace = true
|
||||
kernel-arch-aarch64.workspace = true
|
||||
|
||||
tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
bytemuck.workspace = true
|
||||
futures-util.workspace = true
|
||||
@@ -1,252 +0,0 @@
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::{GpioController, GpioInterruptEvent, PinHandle},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
};
|
||||
use device_tree::{
|
||||
driver::{
|
||||
device_tree_driver, util::generic_gpio_config, DeviceTreeGpioPins, DeviceTreePinController,
|
||||
Node, ProbeContext,
|
||||
},
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
use libk::device::external_interrupt_controller;
|
||||
use libk_mm::device::DeviceMemoryIo;
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
const PUPD_NONE: u32 = 0b00;
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
// Pin function select
|
||||
(0x00 => GPFSEL: [ReadWrite<u32>; 6]),
|
||||
(0x18 => _0),
|
||||
// Set pin
|
||||
(0x1C => GPSET: [WriteOnly<u32>; 2]),
|
||||
(0x24 => _1),
|
||||
// Clear pin
|
||||
(0x28 => GPCLR: [WriteOnly<u32>; 2]),
|
||||
(0x30 => _2),
|
||||
// Current pin level
|
||||
(0x34 => GPLEV: [ReadOnly<u32>; 2]),
|
||||
(0x3C => _3),
|
||||
// Pin event detect status
|
||||
(0x40 => GPEDS: [ReadWrite<u32>; 2]),
|
||||
(0x48 => _4),
|
||||
// Pin rising edge event enable
|
||||
(0x4C => GPREN: [ReadWrite<u32>; 2]),
|
||||
(0x54 => _5),
|
||||
// Pin falling edge event enable
|
||||
(0x58 => GPFEN: [ReadWrite<u32>; 2]),
|
||||
(0x60 => _6),
|
||||
// Pin high event enable
|
||||
(0x64 => GPHEN: [ReadWrite<u32>; 2]),
|
||||
(0x6C => _7),
|
||||
// Pin low event enable
|
||||
(0x70 => GPLEN: [ReadWrite<u32>; 2]),
|
||||
(0x78 => _8),
|
||||
// Pin async rising edge event enable
|
||||
(0x7C => GPAREN: [ReadWrite<u32>; 2]),
|
||||
(0x84 => _9),
|
||||
// Pin async falling edge event enable
|
||||
(0x88 => GPAFEN: [ReadWrite<u32>; 2]),
|
||||
(0x90 => _10),
|
||||
// Pin pull up/down control
|
||||
(0xE4 => GPIO_PUP_PDN_CNTRL_REG: [ReadWrite<u32>; 4]),
|
||||
(0xF4 => _11),
|
||||
(0x100 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct Bcm2711Gpio {
|
||||
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
#[allow(unused)]
|
||||
irqs: [FullIrq; 2],
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
fn write_fsel(&self, pin: usize, value: u32) {
|
||||
let fsel_reg = pin / 10;
|
||||
let fsel_shift = (pin % 10) * 3;
|
||||
|
||||
let mut fsel = self.GPFSEL[fsel_reg].get();
|
||||
fsel &= !(0x7 << fsel_shift);
|
||||
fsel |= (value & 0x7) << fsel_shift;
|
||||
self.GPFSEL[fsel_reg].set(fsel);
|
||||
}
|
||||
|
||||
fn write_pupd(&self, pin: usize, value: u32) {
|
||||
let pupd_reg = pin / 16;
|
||||
let pupd_shift = (pin % 16) * 2;
|
||||
|
||||
let mut pupd = self.GPIO_PUP_PDN_CNTRL_REG[pupd_reg].get();
|
||||
pupd &= !(0x3 << pupd_shift);
|
||||
pupd |= (value & 0x3) << pupd_shift;
|
||||
self.GPIO_PUP_PDN_CNTRL_REG[pupd_reg].set(pupd);
|
||||
}
|
||||
|
||||
fn configure_pin_interrupts(
|
||||
&self,
|
||||
pin: usize,
|
||||
rising_edge: bool,
|
||||
falling_edge: bool,
|
||||
level_high: bool,
|
||||
level_low: bool,
|
||||
) {
|
||||
#[inline]
|
||||
fn modify_reg(reg: &ReadWrite<u32>, bit: u32, set: bool) {
|
||||
if set {
|
||||
reg.set(reg.get() | bit);
|
||||
} else {
|
||||
reg.set(reg.get() & !bit);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO use async edge detection (likely have some limitations)?
|
||||
let reg = pin / 32;
|
||||
let bit = 1 << (pin % 32);
|
||||
|
||||
// Disable async edge events
|
||||
modify_reg(&self.GPAREN[reg], bit, false);
|
||||
modify_reg(&self.GPAFEN[reg], bit, false);
|
||||
|
||||
modify_reg(&self.GPREN[reg], bit, rising_edge);
|
||||
modify_reg(&self.GPFEN[reg], bit, falling_edge);
|
||||
modify_reg(&self.GPHEN[reg], bit, level_high);
|
||||
modify_reg(&self.GPLEN[reg], bit, level_low);
|
||||
|
||||
// Clear interrupt status
|
||||
self.GPEDS[reg].set(bit);
|
||||
}
|
||||
|
||||
fn configure_pin_function(&self, pin: usize, function: u32, pull: u32) {
|
||||
self.write_fsel(pin, function);
|
||||
self.write_pupd(pin, pull);
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Bcm2711Gpio {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
let regs = self.regs.lock();
|
||||
|
||||
// Disable all interrupts by default
|
||||
for pin in 0..58 {
|
||||
regs.configure_pin_interrupts(pin, false, false, false, false);
|
||||
}
|
||||
|
||||
let intc = external_interrupt_controller()?;
|
||||
for irq in self.irqs.iter() {
|
||||
intc.register_irq(irq.irq, irq.options, self.clone())?;
|
||||
intc.enable_irq(irq.irq)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"bcm2711-gpio"
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for Bcm2711Gpio {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
log::warn!("TODO: handle bcm2711-gpio interrupts");
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl GpioController for Bcm2711Gpio {
|
||||
fn setup_gpio(&self, pin: &PinHandle, _event: Option<GpioInterruptEvent>) -> Result<(), Error> {
|
||||
log::warn!(
|
||||
"TOOD: bcm2711 gpio pin #{} setup: input={}, output={}",
|
||||
pin.index,
|
||||
pin.config.input,
|
||||
pin.config.output
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn write_gpio(&self, _pin: &PinHandle, _value: bool) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTreePinController for Bcm2711Gpio {
|
||||
fn configure_pin_group(self: Arc<Self>, group: &Arc<Node>) -> Result<(), Error> {
|
||||
let pins = group.property("brcm,pins").ok_or(Error::InvalidArgument)?;
|
||||
let function = group.property("brcm,function").ok_or(Error::DoesNotExist)?;
|
||||
let pull = group.property("brcm,pull");
|
||||
|
||||
if function.is_empty() || pins.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let function = function.read_cell(0, 1).ok_or(Error::InvalidArgument)? as u32;
|
||||
let regs = self.regs.lock();
|
||||
|
||||
for i in 0..pins.len() / 4 {
|
||||
let pin = pins.read_cell(i, 1).ok_or(Error::InvalidArgument)? as u32;
|
||||
let pull = if let Some(pull) = pull.as_ref().and_then(|p| p.read_cell(i, 1)) {
|
||||
pull as u32
|
||||
} else {
|
||||
PUPD_NONE
|
||||
};
|
||||
log::info!("bcm2711-gpio: gpio{pin} function={function}");
|
||||
regs.configure_pin_function(pin as usize, function, pull);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn map_gpio(
|
||||
self: Arc<Self>,
|
||||
property: &TProp,
|
||||
offset: usize,
|
||||
info: &DeviceTreeGpioPins,
|
||||
) -> Option<(PinHandle, usize)> {
|
||||
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
|
||||
let config = generic_gpio_config(options as u32, info);
|
||||
Some((
|
||||
PinHandle {
|
||||
index: pin as u32,
|
||||
config,
|
||||
parent: self,
|
||||
},
|
||||
2,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["brcm,bcm2711-gpio"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let irq0 = node.interrupt(0)?;
|
||||
let irq1 = node.interrupt(1)?;
|
||||
|
||||
let base = node.map_base(context, 0)?;
|
||||
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
|
||||
|
||||
let gpio = Arc::new(Bcm2711Gpio {
|
||||
regs: IrqSafeSpinlock::new(regs),
|
||||
irqs: [irq0, irq1]
|
||||
});
|
||||
|
||||
node.make_pin_controller(gpio.clone());
|
||||
|
||||
Some(gpio)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod aux;
|
||||
mod aux_uart;
|
||||
mod gpio;
|
||||
mod mbox;
|
||||
@@ -1,17 +0,0 @@
|
||||
[package]
|
||||
name = "ygg_driver_bsp_jh7110"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
device-tree.workspace = true
|
||||
device-api.workspace = true
|
||||
yggdrasil-abi.workspace = true
|
||||
libk-mm.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk.workspace = true
|
||||
|
||||
tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
bytemuck.workspace = true
|
||||
futures-util.workspace = true
|
||||
@@ -1,7 +0,0 @@
|
||||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod clocks;
|
||||
mod pinctrl;
|
||||
@@ -1,445 +0,0 @@
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::{
|
||||
GpioController, GpioInterruptEvent, GpioPinLevel, OutputPinBias, PinHandle,
|
||||
SinglePinDirection,
|
||||
},
|
||||
};
|
||||
use device_tree::{
|
||||
driver::{
|
||||
device_tree_driver,
|
||||
util::{generic_gpio_config, GenericPinctrlBiasConfig, GenericPinctrlConfig},
|
||||
DeviceTreeGpioPins, DeviceTreePinController, Node, ProbeContext,
|
||||
},
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
|
||||
use libk_util::{bit::BitField, sync::IrqSafeSpinlock, OneTimeInit};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PinMuxConfig {
|
||||
pin: u8,
|
||||
function: u8,
|
||||
doen: u8,
|
||||
dout: u8,
|
||||
din: u8,
|
||||
}
|
||||
|
||||
struct SysRegs(DeviceMemoryIoMut<'static, [u32]>);
|
||||
struct AonRegs(DeviceMemoryIoMut<'static, [u32]>);
|
||||
|
||||
unsafe impl Send for SysRegs {}
|
||||
unsafe impl Sync for SysRegs {}
|
||||
unsafe impl Send for AonRegs {}
|
||||
unsafe impl Sync for AonRegs {}
|
||||
|
||||
struct Gpio<R: GpioRegs> {
|
||||
regs: IrqSafeSpinlock<R>,
|
||||
init: OneTimeInit<Result<(), Error>>,
|
||||
clocks: Vec<ClockHandle>,
|
||||
resets: Vec<ResetHandle>,
|
||||
}
|
||||
|
||||
trait GpioRegs: Sized + Send + 'static {
|
||||
const NAME: &'static str;
|
||||
|
||||
const DOUT_OFFSET: usize;
|
||||
const DOEN_OFFSET: usize;
|
||||
const GPI_OFFSET: usize;
|
||||
|
||||
const DOUT_MASK: u32;
|
||||
const DOEN_MASK: u32;
|
||||
const GPI_MASK: u32;
|
||||
|
||||
unsafe fn map(base: PhysicalAddress) -> Result<Self, Error>;
|
||||
fn reg_mut(&mut self, index: usize) -> *mut u32;
|
||||
fn pin_functions(&self, pin: usize) -> Option<(usize, usize, u32)>;
|
||||
|
||||
fn write_output_pin(&mut self, pin: usize, value: bool) {
|
||||
let reg = pin / 4;
|
||||
let shift = (pin % 4) * 8;
|
||||
let reg_dout = self.reg_mut(Self::DOUT_OFFSET + reg);
|
||||
unsafe {
|
||||
let mut val = reg_dout.read_volatile();
|
||||
val.set_bit(shift, value);
|
||||
reg_dout.write_volatile(val);
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_pin(&mut self, pin: usize, doen: u32, dout: u32, din: u32) {
|
||||
let reg = pin / 4;
|
||||
let shift = (pin % 4) * 8;
|
||||
|
||||
let reg_doen = self.reg_mut(Self::DOEN_OFFSET + reg);
|
||||
let reg_dout = self.reg_mut(Self::DOUT_OFFSET + reg);
|
||||
|
||||
unsafe {
|
||||
let mut val = reg_dout.read_volatile();
|
||||
val &= !(Self::DOUT_MASK << shift);
|
||||
val |= dout << shift;
|
||||
reg_dout.write_volatile(val);
|
||||
|
||||
let mut val = reg_doen.read_volatile();
|
||||
val &= !(Self::DOEN_MASK << shift);
|
||||
val |= doen << shift;
|
||||
reg_doen.write_volatile(val);
|
||||
|
||||
if din != 0xFF {
|
||||
let din_reg = din as usize / 4;
|
||||
let din_shift = (din % 4) * 8;
|
||||
|
||||
let reg_din = self.reg_mut(Self::GPI_OFFSET + din_reg);
|
||||
let mut val = reg_din.read_volatile();
|
||||
val &= !(Self::GPI_MASK << din_shift);
|
||||
val |= (pin as u32 + 2) << din_shift;
|
||||
reg_din.write_volatile(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_pin_function(&mut self, pin: usize, func: u32) {
|
||||
let Some((offset, shift, max)) = self.pin_functions(pin) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if func > max {
|
||||
return;
|
||||
}
|
||||
|
||||
log::info!("jh7110-sys-pinctrl: set pad{pin} function={func}");
|
||||
let reg = self.reg_mut(offset / 4);
|
||||
unsafe {
|
||||
let mut val = reg.read_volatile();
|
||||
val &= !(0x3 << shift);
|
||||
val |= func << shift;
|
||||
reg.write_volatile(val);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_input_enabled(&mut self, _pin: usize, _enabled: bool) {}
|
||||
|
||||
fn set_output_enabled(&mut self, _pin: usize, _enabled: bool) {}
|
||||
|
||||
fn set_bias(&mut self, _pin: usize, _bias: GenericPinctrlBiasConfig) {}
|
||||
}
|
||||
|
||||
// sys-pinctrl pads
|
||||
const fn pad_gpio(n: usize) -> usize {
|
||||
n
|
||||
}
|
||||
const PAD_GMAC1_RXC: usize = 82;
|
||||
const SYS_PIN_COUNT: usize = 95;
|
||||
|
||||
const SYS_PIN_FUNCTIONS: [Option<(usize, usize, u32)>; SYS_PIN_COUNT] = const {
|
||||
let mut t = [const { None }; SYS_PIN_COUNT];
|
||||
|
||||
t[PAD_GMAC1_RXC] = Some((0x29C, 0, 1));
|
||||
t[pad_gpio(10)] = Some((0x29C, 2, 3));
|
||||
t[pad_gpio(11)] = Some((0x29C, 5, 3));
|
||||
t[pad_gpio(12)] = Some((0x29C, 8, 3));
|
||||
t[pad_gpio(13)] = Some((0x29C, 11, 3));
|
||||
t[pad_gpio(14)] = Some((0x29C, 14, 3));
|
||||
t[pad_gpio(15)] = Some((0x29C, 17, 3));
|
||||
t[pad_gpio(16)] = Some((0x29C, 20, 3));
|
||||
t[pad_gpio(17)] = Some((0x29C, 23, 3));
|
||||
t[pad_gpio(18)] = Some((0x29C, 26, 3));
|
||||
t[pad_gpio(19)] = Some((0x29C, 29, 3));
|
||||
|
||||
t[pad_gpio(20)] = Some((0x2A0, 0, 3));
|
||||
t[pad_gpio(21)] = Some((0x2A0, 3, 3));
|
||||
t[pad_gpio(22)] = Some((0x2A0, 6, 3));
|
||||
t[pad_gpio(23)] = Some((0x2A0, 9, 3));
|
||||
t[pad_gpio(24)] = Some((0x2A0, 12, 3));
|
||||
t[pad_gpio(25)] = Some((0x2A0, 15, 3));
|
||||
t[pad_gpio(26)] = Some((0x2A0, 18, 3));
|
||||
t[pad_gpio(27)] = Some((0x2A0, 21, 3));
|
||||
t[pad_gpio(28)] = Some((0x2A0, 24, 3));
|
||||
t[pad_gpio(29)] = Some((0x2A0, 27, 3));
|
||||
|
||||
t[pad_gpio(30)] = Some((0x2A4, 0, 3));
|
||||
t[pad_gpio(31)] = Some((0x2A4, 3, 3));
|
||||
t[pad_gpio(32)] = Some((0x2A4, 6, 3));
|
||||
t[pad_gpio(33)] = Some((0x2A4, 9, 3));
|
||||
t[pad_gpio(34)] = Some((0x2A4, 12, 3));
|
||||
t[pad_gpio(35)] = Some((0x2A4, 15, 3));
|
||||
t[pad_gpio(36)] = Some((0x2A4, 17, 3));
|
||||
t[pad_gpio(37)] = Some((0x2A4, 20, 3));
|
||||
t[pad_gpio(38)] = Some((0x2A4, 23, 3));
|
||||
t[pad_gpio(39)] = Some((0x2A4, 26, 3));
|
||||
t[pad_gpio(40)] = Some((0x2A4, 29, 3));
|
||||
|
||||
t[pad_gpio(41)] = Some((0x2A8, 0, 3));
|
||||
t[pad_gpio(42)] = Some((0x2A8, 3, 3));
|
||||
t[pad_gpio(43)] = Some((0x2A8, 6, 3));
|
||||
t[pad_gpio(44)] = Some((0x2A8, 9, 3));
|
||||
t[pad_gpio(45)] = Some((0x2A8, 12, 3));
|
||||
t[pad_gpio(46)] = Some((0x2A8, 15, 3));
|
||||
t[pad_gpio(47)] = Some((0x2A8, 18, 3));
|
||||
t[pad_gpio(48)] = Some((0x2A8, 21, 3));
|
||||
t[pad_gpio(49)] = Some((0x2A8, 24, 3));
|
||||
t[pad_gpio(50)] = Some((0x2A8, 27, 3));
|
||||
t[pad_gpio(51)] = Some((0x2A8, 30, 3));
|
||||
|
||||
t[pad_gpio(52)] = Some((0x2AC, 0, 3));
|
||||
t[pad_gpio(53)] = Some((0x2AC, 2, 3));
|
||||
t[pad_gpio(54)] = Some((0x2AC, 4, 3));
|
||||
t[pad_gpio(55)] = Some((0x2AC, 6, 3));
|
||||
t[pad_gpio(56)] = Some((0x2AC, 9, 3));
|
||||
t[pad_gpio(57)] = Some((0x2AC, 12, 3));
|
||||
t[pad_gpio(58)] = Some((0x2AC, 15, 3));
|
||||
t[pad_gpio(59)] = Some((0x2AC, 18, 3));
|
||||
t[pad_gpio(60)] = Some((0x2AC, 21, 3));
|
||||
t[pad_gpio(61)] = Some((0x2AC, 24, 3));
|
||||
t[pad_gpio(62)] = Some((0x2AC, 27, 3));
|
||||
t[pad_gpio(63)] = Some((0x2AC, 30, 3));
|
||||
|
||||
t[pad_gpio(6)] = Some((0x2B0, 0, 3));
|
||||
t[pad_gpio(7)] = Some((0x2B0, 2, 3));
|
||||
t[pad_gpio(8)] = Some((0x2B0, 5, 3));
|
||||
t[pad_gpio(9)] = Some((0x2B0, 8, 3));
|
||||
|
||||
t
|
||||
};
|
||||
|
||||
impl PinMuxConfig {
|
||||
fn from_fdt(fdt: u32) -> Self {
|
||||
let pin = fdt as u8;
|
||||
let function = ((fdt >> 8) & 0x3) as u8;
|
||||
let doen = ((fdt >> 10) & 0x3F) as u8;
|
||||
let dout = (fdt >> 16) as u8;
|
||||
let din = (fdt >> 24) as u8;
|
||||
|
||||
Self {
|
||||
pin,
|
||||
function,
|
||||
doen,
|
||||
dout,
|
||||
din,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: GpioRegs> Gpio<R> {
|
||||
fn from_device_tree(node: &Arc<Node>, context: &mut ProbeContext) -> Result<Arc<Self>, Error> {
|
||||
let base = node.map_base(context, 0).ok_or(Error::DoesNotExist)?;
|
||||
let regs = unsafe { R::map(base) }?;
|
||||
let clocks = node.clocks().map(|c| c.collect()).unwrap_or_default();
|
||||
let resets = node.resets().map(|c| c.collect()).unwrap_or_default();
|
||||
|
||||
let gpio = Arc::new(Self {
|
||||
regs: IrqSafeSpinlock::new(regs),
|
||||
init: OneTimeInit::new(),
|
||||
clocks,
|
||||
resets,
|
||||
});
|
||||
|
||||
Ok(gpio)
|
||||
}
|
||||
|
||||
fn configure_pinmux(&self, mux: PinMuxConfig, cfg: &GenericPinctrlConfig) {
|
||||
let mut regs = self.regs.lock();
|
||||
regs.configure_pin_function(mux.pin as _, mux.function as _);
|
||||
regs.configure_pin(mux.pin as _, mux.doen as _, mux.dout as _, mux.din as _);
|
||||
if let Some(enable) = cfg.input_enable {
|
||||
regs.set_input_enabled(mux.pin as _, enable);
|
||||
}
|
||||
if let Some(enable) = cfg.output_enable {
|
||||
regs.set_output_enabled(mux.pin as _, enable);
|
||||
}
|
||||
if let Some(bias) = cfg.bias {
|
||||
regs.set_bias(mux.pin as _, bias);
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_pin_output(
|
||||
&self,
|
||||
pin: usize,
|
||||
_active_level: GpioPinLevel,
|
||||
initial_level: GpioPinLevel,
|
||||
_output_bias: Option<OutputPinBias>,
|
||||
) -> Result<(), Error> {
|
||||
// TODO configure pull up/pull down?
|
||||
let mut regs = self.regs.lock();
|
||||
let dout_val = initial_level == GpioPinLevel::High;
|
||||
regs.configure_pin_function(pin, 0);
|
||||
regs.configure_pin(pin, 0, dout_val as u32, 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_init(&self) -> Result<(), Error> {
|
||||
let res = self.init.or_init_with(|| {
|
||||
for clock in self.clocks.iter() {
|
||||
clock.enable()?;
|
||||
}
|
||||
for reset in self.resets.iter() {
|
||||
reset.deassert()?;
|
||||
}
|
||||
|
||||
// TODO disable all pin IRQs
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
*res
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: GpioRegs> Device for Gpio<R> {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
self.ensure_init()
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
R::NAME
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: GpioRegs> GpioController for Gpio<R> {
|
||||
fn setup_gpio(&self, pin: &PinHandle, _event: Option<GpioInterruptEvent>) -> Result<(), Error> {
|
||||
log::warn!(
|
||||
"TODO: setup gpio #{} input={}, output={}",
|
||||
pin.index,
|
||||
pin.config.input,
|
||||
pin.config.output
|
||||
);
|
||||
// TODO bidi pins?
|
||||
match pin.config.force_single_direction() {
|
||||
Some(SinglePinDirection::Input) => todo!(),
|
||||
Some(SinglePinDirection::Output) => self.configure_pin_output(
|
||||
pin.index as usize,
|
||||
pin.config.active_level,
|
||||
pin.config.initial_level,
|
||||
pin.config.output_bias,
|
||||
),
|
||||
None => {
|
||||
todo!("{}: support for bidi gpio", R::NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn write_gpio(&self, pin: &PinHandle, value: bool) -> Result<(), Error> {
|
||||
self.regs.lock().write_output_pin(pin.index as _, value);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: GpioRegs> DeviceTreePinController for Gpio<R> {
|
||||
fn configure_pin_group(self: Arc<Self>, pins: &Arc<Node>) -> Result<(), Error> {
|
||||
self.ensure_init()?;
|
||||
|
||||
for child in pins.children() {
|
||||
let pinmux = child.property("pinmux").ok_or(Error::DoesNotExist)?;
|
||||
let cfg = GenericPinctrlConfig::from_node(child);
|
||||
|
||||
for i in 0..pinmux.len() / 4 {
|
||||
let mux = pinmux.read_cell(i, 1).unwrap() as u32;
|
||||
let mux = PinMuxConfig::from_fdt(mux);
|
||||
self.configure_pinmux(mux, &cfg);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn map_gpio(
|
||||
self: Arc<Self>,
|
||||
property: &TProp,
|
||||
offset: usize,
|
||||
info: &DeviceTreeGpioPins,
|
||||
) -> Option<(PinHandle, usize)> {
|
||||
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
|
||||
let config = generic_gpio_config(options as u32, info);
|
||||
Some((
|
||||
PinHandle {
|
||||
index: pin as u32,
|
||||
config,
|
||||
parent: self,
|
||||
},
|
||||
2,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl GpioRegs for SysRegs {
|
||||
const NAME: &'static str = "jh7110-sys-pinctrl";
|
||||
|
||||
const DOEN_OFFSET: usize = 0x00;
|
||||
const DOUT_OFFSET: usize = 0x40 / 4;
|
||||
const GPI_OFFSET: usize = 0x80 / 4;
|
||||
|
||||
const DOEN_MASK: u32 = 0x1F;
|
||||
const DOUT_MASK: u32 = 0x3F;
|
||||
const GPI_MASK: u32 = 0x3F;
|
||||
|
||||
unsafe fn map(base: PhysicalAddress) -> Result<Self, Error> {
|
||||
DeviceMemoryIoMut::map_slice(base, 192, Default::default()).map(Self)
|
||||
}
|
||||
|
||||
fn pin_functions(&self, pin: usize) -> Option<(usize, usize, u32)> {
|
||||
SYS_PIN_FUNCTIONS.get(pin).copied()?
|
||||
}
|
||||
|
||||
fn reg_mut(&mut self, index: usize) -> *mut u32 {
|
||||
&raw mut self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl GpioRegs for AonRegs {
|
||||
const NAME: &'static str = "jh7110-aon-pinctrl";
|
||||
|
||||
const DOEN_OFFSET: usize = 0x00;
|
||||
const DOUT_OFFSET: usize = 0x04 / 4;
|
||||
const GPI_OFFSET: usize = 0x08 / 4;
|
||||
|
||||
const DOEN_MASK: u32 = 0x7;
|
||||
const DOUT_MASK: u32 = 0xF;
|
||||
const GPI_MASK: u32 = 0xF;
|
||||
|
||||
unsafe fn map(base: PhysicalAddress) -> Result<Self, Error> {
|
||||
DeviceMemoryIoMut::map_slice(base, 64, Default::default()).map(Self)
|
||||
}
|
||||
|
||||
fn pin_functions(&self, _pin: usize) -> Option<(usize, usize, u32)> {
|
||||
None
|
||||
}
|
||||
|
||||
fn reg_mut(&mut self, index: usize) -> *mut u32 {
|
||||
&raw mut self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["starfive,jh7110-sys-pinctrl"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let gpio = Gpio::<SysRegs>::from_device_tree(node, context)
|
||||
.inspect_err(|e| log::error!("jh7110-sys-pinctrl: probe error: {e:?}"))
|
||||
.ok()?;
|
||||
|
||||
node.make_pin_controller(gpio.clone());
|
||||
|
||||
Some(gpio)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["starfive,jh7110-aon-pinctrl"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let gpio = Gpio::<AonRegs>::from_device_tree(node, context)
|
||||
.inspect_err(|e| log::error!("jh7110-aon-pinctrl: probe error: {e:?}"))
|
||||
.ok()?;
|
||||
|
||||
node.make_pin_controller(gpio.clone());
|
||||
|
||||
Some(gpio)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
[package]
|
||||
name = "ygg_driver_bsp_riscv"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
device-tree.workspace = true
|
||||
device-api.workspace = true
|
||||
yggdrasil-abi.workspace = true
|
||||
libk-mm.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk.workspace = true
|
||||
kernel-arch-riscv64.workspace = true
|
||||
|
||||
tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
bytemuck.workspace = true
|
||||
@@ -1,6 +0,0 @@
|
||||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod plic;
|
||||
@@ -1,613 +0,0 @@
|
||||
//! PCI capability structures and queries
|
||||
|
||||
use alloc::{sync::Arc, vec, vec::Vec};
|
||||
use bitflags::bitflags;
|
||||
use device_api::interrupt::{
|
||||
InterruptAffinity, InterruptHandler, MessageInterruptController, MsiInfo,
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
registers::{ReadWrite, WriteOnly},
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::PciBaseAddress;
|
||||
|
||||
use super::{PciCapability, PciCapabilityId, PciConfigurationSpace};
|
||||
|
||||
bitflags! {
|
||||
pub struct PcieLinkControl: u16 {
|
||||
const ASPM_DISABLE = 0 << 0;
|
||||
// Active state power management control
|
||||
const ASPM_MASK = 0x3 << 0;
|
||||
// Enable clock power management
|
||||
const ECPM = 1 << 8;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
|
||||
use core::mem::offset_of;
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
|
||||
use kernel_arch_x86::intrinsics;
|
||||
|
||||
pub trait VirtioCapabilityData<'s, S: PciConfigurationSpace + ?Sized + 's>: Sized {
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self;
|
||||
|
||||
fn space(&self) -> &'s S;
|
||||
fn offset(&self) -> usize;
|
||||
|
||||
fn bar_index(&self) -> Option<usize> {
|
||||
let value = self.space().read_u8(self.offset() + 4);
|
||||
(value <= 0x5).then_some(value as _)
|
||||
}
|
||||
|
||||
fn bar_offset(&self) -> usize {
|
||||
let value = self.space().read_u32(self.offset() + 8);
|
||||
value as _
|
||||
}
|
||||
|
||||
fn length(&self) -> usize {
|
||||
let value = self.space().read_u32(self.offset() + 12);
|
||||
value as _
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VirtioCapability {
|
||||
const CFG_TYPE: u8;
|
||||
const MIN_LEN: usize = 0;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a>: VirtioCapabilityData<'a, S>;
|
||||
}
|
||||
|
||||
/// Power management capability entry
|
||||
pub struct PowerManagementCapability;
|
||||
/// MSI-X capability query
|
||||
pub struct MsiXCapability;
|
||||
/// MSI capability query
|
||||
pub struct MsiCapability;
|
||||
/// PCIe capability
|
||||
pub struct PciExpressCapability;
|
||||
|
||||
// VirtIO-over-PCI capabilities
|
||||
/// VirtIO PCI configuration access
|
||||
pub struct VirtioDeviceConfigCapability;
|
||||
/// VirtIO common configuration
|
||||
pub struct VirtioCommonConfigCapability;
|
||||
/// VirtIO notify configuration
|
||||
pub struct VirtioNotifyConfigCapability;
|
||||
/// VirtIO interrupt status
|
||||
pub struct VirtioInterruptStatusCapability;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DevicePowerState {
|
||||
D0,
|
||||
D1,
|
||||
D2,
|
||||
D3Cold,
|
||||
D3Hot,
|
||||
}
|
||||
|
||||
/// Represents an entry in MSI-X vector table
|
||||
#[repr(C)]
|
||||
pub struct MsiXEntry {
|
||||
/// Address to which the value is written on interrupt
|
||||
pub address: WriteOnly<u64>,
|
||||
/// Value which is written to trigger an interrupt
|
||||
pub data: WriteOnly<u32>,
|
||||
/// Vector control word
|
||||
pub control: ReadWrite<u32>,
|
||||
}
|
||||
|
||||
enum MsiXVectorTableAccess<'a> {
|
||||
Memory(DeviceMemoryIoMut<'a, [MsiXEntry]>),
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
|
||||
Io(u16),
|
||||
}
|
||||
|
||||
pub struct MsiXVectorTable<'a> {
|
||||
access: MsiXVectorTableAccess<'a>,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
/// PCI Power Management capability data structure
|
||||
pub struct PowerManagementData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
/// MSI-X capability data structure
|
||||
pub struct MsiXData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
/// MSI capability data structure
|
||||
pub struct MsiData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
/// PCI Express capability data structure
|
||||
pub struct PcieData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
pub struct VirtioDeviceConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
pub struct VirtioCommonConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
pub struct VirtioNotifyConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
pub struct VirtioInterruptStatusData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<T: VirtioCapability> PciCapability for T {
|
||||
const ID: PciCapabilityId = PciCapabilityId::VendorSpecific;
|
||||
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = T::Output<'a, S>;
|
||||
|
||||
fn check<S: PciConfigurationSpace + ?Sized>(space: &S, offset: usize, len: usize) -> bool {
|
||||
let cfg_type = space.read_u8(offset + 3);
|
||||
cfg_type == T::CFG_TYPE && len >= T::MIN_LEN
|
||||
}
|
||||
|
||||
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
_len: usize,
|
||||
) -> Self::CapabilityData<'s, S> {
|
||||
T::Output::from_space_offset(space, offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl PciCapability for PowerManagementCapability {
|
||||
const ID: PciCapabilityId = PciCapabilityId::PowerManagement;
|
||||
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PowerManagementData<'a, S>;
|
||||
|
||||
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
_len: usize,
|
||||
) -> Self::CapabilityData<'s, S> {
|
||||
PowerManagementData { space, offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl PciCapability for MsiXCapability {
|
||||
const ID: PciCapabilityId = PciCapabilityId::MsiX;
|
||||
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = MsiXData<'a, S>;
|
||||
|
||||
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
_len: usize,
|
||||
) -> Self::CapabilityData<'s, S> {
|
||||
MsiXData { space, offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl PciCapability for MsiCapability {
|
||||
const ID: PciCapabilityId = PciCapabilityId::Msi;
|
||||
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = MsiData<'a, S>;
|
||||
|
||||
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
_len: usize,
|
||||
) -> Self::CapabilityData<'s, S> {
|
||||
MsiData { space, offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl PciCapability for PciExpressCapability {
|
||||
const ID: PciCapabilityId = PciCapabilityId::PciExpress;
|
||||
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PcieData<'a, S>;
|
||||
|
||||
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
_len: usize,
|
||||
) -> Self::CapabilityData<'s, S> {
|
||||
PcieData { space, offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioCapability for VirtioDeviceConfigCapability {
|
||||
const CFG_TYPE: u8 = 0x04;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioDeviceConfigData<'a, S>;
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
|
||||
for VirtioDeviceConfigData<'s, S>
|
||||
{
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self {
|
||||
Self { space, offset }
|
||||
}
|
||||
|
||||
fn space(&self) -> &'s S {
|
||||
self.space
|
||||
}
|
||||
|
||||
fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioCapability for VirtioCommonConfigCapability {
|
||||
const CFG_TYPE: u8 = 0x01;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioCommonConfigData<'a, S>;
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
|
||||
for VirtioCommonConfigData<'s, S>
|
||||
{
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self {
|
||||
Self { space, offset }
|
||||
}
|
||||
|
||||
fn space(&self) -> &'s S {
|
||||
self.space
|
||||
}
|
||||
|
||||
fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioCapability for VirtioNotifyConfigCapability {
|
||||
const CFG_TYPE: u8 = 0x02;
|
||||
const MIN_LEN: usize = 0x14;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioNotifyConfigData<'a, S>;
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioNotifyConfigData<'s, S> {
|
||||
pub fn offset_multiplier(&self) -> usize {
|
||||
self.space.read_u32(self.offset + 16) as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
|
||||
for VirtioNotifyConfigData<'s, S>
|
||||
{
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self {
|
||||
Self { space, offset }
|
||||
}
|
||||
|
||||
fn space(&self) -> &'s S {
|
||||
self.space
|
||||
}
|
||||
|
||||
fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioCapability for VirtioInterruptStatusCapability {
|
||||
const CFG_TYPE: u8 = 0x03;
|
||||
const MIN_LEN: usize = 1;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioInterruptStatusData<'a, S>;
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioInterruptStatusData<'s, S> {
|
||||
pub fn read_status(&self) -> (bool, bool) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
|
||||
for VirtioInterruptStatusData<'s, S>
|
||||
{
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self {
|
||||
Self { space, offset }
|
||||
}
|
||||
|
||||
fn space(&self) -> &'s S {
|
||||
self.space
|
||||
}
|
||||
|
||||
fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> PowerManagementData<'s, S> {
|
||||
pub fn set_device_power_state(&self, state: DevicePowerState) {
|
||||
let pmcsr = self.space.read_u16(self.offset + 4) & !0x3;
|
||||
let current = self.get_device_power_state();
|
||||
|
||||
if state == current {
|
||||
return;
|
||||
}
|
||||
|
||||
log::info!("Set device power state: {state:?}");
|
||||
|
||||
match state {
|
||||
DevicePowerState::D0 => {
|
||||
// power = 0b00 | PME_EN
|
||||
self.space.write_u16(self.offset + 4, pmcsr);
|
||||
}
|
||||
_ => {
|
||||
log::warn!("TODO: {state:?} power state");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_pme_en(&self, state: bool) {
|
||||
let pmcsr = self.space.read_u16(self.offset + 4);
|
||||
let new = if state {
|
||||
pmcsr | (1 << 8)
|
||||
} else {
|
||||
pmcsr & !(1 << 8)
|
||||
};
|
||||
if pmcsr == new {
|
||||
return;
|
||||
}
|
||||
|
||||
log::info!("Set PMCSR.PME_En = {state}");
|
||||
|
||||
self.space.write_u16(self.offset + 4, new);
|
||||
}
|
||||
|
||||
pub fn get_device_power_state(&self) -> DevicePowerState {
|
||||
let pmcsr = self.space.read_u16(self.offset + 4);
|
||||
match pmcsr & 0x3 {
|
||||
0b00 => DevicePowerState::D0,
|
||||
0b01 => DevicePowerState::D1,
|
||||
0b10 => DevicePowerState::D2,
|
||||
0b11 => DevicePowerState::D3Hot,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiXData<'s, S> {
|
||||
// TODO use pending bits as well
|
||||
/// Maps and returns the vector table associated with the device's MSI-X capability
|
||||
pub fn vector_table<'a>(&self) -> Result<MsiXVectorTable<'a>, Error> {
|
||||
let w0 = self.space.read_u16(self.offset + 2);
|
||||
let dw1 = self.space.read_u32(self.offset + 4);
|
||||
|
||||
let table_size = (w0 as usize & 0x3FF) + 1;
|
||||
let bir = dw1 as usize & 0x3;
|
||||
let table_offset = dw1 as usize & !0x3;
|
||||
|
||||
let Some(base) = self.space.bar(bir) else {
|
||||
return Err(Error::DoesNotExist);
|
||||
};
|
||||
|
||||
match base {
|
||||
PciBaseAddress::Memory32(mem32) => unsafe {
|
||||
log::info!("MSI-X table address: {:#x}", mem32 + table_offset as u32);
|
||||
MsiXVectorTable::memory_from_raw_parts(
|
||||
PhysicalAddress::from_u32(mem32).add(table_offset),
|
||||
table_size,
|
||||
)
|
||||
},
|
||||
PciBaseAddress::Memory64(mem64) => unsafe {
|
||||
log::info!("MSI-X table address: {:#x}", mem64 + table_offset as u64);
|
||||
MsiXVectorTable::memory_from_raw_parts(
|
||||
PhysicalAddress::from_u64(mem64).add(table_offset),
|
||||
table_size,
|
||||
)
|
||||
},
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
|
||||
PciBaseAddress::Io(io) => unsafe {
|
||||
log::info!("MSI-X table I/O: {:#x}", io + table_offset as u16);
|
||||
MsiXVectorTable::io_from_raw_parts(io + table_offset as u16, table_size)
|
||||
},
|
||||
#[cfg(any(not(any(target_arch = "x86", target_arch = "x86_64")), rust_analyzer))]
|
||||
PciBaseAddress::Io(_) => Err(Error::DoesNotExist),
|
||||
}
|
||||
}
|
||||
|
||||
/// Changes the global enable status for the device's MSI-X capability. If set, regular IRQs
|
||||
/// are not generated.
|
||||
pub fn set_enabled(&mut self, enabled: bool) {
|
||||
let mut w0 = self.space.read_u32(self.offset);
|
||||
if enabled {
|
||||
w0 |= 1 << 31;
|
||||
} else {
|
||||
w0 &= !(1 << 31);
|
||||
}
|
||||
self.space.write_u32(self.offset, w0);
|
||||
}
|
||||
|
||||
pub fn set_function_mask(&mut self, masked: bool) {
|
||||
let mut w0 = self.space.read_u32(self.offset);
|
||||
if masked {
|
||||
w0 |= 1 << 30;
|
||||
} else {
|
||||
w0 &= !(1 << 30);
|
||||
}
|
||||
self.space.write_u32(self.offset, w0);
|
||||
}
|
||||
}
|
||||
|
||||
impl MsiXVectorTableAccess<'_> {
|
||||
fn set_vector_masked(&mut self, vector: usize, masked: bool) {
|
||||
let old = self.read_control(vector);
|
||||
let new = if masked { old | 1 } else { old & !1 };
|
||||
if old != new {
|
||||
self.write_control(vector, new);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_control(&mut self, vector: usize) -> u32 {
|
||||
match self {
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
|
||||
&mut Self::Io(base) => unsafe {
|
||||
let a = base
|
||||
+ (vector * size_of::<MsiXEntry>() + offset_of!(MsiXEntry, control)) as u16;
|
||||
intrinsics::inl(a)
|
||||
},
|
||||
|
||||
Self::Memory(vectors) => vectors[vector].control.get(),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_address(&mut self, vector: usize, value: u64) {
|
||||
match self {
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
|
||||
&mut Self::Io(base) => unsafe {
|
||||
let a = base + (vector * size_of::<MsiXEntry>()) as u16;
|
||||
intrinsics::outl(a, value as u32);
|
||||
intrinsics::outl(a + 4, (value >> 32) as u32);
|
||||
},
|
||||
|
||||
Self::Memory(vectors) => vectors[vector].address.set(value),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_data(&mut self, vector: usize, value: u32) {
|
||||
match self {
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
|
||||
&mut Self::Io(base) => unsafe {
|
||||
let a =
|
||||
base + (vector * size_of::<MsiXEntry>() + offset_of!(MsiXEntry, data)) as u16;
|
||||
intrinsics::outl(a, value)
|
||||
},
|
||||
|
||||
Self::Memory(vectors) => vectors[vector].data.set(value),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_control(&mut self, vector: usize, value: u32) {
|
||||
match self {
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
|
||||
&mut Self::Io(base) => unsafe {
|
||||
let a = base
|
||||
+ (vector * size_of::<MsiXEntry>() + offset_of!(MsiXEntry, control)) as u16;
|
||||
intrinsics::outl(a, value)
|
||||
},
|
||||
|
||||
Self::Memory(vectors) => vectors[vector].control.set(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MsiXVectorTable<'_> {
|
||||
unsafe fn memory_from_raw_parts(base: PhysicalAddress, len: usize) -> Result<Self, Error> {
|
||||
let vectors = DeviceMemoryIoMut::map_slice(base, len, Default::default())?;
|
||||
Ok(Self {
|
||||
access: MsiXVectorTableAccess::Memory(vectors),
|
||||
len,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
|
||||
unsafe fn io_from_raw_parts(base: u16, len: usize) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
access: MsiXVectorTableAccess::Io(base),
|
||||
len,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn mask_all(&mut self) {
|
||||
for i in 0..self.len {
|
||||
self.access.set_vector_masked(i, true);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_range(
|
||||
&mut self,
|
||||
start: usize,
|
||||
end: usize,
|
||||
ic: &Arc<dyn MessageInterruptController>,
|
||||
affinity: InterruptAffinity,
|
||||
handler: Arc<dyn InterruptHandler>,
|
||||
) -> Result<Vec<MsiInfo>, Error> {
|
||||
assert!(end > start);
|
||||
let mut range = vec![
|
||||
MsiInfo {
|
||||
affinity,
|
||||
..Default::default()
|
||||
};
|
||||
end - start
|
||||
];
|
||||
ic.clone().register_msi_range(&mut range, handler)?;
|
||||
|
||||
for (i, info) in range.iter().enumerate() {
|
||||
let index = i + start;
|
||||
self.access.write_address(index, info.address as _);
|
||||
self.access.write_data(index, info.value);
|
||||
self.access.set_vector_masked(index, false);
|
||||
}
|
||||
|
||||
Ok(range)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiData<'s, S> {
|
||||
pub fn set_enabled(&mut self, enabled: bool) {
|
||||
let mut w0 = self.space.read_u16(self.offset + 2);
|
||||
if enabled {
|
||||
w0 |= 1 << 0;
|
||||
} else {
|
||||
w0 &= !(1 << 0);
|
||||
}
|
||||
self.space.write_u16(self.offset + 2, w0);
|
||||
}
|
||||
|
||||
pub fn register(
|
||||
&mut self,
|
||||
ic: &Arc<dyn MessageInterruptController>,
|
||||
affinity: InterruptAffinity,
|
||||
handler: Arc<dyn InterruptHandler>,
|
||||
) -> Result<MsiInfo, Error> {
|
||||
let info = ic.clone().register_msi(affinity, handler)?;
|
||||
|
||||
let mut w0 = self.space.read_u16(self.offset + 2);
|
||||
// Enable the vector first
|
||||
w0 |= 1 << 0;
|
||||
|
||||
// Reset to one vector
|
||||
w0 &= !(0x7 << 4);
|
||||
|
||||
self.space.write_u16(self.offset + 2, w0);
|
||||
|
||||
if info.value > u16::MAX as u32 {
|
||||
log::warn!("Could not setup a MSI: value={:#x} > u16", info.value);
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
|
||||
if info.address > u32::MAX as usize {
|
||||
if w0 & (1 << 7) == 0 {
|
||||
log::warn!(
|
||||
"Could not setup a MSI: address={:#x} and MSI is not 64 bit capable",
|
||||
info.address
|
||||
);
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
|
||||
todo!("FIXME: PCI 64-bit addresses");
|
||||
// self.space
|
||||
// .write_u32(self.offset + 8, (info.address >> 32) as u32);
|
||||
}
|
||||
self.space.write_u32(self.offset + 4, info.address as u32);
|
||||
|
||||
self.space.write_u16(self.offset + 12, info.value as u16);
|
||||
|
||||
Ok(info)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> PcieData<'s, S> {
|
||||
pub fn link_control(&self) -> PcieLinkControl {
|
||||
PcieLinkControl::from_bits_retain(self.space.read_u16(self.offset + 0x10))
|
||||
}
|
||||
|
||||
pub fn set_link_control(&mut self, value: PcieLinkControl) {
|
||||
self.space.write_u16(self.offset + 0x10, value.bits());
|
||||
}
|
||||
}
|
||||
@@ -1,325 +0,0 @@
|
||||
use core::ops::Range;
|
||||
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
device::Device,
|
||||
interrupt::{
|
||||
ExternalInterruptController, InterruptAffinity, InterruptHandler, Irq, IrqOptions,
|
||||
MessageInterruptController, MsiInfo,
|
||||
},
|
||||
};
|
||||
use libk::device::external_interrupt_controller;
|
||||
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
capability::{MsiCapability, MsiXCapability, MsiXVectorTable},
|
||||
driver::PciDriver,
|
||||
PciAddress, PciCommandRegister, PciConfigSpace, PciConfigurationSpace, PciSegmentInfo,
|
||||
};
|
||||
|
||||
/// Describes a PCI device
|
||||
#[derive(Clone)]
|
||||
pub struct PciDeviceInfo {
|
||||
/// Address of the device
|
||||
pub address: PciAddress,
|
||||
/// Class field of the configuration space
|
||||
pub class: u8,
|
||||
/// Subclass field of the configuration space
|
||||
pub subclass: u8,
|
||||
/// Prog IF field of the configuration space
|
||||
pub prog_if: u8,
|
||||
/// Vendor ID field of the configuration space
|
||||
pub vendor_id: u16,
|
||||
/// Device ID field of the configuration space
|
||||
pub device_id: u16,
|
||||
/// Configuration space access method
|
||||
pub config_space: PciConfigSpace,
|
||||
/// Describes the PCI segment this device is a part of
|
||||
pub segment: Arc<PciSegmentInfo>,
|
||||
|
||||
pub(crate) interrupt_config: Arc<OneTimeInit<IrqSafeRwLock<InterruptConfig>>>,
|
||||
}
|
||||
|
||||
pub struct InterruptConfig {
|
||||
#[allow(unused)]
|
||||
preferred_mode: PreferredInterruptMode,
|
||||
configured_mode: ConfiguredInterruptMode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub enum PciInterruptPin {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum PreferredInterruptMode {
|
||||
Msi(bool),
|
||||
Legacy,
|
||||
}
|
||||
|
||||
enum ConfiguredInterruptMode {
|
||||
MsiX(
|
||||
Arc<dyn MessageInterruptController>,
|
||||
MsiXVectorTable<'static>,
|
||||
),
|
||||
Msi(Arc<dyn MessageInterruptController>),
|
||||
LegacyPin(Arc<dyn ExternalInterruptController>, PciInterruptPin),
|
||||
#[cfg_attr(not(target_arch = "x86"), allow(unused))]
|
||||
LegacyLine(Arc<dyn ExternalInterruptController>, u8),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub struct PciInterrupt {
|
||||
pub address: PciAddress,
|
||||
pub pin: PciInterruptPin,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct PciInterruptRoute {
|
||||
pub number: u32,
|
||||
pub options: IrqOptions,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PciMsiRoute {
|
||||
// TODO `msi-base`
|
||||
pub controller: Arc<dyn MessageInterruptController>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum PciDeviceState {
|
||||
None,
|
||||
Probed,
|
||||
Initialized,
|
||||
Failed,
|
||||
}
|
||||
|
||||
/// Used to store PCI bus devices which were enumerated by the kernel
|
||||
pub struct PciBusDevice {
|
||||
pub(crate) info: PciDeviceInfo,
|
||||
pub(crate) device: Option<Arc<dyn Device>>,
|
||||
pub(crate) driver: Option<&'static dyn PciDriver>,
|
||||
pub(crate) state: PciDeviceState,
|
||||
}
|
||||
|
||||
impl PciDeviceInfo {
|
||||
pub fn set_command(
|
||||
&self,
|
||||
enable_irq: bool,
|
||||
enable_mem: bool,
|
||||
enable_io: bool,
|
||||
enable_bus_master: bool,
|
||||
) {
|
||||
let command = PciCommandRegister::from_bits_retain(self.config_space.command());
|
||||
let mut new = command;
|
||||
if enable_irq {
|
||||
new &= !PciCommandRegister::DISABLE_INTERRUPTS;
|
||||
} else {
|
||||
new |= PciCommandRegister::DISABLE_INTERRUPTS;
|
||||
}
|
||||
if enable_mem {
|
||||
new |= PciCommandRegister::ENABLE_MEMORY;
|
||||
} else {
|
||||
new &= !PciCommandRegister::ENABLE_MEMORY;
|
||||
}
|
||||
if enable_io {
|
||||
new |= PciCommandRegister::ENABLE_IO;
|
||||
} else {
|
||||
new &= !PciCommandRegister::ENABLE_IO;
|
||||
}
|
||||
if enable_bus_master {
|
||||
new |= PciCommandRegister::BUS_MASTER;
|
||||
} else {
|
||||
new &= !PciCommandRegister::BUS_MASTER;
|
||||
}
|
||||
if new != command {
|
||||
self.config_space.set_command(new.bits());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_interrupts(&self, preferred_mode: PreferredInterruptMode) -> Result<(), Error> {
|
||||
self.interrupt_config
|
||||
.try_init_with(|| {
|
||||
let configured_mode = if let PreferredInterruptMode::Msi(want_msix) = preferred_mode
|
||||
&& let Some(msi_route) = self.segment.msi_translation_map.map_msi(self.address)
|
||||
{
|
||||
// Try to setup MSI (or MSI-x, if requested)
|
||||
let mut result = None;
|
||||
if want_msix
|
||||
&& let Some(mut msix) = self.config_space.capability::<MsiXCapability>()
|
||||
{
|
||||
if let Ok(mut vt) = msix.vector_table() {
|
||||
if let Some(mut msi) = self.config_space.capability::<MsiCapability>() {
|
||||
msi.set_enabled(false);
|
||||
}
|
||||
|
||||
vt.mask_all();
|
||||
|
||||
msix.set_function_mask(false);
|
||||
msix.set_enabled(true);
|
||||
|
||||
result = Some(ConfiguredInterruptMode::MsiX(
|
||||
msi_route.controller.clone(),
|
||||
vt,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to MSI if MSI-x is not available or not requested
|
||||
if result.is_none() && self.config_space.capability::<MsiCapability>().is_some()
|
||||
{
|
||||
result = Some(ConfiguredInterruptMode::Msi(msi_route.controller));
|
||||
}
|
||||
|
||||
// Fall back to legacy IRQ if nothing else works
|
||||
if let Some(result) = result {
|
||||
result
|
||||
} else {
|
||||
self.legacy_interrupt_mode()
|
||||
}
|
||||
} else {
|
||||
// MSI not requested or segment does not have MSI functionality
|
||||
self.legacy_interrupt_mode()
|
||||
};
|
||||
|
||||
IrqSafeRwLock::new(InterruptConfig {
|
||||
preferred_mode,
|
||||
configured_mode,
|
||||
})
|
||||
})
|
||||
.expect("Possible bug: double-initialization of PCI(e) interrupt config");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn legacy_interrupt_mode(&self) -> ConfiguredInterruptMode {
|
||||
let Ok(intc) = external_interrupt_controller() else {
|
||||
return ConfiguredInterruptMode::None;
|
||||
};
|
||||
|
||||
// TODO this should be retrieved from interrupt map
|
||||
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
||||
{
|
||||
if let Some(irq) = self.config_space.interrupt_line() {
|
||||
return ConfiguredInterruptMode::LegacyLine(intc.clone(), irq);
|
||||
}
|
||||
}
|
||||
|
||||
match self.config_space.interrupt_pin() {
|
||||
Some(pin) => ConfiguredInterruptMode::LegacyPin(intc.clone(), pin),
|
||||
None => ConfiguredInterruptMode::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_interrupt(
|
||||
&self,
|
||||
affinity: InterruptAffinity,
|
||||
handler: Arc<dyn InterruptHandler>,
|
||||
) -> Result<Option<MsiInfo>, Error> {
|
||||
let mut irq = self.interrupt_config.get().write();
|
||||
|
||||
match &mut irq.configured_mode {
|
||||
ConfiguredInterruptMode::Msi(controller) => {
|
||||
let mut msi = self
|
||||
.config_space
|
||||
.capability::<MsiCapability>()
|
||||
.ok_or(Error::InvalidOperation)?;
|
||||
|
||||
let info = msi.register(controller, affinity, handler)?;
|
||||
|
||||
Ok(Some(info))
|
||||
}
|
||||
ConfiguredInterruptMode::MsiX(controller, msix) => {
|
||||
let info = msix.register_range(0, 1, controller, affinity, handler)?;
|
||||
Ok(Some(info[0]))
|
||||
}
|
||||
ConfiguredInterruptMode::LegacyPin(intc, pin) => {
|
||||
self.try_map_legacy(intc.as_ref(), *pin, handler)?;
|
||||
Ok(None)
|
||||
}
|
||||
ConfiguredInterruptMode::LegacyLine(intc, irq) => {
|
||||
self.try_map_legacy_line(intc.as_ref(), *irq, handler)?;
|
||||
Ok(None)
|
||||
}
|
||||
ConfiguredInterruptMode::None => Err(Error::InvalidOperation),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_interrupt_multiple(
|
||||
&self,
|
||||
vector_range: Range<usize>,
|
||||
affinity: InterruptAffinity,
|
||||
handler: Arc<dyn InterruptHandler>,
|
||||
) -> Result<Vec<MsiInfo>, Error> {
|
||||
let mut irq = self.interrupt_config.get().write();
|
||||
let start = vector_range.start;
|
||||
let end = vector_range.end;
|
||||
|
||||
match &mut irq.configured_mode {
|
||||
ConfiguredInterruptMode::MsiX(controller, msix) => {
|
||||
msix.register_range(start, end, controller, affinity, handler)
|
||||
}
|
||||
_ => Err(Error::InvalidOperation),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_map_legacy(
|
||||
&self,
|
||||
intc: &dyn ExternalInterruptController,
|
||||
pin: PciInterruptPin,
|
||||
handler: Arc<dyn InterruptHandler>,
|
||||
) -> Result<(), Error> {
|
||||
let src = PciInterrupt {
|
||||
address: self.address,
|
||||
pin,
|
||||
};
|
||||
let route = self
|
||||
.segment
|
||||
.irq_translation_map
|
||||
.map_interrupt(&src)
|
||||
.inspect_err(|e| log::warn!("Could not map PCI IRQ {pin:?}: {e:?}"))?;
|
||||
|
||||
log::debug!(
|
||||
"PCI {} pin {:?} -> system IRQ #{}",
|
||||
src.address,
|
||||
src.pin,
|
||||
route.number
|
||||
);
|
||||
|
||||
let irq = Irq::External(route.number);
|
||||
intc.register_irq(irq, route.options, handler)?;
|
||||
intc.enable_irq(irq)
|
||||
}
|
||||
|
||||
fn try_map_legacy_line(
|
||||
&self,
|
||||
intc: &dyn ExternalInterruptController,
|
||||
line: u8,
|
||||
handler: Arc<dyn InterruptHandler>,
|
||||
) -> Result<(), Error> {
|
||||
log::debug!("PCI {} -> IRQ#{}", self.address, line);
|
||||
|
||||
let irq = Irq::External(line as u32);
|
||||
intc.register_irq(irq, Default::default(), handler)?;
|
||||
intc.enable_irq(irq)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for PciInterruptPin {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
1 => Ok(Self::A),
|
||||
2 => Ok(Self::B),
|
||||
3 => Ok(Self::C),
|
||||
4 => Ok(Self::D),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{device::Device, dma::DmaAllocator};
|
||||
use libk::error::Error;
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
|
||||
use crate::device::PciDeviceInfo;
|
||||
|
||||
pub enum PciMatch {
|
||||
Generic(fn(&PciDeviceInfo) -> bool),
|
||||
Vendor(u16, u16),
|
||||
Class(u8, Option<u8>, Option<u8>),
|
||||
}
|
||||
|
||||
pub struct PciDriverMatch {
|
||||
pub driver: &'static dyn PciDriver,
|
||||
pub check: PciMatch,
|
||||
}
|
||||
|
||||
pub trait PciDriver: Sync {
|
||||
fn probe(
|
||||
&self,
|
||||
info: &PciDeviceInfo,
|
||||
dma: &Arc<dyn DmaAllocator>,
|
||||
) -> Result<Arc<dyn Device>, Error>;
|
||||
fn driver_name(&self) -> &str;
|
||||
}
|
||||
|
||||
impl PciMatch {
|
||||
pub fn check_device(&self, info: &PciDeviceInfo) -> bool {
|
||||
match self {
|
||||
Self::Generic(f) => f(info),
|
||||
&Self::Vendor(vendor_, device_) => {
|
||||
info.vendor_id == vendor_ && info.device_id == device_
|
||||
}
|
||||
&Self::Class(class_, Some(subclass_), Some(prog_if_)) => {
|
||||
class_ == info.class && subclass_ == info.subclass && prog_if_ == info.prog_if
|
||||
}
|
||||
&Self::Class(class_, Some(subclass_), _) => {
|
||||
class_ == info.class && subclass_ == info.subclass
|
||||
}
|
||||
&Self::Class(class_, _, _) => class_ == info.class,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_match(pmatch: PciMatch, driver: &'static dyn PciDriver) {
|
||||
DRIVERS.write().push(PciDriverMatch {
|
||||
check: pmatch,
|
||||
driver,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn lookup_driver(info: &PciDeviceInfo) -> Option<&'static dyn PciDriver> {
|
||||
DRIVERS.read().iter().find_map(|pmatch| {
|
||||
if pmatch.check.check_device(info) {
|
||||
Some(pmatch.driver)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
static DRIVERS: IrqSafeRwLock<Vec<PciDriverMatch>> = IrqSafeRwLock::new(Vec::new());
|
||||
@@ -1,141 +0,0 @@
|
||||
use core::fmt;
|
||||
|
||||
use alloc::{collections::btree_map::BTreeMap, sync::Arc, vec::Vec};
|
||||
use device_api::interrupt::MessageInterruptController;
|
||||
use libk::error::Error;
|
||||
|
||||
use crate::{
|
||||
device::{PciInterrupt, PciInterruptRoute, PciMsiRoute},
|
||||
PciAddress,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PciInterruptMap {
|
||||
Fixed(BTreeMap<PciInterrupt, PciInterruptRoute>),
|
||||
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
||||
Acpi(alloc::string::String),
|
||||
Legacy,
|
||||
}
|
||||
|
||||
// TODO device-tree also provides a "msi-base" value, which is ignored and assumed to be zero for
|
||||
// now
|
||||
pub struct PciFixedMsiMapping {
|
||||
pub start_address: PciAddress,
|
||||
pub end_address: PciAddress,
|
||||
pub controller: Arc<dyn MessageInterruptController>,
|
||||
}
|
||||
|
||||
pub struct PciFixedMsiMap {
|
||||
pub entries: Vec<PciFixedMsiMapping>,
|
||||
}
|
||||
|
||||
pub enum PciMsiMap {
|
||||
Fixed(PciFixedMsiMap),
|
||||
Identity(Arc<dyn MessageInterruptController>),
|
||||
Legacy,
|
||||
}
|
||||
|
||||
impl PciInterruptMap {
|
||||
pub fn map_interrupt(&self, interrupt: &PciInterrupt) -> Result<PciInterruptRoute, Error> {
|
||||
match self {
|
||||
Self::Fixed(map) => map.get(interrupt).cloned().ok_or(Error::DoesNotExist),
|
||||
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
||||
Self::Acpi(aml_object_name) => {
|
||||
use device_api::interrupt::{IrqLevel, IrqOptions, IrqTrigger};
|
||||
|
||||
use crate::device::PciInterruptPin;
|
||||
|
||||
let aml_pin = match interrupt.pin {
|
||||
PciInterruptPin::A => ygg_driver_acpi::PciPin::IntA,
|
||||
PciInterruptPin::B => ygg_driver_acpi::PciPin::IntB,
|
||||
PciInterruptPin::C => ygg_driver_acpi::PciPin::IntC,
|
||||
PciInterruptPin::D => ygg_driver_acpi::PciPin::IntD,
|
||||
};
|
||||
let aml_route = ygg_driver_acpi::get_pci_route(
|
||||
aml_object_name.as_str(),
|
||||
interrupt.address.device as u16,
|
||||
interrupt.address.function as u16,
|
||||
aml_pin,
|
||||
)
|
||||
.or_else(|| {
|
||||
ygg_driver_acpi::get_pci_route(
|
||||
aml_object_name.as_str(),
|
||||
interrupt.address.device as u16,
|
||||
0xFFFF,
|
||||
aml_pin,
|
||||
)
|
||||
})
|
||||
.ok_or(Error::DoesNotExist)?;
|
||||
|
||||
let trigger = match aml_route.trigger {
|
||||
ygg_driver_acpi::InterruptTrigger::Edge => IrqTrigger::Edge,
|
||||
ygg_driver_acpi::InterruptTrigger::Level => IrqTrigger::Level,
|
||||
};
|
||||
let level = match aml_route.polarity {
|
||||
ygg_driver_acpi::InterruptPolarity::ActiveLow => IrqLevel::ActiveLow,
|
||||
ygg_driver_acpi::InterruptPolarity::ActiveHigh => IrqLevel::ActiveHigh,
|
||||
};
|
||||
|
||||
Ok(PciInterruptRoute {
|
||||
options: IrqOptions { trigger, level },
|
||||
number: aml_route.irq,
|
||||
})
|
||||
}
|
||||
Self::Legacy => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PciMsiMap {
|
||||
pub fn map_msi(&self, address: PciAddress) -> Option<PciMsiRoute> {
|
||||
match self {
|
||||
Self::Fixed(map) => map.map_msi(address),
|
||||
Self::Identity(controller) => Some(PciMsiRoute {
|
||||
controller: controller.clone(),
|
||||
}),
|
||||
Self::Legacy => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PciMsiMap {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Legacy => f.debug_struct("Legacy").finish(),
|
||||
Self::Fixed(map) => f
|
||||
.debug_struct("Fixed")
|
||||
.field("entries", &map.entries)
|
||||
.finish(),
|
||||
Self::Identity(_) => f.debug_struct("Identity").finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PciFixedMsiMap {
|
||||
pub fn map_msi(&self, address: PciAddress) -> Option<PciMsiRoute> {
|
||||
for entry in self.entries.iter() {
|
||||
if entry.contains(address) {
|
||||
let route = PciMsiRoute {
|
||||
controller: entry.controller.clone(),
|
||||
};
|
||||
return Some(route);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl PciFixedMsiMapping {
|
||||
pub fn contains(&self, address: PciAddress) -> bool {
|
||||
self.start_address <= address && self.end_address > address
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PciFixedMsiMapping {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PciFixedMsiMapping")
|
||||
.field("start_address", &self.start_address)
|
||||
.field("end_address", &self.end_address)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1,684 +1,20 @@
|
||||
//! PCI/PCIe bus interfaces
|
||||
#![no_std]
|
||||
#![feature(let_chains, decl_macro)]
|
||||
#![allow(clippy::missing_transmute_annotations, clippy::identity_op)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use core::fmt;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::device::Device;
|
||||
use libk_util::OneTimeInit;
|
||||
|
||||
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
||||
use acpi::mcfg::McfgEntry;
|
||||
use alloc::{format, sync::Arc, vec::Vec};
|
||||
pub struct PciEndpointDevice {}
|
||||
|
||||
use bitflags::bitflags;
|
||||
use device::{PciBusDevice, PciDeviceInfo, PciDeviceState};
|
||||
use device_api::{device::DeviceInitContext, dma::DmaAllocator};
|
||||
use interrupt::{PciInterruptMap, PciMsiMap};
|
||||
use libk::{
|
||||
dma::DummyDmaAllocator,
|
||||
fs::sysfs::{self, object::KObject},
|
||||
};
|
||||
use libk_mm::address::PhysicalAddress;
|
||||
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
||||
use space::legacy;
|
||||
use yggdrasil_abi::{error::Error, primitive_enum};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use device_api::interrupt::MessageInterruptController;
|
||||
|
||||
pub mod capability;
|
||||
pub mod device;
|
||||
pub mod driver;
|
||||
pub mod interrupt;
|
||||
pub mod macros;
|
||||
mod nodes;
|
||||
mod space;
|
||||
|
||||
pub use space::{
|
||||
ecam::PciEcam,
|
||||
legacy::{LegacyPciAccess, PciLegacyConfigurationSpace},
|
||||
PciConfigSpace, PciConfigurationSpace,
|
||||
};
|
||||
|
||||
bitflags! {
|
||||
/// Command register of the PCI configuration space
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub struct PciCommandRegister: u16 {
|
||||
/// If set, I/O access to the device is enabled
|
||||
const ENABLE_IO = 1 << 0;
|
||||
/// If set, memory-mapped access to the device is enabled
|
||||
const ENABLE_MEMORY = 1 << 1;
|
||||
/// If set, the device can generate PCI bus accesses on its own
|
||||
const BUS_MASTER = 1 << 2;
|
||||
/// If set, interrupts are masked from being raised
|
||||
const DISABLE_INTERRUPTS = 1 << 10;
|
||||
}
|
||||
pub enum PciEndpointImpl {
|
||||
Device(OneTimeInit<PciEndpointDevice>),
|
||||
Bridge(PciBridge),
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Status register of the PCI configuration space
|
||||
pub struct PciStatusRegister: u16 {
|
||||
/// Read-only. If set, the configuration space has a pointer to the capabilities list.
|
||||
const CAPABILITIES_LIST = 1 << 4;
|
||||
}
|
||||
pub struct PciBridge {}
|
||||
|
||||
pub struct PciEndpoint {
|
||||
pub imp: PciEndpointImpl,
|
||||
}
|
||||
|
||||
/// Represents the address of a single object on a bus (or the bus itself)
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct PciAddress {
|
||||
/// PCIe segment group, ignored (?) with PCI
|
||||
pub segment: u8,
|
||||
/// Bus number
|
||||
pub bus: u8,
|
||||
/// Slot/device number
|
||||
pub device: u8,
|
||||
/// Function number
|
||||
pub function: u8,
|
||||
}
|
||||
|
||||
/// Address provided by PCI configuration space Base Address Register
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PciBaseAddress {
|
||||
/// 32-bit memory address
|
||||
Memory32(u32),
|
||||
/// 64-bit memory address
|
||||
Memory64(u64),
|
||||
/// I/O space address
|
||||
Io(u16),
|
||||
}
|
||||
|
||||
primitive_enum! {
|
||||
pub enum PciCapabilityId: u8 {
|
||||
PowerManagement = 0x01,
|
||||
Msi = 0x05,
|
||||
VendorSpecific = 0x09,
|
||||
PciExpress = 0x10,
|
||||
MsiX = 0x11,
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface used for querying PCI capabilities
|
||||
#[allow(unused)]
|
||||
pub trait PciCapability {
|
||||
/// Capability ID
|
||||
const ID: PciCapabilityId;
|
||||
/// Wrapper for accessing the capability data structure
|
||||
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a>;
|
||||
|
||||
fn check<S: PciConfigurationSpace + ?Sized>(space: &S, offset: usize, len: usize) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Constructs an access wrapper for this capability with given offset
|
||||
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
len: usize,
|
||||
) -> Self::CapabilityData<'s, S>;
|
||||
}
|
||||
|
||||
struct BusAddressAllocator {
|
||||
pci_base_64: u64,
|
||||
pci_base_32: u32,
|
||||
// pci_base_io: u16,
|
||||
host_base_64: PhysicalAddress,
|
||||
host_base_32: PhysicalAddress,
|
||||
// host_base_io: PhysicalAddress,
|
||||
size_64: usize,
|
||||
size_32: usize,
|
||||
// size_io: usize,
|
||||
offset_64: u64,
|
||||
offset_32: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PciSegmentInfo {
|
||||
pub segment_number: u8,
|
||||
pub bus_number_start: u8,
|
||||
pub bus_number_end: u8,
|
||||
pub ecam_phys_base: Option<PhysicalAddress>,
|
||||
|
||||
pub irq_translation_map: PciInterruptMap,
|
||||
pub msi_translation_map: PciMsiMap,
|
||||
}
|
||||
|
||||
/// Represents a single PCIe bus segment
|
||||
pub struct PciBusSegment {
|
||||
allocator: Option<BusAddressAllocator>,
|
||||
info: Arc<PciSegmentInfo>,
|
||||
devices: Vec<Arc<KObject<IrqSafeSpinlock<PciBusDevice>>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PciRangeType {
|
||||
Configuration,
|
||||
Io,
|
||||
Memory32,
|
||||
Memory64,
|
||||
}
|
||||
|
||||
pub struct PciAddressRange {
|
||||
pub ty: PciRangeType,
|
||||
pub bus_number: u8,
|
||||
pub pci_base: u64,
|
||||
pub host_base: PhysicalAddress,
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
/// Manager struct to store and control all PCI devices in the system
|
||||
pub struct PciBusManager {
|
||||
segments: Vec<PciBusSegment>,
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
any(target_arch = "x86_64", target_arch = "x86", target_arch = "riscv64"),
|
||||
allow(dead_code)
|
||||
)]
|
||||
impl BusAddressAllocator {
|
||||
pub fn from_ranges(ranges: &[PciAddressRange]) -> Self {
|
||||
let mut range_32 = None;
|
||||
let mut range_64 = None;
|
||||
// let mut range_io = None;
|
||||
|
||||
for range in ranges {
|
||||
let range_val = (range.pci_base, range.host_base, range.size);
|
||||
match range.ty {
|
||||
// PciRangeType::Io if range_io.is_none() => {
|
||||
// range_io.replace(range_val);
|
||||
// }
|
||||
PciRangeType::Memory32 if range_32.is_none() => {
|
||||
range_32.replace(range_val);
|
||||
}
|
||||
PciRangeType::Memory64 if range_64.is_none() => {
|
||||
range_64.replace(range_val);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
let (pci_base_32, host_base_32, size_32) = range_32.unwrap();
|
||||
let (pci_base_64, host_base_64, size_64) = range_64.unwrap();
|
||||
// let (pci_base_io, host_base_io, size_io) = range_io.unwrap();
|
||||
|
||||
Self {
|
||||
pci_base_64,
|
||||
pci_base_32: pci_base_32.try_into().unwrap(),
|
||||
// pci_base_io: pci_base_io.try_into().unwrap(),
|
||||
host_base_64,
|
||||
host_base_32,
|
||||
// host_base_io,
|
||||
size_64,
|
||||
size_32,
|
||||
// size_io,
|
||||
offset_64: 0,
|
||||
offset_32: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocate(&mut self, ty: PciRangeType, size: usize) -> (PciBaseAddress, PhysicalAddress) {
|
||||
match ty {
|
||||
PciRangeType::Io => todo!(),
|
||||
PciRangeType::Memory32 => {
|
||||
if self.offset_32 as usize + size >= self.size_32 {
|
||||
todo!();
|
||||
}
|
||||
let bar = PciBaseAddress::Memory32(self.pci_base_32 + self.offset_32);
|
||||
let host = self.host_base_32.add(self.offset_32 as usize);
|
||||
self.offset_32 += size as u32;
|
||||
(bar, host)
|
||||
}
|
||||
PciRangeType::Memory64 => {
|
||||
if self.offset_64 as usize + size >= self.size_64 {
|
||||
todo!();
|
||||
}
|
||||
let bar = PciBaseAddress::Memory64(self.pci_base_64 + self.offset_64);
|
||||
let host = self.host_base_64.add(self.offset_64 as usize);
|
||||
self.offset_64 += size as u64;
|
||||
(bar, host)
|
||||
}
|
||||
PciRangeType::Configuration => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PciBaseAddress {
|
||||
pub fn as_memory(self) -> Option<PhysicalAddress> {
|
||||
match self {
|
||||
Self::Memory32(address) => Some(PhysicalAddress::from_u64(address as u64)),
|
||||
Self::Memory64(address) => Some(PhysicalAddress::from_u64(address)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> bool {
|
||||
match *self {
|
||||
Self::Memory32(base) => base == 0,
|
||||
Self::Memory64(base) => base == 0,
|
||||
Self::Io(base) => base == 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PciBusSegment {
|
||||
fn probe_config_space(&self, address: PciAddress) -> Result<Option<PciConfigSpace>, Error> {
|
||||
match self.info.ecam_phys_base {
|
||||
Some(ecam_phys_base) => Ok(unsafe {
|
||||
PciEcam::probe_raw_parts(ecam_phys_base, self.info.bus_number_start, address)?
|
||||
}
|
||||
.map(PciConfigSpace::Ecam)),
|
||||
None => Ok(PciLegacyConfigurationSpace::probe(address)?.map(PciConfigSpace::Legacy)),
|
||||
}
|
||||
}
|
||||
|
||||
fn enumerate_function(&mut self, address: PciAddress) -> Result<(), Error> {
|
||||
let Some(config) = self.probe_config_space(address)? else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let header_type = config.header_type();
|
||||
|
||||
// Enumerate multi-function devices
|
||||
if address.function == 0 && header_type & 0x80 != 0 {
|
||||
for function in 1..8 {
|
||||
self.enumerate_function(address.with_function(function))?;
|
||||
}
|
||||
}
|
||||
|
||||
// PCI-to-PCI bridge
|
||||
// if config.class_code() == 0x06 && config.subclass() == 0x04 {
|
||||
// let secondary_bus = config.secondary_bus();
|
||||
// // TODO
|
||||
// }
|
||||
|
||||
if let Some(allocator) = self.allocator.as_mut() {
|
||||
log::debug!("Remapping BARs for {}", address);
|
||||
|
||||
// Find valid BARs
|
||||
let mut i = 0;
|
||||
let mut bar_mask = 0;
|
||||
|
||||
while i < 6 {
|
||||
let w0 = config.read_u32(0x10 + i * 4);
|
||||
|
||||
let bar_width = match w0 & 1 == 0 {
|
||||
// Memory BAR
|
||||
true => match (w0 >> 1) & 3 {
|
||||
// 32-bit BAR
|
||||
0 => 1,
|
||||
// Reserved
|
||||
1 => unimplemented!(),
|
||||
// 64-bit BAR
|
||||
2 => 2,
|
||||
// Unknown
|
||||
_ => unreachable!(),
|
||||
},
|
||||
false => 1,
|
||||
};
|
||||
|
||||
bar_mask |= 1 << i;
|
||||
i += bar_width;
|
||||
}
|
||||
|
||||
for i in 0..6 {
|
||||
if (1 << i) & bar_mask != 0 {
|
||||
let Some(orig_value) = config.bar(i) else {
|
||||
continue;
|
||||
};
|
||||
let size = unsafe { config.bar_size(i) };
|
||||
|
||||
if size != 0 {
|
||||
log::debug!("BAR{}: size={:#x}", i, size);
|
||||
|
||||
match orig_value {
|
||||
PciBaseAddress::Io(_) => (),
|
||||
PciBaseAddress::Memory64(_) => {
|
||||
let (bar, host) = allocator.allocate(PciRangeType::Memory64, size);
|
||||
let bar_address = bar.as_memory().unwrap();
|
||||
unsafe {
|
||||
config.set_bar(i, bar);
|
||||
}
|
||||
log::debug!(
|
||||
"Mapped BAR{} -> pci {:#x} host {:#x}",
|
||||
i,
|
||||
bar_address,
|
||||
host
|
||||
);
|
||||
// TODO Don't yet differentiate between Host/PCI addresses, lol
|
||||
assert_eq!(bar_address, host);
|
||||
}
|
||||
PciBaseAddress::Memory32(_) => {
|
||||
let (bar, host) = allocator.allocate(PciRangeType::Memory32, size);
|
||||
let bar_address = bar.as_memory().unwrap();
|
||||
unsafe {
|
||||
config.set_bar(i, bar);
|
||||
}
|
||||
log::debug!(
|
||||
"Mapped BAR{} -> pci {:#x} host {:#x}",
|
||||
i,
|
||||
bar_address,
|
||||
host
|
||||
);
|
||||
// TODO Don't yet differentiate between Host/PCI addresses, lol
|
||||
assert_eq!(bar_address, host);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let vendor_id = config.vendor_id();
|
||||
let device_id = config.device_id();
|
||||
let class = config.class_code();
|
||||
let subclass = config.subclass();
|
||||
let prog_if = config.prog_if();
|
||||
|
||||
let info = PciDeviceInfo {
|
||||
address,
|
||||
vendor_id,
|
||||
device_id,
|
||||
class,
|
||||
subclass,
|
||||
prog_if,
|
||||
segment: self.info.clone(),
|
||||
config_space: config,
|
||||
interrupt_config: Arc::new(OneTimeInit::new()),
|
||||
};
|
||||
|
||||
let object = nodes::make_sysfs_object(PciBusDevice {
|
||||
info,
|
||||
driver: None,
|
||||
device: None,
|
||||
state: PciDeviceState::None,
|
||||
});
|
||||
let pci_object = PCI_SYSFS_NODE.or_init_with(|| {
|
||||
let bus_object = sysfs::bus().unwrap();
|
||||
let pci_object = KObject::new(());
|
||||
bus_object.add_object("pci", pci_object.clone()).ok();
|
||||
pci_object
|
||||
});
|
||||
|
||||
let name = format!("{address}");
|
||||
pci_object.add_object(name, object.clone()).ok();
|
||||
self.devices.push(object);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn enumerate_bus(&mut self, bus: u8) -> Result<(), Error> {
|
||||
let address = PciAddress::for_bus(self.info.segment_number, bus);
|
||||
|
||||
for i in 0..32 {
|
||||
let device_address = address.with_device(i);
|
||||
|
||||
self.enumerate_function(device_address)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Enumerates the bus segment, placing found devices into the manager
|
||||
pub fn enumerate(&mut self) -> Result<(), Error> {
|
||||
for bus in self.info.bus_number_start..self.info.bus_number_end {
|
||||
self.enumerate_bus(bus)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PciSegmentInfo {
|
||||
pub fn has_msi(&self) -> bool {
|
||||
!matches!(self.msi_translation_map, PciMsiMap::Legacy)
|
||||
}
|
||||
}
|
||||
|
||||
impl PciBusManager {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
segments: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Walks the bus device list and calls init/init_irq functions on any devices with associated
|
||||
/// drivers
|
||||
pub fn probe_bus_devices() -> Result<(), Error> {
|
||||
Self::walk_bus_devices(|device| {
|
||||
probe_bus_device(device, false)?;
|
||||
Ok(true)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn setup_bus_devices(rescan: bool) -> Result<(), Error> {
|
||||
Self::walk_bus_devices(|device| {
|
||||
setup_bus_device(device, rescan)?;
|
||||
Ok(true)
|
||||
})
|
||||
}
|
||||
|
||||
/// Iterates over the bus devices, calling the function on each of them until either an error
|
||||
/// or `Ok(false)` is returned
|
||||
pub fn walk_bus_devices<F: FnMut(&mut PciBusDevice) -> Result<bool, Error>>(
|
||||
mut f: F,
|
||||
) -> Result<(), Error> {
|
||||
let mut this = PCI_MANAGER.lock();
|
||||
|
||||
for segment in this.segments.iter_mut() {
|
||||
for device in segment.devices.iter_mut() {
|
||||
let mut device = device.lock();
|
||||
if !f(&mut device)? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_legacy_segment(access: &'static dyn LegacyPciAccess) -> Result<(), Error> {
|
||||
legacy::PCI.init(access);
|
||||
|
||||
let mut bus_segment = PciBusSegment {
|
||||
info: Arc::new(PciSegmentInfo {
|
||||
segment_number: 0,
|
||||
bus_number_start: 0,
|
||||
bus_number_end: 255,
|
||||
ecam_phys_base: None,
|
||||
irq_translation_map: PciInterruptMap::Legacy,
|
||||
msi_translation_map: PciMsiMap::Legacy,
|
||||
}),
|
||||
allocator: None,
|
||||
devices: Vec::new(),
|
||||
};
|
||||
|
||||
let mut this = PCI_MANAGER.lock();
|
||||
bus_segment.enumerate()?;
|
||||
this.segments.push(bus_segment);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Enumerates a bus segment provided by ACPI MCFG table entry
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub fn add_segment_from_mcfg(
|
||||
entry: &McfgEntry,
|
||||
msi_controller: Arc<dyn MessageInterruptController>,
|
||||
) -> Result<(), Error> {
|
||||
let msi_translation_map = PciMsiMap::Identity(msi_controller);
|
||||
|
||||
let mut bus_segment = PciBusSegment {
|
||||
info: Arc::new(PciSegmentInfo {
|
||||
segment_number: entry.pci_segment_group as u8,
|
||||
bus_number_start: entry.bus_number_start,
|
||||
bus_number_end: entry.bus_number_end,
|
||||
ecam_phys_base: Some(PhysicalAddress::from_u64(entry.base_address)),
|
||||
|
||||
// TODO get the segment's PCI root bridge AML name
|
||||
irq_translation_map: PciInterruptMap::Acpi("\\_SB.PCI0._PRT".into()),
|
||||
msi_translation_map,
|
||||
}),
|
||||
// Firmware done this for us
|
||||
allocator: None,
|
||||
|
||||
devices: Vec::new(),
|
||||
};
|
||||
|
||||
let mut this = PCI_MANAGER.lock();
|
||||
bus_segment.enumerate()?;
|
||||
this.segments.push(bus_segment);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
|
||||
pub fn add_segment_from_device_tree(
|
||||
cfg_base: PhysicalAddress,
|
||||
bus_range: core::ops::Range<u8>,
|
||||
ranges: Vec<PciAddressRange>,
|
||||
irq_translation_map: PciInterruptMap,
|
||||
msi_translation_map: PciMsiMap,
|
||||
) -> Result<(), Error> {
|
||||
let mut bus_segment = PciBusSegment {
|
||||
info: Arc::new(PciSegmentInfo {
|
||||
segment_number: 0,
|
||||
bus_number_start: bus_range.start,
|
||||
bus_number_end: bus_range.end,
|
||||
ecam_phys_base: Some(cfg_base),
|
||||
|
||||
irq_translation_map,
|
||||
msi_translation_map,
|
||||
}),
|
||||
allocator: Some(BusAddressAllocator::from_ranges(&ranges)),
|
||||
|
||||
devices: Vec::new(),
|
||||
};
|
||||
|
||||
let mut this = PCI_MANAGER.lock();
|
||||
bus_segment.enumerate()?;
|
||||
this.segments.push(bus_segment);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PciAddress {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}:{}:{}", self.bus, self.device, self.function)
|
||||
}
|
||||
}
|
||||
|
||||
impl PciAddress {
|
||||
/// Constructs a [PciAddress] representing a bus
|
||||
pub const fn for_bus(segment: u8, bus: u8) -> Self {
|
||||
Self {
|
||||
segment,
|
||||
bus,
|
||||
device: 0,
|
||||
function: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a [PciAddress] representing a specific function
|
||||
pub const fn for_function(segment: u8, bus: u8, device: u8, function: u8) -> Self {
|
||||
Self {
|
||||
segment,
|
||||
bus,
|
||||
device,
|
||||
function,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a [PciAddress] representing a device on a given bus
|
||||
pub const fn with_device(self, device: u8) -> Self {
|
||||
Self {
|
||||
device,
|
||||
function: 0,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a [PciAddress] representing a function of a given bus device
|
||||
pub const fn with_function(self, function: u8) -> Self {
|
||||
Self { function, ..self }
|
||||
}
|
||||
}
|
||||
|
||||
impl PciConfigurationSpace for PciConfigSpace {
|
||||
fn read_u32(&self, offset: usize) -> u32 {
|
||||
match self {
|
||||
Self::Ecam(ecam) => ecam.read_u32(offset),
|
||||
Self::Legacy(legacy) => legacy.read_u32(offset),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_u32(&self, offset: usize, value: u32) {
|
||||
match self {
|
||||
Self::Ecam(ecam) => ecam.write_u32(offset, value),
|
||||
Self::Legacy(legacy) => legacy.write_u32(offset, value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn probe_bus_device(device: &mut PciBusDevice, _rescan: bool) -> Result<(), Error> {
|
||||
// Already has a driver/device set up
|
||||
if device.device.is_some() || device.state != PciDeviceState::None {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(driver) = driver::lookup_driver(&device.info) {
|
||||
let dma: Arc<dyn DmaAllocator> = Arc::new(DummyDmaAllocator);
|
||||
|
||||
match driver.probe(&device.info, &dma) {
|
||||
Ok(instance) => {
|
||||
log::info!("{} -> {}", device.info.address, driver.driver_name());
|
||||
device.device.replace(instance);
|
||||
device.driver.replace(driver);
|
||||
device.state = PciDeviceState::Probed;
|
||||
}
|
||||
Err(error) => {
|
||||
log::error!(
|
||||
"{} ({}) probe error: {error:?}",
|
||||
device.info.address,
|
||||
driver.driver_name()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_bus_device(device: &mut PciBusDevice, _rescan: bool) -> Result<(), Error> {
|
||||
// No driver yet (TODO probe if rescan is asked)
|
||||
let (Some(dev), Some(driver)) = (device.device.as_ref(), device.driver) else {
|
||||
return Ok(());
|
||||
};
|
||||
// Already initialized/failed
|
||||
if device.state != PciDeviceState::Probed {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let dma: Arc<dyn DmaAllocator> = Arc::new(DummyDmaAllocator);
|
||||
let cx = DeviceInitContext {
|
||||
dma_allocator: dma.clone(),
|
||||
};
|
||||
|
||||
match unsafe { dev.clone().init(cx) } {
|
||||
Ok(()) => {
|
||||
device.state = PciDeviceState::Initialized;
|
||||
}
|
||||
Err(error) => {
|
||||
log::error!(
|
||||
"{} ({}) setup error: {error:?}",
|
||||
device.info.address,
|
||||
driver.driver_name()
|
||||
);
|
||||
device.state = PciDeviceState::Failed;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
static PCI_MANAGER: IrqSafeSpinlock<PciBusManager> = IrqSafeSpinlock::new(PciBusManager::new());
|
||||
static PCI_SYSFS_NODE: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
pub macro pci_driver_match {
|
||||
(class ($class:literal:$subclass:literal:$prog_if:literal)) => {
|
||||
$crate::driver::PciMatch::Class($class, Some($subclass), Some($prog_if))
|
||||
},
|
||||
(class ($class:literal:$subclass:literal)) => {
|
||||
$crate::driver::PciMatch::Class($class, Some($subclass), None)
|
||||
},
|
||||
(class $class:literal) => {
|
||||
$crate::driver::PciMatch::Class($class, None, None)
|
||||
},
|
||||
(device ($vendor:literal:$device:literal)) => {
|
||||
$crate::driver::PciMatch::Vendor($vendor, $device)
|
||||
}
|
||||
}
|
||||
|
||||
pub macro pci_driver(
|
||||
matches: [$($kind:ident $match:tt),+ $(,)?],
|
||||
driver: $driver:tt
|
||||
) {
|
||||
#[link_section = ".init_array"]
|
||||
#[used]
|
||||
static __REGISTER_FN: extern "C" fn() = __register_fn;
|
||||
|
||||
extern "C" fn __register_fn() {
|
||||
struct Driver;
|
||||
impl $crate::driver::PciDriver for Driver $driver
|
||||
static DRIVER: Driver = Driver;
|
||||
|
||||
log::info!("register pci driver: {:?}", $crate::driver::PciDriver::driver_name(&Driver));
|
||||
$(
|
||||
let pmatch = $crate::macros::pci_driver_match!($kind $match);
|
||||
$crate::driver::register_match(pmatch, &DRIVER);
|
||||
)+
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
use alloc::{format, string::String, sync::Arc};
|
||||
use libk::{
|
||||
error::Error,
|
||||
fs::sysfs::{
|
||||
attribute::{StringAttribute, StringAttributeOps},
|
||||
object::KObject,
|
||||
},
|
||||
};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
|
||||
use crate::{device::PciBusDevice, PciBaseAddress, PciCapabilityId, PciConfigurationSpace};
|
||||
|
||||
pub(crate) fn make_sysfs_object(
|
||||
device: PciBusDevice,
|
||||
) -> Arc<KObject<IrqSafeSpinlock<PciBusDevice>>> {
|
||||
struct Resources;
|
||||
struct Capabilities;
|
||||
struct Driver;
|
||||
struct Class;
|
||||
struct Id;
|
||||
|
||||
impl StringAttributeOps for Driver {
|
||||
type Data = IrqSafeSpinlock<PciBusDevice>;
|
||||
const NAME: &'static str = "driver";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
let state = state.lock();
|
||||
if let Some(driver) = state.driver.map(|driver| driver.driver_name()) {
|
||||
Ok(driver.into())
|
||||
} else {
|
||||
Ok("".into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StringAttributeOps for Id {
|
||||
type Data = IrqSafeSpinlock<PciBusDevice>;
|
||||
const NAME: &'static str = "id";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
let state = state.lock();
|
||||
Ok(format!(
|
||||
"{:04x}:{:04x}",
|
||||
state.info.vendor_id, state.info.device_id
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl StringAttributeOps for Class {
|
||||
type Data = IrqSafeSpinlock<PciBusDevice>;
|
||||
const NAME: &'static str = "class";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
let state = state.lock();
|
||||
Ok(format!(
|
||||
"{:02x}:{:02x}:{:02x}",
|
||||
state.info.class, state.info.subclass, state.info.prog_if
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl StringAttributeOps for Resources {
|
||||
type Data = IrqSafeSpinlock<PciBusDevice>;
|
||||
const NAME: &'static str = "resources";
|
||||
const NEWLINE: bool = false;
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
use core::fmt::Write;
|
||||
|
||||
let state = state.lock();
|
||||
let mut output = String::new();
|
||||
for i in 0..6 {
|
||||
if let Some(bar) = state.info.config_space.bar(i) {
|
||||
if bar.is_zero() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match bar {
|
||||
PciBaseAddress::Io(base) => {
|
||||
writeln!(output, "{i}:pio:{base:#06x}").ok();
|
||||
}
|
||||
PciBaseAddress::Memory32(base) => {
|
||||
writeln!(output, "{i}:m32:{base:#010x}").ok();
|
||||
}
|
||||
PciBaseAddress::Memory64(base) => {
|
||||
writeln!(output, "{i}:m64:{base:#018x}").ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if output.is_empty() {
|
||||
output.push('\n');
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl StringAttributeOps for Capabilities {
|
||||
type Data = IrqSafeSpinlock<PciBusDevice>;
|
||||
const NAME: &'static str = "capabilities";
|
||||
const NEWLINE: bool = false;
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
use core::fmt::Write;
|
||||
let state = state.lock();
|
||||
let mut output = String::new();
|
||||
for (capability, offset, _) in state.info.config_space.capability_iter() {
|
||||
write!(output, "{offset:04x}:").ok();
|
||||
match capability {
|
||||
Some(PciCapabilityId::Msi) => write!(output, "msi").ok(),
|
||||
Some(PciCapabilityId::MsiX) => write!(output, "msix").ok(),
|
||||
Some(PciCapabilityId::VendorSpecific) => write!(output, "vendor-specific").ok(),
|
||||
Some(PciCapabilityId::PciExpress) => write!(output, "pcie").ok(),
|
||||
Some(PciCapabilityId::PowerManagement) => {
|
||||
write!(output, "power-management").ok()
|
||||
}
|
||||
None => write!(output, "unknown").ok(),
|
||||
};
|
||||
writeln!(output).ok();
|
||||
}
|
||||
if output.is_empty() {
|
||||
output.push('\n');
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
let object = KObject::new(IrqSafeSpinlock::new(device));
|
||||
|
||||
object
|
||||
.add_attribute(StringAttribute::from(Capabilities))
|
||||
.ok();
|
||||
object.add_attribute(StringAttribute::from(Resources)).ok();
|
||||
object.add_attribute(StringAttribute::from(Driver)).ok();
|
||||
object.add_attribute(StringAttribute::from(Class)).ok();
|
||||
object.add_attribute(StringAttribute::from(Id)).ok();
|
||||
|
||||
object
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
//! PCI Express ECAM interface
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryMapping};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use super::{PciAddress, PciConfigurationSpace};
|
||||
|
||||
/// PCI Express Enhanced Configuration Access Mechanism
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct PciEcam {
|
||||
mapping: DeviceMemoryMapping,
|
||||
}
|
||||
|
||||
impl PciConfigurationSpace for PciEcam {
|
||||
fn read_u32(&self, offset: usize) -> u32 {
|
||||
assert_eq!(offset & 3, 0);
|
||||
unsafe { ((self.mapping.address() + offset) as *const u32).read_volatile() }
|
||||
}
|
||||
|
||||
fn write_u32(&self, offset: usize, value: u32) {
|
||||
assert_eq!(offset & 3, 0);
|
||||
unsafe { ((self.mapping.address() + offset) as *mut u32).write_volatile(value) }
|
||||
}
|
||||
}
|
||||
|
||||
impl PciEcam {
|
||||
/// Maps the physical address of a ECAM space for kernel access.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The `phys_addr` must be a valid ECAM address. The address must not alias any other mapped
|
||||
/// regions. The address must be aligned to a 4KiB boundary and be valid for accesses within a
|
||||
/// 4KiB-sized range.
|
||||
pub unsafe fn map(phys_addr: PhysicalAddress) -> Result<Self, Error> {
|
||||
let mapping = DeviceMemoryMapping::map(phys_addr, 0x1000, Default::default())?;
|
||||
Ok(Self { mapping })
|
||||
}
|
||||
|
||||
/// Checks if the ECAM contains a valid device configuration space, mapping and returning a
|
||||
/// [PciEcam] if it does.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// See [PciEcam::map].
|
||||
pub unsafe fn probe_raw_parts(
|
||||
segment_phys_addr: PhysicalAddress,
|
||||
bus_offset: u8,
|
||||
address: PciAddress,
|
||||
) -> Result<Option<Self>, Error> {
|
||||
let phys_addr = segment_phys_addr.add(
|
||||
((address.bus - bus_offset) as usize * 256
|
||||
+ address.device as usize * 8
|
||||
+ address.function as usize)
|
||||
* 0x1000,
|
||||
);
|
||||
let this = Self::map(phys_addr)?;
|
||||
|
||||
Ok(if this.is_valid() { Some(this) } else { None })
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
use libk_util::OneTimeInit;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{PciAddress, PciConfigurationSpace};
|
||||
|
||||
/// Provides access to the legacy (port I/O-driven) PCI configuration space
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct PciLegacyConfigurationSpace {
|
||||
address: PciAddress,
|
||||
}
|
||||
|
||||
pub trait LegacyPciAccess {
|
||||
fn read_u32(&self, bus: u8, slot: u8, func: u8, offset: u8) -> u32;
|
||||
fn write_u32(&self, bus: u8, slot: u8, func: u8, offset: u8, value: u32);
|
||||
}
|
||||
|
||||
impl PciConfigurationSpace for PciLegacyConfigurationSpace {
|
||||
fn read_u32(&self, offset: usize) -> u32 {
|
||||
PCI.get().read_u32(
|
||||
self.address.bus,
|
||||
self.address.device,
|
||||
self.address.function,
|
||||
offset as _,
|
||||
)
|
||||
}
|
||||
|
||||
fn write_u32(&self, offset: usize, value: u32) {
|
||||
PCI.get().write_u32(
|
||||
self.address.bus,
|
||||
self.address.device,
|
||||
self.address.function,
|
||||
offset as _,
|
||||
value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PciLegacyConfigurationSpace {
|
||||
pub fn probe(address: PciAddress) -> Result<Option<Self>, Error> {
|
||||
let this = PciLegacyConfigurationSpace { address };
|
||||
Ok(if this.is_valid() { Some(this) } else { None })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) static PCI: OneTimeInit<&'static dyn LegacyPciAccess> = OneTimeInit::new();
|
||||
@@ -1,394 +0,0 @@
|
||||
use alloc::sync::Arc;
|
||||
use legacy::PciLegacyConfigurationSpace;
|
||||
|
||||
use super::{PciAddress, PciBaseAddress, PciCapability, PciCapabilityId, PciEcam};
|
||||
use crate::{device::PciInterruptPin, PciCommandRegister, PciStatusRegister};
|
||||
|
||||
pub(super) mod ecam;
|
||||
pub(super) mod legacy;
|
||||
|
||||
macro_rules! pci_config_field_getter {
|
||||
($self:ident, u32, $offset:expr) => {
|
||||
$self.read_u32($offset)
|
||||
};
|
||||
|
||||
($self:ident, u16, $offset:expr) => {
|
||||
$self.read_u16($offset)
|
||||
};
|
||||
|
||||
($self:ident, u8, $offset:expr) => {
|
||||
$self.read_u8($offset)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! pci_config_field_setter {
|
||||
($self:ident, u32, $offset:expr, $value:expr) => {
|
||||
$self.write_u32($offset, $value)
|
||||
};
|
||||
|
||||
($self:ident, u16, $offset:expr, $value:expr) => {{
|
||||
$self.write_u16($offset, $value)
|
||||
}};
|
||||
|
||||
($self:ident, u8, $offset:expr, $value:expr) => {
|
||||
$self.write_u8($offset, $value)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! pci_config_field {
|
||||
(
|
||||
$offset:expr => $ty:ident,
|
||||
$(#[$getter_meta:meta])* $getter:ident
|
||||
$(, $(#[$setter_meta:meta])* $setter:ident)?
|
||||
) => {
|
||||
$(#[$getter_meta])*
|
||||
fn $getter(&self) -> $ty {
|
||||
pci_config_field_getter!(self, $ty, $offset)
|
||||
}
|
||||
|
||||
$(
|
||||
$(#[$setter_meta])*
|
||||
fn $setter(&self, value: $ty) {
|
||||
pci_config_field_setter!(self, $ty, $offset, value)
|
||||
}
|
||||
)?
|
||||
};
|
||||
}
|
||||
|
||||
/// Describes a configuration space access method for a PCI device
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum PciConfigSpace {
|
||||
/// Legacy configuration space.
|
||||
///
|
||||
/// See [PciLegacyConfigurationSpace].
|
||||
Legacy(PciLegacyConfigurationSpace),
|
||||
|
||||
/// Enhanced Configuration Access Mechanism (PCIe).
|
||||
///
|
||||
/// See [PciEcam].
|
||||
Ecam(PciEcam),
|
||||
}
|
||||
|
||||
pub struct CapabilityIterator<'s, S: PciConfigurationSpace + ?Sized> {
|
||||
space: &'s S,
|
||||
current: Option<usize>,
|
||||
}
|
||||
|
||||
impl<S: PciConfigurationSpace + ?Sized> Iterator for CapabilityIterator<'_, S> {
|
||||
type Item = (Option<PciCapabilityId>, usize, usize);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let offset = self.current? & !0x3;
|
||||
|
||||
let id = PciCapabilityId::try_from(self.space.read_u8(offset)).ok();
|
||||
let len = self.space.read_u8(offset + 2);
|
||||
let next_pointer = self.space.read_u8(offset + 1);
|
||||
|
||||
self.current = if next_pointer != 0 {
|
||||
Some(next_pointer as usize)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Some((id, offset, len as usize))
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface for accessing the configuration space of a device
|
||||
pub trait PciConfigurationSpace {
|
||||
/// Reads a 32-bit value from the device configuration space.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The `offset` must be u32-aligned.
|
||||
fn read_u32(&self, offset: usize) -> u32;
|
||||
|
||||
/// Writes a 32-bit value to the device configuration space.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The `offset` must be u32-aligned.
|
||||
fn write_u32(&self, offset: usize, value: u32);
|
||||
|
||||
/// Reads a 16-bit value from the device configuration space.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The `offset` must be u16-aligned.
|
||||
fn read_u16(&self, offset: usize) -> u16 {
|
||||
assert_eq!(offset & 1, 0);
|
||||
let value = self.read_u32(offset & !3);
|
||||
(value >> ((offset & 3) * 8)) as u16
|
||||
}
|
||||
|
||||
/// Reads a byte from the device configuration space
|
||||
fn read_u8(&self, offset: usize) -> u8 {
|
||||
let value = self.read_u32(offset & !3);
|
||||
(value >> ((offset & 3) * 8)) as u8
|
||||
}
|
||||
|
||||
/// Writes a 16-bit value to the device configuration space.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The `offset` must be u16-aligned.
|
||||
fn write_u16(&self, offset: usize, value: u16) {
|
||||
let shift = ((offset >> 1) & 1) << 4;
|
||||
assert_eq!(offset & 1, 0);
|
||||
let mut tmp = self.read_u32(offset & !3);
|
||||
tmp &= !(0xFFFF << shift);
|
||||
tmp |= (value as u32) << shift;
|
||||
self.write_u32(offset & !3, tmp);
|
||||
}
|
||||
|
||||
/// Writes a byte to the device configuration space
|
||||
fn write_u8(&self, _offset: usize, _value: u16) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns `true` if the device is present on the bus (i.e. configuration space is not filled
|
||||
/// with only 1's)
|
||||
fn is_valid(&self) -> bool {
|
||||
self.vendor_id() != 0xFFFF && self.device_id() != 0xFFFF
|
||||
}
|
||||
|
||||
pci_config_field!(
|
||||
0x00 => u16,
|
||||
#[doc = "Returns the Vendor ID"] vendor_id
|
||||
);
|
||||
pci_config_field!(0x02 => u16,
|
||||
#[doc = "Returns the Device ID"] device_id
|
||||
);
|
||||
pci_config_field!(
|
||||
0x04 => u16,
|
||||
#[doc = "Returns the value of the command register"] command,
|
||||
#[doc = "Writes to the command word register"] set_command
|
||||
);
|
||||
pci_config_field!(
|
||||
0x06 => u16,
|
||||
#[doc = "Returns the value of the status register"] status
|
||||
);
|
||||
|
||||
pci_config_field!(
|
||||
0x08 => u8,
|
||||
#[doc = "Returns the device Revision ID"]
|
||||
rev_id
|
||||
);
|
||||
pci_config_field!(
|
||||
0x09 => u8,
|
||||
#[doc = "Returns the device Prog IF field"]
|
||||
prog_if
|
||||
);
|
||||
pci_config_field!(
|
||||
0x0A => u8,
|
||||
#[doc = "Returns the device Subclass field"]
|
||||
subclass
|
||||
);
|
||||
pci_config_field!(
|
||||
0x0B => u8,
|
||||
#[doc = "Returns the device Class Code field"]
|
||||
class_code
|
||||
);
|
||||
|
||||
// ...
|
||||
pci_config_field!(
|
||||
0x0E => u8,
|
||||
#[doc = "Returns the header type of the device"]
|
||||
header_type
|
||||
);
|
||||
pci_config_field!(
|
||||
0x19 => u8,
|
||||
#[doc = r#"
|
||||
Returns the secondary bus number associated with this device
|
||||
|
||||
# Note
|
||||
|
||||
The function is only valid for devices with `header_type() == 1`
|
||||
"#]
|
||||
secondary_bus
|
||||
);
|
||||
pci_config_field!(
|
||||
0x34 => u8,
|
||||
#[doc =
|
||||
r"Returns the offset within the configuration space where the Capabilities List
|
||||
is located. Only valid if the corresponding Status Register bit is set"
|
||||
]
|
||||
capability_pointer
|
||||
);
|
||||
|
||||
fn interrupt_pin(&self) -> Option<PciInterruptPin> {
|
||||
PciInterruptPin::try_from(self.read_u8(0x3D) as u32).ok()
|
||||
}
|
||||
|
||||
fn interrupt_line(&self) -> Option<u8> {
|
||||
let value = self.read_u8(0x3C);
|
||||
if value < 16 {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is only meant to be called before the device has seen any use by the OS,
|
||||
/// it has not been tested outside of this use case.
|
||||
unsafe fn bar_size(&self, index: usize) -> usize {
|
||||
let cmd = self.command();
|
||||
|
||||
// Disable I/O and memory
|
||||
self.set_command(
|
||||
cmd & !(PciCommandRegister::ENABLE_IO | PciCommandRegister::ENABLE_MEMORY).bits(),
|
||||
);
|
||||
|
||||
let orig_value = self.bar(index).unwrap();
|
||||
// TODO preserve prefetch bit
|
||||
let mask_value = match orig_value {
|
||||
PciBaseAddress::Io(_) => PciBaseAddress::Io(0xFFFC),
|
||||
PciBaseAddress::Memory32(_) => PciBaseAddress::Memory32(0xFFFFFFF0),
|
||||
PciBaseAddress::Memory64(_) => PciBaseAddress::Memory64(0xFFFFFFFFFFFFFFF0),
|
||||
};
|
||||
self.set_bar(index, mask_value);
|
||||
let new_value = self.bar(index).unwrap();
|
||||
|
||||
let size = match new_value {
|
||||
PciBaseAddress::Io(address) if address != 0 => ((!address) + 1) as usize,
|
||||
PciBaseAddress::Memory32(address) if address != 0 => ((!address) + 1) as usize,
|
||||
PciBaseAddress::Memory64(address) if address != 0 => ((!address) + 1) as usize,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
self.set_bar(index, orig_value);
|
||||
self.set_command(cmd);
|
||||
|
||||
size
|
||||
}
|
||||
|
||||
/// Updates the value of the Base Address Register with given index.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The function is only valid for devices with `header_type() == 0`
|
||||
///
|
||||
/// The `index` corresponds to the actual configuration space BAR index.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Precondition: the device must have memory access disabled through its command register
|
||||
/// prior to setting a BAR.
|
||||
unsafe fn set_bar(&self, index: usize, value: PciBaseAddress) {
|
||||
assert!(index < 6);
|
||||
|
||||
match value {
|
||||
PciBaseAddress::Io(value) => {
|
||||
self.write_u32(0x10 + index * 4, ((value as u32) & !0x3) | 1)
|
||||
}
|
||||
PciBaseAddress::Memory32(address) => self.write_u32(0x10 + index * 4, address & !0xF),
|
||||
PciBaseAddress::Memory64(address) => {
|
||||
self.write_u32(0x10 + index * 4, ((address as u32) & !0xF) | (2 << 1));
|
||||
self.write_u32(0x10 + (index + 1) * 4, (address >> 32) as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the value of the Base Address Register with given index.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The function is only valid for devices with `header_type() == 0`
|
||||
///
|
||||
/// The `index` corresponds to the actual configuration space BAR index, i.e. if a 64-bit
|
||||
/// address occupies [BAR0, BAR1] and BAR 1 is requested, the function will return [None].
|
||||
fn bar(&self, index: usize) -> Option<PciBaseAddress> {
|
||||
assert!(index < 6);
|
||||
|
||||
if index % 2 == 0 {
|
||||
let w0 = self.read_u32(0x10 + index * 4);
|
||||
|
||||
match w0 & 1 {
|
||||
0 => match (w0 >> 1) & 3 {
|
||||
0 => {
|
||||
// 32-bit memory BAR
|
||||
Some(PciBaseAddress::Memory32(w0 & !0xF))
|
||||
}
|
||||
2 => {
|
||||
// 64-bit memory BAR
|
||||
let w1 = self.read_u32(0x10 + (index + 1) * 4);
|
||||
Some(PciBaseAddress::Memory64(
|
||||
((w1 as u64) << 32) | ((w0 as u64) & !0xF),
|
||||
))
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
1 => Some(PciBaseAddress::Io((w0 as u16) & !0x3)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
let prev_w0 = self.read_u32(0x10 + (index - 1) * 4);
|
||||
if prev_w0 & 0x7 == 0x4 {
|
||||
// Previous BAR is 64-bit memory and this one is its continuation
|
||||
return None;
|
||||
}
|
||||
|
||||
let w0 = self.read_u32(0x10 + index * 4);
|
||||
|
||||
match w0 & 1 {
|
||||
0 => match (w0 >> 1) & 3 {
|
||||
0 => {
|
||||
// 32-bit memory BAR
|
||||
Some(PciBaseAddress::Memory32(w0 & !0xF))
|
||||
}
|
||||
// TODO can 64-bit BARs not be on a 64-bit boundary?
|
||||
2 => None,
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
1 => todo!(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over the PCI capabilities
|
||||
fn capability_iter(&self) -> CapabilityIterator<'_, Self> {
|
||||
let status = PciStatusRegister::from_bits_retain(self.status());
|
||||
|
||||
let current = if status.contains(PciStatusRegister::CAPABILITIES_LIST) {
|
||||
let ptr = self.capability_pointer() as usize;
|
||||
|
||||
if ptr != 0 {
|
||||
Some(self.capability_pointer() as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
// Return an empty iterator
|
||||
None
|
||||
};
|
||||
|
||||
CapabilityIterator {
|
||||
space: self,
|
||||
current,
|
||||
}
|
||||
}
|
||||
|
||||
/// Locates a capability within this configuration space
|
||||
fn capability<C: PciCapability>(&self) -> Option<C::CapabilityData<'_, Self>> {
|
||||
self.capability_iter().find_map(|(id, offset, len)| {
|
||||
if id == Some(C::ID) && C::check(self, offset, len) {
|
||||
Some(C::data(self, offset, len))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PciConfigurationSpace> PciConfigurationSpace for Arc<T> {
|
||||
fn read_u32(&self, offset: usize) -> u32 {
|
||||
T::read_u32(self.as_ref(), offset)
|
||||
}
|
||||
|
||||
fn write_u32(&self, offset: usize, value: u32) {
|
||||
T::write_u32(self.as_ref(), offset, value);
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::device::Device;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum InputPinBias {
|
||||
PullUp,
|
||||
PullDown,
|
||||
Floating,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum OutputPinBias {
|
||||
PushPull,
|
||||
OpenDrain,
|
||||
HighZ,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GpioPinLevel {
|
||||
High,
|
||||
Low,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SinglePinDirection {
|
||||
Output,
|
||||
Input,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GpioInterruptMode {
|
||||
RisingEdge,
|
||||
FallingEdge,
|
||||
BothEdges,
|
||||
HighLevel,
|
||||
LowLevel,
|
||||
}
|
||||
|
||||
pub struct GpioInterruptEvent {
|
||||
pub mode: GpioInterruptMode,
|
||||
pub event: u64,
|
||||
}
|
||||
|
||||
pub struct GpioInterrupt {
|
||||
pub mode: GpioInterruptMode,
|
||||
pub handler: Box<dyn Fn() + Send>,
|
||||
}
|
||||
|
||||
pub struct GpioPinConfig {
|
||||
pub input: bool,
|
||||
pub output: bool,
|
||||
pub input_bias: Option<InputPinBias>,
|
||||
pub output_bias: Option<OutputPinBias>,
|
||||
pub active_level: GpioPinLevel,
|
||||
pub initial_level: GpioPinLevel,
|
||||
}
|
||||
|
||||
pub struct PinHandle {
|
||||
pub index: u32,
|
||||
pub config: GpioPinConfig,
|
||||
pub parent: Arc<dyn GpioController>,
|
||||
}
|
||||
|
||||
pub trait GpioController: Device {
|
||||
fn write_gpio(&self, pin: &PinHandle, value: bool) -> Result<(), Error>;
|
||||
fn read_gpio(&self, pin: &PinHandle) -> Result<bool, Error>;
|
||||
|
||||
fn setup_gpio(&self, pin: &PinHandle, event: Option<GpioInterruptEvent>) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub trait GpioOutput: Sync + Send {
|
||||
fn write(&self, value: bool) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
impl PinHandle {
|
||||
pub fn configure(&self) -> Result<(), Error> {
|
||||
self.parent.setup_gpio(self, None)
|
||||
}
|
||||
|
||||
pub fn write(&self, value: bool) -> Result<(), Error> {
|
||||
self.parent.write_gpio(self, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl GpioPinConfig {
|
||||
pub fn force_single_direction(&self) -> Option<SinglePinDirection> {
|
||||
match (self.input, self.output) {
|
||||
(true, false) => Some(SinglePinDirection::Input),
|
||||
(false, true) => Some(SinglePinDirection::Output),
|
||||
(false, false) => None,
|
||||
(true, true) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ extern crate alloc;
|
||||
pub mod bus;
|
||||
pub mod clock;
|
||||
pub mod device;
|
||||
pub mod gpio;
|
||||
pub mod interrupt;
|
||||
pub mod serial;
|
||||
pub mod timer;
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
gpio::PinHandle,
|
||||
interrupt::FullIrq,
|
||||
};
|
||||
use fdt_rs::spec::Phandle;
|
||||
|
||||
use crate::{driver::DeviceTreeGpioPins, DeviceTreePropertyRead, TProp};
|
||||
use crate::{DeviceTreePropertyRead, TProp};
|
||||
|
||||
use super::{lookup_phandle, Node};
|
||||
|
||||
@@ -21,12 +20,6 @@ pub(crate) struct ResetIter<'dt> {
|
||||
pub(crate) offset: usize,
|
||||
}
|
||||
|
||||
pub(crate) struct GpioIter<'o, 'dt> {
|
||||
pub(crate) options: &'o DeviceTreeGpioPins,
|
||||
pub(crate) gpios: TProp<'dt>,
|
||||
pub(crate) offset: usize,
|
||||
}
|
||||
|
||||
impl Iterator for ClockIter<'_> {
|
||||
type Item = ClockHandle;
|
||||
|
||||
@@ -59,22 +52,6 @@ impl Iterator for ResetIter<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for GpioIter<'_, '_> {
|
||||
type Item = PinHandle;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.offset >= self.gpios.len() {
|
||||
return None;
|
||||
}
|
||||
let phandle = self.gpios.read_cell(self.offset, 1)? as Phandle;
|
||||
let gpioc = lookup_phandle(phandle, true)?;
|
||||
let gpioc = gpioc.as_pin_controller()?;
|
||||
let (pin, len) = gpioc.map_gpio(&self.gpios, self.offset + 1, self.options)?;
|
||||
self.offset += len + 1;
|
||||
Some(pin)
|
||||
}
|
||||
}
|
||||
|
||||
// Interrupt controller handling
|
||||
|
||||
/// Reads interrupt information, as interpreted by `interrupt_controller`, from `property` at a
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//! Device tree-based driver definitions
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use device_api::gpio::GpioPinLevel;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
mod controller;
|
||||
@@ -18,21 +17,11 @@ pub use macros::device_tree_driver;
|
||||
pub use registry::{lookup_phandle, register_driver};
|
||||
pub use syscon::DeviceTreeSyscon;
|
||||
pub use traits::{
|
||||
DeviceTreeClockController, DeviceTreeInterruptController, DeviceTreePinController,
|
||||
DeviceTreeResetController, Driver, ProbeContext,
|
||||
DeviceTreeClockController, DeviceTreeInterruptController, DeviceTreeResetController, Driver,
|
||||
ProbeContext,
|
||||
};
|
||||
pub use tree::{find_node, unflatten_device_tree, walk_device_tree, Node};
|
||||
|
||||
/// Contextual information about a GPIO pin being configured
|
||||
pub struct DeviceTreeGpioPins {
|
||||
/// Whether to enable input on the pin
|
||||
pub input: bool,
|
||||
/// Whether to enable output on the pin
|
||||
pub output: bool,
|
||||
/// Initial level to configure the GPIO with
|
||||
pub initial_level: GpioPinLevel,
|
||||
}
|
||||
|
||||
/// Specifies initialization sequence requirement for a driver
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum InitSequence {
|
||||
|
||||
@@ -6,12 +6,10 @@ use device_api::{
|
||||
bus::Bus,
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
device::Device,
|
||||
gpio::PinHandle,
|
||||
interrupt::{ExternalInterruptController, FullIrq},
|
||||
};
|
||||
use libk::error::Error;
|
||||
|
||||
use crate::{driver::DeviceTreeGpioPins, TProp};
|
||||
use crate::TProp;
|
||||
|
||||
use super::{InitSequence, Node};
|
||||
|
||||
@@ -53,20 +51,6 @@ pub struct ProbeContext {
|
||||
pub sequence: Option<InitSequence>,
|
||||
}
|
||||
|
||||
/// `-pinctrl`-type devices, used for configuring pin groups
|
||||
pub trait DeviceTreePinController {
|
||||
/// Configure a pin group
|
||||
fn configure_pin_group(self: Arc<Self>, pins: &Arc<Node>) -> Result<(), Error>;
|
||||
/// Reads GPIO pin information from `property` at given `offset` and maps it to a
|
||||
/// [PinHandle], returning the handle + the size of the GPIO entry.
|
||||
fn map_gpio(
|
||||
self: Arc<Self>,
|
||||
property: &TProp,
|
||||
offset: usize,
|
||||
info: &DeviceTreeGpioPins,
|
||||
) -> Option<(PinHandle, usize)>;
|
||||
}
|
||||
|
||||
impl ProbeContext {
|
||||
/// See [Node::map_range]
|
||||
pub fn map_range(&self, range: Range<u64>) -> Option<Range<u64>> {
|
||||
|
||||
@@ -9,7 +9,6 @@ use device_api::{
|
||||
bus::Bus,
|
||||
clock::{ClockController, ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::PinHandle,
|
||||
interrupt::{ExternalInterruptController, FullIrq, MessageInterruptController},
|
||||
};
|
||||
use fdt_rs::spec::Phandle;
|
||||
@@ -19,10 +18,7 @@ use libk_util::OneTimeInit;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
driver::{
|
||||
controller::GpioIter, traits::DeviceTreePinController, DeviceTreeGpioPins, DeviceTreeSyscon,
|
||||
},
|
||||
tree, DeviceTree, DeviceTreeNodeExt, DeviceTreePropertyRead, TNode, TProp,
|
||||
driver::DeviceTreeSyscon, DeviceTree, DeviceTreeNodeExt, DeviceTreePropertyRead, TNode, TProp,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -59,7 +55,6 @@ pub struct Node {
|
||||
pub(crate) clock_controler: OneTimeInit<Arc<dyn DeviceTreeClockController>>,
|
||||
pub(crate) reset_controller: OneTimeInit<Arc<dyn DeviceTreeResetController>>,
|
||||
pub(crate) system_controller: OneTimeInit<Arc<dyn DeviceTreeSyscon>>,
|
||||
pub(crate) pin_controller: OneTimeInit<Arc<dyn DeviceTreePinController>>,
|
||||
}
|
||||
|
||||
pub(crate) struct ProbedDevice {
|
||||
@@ -95,7 +90,6 @@ impl NodeDevice {
|
||||
|
||||
impl Node {
|
||||
fn probe_single(node: &Arc<Node>, cx: &mut ProbeContext) -> Option<ProbedDevice> {
|
||||
let name = node.name();
|
||||
let compatible = node.compatible.as_ref()?;
|
||||
let drivers = DRIVERS.read();
|
||||
|
||||
@@ -106,6 +100,7 @@ impl Node {
|
||||
|
||||
if libk::config::get().device_tree.log_missing {
|
||||
if driver.is_none() {
|
||||
let name = node.name();
|
||||
// FIXME don't spam virtio missing stuff
|
||||
if !name.map_or(false, |n| n.starts_with("virtio_mmio")) {
|
||||
for (i, compatible) in compatible.as_str_list().enumerate() {
|
||||
@@ -120,11 +115,6 @@ impl Node {
|
||||
}
|
||||
let driver = driver?;
|
||||
|
||||
// Initialize default pinctrl before probing
|
||||
node.setup_pins()
|
||||
.inspect_err(|e| log::error!("{name:?}: pinctrl init error {e:?}"))
|
||||
.ok()?;
|
||||
|
||||
let device = driver.imp.probe(node, cx);
|
||||
|
||||
// Move to early init unless driver wants to sleep
|
||||
@@ -222,11 +212,6 @@ impl Node {
|
||||
self.system_controller.init(syscon);
|
||||
}
|
||||
|
||||
/// Informs the node of its capability as a pin controller.
|
||||
pub fn make_pin_controller(&self, pinctrl: Arc<dyn DeviceTreePinController>) {
|
||||
self.pin_controller.init(pinctrl);
|
||||
}
|
||||
|
||||
/// Returns the device driver associated with this node, if any was probed.
|
||||
pub fn driver(&self) -> Option<&'static dyn Driver> {
|
||||
let probed = self.device.try_get()?.as_probed()?;
|
||||
@@ -352,24 +337,6 @@ impl Node {
|
||||
.map(|range| PhysicalAddress::from_u64(range.start))
|
||||
}
|
||||
|
||||
/// Returns an iterator over the node's defined `gpios`
|
||||
pub fn gpios<'a>(
|
||||
&self,
|
||||
options: &'a DeviceTreeGpioPins,
|
||||
) -> Option<impl Iterator<Item = PinHandle> + 'a> {
|
||||
let gpios = self.property("gpios")?;
|
||||
Some(GpioIter {
|
||||
gpios,
|
||||
options,
|
||||
offset: 0,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a `gpios` entry with given index
|
||||
pub fn gpio(&self, options: &DeviceTreeGpioPins, index: usize) -> Option<PinHandle> {
|
||||
self.gpios(options)?.nth(index)
|
||||
}
|
||||
|
||||
/// Returns an input `clock` handle for a given reset name
|
||||
pub fn named_clock(&self, name: &str) -> Option<ClockHandle> {
|
||||
self.property("clock-names")?
|
||||
@@ -442,16 +409,11 @@ impl Node {
|
||||
.map(|e| e.clone().as_interrupt_controller())
|
||||
}
|
||||
|
||||
/// Attempts to get a system controller represented by this node, if any
|
||||
/// Attempts to get an system controller represented by this node, if any
|
||||
pub fn as_system_controller(self: &Arc<Self>) -> Option<Arc<dyn DeviceTreeSyscon>> {
|
||||
self.system_controller.try_get().cloned()
|
||||
}
|
||||
|
||||
/// Attempts to get a pin controller represented by this node, if any
|
||||
pub fn as_pin_controller(&self) -> Option<Arc<dyn DeviceTreePinController>> {
|
||||
self.pin_controller.try_get().cloned()
|
||||
}
|
||||
|
||||
/// Returns the `#address-cells` value of the node's parent bus
|
||||
pub fn bus_address_cells(&self) -> usize {
|
||||
self.bus_address_cells
|
||||
@@ -482,11 +444,6 @@ impl Node {
|
||||
self.dt_node.property(name)
|
||||
}
|
||||
|
||||
/// Returns `true` if the node has a property `name` inside
|
||||
pub fn has_property(&self, name: &str) -> bool {
|
||||
self.dt_node.has_property(name)
|
||||
}
|
||||
|
||||
/// Interprets property `name` as a single cell and casts it to usize
|
||||
pub fn prop_usize(&self, name: &str) -> Option<usize> {
|
||||
self.dt_node.prop_cell_usize(name)
|
||||
@@ -522,28 +479,6 @@ impl Node {
|
||||
|
||||
Some(node)
|
||||
}
|
||||
|
||||
/// Dump the contents of the device tree node into Info-level log
|
||||
pub fn dump(&self) {
|
||||
tree::dump(self.dt_node.clone(), 0);
|
||||
}
|
||||
|
||||
fn setup_pins(&self) -> Result<(), Error> {
|
||||
// TODO lookup pin state by name
|
||||
let Some(pinctrl0) = self.prop_usize("pinctrl-0") else {
|
||||
return Ok(());
|
||||
};
|
||||
let pinctrl0 = lookup_phandle(pinctrl0 as Phandle, false).ok_or(Error::DoesNotExist)?;
|
||||
|
||||
// Find pinctrl-group's parent pin controller
|
||||
let pin_controller = pinctrl0.parent().ok_or(Error::DoesNotExist)?;
|
||||
pin_controller.probe().ok_or(Error::DoesNotExist)?;
|
||||
let pin_controller = pin_controller
|
||||
.as_pin_controller()
|
||||
.ok_or(Error::DoesNotExist)?;
|
||||
|
||||
pin_controller.configure_pin_group(&pinctrl0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Node {
|
||||
@@ -596,7 +531,6 @@ fn unflatten_node(
|
||||
clock_controler: OneTimeInit::new(),
|
||||
reset_controller: OneTimeInit::new(),
|
||||
system_controller: OneTimeInit::new(),
|
||||
pin_controller: OneTimeInit::new(),
|
||||
});
|
||||
|
||||
if let Some(phandle) = phandle {
|
||||
|
||||
@@ -1,48 +1,17 @@
|
||||
//! General helpers and utilities for device tree drivers
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
gpio::{GpioPinConfig, GpioPinLevel, InputPinBias, OutputPinBias},
|
||||
interrupt::{FullIrq, MessageInterruptController},
|
||||
};
|
||||
use device_api::interrupt::{FullIrq, MessageInterruptController};
|
||||
use fdt_rs::prelude::PropReader;
|
||||
use yggdrasil_abi::net::MacAddress;
|
||||
|
||||
use crate::{
|
||||
driver::{lookup_phandle, map_interrupt_at, DeviceTreeGpioPins},
|
||||
driver::{lookup_phandle, map_interrupt_at},
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
|
||||
use super::Node;
|
||||
|
||||
/// Defines how push-pull is configured for a pin
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum GenericPinctrlBiasConfig {
|
||||
/// Disable push-pull
|
||||
Disable,
|
||||
/// Pull pin up
|
||||
PullUp,
|
||||
/// Pull pin down
|
||||
PullDown,
|
||||
}
|
||||
|
||||
/// Represents pin config parameters most commonly used in FDTs
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct GenericPinctrlConfig {
|
||||
/// Whether and how to configure the push-pull on the pin
|
||||
pub bias: Option<GenericPinctrlBiasConfig>,
|
||||
/// Drive strength in pinmux-specific units
|
||||
pub drive_strength: Option<u32>,
|
||||
/// Whether to enable the pin as an input
|
||||
pub input_enable: Option<bool>,
|
||||
/// Whether to enable the Schmitt trigger on the pin
|
||||
pub input_schmitt_enable: Option<bool>,
|
||||
/// Whether to enable the pin as an output
|
||||
pub output_enable: Option<bool>,
|
||||
/// GPIO rise/fall time
|
||||
pub slew_rate: Option<u32>,
|
||||
}
|
||||
|
||||
/// Represents an entry in a PCIe-controller's `interrupt-map` field
|
||||
#[derive(Debug)]
|
||||
pub struct PcieInterruptEntry {
|
||||
@@ -89,55 +58,6 @@ pub struct PcieMsiMapIter<'a> {
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl GenericPinctrlConfig {
|
||||
fn tristate(enable: bool, disable: bool) -> Option<bool> {
|
||||
if enable {
|
||||
Some(true)
|
||||
} else if disable {
|
||||
Some(false)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves a generic pin configuration from a device tree node
|
||||
pub fn from_node(node: &Node) -> Self {
|
||||
let bias_disable = node.has_property("bias-disable");
|
||||
let bias_pull_up = node.has_property("bias-pull-up");
|
||||
let bias_pull_down = node.has_property("bias-pull-down");
|
||||
let drive_strength = node.prop_usize("drive-strength").map(|p| p as u32);
|
||||
let input_disable = node.has_property("input-disable");
|
||||
let input_enable = node.has_property("input-enable");
|
||||
let input_schmitt_enable = node.has_property("input-schmitt-enable");
|
||||
let input_schmitt_disable = node.has_property("input-schmitt-disable");
|
||||
let output_disable = node.has_property("output-disable");
|
||||
let output_enable = node.has_property("output-enable");
|
||||
let slew_rate = node.prop_usize("slew-rate").map(|p| p as u32);
|
||||
|
||||
let bias = if bias_disable {
|
||||
Some(GenericPinctrlBiasConfig::Disable)
|
||||
} else if bias_pull_up {
|
||||
Some(GenericPinctrlBiasConfig::PullUp)
|
||||
} else if bias_pull_down {
|
||||
Some(GenericPinctrlBiasConfig::PullDown)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let input_enable = Self::tristate(input_enable, input_disable);
|
||||
let input_schmitt_enable = Self::tristate(input_schmitt_enable, input_schmitt_disable);
|
||||
let output_enable = Self::tristate(output_enable, output_disable);
|
||||
|
||||
Self {
|
||||
bias,
|
||||
drive_strength,
|
||||
input_enable,
|
||||
input_schmitt_enable,
|
||||
output_enable,
|
||||
slew_rate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for PcieInterruptMapIter<'_> {
|
||||
type Item = PcieInterruptEntry;
|
||||
|
||||
@@ -275,40 +195,3 @@ pub fn read_mac_address(node: &Arc<Node>) -> Option<MacAddress> {
|
||||
let mac_bytes: [u8; 6] = property.raw().try_into().ok()?;
|
||||
Some(MacAddress::from(mac_bytes))
|
||||
}
|
||||
|
||||
/// Parse a generic gpio configuration from the device tree 1-cell format
|
||||
pub fn generic_gpio_config(cell: u32, gpio: &DeviceTreeGpioPins) -> GpioPinConfig {
|
||||
const GPIO_ACTIVE_LOW: u32 = 1 << 0;
|
||||
const GPIO_SINGLE_ENDED: u32 = 1 << 1;
|
||||
const GPIO_LINE_OPEN_DRAIN: u32 = 1 << 2;
|
||||
const GPIO_PULL_UP: u32 = 1 << 4;
|
||||
const GPIO_PULL_DOWN: u32 = 1 << 5;
|
||||
const GPIO_PULL_DISABLE: u32 = 1 << 6;
|
||||
const GPIO_OPEN_DRAIN: u32 = GPIO_LINE_OPEN_DRAIN | GPIO_SINGLE_ENDED;
|
||||
|
||||
let active_level = if cell & GPIO_ACTIVE_LOW != 0 {
|
||||
GpioPinLevel::Low
|
||||
} else {
|
||||
GpioPinLevel::High
|
||||
};
|
||||
let output_bias = match cell & (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN) {
|
||||
0 => Some(OutputPinBias::PushPull),
|
||||
GPIO_OPEN_DRAIN | GPIO_SINGLE_ENDED => Some(OutputPinBias::OpenDrain),
|
||||
_ => None,
|
||||
};
|
||||
let input_bias = match cell & (GPIO_PULL_UP | GPIO_PULL_DOWN | GPIO_PULL_DISABLE) {
|
||||
GPIO_PULL_DISABLE => Some(InputPinBias::Floating),
|
||||
GPIO_PULL_UP => Some(InputPinBias::PullUp),
|
||||
GPIO_PULL_DOWN => Some(InputPinBias::PullDown),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
GpioPinConfig {
|
||||
input: gpio.input,
|
||||
output: gpio.output,
|
||||
initial_level: gpio.initial_level,
|
||||
input_bias,
|
||||
output_bias,
|
||||
active_level,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ impl<'a> DeviceTree<'a> {
|
||||
unsafe impl Sync for DeviceTree<'_> {}
|
||||
|
||||
fn indent(amount: usize) {
|
||||
log::info!(target: ":raw", "{0:1$}", "", amount * 2);
|
||||
log::info!(target: "raw", "{0:1$}", "", amount * 2);
|
||||
}
|
||||
|
||||
fn dump_property(node: &TNode, prop: TProp, level: usize) {
|
||||
@@ -211,14 +211,14 @@ fn dump_property(node: &TNode, prop: TProp, level: usize) {
|
||||
let name = prop.name().unwrap_or("<unknown-prop>");
|
||||
|
||||
indent(level);
|
||||
log::info!(target: ":raw", "{name}");
|
||||
log::info!(target: "raw", "{name}");
|
||||
|
||||
if prop.len() == 0 {
|
||||
log::info!(target: ":raw", ";\n");
|
||||
log::info!(target: "raw", ";\n");
|
||||
return;
|
||||
}
|
||||
|
||||
log::info!(target: ":raw", " = ");
|
||||
log::info!(target: "raw", " = ");
|
||||
|
||||
let ty = match name {
|
||||
"model" | "compatible" | "clock-output-names" | "clock-names" | "device_type"
|
||||
@@ -234,48 +234,48 @@ fn dump_property(node: &TNode, prop: TProp, level: usize) {
|
||||
// "..."
|
||||
for (i, string) in prop.as_str_list().enumerate() {
|
||||
if i != 0 {
|
||||
log::info!(target: ":raw", ", ");
|
||||
log::info!(target: "raw", ", ");
|
||||
}
|
||||
log::info!(target: ":raw", "{string:?}");
|
||||
log::info!(target: "raw", "{string:?}");
|
||||
}
|
||||
}
|
||||
Type::Cells => {
|
||||
// <...>
|
||||
log::info!(target: ":raw", "<");
|
||||
log::info!(target: "raw", "<");
|
||||
let mut i = 0;
|
||||
while let Some(value) = prop.read_cell(i, 1) {
|
||||
if i != 0 {
|
||||
log::info!(target: ":raw", ", ");
|
||||
log::info!(target: "raw", ", ");
|
||||
}
|
||||
log::info!(target: ":raw", "{value:#x}");
|
||||
log::info!(target: "raw", "{value:#x}");
|
||||
i += 1;
|
||||
}
|
||||
log::info!(target: ":raw", ">");
|
||||
log::info!(target: "raw", ">");
|
||||
}
|
||||
Type::Bytes => {
|
||||
// <...>
|
||||
log::info!(target: ":raw", "<");
|
||||
log::info!(target: "raw", "<");
|
||||
for (i, byte) in prop.raw().iter().enumerate() {
|
||||
if i != 0 {
|
||||
log::info!(target: ":raw", ", ");
|
||||
log::info!(target: "raw", ", ");
|
||||
}
|
||||
log::info!(target: ":raw", "{byte:#x}");
|
||||
log::info!(target: "raw", "{byte:#x}");
|
||||
}
|
||||
log::info!(target: ":raw", ">");
|
||||
log::info!(target: "raw", ">");
|
||||
}
|
||||
}
|
||||
|
||||
log::info!(target: ":raw", ";\n");
|
||||
log::info!(target: "raw", ";\n");
|
||||
}
|
||||
|
||||
pub(crate) fn dump(node: TNode, level: usize) {
|
||||
fn dump(node: TNode, level: usize) {
|
||||
let name = node.name().unwrap_or("<unknown>");
|
||||
|
||||
indent(level);
|
||||
if name.is_empty() {
|
||||
log::info!(target: ":raw", "{{\n");
|
||||
log::info!(target: "raw", "{{\n");
|
||||
} else {
|
||||
log::info!(target: ":raw", "{name}: {{\n");
|
||||
log::info!(target: "raw", "{name}: {{\n");
|
||||
}
|
||||
|
||||
let mut do_break = false;
|
||||
@@ -286,12 +286,12 @@ pub(crate) fn dump(node: TNode, level: usize) {
|
||||
|
||||
for child in node.children() {
|
||||
if do_break {
|
||||
log::info!(target: ":raw", "\n");
|
||||
log::info!(target: "raw", "\n");
|
||||
do_break = false;
|
||||
}
|
||||
dump(child, level + 1);
|
||||
}
|
||||
|
||||
indent(level);
|
||||
log::info!(target: ":raw", "}}\n");
|
||||
log::info!(target: "raw", "}}\n");
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
pub trait BitField: Sized {
|
||||
fn modify_bit(&self, bit: usize, value: bool) -> Self;
|
||||
fn set_bit(&mut self, bit: usize, value: bool);
|
||||
fn get_bit(&self, bit: usize) -> bool;
|
||||
}
|
||||
|
||||
macro_rules! impl_bit_field {
|
||||
($($ty:ty),+ $(,)?) => {
|
||||
$(
|
||||
impl BitField for $ty {
|
||||
#[inline]
|
||||
fn modify_bit(&self, bit: usize, value: bool) -> Self {
|
||||
if value {
|
||||
*self | (1 << bit)
|
||||
} else {
|
||||
*self & !(1 << bit)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_bit(&mut self, bit: usize, value: bool) {
|
||||
*self = self.modify_bit(bit, value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_bit(&self, bit: usize) -> bool {
|
||||
*self & (1 << bit) != 0
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
impl_bit_field!(u8, u16, u32, u64, usize);
|
||||
impl_bit_field!(i8, i16, i32, i64, isize);
|
||||
@@ -18,7 +18,6 @@ use core::{
|
||||
panic,
|
||||
};
|
||||
|
||||
pub mod bit;
|
||||
pub mod event;
|
||||
pub mod ext;
|
||||
pub mod hash_table;
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
use core::{
|
||||
sync::atomic::{AtomicU64, Ordering},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, vec::Vec};
|
||||
|
||||
use device_api::gpio::{GpioInterrupt, GpioInterruptEvent, GpioOutput, PinHandle};
|
||||
use libk_util::{ring::LossyRingQueue, sync::spin_rwlock::IrqSafeRwLock};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::task::runtime;
|
||||
|
||||
static GPIO_EVENT_QUEUE: LossyRingQueue<u64> = LossyRingQueue::with_capacity(64);
|
||||
static GPIO_EVENT_MAP: IrqSafeRwLock<BTreeMap<u64, Box<dyn Fn() + Send>>> =
|
||||
IrqSafeRwLock::new(BTreeMap::new());
|
||||
static GPIO_EVENT_ID: AtomicU64 = AtomicU64::new(1);
|
||||
|
||||
static GPIO_HEARTBEATS: IrqSafeRwLock<Vec<Arc<dyn GpioOutput>>> = IrqSafeRwLock::new(Vec::new());
|
||||
|
||||
pub trait GpioEvent {
|
||||
fn configure_with_interrupt(&self, int: GpioInterrupt) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
impl GpioEvent for PinHandle {
|
||||
fn configure_with_interrupt(&self, int: GpioInterrupt) -> Result<(), Error> {
|
||||
let event = bind_gpio_event(int.handler);
|
||||
self.parent.setup_gpio(
|
||||
self,
|
||||
Some(GpioInterruptEvent {
|
||||
mode: int.mode,
|
||||
event,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn gpio_event_handler_task() {
|
||||
loop {
|
||||
let id = GPIO_EVENT_QUEUE.read().await;
|
||||
|
||||
let lock = GPIO_EVENT_MAP.read();
|
||||
if let Some(handler) = lock.get(&id) {
|
||||
handler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn heartbeat_handler_task() {
|
||||
const LEVELS: &[(Duration, bool)] = &[
|
||||
(Duration::from_millis(50), true),
|
||||
(Duration::from_millis(200), false),
|
||||
(Duration::from_millis(50), true),
|
||||
(Duration::from_millis(700), false),
|
||||
];
|
||||
let mut index = 0;
|
||||
loop {
|
||||
let (timeout, level) = LEVELS[index];
|
||||
|
||||
for pin in GPIO_HEARTBEATS.read().iter() {
|
||||
pin.write(level).ok();
|
||||
}
|
||||
runtime::sleep(timeout).await;
|
||||
|
||||
index = (index + 1) % LEVELS.len();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_heartbeat_gpio(gpio: Arc<dyn GpioOutput>) {
|
||||
GPIO_HEARTBEATS.write().push(gpio);
|
||||
}
|
||||
|
||||
pub fn bind_gpio_event(handler: Box<dyn Fn() + Send>) -> u64 {
|
||||
let id = GPIO_EVENT_ID.fetch_add(1, Ordering::Acquire);
|
||||
GPIO_EVENT_MAP.write().insert(id, handler);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn signal_gpio_event(ev: u64) {
|
||||
GPIO_EVENT_QUEUE.write(ev);
|
||||
}
|
||||
@@ -36,7 +36,6 @@ pub mod arch;
|
||||
pub mod config;
|
||||
pub mod debug;
|
||||
pub mod device;
|
||||
pub mod event;
|
||||
pub mod fs;
|
||||
pub mod module;
|
||||
pub mod random;
|
||||
|
||||
@@ -350,7 +350,6 @@ impl Thread {
|
||||
if debug.tracer.is_some() {
|
||||
let timestamp = monotonic_time();
|
||||
debug.store_state(frame);
|
||||
// log::info!("TRACE {payload:?}");
|
||||
self.events.trace.write(TraceEvent {
|
||||
suspend,
|
||||
timestamp,
|
||||
@@ -514,11 +513,7 @@ impl Thread {
|
||||
|
||||
/// Returns `true` if the thread is a tracee of given process
|
||||
pub fn is_tracee_of(&self, pid: ProcessId) -> bool {
|
||||
{
|
||||
let tracer = self.debug.lock().tracer;
|
||||
// log::info!("{:?}'s tracer: {:?}", self.id, tracer);
|
||||
tracer == Some(pid)
|
||||
}
|
||||
self.debug.lock().tracer == Some(pid)
|
||||
}
|
||||
|
||||
pub fn attach_trace(&self, tracer: ProcessId) -> Result<(), Error> {
|
||||
@@ -582,13 +577,11 @@ impl Thread {
|
||||
DebugControl::Detach => todo!(),
|
||||
DebugControl::Resume => {
|
||||
let single_step = debug::Resume::load_request(input)?;
|
||||
// log::info!("Resume single_step={single_step}");
|
||||
self.resume(single_step);
|
||||
debug::Resume::store_response(&(), buffer)
|
||||
}
|
||||
DebugControl::SetTraceFlags => {
|
||||
let flags = debug::SetTraceFlags::load_request(buffer)?;
|
||||
// log::info!("SetTraceFlags {flags:?}");
|
||||
self.set_trace_flags(flags);
|
||||
debug::SetTraceFlags::store_response(&(), buffer)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ use libk_mm::{
|
||||
};
|
||||
use libk_util::OneTimeInit;
|
||||
use tock_registers::interfaces::Writeable;
|
||||
use ygg_driver_pci::PciBusManager;
|
||||
// use ygg_driver_pci::PciBusManager;
|
||||
|
||||
use crate::{
|
||||
arch::{aarch64::gic::Gic, Platform},
|
||||
@@ -257,7 +257,7 @@ impl AArch64 {
|
||||
InitSequence::Early,
|
||||
);
|
||||
|
||||
PciBusManager::probe_bus_devices()?;
|
||||
// PciBusManager::probe_bus_devices()?;
|
||||
} else {
|
||||
// BSP already initialized everything needed
|
||||
// Setup timer and local interrupt controller
|
||||
|
||||
@@ -7,7 +7,7 @@ use core::{
|
||||
|
||||
use elf::{abi, relocation::Elf64_Rela};
|
||||
use kernel_arch::{Architecture, ArchitectureImpl};
|
||||
use kernel_arch_riscv64::{mem, BOOT_HART_ID, CPU_COUNT};
|
||||
use kernel_arch_riscv64::{mem, CPU_COUNT};
|
||||
use libk::{
|
||||
debug,
|
||||
fs::{devfs, sysfs},
|
||||
@@ -28,6 +28,7 @@ const BOOT_STACK_SIZE: usize = 65536;
|
||||
|
||||
static mut KERNEL_LOAD_BASE: u64 = 0;
|
||||
static mut DTB_ADDRESS: PhysicalAddress = PhysicalAddress::ZERO;
|
||||
static mut BOOT_HART_ID: u64 = 0;
|
||||
static mut BOOT_STACK: BootStack<BOOT_STACK_SIZE> = BootStack::zeroed();
|
||||
|
||||
#[repr(C, align(0x10))]
|
||||
@@ -57,6 +58,11 @@ unsafe extern "C" fn relocate_kernel(image_base: i64, rela_start: usize, rela_en
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the ID of the bootstrap HART
|
||||
pub fn boot_hart_id() -> u64 {
|
||||
unsafe { BOOT_HART_ID }
|
||||
}
|
||||
|
||||
unsafe fn long_jump(pc: usize, sp: usize, a0: usize) -> ! {
|
||||
core::arch::asm!("mv sp, {sp}; jr {pc}", in("a0") a0, sp = in(reg) sp, pc = in(reg) pc, options(noreturn))
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use abi::error::Error;
|
||||
use device_api::interrupt::{IpiDeliveryTarget, IpiMessage};
|
||||
use device_tree::{DeviceTree, DeviceTreeNodeExt};
|
||||
use kernel_arch_riscv64::{
|
||||
boot_hart_id, mem::auto_lower_address, registers::SIP, sbi, ArchitectureImpl, CPU_COUNT,
|
||||
mem::auto_lower_address, registers::SIP, sbi, ArchitectureImpl, CPU_COUNT,
|
||||
};
|
||||
use libk::arch::Cpu;
|
||||
use libk_mm::{
|
||||
@@ -15,7 +15,7 @@ use libk_mm::{
|
||||
};
|
||||
use tock_registers::interfaces::ReadWriteable;
|
||||
|
||||
use crate::panic;
|
||||
use crate::{arch::riscv64::boot::boot_hart_id, panic};
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const SECONDARY_STACK_SIZE: usize = 32768;
|
||||
|
||||
@@ -21,7 +21,7 @@ use libk_mm::{
|
||||
table::EntryLevelExt,
|
||||
};
|
||||
use peripherals::{i8253::I8253, ps2::PS2Controller, rtc::Rtc, serial::ComPort};
|
||||
use ygg_driver_pci::PciBusManager;
|
||||
// use ygg_driver_pci::PciBusManager;
|
||||
|
||||
use crate::fs::{Initrd, INITRD_DATA};
|
||||
|
||||
@@ -90,9 +90,9 @@ pub fn init_platform_early(cmdline: &str) -> Result<EarlyPlatformDevices, Error>
|
||||
}
|
||||
|
||||
pub fn add_legacy_pci() {
|
||||
if let Err(error) = PciBusManager::add_legacy_segment(&pci::PCI) {
|
||||
log::error!("Couldn't add legacy x86 PCI: {error:?}");
|
||||
}
|
||||
// if let Err(error) = PciBusManager::add_legacy_segment(&pci::PCI) {
|
||||
// log::error!("Couldn't add legacy x86 PCI: {error:?}");
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn init_platform_devices(early: EarlyPlatformDevices) {
|
||||
@@ -120,9 +120,9 @@ pub fn init_platform_devices(early: EarlyPlatformDevices) {
|
||||
log::error!("COM port IRQ init error: {error:?}");
|
||||
}
|
||||
|
||||
if let Err(error) = PciBusManager::probe_bus_devices() {
|
||||
log::error!("PCI bus device setup error(s): {error:?}");
|
||||
}
|
||||
// if let Err(error) = PciBusManager::probe_bus_devices() {
|
||||
// log::error!("PCI bus device setup error(s): {error:?}");
|
||||
// }
|
||||
}
|
||||
|
||||
fn init_clock_source(sources: &[ProbeClockSource]) -> Result<SelectedClockSource, Error> {
|
||||
|
||||
+16
-16
@@ -1,6 +1,6 @@
|
||||
use kernel_arch_x86::intrinsics::{IoPort, IoPortAccess};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use ygg_driver_pci::LegacyPciAccess;
|
||||
// use ygg_driver_pci::LegacyPciAccess;
|
||||
|
||||
struct LegacyPciInner {
|
||||
address: IoPort<u32>,
|
||||
@@ -18,21 +18,21 @@ pub(super) static PCI: LegacyPci = LegacyPci {
|
||||
}),
|
||||
};
|
||||
|
||||
impl LegacyPciAccess for LegacyPci {
|
||||
fn write_u32(&self, bus: u8, slot: u8, func: u8, offset: u8, value: u32) {
|
||||
assert_eq!(offset & 0x3, 0);
|
||||
let inner = self.inner.lock();
|
||||
inner.address.write(Self::addr(bus, slot, func, offset));
|
||||
inner.data.write(value);
|
||||
}
|
||||
|
||||
fn read_u32(&self, bus: u8, slot: u8, func: u8, offset: u8) -> u32 {
|
||||
assert_eq!(offset & 0x3, 0);
|
||||
let inner = self.inner.lock();
|
||||
inner.address.write(Self::addr(bus, slot, func, offset));
|
||||
inner.data.read()
|
||||
}
|
||||
}
|
||||
// impl LegacyPciAccess for LegacyPci {
|
||||
// fn write_u32(&self, bus: u8, slot: u8, func: u8, offset: u8, value: u32) {
|
||||
// assert_eq!(offset & 0x3, 0);
|
||||
// let inner = self.inner.lock();
|
||||
// inner.address.write(Self::addr(bus, slot, func, offset));
|
||||
// inner.data.write(value);
|
||||
// }
|
||||
//
|
||||
// fn read_u32(&self, bus: u8, slot: u8, func: u8, offset: u8) -> u32 {
|
||||
// assert_eq!(offset & 0x3, 0);
|
||||
// let inner = self.inner.lock();
|
||||
// inner.address.write(Self::addr(bus, slot, func, offset));
|
||||
// inner.data.read()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl LegacyPci {
|
||||
#[inline]
|
||||
|
||||
@@ -33,7 +33,7 @@ use yboot_proto::{
|
||||
LoadProtocolV1,
|
||||
};
|
||||
use ygg_driver_acpi::{AcpiAllocator, AcpiHandlerImpl, EventAction, FixedEvent};
|
||||
use ygg_driver_pci::PciBusManager;
|
||||
// use ygg_driver_pci::PciBusManager;
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
@@ -294,9 +294,6 @@ impl X86_64 {
|
||||
unreachable!("The processor does not support APIC");
|
||||
};
|
||||
|
||||
for _ in 0..10 {
|
||||
log::info!("TICK");
|
||||
}
|
||||
let ioapic = IoApic::from_acpi(&apic_info)?;
|
||||
register_external_interrupt_controller(ioapic);
|
||||
|
||||
@@ -312,12 +309,12 @@ impl X86_64 {
|
||||
}
|
||||
|
||||
if let Ok(mcfg) = acpi.find_table::<Mcfg>() {
|
||||
for entry in mcfg.entries() {
|
||||
if let Err(error) = PciBusManager::add_segment_from_mcfg(entry, local_apic.clone())
|
||||
{
|
||||
log::error!("Could not add PCI bus segment: {error:?}");
|
||||
}
|
||||
}
|
||||
// for entry in mcfg.entries() {
|
||||
// if let Err(error) = PciBusManager::add_segment_from_mcfg(entry, local_apic.clone())
|
||||
// {
|
||||
// log::error!("Could not add PCI bus segment: {error:?}");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Bus devices
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod pci_host_ecam_generic;
|
||||
// #[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
|
||||
// pub mod pci_host_ecam_generic;
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod simple_bus;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//! BCM283x AUX peripheral
|
||||
use aarch64_cpu::registers::ReadWriteable;
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockController, ClockHandle},
|
||||
@@ -10,11 +13,9 @@ use device_tree::{
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
||||
use tock_registers::{
|
||||
interfaces::ReadWriteable,
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite},
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
@@ -1,5 +1,8 @@
|
||||
//! StarFive JH7110 clock drivers
|
||||
|
||||
use core::{marker::PhantomData, ops::Index};
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockController, ClockHandle, Hertz, ResetController, ResetHandle},
|
||||
@@ -14,7 +17,6 @@ use device_tree::{
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
// SYSCRG clocks
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
//! Clock controller drivers
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
pub mod bcm2835_aux;
|
||||
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod jh7110_clocks;
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod fixed;
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
//! GPIO drivers
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::{GpioInterrupt, GpioInterruptMode, GpioOutput, GpioPinLevel, PinHandle},
|
||||
};
|
||||
use device_tree::driver::{device_tree_driver, DeviceTreeGpioPins, Node, ProbeContext};
|
||||
use libk::event::{self, GpioEvent};
|
||||
|
||||
enum LedFunction {
|
||||
Heartbeat,
|
||||
Other,
|
||||
}
|
||||
|
||||
struct LedPin {
|
||||
name: String,
|
||||
function: LedFunction,
|
||||
handle: PinHandle,
|
||||
}
|
||||
|
||||
struct KeyPin {
|
||||
name: String,
|
||||
handle: PinHandle,
|
||||
}
|
||||
|
||||
trait Pin: Sized + Send + Sync + 'static {
|
||||
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<Self>>;
|
||||
fn configure(self: &Arc<Self>) -> Result<(), Error>;
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
||||
struct PinGroup<P: Pin> {
|
||||
name: String,
|
||||
pins: Vec<Arc<P>>,
|
||||
}
|
||||
|
||||
impl<P: Pin> PinGroup<P> {
|
||||
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<dyn Device>> {
|
||||
let pins = node
|
||||
.children()
|
||||
.filter_map(P::from_device_tree)
|
||||
.collect::<Vec<_>>();
|
||||
if pins.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let name = node.name().unwrap_or("pin-group");
|
||||
Some(Arc::new(Self {
|
||||
pins,
|
||||
name: name.into(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pin> Device for PinGroup<P> {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
for pin in self.pins.iter() {
|
||||
if let Err(error) = pin.configure() {
|
||||
log::error!("gpio {} setup error: {:?}", pin.name(), error);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl Pin for LedPin {
|
||||
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<Self>> {
|
||||
let handle = node.gpio(
|
||||
&DeviceTreeGpioPins {
|
||||
input: false,
|
||||
output: true,
|
||||
initial_level: GpioPinLevel::Low,
|
||||
},
|
||||
0,
|
||||
)?;
|
||||
|
||||
let function = match node.prop_str("function") {
|
||||
Some("heartbeat") => LedFunction::Heartbeat,
|
||||
_ => LedFunction::Other,
|
||||
};
|
||||
|
||||
let name = node.name().unwrap_or("gpio-led").into();
|
||||
Some(Arc::new(Self {
|
||||
name,
|
||||
function,
|
||||
handle,
|
||||
}))
|
||||
}
|
||||
|
||||
fn configure(self: &Arc<Self>) -> Result<(), Error> {
|
||||
self.handle.configure()?;
|
||||
|
||||
match self.function {
|
||||
LedFunction::Heartbeat => {
|
||||
log::info!("Register led {:?} as heartbeat indicator", self.name);
|
||||
event::register_heartbeat_gpio(self.clone())
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl GpioOutput for LedPin {
|
||||
fn write(&self, value: bool) -> Result<(), Error> {
|
||||
self.handle.write(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pin for KeyPin {
|
||||
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<Self>> {
|
||||
let handle = node.gpio(
|
||||
&DeviceTreeGpioPins {
|
||||
input: true,
|
||||
output: false,
|
||||
initial_level: GpioPinLevel::Low,
|
||||
},
|
||||
0,
|
||||
)?;
|
||||
let name = node.name().unwrap_or("gpio-key").into();
|
||||
Some(Arc::new(Self { name, handle }))
|
||||
}
|
||||
|
||||
fn configure(self: &Arc<Self>) -> Result<(), Error> {
|
||||
let key = self.clone();
|
||||
self.handle.configure_with_interrupt(GpioInterrupt {
|
||||
mode: GpioInterruptMode::FallingEdge,
|
||||
handler: Box::new(move || {
|
||||
log::info!("GPIO key triggered: {:?}", key.name);
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["gpio-leds"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
PinGroup::<LedPin>::from_device_tree(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["gpio-keys"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
PinGroup::<KeyPin>::from_device_tree(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
//! Interrupt controller drivers
|
||||
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod riscv_plic;
|
||||
@@ -1,3 +1,6 @@
|
||||
//! RISC-V PLIC driver
|
||||
|
||||
use abi::{error::Error, primitive_enum};
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
@@ -12,7 +15,6 @@ use device_tree::{
|
||||
},
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
use kernel_arch_riscv64::boot_hart_id;
|
||||
use libk::{arch::Cpu, device::register_external_interrupt_controller};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
|
||||
@@ -21,7 +23,8 @@ use tock_registers::{
|
||||
register_structs,
|
||||
registers::{ReadOnly, ReadWrite},
|
||||
};
|
||||
use yggdrasil_abi::{error::Error, primitive_enum};
|
||||
|
||||
use crate::arch::riscv64::boot::boot_hart_id;
|
||||
|
||||
const MAX_IRQS: usize = 1024;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use core::{cell::UnsafeCell, mem::MaybeUninit};
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::device::{Device, DeviceInitContext};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
@@ -22,7 +25,6 @@ use tock_registers::{
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
@@ -0,0 +1,4 @@
|
||||
//! Mailbox interfaces
|
||||
|
||||
#[cfg(any(rust_analyzer, target_arch = "aarch64"))]
|
||||
mod bcm2835_mbox;
|
||||
@@ -6,12 +6,12 @@ use libk_util::OneTimeInit;
|
||||
pub mod bus;
|
||||
pub mod clock;
|
||||
pub mod display;
|
||||
pub mod interrupt;
|
||||
pub mod mbox;
|
||||
pub mod pinctrl;
|
||||
pub mod power;
|
||||
pub mod serial;
|
||||
// pub mod timer;
|
||||
|
||||
#[cfg(any(rust_analyzer, not(target_arch = "x86_64")))]
|
||||
pub mod gpio;
|
||||
|
||||
/// Generic machine description string
|
||||
pub static MACHINE_NAME: OneTimeInit<String> = OneTimeInit::new();
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::ClockHandle,
|
||||
device::{Device, DeviceInitContext},
|
||||
};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
use libk_mm::device::DeviceMemoryIoMut;
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
|
||||
struct Jh7110SysPinctrl {
|
||||
#[allow(unused)]
|
||||
regs: IrqSafeSpinlock<DeviceMemoryIoMut<'static, [u32]>>,
|
||||
clock: ClockHandle,
|
||||
}
|
||||
|
||||
unsafe impl Send for Jh7110SysPinctrl {}
|
||||
unsafe impl Sync for Jh7110SysPinctrl {}
|
||||
|
||||
impl Device for Jh7110SysPinctrl {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
self.clock.enable()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"jh7110-sys-pinctrl"
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["starfive,jh7110-sys-pinctrl"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let clock = node.clock(0).unwrap();
|
||||
let base = node.map_base(context, 0)?;
|
||||
|
||||
let regs = unsafe { DeviceMemoryIoMut::map_slice(base, 0x2B8 / 4, Default::default()) }.ok()?;
|
||||
|
||||
let pinctrl = Arc::new(Jh7110SysPinctrl {
|
||||
clock,
|
||||
regs: IrqSafeSpinlock::new(regs)
|
||||
});
|
||||
|
||||
Some(pinctrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
//! GPIO/Pin controller, multiplexer drivers
|
||||
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
mod jh7110_pinctrl;
|
||||
+6
-4
@@ -1,3 +1,9 @@
|
||||
//! Broadcom BCM2835 mini-UART driver
|
||||
|
||||
use abi::{
|
||||
error::Error,
|
||||
io::{TerminalOptions, TerminalOutputOptions},
|
||||
};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::ClockHandle,
|
||||
@@ -17,10 +23,6 @@ use tock_registers::{
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite},
|
||||
};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{TerminalOptions, TerminalOutputOptions},
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
@@ -1,5 +1,11 @@
|
||||
//! Serial device interfaces
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
pub mod bcm2835_aux_uart;
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
pub mod pl011;
|
||||
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod ns16550a;
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! ARM PL011 driver
|
||||
use abi::{error::Error, io::TerminalOptions};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
@@ -16,7 +18,6 @@ use tock_registers::{
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use yggdrasil_abi::{error::Error, io::TerminalOptions};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
+4
-8
@@ -10,7 +10,7 @@ use libk::{
|
||||
vfs::{impls::fn_hardlink, IoContext, NodeRef, OwnedFilename},
|
||||
};
|
||||
use memfs::MemoryFilesystem;
|
||||
use ygg_driver_pci::PciBusManager;
|
||||
// use ygg_driver_pci::PciBusManager;
|
||||
|
||||
use crate::{
|
||||
fs::{FileBlockAllocator, INITRD_DATA},
|
||||
@@ -37,7 +37,6 @@ pub fn kinit() -> Result<(), Error> {
|
||||
#[cfg(any(rust_analyzer, target_arch = "riscv64", target_arch = "aarch64"))]
|
||||
{
|
||||
use device_tree::driver::InitSequence;
|
||||
use libk::event;
|
||||
|
||||
device_tree::driver::lazy_init(
|
||||
|_| (),
|
||||
@@ -56,14 +55,11 @@ pub fn kinit() -> Result<(), Error> {
|
||||
},
|
||||
InitSequence::Late,
|
||||
);
|
||||
|
||||
runtime::spawn(event::gpio_event_handler_task()).ok();
|
||||
runtime::spawn(event::heartbeat_handler_task()).ok();
|
||||
}
|
||||
// Initialize PCI devices
|
||||
if let Err(error) = PciBusManager::setup_bus_devices(false) {
|
||||
log::error!("pci bus setup error: {error:?}");
|
||||
}
|
||||
// if let Err(error) = PciBusManager::setup_bus_devices(false) {
|
||||
// log::error!("pci bus setup error: {error:?}");
|
||||
// }
|
||||
assert!(!ArchitectureImpl::interrupt_mask());
|
||||
|
||||
runtime::spawn(ygg_driver_usb::bus::bus_handler())?;
|
||||
|
||||
+8
-14
@@ -63,24 +63,18 @@ extern crate alloc;
|
||||
#[cfg(not(rust_analyzer))]
|
||||
extern crate compiler_builtins;
|
||||
|
||||
extern crate ygg_driver_ahci;
|
||||
extern crate ygg_driver_net_rtl81xx;
|
||||
extern crate ygg_driver_nvme;
|
||||
extern crate ygg_driver_usb_xhci;
|
||||
extern crate ygg_driver_virtio_blk;
|
||||
extern crate ygg_driver_virtio_gpu;
|
||||
extern crate ygg_driver_virtio_net;
|
||||
// extern crate ygg_driver_ahci;
|
||||
// extern crate ygg_driver_net_rtl81xx;
|
||||
// extern crate ygg_driver_nvme;
|
||||
// extern crate ygg_driver_usb_xhci;
|
||||
// extern crate ygg_driver_virtio_blk;
|
||||
// extern crate ygg_driver_virtio_gpu;
|
||||
// extern crate ygg_driver_virtio_net;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_arch = "x86_64")] {
|
||||
extern crate ygg_driver_net_igbe;
|
||||
} else if #[cfg(target_arch = "aarch64")] {
|
||||
extern crate ygg_driver_bsp_arm;
|
||||
extern crate ygg_driver_bsp_bcm283x;
|
||||
// extern crate ygg_driver_net_igbe;
|
||||
} else if #[cfg(target_arch = "riscv64")] {
|
||||
extern crate ygg_driver_bsp_riscv;
|
||||
extern crate ygg_driver_bsp_jh7110;
|
||||
|
||||
extern crate ygg_driver_net_stmmac;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,6 @@ struct SpawnOptions<'a> {
|
||||
pub environment: &'a [&'a str],
|
||||
/// Working directory
|
||||
pub directory: Option<&'a str>,
|
||||
/// Root path
|
||||
pub root: Option<&'a str>,
|
||||
/// Optional arguments to specify details of the creation
|
||||
pub optional: &'a [SpawnOption],
|
||||
/// Extra flags controlling the new process behavior
|
||||
|
||||
Generated
-2
@@ -2970,8 +2970,6 @@ name = "strace"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"cross",
|
||||
"libc",
|
||||
"runtime",
|
||||
]
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
pub(crate) mod sys;
|
||||
|
||||
pub mod debug;
|
||||
pub mod fs;
|
||||
pub mod io;
|
||||
pub mod mem;
|
||||
|
||||
@@ -1,64 +1,6 @@
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TraceEvent {
|
||||
SyscallEntry(u64, [u64; 6]),
|
||||
SyscallExit(i64),
|
||||
Exited,
|
||||
}
|
||||
|
||||
pub trait ChildTrace {
|
||||
fn next_event(&mut self) -> io::Result<TraceEvent>;
|
||||
fn resume(&mut self) -> io::Result<()>;
|
||||
fn kill(&mut self) -> io::Result<()>;
|
||||
|
||||
unsafe fn peek_u8(&mut self, address: usize) -> io::Result<u8> {
|
||||
let aligned = address & !7;
|
||||
let shift = (address & 7) << 3;
|
||||
let word = self.peek_u64(aligned)?;
|
||||
Ok((word >> shift) as u8)
|
||||
}
|
||||
unsafe fn peek_u32(&mut self, address: usize) -> io::Result<u32> {
|
||||
eprintln!("PEEK U32 @ {address:#x}");
|
||||
assert_eq!(address & 3, 0);
|
||||
let aligned = address & !7;
|
||||
let shift = (address & 7) << 3;
|
||||
let word = self.peek_u64(aligned)?;
|
||||
Ok((word >> shift) as u32)
|
||||
}
|
||||
unsafe fn peek_u64(&mut self, address: usize) -> io::Result<u64>;
|
||||
|
||||
unsafe fn peek_bytes(&mut self, address: usize, buffer: &mut [u8]) -> io::Result<()> {
|
||||
for i in 0..buffer.len() {
|
||||
buffer[i] = self.peek_u8(address + i)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn peek_cstr(&mut self, address: usize, buffer: &mut [u8]) -> io::Result<usize> {
|
||||
let mut len = 0;
|
||||
while len < buffer.len() - 1 {
|
||||
let byte = self.peek_u8(address + len)?;
|
||||
buffer[len] = byte;
|
||||
if byte == 0 {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
unsafe fn peek_usize(&mut self, address: usize) -> io::Result<usize> {
|
||||
// FIXME I only support 64-bit archs
|
||||
Ok(self.peek_u64(address)? as _)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CommandSpawnExt {
|
||||
type ChildTrace: ChildTrace;
|
||||
|
||||
fn create_session(&mut self) -> io::Result<&mut Self>;
|
||||
fn create_process_group(&mut self) -> io::Result<&mut Self>;
|
||||
// TODO options for tracing across subchildren, etc
|
||||
fn spawn_with_trace(&mut self) -> io::Result<Self::ChildTrace>;
|
||||
}
|
||||
|
||||
@@ -10,12 +10,10 @@ pub mod time;
|
||||
pub mod timer;
|
||||
|
||||
use std::{
|
||||
ffi::{c_int, c_void},
|
||||
ffi::c_int,
|
||||
io,
|
||||
mem::MaybeUninit,
|
||||
os::{fd::RawFd, unix::process::CommandExt},
|
||||
process::Command,
|
||||
ptr::null_mut,
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
@@ -28,126 +26,7 @@ pub use socket::{BorrowedAddressImpl, LocalPacketSocketImpl, OwnedAddressImpl};
|
||||
pub use term::RawStdinImpl;
|
||||
pub use timer::TimerFdImpl;
|
||||
|
||||
use crate::process::{ChildTrace, CommandSpawnExt, TraceEvent};
|
||||
|
||||
pub struct ChildTraceImpl {
|
||||
child: libc::pid_t,
|
||||
}
|
||||
|
||||
impl ChildTrace for ChildTraceImpl {
|
||||
fn next_event(&mut self) -> io::Result<TraceEvent> {
|
||||
let ev = loop {
|
||||
unsafe {
|
||||
let mut status = 0;
|
||||
if libc::waitpid(self.child, &mut status, 0) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
if libc::WIFSTOPPED(status) {
|
||||
let stopsig = libc::WSTOPSIG(status);
|
||||
if stopsig == libc::SIGTRAP | 0x80 {
|
||||
let mut syscall_info = MaybeUninit::<libc::ptrace_syscall_info>::zeroed();
|
||||
if libc::ptrace(
|
||||
libc::PTRACE_GET_SYSCALL_INFO,
|
||||
self.child,
|
||||
size_of::<libc::ptrace_syscall_info>(),
|
||||
syscall_info.as_mut_ptr(),
|
||||
) < 0
|
||||
{
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
let syscall_info = syscall_info.assume_init();
|
||||
match syscall_info.op {
|
||||
libc::PTRACE_SYSCALL_INFO_EXIT => {
|
||||
break TraceEvent::SyscallExit(syscall_info.u.exit.sval);
|
||||
}
|
||||
libc::PTRACE_SYSCALL_INFO_ENTRY => {
|
||||
break TraceEvent::SyscallEntry(
|
||||
syscall_info.u.entry.nr,
|
||||
syscall_info.u.entry.args,
|
||||
);
|
||||
}
|
||||
_ => self.resume()?,
|
||||
}
|
||||
} else if stopsig == libc::SIGTRAP {
|
||||
let event = (status >> 16) & 0xffff;
|
||||
eprintln!("event = {event}");
|
||||
match event {
|
||||
libc::PTRACE_EVENT_EXIT => {
|
||||
// Ignore
|
||||
eprintln!("PTRACE_EVENT_EXIT");
|
||||
self.resume()?;
|
||||
}
|
||||
libc::PTRACE_EVENT_VFORK => {
|
||||
eprintln!("PTRACE_EVENT_VFORK");
|
||||
self.resume()?;
|
||||
}
|
||||
libc::PTRACE_EVENT_EXEC => {
|
||||
// Ignore
|
||||
eprintln!("PTRACE_EVENT_EXEC");
|
||||
self.resume()?;
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
} else {
|
||||
todo!("signum == {stopsig}");
|
||||
}
|
||||
} else if libc::WIFEXITED(status) {
|
||||
break TraceEvent::Exited;
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ev)
|
||||
}
|
||||
|
||||
fn kill(&mut self) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn resume(&mut self) -> io::Result<()> {
|
||||
if unsafe {
|
||||
libc::ptrace(
|
||||
libc::PTRACE_SYSCALL,
|
||||
self.child,
|
||||
null_mut::<c_void>(),
|
||||
null_mut::<c_void>(),
|
||||
)
|
||||
} == 0
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn peek_u64(&mut self, address: usize) -> io::Result<u64> {
|
||||
libc::__errno_location().write_volatile(0);
|
||||
let word = libc::ptrace(
|
||||
libc::PTRACE_PEEKDATA,
|
||||
self.child,
|
||||
address,
|
||||
null_mut::<c_void>(),
|
||||
);
|
||||
if libc::__errno_location().read_volatile() != 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(word as _)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ChildTraceImpl {
|
||||
fn drop(&mut self) {
|
||||
let mut ignore = 0;
|
||||
unsafe {
|
||||
libc::ptrace(libc::PTRACE_DETACH, self.child);
|
||||
libc::waitpid(self.child, &mut ignore, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::process::CommandSpawnExt;
|
||||
|
||||
fn dummy_sigint() {}
|
||||
|
||||
@@ -182,8 +61,6 @@ pub fn clone_fd(fd: RawFd) -> io::Result<RawFd> {
|
||||
}
|
||||
|
||||
impl CommandSpawnExt for Command {
|
||||
type ChildTrace = ChildTraceImpl;
|
||||
|
||||
fn create_process_group(&mut self) -> io::Result<&mut Self> {
|
||||
Ok(self.process_group(0))
|
||||
}
|
||||
@@ -192,54 +69,4 @@ impl CommandSpawnExt for Command {
|
||||
// TODO
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn spawn_with_trace(&mut self) -> io::Result<Self::ChildTrace> {
|
||||
unsafe {
|
||||
match libc::fork() {
|
||||
0 => {
|
||||
// Child
|
||||
// if libc::ptrace(libc::PTRACE_TRACEME, libc::getpid()) != 0 {
|
||||
// panic!("PTRACE_TRACEME: {}", io::Error::last_os_error());
|
||||
// }
|
||||
libc::raise(libc::SIGSTOP);
|
||||
panic!("exec error: {}", self.exec());
|
||||
}
|
||||
code if code < 0 => {
|
||||
// Error
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
pid => {
|
||||
let options = libc::PTRACE_O_TRACESYSGOOD | libc::PTRACE_O_TRACEEXEC;
|
||||
let mut ignore = 0;
|
||||
// Parent
|
||||
if libc::ptrace(
|
||||
libc::PTRACE_SEIZE,
|
||||
pid,
|
||||
null_mut::<c_void>(),
|
||||
null_mut::<c_void>(),
|
||||
) != 0
|
||||
{
|
||||
// TODO waitpid child? kill child?
|
||||
eprintln!("PTRACE_SEIZE error");
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
if libc::waitpid(pid, &mut ignore, 0) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
if libc::ptrace(libc::PTRACE_SETOPTIONS, pid, null_mut::<c_void>(), options)
|
||||
!= 0
|
||||
{
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
libc::ptrace(
|
||||
libc::PTRACE_CONT,
|
||||
pid,
|
||||
null_mut::<c_void>(),
|
||||
null_mut::<c_void>(),
|
||||
);
|
||||
Ok(ChildTraceImpl { child: pid })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,20 +11,8 @@ pub mod timer;
|
||||
|
||||
use std::{
|
||||
io,
|
||||
mem::MaybeUninit,
|
||||
os::{
|
||||
fd::RawFd,
|
||||
yggdrasil::process::{ChildExt, CommandExt},
|
||||
},
|
||||
process::{Child, Command},
|
||||
};
|
||||
|
||||
use runtime::{
|
||||
abi::{
|
||||
arch::SavedFrame,
|
||||
debug::{self, TraceFlags},
|
||||
},
|
||||
rt::{debug::debug_control, process::ThreadEvent},
|
||||
os::{fd::RawFd, yggdrasil::process::CommandExt},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
pub use mem::{FileMappingImpl, SharedMemoryImpl};
|
||||
@@ -36,131 +24,7 @@ pub use socket::{BorrowedAddressImpl, LocalPacketSocketImpl, OwnedAddressImpl};
|
||||
pub use term::RawStdinImpl;
|
||||
pub use timer::TimerFdImpl;
|
||||
|
||||
use crate::process::{ChildTrace, CommandSpawnExt, TraceEvent};
|
||||
|
||||
pub struct ChildTraceImpl {
|
||||
#[allow(unused)]
|
||||
child: Child,
|
||||
buffer: [u8; 512],
|
||||
tid: u32,
|
||||
}
|
||||
|
||||
impl ChildTraceImpl {
|
||||
fn attach(child: Child) -> io::Result<Self> {
|
||||
let mut buffer = [0; 512];
|
||||
let tid = child.main_thread_id()?;
|
||||
|
||||
debug_control::<debug::SetTraceFlags>(
|
||||
tid,
|
||||
&mut buffer,
|
||||
&(TraceFlags::SYSCALL_ENTRY | TraceFlags::SYSCALL_EXIT),
|
||||
)
|
||||
.map_err(io::Error::from)
|
||||
.unwrap();
|
||||
|
||||
Ok(Self { child, tid, buffer })
|
||||
}
|
||||
|
||||
fn read_regs(&mut self) -> io::Result<SavedFrame> {
|
||||
debug_control::<debug::GetRegisters>(self.tid, &mut self.buffer, &())
|
||||
.map_err(io::Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(rust_analyzer, target_arch = "aarch64"))]
|
||||
impl ChildTraceImpl {
|
||||
fn read_syscall_entry(&mut self) -> io::Result<(u64, [u64; 6])> {
|
||||
let frame = self.read_regs()?;
|
||||
Ok((
|
||||
frame.gp_regs[8] as _,
|
||||
[
|
||||
frame.gp_regs[0] as _,
|
||||
frame.gp_regs[1] as _,
|
||||
frame.gp_regs[2] as _,
|
||||
frame.gp_regs[3] as _,
|
||||
frame.gp_regs[4] as _,
|
||||
frame.gp_regs[5] as _,
|
||||
],
|
||||
))
|
||||
}
|
||||
|
||||
fn read_syscall_exit(&mut self) -> io::Result<u64> {
|
||||
let frame = self.read_regs()?;
|
||||
Ok(frame.gp_regs[0] as _)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(rust_analyzer, target_arch = "x86_64"))]
|
||||
impl ChildTraceImpl {
|
||||
fn read_syscall_entry(&mut self) -> io::Result<(u64, [u64; 6])> {
|
||||
let frame = self.read_regs()?;
|
||||
Ok((
|
||||
frame.rax as _,
|
||||
[
|
||||
frame.rdi as _,
|
||||
frame.rsi as _,
|
||||
frame.rdx as _,
|
||||
frame.r10 as _,
|
||||
frame.r8 as _,
|
||||
frame.r9 as _,
|
||||
],
|
||||
))
|
||||
}
|
||||
|
||||
fn read_syscall_exit(&mut self) -> io::Result<u64> {
|
||||
let frame = self.read_regs()?;
|
||||
Ok(frame.rax as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl ChildTrace for ChildTraceImpl {
|
||||
unsafe fn peek_u64(&mut self, address: usize) -> io::Result<u64> {
|
||||
let word = debug_control::<debug::ReadMemory>(self.tid, &mut self.buffer, &address)?;
|
||||
Ok(word as _)
|
||||
}
|
||||
|
||||
fn kill(&mut self) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn resume(&mut self) -> io::Result<()> {
|
||||
debug_control::<debug::Resume>(self.tid, &mut self.buffer, &false).map_err(io::Error::from)
|
||||
}
|
||||
|
||||
fn next_event(&mut self) -> io::Result<TraceEvent> {
|
||||
loop {
|
||||
let mut event = MaybeUninit::uninit();
|
||||
unsafe { runtime::rt::sys::wait_thread(self.tid, &mut event) }
|
||||
.map_err(io::Error::from)?;
|
||||
let event = unsafe { event.assume_init() };
|
||||
|
||||
match event {
|
||||
ThreadEvent::Trace(trace) => {
|
||||
let event = match trace.payload {
|
||||
debug::TraceEventPayload::SyscallEntry(_seq) => {
|
||||
let (nr, args) = self.read_syscall_entry()?;
|
||||
TraceEvent::SyscallEntry(nr, args)
|
||||
}
|
||||
debug::TraceEventPayload::SyscallExit(_seq) => {
|
||||
let status = self.read_syscall_exit()?;
|
||||
TraceEvent::SyscallExit(status as _)
|
||||
}
|
||||
debug::TraceEventPayload::SingleStep => {
|
||||
self.resume()?;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
break Ok(event);
|
||||
}
|
||||
ThreadEvent::Exited => {
|
||||
// TODO status
|
||||
break Ok(TraceEvent::Exited);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::process::CommandSpawnExt;
|
||||
|
||||
pub fn set_sigint_handler(_handler: fn()) {}
|
||||
|
||||
@@ -181,13 +45,6 @@ pub fn clone_fd(fd: RawFd) -> io::Result<RawFd> {
|
||||
}
|
||||
|
||||
impl CommandSpawnExt for Command {
|
||||
type ChildTrace = ChildTraceImpl;
|
||||
|
||||
fn spawn_with_trace(&mut self) -> io::Result<Self::ChildTrace> {
|
||||
unsafe { self.attach_tracing() };
|
||||
self.spawn().and_then(ChildTraceImpl::attach)
|
||||
}
|
||||
|
||||
fn create_process_group(&mut self) -> io::Result<&mut Self> {
|
||||
let group = unsafe { runtime::rt::sys::create_process_group() };
|
||||
Ok(self.process_group(group))
|
||||
|
||||
@@ -41,10 +41,6 @@ path = "src/lib.rs"
|
||||
name = "mount"
|
||||
path = "src/mount.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "chroot"
|
||||
path = "src/chroot.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "login"
|
||||
path = "src/login.rs"
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
use std::process::ExitCode;
|
||||
|
||||
fn usage(cmd: &str) {
|
||||
eprintln!("Usage: {cmd} PATH PROGRAM [ARGS...]");
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let mut args = std::env::args();
|
||||
let cmd = args.next().unwrap();
|
||||
let Some(path) = args.next() else {
|
||||
usage(&cmd);
|
||||
return ExitCode::FAILURE;
|
||||
};
|
||||
let Some(program) = args.next() else {
|
||||
usage(&cmd);
|
||||
return ExitCode::FAILURE;
|
||||
};
|
||||
let cmd_args = args.collect::<Vec<_>>();
|
||||
|
||||
println!("Run {program:?} at {path:?} with args {cmd_args:?}");
|
||||
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
@@ -1,3 +1,68 @@
|
||||
#![feature(yggdrasil_os, if_let_guard)]
|
||||
|
||||
fn main() {}
|
||||
// use std::{fmt::{LowerHex, Display}, io, path::PathBuf, process::Command};
|
||||
//
|
||||
// use clap::Parser;
|
||||
// use debugger::{Debugger, SymbolResolver};
|
||||
// use imp::TargetImpl;
|
||||
// use yggdrasil_abi::arch::SavedFrame;
|
||||
//
|
||||
// #[cfg(any(target_arch = "x86_64", target_arch = "x86", rust_analyzer))]
|
||||
// #[path = "x86.rs"]
|
||||
// pub mod imp;
|
||||
// #[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
// #[path = "aarch64.rs"]
|
||||
// pub mod imp;
|
||||
//
|
||||
// pub mod comm;
|
||||
// pub mod state;
|
||||
//
|
||||
// pub mod debugger;
|
||||
//
|
||||
// #[derive(thiserror::Error, Debug)]
|
||||
// pub enum Error {
|
||||
// #[error("I/O error: {0}")]
|
||||
// IoError(#[from] io::Error),
|
||||
// #[error("Terminal error: {0}")]
|
||||
// TermError(#[from] libterm::Error),
|
||||
// #[error("Debug control error: {0:?}")]
|
||||
// DebugError(yggdrasil_rt::Error),
|
||||
// #[error("Invalid address: {0:?}")]
|
||||
// InvalidAddress(String)
|
||||
// }
|
||||
//
|
||||
// pub trait Target {
|
||||
// type Instruction;
|
||||
// type InstructionFormatter: InstructionFormatter<Self::Instruction>;
|
||||
// type Register: LowerHex;
|
||||
//
|
||||
// fn disassemble(
|
||||
// window: &[u8],
|
||||
// ip: usize,
|
||||
// limit: usize,
|
||||
// ) -> Result<Vec<(usize, Self::Instruction)>, Error>;
|
||||
// fn new_instruction_formatter(resolver: SymbolResolver) -> Self::InstructionFormatter;
|
||||
//
|
||||
// fn register_list(frame: &SavedFrame, out: &mut Vec<(String, Self::Register)>);
|
||||
// fn flags_register_as_display(frame: &SavedFrame) -> impl Display;
|
||||
// fn real_ip(frame: &SavedFrame) -> usize;
|
||||
// }
|
||||
//
|
||||
// pub trait InstructionFormatter<I> {
|
||||
// fn format_instruction(&mut self, insn: &I, out: &mut String);
|
||||
// }
|
||||
//
|
||||
// #[derive(Parser)]
|
||||
// struct Args {
|
||||
// program: PathBuf,
|
||||
// }
|
||||
//
|
||||
// fn main() {
|
||||
// let args = Args::parse();
|
||||
//
|
||||
// let command = Command::new(&args.program);
|
||||
// let debug: Debugger<TargetImpl> = Debugger::from_command(&args.program, command).unwrap();
|
||||
//
|
||||
// debug.run().unwrap();
|
||||
// }
|
||||
|
||||
@@ -4,19 +4,9 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cross.workspace = true
|
||||
runtime.workspace = true
|
||||
|
||||
clap.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "yggdrasil")'.dependencies]
|
||||
runtime.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
libc = "*"
|
||||
|
||||
[dev-dependencies]
|
||||
runtime.workspace = true
|
||||
libc = "*"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
#[cfg(any(rust_analyzer, target_arch = "x86_64"))]
|
||||
#[path = "x86_64.rs"]
|
||||
pub mod imp;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
pub use imp::*;
|
||||
use libc::{c_int, c_uint};
|
||||
|
||||
macro_rules! impl_ioctls {
|
||||
($f:expr, $v:expr, $ty:ty, [$($ioctl:ident),* $(,)?]) => {{
|
||||
#[allow(unreachable_patterns)]
|
||||
let v = match $v as $ty {
|
||||
$(libc::$ioctl => stringify!($ioctl),)*
|
||||
_ => return write!($f, "{:#x}", $v),
|
||||
};
|
||||
write!($f, "{v}")
|
||||
}};
|
||||
}
|
||||
|
||||
fn print_ioctl<W: fmt::Write>(f: &mut W, v: c_uint) -> fmt::Result {
|
||||
impl_ioctls!(
|
||||
f,
|
||||
v,
|
||||
u64,
|
||||
[
|
||||
TCGETS,
|
||||
TCSETS,
|
||||
TCSETSW,
|
||||
TCSETSF,
|
||||
TCGETA,
|
||||
TCSETA,
|
||||
TCSETAW,
|
||||
TCSETAF,
|
||||
TCSBRK,
|
||||
TCXONC,
|
||||
TCFLSH,
|
||||
TIOCEXCL,
|
||||
TIOCNXCL,
|
||||
TIOCSCTTY,
|
||||
TIOCGPGRP,
|
||||
TIOCSPGRP,
|
||||
TIOCOUTQ,
|
||||
TIOCSTI,
|
||||
TIOCGWINSZ,
|
||||
TIOCSWINSZ,
|
||||
TIOCMGET,
|
||||
TIOCMBIS,
|
||||
TIOCMBIC,
|
||||
TIOCMSET,
|
||||
TIOCGSOFTCAR,
|
||||
TIOCSSOFTCAR,
|
||||
FIONREAD,
|
||||
TIOCLINUX,
|
||||
TIOCCONS,
|
||||
TIOCGSERIAL,
|
||||
TIOCSSERIAL,
|
||||
TIOCPKT,
|
||||
FIONBIO,
|
||||
TIOCNOTTY,
|
||||
TIOCSETD,
|
||||
TIOCGETD,
|
||||
TCSBRKP,
|
||||
TIOCSBRK,
|
||||
TIOCCBRK,
|
||||
TIOCGSID,
|
||||
TCGETS2,
|
||||
TCSETS2,
|
||||
TCSETSW2,
|
||||
TCSETSF2,
|
||||
TIOCGRS485,
|
||||
TIOCSRS485,
|
||||
TIOCGPTN,
|
||||
TIOCSPTLCK,
|
||||
TIOCGDEV,
|
||||
TCGETX,
|
||||
TCSETX,
|
||||
TCSETXF,
|
||||
TCSETXW,
|
||||
TIOCSIG,
|
||||
TIOCVHANGUP,
|
||||
TIOCGPKT,
|
||||
TIOCGPTLCK,
|
||||
TIOCGEXCL,
|
||||
TIOCGPTPEER,
|
||||
FIONCLEX,
|
||||
FIOCLEX,
|
||||
FIOASYNC,
|
||||
TIOCSERCONFIG,
|
||||
TIOCSERGWILD,
|
||||
TIOCSERSWILD,
|
||||
TIOCGLCKTRMIOS,
|
||||
TIOCSLCKTRMIOS,
|
||||
TIOCSERGSTRUCT,
|
||||
TIOCSERGETLSR,
|
||||
TIOCSERGETMULTI,
|
||||
TIOCSERSETMULTI,
|
||||
TIOCMIWAIT,
|
||||
TIOCGICOUNT,
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
fn print_prctl<W: fmt::Write>(f: &mut W, c: c_int) -> fmt::Result {
|
||||
impl_ioctls!(
|
||||
f,
|
||||
c,
|
||||
c_int,
|
||||
[
|
||||
PR_SET_PDEATHSIG,
|
||||
PR_GET_PDEATHSIG,
|
||||
PR_GET_DUMPABLE,
|
||||
PR_SET_DUMPABLE,
|
||||
PR_GET_UNALIGN,
|
||||
PR_SET_UNALIGN,
|
||||
PR_GET_KEEPCAPS,
|
||||
PR_SET_KEEPCAPS,
|
||||
PR_GET_FPEMU,
|
||||
PR_SET_FPEMU,
|
||||
PR_GET_FPEXC,
|
||||
PR_SET_FPEXC,
|
||||
PR_GET_TIMING,
|
||||
PR_SET_TIMING,
|
||||
PR_SET_NAME,
|
||||
PR_GET_NAME,
|
||||
PR_GET_ENDIAN,
|
||||
PR_SET_ENDIAN,
|
||||
PR_GET_SECCOMP,
|
||||
PR_SET_SECCOMP,
|
||||
PR_CAPBSET_READ,
|
||||
PR_CAPBSET_DROP,
|
||||
PR_GET_TSC,
|
||||
PR_SET_TSC,
|
||||
PR_GET_SECUREBITS,
|
||||
PR_SET_SECUREBITS,
|
||||
PR_SET_TIMERSLACK,
|
||||
PR_GET_TIMERSLACK,
|
||||
PR_TASK_PERF_EVENTS_DISABLE,
|
||||
PR_TASK_PERF_EVENTS_ENABLE,
|
||||
PR_MCE_KILL,
|
||||
PR_SET_MM,
|
||||
PR_SET_PTRACER,
|
||||
PR_SET_CHILD_SUBREAPER,
|
||||
PR_GET_CHILD_SUBREAPER,
|
||||
PR_SET_NO_NEW_PRIVS,
|
||||
PR_GET_NO_NEW_PRIVS,
|
||||
PR_GET_TID_ADDRESS,
|
||||
PR_SET_THP_DISABLE,
|
||||
PR_GET_THP_DISABLE,
|
||||
PR_MPX_ENABLE_MANAGEMENT,
|
||||
PR_MPX_DISABLE_MANAGEMENT,
|
||||
PR_SET_FP_MODE,
|
||||
PR_GET_FP_MODE,
|
||||
PR_CAP_AMBIENT,
|
||||
PR_GET_SPECULATION_CTRL,
|
||||
PR_SET_SPECULATION_CTRL,
|
||||
PR_SCHED_CORE,
|
||||
PR_SET_MDWE,
|
||||
PR_GET_MDWE,
|
||||
PR_SET_VMA,
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
fn print_access_mode<W: fmt::Write>(f: &mut W, mode: c_int) -> fmt::Result {
|
||||
let s = match mode {
|
||||
libc::F_OK => "F_OK",
|
||||
libc::W_OK => "W_OK",
|
||||
libc::R_OK => "R_OK",
|
||||
libc::X_OK => "X_OK",
|
||||
_ => return write!(f, "{mode:#x}"),
|
||||
};
|
||||
|
||||
write!(f, "{s}")
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
use std::{ffi::c_void, fmt};
|
||||
|
||||
use cross::process::ChildTrace;
|
||||
use libc::{c_char, c_int, c_long, c_uint, c_ulong};
|
||||
|
||||
use crate::definition::{
|
||||
imp::{print_access_mode, print_ioctl, print_prctl},
|
||||
print_cstring, PrintSyscallArgument, Syscall,
|
||||
};
|
||||
|
||||
pub enum SyscallArgument {
|
||||
Fd(c_int),
|
||||
MaybeFd(c_int),
|
||||
Bytes(*const c_char),
|
||||
BytesMut(*const c_char),
|
||||
Size(usize),
|
||||
Filename(*const c_char),
|
||||
OpenOptions(c_int),
|
||||
FileMode(c_int),
|
||||
Ptr(*const c_void),
|
||||
Long(c_long),
|
||||
ULong(c_ulong),
|
||||
Int(c_int),
|
||||
UInt(c_uint),
|
||||
Whence(c_int),
|
||||
Off(libc::off_t),
|
||||
LOff(libc::loff_t),
|
||||
IoctlCmd(c_uint),
|
||||
PrctlCmd(c_int),
|
||||
AccessMode(c_int),
|
||||
AtFd(c_int),
|
||||
}
|
||||
|
||||
impl PrintSyscallArgument for SyscallArgument {
|
||||
fn print_syscall_argument<W: fmt::Write, T: ChildTrace>(&self, f: &mut W, trace: &mut T) {
|
||||
match self {
|
||||
Self::Fd(libc::STDIN_FILENO) => write!(f, "STDIN_FILENO"),
|
||||
Self::Fd(libc::STDOUT_FILENO) => write!(f, "STDOUT_FILENO"),
|
||||
Self::Fd(libc::STDERR_FILENO) => write!(f, "STDERR_FILENO"),
|
||||
Self::Fd(fd) => write!(f, "{fd}"),
|
||||
Self::MaybeFd(-1) => write!(f, "<none>"),
|
||||
Self::MaybeFd(fd) => write!(f, "{fd}"),
|
||||
Self::Bytes(bytes) => write!(f, "{bytes:p}"),
|
||||
Self::BytesMut(bytes) => write!(f, "{bytes:p}"),
|
||||
Self::Size(size) if *size <= 999 => write!(f, "{size}"),
|
||||
Self::Size(size) => write!(f, "{size:#x}"),
|
||||
Self::Filename(filename) => print_cstring(f, trace, filename.addr()),
|
||||
Self::OpenOptions(opts) => write!(f, "{opts:#x}"),
|
||||
Self::FileMode(mode) => write!(f, "0{mode:o}"),
|
||||
Self::Ptr(ptr) => write!(f, "{ptr:p}"),
|
||||
Self::Long(val) => write!(f, "{val}"),
|
||||
Self::ULong(val) => write!(f, "{val}"),
|
||||
Self::Int(val) => write!(f, "{val}"),
|
||||
Self::UInt(val) => write!(f, "{val}"),
|
||||
Self::Whence(libc::SEEK_SET) => write!(f, "SEEK_SET"),
|
||||
Self::Whence(libc::SEEK_CUR) => write!(f, "SEEK_CUR"),
|
||||
Self::Whence(libc::SEEK_END) => write!(f, "SEEK_END"),
|
||||
Self::Whence(w) => write!(f, "{w}"),
|
||||
Self::Off(off) => write!(f, "{off}"),
|
||||
Self::LOff(off) => write!(f, "{off}"),
|
||||
&Self::IoctlCmd(v) => print_ioctl(f, v),
|
||||
&Self::PrctlCmd(v) => print_prctl(f, v),
|
||||
&Self::AccessMode(m) => print_access_mode(f, m),
|
||||
Self::AtFd(libc::AT_FDCWD) => write!(f, "AT_FDCWD"),
|
||||
Self::AtFd(fd) => write!(f, "{fd}"),
|
||||
}
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_syscall_args {
|
||||
($input:expr, [$($collect:expr),*], $i:expr, [$arg:ident, $($tail:ident),+]) => {
|
||||
impl_syscall_args!($input, [$($collect,)* (SyscallArgument::$arg($input[$i] as _))], $i + 1, [$($tail),+])
|
||||
};
|
||||
($input:expr, [$($collect:expr),*], $i:expr, [$arg:ident]) => {
|
||||
vec![$($collect,)* (SyscallArgument::$arg($input[$i] as _))]
|
||||
};
|
||||
($input:expr, [], $i:expr, []) => {
|
||||
vec![]
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_syscall_parse {
|
||||
($(
|
||||
$nr:literal => $name:ident ( $($arg:ident),* )
|
||||
),+ $(,)?) => {
|
||||
pub fn parse_syscall(nr: u64, args: &[u64]) -> Option<Syscall> {
|
||||
Some(match nr {
|
||||
$($nr => Syscall(
|
||||
stringify!($name),
|
||||
impl_syscall_args!(args, [], 0, [$($arg),*])
|
||||
),)+
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_syscall_parse!(
|
||||
0 => read(Fd, BytesMut, Size),
|
||||
1 => write(Fd, Bytes, Size),
|
||||
2 => open(Filename, OpenOptions, FileMode),
|
||||
3 => close(Fd),
|
||||
4 => stat(Filename, BytesMut),
|
||||
5 => fstat(Fd, BytesMut),
|
||||
6 => lstat(Filename, BytesMut),
|
||||
7 => poll(Ptr, ULong, Long),
|
||||
8 => lseek(Fd, Off, Whence),
|
||||
9 => mmap(Ptr, ULong, ULong, ULong, MaybeFd, ULong),
|
||||
10 => mprotect(Ptr, Size, ULong),
|
||||
11 => munmap(Ptr, Size),
|
||||
12 => brk(Ptr),
|
||||
// TODO rt_...
|
||||
16 => ioctl(Fd, IoctlCmd, ULong),
|
||||
17 => pread64(Fd, BytesMut, Size, LOff),
|
||||
21 => access(Filename, AccessMode),
|
||||
157 => prctl(PrctlCmd, ULong, ULong, ULong),
|
||||
218 => set_tid_address(Ptr),
|
||||
334 => rseq(Ptr, UInt, Int, UInt),
|
||||
257 => openat(AtFd, Filename, OpenOptions, FileMode),
|
||||
262 => newfstatat(AtFd, Filename, Ptr, Int),
|
||||
24 => sched_yield(),
|
||||
);
|
||||
@@ -1,67 +0,0 @@
|
||||
use std::fmt;
|
||||
|
||||
use cross::process::ChildTrace;
|
||||
|
||||
#[cfg(any(rust_analyzer, target_os = "linux"))]
|
||||
#[path = "linux/mod.rs"]
|
||||
pub mod imp;
|
||||
#[cfg(any(rust_analyzer, target_os = "yggdrasil"))]
|
||||
#[path = "yggdrasil.rs"]
|
||||
pub mod imp;
|
||||
|
||||
pub use imp::*;
|
||||
|
||||
pub trait PrintSyscallArgument {
|
||||
fn print_syscall_argument<W: fmt::Write, T: ChildTrace>(&self, f: &mut W, trace: &mut T);
|
||||
}
|
||||
|
||||
pub struct Syscall(pub &'static str, pub Vec<SyscallArgument>);
|
||||
|
||||
impl Syscall {
|
||||
pub fn print<W: fmt::Write, T: ChildTrace>(&self, f: &mut W, trace: &mut T) {
|
||||
write!(f, "{}(", self.0).ok();
|
||||
for (i, arg) in self.1.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ").ok();
|
||||
}
|
||||
arg.print_syscall_argument(f, trace);
|
||||
}
|
||||
write!(f, ")").ok();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_string<W: fmt::Write, T: ChildTrace>(
|
||||
f: &mut W,
|
||||
trace: &mut T,
|
||||
address: usize,
|
||||
len: usize,
|
||||
) -> fmt::Result {
|
||||
let mut buffer = [0; 64];
|
||||
let len = len.min(buffer.len());
|
||||
if let Ok(()) = unsafe { trace.peek_bytes(address, &mut buffer[..len]) } {
|
||||
if let Ok(s) = std::str::from_utf8(&buffer[..len]) {
|
||||
write!(f, "{s:?}")
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
} else {
|
||||
write!(f, "<error str {address:#x}>")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_cstring<W: fmt::Write, T: ChildTrace>(
|
||||
f: &mut W,
|
||||
trace: &mut T,
|
||||
address: usize,
|
||||
) -> fmt::Result {
|
||||
let mut buffer = [0; 64];
|
||||
if let Ok(len) = unsafe { trace.peek_cstr(address, &mut buffer) } {
|
||||
if let Ok(s) = std::str::from_utf8(&buffer[..len]) {
|
||||
write!(f, "{s:?}")
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
} else {
|
||||
write!(f, "<error str {address:#x}>")
|
||||
}
|
||||
}
|
||||
@@ -1,335 +0,0 @@
|
||||
use std::fmt;
|
||||
|
||||
use cross::process::ChildTrace;
|
||||
use runtime::{abi::SyscallFunction, abi_lib::SyscallRegister, rt::io::RawFd};
|
||||
|
||||
use crate::definition::{print_string, PrintSyscallArgument, Syscall};
|
||||
|
||||
pub enum SyscallArgument {
|
||||
Bytes(u64, u64),
|
||||
BytesMut(u64, u64),
|
||||
MaybePtr(u64),
|
||||
Fd(u64),
|
||||
OptionFd(u64),
|
||||
Ptr(u64),
|
||||
Size(u64),
|
||||
U64(u64),
|
||||
I64(i64),
|
||||
U32(u32),
|
||||
I32(i32),
|
||||
PtrSystemTime(u64),
|
||||
ClockType(u64),
|
||||
Filename(u64, u64),
|
||||
}
|
||||
|
||||
pub enum ArgType {
|
||||
Bytes,
|
||||
BytesMut,
|
||||
MaybePtr,
|
||||
Fd,
|
||||
OptionFd,
|
||||
Ptr,
|
||||
Size,
|
||||
U64,
|
||||
I64,
|
||||
U32,
|
||||
I32,
|
||||
PtrSystemTime,
|
||||
ClockType,
|
||||
Filename,
|
||||
}
|
||||
|
||||
struct SyscallDefinition(SyscallFunction, &'static str, &'static [ArgType]);
|
||||
|
||||
impl PrintSyscallArgument for SyscallArgument {
|
||||
fn print_syscall_argument<W: fmt::Write, T: ChildTrace>(&self, f: &mut W, trace: &mut T) {
|
||||
match self {
|
||||
&Self::MaybePtr(0) => write!(f, "None"),
|
||||
Self::Fd(fd) => {
|
||||
let fd = unsafe { RawFd::from_raw(*fd as _) };
|
||||
match fd {
|
||||
RawFd::STDIN => write!(f, "STDIN"),
|
||||
RawFd::STDOUT => write!(f, "STDOUT"),
|
||||
RawFd::STDERR => write!(f, "STDERR"),
|
||||
_ => write!(f, "{fd:?}"),
|
||||
}
|
||||
}
|
||||
Self::OptionFd(fd) => match Option::<RawFd>::from_syscall_register(*fd as _) {
|
||||
Some(fd) => write!(f, "Some({fd:?})"),
|
||||
None => write!(f, "None"),
|
||||
},
|
||||
Self::MaybePtr(addr) => write!(f, "Some({addr:#x})"),
|
||||
Self::Ptr(ptr) => write!(f, "{ptr:#x}"),
|
||||
Self::U64(val) => write!(f, "{val}"),
|
||||
Self::I64(val) => write!(f, "{val}"),
|
||||
Self::U32(val) => write!(f, "{val}"),
|
||||
Self::I32(val) => write!(f, "{val}"),
|
||||
Self::Size(val) => write!(f, "{val}"),
|
||||
Self::Bytes(addr, len) => write!(f, "&[{addr:#x}; {len}]"),
|
||||
Self::BytesMut(addr, len) => write!(f, "&mut [{addr:#x}; {len}]"),
|
||||
Self::PtrSystemTime(ptr) => write!(f, "{ptr:#x}"),
|
||||
Self::ClockType(1) => write!(f, "RealTime"),
|
||||
Self::ClockType(2) => write!(f, "Monotonic"),
|
||||
Self::ClockType(v) => write!(f, "{v}"),
|
||||
Self::Filename(ptr, len) => print_string(f, trace, *ptr as _, *len as _),
|
||||
}
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
const DEFS: &[SyscallDefinition] = &const {
|
||||
use ArgType::*;
|
||||
[
|
||||
SyscallDefinition(SyscallFunction::GetRandom, "get_random", &[BytesMut]),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetClock,
|
||||
"get_clock",
|
||||
&[ClockType, PtrSystemTime],
|
||||
),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::SetClock,
|
||||
"set_clock",
|
||||
&[ClockType, PtrSystemTime],
|
||||
),
|
||||
SyscallDefinition(SyscallFunction::Mount, "mount", &[Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Unmount, "unmount", &[Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::LoadModule, "load_module", &[Filename]),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::FilesystemControl,
|
||||
"filesystem_control",
|
||||
&[OptionFd, U32, Size],
|
||||
),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetSystemInfo,
|
||||
"get_system_info",
|
||||
&[U32, BytesMut],
|
||||
), // TODO
|
||||
// Memory management
|
||||
SyscallDefinition(
|
||||
SyscallFunction::MapMemory,
|
||||
"map_memory",
|
||||
&[MaybePtr, Size, U32, Ptr],
|
||||
),
|
||||
SyscallDefinition(SyscallFunction::UnmapMemory, "unmap_memory", &[Ptr, Size]),
|
||||
// Process/thread management
|
||||
SyscallDefinition(
|
||||
SyscallFunction::CreateProcessGroup,
|
||||
"create_process_group",
|
||||
&[],
|
||||
),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetProcessGroupId,
|
||||
"get_process_group_id",
|
||||
&[],
|
||||
),
|
||||
SyscallDefinition(SyscallFunction::ExitProcess, "exit_process", &[U64]), // TODO
|
||||
SyscallDefinition(SyscallFunction::SpawnProcess, "spawn_process", &[Ptr]), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::WaitProcess,
|
||||
"wait_process",
|
||||
&[Ptr, Ptr, U32],
|
||||
), // TODO
|
||||
SyscallDefinition(SyscallFunction::GetPid, "get_pid", &[]),
|
||||
SyscallDefinition(SyscallFunction::GetTid, "get_tid", &[]),
|
||||
SyscallDefinition(SyscallFunction::SpawnThread, "spawn_thread", &[Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::ExitThread, "exit_thread", &[]),
|
||||
SyscallDefinition(SyscallFunction::WaitThread, "wait_thread", &[U32, Ptr]), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetThreadOption,
|
||||
"get_thread_option",
|
||||
&[U32, BytesMut],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::SetThreadOption,
|
||||
"set_thread_option",
|
||||
&[U32, Bytes],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetProcessOption,
|
||||
"get_process_option",
|
||||
&[U32, U32, BytesMut],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::SetProcessOption,
|
||||
"set_process_option",
|
||||
&[U32, U32, Bytes],
|
||||
), // TODO
|
||||
SyscallDefinition(SyscallFunction::Nanosleep, "nanosleep", &[Ptr, Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::ExitSignal, "exit_signal", &[Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::SendSignal, "send_signal", &[U32, U32]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Mutex, "mutex", &[Ptr, Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::StartSession, "start_session", &[]),
|
||||
// I/O
|
||||
SyscallDefinition(
|
||||
SyscallFunction::Open,
|
||||
"open",
|
||||
&[OptionFd, Filename, U32, U32],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::CheckAccess,
|
||||
"check_access",
|
||||
&[OptionFd, Filename, U32],
|
||||
), // TODO
|
||||
SyscallDefinition(SyscallFunction::Close, "close", &[Fd]),
|
||||
SyscallDefinition(SyscallFunction::Write, "write", &[Fd, Bytes]),
|
||||
SyscallDefinition(SyscallFunction::Read, "read", &[Fd, BytesMut]),
|
||||
SyscallDefinition(SyscallFunction::Seek, "seek", &[Fd, U32, Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Truncate, "truncate", &[Fd, U64]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Fsync, "fsync", &[Fd, U32]), // TODO
|
||||
SyscallDefinition(SyscallFunction::ReadAt, "read_at", &[Fd, U64, BytesMut]),
|
||||
SyscallDefinition(SyscallFunction::WriteAt, "write_at", &[Fd, U64, Bytes]),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetFileOption,
|
||||
"get_file_option",
|
||||
&[Fd, U32, BytesMut],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::SetFileOption,
|
||||
"set_file_option",
|
||||
&[Fd, U32, Bytes],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::OpenDirectory,
|
||||
"open_directory",
|
||||
&[OptionFd, Filename],
|
||||
),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::ReadDirectoryEntries,
|
||||
"read_directory_entries",
|
||||
&[Fd, Ptr, Size],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::CreateDirectory,
|
||||
"create_directory",
|
||||
&[OptionFd, Filename, U32],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::ReadLink,
|
||||
"read_link",
|
||||
&[OptionFd, Filename, BytesMut],
|
||||
),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::Remove,
|
||||
"remove",
|
||||
&[OptionFd, Filename, U32],
|
||||
), // TODO
|
||||
SyscallDefinition(SyscallFunction::Rename, "rename", &[Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::CloneFd, "clone_fd", &[Fd, OptionFd]),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::UpdateMetadata,
|
||||
"update_metadata",
|
||||
&[OptionFd, Filename, Ptr],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetMetadata,
|
||||
"get_metadata",
|
||||
&[OptionFd, Filename, Ptr, U32],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::DeviceRequest,
|
||||
"device_request",
|
||||
&[Fd, U32, BytesMut, Size],
|
||||
), // TODO
|
||||
SyscallDefinition(SyscallFunction::CreateTimer, "create_timer", &[U32]), // TODO
|
||||
SyscallDefinition(SyscallFunction::CreatePid, "create_pid", &[Ptr, U32]), // TODO
|
||||
SyscallDefinition(SyscallFunction::CreatePty, "create_pty", &[Ptr, Ptr, Ptr]), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::CreateSharedMemory,
|
||||
"create_shared_memory",
|
||||
&[Size],
|
||||
),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::CreatePollChannel,
|
||||
"create_poll_channel",
|
||||
&[],
|
||||
),
|
||||
SyscallDefinition(SyscallFunction::CreatePipe, "create_pipe", &[Ptr]), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::PollChannelWait,
|
||||
"poll_channel_wait",
|
||||
&[Fd, Ptr, U32, Ptr],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::PollChannelControl,
|
||||
"poll_channel_control",
|
||||
&[Fd, U32, Fd],
|
||||
), // TODO
|
||||
// Network
|
||||
SyscallDefinition(SyscallFunction::CreateSocket, "create_socket", &[U32]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Bind, "bind", &[Fd, Bytes]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Listen, "listen", &[Fd]),
|
||||
SyscallDefinition(SyscallFunction::Connect, "connect", &[Fd, Bytes]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Accept, "accept", &[Fd, MaybePtr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Shutdown, "shutdown", &[Fd, U32]), // TODO
|
||||
SyscallDefinition(SyscallFunction::SendTo, "send_to", &[Fd, Bytes, MaybePtr]), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::ReceiveFrom,
|
||||
"receive_from",
|
||||
&[Fd, BytesMut, MaybePtr],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetSocketOption,
|
||||
"get_socket_option",
|
||||
&[Fd, U32, BytesMut],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::SetSocketOption,
|
||||
"set_socket_option",
|
||||
&[Fd, U32, Bytes],
|
||||
), // TODO
|
||||
SyscallDefinition(SyscallFunction::SendMessage, "send_message", &[Fd, Ptr]), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::ReceiveMessage,
|
||||
"receive_message",
|
||||
&[Fd, Ptr],
|
||||
), // TODO
|
||||
// C compat
|
||||
SyscallDefinition(SyscallFunction::Fork, "fork", &[]),
|
||||
SyscallDefinition(SyscallFunction::Execve, "execve", &[Ptr]), // TODO
|
||||
// Debugging
|
||||
SyscallDefinition(SyscallFunction::DebugTrace, "debug_trace", &[U32, Filename]), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::DebugControl,
|
||||
"debug_control",
|
||||
&[U32, U32, BytesMut, Size],
|
||||
), // TODO
|
||||
]
|
||||
};
|
||||
|
||||
impl SyscallDefinition {
|
||||
fn parse(&self, args: &[u64]) -> Syscall {
|
||||
let mut res = vec![];
|
||||
let mut pos = 0;
|
||||
for arg_def in self.2.iter() {
|
||||
use SyscallArgument::*;
|
||||
|
||||
let (a, l) = match arg_def {
|
||||
ArgType::MaybePtr => (MaybePtr(args[pos]), 1),
|
||||
ArgType::Bytes => (Bytes(args[pos], args[pos + 1]), 2),
|
||||
ArgType::BytesMut => (BytesMut(args[pos], args[pos + 1]), 2),
|
||||
ArgType::Fd => (Fd(args[pos]), 1),
|
||||
ArgType::OptionFd => (OptionFd(args[pos]), 1),
|
||||
ArgType::Ptr => (Ptr(args[pos]), 1),
|
||||
ArgType::Size => (Size(args[pos]), 1),
|
||||
ArgType::U64 => (U64(args[pos]), 1),
|
||||
ArgType::I64 => (I64(args[pos] as _), 1),
|
||||
ArgType::U32 => (U32(args[pos] as _), 1),
|
||||
ArgType::I32 => (I32(args[pos] as _), 1),
|
||||
ArgType::PtrSystemTime => (PtrSystemTime(args[pos]), 1),
|
||||
ArgType::ClockType => (ClockType(args[pos]), 1),
|
||||
ArgType::Filename => (Filename(args[pos], args[pos + 1]), 2),
|
||||
};
|
||||
|
||||
pos += l;
|
||||
res.push(a);
|
||||
}
|
||||
|
||||
Syscall(self.1, res)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_syscall(nr: u64, args: &[u64]) -> Option<Syscall> {
|
||||
let func = SyscallFunction::try_from(nr as usize).ok()?;
|
||||
let def = DEFS.iter().find(|e| e.0 == func)?;
|
||||
Some(def.parse(args))
|
||||
}
|
||||
@@ -1,68 +1,36 @@
|
||||
#![feature(rustc_private)]
|
||||
#![feature(rustc_private, yggdrasil_os, let_chains)]
|
||||
|
||||
use std::{
|
||||
io,
|
||||
process::{Command, ExitCode},
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
use cross::{
|
||||
process::{ChildTrace, CommandSpawnExt, TraceEvent},
|
||||
signal,
|
||||
};
|
||||
use tracer::CommandTracer;
|
||||
|
||||
use crate::definition::parse_syscall;
|
||||
|
||||
pub mod definition;
|
||||
pub mod format;
|
||||
pub mod tracer;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
program: String,
|
||||
command: String,
|
||||
args: Vec<String>,
|
||||
}
|
||||
|
||||
fn run(args: &Args) -> io::Result<()> {
|
||||
let mut command = Command::new(&args.command);
|
||||
command.args(&args.args);
|
||||
let tracer = CommandTracer::spawn(command)?;
|
||||
tracer.run(format::format_syscall)
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args = Args::parse();
|
||||
|
||||
static ABORT: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
signal::set_sigint_handler(|| {
|
||||
ABORT.store(true, Ordering::Release);
|
||||
eprintln!("SIGINT");
|
||||
});
|
||||
let mut child = Command::new(args.program)
|
||||
.args(args.args)
|
||||
.spawn_with_trace()
|
||||
.unwrap();
|
||||
|
||||
let mut last_syscall: Option<(u64, [u64; 6])> = None;
|
||||
|
||||
while !ABORT.load(Ordering::Relaxed) {
|
||||
let event = child.next_event().unwrap();
|
||||
match event {
|
||||
TraceEvent::Exited => {
|
||||
eprintln!("Exit");
|
||||
break;
|
||||
}
|
||||
TraceEvent::SyscallExit(ret) => match last_syscall {
|
||||
Some((nr, args)) => {
|
||||
let call = parse_syscall(nr, &args);
|
||||
if let Some(call) = call {
|
||||
let mut s = String::new();
|
||||
call.print(&mut s, &mut child);
|
||||
eprintln!("{s} -> {ret}");
|
||||
} else {
|
||||
eprintln!("syscall {nr} {args:?} -> {ret}");
|
||||
}
|
||||
}
|
||||
None => {
|
||||
eprintln!("??? -> {ret}");
|
||||
}
|
||||
},
|
||||
TraceEvent::SyscallEntry(nr, args) => {
|
||||
last_syscall = Some((nr, args));
|
||||
}
|
||||
match run(&args) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(error) => {
|
||||
eprintln!("{}: {error}", args.command);
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
child.resume().ok();
|
||||
}
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ const PROGRAMS: &[(&str, &str)] = &[
|
||||
// sysutils
|
||||
("echo", "bin/echo"),
|
||||
("mount", "sbin/mount"),
|
||||
("chroot", "sbin/chroot"),
|
||||
("login", "sbin/login"),
|
||||
("strace", "bin/strace"),
|
||||
("ls", "bin/ls"),
|
||||
|
||||
Reference in New Issue
Block a user