diff --git a/xtask/src/main.rs b/xtask/src/main.rs index f39c78e..7241700 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -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 = 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")] @@ -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, @@ -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() @@ -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) } @@ -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"]) @@ -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 { diff --git a/xtask/src/user.rs b/xtask/src/user.rs index 57a2986..ed4f5d7 100644 --- a/xtask/src/user.rs +++ b/xtask/src/user.rs @@ -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}; @@ -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, @@ -41,7 +41,7 @@ impl Cases { } } -fn build_one(name: impl AsRef, release: bool, base_address: u64) -> PathBuf { +fn build_one(name: impl AsRef, release: bool, base_address: u64, arch: Arch) -> PathBuf { let name = name.as_ref(); let binary = base_address != 0; if binary { @@ -49,7 +49,7 @@ fn build_one(name: impl AsRef, release: bool, base_address: u64) -> PathB } Cargo::build() .package("user_lib") - .target(TARGET_ARCH) + .target(arch.target()) .arg("--bin") .arg(name) .conditional(release, |cargo| { @@ -59,7 +59,8 @@ fn build_one(name: impl AsRef, 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 { @@ -69,37 +70,50 @@ fn build_one(name: impl AsRef, 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::>(&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!( @@ -116,7 +130,7 @@ app_{i}_end:", writeln!( ld, " - .align 3 + .align {align} .section .data .global app_names app_names:" @@ -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()