Skip to content

Commit 0c16754

Browse files
authored
Framebuffer configuration (#179)
* Add desired framebuffer height/width to config, configure UEFI GOP accordingly * Clearer panic message when failing to set gop mode * Add support for configuring VESA * Make framebuffer size behavior consistent; always minimum bounds * Remove duplicate config write by making `parsed_config` public * Remove screen resolution from "Future Plans" in README
1 parent 2bdc5ac commit 0c16754

File tree

6 files changed

+82
-15
lines changed

6 files changed

+82
-15
lines changed

Diff for: README.md

-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ The purposes of the individual assembly stages in this project are the following
6060

6161
## Future Plans
6262

63-
- [ ] 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.
6463
- [ ] 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.
6564
- [ ] Rewrite most of the BIOS assembly stages in Rust. This has already started.
6665
- [ ] Instead of linking the kernel bytes directly with the bootloader, use a filesystem (e.g. FAT) and load the kernel as a separate file.

Diff for: build.rs

+45-10
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ mod binary {
201201
}
202202

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

263-
config_table
264-
.try_into::<ParsedConfig>()
265-
.map(|c| quote! { #c })
266-
.unwrap_or_else(|err| {
264+
let result = config_table.try_into::<ParsedConfig>();
265+
match result {
266+
Ok(p_config) => {
267+
let stream = quote! { #p_config };
268+
config = Some(p_config);
269+
stream
270+
}
271+
Err(err) => {
267272
let err = format!(
268273
"failed to parse bootloader config in {}:\n\n{}",
269274
path,
@@ -272,7 +277,8 @@ mod binary {
272277
quote! {
273278
compile_error!(#err)
274279
}
275-
})
280+
}
281+
}
276282
} else {
277283
let err = format!(
278284
"no bootloader dependency in {}\n\n The \
@@ -286,21 +292,44 @@ mod binary {
286292
}
287293
}
288294
};
295+
let config = config;
289296

290297
// Write config to file
291298
let file_path = out_dir.join("bootloader_config.rs");
292-
let mut file = File::create(file_path).expect("failed to create bootloader_config.rs");
299+
let mut file = File::create(file_path).expect("failed to create config file");
293300
file.write_all(
294301
quote::quote! {
295-
mod parsed_config {
302+
/// Module containing the user-supplied configuration.
303+
/// Public so that `bin/uefi.rs` can read framebuffer configuration.
304+
pub mod parsed_config {
296305
use crate::config::Config;
297-
pub const CONFIG: Config = #config;
306+
/// The parsed configuration given by the user.
307+
pub const CONFIG: Config = #config_stream;
298308
}
299309
}
300310
.to_string()
301311
.as_bytes(),
302312
)
303-
.expect("write to bootloader_config.rs failed");
313+
.expect("writing config failed");
314+
315+
// Write VESA framebuffer configuration
316+
let file_path = out_dir.join("vesa_config.s");
317+
let mut file = File::create(file_path).expect("failed to create vesa config file");
318+
file.write_fmt(format_args!(
319+
"vesa_minx: .2byte {}\n\
320+
vesa_miny: .2byte {}",
321+
config
322+
.as_ref()
323+
.map(|c| c.minimum_framebuffer_width)
324+
.flatten()
325+
.unwrap_or(640),
326+
config
327+
.as_ref()
328+
.map(|c| c.minimum_framebuffer_height)
329+
.flatten()
330+
.unwrap_or(480)
331+
))
332+
.expect("writing config failed");
304333

305334
println!("cargo:rerun-if-env-changed=KERNEL");
306335
println!("cargo:rerun-if-env-changed=KERNEL_MANIFEST");
@@ -333,6 +362,8 @@ mod binary {
333362
pub kernel_stack_address: Option<AlignedAddress>,
334363
pub boot_info_address: Option<AlignedAddress>,
335364
pub framebuffer_address: Option<AlignedAddress>,
365+
pub minimum_framebuffer_height: Option<usize>,
366+
pub minimum_framebuffer_width: Option<usize>,
336367
}
337368

338369
/// Convert to tokens suitable for initializing the `Config` struct.
@@ -351,6 +382,8 @@ mod binary {
351382
let kernel_stack_address = optional(self.kernel_stack_address);
352383
let boot_info_address = optional(self.boot_info_address);
353384
let framebuffer_address = optional(self.framebuffer_address);
385+
let minimum_framebuffer_height = optional(self.minimum_framebuffer_height);
386+
let minimum_framebuffer_width = optional(self.minimum_framebuffer_width);
354387

355388
tokens.extend(quote! { Config {
356389
map_physical_memory: #map_physical_memory,
@@ -362,6 +395,8 @@ mod binary {
362395
kernel_stack_address: #kernel_stack_address,
363396
boot_info_address: #boot_info_address,
364397
framebuffer_address: #framebuffer_address,
398+
minimum_framebuffer_height: #minimum_framebuffer_height,
399+
minimum_framebuffer_width: #minimum_framebuffer_width
365400
}});
366401
}
367402
}

Diff for: src/asm/vesa.s

-3
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,6 @@ vesa_returngood:
124124
xor eax, eax
125125
ret
126126

127-
vesa_minx: .2byte 640
128-
vesa_miny: .2byte 480
129-
130127
vesa_modeok:
131128
.ascii ": Is this OK? (s)ave/(y)es/(n)o "
132129
.byte 8,8,8,8,0

Diff for: src/bin/bios.rs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use x86_64::{PhysAddr, VirtAddr};
2222

2323
global_asm!(include_str!("../asm/stage_1.s"));
2424
global_asm!(include_str!("../asm/stage_2.s"));
25+
global_asm!(include_str!(concat!(env!("OUT_DIR"), "/vesa_config.s")));
2526
global_asm!(include_str!("../asm/vesa.s"));
2627
global_asm!(include_str!("../asm/e820.s"));
2728
global_asm!(include_str!("../asm/stage_3.s"));

Diff for: src/bin/uefi.rs

+24-1
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ static KERNEL: PageAligned<[u8; KERNEL_SIZE]> = PageAligned(KERNEL_BYTES);
1414
struct PageAligned<T>(T);
1515

1616
use bootloader::{
17-
binary::{legacy_memory_region::LegacyFrameAllocator, SystemInfo},
17+
binary::{legacy_memory_region::LegacyFrameAllocator, parsed_config::CONFIG, SystemInfo},
1818
boot_info::FrameBufferInfo,
1919
};
2020
use core::{mem, panic::PanicInfo, slice};
2121
use uefi::{
2222
prelude::{entry, Boot, Handle, ResultExt, Status, SystemTable},
2323
proto::console::gop::{GraphicsOutput, PixelFormat},
2424
table::boot::{MemoryDescriptor, MemoryType},
25+
Completion,
2526
};
2627
use x86_64::{
2728
structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB},
@@ -149,6 +150,28 @@ fn init_logger(st: &SystemTable<Boot>) -> (PhysAddr, FrameBufferInfo) {
149150
.expect_success("failed to locate gop");
150151
let gop = unsafe { &mut *gop.get() };
151152

153+
let mode = {
154+
let modes = gop.modes().map(Completion::unwrap);
155+
match (
156+
CONFIG.minimum_framebuffer_height,
157+
CONFIG.minimum_framebuffer_width,
158+
) {
159+
(Some(height), Some(width)) => modes
160+
.filter(|m| {
161+
let res = m.info().resolution();
162+
res.1 >= height && res.0 >= width
163+
})
164+
.last(),
165+
(Some(height), None) => modes.filter(|m| m.info().resolution().1 >= height).last(),
166+
(None, Some(width)) => modes.filter(|m| m.info().resolution().0 >= width).last(),
167+
_ => None,
168+
}
169+
};
170+
if let Some(mode) = mode {
171+
gop.set_mode(&mode)
172+
.expect_success("Failed to apply the desired display mode");
173+
}
174+
152175
let mode_info = gop.current_mode_info();
153176
let mut framebuffer = gop.frame_buffer();
154177
let slice = unsafe { slice::from_raw_parts_mut(framebuffer.as_mut_ptr(), framebuffer.size()) };

Diff for: src/config.rs

+12
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,16 @@ pub struct Config {
7575
///
7676
/// Only considered if `map_framebuffer` is `true`.
7777
pub framebuffer_address: Option<u64>,
78+
/// Desired minimum height of the framebuffer mode.
79+
///
80+
/// Defaults to using the default mode if neither `minimum_framebuffer_height` or
81+
/// `minimum_framebuffer_width` is supplied, and using the last available mode that
82+
/// fits them if 1 or more is set.
83+
pub minimum_framebuffer_height: Option<usize>,
84+
/// Desired minimum width of the framebuffer mode.
85+
///
86+
/// Defaults to using the default mode if neither `minimum_framebuffer_height` or
87+
/// `minimum_framebuffer_width` is supplied, and using the last available mode that
88+
/// fits them if 1 or more is set.
89+
pub minimum_framebuffer_width: Option<usize>,
7890
}

0 commit comments

Comments
 (0)