|
23 | 23 | //! In theory, a buffer with a width of 640 should have (640 * 4) bytes per row,
|
24 | 24 | //! but in practice there might be some extra padding used for efficiency.
|
25 | 25 |
|
| 26 | +use core::marker::PhantomData; |
| 27 | +use core::mem; |
26 | 28 | use core::ptr;
|
27 | 29 | use crate::{Completion, Result, Status};
|
28 | 30 |
|
@@ -249,29 +251,20 @@ impl GraphicsOutput {
|
249 | 251 | *self.mode.info
|
250 | 252 | }
|
251 | 253 |
|
252 |
| - /// Returns the base pointer and size (in bytes) of the framebuffer |
253 |
| - /// |
254 |
| - /// To use this pointer safely, a caller must... |
255 |
| - /// - Honor the pixel format specificed by the mode info |
256 |
| - /// - Keep pointer accesses in bound |
257 |
| - /// - Use volatile writes so that the compiler does not optimize out or |
258 |
| - /// aggressively reorder framebuffer accesses |
259 |
| - /// - Make sure that the pointer is not used beyond its validity limit |
260 |
| - /// |
261 |
| - /// Although the UEFI spec makes no clear statement about framebuffer |
262 |
| - /// pointer validity, it seems reasonable to expect the framebuffer pointer |
263 |
| - /// to be valid until the next mode change. In the future, a safer interface |
264 |
| - /// may be introduced, which would enforce volatile writes and automatically |
265 |
| - /// check for dangling pointers using Rust's borrow checker. |
266 |
| - pub fn frame_buffer(&mut self) -> (*mut u8, usize) { |
| 254 | + /// Access the frame buffer directly |
| 255 | + pub fn frame_buffer(&mut self) -> FrameBuffer { |
267 | 256 | assert!(
|
268 | 257 | self.mode.info.format != PixelFormat::BltOnly,
|
269 | 258 | "Cannot access the framebuffer in a Blt-only mode"
|
270 | 259 | );
|
271 |
| - let data = self.mode.fb_address as *mut u8; |
272 |
| - let len = self.mode.fb_size; |
| 260 | + let base = self.mode.fb_address as *mut u8; |
| 261 | + let size = self.mode.fb_size; |
273 | 262 |
|
274 |
| - (data, len) |
| 263 | + FrameBuffer { |
| 264 | + base, |
| 265 | + size, |
| 266 | + _lifetime: PhantomData, |
| 267 | + } |
275 | 268 | }
|
276 | 269 | }
|
277 | 270 |
|
@@ -519,3 +512,88 @@ pub enum BltOp<'a> {
|
519 | 512 | dims: (usize, usize),
|
520 | 513 | },
|
521 | 514 | }
|
| 515 | + |
| 516 | +/// Direct access to a memory-mapped frame buffer |
| 517 | +pub struct FrameBuffer<'a> { |
| 518 | + base: *mut u8, |
| 519 | + size: usize, |
| 520 | + _lifetime: PhantomData<&'a mut u8>, |
| 521 | +} |
| 522 | + |
| 523 | +impl<'a> FrameBuffer<'a> { |
| 524 | + /// Access the raw framebuffer pointer |
| 525 | + /// |
| 526 | + /// To use this pointer safely and correctly, you must... |
| 527 | + /// - Honor the pixel format and stride specified by the mode info |
| 528 | + /// - Keep memory accesses in bound |
| 529 | + /// - Use volatile reads and writes |
| 530 | + /// - Make sure that the pointer does not outlive the FrameBuffer |
| 531 | + pub fn as_mut_ptr(&mut self) -> *mut u8 { |
| 532 | + self.base |
| 533 | + } |
| 534 | + |
| 535 | + /// Query the framebuffer size in bytes |
| 536 | + pub fn size(&self) -> usize { |
| 537 | + self.size |
| 538 | + } |
| 539 | + |
| 540 | + /// Modify the i-th byte of the frame buffer |
| 541 | + /// |
| 542 | + /// This operation is unsafe because... |
| 543 | + /// - You must honor the pixel format and stride specified by the mode info |
| 544 | + /// - There is no bound checking on memory accesses in release mode |
| 545 | + #[inline] |
| 546 | + pub unsafe fn write_byte(&mut self, index: usize, value: u8) { |
| 547 | + debug_assert!(index < self.size, "Frame buffer accessed out of bounds"); |
| 548 | + self.base.add(index).write_volatile(value) |
| 549 | + } |
| 550 | + |
| 551 | + /// Read the i-th byte of the frame buffer |
| 552 | + /// |
| 553 | + /// This operation is unsafe because... |
| 554 | + /// - You must honor the pixel format and stride specified by the mode info |
| 555 | + /// - There is no bound checking on memory accesses in release mode |
| 556 | + #[inline] |
| 557 | + pub unsafe fn read_byte(&self, index: usize) -> u8 { |
| 558 | + debug_assert!(index < self.size, "Frame buffer accessed out of bounds"); |
| 559 | + self.base.add(index).read_volatile() |
| 560 | + } |
| 561 | + |
| 562 | + /// Write a value in the frame buffer, starting at the i-th byte |
| 563 | + /// |
| 564 | + /// We only recommend using this method with [u8; N] arrays. Once Rust has |
| 565 | + /// const generics, it will be deprecated and replaced with a write_bytes() |
| 566 | + /// method that only accepts [u8; N] input. |
| 567 | + /// |
| 568 | + /// This operation is unsafe because... |
| 569 | + /// - It is your reponsibility to make sure that the value type makes sense |
| 570 | + /// - You must honor the pixel format and stride specified by the mode info |
| 571 | + /// - There is no bound checking on memory accesses in release mode |
| 572 | + #[inline] |
| 573 | + pub unsafe fn write_value<T>(&mut self, index: usize, value: T) { |
| 574 | + debug_assert!( |
| 575 | + index.saturating_add(mem::size_of::<T>()) <= self.size, |
| 576 | + "Frame buffer accessed out of bounds" |
| 577 | + ); |
| 578 | + (self.base.add(index) as *mut T).write_volatile(value) |
| 579 | + } |
| 580 | + |
| 581 | + /// Read a value from the frame buffer, starting at the i-th byte |
| 582 | + /// |
| 583 | + /// We only recommend using this method with [u8; N] arrays. Once Rust has |
| 584 | + /// const generics, it will be deprecated and replaced with a read_bytes() |
| 585 | + /// method that only accepts [u8; N] input. |
| 586 | + /// |
| 587 | + /// This operation is unsafe because... |
| 588 | + /// - It is your reponsibility to make sure that the value type makes sense |
| 589 | + /// - You must honor the pixel format and stride specified by the mode info |
| 590 | + /// - There is no bound checking on memory accesses in release mode |
| 591 | + #[inline] |
| 592 | + pub unsafe fn read_value<T>(&self, index: usize) -> T { |
| 593 | + debug_assert!( |
| 594 | + index.saturating_add(mem::size_of::<T>()) <= self.size, |
| 595 | + "Frame buffer accessed out of bounds" |
| 596 | + ); |
| 597 | + (self.base.add(index) as *const T).read_volatile() |
| 598 | + } |
| 599 | +} |
0 commit comments