mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 21:06:48 +00:00
Implement OSDK functionalities and opt-in OSDK for asterinas
This commit is contained in:
parent
bc9bce9dea
commit
f97d0f1260
@ -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"
|
||||
|
2
.github/workflows/cargo_check.yml
vendored
2
.github/workflows/cargo_check.yml
vendored
@ -16,6 +16,8 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- run: make install_osdk
|
||||
|
||||
- name: Check
|
||||
id: check
|
||||
run: make check
|
||||
|
8
.github/workflows/integration_test.yml
vendored
8
.github/workflows/integration_test.yml
vendored
@ -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
|
||||
|
8
.github/workflows/unit_test.yml
vendored
8
.github/workflows/unit_test.yml
vendored
@ -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.
|
||||
|
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -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"
|
||||
],
|
||||
|
201
Cargo.lock
generated
201
Cargo.lock
generated
@ -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"
|
||||
|
@ -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",
|
||||
|
112
Makefile
112
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
|
||||
|
88
OSDK.toml
Normal file
88
OSDK.toml
Normal file
@ -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",
|
||||
]
|
21
build.rs
21
build.rs
@ -1,21 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::{error::Error, path::PathBuf};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
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(())
|
||||
}
|
@ -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"] }
|
||||
|
@ -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 {
|
||||
|
@ -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::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
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),
|
||||
};
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -288,7 +288,7 @@ impl fmt::Debug for AtomicBits {
|
||||
}
|
||||
}
|
||||
|
||||
#[if_cfg_ktest]
|
||||
#[cfg(ktest)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
|
@ -162,7 +162,7 @@ impl HasPaddr for DmaCoherent {
|
||||
}
|
||||
}
|
||||
|
||||
#[if_cfg_ktest]
|
||||
#[cfg(ktest)]
|
||||
mod test {
|
||||
use alloc::vec;
|
||||
|
||||
|
@ -195,7 +195,7 @@ impl HasPaddr for DmaStream {
|
||||
}
|
||||
}
|
||||
|
||||
#[if_cfg_ktest]
|
||||
#[cfg(ktest)]
|
||||
mod test {
|
||||
use alloc::vec;
|
||||
|
||||
|
14
framework/libs/aster-main/Cargo.toml
Normal file
14
framework/libs/aster-main/Cargo.toml
Normal file
@ -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"] }
|
22
framework/libs/aster-main/src/lib.rs
Normal file
22
framework/libs/aster-main/src/lib.rs
Normal file
@ -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()
|
||||
}
|
@ -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]
|
||||
|
@ -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 {
|
||||
|
@ -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<PrintFn, PathsIter>(
|
||||
print: &PrintFn,
|
||||
catch_unwind: &CatchUnwindImpl,
|
||||
whitelist: Option<PathsIter>,
|
||||
test_whitelist: Option<PathsIter>,
|
||||
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::<BTreeSet<&str>>());
|
||||
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,
|
||||
|
@ -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");
|
||||
|
2
framework/libs/linux-bzimage/setup/.gitignore
vendored
Normal file
2
framework/libs/linux-bzimage/setup/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# The Rust build cache for the setup crate is placed here.
|
||||
target/
|
@ -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]
|
||||
|
@ -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();
|
||||
}
|
3
osdk/.gitignore
vendored
3
osdk/.gitignore
vendored
@ -8,3 +8,6 @@ target/
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
# Integration test generated files
|
||||
workspace_*
|
146
osdk/Cargo.lock
generated
146
osdk/Cargo.lock
generated
@ -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"
|
||||
|
@ -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"] }
|
||||
|
@ -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.
|
||||
|
12
osdk/src/base_crate/main.rs.template
Normal file
12
osdk/src/base_crate/main.rs.template
Normal file
@ -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); }
|
||||
}
|
108
osdk/src/base_crate/mod.rs
Normal file
108
osdk/src/base_crate/mod.rs
Normal file
@ -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<Path>,
|
||||
dep_crate_name: &str,
|
||||
dep_crate_path: impl AsRef<Path>,
|
||||
) {
|
||||
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<Path>) {
|
||||
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<Path>) {
|
||||
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();
|
||||
}
|
@ -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"
|
||||
}
|
33
osdk/src/bin.rs
Normal file
33
osdk/src/bin.rs
Normal file
@ -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,
|
||||
}
|
191
osdk/src/bundle.rs
Normal file
191
osdk/src/bundle.rs
Normal file
@ -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<Path>) -> 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<Path>) -> 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<String>,
|
||||
#[serde(default)]
|
||||
pub initramfs: Option<PathBuf>,
|
||||
#[serde(default)]
|
||||
pub aster_bin: Option<AsterBin>,
|
||||
#[serde(default)]
|
||||
pub vm_image: Option<AsterVmImage>,
|
||||
#[serde(default)]
|
||||
pub boot: Boot,
|
||||
#[serde(default)]
|
||||
pub qemu: Qemu,
|
||||
#[serde(default)]
|
||||
pub cargo_args: CargoArgs,
|
||||
}
|
@ -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<String>,
|
||||
}
|
||||
|
||||
#[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<String>,
|
||||
#[arg(
|
||||
long = "kcmd_args",
|
||||
help = "Command line arguments for guest kernel",
|
||||
|
102
osdk/src/commands/build/bin.rs
Normal file
102
osdk/src/commands/build/bin.rs
Normal file
@ -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<Path>,
|
||||
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<Path>, 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,
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
157
osdk/src/commands/build/grub.rs
Normal file
157
osdk/src/commands/build/grub.rs
Normal file
@ -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<Path>,
|
||||
aster_bin: &AsterBin,
|
||||
initramfs_path: Option<impl AsRef<Path>>,
|
||||
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<String>,
|
||||
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::<Vec<&str>>().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()
|
||||
}
|
134
osdk/src/commands/build/mod.rs
Normal file
134
osdk/src/commands/build/mod.rs
Normal file
@ -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<Path>,
|
||||
osdk_target_directory: impl AsRef<Path>,
|
||||
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<Path>,
|
||||
osdk_target_directory: impl AsRef<Path>,
|
||||
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,
|
||||
}
|
||||
}
|
0
osdk/src/commands/build/x86_64-i386_pm-none.json
Normal file
0
osdk/src/commands/build/x86_64-i386_pm-none.json
Normal file
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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,
|
||||
};
|
||||
|
22
osdk/src/commands/new/kernel.template
Normal file
22
osdk/src/commands/new/kernel.template
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
#![no_std]
|
||||
|
||||
#[macro_use]
|
||||
extern crate ktest;
|
||||
extern crate aster_frame;
|
||||
|
||||
#[cfg(ktest)]
|
||||
mod tests {
|
||||
#[ktest]
|
@ -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,6 +43,14 @@ 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()
|
||||
};
|
||||
|
27
osdk/src/commands/run.rs
Normal file
27
osdk/src/commands/run.rs
Normal file
@ -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);
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use aster_frame::prelude::*;
|
||||
|
||||
#[aster_main]
|
||||
fn kernel_main() {
|
||||
println!("Hello world from guest kernel!");
|
||||
}
|
70
osdk/src/commands/test.rs
Normal file
70
osdk/src/commands/test.rs
Normal file
@ -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);
|
||||
}
|
@ -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<Path>) -> 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<Path>) -> 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
|
||||
|
@ -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<String>,
|
||||
pub initramfs: Option<PathBuf>,
|
||||
@ -21,7 +22,10 @@ pub struct OsdkManifest {
|
||||
}
|
||||
|
||||
impl OsdkManifest {
|
||||
pub fn from_toml_manifest<S: AsRef<str>>(toml_manifest: TomlManifest, features: &[S]) -> Self {
|
||||
pub fn from_toml_manifest<S: AsRef<str>>(
|
||||
toml_manifest: TomlManifest,
|
||||
selection: Option<S>,
|
||||
) -> 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<Path>) {
|
||||
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<feature>\w+)"\)"#).unwrap();
|
||||
pub static ref SELECT_REGEX: Regex = Regex::new(r#"cfg\(select="(?P<select>\w+)"\)"#).unwrap();
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ pub struct BuildConfig {
|
||||
impl BuildConfig {
|
||||
pub fn parse(args: &BuildArgs) -> Self {
|
||||
let cargo_args = split_features(&args.cargo_args);
|
||||
let mut manifest = load_osdk_manifest(&cargo_args);
|
||||
let mut manifest = load_osdk_manifest(&cargo_args, args.osdk_args.select.as_ref());
|
||||
apply_cli_args(&mut manifest, &args.osdk_args);
|
||||
try_fill_system_configs(&mut manifest);
|
||||
Self {
|
||||
@ -56,7 +56,7 @@ pub struct RunConfig {
|
||||
impl RunConfig {
|
||||
pub fn parse(args: &RunArgs) -> Self {
|
||||
let cargo_args = split_features(&args.cargo_args);
|
||||
let mut manifest = load_osdk_manifest(&cargo_args);
|
||||
let mut manifest = load_osdk_manifest(&cargo_args, args.osdk_args.select.as_ref());
|
||||
apply_cli_args(&mut manifest, &args.osdk_args);
|
||||
try_fill_system_configs(&mut manifest);
|
||||
Self {
|
||||
@ -77,7 +77,7 @@ pub struct TestConfig {
|
||||
impl TestConfig {
|
||||
pub fn parse(args: &TestArgs) -> Self {
|
||||
let cargo_args = split_features(&args.cargo_args);
|
||||
let mut manifest = load_osdk_manifest(&cargo_args);
|
||||
let mut manifest = load_osdk_manifest(&cargo_args, args.osdk_args.select.as_ref());
|
||||
apply_cli_args(&mut manifest, &args.osdk_args);
|
||||
try_fill_system_configs(&mut manifest);
|
||||
Self {
|
||||
@ -88,17 +88,20 @@ impl TestConfig {
|
||||
}
|
||||
}
|
||||
|
||||
fn load_osdk_manifest(cargo_args: &CargoArgs) -> OsdkManifest {
|
||||
let manifest_path = {
|
||||
let feature_strings = get_feature_strings(cargo_args);
|
||||
let cargo_metadata = get_cargo_metadata(None::<&str>, Some(&feature_strings));
|
||||
let workspace_root = cargo_metadata
|
||||
/// FIXME: I guess OSDK manifest is definitely NOT per workspace. It's per crate. When you cannot
|
||||
/// find a manifest per crate, find it in the upper levels.
|
||||
/// I don't bother to do it now, just fix the relpaths.
|
||||
fn load_osdk_manifest<S: AsRef<str>>(cargo_args: &CargoArgs, selection: Option<S>) -> OsdkManifest {
|
||||
let feature_strings = get_feature_strings(cargo_args);
|
||||
let cargo_metadata = get_cargo_metadata(None::<&str>, Some(&feature_strings)).unwrap();
|
||||
let workspace_root = PathBuf::from(
|
||||
cargo_metadata
|
||||
.get("workspace_root")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap();
|
||||
PathBuf::from(workspace_root).join("OSDK.toml")
|
||||
};
|
||||
.unwrap(),
|
||||
);
|
||||
let manifest_path = workspace_root.join("OSDK.toml");
|
||||
|
||||
let Ok(contents) = fs::read_to_string(&manifest_path) else {
|
||||
error_msg!(
|
||||
@ -108,8 +111,18 @@ fn load_osdk_manifest(cargo_args: &CargoArgs) -> OsdkManifest {
|
||||
process::exit(Errno::GetMetadata as _);
|
||||
};
|
||||
|
||||
let toml_manifest: TomlManifest = toml::from_str(&contents).unwrap();
|
||||
OsdkManifest::from_toml_manifest(toml_manifest, &cargo_args.features)
|
||||
let toml_manifest: TomlManifest = toml::from_str(&contents).unwrap_or_else(|err| {
|
||||
error_msg!(
|
||||
"Cannot parse TOML file, {}:\n{}:\n {}",
|
||||
err.message(),
|
||||
manifest_path.to_string_lossy().to_string(),
|
||||
&contents[err.span().unwrap()],
|
||||
);
|
||||
process::exit(Errno::ParseMetadata as _);
|
||||
});
|
||||
let mut osdk_manifest = OsdkManifest::from_toml_manifest(toml_manifest, selection);
|
||||
osdk_manifest.check_canonicalize_all_paths(workspace_root);
|
||||
osdk_manifest
|
||||
}
|
||||
|
||||
/// Split `features` in `cargo_args` to ensure each string contains exactly one feature.
|
||||
@ -126,7 +139,7 @@ fn split_features(cargo_args: &CargoArgs) -> CargoArgs {
|
||||
}
|
||||
|
||||
CargoArgs {
|
||||
release: cargo_args.release,
|
||||
profile: cargo_args.profile.clone(),
|
||||
features,
|
||||
}
|
||||
}
|
||||
|
@ -16,13 +16,25 @@ pub struct Qemu {
|
||||
/// The additional arguments for running qemu, except `-cpu` and `-machine`.
|
||||
#[serde(default)]
|
||||
pub args: Vec<String>,
|
||||
/// The additional drive files
|
||||
#[serde(default)]
|
||||
pub drive_files: Vec<DriveFile>,
|
||||
/// The `-machine` argument for running qemu.
|
||||
#[serde(default)]
|
||||
pub machine: QemuMachine,
|
||||
/// The path of qemu.
|
||||
#[serde(default)]
|
||||
pub path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct DriveFile {
|
||||
#[serde(default)]
|
||||
pub path: PathBuf,
|
||||
#[serde(default)]
|
||||
pub append: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize)]
|
||||
pub struct CfgQemu {
|
||||
pub default: Qemu,
|
||||
@ -44,6 +56,7 @@ impl<'de> Deserialize<'de> for CfgQemu {
|
||||
Path,
|
||||
Args,
|
||||
Machine,
|
||||
DriveFiles,
|
||||
Cfg(String),
|
||||
}
|
||||
|
||||
@ -58,7 +71,7 @@ impl<'de> Deserialize<'de> for CfgQemu {
|
||||
type Value = Field;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("`path`, `args`, `machine` or cfg")
|
||||
formatter.write_str("`path`, `args`, `machine`, `drive_files` or cfg")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
@ -69,6 +82,7 @@ impl<'de> Deserialize<'de> for CfgQemu {
|
||||
"args" => Ok(Field::Args),
|
||||
"machine" => Ok(Field::Machine),
|
||||
"path" => Ok(Field::Path),
|
||||
"drive_files" => Ok(Field::DriveFiles),
|
||||
v => Ok(Field::Cfg(v.to_string())),
|
||||
}
|
||||
}
|
||||
@ -105,6 +119,9 @@ impl<'de> Deserialize<'de> for CfgQemu {
|
||||
Field::Path => {
|
||||
default.path = map.next_value()?;
|
||||
}
|
||||
Field::DriveFiles => {
|
||||
default.drive_files = map.next_value()?;
|
||||
}
|
||||
Field::Cfg(cfg) => {
|
||||
let qemu_args = map.next_value()?;
|
||||
cfgs.insert(cfg, qemu_args);
|
||||
|
@ -8,6 +8,9 @@ pub enum Errno {
|
||||
AddRustToolchain = 3,
|
||||
ParseMetadata = 4,
|
||||
ExecuteCommand = 5,
|
||||
BuildCrate = 6,
|
||||
RunBundle = 7,
|
||||
CreateBaseCrate = 8,
|
||||
}
|
||||
|
||||
/// Print error message to console
|
||||
|
@ -7,6 +7,9 @@ extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
mod base_crate;
|
||||
mod bin;
|
||||
mod bundle;
|
||||
mod cli;
|
||||
mod commands;
|
||||
mod config_manager;
|
||||
@ -14,6 +17,7 @@ mod error;
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
mod utils;
|
||||
mod vm_image;
|
||||
|
||||
fn main() {
|
||||
// init logger
|
||||
|
@ -1,17 +1,13 @@
|
||||
initramfs="./build/initramfs.cpio.gz"
|
||||
|
||||
[qemu]
|
||||
path = "/usr/bin/qemu-system-x86_64"
|
||||
args = ["-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off",
|
||||
args = [
|
||||
"-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"
|
||||
]
|
||||
|
||||
[qemu.'cfg(feature="intel_tdx")']
|
||||
path = "/usr/local/sbin/qemu-kvm"
|
||||
[qemu.'cfg(select="intel_tdx")']
|
||||
|
||||
[qemu.'cfg(feature="iommu")']
|
||||
path = "/usr/bin/qemu-system-x86_64"
|
||||
[qemu.'cfg(select="iommu")']
|
||||
args = [
|
||||
"-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",
|
||||
|
@ -1,6 +1,5 @@
|
||||
kcmd_args = ["init=/bin/busybox", "path=/usr/local/bin"]
|
||||
init_args = ["sh", "-l"]
|
||||
initramfs="./build/initramfs.cpio.gz"
|
||||
|
||||
[boot]
|
||||
loader = "grub"
|
||||
@ -9,7 +8,6 @@ grub-mkrescue = "/usr/bin/grub-mkrescue"
|
||||
ovmf = "/usr/bin/ovmf"
|
||||
|
||||
[qemu]
|
||||
path = "/usr/bin/qemu-system-x86_64"
|
||||
machine = "q35"
|
||||
args = [
|
||||
"-enable-kvm",
|
||||
|
@ -23,9 +23,6 @@ fn deserialize_osdk_manifest() {
|
||||
let content = include_str!("OSDK.toml.full");
|
||||
let osdk_manifest: TomlManifest = toml::from_str(content).unwrap();
|
||||
assert!(osdk_manifest.boot.grub_mkrescue.unwrap() == PathBuf::from("/usr/bin/grub-mkrescue"));
|
||||
assert!(
|
||||
osdk_manifest.qemu.default.path.unwrap() == PathBuf::from("/usr/bin/qemu-system-x86_64")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -65,7 +62,7 @@ fn load_manifest_conditional() {
|
||||
fs::write(path, contents).unwrap();
|
||||
|
||||
let cargo_args = CargoArgs {
|
||||
release: true,
|
||||
profile: "release".to_string(),
|
||||
features: vec![String::from("iommu")],
|
||||
};
|
||||
cargo_osdk_build(PathBuf::from(workspace).join(kernel_name), &cargo_args);
|
||||
@ -118,51 +115,39 @@ fn conditional_manifest() {
|
||||
.cfg
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.contains_key(&String::from("cfg(feature=\"intel_tdx\")")));
|
||||
.contains_key(&String::from("cfg(select=\"intel_tdx\")")));
|
||||
assert!(toml_manifest
|
||||
.qemu
|
||||
.cfg
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.contains_key(&String::from("cfg(feature=\"iommu\")")));
|
||||
.contains_key(&String::from("cfg(select=\"iommu\")")));
|
||||
|
||||
// No features
|
||||
let features: &[&str] = &[];
|
||||
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), features);
|
||||
assert_eq!(
|
||||
manifest.qemu.path,
|
||||
Some(PathBuf::from("/usr/bin/qemu-system-x86_64"))
|
||||
);
|
||||
// Default selection
|
||||
let selection: Option<&str> = None;
|
||||
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), selection);
|
||||
assert!(manifest.qemu.args.contains(&String::from(
|
||||
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off"
|
||||
)));
|
||||
|
||||
// Iommu features
|
||||
let features: &[&str] = &["iommu"];
|
||||
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), features);
|
||||
assert_eq!(
|
||||
manifest.qemu.path,
|
||||
Some(PathBuf::from("/usr/bin/qemu-system-x86_64"))
|
||||
);
|
||||
// Iommu
|
||||
let selection: Option<&str> = Some("iommu");
|
||||
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), selection);
|
||||
assert!(manifest
|
||||
.qemu
|
||||
.args
|
||||
.contains(&String::from("-device ioh3420,id=pcie.0,chassis=1")));
|
||||
|
||||
// Tdx features
|
||||
let features: &[&str] = &["intel_tdx"];
|
||||
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), features);
|
||||
assert_eq!(
|
||||
manifest.qemu.path,
|
||||
Some(PathBuf::from("/usr/local/sbin/qemu-kvm"))
|
||||
);
|
||||
// Tdx
|
||||
let selection: Option<&str> = Some("intel_tdx");
|
||||
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), selection);
|
||||
assert!(manifest.qemu.args.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_feature() {
|
||||
let text = "cfg(feature=\"abc123_\")";
|
||||
let captures = FEATURE_REGEX.captures(text).unwrap();
|
||||
let feature = captures.name("feature").unwrap().as_str();
|
||||
assert_eq!(feature, "abc123_");
|
||||
fn extract_selection() {
|
||||
let text = "cfg(select=\"abc123_\")";
|
||||
let captures = SELECT_REGEX.captures(text).unwrap();
|
||||
let selection = captures.name("select").unwrap().as_str();
|
||||
assert_eq!(selection, "abc123_");
|
||||
}
|
||||
|
@ -19,7 +19,12 @@ pub fn cargo_osdk<T: AsRef<OsStr>, I: IntoIterator<Item = T>>(args: I) -> Comman
|
||||
}
|
||||
|
||||
pub fn assert_success(output: &Output) {
|
||||
assert!(output.status.success());
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"Command output {:#?} seems failed, stderr:\n {}",
|
||||
output,
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
pub fn assert_stdout_contains_msg(output: &Output, msg: &str) {
|
||||
@ -37,7 +42,11 @@ pub fn create_workspace(workspace_name: &str, members: &[&str]) {
|
||||
.iter()
|
||||
.map(|member| toml::Value::String(member.to_string()))
|
||||
.collect();
|
||||
|
||||
let exclude = toml::Value::Array(vec![toml::Value::String("target/osdk/base".to_string())]);
|
||||
|
||||
table.insert("members".to_string(), toml::Value::Array(members));
|
||||
table.insert("exclude".to_string(), exclude);
|
||||
table
|
||||
};
|
||||
|
||||
|
@ -1,13 +1,16 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::{ffi::OsStr, path::Path, process::Command};
|
||||
use std::{ffi::OsStr, path::{Path, PathBuf}, process::Command};
|
||||
|
||||
use crate::{error::Errno, error_msg};
|
||||
|
||||
// FIXME: Crates belonging to Asterinas require a different dependency format. The dependency
|
||||
// should be specified using a relative path instead of a URL.
|
||||
// TODO: The dependency should be corrected when this branch is merged.
|
||||
pub const ASTER_FRAME_DEP: &str =
|
||||
"aster-frame = { git = \"https://github.com/asterinas/asterinas\", rev = \"f2f991b\" }";
|
||||
"aster-frame = { git = \"https://github.com/junyang-zh/asterinas\", branch = \"osdk\" }";
|
||||
pub const KTEST_DEP: &str =
|
||||
"ktest = { git = \"https://github.com/junyang-zh/asterinas\", branch = \"osdk\" }";
|
||||
|
||||
fn cargo() -> Command {
|
||||
Command::new("cargo")
|
||||
@ -24,10 +27,13 @@ pub fn cargo_new_lib(crate_name: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Cargo metadata parsed from the standard output
|
||||
/// of the invocation of Cargo. Return `None` if the command
|
||||
/// fails or the `current_dir` is not in a Cargo workspace.
|
||||
pub fn get_cargo_metadata<S1: AsRef<Path>, S2: AsRef<OsStr>>(
|
||||
current_dir: Option<S1>,
|
||||
cargo_args: Option<&[S2]>,
|
||||
) -> serde_json::Value {
|
||||
) -> Option<serde_json::Value> {
|
||||
let mut command = cargo();
|
||||
command.args(["metadata", "--no-deps", "--format-version", "1"]);
|
||||
|
||||
@ -42,13 +48,48 @@ pub fn get_cargo_metadata<S1: AsRef<Path>, S2: AsRef<OsStr>>(
|
||||
let output = command.output().unwrap();
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
eprintln!("{}", &stderr);
|
||||
|
||||
error_msg!("Failed to get metadata for newly created crate");
|
||||
std::process::exit(Errno::GetMetadata as _);
|
||||
return None;
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
serde_json::from_str(&stdout).unwrap()
|
||||
Some(serde_json::from_str(&stdout).unwrap())
|
||||
}
|
||||
|
||||
pub fn get_target_directory() -> PathBuf {
|
||||
let metadata = get_cargo_metadata(None::<&str>, None::<&[&str]>).unwrap();
|
||||
metadata
|
||||
.get("target_directory")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
pub struct CrateInfo {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
pub fn get_current_crate_info() -> CrateInfo {
|
||||
let metadata = get_cargo_metadata(None::<&str>, None::<&[&str]>).unwrap();
|
||||
let default_members = metadata.get("workspace_default_members").unwrap();
|
||||
assert_eq!(default_members.as_array().unwrap().len(), 1);
|
||||
// The default member string here is in the form of "<crate_name> <crate_version> (path+file://<crate_path>)"
|
||||
let default_member = default_members[0]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.split(" ")
|
||||
.collect::<Vec<&str>>();
|
||||
let name = default_member[0].to_string();
|
||||
let version = default_member[1].to_string();
|
||||
let path = default_member[2]
|
||||
.trim_start_matches("(path+file://")
|
||||
.trim_end_matches(")")
|
||||
.to_string();
|
||||
CrateInfo {
|
||||
name,
|
||||
version,
|
||||
path,
|
||||
}
|
||||
}
|
||||
|
22
osdk/src/vm_image.rs
Normal file
22
osdk/src/vm_image.rs
Normal file
@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AsterVmImage {
|
||||
pub path: PathBuf,
|
||||
pub typ: AsterVmImageType,
|
||||
pub aster_version: String,
|
||||
pub sha256sum: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum AsterVmImageType {
|
||||
GrubIso(AsterGrubIsoImageMeta),
|
||||
// TODO: add more vm image types such as qcow2, etc.
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AsterGrubIsoImageMeta {
|
||||
pub grub_version: String,
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
[package]
|
||||
name = "aster-runner"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.32"
|
||||
clap = { version = "4.3.19", features = ["derive"] }
|
||||
linux-bzimage-builder = { path = "../framework/libs/linux-bzimage/builder" }
|
||||
rand = "0.8.5"
|
||||
xmas-elf = "0.8.0"
|
@ -1,70 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Providing the utility to run the GDB scripts for the runner.
|
||||
|
||||
use std::{fs::OpenOptions, io::Write, path::PathBuf, process::Command};
|
||||
|
||||
use crate::qemu_grub_efi;
|
||||
|
||||
/// Run a GDB client.
|
||||
///
|
||||
/// If argument `gdb_grub` is set true, it will run GRUB's gdb script.
|
||||
///
|
||||
/// Make sure to set GRUB_PREFIX to the actual GRUB you are using.
|
||||
///
|
||||
/// When debugging grub, the OVMF firmware will load the grub kernel at an
|
||||
/// address unknown at the moment. You should use the debug message from our
|
||||
/// custom built OVMF firmware and read the entrypoint address
|
||||
/// (often `0x0007E684000`). Then use the following GDB command to load symbols:
|
||||
/// `dynamic_load_symbols ${ENTRY_ADDRESS}`.
|
||||
/// During each run, the address is unlikely to change. But the address will
|
||||
/// depend on the versions of grub or OVMF.
|
||||
///
|
||||
/// Also, do `set breakpoint pending on` when you want to break on GRUB modules.
|
||||
pub fn run_gdb_client(path: &PathBuf, gdb_grub: bool) {
|
||||
let path = std::fs::canonicalize(path).unwrap();
|
||||
let mut gdb_cmd = Command::new("gdb");
|
||||
// Set the architecture, otherwise GDB will complain about.
|
||||
gdb_cmd.arg("-ex").arg("set arch i386:x86-64:intel");
|
||||
let grub_script = "/tmp/aster-gdb-grub-script";
|
||||
if gdb_grub {
|
||||
let grub_dir = PathBuf::from(qemu_grub_efi::GRUB_PREFIX)
|
||||
.join("lib")
|
||||
.join("grub")
|
||||
.join(qemu_grub_efi::GRUB_VERSION);
|
||||
// Load symbols from GRUB using the provided grub gdb script.
|
||||
// Read the contents from `gdb_grub` and
|
||||
// replace the lines containing "target remote :1234".
|
||||
gdb_cmd.current_dir(&grub_dir);
|
||||
let grub_script_content = std::fs::read_to_string(grub_dir.join("gdb_grub")).unwrap();
|
||||
let lines = grub_script_content.lines().collect::<Vec<_>>();
|
||||
let mut f = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(grub_script)
|
||||
.unwrap();
|
||||
for line in lines {
|
||||
if line.contains("target remote :1234") {
|
||||
// Connect to the GDB server.
|
||||
writeln!(f, "target remote /tmp/aster-gdb-socket").unwrap();
|
||||
} else {
|
||||
writeln!(f, "{}", line).unwrap();
|
||||
}
|
||||
}
|
||||
gdb_cmd.arg("-x").arg(grub_script);
|
||||
} else {
|
||||
// Load symbols from the kernel image.
|
||||
gdb_cmd.arg("-ex").arg(format!("file {}", path.display()));
|
||||
// Connect to the GDB server.
|
||||
gdb_cmd
|
||||
.arg("-ex")
|
||||
.arg("target remote /tmp/aster-gdb-socket");
|
||||
}
|
||||
// Connect to the GDB server and run.
|
||||
println!("running:{:#?}", gdb_cmd);
|
||||
gdb_cmd.status().unwrap();
|
||||
if gdb_grub {
|
||||
// Clean the temporary script file then return.
|
||||
std::fs::remove_file(grub_script).unwrap();
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::{
|
||||
fs::OpenOptions,
|
||||
io::{Seek, SeekFrom, Write},
|
||||
path::PathBuf,
|
||||
process::Command,
|
||||
};
|
||||
|
||||
pub const MACHINE_ARGS: &[&str] = &[
|
||||
"-machine",
|
||||
"microvm,pit=on,pic=off,rtc=on",
|
||||
"-nodefaults",
|
||||
"-no-user-config",
|
||||
];
|
||||
|
||||
pub const DEVICE_ARGS: &[&str] = &[
|
||||
"-device",
|
||||
"virtio-blk-device,drive=x0",
|
||||
"-device",
|
||||
"virtio-keyboard-device",
|
||||
"-device",
|
||||
"virtio-net-device,netdev=net01",
|
||||
"-device",
|
||||
"virtio-serial-device",
|
||||
"-device",
|
||||
"virtconsole,chardev=mux",
|
||||
];
|
||||
|
||||
pub fn create_bootdev_image(path: PathBuf) -> PathBuf {
|
||||
let dir = path.parent().unwrap();
|
||||
let name = path.file_name().unwrap().to_str().unwrap().to_string();
|
||||
let elf_path = dir.join(name.clone()).to_str().unwrap().to_string();
|
||||
let strip_elf_path = dir
|
||||
.join(name.clone() + ".stripped.elf")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
// We use rust-strip to reduce the kernel image size.
|
||||
let status = Command::new("rust-strip")
|
||||
.arg(&elf_path)
|
||||
.arg("-o")
|
||||
.arg(&strip_elf_path)
|
||||
.status();
|
||||
|
||||
match status {
|
||||
Ok(status) => {
|
||||
if !status.success() {
|
||||
panic!("Failed to strip kernel elf.");
|
||||
}
|
||||
}
|
||||
Err(err) => match err.kind() {
|
||||
std::io::ErrorKind::NotFound => panic!(
|
||||
"Not find rust-strip command,
|
||||
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(&strip_elf_path)
|
||||
.unwrap();
|
||||
|
||||
let bytes: [u8; 2] = [0x03, 0x00];
|
||||
|
||||
file.seek(SeekFrom::Start(18)).unwrap();
|
||||
file.write_all(&bytes).unwrap();
|
||||
file.flush().unwrap();
|
||||
|
||||
strip_elf_path.into()
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
pub mod microvm;
|
||||
pub mod qemu_grub_efi;
|
@ -1,194 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::{
|
||||
fs,
|
||||
io::Read,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use linux_bzimage_builder::{make_bzimage, BzImageType};
|
||||
|
||||
use crate::BootProtocol;
|
||||
|
||||
macro_rules! ovmf_prefix {
|
||||
() => {
|
||||
// There are 3 optional OVMF builds at your service in the dev image
|
||||
"/root/ovmf/release/"
|
||||
// "/root/ovmf/debug/"
|
||||
// "/usr/share/OVMF/"
|
||||
};
|
||||
}
|
||||
|
||||
pub const MACHINE_ARGS: &[&str] = &[
|
||||
"-machine",
|
||||
"q35,kernel-irqchip=split",
|
||||
"-drive",
|
||||
concat!(
|
||||
"if=pflash,format=raw,unit=0,readonly=on,file=",
|
||||
ovmf_prefix!(),
|
||||
"OVMF_CODE.fd"
|
||||
),
|
||||
"-drive",
|
||||
concat!(
|
||||
"if=pflash,format=raw,unit=1,file=",
|
||||
ovmf_prefix!(),
|
||||
"OVMF_VARS.fd"
|
||||
),
|
||||
];
|
||||
|
||||
pub const NOIOMMU_DEVICE_ARGS: &[&str] = &[
|
||||
"-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",
|
||||
];
|
||||
|
||||
pub const IOMMU_DEVICE_ARGS: &[&str] = &[
|
||||
"-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",
|
||||
];
|
||||
|
||||
/// The default GRUB tools used.
|
||||
pub const GRUB_PREFIX: &str = "/usr";
|
||||
/// The GRUB version that defaults to use EFI handover. Which is the Debian APT version.
|
||||
pub const GRUB_PREFIX_EFI_HANDOVER: &str = "/usr";
|
||||
/// The GRUB version that uses Loadfile2 and has a fallback to use legacy boot. Which is the custom built upstream 2.12 verion.
|
||||
pub const GRUB_PREFIX_EFI_AND_LEGACY: &str = "/usr/local/grub";
|
||||
|
||||
pub const GRUB_VERSION: &str = "x86_64-efi";
|
||||
|
||||
pub fn create_bootdev_image(
|
||||
kernel_elf_path: PathBuf,
|
||||
initramfs_path: PathBuf,
|
||||
grub_cfg: String,
|
||||
protocol: BootProtocol,
|
||||
) -> PathBuf {
|
||||
let target_dir = kernel_elf_path.parent().unwrap();
|
||||
let iso_root = target_dir.join("iso_root");
|
||||
|
||||
// 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.
|
||||
fs::copy(
|
||||
initramfs_path,
|
||||
iso_root.join("boot").join("initramfs.cpio.gz"),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let target_path = match protocol {
|
||||
BootProtocol::LinuxLegacy32 | BootProtocol::LinuxEfiHandover64 => {
|
||||
let image_type = match protocol {
|
||||
BootProtocol::LinuxLegacy32 => BzImageType::Legacy32,
|
||||
BootProtocol::LinuxEfiHandover64 => BzImageType::Efi64,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let setup_src = Path::new("framework/libs/linux-bzimage/setup");
|
||||
let setup_out_dir = Path::new("target/linux-bzimage-setup");
|
||||
// Make the `bzImage`-compatible kernel image and place it in the boot directory.
|
||||
let target_path = iso_root.join("boot").join("asterinaz");
|
||||
println!("[aster-runner] Building bzImage.");
|
||||
make_bzimage(
|
||||
&target_path,
|
||||
image_type,
|
||||
&kernel_elf_path.as_path(),
|
||||
&setup_src,
|
||||
&setup_out_dir,
|
||||
);
|
||||
target_path
|
||||
}
|
||||
BootProtocol::Multiboot | BootProtocol::Multiboot2 => {
|
||||
// Copy the kernel image to the boot directory.
|
||||
let target_path = iso_root.join("boot").join("atserinas");
|
||||
fs::copy(&kernel_elf_path, &target_path).unwrap();
|
||||
target_path
|
||||
}
|
||||
};
|
||||
let target_name = target_path.file_name().unwrap().to_str().unwrap();
|
||||
|
||||
// Write the grub.cfg file
|
||||
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.
|
||||
let iso_path = target_dir.join(target_name.to_string() + ".iso");
|
||||
let grub_mkrescue_bin = match protocol {
|
||||
BootProtocol::LinuxLegacy32 => PathBuf::from(GRUB_PREFIX_EFI_AND_LEGACY),
|
||||
BootProtocol::LinuxEfiHandover64 => PathBuf::from(GRUB_PREFIX_EFI_HANDOVER),
|
||||
BootProtocol::Multiboot | BootProtocol::Multiboot2 => PathBuf::from(GRUB_PREFIX),
|
||||
}
|
||||
.join("bin")
|
||||
.join("grub-mkrescue");
|
||||
let mut cmd = std::process::Command::new(grub_mkrescue_bin.as_os_str());
|
||||
cmd.arg("--output").arg(&iso_path).arg(iso_root.as_os_str());
|
||||
if !cmd.status().unwrap().success() {
|
||||
panic!("Failed to run `{:?}`.", cmd);
|
||||
}
|
||||
|
||||
iso_path.into()
|
||||
}
|
||||
|
||||
pub fn generate_grub_cfg(
|
||||
template_filename: &str,
|
||||
kcmdline: &str,
|
||||
skip_grub_menu: bool,
|
||||
protocol: BootProtocol,
|
||||
) -> String {
|
||||
let mut buffer = String::new();
|
||||
|
||||
// Read the contents of the file.
|
||||
fs::File::open(template_filename)
|
||||
.unwrap()
|
||||
.read_to_string(&mut buffer)
|
||||
.unwrap();
|
||||
|
||||
// Delete the first two lines that notes the file a template file.
|
||||
let buffer = buffer.lines().skip(2).collect::<Vec<&str>>().join("\n");
|
||||
// Set the timout style and timeout.
|
||||
let buffer = buffer
|
||||
.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 buffer = buffer.replace("#KERNEL_COMMAND_LINE#", kcmdline);
|
||||
// Replace the grub commands according to the protocol selected.
|
||||
let buffer = match protocol {
|
||||
BootProtocol::Multiboot => buffer
|
||||
.replace("#GRUB_CMD_KERNEL#", "multiboot")
|
||||
.replace("#KERNEL#", "/boot/atserinas")
|
||||
.replace("#GRUB_CMD_INITRAMFS#", "module --nounzip"),
|
||||
BootProtocol::Multiboot2 => buffer
|
||||
.replace("#GRUB_CMD_KERNEL#", "multiboot2")
|
||||
.replace("#KERNEL#", "/boot/atserinas")
|
||||
.replace("#GRUB_CMD_INITRAMFS#", "module2 --nounzip"),
|
||||
BootProtocol::LinuxLegacy32 | BootProtocol::LinuxEfiHandover64 => buffer
|
||||
.replace("#GRUB_CMD_KERNEL#", "linux")
|
||||
.replace("#KERNEL#", "/boot/asterinaz")
|
||||
.replace("#GRUB_CMD_INITRAMFS#", "initrd"),
|
||||
};
|
||||
|
||||
buffer
|
||||
}
|
@ -1,234 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! aster-runner is the Asterinas runner script to ease the pain of running
|
||||
//! and testing Asterinas inside a QEMU VM. It should be built and run as the
|
||||
//! cargo runner: https://doc.rust-lang.org/cargo/reference/config.html
|
||||
//!
|
||||
//! The runner will generate the filesystem image for starting Asterinas. If
|
||||
//! we should use the runner in the default mode, which invokes QEMU with
|
||||
//! a GRUB boot device image, the runner would be responsible for generating
|
||||
//! the appropriate kernel image and the boot device image. It also supports
|
||||
//! to directly boot the kernel image without GRUB using the QEMU microvm
|
||||
//! machine type.
|
||||
//!
|
||||
|
||||
pub mod gdb;
|
||||
pub mod machine;
|
||||
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use clap::{Parser, ValueEnum};
|
||||
|
||||
use crate::machine::{microvm, qemu_grub_efi};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, ValueEnum)]
|
||||
enum BootMethod {
|
||||
QemuGrub,
|
||||
Microvm,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, ValueEnum)]
|
||||
pub enum BootProtocol {
|
||||
Multiboot,
|
||||
Multiboot2,
|
||||
LinuxLegacy32,
|
||||
LinuxEfiHandover64,
|
||||
}
|
||||
/// The CLI of this runner.
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
// Positional arguments.
|
||||
/// The Asterinas binary path.
|
||||
path: PathBuf,
|
||||
|
||||
/// Provide the kernel commandline, which specifies
|
||||
/// the init process.
|
||||
kcmdline: String,
|
||||
|
||||
// Optional arguments.
|
||||
/// Boot method. Can be one of the following items:
|
||||
/// - `qemu-grub`;
|
||||
/// - `microvm`.
|
||||
#[arg(long, value_enum, default_value_t = BootMethod::QemuGrub)]
|
||||
boot_method: BootMethod,
|
||||
|
||||
/// Boot protocol. Can be one of the following items:
|
||||
/// - `multiboot`;
|
||||
/// - `multiboot2`;
|
||||
/// - `linux-legacy32`;
|
||||
/// - `linux-efi-handover64`.
|
||||
#[arg(long, value_enum, default_value_t = BootProtocol::Multiboot2)]
|
||||
boot_protocol: BootProtocol,
|
||||
|
||||
/// Enable KVM when running QEMU.
|
||||
#[arg(long, default_value_t = false)]
|
||||
enable_kvm: bool,
|
||||
|
||||
/// Emulate Intel IOMMU by QEMU.
|
||||
#[arg(long, default_value_t = false)]
|
||||
emulate_iommu: bool,
|
||||
|
||||
/// Run QEMU as a GDB server.
|
||||
#[arg(long, default_value_t = false)]
|
||||
halt_for_gdb: bool,
|
||||
|
||||
/// Boot without displaying the GRUB menu.
|
||||
#[arg(long, default_value_t = false)]
|
||||
skip_grub_menu: bool,
|
||||
|
||||
/// Run a GDB client instead of running the kernel.
|
||||
#[arg(long, default_value_t = false)]
|
||||
run_gdb_client: bool,
|
||||
}
|
||||
|
||||
pub const COMMON_ARGS: &[&str] = &[
|
||||
"--no-reboot",
|
||||
"-cpu",
|
||||
"Icelake-Server,+x2apic",
|
||||
"-m",
|
||||
"2G",
|
||||
"-nographic", // TODO: figure out why grub can't shown up without it
|
||||
"-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",
|
||||
];
|
||||
|
||||
pub fn random_hostfwd_ports() -> (u16, u16) {
|
||||
let start = 32768u16;
|
||||
let end = 61000u16;
|
||||
let port1 = rand::random::<u16>() % (end - 1 - start) + start;
|
||||
let port2 = rand::random::<u16>() % (end - port1) + port1;
|
||||
(port1, port2)
|
||||
}
|
||||
|
||||
pub const GDB_ARGS: &[&str] = &[
|
||||
"-chardev",
|
||||
"socket,path=/tmp/aster-gdb-socket,server=on,wait=off,id=gdb0",
|
||||
"-gdb",
|
||||
"chardev:gdb0",
|
||||
"-S",
|
||||
];
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
if args.run_gdb_client {
|
||||
let gdb_grub = args.boot_method == BootMethod::QemuGrub;
|
||||
// You should comment out the next line if you want to debug grub instead
|
||||
// of the kernel because this argument is not exposed by runner CLI.
|
||||
let gdb_grub = gdb_grub && false;
|
||||
gdb::run_gdb_client(&args.path, gdb_grub);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut qemu_cmd = Command::new("qemu-system-x86_64");
|
||||
|
||||
qemu_cmd.args(COMMON_ARGS);
|
||||
|
||||
qemu_cmd.arg("-netdev");
|
||||
let (port1, port2) = random_hostfwd_ports();
|
||||
qemu_cmd.arg(format!(
|
||||
"user,id=net01,hostfwd=tcp::{}-:22,hostfwd=tcp::{}-:8080",
|
||||
port1, port2
|
||||
));
|
||||
println!(
|
||||
"[aster-runner] Binding host ports to guest ports: ({} -> {}); ({} -> {}).",
|
||||
port1, 22, port2, 8080
|
||||
);
|
||||
|
||||
if args.halt_for_gdb {
|
||||
if args.enable_kvm {
|
||||
println!("[aster-runner] Can't enable KVM when running QEMU as a GDB server. Abort.");
|
||||
return;
|
||||
}
|
||||
qemu_cmd.args(GDB_ARGS);
|
||||
}
|
||||
|
||||
if args.enable_kvm {
|
||||
qemu_cmd.arg("-enable-kvm");
|
||||
}
|
||||
// Add machine-specific arguments
|
||||
if args.boot_method == BootMethod::QemuGrub {
|
||||
qemu_cmd.args(qemu_grub_efi::MACHINE_ARGS);
|
||||
} else if args.boot_method == BootMethod::Microvm {
|
||||
qemu_cmd.args(microvm::MACHINE_ARGS);
|
||||
}
|
||||
// Add device arguments
|
||||
if args.boot_method == BootMethod::Microvm {
|
||||
qemu_cmd.args(microvm::DEVICE_ARGS);
|
||||
} else if args.emulate_iommu {
|
||||
qemu_cmd.args(qemu_grub_efi::IOMMU_DEVICE_ARGS);
|
||||
} else {
|
||||
qemu_cmd.args(qemu_grub_efi::NOIOMMU_DEVICE_ARGS);
|
||||
}
|
||||
|
||||
// TODO: Add arguments to the runner CLI tool so that the user can specify
|
||||
// a list of disk drives, each of which may be in a different FS format.
|
||||
let ext2_image = get_fs_image(&PathBuf::from("regression/build/ext2.img"), 0);
|
||||
qemu_cmd.arg("-drive");
|
||||
qemu_cmd.arg(ext2_image);
|
||||
|
||||
if args.boot_method == BootMethod::Microvm {
|
||||
let image = microvm::create_bootdev_image(args.path);
|
||||
qemu_cmd.arg("-kernel");
|
||||
qemu_cmd.arg(image.as_os_str());
|
||||
qemu_cmd.arg("-append");
|
||||
qemu_cmd.arg(&args.kcmdline);
|
||||
qemu_cmd.arg("-initrd");
|
||||
qemu_cmd.arg("regression/build/initramfs.cpio.gz");
|
||||
} else if args.boot_method == BootMethod::QemuGrub {
|
||||
let grub_cfg = qemu_grub_efi::generate_grub_cfg(
|
||||
"runner/grub/grub.cfg.template",
|
||||
&args.kcmdline,
|
||||
args.skip_grub_menu,
|
||||
args.boot_protocol,
|
||||
);
|
||||
let initramfs_path = PathBuf::from("regression/build/initramfs.cpio.gz");
|
||||
let bootdev_image = qemu_grub_efi::create_bootdev_image(
|
||||
args.path,
|
||||
initramfs_path,
|
||||
grub_cfg,
|
||||
args.boot_protocol,
|
||||
);
|
||||
qemu_cmd.arg("-cdrom");
|
||||
qemu_cmd.arg(bootdev_image.as_os_str());
|
||||
}
|
||||
|
||||
println!("[aster-runner] Running: {:#?}", qemu_cmd);
|
||||
|
||||
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 get_fs_image(path: &Path, drive_id: u32) -> String {
|
||||
if !path.exists() {
|
||||
panic!("can not find the fs image")
|
||||
}
|
||||
|
||||
format!(
|
||||
"file={},if=none,format=raw,id=x{}",
|
||||
path.to_string_lossy(),
|
||||
drive_id
|
||||
)
|
||||
}
|
@ -43,6 +43,7 @@ extern crate alloc;
|
||||
extern crate lru;
|
||||
#[macro_use]
|
||||
extern crate controlled;
|
||||
#[cfg(ktest)]
|
||||
#[macro_use]
|
||||
extern crate ktest;
|
||||
#[macro_use]
|
||||
|
@ -579,7 +579,7 @@ pub fn current() -> Arc<Process> {
|
||||
}
|
||||
}
|
||||
|
||||
#[if_cfg_ktest]
|
||||
#[cfg(ktest)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
|
@ -133,7 +133,7 @@ impl<R> VmarChildOptions<R> {
|
||||
}
|
||||
}
|
||||
|
||||
#[if_cfg_ktest]
|
||||
#[cfg(ktest)]
|
||||
mod test {
|
||||
use aster_frame::vm::VmIo;
|
||||
use aster_rights::Full;
|
||||
|
@ -515,7 +515,7 @@ impl VmoChildType for VmoSliceChild {}
|
||||
pub struct VmoCowChild;
|
||||
impl VmoChildType for VmoCowChild {}
|
||||
|
||||
#[if_cfg_ktest]
|
||||
#[cfg(ktest)]
|
||||
mod test {
|
||||
use aster_frame::vm::VmIo;
|
||||
use aster_rights::Full;
|
||||
|
@ -5,8 +5,6 @@
|
||||
|
||||
use core::ops::Mul;
|
||||
|
||||
use ktest::if_cfg_ktest;
|
||||
|
||||
/// A `Coeff` is used to do a fraction multiplication operation with an unsigned integer.
|
||||
/// It can achieve accurate and efficient calculation and avoid numeric overflow at the same time.
|
||||
///
|
||||
@ -127,7 +125,7 @@ impl Mul<u32> for Coeff {
|
||||
}
|
||||
}
|
||||
|
||||
#[if_cfg_ktest]
|
||||
#[cfg(ktest)]
|
||||
mod test {
|
||||
use ktest::ktest;
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
[workspace]
|
||||
members = ["foo", "bar"]
|
||||
members = ["foo1", "bar1"]
|
@ -1,10 +1,10 @@
|
||||
[components]
|
||||
foo = { name = "foo" }
|
||||
bar = { name = "bar" }
|
||||
foo1 = { name = "foo1" }
|
||||
bar1 = { name = "bar1" }
|
||||
|
||||
[whitelist]
|
||||
[whitelist.foo.foo_add]
|
||||
bar = true
|
||||
[whitelist.foo1.foo_add]
|
||||
bar1 = true
|
||||
|
||||
[whitelist.foo.FOO_ITEM]
|
||||
bar = true
|
||||
[whitelist.foo1.FOO_ITEM]
|
||||
bar1 = true
|
@ -1,5 +0,0 @@
|
||||
pub static BAR: &'static usize = &foo::FOO_ITEM;
|
||||
|
||||
pub fn add(left: usize, right: usize) -> usize {
|
||||
foo::foo_add(left, right)
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "bar"
|
||||
name = "bar1"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
foo = {path = "../foo"}
|
||||
foo1 = {path = "../foo1"}
|
@ -0,0 +1,5 @@
|
||||
pub static BAR: &'static usize = &foo1::FOO_ITEM;
|
||||
|
||||
pub fn add(left: usize, right: usize) -> usize {
|
||||
foo1::foo_add(left, right)
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "foo"
|
||||
name = "foo1"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
@ -11,7 +11,7 @@ mod test_utils;
|
||||
fn trait_method() {
|
||||
let stderr = run_cargo_component_cmd!();
|
||||
assert!(stderr.contains("access controlled entry point is disallowed"));
|
||||
assert!(stderr.contains("access foo::Foo::method in bar"));
|
||||
assert!(stderr.contains("access foo::FooTrait::trait_associate_fn in bar"));
|
||||
assert!(stderr.contains("access foo::FooTrait::trait_method in bar"));
|
||||
assert!(stderr.contains("access foo2::Foo::method in bar2"));
|
||||
assert!(stderr.contains("access foo2::FooTrait::trait_associate_fn in bar2"));
|
||||
assert!(stderr.contains("access foo2::FooTrait::trait_method in bar2"));
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
[workspace]
|
||||
members = ["foo", "bar"]
|
||||
members = ["foo2", "bar2"]
|
||||
|
@ -1,19 +1,19 @@
|
||||
[components]
|
||||
foo = { name = "foo" }
|
||||
bar = { name = "bar" }
|
||||
foo2 = { name = "foo2" }
|
||||
bar2 = { name = "bar2" }
|
||||
|
||||
[whitelist]
|
||||
[whitelist.foo.FooTrait.trait_method]
|
||||
bar = false
|
||||
[whitelist.foo2.FooTrait.trait_method]
|
||||
bar2 = false
|
||||
|
||||
[whitelist.foo.FooTrait.trait_associate_fn]
|
||||
bar = false
|
||||
[whitelist.foo2.FooTrait.trait_associate_fn]
|
||||
bar2 = false
|
||||
|
||||
[whitelist.foo.Foo.method]
|
||||
bar = false
|
||||
[whitelist.foo2.Foo.method]
|
||||
bar2 = false
|
||||
|
||||
[whitelist.foo.ObjectSafeTrait.get]
|
||||
bar = true
|
||||
[whitelist.foo2.ObjectSafeTrait.get]
|
||||
bar2 = true
|
||||
|
||||
[whitelist.foo.Foo.associate_fn]
|
||||
bar = true
|
||||
[whitelist.foo2.Foo.associate_fn]
|
||||
bar2 = true
|
||||
|
@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "bar"
|
||||
name = "bar2"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
foo = {path = "../foo"}
|
||||
foo2 = {path = "../foo2"}
|
@ -1,4 +1,4 @@
|
||||
use foo::{ObjectSafeTrait, Foo, FooTrait};
|
||||
use foo2::{ObjectSafeTrait, Foo, FooTrait};
|
||||
|
||||
pub fn method() {
|
||||
let foo_struct = Foo::associate_fn();
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "foo"
|
||||
name = "foo2"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
@ -12,6 +12,6 @@ mod test_utils;
|
||||
fn violate_policy() {
|
||||
let stderr = run_cargo_component_cmd!();
|
||||
assert!(stderr.contains("access controlled entry point is disallowed"));
|
||||
assert!(stderr.contains("access foo::foo_add in bar"));
|
||||
assert!(stderr.contains("access foo::FOO_ITEM in bar"));
|
||||
assert!(stderr.contains("access foo3::foo_add in bar3"));
|
||||
assert!(stderr.contains("access foo3::FOO_ITEM in bar3"));
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
[workspace]
|
||||
members = ["foo", "bar"]
|
||||
members = ["foo3", "bar3"]
|
@ -1,5 +1,5 @@
|
||||
[components]
|
||||
foo = { name = "foo" }
|
||||
bar = { name = "bar" }
|
||||
foo3 = { name = "foo3" }
|
||||
bar3 = { name = "bar3" }
|
||||
|
||||
[whitelist]
|
@ -1,5 +0,0 @@
|
||||
pub static BAR: &'static usize = &foo::FOO_ITEM;
|
||||
|
||||
pub fn add(left: usize, right: usize) -> usize {
|
||||
foo::foo_add(left, right)
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "bar"
|
||||
name = "bar3"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
foo = {path = "../foo"}
|
||||
foo3 = {path = "../foo3"}
|
@ -0,0 +1,5 @@
|
||||
pub static BAR: &'static usize = &foo3::FOO_ITEM;
|
||||
|
||||
pub fn add(left: usize, right: usize) -> usize {
|
||||
foo3::foo_add(left, right)
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "foo"
|
||||
name = "foo3"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::{collections::HashMap, fs::File, io::Read, ops::Add, process::Command, str::FromStr};
|
||||
use std::{collections::HashMap, fs::File, io::Read, ops::Add, path::PathBuf, process::Command, str::FromStr};
|
||||
|
||||
use json::JsonValue;
|
||||
use proc_macro2::{Group, TokenStream};
|
||||
@ -11,6 +11,7 @@ use crate::COMPONENT_FILE_NAME;
|
||||
#[derive(Debug)]
|
||||
pub struct ComponentInfo {
|
||||
name: String,
|
||||
/// The absolute path to the component
|
||||
path: String,
|
||||
priority: u16,
|
||||
}
|
||||
@ -87,13 +88,14 @@ pub fn component_generate() -> Vec<ComponentInfo> {
|
||||
};
|
||||
let component_info = ComponentInfo {
|
||||
name: package["name"].as_str().unwrap().to_string(),
|
||||
path: path.to_owned(),
|
||||
path: PathBuf::from(&workspace_root).join(path).to_str().unwrap().to_string(),
|
||||
priority: *mapping
|
||||
.get(&package["name"].as_str().unwrap().to_string())
|
||||
.unwrap(),
|
||||
};
|
||||
components_info.push(component_info)
|
||||
}
|
||||
|
||||
components_info
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user