Implement OSDK functionalities and opt-in OSDK for asterinas

This commit is contained in:
Zhang Junyang
2024-02-21 16:58:40 +08:00
committed by Tate, Hongliang Tian
parent bc9bce9dea
commit f97d0f1260
103 changed files with 1663 additions and 1295 deletions

View File

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

View File

@ -1,74 +0,0 @@
ENTRY(__multiboot_boot)
OUTPUT_ARCH(i386:x86-64)
OUTPUT_FORMAT(elf64-x86-64)
KERNEL_LMA = 0x8000000;
LINUX_32_ENTRY = 0x8001000;
KERNEL_VMA = 0xffffffff80000000;
SECTIONS
{
. = KERNEL_LMA;
__kernel_start = .;
.multiboot_header : { KEEP(*(.multiboot_header)) }
.multiboot2_header : { KEEP(*(.multiboot2_header)) }
. = LINUX_32_ENTRY;
.boot : { KEEP(*(.boot)) }
. += KERNEL_VMA;
.text : AT(ADDR(.text) - KERNEL_VMA) {
*(.text .text.*)
PROVIDE(__etext = .);
}
.rodata : AT(ADDR(.rodata) - KERNEL_VMA) { *(.rodata .rodata.*) }
.eh_frame_hdr : AT(ADDR(.eh_frame_hdr) - KERNEL_VMA) {
KEEP(*(.eh_frame_hdr .eh_frame_hdr.*))
}
. = ALIGN(8);
.eh_frame : AT(ADDR(.eh_frame) - KERNEL_VMA) {
PROVIDE(__eh_frame = .);
KEEP(*(.eh_frame .eh_frame.*))
}
.gcc_except_table : AT(ADDR(.gcc_except_table) - KERNEL_VMA) { *(.gcc_except_table .gcc_except_table.*) }
.data.rel.ro : AT(ADDR(.data.rel.ro) - KERNEL_VMA) { *(.data.rel.ro .data.rel.ro.*) }
.dynamic : AT(ADDR(.dynamic) - KERNEL_VMA) { *(.dynamic) }
.init_array : AT(ADDR(.init_array) - KERNEL_VMA) {
__sinit_array = .;
KEEP(*(SORT(.init_array .init_array.*)))
__einit_array = .;
}
.got : AT(ADDR(.got) - KERNEL_VMA) { *(.got .got.*) }
.got.plt : AT(ADDR(.got.plt) - KERNEL_VMA) { *(.got.plt .got.plt.*) }
. = DATA_SEGMENT_RELRO_END(0, .);
.data : AT(ADDR(.data) - KERNEL_VMA) { *(.data .data.*) }
.bss : AT(ADDR(.bss) - KERNEL_VMA) {
__bss = .;
*(.bss .bss.*) *(COMMON)
__bss_end = .;
}
.ktest_array : AT(ADDR(.ktest_array) - KERNEL_VMA) {
__ktest_array = .;
KEEP(*(SORT(.ktest_array)))
__ktest_array_end = .;
}
.tdata : AT(ADDR(.tdata) - KERNEL_VMA) { *(.tdata .tdata.*) }
.tbss : AT(ADDR(.tbss) - KERNEL_VMA) { *(.tbss .tbss.*) }
. = DATA_SEGMENT_END(.);
__kernel_end = . - KERNEL_VMA;
}

View File

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

View File

@ -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),
};
}

View File

@ -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() {

View File

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

View File

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

View File

@ -288,7 +288,7 @@ impl fmt::Debug for AtomicBits {
}
}
#[if_cfg_ktest]
#[cfg(ktest)]
mod test {
use super::*;

View File

@ -162,7 +162,7 @@ impl HasPaddr for DmaCoherent {
}
}
#[if_cfg_ktest]
#[cfg(ktest)]
mod test {
use alloc::vec;

View File

@ -195,7 +195,7 @@ impl HasPaddr for DmaStream {
}
}
#[if_cfg_ktest]
#[cfg(ktest)]
mod test {
use alloc::vec;

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

View 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()
}

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,2 @@
# The Rust build cache for the setup crate is placed here.
target/

View File

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