diff --git a/.cargo/config b/.cargo/config index d7f342049bba2c9c7dc247787d0e505f5d3eea70..cb43f23d189459c5e891e399f093b97fc616453f 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,3 +1,3 @@ [build] -target = "aarch64.json" +target = "cfg/aarch64.json" rustflags = ["-C", "llvm-args=-global-isel=false"] diff --git a/.gitignore b/.gitignore index c52220019f4907949606b861a3aec0baf95204ac..e22f20ea5fc83161068d1001209ec688cd3515e1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,14 +9,15 @@ # These are backup files generated by rustfmt **/*.rs.bk -run* /.vscode -upload_release -upload -upload_update .idea build/ -pi4_upload_release *.img *.dts -/*.dtb +*.dtb +/*.ld +/kvm +/rk3588_devcie_tree +/vm1* +/image/Image* +*.patch \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..c371970d7a092253315592d6eff64e008f43b0b5 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,40 @@ +stages: + - style + - build + +default: + cache: + key: $CI_COMMIT_REF_SLUG + paths: + - target + +variables: + CROSS_COMPILE: aarch64-linux-gnu- + RUST_BACKTRACE: full + CARGO_TERM_COLOR: always + RUSTFLAGS: -Dwarnings + +fmt: + stage: style + script: + - cargo fmt -v --check --all + +build: + stage: build + parallel: + matrix: + - BOARD: qemu + FEATURES: + - "" + - gicv3 + - secondary_start + - gicv3,secondary_start + - BOARD: [tx2, pi4] + - BOARD: rk3588 + FEATURES: + - "" + - rk3588-noeth + script: + - make clippy + - make doc + - make diff --git a/Cargo.lock b/Cargo.lock index 61a4b4159b7fee7c1ad0fb9a57ea1ac6f6bfbb1a..e214f709208e8c2d54daae1ca85ab0e824d6698a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,32 +3,36 @@ version = 3 [[package]] -name = "aho-corasick" -version = "1.0.2" +name = "aarch64-cpu" +version = "9.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "ac42a04a61c19fc8196dd728022a784baecc5d63d7e256c01ad1b3fbfab26287" dependencies = [ - "memchr", + "tock-registers", ] [[package]] -name = "ansi_term" -version = "0.12.1" +name = "aho-corasick" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ - "winapi", + "memchr", ] [[package]] -name = "atty" -version = "0.2.14" +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "hermit-abi", "libc", - "winapi", ] [[package]] @@ -39,32 +43,32 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bindgen" -version = "0.59.2" +version = "0.69.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2" dependencies = [ "bitflags", "cexpr", "clang-sys", - "clap", - "env_logger", "lazy_static", "lazycell", "log", "peeking_take_while", + "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", + "syn 2.0.39", "which", ] [[package]] name = "bitflags" -version = "1.3.2" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "buddy_system_allocator" @@ -75,6 +79,33 @@ dependencies = [ "spin 0.7.1", ] +[[package]] +name = "built" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99c4cdc7b2c2364182331055623bdf45254fcb679fea565c40c3c11c101889a" +dependencies = [ + "cargo-lock", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "cargo-lock" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11c675378efb449ed3ce8de78d75d0d80542fc98487c26aba28eb3b82feac72" +dependencies = [ + "semver 1.0.20", + "serde", + "toml", + "url", +] + [[package]] name = "cc" version = "1.0.83" @@ -99,6 +130,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + [[package]] name = "clang-sys" version = "1.6.1" @@ -111,28 +156,10 @@ dependencies = [ ] [[package]] -name = "clap" -version = "2.34.0" +name = "core-foundation-sys" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "cortex-a" -version = "7.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdecfbb28672ad3664e71ae05a398a52df430d86d660691501b28968cc4467e6" -dependencies = [ - "tock-registers", -] +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cstr_core" @@ -157,26 +184,77 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] -name = "env_logger" -version = "0.9.3" +name = "endian-type-rs" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +checksum = "b6419a5c75e40011b9fe0174db3fe24006ab122fbe1b7e9cc5974b338a755c76" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", + "libc", + "windows-sys", ] +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "fdt" version = "0.1.0" +source = "git+https://github.com/karin0/libfdt-rs#4c8cfe54f37e80d2023f758ed976906cb9542248" dependencies = [ "bindgen", "cc", ] +[[package]] +name = "fdt-rs" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99a40cabc11c8258822a593f5c51f2d9f4923e715ca9e2a0630cf77ae15f390b" +dependencies = [ + "endian-type-rs", + "fallible-iterator", + "memoffset 0.5.6", + "num-derive", + "num-traits", + "rustc_version", + "static_assertions", + "unsafe_unwrap", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets", +] + [[package]] name = "glob" version = "0.3.1" @@ -184,19 +262,71 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "hashbrown" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "libc", + "windows-sys", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", ] [[package]] -name = "humantime" +name = "indexmap" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "js-sys" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +dependencies = [ + "wasm-bindgen", +] [[package]] name = "lazy_static" @@ -212,9 +342,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" @@ -226,11 +356,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" + [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -238,15 +374,33 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "memoffset" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] [[package]] name = "minimal-lexical" @@ -264,6 +418,26 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -276,29 +450,45 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "prettyplease" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2", + "syn 2.0.39", +] + [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.9.3" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", @@ -308,9 +498,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.6" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", @@ -319,18 +509,25 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rust_shyper" version = "0.1.0" dependencies = [ + "aarch64-cpu", "buddy_system_allocator", - "cortex-a", + "built", + "cfg-if", + "chrono", "fdt", + "fdt-rs", + "gethostname", "log", + "memoffset 0.8.0", + "smccc", "spin 0.9.8", "tock-registers", "vm-fdt", @@ -342,17 +539,98 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustix" +version = "0.38.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +dependencies = [ + "serde", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "serde_spanned" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +dependencies = [ + "serde", +] + [[package]] name = "shlex" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + +[[package]] +name = "smccc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617d17f088ec733e5a6b86da6ce4cce1414e6e856d6061c16dda51cceae6f68c" [[package]] name = "spin" @@ -370,52 +648,125 @@ dependencies = [ ] [[package]] -name = "strsim" -version = "0.8.0" +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] -name = "termcolor" -version = "1.2.0" +name = "syn" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "winapi-util", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "textwrap" -version = "0.11.0" +name = "syn" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ - "unicode-width", + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", ] +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tock-registers" -version = "0.7.0" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee8fba06c1f4d0b396ef61a54530bb6b28f0dc61c38bc8bc5a5a48161e6282e" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] -name = "unicode-width" -version = "0.1.10" +name = "unicode-normalization" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] [[package]] -name = "vec_map" -version = "0.8.2" +name = "unsafe_unwrap" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "1230ec65f13e0f9b28d789da20d2d419511893ea9dac2c1f4ef67b8b14e5da80" + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] [[package]] name = "vm-fdt" @@ -425,15 +776,70 @@ dependencies = [ "cstr_core", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" + [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix", ] [[package]] @@ -453,16 +859,91 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] -name = "winapi-util" -version = "0.1.5" +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "winapi", + "windows-targets", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows-sys" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml index 3471063fd44c91b3b21aaf20fb89e259d47849f4..8bac4354de6580de34ec4f46e7f890b867c4bb41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,11 @@ license = "MulanPSL-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +crate-type = ["staticlib"] +test = false +bench = false + [profile.dev] panic = "abort" @@ -23,26 +28,50 @@ lto = true panic = "abort" debug = true +[build-dependencies] +chrono = "0.4.23" +built = { version = "0.6.1", features = [] } +gethostname = "0.4.3" + [dependencies] vm-fdt = { git = "https://github.com/OhmR/vm-fdt" } -fdt = { path = "libfdt-binding" } +fdt = { git = "https://github.com/karin0/libfdt-rs" } log = { version = "0.4", features = [ "max_level_trace", "release_max_level_info", ] } spin = { version = "0.9.4", features = ["use_ticket_mutex"] } -cortex-a = "7.4.0" buddy_system_allocator = "0.8.0" +memoffset = { version = "0.8", default-features = false, features = [ + "unstable_const", +] } +cfg-if = "1.0.0" + +[target.'cfg(target_arch = "aarch64")'.dependencies] +cortex-a = { package = "aarch64-cpu", version = "9.3.1" } +smccc = "0.1.1" [dependencies.tock-registers] -version = "0.7.0" +version = "0.8.0" default-features = false features = ["register_types"] +[dependencies.fdt-rs] +version = "x" +default-features = false + [features] -tx2 = [] -qemu = [] -pi4 = [] -update = [] +tx2 = ["ns16550"] +qemu = ["pl011"] +pi4 = ["pl011"] +rk3588 = ["ns16550", "gicv3"] +gicv3 = [] ramdisk = [] static-config = [] +lvl4 = [] +secondary_start = [] +rk3588-noeth = ["rk3588"] +ns16550 = [] +pl011 = [] +unilib = [] +doc = [] diff --git a/Makefile b/Makefile index a3c2bad47e247635b59b775d6dca97f3687f2607..69cc07c54f85278d79c1e551e20cd7da72242606 100644 --- a/Makefile +++ b/Makefile @@ -7,59 +7,119 @@ ARCH ?= aarch64 PROFILE ?= release BOARD ?= tx2 # features, seperate with comma `,` -FEATURES = +FEATURES ?= # Toolchain -TOOLCHAIN=aarch64-none-elf -QEMU = qemu-system-aarch64 -GDB = ${TOOLCHAIN}-gdb -OBJDUMP = ${TOOLCHAIN}-objdump -OBJCOPY = ${TOOLCHAIN}-objcopy +ifeq ($(ARCH), aarch64) + CROSS_COMPILE ?= aarch64-none-elf- +else ifeq ($(ARCH), riscv64) + CROSS_COMPILE ?= riscv64-linux-gnu- +else +$(error bad arch: $(ARCH)) +endif + +QEMU := qemu-system-$(ARCH) + +GDB = ${CROSS_COMPILE}gdb +OBJDUMP = ${CROSS_COMPILE}objdump +OBJCOPY = ${CROSS_COMPILE}objcopy +LD = ${CROSS_COMPILE}ld + +GIC_VERSION ?= 2 + +ifeq ($(GIC_VERSION),3) +override FEATURES += ,gicv3, +else ifneq ($(GIC_VERSION),2) +$(error Bad gic version) +endif + +TEXT_START ?= 0x83000000 +VM0_IMAGE_PATH ?= "./image/L4T" +RELOCATE_IMAGE=librust_shyper.a IMAGE=rust_shyper TARGET_DIR=target/${ARCH}/${PROFILE} -# Cargo flags. -CARGO_FLAGS ?= -Z build-std=core,alloc --target ${ARCH}.json --no-default-features --features ${BOARD},${FEATURES} +# Use target_cfg depending on ARCH +TARGET_CFG := $(CURDIR)/cfg/${ARCH}.json + +# Combine board(tx2, qemu, pi4, ...) with previous features as cargo's features +CARGO_FLAGS ?= -Z build-std=core,alloc -Zbuild-std-features=compiler-builtins-mem --target ${TARGET_CFG} --no-default-features --features "${BOARD},${FEATURES}" ifeq (${PROFILE}, release) CARGO_FLAGS := ${CARGO_FLAGS} --release endif -.PHONY: build qemu tx2 pi4 tx2_update tx2_ramdisk gdb clean +# Make 'cc' crate in dependencies cross compiles properly. +CFLAGS += -fno-stack-protector + +ifeq ($(ARCH), aarch64) + CFLAGS += -mgeneral-regs-only +else ifeq ($(ARCH), riscv64) + export CRATE_CC_NO_DEFAULTS := true + CFLAGS += -ffunction-sections -fdata-sections \ + -fPIC -fno-omit-frame-pointer -mabi=lp64 -mcmodel=medany -march=rv64ima \ + -ffreestanding +endif + +export BOARD CROSS_COMPILE CFLAGS + +CARGO_ACTION ?= build + +TFTP_SERVER ?= root@192.168.106.153:/tftp + +UBOOT_IMAGE ?= Image$(USER)_$(ARCH)_$(BOARD) -build: - cargo build ${CARGO_FLAGS} +.PHONY: build doc cargo upload qemu rk3588 tx2 pi4 tx2_update tx2_ramdisk gdb clean clippy + +build: cargo + bash linkimg.sh -i ${TARGET_DIR}/${RELOCATE_IMAGE} -m ${VM0_IMAGE_PATH} \ + -t ${LD} -f linkers/${ARCH}.ld -s ${TEXT_START} -o ${TARGET_DIR}/${IMAGE} ${OBJDUMP} --demangle -d ${TARGET_DIR}/${IMAGE} > ${TARGET_DIR}/t.txt + ${OBJCOPY} ${TARGET_DIR}/${IMAGE} -O binary ${TARGET_DIR}/${IMAGE}.bin + +cargo: + cargo ${CARGO_ACTION} ${CARGO_FLAGS} + +# TODO: fix the mkimage ARCH because it only accept "arm64" and "AArch64" for aarch64 +upload: build + @mkimage -n ${IMAGE} -A arm64 -O linux -T kernel -C none -a $(TEXT_START) -e $(TEXT_START) -d ${TARGET_DIR}/${IMAGE}.bin ${TARGET_DIR}/${UBOOT_IMAGE} + @echo "*** Upload Image ${UBOOT_IMAGE} ***" + @scp ${TARGET_DIR}/${UBOOT_IMAGE} ${TFTP_SERVER}/${UBOOT_IMAGE} qemu: - $(MAKE) build BOARD=qemu - ${OBJCOPY} ${TARGET_DIR}/${IMAGE} -O binary ${TARGET_DIR}/${IMAGE}.bin + $(MAKE) build BOARD=qemu TEXT_START=0x40080000 VM0_IMAGE_PATH="./image/Image_vanilla" + +rk3588: + $(MAKE) upload BOARD=rk3588 TEXT_START=0x00480000 VM0_IMAGE_PATH="./image/Image-5.10.160" tx2: - $(MAKE) build BOARD=tx2 - # bash upload_release + $(MAKE) upload BOARD=tx2 TEXT_START=0x83000000 VM0_IMAGE_PATH="./image/L4T" tx2_ramdisk: - $(MAKE) build BOARD=tx2 FEATURES=ramdisk - # bash upload_release - -tx2_update: - $(MAKE) build BOARD=tx2 FEATURES=update - # bash upload_update + $(MAKE) upload BOARD=tx2 FEATURES=ramdisk TEXT_START=0x83000000 VM0_IMAGE_PATH="./image/L4T" pi4: - $(MAKE) build BOARD=pi4 - # bash pi4_upload_release + $(MAKE) upload BOARD=pi4 TEXT_START=0xf0080000 VM0_IMAGE_PATH="./image/Image_pi4_5.4.83_tlb" - -QEMU_COMMON_OPTIONS = -machine virt,virtualization=on,gic-version=2\ +ifeq (${ARCH}, aarch64) +QEMU_COMMON_OPTIONS = -machine virt,virtualization=on,gic-version=$(GIC_VERSION)\ -m 8g -cpu cortex-a57 -smp 4 -display none -global virtio-mmio.force-legacy=false\ -kernel ${TARGET_DIR}/${IMAGE}.bin +else ifeq (${ARCH}, riscv64) +QEMU_COMMON_OPTIONS = -machine virt,virtualization=on\ + -m 8g -cpu rv64 -smp 4 -display none -global virtio-mmio.force-legacy=false\ + -kernel ${TARGET_DIR}/${IMAGE}.bin +else +$(error bad qemu arch: $(ARCH)) +endif QEMU_SERIAL_OPTIONS = -serial mon:stdio #\ -serial telnet:localhost:12345,server +# QEMU_NETWORK_OPTIONS = -netdev tap,id=tap0,ifname=tap0,script=no,downscript=no -device virtio-net-device,bus=virtio-mmio-bus.24,netdev=tap0 +#/home/cwm/c-hyper/syberx-hypervisor/build/shyper_qemuv3.bin + QEMU_NETWORK_OPTIONS = -netdev user,id=n0,hostfwd=tcp::5555-:22 -device virtio-net-device,bus=virtio-mmio-bus.24,netdev=n0 QEMU_DISK_OPTIONS = -drive file=${DISK},if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.25 @@ -72,7 +132,13 @@ debug: qemu -s -S gdb: - ${GDB} -x gdb/${ARCH}.gdb + ${GDB} -x gdb/$(ARCH).gdb clean: cargo clean + +clippy: CARGO_ACTION = clippy +clippy: cargo + +doc: + $(MAKE) cargo CARGO_ACTION=doc FEATURES=doc,$(FEATURES) diff --git a/README.ch.md b/README.ch.md index 71283e12b152f44cd919a0c5f28ea7ff76fc73f0..494647b4cac004af01be914814d5b52562116642 100644 --- a/README.ch.md +++ b/README.ch.md @@ -38,6 +38,10 @@ make 主要注意的是,请在编译前,根据需求编辑管理虚拟机(MVM)的配置文件。该文件的路径是 src/config/\_def.rs. +**RK3588的支持** + +目前已经支持Firefly ROC-RK3588S-PC平台,并提供了完整的用户使用手册和相应的附件,具体可以参考[Firefly ROC-RK3588S-PC平台的使用](https://bhpan.buaa.edu.cn/link/AA90DE4F5ED6F447E1A9DE59F7B1A8FF72)。 + **MVM的需求** MVM 是一个可以通过Hypervisor提供的私有特权接口来监控其他虚拟机状态的特权虚拟机,通常情况是一个Linux。我们为MVM实现了一个单独的Linux内核模块。通过改内核模块,MVM可以发起Hypercall来实现诸如虚拟机配置、虚拟机迁移、Hypervisor动态升级等功能。 diff --git a/README.md b/README.md index 0bde0403732ea2f0fd85741d0cfe5f6dcc8f934f..e585ecb8e8c39863420d3143edd82999faa6606b 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,11 @@ For example, `make tx2` is to build Rust-Shyper for TX2. Note that please edit the MVM profile in src/config/\_def.rs according to your requirements. + +**RK3588 support** + +Currently, the Firefly ROC-RK3588S-PC platform is supported, and a complete user manual and corresponding accessories are provided. For details, please refer to [ROC-RK3588S-PC platform](https://bhpan.buaa.edu.cn/link/AA90DE4F5ED6F447E1A9DE59F7B1A8FF72). + **MVM Requirements** MVM is a privileged VM that can monitor the status of other VMs through privileged interfaces provided by the hypervisor. We implement a dedicated Linux kernel module for MVM. Through this module, MVM can make a hypercall to realize specific functions, such as VM configuration, VM migration and hypervisor live-update. Generally, there is only one MVM, and it will monopolize core 0. diff --git a/build.rs b/build.rs index d85a58007151d3181936199f7adea0472b209712..f008806e5a5912b643e72034dfbbe84a9367a4b2 100644 --- a/build.rs +++ b/build.rs @@ -8,29 +8,47 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::process::Command; use std::env::var; +use std::process::Command; -fn main() { - let arch = var("CARGO_CFG_TARGET_ARCH").unwrap(); - let text_start = if cfg!(feature = "tx2") { - if cfg!(feature = "update") { - 0x8a000000_u64 - } else { +// get text_start by arch and platform +fn get_text_start() -> u64 { + let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + + if target_arch == "aarch64" { + if cfg!(feature = "tx2") { 0x83000000_u64 + } else if cfg!(feature = "pi4") { + 0xf0080000_u64 + } else if cfg!(feature = "qemu") { + 0x40080000_u64 + } else if cfg!(feature = "rk3588") { + 0x00400000_u64 + } else { + panic!("Unsupported platform!"); } - } else if cfg!(feature = "pi4") { - 0xf0080000_u64 - } else if cfg!(feature = "qemu") { - 0x40080000_u64 + } else if target_arch == "riscv64" { + 0x80200000_u64 } else { - panic!("Unsupported platform!"); - }; + panic!("Unsupported arch {}!", target_arch); + } +} + +fn main() { + let arch = var("CARGO_CFG_TARGET_ARCH").unwrap(); + + let text_start = get_text_start(); println!("cargo:rustc-link-arg=-Tlinkers/{arch}.ld"); println!("cargo:rustc-link-arg=--defsym=TEXT_START={text_start}"); + let commit_hash = Command::new("git").args(["rev-parse", "HEAD"]).output().unwrap(); + let commit_hash = String::from_utf8(commit_hash.stdout).unwrap(); + println!("cargo:rustc-env=GIT_COMMIT={}", commit_hash.trim()); + // note: add error checking yourself. - let output = Command::new("date").arg("+\"%Y-%m-%d %H:%M:%S %Z\"").output().unwrap(); - let build_time = String::from_utf8(output.stdout).unwrap(); + let build_time = chrono::offset::Local::now().format("%Y-%m-%d %H:%M:%S %Z"); println!("cargo:rustc-env=BUILD_TIME={}", build_time); + let hostname = gethostname::gethostname(); + println!("cargo:rustc-env=HOSTNAME={}", hostname.into_string().unwrap()); + built::write_built_file().expect("Failed to acquire build-time information"); } diff --git a/aarch64.json b/cfg/aarch64.json similarity index 100% rename from aarch64.json rename to cfg/aarch64.json diff --git a/cfg/riscv64.json b/cfg/riscv64.json new file mode 100644 index 0000000000000000000000000000000000000000..3e4aaae0d0d9c9144f7f180fcdd0d25a86364b06 --- /dev/null +++ b/cfg/riscv64.json @@ -0,0 +1,18 @@ +{ + "arch": "riscv64", + "cpu": "generic-rv64", + "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n64-S128", + "disable-redzone": true, + "executables": true, + "features": "+m,+a", + "is-builtin": false, + "code-model": "medium", + "linker": "rust-lld", + "linker-flavor": "ld.lld", + "linker-is-gnu": true, + "llvm-target": "riscv64", + "max-atomic-width": 64, + "panic-strategy": "abort", + "relocation-model": "static", + "target-pointer-width": "64" +} diff --git a/docker/.gitignore b/docker/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..259dcd309311b1c5dd69af19c7004b14df1bba56 --- /dev/null +++ b/docker/.gitignore @@ -0,0 +1 @@ +/rustup.sh diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..45bd9d36309f948030575908ce06182c37b0b905 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,30 @@ +FROM gitlab/gitlab-runner:ubuntu + +ENV DEBIAN_FRONTEND=noninteractive +ARG MIRROR=mirrors.tuna.tsinghua.edu.cn + +RUN sed -i "s/archive.ubuntu.com/${MIRROR}/g" /etc/apt/sources.list; \ + sed -i "s/security.ubuntu.com/${MIRROR}/g" /etc/apt/sources.list; \ + apt-get update && apt-get -y upgrade && \ + apt-get install -y curl build-essential gcc-aarch64-linux-gnu clang git && \ + apt-get clean + +ARG RUNNER_USER=shyper +ARG RUNNER_HOME=/home/${RUNNER_USER} + +RUN useradd -m ${RUNNER_USER} +COPY rustup.sh ${RUNNER_HOME}/ +RUN chown -R ${RUNNER_USER}:${RUNNER_USER} ${RUNNER_HOME} + +USER ${RUNNER_USER} +ENV HOME=${RUNNER_HOME} +WORKDIR ${RUNNER_HOME} + +# ENV HTTP_PROXY=http://172.17.0.1:8080 +# ENV HTTPS_PROXY="$HTTP_PROXY" ALL_PROXY="$HTTP_PROXY" \ +# http_proxy="$HTTP_PROXY" https_proxy="$HTTP_PROXY" all_proxy="$HTTP_PROXY" + +RUN ~/rustup.sh -y +ENV PATH="${RUNNER_HOME}/.cargo/bin:$PATH" + +# ENV HTTP_PROXY= HTTPS_PROXY= ALL_PROXY= http_proxy= https_proxy= all_proxy= diff --git a/docker/build.sh b/docker/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..e9a19f1ef6559f51872ec120ae87183fd6fe775b --- /dev/null +++ b/docker/build.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e +cd "$(dirname "$0")" +if ! [ -x rustup.sh ]; then + curl -f https://sh.rustup.rs -o rustup.sh + chmod +x rustup.sh +fi +docker build . -t shyper-runner:latest "$@" diff --git a/docker/run.sh b/docker/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..75be4b261ea4b0d753534b6d05ce5c48031121a6 --- /dev/null +++ b/docker/run.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -ex + +mkdir -p /srv/shyper-runner/{config,cargo/{git,registry}} + +# Allow non-priveleged runners to create files in our volumes. +chmod 777 /srv/shyper-runner/{config,cargo/{git,registry}} + +docker run -d --name shyper-runner --restart always \ + -v /srv/shyper-runner/config:/etc/gitlab-runner \ + -v /srv/shyper-runner/cargo/git:/home/shyper/.cargo/git \ + -v /srv/shyper-runner/cargo/registry:/home/shyper/.cargo/registry \ + -v /var/run/docker.sock:/var/run/docker.sock \ + shyper-runner:latest \ + run --user=shyper --working-directory=/home/shyper diff --git a/gdb/aarch64.gdb b/gdb/aarch64.gdb index 35130f1a8e901d5c815aedc510a6dd9cf51a9398..de31d14c35080b8bb9a8ab6fbe8ab7846ccedd7b 100644 --- a/gdb/aarch64.gdb +++ b/gdb/aarch64.gdb @@ -1,5 +1,5 @@ target remote 127.0.0.1:1234 -file target/aarch64/debug/rust_shyper +file target/aarch64/release/rust_shyper break *0x40080000 display/i $pc set confirm off diff --git a/image/Image_pi4 b/image/Image-5.10.160 similarity index 49% rename from image/Image_pi4 rename to image/Image-5.10.160 index 70c7f4a64acc1a7920062031cfe28755ba34aa68..46c4cd7265b8122e69caebc22a07496860fee211 100644 Binary files a/image/Image_pi4 and b/image/Image-5.10.160 differ diff --git a/image/Image_pi4_5.4.78 b/image/Image_pi4_5.4.78 deleted file mode 100755 index 944daa0a55b5d3ef947a2c47d6d29c1654b69467..0000000000000000000000000000000000000000 Binary files a/image/Image_pi4_5.4.78 and /dev/null differ diff --git a/image/pi4_fin.dtb b/image/pi4_fin.dtb deleted file mode 100644 index d855798972284675732077a5ff19d80a5acbdbc9..0000000000000000000000000000000000000000 Binary files a/image/pi4_fin.dtb and /dev/null differ diff --git a/libfdt-binding/.gitignore b/libfdt-binding/.gitignore deleted file mode 100644 index 1b26c7816f645916f2c9e150bdd97c72142a0db1..0000000000000000000000000000000000000000 --- a/libfdt-binding/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -/target - - -# Added by cargo -# -# already existing elements were commented out - -#/target -Cargo.lock diff --git a/libfdt-binding/Cargo.toml b/libfdt-binding/Cargo.toml deleted file mode 100644 index e97f07316548769e3d3dd1d67a477e8adb0b9e89..0000000000000000000000000000000000000000 --- a/libfdt-binding/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "fdt" -version = "0.1.0" -edition = "2021" -build = "build.rs" - -[build-dependencies] -bindgen = "0.59.1" -cc = "1.0" diff --git a/libfdt-binding/README.md b/libfdt-binding/README.md deleted file mode 100644 index 017316b49dcbfc59f20b5b8c1805ae45e876d550..0000000000000000000000000000000000000000 --- a/libfdt-binding/README.md +++ /dev/null @@ -1,4 +0,0 @@ -1. build `libfdt-binding.a` with cmake -2. copy `libfdt-binding.a` to Rust link search path -3. add `cargo:rustc-link-lib=static=fdt-binding` to `build.rs` -4. add `libfdt-binding` crate to `Cargo.toml` \ No newline at end of file diff --git a/libfdt-binding/build.rs b/libfdt-binding/build.rs deleted file mode 100644 index f32f2d54e30f09bff4273c950c6be6f51bf5ad29..0000000000000000000000000000000000000000 --- a/libfdt-binding/build.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. -// Rust-Shyper is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -extern crate bindgen; - -use std::env; -use std::path::PathBuf; - -fn main() { - println!("cargo:rerun-if-changed=wrapper.h"); - println!("cargo:rerun-if-changed=./libfdt"); - println!("cargo:rerun-if-changed=build.rs"); - - // compile libfdt-bingding - let fdt_dirs = ["./", "./libfdt"]; - let c_files = fdt_dirs.iter().flat_map(|path| { - std::fs::read_dir(path).unwrap().filter_map(|f| { - let f = f.unwrap(); - if f.file_type().unwrap().is_file() && matches!(f.path().extension(), Some(ext) if ext == "c") { - Some(f.path()) - } else { - None - } - }) - }); - cc::Build::new() - .compiler("aarch64-none-elf-gcc") - .includes(fdt_dirs) - .files(c_files) - .flag("-w") - .flag("-mgeneral-regs-only") - .compile("fdt-binding"); - - let bindings = bindgen::Builder::default() - .use_core() - .ctypes_prefix("myctypes") - .header("wrapper.h") - .parse_callbacks(Box::new(bindgen::CargoCallbacks)) - .generate() - .expect("Unable to generate bindings"); - - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - bindings - .write_to_file(out_path.join("bindings.rs")) - .expect("Couldn't write bindings!"); -} diff --git a/libfdt-binding/libfdt/fdt.c b/libfdt-binding/libfdt/fdt.c deleted file mode 100644 index 0de3a1c70ef101d96d813fc752c9b5479c25175e..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/fdt.c +++ /dev/null @@ -1,320 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) -/* - * libfdt - Flat Device Tree manipulation - * Copyright (C) 2006 David Gibson, IBM Corporation. - */ -#include "libfdt_env.h" - -#include -#include - -#include "libfdt_internal.h" - -/* - * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks - * that the given buffer contains what appears to be a flattened - * device tree with sane information in its header. - */ -int32_t fdt_ro_probe_(const void *fdt) { - uint32_t totalsize = fdt_totalsize(fdt); - - if (can_assume(VALID_DTB)) - return totalsize; - - /* The device tree must be at an 8-byte aligned address */ - if ((uintptr_t) fdt & 7) - return -FDT_ERR_ALIGNMENT; - - if (fdt_magic(fdt) == FDT_MAGIC) { - /* Complete tree */ - if (!can_assume(LATEST)) { - if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) - return -FDT_ERR_BADVERSION; - if (fdt_last_comp_version(fdt) > - FDT_LAST_SUPPORTED_VERSION) - return -FDT_ERR_BADVERSION; - } - } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { - /* Unfinished sequential-write blob */ - if (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0) - return -FDT_ERR_BADSTATE; - } else { - return -FDT_ERR_BADMAGIC; - } - - if (totalsize < INT32_MAX) - return totalsize; - else - return -FDT_ERR_TRUNCATED; -} - -static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) { - return (off >= hdrsize) && (off <= totalsize); -} - -static int check_block_(uint32_t hdrsize, uint32_t totalsize, - uint32_t base, uint32_t size) { - if (!check_off_(hdrsize, totalsize, base)) - return 0; /* block start out of bounds */ - if ((base + size) < base) - return 0; /* overflow */ - if (!check_off_(hdrsize, totalsize, base + size)) - return 0; /* block end out of bounds */ - return 1; -} - -size_t fdt_header_size_(uint32_t version) { - if (version <= 1) - return FDT_V1_SIZE; - else if (version <= 2) - return FDT_V2_SIZE; - else if (version <= 3) - return FDT_V3_SIZE; - else if (version <= 16) - return FDT_V16_SIZE; - else - return FDT_V17_SIZE; -} - -size_t fdt_header_size(const void *fdt) { - return can_assume(LATEST) ? FDT_V17_SIZE : - fdt_header_size_(fdt_version(fdt)); -} - -int fdt_check_header(const void *fdt) { - size_t hdrsize; - - /* The device tree must be at an 8-byte aligned address */ - if ((uintptr_t) fdt & 7) - return -FDT_ERR_ALIGNMENT; - - if (fdt_magic(fdt) != FDT_MAGIC) - return -FDT_ERR_BADMAGIC; - if (!can_assume(LATEST)) { - if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) - || (fdt_last_comp_version(fdt) > - FDT_LAST_SUPPORTED_VERSION)) - return -FDT_ERR_BADVERSION; - if (fdt_version(fdt) < fdt_last_comp_version(fdt)) - return -FDT_ERR_BADVERSION; - } - hdrsize = fdt_header_size(fdt); - if (!can_assume(VALID_DTB)) { - - if ((fdt_totalsize(fdt) < hdrsize) - || (fdt_totalsize(fdt) > INT_MAX)) - return -FDT_ERR_TRUNCATED; - - /* Bounds check memrsv block */ - if (!check_off_(hdrsize, fdt_totalsize(fdt), - fdt_off_mem_rsvmap(fdt))) - return -FDT_ERR_TRUNCATED; - } - - if (!can_assume(VALID_DTB)) { - /* Bounds check structure block */ - if (!can_assume(LATEST) && fdt_version(fdt) < 17) { - if (!check_off_(hdrsize, fdt_totalsize(fdt), - fdt_off_dt_struct(fdt))) - return -FDT_ERR_TRUNCATED; - } else { - if (!check_block_(hdrsize, fdt_totalsize(fdt), - fdt_off_dt_struct(fdt), - fdt_size_dt_struct(fdt))) - return -FDT_ERR_TRUNCATED; - } - - /* Bounds check strings block */ - if (!check_block_(hdrsize, fdt_totalsize(fdt), - fdt_off_dt_strings(fdt), - fdt_size_dt_strings(fdt))) - return -FDT_ERR_TRUNCATED; - } - - return 0; -} - -const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) { - unsigned int uoffset = offset; - unsigned int absoffset = offset + fdt_off_dt_struct(fdt); - - if (offset < 0) - return NULL; - - if (!can_assume(VALID_INPUT)) - if ((absoffset < uoffset) - || ((absoffset + len) < absoffset) - || (absoffset + len) > fdt_totalsize(fdt)) - return NULL; - - if (can_assume(LATEST) || fdt_version(fdt) >= 0x11) - if (((uoffset + len) < uoffset) - || ((offset + len) > fdt_size_dt_struct(fdt))) - return NULL; - - return fdt_offset_ptr_(fdt, offset); -} - -uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) { - const fdt32_t *tagp, *lenp; - uint32_t tag; - int offset = startoffset; - const char *p; - - *nextoffset = -FDT_ERR_TRUNCATED; - tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); - if (!can_assume(VALID_DTB) && !tagp) - return FDT_END; /* premature end */ - tag = fdt32_to_cpu(*tagp); - offset += FDT_TAGSIZE; - - *nextoffset = -FDT_ERR_BADSTRUCTURE; - switch (tag) { - case FDT_BEGIN_NODE: - /* skip name */ - do { - p = fdt_offset_ptr(fdt, offset++, 1); - } while (p && (*p != '\0')); - if (!can_assume(VALID_DTB) && !p) - return FDT_END; /* premature end */ - break; - - case FDT_PROP: - lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); - if (!can_assume(VALID_DTB) && !lenp) - return FDT_END; /* premature end */ - /* skip-name offset, length and value */ - offset += sizeof(struct fdt_property) - FDT_TAGSIZE - + fdt32_to_cpu(*lenp); - if (!can_assume(LATEST) && - fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 && - ((offset - fdt32_to_cpu(*lenp)) % 8) != 0) - offset += 4; - break; - - case FDT_END: - case FDT_END_NODE: - case FDT_NOP: - break; - - default: - return FDT_END; - } - - if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) - return FDT_END; /* premature end */ - - *nextoffset = FDT_TAGALIGN(offset); - return tag; -} - -int fdt_check_node_offset_(const void *fdt, int offset) { - if (!can_assume(VALID_INPUT) - && ((offset < 0) || (offset % FDT_TAGSIZE))) - return -FDT_ERR_BADOFFSET; - - if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE) - return -FDT_ERR_BADOFFSET; - - return offset; -} - -int fdt_check_prop_offset_(const void *fdt, int offset) { - if (!can_assume(VALID_INPUT) - && ((offset < 0) || (offset % FDT_TAGSIZE))) - return -FDT_ERR_BADOFFSET; - - if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP) - return -FDT_ERR_BADOFFSET; - - return offset; -} - -int fdt_next_node(const void *fdt, int offset, int *depth) { - int nextoffset = 0; - uint32_t tag; - - if (offset >= 0) - if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0) - return nextoffset; - - do { - offset = nextoffset; - tag = fdt_next_tag(fdt, offset, &nextoffset); - - switch (tag) { - case FDT_PROP: - case FDT_NOP: - break; - - case FDT_BEGIN_NODE: - if (depth) - (*depth)++; - break; - - case FDT_END_NODE: - if (depth && ((--(*depth)) < 0)) - return nextoffset; - break; - - case FDT_END: - if ((nextoffset >= 0) - || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) - return -FDT_ERR_NOTFOUND; - else - return nextoffset; - } - } while (tag != FDT_BEGIN_NODE); - - return offset; -} - -int fdt_first_subnode(const void *fdt, int offset) { - int depth = 0; - - offset = fdt_next_node(fdt, offset, &depth); - if (offset < 0 || depth != 1) - return -FDT_ERR_NOTFOUND; - - return offset; -} - -int fdt_next_subnode(const void *fdt, int offset) { - int depth = 1; - - /* - * With respect to the parent, the depth of the next subnode will be - * the same as the last. - */ - do { - offset = fdt_next_node(fdt, offset, &depth); - if (offset < 0 || depth < 1) - return -FDT_ERR_NOTFOUND; - } while (depth > 1); - - return offset; -} - -const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) { - int len = strlen(s) + 1; - const char *last = strtab + tabsize - len; - const char *p; - - for (p = strtab; p <= last; p++) - if (memcmp(p, s, len) == 0) - return p; - return NULL; -} - -int fdt_move(const void *fdt, void *buf, int bufsize) { - if (!can_assume(VALID_INPUT) && bufsize < 0) - return -FDT_ERR_NOSPACE; - - FDT_RO_PROBE(fdt); - - if (fdt_totalsize(fdt) > (unsigned int) bufsize) - return -FDT_ERR_NOSPACE; - - memmove(buf, fdt, fdt_totalsize(fdt)); - return 0; -} diff --git a/libfdt-binding/libfdt/fdt.h b/libfdt-binding/libfdt/fdt.h deleted file mode 100644 index f2e68807f277c5009641c54f5198bebb7cb1c28f..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/fdt.h +++ /dev/null @@ -1,66 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ -#ifndef FDT_H -#define FDT_H -/* - * libfdt - Flat Device Tree manipulation - * Copyright (C) 2006 David Gibson, IBM Corporation. - * Copyright 2012 Kim Phillips, Freescale Semiconductor. - */ - -#ifndef __ASSEMBLY__ - -struct fdt_header { - fdt32_t magic; /* magic word FDT_MAGIC */ - fdt32_t totalsize; /* total size of DT block */ - fdt32_t off_dt_struct; /* offset to structure */ - fdt32_t off_dt_strings; /* offset to strings */ - fdt32_t off_mem_rsvmap; /* offset to memory reserve map */ - fdt32_t version; /* format version */ - fdt32_t last_comp_version; /* last compatible version */ - - /* version 2 fields below */ - fdt32_t boot_cpuid_phys; /* Which physical CPU id we're - booting on */ - /* version 3 fields below */ - fdt32_t size_dt_strings; /* size of the strings block */ - - /* version 17 fields below */ - fdt32_t size_dt_struct; /* size of the structure block */ -}; - -struct fdt_reserve_entry { - fdt64_t address; - fdt64_t size; -}; - -struct fdt_node_header { - fdt32_t tag; - char name[0]; -}; - -struct fdt_property { - fdt32_t tag; - fdt32_t len; - fdt32_t nameoff; - char data[0]; -}; - -#endif /* !__ASSEMBLY */ - -#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ -#define FDT_TAGSIZE sizeof(fdt32_t) - -#define FDT_BEGIN_NODE 0x1 /* Start node: full name */ -#define FDT_END_NODE 0x2 /* End node */ -#define FDT_PROP 0x3 /* Property: name off, - size, content */ -#define FDT_NOP 0x4 /* nop */ -#define FDT_END 0x9 - -#define FDT_V1_SIZE (7*sizeof(fdt32_t)) -#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t)) -#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t)) -#define FDT_V16_SIZE FDT_V3_SIZE -#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t)) - -#endif /* FDT_H */ diff --git a/libfdt-binding/libfdt/fdt_addresses.c b/libfdt-binding/libfdt/fdt_addresses.c deleted file mode 100644 index 9a82cd0ba2f9714c748d40ba75cd61cb6fe25005..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/fdt_addresses.c +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) -/* - * libfdt - Flat Device Tree manipulation - * Copyright (C) 2014 David Gibson - * Copyright (C) 2018 embedded brains GmbH - */ -#include "libfdt_env.h" - -#include -#include - -#include "libfdt_internal.h" - -static int fdt_cells(const void *fdt, int nodeoffset, const char *name) -{ - const fdt32_t *c; - uint32_t val; - int len; - - c = fdt_getprop(fdt, nodeoffset, name, &len); - if (!c) - return len; - - if (len != sizeof(*c)) - return -FDT_ERR_BADNCELLS; - - val = fdt32_to_cpu(*c); - if (val > FDT_MAX_NCELLS) - return -FDT_ERR_BADNCELLS; - - return (int)val; -} - -int fdt_address_cells(const void *fdt, int nodeoffset) -{ - int val; - - val = fdt_cells(fdt, nodeoffset, "#address-cells"); - if (val == 0) - return -FDT_ERR_BADNCELLS; - if (val == -FDT_ERR_NOTFOUND) - return 2; - return val; -} - -int fdt_size_cells(const void *fdt, int nodeoffset) -{ - int val; - - val = fdt_cells(fdt, nodeoffset, "#size-cells"); - if (val == -FDT_ERR_NOTFOUND) - return 1; - return val; -} - -/* This function assumes that [address|size]_cells is 1 or 2 */ -int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset, - const char *name, uint64_t addr, uint64_t size) -{ - int addr_cells, size_cells, ret; - uint8_t data[sizeof(fdt64_t) * 2], *prop; - - ret = fdt_address_cells(fdt, parent); - if (ret < 0) - return ret; - addr_cells = ret; - - ret = fdt_size_cells(fdt, parent); - if (ret < 0) - return ret; - size_cells = ret; - - /* check validity of address */ - prop = data; - if (addr_cells == 1) { - if ((addr > UINT32_MAX) || ((UINT32_MAX + 1 - addr) < size)) - return -FDT_ERR_BADVALUE; - - fdt32_st(prop, (uint32_t)addr); - } else if (addr_cells == 2) { - fdt64_st(prop, addr); - } else { - return -FDT_ERR_BADNCELLS; - } - - /* check validity of size */ - prop += addr_cells * sizeof(fdt32_t); - if (size_cells == 1) { - if (size > UINT32_MAX) - return -FDT_ERR_BADVALUE; - - fdt32_st(prop, (uint32_t)size); - } else if (size_cells == 2) { - fdt64_st(prop, size); - } else { - return -FDT_ERR_BADNCELLS; - } - - return fdt_appendprop(fdt, nodeoffset, name, data, - (addr_cells + size_cells) * sizeof(fdt32_t)); -} diff --git a/libfdt-binding/libfdt/fdt_check.c b/libfdt-binding/libfdt/fdt_check.c deleted file mode 100644 index fa410a86e28289a93ac9931bb3f7f64b9ecc548c..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/fdt_check.c +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) -/* - * libfdt - Flat Device Tree manipulation - * Copyright (C) 2006 David Gibson, IBM Corporation. - */ -#include "libfdt_env.h" - -#include -#include - -#include "libfdt_internal.h" - -int fdt_check_full(const void *fdt, size_t bufsize) -{ - int err; - int num_memrsv; - int offset, nextoffset = 0; - uint32_t tag; - unsigned int depth = 0; - const void *prop; - const char *propname; - bool expect_end = false; - - if (bufsize < FDT_V1_SIZE) - return -FDT_ERR_TRUNCATED; - if (bufsize < fdt_header_size(fdt)) - return -FDT_ERR_TRUNCATED; - err = fdt_check_header(fdt); - if (err != 0) - return err; - if (bufsize < fdt_totalsize(fdt)) - return -FDT_ERR_TRUNCATED; - - num_memrsv = fdt_num_mem_rsv(fdt); - if (num_memrsv < 0) - return num_memrsv; - - while (1) { - offset = nextoffset; - tag = fdt_next_tag(fdt, offset, &nextoffset); - - if (nextoffset < 0) - return nextoffset; - - /* If we see two root nodes, something is wrong */ - if (expect_end && tag != FDT_END) - return -FDT_ERR_BADSTRUCTURE; - - switch (tag) { - case FDT_NOP: - break; - - case FDT_END: - if (depth != 0) - return -FDT_ERR_BADSTRUCTURE; - return 0; - - case FDT_BEGIN_NODE: - depth++; - if (depth > INT_MAX) - return -FDT_ERR_BADSTRUCTURE; - - /* The root node must have an empty name */ - if (depth == 1) { - const char *name; - int len; - - name = fdt_get_name(fdt, offset, &len); - if (*name || len) - return -FDT_ERR_BADSTRUCTURE; - } - break; - - case FDT_END_NODE: - if (depth == 0) - return -FDT_ERR_BADSTRUCTURE; - depth--; - if (depth == 0) - expect_end = true; - break; - - case FDT_PROP: - prop = fdt_getprop_by_offset(fdt, offset, &propname, - &err); - if (!prop) - return err; - break; - - default: - return -FDT_ERR_INTERNAL; - } - } -} diff --git a/libfdt-binding/libfdt/fdt_empty_tree.c b/libfdt-binding/libfdt/fdt_empty_tree.c deleted file mode 100644 index 49d54d44b8e78be6d110c41db97312ef66ff89aa..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/fdt_empty_tree.c +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) -/* - * libfdt - Flat Device Tree manipulation - * Copyright (C) 2012 David Gibson, IBM Corporation. - */ -#include "libfdt_env.h" - -#include -#include - -#include "libfdt_internal.h" - -int fdt_create_empty_tree(void *buf, int bufsize) -{ - int err; - - err = fdt_create(buf, bufsize); - if (err) - return err; - - err = fdt_finish_reservemap(buf); - if (err) - return err; - - err = fdt_begin_node(buf, ""); - if (err) - return err; - - err = fdt_end_node(buf); - if (err) - return err; - - err = fdt_finish(buf); - if (err) - return err; - - return fdt_open_into(buf, buf, bufsize); -} diff --git a/libfdt-binding/libfdt/fdt_overlay.c b/libfdt-binding/libfdt/fdt_overlay.c deleted file mode 100644 index d217e79b672299f858a9b242e724e13cb53f110f..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/fdt_overlay.c +++ /dev/null @@ -1,882 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) -/* - * libfdt - Flat Device Tree manipulation - * Copyright (C) 2016 Free Electrons - * Copyright (C) 2016 NextThing Co. - */ -#include "libfdt_env.h" - -#include -#include - -#include "libfdt_internal.h" - -/** - * overlay_get_target_phandle - retrieves the target phandle of a fragment - * @fdto: pointer to the device tree overlay blob - * @fragment: node offset of the fragment in the overlay - * - * overlay_get_target_phandle() retrieves the target phandle of an - * overlay fragment when that fragment uses a phandle (target - * property) instead of a path (target-path property). - * - * returns: - * the phandle pointed by the target property - * 0, if the phandle was not found - * -1, if the phandle was malformed - */ -static uint32_t overlay_get_target_phandle(const void *fdto, int fragment) -{ - const fdt32_t *val; - int len; - - val = fdt_getprop(fdto, fragment, "target", &len); - if (!val) - return 0; - - if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1)) - return (uint32_t)-1; - - return fdt32_to_cpu(*val); -} - -/** - * overlay_get_target - retrieves the offset of a fragment's target - * @fdt: Base device tree blob - * @fdto: Device tree overlay blob - * @fragment: node offset of the fragment in the overlay - * @pathp: pointer which receives the path of the target (or NULL) - * - * overlay_get_target() retrieves the target offset in the base - * device tree of a fragment, no matter how the actual targeting is - * done (through a phandle or a path) - * - * returns: - * the targeted node offset in the base device tree - * Negative error code on error - */ -static int overlay_get_target(const void *fdt, const void *fdto, - int fragment, char const **pathp) -{ - uint32_t phandle; - const char *path = NULL; - int path_len = 0, ret; - - /* Try first to do a phandle based lookup */ - phandle = overlay_get_target_phandle(fdto, fragment); - if (phandle == (uint32_t)-1) - return -FDT_ERR_BADPHANDLE; - - /* no phandle, try path */ - if (!phandle) { - /* And then a path based lookup */ - path = fdt_getprop(fdto, fragment, "target-path", &path_len); - if (path) - ret = fdt_path_offset(fdt, path); - else - ret = path_len; - } else - ret = fdt_node_offset_by_phandle(fdt, phandle); - - /* - * If we haven't found either a target or a - * target-path property in a node that contains a - * __overlay__ subnode (we wouldn't be called - * otherwise), consider it a improperly written - * overlay - */ - if (ret < 0 && path_len == -FDT_ERR_NOTFOUND) - ret = -FDT_ERR_BADOVERLAY; - - /* return on error */ - if (ret < 0) - return ret; - - /* return pointer to path (if available) */ - if (pathp) - *pathp = path ? path : NULL; - - return ret; -} - -/** - * overlay_phandle_add_offset - Increases a phandle by an offset - * @fdt: Base device tree blob - * @node: Device tree overlay blob - * @name: Name of the property to modify (phandle or linux,phandle) - * @delta: offset to apply - * - * overlay_phandle_add_offset() increments a node phandle by a given - * offset. - * - * returns: - * 0 on success. - * Negative error code on error - */ -static int overlay_phandle_add_offset(void *fdt, int node, - const char *name, uint32_t delta) -{ - const fdt32_t *val; - uint32_t adj_val; - int len; - - val = fdt_getprop(fdt, node, name, &len); - if (!val) - return len; - - if (len != sizeof(*val)) - return -FDT_ERR_BADPHANDLE; - - adj_val = fdt32_to_cpu(*val); - if ((adj_val + delta) < adj_val) - return -FDT_ERR_NOPHANDLES; - - adj_val += delta; - if (adj_val == (uint32_t)-1) - return -FDT_ERR_NOPHANDLES; - - return fdt_setprop_inplace_u32(fdt, node, name, adj_val); -} - -/** - * overlay_adjust_node_phandles - Offsets the phandles of a node - * @fdto: Device tree overlay blob - * @node: Offset of the node we want to adjust - * @delta: Offset to shift the phandles of - * - * overlay_adjust_node_phandles() adds a constant to all the phandles - * of a given node. This is mainly use as part of the overlay - * application process, when we want to update all the overlay - * phandles to not conflict with the overlays of the base device tree. - * - * returns: - * 0 on success - * Negative error code on failure - */ -static int overlay_adjust_node_phandles(void *fdto, int node, - uint32_t delta) -{ - int child; - int ret; - - ret = overlay_phandle_add_offset(fdto, node, "phandle", delta); - if (ret && ret != -FDT_ERR_NOTFOUND) - return ret; - - ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta); - if (ret && ret != -FDT_ERR_NOTFOUND) - return ret; - - fdt_for_each_subnode(child, fdto, node) { - ret = overlay_adjust_node_phandles(fdto, child, delta); - if (ret) - return ret; - } - - return 0; -} - -/** - * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay - * @fdto: Device tree overlay blob - * @delta: Offset to shift the phandles of - * - * overlay_adjust_local_phandles() adds a constant to all the - * phandles of an overlay. This is mainly use as part of the overlay - * application process, when we want to update all the overlay - * phandles to not conflict with the overlays of the base device tree. - * - * returns: - * 0 on success - * Negative error code on failure - */ -static int overlay_adjust_local_phandles(void *fdto, uint32_t delta) -{ - /* - * Start adjusting the phandles from the overlay root - */ - return overlay_adjust_node_phandles(fdto, 0, delta); -} - -/** - * overlay_update_local_node_references - Adjust the overlay references - * @fdto: Device tree overlay blob - * @tree_node: Node offset of the node to operate on - * @fixup_node: Node offset of the matching local fixups node - * @delta: Offset to shift the phandles of - * - * overlay_update_local_nodes_references() update the phandles - * pointing to a node within the device tree overlay by adding a - * constant delta. - * - * This is mainly used as part of a device tree application process, - * where you want the device tree overlays phandles to not conflict - * with the ones from the base device tree before merging them. - * - * returns: - * 0 on success - * Negative error code on failure - */ -static int overlay_update_local_node_references(void *fdto, - int tree_node, - int fixup_node, - uint32_t delta) -{ - int fixup_prop; - int fixup_child; - int ret; - - fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) { - const fdt32_t *fixup_val; - const char *tree_val; - const char *name; - int fixup_len; - int tree_len; - int i; - - fixup_val = fdt_getprop_by_offset(fdto, fixup_prop, - &name, &fixup_len); - if (!fixup_val) - return fixup_len; - - if (fixup_len % sizeof(uint32_t)) - return -FDT_ERR_BADOVERLAY; - fixup_len /= sizeof(uint32_t); - - tree_val = fdt_getprop(fdto, tree_node, name, &tree_len); - if (!tree_val) { - if (tree_len == -FDT_ERR_NOTFOUND) - return -FDT_ERR_BADOVERLAY; - - return tree_len; - } - - for (i = 0; i < fixup_len; i++) { - fdt32_t adj_val; - uint32_t poffset; - - poffset = fdt32_to_cpu(fixup_val[i]); - - /* - * phandles to fixup can be unaligned. - * - * Use a memcpy for the architectures that do - * not support unaligned accesses. - */ - memcpy(&adj_val, tree_val + poffset, sizeof(adj_val)); - - adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta); - - ret = fdt_setprop_inplace_namelen_partial(fdto, - tree_node, - name, - strlen(name), - poffset, - &adj_val, - sizeof(adj_val)); - if (ret == -FDT_ERR_NOSPACE) - return -FDT_ERR_BADOVERLAY; - - if (ret) - return ret; - } - } - - fdt_for_each_subnode(fixup_child, fdto, fixup_node) { - const char *fixup_child_name = fdt_get_name(fdto, fixup_child, - NULL); - int tree_child; - - tree_child = fdt_subnode_offset(fdto, tree_node, - fixup_child_name); - if (tree_child == -FDT_ERR_NOTFOUND) - return -FDT_ERR_BADOVERLAY; - if (tree_child < 0) - return tree_child; - - ret = overlay_update_local_node_references(fdto, - tree_child, - fixup_child, - delta); - if (ret) - return ret; - } - - return 0; -} - -/** - * overlay_update_local_references - Adjust the overlay references - * @fdto: Device tree overlay blob - * @delta: Offset to shift the phandles of - * - * overlay_update_local_references() update all the phandles pointing - * to a node within the device tree overlay by adding a constant - * delta to not conflict with the base overlay. - * - * This is mainly used as part of a device tree application process, - * where you want the device tree overlays phandles to not conflict - * with the ones from the base device tree before merging them. - * - * returns: - * 0 on success - * Negative error code on failure - */ -static int overlay_update_local_references(void *fdto, uint32_t delta) -{ - int fixups; - - fixups = fdt_path_offset(fdto, "/__local_fixups__"); - if (fixups < 0) { - /* There's no local phandles to adjust, bail out */ - if (fixups == -FDT_ERR_NOTFOUND) - return 0; - - return fixups; - } - - /* - * Update our local references from the root of the tree - */ - return overlay_update_local_node_references(fdto, 0, fixups, - delta); -} - -/** - * overlay_fixup_one_phandle - Set an overlay phandle to the base one - * @fdt: Base Device Tree blob - * @fdto: Device tree overlay blob - * @symbols_off: Node offset of the symbols node in the base device tree - * @path: Path to a node holding a phandle in the overlay - * @path_len: number of path characters to consider - * @name: Name of the property holding the phandle reference in the overlay - * @name_len: number of name characters to consider - * @poffset: Offset within the overlay property where the phandle is stored - * @label: Label of the node referenced by the phandle - * - * overlay_fixup_one_phandle() resolves an overlay phandle pointing to - * a node in the base device tree. - * - * This is part of the device tree overlay application process, when - * you want all the phandles in the overlay to point to the actual - * base dt nodes. - * - * returns: - * 0 on success - * Negative error code on failure - */ -static int overlay_fixup_one_phandle(void *fdt, void *fdto, - int symbols_off, - const char *path, uint32_t path_len, - const char *name, uint32_t name_len, - int poffset, const char *label) -{ - const char *symbol_path; - uint32_t phandle; - fdt32_t phandle_prop; - int symbol_off, fixup_off; - int prop_len; - - if (symbols_off < 0) - return symbols_off; - - symbol_path = fdt_getprop(fdt, symbols_off, label, - &prop_len); - if (!symbol_path) - return prop_len; - - symbol_off = fdt_path_offset(fdt, symbol_path); - if (symbol_off < 0) - return symbol_off; - - phandle = fdt_get_phandle(fdt, symbol_off); - if (!phandle) - return -FDT_ERR_NOTFOUND; - - fixup_off = fdt_path_offset_namelen(fdto, path, path_len); - if (fixup_off == -FDT_ERR_NOTFOUND) - return -FDT_ERR_BADOVERLAY; - if (fixup_off < 0) - return fixup_off; - - phandle_prop = cpu_to_fdt32(phandle); - return fdt_setprop_inplace_namelen_partial(fdto, fixup_off, - name, name_len, poffset, - &phandle_prop, - sizeof(phandle_prop)); -}; - -/** - * overlay_fixup_phandle - Set an overlay phandle to the base one - * @fdt: Base Device Tree blob - * @fdto: Device tree overlay blob - * @symbols_off: Node offset of the symbols node in the base device tree - * @property: Property offset in the overlay holding the list of fixups - * - * overlay_fixup_phandle() resolves all the overlay phandles pointed - * to in a __fixups__ property, and updates them to match the phandles - * in use in the base device tree. - * - * This is part of the device tree overlay application process, when - * you want all the phandles in the overlay to point to the actual - * base dt nodes. - * - * returns: - * 0 on success - * Negative error code on failure - */ -static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, - int property) -{ - const char *value; - const char *label; - int len; - - value = fdt_getprop_by_offset(fdto, property, - &label, &len); - if (!value) { - if (len == -FDT_ERR_NOTFOUND) - return -FDT_ERR_INTERNAL; - - return len; - } - - do { - const char *path, *name, *fixup_end; - const char *fixup_str = value; - uint32_t path_len, name_len; - uint32_t fixup_len; - char *sep, *endptr; - int poffset, ret; - - fixup_end = memchr(value, '\0', len); - if (!fixup_end) - return -FDT_ERR_BADOVERLAY; - fixup_len = fixup_end - fixup_str; - - len -= fixup_len + 1; - value += fixup_len + 1; - - path = fixup_str; - sep = memchr(fixup_str, ':', fixup_len); - if (!sep || *sep != ':') - return -FDT_ERR_BADOVERLAY; - - path_len = sep - path; - if (path_len == (fixup_len - 1)) - return -FDT_ERR_BADOVERLAY; - - fixup_len -= path_len + 1; - name = sep + 1; - sep = memchr(name, ':', fixup_len); - if (!sep || *sep != ':') - return -FDT_ERR_BADOVERLAY; - - name_len = sep - name; - if (!name_len) - return -FDT_ERR_BADOVERLAY; - - poffset = strtoul(sep + 1, &endptr, 10); - if ((*endptr != '\0') || (endptr <= (sep + 1))) - return -FDT_ERR_BADOVERLAY; - - ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, - path, path_len, name, name_len, - poffset, label); - if (ret) - return ret; - } while (len > 0); - - return 0; -} - -/** - * overlay_fixup_phandles - Resolve the overlay phandles to the base - * device tree - * @fdt: Base Device Tree blob - * @fdto: Device tree overlay blob - * - * overlay_fixup_phandles() resolves all the overlay phandles pointing - * to nodes in the base device tree. - * - * This is one of the steps of the device tree overlay application - * process, when you want all the phandles in the overlay to point to - * the actual base dt nodes. - * - * returns: - * 0 on success - * Negative error code on failure - */ -static int overlay_fixup_phandles(void *fdt, void *fdto) -{ - int fixups_off, symbols_off; - int property; - - /* We can have overlays without any fixups */ - fixups_off = fdt_path_offset(fdto, "/__fixups__"); - if (fixups_off == -FDT_ERR_NOTFOUND) - return 0; /* nothing to do */ - if (fixups_off < 0) - return fixups_off; - - /* And base DTs without symbols */ - symbols_off = fdt_path_offset(fdt, "/__symbols__"); - if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) - return symbols_off; - - fdt_for_each_property_offset(property, fdto, fixups_off) { - int ret; - - ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); - if (ret) - return ret; - } - - return 0; -} - -/** - * overlay_apply_node - Merges a node into the base device tree - * @fdt: Base Device Tree blob - * @target: Node offset in the base device tree to apply the fragment to - * @fdto: Device tree overlay blob - * @node: Node offset in the overlay holding the changes to merge - * - * overlay_apply_node() merges a node into a target base device tree - * node pointed. - * - * This is part of the final step in the device tree overlay - * application process, when all the phandles have been adjusted and - * resolved and you just have to merge overlay into the base device - * tree. - * - * returns: - * 0 on success - * Negative error code on failure - */ -static int overlay_apply_node(void *fdt, int target, - void *fdto, int node) -{ - int property; - int subnode; - - fdt_for_each_property_offset(property, fdto, node) { - const char *name; - const void *prop; - int prop_len; - int ret; - - prop = fdt_getprop_by_offset(fdto, property, &name, - &prop_len); - if (prop_len == -FDT_ERR_NOTFOUND) - return -FDT_ERR_INTERNAL; - if (prop_len < 0) - return prop_len; - - ret = fdt_setprop(fdt, target, name, prop, prop_len); - if (ret) - return ret; - } - - fdt_for_each_subnode(subnode, fdto, node) { - const char *name = fdt_get_name(fdto, subnode, NULL); - int nnode; - int ret; - - nnode = fdt_add_subnode(fdt, target, name); - if (nnode == -FDT_ERR_EXISTS) { - nnode = fdt_subnode_offset(fdt, target, name); - if (nnode == -FDT_ERR_NOTFOUND) - return -FDT_ERR_INTERNAL; - } - - if (nnode < 0) - return nnode; - - ret = overlay_apply_node(fdt, nnode, fdto, subnode); - if (ret) - return ret; - } - - return 0; -} - -/** - * overlay_merge - Merge an overlay into its base device tree - * @fdt: Base Device Tree blob - * @fdto: Device tree overlay blob - * - * overlay_merge() merges an overlay into its base device tree. - * - * This is the next to last step in the device tree overlay application - * process, when all the phandles have been adjusted and resolved and - * you just have to merge overlay into the base device tree. - * - * returns: - * 0 on success - * Negative error code on failure - */ -static int overlay_merge(void *fdt, void *fdto) -{ - int fragment; - - fdt_for_each_subnode(fragment, fdto, 0) { - int overlay; - int target; - int ret; - - /* - * Each fragments will have an __overlay__ node. If - * they don't, it's not supposed to be merged - */ - overlay = fdt_subnode_offset(fdto, fragment, "__overlay__"); - if (overlay == -FDT_ERR_NOTFOUND) - continue; - - if (overlay < 0) - return overlay; - - target = overlay_get_target(fdt, fdto, fragment, NULL); - if (target < 0) - return target; - - ret = overlay_apply_node(fdt, target, fdto, overlay); - if (ret) - return ret; - } - - return 0; -} - -static int get_path_len(const void *fdt, int nodeoffset) -{ - int len = 0, namelen; - const char *name; - - FDT_RO_PROBE(fdt); - - for (;;) { - name = fdt_get_name(fdt, nodeoffset, &namelen); - if (!name) - return namelen; - - /* root? we're done */ - if (namelen == 0) - break; - - nodeoffset = fdt_parent_offset(fdt, nodeoffset); - if (nodeoffset < 0) - return nodeoffset; - len += namelen + 1; - } - - /* in case of root pretend it's "/" */ - if (len == 0) - len++; - return len; -} - -/** - * overlay_symbol_update - Update the symbols of base tree after a merge - * @fdt: Base Device Tree blob - * @fdto: Device tree overlay blob - * - * overlay_symbol_update() updates the symbols of the base tree with the - * symbols of the applied overlay - * - * This is the last step in the device tree overlay application - * process, allowing the reference of overlay symbols by subsequent - * overlay operations. - * - * returns: - * 0 on success - * Negative error code on failure - */ -static int overlay_symbol_update(void *fdt, void *fdto) -{ - int root_sym, ov_sym, prop, path_len, fragment, target; - int len, frag_name_len, ret, rel_path_len; - const char *s, *e; - const char *path; - const char *name; - const char *frag_name; - const char *rel_path; - const char *target_path; - char *buf; - void *p; - - ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__"); - - /* if no overlay symbols exist no problem */ - if (ov_sym < 0) - return 0; - - root_sym = fdt_subnode_offset(fdt, 0, "__symbols__"); - - /* it no root symbols exist we should create them */ - if (root_sym == -FDT_ERR_NOTFOUND) - root_sym = fdt_add_subnode(fdt, 0, "__symbols__"); - - /* any error is fatal now */ - if (root_sym < 0) - return root_sym; - - /* iterate over each overlay symbol */ - fdt_for_each_property_offset(prop, fdto, ov_sym) { - path = fdt_getprop_by_offset(fdto, prop, &name, &path_len); - if (!path) - return path_len; - - /* verify it's a string property (terminated by a single \0) */ - if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1]) - return -FDT_ERR_BADVALUE; - - /* keep end marker to avoid strlen() */ - e = path + path_len; - - if (*path != '/') - return -FDT_ERR_BADVALUE; - - /* get fragment name first */ - s = strchr(path + 1, '/'); - if (!s) { - /* Symbol refers to something that won't end - * up in the target tree */ - continue; - } - - frag_name = path + 1; - frag_name_len = s - path - 1; - - /* verify format; safe since "s" lies in \0 terminated prop */ - len = sizeof("/__overlay__/") - 1; - if ((e - s) > len && (memcmp(s, "/__overlay__/", len) == 0)) { - /* //__overlay__/ */ - rel_path = s + len; - rel_path_len = e - rel_path - 1; - } else if ((e - s) == len - && (memcmp(s, "/__overlay__", len - 1) == 0)) { - /* //__overlay__ */ - rel_path = ""; - rel_path_len = 0; - } else { - /* Symbol refers to something that won't end - * up in the target tree */ - continue; - } - - /* find the fragment index in which the symbol lies */ - ret = fdt_subnode_offset_namelen(fdto, 0, frag_name, - frag_name_len); - /* not found? */ - if (ret < 0) - return -FDT_ERR_BADOVERLAY; - fragment = ret; - - /* an __overlay__ subnode must exist */ - ret = fdt_subnode_offset(fdto, fragment, "__overlay__"); - if (ret < 0) - return -FDT_ERR_BADOVERLAY; - - /* get the target of the fragment */ - ret = overlay_get_target(fdt, fdto, fragment, &target_path); - if (ret < 0) - return ret; - target = ret; - - /* if we have a target path use */ - if (!target_path) { - ret = get_path_len(fdt, target); - if (ret < 0) - return ret; - len = ret; - } else { - len = strlen(target_path); - } - - ret = fdt_setprop_placeholder(fdt, root_sym, name, - len + (len > 1) + rel_path_len + 1, &p); - if (ret < 0) - return ret; - - if (!target_path) { - /* again in case setprop_placeholder changed it */ - ret = overlay_get_target(fdt, fdto, fragment, &target_path); - if (ret < 0) - return ret; - target = ret; - } - - buf = p; - if (len > 1) { /* target is not root */ - if (!target_path) { - ret = fdt_get_path(fdt, target, buf, len + 1); - if (ret < 0) - return ret; - } else - memcpy(buf, target_path, len + 1); - - } else - len--; - - buf[len] = '/'; - memcpy(buf + len + 1, rel_path, rel_path_len); - buf[len + 1 + rel_path_len] = '\0'; - } - - return 0; -} - -int fdt_overlay_apply(void *fdt, void *fdto) -{ - uint32_t delta; - int ret; - - FDT_RO_PROBE(fdt); - FDT_RO_PROBE(fdto); - - ret = fdt_find_max_phandle(fdt, &delta); - if (ret) - goto err; - - ret = overlay_adjust_local_phandles(fdto, delta); - if (ret) - goto err; - - ret = overlay_update_local_references(fdto, delta); - if (ret) - goto err; - - ret = overlay_fixup_phandles(fdt, fdto); - if (ret) - goto err; - - ret = overlay_merge(fdt, fdto); - if (ret) - goto err; - - ret = overlay_symbol_update(fdt, fdto); - if (ret) - goto err; - - /* - * The overlay has been damaged, erase its magic. - */ - fdt_set_magic(fdto, ~0); - - return 0; - -err: - /* - * The overlay might have been damaged, erase its magic. - */ - fdt_set_magic(fdto, ~0); - - /* - * The base device tree might have been damaged, erase its - * magic. - */ - fdt_set_magic(fdt, ~0); - - return ret; -} diff --git a/libfdt-binding/libfdt/fdt_ro.c b/libfdt-binding/libfdt/fdt_ro.c deleted file mode 100644 index 17584da25760b0ecb4b821335a0e4df61977b85f..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/fdt_ro.c +++ /dev/null @@ -1,859 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) -/* - * libfdt - Flat Device Tree manipulation - * Copyright (C) 2006 David Gibson, IBM Corporation. - */ -#include "libfdt_env.h" - -#include -#include - -#include "libfdt_internal.h" - -static int fdt_nodename_eq_(const void *fdt, int offset, - const char *s, int len) -{ - int olen; - const char *p = fdt_get_name(fdt, offset, &olen); - - if (!p || olen < len) - /* short match */ - return 0; - - if (memcmp(p, s, len) != 0) - return 0; - - if (p[len] == '\0') - return 1; - else if (!memchr(s, '@', len) && (p[len] == '@')) - return 1; - else - return 0; -} - -const char *fdt_get_string(const void *fdt, int stroffset, int *lenp) -{ - int32_t totalsize; - uint32_t absoffset; - size_t len; - int err; - const char *s, *n; - - if (can_assume(VALID_INPUT)) { - s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; - - if (lenp) - *lenp = strlen(s); - return s; - } - totalsize = fdt_ro_probe_(fdt); - err = totalsize; - if (totalsize < 0) - goto fail; - - err = -FDT_ERR_BADOFFSET; - absoffset = stroffset + fdt_off_dt_strings(fdt); - if (absoffset >= (unsigned)totalsize) - goto fail; - len = totalsize - absoffset; - - if (fdt_magic(fdt) == FDT_MAGIC) { - if (stroffset < 0) - goto fail; - if (can_assume(LATEST) || fdt_version(fdt) >= 17) { - if ((unsigned)stroffset >= fdt_size_dt_strings(fdt)) - goto fail; - if ((fdt_size_dt_strings(fdt) - stroffset) < len) - len = fdt_size_dt_strings(fdt) - stroffset; - } - } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { - unsigned int sw_stroffset = -stroffset; - - if ((stroffset >= 0) || - (sw_stroffset > fdt_size_dt_strings(fdt))) - goto fail; - if (sw_stroffset < len) - len = sw_stroffset; - } else { - err = -FDT_ERR_INTERNAL; - goto fail; - } - - s = (const char *)fdt + absoffset; - n = memchr(s, '\0', len); - if (!n) { - /* missing terminating NULL */ - err = -FDT_ERR_TRUNCATED; - goto fail; - } - - if (lenp) - *lenp = n - s; - return s; - -fail: - if (lenp) - *lenp = err; - return NULL; -} - -const char *fdt_string(const void *fdt, int stroffset) -{ - return fdt_get_string(fdt, stroffset, NULL); -} - -static int fdt_string_eq_(const void *fdt, int stroffset, - const char *s, int len) -{ - int slen; - const char *p = fdt_get_string(fdt, stroffset, &slen); - - return p && (slen == len) && (memcmp(p, s, len) == 0); -} - -int fdt_find_max_phandle(const void *fdt, uint32_t *phandle) -{ - uint32_t max = 0; - int offset = -1; - - while (true) { - uint32_t value; - - offset = fdt_next_node(fdt, offset, NULL); - if (offset < 0) { - if (offset == -FDT_ERR_NOTFOUND) - break; - - return offset; - } - - value = fdt_get_phandle(fdt, offset); - - if (value > max) - max = value; - } - - if (phandle) - *phandle = max; - - return 0; -} - -int fdt_generate_phandle(const void *fdt, uint32_t *phandle) -{ - uint32_t max; - int err; - - err = fdt_find_max_phandle(fdt, &max); - if (err < 0) - return err; - - if (max == FDT_MAX_PHANDLE) - return -FDT_ERR_NOPHANDLES; - - if (phandle) - *phandle = max + 1; - - return 0; -} - -static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) -{ - unsigned int offset = n * sizeof(struct fdt_reserve_entry); - unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset; - - if (!can_assume(VALID_INPUT)) { - if (absoffset < fdt_off_mem_rsvmap(fdt)) - return NULL; - if (absoffset > fdt_totalsize(fdt) - - sizeof(struct fdt_reserve_entry)) - return NULL; - } - return fdt_mem_rsv_(fdt, n); -} - -int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) -{ - const struct fdt_reserve_entry *re; - - FDT_RO_PROBE(fdt); - re = fdt_mem_rsv(fdt, n); - if (!can_assume(VALID_INPUT) && !re) - return -FDT_ERR_BADOFFSET; - - *address = fdt64_ld_(&re->address); - *size = fdt64_ld_(&re->size); - return 0; -} - -int fdt_num_mem_rsv(const void *fdt) -{ - int i; - const struct fdt_reserve_entry *re; - - for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { - if (fdt64_ld_(&re->size) == 0) - return i; - } - return -FDT_ERR_TRUNCATED; -} - -static int nextprop_(const void *fdt, int offset) -{ - uint32_t tag; - int nextoffset; - - do { - tag = fdt_next_tag(fdt, offset, &nextoffset); - - switch (tag) { - case FDT_END: - if (nextoffset >= 0) - return -FDT_ERR_BADSTRUCTURE; - else - return nextoffset; - - case FDT_PROP: - return offset; - } - offset = nextoffset; - } while (tag == FDT_NOP); - - return -FDT_ERR_NOTFOUND; -} - -int fdt_subnode_offset_namelen(const void *fdt, int offset, - const char *name, int namelen) -{ - int depth; - - FDT_RO_PROBE(fdt); - - for (depth = 0; - (offset >= 0) && (depth >= 0); - offset = fdt_next_node(fdt, offset, &depth)) - if ((depth == 1) - && fdt_nodename_eq_(fdt, offset, name, namelen)) - return offset; - - if (depth < 0) - return -FDT_ERR_NOTFOUND; - return offset; /* error */ -} - -int fdt_subnode_offset(const void *fdt, int parentoffset, - const char *name) -{ - return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); -} - -int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) -{ - const char *end = path + namelen; - const char *p = path; - int offset = 0; - - FDT_RO_PROBE(fdt); - - /* see if we have an alias */ - if (*path != '/') { - const char *q = memchr(path, '/', end - p); - - if (!q) - q = end; - - p = fdt_get_alias_namelen(fdt, p, q - p); - if (!p) - return -FDT_ERR_BADPATH; - offset = fdt_path_offset(fdt, p); - - p = q; - } - - while (p < end) { - const char *q; - - while (*p == '/') { - p++; - if (p == end) - return offset; - } - q = memchr(p, '/', end - p); - if (! q) - q = end; - - offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); - if (offset < 0) - return offset; - - p = q; - } - - return offset; -} - -int fdt_path_offset(const void *fdt, const char *path) -{ - return fdt_path_offset_namelen(fdt, path, strlen(path)); -} - -const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) -{ - const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); - const char *nameptr; - int err; - - if (((err = fdt_ro_probe_(fdt)) < 0) - || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) - goto fail; - - nameptr = nh->name; - - if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { - /* - * For old FDT versions, match the naming conventions of V16: - * give only the leaf name (after all /). The actual tree - * contents are loosely checked. - */ - const char *leaf; - leaf = strrchr(nameptr, '/'); - if (leaf == NULL) { - err = -FDT_ERR_BADSTRUCTURE; - goto fail; - } - nameptr = leaf+1; - } - - if (len) - *len = strlen(nameptr); - - return nameptr; - - fail: - if (len) - *len = err; - return NULL; -} - -int fdt_first_property_offset(const void *fdt, int nodeoffset) -{ - int offset; - - if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) - return offset; - - return nextprop_(fdt, offset); -} - -int fdt_next_property_offset(const void *fdt, int offset) -{ - if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0) - return offset; - - return nextprop_(fdt, offset); -} - -static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, - int offset, - int *lenp) -{ - int err; - const struct fdt_property *prop; - - if (!can_assume(VALID_INPUT) && - (err = fdt_check_prop_offset_(fdt, offset)) < 0) { - if (lenp) - *lenp = err; - return NULL; - } - - prop = fdt_offset_ptr_(fdt, offset); - - if (lenp) - *lenp = fdt32_ld_(&prop->len); - - return prop; -} - -const struct fdt_property *fdt_get_property_by_offset(const void *fdt, - int offset, - int *lenp) -{ - /* Prior to version 16, properties may need realignment - * and this API does not work. fdt_getprop_*() will, however. */ - - if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { - if (lenp) - *lenp = -FDT_ERR_BADVERSION; - return NULL; - } - - return fdt_get_property_by_offset_(fdt, offset, lenp); -} - -static const struct fdt_property *fdt_get_property_namelen_(const void *fdt, - int offset, - const char *name, - int namelen, - int *lenp, - int *poffset) -{ - for (offset = fdt_first_property_offset(fdt, offset); - (offset >= 0); - (offset = fdt_next_property_offset(fdt, offset))) { - const struct fdt_property *prop; - - prop = fdt_get_property_by_offset_(fdt, offset, lenp); - if (!can_assume(LIBFDT_FLAWLESS) && !prop) { - offset = -FDT_ERR_INTERNAL; - break; - } - if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff), - name, namelen)) { - if (poffset) - *poffset = offset; - return prop; - } - } - - if (lenp) - *lenp = offset; - return NULL; -} - - -const struct fdt_property *fdt_get_property_namelen(const void *fdt, - int offset, - const char *name, - int namelen, int *lenp) -{ - /* Prior to version 16, properties may need realignment - * and this API does not work. fdt_getprop_*() will, however. */ - if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { - if (lenp) - *lenp = -FDT_ERR_BADVERSION; - return NULL; - } - - return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp, - NULL); -} - - -const struct fdt_property *fdt_get_property(const void *fdt, - int nodeoffset, - const char *name, int *lenp) -{ - return fdt_get_property_namelen(fdt, nodeoffset, name, - strlen(name), lenp); -} - -const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, - const char *name, int namelen, int *lenp) -{ - int poffset; - const struct fdt_property *prop; - - prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, - &poffset); - if (!prop) - return NULL; - - /* Handle realignment */ - if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && - (poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8) - return prop->data + 4; - return prop->data; -} - -const void *fdt_getprop_by_offset(const void *fdt, int offset, - const char **namep, int *lenp) -{ - const struct fdt_property *prop; - - prop = fdt_get_property_by_offset_(fdt, offset, lenp); - if (!prop) - return NULL; - if (namep) { - const char *name; - int namelen; - - if (!can_assume(VALID_INPUT)) { - name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff), - &namelen); - if (!name) { - if (lenp) - *lenp = namelen; - return NULL; - } - *namep = name; - } else { - *namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff)); - } - } - - /* Handle realignment */ - if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && - (offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8) - return prop->data + 4; - return prop->data; -} - -const void *fdt_getprop(const void *fdt, int nodeoffset, - const char *name, int *lenp) -{ - return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); -} - -uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) -{ - const fdt32_t *php; - int len; - - /* FIXME: This is a bit sub-optimal, since we potentially scan - * over all the properties twice. */ - php = fdt_getprop(fdt, nodeoffset, "phandle", &len); - if (!php || (len != sizeof(*php))) { - php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); - if (!php || (len != sizeof(*php))) - return 0; - } - - return fdt32_ld_(php); -} - -const char *fdt_get_alias_namelen(const void *fdt, - const char *name, int namelen) -{ - int aliasoffset; - - aliasoffset = fdt_path_offset(fdt, "/aliases"); - if (aliasoffset < 0) - return NULL; - - return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); -} - -const char *fdt_get_alias(const void *fdt, const char *name) -{ - return fdt_get_alias_namelen(fdt, name, strlen(name)); -} - -int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) -{ - int pdepth = 0, p = 0; - int offset, depth, namelen; - const char *name; - - FDT_RO_PROBE(fdt); - - if (buflen < 2) - return -FDT_ERR_NOSPACE; - - for (offset = 0, depth = 0; - (offset >= 0) && (offset <= nodeoffset); - offset = fdt_next_node(fdt, offset, &depth)) { - while (pdepth > depth) { - do { - p--; - } while (buf[p-1] != '/'); - pdepth--; - } - - if (pdepth >= depth) { - name = fdt_get_name(fdt, offset, &namelen); - if (!name) - return namelen; - if ((p + namelen + 1) <= buflen) { - memcpy(buf + p, name, namelen); - p += namelen; - buf[p++] = '/'; - pdepth++; - } - } - - if (offset == nodeoffset) { - if (pdepth < (depth + 1)) - return -FDT_ERR_NOSPACE; - - if (p > 1) /* special case so that root path is "/", not "" */ - p--; - buf[p] = '\0'; - return 0; - } - } - - if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) - return -FDT_ERR_BADOFFSET; - else if (offset == -FDT_ERR_BADOFFSET) - return -FDT_ERR_BADSTRUCTURE; - - return offset; /* error from fdt_next_node() */ -} - -int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, - int supernodedepth, int *nodedepth) -{ - int offset, depth; - int supernodeoffset = -FDT_ERR_INTERNAL; - - FDT_RO_PROBE(fdt); - - if (supernodedepth < 0) - return -FDT_ERR_NOTFOUND; - - for (offset = 0, depth = 0; - (offset >= 0) && (offset <= nodeoffset); - offset = fdt_next_node(fdt, offset, &depth)) { - if (depth == supernodedepth) - supernodeoffset = offset; - - if (offset == nodeoffset) { - if (nodedepth) - *nodedepth = depth; - - if (supernodedepth > depth) - return -FDT_ERR_NOTFOUND; - else - return supernodeoffset; - } - } - - if (!can_assume(VALID_INPUT)) { - if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) - return -FDT_ERR_BADOFFSET; - else if (offset == -FDT_ERR_BADOFFSET) - return -FDT_ERR_BADSTRUCTURE; - } - - return offset; /* error from fdt_next_node() */ -} - -int fdt_node_depth(const void *fdt, int nodeoffset) -{ - int nodedepth; - int err; - - err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); - if (err) - return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err : - -FDT_ERR_INTERNAL; - return nodedepth; -} - -int fdt_parent_offset(const void *fdt, int nodeoffset) -{ - int nodedepth = fdt_node_depth(fdt, nodeoffset); - - if (nodedepth < 0) - return nodedepth; - return fdt_supernode_atdepth_offset(fdt, nodeoffset, - nodedepth - 1, NULL); -} - -int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, - const char *propname, - const void *propval, int proplen) -{ - int offset; - const void *val; - int len; - - FDT_RO_PROBE(fdt); - - /* FIXME: The algorithm here is pretty horrible: we scan each - * property of a node in fdt_getprop(), then if that didn't - * find what we want, we scan over them again making our way - * to the next node. Still it's the easiest to implement - * approach; performance can come later. */ - for (offset = fdt_next_node(fdt, startoffset, NULL); - offset >= 0; - offset = fdt_next_node(fdt, offset, NULL)) { - val = fdt_getprop(fdt, offset, propname, &len); - if (val && (len == proplen) - && (memcmp(val, propval, len) == 0)) - return offset; - } - - return offset; /* error from fdt_next_node() */ -} - -int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) -{ - int offset; - - if ((phandle == 0) || (phandle == ~0U)) - return -FDT_ERR_BADPHANDLE; - - FDT_RO_PROBE(fdt); - - /* FIXME: The algorithm here is pretty horrible: we - * potentially scan each property of a node in - * fdt_get_phandle(), then if that didn't find what - * we want, we scan over them again making our way to the next - * node. Still it's the easiest to implement approach; - * performance can come later. */ - for (offset = fdt_next_node(fdt, -1, NULL); - offset >= 0; - offset = fdt_next_node(fdt, offset, NULL)) { - if (fdt_get_phandle(fdt, offset) == phandle) - return offset; - } - - return offset; /* error from fdt_next_node() */ -} - -int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) -{ - int len = strlen(str); - const char *p; - - while (listlen >= len) { - if (memcmp(str, strlist, len+1) == 0) - return 1; - p = memchr(strlist, '\0', listlen); - if (!p) - return 0; /* malformed strlist.. */ - listlen -= (p-strlist) + 1; - strlist = p + 1; - } - return 0; -} - -int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) -{ - const char *list, *end; - int length, count = 0; - - list = fdt_getprop(fdt, nodeoffset, property, &length); - if (!list) - return length; - - end = list + length; - - while (list < end) { - length = strnlen(list, end - list) + 1; - - /* Abort if the last string isn't properly NUL-terminated. */ - if (list + length > end) - return -FDT_ERR_BADVALUE; - - list += length; - count++; - } - - return count; -} - -int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, - const char *string) -{ - int length, len, idx = 0; - const char *list, *end; - - list = fdt_getprop(fdt, nodeoffset, property, &length); - if (!list) - return length; - - len = strlen(string) + 1; - end = list + length; - - while (list < end) { - length = strnlen(list, end - list) + 1; - - /* Abort if the last string isn't properly NUL-terminated. */ - if (list + length > end) - return -FDT_ERR_BADVALUE; - - if (length == len && memcmp(list, string, length) == 0) - return idx; - - list += length; - idx++; - } - - return -FDT_ERR_NOTFOUND; -} - -const char *fdt_stringlist_get(const void *fdt, int nodeoffset, - const char *property, int idx, - int *lenp) -{ - const char *list, *end; - int length; - - list = fdt_getprop(fdt, nodeoffset, property, &length); - if (!list) { - if (lenp) - *lenp = length; - - return NULL; - } - - end = list + length; - - while (list < end) { - length = strnlen(list, end - list) + 1; - - /* Abort if the last string isn't properly NUL-terminated. */ - if (list + length > end) { - if (lenp) - *lenp = -FDT_ERR_BADVALUE; - - return NULL; - } - - if (idx == 0) { - if (lenp) - *lenp = length - 1; - - return list; - } - - list += length; - idx--; - } - - if (lenp) - *lenp = -FDT_ERR_NOTFOUND; - - return NULL; -} - -int fdt_node_check_compatible(const void *fdt, int nodeoffset, - const char *compatible) -{ - const void *prop; - int len; - - prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); - if (!prop) - return len; - - return !fdt_stringlist_contains(prop, len, compatible); -} - -int fdt_node_offset_by_compatible(const void *fdt, int startoffset, - const char *compatible) -{ - int offset, err; - - FDT_RO_PROBE(fdt); - - /* FIXME: The algorithm here is pretty horrible: we scan each - * property of a node in fdt_node_check_compatible(), then if - * that didn't find what we want, we scan over them again - * making our way to the next node. Still it's the easiest to - * implement approach; performance can come later. */ - for (offset = fdt_next_node(fdt, startoffset, NULL); - offset >= 0; - offset = fdt_next_node(fdt, offset, NULL)) { - err = fdt_node_check_compatible(fdt, offset, compatible); - if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) - return err; - else if (err == 0) - return offset; - } - - return offset; /* error from fdt_next_node() */ -} diff --git a/libfdt-binding/libfdt/fdt_rw.c b/libfdt-binding/libfdt/fdt_rw.c deleted file mode 100644 index 2fbb545f9cb608becf46b03a7be4de793f2768f5..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/fdt_rw.c +++ /dev/null @@ -1,497 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) -/* - * libfdt - Flat Device Tree manipulation - * Copyright (C) 2006 David Gibson, IBM Corporation. - */ -#include "libfdt_env.h" - -#include -#include - -#include "libfdt_internal.h" - -static int fdt_blocks_misordered_(const void *fdt, - int mem_rsv_size, int struct_size) -{ - return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8)) - || (fdt_off_dt_struct(fdt) < - (fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) - || (fdt_off_dt_strings(fdt) < - (fdt_off_dt_struct(fdt) + struct_size)) - || (fdt_totalsize(fdt) < - (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); -} - -static int fdt_rw_probe_(void *fdt) -{ - if (can_assume(VALID_DTB)) - return 0; - FDT_RO_PROBE(fdt); - - if (!can_assume(LATEST) && fdt_version(fdt) < 17) - return -FDT_ERR_BADVERSION; - if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry), - fdt_size_dt_struct(fdt))) - return -FDT_ERR_BADLAYOUT; - if (!can_assume(LATEST) && fdt_version(fdt) > 17) - fdt_set_version(fdt, 17); - - return 0; -} - -#define FDT_RW_PROBE(fdt) \ - { \ - int err_; \ - if ((err_ = fdt_rw_probe_(fdt)) != 0) \ - return err_; \ - } - -static inline unsigned int fdt_data_size_(void *fdt) -{ - return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); -} - -static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen) -{ - char *p = splicepoint; - unsigned int dsize = fdt_data_size_(fdt); - size_t soff = p - (char *)fdt; - - if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize)) - return -FDT_ERR_BADOFFSET; - if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen)) - return -FDT_ERR_BADOFFSET; - if (dsize - oldlen + newlen > fdt_totalsize(fdt)) - return -FDT_ERR_NOSPACE; - memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (p + oldlen)); - return 0; -} - -static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p, - int oldn, int newn) -{ - int delta = (newn - oldn) * sizeof(*p); - int err; - err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p)); - if (err) - return err; - fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta); - fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); - return 0; -} - -static int fdt_splice_struct_(void *fdt, void *p, - int oldlen, int newlen) -{ - int delta = newlen - oldlen; - int err; - - if ((err = fdt_splice_(fdt, p, oldlen, newlen))) - return err; - - fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta); - fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); - return 0; -} - -/* Must only be used to roll back in case of error */ -static void fdt_del_last_string_(void *fdt, const char *s) -{ - int newlen = strlen(s) + 1; - - fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen); -} - -static int fdt_splice_string_(void *fdt, int newlen) -{ - void *p = (char *)fdt - + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); - int err; - - if ((err = fdt_splice_(fdt, p, 0, newlen))) - return err; - - fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen); - return 0; -} - -/** - * fdt_find_add_string_() - Find or allocate a string - * - * @fdt: pointer to the device tree to check/adjust - * @s: string to find/add - * @allocated: Set to 0 if the string was found, 1 if not found and so - * allocated. Ignored if can_assume(NO_ROLLBACK) - * @return offset of string in the string table (whether found or added) - */ -static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) -{ - char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); - const char *p; - char *new; - int len = strlen(s) + 1; - int err; - - if (!can_assume(NO_ROLLBACK)) - *allocated = 0; - - p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s); - if (p) - /* found it */ - return (p - strtab); - - new = strtab + fdt_size_dt_strings(fdt); - err = fdt_splice_string_(fdt, len); - if (err) - return err; - - if (!can_assume(NO_ROLLBACK)) - *allocated = 1; - - memcpy(new, s, len); - return (new - strtab); -} - -int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) -{ - struct fdt_reserve_entry *re; - int err; - - FDT_RW_PROBE(fdt); - - re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt)); - err = fdt_splice_mem_rsv_(fdt, re, 0, 1); - if (err) - return err; - - re->address = cpu_to_fdt64(address); - re->size = cpu_to_fdt64(size); - return 0; -} - -int fdt_del_mem_rsv(void *fdt, int n) -{ - struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n); - - FDT_RW_PROBE(fdt); - - if (n >= fdt_num_mem_rsv(fdt)) - return -FDT_ERR_NOTFOUND; - - return fdt_splice_mem_rsv_(fdt, re, 1, 0); -} - -static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name, - int len, struct fdt_property **prop) -{ - int oldlen; - int err; - - *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); - if (!*prop) - return oldlen; - - if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen), - FDT_TAGALIGN(len)))) - return err; - - (*prop)->len = cpu_to_fdt32(len); - return 0; -} - -static int fdt_add_property_(void *fdt, int nodeoffset, const char *name, - int len, struct fdt_property **prop) -{ - int proplen; - int nextoffset; - int namestroff; - int err; - int allocated; - - if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) - return nextoffset; - - namestroff = fdt_find_add_string_(fdt, name, &allocated); - if (namestroff < 0) - return namestroff; - - *prop = fdt_offset_ptr_w_(fdt, nextoffset); - proplen = sizeof(**prop) + FDT_TAGALIGN(len); - - err = fdt_splice_struct_(fdt, *prop, 0, proplen); - if (err) { - /* Delete the string if we failed to add it */ - if (!can_assume(NO_ROLLBACK) && allocated) - fdt_del_last_string_(fdt, name); - return err; - } - - (*prop)->tag = cpu_to_fdt32(FDT_PROP); - (*prop)->nameoff = cpu_to_fdt32(namestroff); - (*prop)->len = cpu_to_fdt32(len); - return 0; -} - -int fdt_set_name(void *fdt, int nodeoffset, const char *name) -{ - char *namep; - int oldlen, newlen; - int err; - - FDT_RW_PROBE(fdt); - - namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen); - if (!namep) - return oldlen; - - newlen = strlen(name); - - err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1), - FDT_TAGALIGN(newlen+1)); - if (err) - return err; - - memcpy(namep, name, newlen+1); - return 0; -} - -int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, - int len, void **prop_data) -{ - struct fdt_property *prop; - int err; - - FDT_RW_PROBE(fdt); - - err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop); - if (err == -FDT_ERR_NOTFOUND) - err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); - if (err) - return err; - - *prop_data = prop->data; - return 0; -} - -int fdt_setprop(void *fdt, int nodeoffset, const char *name, - const void *val, int len) -{ - void *prop_data; - int err; - - err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data); - if (err) - return err; - - if (len) - memcpy(prop_data, val, len); - return 0; -} - -int fdt_appendprop(void *fdt, int nodeoffset, const char *name, - const void *val, int len) -{ - struct fdt_property *prop; - int err, oldlen, newlen; - - FDT_RW_PROBE(fdt); - - prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); - if (prop) { - newlen = len + oldlen; - err = fdt_splice_struct_(fdt, prop->data, - FDT_TAGALIGN(oldlen), - FDT_TAGALIGN(newlen)); - if (err) - return err; - prop->len = cpu_to_fdt32(newlen); - memcpy(prop->data + oldlen, val, len); - } else { - err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); - if (err) - return err; - memcpy(prop->data, val, len); - } - return 0; -} - -int fdt_delprop(void *fdt, int nodeoffset, const char *name) -{ - struct fdt_property *prop; - int len, proplen; - - FDT_RW_PROBE(fdt); - - prop = fdt_get_property_w(fdt, nodeoffset, name, &len); - if (!prop) - return len; - - proplen = sizeof(*prop) + FDT_TAGALIGN(len); - return fdt_splice_struct_(fdt, prop, proplen, 0); -} - -int fdt_add_subnode_namelen(void *fdt, int parentoffset, - const char *name, int namelen) -{ - struct fdt_node_header *nh; - int offset, nextoffset; - int nodelen; - int err; - uint32_t tag; - fdt32_t *endtag; - - FDT_RW_PROBE(fdt); - - offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); - if (offset >= 0) - return -FDT_ERR_EXISTS; - else if (offset != -FDT_ERR_NOTFOUND) - return offset; - - /* Try to place the new node after the parent's properties */ - tag = fdt_next_tag(fdt, parentoffset, &nextoffset); - /* the fdt_subnode_offset_namelen() should ensure this never hits */ - if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE)) - return -FDT_ERR_INTERNAL; - do { - offset = nextoffset; - tag = fdt_next_tag(fdt, offset, &nextoffset); - } while ((tag == FDT_PROP) || (tag == FDT_NOP)); - - nh = fdt_offset_ptr_w_(fdt, offset); - nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE; - - err = fdt_splice_struct_(fdt, nh, 0, nodelen); - if (err) - return err; - - nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); - memset(nh->name, 0, FDT_TAGALIGN(namelen+1)); - memcpy(nh->name, name, namelen); - endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE); - *endtag = cpu_to_fdt32(FDT_END_NODE); - - return offset; -} - -int fdt_add_subnode(void *fdt, int parentoffset, const char *name) -{ - return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); -} - -int fdt_del_node(void *fdt, int nodeoffset) -{ - int endoffset; - - FDT_RW_PROBE(fdt); - - endoffset = fdt_node_end_offset_(fdt, nodeoffset); - if (endoffset < 0) - return endoffset; - - return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset), - endoffset - nodeoffset, 0); -} - -static void fdt_packblocks_(const char *old, char *new, - int mem_rsv_size, int struct_size) -{ - int mem_rsv_off, struct_off, strings_off; - - mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8); - struct_off = mem_rsv_off + mem_rsv_size; - strings_off = struct_off + struct_size; - - memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size); - fdt_set_off_mem_rsvmap(new, mem_rsv_off); - - memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size); - fdt_set_off_dt_struct(new, struct_off); - fdt_set_size_dt_struct(new, struct_size); - - memmove(new + strings_off, old + fdt_off_dt_strings(old), - fdt_size_dt_strings(old)); - fdt_set_off_dt_strings(new, strings_off); - fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); -} - -int fdt_open_into(const void *fdt, void *buf, int bufsize) -{ - int err; - int mem_rsv_size, struct_size; - int newsize; - const char *fdtstart = fdt; - const char *fdtend = fdtstart + fdt_totalsize(fdt); - char *tmp; - - FDT_RO_PROBE(fdt); - - mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) - * sizeof(struct fdt_reserve_entry); - - if (can_assume(LATEST) || fdt_version(fdt) >= 17) { - struct_size = fdt_size_dt_struct(fdt); - } else if (fdt_version(fdt) == 16) { - struct_size = 0; - while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) - ; - if (struct_size < 0) - return struct_size; - } else { - return -FDT_ERR_BADVERSION; - } - - if (can_assume(LIBFDT_ORDER) || - !fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) { - /* no further work necessary */ - err = fdt_move(fdt, buf, bufsize); - if (err) - return err; - fdt_set_version(buf, 17); - fdt_set_size_dt_struct(buf, struct_size); - fdt_set_totalsize(buf, bufsize); - return 0; - } - - /* Need to reorder */ - newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size - + struct_size + fdt_size_dt_strings(fdt); - - if (bufsize < newsize) - return -FDT_ERR_NOSPACE; - - /* First attempt to build converted tree at beginning of buffer */ - tmp = buf; - /* But if that overlaps with the old tree... */ - if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) { - /* Try right after the old tree instead */ - tmp = (char *)(uintptr_t)fdtend; - if ((tmp + newsize) > ((char *)buf + bufsize)) - return -FDT_ERR_NOSPACE; - } - - fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size); - memmove(buf, tmp, newsize); - - fdt_set_magic(buf, FDT_MAGIC); - fdt_set_totalsize(buf, bufsize); - fdt_set_version(buf, 17); - fdt_set_last_comp_version(buf, 16); - fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); - - return 0; -} - -int fdt_pack(void *fdt) -{ - int mem_rsv_size; - - FDT_RW_PROBE(fdt); - - mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) - * sizeof(struct fdt_reserve_entry); - fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); - fdt_set_totalsize(fdt, fdt_data_size_(fdt)); - - return 0; -} diff --git a/libfdt-binding/libfdt/fdt_strerror.c b/libfdt-binding/libfdt/fdt_strerror.c deleted file mode 100644 index b4356931b06d6a6d01ffec217ce9b3e13ba5cf3a..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/fdt_strerror.c +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) -/* - * libfdt - Flat Device Tree manipulation - * Copyright (C) 2006 David Gibson, IBM Corporation. - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "libfdt_env.h" - -#include -#include - -#include "libfdt_internal.h" - -struct fdt_errtabent { - const char *str; -}; - -#define FDT_ERRTABENT(val) \ - [(val)] = { .str = #val, } - -static struct fdt_errtabent fdt_errtable[] = { - FDT_ERRTABENT(FDT_ERR_NOTFOUND), - FDT_ERRTABENT(FDT_ERR_EXISTS), - FDT_ERRTABENT(FDT_ERR_NOSPACE), - - FDT_ERRTABENT(FDT_ERR_BADOFFSET), - FDT_ERRTABENT(FDT_ERR_BADPATH), - FDT_ERRTABENT(FDT_ERR_BADPHANDLE), - FDT_ERRTABENT(FDT_ERR_BADSTATE), - - FDT_ERRTABENT(FDT_ERR_TRUNCATED), - FDT_ERRTABENT(FDT_ERR_BADMAGIC), - FDT_ERRTABENT(FDT_ERR_BADVERSION), - FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), - FDT_ERRTABENT(FDT_ERR_BADLAYOUT), - FDT_ERRTABENT(FDT_ERR_INTERNAL), - FDT_ERRTABENT(FDT_ERR_BADNCELLS), - FDT_ERRTABENT(FDT_ERR_BADVALUE), - FDT_ERRTABENT(FDT_ERR_BADOVERLAY), - FDT_ERRTABENT(FDT_ERR_NOPHANDLES), - FDT_ERRTABENT(FDT_ERR_BADFLAGS), -}; -#define FDT_ERRTABSIZE ((int)(sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))) - -const char *fdt_strerror(int errval) -{ - if (errval > 0) - return ""; - else if (errval == 0) - return ""; - else if (-errval < FDT_ERRTABSIZE) { - const char *s = fdt_errtable[-errval].str; - - if (s) - return s; - } - - return ""; -} diff --git a/libfdt-binding/libfdt/fdt_sw.c b/libfdt-binding/libfdt/fdt_sw.c deleted file mode 100644 index 4c569ee7eb0d1810d385aaaa3a11172c512e783e..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/fdt_sw.c +++ /dev/null @@ -1,384 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) -/* - * libfdt - Flat Device Tree manipulation - * Copyright (C) 2006 David Gibson, IBM Corporation. - */ -#include "libfdt_env.h" - -#include -#include - -#include "libfdt_internal.h" - -static int fdt_sw_probe_(void *fdt) -{ - if (!can_assume(VALID_INPUT)) { - if (fdt_magic(fdt) == FDT_MAGIC) - return -FDT_ERR_BADSTATE; - else if (fdt_magic(fdt) != FDT_SW_MAGIC) - return -FDT_ERR_BADMAGIC; - } - - return 0; -} - -#define FDT_SW_PROBE(fdt) \ - { \ - int err; \ - if ((err = fdt_sw_probe_(fdt)) != 0) \ - return err; \ - } - -/* 'memrsv' state: Initial state after fdt_create() - * - * Allowed functions: - * fdt_add_reservemap_entry() - * fdt_finish_reservemap() [moves to 'struct' state] - */ -static int fdt_sw_probe_memrsv_(void *fdt) -{ - int err = fdt_sw_probe_(fdt); - if (err) - return err; - - if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0) - return -FDT_ERR_BADSTATE; - return 0; -} - -#define FDT_SW_PROBE_MEMRSV(fdt) \ - { \ - int err; \ - if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \ - return err; \ - } - -/* 'struct' state: Enter this state after fdt_finish_reservemap() - * - * Allowed functions: - * fdt_begin_node() - * fdt_end_node() - * fdt_property*() - * fdt_finish() [moves to 'complete' state] - */ -static int fdt_sw_probe_struct_(void *fdt) -{ - int err = fdt_sw_probe_(fdt); - if (err) - return err; - - if (!can_assume(VALID_INPUT) && - fdt_off_dt_strings(fdt) != fdt_totalsize(fdt)) - return -FDT_ERR_BADSTATE; - return 0; -} - -#define FDT_SW_PROBE_STRUCT(fdt) \ - { \ - int err; \ - if ((err = fdt_sw_probe_struct_(fdt)) != 0) \ - return err; \ - } - -static inline uint32_t sw_flags(void *fdt) -{ - /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */ - return fdt_last_comp_version(fdt); -} - -/* 'complete' state: Enter this state after fdt_finish() - * - * Allowed functions: none - */ - -static void *fdt_grab_space_(void *fdt, size_t len) -{ - unsigned int offset = fdt_size_dt_struct(fdt); - unsigned int spaceleft; - - spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) - - fdt_size_dt_strings(fdt); - - if ((offset + len < offset) || (offset + len > spaceleft)) - return NULL; - - fdt_set_size_dt_struct(fdt, offset + len); - return fdt_offset_ptr_w_(fdt, offset); -} - -int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags) -{ - const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header), - sizeof(struct fdt_reserve_entry)); - void *fdt = buf; - - if (bufsize < hdrsize) - return -FDT_ERR_NOSPACE; - - if (flags & ~FDT_CREATE_FLAGS_ALL) - return -FDT_ERR_BADFLAGS; - - memset(buf, 0, bufsize); - - /* - * magic and last_comp_version keep intermediate state during the fdt - * creation process, which is replaced with the proper FDT format by - * fdt_finish(). - * - * flags should be accessed with sw_flags(). - */ - fdt_set_magic(fdt, FDT_SW_MAGIC); - fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); - fdt_set_last_comp_version(fdt, flags); - - fdt_set_totalsize(fdt, bufsize); - - fdt_set_off_mem_rsvmap(fdt, hdrsize); - fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); - fdt_set_off_dt_strings(fdt, 0); - - return 0; -} - -int fdt_create(void *buf, int bufsize) -{ - return fdt_create_with_flags(buf, bufsize, 0); -} - -int fdt_resize(void *fdt, void *buf, int bufsize) -{ - size_t headsize, tailsize; - char *oldtail, *newtail; - - FDT_SW_PROBE(fdt); - - if (bufsize < 0) - return -FDT_ERR_NOSPACE; - - headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); - tailsize = fdt_size_dt_strings(fdt); - - if (!can_assume(VALID_DTB) && - headsize + tailsize > fdt_totalsize(fdt)) - return -FDT_ERR_INTERNAL; - - if ((headsize + tailsize) > (unsigned)bufsize) - return -FDT_ERR_NOSPACE; - - oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; - newtail = (char *)buf + bufsize - tailsize; - - /* Two cases to avoid clobbering data if the old and new - * buffers partially overlap */ - if (buf <= fdt) { - memmove(buf, fdt, headsize); - memmove(newtail, oldtail, tailsize); - } else { - memmove(newtail, oldtail, tailsize); - memmove(buf, fdt, headsize); - } - - fdt_set_totalsize(buf, bufsize); - if (fdt_off_dt_strings(buf)) - fdt_set_off_dt_strings(buf, bufsize); - - return 0; -} - -int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) -{ - struct fdt_reserve_entry *re; - int offset; - - FDT_SW_PROBE_MEMRSV(fdt); - - offset = fdt_off_dt_struct(fdt); - if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) - return -FDT_ERR_NOSPACE; - - re = (struct fdt_reserve_entry *)((char *)fdt + offset); - re->address = cpu_to_fdt64(addr); - re->size = cpu_to_fdt64(size); - - fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); - - return 0; -} - -int fdt_finish_reservemap(void *fdt) -{ - int err = fdt_add_reservemap_entry(fdt, 0, 0); - - if (err) - return err; - - fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt)); - return 0; -} - -int fdt_begin_node(void *fdt, const char *name) -{ - struct fdt_node_header *nh; - int namelen; - - FDT_SW_PROBE_STRUCT(fdt); - - namelen = strlen(name) + 1; - nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); - if (! nh) - return -FDT_ERR_NOSPACE; - - nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); - memcpy(nh->name, name, namelen); - return 0; -} - -int fdt_end_node(void *fdt) -{ - fdt32_t *en; - - FDT_SW_PROBE_STRUCT(fdt); - - en = fdt_grab_space_(fdt, FDT_TAGSIZE); - if (! en) - return -FDT_ERR_NOSPACE; - - *en = cpu_to_fdt32(FDT_END_NODE); - return 0; -} - -static int fdt_add_string_(void *fdt, const char *s) -{ - char *strtab = (char *)fdt + fdt_totalsize(fdt); - unsigned int strtabsize = fdt_size_dt_strings(fdt); - unsigned int len = strlen(s) + 1; - unsigned int struct_top, offset; - - offset = strtabsize + len; - struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); - if (fdt_totalsize(fdt) - offset < struct_top) - return 0; /* no more room :( */ - - memcpy(strtab - offset, s, len); - fdt_set_size_dt_strings(fdt, strtabsize + len); - return -offset; -} - -/* Must only be used to roll back in case of error */ -static void fdt_del_last_string_(void *fdt, const char *s) -{ - int strtabsize = fdt_size_dt_strings(fdt); - int len = strlen(s) + 1; - - fdt_set_size_dt_strings(fdt, strtabsize - len); -} - -static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) -{ - char *strtab = (char *)fdt + fdt_totalsize(fdt); - int strtabsize = fdt_size_dt_strings(fdt); - const char *p; - - *allocated = 0; - - p = fdt_find_string_(strtab - strtabsize, strtabsize, s); - if (p) - return p - strtab; - - *allocated = 1; - - return fdt_add_string_(fdt, s); -} - -int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) -{ - struct fdt_property *prop; - int nameoff; - int allocated; - - FDT_SW_PROBE_STRUCT(fdt); - - /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */ - if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) { - allocated = 1; - nameoff = fdt_add_string_(fdt, name); - } else { - nameoff = fdt_find_add_string_(fdt, name, &allocated); - } - if (nameoff == 0) - return -FDT_ERR_NOSPACE; - - prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); - if (! prop) { - if (allocated) - fdt_del_last_string_(fdt, name); - return -FDT_ERR_NOSPACE; - } - - prop->tag = cpu_to_fdt32(FDT_PROP); - prop->nameoff = cpu_to_fdt32(nameoff); - prop->len = cpu_to_fdt32(len); - *valp = prop->data; - return 0; -} - -int fdt_property(void *fdt, const char *name, const void *val, int len) -{ - void *ptr; - int ret; - - ret = fdt_property_placeholder(fdt, name, len, &ptr); - if (ret) - return ret; - memcpy(ptr, val, len); - return 0; -} - -int fdt_finish(void *fdt) -{ - char *p = (char *)fdt; - fdt32_t *end; - int oldstroffset, newstroffset; - uint32_t tag; - int offset, nextoffset; - - FDT_SW_PROBE_STRUCT(fdt); - - /* Add terminator */ - end = fdt_grab_space_(fdt, sizeof(*end)); - if (! end) - return -FDT_ERR_NOSPACE; - *end = cpu_to_fdt32(FDT_END); - - /* Relocate the string table */ - oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); - newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); - memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); - fdt_set_off_dt_strings(fdt, newstroffset); - - /* Walk the structure, correcting string offsets */ - offset = 0; - while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { - if (tag == FDT_PROP) { - struct fdt_property *prop = - fdt_offset_ptr_w_(fdt, offset); - int nameoff; - - nameoff = fdt32_to_cpu(prop->nameoff); - nameoff += fdt_size_dt_strings(fdt); - prop->nameoff = cpu_to_fdt32(nameoff); - } - offset = nextoffset; - } - if (nextoffset < 0) - return nextoffset; - - /* Finally, adjust the header */ - fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); - - /* And fix up fields that were keeping intermediate state. */ - fdt_set_last_comp_version(fdt, FDT_LAST_COMPATIBLE_VERSION); - fdt_set_magic(fdt, FDT_MAGIC); - - return 0; -} diff --git a/libfdt-binding/libfdt/fdt_wip.c b/libfdt-binding/libfdt/fdt_wip.c deleted file mode 100644 index c2d7566a67dcf114cb15e0e233c8bff126744365..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/fdt_wip.c +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) -/* - * libfdt - Flat Device Tree manipulation - * Copyright (C) 2006 David Gibson, IBM Corporation. - */ -#include "libfdt_env.h" - -#include -#include - -#include "libfdt_internal.h" - -int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, - const char *name, int namelen, - uint32_t idx, const void *val, - int len) -{ - void *propval; - int proplen; - - propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen, - &proplen); - if (!propval) - return proplen; - - if ((unsigned)proplen < (len + idx)) - return -FDT_ERR_NOSPACE; - - memcpy((char *)propval + idx, val, len); - return 0; -} - -int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, - const void *val, int len) -{ - const void *propval; - int proplen; - - propval = fdt_getprop(fdt, nodeoffset, name, &proplen); - if (!propval) - return proplen; - - if (proplen != len) - return -FDT_ERR_NOSPACE; - - return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name, - strlen(name), 0, - val, len); -} - -static void fdt_nop_region_(void *start, int len) -{ - fdt32_t *p; - - for (p = start; (char *)p < ((char *)start + len); p++) - *p = cpu_to_fdt32(FDT_NOP); -} - -int fdt_nop_property(void *fdt, int nodeoffset, const char *name) -{ - struct fdt_property *prop; - int len; - - prop = fdt_get_property_w(fdt, nodeoffset, name, &len); - if (!prop) - return len; - - fdt_nop_region_(prop, len + sizeof(*prop)); - - return 0; -} - -int fdt_node_end_offset_(void *fdt, int offset) -{ - int depth = 0; - - while ((offset >= 0) && (depth >= 0)) - offset = fdt_next_node(fdt, offset, &depth); - - return offset; -} - -int fdt_nop_node(void *fdt, int nodeoffset) -{ - int endoffset; - - endoffset = fdt_node_end_offset_(fdt, nodeoffset); - if (endoffset < 0) - return endoffset; - - fdt_nop_region_(fdt_offset_ptr_w(fdt, nodeoffset, 0), - endoffset - nodeoffset); - return 0; -} diff --git a/libfdt-binding/libfdt/libfdt.h b/libfdt-binding/libfdt/libfdt.h deleted file mode 100644 index 73467f75c58db22b85b44ab9bda9aa17aa199e87..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/libfdt.h +++ /dev/null @@ -1,2122 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ -#ifndef LIBFDT_H -#define LIBFDT_H -/* - * libfdt - Flat Device Tree manipulation - * Copyright (C) 2006 David Gibson, IBM Corporation. - */ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define FDT_FIRST_SUPPORTED_VERSION 0x02 -#define FDT_LAST_COMPATIBLE_VERSION 0x10 -#define FDT_LAST_SUPPORTED_VERSION 0x11 - -/* Error codes: informative error codes */ -#define FDT_ERR_NOTFOUND 1 - /* FDT_ERR_NOTFOUND: The requested node or property does not exist */ -#define FDT_ERR_EXISTS 2 - /* FDT_ERR_EXISTS: Attempted to create a node or property which - * already exists */ -#define FDT_ERR_NOSPACE 3 - /* FDT_ERR_NOSPACE: Operation needed to expand the device - * tree, but its buffer did not have sufficient space to - * contain the expanded tree. Use fdt_open_into() to move the - * device tree to a buffer with more space. */ - -/* Error codes: codes for bad parameters */ -#define FDT_ERR_BADOFFSET 4 - /* FDT_ERR_BADOFFSET: Function was passed a structure block - * offset which is out-of-bounds, or which points to an - * unsuitable part of the structure for the operation. */ -#define FDT_ERR_BADPATH 5 - /* FDT_ERR_BADPATH: Function was passed a badly formatted path - * (e.g. missing a leading / for a function which requires an - * absolute path) */ -#define FDT_ERR_BADPHANDLE 6 - /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle. - * This can be caused either by an invalid phandle property - * length, or the phandle value was either 0 or -1, which are - * not permitted. */ -#define FDT_ERR_BADSTATE 7 - /* FDT_ERR_BADSTATE: Function was passed an incomplete device - * tree created by the sequential-write functions, which is - * not sufficiently complete for the requested operation. */ - -/* Error codes: codes for bad device tree blobs */ -#define FDT_ERR_TRUNCATED 8 - /* FDT_ERR_TRUNCATED: FDT or a sub-block is improperly - * terminated (overflows, goes outside allowed bounds, or - * isn't properly terminated). */ -#define FDT_ERR_BADMAGIC 9 - /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a - * device tree at all - it is missing the flattened device - * tree magic number. */ -#define FDT_ERR_BADVERSION 10 - /* FDT_ERR_BADVERSION: Given device tree has a version which - * can't be handled by the requested operation. For - * read-write functions, this may mean that fdt_open_into() is - * required to convert the tree to the expected version. */ -#define FDT_ERR_BADSTRUCTURE 11 - /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt - * structure block or other serious error (e.g. misnested - * nodes, or subnodes preceding properties). */ -#define FDT_ERR_BADLAYOUT 12 - /* FDT_ERR_BADLAYOUT: For read-write functions, the given - * device tree has it's sub-blocks in an order that the - * function can't handle (memory reserve map, then structure, - * then strings). Use fdt_open_into() to reorganize the tree - * into a form suitable for the read-write operations. */ - -/* "Can't happen" error indicating a bug in libfdt */ -#define FDT_ERR_INTERNAL 13 - /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion. - * Should never be returned, if it is, it indicates a bug in - * libfdt itself. */ - -/* Errors in device tree content */ -#define FDT_ERR_BADNCELLS 14 - /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells - * or similar property with a bad format or value */ - -#define FDT_ERR_BADVALUE 15 - /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected - * value. For example: a property expected to contain a string list - * is not NUL-terminated within the length of its value. */ - -#define FDT_ERR_BADOVERLAY 16 - /* FDT_ERR_BADOVERLAY: The device tree overlay, while - * correctly structured, cannot be applied due to some - * unexpected or missing value, property or node. */ - -#define FDT_ERR_NOPHANDLES 17 - /* FDT_ERR_NOPHANDLES: The device tree doesn't have any - * phandle available anymore without causing an overflow */ - -#define FDT_ERR_BADFLAGS 18 - /* FDT_ERR_BADFLAGS: The function was passed a flags field that - * contains invalid flags or an invalid combination of flags. */ - -#define FDT_ERR_ALIGNMENT 19 - /* FDT_ERR_ALIGNMENT: The device tree base address is not 8-byte - * aligned. */ - -#define FDT_ERR_MAX 19 - -/* constants */ -#define FDT_MAX_PHANDLE 0xfffffffe - /* Valid values for phandles range from 1 to 2^32-2. */ - -/**********************************************************************/ -/* Low-level functions (you probably don't need these) */ -/**********************************************************************/ - -#ifndef SWIG /* This function is not useful in Python */ -const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen); -#endif -static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen) -{ - return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen); -} - -uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); - -/* - * External helpers to access words from a device tree blob. They're built - * to work even with unaligned pointers on platforms (such as ARMv5) that don't - * like unaligned loads and stores. - */ -static inline uint32_t fdt32_ld(const fdt32_t *p) -{ - const uint8_t *bp = (const uint8_t *)p; - - return ((uint32_t)bp[0] << 24) - | ((uint32_t)bp[1] << 16) - | ((uint32_t)bp[2] << 8) - | bp[3]; -} - -static inline void fdt32_st(void *property, uint32_t value) -{ - uint8_t *bp = (uint8_t *)property; - - bp[0] = value >> 24; - bp[1] = (value >> 16) & 0xff; - bp[2] = (value >> 8) & 0xff; - bp[3] = value & 0xff; -} - -static inline uint64_t fdt64_ld(const fdt64_t *p) -{ - const uint8_t *bp = (const uint8_t *)p; - - return ((uint64_t)bp[0] << 56) - | ((uint64_t)bp[1] << 48) - | ((uint64_t)bp[2] << 40) - | ((uint64_t)bp[3] << 32) - | ((uint64_t)bp[4] << 24) - | ((uint64_t)bp[5] << 16) - | ((uint64_t)bp[6] << 8) - | bp[7]; -} - -static inline void fdt64_st(void *property, uint64_t value) -{ - uint8_t *bp = (uint8_t *)property; - - bp[0] = value >> 56; - bp[1] = (value >> 48) & 0xff; - bp[2] = (value >> 40) & 0xff; - bp[3] = (value >> 32) & 0xff; - bp[4] = (value >> 24) & 0xff; - bp[5] = (value >> 16) & 0xff; - bp[6] = (value >> 8) & 0xff; - bp[7] = value & 0xff; -} - -/**********************************************************************/ -/* Traversal functions */ -/**********************************************************************/ - -int fdt_next_node(const void *fdt, int offset, int *depth); - -/** - * fdt_first_subnode() - get offset of first direct subnode - * @fdt: FDT blob - * @offset: Offset of node to check - * - * Return: offset of first subnode, or -FDT_ERR_NOTFOUND if there is none - */ -int fdt_first_subnode(const void *fdt, int offset); - -/** - * fdt_next_subnode() - get offset of next direct subnode - * @fdt: FDT blob - * @offset: Offset of previous subnode - * - * After first calling fdt_first_subnode(), call this function repeatedly to - * get direct subnodes of a parent node. - * - * Return: offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more - * subnodes - */ -int fdt_next_subnode(const void *fdt, int offset); - -/** - * fdt_for_each_subnode - iterate over all subnodes of a parent - * - * @node: child node (int, lvalue) - * @fdt: FDT blob (const void *) - * @parent: parent node (int) - * - * This is actually a wrapper around a for loop and would be used like so: - * - * fdt_for_each_subnode(node, fdt, parent) { - * Use node - * ... - * } - * - * if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) { - * Error handling - * } - * - * Note that this is implemented as a macro and @node is used as - * iterator in the loop. The parent variable be constant or even a - * literal. - */ -#define fdt_for_each_subnode(node, fdt, parent) \ - for (node = fdt_first_subnode(fdt, parent); \ - node >= 0; \ - node = fdt_next_subnode(fdt, node)) - -/**********************************************************************/ -/* General functions */ -/**********************************************************************/ -#define fdt_get_header(fdt, field) \ - (fdt32_ld(&((const struct fdt_header *)(fdt))->field)) -#define fdt_magic(fdt) (fdt_get_header(fdt, magic)) -#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize)) -#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) -#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings)) -#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap)) -#define fdt_version(fdt) (fdt_get_header(fdt, version)) -#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version)) -#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys)) -#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings)) -#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct)) - -#define fdt_set_hdr_(name) \ - static inline void fdt_set_##name(void *fdt, uint32_t val) \ - { \ - struct fdt_header *fdth = (struct fdt_header *)fdt; \ - fdth->name = cpu_to_fdt32(val); \ - } -fdt_set_hdr_(magic); -fdt_set_hdr_(totalsize); -fdt_set_hdr_(off_dt_struct); -fdt_set_hdr_(off_dt_strings); -fdt_set_hdr_(off_mem_rsvmap); -fdt_set_hdr_(version); -fdt_set_hdr_(last_comp_version); -fdt_set_hdr_(boot_cpuid_phys); -fdt_set_hdr_(size_dt_strings); -fdt_set_hdr_(size_dt_struct); -#undef fdt_set_hdr_ - -/** - * fdt_header_size - return the size of the tree's header - * @fdt: pointer to a flattened device tree - * - * Return: size of DTB header in bytes - */ -size_t fdt_header_size(const void *fdt); - -/** - * fdt_header_size_ - internal function to get header size from a version number - * @version: devicetree version number - * - * Return: size of DTB header in bytes - */ -size_t fdt_header_size_(uint32_t version); - -/** - * fdt_check_header - sanity check a device tree header - * @fdt: pointer to data which might be a flattened device tree - * - * fdt_check_header() checks that the given buffer contains what - * appears to be a flattened device tree, and that the header contains - * valid information (to the extent that can be determined from the - * header alone). - * - * returns: - * 0, if the buffer appears to contain a valid device tree - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_TRUNCATED, standard meanings, as above - */ -int fdt_check_header(const void *fdt); - -/** - * fdt_move - move a device tree around in memory - * @fdt: pointer to the device tree to move - * @buf: pointer to memory where the device is to be moved - * @bufsize: size of the memory space at buf - * - * fdt_move() relocates, if possible, the device tree blob located at - * fdt to the buffer at buf of size bufsize. The buffer may overlap - * with the existing device tree blob at fdt. Therefore, - * fdt_move(fdt, fdt, fdt_totalsize(fdt)) - * should always succeed. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, standard meanings - */ -int fdt_move(const void *fdt, void *buf, int bufsize); - -/**********************************************************************/ -/* Read-only functions */ -/**********************************************************************/ - -int fdt_check_full(const void *fdt, size_t bufsize); - -/** - * fdt_get_string - retrieve a string from the strings block of a device tree - * @fdt: pointer to the device tree blob - * @stroffset: offset of the string within the strings block (native endian) - * @lenp: optional pointer to return the string's length - * - * fdt_get_string() retrieves a pointer to a single string from the - * strings block of the device tree blob at fdt, and optionally also - * returns the string's length in *lenp. - * - * returns: - * a pointer to the string, on success - * NULL, if stroffset is out of bounds, or doesn't point to a valid string - */ -const char *fdt_get_string(const void *fdt, int stroffset, int *lenp); - -/** - * fdt_string - retrieve a string from the strings block of a device tree - * @fdt: pointer to the device tree blob - * @stroffset: offset of the string within the strings block (native endian) - * - * fdt_string() retrieves a pointer to a single string from the - * strings block of the device tree blob at fdt. - * - * returns: - * a pointer to the string, on success - * NULL, if stroffset is out of bounds, or doesn't point to a valid string - */ -const char *fdt_string(const void *fdt, int stroffset); - -/** - * fdt_find_max_phandle - find and return the highest phandle in a tree - * @fdt: pointer to the device tree blob - * @phandle: return location for the highest phandle value found in the tree - * - * fdt_find_max_phandle() finds the highest phandle value in the given device - * tree. The value returned in @phandle is only valid if the function returns - * success. - * - * returns: - * 0 on success or a negative error code on failure - */ -int fdt_find_max_phandle(const void *fdt, uint32_t *phandle); - -/** - * fdt_get_max_phandle - retrieves the highest phandle in a tree - * @fdt: pointer to the device tree blob - * - * fdt_get_max_phandle retrieves the highest phandle in the given - * device tree. This will ignore badly formatted phandles, or phandles - * with a value of 0 or -1. - * - * This function is deprecated in favour of fdt_find_max_phandle(). - * - * returns: - * the highest phandle on success - * 0, if no phandle was found in the device tree - * -1, if an error occurred - */ -static inline uint32_t fdt_get_max_phandle(const void *fdt) -{ - uint32_t phandle; - int err; - - err = fdt_find_max_phandle(fdt, &phandle); - if (err < 0) - return (uint32_t)-1; - - return phandle; -} - -/** - * fdt_generate_phandle - return a new, unused phandle for a device tree blob - * @fdt: pointer to the device tree blob - * @phandle: return location for the new phandle - * - * Walks the device tree blob and looks for the highest phandle value. On - * success, the new, unused phandle value (one higher than the previously - * highest phandle value in the device tree blob) will be returned in the - * @phandle parameter. - * - * Return: 0 on success or a negative error-code on failure - */ -int fdt_generate_phandle(const void *fdt, uint32_t *phandle); - -/** - * fdt_num_mem_rsv - retrieve the number of memory reserve map entries - * @fdt: pointer to the device tree blob - * - * Returns the number of entries in the device tree blob's memory - * reservation map. This does not include the terminating 0,0 entry - * or any other (0,0) entries reserved for expansion. - * - * returns: - * the number of entries - */ -int fdt_num_mem_rsv(const void *fdt); - -/** - * fdt_get_mem_rsv - retrieve one memory reserve map entry - * @fdt: pointer to the device tree blob - * @n: index of reserve map entry - * @address: pointer to 64-bit variable to hold the start address - * @size: pointer to 64-bit variable to hold the size of the entry - * - * On success, @address and @size will contain the address and size of - * the n-th reserve map entry from the device tree blob, in - * native-endian format. - * - * returns: - * 0, on success - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, standard meanings - */ -int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size); - -/** - * fdt_subnode_offset_namelen - find a subnode based on substring - * @fdt: pointer to the device tree blob - * @parentoffset: structure block offset of a node - * @name: name of the subnode to locate - * @namelen: number of characters of name to consider - * - * Identical to fdt_subnode_offset(), but only examine the first - * namelen characters of name for matching the subnode name. This is - * useful for finding subnodes based on a portion of a larger string, - * such as a full path. - * - * Return: offset of the subnode or -FDT_ERR_NOTFOUND if name not found. - */ -#ifndef SWIG /* Not available in Python */ -int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, - const char *name, int namelen); -#endif -/** - * fdt_subnode_offset - find a subnode of a given node - * @fdt: pointer to the device tree blob - * @parentoffset: structure block offset of a node - * @name: name of the subnode to locate - * - * fdt_subnode_offset() finds a subnode of the node at structure block - * offset parentoffset with the given name. name may include a unit - * address, in which case fdt_subnode_offset() will find the subnode - * with that unit address, or the unit address may be omitted, in - * which case fdt_subnode_offset() will find an arbitrary subnode - * whose name excluding unit address matches the given name. - * - * returns: - * structure block offset of the requested subnode (>=0), on success - * -FDT_ERR_NOTFOUND, if the requested subnode does not exist - * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE - * tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings. - */ -int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name); - -/** - * fdt_path_offset_namelen - find a tree node by its full path - * @fdt: pointer to the device tree blob - * @path: full path of the node to locate - * @namelen: number of characters of path to consider - * - * Identical to fdt_path_offset(), but only consider the first namelen - * characters of path as the path name. - * - * Return: offset of the node or negative libfdt error value otherwise - */ -#ifndef SWIG /* Not available in Python */ -int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen); -#endif - -/** - * fdt_path_offset - find a tree node by its full path - * @fdt: pointer to the device tree blob - * @path: full path of the node to locate - * - * fdt_path_offset() finds a node of a given path in the device tree. - * Each path component may omit the unit address portion, but the - * results of this are undefined if any such path component is - * ambiguous (that is if there are multiple nodes at the relevant - * level matching the given component, differentiated only by unit - * address). - * - * returns: - * structure block offset of the node with the requested path (>=0), on - * success - * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid - * -FDT_ERR_NOTFOUND, if the requested node does not exist - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings. - */ -int fdt_path_offset(const void *fdt, const char *path); - -/** - * fdt_get_name - retrieve the name of a given node - * @fdt: pointer to the device tree blob - * @nodeoffset: structure block offset of the starting node - * @lenp: pointer to an integer variable (will be overwritten) or NULL - * - * fdt_get_name() retrieves the name (including unit address) of the - * device tree node at structure block offset nodeoffset. If lenp is - * non-NULL, the length of this name is also returned, in the integer - * pointed to by lenp. - * - * returns: - * pointer to the node's name, on success - * If lenp is non-NULL, *lenp contains the length of that name - * (>=0) - * NULL, on error - * if lenp is non-NULL *lenp contains an error code (<0): - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE - * tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, standard meanings - */ -const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp); - -/** - * fdt_first_property_offset - find the offset of a node's first property - * @fdt: pointer to the device tree blob - * @nodeoffset: structure block offset of a node - * - * fdt_first_property_offset() finds the first property of the node at - * the given structure block offset. - * - * returns: - * structure block offset of the property (>=0), on success - * -FDT_ERR_NOTFOUND, if the requested node has no properties - * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings. - */ -int fdt_first_property_offset(const void *fdt, int nodeoffset); - -/** - * fdt_next_property_offset - step through a node's properties - * @fdt: pointer to the device tree blob - * @offset: structure block offset of a property - * - * fdt_next_property_offset() finds the property immediately after the - * one at the given structure block offset. This will be a property - * of the same node as the given property. - * - * returns: - * structure block offset of the next property (>=0), on success - * -FDT_ERR_NOTFOUND, if the given property is the last in its node - * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings. - */ -int fdt_next_property_offset(const void *fdt, int offset); - -/** - * fdt_for_each_property_offset - iterate over all properties of a node - * - * @property: property offset (int, lvalue) - * @fdt: FDT blob (const void *) - * @node: node offset (int) - * - * This is actually a wrapper around a for loop and would be used like so: - * - * fdt_for_each_property_offset(property, fdt, node) { - * Use property - * ... - * } - * - * if ((property < 0) && (property != -FDT_ERR_NOTFOUND)) { - * Error handling - * } - * - * Note that this is implemented as a macro and property is used as - * iterator in the loop. The node variable can be constant or even a - * literal. - */ -#define fdt_for_each_property_offset(property, fdt, node) \ - for (property = fdt_first_property_offset(fdt, node); \ - property >= 0; \ - property = fdt_next_property_offset(fdt, property)) - -/** - * fdt_get_property_by_offset - retrieve the property at a given offset - * @fdt: pointer to the device tree blob - * @offset: offset of the property to retrieve - * @lenp: pointer to an integer variable (will be overwritten) or NULL - * - * fdt_get_property_by_offset() retrieves a pointer to the - * fdt_property structure within the device tree blob at the given - * offset. If lenp is non-NULL, the length of the property value is - * also returned, in the integer pointed to by lenp. - * - * Note that this code only works on device tree versions >= 16. fdt_getprop() - * works on all versions. - * - * returns: - * pointer to the structure representing the property - * if lenp is non-NULL, *lenp contains the length of the property - * value (>=0) - * NULL, on error - * if lenp is non-NULL, *lenp contains an error code (<0): - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings - */ -const struct fdt_property *fdt_get_property_by_offset(const void *fdt, - int offset, - int *lenp); - -/** - * fdt_get_property_namelen - find a property based on substring - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to find - * @name: name of the property to find - * @namelen: number of characters of name to consider - * @lenp: pointer to an integer variable (will be overwritten) or NULL - * - * Identical to fdt_get_property(), but only examine the first namelen - * characters of name for matching the property name. - * - * Return: pointer to the structure representing the property, or NULL - * if not found - */ -#ifndef SWIG /* Not available in Python */ -const struct fdt_property *fdt_get_property_namelen(const void *fdt, - int nodeoffset, - const char *name, - int namelen, int *lenp); -#endif - -/** - * fdt_get_property - find a given property in a given node - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to find - * @name: name of the property to find - * @lenp: pointer to an integer variable (will be overwritten) or NULL - * - * fdt_get_property() retrieves a pointer to the fdt_property - * structure within the device tree blob corresponding to the property - * named 'name' of the node at offset nodeoffset. If lenp is - * non-NULL, the length of the property value is also returned, in the - * integer pointed to by lenp. - * - * returns: - * pointer to the structure representing the property - * if lenp is non-NULL, *lenp contains the length of the property - * value (>=0) - * NULL, on error - * if lenp is non-NULL, *lenp contains an error code (<0): - * -FDT_ERR_NOTFOUND, node does not have named property - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE - * tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings - */ -const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, - const char *name, int *lenp); -static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, - const char *name, - int *lenp) -{ - return (struct fdt_property *)(uintptr_t) - fdt_get_property(fdt, nodeoffset, name, lenp); -} - -/** - * fdt_getprop_by_offset - retrieve the value of a property at a given offset - * @fdt: pointer to the device tree blob - * @offset: offset of the property to read - * @namep: pointer to a string variable (will be overwritten) or NULL - * @lenp: pointer to an integer variable (will be overwritten) or NULL - * - * fdt_getprop_by_offset() retrieves a pointer to the value of the - * property at structure block offset 'offset' (this will be a pointer - * to within the device blob itself, not a copy of the value). If - * lenp is non-NULL, the length of the property value is also - * returned, in the integer pointed to by lenp. If namep is non-NULL, - * the property's namne will also be returned in the char * pointed to - * by namep (this will be a pointer to within the device tree's string - * block, not a new copy of the name). - * - * returns: - * pointer to the property's value - * if lenp is non-NULL, *lenp contains the length of the property - * value (>=0) - * if namep is non-NULL *namep contiains a pointer to the property - * name. - * NULL, on error - * if lenp is non-NULL, *lenp contains an error code (<0): - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings - */ -#ifndef SWIG /* This function is not useful in Python */ -const void *fdt_getprop_by_offset(const void *fdt, int offset, - const char **namep, int *lenp); -#endif - -/** - * fdt_getprop_namelen - get property value based on substring - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to find - * @name: name of the property to find - * @namelen: number of characters of name to consider - * @lenp: pointer to an integer variable (will be overwritten) or NULL - * - * Identical to fdt_getprop(), but only examine the first namelen - * characters of name for matching the property name. - * - * Return: pointer to the property's value or NULL on error - */ -#ifndef SWIG /* Not available in Python */ -const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, - const char *name, int namelen, int *lenp); -static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset, - const char *name, int namelen, - int *lenp) -{ - return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name, - namelen, lenp); -} -#endif - -/** - * fdt_getprop - retrieve the value of a given property - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to find - * @name: name of the property to find - * @lenp: pointer to an integer variable (will be overwritten) or NULL - * - * fdt_getprop() retrieves a pointer to the value of the property - * named @name of the node at offset @nodeoffset (this will be a - * pointer to within the device blob itself, not a copy of the value). - * If @lenp is non-NULL, the length of the property value is also - * returned, in the integer pointed to by @lenp. - * - * returns: - * pointer to the property's value - * if lenp is non-NULL, *lenp contains the length of the property - * value (>=0) - * NULL, on error - * if lenp is non-NULL, *lenp contains an error code (<0): - * -FDT_ERR_NOTFOUND, node does not have named property - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE - * tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings - */ -const void *fdt_getprop(const void *fdt, int nodeoffset, - const char *name, int *lenp); -static inline void *fdt_getprop_w(void *fdt, int nodeoffset, - const char *name, int *lenp) -{ - return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp); -} - -/** - * fdt_get_phandle - retrieve the phandle of a given node - * @fdt: pointer to the device tree blob - * @nodeoffset: structure block offset of the node - * - * fdt_get_phandle() retrieves the phandle of the device tree node at - * structure block offset nodeoffset. - * - * returns: - * the phandle of the node at nodeoffset, on success (!= 0, != -1) - * 0, if the node has no phandle, or another error occurs - */ -uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); - -/** - * fdt_get_alias_namelen - get alias based on substring - * @fdt: pointer to the device tree blob - * @name: name of the alias th look up - * @namelen: number of characters of name to consider - * - * Identical to fdt_get_alias(), but only examine the first @namelen - * characters of @name for matching the alias name. - * - * Return: a pointer to the expansion of the alias named @name, if it exists, - * NULL otherwise - */ -#ifndef SWIG /* Not available in Python */ -const char *fdt_get_alias_namelen(const void *fdt, - const char *name, int namelen); -#endif - -/** - * fdt_get_alias - retrieve the path referenced by a given alias - * @fdt: pointer to the device tree blob - * @name: name of the alias th look up - * - * fdt_get_alias() retrieves the value of a given alias. That is, the - * value of the property named @name in the node /aliases. - * - * returns: - * a pointer to the expansion of the alias named 'name', if it exists - * NULL, if the given alias or the /aliases node does not exist - */ -const char *fdt_get_alias(const void *fdt, const char *name); - -/** - * fdt_get_path - determine the full path of a node - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose path to find - * @buf: character buffer to contain the returned path (will be overwritten) - * @buflen: size of the character buffer at buf - * - * fdt_get_path() computes the full path of the node at offset - * nodeoffset, and records that path in the buffer at buf. - * - * NOTE: This function is expensive, as it must scan the device tree - * structure from the start to nodeoffset. - * - * returns: - * 0, on success - * buf contains the absolute path of the node at - * nodeoffset, as a NUL-terminated string. - * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag - * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) - * characters and will not fit in the given buffer. - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, standard meanings - */ -int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen); - -/** - * fdt_supernode_atdepth_offset - find a specific ancestor of a node - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose parent to find - * @supernodedepth: depth of the ancestor to find - * @nodedepth: pointer to an integer variable (will be overwritten) or NULL - * - * fdt_supernode_atdepth_offset() finds an ancestor of the given node - * at a specific depth from the root (where the root itself has depth - * 0, its immediate subnodes depth 1 and so forth). So - * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL); - * will always return 0, the offset of the root node. If the node at - * nodeoffset has depth D, then: - * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL); - * will return nodeoffset itself. - * - * NOTE: This function is expensive, as it must scan the device tree - * structure from the start to nodeoffset. - * - * returns: - * structure block offset of the node at node offset's ancestor - * of depth supernodedepth (>=0), on success - * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag - * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of - * nodeoffset - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, standard meanings - */ -int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, - int supernodedepth, int *nodedepth); - -/** - * fdt_node_depth - find the depth of a given node - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose parent to find - * - * fdt_node_depth() finds the depth of a given node. The root node - * has depth 0, its immediate subnodes depth 1 and so forth. - * - * NOTE: This function is expensive, as it must scan the device tree - * structure from the start to nodeoffset. - * - * returns: - * depth of the node at nodeoffset (>=0), on success - * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, standard meanings - */ -int fdt_node_depth(const void *fdt, int nodeoffset); - -/** - * fdt_parent_offset - find the parent of a given node - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose parent to find - * - * fdt_parent_offset() locates the parent node of a given node (that - * is, it finds the offset of the node which contains the node at - * nodeoffset as a subnode). - * - * NOTE: This function is expensive, as it must scan the device tree - * structure from the start to nodeoffset, *twice*. - * - * returns: - * structure block offset of the parent of the node at nodeoffset - * (>=0), on success - * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, standard meanings - */ -int fdt_parent_offset(const void *fdt, int nodeoffset); - -/** - * fdt_node_offset_by_prop_value - find nodes with a given property value - * @fdt: pointer to the device tree blob - * @startoffset: only find nodes after this offset - * @propname: property name to check - * @propval: property value to search for - * @proplen: length of the value in propval - * - * fdt_node_offset_by_prop_value() returns the offset of the first - * node after startoffset, which has a property named propname whose - * value is of length proplen and has value equal to propval; or if - * startoffset is -1, the very first such node in the tree. - * - * To iterate through all nodes matching the criterion, the following - * idiom can be used: - * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, - * propval, proplen); - * while (offset != -FDT_ERR_NOTFOUND) { - * // other code here - * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, - * propval, proplen); - * } - * - * Note the -1 in the first call to the function, if 0 is used here - * instead, the function will never locate the root node, even if it - * matches the criterion. - * - * returns: - * structure block offset of the located node (>= 0, >startoffset), - * on success - * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the - * tree after startoffset - * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, standard meanings - */ -int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, - const char *propname, - const void *propval, int proplen); - -/** - * fdt_node_offset_by_phandle - find the node with a given phandle - * @fdt: pointer to the device tree blob - * @phandle: phandle value - * - * fdt_node_offset_by_phandle() returns the offset of the node - * which has the given phandle value. If there is more than one node - * in the tree with the given phandle (an invalid tree), results are - * undefined. - * - * returns: - * structure block offset of the located node (>= 0), on success - * -FDT_ERR_NOTFOUND, no node with that phandle exists - * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1) - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, standard meanings - */ -int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle); - -/** - * fdt_node_check_compatible - check a node's compatible property - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of a tree node - * @compatible: string to match against - * - * fdt_node_check_compatible() returns 0 if the given node contains a - * @compatible property with the given string as one of its elements, - * it returns non-zero otherwise, or on error. - * - * returns: - * 0, if the node has a 'compatible' property listing the given string - * 1, if the node has a 'compatible' property, but it does not list - * the given string - * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property - * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, standard meanings - */ -int fdt_node_check_compatible(const void *fdt, int nodeoffset, - const char *compatible); - -/** - * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value - * @fdt: pointer to the device tree blob - * @startoffset: only find nodes after this offset - * @compatible: 'compatible' string to match against - * - * fdt_node_offset_by_compatible() returns the offset of the first - * node after startoffset, which has a 'compatible' property which - * lists the given compatible string; or if startoffset is -1, the - * very first such node in the tree. - * - * To iterate through all nodes matching the criterion, the following - * idiom can be used: - * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); - * while (offset != -FDT_ERR_NOTFOUND) { - * // other code here - * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); - * } - * - * Note the -1 in the first call to the function, if 0 is used here - * instead, the function will never locate the root node, even if it - * matches the criterion. - * - * returns: - * structure block offset of the located node (>= 0, >startoffset), - * on success - * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the - * tree after startoffset - * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, standard meanings - */ -int fdt_node_offset_by_compatible(const void *fdt, int startoffset, - const char *compatible); - -/** - * fdt_stringlist_contains - check a string list property for a string - * @strlist: Property containing a list of strings to check - * @listlen: Length of property - * @str: String to search for - * - * This is a utility function provided for convenience. The list contains - * one or more strings, each terminated by \0, as is found in a device tree - * "compatible" property. - * - * Return: 1 if the string is found in the list, 0 not found, or invalid list - */ -int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); - -/** - * fdt_stringlist_count - count the number of strings in a string list - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of a tree node - * @property: name of the property containing the string list - * - * Return: - * the number of strings in the given property - * -FDT_ERR_BADVALUE if the property value is not NUL-terminated - * -FDT_ERR_NOTFOUND if the property does not exist - */ -int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property); - -/** - * fdt_stringlist_search - find a string in a string list and return its index - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of a tree node - * @property: name of the property containing the string list - * @string: string to look up in the string list - * - * Note that it is possible for this function to succeed on property values - * that are not NUL-terminated. That's because the function will stop after - * finding the first occurrence of @string. This can for example happen with - * small-valued cell properties, such as #address-cells, when searching for - * the empty string. - * - * return: - * the index of the string in the list of strings - * -FDT_ERR_BADVALUE if the property value is not NUL-terminated - * -FDT_ERR_NOTFOUND if the property does not exist or does not contain - * the given string - */ -int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, - const char *string); - -/** - * fdt_stringlist_get() - obtain the string at a given index in a string list - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of a tree node - * @property: name of the property containing the string list - * @index: index of the string to return - * @lenp: return location for the string length or an error code on failure - * - * Note that this will successfully extract strings from properties with - * non-NUL-terminated values. For example on small-valued cell properties - * this function will return the empty string. - * - * If non-NULL, the length of the string (on success) or a negative error-code - * (on failure) will be stored in the integer pointer to by lenp. - * - * Return: - * A pointer to the string at the given index in the string list or NULL on - * failure. On success the length of the string will be stored in the memory - * location pointed to by the lenp parameter, if non-NULL. On failure one of - * the following negative error codes will be returned in the lenp parameter - * (if non-NULL): - * -FDT_ERR_BADVALUE if the property value is not NUL-terminated - * -FDT_ERR_NOTFOUND if the property does not exist - */ -const char *fdt_stringlist_get(const void *fdt, int nodeoffset, - const char *property, int index, - int *lenp); - -/**********************************************************************/ -/* Read-only functions (addressing related) */ -/**********************************************************************/ - -/** - * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells - * - * This is the maximum value for #address-cells, #size-cells and - * similar properties that will be processed by libfdt. IEE1275 - * requires that OF implementations handle values up to 4. - * Implementations may support larger values, but in practice higher - * values aren't used. - */ -#define FDT_MAX_NCELLS 4 - -/** - * fdt_address_cells - retrieve address size for a bus represented in the tree - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node to find the address size for - * - * When the node has a valid #address-cells property, returns its value. - * - * returns: - * 0 <= n < FDT_MAX_NCELLS, on success - * 2, if the node has no #address-cells property - * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid - * #address-cells property - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings - */ -int fdt_address_cells(const void *fdt, int nodeoffset); - -/** - * fdt_size_cells - retrieve address range size for a bus represented in the - * tree - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node to find the address range size for - * - * When the node has a valid #size-cells property, returns its value. - * - * returns: - * 0 <= n < FDT_MAX_NCELLS, on success - * 1, if the node has no #size-cells property - * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid - * #size-cells property - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings - */ -int fdt_size_cells(const void *fdt, int nodeoffset); - - -/**********************************************************************/ -/* Write-in-place functions */ -/**********************************************************************/ - -/** - * fdt_setprop_inplace_namelen_partial - change a property's value, - * but not its size - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * @namelen: number of characters of name to consider - * @idx: index of the property to change in the array - * @val: pointer to data to replace the property value with - * @len: length of the property value - * - * Identical to fdt_setprop_inplace(), but modifies the given property - * starting from the given index, and using only the first characters - * of the name. It is useful when you want to manipulate only one value of - * an array and you have a string that doesn't end with \0. - * - * Return: 0 on success, negative libfdt error value otherwise - */ -#ifndef SWIG /* Not available in Python */ -int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, - const char *name, int namelen, - uint32_t idx, const void *val, - int len); -#endif - -/** - * fdt_setprop_inplace - change a property's value, but not its size - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * @val: pointer to data to replace the property value with - * @len: length of the property value - * - * fdt_setprop_inplace() replaces the value of a given property with - * the data in val, of length len. This function cannot change the - * size of a property, and so will only work if len is equal to the - * current length of the property. - * - * This function will alter only the bytes in the blob which contain - * the given property value, and will not alter or move any other part - * of the tree. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, if len is not equal to the property's current length - * -FDT_ERR_NOTFOUND, node does not have the named property - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings - */ -#ifndef SWIG /* Not available in Python */ -int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, - const void *val, int len); -#endif - -/** - * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * @val: 32-bit integer value to replace the property with - * - * fdt_setprop_inplace_u32() replaces the value of a given property - * with the 32-bit integer value in val, converting val to big-endian - * if necessary. This function cannot change the size of a property, - * and so will only work if the property already exists and has length - * 4. - * - * This function will alter only the bytes in the blob which contain - * the given property value, and will not alter or move any other part - * of the tree. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, if the property's length is not equal to 4 - * -FDT_ERR_NOTFOUND, node does not have the named property - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings - */ -static inline int fdt_setprop_inplace_u32(void *fdt, int nodeoffset, - const char *name, uint32_t val) -{ - fdt32_t tmp = cpu_to_fdt32(val); - return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); -} - -/** - * fdt_setprop_inplace_u64 - change the value of a 64-bit integer property - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * @val: 64-bit integer value to replace the property with - * - * fdt_setprop_inplace_u64() replaces the value of a given property - * with the 64-bit integer value in val, converting val to big-endian - * if necessary. This function cannot change the size of a property, - * and so will only work if the property already exists and has length - * 8. - * - * This function will alter only the bytes in the blob which contain - * the given property value, and will not alter or move any other part - * of the tree. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, if the property's length is not equal to 8 - * -FDT_ERR_NOTFOUND, node does not have the named property - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings - */ -static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset, - const char *name, uint64_t val) -{ - fdt64_t tmp = cpu_to_fdt64(val); - return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); -} - -/** - * fdt_setprop_inplace_cell - change the value of a single-cell property - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node containing the property - * @name: name of the property to change the value of - * @val: new value of the 32-bit cell - * - * This is an alternative name for fdt_setprop_inplace_u32() - * Return: 0 on success, negative libfdt error number otherwise. - */ -static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, - const char *name, uint32_t val) -{ - return fdt_setprop_inplace_u32(fdt, nodeoffset, name, val); -} - -/** - * fdt_nop_property - replace a property with nop tags - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to nop - * @name: name of the property to nop - * - * fdt_nop_property() will replace a given property's representation - * in the blob with FDT_NOP tags, effectively removing it from the - * tree. - * - * This function will alter only the bytes in the blob which contain - * the property, and will not alter or move any other part of the - * tree. - * - * returns: - * 0, on success - * -FDT_ERR_NOTFOUND, node does not have the named property - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings - */ -int fdt_nop_property(void *fdt, int nodeoffset, const char *name); - -/** - * fdt_nop_node - replace a node (subtree) with nop tags - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node to nop - * - * fdt_nop_node() will replace a given node's representation in the - * blob, including all its subnodes, if any, with FDT_NOP tags, - * effectively removing it from the tree. - * - * This function will alter only the bytes in the blob which contain - * the node and its properties and subnodes, and will not alter or - * move any other part of the tree. - * - * returns: - * 0, on success - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings - */ -int fdt_nop_node(void *fdt, int nodeoffset); - -/**********************************************************************/ -/* Sequential write functions */ -/**********************************************************************/ - -/* fdt_create_with_flags flags */ -#define FDT_CREATE_FLAG_NO_NAME_DEDUP 0x1 - /* FDT_CREATE_FLAG_NO_NAME_DEDUP: Do not try to de-duplicate property - * names in the fdt. This can result in faster creation times, but - * a larger fdt. */ - -#define FDT_CREATE_FLAGS_ALL (FDT_CREATE_FLAG_NO_NAME_DEDUP) - -/** - * fdt_create_with_flags - begin creation of a new fdt - * @buf: pointer to memory allocated where fdt will be created - * @bufsize: size of the memory space at fdt - * @flags: a valid combination of FDT_CREATE_FLAG_ flags, or 0. - * - * fdt_create_with_flags() begins the process of creating a new fdt with - * the sequential write interface. - * - * fdt creation process must end with fdt_finished() to produce a valid fdt. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt - * -FDT_ERR_BADFLAGS, flags is not valid - */ -int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags); - -/** - * fdt_create - begin creation of a new fdt - * @buf: pointer to memory allocated where fdt will be created - * @bufsize: size of the memory space at fdt - * - * fdt_create() is equivalent to fdt_create_with_flags() with flags=0. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt - */ -int fdt_create(void *buf, int bufsize); - -int fdt_resize(void *fdt, void *buf, int bufsize); -int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size); -int fdt_finish_reservemap(void *fdt); -int fdt_begin_node(void *fdt, const char *name); -int fdt_property(void *fdt, const char *name, const void *val, int len); -static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val) -{ - fdt32_t tmp = cpu_to_fdt32(val); - return fdt_property(fdt, name, &tmp, sizeof(tmp)); -} -static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val) -{ - fdt64_t tmp = cpu_to_fdt64(val); - return fdt_property(fdt, name, &tmp, sizeof(tmp)); -} - -#ifndef SWIG /* Not available in Python */ -static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) -{ - return fdt_property_u32(fdt, name, val); -} -#endif - -/** - * fdt_property_placeholder - add a new property and return a ptr to its value - * - * @fdt: pointer to the device tree blob - * @name: name of property to add - * @len: length of property value in bytes - * @valp: returns a pointer to where where the value should be placed - * - * returns: - * 0, on success - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_NOSPACE, standard meanings - */ -int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp); - -#define fdt_property_string(fdt, name, str) \ - fdt_property(fdt, name, str, strlen(str)+1) -int fdt_end_node(void *fdt); -int fdt_finish(void *fdt); - -/**********************************************************************/ -/* Read-write functions */ -/**********************************************************************/ - -int fdt_create_empty_tree(void *buf, int bufsize); -int fdt_open_into(const void *fdt, void *buf, int bufsize); -int fdt_pack(void *fdt); - -/** - * fdt_add_mem_rsv - add one memory reserve map entry - * @fdt: pointer to the device tree blob - * @address: 64-bit start address of the reserve map entry - * @size: 64-bit size of the reserved region - * - * Adds a reserve map entry to the given blob reserving a region at - * address address of length size. - * - * This function will insert data into the reserve map and will - * therefore change the indexes of some entries in the table. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to - * contain the new reservation entry - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_TRUNCATED, standard meanings - */ -int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size); - -/** - * fdt_del_mem_rsv - remove a memory reserve map entry - * @fdt: pointer to the device tree blob - * @n: entry to remove - * - * fdt_del_mem_rsv() removes the n-th memory reserve map entry from - * the blob. - * - * This function will delete data from the reservation table and will - * therefore change the indexes of some entries in the table. - * - * returns: - * 0, on success - * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there - * are less than n+1 reserve map entries) - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_TRUNCATED, standard meanings - */ -int fdt_del_mem_rsv(void *fdt, int n); - -/** - * fdt_set_name - change the name of a given node - * @fdt: pointer to the device tree blob - * @nodeoffset: structure block offset of a node - * @name: name to give the node - * - * fdt_set_name() replaces the name (including unit address, if any) - * of the given node with the given string. NOTE: this function can't - * efficiently check if the new name is unique amongst the given - * node's siblings; results are undefined if this function is invoked - * with a name equal to one of the given node's siblings. - * - * This function may insert or delete data from the blob, and will - * therefore change the offsets of some existing nodes. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, there is insufficient free space in the blob - * to contain the new name - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, standard meanings - */ -int fdt_set_name(void *fdt, int nodeoffset, const char *name); - -/** - * fdt_setprop - create or change a property - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * @val: pointer to data to set the property value to - * @len: length of the property value - * - * fdt_setprop() sets the value of the named property in the given - * node to the given value and length, creating the property if it - * does not already exist. - * - * This function may insert or delete data from the blob, and will - * therefore change the offsets of some existing nodes. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to - * contain the new property value - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_TRUNCATED, standard meanings - */ -int fdt_setprop(void *fdt, int nodeoffset, const char *name, - const void *val, int len); - -/** - * fdt_setprop_placeholder - allocate space for a property - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * @len: length of the property value - * @prop_data: return pointer to property data - * - * fdt_setprop_placeholer() allocates the named property in the given node. - * If the property exists it is resized. In either case a pointer to the - * property data is returned. - * - * This function may insert or delete data from the blob, and will - * therefore change the offsets of some existing nodes. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to - * contain the new property value - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_TRUNCATED, standard meanings - */ -int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, - int len, void **prop_data); - -/** - * fdt_setprop_u32 - set a property to a 32-bit integer - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * @val: 32-bit integer value for the property (native endian) - * - * fdt_setprop_u32() sets the value of the named property in the given - * node to the given 32-bit integer value (converting to big-endian if - * necessary), or creates a new property with that value if it does - * not already exist. - * - * This function may insert or delete data from the blob, and will - * therefore change the offsets of some existing nodes. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to - * contain the new property value - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_TRUNCATED, standard meanings - */ -static inline int fdt_setprop_u32(void *fdt, int nodeoffset, const char *name, - uint32_t val) -{ - fdt32_t tmp = cpu_to_fdt32(val); - return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); -} - -/** - * fdt_setprop_u64 - set a property to a 64-bit integer - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * @val: 64-bit integer value for the property (native endian) - * - * fdt_setprop_u64() sets the value of the named property in the given - * node to the given 64-bit integer value (converting to big-endian if - * necessary), or creates a new property with that value if it does - * not already exist. - * - * This function may insert or delete data from the blob, and will - * therefore change the offsets of some existing nodes. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to - * contain the new property value - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_TRUNCATED, standard meanings - */ -static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name, - uint64_t val) -{ - fdt64_t tmp = cpu_to_fdt64(val); - return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); -} - -/** - * fdt_setprop_cell - set a property to a single cell value - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * @val: 32-bit integer value for the property (native endian) - * - * This is an alternative name for fdt_setprop_u32() - * - * Return: 0 on success, negative libfdt error value otherwise. - */ -static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, - uint32_t val) -{ - return fdt_setprop_u32(fdt, nodeoffset, name, val); -} - -/** - * fdt_setprop_string - set a property to a string value - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * @str: string value for the property - * - * fdt_setprop_string() sets the value of the named property in the - * given node to the given string value (using the length of the - * string to determine the new length of the property), or creates a - * new property with that value if it does not already exist. - * - * This function may insert or delete data from the blob, and will - * therefore change the offsets of some existing nodes. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to - * contain the new property value - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_TRUNCATED, standard meanings - */ -#define fdt_setprop_string(fdt, nodeoffset, name, str) \ - fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) - - -/** - * fdt_setprop_empty - set a property to an empty value - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * - * fdt_setprop_empty() sets the value of the named property in the - * given node to an empty (zero length) value, or creates a new empty - * property if it does not already exist. - * - * This function may insert or delete data from the blob, and will - * therefore change the offsets of some existing nodes. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to - * contain the new property value - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_TRUNCATED, standard meanings - */ -#define fdt_setprop_empty(fdt, nodeoffset, name) \ - fdt_setprop((fdt), (nodeoffset), (name), NULL, 0) - -/** - * fdt_appendprop - append to or create a property - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to append to - * @val: pointer to data to append to the property value - * @len: length of the data to append to the property value - * - * fdt_appendprop() appends the value to the named property in the - * given node, creating the property if it does not already exist. - * - * This function may insert data into the blob, and will therefore - * change the offsets of some existing nodes. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to - * contain the new property value - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_TRUNCATED, standard meanings - */ -int fdt_appendprop(void *fdt, int nodeoffset, const char *name, - const void *val, int len); - -/** - * fdt_appendprop_u32 - append a 32-bit integer value to a property - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * @val: 32-bit integer value to append to the property (native endian) - * - * fdt_appendprop_u32() appends the given 32-bit integer value - * (converting to big-endian if necessary) to the value of the named - * property in the given node, or creates a new property with that - * value if it does not already exist. - * - * This function may insert data into the blob, and will therefore - * change the offsets of some existing nodes. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to - * contain the new property value - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_TRUNCATED, standard meanings - */ -static inline int fdt_appendprop_u32(void *fdt, int nodeoffset, - const char *name, uint32_t val) -{ - fdt32_t tmp = cpu_to_fdt32(val); - return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); -} - -/** - * fdt_appendprop_u64 - append a 64-bit integer value to a property - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * @val: 64-bit integer value to append to the property (native endian) - * - * fdt_appendprop_u64() appends the given 64-bit integer value - * (converting to big-endian if necessary) to the value of the named - * property in the given node, or creates a new property with that - * value if it does not already exist. - * - * This function may insert data into the blob, and will therefore - * change the offsets of some existing nodes. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to - * contain the new property value - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_TRUNCATED, standard meanings - */ -static inline int fdt_appendprop_u64(void *fdt, int nodeoffset, - const char *name, uint64_t val) -{ - fdt64_t tmp = cpu_to_fdt64(val); - return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); -} - -/** - * fdt_appendprop_cell - append a single cell value to a property - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * @val: 32-bit integer value to append to the property (native endian) - * - * This is an alternative name for fdt_appendprop_u32() - * - * Return: 0 on success, negative libfdt error value otherwise. - */ -static inline int fdt_appendprop_cell(void *fdt, int nodeoffset, - const char *name, uint32_t val) -{ - return fdt_appendprop_u32(fdt, nodeoffset, name, val); -} - -/** - * fdt_appendprop_string - append a string to a property - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to change - * @name: name of the property to change - * @str: string value to append to the property - * - * fdt_appendprop_string() appends the given string to the value of - * the named property in the given node, or creates a new property - * with that value if it does not already exist. - * - * This function may insert data into the blob, and will therefore - * change the offsets of some existing nodes. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to - * contain the new property value - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_TRUNCATED, standard meanings - */ -#define fdt_appendprop_string(fdt, nodeoffset, name, str) \ - fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) - -/** - * fdt_appendprop_addrrange - append a address range property - * @fdt: pointer to the device tree blob - * @parent: offset of the parent node - * @nodeoffset: offset of the node to add a property at - * @name: name of property - * @addr: start address of a given range - * @size: size of a given range - * - * fdt_appendprop_addrrange() appends an address range value (start - * address and size) to the value of the named property in the given - * node, or creates a new property with that value if it does not - * already exist. - * If "name" is not specified, a default "reg" is used. - * Cell sizes are determined by parent's #address-cells and #size-cells. - * - * This function may insert data into the blob, and will therefore - * change the offsets of some existing nodes. - * - * returns: - * 0, on success - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid - * #address-cells property - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADVALUE, addr or size doesn't fit to respective cells size - * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to - * contain a new property - * -FDT_ERR_TRUNCATED, standard meanings - */ -int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset, - const char *name, uint64_t addr, uint64_t size); - -/** - * fdt_delprop - delete a property - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node whose property to nop - * @name: name of the property to nop - * - * fdt_del_property() will delete the given property. - * - * This function will delete data from the blob, and will therefore - * change the offsets of some existing nodes. - * - * returns: - * 0, on success - * -FDT_ERR_NOTFOUND, node does not have the named property - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings - */ -int fdt_delprop(void *fdt, int nodeoffset, const char *name); - -/** - * fdt_add_subnode_namelen - creates a new node based on substring - * @fdt: pointer to the device tree blob - * @parentoffset: structure block offset of a node - * @name: name of the subnode to create - * @namelen: number of characters of name to consider - * - * Identical to fdt_add_subnode(), but use only the first @namelen - * characters of @name as the name of the new node. This is useful for - * creating subnodes based on a portion of a larger string, such as a - * full path. - * - * Return: structure block offset of the created subnode (>=0), - * negative libfdt error value otherwise - */ -#ifndef SWIG /* Not available in Python */ -int fdt_add_subnode_namelen(void *fdt, int parentoffset, - const char *name, int namelen); -#endif - -/** - * fdt_add_subnode - creates a new node - * @fdt: pointer to the device tree blob - * @parentoffset: structure block offset of a node - * @name: name of the subnode to locate - * - * fdt_add_subnode() creates a new node as a subnode of the node at - * structure block offset parentoffset, with the given name (which - * should include the unit address, if any). - * - * This function will insert data into the blob, and will therefore - * change the offsets of some existing nodes. - * - * returns: - * structure block offset of the created nodeequested subnode (>=0), on - * success - * -FDT_ERR_NOTFOUND, if the requested subnode does not exist - * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE - * tag - * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of - * the given name - * -FDT_ERR_NOSPACE, if there is insufficient free space in the - * blob to contain the new node - * -FDT_ERR_NOSPACE - * -FDT_ERR_BADLAYOUT - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings. - */ -int fdt_add_subnode(void *fdt, int parentoffset, const char *name); - -/** - * fdt_del_node - delete a node (subtree) - * @fdt: pointer to the device tree blob - * @nodeoffset: offset of the node to nop - * - * fdt_del_node() will remove the given node, including all its - * subnodes if any, from the blob. - * - * This function will delete data from the blob, and will therefore - * change the offsets of some existing nodes. - * - * returns: - * 0, on success - * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_TRUNCATED, standard meanings - */ -int fdt_del_node(void *fdt, int nodeoffset); - -/** - * fdt_overlay_apply - Applies a DT overlay on a base DT - * @fdt: pointer to the base device tree blob - * @fdto: pointer to the device tree overlay blob - * - * fdt_overlay_apply() will apply the given device tree overlay on the - * given base device tree. - * - * Expect the base device tree to be modified, even if the function - * returns an error. - * - * returns: - * 0, on success - * -FDT_ERR_NOSPACE, there's not enough space in the base device tree - * -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or - * properties in the base DT - * -FDT_ERR_BADPHANDLE, - * -FDT_ERR_BADOVERLAY, - * -FDT_ERR_NOPHANDLES, - * -FDT_ERR_INTERNAL, - * -FDT_ERR_BADLAYOUT, - * -FDT_ERR_BADMAGIC, - * -FDT_ERR_BADOFFSET, - * -FDT_ERR_BADPATH, - * -FDT_ERR_BADVERSION, - * -FDT_ERR_BADSTRUCTURE, - * -FDT_ERR_BADSTATE, - * -FDT_ERR_TRUNCATED, standard meanings - */ -int fdt_overlay_apply(void *fdt, void *fdto); - -/**********************************************************************/ -/* Debugging / informational functions */ -/**********************************************************************/ - -const char *fdt_strerror(int errval); - -#ifdef __cplusplus -} -#endif - -#endif /* LIBFDT_H */ diff --git a/libfdt-binding/libfdt/libfdt_env.h b/libfdt-binding/libfdt/libfdt_env.h deleted file mode 100644 index 6d028a4fbd2facf734d92eca7d9316b3fec7057c..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/libfdt_env.h +++ /dev/null @@ -1,95 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ -#ifndef LIBFDT_ENV_H -#define LIBFDT_ENV_H -/* - * libfdt - Flat Device Tree manipulation - * Copyright (C) 2006 David Gibson, IBM Corporation. - * Copyright 2012 Kim Phillips, Freescale Semiconductor. - */ - -#include -#include -#include -#include -#include - -#ifdef __CHECKER__ -#define FDT_FORCE __attribute__((force)) -#define FDT_BITWISE __attribute__((bitwise)) -#else -#define FDT_FORCE -#define FDT_BITWISE -#endif - -typedef uint16_t FDT_BITWISE fdt16_t; -typedef uint32_t FDT_BITWISE fdt32_t; -typedef uint64_t FDT_BITWISE fdt64_t; - -#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n]) -#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1)) -#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \ - (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3)) -#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \ - (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \ - (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \ - (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7)) - -static inline uint16_t fdt16_to_cpu(fdt16_t x) -{ - return (FDT_FORCE uint16_t)CPU_TO_FDT16(x); -} -static inline fdt16_t cpu_to_fdt16(uint16_t x) -{ - return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x); -} - -static inline uint32_t fdt32_to_cpu(fdt32_t x) -{ - return (FDT_FORCE uint32_t)CPU_TO_FDT32(x); -} -static inline fdt32_t cpu_to_fdt32(uint32_t x) -{ - return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x); -} - -static inline uint64_t fdt64_to_cpu(fdt64_t x) -{ - return (FDT_FORCE uint64_t)CPU_TO_FDT64(x); -} -static inline fdt64_t cpu_to_fdt64(uint64_t x) -{ - return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x); -} -#undef CPU_TO_FDT64 -#undef CPU_TO_FDT32 -#undef CPU_TO_FDT16 -#undef EXTRACT_BYTE - -#ifdef __APPLE__ -#include - -/* strnlen() is not available on Mac OS < 10.7 */ -# if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < \ - MAC_OS_X_VERSION_10_7) - -#define strnlen fdt_strnlen - -/* - * fdt_strnlen: returns the length of a string or max_count - which ever is - * smallest. - * Input 1 string: the string whose size is to be determined - * Input 2 max_count: the maximum value returned by this function - * Output: length of the string or max_count (the smallest of the two) - */ -static inline size_t fdt_strnlen(const char *string, size_t max_count) -{ - const char *p = memchr(string, 0, max_count); - return p ? p - string : max_count; -} - -#endif /* !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < - MAC_OS_X_VERSION_10_7) */ - -#endif /* __APPLE__ */ - -#endif /* LIBFDT_ENV_H */ diff --git a/libfdt-binding/libfdt/libfdt_internal.h b/libfdt-binding/libfdt/libfdt_internal.h deleted file mode 100644 index 16bda1906a7b335519b9f748d1be6110de551e79..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/libfdt_internal.h +++ /dev/null @@ -1,192 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ -#ifndef LIBFDT_INTERNAL_H -#define LIBFDT_INTERNAL_H -/* - * libfdt - Flat Device Tree manipulation - * Copyright (C) 2006 David Gibson, IBM Corporation. - */ -#include - -#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) -#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) - -int32_t fdt_ro_probe_(const void *fdt); -#define FDT_RO_PROBE(fdt) \ - { \ - int32_t totalsize_; \ - if ((totalsize_ = fdt_ro_probe_(fdt)) < 0) \ - return totalsize_; \ - } - -int fdt_check_node_offset_(const void *fdt, int offset); -int fdt_check_prop_offset_(const void *fdt, int offset); -const char *fdt_find_string_(const char *strtab, int tabsize, const char *s); -int fdt_node_end_offset_(void *fdt, int nodeoffset); - -static inline const void *fdt_offset_ptr_(const void *fdt, int offset) -{ - return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; -} - -static inline void *fdt_offset_ptr_w_(void *fdt, int offset) -{ - return (void *)(uintptr_t)fdt_offset_ptr_(fdt, offset); -} - -static inline const struct fdt_reserve_entry *fdt_mem_rsv_(const void *fdt, int n) -{ - const struct fdt_reserve_entry *rsv_table = - (const struct fdt_reserve_entry *) - ((const char *)fdt + fdt_off_mem_rsvmap(fdt)); - - return rsv_table + n; -} -static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n) -{ - return (void *)(uintptr_t)fdt_mem_rsv_(fdt, n); -} - -/* - * Internal helpers to access tructural elements of the device tree - * blob (rather than for exaple reading integers from within property - * values). We assume that we are either given a naturally aligned - * address for the platform or if we are not, we are on a platform - * where unaligned memory reads will be handled in a graceful manner. - * If not the external helpers fdtXX_ld() from libfdt.h can be used - * instead. - */ -static inline uint32_t fdt32_ld_(const fdt32_t *p) -{ - return fdt32_to_cpu(*p); -} - -static inline uint64_t fdt64_ld_(const fdt64_t *p) -{ - return fdt64_to_cpu(*p); -} - -#define FDT_SW_MAGIC (~FDT_MAGIC) - -/**********************************************************************/ -/* Checking controls */ -/**********************************************************************/ - -#ifndef FDT_ASSUME_MASK -#define FDT_ASSUME_MASK 0 -#endif - -/* - * Defines assumptions which can be enabled. Each of these can be enabled - * individually. For maximum safety, don't enable any assumptions! - * - * For minimal code size and no safety, use ASSUME_PERFECT at your own risk. - * You should have another method of validating the device tree, such as a - * signature or hash check before using libfdt. - * - * For situations where security is not a concern it may be safe to enable - * ASSUME_SANE. - */ -enum { - /* - * This does essentially no checks. Only the latest device-tree - * version is correctly handled. Inconsistencies or errors in the device - * tree may cause undefined behaviour or crashes. Invalid parameters - * passed to libfdt may do the same. - * - * If an error occurs when modifying the tree it may leave the tree in - * an intermediate (but valid) state. As an example, adding a property - * where there is insufficient space may result in the property name - * being added to the string table even though the property itself is - * not added to the struct section. - * - * Only use this if you have a fully validated device tree with - * the latest supported version and wish to minimise code size. - */ - ASSUME_PERFECT = 0xff, - - /* - * This assumes that the device tree is sane. i.e. header metadata - * and basic hierarchy are correct. - * - * With this assumption enabled, normal device trees produced by libfdt - * and the compiler should be handled safely. Malicious device trees and - * complete garbage may cause libfdt to behave badly or crash. Truncated - * device trees (e.g. those only partially loaded) can also cause - * problems. - * - * Note: Only checks that relate exclusively to the device tree itself - * (not the parameters passed to libfdt) are disabled by this - * assumption. This includes checking headers, tags and the like. - */ - ASSUME_VALID_DTB = 1 << 0, - - /* - * This builds on ASSUME_VALID_DTB and further assumes that libfdt - * functions are called with valid parameters, i.e. not trigger - * FDT_ERR_BADOFFSET or offsets that are out of bounds. It disables any - * extensive checking of parameters and the device tree, making various - * assumptions about correctness. - * - * It doesn't make sense to enable this assumption unless - * ASSUME_VALID_DTB is also enabled. - */ - ASSUME_VALID_INPUT = 1 << 1, - - /* - * This disables checks for device-tree version and removes all code - * which handles older versions. - * - * Only enable this if you know you have a device tree with the latest - * version. - */ - ASSUME_LATEST = 1 << 2, - - /* - * This assumes that it is OK for a failed addition to the device tree, - * due to lack of space or some other problem, to skip any rollback - * steps (such as dropping the property name from the string table). - * This is safe to enable in most circumstances, even though it may - * leave the tree in a sub-optimal state. - */ - ASSUME_NO_ROLLBACK = 1 << 3, - - /* - * This assumes that the device tree components appear in a 'convenient' - * order, i.e. the memory reservation block first, then the structure - * block and finally the string block. - * - * This order is not specified by the device-tree specification, - * but is expected by libfdt. The device-tree compiler always created - * device trees with this order. - * - * This assumption disables a check in fdt_open_into() and removes the - * ability to fix the problem there. This is safe if you know that the - * device tree is correctly ordered. See fdt_blocks_misordered_(). - */ - ASSUME_LIBFDT_ORDER = 1 << 4, - - /* - * This assumes that libfdt itself does not have any internal bugs. It - * drops certain checks that should never be needed unless libfdt has an - * undiscovered bug. - * - * This can generally be considered safe to enable. - */ - ASSUME_LIBFDT_FLAWLESS = 1 << 5, -}; - -/** - * can_assume_() - check if a particular assumption is enabled - * - * @mask: Mask to check (ASSUME_...) - * @return true if that assumption is enabled, else false - */ -static inline bool can_assume_(int mask) -{ - return FDT_ASSUME_MASK & mask; -} - -/** helper macros for checking assumptions */ -#define can_assume(_assume) can_assume_(ASSUME_ ## _assume) - -#endif /* LIBFDT_INTERNAL_H */ diff --git a/libfdt-binding/libfdt/mystr.c b/libfdt-binding/libfdt/mystr.c deleted file mode 100644 index bf7f882f9fc09bbf266cfce14c0dfb1591c7e98f..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/mystr.c +++ /dev/null @@ -1,76 +0,0 @@ -void *memcpy(void *dst, const void *src, unsigned long count); - -int memcmp(const void *vl, const void *vr, unsigned long n) { - const unsigned char *l = vl, *r = vr; - for (; n && *l == *r; n--, l++, r++) - ; - return n ? *l - *r : 0; -} - -void *memchr(const void *src, int c, unsigned long n) { - const unsigned char *s = src; - c = (unsigned char)c; - for (; n && *s != c; s++, n--) - ; - return n ? (void *)s : 0; -} - -void *memmove(void *dest, const void *src, unsigned long n) { - char *d = dest; - const char *s = src; - - if (d == s) - return d; - if ((unsigned long)s - (unsigned long)d - n <= -2 * n) - return memcpy(d, s, n); - - if (d < s) { - for (; n; n--) - *d++ = *s++; - } else { - while (n) - n--, d[n] = s[n]; - } - - return dest; -} - -unsigned long strlen(const char *s) { - const char *a = s; - for (; *s; s++) - ; - return s - a; -} - -unsigned long strnlen(const char *s, unsigned long n) { - const char *p = memchr(s, 0, n); - return p ? (unsigned long)(p - s) : n; -} - -static char *strchrnul(const char *s, int c) { - c = (unsigned char)c; - if (!c) - return (char *)s + strlen(s); - for (; *s && *(unsigned char *)s != c; s++) - ; - return (char *)s; -} - -char *strchr(const char *s, int c) { - char *r = strchrnul(s, c); - return *(unsigned char *)r == (unsigned char)c ? r : 0; -} - -static void *memrchr(const void *m, int c, unsigned long n) { - const unsigned char *s = m; - c = (unsigned char)c; - while (n--) - if (s[n] == c) - return (void *)(s + n); - return 0; -} - - -char *strrchr(const char *s, int c) { return memrchr(s, c, strlen(s) + 1); } - - diff --git a/libfdt-binding/libfdt/wrapper.c b/libfdt-binding/libfdt/wrapper.c deleted file mode 100644 index 62886eecdb0410854ea1b683227fcc1fe8adb167..0000000000000000000000000000000000000000 --- a/libfdt-binding/libfdt/wrapper.c +++ /dev/null @@ -1,298 +0,0 @@ -#include "wrapper.h" -#include "libfdt.h" - -#define PAGE_SIZE 4096 - -int fdt_remove_node(void *fdt, const char *path) { - int node = fdt_path_offset(fdt, path); - if (node < 0) { - return -1; - } - fdt_del_node(fdt, node); - return 0; -} - -int fdt_disable_node(void *fdt, const char *path) { - int len = 0; - int node = fdt_path_offset(fdt, path); - if (node < 0) { - return -1; - } - const char *prop = fdt_getprop(fdt, node, "status", &len); - if (prop == NULL) { - fdt_setprop_string(fdt, node, "status", "disabled"); - return 0; - } - fdt_setprop_inplace(fdt, node, "status", "NILL", len); - return 0; -} - -void fdt_add_virtio(void *fdt, const char *name, uint32_t spi_irq, - uint64_t address) { - int root = fdt_path_offset(fdt, "/"); - int node = fdt_add_subnode(fdt, root, name); - fdt_setprop(fdt, node, "dma-coherent", NULL, 0); - fdt_setprop_string(fdt, node, "compatible", "virtio,mmio"); - fdt32_t irq[3] = { - cpu_to_fdt32(0), - cpu_to_fdt32(spi_irq), - cpu_to_fdt32(0x1), - }; - fdt_setprop(fdt, node, "interrupts", irq, sizeof(irq)); - fdt64_t addr[2] = { - cpu_to_fdt64(address), - cpu_to_fdt64(0x400), - }; - fdt_setprop(fdt, node, "reg", addr, sizeof(addr)); -} - -void fdt_add_vm_service(void *fdt, uint32_t spi_irq, uint64_t address, - uint64_t len) { - int root = fdt_path_offset(fdt, "/"); - int node = fdt_add_subnode(fdt, root, "vm_service"); - fdt_setprop_string(fdt, node, "compatible", "shyper"); - fdt32_t irq[3] = { - cpu_to_fdt32(0), - cpu_to_fdt32(spi_irq), - cpu_to_fdt32(0x1), - }; - fdt_setprop(fdt, node, "interrupts", irq, sizeof(irq)); - if (address != 0 && len != 0) { - fdt64_t addr[2] = { - cpu_to_fdt64(address), - cpu_to_fdt64(len), - }; - fdt_setprop(fdt, node, "reg", addr, sizeof(addr)); - } -} - -void fdt_add_timer(void *fdt, uint32_t trigger_lvl) { - int root = fdt_path_offset(fdt, "/"); - int node = fdt_add_subnode(fdt, root, "timer"); - fdt_setprop_string(fdt, node, "compatible", "arm,armv8-timer"); - fdt32_t irq[12] = { - cpu_to_fdt32(0x1), cpu_to_fdt32(0xd), cpu_to_fdt32(trigger_lvl), - cpu_to_fdt32(0x1), cpu_to_fdt32(0xe), cpu_to_fdt32(trigger_lvl), - cpu_to_fdt32(0x1), cpu_to_fdt32(0xb), cpu_to_fdt32(trigger_lvl), - cpu_to_fdt32(0x1), cpu_to_fdt32(0xa), cpu_to_fdt32(trigger_lvl), - }; - fdt_setprop(fdt, node, "interrupts", irq, sizeof(irq)); -} - -void fdt_add_vm_service_blk(void *fdt, uint32_t spi_irq) { - int root = fdt_path_offset(fdt, "/"); - int node = fdt_add_subnode(fdt, root, "vm_service_blk"); - fdt_setprop_string(fdt, node, "compatible", "shyper_blk"); - fdt32_t irq[3] = { - cpu_to_fdt32(0), - cpu_to_fdt32(spi_irq), - cpu_to_fdt32(0x1), - }; - fdt_setprop(fdt, node, "interrupts", irq, sizeof(irq)); -} - -void fdt_add_cpu(void *fdt, uint64_t linear_id, uint8_t core_id, - uint8_t cluster_id, const char *compatible) { - // NOTE: this function assumes cpu-map does NOT exist and #address-cells = <2> - char node_name[32] = "cpu@x"; - node_name[4] = linear_id + '0'; - int cpus = fdt_path_offset(fdt, "/cpus"); - int node = fdt_add_subnode(fdt, cpus, node_name); - fdt_setprop_string(fdt, node, "compatible", compatible); - fdt_setprop_string(fdt, node, "device_type", "cpu"); - fdt_setprop_string(fdt, node, "enable-method", "psci"); - fdt32_t reg[2] = { - cpu_to_fdt32(0), - cpu_to_fdt32(cluster_id << 8 | core_id), - }; - fdt_setprop(fdt, node, "reg", reg, sizeof(reg)); -} - -void fdt_set_bootcmd(void *fdt, const char *cmdline) { - int node; - node = fdt_path_offset(fdt, "/chosen"); - fdt_setprop_string(fdt, node, "bootargs", cmdline); -} - -void fdt_set_initrd(void *fdt, uint32_t start, uint32_t end) { - // NOTE: linux,initrd-start/end only has one cell (uint32_t) - int node; - node = fdt_path_offset(fdt, "/chosen"); - fdt32_t addr = cpu_to_fdt32((uint32_t)start); - fdt_setprop(fdt, node, "linux,initrd-start", &addr, sizeof(fdt32_t)); - addr = cpu_to_fdt32(end); - fdt_setprop(fdt, node, "linux,initrd-end", &addr, sizeof(fdt32_t)); -} - -void fdt_set_memory(void *fdt, uint64_t region_num, - const struct region *regions, const char *node_name) { - // NOTE: this function dose NOT assume memory_node existed - int r; -#define FDT_MEMORY_REGION_MAX 4 - if (region_num == 0) { - return; - } - if (region_num > FDT_MEMORY_REGION_MAX) { - region_num = FDT_MEMORY_REGION_MAX; - } - int existed = fdt_node_offset_by_prop_value(fdt, 0, "device_type", "memory", - (int)strlen("memory") + 1); - if (existed > 0) { - fdt_del_node(fdt, existed); - } - - int node; - int root = fdt_path_offset(fdt, "/"); - node = fdt_add_subnode(fdt, root, node_name); - if (node < 0) { - return; - } - r = fdt_setprop_string(fdt, node, "device_type", "memory"); - if (r < 0) { - return; - } - - fdt64_t addr[FDT_MEMORY_REGION_MAX * 2]; - for (uint64_t i = 0; i < region_num; ++i) { - addr[2 * i] = cpu_to_fdt64(regions[i].ipa_start); - addr[2 * i + 1] = cpu_to_fdt64(regions[i].length); - } - r = fdt_setprop(fdt, node, "reg", addr, - (int)region_num * 2 * (int)sizeof(fdt64_t)); - if (r < 0) { - return; - } -} - -void fdt_clear_initrd(void *fdt) { - int node; - node = fdt_path_offset(fdt, "/chosen"); - if (node < 0) { - return; - } - fdt_delprop(fdt, node, "linux,initrd-start"); - fdt_delprop(fdt, node, "linux,initrd-end"); -} - -int fdt_setup_gic(void *fdt, uint64_t gicd_addr, uint64_t gicc_addr, - const char *node_name) { - int r; - int node; - node = fdt_node_offset_by_compatible(fdt, 0, "arm,cortex-a15-gic"); - if (node < 0) { - node = fdt_node_offset_by_compatible(fdt, 0, "arm,gic-400"); - if (node < 0) { - return 0; - } - } - fdt64_t addr[4] = { - cpu_to_fdt64(gicd_addr), - cpu_to_fdt64(0x1000), - cpu_to_fdt64(gicc_addr), - cpu_to_fdt64(0x2000), - }; - r = fdt_setprop(fdt, node, "reg", addr, sizeof(addr)); - fdt_nop_property(fdt, node, "interrupts"); - if (r < 0) { - return 0; - } - r = fdt_set_name(fdt, node, node_name); - return 1; -} - -void fdt_setup_serial(void *fdt, const char *compatible, uint64_t addr, - uint32_t spi_irq) { - int r; - int node; - node = fdt_node_offset_by_compatible(fdt, 0, compatible); - if (node < 0) { - return; - } - fdt64_t reg[2] = { - cpu_to_fdt64(addr), - cpu_to_fdt64(0x1000), - }; - r = fdt_setprop(fdt, node, "reg", reg, sizeof(reg)); - if (r < 0) { - return; - } - fdt32_t irq[3] = { - cpu_to_fdt32(0), - cpu_to_fdt32(spi_irq), - cpu_to_fdt32(0x4), - }; - r = fdt_setprop(fdt, node, "interrupts", irq, sizeof(irq)); - if (r < 0) { - return; - } - r = fdt_setprop_string(fdt, node, "status", "okay"); - if (r < 0) { - return; - } - r = fdt_set_name(fdt, node, "serial@0"); -} - -void fdt_set_stdout_path(void *fdt, const char *p) { - int r; - int node; - node = fdt_path_offset(fdt, "/chosen"); - if (node < 0) { - return; - } - r = fdt_setprop_string(fdt, node, "stdout-path", p); -} - -void fdt_clear_stdout_path(void *fdt) { - int node; - node = fdt_path_offset(fdt, "/chosen"); - fdt_delprop(fdt, node, "stdout-path"); -} - -static inline uint64_t round_up(uint64_t value, uint64_t to) { - return ((value + to - 1) / to) * to; -} - -void fdt_enlarge(void *fdt) { - int old_size = fdt_totalsize(fdt); - int new_size = (int)round_up(fdt_totalsize(fdt), PAGE_SIZE) + PAGE_SIZE; - fdt_open_into(fdt, fdt, new_size); -} - -uint64_t fdt_size(void *fdt) { return fdt_totalsize(fdt); } - -int fdt_setup_pmu(void *fdt, const char *compatible, const uint32_t *spi_irq, - uint32_t spi_irq_len, const uint32_t *irq_affi, - uint32_t irq_affi_len) { - const int MAX_LEN = 8; - int r = 0; - if (spi_irq_len != irq_affi_len || spi_irq_len >= MAX_LEN) { - return -1; - } - size_t len = spi_irq_len; - int node = fdt_node_offset_by_compatible(fdt, 0, compatible); - if (node < 0) { - return -1; - } - fdt_delprop(fdt, node, "interrupts"); - fdt_delprop(fdt, node, "interrupt-affinity"); - - fdt32_t irq[3 * MAX_LEN]; - for (size_t i = 0; i < len; i++) { - irq[3 * i] = cpu_to_fdt32(0); - irq[3 * i + 1] = cpu_to_fdt32(spi_irq[i]); - irq[3 * i + 2] = cpu_to_fdt32(0x4); - } - r = fdt_setprop(fdt, node, "interrupts", irq, sizeof(irq[0]) * 3 * len); - if (r < 0) { - return r; - } - fdt32_t affi[MAX_LEN]; - for (size_t i = 0; i < len; i++) { - affi[i] = cpu_to_fdt32(irq_affi[i]); - } - r = fdt_setprop(fdt, node, "interrupt-affinity", affi, sizeof(affi[0]) * len); - if (r < 0) { - return r; - } - return r; -} diff --git a/libfdt-binding/src/lib.rs b/libfdt-binding/src/lib.rs deleted file mode 100644 index 4349a14c269cefe487c33e2c2f69016cee5dff9b..0000000000000000000000000000000000000000 --- a/libfdt-binding/src/lib.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. -// Rust-Shyper is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -#![no_std] -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] - -pub mod myctypes { - pub type c_void = core::ffi::c_void; - - pub type c_char = u8; - pub type c_schar = i8; - pub type c_uchar = u8; - - pub type c_short = i16; - pub type c_ushort = u16; - - pub type c_int = i32; - pub type c_uint = u32; - - pub type c_long = i64; - pub type c_ulong = u64; - - pub type c_longlong = i64; - pub type c_ulonglong = u64; -} - -include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/libfdt-binding/wrapper.h b/libfdt-binding/wrapper.h deleted file mode 100644 index 8f2b7de38e0f4725b1894adc979371dfa8797ee0..0000000000000000000000000000000000000000 --- a/libfdt-binding/wrapper.h +++ /dev/null @@ -1,61 +0,0 @@ -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned long uint64_t; - -// Note: this module assume -// 1. template uses #size-cells = <0x2> and #address-cells = <0x2> -// 2. `chosen` node exists - -struct region { - uint64_t ipa_start; - uint64_t length; -}; - -int fdt_remove_node(void *fdt, const char *path); - -int fdt_disable_node(void *fdt, const char *path); - -void fdt_add_virtio(void *fdt, const char *name, uint32_t spi_irq, - uint64_t address); - -void fdt_add_vm_service(void *fdt, uint32_t spi_irq, uint64_t address, - uint64_t len); - -void fdt_add_timer(void *fdt, uint32_t trigger_lvl); - -void fdt_add_vm_service_blk(void *fdt, uint32_t spi_irq); - -void fdt_add_cpu(void *fdt, uint64_t linear_id, uint8_t core_id, - uint8_t cluster_id, const char *compatible); - -void fdt_set_bootcmd(void *fdt, const char *cmdline); - -void fdt_set_initrd(void *fdt, uint32_t start, uint32_t end); - -void fdt_set_memory(void *fdt, uint64_t region_num, - const struct region *regions, const char *node_name); - -void fdt_clear_initrd(void *fdt); - -int fdt_setup_gic(void *fdt, uint64_t gicd_addr, uint64_t gicc_addr, - const char *node_name); - -void fdt_setup_serial(void *fdt, const char *compatible, uint64_t addr, - uint32_t spi_irq); - -void fdt_set_stdout_path(void *fdt, const char *p); - -void fdt_clear_stdout_path(void *fdt); - -void fdt_enlarge(void *fdt); - -uint64_t fdt_size(void *fdt); - -int fdt_pack(void *fdt); - -int fdt_del_mem_rsv(void *fdt, int n); - -int fdt_setup_pmu(void *fdt, const char *compatible, const uint32_t *spi_irq, - uint32_t spi_irq_len, const uint32_t *irq_affi, - uint32_t irq_affi_len); diff --git a/linkers/aarch64.ld b/linkers/aarch64.ld index e9dfe88be8994fb8cba382c29156228c5baf8845..2f97da31dfaf3d0446e1623ae952fb3868035a73 100644 --- a/linkers/aarch64.ld +++ b/linkers/aarch64.ld @@ -1,4 +1,7 @@ ENTRY(_start) +TARGET(binary) +INPUT("vm0img") +OUTPUT_FORMAT(default) SECTIONS { . = TEXT_START; @@ -21,21 +24,18 @@ SECTIONS .data : { *(.data*) + vm0img(.vm0image) } - . = ALIGN(4096); - _bss_begin = .; .bss (NOLOAD) : ALIGN(4096) { + *(.bss.stack) + + . = ALIGN(4096); + _bss_begin = .; *(.bss*) + . = ALIGN(4096); + _bss_end = .; } - . = ALIGN(4096); - _bss_end = .; _image_end = ABSOLUTE(.); - - . = 0x400000000; - - .cpu_private (NOLOAD) : ALIGN(4096) { - *(.cpu_private) - } } diff --git a/linkers/riscv64.ld b/linkers/riscv64.ld new file mode 100644 index 0000000000000000000000000000000000000000..becd72c894a265acc8891d2bf202f595834711ef --- /dev/null +++ b/linkers/riscv64.ld @@ -0,0 +1,35 @@ +ENTRY(_start) +SECTIONS +{ + . = TEXT_START; + + _image_start = ABSOLUTE(.); + + .boot : { + *(.text.boot) + *(.data.boot) + } + + .text : { + *(.text*) + } + + .rodata : { + *(.rodata*) + } + + + .data : { + *(.data*) + } + + . = ALIGN(4096); + _bss_begin = .; + .bss (NOLOAD) : ALIGN(4096) { + *(.bss*) + } + . = ALIGN(4096); + _bss_end = .; + + _image_end = ABSOLUTE(.); +} diff --git a/linkimg.sh b/linkimg.sh new file mode 100755 index 0000000000000000000000000000000000000000..944752e3f6eee1588e44d7b4135c40e6dad64abd --- /dev/null +++ b/linkimg.sh @@ -0,0 +1,124 @@ +#!/bin/bash +set -e + +RUST_SYHPER= +MVM_IMAGE= +TOOLCHAIN_LD= +LD_FILE= +TEXT_START= +OUTPUT_FILE="a.out" + +function usage { + cat << EOM +Usage: ./$(basename $BASH_SOURCE) [OPTIONS] +This script is used to link Rust_Shyper and linux image. +It supports following options. +OPTIONS: + -h | --help + Displays this help + -i | --image + Specify the hypervisor path + -m | --mvm + Specify the mvm path + -t | --toolchain + Specify the toolchain of the ld + -f | --file + Specify the ld file path + -s | --text-start + + -o | --output + Creates kernel build output in +EOM +} + +# parse input parameters +function parse_input_param { + while [ $# -gt 0 ]; do + case ${1} in + -h | --help) + usage + exit 0 + ;; + -i | --image) + RUST_SYHPER="${2}" + shift 2 + ;; + -m | --mvm) + MVM_IMAGE="${2}" + shift 2 + ;; + -t | --toolchain) + TOOLCHAIN_LD="${2}" + shift 2 + ;; + -f | --file) + LD_FILE="${2}" + shift 2 + ;; + -s | --text-start) + TEXT_START="${2}" + shift 2 + ;; + -o | --output) + OUTPUT_FILE="${2}" + shift 2 + ;; + *) + echo "Error: Invalid option ${1}" + usage + exit 1 + ;; + esac + done +} + +function check_params { + if [ -z "${RUST_SYHPER}" ]; then + echo "Please enter the hypervisor image by -i" + exit 0 + fi + + if [ -z "${MVM_IMAGE}" ]; then + echo "Please enter the mvm image by -m" + exit 0 + fi + + if [ -z "${TOOLCHAIN_LD}" ]; then + echo "Please enter the toolchain parameter by -t" + exit 0 + fi + + if [ -z "${LD_FILE}" ]; then + echo "Please enter the ld file parameter by -f" + exit 0 + fi + + if [ -z "${TEXT_START}" ]; then + echo "Please enter the symbol TEXT_START parameter by -s" + exit 0 + fi +} + +function link_files { + echo "Linking Rust-shyper and image..." + + cp "${MVM_IMAGE}" vm0img + + "${TOOLCHAIN_LD}" "${RUST_SYHPER}" -T "${LD_FILE}" \ + --defsym TEXT_START="${TEXT_START}" -o "${OUTPUT_FILE}" + + if [ "${MVM_IMAGE}" != "vm0img" ]; then + rm -f vm0img + fi + + if [ ! -f "${OUTPUT_FILE}" ]; then + echo "Error: Missing image ${OUTPUT_FILE}" + exit 1 + fi + + echo "Link files successfully." +} + +parse_input_param "$@" +check_params +link_files \ No newline at end of file diff --git a/qemu.bin b/qemu.bin new file mode 100644 index 0000000000000000000000000000000000000000..261778b25deb0dca61cd3c0e648ad07a54814f45 Binary files /dev/null and b/qemu.bin differ diff --git a/rust-toolchain b/rust-toolchain index 63dda16ac91423c40bc1c0d02417dac0c7020b8d..87111cb0aa09304d6ef574eeb572b5d95c837ea0 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-09-14" +channel = "nightly-2023-11-09" components = ["rust-src", "llvm-tools-preview"] diff --git a/src/arch/aarch64/cache.rs b/src/arch/aarch64/cache.rs index 408e17e24d5547bffef4caaa73185143c791e720..1c0a6e51ece0d938a90794bb69f3a974cc3d6484 100644 --- a/src/arch/aarch64/cache.rs +++ b/src/arch/aarch64/cache.rs @@ -10,9 +10,57 @@ use core::arch::global_asm; +/// Date Cache('DC') operations. +/// # Safety: +/// Data cache operations can't trigger any side effects on the safety of the system. +pub mod dc { + macro_rules! define_dc { + ($mode:ident) => { + pub fn $mode(va: usize) { + unsafe { + sysop!(dc, $mode, va as u64); + } + } + }; + } + + define_dc!(ivac); + define_dc!(isw); + define_dc!(cvac); + define_dc!(csw); + define_dc!(cvau); + define_dc!(civac); + define_dc!(cisw); + define_dc!(zva); +} + +/// Instruction Cache('IC') operations. +/// #Safety: +/// Instruction cache operations can't trigger any side effects on the safety of the system. +pub mod ic { + macro_rules! define_ic { + ($mode:ident) => { + pub fn $mode(va: usize) { + unsafe { + sysop!(ic, $mode, va as u64); + } + } + }; + } + + define_ic!(ialluis); + define_ic!(iallu); + define_ic!(ivau); + define_ic!(ivac); + define_ic!(isw); +} + +// TODO: remove the FFI call and use trait for cache operation global_asm!(include_str!("cache.S")); extern "C" { + /// Invalidate the data cache. pub fn cache_invalidate_d(start: usize, len: usize); + /// Clean and invalidate the data cache. pub fn cache_clean_invalidate_d(start: usize, len: usize); } diff --git a/src/arch/aarch64/context_frame.rs b/src/arch/aarch64/context_frame.rs index f8544bc7e89c196f6a5213c94879aef9711b7b65..def56e2390d8992bd109979d5e28382de729fa9b 100644 --- a/src/arch/aarch64/context_frame.rs +++ b/src/arch/aarch64/context_frame.rs @@ -9,21 +9,31 @@ // See the Mulan PSL v2 for more details. use core::arch::global_asm; -use core::fmt::Formatter; +use crate::arch::traits::InterruptContextTrait; +use core::fmt; use cortex_a::registers::*; -use crate::arch::{GICD, GicState}; +use crate::arch::{ + GICD, GicState, CNTVOFF_EL2, VMPIDR_EL2, SP_EL0, SP_EL1, ELR_EL1, SCTLR_EL1, CPACR_EL1, TTBR0_EL1, TTBR1_EL1, + TCR_EL1, ESR_EL1, FAR_EL1, PAR_EL1, MAIR_EL1, AMAIR_EL1, VBAR_EL1, CONTEXTIDR_EL1, TPIDR_EL0, TPIDR_EL1, PMCR_EL0, + HCR_EL2, ACTLR_EL1, CNTP_CTL_EL0, CNTKCTL_EL1, CNTV_CVAL_EL0, CNTV_TVAL_EL0, CNTV_CTL_EL0, TPIDRRO_EL0, + CNTP_TVAL_EL0, CNTVCT_EL0, +}; +use crate::arch::aarch64::regs::{WriteableReg, ReadableReg}; global_asm!(include_str!("fpsimd.S")); extern "C" { + /// Save the floating-point and SIMD registers. pub fn fpsimd_save_ctx(fpsimd_addr: usize); + /// Restore the floating-point and SIMD registers. pub fn fpsimd_restore_ctx(fpsimd_addr: usize); } #[repr(C)] -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone)] +/// Context frame for AArch64. pub struct Aarch64ContextFrame { gpr: [u64; 31], pub spsr: u64, @@ -31,12 +41,12 @@ pub struct Aarch64ContextFrame { sp: u64, } -impl core::fmt::Display for Aarch64ContextFrame { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { +impl fmt::Display for Aarch64ContextFrame { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { for i in 0..31 { write!(f, "x{:02}: {:016x} ", i, self.gpr[i])?; if (i + 1) % 2 == 0 { - write!(f, "\n")?; + writeln!(f)?; } } writeln!(f, "spsr:{:016x}", self.spsr)?; @@ -46,6 +56,12 @@ impl core::fmt::Display for Aarch64ContextFrame { } } +impl fmt::Debug for Aarch64ContextFrame { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "elr {:x} lr {:x}", self.elr, self.gpr[30]) + } +} + impl crate::arch::ContextFrameTrait for Aarch64ContextFrame { fn new(pc: usize, sp: usize, arg: usize) -> Self { let mut r = Aarch64ContextFrame { @@ -55,7 +71,7 @@ impl crate::arch::ContextFrameTrait for Aarch64ContextFrame { + SPSR_EL1::F::Masked + SPSR_EL1::A::Masked + SPSR_EL1::D::Masked) - .value as u64, + .value, elr: pc as u64, sp: sp as u64, }; @@ -101,7 +117,7 @@ impl Aarch64ContextFrame { + SPSR_EL1::F::Masked + SPSR_EL1::A::Masked + SPSR_EL1::D::Masked) - .value as u64, + .value, elr: 0, sp: 0, } @@ -111,6 +127,7 @@ impl Aarch64ContextFrame { #[repr(C)] #[repr(align(16))] #[derive(Copy, Clone, Debug)] +/// Context frame for AArch64. pub struct VmCtxFpsimd { fpsimd: [u64; 64], fpsr: u32, @@ -136,6 +153,7 @@ impl VmCtxFpsimd { #[repr(C)] #[repr(align(16))] #[derive(Debug, Copy, Clone, Default)] +/// Arm GIC irq state struct pub struct GicIrqState { pub id: u64, pub enable: u8, @@ -148,6 +166,7 @@ pub struct GicIrqState { #[repr(C)] #[repr(align(16))] #[derive(Debug, Copy, Clone, Default)] +/// Arm GIC context struct pub struct GicContext { irq_num: usize, pub irq_state: [GicIrqState; 10], @@ -161,8 +180,6 @@ impl GicContext { let idx = self.irq_num; self.irq_state[idx].id = id; self.irq_state[idx].enable = ((GICD.is_enabler(id as usize / 32) >> (id & 32)) & 1) as u8; - // self.irq_state[idx].pend = id; - // self.irq_state[idx].active = id; self.irq_state[idx].priority = GICD.prio(id as usize) as u8; self.irq_state[idx].target = GICD.trgt(id as usize) as u8; self.irq_num += 1; @@ -185,9 +202,9 @@ impl GicContext { } } -#[repr(C)] -#[repr(align(16))] +#[repr(C, align(16))] #[derive(Debug, Copy, Clone)] +/// VM context struct pub struct VmContext { // generic timer pub cntvoff_el2: u64, @@ -240,9 +257,9 @@ pub struct VmContext { pub gic_state: GicState, } -impl VmContext { - pub fn default() -> VmContext { - VmContext { +impl Default for VmContext { + fn default() -> Self { + Self { // generic timer cntvoff_el2: 0, cntp_cval_el0: 0, @@ -263,7 +280,7 @@ impl VmContext { sp_el1: 0, elr_el1: 0, spsr_el1: 0, - sctlr_el1: 0, + sctlr_el1: 0x30C50830, actlr_el1: 0, cpacr_el1: 0, ttbr0_el1: 0, @@ -287,14 +304,28 @@ impl VmContext { // exception pmcr_el0: 0, - vtcr_el2: 0, + vtcr_el2: if cfg!(feature = "lvl4") { + (1 << 31) + + (VTCR_EL2::PS::PA_44B_16TB + + VTCR_EL2::TG0::Granule4KB + + VTCR_EL2::SH0::Inner + + VTCR_EL2::ORGN0::NormalWBRAWA + + VTCR_EL2::IRGN0::NormalWBRAWA + + VTCR_EL2::SL0.val(0b10) // 10: If TG0 is 00 (4KB granule), start at level 0. + + VTCR_EL2::T0SZ.val(64 - 44)) + .value + } else { + 0x8001355c + }, far_el2: 0, hpfar_el2: 0, fpsimd: VmCtxFpsimd::default(), gic_state: GicState::default(), } } +} +impl VmContext { pub fn reset(&mut self) { self.cntvoff_el2 = 0; self.cntp_cval_el0 = 0; @@ -335,101 +366,92 @@ impl VmContext { } pub fn ext_regs_store(&mut self) { - mrs!(self.cntvoff_el2, CNTVOFF_EL2); - // MRS!(self.cntp_cval_el0, CNTP_CVAL_EL0); - mrs!(self.cntv_cval_el0, CNTV_CVAL_EL0); - mrs!(self.cntkctl_el1, CNTKCTL_EL1, "x"); - mrs!(self.cntp_ctl_el0, CNTP_CTL_EL0, "x"); - mrs!(self.cntv_ctl_el0, CNTV_CTL_EL0, "x"); - mrs!(self.cntp_tval_el0, CNTP_TVAL_EL0, "x"); - mrs!(self.cntv_tval_el0, CNTV_TVAL_EL0, "x"); - mrs!(self.cntvct_el0, CNTVCT_EL0); - // MRS!("self.vpidr_el2, VPIDR_EL2, "x"); - mrs!(self.vmpidr_el2, VMPIDR_EL2); - - mrs!(self.sp_el0, SP_EL0); - mrs!(self.sp_el1, SP_EL1); - mrs!(self.elr_el1, ELR_EL1); - mrs!(self.spsr_el1, SPSR_EL1, "x"); - mrs!(self.sctlr_el1, SCTLR_EL1, "x"); - mrs!(self.cpacr_el1, CPACR_EL1, "x"); - mrs!(self.ttbr0_el1, TTBR0_EL1); - mrs!(self.ttbr1_el1, TTBR1_EL1); - mrs!(self.tcr_el1, TCR_EL1); - mrs!(self.esr_el1, ESR_EL1, "x"); - mrs!(self.far_el1, FAR_EL1); - mrs!(self.par_el1, PAR_EL1); - mrs!(self.mair_el1, MAIR_EL1); - mrs!(self.amair_el1, AMAIR_EL1); - mrs!(self.vbar_el1, VBAR_EL1); - mrs!(self.contextidr_el1, CONTEXTIDR_EL1, "x"); - mrs!(self.tpidr_el0, TPIDR_EL0); - mrs!(self.tpidr_el1, TPIDR_EL1); - mrs!(self.tpidrro_el0, TPIDRRO_EL0); - - mrs!(self.pmcr_el0, PMCR_EL0); - mrs!(self.vtcr_el2, VTCR_EL2); - mrs!(self.hcr_el2, HCR_EL2); - // MRS!(self.cptr_el2, CPTR_EL2); - // MRS!(self.hstr_el2, HSTR_EL2); - // MRS!(self.far_el2, FAR_EL2); - // MRS!(self.hpfar_el2, HPFAR_EL2); - mrs!(self.actlr_el1, ACTLR_EL1); - // println!("save sctlr {:x}", self.sctlr_el1); + self.cntvoff_el2 = CNTVOFF_EL2::read(); + self.cntv_cval_el0 = CNTV_CVAL_EL0::read(); + self.cntkctl_el1 = CNTKCTL_EL1::read() as u32; + self.cntp_ctl_el0 = CNTP_CTL_EL0::read() as u32; + self.cntv_ctl_el0 = CNTV_CTL_EL0::read() as u32; + self.cntp_tval_el0 = CNTP_TVAL_EL0::read() as u32; + self.cntv_tval_el0 = CNTV_TVAL_EL0::read() as u32; + self.cntvct_el0 = CNTVCT_EL0::read(); + self.vmpidr_el2 = VMPIDR_EL2::read(); + self.sp_el0 = SP_EL0::read(); + self.sp_el1 = SP_EL1::read(); + self.elr_el1 = ELR_EL1::read(); + self.spsr_el1 = SPSR_EL1.get() as u32; + self.sctlr_el1 = SCTLR_EL1::read() as u32; + self.cpacr_el1 = CPACR_EL1::read() as u32; + self.ttbr0_el1 = TTBR0_EL1::read(); + self.ttbr1_el1 = TTBR1_EL1::read(); + self.tcr_el1 = TCR_EL1::read(); + self.esr_el1 = ESR_EL1::read() as u32; + self.far_el1 = FAR_EL1::read(); + self.par_el1 = PAR_EL1::read(); + self.mair_el1 = MAIR_EL1::read(); + self.amair_el1 = AMAIR_EL1::read(); + self.vbar_el1 = VBAR_EL1::read(); + self.contextidr_el1 = CONTEXTIDR_EL1::read() as u32; + self.tpidr_el0 = TPIDR_EL0::read(); + self.tpidr_el1 = TPIDR_EL1::read(); + self.tpidrro_el0 = TPIDRRO_EL0::read(); + self.pmcr_el0 = PMCR_EL0::read(); + self.vtcr_el2 = VTCR_EL2.get(); + self.hcr_el2 = HCR_EL2::read(); + self.actlr_el1 = ACTLR_EL1::read(); } pub fn ext_regs_restore(&self) { - // println!("restore CNTV_CTL_EL0 {:x}", self.cntv_ctl_el0); - // println!("restore CNTV_CVAL_EL0 {:x}", self.cntv_cval_el0); - msr!(CNTVOFF_EL2, self.cntvoff_el2); - // MSR!(CNTP_CVAL_EL0, self.cntp_cval_el0); - msr!(CNTV_CVAL_EL0, self.cntv_cval_el0); - msr!(CNTKCTL_EL1, self.cntkctl_el1, "x"); - // MSR!(CNTP_CTL_EL0, self.cntp_ctl_el0, "x"); - msr!(CNTV_CTL_EL0, self.cntv_ctl_el0, "x"); - // MSR!(CNTP_TVAL_EL0, {0:x}", in(reg) self.cntp_tval_el0, "x"); - // MSR!(CNTV_TVAL_EL0, {0:x}", in(reg) self.cntv_tval_el0, "x"); - - // MSR!(VPIDR_EL2, self.vpidr_el2, "x"); - msr!(VMPIDR_EL2, self.vmpidr_el2); - - msr!(SP_EL0, self.sp_el0); - msr!(SP_EL1, self.sp_el1); - msr!(ELR_EL1, self.elr_el1); - msr!(SPSR_EL1, self.spsr_el1, "x"); - msr!(SCTLR_EL1, self.sctlr_el1, "x"); - msr!(CPACR_EL1, self.cpacr_el1, "x"); - msr!(TTBR0_EL1, self.ttbr0_el1); - msr!(TTBR1_EL1, self.ttbr1_el1); - msr!(TCR_EL1, self.tcr_el1); - msr!(ESR_EL1, self.esr_el1, "x"); - msr!(FAR_EL1, self.far_el1); - msr!(PAR_EL1, self.par_el1); - msr!(MAIR_EL1, self.mair_el1); - msr!(AMAIR_EL1, self.amair_el1); - msr!(VBAR_EL1, self.vbar_el1); - msr!(CONTEXTIDR_EL1, self.contextidr_el1, "x"); - msr!(TPIDR_EL0, self.tpidr_el0); - msr!(TPIDR_EL1, self.tpidr_el1); - msr!(TPIDRRO_EL0, self.tpidrro_el0); - - msr!(PMCR_EL0, self.pmcr_el0); - msr!(VTCR_EL2, self.vtcr_el2); - msr!(HCR_EL2, self.hcr_el2); - // MSR!(CPTR_EL2, self.cptr_el2); - // MSR!(HSTR_EL2, self.hstr_el2); - // MSR!(FAR_EL2, self.far_el2); - // MSR!(HPFAR_EL2, self.hpfar_el2); - msr!(ACTLR_EL1, self.actlr_el1); + // SAFETY: + // 1. The registers has defined as valid register + // 2. The value is read from the register or + // the value is reset by the hypervisor correctly + unsafe { + CNTVOFF_EL2::write(self.cntvoff_el2); + CNTV_CVAL_EL0::write(self.cntv_cval_el0); + CNTKCTL_EL1::write(self.cntkctl_el1 as usize); + CNTP_CTL_EL0::write(self.cntp_ctl_el0 as usize); + VMPIDR_EL2::write(self.vmpidr_el2); + SP_EL0::write(self.sp_el0); + SP_EL1::write(self.sp_el1); + ELR_EL1::write(self.elr_el1); + SPSR_EL1.set(self.spsr_el1 as u64); + SCTLR_EL1::write(self.sctlr_el1 as usize); + CPACR_EL1::write(self.cpacr_el1 as usize); + TTBR0_EL1::write(self.ttbr0_el1); + TTBR1_EL1::write(self.ttbr1_el1); + TCR_EL1::write(self.tcr_el1); + ESR_EL1::write(self.esr_el1 as usize); + FAR_EL1::write(self.far_el1); + PAR_EL1::write(self.par_el1); + MAIR_EL1::write(self.mair_el1); + AMAIR_EL1::write(self.amair_el1); + VBAR_EL1::write(self.vbar_el1); + CONTEXTIDR_EL1::write(self.contextidr_el1 as usize); + TPIDR_EL0::write(self.tpidr_el0); + TPIDR_EL1::write(self.tpidr_el1); + PMCR_EL0::write(self.pmcr_el0); + VTCR_EL2.set(self.vtcr_el2); + HCR_EL2::write(self.hcr_el2); + ACTLR_EL1::write(self.actlr_el1); + } } - pub fn fpsimd_save_context(&self) { + pub fn fpsimd_save_context(&mut self) { + // SAFETY: + // We use the address of fpsimd to save the all floating point register + // eg. Q0-Q31, FPSR, FPCR + // And the address is valid + // And the value of fpsimd will be changed. unsafe { fpsimd_save_ctx(&self.fpsimd as *const _ as usize); } } pub fn fpsimd_restore_context(&self) { + // SAFETY: + // We use the address of fpsimd to restore the all floating point register + // eg. Q0-Q31, FPSR, FPCR + // And the address is valid unsafe { fpsimd_restore_ctx(&self.fpsimd as *const _ as usize); } diff --git a/src/arch/aarch64/cpu.rs b/src/arch/aarch64/cpu.rs index b045ce0aaa0dc6f581827bccf1548cb9959c11f9..f1aed9e835c8e34faf6711f397dbec008a9c1246 100644 --- a/src/arch/aarch64/cpu.rs +++ b/src/arch/aarch64/cpu.rs @@ -8,7 +8,7 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use cortex_a::registers::DAIF; +use cortex_a::registers::{DAIF, TPIDR_EL2}; use tock_registers::interfaces::*; /// Mask (disable) interrupt from perspective of CPU @@ -23,6 +23,18 @@ pub fn cpu_interrupt_unmask() { DAIF.write(DAIF::I::Unmasked) } +/// return the current interrupt mask state pub fn cpu_daif() -> u64 { DAIF.read(DAIF::I) } + +#[inline(always)] +pub fn current_cpu_arch() -> u64 { + TPIDR_EL2.get() +} + +/// # Safety: +/// The 'cpu_addr' must be a valid address of 'Cpu' struct. +pub unsafe fn set_current_cpu(cpu_addr: u64) { + TPIDR_EL2.set(cpu_addr); +} diff --git a/src/arch/aarch64/exception.S b/src/arch/aarch64/exception.S index 9feed208d48d553ab093769be760291a05672e4e..4a92529301387f2c3bfb789f8ba739ce8f4d1bad 100644 --- a/src/arch/aarch64/exception.S +++ b/src/arch/aarch64/exception.S @@ -117,7 +117,8 @@ fresh_cpu: add sp, sp, #8 ret -.global fresh_hyper -fresh_hyper: +.global fresh_hyper_asm +fresh_hyper_asm: bl fresh_cpu - b context_vm_entry \ No newline at end of file + b context_vm_entry + diff --git a/src/arch/aarch64/exception.rs b/src/arch/aarch64/exception.rs index eb5fff3733fa55c7b93be60a2defeaf0ad356ecc..2070977ae6472c86f5dc7a383653bde37d782114 100644 --- a/src/arch/aarch64/exception.rs +++ b/src/arch/aarch64/exception.rs @@ -12,14 +12,12 @@ use core::arch::global_asm; use tock_registers::interfaces::*; -use crate::arch::{ContextFrameTrait, data_abort_handler, hvc_handler, smc_handler}; +use crate::arch::{ContextFrameTrait, data_abort_handler, hvc_handler, smc_handler, sysreg_handler, isb, at, HPFAR_EL2}; +use crate::arch::aarch64::regs::ReadableReg; use crate::arch::{gicc_clear_current_irq, gicc_get_current_irq}; use crate::arch::ContextFrame; -use crate::kernel::{active_vm_id, current_cpu, FRESH_IRQ_LOGIC_LOCK, FRESH_LOGIC_LOCK, fresh_status, FreshStatus}; +use crate::kernel::{active_vm_id, current_cpu}; use crate::kernel::interrupt_handler; -use crate::lib::time_current_us; - -// use crate::lib::time_current_us; global_asm!(include_str!("exception.S")); @@ -45,9 +43,7 @@ fn exception_far() -> usize { #[inline(always)] fn exception_hpfar() -> usize { - let hpfar: u64; - mrs!(hpfar, HPFAR_EL2); - hpfar as usize + HPFAR_EL2::read() as usize } #[allow(non_upper_case_globals)] @@ -55,15 +51,6 @@ const ESR_ELx_S1PTW_SHIFT: usize = 7; #[allow(non_upper_case_globals)] const ESR_ELx_S1PTW: usize = 1 << ESR_ELx_S1PTW_SHIFT; -macro_rules! arm_at { - ($at_op:expr, $addr:expr) => { - unsafe { - core::arch::asm!(concat!("AT ", $at_op, ", {0}"), in(reg) $addr, options(nomem, nostack)); - core::arch::asm!("isb"); - } - }; -} - fn translate_far_to_hpfar(far: usize) -> Result { /* * We have @@ -79,7 +66,8 @@ fn translate_far_to_hpfar(far: usize) -> Result { use cortex_a::registers::PAR_EL1; let par = PAR_EL1.get(); - arm_at!("s1e1r", far); + at::s1e1r(far); + isb(); let tmp = PAR_EL1.get(); PAR_EL1.set(par); if (tmp & PAR_EL1::F::TranslationAborted.value) != 0 { @@ -95,7 +83,7 @@ pub fn exception_fault_addr() -> usize { let far = exception_far(); let hpfar = if (exception_esr() & ESR_ELx_S1PTW) == 0 && exception_data_abort_is_permission_fault() { translate_far_to_hpfar(far).unwrap_or_else(|_| { - println!("error happen in translate_far_to_hpfar"); + debug!("error happen in translate_far_to_hpfar"); 0 }) } else { @@ -104,6 +92,7 @@ pub fn exception_fault_addr() -> usize { (far & 0xfff) | (hpfar << 8) } +/// Get the length of the instruction that caused the exception /// \return 1 means 32-bit instruction, 0 means 16-bit instruction #[inline(always)] fn exception_instruction_length() -> usize { @@ -172,7 +161,6 @@ extern "C" fn current_el_sp0_synchronous() { #[no_mangle] extern "C" fn current_el_sp0_irq() { - // lower_aarch64_irq(ctx); panic!("current_el_sp0_irq"); } @@ -195,8 +183,7 @@ extern "C" fn current_el_spx_synchronous() { } #[no_mangle] -extern "C" fn current_el_spx_irq(ctx: *mut ContextFrame) { - // println!("current_el_spx_irq"); +extern "C" fn current_el_spx_irq(ctx: &mut ContextFrame) { lower_aarch64_irq(ctx); } @@ -206,102 +193,60 @@ extern "C" fn current_el_spx_serror() { } #[no_mangle] -extern "C" fn lower_aarch64_synchronous(ctx: *mut ContextFrame) { - // println!("lower_aarch64_synchronous"); - let status = fresh_status(); - if status != FreshStatus::None { - if status != FreshStatus::Finish { - println!("lower_aarch64_synchronous: illegal fresh status {:#?}", status); - let time0 = time_current_us(); - FRESH_LOGIC_LOCK.lock(); - let time1 = time_current_us(); - println!("lower_aarch64_synchronous: wait live update {} us", time1 - time0); - } +extern "C" fn lower_aarch64_synchronous(ctx: &mut ContextFrame) { + unsafe { + current_cpu().set_ctx(ctx); } - current_cpu().set_ctx(ctx); match exception_class() { 0x24 => { - // println!("Core[{}] data_abort_handler", cpu_id()); data_abort_handler(); } + 0x18 => { + sysreg_handler(exception_iss() as u32); + } 0x17 => { smc_handler(); } 0x16 => { hvc_handler(); } - _ => unsafe { - println!( + _ => { + debug!( "x0 {:x}, x1 {:x}, x29 {:x}", (*ctx).gpr(0), (*ctx).gpr(1), (*ctx).gpr(29) ); panic!( - "core {} vm {}: handler not presents for EC_{} @ipa 0x{:x}, @pc 0x{:x}", + "core {} vm {}: handler not presents for EC_{:b} @ipa 0x{:x}, @pc 0x{:x}", current_cpu().id, active_vm_id(), exception_class(), exception_fault_addr(), (*ctx).exception_pc() ); - }, + } } current_cpu().clear_ctx(); } #[no_mangle] -extern "C" fn lower_aarch64_irq(ctx: *mut ContextFrame) { - current_cpu().set_ctx(ctx); - let (id, src) = gicc_get_current_irq(); - // if current_cpu().id == 2 { - // println!( - // "Core[{}] lower_aarch64_irq {} 0x{:x} x30 {:x} x19 {:x} x0 {:x}", - // current_cpu().id, - // id, - // current_cpu().get_elr(), - // current_cpu().get_gpr(30), - // current_cpu().get_gpr(19), - // current_cpu().get_gpr(0) - // ); - // } - match fresh_status() { - FreshStatus::FreshVM | FreshStatus::Start => { - // if active_vm().unwrap().has_interrupt(id) { - // println!("lower_aarch64_irq: wait for fresh vm and vcpu"); - // let time0 = time_current_us(); - FRESH_IRQ_LOGIC_LOCK.lock(); - // let time1 = time_current_us(); - // println!("lower_aarch64_irq: wait {} us", time1 - time0); - // } else { - // FRESH_LOGIC_LOCK.lock(); - // } - } - // FreshStatus::FreshVCPU => { - // if !active_vm().unwrap().has_interrupt(id) { - // FRESH_LOGIC_LOCK.lock(); - // } - // } - _ => {} +extern "C" fn lower_aarch64_irq(ctx: &mut ContextFrame) { + unsafe { + current_cpu().set_ctx(ctx); } + if let Some(id) = gicc_get_current_irq() { + if id >= 1022 { + return; + } + // use crate::lib::time_current_us; + // let begin = time_current_us(); + let handled_by_hypervisor = interrupt_handler(id); + // let end = time_current_us(); - if id >= 1022 { - return; + gicc_clear_current_irq(handled_by_hypervisor); } - // use crate::lib::time_current_us; - // let begin = time_current_us(); - let handled_by_hypervisor = interrupt_handler(id, src); - // let end = time_current_us(); - - gicc_clear_current_irq(handled_by_hypervisor); current_cpu().clear_ctx(); - // if current_cpu().active_vcpu.is_some() - // && current_cpu().active_vcpu.as_ref().unwrap().vm().is_some() - // && active_vm_id() == 2 - // && current_cpu().id == 2 - // { - // println!("Core{} VM2 end lower_aarch64_irq irq {}", current_cpu().id, id); - // } } #[no_mangle] diff --git a/src/arch/aarch64/gic.rs b/src/arch/aarch64/gic.rs index 7d66c4c344c3bd2d3c15bebfdad761b4233243e4..4f9d9b0fe52c08e8aea02a0ce797e568b8623488 100644 --- a/src/arch/aarch64/gic.rs +++ b/src/arch/aarch64/gic.rs @@ -9,16 +9,19 @@ // See the Mulan PSL v2 for more details. use alloc::collections::BTreeSet; +use core::sync::atomic::{AtomicUsize, Ordering}; use spin::Mutex; use tock_registers::*; use tock_registers::interfaces::*; use tock_registers::registers::*; +use crate::arch::traits::InterruptController; +use crate::arch::IntCtrl; use crate::board::{Platform, PlatOperation}; use crate::kernel::current_cpu; -use crate::kernel::INTERRUPT_NUM_MAX; -use crate::lib::{bit_extract, trace}; +use crate::utils::bit_extract; +use crate::utils::device_ref::DeviceRef; // GICD BITS const GICD_CTLR_EN_BIT: usize = 0x1; @@ -32,9 +35,9 @@ const GICH_HCR_LRENPIE_BIT: usize = 1 << 2; pub const GIC_SGIS_NUM: usize = 16; const GIC_PPIS_NUM: usize = 16; -pub const GIC_INTS_MAX: usize = INTERRUPT_NUM_MAX; +pub const GIC_INTS_MAX: usize = IntCtrl::NUM_MAX; pub const GIC_PRIVINT_NUM: usize = GIC_SGIS_NUM + GIC_PPIS_NUM; -pub const GIC_SPI_MAX: usize = INTERRUPT_NUM_MAX - GIC_PRIVINT_NUM; +pub const GIC_SPI_MAX: usize = IntCtrl::NUM_MAX - GIC_PRIVINT_NUM; pub const GIC_PRIO_BITS: usize = 8; pub const GIC_TARGET_BITS: usize = 8; pub const GIC_TARGETS_MAX: usize = GIC_TARGET_BITS; @@ -53,7 +56,7 @@ pub const GICD_TYPER_CPUNUM_OFF: usize = 5; // pub const GICD_TYPER_CPUNUM_LEN: usize = 3; pub const GICD_TYPER_CPUNUM_MSK: usize = 0b11111; -pub static GIC_LRS_NUM: Mutex = Mutex::new(0); +pub static GIC_LRS_NUM: AtomicUsize = AtomicUsize::new(0); static GICD_LOCK: Mutex<()> = Mutex::new(()); @@ -95,7 +98,7 @@ impl IrqState { } } - pub fn to_num(&self) -> usize { + pub fn to_num(self) -> usize { match self { IrqState::IrqSInactive => 0, IrqState::IrqSPend => 1, @@ -115,7 +118,7 @@ pub struct GicDesc { register_structs! { #[allow(non_snake_case)] - pub GicDistributorBlock { + pub GicDistributor { (0x0000 => CTLR: ReadWrite), (0x0004 => TYPER: ReadOnly), (0x0008 => IIDR: ReadOnly), @@ -141,29 +144,10 @@ register_structs! { } } -pub struct GicDistributor { - base_addr: usize, -} - -impl core::ops::Deref for GicDistributor { - type Target = GicDistributorBlock; - fn deref(&self) -> &Self::Target { - if self.base_addr < 0x1000 { - panic!("illegal gicd addr {}", self.base_addr); - } - unsafe { &*self.ptr() } - } -} +// SAFETY: GicDistributor is a register block, can be safely shared between threads. +unsafe impl Sync for GicDistributor {} impl GicDistributor { - const fn new(base_addr: usize) -> GicDistributor { - GicDistributor { base_addr } - } - - pub fn ptr(&self) -> *const GicDistributorBlock { - self.base_addr as *const GicDistributorBlock - } - pub fn is_enabler(&self, idx: usize) -> u32 { self.ISENABLER[idx].get() } @@ -245,7 +229,6 @@ impl GicDistributor { } pub fn send_sgi(&self, cpu_if: usize, sgi_num: usize) { - // println!("Core {} send ipi to cpu {}", cpu_id(), cpu_if); self.SGIR.set(((1 << (16 + cpu_if)) | (sgi_num & 0b1111)) as u32); } @@ -281,13 +264,11 @@ impl GicDistributor { let lock = GICD_LOCK.lock(); let prev = self.ITARGETSR[idx].get(); let value = (prev & !mask) | (((trgt as u32) << off) & mask); - // println!("idx {}, val {:x}", idx, value); self.ITARGETSR[idx].set(value); drop(lock); } pub fn set_enable(&self, int_id: usize, en: bool) { - // println!("gicd::set_enbale: en {}, int_id {}", en, int_id); let idx = int_id / 32; let bit = 1 << (int_id % 32); @@ -313,7 +294,7 @@ impl GicDistributor { } } else { let reg_ind = int_id / 32; - let mask = 1 << int_id % 32; + let mask = 1 << (int_id % 32); if pend { self.ISPENDR[reg_ind].set(mask); } else { @@ -326,7 +307,7 @@ impl GicDistributor { pub fn set_act(&self, int_id: usize, act: bool) { let reg_ind = int_id / 32; - let mask = 1 << int_id % 32; + let mask = 1 << (int_id % 32); let lock = GICD_LOCK.lock(); if act { @@ -363,7 +344,7 @@ impl GicDistributor { pub fn state(&self, int_id: usize) -> usize { let reg_ind = int_id / 32; - let mask = 1 << int_id % 32; + let mask = 1 << (int_id % 32); let lock = GICD_LOCK.lock(); let pend = if (self.ISPENDR[reg_ind].get() & mask) != 0 { @@ -377,13 +358,13 @@ impl GicDistributor { 0 }; drop(lock); - return pend | act; + pend | act } } register_structs! { #[allow(non_snake_case)] - pub GicCpuInterfaceBlock { + pub GicCpuInterface { (0x0000 => CTLR: ReadWrite), // CPU Interface Control Register (0x0004 => PMR: ReadWrite), // Interrupt Priority Mask Register (0x0008 => BPR: ReadWrite), // Binary Point Register @@ -407,29 +388,9 @@ register_structs! { } } -pub struct GicCpuInterface { - base_addr: usize, -} - -impl core::ops::Deref for GicCpuInterface { - type Target = GicCpuInterfaceBlock; - fn deref(&self) -> &Self::Target { - if self.base_addr < 0x1000 { - panic!("illegal gicc addr {}", self.base_addr); - } - unsafe { &*self.ptr() } - } -} +unsafe impl Sync for GicCpuInterface {} impl GicCpuInterface { - pub const fn new(base_addr: usize) -> GicCpuInterface { - GicCpuInterface { base_addr } - } - - pub fn ptr(&self) -> *const GicCpuInterfaceBlock { - self.base_addr as *const GicCpuInterfaceBlock - } - fn init(&self) { for i in 0..gich_lrs_num() { GICH.LR[i].set(0); @@ -437,11 +398,6 @@ impl GicCpuInterface { self.PMR.set(u32::MAX); let ctlr_prev = self.CTLR.get(); - // println!( - // "ctlr: {:x}, gich_lrs_num {}", - // ctlr_prev | GICC_CTLR_EN_BIT as u32 | GICC_CTLR_EOImodeNS_BIT as u32, - // gich_lrs_num() - // ); self.CTLR .set(ctlr_prev | GICC_CTLR_EN_BIT as u32 | GICC_CTLR_EOIMODENS_BIT as u32); @@ -480,7 +436,7 @@ impl GicCpuInterface { register_structs! { #[allow(non_snake_case)] - pub GicHypervisorInterfaceBlock { + pub GicHypervisorInterface { (0x0000 => HCR: ReadWrite), (0x0004 => VTR: ReadOnly), (0x0008 => VMCR: ReadWrite), @@ -499,29 +455,9 @@ register_structs! { } } -pub struct GicHypervisorInterface { - base_addr: usize, -} - -impl core::ops::Deref for GicHypervisorInterface { - type Target = GicHypervisorInterfaceBlock; - fn deref(&self) -> &Self::Target { - if trace() && self.base_addr < 0x1000 { - panic!(""); - } - unsafe { &*self.ptr() } - } -} +unsafe impl Sync for GicHypervisorInterface {} impl GicHypervisorInterface { - const fn new(base_addr: usize) -> GicHypervisorInterface { - GicHypervisorInterface { base_addr } - } - - pub fn ptr(&self) -> *const GicHypervisorInterfaceBlock { - self.base_addr as *const GicHypervisorInterfaceBlock - } - pub fn hcr(&self) -> u32 { self.HCR.get() } @@ -566,8 +502,8 @@ pub struct GicState { pub ctlr: u32, } -impl GicState { - pub fn default() -> GicState { +impl Default for GicState { + fn default() -> Self { GicState { hcr: 0, eisr: [0; GIC_LIST_REGS_NUM / 32], @@ -577,56 +513,42 @@ impl GicState { ctlr: 0, } } +} - pub fn save_state(&mut self) { +impl crate::arch::InterruptContextTrait for GicState { + fn save_state(&mut self) { self.hcr = GICH.hcr(); self.apr = GICH.APR.get(); for i in 0..(GIC_LIST_REGS_NUM / 32) { self.eisr[i] = GICH.eisr(i); self.elrsr[i] = GICH.elrsr(i); } - // println!("save state"); - // println!("GICH hcr {:x}", self.hcr); - // println!("GICH apr {:x}", self.apr); - // println!("GICH eisr {:x}", self.eisr[0]); - // println!("GICH elrsr {:x}", self.elrsr[0]); for i in 0..gich_lrs_num() { if self.elrsr[0] & 1 << i == 0 { self.lr[i] = GICH.lr(i); } else { self.lr[i] = 0; } - // println!("GICH_LR[{}] {:x}", i, GICH.lr(i)); } self.ctlr = GICC.CTLR.get(); } - pub fn restore_state(&self) { - // println!("before restore"); - // println!("GICH hcr {:x}", GICH.hcr()); - // println!("GICC ctlr {:x}", GICC.CTLR.get()); - // for i in 0..gich_lrs_num() { - // println!("lr[{}] {:x}", i, GICH.lr(i)); - // } - - // println!("after restore state"); + fn restore_state(&self) { GICH.set_hcr(self.hcr); GICH.APR.set(self.apr); - // println!("GICH hcr {:x}", self.hcr); - // println!("GICH apr {:x}", self.apr); for i in 0..gich_lrs_num() { - // println!("lr[{}] {:x}", i, self.lr[i]); GICH.set_lr(i, self.lr[i]); } GICC.CTLR.set(self.ctlr); - // println!("GICC ctlr {:x}", self.ctlr); } } -pub static GICD: GicDistributor = GicDistributor::new(Platform::GICD_BASE + 0x8_0000_0000); -pub static GICC: GicCpuInterface = GicCpuInterface::new(Platform::GICC_BASE + 0x8_0000_0000); -pub static GICH: GicHypervisorInterface = GicHypervisorInterface::new(Platform::GICH_BASE + 0x8_0000_0000); +// SAFETY: They are GICv2 mmio device regions +pub static GICD: DeviceRef = unsafe { DeviceRef::new(Platform::GICD_BASE as *const GicDistributor) }; +pub static GICC: DeviceRef = unsafe { DeviceRef::new(Platform::GICC_BASE as *const GicCpuInterface) }; +pub static GICH: DeviceRef = + unsafe { DeviceRef::new(Platform::GICH_BASE as *const GicHypervisorInterface) }; #[inline(always)] pub fn gich_lrs_num() -> usize { @@ -671,31 +593,28 @@ pub fn gicc_clear_current_irq(for_hypervisor: bool) { let gicc = &GICC; gicc.EOIR.set(irq); if for_hypervisor { - // let addr = 0x08010000 + 0x1000; - // unsafe { - // let gicc_dir = addr as *mut u32; - // *gicc_dir = irq; - // } gicc.DIR.set(irq); } let irq = 0; current_cpu().current_irq = irq; } -pub fn gicc_get_current_irq() -> (usize, usize) { +pub fn gicc_get_current_irq() -> Option { let iar = GICC.IAR.get(); let irq = iar as usize; current_cpu().current_irq = irq; let id = bit_extract(iar as usize, 0, 10); - let src = bit_extract(iar as usize, 10, 3); - (id, src) + if id >= IntCtrl::NUM_MAX { + None + } else { + Some(id) + } } pub fn gic_lrs() -> usize { - *GIC_LRS_NUM.lock() + GIC_LRS_NUM.load(Ordering::Relaxed) } pub fn set_gic_lrs(lrs: usize) { - let mut gic_lrs = GIC_LRS_NUM.lock(); - *gic_lrs = lrs; + GIC_LRS_NUM.store(lrs, Ordering::Relaxed); } diff --git a/src/arch/aarch64/gicv3.rs b/src/arch/aarch64/gicv3.rs new file mode 100644 index 0000000000000000000000000000000000000000..477df0c59b794f27a35f18404919712d98127910 --- /dev/null +++ b/src/arch/aarch64/gicv3.rs @@ -0,0 +1,1234 @@ +// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. +// Rust-Shyper is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use core::fmt; +use core::mem::size_of; +use core::sync::atomic::{AtomicUsize, Ordering}; + +use alloc::collections::BTreeSet; + +use spin::Mutex; +use tock_registers::*; +use tock_registers::interfaces::*; +use tock_registers::registers::*; + +use crate::arch::traits::InterruptController; +use crate::arch::aarch64::regs::{WriteableReg, ReadableReg}; +use crate::arch::{ + isb, IntCtrl, ICC_SGI1R_EL1, ICH_HCR_EL2, ICH_VTR_EL2, ICC_SRE_EL2, ICC_SRE_EL1, ICH_VMCR_EL2, ICH_AP0R2_EL2, + ICH_AP0R1_EL2, ICH_AP0R0_EL2, ICH_AP1R0_EL2, ICH_AP1R1_EL2, ICH_AP1R2_EL2, ICC_PMR_EL1, ICC_BPR1_EL1, ICC_CTLR_EL1, + ICH_ELRSR_EL2, ICC_IGRPEN1_EL1, ICC_DIR_EL1, ICC_EOIR1_EL1, ICH_LR0_EL2, ICH_LR1_EL2, ICH_LR2_EL2, ICH_LR3_EL2, + ICH_LR4_EL2, ICH_LR5_EL2, ICH_LR6_EL2, ICH_LR7_EL2, ICH_LR8_EL2, ICH_LR9_EL2, ICH_LR10_EL2, ICH_LR11_EL2, + ICH_LR12_EL2, ICH_LR13_EL2, ICH_LR14_EL2, ICH_LR15_EL2, ICH_EISR_EL2, ICH_MISR_EL2, +}; +use crate::board::{Platform, PlatOperation}; +use crate::utils::bit_extract; +use crate::kernel::current_cpu; +use crate::utils::device_ref::DeviceRef; + +use super::ICC_IAR1_EL1; + +pub const MPIDR_AFF_MSK: usize = 0xffff; //we are only supporting 2 affinity levels + +// GICD BITS +const GICD_CTLR_ENS_BIT: usize = 0x1; +const GICD_CTLR_ENNS_BIT: usize = 0b10; +pub const GICD_CTLR_ARE_NS_BIT: usize = 0x1 << 4; +pub const GICD_IROUTER_INV: usize = !MPIDR_AFF_MSK; +pub const GICD_IROUTER_RES0_MSK: usize = (1 << 40) - 1; +pub const GICD_IROUTER_IRM_BIT: usize = 1 << 31; +pub const GICD_TYPER_IDBITS_OFF: usize = 19; +pub const GICD_TYPER_IDBITS_LEN: usize = 5; +pub const GICD_TYPER_IDBITS_MSK: usize = (((1 << ((GICD_TYPER_IDBITS_LEN) - 1)) << 1) - 1) << (GICD_TYPER_IDBITS_OFF); +const GICD_IROUTER_AFF_MSK: usize = GICD_IROUTER_RES0_MSK & !GICD_IROUTER_IRM_BIT; +pub const GICD_TYPER_LPIS: usize = 1 << 17; + +//GICR BITS +pub const GICR_TYPER_PRCNUM_OFF: usize = 8; +pub const GICR_TYPER_AFFVAL_OFF: usize = 32; +pub const GICR_TYPER_LAST_OFF: usize = 4; +const GICR_WAKER_PSLEEP_BIT: usize = 0x2; +const GICR_WAKER_CASLEEP_BIT: usize = 0x4; + +// GICC BITS +pub const GICC_CTLR_EN_BIT: usize = 0x1; +pub const GICC_CTLR_EOIMODENS_BIT: usize = 1 << 9; +pub const GICC_SRE_SRE_BIT: usize = 0x1; +pub const GICC_CTLR_EOIMODE_BIT: usize = 0x1 << 1; +pub const GICC_IGRPEN_EL1_ENB_BIT: usize = 0x1; +pub const GICC_SGIR_AFF1_OFFSET: usize = 16; +pub const GICC_SGIR_SGIINTID_OFF: usize = 24; +pub const GICC_SGIR_SGIINTID_LEN: usize = 4; +pub const GICC_IAR_ID_OFF: usize = 0; +pub const GICC_IAR_ID_LEN: usize = 24; +pub const GICC_SGIR_IRM_BIT: usize = 1 << 40; +pub const GICC_SRE_EL2_ENABLE: usize = 1 << 3; + +// GICH BITS +pub const GICH_LR_VID_OFF: usize = 0; +pub const GICH_LR_VID_LEN: usize = 32; +pub const GICH_LR_VID_MASK: usize = (((1 << ((GICH_LR_VID_LEN) - 1)) << 1) - 1) << (GICH_LR_VID_OFF); +pub const GICH_LR_PID_OFF: usize = 32; +pub const GICH_LR_PID_LEN: usize = 10; +pub const GICH_LR_PID_MSK: usize = (((1 << ((GICH_LR_PID_LEN) - 1)) << 1) - 1) << (GICH_LR_PID_OFF); +pub const GICH_LR_PRIO_OFF: usize = 48; +pub const GICH_LR_PRIO_LEN: usize = 8; +pub const GICH_LR_STATE_LEN: usize = 2; +pub const GICH_LR_STATE_OFF: usize = 62; +pub const GICH_LR_STATE_MSK: usize = (((1 << ((GICH_LR_STATE_LEN) - 1)) << 1) - 1) << (GICH_LR_STATE_OFF); +pub const GICH_LR_STATE_ACT: usize = (2 << GICH_LR_STATE_OFF) & GICH_LR_STATE_MSK; +pub const GICH_LR_STATE_PND: usize = (1 << GICH_LR_STATE_OFF) & GICH_LR_STATE_MSK; +pub const GICH_LR_GRP_BIT: usize = 1 << 60; +pub const GICH_LR_PRIO_MSK: usize = (((1 << ((GICH_LR_PRIO_LEN) - 1)) << 1) - 1) << (GICH_LR_PRIO_OFF); +pub const GICH_LR_HW_BIT: usize = 1 << 61; +pub const GICH_LR_EOI_BIT: usize = 1 << 41; +/* End Of Interrupt. + * This maintenance interrupt is asserted when at least one bit in GICH_EISR == 1. + */ +pub const GICH_HCR_LRENPIE_BIT: usize = 1 << 2; +pub const GICH_HCR_EN_BIT: usize = 1; +pub const GICH_HCR_UIE_BIT: usize = 1 << 1; +pub const GICH_HCR_NPIE_BIT: usize = 1 << 3; +/* Counts the number of EOIs received that do not have a corresponding entry in the List registers. + * The virtual CPU interface increments this field automatically when a matching EOI is received + */ +pub const GICH_HCR_EOIC_OFF: usize = 27; +pub const GICH_HCR_EOIC_LEN: usize = 5; +pub const GICH_HCR_EOIC_MSK: usize = (((1 << ((GICH_HCR_EOIC_LEN) - 1)) << 1) - 1) << (GICH_HCR_EOIC_OFF); +pub const GICH_MISR_U: usize = 1 << 1; +pub const GICH_MISR_EOI: usize = 1; +/* No Pending. + * This maintenance interrupt is asserted + * when GICH_HCR.NPIE == 1 and no List register is in the pending state. + */ +pub const GICH_MISR_NP: usize = 1 << 3; +/* List Register Entry Not Present. + * This maintenance interrupt is asserted + * when GICH_HCR.LRENPIE == 1 and GICH_HCR.EOICount is nonzero. + */ +pub const GICH_MISR_LRPEN: usize = 1 << 2; +const GICH_NUM_ELRSR: usize = 1; +const GICH_VTR_MSK: usize = 0b11111; +const GICH_VTR_PRIBITS_OFF: usize = 29; +const GICH_VTR_PRIBITS_LEN: usize = 3; +const GICH_PMR_MASK: usize = 0xff; +const GICH_VMCR_VPMR_SHIFT: usize = 24; +const GICC_IGRPEN1_EN: usize = 0x1; +const GICH_VMCR_VENG1: usize = 0x1 << 1; +const GICH_VMCR_VEOIM: usize = 0x1 << 9; + +pub const GIC_SGIS_NUM: usize = 16; +const GIC_PPIS_NUM: usize = 16; +pub const GIC_INTS_MAX: usize = IntCtrl::NUM_MAX; +pub const GIC_PRIVINT_NUM: usize = GIC_SGIS_NUM + GIC_PPIS_NUM; +pub const GIC_SPI_MAX: usize = IntCtrl::NUM_MAX - GIC_PRIVINT_NUM; +pub const GIC_PRIO_BITS: usize = 8; +pub const GIC_TARGET_BITS: usize = 8; +pub const GIC_TARGETS_MAX: usize = GIC_TARGET_BITS; +pub const GIC_CONFIG_BITS: usize = 2; + +const GIC_INT_REGS_NUM: usize = GIC_INTS_MAX / 32; +const GIC_PRIO_REGS_NUM: usize = GIC_INTS_MAX * 8 / 32; +const GIC_TARGET_REGS_NUM: usize = GIC_INTS_MAX * 8 / 32; +const GIC_CONFIG_REGS_NUM: usize = GIC_INTS_MAX * 2 / 32; +const GIC_SEC_REGS_NUM: usize = GIC_INTS_MAX * 2 / 32; +pub const GIC_SGI_REGS_NUM: usize = GIC_SGIS_NUM * 8 / 32; +const GIC_INT_RT_NUM: usize = 1019 - 32 + 1; + +pub const GIC_LIST_REGS_NUM: usize = 64; + +pub const GICD_TYPER_CPUNUM_OFF: usize = 5; +pub const GICD_TYPER_CPUNUM_LEN: usize = 3; +pub const GICD_TYPER_CPUNUM_MSK: usize = ((1 << GICD_TYPER_CPUNUM_LEN) - 1) << (GICD_TYPER_CPUNUM_OFF); +const GICD_TYPER_ITLINESNUM_LEN: usize = 0b11111; +pub const ICC_CTLR_EOIMODE_BIT: usize = 0x1 << 1; + +pub static GIC_LRS_NUM: AtomicUsize = AtomicUsize::new(0); + +static GICD_LOCK: Mutex<()> = Mutex::new(()); +static GICR_LOCK: Mutex<()> = Mutex::new(()); + +pub static INTERRUPT_EN_SET: Mutex> = Mutex::new(BTreeSet::new()); + +pub fn add_en_interrupt(id: usize) { + if id < GIC_PRIVINT_NUM { + return; + } + let mut set = INTERRUPT_EN_SET.lock(); + set.insert(id); +} + +pub fn show_en_interrupt() { + let set = INTERRUPT_EN_SET.lock(); + print!("en irq set: "); + for irq in set.iter() { + print!("{} ", irq); + } + print!("\n"); +} + +pub fn gic_prio_reg(int_id: usize) -> usize { + (int_id * GIC_PRIO_BITS) / 32 +} + +pub fn gic_prio_off(int_id: usize) -> usize { + (int_id * GIC_PRIO_BITS) % 32 +} + +#[derive(Copy, Clone, Debug)] +pub enum IrqState { + IrqSInactive, + IrqSPend, + IrqSActive, + IrqSPendActive, +} + +impl IrqState { + pub fn num_to_state(num: usize) -> IrqState { + match num { + 0 => IrqState::IrqSInactive, + 1 => IrqState::IrqSPend, + 2 => IrqState::IrqSActive, + 3 => IrqState::IrqSPendActive, + _ => panic!("num_to_state: illegal irq state"), + } + } + + pub fn to_num(&self) -> usize { + match self { + IrqState::IrqSInactive => 0, + IrqState::IrqSPend => 1, + IrqState::IrqSActive => 2, + IrqState::IrqSPendActive => 3, + } + } +} + +/// Gic Descriptor struct +pub struct GicDesc { + pub gicd_addr: usize, + pub gicc_addr: usize, + pub gich_addr: usize, + pub gicv_addr: usize, + pub gicr_addr: usize, + pub maintenance_int_id: usize, +} + +register_structs! { + #[allow(non_snake_case)] + pub GicDistributor { + (0x0000 => CTLR: ReadWrite), //Distributor Control Register + (0x0004 => TYPER: ReadOnly), //Interrupt Controller Type Register + (0x0008 => IIDR: ReadOnly), //Distributor Implementer Identification Register + (0x000c => TYPER2: ReadOnly), //Interrupt controller Type Register 2 + (0x0010 => STATUSR: ReadWrite), //Error Reporting Status Register, optional + (0x0014 => reserved0), + (0x0040 => SETSPI_NSR: WriteOnly), //Set SPI Register + (0x0044 => reserved1), + (0x0048 => CLRSPI_NSR: WriteOnly), //Clear SPI Register + (0x004c => reserved2), + (0x0050 => SETSPI_SR: WriteOnly), //Set SPI, Secure Register + (0x0054 => reserved3), + (0x0058 => CLRSPI_SR: WriteOnly), //Clear SPI, Secure Register + (0x005c => reserved4), + (0x0080 => IGROUPR: [ReadWrite; GIC_INT_REGS_NUM]), //Interrupt Group Registers + (0x0100 => ISENABLER: [ReadWrite; GIC_INT_REGS_NUM]), //Interrupt Set-Enable Registers + (0x0180 => ICENABLER: [ReadWrite; GIC_INT_REGS_NUM]), //Interrupt Clear-Enable Registers + (0x0200 => ISPENDR: [ReadWrite; GIC_INT_REGS_NUM]), //Interrupt Set-Pending Registers + (0x0280 => ICPENDR: [ReadWrite; GIC_INT_REGS_NUM]), //Interrupt Clear-Pending Registers + (0x0300 => ISACTIVER: [ReadWrite; GIC_INT_REGS_NUM]), //Interrupt Set-Active Registers + (0x0380 => ICACTIVER: [ReadWrite; GIC_INT_REGS_NUM]), //Interrupt Clear-Active Registers + (0x0400 => IPRIORITYR: [ReadWrite; GIC_PRIO_REGS_NUM]), //Interrupt Priority Registers + (0x0800 => ITARGETSR: [ReadWrite; GIC_TARGET_REGS_NUM]), //Interrupt Processor Targets Registers + (0x0c00 => ICFGR: [ReadWrite; GIC_CONFIG_REGS_NUM]), //Interrupt Configuration Registers + (0x0d00 => IGRPMODR: [ReadWrite; GIC_CONFIG_REGS_NUM]), //Interrupt Group Modifier Registers + (0x0e00 => NSACR: [ReadWrite; GIC_SEC_REGS_NUM]), //Non-secure Access Control Registers + (0x0f00 => SGIR: WriteOnly), //Software Generated Interrupt Register + (0x0f04 => reserved6), + (0x0f10 => CPENDSGIR: [ReadWrite; GIC_SGI_REGS_NUM]), //SGI Clear-Pending Registers + (0x0f20 => SPENDSGIR: [ReadWrite; GIC_SGI_REGS_NUM]), //SGI Set-Pending Registers + (0x0f30 => reserved7), + (0x6000 => IROUTER: [ReadWrite; (0x8000 - 0x6000) / size_of::()]), //Interrupt Routing Registers for extended SPI range + (0x8000 => reserved21), + (0xffd0 => ID: [ReadOnly; (0x10000 - 0xffd0) / size_of::()]), //Reserved for ID registers + (0x10000 => @END), + } +} + +impl core::fmt::Debug for GicDistributor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("GicDistributorBlock") + .field("CTLR", &format_args!("{:x}", self.CTLR.get())) + .field("TYPER", &format_args!("{:x}", self.TYPER.get())) + .field("IIDR", &format_args!("{:x}", self.IIDR.get())) + .field("TYPER2", &format_args!("{:x}", self.TYPER2.get())) + .field("STATUSR", &format_args!("{:x}", self.STATUSR.get())) + .finish() + } +} + +unsafe impl Sync for GicDistributor {} + +impl GicDistributor { + pub fn is_enabler(&self, idx: usize) -> u32 { + self.ISENABLER[idx].get() + } + + pub fn is_activer(&self, idx: usize) -> u32 { + self.ISACTIVER[idx].get() + } + + pub fn is_pender(&self, idx: usize) -> u32 { + self.ISPENDR[idx].get() + } + + pub fn cpendsgir(&self, idx: usize) -> u32 { + self.CPENDSGIR[idx].get() + } + + pub fn igroup(&self, idx: usize) -> u32 { + self.IGROUPR[idx].get() + } + + pub fn ipriorityr(&self, idx: usize) -> u32 { + self.IPRIORITYR[idx].get() + } + + pub fn itargetsr(&self, idx: usize) -> u32 { + self.ITARGETSR[idx].get() + } + + pub fn ctlr(&self) -> u32 { + self.CTLR.get() + } + + pub fn id(&self, idx: usize) -> u32 { + self.ID[idx].get() + } + + pub fn icfgr(&self, idx: usize) -> u32 { + self.ICFGR[idx].get() + } + + pub fn ic_enabler(&self, idx: usize) -> u32 { + self.ICENABLER[idx].get() + } + + fn global_init(&self) { + let int_num = gic_max_spi(); + + for i in (GIC_PRIVINT_NUM / 32)..(int_num / 32) { + self.IGROUPR[i].set(u32::MAX); + self.ICENABLER[i].set(u32::MAX); + self.ICPENDR[i].set(u32::MAX); + self.ICACTIVER[i].set(u32::MAX); + } + + for i in (GIC_PRIVINT_NUM * 8 / 32)..(int_num * 8 / 32) { + self.IPRIORITYR[i].set(u32::MAX); + } + + for i in GIC_PRIVINT_NUM..GIC_INTS_MAX { + self.IROUTER[i].set(GICD_IROUTER_INV as u64); + } + + let prev = self.CTLR.get(); + + self.CTLR + .set(prev | GICD_CTLR_ARE_NS_BIT as u32 | GICD_CTLR_ENNS_BIT as u32); + } + + /// send software generated interrupt to a cpu + pub fn send_sgi(&self, cpu_target: usize, sgi_num: usize) { + if sgi_num < GIC_SGIS_NUM { + let mpidr = Platform::cpuid_to_cpuif(cpu_target) & MPIDR_AFF_MSK; + /* We only support two affinity levels */ + let sgi = ((((mpidr) >> 8) & 0xff) << GICC_SGIR_AFF1_OFFSET) //aff1 + | (1 << (mpidr & 0xff)) //aff0 + | ((sgi_num) << GICC_SGIR_SGIINTID_OFF); + // SAFETY: + // AFF1 [23:16] + // TargetList [15:0] + // INTID [27:24] + unsafe { + ICC_SGI1R_EL1::write(sgi as u64); + } + } + } + + /// get priority of `int_id` + pub fn prio(&self, int_id: usize) -> usize { + let idx = (int_id * GIC_PRIO_BITS) / 32; + let off = (int_id * GIC_PRIO_BITS) % 32; + ((self.IPRIORITYR[idx].get() >> off) & 0xff) as usize + } + + /// set priority of `int_id` to `prio` + pub fn set_prio(&self, int_id: usize, prio: u8) { + let idx = ((int_id) * GIC_PRIO_BITS) / 32; + let off = (int_id * GIC_PRIO_BITS) % 32; + let mask = ((1 << (GIC_PRIO_BITS)) - 1) << off; + + let lock = GICD_LOCK.lock(); + + let prev: u32 = self.IPRIORITYR[idx].get(); + let value = (prev & !(mask as u32)) | (((prio as u32) << off) & mask as u32); + self.IPRIORITYR[idx].set(value); + + drop(lock); + } + + pub fn trgt(&self, int_id: usize) -> usize { + let idx = (int_id * 8) / 32; + let off = (int_id * 8) % 32; + ((self.ITARGETSR[idx].get() >> off) & 0xff) as usize + } + + pub fn set_enable(&self, int_id: usize, en: bool) { + let reg_id = int_id / 32; + let mask = 1 << (int_id % 32); + + let lock = GICD_LOCK.lock(); + + if en { + add_en_interrupt(int_id); + self.ISENABLER[reg_id].set(mask); + } else { + self.ICENABLER[reg_id].set(mask); + } + + drop(lock); + } + + pub fn get_pend(&self, int_id: usize) -> bool { + let reg_id = int_id / 32; + let mask = 1 << (int_id % 32); + (self.ISPENDR[reg_id].get() & (mask as u32)) != 0 + } + + pub fn get_act(&self, int_id: usize) -> bool { + let reg_id = int_id / 32; + let mask = 1 << (int_id % 32); + (self.ISACTIVER[reg_id].get() & (mask as u32)) != 0 + } + + pub fn set_pend(&self, int_id: usize, pend: bool) { + let lock = GICD_LOCK.lock(); + + let reg_ind = int_id / 32; + let mask = 1 << (int_id % 32); + if pend { + self.ISPENDR[reg_ind].set(mask); + } else { + self.ICPENDR[reg_ind].set(mask); + } + + drop(lock); + } + + pub fn set_act(&self, int_id: usize, act: bool) { + let reg_ind = int_id / 32; + let mask = 1 << (int_id % 32); + + let lock = GICD_LOCK.lock(); + if act { + self.ISACTIVER[reg_ind].set(mask); + } else { + self.ICACTIVER[reg_ind].set(mask); + } + drop(lock); + } + + pub fn set_icfgr(&self, int_id: usize, cfg: u8) { + let lock = GICD_LOCK.lock(); + let reg_ind = (int_id * GIC_CONFIG_BITS) / 32; + let off = (int_id * GIC_CONFIG_BITS) % 32; + let mask = 0b11 << off; + + let icfgr = self.ICFGR[reg_ind].get(); + self.ICFGR[reg_ind].set((icfgr & !mask) | (((cfg as u32) << off as u32) & mask)); + + drop(lock); + } + + pub fn typer(&self) -> u32 { + self.TYPER.get() + } + + pub fn iidr(&self) -> u32 { + self.IIDR.get() + } + + pub fn state(&self, int_id: usize) -> usize { + let reg_ind = int_id / 32; + let mask = 1 << (int_id % 32); + + let lock = GICD_LOCK.lock(); + let pend = if (self.ISPENDR[reg_ind].get() & mask) != 0 { + 1 + } else { + 0 + }; + let act = if (self.ISACTIVER[reg_ind].get() & mask) != 0 { + 2 + } else { + 0 + }; + drop(lock); + pend | act + } + + /// set route attribute of `int_id` + pub fn set_route(&self, int_id: usize, route: usize) { + if gic_is_priv(int_id) { + return; + } + + let lock = GICD_LOCK.lock(); + + self.IROUTER[int_id].set((route & GICD_IROUTER_AFF_MSK) as u64); + + drop(lock) + } +} + +register_structs! { + #[allow(non_snake_case)] + pub GicRedistributor { + (0x0000 => CTLR: ReadWrite), // Redistributor Control Register + (0x0004 => IIDR: ReadOnly), // Implementer Identification Register + (0x0008 => TYPER: ReadOnly), // Redistributor Type Register + (0x0010 => STATUSR: ReadWrite), // Error Reporting Status Register, optional + (0x0014 => WAKER: ReadWrite), // Redistributor Wake Register + (0x0018 => MPAMIDR: ReadOnly), // Report maximum PARTID and PMG Register + (0x001c => PARTIDR: ReadWrite), // Set PARTID and PMG Register + (0x0020 => reserved18), + (0x0040 => SETLPIR: WriteOnly), // Set LPI Pending Register + (0x0048 => CLRLPIR: WriteOnly), // Clear LPI Pending Register + (0x0050 => reserved17), + (0x0070 => PROPBASER: ReadWrite), //Redistributor Properties Base Address Register + (0x0078 => PEDNBASER: ReadWrite), //Redistributor LPI Pending Table Base Address Register + (0x0080 => reserved16), + (0x00a0 => INVLPIR: WriteOnly), // Redistributor Invalidate LPI Register + (0x00a8 => reserved15), + (0x00b0 => INVALLR: WriteOnly), // Redistributor Invalidate All Register + (0x00b8 => reserved14), + (0x00c0 => SYNCR: ReadOnly), // Redistributor Synchronize Register + (0x00c8 => reserved13), + (0xffd0 => ID: [ReadOnly; (0x10000 - 0xFFD0) / size_of::()]), + (0x10000 => reserved12), + (0x10080 => IGROUPR0: ReadWrite), //SGI_base frame, all below + (0x10084 => reserved11), + (0x10100 => ISENABLER0: ReadWrite), + (0x10104 => reserved10), + (0x10180 => ICENABLER0: ReadWrite), + (0x10184 => reserved9), + (0x10200 => ISPENDR0: ReadWrite), + (0x10204 => reserved8), + (0x10280 => ICPENDR0: ReadWrite), + (0x10284 => reserved7), + (0x10300 => ISACTIVER0: ReadWrite), + (0x10304 => reserved6), + (0x10380 => ICACTIVER0: ReadWrite), + (0x10384 => reserved5), + (0x10400 => IPRIORITYR: [ReadWrite;8]), + (0x10420 => reserved4), + (0x10c00 => ICFGR0: ReadWrite), + (0x10c04 => ICFGR1: ReadWrite), + (0x10c08 => reserved3), + (0x10d00 => IGRPMODR0: ReadWrite), + (0x10d04 => reserved2), + (0x10e00 => NSACR: ReadWrite), + (0x10e04 => reserved1), + (0x20000 => @END), + } +} + +impl core::fmt::Debug for GicRedistributor { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("GicRedistributorBlock") + .field("Current_cpu", ¤t_cpu().id) + .field("CTLR", &format_args!("{:#x}", self.CTLR.get())) + .field("IIDR", &format_args!("{:#x}", self.IIDR.get())) + .field("TYPER", &format_args!("{:#x}", self.TYPER.get())) + .field("STATUSR", &format_args!("{:#x}", self.STATUSR.get())) + .field("WAKER", &format_args!("{:#x}", self.WAKER.get())) + .field("MPAMIDR", &format_args!("{:#x}", self.MPAMIDR.get())) + .field("PARTIDR", &format_args!("{:#x}", self.PARTIDR.get())) + .field("PROPBASER", &format_args!("{:#x}", self.PROPBASER.get())) + .field("PEDNBASER", &format_args!("{:#x}", self.PEDNBASER.get())) + .field("SYNCR", &format_args!("{:#x}", self.SYNCR.get())) + .field("IGROUPR0", &format_args!("{:#x}", self.IGROUPR0.get())) + .field("ISENABLER0", &format_args!("{:#x}", self.ISENABLER0.get())) + .field("ICENABLER0", &format_args!("{:#x}", self.ICENABLER0.get())) + .field("ISPENDR0", &format_args!("{:#x}", self.ISPENDR0.get())) + .field("ICPENDR0", &format_args!("{:#x}", self.ICPENDR0.get())) + .field("ISACTIVER0", &format_args!("{:#x}", self.ISACTIVER0.get())) + .field("ICACTIVER0", &format_args!("{:#x}", self.ICACTIVER0.get())) + .field("ICFGR0", &format_args!("{:#x}", self.ICFGR0.get())) + .field("ICFGR1", &format_args!("{:#x}", self.ICFGR1.get())) + .field("IGRPMODR0", &self.IGRPMODR0.get()) + .field("NSACR", &format_args!("{:#x}", self.NSACR.get())) + .finish() + } +} + +unsafe impl Sync for GicRedistributor {} + +impl core::ops::Index for GicRedistributor { + type Output = GicRedistributor; + + fn index(&self, index: usize) -> &Self::Output { + if index >= crate::board::PLAT_DESC.cpu_desc.num { + panic!("GicRedistributor index out of range"); + } + // SAFRTY: GicRedistributor has constructed with correct address,and we have checked the index + unsafe { &*(self as *const GicRedistributor).add(index) } + } +} + +impl GicRedistributor { + fn init(&self) { + let waker = self[current_cpu().id].WAKER.get(); + self[current_cpu().id].WAKER.set(waker & !GICR_WAKER_PSLEEP_BIT as u32); + while (self[current_cpu().id].WAKER.get() & GICR_WAKER_CASLEEP_BIT as u32) != 0 {} + + self[current_cpu().id].IGROUPR0.set(u32::MAX); + self[current_cpu().id].ICENABLER0.set(u32::MAX); + self[current_cpu().id].ICPENDR0.set(u32::MAX); + self[current_cpu().id].ICACTIVER0.set(u32::MAX); + + for i in 0..gic_prio_reg(GIC_PRIVINT_NUM) { + self[current_cpu().id].IPRIORITYR[i].set(u32::MAX); + } + } + + pub fn set_prio(&self, int_id: usize, prio: u8, gicr_id: u32) { + let reg_id = gic_prio_reg(int_id); + let off = gic_prio_off(int_id); + let mask = (((1 << ((GIC_PRIO_BITS) - 1)) << 1) - 1) << (off); + let lock = GICR_LOCK.lock(); //lock is for per core + + self[gicr_id as usize].IPRIORITYR[reg_id].set( + (self[gicr_id as usize].IPRIORITYR[reg_id].get() & !mask as u32) | (((prio as usize) << off) & mask) as u32, + ); + + drop(lock); + } + + pub fn get_prio(&self, int_id: usize, gicr_id: u32) -> usize { + let reg_id = gic_prio_reg(int_id); + let off = gic_prio_off(int_id); + let mask = (((1 << ((GIC_PRIO_BITS) - 1)) << 1) - 1) << (off); + let lock = GICR_LOCK.lock(); + + let prio = (self[gicr_id as usize].IPRIORITYR[reg_id].get() as usize) >> off & mask; + + drop(lock); + prio + } + + pub fn set_icfgr(&self, int_id: usize, cfg: u8, gicr_id: u32) { + let reg_id = (int_id * GIC_CONFIG_BITS) / u32::BITS as usize; + let off = (int_id * GIC_CONFIG_BITS) % u32::BITS as usize; + let mask = ((1 << (GIC_CONFIG_BITS)) - 1) << (off); + + let lock = GICR_LOCK.lock(); + + match reg_id { + 0 => { + self[gicr_id as usize].ICFGR0.set( + ((self[gicr_id as usize].ICFGR0.get() as usize & !mask) | (((cfg as usize) << off) & mask)) as u32, + ); + } + _ => { + self[gicr_id as usize].ICFGR1.set( + ((self[gicr_id as usize].ICFGR1.get() as usize & !mask) | (((cfg as usize) << off) & mask)) as u32, + ); + } + } + + drop(lock); + } + + pub fn set_pend(&self, int_id: usize, pend: bool, gicr_id: u32) { + let lock = GICR_LOCK.lock(); + + if pend { + self[gicr_id as usize].ISPENDR0.set((1 << (int_id % 32)) as u32); + } else { + self[gicr_id as usize].ICPENDR0.set((1 << (int_id % 32)) as u32); + } + + drop(lock); + } + + pub fn get_pend(&self, int_id: usize, gicr_id: u32) -> bool { + let mask = 1 << (int_id % 32); + if gic_is_priv(int_id) { + (self[gicr_id as usize].ISPENDR0.get() as usize & mask) != 0 + } else { + false + } + } + + pub fn set_act(&self, int_id: usize, act: bool, gicr_id: u32) { + let mask = 1 << (int_id % 32); + + let lock = GICR_LOCK.lock(); + + if act { + self[gicr_id as usize].ISACTIVER0.set(mask as u32); + } else { + self[gicr_id as usize].ICACTIVER0.set(mask as u32); + } + + drop(lock); + } + + pub fn get_act(&self, int_id: usize, gicr_id: u32) -> bool { + let mask = 1 << (int_id % 32); + if gic_is_priv(int_id) { + (self[gicr_id as usize].ISACTIVER0.get() as usize & mask) != 0 + } else { + false + } + } + + pub fn set_enable(&self, int_id: usize, en: bool, gicr_id: u32) { + let mask = 1 << (int_id % 32); + + let lock = GICR_LOCK.lock(); + + if en { + add_en_interrupt(int_id); + self[gicr_id as usize].ISENABLER0.set(mask); + } else { + self[gicr_id as usize].ICENABLER0.set(mask); + } + + drop(lock); + } + + pub fn get_enable(&self, _int_id: usize, gicr_id: u32) -> u32 { + self[gicr_id as usize].ISENABLER0.get() + } + + pub fn get_typer(&self, gicr_id: usize) -> u64 { + self[gicr_id].TYPER.get() + } + + pub fn get_propbaser(&self, gicr_id: usize) -> u64 { + self[gicr_id].PROPBASER.get() + } + + pub fn set_propbaser(&self, gicr_id: usize, val: usize) { + self[gicr_id].PROPBASER.set(val as u64); + } + + pub fn get_pendbaser(&self, gicr_id: usize) -> u64 { + self[gicr_id].PEDNBASER.get() + } + + pub fn set_pendbaser(&self, gicr_id: usize, val: usize) { + self[gicr_id].PEDNBASER.set(val as u64); + } + + pub fn set_ctrlr(&self, gicr_id: usize, val: usize) { + self[gicr_id].CTLR.set(val as u32); + } + + pub fn get_iidr(&self, gicr_id: usize) -> u32 { + self[gicr_id].IIDR.get() + } + + pub fn get_id(&self, gicr_id: u32, index: usize) -> u32 { + self[gicr_id as usize].ID[index].get() + } + + pub fn get_ctrl(&self, gicr_id: u32) -> u32 { + self[gicr_id as usize].CTLR.get() + } + + pub fn is_enabler(&self, gicr_id: u32) -> u32 { + self[gicr_id as usize].ISENABLER0.get() + } + + pub fn get_igroup(&self, gicr_id: u32) -> u32 { + self[gicr_id as usize].IGROUPR0.get() + } + + pub fn priority(&self, gicr_id: usize, index: usize) -> u32 { + self[gicr_id].IPRIORITYR[index].get() + } +} +pub struct GicCpuInterface; + +impl core::fmt::Display for GicCpuInterface { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + writeln!(f, "ICC_SRE_EL2:{:016x}", ICC_SRE_EL2::read())?; + writeln!(f, "ICC_PMR_EL1:{:016x}", ICC_PMR_EL1::read())?; + writeln!(f, "ICC_BPR1_EL1:{:016x}", ICC_BPR1_EL1::read())?; + writeln!(f, "ICC_CTLR_EL1:{:016x}", ICC_CTLR_EL1::read())?; + writeln!(f, "ICH_HCR_EL2:{:016x}", ICH_HCR_EL2::read())?; + writeln!(f, "ICC_IGRPEN1_EL1:{:016x}", ICC_IGRPEN1_EL1::read())?; + Ok(()) + } +} + +impl GicCpuInterface { + fn init(&self) { + // SAFETY: Set the SRE[0] bit to 1 to enable Group 1 interrupts. + unsafe { + ICC_SRE_EL2::write(0b1); + } + + isb(); + + for i in 0..gich_lrs_num() { + GICH.set_lr(i, 0); + } + + let pmr = ICC_PMR_EL1::read(); + // SAFETY: Set the priority mask[7:0] to 0xff means min prio. + unsafe { + ICC_PMR_EL1::write(0xff); + } + // SAFETY: Set the binary point[2:0] to 0x0 + unsafe { + ICC_BPR1_EL1::write(0x0); + } + // SAFETY: Set the EOImode[1] to 1 + unsafe { + ICC_CTLR_EL1::write(ICC_CTLR_EOIMODE_BIT); + } + let hcr = ICH_HCR_EL2::read(); + // SAFETY: Change the LRENPIE[2] to 1 to enable List Register Entry Not Present Interrupt + unsafe { + ICH_HCR_EL2::write(hcr | GICH_HCR_LRENPIE_BIT); + } + // SAFETY: Set the EnableGrp1[0] to 1 to enable Group 1 interrupts. + unsafe { + ICC_IGRPEN1_EL1::write(GICC_IGRPEN_EL1_ENB_BIT); + } + + //Set ICH_VMCR_EL2:Interrupt Controller Virtual Machine Control Register Enables the hypervisor to save and restore the virtual machine view of the GIC state. + let mut ich_vmcr = (pmr & GICH_PMR_MASK) << GICH_VMCR_VPMR_SHIFT; + ich_vmcr |= GICH_VMCR_VENG1 | GICH_VMCR_VEOIM; + // SAFETY: + // Set the VPMR[31:24] to the saved value pmr. + // Set the VENG1[1] to 1 to enable Group 1 virtual interrupts. + // Set the VEOIM[9] to 1 to enable the virtual End of Interrupt Register. + unsafe { + ICH_VMCR_EL2::write(ich_vmcr); + } + } + + pub fn iar(&self) -> u32 { + ICC_IAR1_EL1::read() as u32 + } + + pub fn set_eoir(&self, eoir: u32) { + // SAFETY: Any INTID[23:0] can't trigger side effects. + unsafe { + ICC_EOIR1_EL1::write(eoir as usize); + } + } + + pub fn set_dir(&self, dir: u32) { + // SAFETY: Any INTID[23:0] can't trigger side effects. + unsafe { + ICC_DIR_EL1::write(dir as usize); + } + } +} + +pub struct GicHypervisorInterface; + +impl GicHypervisorInterface { + pub fn hcr(&self) -> usize { + ICH_HCR_EL2::read() + } + + pub fn set_hcr(&self, hcr: usize) { + // SAFETY: Any value can't trigger side effects. + unsafe { + ICH_HCR_EL2::write(hcr); + } + } + + // These registers can be used to locate a usable List register when the hypervisor is delivering an interrupt to a Guest OS. + pub fn elrsr(&self) -> usize { + ICH_ELRSR_EL2::read() + } + + pub fn eisr(&self) -> u32 { + ICH_EISR_EL2::read() as u32 + } + + pub fn lr(&self, lr_idx: usize) -> usize { + match lr_idx { + 0 => ICH_LR0_EL2::read(), + 1 => ICH_LR1_EL2::read(), + 2 => ICH_LR2_EL2::read(), + 3 => ICH_LR3_EL2::read(), + 4 => ICH_LR4_EL2::read(), + 5 => ICH_LR5_EL2::read(), + 6 => ICH_LR6_EL2::read(), + 7 => ICH_LR7_EL2::read(), + 8 => ICH_LR8_EL2::read(), + 9 => ICH_LR9_EL2::read(), + 10 => ICH_LR10_EL2::read(), + 11 => ICH_LR11_EL2::read(), + 12 => ICH_LR12_EL2::read(), + 13 => ICH_LR13_EL2::read(), + 14 => ICH_LR14_EL2::read(), + 15 => ICH_LR15_EL2::read(), + _ => panic!("gic: trying to read inexistent list register"), + } + } + + // Indicates which maintenance interrupts are asserted. + pub fn misr(&self) -> u32 { + ICH_MISR_EL2::read() as u32 + } + + pub fn set_lr(&self, lr_idx: usize, val: usize) { + // SAFETY: Any value can't trap undefined exception. + unsafe { + match lr_idx { + 0 => ICH_LR0_EL2::write(val), + 1 => ICH_LR1_EL2::write(val), + 2 => ICH_LR2_EL2::write(val), + 3 => ICH_LR3_EL2::write(val), + 4 => ICH_LR4_EL2::write(val), + 5 => ICH_LR5_EL2::write(val), + 6 => ICH_LR6_EL2::write(val), + 7 => ICH_LR7_EL2::write(val), + 8 => ICH_LR8_EL2::write(val), + 9 => ICH_LR9_EL2::write(val), + 10 => ICH_LR10_EL2::write(val), + 11 => ICH_LR11_EL2::write(val), + 12 => ICH_LR12_EL2::write(val), + 13 => ICH_LR13_EL2::write(val), + 14 => ICH_LR14_EL2::write(val), + 15 => ICH_LR15_EL2::write(val), + _ => panic!("gic: trying to write inexistent list register"), + } + } + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +/// GIC state struct +pub struct GicState { + pub ctlr: u32, + pub pmr: u32, + pub bpr: u32, + pub eoir: u32, + pub rpr: u32, + pub hppir: u32, + pub priv_isenabler: u32, + pub priv_ipriorityr: [u32; GIC_PRIVINT_NUM / 4], + pub hcr: usize, + pub lr: [usize; GIC_LIST_REGS_NUM], + pub apr0: [u32; 4], + pub apr1: [u32; 4], + igrpen1: usize, + vmcr: u32, + nr_prio: u32, //Priority bits. The number of virtual priority bits implemented, minus one. + sre_el1: u32, +} + +impl Default for GicState { + fn default() -> Self { + let nr_prio = (((ICH_VTR_EL2::read() >> GICH_VTR_PRIBITS_OFF) & ((1 << GICH_VTR_PRIBITS_LEN) - 1)) + 1) as u32; + + GicState { + ctlr: GICC_CTLR_EOIMODE_BIT as u32, + igrpen1: GICC_IGRPEN_EL1_ENB_BIT, + pmr: 0xff, + bpr: 0, + eoir: 0, + rpr: 0, + hppir: 0, + priv_isenabler: GICR[current_cpu().id].ISENABLER0.get(), + priv_ipriorityr: [u32::MAX; GIC_PRIVINT_NUM / 4], + hcr: 0b101, + lr: [0; GIC_LIST_REGS_NUM], + vmcr: 0, + nr_prio, + apr0: [0; 4], + apr1: [0; 4], + sre_el1: 0, + } + } +} + +impl crate::arch::InterruptContextTrait for GicState { + fn save_state(&mut self) { + self.hcr = ICH_HCR_EL2::read(); + // save VMCR_EL2: save and restore the virtual machine view of the GIC state. + self.vmcr = ICH_VMCR_EL2::read() as u32; + // save ICH_AP1Rn_EL2: Provides information about Group 1 virtual active priorities for EL2. + // if some bit set 1:There is a Group 1 interrupt active with this priority level which has not undergone priority drop. + self.save_aprn_regs(); + // save lr + for i in 0..gich_lrs_num() { + self.lr[i] = GICH.lr(i); + } + // save ICC_SRE_EL1: EL1`s systregister use + self.sre_el1 = ICC_SRE_EL1::read() as u32; + // SAFETY: change the value of ICC_SRE_EL2 without GICC_SRE_EL2_ENABLE_bit + unsafe { ICC_SRE_EL2::write(ICC_SRE_EL2::read() & !GICC_SRE_EL2_ENABLE) } + } + + fn restore_state(&self) { + // make EL2 can use sysrem register + // SAFETY: + // Set Enable[3] bit to 1, and set the SRE[0] bits to 1 + // And other bits set to 0 + unsafe { + ICC_SRE_EL2::write(0b1001); + } + // restore ICC_SRE_EL1 for EL1 + // SAFETY: + // Set the SRE[0] bits to 1 + // And other bits set to 0 + unsafe { + ICC_SRE_EL1::write(0x1); + } + isb(); + // SAFETY: The value is saved last time + unsafe { + // restore HCR + ICH_HCR_EL2::write(self.hcr); + // restore ICH_VMCR_EL2 + ICH_VMCR_EL2::write(self.vmcr as usize); + } + // restore aprn + self.restore_aprn_regs(); + // restore lr + for i in 0..gich_lrs_num() { + GICH.set_lr(i, self.lr[i]); + } + } +} + +impl GicState { + fn save_apr2(&mut self) { + self.apr0[2] = ICH_AP0R2_EL2::read() as u32; + self.apr1[2] = ICH_AP1R2_EL2::read() as u32; + } + + fn save_apr1(&mut self) { + self.apr0[1] = ICH_AP0R1_EL2::read() as u32; + self.apr1[1] = ICH_AP1R1_EL2::read() as u32; + } + + fn save_apr0(&mut self) { + self.apr0[0] = ICH_AP0R0_EL2::read() as u32; + self.apr1[0] = ICH_AP1R0_EL2::read() as u32; + } + + fn save_aprn_regs(&mut self) { + match self.nr_prio { + 7 => { + self.save_apr2(); + self.save_apr1(); + self.save_apr0(); + } + 6 => { + self.save_apr1(); + self.save_apr0(); + } + 5 => { + self.save_apr0(); + } + _ => panic!("priority not surpport"), + } + } + + fn restore_aprn_regs(&self) { + // SAFETY: All value is saved last time + let restore_apr2 = || unsafe { + ICH_AP0R2_EL2::write(self.apr0[2] as usize); + ICH_AP1R2_EL2::write(self.apr1[2] as usize); + }; + let restore_apr1 = || unsafe { + ICH_AP0R1_EL2::write(self.apr0[1] as usize); + ICH_AP1R1_EL2::write(self.apr1[1] as usize); + }; + let restore_apr0 = || unsafe { + ICH_AP0R0_EL2::write(self.apr0[0] as usize); + ICH_AP1R0_EL2::write(self.apr1[0] as usize); + }; + match self.nr_prio { + 7 => { + restore_apr2(); + restore_apr1(); + restore_apr0(); + } + 6 => { + restore_apr1(); + restore_apr0(); + } + 5 => { + restore_apr0(); + } + _ => panic!("priority not surpport"), + } + } +} + +// SAFETY: GICD & GICR are GICv3 mmio device regions +pub static GICD: DeviceRef = unsafe { DeviceRef::new(Platform::GICD_BASE as *const GicDistributor) }; +pub static GICC: GicCpuInterface = GicCpuInterface; +pub static GICH: GicHypervisorInterface = GicHypervisorInterface; +pub static GICR: DeviceRef = + unsafe { DeviceRef::new((Platform::GICR_BASE) as *const GicRedistributor) }; + +#[inline(always)] +pub fn gich_lrs_num() -> usize { + (ICH_VTR_EL2::read() & GICH_VTR_MSK) + 1 +} + +#[inline(always)] +pub fn gic_max_spi() -> usize { + let typer = GICD.TYPER.get(); + let value = typer & GICD_TYPER_ITLINESNUM_LEN as u32; + (32 * (value + 1)) as usize +} + +pub fn gic_glb_init() { + set_gic_lrs(gich_lrs_num()); + GICD.global_init(); +} + +pub fn gic_cpu_init() { + GICR.init(); + GICC.init(); +} + +pub fn gic_cpu_reset() { + GICC.init(); +} + +#[inline(always)] +pub fn gic_is_priv(int_id: usize) -> bool { + int_id < GIC_PRIVINT_NUM +} + +#[inline(always)] +pub fn gic_is_sgi(int_id: usize) -> bool { + int_id < GIC_SGIS_NUM +} + +pub fn gicc_clear_current_irq(for_hypervisor: bool) { + let irq = current_cpu().current_irq as u32; + if irq == 0 { + return; + } + GICC.set_eoir(irq); + if for_hypervisor { + GICC.set_dir(irq); + } + current_cpu().current_irq = 0; +} + +pub fn gicc_get_current_irq() -> Option { + let iar = GICC.iar(); + let irq = iar as usize; + current_cpu().current_irq = irq; + let id = bit_extract(iar as usize, GICC_IAR_ID_OFF, GICC_IAR_ID_LEN); + if id >= IntCtrl::NUM_MAX { + None + } else { + Some(id) + } +} + +pub fn gic_lrs() -> usize { + GIC_LRS_NUM.load(Ordering::Relaxed) +} + +pub fn set_gic_lrs(lrs: usize) { + GIC_LRS_NUM.store(lrs, Ordering::Relaxed); +} + +pub fn gic_set_icfgr(int_id: usize, cfg: u8) { + if !gic_is_priv(int_id) { + GICD.set_icfgr(int_id, cfg); + } else { + GICR.set_icfgr(int_id, cfg, current_cpu().id as u32); + } +} + +pub fn gic_set_state(int_id: usize, state: usize, gicr_id: u32) { + gic_set_act(int_id, (state & IrqState::IrqSActive as usize) != 0, gicr_id); + gic_set_pend(int_id, (state & IrqState::IrqSPend as usize) != 0, gicr_id); +} + +pub fn gic_set_act(int_id: usize, act: bool, gicr_id: u32) { + if !gic_is_priv(int_id) { + GICD.set_act(int_id, act); + } else { + GICR.set_act(int_id, act, gicr_id); + } +} + +pub fn gic_set_pend(int_id: usize, pend: bool, gicr_id: u32) { + if !gic_is_priv(int_id) { + GICD.set_pend(int_id, pend); + } else { + GICR.set_pend(int_id, pend, gicr_id); + } +} + +pub fn gic_get_pend(int_id: usize) -> bool { + if !gic_is_priv(int_id) { + GICD.get_pend(int_id) + } else { + GICR.get_pend(int_id, current_cpu().id as u32) + } +} + +pub fn gic_get_act(int_id: usize) -> bool { + if !gic_is_priv(int_id) { + GICD.get_act(int_id) + } else { + GICR.get_act(int_id, current_cpu().id as u32) + } +} + +pub fn gic_set_enable(int_id: usize, en: bool) { + if !gic_is_priv(int_id) { + GICD.set_enable(int_id, en); + } else { + GICR.set_enable(int_id, en, current_cpu().id as u32); + } +} + +pub fn gic_get_prio(int_id: usize) { + if !gic_is_priv(int_id) { + GICD.prio(int_id); + } else { + GICR.get_prio(int_id, current_cpu().id as u32); + } +} + +pub fn gic_set_prio(int_id: usize, prio: u8) { + if !gic_is_priv(int_id) { + GICD.set_prio(int_id, prio); + } else { + GICR.set_prio(int_id, prio, current_cpu().id as u32); + } +} diff --git a/src/arch/aarch64/interface.rs b/src/arch/aarch64/interface.rs index c2fbd074fe62905a970185e6641021b55cc47245..dc79962fa1d2c30faa683309c69e9c5ec6df0488 100644 --- a/src/arch/aarch64/interface.rs +++ b/src/arch/aarch64/interface.rs @@ -10,6 +10,8 @@ pub const PAGE_SIZE: usize = 4096; pub const PAGE_SHIFT: usize = 12; + +/// The number of page table entries in a page. pub const ENTRY_PER_PAGE: usize = PAGE_SIZE / 8; pub type ContextFrame = super::context_frame::Aarch64ContextFrame; diff --git a/src/arch/aarch64/interrupt.rs b/src/arch/aarch64/interrupt.rs index bb35cb89c610ce8fb86d70d79ca40efad8b56a1c..701995e94ffcaa299258db7f21c99f0920532dbc 100644 --- a/src/arch/aarch64/interrupt.rs +++ b/src/arch/aarch64/interrupt.rs @@ -8,73 +8,112 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::arch::{gic_cpu_reset, gicc_clear_current_irq}; +use crate::arch::traits::InterruptController; +use crate::arch::{gic_cpu_reset, GIC_PRIVINT_NUM, gicc_clear_current_irq}; +#[cfg(not(feature = "gicv3"))] use crate::board::{Platform, PlatOperation}; use crate::kernel::{current_cpu, Vcpu, Vm}; -use super::GIC_SGIS_NUM; -use super::GICD; +use super::{GIC_SGIS_NUM, GICD, gicc_get_current_irq}; -pub const INTERRUPT_IRQ_HYPERVISOR_TIMER: usize = 26; -pub const INTERRUPT_IRQ_IPI: usize = 1; +const INTERRUPT_NUM_MAX: usize = 1024; +const INTERRUPT_IRQ_HYPERVISOR_TIMER: usize = 26; +const INTERRUPT_IRQ_GUEST_TIMER: usize = 27; +const INTERRUPT_IRQ_IPI: usize = 1; -pub fn interrupt_arch_init() { - use crate::arch::{gic_cpu_init, gic_glb_init, gic_maintenance_handler}; - use crate::kernel::{interrupt_reserve_int, InterruptHandler}; +pub struct IntCtrl; - crate::lib::barrier(); +impl InterruptController for IntCtrl { + const NUM_MAX: usize = INTERRUPT_NUM_MAX; - if current_cpu().id == 0 { - gic_glb_init(); - } + const PRI_NUN_MAX: usize = GIC_PRIVINT_NUM; - gic_cpu_init(); + const IRQ_IPI: usize = INTERRUPT_IRQ_IPI; - use crate::board::PLAT_DESC; + const IRQ_HYPERVISOR_TIMER: usize = INTERRUPT_IRQ_HYPERVISOR_TIMER; - let int_id = PLAT_DESC.arch_desc.gic_desc.maintenance_int_id; - interrupt_reserve_int(int_id, InterruptHandler::GicMaintenanceHandler(gic_maintenance_handler)); - interrupt_arch_enable(int_id, true); -} + const IRQ_GUEST_TIMER: usize = INTERRUPT_IRQ_GUEST_TIMER; + + fn init() { + use crate::arch::{gic_cpu_init, gic_glb_init, gic_maintenance_handler}; + use crate::kernel::interrupt_reserve_int; + + #[cfg(not(feature = "secondary_start"))] + crate::utils::barrier(); + + if current_cpu().id == 0 { + gic_glb_init(); + } -pub fn interrupt_arch_enable(int_id: usize, en: bool) { - let cpu_id = current_cpu().id; - if en { - GICD.set_prio(int_id, 0x7f); - GICD.set_trgt(int_id, 1 << Platform::cpuid_to_cpuif(cpu_id)); + gic_cpu_init(); - GICD.set_enable(int_id, en); - } else { - GICD.set_enable(int_id, en); + use crate::board::PLAT_DESC; + + let int_id = PLAT_DESC.arch_desc.gic_desc.maintenance_int_id; + interrupt_reserve_int(int_id, gic_maintenance_handler); + Self::enable(int_id, true); } -} -pub fn interrupt_arch_ipi_send(cpu_id: usize, ipi_id: usize) { - if ipi_id < GIC_SGIS_NUM { - GICD.send_sgi(Platform::cpuid_to_cpuif(cpu_id), ipi_id); + fn enable(int_id: usize, en: bool) { + #[cfg(feature = "gicv3")] + { + use tock_registers::interfaces::Readable; + use crate::arch::{gic_set_enable, gic_set_prio}; + gic_set_enable(int_id, en); + gic_set_prio(int_id, 0x1); + GICD.set_route(int_id, cortex_a::registers::MPIDR_EL1.get() as usize); + } + #[cfg(not(feature = "gicv3"))] + { + if en { + GICD.set_prio(int_id, 0x1); + GICD.set_trgt(int_id, 1 << Platform::cpuid_to_cpuif(current_cpu().id)); + GICD.set_enable(int_id, en); + } else { + GICD.set_enable(int_id, en); + } + } } -} -pub fn interrupt_arch_vm_register(vm: Vm, id: usize) { - super::vgic_set_hw_int(vm, id); -} + fn fetch() -> Option { + gicc_get_current_irq() + } + + fn clear() { + gic_cpu_reset(); + gicc_clear_current_irq(true); + } -pub fn interrupt_arch_vm_inject(vm: Vm, vcpu: Vcpu, int_id: usize) { - let vgic = vm.vgic(); - // println!("int {}, cur vcpu vm {}, trgt vcpu vm {}", int_id, active_vm_id(), vcpu.vm_id()); - // restore_vcpu_gic(current_cpu().active_vcpu.clone(), vcpu.clone()); - if let Some(cur_vcpu) = current_cpu().active_vcpu.clone() { - if cur_vcpu.vm_id() == vcpu.vm_id() { - vgic.inject(vcpu, int_id); - return; + fn finish(_int_id: usize) { + todo!() + } + + fn ipi_send(cpu_id: usize, ipi_id: usize) { + if ipi_id < GIC_SGIS_NUM { + #[cfg(not(feature = "gicv3"))] + GICD.send_sgi(Platform::cpuid_to_cpuif(cpu_id), ipi_id); + #[cfg(feature = "gicv3")] + GICD.send_sgi(cpu_id, ipi_id); } } - vcpu.push_int(int_id); - // save_vcpu_gic(current_cpu().active_vcpu.clone(), vcpu.clone()); -} + fn vm_inject(vm: Vm, vcpu: Vcpu, int_id: usize) { + let vgic = vm.vgic(); + if let Some(cur_vcpu) = current_cpu().active_vcpu.clone() { + if cur_vcpu.vm_id() == vcpu.vm_id() { + vgic.inject(vcpu, int_id); + return; + } + } + + vcpu.push_int(int_id); + } -pub fn interrupt_arch_clear() { - gic_cpu_reset(); - gicc_clear_current_irq(true); + fn vm_register(vm: Vm, int_id: usize) { + super::vgic_set_hw_int(vm, int_id); + } + + fn clear_current_irq(for_hypervisor: bool) { + gicc_clear_current_irq(for_hypervisor); + } } diff --git a/src/arch/aarch64/memcpy.S b/src/arch/aarch64/memcpy.S deleted file mode 100644 index 9fea4ae6cfcd27351a336467f7be10df838bd270..0000000000000000000000000000000000000000 --- a/src/arch/aarch64/memcpy.S +++ /dev/null @@ -1,160 +0,0 @@ -/* - * memcpy - copy memory area - * - * Copyright (c) 2012-2020, Arm Limited. - * SPDX-License-Identifier: MIT - */ - -/* Assumptions: - * - * ARMv8-a, AArch64, unaligned accesses. - * - */ - -/* This implementation of memcpy uses unaligned accesses and branchless - sequences to keep the code small, simple and improve performance. - - Copies are split into 3 main cases: small copies of up to 32 bytes, medium - copies of up to 128 bytes, and large copies. The overhead of the overlap - check is negligible since it is only required for large copies. - - Large copies use a software pipelined loop processing 64 bytes per iteration. - The destination pointer is 16-byte aligned to minimize unaligned accesses. - The loop tail is handled by always copying 64 bytes from the end. -*/ - -.text -.global memcpy -// .type memcpy,%function -memcpy: - add x4, x1, x2 - add x5, x0, x2 - cmp x2, 128 - b.hi .Lcopy_long - cmp x2, 32 - b.hi .Lcopy32_128 - - /* Small copies: 0..32 bytes. */ - cmp x2, 16 - b.lo .Lcopy16 - ldp x6, x7, [x1] - ldp x12, x13, [x4, -16] - stp x6, x7, [x0] - stp x12, x13, [x5, -16] - ret - - /* Copy 8-15 bytes. */ -.Lcopy16: - tbz x2, 3, .Lcopy8 - ldr x6, [x1] - ldr x7, [x4, -8] - str x6, [x0] - str x7, [x5, -8] - ret - - .p2align 3 - /* Copy 4-7 bytes. */ -.Lcopy8: - tbz x2, 2, .Lcopy4 - ldr w6, [x1] - ldr w8, [x4, -4] - str w6, [x0] - str w8, [x5, -4] - ret - - /* Copy 0..3 bytes using a branchless sequence. */ -.Lcopy4: - cbz x2, .Lcopy0 - lsr x14, x2, 1 - ldrb w6, [x1] - ldrb w10, [x4, -1] - ldrb w8, [x1, x14] - strb w6, [x0] - strb w8, [x0, x14] - strb w10, [x5, -1] -.Lcopy0: - ret - - .p2align 4 - /* Medium copies: 33..128 bytes. */ -.Lcopy32_128: - ldp x6, x7, [x1] - ldp x8, x9, [x1, 16] - ldp x10, x11, [x4, -32] - ldp x12, x13, [x4, -16] - cmp x2, 64 - b.hi .Lcopy128 - stp x6, x7, [x0] - stp x8, x9, [x0, 16] - stp x10, x11, [x5, -32] - stp x12, x13, [x5, -16] - ret - - .p2align 4 - /* Copy 65..128 bytes. */ -.Lcopy128: - ldp x14, x15, [x1, 32] - ldp x16, x17, [x1, 48] - cmp x2, 96 - b.ls .Lcopy96 - ldp x2, x3, [x4, -64] - ldp x1, x4, [x4, -48] - stp x2, x3, [x5, -64] - stp x1, x4, [x5, -48] -.Lcopy96: - stp x6, x7, [x0] - stp x8, x9, [x0, 16] - stp x14, x15, [x0, 32] - stp x16, x17, [x0, 48] - stp x10, x11, [x5, -32] - stp x12, x13, [x5, -16] - ret - - .p2align 4 - /* Copy more than 128 bytes. */ -.Lcopy_long: - - /* Copy 16 bytes and then align x3 to 16-byte alignment. */ - - ldp x12, x13, [x1] - and x14, x0, 15 - bic x3, x0, 15 - sub x1, x1, x14 - add x2, x2, x14 /* x2 is now 16 too large. */ - ldp x6, x7, [x1, 16] - stp x12, x13, [x0] - ldp x8, x9, [x1, 32] - ldp x10, x11, [x1, 48] - ldp x12, x13, [x1, 64]! - subs x2, x2, 128 + 16 /* Test and readjust x2. */ - b.ls .Lcopy64_from_end - -.Lloop64: - stp x6, x7, [x3, 16] - ldp x6, x7, [x1, 16] - stp x8, x9, [x3, 32] - ldp x8, x9, [x1, 32] - stp x10, x11, [x3, 48] - ldp x10, x11, [x1, 48] - stp x12, x13, [x3, 64]! - ldp x12, x13, [x1, 64]! - subs x2, x2, 64 - b.hi .Lloop64 - - /* Write the last iteration and copy 64 bytes from the end. */ -.Lcopy64_from_end: - ldp x14, x15, [x4, -64] - stp x6, x7, [x3, 16] - ldp x6, x7, [x4, -48] - stp x8, x9, [x3, 32] - ldp x8, x9, [x4, -32] - stp x10, x11, [x3, 48] - ldp x10, x11, [x4, -16] - stp x12, x13, [x3, 64] - stp x14, x15, [x5, -64] - stp x6, x7, [x5, -48] - stp x8, x9, [x5, -32] - stp x10, x11, [x5, -16] - ret - -.size memcpy,.-memcpy diff --git a/src/arch/aarch64/memset.S b/src/arch/aarch64/memset.S deleted file mode 100644 index 0bf58ce1110fe0fe5110fea030359513738f0c82..0000000000000000000000000000000000000000 --- a/src/arch/aarch64/memset.S +++ /dev/null @@ -1,212 +0,0 @@ -/* Copyright (c) 2012-2013, Linaro Limited - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Linaro nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* Assumptions: - * - * ARMv8-a, AArch64 - * Unaligned accesses - * - */ - - -/* By default we assume that the DC instruction can be used to zero - data blocks more efficiently. In some circumstances this might be - unsafe, for example in an asymmetric multiprocessor environment with - different DC clear lengths (neither the upper nor lower lengths are - safe to use). The feature can be disabled by defining DONT_USE_DC. - If code may be run in a virtualized environment, then define - MAYBE_VIRT. This will cause the code to cache the system register - values rather than re-reading them each call. */ - - .macro def_fn f p2align=0 - .text - .p2align \p2align - .global \f - .type \f, %function -\f: - .endm -def_fn memset p2align=6 - mov x8, x0 /* Preserve return value. */ - ands w7, w1, #255 - - orr w7, w7, w7, lsl #8 - orr w7, w7, w7, lsl #16 - orr x7, x7, x7, lsl #32 -.Ltail_maybe_long: - cmp x2, #64 - b.ge .Lnot_short -.Ltail_maybe_tiny: - cmp x2, #15 - b.le .Ltail15tiny -.Ltail63: - ands x3, x2, #0x30 - b.eq .Ltail15 - add x8, x8, x3 - cmp w3, #0x20 - b.eq 1f - b.lt 2f - stp x7, x7, [x8, #-48] -1: - stp x7, x7, [x8, #-32] -2: - stp x7, x7, [x8, #-16] -.Ltail15: - and x2, x2, #15 - add x8, x8, x2 - stp x7, x7, [x8, #-16] /* Repeat some/all of last store. */ - ret -.Ltail15tiny: - /* Set up to 15 bytes. Does not assume earlier memory - being set. */ - tbz x2, #3, 1f - str x7, [x8], #8 -1: - tbz x2, #2, 1f - str w7, [x8], #4 -1: - tbz x2, #1, 1f - strh w7, [x8], #2 -1: - tbz x2, #0, 1f - strb w7, [x8] -1: - ret - /* Critical loop. Start at a new cache line boundary. Assuming - * 64 bytes per line, this ensures the entire loop is in one line. */ - .p2align 6 -.Lnot_short: - neg x4, x8 - ands x4, x4, #15 - b.eq 2f - /* Bring x8 to 128-bit (16-byte) alignment. We know that there's - * more than that to set, so we simply store 16 bytes and advance by - * the amount required to reach alignment. */ - sub x2, x2, x4 - stp x7, x7, [x8] - add x8, x8, x4 - /* There may be less than 63 bytes to go now. */ - cmp x2, #63 - b.le .Ltail63 -2: - sub x8, x8, #16 /* Pre-bias. */ - sub x2, x2, #64 -1: - stp x7, x7, [x8, #16] - stp x7, x7, [x8, #32] - stp x7, x7, [x8, #48] - stp x7, x7, [x8, #64]! - subs x2, x2, #64 - b.ge 1b - tst x2, #0x3f - add x8, x8, #16 - b.ne .Ltail63 - ret -#ifndef DONT_USE_DC - /* For zeroing memory, check to see if we can use the ZVA feature to - * zero entire 'cache' lines. */ -.Lzero_mem: - mov x7, #0 - cmp x2, #63 - b.le .Ltail_maybe_tiny - neg x4, x8 - ands x4, x4, #15 - b.eq 1f - sub x2, x2, x4 - stp x7, x7, [x8] - add x8, x8, x4 - cmp x2, #63 - b.le .Ltail63 -1: - /* For zeroing small amounts of memory, it's not worth setting up - * the line-clear code. */ - cmp x2, #128 - b.lt .Lnot_short -#ifdef MAYBE_VIRT - /* For efficiency when virtualized, we cache the ZVA capability. */ - adrp x4, .Lcache_clear - ldr w5, [x4, #:lo12:.Lcache_clear] - tbnz w5, #31, .Lnot_short - cbnz w5, .Lzero_by_line - mrs x3, dczid_el0 - tbz x3, #4, 1f - /* ZVA not available. Remember this for next time. */ - mov w5, #~0 - str w5, [x4, #:lo12:.Lcache_clear] - b .Lnot_short -1: - mov w9, #4 - and w5, w3, #15 /* Safety: other bits reserved. */ - lsl w5, w9, w5 - str w5, [x4, #:lo12:.Lcache_clear] -#else - mrs x3, dczid_el0 - tbnz x3, #4, .Lnot_short - mov w9, #4 - and w5, w3, #15 /* Safety: other bits reserved. */ - lsl w5, w9, w5 -#endif -.Lzero_by_line: - /* Compute how far we need to go to become suitably aligned. We're - * already at quad-word alignment. */ - cmp x2, x5 - b.lt .Lnot_short /* Not enough to reach alignment. */ - sub x6, x5, #1 - neg x4, x8 - ands x4, x4, x6 - b.eq 1f /* Already aligned. */ - /* Not aligned, check that there's enough to copy after alignment. */ - sub x3, x2, x4 - cmp x3, #64 - ccmp x3, x5, #8, ge /* NZCV=0b1000 */ - b.lt .Lnot_short - /* We know that there's at least 64 bytes to zero and that it's safe - * to overrun by 64 bytes. */ - mov x2, x3 -2: - stp x7, x7, [x8] - stp x7, x7, [x8, #16] - stp x7, x7, [x8, #32] - subs x4, x4, #64 - stp x7, x7, [x8, #48] - add x8, x8, #64 - b.ge 2b - /* We've overrun a bit, so adjust x8 downwards. */ - add x8, x8, x4 -1: - sub x2, x2, x5 -3: - dc zva, x8 - add x8, x8, x5 - subs x2, x2, x5 - b.ge 3b - ands x2, x2, x6 - b.ne .Ltail_maybe_long - ret - .size memset, .-memset -#ifdef MAYBE_VIRT - .bss - .p2align 2 -.Lcache_clear: - .space 4 -#endif -#endif /* DONT_USE_DC */ \ No newline at end of file diff --git a/src/arch/aarch64/mmu.rs b/src/arch/aarch64/mmu.rs index 9bde82037106393cd0ceb2110552c29259809fc8..eb476fb7d0734d508c3435c9d08efd10101add67 100644 --- a/src/arch/aarch64/mmu.rs +++ b/src/arch/aarch64/mmu.rs @@ -8,35 +8,14 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use core::arch::global_asm; use tock_registers::*; use tock_registers::interfaces::*; -use crate::board::PLAT_DESC; -use crate::lib::memset_safe; +use crate::utils::{memset, bit_extract}; +use crate::arch::{at, isb}; use super::interface::*; -#[cfg(feature = "tx2")] -#[cfg(not(feature = "update"))] -global_asm!(include_str!("start.S")); - -#[cfg(feature = "update")] -#[cfg(feature = "tx2")] -global_asm!(include_str!("start_update.S")); - -#[cfg(feature = "pi4")] -#[cfg(not(feature = "update"))] -global_asm!(include_str!("start_pi4.S")); - -#[cfg(feature = "qemu")] -global_asm!(include_str!("start_qemu.S")); - -// const PHYSICAL_ADDRESS_LIMIT_GB: usize = BOARD_PHYSICAL_ADDRESS_LIMIT >> 30; -// const PAGE_SIZE: usize = 4096; -// const PAGE_SHIFT: usize = 12; -// const ENTRY_PER_PAGE: usize = PAGE_SIZE / 8; - register_bitfields! {u64, pub TableDescriptor [ NEXT_LEVEL_TABLE_PPN OFFSET(12) NUMBITS(36) [], // [47:12] @@ -92,22 +71,26 @@ register_bitfields! {u64, ] } +enum MemoryType { + Normal, + Device, +} + #[derive(Copy, Clone)] #[repr(transparent)] struct BlockDescriptor(u64); impl BlockDescriptor { - fn new(output_addr: usize, device: bool) -> BlockDescriptor { + fn new(output_addr: usize, mem_type: MemoryType) -> BlockDescriptor { BlockDescriptor( (PageDescriptorS1::OUTPUT_PPN.val((output_addr >> PAGE_SHIFT) as u64) + PageDescriptorS1::AF::True + PageDescriptorS1::AP::RW_ELx + PageDescriptorS1::TYPE::Block + PageDescriptorS1::VALID::True - + if device { - PageDescriptorS1::AttrIndx::Attr0 + PageDescriptorS1::SH::OuterShareable - } else { - PageDescriptorS1::AttrIndx::Attr1 + PageDescriptorS1::SH::InnerShareable + + match mem_type { + MemoryType::Device => PageDescriptorS1::AttrIndx::Attr0 + PageDescriptorS1::SH::OuterShareable, + MemoryType::Normal => PageDescriptorS1::AttrIndx::Attr1 + PageDescriptorS1::SH::InnerShareable, }) .value, ) @@ -128,51 +111,68 @@ impl BlockDescriptor { #[repr(C)] #[repr(align(4096))] +/// PageTable struct pub struct PageTables { - lvl1: [BlockDescriptor; ENTRY_PER_PAGE], + entry: [BlockDescriptor; ENTRY_PER_PAGE], } -const LVL1_SHIFT: usize = 30; +/// level1 page table +pub static LVL1_PAGE_TABLE: PageTables = PageTables { + entry: [BlockDescriptor(0); ENTRY_PER_PAGE], +}; + +/// level2 page table +pub static LVL2_PAGE_TABLE: PageTables = PageTables { + entry: [BlockDescriptor(0); ENTRY_PER_PAGE], +}; + const PLATFORM_PHYSICAL_LIMIT_GB: usize = 16; #[no_mangle] // #[link_section = ".text.boot"] +/// populate page table pub extern "C" fn pt_populate(lvl1_pt: &mut PageTables, lvl2_pt: &mut PageTables) { - let lvl1_base = lvl1_pt as *const _ as usize; + let lvl1_base: usize = lvl1_pt as *const _ as usize; let lvl2_base = lvl2_pt as *const _ as usize; - memset_safe(lvl1_base as *mut u8, 0, PAGE_SIZE); - memset_safe(lvl2_base as *mut u8, 0, PAGE_SIZE); + // SAFETY: + // The lvl1_pt and lvl2_pt are writable for the Hypervisor in EL2. + // c is a valid value of type u8 without overflow. + unsafe { + memset(lvl1_base as *mut u8, 0, PAGE_SIZE); + memset(lvl2_base as *mut u8, 0, PAGE_SIZE); + } #[cfg(feature = "tx2")] { use crate::arch::pt_lvl2_idx; + use crate::board::PLAT_DESC; for i in 0..PLATFORM_PHYSICAL_LIMIT_GB { + use crate::arch::LVL1_SHIFT; let output_addr = i << LVL1_SHIFT; - lvl1_pt.lvl1[i] = if output_addr >= PLAT_DESC.mem_desc.base { - BlockDescriptor::new(output_addr, false) + lvl1_pt.entry[i] = if output_addr >= PLAT_DESC.mem_desc.base { + BlockDescriptor::new(output_addr, MemoryType::Normal) } else { BlockDescriptor::invalid() } } // for i in PLATFORM_PHYSICAL_LIMIT_GB..ENTRY_PER_PAGE { - // pt.lvl1[i] = BlockDescriptor::invalid(); + // pt.entry[i] = BlockDescriptor::invalid(); // } - // map the devices to HIGH 32GB, whose offset is 2^35 = 0x8_0000_0000 - lvl1_pt.lvl1[32] = BlockDescriptor::table(lvl2_base); + lvl1_pt.entry[0] = BlockDescriptor::table(lvl2_base); // 0x200000 ~ 2MB // UART0 ~ 0x3000000 - 0x3200000 (0x3100000) // UART1 ~ 0xc200000 - 0xc400000 (0xc280000) // EMMC ~ 0x3400000 - 0x3600000 (0x3460000) // GIC ~ 0x3800000 - 0x3a00000 (0x3881000) // SMMU ~ 0x12000000 - 0x13000000 - lvl2_pt.lvl1[pt_lvl2_idx(0x3000000)] = BlockDescriptor::new(0x3000000, true); - lvl2_pt.lvl1[pt_lvl2_idx(0xc200000)] = BlockDescriptor::new(0xc200000, true); - // lvl2_pt.lvl1[pt_lvl2_idx(0x3400000)] = BlockDescriptor::new(0x3400000, true); - lvl2_pt.lvl1[pt_lvl2_idx(0x3800000)] = BlockDescriptor::new(0x3800000, true); + lvl2_pt.entry[pt_lvl2_idx(0x3000000)] = BlockDescriptor::new(0x3000000, MemoryType::Device); + lvl2_pt.entry[pt_lvl2_idx(0xc200000)] = BlockDescriptor::new(0xc200000, MemoryType::Device); + // lvl2_pt.entry[pt_lvl2_idx(0x3400000)] = BlockDescriptor::new(0x3400000, MemoryType::Device); + lvl2_pt.entry[pt_lvl2_idx(0x3800000)] = BlockDescriptor::new(0x3800000, MemoryType::Device); for i in 0..(0x100_0000 / 0x200000) { let addr = 0x12000000 + i * 0x200000; - lvl2_pt.lvl1[pt_lvl2_idx(addr)] = BlockDescriptor::new(addr, true); + lvl2_pt.entry[pt_lvl2_idx(addr)] = BlockDescriptor::new(addr, MemoryType::Device); } } #[cfg(feature = "pi4")] @@ -183,48 +183,85 @@ pub extern "C" fn pt_populate(lvl1_pt: &mut PageTables, lvl2_pt: &mut PageTables // crate::driver::putc('e' as u8); // println!("pt"); // 0x0_0000_0000 ~ 0x0_c000_0000 --> normal memory (3GB) - lvl1_pt.lvl1[0] = BlockDescriptor::new(0, false); - lvl1_pt.lvl1[1] = BlockDescriptor::new(0x40000000, false); - lvl1_pt.lvl1[2] = BlockDescriptor::new(0x80000000, false); - lvl1_pt.lvl1[3] = BlockDescriptor::table(lvl2_base); + lvl1_pt.entry[0] = BlockDescriptor::new(0, MemoryType::Normal); + lvl1_pt.entry[1] = BlockDescriptor::new(0x40000000, MemoryType::Normal); + lvl1_pt.entry[2] = BlockDescriptor::new(0x80000000, MemoryType::Normal); + lvl1_pt.entry[3] = BlockDescriptor::table(lvl2_base); // 0x0_c000_0000 ~ 0x0_fc00_0000 --> normal memory (960MB) for i in 0..480 { - lvl2_pt.lvl1[i] = BlockDescriptor::new(0x0c0000000 + (i << LVL2_SHIFT), false); + lvl2_pt.entry[i] = BlockDescriptor::new(0x0c0000000 + (i << LVL2_SHIFT), MemoryType::Normal); } // 0x0_fc00_0000 ~ 0x1_0000_0000 --> device memory (64MB) for i in 480..512 { - lvl2_pt.lvl1[i] = BlockDescriptor::new(0x0c0000000 + (i << LVL2_SHIFT), true); + lvl2_pt.entry[i] = BlockDescriptor::new(0x0c0000000 + (i << LVL2_SHIFT), MemoryType::Device); } // 0x1_0000_0000 ~ 0x2_0000_0000 --> normal memory (4GB) - lvl1_pt.lvl1[4] = BlockDescriptor::new(0x100000000, false); - lvl1_pt.lvl1[5] = BlockDescriptor::new(0x140000000, false); - lvl1_pt.lvl1[6] = BlockDescriptor::new(0x180000000, false); - lvl1_pt.lvl1[7] = BlockDescriptor::new(0x1c0000000, false); + lvl1_pt.entry[4] = BlockDescriptor::new(0x100000000, MemoryType::Normal); + lvl1_pt.entry[5] = BlockDescriptor::new(0x140000000, MemoryType::Normal); + lvl1_pt.entry[6] = BlockDescriptor::new(0x180000000, MemoryType::Normal); + lvl1_pt.entry[7] = BlockDescriptor::new(0x1c0000000, MemoryType::Normal); for i in 8..512 { - lvl1_pt.lvl1[i] = BlockDescriptor::invalid(); + lvl1_pt.entry[i] = BlockDescriptor::invalid(); } - // 0x8_0000_0000 + 0x0_c000_0000 - lvl1_pt.lvl1[32 + 3] = BlockDescriptor::table(lvl2_base); } #[cfg(feature = "qemu")] { use crate::arch::LVL2_SHIFT; + use crate::board::PLAT_DESC; for index in 0..PLATFORM_PHYSICAL_LIMIT_GB { + use crate::arch::LVL1_SHIFT; let pa = index << LVL1_SHIFT; - lvl1_pt.lvl1[index] = BlockDescriptor::new(pa, pa < PLAT_DESC.mem_desc.base); + if pa < PLAT_DESC.mem_desc.base { + lvl1_pt.entry[index] = BlockDescriptor::new(pa, MemoryType::Device); + } else { + lvl1_pt.entry[index] = BlockDescriptor::new(pa, MemoryType::Normal); + } } - lvl1_pt.lvl1[32] = BlockDescriptor::table(lvl2_base); + lvl1_pt.entry[32] = BlockDescriptor::table(lvl2_base); for (index, pa) in (0..PLAT_DESC.mem_desc.base).step_by(1 << LVL2_SHIFT).enumerate() { if index >= 512 { break; } - lvl2_pt.lvl1[index] = BlockDescriptor::new(pa, true); + lvl2_pt.entry[index] = BlockDescriptor::new(pa, MemoryType::Device); } } + #[cfg(feature = "rk3588")] + { + use crate::arch::LVL2_SHIFT; + // 0x0020_0000 ~ 0xc000_0000 --> normal memory (3GB) + lvl1_pt.entry[0] = BlockDescriptor::new(0, MemoryType::Normal); + lvl1_pt.entry[1] = BlockDescriptor::new(0x40000000, MemoryType::Normal); + lvl1_pt.entry[2] = BlockDescriptor::new(0x80000000, MemoryType::Normal); + lvl1_pt.entry[3] = BlockDescriptor::table(lvl2_base); + // 0xc000_0000 ~ 0xf000_0000 --> normal memory (768MB) + const DEVICE_BOUND: usize = (0xf000_0000 - 0xc000_0000) / (1 << LVL2_SHIFT); + for i in 0..DEVICE_BOUND { + lvl2_pt.entry[i] = BlockDescriptor::new(0x0c0000000 + (i << LVL2_SHIFT), MemoryType::Normal); + } + // 0x0_f000_0000 ~ 0x1_0000_0000 --> device memory (256MB) + for i in DEVICE_BOUND..512 { + lvl2_pt.entry[i] = BlockDescriptor::new(0x0c0000000 + (i << LVL2_SHIFT), MemoryType::Device); + } + // 0x1_0000_0000 ~ 0x2_0000_0000 --> normal memory (4GB) + lvl1_pt.entry[4] = BlockDescriptor::new(0x100000000, MemoryType::Normal); + lvl1_pt.entry[5] = BlockDescriptor::new(0x140000000, MemoryType::Normal); + lvl1_pt.entry[6] = BlockDescriptor::new(0x180000000, MemoryType::Normal); + lvl1_pt.entry[7] = BlockDescriptor::new(0x1c0000000, MemoryType::Normal); + for i in 8..512 { + lvl1_pt.entry[i] = BlockDescriptor::invalid(); + } + // 0x200000 ~ 2MB + // UART0 ~ 0xfea00000 - 0xfec00000 (0xfeb50000) + // UART1 ~ 0xfea00000 - 0xfec00000 (0xfebc0000) + // EMMC ~ 0xfe200000 - 0xfe400000 (0xfe2e0000) + // GIC ~ 0xfe600000 - 0xfe800000 (0xfe600000) + // SMMU1 ~ 0xfc800000 - 0xfce00000 (0xfc900000 size:0x200000;0xfcb00000 size:0x200000) + } } #[no_mangle] // #[link_section = ".text.boot"] +/// init mmu, set mmu-related registers pub extern "C" fn mmu_init(pt: &PageTables) { use cortex_a::registers::*; MAIR_EL2.write( @@ -234,7 +271,7 @@ pub extern "C" fn mmu_init(pt: &PageTables) { + MAIR_EL2::Attr2_Normal_Outer::NonCacheable + MAIR_EL2::Attr2_Normal_Inner::NonCacheable, ); - TTBR0_EL2.set(&pt.lvl1 as *const _ as u64); + TTBR0_EL2.set(&pt.entry as *const _ as u64); TCR_EL2.write( TCR_EL2::PS::Bits_48 @@ -249,3 +286,25 @@ pub extern "C" fn mmu_init(pt: &PageTables) { // SCTLR_EL2.modify(SCTLR_EL2::M::Enable + SCTLR_EL2::C::Cacheable + SCTLR_EL2::I::Cacheable); // barrier::isb(barrier::SY); } + +const PAR_EL1_OFF: usize = 12; +const PAR_EL1_LEN: usize = 36; + +/// translate gva to ipa +pub fn gva2ipa(gva: usize) -> Result { + use cortex_a::registers::PAR_EL1; + + let par = PAR_EL1.get(); + at::s1e1r(gva); + isb(); + let tmp = PAR_EL1.get(); + PAR_EL1.set(par); + + if (tmp & PAR_EL1::F::TranslationAborted.value) != 0 { + Err(()) + } else { + let par_pa = bit_extract(tmp as usize, PAR_EL1_OFF, PAR_EL1_LEN); + let pa = (par_pa << PAR_EL1_OFF) | (gva & (PAGE_SIZE - 1)); + Ok(pa) + } +} diff --git a/src/arch/aarch64/mod.rs b/src/arch/aarch64/mod.rs index 813dc5f840b9d455f63ea9126f733b46b7fc86a7..02ec57e314b14179d4bb25bdc7bdd41d986ca70e 100644 --- a/src/arch/aarch64/mod.rs +++ b/src/arch/aarch64/mod.rs @@ -11,30 +11,36 @@ pub use self::context_frame::*; pub use self::cpu::*; pub use self::exception::*; +#[cfg(not(feature = "gicv3"))] pub use self::gic::*; +#[cfg(feature = "gicv3")] +pub use self::gicv3::*; pub use self::interface::*; pub use self::interrupt::*; -pub use self::mmu::*; pub use self::page_table::*; pub use self::psci::*; -pub use self::regs::*; pub use self::smc::*; -pub use self::cache::*; pub use self::smmu::*; pub use self::sync::*; pub use self::timer::*; -pub use self::tlb::*; pub use self::vcpu::*; +#[cfg(not(feature = "gicv3"))] pub use self::vgic::*; +#[cfg(feature = "gicv3")] +pub use self::vgicv3::*; +pub use self::start::*; +pub use self::regs::*; #[macro_use] mod regs; - mod cache; mod context_frame; mod cpu; mod exception; +#[cfg(not(feature = "gicv3"))] mod gic; +#[cfg(feature = "gicv3")] +mod gicv3; mod interface; mod interrupt; mod mmu; @@ -42,8 +48,18 @@ mod page_table; mod psci; mod smc; mod smmu; +mod start; mod sync; mod timer; mod tlb; mod vcpu; +#[cfg(not(feature = "gicv3"))] mod vgic; +#[cfg(feature = "gicv3")] +mod vgicv3; + +#[repr(C)] +pub struct ArchDesc { + pub gic_desc: GicDesc, + pub smmu_desc: SmmuDesc, +} diff --git a/src/arch/aarch64/page_table.rs b/src/arch/aarch64/page_table.rs index 8df37668facb67cef5458510f3a0c9510ece0569..76f3eb718ea36e75aeb438132c63beb47647dcac 100644 --- a/src/arch/aarch64/page_table.rs +++ b/src/arch/aarch64/page_table.rs @@ -11,19 +11,18 @@ use alloc::sync::Arc; use alloc::vec::Vec; -// use rlibc::{memcpy, memset}; use spin::Mutex; use crate::arch::ArchPageTableEntryTrait; use crate::arch::WORD_SIZE; -use crate::kernel::Cpu; -use crate::lib::{memcpy_safe, memset_safe}; -use crate::lib::round_up; +use crate::kernel::mem_page_alloc; +use crate::utils::round_up; use crate::mm::PageFrame; use super::{PAGE_SIZE, PTE_PER_PAGE}; // page_table const +pub const LVL0_SHIFT: usize = 39; pub const LVL1_SHIFT: usize = 30; pub const LVL2_SHIFT: usize = 21; pub const LVL3_SHIFT: usize = 12; @@ -47,6 +46,7 @@ pub const PTE_S1_FIELD_AF: usize = 1 << 10; pub const PTE_S2_FIELD_MEM_ATTR_DEVICE_NGNRNE: usize = 0; pub const PTE_S2_FIELD_MEM_ATTR_NORMAL_OUTER_WRITE_BACK_CACHEABLE: usize = 0b11 << 4; +pub const PTE_S2_FIELD_MEM_ATTR_NORMAL_OUTER_WRITE_BACK_NOCACHEABLE: usize = 0b1 << 4; pub const PTE_S2_FIELD_MEM_ATTR_NORMAL_INNER_WRITE_BACK_CACHEABLE: usize = 0b11 << 2; @@ -74,19 +74,27 @@ pub const PTE_S2_NORMAL: usize = PTE_S2_FIELD_MEM_ATTR_NORMAL_INNER_WRITE_BACK_C | PTE_S2_FIELD_SH_OUTER_SHAREABLE | PTE_S2_FIELD_AF; +pub const PTE_S2_NORMALNOCACHE: usize = PTE_S2_FIELD_MEM_ATTR_NORMAL_INNER_WRITE_BACK_CACHEABLE + | PTE_S2_FIELD_MEM_ATTR_NORMAL_OUTER_WRITE_BACK_NOCACHEABLE + | PTE_S2_FIELD_AP_RW + | PTE_S2_FIELD_SH_OUTER_SHAREABLE + | PTE_S2_FIELD_AF; + pub const PTE_S2_RO: usize = PTE_S2_FIELD_MEM_ATTR_NORMAL_INNER_WRITE_BACK_CACHEABLE | PTE_S2_FIELD_MEM_ATTR_NORMAL_OUTER_WRITE_BACK_CACHEABLE | PTE_S2_FIELD_AP_RO | PTE_S2_FIELD_SH_OUTER_SHAREABLE | PTE_S2_FIELD_AF; -pub const CPU_BANKED_ADDRESS: usize = 0x400000000; - pub const fn pte_s1_field_attr_indx(idx: usize) -> usize { idx << 2 } // page_table function +pub fn pt_lvl0_idx(va: usize) -> usize { + (va >> LVL0_SHIFT) & (PTE_PER_PAGE - 1) +} + pub fn pt_lvl1_idx(va: usize) -> usize { (va >> LVL1_SHIFT) & (PTE_PER_PAGE - 1) } @@ -99,39 +107,9 @@ pub fn pt_lvl3_idx(va: usize) -> usize { (va >> LVL3_SHIFT) & (PTE_PER_PAGE - 1) } -pub fn pt_map_banked_cpu(cpu: &mut Cpu) -> usize { - extern "C" { - fn lvl1_page_table(); - } - let addr: usize = lvl1_page_table as usize; - - memcpy_safe(&(cpu.cpu_pt.lvl1) as *const _ as *mut u8, addr as *mut u8, PAGE_SIZE); - memset_safe(&(cpu.cpu_pt.lvl2) as *const _ as *mut u8, 0, PAGE_SIZE); - memset_safe(&(cpu.cpu_pt.lvl3) as *const _ as *mut u8, 0, PAGE_SIZE); - - let cpu_addr = cpu as *const _ as usize; - let lvl2_addr = &(cpu.cpu_pt.lvl2) as *const _ as usize; - let lvl3_addr = &(cpu.cpu_pt.lvl3) as *const _ as usize; - cpu.cpu_pt.lvl1[pt_lvl1_idx(CPU_BANKED_ADDRESS)] = lvl2_addr | PTE_S1_NORMAL | PTE_TABLE; - cpu.cpu_pt.lvl2[pt_lvl2_idx(CPU_BANKED_ADDRESS)] = lvl3_addr | PTE_S1_NORMAL | PTE_TABLE; - - use core::mem::size_of; - let page_num = round_up(size_of::(), PAGE_SIZE) / PAGE_SIZE; - - // println!("cpu page num is {}", page_num); - for i in 0..page_num { - cpu.cpu_pt.lvl3[pt_lvl3_idx(CPU_BANKED_ADDRESS) + i] = (cpu_addr + i * PAGE_SIZE) | PTE_S1_NORMAL | PTE_PAGE; - } - - // println!("cpu addr {:x}", cpu_addr); - // println!("lvl2 addr {:x}", lvl2_addr); - // println!("lvl3 addr {:x}", lvl3_addr); - - &(cpu.cpu_pt.lvl1) as *const _ as usize -} - #[repr(transparent)] #[derive(Copy, Clone, Debug)] +/// Aarch64PageTableEntry struct represents a page table entry. pub struct Aarch64PageTableEntry(usize); impl ArchPageTableEntryTrait for Aarch64PageTableEntry { @@ -157,10 +135,14 @@ impl ArchPageTableEntryTrait for Aarch64PageTableEntry { fn entry(&self, index: usize) -> Aarch64PageTableEntry { let addr = self.to_pa() + index * WORD_SIZE; + // SAFETY: The read of any address is safe in EL2 unsafe { Aarch64PageTableEntry((addr as *const usize).read_volatile()) } } - fn set_entry(&self, index: usize, value: Aarch64PageTableEntry) { + /// # Safety: + /// 1. The index can't be out of [0, PAGE_SIZE / WORD_SIZE) + /// 2. The page table entry have write permission + unsafe fn set_entry(&self, index: usize, value: Aarch64PageTableEntry) { let addr = self.to_pa() + index * WORD_SIZE; unsafe { (addr as *mut usize).write_volatile(value.0) } } @@ -171,12 +153,14 @@ impl ArchPageTableEntryTrait for Aarch64PageTableEntry { } #[derive(Clone)] +/// PageTable struct represents a page table, consisting of a directory and a list of pages. pub struct PageTable { pub directory: Arc, pub pages: Arc>>, } impl PageTable { + /// Create a new page table with given directory. pub fn new(directory: PageFrame) -> PageTable { PageTable { directory: Arc::new(directory), @@ -188,13 +172,23 @@ impl PageTable { self.directory.pa() } + /// modify a range of ipa's access permission pub fn access_permission(&self, start_ipa: usize, len: usize, ap: usize) -> (usize, usize) { let directory = Aarch64PageTableEntry::from_pa(self.directory.pa()); let mut ipa = start_ipa; let mut size = 0; let mut pa = 0; while ipa < (start_ipa + len) { - let l1e = directory.entry(pt_lvl1_idx(ipa)); + let l1e = if cfg!(feature = "lvl4") { + let l0e = directory.entry(pt_lvl0_idx(ipa)); + if !l0e.valid() { + ipa += 512 * 512 * 512 * 4096; // 512GB + continue; + } + l0e.entry(pt_lvl1_idx(ipa)) + } else { + directory.entry(pt_lvl1_idx(ipa)) + }; if !l1e.valid() { ipa += 512 * 512 * 4096; // 1GB: 9 + 9 + 12 bits continue; @@ -205,8 +199,13 @@ impl PageTable { continue; } else if l2e.to_pte() & 0b11 == PTE_BLOCK { let pte = l2e.to_pte() & !(0b11 << 6) | ap; - println!("access_permission set 512 page ipa {:x}", ipa); - l1e.set_entry(pt_lvl2_idx(ipa), Aarch64PageTableEntry::from_pa(pte)); + debug!("access_permission set 512 page ipa {:x}", ipa); + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + l1e.set_entry(pt_lvl2_idx(ipa), Aarch64PageTableEntry::from_pa(pte)); + } ipa += 512 * 4096; // 2MB: 9 + 12 bits pa = l2e.to_pa(); size += 512 * 4096; @@ -214,63 +213,126 @@ impl PageTable { } let l3e = l2e.entry(pt_lvl3_idx(ipa)); if l3e.valid() { - // if ipa < 0x8400_0000 { let pte = l3e.to_pte() & !(0b11 << 6) | ap; - l2e.set_entry(pt_lvl3_idx(ipa), Aarch64PageTableEntry::from_pa(pte)); + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + l2e.set_entry(pt_lvl3_idx(ipa), Aarch64PageTableEntry::from_pa(pte)); + } pa = l3e.to_pa(); size += 4096; - // } else if ipa < 0x8000_0000 { - // panic!("illegal ipa {:x}", start_ipa); - // } else { - // // println!( - // // "access_permission ipa {:x} l2e pte {:x} l3e pte {:x}", - // // ipa, - // // l2e.to_pte(), - // // l3e.to_pte() - // // ); - // pa = l3e.to_pa(); - // size += 4096; - // } } ipa += 4096; // 4KB: 12 bits } (pa, size) } + /// map a 2mb page of ipa to a physical address pub fn map_2mb(&self, ipa: usize, pa: usize, pte: usize) { let directory = Aarch64PageTableEntry::from_pa(self.directory.pa()); - let mut l1e = directory.entry(pt_lvl1_idx(ipa)); + let l0e = if cfg!(feature = "lvl4") { + let mut l0e = directory.entry(pt_lvl0_idx(ipa)); + if !l0e.valid() { + if let Ok(frame) = mem_page_alloc() { + l0e = Aarch64PageTableEntry::make_table(frame.pa()); + let mut pages = self.pages.lock(); + pages.push(frame); + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + directory.set_entry(pt_lvl0_idx(ipa), l0e); + } + } else { + error!("map lv0 page failed"); + return; + } + } + l0e + } else { + directory + }; + let mut l1e = l0e.entry(pt_lvl1_idx(ipa)); if !l1e.valid() { - let result = crate::kernel::mem_page_alloc(); + let result = mem_page_alloc(); if let Ok(frame) = result { l1e = Aarch64PageTableEntry::make_table(frame.pa()); let mut pages = self.pages.lock(); pages.push(frame); - directory.set_entry(pt_lvl1_idx(ipa), l1e); + if cfg!(feature = "lvl4") { + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + l0e.set_entry(pt_lvl1_idx(ipa), l1e); + } + } else { + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + directory.set_entry(pt_lvl1_idx(ipa), l1e); + } + } } else { - println!("map lv1 page failed"); + error!("map lv1 page failed"); return; } } let l2e = l1e.entry(pt_lvl2_idx(ipa)); if l2e.valid() { - println!("map_2mb lvl 2 already mapped with 0x{:x}", l2e.to_pte()); + debug!("map_2mb lvl 2 already mapped with 0x{:x}", l2e.to_pte()); } else { - l1e.set_entry(pt_lvl2_idx(ipa), Aarch64PageTableEntry::from_pa(pa | pte | PTE_BLOCK)); + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + l1e.set_entry(pt_lvl2_idx(ipa), Aarch64PageTableEntry::from_pa(pa | pte | PTE_BLOCK)); + } } } + /// unmap a 2mb page of ipa pub fn unmap_2mb(&self, ipa: usize) { let directory = Aarch64PageTableEntry::from_pa(self.directory.pa()); - let l1e = directory.entry(pt_lvl1_idx(ipa)); + let l0e = if cfg!(feature = "lvl4") { + let l0e = directory.entry(pt_lvl0_idx(ipa)); + if !l0e.valid() { + return; + } + l0e + } else { + directory + }; + let l1e = l0e.entry(pt_lvl1_idx(ipa)); if l1e.valid() { let l2e = l1e.entry(pt_lvl2_idx(ipa)); if l2e.valid() { - l1e.set_entry(pt_lvl2_idx(ipa), Aarch64PageTableEntry(0)); + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + l1e.set_entry(pt_lvl2_idx(ipa), Aarch64PageTableEntry(0)); + } if empty_page(l1e.to_pa()) { let l1e_pa = l1e.to_pa(); - directory.set_entry(pt_lvl1_idx(ipa), Aarch64PageTableEntry(0)); + if cfg!(feature = "lvl4") { + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + l0e.set_entry(pt_lvl1_idx(ipa), Aarch64PageTableEntry(0)); + } + } else { + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + directory.set_entry(pt_lvl1_idx(ipa), Aarch64PageTableEntry(0)); + } + } let mut pages = self.pages.lock(); pages.retain(|pf| pf.pa() != l1e_pa); } @@ -278,76 +340,145 @@ impl PageTable { } } + /// map a 2 4kb page of ipa to a physical address pub fn map(&self, ipa: usize, pa: usize, pte: usize) { - // if ipa >= 0x4_0000_0000 { - // println!("map ipa 0x{:x} to pa 0x{:x}", ipa, pa); - // } let directory = Aarch64PageTableEntry::from_pa(self.directory.pa()); - let mut l1e = directory.entry(pt_lvl1_idx(ipa)); - // if ipa >= 0x4_0000_0000 { - // println!("l1e {:x}", l1e.0); - // } + let l0e = if cfg!(feature = "lvl4") { + let mut l0e = directory.entry(pt_lvl0_idx(ipa)); + if !l0e.valid() { + if let Ok(frame) = mem_page_alloc() { + l0e = Aarch64PageTableEntry::make_table(frame.pa()); + let mut pages = self.pages.lock(); + pages.push(frame); + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + directory.set_entry(pt_lvl0_idx(ipa), l0e); + } + } else { + error!("map lv0 page failed"); + return; + } + } + l0e + } else { + directory + }; + let mut l1e = l0e.entry(pt_lvl1_idx(ipa)); if !l1e.valid() { - let result = crate::kernel::mem_page_alloc(); + let result = mem_page_alloc(); if let Ok(frame) = result { l1e = Aarch64PageTableEntry::make_table(frame.pa()); let mut pages = self.pages.lock(); pages.push(frame); - directory.set_entry(pt_lvl1_idx(ipa), l1e); + if cfg!(feature = "lvl4") { + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + l0e.set_entry(pt_lvl1_idx(ipa), l1e); + } + } else { + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + directory.set_entry(pt_lvl1_idx(ipa), l1e); + } + } } else { - println!("map lv1 page failed"); + error!("map lv1 page failed"); return; } } let mut l2e = l1e.entry(pt_lvl2_idx(ipa)); if !l2e.valid() { - let result = crate::kernel::mem_page_alloc(); + let result = mem_page_alloc(); if let Ok(frame) = result { l2e = Aarch64PageTableEntry::make_table(frame.pa()); let mut pages = self.pages.lock(); pages.push(frame); - l1e.set_entry(pt_lvl2_idx(ipa), l2e); + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + l1e.set_entry(pt_lvl2_idx(ipa), l2e); + } } else { - println!("map lv2 page failed {:#?}", result.err()); + error!("map lv2 page failed {:#?}", result.err()); return; } } else if l2e.to_pte() & 0b11 == PTE_BLOCK { - println!("map lvl 2 already mapped with 2mb 0x{:x}", l2e.to_pte()); + debug!("map lvl 2 already mapped with 2mb 0x{:x}", l2e.to_pte()); } - // if ipa >= 0x4_0000_0000 { - // println!("l2e {:x}", l2e.0); - // } let l3e = l2e.entry(pt_lvl3_idx(ipa)); if l3e.valid() { - println!("map lvl 3 already mapped with 0x{:x}", l3e.to_pte()); + debug!("map lvl 3 already mapped with 0x{:x}", l3e.to_pte()); } else { - l2e.set_entry(pt_lvl3_idx(ipa), Aarch64PageTableEntry::from_pa(pa | PTE_TABLE | pte)); + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + l2e.set_entry(pt_lvl3_idx(ipa), Aarch64PageTableEntry::from_pa(pa | PTE_TABLE | pte)); + } } - // if ipa >= 0x4_0000_0000 { - // println!("l3e {:x}", l3e.0); - // } } + /// unmap a 4kb page of ipa pub fn unmap(&self, ipa: usize) { let directory = Aarch64PageTableEntry::from_pa(self.directory.pa()); - let l1e = directory.entry(pt_lvl1_idx(ipa)); + let l0e = if cfg!(feature = "lvl4") { + let l0e = directory.entry(pt_lvl0_idx(ipa)); + if !l0e.valid() { + return; + } + l0e + } else { + directory + }; + let l1e = l0e.entry(pt_lvl1_idx(ipa)); if l1e.valid() { let l2e = l1e.entry(pt_lvl2_idx(ipa)); if l2e.valid() { let l3e = l2e.entry(pt_lvl3_idx(ipa)); if l3e.valid() { - l2e.set_entry(pt_lvl3_idx(ipa), Aarch64PageTableEntry::from_pa(0)); + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + l2e.set_entry(pt_lvl3_idx(ipa), Aarch64PageTableEntry::from_pa(0)); + } // check l2e if empty_page(l2e.to_pa()) { let l2e_pa = l2e.to_pa(); - l1e.set_entry(pt_lvl2_idx(ipa), Aarch64PageTableEntry(0)); + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + l1e.set_entry(pt_lvl2_idx(ipa), Aarch64PageTableEntry(0)); + } let mut pages = self.pages.lock(); pages.retain(|pf| pf.pa != l2e_pa); // check l1e if empty_page(l1e.to_pa()) { let l1e_pa = l1e.to_pa(); - directory.set_entry(pt_lvl1_idx(ipa), Aarch64PageTableEntry(0)); + if cfg!(feature = "lvl4") { + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + l0e.set_entry(pt_lvl1_idx(ipa), Aarch64PageTableEntry(0)); + } + } else { + // SAFETY: + // We set the page table entry to read-write + // And the idx will "& (PTE_PER_PAGE - 1)" so it will not out of range + unsafe { + directory.set_entry(pt_lvl1_idx(ipa), Aarch64PageTableEntry(0)); + } + } pages.retain(|pf| pf.pa != l1e_pa); } } @@ -356,6 +487,7 @@ impl PageTable { } } + /// map a range of ipa to a range of physical space, which page size is 2mb pub fn map_range_2mb(&self, ipa: usize, len: usize, pa: usize, pte: usize) { let size_2mb = 1 << LVL2_SHIFT; let page_num = round_up(len, size_2mb) / size_2mb; @@ -365,6 +497,7 @@ impl PageTable { } } + /// unmap a range of ipa, which page size is 2mb pub fn unmap_range_2mb(&self, ipa: usize, len: usize) { let size_2mb = 1 << LVL2_SHIFT; let page_num = round_up(len, size_2mb) / size_2mb; @@ -374,19 +507,15 @@ impl PageTable { } } + /// map a range of ipa to a range of physical space, which page size is 4kb pub fn map_range(&self, ipa: usize, len: usize, pa: usize, pte: usize) { let page_num = round_up(len, PAGE_SIZE) / PAGE_SIZE; - // if ipa == 0x8010000 { - // println!( - // "map_range: ipa {:x}, len {:x}, pa {:x}, pte 0b{:b}, page_num {:x}", - // ipa, len, pa, pte, page_num, - // ); - // } for i in 0..page_num { self.map(ipa + i * PAGE_SIZE, pa + i * PAGE_SIZE, pte); } } + /// unmap a range of ipa, which page size is 4kb pub fn unmap_range(&self, ipa: usize, len: usize) { let page_num = round_up(len, PAGE_SIZE) / PAGE_SIZE; for i in 0..page_num { @@ -394,24 +523,32 @@ impl PageTable { } } + /// display page table to debug pub fn show_pt(&self, ipa: usize) { // println!("show_pt"); let directory = Aarch64PageTableEntry::from_pa(self.directory.pa()); - println!("1 {:x}", directory.to_pte()); - let l1e = directory.entry(pt_lvl1_idx(ipa)); - println!("2 {:x}", l1e.to_pte()); + debug!("root {:x}", directory.to_pte()); + let l1e = if cfg!(feature = "lvl4") { + let l0e = directory.entry(pt_lvl0_idx(ipa)); + debug!("l0e {:x}", l0e.to_pte()); + l0e.entry(pt_lvl1_idx(ipa)) + } else { + directory.entry(pt_lvl1_idx(ipa)) + }; + debug!("l1e {:x}", l1e.to_pte()); let l2e = l1e.entry(pt_lvl2_idx(ipa)); - println!("3 {:x}", l2e.to_pte()); + debug!("l2e {:x}", l2e.to_pte()); if !l2e.valid() { - println!("invalid ipa {:x} to l2 pte {:x}", ipa, l2e.to_pte()); + error!("invalid ipa {:x} to l2 pte {:x}", ipa, l2e.to_pte()); } else if l2e.to_pte() & 0b11 == PTE_BLOCK { - println!("l2 ipa {:x} to pa {:x}", ipa, l2e.to_pte()); + debug!("l2 ipa {:x} to pa {:x}", ipa, l2e.to_pte()); } else { let l3e = l2e.entry(pt_lvl3_idx(ipa)); - println!("l3 ipa {:x} to pa {:x}", ipa, l3e.to_pte()); + debug!("l3 ipa {:x} to pa {:x}", ipa, l3e.to_pte()); } } + /// map a range of ipa to a range of physical space, using 4kb or 2mb page depending on the alignment of ipa, len and pa pub fn pt_map_range(&self, ipa: usize, len: usize, pa: usize, pte: usize, map_block: bool) { let size_2mb = 1 << LVL2_SHIFT; if ipa % size_2mb == 0 && len % size_2mb == 0 && pa % size_2mb == 0 && map_block { @@ -421,6 +558,7 @@ impl PageTable { } } + /// unmap a range of ipa, using 4kb or 2mb page depending on the alignment of ipa and len pub fn pt_unmap_range(&self, ipa: usize, len: usize, map_block: bool) { let size_2mb = 1 << LVL2_SHIFT; if ipa % size_2mb == 0 && len % size_2mb == 0 && map_block { @@ -431,8 +569,11 @@ impl PageTable { } } +/// check if a page is empty pub fn empty_page(addr: usize) -> bool { for i in 0..(PAGE_SIZE / 8) { + // SAFETY: + // the read of any address is safe in EL2 if unsafe { ((addr + i * 8) as *const usize).read_volatile() != 0 } { return false; } diff --git a/src/arch/aarch64/psci.rs b/src/arch/aarch64/psci.rs index 378bb9c12432551d927611cc299e2cf1f21e9b14..400f8812ca132f4a143b40fc2c810489abc391ee 100644 --- a/src/arch/aarch64/psci.rs +++ b/src/arch/aarch64/psci.rs @@ -9,23 +9,51 @@ // See the Mulan PSL v2 for more details. use crate::arch::{gic_cpu_init, gicc_clear_current_irq, vcpu_arch_init}; +use crate::board::{Platform, PlatOperation}; use crate::kernel::{cpu_idle, current_cpu, ipi_intra_broadcast_msg, Scheduler, timer_enable, Vcpu, VcpuState, Vm}; use crate::kernel::{active_vm, ipi_send_msg, IpiInnerMsg, IpiPowerMessage, IpiType, PowerEvent}; use crate::kernel::CpuState; use crate::kernel::IpiMessage; use crate::vmm::vmm_reboot; +use crate::kernel::vm; +use smccc::psci::{LowestAffinityLevel, SuspendMode}; +use smccc::{self, Smc}; use super::smc::smc_call; pub const PSCI_VERSION: usize = 0x84000000; -pub const PSCI_MIG_INFO_TYPE: usize = 0x84000006; -pub const PSCI_FEATURES: usize = 0x8400000A; -// pub const PSCI_CPU_SUSPEND_AARCH64: usize = 0xc4000001; -// pub const PSCI_CPU_OFF: usize = 0xc4000002; -pub const PSCI_CPU_ON_AARCH64: usize = 0xc4000003; -pub const PSCI_AFFINITY_INFO_AARCH64: usize = 0xc4000004; +pub const PSCI_CPU_SUSPEND_32: usize = 0x84000001; +pub const PSCI_CPU_SUSPEND_64: usize = 0xC4000001; +pub const PSCI_CPU_OFF: usize = 0x84000002; +pub const PSCI_CPU_ON_32: usize = 0x84000003; +pub const PSCI_CPU_ON_64: usize = 0xC4000003; +pub const PSCI_AFFINITY_INFO_32: usize = 0x84000004; +pub const PSCI_AFFINITY_INFO_64: usize = 0xC4000004; +pub const PSCI_MIGRATE_32: usize = 0x84000005; +pub const PSCI_MIGRATE_64: usize = 0xC4000005; +pub const PSCI_MIGRATE_INFO_TYPE: usize = 0x84000006; +pub const PSCI_MIGRATE_INFO_UP_CPU_32: usize = 0x84000007; +pub const PSCI_MIGRATE_INFO_UP_CPU_64: usize = 0xC4000007; pub const PSCI_SYSTEM_OFF: usize = 0x84000008; pub const PSCI_SYSTEM_RESET: usize = 0x84000009; +pub const PSCI_SYSTEM_RESET2_32: usize = 0x84000012; +pub const PSCI_SYSTEM_RESET2_64: usize = 0xC4000012; +pub const PSCI_MEM_PROTECT: usize = 0x84000013; +pub const PSCI_MEM_PROTECT_CHECK_RANGE_32: usize = 0x84000014; +pub const PSCI_MEM_PROTECT_CHECK_RANGE_64: usize = 0xC4000014; +pub const PSCI_FEATURES: usize = 0x8400000A; +pub const PSCI_CPU_FREEZE: usize = 0x8400000B; +pub const PSCI_CPU_DEFAULT_SUSPEND_32: usize = 0x8400000C; +pub const PSCI_CPU_DEFAULT_SUSPEND_64: usize = 0xC400000C; +pub const PSCI_NODE_HW_STATE_32: usize = 0x8400000D; +pub const PSCI_NODE_HW_STATE_64: usize = 0xC400000D; +pub const PSCI_SYSTEM_SUSPEND_32: usize = 0x8400000E; +pub const PSCI_SYSTEM_SUSPEND_64: usize = 0xC400000E; +pub const PSCI_SET_SUSPEND_MODE: usize = 0x8400000F; +pub const PSCI_STAT_RESIDENCY_32: usize = 0x84000010; +pub const PSCI_STAT_RESIDENCY_64: usize = 0xC4000010; +pub const PSCI_STAT_COUNT_32: usize = 0x84000011; +pub const PSCI_STAT_COUNT_64: usize = 0xC4000011; pub const PSCI_E_SUCCESS: usize = 0; pub const PSCI_E_NOT_SUPPORTED: usize = usize::MAX; @@ -35,16 +63,10 @@ const TEGRA_SIP_GET_ACTMON_CLK_COUNTERS: usize = 0xC2FFFE02; pub const PSCI_TOS_NOT_PRESENT_MP: usize = 2; -pub fn power_arch_init() { - use crate::kernel::ipi_register; - if !ipi_register(IpiType::IpiTPower, psci_ipi_handler) { - panic!("power_arch_init: failed to register ipi IpiTPower"); - } -} - pub fn power_arch_vm_shutdown_secondary_cores(vm: Vm) { let m = IpiPowerMessage { src: vm.id(), + vcpuid: 0, event: PowerEvent::PsciIpiCpuReset, entry: 0, context: 0, @@ -55,11 +77,21 @@ pub fn power_arch_vm_shutdown_secondary_cores(vm: Vm) { } } -pub fn power_arch_cpu_on(mpidr: usize, entry: usize, ctx: usize) -> usize { - // println!("power_arch_cpu_on, {:x}, {:x}, {:x}", PSCI_CPU_ON_AARCH64, mpidr, entry); - let r = smc_call(PSCI_CPU_ON_AARCH64, mpidr, entry, ctx).0; - // println!("smc return val is {}", r); - r +/// # Safety: +/// It attempts to power on the CPU specified by the mpidr parameter. +/// The entry is the vaild entry point of the CPU to be powered on. +/// The ctx here is the cpu_idx. +pub unsafe fn power_arch_cpu_on(mpidr: usize, entry: usize, ctx: usize) -> usize { + use crate::kernel::CPU_IF_LIST; + + let cpu_idx = Platform::mpidr2cpuid(mpidr); + let mut cpu_if_list = CPU_IF_LIST.lock(); + if let Some(cpu_if) = cpu_if_list.get_mut(cpu_idx) { + cpu_if.state_for_start = CpuState::CpuIdle; + } + drop(cpu_if_list); + + smc_call(PSCI_CPU_ON_64, mpidr, entry, ctx).0 } pub fn power_arch_cpu_shutdown() { @@ -69,11 +101,17 @@ pub fn power_arch_cpu_shutdown() { cpu_idle(); } -pub fn power_arch_sys_reset() { +/// # Safety: +/// It attempts to reset the system. +/// So the caller must ensure that the system can be reset. +pub unsafe fn power_arch_sys_reset() { smc_call(PSCI_SYSTEM_RESET, 0, 0, 0); } -pub fn power_arch_sys_shutdown() { +/// # Safety: +/// It attempts to shutdown the system. +/// So the caller must ensure that the system can be shutdown. +pub unsafe fn power_arch_sys_shutdown() { smc_call(PSCI_SYSTEM_OFF, 0, 0, 0); } @@ -87,28 +125,70 @@ pub fn smc_guest_handler(fid: usize, x1: usize, x2: usize, x3: usize) -> bool { "smc_guest_handler: fid 0x{:x}, x1 0x{:x}, x2 0x{:x}, x3 0x{:x}", fid, x1, x2, x3 ); - let r; - match fid { - PSCI_VERSION => { - r = smc_call(PSCI_VERSION, 0, 0, 0).0; + let r = match fid { + PSCI_VERSION => smccc::psci::version::() as usize, + PSCI_CPU_SUSPEND_64 => { + // save the vcpu contex for resume + current_cpu().active_vcpu.clone().unwrap().reset_context(); + current_cpu().active_vcpu.clone().unwrap().set_gpr(0, x3); + current_cpu().active_vcpu.clone().unwrap().set_elr(x2); + current_cpu() + .scheduler() + .sleep(current_cpu().active_vcpu.clone().unwrap()); + PSCI_E_SUCCESS } - PSCI_MIG_INFO_TYPE => { - r = PSCI_TOS_NOT_PRESENT_MP; - } - PSCI_CPU_ON_AARCH64 => { - r = psci_guest_cpu_on(x1, x2, x3); - } - PSCI_AFFINITY_INFO_AARCH64 => { - r = 0; + PSCI_CPU_OFF => match smccc::psci::cpu_off::() { + Ok(()) => PSCI_E_SUCCESS, + _ => PSCI_E_NOT_SUPPORTED, + }, + PSCI_CPU_ON_64 => psci_guest_cpu_on(x1, x2, x3), + PSCI_AFFINITY_INFO_64 => { + let lowest = match x2 { + 0 => LowestAffinityLevel::All, + 1 => LowestAffinityLevel::Aff0Ignored, + 2 => LowestAffinityLevel::Aff0Aff1Ignored, + _ => LowestAffinityLevel::Aff0Aff1Aff2Ignored, + }; + match smccc::psci::affinity_info::(x1 as u64, lowest) { + Ok(affinity_state) => affinity_state as usize, + _ => PSCI_E_NOT_SUPPORTED, + } } + PSCI_MIGRATE_64 => match smccc::psci::migrate::(x1 as u64) { + Ok(()) => PSCI_E_SUCCESS, + _ => PSCI_E_NOT_SUPPORTED, + }, + PSCI_MIGRATE_INFO_TYPE => match smccc::psci::migrate_info_type::() { + Ok(migrate_type) => migrate_type as usize, + _ => PSCI_E_NOT_SUPPORTED, + }, + PSCI_MIGRATE_INFO_UP_CPU_64 => smccc::psci::migrate_info_up_cpu::() as usize, + PSCI_SYSTEM_OFF => match smccc::psci::system_off::() { + Ok(()) => PSCI_E_SUCCESS, + _ => PSCI_E_NOT_SUPPORTED, + }, PSCI_SYSTEM_RESET => { psci_guest_sys_reset(); - r = 0; + match smccc::psci::system_reset::() { + Ok(()) => PSCI_E_SUCCESS, + _ => PSCI_E_NOT_SUPPORTED, + } } + PSCI_SYSTEM_RESET2_64 => match smccc::psci::system_reset2::(x1 as u32, x2 as u64) { + Ok(()) => PSCI_E_SUCCESS, + _ => PSCI_E_NOT_SUPPORTED, + }, + PSCI_MEM_PROTECT => match smccc::psci::mem_protect::(x1 != 0) { + Ok(res) => res as usize, + _ => PSCI_E_NOT_SUPPORTED, + }, + PSCI_MEM_PROTECT_CHECK_RANGE_64 => match smccc::psci::mem_protect_check_range::(x1 as u64, x2 as u64) { + Ok(()) => PSCI_E_SUCCESS, + _ => PSCI_E_NOT_SUPPORTED, + }, #[cfg(feature = "tx2")] TEGRA_SIP_GET_ACTMON_CLK_COUNTERS => { - let result = smc_call(fid, x1, x2, x3); - r = result.0; + let result = unsafe { smc_call(fid, x1, x2, x3) }; // println!("x1 0x{:x}, x2 0x{:x}, x3 0x{:x}", x1, x2, x3); // println!( // "result.0 0x{:x}, result.1 0x{:x}, result.2 0x{:x}", @@ -116,20 +196,45 @@ pub fn smc_guest_handler(fid: usize, x1: usize, x2: usize, x3: usize) -> bool { // ); current_cpu().set_gpr(1, result.1); current_cpu().set_gpr(2, result.2); + result.0 } PSCI_FEATURES => match x1 { - PSCI_VERSION | PSCI_CPU_ON_AARCH64 | PSCI_FEATURES => { - r = PSCI_E_SUCCESS; - } - _ => { - r = PSCI_E_NOT_SUPPORTED; - } + PSCI_VERSION | PSCI_CPU_ON_64 | PSCI_FEATURES => PSCI_E_SUCCESS, + _ => PSCI_E_NOT_SUPPORTED, + }, + PSCI_CPU_FREEZE => match smccc::psci::cpu_freeze::() { + Ok(()) => PSCI_E_SUCCESS, + _ => PSCI_E_NOT_SUPPORTED, }, + PSCI_CPU_DEFAULT_SUSPEND_64 => match smccc::psci::cpu_default_suspend::(x1 as u64, x2 as u64) { + Ok(()) => PSCI_E_SUCCESS, + _ => PSCI_E_NOT_SUPPORTED, + }, + PSCI_NODE_HW_STATE_64 => match smccc::psci::node_hw_state::(x1 as u64, x2 as u32) { + Ok(res) => res as usize, + _ => PSCI_E_NOT_SUPPORTED, + }, + PSCI_SYSTEM_SUSPEND_64 => match smccc::psci::system_suspend::(x1 as u64, x2 as u64) { + Ok(()) => PSCI_E_SUCCESS, + _ => PSCI_E_NOT_SUPPORTED, + }, + PSCI_SET_SUSPEND_MODE => { + let mode = match x1 { + 0 => SuspendMode::PlatformCoordinated, + _ => SuspendMode::OsInitiated, + }; + match smccc::psci::set_suspend_mode::(mode) { + Ok(()) => PSCI_E_SUCCESS, + _ => PSCI_E_NOT_SUPPORTED, + } + } + PSCI_STAT_RESIDENCY_64 => smccc::psci::stat_residency::(x1 as u64, x2 as u32) as usize, + PSCI_STAT_COUNT_64 => smccc::psci::stat_count::(x1 as u64, x2 as u32) as usize, _ => { // unimplemented!(); return false; } - } + }; let idx = 0; let val = r; @@ -139,7 +244,6 @@ pub fn smc_guest_handler(fid: usize, x1: usize, x2: usize, x3: usize) -> bool { } fn psci_vcpu_on(vcpu: Vcpu, entry: usize, ctx: usize) { - // println!("psci vcpu on, entry {:x}, ctx {:x}", entry, ctx); if vcpu.phys_id() != current_cpu().id { panic!( "cannot psci on vcpu on cpu {} by cpu {}", @@ -148,7 +252,6 @@ fn psci_vcpu_on(vcpu: Vcpu, entry: usize, ctx: usize) { ); } current_cpu().cpu_state = CpuState::CpuRun; - // let vcpu = current_cpu().active_vcpu.clone().unwrap(); vcpu.reset_context(); vcpu.set_gpr(0, ctx); vcpu.set_elr(entry); @@ -157,12 +260,27 @@ fn psci_vcpu_on(vcpu: Vcpu, entry: usize, ctx: usize) { // let the scheduler enable or disable timer current_cpu().scheduler().wakeup(vcpu); current_cpu().scheduler().do_schedule(); + + if cfg!(feature = "secondary_start") { + extern "C" { + fn context_vm_entry(ctx: usize) -> !; + } + unsafe { + context_vm_entry(current_cpu().ctx_ptr().unwrap() as usize); + } + } } // Todo: need to support more vcpu in one Core pub fn psci_ipi_handler(msg: &IpiMessage) { match msg.ipi_message { IpiInnerMsg::Power(power_msg) => { + if let PowerEvent::PsciIpiVcpuAssignAndCpuOn = power_msg.event { + trace!("receive PsciIpiVcpuAssignAndCpuOn msg"); + let vm = vm(power_msg.src).unwrap(); + let vcpu = vm.vcpuid_to_vcpu(power_msg.vcpuid).unwrap(); + current_cpu().vcpu_array.append_vcpu(vcpu); + } let trgt_vcpu = match current_cpu().vcpu_array.pop_vcpu_through_vmid(power_msg.src) { None => { warn!( @@ -176,7 +294,7 @@ pub fn psci_ipi_handler(msg: &IpiMessage) { }; match power_msg.event { PowerEvent::PsciIpiCpuOn => { - if trgt_vcpu.state() as usize != VcpuState::VcpuInv as usize { + if trgt_vcpu.state() as usize != VcpuState::Invalid as usize { warn!( "psci_ipi_handler: target VCPU {} in VM {} is already running", trgt_vcpu.id(), @@ -201,6 +319,7 @@ pub fn psci_ipi_handler(msg: &IpiMessage) { PowerEvent::PsciIpiCpuReset => { vcpu_arch_init(active_vm().unwrap(), current_cpu().active_vcpu.clone().unwrap()); } + _ => {} } } _ => { @@ -212,8 +331,70 @@ pub fn psci_ipi_handler(msg: &IpiMessage) { } } -pub fn psci_guest_cpu_on(mpidr: usize, entry: usize, ctx: usize) -> usize { - let vcpu_id = mpidr & 0xff; +#[cfg(feature = "secondary_start")] +pub fn psci_guest_cpu_on(vmpidr: usize, entry: usize, ctx: usize) -> usize { + let vcpu_id = Platform::vmpidr2vcpuid(vmpidr); + let vm = active_vm().unwrap(); + let physical_linear_id = vm.vcpuid_to_pcpuid(vcpu_id); + + if vcpu_id >= vm.cpu_num() || physical_linear_id.is_err() { + warn!("psci_guest_cpu_on: target vcpu {} not exist", vcpu_id); + return usize::MAX - 1; + } + + let cpu_idx = physical_linear_id.unwrap(); + + debug!("[psci_guest_cpu_on] {vmpidr}, {cpu_idx}"); + + use crate::kernel::CPU_IF_LIST; + use crate::kernel::StartReason; + let state = CPU_IF_LIST.lock().get(cpu_idx).unwrap().state_for_start; + let mut r = 0; + if state == CpuState::CpuInv { + let mut cpu_if_list = CPU_IF_LIST.lock(); + if let Some(cpu_if) = cpu_if_list.get_mut(cpu_idx as usize) { + cpu_if.ctx = ctx as u64; + cpu_if.entry = entry as u64; + cpu_if.vm_id = vm.id(); + cpu_if.state_for_start = CpuState::CpuIdle; + cpu_if.vcpuid = vcpu_id; + cpu_if.start_reason = StartReason::SecondaryCore; + } + drop(cpu_if_list); + let mpidr = Platform::cpuid2mpidr(cpu_idx); + + let entry_point = crate::arch::_secondary_start as usize; + // SAFETY: + // It attempts to power on the CPU specified by the mpidr parameter. + // The entry is the address of function _secondary_start. + // The ctx here is the cpu_idx. + unsafe { + r = power_arch_cpu_on(mpidr, entry_point, cpu_idx); + } + debug!( + "start to power_arch_cpu_on! mpidr={:X}, entry_point={:X}", + mpidr, entry_point + ); + } else { + let m = IpiPowerMessage { + src: vm.id(), + vcpuid: vcpu_id, + event: PowerEvent::PsciIpiVcpuAssignAndCpuOn, + entry, + context: ctx, + }; + + if !ipi_send_msg(physical_linear_id.unwrap(), IpiType::IpiTPower, IpiInnerMsg::Power(m)) { + warn!("psci_guest_cpu_on: fail to send msg"); + return usize::MAX - 1; + } + } + r +} + +#[cfg(not(feature = "secondary_start"))] +pub fn psci_guest_cpu_on(vmpidr: usize, entry: usize, ctx: usize) -> usize { + let vcpu_id = Platform::vmpidr2vcpuid(vmpidr); let vm = active_vm().unwrap(); let physical_linear_id = vm.vcpuid_to_pcpuid(vcpu_id); @@ -223,7 +404,7 @@ pub fn psci_guest_cpu_on(mpidr: usize, entry: usize, ctx: usize) -> usize { } #[cfg(feature = "tx2")] { - let cluster = (mpidr >> 8) & 0xff; + let cluster = (vmpidr >> 8) & 0xff; if vm.id() == 0 && cluster != 1 { warn!("psci_guest_cpu_on: L4T only support cluster #1"); return usize::MAX - 1; @@ -232,6 +413,7 @@ pub fn psci_guest_cpu_on(mpidr: usize, entry: usize, ctx: usize) -> usize { let m = IpiPowerMessage { src: vm.id(), + vcpuid: 0, event: PowerEvent::PsciIpiCpuOn, entry, context: ctx, @@ -244,3 +426,104 @@ pub fn psci_guest_cpu_on(mpidr: usize, entry: usize, ctx: usize) -> usize { 0 } + +pub fn psci_vm_maincpu_on(vmpidr: usize, entry: usize, ctx: usize, vm_id: usize) -> usize { + let vcpu_id = Platform::vmpidr2vcpuid(vmpidr); + let vm = vm(vm_id).unwrap(); + let physical_linear_id = vm.vcpuid_to_pcpuid(vcpu_id); + + if vcpu_id >= vm.cpu_num() || physical_linear_id.is_err() { + warn!("psci_vm_maincpu_on: target vcpu {} not exist", vcpu_id); + return usize::MAX - 1; + } + + let cpu_idx = physical_linear_id.unwrap(); + + use crate::kernel::{CPU_IF_LIST, StartReason}; + let state = CPU_IF_LIST.lock().get(cpu_idx).unwrap().state_for_start; + let mut r = 0; + if state == CpuState::CpuInv { + let mut cpu_if_list = CPU_IF_LIST.lock(); + if let Some(cpu_if) = cpu_if_list.get_mut(cpu_idx as usize) { + cpu_if.ctx = ctx as u64; + cpu_if.entry = entry as u64; + cpu_if.vm_id = vm.id(); + cpu_if.state_for_start = CpuState::CpuIdle; + cpu_if.vcpuid = vcpu_id; + cpu_if.start_reason = StartReason::MainCore; + } + drop(cpu_if_list); + + let mpidr = Platform::cpuid2mpidr(cpu_idx); + + let entry_point = crate::arch::_secondary_start as usize; + // SAFETY: + // It attempts to power on the CPU specified by the mpidr parameter. + // The entry is the address of function _secondary_start. + // The ctx here is the cpu_idx. + r = unsafe { power_arch_cpu_on(mpidr, entry_point, cpu_idx) }; + debug!( + "start to power_arch_cpu_on! mpidr={:X}, entry_point={:X}", + mpidr, entry_point + ); + } else { + let m = IpiPowerMessage { + src: vm.id(), + vcpuid: vcpu_id, + event: PowerEvent::PsciIpiVcpuAssignAndCpuOn, + entry, + context: ctx, + }; + + if !ipi_send_msg(physical_linear_id.unwrap(), IpiType::IpiTPower, IpiInnerMsg::Power(m)) { + warn!("psci_guest_cpu_on: fail to send msg"); + return usize::MAX - 1; + } + } + + r +} + +#[cfg(feature = "secondary_start")] +pub fn guest_cpu_on(mpidr: usize) { + let cpu_id = Platform::mpidr2cpuid(mpidr); + + use crate::kernel::{CPU_IF_LIST, StartReason}; + let cpu_if_list = CPU_IF_LIST.lock(); + let cpu_if = &cpu_if_list[cpu_id]; + + let vm_id = cpu_if.vm_id; + let entry = cpu_if.entry; + let ctx = cpu_if.ctx; + let vcpuid = cpu_if.vcpuid; + let start_reason = cpu_if.start_reason; + + drop(cpu_if_list); + + let vm = vm(vm_id).unwrap(); + let vcpu = vm.vcpuid_to_vcpu(vcpuid).unwrap(); + + debug!("now add vcpu={} to mpidr={:X}", vcpu.id(), mpidr); + + current_cpu().vcpu_array.append_vcpu(vcpu); + + match start_reason { + StartReason::MainCore => { + use crate::vmm::vmm_boot_vm; + vmm_boot_vm(vm_id); + } + StartReason::SecondaryCore => { + if let Some(trgt_vcpu) = current_cpu().vcpu_array.pop_vcpu_through_vmid(vm_id) { + psci_vcpu_on(trgt_vcpu, entry as usize, ctx as usize); + } else { + error!("pop_vcpu_through_vmid error!"); + } + } + _ => { + todo!() + } + } +} + +#[cfg(not(feature = "secondary_start"))] +pub fn guest_cpu_on(_mpidr: usize) {} diff --git a/src/arch/aarch64/regs.rs b/src/arch/aarch64/regs.rs index 4d1eb03b6835a392c7fa0265227ae30c16f24c63..05d2052ba8c5aab5bd4c97cc8fae66548575dac0 100644 --- a/src/arch/aarch64/regs.rs +++ b/src/arch/aarch64/regs.rs @@ -8,15 +8,60 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use alloc::fmt; + +pub trait SysReg { + // Register value type. + type Value: Copy + Eq + fmt::LowerHex + TryFrom; + // Register name. + const NAME: &'static str; +} + +// Writeable system register. +pub trait WriteableReg: SysReg { + // Write value to the register.. + /// # Safety: + /// The caller must have enough privileges to operate on the register and ensure the value is valid + unsafe fn write(val: Self::Value); +} + +// Readable system register. +pub trait ReadableReg: SysReg { + // Read register value. + // no need to guarantee safety + fn read() -> Self::Value; +} + // Move to ARM register from system coprocessor register. // MRS Xd, sysreg "Xd = sysreg" +// It is a safe macro. +// Because the read behavior of the system register can't trigger any side effects. macro_rules! mrs { - ($val: expr, $reg: expr, $asm_width:tt) => { + ($reg: ident) => { + { + let r; + unsafe { + // TODO: If use 'nomem' option, the system will staff may beacuse of no assign type here + core::arch::asm!(concat!("mrs {0}, ", stringify!($reg)), out(reg) r, options(pure,readonly, nostack)); + } + r + } + }; + ($reg: ident, "x") => { + { + let r: u32; + unsafe { + core::arch::asm!(concat!("mrs {0:x}, ", stringify!($reg)), out(reg) r, options(nomem, nostack)); + } + r + } + }; + ($val: expr, $reg: ident, $asm_width:literal) => { unsafe { core::arch::asm!(concat!("mrs {0:", $asm_width, "}, ", stringify!($reg)), out(reg) $val, options(nomem, nostack)); } }; - ($val: expr, $reg: expr) => { + ($val: expr, $reg: ident) => { unsafe { core::arch::asm!(concat!("mrs {0}, ", stringify!($reg)), out(reg) $val, options(nomem, nostack)); } @@ -25,15 +70,317 @@ macro_rules! mrs { // Move to system coprocessor register from ARM register. // MSR sysreg, Xn "sysreg = Xn" +/// # Safety: +/// It is a unsafe marco. The caller must ensure that the value is valid for the register. macro_rules! msr { - ($reg: expr, $val: expr, $asm_width:tt) => { - unsafe { - core::arch::asm!(concat!("msr ", stringify!($reg), ", {0:", $asm_width, "}"), in(reg) $val, options(nomem, nostack)); + ($reg: ident, $val: expr, $asm_width:tt) => { + unsafe{core::arch::asm!(concat!("msr ", stringify!($reg), ", {0:", $asm_width, "}"), in(reg) $val, options(nomem, nostack));} + }; + ($reg: ident, $val: expr) => { + unsafe{core::arch::asm!(concat!("msr ", stringify!($reg), ", {0:x}"), in(reg) $val, options(nomem, nostack));} + }; +} + +// Declare a system register. +macro_rules! sysreg_declare { + ($name: ident, $type:ty) => { + #[allow(non_camel_case_types)] + pub struct $name; + + impl SysReg for $name { + type Value = $type; + const NAME: &'static str = stringify!($name); } }; - ($reg: expr, $val: expr) => { - unsafe { - core::arch::asm!(concat!("msr ", stringify!($reg), ", {0}"), in(reg) $val, options(nomem, nostack)); +} + +// Only used for Readable system register. +macro_rules! sysreg_impl_read { + ($definename:ident, $asmname:ident) => { + impl ReadableReg for $definename { + #[inline] + fn read() -> Self::Value { + mrs!($asmname) + } + } + }; +} + +// Only used for Writeable system register. +macro_rules! sysreg_impl_write { + ($definename:ident, $asmname:ident) => { + impl WriteableReg for $definename { + #[inline] + unsafe fn write(val: Self::Value) { + msr!($asmname, val) + } } }; } + +// Define a Read-only system register so that it will panic when written. +macro_rules! define_sysreg_ro { + ($definename:ident, $type:ty,$asmname:ident) => { + sysreg_declare!($definename, $type); + sysreg_impl_read!($definename, $asmname); + }; + ($definename:ident, $type:ty) => { + define_sysreg_ro!($definename, $type, $definename); + }; + ($definename:ident) => { + define_sysreg_ro!($definename, usize); + }; +} + +// Define a Write-only system register so that it will panic when reading. +macro_rules! define_sysreg_wo { + ($definename:ident, $type:ty, $asmname:ident) => { + sysreg_declare!($definename, $type); + sysreg_impl_write!($definename, $asmname); + }; + ($definename:ident, $type:ty) => { + define_sysreg_wo!($definename, $type, $definename); + }; + ($definename:ident) => { + define_sysreg_wo!($definename, usize); + }; +} + +macro_rules! define_sysreg { + ($definename:ident, $type:ty, $asmname:ident) => { + sysreg_declare!($definename, $type); + sysreg_impl_read!($definename, $asmname); + sysreg_impl_write!($definename, $asmname); + }; + ($definename:ident, $type:ty) => { + define_sysreg!($definename, $type, $definename); + }; + ($definename:ident) => { + define_sysreg!($definename, usize); + }; +} + +macro_rules! sysop { + // # Safety: + // It is a unsafe marco.The caller must ensure that the behavior won't + // trigger any side effects. + ($instr:ident) => { + core::arch::asm!( + stringify!($instr), + options(nomem, nostack) + ) + }; + // # Safety: + // It is a unsafe marco.The caller must ensure that the behavior won't + // trigger any side effects. + ($instr:ident, $mode:ident) => { + core::arch::asm!( + concat!(stringify!($instr), " ", stringify!($mode)), + options(nomem, nostack) + ) + }; + // # Safety: + // It is a unsafe marco.The caller must ensure that the behavior won't + // trigger any side effects. + // And the args must be a valid value for the instruction. + ($instr:ident, $mode:ident, $args:expr) => { + core::arch::asm!( + concat!(stringify!($instr), " ", stringify!($mode), ", {0} "), + in(reg) $args, + options(nomem, nostack) + ) + }; +} + +macro_rules! define_sysop { + // SAFETY: + // sysop without any passed value can't trigger any side effects. + ($terms:ident) => { + #[doc=concat!("System operation: ", stringify!($terms))] + pub fn $terms() { + unsafe { + sysop!($terms); + } + } + }; +} + +// Data Store Barrier ('DSB') instructions. +// SAFETY: +// DSB can't trigger any side effects. +// So it is a safe marco. +pub mod dsb { + macro_rules! define_dsb { + ($mode:ident) => { + pub fn $mode() { + unsafe { + sysop!(dsb, $mode); + } + } + }; + } + + define_dsb!(oshld); + define_dsb!(oshst); + define_dsb!(osh); + define_dsb!(nshld); + define_dsb!(nshst); + define_dsb!(nsh); + define_dsb!(ishld); + define_dsb!(ishst); + define_dsb!(ish); + define_dsb!(ld); + define_dsb!(st); + define_dsb!(sy); +} + +// Data Synchronization Barrier ('DSB') instructions. +// SAFETY: +// DSB can't trigger any side effects. +pub mod dmb { + macro_rules! define_dmb { + ($mode:ident) => { + pub fn $mode() { + unsafe { + sysop!(dmb, $mode); + } + } + }; + } + + define_dmb!(oshld); + define_dmb!(oshst); + define_dmb!(osh); + define_dmb!(nshld); + define_dmb!(nshst); + define_dmb!(nsh); + define_dmb!(ishld); + define_dmb!(ishst); + define_dmb!(ish); + define_dmb!(ld); + define_dmb!(st); + define_dmb!(sy); +} + +// Address Translation ('AT') Instructions. +pub mod at { + macro_rules! define_at { + ($mode:ident) => { + pub fn $mode(va: usize) { + unsafe { + sysop!(at, $mode, va as u64); + } + } + }; + } + + define_at!(s1e1r); +} + +// Define a system with RW access. +define_sysreg!(DAIF, u64); +define_sysreg!(CNTVOFF_EL2, u64); //Counter-timer Virtual Offset register +define_sysreg!(CNTV_CVAL_EL0, u64); //Counter-timer Virtual Timer CompareValue register +define_sysreg!(CNTKCTL_EL1); //Counter-timer Kernel Control register +define_sysreg!(CNTP_CTL_EL0); // Counter-timer Physical Timer Control register +define_sysreg!(CNTV_CTL_EL0); // Counter-timer Virtual Timer Control register +define_sysreg!(CNTP_TVAL_EL0); // Counter-timer Physical Timer TimerValue register +define_sysreg!(CNTV_TVAL_EL0); // Counter-timer Virtual Timer TimerValue register +define_sysreg!(CNTVCT_EL0, u64); // Counter-timer Virtual Count register +define_sysreg!(VMPIDR_EL2, u64); // Virtualization Multiprocessor ID Register +define_sysreg!(SP_EL0, u64); // Stack Pointer EL0 +define_sysreg!(SP_EL1, u64); // Stack Pointer EL1 +define_sysreg!(ELR_EL1, u64); // Exception Link Register EL1 +define_sysreg!(ELR_EL2, u64); // Exception Link Register EL2 +define_sysreg!(SPSR_EL1); // Saved Program Status Register EL1 +define_sysreg!(SPSR_EL2); // Saved Program Status Register EL2 +define_sysreg!(SCTLR_EL1); // System Control Register EL1 +define_sysreg!(CPACR_EL1); // Architectural Feature Access Control Register EL1 +define_sysreg!(TTBR0_EL1, u64); // Translation Table Base Register 0 EL1 +define_sysreg!(TTBR1_EL1, u64); // Translation Table Base Register 1 EL1 +define_sysreg!(TCR_EL1, u64); // Translation Control Register EL1 +define_sysreg!(ESR_EL1); // Exception Syndrome Register EL1 +define_sysreg!(ESR_EL2); // Exception Syndrome Register EL2 +define_sysreg!(FAR_EL1, u64); // Fault Address Register EL1 +define_sysreg!(FAR_EL2, u64); // Fault Address Register EL2 +define_sysreg!(MAIR_EL1, u64); // Memory Attribute Indirection Register EL1 +define_sysreg!(MAIR_EL2, u64); // Memory Attribute Indirection Register EL2 +define_sysreg!(AMAIR_EL1, u64); // Auxiliary Memory Attribute Indirection Register EL1 +define_sysreg!(AMAIR_EL2, u64); // Auxiliary Memory Attribute Indirection Register EL2 +define_sysreg!(VBAR_EL1, u64); // Vector Base Address Register EL1 +define_sysreg!(VBAR_EL2, u64); // Vector Base Address Register EL2 +define_sysreg!(PAR_EL1, u64); // Physical Address Register EL1 +define_sysreg!(PAR_EL2, u64); // Physical Address Register EL2 +define_sysreg!(TPIDR_EL0, u64); // Software Thread ID Register EL0 +define_sysreg!(TPIDR_EL1, u64); // Software Thread ID Register EL1 +define_sysreg!(TPIDR_EL2, u64); // Software Thread ID Register EL2 +define_sysreg!(SPSEL, u64); // Stack Pointer Select +define_sysreg!(CONTEXTIDR_EL1); // Context ID Register EL1 +define_sysreg!(PMCR_EL0, u64); // Performance Monitors Control Register EL0 +define_sysreg!(VTCR_EL2, u64); // Virtualization Translation Control Register EL2 +define_sysreg!(HCR_EL2, u64); // Hypervisor Configuration Register EL2 +define_sysreg!(ACTLR_EL1, u64); // Auxiliary Control Register EL1 +define_sysreg!(ACTLR_EL2, u64); // Auxiliary Control Register EL2 +define_sysreg!(HPFAR_EL2, u64); // Hypervisor IPA Fault Address Register EL2 +define_sysreg!(AFSR0_EL1, u64); // Auxiliary Fault Status Register 0 EL1 +define_sysreg!(CNTHP_TVAL_EL2); // Counter-timer Hypervisor Physical Timer TimerValue register +define_sysreg!(CNTHP_CTL_EL2); // Counter-timer Hypervisor Physical Timer Control register +define_sysreg!(VTTBR_EL2); // Virtualization Translation Table Base Register EL2 + // define GIC_V3 system register +define_sysreg!(ICH_HCR_EL2); // Interrupt Controller Hypervisor Control Register +define_sysreg!(ICC_SRE_EL2); // Interrupt Controller System Register Enable Register +define_sysreg!(ICC_SRE_EL1); // Interrupt Controller System Register Enable Register +define_sysreg!(ICH_VMCR_EL2); // Interrupt Controller Virtual Machine Control Register +define_sysreg!(ICH_AP0R2_EL2); // Interrupt Controller Active Priorities Group 0 Register 2 +define_sysreg!(ICH_AP0R1_EL2); // Interrupt Controller Active Priorities Group 0 Register 1 +define_sysreg!(ICH_AP0R0_EL2); // Interrupt Controller Active Priorities Group 0 Register 0 +define_sysreg!(ICH_AP1R2_EL2); // Interrupt Controller Active Priorities Group 1 Register 2 +define_sysreg!(ICH_AP1R1_EL2); // Interrupt Controller Active Priorities Group 1 Register 1 +define_sysreg!(ICH_AP1R0_EL2); // Interrupt Controller Active Priorities Group 1 Register 0 +define_sysreg!(ICC_PMR_EL1); // Interrupt Controller Interrupt Priority Mask Register +define_sysreg!(ICC_BPR1_EL1); // Interrupt Controller Binary Point Register 1 +define_sysreg!(ICC_CTLR_EL1); // Interrupt Controller Control Register +define_sysreg!(ICC_IGRPEN1_EL1); // Interrupt Controller Interrupt Group 1 Enable register +define_sysreg!(ICC_EOIR1_EL1); // Interrupt Controller End Of Interrupt Register 1 +define_sysreg!(ICC_DIR_EL1); // Interrupt Controller Deactivate Interrupt Register +define_sysreg!(ICH_ELRSR_EL2); // Interrupt Controller Empty List Register Status Register +define_sysreg!(ICH_LR0_EL2); // Interrupt Controller List Register 0 +define_sysreg!(ICH_LR1_EL2); // Interrupt Controller List Register 1 +define_sysreg!(ICH_LR2_EL2); // Interrupt Controller List Register 2 +define_sysreg!(ICH_LR3_EL2); // Interrupt Controller List Register 3 +define_sysreg!(ICH_LR4_EL2); // Interrupt Controller List Register 4 +define_sysreg!(ICH_LR5_EL2); // Interrupt Controller List Register 5 +define_sysreg!(ICH_LR6_EL2); // Interrupt Controller List Register 6 +define_sysreg!(ICH_LR7_EL2); // Interrupt Controller List Register 7 +define_sysreg!(ICH_LR8_EL2); // Interrupt Controller List Register 8 +define_sysreg!(ICH_LR9_EL2); // Interrupt Controller List Register 9 +define_sysreg!(ICH_LR10_EL2); // Interrupt Controller List Register 10 +define_sysreg!(ICH_LR11_EL2); // Interrupt Controller List Register 11 +define_sysreg!(ICH_LR12_EL2); // Interrupt Controller List Register 12 +define_sysreg!(ICH_LR13_EL2); // Interrupt Controller List Register 13 +define_sysreg!(ICH_LR14_EL2); // Interrupt Controller List Register 14 +define_sysreg!(ICH_LR15_EL2); // Interrupt Controller List Register 15 + +// Define a system with RO access. +define_sysreg_ro!(TPIDRRO_EL0, u64); // Software Thread ID Register EL0 Read-Only +define_sysreg_ro!(MPIDR_EL1, u64); // Multiprocessor Affinity Register EL1 +define_sysreg_ro!(ICC_IAR1_EL1); // Interrupt Controller Interrupt Acknowledge Register 1 +define_sysreg_ro!(ICH_EISR_EL2); // Interrupt Controller Empty Interrupt Status Register +define_sysreg_ro!(ICH_MISR_EL2); // Interrupt Controller Maintenance Interrupt Status Register +define_sysreg_ro!(ICH_VTR_EL2); // Interrupt Controller Virtualization Type Register + +// Define a system with WO access. +define_sysreg_wo!(OSLAR_EL1); // OS Lock Access Register EL1 +define_sysreg_wo!(ICC_SGI1R_EL1, u64); // Interrupt Controller System Register + +// Define a system operation. +define_sysop!(wfi); +define_sysop!(wfe); +define_sysop!(sev); +define_sysop!(sevl); +define_sysop!(isb); + +#[inline(always)] +pub const fn sysreg_enc_addr(op0: usize, op1: usize, crn: usize, crm: usize, op2: usize) -> usize { + (((op0) & 0x3) << 20) | (((op2) & 0x7) << 17) | (((op1) & 0x7) << 14) | (((crn) & 0xf) << 10) | (((crm) & 0xf) << 1) +} diff --git a/src/arch/aarch64/smc.rs b/src/arch/aarch64/smc.rs index e508029a3300ce6c1eb00183b03a88b2875eaaf8..838646962e94be74a52db511b0aa44caf1d23ff4 100644 --- a/src/arch/aarch64/smc.rs +++ b/src/arch/aarch64/smc.rs @@ -11,9 +11,15 @@ use core::arch::asm; #[inline(never)] -pub fn smc_call(x0: usize, x1: usize, x2: usize, x3: usize) -> (usize, usize, usize, usize) { +/// invoke a secure monitor call +/// # Safety: +/// It is unsafe to call this function directly. +/// The caller must ensure that +/// x0 is defined as the SMC function number referenced in the SMC Calling Convention +/// than the args later must be valid for the specified SMC function. +pub unsafe fn smc_call(x0: usize, x1: usize, x2: usize, x3: usize) -> (usize, usize, usize, usize) { #[cfg(target_arch = "aarch64")] - unsafe { + { let r0; let r1; let r2; diff --git a/src/arch/aarch64/smmu.rs b/src/arch/aarch64/smmu.rs index d57b4edc91380065df97b20dea45217745d0867e..88265308de936a0277d6a1b66da6d91a23c113ab 100644 --- a/src/arch/aarch64/smmu.rs +++ b/src/arch/aarch64/smmu.rs @@ -22,7 +22,7 @@ use crate::device::EmuContext; use crate::kernel::VM_NUM_MAX; use crate::kernel::Vm; use crate::kernel::{active_vm, active_vm_id, current_cpu}; -use crate::lib::{bit_extract, FlexBitmap}; +use crate::utils::{bit_extract, FlexBitmap, device_ref::DeviceRef}; pub struct SmmuDesc { pub base: usize, @@ -67,7 +67,8 @@ const SMMUV2_TCR_TG0_4K: usize = 0; const SMMUV2_TCR_IRGN0_WB_RA_WA: usize = 1 << 8; const SMMUV2_TCR_ORGN0_WB_RA_WA: usize = 1 << 10; const SMMUV2_TCR_SH0_IS: usize = 0x3 << 12; -const SMMUV2_TCR_SL0_1: usize = 0x1 << 6; +const SMMUV2_TCR_SL0_12: usize = 0x1 << 6; +const SMMUV2_TCR_SL0_01: usize = 0x2 << 6; const SMMUV2_SCTLR_CFIE: usize = 1 << 6; const SMMUV2_SCTLR_CFRE: usize = 1 << 5; @@ -96,7 +97,7 @@ macro_rules! bit_mask { register_structs! { #[allow(non_snake_case)] - pub SmmuGlobalRegisterSpace0 { + SmmuGlbRS0 { (0x0000 => CR0: ReadWrite), (0x0004 => SCR1: ReadWrite), (0x0008 => CR2: ReadWrite), @@ -181,31 +182,12 @@ register_structs! { } } -#[derive(Clone, Copy)] -pub struct SmmuGlbRS0 { - base_addr: usize, -} - -impl core::ops::Deref for SmmuGlbRS0 { - type Target = SmmuGlobalRegisterSpace0; - fn deref(&self) -> &Self::Target { - unsafe { &*self.ptr() } - } -} - -impl SmmuGlbRS0 { - const fn new(base_addr: usize) -> SmmuGlbRS0 { - SmmuGlbRS0 { base_addr } - } - - pub fn ptr(&self) -> *const SmmuGlobalRegisterSpace0 { - self.base_addr as *const SmmuGlobalRegisterSpace0 - } -} +// SAFETY: SmmuGlbRS0 is a MMIO device. +unsafe impl Sync for SmmuGlbRS0 {} register_structs! { #[allow(non_snake_case)] - pub SmmuGlobalRegisterSpace1 { + pub SmmuGlbRS1 { (0x0000 => CBAR: [ReadWrite; 128]), (0x0200 => reserved_0), (0x0400 => CBFRSYNRA: [ReadWrite; 128]), @@ -216,31 +198,12 @@ register_structs! { } } -#[derive(Clone, Copy)] -pub struct SmmuGlbRS1 { - base_addr: usize, -} - -impl core::ops::Deref for SmmuGlbRS1 { - type Target = SmmuGlobalRegisterSpace1; - fn deref(&self) -> &Self::Target { - unsafe { &*self.ptr() } - } -} - -impl SmmuGlbRS1 { - const fn new(base_addr: usize) -> SmmuGlbRS1 { - SmmuGlbRS1 { base_addr } - } - - pub fn ptr(&self) -> *const SmmuGlobalRegisterSpace1 { - self.base_addr as *const SmmuGlobalRegisterSpace1 - } -} +//SAFETY: SmmuGlbRS1 is a MMIO device. +unsafe impl Sync for SmmuGlbRS1 {} register_structs! { #[allow(non_snake_case)] - pub SmmuStage2TranslationContextBankAddressSpace { + pub SmmuContextBank { (0x0000 => SCTLR: ReadWrite), (0x0004 => ACTLR: ReadWrite), (0x0008 => RESUME: WriteOnly), @@ -262,9 +225,9 @@ register_structs! { (0x07F0 => TLBSYNC: WriteOnly), (0x07F4 => TLBSTATUS: ReadOnly), (0x07F8 => reserved_5), - (0x0e00 => PMEVCNTRm: ReadWrite), // what about 0xe04~0xe38 + (0x0e00 => PMEVCNTR: [ReadWrite; 15]), (0x0e3c => reserved_6), - (0x0e80 => PMEVTYPERm: ReadWrite), // what about 0xe84~0xeB8 + (0x0e80 => PMEVTYPER: [ReadWrite; 15]), (0x0ebc => reserved_7), (0x0f00 => PMCFGR: ReadOnly), (0x0f04 => PMCR: ReadWrite), @@ -286,59 +249,55 @@ register_structs! { } } -#[derive(Clone, Copy)] -pub struct SmmuContextBank { - base_addr: usize, -} +// SAFETY: SmmuContextBank is a MMIO device. +unsafe impl Sync for SmmuContextBank {} -impl core::ops::Deref for SmmuContextBank { - type Target = SmmuStage2TranslationContextBankAddressSpace; - fn deref(&self) -> &Self::Target { - unsafe { &*self.ptr() } - } -} - -impl SmmuContextBank { - pub const fn new(base_addr: usize) -> SmmuContextBank { - SmmuContextBank { base_addr } - } +struct SmmuV2 { + glb_rs0: &'static SmmuGlbRS0, + glb_rs1: &'static SmmuGlbRS1, + context_s2_idx: usize, + context_bank: Vec>, + context_alloc_bitmap: Option, - pub fn ptr(&self) -> *const SmmuStage2TranslationContextBankAddressSpace { - self.base_addr as *const SmmuStage2TranslationContextBankAddressSpace - } + smr_num: usize, + smr_alloc_bitmap: Option, + group_alloc_bitmap: Option, } -pub struct SmmuV2 { - pub glb_rs0: Option, - pub glb_rs1: Option, - pub context_s2_idx: usize, - pub context_bank: Vec, - pub context_alloc_bitmap: Option, +impl SmmuV2 { + fn new() -> Self { + let smmu_base_addr = PLAT_DESC.arch_desc.smmu_desc.base; - pub smr_num: usize, - pub smr_alloc_bitmap: Option, - pub group_alloc_bitmap: Option, -} + // SAFETY: The reference of glb_rs0 is a MMIO address + let glb_rs0 = unsafe { &*(smmu_base_addr as *const SmmuGlbRS0) }; + /* IDR1 */ + let idr1 = glb_rs0.IDR1.get() as usize; + let page_size = if (idr1 & SMMUV2_IDR1_PAGESIZE_BIT) == 0 { + 0x1000 + } else { + 0x10000 + }; -impl SmmuV2 { - pub const fn new() -> SmmuV2 { - SmmuV2 { - glb_rs0: None, - glb_rs1: None, + // SAFETY: The reference of glb_rs1 is a MMIO address + let glb_rs1 = unsafe { &*((smmu_base_addr + page_size) as *const SmmuGlbRS1) }; + let mut r = Self { + glb_rs0, + glb_rs1, context_s2_idx: 0, context_bank: vec![], context_alloc_bitmap: None, smr_num: 0, smr_alloc_bitmap: None, group_alloc_bitmap: None, - } + }; + r.init(); + r } - pub fn init(&mut self) { - let smmu_base_addr = PLAT_DESC.arch_desc.smmu_desc.base + 0x8_0000_0000; + fn init(&mut self) { + let smmu_base_addr = PLAT_DESC.arch_desc.smmu_desc.base; - self.glb_rs0 = Some(SmmuGlbRS0::new(smmu_base_addr)); - let rs0 = self.glb_rs0.as_ref().unwrap(); + let rs0 = self.glb_rs0; /* IDR1 */ let idr1 = rs0.IDR1.get() as usize; let page_size = if (idr1 & SMMUV2_IDR1_PAGESIZE_BIT) == 0 { @@ -346,14 +305,13 @@ impl SmmuV2 { } else { 0x10000 }; - - self.glb_rs1 = Some(SmmuGlbRS1::new(smmu_base_addr + page_size)); let num_pages = 1 << (1 + bit_extract(idr1, SMMUV2_IDR1_NUMPAGEDXB_OFF, SMMUV2_IDR1_NUMPAGEDXB_LEN)); let context_bank_num = bit_extract(idr1, SMMUV2_IDR1_NUMCB_OFF, SMMUV2_IDR1_NUMCB_LEN); let context_base = smmu_base_addr + num_pages * page_size; for i in 0..context_bank_num { + // SAFETY: The reference of Context bank is a MMIO address self.context_bank - .push(SmmuContextBank::new(context_base + page_size * i)); + .push(unsafe { DeviceRef::new((context_base + page_size * i) as *const _) }); } let stage2_context_bank_num = bit_extract(idr1, SMMUV2_IDR1_NUMS2CB_OFF, SMMUV2_IDR1_NUMS2CB_LEN); // TODO: not a good way to seperate context bank into 2 parts, @@ -374,7 +332,7 @@ impl SmmuV2 { rs0.GFSR.set(rs0.GFSR.get()); rs0.NSGFSR.set(rs0.NSGFSR.get()); - println!( + info!( concat!( "SMMU info:\n", " page size {:#x}, num pages {}, context base {:#x}\n", @@ -402,8 +360,8 @@ impl SmmuV2 { rs0.CR0.set(cr0 as u32); } - pub fn check_features(&self) { - let glb_rs0 = self.glb_rs0.as_ref().unwrap(); + fn check_features(&self) { + let glb_rs0 = self.glb_rs0; let version = bit_extract(glb_rs0.IDR7.get() as usize, 4, 4); if version != 2 { panic!("smmu unspoorted version: {}", version); @@ -411,10 +369,8 @@ impl SmmuV2 { if glb_rs0.IDR0.get() as usize & SMMUV2_IDR0_S2TS_BIT == 0 { panic!("smmuv2 does not support 2nd stage translation"); - } else { - if glb_rs0.IDR0.get() as usize & SMMUV2_IDR0_NTS_BIT == 0 { - panic!("smmuv2 does not support Nested Translation (Stage 1 followed by stage 2 translation)"); - } + } else if glb_rs0.IDR0.get() as usize & SMMUV2_IDR0_NTS_BIT == 0 { + panic!("smmuv2 does not support Nested Translation (Stage 1 followed by stage 2 translation)"); } if glb_rs0.IDR0.get() as usize & SMMUV2_IDR0_SMS_BIT == 0 { @@ -459,43 +415,35 @@ impl SmmuV2 { #[inline] fn smr_get_context(&self, smr: usize) -> usize { - bit_extract( - self.glb_rs0.as_ref().unwrap().S2CR[smr].get() as usize, - S2CR_CBNDX_OFF, - S2CR_CBNDX_LEN, - ) + bit_extract(self.glb_rs0.S2CR[smr].get() as usize, S2CR_CBNDX_OFF, S2CR_CBNDX_LEN) } #[inline] fn smr_get_id(&self, smr: usize) -> u16 { - bit_extract( - self.glb_rs0.as_ref().unwrap().SMR[smr].get() as usize, - SMMU_SMR_ID_OFF, - SMMU_SMR_ID_LEN, - ) as u16 + bit_extract(self.glb_rs0.SMR[smr].get() as usize, SMMU_SMR_ID_OFF, SMMU_SMR_ID_LEN) as u16 } #[inline] fn smr_get_mask(&self, smr: usize) -> u16 { bit_extract( - self.glb_rs0.as_ref().unwrap().SMR[smr].get() as usize, + self.glb_rs0.SMR[smr].get() as usize, SMMU_SMR_MASK_OFF, SMMU_SMR_MASK_LEN, ) as u16 } - pub fn alloc_smr(&mut self) -> Option { - let alloc_bitmap = self.smr_alloc_bitmap.as_mut().unwrap(); - for i in 0..alloc_bitmap.vec_len() * 8 { + fn alloc_smr(&mut self) -> Option { + let alloc_bitmap = self.smr_alloc_bitmap.as_ref().unwrap(); + for i in 0..alloc_bitmap.vec_len() { if alloc_bitmap.get(i) == 0 { - alloc_bitmap.set(i, true); + self.smr_alloc_bitmap.as_mut().unwrap().set(i, true); return Some(i); } } None } - pub fn compatible_smr_exists(&mut self, mask: u16, id: u16, context_id: usize, group: bool) -> bool { + fn compatible_smr_exists(&mut self, mask: u16, id: u16, context_id: usize, group: bool) -> bool { for smr in 0..self.smr_num { let bit = self.smr_alloc_bitmap.as_ref().unwrap().get(smr); if bit == 0 { @@ -520,17 +468,18 @@ impl SmmuV2 { } } } + debug!("now, no compatible!"); false } - pub fn write_smr(&mut self, smr: usize, mask: u16, id: u16, group: bool) { + fn write_smr(&mut self, smr: usize, mask: u16, id: u16, group: bool) { if self.smr_alloc_bitmap.as_ref().unwrap().get(smr) == 0 { panic!("smmu: trying to write unallocated smr {}", smr); } else { let mut val: usize = (mask as usize) << SMMU_SMR_MASK_OFF; val |= (id & bit_mask!(SMMU_SMR_ID_OFF, SMMU_SMR_ID_LEN)) as usize; val |= SMMUV2_SMR_VALID; - self.glb_rs0.as_ref().unwrap().SMR[smr].set(val as u32); + self.glb_rs0.SMR[smr].set(val as u32); if group { self.group_alloc_bitmap.as_mut().unwrap().set(smr, true); } @@ -538,20 +487,20 @@ impl SmmuV2 { } // Stream-to-Context - pub fn write_s2c(&mut self, smr: usize, context_id: usize) { + fn write_s2c(&mut self, smr: usize, context_id: usize) { if self.smr_alloc_bitmap.as_ref().unwrap().get(smr) == 0 { panic!("smmu: trying to write unallocated s2c {}", smr); } else { - let mut s2cr: usize = self.glb_rs0.as_ref().unwrap().S2CR[smr].get() as usize; + let mut s2cr: usize = self.glb_rs0.S2CR[smr].get() as usize; s2cr &= bit_mask!(S2CR_IMPL_OFF, S2CR_IMPL_LEN); s2cr |= S2CR_DFLT; s2cr |= context_id & bit_mask!(S2CR_CBNDX_OFF, S2CR_CBNDX_LEN); - self.glb_rs0.as_ref().unwrap().S2CR[smr].set(s2cr as u32); + self.glb_rs0.S2CR[smr].set(s2cr as u32); } } - pub fn alloc_ctxbnk(&mut self) -> Option { + fn alloc_ctxbnk(&mut self) -> Option { let bitmap = match &mut self.context_alloc_bitmap { None => panic!("smmu_alloc_ctxbnk: smmu v2 context_alloc_bitmap not init"), Some(bitmap) => bitmap, @@ -566,24 +515,38 @@ impl SmmuV2 { None } - pub fn write_ctxbnk(&mut self, context_id: usize, root_pt: usize, vm_id: usize) { + fn write_ctxbnk(&mut self, context_id: usize, root_pt: usize, vm_id: usize) { if self.context_alloc_bitmap.is_none() || self.context_alloc_bitmap.as_ref().unwrap().get(context_id) == 0 { panic!("smmu ctx {} not allocated", context_id); } - let rs1 = self.glb_rs1.as_ref().unwrap(); + let rs1 = self.glb_rs1; // Set type as stage 2 only. rs1.CBAR[context_id].set((vm_id as u32) & 0xFF); rs1.CBA2R[context_id].set(1); // CBA2R_RW64_64BIT - let ps = 1; // PASize, 36-bit - let t0sz = 28; - let tcr = ((ps & 0x7) << SMMUV2_TCR_PS_OFF) - | (t0sz & 0x1F) - | SMMUV2_TCR_TG0_4K - | SMMUV2_TCR_ORGN0_WB_RA_WA - | SMMUV2_TCR_IRGN0_WB_RA_WA - | SMMUV2_TCR_SH0_IS - | SMMUV2_TCR_SL0_1; + let ps = if cfg!(feature = "lvl4") { + 0b100 // PASize, 44-bit + } else { + 1 // PASize, 36-bit + }; + let t0sz = if cfg!(feature = "lvl4") { 64 - 44 } else { 28 }; + let tcr = if cfg!(feature = "lvl4") { + ((ps & 0x7) << SMMUV2_TCR_PS_OFF) + | (t0sz & 0x1F) + | SMMUV2_TCR_TG0_4K + | SMMUV2_TCR_ORGN0_WB_RA_WA + | SMMUV2_TCR_IRGN0_WB_RA_WA + | SMMUV2_TCR_SH0_IS + | SMMUV2_TCR_SL0_01 + } else { + ((ps & 0x7) << SMMUV2_TCR_PS_OFF) + | (t0sz & 0x1F) + | SMMUV2_TCR_TG0_4K + | SMMUV2_TCR_ORGN0_WB_RA_WA + | SMMUV2_TCR_IRGN0_WB_RA_WA + | SMMUV2_TCR_SH0_IS + | SMMUV2_TCR_SL0_12 + }; self.context_bank[context_id].TCR.set(tcr as u32); self.context_bank[context_id] .TTBR0 @@ -596,21 +559,20 @@ impl SmmuV2 { root_pt ); let mut sctlr = self.context_bank[context_id].SCTLR.get() as usize; - sctlr = (sctlr) & (0xF << 28 | 0x1 << 20 | 0xF << 9 | 0x1 << 11); + sctlr &= 0xF << 28 | 0x1 << 20 | 0xF << 9 | 0x1 << 11; sctlr |= SMMUV2_SCTLR_CFRE | SMMUV2_SCTLR_CFIE | SMMUV2_SCTLR_M; self.context_bank[context_id].SCTLR.set(sctlr as u32); } } -pub static SMMU_V2: Mutex = Mutex::new(SmmuV2::new()); +static SMMU_V2: spin::Once> = spin::Once::new(); pub fn smmu_init() { - let mut smmu = SMMU_V2.lock(); - smmu.init(); + SMMU_V2.call_once(|| Mutex::new(SmmuV2::new())); } pub fn smmu_vm_init(vm: Vm) -> bool { - let mut smmu_v2 = SMMU_V2.lock(); + let mut smmu_v2 = SMMU_V2.get().unwrap().lock(); match smmu_v2.alloc_ctxbnk() { Some(context_id) => { smmu_v2.write_ctxbnk(context_id, vm.pt_dir(), vm.id()); @@ -622,7 +584,7 @@ pub fn smmu_vm_init(vm: Vm) -> bool { } pub fn smmu_add_device(context_id: usize, stream_id: usize) -> bool { - let mut smmu_v2 = SMMU_V2.lock(); + let mut smmu_v2 = SMMU_V2.get().unwrap().lock(); let prep_id = (stream_id & bit_mask!(SMMU_SMR_ID_OFF, SMMU_SMR_ID_LEN)) as u16; if !smmu_v2.compatible_smr_exists(0, prep_id, context_id, false) { @@ -641,10 +603,10 @@ pub fn smmu_add_device(context_id: usize, stream_id: usize) -> bool { } fn emu_smmu_revise_cbar(emu_ctx: &EmuContext) { - let smmu_v2 = SMMU_V2.lock(); + let smmu_v2 = SMMU_V2.get().unwrap().lock(); - let cbar_addr = smmu_v2.glb_rs1.as_ref().unwrap().CBAR.as_ptr() as usize; - let context_id = (emu_ctx.address - (cbar_addr - 0x8_0000_0000)) / size_of::(); + let cbar_addr = smmu_v2.glb_rs1.CBAR.as_ptr() as usize; + let context_id = (emu_ctx.address - cbar_addr) / size_of::(); let vm_context_id = active_vm().unwrap().iommu_ctx_id(); debug!( "emu_smmu_revise_cbar: vm {} access context id {}, vm context is {}", @@ -658,44 +620,44 @@ fn emu_smmu_revise_cbar(emu_ctx: &EmuContext) { // The SMMUv2 manual suggests that we should use identical VMID for both stages' CBAR cbar |= (vm_context_id & 0xFF) << 8; cbar |= active_vm_id() & 0xFF; - smmu_v2.glb_rs1.as_ref().unwrap().CBAR[context_id].set(cbar as u32); + smmu_v2.glb_rs1.CBAR[context_id].set(cbar as u32); } pub fn emu_smmu_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { let address = emu_ctx.address; - let smmu_v2 = SMMU_V2.lock(); + let smmu_v2 = SMMU_V2.get().unwrap().lock(); let mut permit_write = true; - let cbar = &smmu_v2.glb_rs1.as_ref().unwrap().CBAR; - if cbar.as_ptr_range().contains(&((address + 0x8_0000_0000) as *const _)) && emu_ctx.write { + let cbar = &smmu_v2.glb_rs1.CBAR; + if cbar.as_ptr_range().contains(&(address as *const _)) && emu_ctx.write { drop(smmu_v2); emu_smmu_revise_cbar(emu_ctx); return true; - } else if address + 0x8_0000_0000 >= smmu_v2.context_bank[smmu_v2.context_s2_idx].base_addr { + } else if address >= smmu_v2.context_bank[smmu_v2.context_s2_idx].addr() { // Forbid writing hypervisor's context banks. permit_write = false; } if !emu_ctx.write { let val = if emu_ctx.width > 4 { - unsafe { ptr::read_volatile((address + 0x8_0000_0000) as *const usize) } + unsafe { ptr::read_volatile(address as *const usize) } } else { - unsafe { ptr::read_volatile((address + 0x8_0000_0000) as *const u32) as usize } + unsafe { ptr::read_volatile(address as *const u32) as usize } }; current_cpu().set_gpr(emu_ctx.reg, val); } else { let val = current_cpu().get_gpr(emu_ctx.reg); if permit_write { if emu_ctx.width > 4 { - unsafe { ptr::write_volatile((address + 0x8_0000_0000) as *mut usize, val) }; + unsafe { ptr::write_volatile(address as *mut usize, val) }; } else { - unsafe { ptr::write_volatile((address + 0x8_0000_0000) as *mut u32, val as u32) }; + unsafe { ptr::write_volatile(address as *mut u32, val as u32) }; }; } else { info!( "emu_smmu_handler: vm {} is not allowed to access context[{}]", active_vm_id(), - (address - (smmu_v2.context_bank[0].base_addr as usize - 0x8_0000_0000)) / 0x10000, + (address - smmu_v2.context_bank.first().unwrap().addr()) / 0x10000, ); } } diff --git a/src/arch/aarch64/start.S b/src/arch/aarch64/start.S deleted file mode 100644 index 3bbd8338d032d736d39cd0066e61a596ee4543c5..0000000000000000000000000000000000000000 --- a/src/arch/aarch64/start.S +++ /dev/null @@ -1,195 +0,0 @@ -.section .text.boot - -.global _start -_start: - mov x20, x0 -// #ifdef PLATFORM_TX2 - mrs x0, mpidr_el1 - and x1, x0, #0x100 - cbz x1, 1f - and x0, x0, #3 - b 2f -1: wfe - b 1b - -// #endif -// #ifdef PLATFORM_QEMU -// mrs x0, mpidr_el1 -// and x0, x0, #7 -// #endif - -2: /* - * only cluster 1 cpu 0,1,2,3 reach here - * x0 holds core_id (indexed from zero) - */ - - // disable cache and MMU - mrs x1, sctlr_el2 - bic x1, x1, #0xf - msr sctlr_el2, x1 - - mov x19, x0 // save core_id - - // cache_invalidate(0): clear dl1$ - mov x0, #0 - bl cache_invalidate - - // if (core_id == 0) cache_invalidate(2): clear l2$ - cbnz x19, 3f - mov x0, #2 - bl cache_invalidate - -3: - mov x0, x19 // restore core_id - ic iallu // clear icache - - // setup stack sp per core - adrp x1, boot_stack - mov x2, (4096 * 2) - mul x3, x0, x2 - add x1, x1, x2 - add x1, x1, x3 - - mov sp, x1 - - cbnz x0, 5f - - adrp x0, lvl1_page_table - adrp x1, lvl2_page_table - bl pt_populate - - ldr x0, =_bss_begin - ldr x1, =_bss_end - sub x2, x1, x0 - mov x1, xzr - bl memset - -5: - // Trap nothing from EL1 to El2 - mov x3, xzr - msr cptr_el2, x3 - - adrp x0, lvl1_page_table - bl mmu_init - - mov x0, x19 - bl cpu_map_self - msr ttbr0_el2, x0 - - ldr x0, =(0x80080019) - msr hcr_el2, x0 - - mov x1, 1 - msr spsel, x1 - ldr x1, =CPU - add x1, x1, #(0x4000 + (4096 * 128)) - sub x1, x1, #0x110 - mov sp, x1 - - ldr x1, =vectors - msr vbar_el2, x1 - - - mov x0, x19 - ldr x3, =init - - - ldr x2, =(0x30c51835) - msr sctlr_el2, x2 - - tlbi alle2 - dsb nsh - isb - - mov x1, x20 - br x3 - -/* - * snipet from "Application Note Bare-metal Boot Code for ARMv8-A - * Processors - Version 1.0" - * - * x0 - cache level to be invalidated (0 - dl1$, 1 - il1$, 2 - l2$) - */ -cache_invalidate: - msr csselr_el1, x0 - mrs x4, ccsidr_el1 // read cache size id. - and x1, x4, #0x7 - add x1, x1, #0x4 // x1 = cache line size. - ldr x3, =0x7fff - and x2, x3, x4, lsr #13 // x2 = cache set number – 1. - ldr x3, =0x3ff - and x3, x3, x4, lsr #3 // x3 = cache associativity number – 1. - clz w4, w3 // x4 = way position in the cisw instruction. - mov x5, #0 // x5 = way counter way_loop. -way_loop: - mov x6, #0 // x6 = set counter set_loop. -set_loop: - lsl x7, x5, x4 - orr x7, x0, x7 // set way. - lsl x8, x6, x1 - orr x7, x7, x8 // set set. - dc cisw, x7 // clean and invalidate cache line. - add x6, x6, #1 // increment set counter. - cmp x6, x2 // last set reached yet? - ble set_loop // if not, iterate set_loop, - add x5, x5, #1 // else, next way. - cmp x5, x3 // last way reached yet? - ble way_loop // if not, iterate way_loop - ret - -.global update_request -update_request: - sub sp, sp, #40 - stp x0, x1, [sp, #0] - stp x2, x3, [sp, #16] - str x30, [sp, #32] - - adr x2, . // read pc to x2 - mov x3, #0x8a000000 - cmp x2, x3 - bgt 1f - bl live_update -1: - ldr x30, [sp, #32] - ldp x2, x3, [sp, #16] - ldp x0, x1, [sp, #0] - add sp, sp, #40 - ret - -live_update: - sub sp, sp, #40 - stp x0, x1, [sp, #0] - stp x2, x3, [sp, #16] - str x30, [sp, #32] - - adr x2, . // read pc to x0 - sub x2, x2, #16 // sub str instruction - mov x3, #0x7000000 // 0x83000000 + 0x7000000 - add x2, x2, x3 - blr x2 - - ldr x30, [sp, #32] - ldp x2, x3, [sp, #16] - ldp x0, x1, [sp, #0] - add sp, sp, #40 - ret - -.align 12 -.section .data.boot -boot_stack: - .space 4096 * 16 -boot_stack_top: - -.align 12 -.global lvl1_page_table -lvl1_page_table: - .space 4096 - -.align 12 -.global lvl2_page_table -lvl2_page_table: - .space 4096 - -.align 3 -_barrier: - .quad 0 diff --git a/src/arch/aarch64/start.rs b/src/arch/aarch64/start.rs new file mode 100644 index 0000000000000000000000000000000000000000..42ff29a61402cfb5b024cd6926ab02ea647632f5 --- /dev/null +++ b/src/arch/aarch64/start.rs @@ -0,0 +1,230 @@ +use tock_registers::interfaces::{Writeable, ReadWriteable}; + +use core::arch::asm; +use crate::arch::PAGE_SIZE; +use crate::kernel::{cpu_map_self, CPU_STACK_OFFSET, CPU_STACK_SIZE}; +use crate::board::PLAT_DESC; + +// XXX: Fixed boot stack size limits the maximum number of cpus, see '_start'. +const MAX_CPU: usize = 8; + +#[repr(align(8), C)] +struct CoreBootStack([u8; PAGE_SIZE * 2]); + +struct BootStack([CoreBootStack; NUM]); + +impl BootStack { + const fn new() -> Self { + Self([const { CoreBootStack([0; PAGE_SIZE * 2]) }; NUM]) + } +} +#[link_section = ".bss.stack"] +static mut BOOT_STACK: BootStack<{ PLAT_DESC.cpu_desc.num }> = BootStack::new(); + +extern "C" { + fn _bss_begin(); + fn _bss_end(); + fn vectors(); +} + +#[naked] +#[no_mangle] +#[link_section = ".text.boot"] +/// The entry point of the kernel. +pub unsafe extern "C" fn _start() -> ! { + asm!( + r#" + // save fdt pointer to x20 + mov x20, x0 + + // set stack per core + ldr x0, ={boot_stack} + add sp, x0, #{CORE_BOOT_STACK_SIZE} + + // disable cache and MMU + mrs x1, sctlr_el2 + bic x1, x1, #0xf + msr sctlr_el2, x1 + + // cache_invalidate(0): clear dl1$ + mov x0, #0 + bl {cache_invalidate} + + // if (cpu_id == 0) cache_invalidate(2): clear l2$ + mov x0, #2 + bl {cache_invalidate} + + // clear icache + ic iallu + + // if core_id is not zero, skip bss clearing and pt_populate + bl {clear_bss} + adrp x0, {lvl1_page_table} + adrp x1, {lvl2_page_table} + bl {pt_populate} + + // Trap nothing from EL1 to El2 + mov x3, xzr + msr cptr_el2, x3 + + // init mmu + adrp x0, {lvl1_page_table} + bl {mmu_init} + + // map cpu page table + mrs x0, mpidr_el1 + bl {cpu_map_self} + + bl {init_sysregs} // here, enable cache and MMU, then switch the stack + + // set real sp pointer + msr spsel, #1 + mrs x1, tpidr_el2 + add x1, x1, #({CPU_STACK_OFFSET} + {CPU_STACK_SIZE}) + sub sp, x1, #{CONTEXT_SIZE} + + tlbi alle2 + dsb nsh + isb + + mov x0, x20 + bl {init} + "#, + cache_invalidate = sym cache_invalidate, + boot_stack = sym BOOT_STACK, + CORE_BOOT_STACK_SIZE = const core::mem::size_of::(), + lvl1_page_table = sym super::mmu::LVL1_PAGE_TABLE, + lvl2_page_table = sym super::mmu::LVL2_PAGE_TABLE, + pt_populate = sym super::mmu::pt_populate, + mmu_init = sym super::mmu::mmu_init, + cpu_map_self = sym cpu_map_self, + CPU_STACK_OFFSET = const CPU_STACK_OFFSET, + CPU_STACK_SIZE = const CPU_STACK_SIZE, + CONTEXT_SIZE = const core::mem::size_of::(), + clear_bss = sym clear_bss, + init_sysregs = sym init_sysregs, + init = sym crate::init, + options(noreturn) + ); +} + +#[naked] +#[no_mangle] +pub unsafe extern "C" fn _secondary_start() -> ! { + asm!( + r#" + // save core id to x20 + mov x20, x0 + + // set stack per core by core id + ldr x0, ={boot_stack} + mov x1, #{CORE_BOOT_STACK_SIZE} + mul x2, x20, x1 + add x0, x0, x1 + add sp, x0, x2 + + // disable cache and MMU + mrs x1, sctlr_el2 + bic x1, x1, #0xf + msr sctlr_el2, x1 + + // cache_invalidate(0): clear dl1$ + mov x0, #0 + bl {cache_invalidate} + + mrs x0, mpidr_el1 + ic iallu + + // Trap nothing from EL1 to El2 + mov x3, xzr + msr cptr_el2, x3 + + // init mmu + adrp x0, {lvl1_page_table} + bl {mmu_init} + + // map cpu page table + mrs x0, mpidr_el1 + bl {cpu_map_self} + + bl {init_sysregs} // here, enable cache and MMU, then switch the stack + + // set real sp pointer + msr spsel, #1 + mrs x1, tpidr_el2 + add x1, x1, #({CPU_STACK_OFFSET} + {CPU_STACK_SIZE}) + sub sp, x1, #{CONTEXT_SIZE} + + tlbi alle2 + dsb nsh + isb + + mrs x0, mpidr_el1 + bl {secondary_init} + "#, + cache_invalidate = sym cache_invalidate, + lvl1_page_table = sym super::mmu::LVL1_PAGE_TABLE, + boot_stack = sym BOOT_STACK, + CORE_BOOT_STACK_SIZE = const core::mem::size_of::(), + mmu_init = sym super::mmu::mmu_init, + cpu_map_self = sym cpu_map_self, + CPU_STACK_OFFSET = const CPU_STACK_OFFSET, + CPU_STACK_SIZE = const CPU_STACK_SIZE, + CONTEXT_SIZE = const core::mem::size_of::(), + init_sysregs = sym init_sysregs, + secondary_init = sym crate::secondary_init, + options(noreturn) + ); +} + +fn init_sysregs() { + use cortex_a::registers::{HCR_EL2, VBAR_EL2, SCTLR_EL2}; + HCR_EL2.write( + HCR_EL2::VM::Enable + + HCR_EL2::RW::EL1IsAarch64 + + HCR_EL2::IMO::EnableVirtualIRQ + + HCR_EL2::FMO::EnableVirtualFIQ + + HCR_EL2::TSC::EnableTrapEl1SmcToEl2, + ); + VBAR_EL2.set(vectors as usize as u64); + SCTLR_EL2.modify(SCTLR_EL2::M::Enable + SCTLR_EL2::C::Cacheable + SCTLR_EL2::I::Cacheable); +} + +unsafe extern "C" fn clear_bss() { + core::slice::from_raw_parts_mut(_bss_begin as usize as *mut u8, _bss_end as usize - _bss_begin as usize).fill(0) +} + +unsafe extern "C" fn cache_invalidate(cache_level: usize) { + asm!( + r#" + msr csselr_el1, {0} + mrs x4, ccsidr_el1 // read cache size id. + and x1, x4, #0x7 + add x1, x1, #0x4 // x1 = cache line size. + ldr x3, =0x7fff + and x2, x3, x4, lsr #13 // x2 = cache set number - 1. + ldr x3, =0x3ff + and x3, x3, x4, lsr #3 // x3 = cache associativity number - 1. + clz w4, w3 // x4 = way position in the cisw instruction. + mov x5, #0 // x5 = way counter way_loop. + // way_loop: + 1: + mov x6, #0 // x6 = set counter set_loop. + // set_loop: + 2: + lsl x7, x5, x4 + orr x7, {0}, x7 // set way. + lsl x8, x6, x1 + orr x7, x7, x8 // set set. + dc cisw, x7 // clean and invalidate cache line. + add x6, x6, #1 // increment set counter. + cmp x6, x2 // last set reached yet? + ble 2b // if not, iterate set_loop, + add x5, x5, #1 // else, next way. + cmp x5, x3 // last way reached yet? + ble 1b // if not, iterate way_loop + "#, + in(reg) cache_level, + options(nostack) + ); +} diff --git a/src/arch/aarch64/start_pi4.S b/src/arch/aarch64/start_pi4.S deleted file mode 100644 index e2002639368afb7690ee5247a6a53804bd84400b..0000000000000000000000000000000000000000 --- a/src/arch/aarch64/start_pi4.S +++ /dev/null @@ -1,183 +0,0 @@ -.section .text.boot - -.global _start -_start: - mov x20, x0 // save fdt pointer to x20 - mrs x0, mpidr_el1 - and x0, x0, #7 - -2: /* - * only cluster 1 cpu 0,1,2,3 reach here - * x0 holds core_id (indexed from zero) - */ - - // disable cache and MMU - mrs x1, sctlr_el2 - bic x1, x1, #0xf - msr sctlr_el2, x1 - - mov x19, x0 // save core_id - - // cache_invalidate(0): clear dl1$ - mov x0, #0 - bl cache_invalidate - - // if (core_id == 0) cache_invalidate(2): clear l2$ - cbnz x19, 3f - mov x0, #2 - bl cache_invalidate - -3: - mov x0, x19 // restore core_id - ic iallu // clear icache - - // setup stack sp per core - adrp x1, boot_stack - mov x2, (4096 * 2) - mul x3, x0, x2 - add x1, x1, x3 - add x1, x1, x2 - - adrp x1, boot_stack_top - mov sp, x1 - - cbnz x0, 5f - - ldr x0, =_bss_begin - ldr x1, =_bss_end - sub x2, x1, x0 - mov x1, xzr - bl memset - - adrp x0, lvl1_page_table - adrp x1, lvl2_page_table - ldr x2, =_bss_begin - ldr x3, =_bss_end - bl pt_populate -5: - // Trap nothing from EL1 to El2 - mov x3, xzr - msr cptr_el2, x3 - - adrp x0, lvl1_page_table - bl mmu_init - - mov x0, x19 - bl cpu_map_self - msr ttbr0_el2, x0 - - ldr x0, =(0x80080019) - msr hcr_el2, x0 - - mov x1, 1 - msr spsel, x1 - ldr x1, =CPU - add x1, x1, #(0x4000 + (4096 * 128)) - sub x1, x1, #0x110 - mov sp, x1 - - ldr x1, =vectors - msr vbar_el2, x1 - - mov x0, x19 - ldr x3, =init - - ldr x2, =(0x30c51835) - msr sctlr_el2, x2 - - tlbi alle2 - dsb nsh - isb - - mov x1, x20 - br x3 - -/* - * snipet from "Application Note Bare-metal Boot Code for ARMv8-A - * Processors - Version 1.0" - * - * x0 - cache level to be invalidated (0 - dl1$, 1 - il1$, 2 - l2$) - */ -cache_invalidate: - msr csselr_el1, x0 - mrs x4, ccsidr_el1 // read cache size id. - and x1, x4, #0x7 - add x1, x1, #0x4 // x1 = cache line size. - ldr x3, =0x7fff - and x2, x3, x4, lsr #13 // x2 = cache set number – 1. - ldr x3, =0x3ff - and x3, x3, x4, lsr #3 // x3 = cache associativity number – 1. - clz w4, w3 // x4 = way position in the cisw instruction. - mov x5, #0 // x5 = way counter way_loop. -way_loop: - mov x6, #0 // x6 = set counter set_loop. -set_loop: - lsl x7, x5, x4 - orr x7, x0, x7 // set way. - lsl x8, x6, x1 - orr x7, x7, x8 // set set. - dc cisw, x7 // clean and invalidate cache line. - add x6, x6, #1 // increment set counter. - cmp x6, x2 // last set reached yet? - ble set_loop // if not, iterate set_loop, - add x5, x5, #1 // else, next way. - cmp x5, x3 // last way reached yet? - ble way_loop // if not, iterate way_loop - ret - -.global update_request -update_request: - sub sp, sp, #40 - stp x0, x1, [sp, #0] - stp x2, x3, [sp, #16] - str x30, [sp, #32] - - adr x2, . // read pc to x2 - mov x3, #0x8a000000 - cmp x2, x3 - bgt 1f - bl live_update -1: - ldr x30, [sp, #32] - ldp x2, x3, [sp, #16] - ldp x0, x1, [sp, #0] - add sp, sp, #40 - ret - -live_update: - sub sp, sp, #40 - stp x0, x1, [sp, #0] - stp x2, x3, [sp, #16] - str x30, [sp, #32] - - adr x2, . // read pc to x0 - sub x2, x2, #16 // sub str instruction - mov x3, #0x5000000 - add x2, x2, x3 - blr x2 - - ldr x30, [sp, #32] - ldp x2, x3, [sp, #16] - ldp x0, x1, [sp, #0] - add sp, sp, #40 - ret - -.align 12 -.section .data.boot -boot_stack: - .space 4096 * 8 -boot_stack_top: - -.align 12 -.global lvl1_page_table -lvl1_page_table: - .space 4096 - -.align 12 -.global lvl2_page_table -lvl2_page_table: - .space 4096 - -.align 3 -_barrier: - .quad 0 diff --git a/src/arch/aarch64/start_qemu.S b/src/arch/aarch64/start_qemu.S deleted file mode 100644 index 9351b9a8727c914d81905ee5b508e6dd84c35c3f..0000000000000000000000000000000000000000 --- a/src/arch/aarch64/start_qemu.S +++ /dev/null @@ -1,192 +0,0 @@ -.section .text.boot - -.global _start -_start: - mov x20, x0 -// #ifdef PLATFORM_TX2 -// mrs x0, mpidr_el1 -// and x1, x0, #0x100 -// cbz x1, 1f -// and x0, x0, #3 -// b 2f -// 1: wfe -// b 1b - -// #endif -// #ifdef PLATFORM_QEMU - mrs x0, mpidr_el1 - and x0, x0, #7 -// #endif - -2: /* - * only cluster 1 cpu 0,1,2,3 reach here - * x0 holds core_id (indexed from zero) - */ - - // disable cache and MMU - mrs x1, sctlr_el2 - bic x1, x1, #0xf - msr sctlr_el2, x1 - - mov x19, x0 // save core_id - - // cache_invalidate(0): clear dl1$ - mov x0, #0 - bl cache_invalidate - - // if (core_id == 0) cache_invalidate(2): clear l2$ - cbnz x19, 3f - mov x0, #2 - bl cache_invalidate - -3: - mov x0, x19 // restore core_id - ic iallu // clear icache - - // setup stack sp per core - adrp x1, boot_stack - mov x2, (4096 * 2) - mul x3, x0, x2 - add x1, x1, x2 - add x1, x1, x3 - - mov sp, x1 - - cbnz x0, 5f - - adrp x0, lvl1_page_table - adrp x1, lvl2_page_table - bl pt_populate - - ldr x0, =_bss_begin - ldr x1, =_bss_end - sub x2, x1, x0 - mov x1, xzr - bl memset - -5: - // Trap nothing from EL1 to El2 - mov x3, xzr - msr cptr_el2, x3 - - adrp x0, lvl1_page_table - bl mmu_init - - mov x0, x19 - bl cpu_map_self - msr ttbr0_el2, x0 - - ldr x0, =(0x80080019) - msr hcr_el2, x0 - - mov x1, 1 - msr spsel, x1 - ldr x1, =CPU - add x1, x1, #(0x4000 + (4096 * 128)) - sub x1, x1, #0x110 - mov sp, x1 - - ldr x1, =vectors - msr vbar_el2, x1 - - - mov x0, x19 - ldr x3, =init - - - ldr x2, =(0x30c51835) - msr sctlr_el2, x2 - - tlbi alle2 - dsb nsh - isb - - mov x1, x20 - br x3 - -/* - * snipet from "Application Note Bare-metal Boot Code for ARMv8-A - * Processors - Version 1.0" - * - * x0 - cache level to be invalidated (0 - dl1$, 1 - il1$, 2 - l2$) - */ -cache_invalidate: - msr csselr_el1, x0 - mrs x4, ccsidr_el1 // read cache size id. - and x1, x4, #0x7 - add x1, x1, #0x4 // x1 = cache line size. - ldr x3, =0x7fff - and x2, x3, x4, lsr #13 // x2 = cache set number – 1. - ldr x3, =0x3ff - and x3, x3, x4, lsr #3 // x3 = cache associativity number – 1. - clz w4, w3 // x4 = way position in the cisw instruction. - mov x5, #0 // x5 = way counter way_loop. -way_loop: - mov x6, #0 // x6 = set counter set_loop. -set_loop: - lsl x7, x5, x4 - orr x7, x0, x7 // set way. - lsl x8, x6, x1 - orr x7, x7, x8 // set set. - dc cisw, x7 // clean and invalidate cache line. - add x6, x6, #1 // increment set counter. - cmp x6, x2 // last set reached yet? - ble set_loop // if not, iterate set_loop, - add x5, x5, #1 // else, next way. - cmp x5, x3 // last way reached yet? - ble way_loop // if not, iterate way_loop - ret - -// TODO: need to support multi update -.global update_request -update_request: - sub sp, sp, #40 - stp x0, x1, [sp, #0] - stp x2, x3, [sp, #16] - str x30, [sp, #32] - - adr x2, . // read pc to x2 - mov x3, #0x8a000000 - cmp x2, x3 - blt 1f - bl live_update -1: - ldr x30, [sp, #32] - ldp x2, x3, [sp, #16] - ldp x0, x1, [sp, #0] - add sp, sp, #40 - ret - -live_update: - sub sp, sp, #40 - stp x0, x1, [sp, #0] - stp x2, x3, [sp, #16] - str x30, [sp, #32] - - bl rust_shyper_update - - ldr x30, [sp, #32] - ldp x2, x3, [sp, #16] - ldp x0, x1, [sp, #0] - add sp, sp, #40 - ret - -.align 12 -.section .data.boot -boot_stack: - .space 4096 * 16 -boot_stack_top: - -.align 12 -.global lvl1_page_table -lvl1_page_table: - .space 4096 - -.align 12 -.global lvl2_page_table -lvl2_page_table: - .space 4096 - -.align 3 -_barrier: - .quad 0 diff --git a/src/arch/aarch64/start_update.S b/src/arch/aarch64/start_update.S deleted file mode 100644 index 77b3b7da8e8decf6d5ca7d1177f272c6419c462e..0000000000000000000000000000000000000000 --- a/src/arch/aarch64/start_update.S +++ /dev/null @@ -1,192 +0,0 @@ -.section .text.boot - -.global _start -_start: - mov x20, x0 -// #ifdef PLATFORM_TX2 - mrs x0, mpidr_el1 - and x1, x0, #0x100 - cbz x1, 1f - and x0, x0, #3 - b 2f -1: wfe - b 1b - -// #endif -// #ifdef PLATFORM_QEMU -// mrs x0, mpidr_el1 -// and x0, x0, #7 -// #endif - -2: /* - * only cluster 1 cpu 0,1,2,3 reach here - * x0 holds core_id (indexed from zero) - */ - - // disable cache and MMU - mrs x1, sctlr_el2 - bic x1, x1, #0xf - msr sctlr_el2, x1 - - mov x19, x0 // save core_id - - // cache_invalidate(0): clear dl1$ - mov x0, #0 - bl cache_invalidate - - // if (core_id == 0) cache_invalidate(2): clear l2$ - cbnz x19, 3f - mov x0, #2 - bl cache_invalidate - -3: - mov x0, x19 // restore core_id - ic iallu // clear icache - - // setup stack sp per core - adrp x1, boot_stack - mov x2, (4096 * 2) - mul x3, x0, x2 - add x1, x1, x2 - add x1, x1, x3 - - mov sp, x1 - - cbnz x0, 5f - - adrp x0, lvl1_page_table - adrp x1, lvl2_page_table - bl pt_populate - - ldr x0, =_bss_begin - ldr x1, =_bss_end - sub x2, x1, x0 - mov x1, xzr - bl memset - -5: - // Trap nothing from EL1 to El2 - mov x3, xzr - msr cptr_el2, x3 - - adrp x0, lvl1_page_table - bl mmu_init - - mov x0, x19 - bl cpu_map_self - msr ttbr0_el2, x0 - - ldr x0, =(0x80080019) - msr hcr_el2, x0 - - mov x1, 1 - msr spsel, x1 - ldr x1, =CPU - add x1, x1, #(0x4000 + (4096 * 128)) - sub x1, x1, #0x110 - mov sp, x1 - - ldr x1, =vectors - msr vbar_el2, x1 - - - mov x0, x19 - ldr x3, =init - - - ldr x2, =(0x30c51835) - msr sctlr_el2, x2 - - tlbi alle2 - dsb nsh - isb - - mov x1, x20 - br x3 - -/* - * snipet from "Application Note Bare-metal Boot Code for ARMv8-A - * Processors - Version 1.0" - * - * x0 - cache level to be invalidated (0 - dl1$, 1 - il1$, 2 - l2$) - */ -cache_invalidate: - msr csselr_el1, x0 - mrs x4, ccsidr_el1 // read cache size id. - and x1, x4, #0x7 - add x1, x1, #0x4 // x1 = cache line size. - ldr x3, =0x7fff - and x2, x3, x4, lsr #13 // x2 = cache set number – 1. - ldr x3, =0x3ff - and x3, x3, x4, lsr #3 // x3 = cache associativity number – 1. - clz w4, w3 // x4 = way position in the cisw instruction. - mov x5, #0 // x5 = way counter way_loop. -way_loop: - mov x6, #0 // x6 = set counter set_loop. -set_loop: - lsl x7, x5, x4 - orr x7, x0, x7 // set way. - lsl x8, x6, x1 - orr x7, x7, x8 // set set. - dc cisw, x7 // clean and invalidate cache line. - add x6, x6, #1 // increment set counter. - cmp x6, x2 // last set reached yet? - ble set_loop // if not, iterate set_loop, - add x5, x5, #1 // else, next way. - cmp x5, x3 // last way reached yet? - ble way_loop // if not, iterate way_loop - ret - -// TODO: need to support multi update -.global update_request -update_request: - sub sp, sp, #40 - stp x0, x1, [sp, #0] - stp x2, x3, [sp, #16] - str x30, [sp, #32] - - adr x2, . // read pc to x2 - mov x3, #0x8a000000 - cmp x2, x3 - blt 1f - bl live_update -1: - ldr x30, [sp, #32] - ldp x2, x3, [sp, #16] - ldp x0, x1, [sp, #0] - add sp, sp, #40 - ret - -live_update: - sub sp, sp, #40 - stp x0, x1, [sp, #0] - stp x2, x3, [sp, #16] - str x30, [sp, #32] - - bl rust_shyper_update - - ldr x30, [sp, #32] - ldp x2, x3, [sp, #16] - ldp x0, x1, [sp, #0] - add sp, sp, #40 - ret - -.align 12 -.section .data.boot -boot_stack: - .space 4096 * 16 -boot_stack_top: - -.align 12 -.global lvl1_page_table -lvl1_page_table: - .space 4096 - -.align 12 -.global lvl2_page_table -lvl2_page_table: - .space 4096 - -.align 3 -_barrier: - .quad 0 diff --git a/src/arch/aarch64/sync.rs b/src/arch/aarch64/sync.rs index 29cd3a59febb7016f77f48d9e9a4da15fbb9b15d..27e9769bc993c88f22ee23bc7d7d5d80d92333e7 100644 --- a/src/arch/aarch64/sync.rs +++ b/src/arch/aarch64/sync.rs @@ -11,13 +11,13 @@ use crate::arch::{ exception_data_abort_access_is_sign_ext, exception_data_abort_access_is_write, exception_data_abort_access_reg, exception_data_abort_access_reg_width, exception_data_abort_access_width, exception_data_abort_handleable, - exception_data_abort_is_permission_fault, exception_data_abort_is_translate_fault, exception_iss, + exception_data_abort_is_permission_fault, exception_data_abort_is_translate_fault, exception_iss, smc_call, }; use crate::arch::{exception_esr, exception_fault_addr}; use crate::arch::exception_next_instruction_step; use crate::arch::smc_guest_handler; -use crate::device::{emu_handler, EmuContext}; -use crate::kernel::{active_vm, current_cpu, hvc_guest_handler, migrate_data_abort_handler}; +use crate::device::{emu_handler, emu_reg_handler, EmuContext}; +use crate::kernel::{active_vm, current_cpu, hvc_guest_handler, active_vm_id}; pub const HVC_RETURN_REG: usize = 0; @@ -44,22 +44,6 @@ pub fn data_abort_handler() { if !exception_data_abort_is_translate_fault() { if exception_data_abort_is_permission_fault() { - // println!( - // "write {}, width {}, reg width {}, addr {:x}, iss {:x}, reg idx {}, reg val 0x{:x}, esr 0x{:x}", - // exception_data_abort_access_is_write(), - // emu_ctx.width, - // emu_ctx.reg_width, - // emu_ctx.address, - // exception_iss(), - // emu_ctx.reg, - // current_cpu().get_gpr(emu_ctx.reg), - // exception_esr() - // ); - migrate_data_abort_handler(&emu_ctx); - // no need to rewrite elr - - // let time1 = time_current_us(); - // println!("migrate_data_abort_handler: {}us", time1 - time0); return; } else { panic!( @@ -69,9 +53,12 @@ pub fn data_abort_handler() { ); } } + // if current_cpu().id == 1 { + // println!("emu_handler address:{:#x}", emu_ctx.address); + // } if !emu_handler(&emu_ctx) { active_vm().unwrap().show_pagetable(emu_ctx.address); - println!( + debug!( "write {}, width {}, reg width {}, addr {:x}, iss {:x}, reg idx {}, reg val 0x{:x}, esr 0x{:x}", exception_data_abort_access_is_write(), emu_ctx.width, @@ -91,6 +78,7 @@ pub fn data_abort_handler() { current_cpu().set_elr(val); } +/// architecture specific smc handler entry pub fn smc_handler() { let fid = current_cpu().get_gpr(0); let x1 = current_cpu().get_gpr(1); @@ -98,8 +86,19 @@ pub fn smc_handler() { let x3 = current_cpu().get_gpr(3); if !smc_guest_handler(fid, x1, x2, x3) { - warn!("smc_handler: unknown fid 0x{:x}", fid); - current_cpu().set_gpr(0, 0); + if active_vm_id() == 0 { + // SAFETY: + // We just forward the SMC call to the ATF directly. + // The args are from lower EL, so it is safe to call the ATF. + let res = unsafe { smc_call(fid, x1, x2, x3) }; + current_cpu().set_gpr(0, res.0); + current_cpu().set_gpr(1, res.1); + current_cpu().set_gpr(2, res.2); + current_cpu().set_gpr(3, res.3); + } else { + warn!("smc_handler: unknown fid 0x{:x}", fid); + current_cpu().set_gpr(0, usize::MAX); + } } let elr = current_cpu().get_elr(); @@ -107,6 +106,7 @@ pub fn smc_handler() { current_cpu().set_elr(val); } +/// architecture specific hvc handler entry pub fn hvc_handler() { // let time_start = timer_arch_get_counter(); let x0 = current_cpu().get_gpr(0); @@ -131,11 +131,59 @@ pub fn hvc_handler() { } } // let time_end = timer_arch_get_counter(); - // println!( - // "hvc fid 0x{:x} event 0x{:x} counter {}, freq {:x}", - // hvc_type, - // event, - // time_end - time_start, - // timer_arch_get_frequency() - // ); +} + +#[inline(always)] +fn exception_sysreg_addr(iss: u32) -> u32 { + // (Op0[21..20] + Op2[19..17] + Op1[16..14] + CRn[13..10]) + CRm[4..1] + const ESR_ISS_SYSREG_ADDR: u32 = (0xfff << 10) | (0xf << 1); + iss & ESR_ISS_SYSREG_ADDR +} + +#[inline(always)] +fn exception_sysreg_direction_write(iss: u32) -> bool { + const ESR_ISS_SYSREG_DIRECTION: u32 = 0b1; + (iss & ESR_ISS_SYSREG_DIRECTION) == 0 +} + +#[inline(always)] +fn exception_sysreg_gpr(iss: u32) -> u32 { + const ESR_ISS_SYSREG_REG_OFF: u32 = 5; + const ESR_ISS_SYSREG_REG_LEN: u32 = 5; + const ESR_ISS_SYSREG_REG_MASK: u32 = (1 << ESR_ISS_SYSREG_REG_LEN) - 1; + (iss >> ESR_ISS_SYSREG_REG_OFF) & ESR_ISS_SYSREG_REG_MASK +} + +/*ESR EC:011000 IL, bit [25]:Instruction Length for synchronous exceptions. Possible values of this bit are: 0:16b 1:32b +Op0, bits [21:20] +Op2, bits [19:17] +Op1, bits [16:14] +CRn, bits [13:10] +CRm, bits [4:1] This five parameters are for SYSREG + +Rt, bits [9:5] general-purpose register used for the transfer. +Direction, bit [0] 0:Write access, including MSR instructions. 1:Read access, including MRS instructions. +*/ +pub fn sysreg_handler(iss: u32) { + let reg_addr = exception_sysreg_addr(iss); + + let emu_ctx = EmuContext { + address: reg_addr as usize, + width: 8, + write: exception_sysreg_direction_write(iss), + sign_ext: false, + reg: exception_sysreg_gpr(iss) as usize, + reg_width: 8, + }; + + let elr = current_cpu().get_elr(); + if !emu_reg_handler(&emu_ctx) { + panic!( + "sysreg_handler: Failed to handler emu reg request, ({:#x} at {:#x})", + emu_ctx.address, elr + ); + } + + let val = elr + exception_next_instruction_step(); + current_cpu().set_elr(val); } diff --git a/src/arch/aarch64/timer.rs b/src/arch/aarch64/timer.rs index 77897301d412de8ebe9d167ca4336051548ce947..814fc27177475d42f42f443bc6c17e96343bfbff 100644 --- a/src/arch/aarch64/timer.rs +++ b/src/arch/aarch64/timer.rs @@ -8,47 +8,67 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use spin::Mutex; +use core::sync::atomic::{AtomicUsize, Ordering}; + use tock_registers::interfaces::*; +use crate::arch::{CNTHP_TVAL_EL2, CNTHP_CTL_EL2}; +use crate::arch::aarch64::regs::WriteableReg; const CTL_IMASK: usize = 1 << 1; -pub static TIMER_FREQ: Mutex = Mutex::new(0); -pub static TIMER_SLICE: Mutex = Mutex::new(0); // ms +pub static TIMER_FREQ: AtomicUsize = AtomicUsize::new(0); +pub static TIMER_SLICE: AtomicUsize = AtomicUsize::new(0); // ms +/// Set expiration time to `num` ms later pub fn timer_arch_set(num: usize) { - let slice_lock = TIMER_SLICE.lock(); - let val = *slice_lock * num; - drop(slice_lock); - msr!(CNTHP_TVAL_EL2, val); + let slice = TIMER_SLICE.load(Ordering::Relaxed); + let val = slice * num; + // SAFETY: Set timer value + unsafe { + CNTHP_TVAL_EL2::write(val); + } } +/// Enable timer interrupt pub fn timer_arch_enable_irq() { - let val = 1; - msr!(CNTHP_CTL_EL2, val, "x"); + // SAFETY: Enable[0] timer interrupt + unsafe { + CNTHP_CTL_EL2::write(1); + } } +/// Disable timer interrupt pub fn timer_arch_disable_irq() { - let val = 2; - msr!(CNTHP_CTL_EL2, val, "x"); + // SAFETY: MASK[1] timer interrupt + unsafe { + CNTHP_CTL_EL2::write(2); + } } +/// Get current counter value pub fn timer_arch_get_counter() -> usize { cortex_a::registers::CNTPCT_EL0.get() as usize } +/// Get timer frequency pub fn timer_arch_get_frequency() -> usize { cortex_a::registers::CNTFRQ_EL0.get() as usize } +/// timer init function for specific architecture pub fn timer_arch_init() { - let mut freq_lock = TIMER_FREQ.lock(); - let mut slice_lock = TIMER_SLICE.lock(); - *freq_lock = timer_arch_get_frequency(); - *slice_lock = (*freq_lock) / 1000; // ms + let freq = timer_arch_get_frequency(); + let slice = freq / 1000; + TIMER_FREQ.store(freq, Ordering::Relaxed); + TIMER_SLICE.store(freq / 1000, Ordering::Relaxed); let ctl = 0x3 & (1 | !CTL_IMASK); - let tval = *slice_lock * 10; - msr!(CNTHP_CTL_EL2, ctl); - msr!(CNTHP_TVAL_EL2, tval); + let tval = slice * 10; + // SAFETY: + // Set timer value + // Init timer with enable but mask + unsafe { + CNTHP_CTL_EL2::write(ctl); + CNTHP_TVAL_EL2::write(tval); + } } diff --git a/src/arch/aarch64/tlb.rs b/src/arch/aarch64/tlb.rs index da90ba0baa7f973f66ed6e134f1c7e7be81ad893..f1970005b0e0f72b84ed02cc1456f4f325c49af2 100644 --- a/src/arch/aarch64/tlb.rs +++ b/src/arch/aarch64/tlb.rs @@ -8,10 +8,76 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use core::arch::asm; +use crate::arch::{dsb, isb}; -pub fn tlb_invalidate_guest_all() { - unsafe { - asm!("dsb ish", "tlbi vmalls12e1is", "dsb ish", "isb"); +// Translation Look-a-side Buffer Instrution ('TLBI') operations. +// SAFETY: +// TLBI operations can't trigger any side effects on the safety of the system. +pub mod tlbi { + macro_rules! define_tlbi { + ($mode:ident) => { + pub fn $mode() { + unsafe { + sysop!(tlbi, $mode); + } + } + }; + } + + macro_rules! define_tlbi_ipa { + ($mode:ident) => { + pub fn $mode(ipa: u64) { + unsafe { + sysop!(tlbi, $mode, ipa); + } + } + }; } + + macro_rules! define_tlbi_va { + ($mode:ident) => { + pub fn $mode(va: usize) { + unsafe { + sysop!(tlbi, $mode, va as u64); + } + } + }; + } + + define_tlbi!(alle1is); + define_tlbi!(alle1); + define_tlbi!(alle2is); + define_tlbi!(alle2); + define_tlbi!(vmalle1); + define_tlbi!(vmalle1is); + define_tlbi!(vmalle12e1); + define_tlbi!(vmalle12e1is); + define_tlbi!(vmalls12e1is); + define_tlbi_ipa!(ipas2e1is); + define_tlbi_ipa!(ipas2e1); + define_tlbi_ipa!(ipas2le1is); + define_tlbi_ipa!(ipas2le1); + define_tlbi_va!(vaae1); + define_tlbi_va!(vaae1is); + define_tlbi_va!(vae1is); + define_tlbi_va!(vae1); + define_tlbi_va!(vae2is); + define_tlbi_va!(vae2); +} + +/// invalidate all guest tlb entries +pub fn tlb_invalidate_guest_all() { + dsb::ish(); + tlbi::vmalls12e1is(); + dsb::ish(); + isb(); +} + +/// invalidate all hypervisor tlb entries +pub fn invalid_hypervisor_all() { + dsb::ish(); + tlbi::alle2is(); + dsb::ish(); + isb(); } +// TODO: add more TLB operations diff --git a/src/arch/aarch64/vcpu.rs b/src/arch/aarch64/vcpu.rs index 69e3a99994eb31c90a55bcd493d336c6db8ad254..23180fc97b652015aff597166a5d365f7d559ae8 100644 --- a/src/arch/aarch64/vcpu.rs +++ b/src/arch/aarch64/vcpu.rs @@ -14,9 +14,10 @@ use crate::arch::traits::ContextFrameTrait; use crate::kernel::{Vcpu, Vm}; use crate::kernel::VmType; +/// vcpu init function for specific architecture pub fn vcpu_arch_init(vm: Vm, vcpu: Vcpu) { let config = vm.config(); - let mut vcpu_inner = vcpu.inner.lock(); + let mut vcpu_inner = vcpu.inner.inner_mut.lock(); match config.os_type { VmType::VmTOs => { vcpu_inner.vcpu_ctx.set_argument(config.device_tree_load_ipa()); diff --git a/src/arch/aarch64/vgic.rs b/src/arch/aarch64/vgic.rs index 6dc2341afdbbfcf2136f51acf2f270b343a09368..46d03edf726b23b18c3e55c393864ce30a27a0c0 100644 --- a/src/arch/aarch64/vgic.rs +++ b/src/arch/aarch64/vgic.rs @@ -8,26 +8,23 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use alloc::collections::{BTreeMap, VecDeque}; +use alloc::collections::VecDeque; use alloc::sync::Arc; use alloc::vec::Vec; use spin::Mutex; use crate::{arch::GICH, kernel::IpiInitcMessage}; -use crate::board::{PLATFORM_CPU_NUM_MAX, Platform, PlatOperation}; +use crate::board::{Platform, PlatOperation}; use crate::device::EmuContext; use crate::device::EmuDevs; -use crate::kernel::{ - active_vcpu_id, current_cpu, restore_vcpu_gic, save_vcpu_gic, VgicCpuPrivData, VgicIntData, VgicMigData, -}; +use crate::kernel::{active_vcpu_id, current_cpu, restore_vcpu_gic, save_vcpu_gic}; use crate::kernel::{active_vm, active_vm_id, active_vm_ncpu}; use crate::kernel::{ipi_intra_broadcast_msg, ipi_send_msg, IpiInnerMsg, IpiMessage, IpiType}; -use crate::kernel::{InitcEvent, Vcpu, Vm, vm}; -use crate::lib::{bit_extract, bit_get, bit_set, bitmap_find_nth, ptr_read_write}; +use crate::kernel::{InitcEvent, Vcpu, Vm}; +use crate::utils::{bit_extract, bit_get, bit_set, bitmap_find_nth}; use super::gic::*; - #[derive(Clone)] struct VgicInt { inner: Arc>, @@ -35,31 +32,6 @@ struct VgicInt { } impl VgicInt { - fn update(&self) -> Self { - let new_this = Self::new(self.id() as usize); - match self.owner() { - Some(vcpu) => { - let vm = vcpu.vm().unwrap(); - new_this.set_owner(vm.vcpu(vcpu.id()).unwrap()); - } - None => {} - }; - let mut inner = new_this.inner.lock(); - inner.id = self.id(); - inner.hw = self.hw(); - inner.in_lr = self.in_lr(); - inner.lr = self.lr(); - inner.enabled = self.enabled(); - inner.state = self.state(); - inner.prio = self.prio(); - inner.targets = self.targets(); - inner.cfg = self.cfg(); - inner.in_pend = self.in_pend(); - inner.in_act = self.in_act(); - drop(inner); - new_this - } - fn new(id: usize) -> VgicInt { VgicInt { inner: Arc::new(Mutex::new(VgicIntInner::new(id))), @@ -67,145 +39,6 @@ impl VgicInt { } } - pub fn restore_migrate_data( - &self, - int_data: &VgicIntData, - vcpu_list: &Vec, - vcpuid_map: &BTreeMap, - ) { - let mut inner = self.inner.lock(); - inner.owner = match int_data.owner { - None => None, - Some(id) => Some(vcpu_list[id].clone()), - }; - inner.id = int_data.id; - inner.hw = int_data.hw; - inner.in_lr = int_data.in_lr; - inner.enabled = int_data.enabled; - inner.state = int_data.state; - inner.prio = int_data.prio; - inner.targets = { - let mut pcpu_targets = 0; - // println!( - // "restore vgic int {} migrate target data {:x}", - // int_data.id, int_data.targets - // ); - for vcpuid in 0..PLATFORM_CPU_NUM_MAX { - if (1 << vcpuid) & int_data.targets != 0 { - pcpu_targets |= (1 << vcpuid_map[&vcpuid]) as u8; - } - } - pcpu_targets - }; - inner.cfg = int_data.cfg; - inner.in_pend = int_data.in_pend; - inner.in_act = int_data.in_act; - - // TODO: rude - // if inner.enabled { - // let int_id = inner.id as usize; - // GICD.set_enable(int_id, true); - // GICD.set_prio(int_id, inner.prio); - // GICD.set_trgt(int_id, 1 << platform_cpuid_to_cpuif(current_cpu().id)); - // GICD.set_icfgr(int_id, inner.cfg); - // println!( - // "cpuid {} cpuif {:x}", - // current_cpu().id, - // platform_cpuid_to_cpuif(current_cpu().id) - // ); - - // if int_id > 16 { - // println!( - // "restore_migrate_data after: int {} ISENABLER {:x}, ISACTIVER {:x}, IPRIORITY {:x}, ITARGETSR {:x}, ICFGR {:x}", - // int_id, - // GICD.is_enabler(int_id / 32), - // GICD.is_activer(int_id / 32), - // GICD.ipriorityr(int_id / 4), - // GICD.itargetsr(int_id / 4), - // GICD.icfgr(int_id / 2) - // ); - // } - // } - } - - pub fn save_migrate_data(&self, int_data: &mut VgicIntData, cpuid_map: &BTreeMap) { - let inner = self.inner.lock(); - int_data.owner = match &inner.owner { - None => None, - Some(vcpu) => Some(vcpu.id()), - }; - int_data.id = inner.id; - int_data.hw = inner.hw; - int_data.in_lr = inner.in_lr; - int_data.lr = inner.lr; - int_data.enabled = inner.enabled; - int_data.state = inner.state; - int_data.prio = inner.prio; - int_data.targets = { - let mut vcpu_targets = 0; - for pcpuid in 0..PLATFORM_CPU_NUM_MAX { - if (1 << pcpuid) & inner.targets != 0 { - vcpu_targets |= (1 << cpuid_map[&pcpuid]) as u8; - } - } - vcpu_targets - }; - int_data.cfg = inner.cfg; - int_data.in_pend = inner.in_pend; - int_data.in_act = inner.in_act; - // if inner.enabled { - // println!( - // "int {} is enable, state {:#?}, prio {:x}, targets {:x}, in pend {}, int act {}, int cfg {}", - // inner.id, inner.state, inner.prio, inner.targets, inner.in_pend, inner.in_act, inner.cfg - // ); - - // let int_id = inner.id as usize; - // if int_id > 16 { - // println!( - // "int {} ISENABLER {:x}, ISACTIVER {:x}, IPRIORITY {:x}, ITARGETSR {:x}, ICFGR {:x}", - // int_id, - // GICD.is_enabler(int_id / 32), - // GICD.is_activer(int_id / 32), - // GICD.ipriorityr(int_id / 4), - // GICD.itargetsr(int_id / 4), - // GICD.icfgr(int_id / 2) - // ); - // } - // } - } - - // back up for hyper fresh - pub fn fresh_back_up(&self) -> VgicInt { - let inner = self.inner.lock(); - let owner = { - match &inner.owner { - None => None, - Some(vcpu) => { - let vm_id = vcpu.vm_id(); - let vm = vm(vm_id).unwrap(); - vm.vcpu(vcpu.id()) - } - } - }; - VgicInt { - inner: Arc::new(Mutex::new(VgicIntInner { - owner, - id: inner.id, - hw: inner.hw, - in_lr: inner.in_lr, - lr: inner.lr, - enabled: inner.enabled, - state: inner.state, - prio: inner.prio, - targets: inner.targets, - cfg: inner.cfg, - in_pend: inner.in_pend, - in_act: inner.in_act, - })), - lock: Arc::new(Mutex::new(())), - } - } - fn priv_new(id: usize, owner: Vcpu, targets: usize, enabled: bool) -> VgicInt { VgicInt { inner: Arc::new(Mutex::new(VgicIntInner::priv_new(id, owner, targets, enabled))), @@ -334,52 +167,28 @@ impl VgicInt { fn owner(&self) -> Option { let vgic_int = self.inner.lock(); - match &vgic_int.owner { - Some(vcpu) => { - return Some(vcpu.clone()); - } - None => { - // println!("vgic_int {} owner vcpu is none", vgic_int.id); - return None; - } - } + vgic_int.owner.as_ref().cloned() } fn owner_phys_id(&self) -> Option { let vgic_int = self.inner.lock(); - match &vgic_int.owner { - Some(owner) => { - return Some(owner.phys_id()); - } - None => { - return None; - } - } + vgic_int.owner.as_ref().map(|owner| owner.phys_id()) } fn owner_id(&self) -> Option { let vgic_int = self.inner.lock(); match &vgic_int.owner { - Some(owner) => { - return Some(owner.id()); - } + Some(owner) => Some(owner.id()), None => { - println!("owner_id is None"); - return None; + warn!("owner_id is None"); + None } } } fn owner_vm_id(&self) -> Option { let vgic_int = self.inner.lock(); - match &vgic_int.owner { - Some(owner) => { - return Some(owner.vm_id()); - } - None => { - return None; - } - } + vgic_int.owner.as_ref().map(|owner| owner.vm_id()) } fn owner_vm(&self) -> Vm { @@ -495,38 +304,6 @@ impl VgicCpuPriv { act_list: VecDeque::new(), } } - - // use for migration - pub fn restore_migrate_data( - &mut self, - cpu_priv_data: &VgicCpuPrivData, - vcpu_list: &Vec, - vcpuid_map: &BTreeMap, - ) { - self.sgis = cpu_priv_data.sgis; - self.curr_lrs = cpu_priv_data.curr_lrs; - for (idx, int) in self.interrupts.iter_mut().enumerate() { - int.restore_migrate_data(&cpu_priv_data.interrupts[idx], vcpu_list, vcpuid_map); - } - assert_eq!(self.interrupts.len(), cpu_priv_data.interrupts.len()); - } - - // use for migration - pub fn save_migrate_data(&self, cpu_priv_data: &mut VgicCpuPrivData, cpuid_map: &BTreeMap) { - cpu_priv_data.sgis = self.sgis; - cpu_priv_data.curr_lrs = self.curr_lrs; - for (idx, int) in self.interrupts.iter().enumerate() { - int.save_migrate_data(&mut cpu_priv_data.interrupts[idx], cpuid_map); - } - cpu_priv_data.act_num = self.act_list.len(); - for (idx, int) in self.act_list.iter().enumerate() { - cpu_priv_data.act_list[idx] = int.id() as usize; - } - cpu_priv_data.act_num = self.pend_list.len(); - for (idx, int) in self.pend_list.iter().enumerate() { - cpu_priv_data.pend_list[idx] = int.id() as usize; - } - } } pub struct Vgic { @@ -542,156 +319,6 @@ impl Vgic { } } - pub fn restore_vgic_data( - &self, - vgic_data: &VgicMigData, - vcpu_list: &Vec, - vcpuid_map: &BTreeMap, - ) { - let mut vgicd = self.vgicd.lock(); - vgicd.ctlr = vgic_data.vgicd.ctlr; - vgicd.typer = vgic_data.vgicd.typer; - vgicd.iidr = vgic_data.vgicd.iidr; - for (idx, int) in vgicd.interrupts.iter().enumerate() { - int.restore_migrate_data(&vgic_data.vgicd.interrupts[idx], vcpu_list, vcpuid_map); - } - let mut cpu_priv_list = self.cpu_priv.lock(); - for idx in 0..vgic_data.cpu_priv_num { - cpu_priv_list[idx].restore_migrate_data(&vgic_data.cpu_priv[idx], vcpu_list, vcpuid_map); - assert_eq!(cpu_priv_list[idx].act_list.len(), 0); - assert_eq!(cpu_priv_list[idx].pend_list.len(), 0); - // act list - for act_idx in 0..vgic_data.cpu_priv[idx].act_num { - let id = vgic_data.cpu_priv[idx].act_list[act_idx]; - let interrupt = if id >= GIC_SPI_MAX { - cpu_priv_list[idx].interrupts[id - GIC_SPI_MAX].clone() - } else { - vgicd.interrupts[id].clone() - }; - cpu_priv_list[idx].act_list.push_back(interrupt); - } - // pend list - for pend_idx in 0..vgic_data.cpu_priv[idx].pend_num { - let id = vgic_data.cpu_priv[idx].pend_list[pend_idx]; - let interrupt = if id >= GIC_SPI_MAX { - cpu_priv_list[idx].interrupts[id - GIC_SPI_MAX].clone() - } else { - vgicd.interrupts[id].clone() - }; - cpu_priv_list[idx].pend_list.push_back(interrupt); - } - } - } - - pub fn save_vgic_data(&self, vgic_data: &mut VgicMigData, cpuid_map: &BTreeMap) { - let vgicd = self.vgicd.lock(); - vgic_data.vgicd.iidr = vgicd.iidr; - vgic_data.vgicd.typer = vgicd.typer; - vgic_data.vgicd.ctlr = vgicd.ctlr; - for (idx, int) in vgicd.interrupts.iter().enumerate() { - int.save_migrate_data(&mut vgic_data.vgicd.interrupts[idx], cpuid_map); - } - - let cpu_priv_list = self.cpu_priv.lock(); - vgic_data.cpu_priv_num = cpu_priv_list.len(); - for (idx, cpu_priv) in cpu_priv_list.iter().enumerate() { - cpu_priv.save_migrate_data(&mut vgic_data.cpu_priv[idx], cpuid_map); - } - } - - // reset vcpu in save vgic, use for hypervisor fresh - pub fn save_vgic(&self, src_vgic: Arc) { - // let time0 = time_current_us(); - let src_vgicd = src_vgic.vgicd.lock(); - let mut cur_vgicd = self.vgicd.lock(); - cur_vgicd.ctlr = src_vgicd.ctlr; - cur_vgicd.iidr = src_vgicd.iidr; - cur_vgicd.typer = src_vgicd.typer; - for interrupt in src_vgicd.interrupts.iter() { - cur_vgicd.interrupts.push(interrupt.update()); - } - // cur_vgicd.interrupts.append(&mut src_vgicd.interrupts); - println!( - "src vgicd interrupts len {}, cur interrupts len {}", - src_vgicd.interrupts.len(), - cur_vgicd.interrupts.len() - ); - // let time1 = time_current_us(); - // let mut num = 0; - - // cur_vgicd.interrupts.push(interrupt.fresh_back_up()); - // let time2 = time_current_us(); - - let mut src_cpu_priv = src_vgic.cpu_priv.lock(); - let mut cur_cpu_priv = self.cpu_priv.lock(); - // let mut num1 = 0; - for cpu_priv in src_cpu_priv.iter_mut() { - let vgic_cpu_priv = VgicCpuPriv { - curr_lrs: cpu_priv.curr_lrs, - sgis: cpu_priv.sgis, - interrupts: { - let mut interrupts = vec![]; - // interrupts.append(&mut cpu_priv.interrupts); - for interrupt in cpu_priv.interrupts.iter_mut() { - interrupts.push(interrupt.clone()); - } - for interrupt in cpu_priv.interrupts.iter_mut() { - match interrupt.owner() { - None => {} - Some(vcpu) => { - let vm_id = vcpu.vm_id(); - let vm = vm(vm_id).unwrap(); - let int_id = interrupt.id() as usize; - let phys_id = vcpu.phys_id(); - interrupts.push(VgicInt::priv_new( - int_id, - vm.vcpu(vcpu.id()).unwrap(), - 1 << phys_id, - int_id < GIC_SGIS_NUM, - )); - // num1 += 1; - } - } - // interrupts.push(interrupt.fresh_back_up()); - } - println!( - "src vgicd cpu_priv interrupts len {}, cur interrupts cpu_priv len {}", - cpu_priv.interrupts.len(), - interrupts.len() - ); - interrupts - }, - pend_list: { - let mut pend_list = VecDeque::new(); - for pend_int in cpu_priv.pend_list.iter() { - pend_list.push_back(pend_int.fresh_back_up()); - } - pend_list - }, - act_list: { - let mut act_list = VecDeque::new(); - for act_int in cpu_priv.act_list.iter() { - act_list.push_back(act_int.fresh_back_up()); - } - act_list - }, - }; - cur_cpu_priv.push(vgic_cpu_priv); - } - // let time3 = time_current_us(); - // println!( - // "save vgic: vgicd len {} append {} us, EN_SET size {}, set owner {} {} us, cpu_priv {} change owner {} {} us", - // cur_vgicd.interrupts.len(), - // time1 - time0, - // INTERRUPT_EN_SET.lock().len(), - // num, - // time2 - time1, - // cur_cpu_priv.len(), - // num1, - // time3 - time2 - // ); - } - fn remove_int_list(&self, vcpu: Vcpu, interrupt: VgicInt, is_pend: bool) { let mut cpu_priv = self.cpu_priv.lock(); let vcpu_id = vcpu.id(); @@ -752,10 +379,11 @@ impl Vgic { self.remove_int_list(vcpu.clone(), interrupt.clone(), false); } - if interrupt.id() < GIC_SGIS_NUM as u16 { - if self.cpu_priv_sgis_pend(vcpu.id(), interrupt.id() as usize) != 0 && !interrupt.in_pend() { - self.add_int_list(vcpu, interrupt, true); - } + if interrupt.id() < GIC_SGIS_NUM as u16 + && self.cpu_priv_sgis_pend(vcpu.id(), interrupt.id() as usize) != 0 + && !interrupt.in_pend() + { + self.add_int_list(vcpu, interrupt, true); } } @@ -768,12 +396,10 @@ impl Vgic { } else { Some(cpu_priv[vcpu_id].pend_list[0].clone()) } + } else if cpu_priv[vcpu_id].act_list.is_empty() { + None } else { - if cpu_priv[vcpu_id].act_list.is_empty() { - None - } else { - Some(cpu_priv[vcpu_id].act_list[0].clone()) - } + Some(cpu_priv[vcpu_id].act_list[0].clone()) } } @@ -841,10 +467,10 @@ impl Vgic { if int_id < GIC_PRIVINT_NUM { let vcpu_id = vcpu.id(); return Some(self.cpu_priv_interrupt(vcpu_id, int_id)); - } else if int_id >= GIC_PRIVINT_NUM && int_id < GIC_INTS_MAX { + } else if (GIC_PRIVINT_NUM..GIC_INTS_MAX).contains(&int_id) { return Some(self.vgicd_interrupt(int_id - GIC_PRIVINT_NUM)); } - return None; + None } fn remove_lr(&self, vcpu: Vcpu, interrupt: VgicInt) -> bool { @@ -884,6 +510,19 @@ impl Vgic { if (interrupt.state().to_num() & 1 != 0) && interrupt.enabled() { // println!("remove_lr: interrupt_state {}", interrupt.state().to_num()); let hcr = GICH.hcr(); + /* + NPIE, bit [3] + No Pending Interrupt Enable. Enables the signaling of a maintenance interrupt while no pending interrupts are present in the List registers: + + NPIE Meaning + 0 + Maintenance interrupt disabled. + + 1 + Maintenance interrupt signaled while the List registers contain no interrupts in the pending state. + + When this register has an architecturally-defined reset value, this field resets to 0. + */ GICH.set_hcr(hcr | (1 << 3)); } return true; @@ -944,15 +583,15 @@ impl Vgic { let spilled_int = self .get_int(vcpu.clone(), GICH.lr(idx) as usize & 0b1111111111) .unwrap(); - let spilled_int_lock; if spilled_int.id() != interrupt.id() { - spilled_int_lock = spilled_int.lock.lock(); + let spilled_int_lock = spilled_int.lock.lock(); + self.remove_lr(vcpu.clone(), spilled_int.clone()); + vgic_int_yield_owner(vcpu.clone(), spilled_int.clone()); + drop(spilled_int_lock); + } else { + self.remove_lr(vcpu.clone(), spilled_int.clone()); + vgic_int_yield_owner(vcpu.clone(), spilled_int.clone()); } - self.remove_lr(vcpu.clone(), spilled_int.clone()); - vgic_int_yield_owner(vcpu.clone(), spilled_int.clone()); - // if spilled_int.id() != interrupt.id() { - // drop(spilled_int_lock); - // } } } @@ -988,13 +627,14 @@ impl Vgic { // current_cpu().id, // prev_interrupt.id() // ); - if vgic_owns(vcpu.clone(), prev_interrupt.clone()) { - if prev_interrupt.in_lr() && prev_interrupt.lr() == lr_ind as u16 { - prev_interrupt.set_in_lr(false); - let prev_id = prev_interrupt.id() as usize; - if !gic_is_priv(prev_id) { - vgic_int_yield_owner(vcpu.clone(), prev_interrupt.clone()); - } + if vgic_owns(vcpu.clone(), prev_interrupt.clone()) + && prev_interrupt.in_lr() + && prev_interrupt.lr() == lr_ind as u16 + { + prev_interrupt.set_in_lr(false); + let prev_id = prev_interrupt.id() as usize; + if !gic_is_priv(prev_id) { + vgic_int_yield_owner(vcpu.clone(), prev_interrupt.clone()); } } drop(prev_interrupt_lock); @@ -1018,7 +658,6 @@ impl Vgic { } else if int_id < GIC_SGIS_NUM { if (state & 2) != 0 { lr |= ((self.cpu_priv_sgis_act(vcpu_id, int_id) as usize) << 10) & (0b111 << 10); - // lr |= ((cpu_priv[vcpu_id].sgis[int_id].act as usize) << 10) & (0b111 << 10); lr |= (2 & 0b11) << 28; } else { let mut idx = GIC_TARGETS_MAX - 1; @@ -1050,10 +689,6 @@ impl Vgic { interrupt.set_in_lr(true); interrupt.set_lr(lr_ind as u16); self.set_cpu_priv_curr_lrs(vcpu_id, lr_ind, int_id as u16); - - // if current_cpu().id == 1 { - // println!("Core1 write lr[{}] 0x{:x}", lr_ind, lr); - // } GICH.set_lr(lr_ind, lr as u32); self.update_int_list(vcpu, interrupt); @@ -1071,7 +706,6 @@ impl Vgic { let int_targets = interrupt.targets(); if (int_targets & (1 << cpu_id)) != 0 { - // println!("vm{} route addr lr for int {}", vcpu.vm_id(), interrupt.id()); self.add_lr(vcpu.clone(), interrupt.clone()); } @@ -1119,7 +753,7 @@ impl Vgic { val: en as u8, }; if !ipi_send_msg(int_phys_id, IpiType::IpiTIntc, IpiInnerMsg::Initc(ipi_msg)) { - println!( + error!( "vgicd_set_enable: Failed to send ipi message, target {} type {}", int_phys_id, 0 ); @@ -1128,8 +762,7 @@ impl Vgic { drop(interrupt_lock); } None => { - println!("vgicd_set_enable: interrupt {} is illegal", int_id); - return; + error!("vgicd_set_enable: interrupt {} is illegal", int_id); } } } @@ -1183,7 +816,7 @@ impl Vgic { drop(interrupt_lock); if !ipi_send_msg(phys_id, IpiType::IpiTIntc, IpiInnerMsg::Initc(m)) { - println!( + error!( "vgicd_set_pend: Failed to send ipi message, target {} type {}", phys_id, 0 ); @@ -1233,7 +866,7 @@ impl Vgic { }; let phys_id = interrupt.owner_phys_id().unwrap(); if !ipi_send_msg(phys_id, IpiType::IpiTIntc, IpiInnerMsg::Initc(m)) { - println!( + error!( "vgicd_set_active: Failed to send ipi message, target {} type {}", phys_id, 0 ); @@ -1265,7 +898,7 @@ impl Vgic { IpiType::IpiTIntc, IpiInnerMsg::Initc(m), ) { - println!( + error!( "set_icfgr: Failed to send ipi message, target {} type {}", interrupt.owner_phys_id().unwrap(), 0 @@ -1281,14 +914,13 @@ impl Vgic { fn get_icfgr(&self, vcpu: Vcpu, int_id: usize) -> u8 { let interrupt_option = self.get_int(vcpu, int_id); if let Some(interrupt) = interrupt_option { - return interrupt.cfg(); + interrupt.cfg() } else { unimplemented!(); } } fn sgi_set_pend(&self, vcpu: Vcpu, int_id: usize, pend: bool) { - // let begin = time_current_us(); if bit_extract(int_id, 0, 10) > GIC_SGIS_NUM { return; } @@ -1303,14 +935,13 @@ impl Vgic { let vgic_int_id = interrupt.id() as usize; let pendstate = self.cpu_priv_sgis_pend(vcpu_id, vgic_int_id); - // let pendstate = cpu_priv[vcpu_id].sgis[vgic_int_id].pend; let new_pendstate = if pend { pendstate | (1 << source) as u8 } else { pendstate & !(1 << source) as u8 }; + // state changed ,the two state isn`t equal if (pendstate ^ new_pendstate) != 0 { - // cpu_priv[vcpu_id].sgis[vgic_int_id].pend = new_pendstate; self.set_cpu_priv_sgis_pend(vcpu_id, vgic_int_id, new_pendstate); let state = interrupt.state().to_num(); if new_pendstate != 0 { @@ -1324,7 +955,7 @@ impl Vgic { // println!("state {}", interrupt.state().to_num()); match interrupt.state() { IrqState::IrqSInactive => { - println!("inactive"); + debug!("inactive"); } _ => { self.add_lr(vcpu, interrupt.clone()); @@ -1333,10 +964,8 @@ impl Vgic { } drop(interrupt_lock); } else { - println!("sgi_set_pend: interrupt {} is None", bit_extract(int_id, 0, 10)); + error!("sgi_set_pend: interrupt {} is None", bit_extract(int_id, 0, 10)); } - // let end = time_current_us(); - // println!("sgi_set_pend[{}]", end - begin); } fn set_prio(&self, vcpu: Vcpu, int_id: usize, mut prio: u8) { @@ -1372,7 +1001,7 @@ impl Vgic { IpiType::IpiTIntc, IpiInnerMsg::Initc(m), ) { - println!( + error!( "set_prio: Failed to send ipi message, target {} type {}", interrupt.owner_phys_id().unwrap(), 0 @@ -1385,7 +1014,7 @@ impl Vgic { fn get_prio(&self, vcpu: Vcpu, int_id: usize) -> u8 { let interrupt_option = self.get_int(vcpu, int_id); - return interrupt_option.unwrap().prio(); + interrupt_option.unwrap().prio() } fn set_trgt(&self, vcpu: Vcpu, int_id: usize, trgt: u8) { @@ -1422,7 +1051,7 @@ impl Vgic { IpiType::IpiTIntc, IpiInnerMsg::Initc(m), ) { - println!( + error!( "set_trgt: Failed to send ipi message, target {} type {}", interrupt.owner_phys_id().unwrap(), 0 @@ -1435,7 +1064,7 @@ impl Vgic { fn get_trgt(&self, vcpu: Vcpu, int_id: usize) -> u8 { let interrupt_option = self.get_int(vcpu, int_id); - return interrupt_option.unwrap().targets(); + interrupt_option.unwrap().targets() } pub fn inject(&self, vcpu: Vcpu, int_id: usize) { @@ -1491,7 +1120,7 @@ impl Vgic { let val = self.vgicd_typer() as usize; current_cpu().set_gpr(idx, val); } else { - println!("emu_typer_access: can't write to RO reg"); + warn!("emu_typer_access: can't write to RO reg"); } } @@ -1501,7 +1130,7 @@ impl Vgic { let val = self.vgicd_iidr() as usize; current_cpu().set_gpr(idx, val); } else { - println!("emu_iidr_access: can't write to RO reg"); + warn!("emu_iidr_access: can't write to RO reg"); } } @@ -1527,7 +1156,7 @@ impl Vgic { } } if first_int >= 16 && !vm_has_interrupt_flag { - println!( + error!( "emu_isenabler_access: vm[{}] does not have interrupt {}", vm_id, first_int ); @@ -1552,7 +1181,7 @@ impl Vgic { } fn emu_pendr_access(&self, emu_ctx: &EmuContext, set: bool) { - println!("emu_pendr_access"); + trace!("emu_pendr_access"); let reg_idx = (emu_ctx.address & 0b1111111) / 4; let idx = emu_ctx.reg; let mut val = if emu_ctx.write { current_cpu().get_gpr(idx) } else { 0 }; @@ -1573,7 +1202,7 @@ impl Vgic { } } if first_int >= 16 && !vm_has_interrupt_flag { - println!("emu_pendr_access: vm[{}] does not have interrupt {}", vm_id, first_int); + error!("emu_pendr_access: vm[{}] does not have interrupt {}", vm_id, first_int); return; } @@ -1810,7 +1439,7 @@ impl Vgic { val: true as u8, }; if !ipi_send_msg(i, IpiType::IpiTIntc, IpiInnerMsg::Initc(m)) { - println!( + error!( "emu_sgiregs_access: Failed to send ipi message, target {} type {}", i, 0 ); @@ -1901,14 +1530,6 @@ impl Vgic { } fn handle_trapped_eoir(&self, vcpu: Vcpu) { - // if current_cpu().id == 2 { - // for i in 0..4 { - // println!("gich.LR[{}] 0x{:x}", i, GICH.lr(i)); - // } - // println!("elrsr[0] {:x}", GICH.elrsr(0)); - // println!("eisr[0] {:x}", GICH.eisr(0)); - // println!("hcr 0x{:x}", GICH.hcr()); - // } let gic_lrs = gic_lrs(); let mut lr_idx_opt = bitmap_find_nth( GICH.eisr(0) as usize | ((GICH.eisr(1) as usize) << 32), @@ -1926,12 +1547,6 @@ impl Vgic { match self.get_int(vcpu.clone(), bit_extract(lr_val, 0, 10)) { Some(interrupt) => { let interrupt_lock = interrupt.lock.lock(); - // if current_cpu().id == 2 { - // println!("handle_trapped_eoir interrupt {}", interrupt.id()); - // } - // if current_cpu().id == 1 && interrupt.id() == 49 { - // println!("handle_trapped_eoir interrupt 49"); - // } interrupt.set_in_lr(false); if (interrupt.id() as usize) < GIC_SGIS_NUM { self.add_lr(vcpu.clone(), interrupt.clone()); @@ -1939,7 +1554,6 @@ impl Vgic { vgic_int_yield_owner(vcpu.clone(), interrupt.clone()); } drop(interrupt_lock); - // println!("handle_trapped_eoir: Core {} finish", current_cpu().id); } None => { unimplemented!(); @@ -1956,9 +1570,6 @@ impl Vgic { } fn refill_lrs(&self, vcpu: Vcpu) { - // if current_cpu().id == 1 { - // println!("refill lrs"); - // } let gic_lrs = gic_lrs(); let mut has_pending = false; @@ -1983,13 +1594,10 @@ impl Vgic { let act_head = self.int_list_head(vcpu.clone(), false); let pend_head = self.int_list_head(vcpu.clone(), true); if has_pending { - match act_head { - Some(act_int) => { - if !act_int.in_lr() { - interrupt_opt = Some(act_int.clone()); - } + if let Some(act_int) = act_head { + if !act_int.in_lr() { + interrupt_opt = Some(act_int.clone()); } - None => {} } } if interrupt_opt.is_none() { @@ -2024,29 +1632,23 @@ impl Vgic { true, ); } - // println!("end refill lrs"); } fn eoir_highest_spilled_active(&self, vcpu: Vcpu) { let interrupt = self.int_list_head(vcpu.clone(), false); - match interrupt { - Some(int) => { - int.lock.lock(); - vgic_int_get_owner(vcpu.clone(), int.clone()); + if let Some(int) = interrupt { + int.lock.lock(); + vgic_int_get_owner(vcpu.clone(), int.clone()); - let state = int.state().to_num(); - int.set_state(IrqState::num_to_state(state & !2)); - self.update_int_list(vcpu.clone(), int.clone()); + let state = int.state().to_num(); + int.set_state(IrqState::num_to_state(state & !2)); + self.update_int_list(vcpu.clone(), int.clone()); - if vgic_int_is_hw(int.clone()) { - GICD.set_act(int.id() as usize, false); - } else { - if int.state().to_num() & 1 != 0 { - self.add_lr(vcpu, int); - } - } + if vgic_int_is_hw(int.clone()) { + GICD.set_act(int.id() as usize, false); + } else if int.state().to_num() & 1 != 0 { + self.add_lr(vcpu, int); } - None => {} } } } @@ -2066,7 +1668,7 @@ fn vgic_target_translate(vm: Vm, trgt: u32, v2p: bool) -> u32 { .iter() .enumerate() { - result |= (*val as u32) << (8 * idx); + result |= *val << (8 * idx); if idx >= 4 { panic!("illegal idx, from len {}", from.len()); } @@ -2093,9 +1695,9 @@ fn vgic_owns(vcpu: Vcpu, interrupt: VgicInt) -> bool { // owner_vcpu_id == vcpu_id && owner_pcpu_id == pcpu_id, // result // ); - return owner_vcpu_id == vcpu_id && owner_pcpu_id == pcpu_id; + owner_vcpu_id == vcpu_id && owner_pcpu_id == pcpu_id } - None => return false, + None => false, } // let tmp = interrupt.owner().unwrap(); @@ -2165,16 +1767,12 @@ fn gich_get_lr(interrupt: VgicInt) -> Option { let lr_val = GICH.lr(interrupt.lr() as usize); if (lr_val & 0b1111111111 == interrupt.id() as u32) && (lr_val >> 28 & 0b11 != 0) { - return Some(lr_val as u32); + return Some(lr_val); } - return None; + None } fn vgic_int_get_owner(vcpu: Vcpu, interrupt: VgicInt) -> bool { - // if interrupt.owner().is_none() { - // interrupt.set_owner(vcpu.clone()); - // return true; - // } let vcpu_id = vcpu.id(); let vcpu_vm_id = vcpu.vm_id(); @@ -2197,7 +1795,7 @@ fn vgic_int_get_owner(vcpu: Vcpu, interrupt: VgicInt) -> bool { // return false; } -pub fn gic_maintenance_handler(_arg: usize) { +pub fn gic_maintenance_handler() { let misr = GICH.misr(); let vm = match active_vm() { Some(vm) => vm, @@ -2205,9 +1803,6 @@ pub fn gic_maintenance_handler(_arg: usize) { panic!("gic_maintenance_handler: current vcpu.vm is None"); } }; - // if current_cpu().id == 2 { - // println!("gic_maintenance_handler, misr {:x}", misr); - // } let vgic = vm.vgic(); if misr & 1 != 0 { @@ -2219,7 +1814,6 @@ pub fn gic_maintenance_handler(_arg: usize) { } if misr & (1 << 2) != 0 { - // println!("in gic_maintenance_handler eoir_highest_spilled_active"); let mut hcr = GICH.hcr(); while hcr & (0b11111 << 27) != 0 { vgic.eoir_highest_spilled_active(current_cpu().active_vcpu.clone().unwrap()); @@ -2227,7 +1821,6 @@ pub fn gic_maintenance_handler(_arg: usize) { GICH.set_hcr(hcr); hcr = GICH.hcr(); } - // println!("end gic_maintenance_handler eoir_highest_spilled_active"); } } @@ -2256,12 +1849,6 @@ pub fn emu_intc_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { }; let vgic = vm.vgic(); let vgicd_offset_prefix = (offset & 0xf80) >> 7; - // if current_cpu().id == 2 { - // println!( - // "emu_intc_handler: vgicd_offset_prefix 0x{:x}, offset 0x{:x}", - // vgicd_offset_prefix, offset - // ); - // } if !vgicd_emu_access_is_vaild(emu_ctx) { return false; } @@ -2313,9 +1900,9 @@ pub fn emu_intc_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { } } } - if offset >= 0x400 && offset < 0x800 { + if (0x400..0x800).contains(&offset) { vgic.emu_ipriorityr_access(emu_ctx); - } else if offset >= 0x800 && offset < 0xc00 { + } else if (0x800..0xc00).contains(&offset) { vgic.emu_itargetr_access(emu_ctx); } } @@ -2348,12 +1935,11 @@ pub fn vgicd_emu_access_is_vaild(emu_ctx: &EmuContext) -> bool { } _ => { // TODO: hard code to rebuild (gicd IPRIORITYR and ITARGETSR) - if offset >= 0x400 && offset < 0xc00 { - if (emu_ctx.width == 4 && emu_ctx.address & 0x3 != 0) - || (emu_ctx.width == 2 && emu_ctx.address & 0x1 != 0) - { - return false; - } + if (0x400..0xc00).contains(&offset) + && ((emu_ctx.width == 4 && emu_ctx.address & 0x3 != 0) + || (emu_ctx.width == 2 && emu_ctx.address & 0x1 != 0)) + { + return false; } } } @@ -2364,18 +1950,14 @@ pub fn partial_passthrough_intc_handler(_emu_dev_id: usize, emu_ctx: &EmuContext if !vgicd_emu_access_is_vaild(emu_ctx) { return false; } - let offset = emu_ctx.address & 0xfff; - // println!( - // "partial_passthrough_intc_handler: {} offset_prefix 0x{:x}, offset 0x{:x}", - // if emu_ctx.write { "write" } else { "read" }, offset_prefix, offset - // ); if emu_ctx.write { - // todo: add offset match - let val = current_cpu().get_gpr(emu_ctx.reg); - ptr_read_write(Platform::GICD_BASE + 0x8_0000_0000 + offset, emu_ctx.width, val, false); + // SAFETY: Emu_ctx.address is writeable in EL2 + unsafe { + emu_ctx.write(current_cpu().get_gpr(emu_ctx.reg)); + } } else { - let res = ptr_read_write(Platform::GICD_BASE + 0x8_0000_0000 + offset, emu_ctx.width, 0, true); - current_cpu().set_gpr(emu_ctx.reg, res); + // SAFETY: Emu_ctx.address is readable in EL2 + current_cpu().set_gpr(emu_ctx.reg, unsafe { emu_ctx.read() }); } true @@ -2392,13 +1974,13 @@ pub fn vgic_ipi_handler(msg: &IpiMessage) { val = intc.val; } _ => { - println!("vgic_ipi_handler: illegal ipi"); + error!("vgic_ipi_handler: illegal ipi"); return; } } let trgt_vcpu = match current_cpu().vcpu_array.pop_vcpu_through_vmid(vm_id) { None => { - println!("Core {} received vgic msg from unknown VM {}", current_cpu().id, vm_id); + error!("Core {} received vgic msg from unknown VM {}", current_cpu().id, vm_id); return; } Some(vcpu) => vcpu, @@ -2413,19 +1995,11 @@ pub fn vgic_ipi_handler(msg: &IpiMessage) { }; let vgic = vm.vgic(); - if vm_id as usize != vm.id() { - println!("VM {} received vgic msg from another vm {}", vm.id(), vm_id); + if vm_id != vm.id() { + error!("VM {} received vgic msg from another vm {}", vm.id(), vm_id); return; } if let IpiInnerMsg::Initc(intc) = &msg.ipi_message { - // println!( - // "vgic_ipi_handler: core {} receive vgic_ipi, event {:?}, vm_id {}, int_id {}, val 0x{:x}", - // current_cpu().id, - // intc.event, - // vm_id, - // int_id, - // val - // ); match intc.event { InitcEvent::VgicdGichEn => { let hcr = GICH.hcr(); @@ -2461,7 +2035,7 @@ pub fn vgic_ipi_handler(msg: &IpiMessage) { } } _ => { - println!("vgic_ipi_handler: core {} received unknown event", current_cpu().id) + error!("vgic_ipi_handler: core {} received unknown event", current_cpu().id) } } } @@ -2522,34 +2096,18 @@ pub fn vgic_set_hw_int(vm: Vm, int_id: usize) { if int_id < GIC_PRIVINT_NUM { for i in 0..vm.cpu_num() { let interrupt_option = vgic.get_int(vm.vcpu(i).unwrap(), int_id); - match interrupt_option { - Some(interrupt) => { - let interrupt_lock = interrupt.lock.lock(); - // println!( - // "vgic_set_hw_int: Core {} get int {} lock", - // cpu_id(), - // interrupt.id() - // ); - interrupt.set_hw(true); - drop(interrupt_lock); - } - None => {} - } - } - } else { - let interrupt_option = vgic.get_int(vm.vcpu(0).unwrap(), int_id); - match interrupt_option { - Some(interrupt) => { + if let Some(interrupt) = interrupt_option { let interrupt_lock = interrupt.lock.lock(); - // println!( - // "vgic_set_hw_int: Core {} get int {} lock", - // cpu_id(), - // interrupt.id() - // ); interrupt.set_hw(true); drop(interrupt_lock); } - None => {} + } + } else { + let interrupt_option = vgic.get_int(vm.vcpu(0).unwrap(), int_id); + if let Some(interrupt) = interrupt_option { + let interrupt_lock = interrupt.lock.lock(); + interrupt.set_hw(true); + drop(interrupt_lock); } } } diff --git a/src/arch/aarch64/vgicv3.rs b/src/arch/aarch64/vgicv3.rs new file mode 100644 index 0000000000000000000000000000000000000000..0539c26125e3b194b64e2c832d82d04fca760330 --- /dev/null +++ b/src/arch/aarch64/vgicv3.rs @@ -0,0 +1,2324 @@ +// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. +// Rust-Shyper is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use core::mem::size_of; + +use alloc::collections::VecDeque; +use alloc::sync::Arc; +use alloc::vec::Vec; + +use spin::Mutex; + +use crate::arch::{GICH, MPIDR_EL1}; +use crate::arch::aarch64::regs::ReadableReg; +use crate::board::PLAT_DESC; +use crate::device::EmuContext; +use crate::device::EmuDevs; +use crate::kernel::{current_cpu, restore_vcpu_gic, save_vcpu_gic, cpuid2mpidr, IpiInitcMessage}; +use crate::kernel::{active_vm, active_vm_id}; +use crate::kernel::{ipi_intra_broadcast_msg, ipi_send_msg, IpiInnerMsg, IpiMessage, IpiType}; +use crate::kernel::{InitcEvent, Vcpu, Vm}; +use crate::utils::{bit_extract, bit_get, bitmap_find_nth}; + +use super::gicv3::*; + +#[derive(Clone)] +/// GICv3 interrupt struct +struct VgicInt { + inner: Arc>, + pub lock: Arc>, +} + +impl VgicInt { + fn new(id: usize) -> VgicInt { + VgicInt { + inner: Arc::new(Mutex::new(VgicIntInner::new(id))), + lock: Arc::new(Mutex::new(())), + } + } + + fn priv_new(id: usize, owner: Vcpu, targets: usize, enabled: bool, redist: usize, cfg: usize) -> VgicInt { + VgicInt { + inner: Arc::new(Mutex::new(VgicIntInner::priv_new( + id, owner, targets, enabled, redist, cfg, + ))), + lock: Arc::new(Mutex::new(())), + } + } + + fn set_in_pend_state(&self, is_pend: bool) { + let mut vgic_int = self.inner.lock(); + vgic_int.in_pend = is_pend; + } + + fn set_in_act_state(&self, is_act: bool) { + let mut vgic_int = self.inner.lock(); + vgic_int.in_act = is_act; + } + + pub fn in_pend(&self) -> bool { + let vgic_int = self.inner.lock(); + vgic_int.in_pend + } + + pub fn in_act(&self) -> bool { + let vgic_int = self.inner.lock(); + vgic_int.in_act + } + + fn set_enabled(&self, enabled: bool) { + let mut vgic_int = self.inner.lock(); + vgic_int.enabled = enabled; + } + + fn set_lr(&self, lr: u16) { + let mut vgic_int = self.inner.lock(); + vgic_int.lr = lr; + } + + fn set_targets(&self, targets: u8) { + let mut vgic_int = self.inner.lock(); + vgic_int.targets = targets; + } + + fn set_prio(&self, prio: u8) { + let mut vgic_int = self.inner.lock(); + vgic_int.prio = prio; + } + + fn set_in_lr(&self, in_lr: bool) { + let mut vgic_int = self.inner.lock(); + vgic_int.in_lr = in_lr; + } + + fn set_state(&self, state: IrqState) { + let mut vgic_int = self.inner.lock(); + vgic_int.state = state; + } + + fn set_owner(&self, owner: Vcpu) { + let mut vgic_int = self.inner.lock(); + vgic_int.owner = Some(owner); + } + + fn clear_owner(&self) { + let mut vgic_int = self.inner.lock(); + vgic_int.owner = None; + } + + fn set_hw(&self, hw: bool) { + let mut vgic_int = self.inner.lock(); + vgic_int.hw = hw; + } + + fn set_cfg(&self, cfg: u8) { + let mut vgic_int = self.inner.lock(); + vgic_int.cfg = cfg; + } + + fn lr(&self) -> u16 { + let vgic_int = self.inner.lock(); + vgic_int.lr + } + + fn in_lr(&self) -> bool { + let vgic_int = self.inner.lock(); + vgic_int.in_lr + } + + fn route(&self) -> u64 { + let vgic_int = self.inner.lock(); + vgic_int.route + } + + fn phys_redist(&self) -> u64 { + let vgic_int = self.inner.lock(); + match vgic_int.phys { + VgicIntPhys::Redist(redist) => redist, + _ => { + panic!("must get redist!"); + } + } + } + + fn phys_route(&self) -> u64 { + let vgic_int = self.inner.lock(); + match vgic_int.phys { + VgicIntPhys::Route(route) => route, + _ => { + panic!("must get route!") + } + } + } + + fn set_phys_route(&self, route: usize) { + let mut vgic_int = self.inner.lock(); + vgic_int.phys = VgicIntPhys::Route(route as u64); + } + + fn set_phys_redist(&self, redist: usize) { + let mut vgic_int = self.inner.lock(); + vgic_int.phys = VgicIntPhys::Redist(redist as u64); + } + + fn set_route(&self, route: usize) { + let mut vgic_int = self.inner.lock(); + vgic_int.route = route as u64; + } + + fn id(&self) -> u16 { + let vgic_int = self.inner.lock(); + vgic_int.id + } + + fn enabled(&self) -> bool { + let vgic_int = self.inner.lock(); + vgic_int.enabled + } + + fn prio(&self) -> u8 { + let vgic_int = self.inner.lock(); + vgic_int.prio + } + + fn targets(&self) -> u8 { + let vgic_int = self.inner.lock(); + vgic_int.targets + } + + fn hw(&self) -> bool { + let vgic_int = self.inner.lock(); + vgic_int.hw + } + + pub fn state(&self) -> IrqState { + let vgic_int = self.inner.lock(); + vgic_int.state + } + + fn cfg(&self) -> u8 { + let vgic_int = self.inner.lock(); + vgic_int.cfg + } + + fn owner(&self) -> Option { + let vgic_int = self.inner.lock(); + vgic_int.owner.as_ref().cloned() + } + + fn owner_phys_id(&self) -> Option { + let vgic_int = self.inner.lock(); + vgic_int.owner.as_ref().map(|owner| owner.phys_id()) + } + + fn owner_id(&self) -> Option { + let vgic_int = self.inner.lock(); + match &vgic_int.owner { + Some(owner) => Some(owner.id()), + None => { + error!("owner_id is None"); + None + } + } + } + + fn owner_vm_id(&self) -> Option { + let vgic_int = self.inner.lock(); + vgic_int.owner.as_ref().map(|owner| owner.vm_id()) + } + + fn owner_vm(&self) -> Vm { + let vgic_int = self.inner.lock(); + vgic_int.owner_vm() + } +} + +#[derive(Clone)] +enum VgicIntPhys { + Redist(u64), + Route(u64), +} + +struct VgicIntInner { + owner: Option, + route: u64, + phys: VgicIntPhys, + id: u16, + hw: bool, + in_lr: bool, + lr: u16, + enabled: bool, + state: IrqState, + prio: u8, + targets: u8, + cfg: u8, + + in_pend: bool, + in_act: bool, +} + +impl VgicIntInner { + fn new(id: usize) -> VgicIntInner { + VgicIntInner { + owner: None, + route: GICD_IROUTER_INV as u64, + phys: VgicIntPhys::Route(GICD_IROUTER_INV as u64), + id: (id + GIC_PRIVINT_NUM) as u16, + hw: false, + in_lr: false, + lr: 0, + enabled: false, + state: IrqState::IrqSInactive, + prio: 0xff, + targets: 0, + cfg: 0, + in_pend: false, + in_act: false, + } + } + + fn priv_new(id: usize, owner: Vcpu, targets: usize, enabled: bool, redist: usize, cfg: usize) -> VgicIntInner { + VgicIntInner { + owner: Some(owner), + route: GICD_IROUTER_INV as u64, + phys: VgicIntPhys::Redist(redist as u64), + id: id as u16, + hw: false, + in_lr: false, + lr: 0, + enabled, + state: IrqState::IrqSInactive, + prio: 0xff, + targets: targets as u8, + cfg: cfg as u8, + in_pend: false, + in_act: false, + } + } + + fn owner_vm(&self) -> Vm { + let owner = self.owner.as_ref().unwrap(); + owner.vm().unwrap() + } +} + +/// VGIC Distributor +struct Vgicd { + ctlr: u32, + typer: u32, + iidr: u32, + interrupts: Vec, +} + +impl Vgicd { + fn default() -> Vgicd { + Vgicd { + ctlr: 0, + typer: 0, + iidr: 0, + interrupts: Vec::new(), + } + } +} + +#[derive(Clone, Copy)] +pub struct Sgis { + pub pend: u8, + pub act: u8, +} + +impl Sgis { + fn default() -> Sgis { + Sgis { pend: 0, act: 0 } + } +} + +/// VGIC Redistributor +struct Vgicr { + inner: Arc>, + pub lock: Arc>, +} + +impl Vgicr { + fn default() -> Vgicr { + Vgicr { + inner: Arc::new(Mutex::new(VgicrInner::default())), + lock: Arc::new(Mutex::new(())), + } + } + fn new(typer: usize, cltr: usize, iidr: usize) -> Vgicr { + Vgicr { + inner: Arc::new(Mutex::new(VgicrInner::new(typer, cltr, iidr))), + lock: Arc::new(Mutex::new(())), + } + } + + pub fn get_typer(&self) -> u64 { + let vgicr = self.inner.lock(); + vgicr.typer + } + + pub fn set_typer(&self, typer: usize) { + let mut vgicr = self.inner.lock(); + vgicr.typer = typer as u64; + } +} + +struct VgicrInner { + typer: u64, + cltr: u32, + iidr: u32, +} + +impl VgicrInner { + fn default() -> VgicrInner { + VgicrInner { + typer: 0, + cltr: 0, + iidr: 0, + } + } + + fn new(typer: usize, cltr: usize, iidr: usize) -> VgicrInner { + VgicrInner { + typer: typer as u64, + cltr: cltr as u32, + iidr: iidr as u32, + } + } +} + +/// VGIC CPU Private data +struct VgicCpuPriv { + vigcr: Vgicr, + // gich: GicHypervisorInterfaceBlock, + curr_lrs: [u16; GIC_LIST_REGS_NUM], + sgis: [Sgis; GIC_SGIS_NUM], + interrupts: Vec, + + pend_list: VecDeque, + act_list: VecDeque, +} + +impl VgicCpuPriv { + fn default() -> VgicCpuPriv { + VgicCpuPriv { + vigcr: Vgicr::default(), + curr_lrs: [0; GIC_LIST_REGS_NUM], + sgis: [Sgis::default(); GIC_SGIS_NUM], + interrupts: Vec::new(), + pend_list: VecDeque::new(), + act_list: VecDeque::new(), + } + } + + fn new(typer: usize, cltr: usize, iidr: usize) -> VgicCpuPriv { + VgicCpuPriv { + vigcr: Vgicr::new(typer, cltr, iidr), + curr_lrs: [0; GIC_LIST_REGS_NUM], + sgis: [Sgis::default(); GIC_SGIS_NUM], + interrupts: Vec::new(), + pend_list: VecDeque::new(), + act_list: VecDeque::new(), + } + } +} + +/// VGIC general struct +pub struct Vgic { + vgicd: Mutex, + cpu_priv: Mutex>, +} + +impl Vgic { + pub fn default() -> Vgic { + Vgic { + vgicd: Mutex::new(Vgicd::default()), + cpu_priv: Mutex::new(Vec::new()), + } + } + + fn remove_int_list(&self, vcpu: Vcpu, interrupt: VgicInt, is_pend: bool) { + let mut cpu_priv = self.cpu_priv.lock(); + let vcpu_id = vcpu.id(); + let int_id = interrupt.id(); + if interrupt.in_lr() { + if is_pend { + if !interrupt.in_pend() { + return; + } + for i in 0..cpu_priv[vcpu_id].pend_list.len() { + if cpu_priv[vcpu_id].pend_list[i].id() == int_id { + cpu_priv[vcpu_id].pend_list.remove(i); + break; + } + } + interrupt.set_in_pend_state(false); + } else { + if !interrupt.in_act() { + return; + } + for i in 0..cpu_priv[vcpu_id].act_list.len() { + if cpu_priv[vcpu_id].act_list[i].id() == int_id { + cpu_priv[vcpu_id].act_list.remove(i); + break; + } + } + interrupt.set_in_act_state(false); + }; + } + } + + fn add_int_list(&self, vcpu: Vcpu, interrupt: VgicInt, is_pend: bool) { + let mut cpu_priv = self.cpu_priv.lock(); + let vcpu_id = vcpu.id(); + if !interrupt.in_lr() { + if is_pend { + interrupt.set_in_pend_state(true); + cpu_priv[vcpu_id].pend_list.push_back(interrupt); + } else { + interrupt.set_in_act_state(true); + cpu_priv[vcpu_id].act_list.push_back(interrupt); + } + } + } + + fn update_int_list(&self, vcpu: Vcpu, interrupt: VgicInt) { + let state = interrupt.state().to_num(); + + if state & IrqState::IrqSPend.to_num() != 0 && !interrupt.in_pend() { + self.add_int_list(vcpu.clone(), interrupt.clone(), true); + } else if state & IrqState::IrqSPend.to_num() == 0 { + self.remove_int_list(vcpu.clone(), interrupt.clone(), true); + } + + if state & IrqState::IrqSActive.to_num() != 0 && !interrupt.in_act() { + self.add_int_list(vcpu.clone(), interrupt.clone(), false); + } else if state & IrqState::IrqSActive.to_num() == 0 { + self.remove_int_list(vcpu.clone(), interrupt.clone(), false); + } + } + + fn int_list_head(&self, vcpu: Vcpu, is_pend: bool) -> Option { + let cpu_priv = self.cpu_priv.lock(); + let vcpu_id = vcpu.id(); + if is_pend { + if cpu_priv[vcpu_id].pend_list.is_empty() { + None + } else { + Some(cpu_priv[vcpu_id].pend_list[0].clone()) + } + } else if cpu_priv[vcpu_id].act_list.is_empty() { + None + } else { + Some(cpu_priv[vcpu_id].act_list[0].clone()) + } + } + + fn set_vgicd_ctlr(&self, ctlr: u32) { + let mut vgicd = self.vgicd.lock(); + vgicd.ctlr = ctlr; + } + + pub fn vgicd_ctlr(&self) -> u32 { + let vgicd = self.vgicd.lock(); + vgicd.ctlr + } + + pub fn vgicd_typer(&self) -> u32 { + let vgicd = self.vgicd.lock(); + vgicd.typer + } + + pub fn vgicd_iidr(&self) -> u32 { + let vgicd = self.vgicd.lock(); + vgicd.iidr + } + + fn vgicr_emul_typer_access(&self, emu_ctx: &EmuContext, vgicr_id: usize) { + let cpu_priv = self.cpu_priv.lock(); + if !emu_ctx.write { + current_cpu().set_gpr(emu_ctx.reg, cpu_priv[vgicr_id].vigcr.get_typer() as usize); + } + } + + fn vgicd_set_irouter(&self, vcpu: Vcpu, int_id: usize, val: usize) { + if let Some(interrupt) = self.get_int(vcpu.clone(), int_id) { + let interrupt_lock = interrupt.lock.lock(); + + if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { + self.remove_lr(vcpu.clone(), interrupt.clone()); + + let phys_route = if (val & GICD_IROUTER_IRM_BIT) != 0 { + cpuid2mpidr(vcpu.phys_id()) + } else { + match vcpu.vm().unwrap().get_vcpu_by_mpidr(val & MPIDR_AFF_MSK) { + Some(vcpu) => cpuid2mpidr(vcpu.phys_id()) & MPIDR_AFF_MSK, + _ => GICD_IROUTER_INV, + } + }; + interrupt.set_phys_route(phys_route); + interrupt.set_route(val & GICD_IROUTER_RES0_MSK); + if interrupt.hw() { + GICD.set_route(int_id, phys_route); + } + self.route(vcpu.clone(), interrupt.clone()); + vgic_int_yield_owner(vcpu.clone(), interrupt.clone()); + } else { + let m = IpiInitcMessage { + event: InitcEvent::VgicdRoute, + vm_id: vcpu.vm().unwrap().id(), + int_id: interrupt.id(), + val: val as u8, + }; + if !ipi_send_msg( + interrupt.owner().unwrap().phys_id(), + IpiType::IpiTIntc, + IpiInnerMsg::Initc(m), + ) { + print!( + "vgicd_set_irouter: Failed to send ipi message, target {} type {}", + interrupt.owner().unwrap().phys_id(), + 0 + ); + } + } + drop(interrupt_lock); + } + } + + fn cpu_priv_interrupt(&self, cpu_id: usize, idx: usize) -> VgicInt { + let cpu_priv = self.cpu_priv.lock(); + cpu_priv[cpu_id].interrupts[idx].clone() + } + + fn cpu_priv_curr_lrs(&self, cpu_id: usize, idx: usize) -> u16 { + let cpu_priv = self.cpu_priv.lock(); + cpu_priv[cpu_id].curr_lrs[idx] + } + + fn cpu_priv_sgis_pend(&self, cpu_id: usize, idx: usize) -> u8 { + let cpu_priv = self.cpu_priv.lock(); + cpu_priv[cpu_id].sgis[idx].pend + } + + fn cpu_priv_sgis_act(&self, cpu_id: usize, idx: usize) -> u8 { + let cpu_priv = self.cpu_priv.lock(); + cpu_priv[cpu_id].sgis[idx].act + } + + fn set_cpu_priv_curr_lrs(&self, cpu_id: usize, idx: usize, val: u16) { + let mut cpu_priv = self.cpu_priv.lock(); + cpu_priv[cpu_id].curr_lrs[idx] = val; + } + + fn set_cpu_priv_sgis_pend(&self, cpu_id: usize, idx: usize, pend: u8) { + let mut cpu_priv = self.cpu_priv.lock(); + cpu_priv[cpu_id].sgis[idx].pend = pend; + } + + fn set_cpu_priv_sgis_act(&self, cpu_id: usize, idx: usize, act: u8) { + let mut cpu_priv = self.cpu_priv.lock(); + cpu_priv[cpu_id].sgis[idx].act = act; + } + + fn vgicd_interrupt(&self, idx: usize) -> VgicInt { + let vgicd = self.vgicd.lock(); + vgicd.interrupts[idx].clone() + } + + fn get_int(&self, vcpu: Vcpu, int_id: usize) -> Option { + if int_id < GIC_PRIVINT_NUM { + let vcpu_id = vcpu.id(); + return Some(self.cpu_priv_interrupt(vcpu_id, int_id)); + } else if (GIC_PRIVINT_NUM..GIC_INTS_MAX).contains(&int_id) { + return Some(self.vgicd_interrupt(int_id - GIC_PRIVINT_NUM)); + } + None + } + + fn remove_lr(&self, vcpu: Vcpu, interrupt: VgicInt) -> bool { + if !vgic_owns(vcpu.clone(), interrupt.clone()) { + return false; + } + let int_lr = interrupt.lr(); + + if !interrupt.in_lr() { + return false; + } + + let mut lr_val = 0; + if let Some(lr) = gich_get_lr(interrupt.clone()) { + GICH.set_lr(int_lr as usize, 0); + lr_val = lr; + } + + interrupt.set_in_lr(false); + + let lr_state = bit_extract(lr_val, GICH_LR_STATE_OFF, GICH_LR_STATE_LEN); + if lr_state != IrqState::IrqSInactive.to_num() { + interrupt.set_state(IrqState::num_to_state(lr_state)); + + self.update_int_list(vcpu.clone(), interrupt.clone()); + + if (interrupt.state().to_num() & IrqState::IrqSPend.to_num() != 0) && interrupt.enabled() { + let hcr = GICH.hcr(); + GICH.set_hcr(hcr | GICH_HCR_NPIE_BIT); + } + return true; + } + false + } + + fn add_lr(&self, vcpu: Vcpu, interrupt: VgicInt) -> bool { + if !interrupt.enabled() || interrupt.in_lr() { + return false; + } + + let gic_lrs = gic_lrs(); + let mut lr_ind = None; + + let elrsr = GICH.elrsr(); + //look for empty lr for using whit ICH_ELRSR_EL2 + for i in 0..gic_lrs { + if bit_get(elrsr, i % 32) != 0 { + lr_ind = Some(i); + break; + } + } + + // if there is no empty then, replace one + if lr_ind.is_none() { + let mut pend_found = 0; + // let mut act_found = 0; + let mut min_prio_act = interrupt.prio() as usize; + let mut min_prio_pend = interrupt.prio() as usize; + let mut min_id_act = interrupt.id() as usize; + let mut min_id_pend = interrupt.id() as usize; + let mut act_ind = None; + let mut pend_ind = None; + + for i in 0..gic_lrs { + let lr = GICH.lr(i); + let lr_prio = (lr & GICH_LR_PRIO_MSK) >> GICH_LR_PRIO_OFF; + let lr_state = lr & GICH_LR_STATE_MSK; + let lr_id = (lr & GICH_LR_VID_MASK) >> GICH_LR_VID_OFF; + + // look for min_prio act/pend lr (the value bigger then prio smaller) + if lr_state & GICH_LR_STATE_ACT != 0 { + if lr_prio > min_prio_act || (lr_prio == min_prio_act && lr_id > min_id_act) { + min_id_act = lr_id; + min_prio_act = lr_prio; + act_ind = Some(i); + } + // act_found += 1; + } else if lr_state & GICH_LR_STATE_PND != 0 { + if lr_prio > min_prio_pend || (lr_prio == min_prio_pend && lr_id > min_id_pend) { + min_id_pend = lr_id; + min_prio_pend = lr_prio; + pend_ind = Some(i); + } + pend_found += 1; + } + } + + // replace pend first + if pend_found > 1 { + lr_ind = pend_ind; + } else { + lr_ind = act_ind; + } + + if let Some(idx) = lr_ind { + if let Some(spilled_int) = &self.get_int( + vcpu.clone(), + bit_extract(GICH.lr(idx), GICH_LR_VID_OFF, GICH_LR_VID_LEN), + ) { + if spilled_int.id() != interrupt.id() { + let spilled_int_lock = spilled_int.lock.lock(); + self.remove_lr(vcpu.clone(), spilled_int.clone()); + vgic_int_yield_owner(vcpu.clone(), spilled_int.clone()); + drop(spilled_int_lock); + } else { + self.remove_lr(vcpu.clone(), spilled_int.clone()); + vgic_int_yield_owner(vcpu.clone(), spilled_int.clone()); + } + } + } + } + + match lr_ind { + Some(idx) => { + self.write_lr(vcpu, interrupt, idx); + return true; + } + None => { + // turn on maintenance interrupts + if vgic_get_state(interrupt) & IrqState::IrqSPend.to_num() != 0 { + let hcr = GICH.hcr(); + //No Pending Interrupt Enable. Enables the signaling of a maintenance interrupt when there are no List registers with the State field set to 0b01 + // then a maintance interrupt will come + GICH.set_hcr(hcr | GICH_HCR_NPIE_BIT); + } + } + } + + false + } + + fn write_lr(&self, vcpu: Vcpu, interrupt: VgicInt, lr_ind: usize) { + let vcpu_id = vcpu.id(); + let int_id = interrupt.id() as usize; + let int_prio = interrupt.prio(); + + let prev_int_id = self.cpu_priv_curr_lrs(vcpu_id, lr_ind) as usize; + if prev_int_id != int_id && !gic_is_priv(prev_int_id) { + if let Some(prev_interrupt) = self.get_int(vcpu.clone(), prev_int_id) { + let prev_interrupt_lock = prev_interrupt.lock.lock(); + if vgic_owns(vcpu.clone(), prev_interrupt.clone()) + && prev_interrupt.in_lr() + && (prev_interrupt.lr() == lr_ind as u16) + { + prev_interrupt.set_in_lr(false); + vgic_int_yield_owner(vcpu.clone(), prev_interrupt.clone()); + } + drop(prev_interrupt_lock); + } + } + + let state = vgic_get_state(interrupt.clone()); + + let mut lr = (int_id << GICH_LR_VID_OFF) & GICH_LR_VID_MASK; + lr |= ((int_prio as usize) << GICH_LR_PRIO_OFF) & GICH_LR_PRIO_MSK; + + if vgic_int_is_hw(interrupt.clone()) { + lr |= GICH_LR_HW_BIT; + lr |= (int_id << GICH_LR_PID_OFF) & GICH_LR_PID_MSK; + if state == IrqState::IrqSPendActive.to_num() { + lr |= GICH_LR_STATE_ACT; + } else { + lr |= (state << GICH_LR_STATE_OFF) & GICH_LR_STATE_MSK; + } + } else { + if !gic_is_priv(int_id) && !vgic_int_is_hw(interrupt.clone()) { + lr |= GICH_LR_EOI_BIT; + } + + lr |= (state << GICH_LR_STATE_OFF) & GICH_LR_STATE_MSK; + } + + /* + * When the guest is using vGICv3, all the IRQs are Group 1. Group 0 + * would result in a FIQ, which will not be expected by the guest OS. + */ + lr |= GICH_LR_GRP_BIT; + + interrupt.set_state(IrqState::IrqSInactive); + interrupt.set_in_lr(true); + interrupt.set_lr(lr_ind as u16); + self.set_cpu_priv_curr_lrs(vcpu_id, lr_ind, int_id as u16); + + GICH.set_lr(lr_ind, lr); + self.update_int_list(vcpu, interrupt); + } + + fn route(&self, vcpu: Vcpu, interrupt: VgicInt) { + if let IrqState::IrqSInactive = interrupt.state() { + return; + } + + if !interrupt.enabled() { + return; + } + + if vgic_int_vcpu_is_target(&vcpu, &interrupt) { + self.add_lr(vcpu.clone(), interrupt.clone()); + } + + if !interrupt.in_lr() && vgic_int_has_other_target(interrupt.clone()) { + let vcpu_vm_id = vcpu.vm_id(); + let ipi_msg = IpiInitcMessage { + event: InitcEvent::VgicdRoute, + vm_id: vcpu_vm_id, + int_id: interrupt.id(), + val: 0, + }; + vgic_int_yield_owner(vcpu.clone(), interrupt.clone()); + let trglist = vgic_int_ptarget_mask(interrupt) & !(1 << vcpu.phys_id()); + for i in 0..PLAT_DESC.cpu_desc.num { + if trglist & (1 << i) != 0 { + ipi_send_msg(i, IpiType::IpiTIntc, IpiInnerMsg::Initc(ipi_msg)); + } + } + } + } + + fn set_enable(&self, vcpu: Vcpu, int_id: usize, en: bool) { + match self.get_int(vcpu.clone(), int_id) { + Some(interrupt) => { + let interrupt_lock = interrupt.lock.lock(); + if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { + if interrupt.enabled() ^ en { + interrupt.set_enabled(en); + self.remove_lr(vcpu.clone(), interrupt.clone()); + if interrupt.hw() { + if gic_is_priv(interrupt.id() as usize) { + GICR.set_enable(interrupt.id() as usize, en, interrupt.phys_redist() as u32); + } else { + GICD.set_enable(interrupt.id() as usize, en); + } + } + } + self.route(vcpu.clone(), interrupt.clone()); + vgic_int_yield_owner(vcpu, interrupt.clone()); + } else { + let int_phys_id = interrupt.owner_phys_id().unwrap(); + let vcpu_vm_id = vcpu.vm_id(); + let ipi_msg = IpiInitcMessage { + event: InitcEvent::VgicdSetEn, + vm_id: vcpu_vm_id, + int_id: interrupt.id(), + val: en as u8, + }; + if !ipi_send_msg(int_phys_id, IpiType::IpiTIntc, IpiInnerMsg::Initc(ipi_msg)) { + error!( + "vgicd_set_enable: Failed to send ipi message, target {} type {}", + int_phys_id, 0 + ); + } + } + drop(interrupt_lock); + } + None => { + error!("vgicd_set_enable: interrupt {} is illegal", int_id); + } + } + } + + fn get_enable(&self, vcpu: Vcpu, int_id: usize) -> bool { + self.get_int(vcpu, int_id).unwrap().enabled() + } + + fn set_pend(&self, vcpu: Vcpu, int_id: usize, pend: bool) { + if let Some(interrupt) = self.get_int(vcpu.clone(), int_id) { + let interrupt_lock = interrupt.lock.lock(); + if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { + self.remove_lr(vcpu.clone(), interrupt.clone()); + + let state = interrupt.state().to_num(); + if pend && ((state & 1) == 0) { + interrupt.set_state(IrqState::num_to_state(state | 1)); + } else if !pend && (state & 1) != 0 { + interrupt.set_state(IrqState::num_to_state(state & !1)); + } + + let state = interrupt.state().to_num(); + if interrupt.hw() { + if gic_is_priv(int_id) { + gic_set_state(interrupt.id() as usize, state, interrupt.phys_redist() as u32); + } else { + // GICD non`t need gicr_id + gic_set_state(interrupt.id() as usize, state, 0); + } + } + self.route(vcpu.clone(), interrupt.clone()); + vgic_int_yield_owner(vcpu, interrupt.clone()); + } else { + let vm_id = vcpu.vm_id(); + + let m = IpiInitcMessage { + event: InitcEvent::VgicdSetPend, + vm_id, + int_id: interrupt.id(), + val: pend as u8, + }; + match interrupt.owner() { + Some(owner) => { + let phys_id = owner.phys_id(); + + if !ipi_send_msg(phys_id, IpiType::IpiTIntc, IpiInnerMsg::Initc(m)) { + error!( + "vgicd_set_pend: Failed to send ipi message, target {} type {}", + phys_id, 0 + ); + } + } + None => { + panic!( + "set_pend: Core {} int {} has no owner", + current_cpu().id, + interrupt.id() + ); + } + } + } + drop(interrupt_lock); + } + } + + fn set_active(&self, vcpu: Vcpu, int_id: usize, act: bool) { + let interrupt_option = self.get_int(vcpu.clone(), bit_extract(int_id, 0, 10)); + if let Some(interrupt) = interrupt_option { + let interrupt_lock = interrupt.lock.lock(); + if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { + self.remove_lr(vcpu.clone(), interrupt.clone()); + let state = interrupt.state().to_num(); + if act && ((state & IrqState::IrqSActive.to_num()) == 0) { + interrupt.set_state(IrqState::num_to_state(state | IrqState::IrqSActive.to_num())); + } else if !act && (state & IrqState::IrqSActive.to_num()) != 0 { + interrupt.set_state(IrqState::num_to_state(state & !IrqState::IrqSActive.to_num())); + } + let state = interrupt.state().to_num(); + if interrupt.hw() { + let vgic_int_id = interrupt.id() as usize; + if gic_is_priv(vgic_int_id) { + gic_set_state( + vgic_int_id, + if state == 1 { 2 } else { state }, + interrupt.phys_redist() as u32, + ); + } else { + gic_set_state(vgic_int_id, if state == 1 { 2 } else { state }, 0); + } + } + self.route(vcpu.clone(), interrupt.clone()); + vgic_int_yield_owner(vcpu, interrupt.clone()); + } else { + let vm_id = vcpu.vm_id(); + + let m = IpiInitcMessage { + event: InitcEvent::VgicdSetPend, + vm_id, + int_id: interrupt.id(), + val: act as u8, + }; + let phys_id = interrupt.owner_phys_id().unwrap(); + if !ipi_send_msg(phys_id, IpiType::IpiTIntc, IpiInnerMsg::Initc(m)) { + error!( + "vgicd_set_active: Failed to send ipi message, target {} type {}", + phys_id, 0 + ); + } + } + drop(interrupt_lock); + } + } + + fn set_icfgr(&self, vcpu: Vcpu, int_id: usize, cfg: u8) { + if let Some(interrupt) = self.get_int(vcpu.clone(), int_id) { + let interrupt_lock = interrupt.lock.lock(); + if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { + interrupt.set_cfg(cfg); + if interrupt.hw() { + if gic_is_priv(interrupt.id() as usize) { + GICR.set_icfgr(interrupt.id() as usize, cfg, interrupt.phys_redist() as u32); + } else { + GICD.set_icfgr(interrupt.id() as usize, cfg); + } + } + self.route(vcpu.clone(), interrupt.clone()); + vgic_int_yield_owner(vcpu, interrupt.clone()); + } else { + let m = IpiInitcMessage { + event: InitcEvent::VgicdSetCfg, + vm_id: vcpu.vm_id(), + int_id: interrupt.id(), + val: cfg, + }; + if !ipi_send_msg( + interrupt.owner_phys_id().unwrap(), + IpiType::IpiTIntc, + IpiInnerMsg::Initc(m), + ) { + error!( + "set_icfgr: Failed to send ipi message, target {} type {}", + interrupt.owner_phys_id().unwrap(), + 0 + ); + } + } + drop(interrupt_lock); + } else { + unimplemented!(); + } + } + + fn get_icfgr(&self, vcpu: Vcpu, int_id: usize) -> u8 { + let interrupt_option = self.get_int(vcpu, int_id); + if let Some(interrupt) = interrupt_option { + interrupt.cfg() + } else { + unimplemented!(); + } + } + + fn set_prio(&self, vcpu: Vcpu, int_id: usize, mut prio: u8) { + let interrupt_option = self.get_int(vcpu.clone(), int_id); + prio &= 0xf0; // gicv3 allows 8 priority bits in non-secure state + + if let Some(interrupt) = interrupt_option { + let interrupt_lock = interrupt.lock.lock(); + if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { + if interrupt.prio() != prio { + self.remove_lr(vcpu.clone(), interrupt.clone()); + let prev_prio = interrupt.prio(); + interrupt.set_prio(prio); + if prio <= prev_prio { + self.route(vcpu.clone(), interrupt.clone()); + } + if interrupt.hw() { + if gic_is_priv(interrupt.id() as usize) { + GICR.set_prio(interrupt.id() as usize, prio, interrupt.phys_redist() as u32); + } else { + GICD.set_prio(interrupt.id() as usize, prio); + } + } + } + vgic_int_yield_owner(vcpu, interrupt.clone()); + } else { + let vm_id = vcpu.vm_id(); + + let m = IpiInitcMessage { + event: InitcEvent::VgicdSetPrio, + vm_id, + int_id: interrupt.id(), + val: prio, + }; + if !ipi_send_msg( + interrupt.owner_phys_id().unwrap(), + IpiType::IpiTIntc, + IpiInnerMsg::Initc(m), + ) { + error!( + "set_prio: Failed to send ipi message, target {} type {}", + interrupt.owner_phys_id().unwrap(), + 0 + ); + } + } + drop(interrupt_lock); + } + } + + fn get_prio(&self, vcpu: Vcpu, int_id: usize) -> u8 { + let interrupt_option = self.get_int(vcpu, int_id); + interrupt_option.unwrap().prio() + } + + pub fn inject(&self, vcpu: Vcpu, int_id: usize) { + let interrupt_option = self.get_int(vcpu.clone(), bit_extract(int_id, 0, 10)); + if let Some(interrupt) = interrupt_option { + if interrupt.hw() { + let interrupt_lock = interrupt.lock.lock(); + interrupt.set_owner(vcpu.clone()); + interrupt.set_state(IrqState::IrqSPend); + self.update_int_list(vcpu.clone(), interrupt.clone()); + interrupt.set_in_lr(false); + self.route(vcpu, interrupt.clone()); + drop(interrupt_lock); + } else { + self.set_pend(vcpu, int_id, true); + } + } + } + + fn emu_razwi(&self, emu_ctx: &EmuContext) { + if !emu_ctx.write { + current_cpu().set_gpr(emu_ctx.reg, 0); + } + } + + fn emu_irouter_access(&self, emu_ctx: &EmuContext) { + let first_int = (bit_extract(emu_ctx.address, 0, 16) - 0x6000) / 8; + let idx = emu_ctx.reg; + let mut val = if emu_ctx.write { current_cpu().get_gpr(idx) } else { 0 }; + + if emu_ctx.write { + self.vgicd_set_irouter(current_cpu().active_vcpu.clone().unwrap(), first_int, val); + } else { + if !gic_is_priv(first_int) { + val = self + .get_int(current_cpu().active_vcpu.clone().unwrap(), first_int) + .unwrap() + .route() as usize; + } + current_cpu().set_gpr(idx, val); + } + } + + fn emu_ctrl_access(&self, emu_ctx: &EmuContext) { + if emu_ctx.write { + let prev_ctlr = self.vgicd_ctlr(); + let idx = emu_ctx.reg; + self.set_vgicd_ctlr(current_cpu().get_gpr(idx) as u32 & 0x2 | GICD_CTLR_ARE_NS_BIT as u32); + if prev_ctlr ^ self.vgicd_ctlr() != 0 { + let enable = self.vgicd_ctlr() != 0; + let hcr = GICH.hcr(); + if enable { + GICH.set_hcr(hcr | GICH_HCR_EN_BIT); + } else { + GICH.set_hcr(hcr & !GICH_HCR_EN_BIT); + } + + let m = IpiInitcMessage { + event: InitcEvent::VgicdGichEn, + vm_id: active_vm_id(), + int_id: 0, + val: enable as u8, + }; + ipi_intra_broadcast_msg(active_vm().unwrap(), IpiType::IpiTIntc, IpiInnerMsg::Initc(m)); + } + } else { + let idx = emu_ctx.reg; + let val = self.vgicd_ctlr() as usize; + current_cpu().set_gpr(idx, val | GICD.ctlr() as usize); + } + } + + fn emu_typer_access(&self, emu_ctx: &EmuContext) { + if !emu_ctx.write { + let idx = emu_ctx.reg; + let val = self.vgicd_typer() as usize; + current_cpu().set_gpr(idx, val); + } else { + error!("emu_typer_access: can't write to RO reg"); + } + } + + fn emu_iidr_access(&self, emu_ctx: &EmuContext) { + if !emu_ctx.write { + let idx = emu_ctx.reg; + let val = self.vgicd_iidr() as usize; + current_cpu().set_gpr(idx, val); + } else { + error!("emu_iidr_access: can't write to RO reg"); + } + } + + fn emu_isenabler_access(&self, emu_ctx: &EmuContext) { + let first_int = ((emu_ctx.address & 0xffff) - 0x100) * 8; //emu_ctx.address - offsetof(GICD,ISENABLER) + let idx = emu_ctx.reg; + let mut val = if emu_ctx.write { current_cpu().get_gpr(idx) } else { 0 }; + + let vm_id = active_vm_id(); + let vm = match active_vm() { + Some(vm) => vm, + None => { + panic!("emu_isenabler_access: current vcpu.vm is none"); + } + }; + let mut vm_has_interrupt_flag = false; + + for i in 0..(emu_ctx.width * 8) { + if vm.has_interrupt(first_int + i) || vm.emu_has_interrupt(first_int + i) { + vm_has_interrupt_flag = true; + break; + } + } + if first_int >= 16 && !vm_has_interrupt_flag { + error!( + "emu_isenabler_access: vm[{}] does not have interrupt {}", + vm_id, first_int + ); + return; + } + + if emu_ctx.write { + for i in 0..(emu_ctx.width * 8) { + if bit_get(val, i) != 0 { + self.set_enable(current_cpu().active_vcpu.clone().unwrap(), first_int + i, true); + } + } + } else { + for i in 0..(emu_ctx.width * 8) { + if self.get_enable(current_cpu().active_vcpu.clone().unwrap(), first_int + i) { + val |= 1 << i; + } + } + let idx = emu_ctx.reg; + current_cpu().set_gpr(idx, val); + } + } + + fn emu_pendr_access(&self, emu_ctx: &EmuContext, set: bool) { + let first_int = if set { + // ISPEND emu_ctx.address - OFFSET(GICD/R,ISPENDR) + ((emu_ctx.address & 0xffff) - 0x0200) * 8 + } else { + // ICPEND emu_ctx.address - OFFSET(GICD/R,ICPENDR) + ((emu_ctx.address & 0xffff) - 0x0280) * 8 + }; + + let idx = emu_ctx.reg; + let mut val = if emu_ctx.write { current_cpu().get_gpr(idx) } else { 0 }; + + if emu_ctx.write { + for i in 0..(emu_ctx.width * 8) { + if bit_get(val, i) != 0 { + self.set_pend(current_cpu().active_vcpu.clone().unwrap(), first_int + i, set); + } + } + } else { + for i in 0..32 { + match self.get_int(current_cpu().active_vcpu.clone().unwrap(), first_int + i) { + Some(interrupt) => { + if vgic_get_state(interrupt.clone()) & IrqState::IrqSPend.to_num() + != IrqState::IrqSInactive.to_num() + { + val |= 1 << i; + } + } + None => { + unimplemented!(); + } + } + } + let idx = emu_ctx.reg; + current_cpu().set_gpr(idx, val); + } + } + + fn emu_pidr_access(&self, emu_ctx: &EmuContext) { + if !emu_ctx.write { + current_cpu().set_gpr(emu_ctx.reg, GICD.id(((emu_ctx.address & 0xff) - 0xd0) / 4) as usize); + } + } + + fn emu_ispendr_access(&self, emu_ctx: &EmuContext) { + self.emu_pendr_access(emu_ctx, true); + } + + fn emu_activer_access(&self, emu_ctx: &EmuContext, set: bool) { + let first_int = if set { + // ISACTIVE (emu_ctx.address - OFFSET(GICD/R, ISACTIVER) + 8 * ((emu_ctx.address & 0xffff) - 0x0300) + } else { + // ICACTIVE (emu_ctx.address - OFFSET(GICD/R, ICACTIVER) + 8 * ((emu_ctx.address & 0xffff) - 0x0380) + }; + let idx = emu_ctx.reg; + + let mut val = if emu_ctx.write { current_cpu().get_gpr(idx) } else { 0 }; + let vm_id = active_vm_id(); + let vm = match active_vm() { + Some(vm) => vm, + None => { + panic!("emu_activer_access: current vcpu.vm is none"); + } + }; + let mut vm_has_interrupt_flag = false; + + for i in 0..(emu_ctx.width * 8) { + if vm.has_interrupt(first_int + i) || vm.emu_has_interrupt(first_int + i) { + vm_has_interrupt_flag = true; + break; + } + } + if first_int >= 16 && !vm_has_interrupt_flag { + warn!( + "emu_activer_access: vm[{}] does not have interrupt {}", + vm_id, first_int + ); + return; + } + + if emu_ctx.write { + for i in 0..(emu_ctx.width * 8) { + if bit_get(val, i) != 0 { + self.set_active(current_cpu().active_vcpu.clone().unwrap(), first_int + i, set); + } + } + } else { + for i in 0..(emu_ctx.width * 8) { + match self.get_int(current_cpu().active_vcpu.clone().unwrap(), first_int + i) { + Some(interrupt) => { + if vgic_get_state(interrupt.clone()) & IrqState::IrqSActive.to_num() != 0 { + val |= 1 << i; + } + } + None => { + unimplemented!(); + } + } + } + let idx = emu_ctx.reg; + current_cpu().set_gpr(idx, val); + } + } + + fn emu_isactiver_access(&self, emu_ctx: &EmuContext) { + self.emu_activer_access(emu_ctx, true); + } + + fn emu_icenabler_access(&self, emu_ctx: &EmuContext) { + let first_int = ((emu_ctx.address & 0xffff) - 0x0180) * 8; //emu_ctx.address - OFFSET(GICR/D,ICENABLE) + let idx = emu_ctx.reg; + let mut val = if emu_ctx.write { current_cpu().get_gpr(idx) } else { 0 }; + + let vm_id = active_vm_id(); + let vm = match active_vm() { + Some(vm) => vm, + None => { + panic!("emu_activer_access: current vcpu.vm is none"); + } + }; + let mut vm_has_interrupt_flag = false; + + if emu_ctx.write { + for i in 0..32 { + if vm.has_interrupt(first_int + i) || vm.emu_has_interrupt(first_int + i) { + vm_has_interrupt_flag = true; + break; + } + } + if first_int >= 16 && !vm_has_interrupt_flag { + warn!( + "emu_icenabler_access: vm[{}] does not have interrupt {}", + vm_id, first_int + ); + return; + } + } + + if emu_ctx.write { + for i in 0..(emu_ctx.width * 8) { + if bit_get(val, i) != 0 { + self.set_enable(current_cpu().active_vcpu.clone().unwrap(), first_int + i, false); + } + } + } else { + for i in 0..(emu_ctx.width * 8) { + if self.get_enable(current_cpu().active_vcpu.clone().unwrap(), first_int + i) { + val |= 1 << i; + } + } + let idx = emu_ctx.reg; + current_cpu().set_gpr(idx, val); + } + } + + fn emu_icpendr_access(&self, emu_ctx: &EmuContext) { + self.emu_pendr_access(emu_ctx, false); + } + + fn emu_icativer_access(&self, emu_ctx: &EmuContext) { + self.emu_activer_access(emu_ctx, false); + } + + fn emu_probaser_access(&self, emu_ctx: &EmuContext) { + if emu_ctx.write { + GICR.set_propbaser(current_cpu().id, current_cpu().get_gpr(emu_ctx.reg)); + } else { + current_cpu().set_gpr(emu_ctx.reg, GICR.get_propbaser(current_cpu().id) as usize); + } + } + + fn emu_pendbaser_access(&self, emu_ctx: &EmuContext) { + if emu_ctx.write { + GICR.set_pendbaser(current_cpu().id, current_cpu().get_gpr(emu_ctx.reg)); + } else { + current_cpu().set_gpr(emu_ctx.reg, GICR.get_pendbaser(current_cpu().id) as usize); + } + } + + fn emu_icfgr_access(&self, emu_ctx: &EmuContext) { + let first_int = ((emu_ctx.address & 0xffff) - 0x0C00) * 8 / GIC_CONFIG_BITS; // emu_ctx.address - OFFSET(GICR/D,ICFGR) + + let vm_id = active_vm_id(); + let vm = match active_vm() { + Some(vm) => vm, + None => { + panic!("emu_icfgr_access: current vcpu.vm is none"); + } + }; + let mut vm_has_interrupt_flag = false; + + if emu_ctx.write { + for i in 0..emu_ctx.width * 8 { + if vm.has_interrupt(first_int + i) || vm.emu_has_interrupt(first_int + i) { + vm_has_interrupt_flag = true; + break; + } + } + if first_int >= 16 && !vm_has_interrupt_flag { + warn!("emu_icfgr_access: vm[{}] does not have interrupt {}", vm_id, first_int); + return; + } + } + + if emu_ctx.write { + let idx = emu_ctx.reg; + let cfg = current_cpu().get_gpr(idx); + let mut irq = first_int; + let mut bit = 0; + while bit < (emu_ctx.width * 8) { + self.set_icfgr( + current_cpu().active_vcpu.clone().unwrap(), + irq, + bit_extract(cfg as usize, bit, GIC_CONFIG_BITS) as u8, + ); + bit += 2; + irq += 1; + } + } else { + let mut cfg = 0; + let mut irq = first_int; + let mut bit = 0; + while bit < (emu_ctx.width * 8) { + cfg |= (self.get_icfgr(current_cpu().active_vcpu.clone().unwrap(), irq) as usize) << bit; + bit += 2; + irq += 1; + } + let idx = emu_ctx.reg; + current_cpu().set_gpr(idx, cfg); + } + } + + fn emu_ipriorityr_access(&self, emu_ctx: &EmuContext) { + let first_int = ((emu_ctx.address & 0xffff) - 0x0400) * 8 / GIC_PRIO_BITS; // emu_ctx.address - OFFSET(GICR/D,IPRIORITYR) + let idx = emu_ctx.reg; + let mut val = if emu_ctx.write { current_cpu().get_gpr(idx) } else { 0 }; + + let vm_id = active_vm_id(); + let vm = match active_vm() { + Some(vm) => vm, + None => { + panic!("emu_ipriorityr_access: current vcpu.vm is none"); + } + }; + let mut vm_has_interrupt_flag = false; + + if emu_ctx.write { + for i in 0..emu_ctx.width { + if vm.has_interrupt(first_int + i) || vm.emu_has_interrupt(first_int + i) { + vm_has_interrupt_flag = true; + break; + } + } + if first_int >= 16 && !vm_has_interrupt_flag { + warn!( + "emu_ipriorityr_access: vm[{}] does not have interrupt {}", + vm_id, first_int + ); + return; + } + } + + if emu_ctx.write { + for i in 0..emu_ctx.width { + self.set_prio( + current_cpu().active_vcpu.clone().unwrap(), + first_int + i, + bit_extract(val, GIC_PRIO_BITS * i, GIC_PRIO_BITS) as u8, + ); + } + } else { + for i in 0..emu_ctx.width { + val |= (self.get_prio(current_cpu().active_vcpu.clone().unwrap(), first_int + i) as usize) + << (GIC_PRIO_BITS * i); + } + let idx = emu_ctx.reg; + current_cpu().set_gpr(idx, val); + } + } + + fn handle_trapped_eoir(&self, vcpu: Vcpu) { + let gic_lrs = gic_lrs(); + // eisr():Interrupt Controller End of Interrupt Status Register + let mut lr_idx_opt = bitmap_find_nth(GICH.eisr() as usize, 0, gic_lrs, 1, true); + + while lr_idx_opt.is_some() { + let lr_idx = lr_idx_opt.unwrap(); + let lr_val = GICH.lr(lr_idx) as usize; + GICH.set_lr(lr_idx, 0); + + match self.get_int(vcpu.clone(), bit_extract(lr_val, GICH_LR_VID_OFF, GICH_LR_VID_LEN)) { + Some(interrupt) => { + let interrupt_lock = interrupt.lock.lock(); + interrupt.set_in_lr(false); + if (interrupt.id() as usize) < GIC_SGIS_NUM { + self.add_lr(vcpu.clone(), interrupt.clone()); + } else { + vgic_int_yield_owner(vcpu.clone(), interrupt.clone()); + } + drop(interrupt_lock); + } + None => { + continue; + } + } + lr_idx_opt = bitmap_find_nth(GICH.eisr() as usize, 0, gic_lrs, 1, true); + } + } + + fn vgic_highest_proi_spilled(&self, vcpu: &Vcpu, flag: bool) -> Option { + let cpu_priv = &self.cpu_priv.lock()[vcpu.id()]; + // info!("binding list_len:{}", binding.len()); + let array = [ + Some(cpu_priv.pend_list.iter()), + if flag { None } else { Some(cpu_priv.act_list.iter()) }, + ]; + let binding = array.into_iter().flatten().flatten(); + binding + .min_by_key(|x| (((x.prio() as u32) << 10) | x.id() as u32)) + .cloned() + } + + fn refill_lrs(&self, vcpu: Vcpu, flag: bool) { + let gic_lrs = gic_lrs(); + // ICH_ELRSR_EL2:locate a usable List register when the hypervisor is delivering an interrupt to a Guest OS. + let mut lr_idx_opt = bitmap_find_nth(GICH.elrsr(), 0, gic_lrs, 1, true); + // flag indicates that is no pending or not true:no pending flase:have pending,the we will look up active and pend + let mut new_flags = flag; + while lr_idx_opt.is_some() { + let interrupt_opt: Option = self.vgic_highest_proi_spilled(&vcpu, new_flags); + + match interrupt_opt { + Some(interrupt) => { + let interrupt_lock = interrupt.lock.lock(); + let got_ownership = vgic_int_get_owner(vcpu.clone(), interrupt.clone()); + if got_ownership { + self.write_lr(vcpu.clone(), interrupt.clone(), lr_idx_opt.unwrap()); + } + drop(interrupt_lock); + if !got_ownership { + continue; + } + } + None => { + let hcr = GICH.hcr(); + GICH.set_hcr(hcr & !(GICH_HCR_NPIE_BIT | GICH_HCR_UIE_BIT)); + break; + } + } + + new_flags = false; + lr_idx_opt = bitmap_find_nth(GICH.elrsr(), 0, gic_lrs, 1, true); + } + } + + fn eoir_highest_spilled_active(&self, vcpu: Vcpu) { + let cpu_priv = self.cpu_priv.lock(); + let binding = &cpu_priv[vcpu.id()].act_list; + let interrupt = binding + .iter() + .min_by_key(|x| (((x.prio() as u32) << 10) | x.id() as u32)) + .cloned(); + drop(cpu_priv); + if let Some(int) = interrupt { + int.lock.lock(); + vgic_int_get_owner(vcpu.clone(), int.clone()); + + let state = int.state().to_num(); + int.set_state(IrqState::num_to_state(state & !2)); + self.update_int_list(vcpu.clone(), int.clone()); + + if vgic_int_is_hw(int.clone()) { + gic_set_act(int.id() as usize, false, current_cpu().id as u32); + } else if int.state().to_num() & 1 != 0 { + self.add_lr(vcpu, int.clone()); + } + } + } +} + +#[inline(always)] +fn vgic_broadcast(interrupt: VgicInt) -> bool { + (interrupt.route() as usize & GICD_IROUTER_IRM_BIT) != 0 +} + +#[inline(always)] +fn vgicr_get_id(emu_ctx: &EmuContext) -> u32 { + ((emu_ctx.address - PLAT_DESC.arch_desc.gic_desc.gicr_addr) / size_of::()) as u32 +} + +fn vgicr_emul_ctrl_access(emu_ctx: &EmuContext) { + if !emu_ctx.write { + current_cpu().set_gpr(emu_ctx.reg, GICR.get_ctrl(current_cpu().id as u32) as usize); + } else { + GICR.set_ctrlr(current_cpu().id, current_cpu().get_gpr(emu_ctx.reg)); + } +} + +fn vgicr_emul_pidr_access(emu_ctx: &EmuContext, vgicr_id: usize) { + if !emu_ctx.write { + let pgicr_id = current_cpu() + .active_vcpu + .clone() + .unwrap() + .vm() + .unwrap() + .vcpuid_to_pcpuid(vgicr_id); + if let Ok(pgicr_id) = pgicr_id { + current_cpu().set_gpr( + emu_ctx.reg, + GICR.get_id(pgicr_id as u32, ((emu_ctx.address & 0xff) - 0xd0) / 4) as usize, + ); + } + } +} + +fn vgic_int_vcpu_is_target(vcpu: &Vcpu, interrupt: &VgicInt) -> bool { + let pri = gic_is_priv(interrupt.id() as usize); + let local = pri && (interrupt.phys_redist() as usize == vcpu.phys_id()); + let routed_here = !pri && (interrupt.phys_route() as usize ^ (MPIDR_EL1::read() as usize & MPIDR_AFF_MSK)) == 0; + let any = !pri && vgic_broadcast(interrupt.clone()); + + local || routed_here || any +} + +fn vgic_int_has_other_target(interrupt: VgicInt) -> bool { + let pri = gic_is_priv(interrupt.id() as usize); + if pri { + return false; + } + + let routed_here = !pri && (interrupt.phys_route() as usize ^ (MPIDR_EL1::read() as usize & MPIDR_AFF_MSK)) == 0; + let route_valid = interrupt.phys_route() as usize != GICD_IROUTER_INV; + let any = !pri && vgic_broadcast(interrupt.clone()); + + any || (!routed_here && route_valid) +} + +fn vgic_int_ptarget_mask(interrupt: VgicInt) -> usize { + if vgic_broadcast(interrupt.clone()) { + current_cpu().active_vcpu.clone().unwrap().vm().clone().unwrap().ncpu() & !(1 << current_cpu().id) + } else if cfg!(feature = "rk3588") { + (1 << (interrupt.phys_route() >> 8)) as usize + } else { + (1 << interrupt.phys_route()) as usize + } +} + +fn vgic_target_translate(vm: Vm, trgt: u32, v2p: bool) -> u32 { + let from = trgt.to_le_bytes(); + + let mut result = 0; + for (idx, val) in from + .map(|x| { + if v2p { + vm.vcpu_to_pcpu_mask(x as usize, 8) as u32 + } else { + vm.pcpu_to_vcpu_mask(x as usize, 8) as u32 + } + }) + .iter() + .enumerate() + { + result |= *val << (8 * idx); + if idx >= 4 { + panic!("illegal idx, from len {}", from.len()); + } + } + result +} + +fn vgic_owns(vcpu: Vcpu, interrupt: VgicInt) -> bool { + if gic_is_priv(interrupt.id() as usize) { + return true; + } + + let vcpu_id = vcpu.id(); + let pcpu_id = vcpu.phys_id(); + match interrupt.owner() { + Some(owner) => { + let owner_vcpu_id = owner.id(); + let owner_pcpu_id = owner.phys_id(); + owner_vcpu_id == vcpu_id && owner_pcpu_id == pcpu_id + } + None => false, + } +} + +fn vgic_get_state(interrupt: VgicInt) -> usize { + let mut state = interrupt.state().to_num(); + + if interrupt.in_lr() && interrupt.owner_phys_id().unwrap() == current_cpu().id { + let lr_option = gich_get_lr(interrupt.clone()); + if let Some(lr_val) = lr_option { + state = bit_extract(lr_val, GICH_LR_STATE_OFF, GICH_LR_STATE_LEN); + } + } + + state +} + +fn vgic_int_yield_owner(vcpu: Vcpu, interrupt: VgicInt) { + if !vgic_owns(vcpu, interrupt.clone()) + || interrupt.in_lr() + || (vgic_get_state(interrupt.clone()) & IrqState::IrqSActive.to_num() != 0) + { + return; + } + + interrupt.clear_owner(); +} + +#[inline(always)] +fn vgic_int_is_hw(interrupt: VgicInt) -> bool { + interrupt.id() as usize >= GIC_SGIS_NUM && interrupt.hw() +} + +fn gich_get_lr(interrupt: VgicInt) -> Option { + let cpu_id = current_cpu().id; + let phys_id = interrupt.owner_phys_id().unwrap(); + + if !interrupt.in_lr() || phys_id != cpu_id { + return None; + } + + let lr_val = GICH.lr(interrupt.lr() as usize); + if (bit_extract(lr_val, GICH_LR_VID_OFF, GICH_LR_VID_LEN) == interrupt.id() as usize) + && (bit_extract(lr_val, GICH_LR_STATE_OFF, GICH_LR_STATE_LEN) != IrqState::IrqSInactive.to_num()) + { + return Some(lr_val); + } + None +} + +fn vgic_int_get_owner(vcpu: Vcpu, interrupt: VgicInt) -> bool { + let vcpu_id = vcpu.id(); + let vcpu_vm_id = vcpu.vm_id(); + + match interrupt.owner() { + Some(owner) => { + let owner_vcpu_id = owner.id(); + let owner_vm_id = owner.vm_id(); + + owner_vm_id == vcpu_vm_id && owner_vcpu_id == vcpu_id + } + None => { + interrupt.set_owner(vcpu); + true + } + } +} + +pub fn gic_maintenance_handler() { + let misr = GICH.misr(); + let vm = match active_vm() { + Some(vm) => vm, + None => { + panic!("gic_maintenance_handler: current vcpu.vm is None"); + } + }; + let vgic = vm.vgic(); + + if misr & (GICH_MISR_EOI as u32) != 0 { + vgic.handle_trapped_eoir(current_cpu().active_vcpu.clone().unwrap()); + } + + // NP:List Register Entry Not Present. + // U: underflow Zero or one of the List register entries are marked as a valid interrupt, that is, if the corresponding ICH_LR_EL2.State bits do not equal 0x0. + if misr & (GICH_MISR_NP as u32 | GICH_MISR_U as u32) != 0 { + vgic.refill_lrs( + current_cpu().active_vcpu.clone().unwrap(), + (misr & GICH_MISR_NP as u32) != 0, + ); + } + + if misr & (GICH_MISR_LRPEN as u32) != 0 { + let mut hcr = GICH.hcr(); + while hcr & GICH_HCR_EOIC_MSK != 0 { + vgic.eoir_highest_spilled_active(current_cpu().active_vcpu.clone().unwrap()); + hcr -= 1 << GICH_HCR_EOIC_OFF; + GICH.set_hcr(hcr); + hcr = GICH.hcr(); + } + } +} + +const VGICD_REG_OFFSET_PREFIX_CTLR: usize = 0x0; +// same as TYPER & IIDR +const VGICD_REG_OFFSET_PREFIX_ISENABLER: usize = 0x2; +const VGICD_REG_OFFSET_PREFIX_ICENABLER: usize = 0x3; +const VGICD_REG_OFFSET_PREFIX_ISPENDR: usize = 0x4; +const VGICD_REG_OFFSET_PREFIX_ICPENDR: usize = 0x5; +const VGICD_REG_OFFSET_PREFIX_ISACTIVER: usize = 0x6; +const VGICD_REG_OFFSET_PREFIX_ICACTIVER: usize = 0x7; +const VGICD_REG_OFFSET_PREFIX_ICFGR: usize = 0x18; +const VGICD_REG_OFFSET_PREFIX_SGIR: usize = 0x1e; + +/// emulated interrupt controller handler +pub fn emu_intc_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { + let offset = emu_ctx.address & 0xffff; + + let vm = match crate::kernel::active_vm() { + None => { + panic!("emu_intc_handler: vm is None"); + } + Some(x) => x, + }; + + let vgic = vm.vgic(); + let vgicd_offset_prefix = offset >> 7; + trace!( + "current_cpu:{} emu_intc_handler offset:{:#x} is write:{},val:{:#x}", + current_cpu().id, + emu_ctx.address, + emu_ctx.write, + current_cpu().get_gpr(emu_ctx.reg) + ); + + match vgicd_offset_prefix { + VGICD_REG_OFFSET_PREFIX_ISENABLER => { + vgic.emu_isenabler_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ISPENDR => { + vgic.emu_ispendr_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ISACTIVER => { + vgic.emu_isactiver_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ICENABLER => { + vgic.emu_icenabler_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ICPENDR => { + vgic.emu_icpendr_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ICACTIVER => { + vgic.emu_icativer_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ICFGR => { + vgic.emu_icfgr_access(emu_ctx); + } + _ => { + match offset { + // VGICD_REG_OFFSET(CTLR) + 0 => { + vgic.emu_ctrl_access(emu_ctx); + } + // VGICD_REG_OFFSET(TYPER) + 0x004 => { + vgic.emu_typer_access(emu_ctx); + } + // VGICD_REG_OFFSET(IIDR) + 0x008 => { + vgic.emu_iidr_access(emu_ctx); + } + 0xf00 => { + vgic.emu_razwi(emu_ctx); + } + _ => { + if (0x400..0x800).contains(&offset) { + vgic.emu_ipriorityr_access(emu_ctx); + } else if (0x800..0xc00).contains(&offset) { + vgic.emu_razwi(emu_ctx); + } else if (0x6000..0x8000).contains(&offset) { + vgic.emu_irouter_access(emu_ctx); + } else if (0xffd0..0x10000).contains(&offset) { + //ffe8 is GICD_PIDR2, Peripheral ID2 Register + vgic.emu_pidr_access(emu_ctx); + } else { + vgic.emu_razwi(emu_ctx); + } + } + } + } + } + true +} + +pub fn vgicd_emu_access_is_vaild(emu_ctx: &EmuContext) -> bool { + let offset = emu_ctx.address & 0xffff; + let offset_prefix = (offset & 0xff80) >> 7; + match offset_prefix { + VGICD_REG_OFFSET_PREFIX_CTLR + | VGICD_REG_OFFSET_PREFIX_ISENABLER + | VGICD_REG_OFFSET_PREFIX_ISPENDR + | VGICD_REG_OFFSET_PREFIX_ISACTIVER + | VGICD_REG_OFFSET_PREFIX_ICENABLER + | VGICD_REG_OFFSET_PREFIX_ICPENDR + | VGICD_REG_OFFSET_PREFIX_ICACTIVER + | VGICD_REG_OFFSET_PREFIX_ICFGR => { + if emu_ctx.width != 4 || emu_ctx.address & 0x3 != 0 { + return false; + } + } + VGICD_REG_OFFSET_PREFIX_SGIR => { + if (emu_ctx.width == 4 && emu_ctx.address & 0x3 != 0) || (emu_ctx.width == 2 && emu_ctx.address & 0x1 != 0) + { + return false; + } + } + _ => { + // TODO: hard code to rebuild (gicd IPRIORITYR and ITARGETSR) + if (0x400..0xc00).contains(&offset) + && ((emu_ctx.width == 4 && emu_ctx.address & 0x3 != 0) + || (emu_ctx.width == 2 && emu_ctx.address & 0x1 != 0)) + { + return false; + } + } + } + true +} + +/// partial passthrough interrupt controller handler +pub fn partial_passthrough_intc_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { + if !vgicd_emu_access_is_vaild(emu_ctx) { + return false; + } + if emu_ctx.write { + // SAFETY: Emu_ctx.address is writeable in EL2 + unsafe { + emu_ctx.write(current_cpu().get_gpr(emu_ctx.reg)); + } + } else { + // SAFETY: Emu_ctx.address is readable in EL2 + current_cpu().set_gpr(emu_ctx.reg, unsafe { emu_ctx.read() }); + } + + true +} + +pub fn vgic_ipi_handler(msg: &IpiMessage) { + let vm_id; + let int_id; + let val; + match &msg.ipi_message { + IpiInnerMsg::Initc(intc) => { + vm_id = intc.vm_id; + int_id = intc.int_id; + val = intc.val; + } + _ => { + error!("vgic_ipi_handler: illegal ipi"); + return; + } + } + let trgt_vcpu = match current_cpu().vcpu_array.pop_vcpu_through_vmid(vm_id) { + None => { + error!("Core {} received vgic msg from unknown VM {}", current_cpu().id, vm_id); + return; + } + Some(vcpu) => vcpu, + }; + restore_vcpu_gic(current_cpu().active_vcpu.clone(), trgt_vcpu.clone()); + + let vm = match trgt_vcpu.vm() { + None => { + panic!("vgic_ipi_handler: vm is None"); + } + Some(x) => x, + }; + let vgic = vm.vgic(); + + if vm_id != vm.id() { + error!("VM {} received vgic msg from another vm {}", vm.id(), vm_id); + return; + } + if let IpiInnerMsg::Initc(intc) = &msg.ipi_message { + match intc.event { + InitcEvent::VgicdGichEn => { + let hcr = GICH.hcr(); + if val != 0 { + GICH.set_hcr(hcr | GICH_HCR_EN_BIT); + } else { + GICH.set_hcr(hcr & !GICH_HCR_EN_BIT); + } + } + InitcEvent::VgicdSetEn => { + vgic.set_enable(trgt_vcpu.clone(), int_id as usize, val != 0); + } + InitcEvent::VgicdSetPend => { + vgic.set_pend(trgt_vcpu.clone(), int_id as usize, val != 0); + } + InitcEvent::VgicdSetPrio => { + vgic.set_prio(trgt_vcpu.clone(), int_id as usize, val); + } + InitcEvent::VgicdSetCfg => { + vgic.set_icfgr(trgt_vcpu.clone(), int_id as usize, val); + } + InitcEvent::VgicdRoute => { + let interrupt_option = vgic.get_int(trgt_vcpu.clone(), bit_extract(int_id as usize, 0, 10)); + if let Some(interrupt) = interrupt_option { + let interrupt_lock = interrupt.lock.lock(); + if vgic_int_get_owner(trgt_vcpu.clone(), interrupt.clone()) { + if vgic_int_vcpu_is_target(&trgt_vcpu, &interrupt) { + vgic.add_lr(trgt_vcpu.clone(), interrupt.clone()); + } + vgic_int_yield_owner(trgt_vcpu.clone(), interrupt.clone()); + } + drop(interrupt_lock); + } + } + InitcEvent::Vgicdinject => { + crate::kernel::interrupt_vm_inject(trgt_vcpu.vm().unwrap(), trgt_vcpu.clone(), int_id as usize); + } + _ => { + error!("vgic_ipi_handler: core {} received unknown event", current_cpu().id) + } + } + } + save_vcpu_gic(current_cpu().active_vcpu.clone(), trgt_vcpu); +} + +/// emulated interrupt controller initialize +pub fn emu_intc_init(vm: Vm, emu_dev_id: usize) { + let vgic_cpu_num = vm.config().cpu_num(); + vm.init_intc_mode(true); + + let vgic = Arc::new(Vgic::default()); + + let mut vgicd = vgic.vgicd.lock(); + vgicd.typer = (GICD.typer() & !(GICD_TYPER_CPUNUM_MSK | GICD_TYPER_LPIS) as u32) + | ((((vm.cpu_num() - 1) << GICD_TYPER_CPUNUM_OFF) & GICD_TYPER_CPUNUM_MSK) as u32); + vgicd.iidr = GICD.iidr(); + vgicd.ctlr = 0b10; + + for i in 0..GIC_SPI_MAX { + vgicd.interrupts.push(VgicInt::new(i)); + } + drop(vgicd); + + for i in 0..vgic_cpu_num { + let mut typer = i << GICR_TYPER_PRCNUM_OFF; + let vmpidr = vm.vcpu(i).unwrap().get_vmpidr(); + typer |= (vmpidr & MPIDR_AFF_MSK) << GICR_TYPER_AFFVAL_OFF; + typer |= !!((i == (vm.cpu_num() - 1)) as usize) << GICR_TYPER_LAST_OFF; + //need the low 6 bits for LPI/ITS init + //DPGS, bit [5]:Sets support for GICR_CTLR.DPG* bits + //DirectLPI, bit [3]: Indicates whether this Redistributor supports direct injection of LPIs. + //Dirty, bit [2]: Controls the functionality of GICR_VPENDBASER.Dirty. + //LPI VLPIS, bit [1]: Indicates whether the GIC implementation supports virtual LPIs and the direct injection of virtual LPIs + //PLPIS, bit [0]: Indicates whether the GIC implementation supports physical LPIs + typer |= 0b10_0001; + + let mut cpu_priv = VgicCpuPriv::new( + typer, + GICR.get_ctrl(vm.vcpu(i).unwrap().phys_id() as u32) as usize, + GICR.get_iidr(vm.vcpu(i).unwrap().phys_id()) as usize, + ); + + for int_idx in 0..GIC_SGIS_NUM { + let vcpu = vm.vcpu(i).unwrap(); + let phys_id = vcpu.phys_id(); + + cpu_priv.interrupts.push(VgicInt::priv_new( + int_idx, + vcpu.clone(), + 1 << phys_id, + true, + phys_id, + 0b10, + )); + } + + for int_idx in GIC_SGIS_NUM..GIC_PRIVINT_NUM { + let vcpu = vm.vcpu(i).unwrap(); + let phys_id = vcpu.phys_id(); + + cpu_priv.interrupts.push(VgicInt::priv_new( + int_idx, + vcpu.clone(), + 1 << phys_id, + false, + phys_id, + 0b0, + )); + } + + let mut vgic_cpu_priv = vgic.cpu_priv.lock(); + vgic_cpu_priv.push(cpu_priv); + drop(vgic_cpu_priv); + } + + vm.set_emu_devs(emu_dev_id, EmuDevs::Vgic(vgic.clone())); +} + +pub fn emu_vgicr_init(vm: Vm, emu_dev_id: usize) { + let vigc = vm.emu_dev(vm.intc_dev_id()).clone(); + vm.set_emu_devs(emu_dev_id, vigc); +} + +const VGICR_REG_OFFSET_CLTR: usize = 0x0; +const VGICR_REG_OFFSET_TYPER: usize = 0x8; +const VGICR_REG_OFFSET_PROPBASER: usize = 0x70; +const VGICR_REG_OFFSET_PENDBASER: usize = 0x78; +const VGICR_REG_OFFSET_ISENABLER0: usize = 0x10100; +const VGICR_REG_OFFSET_ISPENDR0: usize = 0x10200; +const VGICR_REG_OFFSET_ISACTIVER0: usize = 0x10300; +const VGICR_REG_OFFSET_ICENABLER0: usize = 0x10180; +const VGICR_REG_OFFSET_ICPENDR0: usize = 0x10280; +const VGICR_REG_OFFSET_ICACTIVER0: usize = 0x10380; +const VGICR_REG_OFFSET_ICFGR0: usize = 0x10c00; +const VGICR_REG_OFFSET_ICFGR1: usize = 0x10c04; + +#[derive(Debug)] +enum GicrRegs { + CLTR = 0x0, + TYPER = 0x8, + ISENABLER0 = 0x10100, + ISPENDR0 = 0x10200, + ISACTIVER0 = 0x10300, + ICENABLER0 = 0x10180, + ICPENDR0 = 0x10280, + ICACTIVER0 = 0x10380, + ICFGR0 = 0x10c00, + ICFGR1 = 0x10c04, + Others, +} + +impl From for GicrRegs { + fn from(val: usize) -> Self { + match val { + 0x0 => Self::CLTR, + 0x8 => Self::TYPER, + 0x10100 => Self::ISENABLER0, + 0x10200 => Self::ISPENDR0, + 0x10300 => Self::ISACTIVER0, + 0x10180 => Self::ICENABLER0, + 0x10280 => Self::ICPENDR0, + 0x10380 => Self::ICACTIVER0, + 0x10c00 => Self::ICFGR0, + 0x10c04 => Self::ICFGR1, + _ => Self::Others, + } + } +} + +pub fn emul_vgicr_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { + let vgic = current_cpu().active_vcpu.clone().unwrap().vm().unwrap().vgic(); + let vgicr_id = vgicr_get_id(emu_ctx); + let offset = emu_ctx.address & 0x1ffff; + + trace!( + "current_cpu:{}emul_vgicr_handler addr:{:#x} reg {:?} offset {:#x} is write:{}, val:{:#x}", + current_cpu().id, + emu_ctx.address, + GicrRegs::from(offset), + offset, + emu_ctx.write, + current_cpu().get_gpr(emu_ctx.reg) + ); + + match offset { + VGICR_REG_OFFSET_CLTR => { + vgicr_emul_ctrl_access(emu_ctx); + } + VGICR_REG_OFFSET_TYPER => { + vgic.vgicr_emul_typer_access(emu_ctx, vgicr_id as usize); + } + VGICR_REG_OFFSET_ISENABLER0 => { + vgic.emu_isenabler_access(emu_ctx); + } + VGICR_REG_OFFSET_ISPENDR0 => { + vgic.emu_ispendr_access(emu_ctx); + } + VGICR_REG_OFFSET_ISACTIVER0 => { + vgic.emu_isactiver_access(emu_ctx); + } + VGICR_REG_OFFSET_ICENABLER0 => { + vgic.emu_icenabler_access(emu_ctx); + } + VGICR_REG_OFFSET_ICPENDR0 => { + vgic.emu_icpendr_access(emu_ctx); + } + VGICR_REG_OFFSET_ICACTIVER0 => { + vgic.emu_icativer_access(emu_ctx); + } + VGICR_REG_OFFSET_ICFGR0 | VGICR_REG_OFFSET_ICFGR1 => { + vgic.emu_icfgr_access(emu_ctx); + } + VGICR_REG_OFFSET_PROPBASER => { + vgic.emu_probaser_access(emu_ctx); + } + VGICR_REG_OFFSET_PENDBASER => { + vgic.emu_pendbaser_access(emu_ctx); + } + _ => { + if (0x10400..0x10420).contains(&offset) { + vgic.emu_ipriorityr_access(emu_ctx); + } else if (0xffd0..0x10000).contains(&offset) { + vgicr_emul_pidr_access(emu_ctx, vgicr_id as usize); + } else { + vgic.emu_razwi(emu_ctx); + } + } + } + true +} + +pub fn partial_passthrough_intc_init(vm: Vm) { + vm.init_intc_mode(false); +} + +pub fn vgic_set_hw_int(vm: Vm, int_id: usize) { + if int_id < GIC_SGIS_NUM { + return; + } + + if !vm.has_vgic() { + return; + } + let vgic = vm.vgic(); + + if int_id < GIC_PRIVINT_NUM { + for i in 0..vm.cpu_num() { + if let Some(interrupt) = vgic.get_int(vm.vcpu(i).unwrap(), int_id) { + let interrupt_lock = interrupt.lock.lock(); + interrupt.set_hw(true); + drop(interrupt_lock); + } + } + } else if let Some(interrupt) = vgic.get_int(vm.vcpu(0).unwrap(), int_id) { + let interrupt_lock = interrupt.lock.lock(); + interrupt.set_hw(true); + drop(interrupt_lock); + } +} + +pub fn vgic_icc_sre_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { + if !emu_ctx.write { + current_cpu().set_gpr(emu_ctx.reg, 0x1); + } + true +} + +pub fn vgic_send_sgi_msg(vcpu: Vcpu, pcpu_mask: usize, int_id: usize) { + let m = IpiInitcMessage { + event: InitcEvent::Vgicdinject, + vm_id: vcpu.vm().clone().unwrap().id(), + int_id: int_id as u16, + val: true as u8, + }; + for i in 0..PLAT_DESC.cpu_desc.num { + if (pcpu_mask & (1 << i)) != 0 { + ipi_send_msg(i, IpiType::IpiTIntc, IpiInnerMsg::Initc(m)); + } + } +} + +pub fn vgic_icc_sgir_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { + if emu_ctx.write { + let sgir = current_cpu().get_gpr(emu_ctx.reg); + let int_id = bit_extract(sgir, GICC_SGIR_SGIINTID_OFF, GICC_SGIR_SGIINTID_LEN); + let targtlist = if (sgir & GICC_SGIR_IRM_BIT) != 0 { + current_cpu().active_vcpu.clone().unwrap().vm().unwrap().ncpu() & !(1 << current_cpu().id) + } else { + let vm = match current_cpu().active_vcpu.clone().unwrap().vm() { + Some(tvm) => tvm, + None => { + panic!("vgic_icc_sgir_handler: current vcpu.vm is none"); + } + }; + let mut vtarget = sgir & 0xffff; + // maybe surrort more cluseter (aff1 != 0) + if sgir & 0xff0000 != 0 && cfg!(feature = "rk3588") { + //for rk3588 the aff1 + vtarget <<= (sgir & 0xf0000) >> 16; + } + vgic_target_translate(vm, vtarget as u32, true) as usize + }; + vgic_send_sgi_msg(current_cpu().active_vcpu.clone().unwrap(), targtlist, int_id); + } + true +} diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 3e5d53c9b683a9ae12dd0ce9ba840a6662253f3d..ee3bb7275cce9e6ad1ac9692ecf231c62ceb0867 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -8,8 +8,17 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub use self::aarch64::*; -pub use self::traits::*; +//! Architecture module, including architecture-specific functions and data structures. +#[cfg(target_arch = "aarch64")] +pub use self::aarch64::*; +#[cfg(target_arch = "aarch64")] mod aarch64; -mod traits; + +#[cfg(target_arch = "riscv64")] +pub use self::riscv64::*; +#[cfg(target_arch = "riscv64")] +mod riscv64; + +pub use self::traits::*; +pub mod traits; diff --git a/src/driver/virtio/mod.rs b/src/arch/riscv64/interface.rs similarity index 67% rename from src/driver/virtio/mod.rs rename to src/arch/riscv64/interface.rs index d3ce3f34a9145eed10d5db1cfcabbd5fb97f0c19..c2fbd074fe62905a970185e6641021b55cc47245 100644 --- a/src/driver/virtio/mod.rs +++ b/src/arch/riscv64/interface.rs @@ -8,14 +8,11 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub use self::blk::*; -pub use self::mmio::*; -pub use self::ring::*; -pub use self::virtio::*; -pub use self::virtio_blk::*; +pub const PAGE_SIZE: usize = 4096; +pub const PAGE_SHIFT: usize = 12; +pub const ENTRY_PER_PAGE: usize = PAGE_SIZE / 8; -mod blk; -mod mmio; -mod ring; -mod virtio; -mod virtio_blk; +pub type ContextFrame = super::context_frame::Aarch64ContextFrame; + +pub const WORD_SIZE: usize = 8; +pub const PTE_PER_PAGE: usize = PAGE_SIZE / WORD_SIZE; diff --git a/src/arch/riscv64/mod.rs b/src/arch/riscv64/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..7f0acf34bfc28c9112670dc5b81b5ffc769a9b4d --- /dev/null +++ b/src/arch/riscv64/mod.rs @@ -0,0 +1 @@ +mod start; diff --git a/src/arch/riscv64/start.S b/src/arch/riscv64/start.S new file mode 100644 index 0000000000000000000000000000000000000000..1c6442418c5a523cf109a8a5276b9fc1ffd220da --- /dev/null +++ b/src/arch/riscv64/start.S @@ -0,0 +1,9 @@ +.option norvc + +// insert this file to boot section +.section .text.boot + +.global _start +_start: + j _start + diff --git a/src/arch/riscv64/start.rs b/src/arch/riscv64/start.rs new file mode 100644 index 0000000000000000000000000000000000000000..51122bd4ffa12c1765b4dc199307f0bd3d9f6215 --- /dev/null +++ b/src/arch/riscv64/start.rs @@ -0,0 +1,3 @@ +use core::arch::global_asm; + +global_asm!(include_str!("start.S")); diff --git a/src/driver/virtio/ring.rs b/src/arch/riscv64/tlb.rs similarity index 82% rename from src/driver/virtio/ring.rs rename to src/arch/riscv64/tlb.rs index b1030064a57aa7d3481e129ccf1662abe53042a5..bfb822a55c68f5dab5515e208ae53519c9629f13 100644 --- a/src/driver/virtio/ring.rs +++ b/src/arch/riscv64/tlb.rs @@ -8,5 +8,11 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub const VIRTIO_RING_F_INDIRECT_DESC: usize = 28; -pub const VIRTIO_RING_F_EVENT_IDX: usize = 29; +use core::arch::asm; + +pub fn tlb_invalidate_guest_all() { + unsafe { + asm!("hfence.vvma", "hfence.gvma"); + } +} + diff --git a/src/arch/traits.rs b/src/arch/traits.rs index a5c1bb60f5306e9126e5265206c3e5a781e4b734..16538e90bf3434a48a57e909a0bfc3a43d4021d4 100644 --- a/src/arch/traits.rs +++ b/src/arch/traits.rs @@ -7,7 +7,10 @@ // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use crate::kernel::Vm; +use crate::kernel::Vcpu; +/// Architecture-independent ContextFrame trait. pub trait ContextFrameTrait { fn new(pc: usize, sp: usize, arg: usize) -> Self; @@ -20,6 +23,7 @@ pub trait ContextFrameTrait { fn gpr(&self, index: usize) -> usize; } +/// Architecture-independent PageTableEntry trait. pub trait ArchPageTableEntryTrait { fn from_pte(value: usize) -> Self; fn from_pa(pa: usize) -> Self; @@ -27,6 +31,31 @@ pub trait ArchPageTableEntryTrait { fn to_pa(&self) -> usize; fn valid(&self) -> bool; fn entry(&self, index: usize) -> Self; - fn set_entry(&self, index: usize, value: Self); + unsafe fn set_entry(&self, index: usize, value: Self); fn make_table(frame_pa: usize) -> Self; } + +/// Architecture-independent Interrupt Context trait. +/// Interrupt Context: the context of interrupt controller of specific VM +pub trait InterruptContextTrait: Default { + fn save_state(&mut self); + fn restore_state(&self); +} + +pub trait InterruptController { + const NUM_MAX: usize; + const PRI_NUN_MAX: usize; + const IRQ_IPI: usize; + const IRQ_HYPERVISOR_TIMER: usize; + const IRQ_GUEST_TIMER: usize; + + fn init(); + fn enable(int_id: usize, en: bool); + fn fetch() -> Option; + fn clear(); + fn finish(int_id: usize); + fn ipi_send(cpu_id: usize, ipi_id: usize); + fn vm_inject(vm: Vm, vcpu: Vcpu, int_id: usize); + fn vm_register(vm: Vm, int_id: usize); + fn clear_current_irq(for_hypervisor: bool); +} diff --git a/src/board/mod.rs b/src/board/mod.rs index dbdae42e85609313a6f081cf5c04daf6eded21f5..d5b578473f3348c9dc3bb121bd64b53e9bc5350b 100644 --- a/src/board/mod.rs +++ b/src/board/mod.rs @@ -8,6 +8,8 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +//! Board-specific configuration + pub use self::platform_common::*; #[cfg(feature = "pi4")] @@ -16,11 +18,15 @@ pub use self::pi4::{Pi4Platform as Platform, PLAT_DESC}; pub use self::qemu::{QemuPlatform as Platform, PLAT_DESC}; #[cfg(feature = "tx2")] pub use self::tx2::{Tx2Platform as Platform, PLAT_DESC}; +#[cfg(feature = "rk3588")] +pub use self::rk3588::{Rk3588Platform as Platform, PLAT_DESC}; #[cfg(feature = "pi4")] mod pi4; mod platform_common; #[cfg(feature = "qemu")] mod qemu; +#[cfg(feature = "rk3588")] +mod rk3588; #[cfg(feature = "tx2")] mod tx2; diff --git a/src/board/pi4/mod.rs b/src/board/pi4/mod.rs index e79869a290f3854ae3a3e70668f5375678dfecb4..d447c379b9c90614d041cf3b16c366c02f3ff46e 100644 --- a/src/board/pi4/mod.rs +++ b/src/board/pi4/mod.rs @@ -8,6 +8,7 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub use self::platform::*; +//! Raspberry Pi 4 platform configuration +pub use self::platform::*; mod platform; diff --git a/src/board/pi4/platform.rs b/src/board/pi4/platform.rs index 824ddfd4b791828729243bba377eb6f556bf829c..6e08008fd76ec4735f35f4496a77124ac984273f 100644 --- a/src/board/pi4/platform.rs +++ b/src/board/pi4/platform.rs @@ -8,35 +8,47 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::arch::GicDesc; -use crate::arch::SmmuDesc; +// Crate imports for architecture and board configurations +use crate::arch::ArchDesc; use crate::board::{ - PlatOperation, Platform, PlatCpuCoreConfig, ArchDesc, PlatCpuConfig, PlatformConfig, PlatMemoryConfig, + PlatOperation, Platform, PlatCpuCoreConfig, ClusterDesc, PlatCpuConfig, PlatformConfig, PlatMemoryConfig, PlatMemRegion, }; + +/// Set scheduling for CPU cores use crate::board::SchedRule::RoundRobin; + +/// Import device-specific constants for ARM Cortex-A57 use crate::device::ARM_CORTEX_A57; + +/// Allow unused imports for potential future use #[allow(unused_imports)] + +/// Import device-specific constants for ARM Cortex-A57 use crate::device::ARM_NVIDIA_DENVER; +/// Represents the platform configuration for Raspberry Pi 4 pub struct Pi4Platform; +/// Definition platform operations for the Raspberry Pi 4 impl PlatOperation for Pi4Platform { + /// UART base addresses const UART_0_ADDR: usize = 0xFE201000; const UART_1_ADDR: usize = 0xFE201400; + /// UART interrupt numbers const UART_0_INT: usize = 32 + 0x79; const UART_1_INT: usize = 32 + 0x79; + /// Hypervisor UART base address const HYPERVISOR_UART_BASE: usize = Self::UART_0_ADDR; + /// GIC (Generic Interrupt Controller) base addresses const GICD_BASE: usize = 0xFF841000; const GICC_BASE: usize = 0xFF842000; const GICH_BASE: usize = 0xFF844000; const GICV_BASE: usize = 0xFF846000; - const SHARE_MEM_BASE: usize = 0x7_0000_0000; - // start sector number (LBA) const DISK_PARTITION_0_START: usize = 2048; const DISK_PARTITION_1_START: usize = 526336; @@ -44,38 +56,31 @@ impl PlatOperation for Pi4Platform { const DISK_PARTITION_3_START: usize = 34082816; const DISK_PARTITION_4_START: usize = 50862080; - // size in sector (512-byte) + /// size in sector (512-byte) const DISK_PARTITION_0_SIZE: usize = 524288; const DISK_PARTITION_1_SIZE: usize = 16777216; const DISK_PARTITION_2_SIZE: usize = 16777216; const DISK_PARTITION_3_SIZE: usize = 16777216; const DISK_PARTITION_4_SIZE: usize = 11471872; + /// Maps CPU ID to CPU interface number fn cpuid_to_cpuif(cpuid: usize) -> usize { cpuid } + /// Maps CPU interface number to CPU ID fn cpuif_to_cpuid(cpuif: usize) -> usize { cpuif } - - fn blk_init() { - todo!() - } - - fn blk_read(_sector: usize, _count: usize, _buf: usize) { - todo!() - } - - fn blk_write(_sector: usize, _count: usize, _buf: usize) { - todo!() - } } -pub static PLAT_DESC: PlatformConfig = PlatformConfig { +/// Static configuration for the Raspberry Pi 4 platform +pub const PLAT_DESC: PlatformConfig = PlatformConfig { + /// CPU configuration details cpu_desc: PlatCpuConfig { num: 4, core_list: &[ + /// Configuration for each CPU core PlatCpuCoreConfig { name: ARM_CORTEX_A57, mpidr: 0x80000000, @@ -97,9 +102,13 @@ pub static PLAT_DESC: PlatformConfig = PlatformConfig { sched: RoundRobin, }, ], + /// Cluster description for the CPU cores + cluster_desc: ClusterDesc { num: 1, core_num: &[4] }, }, + /// Memory configuration details mem_desc: PlatMemoryConfig { regions: &[ + /// Memory region configurations PlatMemRegion { base: 0xf0000000, size: 0xc000000, @@ -117,17 +126,21 @@ pub static PLAT_DESC: PlatformConfig = PlatformConfig { size: 0x100000000, }, ], + /// Memory base configuration base: 0xf0000000, }, + /// Architecture-specific configuration details arch_desc: ArchDesc { - gic_desc: GicDesc { + /// GIC (Generic Interrupt Controller) configuration + gic_desc: crate::arch::GicDesc { gicd_addr: Platform::GICD_BASE, gicc_addr: Platform::GICC_BASE, gich_addr: Platform::GICH_BASE, gicv_addr: Platform::GICV_BASE, maintenance_int_id: 25, }, - smmu_desc: SmmuDesc { + /// SMMU (System Memory Management Unit) configuration + smmu_desc: crate::arch::SmmuDesc { base: 0, interrupt_id: 0, global_mask: 0, diff --git a/src/board/platform_common.rs b/src/board/platform_common.rs index 91c63ffc62d239c7a99a3dab487b873f5d9422a5..060d5200b1b0450d9cb7bec8552f9f22aa8f73bf 100644 --- a/src/board/platform_common.rs +++ b/src/board/platform_common.rs @@ -8,55 +8,59 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::arch::GicDesc; -use crate::arch::SmmuDesc; +use crate::arch::ArchDesc; +/// Maximum number of CPUs supported by the platform pub const PLATFORM_CPU_NUM_MAX: usize = 8; -pub const PLATFORM_VCPU_NUM_MAX: usize = 8; -#[repr(C)] +/// Enum representing the scheduling rule for CPU cores pub enum SchedRule { + /// Round-robin scheduling RoundRobin, + /// No specific scheduling rule None, } -#[repr(C)] +/// Structure representing a memory region in the platform pub struct PlatMemRegion { pub base: usize, pub size: usize, } -#[repr(C)] +/// Configuration for the platform's memory pub struct PlatMemoryConfig { pub base: usize, pub regions: &'static [PlatMemRegion], } +/// Configuration for an individual CPU core pub struct PlatCpuCoreConfig { pub name: u8, pub mpidr: usize, pub sched: SchedRule, } -#[repr(C)] +/// Configuration for the platform's CPU pub struct PlatCpuConfig { pub num: usize, pub core_list: &'static [PlatCpuCoreConfig], + pub cluster_desc: ClusterDesc, } -#[repr(C)] -pub struct ArchDesc { - pub gic_desc: GicDesc, - pub smmu_desc: SmmuDesc, +/// Description of a CPU cluster +pub struct ClusterDesc { + pub num: usize, + pub core_num: &'static [usize], } -#[repr(C)] +/// Configuration for the entire platform, including CPU, memory, and architecture pub struct PlatformConfig { pub cpu_desc: PlatCpuConfig, pub mem_desc: PlatMemoryConfig, pub arch_desc: ArchDesc, } +/// Trait defining operations for platform management pub trait PlatOperation { // must offer UART_0 and UART_1 address const UART_0_ADDR: usize; @@ -75,6 +79,12 @@ pub trait PlatOperation { const GICC_BASE: usize; const GICH_BASE: usize; const GICV_BASE: usize; + #[cfg(feature = "gicv3")] + const GICR_BASE: usize; + #[cfg(feature = "gicv3")] + const ICC_SRE_ADDR: usize; + #[cfg(feature = "gicv3")] + const ICC_SGIR_ADDR: usize; const DISK_PARTITION_0_START: usize = usize::MAX; const DISK_PARTITION_1_START: usize = usize::MAX; @@ -89,49 +99,97 @@ pub trait PlatOperation { const DISK_PARTITION_3_SIZE: usize = usize::MAX; const DISK_PARTITION_4_SIZE: usize = usize::MAX; - const SHARE_MEM_BASE: usize; - - fn cpu_on(arch_core_id: usize, entry: usize, ctx: usize) { + /// # Safety: + /// The caller must ensure that the arch_core_id is in the range of the cpu_desc.core_list + /// The entry must be a valid address with executable permission + /// The ctx must be a valid cpu_idx + unsafe fn cpu_on(arch_core_id: usize, entry: usize, ctx: usize) { crate::arch::power_arch_cpu_on(arch_core_id, entry, ctx); } + /// Shuts down the current CPU fn cpu_shutdown() { crate::arch::power_arch_cpu_shutdown(); } + /// Powers on secondary cores of the CPU fn power_on_secondary_cores() { use super::PLAT_DESC; extern "C" { - fn _image_start(); + fn _secondary_start(); } for i in 1..PLAT_DESC.cpu_desc.num { - Self::cpu_on(PLAT_DESC.cpu_desc.core_list[i].mpidr, _image_start as usize, 0); + // SAFETY: + // We iterate all the cores except the primary core so the arch_core_id must be valid. + // Entry is a valid address with executable permission. + // The 'i' is a valid cpu_idx for ctx. + unsafe { + Self::cpu_on(PLAT_DESC.cpu_desc.core_list[i].mpidr, _secondary_start as usize, i); + } } } - fn sys_reboot() -> ! { - println!("Hypervisor reset..."); - crate::arch::power_arch_sys_reset(); + /// Reboots the system + /// # Safety: + /// The caller must ensure that the system can be reboot + unsafe fn sys_reboot() -> ! { + info!("Hypervisor reset..."); + // SAFETY: We are ready to reset the system when rebooting. + unsafe { + crate::arch::power_arch_sys_reset(); + } loop { core::hint::spin_loop(); } } - fn sys_shutdown() -> ! { - println!("Hypervisor shutdown..."); + /// Shuts down the system + /// # Safety: + /// The caller must ensure that the system can be shutdown + unsafe fn sys_shutdown() -> ! { + info!("Hypervisor shutdown..."); crate::arch::power_arch_sys_shutdown(); loop { core::hint::spin_loop(); } } + /// Maps a CPU ID to a CPU interface number fn cpuid_to_cpuif(cpuid: usize) -> usize; + /// Maps a CPU interface number to a CPU ID fn cpuif_to_cpuid(cpuif: usize) -> usize; - fn blk_init(); + /// Maps an MPIDR value to a CPU ID + /// + /// This function does not print to the console and is used to translate + /// MPIDR (Multiprocessor Affinity Register) values to CPU IDs. + // should not add any println!() + fn mpidr2cpuid(mpidr: usize) -> usize { + use crate::board::PLAT_DESC; + let mpidr = mpidr | 0x8000_0000; + for i in 0..PLAT_DESC.cpu_desc.num { + if mpidr == PLAT_DESC.cpu_desc.core_list[i].mpidr { + return i; + } + } + usize::MAX + } - fn blk_read(sector: usize, count: usize, buf: usize); + /// Maps a CPU ID to an MPIDR value + /// + /// This function is used for translating a CPU ID to its corresponding + /// MPIDR value. + fn cpuid2mpidr(cpuid: usize) -> usize { + use crate::board::PLAT_DESC; + PLAT_DESC.cpu_desc.core_list[cpuid].mpidr + } - fn blk_write(sector: usize, count: usize, buf: usize); + /// Maps a virtual MPIDR value to a virtual CPU ID + /// + /// This function is used in virtualized environments to translate + /// virtual MPIDR values to virtual CPU IDs. + fn vmpidr2vcpuid(vmpidr: usize) -> usize { + vmpidr & 0xff + } } diff --git a/src/board/qemu/mod.rs b/src/board/qemu/mod.rs index e79869a290f3854ae3a3e70668f5375678dfecb4..8e20f4b8d1ea1e228812c47c508cce84cb6e7305 100644 --- a/src/board/qemu/mod.rs +++ b/src/board/qemu/mod.rs @@ -8,6 +8,7 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub use self::platform::*; +//! QEMU simulation platform configuration +pub use self::platform::*; mod platform; diff --git a/src/board/qemu/platform.rs b/src/board/qemu/platform.rs index d9cde80fe695ed3db974bcce05f515b9825c8c03..c0d6c93a30dad0dce4fb9ea97322a0fc24a21944 100644 --- a/src/board/qemu/platform.rs +++ b/src/board/qemu/platform.rs @@ -8,35 +8,49 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +/// external fdt crate +extern crate fdt_rs; + // TODO: move these core name to device -use crate::arch::GicDesc; -use crate::arch::SmmuDesc; +/// Crate imports for architecture and board configurations +use crate::arch::ArchDesc; +#[cfg(feature = "gicv3")] +use crate::arch::sysreg_enc_addr; use crate::board::{ - PlatOperation, Platform, PlatCpuCoreConfig, ArchDesc, PlatCpuConfig, PlatformConfig, PlatMemoryConfig, - PlatMemRegion, + PlatOperation, Platform, PlatCpuCoreConfig, PlatCpuConfig, PlatformConfig, PlatMemoryConfig, PlatMemRegion, + ClusterDesc, }; use crate::board::SchedRule::RoundRobin; use crate::device::ARM_CORTEX_A57; -use crate::driver::{read, write}; +/// Represents the platform configuration for QEMU virtual machines pub struct QemuPlatform; +/// Implementation of platform operations for QEMU virtual machines impl PlatOperation for QemuPlatform { + /// UART base addresses const UART_0_ADDR: usize = 0x9000000; const UART_1_ADDR: usize = 0x9100000; const UART_2_ADDR: usize = 0x9110000; + /// UART interrupt numbers const UART_0_INT: usize = 32 + 0x70; const UART_1_INT: usize = 32 + 0x72; + /// Hypervisor UART base address const HYPERVISOR_UART_BASE: usize = Self::UART_0_ADDR; + /// GIC (Generic Interrupt Controller) base addresses for QEMU const GICD_BASE: usize = 0x08000000; const GICC_BASE: usize = 0x08010000; const GICH_BASE: usize = 0x08030000; const GICV_BASE: usize = 0x08040000; - - const SHARE_MEM_BASE: usize = 0x7_0000_0000; + #[cfg(feature = "gicv3")] + const GICR_BASE: usize = 0x080a0000; + #[cfg(feature = "gicv3")] + const ICC_SRE_ADDR: usize = sysreg_enc_addr(3, 0, 12, 12, 5); + #[cfg(feature = "gicv3")] + const ICC_SGIR_ADDR: usize = sysreg_enc_addr(3, 0, 12, 11, 5); const DISK_PARTITION_0_START: usize = 0; const DISK_PARTITION_1_START: usize = 2097152; @@ -47,32 +61,36 @@ impl PlatOperation for QemuPlatform { const DISK_PARTITION_1_SIZE: usize = 8192000; const DISK_PARTITION_2_SIZE: usize = 8192000; + /// Maps CPU ID to CPU interface number for QEMU fn cpuid_to_cpuif(cpuid: usize) -> usize { + // PLAT_DESC.cpu_desc.core_list[cpuid].mpidr cpuid } - + /// Maps CPU interface number to CPU ID for QEMU fn cpuif_to_cpuid(cpuif: usize) -> usize { cpuif } - fn blk_init() { - println!("Platform block driver init ok"); - crate::driver::virtio_blk_init(); - } - - fn blk_read(sector: usize, count: usize, buf: usize) { - read(sector, count, buf); - } - - fn blk_write(sector: usize, count: usize, buf: usize) { - write(sector, count, buf); + // should not add any println!() + /// Converts MPIDR to CPU ID for QEMU, ensuring it is in the valid range + fn mpidr2cpuid(mpidr: usize) -> usize { + let mpidr = mpidr & !0x8000_0000; + for i in 0..PLAT_DESC.cpu_desc.num { + if mpidr == PLAT_DESC.cpu_desc.core_list[i].mpidr { + return i; + } + } + usize::MAX } } -pub static PLAT_DESC: PlatformConfig = PlatformConfig { +/// Static configuration for the QEMU platform +pub const PLAT_DESC: PlatformConfig = PlatformConfig { + /// CPU configuration details for QEMU cpu_desc: PlatCpuConfig { num: 4, core_list: &[ + /// Configuration for each CPU core in QEMU PlatCpuCoreConfig { name: ARM_CORTEX_A57, mpidr: 0, @@ -94,7 +112,10 @@ pub static PLAT_DESC: PlatformConfig = PlatformConfig { sched: RoundRobin, }, ], + /// Cluster description for the CPU cores + cluster_desc: ClusterDesc { num: 1, core_num: &[4] }, }, + // Memory configuration details for QEMU mem_desc: PlatMemoryConfig { regions: &[ // reserve 0x48000000 ~ 0x48100000 for QEMU dtb @@ -102,6 +123,7 @@ pub static PLAT_DESC: PlatformConfig = PlatformConfig { base: 0x40000000, size: 0x08000000, }, + // General memory region for QEMU PlatMemRegion { base: 0x50000000, size: 0x1f0000000, @@ -109,15 +131,20 @@ pub static PLAT_DESC: PlatformConfig = PlatformConfig { ], base: 0x40000000, }, + // Architecture-specific configuration for QEMU arch_desc: ArchDesc { - gic_desc: GicDesc { + // GIC (Generic Interrupt Controller) configuration for QEMU + gic_desc: crate::arch::GicDesc { gicd_addr: Platform::GICD_BASE, gicc_addr: Platform::GICC_BASE, gich_addr: Platform::GICH_BASE, gicv_addr: Platform::GICV_BASE, + #[cfg(feature = "gicv3")] + gicr_addr: Platform::GICR_BASE, maintenance_int_id: 25, }, - smmu_desc: SmmuDesc { + // SMMU (System Memory Management Unit) configuration for QEMU + smmu_desc: crate::arch::SmmuDesc { base: 0, interrupt_id: 0, global_mask: 0, diff --git a/src/driver/aarch64/mod.rs b/src/board/rk3588/mod.rs similarity index 88% rename from src/driver/aarch64/mod.rs rename to src/board/rk3588/mod.rs index 4cff2612b1e4c15915a5663d43a62297f31e86d2..ff42d475447d3a72cecbb75e46a4752490cabaa7 100644 --- a/src/driver/aarch64/mod.rs +++ b/src/board/rk3588/mod.rs @@ -8,6 +8,7 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub use self::uart::*; +//! rk3588 platform configuration -mod uart; +pub use self::platform::*; +mod platform; diff --git a/src/board/rk3588/platform.rs b/src/board/rk3588/platform.rs new file mode 100644 index 0000000000000000000000000000000000000000..d9eaa293e33b9d75042a0c452dcbd30453e972b8 --- /dev/null +++ b/src/board/rk3588/platform.rs @@ -0,0 +1,191 @@ +// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. +// Rust-Shyper is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +/// Crate imports for architecture and board configurations +use crate::arch::ArchDesc; +use crate::arch::sysreg_enc_addr; +use crate::board::{ + PlatOperation, Platform, PlatCpuCoreConfig, ClusterDesc, PlatCpuConfig, PlatformConfig, PlatMemoryConfig, + PlatMemRegion, +}; +use crate::board::SchedRule::RoundRobin; +use crate::device::ARM_CORTEX_A55; +use crate::device::ARM_CORTEX_A76; +#[allow(unused_imports)] +use crate::device::ARM_NVIDIA_DENVER; + +/// Represents the platform configuration for Rockchip RK3588 +pub struct Rk3588Platform; + +/// Implementation of platform operations for Rockchip RK3588 +impl PlatOperation for Rk3588Platform { + /// UART base addresses + const UART_0_ADDR: usize = 0xfeb50000; + const UART_1_ADDR: usize = 0xfeba0000; + + /// UART interrupt numbers + const UART_0_INT: usize = 32 + 0x12d; + const UART_1_INT: usize = 32 + 0x134; + + /// Hypervisor UART base address + const HYPERVISOR_UART_BASE: usize = Self::UART_0_ADDR; + + /// GIC (Generic Interrupt Controller) base addresses + const GICD_BASE: usize = 0xfe600000; + const GICC_BASE: usize = 0xfe610000; + const GICH_BASE: usize = 0xfe620000; + const GICV_BASE: usize = 0xfe630000; + const GICR_BASE: usize = 0xfe680000; + + /// start sector number (LBA) + const DISK_PARTITION_0_START: usize = 16384; + const DISK_PARTITION_1_START: usize = 32768; + const DISK_PARTITION_2_START: usize = 40960; + + /// size in sector (512-byte) + /// pub const DISK_PARTITION_TOTAL_SIZE: usize = 31457280; + const DISK_PARTITION_0_SIZE: usize = 16384; + const DISK_PARTITION_1_SIZE: usize = 8192; + const DISK_PARTITION_2_SIZE: usize = 524288; + + //sysreg + const ICC_SRE_ADDR: usize = sysreg_enc_addr(3, 0, 12, 12, 5); + const ICC_SGIR_ADDR: usize = sysreg_enc_addr(3, 0, 12, 11, 5); + + /// Converts MPIDR to CPU ID for RK3588 + fn mpidr2cpuid(mpidr: usize) -> usize { + (mpidr >> 8) & 0xff + } + + /// Maps CPU ID to CPU interface number for RK3588 + fn cpuid_to_cpuif(cpuid: usize) -> usize { + PLAT_DESC.cpu_desc.core_list[cpuid].mpidr + } + + /// Maps CPU interface number to CPU ID for RK3588 + fn cpuif_to_cpuid(cpuif: usize) -> usize { + cpuif + } + + /// Converts virtual MPIDR to virtual CPU ID for RK3588 + fn vmpidr2vcpuid(vmpidr: usize) -> usize { + (vmpidr >> 8) & 0xff + } +} + +/// Static configuration for the Rockchip RK3588 platform +pub const PLAT_DESC: PlatformConfig = PlatformConfig { + /// CPU configuration details for RK3588 + cpu_desc: PlatCpuConfig { + num: 8, + core_list: &[ + /// Configuration for each CPU core in cluster 0 + PlatCpuCoreConfig { + //cluster0 + name: ARM_CORTEX_A55, + mpidr: 0x81000000, + sched: RoundRobin, + }, + PlatCpuCoreConfig { + //cluster0 + name: ARM_CORTEX_A55, + mpidr: 0x81000100, + sched: RoundRobin, + }, + PlatCpuCoreConfig { + //cluster0 + name: ARM_CORTEX_A55, + mpidr: 0x81000200, + sched: RoundRobin, + }, + PlatCpuCoreConfig { + //cluster0 + name: ARM_CORTEX_A55, + mpidr: 0x81000300, + sched: RoundRobin, + }, + PlatCpuCoreConfig { + //cluster1 + name: ARM_CORTEX_A76, + mpidr: 0x81000400, + sched: RoundRobin, + }, + PlatCpuCoreConfig { + //cluster1 + name: ARM_CORTEX_A76, + mpidr: 0x81000500, + sched: RoundRobin, + }, + PlatCpuCoreConfig { + //cluster2 + name: ARM_CORTEX_A76, + mpidr: 0x81000600, + sched: RoundRobin, + }, + PlatCpuCoreConfig { + //cluster2 + name: ARM_CORTEX_A76, + mpidr: 0x81000700, + sched: RoundRobin, + }, + ], + /// Cluster description for the CPU cores + cluster_desc: ClusterDesc { + num: 3, + core_num: &[4, 2, 2], + }, + }, + /// Memory configuration details for RK3588 + mem_desc: PlatMemoryConfig { + regions: &[ + /// Memory region configurations + PlatMemRegion { + base: 0x200000, + size: 0x8200000, + }, + PlatMemRegion { + base: 0x9400000, + size: 0x76c00000, + }, + PlatMemRegion { + base: 0x80000000, + size: 0x80000000, + }, + PlatMemRegion { + base: 0x100000000, + size: 0x80000000, + }, + PlatMemRegion { + base: 0x180000000, + size: 0x80000000, + }, + ], + /// Memory base configuration + base: 0x200000, + }, + /// Architecture-specific configuration for RK3588 + arch_desc: ArchDesc { + /// GIC (Generic Interrupt Controller) configuration for RK3588 + gic_desc: crate::arch::GicDesc { + gicd_addr: Platform::GICD_BASE, + gicc_addr: Platform::GICC_BASE, + gich_addr: Platform::GICH_BASE, + gicv_addr: Platform::GICV_BASE, + gicr_addr: Platform::GICR_BASE, + maintenance_int_id: 25, + }, + /// SMMU (System Memory Management Unit) configuration for RK3588 + smmu_desc: crate::arch::SmmuDesc { + base: 0xfcb00000, + interrupt_id: 0x17d, + global_mask: 0, //0x200000 + }, + }, +}; diff --git a/src/board/tx2/mod.rs b/src/board/tx2/mod.rs index e79869a290f3854ae3a3e70668f5375678dfecb4..014f78196d97c4ac48d8a012c30a7bc0b7bac679 100644 --- a/src/board/tx2/mod.rs +++ b/src/board/tx2/mod.rs @@ -7,7 +7,7 @@ // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +//! tx2 platform configuration pub use self::platform::*; - mod platform; diff --git a/src/board/tx2/platform.rs b/src/board/tx2/platform.rs index 55ccd5583d439e06552abeaac3a06e2f56ab6275..a9840f84d27632bcdb132eac9b52160ce7a75b43 100644 --- a/src/board/tx2/platform.rs +++ b/src/board/tx2/platform.rs @@ -8,10 +8,10 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::arch::GicDesc; -use crate::arch::SmmuDesc; +/// Crate imports for architecture and board configurations +use crate::arch::ArchDesc; use crate::board::{ - PlatOperation, Platform, PlatCpuCoreConfig, ArchDesc, PlatCpuConfig, PlatformConfig, PlatMemoryConfig, + PlatOperation, Platform, PlatCpuCoreConfig, ClusterDesc, PlatCpuConfig, PlatformConfig, PlatMemoryConfig, PlatMemRegion, }; use crate::board::SchedRule::RoundRobin; @@ -19,17 +19,23 @@ use crate::device::ARM_CORTEX_A57; #[allow(unused_imports)] use crate::device::ARM_NVIDIA_DENVER; +/// Represents the platform configuration for NVIDIA TX2 pub struct Tx2Platform; +/// Implementation of platform operations for NVIDIA TX2 impl PlatOperation for Tx2Platform { + /// UART base addresses const UART_0_ADDR: usize = 0x3100000; const UART_1_ADDR: usize = 0xc280000; + /// UART interrupt numbers const UART_0_INT: usize = 32 + 0x70; const UART_1_INT: usize = 32 + 0x72; + /// Hypervisor UART base address const HYPERVISOR_UART_BASE: usize = Self::UART_1_ADDR; + /// GIC (Generic Interrupt Controller) base addresses const GICD_BASE: usize = 0x3881000; const GICC_BASE: usize = 0x3882000; const GICH_BASE: usize = 0x3884000; @@ -46,33 +52,23 @@ impl PlatOperation for Tx2Platform { const DISK_PARTITION_1_SIZE: usize = 41943040; const DISK_PARTITION_2_SIZE: usize = 8388608; - const SHARE_MEM_BASE: usize = 0xd_0000_0000; - fn cpuid_to_cpuif(cpuid: usize) -> usize { cpuid + PLAT_DESC.cpu_desc.num } + /// Maps CPU interface number to CPU ID for RK3588 fn cpuif_to_cpuid(cpuif: usize) -> usize { cpuif - PLAT_DESC.cpu_desc.num } - - fn blk_init() { - todo!() - } - - fn blk_read(_sector: usize, _count: usize, _buf: usize) { - todo!() - } - - fn blk_write(_sector: usize, _count: usize, _buf: usize) { - todo!() - } } -pub static PLAT_DESC: PlatformConfig = PlatformConfig { +/// Platform configuration for NVIDIA TX2 +pub const PLAT_DESC: PlatformConfig = PlatformConfig { + /// CPU configuration details for NVIDIA TX2 cpu_desc: PlatCpuConfig { num: 4, core_list: &[ + /// Configuration for the first ARM Cortex-A57 core PlatCpuCoreConfig { name: ARM_CORTEX_A57, mpidr: 0x80000100, @@ -94,9 +90,13 @@ pub static PLAT_DESC: PlatformConfig = PlatformConfig { sched: RoundRobin, }, ], + /// Cluster description for the CPU cores + cluster_desc: ClusterDesc { num: 1, core_num: &[4] }, }, + /// Memory configuration details for NVIDIA TX2 mem_desc: PlatMemoryConfig { regions: &[ + /// Memory region configuration PlatMemRegion { base: 0x80000000, size: 0x10000000, @@ -110,17 +110,21 @@ pub static PLAT_DESC: PlatformConfig = PlatformConfig { size: 0x185600000, }, ], + /// Base address of the memory region for the hypervisor base: 0x80000000, }, + /// Architecture-specific configuration for NVIDIA TX2 arch_desc: ArchDesc { - gic_desc: GicDesc { + // GIC (Generic Interrupt Controller) configuration + gic_desc: crate::arch::GicDesc { gicd_addr: Platform::GICD_BASE, gicc_addr: Platform::GICC_BASE, gich_addr: Platform::GICH_BASE, gicv_addr: Platform::GICV_BASE, maintenance_int_id: 25, }, - smmu_desc: SmmuDesc { + /// SMMU (System Memory Management Unit) configuration + smmu_desc: crate::arch::SmmuDesc { base: 0x12000000, interrupt_id: 187, global_mask: 0x7f80, diff --git a/src/config/config.rs b/src/config/config.rs index 272584beb49cc84b3e39c93ccb752e96a4a45e01..06e099aef710de691cb9490d519864fdc69fcedd 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -8,58 +8,80 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +/// This module contains configurations for the virtual machine. +/// It defines structures and functions to manage VM configurations. +/// Each VM has its own configuration, and this module provides +/// functions to manipulate VM configurations, such as adding memory regions, +/// setting CPU configurations, and adding emulated or passthrough devices. use alloc::string::{String, ToString}; use alloc::sync::Arc; use alloc::vec::Vec; +use core::ffi::CStr; use spin::Mutex; // use crate::board::*; use crate::device::{EmuDeviceType, mediated_blk_free, mediated_blk_request}; use crate::kernel::{active_vm, vm, Vm, vm_ipa2pa, VM_NUM_MAX, VmType}; -use crate::lib::{BitAlloc, BitAlloc16, memcpy_safe}; +use crate::utils::{BitAlloc, BitAlloc16, memcpy}; use crate::vmm::vmm_init_gvm; +/// The maximum length of a VM name. pub const NAME_MAX_LEN: usize = 32; const CFG_MAX_NUM: usize = 0x10; const IRQ_MAX_NUM: usize = 0x40; const PASSTHROUGH_DEV_MAX_NUM: usize = 128; const EMULATED_DEV_MAX_NUM: usize = 16; +/// Represents the type of a device in the device tree. #[derive(Clone, Copy, PartialEq)] pub enum DtbDevType { DevSerial = 0, DevGicd = 1, DevGicc = 2, + DevGicr = 3, } impl DtbDevType { + /// Convert a `usize` value to a `DtbDevType`. pub fn from_usize(value: usize) -> DtbDevType { match value { 0 => DtbDevType::DevSerial, 1 => DtbDevType::DevGicd, 2 => DtbDevType::DevGicc, + 3 => DtbDevType::DevGicr, _ => panic!("Unknown DtbDevType value: {}", value), } } } +//! Represents the configuration of an emulated device for a virtual machine. #[derive(Clone)] pub struct VmEmulatedDeviceConfig { - pub name: Option, + /// The name of the emulated device. + pub name: String, + /// The base IPA (Intermediate Physical Address) of the device. pub base_ipa: usize, + /// The length of the device. pub length: usize, + /// The IRQ (Interrupt Request) ID of the device. pub irq_id: usize, + /// List of configuration values for the device. pub cfg_list: Vec, + /// The type of emulated device. pub emu_type: EmuDeviceType, + /// Indicates whether the device is mediated. pub mediated: bool, } +/// Represents a list of emulated device configurations for a virtual machine. pub struct VmEmulatedDeviceConfigList { + /// List of emulated device configurations. pub emu_dev_list: Vec, } impl VmEmulatedDeviceConfigList { + /// Creates a new, empty list of emulated device configurations. pub const fn default() -> VmEmulatedDeviceConfigList { VmEmulatedDeviceConfigList { emu_dev_list: Vec::new(), @@ -67,11 +89,16 @@ impl VmEmulatedDeviceConfigList { } } +/// Represents the configuration of a passthrough region. #[derive(Default, Clone, Copy, Debug, Eq)] pub struct PassthroughRegion { + /// The IPA (Intermediate Physical Address) of the passthrough region. pub ipa: usize, + /// The PA (Physical Address) of the passthrough region. pub pa: usize, + /// The length of the passthrough region. pub length: usize, + /// Indicates whether the region has device properties. pub dev_property: bool, } @@ -81,14 +108,19 @@ impl PartialEq for PassthroughRegion { } } +/// Represents the configuration of a passthrough device for a virtual machine. #[derive(Default, Clone)] pub struct VmPassthroughDeviceConfig { + /// List of passthrough regions. pub regions: Vec, + /// List of IRQs (Interrupt Requests) for the passthrough device. pub irqs: Vec, + /// List of stream IDs for the passthrough device. pub streams_ids: Vec, } impl VmPassthroughDeviceConfig { + /// Creates a new, default configuration for passthrough devices. pub const fn default() -> VmPassthroughDeviceConfig { VmPassthroughDeviceConfig { regions: Vec::new(), @@ -98,13 +130,17 @@ impl VmPassthroughDeviceConfig { } } +/// Represents a memory region configuration for a virtual machine. #[derive(Clone, Copy, Debug, Eq)] pub struct VmRegion { + /// The starting IPA (Intermediate Physical Address) of the memory region. pub ipa_start: usize, + /// The length of the memory region. pub length: usize, } impl VmRegion { + /// Creates a new memory region configuration. pub const fn default() -> VmRegion { VmRegion { ipa_start: 0, @@ -113,24 +149,28 @@ impl VmRegion { } } +/// Implementation of the PartialEq trait for VmRegion, enabling equality comparisons between VmRegion instances. impl PartialEq for VmRegion { fn eq(&self, other: &Self) -> bool { self.ipa_start == other.ipa_start && self.length == other.length } } +/// Clone implementation for VmMemoryConfig struct. #[derive(Clone)] pub struct VmMemoryConfig { pub region: Vec, } impl VmMemoryConfig { + /// Default constructor for VmMemoryConfig. pub const fn default() -> VmMemoryConfig { VmMemoryConfig { region: vec![] } } } -#[derive(Clone, Copy)] +/// Clone, Copy, and Default implementations for VmImageConfig struct. +#[derive(Clone, Copy, Default)] pub struct VmImageConfig { pub kernel_img_name: Option<&'static str>, pub kernel_load_ipa: usize, @@ -144,19 +184,7 @@ pub struct VmImageConfig { } impl VmImageConfig { - pub const fn default() -> VmImageConfig { - VmImageConfig { - kernel_img_name: None, - kernel_load_ipa: 0, - kernel_load_pa: 0, - kernel_entry_point: 0, - // device_tree_filename: None, - device_tree_load_ipa: 0, - // ramdisk_filename: None, - ramdisk_load_ipa: 0, - mediated_block_index: None, - } - } + /// Constructor for VmImageConfig with essential parameters. pub fn new(kernel_load_ipa: usize, device_tree_load_ipa: usize, ramdisk_load_ipa: usize) -> VmImageConfig { VmImageConfig { kernel_img_name: None, @@ -172,6 +200,7 @@ impl VmImageConfig { } } +/// Configuration for VmCpu (Virtual Machine CPU). #[derive(Clone, Copy)] pub struct VmCpuConfig { pub num: usize, @@ -180,6 +209,7 @@ pub struct VmCpuConfig { } impl VmCpuConfig { + /// Default constructor for VmCpuConfig. pub const fn default() -> VmCpuConfig { VmCpuConfig { num: 0, @@ -187,14 +217,44 @@ impl VmCpuConfig { master: 0, } } + + /// Constructor for VmCpuConfig with specified parameters. + fn new(num: usize, allocate_bitmap: usize, master: usize) -> Self { + /// Adjust num and allocate_bitmap based on the given values. + /// Ensure allocate_bitmap and num match, accepting the lower bitmap by the given CPU num. + /// This is a complex process of bit manipulation to synchronize allocate_bitmap and num. + /// The resulting values are stored in a new VmCpuConfig instance. + let num = usize::min(num, allocate_bitmap.count_ones() as usize); + // make sure `allocate_bitmap` and `num` matches + let allocate_bitmap = { + // only accept the lower bitmap by given cpu num + let mut index = 1 << allocate_bitmap.trailing_zeros(); + let mut remain = num; + while remain > 0 && index <= allocate_bitmap { + if allocate_bitmap & index != 0 { + remain -= 1; + } + index <<= 1; + } + allocate_bitmap & (index - 1) + } as u32; + let master = master as i32; + Self { + num, + allocate_bitmap, + master, + } + } } +/// Structure representing address regions. #[derive(Clone, Copy)] pub struct AddrRegions { pub ipa: usize, pub length: usize, } +/// Configuration for VmDtbDev (Device Tree Device in Virtual Machine). #[derive(Clone)] pub struct VmDtbDevConfig { pub name: String, @@ -203,12 +263,14 @@ pub struct VmDtbDevConfig { pub addr_region: AddrRegions, } +/// Configuration for VMDtbDevConfigList (List of Device Tree Devices in Virtual Machine). #[derive(Clone)] pub struct VMDtbDevConfigList { pub dtb_device_list: Vec, } impl VMDtbDevConfigList { + // Default constructor for VMDtbDevConfigList. pub const fn default() -> VMDtbDevConfigList { VMDtbDevConfigList { dtb_device_list: Vec::new(), @@ -216,12 +278,13 @@ impl VMDtbDevConfigList { } } +/// Configuration for VmConfigEntry (Virtual Machine Configuration Entry). #[derive(Clone)] pub struct VmConfigEntry { // VM id, generate inside hypervisor. pub id: usize, // Following configs are not intended to be modified during configuration. - pub name: Option, + pub name: String, pub os_type: VmType, pub cmdline: String, // Following config can be modified during configuration. @@ -231,26 +294,31 @@ pub struct VmConfigEntry { pub vm_emu_dev_confg: Arc>, pub vm_pt_dev_confg: Arc>, pub vm_dtb_devs: Arc>, + pub fdt_overlay: Arc>>, } -impl VmConfigEntry { - pub fn default() -> VmConfigEntry { +/// Default implementation for VmConfigEntry. +impl Default for VmConfigEntry { + fn default() -> VmConfigEntry { VmConfigEntry { id: 0, - name: Some(String::from("unknown")), + name: String::from("unknown"), os_type: VmType::VmTBma, - cmdline: String::from("root=/dev/vda rw audit=0"), - image: Arc::new(Mutex::new(VmImageConfig::default())), memory: Arc::new(Mutex::new(VmMemoryConfig::default())), cpu: Arc::new(Mutex::new(VmCpuConfig::default())), vm_emu_dev_confg: Arc::new(Mutex::new(VmEmulatedDeviceConfigList::default())), vm_pt_dev_confg: Arc::new(Mutex::new(VmPassthroughDeviceConfig::default())), vm_dtb_devs: Arc::new(Mutex::new(VMDtbDevConfigList::default())), + fdt_overlay: Arc::new(Mutex::new(Vec::new())), } } +} +/// Additional methods for VmConfigEntry. +impl VmConfigEntry { + /// Creates a new VmConfigEntry with the specified parameters. pub fn new( name: String, cmdline: String, @@ -260,8 +328,7 @@ impl VmConfigEntry { ramdisk_load_ipa: usize, ) -> VmConfigEntry { VmConfigEntry { - id: 0, - name: Some(name), + name, os_type: VmType::from_usize(vm_type), cmdline, image: Arc::new(Mutex::new(VmImageConfig::new( @@ -269,34 +336,32 @@ impl VmConfigEntry { device_tree_load_ipa, ramdisk_load_ipa, ))), - memory: Arc::new(Mutex::new(VmMemoryConfig::default())), - cpu: Arc::new(Mutex::new(VmCpuConfig::default())), - vm_emu_dev_confg: Arc::new(Mutex::new(VmEmulatedDeviceConfigList::default())), - vm_pt_dev_confg: Arc::new(Mutex::new(VmPassthroughDeviceConfig::default())), - vm_dtb_devs: Arc::new(Mutex::new(VMDtbDevConfigList::default())), + ..Default::default() } } + /// Returns the ID of the VmConfigEntry. pub fn id(&self) -> usize { self.id } + /// Sets the ID of the VmConfigEntry. pub fn set_id(&mut self, id: usize) { self.id = id; } + /// Returns the name of the virtual machine. pub fn vm_name(&self) -> String { - match &self.name { - Some(name) => name.to_string(), - None => String::from("unknown"), - } + self.name.clone() } + /// Returns the index of the mediated block, if any. pub fn mediated_block_index(&self) -> Option { let img_cfg = self.image.lock(); img_cfg.mediated_block_index } + /// Sets the mediated block index. pub fn set_mediated_block_index(&mut self, med_blk_id: usize) { let mut img_cfg = self.image.lock(); // println!("set_mediated_block_index {}",med_blk_id); @@ -304,98 +369,115 @@ impl VmConfigEntry { // println!("set_mediated_block_index {} self.med_blk_idx {:?}",med_blk_id, img_cfg.mediated_block_index); } + /// Returns the name of the kernel image, if any. pub fn kernel_img_name(&self) -> Option<&'static str> { let img_cfg = self.image.lock(); img_cfg.kernel_img_name } + /// Returns the IPA (Physical Address) of the kernel load address. pub fn kernel_load_ipa(&self) -> usize { let img_cfg = self.image.lock(); img_cfg.kernel_load_ipa } + /// Sets the physical address of the kernel load address. pub fn set_kernel_load_pa(&mut self, kernel_load_pa: usize) { let mut img_cfg = self.image.lock(); img_cfg.kernel_load_pa = kernel_load_pa } + /// Returns the physical address of the kernel load address. pub fn kernel_load_pa(&self) -> usize { let img_cfg = self.image.lock(); img_cfg.kernel_load_pa } + /// Returns the entry point of the kernel. pub fn kernel_entry_point(&self) -> usize { let img_cfg = self.image.lock(); img_cfg.kernel_entry_point } + /// Returns the IPA (Physical Address) of the device tree load address. pub fn device_tree_load_ipa(&self) -> usize { let img_cfg = self.image.lock(); img_cfg.device_tree_load_ipa } + /// Returns the IPA (Physical Address) of the ramdisk load address. pub fn ramdisk_load_ipa(&self) -> usize { let img_cfg = self.image.lock(); img_cfg.ramdisk_load_ipa } + /// Returns the memory regions configured for the virtual machine. pub fn memory_region(&self) -> Vec { let mem_cfg = self.memory.lock(); mem_cfg.region.clone() } + /// Adds a memory configuration with the specified IPA start and length. pub fn add_memory_cfg(&self, ipa_start: usize, length: usize) { let mut mem_cfg = self.memory.lock(); mem_cfg.region.push(VmRegion { ipa_start, length }); } + /// Returns the number of CPUs configured for the virtual machine. pub fn cpu_num(&self) -> usize { let cpu_cfg = self.cpu.lock(); cpu_cfg.num } + /// Returns the CPU allocate bitmap for the virtual machine. pub fn cpu_allocated_bitmap(&self) -> u32 { let cpu_cfg = self.cpu.lock(); cpu_cfg.allocate_bitmap } + /// Returns the master CPU ID for the virtual machine. pub fn cpu_master(&self) -> usize { let cpu_cfg = self.cpu.lock(); cpu_cfg.master as usize } + /// Sets the CPU configuration with the specified number, allocate bitmap, and master CPU ID. pub fn set_cpu_cfg(&self, num: usize, allocate_bitmap: usize, master: usize) { let mut cpu_cfg = self.cpu.lock(); - cpu_cfg.num = usize::min(num, allocate_bitmap.count_ones() as usize); - cpu_cfg.allocate_bitmap = allocate_bitmap as u32; - cpu_cfg.master = master as i32; + *cpu_cfg = VmCpuConfig::new(num, allocate_bitmap, master); } + /// Returns the list of emulated devices configured for the virtual machine. pub fn emulated_device_list(&self) -> Vec { let emu_dev_cfg = self.vm_emu_dev_confg.lock(); emu_dev_cfg.emu_dev_list.clone() } + /// Adds an emulated device configuration to the virtual machine. pub fn add_emulated_device_cfg(&self, cfg: VmEmulatedDeviceConfig) { let mut emu_dev_cfgs = self.vm_emu_dev_confg.lock(); emu_dev_cfgs.emu_dev_list.push(cfg); } + /// Returns the list of passthrough device regions configured for the virtual machine. pub fn passthrough_device_regions(&self) -> Vec { let pt_dev_cfg = self.vm_pt_dev_confg.lock(); pt_dev_cfg.regions.clone() } + /// Returns the list of passthrough device IRQs configured for the virtual machine. pub fn passthrough_device_irqs(&self) -> Vec { let pt_dev_cfg = self.vm_pt_dev_confg.lock(); pt_dev_cfg.irqs.clone() } + /// Returns the list of passthrough device stream IDs configured for the virtual machine. pub fn passthrough_device_stread_ids(&self) -> Vec { let pt_dev_cfg = self.vm_pt_dev_confg.lock(); pt_dev_cfg.streams_ids.clone() } + /// Adds a passthrough device region with the specified IPA start, PA start, and length. pub fn add_passthrough_device_region(&self, base_ipa: usize, base_pa: usize, length: usize) { let mut pt_dev_cfg = self.vm_pt_dev_confg.lock(); let pt_region_cfg = PassthroughRegion { @@ -407,53 +489,65 @@ impl VmConfigEntry { pt_dev_cfg.regions.push(pt_region_cfg) } + /// Adds passthrough device IRQs to the virtual machine configuration. pub fn add_passthrough_device_irqs(&self, irqs: &mut Vec) { let mut pt_dev_cfg = self.vm_pt_dev_confg.lock(); pt_dev_cfg.irqs.append(irqs); } + /// Adds passthrough device stream IDs to the virtual machine configuration. pub fn add_passthrough_device_streams_ids(&self, streams_ids: &mut Vec) { let mut pt_dev_cfg = self.vm_pt_dev_confg.lock(); pt_dev_cfg.streams_ids.append(streams_ids); } + /// Returns the list of DTB (Device Tree Blob) devices configured for the virtual machine. pub fn dtb_device_list(&self) -> Vec { let dtb_dev_cfg = self.vm_dtb_devs.lock(); dtb_dev_cfg.dtb_device_list.clone() } + /// Adds a DTB device configuration to the virtual machine. pub fn add_dtb_device(&self, cfg: VmDtbDevConfig) { let mut dtb_dev_cfg = self.vm_dtb_devs.lock(); dtb_dev_cfg.dtb_device_list.push(cfg); } + /// Returns the IPA of the GICC (Generic Interrupt Controller - CPU Interface) device. pub fn gicc_addr(&self) -> usize { let dtb_devs = self.vm_dtb_devs.lock(); for dev in &dtb_devs.dtb_device_list { - match dev.dev_type { - DtbDevType::DevGicc => { - return dev.addr_region.ipa; - } - _ => {} + if let DtbDevType::DevGicc = dev.dev_type { + return dev.addr_region.ipa; } } 0 } + /// Returns the IPA of the GICD (Generic Interrupt Controller Distributor) device. pub fn gicd_addr(&self) -> usize { let dtb_devs = self.vm_dtb_devs.lock(); for dev in &dtb_devs.dtb_device_list { - match dev.dev_type { - DtbDevType::DevGicd => { - return dev.addr_region.ipa; - } - _ => {} + if let DtbDevType::DevGicd = dev.dev_type { + return dev.addr_region.ipa; + } + } + 0 + } + + /// Returns the IPA of the GICR (Generic Interrupt Controller Redistributor) device. + pub fn gicr_addr(&self) -> usize { + let dtb_devs = self.vm_dtb_devs.lock(); + for dev in &dtb_devs.dtb_device_list { + if let DtbDevType::DevGicr = dev.dev_type { + return dev.addr_region.ipa; } } 0 } } +/// Represents the configuration table for virtual machines. #[derive(Clone)] pub struct VmConfigTable { pub name: Option<&'static str>, @@ -462,8 +556,10 @@ pub struct VmConfigTable { pub entries: Vec, } +/// Additional methods for VmConfigTable. impl VmConfigTable { - pub const fn default() -> VmConfigTable { + /// Creates a new VmConfigTable. + const fn new() -> VmConfigTable { VmConfigTable { name: None, vm_bitmap: BitAlloc16::default(), @@ -472,6 +568,7 @@ impl VmConfigTable { } } + /// Generates a new VM ID and returns it. pub fn generate_vm_id(&mut self) -> Result { for i in 0..VM_NUM_MAX { if self.vm_bitmap.get(i) == 0 { @@ -482,28 +579,31 @@ impl VmConfigTable { Err(()) } + /// Removes a VM ID from the bitmap. pub fn remove_vm_id(&mut self, vm_id: usize) { if vm_id >= VM_NUM_MAX || self.vm_bitmap.get(vm_id) == 0 { - println!("illegal vm id {}", vm_id); + error!("illegal vm id {}", vm_id); } self.vm_bitmap.clear(vm_id); } } -// lazy_static! { -pub static DEF_VM_CONFIG_TABLE: Mutex = Mutex::new(VmConfigTable::default()); -// } +/// Static instance of the default VM configuration table. +pub static DEF_VM_CONFIG_TABLE: Mutex = Mutex::new(VmConfigTable::new()); +/// Sets the configuration name for the default VM configuration table. pub fn vm_cfg_set_config_name(name: &'static str) { let mut vm_config = DEF_VM_CONFIG_TABLE.lock(); vm_config.name = Some(name); } +/// Returns the number of configured virtual machines. pub fn vm_num() -> usize { let vm_config = DEF_VM_CONFIG_TABLE.lock(); vm_config.entries.len() } +/// Returns the type of the virtual machine with the specified ID. pub fn vm_type(vmid: usize) -> VmType { let vm_config = DEF_VM_CONFIG_TABLE.lock(); for vm_cfg_entry in vm_config.entries.iter() { @@ -511,10 +611,11 @@ pub fn vm_type(vmid: usize) -> VmType { return vm_cfg_entry.os_type; } } - println!("failed to find VM[{}] in vm cfg entry list", vmid); - return VmType::VmTOs; + error!("failed to find VM[{}] in vm cfg entry list", vmid); + VmType::VmTOs } +/// Returns a list of IDs for all configured virtual machines. pub fn vm_id_list() -> Vec { let vm_config = DEF_VM_CONFIG_TABLE.lock(); let mut id_list: Vec = Vec::new(); @@ -524,6 +625,7 @@ pub fn vm_id_list() -> Vec { id_list } +/// Returns the configuration entry for the virtual machine with the specified ID. pub fn vm_cfg_entry(vmid: usize) -> Option { let vm_config = DEF_VM_CONFIG_TABLE.lock(); for vm_cfg_entry in vm_config.entries.iter() { @@ -531,38 +633,40 @@ pub fn vm_cfg_entry(vmid: usize) -> Option { return Some(vm_cfg_entry.clone()); } } - println!("failed to find VM[{}] in vm cfg entry list", vmid); - return None; + error!("failed to find VM[{}] in vm cfg entry list", vmid); + None } +/// Adds a virtual machine configuration entry to DEF_VM_CONFIG_TABLE. /* Add VM config entry to DEF_VM_CONFIG_TABLE */ pub fn vm_cfg_add_vm_entry(mut vm_cfg_entry: VmConfigEntry) -> Result { let mut vm_config = DEF_VM_CONFIG_TABLE.lock(); match vm_config.generate_vm_id() { Ok(vm_id) => { - if vm_id == 0 && (vm_config.entries.len() > 0 || vm_config.vm_num > 0) { + if vm_id == 0 && (!vm_config.entries.is_empty() || vm_config.vm_num > 0) { panic!("error in mvm config init, the def vm config table is not empty"); } vm_cfg_entry.set_id(vm_id); vm_config.vm_num += 1; vm_config.entries.push(vm_cfg_entry.clone()); - println!( + info!( "\nSuccessfully add {}[{}] name {:?}, currently vm_num {}", if vm_id == 0 { "MVM" } else { "GVM" }, vm_cfg_entry.id(), - vm_cfg_entry.clone().name.unwrap(), + vm_cfg_entry.name, vm_config.vm_num ); Ok(vm_id) } Err(_) => { - println!("vm_cfg_add_vm_entry, vm num reached max value"); + error!("vm_cfg_add_vm_entry, vm num reached max value"); Err(()) } } } +/// Adds a virtual machine configuration entry to DEF_VM_CONFIG_TABLE. pub fn vm_cfg_remove_vm_entry(vm_id: usize) { let mut vm_config = DEF_VM_CONFIG_TABLE.lock(); for (idx, vm_cfg_entry) in vm_config.entries.iter().enumerate() { @@ -580,68 +684,40 @@ pub fn vm_cfg_remove_vm_entry(vm_id: usize) { return; } } - println!("VM[{}] config not found in vm-config-table", vm_id); + error!("VM[{}] config not found in vm-config-table", vm_id); } +/// Generates a new VM Config Entry and sets basic values. /* Generate a new VM Config Entry, set basic value */ pub fn vm_cfg_add_vm(config_ipa: usize) -> Result { let config_pa = vm_ipa2pa(active_vm().unwrap(), config_ipa); - let ( - vm_name_ipa, - vm_name_length, - vm_type, - cmdline_ipa, - cmdline_length, - kernel_load_ipa, - device_tree_load_ipa, - ramdisk_load_ipa, - ) = unsafe { *(config_pa as *const _) }; - println!("\n\nStart to prepare configuration for new VM"); + // SAFETY: config_pa is from user space, it is checked by shyper.ko firstly. + // And in the function, vm_ipa2pa, it is checked whether the config_pa is in the memory region of the VM. + let [vm_name_ipa, _vm_name_length, vm_type, cmdline_ipa, _cmdline_length, kernel_load_ipa, device_tree_load_ipa, ramdisk_load_ipa] = + unsafe { *(config_pa as *const _) }; + info!("\n\nStart to prepare configuration for new VM"); // Copy VM name from user ipa. let vm_name_pa = vm_ipa2pa(active_vm().unwrap(), vm_name_ipa); if vm_name_pa == 0 { - println!("illegal vm_name_ipa {:x}", vm_name_ipa); + error!("illegal vm_name_ipa {:x}", vm_name_ipa); return Err(()); } - let vm_name_u8 = vec![0 as u8; vm_name_length]; - if vm_name_length > 0 { - memcpy_safe( - &vm_name_u8[0] as *const _ as *const u8, - vm_name_pa as *mut u8, - vm_name_length, - ); - } - - let vm_name_str = match String::from_utf8(vm_name_u8.clone()) { - Ok(_str) => _str, - Err(error) => { - println!("error: {:?} in parsing the vm_name {:?}", error, vm_name_u8); - String::from("unknown") - } - }; + // SAFETY: We have checked the vm_name_pa is in the memory region of the VM by vm_ipa2pa. + let vm_name_str = unsafe { CStr::from_ptr(vm_name_pa as *const _) } + .to_string_lossy() + .to_string(); // Copy VM cmdline from user ipa. let cmdline_pa = vm_ipa2pa(active_vm().unwrap(), cmdline_ipa); if cmdline_pa == 0 { - println!("illegal cmdline_ipa {:x}", cmdline_ipa); + error!("illegal cmdline_ipa {:x}", cmdline_ipa); return Err(()); } - let cmdline_u8 = vec![0 as u8; cmdline_length]; - if cmdline_length > 0 { - memcpy_safe( - &cmdline_u8[0] as *const _ as *const u8, - cmdline_pa as *mut u8, - cmdline_length, - ); - } - let cmdline_str = match String::from_utf8(cmdline_u8.clone()) { - Ok(_str) => _str, - Err(error) => { - println!("error: {:?} in parsing the cmdline {:?}", error, cmdline_u8); - String::from("unknown") - } - }; + // SAFETY: We have checked the cmdline_pa is in the memory region of the VM by vm_ipa2pa. + let cmdline_str = unsafe { CStr::from_ptr(cmdline_pa as *const _) } + .to_string_lossy() + .to_string(); // Generate a new VM config entry. let new_vm_cfg = VmConfigEntry::new( @@ -653,19 +729,21 @@ pub fn vm_cfg_add_vm(config_ipa: usize) -> Result { ramdisk_load_ipa, ); - println!(" VM name is [{:?}]", new_vm_cfg.name.clone().unwrap()); - println!(" cmdline is [{:?}]", new_vm_cfg.cmdline); - println!(" ramdisk is [0x{:x}]", new_vm_cfg.ramdisk_load_ipa()); + debug!(" VM name is [{:?}]", new_vm_cfg.name); + debug!(" cmdline is [{:?}]", new_vm_cfg.cmdline); + debug!(" ramdisk is [0x{:x}]", new_vm_cfg.ramdisk_load_ipa()); vm_cfg_add_vm_entry(new_vm_cfg) } +/// Deletes a VM config entry. /* Delete a VM config entry */ pub fn vm_cfg_del_vm(vmid: usize) -> Result { - println!("VM[{}] delete config entry", vmid); + info!("VM[{}] delete config entry", vmid); vm_cfg_remove_vm_entry(vmid); Ok(0) } +/// Add VM memory region according to VM id. /* Add VM memory region according to VM id */ pub fn vm_cfg_add_mem_region(vmid: usize, ipa_start: usize, length: usize) -> Result { let vm_cfg = match vm_cfg_entry(vmid) { @@ -673,13 +751,14 @@ pub fn vm_cfg_add_mem_region(vmid: usize, ipa_start: usize, length: usize) -> Re None => return Err(()), }; vm_cfg.add_memory_cfg(ipa_start, length); - println!( + info!( "\nVM[{}] vm_cfg_add_mem_region: add region start_ipa {:x} length {:x}", vmid, ipa_start, length ); Ok(0) } +/// Set VM CPU config according to VM id. /* Set VM cpu config according to VM id */ pub fn vm_cfg_set_cpu(vmid: usize, num: usize, allocate_bitmap: usize, master: usize) -> Result { let vm_cfg = match vm_cfg_entry(vmid) { @@ -689,7 +768,7 @@ pub fn vm_cfg_set_cpu(vmid: usize, num: usize, allocate_bitmap: usize, master: u vm_cfg.set_cpu_cfg(num, allocate_bitmap, master); - println!( + info!( "\nVM[{}] vm_cfg_set_cpu: num {} allocate_bitmap {} master {}", vmid, vm_cfg.cpu_num(), @@ -700,6 +779,7 @@ pub fn vm_cfg_set_cpu(vmid: usize, num: usize, allocate_bitmap: usize, master: u Ok(0) } +/// Add emulated device config for VM. /* Add emulated device config for VM */ pub fn vm_cfg_add_emu_dev( vmid: usize, @@ -719,32 +799,33 @@ pub fn vm_cfg_add_emu_dev( // Copy emu device name from user ipa. let name_pa = vm_ipa2pa(active_vm().unwrap(), name_ipa); if name_pa == 0 { - println!("illegal emulated device name_ipa {:x}", name_ipa); + error!("illegal emulated device name_ipa {:x}", name_ipa); return Err(()); } - let name_u8 = vec![0 as u8; NAME_MAX_LEN]; - memcpy_safe(&name_u8[0] as *const _ as *const u8, name_pa as *mut u8, NAME_MAX_LEN); - let name_str = match String::from_utf8(name_u8.clone()) { - Ok(str) => str, - Err(error) => { - println!("error: {:?} in parsing the emulated device name {:?}", error, name_u8); - String::from("unknown") - } - }; + // SAFETY: + // We have checked the name_pa is in the memory region of the VM by vm_ipa2pa. + let name_str = unsafe { CStr::from_ptr(name_pa as *const _) } + .to_string_lossy() + .to_string(); // Copy emu device cfg list from user ipa. let cfg_list_pa = vm_ipa2pa(active_vm().unwrap(), cfg_list_ipa); if cfg_list_pa == 0 { - println!("illegal emulated device cfg_list_ipa {:x}", cfg_list_ipa); + error!("illegal emulated device cfg_list_ipa {:x}", cfg_list_ipa); return Err(()); } - let cfg_list = vec![0 as usize; CFG_MAX_NUM]; - memcpy_safe( - &cfg_list[0] as *const _ as *const u8, - cfg_list_pa as *mut u8, - CFG_MAX_NUM * 8, // sizeof(usize) / sizeof(u8) - ); + let cfg_list = vec![0_usize; CFG_MAX_NUM]; + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy( + &cfg_list[0] as *const _ as *const u8, + cfg_list_pa as *mut u8, + CFG_MAX_NUM * 8, // sizeof(usize) / sizeof(u8) + ); + } - println!( + info!( concat!( "\nVM[{}] vm_cfg_add_emu_dev: ori emu dev num {}\n", " name {:?}\n", @@ -753,7 +834,7 @@ pub fn vm_cfg_add_emu_dev( ), vmid, emu_cfg_list.len(), - name_str.trim_end_matches(char::from(0)), + name_str, cfg_list, base_ipa, length, @@ -763,19 +844,19 @@ pub fn vm_cfg_add_emu_dev( let emu_dev_type = EmuDeviceType::from_usize(emu_type); let emu_dev_cfg = VmEmulatedDeviceConfig { - name: Some(name_str.trim_end_matches(char::from(0)).to_string()), + name: name_str, base_ipa, length, irq_id, cfg_list, emu_type: match emu_dev_type { EmuDeviceType::EmuDeviceTVirtioBlkMediated => EmuDeviceType::EmuDeviceTVirtioBlk, - _ => emu_dev_type.clone(), - }, - mediated: match EmuDeviceType::from_usize(emu_type) { - EmuDeviceType::EmuDeviceTVirtioBlkMediated => true, - _ => false, + _ => emu_dev_type, }, + mediated: matches!( + EmuDeviceType::from_usize(emu_type), + EmuDeviceType::EmuDeviceTVirtioBlkMediated + ), }; vm_cfg.add_emulated_device_cfg(emu_dev_cfg); @@ -784,7 +865,7 @@ pub fn vm_cfg_add_emu_dev( let med_blk_index = match mediated_blk_request() { Ok(idx) => idx, Err(_) => { - println!("no more medaited blk for vm {}", vmid); + error!("no more medaited blk for vm {}", vmid); return Err(()); } }; @@ -794,6 +875,7 @@ pub fn vm_cfg_add_emu_dev( Ok(0) } +/// Add passthrough device config region for VM /* Add passthrough device config region for VM */ pub fn vm_cfg_add_passthrough_device_region( vmid: usize, @@ -809,7 +891,7 @@ pub fn vm_cfg_add_passthrough_device_region( // Get passthrough device config list. let pt_dev_regions = vm_cfg.passthrough_device_regions(); - println!( + info!( concat!( "\nVM[{}] vm_cfg_add_pt_dev: ori pt dev regions num {}\n", " base_ipa {:x} base_pa {:x} length {:x}" @@ -825,9 +907,10 @@ pub fn vm_cfg_add_passthrough_device_region( Ok(0) } +/// Add passthrough device config irqs for VM. /* Add passthrough device config irqs for VM */ pub fn vm_cfg_add_passthrough_device_irqs(vmid: usize, irqs_base_ipa: usize, irqs_length: usize) -> Result { - println!( + info!( "\nVM[{}] vm_cfg_add_pt_dev irqs:\n base_ipa {:x} length {:x}", vmid, irqs_base_ipa, irqs_length ); @@ -835,18 +918,23 @@ pub fn vm_cfg_add_passthrough_device_irqs(vmid: usize, irqs_base_ipa: usize, irq // Copy passthrough device irqs from user ipa. let irqs_base_pa = vm_ipa2pa(active_vm().unwrap(), irqs_base_ipa); if irqs_base_pa == 0 { - println!("illegal irqs_base_ipa {:x}", irqs_base_ipa); + error!("illegal irqs_base_ipa {:x}", irqs_base_ipa); return Err(()); } - let mut irqs = vec![0 as usize, irqs_length]; + let mut irqs = vec![0_usize; irqs_length]; if irqs_length > 0 { - memcpy_safe( - &irqs[0] as *const _ as *const u8, - irqs_base_pa as *mut u8, - irqs_length * 8, // sizeof(usize) / sizeof(u8) - ); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy( + &irqs[0] as *const _ as *const u8, + irqs_base_pa as *mut u8, + irqs_length * 8, // sizeof(usize) / sizeof(u8) + ); + } } - println!(" irqs {:?}", irqs); + debug!(" irqs {:?}", irqs); let vm_cfg = match vm_cfg_entry(vmid) { Some(vm_cfg) => vm_cfg, @@ -856,13 +944,14 @@ pub fn vm_cfg_add_passthrough_device_irqs(vmid: usize, irqs_base_ipa: usize, irq Ok(0) } +/// Add passthrough device config streams ids for VM /* Add passthrough device config streams ids for VM */ pub fn vm_cfg_add_passthrough_device_streams_ids( vmid: usize, streams_ids_base_ipa: usize, streams_ids_length: usize, ) -> Result { - println!( + info!( "\nVM[{}] vm_cfg_add_pt_dev streams ids:\n streams_ids_base_ipa {:x} streams_ids_length {:x}", vmid, streams_ids_base_ipa, streams_ids_length ); @@ -870,18 +959,23 @@ pub fn vm_cfg_add_passthrough_device_streams_ids( // Copy passthrough device streams ids from user ipa. let streams_ids_base_pa = vm_ipa2pa(active_vm().unwrap(), streams_ids_base_ipa); if streams_ids_base_pa == 0 { - println!("illegal streams_ids_base_ipa {:x}", streams_ids_base_ipa); + error!("illegal streams_ids_base_ipa {:x}", streams_ids_base_ipa); return Err(()); } - let mut streams_ids = vec![0 as usize, streams_ids_length]; + let mut streams_ids = vec![0_usize, streams_ids_length]; if streams_ids_length > 0 { - memcpy_safe( - &streams_ids[0] as *const _ as *const u8, - streams_ids_base_pa as *mut u8, - streams_ids_length * 8, // sizeof(usize) / sizeof(u8) - ); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy( + &streams_ids[0] as *const _ as *const u8, + streams_ids_base_pa as *mut u8, + streams_ids_length * 8, // sizeof(usize) / sizeof(u8) + ); + } } - println!(" get streams_ids {:?}", streams_ids); + debug!(" get streams_ids {:?}", streams_ids); let vm_cfg = match vm_cfg_entry(vmid) { Some(vm_cfg) => vm_cfg, @@ -891,6 +985,7 @@ pub fn vm_cfg_add_passthrough_device_streams_ids( Ok(0) } +/// Add device tree device config for VM /* Add device tree device config for VM */ pub fn vm_cfg_add_dtb_dev( vmid: usize, @@ -901,7 +996,7 @@ pub fn vm_cfg_add_dtb_dev( addr_region_ipa: usize, addr_region_length: usize, ) -> Result { - println!( + info!( "\nVM[{}] vm_cfg_add_dtb_dev:\n dev_type {} irq_list_length {} addr_region_ipa {:x} addr_region_length {:x}", vmid, dev_type, irq_list_length, addr_region_ipa, addr_region_length ); @@ -909,50 +1004,40 @@ pub fn vm_cfg_add_dtb_dev( // Copy DTB device name from user ipa. let name_pa = vm_ipa2pa(active_vm().unwrap(), name_ipa); if name_pa == 0 { - println!("illegal dtb_dev name ipa {:x}", name_ipa); + error!("illegal dtb_dev name ipa {:x}", name_ipa); return Err(()); } - let dtb_dev_name_u8 = vec![0 as u8; NAME_MAX_LEN]; - memcpy_safe( - &dtb_dev_name_u8[0] as *const _ as *const u8, - name_pa as *mut u8, - NAME_MAX_LEN, - ); - let dtb_dev_name_str = match String::from_utf8(dtb_dev_name_u8.clone()) { - Ok(str) => str, - Err(error) => { - println!( - "error: {:?} in parsing the DTB device name {:?}", - error, dtb_dev_name_u8 - ); - String::from("unknown") - } - }; - println!( - " get dtb dev name {:?}", - dtb_dev_name_str.trim_end_matches(char::from(0)) - ); + // SAFETY: We have checked the name_pa is in the memory region of the VM by vm_ipa2pa. + let dtb_dev_name_str = unsafe { CStr::from_ptr(name_pa as *const _) } + .to_string_lossy() + .to_string(); + debug!(" get dtb dev name {:?}", dtb_dev_name_str); // Copy DTB device irq list from user ipa. let irq_list_pa = vm_ipa2pa(active_vm().unwrap(), irq_list_ipa); if irq_list_pa == 0 { - println!("illegal dtb_dev irq list ipa {:x}", irq_list_ipa); + error!("illegal dtb_dev irq list ipa {:x}", irq_list_ipa); return Err(()); } let mut dtb_irq_list: Vec = Vec::new(); if irq_list_length > 0 { - let tmp_dtb_irq_list = vec![0 as usize, irq_list_length]; - memcpy_safe( - &tmp_dtb_irq_list[0] as *const _ as *const u8, - irq_list_pa as *mut u8, - irq_list_length * 8, // sizeof(usize) / sizeof(u8) - ); + let tmp_dtb_irq_list = [0_usize, irq_list_length]; + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy( + &tmp_dtb_irq_list[0] as *const _ as *const u8, + irq_list_pa as *mut u8, + irq_list_length * 8, // sizeof(usize) / sizeof(u8) + ); + } for i in 0..irq_list_length { dtb_irq_list.push(tmp_dtb_irq_list[i]); } } - println!(" get dtb dev dtb_irq_list {:?}", dtb_irq_list); + debug!(" get dtb dev dtb_irq_list {:?}", dtb_irq_list); // Get VM config entry. let vm_cfg = match vm_cfg_entry(vmid) { @@ -961,7 +1046,7 @@ pub fn vm_cfg_add_dtb_dev( }; // Get DTB device config list. let vm_dtb_dev = VmDtbDevConfig { - name: dtb_dev_name_str.trim_end_matches(char::from(0)).to_string(), + name: dtb_dev_name_str, dev_type: DtbDevType::from_usize(dev_type), irqs: dtb_irq_list, addr_region: AddrRegions { @@ -975,6 +1060,8 @@ pub fn vm_cfg_add_dtb_dev( Ok(0) } +/// Final step for GVM configuration. +/// Set up GVM configuration and VM kernel image load region. /** * Final Step for GVM configuration. * Set up GVM configuration; @@ -1001,7 +1088,7 @@ fn vm_cfg_finish_configuration(vmid: usize, img_size: usize) -> Vm { continue; } let offset = load_ipa - region.ipa_start; - println!( + info!( "VM [{}] {} kernel image region: ipa=<0x{:x}>, pa=<0x{:x}>, img_size=<{}KB>", vm.id(), config.vm_name(), @@ -1014,6 +1101,10 @@ fn vm_cfg_finish_configuration(vmid: usize, img_size: usize) -> Vm { vm } +/// Uploads the kernel image file from MVM user space. +/// +/// This function is the last step in GVM configuration. It sets up the GVM and loads the kernel +/// image into the specified VM. /** * Load kernel image file from MVM user space. * It's the last step in GVM configuration. @@ -1028,7 +1119,7 @@ pub fn vm_cfg_upload_kernel_image( // Before upload kernel image, set GVM. let vm = match vm(vmid) { None => { - println!( + info!( "\nSuccessfully add configuration file for VM [{}]\nStart to init...", vmid ); @@ -1039,29 +1130,68 @@ pub fn vm_cfg_upload_kernel_image( }; let config = vm.config(); - println!( + info!( "VM[{}] Upload kernel image. cache_ipa:{:x} load_offset:{:x} load_size:{:x}", vmid, cache_ipa, load_offset, load_size ); // Get cache pa. let cache_pa = vm_ipa2pa(active_vm().unwrap(), cache_ipa); if cache_pa == 0 { - println!("illegal cache ipa {:x}", cache_ipa); + error!("illegal cache ipa {:x}", cache_ipa); return Err(()); } + // SAFETY: We have checked the cache_pa is in the memory region of the VM by vm_ipa2pa. let src = unsafe { core::slice::from_raw_parts_mut((cache_pa) as *mut u8, load_size) }; // Get kernel image load pa. let load_pa = config.kernel_load_pa(); if load_pa == 0 { - println!( + error!( "vm_cfg_upload_kernel_image: failed to get kernel image load pa of VM[{}]", vmid ); return Err(()); } // Copy from user space. + // SAFETY: + // We have checked the load_pa is in the memory region of the VM by vm_cfg_finish_configuration. + // And the load_offset and load_size will not exceed the kernel image size. let dst = unsafe { core::slice::from_raw_parts_mut((load_pa + load_offset) as *mut u8, load_size) }; dst.copy_from_slice(src); Ok(0) } + +/// Uploads the device tree from MVM user space. +pub fn vm_cfg_upload_device_tree( + vmid: usize, + _img_size: usize, + cache_ipa: usize, + load_offset: usize, + load_size: usize, +) -> Result { + let cfg = match vm_cfg_entry(vmid) { + None => { + error!("vm_cfg_upload_device_tree: vm {} not found", vmid); + return Err(()); + } + Some(cfg) => cfg, + }; + + info!( + "vm_cfg_upload_device_tree: VM[{}] upload device tree. cache_ipa: {:x} load_offset: {:x} load_size: {}", + vmid, cache_ipa, load_offset, load_size, + ); + + let cache_pa = vm_ipa2pa(active_vm().unwrap(), cache_ipa); + if cache_pa == 0 { + error!("illegal cache ipa {:x}", cache_ipa); + return Err(()); + } + + // SAFETY: We have checked the cache_pa is in the memory region of the VM by vm_ipa2pa. + let src = unsafe { core::slice::from_raw_parts(cache_pa as *mut u8, load_size) }; + let mut dst = cfg.fdt_overlay.lock(); + dst.extend_from_slice(src); + + Ok(0) +} diff --git a/src/config/mod.rs b/src/config/mod.rs index d47a9aacfaf98ca407ea17341397455158570245..4337a9abafca1027eced5a7c62045f2ee761caa6 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -8,6 +8,8 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +//! MVM init configuration, including platform-specific configuration and VM configuration. + pub use self::config::*; #[cfg(feature = "static-config")] pub use self::vm_def::*; @@ -17,12 +19,16 @@ pub use self::pi4_def::*; pub use self::qemu_def::*; #[cfg(feature = "tx2")] pub use self::tx2_def::*; +#[cfg(feature = "rk3588")] +pub use self::rk3588_def::*; mod config; #[cfg(feature = "pi4")] mod pi4_def; #[cfg(feature = "qemu")] mod qemu_def; +#[cfg(feature = "rk3588")] +mod rk3588_def; #[cfg(feature = "tx2")] mod tx2_def; #[cfg(feature = "static-config")] diff --git a/src/config/pi4_def.rs b/src/config/pi4_def.rs index 59afcefbeeed3b2a636d2ab14cb0f6eae00bce36..9acd11610177ca39694486c2aa4a6d77c2dc789b 100644 --- a/src/config/pi4_def.rs +++ b/src/config/pi4_def.rs @@ -20,20 +20,23 @@ use crate::device::EmuDeviceType; use crate::kernel::VmType; use super::{ - PassthroughRegion, vm_cfg_set_config_name, VmConfigEntry, VmCpuConfig, VMDtbDevConfigList, VmEmulatedDeviceConfig, + PassthroughRegion, vm_cfg_set_config_name, VmConfigEntry, VmCpuConfig, VmEmulatedDeviceConfig, VmEmulatedDeviceConfigList, VmImageConfig, VmMemoryConfig, VmPassthroughDeviceConfig, VmRegion, }; +/// Initializes the configuration for the manager VM (VM0). #[rustfmt::skip] pub fn mvm_config_init() { - println!("mvm_config_init() init config for VM0, which is manager VM"); + // Log initialization message for VM0 configuration + info!("mvm_config_init() init config for VM0, which is manager VM"); + // Set the configuration name for VM0 vm_cfg_set_config_name("pi4-default"); // vm0 emu let emu_dev_config = vec![ VmEmulatedDeviceConfig { - name: Some(String::from("interrupt-controller@fff841000")), + name: String::from("interrupt-controller@fff841000"), base_ipa: 0xFFF841000, length: 0x1000, irq_id: 0, @@ -42,7 +45,7 @@ pub fn mvm_config_init() { mediated: false, }, VmEmulatedDeviceConfig { - name: Some(String::from("virtio_net@fa000800")), + name: String::from("virtio_net@fa000800"), base_ipa: 0xfa000800, length: 0x400, irq_id: 32 + 0x17, @@ -51,7 +54,7 @@ pub fn mvm_config_init() { mediated: false, }, VmEmulatedDeviceConfig { - name: Some(String::from("virtio_console@fa000c00")), + name: String::from("virtio_console@fa000c00"), base_ipa: 0xfa000c00, length: 0x1000, irq_id: 32 + 0x20, @@ -60,7 +63,7 @@ pub fn mvm_config_init() { mediated: false, }, VmEmulatedDeviceConfig { - name: Some(String::from("virtio_console@fa002000")), + name: String::from("virtio_console@fa002000"), base_ipa: 0xfa002000, length: 0x1000, irq_id: 32 + 0x18, @@ -69,7 +72,7 @@ pub fn mvm_config_init() { mediated: false, }, VmEmulatedDeviceConfig { - name: Some(String::from("vm_service")), + name: String::from("vm_service"), base_ipa: 0, length: 0, irq_id: 32 + 0x10, @@ -171,7 +174,7 @@ pub fn mvm_config_init() { let mvm_config_entry = VmConfigEntry { id: 0, // name: Some("privileged"), - name: Some(String::from("Raspi4")), + name: String::from("Raspi4"), os_type: VmType::VmTOs, cmdline: // String::from("earlycon=uart8250,mmio32,0x3100000 console=ttyS0,115200n8 root=/dev/nvme0n1p2 rw audit=0 rootwait default_hugepagesz=32M hugepagesz=32M hugepages=4\0"), @@ -196,7 +199,8 @@ pub fn mvm_config_init() { })), vm_emu_dev_confg: Arc::new(Mutex::new(VmEmulatedDeviceConfigList { emu_dev_list: emu_dev_config })), vm_pt_dev_confg: Arc::new(Mutex::new(pt_dev_config)), - vm_dtb_devs: Arc::new(Mutex::new(VMDtbDevConfigList::default())), + ..Default::default() }; + // Add VM0 entry to the configuration let _ = vm_cfg_add_vm_entry(mvm_config_entry); } diff --git a/src/config/qemu_def.rs b/src/config/qemu_def.rs index 26cd0bbeb6ae37f539fc131d11e5c4042a52fd73..eed3179a9e7d5815c10df7e3de0c02ba594accbb 100644 --- a/src/config/qemu_def.rs +++ b/src/config/qemu_def.rs @@ -24,23 +24,58 @@ use super::{ VMDtbDevConfigList, }; +/// Initializes the configuration for the manager VM (VM0). #[rustfmt::skip] pub fn mvm_config_init() { + // Set the configuration name for VM0 vm_cfg_set_config_name("qemu-default"); // vm0 emu let emu_dev_config = vec![ VmEmulatedDeviceConfig { - name: Some(String::from("vgicd")), + name: String::from("vgicd"), base_ipa: Platform::GICD_BASE, + #[cfg(not(feature="gicv3"))] length: 0x1000, - irq_id: 0, + #[cfg(feature="gicv3")] + length: 0x10000, + irq_id: 25, cfg_list: Vec::new(), emu_type: EmuDeviceType::EmuDeviceTGicd, mediated: false, }, + #[cfg(feature="gicv3")] + VmEmulatedDeviceConfig { + name: String::from("vgicr"), + base_ipa: Platform::GICR_BASE, + length: 0xf60000, + irq_id: 25, + cfg_list: Vec::new(), + emu_type: EmuDeviceType::EmuDeviceTGICR, + mediated: false, + }, + #[cfg(feature="gicv3")] + VmEmulatedDeviceConfig { + name: String::from("icc_sre"), + base_ipa: Platform::ICC_SRE_ADDR, + length: 2, + irq_id: 0, + cfg_list: Vec::new(), + emu_type: EmuDeviceType::EmuDeviceTICCSRE, + mediated: false, + }, + #[cfg(feature="gicv3")] + VmEmulatedDeviceConfig { + name: String::from("icc_sgir"), + base_ipa: Platform::ICC_SGIR_ADDR, + length: 2, + irq_id: 0, + cfg_list: Vec::new(), + emu_type: EmuDeviceType::EmuDeviceTSGIR, + mediated: false, + }, // VmEmulatedDeviceConfig { - // name: Some(String::from("virtio-blk0")), + // name: String::from("virtio-blk0"), // base_ipa: 0xa000000, // length: 0x1000, // irq_id: 32 + 0x10, @@ -49,7 +84,7 @@ pub fn mvm_config_init() { // mediated: false, // }, VmEmulatedDeviceConfig { - name: Some(String::from("virtio-nic0")), + name: String::from("virtio-nic0"), base_ipa: 0xa001000, length: 0x1000, irq_id: 32 + 0x11, @@ -58,7 +93,7 @@ pub fn mvm_config_init() { mediated: false, }, VmEmulatedDeviceConfig { - name: Some(String::from("shyper")), + name: String::from("shyper"), base_ipa: 0, length: 0, irq_id: HVC_IRQ, @@ -72,14 +107,17 @@ pub fn mvm_config_init() { let mut pt_dev_config: VmPassthroughDeviceConfig = VmPassthroughDeviceConfig::default(); pt_dev_config.regions = vec![ PassthroughRegion { ipa: Platform::UART_0_ADDR, pa: Platform::UART_0_ADDR, length: 0x1000, dev_property: true }, + #[cfg(not(feature = "gicv3"))] PassthroughRegion { ipa: Platform::GICC_BASE, pa: Platform::GICV_BASE, length: 0x2000, dev_property: true }, + #[cfg(feature = "gicv3")] + PassthroughRegion { ipa: 0x8080000, pa: 0x8080000, length: 0x20000, dev_property: true }, //pass-through gicv3-its // pass-througn virtio blk/net PassthroughRegion { ipa: 0x0a003000, pa: 0x0a003000, length: 0x1000, dev_property: true }, ]; - pt_dev_config.irqs = vec![33, 27, 32 + 0x28, 32 + 0x29]; + pt_dev_config.irqs = vec![33,27, 72, 73,74,75,76,77,78,79]; pt_dev_config.streams_ids = vec![]; // pt_dev_config.push(VmPassthroughDeviceConfig { - // name: Some(String::from("serial0")), + // name: String::from("serial0"), // base_pa: UART_1_ADDR, // base_ipa: 0x9000000, // length: 0x1000, @@ -87,7 +125,7 @@ pub fn mvm_config_init() { // irq_list: vec![UART_1_INT, 27], // }); // pt_dev_config.push(VmPassthroughDeviceConfig { - // name: Some(String::from("gicc")), + // name: String::from("gicc"), // base_pa: PLATFORM_GICV_BASE, // base_ipa: 0x8010000, // length: 0x2000, @@ -95,7 +133,7 @@ pub fn mvm_config_init() { // irq_list: Vec::new(), // }); // pt_dev_config.push(VmPassthroughDeviceConfig { - // name: Some(String::from("nic")), + // name: String::from("nic"), // base_pa: 0x0a003000, // base_ipa: 0x0a003000, // length: 0x1000, @@ -113,7 +151,7 @@ pub fn mvm_config_init() { // vm0 config let mvm_config_entry =VmConfigEntry { id: 0, - name: Some(String::from("supervisor")), + name: String::from("supervisor"), os_type: VmType::VmTOs, cmdline: String::from("earlycon console=ttyAMA0 root=/dev/vda rw audit=0 default_hugepagesz=32M hugepagesz=32M hugepages=4\0"), image: Arc::new(Mutex::new(VmImageConfig { @@ -129,9 +167,9 @@ pub fn mvm_config_init() { mediated_block_index: None, })), cpu: Arc::new(Mutex::new(VmCpuConfig { - num: 4, - allocate_bitmap: 0b1111, - master: -1, + num: 1, + allocate_bitmap: 0b0001, + master: 0, })), memory: Arc::new(Mutex::new(VmMemoryConfig { region: vm_region, @@ -139,6 +177,7 @@ pub fn mvm_config_init() { vm_emu_dev_confg: Arc::new(Mutex::new(VmEmulatedDeviceConfigList { emu_dev_list: emu_dev_config })), vm_pt_dev_confg: Arc::new(Mutex::new(pt_dev_config)), vm_dtb_devs: Arc::new(Mutex::new(VMDtbDevConfigList::default())), + ..Default::default() }; let _ = vm_cfg_add_vm_entry(mvm_config_entry); } @@ -148,7 +187,7 @@ pub fn mvm_config_init() { // // vm1 emu // let mut emu_dev_config: Vec = Vec::new(); // emu_dev_config.push(VmEmulatedDeviceConfig { -// name: Some(String::from("vgicd")), +// name: String::from("vgicd"), // base_ipa: 0x8000000, // length: 0x1000, // irq_id: 0, @@ -157,7 +196,7 @@ pub fn mvm_config_init() { // mediated: false, // }); // emu_dev_config.push(VmEmulatedDeviceConfig { -// name: Some(String::from("virtio-blk1")), +// name: String::from("virtio-blk1"), // base_ipa: 0xa000000, // length: 0x1000, // irq_id: 32 + 0x10, @@ -169,7 +208,7 @@ pub fn mvm_config_init() { // // vm1 passthrough // let mut pt_dev_config: Vec = Vec::new(); // // pt_dev_config.push(VmPassthroughDeviceConfig { -// // name: Some(String::from("serial1")), +// // name: String::from("serial1"), // // base_pa: UART_2_ADDR, // // base_ipa: 0x9000000, // // length: 0x1000, @@ -177,7 +216,7 @@ pub fn mvm_config_init() { // // irq_list: vec![UART_2_INT, 27], // // }); // // pt_dev_config.push(VmPassthroughDeviceConfig { -// // name: Some(String::from("gicc")), +// // name: String::from("gicc"), // // base_pa: PLATFORM_GICV_BASE, // // base_ipa: 0x8010000, // // length: 0x2000, @@ -194,7 +233,7 @@ pub fn mvm_config_init() { // // vm1 config // vm_config.entries.push(Arc::new(VmConfigEntry { // id: 1, -// name: Some(String::from("guest-os-0")), +// name: String::from("guest-os-0"), // os_type: VmType::VmTOs, // memory: VmMemoryConfig { // num: 1, @@ -221,7 +260,7 @@ pub fn mvm_config_init() { // // vm2 BMA emu // let mut emu_dev_config: Vec = Vec::new(); // emu_dev_config.push(VmEmulatedDeviceConfig { -// name: Some(String::from("vgicd")), +// name: String::from("vgicd"), // base_ipa: 0x8000000, // length: 0x1000, // irq_id: 0, @@ -230,7 +269,7 @@ pub fn mvm_config_init() { // mediated: false, // }); // emu_dev_config.push(VmEmulatedDeviceConfig { -// name: Some(String::from("virtio-blk0")), +// name: String::from("virtio-blk0"), // base_ipa: 0xa000000, // length: 0x1000, // irq_id: 32 + 0x10, @@ -242,7 +281,7 @@ pub fn mvm_config_init() { // // vm2 BMA passthrough // let mut pt_dev_config: Vec = Vec::new(); // pt_dev_config.push(VmPassthroughDeviceConfig { -// name: Some(String::from("serial1")), +// name: String::from("serial1"), // base_pa: UART_2_ADDR, // base_ipa: 0x9000000, // length: 0x1000, @@ -250,7 +289,7 @@ pub fn mvm_config_init() { // irq_list: vec![27], // }); // pt_dev_config.push(VmPassthroughDeviceConfig { -// name: Some(String::from("gicc")), +// name: String::from("gicc"), // base_pa: PLATFORM_GICV_BASE, // base_ipa: 0x8010000, // length: 0x2000, @@ -268,7 +307,7 @@ pub fn mvm_config_init() { // // vm2 BMA config // vm_config.entries.push(Arc::new(VmConfigEntry { // id: 2, -// name: Some(String::from("guest-bma-0")), +// name: String::from("guest-bma-0"), // os_type: VmType::VmTBma, // memory: VmMemoryConfig { // num: 1, diff --git a/src/config/rk3588_def.rs b/src/config/rk3588_def.rs new file mode 100644 index 0000000000000000000000000000000000000000..7017be5fa2f85f5447ea5abdbcdcc4eb4656fb64 --- /dev/null +++ b/src/config/rk3588_def.rs @@ -0,0 +1,364 @@ +// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. +// Rust-Shyper is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use alloc::string::String; +use alloc::sync::Arc; +use alloc::vec::Vec; + +use fdt::binding::FdtBuf; +use spin::Mutex; + +use crate::arch::traits::InterruptController; +use crate::board::{Platform, PlatOperation}; +use crate::config::vm_cfg_add_vm_entry; +use crate::device::EmuDeviceType; +use crate::error::Result; +use crate::kernel::{HVC_IRQ, VmType}; + +use super::{ + PassthroughRegion, vm_cfg_set_config_name, VmConfigEntry, VmCpuConfig, VmEmulatedDeviceConfig, + VmEmulatedDeviceConfigList, VmImageConfig, VmMemoryConfig, VmPassthroughDeviceConfig, VmRegion, +}; + +/// This function provides functions for patching the flattened device tree (FDT) for VM configuration. +/// +/// The `patch_fdt` function removes unnecessary nodes and configurations from the FDT to customize +/// it for a specific VM setup. +pub fn patch_fdt(fdt: &mut FdtBuf) -> Result<()> { + // fdt.remove_node(c"/sram@10f000")?; + // use for boot one core + fdt.remove_node(c"/cpus/cpu-map/cluster0/core1")?; + fdt.remove_node(c"/cpus/cpu-map/cluster0/core2")?; + fdt.remove_node(c"/cpus/cpu-map/cluster0/core3")?; + fdt.remove_node(c"/cpus/cpu@100")?; + fdt.remove_node(c"/cpus/cpu@200")?; + fdt.remove_node(c"/cpus/cpu@300")?; + + // use for boot 4 cores in cluster-1. and if want to boot all,don`t remove any code about cpu + fdt.remove_node(c"/cpus/cpu-map/cluster1")?; + fdt.remove_node(c"/cpus/cpu-map/cluster2")?; + fdt.remove_node(c"/cpus/cpu@400")?; + fdt.remove_node(c"/cpus/cpu@500")?; + fdt.remove_node(c"/cpus/cpu@600")?; + fdt.remove_node(c"/cpus/cpu@700")?; + + // use for boot 2 cores in cluster-1 + // fdt.remove_node(c"/cpus/cpu-map/cluster0/core2")?; + // fdt.remove_node(c"/cpus/cpu-map/cluster0/core3")?; + // fdt.remove_node(c"/cpus/cpu@200")?; + // fdt.remove_node(c"/cpus/cpu@300")?; + + fdt.remove_node(c"/cpus/idle-states")?; + + // fdt.remove_node(c"/timer@feae0000")?; + // fdt.remove_node(c"/timer")?; + // fdt.remove_node(c"/i2c@feaa0000")?; + // fdt.remove_node(c"/reserved-memory")?; + // fdt.remove_node(c"/serial@feb70000")?; + // fdt.remove_node(c"/serial@feb80000")?; + // fdt.remove_node(c"/serial@feb60000")?; + // fdt.remove_node(c"/serial@feba0000")?; + // fdt.remove_node(c"/serial@feb50000")?; + + // fdt.remove_node(c"/pcie@fe180000")?; + // fdt.remove_node(c"/pcie@fe190000")?; + + fdt.remove_node(c"/memory")?; + + #[cfg(feature = "rk3588-noeth")] + fdt.remove_node(c"/ethernet@fe1c0000")?; + + Ok(()) +} + +/// Initializes the configuration for the manager VM (VM0). +#[rustfmt::skip] +pub fn mvm_config_init() { + info!("mvm_config_init() init config for VM0, which is manager VM"); + + vm_cfg_set_config_name("rk3588-default"); + + // vm0 emu + let emu_dev_config = vec![ + VmEmulatedDeviceConfig { + name: String::from("interrupt-controller@fe600000"), + base_ipa: Platform::GICD_BASE, + length: 0x10000, + irq_id: 25, + cfg_list: Vec::new(), + emu_type: EmuDeviceType::EmuDeviceTGicd, + mediated: false, + }, + VmEmulatedDeviceConfig { + name: String::from("GICR@0xfe680000"), + base_ipa: Platform::GICR_BASE, + length: 0x100000, + irq_id: 25, + cfg_list: Vec::new(), + emu_type: EmuDeviceType::EmuDeviceTGICR, + mediated: false, + }, + VmEmulatedDeviceConfig { + name: String::from("ICC_SRE_ADDR"), + base_ipa: Platform::ICC_SRE_ADDR, + length: 0, + irq_id: 25, + cfg_list: Vec::new(), + emu_type: EmuDeviceType::EmuDeviceTICCSRE, + mediated: false, + }, + VmEmulatedDeviceConfig { + name: String::from("ICC_SGIR_ADDR"), + base_ipa: Platform::ICC_SGIR_ADDR, + length: 0 , + irq_id: 25, + cfg_list: Vec::new(), + emu_type: EmuDeviceType::EmuDeviceTSGIR, + mediated: false, + }, + // VmEmulatedDeviceConfig { + // name: String::from("virtio-blk@feb60000"), + // base_ipa: 0xfeb60000, + // length: 0x1000, + // irq_id: 48, + // cfg_list: vec![0, 8192000], + // emu_type: EmuDeviceType::EmuDeviceTVirtioBlk, + // mediated: false, + // }, + VmEmulatedDeviceConfig { + name: String::from("virtio_net@f0000000"), + base_ipa: 0xf000_0000, + length: 0x1000, + irq_id: 45, + cfg_list: vec![0x74, 0x56, 0xaa, 0x0f, 0x47, 0xd0], + emu_type: EmuDeviceType::EmuDeviceTVirtioNet, + mediated: false, + }, + VmEmulatedDeviceConfig { + name: String::from("virtio_console@f0001000"), + base_ipa: 0xf000_1000, + length: 0x1000, + irq_id: 46, + cfg_list: vec![1, 0xf0140000], + emu_type: EmuDeviceType::EmuDeviceTVirtioConsole, + mediated: false, + }, + VmEmulatedDeviceConfig { + name: String::from("virtio_console@f0002000"), + base_ipa: 0xf000_2000, + length: 0x1000, + irq_id: 47, + cfg_list: vec![2, 0xf001_1000], + emu_type: EmuDeviceType::EmuDeviceTVirtioConsole, + mediated: false, + }, + // VmEmulatedDeviceConfig { + // name: String::from("iommu"), + // base_ipa: 0x12000000, + // length: 0x1000000, + // irq_id: 0, + // cfg_list: Vec::new(), + // emu_type: EmuDeviceType::EmuDeviceTIOMMU, + // mediated: false, + // }, + VmEmulatedDeviceConfig { + name: String::from("vm_service"), + base_ipa: 0, + length: 0, + irq_id: HVC_IRQ, + cfg_list: Vec::new(), + emu_type: EmuDeviceType::EmuDeviceTShyper, + mediated: false, + } + ]; + + let mut pt_dev_config: VmPassthroughDeviceConfig = VmPassthroughDeviceConfig::default(); + let mut pt = crate::utils::interval::IntervalExcluder::new(); + + // all, exclude gic + pt.add_range(0xfb000000, 0xfe600000); + + // exclude ethernet@0xfe1c0000 for 'noeth' + #[cfg(feature = "rk3588-noeth")] + pt.exclude_len(0xfe1c0000, 0x10000); + + pt_dev_config.regions = pt.into_iter().map(|t| { + PassthroughRegion { ipa: t.left, pa: t.left, length: t.len(), dev_property: true } + }).collect(); + + pt_dev_config.regions.extend(&[ + PassthroughRegion { ipa: 0xfe680000 + 0x100000, pa: 0xfe680000 + 0x100000, length: 0xfeb40000 - (0xfe680000 + 0x100000), dev_property: true }, + PassthroughRegion { ipa: 0xfebc0000, pa: 0xfebc0000, length: 0x0001_0000_0000 - 0xfebc0000, dev_property: true }, + // serial@feb5000——ttyFIQ + PassthroughRegion { ipa: 0xfeb50000, pa: 0xfeb50000, length: 0x100, dev_property: true }, + // serial@feba000——ttyS7 + PassthroughRegion { ipa: 0xfeba0000, pa: 0xfeba0000, length: 0x100, dev_property: true }, + // dev_property: false means non-cachable here. + // See 'vmm_init_passthrough_device'. + PassthroughRegion { ipa: 0x0, pa: 0x0, length: 0x9400000, dev_property: false }, + // device for pci + PassthroughRegion { ipa: 0xa41000000, pa: 0xa41000000, length: 0x400000, dev_property: true }, + PassthroughRegion { ipa: 0xa40c00000, pa: 0xa40c00000, length: 0x400000, dev_property: true }, + // device for PCI bus to be mapped in. + PassthroughRegion { ipa: 0xf4000000, pa: 0xf4000000, length: 0x1000000, dev_property: true }, + PassthroughRegion { ipa: 0xf3000000, pa: 0xf3000000, length: 0x1000000, dev_property: true }, + // device for its + PassthroughRegion { ipa: 0xfe640000, pa: 0xfe640000, length: 0x20000, dev_property: true }, + PassthroughRegion { ipa: 0xfe660000, pa: 0xfe660000, length: 0x20000, dev_property: true }, + ]); + + pt_dev_config.irqs = vec![ + 20, //fsc_interrupt_int_n + 23, //ARM-PMU + 26, //arch-timer + crate::arch::IntCtrl::IRQ_GUEST_TIMER, //timer + 30, //ptimer + 105, //dmc 32 + 0x49 + 118, //fea10000.dma-controller + 119, //fea10000.dma-controller + 120, //fea30000.dma-controller + 121, //fea30000.dma-controller + 122, //fed10000.dma-controller + 123, //fed10000.dma-controller + 124, //fb000000.gpu + 125, //fb000000.gpu + 126, //fb000000.gpu + 127, //fdc38100.rkvdec-core + 128, //fdc38700.iommu + 129, //fdc48100.rkvdec-core + 130, //fdc48700.iommu + 131, //fdbdf000.iommu + 132, //fdbdf000.iommu + 133, //fdbd0000.rkvenc-core + 134, //fdbef000.iommu + 135, //fdbef000.iommu + 136, //fdbe0000.rkvenc-core + 140, //av1d-master + 141, //fdca0000.iommu + 142, //fdab9000.iommu, fdab0000.npu + 143, //fdab9000.iommu, fdab0000.npu + 144, //fdab9000.iommu, fdab0000.npu + 146, //fdb60f00.iommu, rga3_core0 + 145, //fdce0800.iommu + 147, //fdb60f00.iommu, rga3_core1 + 148, //fdb60f00.iommu, rga2 + 149, //fdbb0800.iommu, fdbb0000.iep + 150, //fdb50800.iommu + 151, //fdb50400.vdpu + 153, //fdba0800.iommu + 154, //fdba0000.jpege-core + 155, //fdba4800.iommu + 156, //fdba4000.jpege-core + 157, //fdba8800.iommu + 158, //fdba8000.jpege-core + 159, //fdbac800.iommu + 160, //fdbac000.jpege-core + 161, //fdb90000.jpegd + 162, //fdb90480.iommu + 179, //rockchip-mipi-csi2 + 180, //rockchip-mipi-csi2 + 187, //rkcifhw + 188, //fdd97e00.iommu, fdd90000.vop + 192, //dw-hdmi-qp-hpd + 193, //fde50000.dp + 199, //fde20000.dsi + 201, //fde80000.hdmi + 203, //fde80000.hdmi + 204, //fde80000.hdmi + 212, //i2s + 217, //i2s + 235, //dw-mci + 237, //mmc0 + 247, //ehci_hcd:usb1 + 248, //ehci_hcd:usb3 + 250, //ehci_hcd:usb2 + 251, //ehci_hcd:usb4 + 252, //dwc3 + 254, //xhci-hcd:usb5 + #[cfg(not(feature = "rk3588-noeth"))] + 265, //eth0 + #[cfg(not(feature = "rk3588-noeth"))] + 266, //eth0 + 275, //eth0 + 276, //pcie + 277, //pcie + 278, //pcie + 279, //pcie + 280, //pcie + 281, //pcie + 282, //pcie + 283, //pcie + 284, //pcie + 285, //pcie + 321, //rk_timer + 347, //feaf0000.watchdog + 349, //fd880000.i2c + 351, //feaa0000.i2c + 352, //feab0000.i2c + 353, //feac0000.i2c + 355, //fec80000.i2c + 356, //fec90000.i2c + 359, //feb10000.spi + 360, //feb20000.spi + 365, //debug + 370, //ttyS7 + 384, + 423, //rockchip_usb2phy + 424, //rockchip_usb2phy + 425, //rockchip_usb2phy + 429, //rockchip_thermal + 430, //fec10000.saradc + 455, //debug-signal + ]; + pt_dev_config.streams_ids = vec![]; + + // vm0 vm_region + let vm_region = vec![ + VmRegion { + ipa_start: 0x09400000, + length: 0x76c00000, + }, + ]; + + // vm0 config + let mvm_config_entry = VmConfigEntry { + id: 0, + name: String::from("RK3588"), + os_type: VmType::VmTOs, + cmdline: + //String::from("storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal dsi-0=2 storagenode=/mmc@fe2e0000 androidboot.verifiedbootstate=orange ro rootwait earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 irqchip.gicv3_pseudo_nmi=0 root=PARTLABEL=rootfs rootfstype=ext4 overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1 coherent_pool=1m systemd.gpt_auto=0 cgroup_enable=memory swapaccount=1 net.ifnames=0"), + String::from("earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ,9600n8 irqchip.gicv3_pseudo_nmi=0 root=/dev/mmcblk0p6 rootfstype=ext4 rootwait rw default_hugepagesz=32M hugepagesz=32M hugepages=4"), + // String::from("earlycon=uart8250,mmio32,0xfeb50000 root=/dev/sda1 rootfstype=ext4 rw rootwait console=ttyFIQ0"), + // String::from("emmc androidboot.storagemedia=emmc androidboot.mode=normal dsi-0=2 storagenode=/mmc@fe2e0000 earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 root=/dev/nfs nfsroot=192.168.106.153:/tftp/rootfs,proto=tcp rw ip=192.168.106.143:192.168.106.153:192.168.106.1:255.255.255.0::eth0:off default_hugepagesz=32M hugepagesz=32M hugepages=4"), + // String::from("earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 irqchip.gicv3_pseudo_nmi=0 root=PARTLABEL=rootfs rootfstype=ext4 rw rootwait overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1 coherent_pool=1m systemd.gpt_auto=0 cgroup_enable=memory swapaccount=1 net.ifnames=0\0"), + // String::from("storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal dsi-0=2 storagenode=/mmc@fe2e0000 androidboot.verifiedbootstate=orange rw rootwait earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 irqchip.gicv3_pseudo_nmi=0 root=PARTLABEL=rootfs rootfstype=ext4 overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1 coherent_pool=1m systemd.gpt_auto=0 cgroup_enable=memory swapaccount=1 net.ifnames=0\0"), + image: Arc::new(Mutex::new(VmImageConfig { + kernel_img_name: Some("Linux-5.10"), + kernel_load_ipa: 0x10080000, + kernel_load_pa: 0, + kernel_entry_point: 0x10080000, + device_tree_load_ipa: 0x10000000, + ramdisk_load_ipa: 0, + mediated_block_index: None, + })), + memory: Arc::new(Mutex::new(VmMemoryConfig { + region: vm_region, + })), + cpu: Arc::new(Mutex::new(VmCpuConfig { + num: 1, + allocate_bitmap: 0b1, + master: 0, + })), + vm_emu_dev_confg: Arc::new(Mutex::new(VmEmulatedDeviceConfigList { emu_dev_list: emu_dev_config })), + vm_pt_dev_confg: Arc::new(Mutex::new(pt_dev_config)), + ..Default::default() + }; + let _ = vm_cfg_add_vm_entry(mvm_config_entry); +} diff --git a/src/config/tx2_def.rs b/src/config/tx2_def.rs index d363639c96dd0f6d30d8052fc2bbf38166326aab..d9f6e44c76b3a22915ac05813152dd6ef637f3d7 100644 --- a/src/config/tx2_def.rs +++ b/src/config/tx2_def.rs @@ -14,35 +14,37 @@ use alloc::vec::Vec; use spin::Mutex; +use crate::arch::traits::InterruptController; use crate::board::{Platform, PlatOperation}; use crate::config::vm_cfg_add_vm_entry; use crate::device::EmuDeviceType; -use crate::kernel::{HVC_IRQ, INTERRUPT_IRQ_GUEST_TIMER, VmType}; +use crate::kernel::{HVC_IRQ, VmType}; use super::{ - PassthroughRegion, vm_cfg_set_config_name, VmConfigEntry, VmCpuConfig, VMDtbDevConfigList, VmEmulatedDeviceConfig, + PassthroughRegion, vm_cfg_set_config_name, VmConfigEntry, VmCpuConfig, VmEmulatedDeviceConfig, VmEmulatedDeviceConfigList, VmImageConfig, VmMemoryConfig, VmPassthroughDeviceConfig, VmRegion, }; +/// Initializes the configuration for the manager VM (VM0). #[rustfmt::skip] pub fn mvm_config_init() { - println!("mvm_config_init() init config for VM0, which is manager VM"); + info!("mvm_config_init() init config for VM0, which is manager VM"); vm_cfg_set_config_name("tx2-default"); // vm0 emu let emu_dev_config = vec![ VmEmulatedDeviceConfig { - name: Some(String::from("interrupt-controller@3881000")), + name: String::from("interrupt-controller@3881000"), base_ipa: Platform::GICD_BASE, length: 0x1000, - irq_id: 0, + irq_id: 25, cfg_list: Vec::new(), emu_type: EmuDeviceType::EmuDeviceTGicd, mediated: false, }, VmEmulatedDeviceConfig { - name: Some(String::from("virtio_net@a001000")), + name: String::from("virtio_net@a001000"), base_ipa: 0xa001000, length: 0x1000, irq_id: 32 + 0x100, @@ -51,7 +53,7 @@ pub fn mvm_config_init() { mediated: false, }, VmEmulatedDeviceConfig { - name: Some(String::from("virtio_console@a002000")), + name: String::from("virtio_console@a002000"), base_ipa: 0xa002000, length: 0x1000, irq_id: 32 + 0x101, @@ -60,7 +62,7 @@ pub fn mvm_config_init() { mediated: false, }, VmEmulatedDeviceConfig { - name: Some(String::from("virtio_console@a003000")), + name: String::from("virtio_console@a003000"), base_ipa: 0xa003000, length: 0x1000, irq_id: 32 + 0x102, @@ -69,7 +71,7 @@ pub fn mvm_config_init() { mediated: false, }, VmEmulatedDeviceConfig { - name: Some(String::from("iommu")), + name: String::from("iommu"), base_ipa: 0x12000000, length: 0x1000000, irq_id: 0, @@ -78,7 +80,7 @@ pub fn mvm_config_init() { mediated: false, }, VmEmulatedDeviceConfig { - name: Some(String::from("vm_service")), + name: String::from("vm_service"), base_ipa: 0, length: 0, irq_id: HVC_IRQ, @@ -149,6 +151,7 @@ pub fn mvm_config_init() { // PassthroughRegion { ipa: 0x03b41000, pa: 0x03b41000, length: 0x1000 }, PassthroughRegion { ipa: 0x03c00000, pa: 0x03c00000, length: 0xa0000, dev_property: true }, PassthroughRegion { ipa: Platform::GICC_BASE, pa: Platform::GICV_BASE, length: 0x2000, dev_property: true }, + // PassthroughRegion { ipa: Platform::GICC_BASE, pa: Platform::GICC_BASE, length: 0x2000, dev_property: true }, PassthroughRegion { ipa: 0x8010000, pa: 0x8010000, length: 0x1000, dev_property: true }, PassthroughRegion { ipa: 0x08030000, pa: 0x08030000, length: 0x1000, dev_property: true }, PassthroughRegion { ipa: 0x08050000, pa: 0x08050000, length: 0x1000, dev_property: true }, @@ -201,7 +204,7 @@ pub fn mvm_config_init() { ]; // 146 is UART_INT pt_dev_config.irqs = vec![ - INTERRUPT_IRQ_GUEST_TIMER, 32, 33, 34, 35, 36, 37, 38, 39, 40, 48, 49, 56, 57, 58, 59, 60, 62, 63, 64, 65, 67, 68, + crate::arch::IntCtrl::IRQ_GUEST_TIMER, 32, 33, 34, 35, 36, 37, 38, 39, 40, 48, 49, 56, 57, 58, 59, 60, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 74, 76, 79, 82, 85, 88, 91, 92, 94, 95, 96, 97, 102, 103, 104, 105, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, Platform::UART_0_INT, 151, 152, @@ -231,10 +234,10 @@ pub fn mvm_config_init() { // vm0 config let mvm_config_entry = VmConfigEntry { id: 0, - name: Some(String::from("privileged")), + name: String::from("privileged"), os_type: VmType::VmTOs, cmdline: - // String::from("earlycon=uart8250,mmio32,0x3100000 console=ttyS0,115200n8 root=/dev/nvme0n1p2 rw audit=0 rootwait default_hugepagesz=32M hugepagesz=32M hugepages=4\0"), + // String::from("earlycon=uart8250,mmio32,0x3100000 console=ttyS0,115200n8 root=/dev/nvme0n1p1 rw audit=0 rootwait default_hugepagesz=32M hugepagesz=32M hugepages=4\0"), String::from("earlycon=uart8250,mmio32,0x3100000 console=ttyS0,115200n8 root=/dev/sda1 rw audit=0 rootwait default_hugepagesz=32M hugepagesz=32M hugepages=5\0"), image: Arc::new(Mutex::new(VmImageConfig { @@ -256,7 +259,7 @@ pub fn mvm_config_init() { })), vm_emu_dev_confg: Arc::new(Mutex::new(VmEmulatedDeviceConfigList { emu_dev_list: emu_dev_config })), vm_pt_dev_confg: Arc::new(Mutex::new(pt_dev_config)), - vm_dtb_devs: Arc::new(Mutex::new(VMDtbDevConfigList::default())), + ..Default::default() }; let _ = vm_cfg_add_vm_entry(mvm_config_entry); } diff --git a/src/config/vm_def.rs b/src/config/vm_def.rs index 7508e404fd9f6ca82b7da0a504822473b590cf31..3b0ac18ff1a91f34d7184d3255757e5f45c117c3 100644 --- a/src/config/vm_def.rs +++ b/src/config/vm_def.rs @@ -14,10 +14,12 @@ use alloc::vec::Vec; use spin::Mutex; +use crate::arch::traits::InterruptController; +use crate::arch::IntCtrl; use crate::board::*; use crate::config::vm_cfg_add_vm_entry; use crate::device::EmuDeviceType; -use crate::kernel::{INTERRUPT_IRQ_GUEST_TIMER, VmType}; +use crate::kernel::VmType; use super::{ PassthroughRegion, VmConfigEntry, VmCpuConfig, VMDtbDevConfigList, VmEmulatedDeviceConfig, @@ -25,12 +27,16 @@ use super::{ AddrRegions, DtbDevType, }; +/// Initializes temporary configuration for the first bare metal app (BMA1). +/// +/// This function configures emulated devices, passthrough devices, memory regions, and other settings +/// for BMA1. It then adds the configuration to the virtual machine manager. pub fn init_tmp_config_for_bma1() { - println!("init_tmp_config_for_bma1"); + info!("init_tmp_config_for_bma1"); // #################### bare metal app emu (vm1) ###################### let mut emu_dev_config: Vec = Vec::new(); emu_dev_config.push(VmEmulatedDeviceConfig { - name: Some(String::from("intc@8000000")), + name: String::from("intc@8000000"), base_ipa: 0x8000000, length: 0x1000, irq_id: 0, @@ -39,7 +45,7 @@ pub fn init_tmp_config_for_bma1() { mediated: false, }); emu_dev_config.push(VmEmulatedDeviceConfig { - name: Some(String::from("virtio_blk@a000000")), + name: String::from("virtio_blk@a000000"), base_ipa: 0xa000000, length: 0x1000, irq_id: 32 + 0x10, @@ -76,7 +82,7 @@ pub fn init_tmp_config_for_bma1() { // bma config let bma_config = VmConfigEntry { id: 0, - name: Some(String::from("guest-bma-0")), + name: String::from("guest-bma-0"), os_type: VmType::VmTBma, memory: Arc::new(Mutex::new(VmMemoryConfig { region: vm_region })), image: Arc::new(Mutex::new(VmImageConfig { @@ -101,16 +107,21 @@ pub fn init_tmp_config_for_bma1() { dtb_device_list: vec![], })), cmdline: String::from(""), + ..Default::default() }; let _ = vm_cfg_add_vm_entry(bma_config); } +/// Initializes temporary configuration for the second bare metal app (BMA2). +/// +/// This function configures emulated devices, passthrough devices, memory regions, and other settings +/// for BMA2. It then adds the configuration to the virtual machine manager. pub fn init_tmp_config_for_bma2() { - println!("init_tmp_config_for_bma2"); + info!("init_tmp_config_for_bma2"); // #################### bare metal app emu (vm1) ###################### let mut emu_dev_config: Vec = Vec::new(); emu_dev_config.push(VmEmulatedDeviceConfig { - name: Some(String::from("intc@8000000")), + name: String::from("intc@8000000"), base_ipa: 0x8000000, length: 0x1000, irq_id: 0, @@ -119,7 +130,7 @@ pub fn init_tmp_config_for_bma2() { mediated: false, }); emu_dev_config.push(VmEmulatedDeviceConfig { - name: Some(String::from("virtio_blk@a000000")), + name: String::from("virtio_blk@a000000"), base_ipa: 0xa000000, length: 0x1000, irq_id: 32 + 0x10, @@ -156,7 +167,7 @@ pub fn init_tmp_config_for_bma2() { // bma config let bma_config = VmConfigEntry { id: 0, - name: Some(String::from("guest-bma-1")), + name: String::from("guest-bma-1"), os_type: VmType::VmTBma, memory: Arc::new(Mutex::new(VmMemoryConfig { region: vm_region })), image: Arc::new(Mutex::new(VmImageConfig { @@ -181,17 +192,22 @@ pub fn init_tmp_config_for_bma2() { dtb_device_list: vec![], })), cmdline: String::from(""), + ..Default::default() }; let _ = vm_cfg_add_vm_entry(bma_config); } +/// Initializes temporary configuration for the first virtual machine (VM1). +/// +/// This function configures emulated devices, passthrough devices, memory regions, and other settings +/// for VM1. It then adds the configuration to the virtual machine manager. pub fn init_tmp_config_for_vm1() { - println!("init_tmp_config_for_vm1"); + info!("init_tmp_config_for_vm1"); // #################### vm1 emu ###################### let mut emu_dev_config: Vec = Vec::new(); emu_dev_config.push(VmEmulatedDeviceConfig { - name: Some(String::from("intc@8000000")), + name: String::from("intc@8000000"), base_ipa: 0x8000000, length: 0x1000, irq_id: 0, @@ -200,7 +216,7 @@ pub fn init_tmp_config_for_vm1() { mediated: false, }); emu_dev_config.push(VmEmulatedDeviceConfig { - name: Some(String::from("virtio_blk@a000000")), + name: String::from("virtio_blk@a000000"), base_ipa: 0xa000000, length: 0x1000, irq_id: 32 + 0x10, @@ -212,7 +228,7 @@ pub fn init_tmp_config_for_vm1() { mediated: true, }); emu_dev_config.push(VmEmulatedDeviceConfig { - name: Some(String::from("virtio_net@a001000")), + name: String::from("virtio_net@a001000"), base_ipa: 0xa001000, length: 0x1000, irq_id: 32 + 0x11, @@ -221,7 +237,7 @@ pub fn init_tmp_config_for_vm1() { mediated: false, }); emu_dev_config.push(VmEmulatedDeviceConfig { - name: Some(String::from("virtio_console@a002000")), + name: String::from("virtio_console@a002000"), base_ipa: 0xa002000, length: 0x1000, irq_id: 32 + 0x12, @@ -230,7 +246,7 @@ pub fn init_tmp_config_for_vm1() { mediated: false, }); // emu_dev_config.push(VmEmulatedDeviceConfig { - // name: Some(String::from("vm_service")), + // name: String::from("vm_service"), // base_ipa: 0, // length: 0, // irq_id: HVC_IRQ, @@ -255,8 +271,7 @@ pub fn init_tmp_config_for_vm1() { dev_property: true, }, ]; - // pt_dev_config.irqs = vec![UART_1_INT, INTERRUPT_IRQ_GUEST_TIMER]; - pt_dev_config.irqs = vec![INTERRUPT_IRQ_GUEST_TIMER]; + pt_dev_config.irqs = vec![IntCtrl::IRQ_GUEST_TIMER]; // vm1 vm_region let mut vm_region: Vec = Vec::new(); @@ -297,7 +312,7 @@ pub fn init_tmp_config_for_vm1() { // vm1 config let vm1_config = VmConfigEntry { id: 1, - name: Some(String::from("guest-os-0")), + name: String::from("guest-os-0"), os_type: VmType::VmTOs, // cmdline: "root=/dev/vda rw audit=0", cmdline: String::from("earlycon console=hvc0,115200n8 root=/dev/vda rw audit=0"), @@ -324,18 +339,23 @@ pub fn init_tmp_config_for_vm1() { vm_dtb_devs: Arc::new(Mutex::new(VMDtbDevConfigList { dtb_device_list: vm_dtb_devs, })), + ..Default::default() }; - println!("generate tmp_config for vm1"); + info!("generate tmp_config for vm1"); let _ = vm_cfg_add_vm_entry(vm1_config); } +/// Initializes temporary configuration for the second virtual machine (VM2). +/// +/// This function configures emulated devices, passthrough devices, memory regions, and other settings +/// for VM2. It then adds the configuration to the virtual machine manager. pub fn init_tmp_config_for_vm2() { - println!("init_tmp_config_for_vm2"); + info!("init_tmp_config_for_vm2"); // #################### vm2 emu ###################### let mut emu_dev_config: Vec = Vec::new(); emu_dev_config.push(VmEmulatedDeviceConfig { - name: Some(String::from("intc@8000000")), + name: String::from("intc@8000000"), base_ipa: 0x8000000, length: 0x1000, irq_id: 0, @@ -344,7 +364,7 @@ pub fn init_tmp_config_for_vm2() { mediated: false, }); emu_dev_config.push(VmEmulatedDeviceConfig { - name: Some(String::from("virtio_blk@a000000")), + name: String::from("virtio_blk@a000000"), base_ipa: 0xa000000, length: 0x1000, irq_id: 32 + 0x10, @@ -353,7 +373,7 @@ pub fn init_tmp_config_for_vm2() { mediated: true, }); emu_dev_config.push(VmEmulatedDeviceConfig { - name: Some(String::from("virtio_net@a001000")), + name: String::from("virtio_net@a001000"), base_ipa: 0xa001000, length: 0x1000, irq_id: 32 + 0x11, @@ -362,7 +382,7 @@ pub fn init_tmp_config_for_vm2() { mediated: false, }); emu_dev_config.push(VmEmulatedDeviceConfig { - name: Some(String::from("virtio_console@a003000")), + name: String::from("virtio_console@a003000"), base_ipa: 0xa003000, length: 0x1000, irq_id: 32 + 0x12, @@ -387,8 +407,7 @@ pub fn init_tmp_config_for_vm2() { dev_property: true, }, ]; - // pt_dev_config.irqs = vec![UART_1_INT, INTERRUPT_IRQ_GUEST_TIMER]; - pt_dev_config.irqs = vec![INTERRUPT_IRQ_GUEST_TIMER]; + pt_dev_config.irqs = vec![IntCtrl::IRQ_GUEST_TIMER]; // vm2 vm_region let mut vm_region: Vec = Vec::new(); @@ -429,7 +448,7 @@ pub fn init_tmp_config_for_vm2() { // vm2 config let vm2_config = VmConfigEntry { id: 2, - name: Some(String::from("guest-os-1")), + name: String::from("guest-os-1"), os_type: VmType::VmTOs, // cmdline: "root=/dev/vda rw audit=0", cmdline: String::from("earlycon console=ttyS0,115200n8 root=/dev/vda rw audit=0"), @@ -456,6 +475,155 @@ pub fn init_tmp_config_for_vm2() { vm_dtb_devs: Arc::new(Mutex::new(VMDtbDevConfigList { dtb_device_list: vm_dtb_devs, })), + ..Default::default() }; let _ = vm_cfg_add_vm_entry(vm2_config); } + +#[cfg(feature = "gicv3")] +pub fn init_gicv3_config_for_vm1() { + info!("init_gicv3_config_for_vm1"); + + // #################### vm1 emu ###################### + let mut emu_dev_config: Vec = Vec::new(); + emu_dev_config.push(VmEmulatedDeviceConfig { + name: String::from("interrupt-controller@fe600000"), + base_ipa: Platform::GICD_BASE, + length: 0x10000, + irq_id: 0, + cfg_list: Vec::new(), + emu_type: EmuDeviceType::EmuDeviceTGicd, + mediated: false, + }); + emu_dev_config.push(VmEmulatedDeviceConfig { + name: String::from("GICR@0xfe680000"), + base_ipa: Platform::GICR_BASE, + length: 0x20000 * PLAT_DESC.cpu_desc.num, + irq_id: 0, + cfg_list: Vec::new(), + emu_type: EmuDeviceType::EmuDeviceTGICR, + mediated: false, + }); + emu_dev_config.push(VmEmulatedDeviceConfig { + name: String::from("virtio_blk@a000000"), + base_ipa: 0xa000000, + length: 0x1000, + irq_id: 32 + 0x10, + // cfg_list: vec![DISK_PARTITION_2_START, DISK_PARTITION_2_SIZE], + // cfg_list: vec![0, 8388608], + // cfg_list: vec![0, 67108864i], // 32G + cfg_list: vec![0, 209715200 / 25], // 100G + emu_type: EmuDeviceType::EmuDeviceTVirtioBlk, + mediated: true, + }); + // emu_dev_config.push(VmEmulatedDeviceConfig { + // name: String::from("virtio_net@a001000"), + // base_ipa: 0xa001000, + // length: 0x1000, + // irq_id: 32 + 0x11, + // cfg_list: vec![0x74, 0x56, 0xaa, 0x0f, 0x47, 0xd1], + // emu_type: EmuDeviceType::EmuDeviceTVirtioNet, + // mediated: false, + // }); + emu_dev_config.push(VmEmulatedDeviceConfig { + name: String::from("virtio_console@a002000"), + base_ipa: 0xa002000, + length: 0x1000, + irq_id: 32 + 0x12, + cfg_list: vec![0, 0xa002000], + emu_type: EmuDeviceType::EmuDeviceTVirtioConsole, + mediated: false, + }); + // emu_dev_config.push(VmEmulatedDeviceConfig { + // name: String::from("vm_service"), + // base_ipa: 0, + // length: 0, + // irq_id: HVC_IRQ, + // cfg_list: Vec::new(), + // emu_type: EmuDeviceType::EmuDeviceTShyper, + // mediated: false, + // }); + + // vm1 passthrough + let mut pt_dev_config: VmPassthroughDeviceConfig = VmPassthroughDeviceConfig::default(); + pt_dev_config.regions = vec![ + // PassthroughRegion { + // ipa: UART_1_ADDR, + // pa: UART_1_ADDR, + // length: 0x1000, + // dev_property: true + // }, + ]; + pt_dev_config.irqs = vec![IntCtrl::IRQ_GUEST_TIMER]; + + // vm1 vm_region + let mut vm_region: Vec = Vec::new(); + vm_region.push(VmRegion { + ipa_start: 0x80000000, + length: 0x40000000, + }); + + let mut vm_dtb_devs: Vec = vec![]; + vm_dtb_devs.push(VmDtbDevConfig { + name: String::from("gicd"), + dev_type: DtbDevType::DevGicd, + irqs: vec![], + addr_region: AddrRegions { + ipa: 0x8000000, + length: 0x1000, + }, + }); + vm_dtb_devs.push(VmDtbDevConfig { + name: String::from("gicc"), + dev_type: DtbDevType::DevGicc, + irqs: vec![], + addr_region: AddrRegions { + ipa: 0x8010000, + length: 0x2000, + }, + }); + // vm_dtb_devs.push(VmDtbDevConfig { + // name: String::from("serial"), + // dev_type: DtbDevType::DevSerial, + // irqs: vec![UART_1_INT], + // addr_region: AddrRegions { + // ipa: UART_1_ADDR, + // length: 0x1000, + // }, + // }); + + // vm1 config + let vm1_config = VmConfigEntry { + id: 1, + name: String::from("guest-os-0"), + os_type: VmType::VmTOs, + // cmdline: "root=/dev/vda rw audit=0", + cmdline: String::from("earlycon console=hvc0,115200n8 root=/dev/vda rw audit=0"), + + image: Arc::new(Mutex::new(VmImageConfig { + kernel_img_name: Some("Image_vanilla"), + kernel_load_ipa: 0x80080000, + kernel_load_pa: 0, + kernel_entry_point: 0x80080000, + device_tree_load_ipa: 0x80000000, + ramdisk_load_ipa: 0, //0x83000000, + mediated_block_index: Some(0), + })), + memory: Arc::new(Mutex::new(VmMemoryConfig { region: vm_region })), + cpu: Arc::new(Mutex::new(VmCpuConfig { + num: 1, + allocate_bitmap: 0b0010, + master: 1, + })), + vm_emu_dev_confg: Arc::new(Mutex::new(VmEmulatedDeviceConfigList { + emu_dev_list: emu_dev_config, + })), + vm_pt_dev_confg: Arc::new(Mutex::new(pt_dev_config)), + vm_dtb_devs: Arc::new(Mutex::new(VMDtbDevConfigList { + dtb_device_list: vm_dtb_devs, + })), + ..Default::default() + }; + info!("generate tmp_config for vm1"); + let _ = vm_cfg_add_vm_entry(vm1_config); +} diff --git a/src/device/device.rs b/src/device/device.rs index ccc3b2976b76d8e588545338c7e16b816a971276..abf1f9fe358a8919530fe70069e75cb48592bf2d 100644 --- a/src/device/device.rs +++ b/src/device/device.rs @@ -12,70 +12,75 @@ use alloc::sync::Arc; use spin::Mutex; +/// Constant representing the specific architecture pub const ARM_CORTEX_A57: u8 = 0; +pub const ARM_CORTEX_A55: u8 = 1; +pub const ARM_CORTEX_A76: u8 = 2; pub const ARM_NVIDIA_DENVER: u8 = 1; +/// Structure representing block device statistics. #[derive(Clone)] pub struct BlkStat { inner: Arc>, } impl BlkStat { + /// Creates a new default instance of `BlkStat`. pub fn default() -> BlkStat { BlkStat { inner: Arc::new(Mutex::new(BlkStatInner::default())), } } - pub fn back_up(&self) -> BlkStat { - let current_inner = self.inner.lock(); - let inner = *current_inner; - BlkStat { - inner: Arc::new(Mutex::new(inner)), - } - } - pub fn read_req(&self) -> usize { let inner = self.inner.lock(); inner.read_req } + /// Retrieves the read bytes statistic. pub fn read_byte(&self) -> usize { let inner = self.inner.lock(); inner.read_byte } + /// Retrieves the write requests statistic. pub fn write_req(&self) -> usize { let inner = self.inner.lock(); inner.write_req } + /// Retrieves the write bytes statistic. pub fn write_byte(&self) -> usize { let inner = self.inner.lock(); inner.write_byte } + /// Sets the read requests statistic. pub fn set_read_req(&self, read_req: usize) { let mut inner = self.inner.lock(); inner.read_req = read_req; } + /// Sets the read bytes statistic. pub fn set_read_byte(&self, read_byte: usize) { let mut inner = self.inner.lock(); inner.read_byte = read_byte; } + /// Sets the write requests statistic. pub fn set_write_req(&self, write_req: usize) { let mut inner = self.inner.lock(); inner.write_req = write_req; } + /// Sets the write bytes statistic. pub fn set_write_byte(&self, write_byte: usize) { let mut inner = self.inner.lock(); inner.write_byte = write_byte; } } +/// Inner structure representing block device statistics. #[derive(Copy, Clone)] struct BlkStatInner { read_req: usize, @@ -85,6 +90,7 @@ struct BlkStatInner { } impl BlkStatInner { + /// Creates a new default instance of `BlkStatInner`. fn default() -> BlkStatInner { BlkStatInner { read_req: 0, @@ -95,83 +101,81 @@ impl BlkStatInner { } } +/// Structure representing network interface controller (NIC) statistics. #[derive(Clone)] pub struct NicStat { inner: Arc>, } impl NicStat { + /// Creates a new default instance of `NicStat`. pub fn default() -> NicStat { NicStat { inner: Arc::new(Mutex::new(NicStatInner::default())), } } - pub fn back_up(&self) -> NicStat { - let current_inner = self.inner.lock(); - let inner = *current_inner; - NicStat { - inner: Arc::new(Mutex::new(inner)), - } - } - - pub fn migrate_save(&self, src_stat: NicStat) { - let mut dst_inner = self.inner.lock(); - let src_inner = src_stat.inner.lock(); - *dst_inner = *src_inner; - } - pub fn send_req(&self) -> usize { let inner = self.inner.lock(); inner.send_req } + /// Retrieves the send bytes statistic. pub fn send_byte(&self) -> usize { let inner = self.inner.lock(); inner.send_byte } + /// Retrieves the discard statistic. pub fn discard(&self) -> usize { let inner = self.inner.lock(); inner.discard } + /// Retrieves the receive requests statistic. pub fn receive_req(&self) -> usize { let inner = self.inner.lock(); inner.receive_req } + /// Retrieves the receive bytes statistic. pub fn receive_byte(&self) -> usize { let inner = self.inner.lock(); inner.receive_byte } + /// Sets the send requests statistic. pub fn set_send_req(&self, req: usize) { let mut inner = self.inner.lock(); inner.send_req = req; } + /// Sets the send bytes statistic. pub fn set_send_byte(&self, byte: usize) { let mut inner = self.inner.lock(); inner.send_byte = byte; } + /// Sets the discard statistic. pub fn set_discard(&self, discard: usize) { let mut inner = self.inner.lock(); inner.discard = discard; } + /// Sets the receive requests statistic. pub fn set_receive_req(&self, receive_req: usize) { let mut inner = self.inner.lock(); inner.receive_req = receive_req; } + /// Sets the receive bytes statistic. pub fn set_receive_byte(&self, receive_byte: usize) { let mut inner = self.inner.lock(); inner.receive_byte = receive_byte; } } +/// Inner structure representing network interface controller (NIC) statistics. #[derive(Copy, Clone)] struct NicStatInner { send_req: usize, @@ -182,6 +186,7 @@ struct NicStatInner { } impl NicStatInner { + /// Creates a new default instance of `NicStatInner`. fn default() -> NicStatInner { NicStatInner { send_req: 0, diff --git a/src/device/device_tree.rs b/src/device/device_tree.rs index cd77db779195022ed465565d8158195166aca839..ffb194db5704a49413574001d80fb90dd5bebc9a 100644 --- a/src/device/device_tree.rs +++ b/src/device/device_tree.rs @@ -10,26 +10,29 @@ use alloc::vec::Vec; -use vm_fdt::{Error, FdtWriter, FdtWriterResult}; +use vm_fdt::{FdtWriter, FdtWriterResult}; use crate::config::{DtbDevType, VmDtbDevConfig}; use crate::config::VmConfigEntry; use crate::device::EmuDeviceType; +use crate::error::Result; use crate::SYSTEM_FDT; use crate::vmm::CPIO_RAMDISK; const PI4_DTB_ADDR: usize = 0xf0000000; -pub fn init_vm0_dtb(dtb: *mut fdt::myctypes::c_void) { +/// Initializes the Device Tree Blob (DTB) for the primary VM (vm0). +/// # Safety: +/// Dtb is a valid pointer to a device tree blob +pub unsafe fn init_vm0_dtb(dtb: *mut fdt::myctypes::c_void) -> Result<()> { + use fdt::*; + info!("fdt {dtb:p} has original size {}", fdt_size(dtb)); #[cfg(feature = "tx2")] - unsafe { - use fdt::*; - println!("fdt orignal size {}", fdt_size(dtb)); + { fdt_pack(dtb); fdt_enlarge(dtb); let r = fdt_del_mem_rsv(dtb, 0); assert_eq!(r, 0); - // fdt_add_mem_rsv(fdt, 0x80000000, 0x10000000); fdt_clear_initrd(dtb); let r = fdt_remove_node(dtb, "/cpus/cpu-map/cluster0/core0\0".as_ptr()); assert_eq!(r, 0); @@ -41,8 +44,6 @@ pub fn init_vm0_dtb(dtb: *mut fdt::myctypes::c_void) { assert_eq!(r, 0); let r = fdt_disable_node(dtb, "/sdhci@3460000\0".as_ptr()); assert_eq!(r, 0); - // fdt_disable_node(dtb, "iommu@12000000\0".as_ptr()); - // assert_eq!(r, 0); let r = fdt_disable_node(dtb, "/sdhci@3440000\0".as_ptr()); assert_eq!(r, 0); let r = fdt_disable_node(dtb, "/serial@c280000\0".as_ptr()); @@ -78,30 +79,27 @@ pub fn init_vm0_dtb(dtb: *mut fdt::myctypes::c_void) { ); assert_eq!(r, 0); let len = fdt_size(dtb); - println!("fdt after patched size {}", len); + info!("fdt after patched size {}", len); let slice = core::slice::from_raw_parts(dtb as *const u8, len as usize); SYSTEM_FDT.call_once(|| slice.to_vec()); } #[cfg(feature = "pi4")] - unsafe { - use fdt::*; - use crate::lib::round_up; + { + use crate::utils::round_up; use crate::arch::PAGE_SIZE; let pi_fdt = PI4_DTB_ADDR as *mut fdt::myctypes::c_void; let len = round_up(fdt_size(pi_fdt) as usize, PAGE_SIZE) + PAGE_SIZE; - println!("fdt orignal size {}", len); + info!("fdt orignal size {}", len); let slice = core::slice::from_raw_parts(pi_fdt as *const u8, len as usize); SYSTEM_FDT.call_once(|| slice.to_vec()); } #[cfg(feature = "qemu")] - unsafe { - use fdt::*; - println!("fdt orignal size {}, ptr {dtb:#p}", fdt_size(dtb)); + { fdt_pack(dtb); fdt_enlarge(dtb); fdt_clear_initrd(dtb); - assert_eq!(fdt_disable_node(dtb, "/platform@c000000\0".as_ptr()), 0); + // assert_eq!(fdt_disable_node(dtb, "/platform@c000000\0".as_ptr()), 0); assert_eq!(fdt_remove_node(dtb, "/fw-cfg@9020000\0".as_ptr()), 0); assert_eq!(fdt_remove_node(dtb, "/memory@40000000\0".as_ptr()), 0); assert_eq!(fdt_remove_node(dtb, "/virtio_mmio@a000000\0".as_ptr()), 0); @@ -143,19 +141,31 @@ pub fn init_vm0_dtb(dtb: *mut fdt::myctypes::c_void) { assert_eq!(fdt_remove_node(dtb, "/pl031@9010000\0".as_ptr()), 0); // pass through the only one uart on qemu-system-aarch64 // assert_eq!(fdt_remove_node(dtb, "/pl011@9000000\0".as_ptr()), 0); - + #[cfg(feature = "gicv3")] + assert_eq!(fdt_remove_node(dtb, "/intc@8000000/its@8080000\0".as_ptr()), 0); + #[cfg(not(feature = "gicv3"))] assert_eq!(fdt_remove_node(dtb, "/intc@8000000/v2m@8020000\0".as_ptr()), 0); - assert_eq!(fdt_remove_node(dtb, "/flash@0\0".as_ptr()), 0); + //assert_eq!(fdt_remove_node(dtb, "/flash@0\0".as_ptr()), 0); let len = fdt_size(dtb) as usize; - println!("fdt patched size {}", len); + info!("fdt patched size {}", len); let slice = core::slice::from_raw_parts(dtb as *const u8, len); SYSTEM_FDT.call_once(|| slice.to_vec()); } + #[cfg(feature = "rk3588")] + { + use fdt::binding::Fdt; + use crate::alloc::borrow::ToOwned; + let mut fdt = Fdt::from_ptr(dtb as *const u8).to_owned(); + crate::config::patch_fdt(&mut fdt)?; + SYSTEM_FDT.call_once(move || fdt.into_inner()); + } + Ok(()) } +/// Creates the Device Tree Blob (DTB) for the secondary VM (vm1) based on the provided configuration. // create vm1 fdt demo -pub fn create_fdt(config: VmConfigEntry) -> Result, Error> { +pub fn create_fdt(config: VmConfigEntry) -> FdtWriterResult> { let mut fdt = FdtWriter::new()?; let root_node = fdt.begin_node("root")?; @@ -171,11 +181,16 @@ pub fn create_fdt(config: VmConfigEntry) -> Result, Error> { fdt.end_node(psci)?; create_memory_node(&mut fdt, config.clone())?; - create_timer_node(&mut fdt, 0x8)?; + if cfg!(feature = "rk3588") { + create_timer_node(&mut fdt, 0xf04)?; + // create_pinctrl_node(&mut fdt)?; + } else { + create_timer_node(&mut fdt, 0x8)?; + } // todo: fix create_chosen_node size create_chosen_node(&mut fdt, &config.cmdline, config.ramdisk_load_ipa(), CPIO_RAMDISK.len())?; create_cpu_node(&mut fdt, config.clone())?; - if config.dtb_device_list().len() > 0 { + if !config.dtb_device_list().is_empty() { create_serial_node(&mut fdt, &config.dtb_device_list())?; } // match &config.vm_dtb_devs { @@ -184,6 +199,9 @@ pub fn create_fdt(config: VmConfigEntry) -> Result, Error> { // } // None => {} // } + #[cfg(feature = "gicv3")] + create_gicv3_node(&mut fdt, config.gicr_addr(), config.gicd_addr())?; + #[cfg(not(feature = "gicv3"))] create_gic_node(&mut fdt, config.gicc_addr(), config.gicd_addr())?; for emu_cfg in config.emulated_device_list() { @@ -191,18 +209,14 @@ pub fn create_fdt(config: VmConfigEntry) -> Result, Error> { EmuDeviceType::EmuDeviceTVirtioBlk | EmuDeviceType::EmuDeviceTVirtioNet | EmuDeviceType::EmuDeviceTVirtioConsole => { - println!( - "virtio fdt node init {} {:x}", - emu_cfg.name.as_ref().unwrap(), - emu_cfg.base_ipa - ); - create_virtio_node(&mut fdt, &emu_cfg.name.unwrap(), emu_cfg.irq_id, emu_cfg.base_ipa)?; + info!("virtio fdt node init {} {:x}", emu_cfg.name, emu_cfg.base_ipa); + create_virtio_node(&mut fdt, &emu_cfg.name, emu_cfg.irq_id, emu_cfg.base_ipa)?; } EmuDeviceType::EmuDeviceTShyper => { - println!("shyper fdt node init {:x}", emu_cfg.base_ipa); + info!("shyper fdt node init {:x}", emu_cfg.base_ipa); create_shyper_node( &mut fdt, - &emu_cfg.name.unwrap(), + &emu_cfg.name, emu_cfg.irq_id, emu_cfg.base_ipa, emu_cfg.length, @@ -216,9 +230,31 @@ pub fn create_fdt(config: VmConfigEntry) -> Result, Error> { fdt.finish() } +/// Creates the pinctrl node in the Device Tree for the VM based on the provided configuration. +// rk3588 has pinctrl for using uart +fn create_pinctrl_node(fdt: &mut FdtWriter) -> FdtWriterResult<()> { + let pinctrl = fdt.begin_node("pinctrl")?; + fdt.property_string("compatible", "rockchip-pinctrl")?; + fdt.property_u32("#address-cells", 0x2)?; + fdt.property_u32("#size-cells", 0x2)?; + + let uart0 = fdt.begin_node("uart0")?; + + let uart0m1 = fdt.begin_node("uart0m0-xfer")?; + fdt.property_array_u32("rockchip,pins", &[0x04, 0x19, 0x0a, 0x179, 0x04, 0x18, 0x0a, 0x179])?; + fdt.property_u32("phandle", 0x14d)?; + fdt.end_node(uart0m1)?; + + fdt.end_node(uart0)?; + + fdt.end_node(pinctrl)?; + Ok(()) +} + +/// Creates the memory node in the Device Tree for the VM based on the provided configuration. // hard code for tx2 vm1 fn create_memory_node(fdt: &mut FdtWriter, config: VmConfigEntry) -> FdtWriterResult<()> { - if config.memory_region().len() == 0 { + if config.memory_region().is_empty() { panic!("create_memory_node memory region num 0"); } let memory_name = format!("memory@{:x}", config.memory_region()[0].ipa_start); @@ -234,6 +270,7 @@ fn create_memory_node(fdt: &mut FdtWriter, config: VmConfigEntry) -> FdtWriterRe Ok(()) } +/// Creates the timer node in the Device Tree for the VM based on the provided configuration. fn create_timer_node(fdt: &mut FdtWriter, trigger_lvl: u32) -> FdtWriterResult<()> { let timer = fdt.begin_node("timer")?; fdt.property_string("compatible", "arm,armv8-timer")?; @@ -258,6 +295,7 @@ fn create_timer_node(fdt: &mut FdtWriter, trigger_lvl: u32) -> FdtWriterResult<( Ok(()) } +/// Creates the CPU node in the Device Tree for the VM based on the provided configuration. fn create_cpu_node(fdt: &mut FdtWriter, config: VmConfigEntry) -> FdtWriterResult<()> { let cpus = fdt.begin_node("cpus")?; fdt.property_u32("#size-cells", 0)?; @@ -265,13 +303,23 @@ fn create_cpu_node(fdt: &mut FdtWriter, config: VmConfigEntry) -> FdtWriterResul let cpu_num = config.cpu_allocated_bitmap().count_ones(); for cpu_id in 0..cpu_num { - let cpu_name = format!("cpu@{:x}", cpu_id); - let cpu_node = fdt.begin_node(&cpu_name)?; - fdt.property_string("compatible", "arm,cortex-a57")?; - fdt.property_string("device_type", "cpu")?; - fdt.property_string("enable-method", "psci")?; - fdt.property_array_u32("reg", &[0, cpu_id as u32])?; - fdt.end_node(cpu_node)?; + if cfg!(feature = "rk3588") { + let cpu_name = format!("cpu@{:x}", cpu_id << 8); + let cpu_node = fdt.begin_node(&cpu_name)?; + fdt.property_string("compatible", "arm,cortex-a55")?; + fdt.property_string("device_type", "cpu")?; + fdt.property_string("enable-method", "psci")?; + fdt.property_array_u32("reg", &[0, cpu_id << 8])?; + fdt.end_node(cpu_node)?; + } else { + let cpu_name = format!("cpu@{:x}", cpu_id); + let cpu_node = fdt.begin_node(&cpu_name)?; + fdt.property_string("compatible", "arm,cortex-a57")?; + fdt.property_string("device_type", "cpu")?; + fdt.property_string("enable-method", "psci")?; + fdt.property_array_u32("reg", &[0, cpu_id])?; + fdt.end_node(cpu_node)?; + } } fdt.end_node(cpus)?; @@ -279,27 +327,34 @@ fn create_cpu_node(fdt: &mut FdtWriter, config: VmConfigEntry) -> FdtWriterResul Ok(()) } -fn create_serial_node(fdt: &mut FdtWriter, devs_config: &Vec) -> FdtWriterResult<()> { +/// Creates the serial node in the Device Tree for the VM based on the provided configuration. +fn create_serial_node(fdt: &mut FdtWriter, devs_config: &[VmDtbDevConfig]) -> FdtWriterResult<()> { for dev in devs_config { - match dev.dev_type { - DtbDevType::DevSerial => { - let serial_name = format!("serial@{:x}", dev.addr_region.ipa); - let serial = fdt.begin_node(&serial_name)?; + if dev.dev_type == DtbDevType::DevSerial { + let serial_name = format!("serial@{:x}", dev.addr_region.ipa); + let serial = fdt.begin_node(&serial_name)?; + if cfg!(feature = "rk3588") { + fdt.property_string("compatible", "snps,dw-apb-uart")?; + } else { fdt.property_string("compatible", "ns16550")?; - fdt.property_array_u64("reg", &[dev.addr_region.ipa as u64, 0x1000])?; - fdt.property_u32("reg-shift", 0x2)?; - fdt.property_array_u32("interrupts", &[0x0, (dev.irqs[0] - 32) as u32, 0x4])?; - fdt.property_u32("clock-frequency", 408000000)?; - // fdt.property_string("status", "disabled")?; - fdt.end_node(serial)?; } - _ => {} + fdt.property_u32("clock-frequency", 408000000)?; + fdt.property_array_u64("reg", &[dev.addr_region.ipa as u64, 0x1000])?; + fdt.property_u32("reg-shift", 0x2)?; + fdt.property_array_u32("interrupts", &[0x0, (dev.irqs[0] - 32) as u32, 0x4])?; + fdt.property_string("status", "okay")?; + // if cfg!(feature = "rk3588") { + // fdt.property_string("pinctrl-names", "default")?; + // fdt.property_u32("pinctrl-0", 0x14d)?; + // } + fdt.end_node(serial)?; } } Ok(()) } +/// Creates the chosen node in the Device Tree for the VM based on the provided configuration. fn create_chosen_node(fdt: &mut FdtWriter, cmdline: &str, ipa: usize, size: usize) -> FdtWriterResult<()> { let chosen = fdt.begin_node("chosen")?; fdt.property_string("bootargs", cmdline)?; @@ -309,6 +364,7 @@ fn create_chosen_node(fdt: &mut FdtWriter, cmdline: &str, ipa: usize, size: usiz Ok(()) } +/// Creates the GIC (Generic Interrupt Controller) node in the Device Tree for the VM based on the provided configuration. fn create_gic_node(fdt: &mut FdtWriter, gicc_addr: usize, gicd_addr: usize) -> FdtWriterResult<()> { let gic_name = format!("interrupt-controller@{:x}", gicd_addr); let gic = fdt.begin_node(&gic_name)?; @@ -323,6 +379,24 @@ fn create_gic_node(fdt: &mut FdtWriter, gicc_addr: usize, gicd_addr: usize) -> F Ok(()) } +/// Creates the GICv3 (Generic Interrupt Controller version 3) node in the Device Tree for the VM based on the provided configuration. +fn create_gicv3_node(fdt: &mut FdtWriter, gicr_addr: usize, gicd_addr: usize) -> FdtWriterResult<()> { + info!("create_gicv3_node"); + let gic_name = format!("interrupt-controller@{:x}", gicd_addr); + let gic = fdt.begin_node(&gic_name)?; + + fdt.property_u32("phandle", 0x8001)?; + fdt.property_array_u64("reg", &[gicd_addr as u64, 0x10000, gicr_addr as u64, 0x100000])?; + fdt.property_string("compatible", "arm,gic-v3")?; + fdt.property_u32("#interrupt-cells", 0x03)?; + fdt.property_null("interrupt-controller")?; + fdt.property_null("ranges")?; + fdt.end_node(gic)?; + + Ok(()) +} + +/// Creates a Virtio node in the Device Tree for the VM based on the provided configuration. fn create_virtio_node(fdt: &mut FdtWriter, name: &str, irq: usize, address: usize) -> FdtWriterResult<()> { let virtio = fdt.begin_node(name)?; fdt.property_null("dma-coherent")?; @@ -334,6 +408,7 @@ fn create_virtio_node(fdt: &mut FdtWriter, name: &str, irq: usize, address: usiz Ok(()) } +/// Creates a Shyper (Sample Hypervisor) node in the Device Tree for the VM based on the provided configuration. fn create_shyper_node(fdt: &mut FdtWriter, name: &str, irq: usize, address: usize, len: usize) -> FdtWriterResult<()> { let shyper = fdt.begin_node(name)?; fdt.property_string("compatible", "shyper")?; diff --git a/src/device/emu.rs b/src/device/emu.rs index 215bc6ad5454f5298e772824ded7e0e8691d9768..41b464c04a0a55ba2e816f4b2217b2b065932292 100644 --- a/src/device/emu.rs +++ b/src/device/emu.rs @@ -11,20 +11,19 @@ use alloc::sync::Arc; use alloc::vec::Vec; use core::fmt::{Display, Formatter}; +use core::ptr; use spin::Mutex; +use spin::RwLock; use crate::arch::Vgic; -use crate::device::{ - virtio_blk_notify_handler, virtio_console_notify_handler, virtio_mediated_blk_notify_handler, - virtio_net_notify_handler, VirtioMmio, -}; +use crate::device::VirtioMmio; use crate::kernel::current_cpu; -use crate::lib::in_range; +use crate::utils::in_range; -pub const EMU_DEV_NUM_MAX: usize = 32; pub static EMU_DEVS_LIST: Mutex> = Mutex::new(Vec::new()); +/// Enumeration representing various emulator devices. #[derive(Clone)] pub enum EmuDevs { Vgic(Arc), @@ -33,50 +32,7 @@ pub enum EmuDevs { VirtioConsole(VirtioMmio), None, } - -impl EmuDevs { - pub fn migrate_emu_devs(&mut self, src_dev: EmuDevs) { - match self { - EmuDevs::Vgic(vgic) => { - if let EmuDevs::Vgic(src_vgic) = src_dev { - vgic.save_vgic(src_vgic); - } else { - println!("EmuDevs::migrate_save: illegal src dev type for vgic"); - } - } - EmuDevs::VirtioBlk(mmio) => { - if let EmuDevs::VirtioBlk(src_mmio) = src_dev { - mmio.save_mmio( - src_mmio.clone(), - if src_mmio.dev().mediated() { - Some(virtio_mediated_blk_notify_handler) - } else { - Some(virtio_blk_notify_handler) - }, - ); - } else { - println!("EmuDevs::migrate_save: illegal src dev type for virtio blk"); - } - } - EmuDevs::VirtioNet(mmio) => { - if let EmuDevs::VirtioNet(src_mmio) = src_dev { - mmio.save_mmio(src_mmio, Some(virtio_net_notify_handler)); - } else { - println!("EmuDevs::migrate_save: illegal src dev type for virtio net"); - } - } - EmuDevs::VirtioConsole(mmio) => { - if let EmuDevs::VirtioConsole(src_mmio) = src_dev { - mmio.save_mmio(src_mmio, Some(virtio_console_notify_handler)); - } else { - println!("EmuDevs::migrate_save: illegal src dev type for virtio console"); - } - } - EmuDevs::None => {} - } - } -} - +#[derive(Debug, Clone, Copy)] pub struct EmuContext { pub address: usize, pub width: usize, @@ -86,17 +42,54 @@ pub struct EmuContext { pub reg_width: usize, } +impl EmuContext { + /// Reads from the specified emulator context. + /// # Safety: + /// The address must be readable. + pub unsafe fn read(&self) -> usize { + match self.width { + 1 => ptr::read_volatile(self.address as *const u8) as usize, + 2 => ptr::read_volatile(self.address as *const u16) as usize, + 4 => ptr::read_volatile(self.address as *const u32) as usize, + 8 => ptr::read_volatile(self.address as *const u64) as usize, + _ => panic!("unexpected read width {} at {:x}", self.width, self.address), + } + } + + /// Writes to the specified emulator context. + /// # Safety: + /// The address must be writable. + pub unsafe fn write(&self, val: usize) { + match self.width { + 1 => ptr::write_volatile(self.address as *mut u8, val as u8), + 2 => ptr::write_volatile(self.address as *mut u16, val as u16), + 4 => ptr::write_volatile(self.address as *mut u32, val as u32), + 8 => ptr::write_volatile(self.address as *mut u64, val as u64), + _ => panic!("unexpected write width {} at {:x}", self.width, self.address), + } + } +} + +/// Struct representing an emulator device entry. pub struct EmuDevEntry { + /// The type of the emulator device. pub emu_type: EmuDeviceType, + /// The virtual machine ID associated with the emulator device. pub vm_id: usize, + /// The ID of the emulator device. pub id: usize, + /// The inmediate physical address associated with the emulator device. pub ipa: usize, + /// The size of the emulator device. pub size: usize, + /// The handler function for the emulator device. pub handler: EmuDevHandler, } +/// Enumeration representing the type of emulator devices. #[derive(Clone, Copy, Debug, PartialEq)] pub enum EmuDeviceType { + // Variants representing different emulator device types. EmuDeviceTConsole = 0, EmuDeviceTGicd = 1, EmuDeviceTGPPT = 2, @@ -106,9 +99,14 @@ pub enum EmuDeviceType { EmuDeviceTShyper = 6, EmuDeviceTVirtioBlkMediated = 7, EmuDeviceTIOMMU = 8, + EmuDeviceTICCSRE = 9, + EmuDeviceTSGIR = 10, + EmuDeviceTGICR = 11, + EmuDeviceTMeta = 12, } impl Display for EmuDeviceType { + // Implementation of the Display trait for EmuDeviceType. fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { match self { EmuDeviceType::EmuDeviceTConsole => write!(f, "console"), @@ -120,20 +118,29 @@ impl Display for EmuDeviceType { EmuDeviceType::EmuDeviceTShyper => write!(f, "device shyper"), EmuDeviceType::EmuDeviceTVirtioBlkMediated => write!(f, "medaited virtio block"), EmuDeviceType::EmuDeviceTIOMMU => write!(f, "IOMMU"), + EmuDeviceType::EmuDeviceTICCSRE => write!(f, "interrupt ICC SRE"), + EmuDeviceType::EmuDeviceTSGIR => write!(f, "interrupt ICC SGIR"), + EmuDeviceType::EmuDeviceTGICR => write!(f, "interrupt controller gicr"), + EmuDeviceType::EmuDeviceTMeta => write!(f, "meta device"), } } } +/// Implementation of methods for EmuDeviceType. impl EmuDeviceType { + // Implementation of methods for EmuDeviceType. pub fn removable(&self) -> bool { - match *self { + matches!( + *self, EmuDeviceType::EmuDeviceTGicd - | EmuDeviceType::EmuDeviceTGPPT - | EmuDeviceType::EmuDeviceTVirtioBlk - | EmuDeviceType::EmuDeviceTVirtioNet - | EmuDeviceType::EmuDeviceTVirtioConsole => true, - _ => false, - } + | EmuDeviceType::EmuDeviceTSGIR + | EmuDeviceType::EmuDeviceTICCSRE + | EmuDeviceType::EmuDeviceTGPPT + | EmuDeviceType::EmuDeviceTVirtioBlk + | EmuDeviceType::EmuDeviceTVirtioNet + | EmuDeviceType::EmuDeviceTGICR + | EmuDeviceType::EmuDeviceTVirtioConsole + ) } } @@ -149,6 +156,10 @@ impl EmuDeviceType { 6 => EmuDeviceType::EmuDeviceTShyper, 7 => EmuDeviceType::EmuDeviceTVirtioBlkMediated, 8 => EmuDeviceType::EmuDeviceTIOMMU, + 9 => EmuDeviceType::EmuDeviceTICCSRE, + 10 => EmuDeviceType::EmuDeviceTSGIR, + 11 => EmuDeviceType::EmuDeviceTGICR, + 12 => EmuDeviceType::EmuDeviceTMeta, _ => panic!("Unknown EmuDeviceType value: {}", value), } } @@ -156,6 +167,7 @@ impl EmuDeviceType { pub type EmuDevHandler = fn(usize, &EmuContext) -> bool; +/// Function to handle emulator operations based on the emulator context. // TO CHECK pub fn emu_handler(emu_ctx: &EmuContext) -> bool { let ipa = emu_ctx.address; @@ -164,7 +176,7 @@ pub fn emu_handler(emu_ctx: &EmuContext) -> bool { for emu_dev in &*emu_devs_list { let active_vcpu = current_cpu().active_vcpu.clone().unwrap(); if active_vcpu.vm_id() == emu_dev.vm_id && in_range(ipa, emu_dev.ipa, emu_dev.size - 1) { - // if current_cpu().id == 2 { + // if current_cpu().id == 1 { // println!("emu dev {:#?} handler", emu_dev.emu_type); // } let handler = emu_dev.handler; @@ -173,14 +185,15 @@ pub fn emu_handler(emu_ctx: &EmuContext) -> bool { return handler(id, emu_ctx); } } - println!( + error!( "emu_handler: no emul handler for Core {} data abort ipa 0x{:x}", current_cpu().id, ipa ); - return false; + false } +/// Function to register an emulator device. pub fn emu_register_dev( emu_type: EmuDeviceType, vm_id: usize, @@ -190,9 +203,6 @@ pub fn emu_register_dev( handler: EmuDevHandler, ) { let mut emu_devs_list = EMU_DEVS_LIST.lock(); - if emu_devs_list.len() >= EMU_DEV_NUM_MAX { - panic!("emu_register_dev: can't register more devs"); - } for emu_dev in &*emu_devs_list { if vm_id != emu_dev.vm_id { @@ -213,6 +223,7 @@ pub fn emu_register_dev( }); } +/// Function to remove an emulator device. pub fn emu_remove_dev(vm_id: usize, dev_id: usize, address: usize, size: usize) { let mut emu_devs_list = EMU_DEVS_LIST.lock(); for (idx, emu_dev) in emu_devs_list.iter().enumerate() { @@ -226,3 +237,69 @@ pub fn emu_remove_dev(vm_id: usize, dev_id: usize, address: usize, size: usize) address, size ); } + +/// Static RwLock containing a vector of EmuRegEntry instances, representing emulator registers. +static EMU_REGS_LIST: RwLock> = RwLock::new(Vec::new()); + +/// Handles emulator register operations based on the provided EmuContext. +pub fn emu_reg_handler(emu_ctx: &EmuContext) -> bool { + let address = emu_ctx.address; + let active_vcpu = current_cpu().active_vcpu.as_ref().unwrap(); + let vm_id = active_vcpu.vm_id(); + + let emu_regs_list = EMU_REGS_LIST.read(); + for emu_reg in emu_regs_list.iter() { + if emu_reg.addr == address { + let handler = emu_reg.handler; + drop(emu_regs_list); + return handler(vm_id, emu_ctx); + } + } + error!( + "emu_reg_handler: no handler for Core{} {} reg ({:#x})", + current_cpu().id, + if emu_ctx.write { "write" } else { "read" }, + address + ); + false +} + +/// Registers a new emulator register with the specified type, address, and handler function. +pub fn emu_register_reg(emu_type: EmuRegType, address: usize, handler: EmuRegHandler) { + let mut emu_regs_list = EMU_REGS_LIST.write(); + + for emu_reg in emu_regs_list.iter() { + if address == emu_reg.addr { + warn!( + "emu_register_reg: duplicated emul reg addr: prev address {:#x}", + address + ); + return; + } + } + + emu_regs_list.push(EmuRegEntry { + emu_type, + addr: address, + handler, + }); +} + +/// Type alias for the handler function of emulator registers. +type EmuRegHandler = EmuDevHandler; + +/// Struct representing an entry in the emulator register list. +pub struct EmuRegEntry { + /// The type of the emulator register. + pub emu_type: EmuRegType, + /// The address associated with the emulator register. + pub addr: usize, + /// The handler function for the emulator register. + pub handler: EmuRegHandler, +} + +/// Enumeration representing the type of emulator registers. +pub enum EmuRegType { + /// System register type for emulator registers. + SysReg, +} diff --git a/src/device/meta/meta.rs b/src/device/meta/meta.rs new file mode 100644 index 0000000000000000000000000000000000000000..c96473621bc7199728b4cd96c3d06808d12d1eef --- /dev/null +++ b/src/device/meta/meta.rs @@ -0,0 +1,58 @@ +use crate::config::VmEmulatedDeviceConfig; +use crate::device::EmuContext; +use crate::device::meta::dispatch; +use crate::kernel::Vm; +use crate::error::{ErrorKind, Result}; +use alloc::collections::BTreeMap; +use alloc::boxed::Box; + +use spin::RwLock; + +/// Metadata context containing device ID and emulation context. +#[derive(Debug, Clone, Copy)] +pub struct MetaContext { + pub dev_id: usize, + pub ctx: EmuContext, +} + +/// Trait for meta devices, defining methods for instantiation and handling. +pub trait MetaDevice { + fn new(vm: &Vm, dev_id: usize, args: &str) -> Result + where + Self: Sized; + fn handle(&self, ctx: MetaContext); +} + +/// Type alias for a boxed trait object representing a meta device. +pub type MetaDev = Box; + +/// Static storage for meta devices, protected by a read-write lock. +static META_DEVICES: RwLock> = RwLock::new(BTreeMap::new()); + +/// Handles the emulation of meta devices. +pub fn emu_meta_handler(dev_id: usize, ctx: &EmuContext) -> bool { + let devs = META_DEVICES.read(); + if let Some(dev) = devs.get(&dev_id) { + dev.handle(MetaContext { dev_id, ctx: *ctx }); + true + } else { + false + } +} + +/// Registers a meta device in the system. +pub fn register(dev_id: usize, vm: &Vm, cfg: &VmEmulatedDeviceConfig) -> Result<()> { + let dev = dispatch(vm, dev_id, &cfg.name)?; + let mut devs = META_DEVICES.write(); + if devs.contains_key(&dev_id) { + return ErrorKind::AlreadyExists.into(); + } + devs.insert(dev_id, dev); + Ok(()) +} + +/// Unregisters a meta device from the system. +pub fn unregister(dev_id: usize) { + let mut devs = META_DEVICES.write(); + devs.remove(&dev_id).unwrap(); +} diff --git a/src/device/meta/mod.rs b/src/device/meta/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..ba731a699f50d33fb9b61553f8fccbe4008ace91 --- /dev/null +++ b/src/device/meta/mod.rs @@ -0,0 +1,21 @@ +//! Dispatches the creation of a meta device based on the provided arguments. + +use crate::kernel::Vm; +use crate::error::{Result, ErrorKind}; +use alloc::boxed::Box; + +mod meta; +pub mod rk_cru; + +pub use meta::*; + +// TODO: dispatch via a derive macro and link-time symbols +fn dispatch(vm: &Vm, dev_id: usize, arg: &str) -> Result { + let p = arg.find(' ').unwrap_or(arg.len()); + Ok(Box::new(match &arg[..p] { + "rk_cru" => rk_cru::RockchipGuestCru::new(vm, dev_id, arg), + _ => { + return ErrorKind::NotFound.into(); + } + }?)) +} diff --git a/src/device/meta/rk_cru.rs b/src/device/meta/rk_cru.rs new file mode 100644 index 0000000000000000000000000000000000000000..771e72ef48123f3befb57ee39077c02143c5a539 --- /dev/null +++ b/src/device/meta/rk_cru.rs @@ -0,0 +1,52 @@ +use core::sync::atomic::{AtomicBool, Ordering}; +use crate::device::meta::*; +use crate::kernel::{current_cpu, Vm}; +use crate::error::Result; + +/// Emulated device representing the Rockchip Clock and Reset Unit (CRU). +/// +/// The `RockchipGuestCru` struct implements the `MetaDevice` trait and serves as an +/// emulated device for the Rockchip Clock and Reset Unit (CRU). It handles read and write +/// operations, refusing writes to specific addresses. +pub struct RockchipGuestCru { + /// Atomic flag indicating whether the device should refuse further writes. + flag: AtomicBool, +} + +impl MetaDevice for RockchipGuestCru { + /// Creates a new instance of the `RockchipGuestCru` device. + fn new(vm: &Vm, dev_id: usize, arg: &str) -> Result { + info!("rk_cru: vm {}, dev {dev_id}: {arg}", vm.id()); + Ok(Self { + flag: AtomicBool::new(false), + }) + } + + /// Handles read and write operations for the Rockchip CRU device. + fn handle(&self, ctx: MetaContext) { + let dev_id = ctx.dev_id; + let ctx = ctx.ctx; + + if ctx.write { + let val = current_cpu().get_gpr(ctx.reg); + trace!( + "rk_cru: {} writing {:x} to {:x}, {:?}", + dev_id, + val, + ctx.address, + current_cpu().ctx().unwrap() + ); + if ctx.address == 0xfd7c0834 || ctx.address == 0xfd7c087c { + debug!("rk_cru: {dev_id} refusing to write at {:x}", ctx.address); + self.flag.store(true, Ordering::SeqCst); + } else if self.flag.load(Ordering::SeqCst) { + debug!("rk_cru: {dev_id} still refusing to write at {:x}", ctx.address); + } else { + unsafe { ctx.write(val) }; + } + } else { + let val = unsafe { ctx.read() }; + current_cpu().set_gpr(ctx.reg, val); + } + } +} diff --git a/src/device/mod.rs b/src/device/mod.rs index 6d4def7ab4bd34aba39cddcf4b6e13adbb83a241..4aa0c224270258ea0496af2f453b56ff6e28cef9 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -8,6 +8,8 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +//! Device module, including device tree, device model, and device emulation. + pub use self::device::*; pub use self::device_tree::*; pub use self::emu::*; @@ -16,4 +18,5 @@ pub use self::virtio::*; mod device; mod device_tree; mod emu; +pub mod meta; mod virtio; diff --git a/src/device/virtio/blk.rs b/src/device/virtio/blk.rs index 664aceb9b5cf88170e7211e61ba17a0b5dedfc2f..a9cc519e5823f7ca033261758cefc90a94e41cc0 100644 --- a/src/device/virtio/blk.rs +++ b/src/device/virtio/blk.rs @@ -18,7 +18,7 @@ use crate::kernel::{ active_vm_id, add_async_task, async_blk_id_req, async_blk_io_req, async_ipi_req, AsyncTask, AsyncTaskData, AsyncTaskState, IoAsyncMsg, IoIdAsyncMsg, IpiMediatedMsg, push_used_info, Vm, vm_ipa2pa, }; -use crate::lib::{memcpy_safe, trace}; +use crate::utils::{memcpy, trace}; pub const VIRTQUEUE_BLK_MAX_SIZE: usize = 256; pub const VIRTQUEUE_NET_MAX_SIZE: usize = 256; @@ -43,6 +43,7 @@ pub const VIRTIO_BLK_S_OK: usize = 0; // pub const VIRTIO_BLK_S_IOERR: usize = 1; pub const VIRTIO_BLK_S_UNSUPP: usize = 2; +/// Represents the geometry information of a block device. #[repr(C)] #[derive(Copy, Clone)] struct BlkGeometry { @@ -52,6 +53,7 @@ struct BlkGeometry { } impl BlkGeometry { + /// Creates a default `BlkGeometry` instance. fn default() -> BlkGeometry { BlkGeometry { cylinders: 0, @@ -61,6 +63,7 @@ impl BlkGeometry { } } +/// Represents the topology information of a block device. #[repr(C)] #[derive(Copy, Clone)] struct BlkTopology { @@ -75,6 +78,7 @@ struct BlkTopology { } impl BlkTopology { + /// Creates a default `BlkTopology` instance. fn default() -> BlkTopology { BlkTopology { physical_block_exp: 0, @@ -85,45 +89,48 @@ impl BlkTopology { } } +/// Represents a block descriptor. #[derive(Clone)] pub struct BlkDesc { inner: Arc>, } impl BlkDesc { + /// Creates a default `BlkDesc` instance. pub fn default() -> BlkDesc { BlkDesc { inner: Arc::new(Mutex::new(BlkDescInner::default())), } } - pub fn back_up(&self) -> BlkDesc { - let current_inner = self.inner.lock(); - let inner = *current_inner; - BlkDesc { - inner: Arc::new(Mutex::new(inner)), - } - } + /// Initializes the block descriptor configuration. pub fn cfg_init(&self, bsize: usize) { let mut inner = self.inner.lock(); inner.cfg_init(bsize); } + /// Gets the start address of the block descriptor. pub fn start_addr(&self) -> usize { let inner = self.inner.lock(); &inner.capacity as *const _ as usize } - pub fn offset_data(&self, offset: usize) -> u32 { + /// # Safety: + /// Caller must ensure offset is valid + /// Offset must valid for virtio_mmio + pub unsafe fn offset_data(&self, offset: usize, width: usize) -> usize { let start_addr = self.start_addr(); - if trace() && start_addr + offset < 0x1000 { - panic!("illegal addr {:x}", start_addr + offset); + match width { + 1 => unsafe { *((start_addr + offset) as *const u8) as usize }, + 2 => unsafe { *((start_addr + offset) as *const u16) as usize }, + 4 => unsafe { *((start_addr + offset) as *const u32) as usize }, + 8 => unsafe { *((start_addr + offset) as *const u64) as usize }, + _ => 0, } - let value = unsafe { *((start_addr + offset) as *const u32) }; - return value; } } +/// Represents the inner data structure of a block descriptor. #[repr(C)] #[derive(Copy, Clone)] pub struct BlkDescInner { @@ -145,6 +152,7 @@ pub struct BlkDescInner { } impl BlkDescInner { + /// Creates a default `BlkDescInner` instance. pub fn default() -> BlkDescInner { BlkDescInner { capacity: 0, @@ -165,6 +173,7 @@ impl BlkDescInner { } } + /// Initializes the block descriptor configuration. pub fn cfg_init(&mut self, bsize: usize) { self.capacity = bsize; self.size_max = BLOCKIF_SIZE_MAX as u32; @@ -172,6 +181,7 @@ impl BlkDescInner { } } +/// Represents a block I/O vector. #[repr(C)] #[derive(Clone)] pub struct BlkIov { @@ -179,12 +189,14 @@ pub struct BlkIov { pub len: u32, } +/// Represents a region of a block request. #[repr(C)] pub struct BlkReqRegion { pub start: usize, pub size: usize, } +/// Represents a VirtioBlk request. #[derive(Clone)] pub struct VirtioBlkReq { inner: Arc>, @@ -192,6 +204,7 @@ pub struct VirtioBlkReq { } impl VirtioBlkReq { + /// Creates a default `VirtioBlkReq` instance. pub fn default() -> VirtioBlkReq { VirtioBlkReq { inner: Arc::new(Mutex::new(VirtioBlkReqInner::default())), @@ -199,50 +212,6 @@ impl VirtioBlkReq { } } - pub fn back_up(&self) -> VirtioBlkReq { - let current_inner = self.inner.lock(); - let current_req_list = self.req_list.lock(); - let inner = VirtioBlkReqInner { - region: BlkReqRegion { - start: current_inner.region.start, - size: current_inner.region.size, - }, - mediated: current_inner.mediated, - process_list: { - let mut list = vec![]; - for process in current_inner.process_list.iter() { - list.push(*process) - } - list - }, - }; - let mut req_list = vec![]; - for req in current_req_list.iter() { - req_list.push(VirtioBlkReqNode { - req_type: req.req_type, - reserved: req.reserved, - sector: req.sector, - desc_chain_head_idx: req.desc_chain_head_idx, - iov: { - let mut iov = vec![]; - for io in req.iov.iter() { - iov.push(BlkIov { - data_bg: io.data_bg, - len: io.len, - }); - } - iov - }, - iov_sum_up: req.iov_sum_up, - iov_total: req.iov_total, - }); - } - VirtioBlkReq { - inner: Arc::new(Mutex::new(inner)), - req_list: Arc::new(Mutex::new(req_list)), - } - } - pub fn add_req_node(&self, node: VirtioBlkReqNode, _vm: Vm) { let mut list = self.req_list.lock(); // let mediated_blk = mediated_blk_list_get(vm.med_blk_id()); @@ -267,52 +236,62 @@ impl VirtioBlkReq { // } } + /// Gets the number of requests in the request list. pub fn req_num(&self) -> usize { let list = self.req_list.lock(); list.len() } + /// Gets a request node at a specified index from the request list. pub fn req_node(&self, idx: usize) -> VirtioBlkReqNode { let list = self.req_list.lock(); list[idx].clone() } + /// Clears the request list. pub fn clear_node(&self) { let mut list = self.req_list.lock(); list.clear(); } + /// Sets the start address of the block request region. pub fn set_start(&self, start: usize) { let mut inner = self.inner.lock(); inner.set_start(start); } + /// Sets the size of the block request region. pub fn set_size(&self, size: usize) { let mut inner = self.inner.lock(); inner.set_size(size); } + /// Sets whether the request is mediated. pub fn set_mediated(&self, mediated: bool) { let mut inner = self.inner.lock(); inner.mediated = mediated; } + /// Checks if the request is mediated. pub fn mediated(&self) -> bool { let inner = self.inner.lock(); inner.mediated } + /// Gets the start address of the block request region. pub fn region_start(&self) -> usize { let inner = self.inner.lock(); inner.region.start } + /// Gets the size of the block request region. pub fn region_size(&self) -> usize { let inner = self.inner.lock(); inner.region.size } } +/// Represents a node in a VirtioBlk request. #[repr(C)] #[derive(Clone)] pub struct VirtioBlkReqNode { @@ -328,6 +307,7 @@ pub struct VirtioBlkReqNode { } impl VirtioBlkReqNode { + /// Creates a default `VirtioBlkReqNode` instance. pub fn default() -> VirtioBlkReqNode { VirtioBlkReqNode { req_type: 0, @@ -341,6 +321,7 @@ impl VirtioBlkReqNode { } } +/// Represents the inner data structure of a VirtioBlkReq. #[repr(C)] struct VirtioBlkReqInner { region: BlkReqRegion, @@ -349,6 +330,7 @@ struct VirtioBlkReqInner { } impl VirtioBlkReqInner { + /// Creates a default `VirtioBlkReqInner` instance. pub fn default() -> VirtioBlkReqInner { VirtioBlkReqInner { region: BlkReqRegion { start: 0, size: 0 }, @@ -357,15 +339,18 @@ impl VirtioBlkReqInner { } } + /// Sets the start address of the block request region. pub fn set_start(&mut self, start: usize) { self.region.start = start; } + /// Sets the size of the block request region. pub fn set_size(&mut self, size: usize) { self.region.size = size; } } +/// Generates a block request using the provided parameters. pub fn generate_blk_req(req: VirtioBlkReq, vq: Virtq, dev: VirtioMmio, cache: usize, vm: Vm) { let region_start = req.region_start(); let region_size = req.region_size(); @@ -374,7 +359,7 @@ pub fn generate_blk_req(req: VirtioBlkReq, vq: Virtq, dev: VirtioMmio, cache: us let req_node = req.req_node(idx); let sector = req_node.sector; if sector + req_node.iov_sum_up / SECTOR_BSIZE > region_start + region_size { - println!( + warn!( "blk_req_handler: {} out of vm range", if req_node.req_type == VIRTIO_BLK_T_IN as u32 { "read" @@ -412,14 +397,19 @@ pub fn generate_blk_req(req: VirtioBlkReq, vq: Virtq, dev: VirtioMmio, cache: us let len = iov.len as usize; if len < SECTOR_BSIZE { - println!("blk_req_handler: read len < SECTOR_BSIZE"); + warn!("blk_req_handler: read len < SECTOR_BSIZE"); continue; } if !req.mediated() { if trace() && (data_bg < 0x1000 || cache_ptr < 0x1000) { panic!("illegal des addr {:x}, src addr {:x}", data_bg, cache_ptr); } - memcpy_safe(data_bg as *mut u8, cache_ptr as *mut u8, len); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy(data_bg as *mut u8, cache_ptr as *mut u8, len); + } } cache_ptr += len; } @@ -429,14 +419,19 @@ pub fn generate_blk_req(req: VirtioBlkReq, vq: Virtq, dev: VirtioMmio, cache: us let data_bg = iov.data_bg; let len = iov.len as usize; if len < SECTOR_BSIZE { - println!("blk_req_handler: read len < SECTOR_BSIZE"); + warn!("blk_req_handler: read len < SECTOR_BSIZE"); continue; } if !req.mediated() { if trace() && (data_bg < 0x1000 || cache_ptr < 0x1000) { panic!("illegal des addr {:x}, src addr {:x}", cache_ptr, data_bg); } - memcpy_safe(cache_ptr as *mut u8, data_bg as *mut u8, len); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy(cache_ptr as *mut u8, data_bg as *mut u8, len); + } } cache_ptr += len; } @@ -471,7 +466,12 @@ pub fn generate_blk_req(req: VirtioBlkReq, vq: Virtq, dev: VirtioMmio, cache: us if trace() && (data_bg < 0x1000) { panic!("illegal des addr {:x}", cache_ptr); } - memcpy_safe(data_bg as *mut u8, name, 20); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy(data_bg as *mut u8, name, 20); + } let task = AsyncTask::new( AsyncTaskData::AsyncNoneTask(IoIdAsyncMsg { vq: vq.clone(), @@ -484,7 +484,7 @@ pub fn generate_blk_req(req: VirtioBlkReq, vq: Virtq, dev: VirtioMmio, cache: us add_async_task(task, false); } _ => { - println!("Wrong block request type {} ", req_node.req_type); + warn!("Wrong block request type {} ", req_node.req_type); continue; } } @@ -503,13 +503,16 @@ pub fn generate_blk_req(req: VirtioBlkReq, vq: Virtq, dev: VirtioMmio, cache: us req.clear_node(); } +/// Handles the notification for a mediated block request on the specified Virtqueue (`vq`) and Virtio block device (`blk`) +/// associated with the virtual machine (`vm`). This function creates an asynchronous IPI task to process the mediated +/// block request. pub fn virtio_mediated_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { // add_task_count(); let task = AsyncTask::new( AsyncTaskData::AsyncIpiTask(IpiMediatedMsg { src_id: vm.id(), - vq: vq, - blk: blk, + vq, + blk, }), vm.id(), async_ipi_req(), @@ -518,6 +521,9 @@ pub fn virtio_mediated_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> true } +/// Handles the notification for a Virtio block request on the specified Virtqueue (`vq`) and Virtio block device (`blk`) +/// associated with the virtual machine (`vm`). This function processes the available descriptors in the Virtqueue and +/// generates block requests accordingly. The function returns `true` upon successful handling. pub fn virtio_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { if vm.id() == 0 && active_vm_id() == 0 { panic!("src vm should not be 0"); @@ -527,7 +533,7 @@ pub fn virtio_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { // let begin = time_current_us(); if vq.ready() == 0 { - println!("blk virt_queue is not ready!"); + error!("blk virt_queue is not ready!"); return false; } @@ -572,7 +578,7 @@ pub fn virtio_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { if vq.desc_has_next(next_desc_idx) { if head { if vq.desc_is_writable(next_desc_idx) { - println!( + error!( "Failed to get virt blk queue desc header, idx = {}, flag = {:x}", next_desc_idx, vq.desc_flags(next_desc_idx) @@ -584,16 +590,17 @@ pub fn virtio_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { head = false; let vreq_addr = vm_ipa2pa(vm.clone(), vq.desc_addr(next_desc_idx)); if vreq_addr == 0 { - println!("virtio_blk_notify_handler: failed to get vreq"); + error!("virtio_blk_notify_handler: failed to get vreq"); return false; } + // SAFETY: 'vreq_addr' is checked let vreq = unsafe { &mut *(vreq_addr as *mut VirtioBlkReqNode) }; req_node.req_type = vreq.req_type; req_node.sector = vreq.sector; } else { /*data handler*/ if (vq.desc_flags(next_desc_idx) & 0x2) as u32 >> 1 == req_node.req_type { - println!( + error!( "Failed to get virt blk queue desc data, idx = {}, req.type = {}, desc.flags = {}", next_desc_idx, req_node.req_type, @@ -605,7 +612,7 @@ pub fn virtio_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { } let data_bg = vm_ipa2pa(vm.clone(), vq.desc_addr(next_desc_idx)); if data_bg == 0 { - println!("virtio_blk_notify_handler: failed to get iov data begin"); + error!("virtio_blk_notify_handler: failed to get iov data begin"); return false; } @@ -619,16 +626,17 @@ pub fn virtio_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { } else { /*state handler*/ if !vq.desc_is_writable(next_desc_idx) { - println!("Failed to get virt blk queue desc status, idx = {}", next_desc_idx); + error!("Failed to get virt blk queue desc status, idx = {}", next_desc_idx); blk.notify(vm); // vq.notify(dev.int_id(), vm.clone()); return false; } let vstatus_addr = vm_ipa2pa(vm.clone(), vq.desc_addr(next_desc_idx)); if vstatus_addr == 0 { - println!("virtio_blk_notify_handler: vm[{}] failed to vstatus", vm.id()); + error!("virtio_blk_notify_handler: vm[{}] failed to vstatus", vm.id()); return false; } + // SAFETY: 'vstatus_addr' is checked let vstatus = unsafe { &mut *(vstatus_addr as *mut u8) }; if req_node.req_type > 1 && req_node.req_type != VIRTIO_BLK_T_GET_ID as u32 { *vstatus = VIRTIO_BLK_S_UNSUPP as u8; @@ -657,7 +665,7 @@ pub fn virtio_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { // let time1 = time_current_us(); if vq.avail_flags() == 0 && process_count > 0 && !req.mediated() { - println!("virtio blk notify"); + trace!("virtio blk notify"); blk.notify(vm); // vq.notify(dev.int_id(), vm.clone()); } @@ -670,5 +678,5 @@ pub fn virtio_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { // let end = time_current_us(); // println!("init time {}us, while handle desc ring time {}us, finish task {}us", time0 - begin, time1 - time0, end - time1); - return true; + true } diff --git a/src/device/virtio/console.rs b/src/device/virtio/console.rs index fde79126f88dd85a9960ed549462b53ec871558e..74f0bdb74ea6b17782e099080a8738dba91fb2b1 100644 --- a/src/device/virtio/console.rs +++ b/src/device/virtio/console.rs @@ -17,10 +17,10 @@ use crate::device::{VirtioMmio, Virtq}; use crate::device::DevDesc; use crate::device::EmuDevs; use crate::device::VirtioIov; -use crate::kernel::{active_vm, ConsoleDescData, vm_if_set_mem_map_bit, vm_ipa2pa}; +use crate::kernel::{active_vm, vm_if_set_mem_map_bit, vm_ipa2pa}; use crate::kernel::vm; use crate::kernel::Vm; -use crate::lib::{round_down, trace}; +use crate::utils::round_down; pub const VIRTQUEUE_CONSOLE_MAX_SIZE: usize = 64; @@ -41,33 +41,31 @@ const VIRTIO_CONSOLE_RESIZE: usize = 5; const VIRTIO_CONSOLE_PORT_OPEN: usize = 6; const VIRTIO_CONSOLE_PORT_NAME: usize = 7; +/// A wrapper structure for `ConsoleDescInner` providing a convenient and thread-safe interface. #[derive(Clone)] pub struct ConsoleDesc { inner: Arc>, } +/// Holds data related to console configuration. +pub struct ConsoleDescData { + pub oppo_end_vmid: u16, + pub oppo_end_ipa: u64, + // vm access + pub cols: u16, + pub rows: u16, + pub max_nr_ports: u32, + pub emerg_wr: u32, +} + impl ConsoleDesc { + /// Creates a new `ConsoleDesc` with default inner values. pub fn default() -> ConsoleDesc { ConsoleDesc { inner: Arc::new(Mutex::new(ConsoleDescInner::default())), } } - pub fn back_up(&self) -> ConsoleDesc { - let current_inner = self.inner.lock(); - let inner = ConsoleDescInner { - oppo_end_vmid: current_inner.oppo_end_vmid, - oppo_end_ipa: current_inner.oppo_end_ipa, - cols: current_inner.cols, - rows: current_inner.rows, - max_nr_ports: current_inner.max_nr_ports, - emerg_wr: current_inner.emerg_wr, - }; - ConsoleDesc { - inner: Arc::new(Mutex::new(inner)), - } - } - pub fn cfg_init(&self, oppo_end_vmid: u16, oppo_end_ipa: u64) { let mut inner = self.inner.lock(); inner.oppo_end_vmid = oppo_end_vmid; @@ -76,48 +74,19 @@ impl ConsoleDesc { inner.rows = 25; } + /// Returns the starting address of the console configuration. pub fn start_addr(&self) -> usize { let inner = self.inner.lock(); &inner.cols as *const _ as usize } - pub fn offset_data(&self, offset: usize) -> u32 { - let start_addr = self.start_addr(); - if trace() && start_addr + offset < 0x1000 { - println!("value addr is {}", start_addr + offset); - } - let value = unsafe { *((start_addr + offset) as *const u32) }; - return value; - } - pub fn target_console(&self) -> (u16, u64) { let inner = self.inner.lock(); (inner.oppo_end_vmid, inner.oppo_end_ipa) } - - // use for migration restore - pub fn restore_console_data(&self, desc_data: &ConsoleDescData) { - let mut inner = self.inner.lock(); - // inner.oppo_end_vmid = desc_data.oppo_end_vmid; - // inner.oppo_end_ipa = desc_data.oppo_end_ipa; - inner.cols = desc_data.cols; - inner.rows = desc_data.rows; - inner.max_nr_ports = desc_data.max_nr_ports; - inner.emerg_wr = desc_data.emerg_wr; - } - - // use for migration save - pub fn save_console_data(&self, desc_data: &mut ConsoleDescData) { - let inner = self.inner.lock(); - desc_data.oppo_end_vmid = inner.oppo_end_vmid; - desc_data.oppo_end_ipa = inner.oppo_end_ipa; - desc_data.cols = inner.cols; - desc_data.rows = inner.rows; - desc_data.max_nr_ports = inner.max_nr_ports; - desc_data.emerg_wr = inner.emerg_wr; - } } +/// Represents the inner data structure for `ConsoleDesc`. #[repr(C)] #[derive(Clone, Copy)] pub struct ConsoleDescInner { @@ -131,6 +100,7 @@ pub struct ConsoleDescInner { } impl ConsoleDescInner { + /// Creates a new `ConsoleDescInner` with default values. pub fn default() -> ConsoleDescInner { ConsoleDescInner { oppo_end_vmid: 0, @@ -143,10 +113,14 @@ impl ConsoleDescInner { } } +/// Returns features supported by the console. pub fn console_features() -> usize { VIRTIO_F_VERSION_1 | VIRTIO_CONSOLE_F_SIZE } +/// Handles notification for a Virtio console request on the specified Virtqueue (`vq`), Virtio console device (`console`), and virtual machine (`vm`). +/// This function processes the available descriptors in the Virtqueue and performs necessary operations. +/// Returns `true` upon successful handling. pub fn virtio_console_notify_handler(vq: Virtq, console: VirtioMmio, vm: Vm) -> bool { if vq.vq_indx() % 4 != 1 { // println!("console rx queue notified!"); @@ -154,7 +128,7 @@ pub fn virtio_console_notify_handler(vq: Virtq, console: VirtioMmio, vm: Vm) -> } if vq.ready() == 0 { - println!("virtio_console_notify_handler: console virt_queue is not ready!"); + error!("virtio_console_notify_handler: console virt_queue is not ready!"); return false; } @@ -164,7 +138,7 @@ pub fn virtio_console_notify_handler(vq: Virtq, console: VirtioMmio, vm: Vm) -> let (trgt_vmid, trgt_console_ipa) = match dev.desc() { DevDesc::ConsoleDesc(desc) => desc.target_console(), _ => { - println!("virtio_console_notify_handler: console desc should not be None"); + error!("virtio_console_notify_handler: console desc should not be None"); return false; } }; @@ -180,9 +154,10 @@ pub fn virtio_console_notify_handler(vq: Virtq, console: VirtioMmio, vm: Vm) -> loop { let addr = vm_ipa2pa(active_vm().unwrap(), vq.desc_addr(idx)); if addr == 0 { - println!("virtio_console_notify_handler: failed to desc addr"); + error!("virtio_console_notify_handler: failed to desc addr"); return false; } + // println!("virtio_consle:addr:{:#x}", addr); tx_iov.push_data(addr, vq.desc_len(idx) as usize); len += vq.desc_len(idx) as usize; @@ -191,9 +166,18 @@ pub fn virtio_console_notify_handler(vq: Virtq, console: VirtioMmio, vm: Vm) -> } idx = vq.desc_next(idx) as usize; } + // unsafe { + // let slice = core::slice::from_raw_parts( + // vm_ipa2pa(active_vm().unwrap(), vq.desc_addr(idx)) as *const u8, + // len as usize, + // ); + // use alloc::string::String; + // let s = String::from_utf8_lossy(slice); + // println!("send data:{}", s); + // } if !virtio_console_recv(trgt_vmid, trgt_console_ipa, tx_iov.clone(), len) { - println!("virtio_console_notify_handler: failed send"); + error!("virtio_console_notify_handler: failed send"); // return false; } if vm.id() != 0 { @@ -214,7 +198,7 @@ pub fn virtio_console_notify_handler(vq: Virtq, console: VirtioMmio, vm: Vm) -> } if !vq.avail_is_avail() { - println!("invalid descriptor table index"); + error!("invalid descriptor table index"); return false; } @@ -224,10 +208,12 @@ pub fn virtio_console_notify_handler(vq: Virtq, console: VirtioMmio, vm: Vm) -> true } +/// Receives Virtio console data for the target VM with specified VM ID (`trgt_vmid`), console IPA (`trgt_console_ipa`), I/O vector (`tx_iov`), and length (`len`). +/// Returns `true` upon successful reception. fn virtio_console_recv(trgt_vmid: u16, trgt_console_ipa: u64, tx_iov: VirtioIov, len: usize) -> bool { let trgt_vm = match vm(trgt_vmid as usize) { None => { - println!("target vm [{}] is not ready or not exist", trgt_vmid); + warn!("target vm [{}] is not ready or not exist", trgt_vmid); return true; } Some(vm) => vm, @@ -236,7 +222,7 @@ fn virtio_console_recv(trgt_vmid: u16, trgt_console_ipa: u64, tx_iov: VirtioIov, let console = match trgt_vm.emu_console_dev(trgt_console_ipa as usize) { EmuDevs::VirtioConsole(x) => x, _ => { - println!( + error!( "virtio_console_recv: trgt_vm[{}] failed to get virtio console dev", trgt_vmid ); @@ -245,7 +231,7 @@ fn virtio_console_recv(trgt_vmid: u16, trgt_console_ipa: u64, tx_iov: VirtioIov, }; if !console.dev().activated() { - println!( + warn!( "virtio_console_recv: trgt_vm[{}] virtio console dev is not ready", trgt_vmid ); @@ -255,7 +241,7 @@ fn virtio_console_recv(trgt_vmid: u16, trgt_console_ipa: u64, tx_iov: VirtioIov, let rx_vq = match console.vq(0) { Ok(x) => x, Err(_) => { - println!( + error!( "virtio_console_recv: trgt_vm[{}] failed to get virtio console rx virt queue", trgt_vmid ); @@ -265,7 +251,7 @@ fn virtio_console_recv(trgt_vmid: u16, trgt_console_ipa: u64, tx_iov: VirtioIov, let desc_header_idx_opt = rx_vq.pop_avail_desc_idx(rx_vq.avail_idx()); if !rx_vq.avail_is_avail() { - println!("virtio_console_recv: receive invalid avail desc idx"); + error!("virtio_console_recv: receive invalid avail desc idx"); return false; } else if desc_header_idx_opt.is_none() { // println!("virtio_console_recv: desc_header_idx_opt.is_none()"); @@ -279,13 +265,14 @@ fn virtio_console_recv(trgt_vmid: u16, trgt_console_ipa: u64, tx_iov: VirtioIov, loop { let dst = vm_ipa2pa(trgt_vm.clone(), rx_vq.desc_addr(desc_idx)); if dst == 0 { - println!( + error!( "virtio_console_recv: failed to get dst, desc_idx {}, avail idx {}", desc_idx, rx_vq.avail_idx() ); return false; } + // println!("virtio_consle recv:addr:{:#x}", dst); let desc_len = rx_vq.desc_len(desc_idx) as usize; // dirty pages if trgt_vmid != 0 { @@ -308,12 +295,12 @@ fn virtio_console_recv(trgt_vmid: u16, trgt_console_ipa: u64, tx_iov: VirtioIov, if rx_len < len { rx_vq.put_back_avail_desc_idx(); - println!("virtio_console_recv: rx_len smaller than tx_len"); + error!("virtio_console_recv: rx_len smaller than tx_len"); return false; } if tx_iov.write_through_iov(rx_iov.clone(), len) > 0 { - println!( + error!( "virtio_console_recv: write through iov failed, rx_iov_num {} tx_iov_num {} rx_len {} tx_len {}", rx_iov.num(), tx_iov.num(), @@ -329,7 +316,7 @@ fn virtio_console_recv(trgt_vmid: u16, trgt_console_ipa: u64, tx_iov: VirtioIov, vm_if_set_mem_map_bit(trgt_vm.clone(), used_addr + PAGE_SIZE); } if !rx_vq.update_used_ring(len as u32, desc_idx_header as u32) { - println!( + error!( "virtio_console_recv: update used ring failed len {} rx_vq num {}", len, rx_vq.num() diff --git a/src/device/virtio/dev.rs b/src/device/virtio/dev.rs index 501f72fc2028d5872ccebe20b0ab3457ea0f353a..414ac15df9366a7abc60eee68b774c827fc9b2de 100644 --- a/src/device/virtio/dev.rs +++ b/src/device/virtio/dev.rs @@ -14,15 +14,15 @@ use spin::Mutex; use crate::config::VmEmulatedDeviceConfig; // use crate::device::add_mediated_dev; -use crate::device::{net_features, NetDesc}; -use crate::device::{console_features, ConsoleDesc}; +use crate::device::{net_features, NetDesc, NetDescData}; +use crate::device::{console_features, ConsoleDesc, ConsoleDescData}; use crate::device::{BlkDesc, BLOCKIF_IOV_MAX, VirtioBlkReq}; use crate::device::{VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_F_VERSION_1}; use crate::device::{BlkStat, NicStat}; -use crate::device::DevReq::BlkReq; -use crate::kernel::{ConsoleDescData, DevDescData, mem_pages_alloc, NetDescData, VirtDevData}; +use crate::kernel::mem_pages_alloc; use crate::mm::PageFrame; +/// Represents the type of a Virtio device. #[derive(Copy, Clone, Debug)] pub enum VirtioDeviceType { None = 0, @@ -31,6 +31,35 @@ pub enum VirtioDeviceType { Console = 3, } +/// Placeholder struct for block device configuration data. +pub struct BlkDescData {} + +/// Represents different types of device descriptions. +pub enum DevDescData { + /// Reserved block device description. + BlkDesc(BlkDescData), + /// Network device description. + NetDesc(NetDescData), + /// Console device description. + ConsoleDesc(ConsoleDescData), + /// No device description. + None, +} + +/// Represents various data associated with a Virtio device. +pub struct VirtDevData { + pub activated: bool, + pub dev_type: VirtioDeviceType, + pub features: usize, + pub generation: usize, + pub int_id: usize, + pub desc: DevDescData, + // req: reserve; we used nfs, no need to mig blk req data + // cache: reserve + // stat: reserve +} + +/// Represents the status of a device. #[derive(Clone)] pub enum DevStat { BlkStat(BlkStat), @@ -38,22 +67,6 @@ pub enum DevStat { None, } -impl DevStat { - pub fn copy_from(&mut self, stat: DevStat) { - match stat { - DevStat::BlkStat(src_stat) => { - *self = DevStat::BlkStat(src_stat.back_up()); - } - DevStat::NicStat(src_stat) => { - *self = DevStat::NicStat(src_stat.back_up()); - } - DevStat::None => { - *self = DevStat::None; - } - } - } -} - #[derive(Clone)] pub enum DevDesc { BlkDesc(BlkDesc), @@ -62,204 +75,93 @@ pub enum DevDesc { None, } -impl DevDesc { - pub fn copy_from(&mut self, desc: DevDesc) { - match desc { - DevDesc::BlkDesc(src_desc) => { - *self = DevDesc::BlkDesc(src_desc); - } - DevDesc::NetDesc(src_desc) => { - *self = DevDesc::NetDesc(src_desc.back_up()); - } - DevDesc::ConsoleDesc(src_desc) => { - *self = DevDesc::ConsoleDesc(src_desc.back_up()); - } - DevDesc::None => *self = DevDesc::None, - } - } -} - #[derive(Clone)] pub enum DevReq { BlkReq(VirtioBlkReq), None, } -impl DevReq { - pub fn copy_from(&mut self, src_req: DevReq) { - match src_req { - DevReq::BlkReq(req) => { - *self = BlkReq(req.back_up()); - } - DevReq::None => { - *self = DevReq::None; - } - } - } -} - #[derive(Clone)] pub struct VirtDev { inner: Arc>, } impl VirtDev { + /// Creates a new `VirtDev` with default inner values. pub fn default() -> VirtDev { VirtDev { inner: Arc::new(Mutex::new(VirtDevInner::default())), } } + /// Initializes the Virtio device with the specified parameters. pub fn init(&self, dev_type: VirtioDeviceType, config: &VmEmulatedDeviceConfig, mediated: bool) { let mut inner = self.inner.lock(); inner.init(dev_type, config, mediated); } + /// Retrieves the features supported by the Virtio device. pub fn features(&self) -> usize { let inner = self.inner.lock(); inner.features } + /// Retrieves the generation of the Virtio device. pub fn generation(&self) -> usize { let inner = self.inner.lock(); inner.generation } + /// Retrieves the device description associated with the Virtio device. pub fn desc(&self) -> DevDesc { let inner = self.inner.lock(); inner.desc.clone() } + /// Retrieves the device request associated with the Virtio device. pub fn req(&self) -> DevReq { let inner = self.inner.lock(); inner.req.clone() } + /// Retrieves the interrupt ID associated with the Virtio device. pub fn int_id(&self) -> usize { let inner = self.inner.lock(); inner.int_id } + /// Retrieves the cache associated with the Virtio device. pub fn cache(&self) -> usize { let inner = self.inner.lock(); return inner.cache.as_ref().unwrap().pa(); } + /// Retrieves the status of the Virtio device. pub fn stat(&self) -> DevStat { let inner = self.inner.lock(); inner.stat.clone() } + /// Checks if the Virtio device is activated. pub fn activated(&self) -> bool { let inner = self.inner.lock(); inner.activated } + /// Sets the activation status of the Virtio device. pub fn set_activated(&self, activated: bool) { let mut inner = self.inner.lock(); inner.activated = activated; } + /// Checks if the Virtio device is mediated. pub fn mediated(&self) -> bool { let inner = self.inner.lock(); inner.mediated() } - - pub fn is_net(&self) -> bool { - let inner = self.inner.lock(); - match &inner.desc { - DevDesc::NetDesc(_) => true, - _ => false, - } - } - - // use for migration save - pub fn restore_virt_dev_data(&self, dev_data: &VirtDevData) { - let mut inner = self.inner.lock(); - // println!( - // "activated {}, type {:#?}, features 0x{:x}, generation {}, int id {}", - // dev_data.activated, dev_data.dev_type, dev_data.features, dev_data.generation, dev_data.int_id - // ); - inner.activated = dev_data.activated; - inner.dev_type = dev_data.dev_type; - inner.features = dev_data.features; - inner.generation = dev_data.generation; - inner.int_id = dev_data.int_id; - match &inner.desc { - DevDesc::BlkDesc(_) => { - todo!("restore_virt_dev_data: Migrate vm use nfs"); - } - DevDesc::NetDesc(net_desc) => { - if let DevDescData::NetDesc(desc_data) = &dev_data.desc { - net_desc.restore_net_data(desc_data); - } - } - DevDesc::ConsoleDesc(console_desvc) => { - if let DevDescData::ConsoleDesc(desc_data) = &dev_data.desc { - console_desvc.restore_console_data(desc_data); - } - } - DevDesc::None => {} - } - } - - // use for migration save - pub fn save_virt_dev_data(&self, dev_data: &mut VirtDevData) { - let mut inner = self.inner.lock(); - dev_data.activated = inner.activated; - dev_data.dev_type = inner.dev_type; - dev_data.features = inner.features; - dev_data.generation = inner.generation; - dev_data.int_id = inner.int_id; - match &inner.desc { - DevDesc::BlkDesc(_) => { - todo!("save_virt_dev_data: Migrate vm use nfs"); - } - DevDesc::NetDesc(net_desc) => { - dev_data.desc = DevDescData::NetDesc(NetDescData { mac: [0; 6], status: 0 }); - if let DevDescData::NetDesc(desc_data) = &mut dev_data.desc { - net_desc.save_net_data(desc_data); - } - } - DevDesc::ConsoleDesc(console_desvc) => { - dev_data.desc = DevDescData::ConsoleDesc(ConsoleDescData { - oppo_end_vmid: 0, - oppo_end_ipa: 0, - cols: 0, - rows: 0, - max_nr_ports: 0, - emerg_wr: 0, - }); - if let DevDescData::ConsoleDesc(desc_data) = &mut dev_data.desc { - console_desvc.save_console_data(desc_data); - } - } - DevDesc::None => {} - } - // set activated to false - inner.activated = false; - } - - // use for live update - pub fn save_virt_dev(&self, src_dev: VirtDev) { - let mut inner = self.inner.lock(); - let src_dev_inner = src_dev.inner.lock(); - inner.activated = src_dev_inner.activated; - inner.dev_type = src_dev_inner.dev_type; - inner.features = src_dev_inner.features; - inner.generation = src_dev_inner.generation; - inner.int_id = src_dev_inner.int_id; - inner.desc.copy_from(src_dev_inner.desc.clone()); - inner.req.copy_from(src_dev_inner.req.clone()); - // inner.cache is set by fn dev_init, no need to copy here - inner.cache = match &src_dev_inner.cache { - None => None, - Some(page) => Some(PageFrame::new(page.pa, page.page_num)), - }; - inner.stat.copy_from(src_dev_inner.stat.clone()); - } } +/// Represents the inner data structure for `VirtDev`. pub struct VirtDevInner { activated: bool, dev_type: VirtioDeviceType, @@ -273,6 +175,7 @@ pub struct VirtDevInner { } impl VirtDevInner { + /// Creates a new `VirtDevInner` with default values. pub fn default() -> VirtDevInner { VirtDevInner { activated: false, @@ -287,6 +190,7 @@ impl VirtDevInner { } } + /// Checks if the Virtio device is mediated. pub fn mediated(&self) -> bool { match &self.req { DevReq::BlkReq(req) => req.mediated(), @@ -294,6 +198,7 @@ impl VirtDevInner { } } + /// Initializes the Virtio device with the specified parameters. // virtio_dev_init pub fn init(&mut self, dev_type: VirtioDeviceType, config: &VmEmulatedDeviceConfig, mediated: bool) { self.dev_type = dev_type; @@ -325,7 +230,7 @@ impl VirtDevInner { // } } Err(_) => { - println!("VirtDevInner::init(): mem_pages_alloc failed"); + error!("VirtDevInner::init(): mem_pages_alloc failed"); } } @@ -344,7 +249,7 @@ impl VirtDevInner { self.cache = Some(page_frame); } Err(_) => { - println!("VirtDevInner::init(): mem_pages_alloc failed"); + error!("VirtDevInner::init(): mem_pages_alloc failed"); } } @@ -362,7 +267,7 @@ impl VirtDevInner { self.cache = Some(page_frame); } Err(_) => { - println!("VirtDevInner::init(): mem_pages_alloc failed"); + error!("VirtDevInner::init(): mem_pages_alloc failed"); } } } diff --git a/src/device/virtio/iov.rs b/src/device/virtio/iov.rs index ff94115a75b9877e0223283f6bddbf2208da8ae0..5daf7a905e92d27ae8d4a77118c935f659796f51 100644 --- a/src/device/virtio/iov.rs +++ b/src/device/virtio/iov.rs @@ -14,35 +14,41 @@ use core::slice::from_raw_parts; use spin::Mutex; -use crate::lib::{memcpy_safe, trace}; +use crate::utils::{memcpy, trace}; +/// Represents a Virtio I/O vector. #[derive(Clone)] pub struct VirtioIov { inner: Arc>, } impl VirtioIov { + /// Creates a new `VirtioIov` with default inner values. pub fn default() -> VirtioIov { VirtioIov { inner: Arc::new(Mutex::new(VirtioIovInner::default())), } } + /// Clears the I/O vector by removing all data. pub fn clear(&self) { let mut inner = self.inner.lock(); inner.vector.clear(); } + /// Adds a data segment to the I/O vector. pub fn push_data(&self, buf: usize, len: usize) { let mut inner = self.inner.lock(); inner.vector.push(VirtioIovData { buf, len }); } + /// Retrieves the buffer address at the specified index. pub fn get_buf(&self, idx: usize) -> usize { let inner = self.inner.lock(); inner.vector[idx].buf } + /// Copies data from the I/O vector to the specified buffer. pub fn to_buf(&self, addr: usize, len: usize) { let mut size = len; let inner = self.inner.lock(); @@ -50,15 +56,26 @@ impl VirtioIov { let offset = len - size; let dst = addr + offset; if iov_data.len >= size { - memcpy_safe(dst as *const u8, iov_data.buf as *const u8, size); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy(dst as *const u8, iov_data.buf as *const u8, size); + } break; } else { - memcpy_safe(dst as *const u8, iov_data.buf as *const u8, iov_data.len); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy(dst as *const u8, iov_data.buf as *const u8, iov_data.len); + } size -= iov_data.len; } } } + /// Copies data from the specified buffer to the I/O vector. pub fn from_buf(&self, addr: usize, len: usize) { let mut size = len; let inner = self.inner.lock(); @@ -66,25 +83,38 @@ impl VirtioIov { let offset = len - size; let src = addr + offset; if iov_data.len >= size { - memcpy_safe(iov_data.buf as *const u8, src as *const u8, size); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy(iov_data.buf as *const u8, src as *const u8, size); + } break; } else { - memcpy_safe(iov_data.buf as *const u8, src as *const u8, iov_data.len); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy(iov_data.buf as *const u8, src as *const u8, iov_data.len); + } size -= iov_data.len; } } } + /// Retrieves the number of data segments in the I/O vector. pub fn num(&self) -> usize { let inner = self.inner.lock(); inner.vector.len() } + /// Retrieves the length of the data segment at the specified index. pub fn get_len(&self, idx: usize) -> usize { let inner = self.inner.lock(); inner.vector[idx].len } + /// Retrieves a pointer to the data in the I/O vector. pub fn get_ptr(&self, size: usize) -> &'static [u8] { let inner = self.inner.lock(); // let mut iov_idx = 0; @@ -95,18 +125,21 @@ impl VirtioIov { if trace() && iov_data.buf + idx < 0x1000 { panic!("illegal addr {:x}", iov_data.buf + idx); } + // SAFETY: + // The 'iov_data.buf' is a valid address, and iov_data.len is the length of the buffer. return unsafe { from_raw_parts((iov_data.buf + idx) as *const u8, 14) }; } else { idx -= iov_data.len; } } - println!("iov get_ptr failed"); - println!("get_ptr iov {:#?}", inner.vector); - println!("size {}, idx {}", size, idx); - return &[0]; + debug!("iov get_ptr failed"); + debug!("get_ptr iov {:#?}", inner.vector); + debug!("size {}, idx {}", size, idx); + &[0] } + /// Writes data from the I/O vector to another I/O vector. pub fn write_through_iov(&self, dst: VirtioIov, remain: usize) -> usize { let inner = self.inner.lock(); @@ -117,10 +150,6 @@ impl VirtioIov { let mut dst_vlen_remain = dst.get_len(0); let mut src_vlen_remain = inner.vector[0].len; let mut remain = remain; - // println!( - // "dst_vlen_remain {}, src_vlen_remain {}, remain {}", - // dst_vlen_remain, src_vlen_remain, remain - // ); while remain > 0 { if dst_iov_idx == dst.num() || src_iov_idx == inner.vector.len() { @@ -133,7 +162,12 @@ impl VirtioIov { if trace() && (dst_ptr < 0x1000 || src_ptr < 0x1000) { panic!("illegal des addr {:x}, src addr {:x}", dst_ptr, src_ptr); } - memcpy_safe(dst_ptr as *const u8, src_ptr as *const u8, written); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy(dst_ptr as *const u8, src_ptr as *const u8, written); + } src_iov_idx += 1; if src_iov_idx < inner.vector.len() { src_ptr = inner.vector[src_iov_idx].buf; @@ -141,17 +175,17 @@ impl VirtioIov { dst_ptr += written; dst_vlen_remain -= written; } - // if dst_vlen_remain == 0 { - // dst_iov_idx += 1; - // dst_ptr = dst.get_buf(dst_iov_idx); - // dst_vlen_remain = dst.get_len(dst_iov_idx); - // } } else { written = dst_vlen_remain; if trace() && (dst_ptr < 0x1000 || src_ptr < 0x1000) { panic!("illegal des addr {:x}, src addr {:x}", dst_ptr, src_ptr); } - memcpy_safe(dst_ptr as *const u8, src_ptr as *const u8, written); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy(dst_ptr as *const u8, src_ptr as *const u8, written); + } dst_iov_idx += 1; if dst_iov_idx < dst.num() { dst_ptr = dst.get_buf(dst_iov_idx); @@ -167,10 +201,6 @@ impl VirtioIov { } } } - // if remain < written { - // println!("remain {} less than writter {}", remain, written); - // return 1; - // } remain -= written; } @@ -178,18 +208,21 @@ impl VirtioIov { } } +/// Represents a data segment in the Virtio I/O vector. #[derive(Debug)] struct VirtioIovData { buf: usize, len: usize, } +/// Represents the inner data structure for `VirtioIov`. #[derive(Debug)] struct VirtioIovInner { vector: Vec, } impl VirtioIovInner { + /// Creates a new `VirtioIovInner` with an empty vector. pub fn default() -> VirtioIovInner { VirtioIovInner { vector: Vec::new() } } diff --git a/src/device/virtio/mediated.rs b/src/device/virtio/mediated.rs index 6d6ab75315246bbf040569aa745619fe3e9efdde..c8d71b8ccb8bd94eea46f1ea228035132b8c93ad 100644 --- a/src/device/virtio/mediated.rs +++ b/src/device/virtio/mediated.rs @@ -17,11 +17,12 @@ use crate::kernel::{ active_vm, async_task_exe, AsyncTaskState, finish_async_task, hvc_send_msg_to_vm, HvcDefaultMsg, HvcGuestMsg, IpiInnerMsg, set_front_io_task_state, vm, vm_ipa2pa, VM_LIST, }; -use crate::kernel::{ipi_register, IpiMessage, IpiType}; -use crate::lib::trace; +use crate::kernel::IpiMessage; +/// Mutex for the list of mediated block devices. pub static MEDIATED_BLK_LIST: Mutex> = Mutex::new(Vec::new()); +/// Adds a mediated block to the list and assigns it to a VM if needed. pub fn mediated_blk_list_push(mut blk: MediatedBlk) { let mut list = MEDIATED_BLK_LIST.lock(); for vm in VM_LIST.lock().iter() { @@ -42,6 +43,8 @@ pub fn mediated_blk_list_push(mut blk: MediatedBlk) { list.push(blk); } +/// Requests a mediated block device from the list. +/// Returns the index of the available block device. // TODO: not concern abort the num of sectors pub fn mediated_blk_request() -> Result { let mut list = MEDIATED_BLK_LIST.lock(); @@ -54,77 +57,101 @@ pub fn mediated_blk_request() -> Result { Err(()) } +/// Frees a mediated block device back to the list. pub fn mediated_blk_free(idx: usize) { let mut list = MEDIATED_BLK_LIST.lock(); list[idx].avail = true; } +/// Retrieves a mediated block device from the list by index. pub fn mediated_blk_list_get(idx: usize) -> MediatedBlk { let list = MEDIATED_BLK_LIST.lock(); list[idx].clone() } +/// Retrieves a mediated block device from the list by physical address. pub fn mediated_blk_list_get_from_pa(pa: usize) -> Option { let list = MEDIATED_BLK_LIST.lock(); for blk in &*list { - if blk.base_addr == pa { + if blk.content as usize == pa { return Some(blk.clone()); } } None } +/// Represents a mediated block device. #[derive(Clone)] pub struct MediatedBlk { - pub base_addr: usize, - pub avail: bool, // mediated blk will not be removed after append + content: *mut MediatedBlkContent, + avail: bool, // mediated blk will not be removed after append } +// SAFETY: MediatedBlk is only used in VM0 and VM0 is only one core when muti-vm is supported +unsafe impl Send for MediatedBlk {} + impl MediatedBlk { - pub fn content(&self) -> &mut MediatedBlkContent { - if trace() && self.base_addr < 0x1000 { - panic!("illeagal addr {:x}", self.base_addr); + /// # Safety: + /// Addr must be a valid MMIO address of virtio-blk config + pub unsafe fn from_addr(addr: usize) -> Self { + Self { + content: unsafe { &mut *(addr as *mut MediatedBlkContent) }, + avail: true, } - unsafe { &mut *(self.base_addr as *mut MediatedBlkContent) } } + fn content(&self) -> &'static mut MediatedBlkContent { + // SAFETY: 'content' is a valid pointer after `from_addr` + unsafe { &mut *self.content } + } + + /// Retrieves the maximum number of DMA blocks supported by the mediated block. pub fn dma_block_max(&self) -> usize { self.content().cfg.dma_block_max } + /// Retrieves the number of requests in the mediated block. pub fn nreq(&self) -> usize { self.content().nreq } + /// Retrieves the IPA (Intermediate Physical Address) of the cache associated with the mediated block. pub fn cache_ipa(&self) -> usize { self.content().cfg.cache_ipa } + /// Retrieves the physical address of the cache associated with the mediated block. pub fn cache_pa(&self) -> usize { self.content().cfg.cache_pa } + /// Sets the number of requests in the mediated block. pub fn set_nreq(&self, nreq: usize) { self.content().nreq = nreq; } + /// Sets the type of request for the mediated block. pub fn set_type(&self, req_type: usize) { self.content().req.req_type = req_type as u32; } + /// Sets the sector for the mediated block request. pub fn set_sector(&self, sector: usize) { self.content().req.sector = sector; } + /// Sets the count for the mediated block request. pub fn set_count(&self, count: usize) { self.content().req.count = count; } + /// Sets the physical address of the cache associated with the mediated block. pub fn set_cache_pa(&self, cache_pa: usize) { self.content().cfg.cache_pa = cache_pa; } } +/// Represents the content of a mediated block. #[repr(C)] pub struct MediatedBlkContent { nreq: usize, @@ -132,6 +159,7 @@ pub struct MediatedBlkContent { req: MediatedBlkReq, } +/// Represents the configuration of a mediated block. #[repr(C)] pub struct MediatedBlkCfg { name: [u8; 32], @@ -147,6 +175,7 @@ pub struct MediatedBlkCfg { cache_pa: usize, } +/// Represents the request of a mediated block. #[repr(C)] pub struct MediatedBlkReq { req_type: u32, @@ -154,20 +183,14 @@ pub struct MediatedBlkReq { count: usize, } -pub fn mediated_dev_init() { - if !ipi_register(IpiType::IpiTMediatedDev, mediated_ipi_handler) { - panic!("mediated_dev_init: failed to register ipi IpiTMediatedDev"); - } -} - +/// Appends a mediated block device to VM0 and the mediated block list. // only run in vm0 pub fn mediated_dev_append(_class_id: usize, mmio_ipa: usize) -> Result { let vm = active_vm().unwrap(); let blk_pa = vm_ipa2pa(vm.clone(), mmio_ipa); - let mediated_blk = MediatedBlk { - base_addr: blk_pa, - avail: true, - }; + // TODO: check weather the blk_pa is valid + // SAFETY:'blk_pa' is valid MMIO address of virtio-blk config + let mediated_blk = unsafe { MediatedBlk::from_addr(blk_pa) }; mediated_blk.set_nreq(0); let cache_pa = vm_ipa2pa(vm, mediated_blk.cache_ipa()); @@ -183,6 +206,7 @@ pub fn mediated_dev_append(_class_id: usize, mmio_ipa: usize) -> Result Result { let dev_pa_reg = vm_ipa2pa(active_vm().unwrap(), dev_ipa_reg); @@ -191,48 +215,37 @@ pub fn mediated_blk_notify_handler(dev_ipa_reg: usize) -> Result { let mediated_blk = match mediated_blk_list_get_from_pa(dev_pa_reg) { Some(blk) => blk, None => { - println!("illegal mediated blk pa {:x} ipa {:x}", dev_pa_reg, dev_ipa_reg); + error!("illegal mediated blk pa {:x} ipa {:x}", dev_pa_reg, dev_ipa_reg); return Err(()); } }; - if mediated_blk.avail == false { + if !mediated_blk.avail { // finish current IO task set_front_io_task_state(AsyncTaskState::Finish); } else { - println!("Mediated blk not belong to any VM"); + warn!("Mediated blk not belong to any VM"); } // invoke the excuter to handle finished IO task async_task_exe(); Ok(0) } -fn check_sum(addr: usize, len: usize) -> usize { - let slice = unsafe { core::slice::from_raw_parts(addr as *const usize, len / 8) }; - let mut sum = 0; - for num in slice { - sum ^= num; - } - sum -} - // call by normal VMs ipi request (generated by mediated virtio blk) pub fn mediated_ipi_handler(msg: &IpiMessage) { // println!("core {} mediated_ipi_handler", current_cpu().id); - match &msg.ipi_message { - IpiInnerMsg::MediatedMsg(mediated_msg) => { - let src_id = mediated_msg.src_id; - let vm = vm(src_id).unwrap(); - // generate IO request in `virtio_blk_notify_handler` - virtio_blk_notify_handler(mediated_msg.vq.clone(), mediated_msg.blk.clone(), vm); - // mark the ipi task as finish (pop it from the ipi queue) - finish_async_task(true); - // invoke the executor to do IO request - async_task_exe(); - } - _ => {} + if let IpiInnerMsg::MediatedMsg(mediated_msg) = &msg.ipi_message { + let src_id = mediated_msg.src_id; + let vm = vm(src_id).unwrap(); + // generate IO request in `virtio_blk_notify_handler` + virtio_blk_notify_handler(mediated_msg.vq.clone(), mediated_msg.blk.clone(), vm); + // mark the ipi task as finish (pop it from the ipi queue) + finish_async_task(true); + // invoke the executor to do IO request + async_task_exe(); } } +/// Initiates a read operation on a mediated block device. pub fn mediated_blk_read(blk_idx: usize, sector: usize, count: usize) { let mediated_blk = mediated_blk_list_get(blk_idx); let nreq = mediated_blk.nreq(); @@ -247,10 +260,11 @@ pub fn mediated_blk_read(blk_idx: usize, sector: usize, count: usize) { }; if !hvc_send_msg_to_vm(0, &HvcGuestMsg::Default(med_msg)) { - println!("mediated_blk_read: failed to notify VM 0"); + error!("mediated_blk_read: failed to notify VM 0"); } } +/// Initiates a write operation on a mediated block device. pub fn mediated_blk_write(blk_idx: usize, sector: usize, count: usize) { let mediated_blk = mediated_blk_list_get(blk_idx); let nreq = mediated_blk.nreq(); @@ -266,6 +280,6 @@ pub fn mediated_blk_write(blk_idx: usize, sector: usize, count: usize) { // println!("mediated_blk_write send msg to vm0"); if !hvc_send_msg_to_vm(0, &HvcGuestMsg::Default(med_msg)) { - println!("mediated_blk_write: failed to notify VM 0"); + error!("mediated_blk_write: failed to notify VM 0"); } } diff --git a/src/device/virtio/mmio.rs b/src/device/virtio/mmio.rs index 6349f9c1372876072e6cf946e59b1748abe1df11..25ac2544a4d697d56abb9ba7692fe17d735e6d14 100644 --- a/src/device/virtio/mmio.rs +++ b/src/device/virtio/mmio.rs @@ -18,11 +18,11 @@ use crate::device::{ virtio_net_handle_ctrl, virtio_net_notify_handler, }; use crate::device::{EmuDevs, VirtioDeviceType}; -use crate::device::{VirtioQueue, Virtq}; +use crate::device::{VirtioQueue, Virtq, VirtqData}; use crate::device::{VIRTQUEUE_BLK_MAX_SIZE, VIRTQUEUE_CONSOLE_MAX_SIZE, VIRTQUEUE_NET_MAX_SIZE}; -use crate::device::VirtDev; +use crate::device::{VirtDev, VirtDevData}; use crate::device::VIRTQ_READY; -use crate::kernel::{current_cpu, ipi_send_msg, IpiInnerMsg, IpiIntInjectMsg, IpiType, VirtioMmioData, vm_ipa2pa, VmPa}; +use crate::kernel::{current_cpu, ipi_send_msg, IpiInnerMsg, IpiIntInjectMsg, IpiType, vm_ipa2pa}; use crate::kernel::{active_vm, active_vm_id}; use crate::kernel::Vm; @@ -56,6 +56,7 @@ pub const VIRTIO_MMIO_REGS_END: usize = 0x200; pub const VIRTIO_MMIO_INT_VRING: u32 = 1 << 0; pub const VIRTIO_MMIO_INT_CONFIG: u32 = 1 << 1; +/// Represents the registers of a Virtio MMIO device. #[repr(C)] #[derive(Copy, Clone)] pub struct VirtMmioRegs { @@ -74,7 +75,19 @@ pub struct VirtMmioRegs { dev_stat: u32, } +/// Represents the data structure for Virtio MMIO device. +pub struct VirtioMmioData { + pub id: usize, + pub driver_features: usize, + pub driver_status: usize, + pub regs: VirtMmioRegs, + pub dev: VirtDevData, + pub oppo_dev: VirtDevData, + pub vq: [VirtqData; 4], // TODO: 4 is hard code for vq max len +} + impl VirtMmioRegs { + /// Creates a default instance of VirtMmioRegs. pub fn default() -> VirtMmioRegs { VirtMmioRegs { magic: 0, @@ -93,6 +106,7 @@ impl VirtMmioRegs { } } + /// Initializes the VirtMmioRegs with specified VirtioDeviceType. pub fn init(&mut self, id: VirtioDeviceType) { self.magic = 0x74726976; self.version = 0x2; @@ -102,30 +116,16 @@ impl VirtMmioRegs { self.drv_feature = 0; self.q_sel = 0; } - - pub fn save_regs(&mut self, src: &VirtMmioRegs) { - self.magic = src.magic; - self.version = src.version; - self.device_id = src.device_id; - self.vendor_id = src.vendor_id; - self.dev_feature = src.dev_feature; - self.dev_feature_sel = src.dev_feature_sel; - self.drv_feature = src.drv_feature; - self.drv_feature_sel = src.drv_feature_sel; - self.q_sel = src.q_sel; - self.q_num_max = src.q_num_max; - self.irt_stat = src.irt_stat; - self.irt_ack = src.irt_ack; - self.dev_stat = src.dev_stat; - } } +/// Represents a Virtio MMIO device. #[derive(Clone)] pub struct VirtioMmio { inner: Arc>, } impl VirtioQueue for VirtioMmio { + /// Initializes the Virtio queue based on the specified VirtioDeviceType. fn virtio_queue_init(&self, dev_type: VirtioDeviceType) { match dev_type { VirtioDeviceType::Block => { @@ -170,6 +170,7 @@ impl VirtioQueue for VirtioMmio { } } + /// Resets the Virtio queue at the specified index. fn virtio_queue_reset(&self, index: usize) { let inner = self.inner.lock(); inner.vq[index].reset(index); @@ -177,12 +178,14 @@ impl VirtioQueue for VirtioMmio { } impl VirtioMmio { + /// Creates a new instance of VirtioMmio with the specified ID. pub fn new(id: usize) -> VirtioMmio { VirtioMmio { inner: Arc::new(Mutex::new(VirtioMmioInner::new(id))), } } + /// Notifies the specified VM about the configuration changes. pub fn notify_config(&self, vm: Vm) { let mut inner = self.inner.lock(); inner.regs.irt_stat |= VIRTIO_MMIO_INT_CONFIG; @@ -191,15 +194,16 @@ impl VirtioMmio { drop(inner); use crate::kernel::interrupt_vm_inject; if trgt_id == current_cpu().id { - interrupt_vm_inject(vm.clone(), vm.vcpu(0).unwrap(), int_id, 0); + interrupt_vm_inject(vm.clone(), vm.vcpu(0).unwrap(), int_id); } else { let m = IpiIntInjectMsg { vm_id: vm.id(), int_id }; if !ipi_send_msg(trgt_id, IpiType::IpiTIntInject, IpiInnerMsg::IntInjectMsg(m)) { - println!("notify_config: failed to send ipi to Core {}", trgt_id); + error!("notify_config: failed to send ipi to Core {}", trgt_id); } } } + /// Notifies the specified VM. pub fn notify(&self, vm: Vm) { let mut inner = self.inner.lock(); inner.regs.irt_stat |= VIRTIO_MMIO_INT_VRING; @@ -208,20 +212,22 @@ impl VirtioMmio { drop(inner); use crate::kernel::interrupt_vm_inject; if trgt_id == current_cpu().id { - interrupt_vm_inject(vm.clone(), vm.vcpu(0).unwrap(), int_id, 0); + interrupt_vm_inject(vm.clone(), vm.vcpu(0).unwrap(), int_id); } else { let m = IpiIntInjectMsg { vm_id: vm.id(), int_id }; if !ipi_send_msg(trgt_id, IpiType::IpiTIntInject, IpiInnerMsg::IntInjectMsg(m)) { - println!("notify_config: failed to send ipi to Core {}", trgt_id); + error!("notify_config: failed to send ipi to Core {}", trgt_id); } } } + /// Initializes the MMIO registers based on the specified VirtioDeviceType. pub fn mmio_reg_init(&self, dev_type: VirtioDeviceType) { let mut inner = self.inner.lock(); inner.reg_init(dev_type); } + /// Initializes the Virtio device based on the specified parameters. pub fn dev_init(&self, dev_type: VirtioDeviceType, config: &VmEmulatedDeviceConfig, mediated: bool) { let inner = self.inner.lock(); inner.dev.init(dev_type, config, mediated) @@ -240,101 +246,121 @@ impl VirtioMmio { inner.dev.set_activated(false); } + /// Sets the Interrupt Status (IRT_STAT) for the Virtio MMIO device. pub fn set_irt_stat(&self, irt_stat: u32) { let mut inner = self.inner.lock(); inner.regs.irt_stat = irt_stat; } + /// Sets the Interrupt Acknowledge (IRT_ACK) for the Virtio MMIO device. pub fn set_irt_ack(&self, irt_ack: u32) { let mut inner = self.inner.lock(); inner.regs.irt_ack = irt_ack; } + /// Sets the selected queue index (Q_SEL) for the Virtio MMIO device. pub fn set_q_sel(&self, q_sel: u32) { let mut inner = self.inner.lock(); inner.regs.q_sel = q_sel; } + /// Sets the Device Status (DEV_STAT) for the Virtio MMIO device. pub fn set_dev_stat(&self, dev_stat: u32) { let mut inner = self.inner.lock(); inner.regs.dev_stat = dev_stat; } + /// Sets the maximum queue number (Q_NUM_MAX) for the Virtio MMIO device. pub fn set_q_num_max(&self, q_num_max: u32) { let mut inner = self.inner.lock(); inner.regs.q_num_max = q_num_max; } + /// Sets the Device Features (DEV_FEATURE) for the Virtio MMIO device. pub fn set_dev_feature(&self, dev_feature: u32) { let mut inner = self.inner.lock(); inner.regs.dev_feature = dev_feature; } + /// Sets the Device Features Selector (DEV_FEATURE_SEL) for the Virtio MMIO device. pub fn set_dev_feature_sel(&self, dev_feature_sel: u32) { let mut inner = self.inner.lock(); inner.regs.dev_feature_sel = dev_feature_sel; } + /// Sets the Driver Features (DRV_FEATURE) for the Virtio MMIO device. pub fn set_drv_feature(&self, drv_feature: u32) { let mut inner = self.inner.lock(); inner.regs.drv_feature = drv_feature; } + /// Sets the Driver Features Selector (DRV_FEATURE_SEL) for the Virtio MMIO device. pub fn set_drv_feature_sel(&self, drv_feature_sel: u32) { let mut inner = self.inner.lock(); inner.regs.drv_feature = drv_feature_sel; } + /// Performs a bitwise OR operation on the Driver Features (DRV_FEATURE) with the specified value. pub fn or_driver_feature(&self, driver_features: usize) { let mut inner = self.inner.lock(); inner.driver_features |= driver_features; } + /// Retrieves a clone of the VirtioDev associated with the Virtio MMIO device. pub fn dev(&self) -> VirtDev { let inner = self.inner.lock(); inner.dev.clone() } + /// Retrieves the selected queue index (Q_SEL) for the Virtio MMIO device. pub fn q_sel(&self) -> u32 { let inner = self.inner.lock(); inner.regs.q_sel } + /// Retrieves the magic value from the Virtio MMIO registers. pub fn magic(&self) -> u32 { let inner = self.inner.lock(); inner.regs.magic } + /// Retrieves the version value from the Virtio MMIO registers. pub fn version(&self) -> u32 { let inner = self.inner.lock(); inner.regs.version } + /// Retrieves the device ID from the Virtio MMIO registers. pub fn device_id(&self) -> u32 { let inner = self.inner.lock(); inner.regs.device_id } + /// Retrieves the vendor ID from the Virtio MMIO registers. pub fn vendor_id(&self) -> u32 { let inner = self.inner.lock(); inner.regs.vendor_id } + /// Retrieves the device status (DEV_STAT) from the Virtio MMIO registers. pub fn dev_stat(&self) -> u32 { let inner = self.inner.lock(); inner.regs.dev_stat } + /// Retrieves the Device Features Selector (DEV_FEATURE_SEL) from the Virtio MMIO registers. pub fn dev_feature_sel(&self) -> u32 { let inner = self.inner.lock(); inner.regs.dev_feature_sel } + /// Retrieves the Driver Features Selector (DRV_FEATURE_SEL) from the Virtio MMIO registers. pub fn drv_feature_sel(&self) -> u32 { let inner = self.inner.lock(); inner.regs.drv_feature_sel } + /// Retrieves the maximum queue number (Q_NUM_MAX) from the Virtio MMIO registers. pub fn q_num_max(&self) -> u32 { let inner = self.inner.lock(); inner.regs.q_num_max @@ -345,19 +371,24 @@ impl VirtioMmio { inner.regs.irt_stat } + /// Retrieves a clone of the Virtq associated with the specified index, wrapped in a Result. + /// Returns an Err variant if the index is out of bounds. pub fn vq(&self, idx: usize) -> Result { let inner = self.inner.lock(); if idx >= inner.vq.len() { return Err(()); } - return Ok(inner.vq[idx].clone()); + Ok(inner.vq[idx].clone()) } + /// Retrieves the ID of the Virtio MMIO device. pub fn id(&self) -> usize { let inner = self.inner.lock(); inner.id } + /// Calls the notify handler for the Virtq associated with the specified index. + /// Returns true if the operation is successful, otherwise false. pub fn notify_handler(&self, idx: usize) -> bool { let inner = self.inner.lock(); if idx >= inner.vq.len() { @@ -365,71 +396,11 @@ impl VirtioMmio { } let vq = inner.vq[idx].clone(); drop(inner); - return vq.call_notify_handler(self.clone()); - } - - // use for migration restore - pub fn restore_mmio_data(&self, mmio_data: &VirtioMmioData, pa_region: &Vec) { - let mut inner = self.inner.lock(); - // inner.id = mmio_data.id; - inner.driver_features = mmio_data.driver_features; - inner.driver_status = mmio_data.driver_status; - inner.regs = mmio_data.regs; - inner.dev.restore_virt_dev_data(&mmio_data.dev); - for (idx, vq) in inner.vq.iter().enumerate() { - vq.restore_vq_data(&mmio_data.vq[idx], pa_region); - } - } - - // use for migration save - pub fn save_mmio_data(&self, mmio_data: &mut VirtioMmioData, pa_region: &Vec) { - let inner = self.inner.lock(); - mmio_data.id = inner.id; - mmio_data.driver_features = inner.driver_features; - mmio_data.driver_status = inner.driver_status; - mmio_data.regs = inner.regs; - inner.dev.save_virt_dev_data(&mut mmio_data.dev); - for (idx, vq) in inner.vq.iter().enumerate() { - vq.save_vq_data(&mut mmio_data.vq[idx], pa_region); - } - - // if let DevDescData::ConsoleDesc(desc_data) = &mmio_data.dev.desc { - // let oppo_vm_id = desc_data.oppo_end_vmid; - // let oppo_vm_ipa = desc_data.oppo_end_ipa; - // if oppo_vm_id != 0 { - // return; - // } - // let vm = vm(oppo_vm_id as usize).unwrap(); - // if let EmuDevs::VirtioConsole(console_mmio) = vm.emu_console_dev(oppo_vm_ipa as usize) { - // console_mmio.dev().save_virt_dev_data(&mut mmio_data.oppo_dev); - // } - // } - } - - // use for live update - pub fn save_mmio(&self, virtio_mmio: VirtioMmio, notify_handler: Option bool>) { - // println!("save mmio notify_handler addr {:x}", unsafe { *(&(notify_handler.unwrap()) as *const _ as *const usize) }); - let mut dst_dev = self.inner.lock(); - let src_dev = virtio_mmio.inner.lock(); - let is_net = src_dev.dev.is_net(); - dst_dev.id = src_dev.id; - dst_dev.driver_features = src_dev.driver_features; - dst_dev.driver_status = src_dev.driver_status; - dst_dev.regs.save_regs(&src_dev.regs); - dst_dev.dev.save_virt_dev(src_dev.dev.clone()); - for (idx, vq) in src_dev.vq.iter().enumerate() { - let new_vq = Virtq::default(); - if is_net && idx == src_dev.vq.len() - 1 && idx % 2 == 0 { - // control queue - new_vq.save_vq(vq.clone(), Some(virtio_net_handle_ctrl)); - } else { - new_vq.save_vq(vq.clone(), notify_handler); - } - dst_dev.vq.push(new_vq); - } + vq.call_notify_handler(self.clone()) } } +/// Represents the inner data structure of VirtioMmio. struct VirtioMmioInner { id: usize, driver_features: usize, @@ -441,6 +412,7 @@ struct VirtioMmioInner { } impl VirtioMmioInner { + /// Creates a new `VirtioMmioInner` instance with the given ID. fn new(id: usize) -> VirtioMmioInner { VirtioMmioInner { id, @@ -452,11 +424,13 @@ impl VirtioMmioInner { } } + /// Initializes the registers of the `VirtioMmioInner` with the specified device type. fn reg_init(&mut self, dev_type: VirtioDeviceType) { self.regs.init(dev_type); } } +/// Handles prologue access to Virtio MMIO registers. fn virtio_mmio_prologue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usize, write: bool) { if !write { let value; @@ -485,7 +459,7 @@ fn virtio_mmio_prologue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: u value = mmio.dev_stat(); } _ => { - println!("virtio_be_init_handler wrong reg_read, address=0x{:x}", emu_ctx.address); + error!("virtio_be_init_handler wrong reg_read, address=0x{:x}", emu_ctx.address); return; } } @@ -521,13 +495,13 @@ fn virtio_mmio_prologue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: u } } _ => { - println!("virtio_mmio_prologue_access: wrong reg write 0x{:x}", emu_ctx.address); - return; + error!("virtio_mmio_prologue_access: wrong reg write 0x{:x}", emu_ctx.address); } } } } +/// Handles queue access to Virtio MMIO registers. fn virtio_mmio_queue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usize, write: bool) { if !write { let value; @@ -549,7 +523,7 @@ fn virtio_mmio_queue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usiz } } _ => { - println!( + error!( "virtio_mmio_queue_access: wrong reg_read, address {:x}", emu_ctx.address ); @@ -621,10 +595,15 @@ fn virtio_mmio_queue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usiz virtq.or_desc_table_addr(value << 32); let desc_table_addr = vm_ipa2pa(active_vm().unwrap(), virtq.desc_table_addr()); if desc_table_addr == 0 { - println!("virtio_mmio_queue_access: invalid desc_table_addr"); + error!("virtio_mmio_queue_access: invalid desc_table_addr"); return; } - virtq.set_desc_table(desc_table_addr); + // SAFETY: + // The 'desc_table_addr' is valid MMIO address of virtio-blk config + // And it is checked by vm_ipa2pa to gurantee that it is in the range of vm config memory + unsafe { + virtq.set_desc_table(desc_table_addr); + } } Err(_) => { panic!( @@ -649,10 +628,15 @@ fn virtio_mmio_queue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usiz virtq.or_avail_addr(value << 32); let avail_addr = vm_ipa2pa(active_vm().unwrap(), virtq.avail_addr()); if avail_addr == 0 { - println!("virtio_mmio_queue_access: invalid avail_addr"); + error!("virtio_mmio_queue_access: invalid avail_addr"); return; } - virtq.set_avail(avail_addr); + // SAFETY: + // The 'avail_addr' is valid MMIO address of virtio-blk config + // And it is checked by vm_ipa2pa to gurantee that it is in the range of vm config memory + unsafe { + virtq.set_avail(avail_addr); + } } Err(_) => { panic!( @@ -677,10 +661,15 @@ fn virtio_mmio_queue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usiz virtq.or_used_addr(value << 32); let used_addr = vm_ipa2pa(active_vm().unwrap(), virtq.used_addr()); if used_addr == 0 { - println!("virtio_mmio_queue_access: invalid used_addr"); + error!("virtio_mmio_queue_access: invalid used_addr"); return; } - virtq.set_used(used_addr); + // SAFETY: + // The 'used_addr' is valid MMIO address of virtio-blk config + // And it is checked by vm_ipa2pa to gurantee that it is in the range of vm config memory + unsafe { + virtq.set_used(used_addr); + } } Err(_) => { panic!( @@ -690,43 +679,47 @@ fn virtio_mmio_queue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usiz } }, _ => { - println!("virtio_mmio_queue_access: wrong reg write 0x{:x}", emu_ctx.address); + error!("virtio_mmio_queue_access: wrong reg write 0x{:x}", emu_ctx.address); } } } } +/// Handles config space access to Virtio MMIO registers. fn virtio_mmio_cfg_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usize, write: bool) { if !write { let value; + let width = emu_ctx.width; match offset { VIRTIO_MMIO_CONFIG_GENERATION => { - value = mmio.dev().generation() as u32; + value = mmio.dev().generation(); } VIRTIO_MMIO_CONFIG..=0x1ff => match mmio.dev().desc() { super::DevDesc::BlkDesc(blk_desc) => { - value = blk_desc.offset_data(offset - VIRTIO_MMIO_CONFIG); + // SAFETY: Offset is between VIRTIO_MMIO_CONFIG..VIRTIO_MMIO_REGS_END ,so is valid + value = unsafe { blk_desc.offset_data(offset - VIRTIO_MMIO_CONFIG, width) }; } super::DevDesc::NetDesc(net_desc) => { - value = net_desc.offset_data(offset - VIRTIO_MMIO_CONFIG); + // SAFETY: Offset is between VIRTIO_MMIO_CONFIG..VIRTIO_MMIO_REGS_END ,so is valid + value = unsafe { net_desc.offset_data(offset - VIRTIO_MMIO_CONFIG, width) }; } _ => { panic!("unknow desc type"); } }, _ => { - println!("virtio_mmio_cfg_access: wrong reg write 0x{:x}", emu_ctx.address); + error!("virtio_mmio_cfg_access: wrong reg write 0x{:x}", emu_ctx.address); return; } } let idx = emu_ctx.reg; - let val = value as usize; - current_cpu().set_gpr(idx, val); + current_cpu().set_gpr(idx, value); } else { - println!("virtio_mmio_cfg_access: wrong reg write 0x{:x}", emu_ctx.address); + error!("virtio_mmio_cfg_access: wrong reg write 0x{:x}", emu_ctx.address); } } +/// Initializes the Virtio MMIO device for a virtual machine. pub fn emu_virtio_mmio_init(vm: Vm, emu_dev_id: usize, mediated: bool) -> bool { let virt_dev_type: VirtioDeviceType; let vm_cfg = vm.config(); @@ -745,7 +738,7 @@ pub fn emu_virtio_mmio_init(vm: Vm, emu_dev_id: usize, mediated: bool) -> bool { vm.set_emu_devs(emu_dev_id, EmuDevs::VirtioConsole(mmio.clone())); } _ => { - println!("emu_virtio_mmio_init: unknown emulated device type"); + error!("emu_virtio_mmio_init: unknown emulated device type"); return false; } } @@ -758,8 +751,11 @@ pub fn emu_virtio_mmio_init(vm: Vm, emu_dev_id: usize, mediated: bool) -> bool { true } +/// Handles Virtio MMIO events for the specified emulated device. pub fn emu_virtio_mmio_handler(emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { - // println!("emu_virtio_mmio_handler active_vcpu addr {:x}", unsafe { *(¤t_cpu().active_vcpu as *const _ as *const usize) }); + trace!("emu_virtio_mmio_handler active_vcpu addr {:x}", unsafe { + *(¤t_cpu().active_vcpu as *const _ as *const usize) + }); let vm = match active_vm() { Some(vm) => vm, None => { @@ -780,22 +776,13 @@ pub fn emu_virtio_mmio_handler(emu_dev_id: usize, emu_ctx: &EmuContext) -> bool let offset = addr - vm.config().emulated_device_list()[emu_dev_id].base_ipa; let write = emu_ctx.write; - // if vm.vm_id() == 1 && emu_dev_id == 2 { - // println!("### emu_virtio_mmio_handler offset {:x} ###", offset); - // } if offset == VIRTIO_MMIO_QUEUE_NOTIFY && write { - mmio.set_irt_stat(VIRTIO_MMIO_INT_VRING as u32); - // let q_sel = mmio.q_sel(); - // if q_sel as usize != current_cpu().get_gpr(emu_ctx.reg) { - // println!("{} {}", q_sel as usize, current_cpu().get_gpr(emu_ctx.reg)); - // } - // println!("in VIRTIO_MMIO_QUEUE_NOTIFY"); + mmio.set_irt_stat(VIRTIO_MMIO_INT_VRING); if !mmio.notify_handler(current_cpu().get_gpr(emu_ctx.reg)) { - println!("Failed to handle virtio mmio request!"); + error!("Failed to handle virtio mmio request!"); } } else if offset == VIRTIO_MMIO_INTERRUPT_STATUS && !write { - // println!("in VIRTIO_MMIO_INTERRUPT_STATUS"); let idx = emu_ctx.reg; let val = mmio.irt_stat() as usize; current_cpu().set_gpr(idx, val); @@ -804,19 +791,16 @@ pub fn emu_virtio_mmio_handler(emu_dev_id: usize, emu_ctx: &EmuContext) -> bool let val = mmio.irt_stat(); mmio.set_irt_stat(val & !(current_cpu().get_gpr(idx) as u32)); mmio.set_irt_ack(current_cpu().get_gpr(idx) as u32); - } else if (VIRTIO_MMIO_MAGIC_VALUE <= offset && offset <= VIRTIO_MMIO_GUEST_FEATURES_SEL) + } else if (VIRTIO_MMIO_MAGIC_VALUE..=VIRTIO_MMIO_GUEST_FEATURES_SEL).contains(&offset) || offset == VIRTIO_MMIO_STATUS { - // println!("in virtio_mmio_prologue_access"); virtio_mmio_prologue_access(mmio, emu_ctx, offset, write); - } else if VIRTIO_MMIO_QUEUE_SEL <= offset && offset <= VIRTIO_MMIO_QUEUE_USED_HIGH { - // println!("in virtio_mmio_queue_access"); + } else if (VIRTIO_MMIO_QUEUE_SEL..=VIRTIO_MMIO_QUEUE_USED_HIGH).contains(&offset) { virtio_mmio_queue_access(mmio, emu_ctx, offset, write); - } else if VIRTIO_MMIO_CONFIG_GENERATION <= offset && offset <= VIRTIO_MMIO_REGS_END { - // println!("in virtio_mmio_cfg_access"); + } else if (VIRTIO_MMIO_CONFIG_GENERATION..=VIRTIO_MMIO_REGS_END).contains(&offset) { virtio_mmio_cfg_access(mmio, emu_ctx, offset, write); } else { - println!( + error!( "emu_virtio_mmio_handler: regs wrong {}, address 0x{:x}, offset 0x{:x}", if write { "write" } else { "read" }, addr, @@ -824,5 +808,5 @@ pub fn emu_virtio_mmio_handler(emu_dev_id: usize, emu_ctx: &EmuContext) -> bool ); return false; } - return true; + true } diff --git a/src/device/virtio/mod.rs b/src/device/virtio/mod.rs index 3d1d6c1591d584da8d9ee42304a134591b94590b..bf870fb0b921a3add461e9a1b5f7d2b94a812fdd 100644 --- a/src/device/virtio/mod.rs +++ b/src/device/virtio/mod.rs @@ -8,6 +8,9 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +//! Re-exporting common items +//! Module specific items + pub use self::blk::*; pub use self::dev::*; pub use self::iov::*; diff --git a/src/device/virtio/net.rs b/src/device/virtio/net.rs index 1df1053a2cf76b8d01483fd11f1f944ef968523f..db0af6a755c9dec274428a0b47b6a504586ed40f 100644 --- a/src/device/virtio/net.rs +++ b/src/device/virtio/net.rs @@ -9,7 +9,7 @@ // See the Mulan PSL v2 for more details. use alloc::sync::Arc; -use alloc::vec::Vec; + use core::mem::size_of; use spin::Mutex; @@ -19,14 +19,14 @@ use crate::device::{DevDesc, VirtioMmio, Virtq, VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_ use crate::device::EmuDevs; use crate::device::VirtioIov; use crate::kernel::{ - active_vm, active_vm_id, current_cpu, NetDescData, vm_if_cmp_mac, vm_if_get_cpu_id, vm_if_set_mem_map_bit, - vm_ipa2pa, VM_LIST, VM_STATE_FLAG, + active_vm, active_vm_id, current_cpu, vm_if_cmp_mac, vm_if_get_cpu_id, vm_if_set_mem_map_bit, vm_ipa2pa, VM_LIST, + VM_STATE_FLAG, }; use crate::kernel::{ipi_send_msg, IpiEthernetMsg, IpiInnerMsg, IpiType}; use crate::kernel::IpiMessage; use crate::kernel::vm; use crate::kernel::Vm; -use crate::lib::{round_down, trace}; +use crate::utils::{round_down, trace}; const VIRTIO_NET_OK: u8 = 0; const VIRTIO_NET_ERR: u8 = 1; @@ -69,6 +69,7 @@ const VIRTIO_NET_HDR_F_DATA_VALID: usize = 2; const VIRTIO_NET_HDR_GSO_NONE: usize = 0; +/// Represents the header structure for VirtioNet. #[repr(C)] struct VirtioNetHdr { pub flags: u8, @@ -80,41 +81,39 @@ struct VirtioNetHdr { pub num_buffers: u16, } +/// A cloneable wrapper for the `NetDescInner` structure. #[derive(Clone)] pub struct NetDesc { inner: Arc>, } +/// Holds data related to the network device. +pub struct NetDescData { + pub mac: [u8; 6], + pub status: u16, +} + impl NetDesc { + /// Creates a new `NetDesc` instance with default values. pub fn default() -> NetDesc { NetDesc { inner: Arc::new(Mutex::new(NetDescInner::default())), } } - // use for live update - pub fn back_up(&self) -> NetDesc { - let current_inner = self.inner.lock(); - let inner = NetDescInner { - mac: current_inner.mac, - status: current_inner.status, - }; - NetDesc { - inner: Arc::new(Mutex::new(inner)), - } - } - pub fn set_status(&self, status: u16) { let mut inner = self.inner.lock(); inner.status = status; } + /// Retrieves the status of the network device. pub fn status(&self) -> u16 { let inner = self.inner.lock(); inner.status } - pub fn cfg_init(&self, mac: &Vec) { + /// Initializes the configuration of the network device with the provided MAC address. + pub fn cfg_init(&self, mac: &[usize]) { let mut inner = self.inner.lock(); inner.mac[0] = mac[0] as u8; inner.mac[1] = mac[1] as u8; @@ -124,34 +123,29 @@ impl NetDesc { inner.mac[5] = mac[5] as u8; } - pub fn offset_data(&self, offset: usize) -> u32 { + /// Computes the offset data within the `NetDesc` structure. + /// # SAFETY: + /// Caller must ensure offset is valid + /// Offset must valid for virtio_mmio + pub unsafe fn offset_data(&self, offset: usize, width: usize) -> usize { let inner = self.inner.lock(); let start_addr = &inner.mac[0] as *const _ as usize; - if trace() && start_addr + offset < 0x1000 { - println!("value addr is {}", start_addr + offset); + match width { + 1 => unsafe { *((start_addr + offset) as *const u8) as usize }, + 2 => unsafe { *((start_addr + offset) as *const u16) as usize }, + 4 => unsafe { *((start_addr + offset) as *const u32) as usize }, + 8 => unsafe { *((start_addr + offset) as *const u64) as usize }, + _ => 0, } - let value = unsafe { *((start_addr + offset) as *const u32) }; - return value; - } - - // use for migration - pub fn restore_net_data(&self, desc_data: &NetDescData) { - let mut inner = self.inner.lock(); - inner.mac = desc_data.mac; - inner.status = desc_data.status; - } - - // use for migration - pub fn save_net_data(&self, desc_data: &mut NetDescData) { - let inner = self.inner.lock(); - desc_data.mac = inner.mac; - desc_data.status = inner.status; } } +/// Constant representing the network link being up. pub const VIRTIO_NET_S_LINK_UP: u16 = 1; +/// Constant representing network announcement. pub const VIRTIO_NET_S_ANNOUNCE: u16 = 2; +/// Represents the inner data structure for `NetDesc`. #[repr(C)] #[derive(Clone, Copy)] pub struct NetDescInner { @@ -160,6 +154,7 @@ pub struct NetDescInner { } impl NetDescInner { + /// Creates a new `NetDescInner` instance with default values. pub fn default() -> NetDescInner { NetDescInner { mac: [0; 6], @@ -168,6 +163,7 @@ impl NetDescInner { } } +/// Represents the control header structure for VirtioNet. #[repr(C)] #[derive(Clone, Copy, Default)] struct VirtioNetCtrlHdr { @@ -175,6 +171,7 @@ struct VirtioNetCtrlHdr { command: u8, } +/// Retrieves network features. pub fn net_features() -> usize { VIRTIO_F_VERSION_1 | VIRTIO_NET_F_GUEST_CSUM @@ -192,12 +189,15 @@ pub fn net_features() -> usize { | VIRTIO_NET_F_STATUS } +/// Constant representing VirtioNet control announcement. const VIRTIO_NET_CTRL_ANNOUNCE: u8 = 3; +/// Constant representing VirtioNet control announcement acknowledgment. const VIRTIO_NET_CTRL_ANNOUNCE_ACK: u8 = 0; +/// Handles VirtioNet control operations. pub fn virtio_net_handle_ctrl(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { if vq.ready() == 0 { - println!("virtio net control queue is not ready!"); + error!("virtio net control queue is not ready!"); return false; } @@ -213,7 +213,7 @@ pub fn virtio_net_handle_ctrl(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { loop { let addr = vm_ipa2pa(active_vm().unwrap(), vq.desc_addr(idx)); if addr == 0 { - println!("virtio_net_handle_ctrl: failed to desc addr"); + error!("virtio_net_handle_ctrl: failed to desc addr"); return false; } if vq.desc_flags(idx) & VIRTQ_DESC_F_WRITE != 0 { @@ -247,15 +247,15 @@ pub fn virtio_net_handle_ctrl(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { in_iov.from_buf(&status as *const _ as usize, size_of::()); } _ => { - println!("Control queue header class can't match {}", ctrl.class); + warn!("Control queue header class can't match {}", ctrl.class); } } // update ctrl queue used ring if vm.id() != 0 { let used_addr = vm_ipa2pa(vm.clone(), vq.used_addr()); - if *VM_STATE_FLAG.lock() == 1 { - println!("vm1 virtio net ctrl write memory in 0x{:x}", used_addr); + if VM_STATE_FLAG.load(core::sync::atomic::Ordering::Relaxed) == 1 { + debug!("vm1 virtio net ctrl write memory in 0x{:x}", used_addr); } vm_if_set_mem_map_bit(vm.clone(), used_addr); @@ -272,9 +272,11 @@ pub fn virtio_net_handle_ctrl(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { true } +/// Handles the notification from the VirtioNet device to the specified virtual queue (`vq`) in a virtual machine (`vm`). +/// Returns `true` if the notification is successfully processed; otherwise, returns `false`. pub fn virtio_net_notify_handler(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { if vq.ready() == 0 { - println!("net virt_queue is not ready!"); + error!("net virt_queue is not ready!"); return false; } @@ -296,7 +298,7 @@ pub fn virtio_net_notify_handler(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { loop { let addr = vm_ipa2pa(active_vm().unwrap(), vq.desc_addr(idx)); if addr == 0 { - println!("virtio_net_notify_handler: failed to desc addr"); + error!("virtio_net_notify_handler: failed to desc addr"); return false; } tx_iov.push_data(addr, vq.desc_len(idx) as usize); @@ -315,8 +317,8 @@ pub fn virtio_net_notify_handler(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { if vm.id() != 0 { let used_addr = vm_ipa2pa(vm.clone(), vq.used_addr()); - if *VM_STATE_FLAG.lock() == 1 { - println!("vm1 virtio net write memory in 0x{:x}", used_addr); + if VM_STATE_FLAG.load(core::sync::atomic::Ordering::Relaxed) == 1 { + debug!("vm1 virtio net write memory in 0x{:x}", used_addr); } vm_if_set_mem_map_bit(vm.clone(), used_addr); vm_if_set_mem_map_bit(vm.clone(), used_addr + PAGE_SIZE); @@ -332,7 +334,7 @@ pub fn virtio_net_notify_handler(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { } if !vq.avail_is_avail() { - println!("invalid descriptor table index"); + error!("invalid descriptor table index"); return false; } @@ -343,7 +345,7 @@ pub fn virtio_net_notify_handler(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { if vms_to_notify & 1 != 0 { let vm = match crate::kernel::vm(trgt_vmid) { None => { - println!( + error!( "virtio_net_notify_handler: target vm [{}] is not ready or not exist", trgt_vmid ); @@ -356,14 +358,14 @@ pub fn virtio_net_notify_handler(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { let nic = match vm.emu_net_dev(0) { EmuDevs::VirtioNet(x) => x, _ => { - println!("virtio_net_notify_handler: failed to get virtio net dev"); + error!("virtio_net_notify_handler: failed to get virtio net dev"); return false; } }; let rx_vq = match nic.vq(0) { Ok(x) => x, Err(_) => { - println!( + error!( "virtio_net_notify_handler: vm[{}] failed to get virtio net rx virt queue", vm.id() ); @@ -381,7 +383,7 @@ pub fn virtio_net_notify_handler(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { }; let cpu_trgt = vm_if_get_cpu_id(trgt_vmid); if !ipi_send_msg(cpu_trgt, IpiType::IpiTEthernetMsg, IpiInnerMsg::EnternetMsg(msg)) { - println!( + error!( "virtio_net_notify_handler: failed to send ipi message, target {}", cpu_trgt ); @@ -390,18 +392,19 @@ pub fn virtio_net_notify_handler(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { } trgt_vmid += 1; - vms_to_notify = vms_to_notify >> 1; + vms_to_notify >>= 1; } true } +/// Handles the IPI (Inter-Processor Interrupt) message related to Ethernet. pub fn ethernet_ipi_rev_handler(msg: &IpiMessage) { match msg.ipi_message { IpiInnerMsg::EnternetMsg(ethernet_msg) => { let trgt_vmid = ethernet_msg.trgt_vmid; let vm = match vm(trgt_vmid) { None => { - println!( + error!( "ethernet_ipi_rev_handler: target vm [{}] is not ready or not exist", trgt_vmid ); @@ -422,7 +425,7 @@ pub fn ethernet_ipi_rev_handler(msg: &IpiMessage) { let rx_vq = match nic.vq(0) { Ok(x) => x, Err(_) => { - println!( + error!( "ethernet_ipi_rev_handler: vm[{}] failed to get virtio net rx virt queue", vm.id() ); @@ -441,10 +444,13 @@ pub fn ethernet_ipi_rev_handler(msg: &IpiMessage) { } } +/// Transmits Ethernet frames using VirtioNet. +/// Returns a tuple with the first element indicating success (`true` if successful) and the second element +/// representing the target virtual machine's bitmask. fn ethernet_transmit(tx_iov: VirtioIov, len: usize) -> (bool, usize) { // [ destination MAC - 6 ][ source MAC - 6 ][ EtherType - 2 ][ Payload ] if len < size_of::() || len - size_of::() < 6 + 6 + 2 { - println!( + warn!( "Too short for an ethernet frame, len {}, size of head {}", len, size_of::() @@ -478,15 +484,12 @@ fn ethernet_transmit(tx_iov: VirtioIov, len: usize) -> (bool, usize) { } match ethernet_mac_to_vm_id(frame) { - Ok(vm_id) => { - return (ethernet_send_to(vm_id, tx_iov.clone(), len), 1 << vm_id); - } - Err(_) => { - return (false, 0); - } + Ok(vm_id) => (ethernet_send_to(vm_id, tx_iov.clone(), len), 1 << vm_id), + Err(_) => (false, 0), } } +/// Broadcasts an Ethernet frame to all virtual machines, excluding the current one. fn ethernet_broadcast(tx_iov: VirtioIov, len: usize) -> (bool, usize) { let vm_num = vm_num(); let cur_vm_id = active_vm_id(); @@ -506,6 +509,7 @@ fn ethernet_broadcast(tx_iov: VirtioIov, len: usize) -> (bool, usize) { (trgt_vmid_map != 0, trgt_vmid_map) } +/// Sends an Ethernet frame to the specified virtual machine (`vmid`). fn ethernet_send_to(vmid: usize, tx_iov: VirtioIov, len: usize) -> bool { // println!("ethernet send to vm{}", vmid); let vm = match vm(vmid) { @@ -531,7 +535,7 @@ fn ethernet_send_to(vmid: usize, tx_iov: VirtioIov, len: usize) -> bool { let rx_vq = match nic.vq(0) { Ok(x) => x, Err(_) => { - println!( + error!( "ethernet_send_to: vm[{}] failed to get virtio net rx virt queue", vm.id() ); @@ -541,7 +545,7 @@ fn ethernet_send_to(vmid: usize, tx_iov: VirtioIov, len: usize) -> bool { let desc_header_idx_opt = rx_vq.pop_avail_desc_idx(rx_vq.avail_idx()); if !rx_vq.avail_is_avail() { - println!("ethernet_send_to: receive invalid avail desc idx"); + error!("ethernet_send_to: receive invalid avail desc idx"); return false; } else if desc_header_idx_opt.is_none() { // println!("ethernet_send_to: desc_header_idx_opt is none"); @@ -556,22 +560,22 @@ fn ethernet_send_to(vmid: usize, tx_iov: VirtioIov, len: usize) -> bool { loop { let dst = vm_ipa2pa(vm.clone(), rx_vq.desc_addr(desc_idx)); if dst == 0 { - println!( + debug!( "rx_vq desc base table addr 0x{:x}, idx {}, avail table addr 0x{:x}, avail last idx {}", rx_vq.desc_table_addr(), desc_idx, rx_vq.avail_addr(), rx_vq.avail_idx() ); - println!("ethernet_send_to: failed to get dst {}", vmid); + error!("ethernet_send_to: failed to get dst {}", vmid); return false; } let desc_len = rx_vq.desc_len(desc_idx) as usize; if vmid != 0 { let mut addr = round_down(dst, PAGE_SIZE); - if *VM_STATE_FLAG.lock() == 1 { - println!("A: vm0 virtio net write vm1 memory in 0x{:x}", addr); + if VM_STATE_FLAG.load(core::sync::atomic::Ordering::Relaxed) == 1 { + debug!("A: vm0 virtio net write vm1 memory in 0x{:x}", addr); } while addr <= round_down(dst + desc_len, PAGE_SIZE) { vm_if_set_mem_map_bit(vm.clone(), addr); @@ -591,7 +595,7 @@ fn ethernet_send_to(vmid: usize, tx_iov: VirtioIov, len: usize) -> bool { if rx_len < len { rx_vq.put_back_avail_desc_idx(); - println!("ethernet_send_to: rx_len smaller than tx_len"); + warn!("ethernet_send_to: rx_len smaller than tx_len"); return false; } if trace() && tx_iov.get_buf(0) < 0x1000 { @@ -601,7 +605,7 @@ fn ethernet_send_to(vmid: usize, tx_iov: VirtioIov, len: usize) -> bool { header.num_buffers = 1; if tx_iov.write_through_iov(rx_iov.clone(), len) > 0 { - println!( + error!( "ethernet_send_to: write through iov failed, rx_iov_num {} tx_iov_num {} rx_len {} tx_len {}", rx_iov.num(), tx_iov.num(), @@ -613,8 +617,8 @@ fn ethernet_send_to(vmid: usize, tx_iov: VirtioIov, len: usize) -> bool { if vmid != 0 { let used_addr = vm_ipa2pa(vm.clone(), rx_vq.used_addr()); - if *VM_STATE_FLAG.lock() == 1 { - println!("B: vm0 virtio net write vm1 memory in 0x{:x}", used_addr); + if VM_STATE_FLAG.load(core::sync::atomic::Ordering::Relaxed) == 1 { + debug!("B: vm0 virtio net write vm1 memory in 0x{:x}", used_addr); } vm_if_set_mem_map_bit(vm.clone(), used_addr); vm_if_set_mem_map_bit(vm, used_addr + PAGE_SIZE); @@ -623,13 +627,15 @@ fn ethernet_send_to(vmid: usize, tx_iov: VirtioIov, len: usize) -> bool { return false; } - return true; + true } +/// Determines whether the given Ethernet frame is an ARP (Address Resolution Protocol) packet. fn ethernet_is_arp(frame: &[u8]) -> bool { - return frame[12] == 0x8 && frame[13] == 0x6; + frame[12] == 0x8 && frame[13] == 0x6 } +/// Maps the MAC address in the Ethernet frame to the corresponding virtual machine ID. fn ethernet_mac_to_vm_id(frame: &[u8]) -> Result { for vm in VM_LIST.lock().iter() { let vm_id = vm.id(); @@ -637,19 +643,16 @@ fn ethernet_mac_to_vm_id(frame: &[u8]) -> Result { return Ok(vm_id); } } - return Err(()); + Err(()) } +/// Handles the VirtioNet announcement in a virtual machine (`vm`). pub fn virtio_net_announce(vm: Vm) { - match vm.emu_net_dev(0) { - EmuDevs::VirtioNet(nic) => match nic.dev().desc() { - DevDesc::NetDesc(desc) => { - let status = desc.status(); - desc.set_status(status | VIRTIO_NET_S_ANNOUNCE); - nic.notify_config(vm); - } - _ => {} - }, - _ => {} + if let EmuDevs::VirtioNet(nic) = vm.emu_net_dev(0) { + if let DevDesc::NetDesc(desc) = nic.dev().desc() { + let status = desc.status(); + desc.set_status(status | VIRTIO_NET_S_ANNOUNCE); + nic.notify_config(vm); + } } } diff --git a/src/device/virtio/queue.rs b/src/device/virtio/queue.rs index 6bdc68d60a900ecfb1a9287ddb8696a85852d37f..ff00db0ec653ee0c8b445d62c1109c8257a583ac 100644 --- a/src/device/virtio/queue.rs +++ b/src/device/virtio/queue.rs @@ -9,15 +9,14 @@ // See the Mulan PSL v2 for more details. use alloc::sync::Arc; -use alloc::vec::Vec; + use core::slice; use spin::Mutex; use crate::device::VirtioDeviceType; use crate::device::VirtioMmio; -use crate::kernel::{active_vm, ipa2pa, VirtqData, Vm, vm_ipa2pa, VmPa}; -use crate::lib::trace; +use crate::kernel::{active_vm, Vm, vm_ipa2pa}; pub const VIRTQ_READY: usize = 1; pub const VIRTQ_DESC_F_NEXT: u16 = 1; @@ -27,64 +26,108 @@ pub const VRING_USED_F_NO_NOTIFY: usize = 1; pub const DESC_QUEUE_SIZE: usize = 512; +/// Represents a descriptor in a VirtIO ring buffer. #[repr(C, align(16))] #[derive(Copy, Clone)] struct VringDesc { - /*Address (guest-physical)*/ + /// Guest-physical address of the descriptor. pub addr: usize, - /* Length */ + /// Length of the descriptor. len: u32, - /* The flags as indicated above */ + /// Flags indicating descriptor properties. flags: u16, - /* We chain unused descriptors via this, too */ + /// Index of the next descriptor in the chain. next: u16, } +/// Represents the available ring in a VirtIO queue. #[repr(C)] #[derive(Copy, Clone)] struct VringAvail { + /// Flags indicating the state of the available ring. flags: u16, + /// Index pointing to the next available descriptor in the ring. idx: u16, + /// Array representing the available ring. ring: [u16; 512], } +/// Represents an element in the used ring of a VirtIO queue. #[repr(C)] #[derive(Copy, Clone)] struct VringUsedElem { + /// Identifier of the descriptor. pub id: u32, + /// Length of the used descriptor. pub len: u32, } +/// Represents the data associated with a VirtIO queue. +#[derive(Copy, Clone)] +pub struct VirtqData { + /// Indicates whether the VirtIO queue is ready. + pub ready: usize, + /// Index of the VirtIO queue. + pub vq_index: usize, + /// Number of descriptors in the queue. + pub num: usize, + + /// Last available index in the available ring. + pub last_avail_idx: u16, + /// Last used index in the used ring. + pub last_used_idx: u16, + /// Flags indicating the state of the used ring. + pub used_flags: u16, + + /// Guest-physical address of the descriptor table. + pub desc_table_ipa: usize, + /// Guest-physical address of the available ring. + pub avail_ipa: usize, + /// Guest-physical address of the used ring. + pub used_ipa: usize, +} + +/// Represents the used ring in a VirtIO queue. #[repr(C)] #[derive(Copy, Clone)] pub struct VringUsed { + /// Flags indicating the state of the used ring. flags: u16, + /// Index pointing to the next used descriptor in the ring. idx: u16, + /// Array representing the used ring, containing VringUsedElem elements. ring: [VringUsedElem; 512], } +/// Represents a VirtIO queue trait with initialization and reset methods. pub trait VirtioQueue { + /// Initializes the VirtIO queue with the given device type. fn virtio_queue_init(&self, dev_type: VirtioDeviceType); + /// Resets the VirtIO queue at the specified index. fn virtio_queue_reset(&self, index: usize); } +/// A wrapper struct representing a VirtIO queue. #[derive(Clone)] pub struct Virtq { inner: Arc>>, } impl Virtq { + /// Creates a new default Virtq instance. pub fn default() -> Virtq { Virtq { inner: Arc::new(Mutex::new(VirtqInner::default())), } } + /// Resets the VirtIO queue at the specified index. pub fn reset(&self, index: usize) { let mut inner = self.inner.lock(); inner.reset(index); } + /// Pops the next available descriptor index from the available ring. pub fn pop_avail_desc_idx(&self, avail_idx: u16) -> Option { let mut inner = self.inner.lock(); match &inner.avail { @@ -95,15 +138,16 @@ impl Virtq { let idx = inner.last_avail_idx as usize % inner.num; let avail_desc_idx = avail.ring[idx]; inner.last_avail_idx = inner.last_avail_idx.wrapping_add(1); - return Some(avail_desc_idx); + Some(avail_desc_idx) } None => { - println!("pop_avail_desc_idx: failed to avail table"); - return None; + error!("pop_avail_desc_idx: failed to avail table"); + None } } } + /// Puts back the last popped available descriptor index. pub fn put_back_avail_desc_idx(&self) { let mut inner = self.inner.lock(); match &inner.avail { @@ -111,16 +155,18 @@ impl Virtq { inner.last_avail_idx -= 1; } None => { - println!("put_back_avail_desc_idx: failed to avail table"); + error!("put_back_avail_desc_idx: failed to avail table"); } } } + /// Checks if the available ring has available descriptors. pub fn avail_is_avail(&self) -> bool { let inner = self.inner.lock(); inner.avail.is_some() } + /// Disables notifications for the used ring. pub fn disable_notify(&self) { let mut inner = self.inner.lock(); if inner.used_flags & VRING_USED_F_NO_NOTIFY as u16 != 0 { @@ -129,6 +175,7 @@ impl Virtq { inner.used_flags |= VRING_USED_F_NO_NOTIFY as u16; } + /// Enables notifications for the used ring. pub fn enable_notify(&self) { let mut inner = self.inner.lock(); if inner.used_flags & VRING_USED_F_NO_NOTIFY as u16 == 0 { @@ -137,23 +184,27 @@ impl Virtq { inner.used_flags &= !VRING_USED_F_NO_NOTIFY as u16; } + /// Checks if the available index matches the last available index. pub fn check_avail_idx(&self, avail_idx: u16) -> bool { let inner = self.inner.lock(); - return inner.last_avail_idx == avail_idx; + inner.last_avail_idx == avail_idx } + /// Checks if the descriptor at the given index is writable. pub fn desc_is_writable(&self, idx: usize) -> bool { let inner = self.inner.lock(); let desc_table = inner.desc_table.as_ref().unwrap(); - desc_table[idx].flags & VIRTQ_DESC_F_WRITE as u16 != 0 + desc_table[idx].flags & VIRTQ_DESC_F_WRITE != 0 } + /// Checks if the descriptor at the given index has a next descriptor in the chain. pub fn desc_has_next(&self, idx: usize) -> bool { let inner = self.inner.lock(); let desc_table = inner.desc_table.as_ref().unwrap(); desc_table[idx].flags & VIRTQ_DESC_F_NEXT != 0 } + /// Updates the used ring with the provided information. pub fn update_used_ring(&self, len: u32, desc_chain_head_idx: u32) -> bool { let mut inner = self.inner.lock(); let num = inner.num; @@ -164,152 +215,170 @@ impl Virtq { used.ring[used.idx as usize % num].id = desc_chain_head_idx; used.ring[used.idx as usize % num].len = len; used.idx = used.idx.wrapping_add(1); - return true; + true } None => { - println!("update_used_ring: failed to used table"); - return false; + error!("update_used_ring: failed to used table"); + false } } } + /// Sets the notify handler function for the VirtIO queue. pub fn set_notify_handler(&self, handler: fn(Virtq, VirtioMmio, Vm) -> bool) { let mut inner = self.inner.lock(); inner.notify_handler = Some(handler); } + /// Calls the registered notify handler function. pub fn call_notify_handler(&self, mmio: VirtioMmio) -> bool { let inner = self.inner.lock(); match inner.notify_handler { Some(handler) => { drop(inner); - // println!("call_notify_handler"); - // println!("handler addr {:x}", unsafe { *(&handler as *const _ as *const usize) }); - return handler(self.clone(), mmio, active_vm().unwrap()); + handler(self.clone(), mmio, active_vm().unwrap()) } None => { - println!("call_notify_handler: virtq notify handler is None"); - return false; + error!("call_notify_handler: virtq notify handler is None"); + false } } } + /// Displays information about the descriptors in the VirtIO queue. pub fn show_desc_info(&self, size: usize, vm: Vm) { let inner = self.inner.lock(); let desc = inner.desc_table.as_ref().unwrap(); - println!("[*desc_ring*]"); + info!("[*desc_ring*]"); for i in 0..size { let desc_addr = vm_ipa2pa(vm.clone(), desc[i].addr); - println!( + info!( "index {} desc_addr_ipa 0x{:x} desc_addr_pa 0x{:x} len 0x{:x} flags {} next {}", i, desc[i].addr, desc_addr, desc[i].len, desc[i].flags, desc[i].next ); } } + /// Displays information about the available ring in the VirtIO queue. pub fn show_avail_info(&self, size: usize) { let inner = self.inner.lock(); let avail = inner.avail.as_ref().unwrap(); - println!("[*avail_ring*]"); + info!("[*avail_ring*]"); for i in 0..size { - println!("index {} ring_idx {}", i, avail.ring[i]); + info!("index {} ring_idx {}", i, avail.ring[i]); } } + /// Displays information about the used ring in the VirtIO queue. pub fn show_used_info(&self, size: usize) { let inner = self.inner.lock(); let used = inner.used.as_ref().unwrap(); - println!("[*used_ring*]"); + info!("[*used_ring*]"); for i in 0..size { - println!( + info!( "index {} ring_id {} ring_len {:x}", i, used.ring[i].id, used.ring[i].len ); } } + /// Displays information about the guest-physical addresses of the VirtIO queue components. pub fn show_addr_info(&self) { let inner = self.inner.lock(); - println!( + info!( "avail_addr {:x}, desc_addr {:x}, used_addr {:x}", inner.avail_addr, inner.desc_table_addr, inner.used_addr ); } + /// Sets the last used index for the used ring. pub fn set_last_used_idx(&self, last_used_idx: u16) { let mut inner = self.inner.lock(); inner.last_used_idx = last_used_idx; } + /// Sets the number of descriptors in the VirtIO queue. pub fn set_num(&self, num: usize) { let mut inner = self.inner.lock(); inner.num = num; } + /// Sets the ready state of the VirtIO queue. pub fn set_ready(&self, ready: usize) { let mut inner = self.inner.lock(); inner.ready = ready; } + /// Combines the descriptor table address with the provided address using a bitwise OR operation. pub fn or_desc_table_addr(&self, addr: usize) { let mut inner = self.inner.lock(); inner.desc_table_addr |= addr; } + /// Combines the available ring address with the provided address using a bitwise OR operation. pub fn or_avail_addr(&self, addr: usize) { let mut inner = self.inner.lock(); inner.avail_addr |= addr; } + /// Combines the used ring address with the provided address using a bitwise OR operation. pub fn or_used_addr(&self, addr: usize) { let mut inner = self.inner.lock(); inner.used_addr |= addr; } - pub fn set_desc_table(&self, addr: usize) { + /// Sets the descriptor table for the VirtIO queue. + /// # Safety: + /// The 'desc_table_addr' must be valid MMIO address of virtio queue + /// And it must be in range of the vm memory + pub unsafe fn set_desc_table(&self, addr: usize) { let mut inner = self.inner.lock(); - if trace() && addr < 0x1000 { - panic!("illegal desc ring addr {:x}", addr); - } - inner.desc_table = Some(unsafe { slice::from_raw_parts_mut(addr as *mut VringDesc, DESC_QUEUE_SIZE) }); + inner.desc_table = Some(slice::from_raw_parts_mut(addr as *mut VringDesc, DESC_QUEUE_SIZE)); } - pub fn set_avail(&self, addr: usize) { - if trace() && addr < 0x1000 { - panic!("illegal avail ring addr {:x}", addr); - } + /// Sets the available ring for the VirtIO queue. + /// # Safety: + /// The 'avail_addr' must be valid MMIO address of virtio queue + /// And it must be in range of the vm memory + pub unsafe fn set_avail(&self, addr: usize) { let mut inner = self.inner.lock(); - inner.avail = Some(unsafe { &mut *(addr as *mut VringAvail) }); + inner.avail = Some(&mut *(addr as *mut VringAvail)); } - pub fn set_used(&self, addr: usize) { - if trace() && addr < 0x1000 { - panic!("illegal used ring addr {:x}", addr); - } + /// Sets the used ring for the VirtIO queue. + /// # Safety: + /// The 'used_addr' must be valid MMIO address of virtio queue + /// And it must be in range of the vm memory + pub unsafe fn set_used(&self, addr: usize) { let mut inner = self.inner.lock(); - inner.used = Some(unsafe { &mut *(addr as *mut VringUsed) }); + inner.used = Some(&mut *(addr as *mut VringUsed)); } + /// Returns the last used index for the used ring. pub fn last_used_idx(&self) -> u16 { let inner = self.inner.lock(); inner.last_used_idx } + /// Returns the descriptor table address for the VirtIO queue. pub fn desc_table_addr(&self) -> usize { let inner = self.inner.lock(); inner.desc_table_addr } + /// Returns the available ring address for the VirtIO queue. pub fn avail_addr(&self) -> usize { let inner = self.inner.lock(); inner.avail_addr } + /// Returns the used ring address for the VirtIO queue. pub fn used_addr(&self) -> usize { let inner = self.inner.lock(); inner.used_addr } + /// Returns a pointer to the descriptor table for the VirtIO queue. pub fn desc_table(&self) -> usize { let inner = self.inner.lock(); match &inner.desc_table { @@ -318,6 +387,7 @@ impl Virtq { } } + /// Returns the address of the available ring for the VirtIO queue. pub fn avail(&self) -> usize { let inner = self.inner.lock(); match &inner.avail { @@ -326,6 +396,7 @@ impl Virtq { } } + /// Returns the address of the used ring for the VirtIO queue. pub fn used(&self) -> usize { let inner = self.inner.lock(); match &inner.used { @@ -334,181 +405,114 @@ impl Virtq { } } + /// Returns the ready state of the VirtIO queue. pub fn ready(&self) -> usize { let inner = self.inner.lock(); inner.ready } + /// Returns the VirtIO queue index. pub fn vq_indx(&self) -> usize { let inner = self.inner.lock(); inner.vq_index } + /// Returns the number of descriptors in the VirtIO queue. pub fn num(&self) -> usize { let inner = self.inner.lock(); inner.num } + /// Returns the guest-physical address of the descriptor at the specified index. pub fn desc_addr(&self, idx: usize) -> usize { let inner = self.inner.lock(); let desc_table = inner.desc_table.as_ref().unwrap(); desc_table[idx].addr } + /// Returns the flags of the descriptor at the specified index. pub fn desc_flags(&self, idx: usize) -> u16 { let inner = self.inner.lock(); let desc_table = inner.desc_table.as_ref().unwrap(); desc_table[idx].flags } + /// Returns the 'next' field of the descriptor at the specified index. pub fn desc_next(&self, idx: usize) -> u16 { let inner = self.inner.lock(); let desc_table = inner.desc_table.as_ref().unwrap(); desc_table[idx].next } + /// Returns the length of the descriptor at the specified index. pub fn desc_len(&self, idx: usize) -> u32 { let inner = self.inner.lock(); let desc_table = inner.desc_table.as_ref().unwrap(); desc_table[idx].len } + /// Returns the flags of the available ring. pub fn avail_flags(&self) -> u16 { let inner = self.inner.lock(); let avail = inner.avail.as_ref().unwrap(); avail.flags } + /// Returns the index of the available ring. pub fn avail_idx(&self) -> u16 { let inner = self.inner.lock(); let avail = inner.avail.as_ref().unwrap(); avail.idx } + /// Returns the last available index in the VirtIO queue. pub fn last_avail_idx(&self) -> u16 { let inner = self.inner.lock(); inner.last_avail_idx } + /// Returns the index of the used ring. pub fn used_idx(&self) -> u16 { let inner = self.inner.lock(); let used = inner.used.as_ref().unwrap(); used.idx } - - // use for migration - pub fn restore_vq_data(&self, data: &VirtqData, pa_region: &Vec) { - let mut inner = self.inner.lock(); - inner.ready = data.ready; - inner.vq_index = data.vq_index; - inner.num = data.num; - inner.last_avail_idx = data.last_avail_idx; - inner.last_used_idx = data.last_used_idx; - inner.used_flags = data.used_flags; - inner.desc_table_addr = data.desc_table_ipa; - inner.avail_addr = data.avail_ipa; - inner.used_addr = data.used_ipa; - let desc_table_addr = ipa2pa(pa_region, data.desc_table_ipa); - let avail_addr = ipa2pa(pa_region, data.avail_ipa); - let used_addr = ipa2pa(pa_region, data.used_ipa); - // println!("restore_vq_data: ready {}, vq idx {}, last_avail_idx {}, last_used_idx {}, desc_table_ipa {:x}, avail_ipa {:x}, used_ipa {:x}, desc_table_pa {:x}, avail_pa {:x}, used_pa {:x}", - // data.ready, data.vq_index, data.last_avail_idx, data.last_used_idx, data.desc_table_ipa, data.avail_ipa, data.used_ipa, desc_table_addr, avail_addr, used_addr); - if desc_table_addr != 0 { - inner.desc_table = - Some(unsafe { slice::from_raw_parts_mut(desc_table_addr as *mut VringDesc, DESC_QUEUE_SIZE) }); - } - if avail_addr != 0 { - inner.avail = Some(unsafe { &mut *(avail_addr as *mut VringAvail) }); - // println!("restore_vq_data: avail idx {}", inner.avail.as_ref().unwrap().idx); - } - if used_addr != 0 { - inner.used = Some(unsafe { &mut *(used_addr as *mut VringUsed) }); - // println!("restore_vq_data: used idx {}", inner.used.as_ref().unwrap().idx); - } - } - - // use for migration - pub fn save_vq_data(&self, data: &mut VirtqData, _pa_region: &Vec) { - let inner = self.inner.lock(); - data.ready = inner.ready; - data.vq_index = inner.vq_index; - data.num = inner.num; - data.last_avail_idx = inner.last_avail_idx; - data.last_used_idx = inner.last_used_idx; - data.used_flags = inner.used_flags; - data.desc_table_ipa = inner.desc_table_addr; - data.avail_ipa = inner.avail_addr; - data.used_ipa = inner.used_addr; - - // println!("save_vq_data: ready {}, vq idx {}, last_avail_idx {}, last_used_idx {}, desc_table_ipa {:x}, avail_ipa {:x}, used_ipa {:x}", - // data.ready, data.vq_index, data.last_avail_idx, data.last_used_idx, data.desc_table_ipa, data.avail_ipa, data.used_ipa); - // if inner.avail.is_some() { - // println!("save_vq_data: avail idx {}", inner.avail.as_ref().unwrap().idx); - // } - // if inner.used.is_some() { - // println!("save_vq_data: used idx {}", inner.used.as_ref().unwrap().idx); - // } - } - - // use for live update - pub fn save_vq(&self, vq: Virtq, notify_handler: Option bool>) { - let mut dst_inner = self.inner.lock(); - let src_inner = vq.inner.lock(); - dst_inner.ready = src_inner.ready; - dst_inner.vq_index = src_inner.vq_index; - dst_inner.num = src_inner.num; - - dst_inner.desc_table = match &src_inner.desc_table { - None => None, - Some(desc_table) => { - let desc_addr = &desc_table[0] as *const _ as usize; - Some(unsafe { slice::from_raw_parts_mut(desc_addr as *mut VringDesc, DESC_QUEUE_SIZE) }) - } - }; - dst_inner.avail = match &src_inner.avail { - None => None, - Some(avail) => { - let avail_addr = *avail as *const _ as usize; - Some(unsafe { &mut *(avail_addr as *mut VringAvail) }) - } - }; - dst_inner.used = match &src_inner.used { - None => None, - Some(used) => { - let used_addr = *used as *const _ as usize; - Some(unsafe { &mut *(used_addr as *mut VringUsed) }) - } - }; - - dst_inner.last_avail_idx = src_inner.last_avail_idx; - dst_inner.last_used_idx = src_inner.last_used_idx; - dst_inner.used_flags = src_inner.used_flags; - dst_inner.desc_table_addr = src_inner.desc_table_addr; - dst_inner.avail_addr = src_inner.avail_addr; - dst_inner.used_addr = src_inner.used_addr; - dst_inner.notify_handler = notify_handler; - } } +/// Represents the inner state of a VirtIO queue. pub struct VirtqInner<'a> { + /// The ready state of the VirtIO queue. ready: usize, + /// The index of the VirtIO queue. vq_index: usize, + /// The number of descriptors in the VirtIO queue. num: usize, + /// Optional reference to the descriptor table. desc_table: Option<&'a mut [VringDesc]>, + /// Optional reference to the available ring. avail: Option<&'a mut VringAvail>, + /// Optional reference to the used ring. used: Option<&'a mut VringUsed>, + /// The last available index in the VirtIO queue. last_avail_idx: u16, + /// The last used index in the VirtIO queue. last_used_idx: u16, + /// Flags for the used ring. used_flags: u16, + /// Guest-physical address of the descriptor table. desc_table_addr: usize, + /// Guest-physical address of the available ring. avail_addr: usize, + /// Guest-physical address of the used ring. used_addr: usize, + /// Optional function to handle VirtIO queue notifications. notify_handler: Option bool>, } impl VirtqInner<'_> { + /// Creates a new default instance of `VirtqInner`. pub fn default() -> Self { VirtqInner { ready: 0, @@ -529,7 +533,7 @@ impl VirtqInner<'_> { } } - // virtio_queue_reset + /// Resets the VirtIO queue to its initial state. pub fn reset(&mut self, index: usize) { self.ready = 0; self.vq_index = index; diff --git a/src/driver/gpio/gpio.rs b/src/driver/gpio/gpio.rs index 662a6dd5eb134ebb2d66bab9eaa28faa6cced5b8..a6cb8427253533491cec9a0b7be56fa0fe9e4090 100644 --- a/src/driver/gpio/gpio.rs +++ b/src/driver/gpio/gpio.rs @@ -8,9 +8,12 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +/// Base address for memory-mapped I/O (MMIO). const MMIO_BASE: usize = 0xFE000000; +/// Offset for GPIO Function Select 0 register within MMIO. const GPFSEL0: usize = MMIO_BASE + 0x200000; +/// Converts GPIO alternate function number to corresponding bits. fn alt2bits(alt: u8) -> u8 { match alt { 0 => 0b100, @@ -23,21 +26,19 @@ fn alt2bits(alt: u8) -> u8 { } } +/// Selects the alternate function for a GPIO pin. #[no_mangle] #[inline(never)] pub fn gpio_select_function(gpio: u8, alt: u8) { let mut gpfsel; let field_offset; - match gpio { - 0..=9 => { - gpfsel = unsafe { *(GPFSEL0 as *const u32) }; - field_offset = (gpio as u32) % 10 * 3; - gpfsel &= !(1 << field_offset); - gpfsel &= !(1 << (field_offset + 1)); - gpfsel &= !(1 << (field_offset + 2)); - gpfsel |= (alt2bits(alt) as u32) << field_offset; - unsafe { (GPFSEL0 as *mut u32).write_volatile(gpfsel) }; - } - _ => {} + if gpio <= 9 { + gpfsel = unsafe { *(GPFSEL0 as *const u32) }; + field_offset = (gpio as u32) % 10 * 3; + gpfsel &= !(1 << field_offset); + gpfsel &= !(1 << (field_offset + 1)); + gpfsel &= !(1 << (field_offset + 2)); + gpfsel |= (alt2bits(alt) as u32) << field_offset; + unsafe { (GPFSEL0 as *mut u32).write_volatile(gpfsel) }; } } diff --git a/src/driver/gpio/mod.rs b/src/driver/gpio/mod.rs index e958428d546f801c6fc174514a042ff53eb11dac..6f36ee1e4bf8e05b642775f2e7c3b64a739edb1f 100644 --- a/src/driver/gpio/mod.rs +++ b/src/driver/gpio/mod.rs @@ -8,6 +8,8 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(feature = "pi4")] pub use self::gpio::*; +#[cfg(feature = "pi4")] mod gpio; diff --git a/src/driver/mod.rs b/src/driver/mod.rs index e4c59a0d5a5d1505a328ae17c3237d78a278bc52..697ae97a1caba743f6347e2ee8005b0ec9127dd7 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -8,12 +8,12 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub use self::aarch64::*; +//! Driver module, including device drivers and board drivers. + +pub use uart::putc; + +#[cfg(feature = "pi4")] pub use self::gpio::*; -#[cfg(feature = "qemu")] -pub use self::virtio::*; -mod aarch64; mod gpio; -#[cfg(feature = "qemu")] -mod virtio; +mod uart; diff --git a/src/driver/aarch64/uart.rs b/src/driver/uart/mod.rs similarity index 38% rename from src/driver/aarch64/uart.rs rename to src/driver/uart/mod.rs index 8e4ff457cf1d8fc2a0db6b3ec7fa569f4e9f4e64..13fe4984639d0625c72d20cfa44b7f490ed9929e 100644 --- a/src/driver/aarch64/uart.rs +++ b/src/driver/uart/mod.rs @@ -8,28 +8,45 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use core::ptr; +#[cfg(feature = "ns16550")] +mod ns16550; +#[cfg(feature = "pl011")] +#[allow(dead_code)] +mod pl011; -use crate::board::{Platform, PlatOperation}; +#[cfg(feature = "ns16550")] +use ns16550::Ns16550Mmio32 as Uart; +#[cfg(feature = "pl011")] +use pl011::Pl011Mmio as Uart; +/// A trait defining operations for a UART device. +trait UartOperation { + /// Initializes the UART device. + fn init(&self); + /// Sends a byte through the UART device. + fn send(&self, byte: u8); +} + +use crate::{ + board::{PlatOperation, Platform}, + utils::device_ref::DeviceRef, +}; + +/// The base address for the UART device. +const UART_BASE: usize = Platform::HYPERVISOR_UART_BASE; + +// SAFETY: The reference of unart is a MMIO address +const UART: DeviceRef = unsafe { DeviceRef::new(UART_BASE as *const Uart) }; + +/// Sends a byte to the UART device, replacing '\n' with '\r\n'. pub fn putc(byte: u8) { - const UART_BASE: usize = Platform::HYPERVISOR_UART_BASE + 0x8_0000_0000; - // ns16550 - #[cfg(feature = "tx2")] - unsafe { - if byte == '\n' as u8 { - putc('\r' as u8); - } - while ptr::read_volatile((UART_BASE + 20) as *const u8) & 0x20 == 0 {} - ptr::write_volatile(UART_BASE as *mut u8, byte); - } - // pl011 - #[cfg(any(feature = "pi4", feature = "qemu"))] - unsafe { - if byte == '\n' as u8 { - putc('\r' as u8); - } - while (ptr::read_volatile((UART_BASE as usize + 24) as *const u32) & (1 << 5)) != 0 {} - ptr::write_volatile(UART_BASE as *mut u32, byte as u32); + if byte == b'\n' { + putc(b'\r'); } + UART.send(byte); +} + +/// Initializes the UART device. +pub(super) fn init() { + UART.init(); } diff --git a/src/driver/uart/ns16550.rs b/src/driver/uart/ns16550.rs new file mode 100644 index 0000000000000000000000000000000000000000..7e6b68ce543500111041e068bf22150c0a0dab61 --- /dev/null +++ b/src/driver/uart/ns16550.rs @@ -0,0 +1,317 @@ +// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. +// Rust-Shyper is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use tock_registers::interfaces::*; +use tock_registers::register_bitfields; +use tock_registers::register_structs; +use tock_registers::registers::*; + +/// Register bitfields for an 8-bit unsigned integer. +register_bitfields! { + u8, + + /// Bitfields of the `RHR_THR_DLL` register. + pub RHR_THR_DLL [ + /// The Transmit Holding Register. + /// + /// It holds the characters to be transmitted by the UART. + /// In FIFO mode, a write to this FIFO places the data at + /// the end of the FIFO. + /// + /// NOTE: These bits are write-only. + THR OFFSET(0) NUMBITS(8) [], + + /// The Receive Buffer Register. + /// + /// Rx data can be read from here. + /// + /// NOTE: These bits are read-only. + RHR OFFSET(0) NUMBITS(8) [], + + /// The Divisor Latch LSB register. + /// + /// The value is represented by the low 8 bits of the 16-bit + /// Baud Divisor. + /// + /// NOTE: These bits are read-only. + DLL_LSB OFFSET(0) NUMBITS(8) [] + ], + + /// Bitfields of the `IER_DLM` register. + pub IER_DLM [ + /// Interrupt Enable for End of Received Data. + IE_EORD OFFSET(5) NUMBITS(1) [], + + /// Interrupt Enable for Rx FIFO timeout. + IE_RX_TIMEOUT OFFSET(4) NUMBITS(1) [], + + /// Interrupt Enable for Modem Status Interrupt. + IE_MSI OFFSET(3) NUMBITS(1) [], + + /// Interrupt Enable for Receiver Line Status Interrupt. + IE_RXS OFFSET(2) NUMBITS(1) [], + + /// Interrupt Enable for Transmitter Holding Register Empty Interrupt. + IE_THR OFFSET(1) NUMBITS(1) [], + + /// Interrupt Enable for Receive Data Interrupt. + IE_RHR OFFSET(0) NUMBITS(1) [] + ], + + /// Bitfields of the `ISR_FCR` register. + pub ISR_FCR [ + /// FIFO Mode Status. + EN_FIFO OFFSET(6) NUMBITS(2) [ + /// 16450 Mode. + /// + /// This mode disables FIFOs. + Mode16450 = 0, + /// 16550 Mode. + /// + /// This mode enables FIFOs. + Mode16550 = 1 + ], + + RX_TRIG OFFSET(6) NUMBITS(2) [ + FifoCountGreater1 = 0, + FifoCountGreater4 = 1, + FifoCountGreater8 = 2, + FifoCountGreater16 = 3 + ], + + TX_TRIG OFFSET(4) NUMBITS(2) [ + FifoCountGreater16 = 0, + FifoCountGreater8 = 1, + FifoCountGreater4 = 2, + FifoCountGreater1 = 3 + ], + + /// Whether Encoded Interrupt IDs should be enabled or not. + IS_PRI2 OFFSET(3) NUMBITS(1) [], + + /// The DMA mode to use. + DMA OFFSET(3) NUMBITS(1) [ + /// DMA Mode 0. + /// + /// This is the default mode. + DmaMode0 = 0, + /// DMA Mode 1. + DmaMode1 = 1 + ], + + /// Whether Encoded Interrupt IDs should be enabled or not. + IS_PRI1 OFFSET(2) NUMBITS(1) [], + + /// Clears the contents of the transmit FIFO and resets its counter logic to 0. + TX_CLR OFFSET(2) NUMBITS(1) [ + /// Indicates that the FIFOs were cleared. + NoClear = 0, + /// Indicates that the FIFOs should be cleared or are being cleared right now. + Clear = 1 + ], + + /// Whether Encoded Interrupt IDs should be enabled or not. + IS_PRI0 OFFSET(1) NUMBITS(1) [], + + /// Clears the contents of the receive FIFO and resets the counter logic to 0. + RX_CLR OFFSET(1) NUMBITS(1) [ + /// Indicates that the FIFOs were cleared. + NoClear = 0, + /// Indicates that the FIFOs should be cleared or are being cleared right now. + Clear = 1 + ], + + /// Checks whether an interrupt is pending. + IS_STA OFFSET(0) NUMBITS(1) [ + /// An interrupt is pending. + IntrPend = 0, + /// No interrupt is pending. + NoIntrPend = 1 + ], + + /// Enables the transmit and receive FIFOs. + /// + /// This bit should always be enabled. + FCR_EN_FIFO OFFSET(0) NUMBITS(1) [] + ], + + /// Bitfields of the `LCR` register. + pub LCR [ + /// Whether the Divisor Latch Access Bit should be enabled. + /// + /// NOTE: Set this bit to allow programming of the DLH and DLM Divisors. + DLAB OFFSET(7) NUMBITS(1) [], + + /// Whether a BREAK condition should be set. + /// + /// NOTE: The transmitter sends all zeroes to indicate a BREAK. + SET_B OFFSET(6) NUMBITS(1) [], + + /// Whether parity should be set (forced) to the value in LCR. + SET_P OFFSET(5) NUMBITS(1) [], + + /// Whether the even parity format should be used for number representation. + /// + /// NOTE: There will always be an even number of 1s in the binary representation. + EVEN OFFSET(4) NUMBITS(1) [], + + /// Whether parity should be sent or not. + PAR OFFSET(3) NUMBITS(1) [], + + /// Whether 2 stop bits should be transmitted instead of 1. + /// + /// NOTE: The receiver always checks for 1 stop bit. + STOP OFFSET(2) NUMBITS(1) [], + + /// The Word Length size. + WD_SIZE OFFSET(0) NUMBITS(2) [ + /// Word length of 5. + WordLength5 = 0, + /// Word length of 6. + WordLength6 = 1, + /// Word length of 7. + WordLength7 = 2, + /// Word length of 8. + WordLength8 = 3 + ] + ], + + /// Bitfields of the `MCR` register. + pub MCR [ + /// Whether the old qualified CTS in TX state machine should be used. + DEL_QUAL_CTS_EN OFFSET(7) NUMBITS(1) [], + + /// Whether RTS Hardware Flow Control should be enabled. + RTS_EN OFFSET(6) NUMBITS(1) [], + + /// Whether CTS Hardware Flow Control should be enabled. + CTS_EN OFFSET(5) NUMBITS(1) [], + + /// Whether internal loop back of Serial Out to In should be enabled. + LOOPBK OFFSET(4) NUMBITS(1) [], + + /// nOUT2 (Not Used). + OUT2 OFFSET(3) NUMBITS(1) [], + + /// nOUT1 (Not Used). + OUT1 OFFSET(2) NUMBITS(1) [], + + /// Whether RTS should be forced to high if RTS hardware flow control wasn't enabled. + RTS OFFSET(1) NUMBITS(1) [], + + /// Whether DTR should be forced to high or not. + DTR OFFSET(0) NUMBITS(1) [] + ], + + /// Bitfields of the `LSR` register. + pub LSR [ + /// Denotes a Receive FIFO error, if set to 1. + FIFOE OFFSET(7) NUMBITS(1) [], + + /// Denotes a Transmit Shift Register empty status, if set to 1. + TMTY OFFSET(6) NUMBITS(1) [], + + /// Denotes that the Transmit Holding Register is empty, if set to 1. + /// + /// This means that data can be written. + THRE OFFSET(5) NUMBITS(1) [], + + /// Denotes that a BREAK condition was detected on the line, if set to 1. + BRK OFFSET(4) NUMBITS(1) [], + + /// Denotes a Framing Error, if set to 1. + FERR OFFSET(3) NUMBITS(1) [], + + /// Denotes a Parity Error, if set to 1. + PERR OFFSET(2) NUMBITS(1) [], + + /// Denotes a Receiver Overrun Error, if set to 1. + OVRF OFFSET(1) NUMBITS(1) [], + + /// Denotes that Receiver Data are in FIFO, if set to 1. + /// + /// This means that data are available to read. + RDR OFFSET(0) NUMBITS(1) [] + ], + + /// Bitfields of the `MSR` register. + pub MSR [ + /// State of Carrier detect pin. + CD OFFSET(7) NUMBITS(1) [], + + /// State of Ring Indicator pin. + RI OFFSET(6) NUMBITS(1) [], + + /// State of Data set ready pin. + DSR OFFSET(5) NUMBITS(1) [], + + /// State of Clear to send pin. + CTS OFFSET(4) NUMBITS(1) [], + + /// Change (Delta) in CD state detected. + DCD OFFSET(3) NUMBITS(1) [], + + /// Change (Delta) in RI state detected. + DRI OFFSET(2) NUMBITS(1) [], + + /// Change (Delta) in DSR state detected. + DDSR OFFSET(1) NUMBITS(1) [], + + /// Change (Delta) in CTS detected. + DCTS OFFSET(0) NUMBITS(1) [] + ], + + /// Bitfields of the `SPR` register. + pub SPR [ + /// Scratchpad register (not used internally). + SPR_A OFFSET(0) NUMBITS(8) [] + ], +} + +/// Register struct representing the UART registers. +register_structs! { + /// Representation of the UART registers. + #[allow(non_snake_case)] + pub Ns16550Mmio32 { + (0x00 => pub RHR_THR_DLL: ReadWrite), + (0x01 => _reserved_0), + (0x04 => pub IER_DLM: ReadWrite), + (0x05 => _reserved_1), + (0x08 => pub ISR_FCR: ReadWrite), + (0x09 => _reserved_2), + (0x0c => pub LCR: ReadWrite), + (0x0d => _reserved_3), + (0x10 => pub MCR: ReadWrite), + (0x11 => _reserved_4), + (0x14 => pub LSR: ReadOnly), + (0x15 => _reserved_5), + (0x18 => pub MSR: ReadWrite), + (0x19 => _reserved_6), + (0x1c => pub SPR: ReadWrite), + (0x1d => _reserved_7), + (0x20 => @END), + } +} + +impl super::UartOperation for Ns16550Mmio32 { + #[inline] + fn init(&self) { + self.ISR_FCR.write(ISR_FCR::EN_FIFO::Mode16550); + } + + /// Sends a byte through the UART device. + #[inline] + fn send(&self, byte: u8) { + while !self.LSR.is_set(LSR::THRE) { + core::hint::spin_loop(); + } + self.RHR_THR_DLL.set(byte); + } +} diff --git a/src/driver/uart/pl011.rs b/src/driver/uart/pl011.rs new file mode 100644 index 0000000000000000000000000000000000000000..eda287ecb6bff97bafa26e3b9b20375dbdae2174 --- /dev/null +++ b/src/driver/uart/pl011.rs @@ -0,0 +1,57 @@ +// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. +// Rust-Shyper is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use tock_registers::interfaces::*; +use tock_registers::register_structs; +use tock_registers::registers::*; + +/// Flag indicating that the receive FIFO is full. +const UART_FR_RXFF: u32 = 1 << 4; +/// Flag indicating that the transmit FIFO is full. +const UART_FR_TXFF: u32 = 1 << 5; + +/// Register struct representing the PL011 MMIO block. +register_structs! { + #[allow(non_snake_case)] + pub Pl011Mmio { + (0x000 => pub Data: ReadWrite), + (0x004 => pub RecvStatusErrClr: ReadWrite), + (0x008 => _reserved_1), + (0x018 => pub Flag: ReadOnly), + (0x01c => _reserved_2), + (0x020 => pub IrDALowPower: ReadWrite), + (0x024 => pub IntBaudRate: ReadWrite), + (0x028 => pub FracBaudRate: ReadWrite), + (0x02c => pub LineControl: ReadWrite), + (0x030 => pub Control: ReadWrite), + (0x034 => pub IntFIFOLevel: ReadWrite), + (0x038 => pub IntMaskSetClr: ReadWrite), + (0x03c => pub RawIntStatus: ReadOnly), + (0x040 => pub MaskedIntStatus: ReadOnly), + (0x044 => pub IntClear: WriteOnly), + (0x048 => pub DmaControl: ReadWrite), + (0x04c => _reserved_3), + (0x1000 => @END), + } +} + +impl super::UartOperation for Pl011Mmio { + #[inline] + fn init(&self) {} + + /// Sends a byte through the UART device. + #[inline] + fn send(&self, byte: u8) { + while self.Flag.get() & UART_FR_TXFF != 0 { + core::hint::spin_loop(); + } + self.Data.set(byte as u32); + } +} diff --git a/src/driver/virtio/blk.rs b/src/driver/virtio/blk.rs deleted file mode 100644 index 1ec4ad970c5a8a8ec3c9290d3e7c7fd70a58000b..0000000000000000000000000000000000000000 --- a/src/driver/virtio/blk.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. -// Rust-Shyper is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -// Feature bits -pub const VIRTIO_BLK_F_SIZE_MAX: usize = 1; -pub const VIRTIO_BLK_F_SEG_MAX: usize = 2; -pub const VIRTIO_BLK_F_GEOMETRY: usize = 4; -pub const VIRTIO_BLK_F_RO: usize = 5; -pub const VIRTIO_BLK_F_BLK_SIZE: usize = 6; -pub const VIRTIO_BLK_F_TOPOLOGY: usize = 10; -pub const VIRTIO_BLK_F_MQ: usize = 12; - -// Legacy feature bits -pub const VIRTIO_BLK_F_BARRIER: usize = 0; -pub const VIRTIO_BLK_F_SCSI: usize = 7; -pub const VIRTIO_BLK_F_FLUSH: usize = 9; -pub const VIRTIO_BLK_F_CONFIG_WCE: usize = 11; diff --git a/src/driver/virtio/mmio.rs b/src/driver/virtio/mmio.rs deleted file mode 100644 index ab894fcea0ecd3a74c6fb9fa0b574af7fa0c0e69..0000000000000000000000000000000000000000 --- a/src/driver/virtio/mmio.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. -// Rust-Shyper is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -pub const VIRTIO_MMIO_MAGIC_VALUE: usize = 0x000; -pub const VIRTIO_MMIO_VERSION: usize = 0x004; -pub const VIRTIO_MMIO_DEVICE_ID: usize = 0x008; -pub const VIRTIO_MMIO_VENDOR_ID: usize = 0x00c; -pub const VIRTIO_MMIO_DEVICE_FEATURES: usize = 0x010; - -pub const VIRTIO_MMIO_DRIVER_FEATURES: usize = 0x020; - -pub const VIRTIO_MMIO_GUEST_PAGE_SIZE: usize = 0x028; - -pub const VIRTIO_MMIO_QUEUE_SEL: usize = 0x030; -pub const VIRTIO_MMIO_QUEUE_NUM_MAX: usize = 0x034; -pub const VIRTIO_MMIO_QUEUE_NUM: usize = 0x038; - -pub const VIRTIO_MMIO_QUEUE_PIN: usize = 0x040; - -pub const VIRTIO_MMIO_STATUS: usize = 0x070; diff --git a/src/driver/virtio/virtio.rs b/src/driver/virtio/virtio.rs deleted file mode 100644 index f5601b5945a193f5ed5efd35b6dff319f55b1fef..0000000000000000000000000000000000000000 --- a/src/driver/virtio/virtio.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. -// Rust-Shyper is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -pub const VIRTIO_CONFIG_S_ACKNOWLEDGE: usize = 1; -pub const VIRTIO_CONFIG_S_DRIVER: usize = 2; -pub const VIRTIO_CONFIG_S_DRIVER_OK: usize = 4; -pub const VIRTIO_CONFIG_S_FEATURES_OK: usize = 8; -pub const VIRTIO_CONFIG_S_NEEDS_RESET: usize = 0x40; -pub const VIRTIO_CONFIG_S_FAILED: usize = 0x80; - -pub const VIRTIO_F_ANY_LAYOUT: usize = 27; diff --git a/src/driver/virtio/virtio_blk.rs b/src/driver/virtio/virtio_blk.rs deleted file mode 100644 index ad906a62e26212d0c7ecf8838148baac2e774bb6..0000000000000000000000000000000000000000 --- a/src/driver/virtio/virtio_blk.rs +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. -// Rust-Shyper is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use alloc::boxed::Box; -use core::arch::asm; -use core::mem::size_of; - -// use register::mmio::*; -// use register::*; -use spin::Mutex; -use tock_registers::*; -use tock_registers::interfaces::*; -use tock_registers::registers::*; - -use Operation::*; - -use crate::lib::trace; - -// use crate::device::*; -use super::virtio::*; - -const VIRTIO_MMIO_BASE: usize = 0x0a000000; -const QUEUE_SIZE: usize = 8; -const VIRTIO_F_VERSION_1: u32 = 32; - -#[repr(C)] -#[repr(align(4096))] -#[derive(Debug)] -struct VirtioRing { - desc: [VirtioRingDesc; QUEUE_SIZE], - driver: VirtioRingDriver, - device: VirtioRingDevice, -} - -static VIRTIO_RING: Mutex = Mutex::new(VirtioRing { - desc: [VirtioRingDesc { - addr: 0, - len: 0, - flags: 0, - next: 0, - }; QUEUE_SIZE], - driver: VirtioRingDriver { - flags: 0, - idx: 0, - ring: [0; QUEUE_SIZE], - }, - device: VirtioRingDevice { - flags: 0, - idx: 0, - ring: [VirtioRingDeviceElement { id: 0, len: 0 }; QUEUE_SIZE], - }, -}); - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -struct VirtioRingDesc { - addr: u64, - len: u32, - flags: u16, - next: u16, -} - -#[repr(C)] -#[derive(Debug)] -struct VirtioRingDriver { - flags: u16, - idx: u16, - ring: [u16; QUEUE_SIZE], -} - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -struct VirtioRingDeviceElement { - id: u32, - len: u32, -} - -#[repr(C)] -#[repr(align(4096))] -#[derive(Debug)] -struct VirtioRingDevice { - flags: u16, - idx: u16, - ring: [VirtioRingDeviceElement; QUEUE_SIZE], -} - -register_structs! { - #[allow(non_snake_case)] - VirtioMmioBlock { - (0x000 => MagicValue: ReadOnly), - (0x004 => Version: ReadOnly), - (0x008 => DeviceID: ReadOnly), - (0x00c => VendorID: ReadOnly), - (0x010 => DeviceFeatures: ReadOnly), - (0x014 => DeviceFeaturesSel: WriteOnly), - (0x018 => _reserved_0), - (0x020 => DriverFeatures: WriteOnly), - (0x024 => DriverFeaturesSel: WriteOnly), - (0x028 => _reserved_1), - (0x030 => QueueSel: WriteOnly), - (0x034 => QueueNumMax: ReadOnly), - (0x038 => QueueNum: WriteOnly), - (0x03c => _reserved_2), - (0x044 => QueueReady: ReadWrite), - (0x048 => _reserved_3), - (0x050 => QueueNotify: WriteOnly), - (0x054 => _reserved_4), - (0x060 => InterruptStatus: ReadOnly), - (0x064 => InterruptACK: WriteOnly), - (0x068 => _reserved_5), - (0x070 => Status: ReadWrite), - (0x074 => _reserved_6), - (0x080 => QueueDescLow: WriteOnly), - (0x084 => QueueDescHigh: WriteOnly), - (0x088 => _reserved_7), - (0x090 => QueueDriverLow: WriteOnly), - (0x094 => QueueDriverHigh: WriteOnly), - (0x098 => _reserved_8), - (0x0a0 => QueueDeviceLow: WriteOnly), - (0x0a4 => QueueDeviceHigh: WriteOnly), - (0x0a8 => _reserved_9), - (0x0fc => ConfigGeneration: ReadOnly), - (0x0fd => _reserved_10), - (0x100 => _reserved_config), - (0x200 => @END), - } -} - -struct VirtioMmio { - base_addr: usize, -} - -impl core::ops::Deref for VirtioMmio { - type Target = VirtioMmioBlock; - fn deref(&self) -> &Self::Target { - if trace() && self.base_addr < 0x1000 { - panic!("illegal addr {:x}", self.base_addr); - } - unsafe { &*self.ptr() } - } -} - -impl VirtioMmio { - const fn new(base_addr: usize) -> Self { - VirtioMmio { base_addr } - } - fn ptr(&self) -> *const VirtioMmioBlock { - self.base_addr as *const _ - } -} - -trait BaseAddr { - fn base_addr_u64(&self) -> u64; - fn base_addr_usize(&self) -> usize; -} - -impl BaseAddr for T { - fn base_addr_u64(&self) -> u64 { - self as *const T as u64 - } - fn base_addr_usize(&self) -> usize { - self as *const T as usize - } -} - -static VIRTIO_MMIO: VirtioMmio = VirtioMmio::new(VIRTIO_MMIO_BASE); - -fn virtio_mmio_setup_vq(index: usize) { - let mmio = &VIRTIO_MMIO; - mmio.QueueSel.set(index as u32); - - let num = mmio.QueueNumMax.get(); - if num == 0 { - panic!("queue num max is zero"); - } else if num < QUEUE_SIZE as u32 { - panic!("queue size not supported"); - } - mmio.QueueNum.set(QUEUE_SIZE as u32); - - let ring = VIRTIO_RING.lock(); - - mmio.QueueDescLow.set(ring.desc.base_addr_usize() as u32); - mmio.QueueDescHigh.set((ring.desc.base_addr_usize() >> 32) as u32); - mmio.QueueDriverLow.set(ring.driver.base_addr_usize() as u32); - mmio.QueueDriverHigh.set((ring.driver.base_addr_usize() >> 32) as u32); - mmio.QueueDeviceLow.set(ring.device.base_addr_usize() as u32); - mmio.QueueDeviceHigh.set((ring.device.base_addr_usize() >> 32) as u32); - - mmio.QueueReady.set(1); -} - -pub fn virtio_blk_init() { - let mmio = &VIRTIO_MMIO; - if mmio.MagicValue.get() != 0x74726976 - || mmio.Version.get() != 2 - || mmio.DeviceID.get() != 2 - || mmio.VendorID.get() != 0x554d4551 - { - // println!("mmio.MagicValue {:x}", mmio.MagicValue.get()); - // println!("mmio.Version {:x}", mmio.Version.get()); - // println!("mmio.DeviceID {:x}", mmio.DeviceID.get()); - // println!("mmio.VendorID {:x}", mmio.VendorID.get()); - panic!("could not find virtio blk") - } - - let mut status = VIRTIO_CONFIG_S_ACKNOWLEDGE as u32; - mmio.Status.set(status); - status |= VIRTIO_CONFIG_S_DRIVER as u32; - mmio.Status.set(status); - - let feature: u64 = 1 << VIRTIO_F_VERSION_1; - - mmio.DriverFeaturesSel.set(0); - mmio.DriverFeatures.set(feature as u32); - mmio.DriverFeaturesSel.set(1); - mmio.DriverFeatures.set((feature >> 32) as u32); - - status |= VIRTIO_CONFIG_S_FEATURES_OK as u32; - mmio.Status.set(status); - - status |= VIRTIO_CONFIG_S_DRIVER_OK as u32; - mmio.Status.set(status); - - virtio_mmio_setup_vq(0); -} - -pub enum Operation { - Read, - Write, -} - -#[repr(C)] -pub struct VirtioBlkOutHdr { - t: u32, - priority: u32, - sector: u64, -} - -const VRING_DESC_F_NEXT: u16 = 1; -const VRING_DESC_F_WRITE: u16 = 2; -const VRING_DESC_F_INDIRECT: u16 = 4; - -pub fn read(sector: usize, count: usize, buf: usize) { - io(sector, count, buf, Read); -} - -pub fn write(sector: usize, count: usize, buf: usize) /* -> Box*/ -{ - io(sector, count, buf, Write); -} - -fn io(sector: usize, count: usize, buf: usize, op: Operation) { - let hdr = Box::new(VirtioBlkOutHdr { - t: match op { - Operation::Read => 0, - Operation::Write => 1, - }, - priority: 0, - sector: sector as u64, - // status: 255, - }); - - let status = Box::new(255u8); - let mut ring = VIRTIO_RING.lock(); - - let desc = ring.desc.get_mut(0).unwrap(); - desc.addr = hdr.as_ref() as *const VirtioBlkOutHdr as u64; - desc.len = size_of::() as u32; - desc.flags = VRING_DESC_F_NEXT; - desc.next = 1; - - let desc = ring.desc.get_mut(1).unwrap(); - desc.addr = buf as u64; - desc.len = (512 * count) as u32; - desc.flags = match op { - Operation::Read => VRING_DESC_F_WRITE, - Operation::Write => 0, - }; - desc.flags |= VRING_DESC_F_NEXT; - desc.next = 2; - - let desc = ring.desc.get_mut(2).unwrap(); - desc.addr = status.as_ref() as *const u8 as u64; - desc.len = 1; - desc.flags = VRING_DESC_F_WRITE; - desc.next = 0; - - // avail[0] is flags - // avail[1] tells the device how far to look in avail[2...]. - // avail[2...] are desc[] indices the device should process. - // we only tell device the first index in our chain of descriptors. - let avail = &mut ring.driver; - avail.ring[(avail.idx as usize) % QUEUE_SIZE] = 0; - // barrier - unsafe { - asm!("dsb sy"); - } - avail.idx = avail.idx.wrapping_add(1); - - let mmio = &VIRTIO_MMIO; - - mmio.QueueNotify.set(0); // queue num - - loop { - // barrier - unsafe { - asm!("dsb sy"); - } - if *status == 0 { - return; - } else if *status == 1 { - panic!("VIRTIO_BLK_S_IOERR"); - } else if *status == 2 { - panic!("VIRTIO_BLK_S_UNSUPP"); - } else if *status == 255 { - continue; - } - // if mmio.InterruptStatus.get() == 1 { - // mmio.InterruptACK.set(1); - // break; - // } - } -} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..50884175992641fea2dd23ecbf490b7585d23796 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,73 @@ +//! Defines the error type for the hypervisor. + +use core::fmt; +use core::error::Error as CoreError; +use core::result::Result as CoreResult; +use alloc::boxed::Box; + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum ErrorKind { + Library, + InvalidParam, + NotFound, + AlreadyExists, +} + +type DynError = dyn CoreError + Send + Sync; + +#[derive(Debug)] +pub struct Error { + kind: ErrorKind, + inner: Option>, +} + +pub type Result = CoreResult; + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> CoreResult<(), fmt::Error> { + fmt::Debug::fmt(self, f) + } +} + +#[allow(dead_code)] +impl Error { + pub fn new(kind: ErrorKind, inner: Box) -> Self { + Self { + kind, + inner: Some(inner), + } + } + + pub fn kind(&self) -> ErrorKind { + self.kind + } + + pub fn into_inner(self) -> Option> { + self.inner + } +} + +impl ErrorKind { + pub fn wrap(self, inner: Box) -> Error { + Error::new(self, inner) + } +} + +impl From for Error { + fn from(kind: ErrorKind) -> Self { + Self { kind, inner: None } + } +} + +impl From for Result { + fn from(val: ErrorKind) -> Self { + Err(val.into()) + } +} + +impl From for Error { + fn from(e: T) -> Self { + ErrorKind::Library.wrap(Box::new(e)) + } +} diff --git a/src/kernel/async_task.rs b/src/kernel/async_task.rs index cdc698cd2d92e65f22f4b6efab98c09496b379e1..09a2213e865175a247984d6f839852cef14b4f9c 100644 --- a/src/kernel/async_task.rs +++ b/src/kernel/async_task.rs @@ -24,33 +24,7 @@ use crate::device::{ VirtioMmio, Virtq, }; use crate::kernel::{active_vm_id, ipi_send_msg, IpiInnerMsg, IpiMediatedMsg, IpiType, vm}; -use crate::lib::{memcpy_safe, sleep, trace}; - -pub static TASK_IPI_COUNT: Mutex = Mutex::new(0); -pub static TASK_COUNT: Mutex = Mutex::new(0); - -pub fn add_task_ipi_count() { - let mut count = TASK_IPI_COUNT.lock(); - *count += 1; -} - -pub fn add_task_count() { - let mut count = TASK_COUNT.lock(); - if *count % 100 == 0 { - println!("task count {}, ipi count {}", *count, get_task_ipi_count()); - } - *count += 1; -} - -pub fn get_task_ipi_count() -> usize { - let count = TASK_IPI_COUNT.lock(); - *count -} - -pub fn get_task_count() -> usize { - let count = TASK_COUNT.lock(); - *count -} +use crate::utils::{memcpy, sleep, trace}; #[derive(Clone, Copy, Debug)] pub enum AsyncTaskState { @@ -94,6 +68,7 @@ pub static ASYNC_IPI_TASK_LIST: Mutex> = Mutex::new(Linked pub static ASYNC_IO_TASK_LIST: Mutex> = Mutex::new(FairQueue::new()); pub static ASYNC_USED_INFO_LIST: Mutex>> = Mutex::new(BTreeMap::new()); +/// trait for determining the owner of a task pub trait TaskOwner { fn owner(&self) -> usize; } @@ -104,9 +79,13 @@ pub trait TaskOwner { // queue: LinkedList>>>, // } +/// Fair Queue, a queue of tasks which can serve tasks at a fair manner pub struct FairQueue { + /// number of tasks in the queue len: usize, + /// a map from owner to a sub queue of tasks map: BTreeMap>, + /// a queue of owners, which is used to determine the order of serving tasks queue: LinkedList, } @@ -119,14 +98,17 @@ impl FairQueue { } } + /// check if the queue is empty pub fn is_empty(&self) -> bool { self.map.is_empty() } + /// get the length of the queue (i.e. number of tasks) pub fn len(&self) -> usize { self.len } + /// push a task to the back of the queue, and insert the belonging owner to the back of the queue pub fn push_back(&mut self, task: T) { let key = task.owner(); match self.map.get_mut(&key) { @@ -141,6 +123,7 @@ impl FairQueue { self.len += 1; } + /// pop the front task, and move the belonging owner to the back of the queue pub fn pop_front(&mut self) -> Option { match self.queue.pop_front() { Some(owner) => match self.map.get_mut(&owner) { @@ -160,6 +143,7 @@ impl FairQueue { } } + /// get the front task, but not pop it pub fn front(&self) -> Option<&T> { match self.queue.front() { Some(owner) => match self.map.get(owner) { @@ -170,13 +154,11 @@ impl FairQueue { } } + /// remove the owner and all its tasks pub fn remove(&mut self, owner: usize) { - match self.map.remove(&owner) { - Some(sub_queue) => { - self.len -= sub_queue.len(); - self.queue.drain_filter(|x| *x == owner); - } - None => {} + if let Some(sub_queue) = self.map.remove(&owner) { + self.len -= sub_queue.len(); + self.queue = self.queue.extract_if(|x| *x == owner).collect(); } } } @@ -189,6 +171,7 @@ impl Iterator for FairQueue { } #[derive(Clone)] +/// data of an async task pub enum AsyncTaskData { AsyncIpiTask(IpiMediatedMsg), AsyncIoTask(IoAsyncMsg), @@ -204,9 +187,12 @@ fn set_async_exe_status(status: AsyncExeStatus) { } #[derive(Clone)] +/// an struct to describe an async task pub struct AsyncTask { pub task_data: AsyncTaskData, + /// the owner of the task pub src_vmid: usize, + /// the state of the task pub state: Arc>, pub task: Arc + 'static + Send + Sync>>>>, } @@ -217,6 +203,7 @@ impl TaskOwner for AsyncTask { } } +/// implement wake trait for AsyncTask, manages things that need to be done when a task is waken impl Wake for AsyncTask { fn wake(self: Arc) { todo!() @@ -237,6 +224,7 @@ impl AsyncTask { } } + /// handle a task, and return true if the task is finished pub fn handle(&mut self) -> bool { let mut state = self.state.lock(); match *state { @@ -254,7 +242,7 @@ impl AsyncTask { let waker = Arc::new(self.clone()).into(); let mut context = Context::from_waker(&waker); let _ = self.task.lock().as_mut().poll(&mut context); - return false; + false } pub fn set_state(&self, state: AsyncTaskState) { @@ -263,7 +251,7 @@ impl AsyncTask { } } -// async req function +/// async request function pub async fn async_ipi_req() { let ipi_list = ASYNC_IPI_TASK_LIST.lock(); if ipi_list.is_empty() { @@ -271,23 +259,20 @@ pub async fn async_ipi_req() { } let task = ipi_list.front().unwrap().clone(); drop(ipi_list); - match task.task_data { - AsyncTaskData::AsyncIpiTask(msg) => { - if active_vm_id() == 0 { - virtio_blk_notify_handler(msg.vq.clone(), msg.blk.clone(), vm(msg.src_id).unwrap()); - } else { - // add_task_ipi_count(); - // send IPI to target cpu, and the target will invoke `mediated_ipi_handler` - ipi_send_msg(0, IpiType::IpiTMediatedDev, IpiInnerMsg::MediatedMsg(msg)); - } + if let AsyncTaskData::AsyncIpiTask(msg) = task.task_data { + if active_vm_id() == 0 { + virtio_blk_notify_handler(msg.vq.clone(), msg.blk.clone(), vm(msg.src_id).unwrap()); + } else { + // add_task_ipi_count(); + // send IPI to target cpu, and the target will invoke `mediated_ipi_handler` + ipi_send_msg(0, IpiType::IpiTMediatedDev, IpiInnerMsg::MediatedMsg(msg)); } - _ => {} } } pub async fn async_blk_id_req() {} -// inject an interrupt to service VM +/// inject an interrupt to service VM pub async fn async_blk_io_req() { let io_list = ASYNC_IO_TASK_LIST.lock(); if io_list.is_empty() { @@ -295,8 +280,8 @@ pub async fn async_blk_io_req() { } let task = io_list.front().unwrap().clone(); drop(io_list); - match task.task_data { - AsyncTaskData::AsyncIoTask(msg) => match msg.io_type { + if let AsyncTaskData::AsyncIoTask(msg) = task.task_data { + match msg.io_type { VIRTIO_BLK_T_IN => { mediated_blk_read(msg.blk_id, msg.sector, msg.count); } @@ -309,7 +294,12 @@ pub async fn async_blk_io_req() { if cache_ptr < 0x1000 || data_bg < 0x1000 { panic!("illegal des addr {:x}, src addr {:x}", cache_ptr, data_bg); } - memcpy_safe(cache_ptr as *mut u8, data_bg as *mut u8, len); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy(cache_ptr as *mut u8, data_bg as *mut u8, len); + } cache_ptr += len; } mediated_blk_write(msg.blk_id, msg.sector, msg.count); @@ -317,12 +307,12 @@ pub async fn async_blk_io_req() { _ => { panic!("illegal mediated blk req type {}", msg.io_type); } - }, - _ => {} + } } } // end async req function +/// set the state of the front task in the IO task list pub fn set_front_io_task_state(state: AsyncTaskState) { let io_list = ASYNC_IO_TASK_LIST.lock(); match io_list.front() { @@ -335,6 +325,7 @@ pub fn set_front_io_task_state(state: AsyncTaskState) { } } +/// add a task to the async task list pub fn add_async_task(task: AsyncTask, ipi: bool) { // println!("add {} task", if ipi { "ipi" } else { "blk io" }); let mut ipi_list = ASYNC_IPI_TASK_LIST.lock(); @@ -368,7 +359,7 @@ pub fn add_async_task(task: AsyncTask, ipi: bool) { } } -// async task executor +// async task executor, iterately gets tasks from the queue and executse them pub fn async_task_exe() { if active_vm_id() == 0 { match async_exe_status() { @@ -439,23 +430,23 @@ pub fn finish_async_task(ipi: bool) { drop(ipi_list); match task.task_data { AsyncTaskData::AsyncIoTask(args) => { - match args.io_type { - VIRTIO_BLK_T_IN => { - // let mut sum = 0; - let mut cache_ptr = args.cache; - for idx in 0..args.iov_list.len() { - let data_bg = args.iov_list[idx].data_bg; - let len = args.iov_list[idx].len as usize; - if trace() && (data_bg < 0x1000 || cache_ptr < 0x1000) { - panic!("illegal des addr {:x}, src addr {:x}", data_bg, cache_ptr); - } - memcpy_safe(data_bg as *mut u8, cache_ptr as *mut u8, len); - // sum |= check_sum(data_bg, len); - cache_ptr += len; + if args.io_type == VIRTIO_BLK_T_IN { + // let mut sum = 0; + let mut cache_ptr = args.cache; + for idx in 0..args.iov_list.len() { + let data_bg = args.iov_list[idx].data_bg; + let len = args.iov_list[idx].len as usize; + if trace() && (data_bg < 0x1000 || cache_ptr < 0x1000) { + panic!("illegal des addr {:x}, src addr {:x}", data_bg, cache_ptr); } - // println!("read check_sum is {:x}", sum); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy(data_bg as *mut u8, cache_ptr as *mut u8, len); + } + cache_ptr += len; } - _ => {} } update_used_info(args.vq.clone(), task.src_vmid); @@ -481,7 +472,7 @@ pub fn push_used_info(desc_chain_head_idx: u32, used_len: u32, src_vmid: usize) }); } None => { - println!("async_push_used_info: src_vmid {} not existed", src_vmid); + error!("async_push_used_info: src_vmid {} not existed", src_vmid); } } } @@ -498,7 +489,7 @@ fn update_used_info(vq: Virtq, src_vmid: usize) { // info_list.clear(); } None => { - println!("async_push_used_info: src_vmid {} not existed", src_vmid); + error!("async_push_used_info: src_vmid {} not existed", src_vmid); } } } @@ -518,5 +509,5 @@ pub fn remove_vm_async_task(vm_id: usize) { let mut io_list = ASYNC_IO_TASK_LIST.lock(); let mut ipi_list = ASYNC_IPI_TASK_LIST.lock(); io_list.remove(vm_id); - ipi_list.drain_filter(|x| x.src_vmid == vm_id); + *ipi_list = ipi_list.extract_if(|x| x.src_vmid == vm_id).collect::>(); } diff --git a/src/kernel/cpu.rs b/src/kernel/cpu.rs index 6c0547b5ffad7882c540a8508f166c3ba8d80d5e..99f4c9ae6baebd645533f13989f20fa6be0ea0ac 100644 --- a/src/kernel/cpu.rs +++ b/src/kernel/cpu.rs @@ -9,39 +9,29 @@ // See the Mulan PSL v2 for more details. use alloc::vec::Vec; +use core::ptr; use spin::Mutex; -use crate::arch::{PAGE_SIZE, pt_map_banked_cpu, PTE_PER_PAGE}; +use crate::arch::{PAGE_SIZE, set_current_cpu}; + use crate::arch::ContextFrame; +use crate::arch::{wfi, isb}; use crate::arch::ContextFrameTrait; // use core::ops::{Deref, DerefMut}; -use crate::arch::cpu_interrupt_unmask; -use crate::board::PLATFORM_CPU_NUM_MAX; +use crate::arch::{cpu_interrupt_unmask, current_cpu_arch}; +use crate::board::{PLATFORM_CPU_NUM_MAX, Platform, PlatOperation}; use crate::kernel::{SchedType, Vcpu, VcpuArray, VcpuState, Vm, Scheduler}; use crate::kernel::IpiMessage; -use crate::lib::trace; +use crate::utils::trace; pub const CPU_MASTER: usize = 0; pub const CPU_STACK_SIZE: usize = PAGE_SIZE * 128; pub const CONTEXT_GPR_NUM: usize = 31; - -#[repr(C)] -#[repr(align(4096))] -#[derive(Copy, Clone, Debug, Eq)] -pub struct CpuPt { - pub lvl1: [usize; PTE_PER_PAGE], - pub lvl2: [usize; PTE_PER_PAGE], - pub lvl3: [usize; PTE_PER_PAGE], -} - -impl PartialEq for CpuPt { - fn eq(&self, other: &Self) -> bool { - self.lvl1 == other.lvl1 && self.lvl2 == other.lvl2 && self.lvl3 == other.lvl3 - } -} +pub const CPU_STACK_OFFSET: usize = offset_of!(Cpu, stack); #[derive(Copy, Clone, Debug, Eq)] +/// CPU state Enum pub enum CpuState { CpuInv = 0, CpuIdle = 1, @@ -54,13 +44,35 @@ impl PartialEq for CpuState { } } +#[derive(Copy, Clone, Debug)] +pub enum StartReason { + MainCore, + SecondaryCore, + None, +} + +/// A struct to store the information of a CPU pub struct CpuIf { pub msg_queue: Vec, + pub entry: u64, + pub ctx: u64, + pub vm_id: usize, + pub state_for_start: CpuState, + pub vcpuid: usize, + pub start_reason: StartReason, } impl CpuIf { pub fn default() -> CpuIf { - CpuIf { msg_queue: Vec::new() } + CpuIf { + msg_queue: Vec::new(), + entry: 0, + ctx: 0, + vm_id: 0, + state_for_start: CpuState::CpuInv, + vcpuid: 0, + start_reason: StartReason::None, + } } pub fn push(&mut self, ipi_msg: IpiMessage) { @@ -72,6 +84,7 @@ impl CpuIf { } } +/// stores the information of all CPUs, which count is the number of CPU on the platform pub static CPU_IF_LIST: Mutex> = Mutex::new(Vec::new()); fn cpu_if_init() { @@ -81,20 +94,28 @@ fn cpu_if_init() { } } -#[repr(C)] -#[repr(align(4096))] -// #[derive(Clone)] +#[repr(C, align(4096))] +struct CpuStack([u8; CPU_STACK_SIZE]); + +impl core::ops::Deref for CpuStack { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[repr(C, align(4096))] pub struct Cpu { pub id: usize, pub cpu_state: CpuState, pub active_vcpu: Option, - pub ctx: Option, + pub ctx: *mut ContextFrame, pub sched: SchedType, pub vcpu_array: VcpuArray, pub current_irq: usize, - pub cpu_pt: CpuPt, - pub stack: [u8; CPU_STACK_SIZE], + stack: CpuStack, } impl Cpu { @@ -103,110 +124,82 @@ impl Cpu { id: 0, cpu_state: CpuState::CpuInv, active_vcpu: None, - ctx: None, + ctx: ptr::null_mut(), sched: SchedType::None, vcpu_array: VcpuArray::new(), current_irq: 0, - cpu_pt: CpuPt { - lvl1: [0; PTE_PER_PAGE], - lvl2: [0; PTE_PER_PAGE], - lvl3: [0; PTE_PER_PAGE], - }, - stack: [0; CPU_STACK_SIZE], + stack: CpuStack([0; CPU_STACK_SIZE]), } } - pub fn set_ctx(&mut self, ctx: *mut ContextFrame) { - self.ctx = Some(ctx as usize); + /// # Safety: + /// The caller must ensure that the `ctx` is valid. + /// ctx must be aligned to 8 bytes + pub unsafe fn set_ctx(&mut self, ctx: *mut ContextFrame) { + self.ctx = ctx; } pub fn clear_ctx(&mut self) { - self.ctx = None; + self.ctx = ptr::null_mut(); + } + + pub fn ctx(&self) -> Option<&ContextFrame> { + self.ctx_ptr().map(|addr| unsafe { &*addr }) + } + + pub fn ctx_mut(&self) -> Option<&mut ContextFrame> { + self.ctx_ptr().map(|addr| unsafe { &mut *addr }) + } + + pub fn ctx_ptr(&self) -> Option<*mut ContextFrame> { + if self.ctx.is_null() { + None + } else { + if trace() && (self.ctx as usize) < 0x1000 { + panic!("illegal ctx addr {:p}", self.ctx); + } + Some(self.ctx) + } } pub fn set_gpr(&self, idx: usize, val: usize) { if idx >= CONTEXT_GPR_NUM { return; } - match self.ctx { - Some(ctx_addr) => { - if trace() && ctx_addr < 0x1000 { - panic!("illegal ctx addr {:x}", ctx_addr); - } - let ctx = ctx_addr as *mut ContextFrame; - unsafe { - (*ctx).set_gpr(idx, val); - } - } - None => {} - } + self.ctx_mut().unwrap().set_gpr(idx, val) } pub fn get_gpr(&self, idx: usize) -> usize { if idx >= CONTEXT_GPR_NUM { return 0; } - match self.ctx { - Some(ctx_addr) => { - if trace() && ctx_addr < 0x1000 { - panic!("illegal ctx addr {:x}", ctx_addr); - } - let ctx = ctx_addr as *mut ContextFrame; - unsafe { (*ctx).gpr(idx) } - } - None => 0, - } + self.ctx_mut().unwrap().gpr(idx) } pub fn get_elr(&self) -> usize { - match self.ctx { - Some(ctx_addr) => { - if trace() && ctx_addr < 0x1000 { - panic!("illegal ctx addr {:x}", ctx_addr); - } - let ctx = ctx_addr as *mut ContextFrame; - unsafe { (*ctx).exception_pc() } - } - None => 0, - } + self.ctx().unwrap().exception_pc() } pub fn get_spsr(&self) -> usize { - match self.ctx { - Some(ctx_addr) => { - if trace() && ctx_addr < 0x1000 { - panic!("illegal ctx addr {:x}", ctx_addr); - } - let ctx = ctx_addr as *mut ContextFrame; - unsafe { (*ctx).spsr as usize } - } - None => 0, - } + self.ctx().unwrap().spsr as usize } pub fn set_elr(&self, val: usize) { - match self.ctx { - Some(ctx_addr) => { - if trace() && ctx_addr < 0x1000 { - panic!("illegal ctx addr {:x}", ctx_addr); - } - let ctx = ctx_addr as *mut ContextFrame; - unsafe { (*ctx).set_exception_pc(val) } - } - None => {} - } + self.ctx_mut().unwrap().set_exception_pc(val) } + /// set a active vcpu for this physical cpu pub fn set_active_vcpu(&mut self, active_vcpu: Option) { self.active_vcpu = active_vcpu.clone(); match active_vcpu { None => {} Some(vcpu) => { - vcpu.set_state(VcpuState::VcpuAct); + vcpu.set_state(VcpuState::Running); } } } + /// schedule a vcpu to run on this physical cpu pub fn schedule_to(&mut self, next_vcpu: Vcpu) { if let Some(prev_vcpu) = &self.active_vcpu { if prev_vcpu.vm_id() != next_vcpu.vm_id() { @@ -217,7 +210,7 @@ impl Cpu { // prev_vcpu.vm_id(), // prev_vcpu.id() // ); - prev_vcpu.set_state(VcpuState::VcpuPend); + prev_vcpu.set_state(VcpuState::Ready); prev_vcpu.context_vm_store(); } } @@ -230,11 +223,14 @@ impl Cpu { let vttbr = (next_vcpu.vm_id() << 48) | next_vcpu.vm_pt_dir(); // println!("vttbr {:#x}", vttbr); // TODO: replace the arch related expr + // SAFETY: 'vttbr' is saved in the vcpu struct when last scheduled unsafe { - core::arch::asm!("msr VTTBR_EL2, {0}", "isb", in(reg) vttbr); + core::arch::asm!("msr VTTBR_EL2, {0}", in(reg) vttbr); + isb(); } } + /// get this cpu's scheduler pub fn scheduler(&mut self) -> &mut impl Scheduler { match &mut self.sched { SchedType::None => panic!("scheduler is None"), @@ -242,22 +238,26 @@ impl Cpu { } } + /// check whether this cpu is assigned to one or more vm pub fn assigned(&self) -> bool { self.vcpu_array.vcpu_num() != 0 } -} -#[no_mangle] -#[link_section = ".cpu_private"] -pub static mut CPU: Cpu = Cpu::default(); + pub fn stack_top(&self) -> usize { + self.stack.as_ptr_range().end as usize + } +} pub fn current_cpu() -> &'static mut Cpu { - unsafe { &mut CPU } + // SAFETY: The value of current_cpu_arch() is valid setted by cpu_map_self at boot_stage + unsafe { &mut *(current_cpu_arch() as *mut Cpu) } } pub fn active_vcpu_id() -> usize { - let active_vcpu = current_cpu().active_vcpu.clone().unwrap(); - active_vcpu.id() + match current_cpu().active_vcpu.clone() { + Some(active_vcpu) => active_vcpu.id(), + None => 0xFFFFFFFF, + } } pub fn active_vm_id() -> usize { @@ -267,12 +267,8 @@ pub fn active_vm_id() -> usize { pub fn active_vm() -> Option { match current_cpu().active_vcpu.clone() { - None => { - return None; - } - Some(active_vcpu) => { - return active_vcpu.vm(); - } + None => None, + Some(active_vcpu) => active_vcpu.vm(), } } @@ -283,51 +279,60 @@ pub fn active_vm_ncpu() -> usize { } } +/// initialize the CPU pub fn cpu_init() { let cpu_id = current_cpu().id; if cpu_id == 0 { - use crate::arch::power_arch_init; - use crate::board::{Platform, PlatOperation}; - Platform::power_on_secondary_cores(); - power_arch_init(); cpu_if_init(); + if cfg!(not(feature = "secondary_start")) { + Platform::power_on_secondary_cores(); + } } let state = CpuState::CpuIdle; current_cpu().cpu_state = state; let sp = current_cpu().stack.as_ptr() as usize + CPU_STACK_SIZE; let size = core::mem::size_of::(); - current_cpu().set_ctx((sp - size) as *mut _); - println!("Core {} init ok", cpu_id); - - crate::lib::barrier(); - // println!("after barrier cpu init"); - use crate::board::PLAT_DESC; - if cpu_id == 0 { - println!("Bring up {} cores", PLAT_DESC.cpu_desc.num); - println!("Cpu init ok"); + // SAFETY: Sp is valid when boot_stage setting + unsafe { + current_cpu().set_ctx((sp - size) as *mut _); + } + info!("Core {} init ok", cpu_id); + + if cfg!(not(feature = "secondary_start")) { + crate::utils::barrier(); + // println!("after barrier cpu init"); + use crate::board::PLAT_DESC; + if cpu_id == 0 { + info!("Bring up {} cores", PLAT_DESC.cpu_desc.num); + info!("Cpu init ok"); + } } } +/// make the current cpu idle pub fn cpu_idle() -> ! { let state = CpuState::CpuIdle; current_cpu().cpu_state = state; cpu_interrupt_unmask(); loop { - // TODO: replace it with an Arch function `arch_idle` - cortex_a::asm::wfi(); + wfi(); } } +/// store all cpu's CPU struct in this array pub static mut CPU_LIST: [Cpu; PLATFORM_CPU_NUM_MAX] = [const { Cpu::default() }; PLATFORM_CPU_NUM_MAX]; - -#[no_mangle] -// #[link_section = ".text.boot"] -pub extern "C" fn cpu_map_self(cpu_id: usize) -> usize { - let mut cpu = unsafe { &mut CPU_LIST[cpu_id] }; - (*cpu).id = cpu_id; - - let lvl1_addr = pt_map_banked_cpu(cpu); - - lvl1_addr +pub extern "C" fn cpu_map_self(mpidr: usize) { + let cpu_id = Platform::mpidr2cpuid(mpidr); + // SAFETY: + // One core only call this function once + // And it will get the reference of the CPU_LIST[cpu_id] by cpu_id + // So it won't influence other cores + let cpu = unsafe { &mut CPU_LIST[cpu_id] }; + cpu.id = cpu_id; + // SAFETY: + // The 'cpu' is a valid reference of CPU_LIST[cpu_id] + unsafe { + set_current_cpu(cpu as *const _ as u64); + } } diff --git a/src/kernel/hvc.rs b/src/kernel/hvc.rs index d339c481b1284fed472c1f7257903f22f94c3289..de60b946df8b807a7c865848bc39de8ad9434263 100644 --- a/src/kernel/hvc.rs +++ b/src/kernel/hvc.rs @@ -10,26 +10,22 @@ use alloc::collections::BTreeMap; use core::mem::size_of; +use core::sync::atomic::{AtomicUsize, Ordering}; use spin::Mutex; -use crate::arch::{PAGE_SIZE, PTE_S2_NORMAL}; -use crate::arch::gicc_clear_current_irq; +use crate::arch::PAGE_SIZE; use crate::config::*; use crate::device::{mediated_blk_notify_handler, mediated_dev_append}; use crate::kernel::{ - active_vm, active_vm_id, current_cpu, DIRTY_MEM_THRESHOLD, interrupt_vm_inject, ipi_register, ipi_send_msg, - IpiHvcMsg, IpiInnerMsg, IpiMessage, IpiType, ivc_update_mq, map_migrate_vm_mem, mem_heap_region_reserve, - migrate_finish_ipi_handler, migrate_ready, Scheduler, send_migrate_memcpy_msg, unmap_migrate_vm_mem, - UPDATE_IMG_BASE_ADDR, update_request, vcpu_idle, vm, vm_if_copy_mem_map, vm_if_dirty_mem_map, vm_if_get_cpu_id, - vm_if_ivc_arg, vm_if_ivc_arg_ptr, vm_if_mem_map_dirty_sum, vm_if_mem_map_page_num, vm_if_set_ivc_arg_ptr, - VM_NUM_MAX, VMData, + current_cpu, interrupt_vm_inject, ipi_send_msg, IpiHvcMsg, IpiInnerMsg, IpiMessage, IpiType, ivc_update_mq, vm, + vm_if_get_cpu_id, vm_if_ivc_arg, vm_if_ivc_arg_ptr, vm_if_set_ivc_arg_ptr, VM_NUM_MAX, }; -use crate::lib::{func_barrier, memcpy_safe, round_up, set_barrier_num, trace}; -use crate::lib::unilib::*; -use crate::vmm::{get_vm_id, vmm_boot_vm, vmm_list_vm, vmm_migrate_boot, vmm_reboot_vm, vmm_remove_vm}; - -pub static VM_STATE_FLAG: Mutex = Mutex::new(0); +use crate::utils::{memcpy, trace}; +#[cfg(feature = "unilib")] +use crate::utils::unilib::*; +use crate::vmm::{get_vm_id, vmm_boot_vm, vmm_list_vm, vmm_reboot_vm, vmm_remove_vm}; +pub static VM_STATE_FLAG: AtomicUsize = AtomicUsize::new(0); pub static SHARE_MEM_LIST: Mutex> = Mutex::new(BTreeMap::new()); // If succeed, return 0. @@ -38,12 +34,8 @@ const HVC_FINISH: usize = 0; const HVC_ERR: usize = usize::MAX; // share mem type -pub const MIGRATE_BITMAP: usize = 0; pub const VM_CONTEXT_SEND: usize = 1; pub const VM_CONTEXT_RECEIVE: usize = 2; -pub const MIGRATE_SEND: usize = 3; -pub const MIGRATE_RECEIVE: usize = 4; -pub const LIVE_UPDATE_IMG: usize = 5; // hvc_fid pub const HVC_SYS: usize = 0; @@ -51,6 +43,7 @@ pub const HVC_VMM: usize = 1; pub const HVC_IVC: usize = 2; pub const HVC_MEDIATED: usize = 3; pub const HVC_CONFIG: usize = 0x11; +#[cfg(feature = "unilib")] pub const HVC_UNILIB: usize = 0x12; // hvc_sys_event @@ -58,6 +51,7 @@ pub const HVC_SYS_REBOOT: usize = 0; pub const HVC_SYS_SHUTDOWN: usize = 1; pub const HVC_SYS_UPDATE: usize = 3; pub const HVC_SYS_TEST: usize = 4; +pub const HVC_SYS_UPDATE_MEM_MAP: usize = 5; // hvc_vmm_event pub const HVC_VMM_LIST_VM: usize = 0; @@ -101,16 +95,20 @@ pub const HVC_MEDIATED_DEV_APPEND: usize = 0x30; pub const HVC_MEDIATED_DEV_NOTIFY: usize = 0x31; pub const HVC_MEDIATED_DRV_NOTIFY: usize = 0x32; -pub const HVC_UNILIB_FS_INIT: usize = 0; -pub const HVC_UNILIB_FS_OPEN: usize = 1; -pub const HVC_UNILIB_FS_CLOSE: usize = 2; -pub const HVC_UNILIB_FS_READ: usize = 3; -pub const HVC_UNILIB_FS_WRITE: usize = 4; -pub const HVC_UNILIB_FS_LSEEK: usize = 5; -pub const HVC_UNILIB_FS_STAT: usize = 6; -pub const HVC_UNILIB_FS_UNLINK: usize = 7; -pub const HVC_UNILIB_FS_APPEND: usize = 0x10; -pub const HVC_UNILIB_FS_FINISHED: usize = 0x11; +cfg_if::cfg_if! { + if #[cfg(feature = "unilib")] { + pub const HVC_UNILIB_FS_INIT: usize = 0; + pub const HVC_UNILIB_FS_OPEN: usize = 1; + pub const HVC_UNILIB_FS_CLOSE: usize = 2; + pub const HVC_UNILIB_FS_READ: usize = 3; + pub const HVC_UNILIB_FS_WRITE: usize = 4; + pub const HVC_UNILIB_FS_LSEEK: usize = 5; + pub const HVC_UNILIB_FS_STAT: usize = 6; + pub const HVC_UNILIB_FS_UNLINK: usize = 7; + pub const HVC_UNILIB_FS_APPEND: usize = 0x10; + pub const HVC_UNILIB_FS_FINISHED: usize = 0x11; + } +} // hvc_config_event pub const HVC_CONFIG_ADD_VM: usize = 0; @@ -123,6 +121,8 @@ pub const HVC_CONFIG_PASSTHROUGH_DEVICE_IRQS: usize = 6; pub const HVC_CONFIG_PASSTHROUGH_DEVICE_STREAMS_IDS: usize = 7; pub const HVC_CONFIG_DTB_DEVICE: usize = 8; pub const HVC_CONFIG_UPLOAD_KERNEL_IMAGE: usize = 9; +pub const HVC_CONFIG_MEMORY_COLOR_BUDGET: usize = 10; +pub const HVC_CONFIG_UPLOAD_DEVICE_TREE: usize = 11; #[cfg(feature = "tx2")] pub const HVC_IRQ: usize = 32 + 0x20; @@ -130,12 +130,15 @@ pub const HVC_IRQ: usize = 32 + 0x20; pub const HVC_IRQ: usize = 32 + 0x10; #[cfg(feature = "qemu")] pub const HVC_IRQ: usize = 32 + 0x20; +#[cfg(feature = "rk3588")] +pub const HVC_IRQ: usize = 32 + 0x10; #[repr(C)] pub enum HvcGuestMsg { Default(HvcDefaultMsg), Manage(HvcManageMsg), Migrate(HvcMigrateMsg), + #[cfg(feature = "unilib")] UniLib(HvcUniLibMsg), } @@ -165,6 +168,7 @@ pub struct HvcMigrateMsg { pub page_num: usize, // bitmap page num } +#[cfg(feature = "unilib")] #[repr(C)] pub struct HvcUniLibMsg { pub fid: usize, @@ -190,6 +194,8 @@ pub fn get_share_mem(mem_type: usize) -> usize { } } +#[allow(clippy::too_many_arguments)] +/// handler of hypervisor call from guest os pub fn hvc_guest_handler( hvc_type: usize, event: usize, @@ -207,14 +213,16 @@ pub fn hvc_guest_handler( HVC_IVC => hvc_ivc_handler(event, x0, x1), HVC_MEDIATED => hvc_mediated_handler(event, x0, x1), HVC_CONFIG => hvc_config_handler(event, x0, x1, x2, x3, x4, x5, x6), + #[cfg(feature = "unilib")] HVC_UNILIB => hvc_unilib_handler(event, x0, x1, x2), _ => { - println!("hvc_guest_handler: unknown hvc type {} event {}", hvc_type, event); + error!("hvc_guest_handler: unknown hvc type {} event {}", hvc_type, event); Err(()) } } } +#[allow(clippy::too_many_arguments)] fn hvc_config_handler( event: usize, x0: usize, @@ -236,23 +244,21 @@ fn hvc_config_handler( HVC_CONFIG_PASSTHROUGH_DEVICE_STREAMS_IDS => vm_cfg_add_passthrough_device_streams_ids(x0, x1, x2), HVC_CONFIG_DTB_DEVICE => vm_cfg_add_dtb_dev(x0, x1, x2, x3, x4, x5, x6), HVC_CONFIG_UPLOAD_KERNEL_IMAGE => vm_cfg_upload_kernel_image(x0, x1, x2, x3, x4), + HVC_CONFIG_UPLOAD_DEVICE_TREE => vm_cfg_upload_device_tree(x0, x1, x2, x3, x4), _ => { - println!("hvc_config_handler unknown event {}", event); + error!("hvc_config_handler unknown event {}", event); Err(()) } } } -fn hvc_sys_handler(event: usize, x0: usize) -> Result { +fn hvc_sys_handler(event: usize, _x0: usize) -> Result { match event { HVC_SYS_UPDATE => { - mem_heap_region_reserve(UPDATE_IMG_BASE_ADDR, x0); - update_request(); - Ok(0) + todo!() } HVC_SYS_TEST => { - let vm = active_vm().unwrap(); - crate::device::virtio_net_announce(vm); + println!("HVC SYS TEST"); Ok(0) } _ => Err(()), @@ -280,104 +286,22 @@ fn hvc_vmm_handler(event: usize, x0: usize, _x1: usize) -> Result { get_vm_id(x0); Ok(HVC_FINISH) } - HVC_VMM_MIGRATE_START => { - // demo: migration for bma1 - if x0 == 0 { - println!("migration for mvm is not supported"); - return Err(()); - } - - hvc_send_msg_to_vm( - 0, - &HvcGuestMsg::Migrate(HvcMigrateMsg { - fid: HVC_VMM, - event: HVC_VMM_MIGRATE_START, - vm_id: x0, - oper: MIGRATE_START, - page_num: 0, - }), - ); - Ok(HVC_FINISH) - } - HVC_VMM_MIGRATE_READY => { - // init gvm dirty memory bitmap - // let cpu_trgt = vm_if_get_cpu_id(x0); - // println!( - // "core {} HVC_VMM_MIGRATE_READY, cpu trgt {}, vmid {}", - // current_cpu().id, - // cpu_trgt, - // x0 - // ); - migrate_ready(x0); - mvm_migrate_memory(x0); - vm_if_dirty_mem_map(x0); - - // send_hvc_ipi(0, x0, HVC_VMM, HVC_VMM_MIGRATE_READY, cpu_trgt); - Ok(HVC_FINISH) - } - HVC_VMM_MIGRATE_MEMCPY => { - let dirty_mem_num = vm_if_mem_map_dirty_sum(x0); - // let cpu_trgt = vm_if_get_cpu_id(x0); - if dirty_mem_num < DIRTY_MEM_THRESHOLD { - // Idle live vm, copy dirty mem and vm register struct - let trgt_vm = vm(x0).unwrap(); - set_barrier_num(trgt_vm.cpu_num()); - for vcpu_id in 0..trgt_vm.cpu_num() { - let pcpu_id = trgt_vm.vcpuid_to_pcpuid(vcpu_id).unwrap(); - send_hvc_ipi(0, x0, HVC_VMM, HVC_VMM_MIGRATE_FINISH, pcpu_id); - } - } else { - mvm_migrate_memory(x0); - // send_hvc_ipi(0, x0, HVC_VMM, HVC_VMM_MIGRATE_MEMCPY, cpu_trgt); - } - Ok(HVC_FINISH) - } - HVC_VMM_MIGRATE_INIT_VM => { - info!("migrate init vm {}", x0); - // vmm_init_gvm(x0); - let vm = vm(x0).unwrap(); - map_migrate_vm_mem(vm.clone(), get_share_mem(MIGRATE_RECEIVE)); - vm.context_vm_migrate_init(); - Ok(HVC_FINISH) - } - HVC_VMM_MIGRATE_VM_BOOT => { - let mvm = vm(0).unwrap(); - let vm = vm(x0).unwrap(); - - let size = size_of::(); - mvm.pt_unmap_range(get_share_mem(VM_CONTEXT_RECEIVE), round_up(size, PAGE_SIZE), true); - unmap_migrate_vm_mem(vm.clone(), get_share_mem(MIGRATE_RECEIVE)); - - vm.context_vm_migrate_restore(); - for vcpu_id in 0..vm.cpu_num() { - let cpu_trgt = vm.vcpuid_to_pcpuid(vcpu_id).unwrap(); - // send ipi to target vcpu, copy data and boot vm (in ipi copy gic data) - send_hvc_ipi(0, x0, HVC_VMM, HVC_VMM_MIGRATE_VM_BOOT, cpu_trgt); - } - Ok(HVC_FINISH) - } - HVC_VMM_MIGRATE_FINISH => { - let mvm = vm(0).unwrap(); - let trgt_vm = vm(x0).unwrap(); - let size = size_of::(); - mvm.pt_unmap_range(get_share_mem(VM_CONTEXT_SEND), round_up(size, PAGE_SIZE), true); - mvm.pt_unmap_range( - get_share_mem(MIGRATE_BITMAP), - PAGE_SIZE * vm_if_mem_map_page_num(x0), - true, - ); - unmap_migrate_vm_mem(trgt_vm, get_share_mem(MIGRATE_SEND)); - vmm_remove_vm(x0); - *VM_STATE_FLAG.lock() = 0; - Ok(HVC_FINISH) + HVC_VMM_MIGRATE_START + | HVC_VMM_MIGRATE_READY + | HVC_VMM_MIGRATE_MEMCPY + | HVC_VMM_MIGRATE_INIT_VM + | HVC_VMM_MIGRATE_VM_BOOT + | HVC_VMM_MIGRATE_FINISH => { + error!("unimplemented"); + Err(()) } HVC_VMM_VM_REMOVE => { vmm_remove_vm(x0); - *VM_STATE_FLAG.lock() = 0; + VM_STATE_FLAG.store(0, Ordering::Relaxed); Ok(HVC_FINISH) } _ => { - println!("hvc_vmm unknown event {}", event); + error!("hvc_vmm unknown event {}", event); Err(()) } } @@ -393,25 +317,11 @@ fn hvc_ivc_handler(event: usize, x0: usize, x1: usize) -> Result { } } HVC_IVC_SHARE_MEM => { - let vm = active_vm().unwrap(); - let base = vm.share_mem_base(); - if x0 == LIVE_UPDATE_IMG { - // hard code for pa 0x8a000000, x1 should be 0x8000000 - vm.pt_map_range(base, x1, 0x8a000000, PTE_S2_NORMAL, true); - } - vm.add_share_mem_base(x1); - add_share_mem(x0, base); - info!( - "VM{} add share mem type 0x{:x} base 0x{:x} len 0x{:x}", - active_vm_id(), - x0, - base, - x1 - ); - Ok(base) + error!("not support vm migration and live update"); + Ok(HVC_FINISH) } _ => { - println!("hvc_ivc_handler: unknown event {}", event); + error!("hvc_ivc_handler: unknown event {}", event); Err(()) } } @@ -422,12 +332,13 @@ fn hvc_mediated_handler(event: usize, x0: usize, x1: usize) -> Result HVC_MEDIATED_DEV_APPEND => mediated_dev_append(x0, x1), HVC_MEDIATED_DEV_NOTIFY => mediated_blk_notify_handler(x0), _ => { - println!("unknown mediated event {}", event); - return Err(()); + error!("unknown mediated event {}", event); + Err(()) } } } +#[cfg(feature = "unilib")] fn hvc_unilib_handler(event: usize, x0: usize, x1: usize, x2: usize) -> Result { match event { HVC_UNILIB_FS_INIT => unilib_fs_init(), @@ -441,8 +352,8 @@ fn hvc_unilib_handler(event: usize, x0: usize, x1: usize, x2: usize) -> Result unilib_fs_append(x0), HVC_UNILIB_FS_FINISHED => unilib_fs_finished(x0), _ => { - println!("unknown mediated event {}", event); - return Err(()); + error!("unknown mediated event {}", event); + Err(()) } } } @@ -464,7 +375,7 @@ pub fn hvc_send_msg_to_vm(vm_id: usize, guest_msg: &HvcGuestMsg) -> bool { } if target_addr == 0 { - println!("hvc_send_msg_to_vm: target VM{} interface is not prepared", vm_id); + warn!("hvc_send_msg_to_vm: target VM{} interface is not prepared", vm_id); return false; } @@ -476,42 +387,52 @@ pub fn hvc_send_msg_to_vm(vm_id: usize, guest_msg: &HvcGuestMsg) -> bool { } let (fid, event) = match guest_msg { HvcGuestMsg::Default(msg) => { - memcpy_safe( - target_addr as *const u8, - msg as *const _ as *const u8, - size_of::(), - ); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy( + target_addr as *const u8, + msg as *const _ as *const u8, + size_of::(), + ); + } (msg.fid, msg.event) } - HvcGuestMsg::Migrate(msg) => { - memcpy_safe( - target_addr as *const u8, - msg as *const _ as *const u8, - size_of::(), - ); - (msg.fid, msg.event) + HvcGuestMsg::Migrate(_msg) => { + todo!() } HvcGuestMsg::Manage(msg) => { - memcpy_safe( - target_addr as *const u8, - msg as *const _ as *const u8, - size_of::(), - ); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy( + target_addr as *const u8, + msg as *const _ as *const u8, + size_of::(), + ); + } (msg.fid, msg.event) } + #[cfg(feature = "unilib")] HvcGuestMsg::UniLib(msg) => { - memcpy_safe( - target_addr as *const u8, - msg as *const _ as *const u8, - size_of::(), - ); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy( + target_addr as *const u8, + msg as *const _ as *const u8, + size_of::(), + ); + } (msg.fid, msg.event) } }; let cpu_trgt = vm_if_get_cpu_id(vm_id); if cpu_trgt != current_cpu().id { - // println!("cpu {} send hvc msg to cpu {}", current_cpu().id, cpu_trgt); let ipi_msg = IpiHvcMsg { src_vmid: 0, trgt_vmid: vm_id, @@ -519,7 +440,7 @@ pub fn hvc_send_msg_to_vm(vm_id: usize, guest_msg: &HvcGuestMsg) -> bool { event, }; if !ipi_send_msg(cpu_trgt, IpiType::IpiTHvc, IpiInnerMsg::HvcMsg(ipi_msg)) { - println!( + error!( "hvc_send_msg_to_vm: Failed to send ipi message, target {} type {:#?}", cpu_trgt, IpiType::IpiTHvc @@ -532,19 +453,20 @@ pub fn hvc_send_msg_to_vm(vm_id: usize, guest_msg: &HvcGuestMsg) -> bool { true } -// notify current cpu's vcpu +/// notify current cpu's vcpu pub fn hvc_guest_notify(vm_id: usize) { let vm = vm(vm_id).unwrap(); match current_cpu().vcpu_array.pop_vcpu_through_vmid(vm_id) { None => { - println!( + error!( "hvc_guest_notify: Core {} failed to find vcpu of VM {}", current_cpu().id, vm_id ); } Some(vcpu) => { - interrupt_vm_inject(vm, vcpu, HVC_IRQ, 0); + // println!("hvc_guest_notify here"); + interrupt_vm_inject(vm, vcpu, HVC_IRQ); } }; } @@ -553,7 +475,7 @@ pub fn hvc_ipi_handler(msg: &IpiMessage) { match &msg.ipi_message { IpiInnerMsg::HvcMsg(msg) => { if current_cpu().vcpu_array.pop_vcpu_through_vmid(msg.trgt_vmid).is_none() { - println!( + error!( "hvc_ipi_handler: Core {} failed to find vcpu of VM {}", current_cpu().id, msg.trgt_vmid @@ -571,63 +493,10 @@ pub fn hvc_ipi_handler(msg: &IpiMessage) { hvc_guest_notify(msg.trgt_vmid); } HVC_VMM_MIGRATE_FINISH => { - // 被迁移VM收到该ipi标志vcpu_idle,VM0收到该ipi标志最后一次内存拷贝 - if current_cpu().id == 0 { - migrate_finish_ipi_handler(msg.src_vmid); - return; - } - let trgt_vcpu = match current_cpu().vcpu_array.pop_vcpu_through_vmid(msg.trgt_vmid) { - None => { - println!( - "Core {} failed to find target vcpu, vmid {}", - current_cpu().id, - msg.trgt_vmid - ); - return; - } - Some(vcpu) => vcpu, - }; - let vm = trgt_vcpu.vm().unwrap(); - // println!("Core[{}] clear irq {}", current_cpu().id, current_cpu().current_irq); - gicc_clear_current_irq(true); - // 当满足下序条件时需要拷贝cpu.ctx - // 否则意味着当前核心有多个虚拟机共享,且被迁移虚拟机所在的核心尚未被调度到,寄存器数值无需更新 - if vm.id() == msg.trgt_vmid { - trgt_vcpu.context_vm_store(); - } - // save gic register for each vcpu - trgt_vcpu.context_gic_irqs_store(); - func_barrier(); - *VM_STATE_FLAG.lock() = 1; - if trgt_vcpu.id() == 0 { - vm.context_vm_migrate_save(); - // println!("send finish ipi to core0"); - send_hvc_ipi(msg.trgt_vmid, 0, HVC_VMM, HVC_VMM_MIGRATE_FINISH, 0); - } - // println!( - // "VMData size is {:x}, VgicMigData size is {:x}, VirtioMmioData size is {:x}", - // size_of::(), - // size_of::(), - // size_of::(), - // ); - vcpu_idle(trgt_vcpu); + error!("unimplemented"); } HVC_VMM_MIGRATE_VM_BOOT => { - // let vm = vm(msg.trgt_vmid).unwrap(); - // vm.set_migration_state(true); - - gicc_clear_current_irq(true); - match current_cpu().vcpu_array.pop_vcpu_through_vmid(msg.trgt_vmid) { - None => { - panic!("Core[{}] does not have VM[{}] vcpu", current_cpu().id, msg.trgt_vmid); - } - Some(vcpu) => { - current_cpu().scheduler().yield_to(vcpu.clone()); - // restore gic register for each vcpu - vcpu.context_gic_irqs_restore(); - } - } - vmm_migrate_boot(); + error!("unimplemented"); } _ => {} }, @@ -639,6 +508,7 @@ pub fn hvc_ipi_handler(msg: &IpiMessage) { todo!(); } }, + #[cfg(feature = "unilib")] HVC_UNILIB => { hvc_guest_notify(msg.trgt_vmid); } @@ -648,38 +518,7 @@ pub fn hvc_ipi_handler(msg: &IpiMessage) { } } _ => { - println!("vgic_ipi_handler: illegal ipi"); - return; + error!("vgic_ipi_handler: illegal ipi"); } } } - -fn mvm_migrate_memory(trgt_vmid: usize) { - let vm = vm(trgt_vmid); - vm.as_ref().unwrap().pt_read_only(); - // tlb_invalidate_guest_all(); - vm_if_copy_mem_map(trgt_vmid); - send_migrate_memcpy_msg(trgt_vmid); -} - -pub fn hvc_init() { - if !ipi_register(IpiType::IpiTHvc, hvc_ipi_handler) { - panic!("hvc_init: failed to register hvc ipi {}", IpiType::IpiTHvc as usize) - } -} - -pub fn send_hvc_ipi(src_vmid: usize, trgt_vmid: usize, fid: usize, event: usize, trgt_cpuid: usize) { - let ipi_msg = IpiHvcMsg { - src_vmid, - trgt_vmid, - fid, - event, - }; - if !ipi_send_msg(trgt_cpuid, IpiType::IpiTHvc, IpiInnerMsg::HvcMsg(ipi_msg)) { - println!( - "send_hvc_ipi: Failed to send ipi message, target {} type {:#?}", - 0, - IpiType::IpiTHvc - ); - } -} diff --git a/src/kernel/interrupt.rs b/src/kernel/interrupt.rs index 9805ea430ca44c64e81ee445bcf94c2bbebb36ae..18c771b7e2f93aa0cdc7265c1dff158c9b1cc19a 100644 --- a/src/kernel/interrupt.rs +++ b/src/kernel/interrupt.rs @@ -12,142 +12,78 @@ use alloc::collections::BTreeMap; use spin::Mutex; -use crate::arch::{interrupt_arch_ipi_send, interrupt_arch_vm_inject}; -use crate::arch::{GIC_PRIVINT_NUM, interrupt_arch_vm_register}; -use crate::kernel::{current_cpu, hyper_fresh_ipi_handler, ipi_irq_handler, IpiInnerMsg, IpiMessage, Vcpu, VcpuState}; -use crate::kernel::{ipi_register, IpiType, Vm}; -use crate::lib::{BitAlloc, BitAlloc256, BitAlloc4K, BitMap}; -use crate::vmm::vmm_ipi_handler; - -pub const INTERRUPT_NUM_MAX: usize = 1024; -pub const INTERRUPT_IRQ_HYPERVISOR_TIMER: usize = 26; -pub const INTERRUPT_IRQ_GUEST_TIMER: usize = 27; -pub const INTERRUPT_IRQ_IPI: usize = 1; - -pub static INTERRUPT_HYPER_BITMAP: Mutex> = Mutex::new(BitAlloc4K::default()); -pub static INTERRUPT_GLB_BITMAP: Mutex> = Mutex::new(BitAlloc4K::default()); -// pub static INTERRUPT_HANDLERS: Mutex<[InterruptHandler; INTERRUPT_NUM_MAX]> = -// Mutex::new([InterruptHandler::None; INTERRUPT_NUM_MAX]); -pub static INTERRUPT_HANDLERS: Mutex> = Mutex::new(BTreeMap::new()); - -#[derive(Copy, Clone)] -pub enum InterruptHandler { - IpiIrqHandler(fn()), - GicMaintenanceHandler(fn(usize)), - TimeIrqHandler(fn(usize)), - None, -} +use crate::arch::traits::InterruptController; +use crate::arch::IntCtrl; +use crate::kernel::{current_cpu, ipi_irq_handler, IpiInnerMsg, IpiMessage, Vcpu, VcpuState}; +use crate::kernel::Vm; +use crate::utils::{BitAlloc, BitAlloc256, BitAlloc4K, BitMap}; +use super::Scheduler; -impl InterruptHandler { - pub fn call(&self, arg0: usize, _arg1: usize) { - match self { - InterruptHandler::IpiIrqHandler(irq_handler) => irq_handler(), - InterruptHandler::GicMaintenanceHandler(gic_handler) => gic_handler(arg0), - InterruptHandler::TimeIrqHandler(time_handler) => time_handler(arg0), - InterruptHandler::None => panic!("Call An Empty Interrupt Hanlder!"), - } - } -} +pub static INTERRUPT_GLB_BITMAP: Mutex> = Mutex::new(BitAlloc4K::default()); +pub static INTERRUPT_HANDLERS: Mutex> = Mutex::new(BTreeMap::new()); pub fn interrupt_cpu_ipi_send(target_cpu: usize, ipi_id: usize) { - interrupt_arch_ipi_send(target_cpu, ipi_id); + IntCtrl::ipi_send(target_cpu, ipi_id); } -pub fn interrupt_reserve_int(int_id: usize, handler: InterruptHandler) { - if int_id < INTERRUPT_NUM_MAX { - let mut irq_handler_lock = INTERRUPT_HANDLERS.lock(); - let mut hyper_bitmap_lock = INTERRUPT_HYPER_BITMAP.lock(); - let mut glb_bitmap_lock = INTERRUPT_GLB_BITMAP.lock(); - // irq_handler_lock[int_id] = handler; - irq_handler_lock.insert(int_id, handler); - hyper_bitmap_lock.set(int_id); - glb_bitmap_lock.set(int_id) +pub fn interrupt_reserve_int(int_id: usize, handler: fn()) { + if int_id < IntCtrl::NUM_MAX { + INTERRUPT_HANDLERS.lock().insert(int_id, handler); + INTERRUPT_GLB_BITMAP.lock().set(int_id); } } -fn interrupt_is_reserved(int_id: usize) -> bool { - let hyper_bitmap_lock = INTERRUPT_HYPER_BITMAP.lock(); - hyper_bitmap_lock.get(int_id) != 0 +fn interrupt_is_reserved(int_id: usize) -> Option { + INTERRUPT_HANDLERS.lock().get(&int_id).cloned() } +/// enable or disable specific interrupt on current cpu pub fn interrupt_cpu_enable(int_id: usize, en: bool) { - use crate::arch::interrupt_arch_enable; - interrupt_arch_enable(int_id, en); + IntCtrl::enable(int_id, en); } +/// perform interrupt initialization pub fn interrupt_init() { - use crate::arch::interrupt_arch_init; - interrupt_arch_init(); + IntCtrl::init(); let cpu_id = current_cpu().id; if cpu_id == 0 { - interrupt_reserve_int(INTERRUPT_IRQ_IPI, InterruptHandler::IpiIrqHandler(ipi_irq_handler)); - - if !ipi_register(IpiType::IpiTIntInject, interrupt_inject_ipi_handler) { - panic!( - "interrupt_init: failed to register int inject ipi {:#?}", - IpiType::IpiTIntInject - ) - } - use crate::arch::vgic_ipi_handler; - if !ipi_register(IpiType::IpiTIntc, vgic_ipi_handler) { - panic!("interrupt_init: failed to register intc ipi {:#?}", IpiType::IpiTIntc) - } - use crate::device::ethernet_ipi_rev_handler; - if !ipi_register(IpiType::IpiTEthernetMsg, ethernet_ipi_rev_handler) { - panic!( - "interrupt_init: failed to register eth ipi {:#?}", - IpiType::IpiTEthernetMsg, - ); - } - if !ipi_register(IpiType::IpiTVMM, vmm_ipi_handler) { - panic!("interrupt_init: failed to register ipi vmm"); - } - if !ipi_register(IpiType::IpiTHyperFresh, hyper_fresh_ipi_handler) { - panic!("interrupt_init: failed to register ipi hyper fresh"); - } + interrupt_reserve_int(IntCtrl::IRQ_IPI, ipi_irq_handler); - println!("Interrupt init ok"); + info!("Interrupt init ok"); } - interrupt_cpu_enable(INTERRUPT_IRQ_IPI, true); + interrupt_cpu_enable(IntCtrl::IRQ_IPI, true); } +/// register a new interrupt for specific vm pub fn interrupt_vm_register(vm: Vm, id: usize) -> bool { // println!("VM {} register interrupt {}", vm.id(), id); let mut glb_bitmap_lock = INTERRUPT_GLB_BITMAP.lock(); - if glb_bitmap_lock.get(id) != 0 && id >= GIC_PRIVINT_NUM { - println!("interrupt_vm_register: VM {} interrupts conflict, id = {}", vm.id(), id); + if glb_bitmap_lock.get(id) != 0 && id >= IntCtrl::PRI_NUN_MAX { + warn!("interrupt_vm_register: VM {} interrupts conflict, id = {}", vm.id(), id); return false; } - interrupt_arch_vm_register(vm.clone(), id); + IntCtrl::vm_register(vm.clone(), id); vm.set_int_bit_map(id); glb_bitmap_lock.set(id); true } +/// remove interrupt for specific vm pub fn interrupt_vm_remove(_vm: Vm, id: usize) { let mut glb_bitmap_lock = INTERRUPT_GLB_BITMAP.lock(); // vgic and vm will be removed with struct vm glb_bitmap_lock.clear(id); // todo: for interrupt 16~31, need to check by vm config - if id >= GIC_PRIVINT_NUM { + if id >= IntCtrl::PRI_NUN_MAX { interrupt_cpu_enable(id, false); } } -pub fn interrupt_vm_inject(vm: Vm, vcpu: Vcpu, int_id: usize, _source: usize) { - // if vm.id() == 1 { - // println!("inject int {} to vm1", int_id); - // } - // if current_cpu().id == 2 { - // println!("inject int {} to core 2", int_id); - // } - // if current_cpu().id == 1 && int_id == 49 { - // println!("inject int {} to core 1", int_id); - // } +pub fn interrupt_vm_inject(vm: Vm, vcpu: Vcpu, int_id: usize) { if vcpu.phys_id() != current_cpu().id { - println!( + error!( "interrupt_vm_inject: Core {} failed to find target (VCPU {} VM {})", current_cpu().id, vcpu.id(), @@ -155,45 +91,24 @@ pub fn interrupt_vm_inject(vm: Vm, vcpu: Vcpu, int_id: usize, _source: usize) { ); return; } - interrupt_arch_vm_inject(vm, vcpu, int_id); + if let VcpuState::Sleep = vcpu.state() { + current_cpu().cpu_state = crate::kernel::CpuState::CpuRun; + current_cpu().scheduler().wakeup(vcpu.clone()); + } + IntCtrl::vm_inject(vm, vcpu, int_id); } -pub fn interrupt_handler(int_id: usize, src: usize) -> bool { - if interrupt_is_reserved(int_id) { - let irq_handler_list = INTERRUPT_HANDLERS.lock(); - let irq_handler = irq_handler_list.get(&int_id).unwrap().clone(); - drop(irq_handler_list); - match irq_handler { - InterruptHandler::IpiIrqHandler(ipi_handler) => { - ipi_handler(); - } - InterruptHandler::GicMaintenanceHandler(maintenace_handler) => { - maintenace_handler(int_id); - } - InterruptHandler::TimeIrqHandler(timer_irq_handler) => { - timer_irq_handler(int_id); - } - InterruptHandler::None => { - unimplemented!(); - } - } - // drop(irq_handler); +pub fn interrupt_handler(int_id: usize) -> bool { + if let Some(irq_handler) = interrupt_is_reserved(int_id) { + irq_handler(); return true; } - if int_id >= 16 && int_id < 32 { + if (16..32).contains(&int_id) { if let Some(vcpu) = ¤t_cpu().active_vcpu { if let Some(active_vm) = vcpu.vm() { if active_vm.has_interrupt(int_id) { - interrupt_vm_inject(active_vm, vcpu.clone(), int_id, src); - // if current_cpu().id == 1 { - // println!("GICH_MISR {:x}", GICH.misr()); - // println!("GICH_HCR {:x}", GICH.hcr()); - // for i in 0..4 { - // println!("GICH_LR[{}] {:x}", i, GICH.lr(i)); - // } - // println!("interrupt_handler, inject {} to core1", int_id); - // } + interrupt_vm_inject(active_vm, vcpu.clone(), int_id); return false; } else { return true; @@ -202,25 +117,22 @@ pub fn interrupt_handler(int_id: usize, src: usize) -> bool { } } - for vcpu in current_cpu().vcpu_array.iter() { - if let Some(vcpu) = vcpu { - match vcpu.vm() { - Some(vm) => { - if vm.has_interrupt(int_id) { - if vcpu.state() as usize == VcpuState::VcpuInv as usize { - return true; - } - - interrupt_vm_inject(vm, vcpu.clone(), int_id, src); - return false; - } + for vcpu in current_cpu().vcpu_array.iter().flatten() { + if let Some(vm) = vcpu.vm() { + if vm.has_interrupt(int_id) { + if vcpu.state() as usize == VcpuState::Invalid as usize + || vcpu.state() as usize == VcpuState::Sleep as usize + { + return true; } - None => {} + + interrupt_vm_inject(vm, vcpu.clone(), int_id); + return false; } } } - println!( + warn!( "interrupt_handler: core {} receive unsupported int {}", current_cpu().id, int_id @@ -228,6 +140,7 @@ pub fn interrupt_handler(int_id: usize, src: usize) -> bool { true } +/// ipi interrupt handler entry pub fn interrupt_inject_ipi_handler(msg: &IpiMessage) { match &msg.ipi_message { IpiInnerMsg::IntInjectMsg(int_msg) => { @@ -238,13 +151,12 @@ pub fn interrupt_inject_ipi_handler(msg: &IpiMessage) { panic!("inject int {} to illegal cpu {}", int_id, current_cpu().id); } Some(vcpu) => { - interrupt_vm_inject(vcpu.vm().unwrap(), vcpu, int_id, 0); + interrupt_vm_inject(vcpu.vm().unwrap(), vcpu, int_id); } } } _ => { - println!("interrupt_inject_ipi_handler: illegal ipi type"); - return; + error!("interrupt_inject_ipi_handler: illegal ipi type"); } } } diff --git a/src/kernel/iommu.rs b/src/kernel/iommu.rs index fe6a1df17f67d666744165ee6b9f235949fd9137..be0eb1c10927eec1b351b77f866a17b08e1b4d4e 100644 --- a/src/kernel/iommu.rs +++ b/src/kernel/iommu.rs @@ -11,29 +11,32 @@ use crate::arch::{smmu_add_device, smmu_vm_init}; use crate::kernel::Vm; +/// init iommu pub fn iommu_init() { if cfg!(feature = "tx2") { crate::arch::smmu_init(); - println!("IOMMU init ok"); + info!("IOMMU init ok"); } else { - println!("Platform not support IOMMU"); + warn!("Platform not support IOMMU"); } } +/// init iommu for vm pub fn iommmu_vm_init(vm: Vm) -> bool { if cfg!(feature = "tx2") { - return smmu_vm_init(vm); + smmu_vm_init(vm) } else { - println!("Platform not support IOMMU"); - return false; + warn!("Platform not support IOMMU"); + false } } +/// add device to iommu pub fn iommu_add_device(vm: Vm, stream_id: usize) -> bool { if cfg!(feature = "tx2") { - return smmu_add_device(vm.iommu_ctx_id(), stream_id); + smmu_add_device(vm.iommu_ctx_id(), stream_id) } else { - println!("Platform not support IOMMU"); - return false; + warn!("Platform not support IOMMU"); + false } } diff --git a/src/kernel/ipi.rs b/src/kernel/ipi.rs index 7dec7e35edbbda3f1c6a9c26a617296fecbbf32e..d50827089f9ea7dbd931a53737be89c0c8c065f3 100644 --- a/src/kernel/ipi.rs +++ b/src/kernel/ipi.rs @@ -8,11 +8,7 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use alloc::vec::Vec; - -use spin::Mutex; - -use crate::arch::INTERRUPT_IRQ_IPI; +use crate::arch::traits::InterruptController; use crate::board::PLAT_DESC; use crate::device::{VirtioMmio, Virtq}; use crate::kernel::{CPU_IF_LIST, current_cpu, interrupt_cpu_ipi_send}; @@ -30,17 +26,21 @@ pub enum InitcEvent { VgicdSetTrgt, VgicdSetCfg, VgicdRoute, + Vgicdinject, None, } #[derive(Copy, Clone)] +/// CPU Power event enum pub enum PowerEvent { PsciIpiCpuOn, PsciIpiCpuOff, PsciIpiCpuReset, + PsciIpiVcpuAssignAndCpuOn, } #[derive(Copy, Clone)] +/// Event message struct transfered by IPI pub struct IpiInitcMessage { pub event: InitcEvent, pub vm_id: usize, @@ -52,8 +52,10 @@ pub struct IpiInitcMessage { * src: src vm id */ #[derive(Copy, Clone)] +/// Power Message Struct transfered by IPI pub struct IpiPowerMessage { pub src: usize, + pub vcpuid: usize, pub event: PowerEvent, pub entry: usize, pub context: usize, @@ -66,19 +68,30 @@ pub struct IpiPowerMessage { // } #[derive(Copy, Clone)] +/// Ethernet Message Struct transfered by IPI pub struct IpiEthernetMsg { pub src_vmid: usize, pub trgt_vmid: usize, } #[derive(Copy, Clone)] +/// VM Management Message Struct transfered by IPI pub struct IpiVmmMsg { pub vmid: usize, pub event: VmmEvent, } +#[derive(Copy, Clone)] +/// VCPU Message Struct transfered by IPI +pub struct IpiVcpuMsg { + pub vmid: usize, + pub vcpuid: usize, + pub event: VmmEvent, +} + // only support for mediated blk #[derive(Clone)] +/// Mediated Device Message Struct transfered by IPI pub struct IpiMediatedMsg { pub src_id: usize, pub vq: Virtq, @@ -87,11 +100,13 @@ pub struct IpiMediatedMsg { } #[derive(Clone, Copy)] +/// Mediated Device Notify Message Struct transfered by IPI pub struct IpiMediatedNotifyMsg { pub vm_id: usize, } #[derive(Clone, Copy)] +/// HVC Message Struct transfered by IPI pub struct IpiHvcMsg { pub src_vmid: usize, pub trgt_vmid: usize, @@ -100,29 +115,34 @@ pub struct IpiHvcMsg { } #[derive(Clone, Copy)] +/// Interrupt Inject Message Struct transfered by IPI pub struct IpiIntInjectMsg { pub vm_id: usize, pub int_id: usize, } -#[derive(Copy, Clone, Debug)] -pub enum IpiType { - IpiTIntc = 0, - IpiTPower = 1, - IpiTEthernetMsg = 2, - IpiTHyperFresh = 3, - IpiTHvc = 4, - IpiTVMM = 5, - IpiTMediatedDev = 6, - IpiTIntInject = 8, +declare_enum_with_handler! { + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + #[repr(usize)] + pub enum IpiType [pub IPI_HANDLER_LIST => IpiHandlerFunc] { + IpiTIntc => crate::arch::vgic_ipi_handler, + IpiTPower => crate::arch::psci_ipi_handler, + IpiTEthernetMsg => crate::device::ethernet_ipi_rev_handler, + IpiTHvc => crate::kernel::hvc_ipi_handler, + IpiTVMM => crate::vmm::vmm_ipi_handler, + IpiTMediatedDev => crate::device::mediated_ipi_handler, + IpiTIntInject => crate::kernel::interrupt_inject_ipi_handler, + } } #[derive(Clone)] +/// Struct for all types of IPI Message pub enum IpiInnerMsg { Initc(IpiInitcMessage), Power(IpiPowerMessage), EnternetMsg(IpiEthernetMsg), VmmMsg(IpiVmmMsg), + VcpuMsg(IpiVcpuMsg), MediatedMsg(IpiMediatedMsg), MediatedNotifyMsg(IpiMediatedNotifyMsg), HvcMsg(IpiHvcMsg), @@ -131,6 +151,7 @@ pub enum IpiInnerMsg { None, } +/// Struct for IPI Message pub struct IpiMessage { pub ipi_type: IpiType, pub ipi_message: IpiInnerMsg, @@ -140,6 +161,7 @@ const IPI_HANDLER_MAX: usize = 16; pub type IpiHandlerFunc = fn(&IpiMessage); +/// IPI Handler Struct pub struct IpiHandler { pub handler: IpiHandlerFunc, pub ipi_type: IpiType, @@ -151,8 +173,7 @@ impl IpiHandler { } } -pub static IPI_HANDLER_LIST: Mutex> = Mutex::new(Vec::new()); - +/// ipi handler entry, scanning the received ipi list and call the coresponding handler pub fn ipi_irq_handler() { // println!("ipi handler"); let cpu_id = current_cpu().id; @@ -160,57 +181,36 @@ pub fn ipi_irq_handler() { let mut msg: Option = cpu_if_list[cpu_id].pop(); drop(cpu_if_list); - while !msg.is_none() { + while msg.is_some() { let ipi_msg = msg.unwrap(); let ipi_type = ipi_msg.ipi_type as usize; - let ipi_handler_list = IPI_HANDLER_LIST.lock(); - let len = ipi_handler_list.len(); - let handler = ipi_handler_list[ipi_type].handler.clone(); - drop(ipi_handler_list); - - if len <= ipi_type { - println!("illegal ipi type {}", ipi_type) - } else { - // println!("ipi type is {:#?}", ipi_msg.ipi_type); + if let Some(handler) = IPI_HANDLER_LIST.get(ipi_type) { handler(&ipi_msg); + } else { + error!("illegal ipi type {}", ipi_type) } let mut cpu_if_list = CPU_IF_LIST.lock(); msg = cpu_if_list[cpu_id].pop(); } } -pub fn ipi_register(ipi_type: IpiType, handler: IpiHandlerFunc) -> bool { - // check handler max - let mut ipi_handler_list = IPI_HANDLER_LIST.lock(); - for i in 0..ipi_handler_list.len() { - if ipi_type as usize == ipi_handler_list[i].ipi_type as usize { - println!("ipi_register: try to cover exist ipi handler"); - return false; - } - } - - while (ipi_type as usize) >= ipi_handler_list.len() { - ipi_handler_list.push(IpiHandler::new(handler, ipi_type)); - } - ipi_handler_list[ipi_type as usize] = IpiHandler::new(handler, ipi_type); - // ipi_handler_list.push(IpiHandler::new(handler, ipi_type)); - true -} - fn ipi_send(target_id: usize, msg: IpiMessage) -> bool { if target_id >= PLAT_DESC.cpu_desc.num { - println!("ipi_send: core {} not exist", target_id); + warn!("ipi_send: core {} not exist", target_id); return false; } let mut cpu_if_list = CPU_IF_LIST.lock(); cpu_if_list[target_id].msg_queue.push(msg); - interrupt_cpu_ipi_send(target_id, INTERRUPT_IRQ_IPI); + drop(cpu_if_list); + crate::arch::dsb::ishst(); + interrupt_cpu_ipi_send(target_id, crate::arch::IntCtrl::IRQ_IPI); true } +/// send ipi to target cpu pub fn ipi_send_msg(target_id: usize, ipi_type: IpiType, ipi_message: IpiInnerMsg) -> bool { let msg = IpiMessage { ipi_type, ipi_message }; ipi_send(target_id, msg) @@ -223,7 +223,7 @@ pub fn ipi_intra_broadcast_msg(vm: Vm, ipi_type: IpiType, msg: IpiInnerMsg) -> b if ((1 << i) & vm.ncpu()) != 0 && i != current_cpu().id { n += 1; if !ipi_send_msg(i, ipi_type, msg.clone()) { - println!( + error!( "ipi_intra_broadcast_msg: Failed to send ipi request, cpu {} type {}", i, ipi_type as usize ); diff --git a/src/kernel/ivc.rs b/src/kernel/ivc.rs index 790b85d96fd12c5619187578441108d849399efd..0cd6d294f7399e9c797cf211126d075eec0c3e6b 100644 --- a/src/kernel/ivc.rs +++ b/src/kernel/ivc.rs @@ -21,6 +21,7 @@ use crate::mm::PageFrame; pub static SHARED_MEM: Mutex> = Mutex::new(None); pub const SHARED_MEM_SIZE_MAX: usize = 0x200000; +/// Inter-VM Call shared memory update pub fn ivc_update_mq(receive_ipa: usize, cfg_ipa: usize) -> bool { let vm = active_vm().unwrap(); let vm_id = vm.id(); @@ -28,7 +29,7 @@ pub fn ivc_update_mq(receive_ipa: usize, cfg_ipa: usize) -> bool { let cfg_pa = vm_ipa2pa(vm, cfg_ipa); if receive_pa == 0 { - println!("ivc_update_mq: invalid receive_pa"); + error!("ivc_update_mq: invalid receive_pa"); return false; } @@ -42,6 +43,7 @@ pub fn ivc_update_mq(receive_ipa: usize, cfg_ipa: usize) -> bool { true } +/// init memory region shared by VM pub fn mem_shared_mem_init() { let mut shared_mem = SHARED_MEM.lock(); if shared_mem.is_none() { @@ -64,7 +66,7 @@ pub fn shyper_init(vm: Vm, base_ipa: usize, len: usize) -> bool { true } None => { - println!("shyper_init: shared mem should not be None"); + error!("shyper_init: shared mem should not be None"); false } } diff --git a/src/kernel/live_update.rs b/src/kernel/live_update.rs deleted file mode 100644 index 46973ca992eb4fec876c7092133cb0f69e480228..0000000000000000000000000000000000000000 --- a/src/kernel/live_update.rs +++ /dev/null @@ -1,1106 +0,0 @@ -// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. -// Rust-Shyper is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use alloc::boxed::Box; -use alloc::collections::{BTreeMap, BTreeSet, LinkedList}; -use alloc::string::String; -use alloc::sync::Arc; -use alloc::vec::Vec; - -use spin::{Mutex, RwLock}; - -use crate::arch::{ - emu_intc_handler, emu_smmu_handler, GIC_LRS_NUM, gic_maintenance_handler, gicc_clear_current_irq, INTERRUPT_EN_SET, - PageTable, partial_passthrough_intc_handler, psci_ipi_handler, SMMU_V2, SmmuV2, TIMER_FREQ, TIMER_SLICE, Vgic, - vgic_ipi_handler, -}; -use crate::board::PLAT_DESC; -use crate::config::{ - DEF_VM_CONFIG_TABLE, vm_cfg_entry, VmConfigEntry, VmConfigTable, VmDtbDevConfig, VMDtbDevConfigList, - VmEmulatedDeviceConfig, VmEmulatedDeviceConfigList, VmMemoryConfig, VmPassthroughDeviceConfig, -}; -use crate::device::{ - BlkIov, EMU_DEVS_LIST, emu_virtio_mmio_handler, EmuDevEntry, EmuDeviceType, EmuDevs, ethernet_ipi_rev_handler, - MEDIATED_BLK_LIST, mediated_ipi_handler, MediatedBlk, virtio_blk_notify_handler, virtio_console_notify_handler, - virtio_mediated_blk_notify_handler, virtio_net_notify_handler, VirtioMmio, -}; -use crate::kernel::{ - async_blk_io_req, ASYNC_EXE_STATUS, ASYNC_IO_TASK_LIST, async_ipi_req, ASYNC_IPI_TASK_LIST, ASYNC_USED_INFO_LIST, - AsyncExeStatus, AsyncTask, AsyncTaskData, CPU, Cpu, cpu_idle, CPU_IF_LIST, CpuIf, CpuState, current_cpu, FairQueue, - HEAP_REGION, HeapRegion, hvc_ipi_handler, INTERRUPT_GLB_BITMAP, INTERRUPT_HANDLERS, INTERRUPT_HYPER_BITMAP, - interrupt_inject_ipi_handler, InterruptHandler, IoAsyncMsg, IPI_HANDLER_LIST, ipi_irq_handler, ipi_register, - ipi_send_msg, IpiHandler, IpiInnerMsg, IpiMediatedMsg, IpiMessage, IpiType, mem_heap_region_init, SchedType, - SchedulerUpdate, SHARE_MEM_LIST, timer_irq_handler, UsedInfo, Vcpu, VCPU_LIST, VcpuInner, vm, Vm, VM_IF_LIST, - vm_ipa2pa, VM_LIST, VM_NUM_MAX, VM_REGION, VmInterface, VmRegion, logger_init, -}; -use crate::lib::{BitAlloc256, BitMap, FlexBitmap, time_current_us}; -use crate::mm::{heap_init, PageFrame}; -use crate::vmm::vmm_ipi_handler; - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum FreshStatus { - Start, - FreshVM, - FreshVCPU, - Finish, - None, -} - -#[cfg(feature = "update")] -static FRESH_STATUS: RwLock = RwLock::new(FreshStatus::Start); -#[cfg(not(feature = "update"))] -static FRESH_STATUS: RwLock = RwLock::new(FreshStatus::None); - -pub static FRESH_LOGIC_LOCK: Mutex<()> = Mutex::new(()); -pub static FRESH_IRQ_LOGIC_LOCK: Mutex<()> = Mutex::new(()); -// static FRESH_STATUS: FreshStatus = FreshStatus::None; - -fn set_fresh_status(status: FreshStatus) { - *FRESH_STATUS.write() = status; -} - -pub fn fresh_status() -> FreshStatus { - *FRESH_STATUS.read() -} - -#[cfg(not(feature = "update"))] -pub const UPDATE_IMG_BASE_ADDR: usize = 0x8a000000; -#[cfg(feature = "update")] -pub const UPDATE_IMG_BASE_ADDR: usize = 0x83000000; - -#[repr(C)] -pub struct HypervisorAddr { - cpu_id: usize, - vm_list: usize, - vm_config_table: usize, - vcpu_list: usize, - cpu: usize, - emu_dev_list: usize, - interrupt_hyper_bitmap: usize, - interrupt_glb_bitmap: usize, - interrupt_en_set: usize, - interrupt_handlers: usize, - vm_region: usize, - heap_region: usize, - vm_if_list: usize, - gic_lrs_num: usize, - // address for ipi - cpu_if_list: usize, - ipi_handler_list: usize, - // arch time - time_freq: usize, - time_slice: usize, - // mediated blk - mediated_blk_list: usize, - // async task - async_exe_status: usize, - async_ipi_task_list: usize, - async_io_task_list: usize, - async_used_info_list: usize, - // shared mem - shared_mem_list: usize, - // smmu_v2 - smmu_v2: usize, -} - -pub fn hyper_fresh_ipi_handler(_msg: &IpiMessage) { - update_request(); -} - -pub fn update_request() { - // println!("Src Hypervisor Core[{}] send update request", current_cpu().id); - extern "C" { - pub fn update_request(address_list: &HypervisorAddr, alloc: bool); - } - // VM_STATE_FLAG UNILIB_FS_LIST SYSTEM_FDT - let vm_config_table = &DEF_VM_CONFIG_TABLE as *const _ as usize; - let emu_dev_list = &EMU_DEVS_LIST as *const _ as usize; - let interrupt_hyper_bitmap = &INTERRUPT_HYPER_BITMAP as *const _ as usize; - let interrupt_glb_bitmap = &INTERRUPT_GLB_BITMAP as *const _ as usize; - let interrupt_en_set = &INTERRUPT_EN_SET as *const _ as usize; - let interrupt_handlers = &INTERRUPT_HANDLERS as *const _ as usize; - let vm_region = &VM_REGION as *const _ as usize; - let heap_region = &HEAP_REGION as *const _ as usize; - let vm_list = &VM_LIST as *const _ as usize; - let vm_if_list = &VM_IF_LIST as *const _ as usize; - let vcpu_list = &VCPU_LIST as *const _ as usize; - let cpu = unsafe { &CPU as *const _ as usize }; - let cpu_if_list = &CPU_IF_LIST as *const _ as usize; - let gic_lrs_num = &GIC_LRS_NUM as *const _ as usize; - let ipi_handler_list = &IPI_HANDLER_LIST as *const _ as usize; - let time_freq = &TIMER_FREQ as *const _ as usize; - let time_slice = &TIMER_SLICE as *const _ as usize; - let mediated_blk_list = &MEDIATED_BLK_LIST as *const _ as usize; - let async_exe_status = &ASYNC_EXE_STATUS as *const _ as usize; - let async_ipi_task_list = &ASYNC_IPI_TASK_LIST as *const _ as usize; - let async_io_task_list = &ASYNC_IO_TASK_LIST as *const _ as usize; - let async_used_info_list = &ASYNC_USED_INFO_LIST as *const _ as usize; - let shared_mem_list = &SHARE_MEM_LIST as *const _ as usize; - let smmu_v2 = &SMMU_V2 as *const _ as usize; - - let addr_list = HypervisorAddr { - cpu_id: current_cpu().id, - vm_config_table, - emu_dev_list, - interrupt_hyper_bitmap, - interrupt_glb_bitmap, - interrupt_en_set, - interrupt_handlers, - vm_region, - heap_region, - vm_list, - vm_if_list, - vcpu_list, - cpu, - cpu_if_list, - gic_lrs_num, - ipi_handler_list, - time_freq, - time_slice, - mediated_blk_list, - async_exe_status, - async_ipi_task_list, - async_io_task_list, - async_used_info_list, - shared_mem_list, - smmu_v2, - }; - if current_cpu().id == 0 { - unsafe { - update_request(&addr_list, true); - } - for cpu_id in 0..PLAT_DESC.cpu_desc.num { - if cpu_id != current_cpu().id { - ipi_send_msg(cpu_id, IpiType::IpiTHyperFresh, IpiInnerMsg::HyperFreshMsg()); - } - } - } - unsafe { - update_request(&addr_list, false); - } -} - -#[no_mangle] -pub extern "C" fn rust_shyper_update(address_list: &HypervisorAddr, alloc: bool) { - // TODO: vm0_dtb? - // let mut time0 = 0; - // let mut time1 = 0; - if alloc { - // cpu id is 0 - heap_init(); - mem_heap_region_init(); - // alloc and pre_copy - unsafe { - // DEF_VM_CONFIG_TABLE - let vm_config_table = &*(address_list.vm_config_table as *const Mutex); - vm_config_table_update(vm_config_table); - - // VM_LIST - let vm_list = &*(address_list.vm_list as *const Mutex>); - vm_list_alloc(vm_list); - - // VCPU_LIST - let vcpu_list = &*(address_list.vcpu_list as *const Mutex>); - vcpu_list_alloc(vcpu_list); - - // CPU_IF - let cpu_if = &*(address_list.cpu_if_list as *const Mutex>); - cpu_if_alloc(cpu_if); - - // IPI_HANDLER_LIST - let ipi_handler_list = &*(address_list.ipi_handler_list as *const Mutex>); - ipi_handler_list_update(ipi_handler_list); - - // TIMER_FREQ & TIMER_SLICE - let time_freq = &*(address_list.time_freq as *const Mutex); - let time_slice = &*(address_list.time_slice as *const Mutex); - arch_time_update(time_freq, time_slice); - - // INTERRUPT_HYPER_BITMAP, INTERRUPT_GLB_BITMAP, INTERRUPT_HANDLERS - let interrupt_hyper_bitmap = &*(address_list.interrupt_hyper_bitmap as *const Mutex>); - let interrupt_glb_bitmap = &*(address_list.interrupt_glb_bitmap as *const Mutex>); - let interrutp_en_set = &*(address_list.interrupt_en_set as *const Mutex>); - let interrupt_handlers = - &*(address_list.interrupt_handlers as *const Mutex>); - interrupt_update( - interrupt_hyper_bitmap, - interrupt_glb_bitmap, - interrutp_en_set, - interrupt_handlers, - ); - - // EMU_DEVS_LIST - let emu_dev_list = &*(address_list.emu_dev_list as *const Mutex>); - emu_dev_list_update(emu_dev_list); - - // GIC_LRS_NUM - let gic_lrs_num = &*(address_list.gic_lrs_num as *const Mutex); - gic_lrs_num_update(gic_lrs_num); - } - println!("Finish Alloc VM / VCPU / CPU_IF"); - return; - } - - if address_list.cpu_id == 0 { - let lock0 = FRESH_LOGIC_LOCK.lock(); - let lock1 = FRESH_IRQ_LOGIC_LOCK.lock(); - // set_fresh_status(FreshStatus::Start); - unsafe { - // VM_LIST - let time0 = time_current_us(); - let vm_list = &*(address_list.vm_list as *const Mutex>); - vm_list_update(vm_list); - set_fresh_status(FreshStatus::FreshVM); - let time1 = time_current_us(); - - // VCPU_LIST (add vgic) - let vcpu_list = &*(address_list.vcpu_list as *const Mutex>); - vcpu_update(vcpu_list, vm_list); - let time2 = time_current_us(); - drop(lock1); - set_fresh_status(FreshStatus::FreshVCPU); - let time3 = time_current_us(); - - // CPU: Must update after vcpu and vm - let cpu = &*(address_list.cpu as *const Cpu); - current_cpu_update(cpu); - - // VM_REGION - let vm_region = &*(address_list.vm_region as *const Mutex); - vm_region_update(vm_region); - - // HEAP_REGION - let heap_region = &*(address_list.heap_region as *const Mutex); - heap_region_update(heap_region); - - // VM_IF_LIST - let vm_if_list = &*(address_list.vm_if_list as *const [Mutex; VM_NUM_MAX]); - vm_if_list_update(vm_if_list); - - // MEDIATED_BLK_LIST - let mediated_blk_list = &*(address_list.mediated_blk_list as *const Mutex>); - mediated_blk_list_update(mediated_blk_list); - - // SHARED_MEM_LIST - let shared_mem_list = &*(address_list.shared_mem_list as *const Mutex>); - shared_mem_list_update(shared_mem_list); - - // cpu_if_list - let cpu_if = &*(address_list.cpu_if_list as *const Mutex>); - cpu_if_update(cpu_if); - - // ASYNC_EXE_STATUS、ASYNC_IPI_TASK_LIST、ASYNC_IO_TASK_LIST、ASYNC_USED_INFO_LIST - let async_exe_status = &*(address_list.async_exe_status as *const Mutex); - let async_ipi_task_list = &*(address_list.async_ipi_task_list as *const Mutex>); - let async_io_task_list = &*(address_list.async_io_task_list as *const Mutex>); - let async_used_info_list = - &*(address_list.async_used_info_list as *const Mutex>>); - async_task_update( - async_exe_status, - async_ipi_task_list, - async_io_task_list, - async_used_info_list, - ); - - // SMMU_V2 - let smmu_v2 = &*(address_list.smmu_v2 as *const Mutex); - smmu_update(smmu_v2); - // LOGGER - let _ = logger_init(); - set_fresh_status(FreshStatus::Finish); - drop(lock0); - println!( - "handle VM {} us, handle VCPU {} us, free lock {} us", - time1 - time0, - time2 - time1, - time3 - time2 - ); - println!("Finish Update VM and VCPU_LIST"); - println!("Update CPU[{}]", cpu.id); - println!("Update {} region for VM_REGION", VM_REGION.lock().region.len()); - println!("Update HEAP_REGION"); - println!("Update VM_IF_LIST"); - println!("Update {} Mediated BLK", MEDIATED_BLK_LIST.lock().len()); - println!("Update {} SHARE_MEM_LIST", SHARE_MEM_LIST.lock().len()); - println!("Update CPU_IF_LIST"); - } - } else { - let cpu = unsafe { &*(address_list.cpu as *const Cpu) }; - // let time0 = time_current_us(); - // CPU: Must update after vcpu and vm alloc - current_cpu_update(cpu); - // let time1 = time_current_us(); - // println!("Update CPU[{}], time {}us", cpu.id, time1 - time0); - } - // barrier(); - // if current_cpu().id != 0 { - // println!("Core[{}] handle time {}", current_cpu().id, time1 - time0,); - // } - fresh_hyper(); -} - -pub fn fresh_hyper() { - extern "C" { - pub fn fresh_cpu(); - pub fn fresh_hyper(ctx: usize); - } - if current_cpu().id == 0 { - let ctx = current_cpu().ctx.unwrap(); - println!("CPU[{}] ctx {:x}", current_cpu().id, ctx); - current_cpu().clear_ctx(); - unsafe { fresh_hyper(ctx) }; - } else { - match current_cpu().cpu_state { - CpuState::CpuInv => { - panic!("Core[{}] state {:#?}", current_cpu().id, CpuState::CpuInv); - } - CpuState::CpuIdle => { - println!("Core[{}] state {:#?}", current_cpu().id, CpuState::CpuIdle); - unsafe { fresh_cpu() }; - // println!( - // "Core[{}] current cpu irq {}", - // current_cpu().id, - // current_cpu().current_irq - // ); - gicc_clear_current_irq(true); - cpu_idle(); - } - CpuState::CpuRun => { - println!("Core[{}] state {:#?}", current_cpu().id, CpuState::CpuRun); - // println!( - // "Core[{}] current cpu irq {}", - // current_cpu().id, - // current_cpu().current_irq - // ); - gicc_clear_current_irq(true); - let ctx = current_cpu().ctx.unwrap(); - current_cpu().clear_ctx(); - unsafe { fresh_hyper(ctx) }; - } - } - } -} - -pub fn shared_mem_list_update(src_shared_mem_list: &Mutex>) { - let mut shared_mem_list = SHARE_MEM_LIST.lock(); - for (key, val) in src_shared_mem_list.lock().iter() { - shared_mem_list.insert(*key, *val); - } -} - -pub fn async_task_update( - src_async_exe_status: &Mutex, - src_async_ipi_task_list: &Mutex>, - src_async_io_task_list: &Mutex>, - src_async_used_info_list: &Mutex>>, -) { - let mut async_exe_status = ASYNC_EXE_STATUS.lock(); - let mut async_ipi_task_list = ASYNC_IPI_TASK_LIST.lock(); - let mut async_io_task_list = ASYNC_IO_TASK_LIST.lock(); - let mut async_used_info_list = ASYNC_USED_INFO_LIST.lock(); - assert_eq!(async_ipi_task_list.len(), 0); - assert_eq!(async_io_task_list.len(), 0); - assert_eq!(async_used_info_list.len(), 0); - *async_exe_status = *src_async_exe_status.lock(); - for ipi_task in src_async_ipi_task_list.lock().iter() { - let vm_id = ipi_task.src_vmid; - let vm = vm(vm_id).unwrap(); - let task_data = match &ipi_task.task_data { - AsyncTaskData::AsyncIpiTask(mediated_msg) => { - assert_eq!(mediated_msg.src_id, vm_id); - let mmio_id = mediated_msg.blk.id(); - let vq_idx = mediated_msg.vq.vq_indx(); - match vm.emu_dev(mmio_id) { - EmuDevs::VirtioBlk(blk) => { - let new_vq = blk.vq(vq_idx).clone().unwrap(); - AsyncTaskData::AsyncIpiTask(IpiMediatedMsg { - src_id: vm_id, - vq: new_vq.clone(), - blk: blk.clone(), - }) - } - _ => panic!("illegal mmio dev type in async_task_update"), - } - } - AsyncTaskData::AsyncIoTask(_) => panic!("Find an IO Task in IPI task list"), - AsyncTaskData::AsyncNoneTask(_) => panic!("Find an IO None Task in IPI task list"), - }; - async_ipi_task_list.push_back(AsyncTask { - task_data, - src_vmid: vm_id, - state: Arc::new(Mutex::new(*ipi_task.state.lock())), - task: Arc::new(Mutex::new(Box::pin(async_ipi_req()))), - }) - } - // for io_task in src_async_io_task_list.lock().iter() { - while !src_async_io_task_list.lock().is_empty() { - let io_task = src_async_io_task_list.lock().pop_front().unwrap(); - let vm_id = io_task.src_vmid; - let vm = vm(vm_id).unwrap(); - let task_data = match &io_task.task_data { - AsyncTaskData::AsyncIpiTask(_) => panic!("Find an IPI Task in IO task list"), - AsyncTaskData::AsyncIoTask(io_msg) => { - assert_eq!(vm_id, io_msg.src_vmid); - let vq_idx = io_msg.vq.vq_indx(); - match vm.emu_blk_dev() { - EmuDevs::VirtioBlk(blk) => { - let new_vq = blk.vq(vq_idx).clone().unwrap(); - AsyncTaskData::AsyncIoTask(IoAsyncMsg { - src_vmid: vm_id, - vq: new_vq.clone(), - dev: blk.clone(), - io_type: io_msg.io_type, - blk_id: io_msg.blk_id, - sector: io_msg.sector, - count: io_msg.count, - cache: io_msg.cache, - iov_list: Arc::new({ - let mut list = vec![]; - for iov in io_msg.iov_list.iter() { - list.push(BlkIov { - data_bg: iov.data_bg, - len: iov.len, - }); - } - list - }), - }) - } - _ => panic!("illegal mmio dev type in async_task_update"), - } - } - _ => { - todo!() - } - }; - async_io_task_list.push_back(AsyncTask { - task_data, - src_vmid: vm_id, - state: Arc::new(Mutex::new(*io_task.state.lock())), - task: Arc::new(Mutex::new(Box::pin(async_blk_io_req()))), - }); - } - for (key, used_info) in src_async_used_info_list.lock().iter() { - let mut new_used_info = LinkedList::new(); - for info in used_info.iter() { - new_used_info.push_back(UsedInfo { - desc_chain_head_idx: info.desc_chain_head_idx, - used_len: info.used_len, - }); - } - async_used_info_list.insert(*key, new_used_info); - } - // println!("Update {} ipi task for ASYNC_IPI_TASK_LIST", async_ipi_task_list.len()); - // println!("Update {} io task for ASYNC_IO_TASK_LIST", async_io_task_list.len()); - // println!( - // "Update {} used info for ASYNC_USED_INFO_LIST", - // async_used_info_list.len() - // ); -} - -pub fn mediated_blk_list_update(src_mediated_blk_list: &Mutex>) { - let mut mediated_blk_list = MEDIATED_BLK_LIST.lock(); - assert_eq!(mediated_blk_list.len(), 0); - mediated_blk_list.clear(); - for blk in src_mediated_blk_list.lock().iter() { - mediated_blk_list.push(MediatedBlk { - base_addr: blk.base_addr, - avail: blk.avail, - }); - } -} - -pub fn arch_time_update(src_time_freq: &Mutex, src_time_slice: &Mutex) { - *TIMER_FREQ.lock() = *src_time_freq.lock(); - *TIMER_SLICE.lock() = *src_time_slice.lock(); -} - -pub fn cpu_if_alloc(src_cpu_if: &Mutex>) { - let mut cpu_if_list = CPU_IF_LIST.lock(); - for _ in 0..src_cpu_if.lock().len() { - cpu_if_list.push(CpuIf::default()); - } -} - -pub fn cpu_if_update(src_cpu_if: &Mutex>) { - let mut cpu_if_list = CPU_IF_LIST.lock(); - assert_eq!(cpu_if_list.len(), src_cpu_if.lock().len()); - for (idx, cpu_if) in src_cpu_if.lock().iter().enumerate() { - for (msg_idx, msg) in cpu_if.msg_queue.iter().enumerate() { - // Copy ipi msg - let new_ipi_msg = match msg.ipi_message.clone() { - IpiInnerMsg::Initc(initc) => IpiInnerMsg::Initc(initc), - IpiInnerMsg::Power(power) => IpiInnerMsg::Power(power), - IpiInnerMsg::EnternetMsg(eth_msg) => IpiInnerMsg::EnternetMsg(eth_msg), - IpiInnerMsg::VmmMsg(vmm_msg) => IpiInnerMsg::VmmMsg(vmm_msg), - IpiInnerMsg::MediatedMsg(mediated_msg) => { - let mmio_id = mediated_msg.blk.id(); - let vm_id = mediated_msg.src_id; - let vq_idx = mediated_msg.vq.vq_indx(); - - let vm = vm(vm_id).unwrap(); - match vm.emu_dev(mmio_id) { - EmuDevs::VirtioBlk(blk) => { - let new_vq = blk.vq(vq_idx).clone().unwrap(); - IpiInnerMsg::MediatedMsg(IpiMediatedMsg { - src_id: vm_id, - vq: new_vq.clone(), - blk: blk.clone(), - }) - } - _ => { - panic!("illegal mmio dev type in cpu_if_update"); - } - } - } - IpiInnerMsg::MediatedNotifyMsg(notify_msg) => IpiInnerMsg::MediatedNotifyMsg(notify_msg), - IpiInnerMsg::HvcMsg(hvc_msg) => IpiInnerMsg::HvcMsg(hvc_msg), - IpiInnerMsg::IntInjectMsg(inject_msg) => IpiInnerMsg::IntInjectMsg(inject_msg), - IpiInnerMsg::HyperFreshMsg() => IpiInnerMsg::HyperFreshMsg(), - IpiInnerMsg::None => IpiInnerMsg::None, - }; - cpu_if_list[idx].msg_queue.insert( - msg_idx, - IpiMessage { - ipi_type: msg.ipi_type, - ipi_message: new_ipi_msg, - }, - ); - } - // println!( - // "Update {} ipi msg for CpuIf[{}], after update len is {}", - // cpu_if.msg_queue.len(), - // idx, - // cpu_if_list[idx].msg_queue.len() - // ); - } -} - -pub fn ipi_handler_list_update(src_ipi_handler_list: &Mutex>) { - for ipi_handler in src_ipi_handler_list.lock().iter() { - let handler = match ipi_handler.ipi_type { - IpiType::IpiTIntc => vgic_ipi_handler, - IpiType::IpiTPower => psci_ipi_handler, - IpiType::IpiTEthernetMsg => ethernet_ipi_rev_handler, - IpiType::IpiTHvc => hvc_ipi_handler, - IpiType::IpiTVMM => vmm_ipi_handler, - IpiType::IpiTMediatedDev => mediated_ipi_handler, - IpiType::IpiTIntInject => interrupt_inject_ipi_handler, - IpiType::IpiTHyperFresh => hyper_fresh_ipi_handler, - }; - ipi_register(ipi_handler.ipi_type, handler); - } - println!("Update IPI_HANDLER_LIST"); -} - -pub fn vm_if_list_update(src_vm_if_list: &[Mutex; VM_NUM_MAX]) { - for (idx, vm_if_lock) in src_vm_if_list.iter().enumerate() { - let vm_if = vm_if_lock.lock(); - let mut cur_vm_if = VM_IF_LIST[idx].lock(); - cur_vm_if.master_cpu_id = vm_if.master_cpu_id; - cur_vm_if.state = vm_if.state; - cur_vm_if.vm_type = vm_if.vm_type; - cur_vm_if.mac = vm_if.mac; - cur_vm_if.ivc_arg = vm_if.ivc_arg; - cur_vm_if.ivc_arg_ptr = vm_if.ivc_arg_ptr; - cur_vm_if.mem_map = match &vm_if.mem_map { - None => None, - Some(mem_map) => Some(FlexBitmap { - len: mem_map.len, - map: { - let mut map = vec![]; - for v in mem_map.map.iter() { - map.push(*v); - } - map - }, - }), - }; - cur_vm_if.mem_map_cache = match &vm_if.mem_map_cache { - None => None, - Some(cache) => Some(Arc::new(PageFrame::new(cache.pa, cache.page_num))), - }; - } -} - -pub fn current_cpu_update(src_cpu: &Cpu) { - let cpu = current_cpu(); - // only need to alloc a new VcpuPool from heap, other props all map at 0x400000000 - // current_cpu().sched = src_cpu.sched; - match &src_cpu.sched { - SchedType::SchedRR(rr) => { - cpu.sched = SchedType::SchedRR(rr.update()); - } - SchedType::None => { - cpu.sched = SchedType::None; - } - } - - assert_eq!(cpu.id, src_cpu.id); - assert_eq!(cpu.ctx, src_cpu.ctx); - assert_eq!(cpu.cpu_state, src_cpu.cpu_state); - assert_eq!(cpu.current_irq, src_cpu.current_irq); - assert_eq!(cpu.cpu_pt, src_cpu.cpu_pt); - assert_eq!(cpu.stack, src_cpu.stack); - println!("Update CPU[{}]", cpu.id); -} - -pub fn gic_lrs_num_update(src_gic_lrs_num: &Mutex) { - let gic_lrs_num = *src_gic_lrs_num.lock(); - *GIC_LRS_NUM.lock() = gic_lrs_num; - println!("Update GIC_LRS_NUM"); -} - -// alloc vm_list -pub fn vm_list_alloc(src_vm_list: &Mutex>) { - let mut vm_list = VM_LIST.lock(); - for vm in src_vm_list.lock().iter() { - let new_vm = Vm::new(vm.id()); - vm_list.push(new_vm.clone()); - let mut dst_inner = new_vm.inner.lock(); - let src_inner = vm.inner.lock(); - let pt = match &src_inner.pt { - None => None, - Some(page_table) => { - let new_page_table = PageTable { - directory: Arc::new(PageFrame::new(page_table.directory.pa, page_table.directory.page_num)), - pages: Arc::new(Mutex::new(vec![])), - }; - for page in page_table.pages.lock().iter() { - new_page_table.pages.lock().push(PageFrame::new(page.pa, page.page_num)); - } - Some(new_page_table) - } - }; - dst_inner.ready = src_inner.ready; - dst_inner.config = vm_cfg_entry(src_inner.id); - dst_inner.pt = pt; - dst_inner.mem_region_num = src_inner.mem_region_num; - dst_inner.pa_region = { - let mut pa_region = vec![]; - for region in src_inner.pa_region.iter() { - pa_region.push(*region); - } - pa_region - }; - dst_inner.entry_point = src_inner.entry_point; - dst_inner.has_master = src_inner.has_master; - dst_inner.cpu_num = src_inner.cpu_num; - dst_inner.ncpu = src_inner.ncpu; - dst_inner.intc_dev_id = src_inner.intc_dev_id; - dst_inner.int_bitmap = src_inner.int_bitmap; - dst_inner.share_mem_base = src_inner.share_mem_base; - dst_inner.migrate_save_pf = { - let mut pf = vec![]; - for page in src_inner.migrate_save_pf.iter() { - pf.push(PageFrame::new(page.pa, page.page_num)); - } - pf - }; - dst_inner.migrate_restore_pf = { - let mut pf = vec![]; - for page in src_inner.migrate_restore_pf.iter() { - pf.push(PageFrame::new(page.pa, page.page_num)); - } - pf - }; - dst_inner.med_blk_id = src_inner.med_blk_id; - } - assert_eq!(vm_list.len(), src_vm_list.lock().len()); - println!("Alloc {} VM in VM_LIST", vm_list.len()); -} - -// Set vm.vcpu_list in vcpu_update -pub fn vm_list_update(src_vm_list: &Mutex>) { - // let mut vm_list = VM_LIST.lock(); - assert_eq!(VM_LIST.lock().len(), src_vm_list.lock().len()); - // vm_list.clear(); - // drop(vm_list); - for (idx, vm) in src_vm_list.lock().iter().enumerate() { - let emu_devs = { - let mut emu_devs = vec![]; - // drop(old_inner); - let old_emu_devs = vm.inner.lock().emu_devs.clone(); - for dev in old_emu_devs.iter() { - // TODO: wip - let new_dev = match dev { - EmuDevs::Vgic(_) => { - // set vgic after vcpu update - EmuDevs::None - } - EmuDevs::VirtioBlk(blk) => { - let mmio = VirtioMmio::new(0); - assert_eq!( - (blk.vq(0).unwrap().desc_table()), - vm_ipa2pa(vm.clone(), blk.vq(0).unwrap().desc_table_addr()) - ); - assert_eq!( - (blk.vq(0).unwrap().used()), - vm_ipa2pa(vm.clone(), blk.vq(0).unwrap().used_addr()) - ); - assert_eq!( - (blk.vq(0).unwrap().avail()), - vm_ipa2pa(vm.clone(), blk.vq(0).unwrap().avail_addr()) - ); - mmio.save_mmio( - blk.clone(), - if blk.dev().mediated() { - Some(virtio_mediated_blk_notify_handler) - } else { - Some(virtio_blk_notify_handler) - }, - ); - EmuDevs::VirtioBlk(mmio) - } - EmuDevs::VirtioNet(net) => { - let mmio = VirtioMmio::new(0); - assert_eq!( - (net.vq(0).unwrap().desc_table()), - vm_ipa2pa(vm.clone(), net.vq(0).unwrap().desc_table_addr()) - ); - assert_eq!( - (net.vq(0).unwrap().used()), - vm_ipa2pa(vm.clone(), net.vq(0).unwrap().used_addr()) - ); - assert_eq!( - (net.vq(0).unwrap().avail()), - vm_ipa2pa(vm.clone(), net.vq(0).unwrap().avail_addr()) - ); - println!("VirtioNet save handler {:x}", unsafe { - *(&virtio_net_notify_handler as *const _ as *const usize) - }); - mmio.save_mmio(net.clone(), Some(virtio_net_notify_handler)); - EmuDevs::VirtioNet(mmio) - } - EmuDevs::VirtioConsole(console) => { - let mmio = VirtioMmio::new(0); - assert_eq!( - (console.vq(0).unwrap().desc_table()), - vm_ipa2pa(vm.clone(), console.vq(0).unwrap().desc_table_addr()) - ); - assert_eq!( - (console.vq(0).unwrap().used()), - vm_ipa2pa(vm.clone(), console.vq(0).unwrap().used_addr()) - ); - assert_eq!( - (console.vq(0).unwrap().avail()), - vm_ipa2pa(vm.clone(), console.vq(0).unwrap().avail_addr()) - ); - println!("VirtioConsole save handler {:x}", unsafe { - *(&virtio_console_notify_handler as *const _ as *const usize) - }); - mmio.save_mmio(console.clone(), Some(virtio_console_notify_handler)); - EmuDevs::VirtioConsole(mmio) - } - EmuDevs::None => EmuDevs::None, - }; - emu_devs.push(new_dev); - } - emu_devs - }; - let dst_vm = VM_LIST.lock()[idx].clone(); - let mut dst_inner = dst_vm.inner.lock(); - let src_inner = vm.inner.lock(); - assert_eq!(dst_inner.id, src_inner.id); - dst_inner.emu_devs = emu_devs; - } - // println!("Update VM_LIST"); -} - -pub fn heap_region_update(src_heap_region: &Mutex) { - let mut heap_region = HEAP_REGION.lock(); - let src_region = src_heap_region.lock(); - heap_region.map = src_region.map; - heap_region.region = src_region.region; - assert_eq!(heap_region.region, src_region.region); -} - -pub fn vm_region_update(src_vm_region: &Mutex) { - let mut vm_region = VM_REGION.lock(); - assert_eq!(vm_region.region.len(), 0); - vm_region.region.clear(); - for mem_region in src_vm_region.lock().region.iter() { - vm_region.region.push(*mem_region); - } - assert_eq!(vm_region.region, src_vm_region.lock().region); -} - -pub fn interrupt_update( - src_hyper_bitmap: &Mutex>, - src_glb_bitmap: &Mutex>, - src_en_set: &Mutex>, - src_handlers: &Mutex>, -) { - let mut hyper_bitmap = INTERRUPT_HYPER_BITMAP.lock(); - *hyper_bitmap = *src_hyper_bitmap.lock(); - let mut glb_bitmap = INTERRUPT_GLB_BITMAP.lock(); - *glb_bitmap = *src_glb_bitmap.lock(); - let mut handlers = INTERRUPT_HANDLERS.lock(); - for (int_id, handler) in src_handlers.lock().iter() { - match handler { - InterruptHandler::IpiIrqHandler(_) => { - handlers.insert(*int_id, InterruptHandler::IpiIrqHandler(ipi_irq_handler)); - } - InterruptHandler::GicMaintenanceHandler(_) => { - handlers.insert( - *int_id, - InterruptHandler::GicMaintenanceHandler(gic_maintenance_handler), - ); - } - InterruptHandler::TimeIrqHandler(_) => { - handlers.insert(*int_id, InterruptHandler::TimeIrqHandler(timer_irq_handler)); - } - InterruptHandler::None => { - handlers.insert(*int_id, InterruptHandler::None); - } - } - } - let mut en_set = INTERRUPT_EN_SET.lock(); - (*en_set).extend(&*src_en_set.lock()); - println!("Update INTERRUPT_GLB_BITMAP / INTERRUPT_HYPER_BITMAP / INTERRUPT_EN_SET / INTERRUPT_HANDLERS"); -} - -pub fn emu_dev_list_update(src_emu_dev_list: &Mutex>) { - let mut emu_dev_list = EMU_DEVS_LIST.lock(); - assert_eq!(emu_dev_list.len(), 0); - emu_dev_list.clear(); - for emu_dev_entry in src_emu_dev_list.lock().iter() { - let emu_handler = match emu_dev_entry.emu_type { - EmuDeviceType::EmuDeviceTGicd => emu_intc_handler, - EmuDeviceType::EmuDeviceTGPPT => partial_passthrough_intc_handler, - EmuDeviceType::EmuDeviceTVirtioBlk => emu_virtio_mmio_handler, - EmuDeviceType::EmuDeviceTVirtioNet => emu_virtio_mmio_handler, - EmuDeviceType::EmuDeviceTVirtioConsole => emu_virtio_mmio_handler, - EmuDeviceType::EmuDeviceTIOMMU => emu_smmu_handler, - _ => { - panic!("not support emu dev entry type {}", emu_dev_entry.emu_type); - } - }; - emu_dev_list.push(EmuDevEntry { - emu_type: emu_dev_entry.emu_type, - vm_id: emu_dev_entry.vm_id, - id: emu_dev_entry.id, - ipa: emu_dev_entry.ipa, - size: emu_dev_entry.size, - handler: emu_handler, - }); - } - println!("Update {} emu dev for EMU_DEVS_LIST", emu_dev_list.len()); -} - -pub fn vm_config_table_update(src_vm_config_table: &Mutex) { - let mut vm_config_table = DEF_VM_CONFIG_TABLE.lock(); - let src_config_table = src_vm_config_table.lock(); - vm_config_table.name = src_config_table.name; - vm_config_table.vm_bitmap = src_config_table.vm_bitmap; - vm_config_table.vm_num = src_config_table.vm_num; - assert_eq!(vm_config_table.entries.len(), 0); - vm_config_table.entries.clear(); - for entry in src_config_table.entries.iter() { - let image = *entry.image.lock(); - let memory = VmMemoryConfig { - region: { - let mut region = vec![]; - for mem in entry.memory.lock().region.iter() { - region.push(*mem); - } - assert_eq!(region, entry.memory.lock().region); - region - }, - }; - let cpu = *entry.cpu.lock(); - // emu dev config - let mut vm_emu_dev_confg = VmEmulatedDeviceConfigList { emu_dev_list: vec![] }; - let src_emu_dev_confg_list = entry.vm_emu_dev_confg.lock(); - for emu_config in &src_emu_dev_confg_list.emu_dev_list { - vm_emu_dev_confg.emu_dev_list.push(VmEmulatedDeviceConfig { - name: Some(String::from(emu_config.name.as_ref().unwrap())), - base_ipa: emu_config.base_ipa, - length: emu_config.length, - irq_id: emu_config.irq_id, - cfg_list: { - let mut cfg_list = vec![]; - for cfg in emu_config.cfg_list.iter() { - cfg_list.push(*cfg); - } - assert_eq!(cfg_list, emu_config.cfg_list); - cfg_list - }, - emu_type: emu_config.emu_type, - mediated: emu_config.mediated, - }) - } - // passthrough dev config - let src_pt = entry.vm_pt_dev_confg.lock(); - let mut vm_pt_dev_confg = VmPassthroughDeviceConfig { - regions: vec![], - irqs: vec![], - streams_ids: vec![], - }; - for region in src_pt.regions.iter() { - vm_pt_dev_confg.regions.push(*region); - } - for irq in src_pt.irqs.iter() { - vm_pt_dev_confg.irqs.push(*irq); - } - for streams_id in src_pt.streams_ids.iter() { - vm_pt_dev_confg.streams_ids.push(*streams_id); - } - assert_eq!(vm_pt_dev_confg.regions, src_pt.regions); - assert_eq!(vm_pt_dev_confg.irqs, src_pt.irqs); - assert_eq!(vm_pt_dev_confg.streams_ids, src_pt.streams_ids); - - // dtb config - let mut vm_dtb_devs = VMDtbDevConfigList { - dtb_device_list: vec![], - }; - let src_dtb_confg_list = entry.vm_dtb_devs.lock(); - for dtb_config in src_dtb_confg_list.dtb_device_list.iter() { - vm_dtb_devs.dtb_device_list.push(VmDtbDevConfig { - name: String::from(&dtb_config.name), - dev_type: dtb_config.dev_type, - irqs: { - let mut irqs = vec![]; - for irq in dtb_config.irqs.iter() { - irqs.push(*irq); - } - assert_eq!(irqs, dtb_config.irqs); - irqs - }, - addr_region: dtb_config.addr_region, - }); - } - - vm_config_table.entries.push(VmConfigEntry { - id: entry.id, - name: Some(String::from(entry.name.as_ref().unwrap())), - os_type: entry.os_type, - cmdline: String::from(&entry.cmdline), - image: Arc::new(Mutex::new(image)), - memory: Arc::new(Mutex::new(memory)), - cpu: Arc::new(Mutex::new(cpu)), - vm_emu_dev_confg: Arc::new(Mutex::new(vm_emu_dev_confg)), - vm_pt_dev_confg: Arc::new(Mutex::new(vm_pt_dev_confg)), - vm_dtb_devs: Arc::new(Mutex::new(vm_dtb_devs)), - }); - } - assert_eq!(vm_config_table.entries.len(), src_config_table.entries.len()); - assert_eq!(vm_config_table.vm_num, src_config_table.vm_num); - assert_eq!(vm_config_table.vm_bitmap, src_config_table.vm_bitmap); - assert_eq!(vm_config_table.name, src_config_table.name); - println!("Update {} VM to DEF_VM_CONFIG_TABLE", vm_config_table.vm_num); -} - -pub fn vcpu_list_alloc(src_vcpu_list: &Mutex>) { - let mut vcpu_list = VCPU_LIST.lock(); - for vcpu in src_vcpu_list.lock().iter() { - let src_inner = vcpu.inner.lock(); - let src_vm_option = src_inner.vm.clone(); - let vm = match src_vm_option { - None => None, - Some(src_vm) => { - let vm_id = src_vm.id(); - vm(vm_id) - } - }; - let mut vcpu_inner = VcpuInner::default(); - vcpu_inner.vm = vm.clone(); - vcpu_inner.id = src_inner.id; - vcpu_inner.phys_id = src_inner.phys_id; - let vcpu = Vcpu { - inner: Arc::new(Mutex::new(vcpu_inner)), - }; - vm.unwrap().push_vcpu(vcpu.clone()); - vcpu_list.push(vcpu); - } - assert_eq!(vcpu_list.len(), src_vcpu_list.lock().len()); - println!("Alloc {} VCPU to VCPU_LIST", vcpu_list.len()); -} - -pub fn vcpu_update(src_vcpu_list: &Mutex>, src_vm_list: &Mutex>) { - let vcpu_list = VCPU_LIST.lock(); - // assert_eq!(vcpu_list.len(), src_vcpu_list.lock().len()); - for (idx, vcpu) in src_vcpu_list.lock().iter().enumerate() { - let src_inner = vcpu.inner.lock(); - let mut dst_inner = vcpu_list[idx].inner.lock(); - - // assert_eq!(dst_inner.id, src_inner.id); - // assert_eq!(dst_inner.phys_id, src_inner.phys_id); - dst_inner.state = src_inner.state; - dst_inner.int_list = { - let mut int_list = vec![]; - for int in src_inner.int_list.iter() { - int_list.push(*int); - } - int_list - }; - dst_inner.vcpu_ctx = src_inner.vcpu_ctx; - dst_inner.vm_ctx = src_inner.vm_ctx; - // assert_eq!(dst_inner.int_list, src_inner.int_list); - } - - // Add vgic emu dev for vm - for src_vm in src_vm_list.lock().iter() { - let src_vgic = src_vm.vgic(); - let new_vgic = Vgic::default(); - new_vgic.save_vgic(src_vgic.clone()); - - let vm = vm(src_vm.id()).unwrap(); - if let EmuDevs::None = vm.emu_dev(vm.intc_dev_id()) { - vm.set_emu_devs(vm.intc_dev_id(), EmuDevs::Vgic(Arc::new(new_vgic))); - } else { - panic!("illegal vgic emu dev idx in vm.emu_devs"); - } - } - // println!("Update {} Vcpu to VCPU_LIST", vcpu_list.len()); -} - -fn smmu_update(src_smmu_v2: &Mutex) { - let mut smmu_v2 = SMMU_V2.lock(); - let src_smmu = src_smmu_v2.lock(); - smmu_v2.glb_rs0 = src_smmu.glb_rs0; - smmu_v2.glb_rs1 = src_smmu.glb_rs1; - smmu_v2.context_s2_idx = src_smmu.context_s2_idx; - for ctx_bank in src_smmu.context_bank.iter() { - smmu_v2.context_bank.push(*ctx_bank); - } - smmu_v2.context_alloc_bitmap = match &src_smmu.context_alloc_bitmap { - Some(ctx_bitmap) => { - let mut bitmap = FlexBitmap::new(ctx_bitmap.len); - for v in ctx_bitmap.map.iter() { - bitmap.map.push(*v); - } - Some(bitmap) - } - None => None, - }; - - smmu_v2.smr_num = src_smmu.smr_num; - smmu_v2.smr_alloc_bitmap = match &src_smmu.smr_alloc_bitmap { - Some(smr_bitmap) => { - let mut bitmap = FlexBitmap::new(smr_bitmap.len); - for v in smr_bitmap.map.iter() { - bitmap.map.push(*v); - } - Some(bitmap) - } - None => None, - }; - smmu_v2.group_alloc_bitmap = match &src_smmu.group_alloc_bitmap { - Some(group_bitmap) => { - let mut bitmap = FlexBitmap::new(group_bitmap.len); - for v in group_bitmap.map.iter() { - bitmap.map.push(*v); - } - Some(bitmap) - } - None => None, - }; -} diff --git a/src/kernel/logger.rs b/src/kernel/logger.rs index ea79728e42686881cfd9a21b56290016dc038ea1..e3ccd5aa8301259b1ea43d0c914a8e4bd96d4880 100755 --- a/src/kernel/logger.rs +++ b/src/kernel/logger.rs @@ -8,39 +8,46 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use log::{Level, Metadata, Record}; -use log::{LevelFilter, SetLoggerError}; - -struct SimpleLogger; - -impl log::Log for SimpleLogger { - fn enabled(&self, _metadata: &Metadata) -> bool { - true - } - - fn log(&self, record: &Record) { - if self.enabled(record.metadata()) { - // let ms = crate::lib::timer::current_ms(); - // let s = ms / 1000; - // let ms = ms % 1000; - // print!("[{:04}.{:03}]", s, ms); - - let level = match record.level() { - Level::Error => "[E]", - Level::Warn => "[W]", - Level::Info => "[I]", - Level::Debug => "[D]", - Level::Trace => "[T]", - }; - println!("{}[{}] {}", level, record.target(), record.args()); - } - } - - fn flush(&self) {} -} - -static LOGGER: SimpleLogger = SimpleLogger; - -pub fn logger_init() -> Result<(), SetLoggerError> { - log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Trace)) -} +use log::{Level, Metadata, Record}; +use log::{LevelFilter, SetLoggerError}; + +struct SimpleLogger; + +impl log::Log for SimpleLogger { + fn enabled(&self, _metadata: &Metadata) -> bool { + true + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + // let ms = crate::lib::timer::current_ms(); + // let s = ms / 1000; + // let ms = ms % 1000; + // print!("[{:04}.{:03}]", s, ms); + + let level = match record.level() { + Level::Error => "[E]", + Level::Warn => "[W]", + Level::Info => "[I]", + Level::Debug => "[D]", + Level::Trace => "[T]", + }; + println!( + "{}>{}[{}] {}", + level, + crate::kernel::current_cpu().id, + record.target(), + record.args() + ); + } + } + + fn flush(&self) {} +} + +static LOGGER: SimpleLogger = SimpleLogger; + +/// Initialize global logger, setting log level to `Trace`. +pub fn logger_init() -> Result<(), SetLoggerError> { + log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Trace)) +} diff --git a/src/kernel/mem.rs b/src/kernel/mem.rs index 477653729535d9b76115149cc1693512e4716c69..592a635e628113aa37d384782dacee6b5bc34829 100644 --- a/src/kernel/mem.rs +++ b/src/kernel/mem.rs @@ -11,22 +11,33 @@ use crate::arch::PAGE_SIZE; use crate::board::*; use crate::kernel::mem_shared_mem_init; -use crate::lib::{memset_safe, round_up}; +use crate::utils::{memset, round_up}; use crate::mm::PageFrame; use super::mem_region::*; use self::AllocError::*; - +use core::slice::from_raw_parts_mut; pub const VM_MEM_REGION_MAX: usize = 4; pub fn mem_init() { mem_heap_region_init(); mem_vm_region_init(); mem_shared_mem_init(); - println!("Mem init ok"); + info!("Mem init ok"); +} + +/// Clear BSS section +pub unsafe fn clear_bss() { + extern "C" { + fn _bss_begin(); + fn _bss_end(); + } + println!("clear bss : from {:x} to {:x}", _bss_begin as usize, _bss_end as usize); + from_raw_parts_mut(_bss_begin as usize as *mut u8, _bss_end as usize - _bss_begin as usize).fill(0); } +/// init heap memory region pub fn mem_heap_region_init() { extern "C" { // Note: link-time label, see aarch64.lds @@ -34,17 +45,22 @@ pub fn mem_heap_region_init() { } if PLAT_DESC.mem_desc.regions.is_empty() { - println!("Platform has no memory region!"); + warn!("Platform has no memory region!"); } let base = round_up(_image_end as usize, PAGE_SIZE); let size = round_up( - PLAT_DESC.mem_desc.regions[0].size as usize - (base - PLAT_DESC.mem_desc.base as usize), + PLAT_DESC.mem_desc.regions[0].size - (base - PLAT_DESC.mem_desc.base), PAGE_SIZE, ) / PAGE_SIZE; - println!("init memory, please waiting..."); - memset_safe(base as *mut u8, 0, size as usize * PAGE_SIZE); + info!("init memory, please waiting..."); + // SAFETY: + // The region is writable for the Hypervisor in EL2. + // The 'c' is a valid value of type u8 without overflow. + unsafe { + memset(base as *mut u8, 0, size as usize * PAGE_SIZE); + } // core::intrinsics::volatile_set_memory(ptr, 0, size as usize * PAGE_SIZE); let mut heap_lock = HEAP_REGION.lock(); @@ -52,13 +68,13 @@ pub fn mem_heap_region_init() { drop(heap_lock); - println!( + info!( "Memory Heap: base 0x{:x}, size {} MB / {} pages", base, size * PAGE_SIZE / (1024 * 1024), size ); - println!("Memory Heap init ok"); + info!("Memory Heap init ok"); } /// Reserve Heap Memory from base_addr to base_addr + size @@ -68,8 +84,12 @@ pub fn mem_heap_region_init() { /// ``` pub fn mem_heap_region_reserve(base_addr: usize, size: usize) { let mut heap = HEAP_REGION.lock(); + // //TODO: a compromise way for live_update + if base_addr < heap.region.base { + return; + } heap.reserve_pages(base_addr, round_up(size, PAGE_SIZE) / PAGE_SIZE); - println!( + info!( "Reserve Heap Region 0x{:x} ~ 0x{:x}", base_addr, base_addr + round_up(size, PAGE_SIZE) @@ -102,13 +122,13 @@ fn mem_vm_region_init() { (*vm_region_lock).push(mem_region); } - println!( + info!( "Memory VM regions: total {} region, size {} MB / {} pages", vm_region_num, pages * PAGE_SIZE / (1024 * 1024), pages ); - println!("Memory VM regions init ok!"); + info!("Memory VM regions init ok!"); } #[derive(Debug)] @@ -119,9 +139,15 @@ pub enum AllocError { fn mem_heap_reset() { let heap = HEAP_REGION.lock(); - memset_safe(heap.region.base as *mut u8, 0, heap.region.size * PAGE_SIZE); + // SAFETY: + // The 'heap_region' is writable for the Hypervisor in EL2. + // The 'c' is a valid value of type u8 without overflow. + unsafe { + memset(heap.region.base as *mut u8, 0, heap.region.size * PAGE_SIZE); + } } +/// alloc some page from heap region pub fn mem_heap_alloc(page_num: usize, _aligned: bool) -> Result { if page_num == 0 { return Err(AllocZeroPage); @@ -135,19 +161,23 @@ pub fn mem_heap_alloc(page_num: usize, _aligned: bool) -> Result bool { let mut heap = HEAP_REGION.lock(); heap.free_pages(addr, page_num) } +/// alloc one page pub fn mem_page_alloc() -> Result { PageFrame::alloc_pages(1) } +/// alloc some continuous pages pub fn mem_pages_alloc(page_num: usize) -> Result { PageFrame::alloc_pages(page_num) } +/// alloc some space and create a new vm region pub fn mem_vm_region_alloc(size: usize) -> usize { let mut vm_region = VM_REGION.lock(); for i in 0..vm_region.region.len() { @@ -172,6 +202,7 @@ pub fn mem_vm_region_alloc(size: usize) -> usize { 0 } +/// free a vm region pub fn mem_vm_region_free(start: usize, size: usize) { let mut vm_region = VM_REGION.lock(); let mut free_idx = None; @@ -208,5 +239,5 @@ pub fn mem_vm_region_free(start: usize, size: usize) { } } } - println!("Free mem from pa 0x{:x} to 0x{:x}", start, start + size); + info!("Free mem from pa 0x{:x} to 0x{:x}", start, start + size); } diff --git a/src/kernel/mem_region.rs b/src/kernel/mem_region.rs index 24608ab6ead33202dd8693d7635139a33d57df42..d291cc09464e731a9af8615f53e0184eca6b5f5d 100644 --- a/src/kernel/mem_region.rs +++ b/src/kernel/mem_region.rs @@ -13,14 +13,15 @@ use alloc::vec::Vec; use spin::Mutex; use crate::arch::PAGE_SIZE; -use crate::lib::{BitAlloc, BitAlloc4K, BitAlloc64K, BitMap}; -use crate::lib::memset_safe; +use crate::utils::{BitAlloc, BitAlloc4K, BitAlloc64K, BitMap}; +use crate::utils::memset; use super::AllocError; const TOTAL_MEM_REGION_MAX: usize = 16; #[derive(Copy, Clone, Eq, Debug)] +/// Memory Region Descriptor Struct, used to describe a continuous platform memory region pub struct MemRegion { pub base: usize, pub size: usize, @@ -54,6 +55,7 @@ impl MemRegion { } } +/// Heap Region Struct, using a bitmap to manage heap memory pub struct HeapRegion { pub map: BitMap, pub region: MemRegion, @@ -65,7 +67,12 @@ impl HeapRegion { } // base addr need to align to PAGE_SIZE + /// Reserve a range of pages in heap region, to avoid allocation of this range pub fn reserve_pages(&mut self, base: usize, size: usize) { + debug!( + "reserve pages from {:x}, size {:x}, region from{:x}", + base, size, self.region.base + ); let offset = (base - self.region.base) / PAGE_SIZE; for i in 0..size { if self.map.get(offset + i) != 0 { @@ -96,10 +103,11 @@ impl HeapRegion { } } + /// alloc continuous pages for use pub fn alloc_pages(&mut self, size: usize) -> Result { let res = self.first_fit(size); if res.is_none() { - println!( + error!( "alloc_pages: allocate {} pages failed (heap_base 0x{:x} remain {} total {})", size, self.region.base, self.region.free, self.region.size ); @@ -118,12 +126,19 @@ impl HeapRegion { } let addr = self.region.base + bit * PAGE_SIZE; - memset_safe(addr as *mut u8, 0, size * PAGE_SIZE); - return Ok(addr); + // SAFETY: + // The addr is writable for the Hypervisor in EL2. + // The 'c' is a valid value of type u8 without overflow. + unsafe { + memset(addr as *mut u8, 0, size * PAGE_SIZE); + debug!("mem heap alloc alocate {:x}, size {:x}", addr, size * PAGE_SIZE); + } + Ok(addr) } + /// free a segment of continuous pages pub fn free_pages(&mut self, base: usize, size: usize) -> bool { - use crate::lib::range_in_range; + use crate::utils::range_in_range; if !range_in_range(base, size * PAGE_SIZE, self.region.base, self.region.size * PAGE_SIZE) { panic!( "free_page: out of range (addr 0x{:x} page num {} heap base 0x{:x} heap size 0x{:x})", @@ -141,10 +156,11 @@ impl HeapRegion { } self.region.free += size; self.region.last = page_idx; - return true; + true } } +/// Vm memory region struct pub struct VmRegion { pub region: Vec, } @@ -165,10 +181,11 @@ pub static VM_REGION: Mutex = Mutex::new(VmRegion { }); pub fn bits_to_pages(bits: usize) -> usize { - use crate::lib::round_up; + use crate::utils::round_up; round_up(bits, PAGE_SIZE) } +/// check if a physical address is in heap region pub fn pa_in_heap_region(pa: usize) -> bool { let heap_region = HEAP_REGION.lock(); pa > heap_region.region.base && pa < (heap_region.region.base * PAGE_SIZE + heap_region.region.size) diff --git a/src/kernel/migrate.rs b/src/kernel/migrate.rs deleted file mode 100644 index a476bfcd66a9e5a4a4216ffe8b70622c6399553e..0000000000000000000000000000000000000000 --- a/src/kernel/migrate.rs +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. -// Rust-Shyper is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use crate::arch::{ - Aarch64ContextFrame, GIC_LIST_REGS_NUM, GIC_PRIVINT_NUM, GIC_SGIS_NUM, GIC_SPI_MAX, GicContext, IrqState, - PAGE_SIZE, PTE_S2_FIELD_AP_RW, PTE_S2_NORMAL, PTE_S2_RO, Sgis, VmContext, -}; -use crate::arch::tlb_invalidate_guest_all; -use crate::board::PLATFORM_VCPU_NUM_MAX; -use crate::device::{EMU_DEV_NUM_MAX, EmuContext, VirtioDeviceType, VirtMmioRegs}; -use crate::kernel::{ - active_vm, get_share_mem, hvc_send_msg_to_vm, HVC_VMM, HVC_VMM_MIGRATE_START, HvcGuestMsg, HvcMigrateMsg, - mem_pages_alloc, MIGRATE_BITMAP, MIGRATE_COPY, MIGRATE_FINISH, MIGRATE_SEND, vm, Vm, vm_if_copy_mem_map, - vm_if_mem_map_cache, vm_if_mem_map_page_num, vm_if_set_mem_map, vm_if_set_mem_map_cache, -}; - -pub struct VMData { - pub vm_ctx: [VmContext; PLATFORM_VCPU_NUM_MAX], - pub vcpu_ctx: [Aarch64ContextFrame; PLATFORM_VCPU_NUM_MAX], - pub gic_ctx: [GicContext; PLATFORM_VCPU_NUM_MAX], - pub vgic_ctx: VgicMigData, - pub emu_devs: [EmuDevData; EMU_DEV_NUM_MAX], -} - -pub enum EmuDevData { - VirtioBlk(VirtioMmioData), - VirtioNet(VirtioMmioData), - VirtioConsole(VirtioMmioData), - None, -} - -// virtio vgic migration data -pub struct VgicMigData { - pub vgicd: VgicdData, - pub cpu_priv_num: usize, - pub cpu_priv: [VgicCpuPrivData; 4], -} - -impl VgicMigData { - pub fn default() -> VgicMigData { - VgicMigData { - vgicd: VgicdData::default(), - cpu_priv_num: 0, - cpu_priv: [VgicCpuPrivData::default(); 4], // TODO: 4 is hardcode for vm cpu num max - } - } -} - -pub struct VgicdData { - pub ctlr: u32, - pub typer: u32, - pub iidr: u32, - pub interrupts: [VgicIntData; GIC_SPI_MAX], -} - -impl VgicdData { - pub fn default() -> VgicdData { - VgicdData { - ctlr: 0, - typer: 0, - iidr: 0, - interrupts: [VgicIntData::default(); GIC_SPI_MAX], - } - } -} - -#[derive(Copy, Clone)] -pub struct VgicCpuPrivData { - pub curr_lrs: [u16; GIC_LIST_REGS_NUM], - pub sgis: [Sgis; GIC_SGIS_NUM], - pub interrupts: [VgicIntData; GIC_PRIVINT_NUM], - pub pend_num: usize, - pub pend_list: [usize; 16], - // TODO: 16 is hard code - pub act_num: usize, - pub act_list: [usize; 16], -} - -impl VgicCpuPrivData { - pub fn default() -> VgicCpuPrivData { - VgicCpuPrivData { - curr_lrs: [0; GIC_LIST_REGS_NUM], - sgis: [Sgis { pend: 0, act: 0 }; GIC_SGIS_NUM], - interrupts: [VgicIntData::default(); GIC_PRIVINT_NUM], - pend_num: 0, - pend_list: [0; 16], - act_num: 0, - act_list: [0; 16], - } - } -} - -#[derive(Copy, Clone)] -pub struct VgicIntData { - pub owner: Option, - // vcpu_id - pub id: u16, - pub hw: bool, - pub in_lr: bool, - pub lr: u16, - pub enabled: bool, - pub state: IrqState, - pub prio: u8, - pub targets: u8, - pub cfg: u8, - - pub in_pend: bool, - pub in_act: bool, -} - -impl VgicIntData { - pub fn default() -> VgicIntData { - VgicIntData { - owner: None, - id: 0, - hw: false, - in_lr: false, - lr: 0, - enabled: false, - state: IrqState::IrqSInactive, - prio: 0, - targets: 0, - cfg: 0, - in_pend: false, - in_act: false, - } - } -} - -// virtio mmio migration data -pub struct VirtioMmioData { - pub id: usize, - pub driver_features: usize, - pub driver_status: usize, - pub regs: VirtMmioRegs, - pub dev: VirtDevData, - pub oppo_dev: VirtDevData, - pub vq: [VirtqData; 4], // TODO: 4 is hard code for vq max len -} - -impl VirtioMmioData { - pub fn default() -> VirtioMmioData { - VirtioMmioData { - id: 0, - driver_features: 0, - driver_status: 0, - regs: VirtMmioRegs::default(), - dev: VirtDevData::default(), - oppo_dev: VirtDevData::default(), - vq: [VirtqData::default(); 4], - } - } -} - -#[derive(Copy, Clone)] -pub struct VirtqData { - pub ready: usize, - pub vq_index: usize, - pub num: usize, - - pub last_avail_idx: u16, - pub last_used_idx: u16, - pub used_flags: u16, - - pub desc_table_ipa: usize, - pub avail_ipa: usize, - pub used_ipa: usize, -} - -impl VirtqData { - pub fn default() -> VirtqData { - VirtqData { - ready: 0, - vq_index: 0, - num: 0, - last_avail_idx: 0, - last_used_idx: 0, - used_flags: 0, - desc_table_ipa: 0, - avail_ipa: 0, - used_ipa: 0, - } - } -} - -pub struct VirtDevData { - pub activated: bool, - pub dev_type: VirtioDeviceType, - pub features: usize, - pub generation: usize, - pub int_id: usize, - pub desc: DevDescData, - // req: reserve; we used nfs, no need to mig blk req data - // cache: reserve - // stat: reserve -} - -impl VirtDevData { - pub fn default() -> VirtDevData { - VirtDevData { - activated: false, - dev_type: VirtioDeviceType::None, - features: 0, - generation: 0, - int_id: 0, - desc: DevDescData::None, - } - } -} - -pub enum DevDescData { - // reserve blk desc - BlkDesc(BlkDescData), - NetDesc(NetDescData), - ConsoleDesc(ConsoleDescData), - None, -} - -pub struct BlkDescData {} - -pub struct NetDescData { - pub mac: [u8; 6], - pub status: u16, -} - -pub struct ConsoleDescData { - pub oppo_end_vmid: u16, - pub oppo_end_ipa: u64, - // vm access - pub cols: u16, - pub rows: u16, - pub max_nr_ports: u32, - pub emerg_wr: u32, -} - -pub fn migrate_ready(vmid: usize) { - if vm_if_mem_map_cache(vmid).is_none() { - let trgt_vm = vm(vmid).unwrap(); - map_migrate_vm_mem(trgt_vm, get_share_mem(MIGRATE_SEND)); - match mem_pages_alloc(vm_if_mem_map_page_num(vmid)) { - Ok(pf) => { - active_vm().unwrap().pt_map_range( - get_share_mem(MIGRATE_BITMAP), - PAGE_SIZE * vm_if_mem_map_page_num(vmid), - pf.pa(), - PTE_S2_RO, - true, - ); - vm_if_set_mem_map_cache(vmid, pf); - } - Err(_) => { - panic!("migrate_ready: mem_pages_alloc failed"); - } - } - } -} - -pub fn send_migrate_memcpy_msg(vmid: usize) { - // copy trgt_vm dirty mem map to kernel module - // println!("migrate_memcpy, vm_id {}", vmid); - hvc_send_msg_to_vm( - 0, - &HvcGuestMsg::Migrate(HvcMigrateMsg { - fid: HVC_VMM, - event: HVC_VMM_MIGRATE_START, - vm_id: vmid, - oper: MIGRATE_COPY, - page_num: vm_if_mem_map_page_num(vmid), - }), - ); -} - -pub fn map_migrate_vm_mem(vm: Vm, ipa_start: usize) { - let mut len = 0; - for i in 0..vm.region_num() { - active_vm() - .unwrap() - .pt_map_range(ipa_start + len, vm.pa_length(i), vm.pa_start(i), PTE_S2_NORMAL, true); - len += vm.pa_length(i); - } -} - -pub fn unmap_migrate_vm_mem(vm: Vm, ipa_start: usize) { - let mut len = 0; - for i in 0..vm.region_num() { - // println!("unmap_migrate_vm_mem, ipa_start {:x}, len {:x}", ipa_start, vm.pa_length(i)); - active_vm() - .unwrap() - .pt_unmap_range(ipa_start + len, vm.pa_length(i), true); - len += vm.pa_length(i); - } -} - -pub fn migrate_finish_ipi_handler(vm_id: usize) { - // println!("Core 0 handle VM[{}] finish ipi", vm_id); - // let vm = vm(vm_id).unwrap(); - // copy trgt_vm dirty mem map to kernel module - // let vm = vm(vm_id).unwrap(); - // for i in 0..vm.mem_region_num() { - // unsafe { - // cache_invalidate_d(vm.pa_start(i), vm.pa_length(i)); - // } - // } - // tlb_invalidate_guest_all(); - vm_if_copy_mem_map(vm_id); - - hvc_send_msg_to_vm( - 0, - &HvcGuestMsg::Migrate(HvcMigrateMsg { - fid: HVC_VMM, - event: HVC_VMM_MIGRATE_START, - vm_id, - oper: MIGRATE_FINISH, - page_num: vm_if_mem_map_page_num(vm_id), - }), - ); -} - -pub fn migrate_data_abort_handler(emu_ctx: &EmuContext) { - if emu_ctx.write { - // ptr_read_write(emu_ctx.address, emu_ctx.width, val, false); - let vm = active_vm().unwrap(); - // vm.show_pagetable(emu_ctx.address); - let vm_id = vm.id(); - - let (pa, len) = vm.pt_set_access_permission(emu_ctx.address, PTE_S2_FIELD_AP_RW); - // println!( - // "migrate_data_abort_handler: emu_ctx addr 0x{:x}, write pa {:x}, len 0x{:x}", - // emu_ctx.address, pa, len - // ); - let mut bit = 0; - for i in 0..vm.region_num() { - let start = vm.pa_start(i); - let end = start + vm.pa_length(i); - if pa >= start && pa < end { - bit += (pa - active_vm().unwrap().pa_start(i)) / PAGE_SIZE; - vm_if_set_mem_map(vm_id, bit, len / PAGE_SIZE); - break; - } - bit += vm.pa_length(i) / PAGE_SIZE; - if i + 1 == vm.region_num() { - panic!( - "migrate_data_abort_handler: can not found addr 0x{:x} in vm{} pa region", - pa, vm_id - ); - } - } - // flush tlb for updating page table - tlb_invalidate_guest_all(); - } else { - panic!("migrate_data_abort_handler: permission should be read only"); - } -} diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs index a00688685db7e988b548978de3e9e43683635fa9..7b1416b757ad59539ad939a1565d73827efe2e49 100644 --- a/src/kernel/mod.rs +++ b/src/kernel/mod.rs @@ -8,6 +8,9 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +//! The rust-shyper hypervisor kernel code. +//! + pub use self::async_task::*; pub use self::cpu::*; pub use self::hvc::*; @@ -15,11 +18,8 @@ pub use self::interrupt::*; pub use self::iommu::*; pub use self::ipi::*; pub use self::ivc::*; -pub use self::live_update::*; pub use self::logger::*; pub use self::mem::*; -pub use self::mem_region::*; -pub use self::migrate::*; pub use self::sched::*; // pub use self::task::*; pub use self::timer::*; @@ -34,11 +34,9 @@ mod hvc; mod interrupt; mod ipi; mod ivc; -mod live_update; mod logger; mod mem; mod mem_region; -mod migrate; // mod task; mod iommu; mod sched; diff --git a/src/kernel/sched/mod.rs b/src/kernel/sched/mod.rs index a233ddd25033a68cc7fdaf31c8ee8a8145aa75d7..945b50e39b214ae620e46e12fc70cb330105f321 100644 --- a/src/kernel/sched/mod.rs +++ b/src/kernel/sched/mod.rs @@ -17,27 +17,29 @@ pub use self::sched_rr::SchedulerRR; use crate::kernel::Vcpu; // Must Implement SchedulerTrait for inner struct(the real scheduler object) +/// Scheduler type pub enum SchedType { SchedRR(SchedulerRR), // SchedRT(SchedulerRT), None, } +/// Scheduler trait, used to define common interface for different scheduler pub trait Scheduler { + /// initialize the scheduler, call once when scheduler is created fn init(&mut self); - /* pick the next vcpu object */ + /// pick the next vcpu object fn next(&mut self) -> Option; - /* yield current vcpu */ + /// yield current vcpu fn do_schedule(&mut self); - /* put vcpu into sleep, and remove it from scheduler */ + /// put vcpu into sleep, and remove it from scheduler fn sleep(&mut self, vcpu: Vcpu); - /* wake up vcpu from sleep status, remember to set_active_vcpu when it is none*/ + /// wake up vcpu from sleep status, remember to set_active_vcpu when it is none fn wakeup(&mut self, vcpu: Vcpu); - /* yield to another cpu, only used when vcpu is new added and want to be excuted immediately */ + /// yield to another cpu, only used when vcpu is newly added and want to be excuted immediately fn yield_to(&mut self, vcpu: Vcpu); } -// #[cfg(feature = "update")] pub trait SchedulerUpdate { fn update(&self) -> Self; } diff --git a/src/kernel/sched/sched_rr.rs b/src/kernel/sched/sched_rr.rs index d3377b9403fcf08fa2054c83375c4b5736e49103..3412ed2427ff958bae90a0142cf53a72a4e281c5 100644 --- a/src/kernel/sched/sched_rr.rs +++ b/src/kernel/sched/sched_rr.rs @@ -9,8 +9,10 @@ // See the Mulan PSL v2 for more details. use alloc::vec::Vec; -use crate::kernel::{Vcpu, Scheduler, SchedulerUpdate, current_cpu, VcpuState, timer_enable, vm}; +use crate::kernel::{Vcpu, Scheduler, SchedulerUpdate, current_cpu, VcpuState, timer_enable, vm, run_idle_thread}; +#[derive(Default)] +/// Round-Robin Scheduler struct pub struct SchedulerRR { queue: Vec, active_idx: usize, @@ -27,19 +29,10 @@ impl SchedulerRR { } } -impl Default for SchedulerRR { - fn default() -> Self { - Self { - queue: Default::default(), - active_idx: Default::default(), - base_slice: Default::default(), - } - } -} - impl Scheduler for SchedulerRR { fn init(&mut self) {} + /// Select the next vcpu object in the round-robin queue fn next(&mut self) -> Option { let queue = &self.queue; let len = queue.len(); @@ -47,7 +40,7 @@ impl Scheduler for SchedulerRR { let idx = (self.active_idx + i) % len; match queue.get(idx) { Some(vcpu) => match vcpu.state() { - VcpuState::VcpuInv => {} + VcpuState::Invalid => {} _ => { self.active_idx = idx; return Some(vcpu.clone()); @@ -59,11 +52,25 @@ impl Scheduler for SchedulerRR { None } + /// Schedule to the next vcpu object fn do_schedule(&mut self) { - let next_vcpu = self.next().unwrap(); - current_cpu().schedule_to(next_vcpu); + // let next_vcpu = self.next().unwrap(); + // current_cpu().schedule_to(next_vcpu); + if let Some(next_vcpu) = self.next() { + current_cpu().schedule_to(next_vcpu); + } else { + match current_cpu().ctx_ptr() { + None => { + error!("run_idle_thread: cpu{} ctx is NULL", current_cpu().id); + } + Some(_ctx) => { + run_idle_thread(); + } + } + } } + /// put vcpu into sleep, and remove it from scheduler fn sleep(&mut self, vcpu: Vcpu) { // println!( // "SchedulerRR: Core {} sleep VM[{}] vcpu {}", @@ -74,22 +81,22 @@ impl Scheduler for SchedulerRR { let mut need_schedule = false; { let queue = &mut self.queue; - match queue.iter().position(|x| x.vm_id() == vcpu.vm_id()) { - Some(idx) => { - queue.remove(idx); - if idx < self.active_idx { + if let Some(idx) = queue.iter().position(|x| x.vm_id() == vcpu.vm_id()) { + queue.remove(idx); + match idx.cmp(&self.active_idx) { + core::cmp::Ordering::Less => { self.active_idx -= 1; - } else if idx == self.active_idx { + } + core::cmp::Ordering::Equal => { // cpu.active_vcpu need remove current_cpu().set_active_vcpu(None); - if !queue.is_empty() { - need_schedule = true; - } + need_schedule = true; } + _ => {} } - None => {} } } + vcpu.set_state(VcpuState::Sleep); if self.queue.len() <= 1 { timer_enable(false); } @@ -98,9 +105,10 @@ impl Scheduler for SchedulerRR { } } + /// wakeup a vcpu, meaning that the vcpu is ready to be scheduled fn wakeup(&mut self, vcpu: Vcpu) { let queue = &mut self.queue; - vcpu.set_state(VcpuState::VcpuPend); + vcpu.set_state(VcpuState::Ready); queue.push(vcpu); if queue.len() > 1 { timer_enable(true); @@ -110,6 +118,7 @@ impl Scheduler for SchedulerRR { } } + /// yield to another cpu, only used when vcpu is new added and want to be excuted immediately fn yield_to(&mut self, vcpu: Vcpu) { let queue = &mut self.queue; queue.push(vcpu.clone()); @@ -121,7 +130,6 @@ impl Scheduler for SchedulerRR { } } -// #[cfg(feature = "update")] impl SchedulerUpdate for SchedulerRR { fn update(&self) -> Self { let src_rr = self; @@ -136,22 +144,11 @@ impl SchedulerUpdate for SchedulerRR { new_rr.base_slice = src_rr.base_slice; let active_vcpu = if src_rr.active_idx < src_rr.queue.len() { - println!( - "Core[{}] is some, active_idx {}, addr {:x}", - current_cpu().id, - src_rr.active_idx, - unsafe { *(&new_rr.queue[src_rr.active_idx].clone() as *const _ as *const usize) } - ); Some(new_rr.queue[src_rr.active_idx].clone()) } else { - println!("Core[{}] is none", current_cpu().id); + debug!("Core[{}] is none", current_cpu().id); None }; - if active_vcpu.is_some() { - println!("core[{}] update active_vcpu addr {:x}", current_cpu().id, unsafe { - *(&active_vcpu.clone().unwrap() as *const _ as *const usize) - }); - } current_cpu().set_active_vcpu(active_vcpu); new_rr } diff --git a/src/kernel/timer.rs b/src/kernel/timer.rs index e1143b8ef4016e75846536a39fa32fc3744f12da..fd64966933a79be55d04dbb3dbe6740959323f2b 100644 --- a/src/kernel/timer.rs +++ b/src/kernel/timer.rs @@ -8,9 +8,9 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::arch::INTERRUPT_IRQ_HYPERVISOR_TIMER; -// use crate::board::PLATFORM_CPU_NUM_MAX; -use crate::kernel::{current_cpu, InterruptHandler, Scheduler}; +use crate::arch::traits::InterruptController; +use crate::arch::IntCtrl; +use crate::kernel::{current_cpu, Scheduler}; // #[derive(Copy, Clone)] // struct Timer(bool); @@ -28,30 +28,32 @@ use crate::kernel::{current_cpu, InterruptHandler, Scheduler}; // static TIMER_LIST: Mutex<[Timer; PLATFORM_CPU_NUM_MAX]> = // Mutex::new([Timer::default(); PLATFORM_CPU_NUM_MAX]); +/// initialize timer on current cpu (This function needs to be executed on each cpu) pub fn timer_init() { crate::arch::timer_arch_init(); timer_enable(false); - crate::lib::barrier(); + #[cfg(not(feature = "secondary_start"))] + crate::utils::barrier(); + if current_cpu().id == 0 { - crate::kernel::interrupt_reserve_int( - INTERRUPT_IRQ_HYPERVISOR_TIMER, - InterruptHandler::TimeIrqHandler(timer_irq_handler), - ); - println!("Timer frequency: {}Hz", crate::arch::timer_arch_get_frequency()); - println!("Timer init ok"); + crate::kernel::interrupt_reserve_int(IntCtrl::IRQ_HYPERVISOR_TIMER, timer_irq_handler); + info!("Timer frequency: {}Hz", crate::arch::timer_arch_get_frequency()); + info!("Timer init ok"); } } +/// enable timer on current cpu pub fn timer_enable(val: bool) { // println!( // "Core {} {} EL2 timer", // current_cpu().id, // if val { "enable" } else { "disable" } // ); - super::interrupt::interrupt_cpu_enable(INTERRUPT_IRQ_HYPERVISOR_TIMER, val); + super::interrupt::interrupt_cpu_enable(IntCtrl::IRQ_HYPERVISOR_TIMER, val); } +/// trigger timer interrupt after X ms fn timer_notify_after(ms: usize) { use crate::arch::{timer_arch_enable_irq, timer_arch_set}; if ms == 0 { @@ -62,7 +64,7 @@ fn timer_notify_after(ms: usize) { timer_arch_enable_irq(); } -pub fn timer_irq_handler(_arg: usize) { +pub fn timer_irq_handler() { use crate::arch::timer_arch_disable_irq; timer_arch_disable_irq(); diff --git a/src/kernel/vcpu.rs b/src/kernel/vcpu.rs index 409a0726a39bdf458895ef335db3638949483a4e..85c3d89093b26a4782ad8ed84f4db79e11e1e914 100644 --- a/src/kernel/vcpu.rs +++ b/src/kernel/vcpu.rs @@ -13,48 +13,58 @@ use alloc::vec::Vec; use core::mem::size_of; use spin::Mutex; -use crate::arch::{ - ContextFrame, ContextFrameTrait, cpu_interrupt_unmask, GIC_INTS_MAX, GIC_SGI_REGS_NUM, GICC, GicContext, GICD, - GICH, VmContext, timer_arch_get_counter, -}; -use crate::board::{Platform, PlatOperation, PLATFORM_VCPU_NUM_MAX}; +use crate::arch::{ContextFrame, ContextFrameTrait, GicContext, VmContext, timer_arch_get_counter}; +use crate::board::PlatOperation; use crate::kernel::{current_cpu, interrupt_vm_inject, vm_if_set_state}; use crate::kernel::{active_vcpu_id, active_vm_id}; -use crate::lib::memcpy_safe; +use crate::utils::memcpy; use super::{CpuState, Vm, VmType}; #[derive(Clone, Copy, Debug)] +/// Vcpu state Enum pub enum VcpuState { - VcpuInv = 0, - VcpuPend = 1, - VcpuAct = 2, + Invalid = 0, + Ready = 1, + Running = 2, + Sleep = 3, +} + +struct VcpuInnerConst { + id: usize, + vm: Option, +} + +pub struct VcpuInner { + inner_const: VcpuInnerConst, + pub inner_mut: Mutex, } #[derive(Clone)] +/// Vcpu struct pub struct Vcpu { - pub inner: Arc>, + pub inner: Arc, } impl Vcpu { - pub fn default() -> Vcpu { - Vcpu { - inner: Arc::new(Mutex::new(VcpuInner::default())), - } - } - - pub fn init(&self, vm: Vm, vcpu_id: usize) { - let mut inner = self.inner.lock(); - inner.vm = Some(vm.clone()); - inner.id = vcpu_id; - inner.phys_id = 0; - drop(inner); - crate::arch::vcpu_arch_init(vm, self.clone()); - self.reset_context(); - } - + pub fn new(vm: Vm, vcpu_id: usize) -> Self { + let this = Self { + inner: Arc::new(VcpuInner { + inner_const: VcpuInnerConst { + id: vcpu_id, + vm: Some(vm.clone()), + }, + inner_mut: Mutex::new(VcpuInnerMut::new()), + }), + }; + crate::arch::vcpu_arch_init(vm, this.clone()); + this.reset_context(); + this + } + + /// shutdown this vcpu pub fn shutdown(&self) { - println!( + info!( "Core {} (vm {} vcpu {}) shutdown ok", current_cpu().id, active_vm_id(), @@ -63,107 +73,19 @@ impl Vcpu { crate::board::Platform::cpu_shutdown(); } - pub fn migrate_vm_ctx_save(&self, cache_pa: usize) { - let inner = self.inner.lock(); - memcpy_safe( - cache_pa as *const u8, - &(inner.vm_ctx) as *const _ as *const u8, - size_of::(), - ); - } - - pub fn migrate_vcpu_ctx_save(&self, cache_pa: usize) { - let inner = self.inner.lock(); - memcpy_safe( - cache_pa as *const u8, - &(inner.vcpu_ctx) as *const _ as *const u8, - size_of::(), - ); - } - - pub fn migrate_gic_ctx_save(&self, cache_pa: usize) { - let inner = self.inner.lock(); - memcpy_safe( - cache_pa as *const u8, - &(inner.gic_ctx) as *const _ as *const u8, - size_of::(), - ); - } - - pub fn migrate_vm_ctx_restore(&self, cache_pa: usize) { - let inner = self.inner.lock(); - memcpy_safe( - &(inner.vm_ctx) as *const _ as *const u8, - cache_pa as *const u8, - size_of::(), - ); - } - - pub fn migrate_vcpu_ctx_restore(&self, cache_pa: usize) { - let inner = self.inner.lock(); - memcpy_safe( - &(inner.vcpu_ctx) as *const _ as *const u8, - cache_pa as *const u8, - size_of::(), - ); - } - - pub fn migrate_gic_ctx_restore(&self, cache_pa: usize) { - let inner = self.inner.lock(); - memcpy_safe( - &(inner.gic_ctx) as *const _ as *const u8, - cache_pa as *const u8, - size_of::(), - ); - } - pub fn context_vm_store(&self) { self.save_cpu_ctx(); - let mut inner = self.inner.lock(); + let mut inner = self.inner.inner_mut.lock(); inner.vm_ctx.ext_regs_store(); inner.vm_ctx.fpsimd_save_context(); inner.vm_ctx.gic_save_state(); } - pub fn context_gic_irqs_store(&self) { - let mut inner = self.inner.lock(); - let vm = inner.vm.clone().unwrap(); - inner.gic_ctx; - for irq in vm.config().passthrough_device_irqs() { - inner.gic_ctx.add_irq(irq as u64); - } - inner.gic_ctx.add_irq(25); - let gicv_ctlr = unsafe { &*((Platform::GICV_BASE + 0x8_0000_0000) as *const u32) }; - inner.gic_ctx.set_gicv_ctlr(*gicv_ctlr); - let gicv_pmr = unsafe { &*((Platform::GICV_BASE + 0x8_0000_0000 + 0x4) as *const u32) }; - inner.gic_ctx.set_gicv_pmr(*gicv_pmr); - } - - pub fn context_gic_irqs_restore(&self) { - let inner = self.inner.lock(); - - for irq_state in inner.gic_ctx.irq_state.iter() { - if irq_state.id != 0 { - GICD.set_enable(irq_state.id as usize, irq_state.enable != 0); - GICD.set_prio(irq_state.id as usize, irq_state.priority); - GICD.set_trgt(irq_state.id as usize, 1 << Platform::cpuid_to_cpuif(current_cpu().id)); - } - } - - let gicv_pmr = unsafe { &mut *((Platform::GICV_BASE + 0x8_0000_0000 + 0x4) as *mut u32) }; - *gicv_pmr = inner.gic_ctx.gicv_pmr(); - // println!("Core[{}] save gic context", current_cpu().id); - let gicv_ctlr = unsafe { &mut *((Platform::GICV_BASE + 0x8_0000_0000) as *mut u32) }; - *gicv_ctlr = inner.gic_ctx.gicv_ctlr(); - // show_vcpu_reg_context(); - } - pub fn context_vm_restore(&self) { - // println!("context_vm_restore"); self.restore_cpu_ctx(); - let inner = self.inner.lock(); + let inner = self.inner.inner_mut.lock(); // restore vm's VFP and SIMD inner.vm_ctx.fpsimd_restore_context(); inner.vm_ctx.gic_restore_state(); @@ -174,85 +96,89 @@ impl Vcpu { } pub fn gic_restore_context(&self) { - let inner = self.inner.lock(); + let inner = self.inner.inner_mut.lock(); inner.vm_ctx.gic_restore_state(); } pub fn gic_save_context(&self) { - let mut inner = self.inner.lock(); + let mut inner = self.inner.inner_mut.lock(); inner.vm_ctx.gic_save_state(); } pub fn save_cpu_ctx(&self) { - let inner = self.inner.lock(); - match current_cpu().ctx { + let inner = self.inner.inner_mut.lock(); + match current_cpu().ctx_ptr() { None => { - println!("save_cpu_ctx: cpu{} ctx is NULL", current_cpu().id); + error!("save_cpu_ctx: cpu{} ctx is NULL", current_cpu().id); } - Some(ctx) => { - memcpy_safe( + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + Some(ctx) => unsafe { + memcpy( &(inner.vcpu_ctx) as *const _ as *const u8, ctx as *const u8, size_of::(), ); - } + }, } } fn restore_cpu_ctx(&self) { - let inner = self.inner.lock(); - match current_cpu().ctx { + let inner = self.inner.inner_mut.lock(); + match current_cpu().ctx_ptr() { None => { - println!("restore_cpu_ctx: cpu{} ctx is NULL", current_cpu().id); + error!("restore_cpu_ctx: cpu{} ctx is NULL", current_cpu().id); } - Some(ctx) => { - memcpy_safe( + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + Some(ctx) => unsafe { + memcpy( ctx as *const u8, &(inner.vcpu_ctx) as *const _ as *const u8, size_of::(), ); - } + }, } } pub fn set_phys_id(&self, phys_id: usize) { - let mut inner = self.inner.lock(); - println!("set vcpu {} phys id {}", inner.id, phys_id); + let mut inner = self.inner.inner_mut.lock(); + debug!("set vcpu {} phys id {}", self.id(), phys_id); inner.phys_id = phys_id; } pub fn set_gich_ctlr(&self, ctlr: u32) { - let mut inner = self.inner.lock(); + let mut inner = self.inner.inner_mut.lock(); inner.vm_ctx.gic_state.ctlr = ctlr; } pub fn set_hcr(&self, hcr: u64) { - let mut inner = self.inner.lock(); + let mut inner = self.inner.inner_mut.lock(); inner.vm_ctx.hcr_el2 = hcr; } pub fn state(&self) -> VcpuState { - let inner = self.inner.lock(); - inner.state.clone() + let inner = self.inner.inner_mut.lock(); + inner.state } pub fn set_state(&self, state: VcpuState) { - let mut inner = self.inner.lock(); + let mut inner = self.inner.inner_mut.lock(); inner.state = state; } pub fn id(&self) -> usize { - let inner = self.inner.lock(); - inner.id + self.inner.inner_const.id } pub fn vm(&self) -> Option { - let inner = self.inner.lock(); - inner.vm.clone() + self.inner.inner_const.vm.clone() } pub fn phys_id(&self) -> usize { - let inner = self.inner.lock(); + let inner = self.inner.inner_mut.lock(); inner.phys_id } @@ -265,52 +191,69 @@ impl Vcpu { } pub fn reset_context(&self) { - let mut inner = self.inner.lock(); - inner.reset_context(); - } + let mut inner = self.inner.inner_mut.lock(); + let mut vmpidr = 0; + vmpidr |= 1 << 31; //bit[31]:res1 + + if self.vm().as_ref().unwrap().config().cpu_num() == 1 { + vmpidr |= 1 << 30; //bit[30]: Indicates a Uniprocessor system + } - pub fn reset_vmpidr(&self) { - let mut inner = self.inner.lock(); - inner.reset_vmpidr(); + #[cfg(feature = "tx2")] + if self.vm_id() == 0 { + // A57 is cluster #1 for L4T + vmpidr |= 0x100; + } + + vmpidr |= if cfg!(feature = "rk3588") { + 0x100_0000 | (self.id() << 8) + } else { + self.id() + }; + inner.vm_ctx.vmpidr_el2 = vmpidr as u64; + let vm_id = self.vm().unwrap().id(); + use crate::kernel::vm_if_get_type; + if vm_if_get_type(vm_id) == VmType::VmTBma { + debug!("vm {} bma ctx restore", vm_id); + inner.vm_ctx.reset(); + drop(inner); + self.context_ext_regs_store(); + } } pub fn reset_vtimer_offset(&self) { - let mut inner = self.inner.lock(); - inner.reset_vtimer_offset(); + let mut inner = self.inner.inner_mut.lock(); + let curpct = timer_arch_get_counter() as u64; + inner.vm_ctx.cntvoff_el2 = curpct - inner.vm_ctx.cntvct_el0; } pub fn context_ext_regs_store(&self) { - let mut inner = self.inner.lock(); - inner.context_ext_regs_store(); + let mut inner = self.inner.inner_mut.lock(); + inner.vm_ctx.ext_regs_store(); } pub fn vcpu_ctx_addr(&self) -> usize { - let inner = self.inner.lock(); - inner.vcpu_ctx_addr() + let inner: spin::MutexGuard = self.inner.inner_mut.lock(); + &(inner.vcpu_ctx) as *const _ as usize } pub fn set_elr(&self, elr: usize) { - let mut inner = self.inner.lock(); - inner.set_elr(elr); + let mut inner = self.inner.inner_mut.lock(); + inner.vcpu_ctx.set_exception_pc(elr); } pub fn elr(&self) -> usize { - let inner = self.inner.lock(); + let inner = self.inner.inner_mut.lock(); inner.vcpu_ctx.exception_pc() } pub fn set_gpr(&self, idx: usize, val: usize) { - let mut inner = self.inner.lock(); - inner.set_gpr(idx, val); - } - - pub fn show_ctx(&self) { - let inner = self.inner.lock(); - inner.show_ctx(); + let mut inner = self.inner.inner_mut.lock(); + inner.vcpu_ctx.set_gpr(idx, val); } pub fn push_int(&self, int: usize) { - let mut inner = self.inner.lock(); + let mut inner = self.inner.inner_mut.lock(); if !inner.int_list.contains(&int) { inner.int_list.push(int); } @@ -320,37 +263,71 @@ impl Vcpu { match self.vm() { None => {} Some(vm) => { - let mut inner = self.inner.lock(); + let mut inner = self.inner.inner_mut.lock(); let int_list = inner.int_list.clone(); inner.int_list.clear(); drop(inner); for int in int_list { // println!("schedule: inject int {} for vm {}", int, vm.id()); - interrupt_vm_inject(vm.clone(), self.clone(), int, 0); + interrupt_vm_inject(vm.clone(), self.clone(), int); } } } } + + pub fn get_vmpidr(&self) -> usize { + let inner = self.inner.inner_mut.lock(); + inner.vm_ctx.vmpidr_el2 as usize + } } -pub struct VcpuInner { - pub id: usize, +struct IdleThread { + pub ctx: ContextFrame, +} + +fn idle_thread() { + loop { + cortex_a::asm::wfi(); + } +} + +static IDLE_THREAD: spin::Lazy = spin::Lazy::new(|| { + let mut ctx = ContextFrame::new(idle_thread as usize, current_cpu().stack_top(), 0); + use cortex_a::registers::SPSR_EL2; + ctx.set_exception_pc(idle_thread as usize); + ctx.spsr = (SPSR_EL2::M::EL2h + SPSR_EL2::F::Masked + SPSR_EL2::A::Masked + SPSR_EL2::D::Masked).value; + IdleThread { ctx } +}); + +pub fn run_idle_thread() { + trace!("Core {} idle", current_cpu().id); + current_cpu().cpu_state = CpuState::CpuIdle; + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + crate::utils::memcpy( + current_cpu().ctx as *const u8, + &(IDLE_THREAD.ctx) as *const _ as *const u8, + core::mem::size_of::(), + ); + } +} + +pub struct VcpuInnerMut { pub phys_id: usize, pub state: VcpuState, - pub vm: Option, pub int_list: Vec, pub vcpu_ctx: ContextFrame, pub vm_ctx: VmContext, pub gic_ctx: GicContext, } -impl VcpuInner { - pub fn default() -> VcpuInner { - VcpuInner { - id: 0, +impl VcpuInnerMut { + fn new() -> VcpuInnerMut { + VcpuInnerMut { phys_id: 0, - state: VcpuState::VcpuInv, - vm: None, + state: VcpuState::Invalid, int_list: vec![], vcpu_ctx: ContextFrame::default(), vm_ctx: VmContext::default(), @@ -358,139 +335,12 @@ impl VcpuInner { } } - fn vcpu_ctx_addr(&self) -> usize { - &(self.vcpu_ctx) as *const _ as usize - } - - fn vm_id(&self) -> usize { - let vm = self.vm.as_ref().unwrap(); - vm.id() - } - - fn arch_ctx_reset(&mut self) { - // let migrate = self.vm.as_ref().unwrap().migration_state(); - // if !migrate { - self.vm_ctx.cntvoff_el2 = 0; - self.vm_ctx.sctlr_el1 = 0x30C50830; - self.vm_ctx.cntkctl_el1 = 0; - self.vm_ctx.pmcr_el0 = 0; - self.vm_ctx.vtcr_el2 = 0x8001355c; - // } - let mut vmpidr = 0; - vmpidr |= 1 << 31; - - #[cfg(feature = "tx2")] - if self.vm_id() == 0 { - // A57 is cluster #1 for L4T - vmpidr |= 0x100; - } - - vmpidr |= self.id; - self.vm_ctx.vmpidr_el2 = vmpidr as u64; - } - - fn reset_vmpidr(&mut self) { - let mut vmpidr = 0; - vmpidr |= 1 << 31; - - #[cfg(feature = "tx2")] - if self.vm_id() == 0 { - // A57 is cluster #1 for L4T - vmpidr |= 0x100; - } - - vmpidr |= self.id; - self.vm_ctx.vmpidr_el2 = vmpidr as u64; - } - - fn reset_vtimer_offset(&mut self) { - let curpct = timer_arch_get_counter() as u64; - self.vm_ctx.cntvoff_el2 = curpct - self.vm_ctx.cntvct_el0; - } - - fn reset_context(&mut self) { - // let migrate = self.vm.as_ref().unwrap().migration_state(); - self.arch_ctx_reset(); - // if !migrate { - self.gic_ctx_reset(); - // } - use crate::kernel::vm_if_get_type; - match vm_if_get_type(self.vm_id()) { - VmType::VmTBma => { - println!("vm {} bma ctx restore", self.vm_id()); - self.reset_vm_ctx(); - self.context_ext_regs_store(); - } - _ => {} - } - } - fn gic_ctx_reset(&mut self) { use crate::arch::gich_lrs_num; for i in 0..gich_lrs_num() { self.vm_ctx.gic_state.lr[i] = 0; } - self.vm_ctx.gic_state.hcr |= 1 << 2; - } - - fn context_ext_regs_store(&mut self) { - self.vm_ctx.ext_regs_store(); - } - - fn reset_vm_ctx(&mut self) { - self.vm_ctx.reset(); - } - - fn set_elr(&mut self, elr: usize) { - self.vcpu_ctx.set_exception_pc(elr); - } - - fn set_gpr(&mut self, idx: usize, val: usize) { - self.vcpu_ctx.set_gpr(idx, val); - } - - fn show_ctx(&self) { - println!( - "cntvoff_el2 {:x}, sctlr_el1 {:x}, cntkctl_el1 {:x}, pmcr_el0 {:x}, vtcr_el2 {:x} x0 {:x}", - self.vm_ctx.cntvoff_el2, - self.vm_ctx.sctlr_el1, - self.vm_ctx.cntkctl_el1, - self.vm_ctx.pmcr_el0, - self.vm_ctx.vtcr_el2, - self.vcpu_ctx.gpr(0) - ); - } -} - -pub static VCPU_LIST: Mutex> = Mutex::new(Vec::new()); - -pub fn vcpu_alloc() -> Option { - let mut vcpu_list = VCPU_LIST.lock(); - if vcpu_list.len() >= PLATFORM_VCPU_NUM_MAX { - return None; - } - - let val = Vcpu::default(); - vcpu_list.push(val.clone()); - Some(val) -} - -pub fn vcpu_remove(vcpu: Vcpu) { - let mut vcpu_list = VCPU_LIST.lock(); - for (idx, core) in vcpu_list.iter().enumerate() { - if core.id() == vcpu.id() && core.vm_id() == vcpu.vm_id() { - vcpu_list.remove(idx); - return; - } - } - panic!("illegal vm{} vcpu{}, not exist in vcpu_list", vcpu.vm_id(), vcpu.id()); -} - -pub fn vcpu_idle(_vcpu: Vcpu) -> ! { - cpu_interrupt_unmask(); - loop { - // TODO: replace it with an Arch function `arch_idle` - cortex_a::asm::wfi(); + self.vm_ctx.gic_state.hcr |= 1 << 2; // init hcr } } @@ -517,116 +367,8 @@ pub fn vcpu_run(announce: bool) -> ! { extern "C" { fn context_vm_entry(ctx: usize) -> !; } + // SAFETY: `Cpu` maintains the underlying `ctx`. unsafe { - context_vm_entry(current_cpu().ctx.unwrap()); + context_vm_entry(current_cpu().ctx as usize); } } - -pub fn show_vcpu_reg_context() { - print!("#### GICD ISENABLER ####"); - for i in 0..GIC_INTS_MAX / 32 { - if i % 8 == 0 { - println!(""); - } - print!("{:x} ", GICD.is_enabler(i)); - } - println!(""); - print!("#### GICD ISACTIVER ####"); - for i in 0..GIC_INTS_MAX / 32 { - if i % 8 == 0 { - println!(""); - } - print!("{:x} ", GICD.is_activer(i)); - } - println!(""); - print!("#### GICD ISPENDER ####"); - for i in 0..GIC_INTS_MAX / 32 { - if i % 8 == 0 { - println!(""); - } - print!("{:x} ", GICD.is_pender(i)); - } - println!(""); - print!("#### GICD IGROUP ####"); - for i in 0..GIC_INTS_MAX / 32 { - if i % 8 == 0 { - println!(""); - } - print!("{:x} ", GICD.igroup(i)); - } - println!(""); - print!("#### GICD ICFGR ####"); - for i in 0..GIC_INTS_MAX * 2 / 32 { - if i % 8 == 0 { - println!(""); - } - print!("{:x} ", GICD.icfgr(i)); - } - println!(""); - print!("#### GICD CPENDSGIR ####"); - for i in 0..GIC_SGI_REGS_NUM { - if i % 8 == 0 { - println!(""); - } - print!("{:x} ", GICD.cpendsgir(i)); - } - println!(""); - println!("GICH_APR {:x}", GICH.misr()); - - println!("GICD_CTLR {:x}", GICD.ctlr()); - print!("#### GICD ITARGETSR ####"); - for i in 0..GIC_INTS_MAX * 8 / 32 { - if i % 8 == 0 { - println!(""); - } - print!("{:x} ", GICD.itargetsr(i)); - } - println!(""); - - print!("#### GICD IPRIORITYR ####"); - for i in 0..GIC_INTS_MAX * 8 / 32 { - if i % 16 == 0 { - println!(""); - } - print!("{:x} ", GICD.ipriorityr(i)); - } - println!(""); - - println!("GICC_RPR {:x}", GICC.rpr()); - println!("GICC_HPPIR {:x}", GICC.hppir()); - println!("GICC_BPR {:x}", GICC.bpr()); - println!("GICC_ABPR {:x}", GICC.abpr()); - println!("#### GICC APR ####"); - for i in 0..4 { - print!("{:x} ", GICC.apr(i)); - } - println!(""); - println!("#### GICC NSAPR ####"); - for i in 0..4 { - print!("{:x} ", GICC.nsapr(i)); - } - - println!("GICH_MISR {:x}", GICH.misr()); - println!("GICV_CTLR {:x}", unsafe { - *((Platform::GICV_BASE + 0x8_0000_0000) as *const u32) - }); - println!("GICV_PMR {:x}", unsafe { - *((Platform::GICV_BASE + 0x8_0000_0000 + 0x4) as *const u32) - }); - println!("GICV_BPR {:x}", unsafe { - *((Platform::GICV_BASE + 0x8_0000_0000 + 0x8) as *const u32) - }); - println!("GICV_ABPR {:x}", unsafe { - *((Platform::GICV_BASE + 0x8_0000_0000 + 0x1c) as *const u32) - }); - println!("GICV_STATUSR {:x}", unsafe { - *((Platform::GICV_BASE + 0x8_0000_0000 + 0x2c) as *const u32) - }); - println!( - "GICV_APR[0] {:x}, GICV_APR[1] {:x}, GICV_APR[2] {:x}, GICV_APR[3] {:x}", - unsafe { *((Platform::GICV_BASE + 0x8_0000_0000 + 0xd0) as *const u32) }, - unsafe { *((Platform::GICV_BASE + 0x8_0000_0000 + 0xd4) as *const u32) }, - unsafe { *((Platform::GICV_BASE + 0x8_0000_0000 + 0xd8) as *const u32) }, - unsafe { *((Platform::GICV_BASE + 0x8_0000_0000 + 0xdc) as *const u32) }, - ); -} diff --git a/src/kernel/vcpu_array.rs b/src/kernel/vcpu_array.rs index a20746986ba5d6ce5d6f88c5dd23d0d96c201c42..46d112b83d1eba3bf67901d71b60f3f8fa7e0ea4 100644 --- a/src/kernel/vcpu_array.rs +++ b/src/kernel/vcpu_array.rs @@ -12,6 +12,7 @@ use alloc::slice::{Iter, IterMut}; use crate::board::{PLAT_DESC, SchedRule}; use crate::kernel::{current_cpu, SchedType, SchedulerRR, Vcpu, VM_NUM_MAX, interrupt_cpu_enable}; +/// vcpu array for storing vcpu objects pub struct VcpuArray { array: [Option; VM_NUM_MAX], len: usize, @@ -43,6 +44,14 @@ impl VcpuArray { pub fn append_vcpu(&mut self, vcpu: Vcpu) { // There is only 1 VCPU from a VM in a PCPU let vm_id = vcpu.vm_id(); + + info!( + "append_vcpu: append VM[{}] vcpu {} on core {}", + vm_id, + vcpu.id(), + current_cpu().id + ); + if vm_id >= self.array.len() { panic!("vm_id > self.array.len()"); } @@ -50,12 +59,7 @@ impl VcpuArray { panic!("self.array[vm_id].is_some()"); } vcpu.set_phys_id(current_cpu().id); - info!( - "append_vcpu: append VM[{}] vcpu {} on core {}", - vm_id, - vcpu.id(), - current_cpu().id - ); + self.array[vm_id] = Some(vcpu); self.len += 1; } @@ -104,8 +108,8 @@ pub fn cpu_sched_init() { } } +/// restore gic context for current vcpu pub fn restore_vcpu_gic(cur_vcpu: Option, trgt_vcpu: Vcpu) { - // println!("restore_vcpu_gic"); match cur_vcpu { None => { // println!("None cur vmid trgt {}", trgt_vcpu.vm_id()); @@ -121,8 +125,8 @@ pub fn restore_vcpu_gic(cur_vcpu: Option, trgt_vcpu: Vcpu) { } } +/// save gic context for current vcpu pub fn save_vcpu_gic(cur_vcpu: Option, trgt_vcpu: Vcpu) { - // println!("save_vcpu_gic"); match cur_vcpu { None => { trgt_vcpu.gic_save_context(); diff --git a/src/kernel/vm.rs b/src/kernel/vm.rs index f9037c934b5aeb23e22baeecfab27df803db7c84..a6efe8b57eed273390083be95e9d7271cb04bb16 100644 --- a/src/kernel/vm.rs +++ b/src/kernel/vm.rs @@ -8,29 +8,29 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use alloc::collections::BTreeMap; use alloc::sync::Arc; use alloc::vec::Vec; use core::mem::size_of; use spin::Mutex; -use crate::arch::{PAGE_SIZE, PTE_S2_FIELD_AP_RO, PTE_S2_NORMAL, PTE_S2_RO}; -use crate::arch::{GICC_CTLR_EN_BIT, GICC_CTLR_EOIMODENS_BIT}; +use crate::arch::{PAGE_SIZE, PTE_S2_FIELD_AP_RO}; +use crate::arch::GICC_CTLR_EN_BIT; use crate::arch::PageTable; use crate::arch::Vgic; -use crate::board::{Platform, PlatOperation}; use crate::config::VmConfigEntry; -use crate::device::EmuDevs; -use crate::kernel::{ - EmuDevData, get_share_mem, mem_pages_alloc, VirtioMmioData, VM_CONTEXT_RECEIVE, VM_CONTEXT_SEND, VMData, -}; -use crate::lib::*; +use crate::device::{EmuDevs, VirtioMmioData}; +use crate::utils::*; use crate::mm::PageFrame; - use super::vcpu::Vcpu; -pub const DIRTY_MEM_THRESHOLD: usize = 0x2000; +pub enum EmuDevData { + VirtioBlk(VirtioMmioData), + VirtioNet(VirtioMmioData), + VirtioConsole(VirtioMmioData), + None, +} + pub const VM_NUM_MAX: usize = 8; pub static VM_IF_LIST: [Mutex; VM_NUM_MAX] = [const { Mutex::new(VmInterface::default()) }; VM_NUM_MAX]; @@ -74,10 +74,11 @@ pub fn vm_if_get_cpu_id(vm_id: usize) -> usize { vm_if.master_cpu_id } +/// compare vm_if mac addr with frame mac addr pub fn vm_if_cmp_mac(vm_id: usize, frame: &[u8]) -> bool { let vm_if = VM_IF_LIST[vm_id].lock(); - for i in 0..6 { - if vm_if.mac[i] != frame[i] { + for (i, &b) in vm_if.mac.iter().enumerate() { + if b != frame[i] { return false; } } @@ -133,9 +134,6 @@ pub fn vm_if_set_mem_map_bit(vm: Vm, pa: usize) { let len = vm.pa_length(i); if pa >= start && pa < start + len { bit += (pa - start) / PAGE_SIZE; - // if vm_if.mem_map.as_mut().unwrap().get(bit) == 0 { - // println!("vm_if_set_mem_map_bit: set pa 0x{:x}", pa); - // } vm_if.mem_map.as_mut().unwrap().set(bit, true); return; } else { @@ -159,19 +157,16 @@ pub fn vm_if_copy_mem_map(vm_id: usize) { let mut vm_if = VM_IF_LIST[vm_id].lock(); let mem_map_cache = vm_if.mem_map_cache.clone(); let map = vm_if.mem_map.as_mut().unwrap(); - // map.set(0x15, true); - // TODO: hard code for offset 0x15000 - // println!( - // "vm_if_copy_mem_map: dirty mem page num {}, first dirty page 0x{:x}, bitmap len {:x}", - // map.sum(), - // map.first(), - // size_of::() * map.vec_len() - // ); - memcpy_safe( - mem_map_cache.as_ref().unwrap().pa() as *const u8, - map.slice() as *const _ as *const u8, - size_of::() * map.vec_len(), - ); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy( + mem_map_cache.as_ref().unwrap().pa() as *const u8, + map.slice() as *const _ as *const u8, + size_of::() * map.vec_len(), + ); + } // clear bitmap after copy map.clear(); } @@ -288,6 +283,7 @@ impl Vm { } } + /// Init the VM's interrupt controller mode. pub fn init_intc_mode(&self, emu: bool) { let vm_inner = self.inner.lock(); for vcpu in &vm_inner.vcpu_list { @@ -301,7 +297,8 @@ impl Vm { vcpu.set_gich_ctlr((GICC_CTLR_EN_BIT) as u32); vcpu.set_hcr(0x80080001); // HCR_EL2_GIC_PASSTHROUGH_VAL } else { - vcpu.set_gich_ctlr((GICC_CTLR_EN_BIT | GICC_CTLR_EOIMODENS_BIT) as u32); + #[cfg(not(feature = "gicv3"))] + vcpu.set_gich_ctlr((GICC_CTLR_EN_BIT | crate::arch::GICC_CTLR_EOIMODENS_BIT) as u32); vcpu.set_hcr(0x80080019); } } @@ -362,7 +359,7 @@ impl Vm { Some(vcpu) } None => { - println!( + error!( "vcpu idx {} is to large than vcpu_list len {}", index, vm_inner.vcpu_list.len() @@ -377,7 +374,7 @@ impl Vm { if vcpu.id() >= vm_inner.vcpu_list.len() { vm_inner.vcpu_list.push(vcpu); } else { - println!("VM[{}] insert VCPU {}", vm_inner.id, vcpu.id()); + trace!("VM[{}] insert VCPU {}", vm_inner.id, vcpu.id()); vm_inner.vcpu_list.insert(vcpu.id(), vcpu); } } @@ -488,9 +485,7 @@ impl Vm { pub fn pt_set_access_permission(&self, ipa: usize, ap: usize) -> (usize, usize) { let vm_inner = self.inner.lock(); match &vm_inner.pt { - Some(pt) => { - return pt.access_permission(ipa, PAGE_SIZE, ap); - } + Some(pt) => pt.access_permission(ipa, PAGE_SIZE, ap), None => { panic!("pt_set_access_permission: vm{} pt is empty", vm_inner.id); } @@ -525,7 +520,7 @@ impl Vm { pub fn pt_dir(&self) -> usize { let vm_inner = self.inner.lock(); match &vm_inner.pt { - Some(pt) => return pt.base_pa(), + Some(pt) => pt.base_pa(), None => { panic!("Vm::pt_dir: vm{} pt is empty", vm_inner.id); } @@ -590,9 +585,7 @@ impl Vm { pub fn vgic(&self) -> Arc { let vm_inner = self.inner.lock(); match &vm_inner.emu_devs[vm_inner.intc_dev_id] { - EmuDevs::Vgic(vgic) => { - return vgic.clone(); - } + EmuDevs::Vgic(vgic) => vgic.clone(), _ => { panic!("vm{} cannot find vgic", vm_inner.id); } @@ -604,18 +597,7 @@ impl Vm { if vm_inner.intc_dev_id >= vm_inner.emu_devs.len() { return false; } - match &vm_inner.emu_devs[vm_inner.intc_dev_id] { - EmuDevs::Vgic(_) => true, - _ => false, - } - } - - // TODO: should copy from or copy to addr, not copy from other vm - pub fn migrate_emu_devs(&self, src_vm: Vm) { - let mut vm_inner = self.inner.lock(); - for (idx, emu_dev) in vm_inner.emu_devs.iter_mut().enumerate() { - emu_dev.migrate_emu_devs(src_vm.emu_dev(idx)); - } + matches!(&vm_inner.emu_devs[vm_inner.intc_dev_id], EmuDevs::Vgic(_)) } pub fn emu_dev(&self, dev_id: usize) -> EmuDevs { @@ -628,17 +610,14 @@ impl Vm { let mut dev_num = 0; for i in 0..vm_inner.emu_devs.len() { - match vm_inner.emu_devs[i] { - EmuDevs::VirtioNet(_) => { - if dev_num == id { - return vm_inner.emu_devs[i].clone(); - } - dev_num += 1; + if let EmuDevs::VirtioNet(_) = vm_inner.emu_devs[i] { + if dev_num == id { + return vm_inner.emu_devs[i].clone(); } - _ => {} + dev_num += 1; } } - return EmuDevs::None; + EmuDevs::None } pub fn emu_blk_dev(&self) -> EmuDevs { @@ -647,7 +626,7 @@ impl Vm { return emu.clone(); } } - return EmuDevs::None; + EmuDevs::None } // Get console dev by ipa. @@ -657,11 +636,7 @@ impl Vm { return self.inner.lock().emu_devs[idx].clone(); } } - // println!("emu_console_dev ipa {:x}", ipa); - // for (idx, emu_dev_cfg) in self.config().emulated_device_list().iter().enumerate() { - // println!("emu dev[{}], ipa 0x{:x}", idx, emu_dev_cfg.base_ipa); - // } - return EmuDevs::None; + EmuDevs::None } pub fn ncpu(&self) -> usize { @@ -683,15 +658,19 @@ impl Vm { false } + pub fn vcpuid_to_vcpu(&self, vcpuid: usize) -> Option { + let inner = self.inner.lock(); + inner.vcpu_list.iter().find(|vcpu| vcpu.id() == vcpuid).cloned() + } + pub fn vcpuid_to_pcpuid(&self, vcpuid: usize) -> Result { - // println!("vcpuid_to_pcpuid"); let vm_inner = self.inner.lock(); if vcpuid < vm_inner.cpu_num { let vcpu = vm_inner.vcpu_list[vcpuid].clone(); drop(vm_inner); - return Ok(vcpu.phys_id()); + Ok(vcpu.phys_id()) } else { - return Err(()); + Err(()) } } @@ -702,29 +681,29 @@ impl Vm { return Ok(vcpuid); } } - return Err(()); + Err(()) } pub fn vcpu_to_pcpu_mask(&self, mask: usize, len: usize) -> usize { let mut pmask = 0; for i in 0..len { let shift = self.vcpuid_to_pcpuid(i); - if mask & (1 << i) != 0 && !shift.is_err() { + if mask & (1 << i) != 0 && shift.is_ok() { pmask |= 1 << shift.unwrap(); } } - return pmask; + pmask } pub fn pcpu_to_vcpu_mask(&self, mask: usize, len: usize) -> usize { let mut pmask = 0; for i in 0..len { let shift = self.pcpuid_to_vcpuid(i); - if mask & (1 << i) != 0 && !shift.is_err() { + if mask & (1 << i) != 0 && shift.is_ok() { pmask |= 1 << shift.unwrap(); } } - return pmask; + pmask } pub fn show_pagetable(&self, ipa: usize) { @@ -742,143 +721,18 @@ impl Vm { vm_inner.ready = _ready; } - // init for migrate restore - pub fn context_vm_migrate_init(&self) { - let mvm = vm(0).unwrap(); - // for i in 0..self.ncpu() { - let size = size_of::(); - // println!("context_vm_migrate_init: VM Data size 0x{:x}", size); - match mem_pages_alloc(round_up(size, PAGE_SIZE) / PAGE_SIZE) { - Ok(pf) => { - mvm.pt_map_range( - get_share_mem(VM_CONTEXT_RECEIVE), - round_up(size, PAGE_SIZE), - pf.pa(), - PTE_S2_NORMAL, - true, - ); - let mut inner = self.inner.lock(); - inner.migrate_restore_pf.push(pf); - } - Err(_) => { - panic!("context_vm_migrate_restore_init: mem_pages_alloc for vm context failed"); - } - } - // } - } - - pub fn context_vm_migrate_save(&self) { - let mvm = vm(0).unwrap(); - let size = size_of::(); - match mem_pages_alloc(round_up(size, PAGE_SIZE) / PAGE_SIZE) { - Ok(pf) => { - let mut vm_data = unsafe { &mut *(pf.pa as *mut VMData) }; - let base = get_share_mem(VM_CONTEXT_SEND); - // println!("pt map base 0x{:x} size 0x{:x}", base, size); - mvm.pt_map_range(base, round_up(size, PAGE_SIZE), pf.pa(), PTE_S2_RO, true); - - // key: pcpuid, val: vcpuid - let mut cpuid_map: BTreeMap = BTreeMap::new(); - for vcpu_id in 0..self.cpu_num() { - let vcpu = self.vcpu(vcpu_id).unwrap(); - // vm context - vcpu.migrate_vm_ctx_save(&(vm_data.vm_ctx[vcpu_id]) as *const _ as usize); - // vcpu context - vcpu.migrate_vcpu_ctx_save(&(vm_data.vcpu_ctx[vcpu_id]) as *const _ as usize); - // cpu gic context - vcpu.migrate_gic_ctx_save(&(vm_data.gic_ctx[vcpu_id]) as *const _ as usize); - cpuid_map.insert(self.vcpuid_to_pcpuid(vcpu_id).unwrap(), vcpu_id); - } - - let mut inner = self.inner.lock(); - for (idx, emu) in inner.emu_devs.iter().enumerate() { - match emu { - EmuDevs::Vgic(vgic) => { - vgic.save_vgic_data(&mut vm_data.vgic_ctx, &cpuid_map); - } - EmuDevs::VirtioBlk(mmio) => { - vm_data.emu_devs[idx] = EmuDevData::VirtioBlk(VirtioMmioData::default()); - if let EmuDevData::VirtioBlk(mmio_data) = &mut vm_data.emu_devs[idx] { - // println!("vm[{}] save virtio blk", inner.id); - mmio.save_mmio_data(mmio_data, &inner.pa_region); - } - } - EmuDevs::VirtioNet(mmio) => { - vm_data.emu_devs[idx] = EmuDevData::VirtioNet(VirtioMmioData::default()); - if let EmuDevData::VirtioNet(mmio_data) = &mut vm_data.emu_devs[idx] { - // println!("vm[{}] save virtio net", inner.id); - mmio.save_mmio_data(mmio_data, &inner.pa_region); - } - } - EmuDevs::VirtioConsole(mmio) => { - vm_data.emu_devs[idx] = EmuDevData::VirtioConsole(VirtioMmioData::default()); - if let EmuDevData::VirtioConsole(mmio_data) = &mut vm_data.emu_devs[idx] { - // println!("vm[{}] save virtio console", inner.id); - mmio.save_mmio_data(mmio_data, &inner.pa_region); - } - } - EmuDevs::None => {} - } - } - inner.migrate_save_pf.push(pf); - } - Err(_) => {} - } - } - - pub fn context_vm_migrate_restore(&self) { - // key: vcpuid, val: pcpuid - let mut vcpuid_map: BTreeMap = BTreeMap::new(); - for vcpu_id in 0..self.cpu_num() { - vcpuid_map.insert(vcpu_id, self.vcpuid_to_pcpuid(vcpu_id).unwrap()); - } + pub fn get_vcpu_by_mpidr(&self, mpdir: usize) -> Option { let inner = self.inner.lock(); - let pa = inner.migrate_restore_pf[0].pa(); - let vm_data = unsafe { &mut *(pa as *mut VMData) }; - // migrate emu dev - for (idx, emu) in inner.emu_devs.iter().enumerate() { - match emu { - EmuDevs::Vgic(vgic) => { - vgic.restore_vgic_data(&vm_data.vgic_ctx, &inner.vcpu_list, &vcpuid_map); - } - EmuDevs::VirtioBlk(mmio) => { - if let EmuDevData::VirtioBlk(mmio_data) = &vm_data.emu_devs[idx] { - mmio.restore_mmio_data(mmio_data, &inner.pa_region); - } - } - EmuDevs::VirtioNet(mmio) => { - // println!("context_vm_migrate_restore: net"); - if let EmuDevData::VirtioNet(mmio_data) = &vm_data.emu_devs[idx] { - mmio.restore_mmio_data(mmio_data, &inner.pa_region); - } - } - EmuDevs::VirtioConsole(mmio) => { - // println!("context_vm_migrate_restore: console"); - if let EmuDevData::VirtioConsole(mmio_data) = &mut vm_data.emu_devs[idx] { - mmio.restore_mmio_data(mmio_data, &inner.pa_region); - } - } - EmuDevs::None => {} + let cpuid = if (mpdir >> 8) & 0xff != 0 { + if cfg!(feature = "rk3588") { + mpdir >> 8 + } else { + 4 + (mpdir & 0xff) } - } - drop(inner); - for vcpu_id in 0..self.cpu_num() { - let vcpu = self.vcpu(vcpu_id).unwrap(); - vcpu.migrate_vm_ctx_restore(&vm_data.vm_ctx[vcpu_id] as *const _ as usize); - vcpu.migrate_vcpu_ctx_restore(&vm_data.vcpu_ctx[vcpu_id] as *const _ as usize); - // cpu gic context - vcpu.migrate_gic_ctx_restore(&(vm_data.gic_ctx[vcpu_id]) as *const _ as usize); - } - } - - pub fn share_mem_base(&self) -> usize { - let inner = self.inner.lock(); - inner.share_mem_base - } - - pub fn add_share_mem_base(&self, len: usize) { - let mut inner = self.inner.lock(); - inner.share_mem_base += len; + } else { + mpdir & 0xff + }; + inner.vcpu_list.iter().find(|vcpu| vcpu.id() == cpuid).cloned() } } @@ -906,12 +760,6 @@ pub struct VmInner { pub intc_dev_id: usize, pub int_bitmap: Option>, - // migration - // pub migration_state: bool, - pub share_mem_base: usize, - pub migrate_save_pf: Vec, - pub migrate_restore_pf: Vec, - // iommu pub iommu_ctx_id: Option, @@ -939,10 +787,6 @@ impl VmInner { intc_dev_id: 0, int_bitmap: Some(BitAlloc4K::default()), - // migration_state: false, - share_mem_base: Platform::SHARE_MEM_BASE, // hard code - migrate_save_pf: vec![], - migrate_restore_pf: vec![], iommu_ctx_id: None, emu_devs: Vec::new(), @@ -968,10 +812,6 @@ impl VmInner { intc_dev_id: 0, int_bitmap: Some(BitAlloc4K::default()), - // migration_state: false, - share_mem_base: Platform::SHARE_MEM_BASE, // hard code - migrate_save_pf: vec![], - migrate_restore_pf: vec![], iommu_ctx_id: None, emu_devs: Vec::new(), med_blk_id: None, @@ -984,7 +824,7 @@ pub static VM_LIST: Mutex> = Mutex::new(Vec::new()); pub fn push_vm(id: usize) -> Result<(), ()> { let mut vm_list = VM_LIST.lock(); if vm_list.iter().any(|x| x.id() == id) { - println!("push_vm: vm {} already exists", id); + error!("push_vm: vm {} already exists", id); Err(()) } else { vm_list.push(Vm::new(id)); @@ -1014,7 +854,7 @@ pub fn vm_list_size() -> usize { pub fn vm_ipa2pa(vm: Vm, ipa: usize) -> usize { if ipa == 0 { - println!("vm_ipa2pa: VM {} access invalid ipa {:x}", vm.id(), ipa); + error!("vm_ipa2pa: VM {} access invalid ipa {:x}", vm.id(), ipa); return 0; } @@ -1028,13 +868,13 @@ pub fn vm_ipa2pa(vm: Vm, ipa: usize) -> usize { } } - println!("vm_ipa2pa: VM {} access invalid ipa {:x}", vm.id(), ipa); - return 0; + error!("vm_ipa2pa: VM {} access invalid ipa {:x}", vm.id(), ipa); + 0 } pub fn vm_pa2ipa(vm: Vm, pa: usize) -> usize { if pa == 0 { - println!("vm_pa2ipa: VM {} access invalid pa {:x}", vm.id(), pa); + error!("vm_pa2ipa: VM {} access invalid pa {:x}", vm.id(), pa); return 0; } @@ -1044,13 +884,13 @@ pub fn vm_pa2ipa(vm: Vm, pa: usize) -> usize { } } - println!("vm_pa2ipa: VM {} access invalid pa {:x}", vm.id(), pa); - return 0; + error!("vm_pa2ipa: VM {} access invalid pa {:x}", vm.id(), pa); + 0 } -pub fn pa2ipa(pa_region: &Vec, pa: usize) -> usize { +pub fn pa2ipa(pa_region: &[VmPa], pa: usize) -> usize { if pa == 0 { - println!("pa2ipa: access invalid pa {:x}", pa); + error!("pa2ipa: access invalid pa {:x}", pa); return 0; } @@ -1060,11 +900,11 @@ pub fn pa2ipa(pa_region: &Vec, pa: usize) -> usize { } } - println!("pa2ipa: access invalid pa {:x}", pa); - return 0; + error!("pa2ipa: access invalid pa {:x}", pa); + 0 } -pub fn ipa2pa(pa_region: &Vec, ipa: usize) -> usize { +pub fn ipa2pa(pa_region: &[VmPa], ipa: usize) -> usize { if ipa == 0 { // println!("ipa2pa: access invalid ipa {:x}", ipa); return 0; @@ -1081,5 +921,18 @@ pub fn ipa2pa(pa_region: &Vec, ipa: usize) -> usize { } // println!("ipa2pa: access invalid ipa {:x}", ipa); - return 0; + 0 +} + +pub fn cpuid2mpidr(cpuid: usize) -> usize { + if cfg!(feature = "rk3588") { + 0x81000000 | (cpuid << 8) + } else { + // qemu + if cpuid < 4 { + cpuid | (1 << 31) + } else { + 0x100 | (cpuid - 4) | (1 << 31) + } + } } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..6fb698ba355d657140d51ef3b8ac3b3b078dcca5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,217 @@ +// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. +// Rust-Shyper is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! Rust-Shyper is a type-1 hypervisor based on Rust. It now supports Aarch64 architecture. +//! The introduces of all modules are showed below: +//! * [arch]: The architecture-dependent code, including the code for the Aarch64 architecture. +//! * [board]: The code for the specific board, including the code for the Raspberry Pi 4B, tx2 and so on. +//! * [config]: The configuration file for the hypervisor. +//! * [device]: The emulation of the devices, including virtio emulation. +//! * [driver]: The driver for the devices, including gpio and uart. +//! * [kernel]: The rust-shyper hypervisor kernel code, manages the virtual machines, interrupts, address translation, vcpu scheduling and so on. +//! * [mm]: The memory management code, including the code for the page frame allocator and rust global allocator. +//! * [utils]: The utility code, including the code for the barrier, bitmap and so on. +//! * [vmm]: The virtual machine monitor code, including the code for virtual machine management such as creation, startup, shutdown and removal. +//! * [macros]: Defines the macros for the hypervisor. +//! * error: Defines the error type for the hypervisor. +//! * panic: Defines the panic handler for the hypervisor. + +#![no_std] +#![no_main] +#![feature(core_intrinsics)] +#![feature(alloc_error_handler)] +#![feature(extract_if)] +#![feature(inline_const)] +#![feature(naked_functions)] +#![feature(asm_const)] +#![feature(error_in_core)] +#![feature(slice_group_by)] +#![feature(c_str_literals)] +#![allow(unused_doc_comments)] +#![allow(special_module_name)] +#![allow(clippy::enum_variant_names)] +#![allow(clippy::module_inception)] +#![allow(clippy::wrong_self_convention)] +#![allow(clippy::mut_from_ref)] +#![allow(clippy::upper_case_acronyms)] +#![allow(clippy::modulo_one)] +#![allow(clippy::needless_range_loop)] + +#[macro_use] +extern crate alloc; +extern crate fdt; +#[macro_use] +extern crate log; +#[macro_use] +extern crate memoffset; + +use device::init_vm0_dtb; +use kernel::{cpu_init, interrupt_init, mem_init, timer_init}; +use mm::heap_init; +use vmm::{vm_init, vmm_boot_vm}; + +use crate::kernel::{cpu_sched_init, iommu_init}; + +#[cfg(feature = "doc")] +#[macro_use] +pub mod macros; +#[allow(dead_code)] +#[cfg(feature = "doc")] +pub mod arch; +#[allow(dead_code)] +#[cfg(feature = "doc")] +pub mod board; +#[allow(dead_code)] +#[cfg(feature = "doc")] +pub mod config; +#[allow(dead_code)] +#[cfg(feature = "doc")] +pub mod device; +#[allow(dead_code)] +#[cfg(feature = "doc")] +pub mod driver; +#[allow(dead_code)] +#[cfg(feature = "doc")] +pub mod kernel; +#[allow(dead_code)] +#[cfg(feature = "doc")] +pub mod mm; +#[allow(dead_code)] +#[cfg(feature = "doc")] +pub mod panic; +#[allow(dead_code)] +#[cfg(feature = "doc")] +pub mod utils; +#[allow(dead_code)] +#[cfg(feature = "doc")] +pub mod vmm; + +#[cfg(feature = "doc")] +pub mod error; +#[cfg(feature = "doc")] +pub mod built_info { + include!(concat!(env!("OUT_DIR"), "/built.rs")); +} + +#[macro_use] +#[cfg(not(feature = "doc"))] +mod macros; +#[allow(dead_code)] +#[cfg(not(feature = "doc"))] +mod arch; +#[allow(dead_code)] +#[cfg(not(feature = "doc"))] +mod board; +#[allow(dead_code)] +#[cfg(not(feature = "doc"))] +mod config; +#[allow(dead_code)] +#[cfg(not(feature = "doc"))] +mod device; +#[allow(dead_code)] +#[cfg(not(feature = "doc"))] +mod driver; +#[cfg(not(feature = "doc"))] +mod error; +#[allow(dead_code)] +#[cfg(not(feature = "doc"))] +mod kernel; +#[allow(dead_code)] +#[cfg(not(feature = "doc"))] +mod mm; +#[allow(dead_code)] +#[cfg(not(feature = "doc"))] +mod panic; +#[allow(dead_code)] +#[cfg(not(feature = "doc"))] +mod utils; +#[allow(dead_code)] +#[cfg(not(feature = "doc"))] +mod vmm; +#[cfg(not(feature = "doc"))] +mod built_info { + include!(concat!(env!("OUT_DIR"), "/built.rs")); +} + +// use lib::{BitAlloc, BitAlloc256}; + +pub static SYSTEM_FDT: spin::Once> = spin::Once::new(); + +fn print_built_info() { + println!( + "Welcome to {} {} {}!", + env!("BOARD"), + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION") + ); + println!( + "Built at {build_time} by {hostname}\nCompiler: {rustc_version}\nFeatures: {features:?}\nCommit: {commit_hash}", + build_time = env!("BUILD_TIME"), + hostname = env!("HOSTNAME"), + commit_hash = env!("GIT_COMMIT"), + rustc_version = built_info::RUSTC_VERSION, + features = built_info::FEATURES_LOWERCASE_STR, + ); +} + +// Only core 0 will execute this function +#[no_mangle] +pub fn init(dtb: &mut fdt::myctypes::c_void) { + print_built_info(); + + #[cfg(feature = "pi4")] + { + crate::driver::gpio_select_function(0, 4); + crate::driver::gpio_select_function(1, 4); + } + + heap_init(); + kernel::logger_init().unwrap(); + mem_init(); + // SAFETY: + // DTB is saved value from boot_stage + // And it is passed by bootloader + unsafe { + init_vm0_dtb(dtb).unwrap(); + } + iommu_init(); + cpu_init(); + interrupt_init(); + timer_init(); + cpu_sched_init(); + + #[cfg(not(feature = "secondary_start"))] + crate::utils::barrier(); + + vm_init(); + info!( + "{} Hypervisor init ok\n\nStart booting Monitor VM ...", + env!("CARGO_PKG_NAME") + ); + vmm_boot_vm(0); + + loop { + core::hint::spin_loop(); + } +} + +// Other cores will execute this function +pub fn secondary_init(mpidr: usize) { + cpu_init(); + interrupt_init(); + timer_init(); + cpu_sched_init(); + + #[cfg(not(feature = "secondary_start"))] + crate::utils::barrier(); + use crate::arch::guest_cpu_on; + guest_cpu_on(mpidr); + crate::kernel::cpu_idle(); +} diff --git a/src/lib/fatfs.rs b/src/lib/fatfs.rs deleted file mode 100644 index 01edd3a638bac3e4623cb0864325ebae5c3faf5f..0000000000000000000000000000000000000000 --- a/src/lib/fatfs.rs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. -// Rust-Shyper is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use core_io as io; -use io::{Read, SeekFrom}; -use io::prelude::*; - -use crate::arch::PAGE_SIZE; -use crate::board::{DISK_PARTITION_0_START, platform_blk_read}; - -use super::{round_down, round_up}; - -struct Disk { - pointer: usize, - size: usize, -} - -impl Disk { - const fn default() -> Disk { - Disk { - pointer: 0, - size: 0, - } - } -} - -// TODO: add fatfs read -impl core_io::Read for Disk { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let sector = round_down(self.pointer, 512) / 512; - let offset = self.pointer - round_down(self.pointer, 512); - let count = round_up(offset + buf.len(), 512) / 512; - assert!(count <= 8); - if buf.len() == PAGE_SIZE { - let addr = buf.as_ptr() as usize; - platform_blk_read(sector + DISK_PARTITION_0_START, count, addr); - return Ok(buf.len()); - } - - let result = crate::kernel::mem_page_alloc(); - if let Ok(frame) = result { - // if count >= 4 { - // println!( - // "read sector {} count {} offset {} buf.len {} pointer {}", - // sector, - // count, - // offset, - // buf.len(), - // self.pointer - // ); - // } - platform_blk_read(sector + DISK_PARTITION_0_START, count, frame.pa()); - for i in 0..buf.len() { - buf[i] = frame.as_slice()[offset + i]; - // print!("{}", buf[i]); - } - self.pointer += buf.len(); - Ok(buf.len()) - } else { - println!("read failed"); - Ok(0) - } - } -} - -impl core_io::Write for Disk { - fn flush(&mut self) -> io::Result<()> { - // println!("in flush"); - Ok(()) - } - - // TODO: add fatfs write - fn write(&mut self, buf: &[u8]) -> io::Result { - println!("in write"); - println!("write dropped"); - Ok(0) - } -} - -impl core_io::Seek for Disk { - fn seek(&mut self, pos: SeekFrom) -> io::Result { - match pos { - SeekFrom::Start(u) => { - self.pointer = u as usize; - } - SeekFrom::End(i) => { - self.pointer = self.size - (i as usize); - } - SeekFrom::Current(i) => { - self.pointer += i as usize; - } - } - Ok(self.pointer as u64) - } -} - -// lazy_static! { -// static FS: Mutex>> = Mutex::new(None); -// } -// static ROOT_DIR: Mutex>> = Mutex::new(None); - -pub fn fs_init() { - // let mut disk = Disk { - // pointer: 0, - // size: 536870912, - // }; - - // // let fs = FS.lock(); - // let mut fs: io::Result> = - // fatfs::FileSystem::new(&mut disk, fatfs::FsOptions::new()); - - // let fs = fatfs::FileSystem::new(&mut disk, fatfs::FsOptions::new()).unwrap(); - // let root_dir = fs.root_dir(); - // let file = root_dir.open_file("hello.txt"); - // match file { - // Ok(mut file) => { - // let mut buf = [0u8; 5000]; - // // let len = file.seek(SeekFrom::End(0)).unwrap(); - // file.read(&mut buf); - // file.read(&mut buf[4096..]); - // let mut idx = 0; - // for i in 0..buf.len() { - // let val = buf[i as usize]; - // if val != 0 { - // idx += 1; - // } - // // let tmp = char::from_u32(val as u32); - // // print!("{}", tmp.unwrap()); - // } - // println!("idx is {}", idx); - // println!("FAT file system init ok"); - // } - // Err(_) => { - // println!("err"); - // } - // } -} - -pub fn fs_read_to_mem(filename: &str, buf: &mut [u8]) -> bool { - let mut disk = Disk { - pointer: 0, - size: 536870912, - }; - let count = round_up(buf.len(), PAGE_SIZE) / PAGE_SIZE; - - let fs = fatfs::FileSystem::new(&mut disk, fatfs::FsOptions::new()).unwrap(); - let root_dir = fs.root_dir(); - let file = root_dir.open_file(filename); - match file { - Ok(mut file) => { - for i in 0..count { - if i + 1 != count { - file.read(&mut buf[i * PAGE_SIZE..(i + 1) * PAGE_SIZE]); - } else { - file.read(&mut buf[i * PAGE_SIZE..]); - } - } - return true; - } - Err(_) => { - println!("read file {} failed!", filename); - return false; - } - } -} - -pub fn fs_file_size(filename: &str) -> usize { - let mut disk = Disk { - pointer: 0, - size: 536870912, - }; - let result = fatfs::FileSystem::new(&mut disk, fatfs::FsOptions::new()); - match result { - Ok(fs) => { - let root_dir = fs.root_dir(); - let file = root_dir.open_file(filename); - match file { - Ok(mut file) => { - return file.seek(SeekFrom::End(0)).unwrap() as usize; - } - Err(_) => { - return 0; - } - } - } - Err(err) => { - panic!("Err is {:#?}", err); - } - } - // let fs = .unwrap(); -} diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000000000000000000000000000000000000..b242b5c3c021b5a96dc7c0ac1d7b69e4fb2f53c2 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,71 @@ +//! Macros for the hypervisor. + +/// A macro for formatted printing without a newline. +/// +/// This macro is a reimplementation of the standard `print!` macro, redirecting +/// the output to a custom print handler defined in the `_print` function from +/// the `utils` module. +/// +/// # Examples +/// +/// ``` +/// # use my_crate::print; +/// print!("Hello, {}!", "world"); +/// ``` +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::utils::_print(format_args!($($arg)*))); +} + +/// A macro for formatted printing with a newline. +/// +/// This macro is similar to the standard `println!` macro, redirecting the output +/// to a custom print handler defined in the `_print` function from the `utils` module. +/// +/// # Examples +/// +/// ``` +/// # use my_crate::println; +/// println!("Hello, {}!", "world"); +/// ``` +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); +} + +/// A macro for declaring an enum with associated handlers. +/// +/// This macro simplifies the declaration of an enum with associated handlers. +/// It generates both the enum variants and a static array containing the +/// corresponding handlers. +/// +/// # Examples +/// +/// ``` +/// # use my_crate::declare_enum_with_handler; +/// declare_enum_with_handler! { +/// /// An example enum with handlers. +/// pub enum MyEnum [pub HANDLERS => HandlerType] { +/// Variant1 => handler_function1, +/// Variant2 => handler_function2, +/// } +/// } +/// ``` +#[macro_export] +macro_rules! declare_enum_with_handler { + ( + $(#[$attr:meta])* + $enum_vis:vis enum $enum_name:ident [$array_vis:vis $array:ident => $handler_type:ty] { + $($vis:vis $variant:ident => $handler:expr, )* + } + ) => { + $(#[$attr])* + $enum_vis enum $enum_name { + $($vis $variant, )* + } + $array_vis static $array: &[$handler_type] = &[ + $($handler, )* + ]; + } +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 7ac67fc27c3bfd3a4d77f619d979605152bc15a1..0000000000000000000000000000000000000000 --- a/src/main.rs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. -// Rust-Shyper is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -#![no_std] -#![no_main] -#![feature(core_intrinsics)] -#![feature(default_alloc_error_handler)] -#![feature(alloc_error_handler)] -#![feature(const_btree_new)] -#![feature(drain_filter)] -#![feature(inline_const)] -#![allow(unused_doc_comments)] -#![allow(special_module_name)] - -#[macro_use] -extern crate alloc; -extern crate fdt; -#[macro_use] -// extern crate lazy_static; -extern crate log; - -// extern crate rlibc; - -use device::{init_vm0_dtb, mediated_dev_init}; -use kernel::{cpu_init, interrupt_init, mem_init, timer_init}; -use mm::heap_init; -use vmm::{vm_init, vmm_boot_vm}; - -use crate::kernel::{cpu_sched_init, hvc_init, iommu_init}; - -#[macro_export] -macro_rules! print { - ($($arg:tt)*) => ($crate::lib::_print(format_args!($($arg)*))); -} - -#[macro_export] -macro_rules! println { - () => ($crate::print!("\n")); - ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); -} - -#[allow(dead_code)] -mod arch; -#[allow(dead_code)] -mod board; -#[allow(dead_code)] -mod config; -#[allow(dead_code)] -mod device; -#[allow(dead_code)] -mod driver; -#[allow(dead_code)] -mod kernel; -#[allow(dead_code)] -mod lib; -#[allow(dead_code)] -mod mm; -#[allow(dead_code)] -mod panic; -#[allow(dead_code)] -mod vmm; - -// use lib::{BitAlloc, BitAlloc256}; - -pub static SYSTEM_FDT: spin::Once> = spin::Once::new(); - -#[no_mangle] -pub fn init(cpu_id: usize, dtb: *mut fdt::myctypes::c_void) { - // const UART0: *mut u8 = 0x0900_0000 as *mut u8; - // let out_str = b"AArch64 Bare Metal"; - // for byte in out_str { - // crate::driver::uart::putc(*byte); - // } - // tegra_emmc_blk_read(0, 0, 0 as *mut _); - if cpu_id == 0 { - #[cfg(feature = "tx2")] - println!("Welcome to TX2 Rust-Shyper Hypervisor!"); - #[cfg(feature = "qemu")] - println!("Welcome to Qemu Rust-Shyper Hypervisor!"); - #[cfg(feature = "pi4")] - println!("Welcome to PI4 Rust-Shyper Hypervisor!"); - println!("Built At {}", env!("BUILD_TIME")); - - #[cfg(feature = "pi4")] - { - crate::driver::gpio_select_function(0, 4); - crate::driver::gpio_select_function(1, 4); - } - - heap_init(); - let _ = kernel::logger_init(); - mem_init(); - init_vm0_dtb(dtb); - hvc_init(); - iommu_init(); - } - cpu_init(); - interrupt_init(); - timer_init(); - cpu_sched_init(); - if cpu_id == 0 { - mediated_dev_init(); - } - crate::lib::barrier(); - if cpu_id != 0 { - crate::kernel::cpu_idle(); - } - vm_init(); - println!("Rust-Shyper Hypervisor init ok\n\nStart booting Monitor VM ..."); - vmm_boot_vm(0); - - loop {} -} diff --git a/src/mm/heap.rs b/src/mm/heap.rs index 524242d273f4a22467ceae179c845bf11da2ad0f..7a4878b6eb1ff6e6f9fcfeb23cd34e4ae9deffb5 100644 --- a/src/mm/heap.rs +++ b/src/mm/heap.rs @@ -21,11 +21,18 @@ struct HeapRegion([u8; HEAP_SIZE]); static mut HEAP_REGION: HeapRegion = HeapRegion([0; HEAP_SIZE]); #[global_allocator] +/// Global heap allocator pub static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::empty(); +/// Initialize heap allocator pub fn heap_init() { - println!("init buddy system"); + // SAFEFY: + // HEAP_REGION is aligned and HEAP_SIZE is a multiple of PAGE_SIZE unsafe { + println!( + "init buddy system, heap start from {:x}", + HEAP_REGION.0.as_mut_ptr() as usize + ); HEAP_ALLOCATOR .lock() .init(HEAP_REGION.0.as_mut_ptr() as usize, HEAP_SIZE); diff --git a/src/mm/mod.rs b/src/mm/mod.rs index b9e82154596d0437b2832c6913c5bf5fc24efd3f..b6947f5698e30d102132312a2b58c3166d653d48 100644 --- a/src/mm/mod.rs +++ b/src/mm/mod.rs @@ -8,6 +8,8 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +//! Memory management module, including heap and page frame allocator. + pub use self::heap::*; pub use self::page_frame::*; diff --git a/src/mm/page_frame.rs b/src/mm/page_frame.rs index d5f45e3354e8020cbaa3873db620182dee45481d..cbe1b6ca62d04c1fa8bc1bc8bcdf48e9c4f76a9c 100644 --- a/src/mm/page_frame.rs +++ b/src/mm/page_frame.rs @@ -10,9 +10,9 @@ use crate::arch::PAGE_SIZE; use crate::kernel::{mem_heap_free, mem_heap_alloc, AllocError}; -use crate::lib::{memset_safe, trace}; #[derive(Debug)] +/// PageFrame struct represents a page frame, consisting of physical address and page number. pub struct PageFrame { pub pa: usize, pub page_num: usize, @@ -24,6 +24,7 @@ impl PageFrame { PageFrame { pa, page_num } } + /// Allocate a page frame with given page number. pub fn alloc_pages(page_num: usize) -> Result { match mem_heap_alloc(page_num, false) { Ok(pa) => Ok(Self::new(pa, page_num)), @@ -34,24 +35,6 @@ impl PageFrame { pub fn pa(&self) -> usize { self.pa } - - pub fn zero(&self) { - memset_safe(self.pa as *mut u8, 0, PAGE_SIZE); - } - - pub fn as_slice(&self) -> &'static [T] { - if trace() && self.pa() < 0x1000 { - panic!("illegal addr {:x}", self.pa()); - } - unsafe { core::slice::from_raw_parts(self.pa as *const T, PAGE_SIZE / core::mem::size_of::()) } - } - - pub fn as_mut_slice(&self) -> &'static mut [T] { - if trace() && self.pa() < 0x1000 { - panic!("illegal addr {:x}", self.pa()); - } - unsafe { core::slice::from_raw_parts_mut(self.pa as *mut T, PAGE_SIZE / core::mem::size_of::()) } - } } impl Drop for PageFrame { diff --git a/src/mm/page_pool.rs b/src/mm/page_pool.rs index 3f3a4ae94064edc34b7f557c057b28d3464d1bf3..b8bd611aa9bab93a150599a533f0786836f064cb 100644 --- a/src/mm/page_pool.rs +++ b/src/mm/page_pool.rs @@ -18,8 +18,11 @@ use crate::mm::PageFrame; use self::Error::*; #[derive(Copy, Clone, Debug)] +/// Error type for page pool allocator. pub enum Error { + /// No free page frame. OutOfFrame, + /// Free a page frame that is not allocated. FreeNotAllocated, } @@ -28,6 +31,7 @@ struct PagePool { allocated: Vec, } +/// PagePoolTrait is a trait for page pool allocator, which manages a range of memory and allocates/free page frames. pub trait PagePoolTrait { fn init(&mut self, range: Range); fn allocate(&mut self) -> Result; @@ -69,12 +73,14 @@ static PAGE_POOL: Mutex = Mutex::new(PagePool { allocated: Vec::new(), }); +/// Initialize the page pool allocator. pub fn init() { let range = super::config::paged_range(); let mut pool = PAGE_POOL.lock(); pool.init(range); } +/// Allocate a page frame, panic when error happens. pub fn alloc() -> PageFrame { let mut pool = PAGE_POOL.lock(); if let Ok(frame) = pool.allocate() { @@ -84,12 +90,15 @@ pub fn alloc() -> PageFrame { } } + +/// Try to alloc a page frame, return the error when error happens. pub fn try_alloc() -> Result { let mut pool = PAGE_POOL.lock(); let r = pool.allocate(); r } +/// Free a page frame, return the error when error happens. pub fn free(pa: usize) -> Result<(), Error> { let mut pool = PAGE_POOL.lock(); pool.free(pa) diff --git a/src/panic.rs b/src/panic.rs index e1eae9983208cc09facdf971458d5960d5251064..fdeae81515e046b724d7a4c0fde74fcbba509e52 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -8,12 +8,15 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +//! Panic handler + use core::panic::PanicInfo; #[cfg_attr(target_os = "none", panic_handler)] -#[no_mangle] fn panic(info: &PanicInfo) -> ! { println!("[Panic]"); println!("{}", info); - loop {} + loop { + core::hint::spin_loop(); + } } diff --git a/src/lib/barrier.rs b/src/utils/barrier.rs similarity index 47% rename from src/lib/barrier.rs rename to src/utils/barrier.rs index e76ad3cc7c7d02c1217f457914b08839f7c496c7..7b4562ea8cf4f2f33dbf8c2421b1bef41147ad4f 100644 --- a/src/lib/barrier.rs +++ b/src/utils/barrier.rs @@ -8,55 +8,32 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use core::ptr; use core::sync::atomic::AtomicUsize; use core::sync::atomic::Ordering; use crate::board::PLAT_DESC; -use crate::lib::round_up; +use super::round_up; struct CpuSyncToken { n: usize, count: AtomicUsize, - ready: bool, } -static mut CPU_GLB_SYNC: CpuSyncToken = CpuSyncToken { +static CPU_GLB_SYNC: CpuSyncToken = CpuSyncToken { n: PLAT_DESC.cpu_desc.num, count: AtomicUsize::new(0), - ready: true, -}; - -static mut CPU_FUNC_SYNC: CpuSyncToken = CpuSyncToken { - n: 0, - count: AtomicUsize::new(0), - ready: true, }; #[inline(never)] +/// Wait for all CPUs to reach the barrier. pub fn barrier() { - unsafe { - let ori = CPU_GLB_SYNC.count.fetch_add(1, Ordering::Relaxed); - let next_count = round_up(ori + 1, CPU_GLB_SYNC.n); - while CPU_GLB_SYNC.count.load(Ordering::Acquire) < next_count { - core::hint::spin_loop(); - } - } -} - -#[inline(never)] -pub fn func_barrier() { - unsafe { - let ori = CPU_FUNC_SYNC.count.fetch_add(1, Ordering::Relaxed); - let next_count = round_up(ori + 1, CPU_FUNC_SYNC.n); - while CPU_FUNC_SYNC.count.load(Ordering::Acquire) < next_count { - core::hint::spin_loop(); - } + let ori = CPU_GLB_SYNC.count.fetch_add(1, Ordering::Release); + let next_count = round_up(ori + 1, CPU_GLB_SYNC.n); + while CPU_GLB_SYNC.count.load(Ordering::Acquire) < next_count { + core::hint::spin_loop(); } } -pub fn set_barrier_num(num: usize) { - unsafe { - ptr::write_volatile(&mut CPU_FUNC_SYNC.n, num); - } +pub fn reset_barrier() { + CPU_GLB_SYNC.count.store(0, Ordering::Relaxed); } diff --git a/src/lib/bitmap.rs b/src/utils/bitmap.rs similarity index 88% rename from src/lib/bitmap.rs rename to src/utils/bitmap.rs index 14760344a82ce39d9bc8f96d25fde471fed45f62..716dc893eec6e3254cc684952d968f33e0f10742 100644 --- a/src/lib/bitmap.rs +++ b/src/utils/bitmap.rs @@ -17,40 +17,41 @@ use alloc::vec::Vec; -use crate::lib::bit_get; +use crate::utils::bit_get; +/// a trait of bit map pub trait BitAlloc { - // The bitmap has a total of CAP bits, numbered from 0 to CAP-1 inclusively. + /// The bitmap has a total of CAP bits, numbered from 0 to CAP-1 inclusively. const CAP: usize; - // The default value. Workaround for `const fn new() -> Self`. + /// The default value. Workaround for `const fn new() -> Self`. #[allow(clippy::declare_interior_mutable_const)] const DEFAULT: Self; - // Set a bit. + /// Set a bit. fn set(&mut self, idx: usize); - // Clear a bit + /// Clear a bit fn clear(&mut self, idx: usize); - // Get a bit + /// Get a bit fn get(&self, idx: usize) -> usize; // Whether there are free bits remaining // fn any(&self) -> bool; } -// A bitmap of 4K bits +/// A bitmap of 256 bits pub type BitAlloc256 = BitMap; -// A bitmap of 4K bits +/// A bitmap of 4K bits pub type BitAlloc4K = BitMap; -// A bitmap of 64K bits +/// A bitmap of 64K bits pub type BitAlloc64K = BitMap; -// A bitmap of 1M bits +/// A bitmap of 1M bits pub type BitAlloc1M = BitMap; -// A bitmap of 16M bits +/// A bitmap of 16M bits pub type BitAlloc16M = BitMap; -// A bitmap of 256M bits +/// A bitmap of 256M bits pub type BitAlloc256M = BitMap; #[repr(C)] @@ -113,11 +114,11 @@ impl BitAlloc for BitAlloc16 { const DEFAULT: Self = BitAlloc16(0); fn set(&mut self, idx: usize) { - self.0 = self.0 | (1 << idx); + self.0 |= 1 << idx; } fn clear(&mut self, idx: usize) { - self.0 = self.0 & (!(1 << idx) & 0xffff); + self.0 &= !(1 << idx); } fn get(&self, idx: usize) -> usize { @@ -129,7 +130,7 @@ impl BitAlloc for BitAlloc16 { } } -// flex bit map +/// flex bit map #[derive(Clone)] pub struct FlexBitmap { pub len: usize, @@ -142,12 +143,14 @@ impl FlexBitmap { FlexBitmap { len, map } } + /// init the bitmap with all dirty pub fn init_dirty(&mut self) { for i in 0..self.map.len() { self.map[i] = usize::MAX; } } + /// init the bitmap with all clean pub fn clear(&mut self) { for i in 0..self.map.len() { self.map[i] = 0; @@ -173,6 +176,7 @@ impl FlexBitmap { } } + /// set bits from bit to bit+len to val pub fn set_bits(&mut self, bit: usize, len: usize, val: bool) { if bit + len > self.len { panic!("set_bits: too large idx {} for set bitmap", bit); @@ -201,6 +205,7 @@ impl FlexBitmap { self.map.len() } + /// get the sum of all 1 bits pub fn sum(&self) -> usize { let mut sum = 0; for val in &self.map { @@ -209,6 +214,7 @@ impl FlexBitmap { sum } + /// get the first bit that is 0 pub fn first(&self) -> usize { let mut first = 0; for val in &self.map { @@ -217,13 +223,13 @@ impl FlexBitmap { } else { let mut tmp = *val; while (tmp & 1) == 0 { - tmp = tmp >> 1; + tmp >>= 1; first += 1; } return first; } } - println!("all is 0"); + debug!("all is 0"); first } } diff --git a/src/utils/device_ref.rs b/src/utils/device_ref.rs new file mode 100644 index 0000000000000000000000000000000000000000..6e6ea3381ce5a4b35ee29e51caa6a584fcb50d4c --- /dev/null +++ b/src/utils/device_ref.rs @@ -0,0 +1,52 @@ +use core::ops::Deref; +use core::ptr::NonNull; +use core::marker::PhantomData; + +#[derive(Debug)] +pub struct DeviceRef<'a, T> { + ptr: NonNull, + _maker: PhantomData<&'a T>, +} + +impl DeviceRef<'_, T> { + /// Create a new `DeviceRef` from a raw pointer + /// + /// # Safety + /// + /// - `ptr` must be aligned, non-null, and dereferencable as `T`. + /// - `*ptr` must be valid for the program duration. + pub const unsafe fn new<'a>(ptr: *const T) -> DeviceRef<'a, T> { + // SAFETY: `ptr` is non-null as promised by the caller. + DeviceRef { + ptr: NonNull::new_unchecked(ptr.cast_mut()), + _maker: PhantomData, + } + } + + #[inline(always)] + pub fn addr(&self) -> usize { + self.ptr.as_ptr() as usize + } +} + +impl Clone for DeviceRef<'_, T> { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for DeviceRef<'_, T> {} + +// SAFETY: T provides the necessary guarantees for Sync and DeviceRef provides the identical semantics as &T. +unsafe impl Send for DeviceRef<'_, T> {} +// SAFETY: T provides the necessary guarantees for Sync. +unsafe impl Sync for DeviceRef<'_, T> {} + +impl Deref for DeviceRef<'_, T> { + type Target = T; + fn deref(&self) -> &T { + // SAFETY: `ptr` is aligned and dereferencable for the program + // duration as promised by the caller of `DeviceRef::new`. + unsafe { self.ptr.as_ref() } + } +} diff --git a/src/utils/interval.rs b/src/utils/interval.rs new file mode 100644 index 0000000000000000000000000000000000000000..b303012015a3d4a988d7b8ce5d0141aa60ae7f7b --- /dev/null +++ b/src/utils/interval.rs @@ -0,0 +1,98 @@ +use alloc::vec::{Vec, IntoIter}; +use core::mem::MaybeUninit; + +#[derive(Debug, Clone, Default)] +/// A struct to store all the discontinuous intervals. +pub struct IntervalExcluder { + inner: Vec<(usize, isize)>, +} + +#[derive(Debug, Clone)] +pub struct IntervalIter { + inner: IntoIter<(usize, isize)>, + sum: isize, +} + +const INF: isize = u16::MAX as isize; + +impl IntervalExcluder { + pub fn new() -> Self { + Self::default() + } + + /// Add a range of memory(give left and right) to the interval excluder. + pub fn add_range(&mut self, left: usize, right: usize) -> &mut Self { + self.inner.push((left, 1)); + self.inner.push((right, -1)); + if self.inner.len() > INF as usize { + panic!("too many intervals"); + } + self + } + + /// Add a range of memory(give start and len) to the interval excluder. + pub fn add_len(&mut self, start: usize, length: usize) -> &mut Self { + self.add_range(start, start + length) + } + + /// Exclude a range of memory(give left and right) from the interval excluder. + pub fn exclude_range(&mut self, left: usize, right: usize) -> &mut Self { + self.inner.push((left, -INF)); + self.inner.push((right, INF)); + self + } + + /// Exclude a range of memory(give start and len) from the interval excluder. + pub fn exclude_len(&mut self, start: usize, length: usize) -> &mut Self { + self.exclude_range(start, start + length) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Interval { + pub left: usize, + pub right: usize, +} + +impl Interval { + pub fn len(&self) -> usize { + self.right - self.left + } +} + +impl IntoIterator for IntervalExcluder { + type Item = Interval; + type IntoIter = IntervalIter; + + fn into_iter(self) -> Self::IntoIter { + let mut inner = self.inner; + inner.sort_unstable(); + let inner = inner + .group_by(|a, b| a.0 == b.0) + .map(|a| (a[0].0, a.iter().map(|t| t.1).sum())) + .collect::>() + .into_iter(); + IntervalIter { inner, sum: 0 } + } +} + +/// An iterator over the intervals in an `IntervalExcluder`. +impl Iterator for IntervalIter { + type Item = Interval; + + fn next(&mut self) -> Option { + let mut last = MaybeUninit::::uninit(); + for (pos, delta) in self.inner.by_ref() { + let old = self.sum; + self.sum += delta; + if old <= 0 && self.sum > 0 { + last.write(pos); + } else if old > 0 && self.sum <= 0 { + // SAFETY: `old > 0` implies `last` was initialized in the branch above + let left = unsafe { last.assume_init() }; + return Some(Interval { left, right: pos }); + } + } + None + } +} diff --git a/src/lib/mod.rs b/src/utils/mod.rs similarity index 85% rename from src/lib/mod.rs rename to src/utils/mod.rs index 38a59c91107ccaa3ab1155f88924f56a344d49c9..469c0c9bb0516bf51926f8e5166757086986b1e2 100644 --- a/src/lib/mod.rs +++ b/src/utils/mod.rs @@ -8,9 +8,10 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +//! Utils module, including some common functions and data structures. + pub use self::barrier::*; pub use self::bitmap::*; -// pub use self::fatfs::*; pub use self::print::*; pub use self::string::*; pub use self::time::*; @@ -18,9 +19,11 @@ pub use self::util::*; mod barrier; mod bitmap; -// mod fatfs; +pub mod device_ref; +pub mod interval; mod print; mod string; mod time; +#[cfg(feature = "unilib")] pub mod unilib; mod util; diff --git a/src/lib/print.rs b/src/utils/print.rs similarity index 96% rename from src/lib/print.rs rename to src/utils/print.rs index 82dc2303cb2f48dc5a44d80241337be38c8cae9d..a81bf7f02e75934590262a0c28f8dd36966bd2f9 100644 --- a/src/lib/print.rs +++ b/src/utils/print.rs @@ -25,6 +25,7 @@ impl Write for Writer { } } +/// Print a format string to the console. pub fn _print(args: Arguments) { // use core::fmt::Write; let mut lock = WRITER.lock(); diff --git a/src/lib/string.rs b/src/utils/string.rs similarity index 46% rename from src/lib/string.rs rename to src/utils/string.rs index f89b848bbdc9cdebc8a0f8e4b140eedc19b2cbaf..ebab4ea39aec1aa1348b2ced6ac8abdc0b1cc7fa 100644 --- a/src/lib/string.rs +++ b/src/utils/string.rs @@ -8,25 +8,25 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use core::arch::global_asm; - -global_asm!(include_str!("../arch/aarch64/memset.S")); -global_asm!(include_str!("../arch/aarch64/memcpy.S")); -extern "C" { - pub fn memset(s: *mut u8, c: i32, n: usize) -> *mut u8; - pub fn memcpy(s1: *const u8, s2: *const u8, n: usize) -> *mut u8; -} - -pub fn memset_safe(s: *mut u8, c: i32, n: usize) -> *mut u8 { +/// Safe wrapper for memset +/// # Safety: +/// s must be a valid pointer to a writable memory region of at least n bytes and aligned to 1. +/// c must be a valid value of type u8. +pub unsafe fn memset(s: *mut u8, c: i32, n: usize) { if (s as usize) < 0x1000 { panic!("illegal addr for memset s {:x}", s as usize); } - unsafe { memset(s, c, n) } + core::ptr::write_bytes(s, c as u8, n); } -pub fn memcpy_safe(s1: *const u8, s2: *const u8, n: usize) -> *mut u8 { - if (s1 as usize) < 0x1000 || (s2 as usize) < 0x1000 { - panic!("illegal addr for memcpy s1 {:x} s2 {:x}", s1 as usize, s2 as usize); +/// Safe wrapper for memcpy +/// # Safety: +/// * `src` must be valid for reads of `count * size_of::()` bytes. +/// * `dst` must be valid for writes of `count * size_of::()` bytes. +/// * Both `src` and `dst` must be properly aligned. +pub unsafe fn memcpy(dst: *const u8, src: *const u8, n: usize) { + if (src as usize) < 0x1000 || (dst as usize) < 0x1000 { + panic!("illegal addr for memcpy s1 {:x} s2 {:x}", dst as usize, src as usize); } - unsafe { memcpy(s1, s2, n) } + core::ptr::copy_nonoverlapping(src, dst as *mut _, n); } diff --git a/src/lib/time.rs b/src/utils/time.rs similarity index 90% rename from src/lib/time.rs rename to src/utils/time.rs index a5d04de543fff8df62ed402d7d46d37130943bd3..b25acc66338d5044e8149d3f21b35ed992769ae5 100644 --- a/src/lib/time.rs +++ b/src/utils/time.rs @@ -10,18 +10,21 @@ use crate::arch::{timer_arch_get_counter, timer_arch_get_frequency}; +/// Get current time in microseconds. pub fn time_current_us() -> usize { let count = timer_arch_get_counter(); let freq = timer_arch_get_frequency(); count * 1000000 / freq } +/// Get current time in milliseconds. pub fn time_current_ms() -> usize { let count = timer_arch_get_counter(); let freq = timer_arch_get_frequency(); count * 1000 / freq } +/// Sleep for `us` microseconds. pub fn sleep(us: usize) { let end = time_current_us() + us; while time_current_us() < end { diff --git a/src/lib/unilib.rs b/src/utils/unilib.rs similarity index 90% rename from src/lib/unilib.rs rename to src/utils/unilib.rs index c8af431ad9be0bd686dcc6ffaddd4fc4108e8250..ce93aecc8889b21bacc45e9d32b28aedf8b8d32e 100644 --- a/src/lib/unilib.rs +++ b/src/utils/unilib.rs @@ -20,7 +20,7 @@ use alloc::collections::BTreeMap; use spin::Mutex; -use crate::lib::{memcpy_safe, sleep}; +use crate::utils::{memcpy_safe, sleep}; use crate::kernel::{vm_ipa2pa, active_vm, HVC_UNILIB_FS_INIT, HVC_UNILIB_FS_LSEEK, HVC_UNILIB_FS_UNLINK}; use crate::kernel::{HvcGuestMsg, HvcUniLibMsg, hvc_send_msg_to_vm}; use crate::kernel::HVC_UNILIB; @@ -145,7 +145,7 @@ pub fn unilib_fs_remove(vm_id: usize) { pub fn unilib_fs_init() -> Result { let vm = active_vm().unwrap(); let vm_id = vm.id(); - println!("unilib_fs_init: VM[{}] init unilib-fs", vm.id()); + info!("unilib_fs_init: VM[{}] init unilib-fs", vm.id()); let unilib_msg = HvcUniLibMsg { fid: HVC_UNILIB, event: HVC_UNILIB_FS_INIT, @@ -155,7 +155,7 @@ pub fn unilib_fs_init() -> Result { arg_3: 0, }; if !hvc_send_msg_to_vm(0, &HvcGuestMsg::UniLib(unilib_msg)) { - println!("unilib fs init: failed to notify VM 0"); + error!("unilib fs init: failed to notify VM 0"); return Err(()); } // Enter a loop, wait for VM0 to setup the unilib fs config struct. @@ -163,7 +163,7 @@ pub fn unilib_fs_init() -> Result { let lock = UNILIB_FS_LIST.lock(); match lock.get(&vm_id) { Some(_) => { - println!("unilib_fs_init, fs append success, return"); + trace!("unilib_fs_init, fs append success, return"); drop(lock); return Ok(0); } @@ -187,7 +187,7 @@ pub fn unilib_fs_append(mmio_ipa: usize) -> Result { let mmio_pa = vm_ipa2pa(vm.clone(), mmio_ipa); let unilib_fs = UnilibFS { base_addr: mmio_pa }; let buf_pa = vm_ipa2pa(vm, unilib_fs.buf_ipa()); - println!( + info!( "unilib_fs_append: VM[{}] fs_mmio_ipa 0x{:x}, buf ipa 0x{:x}, buf_pa 0x{:x}", unilib_fs.vm_id(), mmio_ipa, @@ -205,7 +205,7 @@ pub fn unilib_fs_append(mmio_ipa: usize) -> Result { /// ## Arguments /// * `vm_id` - The target GVM's VM id of this unilib fs operation. pub fn unilib_fs_finished(vm_id: usize) -> Result { - println!( + info!( "unilib_fs_finished: VM[{}] fs io request is finished, currently unused", vm_id ); @@ -233,14 +233,17 @@ pub fn unilib_fs_open(path_start_ipa: usize, path_length: usize, flags: usize) - let fs_cfg = match fs_list_lock.get(&vm_id) { Some(cfg) => cfg, None => { - println!("VM[{}] doesn't register a unilib fs, return", vm_id); + error!("VM[{}] doesn't register a unilib fs, return", vm_id); return Err(()); } }; // Copy path to unilib_fs buf, see UnilibFSCfg. let path_pa = vm_ipa2pa(active_vm().unwrap(), path_start_ipa); - memcpy_safe(fs_cfg.get_buf(), path_pa as *mut u8, path_length); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + memcpy(fs_cfg.get_buf(), path_pa as *mut u8, path_length); // Add end '\0' for path buf. unsafe { *((fs_cfg.get_buf() as usize + path_length) as *mut u8) = 0u8; @@ -258,7 +261,7 @@ pub fn unilib_fs_open(path_start_ipa: usize, path_length: usize, flags: usize) - arg_3: 0, }; if !hvc_send_msg_to_vm(0, &HvcGuestMsg::UniLib(unilib_msg)) { - println!("unilib fs open: failed to notify VM 0"); + error!("unilib fs open: failed to notify VM 0"); return Err(()); } @@ -283,7 +286,7 @@ pub fn unilib_fs_close(fd: usize) -> Result { let fs_cfg = match fs_list_lock.get(&vm_id) { Some(cfg) => cfg, None => { - println!("VM[{}] doesn't register a unilib fs, return", vm_id); + error!("VM[{}] doesn't register a unilib fs, return", vm_id); return Err(()); } }; @@ -300,7 +303,7 @@ pub fn unilib_fs_close(fd: usize) -> Result { arg_3: 0, }; if !hvc_send_msg_to_vm(0, &HvcGuestMsg::UniLib(unilib_msg)) { - println!("unilib fs close: failed to notify VM 0"); + error!("unilib fs close: failed to notify VM 0"); return Err(()); } // Still, we need to enter a loop, wait for VM to complete operation. @@ -329,7 +332,7 @@ pub fn unilib_fs_read(fd: usize, buf_ipa: usize, len: usize) -> Result cfg, None => { - println!("VM[{}] doesn't register a unilib fs, return", vm_id); + error!("VM[{}] doesn't register a unilib fs, return", vm_id); return Err(()); } }; @@ -344,7 +347,7 @@ pub fn unilib_fs_read(fd: usize, buf_ipa: usize, len: usize) -> Result Result Result cfg, None => { - println!("VM[{}] doesn't register a unilib fs, return", vm_id); + error!("VM[{}] doesn't register a unilib fs, return", vm_id); return Err(()); } }; let buf_pa = vm_ipa2pa(vm.clone(), buf_ipa); - memcpy_safe(fs_cfg.get_buf(), buf_pa as *mut u8, len); + // Safety: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + memcpy(fs_cfg.get_buf(), buf_pa as *mut u8, len); fs_cfg.prepare_for_request(); @@ -401,7 +410,7 @@ pub fn unilib_fs_write(fd: usize, buf_ipa: usize, len: usize) -> Result Result cfg, None => { - println!("VM[{}] doesn't register a unilib fs, return", vm_id); + error!("VM[{}] doesn't register a unilib fs, return", vm_id); return Err(()); } }; @@ -452,7 +461,7 @@ pub fn unilib_fs_lseek(fd: usize, offset: usize, whence: usize) -> Result Result cfg, None => { - println!("VM[{}] doesn't register a unilib fs, return", vm_id); + error!("VM[{}] doesn't register a unilib fs, return", vm_id); return Err(()); } }; // Copy path to unilib_fs buf, see UnilibFSCfg. let path_pa = vm_ipa2pa(active_vm().unwrap(), path_start_ipa); - memcpy_safe(fs_cfg.get_buf(), path_pa as *mut u8, path_length); + // Safety: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + memcpy(fs_cfg.get_buf(), path_pa as *mut u8, path_length); // Add end '\0' for path buf. unsafe { *((fs_cfg.get_buf() as usize + path_length) as *mut u8) = 0u8; @@ -506,7 +518,7 @@ pub fn unilib_fs_unlink(path_start_ipa: usize, path_length: usize) -> Result bool { TRACE.load(Ordering::Relaxed) } @@ -66,7 +67,7 @@ pub fn bit_set(bits: usize, off: usize) -> usize { // change find nth pub fn bitmap_find_nth(bitmap: usize, start: usize, size: usize, nth: usize, set: bool) -> Option { if size + start > 64 { - println!("bitmap_find_nth: bitmap size is too large"); + error!("bitmap_find_nth: bitmap size is too large"); return None; } let mut count = 0; @@ -84,40 +85,3 @@ pub fn bitmap_find_nth(bitmap: usize, start: usize, size: usize, nth: usize, set None } - -pub fn ptr_read_write(addr: usize, width: usize, val: usize, read: bool) -> usize { - if read { - if width == 1 { - unsafe { ptr::read(addr as *const u8) as usize } - } else if width == 2 { - unsafe { ptr::read(addr as *const u16) as usize } - } else if width == 4 { - unsafe { ptr::read(addr as *const u32) as usize } - } else if width == 8 { - unsafe { ptr::read(addr as *const u64) as usize } - } else { - panic!("ptr_read_write: illegal read len {}", width); - } - } else { - if width == 1 { - unsafe { - ptr::write(addr as *mut u8, val as u8); - } - } else if width == 2 { - unsafe { - ptr::write(addr as *mut u16, val as u16); - } - } else if width == 4 { - unsafe { - ptr::write(addr as *mut u32, val as u32); - } - } else if width == 8 { - unsafe { - ptr::write(addr as *mut u64, val as u64); - } - } else { - panic!("ptr_read_write: illegal write len {}", width); - } - 0 - } -} diff --git a/src/vmm/init.rs b/src/vmm/init.rs index 3640eb57205a9a00b9555d4626fbf3313ad1af2e..816db07e21c0dbdde73f0f6625c4aabdf7b4bff8 100644 --- a/src/vmm/init.rs +++ b/src/vmm/init.rs @@ -13,11 +13,16 @@ use alloc::vec::Vec; use crate::arch::{ emu_intc_handler, emu_intc_init, emu_smmu_handler, partial_passthrough_intc_handler, partial_passthrough_intc_init, }; -use crate::arch::{PTE_S2_DEVICE, PTE_S2_NORMAL}; +#[cfg(feature = "gicv3")] +use crate::arch::{vgic_icc_sre_handler, vgic_icc_sgir_handler, emu_vgicr_init, emul_vgicr_handler}; +use crate::arch::{PTE_S2_DEVICE, PTE_S2_NORMAL, PTE_S2_NORMALNOCACHE}; use crate::arch::PAGE_SIZE; +#[cfg(not(feature = "gicv3"))] use crate::board::*; use crate::config::vm_cfg_entry; -use crate::device::{emu_register_dev, emu_virtio_mmio_handler, emu_virtio_mmio_init}; +use crate::device::{emu_register_dev, emu_virtio_mmio_handler, emu_virtio_mmio_init, meta}; +#[cfg(feature = "gicv3")] +use crate::device::{emu_register_reg, EmuRegType}; use crate::device::create_fdt; use crate::device::EmuDeviceType::*; use crate::kernel::{ @@ -29,12 +34,15 @@ use crate::kernel::{vm, Vm}; use crate::kernel::{active_vcpu_id, vcpu_run}; use crate::kernel::interrupt_vm_register; use crate::kernel::VM_NUM_MAX; -use crate::lib::trace; +use crate::utils::trace; +use crate::error::Result; + +use fdt::binding::*; #[cfg(feature = "ramdisk")] -pub static CPIO_RAMDISK: &'static [u8] = include_bytes!("../../image/net_rootfs.cpio"); +pub static CPIO_RAMDISK: &[u8] = include_bytes!("../../image/net_rootfs.cpio"); #[cfg(not(feature = "ramdisk"))] -pub static CPIO_RAMDISK: &'static [u8] = &[]; +pub static CPIO_RAMDISK: &[u8] = &[]; fn vmm_init_memory(vm: Vm) -> bool { let result = mem_page_alloc(); @@ -46,7 +54,7 @@ fn vmm_init_memory(vm: Vm) -> bool { vm.set_pt(pt_dir_frame); vm.set_mem_region_num(config.memory_region().len()); } else { - println!("vmm_init_memory: page alloc failed"); + error!("vmm_init_memory: page alloc failed"); return false; } @@ -55,15 +63,15 @@ fn vmm_init_memory(vm: Vm) -> bool { vm_mem_size += vm_region.length; if pa == 0 { - println!("vmm_init_memory: vm memory region is not large enough"); + error!("vmm_init_memory: vm memory region is not large enough"); return false; } - println!( + info!( "VM {} memory region: ipa=<0x{:x}>, pa=<0x{:x}>, size=<0x{:x}>", vm_id, vm_region.ipa_start, pa, vm_region.length ); - vm.pt_map_range(vm_region.ipa_start, vm_region.length, pa, PTE_S2_NORMAL, vm_id == 0); + vm.pt_map_range(vm_region.ipa_start, vm_region.length, pa, PTE_S2_NORMAL, false); vm.add_region(VmPa { pa_start: pa, @@ -86,7 +94,7 @@ pub fn vmm_load_image(vm: Vm, bin: &[u8]) { } let offset = load_ipa - region.ipa_start; - println!( + info!( "VM {} loads kernel: ipa=<0x{:x}>, pa=<0x{:x}>, size=<{}K>", vm.id(), load_ipa, @@ -96,6 +104,9 @@ pub fn vmm_load_image(vm: Vm, bin: &[u8]) { if trace() && vm.pa_start(idx) + offset < 0x1000 { panic!("illegal addr {:x}", vm.pa_start(idx) + offset); } + // SAFETY: + // The 'vm.pa_start(idx) + offset' is in range of our memory configuration. + // The 'size' is the length of Image binary. let dst = unsafe { core::slice::from_raw_parts_mut((vm.pa_start(idx) + offset) as *mut u8, size) }; dst.clone_from_slice(bin); return; @@ -103,12 +114,23 @@ pub fn vmm_load_image(vm: Vm, bin: &[u8]) { panic!("vmm_load_image: Image config conflicts with memory config"); } +fn overlay_fdt(vm: &Vm, dtb: &[u8], overlay: &mut [u8]) -> Result { + let fdt = Fdt::from_bytes(dtb)?; + debug!("VM[{}] dtb old size {}", vm.id(), fdt.len()); + let mut buf = FdtBuf::from_fdt_capacity(fdt, (dtb.len() + overlay.len()) * 2)?; + let fdt_overlay = Fdt::from_bytes_mut(overlay)?; + buf.overlay_apply(fdt_overlay)?; + buf.pack()?; + debug!("VM[{}] dtb new size {}", vm.id(), buf.len()); + Ok(buf) +} + pub fn vmm_init_image(vm: Vm) -> bool { let vm_id = vm.id(); let config = vm.config(); if config.kernel_load_ipa() == 0 { - println!("vmm_init_image: kernel load ipa is null"); + error!("vmm_init_image: kernel load ipa is null"); return false; } @@ -121,30 +143,72 @@ pub fn vmm_init_image(vm: Vm) -> bool { Some(name) => { #[cfg(feature = "tx2")] if name == "L4T" { - println!("MVM {} loading Image", vm.id()); - vmm_load_image(vm.clone(), include_bytes!("../../image/L4T")); + info!("MVM {} loading Image", vm.id()); + // vmm_load_image(vm.clone(), include_bytes!("../../image/L4T")); + extern "C" { + fn _binary_vm0img_start(); + fn _binary_vm0img_size(); + } + // SAFETY: + // The '_binary_vm0img_start' and '_binary_vm0img_size' are valid from linker script. + let vm0image = unsafe { + core::slice::from_raw_parts( + _binary_vm0img_start as usize as *const u8, + _binary_vm0img_size as usize, + ) + }; + vmm_load_image(vm.clone(), vm0image); } else if name == "Image_vanilla" { - println!("VM {} loading default Linux Image", vm.id()); + info!("VM {} loading default Linux Image", vm.id()); #[cfg(feature = "static-config")] vmm_load_image(vm.clone(), include_bytes!("../../image/Image_vanilla")); #[cfg(not(feature = "static-config"))] - println!("*** Please enable feature `static-config`"); + info!("*** Please enable feature `static-config`"); } else { warn!("Image {} is not supported", name); } - #[cfg(feature = "pi4")] + #[cfg(any(feature = "qemu", feature = "pi4"))] if name.is_empty() { panic!("kernel image name empty") } else { - vmm_load_image(vm.clone(), include_bytes!("../../image/Image_pi4_5.4.83_tlb")); + extern "C" { + fn _binary_vm0img_start(); + fn _binary_vm0img_size(); + } + // SAFETY: + // The '_binary_vm0img_start' and '_binary_vm0img_size' are valid from linker script. + let vm0image = unsafe { + core::slice::from_raw_parts( + _binary_vm0img_start as usize as *const u8, + _binary_vm0img_size as usize, + ) + }; + vmm_load_image(vm.clone(), vm0image); } - // vmm_load_image(vm.clone(), include_bytes!("../../image/Image_pi4_5.4.78")); - // vmm_load_image(vm.clone(), include_bytes!("../../image/Image_pi4")); - #[cfg(feature = "qemu")] - if name.is_empty() { - panic!("kernel image name empty") - } else { + #[cfg(feature = "rk3588")] + if name == "Linux-5.10" { + info!("MVM {} loading Image", vm.id()); + extern "C" { + fn _binary_vm0img_start(); + fn _binary_vm0img_size(); + } + // SAFETY: + // The '_binary_vm0img_start' and '_binary_vm0img_size' are valid from linker script. + let vm0image = unsafe { + core::slice::from_raw_parts( + _binary_vm0img_start as usize as *const u8, + _binary_vm0img_size as usize, + ) + }; + vmm_load_image(vm.clone(), vm0image); + } else if name == "Image_vanilla" { + info!("VM {} loading default Linux Image", vm.id()); + #[cfg(feature = "static-config")] vmm_load_image(vm.clone(), include_bytes!("../../image/Image_vanilla")); + #[cfg(not(feature = "static-config"))] + info!("*** Please enable feature `static-config`"); + } else { + panic!("kernel image name empty") } } None => { @@ -159,22 +223,56 @@ pub fn vmm_init_image(vm: Vm) -> bool { // Init dtb for MVM. use crate::SYSTEM_FDT; let offset = config.device_tree_load_ipa() - config.memory_region()[0].ipa_start; - println!("MVM[{}] dtb addr 0x{:x}", vm_id, vm.pa_start(0) + offset); + debug!("MVM[{}] dtb addr 0x{:x}", vm_id, vm.pa_start(0) + offset); vm.set_dtb((vm.pa_start(0) + offset) as *mut fdt::myctypes::c_void); + // SAFETY: + // Offset is computed from config.device_tree_load_ipa() and config.memory_region()[0].ipa_start which are both valid. + // The 'vm.pa_start(0) + offset' is in range of our memory configuration. + // The 'vm.dtb' have been set to vm.pa_start(0) + offset which is in range of our memory configuration. unsafe { let src = SYSTEM_FDT.get().unwrap(); let len = src.len(); let dst = core::slice::from_raw_parts_mut((vm.pa_start(0) + offset) as *mut u8, len); - dst.clone_from_slice(&src); + dst.clone_from_slice(src); vmm_setup_fdt(vm.clone()); } } else { // Init dtb for GVM. match create_fdt(config.clone()) { Ok(dtb) => { + let mut overlay = config.fdt_overlay.lock(); let offset = config.device_tree_load_ipa() - vm.config().memory_region()[0].ipa_start; - println!("GVM[{}] dtb addr 0x{:x}", vm.id(), vm.pa_start(0) + offset); - crate::lib::memcpy_safe((vm.pa_start(0) + offset) as *const u8, dtb.as_ptr(), dtb.len()); + let target = (vm.pa_start(0) + offset) as *mut u8; + debug!( + "GVM[{}] dtb addr 0x{:x} overlay {}", + vm.id(), + target as usize, + overlay.len() + ); + if overlay.is_empty() { + // SAFETY: + // The 'target' is in range of our memory configuration. + // The 'src' is a temporary buffer and is valid. + unsafe { + core::ptr::copy_nonoverlapping(dtb.as_ptr(), target, dtb.len()); + } + } else { + let buf = match overlay_fdt(&vm, &dtb, &mut overlay) { + Ok(x) => x, + Err(e) => { + error!("overlay_fdt failed: {:?}", e); + return false; + } + }; + overlay.clear(); + overlay.shrink_to_fit(); + // SAFETY: + // The 'target' is in range of our memory configuration. + // The 'buf' is a vaild value from stack. + unsafe { + core::ptr::copy_nonoverlapping(buf.as_ptr(), target, buf.len()); + } + } } _ => { panic!("vmm_setup_config: create fdt for vm{} fail", vm.id()); @@ -182,7 +280,7 @@ pub fn vmm_init_image(vm: Vm) -> bool { } } } else { - println!( + warn!( "VM {} id {} device tree load ipa is not set", vm_id, vm.config().vm_name() @@ -193,9 +291,12 @@ pub fn vmm_init_image(vm: Vm) -> bool { // Todo: support loading ramdisk from MVM shyper-cli. // ... if config.ramdisk_load_ipa() != 0 { - println!("VM {} use ramdisk CPIO_RAMDISK", vm_id); + info!("VM {} use ramdisk CPIO_RAMDISK", vm_id); let offset = config.ramdisk_load_ipa() - config.memory_region()[0].ipa_start; let len = CPIO_RAMDISK.len(); + // SAFETY: + // The 'vm.pa_start(0) + offset' is in range of our memory configuration. + // The 'len' is the length of CPIO_RAMDISK binary. let dst = unsafe { core::slice::from_raw_parts_mut((vm.pa_start(0) + offset) as *mut u8, len) }; dst.clone_from_slice(CPIO_RAMDISK); } @@ -204,7 +305,7 @@ pub fn vmm_init_image(vm: Vm) -> bool { } fn vmm_init_emulated_device(vm: Vm) -> bool { - let config = vm.config().emulated_device_list(); + let config: Vec = vm.config().emulated_device_list(); for (idx, emu_dev) in config.iter().enumerate() { match emu_dev.emu_type { @@ -294,6 +395,40 @@ fn vmm_init_emulated_device(vm: Vm) -> bool { return false; } } + #[cfg(feature = "gicv3")] + EmuDeviceTICCSRE => { + emu_register_reg(EmuRegType::SysReg, emu_dev.base_ipa, vgic_icc_sre_handler); + } + #[cfg(feature = "gicv3")] + EmuDeviceTSGIR => { + emu_register_reg(EmuRegType::SysReg, emu_dev.base_ipa, vgic_icc_sgir_handler); + } + #[cfg(feature = "gicv3")] + EmuDeviceTGICR => { + emu_register_dev( + EmuDeviceTGICR, + vm.id(), + idx, + emu_dev.base_ipa, + emu_dev.length, + emul_vgicr_handler, + ); + emu_vgicr_init(vm.clone(), idx); + } + EmuDeviceTMeta => { + if meta::register(idx, &vm, emu_dev).is_ok() { + emu_register_dev( + EmuDeviceTMeta, + vm.id(), + idx, + emu_dev.base_ipa, + emu_dev.length, + meta::emu_meta_handler, + ); + } else { + return false; + } + } _ => { warn!("vmm_init_emulated_device: unknown emulated device"); return false; @@ -313,10 +448,12 @@ fn vmm_init_emulated_device(vm: Vm) -> bool { fn vmm_init_passthrough_device(vm: Vm) -> bool { for region in vm.config().passthrough_device_regions() { + // TODO: specify the region property more accurately. + // The 'dev_property' in a device region means cacheable here. if region.dev_property { vm.pt_map_range(region.ipa, region.length, region.pa, PTE_S2_DEVICE, true); } else { - vm.pt_map_range(region.ipa, region.length, region.pa, PTE_S2_NORMAL, true); + vm.pt_map_range(region.ipa, region.length, region.pa, PTE_S2_NORMALNOCACHE, true); } debug!( @@ -348,6 +485,9 @@ fn vmm_init_iommu_device(vm: Vm) -> bool { true } +/// # Safety: +/// This function is unsafe because it trusts the caller to pass a valid pointer to a valid dtb. +/// So the caller must ensure that the vm.dtb() have configured correctly before calling this function. pub unsafe fn vmm_setup_fdt(vm: Vm) { use fdt::*; let config = vm.config(); @@ -366,45 +506,51 @@ pub unsafe fn vmm_setup_fdt(vm: Vm) { fdt_set_memory(dtb, mr.len() as u64, mr.as_ptr(), "memory@200000\0".as_ptr()); #[cfg(feature = "qemu")] fdt_set_memory(dtb, mr.len() as u64, mr.as_ptr(), "memory@50000000\0".as_ptr()); + #[cfg(feature = "rk3588")] + fdt_set_memory(dtb, mr.len() as u64, mr.as_ptr(), "memory@10000000\0".as_ptr()); // FDT+TIMER - fdt_add_timer(dtb, 0x8); + //fdt_add_timer(dtb, 0x04); // FDT+BOOTCMD fdt_set_bootcmd(dtb, config.cmdline.as_ptr()); #[cfg(feature = "tx2")] fdt_set_stdout_path(dtb, "/serial@3100000\0".as_ptr()); // #[cfg(feature = "pi4")] // fdt_set_stdout_path(dtb, "/serial@fe340000\0".as_ptr()); + #[cfg(feature = "rk3588")] + fdt_set_stdout_path(dtb, "/serial@feba0000\0".as_ptr()); - if config.emulated_device_list().len() > 0 { + if !config.emulated_device_list().is_empty() { for emu_cfg in config.emulated_device_list() { match emu_cfg.emu_type { - EmuDeviceTGicd => { + EmuDeviceTGicd | EmuDeviceTGPPT => { + print!("trace fdt_setup_gic\n"); + #[cfg(not(feature = "gicv3"))] #[cfg(any(feature = "tx2", feature = "qemu"))] fdt_setup_gic( dtb, Platform::GICD_BASE as u64, Platform::GICC_BASE as u64, - emu_cfg.name.unwrap().as_ptr(), + emu_cfg.name.as_ptr(), ); #[cfg(feature = "pi4")] let _r = fdt_setup_gic( dtb, (Platform::GICD_BASE | 0xF_0000_0000) as u64, (Platform::GICC_BASE | 0xF_0000_0000) as u64, - emu_cfg.name.unwrap().as_ptr(), + emu_cfg.name.as_ptr(), ); } EmuDeviceTVirtioNet | EmuDeviceTVirtioConsole => { - #[cfg(any(feature = "tx2", feature = "qemu"))] + #[cfg(any(feature = "tx2", feature = "qemu", feature = "rk3588"))] fdt_add_virtio( dtb, - emu_cfg.name.unwrap().as_ptr(), + emu_cfg.name.as_ptr(), emu_cfg.irq_id as u32 - 0x20, emu_cfg.base_ipa as u64, ); } EmuDeviceTShyper => { - #[cfg(any(feature = "tx2", feature = "qemu"))] + #[cfg(any(feature = "tx2", feature = "qemu", feature = "rk3588"))] fdt_add_vm_service( dtb, emu_cfg.irq_id as u32 - 0x20, @@ -412,20 +558,14 @@ pub unsafe fn vmm_setup_fdt(vm: Vm) { emu_cfg.length as u64, ); } - EmuDeviceTIOMMU => { - #[cfg(feature = "tx2")] - trace!("EmuDeviceTIOMMU"); - } - _ => { - todo!(); - } + _ => {} } } } - println!("after dtb size {}", fdt_size(dtb)); + debug!("after dtb size {}", fdt_size(dtb)); } None => { - println!("None dtb"); + warn!("None dtb"); } } } @@ -451,10 +591,10 @@ pub fn vmm_setup_config(vm_id: usize) { } }; - println!( + debug!( "vmm_setup_config VM[{}] name {:?} current core {}", vm_id, - config.name.unwrap(), + config.name, current_cpu().id ); @@ -480,7 +620,7 @@ pub fn vmm_setup_config(vm_id: usize) { } add_async_used_info(vm_id); - info!("VM {} id {} init ok", vm.id(), vm.config().name.unwrap()); + info!("VM {} id {} init ok", vm.id(), vm.config().name); } pub fn vmm_cpu_assign_vcpu(vm_id: usize) { @@ -514,13 +654,14 @@ pub fn vmm_cpu_assign_vcpu(vm_id: usize) { Some(vcpu) => vcpu, }; if vcpu.id() == 0 { - println!("* Core {} is assigned => vm {}, vcpu {}", cpu_id, vm_id, vcpu.id()); + info!("* Core {} is assigned => vm {}, vcpu {}", cpu_id, vm_id, vcpu.id()); } else { - println!("Core {} is assigned => vm {}, vcpu {}", cpu_id, vm_id, vcpu.id()); + info!("Core {} is assigned => vm {}, vcpu {}", cpu_id, vm_id, vcpu.id()); } current_cpu().vcpu_array.append_vcpu(vcpu); } + #[cfg(not(feature = "secondary_start"))] if cfg_cpu_num == vm.cpu_num() { vm.set_ready(true); } @@ -534,17 +675,17 @@ pub fn vm_init() { super::vmm_init_gvm(0); #[cfg(feature = "static-config")] { + #[cfg(not(feature = "gicv3"))] crate::config::init_tmp_config_for_vm1(); - crate::config::init_tmp_config_for_vm2(); + #[cfg(feature = "gicv3")] + crate::config::init_gicv3_config_for_vm1(); super::vmm_init_gvm(1); - super::vmm_init_gvm(2); } } } pub fn vmm_boot() { if current_cpu().assigned() && active_vcpu_id() == 0 { - // active_vm().unwrap().set_migration_state(false); info!("Core {} start running", current_cpu().id); vcpu_run(false); } else { @@ -553,12 +694,3 @@ pub fn vmm_boot() { cpu_idle(); } } - -pub fn vmm_migrate_boot() { - let vcpu = current_cpu().active_vcpu.clone().unwrap(); - vcpu.reset_vmpidr(); - vcpu.reset_vtimer_offset(); - - // println!("Core[{}] start running", current_cpu().id); - vcpu_run(true); -} diff --git a/src/vmm/manager.rs b/src/vmm/manager.rs index 1c50bb320a22d7e51881c8425b33938a7fb73049..d4c63e7578f4c27ba9980b7ea25cfcb1030a373b 100644 --- a/src/vmm/manager.rs +++ b/src/vmm/manager.rs @@ -10,8 +10,8 @@ use alloc::vec::Vec; -use crate::arch::gicc_clear_current_irq; -use crate::arch::power_arch_vm_shutdown_secondary_cores; +use crate::arch::traits::InterruptController; +use crate::arch::{power_arch_vm_shutdown_secondary_cores, IntCtrl}; use crate::board::PLATFORM_CPU_NUM_MAX; use crate::config::{vm_id_list, vm_num}; // use crate::config::{init_tmp_config_for_bma1, init_tmp_config_for_bma2, init_tmp_config_for_vm1, init_tmp_config_for_vm2}; @@ -29,8 +29,7 @@ use crate::kernel::HVC_CONFIG; use crate::kernel::HVC_CONFIG_UPLOAD_KERNEL_IMAGE; use crate::kernel::HVC_VMM; use crate::kernel::HVC_VMM_REBOOT_VM; -use crate::lib::sleep; -use crate::lib::{bit_extract, memcpy_safe, memset_safe}; +use crate::utils::{bit_extract, memcpy, memset}; use crate::vmm::{vmm_cpu_assign_vcpu, vmm_boot, vmm_init_image, vmm_setup_config, vmm_cpu_remove_vcpu}; #[derive(Copy, Clone)] @@ -39,11 +38,12 @@ pub enum VmmEvent { VmmReboot, VmmShutdown, VmmAssignCpu, + VmmAssignVcpu, VmmRemoveCpu, } pub fn vmm_shutdown_secondary_vm() { - println!("Shutting down all VMs..."); + info!("Shutting down all VMs..."); } /* Generate VM structure and push it to VM. @@ -59,7 +59,7 @@ pub fn vmm_push_vm(vm_id: usize) { let vm_cfg = match vm_cfg_entry(vm_id) { Some(vm_cfg) => vm_cfg, None => { - println!("vmm_push_vm: failed to find config for vm {}", vm_id); + error!("vmm_push_vm: failed to find config for vm {}", vm_id); return; } }; @@ -82,17 +82,11 @@ pub fn vmm_alloc_vcpu(vm_id: usize) { }; for i in 0..vm.config().cpu_num() { - use crate::kernel::vcpu_alloc; - if let Some(vcpu) = vcpu_alloc() { - vcpu.init(vm.clone(), i); - vm.push_vcpu(vcpu.clone()); - } else { - println!("failed to allocte vcpu"); - return; - } + let vcpu = crate::kernel::Vcpu::new(vm.clone(), i); + vm.push_vcpu(vcpu); } - println!( + info!( "VM {} init cpu: cores=<{}>, allocat_bits=<0b{:b}>", vm.id(), vm.config().cpu_num(), @@ -106,7 +100,7 @@ pub fn vmm_alloc_vcpu(vm_id: usize) { * @param[in] vm_id: new added VM id. */ pub fn vmm_set_up_cpu(vm_id: usize) { - println!("vmm_set_up_cpu: set up vm {} on cpu {}", vm_id, current_cpu().id); + info!("vmm_set_up_cpu: set up vm {} on cpu {}", vm_id, current_cpu().id); let vm = match vm(vm_id) { None => { panic!( @@ -125,16 +119,38 @@ pub fn vmm_set_up_cpu(vm_id: usize) { let mut cpu_num = 0; while cpu_allocate_bitmap != 0 && target_cpu_id < PLATFORM_CPU_NUM_MAX { if cpu_allocate_bitmap & 1 != 0 { - println!("vmm_set_up_cpu: vm {} physical cpu id {}", vm_id, target_cpu_id); + info!("vmm_set_up_cpu: vm {} physical cpu id {}", vm_id, target_cpu_id); cpu_num += 1; - if target_cpu_id != current_cpu().id { + if cfg!(feature = "secondary_start") { + // Judge if current cpu is allocated. + let vcpu = match vm.select_vcpu2assign(target_cpu_id) { + None => panic!("core {} vm {} cannot find proper vcpu to assign", target_cpu_id, vm_id), + Some(vcpu) => vcpu, + }; + if vcpu.id() == 0 { + info!( + "* Core {} is assigned => vm {}, vcpu {}", + target_cpu_id, + vm_id, + vcpu.id() + ); + } else { + info!("Core {} is assigned => vm {}, vcpu {}", target_cpu_id, vm_id, vcpu.id()); + } + vcpu.set_phys_id(target_cpu_id); + + let cfg_master = vm.config().cpu_master(); + if target_cpu_id == cfg_master && target_cpu_id == current_cpu().id { + current_cpu().vcpu_array.append_vcpu(vcpu); + } + } else if target_cpu_id != current_cpu().id { let m = IpiVmmMsg { vmid: vm_id, event: VmmEvent::VmmAssignCpu, }; if !ipi_send_msg(target_cpu_id, IpiType::IpiTVMM, IpiInnerMsg::VmmMsg(m)) { - println!("vmm_set_up_cpu: failed to send ipi to Core {}", target_cpu_id); + error!("vmm_set_up_cpu: failed to send ipi to Core {}", target_cpu_id); } } else { vmm_cpu_assign_vcpu(vm_id); @@ -143,7 +159,7 @@ pub fn vmm_set_up_cpu(vm_id: usize) { cpu_allocate_bitmap >>= 1; target_cpu_id += 1; } - println!( + info!( "vmm_set_up_cpu: vm {} total physical cpu num {} bitmap {:#b}", vm_id, cpu_num, @@ -151,15 +167,17 @@ pub fn vmm_set_up_cpu(vm_id: usize) { ); // Waiting till others set up. - println!( + info!( "vmm_set_up_cpu: on core {}, waiting VM [{}] to be set up", current_cpu().id, vm_id ); + #[cfg(not(feature = "secondary_start"))] while !vm.ready() { + use crate::utils::sleep; sleep(10); } - println!("vmm_set_up_cpu: VM [{}] is ready", vm_id); + info!("vmm_set_up_cpu: VM [{}] is ready", vm_id); } /* Init VM before boot. @@ -191,36 +209,35 @@ pub fn vmm_init_gvm(vm_id: usize) { */ pub fn vmm_boot_vm(vm_id: usize) { let phys_id = vm_if_get_cpu_id(vm_id); - // println!( - // "vmm_boot_vm: current_cpu {} target vm {} get phys_id {}", - // current_cpu().id, - // vm_id, - // phys_id - // ); if phys_id != current_cpu().id { - let m = IpiVmmMsg { - vmid: vm_id, - event: VmmEvent::VmmBoot, - }; - if !ipi_send_msg(phys_id, IpiType::IpiTVMM, IpiInnerMsg::VmmMsg(m)) { - println!("vmm_boot_vm: failed to send ipi to Core {}", phys_id); + use crate::kernel::{CPU_IF_LIST, CpuState}; + use crate::arch::psci_vm_maincpu_on; + let state = CPU_IF_LIST.lock().get(phys_id).unwrap().state_for_start; + if state == CpuState::CpuInv { + let vmpidr = vm(vm_id).unwrap().pcpuid_to_vcpuid(phys_id).unwrap(); + info!("now start cpu on! vmpidr={vmpidr}"); + psci_vm_maincpu_on(vmpidr, vmm_boot_vm as usize, 0, vm_id); + } else { + let m = IpiVmmMsg { + vmid: vm_id, + event: VmmEvent::VmmBoot, + }; + if !ipi_send_msg(phys_id, IpiType::IpiTVMM, IpiInnerMsg::VmmMsg(m)) { + error!("vmm_boot_vm: failed to send ipi to Core {}", phys_id); + } } } else { - match current_cpu().vcpu_array.pop_vcpu_through_vmid(vm_id) { - None => { - panic!( - "vmm_boot_vm: VM[{}] does not have vcpu on Core {}", - vm_id, - current_cpu().id - ); - } - Some(vcpu) => { - gicc_clear_current_irq(true); - // TODO: try to use `wakeup` (still bugs when booting multi-shared-core VM using wakeup) - current_cpu().scheduler().yield_to(vcpu); - vmm_boot(); - } + if current_cpu().vcpu_array.pop_vcpu_through_vmid(vm_id).is_none() { + let vm = vm(vm_id).unwrap(); + let vcpuid = vm.pcpuid_to_vcpuid(phys_id).unwrap(); + let vcpu = vm.vcpuid_to_vcpu(vcpuid); + current_cpu().vcpu_array.append_vcpu(vcpu.unwrap()); }; + let vcpu = current_cpu().vcpu_array.pop_vcpu_through_vmid(vm_id).unwrap(); + IntCtrl::clear_current_irq(true); + // TODO: try to use `wakeup` (still bugs when booting multi-shared-core VM using wakeup) + current_cpu().scheduler().yield_to(vcpu); + vmm_boot(); } } @@ -235,7 +252,7 @@ pub fn vmm_reboot_vm(arg: usize) { let force = bit_extract(arg, 16, 16) != 0; let cur_vm = active_vm().unwrap(); - println!("vmm_reboot VM [{}] force:{}", vm_id, force); + info!("vmm_reboot VM [{}] force:{}", vm_id, force); if force { if cur_vm.id() == vm_id { @@ -247,7 +264,7 @@ pub fn vmm_reboot_vm(arg: usize) { event: VmmEvent::VmmReboot, }; if !ipi_send_msg(cpu_trgt, IpiType::IpiTVMM, IpiInnerMsg::VmmMsg(m)) { - println!("vmm_reboot_vm: failed to send ipi to Core {}", cpu_trgt); + error!("vmm_reboot_vm: failed to send ipi to Core {}", cpu_trgt); } } return; @@ -259,7 +276,7 @@ pub fn vmm_reboot_vm(arg: usize) { vm_id, }; if !hvc_send_msg_to_vm(vm_id, &HvcGuestMsg::Manage(msg)) { - println!("vmm_reboot_vm: failed to notify VM 0"); + error!("vmm_reboot_vm: failed to notify VM 0"); } } @@ -273,14 +290,18 @@ pub fn vmm_reboot() { if vm.id() == 0 { vmm_shutdown_secondary_vm(); use crate::board::{PlatOperation, Platform}; - Platform::sys_reboot(); + // SAFETY: + // Here we are ready to reboot the system. + unsafe { + Platform::sys_reboot(); + } } // Reset GVM. let vcpu = current_cpu().active_vcpu.clone().unwrap(); - println!("VM [{}] reset...", vm.id()); + info!("VM [{}] reset...", vm.id()); power_arch_vm_shutdown_secondary_cores(vm.clone()); - println!( + info!( "Core {} (VM [{}] vcpu {}) shutdown ok", current_cpu().id, vm.id(), @@ -289,7 +310,7 @@ pub fn vmm_reboot() { // Clear memory region. for idx in 0..vm.mem_region_num() { - println!( + info!( "Core {} (VM [{}] vcpu {}) reset mem region start {:x} size {:x}", current_cpu().id, vm.id(), @@ -297,7 +318,11 @@ pub fn vmm_reboot() { vm.pa_start(idx), vm.pa_length(idx) ); - memset_safe(vm.pa_start(idx) as *mut u8, 0, vm.pa_length(idx)); + // SAFETY: + // The 'vm_pa_region' is writable for the Hypervisor in EL2. + unsafe { + memset(vm.pa_start(idx) as *mut u8, 0, vm.pa_length(idx)); + } } // Reset image. @@ -309,7 +334,7 @@ pub fn vmm_reboot() { vm_if_set_ivc_arg(vm.id(), 0); vm_if_set_ivc_arg_ptr(vm.id(), 0); - crate::arch::interrupt_arch_clear(); + IntCtrl::clear(); crate::arch::vcpu_arch_init(vm.clone(), vm.vcpu(0).unwrap()); vcpu.reset_context(); @@ -327,7 +352,7 @@ pub fn vmm_load_image_from_mvm(vm: Vm) { }; // println!("mediated_blk_write send msg to vm0"); if !hvc_send_msg_to_vm(0, &HvcGuestMsg::Manage(msg)) { - println!("vmm_load_image_from_mvm: failed to notify VM 0"); + error!("vmm_load_image_from_mvm: failed to notify VM 0"); } } @@ -339,9 +364,10 @@ pub fn get_vm_id(id_ipa: usize) -> bool { let vm = active_vm().unwrap(); let id_pa = vm_ipa2pa(vm.clone(), id_ipa); if id_pa == 0 { - println!("illegal id_pa {:x}", id_pa); + error!("illegal id_pa {:x}", id_pa); return false; } + // SAFETY: The 'id_pa' is a valid address checked by shyper.ko unsafe { *(id_pa as *mut usize) = vm.id(); } @@ -367,14 +393,15 @@ struct VMInfoList { * @param[in] vm_info_ipa : vm info list ipa. */ pub fn vmm_list_vm(vm_info_ipa: usize) -> Result { - #[cfg(feature = "update")] - println!("Rust-Shyper list vm"); let vm_info_pa = vm_ipa2pa(active_vm().unwrap(), vm_info_ipa); if vm_info_pa == 0 { - println!("illegal vm_info_ipa {:x}", vm_info_ipa); + error!("illegal vm_info_ipa {:x}", vm_info_ipa); return Err(()); } + // SAFETY: + // The 'vm_info_pa' is a valid address checked by shyper.ko + // And we have checked the vm_info_pa by vm_ipa2pa to make sure it is in range of vm memory. let vm_info = unsafe { &mut *(vm_info_pa as *mut VMInfoList) }; // Get VM num. @@ -384,7 +411,7 @@ pub fn vmm_list_vm(vm_info_ipa: usize) -> Result { let vm_cfg = match vm_cfg_entry(*vmid) { Some(vm_cfg) => vm_cfg, None => { - println!("Failed to get VM config entry for VM[{}]", *vmid); + error!("Failed to get VM config entry for VM[{}]", *vmid); continue; } }; @@ -398,11 +425,16 @@ pub fn vmm_list_vm(vm_info_ipa: usize) -> Result { vm_info.info_list[idx].vm_state = vm_state as u32; let vm_name_u8: Vec = vm_cfg.vm_name().as_bytes().to_vec(); - memcpy_safe( - vm_info.info_list[idx].vm_name.as_ptr() as *const _ as *const u8, - vm_name_u8.as_ptr(), - NAME_MAX_LEN, - ); + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy( + vm_info.info_list[idx].vm_name.as_ptr(), + vm_name_u8.as_ptr(), + NAME_MAX_LEN, + ); + } vm_info.info_list[idx].vm_name[vm_name_u8.len()] = 0; } Ok(0) @@ -418,7 +450,7 @@ pub fn vmm_ipi_handler(msg: &IpiMessage) { vmm_reboot(); } VmmEvent::VmmAssignCpu => { - println!( + info!( "vmm_ipi_handler: core {} receive assign vcpu request for vm[{}]", current_cpu().id, vmm.vmid @@ -426,7 +458,7 @@ pub fn vmm_ipi_handler(msg: &IpiMessage) { vmm_cpu_assign_vcpu(vmm.vmid); } VmmEvent::VmmRemoveCpu => { - println!( + info!( "vmm_ipi_handler: core {} remove vcpu for vm[{}]", current_cpu().id, vmm.vmid @@ -438,8 +470,7 @@ pub fn vmm_ipi_handler(msg: &IpiMessage) { } }, _ => { - println!("vmm_ipi_handler: illegal ipi type"); - return; + error!("vmm_ipi_handler: illegal ipi type"); } } } diff --git a/src/vmm/mod.rs b/src/vmm/mod.rs index 07bc6cd912d42b4d60174682f94d62512b7e4545..eaef70759ab6de796cc45aa8cbeaab9069c0a40d 100644 --- a/src/vmm/mod.rs +++ b/src/vmm/mod.rs @@ -8,6 +8,8 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +//! Virtual machine monitor module, including VM manager and VM initialization. + pub use self::init::*; pub use self::manager::*; pub use self::remove::*; diff --git a/src/vmm/remove.rs b/src/vmm/remove.rs index 9337f1b6909d4899f5cbb65b334b20c02f13768e..dcbca6366d0282c11e995e41b6e5b34ef341a3bb 100644 --- a/src/vmm/remove.rs +++ b/src/vmm/remove.rs @@ -10,14 +10,14 @@ use crate::arch::{GIC_SGIS_NUM, gicc_clear_current_irq}; use crate::config::vm_cfg_remove_vm_entry; -use crate::device::emu_remove_dev; +use crate::device::{emu_remove_dev, EmuDeviceType, meta}; use crate::kernel::{ current_cpu, interrupt_vm_remove, ipi_send_msg, IpiInnerMsg, IpiType, IpiVmmMsg, mem_vm_region_free, - remove_async_used_info, remove_vm, remove_vm_async_task, vcpu_remove, vm, Vm, Scheduler, cpu_idle, + remove_async_used_info, remove_vm, remove_vm_async_task, vm, Vm, Scheduler, cpu_idle, }; use crate::kernel::vm_if_reset; use crate::vmm::VmmEvent; -use crate::lib::memset_safe; +use crate::utils::memset; pub fn vmm_remove_vm(vm_id: usize) { if vm_id == 0 { @@ -27,7 +27,7 @@ pub fn vmm_remove_vm(vm_id: usize) { let vm = match vm(vm_id) { None => { - println!("vmm_remove_vm: vm[{}] not exist", vm_id); + error!("vmm_remove_vm: vm[{}] not exist", vm_id); return; } Some(vm) => vm, @@ -39,7 +39,11 @@ pub fn vmm_remove_vm(vm_id: usize) { vm_if_reset(vm_id); // free mem for idx in 0..vm.region_num() { - memset_safe(vm.pa_start(idx) as *mut u8, 0, vm.pa_length(idx)); + // SAFETY: + // The 'vm_pa_region' is writable for the Hypervisor in EL2. + unsafe { + memset(vm.pa_start(idx) as *mut u8, 0, vm.pa_length(idx)); + } mem_vm_region_free(vm.pa_start(idx), vm.pa_length(idx)); } // emu dev @@ -55,7 +59,8 @@ pub fn vmm_remove_vm(vm_id: usize) { // remove vm cfg vm_cfg_remove_vm_entry(vm_id); // remove vm unilib - crate::lib::unilib::unilib_fs_remove(vm_id); + #[cfg(feature = "unilib")] + crate::utils::unilib::unilib_fs_remove(vm_id); info!("remove vm[{}] successfully", vm_id); } @@ -79,8 +84,6 @@ pub fn vmm_cpu_remove_vcpu(vmid: usize) { fn vmm_remove_vcpu(vm: Vm) { for idx in 0..vm.cpu_num() { let vcpu = vm.vcpu(idx).unwrap(); - // remove vcpu from VCPU_LIST - vcpu_remove(vcpu.clone()); if vcpu.phys_id() == current_cpu().id { vmm_cpu_remove_vcpu(vm.id()); } else { @@ -103,6 +106,9 @@ fn vmm_remove_emulated_device(vm: Vm) { warn!("vmm_remove_emulated_device: cannot remove device {}", emu_dev.emu_type); return; } + if emu_dev.emu_type == EmuDeviceType::EmuDeviceTMeta { + meta::unregister(idx); + } emu_remove_dev(vm.id(), idx, emu_dev.base_ipa, emu_dev.length); // println!( // "VM[{}] removes emulated device: id=<{}>, name=\"{}\", ipa=<0x{:x}>", diff --git a/vm1_config_console.json b/vm1_config_console.json new file mode 100644 index 0000000000000000000000000000000000000000..07b2150d0f7eabf03480d3a398f420ad4aee471b --- /dev/null +++ b/vm1_config_console.json @@ -0,0 +1,123 @@ +{ + "name": "guest-os-1", + "type": "VM_T_LINUX", + "cmdline": "earlycon=uart8250,mmio32,0xfeb50000 console=hvc0 root=/dev/vda rw audit=0", + "image": { + "kernel_filename": "Image_vanilla", + "kernel_load_ipa": "0x80080000", + "kernel_entry_point": "0x80080000", + "device_tree_filename": "-", + "device_tree_load_ipa": "0x80000000", + "ramdisk_filename": "initrd.gz", + "ramdisk_load_ipa": "0" + }, + "memory": { + "num": 1, + "region": [ + { + "ipa_start": "0x80000000", + "length": "0x40000000" + } + ] + }, + "cpu": { + "num": 1, + "allocate_bitmap": "0b0010", + "master" : 1 + }, + "emulated_device" : { + "num": 4, + "emulated_device_list": [ + { + "name": "intc@fe600000", + "base_ipa": "0xfe600000", + "length": "0x10000", + "irq_id": 0, + "type": "EMU_DEVICE_T_GICD" + }, + { + "name": "GICR@fe680000", + "base_ipa": "0xfe680000", + "length": "0x100000", + "irq_id": 0, + "type": "EMU_DEVICE_T_GICR" + }, + { + "name": "virtio_blk@f0020000", + "base_ipa": "0xf0020000", + "length": "0x1000", + "irq_id": 498, + "cfg_num": 2, + "cfg_list": [ + 0, + 8192000 + ], + "type": "EMU_DEVICE_T_VIRTIO_BLK_MEDIATED" + }, + { + "name": "virtio_net@f0000000", + "base_ipa": "0xf0000000", + "length": "0x1000", + "irq_id": 499, + "cfg_num": 6, + "cfg_list": [ + "0x74", + "0x56", + "0xaa", + "0x0f", + "0x47", + "0xd1" + ], + "type": "EMU_DEVICE_T_VIRTIO_NET" + }, + { + "name": "virtio_console@f0040000", + "base_ipa": "0xf0040000", + "length": "0x10000", + "irq_id": 500, + "cfg_num": 2, + "cfg_list": [ + "0", + "0xf0010000" + ], + "type": "EMU_DEVICE_T_VIRTIO_CONSOLE" + } + ] + }, + "passthrough_device": { + "passthrough_device_list": [ + { + "name": "serial1", + "base_pa": "0xfeb50000", + "base_ipa": "0xfeb50000", + "length": "0x1000", + "irq_num": 1, + "irq_list": [ + 27 + ] + } + ] + }, + + "dtb_device" : { + "num": 3, + "dtb_device_list": [ + { + "name": "gicd", + "type": "DTB_DEVICE_T_GICD", + "irq_num": 0, + "irq_list": [], + "addr_region_ipa": "0xfe600000", + "addr_region_length": "0x10000" + }, + { + "name": "gicr", + "type": "DTB_DEVICE_T_GICR", + "irq_num": 0, + "irq_list": [], + "addr_region_ipa": "0xfe680000", + "addr_region_length": "0x100000" + } + ] + } +}