feat: Add chainofcommand

Works fine with a caveat that you need
to press ENTER after Ctrl+C, investigate.

Multiplex QEMU output for serial access.
This commit is contained in:
Berkus Decker 2022-01-22 23:00:03 +02:00
parent cfe4a230de
commit 162592beaa
9 changed files with 1184 additions and 19 deletions

View File

@ -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

666
Cargo.lock generated
View File

@ -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"

View File

@ -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.

View File

@ -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

View File

@ -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}"
]

View File

@ -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]

View File

@ -0,0 +1,25 @@
[package]
name = "chainofcommand"
version = "0.0.1"
authors = ["Berkus Decker <berkus+vesper@metta.systems>"]
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"

View File

@ -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

View File

@ -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<Path> + std::fmt::Display + Clone + Sync + Send + 'static;
async fn expect(
to_console2: &mpsc::Sender<Vec<u8>>,
from_serial: &mut mpsc::Receiver<Vec<u8>>,
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<P>(to_console2: &mpsc::Sender<Vec<u8>>, 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<P>(
to_console2: &mpsc::Sender<Vec<u8>>,
to_serial: &mpsc::Sender<Vec<u8>>,
from_serial: &mut mpsc::Receiver<Vec<u8>>,
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<Vec<u8>>,
mut from_console: mpsc::Receiver<Vec<u8>>,
) -> 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 <EOF>");
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<P>(
to_console2: mpsc::Sender<Vec<u8>>,
mut from_internal: mpsc::Receiver<Vec<u8>>,
to_serial: mpsc::Sender<Vec<u8>>,
mut from_serial: mpsc::Receiver<Vec<u8>>,
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<P>(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::<Bytes>();
// 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<Bytes> {
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::<u32>().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::<std::io::Error>() {
Some(e)
if e.kind() == std::io::ErrorKind::NotFound
|| e.kind() == std::io::ErrorKind::PermissionDenied =>
{
true
}
_ => false,
} || matches!(e.downcast_ref::<tokio_serial::Error>(), Some(e) if e.kind == tokio_serial::ErrorKind::NoDevice)
|| matches!(
e.downcast_ref::<tokio::sync::mpsc::error::SendError<Vec<u8>>>(),
Some(_)
);
if !cont {
break;
}
} else {
// main_loop() returned Ok() we're good to finish
break;
}
execute!(stdout, cursor::SavePosition)?;
}
Ok(())
}