#![no_main] #![no_std] extern crate alloc; use aligned_vec::{AVec, ConstAlign}; use alloc::borrow::ToOwned; use alloc::vec::Vec; use core::time::Duration; use uefi::proto::device_path::build::media::HardDrive; use uefi::proto::device_path::build::DevicePathBuilder; use uefi::proto::device_path::media::{PartitionFormat, PartitionSignature}; use uefi::{prelude::*, println, CStr16}; use uefi::proto::console::gop::GraphicsOutput; use uefi::proto::device_path::{DeviceType, LoadedImageDevicePath}; use uefi::proto::loaded_image::LoadedImage; use uefi::proto::media::block::{BlockIO, BlockIOMedia, Lba}; use uefi::proto::media::disk::DiskIo; use uefi::proto::media::file::{File, FileSystemVolumeLabel}; use uefi::proto::media::fs::SimpleFileSystem; use uefi::proto::media::partition::PartitionInfo; use uefi::proto::misc::Timestamp; use uefi::table::boot::{OpenProtocolAttributes, OpenProtocolParams}; use uefi_graphics2::embedded_graphics::{pixelcolor::Rgb888, prelude::*}; use uefi_graphics2::UefiDisplay; const GIF_SIZE: usize = ((include_bytes!("bad-apple.gif").len() + 511) / 512) * 512; const VOLUME_LABEL: &CStr16 = cstr16!("EFIGIFEDEMO"); #[entry] fn main(_image_handle: Handle, mut boot_system_table: SystemTable) -> Status { uefi::helpers::init(&mut boot_system_table).expect("helper init"); // Disable the watchdog timer boot_system_table .boot_services() .set_watchdog_timer(0, 0x10000, None) .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) .expect("loaded image"); let loaded_image_dev_path = boot_services .open_protocol_exclusive::(img_handle) .expect("loaded image dev path"); println!("{:?}", loaded_image.file_path()); for dev_path_inst in loaded_image_dev_path.instance_iter() { println!("inst: {:?}", dev_path_inst); } let mut dev_path_buf = Vec::new(); let mut dev_path_builder = DevicePathBuilder::with_vec(&mut dev_path_buf); for dev_path_node in loaded_image_dev_path.node_iter() { println!("node: {:?}", dev_path_node); println!( "{}", dev_path_node .to_string( &boot_services, uefi::proto::device_path::text::DisplayOnly(false), uefi::proto::device_path::text::AllowShortcuts(false), ) .unwrap() ); if dev_path_node.device_type() == DeviceType::MEDIA { break; } else { dev_path_builder = dev_path_builder .push(&dev_path_node) .expect("push existing node"); } } let harder_drive = HardDrive { partition_number: 0, partition_start: 0, partition_size: GIF_SIZE as u64 / 512, partition_signature: PartitionSignature::None, partition_format: PartitionFormat::MBR, }; dev_path_builder = dev_path_builder .push(&harder_drive) .expect("push hard drive"); let mut dev_path = dev_path_builder.finalize().expect("finalize dev path"); let device_handle = boot_services .locate_device_path::(&mut dev_path) .expect("locate dev path"); //let mut device_handle = loaded_image.device().expect("device handle"); let disk_io = boot_services .open_protocol_exclusive::(device_handle) .expect("disk io"); /* let handles = boot_services .find_handles::() .expect("sfs handle"); for handle in handles { let mut sfs = boot_services .open_protocol_exclusive::(handle) .expect("sfs proto"); let mut root_dir = sfs.open_volume().expect("open volume"); let vol_info = root_dir .get_boxed_info::() .expect("root dir label"); if vol_info.volume_label() == VOLUME_LABEL { assert_eq!(device_handle, handle); } } */ // TODO boot_services.locate_device_path() // FIXME we currently have the fat partition, need the parent disk let block_io = unsafe { boot_services.open_protocol::( OpenProtocolParams { handle: device_handle, agent: img_handle, controller: None, }, OpenProtocolAttributes::GetProtocol, ) } .expect("block io"); let mut gif_buf: AVec> = AVec::with_capacity(512, GIF_SIZE); for _ in 0..GIF_SIZE { //for _ in 0..512 { gif_buf.push(0u8); } disk_io .read_disk(block_io.media().media_id(), 0, &mut gif_buf) .expect("read disk"); /* block_io .read_blocks(block_io.media().media_id(), Lba::from(0u64), &mut gif_buf) .expect("read blocks"); */ let gif = tinygif::Gif::::from_slice(&gif_buf).expect("gif from slice"); // Get gop let gop_handle = boot_services .get_handle_for_protocol::() .expect("graphics output handle"); let mut gop = boot_services .open_protocol_exclusive::(gop_handle) .expect("graphics output open"); // 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).expect("fill black"); let timer_handle = boot_services .get_handle_for_protocol::() .expect("timestamp handle"); let timer = boot_services .open_protocol_exclusive::(timer_handle) .expect("timestamp open"); 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); let get_real_elapsed = || Duration::from_secs_f64((timer.get_timestamp() - start_timestamp) as f64 / timer_hz); let mut skipped_flushes = 0; let translation = Point::new( (res_w as i32 - gif.width() as i32) / 2, (res_h as i32 / gif.height() as i32) / 2, ); for frame in gif.frames() { 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 if gif_elapsed > get_real_elapsed() { display.flush(); if let Some(time_diff) = get_real_elapsed().checked_sub(gif_elapsed) { boot_services.stall(time_diff.as_micros() as usize); } } else { skipped_flushes += 1; if skipped_flushes == 5 { // don't skip more than 5 frames at a time // so you can actually still see anything skipped_flushes = 0; display.flush(); } } } Status::SUCCESS } /* // uefi-rs book example for reference: #![no_main] #![no_std] extern crate alloc; use alloc::vec; use alloc::vec::Vec; use uefi::prelude::*; use uefi::proto::console::gop::{BltOp, BltPixel, BltRegion, GraphicsOutput}; use uefi::proto::loaded_image::LoadedImage; use uefi::proto::media::block::{BlockIO, Lba}; use uefi::table::boot::{OpenProtocolAttributes, OpenProtocolParams}; use uefi::Result; const GIF_SIZE: usize = include_bytes!("bad-apple.gif").len(); #[derive(Clone, Copy)] struct Point { x: f32, y: f32, } impl Point { fn new(x: f32, y: f32) -> Self { Self { x, y } } } struct Buffer { width: usize, height: usize, pixels: Vec, } impl Buffer { /// Create a new `Buffer`. fn new(width: usize, height: usize) -> Self { Buffer { width, height, pixels: vec![BltPixel::new(0, 0, 0); width * height], } } /// Get a single pixel. fn pixel(&mut self, x: usize, y: usize) -> Option<&mut BltPixel> { self.pixels.get_mut(y * self.width + x) } /// Blit the buffer to the framebuffer. fn blit(&self, gop: &mut GraphicsOutput) -> Result { gop.blt(BltOp::BufferToVideo { buffer: &self.pixels, src: BltRegion::Full, dest: (0, 0), dims: (self.width, self.height), }) } } fn draw_sierpinski(bt: &BootServices) -> Result { // Open graphics output protocol. let gop_handle = bt .get_handle_for_protocol::() .expect("gop handle"); let mut gop = bt .open_protocol_exclusive::(gop_handle) .expect("gop proto"); // Create a buffer to draw into. let (width, height) = gop.current_mode_info().resolution(); let mut buffer = Buffer::new(width, height); // Initialize the buffer with a simple gradient background. for y in 0..height { let r = ((y as f32) / ((height - 1) as f32)) * 255.0; for x in 0..width { let g = ((x as f32) / ((width - 1) as f32)) * 255.0; let pixel = buffer.pixel(x, y).expect("gradient pixel"); pixel.red = r as u8; pixel.green = g as u8; pixel.blue = 255; } } let size = Point::new(width as f32, height as f32); // Define the vertices of a big triangle. let border = 20.0; let triangle = [ Point::new(size.x / 2.0, border), Point::new(border, size.y - border), Point::new(size.x - border, size.y - border), ]; // `p` is the point to draw. Start at the center of the triangle. let mut p = Point::new(size.x / 2.0, size.y / 2.0); // Loop forever, drawing the frame after each new point is changed. loop { // Choose one of the triangle's vertices at random. let v = triangle[1]; // chosen by fair dice roll. // Move `p` halfway to the chosen vertex. p.x = (p.x + v.x) * 0.5; p.y = (p.y + v.y) * 0.5; // Set `p` to black. let pixel = buffer .pixel(p.x as usize, p.y as usize) .expect("black pixel"); pixel.red = 0; pixel.green = 100; pixel.blue = 0; // Draw the buffer to the screen. buffer.blit(&mut gop).expect("blit"); } } #[entry] fn main(_handle: Handle, mut system_table: SystemTable) -> Status { uefi::helpers::init(&mut system_table).expect("table init"); let boot_services = system_table.boot_services(); // Disable the watchdog timer boot_services .set_watchdog_timer(0, 0x10000, None) .expect("disable watchdog timer"); let img_handle = boot_services.image_handle(); let loaded_image = boot_services .open_protocol_exclusive::(img_handle) .expect("loaded image"); let device_handle = loaded_image.device().expect("device handle"); let block_io = unsafe { boot_services.open_protocol::( OpenProtocolParams { handle: device_handle, agent: img_handle, controller: None, }, OpenProtocolAttributes::GetProtocol, ) } .expect("block io"); let mut gif_buf = Vec::new(); gif_buf.resize(GIF_SIZE, 0u8); block_io .read_blocks(block_io.media().media_id(), Lba::from(0u64), &mut gif_buf) .expect("read blocks"); draw_sierpinski(boot_services).expect("sierpinski"); Status::SUCCESS } */