Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Framebuffer configuration #179

Merged
merged 10 commits into from
Oct 7, 2021
70 changes: 52 additions & 18 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ fn main() {

#[cfg(feature = "binary")]
mod binary {
use proc_macro2::TokenStream;
use quote::quote;
use std::convert::TryInto;

Expand Down Expand Up @@ -201,7 +202,8 @@ mod binary {
}

// Parse configuration from the kernel's Cargo.toml
let config = match env::var("KERNEL_MANIFEST") {
let mut config = None;
let config_stream = match env::var("KERNEL_MANIFEST") {
Err(env::VarError::NotPresent) => {
panic!("The KERNEL_MANIFEST environment variable must be set for building the bootloader.\n\n\
Please use `cargo builder` for building.");
Expand Down Expand Up @@ -253,10 +255,14 @@ mod binary {
.cloned()
.unwrap_or_else(|| toml::Value::Table(toml::map::Map::new()));

config_table
.try_into::<ParsedConfig>()
.map(|c| quote! { #c })
.unwrap_or_else(|err| {
let result = config_table.try_into::<ParsedConfig>();
match result {
Ok(p_config) => {
let stream = quote! { #p_config };
config = Some(p_config);
stream
}
Err(err) => {
let err = format!(
"failed to parse bootloader config in {}:\n\n{}",
path,
Expand All @@ -265,7 +271,8 @@ mod binary {
quote! {
compile_error!(#err)
}
})
}
}
} else {
let err = format!(
"no bootloader dependency in {}\n\n The \
Expand All @@ -279,21 +286,42 @@ mod binary {
}
}
};
let config = config;

// Write config to file
let file_path = out_dir.join("bootloader_config.rs");
let mut file = File::create(file_path).expect("failed to create bootloader_config.rs");
file.write_all(
quote::quote! {
mod parsed_config {
use crate::config::Config;
pub const CONFIG: Config = #config;
let write_config = |path: &str, import: TokenStream| {
let file_path = out_dir.join(path);
let mut file = File::create(file_path).expect("failed to create config file");
file.write_all(
quote::quote! {
mod parsed_config {
use #import;
pub const CONFIG: Config = #config_stream;
}
}
}
.to_string()
.as_bytes(),
)
.expect("write to bootloader_config.rs failed");
.to_string()
.as_bytes(),
)
.expect("writing config failed");
};
write_config(
"bootloader_config.rs",
quote::quote! { crate::config::Config },
);
write_config(
"kernel_bootloader_config.rs",
quote::quote! { bootloader::Config },
);

// Write VESA framebuffer configuration
let file_path = out_dir.join("vesa_config.s");
let mut file = File::create(file_path).expect("failed to create vesa config file");
file.write_fmt(format_args!(
"vesa_minx: .2byte {}\n\
vesa_miny: .2byte {}",
config.as_ref().map(|c| c.desired_framebuffer_width).flatten().unwrap_or(640),
config.as_ref().map(|c| c.desired_framebuffer_height).flatten().unwrap_or(480)
)).expect("writing config failed");

println!("cargo:rerun-if-env-changed=KERNEL");
println!("cargo:rerun-if-env-changed=KERNEL_MANIFEST");
Expand Down Expand Up @@ -326,6 +354,8 @@ mod binary {
pub kernel_stack_address: Option<AlignedAddress>,
pub boot_info_address: Option<AlignedAddress>,
pub framebuffer_address: Option<AlignedAddress>,
pub desired_framebuffer_height: Option<usize>,
pub desired_framebuffer_width: Option<usize>,
}

/// Convert to tokens suitable for initializing the `Config` struct.
Expand All @@ -344,6 +374,8 @@ mod binary {
let kernel_stack_address = optional(self.kernel_stack_address);
let boot_info_address = optional(self.boot_info_address);
let framebuffer_address = optional(self.framebuffer_address);
let desired_framebuffer_height = optional(self.desired_framebuffer_height);
let desired_framebuffer_width = optional(self.desired_framebuffer_width);

tokens.extend(quote! { Config {
map_physical_memory: #map_physical_memory,
Expand All @@ -355,6 +387,8 @@ mod binary {
kernel_stack_address: #kernel_stack_address,
boot_info_address: #boot_info_address,
framebuffer_address: #framebuffer_address,
desired_framebuffer_height: #desired_framebuffer_height,
desired_framebuffer_width: #desired_framebuffer_width
}});
}
}
Expand Down
3 changes: 0 additions & 3 deletions src/asm/vesa.s
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,6 @@ vesa_returngood:
xor eax, eax
ret

vesa_minx: .2byte 640
vesa_miny: .2byte 480

vesa_modeok:
.ascii ": Is this OK? (s)ave/(y)es/(n)o "
.byte 8,8,8,8,0
Expand Down
1 change: 1 addition & 0 deletions src/bin/bios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use x86_64::{PhysAddr, VirtAddr};

global_asm!(include_str!("../asm/stage_1.s"));
global_asm!(include_str!("../asm/stage_2.s"));
global_asm!(include_str!(concat!(env!("OUT_DIR"), "/vesa_config.s")));
global_asm!(include_str!("../asm/vesa.s"));
global_asm!(include_str!("../asm/e820.s"));
global_asm!(include_str!("../asm/stage_3.s"));
Expand Down
23 changes: 23 additions & 0 deletions src/bin/uefi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

// Defines the constants `KERNEL_BYTES` (array of `u8`) and `KERNEL_SIZE` (`usize`).
include!(concat!(env!("OUT_DIR"), "/kernel_info.rs"));
// Contains the bootloader configuration specified by the kernel crate (needed for GOP configuration)
include!(concat!(env!("OUT_DIR"), "/kernel_bootloader_config.rs"));

static KERNEL: PageAligned<[u8; KERNEL_SIZE]> = PageAligned(KERNEL_BYTES);

Expand All @@ -18,10 +20,12 @@ use bootloader::{
boot_info::FrameBufferInfo,
};
use core::{mem, panic::PanicInfo, slice};
use parsed_config::CONFIG;
use uefi::{
prelude::{entry, Boot, Handle, ResultExt, Status, SystemTable},
proto::console::gop::{GraphicsOutput, PixelFormat},
table::boot::{MemoryDescriptor, MemoryType},
Completion,
};
use x86_64::{
structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB},
Expand Down Expand Up @@ -149,6 +153,25 @@ fn init_logger(st: &SystemTable<Boot>) -> (PhysAddr, FrameBufferInfo) {
.expect_success("failed to locate gop");
let gop = unsafe { &mut *gop.get() };

let mode = {
let modes = gop.modes().map(Completion::unwrap);
match (
CONFIG.desired_framebuffer_height,
CONFIG.desired_framebuffer_width,
) {
(Some(height), Some(width)) => modes
.filter(|m| m.info().resolution() == (width, height))
.last(),
(Some(height), None) => modes.filter(|m| m.info().resolution().1 == height).last(),
(None, Some(width)) => modes.filter(|m| m.info().resolution().0 == width).last(),
_ => None,
}
};
if let Some(mode) = mode {
gop.set_mode(&mode)
.expect_success("Failed to apply the desired display mode");
}

let mode_info = gop.current_mode_info();
let mut framebuffer = gop.frame_buffer();
let slice = unsafe { slice::from_raw_parts_mut(framebuffer.as_mut_ptr(), framebuffer.size()) };
Expand Down
12 changes: 12 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,16 @@ pub struct Config {
///
/// Only considered if `map_framebuffer` is `true`.
pub framebuffer_address: Option<u64>,
/// Desired height of the framebuffer mode.
///
/// Defaults to using the default mode if neither `desired_framebuffer_height` or
/// `desired_framebuffer_width` is supplied, and using the last available mode that
/// fits them if 1 or more is set.
pub desired_framebuffer_height: Option<usize>,
/// Desired width of the framebuffer mode.
///
/// Defaults to using the default mode if neither `desired_framebuffer_height` or
/// `desired_framebuffer_width` is supplied, and using the last available mode that
/// fits them if 1 or more is set.
pub desired_framebuffer_width: Option<usize>,
}