diff --git a/.gitignore b/.gitignore index e22f20ea5fc83161068d1001209ec688cd3515e1..98a81ec7c995a15209ccc3db7ec9eabb3aec0d92 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 0000000000000000000000000000000000000000..b6719135c436aeca325cdcaf7510b47432ed4571 --- /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 0000000000000000000000000000000000000000..f5a14ce0e6db09fdad7954f4bb10878423fdbb71 --- /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 0000000000000000000000000000000000000000..d06f65cbe896d4376b2c3d2750e56b17bc92e770 --- /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 0000000000000000000000000000000000000000..e2a8c7cdb798ede3231ecdfe7dd80da31e8f2c27 --- /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 0000000000000000000000000000000000000000..389796a621a4f7ae3e509c18d25ea692a151651f --- /dev/null +++ b/cli/src/blk.rs @@ -0,0 +1,346 @@ +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 0000000000000000000000000000000000000000..23da5281c4a8fa03ebd805216caa9b46b63814e0 --- /dev/null +++ b/cli/src/config.rs @@ -0,0 +1,669 @@ +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 0000000000000000000000000000000000000000..4de161024040c9c87f7aa5e0dc490d29baf3340d --- /dev/null +++ b/cli/src/config_arg.rs @@ -0,0 +1,96 @@ +#[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 0000000000000000000000000000000000000000..17ced4ce1b4ba8d2e0cc0053ace6fa3afcf09c7a --- /dev/null +++ b/cli/src/daemon.rs @@ -0,0 +1,275 @@ +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(()) +} diff --git a/cli/src/ioctl_arg.rs b/cli/src/ioctl_arg.rs new file mode 100644 index 0000000000000000000000000000000000000000..edfe086f758384db74a70b252558f5180abf595a --- /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 0000000000000000000000000000000000000000..b90416651f733c78f9ff418268a41d43db582fe9 --- /dev/null +++ b/cli/src/main.rs @@ -0,0 +1,168 @@ +mod blk; +mod config; +mod config_arg; +mod daemon; +mod ioctl_arg; +mod sys; +mod util; +mod vmm; + +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 0000000000000000000000000000000000000000..6817bbdc56948f0fc0830f8fdbe178318922f5c2 --- /dev/null +++ b/cli/src/sys.rs @@ -0,0 +1,86 @@ +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); + } +} diff --git a/cli/src/util.rs b/cli/src/util.rs new file mode 100644 index 0000000000000000000000000000000000000000..5c259822fefd1afcfe5b6dcfb85e27e2df1f16a4 --- /dev/null +++ b/cli/src/util.rs @@ -0,0 +1,133 @@ +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 0000000000000000000000000000000000000000..350faaef0e0c22f8402f12f4df23e22f0b35b6d2 --- /dev/null +++ b/cli/src/vmm.rs @@ -0,0 +1,155 @@ +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"); + } + } + } +}