This commit is contained in:
Jeremy Soller
2020-05-06 08:59:01 -06:00
22 changed files with 2641 additions and 1048 deletions
Generated
+93 -81
View File
@@ -42,13 +42,18 @@ dependencies = [
"nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arrayvec"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"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)",
"hermit-abi 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -133,7 +138,7 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.50"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -144,10 +149,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"
source = "git+https://gitlab.redox-os.org/redox-os/chashmap.git#9a36a4df91930628390d70b697800e32b9e7c9bd"
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)",
"parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -157,7 +162,7 @@ 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)",
"time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -355,7 +360,7 @@ dependencies = [
"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)",
"pin-utils 0.1.0 (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)",
@@ -374,10 +379,10 @@ dependencies = [
[[package]]
name = "hermit-abi"
version = "0.1.10"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -395,8 +400,8 @@ dependencies = [
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -439,7 +444,7 @@ name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -478,7 +483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.68"
version = "0.2.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -497,7 +502,7 @@ dependencies = [
[[package]]
name = "lock_api"
version = "0.3.3"
version = "0.3.4"
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)",
@@ -560,7 +565,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.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (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)",
@@ -579,7 +584,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.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (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)",
@@ -593,7 +598,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.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.16 (git+https://gitlab.redox-os.org/redox-os/mio)",
]
@@ -614,7 +619,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.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -627,7 +632,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.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (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)",
@@ -647,7 +652,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.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (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)",
@@ -669,7 +674,7 @@ version = "0.0.1"
source = "git+https://github.com/willem66745/ntpclient-rust#7e3bdf60eb940825789a8da5181025320e3050b0"
dependencies = [
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -711,11 +716,11 @@ dependencies = [
[[package]]
name = "num_cpus"
version = "1.12.0"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"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)",
"hermit-abi 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -727,11 +732,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "nvmed"
version = "0.1.0"
dependencies = [
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"block-io-wrapper 0.1.0",
"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)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"partitionlib 0.1.0 (git+https://gitlab.redox-os.org/redox-os/partitionlib.git)",
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
"spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"pcid 0.1.0",
"redox-log 0.1.0 (git+https://gitlab.redox-os.org/redox-os/redox-log.git?tag=v0.1.0)",
"redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall.git)",
"smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -762,12 +773,11 @@ dependencies = [
[[package]]
name = "parking_lot"
version = "0.9.0"
version = "0.10.2"
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)",
"lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -775,7 +785,7 @@ name = "parking_lot_core"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (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)",
@@ -784,15 +794,14 @@ dependencies = [
[[package]]
name = "parking_lot_core"
version = "0.6.2"
version = "0.7.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)",
"libc 0.2.69 (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)",
"smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -812,9 +821,9 @@ version = "1.0.2"
source = "git+https://github.com/a8m/pb#87c29c05486afa7335916c870ea3621ff7ef2966"
dependencies = [
"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)",
"libc 0.2.69 (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)",
"time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -823,9 +832,9 @@ name = "pbr"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (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)",
"time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -836,13 +845,15 @@ 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)",
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"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?tag=v0.1.0)",
"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)",
"smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -860,7 +871,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pin-utils"
version = "0.1.0-alpha.4"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -909,7 +920,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.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (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)",
@@ -964,7 +975,7 @@ name = "rand_jitter"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (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)",
]
@@ -976,7 +987,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.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (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)",
@@ -1019,10 +1030,11 @@ dependencies = [
[[package]]
name = "redox-log"
version = "0.1.0"
source = "git+https://gitlab.redox-os.org/redox-os/redox-log.git#08693d48b2d7b56fcb07a1e62e257bacce749cef"
source = "git+https://gitlab.redox-os.org/redox-os/redox-log.git?tag=v0.1.0#30f6bf2464c462c32cd215bc0f1eedafa0707a04"
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)",
"smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1060,9 +1072,9 @@ name = "ring"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.51 (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.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1161,7 +1173,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.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (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)",
@@ -1173,7 +1185,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.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1232,7 +1244,7 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.2.0"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1284,7 +1296,7 @@ name = "termion"
version = "1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (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)",
@@ -1300,15 +1312,15 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.14"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"thiserror-impl 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror-impl 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thiserror-impl"
version = "1.0.14"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1318,11 +1330,10 @@ dependencies = [
[[package]]
name = "time"
version = "0.1.42"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"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)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1334,7 +1345,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)",
"mio 0.6.16 (git+https://gitlab.redox-os.org/redox-os/mio)",
"num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-codec 0.1.1 (git+https://gitlab.redox-os.org/redox-os/tokio)",
"tokio-current-thread 0.1.4 (git+https://gitlab.redox-os.org/redox-os/tokio)",
"tokio-executor 0.1.5 (git+https://gitlab.redox-os.org/redox-os/tokio)",
@@ -1405,7 +1416,7 @@ dependencies = [
"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)",
"mio 0.6.16 (git+https://gitlab.redox-os.org/redox-os/mio)",
"num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-executor 0.1.5 (git+https://gitlab.redox-os.org/redox-os/tokio)",
@@ -1434,7 +1445,7 @@ dependencies = [
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-executor 0.1.5 (git+https://gitlab.redox-os.org/redox-os/tokio)",
]
@@ -1472,7 +1483,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.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (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)",
@@ -1520,7 +1531,7 @@ name = "unicode-normalization"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1573,7 +1584,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.14 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
"xhcid 0.1.0",
]
@@ -1700,13 +1711,13 @@ dependencies = [
"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-log 0.1.0 (git+https://gitlab.redox-os.org/redox-os/redox-log.git?tag=v0.1.0)",
"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.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)",
"thiserror 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1714,6 +1725,7 @@ dependencies = [
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum arg_parser 0.1.0 (git+https://gitlab.redox-os.org/redox-os/arg-parser.git)" = "<none>"
"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
@@ -1726,7 +1738,7 @@ dependencies = [
"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
"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 cc 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9384ca4b90c0ea47e19a5c996d6643a3e73dedf9b89c65efb67587e34da1bb"
"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)" = "<none>"
"checksum chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
@@ -1753,7 +1765,7 @@ dependencies = [
"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)" = "<none>"
"checksum hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e"
"checksum hermit-abi 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15"
"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"
@@ -1763,10 +1775,10 @@ 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.68 (registry+https://github.com/rust-lang/crates.io-index)" = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0"
"checksum libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"
"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 lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
"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"
@@ -1787,19 +1799,19 @@ dependencies = [
"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
"checksum num-iter 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00"
"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
"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.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce"
"checksum parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
"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 parking_lot_core 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
"checksum partitionlib 0.1.0 (git+https://gitlab.redox-os.org/redox-os/partitionlib.git)" = "<none>"
"checksum pbr 1.0.2 (git+https://github.com/a8m/pb)" = "<none>"
"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 pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
"checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
"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"
@@ -1817,7 +1829,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)" = "<none>"
"checksum redox-log 0.1.0 (git+https://gitlab.redox-os.org/redox-os/redox-log.git?tag=v0.1.0)" = "<none>"
"checksum redox_event 0.1.0 (git+https://gitlab.redox-os.org/redox-os/event.git)" = "<none>"
"checksum redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall.git)" = "<none>"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
@@ -1842,7 +1854,7 @@ dependencies = [
"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"
"checksum smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a"
"checksum spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ceac490aa12c567115b40b7b7fceca03a6c9d53d5defea066123debc83c5dc1f"
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
"checksum stb_truetype 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9d1bec4382294c5a680fcebd29f8451e8d8c04479a026f6909004e2ab1cb425d"
@@ -1851,9 +1863,9 @@ dependencies = [
"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.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 thiserror 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "54b3d3d2ff68104100ab257bb6bb0cb26c901abe4bd4ba15961f3bf867924012"
"checksum thiserror-impl 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972988113b7715266f91250ddb98070d033c62a011fa0fcc57434a649310dd"
"checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
"checksum tokio 0.1.13 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "<none>"
"checksum tokio-codec 0.1.1 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "<none>"
"checksum tokio-current-thread 0.1.4 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "<none>"
+3 -2
View File
@@ -28,6 +28,7 @@ name = "NVME storage"
class = 1
subclass = 8
command = ["nvmed", "$NAME", "$BAR0", "$BARSIZE0", "$IRQ"]
use_channel = true
# vboxd
[[drivers]]
@@ -43,5 +44,5 @@ name = "XHCI"
class = 12
subclass = 3
interface = 48
command = ["xhcid", "$NAME", "$BAR0", "$IRQ"]
channel_name = "pcid-xhcid"
command = ["xhcid"]
use_channel = true
+9 -2
View File
@@ -4,8 +4,15 @@ version = "0.1.0"
edition = "2018"
[dependencies]
arrayvec = "0.5"
bitflags = "0.7"
spin = "0.4"
redox_syscall = "0.1"
crossbeam-channel = "0.4"
futures = "0.3"
log = "0.4"
redox-log = { git = "https://gitlab.redox-os.org/redox-os/redox-log.git", tag = "v0.1.0" }
redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" }
partitionlib = { git = "https://gitlab.redox-os.org/redox-os/partitionlib.git" }
smallvec = "1"
block-io-wrapper = { path = "../block-io-wrapper" }
pcid = { path = "../pcid" }
+371 -114
View File
@@ -1,125 +1,382 @@
#![feature(asm)]
extern crate bitflags;
extern crate spin;
extern crate syscall;
use std::{env, usize};
use std::convert::TryInto;
use std::fs::File;
use std::io::{ErrorKind, Read, Write};
use std::os::unix::io::{RawFd, FromRawFd};
use std::os::unix::io::{FromRawFd, RawFd};
use std::ptr::NonNull;
use std::sync::{Arc, Mutex};
use std::{slice, usize};
use syscall::{EVENT_READ, PHYSMAP_NO_CACHE, PHYSMAP_WRITE, Event, Packet, Result, SchemeBlockMut};
use pcid_interface::{PciBar, PciFeature, PciFeatureInfo, PciFunction, PcidServerHandle};
use syscall::{
CloneFlags, Event, Mmio, Packet, Result, SchemeBlockMut, PHYSMAP_NO_CACHE,
PHYSMAP_WRITE,
};
use redox_log::{OutputBuilder, RedoxLogger};
use self::nvme::Nvme;
use self::nvme::{InterruptMethod, InterruptSources, Nvme};
use self::scheme::DiskScheme;
mod nvme;
mod scheme;
fn main() {
let mut args = env::args().skip(1);
let mut name = args.next().expect("nvmed: no name provided");
name.push_str("_nvme");
let bar_str = args.next().expect("nvmed: no address provided");
let bar = usize::from_str_radix(&bar_str, 16).expect("nvmed: failed to parse address");
let bar_size_str = args.next().expect("nvmed: no address size provided");
let bar_size = usize::from_str_radix(&bar_size_str, 16).expect("nvmed: failed to parse address size");
let irq_str = args.next().expect("nvmed: no irq provided");
let irq = irq_str.parse::<u8>().expect("nvmed: failed to parse irq");
print!("{}", format!(" + NVME {} on: {:X} size: {} IRQ: {}\n", name, bar, bar_size, irq));
// Daemonize
if unsafe { syscall::clone(0).unwrap() } == 0 {
let address = unsafe {
syscall::physmap(bar, bar_size, PHYSMAP_WRITE | PHYSMAP_NO_CACHE)
.expect("nvmed: failed to map address")
};
{
let event_fd = syscall::open("event:", syscall::O_RDWR | syscall::O_CLOEXEC)
.expect("nvmed: failed to open event queue");
let mut event_file = unsafe { File::from_raw_fd(event_fd as RawFd) };
let irq_fd = syscall::open(
&format!("irq:{}", irq),
syscall::O_RDWR | syscall::O_NONBLOCK | syscall::O_CLOEXEC
).expect("nvmed: failed to open irq file");
syscall::write(event_fd, &syscall::Event {
id: irq_fd,
flags: syscall::EVENT_READ,
data: 0,
}).expect("nvmed: failed to watch irq file events");
let mut irq_file = unsafe { File::from_raw_fd(irq_fd as RawFd) };
let scheme_name = format!("disk/{}", name);
let socket_fd = syscall::open(
&format!(":{}", scheme_name),
syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK | syscall::O_CLOEXEC
).expect("nvmed: failed to create disk scheme");
syscall::write(event_fd, &syscall::Event {
id: socket_fd,
flags: syscall::EVENT_READ,
data: 1,
}).expect("nvmed: failed to watch disk scheme events");
let mut socket_file = unsafe { File::from_raw_fd(socket_fd as RawFd) };
syscall::setrens(0, 0).expect("nvmed: failed to enter null namespace");
let mut nvme = Nvme::new(address).expect("nvmed: failed to allocate driver data");
let namespaces = unsafe { nvme.init() };
let mut scheme = DiskScheme::new(scheme_name, nvme, namespaces);
let mut todo = Vec::new();
'events: loop {
let mut event = Event::default();
if event_file.read(&mut event).expect("nvmed: failed to read event queue") == 0 {
break;
}
match event.data {
0 => {
let mut irq = [0; 8];
if irq_file.read(&mut irq).expect("nvmed: failed to read irq file") >= irq.len() {
if scheme.irq() {
irq_file.write(&irq).expect("nvmed: failed to write irq file");
}
}
},
1 => loop {
let mut packet = Packet::default();
match socket_file.read(&mut packet) {
Ok(0) => break 'events,
Ok(_) => (),
Err(err) => match err.kind() {
ErrorKind::WouldBlock => break,
_ => Err(err).expect("nvmed: failed to read disk scheme"),
}
}
todo.push(packet);
},
unknown => {
panic!("nvmed: unknown event data {}", unknown);
},
}
let mut i = 0;
while i < todo.len() {
if let Some(a) = scheme.handle(&todo[i]) {
let mut packet = todo.remove(i);
packet.a = a;
socket_file.write(&packet).expect("nvmed: failed to write disk scheme");
} else {
i += 1;
}
}
}
//TODO: destroy NVMe stuff
}
unsafe { let _ = syscall::physunmap(address); }
/// A wrapper for a BAR allocation.
pub struct Bar {
ptr: NonNull<u8>,
physical: usize,
bar_size: usize,
}
impl Bar {
pub fn allocate(bar: usize, bar_size: usize) -> Result<Self> {
Ok(Self {
ptr: NonNull::new(
unsafe { syscall::physmap(bar, bar_size, PHYSMAP_NO_CACHE | PHYSMAP_WRITE)? as *mut u8 },
)
.expect("Mapping a BAR resulted in a nullptr"),
physical: bar,
bar_size,
})
}
}
impl Drop for Bar {
fn drop(&mut self) {
let _ = unsafe { syscall::physunmap(self.physical) };
}
}
/// The PCI BARs that may be allocated.
#[derive(Default)]
pub struct AllocatedBars(pub [Mutex<Option<Bar>>; 6]);
/// Get the most optimal yet functional interrupt mechanism: either (in the order of preference):
/// MSI-X, MSI, and INTx# pin. Returns both runtime interrupt structures (MSI/MSI-X capability
/// structures), and the handles to the interrupts.
fn get_int_method(
pcid_handle: &mut PcidServerHandle,
function: &PciFunction,
allocated_bars: &AllocatedBars,
) -> Result<(InterruptMethod, InterruptSources)> {
log::trace!("Begin get_int_method");
use pcid_interface::irq_helpers;
let features = pcid_handle.fetch_all_features().unwrap();
let has_msi = features.iter().any(|(feature, _)| feature.is_msi());
let has_msix = features.iter().any(|(feature, _)| feature.is_msix());
// TODO: Allocate more than one vector when possible and useful.
if has_msix {
// Extended message signaled interrupts.
use self::nvme::MsixCfg;
use pcid_interface::msi::MsixTableEntry;
let mut capability_struct = match pcid_handle.feature_info(PciFeature::MsiX).unwrap() {
PciFeatureInfo::MsiX(msix) => msix,
_ => unreachable!(),
};
fn bar_base(
allocated_bars: &AllocatedBars,
function: &PciFunction,
bir: u8,
) -> Result<NonNull<u8>> {
let bir = usize::from(bir);
let mut bar_guard = allocated_bars.0[bir].lock().unwrap();
match &mut *bar_guard {
&mut Some(ref bar) => Ok(bar.ptr),
bar_to_set @ &mut None => {
let bar = match function.bars[bir] {
PciBar::Memory(addr) => addr,
other => panic!("Expected memory BAR, found {:?}", other),
};
let bar_size = function.bar_sizes[bir];
let bar = Bar::allocate(bar as usize, bar_size as usize)?;
*bar_to_set = Some(bar);
Ok(bar_to_set.as_ref().unwrap().ptr)
}
}
}
let table_bar_base: *mut u8 =
bar_base(allocated_bars, function, capability_struct.table_bir())?.as_ptr();
let pba_bar_base: *mut u8 =
bar_base(allocated_bars, function, capability_struct.pba_bir())?.as_ptr();
let table_base =
unsafe { table_bar_base.offset(capability_struct.table_offset() as isize) };
let pba_base = unsafe { pba_bar_base.offset(capability_struct.pba_offset() as isize) };
let vector_count = capability_struct.table_size();
let table_entries: &'static mut [MsixTableEntry] = unsafe {
slice::from_raw_parts_mut(table_base as *mut MsixTableEntry, vector_count as usize)
};
let pba_entries: &'static mut [Mmio<u64>] = unsafe {
slice::from_raw_parts_mut(
table_base as *mut Mmio<u64>,
(vector_count as usize + 63) / 64,
)
};
// Mask all interrupts in case some earlier driver/os already unmasked them (according to
// the PCI Local Bus spec 3.0, they are masked after system reset).
for table_entry in table_entries.iter_mut() {
table_entry.mask();
}
pcid_handle.enable_feature(PciFeature::MsiX).unwrap();
capability_struct.set_msix_enabled(true); // only affects our local mirror of the cap
let (msix_vector_number, irq_handle) = {
use msi_x86_64::DeliveryMode;
use pcid_interface::msi::x86_64 as msi_x86_64;
let entry: &mut MsixTableEntry = &mut table_entries[0];
let bsp_cpu_id =
irq_helpers::read_bsp_apic_id().expect("nvmed: failed to read APIC ID");
let bsp_lapic_id = bsp_cpu_id
.try_into()
.expect("nvmed: BSP local apic ID couldn't fit inside u8");
let (vector, irq_handle) = irq_helpers::allocate_single_interrupt_vector(bsp_cpu_id)
.expect("nvmed: failed to allocate single MSI-X interrupt vector")
.expect("nvmed: no interrupt vectors left on BSP");
let msg_addr = msi_x86_64::message_address(bsp_lapic_id, false, false);
let msg_data = msi_x86_64::message_data_edge_triggered(DeliveryMode::Fixed, vector);
entry.set_addr_lo(msg_addr);
entry.set_msg_data(msg_data);
(0, irq_handle)
};
let interrupt_method = InterruptMethod::MsiX(MsixCfg {
cap: capability_struct,
table: table_entries,
pba: pba_entries,
});
let interrupt_sources =
InterruptSources::MsiX(std::iter::once((msix_vector_number, irq_handle)).collect());
Ok((interrupt_method, interrupt_sources))
} else if has_msi {
// Message signaled interrupts.
let capability_struct = match pcid_handle.feature_info(PciFeature::Msi).unwrap() {
PciFeatureInfo::Msi(msi) => msi,
_ => unreachable!(),
};
let (msi_vector_number, irq_handle) = {
use msi_x86_64::DeliveryMode;
use pcid_interface::msi::x86_64 as msi_x86_64;
use pcid_interface::{MsiSetFeatureInfo, SetFeatureInfo};
let bsp_cpu_id =
irq_helpers::read_bsp_apic_id().expect("nvmed: failed to read BSP APIC ID");
let bsp_lapic_id = bsp_cpu_id
.try_into()
.expect("nvmed: BSP local apic ID couldn't fit inside u8");
let (vector, irq_handle) = irq_helpers::allocate_single_interrupt_vector(bsp_cpu_id)
.expect("nvmed: failed to allocate single MSI interrupt vector")
.expect("nvmed: no interrupt vectors left on BSP");
let msg_addr = msi_x86_64::message_address(bsp_lapic_id, false, false);
let msg_data =
msi_x86_64::message_data_edge_triggered(DeliveryMode::Fixed, vector) as u16;
pcid_handle.set_feature_info(SetFeatureInfo::Msi(MsiSetFeatureInfo {
message_address: Some(msg_addr),
message_upper_address: Some(0),
message_data: Some(msg_data),
multi_message_enable: Some(0), // enable 2^0=1 vectors
mask_bits: None,
})).unwrap();
(0, irq_handle)
};
let interrupt_method = InterruptMethod::Msi(capability_struct);
let interrupt_sources =
InterruptSources::Msi(std::iter::once((msi_vector_number, irq_handle)).collect());
pcid_handle.enable_feature(PciFeature::Msi).unwrap();
Ok((interrupt_method, interrupt_sources))
} else if function.legacy_interrupt_pin.is_some() {
// INTx# pin based interrupts.
let irq_handle = File::open(format!("irq:{}", function.legacy_interrupt_line))
.expect("nvmed: failed to open INTx# interrupt line");
Ok((InterruptMethod::Intx, InterruptSources::Intx(irq_handle)))
} else {
// No interrupts at all
todo!("handling of no interrupts")
}
}
fn setup_logging() -> Option<&'static RedoxLogger> {
let mut logger = RedoxLogger::new()
.with_output(
OutputBuilder::stderr()
.with_filter(log::LevelFilter::Info) // limit global output to important info
.with_ansi_escape_codes()
.flush_on_newline(true)
.build()
);
#[cfg(target_os = "redox")]
match OutputBuilder::in_redox_logging_scheme("disk", "pcie", "nvme.log") {
Ok(b) => logger = logger.with_output(
// TODO: Add a configuration file for this
b.with_filter(log::LevelFilter::Info)
.flush_on_newline(true)
.build()
),
Err(error) => eprintln!("nvmed: failed to create nvme.log: {}", error),
}
#[cfg(target_os = "redox")]
match OutputBuilder::in_redox_logging_scheme("disk", "pcie", "nvme.ansi.log") {
Ok(b) => logger = logger.with_output(
b.with_filter(log::LevelFilter::Info)
.with_ansi_escape_codes()
.flush_on_newline(true)
.build()
),
Err(error) => eprintln!("nvmed: failed to create nvme.ansi.log: {}", error),
}
match logger.enable() {
Ok(logger_ref) => {
eprintln!("nvmed: enabled logger");
Some(logger_ref)
}
Err(error) => {
eprintln!("nvmed: failed to set default logger: {}", error);
None
}
}
}
fn main() {
// Daemonize
if unsafe { syscall::clone(CloneFlags::empty()).unwrap() } != 0 {
return;
}
let _logger_ref = setup_logging();
let mut pcid_handle =
PcidServerHandle::connect_default().expect("nvmed: failed to setup channel to pcid");
let pci_config = pcid_handle
.fetch_config()
.expect("nvmed: failed to fetch config");
let bar = match pci_config.func.bars[0] {
PciBar::Memory(mem) => mem,
other => panic!("received a non-memory BAR ({:?})", other),
};
let bar_size = pci_config.func.bar_sizes[0];
let irq = pci_config.func.legacy_interrupt_line;
let mut name = pci_config.func.name();
name.push_str("_nvme");
log::info!("NVME PCI CONFIG: {:?}", pci_config);
let allocated_bars = AllocatedBars::default();
let address = unsafe {
syscall::physmap(
bar as usize,
bar_size as usize,
PHYSMAP_WRITE | PHYSMAP_NO_CACHE,
)
.expect("nvmed: failed to map address")
};
*allocated_bars.0[0].lock().unwrap() = Some(Bar {
physical: bar as usize,
bar_size: bar_size as usize,
ptr: NonNull::new(address as *mut u8).expect("Physmapping BAR gave nullptr"),
});
let event_fd = syscall::open("event:", syscall::O_RDWR | syscall::O_CLOEXEC)
.expect("nvmed: failed to open event queue");
let mut event_file = unsafe { File::from_raw_fd(event_fd as RawFd) };
let scheme_name = format!("disk/{}", name);
let socket_fd = syscall::open(
&format!(":{}", scheme_name),
syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK | syscall::O_CLOEXEC,
)
.expect("nvmed: failed to create disk scheme");
syscall::write(
event_fd,
&syscall::Event {
id: socket_fd,
flags: syscall::EVENT_READ,
data: 0,
},
)
.expect("nvmed: failed to watch disk scheme events");
let mut socket_file = unsafe { File::from_raw_fd(socket_fd as RawFd) };
let (reactor_sender, reactor_receiver) = crossbeam_channel::unbounded();
let (interrupt_method, interrupt_sources) =
get_int_method(&mut pcid_handle, &pci_config.func, &allocated_bars)
.expect("nvmed: failed to find a suitable interrupt method");
let mut nvme = Nvme::new(address, interrupt_method, pcid_handle, reactor_sender)
.expect("nvmed: failed to allocate driver data");
unsafe { nvme.init() }
log::debug!("Finished base initialization");
let nvme = Arc::new(nvme);
let reactor_thread = nvme::cq_reactor::start_cq_reactor_thread(Arc::clone(&nvme), interrupt_sources, reactor_receiver);
let namespaces = futures::executor::block_on(nvme.init_with_queues());
syscall::setrens(0, 0).expect("nvmed: failed to enter null namespace");
let mut scheme = DiskScheme::new(scheme_name, nvme, namespaces);
let mut todo = Vec::new();
'events: loop {
let mut event = Event::default();
if event_file
.read(&mut event)
.expect("nvmed: failed to read event queue")
== 0
{
break;
}
match event.data {
0 => loop {
let mut packet = Packet::default();
match socket_file.read(&mut packet) {
Ok(0) => break 'events,
Ok(_) => (),
Err(err) => match err.kind() {
ErrorKind::WouldBlock => break,
_ => Err(err).expect("nvmed: failed to read disk scheme"),
},
}
todo.push(packet);
},
unknown => {
panic!("nvmed: unknown event data {}", unknown);
}
}
let mut i = 0;
while i < todo.len() {
if let Some(a) = scheme.handle(&todo[i]) {
let mut packet = todo.remove(i);
packet.a = a;
socket_file
.write(&packet)
.expect("nvmed: failed to write disk scheme");
} else {
i += 1;
}
}
}
//TODO: destroy NVMe stuff
reactor_thread.join().expect("nvmed: failed to join reactor thread");
}
-655
View File
@@ -1,655 +0,0 @@
use std::{ptr, thread};
use std::collections::BTreeMap;
use syscall::io::{Dma, Io, Mmio};
use syscall::error::{Error, Result, EINVAL};
#[derive(Clone, Copy)]
#[repr(packed)]
pub struct NvmeCmd {
/// Opcode
opcode: u8,
/// Flags
flags: u8,
/// Command ID
cid: u16,
/// Namespace identifier
nsid: u32,
/// Reserved
_rsvd: u64,
/// Metadata pointer
mptr: u64,
/// Data pointer
dptr: [u64; 2],
/// Command dword 10
cdw10: u32,
/// Command dword 11
cdw11: u32,
/// Command dword 12
cdw12: u32,
/// Command dword 13
cdw13: u32,
/// Command dword 14
cdw14: u32,
/// Command dword 15
cdw15: u32,
}
impl NvmeCmd {
pub fn create_io_completion_queue(cid: u16, qid: u16, ptr: usize, size: u16) -> Self {
Self {
opcode: 5,
flags: 0,
cid: cid,
nsid: 0,
_rsvd: 0,
mptr: 0,
dptr: [ptr as u64, 0],
cdw10: ((size as u32) << 16) | (qid as u32),
cdw11: 1 /* Physically Contiguous */, //TODO: IV, IEN
cdw12: 0,
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
pub fn create_io_submission_queue(cid: u16, qid: u16, ptr: usize, size: u16, cqid: u16) -> Self {
Self {
opcode: 1,
flags: 0,
cid: cid,
nsid: 0,
_rsvd: 0,
mptr: 0,
dptr: [ptr as u64, 0],
cdw10: ((size as u32) << 16) | (qid as u32),
cdw11: ((cqid as u32) << 16) | 1 /* Physically Contiguous */, //TODO: QPRIO
cdw12: 0, //TODO: NVMSETID
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
pub fn identify_namespace(cid: u16, ptr: usize, nsid: u32) -> Self {
Self {
opcode: 6,
flags: 0,
cid: cid,
nsid: nsid,
_rsvd: 0,
mptr: 0,
dptr: [ptr as u64, 0],
cdw10: 0,
cdw11: 0,
cdw12: 0,
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
pub fn identify_controller(cid: u16, ptr: usize) -> Self {
Self {
opcode: 6,
flags: 0,
cid: cid,
nsid: 0,
_rsvd: 0,
mptr: 0,
dptr: [ptr as u64, 0],
cdw10: 1,
cdw11: 0,
cdw12: 0,
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
pub fn identify_namespace_list(cid: u16, ptr: usize, base: u32) -> Self {
Self {
opcode: 6,
flags: 0,
cid: cid,
nsid: base,
_rsvd: 0,
mptr: 0,
dptr: [ptr as u64, 0],
cdw10: 2,
cdw11: 0,
cdw12: 0,
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
pub fn io_read(cid: u16, nsid: u32, lba: u64, blocks_1: u16, ptr0: u64, ptr1: u64) -> Self {
Self {
opcode: 2,
flags: 1 << 6,
cid: cid,
nsid: nsid,
_rsvd: 0,
mptr: 0,
dptr: [ptr0, ptr1],
cdw10: lba as u32,
cdw11: (lba >> 32) as u32,
cdw12: blocks_1 as u32,
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
pub fn io_write(cid: u16, nsid: u32, lba: u64, blocks_1: u16, ptr0: u64, ptr1: u64) -> Self {
Self {
opcode: 1,
flags: 1 << 6,
cid: cid,
nsid: nsid,
_rsvd: 0,
mptr: 0,
dptr: [ptr0, ptr1],
cdw10: lba as u32,
cdw11: (lba >> 32) as u32,
cdw12: blocks_1 as u32,
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
}
#[derive(Clone, Copy, Debug)]
#[repr(packed)]
pub struct NvmeComp {
command_specific: u32,
_rsvd: u32,
sq_head: u16,
sq_id: u16,
cid: u16,
status: u16,
}
#[repr(packed)]
pub struct NvmeRegs {
/// Controller Capabilities
cap: Mmio<u64>,
/// Version
vs: Mmio<u32>,
/// Interrupt mask set
intms: Mmio<u32>,
/// Interrupt mask clear
intmc: Mmio<u32>,
/// Controller configuration
cc: Mmio<u32>,
/// Reserved
_rsvd: Mmio<u32>,
/// Controller status
csts: Mmio<u32>,
/// NVM subsystem reset
nssr: Mmio<u32>,
/// Admin queue attributes
aqa: Mmio<u32>,
/// Admin submission queue base address
asq: Mmio<u64>,
/// Admin completion queue base address
acq: Mmio<u64>,
/// Controller memory buffer location
cmbloc: Mmio<u32>,
/// Controller memory buffer size
cmbsz: Mmio<u32>,
}
pub struct NvmeCmdQueue {
data: Dma<[NvmeCmd; 64]>,
i: usize,
}
impl NvmeCmdQueue {
fn new() -> Result<Self> {
Ok(Self {
data: Dma::zeroed()?,
i: 0,
})
}
fn submit(&mut self, entry: NvmeCmd) -> usize {
self.data[self.i] = entry;
self.i = (self.i + 1) % self.data.len();
self.i
}
}
pub struct NvmeCompQueue {
data: Dma<[NvmeComp; 256]>,
i: usize,
phase: bool,
}
impl NvmeCompQueue {
fn new() -> Result<Self> {
Ok(Self {
data: Dma::zeroed()?,
i: 0,
phase: true,
})
}
pub (crate) fn complete(&mut self) -> Option<(usize, NvmeComp)> {
let entry = unsafe {
ptr::read_volatile(self.data.as_ptr().add(self.i))
};
// println!("{:?}", entry);
if ((entry.status & 1) == 1) == self.phase {
self.i = (self.i + 1) % self.data.len();
if self.i == 0 {
self.phase = ! self.phase;
}
Some((self.i, entry))
} else {
None
}
}
fn complete_spin(&mut self) -> (usize, NvmeComp) {
loop {
if let Some(some) = self.complete() {
return some;
} else {
unsafe { asm!("pause"); }
}
}
}
}
pub struct NvmeNamespace {
pub id: u32,
pub blocks: u64,
pub block_size: u64,
}
pub struct Nvme {
regs: &'static mut NvmeRegs,
submission_queues: [NvmeCmdQueue; 2],
pub (crate) completion_queues: [NvmeCompQueue; 2],
buffer: Dma<[u8; 512 * 4096]>, // 2MB of buffer
buffer_prp: Dma<[u64; 512]>, // 4KB of PRP for the buffer
}
impl Nvme {
pub fn new(address: usize) -> Result<Self> {
Ok(Nvme {
regs: unsafe { &mut *(address as *mut NvmeRegs) },
submission_queues: [NvmeCmdQueue::new()?, NvmeCmdQueue::new()?],
completion_queues: [NvmeCompQueue::new()?, NvmeCompQueue::new()?],
buffer: Dma::zeroed()?,
buffer_prp: Dma::zeroed()?,
})
}
unsafe fn doorbell(&mut self, index: usize) -> &'static mut Mmio<u32> {
let dstrd = ((self.regs.cap.read() >> 32) & 0b1111) as usize;
let addr = (self.regs as *mut _ as usize)
+ 0x1000
+ index * (4 << dstrd);
&mut *(addr as *mut Mmio<u32>)
}
pub unsafe fn submission_queue_tail(&mut self, qid: u16, tail: u16) {
self.doorbell(2 * (qid as usize)).write(tail as u32);
}
pub unsafe fn completion_queue_head(&mut self, qid: u16, head: u16) {
self.doorbell(2 * (qid as usize) + 1).write(head as u32)
}
pub unsafe fn init(&mut self) -> BTreeMap<u32, NvmeNamespace> {
for i in 0..self.buffer_prp.len() {
self.buffer_prp[i] = (self.buffer.physical() + i * 4096) as u64;
}
// println!(" - CAPS: {:X}", self.regs.cap.read());
// println!(" - VS: {:X}", self.regs.vs.read());
// println!(" - CC: {:X}", self.regs.cc.read());
// println!(" - CSTS: {:X}", self.regs.csts.read());
// println!(" - Disable");
self.regs.cc.writef(1, false);
// println!(" - Waiting for not ready");
loop {
let csts = self.regs.csts.read();
// println!("CSTS: {:X}", csts);
if csts & 1 == 1 {
asm!("pause");
} else {
break;
}
}
// println!(" - Mask all interrupts");
self.regs.intms.write(0xFFFFFFFF);
for (qid, queue) in self.completion_queues.iter().enumerate() {
let data = &queue.data;
// println!(" - completion queue {}: {:X}, {}", qid, data.physical(), data.len());
}
for (qid, queue) in self.submission_queues.iter().enumerate() {
let data = &queue.data;
// println!(" - submission queue {}: {:X}, {}", qid, data.physical(), data.len());
}
{
let asq = &self.submission_queues[0];
let acq = &self.completion_queues[0];
self.regs.aqa.write(((acq.data.len() as u32 - 1) << 16) | (asq.data.len() as u32 - 1));
self.regs.asq.write(asq.data.physical() as u64);
self.regs.acq.write(acq.data.physical() as u64);
// Set IOCQES, IOSQES, AMS, MPS, and CSS
let mut cc = self.regs.cc.read();
cc &= 0xFF00000F;
cc |= (4 << 20) | (6 << 16);
self.regs.cc.write(cc);
}
// println!(" - Enable");
self.regs.cc.writef(1, true);
// println!(" - Waiting for ready");
loop {
let csts = self.regs.csts.read();
// println!("CSTS: {:X}", csts);
if csts & 1 == 0 {
asm!("pause");
} else {
break;
}
}
{
//TODO: Use buffer
let data: Dma<[u8; 4096]> = Dma::zeroed().unwrap();
// println!(" - Attempting to identify controller");
{
let qid = 0;
let queue = &mut self.submission_queues[qid];
let cid = queue.i as u16;
let entry = NvmeCmd::identify_controller(
cid, data.physical()
);
let tail = queue.submit(entry);
self.submission_queue_tail(qid as u16, tail as u16);
}
// println!(" - Waiting to identify controller");
{
let qid = 0;
let queue = &mut self.completion_queues[qid];
let (head, entry) = queue.complete_spin();
self.completion_queue_head(qid as u16, head as u16);
}
// println!(" - Dumping identify controller");
let mut serial = String::new();
for &b in &data[4..24] {
if b == 0 {
break;
}
serial.push(b as char);
}
let mut model = String::new();
for &b in &data[24..64] {
if b == 0 {
break;
}
model.push(b as char);
}
let mut firmware = String::new();
for &b in &data[64..72] {
if b == 0 {
break;
}
firmware.push(b as char);
}
println!(
" - Model: {} Serial: {} Firmware: {}",
model.trim(),
serial.trim(),
firmware.trim()
);
}
let mut nsids = Vec::new();
{
//TODO: Use buffer
let data: Dma<[u32; 1024]> = Dma::zeroed().unwrap();
// println!(" - Attempting to retrieve namespace ID list");
{
let qid = 0;
let queue = &mut self.submission_queues[qid];
let cid = queue.i as u16;
let entry = NvmeCmd::identify_namespace_list(
cid, data.physical(), 0
);
let tail = queue.submit(entry);
self.submission_queue_tail(qid as u16, tail as u16);
}
// println!(" - Waiting to retrieve namespace ID list");
{
let qid = 0;
let queue = &mut self.completion_queues[qid];
let (head, entry) = queue.complete_spin();
self.completion_queue_head(qid as u16, head as u16);
}
// println!(" - Dumping namespace ID list");
for &nsid in data.iter() {
if nsid != 0 {
nsids.push(nsid);
}
}
}
let mut namespaces = BTreeMap::new();
for &nsid in nsids.iter() {
//TODO: Use buffer
let data: Dma<[u8; 4096]> = Dma::zeroed().unwrap();
// println!(" - Attempting to identify namespace {}", nsid);
{
let qid = 0;
let queue = &mut self.submission_queues[qid];
let cid = queue.i as u16;
let entry = NvmeCmd::identify_namespace(
cid, data.physical(), nsid
);
let tail = queue.submit(entry);
self.submission_queue_tail(qid as u16, tail as u16);
}
// println!(" - Waiting to identify namespace {}", nsid);
{
let qid = 0;
let queue = &mut self.completion_queues[qid];
let (head, entry) = queue.complete_spin();
self.completion_queue_head(qid as u16, head as u16);
}
// println!(" - Dumping identify namespace");
let size = *(data.as_ptr().offset(0) as *const u64);
let capacity = *(data.as_ptr().offset(8) as *const u64);
println!(
" - ID: {} Size: {} Capacity: {}",
nsid,
size,
capacity
);
//TODO: Read block size
namespaces.insert(nsid, NvmeNamespace {
id: nsid,
blocks: size,
block_size: 512, // TODO
});
}
for io_qid in 1..self.completion_queues.len() {
let (ptr, len) = {
let queue = &self.completion_queues[io_qid];
(queue.data.physical(), queue.data.len())
};
// println!(" - Attempting to create I/O completion queue {}", io_qid);
{
let qid = 0;
let queue = &mut self.submission_queues[qid];
let cid = queue.i as u16;
let entry = NvmeCmd::create_io_completion_queue(
cid, io_qid as u16, ptr, (len - 1) as u16
);
let tail = queue.submit(entry);
self.submission_queue_tail(qid as u16, tail as u16);
}
// println!(" - Waiting to create I/O completion queue {}", io_qid);
{
let qid = 0;
let queue = &mut self.completion_queues[qid];
let (head, entry) = queue.complete_spin();
self.completion_queue_head(qid as u16, head as u16);
}
}
for io_qid in 1..self.submission_queues.len() {
let (ptr, len) = {
let queue = &self.submission_queues[io_qid];
(queue.data.physical(), queue.data.len())
};
// println!(" - Attempting to create I/O submission queue {}", io_qid);
{
let qid = 0;
let queue = &mut self.submission_queues[qid];
let cid = queue.i as u16;
//TODO: Get completion queue ID through smarter mechanism
let entry = NvmeCmd::create_io_submission_queue(
cid, io_qid as u16, ptr, (len - 1) as u16, io_qid as u16
);
let tail = queue.submit(entry);
self.submission_queue_tail(qid as u16, tail as u16);
}
// println!(" - Waiting to create I/O submission queue {}", io_qid);
{
let qid = 0;
let queue = &mut self.completion_queues[qid];
let (head, entry) = queue.complete_spin();
self.completion_queue_head(qid as u16, head as u16);
}
}
// println!(" - Complete");
namespaces
}
unsafe fn namespace_rw(&mut self, nsid: u32, lba: u64, blocks_1: u16, write: bool) -> Result<()> {
//TODO: Get real block size
let block_size = 512;
let bytes = ((blocks_1 as u64) + 1) * block_size;
let (ptr0, ptr1) = if bytes <= 4096 {
(self.buffer_prp[0], 0)
} else if bytes <= 8192 {
(self.buffer_prp[0], self.buffer_prp[1])
} else {
(self.buffer_prp[0], (self.buffer_prp.physical() + 8) as u64)
};
{
let qid = 1;
let queue = &mut self.submission_queues[qid];
let cid = queue.i as u16;
let entry = if write {
NvmeCmd::io_write(
cid, nsid, lba, blocks_1, ptr0, ptr1
)
} else {
NvmeCmd::io_read(
cid, nsid, lba, blocks_1, ptr0, ptr1
)
};
let tail = queue.submit(entry);
self.submission_queue_tail(qid as u16, tail as u16);
}
{
let qid = 1;
let queue = &mut self.completion_queues[qid];
let (head, entry) = queue.complete_spin();
//TODO: Handle errors
self.completion_queue_head(qid as u16, head as u16);
}
Ok(())
}
pub unsafe fn namespace_read(&mut self, nsid: u32, mut lba: u64, buf: &mut [u8]) -> Result<Option<usize>> {
//TODO: Use interrupts
//TODO: Get real block size
let block_size = 512;
for chunk in buf.chunks_mut(self.buffer.len()) {
let blocks = (chunk.len() + block_size - 1) / block_size;
assert!(blocks > 0);
assert!(blocks <= 0x1_0000);
self.namespace_rw(nsid, lba, (blocks - 1) as u16, false)?;
chunk.copy_from_slice(&self.buffer[..chunk.len()]);
lba += blocks as u64;
}
Ok(Some(buf.len()))
}
pub unsafe fn namespace_write(&mut self, nsid: u32, mut lba: u64, buf: &[u8]) -> Result<Option<usize>> {
//TODO: Use interrupts
//TODO: Get real block size
let block_size = 512;
for chunk in buf.chunks(self.buffer.len()) {
let blocks = (chunk.len() + block_size - 1) / block_size;
assert!(blocks > 0);
assert!(blocks <= 0x1_0000);
self.buffer[..chunk.len()].copy_from_slice(chunk);
self.namespace_rw(nsid, lba, (blocks - 1) as u16, true)?;
lba += blocks as u64;
}
Ok(Some(buf.len()))
}
}
+162
View File
@@ -0,0 +1,162 @@
use super::NvmeCmd;
impl NvmeCmd {
pub fn create_io_completion_queue(
cid: u16,
qid: u16,
ptr: usize,
size: u16,
iv: Option<u16>,
) -> Self {
const DW11_PHYSICALLY_CONTIGUOUS_BIT: u32 = 0x0000_0001;
const DW11_ENABLE_INTERRUPTS_BIT: u32 = 0x0000_0002;
const DW11_INTERRUPT_VECTOR_SHIFT: u8 = 16;
Self {
opcode: 5,
flags: 0,
cid,
nsid: 0,
_rsvd: 0,
mptr: 0,
dptr: [ptr as u64, 0],
cdw10: ((size as u32) << 16) | (qid as u32),
cdw11: DW11_PHYSICALLY_CONTIGUOUS_BIT
| if let Some(iv) = iv {
// enable interrupts if a vector is present
DW11_ENABLE_INTERRUPTS_BIT | (u32::from(iv) << DW11_INTERRUPT_VECTOR_SHIFT)
} else {
0
},
cdw12: 0,
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
pub fn create_io_submission_queue(
cid: u16,
qid: u16,
ptr: usize,
size: u16,
cqid: u16,
) -> Self {
Self {
opcode: 1,
flags: 0,
cid,
nsid: 0,
_rsvd: 0,
mptr: 0,
dptr: [ptr as u64, 0],
cdw10: ((size as u32) << 16) | (qid as u32),
cdw11: ((cqid as u32) << 16) | 1, /* Physically Contiguous */
//TODO: QPRIO
cdw12: 0, //TODO: NVMSETID
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
pub fn identify_namespace(cid: u16, ptr: usize, nsid: u32) -> Self {
Self {
opcode: 6,
flags: 0,
cid,
nsid,
_rsvd: 0,
mptr: 0,
dptr: [ptr as u64, 0],
cdw10: 0,
cdw11: 0,
cdw12: 0,
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
pub fn identify_controller(cid: u16, ptr: usize) -> Self {
Self {
opcode: 6,
flags: 0,
cid,
nsid: 0,
_rsvd: 0,
mptr: 0,
dptr: [ptr as u64, 0],
cdw10: 1,
cdw11: 0,
cdw12: 0,
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
pub fn identify_namespace_list(cid: u16, ptr: usize, base: u32) -> Self {
Self {
opcode: 6,
flags: 0,
cid,
nsid: base,
_rsvd: 0,
mptr: 0,
dptr: [ptr as u64, 0],
cdw10: 2,
cdw11: 0,
cdw12: 0,
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
pub fn get_features(cid: u16, ptr: usize, fid: u8) -> Self {
Self {
opcode: 0xA,
dptr: [ptr as u64, 0],
cdw10: u32::from(fid), // TODO: SEL
..Default::default()
}
}
pub fn io_read(cid: u16, nsid: u32, lba: u64, blocks_1: u16, ptr0: u64, ptr1: u64) -> Self {
Self {
opcode: 2,
flags: 1 << 6,
cid,
nsid,
_rsvd: 0,
mptr: 0,
dptr: [ptr0, ptr1],
cdw10: lba as u32,
cdw11: (lba >> 32) as u32,
cdw12: blocks_1 as u32,
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
pub fn io_write(cid: u16, nsid: u32, lba: u64, blocks_1: u16, ptr0: u64, ptr1: u64) -> Self {
Self {
opcode: 1,
flags: 1 << 6,
cid,
nsid,
_rsvd: 0,
mptr: 0,
dptr: [ptr0, ptr1],
cdw10: lba as u32,
cdw11: (lba >> 32) as u32,
cdw12: blocks_1 as u32,
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
}
+386
View File
@@ -0,0 +1,386 @@
//! The Completion Queue Reactor. Functions like any other async/await reactor, but is driven by
//! IRQs triggering wakeups in order to poll NVME completion queues (see `CompletionFuture`).
//!
//! While the reactor is primarily intended to wait for IRQs and then poll completion queues, it
//! can also be used for notifying when a full submission queue can submit a new command (see
//! `AvailableSqEntryFuture`).
use std::convert::TryFrom;
use std::fs::File;
use std::future::Future;
use std::io::prelude::*;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::{mem, task, thread};
use syscall::data::Event;
use syscall::Result;
use crossbeam_channel::Receiver;
use super::{CmdId, CqId, InterruptSources, Nvme, NvmeComp, NvmeCmd, SqId};
/// A notification request, sent by the future in order to tell the completion thread that the
/// current task wants a notification when a matching completion queue entry has been seen.
#[derive(Debug)]
pub enum NotifReq {
RequestCompletion {
cq_id: CqId,
sq_id: SqId,
cmd_id: CmdId,
waker: task::Waker,
// TODO: Get rid of this allocation, or maybe a thread-local vec for reusing.
// TODO: Maybe the `remem` crate.
message: Arc<Mutex<Option<CompletionMessage>>>,
},
RequestAvailSubmission {
sq_id: SqId,
waker: task::Waker,
}
}
enum PendingReq {
PendingCompletion {
waker: task::Waker,
message: Arc<Mutex<Option<CompletionMessage>>>,
cq_id: CqId,
sq_id: SqId,
cmd_id: CmdId,
},
PendingAvailSubmission {
waker: task::Waker,
sq_id: SqId,
},
}
struct CqReactor {
int_sources: InterruptSources,
nvme: Arc<Nvme>,
pending_reqs: Vec<PendingReq>,
// used to store commands that may be completed before a completion is requested
receiver: Receiver<NotifReq>,
event_queue: File,
}
impl CqReactor {
fn create_event_queue(int_sources: &mut InterruptSources) -> Result<File> {
use syscall::flag::*;
let fd = syscall::open("event:", O_CLOEXEC | O_RDWR)?;
let mut file = unsafe { File::from_raw_fd(fd as RawFd) };
for (num, irq_handle) in int_sources.iter_mut() {
if file
.write(&Event {
id: irq_handle.as_raw_fd() as usize,
flags: syscall::EVENT_READ,
data: num as usize,
})
.unwrap()
== 0
{
panic!("Failed to setup event queue for {} {:?}", num, irq_handle);
}
}
Ok(file)
}
fn new(
nvme: Arc<Nvme>,
mut int_sources: InterruptSources,
receiver: Receiver<NotifReq>,
) -> Result<Self> {
Ok(Self {
event_queue: Self::create_event_queue(&mut int_sources)?,
int_sources,
nvme,
pending_reqs: Vec::new(),
receiver,
})
}
fn handle_notif_reqs_raw(pending_reqs: &mut Vec<PendingReq>, receiver: &Receiver<NotifReq>, block_until_first: bool) {
let mut blocking_iter;
let mut nonblocking_iter;
let iter: &mut dyn Iterator<Item = NotifReq> = if block_until_first {
blocking_iter = std::iter::once(receiver.recv().unwrap()).chain(receiver.try_iter());
&mut blocking_iter
} else {
nonblocking_iter = receiver.try_iter();
&mut nonblocking_iter
};
for req in iter {
log::trace!("Got notif req: {:?}", req);
match req {
NotifReq::RequestCompletion {
sq_id,
cq_id,
cmd_id,
waker,
message,
} => pending_reqs.push(PendingReq::PendingCompletion {
sq_id,
cq_id,
cmd_id,
message,
waker,
}),
NotifReq::RequestAvailSubmission { sq_id, waker } => pending_reqs.push(PendingReq::PendingAvailSubmission { sq_id, waker, }),
}
}
}
fn poll_completion_queues(&mut self, iv: u16) -> Option<()> {
let ivs_read_guard = self.nvme.cqs_for_ivs.read().unwrap();
let cqs_read_guard = self.nvme.completion_queues.read().unwrap();
let mut entry_count = 0;
let cq_ids = ivs_read_guard.get(&iv)?;
for cq_id in cq_ids.iter().copied() {
let mut completion_queue_guard = cqs_read_guard.get(&cq_id)?.lock().unwrap();
let &mut (ref mut completion_queue, _) = &mut *completion_queue_guard;
while let Some((head, entry)) = completion_queue.complete() {
unsafe { self.nvme.completion_queue_head(cq_id, head) };
log::trace!("Got completion queue entry (CQID {}): {:?} at {}", cq_id, entry, head);
{
let submission_queues_read_lock = self.nvme.submission_queues.read().unwrap();
// this lock is actually important, since it will block during submission from other
// threads. the lock won't be held for long by the submitters, but it still prevents
// the entry being lost before this reactor is actually able to respond:
let &(ref sq_lock, corresponding_cq_id) = submission_queues_read_lock.get(&{entry.sq_id}).expect("nvmed: internal error: queue returned from controller doesn't exist");
assert_eq!(cq_id, corresponding_cq_id);
let mut sq_guard = sq_lock.lock().unwrap();
sq_guard.head = entry.sq_head;
// the channel still has to be polled twice though:
Self::handle_notif_reqs_raw(&mut self.pending_reqs, &self.receiver, false);
}
Self::try_notify_futures(&mut self.pending_reqs, cq_id, &entry);
entry_count += 1;
}
}
if entry_count == 0 {}
Some(())
}
fn finish_pending_completion(pending_reqs: &mut Vec<PendingReq>, req_cq_id: CqId, cq_id: CqId, sq_id: SqId, cmd_id: CmdId, entry: &NvmeComp, i: usize) -> bool {
if req_cq_id == cq_id
&& sq_id == entry.sq_id
&& cmd_id == entry.cid
{
let (waker, message) = match pending_reqs.remove(i) {
PendingReq::PendingCompletion { waker, message, .. } => (waker, message),
_ => unreachable!(),
};
*message.lock().unwrap() = Some(CompletionMessage { cq_entry: *entry });
waker.wake();
true
} else {
false
}
}
fn finish_pending_avail_submission(pending_reqs: &mut Vec<PendingReq>, sq_id: SqId, entry: &NvmeComp, i: usize) -> bool {
if sq_id == entry.sq_id {
let waker = match pending_reqs.remove(i) {
PendingReq::PendingAvailSubmission { waker, .. } => waker,
_ => unreachable!(),
};
waker.wake();
true
} else {
false
}
}
fn try_notify_futures(pending_reqs: &mut Vec<PendingReq>, cq_id: CqId, entry: &NvmeComp) -> Option<()> {
let mut i = 0usize;
let mut futures_notified = 0;
while i < pending_reqs.len() {
match &pending_reqs[i] {
&PendingReq::PendingCompletion { cq_id: req_cq_id, sq_id, cmd_id, .. } => if Self::finish_pending_completion(pending_reqs, req_cq_id, cq_id, sq_id, cmd_id, entry, i) {
futures_notified += 1;
} else {
i += 1;
}
&PendingReq::PendingAvailSubmission { sq_id, .. } => if Self::finish_pending_avail_submission(pending_reqs, sq_id, entry, i) {
futures_notified += 1;
} else {
i += 1;
}
}
}
if futures_notified == 0 {}
Some(())
}
fn run(mut self) {
log::debug!("Running CQ reactor");
let mut event = Event::default();
let mut irq_word = [0u8; 8]; // stores the IRQ count
const WORD_SIZE: usize = mem::size_of::<usize>();
loop {
let block_until_first = self.pending_reqs.is_empty();
Self::handle_notif_reqs_raw(&mut self.pending_reqs, &self.receiver, block_until_first);
log::trace!("Handled notif reqs");
// block on getting the next event
if self.event_queue.read(&mut event).unwrap() == 0 {
// event queue has been destroyed
break;
}
let (vector, irq_handle) = match self.int_sources.iter_mut().nth(event.data) {
Some(s) => s,
None => continue,
};
if irq_handle.read(&mut irq_word[..WORD_SIZE]).unwrap() == 0 {
continue;
}
// acknowledge the interrupt (only necessary for level-triggered INTx# interrups)
if irq_handle.write(&irq_word[..WORD_SIZE]).unwrap() == 0 {
continue;
}
log::trace!("NVME IRQ: vector {}", vector);
self.nvme.set_vector_masked(vector, true);
self.poll_completion_queues(vector);
self.nvme.set_vector_masked(vector, false);
}
}
}
pub fn start_cq_reactor_thread(
nvme: Arc<Nvme>,
interrupt_sources: InterruptSources,
receiver: Receiver<NotifReq>,
) -> thread::JoinHandle<()> {
// Actually, nothing prevents us from spawning additional threads. the channel is MPMC and
// everything is properly synchronized. I'm not saying this is strictly required, but with
// multiple completion queues it might actually be worth considering. The (in-kernel) IRQ
// subsystem can have some room for improvement regarding lowering the latency, but MSI-X allows
// multiple vectors to point to different CPUs, so that the load can be balanced across the
// logical processors.
thread::spawn(move || {
CqReactor::new(nvme, interrupt_sources, receiver)
.expect("nvmed: failed to setup CQ reactor")
.run()
})
}
#[derive(Debug)]
pub struct CompletionMessage {
cq_entry: NvmeComp,
}
enum CompletionFutureState<'a, F> {
// the future is in its initial state: the command has not been submitted yet, and no interest
// has been registered. this state will repeat until a free submission queue entry appears to
// it, which it probably will since queues aren't supposed to be nearly always be full.
PendingSubmission {
cmd_init: F,
nvme: &'a Nvme,
sq_id: SqId,
},
PendingCompletion {
nvme: &'a Nvme,
cq_id: CqId,
cmd_id: CmdId,
sq_id: SqId,
message: Arc<Mutex<Option<CompletionMessage>>>,
},
Finished,
Placeholder,
}
pub struct CompletionFuture<'a, F> {
state: CompletionFutureState<'a, F>,
}
// enum not self-referential
impl<F> Unpin for CompletionFuture<'_, F> {}
impl<F> Future for CompletionFuture<'_, F>
where
F: FnOnce(CmdId) -> NvmeCmd,
{
type Output = NvmeComp;
fn poll(self: Pin<&mut Self>, context: &mut task::Context) -> task::Poll<Self::Output> {
let this = &mut self.get_mut().state;
match mem::replace(this, CompletionFutureState::Placeholder) {
CompletionFutureState::PendingSubmission { cmd_init, nvme, sq_id } => {
let sqs_read_guard = nvme.submission_queues.read().unwrap();
let &(ref sq_lock, cq_id) = sqs_read_guard
.get(&sq_id)
.expect("nvmed: internal error: given SQ for SQ ID not there");
let mut sq_guard = sq_lock.lock().unwrap();
let sq = &mut *sq_guard;
if sq.is_full() {
// when the CQ reactor gets a new completion queue entry, it'll lock the
// submisson queue it came from. since we're holding the same lock, this
// message will always be sent before the reactor is done with the entry.
nvme.reactor_sender.send(NotifReq::RequestAvailSubmission { sq_id, waker: context.waker().clone() }).unwrap();
*this = CompletionFutureState::PendingSubmission { cmd_init, nvme, sq_id };
return task::Poll::Pending;
}
let cmd_id =
u16::try_from(sq.tail).expect("nvmed: internal error: CQ has more than 2^16 entries");
let tail = sq.submit_unchecked(cmd_init(cmd_id));
let tail = u16::try_from(tail).unwrap();
// make sure that we register interest before the reactor can get notified
let message = Arc::new(Mutex::new(None));
*this = CompletionFutureState::PendingCompletion { nvme, cq_id, cmd_id, sq_id, message: Arc::clone(&message), };
nvme.reactor_sender.send(NotifReq::RequestCompletion { cq_id, sq_id, cmd_id, message, waker: context.waker().clone() }).expect("reactor dead");
unsafe { nvme.submission_queue_tail(sq_id, tail) };
task::Poll::Pending
}
CompletionFutureState::PendingCompletion {
message,
cq_id,
cmd_id,
sq_id,
nvme,
} => {
if let Some(value) = message.lock().unwrap().take() {
*this = CompletionFutureState::Finished;
return task::Poll::Ready(value.cq_entry);
}
nvme.reactor_sender.send(NotifReq::RequestCompletion {
cq_id,
sq_id,
cmd_id,
waker: context.waker().clone(),
message: Arc::clone(&message),
}).expect("reactor dead");
*this = CompletionFutureState::PendingCompletion { message, cq_id, cmd_id, sq_id, nvme };
task::Poll::Pending
}
CompletionFutureState::Finished => {
panic!("calling poll() on an already finished CompletionFuture")
}
CompletionFutureState::Placeholder => unreachable!(),
}
}
}
impl Nvme {
pub fn submit_and_complete_command<F: FnOnce(CmdId) -> NvmeCmd>(&self, sq_id: SqId, cmd_init: F) -> CompletionFuture<F> {
CompletionFuture {
state: CompletionFutureState::PendingSubmission { cmd_init, nvme: &self, sq_id },
}
}
}
+216
View File
@@ -0,0 +1,216 @@
use syscall::Dma;
use super::{Nvme, NvmeCmd, NvmeNamespace};
/// See NVME spec section 5.15.2.2.
#[derive(Clone, Copy)]
#[repr(packed)]
pub struct IdentifyControllerData {
/// PCI vendor ID, always the same as in the PCI function header.
pub vid: u16,
/// PCI subsystem vendor ID.
pub ssvid: u16,
/// ASCII
pub serial_no: [u8; 20],
/// ASCII
pub model_no: [u8; 48],
/// ASCII
pub firmware_rev: [u8; 8],
// TODO: Lots of fields
pub _4k_pad: [u8; 4096 - 72],
}
/// See NVME spec section 5.15.2.1.
#[derive(Clone, Copy)]
#[repr(packed)]
pub struct IdentifyNamespaceData {
pub nsze: u64,
pub ncap: u64,
pub nuse: u64,
pub nsfeat: u8,
pub nlbaf: u8,
pub flbas: u8,
pub mc: u8,
pub dpc: u8,
pub dps: u8,
pub nmic: u8,
pub rescap: u8,
// 32
pub fpi: u8,
pub dlfeat: u8,
pub nawun: u16,
pub nawupf: u16,
pub nacwu: u16,
// 40
pub nabsn: u16,
pub nabo: u16,
pub nabspf: u16,
pub noiob: u16,
// 48
pub nvmcap: u128,
// 64
pub npwg: u16,
pub npwa: u16,
pub npdg: u16,
pub npda: u16,
// 72
pub nows: u16,
pub _rsvd1: [u8; 18],
// 92
pub anagrpid: u32,
pub _rsvd2: [u8; 3],
pub nsattr: u8,
// 100
pub nvmsetid: u16,
pub endgid: u16,
pub nguid: [u8; 16],
pub eui64: u64,
pub lba_format_support: [LbaFormat; 16],
pub _rsvd3: [u8; 192],
pub vendor_specific: [u8; 3712],
}
impl IdentifyNamespaceData {
pub fn size_in_blocks(&self) -> u64 {
self.nsze
}
pub fn capacity_in_blocks(&self) -> u64 {
self.ncap
}
/// Guaranteed to be within 0..=15
pub fn formatted_lba_size_idx(&self) -> usize {
(self.flbas & 0xF) as usize
}
pub fn formatted_lba_size(&self) -> &LbaFormat {
&self.lba_format_support[self.formatted_lba_size_idx()]
}
pub fn has_metadata_after_data(&self) -> bool {
(self.flbas & (1 << 4)) != 0
}
}
#[derive(Clone, Copy)]
#[repr(packed)]
pub struct LbaFormat(pub u32);
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RelativePerformance {
Best = 0b00,
Better,
Good,
Degraded,
}
impl Ord for RelativePerformance {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
// higher performance is better, hence reversed
Ord::cmp(&(*self as u8), &(*other as u8)).reverse()
}
}
impl PartialOrd for RelativePerformance {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(Ord::cmp(self, other))
}
}
impl LbaFormat {
pub fn relative_performance(&self) -> RelativePerformance {
match ((self.0 >> 24) & 0b11) {
0b00 => RelativePerformance::Best,
0b01 => RelativePerformance::Better,
0b10 => RelativePerformance::Good,
0b11 => RelativePerformance::Degraded,
_ => unreachable!(),
}
}
pub fn is_available(&self) -> bool {
self.log_lba_data_size() != 0
}
pub fn log_lba_data_size(&self) -> u8 {
((self.0 >> 16) & 0xFF) as u8
}
pub fn lba_data_size(&self) -> Option<u64> {
if self.log_lba_data_size() < 9 { return None }
if self.log_lba_data_size() >= 32 { return None }
Some(1u64 << self.log_lba_data_size())
}
pub fn metadata_size(&self) -> u16 {
(self.0 & 0xFFFF) as u16
}
}
impl Nvme {
/// Returns the serial number, model, and firmware, in that order.
pub async fn identify_controller(&self) {
// TODO: Use same buffer
let data: Dma<IdentifyControllerData> = Dma::zeroed().unwrap();
// println!(" - Attempting to identify controller");
let comp = self
.submit_and_complete_admin_command(|cid| NvmeCmd::identify_controller(cid, data.physical()))
.await;
log::trace!("Completion: {:?}", comp);
// println!(" - Dumping identify controller");
let model_cow = String::from_utf8_lossy(&data.model_no);
let serial_cow = String::from_utf8_lossy(&data.serial_no);
let fw_cow = String::from_utf8_lossy(&data.firmware_rev);
let model = model_cow.trim();
let serial = serial_cow.trim();
let firmware = fw_cow.trim();
println!(
" - Model: {} Serial: {} Firmware: {}",
model, serial, firmware,
);
}
pub async fn identify_namespace_list(&self, base: u32) -> Vec<u32> {
// TODO: Use buffer
let data: Dma<[u32; 1024]> = Dma::zeroed().unwrap();
// println!(" - Attempting to retrieve namespace ID list");
let comp = self
.submit_and_complete_admin_command(|cid| {
NvmeCmd::identify_namespace_list(cid, data.physical(), base)
})
.await;
log::trace!("Completion2: {:?}", comp);
// println!(" - Dumping namespace ID list");
data.iter().copied().take_while(|&nsid| nsid != 0).collect()
}
pub async fn identify_namespace(&self, nsid: u32) -> NvmeNamespace {
//TODO: Use buffer
let data: Dma<IdentifyNamespaceData> = Dma::zeroed().unwrap();
// println!(" - Attempting to identify namespace {}", nsid);
let comp = self
.submit_and_complete_admin_command(|cid| NvmeCmd::identify_namespace(cid, data.physical(), nsid))
.await;
// println!(" - Dumping identify namespace");
let size = data.size_in_blocks();
let capacity = data.capacity_in_blocks();
log::info!("NSID: {} Size: {} Capacity: {}", nsid, size, capacity);
let block_size = data.formatted_lba_size().lba_data_size().expect("nvmed: error: size outside 512-2^64 range");
log::debug!("NVME block size: {}", block_size);
NvmeNamespace {
id: nsid,
blocks: size,
block_size,
}
}
}
+582
View File
@@ -0,0 +1,582 @@
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::fs::File;
use std::ptr;
use std::sync::atomic::{AtomicU16, AtomicU64, AtomicUsize, Ordering};
use std::sync::{Mutex, RwLock};
use crossbeam_channel::Sender;
use smallvec::{smallvec, SmallVec};
use syscall::error::{Error, Result, EINVAL};
use syscall::io::{Dma, Io, Mmio};
pub mod cmd;
pub mod cq_reactor;
pub mod identify;
pub mod queues;
use self::cq_reactor::NotifReq;
pub use self::queues::{NvmeCmd, NvmeCmdQueue, NvmeComp, NvmeCompQueue};
use pcid_interface::msi::{MsiCapability, MsixCapability, MsixTableEntry};
use pcid_interface::PcidServerHandle;
/// Used in conjunction with `InterruptMethod`, primarily by the CQ reactor.
#[derive(Debug)]
pub enum InterruptSources {
MsiX(BTreeMap<u16, File>),
Msi(BTreeMap<u8, File>),
Intx(File),
}
impl InterruptSources {
pub fn iter_mut(&mut self) -> impl Iterator<Item = (u16, &mut File)> {
use std::collections::btree_map::IterMut as BTreeIterMut;
use std::iter::Once;
enum IterMut<'a> {
Msi(BTreeIterMut<'a, u8, File>),
MsiX(BTreeIterMut<'a, u16, File>),
Intx(Once<&'a mut File>),
}
impl<'a> Iterator for IterMut<'a> {
type Item = (u16, &'a mut File);
fn next(&mut self) -> Option<Self::Item> {
match self {
&mut Self::Msi(ref mut iter) => iter
.next()
.map(|(&vector, handle)| (u16::from(vector), handle)),
&mut Self::MsiX(ref mut iter) => {
iter.next().map(|(&vector, handle)| (vector, handle))
}
&mut Self::Intx(ref mut iter) => iter.next().map(|handle| (0u16, handle)),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
&Self::Msi(ref iter) => iter.size_hint(),
&Self::MsiX(ref iter) => iter.size_hint(),
&Self::Intx(ref iter) => iter.size_hint(),
}
}
}
match self {
&mut Self::MsiX(ref mut map) => IterMut::MsiX(map.iter_mut()),
&mut Self::Msi(ref mut map) => IterMut::Msi(map.iter_mut()),
&mut Self::Intx(ref mut single) => IterMut::Intx(std::iter::once(single)),
}
}
}
/// The way interrupts are sent. Unlike other PCI-based interfaces, like XHCI, it doesn't seem like
/// NVME supports operating with interrupts completely disabled.
pub enum InterruptMethod {
/// Traditional level-triggered, INTx# interrupt pins.
Intx,
/// Message signaled interrupts
Msi(MsiCapability),
/// Extended message signaled interrupts
MsiX(MsixCfg),
}
impl InterruptMethod {
fn is_intx(&self) -> bool {
if let Self::Intx = self {
true
} else {
false
}
}
fn is_msi(&self) -> bool {
if let Self::Msi(_) = self {
true
} else {
false
}
}
fn is_msix(&self) -> bool {
if let Self::MsiX(_) = self {
true
} else {
false
}
}
}
pub struct MsixCfg {
pub cap: MsixCapability,
pub table: &'static mut [MsixTableEntry],
pub pba: &'static mut [Mmio<u64>],
}
#[repr(packed)]
pub struct NvmeRegs {
/// Controller Capabilities
cap: Mmio<u64>,
/// Version
vs: Mmio<u32>,
/// Interrupt mask set
intms: Mmio<u32>,
/// Interrupt mask clear
intmc: Mmio<u32>,
/// Controller configuration
cc: Mmio<u32>,
/// Reserved
_rsvd: Mmio<u32>,
/// Controller status
csts: Mmio<u32>,
/// NVM subsystem reset
nssr: Mmio<u32>,
/// Admin queue attributes
aqa: Mmio<u32>,
/// Admin submission queue base address
asq: Mmio<u64>,
/// Admin completion queue base address
acq: Mmio<u64>,
/// Controller memory buffer location
cmbloc: Mmio<u32>,
/// Controller memory buffer size
cmbsz: Mmio<u32>,
}
#[derive(Debug)]
pub struct NvmeNamespace {
pub id: u32,
pub blocks: u64,
pub block_size: u64,
}
pub type CqId = u16;
pub type SqId = u16;
pub type CmdId = u16;
pub type AtomicCqId = AtomicU16;
pub type AtomicSqId = AtomicU16;
pub type AtomicCmdId = AtomicU16;
pub struct Nvme {
interrupt_method: Mutex<InterruptMethod>,
pcid_interface: Mutex<PcidServerHandle>,
regs: RwLock<&'static mut NvmeRegs>,
pub(crate) submission_queues: RwLock<BTreeMap<SqId, (Mutex<NvmeCmdQueue>, CqId)>>,
pub(crate) completion_queues:
RwLock<BTreeMap<CqId, Mutex<(NvmeCompQueue, SmallVec<[SqId; 16]>)>>>,
// maps interrupt vectors with the completion queues they have
cqs_for_ivs: RwLock<BTreeMap<u16, SmallVec<[CqId; 4]>>>,
buffer: Mutex<Dma<[u8; 512 * 4096]>>, // 2MB of buffer
buffer_prp: Mutex<Dma<[u64; 512]>>, // 4KB of PRP for the buffer
reactor_sender: Sender<cq_reactor::NotifReq>,
next_sqid: AtomicSqId,
next_cqid: AtomicCqId,
next_avail_submission_epoch: AtomicU64,
}
unsafe impl Send for Nvme {}
unsafe impl Sync for Nvme {}
/// How to handle full submission queues.
pub enum FullSqHandling {
/// Return an error immediately prior to posting the command.
ErrorDirectly,
/// Tell the IRQ reactor that we want to be notified when a command on the same submission
/// queue has been completed.
Wait,
}
impl Nvme {
pub fn new(
address: usize,
interrupt_method: InterruptMethod,
pcid_interface: PcidServerHandle,
reactor_sender: Sender<NotifReq>,
) -> Result<Self> {
Ok(Nvme {
regs: RwLock::new(unsafe { &mut *(address as *mut NvmeRegs) }),
submission_queues: RwLock::new(
std::iter::once((0u16, (Mutex::new(NvmeCmdQueue::new()?), 0u16))).collect(),
),
completion_queues: RwLock::new(
std::iter::once((0u16, Mutex::new((NvmeCompQueue::new()?, smallvec!(0))))).collect(),
),
// map the zero interrupt vector (which according to the spec shall always point to the
// admin completion queue) to CQID 0 (admin completion queue)
cqs_for_ivs: RwLock::new(std::iter::once((0, smallvec!(0))).collect()),
buffer: Mutex::new(Dma::zeroed()?),
buffer_prp: Mutex::new(Dma::zeroed()?),
interrupt_method: Mutex::new(interrupt_method),
pcid_interface: Mutex::new(pcid_interface),
reactor_sender,
next_sqid: AtomicSqId::new(0),
next_cqid: AtomicCqId::new(0),
next_avail_submission_epoch: AtomicU64::new(0),
})
}
/// Write to a doorbell register.
///
/// # Locking
/// Locks `regs`.
unsafe fn doorbell_write(&self, index: usize, value: u32) {
use std::ops::DerefMut;
let mut regs_guard = self.regs.write().unwrap();
let mut regs: &mut NvmeRegs = regs_guard.deref_mut();
let dstrd = ((regs.cap.read() >> 32) & 0b1111) as usize;
let addr = (regs as *mut NvmeRegs as usize) + 0x1000 + index * (4 << dstrd);
(&mut *(addr as *mut Mmio<u32>)).write(value);
}
pub unsafe fn submission_queue_tail(&self, qid: u16, tail: u16) {
self.doorbell_write(2 * (qid as usize), u32::from(tail));
}
pub unsafe fn completion_queue_head(&self, qid: u16, head: u16) {
self.doorbell_write(2 * (qid as usize) + 1, u32::from(head));
}
pub unsafe fn init(&mut self) {
let mut buffer = self.buffer.get_mut().unwrap();
let mut buffer_prp = self.buffer_prp.get_mut().unwrap();
for i in 0..buffer_prp.len() {
buffer_prp[i] = (buffer.physical() + i * 4096) as u64;
}
{
let regs = self.regs.read().unwrap();
log::debug!("CAPS: {:X}", regs.cap.read());
log::debug!("VS: {:X}", regs.vs.read());
log::debug!("CC: {:X}", regs.cc.read());
log::debug!("CSTS: {:X}", regs.csts.read());
}
log::debug!("Disabling controller.");
self.regs.get_mut().unwrap().cc.writef(1, false);
log::trace!("Waiting for not ready.");
loop {
let csts = self.regs.get_mut().unwrap().csts.read();
log::trace!("CSTS: {:X}", csts);
if csts & 1 == 1 {
std::arch::x86_64::_mm_pause();
} else {
break;
}
}
match self.interrupt_method.get_mut().unwrap() {
&mut InterruptMethod::Intx | InterruptMethod::Msi(_) => {
self.regs.get_mut().unwrap().intms.write(0xFFFF_FFFF);
self.regs.get_mut().unwrap().intmc.write(0x0000_0001);
}
&mut InterruptMethod::MsiX(ref mut cfg) => {
cfg.table[0].unmask();
}
}
for (qid, queue) in self.completion_queues.get_mut().unwrap().iter_mut() {
let &(ref cq, ref sq_ids) = &*queue.get_mut().unwrap();
let data = &cq.data;
log::debug!("completion queue {}: {:X}, {}, (submission queue ids: {:?}", qid, data.physical(), data.len(), sq_ids);
}
for (qid, (queue, cq_id)) in self.submission_queues.get_mut().unwrap().iter_mut() {
let data = &queue.get_mut().unwrap().data;
log::debug!("submission queue {}: {:X}, {}, attached to CQID: {}", qid, data.physical(), data.len(), cq_id);
}
{
let regs = self.regs.get_mut().unwrap();
let submission_queues = self.submission_queues.get_mut().unwrap();
let completion_queues = self.completion_queues.get_mut().unwrap();
let asq = submission_queues.get_mut(&0).unwrap().0.get_mut().unwrap();
let (acq, _) = completion_queues.get_mut(&0).unwrap().get_mut().unwrap();
regs.aqa
.write(((acq.data.len() as u32 - 1) << 16) | (asq.data.len() as u32 - 1));
regs.asq.write(asq.data.physical() as u64);
regs.acq.write(acq.data.physical() as u64);
// Set IOCQES, IOSQES, AMS, MPS, and CSS
let mut cc = regs.cc.read();
cc &= 0xFF00000F;
cc |= (4 << 20) | (6 << 16);
regs.cc.write(cc);
}
log::debug!("Enabling controller.");
self.regs.get_mut().unwrap().cc.writef(1, true);
log::debug!("Waiting for ready");
loop {
let csts = self.regs.get_mut().unwrap().csts.read();
log::debug!("CSTS: {:X}", csts);
if csts & 1 == 0 {
std::arch::x86_64::_mm_pause();
} else {
break;
}
}
}
/// Masks or unmasks multiple vectors.
///
/// # Panics
/// Will panic if the same vector is called twice with different mask flags.
pub fn set_vectors_masked(&self, vectors: impl IntoIterator<Item = (u16, bool)>) {
let mut interrupt_method_guard = self.interrupt_method.lock().unwrap();
match &mut *interrupt_method_guard {
&mut InterruptMethod::Intx => {
let mut iter = vectors.into_iter();
let (vector, mask) = match iter.next() {
Some(f) => f,
None => return,
};
assert_eq!(
iter.next(),
None,
"nvmed: internal error: multiple vectors on INTx#"
);
assert_eq!(vector, 0, "nvmed: internal error: nonzero vector on INTx#");
if mask {
self.regs.write().unwrap().intms.write(0x0000_0001);
} else {
self.regs.write().unwrap().intmc.write(0x0000_0001);
}
}
&mut InterruptMethod::Msi(ref mut cap) => {
let mut to_mask = 0x0000_0000;
let mut to_clear = 0x0000_0000;
for (vector, mask) in vectors {
assert!(
vector < (1 << cap.multi_message_enable()),
"nvmed: internal error: MSI vector out of range"
);
let vector = vector as u8;
if mask {
assert_ne!(
to_clear & (1 << vector),
(1 << vector),
"nvmed: internal error: cannot both mask and set"
);
to_mask |= 1 << vector;
} else {
assert_ne!(
to_mask & (1 << vector),
(1 << vector),
"nvmed: internal error: cannot both mask and set"
);
to_clear |= 1 << vector;
}
}
if to_mask != 0 {
self.regs.write().unwrap().intms.write(to_mask);
}
if to_clear != 0 {
self.regs.write().unwrap().intmc.write(to_clear);
}
}
&mut InterruptMethod::MsiX(ref mut cfg) => {
for (vector, mask) in vectors {
cfg.table
.get_mut(vector as usize)
.expect("nvmed: internal error: MSI-X vector out of range")
.set_masked(mask);
}
}
}
}
pub fn set_vector_masked(&self, vector: u16, masked: bool) {
self.set_vectors_masked(std::iter::once((vector, masked)))
}
pub async fn submit_and_complete_admin_command<F: FnOnce(CmdId) -> NvmeCmd>(&self, cmd_init: F) -> NvmeComp {
self.submit_and_complete_command(0, cmd_init).await
}
pub async fn create_io_completion_queue(&self, io_cq_id: CqId, vector: Option<u16>) {
let (ptr, len) = {
let mut completion_queues_guard = self.completion_queues.write().unwrap();
let queue_guard = completion_queues_guard
.entry(io_cq_id)
.or_insert_with(|| {
let queue = NvmeCompQueue::new()
.expect("nvmed: failed to allocate I/O completion queue");
let sqs = SmallVec::new();
Mutex::new((queue, sqs))
})
.get_mut()
.unwrap();
let &(ref queue, _) = &*queue_guard;
(queue.data.physical(), queue.data.len())
};
let len =
u16::try_from(len).expect("nvmed: internal error: I/O CQ longer than 2^16 entries");
let raw_len = len
.checked_sub(1)
.expect("nvmed: internal error: CQID 0 for I/O CQ");
let comp = self
.submit_and_complete_admin_command(|cid| {
NvmeCmd::create_io_completion_queue(cid, io_cq_id, ptr, raw_len, vector)
})
.await;
if let Some(vector) = vector {
self.cqs_for_ivs
.write()
.unwrap()
.entry(vector)
.or_insert_with(SmallVec::new)
.push(io_cq_id);
}
}
pub async fn create_io_submission_queue(&self, io_sq_id: SqId, io_cq_id: CqId) {
let (ptr, len) = {
let mut submission_queues_guard = self.submission_queues.write().unwrap();
let (queue_lock, _) = submission_queues_guard
.entry(io_sq_id)
.or_insert_with(|| {
(Mutex::new(
NvmeCmdQueue::new()
.expect("nvmed: failed to allocate I/O completion queue"),
), io_cq_id)
});
let queue = queue_lock.get_mut().unwrap();
(queue.data.physical(), queue.data.len())
};
let len =
u16::try_from(len).expect("nvmed: internal error: I/O SQ longer than 2^16 entries");
let raw_len = len
.checked_sub(1)
.expect("nvmed: internal error: SQID 0 for I/O SQ");
let comp = self
.submit_and_complete_admin_command(|cid| {
NvmeCmd::create_io_submission_queue(cid, io_sq_id, ptr, raw_len, io_cq_id)
})
.await;
}
pub async fn init_with_queues(&self) -> BTreeMap<u32, NvmeNamespace> {
log::trace!("preinit");
let ((), nsids) =
futures::join!(self.identify_controller(), self.identify_namespace_list(0));
log::debug!("first commands");
let mut namespaces = BTreeMap::new();
for nsid in nsids.iter().copied() {
namespaces.insert(nsid, self.identify_namespace(nsid).await);
}
// TODO: Multiple queues
self.create_io_completion_queue(1, Some(0)).await;
self.create_io_submission_queue(1, 1).await;
namespaces
}
async fn namespace_rw(
&self,
namespace: &NvmeNamespace,
nsid: u32,
lba: u64,
blocks_1: u16,
write: bool,
) -> Result<()> {
let block_size = namespace.block_size;
let buffer_prp_guard = self.buffer_prp.lock().unwrap();
let bytes = ((blocks_1 as u64) + 1) * block_size;
let (ptr0, ptr1) = if bytes <= 4096 {
(buffer_prp_guard[0], 0)
} else if bytes <= 8192 {
(buffer_prp_guard[0], buffer_prp_guard[1])
} else {
(buffer_prp_guard[0], (buffer_prp_guard.physical() + 8) as u64)
};
let comp = self.submit_and_complete_command(1, |cid| {
if write {
NvmeCmd::io_write(cid, nsid, lba, blocks_1, ptr0, ptr1)
} else {
NvmeCmd::io_read(cid, nsid, lba, blocks_1, ptr0, ptr1)
}
}).await;
// TODO: Handle errors
Ok(())
}
pub async fn namespace_read(
&self,
namespace: &NvmeNamespace,
nsid: u32,
mut lba: u64,
buf: &mut [u8],
) -> Result<Option<usize>> {
let block_size = namespace.block_size as usize;
let buffer_guard = self.buffer.lock().unwrap();
for chunk in buf.chunks_mut(buffer_guard.len()) {
let blocks = (chunk.len() + block_size - 1) / block_size;
assert!(blocks > 0);
assert!(blocks <= 0x1_0000);
self.namespace_rw(namespace, nsid, lba, (blocks - 1) as u16, false).await?;
chunk.copy_from_slice(&buffer_guard[..chunk.len()]);
lba += blocks as u64;
}
Ok(Some(buf.len()))
}
pub async fn namespace_write(
&self,
namespace: &NvmeNamespace,
nsid: u32,
mut lba: u64,
buf: &[u8],
) -> Result<Option<usize>> {
let block_size = namespace.block_size as usize;
let mut buffer_guard = self.buffer.lock().unwrap();
for chunk in buf.chunks(buffer_guard.len()) {
let blocks = (chunk.len() + block_size - 1) / block_size;
assert!(blocks > 0);
assert!(blocks <= 0x1_0000);
buffer_guard[..chunk.len()].copy_from_slice(chunk);
self.namespace_rw(namespace, nsid, lba, (blocks - 1) as u16, true).await?;
lba += blocks as u64;
}
Ok(Some(buf.len()))
}
}
+120
View File
@@ -0,0 +1,120 @@
use std::ptr;
use syscall::{Dma, Result};
/// A submission queue entry.
#[derive(Clone, Copy, Debug, Default)]
#[repr(packed)]
pub struct NvmeCmd {
/// Opcode
pub opcode: u8,
/// Flags
pub flags: u8,
/// Command ID
pub cid: u16,
/// Namespace identifier
pub nsid: u32,
/// Reserved
pub _rsvd: u64,
/// Metadata pointer
pub mptr: u64,
/// Data pointer
pub dptr: [u64; 2],
/// Command dword 10
pub cdw10: u32,
/// Command dword 11
pub cdw11: u32,
/// Command dword 12
pub cdw12: u32,
/// Command dword 13
pub cdw13: u32,
/// Command dword 14
pub cdw14: u32,
/// Command dword 15
pub cdw15: u32,
}
/// A completion queue entry.
#[derive(Clone, Copy, Debug)]
#[repr(packed)]
pub struct NvmeComp {
pub command_specific: u32,
pub _rsvd: u32,
pub sq_head: u16,
pub sq_id: u16,
pub cid: u16,
pub status: u16,
}
/// Completion queue
pub struct NvmeCompQueue {
pub data: Dma<[NvmeComp]>,
pub head: u16,
pub phase: bool,
}
impl NvmeCompQueue {
pub fn new() -> Result<Self> {
Ok(Self {
data: unsafe { Dma::zeroed_unsized(256)? },
head: 0,
phase: true,
})
}
/// Get a new completion queue entry, or return None if no entry is available yet.
pub(crate) fn complete(&mut self) -> Option<(u16, NvmeComp)> {
let entry = unsafe { ptr::read_volatile(self.data.as_ptr().add(self.head as usize)) };
if ((entry.status & 1) == 1) == self.phase {
self.head = (self.head + 1) % (self.data.len() as u16);
if self.head == 0 {
self.phase = !self.phase;
}
Some((self.head, entry))
} else {
None
}
}
/// Get a new CQ entry, busy waiting until an entry appears.
fn complete_spin(&mut self) -> (u16, NvmeComp) {
loop {
if let Some(some) = self.complete() {
return some;
} else {
unsafe { std::arch::x86_64::_mm_pause() }
}
}
}
}
/// Submission queue
pub struct NvmeCmdQueue {
pub data: Dma<[NvmeCmd]>,
pub tail: u16,
pub head: u16,
}
impl NvmeCmdQueue {
pub fn new() -> Result<Self> {
Ok(Self {
data: unsafe { Dma::zeroed_unsized(64)? },
tail: 0,
head: 0,
})
}
pub fn is_empty(&self) -> bool {
self.head == self.tail
}
pub fn is_full(&self) -> bool {
self.head == self.tail + 1
}
/// Add a new submission command entry to the queue. The caller must ensure that the queue have free
/// entries; this can be checked using `is_full`.
pub fn submit_unchecked(&mut self, entry: NvmeCmd) -> u16 {
unsafe { ptr::write_volatile(&mut self.data[self.tail as usize] as *mut _, entry) }
self.tail = (self.tail + 1) % (self.data.len() as u16);
self.tail
}
}
+149 -79
View File
@@ -1,13 +1,15 @@
use std::collections::BTreeMap;
use std::{cmp, str};
use std::convert::{TryFrom, TryInto};
use std::fmt::Write;
use std::io::prelude::*;
use std::io;
use std::io::prelude::*;
use std::sync::Arc;
use std::{cmp, str};
use syscall::{
Error, EACCES, EBADF, EINVAL, EISDIR, ENOENT, EOVERFLOW, Result,
Io, SchemeBlockMut, Stat, MODE_DIR, MODE_FILE, O_DIRECTORY,
O_STAT, SEEK_CUR, SEEK_END, SEEK_SET};
Error, Io, Result, SchemeBlockMut, Stat, EACCES, EBADF, EINVAL, EISDIR, ENOENT, EOVERFLOW,
MODE_DIR, MODE_FILE, O_DIRECTORY, O_STAT, SEEK_CUR, SEEK_END, SEEK_SET,
};
use crate::nvme::{Nvme, NvmeNamespace};
@@ -15,8 +17,8 @@ use partitionlib::{LogicalBlockSize, PartitionTable};
#[derive(Clone)]
enum Handle {
List(Vec<u8>, usize), // entries, offset
Disk(u32, usize), // disk num, offset
List(Vec<u8>, usize), // entries, offset
Disk(u32, usize), // disk num, offset
Partition(u32, u32, usize), // disk num, part num, offset
}
@@ -32,23 +34,35 @@ impl AsRef<NvmeNamespace> for DiskWrapper {
}
impl DiskWrapper {
fn pt(disk: &mut NvmeNamespace, nvme: &mut Nvme) -> Option<PartitionTable> {
fn pt(disk: &mut NvmeNamespace, nvme: &Nvme) -> Option<PartitionTable> {
let bs = match disk.block_size {
512 => LogicalBlockSize::Lb512,
4096 => LogicalBlockSize::Lb4096,
_ => return None,
};
struct Device<'a, 'b> { disk: &'a mut NvmeNamespace, nvme: &'a mut Nvme, offset: u64, block_bytes: &'b mut [u8] }
struct Device<'a, 'b> {
disk: &'a mut NvmeNamespace,
nvme: &'a Nvme,
offset: u64,
block_bytes: &'b mut [u8],
}
impl<'a, 'b> Seek for Device<'a, 'b> {
fn seek(&mut self, from: io::SeekFrom) -> io::Result<u64> {
let size_u = self.disk.blocks * self.disk.block_size;
let size = i64::try_from(size_u).or(Err(io::Error::new(io::ErrorKind::Other, "Disk larger than 2^63 - 1 bytes")))?;
let size = i64::try_from(size_u).or(Err(io::Error::new(
io::ErrorKind::Other,
"Disk larger than 2^63 - 1 bytes",
)))?;
self.offset = match from {
io::SeekFrom::Start(new_pos) => cmp::min(size_u, new_pos),
io::SeekFrom::Current(new_pos) => cmp::max(0, cmp::min(size, self.offset as i64 + new_pos)) as u64,
io::SeekFrom::End(new_pos) => cmp::max(0, cmp::min(size + new_pos, size)) as u64,
io::SeekFrom::Current(new_pos) => {
cmp::max(0, cmp::min(size, self.offset as i64 + new_pos)) as u64
}
io::SeekFrom::End(new_pos) => {
cmp::max(0, cmp::min(size + new_pos, size)) as u64
}
};
Ok(self.offset)
@@ -68,20 +82,29 @@ impl DiskWrapper {
return Err(io::Error::from_raw_os_error(syscall::EOVERFLOW));
}
loop {
match unsafe {
nvme.namespace_read(disk.id, block, block_bytes).map_err(|err| io::Error::from_raw_os_error(err.errno))?
} {
match futures::executor::block_on(nvme.namespace_read(disk, disk.id, block, block_bytes))
.map_err(|err| io::Error::from_raw_os_error(err.errno))? {
Some(bytes) => {
assert_eq!(bytes, block_bytes.len());
assert_eq!(bytes, blksize as usize);
return Ok(());
}
None => { std::thread::yield_now(); continue }
// TODO: Does this driver have (internal) error handling at all?
None => {
std::thread::yield_now();
continue;
} // TODO: Does this driver have (internal) error handling at all?
}
}
};
let bytes_read = block_io_wrapper::read(self.offset, blksize.try_into().expect("Unreasonable block size above 2^32 bytes"), buf, self.block_bytes, read_block)?;
let bytes_read = block_io_wrapper::read(
self.offset,
blksize
.try_into()
.expect("Unreasonable block size above 2^32 bytes"),
buf,
self.block_bytes,
read_block,
)?;
self.offset += bytes_read as u64;
Ok(bytes_read)
}
@@ -89,9 +112,19 @@ impl DiskWrapper {
let mut block_bytes = [0u8; 4096];
partitionlib::get_partitions(&mut Device { disk, nvme, offset: 0, block_bytes: &mut block_bytes[..bs.into()] }, bs).ok().flatten()
partitionlib::get_partitions(
&mut Device {
disk,
nvme,
offset: 0,
block_bytes: &mut block_bytes[..bs.into()],
},
bs,
)
.ok()
.flatten()
}
fn new(mut inner: NvmeNamespace, nvme: &mut Nvme) -> Self {
fn new(mut inner: NvmeNamespace, nvme: &Nvme) -> Self {
Self {
pt: Self::pt(&mut inner, nvme),
inner,
@@ -101,46 +134,37 @@ impl DiskWrapper {
pub struct DiskScheme {
scheme_name: String,
nvme: Nvme,
nvme: Arc<Nvme>,
disks: BTreeMap<u32, DiskWrapper>,
handles: BTreeMap<usize, Handle>,
next_id: usize
next_id: usize,
}
impl DiskScheme {
pub fn new(scheme_name: String, mut nvme: Nvme, disks: BTreeMap<u32, NvmeNamespace>) -> DiskScheme {
pub fn new(
scheme_name: String,
nvme: Arc<Nvme>,
disks: BTreeMap<u32, NvmeNamespace>,
) -> DiskScheme {
DiskScheme {
scheme_name,
disks: disks.into_iter().map(|(k, v)| (k, DiskWrapper::new(v, &mut nvme))).collect(),
disks: disks
.into_iter()
.map(|(k, v)| (k, DiskWrapper::new(v, &nvme)))
.collect(),
nvme,
handles: BTreeMap::new(),
next_id: 0
next_id: 0,
}
}
}
impl DiskScheme {
pub fn irq(&mut self) -> bool {
let mut found_completion = false;
let nvme = &mut self.nvme;
for qid in 0..nvme.completion_queues.len() {
while let Some((head, entry)) = nvme.completion_queues[qid].complete() {
found_completion = true;
println!("nvmed: Unhandled completion {:?}", entry);
//TODO: Handle errors
unsafe { nvme.completion_queue_head(qid as u16, head as u16); }
}
}
found_completion
}
}
impl SchemeBlockMut for DiskScheme {
fn open(&mut self, path: &[u8], flags: usize, uid: u32, _gid: u32) -> Result<Option<usize>> {
if uid == 0 {
let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?.trim_matches('/');
let path_str = str::from_utf8(path)
.or(Err(Error::new(ENOENT)))?
.trim_matches('/');
if path_str.is_empty() {
if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT {
let mut list = String::new();
@@ -175,10 +199,18 @@ impl SchemeBlockMut for DiskScheme {
let part_num = part_num_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
if let Some(disk) = self.disks.get(&nsid) {
if disk.pt.as_ref().ok_or(Error::new(ENOENT))?.partitions.get(part_num as usize).is_some() {
if disk
.pt
.as_ref()
.ok_or(Error::new(ENOENT))?
.partitions
.get(part_num as usize)
.is_some()
{
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, Handle::Partition(nsid, part_num, 0));
self.handles
.insert(id, Handle::Partition(nsid, part_num, 0));
Ok(Some(id))
} else {
Err(Error::new(ENOENT))
@@ -204,7 +236,7 @@ impl SchemeBlockMut for DiskScheme {
}
fn dup(&mut self, id: usize, buf: &[u8]) -> Result<Option<usize>> {
if ! buf.is_empty() {
if !buf.is_empty() {
return Err(Error::new(EINVAL));
}
@@ -225,22 +257,36 @@ impl SchemeBlockMut for DiskScheme {
stat.st_mode = MODE_DIR;
stat.st_size = data.len() as u64;
Ok(Some(0))
},
}
Handle::Disk(number, _) => {
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
stat.st_mode = MODE_FILE;
stat.st_blocks = disk.as_ref().blocks;
stat.st_blksize = disk.as_ref().block_size.try_into().expect("Unreasonable block size of over 2^32 bytes");
stat.st_blksize = disk
.as_ref()
.block_size
.try_into()
.expect("Unreasonable block size of over 2^32 bytes");
stat.st_size = disk.as_ref().blocks * disk.as_ref().block_size;
Ok(Some(0))
}
Handle::Partition(disk_num, part_num, _) => {
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
let part = disk.pt.as_ref().ok_or(Error::new(EBADF))?.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?;
let part = disk
.pt
.as_ref()
.ok_or(Error::new(EBADF))?
.partitions
.get(part_num as usize)
.ok_or(Error::new(EBADF))?;
stat.st_mode = MODE_FILE;
stat.st_size = part.size * disk.as_ref().block_size;
stat.st_blocks = part.size;
stat.st_blksize = disk.as_ref().block_size.try_into().expect("Unreasonable block size of over 2^32 bytes");
stat.st_blksize = disk
.as_ref()
.block_size
.try_into()
.expect("Unreasonable block size of over 2^32 bytes");
Ok(Some(0))
}
}
@@ -301,9 +347,7 @@ impl SchemeBlockMut for DiskScheme {
Handle::Disk(number, ref mut size) => {
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
let block_size = disk.as_ref().block_size;
if let Some(count) = unsafe {
self.nvme.namespace_read(disk.as_ref().id, (*size as u64)/block_size, buf)?
} {
if let Some(count) = futures::executor::block_on(self.nvme.namespace_read(disk.as_ref(), disk.as_ref().id, (*size as u64) / block_size, buf))? {
*size += count;
Ok(Some(count))
} else {
@@ -312,7 +356,13 @@ impl SchemeBlockMut for DiskScheme {
}
Handle::Partition(disk_num, part_num, ref mut offset) => {
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
let part = disk.pt.as_ref().ok_or(Error::new(EBADF))?.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?;
let part = disk
.pt
.as_ref()
.ok_or(Error::new(EBADF))?
.partitions
.get(part_num as usize)
.ok_or(Error::new(EBADF))?;
let block_size = disk.as_ref().block_size;
let rel_block = (*offset as u64) / block_size;
@@ -322,9 +372,7 @@ impl SchemeBlockMut for DiskScheme {
let abs_block = part.start_lba + rel_block;
if let Some(count) = unsafe {
self.nvme.namespace_read(disk.as_ref().id, abs_block, buf)?
} {
if let Some(count) = futures::executor::block_on(self.nvme.namespace_read(disk.as_ref(), disk.as_ref().id, abs_block, buf))? {
*offset += count;
Ok(Some(count))
} else {
@@ -336,15 +384,12 @@ impl SchemeBlockMut for DiskScheme {
fn write(&mut self, id: usize, buf: &[u8]) -> Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(_, _) => {
Err(Error::new(EBADF))
},
Handle::List(_, _) => Err(Error::new(EBADF)),
Handle::Disk(number, ref mut size) => {
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
let block_size = disk.as_ref().block_size;
if let Some(count) = unsafe {
self.nvme.namespace_write(disk.as_ref().id, (*size as u64)/block_size, buf)?
} {
if let Some(count) = futures::executor::block_on(self.nvme
.namespace_write(disk.as_ref(), disk.as_ref().id, (*size as u64) / block_size, buf))? {
*size += count;
Ok(Some(count))
} else {
@@ -353,7 +398,13 @@ impl SchemeBlockMut for DiskScheme {
}
Handle::Partition(disk_num, part_num, ref mut offset) => {
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
let part = disk.pt.as_ref().ok_or(Error::new(EBADF))?.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?;
let part = disk
.pt
.as_ref()
.ok_or(Error::new(EBADF))?
.partitions
.get(part_num as usize)
.ok_or(Error::new(EBADF))?;
let block_size = disk.as_ref().block_size;
let rel_block = (*offset as u64) / block_size;
@@ -363,9 +414,7 @@ impl SchemeBlockMut for DiskScheme {
let abs_block = part.start_lba + rel_block;
if let Some(count) = unsafe {
self.nvme.namespace_write(disk.as_ref().id, abs_block, buf)?
} {
if let Some(count) = futures::executor::block_on(self.nvme.namespace_write(disk.as_ref(), disk.as_ref().id, abs_block, buf))? {
*offset += count;
Ok(Some(count))
} else {
@@ -381,9 +430,13 @@ impl SchemeBlockMut for DiskScheme {
let len = handle.len() as usize;
*size = match whence {
SEEK_SET => cmp::min(len, pos),
SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize,
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
_ => return Err(Error::new(EINVAL))
SEEK_CUR => {
cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize
}
SEEK_END => {
cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize
}
_ => return Err(Error::new(EINVAL)),
};
Ok(Some(*size))
@@ -393,24 +446,38 @@ impl SchemeBlockMut for DiskScheme {
let len = (disk.as_ref().blocks * disk.as_ref().block_size) as usize;
*size = match whence {
SEEK_SET => cmp::min(len, pos),
SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize,
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
_ => return Err(Error::new(EINVAL))
SEEK_CUR => {
cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize
}
SEEK_END => {
cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize
}
_ => return Err(Error::new(EINVAL)),
};
Ok(Some(*size))
}
Handle::Partition(disk_num, part_num, ref mut size) => {
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
let part = disk.pt.as_ref().ok_or(Error::new(EBADF))?.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?;
let part = disk
.pt
.as_ref()
.ok_or(Error::new(EBADF))?
.partitions
.get(part_num as usize)
.ok_or(Error::new(EBADF))?;
let len = (part.size * disk.as_ref().block_size) as usize;
*size = match whence {
SEEK_SET => cmp::min(len, pos),
SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize,
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
_ => return Err(Error::new(EINVAL))
SEEK_CUR => {
cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize
}
SEEK_END => {
cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize
}
_ => return Err(Error::new(EINVAL)),
};
Ok(Some(*size))
@@ -419,6 +486,9 @@ impl SchemeBlockMut for DiskScheme {
}
fn close(&mut self, id: usize) -> Result<Option<usize>> {
self.handles.remove(&id).ok_or(Error::new(EBADF)).and(Ok(Some(0)))
self.handles
.remove(&id)
.ok_or(Error::new(EBADF))
.and(Ok(Some(0)))
}
}
+2
View File
@@ -16,7 +16,9 @@ bincode = "1.2"
bitflags = "1"
byteorder = "1.2"
libc = "0.2"
log = "0.4"
plain = "0.2"
redox-log = { git = "https://gitlab.redox-os.org/redox-os/redox-log.git", tag = "v0.1.0" }
redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
+1 -1
View File
@@ -19,5 +19,5 @@ pub struct DriverConfig {
pub device: Option<u16>,
pub device_id_range: Option<Range<u16>>,
pub command: Option<Vec<String>>,
pub channel_name: Option<String>,
pub use_channel: Option<bool>,
}
+155
View File
@@ -0,0 +1,155 @@
//! IRQ helpers.
//!
//! This module allows easy handling of the `irq:` scheme, and allocating interrupt vectors for use
//! by INTx#, MSI, or MSI-X.
use std::convert::TryFrom;
use std::fs::{self, File};
use std::io::{self, prelude::*};
use std::num::NonZeroU8;
/// Read the local APIC ID of the bootstrap processor.
pub fn read_bsp_apic_id() -> io::Result<usize> {
let mut buffer = [0u8; 8];
let mut file = File::open("irq:bsp")?;
let bytes_read = file.read(&mut buffer)?;
(if bytes_read == 8 {
usize::try_from(u64::from_le_bytes(buffer))
} else if bytes_read == 4 {
usize::try_from(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::<usize>());
}).or(Err(io::Error::new(io::ErrorKind::InvalidData, "bad BSP int size")))
}
// TODO: Perhaps read the MADT instead?
/// Obtains an interator over all of the visible CPU ids, for use in IRQ allocation and MSI
/// capability structs or MSI-X tables.
pub fn cpu_ids() -> io::Result<impl Iterator<Item = io::Result<usize>> + 'static> {
Ok(fs::read_dir("irq:")?
.filter_map(|entry| -> Option<io::Result<_>> { match entry {
Ok(e) => {
let path = e.path();
let file_name = path.file_name()?.to_str()?;
// the file name should be in the format `cpu-<CPU ID>`
if ! file_name.starts_with("cpu-") {
return None;
}
u8::from_str_radix(&file_name[4..], 16).map(usize::from).map(Ok).ok()
}
Err(e) => Some(Err(e)),
} }))
}
/// Allocate multiple interrupt vectors, from the IDT of the specified processor, returning the
/// start vector and the IRQ handles.
///
/// The alignment is a requirement for the allocation range. For example, with an alignment of 8,
/// only ranges that begin with a multiple of eight are accepted. The IRQ handles returned will
/// always correspond to the subsequent IRQ numbers beginning the first value in the return tuple.
///
/// This function is not actually guaranteed to allocate all of the IRQs specified in `count`,
/// since another process might already have requested one vector in the range. The caller must
/// check that the returned vector have the same length as `count`. In the future this function may
/// perhaps lock the entire directory to prevent this from happening, or maybe find the smallest free
/// range with the minimum alignment, to allow other drivers to obtain their necessary IRQs.
///
/// Note that this count/alignment restriction is only mandatory for MSI; MSI-X allows for
/// individually allocated vectors that might be spread out, even on multiple CPUs. Thus, multiple
/// invocations with alignment 1 and count 1 are totally acceptable, although allocating in bulk
/// minimizes the initialization overhead.
pub fn allocate_aligned_interrupt_vectors(cpu_id: usize, alignment: NonZeroU8, count: u8) -> io::Result<Option<(u8, Vec<File>)>> {
let cpu_id = u8::try_from(cpu_id).expect("usize cpu ids not implemented yet");
if count == 0 { return Ok(None) }
let available_irqs = fs::read_dir(format!("irq:cpu-{:02x}", cpu_id))?;
let mut available_irq_numbers = available_irqs.filter_map(|entry| -> Option<io::Result<_>> {
let entry = match entry {
Ok(e) => e,
Err(err) => return Some(Err(err)),
};
let path = entry.path();
let file_name = match path.file_name() {
Some(f) => f,
None => return None,
};
let path_str = match file_name.to_str() {
Some(s) => s,
None => return None,
};
match path_str.parse::<u8>() {
Ok(p) => Some(Ok(p)),
Err(_) => None,
}
});
// TODO: fcntl F_SETLK on `irq:/`?
let mut handles = Vec::with_capacity(usize::from(count));
let mut index = 0;
let mut first = None;
while let Some(number) = available_irq_numbers.next() {
let number = number?;
// Skip until a suitable alignment is found.
if number % u8::from(alignment) != 0 {
continue;
}
let first = *first.get_or_insert(number);
let irq_number = first + index;
// From the point where the range is aligned, we can start to advance until `count` IRQs
// have been allocated.
if index >= count {
break;
}
// if found, reserve the irq
let irq_handle = match File::create(format!("irq:cpu-{:02x}/{}", cpu_id, irq_number)) {
Ok(handle) => handle,
// return early if the entire range couldn't be allocated
Err(err) if err.kind() == io::ErrorKind::NotFound => break,
Err(err) => return Err(err),
};
handles.push(irq_handle);
index += 1;
}
if handles.is_empty() {
return Ok(None);
}
let first = match first {
Some(f) => f,
None => return Ok(None),
};
Ok(Some((first + 32, handles)))
}
/// Allocate at most `count` interrupt vectors, which can start at any offset. Unless MSI is used
/// and an entire aligned range of vectors is needed, this function should be used.
pub fn allocate_interrupt_vectors(cpu_id: usize, count: u8) -> io::Result<Option<(u8, Vec<File>)>> {
allocate_aligned_interrupt_vectors(cpu_id, NonZeroU8::new(1).unwrap(), count)
}
/// Allocate a single interrupt vector, returning both the vector number (starting from 32 up to
/// 254), and its IRQ handle which is then reserved. Returns Ok(None) if allocation fails due to
/// no available IRQs.
pub fn allocate_single_interrupt_vector(cpu_id: usize) -> io::Result<Option<(u8, File)>> {
let (base, mut files) = match allocate_interrupt_vectors(cpu_id, 1) {
Ok(Some((base, files))) => (base, files),
Ok(None) => return Ok(None),
Err(err) => return Err(err),
};
assert_eq!(files.len(), 1);
Ok(Some((base, files.pop().unwrap())))
}
@@ -10,6 +10,8 @@ use thiserror::Error;
pub use crate::pci::PciBar;
pub use crate::pci::msi;
pub mod irq_helpers;
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[repr(u8)]
pub enum LegacyInterruptPin {
@@ -40,7 +42,9 @@ pub struct PciFunction {
/// BAR sizes
pub bar_sizes: [u32; 6],
/// Legacy IRQ line
/// Legacy IRQ line: It's the responsibility of pcid to make sure that it be mapped in either
/// the I/O APIC or the 8259 PIC, so that the subdriver can map the interrupt vector directly.
/// The vector to map is always this field, plus 32.
pub legacy_interrupt_line: u8,
/// Legacy interrupt pin (INTx#), none if INTx# interrupts aren't supported at all.
@@ -51,6 +55,11 @@ pub struct PciFunction {
/// Device ID
pub devid: u16,
}
impl PciFunction {
pub fn name(&self) -> String {
format!("pci-{:>02X}.{:>02X}.{:>02X}", self.bus_num, self.dev_num, self.func_num)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SubdriverArguments {
@@ -114,6 +123,48 @@ pub enum PcidClientHandleError {
}
pub type Result<T, E = PcidClientHandleError> = std::result::Result<T, E>;
// TODO: Remove these "features" and just go strait to the actual thing.
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct MsiSetFeatureInfo {
/// The Multi Message Enable field of the Message Control in the MSI Capability Structure,
/// is the log2 of the interrupt vectors, minus one. Can only be 0b000..=0b101.
pub multi_message_enable: Option<u8>,
/// The system-specific message address, must be DWORD aligned.
///
/// The message address contains things like the CPU that will be targeted, at least on
/// x86_64.
pub message_address: Option<u32>,
/// The upper 32 bits of the 64-bit message address. Not guaranteed to exist, and is
/// reserved on x86_64 (currently).
pub message_upper_address: Option<u32>,
/// The message data, containing the actual interrupt vector (lower 8 bits), etc.
///
/// The spec mentions that the lower N bits can be modified, where N is the multi message
/// enable, which means that the vector set here has to be aligned to that number, and that
/// all vectors in that range have to be allocated.
pub message_data: Option<u16>,
/// A bitmap of the vectors that are masked. This field is not guaranteed (and not likely,
/// at least according to the feature flags I got from QEMU), to exist.
pub mask_bits: Option<u32>,
}
/// Some flags that might be set simultaneously, but separately.
#[derive(Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum SetFeatureInfo {
Msi(MsiSetFeatureInfo),
MsiX {
/// Masks the entire function, and all of its vectors.
function_mask: Option<bool>,
},
}
#[derive(Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum PcidClientRequest {
@@ -122,12 +173,14 @@ pub enum PcidClientRequest {
EnableFeature(PciFeature),
FeatureStatus(PciFeature),
FeatureInfo(PciFeature),
SetFeatureInfo(SetFeatureInfo),
}
#[derive(Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum PcidServerResponseError {
NonexistentFeature(PciFeature),
InvalidBitPattern,
}
#[derive(Debug, Serialize, Deserialize)]
@@ -139,6 +192,7 @@ pub enum PcidClientResponse {
FeatureStatus(PciFeature, FeatureStatus),
Error(PcidServerResponseError),
FeatureInfo(PciFeature, PciFeatureInfo),
SetFeatureInfo(PciFeature),
}
// 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
@@ -226,4 +280,11 @@ impl PcidServerHandle {
other => Err(PcidClientHandleError::InvalidResponse(other)),
}
}
pub fn set_feature_info(&mut self, info: SetFeatureInfo) -> Result<()> {
self.send(&PcidClientRequest::SetFeatureInfo(info))?;
match self.recv()? {
PcidClientResponse::SetFeatureInfo(_) => Ok(()),
other => Err(PcidClientHandleError::InvalidResponse(other)),
}
}
}
+116 -19
View File
@@ -9,6 +9,9 @@ use std::{env, io, i64, thread};
use syscall::iopl;
use log::{error, info, warn, trace};
use redox_log::{OutputBuilder, RedoxLogger};
use crate::config::Config;
use crate::pci::{CfgAccess, Pci, PciIter, PciBar, PciBus, PciClass, PciDev, PciFunc, PciHeader, PciHeaderError, PciHeaderType};
use crate::pci::cap::Capability as PciCapability;
@@ -29,7 +32,7 @@ pub struct DriverHandler {
state: Arc<State>,
}
fn with_pci_func_raw<T, F: FnOnce(&PciFunc) -> T>(pci: &Pci, bus_num: u8, dev_num: u8, func_num: u8, function: F) -> T {
fn with_pci_func_raw<T, F: FnOnce(&PciFunc) -> T>(pci: &dyn CfgAccess, bus_num: u8, dev_num: u8, func_num: u8, function: F) -> T {
let bus = PciBus {
pci,
num: bus_num,
@@ -46,7 +49,7 @@ fn with_pci_func_raw<T, F: FnOnce(&PciFunc) -> T>(pci: &Pci, bus_num: u8, dev_nu
}
impl DriverHandler {
fn with_pci_func_raw<T, F: FnOnce(&PciFunc) -> T>(&self, function: F) -> T {
with_pci_func_raw(&self.state.pci, self.bus_num, self.dev_num, self.func_num, function)
with_pci_func_raw(self.state.preferred_cfg_access(), 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::*;
@@ -70,7 +73,7 @@ impl DriverHandler {
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| {
with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| {
capability.set_enabled(true);
capability.write_message_control(func, offset);
});
@@ -83,7 +86,7 @@ impl DriverHandler {
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| {
with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| {
capability.set_msix_enabled(true);
capability.write_a(func, offset);
});
@@ -115,6 +118,56 @@ impl DriverHandler {
return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature));
}
}),
PcidClientRequest::SetFeatureInfo(info_to_set) => match info_to_set {
SetFeatureInfo::Msi(info_to_set) => if let Some((offset, info)) = self.capabilities.iter_mut().find_map(|(offset, capability)| Some((*offset, capability.as_msi_mut()?))) {
if let Some(mme) = info_to_set.multi_message_enable {
if info.multi_message_capable() < mme || mme > 0b101 {
return PcidClientResponse::Error(PcidServerResponseError::InvalidBitPattern);
}
info.set_multi_message_enable(mme);
}
if let Some(message_addr) = info_to_set.message_address {
if message_addr & 0b11 != 0 {
return PcidClientResponse::Error(PcidServerResponseError::InvalidBitPattern);
}
info.set_message_address(message_addr);
}
if let Some(message_addr_upper) = info_to_set.message_upper_address {
info.set_message_upper_address(message_addr_upper);
}
if let Some(message_data) = info_to_set.message_data {
if message_data & ((1 << info.multi_message_enable()) - 1) != 0 {
return PcidClientResponse::Error(PcidServerResponseError::InvalidBitPattern);
}
info.set_message_data(message_data);
}
if let Some(mask_bits) = info_to_set.mask_bits {
info.set_mask_bits(mask_bits);
}
unsafe {
with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| {
info.write_all(func, offset);
});
}
PcidClientResponse::SetFeatureInfo(PciFeature::Msi)
} else {
return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(PciFeature::Msi));
}
SetFeatureInfo::MsiX { function_mask } => if let Some((offset, info)) = self.capabilities.iter_mut().find_map(|(offset, capability)| Some((*offset, capability.as_msix_mut()?))) {
if let Some(mask) = function_mask {
info.set_function_mask(mask);
unsafe {
with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| {
info.write_a(func, offset);
});
}
}
PcidClientResponse::SetFeatureInfo(PciFeature::MsiX)
} else {
return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(PciFeature::MsiX));
}
}
}
}
fn handle_spawn(mut self, pcid_to_client_write: Option<usize>, pcid_from_client_read: Option<usize>, args: driver_interface::SubdriverArguments) {
@@ -139,13 +192,15 @@ pub struct State {
}
impl State {
fn preferred_cfg_access(&self) -> &dyn CfgAccess {
self.pcie.as_ref().map(|pcie| pcie as &dyn CfgAccess).unwrap_or(&*self.pci as &dyn CfgAccess)
// TODO
//self.pcie.as_ref().map(|pcie| pcie as &dyn CfgAccess).unwrap_or(&*self.pci as &dyn CfgAccess)
&*self.pci as &dyn CfgAccess
}
}
fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
dev_num: u8, func_num: u8, header: PciHeader) {
let pci = &state.pci;
let pci = state.preferred_cfg_access();
let raw_class: u8 = header.class().into();
let mut string = format!("PCI {:>02X}/{:>02X}/{:>02X} {:>04X}:{:>04X} {:>02X}.{:>02X}.{:>02X}.{:>02X} {:?}",
@@ -193,7 +248,7 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
string.push('\n');
print!("{}", string);
info!("{}", string);
for driver in config.drivers.iter() {
if let Some(class) = driver.class {
@@ -312,7 +367,7 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
};
crate::pci::cap::CapabilitiesIter { inner: crate::pci::cap::CapabilityOffsetsIter::new(header.cap_pointer(), &func) }.collect::<Vec<_>>()
};
println!("PCI DEVICE CAPABILITIES for {}: {:?}", args.iter().map(|string| string.as_ref()).nth(0).unwrap_or("[unknown]"), capabilities);
info!("PCI DEVICE CAPABILITIES for {}: {:?}", args.iter().map(|string| string.as_ref()).nth(0).unwrap_or("[unknown]"), capabilities);
use driver_interface::LegacyInterruptPin;
@@ -324,7 +379,7 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
4 => Some(LegacyInterruptPin::IntD),
other => {
println!("pcid: invalid interrupt pin: {}", other);
warn!("pcid: invalid interrupt pin: {}", other);
None
}
};
@@ -353,7 +408,7 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
"$BUS" => format!("{:>02X}", bus_num),
"$DEV" => format!("{:>02X}", dev_num),
"$FUNC" => format!("{:>02X}", func_num),
"$NAME" => format!("pci-{:>02X}.{:>02X}.{:>02X}", bus_num, dev_num, func_num),
"$NAME" => func.name(),
"$BAR0" => format!("{}", bars[0]),
"$BAR1" => format!("{}", bars[1]),
"$BAR2" => format!("{}", bars[2]),
@@ -374,9 +429,9 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
command.arg(&arg);
}
println!("PCID SPAWN {:?}", command);
info!("PCID SPAWN {:?}", command);
let (pcid_to_client_write, pcid_from_client_read, envs) = if driver.channel_name.is_some() {
let (pcid_to_client_write, pcid_from_client_read, envs) = if driver.use_channel.unwrap_or(false) {
let mut fds1 = [0usize; 2];
let mut fds2 = [0usize; 2];
@@ -407,16 +462,56 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
});
match child.wait() {
Ok(_status) => (),
Err(err) => println!("pcid: failed to wait for {:?}: {}", command, err),
Err(err) => error!("pcid: failed to wait for {:?}: {}", command, err),
}
}
Err(err) => println!("pcid: failed to execute {:?}: {}", command, err)
Err(err) => error!("pcid: failed to execute {:?}: {}", command, err)
}
}
}
}
}
fn setup_logging() -> Option<&'static RedoxLogger> {
let mut logger = RedoxLogger::new()
.with_output(
OutputBuilder::stderr()
.with_ansi_escape_codes()
.with_filter(log::LevelFilter::Info)
.flush_on_newline(true)
.build()
);
match OutputBuilder::in_redox_logging_scheme("bus", "pci", "pcid.log") {
Ok(b) => logger = logger.with_output(
b.with_filter(log::LevelFilter::Trace)
.flush_on_newline(true)
.build()
),
Err(error) => eprintln!("pcid: failed to open pcid.log"),
}
match OutputBuilder::in_redox_logging_scheme("bus", "pci", "pcid.ansi.log") {
Ok(b) => logger = logger.with_output(
b.with_filter(log::LevelFilter::Trace)
.with_ansi_escape_codes()
.flush_on_newline(true)
.build()
),
Err(error) => eprintln!("pcid: failed to open pcid.ansi.log"),
}
match logger.enable() {
Ok(logger_ref) => {
eprintln!("pcid: enabled logger");
Some(logger_ref)
}
Err(error) => {
eprintln!("pcid: failed to set default logger: {}", error);
None
}
}
}
fn main() {
let mut config = Config::default();
@@ -447,6 +542,8 @@ fn main() {
}
}
let _logger_ref = setup_logging();
let pci = Arc::new(Pci::new());
let state = Arc::new(State {
@@ -454,7 +551,7 @@ fn main() {
pcie: match Pcie::new(Arc::clone(&pci)) {
Ok(pcie) => Some(pcie),
Err(error) => {
println!("Couldn't retrieve PCIe info, perhaps the kernel is not compiled with acpi? Using the PCI 3.0 configuration space instead. Error: {:?}", error);
info!("Couldn't retrieve PCIe info, perhaps the kernel is not compiled with acpi? Using the PCI 3.0 configuration space instead. Error: {:?}", error);
None
}
},
@@ -463,7 +560,7 @@ fn main() {
let pci = state.preferred_cfg_access();
print!("PCI BS/DV/FN VEND:DEVI CL.SC.IN.RV\n");
info!("PCI BS/DV/FN VEND:DEVI CL.SC.IN.RV");
'bus: for bus in PciIter::new(pci) {
'dev: for dev in bus.devs() {
@@ -476,16 +573,16 @@ fn main() {
Err(PciHeaderError::NoDevice) => {
if func_num == 0 {
if dev.num == 0 {
// println!("PCI {:>02X}: no bus", bus.num);
trace!("PCI {:>02X}: no bus", bus.num);
continue 'bus;
} else {
// println!("PCI {:>02X}/{:>02X}: no dev", bus.num, dev.num);
trace!("PCI {:>02X}/{:>02X}: no dev", bus.num, dev.num);
continue 'dev;
}
}
},
Err(PciHeaderError::UnknownHeaderType(id)) => {
println!("pcid: unknown header type: {}", id);
warn!("pcid: unknown header type: {}", id);
}
}
}
+8 -4
View File
@@ -25,7 +25,7 @@ where
if self.offset == 0 { return None };
let first_dword = dbg!(self.reader.read_u32(dbg!(u16::from(self.offset))));
let first_dword = self.reader.read_u32(u16::from(self.offset));
let next = ((first_dword >> 8) & 0xFF) as u8;
let offset = self.offset;
@@ -41,6 +41,7 @@ pub enum CapabilityId {
Msi = 0x05,
MsiX = 0x11,
Pcie = 0x10,
Sata = 0x12, // only on AHCI functions
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
@@ -48,13 +49,13 @@ pub enum MsiCapability {
_32BitAddress {
message_control: u32,
message_address: u32,
message_data: u32,
message_data: u16,
},
_64BitAddress {
message_control: u32,
message_address_lo: u32,
message_address_hi: u32,
message_data: u32,
message_data: u16,
},
_32BitAddressWithPvm {
message_control: u32,
@@ -85,11 +86,12 @@ pub struct MsixCapability {
pub c: u32,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Capability {
Msi(MsiCapability),
MsiX(MsixCapability),
Pcie(PcieCapability),
FunctionSpecific(u8, Vec<u8>), // TODO: Arrayvec
Other(u8),
}
@@ -156,6 +158,8 @@ impl Capability {
Self::parse_msix(reader, offset)
} else if capability_id == CapabilityId::Pcie as u8 {
Self::parse_pcie(reader, offset)
} else if capability_id == CapabilityId::Sata as u8 {
Self::FunctionSpecific(capability_id, reader.read_range(offset.into(), 8))
} else {
Self::Other(capability_id)
//panic!("unimplemented or malformed capability id: {}", capability_id)
+3 -1
View File
@@ -8,6 +8,8 @@ pub use self::dev::{PciDev, PciDevIter};
pub use self::func::PciFunc;
pub use self::header::{PciHeader, PciHeaderError, PciHeaderType};
use log::info;
mod bar;
mod bus;
pub mod cap;
@@ -45,7 +47,7 @@ impl Pci {
fn set_iopl() {
// make sure that pcid is not granted io port permission unless pcie memory-mapped
// configuration space is not available.
println!("PCI: couldn't find or access PCIe extended configuration, and thus falling back to PCI 3.0 io ports");
info!("PCI: couldn't find or access PCIe extended configuration, and thus falling back to PCI 3.0 io ports");
unsafe { syscall::iopl(3).expect("pcid: failed to set iopl to 3"); }
}
fn address(bus: u8, dev: u8, func: u8, offset: u8) -> u32 {
+112 -9
View File
@@ -48,13 +48,13 @@ impl MsiCapability {
message_control: dword,
message_address_lo: reader.read_u32(u16::from(offset + 4)),
message_address_hi: reader.read_u32(u16::from(offset + 8)),
message_data: reader.read_u32(u16::from(offset + 12)),
message_data: reader.read_u32(u16::from(offset + 12)) as u16,
}
} else {
Self::_32BitAddress {
message_control: dword,
message_address: reader.read_u32(u16::from(offset + 4)),
message_data: reader.read_u32(u16::from(offset + 8)),
message_data: reader.read_u32(u16::from(offset + 8)) as u16,
}
}
}
@@ -80,7 +80,7 @@ impl MsiCapability {
| Self::_64BitAddressWithPvm { ref mut message_control, .. } => *message_control = new_message_control,
}
}
pub unsafe fn write_message_control<W: ConfigWriter>(&mut self, writer: &W, offset: u8) {
pub unsafe fn write_message_control<W: ConfigWriter>(&self, writer: &W, offset: u8) {
writer.write_u32(u16::from(offset), self.message_control_raw());
}
pub fn is_pvt_capable(&self) -> bool {
@@ -100,14 +100,106 @@ impl MsiCapability {
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 {
pub fn multi_message_enable(&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) {
pub fn set_multi_message_enable(&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);
}
pub fn message_address(&self) -> u32 {
match self {
&Self::_32BitAddress { message_address, .. } | &Self::_32BitAddressWithPvm { message_address, .. } => message_address,
&Self::_64BitAddress { message_address_lo, .. } | &Self::_64BitAddressWithPvm { message_address_lo, .. } => message_address_lo,
}
}
pub fn message_upper_address(&self) -> Option<u32> {
match self {
&Self::_64BitAddress { message_address_hi, .. } | &Self::_64BitAddressWithPvm { message_address_hi, .. } => Some(message_address_hi),
&Self::_32BitAddress { .. } | &Self::_32BitAddressWithPvm { .. } => None,
}
}
pub fn message_data(&self) -> u16 {
match self {
&Self::_32BitAddress { message_data, .. } | &Self::_64BitAddress { message_data, .. } => message_data,
&Self::_32BitAddressWithPvm { message_data, .. } | &Self::_64BitAddressWithPvm { message_data, .. } => message_data as u16,
}
}
pub fn mask_bits(&self) -> Option<u32> {
match self {
&Self::_32BitAddressWithPvm { mask_bits, .. } | &Self::_64BitAddressWithPvm { mask_bits, .. } => Some(mask_bits),
&Self::_32BitAddress { .. } | &Self::_64BitAddress { .. } => None,
}
}
pub fn pending_bits(&self) -> Option<u32> {
match self {
&Self::_32BitAddressWithPvm { pending_bits, .. } | &Self::_64BitAddressWithPvm { pending_bits, .. } => Some(pending_bits),
&Self::_32BitAddress { .. } | &Self::_64BitAddress { .. } => None,
}
}
pub fn set_message_address(&mut self, message_address: u32) {
assert_eq!(message_address & 0xFFFF_FFFC, message_address, "unaligned message address (this should already be validated)");
match self {
&mut Self::_32BitAddress { message_address: ref mut addr, .. } | &mut Self::_32BitAddressWithPvm { message_address: ref mut addr, .. } => *addr = message_address,
&mut Self::_64BitAddress { message_address_lo: ref mut addr, .. } | &mut Self::_64BitAddressWithPvm { message_address_lo: ref mut addr, .. } => *addr = message_address,
}
}
pub fn set_message_upper_address(&mut self, message_upper_address: u32) -> Option<()> {
match self {
&mut Self::_64BitAddress { ref mut message_address_hi, .. } | &mut Self::_64BitAddressWithPvm { ref mut message_address_hi, .. } => *message_address_hi = message_upper_address,
&mut Self::_32BitAddress { .. } | &mut Self::_32BitAddressWithPvm { .. } => return None,
}
Some(())
}
pub fn set_message_data(&mut self, value: u16) {
match self {
&mut Self::_32BitAddress { ref mut message_data, .. } | &mut Self::_64BitAddress { ref mut message_data, .. } => *message_data = value,
&mut Self::_32BitAddressWithPvm { ref mut message_data, .. } | &mut Self::_64BitAddressWithPvm { ref mut message_data, .. } => {
*message_data &= 0xFFFF_0000;
*message_data |= u32::from(value);
}
}
}
pub fn set_mask_bits(&mut self, mask_bits: u32) -> Option<()> {
match self {
&mut Self::_32BitAddressWithPvm { mask_bits: ref mut bits, .. } | &mut Self::_64BitAddressWithPvm { mask_bits: ref mut bits, .. } => *bits = mask_bits,
&mut Self::_32BitAddress { .. } | &mut Self::_64BitAddress { .. } => return None,
}
Some(())
}
pub unsafe fn write_message_address<W: ConfigWriter>(&self, writer: &W, offset: u8) {
writer.write_u32(u16::from(offset) + 4, self.message_address())
}
pub unsafe fn write_message_upper_address<W: ConfigWriter>(&self, writer: &W, offset: u8) -> Option<()> {
let value = self.message_upper_address()?;
writer.write_u32(u16::from(offset + 8), value);
Some(())
}
pub unsafe fn write_message_data<W: ConfigWriter>(&self, writer: &W, offset: u8) {
match self {
&Self::_32BitAddress { message_data, .. } => writer.write_u32(u16::from(offset + 8), message_data.into()),
&Self::_32BitAddressWithPvm { message_data, .. } => writer.write_u32(u16::from(offset + 8), message_data),
&Self::_64BitAddress { message_data, .. } => writer.write_u32(u16::from(offset + 12), message_data.into()),
&Self::_64BitAddressWithPvm { message_data, .. } => writer.write_u32(u16::from(offset + 12), message_data),
}
}
pub unsafe fn write_mask_bits<W: ConfigWriter>(&self, writer: &W, offset: u8) -> Option<()> {
match self {
&Self::_32BitAddressWithPvm { mask_bits, .. } => writer.write_u32(u16::from(offset + 12), mask_bits),
&Self::_64BitAddressWithPvm { mask_bits, .. } => writer.write_u32(u16::from(offset + 16), mask_bits),
&Self::_32BitAddress { .. } | &Self::_64BitAddress { .. } => return None,
}
Some(())
}
pub unsafe fn write_all<W: ConfigWriter>(&self, writer: &W, offset: u8) {
self.write_message_control(writer, offset);
self.write_message_address(writer, offset);
self.write_message_upper_address(writer, offset);
self.write_message_data(writer, offset);
self.write_mask_bits(writer, offset);
}
}
impl MsixCapability {
@@ -293,12 +385,11 @@ pub mod x86_64 {
}
// TODO: should the reserved field be preserved?
pub const fn message_address(destination_id: u8, rh: bool, dm: bool, xx: u8) -> u32 {
pub const fn message_address(destination_id: u8, rh: bool, dm: bool) -> 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)
@@ -321,22 +412,34 @@ impl MsixTableEntry {
pub fn addr_hi(&self) -> u32 {
self.addr_hi.read()
}
pub fn set_addr_lo(&mut self, value: u32) {
self.addr_lo.write(value);
}
pub fn set_addr_hi(&mut self, value: u32) {
self.addr_hi.write(value);
}
pub fn msg_data(&self) -> u32 {
self.msg_data.read()
}
pub fn vec_ctl(&self) -> u32 {
self.vec_ctl.read()
}
pub fn set_msg_data(&mut self, value: u32) {
self.msg_data.write(value);
}
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 set_masked(&mut self, masked: bool) {
self.vec_ctl.writef(Self::VEC_CTL_MASK_BIT, masked)
}
pub fn mask(&mut self) {
self.vec_ctl.writef(Self::VEC_CTL_MASK_BIT, true)
self.set_masked(true);
}
pub fn unmask(&mut self) {
self.vec_ctl.writef(Self::VEC_CTL_MASK_BIT, false)
self.set_masked(false);
}
}
+2 -1
View File
@@ -20,11 +20,12 @@ plain = "0.2"
lazy_static = "1.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-log = { git = "https://gitlab.redox-os.org/redox-os/redox-log.git", tag = "v0.1.0" }
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" }
+88 -77
View File
@@ -1,7 +1,7 @@
#[macro_use]
extern crate bitflags;
use std::convert::TryInto;
use std::convert::{TryFrom, TryInto};
use std::fs::{self, File};
use std::future::Future;
use std::io::{self, Read, Write};
@@ -11,11 +11,13 @@ use std::ptr::NonNull;
use std::sync::{Arc, Mutex};
use std::env;
use pcid_interface::{PcidServerHandle, PciFeature, PciFeatureInfo};
use pcid_interface::{MsiSetFeatureInfo, PcidServerHandle, PciFeature, PciFeatureInfo, SetFeatureInfo};
use pcid_interface::irq_helpers::{read_bsp_apic_id, allocate_single_interrupt_vector};
use pcid_interface::msi::{MsiCapability, MsixCapability, MsixTableEntry};
use event::{Event, EventQueue};
use log::info;
use redox_log::{RedoxLogger, OutputBuilder};
use syscall::data::Packet;
use syscall::error::EWOULDBLOCK;
use syscall::flag::{CloneFlags, PHYSMAP_NO_CACHE, PHYSMAP_WRITE};
@@ -32,74 +34,61 @@ pub mod driver_interface;
mod usb;
mod xhci;
/// Read the local APIC id of the bootstrap processor.
fn read_bsp_apic_id() -> io::Result<u32> {
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::<usize>());
})
}
/// Allocate an interrupt vector, located at the BSP's IDT.
fn allocate_interrupt_vector() -> io::Result<Option<(u8, File)>> {
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::<u8>() {
// 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<Xhci>, packet: Packet) -> Packet {
todo!()
}
fn setup_logging() -> Option<&'static RedoxLogger> {
let mut logger = RedoxLogger::new()
.with_output(
OutputBuilder::stderr()
.with_filter(log::LevelFilter::Info) // limit global output to important info
.with_ansi_escape_codes()
.flush_on_newline(true)
.build()
);
#[cfg(target_os = "redox")]
match OutputBuilder::in_redox_logging_scheme("usb", "host", "xhci.log") {
Ok(b) => logger = logger.with_output(
// TODO: Add a configuration file for this
b.with_filter(log::LevelFilter::Trace)
.flush_on_newline(true)
.build()
),
Err(error) => eprintln!("Failed to create xhci.log: {}", error),
}
#[cfg(target_os = "redox")]
match OutputBuilder::in_redox_logging_scheme("usb", "host", "xhci.ansi.log") {
Ok(b) => logger = logger.with_output(
b.with_filter(log::LevelFilter::Trace)
.with_ansi_escape_codes()
.flush_on_newline(true)
.build()
),
Err(error) => eprintln!("Failed to create xhci.ansi.log: {}", error),
}
match logger.enable() {
Ok(logger_ref) => {
eprintln!("xhcid: enabled logger");
Some(logger_ref)
}
Err(error) => {
eprintln!("xhcid: failed to set default logger: {}", error);
None
}
}
}
fn main() {
let mut args = env::args().skip(1);
let mut name = args.next().expect("xhcid: no name provided");
name.push_str("_xhci");
// Daemonize
if unsafe { syscall::clone(CloneFlags::empty()).unwrap() } != 0 {
return;
}
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 _logger_ref = setup_logging();
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");
@@ -108,6 +97,9 @@ fn main() {
let bar = pci_config.func.bars[0];
let irq = pci_config.func.legacy_interrupt_line;
let mut name = pci_config.func.name();
name.push_str("_xhci");
let bar_ptr = match bar {
pcid_interface::PciBar::Memory(ptr) => ptr,
other => panic!("Expected memory bar, found {}", other),
@@ -124,29 +116,45 @@ fn main() {
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 {
use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix};
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: Allow allocation of up to 32 vectors.
todo!("msi (msix is implemented though)")
// TODO: Find a way to abstract this away, potantially as a helper module for
// pcid_interface, so that this can be shared between nvmed, xhcid, ixgebd, etc..
let destination_id = read_bsp_apic_id().expect("xhcid: failed to read BSP apic id");
let lapic_id = u8::try_from(destination_id).expect("CPU id didn't fit inside u8");
let msg_addr = x86_64_msix::message_address(lapic_id, false, false);
let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id).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);
let set_feature_info = MsiSetFeatureInfo {
multi_message_enable: Some(0),
message_address: Some(msg_addr),
message_upper_address: Some(0),
message_data: Some(msg_data as u16),
mask_bits: None,
};
pcid_handle.set_feature_info(SetFeatureInfo::Msi(set_feature_info)).expect("xhcid: failed to set feature info");
pcid_handle.enable_feature(PciFeature::Msi).expect("xhcid: failed to enable MSI");
info!("Enabled MSI");
(Some(interrupt_handle), InterruptMethod::Msi)
} 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!(),
@@ -158,7 +166,6 @@ fn main() {
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!()
@@ -178,7 +185,7 @@ fn main() {
// Allocate one msi vector.
{
let method = {
use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix};
// primary interrupter
@@ -188,11 +195,12 @@ fn main() {
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 lapic_id = u8::try_from(destination_id).expect("xhcid: CPU id couldn't fit inside u8");
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 addr = x86_64_msix::message_address(lapic_id, rh, dm);
let (vector, interrupt_handle) = allocate_interrupt_vector().expect("xhcid: failed to allocate interrupt vector").expect("xhcid: no interrupt vectors left");
let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id).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);
@@ -201,7 +209,12 @@ fn main() {
table_entry_pointer.vec_ctl.writef(MsixTableEntry::VEC_CTL_MASK_BIT, false);
(Some(interrupt_handle), InterruptMethod::MsiX(Mutex::new(info)))
}
};
pcid_handle.enable_feature(PciFeature::MsiX).expect("xhcid: failed to enable MSI-X");
info!("Enabled MSI-X");
method
} 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)
@@ -210,8 +223,6 @@ fn main() {
(None, InterruptMethod::Polling)
};
std::thread::sleep(std::time::Duration::from_millis(300));
print!(
"{}",
format!(" + XHCI {} on: {} IRQ: {}\n", name, bar, irq)
+1 -2
View File
@@ -212,7 +212,7 @@ impl IrqReactor {
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) {
StateKind::CommandCompletion { phys_ptr } if trb.trb_type() == TrbType::CommandCompletion as u8 => if trb.completion_trb_pointer() == Some(phys_ptr) {
trace!("Found matching command completion future");
let state = self.states.remove(index);
@@ -430,7 +430,6 @@ impl Xhci {
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).