Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 75 additions & 15 deletions xtask/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,41 @@ use std::{
path::{Path, PathBuf},
};

const TARGET_ARCH: &str = "riscv64gc-unknown-none-elf";

static PROJECT: Lazy<&'static Path> =
pub static PROJECT: Lazy<&'static Path> =
Lazy::new(|| Path::new(std::env!("CARGO_MANIFEST_DIR")).parent().unwrap());

static TARGET: Lazy<PathBuf> = Lazy::new(|| PROJECT.join("target").join(TARGET_ARCH));
/// Target architecture
#[derive(Clone, Copy, Default, ValueEnum)]
pub enum Arch {
/// RISC-V 32-bit
Riscv32,
/// RISC-V 64-bit
#[default]
Riscv64,
}

impl Arch {
/// Get Rust target triple
pub fn target(&self) -> &'static str {
match self {
Arch::Riscv32 => "riscv32imac-unknown-none-elf",
Arch::Riscv64 => "riscv64gc-unknown-none-elf",
}
}

/// Get QEMU system name
pub fn qemu_system(&self) -> &'static str {
match self {
Arch::Riscv32 => "riscv32",
Arch::Riscv64 => "riscv64",
}
}

/// Get target directory
pub fn target_dir(&self) -> PathBuf {
PROJECT.join("target").join(self.target())
}
}

#[derive(Parser)]
#[clap(name = "rCore-Tutorial")]
Expand Down Expand Up @@ -55,6 +84,9 @@ struct BuildArgs {
/// lab or not
#[clap(long)]
lab: bool,
/// target architecture
#[clap(long, value_enum, default_value_t = Arch::Riscv64)]
arch: Arch,
/// features
#[clap(short, long)]
features: Option<String>,
Expand All @@ -64,18 +96,34 @@ struct BuildArgs {
/// build in release mode
#[clap(long)]
release: bool,
/// build for no-bios mode (kernel at 0x80000000)
#[clap(long)]
nobios: bool,
}

impl BuildArgs {
/// Validate arguments
fn validate(&self) {
// RV32 only supports nobios mode
if matches!(self.arch, Arch::Riscv32) && !self.nobios {
eprintln!("Error: RV32 only supports nobios mode. Please add --nobios flag.");
eprintln!("Usage: cargo qemu --ch {} --arch riscv32 --nobios", self.ch);
std::process::exit(1);
}
}

fn make(&self) -> PathBuf {
self.validate();

let target_dir = self.arch.target_dir();
let mut env: HashMap<&str, OsString> = HashMap::new();
let package = match self.ch {
1 => if self.lab { "ch1-lab" } else { "ch1" }.to_string(),
2..=8 => {
user::build_for(self.ch, false);
user::build_for(self.ch, false, self.arch);
env.insert(
"APP_ASM",
TARGET
target_dir
.join("debug")
.join("app.asm")
.as_os_str()
Expand All @@ -85,25 +133,28 @@ impl BuildArgs {
}
_ => unreachable!(),
};
// 生成
// Build
let mut build = Cargo::build();
build
.package(&package)
.optional(&self.features, |cargo, features| {
cargo.features(false, features.split_whitespace());
})
.conditional(self.nobios, |cargo| {
cargo.features(false, ["nobios"]);
})
.optional(&self.log, |cargo, log| {
cargo.env("LOG", log);
})
.conditional(self.release, |cargo| {
cargo.release();
})
.target(TARGET_ARCH);
.target(self.arch.target());
for (key, value) in env {
build.env(key, value);
}
build.invoke();
TARGET
target_dir
.join(if self.release { "release" } else { "debug" })
.join(package)
}
Expand Down Expand Up @@ -148,15 +199,24 @@ struct QemuArgs {
impl QemuArgs {
fn run(self) {
let elf = self.build.make();
let target_dir = self.build.arch.target_dir();

if let Some(p) = &self.qemu_dir {
Qemu::search_at(p);
}
let mut qemu = Qemu::system("riscv64");
let mut qemu = Qemu::system(self.build.arch.qemu_system());
qemu.args(&["-machine", "virt"])
.arg("-nographic")
.arg("-bios")
.arg(PROJECT.join("rustsbi-qemu.bin"))
.arg("-kernel")
.arg("-nographic");

if self.build.nobios {
// No BIOS mode: kernel is loaded at 0x80000000
qemu.args(&["-bios", "none"]);
} else {
// SBI mode: use RustSBI, kernel is loaded at 0x80200000
qemu.arg("-bios").arg(PROJECT.join("rustsbi-qemu.bin"));
}

qemu.arg("-kernel")
.arg(objcopy(elf, true))
.args(&["-smp", &self.smp.unwrap_or(1).to_string()])
.args(&["-m", "64M"])
Expand All @@ -167,7 +227,7 @@ impl QemuArgs {
"-drive",
format!(
"file={},if=none,format=raw,id=x0",
TARGET
target_dir
.join(if self.build.release {
"release"
} else {
Expand Down
48 changes: 31 additions & 17 deletions xtask/src/user.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{fs_pack::easy_fs_pack, objcopy, PROJECT, TARGET, TARGET_ARCH};
use crate::{fs_pack::easy_fs_pack, objcopy, Arch, PROJECT};
use os_xtask_utils::{Cargo, CommandExt};
use serde_derive::Deserialize;
use std::{collections::HashMap, ffi::OsStr, fs::File, io::Write, path::PathBuf};
Expand All @@ -17,14 +17,14 @@ pub struct CasesInfo {
}

impl Cases {
fn build(&mut self, release: bool) -> CasesInfo {
fn build(&mut self, release: bool, arch: Arch) -> CasesInfo {
if let Some(names) = &self.cases {
let base = self.base.unwrap_or(0);
let step = self.step.filter(|_| self.base.is_some()).unwrap_or(0);
let cases = names
.into_iter()
.enumerate()
.map(|(i, name)| build_one(name, release, base + i as u64 * step))
.map(|(i, name)| build_one(name, release, base + i as u64 * step, arch))
.collect();
CasesInfo {
base,
Expand All @@ -41,15 +41,15 @@ impl Cases {
}
}

fn build_one(name: impl AsRef<OsStr>, release: bool, base_address: u64) -> PathBuf {
fn build_one(name: impl AsRef<OsStr>, release: bool, base_address: u64, arch: Arch) -> PathBuf {
let name = name.as_ref();
let binary = base_address != 0;
if binary {
println!("build {name:?} at {base_address:#x}");
}
Cargo::build()
.package("user_lib")
.target(TARGET_ARCH)
.target(arch.target())
.arg("--bin")
.arg(name)
.conditional(release, |cargo| {
Expand All @@ -59,7 +59,8 @@ fn build_one(name: impl AsRef<OsStr>, release: bool, base_address: u64) -> PathB
cargo.env("BASE_ADDRESS", base_address.to_string());
})
.invoke();
let elf = TARGET
let elf = arch
.target_dir()
.join(if release { "release" } else { "debug" })
.join(name);
if binary {
Expand All @@ -69,37 +70,50 @@ fn build_one(name: impl AsRef<OsStr>, release: bool, base_address: u64) -> PathB
}
}

pub fn build_for(ch: u8, release: bool) {
pub fn build_for(ch: u8, release: bool, arch: Arch) {
let cfg = std::fs::read_to_string(PROJECT.join("user/cases.toml")).unwrap();
let mut cases = toml::from_str::<HashMap<String, Cases>>(&cfg)
.unwrap()
.remove(&format!("ch{ch}"))
.unwrap_or_default();
let CasesInfo { base, step, bins } = cases.build(release);
let CasesInfo { base, step, bins } = cases.build(release, arch);
if bins.is_empty() {
return;
}
let asm = TARGET
let target_dir = arch.target_dir();
let asm = target_dir
.join(if release { "release" } else { "debug" })
.join("app.asm");
let mut ld = File::create(asm).unwrap();

// Use .word for RV32, .quad for RV64
let data_directive = match arch {
Arch::Riscv32 => ".word",
Arch::Riscv64 => ".quad",
};

let align = match arch {
Arch::Riscv32 => 2,
Arch::Riscv64 => 3,
};

writeln!(
ld,
"\
.global apps
.section .data
.align 3
.align {align}
apps:
.quad {base:#x}
.quad {step:#x}
.quad {}",
{data_directive} {base:#x}
{data_directive} {step:#x}
{data_directive} {}",
bins.len(),
)
.unwrap();

(0..bins.len()).for_each(|i| writeln!(ld, " .quad app_{i}_start").unwrap());
(0..bins.len()).for_each(|i| writeln!(ld, " {data_directive} app_{i}_start").unwrap());

writeln!(ld, " .quad app_{}_end", bins.len() - 1).unwrap();
writeln!(ld, " {data_directive} app_{}_end", bins.len() - 1).unwrap();

bins.iter().enumerate().for_each(|(i, path)| {
writeln!(
Expand All @@ -116,7 +130,7 @@ app_{i}_end:",
writeln!(
ld,
"
.align 3
.align {align}
.section .data
.global app_names
app_names:"
Expand All @@ -128,7 +142,7 @@ app_names:"
} else if ch >= 6 {
easy_fs_pack(
&cases.cases.unwrap(),
TARGET
target_dir
.join(if release { "release" } else { "debug" })
.into_os_string()
.into_string()
Expand Down