diff --git a/Cargo.lock b/Cargo.lock index c41a1e0178..d93fb99f01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,8 +47,8 @@ name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -84,6 +84,15 @@ dependencies = [ "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bincode" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bitflags" version = "0.7.0" @@ -132,6 +141,25 @@ name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "chashmap" +version = "2.2.2" +source = "git+https://gitlab.redox-os.org/redox-os/chashmap.git#da92c702e052cde00db5e409dfb234af71928152" +dependencies = [ + "owning_ref 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "chrono" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "clap" version = "2.33.0" @@ -164,10 +192,11 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -188,8 +217,8 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -203,10 +232,10 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -250,6 +279,88 @@ name = "futures" version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "futures" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-channel" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-executor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-io" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-sink" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-task" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-util" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-nested 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "gpt" version = "0.6.3" @@ -263,10 +374,10 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.6" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -328,7 +439,7 @@ name = "iovec" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -367,7 +478,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -380,10 +491,19 @@ name = "lock_api" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "owning_ref 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lock_api" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "log" version = "0.3.9" @@ -410,12 +530,17 @@ name = "maybe-uninit" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "memoffset" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -435,7 +560,7 @@ dependencies = [ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", @@ -454,7 +579,7 @@ dependencies = [ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", @@ -468,7 +593,7 @@ version = "0.6.7" source = "git+https://gitlab.redox-os.org/redox-os/mio-uds#22580ca398cdb5ed6f50fb61134e5579e2213999" dependencies = [ "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (git+https://gitlab.redox-os.org/redox-os/mio)", ] @@ -489,7 +614,7 @@ version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -502,7 +627,7 @@ dependencies = [ "extra 0.1.0 (git+https://gitlab.redox-os.org/redox-os/libextra.git)", "hyper 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-rustls 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (git+https://gitlab.redox-os.org/redox-os/mio)", "ntpclient 0.0.1 (git+https://github.com/willem66745/ntpclient-rust)", "pbr 1.0.2 (git+https://github.com/a8m/pb)", @@ -522,7 +647,7 @@ source = "git+https://gitlab.redox-os.org/redox-os/netutils.git?branch=redox-uni dependencies = [ "arg_parser 0.1.0 (git+https://gitlab.redox-os.org/redox-os/arg-parser.git)", "extra 0.1.0 (git+https://gitlab.redox-os.org/redox-os/libextra.git)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", "ntpclient 0.0.1 (git+https://github.com/willem66745/ntpclient-rust)", "pbr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -589,8 +714,8 @@ name = "num_cpus" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -620,7 +745,7 @@ dependencies = [ [[package]] name = "owning_ref" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -635,18 +760,42 @@ dependencies = [ "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parking_lot" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parking_lot_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "partitionlib" version = "0.1.0" @@ -662,8 +811,8 @@ name = "pbr" version = "1.0.2" source = "git+https://github.com/a8m/pb#87c29c05486afa7335916c870ea3621ff7ef2966" dependencies = [ - "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -674,7 +823,7 @@ name = "pbr" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -684,12 +833,15 @@ dependencies = [ name = "pcid" version = "0.1.0" dependencies = [ + "bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall.git)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -704,14 +856,29 @@ name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "pin-utils" +version = "0.1.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "plain" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "proc-macro-hack" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro-nested" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -729,10 +896,10 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -741,7 +908,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -796,7 +963,7 @@ name = "rand_jitter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -808,7 +975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -848,6 +1015,16 @@ dependencies = [ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "redox-log" +version = "0.1.0" +source = "git+https://gitlab.redox-os.org/redox-os/redox-log.git#08693d48b2d7b56fcb07a1e62e257bacce749cef" +dependencies = [ + "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "redox_event" version = "0.1.0" @@ -884,7 +1061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -931,7 +1108,7 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -946,7 +1123,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "scopeguard" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -962,9 +1139,9 @@ name = "scroll_derive" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -983,7 +1160,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2-sys 0.32.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -995,7 +1172,7 @@ version = "0.32.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1013,30 +1190,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.104" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.104" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.47" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1057,7 +1234,7 @@ name = "smallvec" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1093,11 +1270,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1106,7 +1283,7 @@ name = "termion" version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1122,20 +1299,20 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.10" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "thiserror-impl 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror-impl 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "thiserror-impl" -version = "1.0.10" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1143,7 +1320,7 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1294,7 +1471,7 @@ dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (git+https://gitlab.redox-os.org/redox-os/mio)", "mio-uds 0.6.7 (git+https://gitlab.redox-os.org/redox-os/mio-uds)", @@ -1303,20 +1480,12 @@ dependencies = [ "tokio-reactor 0.1.7 (git+https://gitlab.redox-os.org/redox-os/tokio)", ] -[[package]] -name = "toml" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "toml" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1403,7 +1572,7 @@ dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall.git)", - "thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "xhcid 0.1.0", ] @@ -1523,15 +1692,20 @@ name = "xhcid" version = "0.1.0" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "chashmap 2.2.2 (git+https://gitlab.redox-os.org/redox-os/chashmap.git)", + "crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pcid 0.1.0", "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "redox-log 0.1.0 (git+https://gitlab.redox-os.org/redox-os/redox-log.git)", "redox_event 0.1.0 (git+https://gitlab.redox-os.org/redox-os/event.git)", "redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall.git)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1544,6 +1718,7 @@ dependencies = [ "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +"checksum bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" @@ -1552,21 +1727,32 @@ dependencies = [ "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" "checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum chashmap 2.2.2 (git+https://gitlab.redox-os.org/redox-os/chashmap.git)" = "" +"checksum chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" -"checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" +"checksum crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" "checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" "checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" +"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" "checksum extra 0.1.0 (git+https://gitlab.redox-os.org/redox-os/libextra.git)" = "" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" +"checksum futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" +"checksum futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" +"checksum futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" +"checksum futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" +"checksum futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" +"checksum futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" +"checksum futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" +"checksum futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" +"checksum futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" "checksum gpt 0.6.3 (git+https://gitlab.redox-os.org/redox-os/gpt)" = "" -"checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" +"checksum hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" "checksum hyper 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)" = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" "checksum hyper-rustls 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "71f7b2e5858ab9e19771dc361159f09ee5031734a6f7471fe0947db0238d92b7" @@ -1576,14 +1762,16 @@ dependencies = [ "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +"checksum libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)" = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" +"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" -"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" +"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +"checksum memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" "checksum mio 0.6.16 (git+https://gitlab.redox-os.org/redox-os/mio)" = "" "checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" @@ -1601,16 +1789,21 @@ dependencies = [ "checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" "checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" "checksum orbclient 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f8b18f57ab94fbd058e30aa57f712ec423c0bb7403f8493a6c58eef0c36d9402" -"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" +"checksum owning_ref 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" +"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" +"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" "checksum partitionlib 0.1.0 (git+https://gitlab.redox-os.org/redox-os/partitionlib.git)" = "" "checksum pbr 1.0.2 (git+https://github.com/a8m/pb)" = "" "checksum pbr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4403eb718d70c03ee279e51737782902c68cca01e870a33b6a2f9dfb50b9cd83" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" "checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" -"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" -"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" +"checksum proc-macro-nested 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" +"checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" @@ -1623,6 +1816,7 @@ dependencies = [ "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" "checksum ransid 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "22b9af151b0590163dfa64e1c92c0831377d61942df4c19820c704390ebc3045" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +"checksum redox-log 0.1.0 (git+https://gitlab.redox-os.org/redox-os/redox-log.git)" = "" "checksum redox_event 0.1.0 (git+https://gitlab.redox-os.org/redox-os/event.git)" = "" "checksum redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall.git)" = "" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" @@ -1631,10 +1825,10 @@ dependencies = [ "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustls 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "942b71057b31981152970d57399c25f72e27a6ee0d207a669d8304cabf44705b" "checksum rusttype 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d8d729e72445ad579171b01a9231657736b3793a2cf423078e687e20ecb8695a" -"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +"checksum ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" "checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" "checksum scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1" "checksum scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28" "checksum sct 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb8f61f9e6eadd062a71c380043d28036304a4706b3c4dd001ff3387ed00745a" @@ -1642,9 +1836,9 @@ dependencies = [ "checksum sdl2-sys 0.32.6 (registry+https://github.com/rust-lang/crates.io-index)" = "34e71125077d297d57e4c1acfe8981b5bdfbf5a20e7b589abfdcb33bf1127f86" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" -"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" -"checksum serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "15913895b61e0be854afd32fd4163fcd2a3df34142cf2cb961b310ce694cbf90" +"checksum serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +"checksum serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +"checksum serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)" = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" "checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" @@ -1653,11 +1847,11 @@ dependencies = [ "checksum stb_truetype 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9d1bec4382294c5a680fcebd29f8451e8d8c04479a026f6909004e2ab1cb425d" "checksum stb_truetype 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f77b6b07e862c66a9f3e62a07588fee67cd90a9135a2b942409f195507b4fb51" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +"checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" "checksum termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c22cec9d8978d906be5ac94bceb5a010d885c626c4c8855721a4dbd20e3ac905" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -"checksum thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "205684fd018ca14432b12cce6ea3d46763311a571c3d294e71ba3f01adcf1aad" -"checksum thiserror-impl 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "57e4d2e50ca050ed44fb58309bdce3efa79948f84f9993ad1978de5eebdce5a7" +"checksum thiserror 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "f0570dc61221295909abdb95c739f2e74325e14293b2026b0a7e195091ec54ae" +"checksum thiserror-impl 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "227362df41d566be41a28f64401e07a043157c21c14b9785a0d8e256f940a8fd" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum tokio 0.1.13 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "" "checksum tokio-codec 0.1.1 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "" @@ -1671,7 +1865,6 @@ dependencies = [ "checksum tokio-timer 0.2.8 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "" "checksum tokio-udp 0.1.3 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "" "checksum tokio-uds 0.2.4 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "" -"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" "checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" diff --git a/bgad/src/scheme.rs b/bgad/src/scheme.rs index bad949c717..635d581017 100644 --- a/bgad/src/scheme.rs +++ b/bgad/src/scheme.rs @@ -1,4 +1,3 @@ -use orbclient; use std::fs::File; use std::io::Write; use std::str; diff --git a/initfs.toml b/initfs.toml index b604eec197..6861491750 100644 --- a/initfs.toml +++ b/initfs.toml @@ -22,7 +22,7 @@ vendor = 33006 device = 48879 command = ["bgad", "$NAME", "$BAR0"] -#nvmed +# nvmed [[drivers]] name = "NVME storage" class = 1 @@ -37,9 +37,11 @@ vendor = 33006 device = 51966 command = ["vboxd", "$NAME", "$BAR0", "$BAR1", "$IRQ"] +# xhcid [[drivers]] name = "XHCI" class = 12 subclass = 3 interface = 48 command = ["xhcid", "$NAME", "$BAR0", "$IRQ"] +channel_name = "pcid-xhcid" diff --git a/pcid/.gitignore b/pcid/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/pcid/.gitignore @@ -0,0 +1 @@ +/target diff --git a/pcid/Cargo.toml b/pcid/Cargo.toml index e35303f094..966d0ebff2 100644 --- a/pcid/Cargo.toml +++ b/pcid/Cargo.toml @@ -3,10 +3,21 @@ name = "pcid" version = "0.1.0" edition = "2018" +[[bin]] +name = "pcid" +path = "src/main.rs" + +[lib] +name = "pcid_interface" +path = "src/lib.rs" + [dependencies] -bitflags = "1.0" +bincode = "1.2" +bitflags = "1" byteorder = "1.2" -redox_syscall = "0.1" -serde = "1.0" -serde_derive = "1.0" -toml = "0.4" +libc = "0.2" +redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "1" +toml = "0.5" diff --git a/pcid/src/config.rs b/pcid/src/config.rs index 74ba8b4eaa..3c79746816 100644 --- a/pcid/src/config.rs +++ b/pcid/src/config.rs @@ -1,12 +1,14 @@ use std::collections::BTreeMap; use std::ops::Range; -#[derive(Debug, Default, Deserialize)] +use serde::Deserialize; + +#[derive(Clone, Debug, Default, Deserialize)] pub struct Config { - pub drivers: Vec + pub drivers: Vec, } -#[derive(Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Deserialize)] pub struct DriverConfig { pub name: Option, pub class: Option, @@ -16,5 +18,6 @@ pub struct DriverConfig { pub vendor: Option, pub device: Option, pub device_id_range: Option>, - pub command: Option> + pub command: Option>, + pub channel_name: Option, } diff --git a/pcid/src/driver_interface.rs b/pcid/src/driver_interface.rs new file mode 100644 index 0000000000..28c06ef02a --- /dev/null +++ b/pcid/src/driver_interface.rs @@ -0,0 +1,229 @@ +use std::fs::{File, OpenOptions}; +use std::io::prelude::*; +use std::{env, io}; + +use std::os::unix::io::{FromRawFd, RawFd}; + +use serde::{Serialize, Deserialize, de::DeserializeOwned}; +use thiserror::Error; + +pub use crate::pci::PciBar; +pub use crate::pci::msi; + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[repr(u8)] +pub enum LegacyInterruptPin { + /// INTa# + IntA = 1, + /// INTb# + IntB = 2, + /// INTc# + IntC = 3, + /// INTd# + IntD = 4, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct PciFunction { + /// Number of PCI bus + pub bus_num: u8, + + /// Number of PCI device + pub dev_num: u8, + + /// Number of PCI function + pub func_num: u8, + + /// PCI Base Address Registers + pub bars: [PciBar; 6], + + /// BAR sizes + pub bar_sizes: [u32; 6], + + /// Legacy IRQ line + pub legacy_interrupt_line: u8, + + /// Legacy interrupt pin (INTx#), none if INTx# interrupts aren't supported at all. + pub legacy_interrupt_pin: Option, + + /// Vendor ID + pub venid: u16, + /// Device ID + pub devid: u16, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SubdriverArguments { + pub func: PciFunction, +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum FeatureStatus { + Enabled, + Disabled, +} + +impl FeatureStatus { + pub fn enabled(enabled: bool) -> Self { + if enabled { + Self::Enabled + } else { + Self::Disabled + } + } + pub fn is_enabled(&self) -> bool { + if let &Self::Enabled = self { true } else { false } + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum PciFeature { + Msi, + MsiX, +} +impl PciFeature { + pub fn is_msi(&self) -> bool { + if let &Self::Msi = self { true } else { false } + } + pub fn is_msix(&self) -> bool { + if let &Self::MsiX = self { true } else { false } + } +} +#[derive(Debug, Serialize, Deserialize)] +pub enum PciFeatureInfo { + Msi(msi::MsiCapability), + MsiX(msi::MsixCapability), +} + +#[derive(Debug, Error)] +pub enum PcidClientHandleError { + #[error("i/o error: {0}")] + IoError(#[from] io::Error), + + #[error("JSON ser/de error: {0}")] + SerializationError(#[from] bincode::Error), + + #[error("environment variable error: {0}")] + EnvError(#[from] env::VarError), + + #[error("malformed fd: {0}")] + EnvValidityError(std::num::ParseIntError), + + #[error("invalid response: {0:?}")] + InvalidResponse(PcidClientResponse), +} +pub type Result = std::result::Result; + +#[derive(Debug, Serialize, Deserialize)] +#[non_exhaustive] +pub enum PcidClientRequest { + RequestConfig, + RequestFeatures, + EnableFeature(PciFeature), + FeatureStatus(PciFeature), + FeatureInfo(PciFeature), +} + +#[derive(Debug, Serialize, Deserialize)] +#[non_exhaustive] +pub enum PcidServerResponseError { + NonexistentFeature(PciFeature), +} + +#[derive(Debug, Serialize, Deserialize)] +#[non_exhaustive] +pub enum PcidClientResponse { + Config(SubdriverArguments), + AllFeatures(Vec<(PciFeature, FeatureStatus)>), + FeatureEnabled(PciFeature), + FeatureStatus(PciFeature, FeatureStatus), + Error(PcidServerResponseError), + FeatureInfo(PciFeature, PciFeatureInfo), +} + +// TODO: Ideally, pcid might have its own scheme, like lots of other Redox drivers, where this kind of IPC is done. Otherwise, instead of writing serde messages over +// a channel, the communication could potentially be done via mmap, using a channel +// very similar to crossbeam-channel or libstd's mpsc (except the cycle, enqueue and dequeue fields +// are stored in the same buffer as the actual data). +/// A handle from a `pcid` client (e.g. `ahcid`) to `pcid`. +pub struct PcidServerHandle { + pcid_to_client: File, + pcid_from_client: File, +} + +pub(crate) fn send(w: &mut W, message: &T) -> Result<()> { + let mut data = Vec::new(); + bincode::serialize_into(&mut data, message)?; + let length_bytes = u64::to_le_bytes(data.len() as u64); + w.write_all(&length_bytes)?; + w.write_all(&data)?; + Ok(()) +} +pub(crate) fn recv(r: &mut R) -> Result { + let mut length_bytes = [0u8; 8]; + r.read_exact(&mut length_bytes)?; + let length = u64::from_le_bytes(length_bytes); + if length > 0x100_000 { + panic!("pcid_interface: buffer too large"); + } + let mut data = vec! [0u8; length as usize]; + r.read_exact(&mut data)?; + + Ok(bincode::deserialize_from(&data[..])?) +} + +impl PcidServerHandle { + pub fn connect(pcid_to_client: RawFd, pcid_from_client: RawFd) -> Result { + Ok(Self { + pcid_to_client: unsafe { File::from_raw_fd(pcid_to_client) }, + pcid_from_client: unsafe { File::from_raw_fd(pcid_from_client) }, + }) + } + pub fn connect_default() -> Result { + let pcid_to_client_fd = env::var("PCID_TO_CLIENT_FD")?.parse::().map_err(PcidClientHandleError::EnvValidityError)?; + let pcid_from_client_fd = env::var("PCID_FROM_CLIENT_FD")?.parse::().map_err(PcidClientHandleError::EnvValidityError)?; + + Self::connect(pcid_to_client_fd, pcid_from_client_fd) + } + pub(crate) fn send(&mut self, req: &PcidClientRequest) -> Result<()> { + send(&mut self.pcid_from_client, req) + } + pub(crate) fn recv(&mut self) -> Result { + recv(&mut self.pcid_to_client) + } + pub fn fetch_config(&mut self) -> Result { + self.send(&PcidClientRequest::RequestConfig)?; + match self.recv()? { + PcidClientResponse::Config(a) => Ok(a), + other => Err(PcidClientHandleError::InvalidResponse(other)), + } + } + pub fn fetch_all_features(&mut self) -> Result> { + self.send(&PcidClientRequest::RequestFeatures)?; + match self.recv()? { + PcidClientResponse::AllFeatures(a) => Ok(a), + other => Err(PcidClientHandleError::InvalidResponse(other)), + } + } + pub fn feature_status(&mut self, feature: PciFeature) -> Result { + self.send(&PcidClientRequest::FeatureStatus(feature))?; + match self.recv()? { + PcidClientResponse::FeatureStatus(feat, status) if feat == feature => Ok(status), + other => Err(PcidClientHandleError::InvalidResponse(other)), + } + } + pub fn enable_feature(&mut self, feature: PciFeature) -> Result<()> { + self.send(&PcidClientRequest::EnableFeature(feature))?; + match self.recv()? { + PcidClientResponse::FeatureEnabled(feat) if feat == feature => Ok(()), + other => Err(PcidClientHandleError::InvalidResponse(other)), + } + } + pub fn feature_info(&mut self, feature: PciFeature) -> Result { + self.send(&PcidClientRequest::FeatureInfo(feature))?; + match self.recv()? { + PcidClientResponse::FeatureInfo(feat, info) if feat == feature => Ok(info), + other => Err(PcidClientHandleError::InvalidResponse(other)), + } + } +} diff --git a/pcid/src/lib.rs b/pcid/src/lib.rs new file mode 100644 index 0000000000..e03e5bfccf --- /dev/null +++ b/pcid/src/lib.rs @@ -0,0 +1,7 @@ +//! Interface to `pcid`. + +#![feature(asm)] + +mod driver_interface; +mod pci; +pub use driver_interface::*; diff --git a/pcid/src/main.rs b/pcid/src/main.rs index 88d85e07c0..daef5c699a 100644 --- a/pcid/src/main.rs +++ b/pcid/src/main.rs @@ -1,37 +1,163 @@ #![feature(asm)] -#[macro_use] extern crate bitflags; +extern crate bitflags; extern crate byteorder; -#[macro_use] extern crate serde_derive; extern crate syscall; extern crate toml; -use std::{env, i64}; use std::fs::{File, metadata, read_dir}; -use std::io::Read; +use std::io::prelude::*; +use std::os::unix::io::{FromRawFd, RawFd}; use std::process::Command; +use std::sync::{Arc, Mutex}; +use std::{env, io, i64, thread}; + use syscall::iopl; use crate::config::Config; -use crate::pci::{Pci, PciBar, PciClass, PciHeader, PciHeaderError, PciHeaderType}; +use crate::pci::{Pci, PciBar, PciBus, PciClass, PciDev, PciFunc, PciHeader, PciHeaderError, PciHeaderType}; +use crate::pci::cap::Capability as PciCapability; mod config; +mod driver_interface; mod pci; -fn handle_parsed_header(config: &Config, pci: &Pci, bus_num: u8, +pub struct DriverHandler { + config: config::DriverConfig, + bus_num: u8, + dev_num: u8, + func_num: u8, + header: PciHeader, + capabilities: Vec<(u8, PciCapability)>, + + state: Arc, +} +fn with_pci_func_raw T>(pci: &Pci, bus_num: u8, dev_num: u8, func_num: u8, function: F) -> T { + let bus = PciBus { + pci, + num: bus_num, + }; + let dev = PciDev { + bus: &bus, + num: dev_num, + }; + let func = PciFunc { + dev: &dev, + num: func_num, + }; + function(&func) +} +impl DriverHandler { + fn with_pci_func_raw T>(&self, function: F) -> T { + with_pci_func_raw(&self.state.pci, self.bus_num, self.dev_num, self.func_num, function) + } + fn respond(&mut self, request: driver_interface::PcidClientRequest, args: &driver_interface::SubdriverArguments) -> driver_interface::PcidClientResponse { + use driver_interface::*; + use crate::pci::cap::{MsiCapability, MsixCapability}; + + match request { + PcidClientRequest::RequestConfig => { + PcidClientResponse::Config(args.clone()) + } + PcidClientRequest::RequestFeatures => { + PcidClientResponse::AllFeatures(self.capabilities.iter().filter_map(|(_, capability)| match capability { + PciCapability::Msi(msi) => Some((PciFeature::Msi, FeatureStatus::enabled(msi.enabled()))), + PciCapability::MsiX(msix) => Some((PciFeature::MsiX, FeatureStatus::enabled(msix.msix_enabled()))), + _ => None, + }).collect()) + } + PcidClientRequest::EnableFeature(feature) => match feature { + PciFeature::Msi => { + let (offset, capability): (u8, &mut MsiCapability) = match self.capabilities.iter_mut().find_map(|&mut (offset, ref mut capability)| capability.as_msi_mut().map(|cap| (offset, cap))) { + Some(tuple) => tuple, + None => return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature)), + }; + unsafe { + with_pci_func_raw(&self.state.pci, self.bus_num, self.dev_num, self.func_num, |func| { + capability.set_enabled(true); + capability.write_message_control(func, offset); + }); + } + PcidClientResponse::FeatureEnabled(feature) + } + PciFeature::MsiX => { + let (offset, capability): (u8, &mut MsixCapability) = match self.capabilities.iter_mut().find_map(|&mut (offset, ref mut capability)| capability.as_msix_mut().map(|cap| (offset, cap))) { + Some(tuple) => tuple, + None => return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature)), + }; + unsafe { + with_pci_func_raw(&self.state.pci, self.bus_num, self.dev_num, self.func_num, |func| { + capability.set_msix_enabled(true); + capability.write_a(func, offset); + }); + } + PcidClientResponse::FeatureEnabled(feature) + } + } + PcidClientRequest::FeatureStatus(feature) => PcidClientResponse::FeatureStatus(feature, match feature { + PciFeature::Msi => self.capabilities.iter().find_map(|(_, capability)| if let PciCapability::Msi(msi) = capability { + Some(FeatureStatus::enabled(msi.enabled())) + } else { + None + }).unwrap_or(FeatureStatus::Disabled), + PciFeature::MsiX => self.capabilities.iter().find_map(|(_, capability)| if let PciCapability::MsiX(msix) = capability { + Some(FeatureStatus::enabled(msix.msix_enabled())) + } else { + None + }).unwrap_or(FeatureStatus::Disabled), + }), + PcidClientRequest::FeatureInfo(feature) => PcidClientResponse::FeatureInfo(feature, match feature { + PciFeature::Msi => if let Some(info) = self.capabilities.iter().find_map(|(_, capability)| capability.as_msi()) { + PciFeatureInfo::Msi(*info) + } else { + return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature)); + } + PciFeature::MsiX => if let Some(info) = self.capabilities.iter().find_map(|(_, capability)| capability.as_msix()) { + PciFeatureInfo::MsiX(*info) + } else { + return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature)); + } + }), + } + } + fn handle_spawn(mut self, pcid_to_client_write: Option, pcid_from_client_read: Option, args: driver_interface::SubdriverArguments) { + use driver_interface::*; + + if let (Some(pcid_to_client_fd), Some(pcid_from_client_fd)) = (pcid_to_client_write, pcid_from_client_read) { + let mut pcid_to_client = unsafe { File::from_raw_fd(pcid_to_client_fd as RawFd) }; + let mut pcid_from_client = unsafe { File::from_raw_fd(pcid_from_client_fd as RawFd) }; + + while let Ok(msg) = recv(&mut pcid_from_client) { + let response = self.respond(msg, &args); + send(&mut pcid_to_client, &response).unwrap(); + } + } + } +} + +pub struct State { + threads: Mutex>>, + pci: Pci, +} + +fn handle_parsed_header(state: Arc, config: &Config, bus_num: u8, dev_num: u8, func_num: u8, header: PciHeader) { + let pci = &state.pci; + let raw_class: u8 = header.class().into(); let mut string = format!("PCI {:>02X}/{:>02X}/{:>02X} {:>04X}:{:>04X} {:>02X}.{:>02X}.{:>02X}.{:>02X} {:?}", bus_num, dev_num, func_num, header.vendor_id(), header.device_id(), raw_class, header.subclass(), header.interface(), header.revision(), header.class()); - match header.class() { + PciClass::Legacy if header.subclass() == 1 => string.push_str(" VGA CTL"), PciClass::Storage => match header.subclass() { 0x01 => { string.push_str(" IDE"); }, - 0x06 => { - string.push_str(" SATA"); + 0x06 => if header.interface() == 0 { + string.push_str(" SATA VND"); + } else if header.interface() == 1 { + string.push_str(" SATA AHCI"); }, _ => () }, @@ -130,6 +256,8 @@ fn handle_parsed_header(config: &Config, pci: &Pci, bus_num: u8, pci.write(bus_num, dev_num, func_num, 0x3C, data); } + let interrupt_pin = unsafe { pci.read(bus_num, dev_num, func_num, 0x3B) }; + // Find BAR sizes let mut bars = [PciBar::None; 6]; let mut bar_sizes = [0; 6]; @@ -166,9 +294,54 @@ fn handle_parsed_header(config: &Config, pci: &Pci, bus_num: u8, } } - // TODO: find a better way to pass the header data down to the - // device driver, making passing the capabilities list etc - // posible. + let capabilities = { + let bus = PciBus { + pci, + num: bus_num, + }; + let dev = PciDev { + bus: &bus, + num: dev_num + }; + let func = PciFunc { + dev: &dev, + num: func_num, + }; + crate::pci::cap::CapabilitiesIter { inner: crate::pci::cap::CapabilityOffsetsIter::new(header.cap_pointer(), &func) }.collect::>() + }; + println!("PCI DEVICE CAPABILITIES for {}: {:?}", args.iter().map(|string| string.as_ref()).nth(0).unwrap_or("[unknown]"), capabilities); + + use driver_interface::LegacyInterruptPin; + + let legacy_interrupt_pin = match interrupt_pin { + 0 => None, + 1 => Some(LegacyInterruptPin::IntA), + 2 => Some(LegacyInterruptPin::IntB), + 3 => Some(LegacyInterruptPin::IntC), + 4 => Some(LegacyInterruptPin::IntD), + + other => { + println!("pcid: invalid interrupt pin: {}", other); + None + } + }; + + let func = driver_interface::PciFunction { + bars, + bar_sizes, + bus_num, + dev_num, + func_num, + devid: header.device_id(), + legacy_interrupt_line: irq, + legacy_interrupt_pin, + venid: header.vendor_id(), + }; + + let subdriver_args = driver_interface::SubdriverArguments { + func, + }; + let mut args = args.iter(); if let Some(program) = args.next() { let mut command = Command::new(program); @@ -199,11 +372,41 @@ fn handle_parsed_header(config: &Config, pci: &Pci, bus_num: u8, } println!("PCID SPAWN {:?}", command); - match command.spawn() { - Ok(mut child) => match child.wait() { - Ok(_status) => (), //println!("pcid: waited for {}: {:?}", line, status.code()), - Err(err) => println!("pcid: failed to wait for {:?}: {}", command, err) - }, + + let (pcid_to_client_write, pcid_from_client_read, envs) = if driver.channel_name.is_some() { + let mut fds1 = [0usize; 2]; + let mut fds2 = [0usize; 2]; + + syscall::pipe2(&mut fds1, 0).expect("pcid: failed to create pcid->client pipe"); + syscall::pipe2(&mut fds2, 0).expect("pcid: failed to create client->pcid pipe"); + + let [pcid_to_client_read, pcid_to_client_write] = fds1; + let [pcid_from_client_read, pcid_from_client_write] = fds2; + + (Some(pcid_to_client_write), Some(pcid_from_client_read), vec! [("PCID_TO_CLIENT_FD", format!("{}", pcid_to_client_read)), ("PCID_FROM_CLIENT_FD", format!("{}", pcid_from_client_write))]) + } else { + (None, None, vec! []) + }; + + match command.envs(envs).spawn() { + Ok(mut child) => { + let driver_handler = DriverHandler { + bus_num, + dev_num, + func_num, + config: driver.clone(), + header, + state: Arc::clone(&state), + capabilities, + }; + let thread = thread::spawn(move || { + driver_handler.handle_spawn(pcid_to_client_write, pcid_from_client_read, subdriver_args); + }); + match child.wait() { + Ok(_status) => (), + Err(err) => println!("pcid: failed to wait for {:?}: {}", command, err), + } + } Err(err) => println!("pcid: failed to execute {:?}: {}", command, err) } } @@ -241,18 +444,24 @@ fn main() { } } + let state = Arc::new(State { + pci: Pci::new(), + threads: Mutex::new(Vec::new()), + }); + + let pci = &state.pci; + unsafe { iopl(3).unwrap() }; print!("PCI BS/DV/FN VEND:DEVI CL.SC.IN.RV\n"); - let pci = Pci::new(); 'bus: for bus in pci.buses() { 'dev: for dev in bus.devs() { for func in dev.funcs() { let func_num = func.num; match PciHeader::from_reader(func) { Ok(header) => { - handle_parsed_header(&config, &pci, bus.num, dev.num, func_num, header); + handle_parsed_header(Arc::clone(&state), &config, bus.num, dev.num, func_num, header); } Err(PciHeaderError::NoDevice) => { if func_num == 0 { @@ -272,4 +481,8 @@ fn main() { } } } + + for thread in state.threads.lock().unwrap().drain(..) { + thread.join().unwrap(); + } } diff --git a/pcid/src/pci/bar.rs b/pcid/src/pci/bar.rs index b1efde2e38..1ff92f9739 100644 --- a/pcid/src/pci/bar.rs +++ b/pcid/src/pci/bar.rs @@ -1,6 +1,8 @@ use std::fmt; -#[derive(Clone, Copy, Debug, PartialEq)] +use serde::{Serialize, Deserialize}; + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub enum PciBar { None, Memory(u32), diff --git a/pcid/src/pci/bus.rs b/pcid/src/pci/bus.rs index 120fa458f9..388e796e61 100644 --- a/pcid/src/pci/bus.rs +++ b/pcid/src/pci/bus.rs @@ -13,6 +13,9 @@ impl<'pci> PciBus<'pci> { pub unsafe fn read(&self, dev: u8, func: u8, offset: u8) -> u32 { self.pci.read(self.num, dev, func, offset) } + pub unsafe fn write(&self, dev: u8, func: u8, offset: u8, value: u32) { + self.pci.write(self.num, dev, func, offset, value) + } } pub struct PciBusIter<'pci> { diff --git a/pcid/src/pci/cap.rs b/pcid/src/pci/cap.rs new file mode 100644 index 0000000000..6159dc0f6a --- /dev/null +++ b/pcid/src/pci/cap.rs @@ -0,0 +1,180 @@ +use super::func::ConfigReader; +use serde::{Serialize, Deserialize}; + +pub struct CapabilityOffsetsIter<'a, R> { + offset: u8, + reader: &'a R, +} +impl<'a, R> CapabilityOffsetsIter<'a, R> { + pub fn new(offset: u8, reader: &'a R) -> Self { + Self { + offset, + reader, + } + } +} +impl<'a, R> Iterator for CapabilityOffsetsIter<'a, R> +where + R: ConfigReader +{ + type Item = u8; + + fn next(&mut self) -> Option { + unsafe { + assert_eq!(self.offset & 0xF8, self.offset, "capability must be dword aligned"); + + if self.offset == 0 { return None }; + + let first_dword = dbg!(self.reader.read_u32(dbg!(self.offset))); + let next = ((first_dword >> 8) & 0xFF) as u8; + + let offset = self.offset; + self.offset = next; + + Some(offset) + } + } +} + +#[repr(u8)] +pub enum CapabilityId { + Msi = 0x05, + MsiX = 0x11, + Pcie = 0x10, +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum MsiCapability { + _32BitAddress { + message_control: u32, + message_address: u32, + message_data: u32, + }, + _64BitAddress { + message_control: u32, + message_address_lo: u32, + message_address_hi: u32, + message_data: u32, + }, + _32BitAddressWithPvm { + message_control: u32, + message_address: u32, + message_data: u32, + mask_bits: u32, + pending_bits: u32, + }, + _64BitAddressWithPvm { + message_control: u32, + message_address_lo: u32, + message_address_hi: u32, + message_data: u32, + mask_bits: u32, + pending_bits: u32, + }, +} + + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct PcieCapability { +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub struct MsixCapability { + pub a: u32, + pub b: u32, + pub c: u32, +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum Capability { + Msi(MsiCapability), + MsiX(MsixCapability), + Pcie(PcieCapability), + Other(u8), +} + +impl Capability { + pub fn as_msi(&self) -> Option<&MsiCapability> { + match self { + &Self::Msi(ref msi) => Some(msi), + _ => None, + } + } + pub fn as_msix(&self) -> Option<&MsixCapability> { + match self { + &Self::MsiX(ref msix) => Some(msix), + _ => None, + } + } + pub fn as_msi_mut(&mut self) -> Option<&mut MsiCapability> { + match self { + &mut Self::Msi(ref mut msi) => Some(msi), + _ => None, + } + } + pub fn as_msix_mut(&mut self) -> Option<&mut MsixCapability> { + match self { + &mut Self::MsiX(ref mut msix) => Some(msix), + _ => None, + } + } + pub fn into_msi(self) -> Option { + match self { + Self::Msi(msi) => Some(msi), + _ => None, + } + } + pub fn into_msix(self) -> Option { + match self { + Self::MsiX(msix) => Some(msix), + _ => None, + } + } + unsafe fn parse_msi(reader: &R, offset: u8) -> Self { + Self::Msi(MsiCapability::parse(reader, offset)) + } + unsafe fn parse_msix(reader: &R, offset: u8) -> Self { + Self::MsiX(MsixCapability { + a: reader.read_u32(offset), + b: reader.read_u32(offset + 4), + c: reader.read_u32(offset + 8), + }) + } + unsafe fn parse_pcie(reader: &R, offset: u8) -> Self { + // TODO + Self::Pcie(PcieCapability {}) + } + unsafe fn parse(reader: &R, offset: u8) -> Self { + assert_eq!(offset & 0xF8, offset, "capability must be dword aligned"); + + let dword = reader.read_u32(offset); + let capability_id = (dword & 0xFF) as u8; + + if capability_id == CapabilityId::Msi as u8 { + Self::parse_msi(reader, offset) + } else if capability_id == CapabilityId::MsiX as u8 { + Self::parse_msix(reader, offset) + } else if capability_id == CapabilityId::Pcie as u8 { + Self::parse_pcie(reader, offset) + } else { + Self::Other(capability_id) + //panic!("unimplemented or malformed capability id: {}", capability_id) + } + } +} + +pub struct CapabilitiesIter<'a, R> { + pub inner: CapabilityOffsetsIter<'a, R>, +} + +impl<'a, R> Iterator for CapabilitiesIter<'a, R> +where + R: ConfigReader +{ + type Item = (u8, Capability); + + fn next(&mut self) -> Option { + let offset = self.inner.next()?; + Some((offset, unsafe { Capability::parse(self.inner.reader, offset) })) + } +} diff --git a/pcid/src/pci/dev.rs b/pcid/src/pci/dev.rs index 05088886e8..6d021994e5 100644 --- a/pcid/src/pci/dev.rs +++ b/pcid/src/pci/dev.rs @@ -13,6 +13,9 @@ impl<'pci> PciDev<'pci> { pub unsafe fn read(&self, func: u8, offset: u8) -> u32 { self.bus.read(self.num, func, offset) } + pub unsafe fn write(&self, func: u8, offset: u8, value: u32) { + self.bus.write(self.num, func, offset, value); + } } pub struct PciDevIter<'pci> { diff --git a/pcid/src/pci/func.rs b/pcid/src/pci/func.rs index 5c206f3607..0a3be3c874 100644 --- a/pcid/src/pci/func.rs +++ b/pcid/src/pci/func.rs @@ -2,6 +2,9 @@ use byteorder::{LittleEndian, ByteOrder}; use super::PciDev; +// TODO: PCI Express Configuration Space, which uses a flat memory buffer, rather than IN/OUT +// instructions. + pub trait ConfigReader { unsafe fn read_range(&self, offset: u8, len: u8) -> Vec { assert!(len > 3 && len % 4 == 0); @@ -17,11 +20,22 @@ pub trait ConfigReader { } unsafe fn read_u32(&self, offset: u8) -> u32; + + unsafe fn read_u8(&self, offset: u8) -> u8 { + let dword_offset = (offset / 4) * 4; + let dword = self.read_u32(dword_offset); + + let shift = (offset % 4) * 8; + ((dword >> shift) & 0xFF) as u8 + } +} +pub trait ConfigWriter { + unsafe fn write_u32(&self, offset: u8, value: u32); } pub struct PciFunc<'pci> { pub dev: &'pci PciDev<'pci>, - pub num: u8 + pub num: u8, } impl<'pci> ConfigReader for PciFunc<'pci> { @@ -29,3 +43,8 @@ impl<'pci> ConfigReader for PciFunc<'pci> { self.dev.read(self.num, offset) } } +impl<'pci> ConfigWriter for PciFunc<'pci> { + unsafe fn write_u32(&self, offset: u8, value: u32) { + self.dev.write(self.num, offset, value); + } +} diff --git a/pcid/src/pci/header.rs b/pcid/src/pci/header.rs index 272d917b1f..0d8513bb82 100644 --- a/pcid/src/pci/header.rs +++ b/pcid/src/pci/header.rs @@ -3,6 +3,7 @@ use byteorder::{LittleEndian, ByteOrder}; use super::func::ConfigReader; use super::class::PciClass; use super::bar::PciBar; +use bitflags::bitflags; #[derive(Debug, PartialEq)] pub enum PciHeaderError { @@ -26,7 +27,7 @@ bitflags! { } } -#[derive(Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum PciHeader { General { vendor_id: u16, @@ -123,7 +124,6 @@ impl PciHeader { let subsystem_vendor_id = LittleEndian::read_u16(&bytes[28..30]); let subsystem_id = LittleEndian::read_u16(&bytes[30..32]); let expansion_rom_bar = LittleEndian::read_u32(&bytes[32..36]); - // TODO: Parse out the capabilities list. let cap_pointer = bytes[36]; let interrupt_line = bytes[44]; let interrupt_pin = bytes[45]; @@ -158,7 +158,6 @@ impl PciHeader { let prefetch_limit_upper = LittleEndian::read_u32(&bytes[28..32]); let io_base_upper = LittleEndian::read_u16(&bytes[32..34]); let io_limit_upper = LittleEndian::read_u16(&bytes[34..36]); - // TODO: Parse out the capabilities list. let cap_pointer = bytes[36]; let expansion_rom = LittleEndian::read_u32(&bytes[40..44]); let interrupt_line = bytes[44]; @@ -265,6 +264,11 @@ impl PciHeader { } } + pub fn cap_pointer(&self) -> u8 { + match self { + &PciHeader::General { cap_pointer, .. } | &PciHeader::PciToPci { cap_pointer, .. } => cap_pointer, + } + } } #[cfg(test)] diff --git a/pcid/src/pci/mod.rs b/pcid/src/pci/mod.rs index e70048fd84..b3395517eb 100644 --- a/pcid/src/pci/mod.rs +++ b/pcid/src/pci/mod.rs @@ -1,3 +1,5 @@ +use std::sync::Mutex; + pub use self::bar::PciBar; pub use self::bus::{PciBus, PciBusIter}; pub use self::class::PciClass; @@ -7,16 +9,22 @@ pub use self::header::{PciHeader, PciHeaderError, PciHeaderType}; mod bar; mod bus; +pub mod cap; mod class; mod dev; mod func; pub mod header; +pub mod msi; -pub struct Pci; +pub struct Pci { + lock: Mutex<()>, +} impl Pci { pub fn new() -> Self { - Pci + Self { + lock: Mutex::new(()), + } } pub fn buses<'pci>(&'pci self) -> PciIter<'pci> { @@ -24,7 +32,7 @@ impl Pci { } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub unsafe fn read(&self, bus: u8, dev: u8, func: u8, offset: u8) -> u32 { + pub unsafe fn read_nolock(&self, bus: u8, dev: u8, func: u8, offset: u8) -> u32 { let address = 0x80000000 | ((bus as u32) << 16) | ((dev as u32) << 11) | ((func as u32) << 8) | ((offset as u32) & 0xFC); let value: u32; asm!("mov dx, 0xCF8 @@ -36,7 +44,13 @@ impl Pci { } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub unsafe fn write(&self, bus: u8, dev: u8, func: u8, offset: u8, value: u32) { + pub unsafe fn read(&self, bus: u8, dev: u8, func: u8, offset: u8) -> u32 { + let _guard = self.lock.lock().unwrap(); + self.read_nolock(bus, dev, func, offset) + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub unsafe fn write_nolock(&self, bus: u8, dev: u8, func: u8, offset: u8, value: u32) { let address = 0x80000000 | ((bus as u32) << 16) | ((dev as u32) << 11) | ((func as u32) << 8) | ((offset as u32) & 0xFC); asm!("mov dx, 0xCF8 out dx, eax" @@ -45,6 +59,11 @@ impl Pci { out dx, eax" : : "{eax}"(value) : "dx" : "intel", "volatile"); } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + pub unsafe fn write(&self, bus: u8, dev: u8, func: u8, offset: u8, value: u32) { + let _guard = self.lock.lock().unwrap(); + self.write_nolock(bus, dev, func, offset, value) + } } pub struct PciIter<'pci> { diff --git a/pcid/src/pci/msi.rs b/pcid/src/pci/msi.rs new file mode 100644 index 0000000000..d4400257f4 --- /dev/null +++ b/pcid/src/pci/msi.rs @@ -0,0 +1,351 @@ +use std::fmt; + +use super::bar::PciBar; +pub use super::cap::{MsiCapability, MsixCapability}; +use super::func::{ConfigReader, ConfigWriter}; + +use syscall::{Io, Mmio}; + +impl MsiCapability { + pub const MC_PVT_CAPABLE_BIT: u16 = 1 << 8; + pub const MC_64_BIT_ADDR_BIT: u16 = 1 << 7; + + pub const MC_MULTI_MESSAGE_MASK: u16 = 0x000E; + pub const MC_MULTI_MESSAGE_SHIFT: u8 = 1; + + pub const MC_MULTI_MESSAGE_ENABLE_MASK: u16 = 0x0070; + pub const MC_MULTI_MESSAGE_ENABLE_SHIFT: u8 = 4; + + pub const MC_MSI_ENABLED_BIT: u16 = 1; + + pub unsafe fn parse(reader: &R, offset: u8) -> Self { + let dword = reader.read_u32(offset); + + let message_control = (dword >> 16) as u16; + + if message_control & Self::MC_PVT_CAPABLE_BIT != 0 { + if message_control & Self::MC_64_BIT_ADDR_BIT != 0 { + Self::_64BitAddressWithPvm { + message_control: dword, + message_address_lo: reader.read_u32(offset + 4), + message_address_hi: reader.read_u32(offset + 8), + message_data: reader.read_u32(offset + 12), + mask_bits: reader.read_u32(offset + 16), + pending_bits: reader.read_u32(offset + 20), + } + } else { + Self::_32BitAddressWithPvm { + message_control: dword, + message_address: reader.read_u32(offset + 4), + message_data: reader.read_u32(offset + 8), + mask_bits: reader.read_u32(offset + 12), + pending_bits: reader.read_u32(offset + 16), + } + } + } else { + if message_control & Self::MC_64_BIT_ADDR_BIT != 0 { + Self::_64BitAddress { + message_control: dword, + message_address_lo: reader.read_u32(offset + 4), + message_address_hi: reader.read_u32(offset + 8), + message_data: reader.read_u32(offset + 12), + } + } else { + Self::_32BitAddress { + message_control: dword, + message_address: reader.read_u32(offset + 4), + message_data: reader.read_u32(offset + 8), + } + } + } + } + + fn message_control_raw(&self) -> u32 { + match self { + Self::_32BitAddress { message_control, .. } | Self::_64BitAddress { message_control, .. } | Self::_32BitAddressWithPvm { message_control, .. } | Self::_64BitAddressWithPvm { message_control, .. } => *message_control, + } + } + pub fn message_control(&self) -> u16 { + (self.message_control_raw() >> 16) as u16 + } + pub fn set_message_control(&mut self, value: u16) { + let mut new_message_control = self.message_control_raw(); + new_message_control &= 0x0000_FFFF; + new_message_control |= u32::from(value) << 16; + + match self { + Self::_32BitAddress { ref mut message_control, .. } + | Self::_64BitAddress { ref mut message_control, .. } + | Self::_32BitAddressWithPvm { ref mut message_control, .. } + | Self::_64BitAddressWithPvm { ref mut message_control, .. } => *message_control = new_message_control, + } + } + pub unsafe fn write_message_control(&mut self, writer: &W, offset: u8) { + writer.write_u32(offset, self.message_control_raw()); + } + pub fn is_pvt_capable(&self) -> bool { + self.message_control() & Self::MC_PVT_CAPABLE_BIT != 0 + } + pub fn has_64_bit_addr(&self) -> bool { + self.message_control() & Self::MC_64_BIT_ADDR_BIT != 0 + } + pub fn enabled(&self) -> bool { + self.message_control() & Self::MC_MSI_ENABLED_BIT != 0 + } + pub fn set_enabled(&mut self, enabled: bool) { + let mut new_message_control = self.message_control() & (!Self::MC_MSI_ENABLED_BIT); + new_message_control |= u16::from(enabled); + self.set_message_control(new_message_control); + } + pub fn multi_message_capable(&self) -> u8 { + ((self.message_control() & Self::MC_MULTI_MESSAGE_MASK) >> Self::MC_MULTI_MESSAGE_SHIFT) as u8 + } + pub fn multi_message_enabled(&self) -> u8 { + ((self.message_control() & Self::MC_MULTI_MESSAGE_ENABLE_MASK) >> Self::MC_MULTI_MESSAGE_ENABLE_SHIFT) as u8 + } + pub fn set_multi_message_enabled(&mut self, log_mme: u8) { + let mut new_message_control = self.message_control() & (!Self::MC_MULTI_MESSAGE_ENABLE_MASK); + new_message_control |= (u16::from(log_mme) << Self::MC_MULTI_MESSAGE_ENABLE_SHIFT); + self.set_message_control(new_message_control); + } +} + +impl MsixCapability { + pub const MC_MSIX_ENABLED_BIT: u16 = 1 << 15; + pub const MC_MSIX_ENABLED_SHIFT: u8 = 15; + pub const MC_FUNCTION_MASK_BIT: u16 = 1 << 14; + pub const MC_FUNCTION_MASK_SHIFT: u8 = 14; + pub const MC_TABLE_SIZE_MASK: u16 = 0x03FF; + + /// The Message Control field, containing the enabled and function mask bits, as well as the + /// table size. + pub const fn message_control(&self) -> u16 { + (self.a >> 16) as u16 + } + + pub fn set_message_control(&mut self, message_control: u16) { + self.a &= 0x0000_FFFF; + self.a |= u32::from(message_control) << 16; + } + /// Returns the MSI-X table size, subtracted by one. + pub const fn table_size_raw(&self) -> u16 { + self.message_control() & Self::MC_TABLE_SIZE_MASK + } + /// Returns the MSI-X table size. + pub const fn table_size(&self) -> u16 { + self.table_size_raw() + 1 + } + /// Returns the MSI-X enabled bit, which enables MSI-X if the MSI enable bit is also set in the + /// MSI capability structure. + pub const fn msix_enabled(&self) -> bool { + self.message_control() & Self::MC_MSIX_ENABLED_BIT != 0 + } + /// The MSI-X function mask, which overrides each of the vectors' mask bit, when set. + pub const fn function_mask(&self) -> bool { + self.message_control() & Self::MC_FUNCTION_MASK_BIT != 0 + } + + pub fn set_msix_enabled(&mut self, enabled: bool) { + let mut new_message_control = self.message_control(); + new_message_control &= !(Self::MC_MSIX_ENABLED_BIT); + new_message_control |= u16::from(enabled) << Self::MC_MSIX_ENABLED_SHIFT; + self.set_message_control(new_message_control); + } + + pub fn set_function_mask(&mut self, function_mask: bool) { + let mut new_message_control = self.message_control(); + new_message_control &= !(Self::MC_FUNCTION_MASK_BIT); + new_message_control |= u16::from(function_mask) << Self::MC_FUNCTION_MASK_SHIFT; + self.set_message_control(new_message_control); + } + pub const TABLE_OFFSET_MASK: u32 = 0xFFFF_FFF8; + pub const TABLE_BIR_MASK: u32 = 0x0000_0007; + + /// The table offset is guaranteed to be QWORD aligned (8 bytes). + pub const fn table_offset(&self) -> u32 { + self.b & Self::TABLE_OFFSET_MASK + } + /// The table BIR, which is used to map the offset to a memory location. + pub const fn table_bir(&self) -> u8 { + (self.b & Self::TABLE_BIR_MASK) as u8 + } + + pub fn set_table_offset(&mut self, offset: u32) { + assert_eq!(offset & Self::TABLE_OFFSET_MASK, offset, "MSI-X table offset has to be QWORD aligned"); + self.b &= !Self::TABLE_OFFSET_MASK; + self.b |= offset; + } + pub const PBA_OFFSET_MASK: u32 = 0xFFFF_FFF8; + pub const PBA_BIR_MASK: u32 = 0x0000_0007; + + /// The Pending Bit Array offset is guaranteed to be QWORD aligned (8 bytes). + pub const fn pba_offset(&self) -> u32 { + self.c & Self::PBA_OFFSET_MASK + } + /// The Pending Bit Array BIR, which is used to map the offset to a memory location. + pub const fn pba_bir(&self) -> u8 { + (self.c & Self::PBA_BIR_MASK) as u8 + } + + pub fn set_pba_offset(&mut self, offset: u32) { + assert_eq!(offset & Self::PBA_OFFSET_MASK, offset, "MSI-X Pending Bit Array offset has to be QWORD aligned"); + self.c &= !Self::PBA_OFFSET_MASK; + self.c |= offset; + } + + pub fn table_base_pointer(&self, bars: [PciBar; 6]) -> usize { + if self.table_bir() > 5 { + panic!("MSI-X Table BIR contained a reserved enum value: {}", self.table_bir()); + } + let base = bars[usize::from(self.table_bir())]; + + if let PciBar::Memory(ptr) = base { + ptr as usize + self.table_offset() as usize + } else { + panic!("MSI-X Table BIR referenced a non-memory BAR: {:?}", base); + } + } + pub fn table_pointer(&self, bars: [PciBar; 6], k: u16) -> usize { + self.table_base_pointer(bars) + k as usize * 16 + } + + pub fn pba_base_pointer(&self, bars: [PciBar; 6]) -> usize { + if self.pba_bir() > 5 { + panic!("MSI-X PBA BIR contained a reserved enum value: {}", self.pba_bir()); + } + let base = bars[usize::from(self.pba_bir())]; + + if let PciBar::Memory(ptr) = base { + ptr as usize + self.pba_offset() as usize + } else { + panic!("MSI-X PBA BIR referenced a non-memory BAR: {:?}", base); + } + } + pub fn pba_pointer_dword(&self, bars: [PciBar; 6], k: u16) -> usize { + self.pba_base_pointer(bars) + (k as usize / 32) * 4 + } + pub const fn pba_bit_dword(&self, k: u16) -> u8 { + (k % 32) as u8 + } + + pub fn pba_pointer_qword(&self, bars: [PciBar; 6], k: u16) -> usize { + self.pba_base_pointer(bars) + (k as usize / 64) * 8 + } + pub const fn pba_bit_qword(&self, k: u16) -> u8 { + (k % 64) as u8 + } + + /// Write the first DWORD into configuration space (containing the partially modifiable Message + /// Control field). + pub unsafe fn write_a(&self, writer: &W, offset: u8) { + writer.write_u32(offset, self.a) + } + /// Write the second DWORD into configuration space (containing the modifiable table + /// offset and the readonly table BIR). + pub unsafe fn write_b(&self, writer: &W, offset: u8) { + writer.write_u32(offset + 4, self.a) + } + /// Write the third DWORD into configuration space (containing the modifiable pending bit array + /// offset, and the readonly PBA BIR). + pub unsafe fn write_c(&self, writer: &W, offset: u8) { + writer.write_u32(offset + 8, self.a) + } + /// Write this capability structure back to configuration space. + pub unsafe fn write_all(&self, writer: &W, offset: u8) { + self.write_a(writer, offset); + self.write_b(writer, offset); + self.write_c(writer, offset); + } +} + +#[repr(packed)] +pub struct MsixTableEntry { + pub addr_lo: Mmio, + pub addr_hi: Mmio, + pub msg_data: Mmio, + pub vec_ctl: Mmio, +} + +#[cfg(target_arch = "x86_64")] +pub mod x86_64 { + #[repr(u8)] + pub enum TriggerMode { + Edge = 0, + Level = 1, + } + + #[repr(u8)] + pub enum LevelTriggerMode { + Deassert = 0, + Assert = 1, + } + + #[repr(u8)] + pub enum DeliveryMode { + Fixed = 0b000, + LowestPriority = 0b001, + Smi = 0b010, + // 0b011 is reserved + Nmi = 0b100, + Init = 0b101, + // 0b110 is reserved + ExtInit = 0b111, + } + + // TODO: should the reserved field be preserved? + pub const fn message_address(destination_id: u8, rh: bool, dm: bool, xx: u8) -> u32 { + 0xFEE0_0000u32 + | ((destination_id as u32) << 12) + | ((rh as u32) << 3) + | ((dm as u32) << 2) + | xx as u32 + } + pub const fn message_data(trigger_mode: TriggerMode, level_trigger_mode: LevelTriggerMode, delivery_mode: DeliveryMode, vector: u8) -> u32 { + ((trigger_mode as u32) << 15) + | ((level_trigger_mode as u32) << 14) + | ((delivery_mode as u32) << 8) + | vector as u32 + } + pub const fn message_data_level_triggered(level_trigger_mode: LevelTriggerMode, delivery_mode: DeliveryMode, vector: u8) -> u32 { + message_data(TriggerMode::Level, level_trigger_mode, delivery_mode, vector) + } + pub const fn message_data_edge_triggered(delivery_mode: DeliveryMode, vector: u8) -> u32 { + message_data(TriggerMode::Edge, LevelTriggerMode::Deassert, delivery_mode, vector) + } +} + +impl MsixTableEntry { + pub fn addr_lo(&self) -> u32 { + self.addr_lo.read() + } + pub fn addr_hi(&self) -> u32 { + self.addr_hi.read() + } + pub fn msg_data(&self) -> u32 { + self.msg_data.read() + } + pub fn vec_ctl(&self) -> u32 { + self.vec_ctl.read() + } + pub fn addr(&self) -> u64 { + u64::from(self.addr_lo()) | (u64::from(self.addr_hi()) << 32) + } + pub const VEC_CTL_MASK_BIT: u32 = 1; + + pub fn mask(&mut self) { + self.vec_ctl.writef(Self::VEC_CTL_MASK_BIT, true) + } + pub fn unmask(&mut self) { + self.vec_ctl.writef(Self::VEC_CTL_MASK_BIT, false) + } +} + +impl fmt::Debug for MsixTableEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("MsixTableEntry") + .field("addr", &self.addr()) + .field("msg_data", &self.msg_data()) + .field("vec_ctl", &self.vec_ctl()) + .finish() + } +} diff --git a/xhcid/Cargo.toml b/xhcid/Cargo.toml index b67f291149..c1ef18ce37 100644 --- a/xhcid/Cargo.toml +++ b/xhcid/Cargo.toml @@ -13,13 +13,18 @@ path = "src/lib.rs" [dependencies] bitflags = "1" +chashmap = { git = "https://gitlab.redox-os.org/redox-os/chashmap.git" } +crossbeam-channel = "0.4" +futures = "0.3" plain = "0.2" lazy_static = "1.4" -spin = "0.4" +log = "0.4" redox_event = { git = "https://gitlab.redox-os.org/redox-os/event.git" } +redox-log = { git = "https://gitlab.redox-os.org/redox-os/redox-log.git" } redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" } serde = { version = "1", features = ["derive"] } serde_json = "1" smallvec = { version = "1", features = ["serde"] } thiserror = "1" toml = "0.5" +pcid = { path = "../pcid" } diff --git a/xhcid/src/driver_interface.rs b/xhcid/src/driver_interface.rs index 1d0968f402..8b560a5cd1 100644 --- a/xhcid/src/driver_interface.rs +++ b/xhcid/src/driver_interface.rs @@ -14,7 +14,7 @@ use thiserror::Error; pub use crate::usb::{EndpointTy, ENDP_ATTR_TY_MASK}; -#[derive(Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct ConfigureEndpointsReq { /// Index into the configuration descriptors of the device descriptor. pub config_desc: u8, diff --git a/xhcid/src/main.rs b/xhcid/src/main.rs index 2cc823886d..66a84b2262 100644 --- a/xhcid/src/main.rs +++ b/xhcid/src/main.rs @@ -1,140 +1,278 @@ #[macro_use] extern crate bitflags; -extern crate event; -extern crate plain; -extern crate syscall; + +use std::convert::TryInto; +use std::fs::{self, File}; +use std::future::Future; +use std::io::{self, Read, Write}; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::pin::Pin; +use std::ptr::NonNull; +use std::sync::{Arc, Mutex}; +use std::env; + +use pcid_interface::{PcidServerHandle, PciFeature, PciFeatureInfo}; +use pcid_interface::msi::{MsiCapability, MsixCapability, MsixTableEntry}; use event::{Event, EventQueue}; -use std::cell::RefCell; -use std::fs::File; -use std::io::{Read, Result, Write}; -use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use std::sync::Arc; -use std::{env, io}; +use log::info; use syscall::data::Packet; use syscall::error::EWOULDBLOCK; use syscall::flag::{CloneFlags, PHYSMAP_NO_CACHE, PHYSMAP_WRITE}; -use syscall::scheme::SchemeMut; +use syscall::scheme::Scheme; +use syscall::io::Io; -use crate::xhci::Xhci; +use crate::xhci::{InterruptMethod, Xhci}; + +// Declare as pub so that no warnings appear due to parts of the interface code not being used by +// the driver. Since there's also a dedicated crate for the driver interface, those warnings don't +// mean anything. +pub mod driver_interface; -mod driver_interface; mod usb; mod xhci; +/// Read the local APIC id of the bootstrap processor. +fn read_bsp_apic_id() -> io::Result { + let mut buffer = [0u8; 8]; + + let mut file = File::open("irq:bsp")?; + let bytes_read = file.read(&mut buffer)?; + + Ok(if bytes_read == 8 { + u64::from_le_bytes(buffer) as u32 + } else if bytes_read == 4 { + u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]) + } else { + panic!("`irq:` scheme responded with {} bytes, expected {}", bytes_read, std::mem::size_of::()); + }) +} +/// Allocate an interrupt vector, located at the BSP's IDT. +fn allocate_interrupt_vector() -> io::Result> { + let available_irqs = fs::read_dir("irq:")?; + + for entry in available_irqs { + let entry = entry?; + let path = entry.path(); + + let file_name = match path.file_name() { + Some(f) => f, + None => continue, + }; + + let path_str = match file_name.to_str() { + Some(s) => s, + None => continue, + }; + + if let Ok(irq_number) = path_str.parse::() { + // if found, reserve the irq + let irq_handle = File::create(format!("irq:{}", irq_number))?; + let interrupt_vector = irq_number + 32; + return Ok(Some((interrupt_vector, irq_handle))); + } + } + Ok(None) +} + +async fn handle_packet(hci: Arc, packet: Packet) -> Packet { + todo!() +} + fn main() { let mut args = env::args().skip(1); let mut name = args.next().expect("xhcid: no name provided"); name.push_str("_xhci"); - let bar_str = args.next().expect("xhcid: no address provided"); - let bar = usize::from_str_radix(&bar_str, 16).expect("xhcid: failed to parse address"); + // Daemonize + if unsafe { syscall::clone(CloneFlags::empty()).unwrap() } != 0 { + return; + } - let irq_str = args.next().expect("xhcid: no IRQ provided"); - let irq = irq_str.parse::().expect("xhcid: failed to parse irq"); + match redox_log::RedoxLogger::new("usb", "host", "xhci.log") { + Ok(logger) => match logger.with_stdout_mirror().enable() { + Ok(_) => { + println!("xhcid: enabled logger"); + log::set_max_level(log::LevelFilter::Debug); + } + Err(error) => eprintln!("xhcid: failed to set default logger: {}", error), + } + Err(error) => eprintln!("xhcid: failed to initialize logger: {}", error), + } + + let mut pcid_handle = PcidServerHandle::connect_default().expect("xhcid: failed to setup channel to pcid"); + let pci_config = pcid_handle.fetch_config().expect("xhcid: failed to fetch config"); + info!("XHCI PCI CONFIG: {:?}", pci_config); + + let bar = pci_config.func.bars[0]; + let irq = pci_config.func.legacy_interrupt_line; + + let bar_ptr = match bar { + pcid_interface::PciBar::Memory(ptr) => ptr, + other => panic!("Expected memory bar, found {}", other), + }; + + let address = unsafe { + syscall::physmap(bar_ptr as usize, 65536, PHYSMAP_WRITE | PHYSMAP_NO_CACHE) + .expect("xhcid: failed to map address") + }; + + let all_pci_features = pcid_handle.fetch_all_features().expect("xhcid: failed to fetch pci features"); + info!("XHCI PCI FEATURES: {:?}", all_pci_features); + + let (has_msi, mut msi_enabled) = all_pci_features.iter().map(|(feature, status)| (feature.is_msi(), status.is_enabled())).find(|&(f, _)| f).unwrap_or((false, false)); + let (has_msix, mut msix_enabled) = all_pci_features.iter().map(|(feature, status)| (feature.is_msix(), status.is_enabled())).find(|&(f, _)| f).unwrap_or((false, false)); + + dbg!(has_msi, msi_enabled); + dbg!(has_msix, msix_enabled); + + if has_msi && !msi_enabled && !has_msix { + pcid_handle.enable_feature(PciFeature::Msi).expect("xhcid: failed to enable MSI"); + info!("Enabled MSI"); + msi_enabled = true; + } + if has_msix && !msix_enabled { + pcid_handle.enable_feature(PciFeature::MsiX).expect("xhcid: failed to enable MSI-X"); + info!("Enabled MSI-X"); + msix_enabled = true; + } + + let (mut irq_file, interrupt_method) = if msi_enabled && !msix_enabled { + let mut capability = match pcid_handle.feature_info(PciFeature::MsiX).expect("xhcid: failed to retrieve the MSI capability structure from pcid") { + PciFeatureInfo::Msi(s) => s, + PciFeatureInfo::MsiX(_) => panic!(), + }; + // use one vector + capability.set_multi_message_enabled(0); + + todo!("msi (msix is implemented though)") + } else if msix_enabled { + let capability = match pcid_handle.feature_info(PciFeature::MsiX).expect("xhcid: failed to retrieve the MSI-X capability structure from pcid") { + PciFeatureInfo::Msi(_) => panic!(), + PciFeatureInfo::MsiX(s) => s, + }; + let table_size = capability.table_size(); + let table_base = capability.table_base_pointer(pci_config.func.bars); + let table_min_length = table_size * 16; + let pba_min_length = crate::xhci::scheme::div_round_up(table_size, 8); + + let pba_base = capability.pba_base_pointer(pci_config.func.bars); + dbg!(table_size, table_base, table_min_length, pba_base); + + if !(bar_ptr..bar_ptr + 65536).contains(&(table_base as u32 + table_min_length as u32)) { + todo!() + } + if !(bar_ptr..bar_ptr + 65536).contains(&(pba_base as u32 + pba_min_length as u32)) { + todo!() + } + + let virt_table_base = ((table_base - bar_ptr as usize) + address) as *mut MsixTableEntry; + let virt_pba_base = ((pba_base - bar_ptr as usize) + address) as *mut u64; + + let mut info = xhci::MsixInfo { + virt_table_base: NonNull::new(virt_table_base).unwrap(), + virt_pba_base: NonNull::new(virt_pba_base).unwrap(), + capability, + }; + + // Allocate one msi vector. + + { + use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix}; + + // primary interrupter + let k = 0; + + assert_eq!(std::mem::size_of::(), 16); + let table_entry_pointer = info.table_entry_pointer(k); + + let destination_id = read_bsp_apic_id().expect("xhcid: failed to read BSP apic id"); + let rh = false; + let dm = false; + let addr = x86_64_msix::message_address(destination_id.try_into().expect("xhcid: BSP apic id couldn't fit u8"), rh, dm, 0b00); + + let (vector, interrupt_handle) = allocate_interrupt_vector().expect("xhcid: failed to allocate interrupt vector").expect("xhcid: no interrupt vectors left"); + let msg_data = x86_64_msix::message_data_edge_triggered(DeliveryMode::Fixed, vector); + + table_entry_pointer.addr_lo.write(addr); + table_entry_pointer.addr_hi.write(0); + table_entry_pointer.msg_data.write(msg_data); + table_entry_pointer.vec_ctl.writef(MsixTableEntry::VEC_CTL_MASK_BIT, false); + + (Some(interrupt_handle), InterruptMethod::MsiX(Mutex::new(info))) + } + } else if pci_config.func.legacy_interrupt_pin.is_some() { + // legacy INTx# interrupt pins. + (Some(File::open(format!("irq:{}", irq)).expect("xhcid: failed to open legacy IRQ file")), InterruptMethod::Intx) + } else { + // no interrupts at all + (None, InterruptMethod::Polling) + }; + + std::thread::sleep(std::time::Duration::from_millis(300)); print!( "{}", - format!(" + XHCI {} on: {:X} IRQ: {}\n", name, bar, irq) + format!(" + XHCI {} on: {} IRQ: {}\n", name, bar, irq) ); - // Daemonize - if unsafe { syscall::clone(CloneFlags::empty()).unwrap() } == 0 { - let socket_fd = syscall::open( - format!(":usb/{}", name), - syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK, - ) - .expect("xhcid: failed to create usb scheme"); - let socket = Arc::new(RefCell::new(unsafe { - File::from_raw_fd(socket_fd as RawFd) - })); + let socket_fd = syscall::open( + format!(":usb/{}", name), + syscall::O_RDWR | syscall::O_CREAT, + ) + .expect("xhcid: failed to create usb scheme"); + let socket = Arc::new(Mutex::new(unsafe { + File::from_raw_fd(socket_fd as RawFd) + })); - let mut irq_file = - File::open(format!("irq:{}", irq)).expect("xhcid: failed to open IRQ file"); + let hci = Arc::new(Xhci::new(name, address, interrupt_method, pcid_handle).expect("xhcid: failed to allocate device")); + xhci::start_irq_reactor(&hci, irq_file); + futures::executor::block_on(hci.probe()).expect("xhcid: failed to probe"); - let address = unsafe { - syscall::physmap(bar, 65536, PHYSMAP_WRITE | PHYSMAP_NO_CACHE) - .expect("xhcid: failed to map address") - }; - { - let hci = Arc::new(RefCell::new( - Xhci::new(name, address).expect("xhcid: failed to allocate device"), - )); + let mut event_queue = + EventQueue::<()>::new().expect("xhcid: failed to create event queue"); - hci.borrow_mut().probe().expect("xhcid: failed to probe"); + syscall::setrens(0, 0).expect("xhcid: failed to enter null namespace"); - let mut event_queue = - EventQueue::<()>::new().expect("xhcid: failed to create event queue"); + let todo = Arc::new(Mutex::new(Vec::::new())); + let todo_futures = Arc::new(Mutex::new(Vec:: + Send + Sync + 'static>>>::new())); - syscall::setrens(0, 0).expect("xhcid: failed to enter null namespace"); + let socket_fd = socket.lock().unwrap().as_raw_fd(); + let socket_packet = socket.clone(); + event_queue + .add(socket_fd, move |_| -> io::Result> { + let mut socket = socket_packet.lock().unwrap(); + let mut todo = todo.lock().unwrap(); - let todo = Arc::new(RefCell::new(Vec::::new())); + loop { + let mut packet = Packet::default(); + match socket.read(&mut packet) { + Ok(0) => break, + Ok(_) => (), + Err(err) => return Err(err), + } - let hci_irq = hci.clone(); - let socket_irq = socket.clone(); - let todo_irq = todo.clone(); - event_queue - .add(irq_file.as_raw_fd(), move |_| -> Result> { - let mut irq = [0; 8]; - irq_file.read(&mut irq)?; + let a = packet.a; + hci.handle(&mut packet); + if packet.a == (-EWOULDBLOCK) as usize { + packet.a = a; + todo.push(packet); + } else { + socket.write(&packet)?; + } + } + Ok(None) + }) + .expect("xhcid: failed to catch events on scheme file"); - if hci_irq.borrow_mut().trigger_irq() { - irq_file.write(&mut irq)?; + event_queue + .trigger_all(Event { fd: 0, flags: 0 }) + .expect("xhcid: failed to trigger events"); - let mut todo = todo_irq.borrow_mut(); - let mut i = 0; - while i < todo.len() { - let a = todo[i].a; - hci_irq.borrow_mut().handle(&mut todo[i]); - if todo[i].a == (-EWOULDBLOCK) as usize { - todo[i].a = a; - i += 1; - } else { - socket_irq.borrow_mut().write(&mut todo[i])?; - todo.remove(i); - } - } - } + event_queue.run().expect("xhcid: failed to handle events"); - Ok(None) - }) - .expect("xhcid: failed to catch events on IRQ file"); - - let socket_fd = socket.borrow().as_raw_fd(); - let socket_packet = socket.clone(); - event_queue - .add(socket_fd, move |_| -> Result> { - loop { - let mut packet = Packet::default(); - match socket_packet.borrow_mut().read(&mut packet) { - Ok(0) => break, - Err(err) if err.kind() == io::ErrorKind::WouldBlock => break, - Ok(_) => (), - Err(err) => return Err(err), - } - - let a = packet.a; - hci.borrow_mut().handle(&mut packet); - if packet.a == (-EWOULDBLOCK) as usize { - packet.a = a; - todo.borrow_mut().push(packet); - } else { - socket_packet.borrow_mut().write(&mut packet)?; - } - } - Ok(None) - }) - .expect("xhcid: failed to catch events on scheme file"); - - event_queue - .trigger_all(Event { fd: 0, flags: 0 }) - .expect("xhcid: failed to trigger events"); - - event_queue.run().expect("xhcid: failed to handle events"); - } - unsafe { - let _ = syscall::physunmap(address); - } + unsafe { + let _ = syscall::physunmap(address); } } diff --git a/xhcid/src/xhci/capability.rs b/xhcid/src/xhci/capability.rs index 59e841b24e..4ec7b10a15 100644 --- a/xhcid/src/xhci/capability.rs +++ b/xhcid/src/xhci/capability.rs @@ -27,6 +27,13 @@ pub const HCS_PARAMS1_MAX_PORTS_SHIFT: u8 = 24; pub const HCS_PARAMS1_MAX_SLOTS_MASK: u32 = 0x0000_00FF; pub const HCS_PARAMS1_MAX_SLOTS_SHIFT: u8 = 0; +pub const HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_LO_MASK: u32 = 0xF800_0000; +pub const HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_LO_SHIFT: u8 = 27; +pub const HCS_PARAMS2_SPR_BIT: u32 = 1 << HCS_PARAMS2_SPR_SHIFT; +pub const HCS_PARAMS2_SPR_SHIFT: u8 = 26; +pub const HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_HI_MASK: u32 = 0x03E0_0000; +pub const HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_HI_SHIFT: u8 = 21; + impl CapabilityRegs { pub fn lec(&self) -> bool { self.hcc_params2.readf(HCC_PARAMS2_LEC_BIT) @@ -48,4 +55,17 @@ impl CapabilityRegs { pub fn ext_caps_ptr_in_dwords(&self) -> u16 { ((self.hcc_params1.read() & HCC_PARAMS1_XECP_MASK) >> HCC_PARAMS1_XECP_SHIFT) as u16 } + pub fn max_scratchpad_bufs_lo(&self) -> u8 { + ((self.hcs_params2.read() & HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_LO_MASK) >> HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_LO_SHIFT) as u8 + } + pub fn spr(&self) -> bool { + self.hcs_params2.readf(HCS_PARAMS2_SPR_BIT) + } + pub fn max_scratchpad_bufs_hi(&self) -> u8 { + ((self.hcs_params2.read() & HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_HI_MASK) >> HCS_PARAMS2_MAX_SCRATCHPAD_BUFS_HI_SHIFT) as u8 + } + pub fn max_scratchpad_bufs(&self) -> u16 { + u16::from(self.max_scratchpad_bufs_lo()) + | (u16::from(self.max_scratchpad_bufs_hi()) << 5) + } } diff --git a/xhcid/src/xhci/command.rs b/xhcid/src/xhci/command.rs deleted file mode 100644 index ca78d592fe..0000000000 --- a/xhcid/src/xhci/command.rs +++ /dev/null @@ -1,45 +0,0 @@ -use syscall::error::Result; - -use super::event::EventRing; -use super::ring::Ring; -use super::trb::Trb; - -pub struct CommandRing { - pub ring: Ring, - pub events: EventRing, -} - -impl CommandRing { - pub fn new() -> Result { - Ok(CommandRing { - ring: Ring::new(16, true)?, - events: EventRing::new()?, - }) - } - - pub fn crcr(&self) -> u64 { - self.ring.register() - } - - pub fn erdp(&self) -> u64 { - self.events.ring.register() - } - - pub fn erstba(&self) -> u64 { - self.events.ste.physical() as u64 - } - - pub fn next(&mut self) -> (&mut Trb, bool, &mut Trb) { - let cmd = self.ring.next(); - let event = self.events.next(); - (cmd.0, cmd.1, event) - } - - pub fn next_cmd(&mut self) -> (&mut Trb, bool) { - self.ring.next() - } - - pub fn next_event(&mut self) -> &mut Trb { - self.events.next() - } -} diff --git a/xhcid/src/xhci/context.rs b/xhcid/src/xhci/context.rs index 74944da174..e971ad28a7 100644 --- a/xhcid/src/xhci/context.rs +++ b/xhcid/src/xhci/context.rs @@ -1,5 +1,6 @@ use std::collections::BTreeMap; +use log::debug; use syscall::error::Result; use syscall::io::{Dma, Io, Mmio}; @@ -59,7 +60,7 @@ pub struct InputContext { } impl InputContext { pub fn dump_control(&self) { - println!( + debug!( "INPUT CONTEXT: {} {} [{} {} {} {} {}] {}", self.drop_context.read(), self.add_context.read(), @@ -75,7 +76,7 @@ impl InputContext { pub struct DeviceContextList { pub dcbaa: Dma<[u64; 256]>, - pub contexts: Vec>, + pub contexts: Box<[Dma]>, } impl DeviceContextList { @@ -91,8 +92,8 @@ impl DeviceContextList { } Ok(DeviceContextList { - dcbaa: dcbaa, - contexts: contexts, + dcbaa, + contexts: contexts.into_boxed_slice(), }) } @@ -159,3 +160,38 @@ impl StreamContextArray { self.contexts.physical() as u64 } } + +#[repr(packed)] +pub struct ScratchpadBufferEntry { + pub value: Mmio, +} +impl ScratchpadBufferEntry { + pub fn set_addr(&mut self, addr: u64) { + self.value.write(addr); + } +} + +pub struct ScratchpadBufferArray { + pub entries: Dma<[ScratchpadBufferEntry]>, + pub pages: Vec, +} +impl ScratchpadBufferArray { + pub fn new(page_size: usize, entries: u16) -> Result { + let mut entries = unsafe { Dma::zeroed_unsized(entries as usize)? }; + + let pages = entries.iter_mut().map(|entry: &mut ScratchpadBufferEntry| -> Result { + let pointer = unsafe { syscall::physalloc(page_size)? }; + assert_eq!(pointer & 0xFFFF_FFFF_FFFF_F000, pointer, "physically allocated pointer (physalloc) wasn't 4k page-aligned"); + entry.set_addr(pointer as u64); + Ok(pointer) + }).collect::, _>>()?; + + Ok(Self { + entries, + pages, + }) + } + pub fn register(&self) -> usize { + self.entries.physical() + } +} diff --git a/xhcid/src/xhci/event.rs b/xhcid/src/xhci/event.rs index b8301b34be..b3e0af0e35 100644 --- a/xhcid/src/xhci/event.rs +++ b/xhcid/src/xhci/event.rs @@ -12,6 +12,7 @@ pub struct EventRingSte { _rsvd2: Mmio, } +// TODO: Use atomic operations, and perhaps an occasional lock for reallocating. pub struct EventRing { pub ste: Dma<[EventRingSte]>, pub ring: Ring, @@ -33,4 +34,10 @@ impl EventRing { pub fn next(&mut self) -> &mut Trb { self.ring.next().0 } + pub fn erdp(&self) -> u64 { + self.ring.register() & 0xFFFF_FFFF_FFFF_FFF0 + } + pub fn erstba(&self) -> u64 { + self.ste.physical() as u64 + } } diff --git a/xhcid/src/xhci/irq_reactor.rs b/xhcid/src/xhci/irq_reactor.rs new file mode 100644 index 0000000000..581e2214d2 --- /dev/null +++ b/xhcid/src/xhci/irq_reactor.rs @@ -0,0 +1,468 @@ +use std::collections::BTreeMap; +use std::fs::File; +use std::future::Future; +use std::io::prelude::*; +use std::pin::Pin; +use std::sync::{Arc, Mutex}; +use std::sync::atomic::{self, AtomicUsize}; +use std::{io, mem, task, thread}; + +use std::os::unix::io::AsRawFd; + +use crossbeam_channel::{Sender, Receiver}; +use log::{debug, error, info, warn, trace}; +use futures::Stream; +use syscall::Io; + +use event::{Event, EventQueue}; + +use super::Xhci; +use super::ring::Ring; +use super::trb::{Trb, TrbCompletionCode, TrbType}; +use super::event::EventRing; + +/// Short-term states (as in, they are removed when the waker is consumed, but probably pushed back +/// by the future unless it completed). +#[derive(Debug)] +pub struct State { + waker: task::Waker, + kind: StateKind, + message: Arc>>, + is_isoch_or_vf: bool, +} + +#[derive(Debug)] +pub struct NextEventTrb { + pub event_trb: Trb, + pub src_trb: Option, +} + +// TODO: Perhaps all of the transfer rings used by the xHC should be stored linearly, and then +// indexed using this struct instead. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct RingId { + pub port: u8, + pub endpoint_num: u8, + pub stream_id: u16, +} +impl RingId { + pub const fn default_control_pipe(port: u8) -> Self { + Self { + port, + endpoint_num: 0, + stream_id: 0, + } + } +} + +/// The state specific to a TRB-type. Since some of the event TDs may asynchronously appear, for +/// example the Command Completion Event and the Transfer Event TDs, they have to be +/// distinguishable. Luckily, the xHC also gives us the actual (physical) pointer to the source +/// TRB, from the command ring, unless the event TD has one the completion codes Ring Underrun, +/// Ring Overrun, or VF Event Ring Full Error. When these errors are encountered, it simply +/// indicates that the commands causing the errors continue to be pending, and thus no information +/// is lost. +#[derive(Clone, Copy, Debug)] +pub enum StateKind { + CommandCompletion { phys_ptr: u64 }, + Transfer { phys_ptr: u64, ring_id: RingId }, + Other(TrbType), +} + +impl StateKind { + pub fn trb_type(&self) -> TrbType { + match self { + &Self::CommandCompletion { .. } => TrbType::CommandCompletion, + &Self::Transfer { .. } => TrbType::Transfer, + &Self::Other(ty) => ty, + } + } +} + + +pub struct IrqReactor { + hci: Arc, + irq_file: Option, + receiver: Receiver, + + states: Vec, + + // TODO: Since the IRQ reactor is the only part of this driver that gets event TRBs, perhaps + // the event ring should be owned here? +} + +pub type NewPendingTrb = State; + +impl IrqReactor { + pub fn new(hci: Arc, receiver: Receiver, irq_file: Option) -> Self { + Self { + hci, + irq_file, + receiver, + states: Vec::new(), + } + } + // TODO: Configure the amount of time wait when no more work can be done (for IRQ-less polling). + fn pause(&self) { + std::thread::yield_now(); + } + fn run_polling(mut self) { + debug!("Running IRQ reactor in polling mode."); + let hci_clone = Arc::clone(&self.hci); + + 'event_loop: loop { + let mut event_ring_guard = hci_clone.primary_event_ring.lock().unwrap(); + + let index = event_ring_guard.ring.next_index(); + + let mut trb; + + 'busy_waiting: loop { + trb = &event_ring_guard.ring.trbs[index]; + + if trb.completion_code() == TrbCompletionCode::Invalid as u8 { + self.pause(); + continue 'busy_waiting; + } + } + if self.check_event_ring_full(trb.clone()) { continue 'event_loop } + + self.handle_requests(); + self.acknowledge(trb.clone()); + + self.update_erdp(&*event_ring_guard); + } + } + fn run_with_irq_file(mut self) { + debug!("Running IRQ reactor with IRQ file and event queue"); + + let hci_clone = Arc::clone(&self.hci); + let mut event_queue = EventQueue::<()>::new().expect("xhcid irq_reactor: failed to create IRQ event queue"); + let irq_fd = self.irq_file.as_ref().unwrap().as_raw_fd(); + + let mut event_trb_index = { hci_clone.primary_event_ring.lock().unwrap().ring.next_index() }; + + event_queue.add(irq_fd, move |_| -> io::Result> { + trace!("IRQ event queue notified"); + let mut buffer = [0u8; 8]; + + let _ = self.irq_file.as_mut().unwrap().read(&mut buffer).expect("Failed to read from irq scheme"); + + if !self.hci.received_irq() { + // continue only when an IRQ to this device was received + trace!("no interrupt pending"); + return Ok(None); + } + + trace!("IRQ reactor received an IRQ"); + + let _ = self.irq_file.as_mut().unwrap().write(&buffer); + + // TODO: More event rings, probably even with different IRQs. + + let mut event_ring = hci_clone.primary_event_ring.lock().unwrap(); + + let mut count = 0; + + 'trb_loop: loop { + let event_trb = &mut event_ring.ring.trbs[event_trb_index]; + + if event_trb.completion_code() == TrbCompletionCode::Invalid as u8 { + if count == 0 { warn!("xhci: Received interrupt, but no event was found in the event ring. Ignoring interrupt.") } + // no more events were found, continue the loop + return Ok(None); + } else { count += 1 } + + trace!("Found event TRB: {:?}", event_trb); + + if self.check_event_ring_full(event_trb.clone()) { + info!("Had to resize event TRB, retrying..."); + hci_clone.event_handler_finished(); + return Ok(None); + } + + self.handle_requests(); + self.acknowledge(event_trb.clone()); + + event_trb.reserved(false); + + self.update_erdp(&*event_ring); + + event_trb_index = event_ring.ring.next_index(); + } + }).expect("xhcid: failed to catch irq events"); + event_queue.run().expect("xhcid: failed to run IRQ event queue"); + } + fn update_erdp(&self, event_ring: &EventRing) { + let dequeue_pointer_and_dcs = event_ring.erdp(); + let dequeue_pointer = dequeue_pointer_and_dcs & 0xFFFF_FFFF_FFFF_FFFE; + assert_eq!(dequeue_pointer & 0xFFFF_FFFF_FFFF_FFF0, dequeue_pointer, "unaligned ERDP received from primary event ring"); + + debug!("Updated ERDP to {:#0x}", dequeue_pointer); + + self.hci.run.lock().unwrap().ints[0].erdp.write(dequeue_pointer); + } + fn handle_requests(&mut self) { + self.states.extend(self.receiver.try_iter().inspect(|req| trace!("Received request: {:?}", req))); + } + fn acknowledge(&mut self, trb: Trb) { + let mut index = 0; + + loop { + if index >= self.states.len() { break } + + match self.states[index].kind { + StateKind::CommandCompletion { phys_ptr } if dbg!(trb.trb_type()) == TrbType::CommandCompletion as u8 => if dbg!(trb.completion_trb_pointer()) == Some(phys_ptr) { + trace!("Found matching command completion future"); + let state = self.states.remove(index); + + // Before waking, it's crucial that the command TRB that generated this event + // is fetched before removing this event TRB from the queue. + let command_trb = match self.hci.cmd.lock().unwrap().phys_addr_to_entry_mut(phys_ptr) { + Some(command_trb) => { + let t = command_trb.clone(); + command_trb.reserved(false); + t + }, + None => { + warn!("The xHC supplied a pointer to a command TRB that was outside the known command ring bounds. Ignoring event TRB {:?}.", trb); + continue; + } + }; + + // TODO: Validate the command TRB. + *state.message.lock().unwrap() = Some(NextEventTrb { + src_trb: Some(command_trb.clone()), + event_trb: trb.clone(), + }); + + trace!("Waking up future with waker: {:?}", state.waker); + state.waker.wake(); + + return; + } else if trb.completion_trb_pointer().is_none() { + warn!("Command TRB somehow resulted in an error that only can be caused by transfer TRBs. Ignoring event TRB: {:?}.", trb); + continue; + } else { + // The event TRB simply didn't match the current future + continue; + } + + StateKind::Transfer { phys_ptr, ring_id } if trb.trb_type() == TrbType::Transfer as u8 => if let Some(src_trb) = trb.transfer_event_trb_pointer().map(|ptr| self.hci.get_transfer_trb(ptr, ring_id)).flatten() { + if trb.transfer_event_trb_pointer() == Some(phys_ptr) { + // Give the source transfer TRB together with the event TRB, to the future. + + let state = self.states.remove(index); + *state.message.lock().unwrap() = Some(NextEventTrb { + src_trb: Some(src_trb), + event_trb: trb.clone(), + }); + state.waker.wake(); + return; + } else if trb.transfer_event_trb_pointer().is_none() { + // Ring Overrun, Ring Underrun, or Virtual Function Event Ring Full. + // + // These errors are caused when either an isoch transfer that shall write data, doesn't + // have any data since the ring is empty, or if an isoch receive is impossible due to a + // full ring. The Virtual Function Event Ring Full is only for Virtual Machine + // Managers, and since this isn't implemented yet, they are irrelevant. + // + // The best solution here is to differentiate between isoch transfers (and + // virtual function event rings when virtualization gets implemented), with + // regular commands and transfers, and send the error TRB to all of them, or + // possibly an error code wrapped in a Result. + self.acknowledge_failed_transfer_trbs(trb); + return; + } else { + // The event TRB simply didn't match the current future + continue; + } + } else { continue } + + StateKind::Other(trb_type) if trb_type as u8 == trb.trb_type() => { + let state = self.states.remove(index); + state.waker.wake(); + return; + } + + _ => { + index += 1; + continue; + } + } + } + warn!("Lost event TRB: {:?}", trb); + } + fn acknowledge_failed_transfer_trbs(&mut self, trb: Trb) { + let mut index = 0; + + loop { + if ! self.states[index].is_isoch_or_vf { + index += 1; + if index >= self.states.len() { + break; + } + continue; + } + let state = self.states.remove(index); + *state.message.lock().unwrap() = Some(NextEventTrb { + event_trb: trb.clone(), + src_trb: None, + }); + state.waker.wake(); + } + } + /// Checks if an event TRB is a Host Controller Event, with the completion code Event Ring + /// Full. If so, it grows the event ring. The return value is whether the event ring was full, + /// and then grown. + fn check_event_ring_full(&mut self, event_trb: Trb) -> bool { + let had_event_ring_full_error = event_trb.trb_type() == TrbType::HostController as u8 && event_trb.completion_code() == TrbCompletionCode::EventRingFull as u8; + + if had_event_ring_full_error { + self.grow_event_ring(); + } + had_event_ring_full_error + } + /// Grows the event ring + fn grow_event_ring(&mut self) { + // TODO + error!("TODO: grow event ring"); + } + + pub fn run(mut self) { + if self.irq_file.is_some() { + self.run_with_irq_file(); + } else { + self.run_polling(); + } + } +} + +struct FutureState { + message: Arc>>, + is_isoch_or_vf: bool, + state_kind: StateKind, +} + +enum EventTrbFuture { + Pending { state: FutureState, sender: Sender, }, + Finished, +} + +impl Future for EventTrbFuture { + type Output = NextEventTrb; + + fn poll(self: Pin<&mut Self>, context: &mut task::Context) -> task::Poll { + let this = self.get_mut(); + + let message = match this { + &mut Self::Pending { ref state, ref sender } => match state.message.lock().unwrap().take() { + Some(message) => message, + + None => { + sender.send(State { + message: Arc::clone(&state.message), + is_isoch_or_vf: state.is_isoch_or_vf, + kind: state.state_kind, + waker: context.waker().clone(), + }).expect("IRQ reactor thread unexpectedly stopped"); + + return task::Poll::Pending; + } + } + &mut Self::Finished => panic!("Polling finished EventTrbFuture again."), + }; + *this = Self::Finished; + task::Poll::Ready(message) + } +} + +impl Xhci { + pub fn get_transfer_trb(&self, paddr: u64, id: RingId) -> Option { + self.with_ring(id, |ring| ring.phys_addr_to_entry(paddr)).flatten() + } + pub fn with_ring T>(&self, id: RingId, function: F) -> Option { + use super::RingOrStreams; + + let slot_state = self.port_states.get(&(id.port as usize))?; + let endpoint_state = slot_state.endpoint_states.get(&id.endpoint_num)?; + + let ring_ref = match endpoint_state.transfer { + RingOrStreams::Ring(ref ring) => ring, + RingOrStreams::Streams(ref ctx_arr) => ctx_arr.rings.get(&id.stream_id)?, + }; + + Some(function(ring_ref)) + } + pub fn with_ring_mut T>(&self, id: RingId, function: F) -> Option { + use super::RingOrStreams; + + let mut slot_state = self.port_states.get_mut(&(id.port as usize))?; + let mut endpoint_state = slot_state.endpoint_states.get_mut(&id.endpoint_num)?; + + let ring_ref = match endpoint_state.transfer { + RingOrStreams::Ring(ref mut ring) => ring, + RingOrStreams::Streams(ref mut ctx_arr) => ctx_arr.rings.get_mut(&id.stream_id)?, + }; + + Some(function(ring_ref)) + } + pub fn next_transfer_event_trb(&self, ring_id: RingId, ring: &Ring, trb: &Trb) -> impl Future + Send + Sync + 'static { + if ! trb.is_transfer_trb() { + panic!("Invalid TRB type given to next_transfer_event_trb(): {} (TRB {:?}. Expected transfer TRB.", trb.trb_type(), trb) + } + + let is_isoch_or_vf = trb.trb_type() == TrbType::Isoch as u8; + + EventTrbFuture::Pending { + state: FutureState { + is_isoch_or_vf, + state_kind: StateKind::Transfer { + ring_id, + phys_ptr: ring.trb_phys_ptr(trb), + }, + message: Arc::new(Mutex::new(None)), + }, + sender: self.irq_reactor_sender.clone(), + } + } + pub fn next_command_completion_event_trb(&self, command_ring: &Ring, trb: &Trb) -> impl Future + Send + Sync + 'static { + if ! trb.is_command_trb() { + panic!("Invalid TRB type given to next_command_completion_event_trb(): {} (TRB {:?}. Expected command TRB.", trb.trb_type(), trb) + } + dbg!(command_ring.trbs.physical()); + EventTrbFuture::Pending { + state: FutureState { + // This is only possible for transfers if they are isochronous, or for Force Event TRBs (virtualization). + is_isoch_or_vf: false, + state_kind: StateKind::CommandCompletion { + phys_ptr: command_ring.trb_phys_ptr(trb), + }, + message: Arc::new(Mutex::new(None)), + }, + sender: self.irq_reactor_sender.clone(), + } + } + pub fn next_misc_event_trb(&self, trb_type: TrbType) -> impl Future + Send + Sync + 'static { + let valid_trb_types = [ + TrbType::PortStatusChange as u8, + TrbType::BandwidthRequest as u8, + TrbType::Doorbell as u8, + TrbType::HostController as u8, + TrbType::DeviceNotification as u8, + TrbType::MfindexWrap as u8, + ]; + if ! valid_trb_types.contains(&(trb_type as u8)) { + panic!("Invalid TRB type given to next_misc_event_trb(): {:?}. Only event TRB types that are neither transfer events or command completion events can be used.", trb_type) + } + EventTrbFuture::Pending { + state: FutureState { + is_isoch_or_vf: false, + state_kind: StateKind::Other(trb_type), + message: Arc::new(Mutex::new(None)), + }, + sender: self.irq_reactor_sender.clone(), + } + } + +} diff --git a/xhcid/src/xhci/mod.rs b/xhcid/src/xhci/mod.rs index ed4a0e29e8..d8f3348f71 100644 --- a/xhcid/src/xhci/mod.rs +++ b/xhcid/src/xhci/mod.rs @@ -1,107 +1,156 @@ use std::collections::BTreeMap; use std::convert::TryFrom; +use std::fs::File; +use std::future::Future; use std::pin::Pin; use std::ptr::NonNull; -use std::sync::{atomic::AtomicBool, Arc, Mutex, Weak}; -use std::{mem, process, slice, sync::atomic, task}; +use std::sync::{Arc, Mutex, MutexGuard, Weak}; +use std::sync::atomic::{AtomicBool, AtomicUsize}; +use std::{mem, process, slice, sync::atomic, task, thread}; + +use chashmap::CHashMap; +use crossbeam_channel::{Receiver, Sender}; +use log::{debug, error, info, trace, warn}; use serde::Deserialize; -use syscall::error::{Error, Result, EBADF, EBADMSG, ENOENT}; +use syscall::error::{Error, Result, EBADF, EBADMSG, ENOENT, EIO}; +use syscall::flag::O_RDONLY; use syscall::io::{Dma, Io}; use crate::usb; +use pcid_interface::msi::{MsixTableEntry, MsixCapability}; +use pcid_interface::{PcidServerHandle, PciFeature}; + mod capability; -mod command; mod context; mod doorbell; mod event; mod extended; +pub mod irq_reactor; mod operational; mod port; mod ring; mod runtime; -mod scheme; +pub mod scheme; mod trb; use self::capability::CapabilityRegs; -use self::command::CommandRing; -use self::context::{DeviceContextList, InputContext, StreamContextArray}; +use self::context::{DeviceContextList, InputContext, ScratchpadBufferArray, StreamContextArray}; use self::doorbell::Doorbell; +use self::irq_reactor::{IrqReactor, NewPendingTrb, RingId}; +use self::event::EventRing; use self::extended::{CapabilityId, ExtendedCapabilitiesIter, ProtocolSpeed, SupportedProtoCap}; use self::operational::OperationalRegs; use self::port::Port; use self::ring::Ring; use self::runtime::{Interrupter, RuntimeRegs}; -use self::trb::{TransferKind, TrbCompletionCode, TrbType}; +use self::trb::{TransferKind, Trb, TrbCompletionCode, TrbType}; use self::scheme::EndpIfState; use crate::driver_interface::*; -struct Device<'a> { - ring: &'a mut Ring, - cmd: &'a mut CommandRing, - db: &'a mut Doorbell, - int: &'a mut Interrupter, +pub enum InterruptMethod { + /// No interrupts whatsoever; the driver will instead rely on polling event rings. + Polling, + + /// Legacy PCI INTx# interrupt pin. + Intx, + + /// Message signaled interrupts. + Msi, + + /// Extended message signaled interrupts. + MsiX(Mutex), } -impl<'a> Device<'a> { - fn get_desc(&mut self, kind: usb::DescriptorKind, index: u8, desc: &mut Dma) { +pub struct MsixInfo { + pub virt_table_base: NonNull, + pub virt_pba_base: NonNull, + pub capability: MsixCapability, +} +impl MsixInfo { + pub unsafe fn table_entry_pointer_unchecked(&mut self, k: usize) -> &mut MsixTableEntry { + &mut *self.virt_table_base.as_ptr().offset(k as isize) + } + pub fn table_entry_pointer(&mut self, k: usize) -> &mut MsixTableEntry { + assert!(k < self.capability.table_size() as usize); + unsafe { self.table_entry_pointer_unchecked(k) } + } + pub unsafe fn pba_pointer_unchecked(&mut self, k: usize) -> &mut u64 { + &mut *self.virt_pba_base.as_ptr().offset(k as isize) + } + pub fn pba_pointer(&mut self, k: usize) -> &mut u64 { + assert!(k < self.capability.table_size() as usize); + unsafe { self.pba_pointer_unchecked(k) } + } + pub fn pba(&mut self, k: usize) -> bool { + let byte = k / 64; + let bit = k % 64; + *self.pba_pointer(byte) & (1 << bit) != 0 + } +} + +impl Xhci { + /// Gets descriptors, before the port state is initiated. + async fn get_desc_raw(&self, port: usize, slot: u8, kind: usb::DescriptorKind, index: u8, desc: &mut Dma) -> Result<()> { let len = mem::size_of::(); - { - let (cmd, cycle) = self.ring.next(); + let future = { + let mut port_state = self.port_states.get_mut(&port).ok_or(Error::new(ENOENT))?; + let ring = port_state.endpoint_states.get_mut(&0).ok_or(Error::new(EIO))?.ring().expect("no ring for the default control pipe"); + + let (cmd, cycle) = ring.next(); cmd.setup( usb::Setup::get_descriptor(kind, index, 0, len as u16), TransferKind::In, cycle, ); - } - { - let (cmd, cycle) = self.ring.next(); + let (cmd, cycle) = ring.next(); cmd.data(desc.physical(), len as u16, true, cycle); - } - { - let (cmd, cycle) = self.ring.next(); - cmd.status(false, cycle); - } + let last_index = ring.next_index(); + let (cmd, cycle) = (&mut ring.trbs[last_index], ring.cycle); + cmd.status(0, true, true, false, false, cycle); - self.db.write(1); + self.next_transfer_event_trb(RingId::default_control_pipe(port as u8), &ring, &ring.trbs[last_index]) + }; - { - let event = self.cmd.next_event(); - while event.data.read() == 0 { - println!(" - Waiting for event"); - } - } + self.dbs.lock().unwrap()[usize::from(slot)].write(Self::def_control_endp_doorbell()); - self.int.erdp.write(self.cmd.erdp()); + let trbs = future.await; + let event_trb = trbs.event_trb; + let status_trb = trbs.src_trb.unwrap(); + + self::scheme::handle_transfer_event_trb("GET_DESC", &event_trb, &status_trb)?; + + self.event_handler_finished(); + Ok(()) } - fn get_device(&mut self) -> Result { + async fn fetch_dev_desc(&self, port: usize, slot: u8) -> Result { let mut desc = Dma::::zeroed()?; - self.get_desc(usb::DescriptorKind::Device, 0, &mut desc); + self.get_desc_raw(port, slot, usb::DescriptorKind::Device, 0, &mut desc).await?; Ok(*desc) } - fn get_config(&mut self, config: u8) -> Result<(usb::ConfigDescriptor, [u8; 4087])> { + async fn fetch_config_desc(&self, port: usize, slot: u8, config: u8) -> Result<(usb::ConfigDescriptor, [u8; 4087])> { let mut desc = Dma::<(usb::ConfigDescriptor, [u8; 4087])>::zeroed()?; - self.get_desc(usb::DescriptorKind::Configuration, config, &mut desc); + self.get_desc_raw(port, slot, usb::DescriptorKind::Configuration, config, &mut desc).await?; Ok(*desc) } - fn get_bos(&mut self) -> Result<(usb::BosDescriptor, [u8; 4087])> { + async fn fetch_bos_desc(&self, port: usize, slot: u8) -> Result<(usb::BosDescriptor, [u8; 4087])> { let mut desc = Dma::<(usb::BosDescriptor, [u8; 4087])>::zeroed()?; - self.get_desc(usb::DescriptorKind::BinaryObjectStorage, 0, &mut desc); + self.get_desc_raw(port, slot, usb::DescriptorKind::BinaryObjectStorage, 0, &mut desc).await?; Ok(*desc) } - fn get_string(&mut self, index: u8) -> Result { + async fn fetch_string_desc(&self, port: usize, slot: u8, index: u8) -> Result { let mut sdesc = Dma::<(u8, u8, [u16; 127])>::zeroed()?; - self.get_desc(usb::DescriptorKind::String, index, &mut sdesc); + self.get_desc_raw(port, slot, usb::DescriptorKind::String, index, &mut sdesc).await?; let len = sdesc.0 as usize; if len > 2 { @@ -113,38 +162,54 @@ impl<'a> Device<'a> { } pub struct Xhci { - cap: &'static mut CapabilityRegs, - op: &'static mut OperationalRegs, - ports: &'static mut [Port], - dbs: &'static mut [Doorbell], - run: &'static mut RuntimeRegs, - dev_ctx: DeviceContextList, - cmd: CommandRing, + // immutable + cap: &'static CapabilityRegs, + page_size: usize, + // XXX: It would be really useful to be able to mutably access individual elements of a slice, + // without having to wrap every element in a lock (which wouldn't work since they're packed). + op: Mutex<&'static mut OperationalRegs>, + ports: Mutex<&'static mut [Port]>, + dbs: Mutex<&'static mut [Doorbell]>, + run: Mutex<&'static mut RuntimeRegs>, + cmd: Mutex, + primary_event_ring: Mutex, + + // immutable + dev_ctx: DeviceContextList, + scratchpad_buf_arr: Option, + + // used for the extended capabilities, and so far none of them are mutated, and thus no lock. base: *const u8, - handles: BTreeMap, - next_handle: usize, - port_states: BTreeMap, + handles: CHashMap, + next_handle: AtomicUsize, + port_states: CHashMap, - // TODO: Is this the correct implementation? I mean, there will be a really limited number of - // IRQs, if not just one, and since we probably wont use a thread pool scheduler like those of - // async-std or tokio, one could possibly assume that the futures themselves won't have to push - // all the wakers. - // TODO: This should probably be a BTreeMap (or just a VecMap) of states for each IRQ number, - // if more than one are used. I'm not sure if the XHCI interrupters actually use different - // IRQs, but it would make sense in case the hub has both isochronous (which trigger interrupts - // reapeatedly with some time in between), bulk, control, etc. I might be wrong though... - irq_state: Arc, - - drivers: BTreeMap, + drivers: CHashMap, scheme_name: String, + + interrupt_method: InterruptMethod, + pcid_handle: Mutex, + + irq_reactor: Mutex>>, + + irq_reactor_sender: Sender, + + // not used, but still stored so that the thread, when created, can get the channel without the + // channel being in a mutex. + irq_reactor_receiver: Receiver, } +unsafe impl Send for Xhci {} +unsafe impl Sync for Xhci {} + struct PortState { slot: u8, - input_context: Dma, - dev_desc: DevDesc, + cfg_idx: Option, + if_idx: Option, + input_context: Mutex>, + dev_desc: Option, endpoint_states: BTreeMap, } @@ -167,146 +232,209 @@ impl EndpointState { } impl Xhci { - pub fn new(scheme_name: String, address: usize) -> Result { + pub fn new(scheme_name: String, address: usize, interrupt_method: InterruptMethod, pcid_handle: PcidServerHandle) -> Result { let cap = unsafe { &mut *(address as *mut CapabilityRegs) }; - println!(" - CAP {:X}", address); + debug!("CAP REGS BASE {:X}", address); + + let page_size = { + let memory_fd = syscall::open("memory:", O_RDONLY)?; + let mut stat = syscall::data::StatVfs::default(); + syscall::fstatvfs(memory_fd, &mut stat)?; + stat.f_bsize as usize + }; let op_base = address + cap.len.read() as usize; let op = unsafe { &mut *(op_base as *mut OperationalRegs) }; - println!(" - OP {:X}", op_base); + debug!("OP REGS BASE {:X}", op_base); let (max_slots, max_ports) = { - println!(" - Wait for ready"); + debug!("Waiting for xHC becoming ready."); // Wait until controller is ready while op.usb_sts.readf(1 << 11) { - println!(" - Waiting for XHCI ready"); + trace!("Waiting for the xHC to be ready."); } - println!(" - Stop"); + debug!("Stopping the xHC"); // Set run/stop to 0 op.usb_cmd.writef(1, false); - println!(" - Wait for not running"); + debug!("Waiting for the xHC to stop."); // Wait until controller not running while !op.usb_sts.readf(1) { - println!(" - Waiting for XHCI stopped"); + trace!("Waiting for the xHC to stop."); } - println!(" - Reset"); + debug!("Resetting the xHC."); op.usb_cmd.writef(1 << 1, true); while op.usb_sts.readf(1 << 1) { - println!(" - Waiting for XHCI reset"); + trace!("Waiting for the xHC to reset."); } - println!(" - Read max slots"); + debug!("Reading max slots."); let max_slots = cap.max_slots(); let max_ports = cap.max_ports(); - println!(" - Max Slots: {}, Max Ports {}", max_slots, max_ports); + info!("xHC max slots: {}, max ports: {}", max_slots, max_ports); (max_slots, max_ports) }; let port_base = op_base + 0x400; let ports = unsafe { slice::from_raw_parts_mut(port_base as *mut Port, max_ports as usize) }; - println!(" - PORT {:X}", port_base); + debug!("PORT BASE {:X}", port_base); let db_base = address + cap.db_offset.read() as usize; let dbs = unsafe { slice::from_raw_parts_mut(db_base as *mut Doorbell, 256) }; - println!(" - DOORBELL {:X}", db_base); + debug!("DOORBELL REGS BASE {:X}", db_base); let run_base = address + cap.rts_offset.read() as usize; let run = unsafe { &mut *(run_base as *mut RuntimeRegs) }; - println!(" - RUNTIME {:X}", run_base); + debug!("RUNTIME REGS BASE {:X}", run_base); - let mut xhci = Xhci { + // Create the command ring with 4096 / 16 (TRB size) entries, so that it uses all of the + // DMA allocation (which is at least a 4k page). + let entries_per_page = page_size / mem::size_of::(); + let cmd = Ring::new(entries_per_page, true)?; + + let (irq_reactor_sender, irq_reactor_receiver) = crossbeam_channel::unbounded(); + + let mut xhci = Self { base: address as *const u8, - cap, - op, - ports, - dbs, - run, - dev_ctx: DeviceContextList::new(max_slots)?, - cmd: CommandRing::new()?, - handles: BTreeMap::new(), - next_handle: 0, - port_states: BTreeMap::new(), - irq_state: Arc::new(IrqState { - triggered: AtomicBool::new(false), - wakers: Mutex::new(Vec::new()), - }), - drivers: BTreeMap::new(), + cap, + page_size, + + op: Mutex::new(op), + ports: Mutex::new(ports), + dbs: Mutex::new(dbs), + run: Mutex::new(run), + + dev_ctx: DeviceContextList::new(max_slots)?, + scratchpad_buf_arr: None, // initialized in init() + + cmd: Mutex::new(cmd), + primary_event_ring: Mutex::new(EventRing::new()?), + handles: CHashMap::new(), + next_handle: AtomicUsize::new(0), + port_states: CHashMap::new(), + + drivers: CHashMap::new(), scheme_name, + + interrupt_method, + pcid_handle: Mutex::new(pcid_handle), + + irq_reactor: Mutex::new(None), + irq_reactor_sender, + irq_reactor_receiver, }; - xhci.init(max_slots); + xhci.init(max_slots)?; Ok(xhci) } - pub fn init(&mut self, max_slots: u8) { + pub fn init(&mut self, max_slots: u8) -> Result<()> { // Set enabled slots - println!(" - Set enabled slots to {}", max_slots); - self.op.config.write(max_slots as u32); - println!(" - Enabled Slots: {}", self.op.config.read() & 0xFF); + debug!("Setting enabled slots to {}.", max_slots); + self.op.get_mut().unwrap().config.write(max_slots as u32); + debug!("Enabled Slots: {}", self.op.get_mut().unwrap().config.read() & 0xFF); // Set device context address array pointer let dcbaap = self.dev_ctx.dcbaap(); - println!(" - Write DCBAAP: {:X}", dcbaap); - self.op.dcbaap.write(dcbaap as u64); + debug!("Writing DCBAAP: {:X}", dcbaap); + self.op.get_mut().unwrap().dcbaap.write(dcbaap as u64); // Set command ring control register - let crcr = self.cmd.crcr(); - println!(" - Write CRCR: {:X}", crcr); - self.op.crcr.write(crcr as u64); + let crcr = self.cmd.get_mut().unwrap().register(); + assert_eq!(crcr & 0xFFFF_FFFF_FFFF_FFC1, crcr, "unaligned CRCR"); + debug!("Writing CRCR: {:X}", crcr); + self.op.get_mut().unwrap().crcr.write(crcr as u64); // Set event ring segment table registers - println!(" - Interrupter 0: {:X}", self.run.ints.as_ptr() as usize); + debug!("Interrupter 0: {:p}", self.run.get_mut().unwrap().ints.as_ptr()); { + let int = &mut self.run.get_mut().unwrap().ints[0]; + let erstz = 1; - println!(" - Write ERSTZ: {}", erstz); - self.run.ints[0].erstsz.write(erstz); + debug!("Writing ERSTZ: {}", erstz); + int.erstsz.write(erstz); - let erdp = self.cmd.erdp(); - println!(" - Write ERDP: {:X}", erdp); - self.run.ints[0].erdp.write(erdp as u64); + let erdp = self.primary_event_ring.get_mut().unwrap().erdp(); + debug!("Writing ERDP: {:X}", erdp); + int.erdp.write(erdp as u64 | (1 << 3)); - let erstba = self.cmd.erstba(); - println!(" - Write ERSTBA: {:X}", erstba); - self.run.ints[0].erstba.write(erstba as u64); + let erstba = self.primary_event_ring.get_mut().unwrap().erstba(); + debug!("Writing ERSTBA: {:X}", erstba); + int.erstba.write(erstba as u64); + + debug!("Writing IMODC and IMODI: {} and {}", 0, 0); + int.imod.write(0); + + debug!("Enabling Primary Interrupter."); + int.iman.writef(1 << 1 | 1, true); - println!(" - Enable interrupts"); - self.run.ints[0].iman.writef(1 << 1, true); } + self.op.get_mut().unwrap().usb_cmd.writef(1 << 2, true); + + // Setup the scratchpad buffers that are required for the xHC to function. + self.setup_scratchpads()?; // Set run/stop to 1 - println!(" - Start"); - self.op.usb_cmd.writef(1 | 1 << 2, true); + info!("Starting xHC."); + self.op.get_mut().unwrap().usb_cmd.writef(1, true); // Wait until controller is running - println!(" - Wait for running"); - while self.op.usb_sts.readf(1) { - println!(" - Waiting for XHCI running"); + debug!("Waiting for start request to complete."); + while self.op.get_mut().unwrap().usb_sts.readf(1) { + trace!("Waiting for XHCI to report running status."); } // Ring command doorbell - println!(" - Ring doorbell"); - self.dbs[0].write(0); + debug!("Ringing command doorbell."); + self.dbs.get_mut().unwrap()[0].write(0); - println!(" - XHCI initialized"); + info!("XHCI initialized."); + + if self.cap.cic() { + self.op.get_mut().unwrap().set_cie(true); + } + + Ok(()) } - pub fn enable_port_slot(&mut self, slot_ty: u8) -> Result { + pub fn setup_scratchpads(&mut self) -> Result<()> { + let buf_count = self.cap.max_scratchpad_bufs(); + + if buf_count == 0 { + return Ok(()); + } + let scratchpad_buf_arr = ScratchpadBufferArray::new(self.page_size,buf_count)?; + self.dev_ctx.dcbaa[0] = scratchpad_buf_arr.register() as u64; + debug!("Setting up {} scratchpads, at {:#0x}", buf_count, scratchpad_buf_arr.register()); + self.scratchpad_buf_arr = Some(scratchpad_buf_arr); + + Ok(()) + } + + pub async fn enable_port_slot(&self, slot_ty: u8) -> Result { assert_eq!(slot_ty & 0x1F, slot_ty); - let cloned_event_trb = - self.execute_command("ENABLE_SLOT", |cmd, cycle| cmd.enable_slot(0, cycle))?; - Ok(cloned_event_trb.event_slot()) + let (event_trb, command_trb) = + self.execute_command(|cmd, cycle| cmd.enable_slot(slot_ty, cycle)).await; + + self::scheme::handle_event_trb("ENABLE_SLOT", &event_trb, &command_trb)?; + self.event_handler_finished(); + + Ok(event_trb.event_slot()) } - pub fn disable_port_slot(&mut self, slot: u8) -> Result<()> { - self.execute_command("DISABLE_SLOT", |cmd, cycle| cmd.enable_slot(0, cycle))?; + pub async fn disable_port_slot(&self, slot: u8) -> Result<()> { + let (event_trb, command_trb) = self.execute_command(|cmd, cycle| cmd.disable_slot(slot, cycle)).await; + + self::scheme::handle_event_trb("DISABLE_SLOT", &event_trb, &command_trb)?; + self.event_handler_finished(); + Ok(()) } @@ -314,49 +442,45 @@ impl Xhci { self.dev_ctx.contexts[slot].slot.state() } - pub fn probe(&mut self) -> Result<()> { - for i in 0..self.ports.len() { + pub async fn probe(&self) -> Result<()> { + info!("XHCI capabilities: {:?}", self.capabilities_iter().collect::>()); + + let port_count = { self.ports.lock().unwrap().len() }; + + for i in 0..port_count { let (data, state, speed, flags) = { - let port = &self.ports[i]; + let port = &self.ports.lock().unwrap()[i]; (port.read(), port.state(), port.speed(), port.flags()) }; - println!( - " + XHCI Port {}: {:X}, State {}, Speed {}, Flags {:?}", + info!( + "XHCI Port {}: {:X}, State {}, Speed {}, Flags {:?}", i, data, state, speed, flags ); if flags.contains(port::PortFlags::PORT_CCS) { - //TODO: Link TRB when running to the end of the ring buffer - - println!(" - Enable slot"); - let slot_ty = self .supported_protocol(i as u8) .expect("Failed to find supported protocol information for port") .proto_slot_ty(); - let slot = self.enable_port_slot(slot_ty)?; - println!(" - Slot {}", slot); + debug!("Slot type: {}", slot_ty); + debug!("Enabling slot."); + let slot = self.enable_port_slot(slot_ty).await?; + + info!("Enabled port {}, which the xHC mapped to {}", i, slot); let mut input = Dma::::zeroed()?; - let mut ring = self.address_device(&mut input, i, slot_ty, slot, speed)?; + let mut ring = self.address_device(&mut input, i, slot_ty, slot, speed).await?; + info!("Addressed device"); - let dev_desc = Self::get_dev_desc_raw( - &mut self.ports, - &mut self.run, - &mut self.cmd, - &mut self.dbs, - i, - slot, - &mut ring, - )?; - - self.update_default_control_pipe(&mut input, slot, &dev_desc)?; + // TODO: Should the descriptors be cached in PortState, or refetched? let mut port_state = PortState { slot, - input_context: input, - dev_desc, + input_context: Mutex::new(input), + dev_desc: None, + cfg_idx: None, + if_idx: None, endpoint_states: std::iter::once(( 0, EndpointState { @@ -366,25 +490,33 @@ impl Xhci { )) .collect::>(), }; + self.port_states.insert(i, port_state); - if self.cap.cic() { - self.op.set_cie(true); + let dev_desc = self.get_desc(i, slot).await?; + self.port_states.get_mut(&i).unwrap().dev_desc = Some(dev_desc); + + { + let mut port_state = self.port_states.get_mut(&i).unwrap(); + + let mut input = port_state.input_context.lock().unwrap(); + let dev_desc = port_state.dev_desc.as_ref().unwrap(); + + self.update_default_control_pipe(&mut *input, slot, dev_desc).await?; } /*match self.spawn_drivers(i, &mut port_state) { Ok(()) => (), - Err(err) => println!("Failed to spawn driver for port {}: `{}`", i, err), + Err(err) => error!("Failed to spawn driver for port {}: `{}`", i, err), }*/ - self.port_states.insert(i, port_state); } } Ok(()) } - pub fn update_default_control_pipe( - &mut self, + pub async fn update_default_control_pipe( + &self, input_context: &mut Dma, slot_id: u8, dev_desc: &DevDesc, @@ -403,14 +535,18 @@ impl Xhci { b |= (new_max_packet_size) << 16; endp_ctx.b.write(b); - self.execute_command("EVALUATE_CONTEXT", |trb, cycle| { + let (event_trb, command_trb) = self.execute_command(|trb, cycle| { trb.evaluate_context(slot_id, input_context.physical(), false, cycle) - })?; + }).await; + + self::scheme::handle_event_trb("EVALUATE_CONTEXT", &event_trb, &command_trb)?; + self.event_handler_finished(); + Ok(()) } - pub fn address_device( - &mut self, + pub async fn address_device( + &self, input_context: &mut Dma, i: usize, slot_ty: u8, @@ -498,39 +634,65 @@ impl Xhci { let input_context_physical = input_context.physical(); - self.execute_command("ADDRESS_DEVICE", |trb, cycle| { + let (event_trb, _) = self.execute_command(|trb, cycle| { trb.address_device(slot, input_context_physical, false, cycle) - }) - .expect("ADDRESS_DEVICE failed"); + }).await; + + if event_trb.completion_code() != TrbCompletionCode::Success as u8 { + error!("Failed to address device at slot {} (port {})", slot, i); + self.event_handler_finished(); + return Err(Error::new(EIO)); + } + self.event_handler_finished(); + Ok(ring) } + pub fn uses_msi(&self) -> bool { + if let InterruptMethod::Msi = self.interrupt_method { true } else { false } + } + pub fn uses_msix(&self) -> bool { + if let InterruptMethod::MsiX(_) = self.interrupt_method { true } else { false } + } + // TODO: Perhaps use an rwlock? + pub fn msix_info(&self) -> Option> { + match self.interrupt_method { + InterruptMethod::MsiX(ref info) => Some(info.lock().unwrap()), + _ => None, + } + } + pub fn msix_info_mut(&self) -> Option> { + match self.interrupt_method { + InterruptMethod::MsiX(ref info) => Some(info.lock().unwrap()), + _ => None, + } + } - pub fn trigger_irq(&mut self) -> bool { - // Read the Interrupter Pending bit. - if self.run.ints[0].iman.readf(1) { - //println!("XHCI Interrupt"); + /// Checks whether an IRQ has been received from *this* device, in case of an interrupt. Always + /// true when using MSI/MSI-X. + pub fn received_irq(&self) -> bool { + let mut runtime_regs = self.run.lock().unwrap(); - // If set, set it back to zero, so that new interrupts can be triggered. - // FIXME: MSI and MSI-X systems - self.run.ints[0].iman.writef(1, true); - - // Wake all futures awaiting the IRQ. - for waker in self.irq_state.wakers.lock().unwrap().drain(..) { - waker.wake(); - } + if self.uses_msi() || self.uses_msix() { + // Since using MSI and MSI-X implies having no IRQ sharing whatsoever, the IP bit + // doesn't have to be touched. + trace!("Successfully received MSI/MSI-X interrupt, IP={}, EHB={}", runtime_regs.ints[0].iman.readf(1), runtime_regs.ints[0].erdp.readf(3)); + true + } else if runtime_regs.ints[0].iman.readf(1) { + trace!("Successfully received INTx# interrupt, IP={}, EHB={}", runtime_regs.ints[0].iman.readf(1), runtime_regs.ints[0].erdp.readf(3)); + // If MSI and/or MSI-X are not used, the interrupt might have to be shared, and thus there is + // a special register to specify whether the IRQ actually came from the xHC. + runtime_regs.ints[0].iman.writef(1, true); + // The interrupt came from the xHC. true } else { + // The interrupt came from a different device. false } + } - pub(crate) fn irq(&self) -> IrqFuture { - IrqFuture { - state: IrqFutureState::Pending(Arc::downgrade(&self.irq_state)), - } - } - fn spawn_drivers(&mut self, port: usize, ps: &mut PortState) -> Result<()> { + fn spawn_drivers(&self, port: usize, ps: &mut PortState) -> Result<()> { // TODO: There should probably be a way to select alternate interfaces, and not just the // first one. // TODO: Now that there are some good error crates, I don't think errno.h error codes are @@ -538,12 +700,14 @@ impl Xhci { let ifdesc = &ps .dev_desc + .as_ref().unwrap() .config_descs .first() .ok_or(Error::new(EBADF))? .interface_descs .first() .ok_or(Error::new(EBADF))?; + let drivers_usercfg: &DriversConfig = &DRIVERS_CONFIG; if let Some(driver) = drivers_usercfg.drivers.iter().find(|driver| { @@ -553,7 +717,7 @@ impl Xhci { .map(|subclass| subclass == ifdesc.sub_class) .unwrap_or(true) }) { - println!("Loading driver \"{}\"", driver.name); + info!("Loading subdriver\"{}\"", driver.name); let (command, args) = driver.command.split_first().ok_or(Error::new(EBADMSG))?; let if_proto = ifdesc.protocol; @@ -680,6 +844,18 @@ impl Xhci { .find(|speed| speed.psiv() == psiv) } } +pub fn start_irq_reactor(hci: &Arc, irq_file: Option) { + let receiver = hci.irq_reactor_receiver.clone(); + let hci_clone = Arc::clone(&hci); + + debug!("About to start IRQ reactor"); + + *hci.irq_reactor.lock().unwrap() = Some(thread::spawn(move || { + info!("Started IRQ reactor thread"); + IrqReactor::new(hci_clone, receiver, irq_file).run() + })); +} + #[derive(Deserialize)] struct DriverConfig { name: String, @@ -707,44 +883,3 @@ lazy_static! { toml::from_slice::(TOML).expect("Failed to parse internally embedded config file") }; } - -pub(crate) struct IrqFuture { - state: IrqFutureState, -} - -struct IrqState { - triggered: AtomicBool, - // TODO: Perhaps a channel? - wakers: Mutex>, -} - -enum IrqFutureState { - Pending(Weak), - Finished, -} - -impl std::future::Future for IrqFuture { - type Output = (); - - fn poll(self: Pin<&mut Self>, context: &mut task::Context) -> task::Poll { - let this = self.get_mut(); - - match &mut this.state { - // TODO: Ordering? - IrqFutureState::Pending(state_weak) => { - let state = state_weak.upgrade().expect( - "IRQ futures keep getting polled even after the driver has been deinitialized", - ); - - if state.triggered.load(atomic::Ordering::SeqCst) { - this.state = IrqFutureState::Finished; - task::Poll::Ready(()) - } else { - state.wakers.lock().unwrap().push(context.waker().clone()); - task::Poll::Pending - } - } - IrqFutureState::Finished => panic!("polling finished future"), - } - } -} diff --git a/xhcid/src/xhci/ring.rs b/xhcid/src/xhci/ring.rs index c941c70dca..67cafcf080 100644 --- a/xhcid/src/xhci/ring.rs +++ b/xhcid/src/xhci/ring.rs @@ -1,3 +1,5 @@ +use std::mem; + use syscall::error::Result; use syscall::io::Dma; @@ -26,7 +28,7 @@ impl Ring { addr as u64 | self.cycle as u64 } - pub fn next(&mut self) -> (&mut Trb, bool) { + pub fn next_index(&mut self) -> usize { let mut i; loop { i = self.i; @@ -45,7 +47,11 @@ impl Ring { break; } } + i + } + pub fn next(&mut self) -> (&mut Trb, bool) { + let i = self.next_index(); (&mut self.trbs[i], self.cycle) } /// Endless iterator that iterates through the ring items, over and over again. The iterator @@ -53,6 +59,55 @@ impl Ring { pub fn iter(&self) -> impl Iterator + '_ { Iter { ring: self, i: self.i } } + /// Takes a physical address and returns the index into this ring, that the index represents. + /// Returns `None` if the address is outside the bounds of this ring. + /// + /// # Panics + /// Panics if paddr is not a multiple of 16 bytes, i.e. the size of a TRB. + // TODO: Use usize instead of u64. + pub fn phys_addr_to_index(&self, paddr: u64) -> Option { + let base = self.trbs.physical(); + let offset = paddr.checked_sub(base as u64)? as usize; + + assert_eq!(offset % mem::size_of::(), 0, "unaligned TRB physical address"); + + let index = offset / mem::size_of::(); + + if index > self.trbs.len() { + return None; + } + + Some(index) + } + pub fn phys_addr_to_entry_ref(&self, paddr: u64) -> Option<&Trb> { + Some(&self.trbs[self.phys_addr_to_index(paddr)?]) + } + pub fn phys_addr_to_entry_mut(&mut self, paddr: u64) -> Option<&mut Trb> { + let index = self.phys_addr_to_index(paddr)?; + Some(&mut self.trbs[index]) + } + pub fn phys_addr_to_entry(&self, paddr: u64) -> Option { + Some(self.trbs[self.phys_addr_to_index(paddr)?].clone()) + } + pub(crate) fn start_virt_addr(&self) -> *const Trb { + self.trbs.as_ptr() + } + pub(crate) fn end_virt_addr(&self) -> *const Trb { + unsafe { self.start_virt_addr().offset(self.trbs.len() as isize) } + } + pub fn trb_phys_ptr(&self, trb: &Trb) -> u64 { + let trb_virt_pointer = trb as *const Trb; + let trbs_base_virt_pointer = self.trbs.as_ptr(); + + if (trb_virt_pointer as usize) < (trbs_base_virt_pointer as usize) || (trb_virt_pointer as usize) > (trbs_base_virt_pointer as usize) + self.trbs.len() * mem::size_of::() { + panic!("Gave a TRB outside of the ring, when retrieving its physical address in that ring. TRB: {:?} (at address {:p})", trb, trb); + } + let trb_offset_from_base = trb_virt_pointer as u64 - trbs_base_virt_pointer as u64; + + let trbs_base_phys_ptr = self.trbs.physical() as u64; + let trb_phys_ptr = trbs_base_phys_ptr + trb_offset_from_base; + trb_phys_ptr + } /* /// Endless mutable iterator that iterates through the ring items, over and over again. The /// iterator doesn't enqueue or dequeue anything, but the trbs are mutably borrowed. diff --git a/xhcid/src/xhci/scheme.rs b/xhcid/src/xhci/scheme.rs index e26b032810..039da4431f 100644 --- a/xhcid/src/xhci/scheme.rs +++ b/xhcid/src/xhci/scheme.rs @@ -1,12 +1,16 @@ use std::convert::TryFrom; use std::io::prelude::*; -use std::{cmp, io, mem, path, str}; +use std::ops::Deref; +use std::sync::atomic; +use std::{cmp, fmt, io, mem, path, str}; +use futures::executor::block_on; +use log::{debug, error, info, warn, trace}; use serde::{Deserialize, Serialize}; use smallvec::{smallvec, SmallVec}; use syscall::io::{Dma, Io}; -use syscall::scheme::SchemeMut; +use syscall::scheme::Scheme; use syscall::{ Error, Result, Stat, EACCES, EBADF, EBADFD, EBADMSG, EEXIST, EINVAL, EIO, EISDIR, ENOENT, ENOSYS, ENOTDIR, ENXIO, EOPNOTSUPP, EOVERFLOW, EPERM, EPROTO, ESPIPE, MODE_CHR, MODE_DIR, @@ -15,15 +19,15 @@ use syscall::{ }; use super::{port, usb}; -use super::{Device, EndpointState, Xhci}; +use super::{EndpointState, Xhci}; -use super::command::CommandRing; use super::context::{ InputContext, SlotState, StreamContext, StreamContextArray, StreamContextType, ENDPOINT_CONTEXT_STATUS_MASK, }; use super::doorbell::Doorbell; use super::extended::ProtocolSpeed; +use super::irq_reactor::RingId; use super::operational::OperationalRegs; use super::ring::Ring; use super::runtime::RuntimeRegs; @@ -50,6 +54,7 @@ pub enum EndpIfState { } /// Subdirs of an endpoint +#[derive(Debug)] pub enum EndpointHandleTy { /// portX/endpoints/Y/data. Allows clients to read and write data associated with ctl requests. Data, @@ -61,7 +66,7 @@ pub enum EndpointHandleTy { Root(usize, Vec), // offset, content } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum PortTransferState { /// Ready to read or write to do another transfer Ready, @@ -78,6 +83,7 @@ pub enum PortReqState { Tmp, } +#[derive(Debug)] pub enum Handle { TopLevel(usize, Vec), // offset, contents (ports) Port(usize, usize, Vec), // port, offset, contents @@ -89,6 +95,34 @@ pub enum Handle { ConfigureEndpoints(usize), // port } +#[derive(Clone, Copy)] +struct DmaSliceDbg<'a, T>(&'a Dma<[T]>); + +impl<'a, T> fmt::Debug for DmaSliceDbg<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let DmaSliceDbg(dma) = self; + + f.debug_struct("Dma") + .field("phys_ptr", &(dma.physical() as *const u8)) + .field("virt_ptr", &(dma.deref().as_ptr() as *const u8)) + .field("length", &(dma.len() * mem::size_of::())) + .finish() + } +} + +impl fmt::Debug for PortReqState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Init => f.debug_struct("PortReqState::Init").finish(), + Self::WaitingForDeviceBytes(ref dma, setup) => f.debug_tuple("PortReqState::WaitingForDeviceBytes").field(&DmaSliceDbg(dma)).field(&setup).finish(), + Self::WaitingForHostBytes(ref dma, setup) => f.debug_tuple("PortReqState::WaitingForHostBytes").field(&DmaSliceDbg(dma)).field(&setup).finish(), + Self::TmpSetup(setup) => f.debug_tuple("PortReqState::TmpSetup").field(&setup).finish(), + Self::Tmp => f.debug_struct("PortReqState::Init").finish(), + } + } +} + + // TODO: Even though the driver interface descriptors are originally intended for JSON, they should suffice... for // now. @@ -140,31 +174,6 @@ impl From for SuperSpeedPlusIsochCmp { } } -impl IfDesc { - fn new( - dev: &mut Device, - desc: usb::InterfaceDescriptor, - endps: impl IntoIterator, - hid_descs: impl IntoIterator, - ) -> Result { - Ok(Self { - alternate_setting: desc.alternate_setting, - class: desc.class, - interface_str: if desc.interface_str > 0 { - Some(dev.get_string(desc.interface_str)?) - } else { - None - }, - kind: desc.kind, - number: desc.number, - protocol: desc.protocol, - sub_class: desc.sub_class, - endpoints: endps.into_iter().collect(), - hid_descs: hid_descs.into_iter().collect(), - }) - } -} - /// Any descriptor that can be stored in the config desc "data" area. #[derive(Debug)] pub enum AnyDescriptor { @@ -211,39 +220,65 @@ impl AnyDescriptor { } impl Xhci { - pub fn execute_command( - &mut self, - cmd_name: &str, - f: F, - ) -> Result { - self.run.ints[0].erdp.write(self.cmd.erdp()); - let (cmd, cycle, event) = self.cmd.next(); - - f(cmd, cycle); - - self.dbs[0].write(0); - - while event.data.read() == 0 { - println!(" - {} Waiting for event", cmd_name); - } - - if event.completion_code() != TrbCompletionCode::Success as u8 - || event.trb_type() != TrbType::CommandCompletion as u8 - { - println!("{} failed with event TRB ({:#0x} {:#0x} {:#0x}) and command TRB ({:#0x} {:#0x} {:#0x})", cmd_name, event.data.read(), event.status.read(), event.control.read(), cmd.data.read(), cmd.status.read(), cmd.control.read()); - return Err(Error::new(EIO)); - } - - let ret = event.clone(); - - cmd.reserved(false); - event.reserved(false); - - self.run.ints[0].erdp.write(self.cmd.erdp()); - Ok(ret) + async fn new_if_desc( + &self, + port_id: usize, + slot: u8, + desc: usb::InterfaceDescriptor, + endps: impl IntoIterator, + hid_descs: impl IntoIterator, + ) -> Result { + Ok(IfDesc { + alternate_setting: desc.alternate_setting, + class: desc.class, + interface_str: if desc.interface_str > 0 { + Some(self.fetch_string_desc(port_id, slot, desc.interface_str).await?) + } else { + None + }, + kind: desc.kind, + number: desc.number, + protocol: desc.protocol, + sub_class: desc.sub_class, + endpoints: endps.into_iter().collect(), + hid_descs: hid_descs.into_iter().collect(), + }) } - pub fn execute_control_transfer( - &mut self, + /// Pushes a command TRB to the command ring, rings the doorbell, and then awaits its Command + /// Completion Event. + /// + /// # Locking + /// This function will lock `Xhci::cmd` and `Xhci::dbs`. + pub async fn execute_command( + &self, + f: F, + ) -> (Trb, Trb) { + let next_event = { + let mut command_ring = self.cmd.lock().unwrap(); + let (cmd_index, cycle) = (command_ring.next_index(), command_ring.cycle); + + { + let command_trb = &mut command_ring.trbs[cmd_index]; + f(command_trb, cycle); + } + + // get the future here before awaiting, to destroy the lock before deadlock + let command_trb = &command_ring.trbs[cmd_index]; + self.next_command_completion_event_trb(&*command_ring, command_trb) + }; + + self.dbs.lock().unwrap()[0].write(0); + + let trbs = next_event.await; + let event_trb = trbs.event_trb; + let command_trb = trbs.src_trb.expect("Command completion event TRBs shall always have a valid pointer to a valid source command TRB"); + + assert_eq!(event_trb.trb_type(), TrbType::CommandCompletion as u8, "The IRQ reactor (or the xHC) gave an invalid event TRB"); + + (event_trb, command_trb) + } + pub async fn execute_control_transfer( + &self, port_num: usize, setup: usb::Setup, tk: TransferKind, @@ -253,60 +288,61 @@ impl Xhci { where D: FnMut(&mut Trb, bool) -> ControlFlow, { - let slot = self.port_state(port_num)?.slot; - let ring = self - .endpoint_state_mut(port_num, 0)? - .ring() - .ok_or(Error::new(EIO))?; + let (future, slot) = { + let mut port_state = self.port_state_mut(port_num)?; + let slot = port_state.slot; + + let mut endpoint_state = port_state + .endpoint_states + .get_mut(&0).ok_or(Error::new(EIO))?; + + let ring = endpoint_state + .ring() + .ok_or(Error::new(EIO))?; - { let (cmd, cycle) = ring.next(); cmd.setup(setup, tk, cycle); - } - if tk != TransferKind::NoData { - loop { - let (trb, cycle) = ring.next(); - match d(trb, cycle) { - ControlFlow::Break => break, - ControlFlow::Continue => continue, + + if tk != TransferKind::NoData { + loop { + let (trb, cycle) = ring.next(); + match d(trb, cycle) { + ControlFlow::Break => break, + ControlFlow::Continue => continue, + } } } - } - { - let (cmd, cycle) = ring.next(); - cmd.status(tk == TransferKind::In, cycle); - } - self.dbs[usize::from(slot)].write(Self::def_control_endp_doorbell()); - let cloned_trb = { - let event = self.cmd.next_event(); - while event.data.read() == 0 { - println!(" - {} Waiting for event", name); - } + let last_index = ring.next_index(); + let (cmd, cycle) = (&mut ring.trbs[last_index], ring.cycle); - if event.completion_code() != TrbCompletionCode::Success as u8 - || event.trb_type() != TrbType::Transfer as u8 - { - println!( - "{} CONTROL TRANSFER ERROR, EVENT TRB {:#0x} {:#0x} {:#0}»", - name, - event.data.read(), - event.status.read(), - event.control.read() - ); - } - event.reserved(false); - event.clone() + let interrupter = 0; + let ioc = true; + let ch = false; + let ent = false; + + cmd.status(interrupter, tk == TransferKind::In, ioc, ch, ent, cycle); + (self.next_transfer_event_trb(RingId::default_control_pipe(port_num as u8), ring, &ring.trbs[last_index]), slot) }; - self.run.ints[0].erdp.write(self.cmd.erdp()); + self.dbs.lock().unwrap()[usize::from(slot)].write(Self::def_control_endp_doorbell()); - Ok(cloned_trb) + let trbs = future.await; + let event_trb = trbs.event_trb; + let status_trb = trbs.src_trb.unwrap(); + + handle_transfer_event_trb("CONTROL_TRANSFER", &event_trb, &status_trb)?; + + self.event_handler_finished(); + + Ok(event_trb) } /// NOTE: There has to be AT LEAST one successful invocation of `d`, that actually updates the /// TRB (it could be a NO-OP in the worst case). - pub fn execute_transfer( - &mut self, + /// The function is also required to set the Interrupt on Completion flag, or this function + /// will never complete. + pub async fn execute_transfer( + &self, port_num: usize, endp_num: u8, stream_id: u16, @@ -316,8 +352,16 @@ impl Xhci { where D: FnMut(&mut Trb, bool) -> ControlFlow, { - let port_state = self.port_state_mut(port_num)?; + let endp_idx = endp_num.checked_sub(1).ok_or(Error::new(EIO))?; + let mut port_state = self.port_state_mut(port_num)?; + + let (cfg_idx, if_idx) = match (port_state.cfg_idx, port_state.if_idx) { + (Some(c), Some(i)) => (c, i), + _ => return Err(Error::new(EIO)), + }; + let slot = port_state.slot; + let endp_state = port_state .endpoint_states .get_mut(&endp_num) @@ -337,85 +381,87 @@ impl Xhci { .ok_or(Error::new(EBADF))?), }; - loop { - let (trb, cycle) = ring.next(); + let future = loop { + let last_index = ring.next_index(); + let (trb, cycle) = (&mut ring.trbs[last_index], ring.cycle); + match d(trb, cycle) { - ControlFlow::Break => break, + ControlFlow::Break => { + break self.next_transfer_event_trb(super::irq_reactor::RingId { port: port_num as u8, endpoint_num: endp_num, stream_id }, ring, &ring.trbs[last_index]); + } ControlFlow::Continue => continue, } - } + }; - self.dbs[usize::from(slot)].write(Self::endp_doorbell( + let endp_desc = port_state.dev_desc.as_ref().unwrap().config_descs.get(usize::from(cfg_idx)).ok_or(Error::new(EIO))?.interface_descs.get(usize::from(if_idx)).ok_or(Error::new(EIO))?.endpoints.get(usize::from(endp_idx)).ok_or(Error::new(EBADFD))?; + + self.dbs.lock().unwrap()[usize::from(slot)].write(Self::endp_doorbell( endp_num, - self.endp_desc(port_num, endp_num)?, + endp_desc, if has_streams { stream_id } else { 0 }, )); - let cloned_trb = { - let event = self.cmd.next_event(); - while event.data.read() == 0 { - println!(" - {} Waiting for event", name); - } + drop(port_state); + let trbs = future.await; + let event_trb = trbs.event_trb; + let transfer_trb = trbs.src_trb.unwrap(); - // FIXME: EDTLA if event data was set - if event.completion_code() != TrbCompletionCode::ShortPacket as u8 - && event.transfer_length() != 0 - { - println!( - "Event trb didn't yield a short packet, but some bytes were not transferred" - ); - } + handle_transfer_event_trb("EXECUTE_TRANSFER", &event_trb, &transfer_trb)?; - if event.completion_code() != TrbCompletionCode::Success as u8 - || event.trb_type() != TrbType::Transfer as u8 - { - println!( - "Custom transfer event failed with {:#0x} {:#0x} {:#0x}", - event.data.read(), - event.status.read(), - event.control.read() - ); - } - // TODO: Handle event data - println!("EVENT DATA: {:?}", event.event_data()); + // FIXME: EDTLA if event data was set + if event_trb.completion_code() != TrbCompletionCode::ShortPacket as u8 + && event_trb.transfer_length() != 0 + { + error!( + "Event trb didn't yield a short packet, but some bytes were not transferred" + ); + return Err(Error::new(EIO)); + } - let cloned_trb = event.clone(); - event.reserved(false); + // TODO: Handle event data + debug!("EVENT DATA: {:?}", event_trb.event_data()); - cloned_trb - }; - - self.run.ints[0].erdp.write(self.cmd.erdp()); - - Ok(cloned_trb) + Ok(event_trb) } - fn device_req_no_data(&mut self, port: usize, req: usb::Setup) -> Result<()> { + async fn device_req_no_data(&self, port: usize, req: usb::Setup) -> Result<()> { + trace!("DEVICE_REQ_NO_DATA port {}, req: {:?}", port, req); + self.execute_control_transfer( port, req, TransferKind::NoData, "DEVICE_REQ_NO_DATA", |_, _| ControlFlow::Break, - )?; + ).await?; Ok(()) } - fn set_configuration(&mut self, port: usize, config: u8) -> Result<()> { - self.device_req_no_data(port, usb::Setup::set_configuration(config)) + async fn set_configuration(&self, port: usize, config: u8) -> Result<()> { + debug!("Setting configuration value {} to port {}", config, port); + self.device_req_no_data(port, usb::Setup::set_configuration(config)).await } - fn set_interface( - &mut self, + async fn set_interface( + &self, port: usize, interface_num: u8, alternate_setting: u8, ) -> Result<()> { + debug!("Setting interface value {} (alternate setting {}) to port {}", interface_num, alternate_setting, port); self.device_req_no_data( port, usb::Setup::set_interface(interface_num, alternate_setting), - ) + ).await } - fn reset_endpoint(&mut self, port_num: usize, endp_num: u8, tsp: bool) -> Result<()> { - let endp_num_xhc = Self::endp_num_to_dci(endp_num, self.endp_desc(port_num, endp_num)?); + async fn reset_endpoint(&self, port_num: usize, endp_num: u8, tsp: bool) -> Result<()> { + let port_state = self.port_states.get(&port_num).ok_or(Error::new(EBADFD))?; + + let (cfg_idx, if_idx) = match (port_state.cfg_idx, port_state.if_idx) { + (Some(c), Some(i)) => (c, i), + _ => return Err(Error::new(EIO)), + }; + + let endp_desc = port_state.dev_desc.as_ref().unwrap().config_descs[usize::from(cfg_idx)].interface_descs[usize::from(if_idx)].endpoints.get(usize::from(endp_num)).ok_or(Error::new(EBADFD))?; + let endp_num_xhc = Self::endp_num_to_dci(endp_num, endp_desc); let slot = self .port_states @@ -423,10 +469,12 @@ impl Xhci { .ok_or(Error::new(EBADF))? .slot; - self.execute_command("RESET_ENDPOINT", |trb, cycle| { - trb.reset_endpoint(slot, endp_num_xhc, tsp, cycle) - })?; - Ok(()) + let (event_trb, command_trb) = self.execute_command(|trb, cycle| { + trb.reset_endpoint(slot, endp_num_xhc, tsp, cycle); + }).await; + self.event_handler_finished(); + + handle_event_trb("RESET_ENDPOINT", &event_trb, &command_trb) } fn endp_ctx_interval(speed_id: &ProtocolSpeed, endp_desc: &EndpDesc) -> u8 { @@ -503,62 +551,19 @@ impl Xhci { } } - fn port_state(&self, port: usize) -> Result<&super::PortState> { + fn port_state(&self, port: usize) -> Result> { self.port_states.get(&port).ok_or(Error::new(EBADF)) } - fn port_state_mut(&mut self, port: usize) -> Result<&mut super::PortState> { + fn port_state_mut(&self, port: usize) -> Result> { self.port_states.get_mut(&port).ok_or(Error::new(EBADF)) } - fn endpoint_state_mut(&mut self, port: usize, endp_num: u8) -> Result<&mut EndpointState> { - self.port_state_mut(port)? - .endpoint_states - .get_mut(&endp_num) - .ok_or(Error::new(EBADF)) - } - fn input_context(&mut self, port: usize) -> Result<&mut Dma> { - Ok(&mut self.port_state_mut(port)?.input_context) - } - fn endp_ctx( - &mut self, - port: usize, - endp_num_xhc: u8, - ) -> Result<&mut super::context::EndpointContext> { - Ok(self - .input_context(port)? - .device - .endpoints - .get_mut(endp_num_xhc as usize - 1) - .ok_or(Error::new(EIO))?) - } - fn dev_desc(&self, port: usize) -> Result<&DevDesc> { - Ok(&self.port_state(port)?.dev_desc) - } - fn config_descs(&self, port: usize) -> Result<&[ConfDesc]> { - Ok(&self.dev_desc(port)?.config_descs) - } - fn config_desc(&self, port: usize, desc: u8) -> Result<&ConfDesc> { - Ok(self - .config_descs(port)? - .get(usize::from(desc)) - .ok_or(Error::new(EBADF))?) - } - fn endp_descs(&self, port: usize, config_desc: u8, if_desc: u8) -> Result<&[EndpDesc]> { - Ok(&self - .port_state(port)? - .dev_desc - .config_descs - .get(usize::from(config_desc)) - .ok_or(Error::new(EIO))? - .interface_descs - .get(usize::from(if_desc)) - .ok_or(Error::new(EIO))? - .endpoints) - } - fn configure_endpoints(&mut self, port: usize, json_buf: &[u8]) -> Result<()> { + async fn configure_endpoints(&self, port: usize, json_buf: &[u8]) -> Result<()> { let mut req: ConfigureEndpointsReq = serde_json::from_slice(json_buf).or(Err(Error::new(EBADMSG)))?; - if (!self.cap.cic() || !self.op.cie()) + debug!("Running configure endpoints command, at port {}, request: {:?}", port, req); + + if (!self.cap.cic() || !self.op.lock().unwrap().cie()) && (req.config_desc != 0 || req.interface_desc != None || req.alternate_setting != None) { //return Err(Error::new(EOPNOTSUPP)); @@ -570,9 +575,14 @@ impl Xhci { return Err(Error::new(EBADMSG)); } - let (endp_desc_count, new_context_entries) = { - let endpoints = - self.endp_descs(port, req.config_desc, req.interface_desc.unwrap_or(0))?; + let (endp_desc_count, new_context_entries, configuration_value) = { + let mut port_state = self.port_states.get_mut(&port).ok_or(Error::new(EBADFD))?; + port_state.cfg_idx = Some(req.config_desc); + port_state.if_idx = Some(req.interface_desc.unwrap_or(0)); + + let config_desc = port_state.dev_desc.as_ref().unwrap().config_descs.get(usize::from(req.config_desc)).ok_or(Error::new(EBADFD))?; + + let endpoints = &config_desc.interface_descs.get(usize::from(req.interface_desc.unwrap_or(0))).ok_or(Error::new(EBADFD))?.endpoints; if endpoints.len() >= 31 { return Err(Error::new(EIO)); @@ -584,18 +594,20 @@ impl Xhci { Some(l) => Self::endp_num_to_dci(endpoints.len() as u8, l), None => 1, }) + 1, + config_desc.configuration_value, ) }; let lec = self.cap.lec(); let log_max_psa_size = self.cap.max_psa_size(); - let port_speed_id = self.ports[port].speed(); + let port_speed_id = self.ports.lock().unwrap()[port].speed(); let speed_id: &ProtocolSpeed = self .lookup_psiv(port as u8, port_speed_id) .ok_or(Error::new(EIO))?; { - let input_context = self.input_context(port)?; + let port_state = self.port_states.get(&port).ok_or(Error::new(EBADFD))?; + let mut input_context = port_state.input_context.lock().unwrap(); // Configure the slot context as well, which holds the last index of the endp descs. input_context.add_context.write(1); @@ -621,9 +633,9 @@ impl Xhci { for endp_idx in 0..endp_desc_count as u8 { let endp_num = endp_idx + 1; - let endpoints = - self.endp_descs(port, req.config_desc, req.interface_desc.unwrap_or(0))?; - let dev_desc = self.dev_desc(port)?; + let mut port_state = self.port_states.get_mut(&port).ok_or(Error::new(EBADFD))?; + let dev_desc = port_state.dev_desc.as_ref().unwrap(); + let endpoints = &dev_desc.config_descs.get(usize::from(req.config_desc)).ok_or(Error::new(EBADFD))?.interface_descs.get(usize::from(req.interface_desc.unwrap_or(0))).ok_or(Error::new(EBADFD))?.endpoints; let endp_desc = endpoints.get(endp_idx as usize).ok_or(Error::new(EIO))?; let endp_num_xhc = Self::endp_num_to_dci(endp_num, endp_desc); @@ -681,8 +693,6 @@ impl Xhci { assert_eq!(max_error_count & 0x3, max_error_count); assert_ne!(ep_ty, 0); // 0 means invalid. - let port_state = self.port_state_mut(port)?; - let ring_ptr = if usb_log_max_streams.is_some() { let mut array = StreamContextArray::new(1 << (primary_streams + 1))?; @@ -724,10 +734,10 @@ impl Xhci { }; assert_eq!(primary_streams & 0x1F, primary_streams); - let input_context = self.input_context(port)?; + let mut input_context = port_state.input_context.lock().unwrap(); input_context.add_context.writef(1 << endp_num_xhc, true); - let endp_ctx = self.endp_ctx(port, endp_num_xhc)?; + let endp_ctx = input_context.device.endpoints.get_mut(endp_num_xhc as usize - 1).ok_or(Error::new(EIO))?; endp_ctx.a.write( u32::from(mult) << 8 @@ -752,52 +762,70 @@ impl Xhci { .write(u32::from(avg_trb_len) | (u32::from(max_esit_payload_lo) << 16)); } - let slot = self.port_state(port)?.slot; - let input_context_physical = self.input_context(port)?.physical(); - self.execute_command("CONFIGURE_ENDPOINT", |trb, cycle| { - trb.configure_endpoint(slot, input_context_physical, cycle) - })?; + { + let port_state = self.port_states.get(&port).ok_or(Error::new(EBADFD))?; + let slot = port_state.slot; + let input_context_physical = port_state.input_context.lock().unwrap().physical(); + + let (event_trb, command_trb) = self.execute_command(|trb, cycle| { + trb.configure_endpoint(slot, input_context_physical, cycle) + }).await; + + self.event_handler_finished(); + + handle_event_trb("CONFIGURE_ENDPOINT", &event_trb, &command_trb)?; + } // Tell the device about this configuration. - let configuration_value = self.config_desc(port, req.config_desc)?.configuration_value; - self.set_configuration(port, configuration_value)?; + self.set_configuration(port, configuration_value).await?; if let (Some(interface_num), Some(alternate_setting)) = (req.interface_desc, req.alternate_setting) { - self.set_interface(port, interface_num, alternate_setting)?; + self.set_interface(port, interface_num, alternate_setting).await?; } Ok(()) } - fn transfer_read( - &mut self, + async fn transfer_read( + &self, port_num: usize, endp_idx: u8, buf: &mut [u8], ) -> Result<(u8, u32)> { - self.transfer( + if buf.is_empty() { + return Err(Error::new(EINVAL)); + } + let dma_buffer = unsafe { Dma::<[u8]>::zeroed_unsized(buf.len())? }; + + let (completion_code, bytes_transferred, dma_buffer) = self.transfer( port_num, endp_idx, - if !buf.is_empty() { - DeviceReqData::In(buf) - } else { - DeviceReqData::NoData - }, - ) + Some(dma_buffer), + PortReqDirection::DeviceToHost, + ).await?; + + buf.copy_from_slice(&*dma_buffer.as_ref().unwrap()); + Ok((completion_code, bytes_transferred)) } - fn transfer_write(&mut self, port_num: usize, endp_idx: u8, buf: &[u8]) -> Result<(u8, u32)> { - self.transfer( + async fn transfer_write(&self, port_num: usize, endp_idx: u8, sbuf: &[u8]) -> Result<(u8, u32)> { + if sbuf.is_empty() { + return Err(Error::new(EINVAL)); + } + let mut dma_buffer = unsafe { Dma::<[u8]>::zeroed_unsized(sbuf.len()) }?; + dma_buffer.copy_from_slice(sbuf); + + trace!("TRANSFER_WRITE port {} ep {}, buffer at {:p}, size {}, dma buffer {:?}", port_num, endp_idx + 1, sbuf.as_ptr(), sbuf.len(), DmaSliceDbg(&dma_buffer)); + + let (completion_code, bytes_transferred, _) = self.transfer( port_num, endp_idx, - if !buf.is_empty() { - DeviceReqData::Out(buf) - } else { - DeviceReqData::NoData - }, - ) + Some(dma_buffer), + PortReqDirection::HostToDevice, + ).await?; + Ok((completion_code, bytes_transferred)) } - const fn def_control_endp_doorbell() -> u32 { + pub const fn def_control_endp_doorbell() -> u32 { 1 } // TODO: Wrap DCIs and driver-level endp_num into distinct types, due to the high chance of @@ -815,12 +843,6 @@ impl Xhci { unreachable!() } } - fn endp_desc(&self, port_num: usize, endp_num: u8) -> Result<&EndpDesc> { - Ok(self - .endp_descs(port_num, 0, 0)? - .get(usize::from(endp_num) - 1) - .ok_or(Error::new(EBADF))?) - } fn endp_doorbell(endp_num: u8, desc: &EndpDesc, stream_id: u16) -> u32 { let db_target = Self::endp_num_to_dci(endp_num, desc); let db_task_id: u16 = stream_id; @@ -828,45 +850,34 @@ impl Xhci { (u32::from(db_task_id) << 16) | u32::from(db_target) } // TODO: Rename DeviceReqData to something more general. - fn transfer( - &mut self, + async fn transfer( + &self, port_num: usize, endp_idx: u8, - mut buf: DeviceReqData, - ) -> Result<(u8, u32)> { + dma_buf: Option>, + direction: PortReqDirection, + ) -> Result<(u8, u32, Option>)> { // TODO: Check that only readable enpoints are read, etc. let endp_num = endp_idx + 1; - // TODO: Check that buf has a nonzero size, otherwise (at least for Rust's GlobalAlloc), - // UB. - let dma_buffer = match buf { - DeviceReqData::Out(sbuf) => { - if sbuf.is_empty() { - return Err(Error::new(EINVAL)); - } - let mut dma_buffer = unsafe { Dma::<[u8]>::zeroed_unsized(sbuf.len()) }?; - dma_buffer.copy_from_slice(sbuf); - Some(dma_buffer) - } - DeviceReqData::In(ref dbuf) => { - if dbuf.is_empty() { - return Err(Error::new(EINVAL)); - } - Some(unsafe { Dma::<[u8]>::zeroed_unsized(dbuf.len()) }?) - } - DeviceReqData::NoData => None, - }; - let port_state = self + let mut port_state = self .port_states .get_mut(&port_num) .ok_or(Error::new(EBADFD))?; + + let (cfg_idx, if_idx) = match (port_state.cfg_idx, port_state.if_idx) { + (Some(c), Some(i)) => (c, i), + _ => return Err(Error::new(EIO)), + }; + let endp_desc: &EndpDesc = port_state .dev_desc + .as_ref().unwrap() .config_descs - .get(0) + .get(usize::from(cfg_idx)) .ok_or(Error::new(EIO))? .interface_descs - .get(0) + .get(usize::from(if_idx)) .ok_or(Error::new(EIO))? .endpoints .get(endp_idx as usize) @@ -878,7 +889,7 @@ impl Xhci { return Err(Error::new(ENOSYS)); } - if EndpDirection::from(buf.direction()) != endp_desc.direction() { + if EndpDirection::from(direction) != endp_desc.direction() { return Err(Error::new(EBADF)); } @@ -887,23 +898,22 @@ impl Xhci { let (buffer, idt, estimated_td_size) = { let (buffer, idt) = - if buf.len() <= 8 && max_packet_size >= 8 && direction != EndpDirection::In { - buf.map_buf(|sbuf| { + if dma_buf.as_ref().map(|buf| buf.len()).unwrap_or(0) <= 8 && max_packet_size >= 8 && direction != EndpDirection::In { + dma_buf.as_ref().map(|sbuf| { let mut bytes = [0u8; 8]; - bytes[..buf.len()].copy_from_slice(&sbuf[..buf.len()]); - // FIXME: little endian, right? + bytes[..sbuf.len()].copy_from_slice(&sbuf); (u64::from_le_bytes(bytes), true) }) .unwrap_or((0, false)) } else { ( - dma_buffer.as_ref().map(|dma| dma.physical()).unwrap_or(0) as u64, + dma_buf.as_ref().map(|dma| dma.physical()).unwrap_or(0) as u64, false, ) }; let estimated_td_size = cmp::min( u8::try_from( - div_round_up(buf.len(), max_transfer_size as usize) * mem::size_of::(), + div_round_up(dma_buf.as_ref().map(|buf| buf.len()).unwrap_or(0), max_transfer_size as usize) * mem::size_of::(), ) .ok() .unwrap_or(0x1F), @@ -914,7 +924,9 @@ impl Xhci { let stream_id = 1u16; - let mut bytes_left = buf.len(); + let mut bytes_left = dma_buf.as_ref().map(|buf| buf.len()).unwrap_or(0); + + drop(port_state); let event = self.execute_transfer( port_num, @@ -950,159 +962,124 @@ impl Xhci { ControlFlow::Break } }, - )?; + ).await?; + self.event_handler_finished(); - let bytes_transferred = buf.len() as u32 - event.transfer_length(); + let bytes_transferred = dma_buf.as_ref().map(|buf| buf.len() as u32 - event.transfer_length()).unwrap_or(0); - if let DeviceReqData::In(dbuf) = &mut buf { - dbuf.copy_from_slice(&*dma_buffer.as_ref().unwrap()); - } - - Ok((event.completion_code(), bytes_transferred)) + Ok((event.completion_code(), bytes_transferred, dma_buf)) } - pub(crate) fn get_dev_desc(&mut self, port_id: usize) -> Result { - let st = self - .port_states - .get_mut(&port_id) - .ok_or(Error::new(ENOENT))?; - Self::get_dev_desc_raw( - &mut self.ports, - &mut self.run, - &mut self.cmd, - &mut self.dbs, - port_id, - st.slot, - st.endpoint_states - .get_mut(&0) - .ok_or(Error::new(EIO))? - .ring() - .ok_or(Error::new(EIO))?, - ) - } - pub(crate) fn get_dev_desc_raw( - ports: &mut [port::Port], - run: &mut RuntimeRegs, - cmd: &mut CommandRing, - dbs: &mut [Doorbell], + pub async fn get_desc( + &self, port_id: usize, slot: u8, - ring: &mut Ring, ) -> Result { + let ports = self.ports.lock().unwrap(); let port = ports.get(port_id).ok_or(Error::new(ENOENT))?; if !port.flags().contains(port::PortFlags::PORT_CCS) { return Err(Error::new(ENOENT)); } - // TODO: Should the descriptors be stored in PortState? - - run.ints[0].erdp.write(cmd.erdp()); - - let mut dev = Device { - ring, - cmd, - db: &mut dbs[slot as usize], - int: &mut run.ints[0], - }; - - let raw_dd = dev.get_device()?; + let raw_dd = self.fetch_dev_desc(port_id, slot).await?; let (manufacturer_str, product_str, serial_str) = ( if raw_dd.manufacturer_str > 0 { - Some(dev.get_string(raw_dd.manufacturer_str)?) + Some(self.fetch_string_desc(port_id, slot, raw_dd.manufacturer_str).await?) } else { None }, if raw_dd.product_str > 0 { - Some(dev.get_string(raw_dd.product_str)?) + Some(self.fetch_string_desc(port_id, slot, raw_dd.product_str).await?) } else { None }, if raw_dd.serial_str > 0 { - Some(dev.get_string(raw_dd.serial_str)?) + Some(self.fetch_string_desc(port_id, slot, raw_dd.serial_str).await?) } else { None }, ); - let (bos_desc, bos_data) = dev.get_bos()?; + let (bos_desc, bos_data) = self.fetch_bos_desc(port_id, slot).await?; + let supports_superspeed = usb::bos_capability_descs(bos_desc, &bos_data).any(|desc| desc.is_superspeed()); let supports_superspeedplus = usb::bos_capability_descs(bos_desc, &bos_data).any(|desc| desc.is_superspeedplus()); - let config_descs = (0..raw_dd.configurations) - .map(|index| -> Result<_> { - let (desc, data) = dev.get_config(index)?; + let mut config_descs = SmallVec::new(); - let extra_length = desc.total_length as usize - mem::size_of_val(&desc); - let data = &data[..extra_length]; + for index in 0..raw_dd.configurations { + let (desc, data) = self.fetch_config_desc(port_id, slot, index).await?; - let mut i = 0; - let mut descriptors = Vec::new(); + let extra_length = desc.total_length as usize - mem::size_of_val(&desc); + let data = &data[..extra_length]; - while let Some((descriptor, len)) = AnyDescriptor::parse(&data[i..]) { - descriptors.push(descriptor); - i += len; - } + let mut i = 0; + let mut descriptors = Vec::new(); - let mut interface_descs = SmallVec::new(); - let mut iter = descriptors.into_iter(); + while let Some((descriptor, len)) = AnyDescriptor::parse(&data[i..]) { + descriptors.push(descriptor); + i += len; + } - while let Some(item) = iter.next() { - if let AnyDescriptor::Interface(idesc) = item { - let mut endpoints = SmallVec::<[EndpDesc; 4]>::new(); - let mut hid_descs = SmallVec::<[HidDesc; 1]>::new(); + let mut interface_descs = SmallVec::new(); + let mut iter = descriptors.into_iter(); - for _ in 0..idesc.endpoints { + while let Some(item) = iter.next() { + if let AnyDescriptor::Interface(idesc) = item { + let mut endpoints = SmallVec::<[EndpDesc; 4]>::new(); + let mut hid_descs = SmallVec::<[HidDesc; 1]>::new(); + + for _ in 0..idesc.endpoints { + let next = match iter.next() { + Some(AnyDescriptor::Endpoint(n)) => n, + Some(AnyDescriptor::Hid(h)) if idesc.class == 3 => { + hid_descs.push(h.into()); + break; + } + _ => break, + }; + let mut endp = EndpDesc::from(next); + + if supports_superspeed { let next = match iter.next() { - Some(AnyDescriptor::Endpoint(n)) => n, - Some(AnyDescriptor::Hid(h)) if idesc.class == 3 => { - hid_descs.push(h.into()); - break; - } + Some(AnyDescriptor::SuperSpeedCompanion(n)) => n, _ => break, }; - let mut endp = EndpDesc::from(next); + endp.ssc = Some(SuperSpeedCmp::from(next)); - if supports_superspeed { + if endp.has_ssp_companion() && supports_superspeedplus { let next = match iter.next() { - Some(AnyDescriptor::SuperSpeedCompanion(n)) => n, + Some(AnyDescriptor::SuperSpeedPlusCompanion(n)) => n, _ => break, }; - endp.ssc = Some(SuperSpeedCmp::from(next)); - - if endp.has_ssp_companion() && supports_superspeedplus { - let next = match iter.next() { - Some(AnyDescriptor::SuperSpeedPlusCompanion(n)) => n, - _ => break, - }; - endp.sspc = Some(SuperSpeedPlusIsochCmp::from(next)); - } + endp.sspc = Some(SuperSpeedPlusIsochCmp::from(next)); } - endpoints.push(endp); } - - interface_descs.push(IfDesc::new(&mut dev, idesc, endpoints, hid_descs)?); - } else { - // TODO - break; + endpoints.push(endp); } - } - Ok(ConfDesc { - kind: desc.kind, - configuration: if desc.configuration_str > 0 { - Some(dev.get_string(desc.configuration_str)?) - } else { - None - }, - configuration_value: desc.configuration_value, - attributes: desc.attributes, - max_power: desc.max_power, - interface_descs, - }) - }) - .collect::>>()?; + interface_descs.push(self.new_if_desc(port_id, slot, idesc, endpoints, hid_descs).await?); + } else { + // TODO + break; + } + } + + config_descs.push(ConfDesc { + kind: desc.kind, + configuration: if desc.configuration_str > 0 { + Some(self.fetch_string_desc(port_id, slot, desc.configuration_str).await?) + } else { + None + }, + configuration_value: desc.configuration_value, + attributes: desc.attributes, + max_power: desc.max_power, + interface_descs, + }); + }; Ok(DevDesc { kind: raw_dd.kind, @@ -1120,7 +1097,7 @@ impl Xhci { config_descs, }) } - fn port_desc_json(&mut self, port_id: usize) -> Result> { + fn port_desc_json(&self, port_id: usize) -> Result> { let dev_desc = &self .port_states .get(&port_id) @@ -1137,18 +1114,13 @@ impl Xhci { bytes_to_read } - fn port_req_transfer( - &mut self, + async fn port_req_transfer( + &self, port_num: usize, data_buffer: Option<&mut Dma<[u8]>>, setup: usb::Setup, transfer_kind: TransferKind, ) -> Result<()> { - // TODO: This json format might be too high level, but is useful for debugging, - // but when actual device-specific drivers are written, a binary format would - // be better. Maybe something simple like bincode could be used, if a custom binary struct - // is too much overkill. - self.execute_control_transfer( port_num, setup, @@ -1163,10 +1135,10 @@ impl Xhci { ); ControlFlow::Break }, - )?; + ).await?; Ok(()) } - fn port_req_init_st(&mut self, port_num: usize, req: &PortReq) -> Result { + fn port_req_init_st(&self, port_num: usize, req: &PortReq) -> Result { use usb::setup::*; let direction = ReqDirection::from(req.direction); @@ -1203,8 +1175,8 @@ impl Xhci { // FIXME: Make sure there aren't any other PortReq handles, perhaps by storing the state in // PortState? } - fn handle_port_req_write( - &mut self, + async fn handle_port_req_write( + &self, fd: usize, port_num: usize, mut st: PortReqState, @@ -1218,7 +1190,7 @@ impl Xhci { if let PortReqState::TmpSetup(setup) = st { // No need for any additional reads or writes, before completing. - self.port_req_transfer(port_num, None, setup, TransferKind::NoData)?; + self.port_req_transfer(port_num, None, setup, TransferKind::NoData).await?; st = PortReqState::Init; } @@ -1230,7 +1202,7 @@ impl Xhci { } dma_buffer.copy_from_slice(buf); - self.port_req_transfer(port_num, Some(&mut dma_buffer), setup, TransferKind::Out)?; + self.port_req_transfer(port_num, Some(&mut dma_buffer), setup, TransferKind::Out).await?; st = PortReqState::Init; buf.len() @@ -1238,14 +1210,15 @@ impl Xhci { PortReqState::WaitingForDeviceBytes(_, _) => return Err(Error::new(EBADF)), PortReqState::Tmp | PortReqState::TmpSetup(_) => unreachable!(), }; - match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? { + let mut guard = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?; + match &mut *guard { Handle::PortReq(_, ref mut state) => *state = st, _ => unreachable!(), } Ok(bytes_written) } - fn handle_port_req_read( - &mut self, + async fn handle_port_req_read( + &self, fd: usize, port_num: usize, mut st: PortReqState, @@ -1256,7 +1229,7 @@ impl Xhci { if buf.len() != dma_buffer.len() { return Err(Error::new(EINVAL)); } - self.port_req_transfer(port_num, Some(&mut dma_buffer), setup, TransferKind::In)?; + self.port_req_transfer(port_num, Some(&mut dma_buffer), setup, TransferKind::In).await?; buf.copy_from_slice(&dma_buffer); st = PortReqState::Init; @@ -1268,7 +1241,9 @@ impl Xhci { } PortReqState::Tmp | PortReqState::TmpSetup(_) => unreachable!(), }; - match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? { + + let mut guard = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?; + match &mut *guard { Handle::PortReq(_, ref mut state) => *state = st, _ => unreachable!(), } @@ -1276,8 +1251,8 @@ impl Xhci { } } -impl SchemeMut for Xhci { - fn open(&mut self, path: &[u8], flags: usize, uid: u32, _gid: u32) -> Result { +impl Scheme for Xhci { + fn open(&self, path: &[u8], flags: usize, uid: u32, _gid: u32) -> Result { if uid != 0 { return Err(Error::new(EACCES)); } @@ -1302,8 +1277,9 @@ impl SchemeMut for Xhci { if flags & O_DIRECTORY != 0 || flags & O_STAT != 0 { let mut contents = Vec::new(); - for (index, _) in self - .ports + let ports_guard = self.ports.lock().unwrap(); + + for (index, _) in ports_guard .iter() .enumerate() .filter(|(_, port)| port.flags().contains(port::PortFlags::PORT_CCS)) @@ -1443,85 +1419,93 @@ impl SchemeMut for Xhci { _ => return Err(Error::new(ENOENT)), }; - let fd = self.next_handle; - self.next_handle += 1; + let fd = self.next_handle.fetch_add(1, atomic::Ordering::Relaxed); + + trace!("OPENED {} to FD={}, handle: {:?}", path_str, fd, handle); + self.handles.insert(fd, handle); Ok(fd) } - fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result { - match self.handles.get(&id).ok_or(Error::new(EBADF))? { - Handle::TopLevel(_, ref buf) - | Handle::Port(_, _, ref buf) - | Handle::Endpoints(_, _, ref buf) => { + fn fstat(&self, id: usize, stat: &mut Stat) -> Result { + let mut guard = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + match &*guard { + &Handle::TopLevel(_, ref buf) + | &Handle::Port(_, _, ref buf) + | &Handle::Endpoints(_, _, ref buf) => { stat.st_mode = MODE_DIR; stat.st_size = buf.len() as u64; } - Handle::PortDesc(_, _, ref buf) => { + &Handle::PortDesc(_, _, ref buf) => { stat.st_mode = MODE_FILE; stat.st_size = buf.len() as u64; } - Handle::PortReq(_, PortReqState::WaitingForDeviceBytes(ref buf, _)) - | Handle::PortReq(_, PortReqState::WaitingForHostBytes(ref buf, _)) => { + &Handle::PortReq(_, PortReqState::WaitingForDeviceBytes(ref buf, _)) + | &Handle::PortReq(_, PortReqState::WaitingForHostBytes(ref buf, _)) => { stat.st_mode = MODE_CHR; stat.st_size = buf.len() as u64; } - Handle::PortReq(_, PortReqState::Tmp) - | Handle::PortReq(_, PortReqState::TmpSetup(_)) => unreachable!(), + &Handle::PortReq(_, PortReqState::Tmp) + | &Handle::PortReq(_, PortReqState::TmpSetup(_)) => unreachable!(), - Handle::PortState(_, _) | Handle::PortReq(_, _) => stat.st_mode = MODE_CHR, - Handle::Endpoint(_, _, st) => match st { - EndpointHandleTy::Ctl | EndpointHandleTy::Data => stat.st_mode = MODE_CHR, - EndpointHandleTy::Root(_, ref buf) => { + &Handle::PortState(_, _) | &Handle::PortReq(_, _) => stat.st_mode = MODE_CHR, + &Handle::Endpoint(_, _, ref st) => match st { + &EndpointHandleTy::Ctl | &EndpointHandleTy::Data => stat.st_mode = MODE_CHR, + &EndpointHandleTy::Root(_, ref buf) => { stat.st_mode = MODE_DIR; stat.st_size = buf.len() as u64; } }, - Handle::ConfigureEndpoints(_) => { + &Handle::ConfigureEndpoints(_) => { stat.st_mode = MODE_CHR | 0o200; // write only } } Ok(0) } - fn fpath(&mut self, fd: usize, buffer: &mut [u8]) -> Result { - // XXX: write!() should return the length instead of (). - let mut src = Vec::::new(); - match self.handles.get(&fd).ok_or(Error::new(EBADF))? { - Handle::TopLevel(_, _) => write!(src, "/").unwrap(), - Handle::Port(port_num, _, _) => write!(src, "/port{}/", port_num).unwrap(), - Handle::PortDesc(port_num, _, _) => { - write!(src, "/port{}/descriptors", port_num).unwrap() + fn fpath(&self, fd: usize, buffer: &mut [u8]) -> Result { + let mut cursor = io::Cursor::new(buffer); + + let guard = self.handles.get(&fd).ok_or(Error::new(EBADF))?; + match &*guard { + &Handle::TopLevel(_, _) => write!(cursor, "/").unwrap(), + &Handle::Port(port_num, _, _) => write!(cursor, "/port{}/", port_num).unwrap(), + &Handle::PortDesc(port_num, _, _) => { + write!(cursor, "/port{}/descriptors", port_num).unwrap() } - Handle::PortState(port_num, _) => write!(src, "/port{}/state", port_num).unwrap(), - Handle::PortReq(port_num, _) => write!(src, "/port{}/request", port_num).unwrap(), - Handle::Endpoints(port_num, _, _) => { - write!(src, "/port{}/endpoints/", port_num).unwrap() + &Handle::PortState(port_num, _) => write!(cursor, "/port{}/state", port_num).unwrap(), + &Handle::PortReq(port_num, _) => write!(cursor, "/port{}/request", port_num).unwrap(), + &Handle::Endpoints(port_num, _, _) => { + write!(cursor, "/port{}/endpoints/", port_num).unwrap() } - Handle::Endpoint(port_num, endp_num, st) => write!( - src, + &Handle::Endpoint(port_num, endp_num, ref st) => write!( + cursor, "/port{}/endpoints/{}/{}", port_num, endp_num, match st { - EndpointHandleTy::Root(_, _) => "", - EndpointHandleTy::Ctl => "ctl", - EndpointHandleTy::Data => "data", + &EndpointHandleTy::Root(_, _) => "", + &EndpointHandleTy::Ctl => "ctl", + &EndpointHandleTy::Data => "data", } ) .unwrap(), - Handle::ConfigureEndpoints(port_num) => { - write!(src, "/port{}/configure", port_num).unwrap() + &Handle::ConfigureEndpoints(port_num) => { + write!(cursor, "/port{}/configure", port_num).unwrap() } } - let bytes_to_read = cmp::min(src.len(), buffer.len()); - buffer[..bytes_to_read].copy_from_slice(&src[..bytes_to_read]); - Ok(bytes_to_read) + let src_len = usize::try_from(cursor.seek(io::SeekFrom::End(0)).unwrap()).unwrap(); + Ok(src_len) } - fn seek(&mut self, fd: usize, pos: usize, whence: usize) -> Result { - match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? { + fn seek(&self, fd: usize, pos: usize, whence: usize) -> Result { + let mut guard = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?; + + trace!("SEEK fd={}, handle={:?}, pos {}, whence {}", fd, guard, pos, whence); + + match &mut *guard { // Directories, or fixed files Handle::TopLevel(ref mut offset, ref buf) | Handle::Port(_, ref mut offset, ref buf) @@ -1552,8 +1536,10 @@ impl SchemeMut for Xhci { } } - fn read(&mut self, fd: usize, buf: &mut [u8]) -> Result { - match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? { + fn read(&self, fd: usize, buf: &mut [u8]) -> Result { + let mut guard = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?; + trace!("READ fd={}, handle={:?}, buf=(addr {:p}, length {})", fd, guard, buf.as_ptr(), buf.len()); + match &mut *guard { Handle::TopLevel(ref mut offset, ref src_buf) | Handle::Port(_, ref mut offset, ref src_buf) | Handle::PortDesc(_, ref mut offset, ref src_buf) @@ -1571,7 +1557,7 @@ impl SchemeMut for Xhci { &mut Handle::Endpoint(port_num, endp_num, ref mut st) => match st { EndpointHandleTy::Ctl => self.on_read_endp_ctl(port_num, endp_num, buf), - EndpointHandleTy::Data => self.on_read_endp_data(port_num, endp_num, buf), + EndpointHandleTy::Data => block_on(self.on_read_endp_data(port_num, endp_num, buf)), EndpointHandleTy::Root(_, _) => return Err(Error::new(EBADF)), }, &mut Handle::PortState(port_num, ref mut offset) => { @@ -1600,31 +1586,36 @@ impl SchemeMut for Xhci { } &mut Handle::PortReq(port_num, ref mut st) => { let state = std::mem::replace(st, PortReqState::Tmp); - self.handle_port_req_read(fd, port_num, state, buf) + drop(guard); // release the lock + block_on(self.handle_port_req_read(fd, port_num, state, buf)) } } } - fn write(&mut self, fd: usize, buf: &[u8]) -> Result { - match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? { + fn write(&self, fd: usize, buf: &[u8]) -> Result { + let mut guard = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?; + trace!("WRITE fd={}, handle={:?}, buf=(addr {:p}, length {})", fd, guard, buf.as_ptr(), buf.len()); + + match &mut *guard { &mut Handle::ConfigureEndpoints(port_num) => { - self.configure_endpoints(port_num, buf)?; + block_on(self.configure_endpoints(port_num, buf))?; Ok(buf.len()) } &mut Handle::Endpoint(port_num, endp_num, ref ep_file_ty) => match ep_file_ty { - EndpointHandleTy::Ctl => self.on_write_endp_ctl(port_num, endp_num, buf), - EndpointHandleTy::Data => self.on_write_endp_data(port_num, endp_num, buf), + EndpointHandleTy::Ctl => block_on(self.on_write_endp_ctl(port_num, endp_num, buf)), + EndpointHandleTy::Data => block_on(self.on_write_endp_data(port_num, endp_num, buf)), EndpointHandleTy::Root(_, _) => return Err(Error::new(EBADF)), }, &mut Handle::PortReq(port_num, ref mut st) => { let state = std::mem::replace(st, PortReqState::Tmp); - self.handle_port_req_write(fd, port_num, state, buf) + drop(guard); // release the lock + block_on(self.handle_port_req_write(fd, port_num, state, buf)) } // TODO: Introduce PortReqState::Waiting, which this write call changes to // PortReqState::ReadyToWrite when all bytes are written. _ => return Err(Error::new(EBADF)), } } - fn close(&mut self, fd: usize) -> Result { + fn close(&self, fd: usize) -> Result { if self.handles.remove(&fd).is_none() { return Err(Error::new(EBADF)); } @@ -1632,17 +1623,33 @@ impl SchemeMut for Xhci { } } impl Xhci { - pub fn get_endp_status(&mut self, port_num: usize, endp_num: u8) -> Result { - let slot = self + pub fn get_endp_status(&self, port_num: usize, endp_num: u8) -> Result { + let port_state = self .port_states .get(&port_num) - .ok_or(Error::new(EBADFD))? - .slot; + .ok_or(Error::new(EBADFD))?; + + let slot = port_state.slot; + + let endp_desc = port_state + .dev_desc + .as_ref().unwrap() + .config_descs + .get(0) + .ok_or(Error::new(EIO))? + .interface_descs + .get(0) + .ok_or(Error::new(EIO))? + .endpoints + .get(endp_num as usize - 1) + .ok_or(Error::new(EBADFD))?; + let endp_num_xhc = if endp_num != 0 { - Self::endp_num_to_dci(endp_num, self.endp_desc(port_num, endp_num)?) + Self::endp_num_to_dci(endp_num, endp_desc) } else { 1 }; + let raw = self .dev_ctx .contexts @@ -1654,6 +1661,7 @@ impl Xhci { .a .read() & super::context::ENDPOINT_CONTEXT_STATUS_MASK; + Ok(match raw { 0 => EndpointStatus::Disabled, 1 => EndpointStatus::Enabled, @@ -1663,8 +1671,8 @@ impl Xhci { _ => return Err(Error::new(EIO)), }) } - pub fn on_req_reset_device( - &mut self, + pub async fn on_req_reset_device( + &self, port_num: usize, endp_num: u8, clear_feature: bool, @@ -1674,8 +1682,8 @@ impl Xhci { } // Change the endpoint state from anything, but most likely HALTED (otherwise resetting // would be quite meaningless), to stopped. - self.reset_endpoint(port_num, endp_num, false)?; - self.restart_endpoint(port_num, endp_num)?; + self.reset_endpoint(port_num, endp_num, false).await?; + self.restart_endpoint(port_num, endp_num).await?; if clear_feature { self.device_req_no_data( @@ -1687,35 +1695,22 @@ impl Xhci { index: 0, // TODO: interface num length: 0, }, - )?; + ).await?; } Ok(()) } - pub fn restart_endpoint(&mut self, port_num: usize, endp_num: u8) -> Result<()> { - let port_state = self + pub async fn restart_endpoint(&self, port_num: usize, endp_num: u8) -> Result<()> { + let mut port_state = self .port_states .get_mut(&port_num) .ok_or(Error::new(EBADFD))?; - let direction = if endp_num != 0 { - let endp_desc = port_state - .dev_desc - .config_descs - .get(0) - .ok_or(Error::new(EIO))? - .interface_descs - .get(0) - .ok_or(Error::new(EIO))? - .endpoints - .get(endp_num as usize - 1) - .ok_or(Error::new(EBADFD))?; - endp_desc.direction() - } else { - EndpDirection::Bidirectional - }; - let endpoint_state: &mut EndpointState = port_state + let slot = port_state.slot; + + let mut endpoint_state = port_state .endpoint_states .get_mut(&endp_num) .ok_or(Error::new(EBADFD))?; + let (has_streams, ring) = match &mut endpoint_state.transfer { &mut super::RingOrStreams::Ring(ref mut ring) => (false, ring), &mut super::RingOrStreams::Streams(ref mut arr) => { @@ -1727,16 +1722,36 @@ impl Xhci { cmd.transfer_no_op(0, false, false, false, cycle); let deque_ptr_and_cycle = ring.register(); - let slot = port_state.slot; - self.set_tr_deque_ptr(port_num, endp_num, deque_ptr_and_cycle)?; + let endp_desc = port_state + .dev_desc + .as_ref().unwrap() + .config_descs + .get(0) + .ok_or(Error::new(EIO))? + .interface_descs + .get(0) + .ok_or(Error::new(EIO))? + .endpoints + .get(endp_num as usize - 1) + .ok_or(Error::new(EBADFD))?; + + let doorbell = if endp_num != 0 { + let stream_id = 1u16; + + Self::endp_doorbell( + endp_num, + endp_desc, + if has_streams { stream_id } else { 0 }, + ) + } else { + Self::def_control_endp_doorbell() + }; + + self.dbs.lock().unwrap()[slot as usize].write(doorbell); + + self.set_tr_deque_ptr(port_num, endp_num, deque_ptr_and_cycle).await?; - let stream_id = 1u16; - self.dbs[slot as usize].write(Self::endp_doorbell( - endp_num, - self.endp_desc(port_num, endp_num)?, - if has_streams { stream_id } else { 0 }, - )); Ok(()) } pub fn endp_direction(&self, port_num: usize, endp_num: u8) -> Result { @@ -1745,6 +1760,7 @@ impl Xhci { .get(&port_num) .ok_or(Error::new(EIO))? .dev_desc + .as_ref().unwrap() .config_descs .first() .ok_or(Error::new(EIO))? @@ -1759,16 +1775,24 @@ impl Xhci { pub fn slot(&self, port_num: usize) -> Result { Ok(self.port_states.get(&port_num).ok_or(Error::new(EIO))?.slot) } - pub fn set_tr_deque_ptr( - &mut self, + pub async fn set_tr_deque_ptr( + &self, port_num: usize, endp_num: u8, deque_ptr_and_cycle: u64, ) -> Result<()> { - let slot = self.slot(port_num)?; - let endp_num_xhc = Self::endp_num_to_dci(endp_num, self.endp_desc(port_num, endp_num)?); + let port_state = self.port_states.get(&port_num).ok_or(Error::new(EBADFD))?; + let slot = port_state.slot; - self.execute_command("SET_TR_DEQUEUE_POINTER", |trb, cycle| { + let (cfg_idx, if_idx) = match (port_state.cfg_idx, port_state.if_idx) { + (Some(c), Some(i)) => (c, i), + _ => return Err(Error::new(EIO)), + }; + + let endp_desc = port_state.dev_desc.as_ref().unwrap().config_descs[usize::from(cfg_idx)].interface_descs[usize::from(if_idx)].endpoints.get(usize::from(endp_num)).ok_or(Error::new(EBADFD))?; + let endp_num_xhc = Self::endp_num_to_dci(endp_num, endp_desc); + + let (event_trb, command_trb) = self.execute_command(|trb, cycle| { trb.set_tr_deque_ptr( deque_ptr_and_cycle, cycle, @@ -1777,24 +1801,28 @@ impl Xhci { endp_num_xhc, slot, ) - })?; + }).await; + self.event_handler_finished(); - Ok(()) + handle_event_trb("SET_TR_DEQUEUE_PTR", &event_trb, &command_trb) } - pub fn on_write_endp_ctl( - &mut self, + pub async fn on_write_endp_ctl( + &self, port_num: usize, endp_num: u8, buf: &[u8], ) -> Result { - let ep_if_state = &mut self + let mut port_state = self .port_states .get_mut(&port_num) - .ok_or(Error::new(EBADF))? + .ok_or(Error::new(EBADF))?; + + let ep_if_state = &mut port_state .endpoint_states .get_mut(&endp_num) .ok_or(Error::new(EBADF))? .driver_if_state; + let req = serde_json::from_slice::(buf).or(Err(Error::new(EBADMSG)))?; match req { XhciEndpCtlReq::Status => match ep_if_state { @@ -1804,9 +1832,7 @@ impl Xhci { } }, XhciEndpCtlReq::Reset { no_clear_feature } => match ep_if_state { - EndpIfState::Init => { - self.on_req_reset_device(port_num, endp_num, !no_clear_feature)? - } + EndpIfState::Init => self.on_req_reset_device(port_num, endp_num, !no_clear_feature).await?, other => { return Err(Error::new(EBADF)); } @@ -1816,16 +1842,18 @@ impl Xhci { if direction == XhciEndpCtlDirection::NoData { // Yield the result directly because no bytes have to be sent or received // beforehand. - let (completion_code, bytes_transferred) = - self.transfer(port_num, endp_num - 1, DeviceReqData::NoData)?; + let (completion_code, bytes_transferred, _) = + self.transfer(port_num, endp_num - 1, None, PortReqDirection::DeviceToHost).await?; if bytes_transferred > 0 { return Err(Error::new(EIO)); } let result = Self::transfer_result(completion_code, 0); - let new_state = &mut self + + let mut port_state = self .port_states .get_mut(&port_num) - .ok_or(Error::new(EBADF))? + .ok_or(Error::new(EBADF))?; + let new_state = &mut port_state .endpoint_states .get_mut(&endp_num) .ok_or(Error::new(EBADF))? @@ -1864,13 +1892,17 @@ impl Xhci { bytes_transferred, } } - pub fn on_write_endp_data( - &mut self, + pub async fn on_write_endp_data( + &self, port_num: usize, endp_num: u8, buf: &[u8], ) -> Result { - let ep_if_state = &mut self.endpoint_state_mut(port_num, endp_num)?.driver_if_state; + let mut port_state = self.port_states.get_mut(&port_num).ok_or(Error::new(EBADFD))?; + let mut endpoint_state = port_state.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADFD))?; + + let ep_if_state = &mut endpoint_state.driver_if_state; + match ep_if_state { &mut EndpIfState::WaitingForDataPipe { direction: XhciEndpCtlDirection::Out, @@ -1880,15 +1912,18 @@ impl Xhci { if buf.len() > total_bytes_to_transfer as usize - bytes_transferred as usize { return Err(Error::new(EINVAL)); } + drop(port_state); let (completion_code, some_bytes_transferred) = - self.transfer_write(port_num, endp_num - 1, buf)?; + self.transfer_write(port_num, endp_num - 1, buf).await?; let result = Self::transfer_result(completion_code, some_bytes_transferred); // To avoid having to read from the Ctl interface file, the client should stop // invoking further data transfer calls if any single transfer returns fewer bytes // than requested. - let ep_if_state = &mut self.endpoint_state_mut(port_num, endp_num)?.driver_if_state; + let mut port_state = self.port_states.get_mut(&port_num).ok_or(Error::new(EBADFD))?; + let mut endpoint_state = port_state.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADFD))?; + let ep_if_state = &mut endpoint_state.driver_if_state; if let &mut EndpIfState::WaitingForDataPipe { direction: XhciEndpCtlDirection::Out, @@ -1910,15 +1945,17 @@ impl Xhci { } } pub fn on_read_endp_ctl( - &mut self, + &self, port_num: usize, endp_num: u8, buf: &mut [u8], ) -> Result { - let ep_if_state = &mut self + let port_state = &mut self .port_states .get_mut(&port_num) - .ok_or(Error::new(EBADF))? + .ok_or(Error::new(EBADF))?; + + let ep_if_state = &mut port_state .endpoint_states .get_mut(&endp_num) .ok_or(Error::new(EBADF))? @@ -1942,20 +1979,23 @@ impl Xhci { serde_json::to_writer(&mut cursor, &res).or(Err(Error::new(EIO)))?; Ok(cursor.seek(io::SeekFrom::Current(0)).unwrap() as usize) } - pub fn on_read_endp_data( - &mut self, + pub async fn on_read_endp_data( + &self, port_num: usize, endp_num: u8, buf: &mut [u8], ) -> Result { - let ep_if_state = &mut self + let mut port_state = self .port_states .get_mut(&port_num) - .ok_or(Error::new(EBADF))? + .ok_or(Error::new(EBADF))?; + + let mut ep_if_state = &mut port_state .endpoint_states .get_mut(&endp_num) .ok_or(Error::new(EBADF))? .driver_if_state; + match ep_if_state { &mut EndpIfState::WaitingForDataPipe { direction: XhciEndpCtlDirection::In, @@ -1966,22 +2006,27 @@ impl Xhci { return Err(Error::new(EINVAL)); } + drop(port_state); let (completion_code, some_bytes_transferred) = - self.transfer_read(port_num, endp_num - 1, buf)?; + self.transfer_read(port_num, endp_num - 1, buf).await?; // Just as with on_write_endp_data, a client issuing multiple reads must always // stop reading if one read returns fewer bytes than expected. let result = Self::transfer_result(completion_code, some_bytes_transferred); - let ep_if_state = &mut self + let mut port_state = self .port_states .get_mut(&port_num) - .ok_or(Error::new(EBADF))? + .ok_or(Error::new(EBADF))?; + + let mut ep_state = port_state .endpoint_states .get_mut(&endp_num) - .ok_or(Error::new(EBADF))? - .driver_if_state; + .ok_or(Error::new(EBADF))?; + + let ep_if_state = &mut ep_state.driver_if_state; + if let &mut EndpIfState::WaitingForDataPipe { direction: XhciEndpCtlDirection::In, bytes_to_transfer, @@ -2001,9 +2046,35 @@ impl Xhci { _ => return Err(Error::new(EBADF)), } } + /// Notifies the xHC that the current event handler has finished, so that new interrupts can be + /// sent. This is required after each invocation of `Self::execute_command`. + /// + /// # Locking + /// This function locks `Xhci::run`. + pub fn event_handler_finished(&self) { + trace!("Event handler finished"); + // write 1 to EHB to clear it + self.run.lock().unwrap().ints[0].erdp.writef(1 << 3, true); + } +} +pub fn handle_event_trb(name: &str, event_trb: &Trb, command_trb: &Trb) -> Result<()> { + if event_trb.completion_code() == TrbCompletionCode::Success as u8 { + Ok(()) + } else { + error!("{} command (TRB {:?}) failed with event trb {:?}", name, command_trb, event_trb); + Err(Error::new(EIO)) + } +} +pub fn handle_transfer_event_trb(name: &str, event_trb: &Trb, transfer_trb: &Trb) -> Result<()> { + if event_trb.completion_code() == TrbCompletionCode::Success as u8 || event_trb.completion_code() == TrbCompletionCode::ShortPacket as u8 { + Ok(()) + } else { + error!("{} transfer (TRB {:?}) failed with event trb {:?}", name, transfer_trb, event_trb); + Err(Error::new(EIO)) + } } use std::ops::{Add, Div, Rem}; -fn div_round_up(a: T, b: T) -> T +pub fn div_round_up(a: T, b: T) -> T where T: Add + Div + Rem + PartialEq + From + Copy, { diff --git a/xhcid/src/xhci/trb.rs b/xhcid/src/xhci/trb.rs index d6d4cb30e0..a26a28f626 100644 --- a/xhcid/src/xhci/trb.rs +++ b/xhcid/src/xhci/trb.rs @@ -5,6 +5,7 @@ use syscall::io::{Io, Mmio}; use super::context::StreamContextType; #[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum TrbType { Reserved, /* Transfer */ @@ -33,8 +34,8 @@ pub enum TrbType { ForceHeader, NoOpCmd, /* Reserved */ - Rsv24, - Rsv25, + GetExtendedProperty, + SetExtendedProperty, Rsv26, Rsv27, Rsv28, @@ -54,6 +55,7 @@ pub enum TrbType { } #[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum TrbCompletionCode { Invalid, Success, @@ -157,6 +159,34 @@ impl Trb { pub fn completion_param(&self) -> u32 { self.status.read() & TRB_STATUS_COMPLETION_PARAM_MASK } + fn has_completion_trb_pointer(&self) -> bool { + if self.completion_code() == TrbCompletionCode::RingUnderrun as u8 || self.completion_code() == TrbCompletionCode::RingOverrun as u8 { + false + } else if self.completion_code() == TrbCompletionCode::VfEventRingFull as u8 { + false + } else { + true + } + } + pub fn completion_trb_pointer(&self) -> Option { + debug_assert_eq!(self.trb_type(), TrbType::CommandCompletion as u8); + + if self.has_completion_trb_pointer() { + Some(self.data.read()) + } else { + None + } + } + pub fn transfer_event_trb_pointer(&self) -> Option { + debug_assert_eq!(self.trb_type(), TrbType::Transfer as u8); + + if self.has_completion_trb_pointer() { + Some(self.data.read()) + } else { + None + } + } + pub fn event_slot(&self) -> u8 { (self.control.read() >> 24) as u8 } @@ -203,6 +233,15 @@ impl Trb { | (cycle as u32), ); } + pub fn disable_slot(&mut self, slot: u8, cycle: bool) { + self.set( + 0, + 0, + (u32::from(slot) << 24) + | ((TrbType::DisableSlot as u32) << 10) + | u32::from(cycle) + ); + } pub fn address_device(&mut self, slot_id: u8, input_ctx_ptr: usize, bsr: bool, cycle: bool) { assert_eq!( @@ -335,13 +374,15 @@ impl Trb { ); } - pub fn status(&mut self, input: bool, cycle: bool) { + pub fn status(&mut self, interrupter: u16, input: bool, ioc: bool, ch: bool, ent: bool, cycle: bool) { self.set( 0, - 0, - ((input as u32) << 16) + u32::from(interrupter) << 22, + (u32::from(input) << 16) | ((TrbType::StatusStage as u32) << 10) - | (1 << 5) + | (u32::from(ioc) << 5) + | (u32::from(ch) << 4) + | (u32::from(ent) << 1) | (cycle as u32), ); } @@ -374,6 +415,43 @@ impl Trb { | ((TrbType::Normal as u32) << 10), ) } + pub fn is_command_trb(&self) -> bool { + let valid_trb_types = [ + TrbType::NoOpCmd as u8, + TrbType::EnableSlot as u8, + TrbType::DisableSlot as u8, + TrbType::AddressDevice as u8, + TrbType::ConfigureEndpoint as u8, + TrbType::EvaluateContext as u8, + TrbType::ResetEndpoint as u8, + TrbType::StopEndpoint as u8, + TrbType::SetTrDequeuePointer as u8, + TrbType::ResetDevice as u8, + TrbType::ForceEvent as u8, + TrbType::NegotiateBandwidth as u8, + TrbType::SetLatencyToleranceValue as u8, + TrbType::GetPortBandwidth as u8, + TrbType::ForceHeader as u8, + TrbType::GetExtendedProperty as u8, + TrbType::SetExtendedProperty as u8, + ]; + valid_trb_types.contains(&self.trb_type()) + } + pub fn is_transfer_trb(&self) -> bool { + // XXX: Unfortunately, the only way to use match statements with integer constants, is to + // precast them into valid enum values, which either requires a derive macro such as + // num_traits's #[derive(FromPrimitive)], or manually writing the reverse match statement + // first. + let valid_trb_types = [ + TrbType::Normal as u8, + TrbType::SetupStage as u8, + TrbType::DataStage as u8, + TrbType::StatusStage as u8, + TrbType::Isoch as u8, + TrbType::NoOp as u8, + ]; + valid_trb_types.contains(&self.trb_type()) + } } impl fmt::Debug for Trb {