-
-
Notifications
You must be signed in to change notification settings - Fork 177
Closed
Description
When allocating memory for a UEFI application using BootServices with a custom memory type, the ExitBootServices call later freezes on a real machine, but it works fine in QEMU.
Everything works as expected on the real machine as well, if MemoryType::LOADER_DATA
is used.
System Information:
Tested on: Lenovo V15 G3 IAP
CPU: 12th Gen Intel(R) Core(TM) i5-1235U
Rust Toolchain: nightly
Dependencies Used:
log = "0.4.22"
uefi = { version = "0.31.0", features = ["logger", "alloc", "global_allocator", "panic_handler"] }
Source Code:
config.toml
[unstable]
build-std = ["core", "compiler_builtins", "alloc"]
build-std-features = ["compiler-builtins-mem"]
[build]
target = "x86_64-unknown-uefi"
main.rs
#![no_std]
#![no_main]
extern crate alloc;
use alloc::format;
use alloc::string::String;
use log::info;
use uefi::{
entry,
Handle,
Status, table::{Boot, SystemTable},
};
use uefi::prelude::BootServices;
use uefi::proto::console::gop::{GraphicsOutput, PixelFormat};
use uefi::table::boot::{AllocateType, MemoryType};
use crate::framebuffer::{Color, FrameBufferMetadata, RawFrameBuffer};
mod framebuffer;
const MY_MEMORY_TYPE: MemoryType = MemoryType::custom(0x80000001);
#[entry]
fn main(_image_handle: Handle, system_table: SystemTable<Boot>) -> Status {
uefi::helpers::init().unwrap();
info!("Hello, uefi world!");
// some boilerplate to get a working framebuffer using Graphics Output Protocol
let fb_meta = initialize_framebuffer(system_table.boot_services()).unwrap();
let fb = RawFrameBuffer::from(fb_meta);
// color white indicates framebuffer initialization was successful
fb.fill(Color::white());
// allocate some memory using the custom memory type
let x = system_table.boot_services().allocate_pages(AllocateType::AnyPages, MY_MEMORY_TYPE, 5).unwrap();
// "use" the newly allocated memory
let _y = unsafe { *(x as *const u8) };
// color blue indicates allocation was successful
fb.fill(Color::blue());
// exit boot services
let (_, _) = unsafe { system_table.exit_boot_services(MemoryType::LOADER_DATA) };
// color green indicates exit was successful
fb.fill(Color::green());
loop {}
}
/// Initialize framebuffer (GOP)
fn initialize_framebuffer(
boot_services: &BootServices,
) -> Result<FrameBufferMetadata, String> {
let gop_handle = boot_services
.get_handle_for_protocol::<GraphicsOutput>()
.map_err(|error| format!("Could not get handle for GOP: {error}."))?;
let mut gop = boot_services
.open_protocol_exclusive::<GraphicsOutput>(gop_handle)
.map_err(|error| format!("Could not open GOP: {error}."))?;
let mut raw_frame_buffer = gop.frame_buffer();
let base = raw_frame_buffer.as_mut_ptr() as u64;
let size = raw_frame_buffer.size();
let info = gop.current_mode_info();
let is_rgb = match info.pixel_format() {
PixelFormat::Rgb => Ok(true),
PixelFormat::Bgr => Ok(false),
PixelFormat::Bitmask | PixelFormat::BltOnly => {
Err("Not supported")
}
}?;
let (width, height) = info.resolution();
let stride = info.stride();
Ok(FrameBufferMetadata {
base,
size,
width,
height,
stride,
is_rgb,
})
}
framebuffer.rs
use core::{
fmt,
fmt::{Debug, Formatter},
};
use core::ptr::write_volatile;
pub(crate) const BPP: usize = 4;
#[derive(Copy, Clone)]
pub(crate) struct FrameBufferMetadata {
pub(crate) base: u64,
pub(crate) size: usize,
pub(crate) width: usize,
pub(crate) height: usize,
pub(crate) stride: usize, // pixels per scanline
pub(crate) is_rgb: bool,
}
impl Debug for FrameBufferMetadata {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!(
"FrameBufferMetadata {{\n\tbase: {:#x},\n\tsize: {:#x},\n\twidth: {},\n\theight: {},\n\tstride: {},\n}}",
self.base, self.size, self.width, self.height, self.stride
))
}
}
#[derive(Copy, Clone, Debug, Default)]
pub(crate) struct Color {
pub(crate) red: u8,
pub(crate) green: u8,
pub(crate) blue: u8,
}
macro_rules! color {
($color:ident, $red:expr, $green:expr, $blue:expr) => {
impl Color {
pub(crate) const fn $color() -> Color {
Color {
red: $red,
green: $green,
blue: $blue,
}
}
}
};
}
color!(green, 0x00, 0xFF, 0x00);
color!(blue, 0x00, 0x00, 0xFF);
color!(white, 0xFF, 0xFF, 0xFF);
/// Directly accesses video memory in order to display graphics
#[derive(Clone, Debug)]
pub(crate) struct RawFrameBuffer {
pub(crate) meta_data: FrameBufferMetadata,
}
impl RawFrameBuffer {
/// Draws a pixel onto the screen at coordinates x,y and with the specified color. Returns, whether the action succeeds or the coordinates are invalid.
pub(crate) fn draw_pixel(
&self,
x: usize,
y: usize,
color: Color,
) -> Result<(), (usize, usize)> {
if !self.in_bounds(x, y) {
return Err((x, y));
}
let pitch = self.meta_data.stride * BPP;
unsafe {
let pixel = (self.meta_data.base as *mut u8).add(pitch * y + BPP * x);
if self.meta_data.is_rgb {
write_volatile(pixel, color.red);
write_volatile(pixel.add(1), color.green);
write_volatile(pixel.add(2), color.blue);
} else {
write_volatile(pixel, color.blue);
write_volatile(pixel.add(1), color.green);
write_volatile(pixel.add(2), color.red);
}
}
Ok(())
}
/// Fills entire display with certain color
pub(crate) fn fill(&self, color: Color) {
for x in 0..self.meta_data.width {
for y in 0..self.meta_data.height {
self.draw_pixel(x, y, color).unwrap();
}
}
}
}
impl RawFrameBuffer {
/// Whether a point is within the framebuffer vram
fn in_bounds(&self, x: usize, y: usize) -> bool {
x < self.meta_data.width && y < self.meta_data.height
}
}
impl From<FrameBufferMetadata> for RawFrameBuffer {
fn from(value: FrameBufferMetadata) -> Self {
Self { meta_data: value }
}
}
Note: I'm relatively new to the UEFI and OSDev community so there might be an issue with my code itself. Please feel free to correct me if the mistake is mine.
Metadata
Metadata
Assignees
Labels
No labels