From bb13a86f4b180e354702c91ecb9bc7afbb724ede Mon Sep 17 00:00:00 2001 From: Ellie Ang Date: Sun, 20 Jun 2021 22:38:52 +0200 Subject: [PATCH 1/7] Add desired framebuffer height/width to config, configure UEFI GOP accordingly --- build.rs | 41 +++++++++++++++++++++++++++++------------ src/bin/uefi.rs | 23 +++++++++++++++++++++++ src/config.rs | 12 ++++++++++++ 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/build.rs b/build.rs index 87081628..7837af7c 100644 --- a/build.rs +++ b/build.rs @@ -8,6 +8,7 @@ fn main() { #[cfg(feature = "binary")] mod binary { + use proc_macro2::TokenStream; use quote::quote; use std::convert::TryInto; @@ -281,19 +282,29 @@ mod binary { }; // 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; + } } - } - .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 }, + ); println!("cargo:rerun-if-env-changed=KERNEL"); println!("cargo:rerun-if-env-changed=KERNEL_MANIFEST"); @@ -326,6 +337,8 @@ mod binary { pub kernel_stack_address: Option, pub boot_info_address: Option, pub framebuffer_address: Option, + pub desired_framebuffer_height: Option, + pub desired_framebuffer_width: Option, } /// Convert to tokens suitable for initializing the `Config` struct. @@ -344,6 +357,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, @@ -355,6 +370,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 }}); } } diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 2a398383..1b37cabb 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -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); @@ -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}, @@ -149,6 +153,25 @@ fn init_logger(st: &SystemTable) -> (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 gop 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()) }; diff --git a/src/config.rs b/src/config.rs index 6859f947..85cc89be 100644 --- a/src/config.rs +++ b/src/config.rs @@ -75,4 +75,16 @@ pub struct Config { /// /// Only considered if `map_framebuffer` is `true`. pub framebuffer_address: Option, + /// Desired height of the framebuffer mode when running in UEFI 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, + /// Desired width of the framebuffer mode when running in UEFI 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, } From f4a9df382406b13449551677ac6081bbd781b69b Mon Sep 17 00:00:00 2001 From: Ellie Ang Date: Mon, 21 Jun 2021 15:38:38 +0200 Subject: [PATCH 2/7] Clearer panic message when failing to set gop mode --- src/bin/uefi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 1b37cabb..84619346 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -169,7 +169,7 @@ fn init_logger(st: &SystemTable) -> (PhysAddr, FrameBufferInfo) { }; if let Some(mode) = mode { gop.set_mode(&mode) - .expect_success("failed to apply gop mode"); + .expect_success("Failed to apply the desired display mode"); } let mode_info = gop.current_mode_info(); From c8ea06ca4ec428763cd8d353b28996c29acd1a58 Mon Sep 17 00:00:00 2001 From: Ellie Ang Date: Mon, 21 Jun 2021 16:06:19 +0200 Subject: [PATCH 3/7] Add support for configuring VESA --- build.rs | 31 ++++++++++++++++++++++++------- src/asm/vesa.s | 3 --- src/bin/bios.rs | 1 + src/config.rs | 4 ++-- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/build.rs b/build.rs index 7837af7c..bbbc5fc7 100644 --- a/build.rs +++ b/build.rs @@ -202,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."); @@ -254,10 +255,14 @@ mod binary { .cloned() .unwrap_or_else(|| toml::Value::Table(toml::map::Map::new())); - config_table - .try_into::() - .map(|c| quote! { #c }) - .unwrap_or_else(|err| { + let result = config_table.try_into::(); + 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, @@ -266,7 +271,8 @@ mod binary { quote! { compile_error!(#err) } - }) + } + } } else { let err = format!( "no bootloader dependency in {}\n\n The \ @@ -280,6 +286,7 @@ mod binary { } } }; + let config = config; // Write config to file let write_config = |path: &str, import: TokenStream| { @@ -289,7 +296,7 @@ mod binary { quote::quote! { mod parsed_config { use #import; - pub const CONFIG: Config = #config; + pub const CONFIG: Config = #config_stream; } } .to_string() @@ -306,6 +313,16 @@ mod binary { 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"); println!("cargo:rerun-if-changed={}", kernel.display()); diff --git a/src/asm/vesa.s b/src/asm/vesa.s index e8c18026..adeaf4fd 100644 --- a/src/asm/vesa.s +++ b/src/asm/vesa.s @@ -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 diff --git a/src/bin/bios.rs b/src/bin/bios.rs index c39c45f2..ec9d939f 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -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")); diff --git a/src/config.rs b/src/config.rs index 85cc89be..cac57da7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -75,13 +75,13 @@ pub struct Config { /// /// Only considered if `map_framebuffer` is `true`. pub framebuffer_address: Option, - /// Desired height of the framebuffer mode when running in UEFI mode. + /// 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, - /// Desired width of the framebuffer mode when running in UEFI mode. + /// 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 From 037cc28a9b8c4887c1c678c68fec5385b0fb2fd6 Mon Sep 17 00:00:00 2001 From: Ellie Ang Date: Mon, 23 Aug 2021 16:29:27 +0200 Subject: [PATCH 4/7] Apply formatting --- build.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/build.rs b/build.rs index bbbc5fc7..401a1155 100644 --- a/build.rs +++ b/build.rs @@ -319,9 +319,18 @@ mod binary { 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"); + 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"); From e8aaa413543e255afab07494874493c755ea74c0 Mon Sep 17 00:00:00 2001 From: Ellie Ang Date: Mon, 23 Aug 2021 16:44:39 +0200 Subject: [PATCH 5/7] Make framebuffer size behavior consistent; always minimum bounds --- build.rs | 16 ++++++++-------- src/bin/uefi.rs | 13 ++++++++----- src/config.rs | 16 ++++++++-------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/build.rs b/build.rs index 401a1155..84184d05 100644 --- a/build.rs +++ b/build.rs @@ -321,12 +321,12 @@ mod binary { vesa_miny: .2byte {}", config .as_ref() - .map(|c| c.desired_framebuffer_width) + .map(|c| c.minimum_framebuffer_width) .flatten() .unwrap_or(640), config .as_ref() - .map(|c| c.desired_framebuffer_height) + .map(|c| c.minimum_framebuffer_height) .flatten() .unwrap_or(480) )) @@ -363,8 +363,8 @@ mod binary { pub kernel_stack_address: Option, pub boot_info_address: Option, pub framebuffer_address: Option, - pub desired_framebuffer_height: Option, - pub desired_framebuffer_width: Option, + pub minimum_framebuffer_height: Option, + pub minimum_framebuffer_width: Option, } /// Convert to tokens suitable for initializing the `Config` struct. @@ -383,8 +383,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); + 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, @@ -396,8 +396,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 + minimum_framebuffer_height: #minimum_framebuffer_height, + minimum_framebuffer_width: #minimum_framebuffer_width }}); } } diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 84619346..c5fd07d6 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -156,14 +156,17 @@ fn init_logger(st: &SystemTable) -> (PhysAddr, FrameBufferInfo) { let mode = { let modes = gop.modes().map(Completion::unwrap); match ( - CONFIG.desired_framebuffer_height, - CONFIG.desired_framebuffer_width, + CONFIG.minimum_framebuffer_height, + CONFIG.minimum_framebuffer_width, ) { (Some(height), Some(width)) => modes - .filter(|m| m.info().resolution() == (width, height)) + .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(), + (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, } }; diff --git a/src/config.rs b/src/config.rs index cac57da7..4f6ee1a4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -75,16 +75,16 @@ pub struct Config { /// /// Only considered if `map_framebuffer` is `true`. pub framebuffer_address: Option, - /// Desired height of the framebuffer mode. + /// Desired minimum 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 + /// 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 desired_framebuffer_height: Option, - /// Desired width of the framebuffer mode. + pub minimum_framebuffer_height: Option, + /// Desired minimum 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 + /// 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 desired_framebuffer_width: Option, + pub minimum_framebuffer_width: Option, } From 53cc3b2fc27de4a7eef69873c561c6e60ccb9b84 Mon Sep 17 00:00:00 2001 From: Ellie Ang Date: Mon, 23 Aug 2021 16:56:51 +0200 Subject: [PATCH 6/7] Remove duplicate config write by making `parsed_config` public --- build.rs | 38 +++++++++++++++----------------------- src/bin/uefi.rs | 5 +---- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/build.rs b/build.rs index 84184d05..2b5d679c 100644 --- a/build.rs +++ b/build.rs @@ -8,7 +8,6 @@ fn main() { #[cfg(feature = "binary")] mod binary { - use proc_macro2::TokenStream; use quote::quote; use std::convert::TryInto; @@ -289,29 +288,22 @@ mod binary { let config = config; // Write config to file - 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; - } + let file_path = out_dir.join("bootloader_config.rs"); + let mut file = File::create(file_path).expect("failed to create config file"); + file.write_all( + quote::quote! { + /// Module containing the user-supplied configuration. + /// Public so that `bin/uefi.rs` can read framebuffer configuration. + pub mod parsed_config { + use crate::config::Config; + /// The parsed configuration given by the user. + pub const CONFIG: Config = #config_stream; } - .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 }, - ); + } + .to_string() + .as_bytes(), + ) + .expect("writing config failed"); // Write VESA framebuffer configuration let file_path = out_dir.join("vesa_config.s"); diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index c5fd07d6..59e4291c 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -7,8 +7,6 @@ // 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); @@ -16,11 +14,10 @@ static KERNEL: PageAligned<[u8; KERNEL_SIZE]> = PageAligned(KERNEL_BYTES); struct PageAligned(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 parsed_config::CONFIG; use uefi::{ prelude::{entry, Boot, Handle, ResultExt, Status, SystemTable}, proto::console::gop::{GraphicsOutput, PixelFormat}, From 7a4704b9aa44c9106ce014d9275caaf6bf3b9aaa Mon Sep 17 00:00:00 2001 From: Ellie Ang Date: Mon, 4 Oct 2021 20:17:24 +0200 Subject: [PATCH 7/7] Remove screen resolution from "Future Plans" in README --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 6a0c0833..56540e77 100644 --- a/README.md +++ b/README.md @@ -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.