From f97d0f12605c5424085a8cc03f41cba521643b98 Mon Sep 17 00:00:00 2001 From: Zhang Junyang Date: Wed, 21 Feb 2024 16:58:40 +0800 Subject: [PATCH] Implement OSDK functionalities and opt-in OSDK for asterinas --- .cargo/config.toml | 14 -- .github/workflows/cargo_check.yml | 2 + .github/workflows/integration_test.yml | 8 +- .github/workflows/unit_test.yml | 8 +- .vscode/settings.json | 4 +- Cargo.lock | 201 +-------------- Cargo.toml | 7 +- Makefile | 114 ++++----- OSDK.toml | 88 +++++++ build.rs | 21 -- framework/aster-frame/Cargo.toml | 1 + framework/aster-frame/src/boot/kcmdline.rs | 14 +- framework/aster-frame/src/boot/mod.rs | 66 +++-- framework/aster-frame/src/lib.rs | 3 +- framework/aster-frame/src/panicking.rs | 16 +- framework/aster-frame/src/prelude.rs | 4 + framework/aster-frame/src/sync/atomic_bits.rs | 2 +- .../aster-frame/src/vm/dma/dma_coherent.rs | 2 +- .../aster-frame/src/vm/dma/dma_stream.rs | 2 +- framework/libs/aster-main/Cargo.toml | 14 ++ framework/libs/aster-main/src/lib.rs | 22 ++ framework/libs/ktest-proc-macro/src/lib.rs | 19 +- framework/libs/ktest/src/lib.rs | 6 +- framework/libs/ktest/src/runner.rs | 15 +- .../libs/linux-bzimage/builder/src/lib.rs | 37 +-- framework/libs/linux-bzimage/setup/.gitignore | 2 + framework/libs/linux-bzimage/setup/Cargo.toml | 4 + kernel/main.rs | 19 -- osdk/.gitignore | 3 + osdk/Cargo.lock | 146 +++++++---- osdk/Cargo.toml | 1 + osdk/README.md | 42 +++- ...hain.toml.template => rust-toolchain.toml} | 0 osdk/src/base_crate/main.rs.template | 12 + osdk/src/base_crate/mod.rs | 108 ++++++++ .../x86_64-custom.json.template | 5 + .../src/base_crate/x86_64-custom.ld.template | 0 osdk/src/bin.rs | 33 +++ osdk/src/bundle.rs | 191 ++++++++++++++ osdk/src/cli.rs | 31 +-- osdk/src/commands/build/bin.rs | 102 ++++++++ .../src/commands/build}/grub.cfg.template | 2 +- osdk/src/commands/build/grub.rs | 157 ++++++++++++ osdk/src/commands/build/mod.rs | 134 ++++++++++ .../commands/build/x86_64-i386_pm-none.json | 0 osdk/src/commands/check.rs | 10 +- osdk/src/commands/clippy.rs | 11 +- osdk/src/commands/mod.rs | 4 + osdk/src/commands/new/kernel.template | 22 ++ .../commands/{template => new}/lib.template | 6 +- osdk/src/commands/{new.rs => new/mod.rs} | 60 ++++- osdk/src/commands/run.rs | 27 ++ osdk/src/commands/template/kernel.template | 9 - osdk/src/commands/test.rs | 70 ++++++ osdk/src/commands/utils.rs | 10 +- osdk/src/config_manager/manifest.rs | 84 +++++-- osdk/src/config_manager/mod.rs | 41 +-- osdk/src/config_manager/qemu.rs | 19 +- osdk/src/error.rs | 3 + osdk/src/main.rs | 4 + .../test/config_manager/OSDK.toml.conditional | 12 +- osdk/src/test/config_manager/OSDK.toml.full | 2 - osdk/src/test/config_manager/manifest.rs | 49 ++-- osdk/src/test/utils.rs | 11 +- osdk/src/utils.rs | 59 ++++- osdk/src/vm_image.rs | 22 ++ runner/Cargo.toml | 12 - runner/src/gdb.rs | 70 ------ runner/src/machine/microvm.rs | 80 ------ runner/src/machine/mod.rs | 4 - runner/src/machine/qemu_grub_efi.rs | 194 --------------- runner/src/main.rs | 234 ------------------ rust-toolchain.toml | 2 +- services/aster-nix/src/lib.rs | 1 + services/aster-nix/src/process/process/mod.rs | 2 +- services/aster-nix/src/vm/vmar/options.rs | 2 +- services/aster-nix/src/vm/vmo/options.rs | 2 +- services/libs/aster-util/src/coeff.rs | 4 +- .../tests/regression_test/Cargo.toml | 2 +- .../tests/regression_test/Components.toml | 12 +- .../tests/regression_test/bar/src/lib.rs | 5 - .../bar => regression_test/bar1}/Cargo.toml | 4 +- .../tests/regression_test/bar1/src/lib.rs | 5 + .../foo => regression_test/foo1}/Cargo.toml | 2 +- .../regression_test/{foo => foo1}/src/lib.rs | 0 .../cargo-component/tests/trait_method.rs | 6 +- .../tests/trait_method_test/Cargo.toml | 2 +- .../tests/trait_method_test/Components.toml | 24 +- .../bar => trait_method_test/bar2}/Cargo.toml | 4 +- .../{bar => bar2}/src/lib.rs | 2 +- .../{foo => foo2}/Cargo.toml | 2 +- .../{foo => foo2}/src/lib.rs | 0 .../cargo-component/tests/violate_policy.rs | 4 +- .../tests/violate_policy_test/Cargo.toml | 2 +- .../tests/violate_policy_test/Components.toml | 4 +- .../tests/violate_policy_test/bar/src/lib.rs | 5 - .../bar3}/Cargo.toml | 4 +- .../tests/violate_policy_test/bar3/src/lib.rs | 5 + .../foo3}/Cargo.toml | 2 +- .../{foo => foo3}/src/lib.rs | 0 .../comp-sys/component-macro/src/priority.rs | 6 +- src/lib.rs | 16 ++ x86_64-custom.json | 16 -- 103 files changed, 1663 insertions(+), 1295 deletions(-) delete mode 100644 .cargo/config.toml create mode 100644 OSDK.toml delete mode 100644 build.rs create mode 100644 framework/libs/aster-main/Cargo.toml create mode 100644 framework/libs/aster-main/src/lib.rs create mode 100644 framework/libs/linux-bzimage/setup/.gitignore delete mode 100644 kernel/main.rs rename osdk/{src/commands/template/rust-toolchain.toml.template => rust-toolchain.toml} (100%) create mode 100644 osdk/src/base_crate/main.rs.template create mode 100644 osdk/src/base_crate/mod.rs rename osdk/src/{commands/template => base_crate}/x86_64-custom.json.template (84%) rename framework/aster-frame/src/arch/x86/linker.ld => osdk/src/base_crate/x86_64-custom.ld.template (100%) create mode 100644 osdk/src/bin.rs create mode 100644 osdk/src/bundle.rs create mode 100644 osdk/src/commands/build/bin.rs rename {runner/grub => osdk/src/commands/build}/grub.cfg.template (87%) create mode 100644 osdk/src/commands/build/grub.rs create mode 100644 osdk/src/commands/build/mod.rs create mode 100644 osdk/src/commands/build/x86_64-i386_pm-none.json create mode 100644 osdk/src/commands/new/kernel.template rename osdk/src/commands/{template => new}/lib.template (75%) rename osdk/src/commands/{new.rs => new/mod.rs} (76%) create mode 100644 osdk/src/commands/run.rs delete mode 100644 osdk/src/commands/template/kernel.template create mode 100644 osdk/src/commands/test.rs create mode 100644 osdk/src/vm_image.rs delete mode 100644 runner/Cargo.toml delete mode 100644 runner/src/gdb.rs delete mode 100644 runner/src/machine/microvm.rs delete mode 100644 runner/src/machine/mod.rs delete mode 100644 runner/src/machine/qemu_grub_efi.rs delete mode 100644 runner/src/main.rs delete mode 100644 services/libs/comp-sys/cargo-component/tests/regression_test/bar/src/lib.rs rename services/libs/comp-sys/cargo-component/tests/{trait_method_test/bar => regression_test/bar1}/Cargo.toml (79%) create mode 100644 services/libs/comp-sys/cargo-component/tests/regression_test/bar1/src/lib.rs rename services/libs/comp-sys/cargo-component/tests/{violate_policy_test/foo => regression_test/foo1}/Cargo.toml (93%) rename services/libs/comp-sys/cargo-component/tests/regression_test/{foo => foo1}/src/lib.rs (100%) rename services/libs/comp-sys/cargo-component/tests/{violate_policy_test/bar => trait_method_test/bar2}/Cargo.toml (79%) rename services/libs/comp-sys/cargo-component/tests/trait_method_test/{bar => bar2}/src/lib.rs (92%) rename services/libs/comp-sys/cargo-component/tests/trait_method_test/{foo => foo2}/Cargo.toml (93%) rename services/libs/comp-sys/cargo-component/tests/trait_method_test/{foo => foo2}/src/lib.rs (100%) delete mode 100644 services/libs/comp-sys/cargo-component/tests/violate_policy_test/bar/src/lib.rs rename services/libs/comp-sys/cargo-component/tests/{regression_test/bar => violate_policy_test/bar3}/Cargo.toml (79%) create mode 100644 services/libs/comp-sys/cargo-component/tests/violate_policy_test/bar3/src/lib.rs rename services/libs/comp-sys/cargo-component/tests/{regression_test/foo => violate_policy_test/foo3}/Cargo.toml (93%) rename services/libs/comp-sys/cargo-component/tests/violate_policy_test/{foo => foo3}/src/lib.rs (100%) create mode 100644 src/lib.rs delete mode 100644 x86_64-custom.json diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 0b4b26d96..000000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,14 +0,0 @@ - -[target.'cfg(target_os = "none")'] -runner = "cargo run --package aster-runner --" - -[alias] -kcheck = "check --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem" -kbuild = "build --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem" -krun = "run --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem" -kclippy = "clippy --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem" -component-check = "component check --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem" - -[install] -root = "./target" - diff --git a/.github/workflows/cargo_check.yml b/.github/workflows/cargo_check.yml index 66d47b081..a84f90b1e 100644 --- a/.github/workflows/cargo_check.yml +++ b/.github/workflows/cargo_check.yml @@ -15,6 +15,8 @@ jobs: - run: echo "Running in asterinas/asterinas:0.3.1" - uses: actions/checkout@v3 + + - run: make install_osdk - name: Check id: check diff --git a/.github/workflows/integration_test.yml b/.github/workflows/integration_test.yml index e9170f681..fa63a2b70 100644 --- a/.github/workflows/integration_test.yml +++ b/.github/workflows/integration_test.yml @@ -16,6 +16,8 @@ jobs: - uses: actions/checkout@v3 + - run: make install_osdk + - name: Boot Test (Multiboot) id: boot_test_mb run: make run AUTO_TEST=boot ENABLE_KVM=0 BOOT_PROTOCOL=multiboot RELEASE_MODE=1 @@ -26,7 +28,7 @@ jobs: - name: Boot Test (MicroVM) id: boot_test_microvm - run: make run AUTO_TEST=boot ENABLE_KVM=0 BOOT_METHOD=microvm RELEASE_MODE=1 + run: make run AUTO_TEST=boot ENABLE_KVM=0 QEMU_MACHINE=microvm RELEASE_MODE=1 - name: Boot Test (Linux Legacy 32-bit Boot Protocol) id: boot_test_linux_legacy32 @@ -42,9 +44,9 @@ jobs: - name: Syscall Test at Ext2 (MicroVM) id: syscall_test_at_ext2_microvm - run: make run AUTO_TEST=syscall SYSCALL_TEST_DIR=/ext2 ENABLE_KVM=0 BOOT_METHOD=microvm RELEASE_MODE=1 + run: make run AUTO_TEST=syscall SYSCALL_TEST_DIR=/ext2 ENABLE_KVM=0 QEMU_MACHINE=microvm RELEASE_MODE=1 - name: Regression Test (MicroVM) id: regression_test_linux - run: make run AUTO_TEST=regression ENABLE_KVM=0 BOOT_METHOD=microvm RELEASE_MODE=1 + run: make run AUTO_TEST=regression ENABLE_KVM=0 QEMU_MACHINE=microvm RELEASE_MODE=1 \ No newline at end of file diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index b5fff6ad5..2ccf0d354 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -16,12 +16,14 @@ jobs: - uses: actions/checkout@v3 - - name: Ktest Unit Test - id: ktest_unit_test - run: make run KTEST=1 ENABLE_KVM=0 RELEASE_MODE=1 + - run: make install_osdk - name: Usermode Unit test id: usermode_unit_test run: make test + - name: Ktest Unit Test + id: ktest_unit_test + run: make update_initramfs && make ktest + # TODO: add component check. diff --git a/.vscode/settings.json b/.vscode/settings.json index dd50ab5c3..13303c1ce 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,9 @@ { "rust-analyzer.check.allTargets": false, - "rust-analyzer.cargo.target": "x86_64-custom.json", + "rust-analyzer.cargo.target": "x86_64-unknown-none", "rust-analyzer.check.extraArgs": [ "--target", - "x86_64-custom.json", + "x86_64-unknown-none", "-Zbuild-std=core,alloc,compiler_builtins", "-Zbuild-std-features=compiler-builtins-mem" ], diff --git a/Cargo.lock b/Cargo.lock index 402bd4003..32b898806 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,60 +47,6 @@ dependencies = [ "spinning_top", ] -[[package]] -name = "anstream" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" -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.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" -dependencies = [ - "anstyle", - "windows-sys", -] - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - [[package]] name = "arrayvec" version = "0.5.2" @@ -148,6 +94,7 @@ dependencies = [ "acpi", "align_ext", "aml", + "aster-main", "bit_field", "bitflags 1.3.2", "bitvec", @@ -201,6 +148,15 @@ dependencies = [ "spin 0.9.8", ] +[[package]] +name = "aster-main" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + [[package]] name = "aster-network" version = "0.1.0" @@ -285,17 +241,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "aster-runner" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap", - "linux-bzimage-builder", - "rand", - "xmas-elf 0.8.0", -] - [[package]] name = "aster-time" version = "0.1.0" @@ -448,52 +393,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "clap" -version = "4.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim 0.11.0", -] - -[[package]] -name = "clap_derive" -version = "4.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.49", -] - -[[package]] -name = "clap_lex" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - [[package]] name = "component" version = "0.1.0" @@ -596,7 +495,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", + "strsim", "syn 1.0.109", ] @@ -796,12 +695,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "ident_case" version = "1.0.1" @@ -1384,12 +1277,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "strsim" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" - [[package]] name = "syn" version = "1.0.109" @@ -1670,72 +1557,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -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.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "winnow" version = "0.5.7" diff --git a/Cargo.toml b/Cargo.toml index 3260c3e5d..5c6d886b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,10 +3,6 @@ name = "asterinas" version = "0.3.1" edition = "2021" -[[bin]] -name = "asterinas" -path = "kernel/main.rs" - [dependencies] aster-frame = { path = "framework/aster-frame" } aster-nix = { path = "services/aster-nix" } @@ -32,9 +28,9 @@ panic = "unwind" [workspace] members = [ - "runner", "framework/aster-frame", "framework/libs/align_ext", + "framework/libs/aster-main", "framework/libs/linux-bzimage/builder", "framework/libs/linux-bzimage/boot-params", "framework/libs/linux-bzimage/setup", @@ -61,6 +57,7 @@ members = [ exclude = [ "osdk", + "target/osdk/base", "services/libs/comp-sys/cargo-component", "services/libs/comp-sys/component", "services/libs/comp-sys/component-macro", diff --git a/Makefile b/Makefile index fccd9743c..e0ff2ab22 100644 --- a/Makefile +++ b/Makefile @@ -1,92 +1,72 @@ # SPDX-License-Identifier: MPL-2.0 -# Make varaiables and defaults, you should refer to aster-runner for more details +# The Makefile provides a way to run arbitrary tests in the kernel +# mode using the kernel command line. +# Here are the options for the auto test feature. AUTO_TEST ?= none -BOOT_METHOD ?= qemu-grub +BOOT_LOADER ?= grub BOOT_PROTOCOL ?= multiboot2 +QEMU_MACHINE ?= q35 BUILD_SYSCALL_TEST ?= 0 EMULATE_IOMMU ?= 0 ENABLE_KVM ?= 1 -GDB_CLIENT ?= 0 -GDB_SERVER ?= 0 INTEL_TDX ?= 0 -KTEST ?= 0 -KTEST_CRATES ?= all -KTEST_WHITELIST ?= SKIP_GRUB_MENU ?= 1 SYSCALL_TEST_DIR ?= /tmp RELEASE_MODE ?= 0 -# End of setting up Make varaiables +# End of auto test features. + +CARGO_OSDK_ARGS := -KERNEL_CMDLINE := SHELL="/bin/sh" LOGNAME="root" HOME="/" USER="root" PATH="/bin:/benchmark" init=/usr/bin/busybox -KERNEL_CMDLINE += ktest.whitelist="$(KTEST_WHITELIST)" -INIT_CMDLINE := sh -l ifeq ($(AUTO_TEST), syscall) BUILD_SYSCALL_TEST := 1 -KERNEL_CMDLINE += SYSCALL_TEST_DIR=$(SYSCALL_TEST_DIR) -INIT_CMDLINE += /opt/syscall_test/run_syscall_test.sh +CARGO_OSDK_ARGS += --kcmd_args="SYSCALL_TEST_DIR=$(SYSCALL_TEST_DIR)" +CARGO_OSDK_ARGS += --init_args="/opt/syscall_test/run_syscall_test.sh" endif ifeq ($(AUTO_TEST), regression) -INIT_CMDLINE += /regression/run_regression_test.sh +CARGO_OSDK_ARGS += --init_args="/regression/run_regression_test.sh" endif ifeq ($(AUTO_TEST), boot) -INIT_CMDLINE += -c exit 0 +CARGO_OSDK_ARGS += --init_args="-c exit 0" endif -CARGO_KBUILD_ARGS := -CARGO_KRUN_ARGS := -GLOBAL_RUSTC_FLAGS := - ifeq ($(RELEASE_MODE), 1) -CARGO_KBUILD_ARGS += --release -CARGO_KRUN_ARGS += --release +CARGO_OSDK_ARGS += --profile release endif ifeq ($(INTEL_TDX), 1) -CARGO_KBUILD_ARGS += --features intel_tdx -CARGO_KRUN_ARGS += --features intel_tdx +CARGO_OSDK_ARGS += --features intel_tdx endif -RUNNER_ARGS := '$(KERNEL_CMDLINE) -- $(INIT_CMDLINE)' -RUNNER_ARGS += --boot-method="$(BOOT_METHOD)" -RUNNER_ARGS += --boot-protocol="$(BOOT_PROTOCOL)" +CARGO_OSDK_ARGS += --boot.loader="$(BOOT_LOADER)" +CARGO_OSDK_ARGS += --boot.protocol="$(BOOT_PROTOCOL)" +CARGO_OSDK_ARGS += --qemu.machine="$(QEMU_MACHINE)" + +ifeq ($(QEMU_MACHINE), microvm) +CARGO_OSDK_ARGS += --select microvm +endif + +# To test the linux-efi-handover64 boot protocol, we need to use Debian's +# GRUB release, which is installed in /usr/bin in our Docker image. +ifeq ($(BOOT_PROTOCOL), linux-efi-handover64) +CARGO_OSDK_ARGS += --boot.grub-mkrescue=/usr/bin/grub-mkrescue +endif ifeq ($(EMULATE_IOMMU), 1) -RUNNER_ARGS += --emulate-iommu +CARGO_OSDK_ARGS += --select iommu endif ifeq ($(ENABLE_KVM), 1) -RUNNER_ARGS += --enable-kvm -endif - -ifeq ($(GDB_SERVER), 1) -ENABLE_KVM := 0 -RUNNER_ARGS += --halt-for-gdb -endif - -ifeq ($(GDB_CLIENT), 1) -RUNNER_ARGS += --run-gdb-client -endif - -ifeq ($(KTEST), 1) -comma := , -GLOBAL_RUSTC_FLAGS += --cfg ktest --cfg ktest=\"$(subst $(comma),\" --cfg ktest=\",$(KTEST_CRATES))\" -endif - -ifeq ($(SKIP_GRUB_MENU), 1) -RUNNER_ARGS += --skip-grub-menu +CARGO_OSDK_ARGS += --qemu.args="--enable-kvm" endif # Pass make variables to all subdirectory makes export -# Toolchain variables that are used when building the Linux setup header -export CARGO := cargo - # Maintain a list of usermode crates that can be tested with `cargo test` USERMODE_TESTABLE := \ - runner \ framework/libs/align_ext \ + framework/libs/aster-main \ framework/libs/linux-bzimage/builder \ framework/libs/linux-bzimage/boot-params \ framework/libs/ktest \ @@ -100,31 +80,45 @@ USERMODE_TESTABLE := \ services/libs/typeflags \ services/libs/typeflags-util -.PHONY: all setup build tools run test docs check clean update_initramfs +# Maintain a list of kernel crates that can be tested with `cargo osdk test` +KTEST_TESTABLE := \ + "framework/aster-frame" \ + "services/aster-nix" \ + "services/comps/block" \ + "services/comps/console" \ + "services/comps/framebuffer" \ + "services/comps/input" \ + "services/comps/network" \ + "services/comps/time" \ + "services/comps/virtio" + +.PHONY: all install_osdk build tools run test docs check clean update_initramfs all: build -setup: - @rustup component add rust-src - @rustup component add rustc-dev - @rustup component add llvm-tools-preview - @cargo install mdbook +install_osdk: + @cargo install cargo-osdk --path osdk --force build: @make --no-print-directory -C regression - @RUSTFLAGS="$(GLOBAL_RUSTC_FLAGS)" cargo kbuild $(CARGO_KBUILD_ARGS) + @cargo osdk build $(CARGO_OSDK_ARGS) tools: @cd services/libs/comp-sys && cargo install --path cargo-component run: build - @RUSTFLAGS="$(GLOBAL_RUSTC_FLAGS)" cargo krun $(CARGO_KRUN_ARGS) -- $(RUNNER_ARGS) + @cargo osdk run $(CARGO_OSDK_ARGS) test: @for dir in $(USERMODE_TESTABLE); do \ (cd $$dir && cargo test) || exit 1; \ done +ktest: + @for dir in $(KTEST_TESTABLE); do \ + (cd $$dir && cargo osdk test) || exit 1; \ + done + docs: @cargo doc # Build Rust docs @echo "" # Add a blank line @@ -135,7 +129,7 @@ format: check: ./tools/format_all.sh --check # Check Rust format issues - @cargo kclippy -- -D warnings # Make build fail if any warnings are found by rustc and clippy + @cargo osdk clippy clean: @cargo clean @@ -144,4 +138,4 @@ clean: update_initramfs: @make --no-print-directory -C regression clean - @make --no-print-directory -C regression \ No newline at end of file + @make --no-print-directory -C regression diff --git a/OSDK.toml b/OSDK.toml new file mode 100644 index 000000000..c54bf2505 --- /dev/null +++ b/OSDK.toml @@ -0,0 +1,88 @@ +kcmd_args = [ + "SHELL=/bin/sh", + "LOGNAME=root", + "HOME=/", + "USER=root", + "PATH=/bin:/benchmark", + "init=/usr/bin/busybox", +] +init_args = ["sh", "-l"] +initramfs = "regression/build/initramfs.cpio.gz" + +[boot] +protocol = "multiboot2" +loader = "grub" +ovmf = "/root/ovmf/release" + +[qemu] +machine = "q35" +drive_files = [ + ["regression/build/ext2.img", "if=none,format=raw,id=x0"], +] +args = [ + "--no-reboot", + "-m 2G", + "-nographic", + "-serial chardev:mux", + "-monitor chardev:mux", + "-chardev stdio,id=mux,mux=on,signal=off,logfile=qemu.log", + "-display none", + "-device isa-debug-exit,iobase=0xf4,iosize=0x04", + "-object filter-dump,id=filter0,netdev=net01,file=virtio-net.pcap", + "-netdev user,id=net01,hostfwd=tcp::36788-:22,hostfwd=tcp::55834-:8080", + "-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off", + "-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off", + "-device virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off", + "-device virtio-serial-pci,disable-legacy=on,disable-modern=off", + "-device virtconsole,chardev=mux", +] + +[qemu.'cfg(select="iommu")'] +machine = "q35" +drive_files = [ + ["regression/build/ext2.img", "if=none,format=raw,id=x0"], +] +args = [ + "--no-reboot", + "-m 2G", + "-nographic", + "-serial chardev:mux", + "-monitor chardev:mux", + "-chardev stdio,id=mux,mux=on,signal=off,logfile=qemu.log", + "-display none", + "-device isa-debug-exit,iobase=0xf4,iosize=0x04", + "-object filter-dump,id=filter0,netdev=net01,file=virtio-net.pcap", + "-netdev user,id=net01,hostfwd=tcp::36788-:22,hostfwd=tcp::55834-:8080", + "-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on", + "-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on", + "-device virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on", + "-device virtio-serial-pci,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on", + "-device virtconsole,chardev=mux", + "-device intel-iommu,intremap=on,device-iotlb=on", + "-device ioh3420,id=pcie.0,chassis=1", +] + +[qemu.'cfg(select="microvm")'] +machine = "microvm" +drive_files = [ + ["regression/build/ext2.img", "if=none,format=raw,id=x0"], +] +args = [ + "--no-reboot", + "-m 2G", + "-nographic", + "-serial chardev:mux", + "-monitor chardev:mux", + "-chardev stdio,id=mux,mux=on,signal=off,logfile=qemu.log", + "-display none", + "-device isa-debug-exit,iobase=0xf4,iosize=0x04", + "-object filter-dump,id=filter0,netdev=net01,file=virtio-net.pcap", + "-netdev user,id=net01,hostfwd=tcp::36788-:22,hostfwd=tcp::55834-:8080", + "-nodefaults", + "-no-user-config", + "-device virtio-blk-device,drive=x0", + "-device virtio-keyboard-device", + "-device virtio-net-device,netdev=net01", + "-device virtio-serial-device", + "-device virtconsole,chardev=mux", +] diff --git a/build.rs b/build.rs deleted file mode 100644 index cff89a754..000000000 --- a/build.rs +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 - -use std::{error::Error, path::PathBuf}; - -fn main() -> Result<(), Box> { - let target = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); - let linker_script_path = if target == "x86_64" { - PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) - .join("framework") - .join("aster-frame") - .join("src") - .join("arch") - .join("x86") - .join("linker.ld") - } else { - panic!("Unsupported target arch: {}", target); - }; - println!("cargo:rerun-if-changed={}", linker_script_path.display()); - println!("cargo:rustc-link-arg=-T{}", linker_script_path.display()); - Ok(()) -} diff --git a/framework/aster-frame/Cargo.toml b/framework/aster-frame/Cargo.toml index 79b1d39cc..694cce44b 100644 --- a/framework/aster-frame/Cargo.toml +++ b/framework/aster-frame/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] align_ext = { path = "../libs/align_ext" } +aster-main = { path = "../libs/aster-main" } bit_field = "0.10.1" bitflags = "1.3" bitvec = { version = "1.0", default-features = false, features = ["alloc"] } diff --git a/framework/aster-frame/src/boot/kcmdline.rs b/framework/aster-frame/src/boot/kcmdline.rs index 98311ade2..385d600e0 100644 --- a/framework/aster-frame/src/boot/kcmdline.rs +++ b/framework/aster-frame/src/boot/kcmdline.rs @@ -91,11 +91,6 @@ impl From<&str> for KCmdlineArg { // The main parse loop. The processing steps are arranged (not very strictly) // by the analysis over the Backus–Naur form syntax tree. for arg in split_arg(cmdline) { - // FIXME: The -kernel option in QEMU seems to add this string to the command line, which we skip for now. - if arg.starts_with("target/x86_64-custom/") { - warn!("Found kcmdline: {:?}, skipped for now.", arg); - continue; - } // Cmdline => KernelArg "--" InitArg // KernelArg => Arg "\s+" KernelArg | %empty // InitArg => Arg "\s+" InitArg | %empty @@ -116,7 +111,8 @@ impl From<&str> for KCmdlineArg { 1 => (arg_pattern[0], None), 2 => (arg_pattern[0], Some(arg_pattern[1])), _ => { - panic!("Unable to parse argument {}", arg); + warn!("Unable to parse kernel argument {}, skip for now", arg); + continue; } }; // Entry => Module "." ModuleOptionName | KernelOptionName @@ -125,7 +121,11 @@ impl From<&str> for KCmdlineArg { 1 => (None, entry_pattern[0]), 2 => (Some(entry_pattern[0]), entry_pattern[1]), _ => { - panic!("Unable to parse entry {} in argument {}", entry, arg); + warn!( + "Unable to parse entry {} in argument {}, skip for now", + entry, arg + ); + continue; } }; if let Some(modname) = node { diff --git a/framework/aster-frame/src/boot/mod.rs b/framework/aster-frame/src/boot/mod.rs index 611e7a068..8ee4c17a9 100644 --- a/framework/aster-frame/src/boot/mod.rs +++ b/framework/aster-frame/src/boot/mod.rs @@ -113,48 +113,38 @@ pub fn call_aster_main() -> ! { // The entry point of kernel code, which should be defined by the package that // uses aster-frame. extern "Rust" { - fn aster_main() -> !; + fn __aster_main() -> !; } - aster_main(); + __aster_main(); } #[cfg(ktest)] - { - use alloc::{boxed::Box, string::ToString}; - use core::any::Any; - - use crate::arch::qemu::{exit_qemu, QemuExitCode}; + unsafe { crate::init(); - let fn_catch_unwind = &(unwinding::panic::catch_unwind::<(), fn()> - as fn(fn()) -> Result<(), Box<(dyn Any + Send + 'static)>>); - // Parse the whitelist from the kernel command line. - let mut paths = None; - let args = kernel_cmdline().get_module_args("ktest"); - if let Some(args) = args { - for options in args { - match options { - kcmdline::ModuleArg::KeyVal(key, val) => { - if key.to_str().unwrap() == "whitelist" && val.to_str().unwrap() != "" { - let paths_str = val.to_str().unwrap(); - paths = Some( - paths_str - .split(',') - .map(|s| s.to_string()) - .collect::>(), - ); - } - } - _ => {} - } - } - } - use ktest::runner::{run_ktests, KtestResult}; - match run_ktests( - &crate::console::print, - fn_catch_unwind, - paths.map(|v| v.into_iter()), - ) { - KtestResult::Ok => exit_qemu(QemuExitCode::Success), - KtestResult::Failed => exit_qemu(QemuExitCode::Failed), + // The whitelists that will be generated by OSDK runner as static consts. + extern "Rust" { + static KTEST_TEST_WHITELIST: Option<&'static [&'static str]>; + static KTEST_CRATE_WHITELIST: Option<&'static [&'static str]>; } + run_ktests(KTEST_TEST_WHITELIST, KTEST_CRATE_WHITELIST); } } + +fn run_ktests(test_whitelist: Option<&[&str]>, crate_whitelist: Option<&[&str]>) -> ! { + use crate::arch::qemu::{exit_qemu, QemuExitCode}; + use alloc::{boxed::Box, string::ToString}; + use core::any::Any; + + let fn_catch_unwind = &(unwinding::panic::catch_unwind::<(), fn()> + as fn(fn()) -> Result<(), Box<(dyn Any + Send + 'static)>>); + + use ktest::runner::{run_ktests, KtestResult}; + match run_ktests( + &crate::console::print, + fn_catch_unwind, + test_whitelist.map(|s| s.iter().map(|s| s.to_string())), + crate_whitelist, + ) { + KtestResult::Ok => exit_qemu(QemuExitCode::Success), + KtestResult::Failed => exit_qemu(QemuExitCode::Failed), + }; +} diff --git a/framework/aster-frame/src/lib.rs b/framework/aster-frame/src/lib.rs index d1567bfe5..50f50298f 100644 --- a/framework/aster-frame/src/lib.rs +++ b/framework/aster-frame/src/lib.rs @@ -19,6 +19,7 @@ #![no_std] extern crate alloc; +#[cfg(ktest)] #[macro_use] extern crate ktest; #[macro_use] @@ -83,7 +84,7 @@ fn invoke_ffi_init_funcs() { } /// Simple unit tests for the ktest framework. -#[if_cfg_ktest] +#[cfg(ktest)] mod test { #[ktest] fn trivial_assertion() { diff --git a/framework/aster-frame/src/panicking.rs b/framework/aster-frame/src/panicking.rs index 7ef511b1f..206e6b144 100644 --- a/framework/aster-frame/src/panicking.rs +++ b/framework/aster-frame/src/panicking.rs @@ -23,12 +23,12 @@ use unwinding::{ panic::begin_panic, }; -fn abort() -> ! { - exit_qemu(QemuExitCode::Failed); -} - -#[panic_handler] -fn panic_handler(info: &core::panic::PanicInfo) -> ! { +/// The panic handler must be defined in the binary crate or in the crate that the binary +/// crate explicity declares by `extern crate`. We cannot let the base crate depend on the +/// framework due to prismatic dependencies. That's why we export this symbol and state the +/// panic handler in the binary crate. +#[export_name = "__aster_panic_handler"] +pub fn panic_handler(info: &core::panic::PanicInfo) -> ! { let throw_info = ktest::PanicInfo { message: info.message().unwrap().to_string(), file: info.location().unwrap().file().to_string(), @@ -46,6 +46,10 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! { abort(); } +fn abort() -> ! { + exit_qemu(QemuExitCode::Failed); +} + fn print_stack_trace() { struct CallbackData { counter: usize, diff --git a/framework/aster-frame/src/prelude.rs b/framework/aster-frame/src/prelude.rs index d56554df6..42eacab20 100644 --- a/framework/aster-frame/src/prelude.rs +++ b/framework/aster-frame/src/prelude.rs @@ -8,3 +8,7 @@ pub(crate) use alloc::{boxed::Box, sync::Arc, vec::Vec}; pub(crate) use core::any::Any; pub use crate::vm::{Paddr, Vaddr}; + +pub use crate::early_print as print; +pub use crate::early_println as println; +pub use aster_main::aster_main; diff --git a/framework/aster-frame/src/sync/atomic_bits.rs b/framework/aster-frame/src/sync/atomic_bits.rs index 76fb30836..076e17a10 100644 --- a/framework/aster-frame/src/sync/atomic_bits.rs +++ b/framework/aster-frame/src/sync/atomic_bits.rs @@ -288,7 +288,7 @@ impl fmt::Debug for AtomicBits { } } -#[if_cfg_ktest] +#[cfg(ktest)] mod test { use super::*; diff --git a/framework/aster-frame/src/vm/dma/dma_coherent.rs b/framework/aster-frame/src/vm/dma/dma_coherent.rs index fe4cbe8a0..577f33dc9 100644 --- a/framework/aster-frame/src/vm/dma/dma_coherent.rs +++ b/framework/aster-frame/src/vm/dma/dma_coherent.rs @@ -162,7 +162,7 @@ impl HasPaddr for DmaCoherent { } } -#[if_cfg_ktest] +#[cfg(ktest)] mod test { use alloc::vec; diff --git a/framework/aster-frame/src/vm/dma/dma_stream.rs b/framework/aster-frame/src/vm/dma/dma_stream.rs index e31201cac..774653562 100644 --- a/framework/aster-frame/src/vm/dma/dma_stream.rs +++ b/framework/aster-frame/src/vm/dma/dma_stream.rs @@ -195,7 +195,7 @@ impl HasPaddr for DmaStream { } } -#[if_cfg_ktest] +#[cfg(ktest)] mod test { use alloc::vec; diff --git a/framework/libs/aster-main/Cargo.toml b/framework/libs/aster-main/Cargo.toml new file mode 100644 index 000000000..482ea6015 --- /dev/null +++ b/framework/libs/aster-main/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "aster-main" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.78" +quote = "1.0.35" +syn = { version = "2.0.48", features = ["full"] } diff --git a/framework/libs/aster-main/src/lib.rs b/framework/libs/aster-main/src/lib.rs new file mode 100644 index 000000000..402b95494 --- /dev/null +++ b/framework/libs/aster-main/src/lib.rs @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MPL-2.0 + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, ItemFn}; + +#[proc_macro_attribute] +pub fn aster_main(_attr: TokenStream, item: TokenStream) -> TokenStream { + let main_fn = parse_macro_input!(item as ItemFn); + let main_fn_name = &main_fn.sig.ident; + + quote!( + #[no_mangle] + pub fn __aster_main() -> ! { + aster_frame::init(); + #main_fn_name(); + } + + #main_fn + ) + .into() +} diff --git a/framework/libs/ktest-proc-macro/src/lib.rs b/framework/libs/ktest-proc-macro/src/lib.rs index 6edbcdbbd..c272e5a6e 100644 --- a/framework/libs/ktest-proc-macro/src/lib.rs +++ b/framework/libs/ktest-proc-macro/src/lib.rs @@ -7,24 +7,7 @@ extern crate proc_macro2; use proc_macro::TokenStream; use quote::quote; use rand::{distributions::Alphanumeric, Rng}; -use syn::{parse_macro_input, Expr, Ident, ItemFn, ItemMod}; - -/// The conditional compilation attribute macro to control the compilation of test -/// modules. -#[proc_macro_attribute] -pub fn if_cfg_ktest(_attr: TokenStream, item: TokenStream) -> TokenStream { - // Assuming that the item is a module declearation, otherwise panics. - let input = parse_macro_input!(item as ItemMod); - - let crate_name = std::env::var("CARGO_PKG_NAME").unwrap(); - - let output = quote! { - #[cfg(all(ktest, any(ktest = "all", ktest = #crate_name)))] - #input - }; - - TokenStream::from(output) -} +use syn::{parse_macro_input, Expr, Ident, ItemFn}; /// The test attribute macro to mark a test function. #[proc_macro_attribute] diff --git a/framework/libs/ktest/src/lib.rs b/framework/libs/ktest/src/lib.rs index e6a2d04d0..569ab34be 100644 --- a/framework/libs/ktest/src/lib.rs +++ b/framework/libs/ktest/src/lib.rs @@ -21,8 +21,8 @@ //! module, e.g.: //! //! ```rust -//! use ktest::{ktest, if_cfg_ktest}; -//! #[if_cfg_ktest] +//! use ktest::ktest; +//! #[cfg(ktest)] //! mod test { //! #[ktest] //! fn trivial_assertion() { @@ -97,7 +97,7 @@ extern crate alloc; use alloc::{boxed::Box, string::String}; use core::result::Result; -pub use ktest_proc_macro::{if_cfg_ktest, ktest}; +pub use ktest_proc_macro::ktest; #[derive(Clone, Debug)] pub struct PanicInfo { diff --git a/framework/libs/ktest/src/runner.rs b/framework/libs/ktest/src/runner.rs index 7dc005ee0..624ddfd5e 100644 --- a/framework/libs/ktest/src/runner.rs +++ b/framework/libs/ktest/src/runner.rs @@ -3,7 +3,7 @@ //! Test runner enabling control over the tests. //! -use alloc::{string::String, vec::Vec}; +use alloc::{string::String, vec::Vec, collections::BTreeSet}; use core::format_args; use owo_colors::OwoColorize; @@ -35,7 +35,8 @@ pub enum KtestResult { pub fn run_ktests( print: &PrintFn, catch_unwind: &CatchUnwindImpl, - whitelist: Option, + test_whitelist: Option, + crate_whitelist: Option<&[&str]>, ) -> KtestResult where PrintFn: Fn(core::fmt::Arguments), @@ -48,7 +49,7 @@ where } let whitelist_trie = - whitelist.map(|paths| SuffixTrie::from_paths(paths.map(|p| KtestPath::from(&p)))); + test_whitelist.map(|paths| SuffixTrie::from_paths(paths.map(|p| KtestPath::from(&p)))); let tree = KtestTree::from_iter(KtestIter::new()); print!( @@ -56,7 +57,15 @@ where tree.nr_tot_tests(), tree.nr_tot_crates() ); + let crate_set = + crate_whitelist.map(|crates| crates.iter().copied().collect::>()); for crate_ in tree.iter() { + if let Some(crate_set) = &crate_set { + if !crate_set.contains(crate_.name()) { + print!("\n[ktest runner] skipping crate \"{}\".\n", crate_.name()); + continue; + } + } match run_crate_ktests(crate_, print, catch_unwind, &whitelist_trie) { KtestResult::Ok => {} KtestResult::Failed => return KtestResult::Failed, diff --git a/framework/libs/linux-bzimage/builder/src/lib.rs b/framework/libs/linux-bzimage/builder/src/lib.rs index 3d43a3679..6ab8a7dfa 100644 --- a/framework/libs/linux-bzimage/builder/src/lib.rs +++ b/framework/libs/linux-bzimage/builder/src/lib.rs @@ -39,26 +39,16 @@ pub enum BzImageType { /// - `target_image_path`: The path to the target bzImage. /// - `image_type`: The type of the bzImage that we are building. /// - `kernel_path`: The path to the kernel ELF. -/// - `setup_src`: The path to the setup crate. /// - `setup_tmp_out_dir`: The path to the temporary output directory for the setup binary. -pub fn make_bzimage( - target_image_path: &Path, - image_type: BzImageType, - kernel_path: &Path, - setup_src: &Path, - setup_tmp_out_dir: &Path, -) { +pub fn make_bzimage(target_image_path: &Path, image_type: BzImageType, kernel_path: &Path) { let setup = match image_type { BzImageType::Legacy32 => { - let arch = setup_src - .join("x86_64-i386_pm-none.json") + let arch = PathBuf::from("../../setup/x86_64-i386_pm-none.json") .canonicalize() .unwrap(); - build_setup_with_arch(setup_src, setup_tmp_out_dir, &SetupBuildArch::Other(arch)) - } - BzImageType::Efi64 => { - build_setup_with_arch(setup_src, setup_tmp_out_dir, &SetupBuildArch::X86_64) + build_setup_with_arch(&SetupBuildArch::Other(arch)) } + BzImageType::Efi64 => build_setup_with_arch(&SetupBuildArch::X86_64), }; let mut setup_elf = Vec::new(); @@ -186,33 +176,24 @@ fn fill_legacy_header_fields( /// Build the setup binary. /// /// It will return the path to the built setup binary. -fn build_setup_with_arch(source_dir: &Path, tmp_out_dir: &Path, arch: &SetupBuildArch) -> PathBuf { - if !tmp_out_dir.exists() { - std::fs::create_dir_all(&tmp_out_dir).unwrap(); - } - let tmp_out_dir = std::fs::canonicalize(tmp_out_dir).unwrap(); - +fn build_setup_with_arch(arch: &SetupBuildArch) -> PathBuf { // Relocations are fewer in release mode. That's why the release mode is more stable than // the debug mode. let profile = "release"; - let cargo = std::env::var("CARGO").unwrap(); - let mut cmd = std::process::Command::new(cargo); - cmd.current_dir(source_dir); + let mut cmd = std::process::Command::new("cargo"); + cmd.current_dir("../../setup"); cmd.arg("build"); if profile == "release" { cmd.arg("--release"); } - cmd.arg("--package").arg("linux-bzimage-setup"); + cmd.arg("--bin").arg("linux-bzimage-setup"); cmd.arg("--target").arg(match arch { SetupBuildArch::X86_64 => "x86_64-unknown-none", SetupBuildArch::Other(path) => path.to_str().unwrap(), }); cmd.arg("-Zbuild-std=core,alloc,compiler_builtins"); cmd.arg("-Zbuild-std-features=compiler-builtins-mem"); - // Specify the build target directory to avoid cargo running - // into a deadlock reading the workspace files. - cmd.arg("--target-dir").arg(tmp_out_dir.as_os_str()); cmd.env_remove("RUSTFLAGS"); cmd.env_remove("CARGO_ENCODED_RUSTFLAGS"); @@ -231,7 +212,7 @@ fn build_setup_with_arch(source_dir: &Path, tmp_out_dir: &Path, arch: &SetupBuil SetupBuildArch::Other(path) => path.file_stem().unwrap().to_str().unwrap(), }; - let setup_artifact = tmp_out_dir + let setup_artifact = PathBuf::from("../../setup/target") .join(arch_name) .join(profile) .join("linux-bzimage-setup"); diff --git a/framework/libs/linux-bzimage/setup/.gitignore b/framework/libs/linux-bzimage/setup/.gitignore new file mode 100644 index 000000000..364274d81 --- /dev/null +++ b/framework/libs/linux-bzimage/setup/.gitignore @@ -0,0 +1,2 @@ +# The Rust build cache for the setup crate is placed here. +target/ \ No newline at end of file diff --git a/framework/libs/linux-bzimage/setup/Cargo.toml b/framework/libs/linux-bzimage/setup/Cargo.toml index 0e06e9b59..23b0f633e 100644 --- a/framework/libs/linux-bzimage/setup/Cargo.toml +++ b/framework/libs/linux-bzimage/setup/Cargo.toml @@ -3,6 +3,10 @@ name = "linux-bzimage-setup" version = "0.1.0" edition = "2021" +[[bin]] +name = "linux-bzimage-setup" +path = "src/main.rs" + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/kernel/main.rs b/kernel/main.rs deleted file mode 100644 index 3752bdbc4..000000000 --- a/kernel/main.rs +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 - -#![no_std] -#![no_main] -// The `export_name` attribute for the `aster_main` entrypoint requires the removal of safety check. -// Please be aware that the kernel is not allowed to introduce any other unsafe operations. -// #![forbid(unsafe_code)] -extern crate aster_frame; - -use aster_frame::early_println; - -#[export_name = "aster_main"] -pub fn main() -> ! { - aster_frame::init(); - early_println!("[kernel] finish init aster_frame"); - component::init_all(component::parse_metadata!()).unwrap(); - aster_nix::init(); - aster_nix::run_first_process(); -} diff --git a/osdk/.gitignore b/osdk/.gitignore index 73fab072c..2c54a7699 100644 --- a/osdk/.gitignore +++ b/osdk/.gitignore @@ -8,3 +8,6 @@ target/ # MSVC Windows builds of rustc generate these, which store debugging information *.pdb + +# Integration test generated files +workspace_* \ No newline at end of file diff --git a/osdk/Cargo.lock b/osdk/Cargo.lock index 09ab89cde..d41784003 100644 --- a/osdk/Cargo.lock +++ b/osdk/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" dependencies = [ "anstyle", "anstyle-parse", @@ -27,9 +27,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "assert_cmd" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00ad3f3a942eee60335ab4342358c161ee296829e0d16ff42fc1d6cb07815467" +checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8" dependencies = [ "anstyle", "bstr", @@ -74,6 +74,12 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.4.2" @@ -91,6 +97,26 @@ dependencies = [ "serde", ] +[[package]] +name = "bytemuck" +version = "1.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "cargo-osdk" version = "0.1.5" @@ -100,6 +126,7 @@ dependencies = [ "env_logger", "indexmap", "lazy_static", + "linux-bzimage-builder", "log", "regex", "serde", @@ -110,9 +137,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.17" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80932e03c33999b9235edb8655bc9df3204adc9887c2f95b50cb1deb9fd54253" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", "clap_derive", @@ -120,9 +147,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.17" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c0db58c659eef1c73e444d298c27322a1b52f6927d2ad470c0c0f96fa7b8fa" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstream", "anstyle", @@ -132,9 +159,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", "proc-macro2", @@ -144,9 +171,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" @@ -168,9 +195,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "env_filter" @@ -184,9 +211,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eeb342678d785662fd2514be38c459bb925f02b68dd2a3e0f21d7ef82d979dd" +checksum = "6c012a26a7f605efc424dd53697843a72be7dc86ad2d01f7814337794a12231d" dependencies = [ "anstream", "anstyle", @@ -240,9 +267,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "indexmap" -version = "2.2.1" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown", @@ -262,9 +289,19 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "linux-bzimage-builder" +version = "0.1.0" +dependencies = [ + "bitflags 1.3.2", + "bytemuck", + "serde", + "xmas-elf", +] [[package]] name = "linux-raw-sys" @@ -319,9 +356,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -349,9 +386,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -366,11 +403,11 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", @@ -379,24 +416,24 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "serde" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", @@ -405,9 +442,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -425,15 +462,15 @@ dependencies = [ [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "syn" -version = "2.0.48" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ "proc-macro2", "quote", @@ -448,9 +485,9 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "toml" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" dependencies = [ "indexmap", "serde", @@ -470,9 +507,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" dependencies = [ "indexmap", "serde", @@ -583,9 +620,24 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.34" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +checksum = "7a4191c47f15cc3ec71fcb4913cb83d58def65dd3787610213c649283b5ce178" dependencies = [ "memchr", ] + +[[package]] +name = "xmas-elf" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42c49817e78342f7f30a181573d82ff55b88a35f86ccaf07fc64b3008f56d1c6" +dependencies = [ + "zero", +] + +[[package]] +name = "zero" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fe21bcc34ca7fe6dd56cc2cb1261ea59d6b93620215aefb5ea6032265527784" diff --git a/osdk/Cargo.toml b/osdk/Cargo.toml index 976e969e5..c3e1f93c3 100644 --- a/osdk/Cargo.toml +++ b/osdk/Cargo.toml @@ -13,6 +13,7 @@ clap = { version = "4.4.17", features = ["cargo", "derive"] } env_logger = "0.11.0" indexmap = "2.2.1" lazy_static = "1.4.0" +linux-bzimage-builder = { path = "../framework/libs/linux-bzimage/builder" } log = "0.4.20" regex = "1.10.3" serde = { version = "1.0.195", features = ["derive"] } diff --git a/osdk/README.md b/osdk/README.md index bc234c44c..d5507c63e 100644 --- a/osdk/README.md +++ b/osdk/README.md @@ -14,19 +14,15 @@ OSDK (short for Operating System Development Kit) is designed to simplify the de Currenly, `cargo-osdk` only supports x86_64 ubuntu system. -To run a kernel with QEMU, `cargo-osdk` requires the following tools to be installed: +`cargo-osdk` requires the following tools to be installed: - Rust >= 1.75.0 -- gcc -- qemu-system-x86_64 -- grub-mkrescue -- ovmf -- xorriso +- Gcc compiler About how to install Rust, you can refer to the [official site](https://www.rust-lang.org/tools/install). -Other tools can be installed by +Gcc compiler can be installed by ```bash -apt install build-essential grub2-common qemu-system-x86 ovmf xorriso +apt install build-essential ``` #### Install @@ -47,6 +43,17 @@ cargo install --force cargo-osdk Here we provide a simple demo to demonstrate how to create and run a simple kernel with `cargo-osdk`. +Suppose you are on a x86_64 ubuntu machine, to run a kernel with QEMU, the following tools should be installed: +- qemu-system-x86_64 +- grub-mkrescue +- ovmf +- xorriso + +If these tools are missing, they can be installed by +```bash +apt install grub2-common qemu-system-x86 ovmf xorriso +``` + With `cargo-osdk`, a kernel project can be created by one command ```bash cargo osdk new --kernel my-first-os @@ -97,17 +104,15 @@ ovmf = "/usr/bin/ovmf" # <7> path = "/usr/bin/qemu-system-x86_64" # <8> machine = "q35" # <9> args = [ # <10> - "-enable-kvm", + "--enable-kvm", "-m 2G", - "-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off" + "--device virtio-keyboard-pci,disable-legacy=on,disable-modern=off" ] [qemu.'cfg(feature="iommu")'] # <11> path = "/usr/local/sbin/qemu-kvm" # <8> machine = "q35" # <9> args = [ # <10> - "-enable-kvm", - "-m 2G", "-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on", "-device intel-iommu,intremap=on,device-iotlb=on" ] @@ -139,7 +144,18 @@ Optional. Default is `q35`. The allowed values are `q35` and `microvm`. 10. Additional arguments passed to QEMU. Optional. The default value is empty. -Each argument should be in the form `KEY VALUE` (separated by space), or `KEY` if no value is required. Some keys can appear multiple times (e.g., `-device`, `-netdev`), while other keys can appear at most once. Certain keys, such as `-cpu` and `-machine`, are not allowed to be set here as they may conflict with the internal settings of `cargo-osdk`. +Each argument should be in the form `KEY VALUE` (separated by space), or `KEY` if no value is required. Some keys can appear multiple times (e.g., `--device`, `--netdev`), while other keys can appear at most once. Certain keys, such as `-cpu` and `-machine`, are not allowed to be set here as they may conflict with the internal settings of `cargo-osdk`. 11. Conditional QEMU settings. Optional. The default value is empty. Conditional QEMU settings allow for a condition to be specified after `qemu`. Currently, `cargo-osdk` only supports the condition `cfg(feature="FEATURE")`, which activates the QEMU settings only if the `FEATURE` is set. The `FEATURE` must be defined in the project's `Cargo.toml`. At most one conditional setting can be activated at a time. If multiple conditional settings can be activated simultaneously, `cargo-osdk` will report an error. In the future, `cargo-osdk` will support all possible conditions that [Rust conditional compilation](https://doc.rust-lang.org/reference/conditional-compilation.html) supports. + + +### The framekernel architecture + +The architecture divides the OS development into two distinct realms: the safe world and the unsafe world. In the safe world, only safe Rust code is allowed, while the unsafe world can tap into the power of the unsafe keyword. At the heart of the unsafe world lies `aster-frame`, a compact framework with limited functionalities. It encapsulates essential OS operations such as booting, physical memory management, context switching, and more. + +With `aster-frame` as the foundation, higher-level OS functionalities like process management, file systems, network protocols, and even device drivers can be built upon it using only safe Rust. This segregation ensures that critical operations are handled securely in the unsafe realm while allowing for the development of complex and feature-rich OS components. + +In addition to OS functionalities, `aster-frame` also provides development utilities, including kernel mode unit test support. The shared base of crates built on `aster-frame` allows for easy reuse and facilitates the creation of sophisticated operating systems with rich features. + +Overall, this architectural approach promotes safety and modularity, empowering developers to build robust and advanced OS systems using Rust. diff --git a/osdk/src/commands/template/rust-toolchain.toml.template b/osdk/rust-toolchain.toml similarity index 100% rename from osdk/src/commands/template/rust-toolchain.toml.template rename to osdk/rust-toolchain.toml diff --git a/osdk/src/base_crate/main.rs.template b/osdk/src/base_crate/main.rs.template new file mode 100644 index 000000000..fa2e00d3b --- /dev/null +++ b/osdk/src/base_crate/main.rs.template @@ -0,0 +1,12 @@ +#![no_std] +#![no_main] + +extern crate #TARGET_NAME#; + +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + extern "Rust" { + pub fn __aster_panic_handler(info: &core::panic::PanicInfo) -> !; + } + unsafe { __aster_panic_handler(info); } +} diff --git a/osdk/src/base_crate/mod.rs b/osdk/src/base_crate/mod.rs new file mode 100644 index 000000000..9558d43b5 --- /dev/null +++ b/osdk/src/base_crate/mod.rs @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! The base crate is the OSDK generated crate that is ultimately built by cargo. +//! It will depend on the kernel crate. +//! + +use std::path::Path; +use std::process::Command; +use std::str::FromStr; +use std::{fs, process}; + +use crate::error::Errno; +use crate::error_msg; + +pub fn new_base_crate( + base_crate_path: impl AsRef, + dep_crate_name: &str, + dep_crate_path: impl AsRef, +) { + if base_crate_path.as_ref().exists() { + std::fs::remove_dir_all(&base_crate_path).unwrap(); + } + + let mut cmd = Command::new("cargo"); + cmd.arg("new").arg("--bin").arg(base_crate_path.as_ref()); + cmd.arg("--vcs").arg("none"); + + if !cmd.status().unwrap().success() { + error_msg!( + "Failed to create base crate at: {:#?}", + base_crate_path.as_ref() + ); + process::exit(Errno::CreateBaseCrate as _); + } + + // Set the current directory to the target osdk directory + let original_dir = std::env::current_dir().unwrap(); + std::env::set_current_dir(&base_crate_path).unwrap(); + + // Add linker.ld file + let linker_ld = include_str!("x86_64-custom.ld.template"); + fs::write("x86_64-custom.ld", linker_ld).unwrap(); + + // Add target json file + let target_json = include_str!("x86_64-custom.json.template"); + fs::write("x86_64-custom.json", target_json).unwrap(); + + // Overrite the main.rs file + let main_rs = include_str!("main.rs.template"); + // Replace all occurence of `#TARGET_NAME#` with the `dep_crate_name` + let main_rs = main_rs.replace("#TARGET_NAME#", &dep_crate_name.replace("-", "_")); + fs::write("src/main.rs", main_rs).unwrap(); + + // Add dependencies to the Cargo.toml + add_manifest_dependency(dep_crate_name, dep_crate_path); + + // Copy the manifest configurations from the target crate to the base crate + copy_manifest_configurations(base_crate_path); + + // Get back to the original directory + std::env::set_current_dir(&original_dir).unwrap(); +} + +fn add_manifest_dependency(crate_name: &str, crate_path: impl AsRef) { + let mainfest_path = "Cargo.toml"; + + let mut manifest: toml::Table = { + let content = fs::read_to_string(mainfest_path).unwrap(); + toml::from_str(&content).unwrap() + }; + + let dependencies = manifest.get_mut("dependencies").unwrap(); + + let dep = toml::Table::from_str(&format!( + "{} = {{ path = \"{}\"}}", + crate_name, + crate_path.as_ref().display() + )) + .unwrap(); + dependencies.as_table_mut().unwrap().extend(dep); + + let content = toml::to_string(&manifest).unwrap(); + fs::write(mainfest_path, content).unwrap(); +} + +fn copy_manifest_configurations(target_crate_path: impl AsRef) { + let target_manifest_path = target_crate_path.as_ref().join("Cargo.toml"); + let manifest_path = "Cargo.toml"; + + let target_manifest: toml::Table = { + let content = fs::read_to_string(target_manifest_path).unwrap(); + toml::from_str(&content).unwrap() + }; + + let mut manifest: toml::Table = { + let content = fs::read_to_string(manifest_path).unwrap(); + toml::from_str(&content).unwrap() + }; + + // Copy the profile configurations + let profile = target_manifest.get("profile"); + if let Some(profile) = profile { + manifest.insert("profile".to_string(), profile.clone()); + } + + let content = toml::to_string(&manifest).unwrap(); + fs::write(manifest_path, content).unwrap(); +} diff --git a/osdk/src/commands/template/x86_64-custom.json.template b/osdk/src/base_crate/x86_64-custom.json.template similarity index 84% rename from osdk/src/commands/template/x86_64-custom.json.template rename to osdk/src/base_crate/x86_64-custom.json.template index a68eefb4b..a5fadfaa0 100644 --- a/osdk/src/commands/template/x86_64-custom.json.template +++ b/osdk/src/base_crate/x86_64-custom.json.template @@ -11,6 +11,11 @@ "executables": true, "linker-flavor": "ld.lld", "linker": "rust-lld", + "pre-link-args": { + "ld.lld": [ + "--script=x86_64-custom.ld" + ] + }, "disable-redzone": true, "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float" } \ No newline at end of file diff --git a/framework/aster-frame/src/arch/x86/linker.ld b/osdk/src/base_crate/x86_64-custom.ld.template similarity index 100% rename from framework/aster-frame/src/arch/x86/linker.ld rename to osdk/src/base_crate/x86_64-custom.ld.template diff --git a/osdk/src/bin.rs b/osdk/src/bin.rs new file mode 100644 index 000000000..b748088db --- /dev/null +++ b/osdk/src/bin.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MPL-2.0 + +use std::path::PathBuf; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AsterBin { + pub path: PathBuf, + pub typ: AsterBinType, + pub version: String, + pub sha256sum: String, + pub stripped: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum AsterBinType { + Elf(AsterElfMeta), + BzImage(AsterBzImageMeta), +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AsterElfMeta { + pub has_linux_header: bool, + pub has_pvh_header: bool, + pub has_multiboot_header: bool, + pub has_multiboot2_header: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AsterBzImageMeta { + pub support_legacy32_boot: bool, + pub support_efi_boot: bool, + pub support_efi_handover: bool, +} diff --git a/osdk/src/bundle.rs b/osdk/src/bundle.rs new file mode 100644 index 000000000..1a9f9e68d --- /dev/null +++ b/osdk/src/bundle.rs @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: MPL-2.0 + +use std::path::{Path, PathBuf}; +use std::process::Command; + +use crate::bin::AsterBin; +use crate::cli::CargoArgs; +use crate::config_manager::{ + boot::Boot, + qemu::{Qemu, QemuMachine}, + RunConfig, +}; +use crate::vm_image::AsterVmImage; +use crate::{error::Errno, error_msg}; + +/// The osdk bundle artifact that stores as `bundle` directory. +/// +/// This `Bundle` struct is used to track a bundle on a filesystem. Every modification to the bundle +/// would result in file system writes. But the bundle will not be removed from the file system when +/// the `Bundle` is dropped. +pub struct Bundle { + manifest: BundleManifest, + path: PathBuf, +} + +impl Bundle { + pub fn new(manifest: BundleManifest, path: impl AsRef) -> Self { + std::fs::create_dir_all(path.as_ref()).unwrap(); + let created = Self { + manifest, + path: path.as_ref().to_path_buf(), + }; + created.write_manifest_content(); + created + } + + // FIXME: the load function should be used when implementing build cache, but it is not + // implemented yet. + #[allow(dead_code)] + pub fn load(path: impl AsRef) -> Self { + let manifest_file_path = path.as_ref().join("bundle.toml"); + let manifest_file_content = std::fs::read_to_string(&manifest_file_path).unwrap(); + let manifest: BundleManifest = toml::from_str(&manifest_file_content).unwrap(); + // TODO: check integrity of the loaded bundle. + Self { + manifest, + path: path.as_ref().to_path_buf(), + } + } + + pub fn can_run_with_config(&self, config: &RunConfig) -> bool { + // TODO: This pairwise comparison will result in some false negatives. We may + // fix it by pondering upon each fields with more care. + self.manifest.kcmd_args == config.manifest.kcmd_args + && self.manifest.initramfs == config.manifest.initramfs + && self.manifest.boot == config.manifest.boot + && self.manifest.qemu == config.manifest.qemu + && self.manifest.cargo_args == config.cargo_args + } + + pub fn run(&self, config: &RunConfig) { + if !self.can_run_with_config(config) { + error_msg!("The bundle is not compatible with the run configuration"); + std::process::exit(Errno::RunBundle as _); + } + let mut qemu_cmd = Command::new(config.manifest.qemu.path.clone().unwrap()); + // FIXME: Arguments like "-m 2G" sould be separated into "-m" and "2G". This + // is a dirty hack to make it work. Anything like space in the paths will + // break this. + for arg in &config.manifest.qemu.args { + for part in arg.split_whitespace() { + qemu_cmd.arg(part); + } + } + match config.manifest.qemu.machine { + QemuMachine::Microvm => { + qemu_cmd.arg("-machine").arg("microvm"); + let Some(ref aster_bin) = self.manifest.aster_bin else { + error_msg!("Kernel ELF binary is required for Microvm"); + std::process::exit(Errno::RunBundle as _); + }; + qemu_cmd.arg("-kernel").arg(self.path.join(&aster_bin.path)); + let Some(ref initramfs) = config.manifest.initramfs else { + error_msg!("Initramfs is required for Microvm"); + std::process::exit(Errno::RunBundle as _); + }; + qemu_cmd.arg("-initrd").arg(initramfs); + qemu_cmd + .arg("-append") + .arg(config.manifest.kcmd_args.join(" ")); + } + QemuMachine::Q35 => { + qemu_cmd.arg("-machine").arg("q35,kernel-irqchip=split"); + let Some(ref vm_image) = self.manifest.vm_image else { + error_msg!("VM image is required for QEMU booting"); + std::process::exit(Errno::RunBundle as _); + }; + qemu_cmd.arg("-cdrom").arg(self.path.join(&vm_image.path)); + if let Some(ovmf) = &config.manifest.boot.ovmf { + qemu_cmd.arg("-drive").arg(format!( + "if=pflash,format=raw,unit=0,readonly=on,file={}", + ovmf.join("OVMF_CODE.fd").display() + )); + qemu_cmd.arg("-drive").arg(format!( + "if=pflash,format=raw,unit=1,file={}", + ovmf.join("OVMF_VARS.fd").display() + )); + } + } + }; + qemu_cmd.arg("-cpu").arg("Icelake-Server,+x2apic"); + + for drive_file in &config.manifest.qemu.drive_files { + qemu_cmd.arg("-drive").arg(format!( + "file={},{}", + drive_file.path.display(), + drive_file.append, + )); + } + + let exit_status = qemu_cmd.status().unwrap(); + if !exit_status.success() { + // FIXME: Exit code manipulation is not needed when using non-x86 QEMU + let qemu_exit_code = exit_status.code().unwrap(); + let kernel_exit_code = qemu_exit_code >> 1; + match kernel_exit_code { + 0x10 /*aster_frame::QemuExitCode::Success*/ => { std::process::exit(0); }, + 0x20 /*aster_frame::QemuExitCode::Failed*/ => { std::process::exit(1); }, + _ /* unknown, e.g., a triple fault */ => { std::process::exit(2) }, + } + } + } + + pub fn add_vm_image(&mut self, vm_image: &AsterVmImage) { + if self.manifest.vm_image.is_some() { + panic!("vm_image already exists"); + } + let file_name = vm_image.path.file_name().unwrap(); + let copied_path = self.path.join(file_name); + std::fs::copy(&vm_image.path, &copied_path).unwrap(); + self.manifest.vm_image = Some(AsterVmImage { + path: file_name.into(), + typ: vm_image.typ.clone(), + aster_version: vm_image.aster_version.clone(), + sha256sum: vm_image.sha256sum.clone(), + }); + self.write_manifest_content(); + } + + pub fn add_aster_bin(&mut self, aster_bin: &AsterBin) { + if self.manifest.aster_bin.is_some() { + panic!("aster_bin already exists"); + } + let file_name = aster_bin.path.file_name().unwrap(); + let copied_path = self.path.join(file_name); + std::fs::copy(&aster_bin.path, &copied_path).unwrap(); + self.manifest.aster_bin = Some(AsterBin { + path: file_name.into(), + typ: aster_bin.typ.clone(), + version: aster_bin.version.clone(), + sha256sum: aster_bin.sha256sum.clone(), + stripped: aster_bin.stripped.clone(), + }); + self.write_manifest_content(); + } + + fn write_manifest_content(&self) { + let manifest_file_content = toml::to_string(&self.manifest).unwrap(); + let manifest_file_path = self.path.join("bundle.toml"); + std::fs::write(&manifest_file_path, manifest_file_content).unwrap(); + } +} + +/// The osdk bundle artifact manifest that stores as `bundle.toml`. +#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct BundleManifest { + #[serde(default)] + pub kcmd_args: Vec, + #[serde(default)] + pub initramfs: Option, + #[serde(default)] + pub aster_bin: Option, + #[serde(default)] + pub vm_image: Option, + #[serde(default)] + pub boot: Boot, + #[serde(default)] + pub qemu: Qemu, + #[serde(default)] + pub cargo_args: CargoArgs, +} diff --git a/osdk/src/cli.rs b/osdk/src/cli.rs index 1e5ee64a4..b0d260ddf 100644 --- a/osdk/src/cli.rs +++ b/osdk/src/cli.rs @@ -5,7 +5,10 @@ use std::path::PathBuf; use clap::{crate_version, Args, Parser}; use crate::{ - commands::{execute_check_command, execute_clippy_command, execute_new_command}, + commands::{ + execute_build_command, execute_check_command, execute_clippy_command, execute_new_command, + execute_run_command, execute_test_command, + }, config_manager::{ boot::{BootLoader, BootProtocol}, qemu::QemuMachine, @@ -24,21 +27,15 @@ pub fn main() { OsdkSubcommand::New(args) => execute_new_command(args), OsdkSubcommand::Build(build_args) => { let build_config = BuildConfig::parse(build_args); - println!("{:?}", build_config); - // TODO: execute_build_command(build_config); - // todo!("execute build command"); + execute_build_command(&build_config); } OsdkSubcommand::Run(run_args) => { let run_config = RunConfig::parse(run_args); - println!("{:?}", run_config); - // TODO: execute_run_command(run_config); - // todo!("execute run command"); + execute_run_command(&run_config); } OsdkSubcommand::Test(test_args) => { let test_config = TestConfig::parse(test_args); - println!("{:?}", test_config); - // TODO: execute_test_command(test_config); - // todo!("execute test command"); + execute_test_command(&test_config); } OsdkSubcommand::Check => execute_check_command(), OsdkSubcommand::Clippy => execute_clippy_command(), @@ -114,20 +111,26 @@ pub struct TestArgs { pub osdk_args: OsdkArgs, } -#[derive(Debug, Args, Default)] +#[derive(Debug, Args, Default, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct CargoArgs { #[arg( long, - help = "Build artifacts in release mode", - default_value = "false" + help = "The Cargo build profile (built-in candidates are 'debug', 'release' and 'dev')", + default_value = "dev" )] - pub release: bool, + pub profile: String, #[arg(long, value_name = "FEATURES", help = "List of features to activate")] pub features: Vec, } #[derive(Debug, Args)] pub struct OsdkArgs { + #[arg( + long = "select", + help = "Select the specific configuration provided in the OSDK manifest", + value_name = "SELECTION" + )] + pub select: Option, #[arg( long = "kcmd_args", help = "Command line arguments for guest kernel", diff --git a/osdk/src/commands/build/bin.rs b/osdk/src/commands/build/bin.rs new file mode 100644 index 000000000..d4f8eb24a --- /dev/null +++ b/osdk/src/commands/build/bin.rs @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: MPL-2.0 + +use linux_bzimage_builder::{make_bzimage, BzImageType}; + +use std::path::Path; +use std::process::Command; +use std::{ + fs::OpenOptions, + io::{Seek, SeekFrom, Write}, +}; + +use crate::bin::{AsterBin, AsterBinType, AsterBzImageMeta, AsterElfMeta}; +use crate::config_manager::boot::BootProtocol; +use crate::utils::get_current_crate_info; + +pub fn make_install_bzimage( + install_dir: impl AsRef, + aster_elf: &AsterBin, + protocol: &BootProtocol, +) -> AsterBin { + let target_name = get_current_crate_info().name; + let image_type = match protocol { + BootProtocol::LinuxLegacy32 => BzImageType::Legacy32, + BootProtocol::LinuxEfiHandover64 => BzImageType::Efi64, + _ => unreachable!(), + }; + // Make the `bzImage`-compatible kernel image and place it in the boot directory. + let install_path = install_dir.as_ref().join(&target_name); + info!("Building bzImage"); + make_bzimage(&install_path, image_type, &aster_elf.path); + + AsterBin { + path: install_path, + typ: AsterBinType::BzImage(AsterBzImageMeta { + support_legacy32_boot: matches!(protocol, BootProtocol::LinuxLegacy32), + support_efi_boot: false, + support_efi_handover: matches!(protocol, BootProtocol::LinuxEfiHandover64), + }), + version: aster_elf.version.clone(), + sha256sum: "TODO".to_string(), + stripped: aster_elf.stripped, + } +} + +pub fn strip_elf_for_qemu(install_dir: impl AsRef, elf: &AsterBin) -> AsterBin { + let stripped_elf_path = { + let elf_name = elf.path.file_name().unwrap().to_str().unwrap().to_string(); + install_dir.as_ref().join(elf_name + ".stripped.elf") + }; + + // We use rust-strip to reduce the kernel image size. + let status = Command::new("rust-strip") + .arg(&elf.path) + .arg("-o") + .arg(stripped_elf_path.as_os_str()) + .status(); + + match status { + Ok(status) => { + if !status.success() { + panic!("Failed to strip kernel elf."); + } + } + Err(err) => match err.kind() { + std::io::ErrorKind::NotFound => panic!( + "`rust-strip` command not found. Please + try `cargo install cargo-binutils` and then rerun." + ), + _ => panic!("Strip kernel elf failed, err:{:#?}", err), + }, + } + + // Because QEMU denies a x86_64 multiboot ELF file (GRUB2 accept it, btw), + // modify `em_machine` to pretend to be an x86 (32-bit) ELF image, + // + // https://github.com/qemu/qemu/blob/950c4e6c94b15cd0d8b63891dddd7a8dbf458e6a/hw/i386/multiboot.c#L197 + // Set EM_386 (0x0003) to em_machine. + let mut file = OpenOptions::new() + .read(true) + .write(true) + .open(&stripped_elf_path) + .unwrap(); + + let bytes: [u8; 2] = [0x03, 0x00]; + + file.seek(SeekFrom::Start(18)).unwrap(); + file.write_all(&bytes).unwrap(); + file.flush().unwrap(); + + AsterBin { + path: stripped_elf_path, + typ: AsterBinType::Elf(AsterElfMeta { + has_linux_header: false, + has_pvh_header: false, + has_multiboot_header: true, + has_multiboot2_header: true, + }), + version: elf.version.clone(), + sha256sum: "TODO".to_string(), + stripped: true, + } +} diff --git a/runner/grub/grub.cfg.template b/osdk/src/commands/build/grub.cfg.template similarity index 87% rename from runner/grub/grub.cfg.template rename to osdk/src/commands/build/grub.cfg.template index a17634136..17d80c6e2 100644 --- a/runner/grub/grub.cfg.template +++ b/osdk/src/commands/build/grub.cfg.template @@ -9,6 +9,6 @@ set timeout=#GRUB_TIMEOUT# menuentry 'asterinas' { #GRUB_CMD_KERNEL# #KERNEL# #KERNEL_COMMAND_LINE# - #GRUB_CMD_INITRAMFS# /boot/initramfs.cpio.gz + #GRUB_CMD_INITRAMFS# boot } diff --git a/osdk/src/commands/build/grub.rs b/osdk/src/commands/build/grub.rs new file mode 100644 index 000000000..a1ff2057a --- /dev/null +++ b/osdk/src/commands/build/grub.rs @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: MPL-2.0 + +use std::fs; +use std::path::{Path, PathBuf}; + +use crate::bin::AsterBin; +use crate::config_manager::{boot::BootProtocol, BuildConfig}; +use crate::utils::get_current_crate_info; +use crate::vm_image::{AsterGrubIsoImageMeta, AsterVmImage, AsterVmImageType}; + +use super::bin::make_install_bzimage; + +pub fn create_bootdev_image( + target_dir: impl AsRef, + aster_bin: &AsterBin, + initramfs_path: Option>, + config: &BuildConfig, +) -> AsterVmImage { + let target_name = get_current_crate_info().name; + let iso_root = &target_dir.as_ref().join("iso_root"); + let protocol = &config.manifest.boot.protocol; + + // Clear or make the iso dir. + if iso_root.exists() { + fs::remove_dir_all(&iso_root).unwrap(); + } + fs::create_dir_all(iso_root.join("boot").join("grub")).unwrap(); + + // Copy the initramfs to the boot directory. + if let Some(init_path) = &initramfs_path { + fs::copy( + init_path.as_ref().to_str().unwrap(), + iso_root.join("boot").join("initramfs.cpio.gz"), + ) + .unwrap(); + } + + // Make the kernel image and place it in the boot directory. + match protocol { + BootProtocol::LinuxLegacy32 | BootProtocol::LinuxEfiHandover64 => { + make_install_bzimage(&iso_root.join("boot"), aster_bin, protocol); + } + BootProtocol::Multiboot | BootProtocol::Multiboot2 => { + // Copy the kernel image to the boot directory. + let target_path = iso_root.join("boot").join(&target_name); + fs::copy(&aster_bin.path, &target_path).unwrap(); + } + }; + + // Write the grub.cfg file + let initramfs_in_image = if initramfs_path.is_some() { + Some("/boot/initramfs.cpio.gz".to_string()) + } else { + None + }; + let grub_cfg = generate_grub_cfg( + &config.manifest.kcmd_args.join(" "), + true, + initramfs_in_image, + protocol, + ); + let grub_cfg_path = iso_root.join("boot").join("grub").join("grub.cfg"); + fs::write(&grub_cfg_path, grub_cfg).unwrap(); + + // Make the boot device CDROM image using `grub-mkrescue`. + let iso_path = &target_dir.as_ref().join(target_name.to_string() + ".iso"); + let grub_mkrescue_bin = &config.manifest.boot.grub_mkrescue.clone().unwrap(); + let mut grub_mkrescue_cmd = std::process::Command::new(grub_mkrescue_bin.as_os_str()); + grub_mkrescue_cmd + .arg(iso_root.as_os_str()) + .arg("-o") + .arg(iso_path); + if !grub_mkrescue_cmd.status().unwrap().success() { + panic!("Failed to run {:#?}.", grub_mkrescue_cmd); + } + + AsterVmImage { + path: iso_path.clone(), + typ: AsterVmImageType::GrubIso(AsterGrubIsoImageMeta { + grub_version: get_grub_mkrescue_version(grub_mkrescue_bin), + }), + aster_version: aster_bin.version.clone(), + sha256sum: "TODO".to_string(), + } +} + +fn generate_grub_cfg( + kcmdline: &str, + skip_grub_menu: bool, + initramfs_path: Option, + protocol: &BootProtocol, +) -> String { + let target_name = get_current_crate_info().name; + let grub_cfg = include_str!("grub.cfg.template").to_string(); + + // Delete the first two lines that notes the file a template file. + let grub_cfg = grub_cfg.lines().skip(2).collect::>().join("\n"); + // Set the timout style and timeout. + let grub_cfg = grub_cfg + .replace( + "#GRUB_TIMEOUT_STYLE#", + if skip_grub_menu { "hidden" } else { "menu" }, + ) + .replace("#GRUB_TIMEOUT#", if skip_grub_menu { "0" } else { "1" }); + // Replace all occurrences of "#KERNEL_COMMAND_LINE#" with the desired value. + let grub_cfg = grub_cfg.replace("#KERNEL_COMMAND_LINE#", kcmdline); + // Replace the grub commands according to the protocol selected. + let aster_bin_path_on_device = PathBuf::from("/boot") + .join(&target_name) + .into_os_string() + .into_string() + .unwrap(); + let grub_cfg = match protocol { + BootProtocol::Multiboot => grub_cfg + .replace("#GRUB_CMD_KERNEL#", "multiboot") + .replace("#KERNEL#", &aster_bin_path_on_device) + .replace( + "#GRUB_CMD_INITRAMFS#", + &if let Some(p) = &initramfs_path { + "module --nounzip ".to_owned() + p + } else { + "".to_owned() + }, + ), + BootProtocol::Multiboot2 => grub_cfg + .replace("#GRUB_CMD_KERNEL#", "multiboot2") + .replace("#KERNEL#", &aster_bin_path_on_device) + .replace( + "#GRUB_CMD_INITRAMFS#", + &if let Some(p) = &initramfs_path { + "module2 --nounzip ".to_owned() + p + } else { + "".to_owned() + }, + ), + BootProtocol::LinuxLegacy32 | BootProtocol::LinuxEfiHandover64 => grub_cfg + .replace("#GRUB_CMD_KERNEL#", "linux") + .replace("#KERNEL#", &aster_bin_path_on_device) + .replace( + "#GRUB_CMD_INITRAMFS#", + &if let Some(p) = &initramfs_path { + "initrd ".to_owned() + p + } else { + "".to_owned() + }, + ), + }; + + grub_cfg +} + +fn get_grub_mkrescue_version(grub_mkrescue: &PathBuf) -> String { + let mut cmd = std::process::Command::new(grub_mkrescue); + cmd.arg("--version"); + let output = cmd.output().unwrap(); + String::from_utf8(output.stdout).unwrap() +} diff --git a/osdk/src/commands/build/mod.rs b/osdk/src/commands/build/mod.rs new file mode 100644 index 000000000..1a9f5b77d --- /dev/null +++ b/osdk/src/commands/build/mod.rs @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: MPL-2.0 + +mod bin; +mod grub; + +use std::path::{Path, PathBuf}; +use std::process; +use std::str::FromStr; + +use bin::strip_elf_for_qemu; + +use crate::base_crate::new_base_crate; +use crate::bin::{AsterBin, AsterBinType, AsterElfMeta}; +use crate::bundle::{Bundle, BundleManifest}; +use crate::cli::CargoArgs; +use crate::config_manager::{qemu::QemuMachine, BuildConfig}; +use crate::utils::{get_current_crate_info, get_target_directory}; +use crate::{error::Errno, error_msg}; + +use super::utils::{cargo, COMMON_CARGO_ARGS, DEFAULT_TARGET_RELPATH}; + +pub fn execute_build_command(config: &BuildConfig) { + let osdk_target_directory = get_target_directory().join(DEFAULT_TARGET_RELPATH); + if !osdk_target_directory.exists() { + std::fs::create_dir_all(&osdk_target_directory).unwrap(); + } + let target_info = get_current_crate_info(); + let bundle_path = osdk_target_directory.join(&target_info.name); + + let _bundle = create_base_and_build(&bundle_path, &osdk_target_directory, &config); +} + +pub fn create_base_and_build( + bundle_path: impl AsRef, + osdk_target_directory: impl AsRef, + config: &BuildConfig, +) -> Bundle { + let base_crate_path = osdk_target_directory.as_ref().join("base"); + new_base_crate( + &base_crate_path, + &get_current_crate_info().name, + &get_current_crate_info().path, + ); + let original_dir = std::env::current_dir().unwrap(); + std::env::set_current_dir(&base_crate_path).unwrap(); + let bundle = do_build(&bundle_path, &osdk_target_directory, &config); + std::env::set_current_dir(&original_dir).unwrap(); + bundle +} + +pub fn do_build( + bundle_path: impl AsRef, + osdk_target_directory: impl AsRef, + config: &BuildConfig, +) -> Bundle { + if let Some(ref initramfs) = config.manifest.initramfs { + if !initramfs.exists() { + error_msg!("initramfs file not found: {}", initramfs.display()); + process::exit(Errno::BuildCrate as _); + } + }; + let mut bundle = Bundle::new( + BundleManifest { + kcmd_args: config.manifest.kcmd_args.clone(), + initramfs: config.manifest.initramfs.clone(), + aster_bin: None, + vm_image: None, + boot: config.manifest.boot.clone(), + qemu: config.manifest.qemu.clone(), + cargo_args: config.cargo_args.clone(), + }, + &bundle_path, + ); + info!("Building kernel ELF"); + let aster_elf = build_kernel_elf(&config.cargo_args); + + if matches!(config.manifest.qemu.machine, QemuMachine::Microvm) { + let stripped_elf = strip_elf_for_qemu(&osdk_target_directory, &aster_elf); + bundle.add_aster_bin(&stripped_elf); + } + + // TODO: A boot device is required if we use GRUB. Actually you can boot + // a multiboot kernel with Q35 machine directly without a bootloader. + // We are currently ignoring this case. + if matches!(config.manifest.qemu.machine, QemuMachine::Q35) { + info!("Building boot device image"); + let bootdev_image = grub::create_bootdev_image( + &osdk_target_directory, + &aster_elf, + config.manifest.initramfs.as_ref(), + &config, + ); + bundle.add_vm_image(&bootdev_image); + } + + bundle +} + +fn build_kernel_elf(args: &CargoArgs) -> AsterBin { + let target_directory = get_target_directory(); + let target_json_path = PathBuf::from_str("x86_64-custom.json").unwrap(); + + let mut command = cargo(); + command.arg("build").arg("--target").arg(&target_json_path); + command.args(COMMON_CARGO_ARGS); + command.arg("--profile=".to_string() + &args.profile); + let status = command.status().unwrap(); + if !status.success() { + error_msg!("Cargo build failed"); + process::exit(Errno::ExecuteCommand as _); + } + + let aster_bin_path = PathBuf::from(target_directory) + .join(target_json_path.file_stem().unwrap().to_str().unwrap()); + let aster_bin_path = if args.profile == "dev" { + aster_bin_path.join("debug") + } else { + aster_bin_path.join(&args.profile) + } + .join(get_current_crate_info().name); + + AsterBin { + path: aster_bin_path, + typ: AsterBinType::Elf(AsterElfMeta { + has_linux_header: false, + has_pvh_header: false, + has_multiboot_header: true, + has_multiboot2_header: true, + }), + version: get_current_crate_info().version, + sha256sum: "TODO".to_string(), + stripped: false, + } +} diff --git a/osdk/src/commands/build/x86_64-i386_pm-none.json b/osdk/src/commands/build/x86_64-i386_pm-none.json new file mode 100644 index 000000000..e69de29bb diff --git a/osdk/src/commands/check.rs b/osdk/src/commands/check.rs index f268acba4..5a96ea75f 100644 --- a/osdk/src/commands/check.rs +++ b/osdk/src/commands/check.rs @@ -2,17 +2,17 @@ use std::process; +use crate::commands::utils::create_target_json; +use crate::error::Errno; +use crate::error_msg; + use super::utils::{cargo, COMMON_CARGO_ARGS}; use crate::{ commands::utils::create_target_json, error::Errno, error_msg, utils::get_cargo_metadata, }; pub fn execute_check_command() { - let target_json_path = { - let metadata = get_cargo_metadata(None::<&str>, None::<&[&str]>); - let target_directory = metadata.get("target_directory").unwrap().as_str().unwrap(); - create_target_json(target_directory) - }; + let target_json_path = create_target_json(); let mut command = cargo(); command.arg("check").arg("--target").arg(target_json_path); diff --git a/osdk/src/commands/clippy.rs b/osdk/src/commands/clippy.rs index 1505514be..332054237 100644 --- a/osdk/src/commands/clippy.rs +++ b/osdk/src/commands/clippy.rs @@ -4,19 +4,15 @@ use std::process; use super::utils::{cargo, COMMON_CARGO_ARGS}; use crate::{ - commands::utils::create_target_json, error::Errno, error_msg, utils::get_cargo_metadata, + error_msg, commands::utils::create_target_json, error::Errno, error_msg, utils::get_cargo_metadata, }; pub fn execute_clippy_command() { - let target_json_path = { - let metadata = get_cargo_metadata(None::<&str>, None::<&[&str]>); - let target_directory = metadata.get("target_directory").unwrap().as_str().unwrap(); - create_target_json(target_directory) - }; + let target_json_path = create_target_json(); let mut command = cargo(); command.arg("clippy").arg("-h"); - info!("[Running] cargo clippy -h"); + info!("Running `cargo clippy -h`"); let output = command.output().unwrap(); if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); @@ -28,6 +24,7 @@ pub fn execute_clippy_command() { let mut command = cargo(); command.arg("clippy").arg("--target").arg(target_json_path); command.args(COMMON_CARGO_ARGS); + // TODO: Add support for custom clippy args using OSDK commandline rather than hardcode it. command.args(["--", "-D", "warnings"]); let status = command.status().unwrap(); if !status.success() { diff --git a/osdk/src/commands/mod.rs b/osdk/src/commands/mod.rs index 5cba14eb8..97ac6c324 100644 --- a/osdk/src/commands/mod.rs +++ b/osdk/src/commands/mod.rs @@ -2,11 +2,15 @@ //! This module contains subcommands of cargo-osdk. +mod build; mod check; mod clippy; mod new; +mod run; +mod test; mod utils; pub use self::{ check::execute_check_command, clippy::execute_clippy_command, new::execute_new_command, + run::execute_run_command, test::execute_test_command, }; diff --git a/osdk/src/commands/new/kernel.template b/osdk/src/commands/new/kernel.template new file mode 100644 index 000000000..074b2b1ec --- /dev/null +++ b/osdk/src/commands/new/kernel.template @@ -0,0 +1,22 @@ +#![no_std] +#![no_main] +#![forbid(unsafe_code)] + +#[macro_use] +extern crate ktest; + +use aster_frame::prelude::*; + +#[aster_main] +fn kernel_main() -> ! { + println!("Hello world from guest kernel!"); + loop {} +} + +#[cfg(ktest)] +mod test { + #[ktest] + fn trivial_test() { + assert_eq!(1 + 1, 2); + } +} diff --git a/osdk/src/commands/template/lib.template b/osdk/src/commands/new/lib.template similarity index 75% rename from osdk/src/commands/template/lib.template rename to osdk/src/commands/new/lib.template index a87547e8e..ddd47d4d9 100644 --- a/osdk/src/commands/template/lib.template +++ b/osdk/src/commands/new/lib.template @@ -1,5 +1,9 @@ #![no_std] +#[macro_use] +extern crate ktest; +extern crate aster_frame; + #[cfg(ktest)] mod tests { #[ktest] @@ -7,4 +11,4 @@ mod tests { let memory_regions = aster_frame::boot::memory_regions(); assert!(!memory_regions.is_empty()); } -} \ No newline at end of file +} diff --git a/osdk/src/commands/new.rs b/osdk/src/commands/new/mod.rs similarity index 76% rename from osdk/src/commands/new.rs rename to osdk/src/commands/new/mod.rs index 8d09da443..ea1afb04f 100644 --- a/osdk/src/commands/new.rs +++ b/osdk/src/commands/new/mod.rs @@ -1,17 +1,18 @@ // SPDX-License-Identifier: MPL-2.0 -use std::{fs, path::PathBuf, process, str::FromStr}; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::{fs, process}; +use std::ffi::OsStr; -use crate::{ - cli::NewArgs, - error::Errno, - error_msg, - utils::{cargo_new_lib, get_cargo_metadata, ASTER_FRAME_DEP}, -}; +use crate::cli::NewArgs; +use crate::error::Errno; +use crate::error_msg; +use crate::utils::{cargo_new_lib, get_cargo_metadata, ASTER_FRAME_DEP, KTEST_DEP}; pub fn execute_new_command(args: &NewArgs) { cargo_new_lib(&args.crate_name); - let cargo_metadata = get_cargo_metadata(Some(&args.crate_name), None::<&[&str]>); + let cargo_metadata = get_cargo_metadata(Some(&args.crate_name), None::<&[&str]>).unwrap(); add_manifest_dependencies(&cargo_metadata, &args.crate_name); create_osdk_manifest(&cargo_metadata); if args.kernel { @@ -22,6 +23,14 @@ pub fn execute_new_command(args: &NewArgs) { add_rust_toolchain(&cargo_metadata); } +/// OSDK assumes that the toolchain used by the kernel should be same same as the toolchain +/// specified in the asterinas workspace. +macro_rules! aster_rust_toolchain { + () => { + include_str!("../../../../rust-toolchain.toml") + }; +} + fn add_manifest_dependencies(cargo_metadata: &serde_json::Value, crate_name: &str) { let mainfest_path = get_manifest_path(cargo_metadata, crate_name); @@ -34,7 +43,15 @@ fn add_manifest_dependencies(cargo_metadata: &serde_json::Value, crate_name: &st let aster_frame_dep = toml::Table::from_str(ASTER_FRAME_DEP).unwrap(); dependencies.as_table_mut().unwrap().extend(aster_frame_dep); + let ktest_dep = toml::Table::from_str(KTEST_DEP).unwrap(); + dependencies.as_table_mut().unwrap().extend(ktest_dep); + // If we created a workspace by `osdk new`, we should exclude the `base` crate from the workspace. + if get_cargo_metadata::<&Path, &OsStr>(None, None).is_none() { + let exclude = toml::Table::from_str(r#"exclude = ["target/osdk/base"]"#).unwrap(); + manifest.insert("workspace".to_string(), toml::Value::Table(exclude)); + } + let content = toml::to_string(&manifest).unwrap(); fs::write(mainfest_path, content).unwrap(); } @@ -51,20 +68,37 @@ fn create_osdk_manifest(cargo_metadata: &serde_json::Value) { } // Create `OSDK.toml` for the workspace - fs::write(osdk_manifest_path, "").unwrap(); + // FIXME: we need ovmf for grub-efi, the user may not have it. + // The apt OVMF repo installs to `/usr/share/OVMF` + fs::write(osdk_manifest_path, r#" +[boot] +ovmf = "/usr/share/OVMF" +[qemu] +machine = "q35" +args = [ + "--no-reboot", + "-m 2G", + "-nographic", + "-serial chardev:mux", + "-monitor chardev:mux", + "-chardev stdio,id=mux,mux=on,signal=off", + "-display none", + "-device isa-debug-exit,iobase=0xf4,iosize=0x04", +] +"#).unwrap(); } /// Write the default content of `src/kernel.rs`, with contents in provided template. fn write_kernel_template(cargo_metadata: &serde_json::Value, crate_name: &str) { let src_path = get_src_path(cargo_metadata, crate_name); - let contents = include_str!("template/kernel.template"); + let contents = include_str!("kernel.template"); fs::write(src_path, contents).unwrap(); } /// Write the default content of `src/lib.rs`, with contents in provided template. fn write_library_template(cargo_metadata: &serde_json::Value, crate_name: &str) { let src_path = get_src_path(cargo_metadata, crate_name); - let contents = include_str!("template/lib.template"); + let contents = include_str!("lib.template"); fs::write(src_path, contents).unwrap(); } @@ -83,7 +117,7 @@ fn add_rust_toolchain(cargo_metadata: &serde_json::Value) { return; } - let contents = include_str!("template/rust-toolchain.toml.template"); + let contents = aster_rust_toolchain!(); fs::write(rust_toolchain_path, contents).unwrap(); } @@ -134,7 +168,7 @@ fn get_package_metadata<'a>( fn check_rust_toolchain(toolchain: &toml::Table) { let expected = { - let contents = include_str!("template/rust-toolchain.toml.template"); + let contents = aster_rust_toolchain!(); toml::Table::from_str(contents).unwrap() }; diff --git a/osdk/src/commands/run.rs b/osdk/src/commands/run.rs new file mode 100644 index 000000000..a65d4bb91 --- /dev/null +++ b/osdk/src/commands/run.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MPL-2.0 + +use crate::config_manager::{BuildConfig, RunConfig}; +use crate::utils::{get_current_crate_info, get_target_directory}; + +use super::build::create_base_and_build; +use super::utils::DEFAULT_TARGET_RELPATH; + +pub fn execute_run_command(config: &RunConfig) { + let osdk_target_directory = get_target_directory().join(DEFAULT_TARGET_RELPATH); + let target_name = get_current_crate_info().name; + let default_bundle_directory = osdk_target_directory.join(target_name); + + let required_build_config = BuildConfig { + manifest: config.manifest.clone(), + cargo_args: config.cargo_args.clone(), + }; + + // TODO: Check if the bundle is already built and compatible with the run configuration. + let bundle = create_base_and_build( + &default_bundle_directory, + &osdk_target_directory, + &required_build_config, + ); + + bundle.run(&config); +} diff --git a/osdk/src/commands/template/kernel.template b/osdk/src/commands/template/kernel.template deleted file mode 100644 index 510cbcc8e..000000000 --- a/osdk/src/commands/template/kernel.template +++ /dev/null @@ -1,9 +0,0 @@ -#![no_std] -#![no_main] - -use aster_frame::prelude::*; - -#[aster_main] -fn kernel_main() { - println!("Hello world from guest kernel!"); -} \ No newline at end of file diff --git a/osdk/src/commands/test.rs b/osdk/src/commands/test.rs new file mode 100644 index 000000000..11def4698 --- /dev/null +++ b/osdk/src/commands/test.rs @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MPL-2.0 + +use std::fs; + +use crate::base_crate::new_base_crate; +use crate::config_manager::{BuildConfig, RunConfig, TestConfig}; +use crate::utils::{get_current_crate_info, get_target_directory}; + +use super::build::do_build; +use super::utils::DEFAULT_TARGET_RELPATH; + +pub fn execute_test_command(config: &TestConfig) { + let current_crate = get_current_crate_info(); + let osdk_target_directory = get_target_directory().join(DEFAULT_TARGET_RELPATH); + let target_crate_dir = osdk_target_directory.join("base"); + new_base_crate(&target_crate_dir, ¤t_crate.name, ¤t_crate.path); + + let main_rs_path = target_crate_dir.join("src").join("main.rs"); + + let ktest_test_whitelist = match &config.test_name { + Some(name) => format!(r#"Some(&["{}"])"#, name), + None => format!(r#"None"#), + }; + + let mut ktest_crate_whitelist = vec![current_crate.name]; + if let Some(name) = &config.test_name { + ktest_crate_whitelist.push(name.clone()); + } + + let ktest_static_var = format!( + r#" +#[no_mangle] +pub static KTEST_TEST_WHITELIST: Option<&[&str]> = {}; +#[no_mangle] +pub static KTEST_CRATE_WHITELIST: Option<&[&str]> = Some(&{:#?}); +"#, + ktest_test_whitelist, ktest_crate_whitelist, + ); + + // Append the ktest static variable to the main.rs file + let mut main_rs_content = fs::read_to_string(&main_rs_path).unwrap(); + main_rs_content.push_str(&ktest_static_var); + fs::write(&main_rs_path, main_rs_content).unwrap(); + + // Build the kernel with the given base crate + let target_name = get_current_crate_info().name; + let default_bundle_directory = osdk_target_directory.join(target_name); + let required_build_config = BuildConfig { + manifest: config.manifest.clone(), + cargo_args: config.cargo_args.clone(), + }; + let original_dir = std::env::current_dir().unwrap(); + std::env::set_current_dir(&target_crate_dir).unwrap(); + // Add `--cfg ktest` to RUSTFLAGS + std::env::set_var("RUSTFLAGS", "--cfg ktest"); + let bundle = do_build( + &default_bundle_directory, + &osdk_target_directory, + &required_build_config, + ); + std::env::remove_var("RUSTFLAGS"); + std::env::set_current_dir(&original_dir).unwrap(); + + let required_run_config = RunConfig { + manifest: required_build_config.manifest.clone(), + cargo_args: required_build_config.cargo_args.clone(), + }; + + bundle.run(&required_run_config); +} diff --git a/osdk/src/commands/utils.rs b/osdk/src/commands/utils.rs index f4cb8987b..85bd60c6d 100644 --- a/osdk/src/commands/utils.rs +++ b/osdk/src/commands/utils.rs @@ -6,17 +6,21 @@ use std::{ process::Command, }; +use crate::utils::get_target_directory; + pub const COMMON_CARGO_ARGS: &[&str] = &[ "-Zbuild-std=core,alloc,compiler_builtins", "-Zbuild-std-features=compiler-builtins-mem", ]; +pub const DEFAULT_TARGET_RELPATH: &str = "osdk"; + pub fn cargo() -> Command { Command::new("cargo") } -pub fn create_target_json(target_directory: impl AsRef) -> PathBuf { - let target_osdk_dir = PathBuf::from(target_directory.as_ref()).join("osdk"); +pub fn create_target_json() -> PathBuf { + let target_osdk_dir = get_target_directory().join(DEFAULT_TARGET_RELPATH); fs::create_dir_all(&target_osdk_dir).unwrap(); let target_json_path = target_osdk_dir.join("x86_64-custom.json"); @@ -24,7 +28,7 @@ pub fn create_target_json(target_directory: impl AsRef) -> PathBuf { return target_json_path; } - let contents = include_str!("template/x86_64-custom.json.template"); + let contents = include_str!("../base_crate/x86_64-custom.json.template"); fs::write(&target_json_path, contents).unwrap(); target_json_path diff --git a/osdk/src/config_manager/manifest.rs b/osdk/src/config_manager/manifest.rs index 801d58e0d..45150f5f5 100644 --- a/osdk/src/config_manager/manifest.rs +++ b/osdk/src/config_manager/manifest.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 -use std::{path::PathBuf, process}; +use std::path::{Path, PathBuf}; +use std::process; use regex::Regex; use serde::Deserialize; @@ -12,7 +13,7 @@ use super::{ use crate::{error::Errno, error_msg}; /// The osdk manifest from configuration file and command line arguments. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct OsdkManifest { pub kcmd_args: Vec, pub initramfs: Option, @@ -21,7 +22,10 @@ pub struct OsdkManifest { } impl OsdkManifest { - pub fn from_toml_manifest>(toml_manifest: TomlManifest, features: &[S]) -> Self { + pub fn from_toml_manifest>( + toml_manifest: TomlManifest, + selection: Option, + ) -> Self { let TomlManifest { mut kcmd_args, mut init_args, @@ -46,26 +50,26 @@ impl OsdkManifest { let mut qemu_args = None; - let mut feature_enabled_args: Vec<_> = cfg - .into_iter() - .filter_map(|(cfg, args)| { - if features - .iter() - .any(|feature| cfg.contains(feature.as_ref())) - { - Some(args) - } else { - None - } - }) - .collect(); + let mut selected_args: Vec<_> = if let Some(sel) = selection { + cfg.into_iter() + .filter_map(|(cfg, args)| { + if cfg.contains(sel.as_ref()) { + Some(args) + } else { + None + } + }) + .collect() + } else { + vec![] + }; - if feature_enabled_args.len() > 1 { - error_msg!("Multiple features are conflict"); + if selected_args.len() > 1 { + error_msg!("Multiple selections are not allowed"); process::exit(Errno::ParseMetadata as _); - } else if feature_enabled_args.len() == 1 { - qemu_args = Some(feature_enabled_args.remove(0)); - } else if feature_enabled_args.is_empty() { + } else if selected_args.len() == 1 { + qemu_args = Some(selected_args.remove(0)); + } else if selected_args.is_empty() { qemu_args = Some(default); } @@ -82,6 +86,38 @@ impl OsdkManifest { qemu: qemu_args.unwrap(), } } + + pub fn check_canonicalize_all_paths(&mut self, manifest_file_dir: impl AsRef) { + macro_rules! canonicalize_path { + ($path:expr) => {{ + let path = if $path.is_relative() { + manifest_file_dir.as_ref().join($path) + } else { + $path.clone() + }; + path.canonicalize().unwrap_or_else(|_| { + error_msg!("File specified but not found: {:#?}", path); + process::exit(Errno::ParseMetadata as _); + }) + }}; + } + macro_rules! canonicalize_optional_path { + ($path:expr) => { + if let Some(path_inner) = &$path { + Some(canonicalize_path!(path_inner)) + } else { + None + } + }; + } + self.initramfs = canonicalize_optional_path!(self.initramfs); + self.boot.grub_mkrescue = canonicalize_optional_path!(self.boot.grub_mkrescue); + self.boot.ovmf = canonicalize_optional_path!(self.boot.ovmf); + self.qemu.path = canonicalize_optional_path!(self.qemu.path); + for drive_file in &mut self.qemu.drive_files { + drive_file.path = canonicalize_path!(&drive_file.path); + } + } } /// The osdk manifest from configuration file `OSDK.toml`. @@ -111,12 +147,12 @@ fn check_args(arg_name: &str, args: &[String]) { /// Check cfg that is in the form that we can accept fn check_cfg(cfg: &str) { - if FEATURE_REGEX.captures(cfg).is_none() { - error_msg!("{} is not allowed to used after `qemu` in `OSDK.toml`. Currently we only allowed cfg like `cfg(feature=\"foo\")`", cfg); + if SELECT_REGEX.captures(cfg).is_none() { + error_msg!("{} is not allowed to used after `qemu` in `OSDK.toml`. Currently we only allow cfgs like `cfg(select=\"foo\")`", cfg); process::exit(Errno::ParseMetadata as _); } } lazy_static::lazy_static! { - pub static ref FEATURE_REGEX: Regex = Regex::new(r#"cfg\(feature="(?P\w+)"\)"#).unwrap(); + pub static ref SELECT_REGEX: Regex = Regex::new(r#"cfg\(select="(?P