diff --git a/Cargo.lock b/Cargo.lock index 80da4f8..8edf5a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,18 @@ dependencies = [ "libc", ] +[[package]] +name = "arbitrary-int" +version = "1.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + [[package]] name = "atomic-polyfill" version = "1.0.3" @@ -123,6 +135,7 @@ name = "efigife" version = "0.1.0" dependencies = [ "embedded-graphics", + "log", "tinygif", "uefi", "uefi-graphics2", @@ -284,6 +297,17 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "mbrs" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d080a720bb58430e0039347784859f9be41bf8578307b8156be4e02ca3b316e" +dependencies = [ + "arbitrary-int", + "arrayref", + "thiserror", +] + [[package]] name = "memchr" version = "2.7.2" @@ -445,6 +469,26 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thiserror" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "tinygif" version = "0.0.4" @@ -696,4 +740,5 @@ name = "xtask" version = "0.1.0" dependencies = [ "fatfs", + "mbrs", ] diff --git a/Cargo.toml b/Cargo.toml index 1d3b699..29a71eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,8 @@ resolver = "2" [workspace.dependencies] fatfs = { path = "rust-fatfs", default-features = false, features = ["alloc", "chrono"] } embedded-graphics = "0.8.1" +log = "0.4.21" +mbrs = "0.3.1" tinygif = { version = "0.0.4", features = ["8k"] } uefi = { version = "0.28.0", features = ["global_allocator", "panic_handler", "alloc"] } uefi-graphics2 = "0.1.3" diff --git a/efi-bin/Cargo.toml b/efi-bin/Cargo.toml index 7f6f425..72c701a 100644 --- a/efi-bin/Cargo.toml +++ b/efi-bin/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] embedded-graphics.workspace = true +log.workspace = true tinygif.workspace = true uefi.workspace = true uefi-graphics2.workspace = true diff --git a/efi-bin/src/main.rs b/efi-bin/src/main.rs index 3a5dcd4..c1189ae 100644 --- a/efi-bin/src/main.rs +++ b/efi-bin/src/main.rs @@ -1,6 +1,8 @@ #![no_main] #![no_std] +extern crate alloc; + use core::time::Duration; use uefi::prelude::*; @@ -17,22 +19,26 @@ const GIF_SIZE: usize = include_bytes!("bad-apple.gif").len(); #[entry] fn main(_image_handle: Handle, mut boot_system_table: SystemTable) -> Status { - uefi::helpers::init(&mut boot_system_table).unwrap(); + uefi::helpers::init(&mut boot_system_table).expect("helper init"); + + log::info!("hello!"); // Disable the watchdog timer boot_system_table .boot_services() .set_watchdog_timer(0, 0x10000, None) - .unwrap(); + .expect("disable watchdog timer"); let boot_services = boot_system_table.boot_services(); + boot_services.stall(1_000_000); + let img_handle = boot_services.image_handle(); let loaded_image = boot_services .open_protocol_exclusive::(img_handle) - .unwrap(); + .expect("loaded image"); - let device_handle = loaded_image.device().unwrap(); + let device_handle = loaded_image.device().expect("device handle"); let block_io = unsafe { boot_services.open_protocol::( @@ -44,39 +50,56 @@ fn main(_image_handle: Handle, mut boot_system_table: SystemTable) -> Stat OpenProtocolAttributes::GetProtocol, ) } - .unwrap(); + .expect("block io"); let mut gif_buf = [0u8; GIF_SIZE]; block_io .read_blocks(block_io.media().media_id(), Lba::from(0u64), &mut gif_buf) - .unwrap(); + .expect("read blocks"); - let gif = tinygif::Gif::::from_slice(&gif_buf).unwrap(); + let gif = tinygif::Gif::::from_slice(&gif_buf).expect("gif from slice"); // Get gop let gop_handle = boot_services .get_handle_for_protocol::() - .unwrap(); + .expect("graphics output handle"); let mut gop = boot_services .open_protocol_exclusive::(gop_handle) - .unwrap(); + .expect("graphics output open"); + use alloc::vec::Vec; // Create UefiDisplay + for mode in gop.modes(&boot_services).collect::>() { + let (w, h) = mode.info().resolution(); + if w >= gif.width() as usize && h >= gif.height() as usize { + match mode.info().pixel_format() { + uefi::proto::console::gop::PixelFormat::Rgb + | uefi::proto::console::gop::PixelFormat::Bgr => { + if gop.set_mode(&mode).is_ok() { + break; + } + } + uefi::proto::console::gop::PixelFormat::Bitmask + | uefi::proto::console::gop::PixelFormat::BltOnly => {} + } + } + } + let mode = gop.current_mode_info(); let (res_w, res_h) = mode.resolution(); let mut display = UefiDisplay::new(gop.frame_buffer(), mode); // Tint the entire screen cyan - display.fill_entire(Rgb888::BLACK).unwrap(); + display.fill_entire(Rgb888::BLACK).expect("fill black"); let timer_handle = boot_services .get_handle_for_protocol::() - .unwrap(); + .expect("timestamp handle"); let timer = boot_services .open_protocol_exclusive::(timer_handle) - .unwrap(); + .expect("timestamp open"); - let timer_hz = timer.get_properties().unwrap().frequency as f64; + let timer_hz = timer.get_properties().expect("timer props").frequency as f64; let start_timestamp = timer.get_timestamp(); let mut gif_elapsed = Duration::new(0, 0); @@ -90,7 +113,9 @@ fn main(_image_handle: Handle, mut boot_system_table: SystemTable) -> Stat (res_h as i32 / gif.height() as i32) / 2, ); for frame in gif.frames() { - frame.draw(&mut display.translated(translation)).unwrap(); + frame + .draw(&mut display.translated(translation)) + .expect("frame draw"); gif_elapsed += Duration::from_millis(frame.delay_centis as u64 * 10); // skip flush to catch up diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 149ee61..94c7f10 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] fatfs.workspace = true +mbrs.workspace = true diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 34f4f57..8ae0535 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -9,16 +9,43 @@ use fatfs::{ FileSystem, FormatVolumeOptions, FsOptions, IoBase, IoError, Read as FatRead, Seek as FatSeek, Write as FatWrite, }; +use mbrs::{Mbr, PartInfo, PartType}; + +#[derive(Copy, Clone)] +enum Cpu { + X86_64, + AArch64, +} +impl std::fmt::Display for Cpu { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Cpu::X86_64 => "x86_64", + Cpu::AArch64 => "aarch64", + }) + } +} fn main() { let mut args: VecDeque<_> = std::env::args().skip(1).collect(); match args.pop_front() { - Some(cmd) => match cmd.as_str() { - "build" => do_build(args), - "run" => do_run(args), - x => eprintln!("unknown command {:?}", x), - }, + Some(cmd) => { + let cpu = match args + .pop_front() + .unwrap_or(std::env::consts::ARCH.to_string()) + .as_str() + { + "aarch64" | "arm64" => Cpu::AArch64, + "x86_64" | "x64" => Cpu::X86_64, + x => unimplemented!("target cpu {:?}", x), + }; + + match cmd.as_str() { + "build" => do_build(cpu, args), + "run" => do_run(cpu, args), + x => eprintln!("unknown command {:?}", x), + } + } None => eprintln!("usage: cargo xtask [build|run] [x86_64|aarch64]"), } } @@ -73,14 +100,15 @@ impl FatWrite for MyStdIoWrapper { } } -fn do_build(args: VecDeque) { - let build_success = Command::new("cargo") +fn do_build(cpu: Cpu, _args: VecDeque) { + let build = "debug"; + let build_success = Command::new(env!("CARGO")) .arg("build") .arg("--bin") .arg("efigife") - .arg("--release") + //.arg("--release") .arg("--target") - .arg("aarch64-unknown-uefi") // TODO: target according to arg + .arg(format!("{}-unknown-uefi", cpu)) .stdin(Stdio::inherit()) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) @@ -90,11 +118,12 @@ fn do_build(args: VecDeque) { .success(); assert!(build_success); - let efi_bin_path = concat!( + let efi_bin_path = format!( + "{}/../target/{}-unknown-uefi/{}/efigife.efi", env!("CARGO_MANIFEST_DIR"), - "/../target/aarch64-unknown-uefi/release/efigife.efi" + cpu, + build ); - eprintln!("{}", efi_bin_path); let mut efi_bin_buf = Vec::new(); let mut efi_bin_file = File::open(efi_bin_path).unwrap(); let efi_bin_size = efi_bin_file.read_to_end(&mut efi_bin_buf); @@ -110,7 +139,11 @@ fn do_build(args: VecDeque) { ) .unwrap(); let fat_fs = FileSystem::new(fat_disk, FsOptions::new()).unwrap(); - let boot_efi_name = "BOOTAA64.EFI"; // TODO: parameterize on arg + let boot_efi_name = match cpu { + Cpu::AArch64 => "BOOTAA64.EFI", + Cpu::X86_64 => "BOOTX64.EFI", + }; + let mut boot_efi = fat_fs .root_dir() .create_dir("EFI") @@ -130,49 +163,82 @@ fn do_build(args: VecDeque) { let mut buf = Vec::new(); let src_gif_size = src_gif_file.read_to_end(&mut buf).unwrap(); drop(src_gif_file); + let fat_start_sector = (src_gif_size + 511) / 512; + let fat_sector_count = (fat_disk_buf.len() + 511) / 512; buf.extend(std::iter::repeat(0).take(fat_start_sector * 512 - buf.len())); buf.extend(fat_disk_buf); + let mut mbr = Mbr::default(); + mbr.partition_table.entries[0] = Some( + PartInfo::try_from_lba( + true, + fat_start_sector as u32, + fat_sector_count as u32, + PartType::Efi, + ) + .unwrap(), + ); + let fake_mbr_buf = <[u8; 512]>::try_from(&mbr).unwrap(); + buf[0x1be..0x200].copy_from_slice(&fake_mbr_buf[0x1be..0x200]); + let out_img_path = format!( "{}/../target/bad-apple.{}-efi.raw.gif", env!("CARGO_MANIFEST_DIR"), - "aarch64" + cpu ); let mut out_img_file = File::create(out_img_path).unwrap(); out_img_file.write_all(&buf).unwrap(); } -fn do_run(args: VecDeque) { - do_build(args.clone()); +fn do_run(cpu: Cpu, args: VecDeque) { + do_build(cpu, args.clone()); - let mut cmd = match args[0].as_str() { - "aarch64" | "arm64" => { - let mut cmd = Command::new("qemu-system-aarch64"); - cmd.arg("-drive") - .arg("if=none,id=code,format=raw,file=/usr/share/qemu/edk2-aarch64-code.fd,readonly=on") - .arg("-M") - .arg("virt,pflash0=code,accel=kvm,gic-version=3") - .arg("-cpu") - .arg("host") - .arg("-m") - .arg("512") - .arg("-device") - .arg("virtio-gpu-pci"); - cmd + let mut cmd = Command::new(format!("qemu-system-{}", cpu)); + let kvm = cpu.to_string() == std::env::consts::ARCH; + match cpu { + Cpu::AArch64 => { + if kvm { + cmd.arg("-M") + .arg("virt,pflash0=code,accel=kvm,gic-version=3") + .arg("-cpu") + .arg("host"); + } else { + cmd.arg("-M").arg("virt,pflash0=code,gic-version=3"); + } + cmd.arg("-drive").arg( + "if=none,id=code,format=raw,file=/usr/share/qemu/edk2-aarch64-code.fd,readonly=on", + ); } - "x86_64" | "x64" => { - //let mut cmd = Command::new("qemu-system-x86_64"); - todo!(); + Cpu::X86_64 => { + if kvm { + cmd.arg("-M") + .arg("q35,smm=on,accel=kvm") + .arg("-cpu") + .arg("host"); + } else { + cmd.arg("-M").arg("q35,smm=on"); + } + cmd.arg("-global") + .arg("ICH9-LPC.disable_s3=1") + .arg("-drive") + .arg( + "if=pflash,format=raw,unit=0,file=/usr/share/edk2-ovmf/OVMF_CODE.fd,readonly=on", + ); } - x => unimplemented!("target cpu {:?}", x), - }; + } + cmd.arg("-m") + .arg("512") + .arg("-device") + .arg("virtio-gpu-pci") + .args(&args); let out_img_path = format!( "{}/../target/bad-apple.{}-efi.raw.gif", env!("CARGO_MANIFEST_DIR"), - "aarch64" + cpu ); - cmd.arg(out_img_path) + cmd.arg("-drive") + .arg(format!("format=raw,file={}", out_img_path)) .stdin(Stdio::inherit()) .stdout(Stdio::inherit()) .stderr(Stdio::inherit())