efigife/efi-bin/src/main.rs

393 lines
12 KiB
Rust
Raw Normal View History

2024-05-05 03:37:03 +00:00
#![no_main]
#![no_std]
2024-05-07 06:06:15 +00:00
extern crate alloc;
2024-05-07 08:26:11 +00:00
use aligned_vec::{AVec, ConstAlign};
2024-05-07 19:37:38 +00:00
use alloc::borrow::ToOwned;
2024-05-07 08:26:11 +00:00
use alloc::vec::Vec;
2024-05-05 03:37:03 +00:00
use core::time::Duration;
2024-05-07 19:37:38 +00:00
use uefi::proto::device_path::build::media::HardDrive;
use uefi::proto::device_path::build::DevicePathBuilder;
use uefi::proto::device_path::media::{PartitionFormat, PartitionSignature};
2024-05-05 03:37:03 +00:00
2024-05-07 19:37:38 +00:00
use uefi::{prelude::*, println, CStr16};
2024-05-07 08:26:11 +00:00
2024-05-05 03:37:03 +00:00
use uefi::proto::console::gop::GraphicsOutput;
2024-05-07 19:37:38 +00:00
use uefi::proto::device_path::{DeviceType, LoadedImageDevicePath};
2024-05-05 03:37:03 +00:00
use uefi::proto::loaded_image::LoadedImage;
2024-05-07 19:37:38 +00:00
use uefi::proto::media::block::{BlockIO, BlockIOMedia, Lba};
use uefi::proto::media::disk::DiskIo;
2024-05-07 08:26:11 +00:00
use uefi::proto::media::file::{File, FileSystemVolumeLabel};
use uefi::proto::media::fs::SimpleFileSystem;
2024-05-07 19:37:38 +00:00
use uefi::proto::media::partition::PartitionInfo;
2024-05-05 03:37:03 +00:00
use uefi::proto::misc::Timestamp;
use uefi::table::boot::{OpenProtocolAttributes, OpenProtocolParams};
use uefi_graphics2::embedded_graphics::{pixelcolor::Rgb888, prelude::*};
use uefi_graphics2::UefiDisplay;
2024-05-07 08:26:11 +00:00
const GIF_SIZE: usize = ((include_bytes!("bad-apple.gif").len() + 511) / 512) * 512;
const VOLUME_LABEL: &CStr16 = cstr16!("EFIGIFEDEMO");
2024-05-05 03:37:03 +00:00
#[entry]
fn main(_image_handle: Handle, mut boot_system_table: SystemTable<Boot>) -> Status {
2024-05-07 06:06:15 +00:00
uefi::helpers::init(&mut boot_system_table).expect("helper init");
2024-05-05 03:37:03 +00:00
// Disable the watchdog timer
boot_system_table
.boot_services()
.set_watchdog_timer(0, 0x10000, None)
2024-05-07 06:06:15 +00:00
.expect("disable watchdog timer");
2024-05-05 03:37:03 +00:00
let boot_services = boot_system_table.boot_services();
2024-05-07 06:06:15 +00:00
boot_services.stall(1_000_000);
2024-05-05 03:37:03 +00:00
let img_handle = boot_services.image_handle();
2024-05-07 19:37:38 +00:00
let loaded_image = boot_services
.open_protocol_exclusive::<LoadedImage>(img_handle)
.expect("loaded image");
2024-05-07 08:26:11 +00:00
let loaded_image_dev_path = boot_services
.open_protocol_exclusive::<LoadedImageDevicePath>(img_handle)
2024-05-07 19:37:38 +00:00
.expect("loaded image dev path");
2024-05-05 03:37:03 +00:00
2024-05-07 19:37:38 +00:00
println!("{:?}", loaded_image.file_path());
2024-05-07 08:26:11 +00:00
2024-05-07 19:37:38 +00:00
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::<DiskIo>(&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::<DiskIo>(device_handle)
.expect("disk io");
2024-05-07 08:26:11 +00:00
/*
let handles = boot_services
.find_handles::<uefi::proto::media::fs::SimpleFileSystem>()
.expect("sfs handle");
for handle in handles {
let mut sfs = boot_services
.open_protocol_exclusive::<SimpleFileSystem>(handle)
.expect("sfs proto");
let mut root_dir = sfs.open_volume().expect("open volume");
let vol_info = root_dir
.get_boxed_info::<FileSystemVolumeLabel>()
.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
2024-05-05 03:37:03 +00:00
let block_io = unsafe {
boot_services.open_protocol::<BlockIO>(
OpenProtocolParams {
handle: device_handle,
agent: img_handle,
controller: None,
},
OpenProtocolAttributes::GetProtocol,
)
}
2024-05-07 06:06:15 +00:00
.expect("block io");
2024-05-05 03:37:03 +00:00
2024-05-07 08:26:11 +00:00
let mut gif_buf: AVec<u8, ConstAlign<512>> = AVec::with_capacity(512, GIF_SIZE);
2024-05-07 19:37:38 +00:00
for _ in 0..GIF_SIZE {
//for _ in 0..512 {
2024-05-07 08:26:11 +00:00
gif_buf.push(0u8);
}
2024-05-07 19:37:38 +00:00
disk_io
.read_disk(block_io.media().media_id(), 0, &mut gif_buf)
.expect("read disk");
/*
2024-05-05 03:37:03 +00:00
block_io
.read_blocks(block_io.media().media_id(), Lba::from(0u64), &mut gif_buf)
2024-05-07 06:06:15 +00:00
.expect("read blocks");
2024-05-07 19:37:38 +00:00
*/
2024-05-07 08:26:11 +00:00
2024-05-07 06:06:15 +00:00
let gif = tinygif::Gif::<Rgb888>::from_slice(&gif_buf).expect("gif from slice");
2024-05-05 03:37:03 +00:00
// Get gop
let gop_handle = boot_services
.get_handle_for_protocol::<GraphicsOutput>()
2024-05-07 06:06:15 +00:00
.expect("graphics output handle");
2024-05-05 03:37:03 +00:00
let mut gop = boot_services
.open_protocol_exclusive::<GraphicsOutput>(gop_handle)
2024-05-07 06:06:15 +00:00
.expect("graphics output open");
2024-05-05 03:37:03 +00:00
// Create UefiDisplay
2024-05-07 06:06:15 +00:00
for mode in gop.modes(&boot_services).collect::<Vec<_>>() {
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 => {}
}
}
}
2024-05-05 03:37:03 +00:00
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
2024-05-07 06:06:15 +00:00
display.fill_entire(Rgb888::BLACK).expect("fill black");
2024-05-05 03:37:03 +00:00
let timer_handle = boot_services
.get_handle_for_protocol::<Timestamp>()
2024-05-07 06:06:15 +00:00
.expect("timestamp handle");
2024-05-05 03:37:03 +00:00
let timer = boot_services
.open_protocol_exclusive::<Timestamp>(timer_handle)
2024-05-07 06:06:15 +00:00
.expect("timestamp open");
2024-05-05 03:37:03 +00:00
2024-05-07 06:06:15 +00:00
let timer_hz = timer.get_properties().expect("timer props").frequency as f64;
2024-05-05 03:37:03 +00:00
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() {
2024-05-07 06:06:15 +00:00
frame
.draw(&mut display.translated(translation))
.expect("frame draw");
2024-05-05 03:37:03 +00:00
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
}
2024-05-07 08:26:11 +00:00
/*
// 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<BltPixel>,
}
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::<GraphicsOutput>()
.expect("gop handle");
let mut gop = bt
.open_protocol_exclusive::<GraphicsOutput>(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<Boot>) -> 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::<LoadedImage>(img_handle)
.expect("loaded image");
let device_handle = loaded_image.device().expect("device handle");
let block_io = unsafe {
boot_services.open_protocol::<BlockIO>(
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
}
*/