Skip to content

Commit

Permalink
Expose more info about the graphical framebuffer (#787)
Browse files Browse the repository at this point in the history
This makes it possible to obtain:
* The stride of the framebuffer in memory, via `bytes_per_scanline`.
* The size of each pixel in memory.
* The format of each pixel in memory: its Red, Green, Blue values.
  • Loading branch information
kevinaboos authored Jan 5, 2023
1 parent 6628d27 commit cc416c9
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 57 deletions.
37 changes: 17 additions & 20 deletions kernel/framebuffer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,25 @@ pub use pixel::*;
/// because its memory is directly mapped to the VESA display device's underlying physical memory.
pub fn init<P: Pixel>() -> Result<Framebuffer<P>, &'static str> {
// get the graphic mode information
let vesa_display_phys_start: PhysicalAddress;
let buffer_width: usize;
let buffer_height: usize;
{
let graphic_info = multicore_bringup::GRAPHIC_INFO.lock();
info!("Graphical framebuffer info: {} x {}, at paddr {:#X}",
graphic_info.width(),
graphic_info.height(),
graphic_info.physical_address(),
);
if graphic_info.physical_address() == 0 {
return Err("Failed to get graphic mode information!");
}
vesa_display_phys_start = PhysicalAddress::new(graphic_info.physical_address() as usize)
.ok_or("Graphic mode physical address was invalid")?;
buffer_width = graphic_info.width() as usize;
buffer_height = graphic_info.height() as usize;
};
let graphic_info = multicore_bringup::get_graphic_info()
.ok_or("Failed to get graphic mode information!")?;

let vesa_display_phys_start = PhysicalAddress::new(graphic_info.physical_address() as usize)
.ok_or("Graphic mode physical address was invalid")?;
let buffer_width = graphic_info.width() as usize;
let buffer_height = graphic_info.height() as usize;
info!("Graphical framebuffer info: {} x {}, at paddr {:#X}",
graphic_info.width(),
graphic_info.height(),
graphic_info.physical_address(),
);

// create and return the final framebuffer
let framebuffer = Framebuffer::new(buffer_width, buffer_height, Some(vesa_display_phys_start))?;
Ok(framebuffer)
Framebuffer::new(
buffer_width,
buffer_height,
Some(vesa_display_phys_start),
)
}

/// A framebuffer is a region of memory interpreted as a 2-D array of pixels.
Expand Down
93 changes: 80 additions & 13 deletions kernel/multicore_bringup/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,16 @@ const TRAMPOLINE: usize = AP_STARTUP - PAGE_SIZE;
const GRAPHIC_INFO_OFFSET_FROM_TRAMPOLINE: usize = 0x100;

/// Graphic mode information that will be updated after `handle_ap_cores()` is invoked.
pub static GRAPHIC_INFO: Mutex<GraphicInfo> = Mutex::new(GraphicInfo::new());
static GRAPHIC_INFO: Mutex<Option<GraphicInfo>> = Mutex::new(None);

/// Returns information about the currently-active graphical framebuffer.
///
/// This will return `None` if `handle_ap_cores()` has not yet been invoked
/// (which is the function that obtains the graphic info in the first place),
/// or if the obtained graphic info is invalid.
pub fn get_graphic_info() -> Option<GraphicInfo> {
GRAPHIC_INFO.lock().filter(GraphicInfo::is_valid)
}

/// A structure to access information about the graphical framebuffer mode
/// that was discovered and chosen in the AP's real-mode initialization sequence.
Expand All @@ -75,26 +84,40 @@ pub struct GraphicInfo {
_attributes: u16,
/// The total size of the graphic VGA memory in 64 KiB chunks.
total_memory_size_64_kib_chunks: u16,
/// The number of bytes in each row or line of the framebuffer's memory.
/// This is similar to the "stride" of a framebuffer, but is expressed
/// in units of bytes rather than in units of pixels.
bytes_per_scanline: u16,
/// The size of each pixel, in number of bits.
bits_per_pixel: u8,
/// The size of a pixel's red component, in number of bits.
red_mask_size: u8,
/// The bit position of the least significant byte of a pixel's red component.
red_field_position: u8,
/// The size of a pixel's green component, in number of bits.
green_mask_size: u8,
/// The bit position of the least significant byte of a pixel's green component.
green_field_position: u8,
/// The size of a pixel's blue component, in number of bits.
blue_mask_size: u8,
/// The bit position of the least significant byte of a pixel's blue component.
blue_field_position: u8,
}

impl GraphicInfo {
const fn new() -> Self {
Self {
width: 0,
height: 0,
physical_address: 0,
_mode: 0,
_attributes: 0,
total_memory_size_64_kib_chunks: 0,
}
/// Checks this `GraphicInfo` to ensure it is valid.
///
/// Currently, its width, height, and physical address all must be non-zero.
fn is_valid(&self) -> bool {
self.width != 0 && self.height != 0 && self.physical_address != 0
}

/// Returns the visible width of the screen, in pixels.
pub fn width(&self) -> u16 {
self.width
}

/// Returns the height width of the screen, in pixels.
/// Returns the visible height of the screen, in pixels.
pub fn height(&self) -> u16 {
self.height
}
Expand All @@ -112,6 +135,49 @@ impl GraphicInfo {
pub fn total_memory_size_in_bytes(&self) -> u32 {
(self.total_memory_size_64_kib_chunks as u32) << 16
}

/// The number of bytes in each row or line of the framebuffer's memory.
///
/// This is similar to the "stride" of a framebuffer, but is expressed
/// in units of bytes rather than in units of pixels.
pub fn bytes_per_scanline(&self) -> u16 {
self.bytes_per_scanline
}

/// The size of each pixel, in number of bits, *not* bytes.
pub fn bits_per_pixel(&self) -> u8 {
self.bits_per_pixel
}

/// The size of a pixel's Red value, in number of bits.
pub fn red_size(&self) -> u8 {
self.red_mask_size
}

/// The position of the least significant bit of a pixel's Red value.
pub fn red_position(&self) -> u8 {
self.red_field_position
}

/// The size of a pixel's Green value, in number of bits.
pub fn green_size(&self) -> u8 {
self.green_mask_size
}

/// The position of the least significant bit of a pixel's Green value.
pub fn green_position(&self) -> u8 {
self.green_field_position
}

/// The size of a pixel's Blue value, in number of bits.
pub fn blue_size(&self) -> u8 {
self.blue_mask_size
}

/// The position of the least significant bit of a pixel's Blue value.
pub fn blue_position(&self) -> u8 {
self.blue_field_position
}
}

/// Starts up and sets up AP cores based on system information from ACPI
Expand Down Expand Up @@ -242,9 +308,10 @@ pub fn handle_ap_cores(

// Retrieve the graphic mode information written during the AP bootup sequence in `ap_realmode.asm`.
{
let graphic_info = trampoline_mapped_pages.as_type::<GraphicInfo>(GRAPHIC_INFO_OFFSET_FROM_TRAMPOLINE)?;
let graphic_info = trampoline_mapped_pages
.as_type::<GraphicInfo>(GRAPHIC_INFO_OFFSET_FROM_TRAMPOLINE)?;
info!("Obtained graphic info from real mode: {:?}", graphic_info);
*GRAPHIC_INFO.lock() = graphic_info.clone();
*GRAPHIC_INFO.lock() = Some(*graphic_info);
}

// Wait for all CPUs to finish booting and init
Expand Down
87 changes: 63 additions & 24 deletions kernel/nano_core/src/asm/ap_realmode.asm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ VBECardInfo:

ABSOLUTE 0x5200
VBEModeInfo:
; All VBE versions have the following
.attributes resw 1
.winAattr resb 1
.winBattr resb 1
Expand All @@ -27,6 +28,7 @@ VBEModeInfo:
.winBsegment resw 1
.winfuncptr resd 1
.bytesperscanline resw 1
; VBE 1.2 and above has the following
.width resw 1
.height resw 1
.xcharsize resb 1
Expand All @@ -47,9 +49,11 @@ VBEModeInfo:
.rsvdmasksize resb 1
.rsvdfieldposition resb 1
.directcolormodeinfo resb 1
; VBE 2.0 and above has the following
.physbaseptr resd 1
.reserved1 resd 1
.reserved2 resw 1
; VBE 3.0 and above has the following (currently unused)
.reserved_vbe3 resb 13
.reserved3 resb 189

Expand All @@ -65,6 +69,14 @@ best_mode:
.physaddr resd 1
.attributes resw 1
.totalmemory64KiB resw 1
.bytesperscanline resw 1
.bitsperpixel resb 1
.redmasksize resb 1
.redfieldposition resb 1
.greenmasksize resb 1
.greenfieldposition resb 1
.bluemasksize resb 1
.bluefieldposition resb 1
%endif ; BIOS

section .init.realmodetext16 progbits alloc exec nowrite
Expand Down Expand Up @@ -118,36 +130,22 @@ ap_start_realmode:


; This is the start of the graphics mode code.
; First, initialize both our "best" mode info and the Rust-visible GraphicInfo to all zeros.
mov word [best_mode.mode], 0
mov word [best_mode.width], 0
mov word [best_mode.height], 0
mov word [best_mode.physaddr], 0
mov word [best_mode.physaddr+2], 0
mov word [best_mode.attributes], 0
mov word [best_mode.totalmemory64KiB], 0
; First, zero-out the key fields of our "best" mode info and the Rust-visible GraphicInfo.
mov word [best_mode.mode], 0
mov word [best_mode.width], 0
mov word [best_mode.height], 0
mov dword [best_mode.physaddr], 0
; WARNING: the below code must be kept in sync with the `GraphicInfo` struct
; in the `multicore_bringup` crate.
push di
mov ax, 0
mov es, ax
mov di, 0xF100
; set width to zero
mov word [es:di+0], 0
mov word [es:di+2], 0
mov word [es:di+4], 0
mov word [es:di+6], 0
; set height to zero
mov word [es:di+8], 0
mov word [es:di+10], 0
mov word [es:di+12], 0
mov word [es:di+14], 0
; set physical address to zero
mov word [es:di+16], 0
mov word [es:di+18], 0
mov word [es:di+20], 0
mov word [es:di+22], 0
mov di, 0xF100 ; see `GRAPHIC_INFO_OFFSET_FROM_TRAMPOLINE`
; set key fields of `GraphicInfo` to zero
mov word [es:di+0], 0 ; width
mov word [es:di+2], 0 ; height
mov dword [es:di+4], 0 ; physical address
pop di

; Next, we get the VBE card info such that we can iterate over the list of available modes.
Expand Down Expand Up @@ -219,6 +217,22 @@ get_vbe_card_info:
mov word [best_mode.attributes], ax
mov word ax, [VBECardInfo.totalmemory64KiB]
mov word [best_mode.totalmemory64KiB], ax
mov word ax, [VBEModeInfo.bytesperscanline]
mov word [best_mode.bytesperscanline], ax
mov byte al, [VBEModeInfo.bitsperpixel]
mov byte [best_mode.bitsperpixel], al
mov byte al, [VBEModeInfo.redmasksize]
mov byte [best_mode.redmasksize], al
mov byte al, [VBEModeInfo.redfieldposition]
mov byte [best_mode.redfieldposition], al
mov byte al, [VBEModeInfo.greenmasksize]
mov byte [best_mode.greenmasksize], al
mov byte al, [VBEModeInfo.greenfieldposition]
mov byte [best_mode.greenfieldposition], al
mov byte al, [VBEModeInfo.bluemasksize]
mov byte [best_mode.bluemasksize], al
mov byte al, [VBEModeInfo.bluefieldposition]
mov byte [best_mode.bluefieldposition], al
jmp .next_mode ; we may find better modes later, so keep iterating!

; Once we have iterated over all available graphic modes, we jump here to
Expand Down Expand Up @@ -251,6 +265,31 @@ mode_iter_done:
; move the best mode's totalmemory64KiB value to [0:F10C]
mov word ax, [best_mode.totalmemory64KiB]
mov word [es:di+12], ax
; move the best mode's bytesperscanline value to [0:F10E]
mov word ax, [best_mode.bytesperscanline]
mov word [es:di+14], ax
; move the best mode's bitsperpixel value to [0:F110]
mov byte al, [best_mode.bitsperpixel]
mov byte [es:di+16], al
; move the best mode's redmasksize value to [0:F111]
mov byte al, [best_mode.redmasksize]
mov byte [es:di+17], al
; move the best mode's redfieldposition value to [0:F112]
mov byte al, [best_mode.redfieldposition]
mov byte [es:di+18], al
; move the best mode's greenmasksize value to [0:F113]
mov byte al, [best_mode.greenmasksize]
mov byte [es:di+19], al
; move the best mode's greenfieldposition value to [0:F114]
mov byte al, [best_mode.greenfieldposition]
mov byte [es:di+20], al
; move the best mode's bluemasksize value to [0:F115]
mov byte al, [best_mode.bluemasksize]
mov byte [es:di+21], al
; move the best mode's bluefieldposition value to [0:F116]
mov byte al, [best_mode.bluefieldposition]
mov byte [es:di+22], al
; Note: the size of `GraphicInfo` is currently 23 bytes.
pop di

; Finally, once we have saved the info of the best graphical mode,
Expand Down

0 comments on commit cc416c9

Please sign in to comment.