Merge pull request #2 from Nivirx/next

merge UEFI-FB driver module
This commit was merged in pull request #2.
This commit is contained in:
2023-03-18 17:39:28 -07:00
committed by GitHub
6 changed files with 198 additions and 89 deletions

7
.gitignore vendored
View File

@@ -1 +1,8 @@
/target /target
/BOOT/EFI/BOOT/BOOTX64.EFI
/BOOT/EFI/BOOT
/BOOT/EFI
/BOOT
/*.fd

Binary file not shown.

View File

@@ -13,23 +13,26 @@ qemu_drive := -drive format=raw,file=fat:rw:$(BOOT_DIR)
target_debug := target/$(TARGET)/debug/$(PROJECT).efi target_debug := target/$(TARGET)/debug/$(PROJECT).efi
target_release := target/$(TARGET)/release/$(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) all: $(target_debug) $(target_release)
debug: $(target_debug) debug: $(target_debug)
release: $(target_release) release: $(target_release)
configure:
mkdir $(BOOT_DIR)
clean: clean:
rm -rv $(BOOT_DIR) rm -rv $(BOOT_DIR)
@RUST_TARGET_PATH=$(shell pwd) cargo clean --target $(TARGET) @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 @RUST_TARGET_PATH=$(shell pwd) cargo +nightly build -Z build-std --target $(TARGET) --verbose
mkdir -p $(BOOT_DIR)/EFI/BOOT/ mkdir -p $(BOOT_DIR)/EFI/BOOT/
cp -v $(target_debug) $(BOOT_DIR)/EFI/BOOT/BOOTX64.EFI cp -v $(target_debug) $(BOOT_DIR)/EFI/BOOT/BOOTX64.EFI
@qemu-system-$(ARCH) $(qemu_args) $(qemu_efi) $(qemu_efi_vars) $(qemu_drive) @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 @RUST_TARGET_PATH=$(shell pwd) cargo +nightly build -Z build-std --target $(TARGET) --release
mkdir -p $(BOOT_DIR)/EFI/BOOT/ mkdir -p $(BOOT_DIR)/EFI/BOOT/
cp -v $(target_release) $(BOOT_DIR)/EFI/BOOT/BOOTX64.EFI cp -v $(target_release) $(BOOT_DIR)/EFI/BOOT/BOOTX64.EFI

View File

@@ -28,6 +28,10 @@ use uefi::{
AllocateType, BootServices, MemoryType, OpenProtocolAttributes, OpenProtocolParams, AllocateType, BootServices, MemoryType, OpenProtocolAttributes, OpenProtocolParams,
}, },
}; };
use uefi_fb::UefiFb;
mod uefi_fb;
mod sync;
#[uefi_macros::entry] #[uefi_macros::entry]
pub fn efi_main(_image: Handle, mut st: SystemTable<Boot>) -> Status { pub fn efi_main(_image: Handle, mut st: SystemTable<Boot>) -> Status {
@@ -65,95 +69,16 @@ pub fn efi_main(_image: Handle, mut st: SystemTable<Boot>) -> 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::<GraphicsOutput>() let mi = fb.current_mode_info();
.expect("missing GraphicsOutput protocol");
let mut gop = unsafe {
let mut gop_proto = st.boot_services().open_protocol::<GraphicsOutput>(
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 stride = mi.stride(); let stride = mi.stride();
let (width, height) = mi.resolution(); 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| { let mut fill_rectangle = |(x1, y1), (x2, y2), color| {
assert!((x1 < width) && (x2 < width), "Bad X coordinate"); assert!((x1 < width) && (x2 < width), "Bad X coordinate");
assert!((y1 < height) && (y2 < height), "Bad Y coordinate"); assert!((y1 < height) && (y2 < height), "Bad Y coordinate");
@@ -162,7 +87,7 @@ fn draw_fb(gop: &mut GraphicsOutput) {
unsafe { unsafe {
let pixel_index = (row * stride) + column; let pixel_index = (row * stride) + column;
let pixel_base = 4 * pixel_index; 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((50, 30), (150, 600), [250, 128, 64]);
fill_rectangle((400, 120), (750, 450), [16, 128, 255]); fill_rectangle((400, 120), (750, 450), [16, 128, 255]);
}
info!("GOP Framebuffer test complete");
Status::SUCCESS
}

57
src/sync/mod.rs Normal file
View File

@@ -0,0 +1,57 @@
use core::arch::asm;
use core::sync::atomic::{AtomicBool, Ordering};
use core::cell::UnsafeCell;
pub struct Spinlock<T> {
locked: AtomicBool,
data: UnsafeCell<T>,
}
unsafe impl<T: Send> Sync for Spinlock<T> {}
impl<T> Spinlock<T> {
pub const fn new(data: T) -> Self {
Self {
locked: AtomicBool::new(false),
data: UnsafeCell::new(data),
}
}
pub fn lock(&self) -> SpinlockGuard<T> {
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<T>,
}
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);
}
}

114
src/uefi_fb/mod.rs Normal file
View File

@@ -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::<GraphicsOutput>()
.expect("missing GraphicsOutput protocol");
let mut gop = unsafe {
let mut gop_proto = bt.open_protocol::<GraphicsOutput>(
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) };
}
}