From 9dcd410075b9bf719c735f7e1b773ddd5f8e05e9 Mon Sep 17 00:00:00 2001 From: Graham MacDonald Date: Sun, 21 Jul 2024 15:59:56 +0100 Subject: [PATCH 1/2] Add support for qemu machine raspi4b Signed-off-by: Graham MacDonald --- aarch64/lib/config_default.toml | 8 +- aarch64/lib/config_raspi4b.toml | 14 +++ aarch64/src/main.rs | 27 +++-- aarch64/src/pagealloc.rs | 4 +- aarch64/src/uartmini.rs | 4 +- port/src/bitmapalloc.rs | 3 +- port/src/mem.rs | 3 +- xtask/src/config.rs | 193 ++++++++++++++++++++------------ xtask/src/main.rs | 35 +++--- 9 files changed, 182 insertions(+), 109 deletions(-) create mode 100644 aarch64/lib/config_raspi4b.toml diff --git a/aarch64/lib/config_default.toml b/aarch64/lib/config_default.toml index 901ebb3..11d7bf0 100644 --- a/aarch64/lib/config_default.toml +++ b/aarch64/lib/config_default.toml @@ -1,8 +1,6 @@ [build] target = "lib/aarch64-unknown-none-elf.json" -buildflags = [ - "-Z", "build-std=core,alloc" -] +buildflags = ["-Z", "build-std=core,alloc"] [link] # linker script to use @@ -10,3 +8,7 @@ script = 'aarch64/lib/kernel.ld' # kernel load address to insert into kernel.ld load-address = '0xffff800000100000 - 0x80000' + +[qemu] +machine = "raspi3b" +dtb = "aarch64/lib/bcm2710-rpi-3-b.dtb" diff --git a/aarch64/lib/config_raspi4b.toml b/aarch64/lib/config_raspi4b.toml new file mode 100644 index 0000000..7c48d8a --- /dev/null +++ b/aarch64/lib/config_raspi4b.toml @@ -0,0 +1,14 @@ +[build] +target = "lib/aarch64-unknown-none-elf.json" +buildflags = ["-Z", "build-std=core,alloc"] + +[link] +# linker script to use +script = 'aarch64/lib/kernel.ld' + +# kernel load address to insert into kernel.ld +load-address = '0xffff800000100000 - 0x80000' + +[qemu] +machine = "raspi4b" +dtb = "aarch64/lib/bcm2711-rpi-4-b.dtb" diff --git a/aarch64/src/main.rs b/aarch64/src/main.rs index 43f98cd..2edce89 100644 --- a/aarch64/src/main.rs +++ b/aarch64/src/main.rs @@ -66,14 +66,16 @@ fn print_binary_sections() { } } -fn print_memory_info() { +fn print_physical_memory_info() { println!("Physical memory map:"); let arm_mem = mailbox::get_arm_memory(); println!(" Memory:\t{arm_mem} ({:#x})", arm_mem.size()); let vc_mem = mailbox::get_vc_memory(); println!(" Video:\t{vc_mem} ({:#x})", vc_mem.size()); +} - println!("Memory usage::"); +fn print_memory_info() { + println!("Memory usage:"); let (used, total) = pagealloc::usage_bytes(); println!(" Used:\t\t{used:#016x}"); println!(" Total:\t{total:#016x}"); @@ -84,25 +86,26 @@ fn print_pi_name(board_revision: u32) { let name = match board_revision { 0xa21041 => "Raspberry Pi 2B", 0xa02082 => "Raspberry Pi 3B", + 0xb03115 => "Raspberry Pi 4B", 0xa220a0 => "Raspberry Compute Module 3", - _ => "Unknown", + _ => "Unrecognised", }; - println!(" Board Name: {name}"); + println!(" Board Name:\t{name}"); } fn print_board_info() { println!("Board information:"); let board_revision = mailbox::get_board_revision(); print_pi_name(board_revision); - println!(" Board Revision: {board_revision:#010x}"); + println!(" Board Rev:\t{board_revision:#010x}"); let model = mailbox::get_board_model(); - println!(" Board Model: {model:#010x}"); + println!(" Board Model:\t{model:#010x}"); let serial = mailbox::get_board_serial(); - println!(" Serial Number: {serial:#010x}"); + println!(" Serial Num:\t{serial:#010x}"); let mailbox::MacAddress { a, b, c, d, e, f } = mailbox::get_board_macaddr(); - println!(" MAC Address: {a:02x}:{b:02x}:{c:02x}:{d:02x}:{e:02x}:{f:02x}"); + println!(" MAC Address:\t{a:02x}:{b:02x}:{c:02x}:{d:02x}:{e:02x}:{f:02x}"); let fw_revision = mailbox::get_firmware_revision(); - println!(" Firmware Revision: {fw_revision:#010x}"); + println!(" Firmware Rev:\t{fw_revision:#010x}"); } /// dtb_va is the virtual address of the DTB structure. The physical address is @@ -123,6 +126,10 @@ pub extern "C" fn main9(dtb_va: usize) { println!("DTB found at: {:#x}", dtb_va); println!("midr_el1: {:?}", registers::MidrEl1::read()); + print_binary_sections(); + print_physical_memory_info(); + print_board_info(); + // Map address space accurately using rust VM code to manage page tables unsafe { let dtb_range = PhysRange::with_len(from_virt_to_physaddr(dtb_va).addr(), dt.size()); @@ -132,9 +139,7 @@ pub extern "C" fn main9(dtb_va: usize) { // From this point we can use the global allocator - print_binary_sections(); print_memory_info(); - print_board_info(); kernel_root().print_recursive_tables(); diff --git a/aarch64/src/pagealloc.rs b/aarch64/src/pagealloc.rs index bba2d0a..f3ca378 100644 --- a/aarch64/src/pagealloc.rs +++ b/aarch64/src/pagealloc.rs @@ -19,9 +19,9 @@ use port::{ }; /// Set up bitmap page allocator assuming everything is allocated. -static PAGE_ALLOC: Lock> = Lock::new( +static PAGE_ALLOC: Lock> = Lock::new( "page_alloc", - const { BitmapPageAlloc::<16, PAGE_SIZE_4K>::new_all_allocated(PAGE_SIZE_4K) }, + const { BitmapPageAlloc::<32, PAGE_SIZE_4K>::new_all_allocated(PAGE_SIZE_4K) }, ); /// The bitmap allocator has all pages marked as allocated initially. We'll diff --git a/aarch64/src/uartmini.rs b/aarch64/src/uartmini.rs index 687c8d2..cf24aff 100644 --- a/aarch64/src/uartmini.rs +++ b/aarch64/src/uartmini.rs @@ -20,10 +20,12 @@ pub struct MiniUart { #[allow(dead_code)] impl MiniUart { pub fn new(dt: &DeviceTree, mmio_virt_offset: usize) -> MiniUart { - // TODO use aliases? + // Bcm2835 and bcm2711 are essentially the same for our needs here. + // If fdt.rs supported aliases well, we could try to just look up 'gpio'. let gpio_range = VirtRange::from( &dt.find_compatible("brcm,bcm2835-gpio") .next() + .or_else(|| dt.find_compatible("brcm,bcm2711-gpio").next()) .and_then(|uart| dt.property_translated_reg_iter(uart).next()) .and_then(|reg| reg.regblock()) .unwrap() diff --git a/port/src/bitmapalloc.rs b/port/src/bitmapalloc.rs index 680626f..a5114a6 100644 --- a/port/src/bitmapalloc.rs +++ b/port/src/bitmapalloc.rs @@ -35,6 +35,7 @@ impl Bitmap { #[derive(Debug, PartialEq)] pub enum BitmapPageAllocError { + NotEnoughBitmaps, OutOfBounds, MisalignedAddr, OutOfSpace, @@ -215,7 +216,7 @@ impl check_end: bool, ) -> Result<(), BitmapPageAllocError> { if check_end && range.0.end > self.end { - return Err(BitmapPageAllocError::OutOfBounds); + return Err(BitmapPageAllocError::NotEnoughBitmaps); } for pa in range.step_by_rounded(self.alloc_page_size) { diff --git a/port/src/mem.rs b/port/src/mem.rs index d9f7087..40154ee 100644 --- a/port/src/mem.rs +++ b/port/src/mem.rs @@ -149,8 +149,7 @@ impl PhysRange { impl fmt::Display for PhysRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:#016x}..{:#016x}", self.0.start.addr(), self.0.end.addr())?; - Ok(()) + write!(f, "{:#016x}..{:#016x}", self.0.start.addr(), self.0.end.addr()) } } diff --git a/xtask/src/config.rs b/xtask/src/config.rs index 5d99726..09539be 100644 --- a/xtask/src/config.rs +++ b/xtask/src/config.rs @@ -1,6 +1,6 @@ /// Test /// -use crate::{cargo, Command, Profile}; +use crate::{Command, Profile}; use serde::{Deserialize, Serialize}; use std::{ @@ -65,6 +65,20 @@ pub struct Config { /// pub mod virt; /// ``` pub platform: Option, + + /// Filepath of DTB file relative to crate + pub dtb: Option, +} + +/// Qemu section +/// Affects arguments to be passed to qemu - doesn't affect build artefacts. +#[derive(Debug, Serialize, Deserialize)] +pub struct Qemu { + /// Machine (`-M`) value for qemu: raspi3b, raspi4b, etc. + pub machine: Option, + + /// Filepath of DTB file relative to crate + pub dtb: Option, } /// the TOML document @@ -73,6 +87,7 @@ pub struct Configuration { pub build: Option, pub config: Option, pub link: Option>, + pub qemu: Option, } impl Configuration { @@ -96,40 +111,28 @@ impl Configuration { } } -/// task could be 'build', 'clippy' -pub fn generate_args( - task: &str, - config: &Configuration, - target: &str, - profile: &Profile, - wks_path: &str, -) -> Command { - // we have to pass the rustflags all at once, so collect them - let mut rustflags: Vec = Vec::new(); - let mut cmd = Command::new(cargo()); - cmd.arg(task); - +fn apply_build(cmd: &mut Command, rustflags: &mut Vec, config: &Configuration) { if let Some(config) = &config.build { - if task != "clippy" { - let target = &config.target; - cmd.arg("--target").arg(target); - - if let Some(flags) = &config.buildflags { - // add the buildflags to the command - for f in flags { - cmd.arg(f); - } + let target = &config.target; + cmd.arg("--target").arg(target); + + if let Some(flags) = &config.buildflags { + // add the buildflags to the command + for f in flags { + cmd.arg(f); } + } - if let Some(flags) = &config.rustflags { - // store the passed rustflags temporarily - for f in flags { - rustflags.push(f.to_string()); - } + if let Some(flags) = &config.rustflags { + // store the passed rustflags temporarily + for f in flags { + rustflags.push(f.to_string()); } } } +} +fn apply_platform_config(cmd: &mut Command, rustflags: &mut Vec, config: &Configuration) { if let Some(config) = &config.config { // if the target will use features make them available if let Some(features) = &config.features { @@ -194,66 +197,110 @@ pub fn generate_args( } } } +} +fn apply_link( + rustflags: &mut Vec, + config: &Configuration, + target: &str, + profile: &Profile, + workspace_path: &str, +) { // we don't need to handle the linker script for clippy - if task != "clippy" { - if let Some(link) = &config.link { - let filename = link["script"].clone(); - - // do we have a linker script ? - if !filename.is_empty() { - let mut contents = match fs::read_to_string(format!("{}/{}", wks_path, filename)) { - Ok(c) => c, - Err(_) => { - eprintln!("Could not read file `{filename}`"); - exit(1); - } - }; - - // replace the placeholders with the values from the TOML - if let Some(link) = &config.link { - for l in link.iter() { - match l.0.as_str() { - "arch" => contents = contents.replace("${ARCH}", l.1), - "load-address" => contents = contents.replace("${LOAD-ADDRESS}", l.1), - "script" => {} // do nothing for the script option - _ => eprintln!("ignoring unknown option '{} = {}'", l.0, l.1), - } + if let Some(link) = &config.link { + let filename = link["script"].clone(); + + // do we have a linker script ? + if !filename.is_empty() { + let mut contents = match fs::read_to_string(format!("{}/{}", workspace_path, filename)) + { + Ok(c) => c, + Err(_) => { + eprintln!("Could not read file `{filename}`"); + exit(1); + } + }; + + // replace the placeholders with the values from the TOML + if let Some(link) = &config.link { + for l in link.iter() { + match l.0.as_str() { + "arch" => contents = contents.replace("${ARCH}", l.1), + "load-address" => contents = contents.replace("${LOAD-ADDRESS}", l.1), + "script" => {} // do nothing for the script option + _ => eprintln!("ignoring unknown option '{} = {}'", l.0, l.1), } } + } - // construct the path to the target directory - let path = format!( - "{}/target/{}/{}", - wks_path, - target, - profile.to_string().to_lowercase() - ); - - // make sure the target directory exists - if !std::path::Path::new(&path).exists() { - // if not, create it - let _ = create_dir_all(&path); - } + // construct the path to the target directory + let path = format!( + "{}/target/{}/{}", + workspace_path, + target, + profile.to_string().to_lowercase() + ); + + // make sure the target directory exists + if !std::path::Path::new(&path).exists() { + // if not, create it + let _ = create_dir_all(&path); + } - // everything is setup, now create the linker script - // in the target directory - let mut file = File::create(format!("{}/kernel.ld", path)).unwrap(); - let _ = file.write_all(contents.as_bytes()); + // everything is setup, now create the linker script + // in the target directory + let mut file = File::create(format!("{}/kernel.ld", path)).unwrap(); + let _ = file.write_all(contents.as_bytes()); - // pass the script path to the rustflags - rustflags.push(format!("-Clink-args=-T{}/kernel.ld", path)); - } + // pass the script path to the rustflags + rustflags.push(format!("-Clink-args=-T{}/kernel.ld", path)); + } + } +} + +fn apply_qemu_config(cmd: &mut Command, config: &Configuration) { + if let Some(config) = &config.qemu { + if let Some(machine) = &config.machine { + cmd.arg("-M"); + cmd.arg(machine); + } + if let Some(dtb) = &config.dtb { + cmd.arg("-dtb"); + cmd.arg(dtb); } } +} +fn apply_rustflags(cmd: &mut Command, rustflags: &[String]) { // pass the collected rustflags - // !! this overwrites the build.rustflags from the target Cargo.toml !! + // !! this overrides the build.rustflags from the target Cargo.toml !! if !rustflags.is_empty() { let flat = rustflags.join(" "); cmd.arg("--config"); cmd.arg(format!("build.rustflags='{}'", flat)); } +} + +pub fn apply_to_clippy_step(cmd: &mut Command, config: &Configuration) { + let mut rustflags: Vec = Vec::new(); + apply_platform_config(cmd, &mut rustflags, config); + apply_rustflags(cmd, &rustflags); +} + +pub fn apply_to_build_step( + cmd: &mut Command, + config: &Configuration, + target: &str, + profile: &Profile, + workspace_path: &str, +) { + let mut rustflags: Vec = Vec::new(); + apply_build(cmd, &mut rustflags, config); + apply_platform_config(cmd, &mut rustflags, config); + apply_link(&mut rustflags, config, target, profile, workspace_path); + apply_rustflags(cmd, &rustflags); +} - cmd +pub fn apply_to_qemu_step(cmd: &mut Command, config: &Configuration) { + apply_qemu_config(cmd, config); } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 88ca412..03d4afa 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,4 +1,5 @@ -use crate::config::{generate_args, Configuration}; +use crate::config::Configuration; +use config::{apply_to_build_step, apply_to_clippy_step, apply_to_qemu_step}; use std::{ env, fmt, path::{Path, PathBuf}, @@ -306,13 +307,17 @@ impl BuildStep { } fn run(self) -> Result<()> { - let mut cmd = generate_args( - "build", + let mut cmd = Command::new(cargo()); + cmd.arg("build"); + + apply_to_build_step( + &mut cmd, &self.config, &self.arch.target(), &self.profile, workspace().to_str().unwrap(), ); + cmd.current_dir(workspace()); cmd.arg("--workspace"); cmd.arg("--exclude").arg("xtask"); @@ -429,6 +434,7 @@ impl DistStep { struct QemuStep { arch: Arch, + config: Configuration, profile: Profile, wait_for_gdb: bool, kvm: bool, @@ -439,6 +445,7 @@ struct QemuStep { impl QemuStep { fn new(matches: &clap::ArgMatches) -> Self { let arch = Arch::from(matches); + let config = load_config(arch, matches); let profile = Profile::from(matches); let wait_for_gdb = matches.get_flag("gdb"); let kvm = matches.get_flag("kvm"); @@ -450,7 +457,7 @@ impl QemuStep { .clone(); let verbose = verbose(matches); - Self { arch, profile, wait_for_gdb, kvm, dump_dtb, verbose } + Self { arch, config, profile, wait_for_gdb, kvm, dump_dtb, verbose } } fn run(self) -> Result<()> { @@ -466,6 +473,8 @@ impl QemuStep { Arch::Aarch64 => { let mut cmd = Command::new(qemu_system); + apply_to_qemu_step(&mut cmd, &self.config); + // TODO Choose UART at cmdline // If using UART0 (PL011), this enables serial cmd.arg("-nographic"); @@ -476,13 +485,9 @@ impl QemuStep { cmd.arg("-serial"); cmd.arg("mon:stdio"); - cmd.arg("-M"); - cmd.arg("raspi3b"); if self.wait_for_gdb { cmd.arg("-s").arg("-S"); } - cmd.arg("-dtb"); - cmd.arg("aarch64/lib/bcm2710-rpi-3-b.dtb"); // Show exception level change events in stdout cmd.arg("-d"); cmd.arg("int"); @@ -510,7 +515,7 @@ impl QemuStep { } cmd.arg("-cpu").arg("rv64"); // FIXME: This is not needed as of now, and will only work once the - // FIXME: // disk.bin is also taken care of. Doesn't exist by default. + // FIXME: disk.bin is also taken care of. Doesn't exist by default. if false { cmd.arg("-drive").arg("file=disk.bin,format=raw,id=hd0"); cmd.arg("-device").arg("virtio-blk-device,drive=hd0"); @@ -729,13 +734,11 @@ impl ClippyStep { } fn run(self) -> Result<()> { - let mut cmd = generate_args( - "clippy", - &self.config, - &self.arch.target(), - &self.profile, - workspace().to_str().unwrap(), - ); + let mut cmd = Command::new(cargo()); + cmd.arg("clippy"); + + apply_to_clippy_step(&mut cmd, &self.config); + cmd.current_dir(workspace()); cmd.arg("--workspace"); exclude_other_arches(self.arch, &mut cmd); From bbca3a4368eccf1c6171454edacfe200fbe57b40 Mon Sep 17 00:00:00 2001 From: Graham MacDonald Date: Sun, 21 Jul 2024 16:08:47 +0100 Subject: [PATCH 2/2] Update platforms in readme Signed-off-by: Graham MacDonald --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 797811d..4b64c35 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,13 @@ check to ensure `qemu` or `qemu-kvm` is installed and the R9 can be run using qemu for the various supported architectures: -|Arch|Commandline| -|----|-----------| -|aarch64|cargo xtask qemu --arch aarch64 --verbose| -|x86-64|cargo xtask qemu --arch x86-64 --verbose| -|x86-64 (with kvm)|cargo xtask qemu --arch x86-64 --kvm --verbose| -|riscv|cargo xtask qemu --arch riscv64 --verbose| +|Arch|Platform|Commandline| +|----|--------|-----------| +|aarch64|raspi3b|cargo xtask qemu --arch aarch64 --verbose| +|aarch64|raspi4b|cargo xtask qemu --arch aarch64 --config raspi4b --verbose| +|x86-64|q35|cargo xtask qemu --arch x86-64 --verbose| +|x86-64 (with kvm)|q35|cargo xtask qemu --arch x86-64 --kvm --verbose| +|riscv|virt|cargo xtask qemu --arch riscv64 --verbose| ## Running on Real Hardware™️