From 162592beaab7b822f4e932d3cfb32472dc255ff3 Mon Sep 17 00:00:00 2001 From: Berkus Decker Date: Sat, 22 Jan 2022 23:00:03 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9C=A8=20Add=20chainofcommand?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Works fine with a caveat that you need to press ENTER after Ctrl+C, investigate. Multiplex QEMU output for serial access. --- .github/workflows/build.yml | 16 +- Cargo.lock | 666 ++++++++++++++++++++++++++++++- Cargo.toml | 6 +- Justfile | 5 + Makefile.toml | 3 +- bin/chainboot/Makefile.toml | 2 +- bin/chainofcommand/Cargo.toml | 25 ++ bin/chainofcommand/Makefile.toml | 44 ++ bin/chainofcommand/src/main.rs | 436 ++++++++++++++++++++ 9 files changed, 1184 insertions(+), 19 deletions(-) create mode 100644 bin/chainofcommand/Cargo.toml create mode 100644 bin/chainofcommand/Makefile.toml create mode 100644 bin/chainofcommand/src/main.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index adcfc56..3fb083e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,6 +45,17 @@ jobs: - name: "Install build tools" run: cargo install cargo-make cargo-binutils + - name: "Prepare packages (Linux)" + run: | + sudo apt install software-properties-common + sudo add-apt-repository ppa:jacob/virtualisation + sudo apt update + if: runner.os == 'Linux' + + - name: "Install dev libraries (Linux)" + run: sudo apt install libudev-dev + if: runner.os == 'Linux' + - name: "Validate rust-lld" run: | which rust-lld || echo "Not found" @@ -63,9 +74,6 @@ jobs: - name: Install QEMU (Linux) run: | - sudo apt install software-properties-common - sudo add-apt-repository ppa:jacob/virtualisation - sudo apt update sudo apt install qemu-system-aarch64 if: runner.os == 'Linux' @@ -135,6 +143,8 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v1 + - run: sudo apt update + - run: sudo apt install libudev-dev - run: rustup toolchain install nightly - run: cargo install cargo-make - run: env CLIPPY_FEATURES=${{ matrix.features }} cargo make clippy diff --git a/Cargo.lock b/Cargo.lock index 3982c1c..ce17f6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,68 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "CoreFoundation-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0e9889e6db118d49d88d84728d0e964d973a5680befb5f85f55141beea5c20b" +dependencies = [ + "libc", + "mach 0.1.2", +] + +[[package]] +name = "IOKit-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99696c398cbaf669d2368076bdb3d627fb0ce51a26899d7c61228c5c0af3bf4a" +dependencies = [ + "CoreFoundation-sys", + "libc", + "mach 0.1.2", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + [[package]] name = "bit_field" version = "0.10.1" @@ -14,6 +76,18 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + [[package]] name = "cfg-if" version = "1.0.0" @@ -38,20 +112,193 @@ dependencies = [ ] [[package]] -name = "cortex-a" -version = "7.0.0" +name = "chainofcommand" +version = "0.0.1" +dependencies = [ + "anyhow", + "bytes", + "clap", + "crossterm", + "defer", + "fehler", + "futures", + "seahash", + "tokio", + "tokio-serial", + "tokio-util", +] + +[[package]] +name = "clap" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b27f5d071b671f9799dfdf0c0afcec11de65195383d36e186639c83b00705e4f" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "cortex-a" +version = "7.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd95fd055d118f77d4e4d527201b6ceccd13586b19b4dac1270f7081fef0f98" dependencies = [ "tock-registers", ] +[[package]] +name = "crossterm" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85525306c4291d1b73ce93c8acf9c339f9b213aef6c1d85c3830cbf1c16325c" +dependencies = [ + "bitflags", + "crossterm_winapi", + "futures-core", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" +dependencies = [ + "winapi", +] + +[[package]] +name = "defer" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "647605a6345d5e89c3950a36a638c56478af9b414c55c6f2477c73b115f9acde" + [[package]] name = "doc-comment" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "fehler" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5729fe49ba028cd550747b6e62cd3d841beccab5390aa398538c31a2d983635" +dependencies = [ + "fehler-macros", +] + +[[package]] +name = "fehler-macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb5acb1045ebbfa222e2c50679e392a71dd77030b78fb0189f2d9c5974400f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" + +[[package]] +name = "futures-executor" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2" + +[[package]] +name = "futures-macro" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508" + +[[package]] +name = "futures-task" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72" + +[[package]] +name = "futures-util" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "heck" version = "0.3.3" @@ -61,6 +308,66 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "libc" +version = "0.2.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eef78b64d87775463c549fbd80e19249ef436ea3bf1de2a1eb7e717ec7fab1e9" + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "mach" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd13ee2dd61cc82833ba05ade5a30bb3d63f7ced605ef827063c63078302de9" +dependencies = [ + "libc", +] + +[[package]] +name = "mach" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" +dependencies = [ + "libc", +] + [[package]] name = "machine" version = "0.0.1" @@ -77,6 +384,91 @@ dependencies = [ "ux", ] +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "mio-serial" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f47dcdec193b77e2166faa0e3be4ee04f324ce6780c54efe7af6c3ee7360e4" +dependencies = [ + "log", + "mio", + "nix 0.22.3", + "serialport", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "nix" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nix" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + [[package]] name = "nucleus" version = "0.0.1" @@ -94,25 +486,78 @@ dependencies = [ ] [[package]] -name = "proc-macro2" -version = "1.0.27" +name = "num_cpus" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" dependencies = [ "unicode-xid", ] [[package]] name = "qemu-exit" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0fa04276d522a40ed717bf874183a3b2a8bbb3fb4c646b03a7eb874ce5d543" +checksum = "9ff023245bfcc73fb890e1f8d5383825b3131cc920020a5c487d6f113dfc428a" [[package]] name = "quote" -version = "1.0.9" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" dependencies = [ "proc-macro2", ] @@ -123,12 +568,101 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "seahash" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "serialport" +version = "4.0.1" +source = "git+https://github.com/metta-systems/serialport-rs?branch=macos-ENOTTY-fix#91ee1f740ee7d7c9506df8af082860a478656e18" +dependencies = [ + "CoreFoundation-sys", + "IOKit-sys", + "bitflags", + "cfg-if", + "mach 0.2.3", + "nix 0.23.1", + "regex", + "winapi", +] + +[[package]] +name = "signal-hook" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fd5867f1c4f2c5be079aee7a2adf1152ebb04a4bc4d341f504b7dece607ed4" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + [[package]] name = "snafu" version = "0.7.0" @@ -152,28 +686,106 @@ dependencies = [ ] [[package]] -name = "syn" -version = "1.0.73" +name = "strsim" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "tock-registers" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee8fba06c1f4d0b396ef61a54530bb6b28f0dc61c38bc8bc5a5a48161e6282e" +[[package]] +name = "tokio" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-serial" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75b04fbb029a3f77272b3356453c97c5dbdebcf28aa248a34fd80a442f2dda1" +dependencies = [ + "cfg-if", + "futures", + "log", + "mio-serial", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + [[package]] name = "unicode-segmentation" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + [[package]] name = "unicode-xid" version = "0.2.2" @@ -191,3 +803,31 @@ name = "ux" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 3be9524..b3cb1ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,13 @@ [workspace] members = [ "nucleus", - "bin/chainboot" + "bin/chainboot", + "bin/chainofcommand" ] +[patch.crates-io] +serialport = { git = "https://github.com/metta-systems/serialport-rs", branch = "macos-ENOTTY-fix" } + [profile.dev] # See https://github.com/rust-lang/cargo/issues/7359 about why panic=abort is not working here. # It is still defined in the target JSON so not stricly necessary to specify it here anyway. diff --git a/Justfile b/Justfile index cdd02b7..d101266 100644 --- a/Justfile +++ b/Justfile @@ -13,6 +13,11 @@ zellij-cb: cargo make zellij-cb zellij --layout-path emulation/layout.zellij +# Build chainofcommand serial loader +chainofcommand: + cd bin/chainofcommand + cargo make build + # Build and run kernel in QEMU qemu: cargo make qemu diff --git a/Makefile.toml b/Makefile.toml index 606904d..e9c416c 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -63,7 +63,7 @@ QEMU_CONTAINER_CMD = "qemu-system-aarch64" # QEMU has renamed the RasPi machines since version 6.2.0, use just `raspi3` for previous versions. QEMU_OPTS = "-M ${QEMU_MACHINE} -d int -semihosting" QEMU_DISASM_OPTS = "-d in_asm,unimp,int" -QEMU_SERIAL_OPTS = "-serial null -serial stdio" +QEMU_SERIAL_OPTS = "-serial pty -serial stdio" QEMU_TESTS_OPTS = "-nographic" # For gdb connection: # - if this is set, MUST have gdb attached for SYS_WRITE0 to work, otherwise QEMU will crash. @@ -99,6 +99,7 @@ args = ["build", "@@split(PLATFORM_TARGET, )", "--release"] dependencies = ["build-qemu", "kernel-binary"] env = { "TARGET_FEATURES" = "${QEMU_FEATURES}" } script = [ + "echo Run QEMU ${QEMU_OPTS} ${QEMU_RUNNER_OPTS} with ${KERNEL_BIN}", "${QEMU} ${QEMU_OPTS} ${QEMU_RUNNER_OPTS} -dtb ${TARGET_DTB} -kernel ${KERNEL_BIN}" ] diff --git a/bin/chainboot/Makefile.toml b/bin/chainboot/Makefile.toml index 7e7a4cf..845736f 100644 --- a/bin/chainboot/Makefile.toml +++ b/bin/chainboot/Makefile.toml @@ -24,7 +24,7 @@ run_task = "zellij-config" disabled = true [tasks.qemu-cb] -env = { "QEMU_RUNNER_OPTS" = "${QEMU_SERIAL_OPTS}", "KERNEL_BIN" = "${CHAINBOOT_BIN}", "TARGET_DTB" = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb" } +env = { "QEMU_RUNNER_OPTS" = "${QEMU_DISASM_OPTS} -serial pty", "KERNEL_BIN" = "${CHAINBOOT_BIN}", "TARGET_DTB" = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb" } extend = "qemu-runner" [tasks.gdb] diff --git a/bin/chainofcommand/Cargo.toml b/bin/chainofcommand/Cargo.toml new file mode 100644 index 0000000..16a8bb5 --- /dev/null +++ b/bin/chainofcommand/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "chainofcommand" +version = "0.0.1" +authors = ["Berkus Decker "] +description = "Host server for chainboot" +license = "BlueOak-1.0.0" +categories = ["no-std", "embedded", "os"] +publish = false +edition = "2021" + +[badges] +maintenance = { status = "experimental" } + +[dependencies] +clap = "2.33" +seahash = "4.1" +anyhow = "1.0" +fehler = "1.0" +crossterm = { version = "0.22", features = ["event-stream"] } +tokio-serial = "5.4" +tokio = { version = "1.15", features = ["full"] } +futures = "0.3" +defer = "0.1" +tokio-util = { version = "0.6", features = ["codec"] } +bytes = "1.1" diff --git a/bin/chainofcommand/Makefile.toml b/bin/chainofcommand/Makefile.toml new file mode 100644 index 0000000..2011b8f --- /dev/null +++ b/bin/chainofcommand/Makefile.toml @@ -0,0 +1,44 @@ +[tasks.build] +command = "cargo" +args = ["build"] + +[tasks.test] +command = "cargo" +args = ["test"] + +[tasks.clippy] +command = "cargo" +args = ["clippy", "--", "-D", "warnings"] + +[tasks.hopper] +disabled = true + +[tasks.kernel-binary] +disabled = true + +[tasks.zellij-nucleus] +disabled = true + +[tasks.zellij-cb] +disabled = true + +[tasks.zellij-cb-gdb] +disabled = true + +[tasks.qemu] +disabled = true + +[tasks.qemu-cb] +disabled = true + +[tasks.sdcard] +disabled = true + +[tasks.cb-eject] +disabled = true + +[tasks.gdb] +disabled = true + +[tasks.gdb-cb] +disabled = true diff --git a/bin/chainofcommand/src/main.rs b/bin/chainofcommand/src/main.rs new file mode 100644 index 0000000..5eb4718 --- /dev/null +++ b/bin/chainofcommand/src/main.rs @@ -0,0 +1,436 @@ +#![feature(trait_alias)] + +use { + anyhow::{anyhow, Result}, + bytes::Bytes, + clap::{App, AppSettings, Arg}, + crossterm::{ + cursor, + event::{Event, EventStream, KeyCode, KeyEvent, KeyModifiers}, + execute, style, terminal, + tty::IsTty, + }, + defer::defer, + futures::{future::FutureExt, StreamExt}, + seahash::SeaHasher, + std::{ + fs::File, + hash::Hasher, + io::{BufRead, BufReader}, + path::Path, + time::Duration, + }, + tokio::{io::AsyncReadExt, sync::mpsc}, + tokio_serial::{SerialPortBuilderExt, SerialStream}, +}; + +trait Writable = std::io::Write + Send; +trait ThePath = AsRef + std::fmt::Display + Clone + Sync + Send + 'static; + +async fn expect( + to_console2: &mpsc::Sender>, + from_serial: &mut mpsc::Receiver>, + m: &str, +) -> Result<()> { + if let Some(buf) = from_serial.recv().await { + if buf.len() == m.len() && String::from_utf8_lossy(buf.as_ref()) == m { + return Ok(()); + } + to_console2.send(buf).await?; + return Err(anyhow!("Failed to receive expected value")); + } + Err(anyhow!("Failed to receive expected value")) +} + +async fn load_kernel

(to_console2: &mpsc::Sender>, kernel: P) -> Result<(File, u64)> +where + P: ThePath, +{ + to_console2 + .send("[>>] Loading kernel image\n".into()) + .await?; + + let kernel_file = match std::fs::File::open(kernel.clone()) { + Ok(file) => file, + Err(_) => return Err(anyhow!("Couldn't open kernel file {}", kernel)), + }; + let kernel_size: u64 = kernel_file.metadata()?.len(); + + to_console2 + .send(format!("[>>] .. {} ({} bytes)\n", kernel, kernel_size).into()) + .await?; + + Ok((kernel_file, kernel_size)) +} + +async fn send_kernel

( + to_console2: &mpsc::Sender>, + to_serial: &mpsc::Sender>, + from_serial: &mut mpsc::Receiver>, + kernel: P, +) -> Result<()> +where + P: ThePath, +{ + let (kernel_file, kernel_size) = load_kernel(to_console2, kernel).await?; + + to_console2.send("[>>] Sending image size\n".into()).await?; + + to_serial.send(kernel_size.to_le_bytes().into()).await?; + + // Wait for OK response + expect(to_console2, from_serial, "OK").await?; + + to_console2 + .send("[>>] Sending kernel image\n".into()) + .await?; + + let mut hasher = SeaHasher::new(); + let mut reader = BufReader::with_capacity(1, kernel_file); + loop { + let length = { + let buf = reader.fill_buf()?; + to_serial.send(buf.into()).await?; + hasher.write(buf); + buf.len() + }; + if length == 0 { + break; + } + reader.consume(length); + } + let hashed_value: u64 = hasher.finish(); + + to_console2 + .send(format!("[>>] Sending image checksum {:x}\n", hashed_value).into()) + .await?; + + to_serial.send(hashed_value.to_le_bytes().into()).await?; + + expect(to_console2, from_serial, "OK").await?; + + Ok(()) +} + +// Async reading using Tokio: https://fasterthanli.me/articles/a-terminal-case-of-linux + +async fn serial_loop( + mut port: tokio_serial::SerialStream, + to_console: mpsc::Sender>, + mut from_console: mpsc::Receiver>, +) -> Result<()> { + let mut buf = [0; 256]; + loop { + tokio::select! { + // _ = poll_send => {}, + + Some(msg) = from_console.recv() => { + // debug!("serial write {} bytes", msg.len()); + tokio::io::AsyncWriteExt::write_all(&mut port, msg.as_ref()).await?; + } + + res = port.read(&mut buf) => { + match res { + Ok(0) => { + // info!("Serial "); + return Ok(()); + } + Ok(n) => { + // debug!("Serial read {n} bytes."); + to_console.send(buf[0..n].to_owned()).await?; + } + Err(e) => { + // if e.kind() == ErrorKind::TimedOut { + // execute!(w, style::Print("\r\nTimeout: the serial device has been unplugged!"))?; + // } else { + // execute!(w, style::Print(format!("\r\nSerial Error: {:?}\r", e)))?; + // } + // break; + return Err(anyhow!(e)); + } + } + } + } + } +} + +async fn console_loop

( + to_console2: mpsc::Sender>, + mut from_internal: mpsc::Receiver>, + to_serial: mpsc::Sender>, + mut from_serial: mpsc::Receiver>, + kernel: P, +) -> Result<()> +where + P: ThePath, +{ + let mut w = std::io::stdout(); + + let mut breaks = 0; + + let mut event_reader = EventStream::new(); + + loop { + tokio::select! { + biased; + + Some(received) = from_internal.recv() => { + for &x in &received[..] { + execute!(w, style::Print(format!("{}", x as char)))?; + } + w.flush()?; + } + + Some(received) = from_serial.recv() => { + // execute!(w, cursor::MoveToNextLine(1), style::Print(format!("[>>] Received {} bytes from serial", from_serial.len())), cursor::MoveToNextLine(1))?; + + for &x in &received[..] { + if x == 0x3 { + // execute!(w, cursor::MoveToNextLine(1), style::Print("[>>] Received a BREAK"), cursor::MoveToNextLine(1))?; + breaks += 1; + // Await for 3 consecutive \3 to start downloading + if breaks == 3 { + // execute!(w, cursor::MoveToNextLine(1), style::Print("[>>] Received 3 BREAKs"), cursor::MoveToNextLine(1))?; + breaks = 0; + send_kernel(&to_console2, &to_serial, &mut from_serial, kernel.clone()).await?; + to_console2.send("[>>] Send successful, pass-through\n".into()).await?; + } + } else { + while breaks > 0 { + execute!(w, style::Print(format!("{}", 3 as char)))?; + breaks -= 1; + } + execute!(w, style::Print(format!("{}", x as char)))?; + w.flush()?; + } + } + } + + maybe_event = event_reader.next().fuse() => { + match maybe_event { + Some(Ok(Event::Key(key_event))) => { + if key_event.code == KeyCode::Char('c') && key_event.modifiers == KeyModifiers::CONTROL { + return Ok(()); + } + if let Some(key) = handle_key_event(key_event) { + to_serial.send(key.to_vec()).await?; + // Local echo + execute!(w, style::Print(format!("{:?}", key)))?; + w.flush()?; + } + } + Some(Ok(_)) => {}, + Some(Err(e)) => { + execute!(w, style::Print(format!("Console read error: {:?}\r", e)))?; + w.flush()?; + }, + None => return Err(anyhow!("woops")), + } + } + } + } +} + +async fn main_loop

(port: SerialStream, kernel: P) -> Result<()> +where + P: ThePath, +{ + // read from serial -> to_console==>from_serial -> output to console + let (to_console, from_serial) = mpsc::channel(256); + let (to_console2, from_internal) = mpsc::channel(256); + + // read from console -> to_serial==>from_console -> output to serial + let (to_serial, from_console) = mpsc::channel(256); + + tokio::spawn(serial_loop(port, to_console.clone(), from_console)); + console_loop(to_console2, from_internal, to_serial, from_serial, kernel).await + + // TODO: framed + + // rx_device -> serial_reader -> app + // app -> serial_writer -> serial_consumer -> (poll_send to drive) -> serial_sink -> tx_device + // let (rx_device, tx_device) = split(port); + + // let mut serial_reader = FramedRead::new(rx_device, BytesCodec::new()); + // let serial_sink = FramedWrite::new(tx_device, BytesCodec::new()); + // + // let (serial_writer, serial_consumer) = mpsc::unbounded::(); + // let mut poll_send = serial_consumer.map(Ok).forward(serial_sink); +} + +// From remote_serial -- https://github.com/zhp-rs/remote_serial/ (Licensed under MIT License) +fn handle_key_event(key_event: KeyEvent) -> Option { + let mut buf = [0; 4]; + + let key_str: Option<&[u8]> = match key_event.code { + KeyCode::Backspace => Some(b"\x08"), + KeyCode::Enter => Some(b"\x0D"), + KeyCode::Left => Some(b"\x1b[D"), + KeyCode::Right => Some(b"\x1b[C"), + KeyCode::Home => Some(b"\x1b[H"), + KeyCode::End => Some(b"\x1b[F"), + KeyCode::Up => Some(b"\x1b[A"), + KeyCode::Down => Some(b"\x1b[B"), + KeyCode::Tab => Some(b"\x09"), + KeyCode::Delete => Some(b"\x1b[3~"), + KeyCode::Insert => Some(b"\x1b[2~"), + KeyCode::Esc => Some(b"\x1b"), + KeyCode::Char(ch) => { + if key_event.modifiers & KeyModifiers::CONTROL == KeyModifiers::CONTROL { + buf[0] = ch as u8; + if ('a'..='z').contains(&ch) || (ch == ' ') { + buf[0] &= 0x1f; + Some(&buf[0..1]) + } else if ('4'..='7').contains(&ch) { + // crossterm returns Control-4 thru 7 for \x1c thru \x1f + buf[0] = (buf[0] + 8) & 0x1f; + Some(&buf[0..1]) + } else { + Some(ch.encode_utf8(&mut buf).as_bytes()) + } + } else { + Some(ch.encode_utf8(&mut buf).as_bytes()) + } + } + _ => None, + }; + key_str.map(Bytes::copy_from_slice) +} + +// 1. connect to given serial port, e.g. /dev/ttyUSB23234 +// 2. Await for \3\3\3 start signal, meanwhile pass-through all traffic to console +// 3. send selected kernel binary with checksum to the target +// 4. go to 2 + +#[tokio::main] +async fn main() -> Result<()> { + let matches = App::new("ChainOfCommand - command chainboot protocol") + .about("Use to send freshly built kernel to chainboot-compatible boot loader") + .setting(AppSettings::DisableVersion) + .arg( + Arg::with_name("port") + .help("The device path to a serial port, e.g. /dev/ttyUSB0") + .required(true), + ) + .arg( + Arg::with_name("baud") + .help("The baud rate to connect at") + .use_delimiter(false) + .required(true), // .validator(valid_baud), + ) + .arg( + Arg::with_name("kernel") + .long("kernel") + .help("Path of the binary kernel image to send") + .takes_value(true) + .default_value("kernel8.img"), + ) + .get_matches(); + let port_name = matches.value_of("port").unwrap(); + let baud_rate = matches.value_of("baud").unwrap().parse::().unwrap(); + let kernel = matches.value_of("kernel").unwrap().to_owned(); + + // Check that STDIN is a proper tty + if !std::io::stdin().is_tty() { + panic!("Must have a TTY for stdin"); + } + + // Disable line buffering, local echo, etc. + terminal::enable_raw_mode()?; + defer(|| terminal::disable_raw_mode().unwrap_or(())); + + let mut serial_toggle = false; + let mut stdout = std::io::stdout(); + + execute!(stdout, cursor::SavePosition)?; + + loop { + execute!( + stdout, + cursor::RestorePosition, + style::Print("[>>] Opening serial port ") + )?; + + // tokio_serial::new() creates a builder with 8N1 setup without flow control by default. + let port = tokio_serial::new(port_name, baud_rate).open_native_async(); + if let Err(e) = port { + let cont = match e.kind { + tokio_serial::ErrorKind::NoDevice => true, + tokio_serial::ErrorKind::Io(e) + if e == std::io::ErrorKind::NotFound + || e == std::io::ErrorKind::PermissionDenied => + { + true + } + _ => false, + }; + if cont { + execute!( + stdout, + cursor::RestorePosition, + style::Print(format!( + "[>>] Waiting for serial port {}\r", + if serial_toggle { "# " } else { " #" } + )) + )?; + stdout.flush()?; + serial_toggle = !serial_toggle; + + if crossterm::event::poll(Duration::from_millis(1000))? { + if let Event::Key(KeyEvent { code, modifiers }) = crossterm::event::read()? { + if code == KeyCode::Char('c') && modifiers == KeyModifiers::CONTROL { + return Ok(()); + } + } + } + + continue; + } + return Err(e.into()); + } + + execute!( + stdout, + style::Print("\n[>>] Waiting for handshake, pass-through"), + )?; + stdout.flush()?; + + // Run in pass-through mode by default. + // Once we receive BREAK (0x3) three times, switch to kernel send mode and upload kernel, + // then switch back to pass-through mode. + + // Input from STDIN should pass through to serial + // Input from serial should pass through to STDOUT + + let port = port?; + + if let Err(e) = main_loop(port, kernel.clone()).await { + execute!(stdout, style::Print(format!("\nError: {:?}\n", e)))?; + stdout.flush()?; + + let cont = match e.downcast_ref::() { + Some(e) + if e.kind() == std::io::ErrorKind::NotFound + || e.kind() == std::io::ErrorKind::PermissionDenied => + { + true + } + _ => false, + } || matches!(e.downcast_ref::(), Some(e) if e.kind == tokio_serial::ErrorKind::NoDevice) + || matches!( + e.downcast_ref::>>(), + Some(_) + ); + + if !cont { + break; + } + } else { + // main_loop() returned Ok() we're good to finish + break; + } + execute!(stdout, cursor::SavePosition)?; + } + + Ok(()) +}