Skip to content
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>,
}