diff --git a/.gitignore b/.gitignore index ea8c4bf..aff7864 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,8 @@ /target +/BOOT/EFI/BOOT/BOOTX64.EFI +/BOOT/EFI/BOOT +/BOOT/EFI +/BOOT +/*.fd + + diff --git a/BOOT/EFI/BOOT/BOOTX64.EFI b/BOOT/EFI/BOOT/BOOTX64.EFI deleted file mode 100755 index 7b804ac..0000000 Binary files a/BOOT/EFI/BOOT/BOOTX64.EFI and /dev/null differ diff --git a/Makefile b/Makefile index d463ed4..8c89522 100755 --- a/Makefile +++ b/Makefile @@ -13,23 +13,26 @@ qemu_drive := -drive format=raw,file=fat:rw:$(BOOT_DIR) target_debug := target/$(TARGET)/debug/$(PROJECT).efi target_release := target/$(TARGET)/release/$(PROJECT).efi -.PHONY: all release debug clean run-debug run +.PHONY: all release debug clean launch-debug launch configure all: $(target_debug) $(target_release) debug: $(target_debug) release: $(target_release) +configure: + mkdir $(BOOT_DIR) + clean: rm -rv $(BOOT_DIR) @RUST_TARGET_PATH=$(shell pwd) cargo clean --target $(TARGET) -run-debug: $(target_debug) +launch-debug: $(target_debug) @RUST_TARGET_PATH=$(shell pwd) cargo +nightly build -Z build-std --target $(TARGET) --verbose mkdir -p $(BOOT_DIR)/EFI/BOOT/ cp -v $(target_debug) $(BOOT_DIR)/EFI/BOOT/BOOTX64.EFI @qemu-system-$(ARCH) $(qemu_args) $(qemu_efi) $(qemu_efi_vars) $(qemu_drive) -run: $(target_release) +launch: $(target_release) @RUST_TARGET_PATH=$(shell pwd) cargo +nightly build -Z build-std --target $(TARGET) --release mkdir -p $(BOOT_DIR)/EFI/BOOT/ cp -v $(target_release) $(BOOT_DIR)/EFI/BOOT/BOOTX64.EFI diff --git a/src/main.rs b/src/main.rs index 87a8dbb..adfb057 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,6 +28,10 @@ use uefi::{ AllocateType, BootServices, MemoryType, OpenProtocolAttributes, OpenProtocolParams, }, }; +use uefi_fb::UefiFb; + +mod uefi_fb; +mod sync; #[uefi_macros::entry] pub fn efi_main(_image: Handle, mut st: SystemTable) -> Status { @@ -65,95 +69,16 @@ pub fn efi_main(_image: Handle, mut st: SystemTable) -> Status { } }; - + let mut fb = uefi_fb::UefiFb::new(); + // None sets a sane default of 1024x768 + fb.set_graphics_mode(None); + fb.fb_blt_fill(BltPixel::new(100, 149, 237), (0, 0), (1024, 768)); - let handle = bt.get_handle_for_protocol::() - .expect("missing GraphicsOutput protocol"); - - - let mut gop = unsafe { - let mut gop_proto = st.boot_services().open_protocol::( - OpenProtocolParams { - handle, - agent: st.boot_services().image_handle(), - controller: None, - }, - // For this test, don't open in exclusive mode. That - // would break the connection between stdout and the - // video console. - OpenProtocolAttributes::GetProtocol, - ) - .expect("failed to open Graphics Output Protocol"); - - gop_proto - }; - - set_graphics_mode(&mut gop); - fill_color(&mut gop); - draw_fb(&mut gop); - info!("GOP Framebuffer test complete"); - - // Return success status - Status::SUCCESS -} - -// Set a larger graphics mode. -fn set_graphics_mode(gop: &mut GraphicsOutput) { - // We know for sure QEMU has a 1024x768 mode. - let mode = gop - .modes() - .find(|mode| { - let info = mode.info(); - info.resolution() == (1024, 768) - }) - .unwrap(); - - gop.set_mode(&mode).expect("Failed to set graphics mode"); -} - -// Fill the screen with color. -fn fill_color(gop: &mut GraphicsOutput) { - let op = BltOp::VideoFill { - // Cornflower blue. - color: BltPixel::new(100, 149, 237), - dest: (0, 0), - dims: (1024, 768), - }; - - gop.blt(op).expect("Failed to fill screen with color"); -} - -// Draw directly to the frame buffer. -fn draw_fb(gop: &mut GraphicsOutput) { - // The `virtio-gpu-pci` graphics device we use on aarch64 doesn't - // support `PixelFormat::BltOnly`. - if cfg!(target_arch = "aarch64") { - return; - } - - let mi = gop.current_mode_info(); + let mi = fb.current_mode_info(); let stride = mi.stride(); let (width, height) = mi.resolution(); - let mut fb = gop.frame_buffer(); - - type PixelWriter = unsafe fn(&mut FrameBuffer, usize, [u8; 3]); - unsafe fn write_pixel_rgb(fb: &mut FrameBuffer, pixel_base: usize, rgb: [u8; 3]) { - fb.write_value(pixel_base, rgb); - } - unsafe fn write_pixel_bgr(fb: &mut FrameBuffer, pixel_base: usize, rgb: [u8; 3]) { - fb.write_value(pixel_base, [rgb[2], rgb[1], rgb[0]]); - } - let write_pixel: PixelWriter = match mi.pixel_format() { - PixelFormat::Rgb => write_pixel_rgb, - PixelFormat::Bgr => write_pixel_bgr, - _ => { - info!("This pixel format is not supported by the drawing demo"); - return; - } - }; - let mut fill_rectangle = |(x1, y1), (x2, y2), color| { assert!((x1 < width) && (x2 < width), "Bad X coordinate"); assert!((y1 < height) && (y2 < height), "Bad Y coordinate"); @@ -162,7 +87,7 @@ fn draw_fb(gop: &mut GraphicsOutput) { unsafe { let pixel_index = (row * stride) + column; let pixel_base = 4 * pixel_index; - write_pixel(&mut fb, pixel_base, color); + fb.draw_fb(pixel_base, color); } } } @@ -170,4 +95,7 @@ fn draw_fb(gop: &mut GraphicsOutput) { fill_rectangle((50, 30), (150, 600), [250, 128, 64]); fill_rectangle((400, 120), (750, 450), [16, 128, 255]); -} + + info!("GOP Framebuffer test complete"); + Status::SUCCESS +} \ No newline at end of file diff --git a/src/sync/mod.rs b/src/sync/mod.rs new file mode 100644 index 0000000..3f97990 --- /dev/null +++ b/src/sync/mod.rs @@ -0,0 +1,57 @@ +use core::arch::asm; +use core::sync::atomic::{AtomicBool, Ordering}; +use core::cell::UnsafeCell; + +pub struct Spinlock { + locked: AtomicBool, + data: UnsafeCell, +} + +unsafe impl Sync for Spinlock {} + +impl Spinlock { + pub const fn new(data: T) -> Self { + Self { + locked: AtomicBool::new(false), + data: UnsafeCell::new(data), + } + } + + pub fn lock(&self) -> SpinlockGuard { + while let Err(_) = self.locked.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) { + while self.locked.load(Ordering::Relaxed) { + // Spin until the lock is released + unsafe { asm!("pause") }; + } + } + + SpinlockGuard { + spinlock: &self, + } + } + +} + +pub struct SpinlockGuard<'a, T> { + spinlock: &'a Spinlock, +} + +impl<'a, T> core::ops::Deref for SpinlockGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.spinlock.data.get() } + } +} + +impl<'a, T> core::ops::DerefMut for SpinlockGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.spinlock.data.get() } + } +} + +impl<'a, T> Drop for SpinlockGuard<'a, T> { + fn drop(&mut self) { + self.spinlock.locked.store(false, Ordering::Release); + } +} diff --git a/src/uefi_fb/mod.rs b/src/uefi_fb/mod.rs new file mode 100644 index 0000000..e7e0950 --- /dev/null +++ b/src/uefi_fb/mod.rs @@ -0,0 +1,114 @@ +use core::ffi::c_void; +use core::ptr; +use uefi::Handle; +use uefi::proto::ProtocolPointer; +use uefi::table::boot::ScopedProtocol; +use uefi::{prelude::*, Identify}; +use uefi::{ + proto::console::gop::{BltOp, BltPixel, FrameBuffer, GraphicsOutput, PixelFormat}, + table::boot::{ + AllocateType, BootServices, MemoryType, OpenProtocolAttributes, OpenProtocolParams, + }, +}; +use alloc::sync::Arc; +use crate::sync; +use crate::sync::{Spinlock, SpinlockGuard}; + +pub struct UefiFb<'a> { + gop: ScopedProtocol<'a, GraphicsOutput<'a>> +} + +impl<'a> core::ops::Deref for UefiFb<'a> { + type Target = ScopedProtocol<'a, GraphicsOutput<'a>>; + + fn deref(&self) -> &Self::Target { + &self.gop + } +} + +impl<'a> UefiFb<'a> { + + pub fn new() -> UefiFb<'a> { + let st_ref = unsafe { uefi_services::system_table().as_ref() }; + let bt = st_ref.boot_services(); + + let handle = bt.get_handle_for_protocol::() + .expect("missing GraphicsOutput protocol"); + + + let mut gop = unsafe { + let mut gop_proto = bt.open_protocol::( + OpenProtocolParams { + handle, + agent: bt.image_handle(), + controller: None, + }, + // For this test, don't open in exclusive mode. That + // would break the connection between stdout and the + // video console. + OpenProtocolAttributes::GetProtocol, + ) + .expect("failed to open Graphics Output Protocol"); + + gop_proto + }; + + UefiFb { gop } + + } + + // Set a larger graphics mode. + pub fn set_graphics_mode(&mut self, mode_tuple: Option<(i16,i16,i16)>) { + if mode_tuple == None { + // We know for sure QEMU has a 1024x768 mode. + let mode = self.gop + .modes() + .find(|mode| { + let info = mode.info(); + info.resolution() == (1024, 768) + }) + .unwrap(); + + self.gop.set_mode(&mode).expect("Failed to set graphics mode"); + } else { + unimplemented!() + } + } + + // Fill the screen with color. + pub fn fb_blt_fill(&mut self, color: BltPixel, dest: (usize, usize), dims: (usize, usize)) { + let op = BltOp::VideoFill { + color, + dest, + dims + }; + + self.gop.blt(op).expect("Failed to fill screen with color"); + } + + // Draw directly to the frame buffer. + pub fn draw_fb(&mut self, index: usize, rgb: [u8; 3]) { + let mi = self.gop.current_mode_info(); + let mut fb = self.gop.frame_buffer(); + + type PixelWriter = unsafe fn(&mut FrameBuffer, usize, [u8; 3]); + + unsafe fn write_pixel_rgb(fb: &mut FrameBuffer, index: usize, rgb: [u8; 3]) { + fb.write_value(index, rgb); + } + unsafe fn write_pixel_bgr(fb: &mut FrameBuffer, index: usize, rgb: [u8; 3]) { + fb.write_value(index, [rgb[2], rgb[1], rgb[0]]); + } + + let write_pixel: PixelWriter = match mi.pixel_format() { + PixelFormat::Rgb => write_pixel_rgb, + PixelFormat::Bgr => write_pixel_bgr, + _ => { + info!("This pixel format is not supported by the drawing demo"); + return; + } + }; + + unsafe { write_pixel(&mut fb, index, rgb) }; + } +} \ No newline at end of file