From 381788e55218f39dcb46e644b907d29afcd3c66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E4=BB=81=E9=B9=8F?= <2110459069@qq.com> Date: Thu, 30 May 2024 11:35:19 +0800 Subject: [PATCH 1/2] feat: Add command-line interface(cli) for vm booting and management Specifically, the cli run in MVM, and provides the following functions: * Config and booting other VMs(GVM and RTVM) * Run as a daemon to provide mediated disk service * View and shutdown other VM --- .gitignore | 3 +- cli/Cargo.lock | 668 ++++++++++++++++++++++++++++++++++++++++++ cli/Cargo.toml | 19 ++ cli/Makefile | 2 + cli/readme.md | 3 + cli/src/blk.rs | 287 ++++++++++++++++++ cli/src/config.rs | 615 ++++++++++++++++++++++++++++++++++++++ cli/src/config_arg.rs | 98 +++++++ cli/src/daemon.rs | 271 +++++++++++++++++ cli/src/ioctl_arg.rs | 11 + cli/src/main.rs | 171 +++++++++++ cli/src/sys.rs | 76 +++++ cli/src/util.rs | 127 ++++++++ cli/src/vmm.rs | 149 ++++++++++ 14 files changed, 2499 insertions(+), 1 deletion(-) create mode 100644 cli/Cargo.lock create mode 100644 cli/Cargo.toml create mode 100644 cli/Makefile create mode 100644 cli/readme.md create mode 100644 cli/src/blk.rs create mode 100644 cli/src/config.rs create mode 100644 cli/src/config_arg.rs create mode 100644 cli/src/daemon.rs create mode 100644 cli/src/ioctl_arg.rs create mode 100644 cli/src/main.rs create mode 100644 cli/src/sys.rs create mode 100644 cli/src/util.rs create mode 100644 cli/src/vmm.rs diff --git a/.gitignore b/.gitignore index e22f20e..98a81ec 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ build/ /rk3588_devcie_tree /vm1* /image/Image* -*.patch \ No newline at end of file +*.patch +target diff --git a/cli/Cargo.lock b/cli/Cargo.lock new file mode 100644 index 0000000..b671913 --- /dev/null +++ b/cli/Cargo.lock @@ -0,0 +1,668 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bindgen" +version = "0.66.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" +dependencies = [ + "bitflags 2.5.0", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fdisk" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b10392e80d15ed7899d7ae420e2b67fa69a22b1bb31e4fb0f2d8882abb97ef6" +dependencies = [ + "anyhow", + "fdisk-sys", + "libc", + "nix 0.26.4", +] + +[[package]] +name = "fdisk-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab0030b4494a47e7c4eb3163e0742561ddd5bd60a6e7cc44af115bd1e2b5304" +dependencies = [ + "bindgen", + "libc", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", + "pin-utils", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.201" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.201" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "shyper-cli-rust" +version = "0.1.0" +dependencies = [ + "clap", + "env_logger", + "fdisk", + "libc", + "log", + "nix 0.29.0", + "once_cell", + "serde", + "serde_json", + "signal-hook", +] + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/cli/Cargo.toml b/cli/Cargo.toml new file mode 100644 index 0000000..f5a14ce --- /dev/null +++ b/cli/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "shyper-cli-rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "4.5.4", features = ["derive"] } +env_logger = "0.11.3" +fdisk = "0.2.0" +libc = "0.2.154" +log = "0.4.21" +nix = { version = "0.29.0", features = ["signal"] } +once_cell = "1.19.0" +serde = { version = "1.0.201", features = ["derive"] } +serde_json = "1.0.117" +signal-hook = "0.3.17" + diff --git a/cli/Makefile b/cli/Makefile new file mode 100644 index 0000000..d06f65c --- /dev/null +++ b/cli/Makefile @@ -0,0 +1,2 @@ +all: + cargo build --release \ No newline at end of file diff --git a/cli/readme.md b/cli/readme.md new file mode 100644 index 0000000..e2a8c7c --- /dev/null +++ b/cli/readme.md @@ -0,0 +1,3 @@ +# How to compile + +Put this in aarch64/riscv environment, then run `make`. diff --git a/cli/src/blk.rs b/cli/src/blk.rs new file mode 100644 index 0000000..9ee1f8a --- /dev/null +++ b/cli/src/blk.rs @@ -0,0 +1,287 @@ +use std::{fs, os::linux::fs::MetadataExt, process::{id, Command}, slice::from_raw_parts, sync::Mutex}; +use libc::{c_uint, c_void, ioctl, memset, mmap, open, preadv2, size_t, MAP_ANONYMOUS, MAP_HUGETLB, MAP_PRIVATE, O_DIRECT, O_RDWR, PROT_READ, PROT_WRITE, S_IFBLK, S_IFMT}; +use log::{info, warn}; +use once_cell::sync::OnceCell; + +use crate::{daemon::generate_hvc_mode, ioctl_arg::{IOCTL_SYS, IOCTL_SYS_APPEND_MED_BLK}, util::{check_cache_address, cstr_arr_to_string, string_to_cstr_arr, virt_to_phys_user}}; + +pub const HUGE_TLB_MAX: usize = 2 * 1024 * 1024; +pub const BLOCK_SIZE: usize = 512; + +pub static SHYPER_FD: OnceCell = OnceCell::new(); + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct MediatedBlkCfg { + name: [u8; 32], + block_dev_path: [u8; 32], + block_num: u64, + dma_block_max: u64, + cache_size: u64, + idx: u16, + pcache: bool, + cache_va: u64, + cache_ipa: u64, + cache_pa: u64, +} + +// Set this only once in the config_daemon +pub static MED_BLK_LIST: OnceCell> = OnceCell::new(); +pub static IMG_FILE_FDS: Mutex> = Mutex::new(Vec::new()); + +// Read the count blocks of the blk id starting from the lba sector +// Note: do not exceed cache_size! +#[inline(always)] +fn blk_read(blk_id: u16, lba: u64, mut count: u64) { + let binding = MED_BLK_LIST.get().unwrap(); + let blk_cfg = binding.get(blk_id as usize).unwrap(); + let binding2 = IMG_FILE_FDS.lock().unwrap(); + let img_file = binding2.get(blk_id as usize).unwrap(); + + if count > blk_cfg.dma_block_max { + warn!("blk_read count {} > dma_block_max {}, shrink count to {}", count, blk_cfg.dma_block_max, blk_cfg.dma_block_max); + count = blk_cfg.dma_block_max; + } + + let iov = libc::iovec { + iov_base: blk_cfg.cache_va as *mut c_void, + iov_len: count as usize * BLOCK_SIZE, + }; + unsafe { + let read_len = preadv2(*img_file, &iov, 1, lba as i64 * BLOCK_SIZE as i64, 0); + + if read_len < 0 { + warn!("read lba {:#x} size {:#x} failed!", lba, count * BLOCK_SIZE as u64); + } else if read_len != (count as isize * BLOCK_SIZE as isize) { + warn!("read lba {:#x} size {:#x} failed! read_len = {:#x}", lba, count * BLOCK_SIZE as u64, read_len); + } + } +} + +fn blk_write(blk_id: u16, lba: u64, mut count: u64) { + let binding = MED_BLK_LIST.get().unwrap(); + let blk_cfg = binding.get(blk_id as usize).unwrap(); + let binding2 = IMG_FILE_FDS.lock().unwrap(); + let img_file = binding2.get(blk_id as usize).unwrap(); + + if count > blk_cfg.dma_block_max { + warn!("blk_write count {} > dma_block_max {}, shrink count to {}", count, blk_cfg.dma_block_max, blk_cfg.dma_block_max); + count = blk_cfg.dma_block_max; + } + + unsafe { + let write_len = libc::pwrite(*img_file, blk_cfg.cache_va as *const c_void, count as usize * BLOCK_SIZE, lba as i64 * BLOCK_SIZE as i64); + + if write_len < 0 { + warn!("write lba {:#x} size {:#x} failed!", lba, count * BLOCK_SIZE as u64); + } else if write_len != (count as isize * BLOCK_SIZE as isize) { + warn!("write lba {:#x} size {:#x} failed! write_len = {:#x}", lba, count * BLOCK_SIZE as u64, write_len); + } + } +} + +// Read/write sector 0 of the disk to test whether the disk is ready +fn blk_try_rw(blk_id: u16) -> Result<(), String> { + let mut origin_data: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE]; + let binding = MED_BLK_LIST.get().unwrap(); + let blk = binding.get(blk_id as usize).unwrap(); + + // Read origin data, and save it in origin_data + blk_read(blk_id, 0, 1); + unsafe { + origin_data.clone_from(from_raw_parts(blk.cache_va as *const u8, BLOCK_SIZE).try_into().unwrap()); + } + + let cache = blk.cache_va as *mut u8; + for i in 0..BLOCK_SIZE { + unsafe { *cache.add(i) = (i % 256) as u8 }; + } + blk_write(blk_id, 0, 1); + + unsafe { memset(cache as *mut c_void, 0, BLOCK_SIZE) }; + blk_read(blk_id, 0, 1); + + // Check if the data written before is read correctly + for i in 0..BLOCK_SIZE { + if unsafe { *cache.add(i) } != (i % 256) as u8 { + return Err(format!("blk {} read write test failed!", blk_id)); + } + } + + unsafe { cache.copy_from(origin_data.as_ptr(), BLOCK_SIZE) }; + + // Written back + blk_write(blk_id, 0, 1); + Ok(()) +} + +pub fn mediated_blk_init() { + // Kernel boot options cmdline: + // default_hugepagesz=32M hugepagesz=32M hugepages=1 + // mount hugetlbfs + // mkdir /mnt/huge + // mount -t hugetlbfs -o pagesize=32M none /mnt/huge + let mut med_blk_list = MED_BLK_LIST.get().expect("med_blk_list is None"); + let mut img_file_fds = IMG_FILE_FDS.lock().unwrap(); + if med_blk_list.is_empty() { + warn!("NO mediated block device!"); + } + + for i in 0..med_blk_list.len() { + img_file_fds.push(-1); + } + + let output = Command::new("mkdir") + .arg("-p") + .arg("/mnt/huge") + .output() + .expect("failed to execute mkdir"); + + if !output.status.success() { + warn!("mkdir /mnt/huge failed"); + return; + } + + let output = Command::new("mount") + .arg("-t") + .arg("hugetlbfs") + .arg("-o") + .arg("pagesize=2M") + .arg("none") + .arg("/mnt/huge") + .output() + .expect("failed to execute mount"); + + if !output.status.success() { + warn!("mount hugetlbfs failed"); + return; + } + + unsafe { + let fd = libc::open("/dev/shyper\0".as_ptr() as *const u8, libc::O_RDWR); + SHYPER_FD.set(fd).unwrap(); + if fd < 0 { + warn!("open /dev/shyper failed"); + return; + } + } + + for i in 0..med_blk_list.len() { + let cache_size = med_blk_list[i].cache_size; + let block_dev_path = med_blk_list[i].block_dev_path.clone(); + info!("Shyper daemon init blk {} with cache size {}", + cstr_arr_to_string(med_blk_list[i].name.as_slice()), + cache_size + ); + + info!("Shyper daemon init blk {} va {:#x} with cache pa {:#x}", + cstr_arr_to_string(med_blk_list[i].name.as_slice()), + med_blk_list[i].cache_va as u64, + med_blk_list[i].cache_ipa as u64 + ); + + unsafe { + let fd = open(block_dev_path.as_ptr() as *const u8, O_RDWR | O_DIRECT); + if fd < 0 { + warn!("open block device {} failed: errcode = {}", cstr_arr_to_string(block_dev_path.as_slice()), fd); + return; + } + img_file_fds[i] = fd; + } + drop(img_file_fds); + + // block_try_rw + if let Err(err) = blk_try_rw(i as u16) { + warn!("blk_try_rw failed: {}", err); + return; + } + + let request = generate_hvc_mode(IOCTL_SYS, IOCTL_SYS_APPEND_MED_BLK); + unsafe { + if ioctl(*SHYPER_FD.get().unwrap(), request as u64, &med_blk_list[i] as *const MediatedBlkCfg as *mut c_void) != 0 { + warn!("ioctl append mediated blk failed"); + return; + } + } + + img_file_fds = IMG_FILE_FDS.lock().unwrap(); + info!("Shyper daemon init blk {} success", cstr_arr_to_string(med_blk_list[i].name.clone().as_slice())); + } +} + +// mediated_blk_read: Do reading, and after that send finishing signal to kernel module +pub fn mediated_blk_read(blk_id: u16, lba: u64, count: u64) { + blk_read(blk_id, lba, count); + + let ret = unsafe { + libc::ioctl(*SHYPER_FD.get().unwrap(), 0x0331, blk_id as c_uint) + }; + if ret != 0 { + warn!("Mediated blk read ioctl failed"); + } +} + +pub fn mediated_blk_write(blk_id: u16, lba: u64, count: u64) { + blk_write(blk_id, lba, count); + + let ret = unsafe { + libc::ioctl(*SHYPER_FD.get().unwrap(), 0x0331, blk_id as c_uint) + }; + if ret != 0 { + warn!("Mediated blk read ioctl failed"); + } +} + +// Add a mediated blk +pub fn mediated_blk_add(index: usize, dev: String) -> Result { + let metadata = fs::metadata(dev.clone()).map_err(|x| format!("metadata err: {}", x))?; + + let file_type = metadata.st_mode() & (S_IFMT as u32); + if file_type != S_IFBLK { + warn!("{} is not a block device, but we can also use {} as a img file", dev, dev); + } + + let ctx = fdisk::Context::new(); + ctx.assign_device(dev.clone(), true).map_err(|dev| format!("assign device {} err", dev))?; + + let nsec = ctx.logical_sectors(); + info!("Shyper daemon add blk {} with {} sectors", dev.clone(), nsec); + + let cache_va; + let cache_size = HUGE_TLB_MAX as u64; + unsafe { + cache_va = mmap(0 as *mut c_void, cache_size as size_t, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 0, 0); + if cache_va == libc::MAP_FAILED { + warn!("mmap cache failed"); + return Err("mmap cache failed".to_string()); + } + } + + if let Err(err) = check_cache_address(cache_va, cache_size) { + warn!("check cache address failed: {}", err); + return Err("check cache address failed".to_string()); + } + + let phys_result = virt_to_phys_user(id(), cache_va as u64); + if let Err(err) = phys_result { + warn!("virt_to_phys_user failed: {}", err); + return Err(format!("virt_to_phys_user failed: {}", err)); + } + + let cfg = MediatedBlkCfg { + name: string_to_cstr_arr(format!("MEDBLK{}", index)), + block_dev_path: string_to_cstr_arr(dev.clone()), + block_num: nsec, + dma_block_max: cache_size / BLOCK_SIZE as u64, + cache_size, + idx: index as u16, + pcache: false, + cache_va: cache_va as u64, + cache_ipa: phys_result.unwrap(), + cache_pa: 0, + }; + ctx.deassign_device(false).map_err(|x| format!("deassign device {} err: {}", dev, x))?; + + Ok(cfg) +} + diff --git a/cli/src/config.rs b/cli/src/config.rs new file mode 100644 index 0000000..63d0413 --- /dev/null +++ b/cli/src/config.rs @@ -0,0 +1,615 @@ +use std::{fs::{File, OpenOptions}, io::BufReader, os::fd::AsRawFd, process}; + +use libc::{c_void, close, ioctl, lseek, mmap, munmap, open, MAP_ANONYMOUS, MAP_FAILED, MAP_HUGETLB, MAP_PRIVATE, O_RDONLY, O_RDWR, PROT_READ, PROT_WRITE, SEEK_SET}; +use log::{error, info, warn}; +use serde::{de::{self, Visitor}, Deserialize, Deserializer, Serialize}; +use serde_json::Value; + +use crate::{config_arg::{VmAddConfigArg, VmAddDtbDeviceConfigArg, VmAddEmulatedDeviceConfigArg, VmAddMemoryRegionConfigArg, VmAddPassthroughDeviceIrqsConfigArg, VmAddPassthroughDeviceRegionConfigArg, VmAddPassthroughDeviceStreamsIdsConfigArg, VmKernelImageInfo, VmLoadKernelImgFileArg, VmMemoryColorBudgetConfigArg, VmSetCpuConfigArg}, daemon::{generate_hvc_mode, HVC_CONFIG, HVC_CONFIG_ADD_VM, HVC_CONFIG_CPU, HVC_CONFIG_DELETE_VM, HVC_CONFIG_DTB_DEVICE, HVC_CONFIG_EMULATED_DEVICE, HVC_CONFIG_MEMORY_COLOR_BUDGET, HVC_CONFIG_MEMORY_REGION, HVC_CONFIG_PASSTHROUGH_DEVICE_IRQS, HVC_CONFIG_PASSTHROUGH_DEVICE_REGION, HVC_CONFIG_PASSTHROUGH_DEVICE_STREAMS_IDS, HVC_CONFIG_UPLOAD_DEVICE_TREE, HVC_CONFIG_UPLOAD_KERNEL_IMAGE}, ioctl_arg::{IOCTL_SYS, IOCTL_SYS_SET_KERNEL_IMG_NAME}, util::{check_cache_address, file_size, string_to_u64, virt_to_phys_user}}; + +const CACHE_MAX: usize = 2 * 1024 * 1024; +const CMDLINE_MAX_LEN: usize = 1024; + +const PASSTHROUGH_DEV_MAX_NUM: usize = 128; +const EMULATED_DEV_MAX_NUM: usize = 16; +const DTB_DEV_MAX_NUM: usize = 16; +const DEV_MAX_NUM: usize = PASSTHROUGH_DEV_MAX_NUM + EMULATED_DEV_MAX_NUM + DTB_DEV_MAX_NUM; +const CFG_MAX_NUM: usize = 0x10; +const IRQ_MAX_NUM: usize = 0x40; + +#[allow(non_camel_case_types)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum VmType { + VM_T_LINUX, + VM_T_BARE_MATEL_APP, + VM_T_FREERTOS, +} + +#[allow(non_camel_case_types)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum EmuDeviceType { + EMU_DEVICE_T_CONSOLE, + EMU_DEVICE_T_GICD, + EMU_DEVICE_T_GPPT, + EMU_DEVICE_T_VIRTIO_BLK, + EMU_DEVICE_T_VIRTIO_NET, + EMU_DEVICE_T_VIRTIO_CONSOLE, + EMU_DEVICE_T_SHYPER, + EMU_DEVICE_T_VIRTIO_BLK_MEDIATED, + EMU_DEVICE_T_IOMMU, + EMU_DEVICE_T_SRE, + EMU_DEVICE_T_SGIR, + EMU_DEVICE_T_GICR, + EMU_DEVICE_T_META, + EMU_DEVICE_T_PLIC, +} + +#[allow(non_camel_case_types)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum DtbDeviceType { + DTB_DEVICE_T_CONSOLE, + DTB_DEVICE_T_GICD, + DTB_DEVICE_T_GICC, + DTB_DEVICE_T_GICR, + DTB_DEVICE_T_PLIC, +} + +// parse hex string to u64, like: "0x8000" -> 32768 +fn deserialize_hex_string<'de, D>(deserializer: D) -> Result +where + D: de::Deserializer<'de>, +{ + struct HexStringVisitor; + + impl<'de> Visitor<'de> for HexStringVisitor { + type Value = u64; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a hex string") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + let value = value.trim(); + if value == "0" { + Ok(0) + } else if value.len() <= 2 { + Err(de::Error::custom("value is not long enough for hex string")) + } else { + // Remove the "0x" prefix and parse the remaining string as u64 + u64::from_str_radix(&value[2..], 16).map_err(de::Error::custom) + } + } + } + + deserializer.deserialize_str(HexStringVisitor) +} + +fn deserialize_binary_string<'de, D>(deserializer: D) -> Result +where + D: de::Deserializer<'de>, +{ + struct BinStringVisitor; + + impl<'de> Visitor<'de> for BinStringVisitor { + type Value = u32; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a binary string, like 0b0111") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + // Remove the "0b" prefix and parse the remaining string as u64 + u32::from_str_radix(&value[2..], 2).map_err(de::Error::custom) + } + } + + deserializer.deserialize_str(BinStringVisitor) +} + +// parse colors, like "0-13,14,15,32-63", to num array +fn deserialize_memory_colors_str_to_vec<'de, D>(deserializer: D) -> Result>, D::Error> +where + D: de::Deserializer<'de>, +{ + struct MemColorsStrVisitor; + + impl<'de> Visitor<'de> for MemColorsStrVisitor { + type Value = Option>; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a colors description string, like \"0-13,14,15,32-63\"") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + let mut vec: Vec = Vec::new(); + for color_slice in value.split(',') { + if !color_slice.contains("-") { + let color = u64::from_str_radix(color_slice, 10).map_err(de::Error::custom)?; + vec.push(color); + continue; + } + + let pos = color_slice.find("-").unwrap(); + let len = color_slice.len(); + let start = u64::from_str_radix(&color_slice[0..pos], 10).map_err(de::Error::custom)?; + let end = u64::from_str_radix(&color_slice[pos+1..len], 10).map_err(de::Error::custom)?; + + for i in start..end+1 { + vec.push(i); + } + } + Ok(Some(vec)) + } + } + + deserializer.deserialize_str(MemColorsStrVisitor) +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct VmImageConfig { + pub kernel_filename: String, + #[serde(deserialize_with = "deserialize_hex_string")] + pub kernel_load_ipa: u64, + #[serde(deserialize_with = "deserialize_hex_string")] + pub kernel_entry_point: u64, + pub device_tree_filename: String, + #[serde(deserialize_with = "deserialize_hex_string")] + pub device_tree_load_ipa: u64, + pub ramdisk_filename: String, + #[serde(deserialize_with = "deserialize_hex_string")] + pub ramdisk_load_ipa: u64, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct MemoryRegion { + #[serde(deserialize_with = "deserialize_hex_string")] + pub ipa_start: u64, + #[serde(deserialize_with = "deserialize_hex_string")] + pub length: u64, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct VmMemoryConfig { + pub region: Vec, + // Add default attr, in case when colors field is missing then colors will be filled with None value + #[serde(deserialize_with = "deserialize_memory_colors_str_to_vec", default)] + pub colors: Option>, + pub budget: Option, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct VmCpuConfig { + pub num: u32, + #[serde(deserialize_with = "deserialize_binary_string")] + pub allocate_bitmap: u32, + pub master: i32, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct VmEmulatedDeviceConfig { + pub emulated_device_list: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct EmulatedDevice { + pub name: String, + #[serde(deserialize_with = "deserialize_hex_string")] + pub base_ipa: u64, + #[serde(deserialize_with = "deserialize_hex_string")] + pub length: u64, + pub irq_id: usize, + #[serde(default)] + pub cfg_num: usize, + #[serde(deserialize_with = "deserialize_cfg_list", default)] + pub cfg_list: Vec, + pub r#type: EmuDeviceType, +} + +fn deserialize_cfg_list<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de> +{ + // 解析为serdes_json定义的可变Value,以应对Value是不同值的情况 + let value: Value = Deserialize::deserialize(deserializer)?; + let mut vec: Vec = Vec::new(); + + match value { + Value::Array(arr) => { + for item in arr { + if let Value::Number(n) = item { + if let Some(n) = n.as_u64() { + vec.push(n); + } else { + return Err(de::Error::custom(format!("Can't cast {} to u64", n))); + } + } else if let Value::String(s) = item { + match string_to_u64(s) { + Ok(n) => vec.push(n), + Err(err) => { + return Err(de::Error::custom(err)); + } + } + } else { + return Err(de::Error::custom(format!("Not in num/string format: {}", item))); + } + } + Ok(vec) + } + _ => { + Err(de::Error::custom("cfg_list is not array!")) + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct VmPassthroughDeviceConfig { + pub passthrough_device_list: Vec +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct PassthroughDevice { + pub name: String, + #[serde(deserialize_with = "deserialize_hex_string")] + pub base_pa: u64, + #[serde(deserialize_with = "deserialize_hex_string")] + pub base_ipa: u64, + #[serde(deserialize_with = "deserialize_hex_string")] + pub length: u64, + pub smmu_id: Option, + pub irq_num: usize, + pub irq_list: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct VmDtbDeviceConfig { + pub dtb_device_list: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct DtbDevice { + pub name: String, + pub r#type: DtbDeviceType, + pub irq_num: usize, + pub irq_list: Vec, + #[serde(deserialize_with = "deserialize_hex_string")] + pub addr_region_ipa: u64, + #[serde(deserialize_with = "deserialize_hex_string")] + pub addr_region_length: u64, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct VmConfigEntry { + pub name: String, + pub r#type: VmType, + pub cmdline: String, + pub image: VmImageConfig, + pub memory: VmMemoryConfig, + pub cpu: VmCpuConfig, + pub emulated_device: VmEmulatedDeviceConfig, + pub passthrough_device: VmPassthroughDeviceConfig, + pub dtb_device: VmDtbDeviceConfig, +} + +pub fn parse_vm_entry(json_file: String) -> Result { + // Open the file in read-only mode with buffer. + let file = File::open(json_file).map_err(|err| err.to_string())?; + let reader = BufReader::new(file); + + let entry: VmConfigEntry = serde_json::from_reader(reader).map_err(|err| err.to_string())?; + Ok(entry) +} + +pub fn config_delete_vm(vm_id: u64) { + let fd_event = generate_hvc_mode(HVC_CONFIG, HVC_CONFIG_DELETE_VM) as u64; + let file = OpenOptions::new().read(true).write(true).open("/dev/shyper").unwrap(); + let fd = file.as_raw_fd(); + + let result = unsafe { libc::ioctl(fd, fd_event, vm_id) }; + + if result != 0 { + error!("Failed to delete VM[{}] config", vm_id); + } else { + info!("DELETE VM [{}] config successfully", vm_id); + } +} + +fn ioctl_send_config(fd: i32, fd_event: usize, arg: *const c_void) -> Result<(), String> { + let result = unsafe { ioctl(fd, fd_event as u64, arg) }; + if result != 0 { + return Err(String::from("ioctl failed")); + } + Ok(()) +} + +pub fn config_vm_info(vm_cfg: VmConfigEntry, vm_id: u64, fd: i32) -> Result<(), String> { + // 2. Add VM memory region + let fd_event = generate_hvc_mode(HVC_CONFIG, HVC_CONFIG_MEMORY_REGION); + for region in vm_cfg.memory.region { + let mem_cfg_arg = VmAddMemoryRegionConfigArg { + vmid: vm_id, + ipa_start: region.ipa_start, + length: region.length, + }; + ioctl_send_config(fd, fd_event, &mem_cfg_arg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_add_memory_region_config_arg"))?; + } + + // 3. Add VM memory color and budget information + let has_color = vm_cfg.memory.colors.is_some(); + let has_budget = vm_cfg.memory.budget.is_some(); + if has_color || has_budget { + let fd_event = generate_hvc_mode(HVC_CONFIG, HVC_CONFIG_MEMORY_COLOR_BUDGET); + let cfg = VmMemoryColorBudgetConfigArg { + vmid: vm_id, + color_num: if has_color { vm_cfg.memory.colors.as_ref().unwrap().len() as u64 } else { 0 }, + color_array_addr: if has_color { vm_cfg.memory.colors.unwrap().as_ptr() as *const u64 as u64 } else { 0 }, + budget: if has_budget { vm_cfg.memory.budget.unwrap() } else { 0 }, + }; + ioctl_send_config(fd, fd_event, &cfg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_memory_color_budget_config_arg"))?; + } + + // 4. Set VM CPU config + let fd_event = generate_hvc_mode(HVC_CONFIG, HVC_CONFIG_CPU); + let cpu_cfg_arg = VmSetCpuConfigArg { + vmid: vm_id, + num: vm_cfg.cpu.num, + allocate_bitmap: vm_cfg.cpu.allocate_bitmap, + master: vm_cfg.cpu.master, + }; + ioctl_send_config(fd, fd_event, &cpu_cfg_arg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_cpu_config_arg"))?; + + // 5. Add VM emulated device config + let fd_event = generate_hvc_mode(HVC_CONFIG, HVC_CONFIG_EMULATED_DEVICE); + for device in vm_cfg.emulated_device.emulated_device_list { + let emu_cfg_arg = VmAddEmulatedDeviceConfigArg { + vmid: vm_id, + dev_name_addr: device.name.as_ptr() as u64, + dev_name_length: device.name.len() as u64, + base_ipa: device.base_ipa, + length: device.length, + irq_id: device.irq_id as u64, + cfg_list_addr: device.cfg_list.as_ptr() as *const u64 as u64, + cfg_list_length: device.cfg_list.len() as u64, + emu_type: device.r#type as u64, + }; + ioctl_send_config(fd, fd_event, &emu_cfg_arg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_emulated_device_config_arg"))?; + } + + // record passthrough device irqs and stream ids + let mut passthrough_irqs: Vec = Vec::new(); + let mut passthrough_stream_ids: Vec = Vec::new(); + + // 6. Add VM passthrough device region config. + let fd_event = generate_hvc_mode(HVC_CONFIG, HVC_CONFIG_PASSTHROUGH_DEVICE_REGION); + for device in vm_cfg.passthrough_device.passthrough_device_list { + let passthrough_cfg_arg = VmAddPassthroughDeviceRegionConfigArg { + vmid: vm_id, + base_ipa: device.base_ipa, + base_pa: device.base_pa, + length: device.length, + }; + ioctl_send_config(fd, fd_event, &passthrough_cfg_arg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_passthrough_device_region_config_arg"))?; + + passthrough_irqs.append(&mut device.irq_list.clone().into_iter().map(|x| x as u64).collect()); + if device.smmu_id.is_some() { + passthrough_stream_ids.push(device.smmu_id.unwrap() as u64); + } + } + + // 7. Add VM passthrough device irqs. + if !passthrough_irqs.is_empty() { + let fd_event = generate_hvc_mode(HVC_CONFIG, HVC_CONFIG_PASSTHROUGH_DEVICE_IRQS); + let passthrough_irqs_cfg_arg = VmAddPassthroughDeviceIrqsConfigArg { + vmid: vm_id, + irqs_addr: passthrough_irqs.as_ptr() as *const u64 as u64, + irqs_length: passthrough_irqs.len() as u64, + }; + ioctl_send_config(fd, fd_event, &passthrough_irqs_cfg_arg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_passthrough_device_irqs_config_arg"))?; + } + + // 8. Add VM passthrough device streams ids + if !passthrough_stream_ids.is_empty() { + let fd_event = generate_hvc_mode(HVC_CONFIG, HVC_CONFIG_PASSTHROUGH_DEVICE_STREAMS_IDS); + let passthrough_stream_ids_cfg_arg = VmAddPassthroughDeviceStreamsIdsConfigArg { + vmid: vm_id, + streams_ids_addr: passthrough_stream_ids.as_ptr() as *const u64 as u64, + streams_ids_length: passthrough_stream_ids.len() as u64, + }; + ioctl_send_config(fd, fd_event, &passthrough_stream_ids_cfg_arg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_passthrough_device_streams_ids_config_arg"))?; + } + + // 9. Add VM dtb device config + let fd_event = generate_hvc_mode(HVC_CONFIG, HVC_CONFIG_DTB_DEVICE); + for dtb_device in vm_cfg.dtb_device.dtb_device_list { + let dtb_cfg_arg = VmAddDtbDeviceConfigArg { + vmid: vm_id, + dev_name_addr: dtb_device.name.as_ptr() as u64, + dev_name_length: dtb_device.name.len() as u64, + dev_type: dtb_device.r#type as u64, + irq_list_addr: dtb_device.irq_list.as_ptr() as *const u64 as u64, + irq_list_length: dtb_device.irq_list.len() as u64, + addr_region_ipa: dtb_device.addr_region_ipa, + }; + ioctl_send_config(fd, fd_event, &dtb_cfg_arg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_dtb_device_config_arg"))?; + } + + // 10. Copy dtb_file and img_file to memory + if !vm_cfg.image.device_tree_filename.is_empty() { + copy_device_tree_to_memory(vm_id, vm_cfg.image.device_tree_filename.clone(), fd as u32).map_err(|_| format!("failed to copy device tree {} to memory", vm_cfg.image.device_tree_filename))?; + } + if !copy_img_file_to_memory(vm_id, vm_cfg.image.kernel_filename.clone(), fd as u32).is_ok() { + return Err(format!("failed to copy kernel image {} to memory", vm_cfg.image.kernel_filename)); + } + + // 10. Store kernel image file name in kernel module. + let fd_event = generate_hvc_mode(IOCTL_SYS, IOCTL_SYS_SET_KERNEL_IMG_NAME); + let mut img_arg = VmKernelImageInfo { + vm_id, + image_name: [0; 32], + }; + img_arg.image_name[..vm_cfg.image.kernel_filename.len()].copy_from_slice(vm_cfg.image.kernel_filename.as_bytes()); + img_arg.image_name[vm_cfg.image.kernel_filename.len()] = 0; + + let result = unsafe { ioctl(fd, fd_event as u64, &img_arg as *const _ as *const c_void) }; + if result != 0 { + return Err(String::from("Failed to set kernel image name")); + } + + Ok(()) +} + +pub fn config_add_vm(config_json: String) -> Result { + let vm_cfg = parse_vm_entry(config_json)?; + let vm_cfg_2 = vm_cfg.clone(); + println!("Parse VM config successfully, VM name [{}]", vm_cfg.name); + + let fd = unsafe { + open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR) + }; + + if fd < 0 { + return Err(format!("Failed to open /dev/shyper: {}", fd)); + } + + // 1. Add VM to Hypervisor + let vm_id: u64 = 0; + let vm_add_req = VmAddConfigArg { + vm_name_addr: vm_cfg.name.as_ptr() as u64, + vm_name_length: vm_cfg.name.len() as u64, + vm_type: vm_cfg.r#type as u64, + cmd_line_addr: vm_cfg.cmdline.as_ptr() as u64, + cmd_line_length: vm_cfg.cmdline.len() as u64, + kernel_load_ipa: vm_cfg.image.kernel_load_ipa, + device_tree_load_ipa: vm_cfg.image.device_tree_load_ipa, + ramdisk_load_ipa: vm_cfg.image.ramdisk_load_ipa, + vm_id_addr: &vm_id as *const u64 as u64, + }; + let fd_event = generate_hvc_mode(HVC_CONFIG, HVC_CONFIG_ADD_VM); + ioctl_send_config(fd, fd_event, &vm_add_req as *const _ as *const c_void).map_err(|_| String::from("config_add_vm failed"))?; + info!("Send VM [{}] config successfully", vm_id); + + // deal with failure condition + match config_vm_info(vm_cfg_2, vm_id, fd) { + Ok(_) => { + info!("Config VM [{}] successfully", vm_id); + unsafe { close(fd) }; + Ok(vm_id) + } + Err(err) => { + error!("Config VM [{}] failed: {}", vm_id, err); + unsafe { close(fd) }; + config_delete_vm(vm_id); + Err(err) + } + } +} + +fn copy_file_to_hypervisor(vmid: u64, filename: String, shyper_fd: u32, upload_mode: usize) -> Result<(), String> { + // Create a Cache_buffer, copy it in batches to the buffer, and then use ioctl to copy the data to the hypervisor + let file_size = file_size(&filename)?; + let mut copied_size: u64 = 0; + let mut coping_size: u64; + let cache_va: *mut c_void; + let file_fd: i32; + + unsafe { + file_fd = open(filename.as_ptr() as *const u8, O_RDONLY); + cache_va = mmap(0 as *mut c_void, CACHE_MAX, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 0, 0); + if cache_va == MAP_FAILED { + close(file_fd); + return Err(String::from("Allocate cache memory error!")); + } + } + + // check whether cache address is valid + if let Err(err) = check_cache_address(cache_va, CACHE_MAX as u64) { + warn!("Cache address is invalid"); + unsafe { + close(file_fd); + munmap(cache_va, CACHE_MAX); + } + return Err(err); + } + + let cache_ipa = virt_to_phys_user(process::id(), cache_va as u64).map_err( + |err| { + warn!("Failed to get cache pa\n"); + unsafe { + close(file_fd); + munmap(cache_va, CACHE_MAX); + } + err + } + )?; + + while copied_size < file_size { + // set read start is previous read + unsafe { + if lseek(file_fd, copied_size as i64, SEEK_SET) < 0 { + warn!("seek file {} pos {} failed\n", filename, copied_size); + close(file_fd); + munmap(cache_va, CACHE_MAX); + return Err(String::from("lseek err")); + } + + coping_size = if file_size - copied_size > CACHE_MAX as u64 { + CACHE_MAX as u64 + } else { + file_size + }; + + if libc::read(file_fd, cache_va, coping_size as usize) == -1 { + warn!("read kernel image {} pos {} size {} failed\n", filename, copied_size, + coping_size); + close(file_fd); + munmap(cache_va, CACHE_MAX); + return Err(format!("read file {} err", filename)); + } + + // Use HVC to copy to Hypervisor memory + let arg = VmLoadKernelImgFileArg { + vmid, + img_size: file_size, + cache_ipa, + load_offset: copied_size, + load_size: coping_size, + }; + let fd_event = generate_hvc_mode(HVC_CONFIG, upload_mode); + + let ret = ioctl(shyper_fd as i32, fd_event as u64, &arg as *const _ as *const u8); + if ret != 0 { + warn!("Copy_file_to_hypervisor: ioctl failed"); + } + copied_size += coping_size; + } + } + + unsafe { + close(file_fd); + if munmap(cache_va, CACHE_MAX) != 0 { + warn!("Failed to unmap cache va {:#x}", cache_va as u64); + } + } + + Ok(()) +} + +pub fn copy_img_file_to_memory(vmid: u64, filename: String, shyper_fd: u32) -> Result<(), String> { + copy_file_to_hypervisor(vmid, filename, shyper_fd, HVC_CONFIG_UPLOAD_KERNEL_IMAGE) +} + +pub fn copy_device_tree_to_memory(vmid: u64, filename: String, shyper_fd: u32) -> Result<(), String> { + copy_file_to_hypervisor(vmid, filename, shyper_fd, HVC_CONFIG_UPLOAD_DEVICE_TREE) +} + + + + diff --git a/cli/src/config_arg.rs b/cli/src/config_arg.rs new file mode 100644 index 0000000..5d02c91 --- /dev/null +++ b/cli/src/config_arg.rs @@ -0,0 +1,98 @@ + +#[repr(C)] +pub struct VmAddConfigArg { + pub vm_name_addr: u64, + pub vm_name_length: u64, + pub vm_type: u64, + pub cmd_line_addr: u64, + pub cmd_line_length: u64, + pub kernel_load_ipa: u64, + pub device_tree_load_ipa: u64, + pub ramdisk_load_ipa: u64, + pub vm_id_addr: u64, +} + +#[repr(C)] +pub struct VmSetCpuConfigArg { + pub vmid: u64, + pub num: u32, + pub allocate_bitmap: u32, + pub master: i32, +} + +#[repr(C)] +pub struct VmMemoryColorBudgetConfigArg { + pub vmid: u64, + pub color_num: u64, + pub color_array_addr: u64, + pub budget: u32, +} + +#[repr(C)] +pub struct VmAddEmulatedDeviceConfigArg { + pub vmid: u64, + pub dev_name_addr: u64, + pub dev_name_length: u64, + pub base_ipa: u64, + pub length: u64, + pub irq_id: u64, + pub cfg_list_addr: u64, + pub cfg_list_length: u64, + pub emu_type: u64, +} + +#[repr(C)] +pub struct VmAddMemoryRegionConfigArg { + pub vmid: u64, + pub ipa_start: u64, + pub length: u64, +} + + +#[repr(C)] +pub struct VmAddPassthroughDeviceRegionConfigArg { + pub vmid: u64, + pub base_ipa: u64, + pub base_pa: u64, + pub length: u64, +} + +#[repr(C)] +pub struct VmAddPassthroughDeviceIrqsConfigArg { + pub vmid: u64, + pub irqs_addr: u64, + pub irqs_length: u64, +} + +#[repr(C)] +pub struct VmAddPassthroughDeviceStreamsIdsConfigArg { + pub vmid: u64, + pub streams_ids_addr: u64, + pub streams_ids_length: u64, +} + +#[repr(C)] +pub struct VmAddDtbDeviceConfigArg { + pub vmid: u64, + pub dev_name_addr: u64, + pub dev_name_length: u64, + pub dev_type: u64, + pub irq_list_addr: u64, + pub irq_list_length: u64, + pub addr_region_ipa: u64, +} + +#[repr(C)] +pub struct VmLoadKernelImgFileArg { + pub vmid: u64, + pub img_size: u64, + pub cache_ipa: u64, + pub load_offset: u64, + pub load_size: u64, +} + +#[repr(C)] +pub struct VmKernelImageInfo { + pub vm_id: u64, + pub image_name: [u8; 32], +} diff --git a/cli/src/daemon.rs b/cli/src/daemon.rs new file mode 100644 index 0000000..7eb4f5a --- /dev/null +++ b/cli/src/daemon.rs @@ -0,0 +1,271 @@ +use std::{fs::{self, File}, io::Read, mem, process}; +use libc::{c_char, c_int, c_ulong, c_ulonglong, close, ioctl, open, uintptr_t, O_RDWR, SIGTERM, SIGUSR1}; +use serde::{Serialize, Deserialize}; +use log::{debug, error, info, warn}; +use signal_hook::iterator::Signals; + +use crate::{blk::{mediated_blk_add, mediated_blk_init, mediated_blk_read, mediated_blk_write, MediatedBlkCfg, MED_BLK_LIST}, config::copy_img_file_to_memory, ioctl_arg::{IOCTL_SYS, IOCTL_SYS_GET_KERNEL_IMG_NAME}, util::cstr_arr_to_string, vmm::vmm_boot}; + +#[derive(Serialize, Deserialize, Debug)] +struct DaemonConfig { + mediated: Vec +} + +#[repr(C)] +struct HvcType { + hvc_fid: u64, + hvc_event: u64, +} + +#[repr(C)] +struct BlkArg { + hvc_fid: u64, + hvc_event: u64, + blk_id: u16, + r#type: u32, + sector: u64, + count: u64, +} + +#[repr(C)] +struct CfgArg { + hvc_fid: u64, + hvc_event: u64, + vm_id: u64, +} + +// hvc_fid +pub const HVC_SYS: usize = 0; +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 +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; +pub const HVC_VMM_GET_VM_STATE: usize = 1; +pub const HVC_VMM_BOOT_VM: usize = 2; +pub const HVC_VMM_SHUTDOWN_VM: usize = 3; +pub const HVC_VMM_REBOOT_VM: usize = 4; +pub const HVC_VMM_GET_VM_DEF_CFG: usize = 5; +pub const HVC_VMM_GET_VM_CFG: usize = 6; +pub const HVC_VMM_SET_VM_CFG: usize = 7; +pub const HVC_VMM_GET_VM_ID: usize = 8; +pub const HVC_VMM_TRACE_VMEXIT: usize = 9; +// for src vm: send msg to MVM to ask for migrating +pub const HVC_VMM_MIGRATE_START: usize = 10; +pub const HVC_VMM_MIGRATE_READY: usize = 11; +// for sender: copy dirty memory to receiver +pub const HVC_VMM_MIGRATE_MEMCPY: usize = 12; +pub const HVC_VMM_MIGRATE_FINISH: usize = 13; +// for receiver: init new vm but not boot +pub const HVC_VMM_MIGRATE_INIT_VM: usize = 14; +pub const HVC_VMM_MIGRATE_VM_BOOT: usize = 15; +pub const HVC_VMM_VM_REMOVE: usize = 16; + +// hvc_ivc_event +pub const HVC_IVC_UPDATE_MQ: usize = 0; +pub const HVC_IVC_SEND_MSG: usize = 1; +pub const HVC_IVC_BROADCAST_MSG: usize = 2; +pub const HVC_IVC_INIT_KEEP_ALIVE: usize = 3; +pub const HVC_IVC_KEEP_ALIVE: usize = 4; +pub const HVC_IVC_ACK: usize = 5; +pub const HVC_IVC_GET_TIME: usize = 6; +pub const HVC_IVC_SHARE_MEM: usize = 7; +pub const HVC_IVC_SEND_SHAREMEM: usize = 0x10; +//shared mem communication +pub const HVC_IVC_GET_SHARED_MEM_IPA: usize = 0x11; +pub const HVC_IVC_SEND_SHAREMEM_TEST_SPEED: usize = 0x12; + +// hvc_mediated_event +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_MEDIATED_USER_NOTIFY: usize = 0x20; +// hvc_config_event +pub const HVC_CONFIG_ADD_VM: usize = 0; +pub const HVC_CONFIG_DELETE_VM: usize = 1; +pub const HVC_CONFIG_CPU: usize = 2; +pub const HVC_CONFIG_MEMORY_REGION: usize = 3; +pub const HVC_CONFIG_EMULATED_DEVICE: usize = 4; +pub const HVC_CONFIG_PASSTHROUGH_DEVICE_REGION: usize = 5; +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; + +pub fn generate_hvc_mode(fid: usize, event: usize) -> usize { + ((fid << 8) | event) & 0xffff +} + +// Execute the signal processing function only once +fn sig_handle_event(signal: i32) { + // info!("Receive signal {}", signal); + + let mut file = File::open("/dev/shyper").unwrap(); + const HVC_TYPE_SIZE: usize = mem::size_of::(); + const BLK_ARG_SIZE: usize = mem::size_of::(); + const CONFIG_ARG_SIZE: usize = mem::size_of::(); + let mut buf: [u8; 256] = [0; 256]; + + let n = file.read(&mut buf).unwrap(); + drop(file); + + if n == 0 { + warn!("Lost signal {}!", signal); + } + let hvc_type: HvcType; + + unsafe { + // try_into cast &[u8] to &[u8; HVC_TYPE_SIZE] + hvc_type = mem::transmute::<[u8; HVC_TYPE_SIZE], HvcType>(buf[0..HVC_TYPE_SIZE].try_into().unwrap()); + } + + match hvc_type.hvc_fid as usize { + HVC_MEDIATED => { + match hvc_type.hvc_event as usize { + HVC_MEDIATED_USER_NOTIFY => { + let blk_arg; + unsafe { + blk_arg = mem::transmute::<[u8; BLK_ARG_SIZE], BlkArg>(buf[0..BLK_ARG_SIZE].try_into().unwrap()); + } + if blk_arg.r#type == 0 { + mediated_blk_read(blk_arg.blk_id, blk_arg.sector, blk_arg.count); + } else if blk_arg.r#type == 1 { + mediated_blk_write(blk_arg.blk_id, blk_arg.sector, blk_arg.count); + } else { + warn!("[sig_handle_event] unknown blk req type {}", blk_arg.r#type); + } + return; + } + _ => return + } + } + HVC_CONFIG => { + match hvc_type.hvc_event as usize { + HVC_CONFIG_UPLOAD_KERNEL_IMAGE => { + let cfg_arg; + unsafe { + cfg_arg = mem::transmute::<[u8; CONFIG_ARG_SIZE], CfgArg>(buf[0..CONFIG_ARG_SIZE].try_into().unwrap()); + } + let fd_event = generate_hvc_mode(IOCTL_SYS, IOCTL_SYS_GET_KERNEL_IMG_NAME); + + #[repr(C)] + struct NameArg { + vm_id: u64, + name_addr: *mut c_char, + } + + let filename: [u8; 64] = [0; 64]; + let mut name_arg: NameArg = NameArg { + vm_id: cfg_arg.vm_id, + name_addr: filename.as_ptr() as *mut c_char + }; + + unsafe { + let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); + if ioctl(fd, fd_event as c_ulonglong, &mut name_arg as *mut NameArg as uintptr_t) != 0 { + warn!("sig_handle_event: failed to get VM[{}] name\n", cfg_arg.vm_id); + close(fd); + return; + } + + let img_name = cstr_arr_to_string(filename.as_slice()); + if let Err(err) = copy_img_file_to_memory(cfg_arg.vm_id, img_name, fd as u32) { + warn!("sig_handle_event: failed to copy img file to memory: {}", err); + return; + } + vmm_boot(cfg_arg.vm_id as u32); + close(fd); + return; + } + } + _ => return + } + } + _ => return + } +} + +pub fn init_daemon() { + // IGNORE: get semaphore and file_lock + // IGNORE: create migrate fifo file + // IGNORE: IVC Init + let pid = process::id(); + let mut vmid: c_int = 0; + unsafe { + let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); + if fd < 0 { + error!("open /dev/shyper failed: errcode = {}", *libc::__errno_location()); + return; + } + // Set process id, in case kernel module can send signal to cli + if ioctl(fd, 0x1002, c_ulong::from(pid)) < 0 { + error!("ioctl set pid failed: errcode = {}", *libc::__errno_location()); + close(fd); + return; + } + // Get vmid + if ioctl(fd, 0x1004, &mut vmid as *mut c_int) < 0 { + error!("ioctl get vmid failed: errcode = {}", *libc::__errno_location()); + close(fd); + return; + } + close(fd); + } + + // Init mediated block partition + if vmid == 0 { + info!("VM[{}] start to init blk service\n", vmid); + mediated_blk_init(); + } + info!("VM[{}] daemon process init success\n", vmid); + + // TODO: The signal processing at this time is not real-time, but the signal is first written to the channel in the custom handler set in signal_hook, + // and then captured in the user mode polling + // Consider using signal_hook's real-time signal processing to enhance real-time performance + let mut signals = Signals::new(&[SIGUSR1]).unwrap(); + for signal in &mut signals { + sig_handle_event(signal); + } +} + +pub fn config_daemon(path: String) -> Result<(), String> { + info!("Start Shyper-cli daemon configure"); + let json_str = fs::read_to_string(path.clone()).map_err( + |err| format!("Open json file {} err: {}", path.clone(), err) + )?; + let config: DaemonConfig = serde_json::from_str(&json_str).map_err( + |err| format!("Parse json err: {}", err) + )?; + debug!("config is {:?}", config); + + let mut disk_cnt = 0; + let mut disks: Vec = Vec::new(); + for disk in config.mediated { + // Add disk, and if error happens, skip it + let result = mediated_blk_add(disk_cnt, disk.clone()); + if result.is_ok() { + disk_cnt += 1; + disks.push(result.unwrap()); + } else { + warn!("Add mediated disk {} failed: {}", disk, result.err().unwrap()); + } + } + MED_BLK_LIST.set(disks).unwrap(); + + info!("daemon configure {} mediated disk(s)", disk_cnt); + info!("daemon configuration finished"); + Ok(()) +} \ No newline at end of file diff --git a/cli/src/ioctl_arg.rs b/cli/src/ioctl_arg.rs new file mode 100644 index 0000000..edfe086 --- /dev/null +++ b/cli/src/ioctl_arg.rs @@ -0,0 +1,11 @@ +pub const IOCTL_SYS: usize = 0x10; + +// ioctl_sys_event +pub const IOCTL_SYS_GET_STATE: usize = 0; +pub const IOCTL_SYS_RECEIVE_MSG: usize = 1; +pub const IOCTL_SYS_INIT_USR_PID: usize = 2; +pub const IOCTL_SYS_GET_SEND_IDX: usize = 3; +pub const IOCTL_SYS_GET_VMID: usize = 4; +pub const IOCTL_SYS_SET_KERNEL_IMG_NAME: usize = 5; +pub const IOCTL_SYS_GET_KERNEL_IMG_NAME: usize = 6; +pub const IOCTL_SYS_APPEND_MED_BLK: usize = 0x10; diff --git a/cli/src/main.rs b/cli/src/main.rs new file mode 100644 index 0000000..387ff78 --- /dev/null +++ b/cli/src/main.rs @@ -0,0 +1,171 @@ +mod sys; +mod util; +mod daemon; +mod ioctl_arg; +mod vmm; +mod blk; +mod config; +mod config_arg; + +use std::path::Path; + +use clap::{Parser, Subcommand, ValueEnum}; +use config::parse_vm_entry; +use daemon::{config_daemon, init_daemon}; +use log::{error, info, warn}; +use sys::{sys_reboot, sys_shutdown, sys_test, sys_update}; +use vmm::{vmm_boot, vmm_getvmid, vmm_list_vm_info, vmm_reboot, vmm_remove}; + +use crate::config::config_add_vm; + +#[derive(Parser)] +#[command(version, author, about, long_about = "CommandLine Interface for Shyper/Rust-Shyper Hypervisor")] +struct CLI { + #[command(subcommand)] + subcmd: CLISubCmd, +} + +#[derive(Subcommand)] +enum CLISubCmd { + /// system subcommand + System { + #[command(subcommand)] + subcmd: SystemSubCmd, + }, + Vm { + #[command(subcommand)] + subcmd: VmSubCmd, + } +} + +#[derive(Subcommand)] +enum SystemSubCmd { + Reboot { + /// A force flag to set. + #[arg(short, long)] + force: bool, + }, + Shutdown { + /// A force flag to set. + #[arg(short, long)] + force: bool, + }, + Update { + /// new hypervisor image + image: String, + }, + Test { + + }, + Daemon { + /// daemon config, specifically mediated disk config + #[arg(default_value = "cli-config.json")] + config: String + } +} + +#[derive(Subcommand)] +enum VmSubCmd { + /// list the info of the vm + List { + + }, + Boot { + vmid: u32, + /// Choose display method, currently only supported SDL2. + #[arg(long)] + display: Option, + }, + Reboot { + vmid: u32, + /// A force flag to set. + #[arg(short, long)] + force: bool, + }, + Remove { + vmid: u32, + }, + Getdefconfig { + vmid: u32, + }, + Config { + /// vm config file, in json format + #[arg(value_parser = parse_file)] + config: String, + }, + Delconfig { + vmid: u32, + /// A force flag to set. + #[arg(short, long)] + force: bool, + }, + Getvmid { + + } +} + +fn parse_file(file: &str) -> Result { + if file.is_empty() { + return Err(String::from("CONFIG can't be empty!")); + } + + let file_path = Path::new(file); + if !file_path.exists() { + // judge whether the file exists or not + return Err(String::from(format!("File {} not exists!", file))); + } + + Ok(String::from(file)) +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +enum DisplayMode { + SDL +} + +fn exec_system_cmd(subcmd: SystemSubCmd) { + match subcmd { + SystemSubCmd::Reboot { force } => sys_reboot(force), + SystemSubCmd::Shutdown { force } => sys_shutdown(force), + SystemSubCmd::Update { image } => sys_update(image), + SystemSubCmd::Test { } => sys_test(), + SystemSubCmd::Daemon { config } => { + config_daemon(config).unwrap(); + init_daemon(); + }, + } +} + +fn exec_vm_cmd(subcmd: VmSubCmd) { + match subcmd { + VmSubCmd::List { } => vmm_list_vm_info(), + VmSubCmd::Boot { vmid, display } => vmm_boot(vmid), + VmSubCmd::Reboot { vmid, force } => vmm_reboot(force, vmid), + VmSubCmd::Remove { vmid } => vmm_remove(vmid), + VmSubCmd::Getdefconfig { vmid } => todo!(), + VmSubCmd::Config { config } => { + if let Err(err) = config_add_vm(config) { + error!("Add vm failed: {}", err); + } + }, + VmSubCmd::Delconfig { vmid, force } => todo!(), + VmSubCmd::Getvmid { } => vmm_getvmid(), + } +} + +fn main() { + // configure logger and set log level + env_logger::Builder::new() + .filter_level(log::LevelFilter::Info) + .init(); + + let cli = CLI::parse(); + match cli.subcmd { + CLISubCmd::System { subcmd } => { + exec_system_cmd(subcmd); + }, + CLISubCmd::Vm { subcmd } => { + exec_vm_cmd(subcmd); + }, + } +} diff --git a/cli/src/sys.rs b/cli/src/sys.rs new file mode 100644 index 0000000..e2fb5b4 --- /dev/null +++ b/cli/src/sys.rs @@ -0,0 +1,76 @@ +use libc::{c_void, close, ioctl, lseek, open, read, size_t, MAP_LOCKED, MAP_SHARED, O_RDONLY, O_RDWR, PROT_READ, PROT_WRITE, SEEK_SET}; + +use crate::util::{bool_to_cint, file_size}; + +pub fn sys_reboot(force: bool) { + unsafe { + let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); + println!("sys reboot fd {}", fd); + if ioctl(fd, 0x0000, bool_to_cint(force)) != 0 { + println!("err: ioctl fail!"); + } + close(fd); + } +} + +pub fn sys_shutdown(force: bool) { + unsafe { + let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); + println!("sys shutdown fd {}", fd); + if ioctl(fd, 0x0001, bool_to_cint(force)) != 0 { + println!("err: ioctl fail!"); + } + close(fd); + } +} + +pub fn sys_test() { + unsafe { + let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); + println!("sys test fd {}", fd); + if ioctl(fd, 0x0004, 0) != 0 { + println!("err: ioctl fail!"); + } + close(fd); + } +} + +fn update_image(path: String) -> u64 { + let size = file_size(&path).unwrap(); + if size == 0 { + return 0; + } + + unsafe { + let share_mem_fd = open("/dev/hyper_update".as_ptr() as *const u8, O_RDWR); + let addr = libc::mmap(0 as *mut c_void, 0x8000000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, share_mem_fd, 0); + if addr.is_null() { + println!("[sys_update] update image mmap failed"); + return size; + } + let image_fd = open(path.as_ptr() as *const u8, O_RDONLY); + + // mkimage file has 64B header + lseek(image_fd, 64, SEEK_SET); + read(image_fd, addr, (size - 64) as size_t); + close(image_fd); + close(share_mem_fd); + } + size +} + +pub fn sys_update(path: String) { + let size = update_image(path.clone()); + if size == 0 { + println!("File {} size is 0, abort", path); + return; + } + + unsafe { + let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); + if ioctl(fd, 0x0000, size) != 0 { + println!("err: ioctl fail!"); + } + close(fd); + } +} \ No newline at end of file diff --git a/cli/src/util.rs b/cli/src/util.rs new file mode 100644 index 0000000..1c7a14e --- /dev/null +++ b/cli/src/util.rs @@ -0,0 +1,127 @@ +use std::{ffi::CString, fs::File, io::{Read, Seek}, mem, slice::from_raw_parts_mut}; + +use libc::{c_int, c_uchar, c_ulong, c_void, sysconf, _SC_PAGE_SIZE}; + +pub fn bool_to_cint(var: bool) -> c_int { + if var { + c_int::from(1) + } else { + c_int::from(0) + } +} + +pub fn file_size(path: &String) -> Result { + if let Ok(file) = File::open(&path) { + if let Ok(metadata) = file.metadata() { + Ok(metadata.len()) + } else { + Err(format!("Get file {} metadata err", path)) + } + } else { + Err(format!("Open file {} err", path)) + } +} + +pub fn cstr_arr_to_string(buf: &[u8]) -> String { + let mut vec: Vec = Vec::new(); + for i in buf { + if *i == 0 { + // terminate char + break; + } else { + vec.push(*i); + } + } + unsafe { + CString::from_vec_unchecked(vec).to_string_lossy().into() + } +} + +pub fn string_to_cstr_arr(s: String) -> [u8; 32] { + let mut buf: [u8; 32] = [0; 32]; + let s = s.as_bytes(); + for i in 0..s.len() { + buf[i] = s[i]; + } + buf +} + +// convert program of pid's virtual address to physical address +// returns paddr +pub fn virt_to_phys_user(pid: u32, vaddr: u64) -> Result { + let pagemap_path = format!("/proc/{}/pagemap", pid); + let mut file = File::open(&pagemap_path).map_err( + |err| format!("Open {} err: {}", &pagemap_path, err) + )?; + + let page_size: usize = unsafe { sysconf(_SC_PAGE_SIZE) } as usize; + let offset = ((vaddr as usize) / page_size) * (mem::size_of::() as usize); + + if file.seek(std::io::SeekFrom::Start(offset as u64)).is_err() { + return Err(format!("File {} is not big enough to access offset {}", pagemap_path, offset)); + } + + let mut pagemap_entry: c_ulong = 0; + if file.read_exact(unsafe { + from_raw_parts_mut( + &mut pagemap_entry as *mut _ as *mut u8, + mem::size_of::() + ) + }).is_err() { + return Err(format!("Read page table entry err")); + } + + if (pagemap_entry & (1 << 63)) == 0 { + return Err(format!("Virtual Address 0x{:#x} converts to paddr err: page not in memory", vaddr)); + } + + // Note: 以下注释是pagemap_entry每一位的意义 + // entry->soft_dirty = (data >> 55) & 1; + // entry->file_page = (data >> 61) & 1; + // entry->swapped = (data >> 62) & 1; + // entry->present = (data >> 63) & 1; + + let pfn = pagemap_entry & ((1 << 55) - 1); + let paddr = pfn * (page_size as u64) + vaddr % (page_size as u64); + Ok(paddr) +} + +pub fn check_cache_address(cache_va: *mut c_void, len: u64) -> Result<(), String> { + let cache_va = cache_va as *mut c_uchar; + // write_bytes + for i in 0..len { + unsafe { + *cache_va.add(i as usize) = (i % 128) as u8; + } + } + // read and check bytes + for i in 0..len { + unsafe { + if *cache_va.add(i as usize) != (i % 128) as u8 { + return Err(format!("check_cache_address: Mismatch at {} offset", i)); + } + } + } + Ok(()) +} + +pub fn string_to_u64(s: String) -> Result { + let s = s.trim().to_string(); + if s.starts_with("0x") { + match u64::from_str_radix(&s[2..], 16) { + Ok(num) => Ok(num), + Err(_) => Err(format!("Not hex string: {}", s)) + } + } else if s.starts_with("0b") { + match u64::from_str_radix(&s[2..], 2) { + Ok(num) => Ok(num), + Err(_) => Err(format!("Not binary string: {}", s)) + } + } else { + // must be decimal + match u64::from_str_radix(&s, 10) { + Ok(num) => Ok(num), + Err(_) => Err(format!("String {} is not in hex/bin/decimal format!", s)) + } + } +} diff --git a/cli/src/vmm.rs b/cli/src/vmm.rs new file mode 100644 index 0000000..2d0e65c --- /dev/null +++ b/cli/src/vmm.rs @@ -0,0 +1,149 @@ +use std::mem; + +use libc::{close, ioctl, open, O_RDWR}; +use log::{error, info}; + +use crate::util::cstr_arr_to_string; + +pub const NAME_MAX_LEN: usize = 32; +const VM_NUM_MAX: usize = 16; +const VM_PAGE_SIZE: usize = 0x1000; + +#[repr(C)] +#[derive(Clone)] +struct VMInfo { + pub id: u32, + pub vm_name: [u8; NAME_MAX_LEN], + pub vm_type: u32, + pub vm_state: u32, +} + +const VM_T_LINUX: u32 = 0; +const VM_T_BARE_MATEL_APP: u32 = 1; +const VM_T_FREERTOS: u32 = 2; + +const VM_S_INV: u32 = 0; +const VM_S_PENDING: u32 = 1; +const VM_S_ACTIVE: u32 = 2; + +#[repr(C)] +struct VMInfoList { + pub vm_num: usize, + pub info_list: [VMInfo; VM_NUM_MAX], +} + +pub fn vmm_boot(vm_id: u32) { + unsafe { + let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); + if ioctl(fd, 0x0102, vm_id) != 0 { + error!("err: ioctl fail!"); + } + close(fd); + } +} + +pub fn vmm_shutdown(force: bool, vm_id: u32) { + unsafe { + let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); + let arg: u64 = match force { + true => ((1 << 16) | vm_id) as u64, + false => vm_id as u64, + }; + if ioctl(fd, 0x0103, arg) != 0 { + error!("err: ioctl fail!"); + } + close(fd); + } +} + +pub fn vmm_reboot(force: bool, vm_id: u32) { + unsafe { + let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); + let arg: u64 = match force { + true => ((1 << 16) | vm_id) as u64, + false => vm_id as u64, + }; + if ioctl(fd, 0x0104, arg) != 0 { + error!("err: ioctl fail!"); + } + close(fd); + } +} + +pub fn vmm_remove(vm_id: u32) { + unsafe { + let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); + if ioctl(fd, 0x0110, vm_id) != 0 { + error!("err: ioctl fail!"); + } + close(fd); + } +} + +pub fn vmm_getvmid() { + unsafe { + let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); + let mut id: u32 = 0; + if ioctl(fd, 0x0108, &mut id as *mut u32) != 0 { + error!("err: ioctl fail!"); + } else { + info!("Current VM id is {}", id); + } + close(fd); + } +} + +pub fn vmm_list_vm_info() { + let vm_info_list: VMInfoList; + const VM_INFO_LIST_SIZE: usize = mem::size_of::(); + unsafe { + let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); + let buf: [u8; VM_PAGE_SIZE] = [0; VM_PAGE_SIZE]; + if ioctl(fd, 0x0100, buf.as_ptr()) != 0 { + error!("err: ioctl fail!"); + close(fd); + return; + } else { + vm_info_list = mem::transmute::<[u8; VM_INFO_LIST_SIZE], VMInfoList>(buf[0..VM_INFO_LIST_SIZE].try_into().unwrap()); + } + } + display_vm_list_info(vm_info_list); +} + +fn display_vm_list_info(vm_info_list: VMInfoList) { + let vm_num = vm_info_list.vm_num; + for i in 0..vm_num { + let info = &vm_info_list.info_list[i]; + println!("----------vm[{}]----------", i); + println!("vm id [{}] name: {} type: {}\n", info.id, cstr_arr_to_string(info.vm_name.as_slice()), info.vm_type); + match info.vm_type { + VM_T_LINUX => { + println!("vm type: Linux"); + } + VM_T_BARE_MATEL_APP => { + println!("vm type: Bare Metal App"); + } + VM_T_FREERTOS => { + println!("vm type: FreeRTOS"); + } + _ => { + println!("vm type: illegal type"); + } + } + + match info.vm_state { + VM_S_INV => { + println!("vm state: Inactive"); + } + VM_S_PENDING => { + println!("vm state: Pending"); + } + VM_S_ACTIVE => { + println!("vm state: Active"); + } + _ => { + println!("vm state: illegal state"); + } + } + } +} -- Gitee From eeea9604533e6b2b2cb7821f98ca8a5f9f0868c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E4=BB=81=E9=B9=8F?= <2110459069@qq.com> Date: Thu, 30 May 2024 11:39:03 +0800 Subject: [PATCH 2/2] fix: do cargo fmt --- cli/src/blk.rs | 109 +++++++++++++++++++++++-------- cli/src/config.rs | 146 +++++++++++++++++++++++++++++------------- cli/src/config_arg.rs | 2 - cli/src/daemon.rs | 134 +++++++++++++++++++------------------- cli/src/main.rs | 57 ++++++++--------- cli/src/sys.rs | 22 +++++-- cli/src/util.rs | 42 ++++++------ cli/src/vmm.rs | 20 ++++-- 8 files changed, 333 insertions(+), 199 deletions(-) diff --git a/cli/src/blk.rs b/cli/src/blk.rs index 9ee1f8a..389796a 100644 --- a/cli/src/blk.rs +++ b/cli/src/blk.rs @@ -1,9 +1,22 @@ -use std::{fs, os::linux::fs::MetadataExt, process::{id, Command}, slice::from_raw_parts, sync::Mutex}; -use libc::{c_uint, c_void, ioctl, memset, mmap, open, preadv2, size_t, MAP_ANONYMOUS, MAP_HUGETLB, MAP_PRIVATE, O_DIRECT, O_RDWR, PROT_READ, PROT_WRITE, S_IFBLK, S_IFMT}; +use std::{ + fs, + os::linux::fs::MetadataExt, + process::{id, Command}, + slice::from_raw_parts, + sync::Mutex, +}; +use libc::{ + c_uint, c_void, ioctl, memset, mmap, open, preadv2, size_t, MAP_ANONYMOUS, MAP_HUGETLB, MAP_PRIVATE, O_DIRECT, + O_RDWR, PROT_READ, PROT_WRITE, S_IFBLK, S_IFMT, +}; use log::{info, warn}; use once_cell::sync::OnceCell; -use crate::{daemon::generate_hvc_mode, ioctl_arg::{IOCTL_SYS, IOCTL_SYS_APPEND_MED_BLK}, util::{check_cache_address, cstr_arr_to_string, string_to_cstr_arr, virt_to_phys_user}}; +use crate::{ + daemon::generate_hvc_mode, + ioctl_arg::{IOCTL_SYS, IOCTL_SYS_APPEND_MED_BLK}, + util::{check_cache_address, cstr_arr_to_string, string_to_cstr_arr, virt_to_phys_user}, +}; pub const HUGE_TLB_MAX: usize = 2 * 1024 * 1024; pub const BLOCK_SIZE: usize = 512; @@ -39,7 +52,10 @@ fn blk_read(blk_id: u16, lba: u64, mut count: u64) { let img_file = binding2.get(blk_id as usize).unwrap(); if count > blk_cfg.dma_block_max { - warn!("blk_read count {} > dma_block_max {}, shrink count to {}", count, blk_cfg.dma_block_max, blk_cfg.dma_block_max); + warn!( + "blk_read count {} > dma_block_max {}, shrink count to {}", + count, blk_cfg.dma_block_max, blk_cfg.dma_block_max + ); count = blk_cfg.dma_block_max; } @@ -53,7 +69,12 @@ fn blk_read(blk_id: u16, lba: u64, mut count: u64) { if read_len < 0 { warn!("read lba {:#x} size {:#x} failed!", lba, count * BLOCK_SIZE as u64); } else if read_len != (count as isize * BLOCK_SIZE as isize) { - warn!("read lba {:#x} size {:#x} failed! read_len = {:#x}", lba, count * BLOCK_SIZE as u64, read_len); + warn!( + "read lba {:#x} size {:#x} failed! read_len = {:#x}", + lba, + count * BLOCK_SIZE as u64, + read_len + ); } } } @@ -65,17 +86,30 @@ fn blk_write(blk_id: u16, lba: u64, mut count: u64) { let img_file = binding2.get(blk_id as usize).unwrap(); if count > blk_cfg.dma_block_max { - warn!("blk_write count {} > dma_block_max {}, shrink count to {}", count, blk_cfg.dma_block_max, blk_cfg.dma_block_max); + warn!( + "blk_write count {} > dma_block_max {}, shrink count to {}", + count, blk_cfg.dma_block_max, blk_cfg.dma_block_max + ); count = blk_cfg.dma_block_max; } unsafe { - let write_len = libc::pwrite(*img_file, blk_cfg.cache_va as *const c_void, count as usize * BLOCK_SIZE, lba as i64 * BLOCK_SIZE as i64); + let write_len = libc::pwrite( + *img_file, + blk_cfg.cache_va as *const c_void, + count as usize * BLOCK_SIZE, + lba as i64 * BLOCK_SIZE as i64, + ); if write_len < 0 { warn!("write lba {:#x} size {:#x} failed!", lba, count * BLOCK_SIZE as u64); } else if write_len != (count as isize * BLOCK_SIZE as isize) { - warn!("write lba {:#x} size {:#x} failed! write_len = {:#x}", lba, count * BLOCK_SIZE as u64, write_len); + warn!( + "write lba {:#x} size {:#x} failed! write_len = {:#x}", + lba, + count * BLOCK_SIZE as u64, + write_len + ); } } } @@ -89,7 +123,11 @@ fn blk_try_rw(blk_id: u16) -> Result<(), String> { // Read origin data, and save it in origin_data blk_read(blk_id, 0, 1); unsafe { - origin_data.clone_from(from_raw_parts(blk.cache_va as *const u8, BLOCK_SIZE).try_into().unwrap()); + origin_data.clone_from( + from_raw_parts(blk.cache_va as *const u8, BLOCK_SIZE) + .try_into() + .unwrap(), + ); } let cache = blk.cache_va as *mut u8; @@ -169,12 +207,14 @@ pub fn mediated_blk_init() { for i in 0..med_blk_list.len() { let cache_size = med_blk_list[i].cache_size; let block_dev_path = med_blk_list[i].block_dev_path.clone(); - info!("Shyper daemon init blk {} with cache size {}", + info!( + "Shyper daemon init blk {} with cache size {}", cstr_arr_to_string(med_blk_list[i].name.as_slice()), cache_size ); - info!("Shyper daemon init blk {} va {:#x} with cache pa {:#x}", + info!( + "Shyper daemon init blk {} va {:#x} with cache pa {:#x}", cstr_arr_to_string(med_blk_list[i].name.as_slice()), med_blk_list[i].cache_va as u64, med_blk_list[i].cache_ipa as u64 @@ -183,7 +223,11 @@ pub fn mediated_blk_init() { unsafe { let fd = open(block_dev_path.as_ptr() as *const u8, O_RDWR | O_DIRECT); if fd < 0 { - warn!("open block device {} failed: errcode = {}", cstr_arr_to_string(block_dev_path.as_slice()), fd); + warn!( + "open block device {} failed: errcode = {}", + cstr_arr_to_string(block_dev_path.as_slice()), + fd + ); return; } img_file_fds[i] = fd; @@ -198,14 +242,22 @@ pub fn mediated_blk_init() { let request = generate_hvc_mode(IOCTL_SYS, IOCTL_SYS_APPEND_MED_BLK); unsafe { - if ioctl(*SHYPER_FD.get().unwrap(), request as u64, &med_blk_list[i] as *const MediatedBlkCfg as *mut c_void) != 0 { + if ioctl( + *SHYPER_FD.get().unwrap(), + request as u64, + &med_blk_list[i] as *const MediatedBlkCfg as *mut c_void, + ) != 0 + { warn!("ioctl append mediated blk failed"); return; } } img_file_fds = IMG_FILE_FDS.lock().unwrap(); - info!("Shyper daemon init blk {} success", cstr_arr_to_string(med_blk_list[i].name.clone().as_slice())); + info!( + "Shyper daemon init blk {} success", + cstr_arr_to_string(med_blk_list[i].name.clone().as_slice()) + ); } } @@ -213,9 +265,7 @@ pub fn mediated_blk_init() { pub fn mediated_blk_read(blk_id: u16, lba: u64, count: u64) { blk_read(blk_id, lba, count); - let ret = unsafe { - libc::ioctl(*SHYPER_FD.get().unwrap(), 0x0331, blk_id as c_uint) - }; + let ret = unsafe { libc::ioctl(*SHYPER_FD.get().unwrap(), 0x0331, blk_id as c_uint) }; if ret != 0 { warn!("Mediated blk read ioctl failed"); } @@ -224,9 +274,7 @@ pub fn mediated_blk_read(blk_id: u16, lba: u64, count: u64) { pub fn mediated_blk_write(blk_id: u16, lba: u64, count: u64) { blk_write(blk_id, lba, count); - let ret = unsafe { - libc::ioctl(*SHYPER_FD.get().unwrap(), 0x0331, blk_id as c_uint) - }; + let ret = unsafe { libc::ioctl(*SHYPER_FD.get().unwrap(), 0x0331, blk_id as c_uint) }; if ret != 0 { warn!("Mediated blk read ioctl failed"); } @@ -238,11 +286,15 @@ pub fn mediated_blk_add(index: usize, dev: String) -> Result Result Result(deserializer: D) -> Result, D::Error> +fn deserialize_cfg_list<'de, D>(deserializer: D) -> Result, D::Error> where - D: Deserializer<'de> + D: Deserializer<'de>, { // 解析为serdes_json定义的可变Value,以应对Value是不同值的情况 let value: Value = Deserialize::deserialize(deserializer)?; @@ -244,15 +270,13 @@ where } Ok(vec) } - _ => { - Err(de::Error::custom("cfg_list is not array!")) - } + _ => Err(de::Error::custom("cfg_list is not array!")), } } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct VmPassthroughDeviceConfig { - pub passthrough_device_list: Vec + pub passthrough_device_list: Vec, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -339,9 +363,10 @@ pub fn config_vm_info(vm_cfg: VmConfigEntry, vm_id: u64, fd: i32) -> Result<(), ipa_start: region.ipa_start, length: region.length, }; - ioctl_send_config(fd, fd_event, &mem_cfg_arg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_add_memory_region_config_arg"))?; + ioctl_send_config(fd, fd_event, &mem_cfg_arg as *const _ as *const c_void) + .map_err(|_| String::from("failed to send vm_add_memory_region_config_arg"))?; } - + // 3. Add VM memory color and budget information let has_color = vm_cfg.memory.colors.is_some(); let has_budget = vm_cfg.memory.budget.is_some(); @@ -349,11 +374,20 @@ pub fn config_vm_info(vm_cfg: VmConfigEntry, vm_id: u64, fd: i32) -> Result<(), let fd_event = generate_hvc_mode(HVC_CONFIG, HVC_CONFIG_MEMORY_COLOR_BUDGET); let cfg = VmMemoryColorBudgetConfigArg { vmid: vm_id, - color_num: if has_color { vm_cfg.memory.colors.as_ref().unwrap().len() as u64 } else { 0 }, - color_array_addr: if has_color { vm_cfg.memory.colors.unwrap().as_ptr() as *const u64 as u64 } else { 0 }, + color_num: if has_color { + vm_cfg.memory.colors.as_ref().unwrap().len() as u64 + } else { + 0 + }, + color_array_addr: if has_color { + vm_cfg.memory.colors.unwrap().as_ptr() as *const u64 as u64 + } else { + 0 + }, budget: if has_budget { vm_cfg.memory.budget.unwrap() } else { 0 }, }; - ioctl_send_config(fd, fd_event, &cfg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_memory_color_budget_config_arg"))?; + ioctl_send_config(fd, fd_event, &cfg as *const _ as *const c_void) + .map_err(|_| String::from("failed to send vm_memory_color_budget_config_arg"))?; } // 4. Set VM CPU config @@ -364,7 +398,8 @@ pub fn config_vm_info(vm_cfg: VmConfigEntry, vm_id: u64, fd: i32) -> Result<(), allocate_bitmap: vm_cfg.cpu.allocate_bitmap, master: vm_cfg.cpu.master, }; - ioctl_send_config(fd, fd_event, &cpu_cfg_arg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_cpu_config_arg"))?; + ioctl_send_config(fd, fd_event, &cpu_cfg_arg as *const _ as *const c_void) + .map_err(|_| String::from("failed to send vm_cpu_config_arg"))?; // 5. Add VM emulated device config let fd_event = generate_hvc_mode(HVC_CONFIG, HVC_CONFIG_EMULATED_DEVICE); @@ -380,7 +415,8 @@ pub fn config_vm_info(vm_cfg: VmConfigEntry, vm_id: u64, fd: i32) -> Result<(), cfg_list_length: device.cfg_list.len() as u64, emu_type: device.r#type as u64, }; - ioctl_send_config(fd, fd_event, &emu_cfg_arg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_emulated_device_config_arg"))?; + ioctl_send_config(fd, fd_event, &emu_cfg_arg as *const _ as *const c_void) + .map_err(|_| String::from("failed to send vm_emulated_device_config_arg"))?; } // record passthrough device irqs and stream ids @@ -396,7 +432,8 @@ pub fn config_vm_info(vm_cfg: VmConfigEntry, vm_id: u64, fd: i32) -> Result<(), base_pa: device.base_pa, length: device.length, }; - ioctl_send_config(fd, fd_event, &passthrough_cfg_arg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_passthrough_device_region_config_arg"))?; + ioctl_send_config(fd, fd_event, &passthrough_cfg_arg as *const _ as *const c_void) + .map_err(|_| String::from("failed to send vm_passthrough_device_region_config_arg"))?; passthrough_irqs.append(&mut device.irq_list.clone().into_iter().map(|x| x as u64).collect()); if device.smmu_id.is_some() { @@ -412,7 +449,8 @@ pub fn config_vm_info(vm_cfg: VmConfigEntry, vm_id: u64, fd: i32) -> Result<(), irqs_addr: passthrough_irqs.as_ptr() as *const u64 as u64, irqs_length: passthrough_irqs.len() as u64, }; - ioctl_send_config(fd, fd_event, &passthrough_irqs_cfg_arg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_passthrough_device_irqs_config_arg"))?; + ioctl_send_config(fd, fd_event, &passthrough_irqs_cfg_arg as *const _ as *const c_void) + .map_err(|_| String::from("failed to send vm_passthrough_device_irqs_config_arg"))?; } // 8. Add VM passthrough device streams ids @@ -423,7 +461,12 @@ pub fn config_vm_info(vm_cfg: VmConfigEntry, vm_id: u64, fd: i32) -> Result<(), streams_ids_addr: passthrough_stream_ids.as_ptr() as *const u64 as u64, streams_ids_length: passthrough_stream_ids.len() as u64, }; - ioctl_send_config(fd, fd_event, &passthrough_stream_ids_cfg_arg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_passthrough_device_streams_ids_config_arg"))?; + ioctl_send_config( + fd, + fd_event, + &passthrough_stream_ids_cfg_arg as *const _ as *const c_void, + ) + .map_err(|_| String::from("failed to send vm_passthrough_device_streams_ids_config_arg"))?; } // 9. Add VM dtb device config @@ -438,15 +481,24 @@ pub fn config_vm_info(vm_cfg: VmConfigEntry, vm_id: u64, fd: i32) -> Result<(), irq_list_length: dtb_device.irq_list.len() as u64, addr_region_ipa: dtb_device.addr_region_ipa, }; - ioctl_send_config(fd, fd_event, &dtb_cfg_arg as *const _ as *const c_void).map_err(|_| String::from("failed to send vm_dtb_device_config_arg"))?; + ioctl_send_config(fd, fd_event, &dtb_cfg_arg as *const _ as *const c_void) + .map_err(|_| String::from("failed to send vm_dtb_device_config_arg"))?; } // 10. Copy dtb_file and img_file to memory if !vm_cfg.image.device_tree_filename.is_empty() { - copy_device_tree_to_memory(vm_id, vm_cfg.image.device_tree_filename.clone(), fd as u32).map_err(|_| format!("failed to copy device tree {} to memory", vm_cfg.image.device_tree_filename))?; + copy_device_tree_to_memory(vm_id, vm_cfg.image.device_tree_filename.clone(), fd as u32).map_err(|_| { + format!( + "failed to copy device tree {} to memory", + vm_cfg.image.device_tree_filename + ) + })?; } if !copy_img_file_to_memory(vm_id, vm_cfg.image.kernel_filename.clone(), fd as u32).is_ok() { - return Err(format!("failed to copy kernel image {} to memory", vm_cfg.image.kernel_filename)); + return Err(format!( + "failed to copy kernel image {} to memory", + vm_cfg.image.kernel_filename + )); } // 10. Store kernel image file name in kernel module. @@ -471,9 +523,7 @@ pub fn config_add_vm(config_json: String) -> Result { let vm_cfg_2 = vm_cfg.clone(); println!("Parse VM config successfully, VM name [{}]", vm_cfg.name); - let fd = unsafe { - open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR) - }; + let fd = unsafe { open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR) }; if fd < 0 { return Err(format!("Failed to open /dev/shyper: {}", fd)); @@ -493,7 +543,8 @@ pub fn config_add_vm(config_json: String) -> Result { vm_id_addr: &vm_id as *const u64 as u64, }; let fd_event = generate_hvc_mode(HVC_CONFIG, HVC_CONFIG_ADD_VM); - ioctl_send_config(fd, fd_event, &vm_add_req as *const _ as *const c_void).map_err(|_| String::from("config_add_vm failed"))?; + ioctl_send_config(fd, fd_event, &vm_add_req as *const _ as *const c_void) + .map_err(|_| String::from("config_add_vm failed"))?; info!("Send VM [{}] config successfully", vm_id); // deal with failure condition @@ -522,7 +573,14 @@ fn copy_file_to_hypervisor(vmid: u64, filename: String, shyper_fd: u32, upload_m unsafe { file_fd = open(filename.as_ptr() as *const u8, O_RDONLY); - cache_va = mmap(0 as *mut c_void, CACHE_MAX, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 0, 0); + cache_va = mmap( + 0 as *mut c_void, + CACHE_MAX, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, + 0, + 0, + ); if cache_va == MAP_FAILED { close(file_fd); return Err(String::from("Allocate cache memory error!")); @@ -539,16 +597,14 @@ fn copy_file_to_hypervisor(vmid: u64, filename: String, shyper_fd: u32, upload_m return Err(err); } - let cache_ipa = virt_to_phys_user(process::id(), cache_va as u64).map_err( - |err| { - warn!("Failed to get cache pa\n"); - unsafe { - close(file_fd); - munmap(cache_va, CACHE_MAX); - } - err + let cache_ipa = virt_to_phys_user(process::id(), cache_va as u64).map_err(|err| { + warn!("Failed to get cache pa\n"); + unsafe { + close(file_fd); + munmap(cache_va, CACHE_MAX); } - )?; + err + })?; while copied_size < file_size { // set read start is previous read @@ -567,8 +623,10 @@ fn copy_file_to_hypervisor(vmid: u64, filename: String, shyper_fd: u32, upload_m }; if libc::read(file_fd, cache_va, coping_size as usize) == -1 { - warn!("read kernel image {} pos {} size {} failed\n", filename, copied_size, - coping_size); + warn!( + "read kernel image {} pos {} size {} failed\n", + filename, copied_size, coping_size + ); close(file_fd); munmap(cache_va, CACHE_MAX); return Err(format!("read file {} err", filename)); @@ -609,7 +667,3 @@ pub fn copy_img_file_to_memory(vmid: u64, filename: String, shyper_fd: u32) -> R pub fn copy_device_tree_to_memory(vmid: u64, filename: String, shyper_fd: u32) -> Result<(), String> { copy_file_to_hypervisor(vmid, filename, shyper_fd, HVC_CONFIG_UPLOAD_DEVICE_TREE) } - - - - diff --git a/cli/src/config_arg.rs b/cli/src/config_arg.rs index 5d02c91..4de1610 100644 --- a/cli/src/config_arg.rs +++ b/cli/src/config_arg.rs @@ -1,4 +1,3 @@ - #[repr(C)] pub struct VmAddConfigArg { pub vm_name_addr: u64, @@ -48,7 +47,6 @@ pub struct VmAddMemoryRegionConfigArg { pub length: u64, } - #[repr(C)] pub struct VmAddPassthroughDeviceRegionConfigArg { pub vmid: u64, diff --git a/cli/src/daemon.rs b/cli/src/daemon.rs index 7eb4f5a..17ced4c 100644 --- a/cli/src/daemon.rs +++ b/cli/src/daemon.rs @@ -1,14 +1,24 @@ -use std::{fs::{self, File}, io::Read, mem, process}; +use std::{ + fs::{self, File}, + io::Read, + mem, process, +}; use libc::{c_char, c_int, c_ulong, c_ulonglong, close, ioctl, open, uintptr_t, O_RDWR, SIGTERM, SIGUSR1}; use serde::{Serialize, Deserialize}; use log::{debug, error, info, warn}; use signal_hook::iterator::Signals; -use crate::{blk::{mediated_blk_add, mediated_blk_init, mediated_blk_read, mediated_blk_write, MediatedBlkCfg, MED_BLK_LIST}, config::copy_img_file_to_memory, ioctl_arg::{IOCTL_SYS, IOCTL_SYS_GET_KERNEL_IMG_NAME}, util::cstr_arr_to_string, vmm::vmm_boot}; +use crate::{ + blk::{mediated_blk_add, mediated_blk_init, mediated_blk_read, mediated_blk_write, MediatedBlkCfg, MED_BLK_LIST}, + config::copy_img_file_to_memory, + ioctl_arg::{IOCTL_SYS, IOCTL_SYS_GET_KERNEL_IMG_NAME}, + util::cstr_arr_to_string, + vmm::vmm_boot, +}; #[derive(Serialize, Deserialize, Debug)] struct DaemonConfig { - mediated: Vec + mediated: Vec, } #[repr(C)] @@ -133,68 +143,65 @@ fn sig_handle_event(signal: i32) { } match hvc_type.hvc_fid as usize { - HVC_MEDIATED => { - match hvc_type.hvc_event as usize { - HVC_MEDIATED_USER_NOTIFY => { - let blk_arg; - unsafe { - blk_arg = mem::transmute::<[u8; BLK_ARG_SIZE], BlkArg>(buf[0..BLK_ARG_SIZE].try_into().unwrap()); - } - if blk_arg.r#type == 0 { - mediated_blk_read(blk_arg.blk_id, blk_arg.sector, blk_arg.count); - } else if blk_arg.r#type == 1 { - mediated_blk_write(blk_arg.blk_id, blk_arg.sector, blk_arg.count); - } else { - warn!("[sig_handle_event] unknown blk req type {}", blk_arg.r#type); - } - return; + HVC_MEDIATED => match hvc_type.hvc_event as usize { + HVC_MEDIATED_USER_NOTIFY => { + let blk_arg; + unsafe { + blk_arg = mem::transmute::<[u8; BLK_ARG_SIZE], BlkArg>(buf[0..BLK_ARG_SIZE].try_into().unwrap()); } - _ => return + if blk_arg.r#type == 0 { + mediated_blk_read(blk_arg.blk_id, blk_arg.sector, blk_arg.count); + } else if blk_arg.r#type == 1 { + mediated_blk_write(blk_arg.blk_id, blk_arg.sector, blk_arg.count); + } else { + warn!("[sig_handle_event] unknown blk req type {}", blk_arg.r#type); + } + return; } - } - HVC_CONFIG => { - match hvc_type.hvc_event as usize { - HVC_CONFIG_UPLOAD_KERNEL_IMAGE => { - let cfg_arg; - unsafe { - cfg_arg = mem::transmute::<[u8; CONFIG_ARG_SIZE], CfgArg>(buf[0..CONFIG_ARG_SIZE].try_into().unwrap()); - } - let fd_event = generate_hvc_mode(IOCTL_SYS, IOCTL_SYS_GET_KERNEL_IMG_NAME); - - #[repr(C)] - struct NameArg { - vm_id: u64, - name_addr: *mut c_char, - } + _ => return, + }, + HVC_CONFIG => match hvc_type.hvc_event as usize { + HVC_CONFIG_UPLOAD_KERNEL_IMAGE => { + let cfg_arg; + unsafe { + cfg_arg = + mem::transmute::<[u8; CONFIG_ARG_SIZE], CfgArg>(buf[0..CONFIG_ARG_SIZE].try_into().unwrap()); + } + let fd_event = generate_hvc_mode(IOCTL_SYS, IOCTL_SYS_GET_KERNEL_IMG_NAME); + + #[repr(C)] + struct NameArg { + vm_id: u64, + name_addr: *mut c_char, + } - let filename: [u8; 64] = [0; 64]; - let mut name_arg: NameArg = NameArg { - vm_id: cfg_arg.vm_id, - name_addr: filename.as_ptr() as *mut c_char - }; + let filename: [u8; 64] = [0; 64]; + let mut name_arg: NameArg = NameArg { + vm_id: cfg_arg.vm_id, + name_addr: filename.as_ptr() as *mut c_char, + }; - unsafe { - let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); - if ioctl(fd, fd_event as c_ulonglong, &mut name_arg as *mut NameArg as uintptr_t) != 0 { - warn!("sig_handle_event: failed to get VM[{}] name\n", cfg_arg.vm_id); - close(fd); - return; - } - - let img_name = cstr_arr_to_string(filename.as_slice()); - if let Err(err) = copy_img_file_to_memory(cfg_arg.vm_id, img_name, fd as u32) { - warn!("sig_handle_event: failed to copy img file to memory: {}", err); - return; - } - vmm_boot(cfg_arg.vm_id as u32); + unsafe { + let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); + if ioctl(fd, fd_event as c_ulonglong, &mut name_arg as *mut NameArg as uintptr_t) != 0 { + warn!("sig_handle_event: failed to get VM[{}] name\n", cfg_arg.vm_id); close(fd); return; } + + let img_name = cstr_arr_to_string(filename.as_slice()); + if let Err(err) = copy_img_file_to_memory(cfg_arg.vm_id, img_name, fd as u32) { + warn!("sig_handle_event: failed to copy img file to memory: {}", err); + return; + } + vmm_boot(cfg_arg.vm_id as u32); + close(fd); + return; } - _ => return } - } - _ => return + _ => return, + }, + _ => return, } } @@ -224,14 +231,14 @@ pub fn init_daemon() { } close(fd); } - + // Init mediated block partition if vmid == 0 { info!("VM[{}] start to init blk service\n", vmid); mediated_blk_init(); } info!("VM[{}] daemon process init success\n", vmid); - + // TODO: The signal processing at this time is not real-time, but the signal is first written to the channel in the custom handler set in signal_hook, // and then captured in the user mode polling // Consider using signal_hook's real-time signal processing to enhance real-time performance @@ -243,12 +250,9 @@ pub fn init_daemon() { pub fn config_daemon(path: String) -> Result<(), String> { info!("Start Shyper-cli daemon configure"); - let json_str = fs::read_to_string(path.clone()).map_err( - |err| format!("Open json file {} err: {}", path.clone(), err) - )?; - let config: DaemonConfig = serde_json::from_str(&json_str).map_err( - |err| format!("Parse json err: {}", err) - )?; + let json_str = + fs::read_to_string(path.clone()).map_err(|err| format!("Open json file {} err: {}", path.clone(), err))?; + let config: DaemonConfig = serde_json::from_str(&json_str).map_err(|err| format!("Parse json err: {}", err))?; debug!("config is {:?}", config); let mut disk_cnt = 0; @@ -268,4 +272,4 @@ pub fn config_daemon(path: String) -> Result<(), String> { info!("daemon configure {} mediated disk(s)", disk_cnt); info!("daemon configuration finished"); Ok(()) -} \ No newline at end of file +} diff --git a/cli/src/main.rs b/cli/src/main.rs index 387ff78..b904166 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,11 +1,11 @@ -mod sys; -mod util; -mod daemon; -mod ioctl_arg; -mod vmm; mod blk; mod config; mod config_arg; +mod daemon; +mod ioctl_arg; +mod sys; +mod util; +mod vmm; use std::path::Path; @@ -19,7 +19,12 @@ use vmm::{vmm_boot, vmm_getvmid, vmm_list_vm_info, vmm_reboot, vmm_remove}; use crate::config::config_add_vm; #[derive(Parser)] -#[command(version, author, about, long_about = "CommandLine Interface for Shyper/Rust-Shyper Hypervisor")] +#[command( + version, + author, + about, + long_about = "CommandLine Interface for Shyper/Rust-Shyper Hypervisor" +)] struct CLI { #[command(subcommand)] subcmd: CLISubCmd, @@ -35,7 +40,7 @@ enum CLISubCmd { Vm { #[command(subcommand)] subcmd: VmSubCmd, - } + }, } #[derive(Subcommand)] @@ -54,22 +59,18 @@ enum SystemSubCmd { /// new hypervisor image image: String, }, - Test { - - }, + Test {}, Daemon { /// daemon config, specifically mediated disk config #[arg(default_value = "cli-config.json")] - config: String - } + config: String, + }, } #[derive(Subcommand)] enum VmSubCmd { /// list the info of the vm - List { - - }, + List {}, Boot { vmid: u32, /// Choose display method, currently only supported SDL2. @@ -99,16 +100,14 @@ enum VmSubCmd { #[arg(short, long)] force: bool, }, - Getvmid { - - } + Getvmid {}, } fn parse_file(file: &str) -> Result { if file.is_empty() { return Err(String::from("CONFIG can't be empty!")); } - + let file_path = Path::new(file); if !file_path.exists() { // judge whether the file exists or not @@ -120,7 +119,7 @@ fn parse_file(file: &str) -> Result { #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] enum DisplayMode { - SDL + SDL, } fn exec_system_cmd(subcmd: SystemSubCmd) { @@ -128,17 +127,17 @@ fn exec_system_cmd(subcmd: SystemSubCmd) { SystemSubCmd::Reboot { force } => sys_reboot(force), SystemSubCmd::Shutdown { force } => sys_shutdown(force), SystemSubCmd::Update { image } => sys_update(image), - SystemSubCmd::Test { } => sys_test(), + SystemSubCmd::Test {} => sys_test(), SystemSubCmd::Daemon { config } => { config_daemon(config).unwrap(); init_daemon(); - }, + } } } fn exec_vm_cmd(subcmd: VmSubCmd) { match subcmd { - VmSubCmd::List { } => vmm_list_vm_info(), + VmSubCmd::List {} => vmm_list_vm_info(), VmSubCmd::Boot { vmid, display } => vmm_boot(vmid), VmSubCmd::Reboot { vmid, force } => vmm_reboot(force, vmid), VmSubCmd::Remove { vmid } => vmm_remove(vmid), @@ -147,25 +146,23 @@ fn exec_vm_cmd(subcmd: VmSubCmd) { if let Err(err) = config_add_vm(config) { error!("Add vm failed: {}", err); } - }, + } VmSubCmd::Delconfig { vmid, force } => todo!(), - VmSubCmd::Getvmid { } => vmm_getvmid(), + VmSubCmd::Getvmid {} => vmm_getvmid(), } } fn main() { // configure logger and set log level - env_logger::Builder::new() - .filter_level(log::LevelFilter::Info) - .init(); + env_logger::Builder::new().filter_level(log::LevelFilter::Info).init(); let cli = CLI::parse(); match cli.subcmd { CLISubCmd::System { subcmd } => { exec_system_cmd(subcmd); - }, + } CLISubCmd::Vm { subcmd } => { exec_vm_cmd(subcmd); - }, + } } } diff --git a/cli/src/sys.rs b/cli/src/sys.rs index e2fb5b4..6817bbd 100644 --- a/cli/src/sys.rs +++ b/cli/src/sys.rs @@ -1,8 +1,11 @@ -use libc::{c_void, close, ioctl, lseek, open, read, size_t, MAP_LOCKED, MAP_SHARED, O_RDONLY, O_RDWR, PROT_READ, PROT_WRITE, SEEK_SET}; +use libc::{ + c_void, close, ioctl, lseek, open, read, size_t, MAP_LOCKED, MAP_SHARED, O_RDONLY, O_RDWR, PROT_READ, PROT_WRITE, + SEEK_SET, +}; use crate::util::{bool_to_cint, file_size}; -pub fn sys_reboot(force: bool) { +pub fn sys_reboot(force: bool) { unsafe { let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); println!("sys reboot fd {}", fd); @@ -13,7 +16,7 @@ pub fn sys_reboot(force: bool) { } } -pub fn sys_shutdown(force: bool) { +pub fn sys_shutdown(force: bool) { unsafe { let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); println!("sys shutdown fd {}", fd); @@ -24,7 +27,7 @@ pub fn sys_shutdown(force: bool) { } } -pub fn sys_test() { +pub fn sys_test() { unsafe { let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); println!("sys test fd {}", fd); @@ -43,7 +46,14 @@ fn update_image(path: String) -> u64 { unsafe { let share_mem_fd = open("/dev/hyper_update".as_ptr() as *const u8, O_RDWR); - let addr = libc::mmap(0 as *mut c_void, 0x8000000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, share_mem_fd, 0); + let addr = libc::mmap( + 0 as *mut c_void, + 0x8000000, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_LOCKED, + share_mem_fd, + 0, + ); if addr.is_null() { println!("[sys_update] update image mmap failed"); return size; @@ -73,4 +83,4 @@ pub fn sys_update(path: String) { } close(fd); } -} \ No newline at end of file +} diff --git a/cli/src/util.rs b/cli/src/util.rs index 1c7a14e..5c25982 100644 --- a/cli/src/util.rs +++ b/cli/src/util.rs @@ -1,4 +1,10 @@ -use std::{ffi::CString, fs::File, io::{Read, Seek}, mem, slice::from_raw_parts_mut}; +use std::{ + ffi::CString, + fs::File, + io::{Read, Seek}, + mem, + slice::from_raw_parts_mut, +}; use libc::{c_int, c_uchar, c_ulong, c_void, sysconf, _SC_PAGE_SIZE}; @@ -32,9 +38,7 @@ pub fn cstr_arr_to_string(buf: &[u8]) -> String { vec.push(*i); } } - unsafe { - CString::from_vec_unchecked(vec).to_string_lossy().into() - } + unsafe { CString::from_vec_unchecked(vec).to_string_lossy().into() } } pub fn string_to_cstr_arr(s: String) -> [u8; 32] { @@ -50,29 +54,31 @@ pub fn string_to_cstr_arr(s: String) -> [u8; 32] { // returns paddr pub fn virt_to_phys_user(pid: u32, vaddr: u64) -> Result { let pagemap_path = format!("/proc/{}/pagemap", pid); - let mut file = File::open(&pagemap_path).map_err( - |err| format!("Open {} err: {}", &pagemap_path, err) - )?; + let mut file = File::open(&pagemap_path).map_err(|err| format!("Open {} err: {}", &pagemap_path, err))?; let page_size: usize = unsafe { sysconf(_SC_PAGE_SIZE) } as usize; let offset = ((vaddr as usize) / page_size) * (mem::size_of::() as usize); if file.seek(std::io::SeekFrom::Start(offset as u64)).is_err() { - return Err(format!("File {} is not big enough to access offset {}", pagemap_path, offset)); + return Err(format!( + "File {} is not big enough to access offset {}", + pagemap_path, offset + )); } let mut pagemap_entry: c_ulong = 0; - if file.read_exact(unsafe { - from_raw_parts_mut( - &mut pagemap_entry as *mut _ as *mut u8, - mem::size_of::() - ) - }).is_err() { + if file + .read_exact(unsafe { from_raw_parts_mut(&mut pagemap_entry as *mut _ as *mut u8, mem::size_of::()) }) + .is_err() + { return Err(format!("Read page table entry err")); } if (pagemap_entry & (1 << 63)) == 0 { - return Err(format!("Virtual Address 0x{:#x} converts to paddr err: page not in memory", vaddr)); + return Err(format!( + "Virtual Address 0x{:#x} converts to paddr err: page not in memory", + vaddr + )); } // Note: 以下注释是pagemap_entry每一位的意义 @@ -110,18 +116,18 @@ pub fn string_to_u64(s: String) -> Result { if s.starts_with("0x") { match u64::from_str_radix(&s[2..], 16) { Ok(num) => Ok(num), - Err(_) => Err(format!("Not hex string: {}", s)) + Err(_) => Err(format!("Not hex string: {}", s)), } } else if s.starts_with("0b") { match u64::from_str_radix(&s[2..], 2) { Ok(num) => Ok(num), - Err(_) => Err(format!("Not binary string: {}", s)) + Err(_) => Err(format!("Not binary string: {}", s)), } } else { // must be decimal match u64::from_str_radix(&s, 10) { Ok(num) => Ok(num), - Err(_) => Err(format!("String {} is not in hex/bin/decimal format!", s)) + Err(_) => Err(format!("String {} is not in hex/bin/decimal format!", s)), } } } diff --git a/cli/src/vmm.rs b/cli/src/vmm.rs index 2d0e65c..350faae 100644 --- a/cli/src/vmm.rs +++ b/cli/src/vmm.rs @@ -32,7 +32,7 @@ struct VMInfoList { pub info_list: [VMInfo; VM_NUM_MAX], } -pub fn vmm_boot(vm_id: u32) { +pub fn vmm_boot(vm_id: u32) { unsafe { let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); if ioctl(fd, 0x0102, vm_id) != 0 { @@ -42,7 +42,7 @@ pub fn vmm_boot(vm_id: u32) { } } -pub fn vmm_shutdown(force: bool, vm_id: u32) { +pub fn vmm_shutdown(force: bool, vm_id: u32) { unsafe { let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); let arg: u64 = match force { @@ -56,7 +56,7 @@ pub fn vmm_shutdown(force: bool, vm_id: u32) { } } -pub fn vmm_reboot(force: bool, vm_id: u32) { +pub fn vmm_reboot(force: bool, vm_id: u32) { unsafe { let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); let arg: u64 = match force { @@ -70,7 +70,7 @@ pub fn vmm_reboot(force: bool, vm_id: u32) { } } -pub fn vmm_remove(vm_id: u32) { +pub fn vmm_remove(vm_id: u32) { unsafe { let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); if ioctl(fd, 0x0110, vm_id) != 0 { @@ -80,7 +80,7 @@ pub fn vmm_remove(vm_id: u32) { } } -pub fn vmm_getvmid() { +pub fn vmm_getvmid() { unsafe { let fd = open("/dev/shyper\0".as_ptr() as *const u8, O_RDWR); let mut id: u32 = 0; @@ -104,7 +104,8 @@ pub fn vmm_list_vm_info() { close(fd); return; } else { - vm_info_list = mem::transmute::<[u8; VM_INFO_LIST_SIZE], VMInfoList>(buf[0..VM_INFO_LIST_SIZE].try_into().unwrap()); + vm_info_list = + mem::transmute::<[u8; VM_INFO_LIST_SIZE], VMInfoList>(buf[0..VM_INFO_LIST_SIZE].try_into().unwrap()); } } display_vm_list_info(vm_info_list); @@ -115,7 +116,12 @@ fn display_vm_list_info(vm_info_list: VMInfoList) { for i in 0..vm_num { let info = &vm_info_list.info_list[i]; println!("----------vm[{}]----------", i); - println!("vm id [{}] name: {} type: {}\n", info.id, cstr_arr_to_string(info.vm_name.as_slice()), info.vm_type); + println!( + "vm id [{}] name: {} type: {}\n", + info.id, + cstr_arr_to_string(info.vm_name.as_slice()), + info.vm_type + ); match info.vm_type { VM_T_LINUX => { println!("vm type: Linux"); -- Gitee