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
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ The purposes of the individual assembly stages in this project are the following

## Future Plans

- [ ] Allow to configure the desired screen resolution. Right now we just use the first available VESA screen mode on BIOS and the default GOP mode on UEFI.
- [ ] Create a `multiboot2` compatible disk image in addition to the BIOS and UEFI disk images. This would make it possible to use it on top of the GRUB bootloader.
- [ ] Rewrite most of the BIOS assembly stages in Rust. This has already started.
- [ ] Instead of linking the kernel bytes directly with the bootloader, use a filesystem (e.g. FAT) and load the kernel as a separate file.
Expand Down
55 changes: 45 additions & 10 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,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 @@ -260,10 +261,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 @@ -272,7 +277,8 @@ mod binary {
quote! {
compile_error!(#err)
}
})
}
}
} else {
let err = format!(
"no bootloader dependency in {}\n\n The \
Expand All @@ -286,21 +292,44 @@ 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");
let mut file = File::create(file_path).expect("failed to create config file");
file.write_all(
quote::quote! {
mod parsed_config {
/// Module containing the user-supplied configuration.
/// Public so that `bin/uefi.rs` can read framebuffer configuration.
pub mod parsed_config {
use crate::config::Config;
pub const CONFIG: Config = #config;
/// The parsed configuration given by the user.
pub const CONFIG: Config = #config_stream;
}
}
.to_string()
.as_bytes(),
)
.expect("write to bootloader_config.rs failed");
.expect("writing config failed");

// 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.minimum_framebuffer_width)
.flatten()
.unwrap_or(640),
config
.as_ref()
.map(|c| c.minimum_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 @@ -333,6 +362,8 @@ mod binary {
pub kernel_stack_address: Option<AlignedAddress>,
pub boot_info_address: Option<AlignedAddress>,
pub framebuffer_address: Option<AlignedAddress>,
pub minimum_framebuffer_height: Option<usize>,
pub minimum_framebuffer_width: Option<usize>,
}

/// Convert to tokens suitable for initializing the `Config` struct.
Expand All @@ -351,6 +382,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 minimum_framebuffer_height = optional(self.minimum_framebuffer_height);
let minimum_framebuffer_width = optional(self.minimum_framebuffer_width);

tokens.extend(quote! { Config {
map_physical_memory: #map_physical_memory,
Expand All @@ -362,6 +395,8 @@ mod binary {
kernel_stack_address: #kernel_stack_address,
boot_info_address: #boot_info_address,
framebuffer_address: #framebuffer_address,
minimum_framebuffer_height: #minimum_framebuffer_height,
minimum_framebuffer_width: #minimum_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
25 changes: 24 additions & 1 deletion src/bin/uefi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ static KERNEL: PageAligned<[u8; KERNEL_SIZE]> = PageAligned(KERNEL_BYTES);
struct PageAligned<T>(T);

use bootloader::{
binary::{legacy_memory_region::LegacyFrameAllocator, SystemInfo},
binary::{legacy_memory_region::LegacyFrameAllocator, parsed_config::CONFIG, SystemInfo},
boot_info::FrameBufferInfo,
};
use core::{mem, panic::PanicInfo, slice};
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 +150,28 @@ 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.minimum_framebuffer_height,
CONFIG.minimum_framebuffer_width,
) {
(Some(height), Some(width)) => modes
.filter(|m| {
let res = m.info().resolution();
res.1 >= height && res.0 >= width
})
.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 minimum height of the framebuffer mode.
///
/// Defaults to using the default mode if neither `minimum_framebuffer_height` or
/// `minimum_framebuffer_width` is supplied, and using the last available mode that
/// fits them if 1 or more is set.
pub minimum_framebuffer_height: Option<usize>,
/// Desired minimum width of the framebuffer mode.
///
/// Defaults to using the default mode if neither `minimum_framebuffer_height` or
/// `minimum_framebuffer_width` is supplied, and using the last available mode that
/// fits them if 1 or more is set.
pub minimum_framebuffer_width: Option<usize>,
}