diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0345feb4..b0dc8b93 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,10 @@ on: pull_request: merge_group: +env: + RUSTFLAGS: -Dwarnings + RUSTDOCFLAGS: -Dwarnings + defaults: run: shell: bash @@ -24,8 +28,6 @@ jobs: components: clippy - name: Clippy run: cargo xtask clippy - env: - RUSTFLAGS: -Dwarnings fmt: name: Format @@ -43,7 +45,7 @@ jobs: name: Run strategy: matrix: - target: [x86_64, x86_64-uefi, x86_64-fc, aarch64, riscv64] + target: [x86_64, aarch64, riscv64] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: @@ -55,13 +57,12 @@ jobs: - name: Install QEMU, NASM (macos) if: matrix.os == 'macos-latest' run: | - # enable once it works again - # brew update + brew update brew install qemu nasm - name: Install QEMU, NASM (windows) if: matrix.os == 'windows-latest' run: | - choco install qemu --version=2023.5.31 --checksum64=20d26b460ec045b2cad4bdc4af59713db3110ab95dd73821a590571e4fc1ce1b972e9647867c16e061d1f7381de362ac9c9bfc027f47cdcce8a186818b595ffb + choco install qemu echo "C:\Program Files\qemu" >> $GITHUB_PATH choco install nasm echo "C:\Program Files\NASM" >> $GITHUB_PATH @@ -70,79 +71,48 @@ jobs: lfs: true - name: Install stable Rust toolchain uses: dtolnay/rust-toolchain@stable - - name: Build - run: cargo xtask build --target ${{ matrix.target }} - - name: Run loader (x86_64) + - name: Run VM (hello_world, dev) + run: cargo xtask ci qemu --target ${{ matrix.target }} + - name: Run VM (hello_world, release) + run: cargo xtask ci qemu --target ${{ matrix.target }} --release + - name: Run VM (hello_c, dev) if: matrix.target == 'x86_64' - run: | - qemu-system-x86_64 \ - -cpu qemu64,apic,fsgsbase,fxsr,rdrand,rdtscp,xsave,xsaveopt \ - -smp 1 -m 64M \ - -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ - -display none -serial stdio \ - -kernel target/x86_64/debug/hermit-loader \ - -initrd data/x86_64/hello_world - qemu-system-x86_64 \ - -cpu qemu64,apic,fsgsbase,fxsr,rdrand,rdtscp,xsave,xsaveopt \ - -smp 1 -m 64M \ - -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ - -display none -serial stdio \ - -kernel target/x86_64/debug/hermit-loader \ - -initrd data/x86_64/hello_c - - name: Run loader (aarch64) - if: matrix.target == 'aarch64' - run: | - qemu-system-aarch64 \ - -machine virt,gic-version=3 -cpu cortex-a72 -smp 1 -m 512M \ - -display none -serial stdio -semihosting \ - -kernel target/aarch64/debug/hermit-loader \ - -device guest-loader,addr=0x48000000,initrd=data/aarch64/hello_world - - name: Run loader (riscv64) - if: matrix.target == 'riscv64' - run: | - qemu-system-riscv64 \ - -machine virt \ - -cpu rv64 \ - -smp 1 \ - -m 32M \ - -display none -serial stdio \ - -kernel target/riscv64/debug/hermit-loader \ - -initrd data/riscv64/hello_world - - name: Build (release) - run: cargo xtask build --target ${{ matrix.target }} --release - - name: Run loader (release, x86_64) + run: cargo xtask ci qemu --target ${{ matrix.target }} --image hello_c + - name: Run VM (hello_c, release) if: matrix.target == 'x86_64' + run: cargo xtask ci qemu --target ${{ matrix.target }} --image hello_c --release + + run-kvm: + name: Run (KVM) + runs-on: [self-hosted] + steps: + - name: Install QEMU run: | - qemu-system-x86_64 \ - -cpu qemu64,apic,fsgsbase,fxsr,rdrand,rdtscp,xsave,xsaveopt \ - -smp 1 -m 64M \ - -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ - -display none -serial stdio \ - -kernel target/x86_64/release/hermit-loader \ - -initrd data/x86_64/hello_world - qemu-system-x86_64 \ - -cpu qemu64,apic,fsgsbase,fxsr,rdrand,rdtscp,xsave,xsaveopt \ - -smp 1 -m 64M \ - -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ - -display none -serial stdio \ - -kernel target/x86_64/release/hermit-loader \ - -initrd data/x86_64/hello_c - - name: Run loader (release, aarch64) - if: matrix.target == 'aarch64' - run: | - qemu-system-aarch64 \ - -machine virt,gic-version=3 -cpu cortex-a72 -smp 1 -m 512M \ - -display none -serial stdio -semihosting \ - -kernel target/aarch64/release/hermit-loader \ - -device guest-loader,addr=0x48000000,initrd=data/aarch64/hello_world - - name: Run loader (release, riscv64) - if: matrix.target == 'riscv64' + sudo apt-get update + sudo apt-get install -y --no-install-recommends qemu-system-x86 + - uses: actions/checkout@v4 + with: + lfs: true + - name: Install firecracker run: | - qemu-system-riscv64 \ - -machine virt \ - -cpu rv64 \ - -smp 1 \ - -m 32M \ - -display none -serial stdio \ - -kernel target/riscv64/release/hermit-loader \ - -initrd data/riscv64/hello_world + # https://github.com/firecracker-microvm/firecracker/blob/v1.5.1/docs/getting-started.md#getting-a-firecracker-binary + ARCH="$(uname -m)" + release_url="https://github.com/firecracker-microvm/firecracker/releases" + latest=$(basename $(curl -fsSLI -o /dev/null -w %{url_effective} ${release_url}/latest)) + curl -L ${release_url}/download/${latest}/firecracker-${latest}-${ARCH}.tgz \ + | tar -xz + + # Rename the binary to "firecracker" + mv release-${latest}-$(uname -m)/firecracker-${latest}-${ARCH} firecracker + echo "$PWD" >> $GITHUB_PATH + + ./firecracker --version + - uses: dtolnay/rust-toolchain@stable + - name: Run QEMU (hello_world, dev) + run: cargo xtask ci qemu --target x86_64 --accel + - name: Run QEMU (hello_world, release) + run: cargo xtask ci qemu --target x86_64 --accel --release + - name: Run Firecracker (hello_world, dev) + run: cargo xtask ci firecracker --target x86_64-fc --features fc + - name: Run Firecracker (hello_world, release) + run: cargo xtask ci firecracker --target x86_64-fc --features fc --release \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 2126bc5d..27583360 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,6 +23,54 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +[[package]] +name = "anstream" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "anyhow" version = "1.0.79" @@ -68,12 +116,89 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "critical-section" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + [[package]] name = "deranged" version = "0.3.11" @@ -83,6 +208,12 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + [[package]] name = "embedded-hal" version = "1.0.0" @@ -121,6 +252,12 @@ dependencies = [ "scroll 0.12.0", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-dtb" version = "0.1.1" @@ -169,9 +306,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.149" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "llvm-tools" @@ -231,6 +368,21 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4d98d0065f4b1daf164b3eafb11974c94662e5e2396cf03f32d0bb5c17da51" +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + [[package]] name = "paste" version = "1.0.14" @@ -296,6 +448,26 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "rayon" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "riscv" version = "0.11.0" @@ -351,6 +523,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "1.0.109" @@ -373,6 +551,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sysinfo" +version = "0.30.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb4f3438c8f6389c864e61221cbc97e9bca98b4daf39a5beb7bea660f528bb2" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "windows", +] + [[package]] name = "time" version = "0.3.31" @@ -476,12 +669,125 @@ 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 = "volatile" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" +[[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" + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "x86" version = "0.52.0" @@ -505,21 +811,6 @@ dependencies = [ "volatile", ] -[[package]] -name = "xflags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d9e15fbb3de55454b0106e314b28e671279009b363e6f1d8e39fdc3bf048944" -dependencies = [ - "xflags-macros", -] - -[[package]] -name = "xflags-macros" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "672423d4fea7ffa2f6c25ba60031ea13dc6258070556f125cc4d790007d4a155" - [[package]] name = "xshell" version = "0.2.5" @@ -540,7 +831,8 @@ name = "xtask" version = "0.1.0" dependencies = [ "anyhow", + "clap", "llvm-tools", - "xflags", + "sysinfo", "xshell", ] diff --git a/data/aarch64/hello_world b/data/aarch64/hello_world index fbc9382a..8c5caeb2 100755 --- a/data/aarch64/hello_world +++ b/data/aarch64/hello_world @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a78511fc4cb46142aec755d957c99ee8776ec8404f2c09826c849c2f5969dba5 -size 5073024 +oid sha256:8ac9995db3884278615b71baf387904f9bcd39ea596aecefa12550d47fae4a10 +size 525976 diff --git a/data/riscv64/hello_world b/data/riscv64/hello_world index 1d6d3046..e340dd15 100755 --- a/data/riscv64/hello_world +++ b/data/riscv64/hello_world @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eced9ae473fa1ffc91f210c038b1abf3552256657645355c0d40fce2445ed687 -size 856776 +oid sha256:c2b2ca24ea0ca8880c3609857005f85f1679ad3fa550c45d26b3f0ee8729ecba +size 710144 diff --git a/data/x86_64-fc/hello_world b/data/x86_64-fc/hello_world new file mode 100755 index 00000000..3503ef6f --- /dev/null +++ b/data/x86_64-fc/hello_world @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a903e3be2095e2e85c87d388a0cd017d0911a33c013a71895da40707801f1561 +size 1482928 diff --git a/data/x86_64/hello_world b/data/x86_64/hello_world index 555e195b..8b45fded 100755 --- a/data/x86_64/hello_world +++ b/data/x86_64/hello_world @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bcea9b0fa24d91c84cf356f103f7bd0912e94c6d683173a699959a50c3861ef -size 5182784 +oid sha256:1ce4826673390b08575acd132580dea64969e77d1638bcef618d68d3783e298a +size 1525000 diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index f75edb6e..7cdba46f 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] anyhow = "1.0" +clap = { version = "4", features = ["derive"] } llvm-tools = "0.1" -xflags = "0.3" -xshell = "0.2" +sysinfo = "0.30" +xshell = "0.2" \ No newline at end of file diff --git a/xtask/src/artifact.rs b/xtask/src/artifact.rs new file mode 100644 index 00000000..e86298f2 --- /dev/null +++ b/xtask/src/artifact.rs @@ -0,0 +1,74 @@ +use std::path::{Path, PathBuf}; + +use clap::Args; + +use crate::object::Object; +use crate::target::Target; + +#[derive(Args)] +pub struct Artifact { + /// Target. + #[arg(value_enum, long)] + pub target: Target, + + /// Directory for all generated artifacts. + #[arg(long, id = "DIRECTORY")] + pub target_dir: Option, + + /// Build artifacts in release mode, with optimizations. + #[arg(short, long)] + pub release: bool, + + /// Build artifacts with the specified profile. + #[arg(long, id = "PROFILE-NAME")] + pub profile: Option, +} + +impl Artifact { + pub fn profile(&self) -> &str { + self.profile + .as_deref() + .unwrap_or(if self.release { "release" } else { "dev" }) + } + + pub fn profile_path_component(&self) -> &str { + match self.profile() { + "dev" => "debug", + profile => profile, + } + } + + pub fn target_dir(&self) -> &Path { + self.target_dir + .as_deref() + .unwrap_or_else(|| Path::new("target")) + } + + pub fn build_object(&self) -> Object { + [ + self.target_dir(), + self.target.triple().as_ref(), + self.profile_path_component().as_ref(), + self.target.build_name().as_ref(), + ] + .iter() + .collect::() + .into() + } + + pub fn dist_object(&self) -> Object { + [ + self.target_dir(), + self.target.name().as_ref(), + self.profile_path_component().as_ref(), + self.target.dist_name().as_ref(), + ] + .iter() + .collect::() + .into() + } + + pub fn ci_image(&self, image: &str) -> PathBuf { + ["data", self.target.name(), image].iter().collect() + } +} diff --git a/xtask/src/build.rs b/xtask/src/build.rs new file mode 100644 index 00000000..0095eed4 --- /dev/null +++ b/xtask/src/build.rs @@ -0,0 +1,78 @@ +use std::env::{self, VarError}; +use std::path::PathBuf; + +use anyhow::Result; +use clap::Args; +use xshell::cmd; + +use crate::cargo_build::{CargoBuild, CmdExt}; +use crate::target::Target; + +/// Build the kernel. +#[derive(Args)] +pub struct Build { + #[command(flatten)] + cargo_build: CargoBuild, +} + +impl Build { + pub fn run(&self) -> Result<()> { + self.cargo_build.artifact.target.install()?; + + let sh = crate::sh()?; + + eprintln!("Building loader"); + cmd!(sh, "cargo build") + .env("CARGO_ENCODED_RUSTFLAGS", self.cargo_encoded_rustflags()?) + .args(self.cargo_build.artifact.target.cargo_args()) + .cargo_build_args(&self.cargo_build) + .run()?; + + let build_object = self.cargo_build.artifact.build_object(); + let dist_object = self.cargo_build.artifact.dist_object(); + eprintln!( + "Copying {} to {}", + build_object.as_ref().display(), + dist_object.as_ref().display() + ); + sh.create_dir(dist_object.as_ref().parent().unwrap())?; + sh.copy_file(&build_object, &dist_object)?; + + if self.cargo_build.artifact.target == Target::X86_64 { + eprintln!("Converting object to elf32-i386"); + dist_object.convert_to_elf32_i386()?; + } + + eprintln!("Loader available at {}", dist_object.as_ref().display()); + Ok(()) + } + + fn cargo_encoded_rustflags(&self) -> Result { + let outer_rustflags = match env::var("CARGO_ENCODED_RUSTFLAGS") { + Ok(s) => Some(s), + Err(VarError::NotPresent) => None, + Err(err) => return Err(err.into()), + }; + + let mut rustflags = outer_rustflags + .as_ref() + .map(|s| vec![s.as_str()]) + .unwrap_or_default(); + + rustflags.extend(self.cargo_build.artifact.target.rustflags()); + + Ok(rustflags.join("\x1f")) + } + + pub fn dist_object(&self) -> PathBuf { + self.cargo_build.artifact.dist_object().into() + } + + pub fn target(&self) -> Target { + self.cargo_build.artifact.target + } + + pub fn ci_image(&self, image: &str) -> PathBuf { + self.cargo_build.artifact.ci_image(image) + } +} diff --git a/xtask/src/cargo_build.rs b/xtask/src/cargo_build.rs new file mode 100644 index 00000000..585b8294 --- /dev/null +++ b/xtask/src/cargo_build.rs @@ -0,0 +1,73 @@ +use std::ffi::OsStr; + +use clap::Args; +use xshell::Cmd; + +use crate::artifact::Artifact; + +#[derive(Args)] +pub struct CargoBuild { + #[command(flatten)] + pub artifact: Artifact, + + /// Do not activate the `default` feature. + #[arg(long)] + no_default_features: bool, + + /// Space or comma separated list of features to activate. + #[arg(long)] + pub features: Vec, +} + +pub trait CmdExt { + fn cargo_build_args(self, cargo_build: &CargoBuild) -> Self; + fn target_dir_args(self, cargo_build: &CargoBuild) -> Self; +} + +impl CmdExt for Cmd<'_> { + fn cargo_build_args(self, cargo_build: &CargoBuild) -> Self { + let cmd = self + .target_dir_args(cargo_build) + .args(cargo_build.no_default_features_args()) + .args(cargo_build.features_args()) + .args(cargo_build.release_args()); + + if let Some(profile) = &cargo_build.artifact.profile { + cmd.args(&["--profile", profile]) + } else { + cmd + } + } + + fn target_dir_args(self, cargo_build: &CargoBuild) -> Self { + if let Some(target_dir) = &cargo_build.artifact.target_dir { + self.args::<&[&OsStr]>(&["--target-dir".as_ref(), target_dir.as_ref()]) + } else { + self + } + } +} + +impl CargoBuild { + fn release_args(&self) -> &'static [&'static str] { + if self.artifact.release { + &["--release"] + } else { + &[] + } + } + + fn no_default_features_args(&self) -> &'static [&'static str] { + if self.no_default_features { + &["--no-default-features"] + } else { + &[] + } + } + + fn features_args(&self) -> impl Iterator { + self.features + .iter() + .flat_map(|feature| ["--features", feature.as_str()]) + } +} diff --git a/xtask/src/ci/firecracker.rs b/xtask/src/ci/firecracker.rs new file mode 100644 index 00000000..4ec63a6f --- /dev/null +++ b/xtask/src/ci/firecracker.rs @@ -0,0 +1,45 @@ +use std::path::Path; + +use anyhow::Result; +use clap::Args; +use xshell::cmd; + +use crate::build::Build; + +/// Run hermit-rs images on Firecracker. +#[derive(Args)] +pub struct Firecracker { + #[command(flatten)] + build: Build, + + #[arg(long, default_value_t = String::from("hello_world"))] + image: String, +} + +impl Firecracker { + pub fn run(self) -> Result<()> { + self.build.run()?; + + let sh = crate::sh()?; + + let config = format!( + include_str!("firecracker_vm_config.json"), + kernel_image_path = self.build.dist_object().display(), + initrd_path = self.build.ci_image(&self.image).display(), + ); + eprintln!("firecracker config"); + eprintln!("{config}"); + let config_path = Path::new("firecracker_vm_config.json"); + sh.write_file(config_path, config)?; + + let log_path = Path::new("firecracker.log"); + sh.write_file(log_path, "")?; + cmd!(sh, "firecracker --no-api --config-file {config_path} --log-path {log_path} --level Info --show-level --show-log-origin").run()?; + let log = sh.read_file(log_path)?; + + eprintln!("firecracker log"); + eprintln!("{log}"); + + Ok(()) + } +} diff --git a/xtask/src/ci/firecracker_vm_config.json b/xtask/src/ci/firecracker_vm_config.json new file mode 100644 index 00000000..14c7b8e2 --- /dev/null +++ b/xtask/src/ci/firecracker_vm_config.json @@ -0,0 +1,13 @@ +{{ + "boot-source": {{ + "kernel_image_path": "{kernel_image_path}", + "initrd_path": "{initrd_path}", + "boot_args": "" + }}, + "drives": [], + "machine-config": {{ + "vcpu_count": 1, + "mem_size_mib": 256, + "smt": false + }} +}} diff --git a/xtask/src/ci/mod.rs b/xtask/src/ci/mod.rs new file mode 100644 index 00000000..409b7690 --- /dev/null +++ b/xtask/src/ci/mod.rs @@ -0,0 +1,25 @@ +use anyhow::Result; +use clap::Subcommand; + +mod firecracker; +mod qemu; + +/// Run CI tasks. +#[derive(Subcommand)] +pub enum Ci { + Firecracker(firecracker::Firecracker), + Qemu(qemu::Qemu), +} + +impl Ci { + pub fn run(self) -> Result<()> { + match self { + Self::Firecracker(firecracker) => firecracker.run(), + Self::Qemu(qemu) => qemu.run(), + } + } +} + +fn in_ci() -> bool { + std::env::var_os("CI") == Some("true".into()) +} diff --git a/xtask/src/ci/qemu.rs b/xtask/src/ci/qemu.rs new file mode 100644 index 00000000..764c425d --- /dev/null +++ b/xtask/src/ci/qemu.rs @@ -0,0 +1,182 @@ +use std::env; +use std::process::{Command, ExitStatus}; + +use anyhow::{ensure, Result}; +use clap::Args; +use sysinfo::{CpuRefreshKind, System}; +use xshell::cmd; + +use crate::build::Build; +use crate::target::Target; + +/// Run hermit-rs images on QEMU. +#[derive(Args)] +pub struct Qemu { + /// Enable hardware acceleration. + #[arg(long)] + accel: bool, + + /// Enable the `microvm` machine type. + #[arg(long)] + microvm: bool, + + #[command(flatten)] + build: Build, + + #[arg(long, default_value_t = String::from("hello_world"))] + image: String, +} + +impl Qemu { + pub fn run(self) -> Result<()> { + if super::in_ci() { + eprintln!("::group::cargo build") + } + + self.build.run()?; + + if super::in_ci() { + eprintln!("::endgroup::") + } + + let sh = crate::sh()?; + + let target = self.build.target(); + let arch = target.arch(); + let qemu = env::var_os("QEMU").unwrap_or_else(|| format!("qemu-system-{arch}").into()); + + let dist_object = self.build.dist_object(); + let qemu = cmd!(sh, "{qemu}") + .args(&["-display", "none"]) + .args(&["-serial", "stdio"]) + .arg("-kernel") + .arg(&dist_object) + .args(self.machine_args()) + .args(self.cpu_args()) + .args(self.memory_args()); + + eprintln!("$ {qemu}"); + let status = Command::from(qemu).status()?; + ensure!(status.qemu_success(), "QEMU exit code: {:?}", status.code()); + + Ok(()) + } + + fn machine_args(&self) -> Vec { + if self.microvm { + let frequency = get_frequency(); + vec![ + "-M".to_string(), + "microvm,x-option-roms=off,pit=off,pic=off,rtc=on".to_string(), + "-global".to_string(), + "virtio-mmio.force-legacy=on".to_string(), + "-nodefaults".to_string(), + "-no-user-config".to_string(), + "-append".to_string(), + format!("-freq {frequency}"), + ] + } else if self.build.target() == Target::Aarch64 { + vec!["-machine".to_string(), "virt,gic-version=3".to_string()] + } else if self.build.target() == Target::Riscv64 { + vec!["-machine".to_string(), "virt".to_string()] + } else { + vec![] + } + } + + fn cpu_args(&self) -> Vec { + match self.build.target() { + Target::X86_64 | Target::X86_64Fc | Target::X86_64Uefi => { + let mut cpu_args = if self.accel { + if cfg!(target_os = "linux") { + vec![ + "-enable-kvm".to_string(), + "-cpu".to_string(), + "host".to_string(), + ] + } else { + todo!() + } + } else { + vec!["-cpu".to_string(), "Skylake-Client".to_string()] + }; + cpu_args.push("-device".to_string()); + cpu_args.push("isa-debug-exit,iobase=0xf4,iosize=0x04".to_string()); + cpu_args.push("-initrd".to_string()); + cpu_args.push( + self.build + .ci_image(&self.image) + .into_os_string() + .into_string() + .unwrap(), + ); + cpu_args + } + Target::Aarch64 => { + let mut cpu_args = if self.accel { + todo!() + } else { + vec!["-cpu".to_string(), "cortex-a72".to_string()] + }; + cpu_args.push("-semihosting".to_string()); + cpu_args.push("-device".to_string()); + cpu_args.push(format!( + "guest-loader,addr=0x48000000,initrd={}", + self.build.ci_image(&self.image).display() + )); + cpu_args + } + Target::Riscv64 => { + let mut cpu_args = if self.accel { + todo!() + } else { + vec!["-cpu".to_string(), "rv64".to_string()] + }; + cpu_args.push("-initrd".to_string()); + cpu_args.push( + self.build + .ci_image(&self.image) + .into_os_string() + .into_string() + .unwrap(), + ); + cpu_args + } + } + } + + fn memory(&self) -> usize { + let mut memory = 32usize; + if self.image == "hello_c" { + memory = memory.max(64); + } + if self.build.target() == Target::Aarch64 { + memory = memory.max(256); + } + memory + } + + fn memory_args(&self) -> [String; 2] { + ["-m".to_string(), format!("{}M", self.memory())] + } +} + +fn get_frequency() -> u64 { + let mut sys = System::new(); + sys.refresh_cpu_specifics(CpuRefreshKind::new().with_frequency()); + let frequency = sys.cpus().first().unwrap().frequency(); + if !sys.cpus().iter().all(|cpu| cpu.frequency() == frequency) { + eprintln!("CPU frequencies are not all equal"); + } + frequency +} + +trait ExitStatusExt { + fn qemu_success(&self) -> bool; +} + +impl ExitStatusExt for ExitStatus { + fn qemu_success(&self) -> bool { + self.success() || self.code() == Some(3) + } +} diff --git a/xtask/src/clippy.rs b/xtask/src/clippy.rs new file mode 100644 index 00000000..56cfd4ba --- /dev/null +++ b/xtask/src/clippy.rs @@ -0,0 +1,32 @@ +use anyhow::Result; +use clap::Args; +use xshell::cmd; + +use crate::target::Target; + +/// Run Clippy for all targets. +#[derive(Args)] +pub struct Clippy; + +impl Clippy { + pub fn run(self) -> Result<()> { + let sh = crate::sh()?; + + for target in [ + Target::X86_64, + Target::X86_64Fc, + Target::X86_64Uefi, + Target::Aarch64, + Target::Riscv64, + ] { + target.install()?; + let triple = target.triple(); + let feature_flags = target.feature_flags(); + cmd!(sh, "cargo clippy --target={triple} {feature_flags...}").run()?; + } + + cmd!(sh, "cargo clippy --package xtask").run()?; + + Ok(()) + } +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 264167cf..45f5753a 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,171 +1,54 @@ //! See . -mod flags; +mod artifact; +mod build; +mod cargo_build; +mod ci; +mod clippy; +mod object; mod target; -use std::env::{self, VarError}; -use std::ffi::OsStr; +use std::env; use std::path::{Path, PathBuf}; use anyhow::{anyhow, Result}; -use llvm_tools::LlvmTools; -use xshell::{cmd, Shell}; - -use crate::target::Target; - -fn main() -> Result<()> { - flags::Xtask::from_env()?.run() +use clap::Parser; + +#[derive(Parser)] +enum Cli { + Build(build::Build), + #[command(subcommand)] + Ci(ci::Ci), + Clippy(clippy::Clippy), } -impl flags::Xtask { +impl Cli { fn run(self) -> Result<()> { - match self.subcommand { - flags::XtaskCmd::Build(build) => build.run(), - flags::XtaskCmd::Clippy(clippy) => clippy.run(), + match self { + Self::Build(build) => build.run(), + Self::Ci(ci) => ci.run(), + Self::Clippy(clippy) => clippy.run(), } } } -impl flags::Build { - fn run(self) -> Result<()> { - self.target.install()?; - - let sh = Shell::new()?; - - eprintln!("Building loader"); - let triple = self.target.triple(); - cmd!(sh, "cargo build --target={triple}") - .env("CARGO_ENCODED_RUSTFLAGS", self.cargo_encoded_rustflags()?) - .args(self.target.feature_flags()) - .args(self.target_dir_args()) - .args(self.profile_args()) - .run()?; - - let build_object = self.build_object(); - let dist_object = self.dist_object(); - eprintln!( - "Copying {} to {}", - build_object.display(), - dist_object.display() - ); - sh.create_dir(dist_object.parent().unwrap())?; - sh.copy_file(&build_object, &dist_object)?; - - if self.target == Target::X86_64 { - eprintln!("Converting object to elf32-i386"); - self.convert_to_elf32_i386()?; - } - - eprintln!("Loader available at {}", self.dist_object().display()); - Ok(()) - } - - fn cargo_encoded_rustflags(&self) -> Result { - let outer_rustflags = match env::var("CARGO_ENCODED_RUSTFLAGS") { - Ok(s) => Some(s), - Err(VarError::NotPresent) => None, - Err(err) => return Err(err.into()), - }; - - let mut rustflags = outer_rustflags - .as_ref() - .map(|s| vec![s.as_str()]) - .unwrap_or_default(); - - rustflags.extend(self.target.rustflags()); - - Ok(rustflags.join("\x1f")) - } - - fn target_dir_args(&self) -> [&OsStr; 2] { - ["--target-dir".as_ref(), self.target_dir().as_ref()] - } - - fn profile_args(&self) -> [&str; 2] { - ["--profile", self.profile()] - } - - fn convert_to_elf32_i386(&self) -> Result<()> { - let sh = Shell::new()?; - let objcopy = binutil("objcopy")?; - let object = self.dist_object(); - cmd!(sh, "{objcopy} --output-target elf32-i386 {object}").run()?; - Ok(()) - } - - fn profile(&self) -> &str { - self.profile - .as_deref() - .unwrap_or(if self.release { "release" } else { "dev" }) - } - - fn target_dir(&self) -> &Path { - self.target_dir - .as_deref() - .unwrap_or_else(|| Path::new("target")) - } - - fn out_dir(&self) -> PathBuf { - let mut out_dir = self.target_dir().to_path_buf(); - out_dir.push(self.target.triple()); - out_dir.push(match self.profile() { - "dev" => "debug", - profile => profile, - }); - out_dir - } - - fn dist_dir(&self) -> PathBuf { - let mut out_dir = self.target_dir().to_path_buf(); - out_dir.push(self.target.name()); - out_dir.push(match self.profile() { - "dev" => "debug", - profile => profile, - }); - out_dir - } - - fn build_object(&self) -> PathBuf { - let mut build_object = self.out_dir(); - build_object.push(self.target.build_name()); - build_object - } - - fn dist_object(&self) -> PathBuf { - let mut dist_object = self.dist_dir(); - dist_object.push(self.target.dist_name()); - dist_object - } +fn main() -> Result<()> { + let cli = Cli::parse(); + cli.run() } -impl flags::Clippy { - fn run(self) -> Result<()> { - let sh = Shell::new()?; - - for target in [ - Target::X86_64, - Target::X86_64Fc, - Target::X86_64Uefi, - Target::AArch64, - Target::Riscv64, - ] { - target.install()?; - let triple = target.triple(); - let feature_flags = target.feature_flags(); - cmd!(sh, "cargo clippy --target={triple} {feature_flags...}").run()?; - } - - cmd!(sh, "cargo clippy --package xtask").run()?; - - Ok(()) - } +pub fn sh() -> Result { + let sh = xshell::Shell::new()?; + let project_root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap(); + sh.change_dir(project_root); + Ok(sh) } -fn binutil(name: &str) -> Result { +pub fn binutil(name: &str) -> Result { let exe_suffix = env::consts::EXE_SUFFIX; let exe = format!("llvm-{name}{exe_suffix}"); - let path = LlvmTools::new() + let path = llvm_tools::LlvmTools::new() .map_err(|err| anyhow!("{err:?}"))? .tool(&exe) .ok_or_else(|| anyhow!("could not find {exe}"))?; diff --git a/xtask/src/object.rs b/xtask/src/object.rs new file mode 100644 index 00000000..50f57768 --- /dev/null +++ b/xtask/src/object.rs @@ -0,0 +1,34 @@ +use std::path::{Path, PathBuf}; + +use anyhow::Result; +use xshell::cmd; + +pub struct Object(PathBuf); + +impl From for Object { + fn from(object: PathBuf) -> Self { + Self(object) + } +} + +impl From for PathBuf { + fn from(value: Object) -> Self { + value.0 + } +} + +impl AsRef for Object { + fn as_ref(&self) -> &Path { + &self.0 + } +} + +impl Object { + pub fn convert_to_elf32_i386(&self) -> Result<()> { + let sh = crate::sh()?; + let objcopy = crate::binutil("objcopy")?; + let object = self.as_ref(); + cmd!(sh, "{objcopy} --output-target elf32-i386 {object}").run()?; + Ok(()) + } +} diff --git a/xtask/src/target.rs b/xtask/src/target.rs index 7d8ea657..59da310b 100644 --- a/xtask/src/target.rs +++ b/xtask/src/target.rs @@ -8,7 +8,7 @@ pub enum Target { X86_64, X86_64Fc, X86_64Uefi, - AArch64, + Aarch64, Riscv64, } @@ -31,7 +31,17 @@ impl Target { Self::X86_64 => "x86_64", Self::X86_64Fc => "x86_64-fc", Self::X86_64Uefi => "x86_64-uefi", - Self::AArch64 => "aarch64", + Self::Aarch64 => "aarch64", + Self::Riscv64 => "riscv64", + } + } + + pub fn arch(&self) -> &'static str { + match self { + Self::X86_64 => "x86_64", + Self::X86_64Fc => "x86_64", + Self::X86_64Uefi => "x86_64", + Self::Aarch64 => "aarch64", Self::Riscv64 => "riscv64", } } @@ -41,11 +51,21 @@ impl Target { Self::X86_64 => "x86_64-unknown-none", Self::X86_64Fc => "x86_64-unknown-none", Self::X86_64Uefi => "x86_64-unknown-uefi", - Self::AArch64 => "aarch64-unknown-none-softfloat", + Self::Aarch64 => "aarch64-unknown-none-softfloat", Self::Riscv64 => "riscv64imac-unknown-none-elf", } } + pub fn cargo_args(&self) -> &'static [&'static str] { + match self { + Self::X86_64 => &["--target=x86_64-unknown-none"], + Self::X86_64Fc => &["--target=x86_64-unknown-none"], + Self::X86_64Uefi => &["--target=x86_64-unknown-uefi"], + Self::Aarch64 => &["--target=aarch64-unknown-none-softfloat"], + Self::Riscv64 => &["--target=riscv64imac-unknown-none-elf"], + } + } + pub fn rustflags(&self) -> &'static [&'static str] { match self { Self::X86_64 => &[ @@ -57,7 +77,7 @@ impl Target { "-Crelocation-model=static", ], Self::X86_64Uefi => &[], - Self::AArch64 => &["-Clink-arg=-Tsrc/arch/aarch64/link.ld"], + Self::Aarch64 => &["-Clink-arg=-Tsrc/arch/aarch64/link.ld"], Self::Riscv64 => &["-Clink-arg=-Tsrc/arch/riscv64/link.ld"], } } @@ -92,7 +112,7 @@ impl FromStr for Target { "x86_64" => Ok(Self::X86_64), "x86_64-fc" => Ok(Self::X86_64Fc), "x86_64-uefi" => Ok(Self::X86_64Uefi), - "aarch64" => Ok(Self::AArch64), + "aarch64" => Ok(Self::Aarch64), "riscv64" => Ok(Self::Riscv64), s => Err(anyhow!("Unsupported target: {s}")), }