Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22fba9cb30 | |||
| 552de70191 | |||
| 2964b668df | |||
| 01c78aaf89 | |||
| fb30dd9a40 | |||
| d78daca722 | |||
| 28d1aa4744 | |||
| b7cea07da6 | |||
| 51a3a9f8af | |||
| 6904c26ebe | |||
| 9905186449 | |||
| b68a129d37 | |||
| e3c75903ff | |||
| 1c330cedb7 | |||
| c7d94e4d8e | |||
| 77136432cf | |||
| 7566934c71 | |||
| a67841988f | |||
| 10d0b45371 | |||
| 6291d4412d | |||
| 3abf83c222 | |||
| 6bd269337a | |||
| 338ce7b282 | |||
| f22575dd0c | |||
| dc9987fb73 | |||
| c83b1452c4 | |||
| b1c37444d5 | |||
| b15c387e97 |
Generated
+219
-1
@@ -38,6 +38,12 @@ 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"
|
||||
@@ -643,7 +649,7 @@ checksum = "581d3afdd654deb68c19fcbe4bc411910cc64067d4a13d8637bda7722cb9c2ea"
|
||||
dependencies = [
|
||||
"endian-type-rs",
|
||||
"fallible-iterator",
|
||||
"memoffset",
|
||||
"memoffset 0.5.6",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"rustc_version 0.2.3",
|
||||
@@ -1095,6 +1101,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"kernel-arch-aarch64",
|
||||
"kernel-arch-hosted",
|
||||
"kernel-arch-i686",
|
||||
"kernel-arch-interface",
|
||||
"kernel-arch-riscv64",
|
||||
"kernel-arch-x86_64",
|
||||
@@ -1110,6 +1117,7 @@ dependencies = [
|
||||
"device-api",
|
||||
"kernel-arch-interface",
|
||||
"libk-mm-interface",
|
||||
"log",
|
||||
"static_assertions",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
@@ -1125,6 +1133,21 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel-arch-i686"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"device-api",
|
||||
"kernel-arch-interface",
|
||||
"kernel-arch-x86",
|
||||
"libk-mm-interface",
|
||||
"log",
|
||||
"static_assertions",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel-arch-interface"
|
||||
version = "0.1.0"
|
||||
@@ -1413,6 +1436,15 @@ 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"
|
||||
@@ -1497,6 +1529,12 @@ 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"
|
||||
@@ -2540,6 +2578,18 @@ 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"
|
||||
@@ -2585,6 +2635,26 @@ 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_fat32"
|
||||
version = "0.1.0"
|
||||
@@ -2627,6 +2697,21 @@ 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"
|
||||
@@ -2640,6 +2725,22 @@ 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"
|
||||
@@ -2657,6 +2758,26 @@ 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"
|
||||
@@ -2704,6 +2825,94 @@ 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"
|
||||
@@ -2743,6 +2952,7 @@ dependencies = [
|
||||
"git-version",
|
||||
"kernel-arch",
|
||||
"kernel-arch-aarch64",
|
||||
"kernel-arch-i686",
|
||||
"kernel-arch-interface",
|
||||
"kernel-arch-riscv64",
|
||||
"kernel-arch-x86",
|
||||
@@ -2758,13 +2968,21 @@ dependencies = [
|
||||
"vmalloc",
|
||||
"yboot-proto",
|
||||
"ygg_driver_acpi",
|
||||
"ygg_driver_ahci",
|
||||
"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",
|
||||
]
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![allow(unused)]
|
||||
#![no_std]
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
@@ -16,7 +16,6 @@ use crate::elf::types::{PT_LOAD, SHF_ALLOC, SHF_WRITE, SHT_PROGBITS};
|
||||
|
||||
use self::types::{Ehdr, Phdr, Shdr};
|
||||
|
||||
#[allow(unused)]
|
||||
mod types {
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ pub fn load_somewhere(
|
||||
|
||||
let file_info: &FileInfo = file.get_info(&mut info_buffer).unwrap();
|
||||
let size = file_info.file_size();
|
||||
let page_count = size.div_ceil(0x1000);
|
||||
let page_count = (size + 0xFFF) / 0x1000;
|
||||
|
||||
let base = bs.allocate_pages(
|
||||
AllocateType::MaxAddress(MAXIMUM_ADDRESS),
|
||||
|
||||
+38
-26
@@ -3,43 +3,55 @@
|
||||
|
||||
Booting Yggdrasil on Raspberry Pi 4B with u-boot:
|
||||
|
||||
1. Clone u-boot sources and build with rpi_4_defconfig
|
||||
1. Clone u-boot sources to some directory and checkout some
|
||||
stable branch. I've used v2024.10.
|
||||
2. Modify cmd/boot.c by replacing the do_go_exec function:
|
||||
|
||||
/* Allow ports to override the default behavior */
|
||||
__attribute__((weak))
|
||||
unsigned long do_go_exec(ulong (*entry)(int, char * const []), int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
void *entry_ptr = (void *) entry;
|
||||
ulong fdt_addr_r = 0;
|
||||
if (argc >= 2) {
|
||||
fdt_addr_r = hextoul(argv[1], NULL);
|
||||
}
|
||||
void (*func)(ulong) = entry_ptr;
|
||||
func(fdt_addr_r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
3. make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 rpi_4_defconfig
|
||||
4. make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 -j
|
||||
5. Copy u-boot.bin into your Pi SD-card's boot partition.
|
||||
**NOTE** I assume you have all the bootloader parts in the boot partition already.
|
||||
If not, clone raspberry fw repo and copy the following files to the boot partition:
|
||||
* bootcode.bin
|
||||
* start4.elf
|
||||
* all the .dtb files (a bcm2711-rpi-4-b.dtb should be enough though)
|
||||
|
||||
2. Copy u-boot.bin into the Pi's boot partition and edit the config.txt:
|
||||
6. config.txt:
|
||||
enable_uart=1
|
||||
arm64_bit=1
|
||||
kernel=u-boot.bin
|
||||
|
||||
3. Compile the OS with `cargo xtask --arch=aarch64 --board=raspi4b --release`
|
||||
4. Copy the following files into some directory:
|
||||
* target/aarch64-unknown-raspi4b/release/kernel.bin
|
||||
7. Compile the OS with `cargo xtask --arch=aarch64 --board=raspi4b --release`
|
||||
8. Copy the following files into some directory:
|
||||
* target/aarch64-unknown-raspi4b/release/yggdrasil-kernel
|
||||
* userspace/target/aarch64-unknown-yggdrasil/release/initrd.tar
|
||||
5. cd into that directory and start a TFTP server of your choice. I used `uftpd`.
|
||||
9. cd into that directory and start a TFTP server of your choice. I used `uftpd`.
|
||||
|
||||
6. Connect an ethernet and serial to the Pi and run the following commands in u-boot shell:
|
||||
10. Connect an ethernet and serial to the Pi and run the following commands in u-boot shell:
|
||||
|
||||
### If using DHCP
|
||||
$ dhcp
|
||||
### If not using DHCP
|
||||
$ env set ipaddr <RASPBERRY-IP-ADDR>
|
||||
$ env set fdt_addr_r 0x11000000
|
||||
$ env set initrd_addr_r 0x04000000
|
||||
$ tftpboot ${initrd_addr_r} <BUILD-MACHINE-IP-ADDR>:initrd.tar
|
||||
$ tftpboot ${loadaddr} <BUILD-MACHINE-IP-ADDR>: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}:<SIZE-PRINTED-WHEN-LOADING-INITRD> ${fdt_addr_r}
|
||||
tftpboot 0x04000000 <YOUR IP>:initrd.tar
|
||||
tftpboot ${loadaddr} <YOUR IP>:yggdrasil-kernel
|
||||
load mmc 0:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb
|
||||
fdt addr ${fdt_addr_r}
|
||||
fdt resize
|
||||
fdt memory 0x0 0x3C000000
|
||||
fdt chosen 0x04000000 <WHATEVER SIZE WAS PRINTED WHEN RUNNING THE FIRST COMMAND>
|
||||
bootelf -p
|
||||
go ${kernel_addr_r} ${fdt_addr_r}
|
||||
|
||||
###### Assuming BUILD-MACHINE-IP-ADDR is 13.0.0.1 and RASPBERRY-IP-ADDR is 13.0.0.2, here's
|
||||
###### a quick command for a development boot
|
||||
###### (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}
|
||||
11. Yggdrasil OS should start!
|
||||
|
||||
+14
-23
@@ -1,6 +1,7 @@
|
||||
Booting Yggdrasil OS on Starfive VisionFive 2 RISC-V board:
|
||||
|
||||
* TODO: proper format for initrd image
|
||||
* TODO: 0x70000000 can be replaced with a builtin var?
|
||||
|
||||
Prerequisites:
|
||||
|
||||
@@ -12,28 +13,18 @@ Steps:
|
||||
|
||||
1. Copy yggdrasil-kernel.bin and initrd.img into some directory and start a TFTP server there
|
||||
2. Connect to VF2's serial port, ethernet and enter u-boot
|
||||
3. Run the following commands in u-boot:
|
||||
|
||||
### If using DHCP
|
||||
$ dhcp
|
||||
### If not using DHCP
|
||||
$ env set ipaddr <VF2-IP-ADDR>
|
||||
$ env set initrd_addr_r 0x70000000
|
||||
### [Optional] set some kernel cmdline params
|
||||
$ env set bootargs "debug.serial-level=info"
|
||||
$ tftpboot ${initrd_addr_r} <BUILD-MACHINE-IP-ADDR>:initrd.img
|
||||
$ tftpboot ${loadaddr} <BUILD-MACHINE-IP-ADDR>:yggdrasil-kernel.bin
|
||||
$ load mmc 1:3 ${fdt_addr_r} dtbs/6.6.20-starfive/starfive/${fdtfile}
|
||||
$ fdt resize
|
||||
$ booti ${loadaddr} ${initrd_addr_r}:<initrd-size> ${fdt_addr_r}
|
||||
|
||||
###### Assuming BUILD-MACHINE-IP-ADDR is 13.0.0.1 and VF2-IP-ADDR is 13.0.0.2, here's
|
||||
###### a quick command for a development boot
|
||||
###### (FIXME when initrd gets larger than 64MiB)
|
||||
|
||||
env set ipaddr 13.0.0.2; env set initrd_addr_r 0x70000000; tftpboot ${initrd_addr_r} 13.0.0.1:initrd.img; tftpboot ${loadaddr} 13.0.0.1: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}
|
||||
|
||||
#### For DHCP boot with BUILD-MACHINE-IP-ADDR 192.168.88.10
|
||||
3. Run the following commands:
|
||||
|
||||
# Get an IP address
|
||||
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}
|
||||
# [Optional] set some kernel cmdline params
|
||||
setenv bootargs "debug.serial-level=info"
|
||||
# Load initrd
|
||||
tftpboot 0x70000000 <your-ip-address>:initrd.img
|
||||
# Load kernel
|
||||
tftpboot ${loadaddr} <your-ip-address>:yggdrasil-kernel.bin
|
||||
# Load dtb
|
||||
load mmc 1:3 ${fdt_addr_r} dtbs/...-starfive/starfive/${fdtfile}
|
||||
fdt resize
|
||||
# Enter the kernel
|
||||
booti ${loadaddr} 0x70000000:<initrd-size> ${fdt_addr_r}
|
||||
|
||||
Binary file not shown.
@@ -1,433 +0,0 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
interrupt-parent = <0x8005>;
|
||||
dma-coherent;
|
||||
model = "linux,dummy-virt";
|
||||
#size-cells = <0x02>;
|
||||
#address-cells = <0x02>;
|
||||
compatible = "linux,dummy-virt";
|
||||
|
||||
psci {
|
||||
migrate = <0xc4000005>;
|
||||
cpu_on = <0xc4000003>;
|
||||
cpu_off = <0x84000002>;
|
||||
cpu_suspend = <0xc4000001>;
|
||||
method = "smc";
|
||||
compatible = "arm,psci-1.0", "arm,psci-0.2", "arm,psci";
|
||||
};
|
||||
|
||||
memory@40000000 {
|
||||
reg = <0x00 0x40000000 0x00 0x20000000>;
|
||||
device_type = "memory";
|
||||
};
|
||||
|
||||
platform-bus@c000000 {
|
||||
interrupt-parent = <0x8005>;
|
||||
ranges = <0x00 0x00 0xc000000 0x2000000>;
|
||||
#address-cells = <0x01>;
|
||||
#size-cells = <0x01>;
|
||||
compatible = "qemu,platform", "simple-bus";
|
||||
};
|
||||
|
||||
fw-cfg@9020000 {
|
||||
dma-coherent;
|
||||
reg = <0x00 0x9020000 0x00 0x18>;
|
||||
compatible = "qemu,fw-cfg-mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a000000 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x10 0x01>;
|
||||
reg = <0x00 0xa000000 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a000200 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x11 0x01>;
|
||||
reg = <0x00 0xa000200 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a000400 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x12 0x01>;
|
||||
reg = <0x00 0xa000400 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a000600 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x13 0x01>;
|
||||
reg = <0x00 0xa000600 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a000800 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x14 0x01>;
|
||||
reg = <0x00 0xa000800 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a000a00 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x15 0x01>;
|
||||
reg = <0x00 0xa000a00 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a000c00 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x16 0x01>;
|
||||
reg = <0x00 0xa000c00 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a000e00 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x17 0x01>;
|
||||
reg = <0x00 0xa000e00 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a001000 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x18 0x01>;
|
||||
reg = <0x00 0xa001000 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a001200 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x19 0x01>;
|
||||
reg = <0x00 0xa001200 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a001400 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x1a 0x01>;
|
||||
reg = <0x00 0xa001400 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a001600 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x1b 0x01>;
|
||||
reg = <0x00 0xa001600 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a001800 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x1c 0x01>;
|
||||
reg = <0x00 0xa001800 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a001a00 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x1d 0x01>;
|
||||
reg = <0x00 0xa001a00 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a001c00 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x1e 0x01>;
|
||||
reg = <0x00 0xa001c00 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a001e00 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x1f 0x01>;
|
||||
reg = <0x00 0xa001e00 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a002000 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x20 0x01>;
|
||||
reg = <0x00 0xa002000 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a002200 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x21 0x01>;
|
||||
reg = <0x00 0xa002200 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a002400 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x22 0x01>;
|
||||
reg = <0x00 0xa002400 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a002600 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x23 0x01>;
|
||||
reg = <0x00 0xa002600 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a002800 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x24 0x01>;
|
||||
reg = <0x00 0xa002800 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a002a00 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x25 0x01>;
|
||||
reg = <0x00 0xa002a00 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a002c00 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x26 0x01>;
|
||||
reg = <0x00 0xa002c00 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a002e00 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x27 0x01>;
|
||||
reg = <0x00 0xa002e00 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a003000 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x28 0x01>;
|
||||
reg = <0x00 0xa003000 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a003200 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x29 0x01>;
|
||||
reg = <0x00 0xa003200 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a003400 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x2a 0x01>;
|
||||
reg = <0x00 0xa003400 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a003600 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x2b 0x01>;
|
||||
reg = <0x00 0xa003600 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a003800 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x2c 0x01>;
|
||||
reg = <0x00 0xa003800 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a003a00 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x2d 0x01>;
|
||||
reg = <0x00 0xa003a00 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a003c00 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x2e 0x01>;
|
||||
reg = <0x00 0xa003c00 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@a003e00 {
|
||||
dma-coherent;
|
||||
interrupts = <0x00 0x2f 0x01>;
|
||||
reg = <0x00 0xa003e00 0x00 0x200>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
gpio-keys {
|
||||
compatible = "gpio-keys";
|
||||
|
||||
poweroff {
|
||||
gpios = <0x8007 0x03 0x00>;
|
||||
linux,code = <0x74>;
|
||||
label = "GPIO Key Poweroff";
|
||||
};
|
||||
};
|
||||
|
||||
pl061@9030000 {
|
||||
phandle = <0x8007>;
|
||||
clock-names = "apb_pclk";
|
||||
clocks = <0x8000>;
|
||||
interrupts = <0x00 0x07 0x04>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <0x02>;
|
||||
compatible = "arm,pl061", "arm,primecell";
|
||||
reg = <0x00 0x9030000 0x00 0x1000>;
|
||||
};
|
||||
|
||||
pcie@10000000 {
|
||||
interrupt-map-mask = <0x1800 0x00 0x00 0x07>;
|
||||
interrupt-map = <0x00 0x00 0x00 0x01 0x8005 0x00 0x00 0x00 0x03 0x04 0x00 0x00 0x00 0x02 0x8005 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x03 0x8005 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x04 0x8005 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x01 0x8005 0x00 0x00 0x00 0x04 0x04 0x800 0x00 0x00 0x02 0x8005 0x00 0x00 0x00 0x05 0x04 0x800 0x00 0x00 0x03 0x8005 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x04 0x8005 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x01 0x8005 0x00 0x00 0x00 0x05 0x04 0x1000 0x00 0x00 0x02 0x8005 0x00 0x00 0x00 0x06 0x04 0x1000 0x00 0x00 0x03 0x8005 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x04 0x8005 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x01 0x8005 0x00 0x00 0x00 0x06 0x04 0x1800 0x00 0x00 0x02 0x8005 0x00 0x00 0x00 0x03 0x04 0x1800 0x00 0x00 0x03 0x8005 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x04 0x8005 0x00 0x00 0x00 0x05 0x04>;
|
||||
#interrupt-cells = <0x01>;
|
||||
ranges = <0x1000000 0x00 0x00 0x00 0x3eff0000 0x00 0x10000 0x2000000 0x00 0x10000000 0x00 0x10000000 0x00 0x2eff0000 0x3000000 0x80 0x00 0x80 0x00 0x80 0x00>;
|
||||
reg = <0x40 0x10000000 0x00 0x10000000>;
|
||||
msi-map = <0x00 0x8006 0x00 0x10000>;
|
||||
dma-coherent;
|
||||
bus-range = <0x00 0xff>;
|
||||
linux,pci-domain = <0x00>;
|
||||
#size-cells = <0x02>;
|
||||
#address-cells = <0x03>;
|
||||
device_type = "pci";
|
||||
compatible = "pci-host-ecam-generic";
|
||||
};
|
||||
|
||||
pl031@9010000 {
|
||||
clock-names = "apb_pclk";
|
||||
clocks = <0x8000>;
|
||||
interrupts = <0x00 0x02 0x04>;
|
||||
reg = <0x00 0x9010000 0x00 0x1000>;
|
||||
compatible = "arm,pl031", "arm,primecell";
|
||||
};
|
||||
|
||||
pl011@9000000 {
|
||||
clock-names = "uartclk", "apb_pclk";
|
||||
clocks = <0x8000 0x8000>;
|
||||
interrupts = <0x00 0x01 0x04>;
|
||||
reg = <0x00 0x9000000 0x00 0x1000>;
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
};
|
||||
|
||||
pmu {
|
||||
interrupts = <0x01 0x07 0xf04>;
|
||||
compatible = "arm,armv8-pmuv3";
|
||||
};
|
||||
|
||||
intc@8000000 {
|
||||
phandle = <0x8005>;
|
||||
interrupts = <0x01 0x09 0x04>;
|
||||
reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000 0x00 0x8030000 0x00 0x10000 0x00 0x8040000 0x00 0x10000>;
|
||||
compatible = "arm,cortex-a15-gic";
|
||||
ranges;
|
||||
#size-cells = <0x02>;
|
||||
#address-cells = <0x02>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <0x03>;
|
||||
|
||||
v2m@8020000 {
|
||||
phandle = <0x8006>;
|
||||
reg = <0x00 0x8020000 0x00 0x1000>;
|
||||
msi-controller;
|
||||
compatible = "arm,gic-v2m-frame";
|
||||
};
|
||||
};
|
||||
|
||||
flash@0 {
|
||||
bank-width = <0x04>;
|
||||
reg = <0x00 0x00 0x00 0x4000000 0x00 0x4000000 0x00 0x4000000>;
|
||||
compatible = "cfi-flash";
|
||||
};
|
||||
|
||||
cpus {
|
||||
#size-cells = <0x00>;
|
||||
#address-cells = <0x01>;
|
||||
|
||||
cpu-map {
|
||||
|
||||
socket0 {
|
||||
|
||||
cluster0 {
|
||||
|
||||
core0 {
|
||||
cpu = <0x8004>;
|
||||
};
|
||||
|
||||
core1 {
|
||||
cpu = <0x8003>;
|
||||
};
|
||||
|
||||
core2 {
|
||||
cpu = <0x8002>;
|
||||
};
|
||||
|
||||
core3 {
|
||||
cpu = <0x8001>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
cpu@0 {
|
||||
phandle = <0x8004>;
|
||||
reg = <0x00>;
|
||||
enable-method = "psci";
|
||||
compatible = "arm,cortex-a57";
|
||||
device_type = "cpu";
|
||||
};
|
||||
|
||||
cpu@1 {
|
||||
phandle = <0x8003>;
|
||||
reg = <0x01>;
|
||||
enable-method = "psci";
|
||||
compatible = "arm,cortex-a57";
|
||||
device_type = "cpu";
|
||||
};
|
||||
|
||||
cpu@2 {
|
||||
phandle = <0x8002>;
|
||||
reg = <0x02>;
|
||||
enable-method = "psci";
|
||||
compatible = "arm,cortex-a57";
|
||||
device_type = "cpu";
|
||||
};
|
||||
|
||||
cpu@3 {
|
||||
phandle = <0x8001>;
|
||||
reg = <0x03>;
|
||||
enable-method = "psci";
|
||||
compatible = "arm,cortex-a57";
|
||||
device_type = "cpu";
|
||||
};
|
||||
};
|
||||
|
||||
timer {
|
||||
interrupts = <0x01 0x0d 0xf04 0x01 0x0e 0xf04 0x01 0x0b 0xf04 0x01 0x0a 0xf04 0x01 0x0c 0xf04>;
|
||||
always-on;
|
||||
compatible = "arm,armv8-timer", "arm,armv7-timer";
|
||||
};
|
||||
|
||||
apb-pclk {
|
||||
phandle = <0x8000>;
|
||||
clock-output-names = "clk24mhz";
|
||||
clock-frequency = <0x16e3600>;
|
||||
#clock-cells = <0x00>;
|
||||
compatible = "fixed-clock";
|
||||
};
|
||||
|
||||
aliases {
|
||||
serial0 = "/pl011@9000000";
|
||||
};
|
||||
|
||||
chosen {
|
||||
linux,initrd-end = <0x00 0x49fd4600>;
|
||||
linux,initrd-start = <0x00 0x48000000>;
|
||||
stdout-path = "/pl011@9000000";
|
||||
rng-seed = <0xf119f64b 0xacade219 0xaefd1e87 0x5fb37f65 0xc770054a 0xd779b25f 0x1ba6d6e9 0x8121c19d>;
|
||||
kaslr-seed = <0x1f500308 0xbb36e27a>;
|
||||
};
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,217 +0,0 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <0x02>;
|
||||
#size-cells = <0x02>;
|
||||
compatible = "riscv-virtio";
|
||||
model = "riscv-virtio,qemu";
|
||||
|
||||
poweroff {
|
||||
value = <0x5555>;
|
||||
offset = <0x00>;
|
||||
regmap = <0x04>;
|
||||
compatible = "syscon-poweroff";
|
||||
};
|
||||
|
||||
reboot {
|
||||
value = <0x7777>;
|
||||
offset = <0x00>;
|
||||
regmap = <0x04>;
|
||||
compatible = "syscon-reboot";
|
||||
};
|
||||
|
||||
platform-bus@4000000 {
|
||||
interrupt-parent = <0x03>;
|
||||
ranges = <0x00 0x00 0x4000000 0x2000000>;
|
||||
#address-cells = <0x01>;
|
||||
#size-cells = <0x01>;
|
||||
compatible = "qemu,platform", "simple-bus";
|
||||
};
|
||||
|
||||
memory@80000000 {
|
||||
device_type = "memory";
|
||||
reg = <0x00 0x80000000 0x00 0x40000000>;
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <0x01>;
|
||||
#size-cells = <0x00>;
|
||||
timebase-frequency = <0x989680>;
|
||||
|
||||
cpu@0 {
|
||||
phandle = <0x01>;
|
||||
device_type = "cpu";
|
||||
reg = <0x00>;
|
||||
status = "okay";
|
||||
compatible = "riscv";
|
||||
riscv,cbop-block-size = <0x40>;
|
||||
riscv,cboz-block-size = <0x40>;
|
||||
riscv,cbom-block-size = <0x40>;
|
||||
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "h", "zic64b", "zicbom", "zicbop", "zicboz", "ziccamoa", "ziccif", "zicclsm", "ziccrse", "zicntr", "zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zmmul", "za64rs", "zaamo", "zalrsc", "zawrs", "zfa", "zca", "zcd", "zba", "zbb", "zbc", "zbs", "shcounterenw", "shgatpa", "shtvala", "shvsatpa", "shvstvala", "shvstvecd", "ssccptr", "sscounterenw", "sstc", "sstvala", "sstvecd", "ssu64xl", "svadu", "svvptc";
|
||||
riscv,isa-base = "rv64i";
|
||||
riscv,isa = "rv64imafdch_zic64b_zicbom_zicbop_zicboz_ziccamoa_ziccif_zicclsm_ziccrse_zicntr_zicsr_zifencei_zihintntl_zihintpause_zihpm_zmmul_za64rs_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbc_zbs_shcounterenw_shgatpa_shtvala_shvsatpa_shvstvala_shvstvecd_ssccptr_sscounterenw_sstc_sstvala_sstvecd_ssu64xl_svadu_svvptc";
|
||||
mmu-type = "riscv,sv57";
|
||||
|
||||
interrupt-controller {
|
||||
#interrupt-cells = <0x01>;
|
||||
interrupt-controller;
|
||||
compatible = "riscv,cpu-intc";
|
||||
phandle = <0x02>;
|
||||
};
|
||||
};
|
||||
|
||||
cpu-map {
|
||||
|
||||
cluster0 {
|
||||
|
||||
core0 {
|
||||
cpu = <0x01>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
pmu {
|
||||
riscv,event-to-mhpmcounters = <0x01 0x01 0x7fff9 0x02 0x02 0x7fffc 0x10019 0x10019 0x7fff8 0x1001b 0x1001b 0x7fff8 0x10021 0x10021 0x7fff8>;
|
||||
compatible = "riscv,pmu";
|
||||
};
|
||||
|
||||
fw-cfg@10100000 {
|
||||
dma-coherent;
|
||||
reg = <0x00 0x10100000 0x00 0x18>;
|
||||
compatible = "qemu,fw-cfg-mmio";
|
||||
};
|
||||
|
||||
flash@20000000 {
|
||||
bank-width = <0x04>;
|
||||
reg = <0x00 0x20000000 0x00 0x2000000 0x00 0x22000000 0x00 0x2000000>;
|
||||
compatible = "cfi-flash";
|
||||
};
|
||||
|
||||
aliases {
|
||||
serial0 = "/soc/serial@10000000";
|
||||
};
|
||||
|
||||
chosen {
|
||||
linux,initrd-end = <0x00 0xa2b4f200>;
|
||||
linux,initrd-start = <0x00 0xa0200000>;
|
||||
stdout-path = "/soc/serial@10000000";
|
||||
rng-seed = <0xa7074b10 0xf3373c0c 0x94a3a9a0 0xa2442477 0x817e30af 0x6460a6d7 0xbcaa71c4 0xb75dd35>;
|
||||
};
|
||||
|
||||
soc {
|
||||
#address-cells = <0x02>;
|
||||
#size-cells = <0x02>;
|
||||
compatible = "simple-bus";
|
||||
ranges;
|
||||
|
||||
rtc@101000 {
|
||||
interrupts = <0x0b>;
|
||||
interrupt-parent = <0x03>;
|
||||
reg = <0x00 0x101000 0x00 0x1000>;
|
||||
compatible = "google,goldfish-rtc";
|
||||
};
|
||||
|
||||
serial@10000000 {
|
||||
interrupts = <0x0a>;
|
||||
interrupt-parent = <0x03>;
|
||||
clock-frequency = "", "8@";
|
||||
reg = <0x00 0x10000000 0x00 0x100>;
|
||||
compatible = "ns16550a";
|
||||
};
|
||||
|
||||
test@100000 {
|
||||
phandle = <0x04>;
|
||||
reg = <0x00 0x100000 0x00 0x1000>;
|
||||
compatible = "sifive,test1", "sifive,test0", "syscon";
|
||||
};
|
||||
|
||||
virtio_mmio@10008000 {
|
||||
interrupts = <0x08>;
|
||||
interrupt-parent = <0x03>;
|
||||
reg = <0x00 0x10008000 0x00 0x1000>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@10007000 {
|
||||
interrupts = <0x07>;
|
||||
interrupt-parent = <0x03>;
|
||||
reg = <0x00 0x10007000 0x00 0x1000>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@10006000 {
|
||||
interrupts = <0x06>;
|
||||
interrupt-parent = <0x03>;
|
||||
reg = <0x00 0x10006000 0x00 0x1000>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@10005000 {
|
||||
interrupts = <0x05>;
|
||||
interrupt-parent = <0x03>;
|
||||
reg = <0x00 0x10005000 0x00 0x1000>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@10004000 {
|
||||
interrupts = <0x04>;
|
||||
interrupt-parent = <0x03>;
|
||||
reg = <0x00 0x10004000 0x00 0x1000>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@10003000 {
|
||||
interrupts = <0x03>;
|
||||
interrupt-parent = <0x03>;
|
||||
reg = <0x00 0x10003000 0x00 0x1000>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@10002000 {
|
||||
interrupts = <0x02>;
|
||||
interrupt-parent = <0x03>;
|
||||
reg = <0x00 0x10002000 0x00 0x1000>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
virtio_mmio@10001000 {
|
||||
interrupts = <0x01>;
|
||||
interrupt-parent = <0x03>;
|
||||
reg = <0x00 0x10001000 0x00 0x1000>;
|
||||
compatible = "virtio,mmio";
|
||||
};
|
||||
|
||||
plic@c000000 {
|
||||
phandle = <0x03>;
|
||||
riscv,ndev = <0x5f>;
|
||||
reg = <0x00 0xc000000 0x00 0x600000>;
|
||||
interrupts-extended = <0x02 0x0b 0x02 0x09>;
|
||||
interrupt-controller;
|
||||
compatible = "sifive,plic-1.0.0", "riscv,plic0";
|
||||
#address-cells = <0x00>;
|
||||
#interrupt-cells = <0x01>;
|
||||
};
|
||||
|
||||
clint@2000000 {
|
||||
interrupts-extended = <0x02 0x03 0x02 0x07>;
|
||||
reg = <0x00 0x2000000 0x00 0x10000>;
|
||||
compatible = "sifive,clint0", "riscv,clint0";
|
||||
};
|
||||
|
||||
pci@30000000 {
|
||||
interrupt-map-mask = <0x1800 0x00 0x00 0x07>;
|
||||
interrupt-map = <0x00 0x00 0x00 0x01 0x03 0x20 0x00 0x00 0x00 0x02 0x03 0x21 0x00 0x00 0x00 0x03 0x03 0x22 0x00 0x00 0x00 0x04 0x03 0x23 0x800 0x00 0x00 0x01 0x03 0x21 0x800 0x00 0x00 0x02 0x03 0x22 0x800 0x00 0x00 0x03 0x03 0x23 0x800 0x00 0x00 0x04 0x03 0x20 0x1000 0x00 0x00 0x01 0x03 0x22 0x1000 0x00 0x00 0x02 0x03 0x23 0x1000 0x00 0x00 0x03 0x03 0x20 0x1000 0x00 0x00 0x04 0x03 0x21 0x1800 0x00 0x00 0x01 0x03 0x23 0x1800 0x00 0x00 0x02 0x03 0x20 0x1800 0x00 0x00 0x03 0x03 0x21 0x1800 0x00 0x00 0x04 0x03 0x22>;
|
||||
ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x04 0x00 0x04 0x00 0x04 0x00>;
|
||||
reg = <0x00 0x30000000 0x00 0x10000000>;
|
||||
dma-coherent;
|
||||
bus-range = <0x00 0xff>;
|
||||
linux,pci-domain = <0x00>;
|
||||
device_type = "pci";
|
||||
compatible = "pci-host-ecam-generic";
|
||||
#size-cells = <0x02>;
|
||||
#interrupt-cells = <0x01>;
|
||||
#address-cells = <0x03>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
# Lower-half
|
||||
symbol-file -o 0x40080000 target/aarch64-unknown-none/release/yggdrasil-kernel
|
||||
symbol-file -o 0xFFFFFF8040080000 target/aarch64-unknown-none/release/yggdrasil-kernel
|
||||
target remote :1234
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"arch": "x86",
|
||||
"cpu": "pentium4",
|
||||
"os": "none",
|
||||
"abi": "softfloat",
|
||||
"llvm-target": "i686-unknown-linux-gnu",
|
||||
"data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
|
||||
"max-atomic-width": 64,
|
||||
"target-pointer-width": "32",
|
||||
"features": "-avx,-sse,+soft-float",
|
||||
|
||||
"executables": true,
|
||||
"stack-probes": {
|
||||
"kind": "inline"
|
||||
},
|
||||
"dynamic-linking": true,
|
||||
"panic-strategy": "abort",
|
||||
"relocation-model": "pic",
|
||||
|
||||
"has-thread-local": false,
|
||||
|
||||
"supported-split-debuginfo": [
|
||||
"packed",
|
||||
"unpacked",
|
||||
"off"
|
||||
],
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld"
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
ENTRY(__aarch64_entry);
|
||||
|
||||
SECTIONS {
|
||||
. = 0x0;
|
||||
|
||||
PROVIDE(__kernel_start = .);
|
||||
|
||||
.text : {
|
||||
*(.text.entry)
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
|
||||
.rodata : {
|
||||
*(.rodata*)
|
||||
*(.eh_frame*)
|
||||
|
||||
. = ALIGN(16);
|
||||
PROVIDE(__init_array_start = .);
|
||||
KEEP(*(.init_array*));
|
||||
PROVIDE(__init_array_end = .);
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
|
||||
.rela : {
|
||||
PROVIDE(__rela_start = .);
|
||||
*(.rela*)
|
||||
PROVIDE(__rela_end = .);
|
||||
}
|
||||
|
||||
.got : {
|
||||
*(.got*)
|
||||
}
|
||||
|
||||
.dynamic : {
|
||||
*(.dynamic)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
|
||||
.data : {
|
||||
*(.data*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start = .);
|
||||
|
||||
.bss : {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_end = .);
|
||||
PROVIDE(__kernel_end = .);
|
||||
PROVIDE(__kernel_size = __kernel_end - __kernel_start);
|
||||
|
||||
.dynsym : {
|
||||
*(.dynsym)
|
||||
}
|
||||
|
||||
.gnu.hash : {
|
||||
*(.gnu.hash)
|
||||
}
|
||||
|
||||
.hash : {
|
||||
*(.hash)
|
||||
}
|
||||
|
||||
.dynstr : {
|
||||
*(.dynstr)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
ENTRY(__aarch64_entry);
|
||||
|
||||
/* KERNEL_PHYS_BASE = 0x40080000; */
|
||||
KERNEL_VIRT_OFFSET = 0xFFFFFF8000000000;
|
||||
|
||||
SECTIONS {
|
||||
/* . = KERNEL_PHYS_BASE; */
|
||||
. = 0x0;
|
||||
|
||||
PROVIDE(__kernel_start = .);
|
||||
|
||||
/* .text.entry : {
|
||||
*(.text.entry)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
. = . + KERNEL_VIRT_OFFSET; */
|
||||
|
||||
.text : {
|
||||
KEEP(*(.text.entry));
|
||||
KEEP(*(.text.vectors));
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.rodata : {
|
||||
*(.rodata*)
|
||||
*(.eh_frame*)
|
||||
|
||||
. = ALIGN(16);
|
||||
PROVIDE(__init_array_start = .);
|
||||
KEEP(*(.init_array*))
|
||||
PROVIDE(__init_array_end = .);
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__rela_start = .);
|
||||
.rela : {
|
||||
*(.rela*)
|
||||
}
|
||||
PROVIDE(__rela_end = .);
|
||||
|
||||
/* . = ALIGN(4K); */
|
||||
/* .data.tables : { */
|
||||
/* KEEP(*(.data.tables)) */
|
||||
/* } */
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data : {
|
||||
*(.data*)
|
||||
*(.got*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start = .);
|
||||
/* PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET); */
|
||||
.bss : {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
}
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_end = .);
|
||||
/* PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET); */
|
||||
/* PROVIDE(__bss_size = __bss_end_phys - __bss_start_phys); */
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
};
|
||||
@@ -1,75 +1,52 @@
|
||||
ENTRY(__riscv64_entry);
|
||||
ENTRY(__rv64_entry);
|
||||
|
||||
KERNEL_VIRT_OFFSET = 0xFFFFFFF000000000;
|
||||
|
||||
SECTIONS {
|
||||
. = 0x0;
|
||||
|
||||
. = 0;
|
||||
PROVIDE(__kernel_start = .);
|
||||
|
||||
.text : {
|
||||
*(.text.entry)
|
||||
KEEP(*(.text.entry));
|
||||
KEEP(*(.text.vectors));
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
|
||||
.rodata : {
|
||||
*(.rodata*)
|
||||
*(.eh_frame*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__rela_start = .);
|
||||
.rela : {
|
||||
*(.rela*)
|
||||
}
|
||||
PROVIDE(__rela_end = .);
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data : {
|
||||
*(.data*)
|
||||
. = ALIGN(8);
|
||||
/* PROVIDE(__global_pointer = . + 0x800 - KERNEL_VIRT_OFFSET); */
|
||||
|
||||
. = ALIGN(16);
|
||||
PROVIDE(__init_array_start = .);
|
||||
KEEP(*(.init_array*));
|
||||
KEEP(*(.init_array*))
|
||||
PROVIDE(__init_array_end = .);
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
|
||||
.rela : {
|
||||
PROVIDE(__rela_start = .);
|
||||
*(.rela*)
|
||||
PROVIDE(__rela_end = .);
|
||||
}
|
||||
|
||||
.got : {
|
||||
*(.got*)
|
||||
}
|
||||
|
||||
.dynamic : {
|
||||
*(.dynamic)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
|
||||
.data : {
|
||||
*(.data*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start = .);
|
||||
|
||||
.bss : {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_end = .);
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
PROVIDE(__kernel_size = __kernel_end - __kernel_start);
|
||||
|
||||
.dynsym : {
|
||||
*(.dynsym)
|
||||
}
|
||||
|
||||
.gnu.hash : {
|
||||
*(.gnu.hash)
|
||||
}
|
||||
|
||||
.hash : {
|
||||
*(.hash)
|
||||
}
|
||||
|
||||
.dynstr : {
|
||||
*(.dynstr)
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
ENTRY(__i686_entry);
|
||||
|
||||
KERNEL_PHYS_BASE = 0x100000;
|
||||
KERNEL_VIRT_OFFSET = 0xC0000000;
|
||||
|
||||
SECTIONS {
|
||||
. = KERNEL_PHYS_BASE;
|
||||
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
|
||||
|
||||
.text.entry : {
|
||||
KEEP(*(.multiboot))
|
||||
*(.text.entry)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
. = . + KERNEL_VIRT_OFFSET;
|
||||
|
||||
.text : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
.export.text : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.export.text*))
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(.eh_frame*)
|
||||
*(.rodata*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.data.tables))
|
||||
}
|
||||
|
||||
.data : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.data.yboot))
|
||||
*(.data*)
|
||||
*(.got*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
|
||||
.bss : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
}
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
};
|
||||
@@ -1,8 +1,13 @@
|
||||
ENTRY(__x86_64_entry);
|
||||
|
||||
SECTIONS {
|
||||
. = 0x0;
|
||||
/* KERNEL_PHYS_BASE = 0x200000; */
|
||||
KERNEL_VIRT_OFFSET = 0xFFFFFF8000000000;
|
||||
|
||||
SECTIONS {
|
||||
/* . = KERNEL_PHYS_BASE; */
|
||||
/* PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET); */
|
||||
|
||||
. = 0x0;
|
||||
PROVIDE(__kernel_start = .);
|
||||
|
||||
.text : {
|
||||
@@ -10,32 +15,24 @@ SECTIONS {
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.export.text : {
|
||||
KEEP(*(.export.text*))
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.rodata : {
|
||||
*(.rodata*)
|
||||
*(.eh_frame*)
|
||||
|
||||
. = ALIGN(16);
|
||||
PROVIDE(__init_array_start = .);
|
||||
KEEP(*(.init_array*));
|
||||
PROVIDE(__init_array_end = .);
|
||||
*(.rodata*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
|
||||
PROVIDE(__rela_start = .);
|
||||
.rela : {
|
||||
PROVIDE(__rela_start = .);
|
||||
*(.rela*)
|
||||
PROVIDE(__rela_end = .);
|
||||
}
|
||||
|
||||
.dynamic : {
|
||||
*(.dynamic)
|
||||
}
|
||||
PROVIDE(__rela_end = .);
|
||||
|
||||
. = ALIGN(4K);
|
||||
|
||||
.data : {
|
||||
KEEP(*(.data.yboot))
|
||||
*(.data*)
|
||||
@@ -44,30 +41,12 @@ SECTIONS {
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start = .);
|
||||
|
||||
.bss : {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_end = .);
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
PROVIDE(__kernel_size = __kernel_end - __kernel_start);
|
||||
|
||||
.dynsym : {
|
||||
*(.dynsym)
|
||||
}
|
||||
|
||||
.gnu.hash : {
|
||||
*(.gnu.hash)
|
||||
}
|
||||
|
||||
.hash : {
|
||||
*(.hash)
|
||||
}
|
||||
|
||||
.dynstr : {
|
||||
*(.dynstr)
|
||||
}
|
||||
}
|
||||
};
|
||||
+14
-9
@@ -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_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_input = { path = "driver/input" }
|
||||
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" }
|
||||
@@ -68,10 +68,14 @@ 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
|
||||
|
||||
[target.'cfg(target_arch = "x86")'.dependencies]
|
||||
kernel-arch-i686.workspace = true
|
||||
kernel-arch-x86.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
abi-generator.workspace = true
|
||||
|
||||
@@ -82,6 +86,7 @@ prettyplease = "0.2.15"
|
||||
aarch64-cpu.workspace = true
|
||||
device-tree.workspace = true
|
||||
kernel-arch-x86_64.workspace = true
|
||||
kernel-arch-i686.workspace = true
|
||||
kernel-arch-x86.workspace = true
|
||||
kernel-arch-aarch64.workspace = true
|
||||
kernel-arch-riscv64.workspace = true
|
||||
|
||||
@@ -9,6 +9,9 @@ kernel-arch-x86_64.path = "x86_64"
|
||||
[target.'cfg(all(target_os = "none", target_arch = "aarch64"))'.dependencies]
|
||||
kernel-arch-aarch64.path = "aarch64"
|
||||
|
||||
[target.'cfg(all(target_os = "none", target_arch = "x86"))'.dependencies]
|
||||
kernel-arch-i686.path = "i686"
|
||||
|
||||
[target.'cfg(all(target_os = "none", target_arch = "riscv64"))'.dependencies]
|
||||
kernel-arch-riscv64.path = "riscv64"
|
||||
|
||||
|
||||
@@ -13,9 +13,15 @@ bitflags.workspace = true
|
||||
static_assertions.workspace = true
|
||||
aarch64-cpu.workspace = true
|
||||
tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
aarch64_board_virt = []
|
||||
aarch64_board_raspi4b = []
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -5,7 +5,7 @@ fn build_fp_context_obj() {
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
|
||||
println!("cargo:rerun-if-changed={FP_CONTEXT_S}");
|
||||
println!("cargo:rerun-if-changed={}", FP_CONTEXT_S);
|
||||
|
||||
cc::Build::new()
|
||||
.out_dir(&out_dir)
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
use core::ops::Range;
|
||||
|
||||
use kernel_arch_interface::mem::DeviceMemoryAttributes;
|
||||
use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
table::{DevicePageManager, DevicePageManagerLevel, EntryLevel},
|
||||
};
|
||||
|
||||
use crate::mem::table::PageEntry;
|
||||
|
||||
use super::{
|
||||
table::{PageTable, L2, L3},
|
||||
tlb_flush_range_va, DEVICE_MAPPING_OFFSET, DEVICE_MEMORY_L3_COUNT,
|
||||
};
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct L2DeviceMemory(pub PageTable<L2>);
|
||||
#[repr(transparent)]
|
||||
pub struct L3DeviceMemory(pub [PageTable<L3>; DEVICE_MEMORY_L3_COUNT]);
|
||||
|
||||
pub(super) static mut DEVICE_MEMORY: DevicePageManager<L3DeviceMemory, L2DeviceMemory> =
|
||||
DevicePageManager::new(
|
||||
L3DeviceMemory([PageTable::zeroed(); DEVICE_MEMORY_L3_COUNT]),
|
||||
L2DeviceMemory(PageTable::zeroed()),
|
||||
);
|
||||
|
||||
impl DevicePageManagerLevel for L2DeviceMemory {
|
||||
type Level = L2;
|
||||
const VIRTUAL_BASE: usize = DEVICE_MAPPING_OFFSET;
|
||||
const INDEX_RANGE: Range<usize> = DEVICE_MEMORY_L3_COUNT..512;
|
||||
|
||||
fn map_page(
|
||||
&mut self,
|
||||
index: usize,
|
||||
physical: PhysicalAddress,
|
||||
attrs: &DeviceMemoryAttributes,
|
||||
) {
|
||||
let _ = index;
|
||||
let _ = physical;
|
||||
let _ = attrs;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn unmap_page(&mut self, index: usize) {
|
||||
let _ = index;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn is_mapped(&self, index: usize) -> bool {
|
||||
let _ = index;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn flush_range(range: Range<usize>) {
|
||||
let start = range.start * L2::SIZE + Self::VIRTUAL_BASE;
|
||||
let size = (range.end - range.start) * L2::SIZE;
|
||||
tlb_flush_range_va(start, size);
|
||||
}
|
||||
}
|
||||
|
||||
impl DevicePageManagerLevel for L3DeviceMemory {
|
||||
type Level = L3;
|
||||
const VIRTUAL_BASE: usize = DEVICE_MAPPING_OFFSET;
|
||||
const INDEX_RANGE: Range<usize> = 0..512 * DEVICE_MEMORY_L3_COUNT;
|
||||
|
||||
fn map_page(
|
||||
&mut self,
|
||||
index: usize,
|
||||
physical: PhysicalAddress,
|
||||
attrs: &DeviceMemoryAttributes,
|
||||
) {
|
||||
let _ = attrs;
|
||||
let l2i = index / 512;
|
||||
let l3i = index % 512;
|
||||
self.0[l2i][l3i] = PageEntry::device_page(physical);
|
||||
}
|
||||
|
||||
// TODO
|
||||
fn unmap_page(&mut self, index: usize) {
|
||||
let _ = index;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn is_mapped(&self, index: usize) -> bool {
|
||||
let l2i = index / 512;
|
||||
let l3i = index % 512;
|
||||
self.0[l2i][l3i].is_present()
|
||||
}
|
||||
|
||||
fn flush_range(range: Range<usize>) {
|
||||
let start = range.start * L3::SIZE + Self::VIRTUAL_BASE;
|
||||
let size = (range.end - range.start) * L3::SIZE;
|
||||
tlb_flush_range_va(start, size);
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
use core::ops::Range;
|
||||
|
||||
use aarch64_cpu::registers::{TTBR0_EL1, TTBR1_EL1};
|
||||
use kernel_arch_interface::{mem::DeviceMemoryAttributes, KERNEL_VIRT_OFFSET};
|
||||
use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
device::{DevicePageManager, DevicePageTableLevel},
|
||||
table::EntryLevel,
|
||||
};
|
||||
|
||||
use crate::mem::{
|
||||
auto_lower_address,
|
||||
table::{PageAttributes, PageEntry, PageTable, L1, L2, L3},
|
||||
tlb_flush_range_va,
|
||||
};
|
||||
|
||||
pub const IDENTITY_SIZE_L1: usize = 8;
|
||||
pub const DEVICE_L1: usize = IDENTITY_SIZE_L1;
|
||||
pub const DEVICE_MAPPING_L3_COUNT: usize = 32;
|
||||
pub const DEVICE_MAPPING_OFFSET: usize = KERNEL_VIRT_OFFSET + (DEVICE_L1 << L1::SHIFT);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct L2DeviceMemory(pub PageTable<L2>);
|
||||
#[repr(transparent)]
|
||||
pub struct L3DeviceMemory(pub [PageTable<L3>; DEVICE_MAPPING_L3_COUNT]);
|
||||
|
||||
static mut KERNEL_L1: PageTable<L1> = PageTable::zeroed();
|
||||
pub(super) static mut DEVICE_MEMORY: DevicePageManager<L3DeviceMemory, L2DeviceMemory> =
|
||||
DevicePageManager::new(
|
||||
L3DeviceMemory([PageTable::zeroed(); DEVICE_MAPPING_L3_COUNT]),
|
||||
L2DeviceMemory(PageTable::zeroed()),
|
||||
);
|
||||
|
||||
impl DevicePageTableLevel for L2DeviceMemory {
|
||||
type Level = L2;
|
||||
const VIRTUAL_BASE: usize = DEVICE_MAPPING_OFFSET;
|
||||
const INDEX_RANGE: Range<usize> = DEVICE_MAPPING_L3_COUNT..512;
|
||||
|
||||
fn map_page(
|
||||
&mut self,
|
||||
index: usize,
|
||||
physical: PhysicalAddress,
|
||||
attrs: &DeviceMemoryAttributes,
|
||||
) {
|
||||
let _ = attrs;
|
||||
self.0[index] = PageEntry::device_block(physical);
|
||||
}
|
||||
|
||||
fn unmap_page(&mut self, index: usize) {
|
||||
self.0[index] = PageEntry::INVALID;
|
||||
}
|
||||
|
||||
fn is_mapped(&self, index: usize) -> bool {
|
||||
self.0[index].is_present()
|
||||
}
|
||||
|
||||
fn flush_range(range: Range<usize>) {
|
||||
let start = range.start * L2::SIZE + Self::VIRTUAL_BASE;
|
||||
let size = (range.end - range.start) * L2::SIZE;
|
||||
tlb_flush_range_va(start, size);
|
||||
}
|
||||
}
|
||||
|
||||
impl DevicePageTableLevel for L3DeviceMemory {
|
||||
type Level = L3;
|
||||
const VIRTUAL_BASE: usize = DEVICE_MAPPING_OFFSET;
|
||||
const INDEX_RANGE: Range<usize> = 0..512 * DEVICE_MAPPING_L3_COUNT;
|
||||
|
||||
fn map_page(
|
||||
&mut self,
|
||||
index: usize,
|
||||
physical: PhysicalAddress,
|
||||
attrs: &DeviceMemoryAttributes,
|
||||
) {
|
||||
let _ = attrs;
|
||||
let l2i = index / 512;
|
||||
let l3i = index % 512;
|
||||
self.0[l2i][l3i] = PageEntry::device_page(physical);
|
||||
}
|
||||
|
||||
fn unmap_page(&mut self, index: usize) {
|
||||
let l2i = index / 512;
|
||||
let l3i = index % 512;
|
||||
self.0[l2i][l3i] = PageEntry::INVALID;
|
||||
}
|
||||
|
||||
fn is_mapped(&self, index: usize) -> bool {
|
||||
let l2i = index / 512;
|
||||
let l3i = index % 512;
|
||||
self.0[l2i][l3i].is_present()
|
||||
}
|
||||
|
||||
fn flush_range(range: Range<usize>) {
|
||||
let start = range.start * L3::SIZE + Self::VIRTUAL_BASE;
|
||||
let size = (range.end - range.start) * L3::SIZE;
|
||||
tlb_flush_range_va(start, size);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn setup() {
|
||||
// 0..IDENTITY_SIZE_L1 -> lower RAM region
|
||||
for i in 0..IDENTITY_SIZE_L1 {
|
||||
let phys = PhysicalAddress::from_usize(i << L1::SHIFT);
|
||||
KERNEL_L1[i] = PageEntry::normal_block(phys, PageAttributes::empty());
|
||||
}
|
||||
|
||||
// DEVICE_L1 -> Device L2 table
|
||||
// 0..DEVICE_MAPPING_L3_COUNT -> Device L3 tables -> Device L3 pages
|
||||
// ..512 -> Device L2 pages
|
||||
for i in 0..DEVICE_MAPPING_L3_COUNT {
|
||||
let phys =
|
||||
PhysicalAddress::from_usize(auto_lower_address(&raw const DEVICE_MEMORY.normal.0[i]));
|
||||
DEVICE_MEMORY.large.0[i] = PageEntry::table(phys, PageAttributes::empty());
|
||||
}
|
||||
let phys = PhysicalAddress::from_usize(auto_lower_address(&raw const DEVICE_MEMORY.large.0));
|
||||
KERNEL_L1[DEVICE_L1] = PageEntry::table(phys, PageAttributes::empty());
|
||||
}
|
||||
|
||||
pub unsafe fn load() {
|
||||
let ttbr_physical = auto_lower_address(&raw const KERNEL_L1) as u64;
|
||||
|
||||
TTBR0_EL1.set_baddr(ttbr_physical);
|
||||
TTBR1_EL1.set_baddr(ttbr_physical);
|
||||
}
|
||||
@@ -1,57 +1,10 @@
|
||||
use core::sync::atomic::{self, Ordering};
|
||||
|
||||
use aarch64_cpu::{
|
||||
asm::barrier,
|
||||
registers::{PAR_EL1, SCTLR_EL1},
|
||||
};
|
||||
use aarch64_cpu::{asm::barrier, registers::PAR_EL1};
|
||||
use libk_mm_interface::table::{EntryLevel, EntryLevelExt};
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable};
|
||||
use tock_registers::interfaces::Readable;
|
||||
|
||||
use crate::mem::table::L3;
|
||||
|
||||
/// Enables data cache.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Manipulates low-level machine state, use with care.
|
||||
pub unsafe fn enable_dcache() {
|
||||
barrier::dsb(barrier::ISHST);
|
||||
barrier::isb(barrier::SY);
|
||||
|
||||
SCTLR_EL1.modify(SCTLR_EL1::C::Cacheable);
|
||||
|
||||
barrier::dsb(barrier::ISH);
|
||||
barrier::isb(barrier::SY);
|
||||
}
|
||||
|
||||
/// Enables instruction cache.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Manipulates low-level machine state, use with care.
|
||||
pub unsafe fn enable_icache() {
|
||||
barrier::isb(barrier::SY);
|
||||
|
||||
SCTLR_EL1.modify(SCTLR_EL1::I::Cacheable);
|
||||
|
||||
barrier::dsb(barrier::ISH);
|
||||
barrier::isb(barrier::SY);
|
||||
}
|
||||
|
||||
/// Disables instruction cache.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Manipulates low-level machine state, use with care. Might break some instructions.
|
||||
pub unsafe fn disable_icache() {
|
||||
barrier::isb(barrier::SY);
|
||||
|
||||
ic_iallu();
|
||||
SCTLR_EL1.modify(SCTLR_EL1::I::NonCacheable);
|
||||
|
||||
barrier::dsb(barrier::ISH);
|
||||
barrier::isb(barrier::SY);
|
||||
}
|
||||
use super::table::L3;
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_asid(asid: u8) {
|
||||
@@ -85,7 +38,6 @@ pub fn tlb_flush_vaae1(page: usize) {
|
||||
barrier::isb(barrier::SY);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_range_va(base: usize, size: usize) {
|
||||
let end = (base + size).page_align_up::<L3>();
|
||||
let base = base.page_align_down::<L3>();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
use aarch64_cpu::{
|
||||
asm::barrier,
|
||||
registers::{MAIR_EL1, SCTLR_EL1, TCR_EL1},
|
||||
registers::{MAIR_EL1, SCTLR_EL1, TCR_EL1, TTBR0_EL1, TTBR1_EL1},
|
||||
};
|
||||
use kernel_arch_interface::{
|
||||
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
|
||||
@@ -9,41 +8,36 @@ use kernel_arch_interface::{
|
||||
KERNEL_VIRT_OFFSET,
|
||||
};
|
||||
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel};
|
||||
use table::{PageAttributes, PageEntry, PageTable, L1};
|
||||
use tock_registers::interfaces::{ReadWriteable, Writeable};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
pub use intrinsics::*;
|
||||
mod intrinsics;
|
||||
|
||||
use crate::{mem::table::L1, ArchitectureImpl};
|
||||
|
||||
pub mod fixed;
|
||||
pub mod intrinsics;
|
||||
pub mod device;
|
||||
pub mod process;
|
||||
pub mod table;
|
||||
|
||||
pub use intrinsics::*;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::ArchitectureImpl;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KernelTableManagerImpl;
|
||||
|
||||
static KERNEL_MEMORY_LOCK: IrqSafeSpinlock<ArchitectureImpl, ()> = IrqSafeSpinlock::new(());
|
||||
|
||||
impl KernelTableManager for KernelTableManagerImpl {
|
||||
fn virtualize(address: u64) -> usize {
|
||||
let address = address as usize;
|
||||
if address < fixed::IDENTITY_SIZE_L1 * L1::SIZE {
|
||||
address + KERNEL_VIRT_OFFSET
|
||||
} else {
|
||||
panic!("Invalid physical address: {address:#x}");
|
||||
fn virtualize(phys: u64) -> usize {
|
||||
if phys as usize >= IDENTITY_SIZE {
|
||||
unreachable!("Invalid physical address to virtualize: {phys:#x}");
|
||||
}
|
||||
phys as usize + KERNEL_VIRT_OFFSET
|
||||
}
|
||||
|
||||
fn physicalize(address: usize) -> u64 {
|
||||
if address < KERNEL_VIRT_OFFSET
|
||||
|| address - KERNEL_VIRT_OFFSET >= fixed::IDENTITY_SIZE_L1 * L1::SIZE
|
||||
{
|
||||
panic!("Invalid virtual (-> physical) address {address:#x}");
|
||||
fn physicalize(virt: usize) -> u64 {
|
||||
if virt < KERNEL_VIRT_OFFSET || virt - KERNEL_VIRT_OFFSET >= IDENTITY_SIZE {
|
||||
unreachable!("Invalid virtualized address: {virt:#x}");
|
||||
}
|
||||
|
||||
(address - KERNEL_VIRT_OFFSET) as u64
|
||||
(virt - KERNEL_VIRT_OFFSET) as _
|
||||
}
|
||||
|
||||
unsafe fn map_device_pages(
|
||||
@@ -51,66 +45,93 @@ impl KernelTableManager for KernelTableManagerImpl {
|
||||
count: usize,
|
||||
attrs: DeviceMemoryAttributes,
|
||||
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
|
||||
let _lock = KERNEL_MEMORY_LOCK.lock();
|
||||
let _guard = DEVICE_MEMORY_LOCK.lock();
|
||||
#[allow(static_mut_refs)]
|
||||
fixed::DEVICE_MEMORY.map_device_pages(PhysicalAddress::from_u64(base), count, attrs)
|
||||
unsafe {
|
||||
device::DEVICE_MEMORY.map_device_pages(PhysicalAddress::from_u64(base), count, attrs)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
|
||||
let _lock = KERNEL_MEMORY_LOCK.lock();
|
||||
#[allow(static_mut_refs)]
|
||||
fixed::DEVICE_MEMORY.unmap_device_pages(mapping);
|
||||
unsafe fn unmap_device_pages(_mapping: &RawDeviceMemoryMapping<Self>) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn auto_lower_address<T>(ptr: *const T) -> usize {
|
||||
let address = ptr.addr();
|
||||
if address < KERNEL_VIRT_OFFSET {
|
||||
address
|
||||
} else {
|
||||
address - KERNEL_VIRT_OFFSET
|
||||
// 64GiB of identity-mapped memory
|
||||
pub const IDENTITY_SIZE: usize = 64 * L1::SIZE;
|
||||
pub const IDENTITY_L1_START: usize = (KERNEL_VIRT_OFFSET >> L1::SHIFT) & 0x1FF;
|
||||
// 1GiB of memory-mapped devices
|
||||
pub const DEVICE_MEMORY_L1: usize = IDENTITY_L1_START + IDENTITY_SIZE / L1::SIZE;
|
||||
pub const DEVICE_MEMORY_L3_COUNT: usize = 32;
|
||||
pub const DEVICE_MAPPING_OFFSET: usize = KERNEL_VIRT_OFFSET + DEVICE_MEMORY_L1 * L1::SIZE;
|
||||
static DEVICE_MEMORY_LOCK: IrqSafeSpinlock<ArchitectureImpl, ()> = IrqSafeSpinlock::new(());
|
||||
|
||||
static mut FIXED_L1: PageTable<L1> = const {
|
||||
if IDENTITY_L1_START != 0 {
|
||||
panic!("Invalid KERNEL_VIRT_OFFSET");
|
||||
}
|
||||
if IDENTITY_L1_START + IDENTITY_SIZE / L1::SIZE >= 500 {
|
||||
panic!("Invalid KERNEL_VIRT_OFFSET + IDENTITY_SIZE");
|
||||
}
|
||||
|
||||
let mut table = PageTable::<L1>::zeroed();
|
||||
let mut i = 0;
|
||||
|
||||
while i < IDENTITY_SIZE / L1::SIZE {
|
||||
let phys = PhysicalAddress::from_usize(i * L1::SIZE);
|
||||
table.entries[i + IDENTITY_L1_START] =
|
||||
PageEntry::normal_block(phys, PageAttributes::empty());
|
||||
i += 1;
|
||||
}
|
||||
|
||||
table
|
||||
};
|
||||
|
||||
pub unsafe fn setup_fixed_tables() {
|
||||
let device_l2_physical = auto_lower_address(&raw const device::DEVICE_MEMORY.large);
|
||||
FIXED_L1[DEVICE_MEMORY_L1] = PageEntry::table(
|
||||
PhysicalAddress::from_usize(device_l2_physical),
|
||||
PageAttributes::empty(),
|
||||
);
|
||||
for i in 0..DEVICE_MEMORY_L3_COUNT {
|
||||
let device_l3_physical = auto_lower_address(&raw const device::DEVICE_MEMORY.normal.0[i]);
|
||||
device::DEVICE_MEMORY.large.0[i] = PageEntry::table(
|
||||
PhysicalAddress::from_usize(device_l3_physical),
|
||||
PageAttributes::empty(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn auto_upper_address<T>(ptr: *const T) -> usize {
|
||||
let address = ptr.addr();
|
||||
if address < KERNEL_VIRT_OFFSET {
|
||||
address + KERNEL_VIRT_OFFSET
|
||||
} else {
|
||||
address
|
||||
}
|
||||
pub fn load_fixed_tables() {
|
||||
let l1_physical = auto_lower_address(&raw const FIXED_L1);
|
||||
TTBR0_EL1.set_baddr(l1_physical as _);
|
||||
TTBR1_EL1.set_baddr(l1_physical as _);
|
||||
}
|
||||
|
||||
fn setup_memory_attributes() {
|
||||
pub fn configure_mmu() {
|
||||
// TODO: Figure out why WriteBack_NonTransient_ReadWriteAlloc doesn't work on Pi 4B
|
||||
|
||||
MAIR_EL1.write(
|
||||
//// Attribute 0 -- normal memory
|
||||
MAIR_EL1::Attr0_Normal_Inner::WriteBack_NonTransient +
|
||||
MAIR_EL1::Attr0_Normal_Outer::WriteBack_NonTransient +
|
||||
//// Attribute 1 -- normal non-cacheable memory
|
||||
MAIR_EL1::Attr0_Normal_Inner::NonCacheable +
|
||||
MAIR_EL1::Attr0_Normal_Outer::NonCacheable +
|
||||
//// Attribute 2 -- device memory
|
||||
MAIR_EL1::Attr1_Device::nonGathering_nonReordering_EarlyWriteAck,
|
||||
MAIR_EL1::Attr0_Normal_Outer::WriteBack_NonTransient +
|
||||
//// Attribute 1 -- normal non-cacheable memory
|
||||
MAIR_EL1::Attr0_Normal_Inner::NonCacheable +
|
||||
MAIR_EL1::Attr0_Normal_Outer::NonCacheable +
|
||||
//// Attribute 2 -- device memory
|
||||
MAIR_EL1::Attr1_Device::nonGathering_nonReordering_EarlyWriteAck,
|
||||
);
|
||||
}
|
||||
|
||||
unsafe fn enable_mmu() {
|
||||
TCR_EL1.write(
|
||||
TCR_EL1::AS::ASID8Bits +
|
||||
TCR_EL1::A1::TTBR0 +
|
||||
TCR_EL1::HD::CLEAR +
|
||||
// General
|
||||
TCR_EL1::IPS::Bits_48 +
|
||||
// TTBR0
|
||||
TCR_EL1::TG0::KiB_4 + TCR_EL1::T0SZ.val(25) + TCR_EL1::SH0::Inner +
|
||||
// TTBR1
|
||||
TCR_EL1::TG1::KiB_4 + TCR_EL1::T1SZ.val(25) + TCR_EL1::SH1::Inner,
|
||||
TCR_EL1::A1::TTBR0 +
|
||||
TCR_EL1::HD::CLEAR +
|
||||
// General
|
||||
TCR_EL1::IPS::Bits_48 +
|
||||
// TTBR0
|
||||
TCR_EL1::TG0::KiB_4 + TCR_EL1::T0SZ.val(25) + TCR_EL1::SH0::Inner +
|
||||
// TTBR1
|
||||
TCR_EL1::TG1::KiB_4 + TCR_EL1::T1SZ.val(25) + TCR_EL1::SH1::Inner,
|
||||
);
|
||||
|
||||
barrier::dsb(barrier::ISHST);
|
||||
barrier::isb(barrier::SY);
|
||||
|
||||
@@ -127,20 +148,62 @@ unsafe fn enable_mmu() {
|
||||
|
||||
barrier::dsb(barrier::ISH);
|
||||
barrier::isb(barrier::SY);
|
||||
}
|
||||
|
||||
pub fn enable_mmu() {
|
||||
// Enable translation
|
||||
SCTLR_EL1.modify(SCTLR_EL1::M::Enable);
|
||||
|
||||
// Enable caches
|
||||
enable_icache();
|
||||
enable_dcache();
|
||||
}
|
||||
|
||||
pub unsafe fn init_lower(bsp: bool) {
|
||||
setup_memory_attributes();
|
||||
if bsp {
|
||||
fixed::setup();
|
||||
/// Enables data cache.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Manipulates low-level machine state, use with care.
|
||||
pub unsafe fn enable_dcache() {
|
||||
barrier::dsb(barrier::ISHST);
|
||||
barrier::isb(barrier::SY);
|
||||
|
||||
SCTLR_EL1.modify(SCTLR_EL1::C::Cacheable);
|
||||
|
||||
barrier::dsb(barrier::ISH);
|
||||
barrier::isb(barrier::SY);
|
||||
}
|
||||
|
||||
/// Enables instruction cache.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Manipulates low-level machine state, use with care.
|
||||
pub unsafe fn enable_icache() {
|
||||
barrier::isb(barrier::SY);
|
||||
|
||||
SCTLR_EL1.modify(SCTLR_EL1::I::Cacheable);
|
||||
|
||||
barrier::dsb(barrier::ISH);
|
||||
barrier::isb(barrier::SY);
|
||||
}
|
||||
|
||||
/// Disables instruction cache.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Manipulates low-level machine state, use with care. Might break some instructions.
|
||||
pub unsafe fn disable_icache() {
|
||||
barrier::isb(barrier::SY);
|
||||
|
||||
ic_iallu();
|
||||
SCTLR_EL1.modify(SCTLR_EL1::I::NonCacheable);
|
||||
|
||||
barrier::dsb(barrier::ISH);
|
||||
barrier::isb(barrier::SY);
|
||||
}
|
||||
|
||||
fn auto_lower_address<T>(ptr: *const T) -> usize {
|
||||
let address = ptr.addr();
|
||||
if address < KERNEL_VIRT_OFFSET {
|
||||
address
|
||||
} else {
|
||||
address - KERNEL_VIRT_OFFSET
|
||||
}
|
||||
fixed::load();
|
||||
enable_mmu();
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ bitflags! {
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C, align(0x1000))]
|
||||
pub struct PageTable<L: EntryLevel> {
|
||||
entries: [PageEntry<L>; 512],
|
||||
pub entries: [PageEntry<L>; 512],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -262,18 +262,13 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn normal_block(phys: PhysicalAddress, attrs: PageAttributes) -> Self {
|
||||
Self(
|
||||
phys.into_u64()
|
||||
| (PageAttributes::BLOCK
|
||||
| PageAttributes::PRESENT
|
||||
| PageAttributes::ACCESS
|
||||
| PageAttributes::SH_OUTER
|
||||
| PageAttributes::PAGE_ATTR_NORMAL
|
||||
| attrs)
|
||||
.bits(),
|
||||
PhantomData,
|
||||
)
|
||||
pub const fn normal_block(phys: PhysicalAddress, attrs: PageAttributes) -> Self {
|
||||
const ATTR: u64 = PageAttributes::BLOCK.bits()
|
||||
| PageAttributes::PRESENT.bits()
|
||||
| PageAttributes::ACCESS.bits()
|
||||
| PageAttributes::SH_OUTER.bits()
|
||||
| PageAttributes::PAGE_ATTR_NORMAL.bits();
|
||||
Self(phys.into_u64() | attrs.bits() | ATTR, PhantomData)
|
||||
}
|
||||
|
||||
pub fn device_block(phys: PhysicalAddress) -> Self {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "kernel-arch-i686"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
yggdrasil-abi.workspace = true
|
||||
kernel-arch-interface.workspace = true
|
||||
libk-mm-interface.workspace = true
|
||||
device-api = { workspace = true, features = ["derive"] }
|
||||
kernel-arch-x86.workspace = true
|
||||
|
||||
bitflags.workspace = true
|
||||
static_assertions.workspace = true
|
||||
tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
@@ -0,0 +1,116 @@
|
||||
// vi: set ft=asm :
|
||||
|
||||
.macro SAVE_TASK_STATE
|
||||
push %edi
|
||||
push %esi
|
||||
push %ebp
|
||||
push %ebx
|
||||
.endm
|
||||
|
||||
.macro LOAD_TASK_STATE
|
||||
pop %ebx
|
||||
pop %ebp
|
||||
pop %esi
|
||||
pop %edi
|
||||
.endm
|
||||
|
||||
.section .text
|
||||
|
||||
.global __i686_task_enter_kernel
|
||||
.global __i686_task_enter_user
|
||||
.global __i686_task_enter_from_fork
|
||||
.global __i686_switch_task
|
||||
.global __i686_enter_task
|
||||
.global __i686_switch_and_drop
|
||||
|
||||
__i686_task_enter_kernel:
|
||||
// %esp + 4: argument
|
||||
// %esp + 0: entry
|
||||
xor %ecx, %ecx
|
||||
xchg (%esp), %ecx
|
||||
|
||||
// Enable IRQ in EFLAGS
|
||||
pushfl
|
||||
pop %edx
|
||||
or $(1 << 9), %edx
|
||||
|
||||
// Setup iret
|
||||
push %edx // eflags
|
||||
pushl $0x08 // cs
|
||||
push %ecx // eip
|
||||
|
||||
iret
|
||||
|
||||
__i686_task_enter_user:
|
||||
pop %edx // User %esp
|
||||
pop %ecx // entry
|
||||
pop %eax // flags
|
||||
|
||||
// Setup iret
|
||||
|
||||
// %ss:%esp
|
||||
pushl $0x23
|
||||
push %edx
|
||||
|
||||
// %eflags
|
||||
push %eax
|
||||
|
||||
// %cs:%eip
|
||||
pushl $0x1B
|
||||
push %ecx
|
||||
|
||||
mov $0x23, %bx
|
||||
mov %bx, %ds
|
||||
mov %bx, %es
|
||||
mov %bx, %fs
|
||||
|
||||
iret
|
||||
|
||||
__i686_task_enter_from_fork:
|
||||
jmp .
|
||||
|
||||
__i686_switch_task:
|
||||
// %esp + 0: return
|
||||
// %esp + 4: destination
|
||||
// %esp + 8: source
|
||||
mov 4(%esp), %eax
|
||||
mov 8(%esp), %ecx
|
||||
|
||||
SAVE_TASK_STATE
|
||||
|
||||
// Store stack to "from" context
|
||||
mov %esp, (%ecx)
|
||||
|
||||
// Load stack from "to" context
|
||||
mov (%eax), %esp
|
||||
|
||||
LOAD_TASK_STATE
|
||||
|
||||
ret
|
||||
|
||||
__i686_enter_task:
|
||||
// %esp + 0: return
|
||||
// %esp + 4: destination
|
||||
|
||||
// Switch to destination stack
|
||||
mov 4(%esp), %eax
|
||||
mov (%eax), %esp
|
||||
|
||||
LOAD_TASK_STATE
|
||||
|
||||
ret
|
||||
|
||||
__i686_switch_and_drop:
|
||||
// %esp + 0: return
|
||||
// %esp + 4: destination
|
||||
// %esp + 8: thread to drop
|
||||
mov 8(%esp), %ecx
|
||||
mov 4(%esp), %eax
|
||||
// Switch to stack
|
||||
mov (%eax), %esp
|
||||
|
||||
LOAD_TASK_STATE
|
||||
|
||||
// TODO actually drop the thread
|
||||
|
||||
ret
|
||||
@@ -0,0 +1,462 @@
|
||||
use core::{arch::global_asm, cell::UnsafeCell, marker::PhantomData};
|
||||
|
||||
use kernel_arch_interface::{
|
||||
mem::{KernelTableManager, PhysicalMemoryAllocator},
|
||||
task::{StackBuilder, TaskContext, TaskFrame, UserContextInfo},
|
||||
};
|
||||
use kernel_arch_x86::registers::{FpuContext, CR3};
|
||||
use libk_mm_interface::address::{AsPhysicalAddress, PhysicalAddress};
|
||||
use tock_registers::interfaces::Writeable;
|
||||
use yggdrasil_abi::{arch::SavedFrame, error::Error};
|
||||
|
||||
use crate::{
|
||||
gdt::{self, TSS},
|
||||
mem::KERNEL_TABLES,
|
||||
};
|
||||
|
||||
#[allow(unused)]
|
||||
#[repr(C)]
|
||||
pub struct ExceptionFrame {
|
||||
pub eax: u32,
|
||||
pub ecx: u32,
|
||||
pub edx: u32,
|
||||
pub ebx: u32,
|
||||
pub ebp: u32,
|
||||
pub esi: u32,
|
||||
pub edi: u32,
|
||||
|
||||
pub exc_number: u32,
|
||||
pub exc_code: u32,
|
||||
|
||||
pub eip: u32,
|
||||
pub cs: u32,
|
||||
pub eflags: u32,
|
||||
pub esp: u32,
|
||||
pub ss: u32,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct SyscallFrame {
|
||||
pub eax: usize,
|
||||
// ebx, ecx, edx, esi, edi, ebp
|
||||
pub args: [usize; 6],
|
||||
|
||||
pub eip: u32,
|
||||
pub cs: u32,
|
||||
pub eflags: u32,
|
||||
pub esp: u32,
|
||||
pub ss: u32,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[repr(C)]
|
||||
pub struct InterruptFrame {
|
||||
pub eax: u32,
|
||||
pub ecx: u32,
|
||||
pub edx: u32,
|
||||
pub ebx: u32,
|
||||
pub ebp: u32,
|
||||
pub esi: u32,
|
||||
pub edi: u32,
|
||||
|
||||
pub irq_number: u32,
|
||||
|
||||
pub eip: u32,
|
||||
pub cs: u32,
|
||||
pub eflags: u32,
|
||||
esp: u32,
|
||||
ss: u32,
|
||||
}
|
||||
|
||||
#[repr(C, align(0x10))]
|
||||
struct Inner {
|
||||
// 0x00
|
||||
sp: usize,
|
||||
|
||||
gs_base: usize,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct TaskContextImpl<
|
||||
K: KernelTableManager,
|
||||
PA: PhysicalMemoryAllocator<Address = PhysicalAddress>,
|
||||
> {
|
||||
inner: UnsafeCell<Inner>,
|
||||
fpu_context: Option<UnsafeCell<FpuContext>>,
|
||||
stack_base_phys: PhysicalAddress,
|
||||
stack_size: usize,
|
||||
|
||||
cr3: u32,
|
||||
tss_esp0: u32,
|
||||
|
||||
_pd: PhantomData<(K, PA)>,
|
||||
}
|
||||
|
||||
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
|
||||
TaskContextImpl<K, PA>
|
||||
{
|
||||
unsafe fn store_state(&self) {
|
||||
if let Some(fpu) = self.fpu_context.as_ref() {
|
||||
FpuContext::store(fpu.get());
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn load_state(&self) {
|
||||
if let Some(fpu) = self.fpu_context.as_ref() {
|
||||
FpuContext::restore(fpu.get());
|
||||
}
|
||||
gdt::set_gs_base((*self.inner.get()).gs_base);
|
||||
TSS.esp0 = self.tss_esp0;
|
||||
CR3.set(self.cr3 as _);
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
|
||||
TaskContext<K, PA> for TaskContextImpl<K, PA>
|
||||
{
|
||||
const SIGNAL_STACK_EXTRA_ALIGN: usize = 0;
|
||||
const USER_STACK_EXTRA_ALIGN: usize = 0;
|
||||
|
||||
fn user(context: UserContextInfo) -> Result<Self, Error> {
|
||||
const USER_TASK_PAGES: usize = 16;
|
||||
|
||||
let stack_base_phys = PA::allocate_contiguous_pages(USER_TASK_PAGES)?;
|
||||
let stack_base = stack_base_phys.raw_virtualize::<K>();
|
||||
|
||||
let mut stack = StackBuilder::new(stack_base, USER_TASK_PAGES * 0x1000);
|
||||
let mut flags = 0x200;
|
||||
|
||||
if context.single_step {
|
||||
flags |= 1 << 8;
|
||||
}
|
||||
|
||||
stack.push(flags);
|
||||
stack.push(context.entry as _);
|
||||
stack.push(context.stack_pointer);
|
||||
|
||||
setup_common_context(&mut stack, __i686_task_enter_user as _);
|
||||
|
||||
let sp = stack.build();
|
||||
let esp0 = stack_base + USER_TASK_PAGES * 0x1000;
|
||||
let fpu_context = FpuContext::new(true);
|
||||
|
||||
Ok(Self {
|
||||
inner: UnsafeCell::new(Inner {
|
||||
sp,
|
||||
gs_base: context.thread_pointer,
|
||||
}),
|
||||
fpu_context: Some(UnsafeCell::new(fpu_context)),
|
||||
stack_base_phys,
|
||||
stack_size: USER_TASK_PAGES * 0x1000,
|
||||
|
||||
tss_esp0: esp0 as _,
|
||||
cr3: context.address_space.try_into().unwrap(),
|
||||
|
||||
_pd: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn kernel(
|
||||
entry: extern "C" fn(usize) -> !,
|
||||
arg: usize,
|
||||
) -> Result<Self, yggdrasil_abi::error::Error> {
|
||||
const KERNEL_TASK_PAGES: usize = 32;
|
||||
|
||||
let stack_base_phys = PA::allocate_contiguous_pages(KERNEL_TASK_PAGES)?;
|
||||
let stack_base = stack_base_phys.raw_virtualize::<K>();
|
||||
|
||||
let mut stack = StackBuilder::new(stack_base, KERNEL_TASK_PAGES * 0x1000);
|
||||
|
||||
// Entry and argument
|
||||
stack.push(arg);
|
||||
stack.push(entry as _);
|
||||
|
||||
// XXX
|
||||
setup_common_context(&mut stack, __i686_task_enter_kernel as _);
|
||||
|
||||
let sp = stack.build();
|
||||
let cr3 = unsafe {
|
||||
KERNEL_TABLES
|
||||
.lock()
|
||||
.as_physical_address()
|
||||
.try_into_u32()
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// TODO stack is leaked
|
||||
|
||||
Ok(Self {
|
||||
inner: UnsafeCell::new(Inner { sp, gs_base: 0 }),
|
||||
fpu_context: None,
|
||||
stack_base_phys,
|
||||
stack_size: KERNEL_TASK_PAGES * 0x1000,
|
||||
|
||||
tss_esp0: 0,
|
||||
cr3,
|
||||
|
||||
_pd: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn switch(&self, from: &Self) {
|
||||
if core::ptr::addr_eq(self, from) {
|
||||
return;
|
||||
}
|
||||
|
||||
from.store_state();
|
||||
self.load_state();
|
||||
|
||||
__i686_switch_task(self.inner.get(), from.inner.get());
|
||||
}
|
||||
|
||||
unsafe fn enter(&self) -> ! {
|
||||
self.load_state();
|
||||
__i686_enter_task(self.inner.get())
|
||||
}
|
||||
|
||||
unsafe fn switch_and_drop(&self, thread: *const ()) {
|
||||
self.load_state();
|
||||
__i686_switch_and_drop(self.inner.get(), thread);
|
||||
}
|
||||
|
||||
fn set_thread_pointer(&self, tp: usize) {
|
||||
unsafe { (*self.inner.get()).gs_base = tp };
|
||||
gdt::set_gs_base(tp);
|
||||
}
|
||||
|
||||
fn align_stack_for_entry(sp: usize) -> usize {
|
||||
(sp & !0xF) - 12
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_common_context(builder: &mut StackBuilder, entry: usize) {
|
||||
builder.push(entry);
|
||||
|
||||
builder.push(0); // %edi
|
||||
builder.push(0); // %esi
|
||||
builder.push(0); // %ebp
|
||||
builder.push(0); // %ebx
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn __i686_task_enter_kernel();
|
||||
fn __i686_task_enter_user();
|
||||
fn __i686_task_enter_from_fork();
|
||||
fn __i686_enter_task(to: *mut Inner) -> !;
|
||||
fn __i686_switch_task(to: *mut Inner, from: *mut Inner);
|
||||
fn __i686_switch_and_drop(to: *mut Inner, from: *const ());
|
||||
}
|
||||
|
||||
impl TaskFrame for SyscallFrame {
|
||||
fn store(&self) -> SavedFrame {
|
||||
SavedFrame {
|
||||
eax: self.eax as _,
|
||||
ecx: self.args[1] as _,
|
||||
edx: self.args[2] as _,
|
||||
ebx: self.args[0] as _,
|
||||
ebp: self.args[5] as _,
|
||||
esi: self.args[3] as _,
|
||||
edi: self.args[4] as _,
|
||||
|
||||
user_ip: self.eip,
|
||||
user_sp: self.esp,
|
||||
eflags: self.eflags,
|
||||
}
|
||||
}
|
||||
|
||||
fn restore(&mut self, saved: &SavedFrame) {
|
||||
self.eax = saved.eax as _;
|
||||
self.args[0] = saved.ebx as _;
|
||||
self.args[1] = saved.ecx as _;
|
||||
self.args[2] = saved.edx as _;
|
||||
self.args[3] = saved.esi as _;
|
||||
self.args[4] = saved.edi as _;
|
||||
self.args[5] = saved.ebp as _;
|
||||
|
||||
self.eip = saved.user_ip;
|
||||
self.esp = saved.user_sp;
|
||||
self.eflags = saved.eflags;
|
||||
}
|
||||
|
||||
fn user_sp(&self) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn user_ip(&self) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn argument(&self) -> u64 {
|
||||
self.args[0] as _
|
||||
}
|
||||
|
||||
fn set_user_sp(&mut self, value: usize) {
|
||||
self.esp = value as _;
|
||||
}
|
||||
|
||||
fn set_user_ip(&mut self, value: usize) {
|
||||
self.eip = value as _;
|
||||
}
|
||||
|
||||
fn set_argument(&mut self, value: u64) {
|
||||
// TODO implement ABI for passing 64-bit values via EAX/EDX
|
||||
if value & (1 << 63) != 0 {
|
||||
assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000);
|
||||
}
|
||||
|
||||
self.eax = value as usize;
|
||||
}
|
||||
|
||||
fn set_single_step(&mut self, _step: bool) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_return_value(&mut self, value: u64) {
|
||||
// TODO implement ABI for returning 64-bit values via EAX/EDX
|
||||
if value & (1 << 63) != 0 {
|
||||
assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000);
|
||||
}
|
||||
|
||||
self.eax = value as usize;
|
||||
}
|
||||
}
|
||||
|
||||
impl TaskFrame for InterruptFrame {
|
||||
fn store(&self) -> SavedFrame {
|
||||
SavedFrame {
|
||||
eax: self.eax,
|
||||
ecx: self.ecx,
|
||||
edx: self.edx,
|
||||
ebx: self.ebx,
|
||||
ebp: self.ebp,
|
||||
esi: self.esi,
|
||||
edi: self.edi,
|
||||
|
||||
user_ip: self.eip,
|
||||
user_sp: self.esp,
|
||||
eflags: self.eflags,
|
||||
}
|
||||
}
|
||||
|
||||
fn restore(&mut self, _saved: &SavedFrame) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn user_sp(&self) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn user_ip(&self) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn argument(&self) -> u64 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_user_sp(&mut self, value: usize) {
|
||||
self.esp = value as u32;
|
||||
}
|
||||
|
||||
fn set_user_ip(&mut self, value: usize) {
|
||||
self.eip = value as u32;
|
||||
}
|
||||
|
||||
fn set_argument(&mut self, value: u64) {
|
||||
// TODO implement ABI for returning 64-bit values via EAX/EDX
|
||||
if value & (1 << 63) != 0 {
|
||||
assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000);
|
||||
}
|
||||
|
||||
self.eax = value as u32;
|
||||
}
|
||||
|
||||
fn set_single_step(&mut self, step: bool) {
|
||||
if step {
|
||||
self.eflags |= 1 << 8;
|
||||
} else {
|
||||
self.eflags &= !(1 << 8);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_return_value(&mut self, value: u64) {
|
||||
// TODO implement ABI for returning 64-bit values via EAX/EDX
|
||||
if value & (1 << 63) != 0 {
|
||||
assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000);
|
||||
}
|
||||
|
||||
self.eax = value as u32;
|
||||
}
|
||||
}
|
||||
|
||||
impl TaskFrame for ExceptionFrame {
|
||||
fn store(&self) -> SavedFrame {
|
||||
SavedFrame {
|
||||
eax: self.eax,
|
||||
ecx: self.ecx,
|
||||
edx: self.edx,
|
||||
ebx: self.ebx,
|
||||
ebp: self.ebp,
|
||||
esi: self.esi,
|
||||
edi: self.edi,
|
||||
|
||||
user_ip: self.eip,
|
||||
user_sp: self.esp,
|
||||
eflags: self.eflags,
|
||||
}
|
||||
}
|
||||
|
||||
fn restore(&mut self, _saved: &SavedFrame) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn user_sp(&self) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn user_ip(&self) -> usize {
|
||||
self.eip as _
|
||||
}
|
||||
|
||||
fn argument(&self) -> u64 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_user_sp(&mut self, value: usize) {
|
||||
self.esp = value as u32;
|
||||
}
|
||||
|
||||
fn set_user_ip(&mut self, value: usize) {
|
||||
self.eip = value as u32;
|
||||
}
|
||||
|
||||
fn set_argument(&mut self, value: u64) {
|
||||
// TODO implement ABI for returning 64-bit values via EAX/EDX
|
||||
if value & (1 << 63) != 0 {
|
||||
assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000);
|
||||
}
|
||||
|
||||
self.eax = value as u32;
|
||||
}
|
||||
|
||||
fn set_single_step(&mut self, step: bool) {
|
||||
if step {
|
||||
self.eflags |= 1 << 8;
|
||||
} else {
|
||||
self.eflags &= !(1 << 8);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_return_value(&mut self, value: u64) {
|
||||
// TODO implement ABI for returning 64-bit values via EAX/EDX
|
||||
if value & (1 << 63) != 0 {
|
||||
assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000);
|
||||
}
|
||||
|
||||
self.eax = value as u32;
|
||||
}
|
||||
}
|
||||
|
||||
global_asm!(include_str!("context.S"), options(att_syntax));
|
||||
@@ -0,0 +1,108 @@
|
||||
use core::{cell::UnsafeCell, ptr::addr_of_mut};
|
||||
|
||||
use kernel_arch_interface::guard::IrqGuard;
|
||||
pub use kernel_arch_x86::gdt::{Entry, Pointer};
|
||||
|
||||
use crate::ArchitectureImpl;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(C, packed)]
|
||||
pub struct Tss {
|
||||
prev_tss: u32,
|
||||
pub esp0: u32,
|
||||
pub ss0: u16,
|
||||
_res0: u16,
|
||||
esp1: u32,
|
||||
ss1: u16,
|
||||
_res1: u16,
|
||||
esp2: u32,
|
||||
ss2: u16,
|
||||
_res2: u16,
|
||||
cr3: u32,
|
||||
eip: u32,
|
||||
eflags: u32,
|
||||
eax: u32,
|
||||
ecx: u32,
|
||||
edx: u32,
|
||||
ebx: u32,
|
||||
esp: u32,
|
||||
ebp: u32,
|
||||
esi: u32,
|
||||
edi: u32,
|
||||
es: u32,
|
||||
cs: u32,
|
||||
ss: u32,
|
||||
ds: u32,
|
||||
fs: u32,
|
||||
gs: u32,
|
||||
ldt: u32,
|
||||
trap: u16,
|
||||
iomap_base: u16,
|
||||
}
|
||||
|
||||
impl Tss {
|
||||
const NULL: Self = Self {
|
||||
prev_tss: 0,
|
||||
esp0: 0,
|
||||
ss0: 0x10,
|
||||
_res0: 0,
|
||||
esp1: 0,
|
||||
ss1: 0,
|
||||
_res1: 0,
|
||||
esp2: 0,
|
||||
ss2: 0,
|
||||
_res2: 0,
|
||||
cr3: 0,
|
||||
eip: 0,
|
||||
eflags: 0,
|
||||
eax: 0,
|
||||
ecx: 0,
|
||||
edx: 0,
|
||||
ebx: 0,
|
||||
esp: 0,
|
||||
ebp: 0,
|
||||
esi: 0,
|
||||
edi: 0,
|
||||
es: 0,
|
||||
cs: 0,
|
||||
ss: 0,
|
||||
ds: 0,
|
||||
fs: 0,
|
||||
gs: 0,
|
||||
ldt: 0,
|
||||
trap: 0,
|
||||
iomap_base: 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub static mut TSS: Tss = Tss::NULL;
|
||||
pub static mut GDT: UnsafeCell<[Entry; 7]> = UnsafeCell::new([
|
||||
Entry::NULL, // 0x00
|
||||
Entry::RING0_CS32, // 0x08
|
||||
Entry::RING0_DS32, // 0x10
|
||||
Entry::RING3_CS32, // 0x1B
|
||||
Entry::RING3_DS32, // 0x23
|
||||
Entry::NULL, // 0x28, TSS
|
||||
Entry::RING3_GS32, // 0x33, Task GS
|
||||
]);
|
||||
|
||||
pub fn create_gdt() -> (&'static [Entry], &'static Tss) {
|
||||
// Won't be deallocated, so leaks are not a concern
|
||||
let tss = unsafe { &mut *addr_of_mut!(TSS) };
|
||||
tss.ss0 = 0x10;
|
||||
let tss_addr = (tss as *mut Tss).addr();
|
||||
#[allow(static_mut_refs)]
|
||||
let gdt = unsafe { GDT.get_mut() };
|
||||
gdt[5] = Entry::tss(tss_addr as u32, (size_of::<Tss>() - 1) as u32);
|
||||
|
||||
(gdt, tss)
|
||||
}
|
||||
|
||||
pub fn set_gs_base(gs_base: usize) {
|
||||
let _guard = IrqGuard::<ArchitectureImpl>::acquire();
|
||||
unsafe {
|
||||
#[allow(static_mut_refs)]
|
||||
GDT.get_mut()[6].set_base(gs_base);
|
||||
core::arch::asm!("mov $0x33, %ax; mov %ax, %gs", out("ax") _, options(att_syntax, nostack));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
#![feature(never_type, naked_functions, trace_macros)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use core::ptr::null_mut;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use device_api::interrupt::{LocalInterruptController, MessageInterruptController};
|
||||
use kernel_arch_interface::{
|
||||
cpu::{CpuData, CpuImpl, IpiQueue},
|
||||
task::Scheduler,
|
||||
Architecture,
|
||||
};
|
||||
|
||||
pub mod context;
|
||||
pub mod gdt;
|
||||
pub mod mem;
|
||||
|
||||
pub use context::TaskContextImpl;
|
||||
use kernel_arch_x86::cpuid::CpuFeatures;
|
||||
pub use mem::{KernelTableManagerImpl, ProcessAddressSpaceImpl};
|
||||
|
||||
pub struct ArchitectureImpl;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PerCpuData {
|
||||
pub available_features: CpuFeatures,
|
||||
pub enabled_features: CpuFeatures,
|
||||
}
|
||||
|
||||
impl CpuData for PerCpuData {}
|
||||
|
||||
static mut CPU: *mut () = null_mut();
|
||||
|
||||
#[naked]
|
||||
extern "C" fn idle_task(_: usize) -> ! {
|
||||
unsafe {
|
||||
core::arch::naked_asm!(
|
||||
r#"
|
||||
1:
|
||||
nop
|
||||
jmp 1b
|
||||
"#,
|
||||
options(att_syntax)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl Architecture for ArchitectureImpl {
|
||||
type PerCpuData = PerCpuData;
|
||||
type CpuFeatures = CpuFeatures;
|
||||
type BreakpointType = u8;
|
||||
|
||||
const BREAKPOINT_VALUE: Self::BreakpointType = 0xCC;
|
||||
|
||||
unsafe fn init_local_cpu<S: Scheduler + 'static>(id: Option<u32>, data: Self::PerCpuData) {
|
||||
use alloc::boxed::Box;
|
||||
|
||||
let cpu = Box::leak(Box::new(CpuImpl::<Self, S>::new(
|
||||
id.expect("x86_64 required manual CPU ID set"),
|
||||
data,
|
||||
)));
|
||||
|
||||
cpu.set_local();
|
||||
}
|
||||
|
||||
unsafe fn set_interrupt_mask(mask: bool) -> bool {
|
||||
let old = Self::interrupt_mask();
|
||||
if mask {
|
||||
core::arch::asm!("cli");
|
||||
} else {
|
||||
core::arch::asm!("sti");
|
||||
}
|
||||
old
|
||||
}
|
||||
|
||||
fn interrupt_mask() -> bool {
|
||||
let mut flags: u32;
|
||||
unsafe {
|
||||
core::arch::asm!("pushfl; pop {0:e}", out(reg) flags, options(att_syntax));
|
||||
}
|
||||
// If IF is zero, interrupts are disabled (masked)
|
||||
flags & (1 << 9) == 0
|
||||
}
|
||||
|
||||
fn wait_for_interrupt() {
|
||||
unsafe {
|
||||
core::arch::asm!("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn init_ipi_queues(_queues: Vec<IpiQueue<Self>>) {}
|
||||
|
||||
fn local_cpu() -> *mut () {
|
||||
unsafe { CPU }
|
||||
}
|
||||
|
||||
fn cpu_index<S: Scheduler + 'static>() -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
unsafe fn set_local_cpu(cpu: *mut ()) {
|
||||
CPU = cpu;
|
||||
}
|
||||
|
||||
fn cpu_count() -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
|
||||
None
|
||||
}
|
||||
|
||||
fn message_interrupt_controller() -> Option<&'static dyn MessageInterruptController> {
|
||||
None
|
||||
}
|
||||
|
||||
fn ipi_queue(_cpu_id: u32) -> Option<&'static IpiQueue<Self>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn idle_task() -> extern "C" fn(usize) -> ! {
|
||||
idle_task
|
||||
}
|
||||
|
||||
fn halt() -> ! {
|
||||
loop {
|
||||
unsafe {
|
||||
core::arch::asm!("cli; hlt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cpu_available_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
|
||||
Some(&cpu.available_features)
|
||||
}
|
||||
|
||||
fn cpu_enabled_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
|
||||
Some(&cpu.enabled_features)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
use kernel_arch_interface::{sync::IrqSafeSpinlock, KERNEL_VIRT_OFFSET};
|
||||
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel, KernelImageObject};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
mem::{flush_tlb_entry, table::PageAttributes},
|
||||
ArchitectureImpl,
|
||||
};
|
||||
|
||||
use super::{
|
||||
table::{PageEntry, PageTable, L0, L3},
|
||||
KERNEL_TABLES,
|
||||
};
|
||||
|
||||
pub const KERNEL_SPLIT_L0: usize = KERNEL_VIRT_OFFSET >> 22;
|
||||
pub const DYNAMIC_MAP_COUNT: usize = 64;
|
||||
pub const FIXED_MAP_COUNT: usize = 1024 - (KERNEL_SPLIT_L0 + DYNAMIC_MAP_COUNT);
|
||||
pub const DYNAMIC_MAP_OFFSET: usize = (KERNEL_SPLIT_L0 + FIXED_MAP_COUNT) << L0::SHIFT;
|
||||
pub const MAX_FIXED_PHYSICAL: PhysicalAddress =
|
||||
PhysicalAddress::from_u64((FIXED_MAP_COUNT as u64) << 22);
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FixedTables {
|
||||
pub l0: KernelL0,
|
||||
pub dynamic: IrqSafeSpinlock<ArchitectureImpl, KernelDynamic>,
|
||||
}
|
||||
|
||||
#[repr(C, align(0x1000))]
|
||||
pub struct KernelL0 {
|
||||
pub lower: [PageEntry<L0>; KERNEL_SPLIT_L0],
|
||||
pub kernel: [PageEntry<L0>; FIXED_MAP_COUNT],
|
||||
pub dynamic: [PageEntry<L0>; DYNAMIC_MAP_COUNT],
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct KernelDynamic {
|
||||
pub l3s: [KernelImageObject<PageTable<L3>>; DYNAMIC_MAP_COUNT],
|
||||
free: usize,
|
||||
}
|
||||
|
||||
impl FixedTables {
|
||||
pub const fn zeroed() -> Self {
|
||||
Self {
|
||||
l0: KernelL0::zeroed(),
|
||||
dynamic: IrqSafeSpinlock::new(KernelDynamic::zeroed()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn virtualize(&mut self, address: PhysicalAddress) -> usize {
|
||||
if address < MAX_FIXED_PHYSICAL {
|
||||
// It's a fixed address
|
||||
address.into_u64() as usize + KERNEL_VIRT_OFFSET
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn physicalize(&mut self, address: usize) -> Option<PhysicalAddress> {
|
||||
if address < KERNEL_VIRT_OFFSET {
|
||||
return None;
|
||||
}
|
||||
|
||||
if address < KERNEL_VIRT_OFFSET + MAX_FIXED_PHYSICAL.into_u64() as usize {
|
||||
// It's a fixed address
|
||||
Some(PhysicalAddress::from_usize(address - KERNEL_VIRT_OFFSET))
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_dynamic_memory(&mut self, base: u64, page_count: usize) -> Result<usize, Error> {
|
||||
self.dynamic.lock().map(base, page_count)
|
||||
}
|
||||
}
|
||||
|
||||
impl KernelL0 {
|
||||
pub const fn zeroed() -> Self {
|
||||
Self {
|
||||
lower: [PageEntry::INVALID; KERNEL_SPLIT_L0],
|
||||
kernel: [PageEntry::INVALID; FIXED_MAP_COUNT],
|
||||
dynamic: [PageEntry::INVALID; DYNAMIC_MAP_COUNT],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KernelDynamic {
|
||||
pub const fn zeroed() -> Self {
|
||||
Self {
|
||||
l3s: [const { unsafe { KernelImageObject::new(PageTable::zeroed()) } };
|
||||
DYNAMIC_MAP_COUNT],
|
||||
free: DYNAMIC_MAP_COUNT * 1024,
|
||||
}
|
||||
}
|
||||
|
||||
fn map(&mut self, base: u64, page_count: usize) -> Result<usize, Error> {
|
||||
if page_count > self.free {
|
||||
return Err(Error::OutOfMemory);
|
||||
}
|
||||
|
||||
'l0: for i in 0..DYNAMIC_MAP_COUNT * 1024 - page_count {
|
||||
for j in 0..page_count {
|
||||
let entry = self.entry(i + j);
|
||||
if entry.is_present() {
|
||||
continue 'l0;
|
||||
}
|
||||
}
|
||||
|
||||
self.free -= page_count;
|
||||
for j in 0..page_count {
|
||||
let address = PhysicalAddress::from_u64(base + ((j as u64) << L3::SHIFT));
|
||||
*self.entry_mut(i + j) = PageEntry::page(address, PageAttributes::WRITABLE);
|
||||
unsafe {
|
||||
flush_tlb_entry(DYNAMIC_MAP_OFFSET + ((i + j) << L3::SHIFT));
|
||||
}
|
||||
}
|
||||
let addr = DYNAMIC_MAP_OFFSET + (i << L3::SHIFT);
|
||||
return Ok(addr);
|
||||
}
|
||||
|
||||
Err(Error::OutOfMemory)
|
||||
}
|
||||
|
||||
fn entry(&self, index: usize) -> &PageEntry<L3> {
|
||||
&self.l3s[index / 1024][index % 1024]
|
||||
}
|
||||
|
||||
fn entry_mut(&mut self, index: usize) -> &mut PageEntry<L3> {
|
||||
&mut self.l3s[index / 1024][index % 1024]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clone_kernel_tables(dst: &mut PageTable<L0>) {
|
||||
let tables = KERNEL_TABLES.lock();
|
||||
for (i, entry) in tables.l0.kernel.iter().enumerate() {
|
||||
dst[i + KERNEL_SPLIT_L0] = *entry;
|
||||
}
|
||||
|
||||
for (i, entry) in tables.l0.dynamic.iter().enumerate() {
|
||||
dst[i + KERNEL_SPLIT_L0 + FIXED_MAP_COUNT] = *entry;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
use fixed::FixedTables;
|
||||
use kernel_arch_interface::{
|
||||
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
|
||||
split_spinlock, KERNEL_VIRT_OFFSET,
|
||||
};
|
||||
use libk_mm_interface::{
|
||||
address::{AsPhysicalAddress, PhysicalAddress},
|
||||
table::{page_count, EntryLevel},
|
||||
};
|
||||
use table::{PageAttributes, PageEntry, L0, L3};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
pub mod fixed;
|
||||
pub mod process;
|
||||
pub mod table;
|
||||
|
||||
pub use process::ProcessAddressSpaceImpl;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KernelTableManagerImpl;
|
||||
|
||||
split_spinlock! {
|
||||
use libk_mm_interface::KernelImageObject;
|
||||
use crate::mem::FixedTables;
|
||||
use crate::ArchitectureImpl;
|
||||
|
||||
#[link_section = ".data.tables"]
|
||||
static KERNEL_TABLES: KernelImageObject<FixedTables> = unsafe {
|
||||
KernelImageObject::new(FixedTables::zeroed())
|
||||
};
|
||||
}
|
||||
|
||||
impl KernelTableManager for KernelTableManagerImpl {
|
||||
unsafe fn map_device_pages(
|
||||
base: u64,
|
||||
count: usize,
|
||||
_attrs: DeviceMemoryAttributes,
|
||||
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
|
||||
// TODO page align up
|
||||
|
||||
let offset = (base & 0xFFF) as usize;
|
||||
let base = base & !0xFFF;
|
||||
let end = (base + count as u64 + 0xFFF) & !0xFFF;
|
||||
|
||||
// assert_eq!(base & 0xFFF, 0);
|
||||
if end < fixed::MAX_FIXED_PHYSICAL.into_u64() {
|
||||
// 1:1
|
||||
let address = Self::virtualize(base);
|
||||
Ok(RawDeviceMemoryMapping::from_raw_parts(
|
||||
address, address, 0, 0,
|
||||
))
|
||||
} else {
|
||||
assert_eq!(base & 0xFFF, 0);
|
||||
log::info!("map_device_pages({:#x}, {})", base, count);
|
||||
let page_count = page_count::<L3>(count);
|
||||
let virt = KERNEL_TABLES.lock().map_dynamic_memory(base, page_count)?;
|
||||
|
||||
Ok(RawDeviceMemoryMapping::from_raw_parts(
|
||||
virt + offset,
|
||||
virt,
|
||||
page_count,
|
||||
0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn unmap_device_pages(_mapping: &RawDeviceMemoryMapping<Self>) {
|
||||
// todo!()
|
||||
}
|
||||
|
||||
fn virtualize(phys: u64) -> usize {
|
||||
KERNEL_TABLES
|
||||
.lock()
|
||||
.virtualize(PhysicalAddress::from_u64(phys))
|
||||
}
|
||||
|
||||
fn physicalize(virt: usize) -> u64 {
|
||||
KERNEL_TABLES
|
||||
.lock()
|
||||
.physicalize(virt)
|
||||
.expect("Invalid virtual address")
|
||||
.into_u64()
|
||||
}
|
||||
|
||||
unsafe fn unmap_physical_address(virt: usize) {
|
||||
if virt < KERNEL_VIRT_OFFSET {
|
||||
panic!("Invalid 'virtualized' address: {:#x}", virt);
|
||||
}
|
||||
let virt = virt - KERNEL_VIRT_OFFSET;
|
||||
if virt >= fixed::FIXED_MAP_COUNT << L0::SHIFT {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets up fixed MMU translation tables.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only meant to be called once during early OS init.
|
||||
pub unsafe fn init_fixed_tables() {
|
||||
let mut tables = KERNEL_TABLES.lock();
|
||||
|
||||
// Unmap lower stuff
|
||||
for (i, entry) in tables.l0.lower.iter_mut().enumerate() {
|
||||
*entry = PageEntry::INVALID;
|
||||
flush_tlb_entry(i << 22);
|
||||
}
|
||||
|
||||
// Map the rest of fixed translation
|
||||
for (i, entry) in tables.l0.kernel.iter_mut().enumerate() {
|
||||
let virt = KERNEL_VIRT_OFFSET + (i << L0::SHIFT);
|
||||
let phys = (i << L0::SHIFT) as u32;
|
||||
*entry = PageEntry::block(PhysicalAddress::from_u32(phys), PageAttributes::WRITABLE);
|
||||
flush_tlb_entry(virt);
|
||||
}
|
||||
|
||||
let dynamic_len = tables.l0.dynamic.len();
|
||||
for i in 0..dynamic_len {
|
||||
let phys = tables.dynamic.lock().l3s[i].as_physical_address();
|
||||
tables.l0.dynamic[i] = PageEntry::table(phys, PageAttributes::WRITABLE);
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `address` must be page-aligned.
|
||||
#[inline]
|
||||
pub unsafe fn flush_tlb_entry(address: usize) {
|
||||
core::arch::asm!("invlpg ({0})", in(reg) address, options(att_syntax));
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use kernel_arch_interface::KERNEL_VIRT_OFFSET;
|
||||
use libk_mm_interface::{
|
||||
address::{AsPhysicalAddress, PhysicalAddress},
|
||||
pointer::PhysicalRefMut,
|
||||
process::ProcessAddressSpaceManager,
|
||||
table::{
|
||||
EntryLevel, EntryLevelDrop, EntryLevelExt, MapAttributes, NextPageTable, TableAllocator,
|
||||
},
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{mem::flush_tlb_entry, KernelTableManagerImpl};
|
||||
|
||||
use super::{
|
||||
fixed::{clone_kernel_tables, KERNEL_SPLIT_L0},
|
||||
table::{PageEntry, PageTable, L0, L3},
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ProcessAddressSpaceImpl<TA: TableAllocator> {
|
||||
l0: PhysicalRefMut<'static, PageTable<L0>, KernelTableManagerImpl>,
|
||||
_alloc: PhantomData<TA>,
|
||||
}
|
||||
|
||||
impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceImpl<TA> {
|
||||
const UPPER_LIMIT_PFN: usize = KERNEL_VIRT_OFFSET >> L3::SHIFT;
|
||||
const LOWER_LIMIT_PFN: usize = 32;
|
||||
|
||||
fn new() -> Result<Self, Error> {
|
||||
let mut l0 = unsafe {
|
||||
PhysicalRefMut::<'static, PageTable<L0>, KernelTableManagerImpl>::map(
|
||||
TA::allocate_page_table()?,
|
||||
)
|
||||
};
|
||||
|
||||
for i in 0..1024 {
|
||||
l0[i] = PageEntry::INVALID;
|
||||
}
|
||||
|
||||
clone_kernel_tables(&mut l0);
|
||||
|
||||
Ok(Self {
|
||||
l0,
|
||||
_alloc: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn clear(&mut self) {
|
||||
self.l0.drop_range::<TA>(0..KERNEL_SPLIT_L0);
|
||||
}
|
||||
|
||||
fn translate(&self, address: usize) -> Result<(PhysicalAddress, MapAttributes), Error> {
|
||||
self.read_l3_entry(address).ok_or(Error::DoesNotExist)
|
||||
}
|
||||
|
||||
unsafe fn map_page(
|
||||
&mut self,
|
||||
address: usize,
|
||||
physical: PhysicalAddress,
|
||||
flags: MapAttributes,
|
||||
) -> Result<(), Error> {
|
||||
self.write_l3_entry(address, PageEntry::page(physical, flags.into()), false)
|
||||
}
|
||||
|
||||
unsafe fn unmap_page(&mut self, address: usize) -> Result<PhysicalAddress, Error> {
|
||||
self.pop_l3_entry(address)
|
||||
}
|
||||
|
||||
fn as_address_with_asid(&self) -> (u64, u64) {
|
||||
(unsafe { self.l0.as_physical_address().into_u64() }, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<TA: TableAllocator> ProcessAddressSpaceImpl<TA> {
|
||||
// Write a single 4KiB entry
|
||||
fn write_l3_entry(
|
||||
&mut self,
|
||||
virt: usize,
|
||||
entry: PageEntry<L3>,
|
||||
overwrite: bool,
|
||||
) -> Result<(), Error> {
|
||||
let l0i = virt.page_index::<L0>();
|
||||
let l3i = virt.page_index::<L3>();
|
||||
|
||||
let mut l3 = self.l0.get_mut_or_alloc::<TA>(l0i)?;
|
||||
|
||||
if l3[l3i].is_present() && !overwrite {
|
||||
todo!();
|
||||
}
|
||||
|
||||
l3[l3i] = entry;
|
||||
unsafe {
|
||||
flush_tlb_entry(virt);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pop_l3_entry(&mut self, virt: usize) -> Result<PhysicalAddress, Error> {
|
||||
let l0i = virt.page_index::<L0>();
|
||||
let l3i = virt.page_index::<L3>();
|
||||
|
||||
let mut l3 = self.l0.get_mut(l0i).ok_or(Error::DoesNotExist)?;
|
||||
let page = l3[l3i].as_page().ok_or(Error::DoesNotExist)?;
|
||||
|
||||
l3[l3i] = PageEntry::INVALID;
|
||||
unsafe {
|
||||
flush_tlb_entry(virt);
|
||||
}
|
||||
|
||||
Ok(page)
|
||||
}
|
||||
|
||||
fn read_l3_entry(&self, virt: usize) -> Option<(PhysicalAddress, MapAttributes)> {
|
||||
let l0i = virt.page_index::<L0>();
|
||||
let l3i = virt.page_index::<L3>();
|
||||
|
||||
let l3 = self.l0.get(l0i)?;
|
||||
let page = l3[l3i].as_page()?;
|
||||
|
||||
Some((page.add(virt & 0xFFF), l3[l3i].attributes().into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<TA: TableAllocator> Drop for ProcessAddressSpaceImpl<TA> {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: with safe usage of the ProcessAddressSpaceImpl, clearing and dropping
|
||||
// is safe, no one refers to the memory
|
||||
unsafe {
|
||||
self.clear();
|
||||
let l0_phys = self.l0.as_physical_address();
|
||||
TA::free_page_table(l0_phys);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
ops::{Index, IndexMut, Range},
|
||||
};
|
||||
|
||||
use bitflags::bitflags;
|
||||
use libk_mm_interface::{
|
||||
address::{AsPhysicalAddress, PhysicalAddress},
|
||||
pointer::{PhysicalRef, PhysicalRefMut},
|
||||
table::{
|
||||
EntryLevel, EntryLevelDrop, MapAttributes, NextPageTable, NonTerminalEntryLevel,
|
||||
TableAllocator,
|
||||
},
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::KernelTableManagerImpl;
|
||||
|
||||
bitflags! {
|
||||
/// Describes how each page table entry is mapped
|
||||
pub struct PageAttributes: u32 {
|
||||
/// When set, the mapping is considered valid and pointing somewhere
|
||||
const PRESENT = 1 << 0;
|
||||
/// For tables, allows writes to further translation levels, for pages/blocks, allows
|
||||
/// writes to the region covered by the entry
|
||||
const WRITABLE = 1 << 1;
|
||||
/// When set for L2 entries, the mapping specifies a 2MiB page instead of a page table
|
||||
/// reference
|
||||
const BLOCK = 1 << 7;
|
||||
/// For tables, allows user access to further translation levels, for pages/blocks, allows
|
||||
/// user access to the region covered by the entry
|
||||
const USER = 1 << 2;
|
||||
}
|
||||
}
|
||||
// TODO stuff for PAE?
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct L3;
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct L0;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct PageEntry<L: EntryLevel>(u32, PhantomData<L>);
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C, align(0x1000))]
|
||||
pub struct PageTable<L: EntryLevel> {
|
||||
data: [PageEntry<L>; 1024],
|
||||
}
|
||||
|
||||
impl EntryLevel for L3 {
|
||||
const SHIFT: usize = 12;
|
||||
}
|
||||
|
||||
impl EntryLevel for L0 {
|
||||
const SHIFT: usize = 22;
|
||||
}
|
||||
|
||||
impl NonTerminalEntryLevel for L0 {
|
||||
type NextLevel = L3;
|
||||
}
|
||||
|
||||
impl PageEntry<L3> {
|
||||
pub fn page(address: PhysicalAddress, attrs: PageAttributes) -> Self {
|
||||
Self(
|
||||
address.try_into_u32().unwrap() | (PageAttributes::PRESENT | attrs).bits(),
|
||||
PhantomData,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_page(&self) -> Option<PhysicalAddress> {
|
||||
if self.0 & PageAttributes::PRESENT.bits() != 0 {
|
||||
Some(PhysicalAddress::from_u32(self.0 & !0xFFF))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PageEntry<L0> {
|
||||
pub fn block(address: PhysicalAddress, attrs: PageAttributes) -> Self {
|
||||
Self(
|
||||
address.try_into_u32().unwrap()
|
||||
| (PageAttributes::PRESENT | PageAttributes::BLOCK | attrs).bits(),
|
||||
PhantomData,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn table(address: PhysicalAddress, attrs: PageAttributes) -> Self {
|
||||
Self(
|
||||
address.try_into_u32().unwrap() | (PageAttributes::PRESENT | attrs).bits(),
|
||||
PhantomData,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_table(&self) -> Option<PhysicalAddress> {
|
||||
if self.0 & PageAttributes::PRESENT.bits() != 0
|
||||
&& self.0 & PageAttributes::BLOCK.bits() == 0
|
||||
{
|
||||
Some(PhysicalAddress::from_u32(self.0 & !0xFFF))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: EntryLevel> PageEntry<L> {
|
||||
pub const INVALID: Self = Self(0, PhantomData);
|
||||
|
||||
pub fn is_present(&self) -> bool {
|
||||
self.0 & (1 << 0) != 0
|
||||
}
|
||||
|
||||
pub fn attributes(&self) -> PageAttributes {
|
||||
PageAttributes::from_bits_retain(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: EntryLevel> PageTable<L> {
|
||||
pub const fn zeroed() -> Self {
|
||||
Self {
|
||||
data: [PageEntry::INVALID; 1024],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_zeroed<'a, TA: TableAllocator>(
|
||||
) -> Result<PhysicalRefMut<'a, Self, KernelTableManagerImpl>, Error> {
|
||||
let physical = TA::allocate_page_table()?;
|
||||
let mut table =
|
||||
unsafe { PhysicalRefMut::<'a, Self, KernelTableManagerImpl>::map(physical) };
|
||||
|
||||
for i in 0..1024 {
|
||||
table[i] = PageEntry::INVALID;
|
||||
}
|
||||
|
||||
Ok(table)
|
||||
}
|
||||
|
||||
/// Recursively clears and deallocates the translation table.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure the table is no longer in use and is not referenced anymore.
|
||||
pub unsafe fn free<TA: TableAllocator>(this: PhysicalRefMut<Self, KernelTableManagerImpl>) {
|
||||
let physical = this.as_physical_address();
|
||||
TA::free_page_table(physical);
|
||||
}
|
||||
}
|
||||
|
||||
impl NextPageTable for PageTable<L0> {
|
||||
type NextLevel = PageTable<L3>;
|
||||
type TableRef = PhysicalRef<'static, Self::NextLevel, KernelTableManagerImpl>;
|
||||
type TableRefMut = PhysicalRefMut<'static, Self::NextLevel, KernelTableManagerImpl>;
|
||||
|
||||
fn get(&self, index: usize) -> Option<Self::TableRef> {
|
||||
self[index]
|
||||
.as_table()
|
||||
.map(|addr| unsafe { PhysicalRef::map(addr) })
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, index: usize) -> Option<Self::TableRefMut> {
|
||||
self[index]
|
||||
.as_table()
|
||||
.map(|addr| unsafe { PhysicalRefMut::map(addr) })
|
||||
}
|
||||
|
||||
fn get_mut_or_alloc<TA: TableAllocator>(
|
||||
&mut self,
|
||||
index: usize,
|
||||
) -> Result<Self::TableRefMut, Error> {
|
||||
let entry = self[index];
|
||||
|
||||
if let Some(table) = entry.as_table() {
|
||||
Ok(unsafe { PhysicalRefMut::map(table) })
|
||||
} else {
|
||||
let table = PageTable::new_zeroed::<TA>()?;
|
||||
self[index] = PageEntry::<L0>::table(
|
||||
unsafe { table.as_physical_address() },
|
||||
PageAttributes::WRITABLE | PageAttributes::USER,
|
||||
);
|
||||
Ok(table)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: EntryLevel> Index<usize> for PageTable<L> {
|
||||
type Output = PageEntry<L>;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.data[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: EntryLevel> IndexMut<usize> for PageTable<L> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
&mut self.data[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl EntryLevelDrop for PageTable<L3> {
|
||||
const FULL_RANGE: Range<usize> = 0..1024;
|
||||
|
||||
unsafe fn drop_range<TA: TableAllocator>(&mut self, _range: Range<usize>) {}
|
||||
}
|
||||
|
||||
impl EntryLevelDrop for PageTable<L0> {
|
||||
const FULL_RANGE: Range<usize> = 0..1024;
|
||||
|
||||
unsafe fn drop_range<TA: TableAllocator>(&mut self, range: Range<usize>) {
|
||||
for index in range {
|
||||
let entry = self[index];
|
||||
|
||||
if let Some(table) = entry.as_table() {
|
||||
let mut table_ref: PhysicalRefMut<PageTable<L3>, KernelTableManagerImpl> =
|
||||
PhysicalRefMut::map(table);
|
||||
|
||||
table_ref.drop_all::<TA>();
|
||||
|
||||
TA::free_page_table(table);
|
||||
} else if entry.is_present() {
|
||||
// Memory must've been cleared beforehand, so no non-table entries must be present
|
||||
panic!(
|
||||
"Expected a table containing only tables, got table[{}] = {:#x?}",
|
||||
index, entry.0
|
||||
);
|
||||
}
|
||||
|
||||
self[index] = PageEntry::INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MapAttributes> for PageAttributes {
|
||||
fn from(value: MapAttributes) -> Self {
|
||||
let mut res = PageAttributes::WRITABLE;
|
||||
if value.intersects(MapAttributes::USER_READ | MapAttributes::USER_WRITE) {
|
||||
res |= PageAttributes::USER;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PageAttributes> for MapAttributes {
|
||||
fn from(value: PageAttributes) -> Self {
|
||||
let mut res = MapAttributes::empty();
|
||||
if value.contains(PageAttributes::USER) {
|
||||
res |= MapAttributes::USER_READ;
|
||||
if value.contains(PageAttributes::WRITABLE) {
|
||||
res |= MapAttributes::USER_WRITE;
|
||||
}
|
||||
}
|
||||
// TODO ???
|
||||
res |= MapAttributes::NON_GLOBAL;
|
||||
res
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ pub mod sync;
|
||||
pub mod task;
|
||||
pub mod util;
|
||||
|
||||
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
||||
pub const KERNEL_VIRT_OFFSET: usize = 0xC0000000;
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64", rust_analyzer))]
|
||||
pub const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000;
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
|
||||
@@ -2,6 +2,9 @@ use core::{fmt, marker::PhantomData, mem::size_of, ptr::NonNull};
|
||||
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
pub mod address;
|
||||
pub mod table;
|
||||
|
||||
pub trait PhysicalMemoryAllocator {
|
||||
type Address;
|
||||
|
||||
|
||||
@@ -15,5 +15,11 @@ static_assertions.workspace = true
|
||||
log.workspace = true
|
||||
cfg-if.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
riscv64_board_virt = []
|
||||
riscv64_board_jh7110 = []
|
||||
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -13,7 +13,7 @@ use tock_registers::{
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
mem::{self},
|
||||
mem::{self, KERNEL_VIRT_OFFSET},
|
||||
registers::SATP,
|
||||
ArchitectureImpl, PerCpuData,
|
||||
};
|
||||
@@ -124,7 +124,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
|
||||
// TODO stack is leaked
|
||||
let satp = InMemoryRegister::new(0);
|
||||
let kernel_table_phys = mem::fixed::table_physical_address().into_u64();
|
||||
let kernel_table_phys = ((&raw const mem::FIXED_L1).addr() - KERNEL_VIRT_OFFSET) as u64;
|
||||
satp.write(SATP::MODE::Sv39 + SATP::ASID.val(0) + SATP::PPN.val(kernel_table_phys >> 12));
|
||||
|
||||
Ok(Self {
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
use kernel_arch_interface::sync::IrqSafeSpinlock;
|
||||
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel};
|
||||
|
||||
use crate::{
|
||||
mem::{
|
||||
auto_lower_address,
|
||||
table::{PageEntry, PageTable, L1},
|
||||
KERNEL_VIRT_OFFSET,
|
||||
},
|
||||
ArchitectureImpl,
|
||||
};
|
||||
|
||||
pub const IDENTITY_SIZE_L1: usize = 64;
|
||||
|
||||
pub(super) static mut KERNEL_L1: PageTable<L1> = const {
|
||||
let mut table = PageTable::zeroed();
|
||||
|
||||
let mut index = 0;
|
||||
while index < IDENTITY_SIZE_L1 {
|
||||
let entry = PageEntry::identity_block(PhysicalAddress::from_usize(index << L1::SHIFT));
|
||||
table.entries[index] = entry;
|
||||
table.entries[index + ((KERNEL_VIRT_OFFSET >> L1::SHIFT) & 0x1FF)] = entry;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
table
|
||||
};
|
||||
pub(super) static LOCK: IrqSafeSpinlock<ArchitectureImpl, ()> = IrqSafeSpinlock::new(());
|
||||
|
||||
pub fn table_physical_address() -> PhysicalAddress {
|
||||
PhysicalAddress::from_usize(auto_lower_address(&raw const KERNEL_L1))
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
use libk_mm_interface::table::{EntryLevel, EntryLevelExt};
|
||||
|
||||
use crate::mem::table::L3;
|
||||
|
||||
pub fn tlb_flush_global_full() {
|
||||
tlb_flush_full();
|
||||
// TODO send TLB shootdown IPI to other harts
|
||||
}
|
||||
|
||||
pub fn tlb_flush_global_va(va: usize) {
|
||||
tlb_flush_va(va);
|
||||
// TODO send TLB shootdown IPI to other harts
|
||||
}
|
||||
|
||||
pub fn tlb_flush_range_va(start: usize, size: usize) {
|
||||
let end = (start + size).page_align_up::<L3>();
|
||||
let start = start.page_align_down::<L3>();
|
||||
|
||||
for page in (start..end).step_by(L3::SIZE) {
|
||||
tlb_flush_va(page);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tlb_flush_range_va_asid(asid: usize, start: usize, size: usize) {
|
||||
let end = (start + size).page_align_up::<L3>();
|
||||
let start = start.page_align_down::<L3>();
|
||||
|
||||
for page in (start..end).step_by(L3::SIZE) {
|
||||
tlb_flush_va_asid(page, asid);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_full() {
|
||||
unsafe { core::arch::asm!("sfence.vma") };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_va(va: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma {0}, zero", in(reg) va) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_asid(asid: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma zero, {0}", in(reg) asid) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_va_asid(va: usize, asid: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma {0}, {1}", in(reg) va, in(reg) asid) };
|
||||
}
|
||||
@@ -5,22 +5,34 @@ use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
table::{page_index, EntryLevel, EntryLevelExt},
|
||||
};
|
||||
use table::{PageEntry, PageTable, L1, L3};
|
||||
use tock_registers::interfaces::Writeable;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
mem::table::{PageTable, L1, L3},
|
||||
registers::SATP,
|
||||
};
|
||||
use crate::registers::SATP;
|
||||
|
||||
pub use intrinsics::*;
|
||||
|
||||
pub mod fixed;
|
||||
pub mod intrinsics;
|
||||
pub mod process;
|
||||
pub mod table;
|
||||
|
||||
pub const KERNEL_VIRT_OFFSET: usize = kernel_arch_interface::KERNEL_VIRT_OFFSET;
|
||||
pub const SIGN_EXTEND_MASK: usize = 0xFFFFFF80_00000000;
|
||||
|
||||
pub const IDENTITY_SIZE: usize = 64 * L1::SIZE;
|
||||
pub const IDENTITY_L1_START: usize = page_index::<L1>(KERNEL_VIRT_OFFSET);
|
||||
|
||||
pub static mut FIXED_L1: PageTable<L1> = const {
|
||||
let mut table = PageTable::zeroed();
|
||||
|
||||
let mut i = 0;
|
||||
while i < IDENTITY_SIZE / L1::SIZE {
|
||||
let entry = PageEntry::identity_block(PhysicalAddress::from_usize(i * L1::SIZE));
|
||||
table.entries[i] = entry;
|
||||
table.entries[i + IDENTITY_L1_START] = entry;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
table
|
||||
};
|
||||
|
||||
/// Any VAs above this one are sign-extended
|
||||
pub const USER_BOUNDARY: usize = 0x40_00000000;
|
||||
@@ -31,18 +43,15 @@ pub struct KernelTableManagerImpl;
|
||||
impl KernelTableManager for KernelTableManagerImpl {
|
||||
fn virtualize(address: u64) -> usize {
|
||||
let address = address as usize;
|
||||
if address < fixed::IDENTITY_SIZE_L1 * L1::SIZE {
|
||||
address + KERNEL_VIRT_OFFSET
|
||||
} else {
|
||||
panic!("Invalid physical address: {address:#x}")
|
||||
if address >= IDENTITY_SIZE {
|
||||
panic!("Invalid physical address: {address:#x}");
|
||||
}
|
||||
address + KERNEL_VIRT_OFFSET
|
||||
}
|
||||
|
||||
fn physicalize(address: usize) -> u64 {
|
||||
if address < KERNEL_VIRT_OFFSET
|
||||
|| address - KERNEL_VIRT_OFFSET >= fixed::IDENTITY_SIZE_L1 * L1::SIZE
|
||||
{
|
||||
panic!("Invalid virtualized address: {address:#x}");
|
||||
if address < KERNEL_VIRT_OFFSET || address - KERNEL_VIRT_OFFSET >= IDENTITY_SIZE {
|
||||
panic!("Invalid \"physicalized\" virtual address {address:#x}");
|
||||
}
|
||||
(address - KERNEL_VIRT_OFFSET) as u64
|
||||
}
|
||||
@@ -53,7 +62,6 @@ impl KernelTableManager for KernelTableManagerImpl {
|
||||
attrs: DeviceMemoryAttributes,
|
||||
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
|
||||
let _ = attrs;
|
||||
let _lock = fixed::LOCK.lock();
|
||||
let base = PhysicalAddress::from_u64(base);
|
||||
let l3_aligned_base = base.page_align_down::<L3>();
|
||||
let l3_aligned_end = base.add(count).page_align_up::<L3>();
|
||||
@@ -92,14 +100,75 @@ pub fn auto_lower_address<T>(x: *const T) -> usize {
|
||||
///
|
||||
/// Only meant to be called once per each HART during their early init.
|
||||
pub unsafe fn enable_mmu() {
|
||||
let l1_phys = auto_lower_address(&raw const fixed::KERNEL_L1) as u64;
|
||||
let l1_phys = auto_lower_address(&raw const FIXED_L1) as u64;
|
||||
tlb_flush_full();
|
||||
SATP.write(SATP::PPN.val(l1_phys >> 12) + SATP::MODE::Sv39);
|
||||
}
|
||||
|
||||
pub fn clone_kernel_tables(dst: &mut PageTable<L1>) {
|
||||
let _lock = fixed::LOCK.lock();
|
||||
for l1i in page_index::<L1>(USER_BOUNDARY)..512 {
|
||||
dst[l1i] = unsafe { fixed::KERNEL_L1[l1i] };
|
||||
/// Removes the lower half translation mappings.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Needs to be called once after secondary HARTs are initialized.
|
||||
pub unsafe fn unmap_lower_half() {
|
||||
// for i in 0..(IDENTITY_SIZE / L1::SIZE) {
|
||||
// unsafe { FIXED_L1[i] = PageEntry::INVALID };
|
||||
// }
|
||||
// tlb_flush_full();
|
||||
}
|
||||
|
||||
pub fn tlb_flush_global_full() {
|
||||
tlb_flush_full();
|
||||
// TODO send TLB shootdown IPI to other harts
|
||||
}
|
||||
|
||||
pub fn tlb_flush_global_va(va: usize) {
|
||||
tlb_flush_va(va);
|
||||
// TODO send TLB shootdown IPI to other harts
|
||||
}
|
||||
|
||||
pub fn tlb_flush_range_va(start: usize, size: usize) {
|
||||
let end = (start + size).page_align_up::<L3>();
|
||||
let start = start.page_align_down::<L3>();
|
||||
|
||||
for page in (start..end).step_by(L3::SIZE) {
|
||||
tlb_flush_va(page);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tlb_flush_range_va_asid(asid: usize, start: usize, size: usize) {
|
||||
let end = (start + size).page_align_up::<L3>();
|
||||
let start = start.page_align_down::<L3>();
|
||||
|
||||
for page in (start..end).step_by(L3::SIZE) {
|
||||
tlb_flush_va_asid(page, asid);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_full() {
|
||||
unsafe { core::arch::asm!("sfence.vma") };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_va(va: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma {0}, zero", in(reg) va) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_asid(asid: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma zero, {0}", in(reg) asid) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_va_asid(va: usize, asid: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma {0}, {1}", in(reg) va, in(reg) asid) };
|
||||
}
|
||||
|
||||
pub fn clone_kernel_tables(dst: &mut PageTable<L1>) {
|
||||
// let tables = KERNEL_TABLES.lock();
|
||||
for l1i in page_index::<L1>(USER_BOUNDARY)..512 {
|
||||
// dst[l1i] = unsafe { PageEntry::from_raw(tables.l1.data[l1i]) };
|
||||
dst[l1i] = unsafe { FIXED_L1[l1i] };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,13 +13,10 @@ use libk_mm_interface::{
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::mem::{
|
||||
clone_kernel_tables,
|
||||
table::{PageAttributes, PageEntry},
|
||||
};
|
||||
use crate::mem::{clone_kernel_tables, table::PageEntry};
|
||||
|
||||
use super::{
|
||||
table::{DroppableRange, PageTable, L1, L2, L3},
|
||||
table::{DroppableRange, PageAttributes, PageTable, L1, L2, L3},
|
||||
KernelTableManagerImpl, USER_BOUNDARY,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use core::{
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
ops::{Index, IndexMut, Range},
|
||||
};
|
||||
@@ -18,6 +17,7 @@ use yggdrasil_abi::error::Error;
|
||||
|
||||
use super::{KernelTableManagerImpl, USER_BOUNDARY};
|
||||
|
||||
// pub use memtables::riscv64::PageAttributes;
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct PageAttributes: u64 {
|
||||
@@ -75,9 +75,10 @@ impl EntryLevel for L1 {
|
||||
const SHIFT: usize = 30;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C, align(0x1000))]
|
||||
pub struct PageTable<L: EntryLevel> {
|
||||
pub(crate) entries: [PageEntry<L>; 512],
|
||||
pub entries: [PageEntry<L>; 512],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
@@ -240,16 +241,14 @@ impl<L: NonTerminalEntryLevel + 'static> NextPageTable for PageTable<L> {
|
||||
|
||||
impl<L: NonTerminalEntryLevel> PageEntry<L> {
|
||||
pub const fn identity_block(address: PhysicalAddress) -> Self {
|
||||
Self(
|
||||
(address.into_u64() >> 2)
|
||||
| PageAttributes::R.bits()
|
||||
| PageAttributes::W.bits()
|
||||
| PageAttributes::X.bits()
|
||||
| PageAttributes::V.bits()
|
||||
| PageAttributes::D.bits()
|
||||
| PageAttributes::A.bits(),
|
||||
PhantomData,
|
||||
)
|
||||
const ATTR: u64 = PageAttributes::R.bits()
|
||||
| PageAttributes::W.bits()
|
||||
| PageAttributes::X.bits()
|
||||
| PageAttributes::A.bits()
|
||||
| PageAttributes::D.bits()
|
||||
| PageAttributes::V.bits();
|
||||
|
||||
Self((address.into_u64() >> 2) | ATTR, PhantomData)
|
||||
}
|
||||
|
||||
pub fn block(address: PhysicalAddress, attrs: PageAttributes) -> Self {
|
||||
@@ -318,26 +317,3 @@ impl<L: EntryLevel> IndexMut<usize> for PageTable<L> {
|
||||
&mut self.entries[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PageAttributes {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use fmt::Write;
|
||||
|
||||
macro_rules! bit {
|
||||
($self:ident, $field:expr, $letter:literal) => {
|
||||
if $self.contains($field) {
|
||||
f.write_char($letter)
|
||||
} else {
|
||||
f.write_char('-')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bit!(self, Self::R, 'r')?;
|
||||
bit!(self, Self::W, 'w')?;
|
||||
bit!(self, Self::X, 'x')?;
|
||||
bit!(self, Self::U, 'u')?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ cfg_if! {
|
||||
extern crate kernel_arch_aarch64 as imp;
|
||||
} else if #[cfg(target_arch = "x86_64")] {
|
||||
extern crate kernel_arch_x86_64 as imp;
|
||||
} else if #[cfg(target_arch = "x86")] {
|
||||
extern crate kernel_arch_i686 as imp;
|
||||
} else if #[cfg(target_arch = "riscv64")] {
|
||||
extern crate kernel_arch_riscv64 as imp;
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#[allow(dead_code)]
|
||||
#[repr(C, packed)]
|
||||
#[repr(packed)]
|
||||
pub struct Entry {
|
||||
pub limit_lo: u16,
|
||||
pub base_lo: u16,
|
||||
@@ -10,7 +10,7 @@ pub struct Entry {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(C, packed)]
|
||||
#[repr(packed)]
|
||||
pub struct Pointer {
|
||||
pub limit: u16,
|
||||
pub offset: usize,
|
||||
@@ -121,7 +121,7 @@ mod imp {
|
||||
use super::{Entry, Pointer};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(C, packed)]
|
||||
#[repr(packed)]
|
||||
pub struct Tss {
|
||||
_0: u32,
|
||||
rsp0: u64,
|
||||
|
||||
@@ -10,7 +10,7 @@ use tock_registers::interfaces::Writeable;
|
||||
use yggdrasil_abi::{arch::SavedFrame, error::Error};
|
||||
|
||||
use crate::{
|
||||
mem::{auto_lower_address, fixed},
|
||||
mem::{auto_lower_address, FIXED_PML4},
|
||||
ArchitectureImpl,
|
||||
};
|
||||
|
||||
@@ -434,7 +434,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
fn kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<Self, Error> {
|
||||
const KERNEL_TASK_PAGES: usize = 32;
|
||||
|
||||
let cr3: usize = auto_lower_address(&raw const fixed::KERNEL_PML4); // unsafe { KERNEL_TABLES.lock().as_physical_address() }.into();
|
||||
let cr3 = auto_lower_address(&raw const FIXED_PML4);
|
||||
|
||||
let stack_base_phys = PA::allocate_contiguous_pages(KERNEL_TASK_PAGES)?;
|
||||
let stack_base = stack_base_phys.raw_virtualize::<K>();
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
use core::ops::Range;
|
||||
|
||||
use kernel_arch_interface::mem::DeviceMemoryAttributes;
|
||||
use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
table::{DevicePageManager, DevicePageManagerLevel},
|
||||
};
|
||||
|
||||
use crate::mem::table::PageEntry;
|
||||
|
||||
use super::{
|
||||
table::{PageAttributes, PageTable, L2, L3},
|
||||
DEVICE_MAPPING_OFFSET, DEVICE_MEMORY_L3_COUNT,
|
||||
};
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct L2DeviceMemory(pub PageTable<L2>);
|
||||
#[repr(transparent)]
|
||||
pub struct L3DeviceMemory(pub [PageTable<L3>; DEVICE_MEMORY_L3_COUNT]);
|
||||
|
||||
pub(super) static mut DEVICE_MEMORY: DevicePageManager<L3DeviceMemory, L2DeviceMemory> =
|
||||
DevicePageManager::new(
|
||||
L3DeviceMemory([PageTable::zeroed(); DEVICE_MEMORY_L3_COUNT]),
|
||||
L2DeviceMemory(PageTable::zeroed()),
|
||||
);
|
||||
|
||||
impl DevicePageManagerLevel for L2DeviceMemory {
|
||||
type Level = L2;
|
||||
const VIRTUAL_BASE: usize = DEVICE_MAPPING_OFFSET;
|
||||
const INDEX_RANGE: Range<usize> = DEVICE_MEMORY_L3_COUNT..512;
|
||||
|
||||
fn map_page(
|
||||
&mut self,
|
||||
index: usize,
|
||||
physical: PhysicalAddress,
|
||||
attrs: &DeviceMemoryAttributes,
|
||||
) {
|
||||
let _ = attrs;
|
||||
self.0[index] = PageEntry::<L2>::block(physical, PageAttributes::WRITABLE);
|
||||
}
|
||||
|
||||
fn unmap_page(&mut self, index: usize) {
|
||||
self.0[index] = PageEntry::INVALID;
|
||||
}
|
||||
|
||||
fn is_mapped(&self, index: usize) -> bool {
|
||||
self.0[index].is_present()
|
||||
}
|
||||
|
||||
fn flush_range(range: Range<usize>) {
|
||||
let _ = range;
|
||||
}
|
||||
}
|
||||
|
||||
impl DevicePageManagerLevel for L3DeviceMemory {
|
||||
type Level = L3;
|
||||
const VIRTUAL_BASE: usize = DEVICE_MAPPING_OFFSET;
|
||||
const INDEX_RANGE: Range<usize> = 0..512 * DEVICE_MEMORY_L3_COUNT;
|
||||
|
||||
fn map_page(
|
||||
&mut self,
|
||||
index: usize,
|
||||
physical: PhysicalAddress,
|
||||
attrs: &DeviceMemoryAttributes,
|
||||
) {
|
||||
let _ = attrs;
|
||||
let l2i = index / 512;
|
||||
let l3i = index % 512;
|
||||
self.0[l2i][l3i] = PageEntry::page(physical, PageAttributes::WRITABLE);
|
||||
}
|
||||
|
||||
fn unmap_page(&mut self, index: usize) {
|
||||
let l2i = index / 512;
|
||||
let l3i = index % 512;
|
||||
self.0[l2i][l3i] = PageEntry::INVALID;
|
||||
}
|
||||
|
||||
fn is_mapped(&self, index: usize) -> bool {
|
||||
let l2i = index / 512;
|
||||
let l3i = index % 512;
|
||||
self.0[l2i][l3i].is_present()
|
||||
}
|
||||
|
||||
fn flush_range(range: Range<usize>) {
|
||||
let _ = range;
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
use core::ops::Range;
|
||||
|
||||
use kernel_arch_interface::{mem::DeviceMemoryAttributes, sync::IrqSafeSpinlock, Architecture};
|
||||
use kernel_arch_x86::registers::CR3;
|
||||
use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
device::{DevicePageManager, DevicePageTableLevel},
|
||||
table::{page_index, EntryLevel},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
mem::{
|
||||
auto_lower_address,
|
||||
table::{PageAttributes, PageEntry, PageTable, L0, L1, L2, L3},
|
||||
},
|
||||
ArchitectureImpl, KERNEL_VIRT_OFFSET,
|
||||
};
|
||||
|
||||
pub const IDENTITY_SIZE_L1: usize = 64;
|
||||
pub const KERNEL_L0I: usize = page_index::<L0>(KERNEL_VIRT_OFFSET);
|
||||
|
||||
pub const DEVICE_L1: usize = IDENTITY_SIZE_L1;
|
||||
pub const DEVICE_MAPPING_L3_COUNT: usize = 32;
|
||||
pub const DEVICE_MAPPING_OFFSET: usize = KERNEL_VIRT_OFFSET + (DEVICE_L1 << L1::SHIFT);
|
||||
|
||||
pub static LOCK: IrqSafeSpinlock<ArchitectureImpl, ()> = IrqSafeSpinlock::new(());
|
||||
pub static mut KERNEL_PDPT: PageTable<L1> = PageTable::zeroed();
|
||||
pub static mut KERNEL_PML4: PageTable<L0> = PageTable::zeroed();
|
||||
|
||||
pub(super) static mut DEVICE_MEMORY: DevicePageManager<L3DeviceMemory, L2DeviceMemory> =
|
||||
DevicePageManager::new(
|
||||
L3DeviceMemory([PageTable::zeroed(); DEVICE_MAPPING_L3_COUNT]),
|
||||
L2DeviceMemory(PageTable::zeroed()),
|
||||
);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct L2DeviceMemory(pub PageTable<L2>);
|
||||
#[repr(transparent)]
|
||||
pub struct L3DeviceMemory(pub [PageTable<L3>; DEVICE_MAPPING_L3_COUNT]);
|
||||
|
||||
impl DevicePageTableLevel for L2DeviceMemory {
|
||||
type Level = L2;
|
||||
const VIRTUAL_BASE: usize = DEVICE_MAPPING_OFFSET;
|
||||
const INDEX_RANGE: Range<usize> = DEVICE_MAPPING_L3_COUNT..512;
|
||||
|
||||
fn map_page(
|
||||
&mut self,
|
||||
index: usize,
|
||||
physical: PhysicalAddress,
|
||||
attrs: &DeviceMemoryAttributes,
|
||||
) {
|
||||
let _ = attrs;
|
||||
self.0[index] = PageEntry::<L2>::block(physical, PageAttributes::WRITABLE);
|
||||
}
|
||||
|
||||
fn unmap_page(&mut self, index: usize) {
|
||||
self.0[index - DEVICE_MAPPING_L3_COUNT] = PageEntry::INVALID;
|
||||
}
|
||||
|
||||
fn is_mapped(&self, index: usize) -> bool {
|
||||
self.0[index - DEVICE_MAPPING_L3_COUNT].is_present()
|
||||
}
|
||||
|
||||
fn flush_range(range: Range<usize>) {
|
||||
let _ = range;
|
||||
}
|
||||
}
|
||||
|
||||
impl DevicePageTableLevel for L3DeviceMemory {
|
||||
type Level = L3;
|
||||
const VIRTUAL_BASE: usize = DEVICE_MAPPING_OFFSET;
|
||||
const INDEX_RANGE: Range<usize> = 0..512 * DEVICE_MAPPING_L3_COUNT;
|
||||
|
||||
fn map_page(
|
||||
&mut self,
|
||||
index: usize,
|
||||
physical: PhysicalAddress,
|
||||
attrs: &DeviceMemoryAttributes,
|
||||
) {
|
||||
let _ = attrs;
|
||||
let l2i = index / 512;
|
||||
let l3i = index % 512;
|
||||
self.0[l2i][l3i] = PageEntry::page(physical, PageAttributes::WRITABLE);
|
||||
}
|
||||
|
||||
fn unmap_page(&mut self, index: usize) {
|
||||
let l2i = index / 512;
|
||||
let l3i = index % 512;
|
||||
self.0[l2i][l3i] = PageEntry::INVALID;
|
||||
}
|
||||
|
||||
fn is_mapped(&self, index: usize) -> bool {
|
||||
let l2i = index / 512;
|
||||
let l3i = index % 512;
|
||||
self.0[l2i][l3i].is_present()
|
||||
}
|
||||
|
||||
fn flush_range(range: Range<usize>) {
|
||||
let _ = range;
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe fn setup(have_1gib_pages: bool) {
|
||||
let phys = PhysicalAddress::from_usize(auto_lower_address(&raw const KERNEL_PDPT));
|
||||
KERNEL_PML4[KERNEL_L0I] = PageEntry::table(phys, PageAttributes::WRITABLE);
|
||||
|
||||
if have_1gib_pages {
|
||||
for i in 0..IDENTITY_SIZE_L1 {
|
||||
let phys = PhysicalAddress::from_usize(i * L1::SIZE);
|
||||
KERNEL_PDPT[i] = PageEntry::<L1>::block(phys, PageAttributes::WRITABLE);
|
||||
}
|
||||
} else {
|
||||
// TODO
|
||||
ArchitectureImpl::halt();
|
||||
}
|
||||
|
||||
// DEVICE_L1 -> Device L2 table
|
||||
// 0..DEVICE_MAPPING_L3_COUNT -> Device L3 tables -> Device L3 pages
|
||||
// ..512 -> Device L2 pages
|
||||
for i in 0..DEVICE_MAPPING_L3_COUNT {
|
||||
let phys =
|
||||
PhysicalAddress::from_usize(auto_lower_address(&raw const DEVICE_MEMORY.normal.0[i]));
|
||||
DEVICE_MEMORY.large.0[i] = PageEntry::table(phys, PageAttributes::WRITABLE);
|
||||
}
|
||||
let phys = PhysicalAddress::from_usize(auto_lower_address(&raw const DEVICE_MEMORY.large.0));
|
||||
KERNEL_PDPT[DEVICE_L1] = PageEntry::table(phys, PageAttributes::WRITABLE);
|
||||
}
|
||||
|
||||
pub(super) unsafe fn load() {
|
||||
CR3.set_address(auto_lower_address(&raw const KERNEL_PML4));
|
||||
}
|
||||
@@ -1,14 +1,19 @@
|
||||
use kernel_arch_interface::mem::{
|
||||
DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping,
|
||||
use kernel_arch_interface::{
|
||||
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
|
||||
sync::IrqSafeSpinlock,
|
||||
};
|
||||
use kernel_arch_x86::registers::CR3;
|
||||
use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
table::{EntryLevel, EntryLevelExt},
|
||||
};
|
||||
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::KERNEL_VIRT_OFFSET;
|
||||
use crate::{ArchitectureImpl, KERNEL_VIRT_OFFSET};
|
||||
|
||||
use self::table::{PageTable, L0, L1};
|
||||
use self::table::{PageAttributes, PageEntry, PageTable, L0, L1, L2};
|
||||
|
||||
pub mod fixed;
|
||||
pub mod device;
|
||||
pub mod process;
|
||||
pub mod table;
|
||||
|
||||
@@ -18,20 +23,17 @@ pub struct KernelTableManagerImpl;
|
||||
impl KernelTableManager for KernelTableManagerImpl {
|
||||
fn virtualize(address: u64) -> usize {
|
||||
let address = address as usize;
|
||||
if address < fixed::IDENTITY_SIZE_L1 * L1::SIZE {
|
||||
address + KERNEL_VIRT_OFFSET
|
||||
} else {
|
||||
panic!("Invalid physical address: {address:#x}");
|
||||
if address >= IDENTITY_SIZE {
|
||||
panic!("Invalid physical address to virtualize: {address:#x}");
|
||||
}
|
||||
address + KERNEL_VIRT_OFFSET
|
||||
}
|
||||
|
||||
fn physicalize(address: usize) -> u64 {
|
||||
if address < KERNEL_VIRT_OFFSET
|
||||
|| address - KERNEL_VIRT_OFFSET >= fixed::IDENTITY_SIZE_L1 * L1::SIZE
|
||||
{
|
||||
if address < KERNEL_VIRT_OFFSET || address - KERNEL_VIRT_OFFSET >= IDENTITY_SIZE {
|
||||
panic!("Invalid virtualized address: {address:#x}");
|
||||
}
|
||||
(address - KERNEL_VIRT_OFFSET) as u64
|
||||
(address - KERNEL_VIRT_OFFSET) as _
|
||||
}
|
||||
|
||||
unsafe fn map_device_pages(
|
||||
@@ -39,24 +41,41 @@ impl KernelTableManager for KernelTableManagerImpl {
|
||||
count: usize,
|
||||
attrs: DeviceMemoryAttributes,
|
||||
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
|
||||
let _lock = fixed::LOCK.lock();
|
||||
let _guard = DEVICE_MEMORY_LOCK.lock();
|
||||
#[allow(static_mut_refs)]
|
||||
fixed::DEVICE_MEMORY.map_device_pages(PhysicalAddress::from_u64(base), count, attrs)
|
||||
{
|
||||
device::DEVICE_MEMORY.map_device_pages(PhysicalAddress::from_u64(base), count, attrs)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
|
||||
let _lock = fixed::LOCK.lock();
|
||||
let _guard = DEVICE_MEMORY_LOCK.lock();
|
||||
#[allow(static_mut_refs)]
|
||||
fixed::DEVICE_MEMORY.unmap_device_pages(mapping);
|
||||
{
|
||||
device::DEVICE_MEMORY.unmap_device_pages(mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clone_kernel_tables(dst: &mut PageTable<L0>) {
|
||||
unsafe {
|
||||
dst[fixed::KERNEL_L0I] = fixed::KERNEL_PML4[fixed::KERNEL_L0I];
|
||||
let kernel_l0i = KERNEL_VIRT_OFFSET.page_index::<L0>();
|
||||
dst[kernel_l0i] = FIXED_PML4[kernel_l0i];
|
||||
}
|
||||
}
|
||||
|
||||
const FIXED_PD_COUNT: usize = 32;
|
||||
const IDENTITY_SIZE: usize = FIXED_PD_COUNT * L1::SIZE;
|
||||
|
||||
const DEVICE_MAPPING_L1I: usize = FIXED_PD_COUNT;
|
||||
const DEVICE_MEMORY_L3_COUNT: usize = 16;
|
||||
const DEVICE_MAPPING_OFFSET: usize = KERNEL_VIRT_OFFSET + IDENTITY_SIZE;
|
||||
|
||||
static DEVICE_MEMORY_LOCK: IrqSafeSpinlock<ArchitectureImpl, ()> = IrqSafeSpinlock::new(());
|
||||
static mut FIXED_PDS: [PageTable<L2>; FIXED_PD_COUNT] = [PageTable::zeroed(); FIXED_PD_COUNT];
|
||||
static mut FIXED_PDPT: PageTable<L1> = PageTable::zeroed();
|
||||
pub static mut FIXED_PML4: PageTable<L0> = PageTable::zeroed();
|
||||
|
||||
pub fn auto_lower_address<T>(pointer: *const T) -> usize {
|
||||
let address = pointer.addr();
|
||||
if address < KERNEL_VIRT_OFFSET {
|
||||
@@ -66,30 +85,80 @@ pub fn auto_lower_address<T>(pointer: *const T) -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets up the following memory map:
|
||||
/// ...: KERNEL_TABLES.l0:
|
||||
/// * 0xFFFFFF0000000000 .. 0xFFFFFFFF8000000000 : RAM_MAPPING_L1
|
||||
/// * 0xFFFFFF8000000000 .. ... : KERNEL_TABLES.kernel_l1:
|
||||
/// * 0xFFFFFF8000000000 .. 0xFFFFFF8040000000 : KERNEL_TABLES.kernel_l2
|
||||
/// * 0xFFFFFF8000000000 .. 0xFFFFFF8000200000 : ---
|
||||
/// * 0xFFFFFF8000200000 .. 0xFFFFFF8000400000 : EARLY_MAPPING_L3
|
||||
/// * 0xFFFFFF8000400000 .. ... : KERNEL_TABLES.kernel_l3s
|
||||
/// * 0xFFFFFF8040000000 .. 0xFFFFFF8080000000 : ---
|
||||
/// * 0xFFFFFF8080000000 .. 0xFFFFFF8100000000 : DEVICE_MAPPING_L2
|
||||
/// * 0xFFFFFF8080000000 .. 0xFFFFFF8080800000 : DEVICE_MAPPING_L3S
|
||||
/// * 0xFFFFFF8080800000 .. 0xFFFFFF8100000000 : ...
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe, must only be called by BSP during its early init, must already be in "higher-half"
|
||||
#[inline(never)]
|
||||
pub unsafe fn init_fixed_tables(have_1gib_pages: bool, bsp: bool) {
|
||||
fixed::setup(have_1gib_pages);
|
||||
if bsp {
|
||||
fixed::load();
|
||||
pub unsafe fn init_fixed_tables(have_1gib_pages: bool) {
|
||||
if have_1gib_pages {
|
||||
for i in 0..IDENTITY_SIZE / L1::SIZE {
|
||||
FIXED_PDPT[i] = PageEntry::<L1>::block(
|
||||
PhysicalAddress::from_usize(i * L1::SIZE),
|
||||
PageAttributes::WRITABLE,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for i in 0..IDENTITY_SIZE / L1::SIZE {
|
||||
for j in 0..512 {
|
||||
FIXED_PDS[i][j] = PageEntry::<L2>::block(
|
||||
PhysicalAddress::from_usize(i * L1::SIZE + j * L2::SIZE),
|
||||
PageAttributes::WRITABLE,
|
||||
);
|
||||
}
|
||||
let pd_physical =
|
||||
PhysicalAddress::from_usize(auto_lower_address(&raw const FIXED_PDS[i]));
|
||||
FIXED_PDPT[i] = PageEntry::table(pd_physical, PageAttributes::WRITABLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Device memory
|
||||
let device_pd_physical =
|
||||
PhysicalAddress::from_usize(auto_lower_address(&raw const device::DEVICE_MEMORY.large));
|
||||
|
||||
for i in 0..DEVICE_MEMORY_L3_COUNT {
|
||||
let device_pt_physical = PhysicalAddress::from_usize(auto_lower_address(
|
||||
&raw const device::DEVICE_MEMORY.normal.0[i],
|
||||
));
|
||||
device::DEVICE_MEMORY.large.0[i] =
|
||||
PageEntry::table(device_pt_physical, PageAttributes::WRITABLE);
|
||||
}
|
||||
|
||||
FIXED_PDPT[DEVICE_MAPPING_L1I] =
|
||||
PageEntry::<L1>::table(device_pd_physical, PageAttributes::WRITABLE);
|
||||
|
||||
let pdpt_physical = PhysicalAddress::from_usize(auto_lower_address(&raw const FIXED_PDPT));
|
||||
|
||||
FIXED_PML4[KERNEL_VIRT_OFFSET.page_index::<L0>()] =
|
||||
PageEntry::table(pdpt_physical, PageAttributes::WRITABLE);
|
||||
|
||||
let pml4_physical = auto_lower_address(&raw const FIXED_PML4);
|
||||
CR3.set_address(pml4_physical);
|
||||
}
|
||||
// let mut tables = KERNEL_TABLES.lock();
|
||||
//
|
||||
// // TODO this could be built in compile-time too?
|
||||
// let early_mapping_l3_phys = auto_address(&raw const EARLY_MAPPING_L3);
|
||||
// let device_mapping_l2_phys = auto_address(&raw const DEVICE_MAPPING_L2);
|
||||
// let ram_mapping_l1_phys = auto_address(&raw const RAM_MAPPING_L1);
|
||||
//
|
||||
// for i in 0..DEVICE_MAPPING_L3_COUNT {
|
||||
// let device_mapping_l3_phys =
|
||||
// PhysicalAddress::from_usize(auto_address(&raw const DEVICE_MAPPING_L3S[i]));
|
||||
// DEVICE_MAPPING_L2[i] = PageEntry::table(device_mapping_l3_phys, PageAttributes::WRITABLE);
|
||||
// }
|
||||
//
|
||||
// assert_eq!(tables.kernel_l2.data[EARLY_MAPPING_L2I], 0);
|
||||
// tables.kernel_l2.data[EARLY_MAPPING_L2I] = (early_mapping_l3_phys as u64)
|
||||
// | (PageAttributes::WRITABLE | PageAttributes::PRESENT).bits();
|
||||
//
|
||||
// assert_eq!(tables.kernel_l1.data[DEVICE_MAPPING_L1I], 0);
|
||||
// tables.kernel_l1.data[DEVICE_MAPPING_L1I] = (device_mapping_l2_phys as u64)
|
||||
// | (PageAttributes::WRITABLE | PageAttributes::PRESENT).bits();
|
||||
//
|
||||
// assert_eq!(tables.l0.data[RAM_MAPPING_L0I], 0);
|
||||
// tables.l0.data[RAM_MAPPING_L0I] =
|
||||
// (ram_mapping_l1_phys as u64) | (PageAttributes::WRITABLE | PageAttributes::PRESENT).bits();
|
||||
//
|
||||
// // TODO ENABLE EFER.NXE
|
||||
// let cr3 = auto_address(&raw const tables.l0);
|
||||
// }
|
||||
//
|
||||
/// # Safety
|
||||
///
|
||||
/// `address` must be page-aligned.
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@ fn build_x86_64() {
|
||||
const DEFAULT_8086_AS: &str = "nasm";
|
||||
const AP_BOOTSTRAP_S: &str = "src/arch/x86_64/boot/ap_boot.S";
|
||||
|
||||
println!("cargo:rerun-if-changed={AP_BOOTSTRAP_S}");
|
||||
println!("cargo:rerun-if-changed={}", AP_BOOTSTRAP_S);
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let assembler = env::var("AS8086").unwrap_or(DEFAULT_8086_AS.to_owned());
|
||||
@@ -35,7 +35,7 @@ fn build_x86_64() {
|
||||
|
||||
if !output.status.success() {
|
||||
io::stderr().write_all(&output.stderr).ok();
|
||||
panic!("{assembler}: could not assemble {AP_BOOTSTRAP_S}");
|
||||
panic!("{}: could not assemble {}", assembler, AP_BOOTSTRAP_S);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,6 @@ fn main() {
|
||||
"x86_64" => build_x86_64(),
|
||||
"aarch64" => (),
|
||||
"riscv64" => (),
|
||||
_ => panic!("Unknown target arch: {arch:?}"),
|
||||
_ => panic!("Unknown target arch: {:?}", arch),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,9 +35,6 @@ unsafe impl Allocator for AcpiAllocator {
|
||||
}
|
||||
|
||||
// TODO don't map memory as device if not necessary
|
||||
/// # Safety
|
||||
///
|
||||
/// Allows direct reads from physical memory, unsafe
|
||||
pub unsafe fn read_memory<T>(address: PhysicalAddress) -> T {
|
||||
let io =
|
||||
unsafe { DeviceMemoryMapping::map(address, size_of::<T>(), Default::default()).unwrap() };
|
||||
@@ -52,9 +49,6 @@ pub unsafe fn read_memory<T>(address: PhysicalAddress) -> T {
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Allows direct writes to physical memory, unsafe
|
||||
pub unsafe fn write_memory<T>(address: PhysicalAddress, value: T) {
|
||||
let io =
|
||||
unsafe { DeviceMemoryMapping::map(address, size_of::<T>(), Default::default()).unwrap() };
|
||||
|
||||
@@ -129,7 +129,7 @@ impl PrpList {
|
||||
list: None,
|
||||
}),
|
||||
_ => {
|
||||
let count = size.div_ceil(0x1000);
|
||||
let count = (size + 0xFFF) / 0x1000;
|
||||
let list =
|
||||
DmaBuffer::new_slice_with(dma, |i| base.add((i + 1) * 0x1000), count - 1)
|
||||
.map_err(NvmeError::MemoryError)?;
|
||||
|
||||
@@ -71,10 +71,10 @@ impl ScsiEnclosure {
|
||||
|
||||
// Probe LUNs
|
||||
for i in 0..lun_count {
|
||||
if this.probe_lun(i as u8).await
|
||||
&& let Ok(unit) = ScsiUnit::setup(this.clone(), i as u8).await
|
||||
{
|
||||
*this.units[i].write() = Some(unit);
|
||||
if this.probe_lun(i as u8).await {
|
||||
if let Ok(unit) = ScsiUnit::setup(this.clone(), i as u8).await {
|
||||
*this.units[i].write() = Some(unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,11 @@ impl ScsiEnclosure {
|
||||
attempts -= 1;
|
||||
}
|
||||
|
||||
attempts != 0
|
||||
if attempts == 0 {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
async fn poll(self: &Arc<Self>) {
|
||||
|
||||
@@ -44,7 +44,7 @@ impl ScsiTransportWrapper {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
let lba_bytes = (lba as u32).to_be_bytes();
|
||||
let lba_count = lba_count.to_be_bytes();
|
||||
let lba_count = (lba_count as u16).to_be_bytes();
|
||||
// Issue a READ (10) command
|
||||
let request_buffer = [
|
||||
0x28,
|
||||
|
||||
@@ -0,0 +1,613 @@
|
||||
//! 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
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(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
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());
|
||||
@@ -0,0 +1,141 @@
|
||||
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,20 +1,684 @@
|
||||
//! PCI/PCIe bus interfaces
|
||||
#![no_std]
|
||||
#![feature(let_chains, decl_macro)]
|
||||
#![allow(clippy::missing_transmute_annotations)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use device_api::device::Device;
|
||||
use libk_util::OneTimeInit;
|
||||
use core::fmt;
|
||||
|
||||
pub struct PciEndpointDevice {}
|
||||
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
||||
use acpi::mcfg::McfgEntry;
|
||||
use alloc::{format, sync::Arc, vec::Vec};
|
||||
|
||||
pub enum PciEndpointImpl {
|
||||
Device(OneTimeInit<PciEndpointDevice>),
|
||||
Bridge(PciBridge),
|
||||
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 struct PciBridge {}
|
||||
|
||||
pub struct PciEndpoint {
|
||||
pub imp: PciEndpointImpl,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// 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();
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
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);
|
||||
)+
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
//! 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 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
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();
|
||||
@@ -0,0 +1,394 @@
|
||||
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.map_or(false, |id| id == 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct UsbRoute {
|
||||
bus: u16,
|
||||
ports: [u8; 8],
|
||||
len: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct UsbBusAddress {
|
||||
pub bus: u16,
|
||||
pub device: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct UsbInterfaceAddress {
|
||||
pub device: UsbBusAddress,
|
||||
pub interface: u8,
|
||||
}
|
||||
|
||||
impl UsbBusAddress {
|
||||
pub fn with_interface(self, interface: u8) -> UsbInterfaceAddress {
|
||||
UsbInterfaceAddress {
|
||||
device: self,
|
||||
interface,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UsbBusAddress {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "<Bus {} Device {}>", self.bus, self.device)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UsbInterfaceAddress {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"<Bus {} Device {} Interface {}>",
|
||||
self.device.bus, self.device.device, self.interface
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UsbRoute {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}-", self.bus)?;
|
||||
for (i, &port) in self.ports[..self.len as usize].iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ".")?;
|
||||
}
|
||||
write!(f, "{port}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,56 @@
|
||||
use core::sync::atomic::{AtomicU16, Ordering};
|
||||
|
||||
use alloc::{collections::BTreeMap, sync::Arc};
|
||||
use libk_util::{queue::UnboundedMpmcQueue, sync::spin_rwlock::IrqSafeRwLock};
|
||||
use alloc::{collections::BTreeMap, format, sync::Arc};
|
||||
use libk_util::{queue::UnboundedMpmcQueue, sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
|
||||
|
||||
use crate::{
|
||||
address::UsbBusAddress,
|
||||
class_driver,
|
||||
device::{UsbBusAddress, UsbDeviceAccess},
|
||||
device::{UsbDevice, UsbDeviceAccess},
|
||||
sysfs::{self, UsbBusKObject},
|
||||
UsbHostController,
|
||||
};
|
||||
|
||||
pub struct UsbBusWrapper {
|
||||
pub(crate) hc: Arc<dyn UsbHostController>,
|
||||
pub(crate) index: u16,
|
||||
kobject: OneTimeInit<UsbBusKObject>,
|
||||
}
|
||||
|
||||
pub struct UsbBusManager {
|
||||
busses: IrqSafeRwLock<BTreeMap<u16, Arc<dyn UsbHostController>>>,
|
||||
busses: IrqSafeRwLock<BTreeMap<u16, Arc<UsbBusWrapper>>>,
|
||||
devices: IrqSafeRwLock<BTreeMap<UsbBusAddress, Arc<UsbDeviceAccess>>>,
|
||||
|
||||
last_bus_address: AtomicU16,
|
||||
}
|
||||
|
||||
impl UsbBusWrapper {
|
||||
pub fn kobject(&self) -> &UsbBusKObject {
|
||||
self.kobject.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbBusManager {
|
||||
pub fn register_bus(hc: Arc<dyn UsbHostController>) -> u16 {
|
||||
pub fn register_bus(hc: Arc<dyn UsbHostController>) -> (u16, Arc<UsbBusWrapper>) {
|
||||
let i = BUS_MANAGER.last_bus_address.fetch_add(1, Ordering::AcqRel);
|
||||
BUS_MANAGER.busses.write().insert(i, hc);
|
||||
i
|
||||
let wrapper = Arc::new(UsbBusWrapper {
|
||||
hc,
|
||||
index: i,
|
||||
kobject: OneTimeInit::new(),
|
||||
});
|
||||
BUS_MANAGER.busses.write().insert(i, wrapper.clone());
|
||||
wrapper.kobject.init(sysfs::register_bus_kobject(&wrapper));
|
||||
(i, wrapper)
|
||||
}
|
||||
|
||||
pub fn register_device(device: Arc<UsbDeviceAccess>) {
|
||||
log::info!("usb: register device {}", device.bus_address());
|
||||
|
||||
BUS_MANAGER
|
||||
.devices
|
||||
.write()
|
||||
.insert(device.bus_address(), device.clone());
|
||||
device.kobject.init(sysfs::register_device_kobject(&device));
|
||||
|
||||
QUEUE.push_back(device);
|
||||
}
|
||||
@@ -51,7 +73,11 @@ pub async fn bus_handler() {
|
||||
new_device.bus_address()
|
||||
);
|
||||
|
||||
class_driver::spawn_driver(new_device).await.ok();
|
||||
let address = new_device.bus_address();
|
||||
if let Err(error) = class_driver::setup_device(new_device).await {
|
||||
log::warn!("USB device {address} setup error: {error:?}",);
|
||||
}
|
||||
// class_driver::spawn_driver(new_device).await.ok();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,12 @@ use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent};
|
||||
|
||||
use crate::{device::UsbDeviceAccess, error::UsbError, info::UsbDeviceClass};
|
||||
|
||||
use super::{UsbClassInfo, UsbDriver};
|
||||
use crate::{
|
||||
class_driver::{UsbInterfaceClass, UsbInterfaceDriver},
|
||||
device::UsbDeviceAccess,
|
||||
error::UsbError,
|
||||
info::UsbInterfaceInfo,
|
||||
};
|
||||
|
||||
pub struct UsbHidKeyboardDriver;
|
||||
|
||||
@@ -125,10 +128,14 @@ impl KeyboardState {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UsbDriver for UsbHidKeyboardDriver {
|
||||
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
|
||||
impl UsbInterfaceDriver for UsbHidKeyboardDriver {
|
||||
async fn run(
|
||||
self: Arc<Self>,
|
||||
device: Arc<UsbDeviceAccess>,
|
||||
interface: UsbInterfaceInfo,
|
||||
) -> Result<(), UsbError> {
|
||||
// TODO not sure whether to use boot protocol (easy) or GetReport
|
||||
let config = device.select_configuration(|_| true).await?.unwrap();
|
||||
let config = device.current_configuration().unwrap();
|
||||
|
||||
log::info!("Setup HID keyboard");
|
||||
let pipe = device
|
||||
@@ -156,7 +163,7 @@ impl UsbDriver for UsbHidKeyboardDriver {
|
||||
|
||||
for &event in events {
|
||||
log::trace!("Generic Keyboard: {:?}", event);
|
||||
ygg_driver_input::send_event(event);
|
||||
ygg_driver_input::send_keyboard_event(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,12 +172,18 @@ impl UsbDriver for UsbHidKeyboardDriver {
|
||||
"USB HID Keyboard"
|
||||
}
|
||||
|
||||
fn probe(&self, class: &UsbClassInfo, _device: &UsbDeviceAccess) -> bool {
|
||||
log::info!(
|
||||
"class = {:?}, subclass = {:02x}",
|
||||
class.class,
|
||||
class.subclass
|
||||
);
|
||||
class.class == UsbDeviceClass::Hid && (class.subclass == 0x00 || class.subclass == 0x01)
|
||||
fn probe(
|
||||
&self,
|
||||
device: &UsbDeviceAccess,
|
||||
interface: &UsbInterfaceInfo,
|
||||
class: UsbInterfaceClass,
|
||||
) -> bool {
|
||||
let _ = (device, interface);
|
||||
class
|
||||
== UsbInterfaceClass {
|
||||
class: 3,
|
||||
subclass: 1,
|
||||
protocol: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use yggdrasil_abi::io::{ButtonMask, MouseEvent};
|
||||
|
||||
use crate::{
|
||||
class_driver::{UsbInterfaceClass, UsbInterfaceDriver},
|
||||
device::UsbDeviceAccess,
|
||||
error::UsbError,
|
||||
info::UsbInterfaceInfo,
|
||||
};
|
||||
|
||||
pub struct UsbHidMouseDriver;
|
||||
|
||||
#[async_trait]
|
||||
impl UsbInterfaceDriver for UsbHidMouseDriver {
|
||||
async fn run(
|
||||
self: Arc<Self>,
|
||||
device: Arc<UsbDeviceAccess>,
|
||||
interface: UsbInterfaceInfo,
|
||||
) -> Result<(), UsbError> {
|
||||
let config = device.current_configuration().unwrap();
|
||||
|
||||
log::info!("Setup HID mouse");
|
||||
let pipe = device
|
||||
.open_interrupt_in_pipe(1, config.endpoints[0].max_packet_size as u16)
|
||||
.await?;
|
||||
|
||||
let mut buffer = [0; 16];
|
||||
let mut button_state = 0;
|
||||
|
||||
loop {
|
||||
let len = pipe.read(&mut buffer).await?;
|
||||
if len < 4 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let event = MouseEvent {
|
||||
buttons: ButtonMask(buffer[0]),
|
||||
dx: (buffer[1] as i8) as i32,
|
||||
dy: (buffer[2] as i8) as i32,
|
||||
};
|
||||
|
||||
ygg_driver_input::send_mouse_event(event);
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"USB HID Mouse"
|
||||
}
|
||||
|
||||
fn probe(
|
||||
&self,
|
||||
device: &UsbDeviceAccess,
|
||||
interface: &UsbInterfaceInfo,
|
||||
class: UsbInterfaceClass,
|
||||
) -> bool {
|
||||
class
|
||||
== UsbInterfaceClass {
|
||||
class: 3,
|
||||
subclass: 1,
|
||||
protocol: 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,273 +1,273 @@
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use libk::{
|
||||
dma::{DmaBuffer, DmaSliceMut},
|
||||
error::Error,
|
||||
};
|
||||
use ygg_driver_scsi::{transport::ScsiTransport, ScsiEnclosure};
|
||||
|
||||
use crate::{
|
||||
communication::UsbDirection,
|
||||
device::{UsbDeviceAccess, UsbDeviceDetachHandler},
|
||||
error::UsbError,
|
||||
info::{UsbDeviceClass, UsbEndpointType},
|
||||
pipe::{
|
||||
control::{ControlTransferSetup, UsbClassSpecificRequest},
|
||||
normal::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{UsbClassInfo, UsbDriver};
|
||||
|
||||
pub struct UsbMassStorageDriverBulkOnly;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
struct Cbw {
|
||||
signature: u32, // 0x00
|
||||
tag: u32, // 0x04
|
||||
transfer_length: u32, // 0x08
|
||||
flags: u8, // 0x0C
|
||||
lun: u8, // 0x0D
|
||||
cb_length: u8, // 0x0E
|
||||
cb_data: [u8; 16], // 0x0F
|
||||
// Not sent
|
||||
_0: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
struct Csw {
|
||||
signature: u32,
|
||||
tag: u32,
|
||||
data_residue: u32,
|
||||
status: u8,
|
||||
_0: [u8; 3],
|
||||
}
|
||||
|
||||
struct Bbb {
|
||||
#[allow(unused)]
|
||||
device: Arc<UsbDeviceAccess>,
|
||||
in_pipe: UsbBulkInPipeAccess,
|
||||
out_pipe: UsbBulkOutPipeAccess,
|
||||
last_tag: u32,
|
||||
}
|
||||
|
||||
struct DetachHandler(Arc<ScsiEnclosure>);
|
||||
|
||||
impl Bbb {
|
||||
pub fn new(
|
||||
device: Arc<UsbDeviceAccess>,
|
||||
in_pipe: UsbBulkInPipeAccess,
|
||||
out_pipe: UsbBulkOutPipeAccess,
|
||||
) -> Result<Self, UsbError> {
|
||||
Ok(Self {
|
||||
device,
|
||||
in_pipe,
|
||||
out_pipe,
|
||||
last_tag: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Bbb {
|
||||
async fn send_cbw(
|
||||
&mut self,
|
||||
lun: u8,
|
||||
host_to_dev: bool,
|
||||
command: &[u8],
|
||||
response_len: usize,
|
||||
) -> Result<u32, Error> {
|
||||
self.last_tag = self.last_tag.wrapping_add(1);
|
||||
|
||||
let flags = if !host_to_dev { 1 << 7 } else { 0 };
|
||||
let tag = self.last_tag;
|
||||
let mut cbw_bytes = [0; 32];
|
||||
let cbw = bytemuck::from_bytes_mut::<Cbw>(&mut cbw_bytes);
|
||||
|
||||
cbw.signature = 0x43425355;
|
||||
cbw.transfer_length = response_len as u32;
|
||||
cbw.flags = flags;
|
||||
cbw.tag = tag;
|
||||
cbw.lun = lun;
|
||||
cbw.cb_length = command.len() as u8;
|
||||
cbw.cb_data[..command.len()].copy_from_slice(command);
|
||||
|
||||
self.out_pipe
|
||||
.write(&cbw_bytes[..31])
|
||||
.await
|
||||
.inspect_err(|error| log::error!("msc: CBW send error: {error:?}"))?;
|
||||
|
||||
Ok(tag)
|
||||
}
|
||||
|
||||
async fn read_csw(&mut self, tag: u32) -> Result<(), Error> {
|
||||
let mut csw_bytes = [0; 16];
|
||||
self.in_pipe
|
||||
.read_exact(&mut csw_bytes[..13])
|
||||
.await
|
||||
.inspect_err(|error| log::error!("msc: CSW receive error: {error:?}"))?;
|
||||
let csw = bytemuck::from_bytes::<Csw>(&csw_bytes);
|
||||
|
||||
if csw.signature != 0x53425355 {
|
||||
log::warn!("msc: invalid csw signature");
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
if csw.tag != tag {
|
||||
let csw_tag = csw.tag;
|
||||
log::warn!("msc: invalid csw tag (got {}, expected {tag})", csw_tag);
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
if csw.status != 0x00 {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn read_response_data(
|
||||
&mut self,
|
||||
buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
|
||||
) -> Result<usize, Error> {
|
||||
if buffer.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
let len = self
|
||||
.in_pipe
|
||||
.read_dma(buffer)
|
||||
.await
|
||||
.inspect_err(|error| log::error!("msc: DMA read error: {error:?}"))?;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ScsiTransport for Bbb {
|
||||
fn allocate_buffer(&self, size: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
|
||||
Ok(self.in_pipe.allocate_dma_buffer(size)?)
|
||||
}
|
||||
|
||||
async fn perform_request_raw(
|
||||
&mut self,
|
||||
lun: u8,
|
||||
request_data: &[u8],
|
||||
response_buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
|
||||
) -> Result<usize, Error> {
|
||||
if request_data.len() > 16 || response_buffer.len() > self.max_bytes_per_request() {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let tag = self
|
||||
.send_cbw(lun, false, request_data, response_buffer.len())
|
||||
.await?;
|
||||
let response_len = self.read_response_data(response_buffer).await?;
|
||||
self.read_csw(tag).await?;
|
||||
Ok(response_len)
|
||||
}
|
||||
|
||||
fn max_bytes_per_request(&self) -> usize {
|
||||
32768
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbDeviceDetachHandler for DetachHandler {
|
||||
fn handle_device_detach(&self) {
|
||||
log::info!("Mass storage detached");
|
||||
self.0.detach();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Pod, Zeroable, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct BulkOnlyMassStorageReset;
|
||||
|
||||
#[derive(Debug, Pod, Zeroable, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct GetMaxLun;
|
||||
|
||||
impl UsbClassSpecificRequest for BulkOnlyMassStorageReset {
|
||||
const BM_REQUEST_TYPE: u8 = 0b00100001;
|
||||
const B_REQUEST: u8 = 0b11111111;
|
||||
}
|
||||
|
||||
impl UsbClassSpecificRequest for GetMaxLun {
|
||||
const BM_REQUEST_TYPE: u8 = 0b10100001;
|
||||
const B_REQUEST: u8 = 0b11111110;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UsbDriver for UsbMassStorageDriverBulkOnly {
|
||||
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
|
||||
// TODO filter to only accept BBB config
|
||||
let config = device.select_configuration(|_| true).await?.unwrap();
|
||||
// Bulk-in, bulk-out
|
||||
assert_eq!(config.endpoints.len(), 2);
|
||||
let control_pipe = device.control_pipe();
|
||||
let (in_index, in_info) = config
|
||||
.find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::In))
|
||||
.ok_or(UsbError::InvalidConfiguration)?;
|
||||
let (out_index, out_info) = config
|
||||
.find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::Out))
|
||||
.ok_or(UsbError::InvalidConfiguration)?;
|
||||
let in_pipe = device
|
||||
.open_bulk_in_pipe(in_index, in_info.max_packet_size as u16)
|
||||
.await?;
|
||||
let out_pipe = device
|
||||
.open_bulk_out_pipe(out_index, out_info.max_packet_size as u16)
|
||||
.await?;
|
||||
|
||||
// Perform a Bulk-Only Mass Storage Reset
|
||||
// TODO interface id?
|
||||
control_pipe
|
||||
.control_transfer(ControlTransferSetup {
|
||||
bm_request_type: BulkOnlyMassStorageReset::BM_REQUEST_TYPE,
|
||||
b_request: BulkOnlyMassStorageReset::B_REQUEST,
|
||||
w_value: 0,
|
||||
w_index: 0,
|
||||
w_length: 0,
|
||||
})
|
||||
.await?;
|
||||
|
||||
// Get max LUN
|
||||
// TODO on devices which do not support multiple LUNs, this command may STALL
|
||||
let mut buffer = [MaybeUninit::uninit()];
|
||||
let len = control_pipe
|
||||
.control_transfer_in(
|
||||
ControlTransferSetup {
|
||||
bm_request_type: GetMaxLun::BM_REQUEST_TYPE,
|
||||
b_request: GetMaxLun::B_REQUEST,
|
||||
w_value: 0,
|
||||
w_index: 0,
|
||||
w_length: 1,
|
||||
},
|
||||
&mut buffer,
|
||||
)
|
||||
.await?;
|
||||
let max_lun = if len < 1 {
|
||||
0
|
||||
} else {
|
||||
unsafe { buffer[0].assume_init() }
|
||||
};
|
||||
|
||||
let bbb = Bbb::new(device.clone(), in_pipe, out_pipe)?;
|
||||
let scsi = ScsiEnclosure::setup(Box::new(bbb), max_lun as usize + 1)
|
||||
.await
|
||||
.inspect_err(|error| log::error!("msc: scsi error {error:?}"))
|
||||
.map_err(|_| UsbError::DriverError)?;
|
||||
let detach = DetachHandler(scsi.clone());
|
||||
device.set_detach_handler(Arc::new(detach));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"USB Mass Storage"
|
||||
}
|
||||
|
||||
fn probe(&self, class: &UsbClassInfo, _device: &UsbDeviceAccess) -> bool {
|
||||
// TODO support other protocols
|
||||
class.class == UsbDeviceClass::MassStorage && class.interface_protocol_number == 0x50
|
||||
}
|
||||
}
|
||||
// use core::mem::MaybeUninit;
|
||||
//
|
||||
// use alloc::{boxed::Box, sync::Arc};
|
||||
// use async_trait::async_trait;
|
||||
// use bytemuck::{Pod, Zeroable};
|
||||
// use libk::{
|
||||
// dma::{DmaBuffer, DmaSliceMut},
|
||||
// error::Error,
|
||||
// };
|
||||
// use ygg_driver_scsi::{transport::ScsiTransport, ScsiEnclosure};
|
||||
//
|
||||
// use crate::{
|
||||
// communication::UsbDirection,
|
||||
// device::{UsbDeviceAccess, UsbDeviceDetachHandler},
|
||||
// error::UsbError,
|
||||
// info::{UsbDeviceClass, UsbEndpointType},
|
||||
// pipe::{
|
||||
// control::{ControlTransferSetup, UsbClassSpecificRequest},
|
||||
// normal::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
|
||||
// },
|
||||
// };
|
||||
//
|
||||
// use super::{UsbClassInfo, UsbDriver};
|
||||
//
|
||||
// pub struct UsbMassStorageDriverBulkOnly;
|
||||
//
|
||||
// #[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
// #[repr(C)]
|
||||
// struct Cbw {
|
||||
// signature: u32, // 0x00
|
||||
// tag: u32, // 0x04
|
||||
// transfer_length: u32, // 0x08
|
||||
// flags: u8, // 0x0C
|
||||
// lun: u8, // 0x0D
|
||||
// cb_length: u8, // 0x0E
|
||||
// cb_data: [u8; 16], // 0x0F
|
||||
// // Not sent
|
||||
// _0: u8,
|
||||
// }
|
||||
//
|
||||
// #[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
// #[repr(C)]
|
||||
// struct Csw {
|
||||
// signature: u32,
|
||||
// tag: u32,
|
||||
// data_residue: u32,
|
||||
// status: u8,
|
||||
// _0: [u8; 3],
|
||||
// }
|
||||
//
|
||||
// struct Bbb {
|
||||
// #[allow(unused)]
|
||||
// device: Arc<UsbDeviceAccess>,
|
||||
// in_pipe: UsbBulkInPipeAccess,
|
||||
// out_pipe: UsbBulkOutPipeAccess,
|
||||
// last_tag: u32,
|
||||
// }
|
||||
//
|
||||
// struct DetachHandler(Arc<ScsiEnclosure>);
|
||||
//
|
||||
// impl Bbb {
|
||||
// pub fn new(
|
||||
// device: Arc<UsbDeviceAccess>,
|
||||
// in_pipe: UsbBulkInPipeAccess,
|
||||
// out_pipe: UsbBulkOutPipeAccess,
|
||||
// ) -> Result<Self, UsbError> {
|
||||
// Ok(Self {
|
||||
// device,
|
||||
// in_pipe,
|
||||
// out_pipe,
|
||||
// last_tag: 0,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Bbb {
|
||||
// async fn send_cbw(
|
||||
// &mut self,
|
||||
// lun: u8,
|
||||
// host_to_dev: bool,
|
||||
// command: &[u8],
|
||||
// response_len: usize,
|
||||
// ) -> Result<u32, Error> {
|
||||
// self.last_tag = self.last_tag.wrapping_add(1);
|
||||
//
|
||||
// let flags = if !host_to_dev { 1 << 7 } else { 0 };
|
||||
// let tag = self.last_tag;
|
||||
// let mut cbw_bytes = [0; 32];
|
||||
// let cbw = bytemuck::from_bytes_mut::<Cbw>(&mut cbw_bytes);
|
||||
//
|
||||
// cbw.signature = 0x43425355;
|
||||
// cbw.transfer_length = response_len as u32;
|
||||
// cbw.flags = flags;
|
||||
// cbw.tag = tag;
|
||||
// cbw.lun = lun;
|
||||
// cbw.cb_length = command.len() as u8;
|
||||
// cbw.cb_data[..command.len()].copy_from_slice(command);
|
||||
//
|
||||
// self.out_pipe
|
||||
// .write(&cbw_bytes[..31])
|
||||
// .await
|
||||
// .inspect_err(|error| log::error!("msc: CBW send error: {error:?}"))?;
|
||||
//
|
||||
// Ok(tag)
|
||||
// }
|
||||
//
|
||||
// async fn read_csw(&mut self, tag: u32) -> Result<(), Error> {
|
||||
// let mut csw_bytes = [0; 16];
|
||||
// self.in_pipe
|
||||
// .read_exact(&mut csw_bytes[..13])
|
||||
// .await
|
||||
// .inspect_err(|error| log::error!("msc: CSW receive error: {error:?}"))?;
|
||||
// let csw = bytemuck::from_bytes::<Csw>(&csw_bytes);
|
||||
//
|
||||
// if csw.signature != 0x53425355 {
|
||||
// log::warn!("msc: invalid csw signature");
|
||||
// return Err(Error::InvalidArgument);
|
||||
// }
|
||||
// if csw.tag != tag {
|
||||
// let csw_tag = csw.tag;
|
||||
// log::warn!("msc: invalid csw tag (got {}, expected {tag})", csw_tag);
|
||||
// return Err(Error::InvalidArgument);
|
||||
// }
|
||||
// if csw.status != 0x00 {
|
||||
// return Err(Error::InvalidArgument);
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
//
|
||||
// async fn read_response_data(
|
||||
// &mut self,
|
||||
// buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
|
||||
// ) -> Result<usize, Error> {
|
||||
// if buffer.len() == 0 {
|
||||
// return Ok(0);
|
||||
// }
|
||||
// let len = self
|
||||
// .in_pipe
|
||||
// .read_dma(buffer)
|
||||
// .await
|
||||
// .inspect_err(|error| log::error!("msc: DMA read error: {error:?}"))?;
|
||||
// Ok(len)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[async_trait]
|
||||
// impl ScsiTransport for Bbb {
|
||||
// fn allocate_buffer(&self, size: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
|
||||
// Ok(self.in_pipe.allocate_dma_buffer(size)?)
|
||||
// }
|
||||
//
|
||||
// async fn perform_request_raw(
|
||||
// &mut self,
|
||||
// lun: u8,
|
||||
// request_data: &[u8],
|
||||
// response_buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
|
||||
// ) -> Result<usize, Error> {
|
||||
// if request_data.len() > 16 || response_buffer.len() > self.max_bytes_per_request() {
|
||||
// return Err(Error::InvalidArgument);
|
||||
// }
|
||||
//
|
||||
// let tag = self
|
||||
// .send_cbw(lun, false, request_data, response_buffer.len())
|
||||
// .await?;
|
||||
// let response_len = self.read_response_data(response_buffer).await?;
|
||||
// self.read_csw(tag).await?;
|
||||
// Ok(response_len)
|
||||
// }
|
||||
//
|
||||
// fn max_bytes_per_request(&self) -> usize {
|
||||
// 32768
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl UsbDeviceDetachHandler for DetachHandler {
|
||||
// fn handle_device_detach(&self) {
|
||||
// log::info!("Mass storage detached");
|
||||
// self.0.detach();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[derive(Debug, Pod, Zeroable, Clone, Copy)]
|
||||
// #[repr(C)]
|
||||
// pub struct BulkOnlyMassStorageReset;
|
||||
//
|
||||
// #[derive(Debug, Pod, Zeroable, Clone, Copy)]
|
||||
// #[repr(C)]
|
||||
// pub struct GetMaxLun;
|
||||
//
|
||||
// impl UsbClassSpecificRequest for BulkOnlyMassStorageReset {
|
||||
// const BM_REQUEST_TYPE: u8 = 0b00100001;
|
||||
// const B_REQUEST: u8 = 0b11111111;
|
||||
// }
|
||||
//
|
||||
// impl UsbClassSpecificRequest for GetMaxLun {
|
||||
// const BM_REQUEST_TYPE: u8 = 0b10100001;
|
||||
// const B_REQUEST: u8 = 0b11111110;
|
||||
// }
|
||||
//
|
||||
// #[async_trait]
|
||||
// impl UsbDriver for UsbMassStorageDriverBulkOnly {
|
||||
// async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
|
||||
// // TODO filter to only accept BBB config
|
||||
// let config = device.select_configuration(|_| true).await?.unwrap();
|
||||
// // Bulk-in, bulk-out
|
||||
// assert_eq!(config.endpoints.len(), 2);
|
||||
// let control_pipe = device.control_pipe();
|
||||
// let (in_index, in_info) = config
|
||||
// .find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::In))
|
||||
// .ok_or(UsbError::InvalidConfiguration)?;
|
||||
// let (out_index, out_info) = config
|
||||
// .find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::Out))
|
||||
// .ok_or(UsbError::InvalidConfiguration)?;
|
||||
// let in_pipe = device
|
||||
// .open_bulk_in_pipe(in_index, in_info.max_packet_size as u16)
|
||||
// .await?;
|
||||
// let out_pipe = device
|
||||
// .open_bulk_out_pipe(out_index, out_info.max_packet_size as u16)
|
||||
// .await?;
|
||||
//
|
||||
// // Perform a Bulk-Only Mass Storage Reset
|
||||
// // TODO interface id?
|
||||
// control_pipe
|
||||
// .control_transfer(ControlTransferSetup {
|
||||
// bm_request_type: BulkOnlyMassStorageReset::BM_REQUEST_TYPE,
|
||||
// b_request: BulkOnlyMassStorageReset::B_REQUEST,
|
||||
// w_value: 0,
|
||||
// w_index: 0,
|
||||
// w_length: 0,
|
||||
// })
|
||||
// .await?;
|
||||
//
|
||||
// // Get max LUN
|
||||
// // TODO on devices which do not support multiple LUNs, this command may STALL
|
||||
// let mut buffer = [MaybeUninit::uninit()];
|
||||
// let len = control_pipe
|
||||
// .control_transfer_in(
|
||||
// ControlTransferSetup {
|
||||
// bm_request_type: GetMaxLun::BM_REQUEST_TYPE,
|
||||
// b_request: GetMaxLun::B_REQUEST,
|
||||
// w_value: 0,
|
||||
// w_index: 0,
|
||||
// w_length: 1,
|
||||
// },
|
||||
// &mut buffer,
|
||||
// )
|
||||
// .await?;
|
||||
// let max_lun = if len < 1 {
|
||||
// 0
|
||||
// } else {
|
||||
// unsafe { buffer[0].assume_init() }
|
||||
// };
|
||||
//
|
||||
// let bbb = Bbb::new(device.clone(), in_pipe, out_pipe)?;
|
||||
// let scsi = ScsiEnclosure::setup(Box::new(bbb), max_lun as usize + 1)
|
||||
// .await
|
||||
// .inspect_err(|error| log::error!("msc: scsi error {error:?}"))
|
||||
// .map_err(|_| UsbError::DriverError)?;
|
||||
// let detach = DetachHandler(scsi.clone());
|
||||
// device.set_detach_handler(Arc::new(detach));
|
||||
//
|
||||
// Ok(())
|
||||
// }
|
||||
//
|
||||
// fn name(&self) -> &'static str {
|
||||
// "USB Mass Storage"
|
||||
// }
|
||||
//
|
||||
// fn probe(&self, class: &UsbClassInfo, _device: &UsbDeviceAccess) -> bool {
|
||||
// // TODO support other protocols
|
||||
// class.class == UsbDeviceClass::MassStorage && class.interface_protocol_number == 0x50
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,117 +1,208 @@
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use async_trait::async_trait;
|
||||
use libk::task::runtime;
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
|
||||
use crate::{
|
||||
address::UsbInterfaceAddress,
|
||||
device::UsbDeviceAccess,
|
||||
error::UsbError,
|
||||
info::{UsbDeviceClass, UsbDeviceProtocol},
|
||||
info::{UsbInterfaceInfo, CLASS_FROM_INTERFACE},
|
||||
pipe::control::{ControlTransferSetup, UsbClassSpecificRequest},
|
||||
};
|
||||
|
||||
// use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
// use async_trait::async_trait;
|
||||
// use libk::task::runtime;
|
||||
// use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
//
|
||||
// use crate::{
|
||||
// device::UsbDeviceAccess,
|
||||
// error::UsbError,
|
||||
// info::{UsbDeviceClass, UsbDeviceProtocol},
|
||||
// };
|
||||
//
|
||||
pub mod hid_keyboard;
|
||||
pub mod mass_storage;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UsbClassInfo {
|
||||
pub class: UsbDeviceClass,
|
||||
pub mod hid_mouse;
|
||||
// pub mod mass_storage;
|
||||
//
|
||||
// #[derive(Debug)]
|
||||
// pub struct UsbClassInfo {
|
||||
// pub class: UsbDeviceClass,
|
||||
// pub subclass: u8,
|
||||
// pub protocol: UsbDeviceProtocol,
|
||||
// pub device_protocol_number: u8,
|
||||
// pub interface_protocol_number: u8,
|
||||
// }
|
||||
//
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct UsbInterfaceClass {
|
||||
pub class: u8,
|
||||
pub subclass: u8,
|
||||
pub protocol: UsbDeviceProtocol,
|
||||
pub device_protocol_number: u8,
|
||||
pub interface_protocol_number: u8,
|
||||
pub protocol: u8,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait UsbDriver: Send + Sync {
|
||||
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError>;
|
||||
pub trait UsbInterfaceDriver: Send + Sync {
|
||||
async fn run(
|
||||
self: Arc<Self>,
|
||||
device: Arc<UsbDeviceAccess>,
|
||||
interface: UsbInterfaceInfo,
|
||||
) -> Result<(), UsbError>;
|
||||
|
||||
fn name(&self) -> &'static str;
|
||||
fn probe(&self, class: &UsbClassInfo, device: &UsbDeviceAccess) -> bool;
|
||||
fn probe(
|
||||
&self,
|
||||
device: &UsbDeviceAccess,
|
||||
interface: &UsbInterfaceInfo,
|
||||
class: UsbInterfaceClass,
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
async fn extract_class_info(device: &UsbDeviceAccess) -> Result<Option<UsbClassInfo>, UsbError> {
|
||||
if device.info.num_configurations != 1 {
|
||||
return Ok(None);
|
||||
}
|
||||
let device_info = &device.info;
|
||||
let config_info = device.query_configuration_info(0).await?;
|
||||
// async fn extract_class_info(device: &UsbDeviceAccess) -> Result<Option<UsbClassInfo>, UsbError> {
|
||||
// if device.info.num_configurations != 1 {
|
||||
// return Ok(None);
|
||||
// }
|
||||
// let device_info = &device.info;
|
||||
// let config_info = device.query_configuration_info(0).await?;
|
||||
//
|
||||
// if config_info.interfaces.len() >= 1 {
|
||||
// let if_info = &config_info.interfaces[0];
|
||||
//
|
||||
// let class = if device_info.device_class == UsbDeviceClass::FromInterface {
|
||||
// if_info.interface_class
|
||||
// } else {
|
||||
// device_info.device_class
|
||||
// };
|
||||
// let subclass = if device_info.device_subclass == 0 {
|
||||
// if_info.interface_subclass
|
||||
// } else {
|
||||
// device_info.device_subclass
|
||||
// };
|
||||
// let protocol = if device_info.device_protocol == UsbDeviceProtocol::FromInterface {
|
||||
// if_info.interface_protocol
|
||||
// } else {
|
||||
// device_info.device_protocol
|
||||
// };
|
||||
//
|
||||
// Ok(Some(UsbClassInfo {
|
||||
// class,
|
||||
// subclass,
|
||||
// protocol,
|
||||
// interface_protocol_number: if_info.interface_protocol_number,
|
||||
// device_protocol_number: device_info.device_protocol_number,
|
||||
// }))
|
||||
// } else {
|
||||
// Ok(None)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// async fn pick_driver(
|
||||
// device: &UsbDeviceAccess,
|
||||
// ) -> Result<Option<Arc<dyn UsbDriver + 'static>>, UsbError> {
|
||||
// let Some(class) = extract_class_info(device).await? else {
|
||||
// return Ok(None);
|
||||
// };
|
||||
//
|
||||
// for driver in USB_DEVICE_DRIVERS.read().iter() {
|
||||
// if driver.probe(&class, device) {
|
||||
// return Ok(Some(driver.clone()));
|
||||
// }
|
||||
// }
|
||||
// Ok(None)
|
||||
// }
|
||||
//
|
||||
// pub async fn spawn_driver(device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
|
||||
// // if let Some(driver) = pick_driver(&device).await? {
|
||||
// // runtime::spawn(async move {
|
||||
// // let name = driver.name();
|
||||
// // match driver.run(device).await {
|
||||
// // e @ Err(UsbError::DeviceDisconnected) => {
|
||||
// // log::warn!(
|
||||
// // "Driver {:?} did not exit cleanly: device disconnected",
|
||||
// // name,
|
||||
// // );
|
||||
//
|
||||
// // e
|
||||
// // }
|
||||
// // e => e,
|
||||
// // }
|
||||
// // })
|
||||
// // .map_err(UsbError::SystemError)?;
|
||||
// // }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
if !config_info.interfaces.is_empty() {
|
||||
let if_info = &config_info.interfaces[0];
|
||||
|
||||
let class = if device_info.device_class == UsbDeviceClass::FromInterface {
|
||||
if_info.interface_class
|
||||
} else {
|
||||
device_info.device_class
|
||||
};
|
||||
let subclass = if device_info.device_subclass == 0 {
|
||||
if_info.interface_subclass
|
||||
} else {
|
||||
device_info.device_subclass
|
||||
};
|
||||
let protocol = if device_info.device_protocol == UsbDeviceProtocol::FromInterface {
|
||||
if_info.interface_protocol
|
||||
} else {
|
||||
device_info.device_protocol
|
||||
};
|
||||
|
||||
Ok(Some(UsbClassInfo {
|
||||
class,
|
||||
subclass,
|
||||
protocol,
|
||||
interface_protocol_number: if_info.interface_protocol_number,
|
||||
device_protocol_number: device_info.device_protocol_number,
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
async fn pick_driver(
|
||||
device: &UsbDeviceAccess,
|
||||
) -> Result<Option<Arc<dyn UsbDriver + 'static>>, UsbError> {
|
||||
let Some(class) = extract_class_info(device).await? else {
|
||||
return Ok(None);
|
||||
async fn setup_interface(
|
||||
device: &Arc<UsbDeviceAccess>,
|
||||
address: UsbInterfaceAddress,
|
||||
interface: &UsbInterfaceInfo,
|
||||
) -> Result<(), UsbError> {
|
||||
let class = UsbInterfaceClass {
|
||||
class: interface.interface_class,
|
||||
subclass: interface.interface_subclass,
|
||||
protocol: interface.interface_protocol,
|
||||
};
|
||||
|
||||
for driver in USB_DEVICE_DRIVERS.read().iter() {
|
||||
if driver.probe(&class, device) {
|
||||
return Ok(Some(driver.clone()));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub async fn spawn_driver(device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
|
||||
if let Some(driver) = pick_driver(&device).await? {
|
||||
runtime::spawn(async move {
|
||||
let name = driver.name();
|
||||
match driver.run(device).await {
|
||||
e @ Err(UsbError::DeviceDisconnected) => {
|
||||
log::warn!(
|
||||
"Driver {:?} did not exit cleanly: device disconnected",
|
||||
name,
|
||||
);
|
||||
|
||||
e
|
||||
let drivers = USB_INTERFACE_DRIVERS.read();
|
||||
for driver in drivers.iter() {
|
||||
if driver.probe(device, interface, class) {
|
||||
let driver = driver.clone();
|
||||
let device = device.clone();
|
||||
let interface = interface.clone();
|
||||
runtime::spawn(async move {
|
||||
let name = driver.name();
|
||||
match driver.run(device, interface).await {
|
||||
e @ Err(UsbError::DeviceDisconnected) => {
|
||||
log::warn!("{address} did not exit cleanly: device disconnected ({name})");
|
||||
e
|
||||
}
|
||||
e => e,
|
||||
}
|
||||
e => e,
|
||||
}
|
||||
})
|
||||
.map_err(UsbError::SystemError)?;
|
||||
})
|
||||
.map_err(UsbError::SystemError)?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn register_driver(driver: Arc<dyn UsbDriver + 'static>) {
|
||||
pub async fn setup_device(device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
|
||||
// If device has only one configuration available, use it
|
||||
// TODO support devices with multiple configurations
|
||||
let address = device.bus_address();
|
||||
log::info!("Setup USB device @ {address}");
|
||||
|
||||
let Some(config_info) = device.use_default_configuration().await? else {
|
||||
log::warn!("{address} has multiple configurations, not supported yet",);
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// Setup drivers for interfaces
|
||||
log::info!("{address}: {config_info:#?}");
|
||||
|
||||
// TODO device-level drivers
|
||||
for interface in config_info.interfaces.iter() {
|
||||
let address = address.with_interface(interface.number);
|
||||
if let Err(error) = setup_interface(&device, address, interface).await {
|
||||
log::error!("{}: {:?}", address, error);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn register_driver(driver: Arc<dyn UsbInterfaceDriver + 'static>) {
|
||||
// TODO check for duplicates
|
||||
USB_DEVICE_DRIVERS.write().push(driver);
|
||||
USB_INTERFACE_DRIVERS.write().push(driver);
|
||||
}
|
||||
|
||||
pub fn register_default_class_drivers() {
|
||||
register_driver(Arc::new(hid_keyboard::UsbHidKeyboardDriver));
|
||||
register_driver(Arc::new(mass_storage::UsbMassStorageDriverBulkOnly));
|
||||
register_driver(Arc::new(hid_mouse::UsbHidMouseDriver));
|
||||
// register_driver(Arc::new(mass_storage::UsbMassStorageDriverBulkOnly));
|
||||
}
|
||||
|
||||
static USB_DEVICE_DRIVERS: IrqSafeRwLock<Vec<Arc<dyn UsbDriver + 'static>>> =
|
||||
static USB_INTERFACE_DRIVERS: IrqSafeRwLock<Vec<Arc<dyn UsbInterfaceDriver + 'static>>> =
|
||||
IrqSafeRwLock::new(Vec::new());
|
||||
|
||||
@@ -2,9 +2,9 @@ use bytemuck::{Pod, Zeroable};
|
||||
|
||||
use crate::{
|
||||
communication::UsbDirection,
|
||||
device::UsbSpeed,
|
||||
device::{self, UsbSpeed},
|
||||
error::UsbError,
|
||||
info::{UsbDeviceClass, UsbDeviceProtocol, UsbEndpointType, UsbVersion},
|
||||
info::UsbEndpointType,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
|
||||
@@ -91,15 +91,15 @@ pub struct UsbOtherSpeedConfiguration {
|
||||
pub max_power: u8,
|
||||
}
|
||||
|
||||
impl UsbInterfaceDescriptor {
|
||||
pub fn class(&self) -> UsbDeviceClass {
|
||||
UsbDeviceClass::try_from(self.interface_class).unwrap_or(UsbDeviceClass::Unknown)
|
||||
}
|
||||
|
||||
pub fn protocol(&self) -> UsbDeviceProtocol {
|
||||
UsbDeviceProtocol::try_from(self.interface_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
|
||||
}
|
||||
}
|
||||
// impl UsbInterfaceDescriptor {
|
||||
// pub fn class(&self) -> UsbDeviceClass {
|
||||
// UsbDeviceClass::try_from(self.interface_class).unwrap_or(UsbDeviceClass::Unknown)
|
||||
// }
|
||||
//
|
||||
// pub fn protocol(&self) -> UsbDeviceProtocol {
|
||||
// UsbDeviceProtocol::try_from(self.interface_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl UsbEndpointDescriptor {
|
||||
pub fn direction(&self) -> UsbDirection {
|
||||
@@ -127,16 +127,16 @@ impl UsbEndpointDescriptor {
|
||||
}
|
||||
|
||||
impl UsbDeviceDescriptor {
|
||||
pub fn class(&self) -> UsbDeviceClass {
|
||||
UsbDeviceClass::try_from(self.device_class).unwrap_or(UsbDeviceClass::Unknown)
|
||||
}
|
||||
// pub fn class(&self) -> UsbDeviceClass {
|
||||
// UsbDeviceClass::try_from(self.device_class).unwrap_or(UsbDeviceClass::Unknown)
|
||||
// }
|
||||
|
||||
pub fn protocol(&self) -> UsbDeviceProtocol {
|
||||
UsbDeviceProtocol::try_from(self.device_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
|
||||
}
|
||||
// pub fn protocol(&self) -> UsbDeviceProtocol {
|
||||
// UsbDeviceProtocol::try_from(self.device_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
|
||||
// }
|
||||
|
||||
pub fn max_packet_size(&self, version: UsbVersion, speed: UsbSpeed) -> Result<usize, UsbError> {
|
||||
match (version.is_version_3(), speed, self.max_packet_size_0) {
|
||||
pub fn max_packet_size(&self, version: u16, speed: UsbSpeed) -> Result<usize, UsbError> {
|
||||
match (is_version_3(version), speed, self.max_packet_size_0) {
|
||||
(true, UsbSpeed::Super, 9) => Ok(1 << 9),
|
||||
(true, _, _) => todo!("Non-GenX speed USB3+ maxpacketsize0"),
|
||||
(false, _, 8) => Ok(8),
|
||||
@@ -147,3 +147,7 @@ impl UsbDeviceDescriptor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_version_3(version: u16) -> bool {
|
||||
version & 0xFF00 == 0x300
|
||||
}
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
use core::{fmt, ops::Deref};
|
||||
use core::{
|
||||
fmt,
|
||||
ops::{Deref, Sub},
|
||||
};
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
format,
|
||||
string::{String, ToString},
|
||||
sync::Arc,
|
||||
vec::Vec,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use libk_util::sync::spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard};
|
||||
use libk::error::Error;
|
||||
use libk_util::{
|
||||
sync::spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard},
|
||||
OneTimeInit,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
address::UsbBusAddress,
|
||||
bus::UsbBusWrapper,
|
||||
error::UsbError,
|
||||
info::{
|
||||
UsbConfigurationInfo, UsbDeviceInfo, UsbEndpointInfo, UsbEndpointType, UsbInterfaceInfo,
|
||||
UsbVersion,
|
||||
},
|
||||
pipe::{
|
||||
control::{ConfigurationDescriptorEntry, UsbControlPipeAccess},
|
||||
@@ -17,21 +31,26 @@ use crate::{
|
||||
UsbNormalPipeOut,
|
||||
},
|
||||
},
|
||||
sysfs::UsbDeviceKObject,
|
||||
UsbHostController,
|
||||
};
|
||||
|
||||
// High-level structures for info provided through descriptors
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub struct UsbBusAddress {
|
||||
pub bus: u16,
|
||||
pub device: u8,
|
||||
}
|
||||
// #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
// pub struct UsbBusAddress {
|
||||
// pub bus: u16,
|
||||
// pub device: u8,
|
||||
// }
|
||||
|
||||
pub struct UsbDeviceAccess {
|
||||
pub device: Arc<dyn UsbDevice>,
|
||||
pub bus: Arc<UsbBusWrapper>,
|
||||
pub info: UsbDeviceInfo,
|
||||
pub current_configuration: IrqSafeRwLock<Option<UsbConfigurationInfo>>,
|
||||
pub configurations: Vec<UsbConfigurationInfo>,
|
||||
current_configuration: IrqSafeRwLock<Option<usize>>,
|
||||
|
||||
pub(crate) kobject: OneTimeInit<UsbDeviceKObject>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
@@ -69,7 +88,7 @@ pub trait UsbDevice: Send + Sync {
|
||||
fn port_number(&self) -> u8;
|
||||
fn bus_address(&self) -> UsbBusAddress;
|
||||
fn speed(&self) -> UsbSpeed;
|
||||
fn controller_ref(&self) -> &dyn UsbHostController;
|
||||
fn host_controller(&self) -> Arc<dyn UsbHostController>;
|
||||
|
||||
fn set_detach_handler(&self, handler: Arc<dyn UsbDeviceDetachHandler>);
|
||||
fn handle_detach(&self);
|
||||
@@ -84,47 +103,43 @@ impl UsbDeviceAccess {
|
||||
/// * Device is not yet configured
|
||||
/// * Control pipe for the device has been properly set up
|
||||
/// * Device has been assigned a bus address
|
||||
pub async fn setup(raw: Arc<dyn UsbDevice>) -> Result<Self, UsbError> {
|
||||
pub async fn setup(bus: Arc<UsbBusWrapper>, raw: Arc<dyn UsbDevice>) -> Result<Self, UsbError> {
|
||||
let control = raw.control_pipe();
|
||||
|
||||
let device_desc = control.query_device_descriptor().await?;
|
||||
|
||||
let bcd_usb = device_desc.bcd_usb;
|
||||
let usb_version = UsbVersion::from_bcd_usb(device_desc.bcd_usb)
|
||||
.ok_or(UsbError::InvalidDescriptorField)
|
||||
.inspect_err(|_| {
|
||||
log::error!(
|
||||
"{}: unsupported/invalid USB version: {:#x}",
|
||||
raw.bus_address(),
|
||||
bcd_usb
|
||||
)
|
||||
})?;
|
||||
|
||||
let manufacturer = control.query_string(device_desc.manufacturer_str).await?;
|
||||
let product = control.query_string(device_desc.product_str).await?;
|
||||
|
||||
// Query device
|
||||
let info = UsbDeviceInfo {
|
||||
manufacturer,
|
||||
product,
|
||||
usb_version,
|
||||
usb_version: device_desc.bcd_usb,
|
||||
|
||||
id_vendor: device_desc.id_vendor,
|
||||
id_product: device_desc.id_product,
|
||||
|
||||
device_class: device_desc.class(),
|
||||
device_class: device_desc.device_class,
|
||||
device_subclass: device_desc.device_subclass,
|
||||
device_protocol: device_desc.protocol(),
|
||||
device_protocol_number: device_desc.device_protocol,
|
||||
device_protocol: device_desc.device_protocol,
|
||||
|
||||
num_configurations: device_desc.num_configurations,
|
||||
|
||||
max_packet_size: device_desc.max_packet_size(usb_version, raw.speed())?,
|
||||
max_packet_size: device_desc.max_packet_size(device_desc.bcd_usb, raw.speed())?,
|
||||
};
|
||||
|
||||
let configurations =
|
||||
Self::query_configurations(control, device_desc.num_configurations).await?;
|
||||
|
||||
Ok(Self {
|
||||
device: raw,
|
||||
bus,
|
||||
info,
|
||||
current_configuration: IrqSafeRwLock::new(None),
|
||||
configurations,
|
||||
|
||||
kobject: OneTimeInit::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -164,45 +179,54 @@ impl UsbDeviceAccess {
|
||||
Ok(UsbBulkOutPipeAccess(pipe))
|
||||
}
|
||||
|
||||
pub fn read_current_configuration(
|
||||
&self,
|
||||
) -> IrqSafeRwLockReadGuard<'_, Option<UsbConfigurationInfo>> {
|
||||
self.current_configuration.read()
|
||||
pub fn current_configuration(&self) -> Option<&UsbConfigurationInfo> {
|
||||
let index = (*self.current_configuration.read())?;
|
||||
Some(&self.configurations[index])
|
||||
}
|
||||
|
||||
pub async fn select_configuration<F: Fn(&UsbConfigurationInfo) -> bool>(
|
||||
pub async fn use_default_configuration(
|
||||
&self,
|
||||
predicate: F,
|
||||
) -> Result<Option<UsbConfigurationInfo>, UsbError> {
|
||||
let mut current_config = self.current_configuration.write();
|
||||
let control_pipe = self.control_pipe();
|
||||
|
||||
for i in 0..self.info.num_configurations {
|
||||
let info = self.query_configuration_info(i).await?;
|
||||
|
||||
if predicate(&info) {
|
||||
log::debug!("Selected configuration: {:#?}", info);
|
||||
let config = current_config.insert(info);
|
||||
|
||||
control_pipe
|
||||
.set_configuration(config.config_value as _)
|
||||
.await?;
|
||||
|
||||
return Ok(Some(config.clone()));
|
||||
}
|
||||
if self.configurations.len() != 1 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
self.set_configuration(0).await.map(Some)
|
||||
}
|
||||
|
||||
pub async fn query_configuration_info(
|
||||
&self,
|
||||
index: u8,
|
||||
) -> Result<UsbConfigurationInfo, UsbError> {
|
||||
if index >= self.info.num_configurations {
|
||||
pub async fn set_configuration(&self, index: usize) -> Result<UsbConfigurationInfo, UsbError> {
|
||||
if index >= self.configurations.len() {
|
||||
return Err(UsbError::InvalidConfiguration);
|
||||
}
|
||||
|
||||
let mut current = self.current_configuration.write();
|
||||
let control_pipe = self.control_pipe();
|
||||
let info = self.configurations[index].clone();
|
||||
|
||||
control_pipe
|
||||
.set_configuration(info.config_value as _)
|
||||
.await?;
|
||||
*current = Some(index);
|
||||
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
async fn query_configurations(
|
||||
control_pipe: &UsbControlPipeAccess,
|
||||
num_configurations: u8,
|
||||
) -> Result<Vec<UsbConfigurationInfo>, UsbError> {
|
||||
let mut configurations = Vec::new();
|
||||
for i in 0..num_configurations {
|
||||
let configuration = Self::query_configuration(control_pipe, i).await?;
|
||||
configurations.push(configuration);
|
||||
}
|
||||
Ok(configurations)
|
||||
}
|
||||
|
||||
async fn query_configuration(
|
||||
control_pipe: &UsbControlPipeAccess,
|
||||
index: u8,
|
||||
) -> Result<UsbConfigurationInfo, UsbError> {
|
||||
let query = control_pipe.query_configuration_descriptor(index).await?;
|
||||
|
||||
let configuration_name = control_pipe
|
||||
@@ -228,10 +252,9 @@ impl UsbDeviceAccess {
|
||||
name,
|
||||
number: iface.interface_number,
|
||||
|
||||
interface_class: iface.class(),
|
||||
interface_class: iface.interface_class,
|
||||
interface_subclass: iface.interface_subclass,
|
||||
interface_protocol: iface.protocol(),
|
||||
interface_protocol_number: iface.interface_protocol,
|
||||
interface_protocol: iface.interface_protocol,
|
||||
});
|
||||
}
|
||||
_ => (),
|
||||
@@ -248,6 +271,13 @@ impl UsbDeviceAccess {
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
// pub async fn query_configuration_info(
|
||||
// &self,
|
||||
// index: u8,
|
||||
// ) -> Result<UsbConfigurationInfo, UsbError> {
|
||||
// let control_pipe = self.control_pipe();
|
||||
// }
|
||||
|
||||
pub fn set_detach_handler(&self, handler: Arc<dyn UsbDeviceDetachHandler>) {
|
||||
self.device.set_detach_handler(handler);
|
||||
}
|
||||
@@ -260,9 +290,3 @@ impl Deref for UsbDeviceAccess {
|
||||
&*self.device
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UsbBusAddress {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.bus, self.device)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,41 +29,51 @@ pub enum UsbUsageType {
|
||||
Reserved,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub enum UsbVersion {
|
||||
Usb11,
|
||||
Usb20,
|
||||
Usb21,
|
||||
Usb30,
|
||||
Usb31,
|
||||
Usb32,
|
||||
}
|
||||
// #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
// pub enum UsbVersion {
|
||||
// Usb11,
|
||||
// Usb20,
|
||||
// Usb21,
|
||||
// Usb30,
|
||||
// Usb31,
|
||||
// Usb32,
|
||||
// }
|
||||
|
||||
primitive_enum! {
|
||||
pub enum UsbDeviceClass: u8 {
|
||||
FromInterface = 0x00,
|
||||
Hid = 0x03,
|
||||
MassStorage = 0x08,
|
||||
Unknown = 0xFF,
|
||||
}
|
||||
}
|
||||
pub const CLASS_FROM_INTERFACE: u8 = 0x00;
|
||||
pub const CLASS_HID: u8 = 0x03;
|
||||
pub const CLASS_MASS_STORAGE: u8 = 0x08;
|
||||
|
||||
primitive_enum! {
|
||||
pub enum UsbDeviceProtocol: u8 {
|
||||
FromInterface = 0x00,
|
||||
Unknown = 0xFF,
|
||||
}
|
||||
}
|
||||
// primitive_enum! {
|
||||
// pub enum UsbDeviceClass: u8 {
|
||||
// FromInterface = 0x00,
|
||||
// Hid = 0x03,
|
||||
// MassStorage = 0x08,
|
||||
// Unknown = 0xFF,
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// primitive_enum! {
|
||||
// pub enum UsbDeviceProtocol: u8 {
|
||||
// FromInterface = 0x00,
|
||||
// Unknown = 0xFF,
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UsbInterfaceInfo {
|
||||
pub name: String,
|
||||
pub number: u8,
|
||||
|
||||
pub interface_class: UsbDeviceClass,
|
||||
pub interface_class: u8,
|
||||
pub interface_subclass: u8,
|
||||
pub interface_protocol: UsbDeviceProtocol,
|
||||
pub interface_protocol_number: u8,
|
||||
pub interface_protocol: u8,
|
||||
// pub name: String,
|
||||
// pub number: u8,
|
||||
|
||||
// pub interface_class: UsbDeviceClass,
|
||||
// pub interface_subclass: u8,
|
||||
// pub interface_protocol: UsbDeviceProtocol,
|
||||
// pub interface_protocol_number: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -87,15 +97,14 @@ pub struct UsbDeviceInfo {
|
||||
pub manufacturer: String,
|
||||
pub product: String,
|
||||
|
||||
pub usb_version: UsbVersion,
|
||||
pub usb_version: u16,
|
||||
|
||||
pub id_vendor: u16,
|
||||
pub id_product: u16,
|
||||
|
||||
pub device_class: UsbDeviceClass,
|
||||
pub device_class: u8,
|
||||
pub device_subclass: u8,
|
||||
pub device_protocol: UsbDeviceProtocol,
|
||||
pub device_protocol_number: u8,
|
||||
pub device_protocol: u8,
|
||||
|
||||
/// Max packet size for endpoint zero
|
||||
pub max_packet_size: usize,
|
||||
@@ -103,37 +112,37 @@ pub struct UsbDeviceInfo {
|
||||
pub num_configurations: u8,
|
||||
}
|
||||
|
||||
impl UsbVersion {
|
||||
pub fn is_version_3(&self) -> bool {
|
||||
matches!(self, Self::Usb30 | Self::Usb31 | Self::Usb32)
|
||||
}
|
||||
|
||||
pub fn from_bcd_usb(value: u16) -> Option<Self> {
|
||||
match value {
|
||||
0x110 => Some(UsbVersion::Usb11),
|
||||
0x200..=0x20F => Some(UsbVersion::Usb20),
|
||||
0x210..=0x21F => Some(UsbVersion::Usb21),
|
||||
0x300 => Some(UsbVersion::Usb30),
|
||||
0x310 => Some(UsbVersion::Usb31),
|
||||
0x320 => Some(UsbVersion::Usb32),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UsbVersion {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let string = match self {
|
||||
Self::Usb11 => "USB1.1",
|
||||
Self::Usb20 => "USB2.0",
|
||||
Self::Usb21 => "USB2.1",
|
||||
Self::Usb30 => "USB3.0",
|
||||
Self::Usb31 => "USB3.1",
|
||||
Self::Usb32 => "USB3.2",
|
||||
};
|
||||
f.write_str(string)
|
||||
}
|
||||
}
|
||||
// impl UsbVersion {
|
||||
// pub fn is_version_3(&self) -> bool {
|
||||
// matches!(self, Self::Usb30 | Self::Usb31 | Self::Usb32)
|
||||
// }
|
||||
//
|
||||
// pub fn from_bcd_usb(value: u16) -> Option<Self> {
|
||||
// match value {
|
||||
// 0x110 => Some(UsbVersion::Usb11),
|
||||
// 0x200..=0x20F => Some(UsbVersion::Usb20),
|
||||
// 0x210..=0x21F => Some(UsbVersion::Usb21),
|
||||
// 0x300 => Some(UsbVersion::Usb30),
|
||||
// 0x310 => Some(UsbVersion::Usb31),
|
||||
// 0x320 => Some(UsbVersion::Usb32),
|
||||
// _ => None,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl fmt::Display for UsbVersion {
|
||||
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// let string = match self {
|
||||
// Self::Usb11 => "USB1.1",
|
||||
// Self::Usb20 => "USB2.0",
|
||||
// Self::Usb21 => "USB2.1",
|
||||
// Self::Usb30 => "USB3.0",
|
||||
// Self::Usb31 => "USB3.1",
|
||||
// Self::Usb32 => "USB3.2",
|
||||
// };
|
||||
// f.write_str(string)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl UsbEndpointInfo {
|
||||
pub fn is(&self, ty: UsbEndpointType, dir: UsbDirection) -> bool {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub struct UsbInterface {}
|
||||
@@ -8,15 +8,20 @@
|
||||
maybe_uninit_fill
|
||||
)]
|
||||
|
||||
use crate::sysfs::UsbBusKObject;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod address;
|
||||
pub mod bus;
|
||||
pub mod communication;
|
||||
pub mod descriptor;
|
||||
pub mod device;
|
||||
pub mod error;
|
||||
pub mod info;
|
||||
pub mod interface;
|
||||
pub mod pipe;
|
||||
pub mod sysfs;
|
||||
pub mod util;
|
||||
|
||||
pub mod class_driver;
|
||||
@@ -25,4 +30,8 @@ pub mod class_driver;
|
||||
|
||||
pub trait UsbEndpoint: Sync {}
|
||||
|
||||
pub trait UsbHostController: Sync + Send {}
|
||||
pub trait UsbHostController: Sync + Send {
|
||||
fn register_sysfs_properties(&self, kobject: &UsbBusKObject) {
|
||||
let _ = kobject;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ pub trait UsbDeviceRequest: Sized + Pod {
|
||||
pub trait UsbClassSpecificRequest: Sized + Pod {
|
||||
const BM_REQUEST_TYPE: u8;
|
||||
const B_REQUEST: u8;
|
||||
const W_VALUE: u16 = 0;
|
||||
}
|
||||
|
||||
pub trait UsbDescriptorRequest: UsbDeviceRequest {
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
use alloc::{format, sync::Arc};
|
||||
use libk::{
|
||||
error::Error,
|
||||
fs::sysfs::{
|
||||
self,
|
||||
attribute::{IntegerAttribute, IntegerAttributeFormat, IntegerAttributeOps},
|
||||
object::KObject,
|
||||
},
|
||||
};
|
||||
use libk_util::OneTimeInit;
|
||||
|
||||
use crate::{bus::UsbBusWrapper, device::UsbDeviceAccess};
|
||||
|
||||
pub type UsbBusKObject = Arc<KObject<Arc<UsbBusWrapper>>>;
|
||||
pub type UsbDeviceKObject = Arc<KObject<Arc<UsbDeviceAccess>>>;
|
||||
|
||||
pub(crate) fn register_bus_kobject(bus: &Arc<UsbBusWrapper>) -> UsbBusKObject {
|
||||
let root = sysfs_usb_root();
|
||||
let bus_kobject = KObject::new(bus.clone());
|
||||
bus.hc.register_sysfs_properties(&bus_kobject);
|
||||
root.add_object(format!("{}", bus.index), bus_kobject.clone())
|
||||
.ok();
|
||||
bus_kobject
|
||||
}
|
||||
|
||||
pub(crate) fn register_device_kobject(device: &Arc<UsbDeviceAccess>) -> UsbDeviceKObject {
|
||||
struct Class;
|
||||
struct Subclass;
|
||||
struct Protocol;
|
||||
struct Version;
|
||||
struct IdVendor;
|
||||
struct IdProduct;
|
||||
|
||||
impl IntegerAttributeOps<u8> for Class {
|
||||
type Data = Arc<UsbDeviceAccess>;
|
||||
const NAME: &'static str = "class";
|
||||
const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Hex;
|
||||
|
||||
fn read(state: &Self::Data) -> Result<u8, Error> {
|
||||
Ok(state.info.device_class)
|
||||
}
|
||||
}
|
||||
impl IntegerAttributeOps<u8> for Subclass {
|
||||
type Data = Arc<UsbDeviceAccess>;
|
||||
const NAME: &'static str = "subclass";
|
||||
const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Hex;
|
||||
|
||||
fn read(state: &Self::Data) -> Result<u8, Error> {
|
||||
Ok(state.info.device_subclass)
|
||||
}
|
||||
}
|
||||
impl IntegerAttributeOps<u8> for Protocol {
|
||||
type Data = Arc<UsbDeviceAccess>;
|
||||
const NAME: &'static str = "protocol";
|
||||
const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Hex;
|
||||
|
||||
fn read(state: &Self::Data) -> Result<u8, Error> {
|
||||
Ok(state.info.device_protocol)
|
||||
}
|
||||
}
|
||||
impl IntegerAttributeOps<u16> for Version {
|
||||
type Data = Arc<UsbDeviceAccess>;
|
||||
const NAME: &'static str = "version";
|
||||
const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Hex;
|
||||
|
||||
fn read(state: &Self::Data) -> Result<u16, Error> {
|
||||
Ok(state.info.usb_version)
|
||||
}
|
||||
}
|
||||
impl IntegerAttributeOps<u16> for IdVendor {
|
||||
type Data = Arc<UsbDeviceAccess>;
|
||||
const NAME: &'static str = "vendor";
|
||||
const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Hex;
|
||||
|
||||
fn read(state: &Self::Data) -> Result<u16, Error> {
|
||||
Ok(state.info.id_vendor)
|
||||
}
|
||||
}
|
||||
impl IntegerAttributeOps<u16> for IdProduct {
|
||||
type Data = Arc<UsbDeviceAccess>;
|
||||
const NAME: &'static str = "product";
|
||||
const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Hex;
|
||||
|
||||
fn read(state: &Self::Data) -> Result<u16, Error> {
|
||||
Ok(state.info.id_product)
|
||||
}
|
||||
}
|
||||
|
||||
let bus_kobject = device.bus.kobject();
|
||||
let device_kobject = KObject::new(device.clone());
|
||||
|
||||
device_kobject
|
||||
.add_attribute(IntegerAttribute::from(Class))
|
||||
.ok();
|
||||
device_kobject
|
||||
.add_attribute(IntegerAttribute::from(Subclass))
|
||||
.ok();
|
||||
device_kobject
|
||||
.add_attribute(IntegerAttribute::from(Protocol))
|
||||
.ok();
|
||||
device_kobject
|
||||
.add_attribute(IntegerAttribute::from(Version))
|
||||
.ok();
|
||||
device_kobject
|
||||
.add_attribute(IntegerAttribute::from(IdVendor))
|
||||
.ok();
|
||||
device_kobject
|
||||
.add_attribute(IntegerAttribute::from(IdProduct))
|
||||
.ok();
|
||||
|
||||
let address = device.bus_address();
|
||||
|
||||
bus_kobject
|
||||
.add_object(format!("{}", address.device), device_kobject.clone())
|
||||
.ok();
|
||||
|
||||
device_kobject
|
||||
}
|
||||
|
||||
fn sysfs_usb_root() -> &'static Arc<KObject<()>> {
|
||||
static USB_ROOT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
||||
|
||||
USB_ROOT.or_init_with(|| {
|
||||
let bus_object = sysfs::bus().expect("bus object");
|
||||
let usb_object = KObject::new(());
|
||||
bus_object.add_object("usb", usb_object.clone()).ok();
|
||||
usb_object
|
||||
})
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#![feature(if_let_guard, impl_trait_in_assoc_type)]
|
||||
#![feature(if_let_guard, async_drop, impl_trait_in_assoc_type)]
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
#![allow(clippy::new_ret_no_self)]
|
||||
#![allow(clippy::new_ret_no_self, incomplete_features)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
|
||||
@@ -160,7 +160,8 @@ impl Fat32FsInfo {
|
||||
let signature0 = &self.bytes[0..4];
|
||||
let signature1 = &self.bytes[484..488];
|
||||
|
||||
signature0 == Self::SIGNATURE0.to_le_bytes() && signature1 == Self::SIGNATURE1.to_le_bytes()
|
||||
signature0 == &Self::SIGNATURE0.to_le_bytes()
|
||||
&& signature1 == &Self::SIGNATURE1.to_le_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -303,7 +303,7 @@ impl CommonImpl for DirectoryNode {
|
||||
}
|
||||
|
||||
fn metadata(&self, _node: &NodeRef) -> Result<Metadata, Error> {
|
||||
Ok(self.metadata)
|
||||
Ok(self.metadata.clone())
|
||||
}
|
||||
|
||||
fn set_metadata(&self, _node: &NodeRef, _metadata: &Metadata) -> Result<(), Error> {
|
||||
@@ -357,7 +357,7 @@ impl DirectoryImpl for DirectoryNode {
|
||||
}
|
||||
|
||||
fn len(&self, _node: &NodeRef) -> Result<usize, Error> {
|
||||
Ok((self.size_bytes.unwrap_or(0) / DIRENT_SIZE as u32) as usize)
|
||||
Ok((self.size_bytes.unwrap_or(0) as u32 / DIRENT_SIZE as u32) as usize)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@ impl Fat32Fs {
|
||||
) -> Result<NodeRef, Error> {
|
||||
let mut cached = true;
|
||||
for option in options {
|
||||
#[allow(clippy::single_match)]
|
||||
match option {
|
||||
FilesystemMountOption::Sync => cached = false,
|
||||
_ => (),
|
||||
|
||||
@@ -16,7 +16,7 @@ pub(crate) struct TarIterator<'a> {
|
||||
zero_blocks: usize,
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[repr(packed)]
|
||||
pub(crate) struct TarEntry {
|
||||
pub name: TarString<100>,
|
||||
pub mode: OctalField<8>,
|
||||
|
||||
@@ -9,14 +9,20 @@ use async_trait::async_trait;
|
||||
use device_api::device::Device;
|
||||
use libk::{device::char::CharDevice, vfs::FileReadiness};
|
||||
use libk_util::{ring::LossyRingQueue, OneTimeInit};
|
||||
use yggdrasil_abi::{error::Error, io::KeyboardKeyEvent};
|
||||
use yggdrasil_abi::{
|
||||
abi_serde::wire,
|
||||
error::Error,
|
||||
io::{KeyboardKeyEvent, MouseEvent},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct KeyboardDevice;
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct MouseDevice;
|
||||
|
||||
impl FileReadiness for KeyboardDevice {
|
||||
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
|
||||
INPUT_QUEUE.poll_readable(cx).map(Ok)
|
||||
KEYBOARD_INPUT_QUEUE.poll_readable(cx).map(Ok)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +39,7 @@ impl CharDevice for KeyboardDevice {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let ev = INPUT_QUEUE.read().await;
|
||||
let ev = KEYBOARD_INPUT_QUEUE.read().await;
|
||||
|
||||
buf[..4].copy_from_slice(&ev.as_bytes());
|
||||
|
||||
@@ -45,7 +51,7 @@ impl CharDevice for KeyboardDevice {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let ev = INPUT_QUEUE.try_read().ok_or(Error::WouldBlock)?;
|
||||
let ev = KEYBOARD_INPUT_QUEUE.try_read().ok_or(Error::WouldBlock)?;
|
||||
|
||||
buf[..4].copy_from_slice(&ev.as_bytes());
|
||||
|
||||
@@ -68,15 +74,68 @@ impl CharDevice for KeyboardDevice {
|
||||
}
|
||||
}
|
||||
|
||||
static INPUT_QUEUE: LossyRingQueue<KeyboardKeyEvent> = LossyRingQueue::with_capacity(32);
|
||||
impl FileReadiness for MouseDevice {
|
||||
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
|
||||
MOUSE_INPUT_QUEUE.poll_readable(cx).map(Ok)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for MouseDevice {
|
||||
fn display_name(&self) -> &str {
|
||||
"Mouse input pseudo-device"
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CharDevice for MouseDevice {
|
||||
async fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
let ev = MOUSE_INPUT_QUEUE.read().await;
|
||||
let len = wire::to_slice(&ev, buf)?;
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn read_nonblocking(&self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
let ev = MOUSE_INPUT_QUEUE.try_read().ok_or(Error::WouldBlock)?;
|
||||
let len = wire::to_slice(&ev, buf)?;
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn is_writeable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
|
||||
let _ = option;
|
||||
let _ = buffer;
|
||||
let _ = len;
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
|
||||
fn is_terminal(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
static KEYBOARD_INPUT_QUEUE: LossyRingQueue<KeyboardKeyEvent> = LossyRingQueue::with_capacity(32);
|
||||
static KEYBOARD_DEVICE: OneTimeInit<Arc<KeyboardDevice>> = OneTimeInit::new();
|
||||
|
||||
pub fn setup() -> Arc<KeyboardDevice> {
|
||||
static MOUSE_INPUT_QUEUE: LossyRingQueue<MouseEvent> = LossyRingQueue::with_capacity(32);
|
||||
static MOUSE_DEVICE: OneTimeInit<Arc<MouseDevice>> = OneTimeInit::new();
|
||||
|
||||
pub fn setup_keyboard() -> Arc<KeyboardDevice> {
|
||||
KEYBOARD_DEVICE
|
||||
.or_init_with(|| Arc::new(KeyboardDevice))
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub fn send_event(ev: KeyboardKeyEvent) {
|
||||
INPUT_QUEUE.write(ev);
|
||||
pub fn setup_mouse() -> Arc<MouseDevice> {
|
||||
MOUSE_DEVICE.or_init_with(|| Arc::new(MouseDevice)).clone()
|
||||
}
|
||||
|
||||
pub fn send_keyboard_event(ev: KeyboardKeyEvent) {
|
||||
KEYBOARD_INPUT_QUEUE.write(ev);
|
||||
}
|
||||
|
||||
pub fn send_mouse_event(ev: MouseEvent) {
|
||||
MOUSE_INPUT_QUEUE.write(ev);
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ pub fn register_interface(
|
||||
NetworkInterfaceType::Ethernet => {
|
||||
static LAST_ETHERNET_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
let eth_id = LAST_ETHERNET_ID.fetch_add(1, Ordering::SeqCst);
|
||||
format!("eth{eth_id}").into_boxed_str()
|
||||
format!("eth{}", eth_id).into_boxed_str()
|
||||
}
|
||||
NetworkInterfaceType::Loopback => "lo".into(),
|
||||
};
|
||||
|
||||
@@ -133,7 +133,7 @@ impl Route {
|
||||
|
||||
pub fn insert(route: Self) -> Result<(), Error> {
|
||||
// TODO check for conflicts
|
||||
log::debug!("Add route: {route}");
|
||||
log::debug!("Add route: {}", route);
|
||||
ROUTES.write().push(route);
|
||||
Ok(())
|
||||
}
|
||||
@@ -143,7 +143,7 @@ impl fmt::Display for Route {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{} ", self.subnet)?;
|
||||
if let Some(gw) = self.gateway {
|
||||
write!(f, " via {gw}")?;
|
||||
write!(f, " via {}", gw)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ fn describe_route(route: &Route) -> RouteInfo {
|
||||
interface_name: interface.name.clone(),
|
||||
interface_id: route.interface,
|
||||
subnet: route.subnet,
|
||||
gateway: route.gateway,
|
||||
gateway: route.gateway.map(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,9 +110,9 @@ impl LocalPacketSocket {
|
||||
///
|
||||
/// 1. If this socket is bound to a name, the name will be used
|
||||
/// 2. Otherwise:
|
||||
/// 2.1. If `remote` is "known" by this socket, the address in the table will be used
|
||||
/// 2.2. Otherwise, this socket is "paired" with the remote by generating a new anonymous
|
||||
/// address unique to that socket
|
||||
/// 2.1. If `remote` is "known" by this socket, the address in the table will be used
|
||||
/// 2.2. Otherwise, this socket is "paired" with the remote by generating a new anonymous
|
||||
/// address unique to that socket
|
||||
fn get_send_info(
|
||||
self: &Arc<Self>,
|
||||
remote: &OwnedAddress,
|
||||
|
||||
@@ -81,7 +81,7 @@ impl RawSocket {
|
||||
};
|
||||
|
||||
for id in ids {
|
||||
if except == Some(*id) {
|
||||
if except.map_or(false, |i| i == *id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ impl Socket for RawSocket {
|
||||
if message.payload.len() > 1024 {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
let mut builder = TxPacketBuilder::new(&interface, message.payload.len())?;
|
||||
let mut builder = TxPacketBuilder::new(&*interface, message.payload.len())?;
|
||||
builder.push_bytes(message.payload)?;
|
||||
// false to prevent loopback
|
||||
builder.transmit(Some(self.id))?;
|
||||
|
||||
@@ -320,14 +320,14 @@ impl InterruptHandler for Rtl8139 {
|
||||
let rx_len_1 = unsafe { rx.buffer[rx_pos + 3].assume_init() };
|
||||
let rx_len = u16::from_le_bytes([rx_len_0, rx_len_1]) as usize;
|
||||
|
||||
if rx_len >= 16
|
||||
&& let Ok(mut packet_buf) = DmaBuffer::new_uninit_slice(&*self.dma, rx_len)
|
||||
{
|
||||
packet_buf.copy_from_slice(&rx.buffer[rx_pos + 4..rx_pos + rx_len + 4]);
|
||||
let packet_buf = unsafe { DmaBuffer::assume_init_slice(packet_buf) };
|
||||
// let packet_buf = unsafe { packet_buf.assume_init_slice() };
|
||||
let packet = RxPacket::new(packet_buf, 0, nic);
|
||||
ygg_driver_net_core::receive_packet(packet).ok();
|
||||
if rx_len >= 16 {
|
||||
if let Ok(mut packet_buf) = DmaBuffer::new_uninit_slice(&*self.dma, rx_len) {
|
||||
packet_buf.copy_from_slice(&rx.buffer[rx_pos + 4..rx_pos + rx_len + 4]);
|
||||
let packet_buf = unsafe { DmaBuffer::assume_init_slice(packet_buf) };
|
||||
// let packet_buf = unsafe { packet_buf.assume_init_slice() };
|
||||
let packet = RxPacket::new(packet_buf, 0, nic);
|
||||
ygg_driver_net_core::receive_packet(packet).ok();
|
||||
}
|
||||
}
|
||||
|
||||
// rx_len + 4, aligned to 4 bytes
|
||||
|
||||
@@ -197,7 +197,7 @@ impl RxRing {
|
||||
let index = self.rd % self.entries.len();
|
||||
let entry = &mut self.entries[index];
|
||||
|
||||
if entry.rx_completed().is_some() {
|
||||
if let Some(_) = entry.rx_completed() {
|
||||
// Grab the current buffer (the one just written to by the DMA), replace it
|
||||
// with the newly allocated one, and mark the descriptor as DMA-owned again
|
||||
let new_buffer = DmaBuffer::new_uninit_slice(dma, 4096)?;
|
||||
|
||||
@@ -25,10 +25,11 @@ use tock_registers::{
|
||||
};
|
||||
use ygg_driver_pci::{device::PciDeviceInfo, PciConfigurationSpace};
|
||||
use ygg_driver_usb::{
|
||||
bus::UsbBusManager,
|
||||
device::{UsbBusAddress, UsbDeviceAccess, UsbSpeed},
|
||||
address::UsbBusAddress,
|
||||
bus::{UsbBusManager, UsbBusWrapper},
|
||||
descriptor,
|
||||
device::{UsbDeviceAccess, UsbSpeed},
|
||||
error::UsbError,
|
||||
info::UsbVersion,
|
||||
pipe::control::UsbControlPipeAccess,
|
||||
UsbHostController,
|
||||
};
|
||||
@@ -67,7 +68,7 @@ struct ScratchpadArray {
|
||||
}
|
||||
|
||||
struct RootHubPort {
|
||||
version: UsbVersion,
|
||||
version: u16,
|
||||
slot_type: u8,
|
||||
}
|
||||
|
||||
@@ -90,6 +91,7 @@ pub struct Xhci {
|
||||
pub(crate) slots: Vec<IrqSafeRwLock<Option<Arc<XhciBusDevice>>>>,
|
||||
pub(crate) port_slot_map: Vec<AtomicU8>,
|
||||
bus_index: OneTimeInit<u16>,
|
||||
bus: OneTimeInit<Arc<UsbBusWrapper>>,
|
||||
port_event_map: EventBitmap,
|
||||
}
|
||||
|
||||
@@ -146,9 +148,7 @@ impl Xhci {
|
||||
for cap in regs.extended_capabilities.iter() {
|
||||
match cap {
|
||||
ExtendedCapability::ProtocolSupport(support) => {
|
||||
let Some(version) = support.usb_revision() else {
|
||||
continue;
|
||||
};
|
||||
let version = support.usb_revision();
|
||||
|
||||
for port in support.port_range() {
|
||||
log::info!("* Port {port}: {version}");
|
||||
@@ -195,6 +195,7 @@ impl Xhci {
|
||||
|
||||
root_hub_ports,
|
||||
bus_index: OneTimeInit::new(),
|
||||
bus: OneTimeInit::new(),
|
||||
endpoints: IrqSafeRwLock::new(BTreeMap::new()),
|
||||
slots,
|
||||
port_slot_map,
|
||||
@@ -282,7 +283,7 @@ impl Xhci {
|
||||
.as_ref()
|
||||
.ok_or(UsbError::PortInitFailed)?;
|
||||
|
||||
let need_reset = !root_hub_port.version.is_version_3();
|
||||
let need_reset = !descriptor::is_version_3(root_hub_port.version);
|
||||
|
||||
if need_reset {
|
||||
self.reset_port(regs).await?;
|
||||
@@ -341,7 +342,8 @@ impl Xhci {
|
||||
device: bus_address,
|
||||
});
|
||||
|
||||
let device = UsbDeviceAccess::setup(slot).await?;
|
||||
let bus = self.bus.get();
|
||||
let device = UsbDeviceAccess::setup(bus.clone(), slot).await?;
|
||||
UsbBusManager::register_device(device.into());
|
||||
|
||||
Ok(())
|
||||
@@ -523,8 +525,9 @@ impl Device for Xhci {
|
||||
|
||||
op.wait_usbsts_bit(USBSTS::CNR::CLEAR, 100000000)?;
|
||||
|
||||
let bus = UsbBusManager::register_bus(self.clone());
|
||||
self.bus_index.init(bus);
|
||||
let (bus_index, bus) = UsbBusManager::register_bus(self.clone());
|
||||
self.bus_index.init(bus_index);
|
||||
self.bus.init(bus);
|
||||
|
||||
runtime::spawn(self.clone().port_handler_task()).ok();
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@ use libk_util::{
|
||||
};
|
||||
use xhci_lib::context;
|
||||
use ygg_driver_usb::{
|
||||
address::UsbBusAddress,
|
||||
communication::UsbDirection,
|
||||
device::{UsbBusAddress, UsbDevice, UsbDeviceDetachHandler, UsbSpeed},
|
||||
device::{UsbDevice, UsbDeviceDetachHandler, UsbSpeed},
|
||||
error::UsbError,
|
||||
info::UsbEndpointType,
|
||||
pipe::{
|
||||
@@ -63,8 +64,8 @@ impl UsbDevice for XhciBusDevice {
|
||||
*self.detach_handler.lock() = Some(handler);
|
||||
}
|
||||
|
||||
fn controller_ref(&self) -> &dyn UsbHostController {
|
||||
self.xhci.as_ref()
|
||||
fn host_controller(&self) -> Arc<dyn UsbHostController> {
|
||||
self.xhci.clone()
|
||||
}
|
||||
|
||||
fn debug(&self) {}
|
||||
|
||||
@@ -143,14 +143,14 @@ impl UsbControlPipe for ControlPipe {
|
||||
setup: ControlTransferSetup,
|
||||
buffer: &[u8],
|
||||
) -> Result<usize, UsbError> {
|
||||
let dma_buffer =
|
||||
let mut dma_buffer =
|
||||
DmaBuffer::from_slice(&*self.xhci.dma, buffer).map_err(UsbError::MemoryError)?;
|
||||
|
||||
self.ring
|
||||
.control_transfer(
|
||||
self.xhci.as_ref(),
|
||||
setup,
|
||||
ControlDataStage::Out(&dma_buffer),
|
||||
ControlDataStage::Out(&mut dma_buffer),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use alloc::vec::Vec;
|
||||
use libk::error::Error;
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
use ygg_driver_usb::{error::UsbError, info::UsbVersion};
|
||||
use ygg_driver_usb::error::UsbError;
|
||||
|
||||
pub struct ProtocolSupport {
|
||||
words: [u32; 4],
|
||||
@@ -69,8 +69,8 @@ impl ExtendedCapability {
|
||||
}
|
||||
|
||||
impl ProtocolSupport {
|
||||
pub fn usb_revision(&self) -> Option<UsbVersion> {
|
||||
UsbVersion::from_bcd_usb((self.words[0] >> 16) as u16)
|
||||
pub fn usb_revision(&self) -> u16 {
|
||||
(self.words[0] >> 16) as u16
|
||||
}
|
||||
|
||||
pub fn slot_type(&self) -> u8 {
|
||||
|
||||
@@ -161,9 +161,9 @@ pub fn portsc_to_neutral(
|
||||
portsc.bitand(RO_MASK | RWS_MASK)
|
||||
}
|
||||
|
||||
impl From<PortNumber> for u8 {
|
||||
fn from(value: PortNumber) -> Self {
|
||||
value.0
|
||||
impl Into<u8> for PortNumber {
|
||||
fn into(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user