From 052f8ee2b7b3034a2ead468c9564d1a8cf599ad0 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 15 Apr 2020 10:19:19 +0200 Subject: [PATCH 001/174] Initial Commit --- .gitignore | 1 + Cargo.toml | 9 +++++++++ src/main.rs | 3 +++ 3 files changed, 13 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..1492c241 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "uefi-test" +version = "0.1.0" +authors = ["Philipp Oppermann "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 00000000..e7a11a96 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} From 7352a281f8991a868bcaa4075081d42eb2123ded Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 15 Apr 2020 10:37:24 +0200 Subject: [PATCH 002/174] Create a minimal UEFI application --- .cargo/config.toml | 6 +++ Cargo.lock | 102 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + rust-toolchain | 1 + src/main.rs | 17 +++++++- 5 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 .cargo/config.toml create mode 100644 Cargo.lock create mode 100644 rust-toolchain diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..ffa32738 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,6 @@ +[build] +target = "x86_64-unknown-uefi" + +[alias] +xbuild = "build -Z build-std=core" +xcheck = "check -Z build-std=core" diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..53e43d53 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,102 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bit_field" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "proc-macro2" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "ucs2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85061f4e43545a613c0da6b87725bf23f8da8613cf2473719c4f71a270c4ce8a" +dependencies = [ + "bit_field", +] + +[[package]] +name = "uefi" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cec6a2850639f588cc8042140b5deaa2f2170c23db5adb08f4316fa04cdb4a0" +dependencies = [ + "bitflags", + "log", + "ucs2", + "uefi-macros", +] + +[[package]] +name = "uefi-macros" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d4a1b0215dc72e83d8d501b3275eb59477d3b595be8861abfb8ab110180955" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "uefi-test" +version = "0.1.0" +dependencies = [ + "uefi", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" diff --git a/Cargo.toml b/Cargo.toml index 1492c241..394afd8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +uefi = "0.4.4" diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 00000000..bf867e0a --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly diff --git a/src/main.rs b/src/main.rs index e7a11a96..a4780b44 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,16 @@ -fn main() { - println!("Hello, world!"); +#![no_std] +#![no_main] +#![feature(abi_efiapi)] + +use core::panic::PanicInfo; +use uefi::prelude::{entry, Boot, Handle, Status, SystemTable}; + +#[entry] +fn efi_main(image: Handle, st: SystemTable) -> Status { + Status::SUCCESS +} + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop {} } From ba6b02ef9e8b8dff0335644b4b4b4e82fc3e280f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 15 Apr 2020 10:38:41 +0200 Subject: [PATCH 003/174] Add rust-analyzer configuration for VSCode --- .vscode/settings.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..9895c798 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "rust-analyzer.checkOnSave.command": "xcheck", + "rust-analyzer.checkOnSave.allTargets": false +} \ No newline at end of file From dca9ddf2a697a605df0bc82cbb62d57c78d69a56 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 16 Jun 2020 09:49:17 +0200 Subject: [PATCH 004/174] Use a general `x` alias for cross compiling Use rust-analyzer's support for extra arguments instead of using `xcheck` alias. --- .cargo/config.toml | 3 +-- .vscode/settings.json | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index ffa32738..f54e14a3 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -2,5 +2,4 @@ target = "x86_64-unknown-uefi" [alias] -xbuild = "build -Z build-std=core" -xcheck = "check -Z build-std=core" +x = "-Z build-std=core,alloc" diff --git a/.vscode/settings.json b/.vscode/settings.json index 9895c798..309b3c40 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,6 @@ { - "rust-analyzer.checkOnSave.command": "xcheck", - "rust-analyzer.checkOnSave.allTargets": false + "rust-analyzer.checkOnSave.allTargets": false, + "rust-analyzer.checkOnSave.extraArgs": [ + "-Zbuild-std=core,alloc" + ] } \ No newline at end of file From 4769e94accf7ce7ae0822fc9132acbdb70490116 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 16 Jun 2020 09:50:05 +0200 Subject: [PATCH 005/174] Initialize framebuffer and exit boot services --- Cargo.lock | 58 ++++++++++++++++++++++++++++++++++++++++---------- Cargo.toml | 3 +++ src/main.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 105 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53e43d53..3a7d376a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,11 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "bit_field" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" + [[package]] name = "bit_field" version = "0.10.0" @@ -29,27 +35,33 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.10" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.3" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ "proc-macro2", ] +[[package]] +name = "rlibc" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" + [[package]] name = "syn" -version = "1.0.17" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" +checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" dependencies = [ "proc-macro2", "quote", @@ -62,14 +74,14 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85061f4e43545a613c0da6b87725bf23f8da8613cf2473719c4f71a270c4ce8a" dependencies = [ - "bit_field", + "bit_field 0.10.0", ] [[package]] name = "uefi" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec6a2850639f588cc8042140b5deaa2f2170c23db5adb08f4316fa04cdb4a0" +checksum = "ab1f1403ecbad37d25120161acc3db12066febf3446efcc40b7631d30678505d" dependencies = [ "bitflags", "log", @@ -79,20 +91,34 @@ dependencies = [ [[package]] name = "uefi-macros" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d4a1b0215dc72e83d8d501b3275eb59477d3b595be8861abfb8ab110180955" +checksum = "7a69fa8dd920e84d783769c44560484ade81f6c765cde2e1cc46c754ddf95947" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "uefi-services" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a985e3dd3e8ce234af981948ff1138e72936af03b04a90b082f9d8a6f35ad37" +dependencies = [ + "log", + "uefi", + "x86_64", +] + [[package]] name = "uefi-test" version = "0.1.0" dependencies = [ + "log", + "rlibc", "uefi", + "uefi-services", ] [[package]] @@ -100,3 +126,13 @@ name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "x86_64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "365de37eb7c6da582cbb510dd0f3f1235d24ff6309a8a96e8a9909cc9bfd608f" +dependencies = [ + "bit_field 0.9.0", + "bitflags", +] diff --git a/Cargo.toml b/Cargo.toml index 394afd8b..832122ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,6 @@ edition = "2018" [dependencies] uefi = "0.4.4" +rlibc = "1.0.0" +uefi-services = "0.2.4" +log = "0.4.8" diff --git a/src/main.rs b/src/main.rs index a4780b44..d50a26db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,15 +2,64 @@ #![no_main] #![feature(abi_efiapi)] -use core::panic::PanicInfo; -use uefi::prelude::{entry, Boot, Handle, Status, SystemTable}; +extern crate alloc; +extern crate rlibc; + +use alloc::vec; +use core::mem; +use uefi::{ + prelude::{entry, Boot, Handle, ResultExt, Status, SystemTable}, + proto::console::gop::GraphicsOutput, + table::boot::MemoryDescriptor, +}; #[entry] fn efi_main(image: Handle, st: SystemTable) -> Status { - Status::SUCCESS -} + // Initialize utilities (logging, memory allocation...) + uefi_services::init(&st).expect_success("Failed to initialize utilities"); + + log::set_max_level(log::LevelFilter::Info); + + let stdout = st.stdout(); + stdout.reset(true).expect_success("failed to reset stdout"); + log::info!("Hello World from UEFI bootloader!"); + + let boot_services = st.boot_services(); + let gop = boot_services + .locate_protocol::() + .expect_success("failed to locate gop"); + let gop = unsafe { &mut *gop.get() }; + + // print available video modes + for mode in gop.modes().map(|c| c.unwrap()) { + log::trace!("Mode: {:x?}", mode.info()); + } + + let mode_info = gop.current_mode_info(); + let (width, height) = mode_info.resolution(); + log::info!("Active video Mode: {:x?}", mode_info); + + let mut framebuffer = gop.frame_buffer(); + + let max_mmap_size = + st.boot_services().memory_map_size() + 8 * mem::size_of::(); + let mut mmap_storage = vec![0; max_mmap_size]; + + log::trace!("exiting boot services"); + + st.exit_boot_services(image, &mut mmap_storage) + .expect_success("Failed to exit boot services"); + + let fill_color = [0x00u8, 0x00, 0xff, 0x00]; + for col in 0..width { + for row in 0..height { + let index = row * mode_info.stride() + col; + let byte_index = index * 4; + unsafe { + framebuffer.write_value(byte_index, fill_color); + } + } + } -#[panic_handler] -fn panic(_info: &PanicInfo) -> ! { loop {} } From 45b36ff54293ac5169cfb2a5abd5df485f4a5cbe Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 16 Jun 2020 16:35:44 +0200 Subject: [PATCH 006/174] Start a bootloader library to abstract over differences between BIOS and UEFI --- bootloader-lib/Cargo.toml | 22 +++++ bootloader-lib/src/lib.rs | 26 +++++ bootloader-lib/src/load_kernel.rs | 86 +++++++++++++++++ bootloader-lib/src/logger.rs | 155 ++++++++++++++++++++++++++++++ 4 files changed, 289 insertions(+) create mode 100644 bootloader-lib/Cargo.toml create mode 100644 bootloader-lib/src/lib.rs create mode 100644 bootloader-lib/src/load_kernel.rs create mode 100644 bootloader-lib/src/logger.rs diff --git a/bootloader-lib/Cargo.toml b/bootloader-lib/Cargo.toml new file mode 100644 index 00000000..593b011b --- /dev/null +++ b/bootloader-lib/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "bootloader-lib" +version = "0.1.0" +authors = ["Philipp Oppermann "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +xmas-elf = "0.7.0" +x86_64 = "0.11.0" +log = "0.4.8" +spinning_top = "0.1.0" + +[dependencies.conquer-once] +version = "0.2.0" +default-features = false + +[dependencies.font8x8] +version = "0.2.5" +default-features = false +features = ["unicode"] diff --git a/bootloader-lib/src/lib.rs b/bootloader-lib/src/lib.rs new file mode 100644 index 00000000..fff33d9a --- /dev/null +++ b/bootloader-lib/src/lib.rs @@ -0,0 +1,26 @@ +#![feature(slice_fill)] +#![no_std] + +pub use logger::{FrameBufferInfo, PixelFormat}; +use core::panic::PanicInfo; + +mod load_kernel; +mod logger; + +pub fn init_logger(framebuffer: &'static mut [u8], info: FrameBufferInfo) { + let logger = logger::LOGGER.get_or_init(move || logger::LockedLogger::new(framebuffer, info)); + log::set_logger(logger).expect("logger already set"); + log::set_max_level(log::LevelFilter::Info); +} + +pub fn load_kernel(kernel: &'static [u8]) -> ! { + load_kernel::load_kernel(kernel).expect("Failed to parse kernel"); + loop {} +} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + unsafe { logger::LOGGER.get().map(|l| l.force_unlock()) }; + log::error!("{}", info); + loop {} +} diff --git a/bootloader-lib/src/load_kernel.rs b/bootloader-lib/src/load_kernel.rs new file mode 100644 index 00000000..68313d1c --- /dev/null +++ b/bootloader-lib/src/load_kernel.rs @@ -0,0 +1,86 @@ +use x86_64::{ + structures::paging::{Page, PhysFrame}, + PhysAddr, VirtAddr, +}; +use xmas_elf::{ + header, + program::{self, ProgramHeader, Type}, + ElfFile, +}; + +const PAGE_SIZE: u64 = 4096; + +struct Loader<'a> { + bytes: &'a [u8], + elf_file: ElfFile<'a>, +} + +impl<'a> Loader<'a> { + fn new(bytes: &'a [u8]) -> Result { + let elf_file = ElfFile::new(bytes)?; + header::sanity_check(&elf_file)?; + let loader = Loader { bytes, elf_file }; + + log::info!("Elf file loaded at {:#p}", bytes); + if !loader.kernel_offset().is_aligned(PAGE_SIZE) { + return Err("Loaded kernel ELF file is not sufficiently aligned"); + } + + Ok(loader) + } + + fn kernel_offset(&self) -> PhysAddr { + PhysAddr::new(&self.bytes[0] as *const u8 as u64) + } + + fn load_segments(&self) -> Result<(), &'static str> { + for program_header in self.elf_file.program_iter() { + program::sanity_check(program_header, &self.elf_file)?; + match program_header.get_type()? { + Type::Load => self.handle_load_segment(program_header)?, + Type::Tls => self.handle_tls_segment(program_header)?, + Type::Null + | Type::Dynamic + | Type::Interp + | Type::Note + | Type::ShLib + | Type::Phdr + | Type::GnuRelro + | Type::OsSpecific(_) + | Type::ProcessorSpecific(_) => {} + } + } + Ok(()) + } + + fn handle_load_segment(&self, segment: ProgramHeader) -> Result<(), &'static str> { + log::info!("Handling Segment: {:x?}", segment); + + let phys_start_addr = self.kernel_offset() + segment.offset(); + let start_frame: PhysFrame = PhysFrame::containing_address(phys_start_addr); + let end_frame: PhysFrame = + PhysFrame::containing_address(phys_start_addr + segment.file_size() - 1u64); + + let virt_start_addr = VirtAddr::new(segment.virtual_addr()); + let start_page: Page = Page::containing_address(virt_start_addr); + + for frame in PhysFrame::range_inclusive(start_frame, end_frame) { + let offset = frame - start_frame; + let page = start_page + offset; + log::info!("mapping {:?} to {:?}", page, frame); + } + + Ok(()) + } + + fn handle_tls_segment(&self, segment: ProgramHeader) -> Result<(), &'static str> { + todo!() + } +} + +pub fn load_kernel(bytes: &[u8]) -> Result<(), &'static str> { + let loader = Loader::new(bytes)?; + loader.load_segments()?; + + Err("unfinished implementation!") +} diff --git a/bootloader-lib/src/logger.rs b/bootloader-lib/src/logger.rs new file mode 100644 index 00000000..cc53ad43 --- /dev/null +++ b/bootloader-lib/src/logger.rs @@ -0,0 +1,155 @@ +use conquer_once::spin::OnceCell; +use core::{ + fmt::{self, Write}, + ptr, +}; +use font8x8::UnicodeFonts; +use spinning_top::Spinlock; + +pub static LOGGER: OnceCell = OnceCell::uninit(); + +pub struct LockedLogger(Spinlock); + +/// Additional vertical space between lines +const LINE_SPACING: usize = 2; +/// Additional vertical space between separate log messages +const LOG_SPACING: usize = 6; + +impl LockedLogger { + pub fn new(framebuffer: &'static mut [u8], info: FrameBufferInfo) -> Self { + LockedLogger(Spinlock::new(Logger::new(framebuffer, info))) + } + + pub unsafe fn force_unlock(&self) { + self.0.force_unlock(); + } +} + +impl log::Log for LockedLogger { + fn enabled(&self, _metadata: &log::Metadata) -> bool { + true + } + + fn log(&self, record: &log::Record) { + let mut logger = self.0.lock(); + writeln!(logger, "{}: {}", record.level(), record.args()).unwrap(); + logger.add_vspace(LOG_SPACING); + } + + fn flush(&self) {} +} + +pub struct Logger { + framebuffer: &'static mut [u8], + info: FrameBufferInfo, + x_pos: usize, + y_pos: usize, +} + +impl Logger { + pub fn new(framebuffer: &'static mut [u8], info: FrameBufferInfo) -> Self { + let mut logger = Self { + framebuffer, + info, + x_pos: 0, + y_pos: 0, + }; + logger.clear(); + logger + } + + fn newline(&mut self) { + self.y_pos += 8 + LINE_SPACING; + self.carriage_return() + } + + fn add_vspace(&mut self, space: usize) { + self.y_pos += space; + } + + fn carriage_return(&mut self) { + self.x_pos = 0; + } + + /// Erases all text on the screen + pub fn clear(&mut self) { + self.x_pos = 0; + self.y_pos = 0; + self.framebuffer.fill(0); + } + + fn width(&self) -> usize { + self.info.horizontal_resolution + } + + fn height(&self) -> usize { + self.info.vertical_resolution + } + + fn write_char(&mut self, c: char) { + match c { + '\n' => self.newline(), + '\r' => self.carriage_return(), + c => { + if self.x_pos >= self.width() { + self.newline(); + } + if self.y_pos >= self.height() { + self.clear(); + } + let rendered = font8x8::BASIC_FONTS + .get(c) + .expect("character not found in basic font"); + self.write_rendered_char(rendered); + } + } + } + + fn write_rendered_char(&mut self, rendered_char: [u8; 8]) { + for (y, byte) in rendered_char.iter().enumerate() { + for (x, bit) in (0..8).enumerate() { + let alpha = if *byte & (1 << bit) == 0 { 0 } else { 255 }; + self.write_pixel(self.x_pos + x, self.y_pos + y, alpha); + } + } + self.x_pos += 8; + } + + fn write_pixel(&mut self, x: usize, y: usize, intensity: u8) { + let pixel_offset = y * self.info.stride + x; + let (bytes_per_pixel, color) = match self.info.pixel_format { + PixelFormat::RGB => (4, [intensity, intensity, intensity / 2, 0]), + PixelFormat::BGR => (4, [intensity / 2, intensity, intensity, 0]), + }; + let byte_offset = pixel_offset * bytes_per_pixel; + self.framebuffer[byte_offset..(byte_offset + bytes_per_pixel)] + .copy_from_slice(&color[..bytes_per_pixel]); + let _ = unsafe { ptr::read_volatile(&self.framebuffer[byte_offset]) }; + } +} + +unsafe impl Send for Logger {} +unsafe impl Sync for Logger {} + +impl fmt::Write for Logger { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + self.write_char(c); + } + Ok(()) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct FrameBufferInfo { + pub horizontal_resolution: usize, + pub vertical_resolution: usize, + pub pixel_format: PixelFormat, + pub stride: usize, +} + +#[derive(Debug, Clone, Copy)] +pub enum PixelFormat { + RGB, + BGR, +} From 695c2102e0d4abc0b7ee1a4d5aac44f14cbec1b0 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 16 Jun 2020 16:37:02 +0200 Subject: [PATCH 007/174] Make uefi app use bootloader lib --- Cargo.toml | 9 +++++- src/main.rs | 85 +++++++++++++++++++++++++++-------------------------- 2 files changed, 52 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 832122ae..575784a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,10 +4,17 @@ version = "0.1.0" authors = ["Philipp Oppermann "] edition = "2018" +[workspace] +members = [ + "bootloader-lib", +] + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] uefi = "0.4.4" rlibc = "1.0.0" -uefi-services = "0.2.4" log = "0.4.8" + +[dependencies.bootloader-lib] +path = "bootloader-lib" diff --git a/src/main.rs b/src/main.rs index d50a26db..1dada90b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,64 +2,67 @@ #![no_main] #![feature(abi_efiapi)] -extern crate alloc; +static KERNEL: PageAligned<[u8; 137224]> = PageAligned(*include_bytes!( + "../../blog_os/post-01/target/x86_64-blog_os/debug/blog_os" +)); + +#[repr(align(4096))] +struct PageAligned(T); + extern crate rlibc; -use alloc::vec; -use core::mem; +use core::{mem, slice}; use uefi::{ prelude::{entry, Boot, Handle, ResultExt, Status, SystemTable}, - proto::console::gop::GraphicsOutput, - table::boot::MemoryDescriptor, + proto::console::gop::{GraphicsOutput, PixelFormat}, + table::boot::{MemoryDescriptor, MemoryType}, }; #[entry] fn efi_main(image: Handle, st: SystemTable) -> Status { - // Initialize utilities (logging, memory allocation...) - uefi_services::init(&st).expect_success("Failed to initialize utilities"); + init_logger(&st); + log::info!("Hello World from UEFI bootloader!"); - log::set_max_level(log::LevelFilter::Info); + let mmap_storage = { + let max_mmap_size = + st.boot_services().memory_map_size() + 8 * mem::size_of::(); + let ptr = st + .boot_services() + .allocate_pool(MemoryType::LOADER_DATA, max_mmap_size)? + .log(); + unsafe { slice::from_raw_parts_mut(ptr, max_mmap_size) } + }; - let stdout = st.stdout(); - stdout.reset(true).expect_success("failed to reset stdout"); - log::info!("Hello World from UEFI bootloader!"); + log::trace!("exiting boot services"); + let (_st, memory_map) = st + .exit_boot_services(image, mmap_storage) + .expect_success("Failed to exit boot services"); + + bootloader_lib::load_kernel(&KERNEL.0) +} - let boot_services = st.boot_services(); - let gop = boot_services +fn init_logger(st: &SystemTable) { + let gop = st + .boot_services() .locate_protocol::() .expect_success("failed to locate gop"); let gop = unsafe { &mut *gop.get() }; - // print available video modes - for mode in gop.modes().map(|c| c.unwrap()) { - log::trace!("Mode: {:x?}", mode.info()); - } - let mode_info = gop.current_mode_info(); - let (width, height) = mode_info.resolution(); - log::info!("Active video Mode: {:x?}", mode_info); - let mut framebuffer = gop.frame_buffer(); - - let max_mmap_size = - st.boot_services().memory_map_size() + 8 * mem::size_of::(); - let mut mmap_storage = vec![0; max_mmap_size]; - - log::trace!("exiting boot services"); - - st.exit_boot_services(image, &mut mmap_storage) - .expect_success("Failed to exit boot services"); - - let fill_color = [0x00u8, 0x00, 0xff, 0x00]; - for col in 0..width { - for row in 0..height { - let index = row * mode_info.stride() + col; - let byte_index = index * 4; - unsafe { - framebuffer.write_value(byte_index, fill_color); + let slice = unsafe { slice::from_raw_parts_mut(framebuffer.as_mut_ptr(), framebuffer.size()) }; + let info = bootloader_lib::FrameBufferInfo { + horizontal_resolution: mode_info.resolution().0, + vertical_resolution: mode_info.resolution().1, + pixel_format: match mode_info.pixel_format() { + PixelFormat::RGB => bootloader_lib::PixelFormat::BGR, + PixelFormat::BGR => bootloader_lib::PixelFormat::BGR, + PixelFormat::Bitmask | PixelFormat::BltOnly => { + panic!("Bitmask and BltOnly framebuffers are not supported") } - } - } + }, + stride: mode_info.stride(), + }; - loop {} + bootloader_lib::init_logger(slice, info); } From 3b337b5ce20ea460b3ee5766590a31088e1c65a0 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 16 Jun 2020 16:38:29 +0200 Subject: [PATCH 008/174] Add a runner executable for supporting `cargo x run` --- .cargo/config.toml | 3 ++ Cargo.toml | 1 + runner/Cargo.toml | 10 +++++++ runner/src/main.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 runner/Cargo.toml create mode 100644 runner/src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index f54e14a3..d15a2d68 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,8 @@ [build] target = "x86_64-unknown-uefi" +[target.x86_64-unknown-uefi] +runner = "cargo run --manifest-path runner/Cargo.toml --target x86_64-unknown-linux-gnu" + [alias] x = "-Z build-std=core,alloc" diff --git a/Cargo.toml b/Cargo.toml index 575784a8..ff1176a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" [workspace] members = [ "bootloader-lib", + "runner", ] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/runner/Cargo.toml b/runner/Cargo.toml new file mode 100644 index 00000000..976b96df --- /dev/null +++ b/runner/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "runner" +version = "0.1.0" +authors = ["Philipp Oppermann "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.28" diff --git a/runner/src/main.rs b/runner/src/main.rs new file mode 100644 index 00000000..e817ddc6 --- /dev/null +++ b/runner/src/main.rs @@ -0,0 +1,68 @@ +use std::{ + env, fs, + path::Path, + process::{exit, Command}, +}; + +type ExitCode = i32; + +fn main() { + let args: Vec = env::args().collect(); + if args.len() > 2 { + eprintln!("too many arguments passed: {:?}", args); + exit(1); + } + if args.len() < 2 { + eprintln!("not enough arguments passed: {:?}", args); + exit(1); + } + let file_path = Path::new(&args[1]); + if !file_path.exists() { + eprintln!("file does not exist: {:?}", file_path); + exit(1); + } + + match runner(file_path) { + Err(err) => { + eprintln!("ERROR: {:?}", err); + exit(1); + } + Ok(Some(exit_code)) => exit(exit_code), + Ok(None) => {} + } +} + +fn runner(file_path: &Path) -> anyhow::Result> { + let uefi_partition_dir = Path::new("target/uefi_esp"); + let boot_dir = uefi_partition_dir.join("EFI").join("BOOT"); + fs::create_dir_all(&boot_dir)?; + fs::copy(file_path, boot_dir.join("BootX64.efi"))?; + + let ovmf_code = Path::new("ovmf/OVMF_CODE.fd").canonicalize()?; + let ovmf_vars = Path::new("ovmf/OVMF_VARS.fd").canonicalize()?; + + let mut qemu = Command::new("qemu-system-x86_64"); + qemu.arg("-drive").arg(format!( + "if=pflash,format=raw,file={},readonly=on", + ovmf_code.display() + )); + qemu.arg("-drive").arg(format!( + "if=pflash,format=raw,file={},readonly=on", + ovmf_vars.display() + )); + qemu.arg("-drive").arg(format!( + "format=raw,file=fat:rw:{}", + uefi_partition_dir.canonicalize()?.display() + )); + qemu.arg("-s"); + qemu.arg("-nodefaults"); + qemu.arg("-vga").arg("std"); + println!("{:?}", qemu); + let exit_status = qemu.status()?; + let ret = if exit_status.success() { + None + } else { + exit_status.code() + }; + Ok(ret) +} From 52611f47dee87e3de8909d8e5957dcc50a638a5a Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 16 Jun 2020 16:37:23 +0200 Subject: [PATCH 009/174] Update Cargo.lock --- Cargo.lock | 98 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a7d376a..c7114b04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,11 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "anyhow" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" + [[package]] name = "bit_field" version = "0.9.0" @@ -18,12 +24,54 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bootloader-lib" +version = "0.1.0" +dependencies = [ + "conquer-once", + "font8x8", + "log", + "spinning_top", + "x86_64", + "xmas-elf", +] + [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "conquer-once" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f7644600a548ecad74e4a918392af1798f7dd045be610be3203b9e129b4f98f" +dependencies = [ + "conquer-util", +] + +[[package]] +name = "conquer-util" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "654fb2472cc369d311c547103a1fa81d467bef370ae7a0680f65939895b1182a" + +[[package]] +name = "font8x8" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44226c40489fb1d602344a1d8f1b544570c3435e396dda1eda7b5ef010d8f1be" + +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.8" @@ -57,6 +105,28 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" +[[package]] +name = "runner" +version = "0.1.0" +dependencies = [ + "anyhow", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "spinning_top" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d801a3a53bcf5071f85fef8d5cab9e5f638fc5580a37e6eb7aba4b37438d24" +dependencies = [ + "lock_api", +] + [[package]] name = "syn" version = "1.0.31" @@ -100,25 +170,14 @@ dependencies = [ "syn", ] -[[package]] -name = "uefi-services" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a985e3dd3e8ce234af981948ff1138e72936af03b04a90b082f9d8a6f35ad37" -dependencies = [ - "log", - "uefi", - "x86_64", -] - [[package]] name = "uefi-test" version = "0.1.0" dependencies = [ + "bootloader-lib", "log", "rlibc", "uefi", - "uefi-services", ] [[package]] @@ -136,3 +195,18 @@ dependencies = [ "bit_field 0.9.0", "bitflags", ] + +[[package]] +name = "xmas-elf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e74de9a366f6ab8c405fa6b371d9ac24943921fa14b3d64afcb202065c405f11" +dependencies = [ + "zero", +] + +[[package]] +name = "zero" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f1bc8a6b2005884962297587045002d8cfb8dcec9db332f4ca216ddc5de82c5" From 959a75d1d8812f7b8f49a1b1e31a5a470506621f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 16 Jun 2020 17:24:58 +0200 Subject: [PATCH 010/174] Enable overflow checks also in release mode --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index ff1176a1..424b196e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,6 @@ log = "0.4.8" [dependencies.bootloader-lib] path = "bootloader-lib" + +[profile.release] +overflow-checks = true From 4960b20736650212f2cf5d34f977e840acb8f58b Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 16 Jun 2020 17:25:14 +0200 Subject: [PATCH 011/174] Set logging level to minimum (all messages) --- bootloader-lib/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootloader-lib/src/lib.rs b/bootloader-lib/src/lib.rs index fff33d9a..d8708084 100644 --- a/bootloader-lib/src/lib.rs +++ b/bootloader-lib/src/lib.rs @@ -10,7 +10,7 @@ mod logger; pub fn init_logger(framebuffer: &'static mut [u8], info: FrameBufferInfo) { let logger = logger::LOGGER.get_or_init(move || logger::LockedLogger::new(framebuffer, info)); log::set_logger(logger).expect("logger already set"); - log::set_max_level(log::LevelFilter::Info); + log::set_max_level(log::LevelFilter::Trace); } pub fn load_kernel(kernel: &'static [u8]) -> ! { From d89c56e2aaa92694107e984671ff343ef932783b Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 16 Jun 2020 17:25:33 +0200 Subject: [PATCH 012/174] Fix overflow bug in logger --- bootloader-lib/src/logger.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootloader-lib/src/logger.rs b/bootloader-lib/src/logger.rs index cc53ad43..18ad66f1 100644 --- a/bootloader-lib/src/logger.rs +++ b/bootloader-lib/src/logger.rs @@ -94,7 +94,7 @@ impl Logger { if self.x_pos >= self.width() { self.newline(); } - if self.y_pos >= self.height() { + if self.y_pos >= (self.height() - 8) { self.clear(); } let rendered = font8x8::BASIC_FONTS From ac42e11bc83801f5cc2b084d46ad345e2db70749 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 18 Jul 2020 14:59:53 +0200 Subject: [PATCH 013/174] Start using x86_64 crate to map kernel ELF in new page table --- Cargo.lock | 1 + Cargo.toml | 1 + bootloader-lib/src/lib.rs | 12 +++-- bootloader-lib/src/load_kernel.rs | 85 +++++++++++++++++++++++-------- src/main.rs | 35 ++++++++++++- 5 files changed, 108 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7114b04..6160713a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,6 +178,7 @@ dependencies = [ "log", "rlibc", "uefi", + "x86_64", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 424b196e..3f42112b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ members = [ uefi = "0.4.4" rlibc = "1.0.0" log = "0.4.8" +x86_64 = "0.11.0" [dependencies.bootloader-lib] path = "bootloader-lib" diff --git a/bootloader-lib/src/lib.rs b/bootloader-lib/src/lib.rs index d8708084..71d3f906 100644 --- a/bootloader-lib/src/lib.rs +++ b/bootloader-lib/src/lib.rs @@ -1,8 +1,9 @@ #![feature(slice_fill)] #![no_std] -pub use logger::{FrameBufferInfo, PixelFormat}; use core::panic::PanicInfo; +pub use logger::{FrameBufferInfo, PixelFormat}; +use x86_64::structures::paging::{FrameAllocator, MapperAllSizes, Size4KiB}; mod load_kernel; mod logger; @@ -13,9 +14,12 @@ pub fn init_logger(framebuffer: &'static mut [u8], info: FrameBufferInfo) { log::set_max_level(log::LevelFilter::Trace); } -pub fn load_kernel(kernel: &'static [u8]) -> ! { - load_kernel::load_kernel(kernel).expect("Failed to parse kernel"); - loop {} +pub fn load_kernel( + kernel: &'static [u8], + page_table: &mut impl MapperAllSizes, + frame_allocator: &mut impl FrameAllocator, +) { + load_kernel::load_kernel(kernel, page_table, frame_allocator).expect("Failed to parse kernel"); } #[panic_handler] diff --git a/bootloader-lib/src/load_kernel.rs b/bootloader-lib/src/load_kernel.rs index 68313d1c..c6e3c226 100644 --- a/bootloader-lib/src/load_kernel.rs +++ b/bootloader-lib/src/load_kernel.rs @@ -1,5 +1,7 @@ use x86_64::{ - structures::paging::{Page, PhysFrame}, + structures::paging::{ + FrameAllocator, MapperAllSizes, Page, PageTableFlags as Flags, PhysFrame, Size4KiB, + }, PhysAddr, VirtAddr, }; use xmas_elf::{ @@ -10,35 +12,53 @@ use xmas_elf::{ const PAGE_SIZE: u64 = 4096; -struct Loader<'a> { - bytes: &'a [u8], +struct Loader<'a, M, F> { elf_file: ElfFile<'a>, + inner: Inner<'a, M, F>, } -impl<'a> Loader<'a> { - fn new(bytes: &'a [u8]) -> Result { - let elf_file = ElfFile::new(bytes)?; - header::sanity_check(&elf_file)?; - let loader = Loader { bytes, elf_file }; +struct Inner<'a, M, F> { + kernel_offset: PhysAddr, + page_table: &'a mut M, + frame_allocator: &'a mut F, +} +impl<'a, M, F> Loader<'a, M, F> +where + M: MapperAllSizes, + F: FrameAllocator, +{ + fn new( + bytes: &'a [u8], + page_table: &'a mut M, + frame_allocator: &'a mut F, + ) -> Result { log::info!("Elf file loaded at {:#p}", bytes); - if !loader.kernel_offset().is_aligned(PAGE_SIZE) { + let kernel_offset = PhysAddr::new(&bytes[0] as *const u8 as u64); + if !kernel_offset.is_aligned(PAGE_SIZE) { return Err("Loaded kernel ELF file is not sufficiently aligned"); } - Ok(loader) - } + let elf_file = ElfFile::new(bytes)?; + header::sanity_check(&elf_file)?; + let loader = Loader { + elf_file, + inner: Inner { + kernel_offset, + page_table, + frame_allocator, + }, + }; - fn kernel_offset(&self) -> PhysAddr { - PhysAddr::new(&self.bytes[0] as *const u8 as u64) + Ok(loader) } - fn load_segments(&self) -> Result<(), &'static str> { + fn load_segments(&mut self) -> Result<(), &'static str> { for program_header in self.elf_file.program_iter() { program::sanity_check(program_header, &self.elf_file)?; match program_header.get_type()? { - Type::Load => self.handle_load_segment(program_header)?, - Type::Tls => self.handle_tls_segment(program_header)?, + Type::Load => self.inner.handle_load_segment(program_header)?, + Type::Tls => self.inner.handle_tls_segment(program_header)?, Type::Null | Type::Dynamic | Type::Interp @@ -52,11 +72,17 @@ impl<'a> Loader<'a> { } Ok(()) } +} - fn handle_load_segment(&self, segment: ProgramHeader) -> Result<(), &'static str> { +impl<'a, M, F> Inner<'a, M, F> +where + M: MapperAllSizes, + F: FrameAllocator, +{ + fn handle_load_segment(&mut self, segment: ProgramHeader) -> Result<(), &'static str> { log::info!("Handling Segment: {:x?}", segment); - let phys_start_addr = self.kernel_offset() + segment.offset(); + let phys_start_addr = self.kernel_offset + segment.offset(); let start_frame: PhysFrame = PhysFrame::containing_address(phys_start_addr); let end_frame: PhysFrame = PhysFrame::containing_address(phys_start_addr + segment.file_size() - 1u64); @@ -64,10 +90,23 @@ impl<'a> Loader<'a> { let virt_start_addr = VirtAddr::new(segment.virtual_addr()); let start_page: Page = Page::containing_address(virt_start_addr); + let mut flags = Flags::PRESENT; + if !segment.flags().is_execute() { + flags |= Flags::NO_EXECUTE; + } + if segment.flags().is_write() { + flags |= Flags::WRITABLE; + } + for frame in PhysFrame::range_inclusive(start_frame, end_frame) { let offset = frame - start_frame; let page = start_page + offset; - log::info!("mapping {:?} to {:?}", page, frame); + unsafe { + self.page_table + .map_to(page, frame, flags, self.frame_allocator) + .map_err(|_err| "map_to failed")? + } + .flush(); } Ok(()) @@ -78,8 +117,12 @@ impl<'a> Loader<'a> { } } -pub fn load_kernel(bytes: &[u8]) -> Result<(), &'static str> { - let loader = Loader::new(bytes)?; +pub fn load_kernel( + bytes: &[u8], + page_table: &mut impl MapperAllSizes, + frame_allocator: &mut impl FrameAllocator, +) -> Result<(), &'static str> { + let mut loader = Loader::new(bytes, page_table, frame_allocator)?; loader.load_segments()?; Err("unfinished implementation!") diff --git a/src/main.rs b/src/main.rs index 1dada90b..7099db0b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,10 @@ use uefi::{ proto::console::gop::{GraphicsOutput, PixelFormat}, table::boot::{MemoryDescriptor, MemoryType}, }; +use x86_64::{ + structures::paging::{OffsetPageTable, PageTable, PhysFrame}, + VirtAddr, +}; #[entry] fn efi_main(image: Handle, st: SystemTable) -> Status { @@ -38,7 +42,36 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { .exit_boot_services(image, mmap_storage) .expect_success("Failed to exit boot services"); - bootloader_lib::load_kernel(&KERNEL.0) + for memory_region in memory_map { + let addr = 0x7c42000; + if memory_region.phys_start <= addr + && addr < (memory_region.phys_start + memory_region.page_count * 4096) + { + log::trace!("{:#x?}", memory_region); + } + } + + let mut frame_allocator = (); + + let mut page_table = { + // UEFI identity maps all memory, so physical memory offset is 0 + let phys_offset = VirtAddr::new(0); + // get an unused frame for new level 4 page table + let frame: PhysFrame = frame_allocator.allocate_frame().expect("no unused frames"); + // get the corresponding virtual address + let addr = phys_offset + frame.start_address().as_u64(); + // initialize a new page table + let ptr = addr.as_mut_ptr(); + unsafe { *ptr = PageTable::new() }; + let level_4_table = unsafe { &mut *ptr }; + unsafe { OffsetPageTable::new(level_4_table, phys_offset) } + }; + + bootloader_lib::load_kernel(&KERNEL.0, &mut page_table, &mut frame_allocator); + + loop { + unsafe { asm!("cli; hlt") }; + } } fn init_logger(st: &SystemTable) { From 3b59bab3f63dc183552c1728655bb61b54dd60ce Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 18 Jul 2020 15:00:23 +0200 Subject: [PATCH 014/174] Start QEMU with `-d int` and `--no-reboot` --- runner/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runner/src/main.rs b/runner/src/main.rs index e817ddc6..3a41d08e 100644 --- a/runner/src/main.rs +++ b/runner/src/main.rs @@ -57,6 +57,8 @@ fn runner(file_path: &Path) -> anyhow::Result> { qemu.arg("-s"); qemu.arg("-nodefaults"); qemu.arg("-vga").arg("std"); + qemu.arg("-d").arg("int"); + qemu.arg("--no-reboot"); println!("{:?}", qemu); let exit_status = qemu.status()?; let ret = if exit_status.success() { From eff86617c739dcc11ccb35911cfdb6b7eadfb014 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 19 Jul 2020 22:24:31 +0200 Subject: [PATCH 015/174] Add FIXME note about handling bss sections --- bootloader-lib/src/load_kernel.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bootloader-lib/src/load_kernel.rs b/bootloader-lib/src/load_kernel.rs index c6e3c226..2a8e4c62 100644 --- a/bootloader-lib/src/load_kernel.rs +++ b/bootloader-lib/src/load_kernel.rs @@ -109,6 +109,8 @@ where .flush(); } + // FIXME: Handle .bss section (mem_size > file_size) + Ok(()) } From e6a65bfb9c100dbd9154d24f1db7e19e01889a01 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 19 Jul 2020 22:25:24 +0200 Subject: [PATCH 016/174] Implement a frame allocator --- src/main.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7099db0b..fe4d0a33 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,13 +15,15 @@ use core::{mem, slice}; use uefi::{ prelude::{entry, Boot, Handle, ResultExt, Status, SystemTable}, proto::console::gop::{GraphicsOutput, PixelFormat}, - table::boot::{MemoryDescriptor, MemoryType}, + table::boot::{MemoryDescriptor, MemoryMapIter, MemoryType}, }; use x86_64::{ - structures::paging::{OffsetPageTable, PageTable, PhysFrame}, - VirtAddr, + structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB}, + PhysAddr, VirtAddr, }; +const PAGE_SIZE: u64 = 4096; + #[entry] fn efi_main(image: Handle, st: SystemTable) -> Status { init_logger(&st); @@ -42,16 +44,7 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { .exit_boot_services(image, mmap_storage) .expect_success("Failed to exit boot services"); - for memory_region in memory_map { - let addr = 0x7c42000; - if memory_region.phys_start <= addr - && addr < (memory_region.phys_start + memory_region.page_count * 4096) - { - log::trace!("{:#x?}", memory_region); - } - } - - let mut frame_allocator = (); + let mut frame_allocator = UefiFrameAllocator::new(memory_map); let mut page_table = { // UEFI identity maps all memory, so physical memory offset is 0 @@ -99,3 +92,61 @@ fn init_logger(st: &SystemTable) { bootloader_lib::init_logger(slice, info); } + +struct UefiFrameAllocator<'a> { + memory_map: MemoryMapIter<'a>, + current_descriptor: Option<&'a MemoryDescriptor>, + next_frame: PhysFrame, +} + +impl<'a> UefiFrameAllocator<'a> { + fn new(memory_map: MemoryMapIter<'a>) -> Self { + Self { + memory_map, + current_descriptor: None, + next_frame: PhysFrame::containing_address(PhysAddr::new(0)), + } + } + + fn allocate_frame_from_descriptor( + &mut self, + descriptor: &MemoryDescriptor, + ) -> Option { + let start_addr = PhysAddr::new(descriptor.phys_start); + let start_frame: PhysFrame = PhysFrame::containing_address(start_addr.align_up(PAGE_SIZE)); + let end_frame = start_frame + descriptor.page_count; + if self.next_frame < end_frame { + let ret = self.next_frame; + self.next_frame += 1; + Some(ret) + } else { + None + } + } +} + +unsafe impl FrameAllocator for UefiFrameAllocator<'_> { + fn allocate_frame(&mut self) -> Option> { + if let Some(current_descriptor) = self.current_descriptor { + match self.allocate_frame_from_descriptor(current_descriptor) { + Some(frame) => return Some(frame), + None => { + self.current_descriptor = None; + } + } + } + + // find next suitable descriptor + while let Some(descriptor) = self.memory_map.next() { + if descriptor.ty != MemoryType::CONVENTIONAL { + continue; + } + if let Some(frame) = self.allocate_frame_from_descriptor(descriptor) { + self.current_descriptor = Some(descriptor); + return Some(frame); + } + } + + None + } +} From 7377097ddbed4c9a996f9d1f0610f5b04a4b1d65 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 19 Jul 2020 22:25:54 +0200 Subject: [PATCH 017/174] No longer error in `load_kernel` --- bootloader-lib/src/load_kernel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootloader-lib/src/load_kernel.rs b/bootloader-lib/src/load_kernel.rs index 2a8e4c62..88d146e0 100644 --- a/bootloader-lib/src/load_kernel.rs +++ b/bootloader-lib/src/load_kernel.rs @@ -127,5 +127,5 @@ pub fn load_kernel( let mut loader = Loader::new(bytes, page_table, frame_allocator)?; loader.load_segments()?; - Err("unfinished implementation!") + Ok(()) } From 6ea0afd521c937e6854bf5e8d6b6bc89cc90594b Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 13:14:02 +0200 Subject: [PATCH 018/174] Fix, improve, and document kernel loading code We operate on a separate page table, so we can't access the pages that we just mapped. Instead, we can rely on the fact that all memory is identity-mapped by the UEFI firmware, which gives us an alternative way to access frame data. --- bootloader-lib/src/load_kernel.rs | 144 ++++++++++++++++++++++++++++-- 1 file changed, 135 insertions(+), 9 deletions(-) diff --git a/bootloader-lib/src/load_kernel.rs b/bootloader-lib/src/load_kernel.rs index 88d146e0..dcd7a58c 100644 --- a/bootloader-lib/src/load_kernel.rs +++ b/bootloader-lib/src/load_kernel.rs @@ -1,6 +1,8 @@ use x86_64::{ + align_up, structures::paging::{ - FrameAllocator, MapperAllSizes, Page, PageTableFlags as Flags, PhysFrame, Size4KiB, + FrameAllocator, MapperAllSizes, Page, PageSize, PageTableFlags as Flags, PhysFrame, + Size4KiB, }, PhysAddr, VirtAddr, }; @@ -90,31 +92,155 @@ where let virt_start_addr = VirtAddr::new(segment.virtual_addr()); let start_page: Page = Page::containing_address(virt_start_addr); - let mut flags = Flags::PRESENT; + let mut segment_flags = Flags::PRESENT; if !segment.flags().is_execute() { - flags |= Flags::NO_EXECUTE; + segment_flags |= Flags::NO_EXECUTE; } if segment.flags().is_write() { - flags |= Flags::WRITABLE; + segment_flags |= Flags::WRITABLE; } + // map all frames of the segment at the desired virtual address for frame in PhysFrame::range_inclusive(start_frame, end_frame) { let offset = frame - start_frame; let page = start_page + offset; - unsafe { + let flusher = unsafe { self.page_table - .map_to(page, frame, flags, self.frame_allocator) + .map_to(page, frame, segment_flags, self.frame_allocator) .map_err(|_err| "map_to failed")? + }; + // we operate on an inactive page table, so there's no need to flush anything + flusher.ignore(); + } + + // Handle .bss section (mem_size > file_size) + if segment.mem_size() > segment.file_size() { + // .bss section (or similar), which needs to be mapped and zeroed + self.handle_bss_section(&segment, segment_flags)?; + } + + Ok(()) + } + + fn handle_bss_section( + &mut self, + segment: &ProgramHeader, + segment_flags: Flags, + ) -> Result<(), &'static str> { + log::info!("Mapping bss section"); + + let virt_start_addr = VirtAddr::new(segment.virtual_addr()); + let phys_start_addr = self.kernel_offset + segment.offset(); + let mem_size = segment.mem_size(); + let file_size = segment.file_size(); + + // calculate virual memory region that must be zeroed + let zero_start = virt_start_addr + file_size; + let zero_end = virt_start_addr + mem_size; + + // a type alias that helps in efficiently clearing a page + type PageArray = [u64; Size4KiB::SIZE as usize / 8]; + const ZERO_ARRAY: PageArray = [0; Size4KiB::SIZE as usize / 8]; + + // In some cases, `zero_start` might not be page-aligned. This requires some + // special treatment because we can't safely zero a frame of the original file. + let data_bytes_before_zero = zero_start.as_u64() & 0xfff; + if data_bytes_before_zero != 0 { + // The last non-bss frame of the segment consists partly of data and partly of bss + // memory, which must be zeroed. Unfortunately, the file representation might have + // reused the part of the frame that should be zeroed to store the next segment. This + // means that we can't simply overwrite that part with zeroes, as we might overwrite + // other data this way. + // + // Example: + // + // XXXXXXXXXXXXXXX000000YYYYYYY000ZZZZZZZZZZZ virtual memory (XYZ are data) + // |·············| /·····/ /·········/ + // |·············| ___/·····/ /·········/ + // |·············|/·····/‾‾‾ /·········/ + // |·············||·····|/·̅·̅·̅·̅·̅·····/‾‾‾‾ + // XXXXXXXXXXXXXXXYYYYYYYZZZZZZZZZZZ file memory (zeros are not saved) + // ' ' ' ' ' + // The areas filled with dots (`·`) indicate a mapping between virtual and file + // memory. We see that the data regions `X`, `Y`, `Z` have a valid mapping, while + // the regions that are initialized with 0 have not. + // + // The ticks (`'`) below the file memory line indicate the start of a new frame. We + // see that the last frames of the `X` and `Y` regions in the file are followed + // by the bytes of the next region. So we can't zero these parts of the frame + // because they are needed by other memory regions. + // + // To solve this problem, we need to allocate a new frame for the last segment page + // and copy all data content of the original frame over. Afterwards, we can zero + // the remaining part of the frame since the frame is no longer shared with other + // segments now. + + // calculate the frame where the last segment page is mapped + let orig_frame = PhysFrame::containing_address(phys_start_addr + file_size - 1); + // allocate a new frame to replace `orig_frame` + let new_frame = self.frame_allocator.allocate_frame().unwrap(); + + // zero new frame, utilizing that it's identity-mapped + { + let new_frame_ptr = new_frame.start_address().as_u64() as *mut PageArray; + unsafe { new_frame_ptr.write(ZERO_ARRAY) }; + } + + // copy the data bytes from orig_frame to new_frame + { + log::info!("Copy contents"); + let orig_bytes_ptr = orig_frame.start_address().as_u64() as *mut u8; + let new_bytes_ptr = new_frame.start_address().as_u64() as *mut u8; + + for offset in 0..(data_bytes_before_zero as isize) { + unsafe { + let orig_byte = orig_bytes_ptr.offset(offset).read(); + new_bytes_ptr.offset(offset).write(orig_byte); + } + } + } + + // remap last page from orig_frame to `new_frame` + log::info!("Remap last page"); + let last_page = Page::containing_address(virt_start_addr + file_size - 1); + self.page_table + .unmap(last_page.clone()) + .map_err(|_err| "Failed to unmap last segment page because of bss memory")?; + let flusher = unsafe { + self.page_table + .map_to(last_page, new_frame, segment_flags, self.frame_allocator) } - .flush(); + .map_err(|_err| "Failed to remap last segment page because of bss memory")?; + // we operate on an inactive page table, so we don't need to flush our changes + flusher.ignore(); } - // FIXME: Handle .bss section (mem_size > file_size) + // map additional frames for `.bss` memory that is not present in source file + let start_page: Page = + Page::containing_address(VirtAddr::new(align_up(zero_start.as_u64(), Size4KiB::SIZE))); + let end_page = Page::containing_address(zero_end); + for page in Page::range_inclusive(start_page, end_page) { + // allocate a new unused frame + let frame = self.frame_allocator.allocate_frame().unwrap(); + + // zero frame, utilizing identity-mapping + let frame_ptr = frame.start_address().as_u64() as *mut PageArray; + unsafe { frame_ptr.write(ZERO_ARRAY) }; + + // map frame + let flusher = unsafe { + self.page_table + .map_to(page, frame, segment_flags, self.frame_allocator) + .map_err(|_err| "Failed to map new frame for bss memory")? + }; + // we operate on an inactive page table, so we don't need to flush our changes + flusher.ignore(); + } Ok(()) } - fn handle_tls_segment(&self, segment: ProgramHeader) -> Result<(), &'static str> { + fn handle_tls_segment(&mut self, segment: ProgramHeader) -> Result<(), &'static str> { todo!() } } From 1825f1b2a2f918c1f8eceae97c81e4d9ac130d80 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 13:14:20 +0200 Subject: [PATCH 019/174] Return the entry point address from `load_kernel` --- bootloader-lib/src/lib.rs | 9 ++++++--- bootloader-lib/src/load_kernel.rs | 8 ++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/bootloader-lib/src/lib.rs b/bootloader-lib/src/lib.rs index 71d3f906..3c766bcb 100644 --- a/bootloader-lib/src/lib.rs +++ b/bootloader-lib/src/lib.rs @@ -3,7 +3,10 @@ use core::panic::PanicInfo; pub use logger::{FrameBufferInfo, PixelFormat}; -use x86_64::structures::paging::{FrameAllocator, MapperAllSizes, Size4KiB}; +use x86_64::{ + structures::paging::{FrameAllocator, MapperAllSizes, Size4KiB}, + VirtAddr, +}; mod load_kernel; mod logger; @@ -18,8 +21,8 @@ pub fn load_kernel( kernel: &'static [u8], page_table: &mut impl MapperAllSizes, frame_allocator: &mut impl FrameAllocator, -) { - load_kernel::load_kernel(kernel, page_table, frame_allocator).expect("Failed to parse kernel"); +) -> VirtAddr { + load_kernel::load_kernel(kernel, page_table, frame_allocator).expect("Failed to parse kernel") } #[panic_handler] diff --git a/bootloader-lib/src/load_kernel.rs b/bootloader-lib/src/load_kernel.rs index dcd7a58c..14002b93 100644 --- a/bootloader-lib/src/load_kernel.rs +++ b/bootloader-lib/src/load_kernel.rs @@ -74,6 +74,10 @@ where } Ok(()) } + + fn entry_point(&self) -> VirtAddr { + VirtAddr::new(self.elf_file.header.pt2.entry_point()) + } } impl<'a, M, F> Inner<'a, M, F> @@ -249,9 +253,9 @@ pub fn load_kernel( bytes: &[u8], page_table: &mut impl MapperAllSizes, frame_allocator: &mut impl FrameAllocator, -) -> Result<(), &'static str> { +) -> Result { let mut loader = Loader::new(bytes, page_table, frame_allocator)?; loader.load_segments()?; - Ok(()) + Ok(loader.entry_point()) } From 282487296964f52cd110551fe387d05245f3bc8c Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 13:16:37 +0200 Subject: [PATCH 020/174] Deny unsafe_op_in_unsafe_fn lint to make things safer --- bootloader-lib/src/lib.rs | 4 +++- bootloader-lib/src/logger.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bootloader-lib/src/lib.rs b/bootloader-lib/src/lib.rs index 3c766bcb..5f4ded15 100644 --- a/bootloader-lib/src/lib.rs +++ b/bootloader-lib/src/lib.rs @@ -1,5 +1,7 @@ -#![feature(slice_fill)] #![no_std] +#![feature(slice_fill)] +#![feature(unsafe_block_in_unsafe_fn)] +#![deny(unsafe_op_in_unsafe_fn)] use core::panic::PanicInfo; pub use logger::{FrameBufferInfo, PixelFormat}; diff --git a/bootloader-lib/src/logger.rs b/bootloader-lib/src/logger.rs index 18ad66f1..4346195b 100644 --- a/bootloader-lib/src/logger.rs +++ b/bootloader-lib/src/logger.rs @@ -21,7 +21,7 @@ impl LockedLogger { } pub unsafe fn force_unlock(&self) { - self.0.force_unlock(); + unsafe { self.0.force_unlock() }; } } From 85bbec4e7a6395851f1e600a4f48ed68ab9cd71c Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 13:16:53 +0200 Subject: [PATCH 021/174] Halt cpu when panicking --- bootloader-lib/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bootloader-lib/src/lib.rs b/bootloader-lib/src/lib.rs index 5f4ded15..da20bd26 100644 --- a/bootloader-lib/src/lib.rs +++ b/bootloader-lib/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![feature(slice_fill)] +#![feature(asm)] #![feature(unsafe_block_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)] @@ -31,5 +32,7 @@ pub fn load_kernel( fn panic(info: &PanicInfo) -> ! { unsafe { logger::LOGGER.get().map(|l| l.force_unlock()) }; log::error!("{}", info); - loop {} + loop { + unsafe { asm!("cli; hlt") }; + } } From adc0b6aa3ac52aa18483bb0edbd2ec41f92a0215 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 13:18:54 +0200 Subject: [PATCH 022/174] Create a kernel stack and do the context switch to the kernel --- src/main.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index fe4d0a33..d732ea90 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,9 @@ #![no_std] #![no_main] #![feature(abi_efiapi)] +#![feature(asm)] +#![feature(unsafe_block_in_unsafe_fn)] +#![deny(unsafe_op_in_unsafe_fn)] static KERNEL: PageAligned<[u8; 137224]> = PageAligned(*include_bytes!( "../../blog_os/post-01/target/x86_64-blog_os/debug/blog_os" @@ -18,7 +21,11 @@ use uefi::{ table::boot::{MemoryDescriptor, MemoryMapIter, MemoryType}, }; use x86_64::{ - structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB}, + registers, + structures::paging::{ + FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PageTableFlags, PhysFrame, + Size4KiB, + }, PhysAddr, VirtAddr, }; @@ -26,8 +33,9 @@ const PAGE_SIZE: u64 = 4096; #[entry] fn efi_main(image: Handle, st: SystemTable) -> Status { - init_logger(&st); + let framebuffer_addr = init_logger(&st); log::info!("Hello World from UEFI bootloader!"); + log::info!("Using framebuffer at {:#x}", framebuffer_addr); let mmap_storage = { let max_mmap_size = @@ -46,28 +54,91 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { let mut frame_allocator = UefiFrameAllocator::new(memory_map); - let mut page_table = { + let (mut page_table, level_4_frame) = { // UEFI identity maps all memory, so physical memory offset is 0 let phys_offset = VirtAddr::new(0); // get an unused frame for new level 4 page table let frame: PhysFrame = frame_allocator.allocate_frame().expect("no unused frames"); + log::info!("New page table at: {:#?}", &frame); // get the corresponding virtual address let addr = phys_offset + frame.start_address().as_u64(); // initialize a new page table let ptr = addr.as_mut_ptr(); unsafe { *ptr = PageTable::new() }; let level_4_table = unsafe { &mut *ptr }; - unsafe { OffsetPageTable::new(level_4_table, phys_offset) } + ( + unsafe { OffsetPageTable::new(level_4_table, phys_offset) }, + frame, + ) }; + log::info!("New page table at: {:?}", level_4_frame); + + let entry_point = bootloader_lib::load_kernel(&KERNEL.0, &mut page_table, &mut frame_allocator); + log::info!("Entry point at: {:#x}", entry_point.as_u64()); + + // create a stack + let stack_start: Page = Page::containing_address(VirtAddr::new(0xfff00000000)); + let stack_end = stack_start + 20; + for page in Page::range(stack_start, stack_end) { + let frame = frame_allocator + .allocate_frame() + .expect("frame allocation failed when mapping a kernel stack"); + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + unsafe { page_table.map_to(page, frame, flags, &mut frame_allocator) } + .unwrap() + .flush(); + } - bootloader_lib::load_kernel(&KERNEL.0, &mut page_table, &mut frame_allocator); + let addresses = Addresses { + page_table: level_4_frame, + stack_top: stack_end.start_address(), + entry_point, + framebuffer_addr, + }; + unsafe { + context_switch(addresses, page_table, frame_allocator); + } +} - loop { - unsafe { asm!("cli; hlt") }; +pub unsafe fn context_switch( + addresses: Addresses, + mut page_table: OffsetPageTable, + mut frame_allocator: UefiFrameAllocator, +) -> ! { + // identity-map current and next frame, so that we don't get an immediate pagefault + // after switching the active page table + let current_addr = PhysAddr::new(registers::read_rip()); + let current_frame: PhysFrame = PhysFrame::containing_address(current_addr); + for frame in PhysFrame::range_inclusive(current_frame, current_frame + 1) { + unsafe { page_table.identity_map(frame, PageTableFlags::PRESENT, &mut frame_allocator) } + .unwrap() + .flush(); } + + // we don't need the page table anymore + mem::drop(page_table); + + // do the context switch + unsafe { + asm!( + "mov cr3, {}; mov rsp, {}; push 0; jmp {}", + in(reg) addresses.page_table.start_address().as_u64(), + in(reg) addresses.stack_top.as_u64(), + in(reg) addresses.entry_point.as_u64(), + in("rdi") addresses.framebuffer_addr, + ); + } + unreachable!(); +} + +struct Addresses { + page_table: PhysFrame, + stack_top: VirtAddr, + entry_point: VirtAddr, + framebuffer_addr: PhysAddr, } -fn init_logger(st: &SystemTable) { +fn init_logger(st: &SystemTable) -> PhysAddr { let gop = st .boot_services() .locate_protocol::() @@ -91,6 +162,8 @@ fn init_logger(st: &SystemTable) { }; bootloader_lib::init_logger(slice, info); + + PhysAddr::new(framebuffer.as_mut_ptr() as u64) } struct UefiFrameAllocator<'a> { From c2d0ad7c0ade16fff8d6077223be49f8a518b51d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 13:33:27 +0200 Subject: [PATCH 023/174] Move `main.rs` to `bin/uefi.rs` Prepare for having multiple executables. --- src/{main.rs => bin/uefi.rs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/{main.rs => bin/uefi.rs} (98%) diff --git a/src/main.rs b/src/bin/uefi.rs similarity index 98% rename from src/main.rs rename to src/bin/uefi.rs index d732ea90..1a978521 100644 --- a/src/main.rs +++ b/src/bin/uefi.rs @@ -5,8 +5,8 @@ #![feature(unsafe_block_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)] -static KERNEL: PageAligned<[u8; 137224]> = PageAligned(*include_bytes!( - "../../blog_os/post-01/target/x86_64-blog_os/debug/blog_os" +static KERNEL: PageAligned<[u8; 1736736]> = PageAligned(*include_bytes!( + "../../../blog_os/post-01/target/x86_64-blog_os/debug/blog_os" )); #[repr(align(4096))] From 2121bd7f7ada49c390be82fce755d2f8ec4770f0 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 13:33:47 +0200 Subject: [PATCH 024/174] Fix two minor issues in uefi.rs --- src/bin/uefi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 1a978521..f2e97892 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -100,7 +100,7 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { } } -pub unsafe fn context_switch( +unsafe fn context_switch( addresses: Addresses, mut page_table: OffsetPageTable, mut frame_allocator: UefiFrameAllocator, @@ -125,7 +125,7 @@ pub unsafe fn context_switch( in(reg) addresses.page_table.start_address().as_u64(), in(reg) addresses.stack_top.as_u64(), in(reg) addresses.entry_point.as_u64(), - in("rdi") addresses.framebuffer_addr, + in("rdi") addresses.framebuffer_addr.as_u64(), ); } unreachable!(); From f1f52155ad5901a424471922034efc4849579c16 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 13:34:02 +0200 Subject: [PATCH 025/174] Fix type inference errors --- bootloader-lib/src/load_kernel.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bootloader-lib/src/load_kernel.rs b/bootloader-lib/src/load_kernel.rs index 14002b93..9cd00c88 100644 --- a/bootloader-lib/src/load_kernel.rs +++ b/bootloader-lib/src/load_kernel.rs @@ -180,7 +180,7 @@ where // segments now. // calculate the frame where the last segment page is mapped - let orig_frame = PhysFrame::containing_address(phys_start_addr + file_size - 1); + let orig_frame: PhysFrame = PhysFrame::containing_address(phys_start_addr + file_size - 1u64); // allocate a new frame to replace `orig_frame` let new_frame = self.frame_allocator.allocate_frame().unwrap(); @@ -206,7 +206,7 @@ where // remap last page from orig_frame to `new_frame` log::info!("Remap last page"); - let last_page = Page::containing_address(virt_start_addr + file_size - 1); + let last_page = Page::containing_address(virt_start_addr + file_size - 1u64); self.page_table .unmap(last_page.clone()) .map_err(|_err| "Failed to unmap last segment page because of bss memory")?; From 6aa62eb60653260ee71b757ef088d3b0fd617ec4 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 14:27:47 +0200 Subject: [PATCH 026/174] Move uefi related files to uefi subdirectory Prepare for merging the curent implementation of the bootloader crate --- {.cargo => uefi/.cargo}/config.toml | 0 .gitignore => uefi/.gitignore | 0 Cargo.lock => uefi/Cargo.lock | 0 Cargo.toml => uefi/Cargo.toml | 0 {runner => uefi/runner}/Cargo.toml | 0 {runner => uefi/runner}/src/main.rs | 0 rust-toolchain => uefi/rust-toolchain | 0 {src => uefi/src}/bin/uefi.rs | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename {.cargo => uefi/.cargo}/config.toml (100%) rename .gitignore => uefi/.gitignore (100%) rename Cargo.lock => uefi/Cargo.lock (100%) rename Cargo.toml => uefi/Cargo.toml (100%) rename {runner => uefi/runner}/Cargo.toml (100%) rename {runner => uefi/runner}/src/main.rs (100%) rename rust-toolchain => uefi/rust-toolchain (100%) rename {src => uefi/src}/bin/uefi.rs (100%) diff --git a/.cargo/config.toml b/uefi/.cargo/config.toml similarity index 100% rename from .cargo/config.toml rename to uefi/.cargo/config.toml diff --git a/.gitignore b/uefi/.gitignore similarity index 100% rename from .gitignore rename to uefi/.gitignore diff --git a/Cargo.lock b/uefi/Cargo.lock similarity index 100% rename from Cargo.lock rename to uefi/Cargo.lock diff --git a/Cargo.toml b/uefi/Cargo.toml similarity index 100% rename from Cargo.toml rename to uefi/Cargo.toml diff --git a/runner/Cargo.toml b/uefi/runner/Cargo.toml similarity index 100% rename from runner/Cargo.toml rename to uefi/runner/Cargo.toml diff --git a/runner/src/main.rs b/uefi/runner/src/main.rs similarity index 100% rename from runner/src/main.rs rename to uefi/runner/src/main.rs diff --git a/rust-toolchain b/uefi/rust-toolchain similarity index 100% rename from rust-toolchain rename to uefi/rust-toolchain diff --git a/src/bin/uefi.rs b/uefi/src/bin/uefi.rs similarity index 100% rename from src/bin/uefi.rs rename to uefi/src/bin/uefi.rs From 16a7814a7905b8b6142924a3f234ab1a11ddef90 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 14:30:10 +0200 Subject: [PATCH 027/174] Rename main.rs to bin/bios.rs --- src/{main.rs => bin/bios.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{main.rs => bin/bios.rs} (100%) diff --git a/src/main.rs b/src/bin/bios.rs similarity index 100% rename from src/main.rs rename to src/bin/bios.rs From a123733f1b966494009d7fedb9b3be9e1b58ee89 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 14:31:27 +0200 Subject: [PATCH 028/174] Include uefi.rs in bootloader binaries --- {uefi/src => src}/bin/uefi.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {uefi/src => src}/bin/uefi.rs (100%) diff --git a/uefi/src/bin/uefi.rs b/src/bin/uefi.rs similarity index 100% rename from uefi/src/bin/uefi.rs rename to src/bin/uefi.rs From 60d5ed54491d37a7116fe75b308197193b0df120 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 14:31:56 +0200 Subject: [PATCH 029/174] Move runner crate to top level --- {uefi/runner => runner}/Cargo.toml | 0 {uefi/runner => runner}/src/main.rs | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {uefi/runner => runner}/Cargo.toml (100%) rename {uefi/runner => runner}/src/main.rs (100%) diff --git a/uefi/runner/Cargo.toml b/runner/Cargo.toml similarity index 100% rename from uefi/runner/Cargo.toml rename to runner/Cargo.toml diff --git a/uefi/runner/src/main.rs b/runner/src/main.rs similarity index 100% rename from uefi/runner/src/main.rs rename to runner/src/main.rs From 347fb16168b6010b133cc09b25d3df596f9fb5fd Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 14:32:49 +0200 Subject: [PATCH 030/174] Remove superfluous files from uefi folder --- uefi/.gitignore | 1 - uefi/Cargo.lock | 213 -------------------------------------------- uefi/rust-toolchain | 1 - 3 files changed, 215 deletions(-) delete mode 100644 uefi/.gitignore delete mode 100644 uefi/Cargo.lock delete mode 100644 uefi/rust-toolchain diff --git a/uefi/.gitignore b/uefi/.gitignore deleted file mode 100644 index ea8c4bf7..00000000 --- a/uefi/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/uefi/Cargo.lock b/uefi/Cargo.lock deleted file mode 100644 index 6160713a..00000000 --- a/uefi/Cargo.lock +++ /dev/null @@ -1,213 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "anyhow" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" - -[[package]] -name = "bit_field" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" - -[[package]] -name = "bit_field" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "bootloader-lib" -version = "0.1.0" -dependencies = [ - "conquer-once", - "font8x8", - "log", - "spinning_top", - "x86_64", - "xmas-elf", -] - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "conquer-once" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f7644600a548ecad74e4a918392af1798f7dd045be610be3203b9e129b4f98f" -dependencies = [ - "conquer-util", -] - -[[package]] -name = "conquer-util" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "654fb2472cc369d311c547103a1fa81d467bef370ae7a0680f65939895b1182a" - -[[package]] -name = "font8x8" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44226c40489fb1d602344a1d8f1b544570c3435e396dda1eda7b5ef010d8f1be" - -[[package]] -name = "lock_api" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "proc-macro2" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rlibc" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" - -[[package]] -name = "runner" -version = "0.1.0" -dependencies = [ - "anyhow", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "spinning_top" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d801a3a53bcf5071f85fef8d5cab9e5f638fc5580a37e6eb7aba4b37438d24" -dependencies = [ - "lock_api", -] - -[[package]] -name = "syn" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "ucs2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85061f4e43545a613c0da6b87725bf23f8da8613cf2473719c4f71a270c4ce8a" -dependencies = [ - "bit_field 0.10.0", -] - -[[package]] -name = "uefi" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab1f1403ecbad37d25120161acc3db12066febf3446efcc40b7631d30678505d" -dependencies = [ - "bitflags", - "log", - "ucs2", - "uefi-macros", -] - -[[package]] -name = "uefi-macros" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a69fa8dd920e84d783769c44560484ade81f6c765cde2e1cc46c754ddf95947" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "uefi-test" -version = "0.1.0" -dependencies = [ - "bootloader-lib", - "log", - "rlibc", - "uefi", - "x86_64", -] - -[[package]] -name = "unicode-xid" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" - -[[package]] -name = "x86_64" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "365de37eb7c6da582cbb510dd0f3f1235d24ff6309a8a96e8a9909cc9bfd608f" -dependencies = [ - "bit_field 0.9.0", - "bitflags", -] - -[[package]] -name = "xmas-elf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e74de9a366f6ab8c405fa6b371d9ac24943921fa14b3d64afcb202065c405f11" -dependencies = [ - "zero", -] - -[[package]] -name = "zero" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f1bc8a6b2005884962297587045002d8cfb8dcec9db332f4ca216ddc5de82c5" diff --git a/uefi/rust-toolchain b/uefi/rust-toolchain deleted file mode 100644 index bf867e0a..00000000 --- a/uefi/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly From 10f714f41b2b3352907e7d13b6f4545824a0a4eb Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 14:35:41 +0200 Subject: [PATCH 031/174] Merge UEFI Cargo.toml into main Cargo.toml --- Cargo.toml | 15 ++++++++++++++- uefi/Cargo.toml | 25 ------------------------- 2 files changed, 14 insertions(+), 26 deletions(-) delete mode 100644 uefi/Cargo.toml diff --git a/Cargo.toml b/Cargo.toml index 3b9d1734..16e18659 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,12 @@ repository = "https://github.com/rust-osdev/bootloader" edition = "2018" build = "build.rs" +[workspace] +members = [ + "bootloader-lib", + "runner", +] + [[bin]] name = "bootloader" required-features = ["binary"] @@ -19,6 +25,8 @@ usize_conversions = { version = "0.2.0", optional = true } fixedvec = { version = "0.2.4", optional = true } bit_field = { version = "0.10.0", optional = true } rlibc = { version = "1.0.0", optional = true } +log = { version = "0.4.8", optional = true } +uefi = { version = "0.4.4", optional = true } [dependencies.font8x8] version = "0.2.4" @@ -26,13 +34,17 @@ default-features = false features = ["unicode"] optional = true +[dependencies.bootloader-lib] +path = "bootloader-lib" +optional = "true" + [build-dependencies] llvm-tools = { version = "0.1", optional = true } toml = { version = "0.5.1", optional = true } [features] default = [] -binary = ["xmas-elf", "x86_64", "usize_conversions", "fixedvec", "llvm-tools", "toml", "rlibc"] +binary = ["xmas-elf", "x86_64", "usize_conversions", "fixedvec", "llvm-tools", "toml", "rlibc", "log", "uefi", "bootloader-lib"] vga_320x200 = ["font8x8"] recursive_page_table = [] map_physical_memory = [] @@ -45,6 +57,7 @@ panic = "abort" panic = "abort" lto = false debug = true +overflow-checks = true [package.metadata.bootloader] target = "x86_64-bootloader.json" diff --git a/uefi/Cargo.toml b/uefi/Cargo.toml deleted file mode 100644 index 3f42112b..00000000 --- a/uefi/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "uefi-test" -version = "0.1.0" -authors = ["Philipp Oppermann "] -edition = "2018" - -[workspace] -members = [ - "bootloader-lib", - "runner", -] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -uefi = "0.4.4" -rlibc = "1.0.0" -log = "0.4.8" -x86_64 = "0.11.0" - -[dependencies.bootloader-lib] -path = "bootloader-lib" - -[profile.release] -overflow-checks = true From 9bff6bb2f4a9374767487f212920b03eb6983679 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 14:49:43 +0200 Subject: [PATCH 032/174] Merge UEFI cargo config into main cargo config --- .cargo/config | 2 -- {uefi/.cargo => .cargo}/config.toml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 .cargo/config rename {uefi/.cargo => .cargo}/config.toml (83%) diff --git a/.cargo/config b/.cargo/config deleted file mode 100644 index fb871b1b..00000000 --- a/.cargo/config +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "x86_64-bootloader.json" diff --git a/uefi/.cargo/config.toml b/.cargo/config.toml similarity index 83% rename from uefi/.cargo/config.toml rename to .cargo/config.toml index d15a2d68..fe8d8410 100644 --- a/uefi/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,5 @@ [build] -target = "x86_64-unknown-uefi" +target = "x86_64-bootloader.json" [target.x86_64-unknown-uefi] runner = "cargo run --manifest-path runner/Cargo.toml --target x86_64-unknown-linux-gnu" From 0690101881bea252474eb9a196d5d77fdb1a4598 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 14:50:32 +0200 Subject: [PATCH 033/174] Fix Cargo.toml syntax --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 16e18659..b98d3a05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ optional = true [dependencies.bootloader-lib] path = "bootloader-lib" -optional = "true" +optional = true [build-dependencies] llvm-tools = { version = "0.1", optional = true } From bd2388f2540438bd66f96755956f615ad6db9dcc Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 14:51:46 +0200 Subject: [PATCH 034/174] Make the binary feature required for both the bios and uefi executables --- Cargo.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b98d3a05..bd9debe2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,11 @@ members = [ ] [[bin]] -name = "bootloader" +name = "bios" +required-features = ["binary"] + +[[bin]] +name = "uefi" required-features = ["binary"] [dependencies] From c7c90b0162bd98170d4d284e7d72e7642cb37712 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 14:55:38 +0200 Subject: [PATCH 035/174] Separate binary feature into `bios_bin` and `uefi_bin` features --- Cargo.toml | 7 ++++--- build.rs | 10 +++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bd9debe2..cc2be5ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,11 +16,11 @@ members = [ [[bin]] name = "bios" -required-features = ["binary"] +required-features = ["bios_bin"] [[bin]] name = "uefi" -required-features = ["binary"] +required-features = ["uefi_bin"] [dependencies] xmas-elf = { version = "0.6.2", optional = true } @@ -48,7 +48,8 @@ toml = { version = "0.5.1", optional = true } [features] default = [] -binary = ["xmas-elf", "x86_64", "usize_conversions", "fixedvec", "llvm-tools", "toml", "rlibc", "log", "uefi", "bootloader-lib"] +bios_bin = ["xmas-elf", "x86_64", "usize_conversions", "fixedvec", "llvm-tools", "toml", "rlibc"] +uefi_bin = ["x86_64", "rlibc", "log", "uefi", "bootloader-lib"] vga_320x200 = ["font8x8"] recursive_page_table = [] map_physical_memory = [] diff --git a/build.rs b/build.rs index 7918c624..b8bfee39 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,7 @@ -#[cfg(not(feature = "binary"))] +#[cfg(not(feature = "bios_bin"))] fn main() {} -#[cfg(feature = "binary")] +#[cfg(feature = "bios_bin")] #[derive(Default)] struct BootloaderConfig { physical_memory_offset: Option, @@ -10,7 +10,7 @@ struct BootloaderConfig { boot_info_address: Option, } -#[cfg(feature = "binary")] +#[cfg(feature = "bios_bin")] fn parse_aligned_addr(key: &str, value: &str) -> u64 { let num = if value.starts_with("0x") { u64::from_str_radix(&value[2..], 16) @@ -33,7 +33,7 @@ fn parse_aligned_addr(key: &str, value: &str) -> u64 { } } -#[cfg(feature = "binary")] +#[cfg(feature = "bios_bin")] fn parse_to_config(cfg: &mut BootloaderConfig, table: &toml::value::Table) { use toml::Value; @@ -84,7 +84,7 @@ fn parse_to_config(cfg: &mut BootloaderConfig, table: &toml::value::Table) { } } -#[cfg(feature = "binary")] +#[cfg(feature = "bios_bin")] fn main() { use std::{ env, From c6960f33372e3daa35331cd844065a7d25c21f60 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 14:56:02 +0200 Subject: [PATCH 036/174] Update Cargo.lock --- Cargo.lock | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ab3c0dee..ab2ef52c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,10 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "anyhow" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bit_field" version = "0.9.0" @@ -12,7 +17,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bitflags" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -20,16 +25,49 @@ name = "bootloader" version = "0.9.8" dependencies = [ "bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bootloader-lib 0.1.0", "fixedvec 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "font8x8 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "rlibc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "uefi 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bootloader-lib" +version = "0.1.0" +dependencies = [ + "conquer-once 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "font8x8 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "spinning_top 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "xmas-elf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "conquer-once" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "conquer-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "conquer-util" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fixedvec" version = "0.2.4" @@ -45,16 +83,78 @@ name = "llvm-tools" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rlibc" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "runner" +version = "0.1.0" +dependencies = [ + "anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "serde" version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "spinning_top" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "toml" version = "0.5.1" @@ -63,6 +163,40 @@ dependencies = [ "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ucs2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "uefi" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "ucs2 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "uefi-macros 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "uefi-macros" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "usize_conversions" version = "0.2.0" @@ -74,7 +208,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -85,22 +219,46 @@ dependencies = [ "zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "xmas-elf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "zero" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] +"checksum anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" "checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" "checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" -"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum conquer-once 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "96eb12fb69466716fbae9009d389e6a30830ae8975e170eff2d2cff579f9efa3" +"checksum conquer-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "654fb2472cc369d311c547103a1fa81d467bef370ae7a0680f65939895b1182a" "checksum fixedvec 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b395ef2adf62bdeefcd1b59ad0dd2225c6c333ec79656ea79ac5285c46d051ea" "checksum font8x8 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "44226c40489fb1d602344a1d8f1b544570c3435e396dda1eda7b5ef010d8f1be" "checksum llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" +"checksum lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +"checksum log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +"checksum proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" +"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" "checksum rlibc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" +"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" "checksum serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113" +"checksum spinning_top 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "047031d6df5f5ae0092c97aa4f6bb04cfc9c081b4cd4cb9cdb38657994279a00" +"checksum syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0" "checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" +"checksum ucs2 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "85061f4e43545a613c0da6b87725bf23f8da8613cf2473719c4f71a270c4ce8a" +"checksum uefi 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "08fcd8a8b1c2488ea5adb67f3840584557a7089150c6f209148567397f767e12" +"checksum uefi-macros 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a69fa8dd920e84d783769c44560484ade81f6c765cde2e1cc46c754ddf95947" +"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" "checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" "checksum x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "365de37eb7c6da582cbb510dd0f3f1235d24ff6309a8a96e8a9909cc9bfd608f" "checksum xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22678df5df766e8d1e5d609da69f0c3132d794edf6ab5e75e7abcd2270d4cf58" +"checksum xmas-elf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e74de9a366f6ab8c405fa6b371d9ac24943921fa14b3d64afcb202065c405f11" "checksum zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f1bc8a6b2005884962297587045002d8cfb8dcec9db332f4ca216ddc5de82c5" From 55267b38c9e108dfb4d8f30e5955086e34b246c7 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 15:18:01 +0200 Subject: [PATCH 037/174] Add command aliases for building/running bios and uefi binaries --- .cargo/config.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.cargo/config.toml b/.cargo/config.toml index fe8d8410..ee3f4e4e 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -6,3 +6,8 @@ runner = "cargo run --manifest-path runner/Cargo.toml --target x86_64-unknown-li [alias] x = "-Z build-std=core,alloc" + +build-uefi = "x build --target x86_64-unknown-uefi --bin uefi --features uefi_bin" +run-uefi = "x run --target x86_64-unknown-uefi --bin uefi --features uefi_bin" + +build-bios = "x build --target x86_64-bootloader.json --bin bios --features bios_bin" From 9fb60a3576701d44c47ef57da579b70c6e42ff99 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 15:41:45 +0200 Subject: [PATCH 038/174] Reintroduce the `binary` feature as parent feature of bios_bin and uefi_bin --- Cargo.toml | 5 +++-- build.rs | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cc2be5ad..8bcbe26c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,8 +48,9 @@ toml = { version = "0.5.1", optional = true } [features] default = [] -bios_bin = ["xmas-elf", "x86_64", "usize_conversions", "fixedvec", "llvm-tools", "toml", "rlibc"] -uefi_bin = ["x86_64", "rlibc", "log", "uefi", "bootloader-lib"] +bios_bin = ["binary", "xmas-elf", "x86_64", "usize_conversions", "fixedvec", "rlibc"] +uefi_bin = ["binary", "x86_64", "rlibc", "log", "uefi", "bootloader-lib"] +binary = ["llvm-tools", "toml"] vga_320x200 = ["font8x8"] recursive_page_table = [] map_physical_memory = [] diff --git a/build.rs b/build.rs index b8bfee39..d05a18cb 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,7 @@ -#[cfg(not(feature = "bios_bin"))] +#[cfg(not(feature = "binary"))] fn main() {} -#[cfg(feature = "bios_bin")] +#[cfg(feature = "binary")] #[derive(Default)] struct BootloaderConfig { physical_memory_offset: Option, @@ -10,7 +10,7 @@ struct BootloaderConfig { boot_info_address: Option, } -#[cfg(feature = "bios_bin")] +#[cfg(feature = "binary")] fn parse_aligned_addr(key: &str, value: &str) -> u64 { let num = if value.starts_with("0x") { u64::from_str_radix(&value[2..], 16) @@ -33,7 +33,7 @@ fn parse_aligned_addr(key: &str, value: &str) -> u64 { } } -#[cfg(feature = "bios_bin")] +#[cfg(feature = "binary")] fn parse_to_config(cfg: &mut BootloaderConfig, table: &toml::value::Table) { use toml::Value; @@ -101,7 +101,7 @@ fn main() { .expect("target has no file stem") != "x86_64-bootloader" { - panic!("The bootloader must be compiled for the `x86_64-bootloader.json` target."); + panic!("The BIOS bootloader must be compiled for the `x86_64-bootloader.json` target."); } let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set")); From f37f58765aebceb8217369b643796218ce4118a6 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 24 Jul 2020 15:42:51 +0200 Subject: [PATCH 039/174] Add a build script for the uefi bootloader Use it to parse the configuration and include the kernel through the KERNEL environment variabe instead of hardcoding the path. --- build.rs | 172 ++++++++++++++++++++++++++++++++++++++++++++++++ src/bin/uefi.rs | 7 +- 2 files changed, 176 insertions(+), 3 deletions(-) diff --git a/build.rs b/build.rs index d05a18cb..f0bfd7e2 100644 --- a/build.rs +++ b/build.rs @@ -297,3 +297,175 @@ fn main() { println!("cargo:rerun-if-changed={}", kernel.display()); println!("cargo:rerun-if-changed=build.rs"); } + + +#[cfg(feature = "uefi_bin")] +fn main() { + use std::{ + env, + fs::{self, File}, + io::Write, + path::{Path, PathBuf}, + process::{self, Command}, + }; + use toml::Value; + + let target = env::var("TARGET").expect("TARGET not set"); + if Path::new(&target) + .file_stem() + .expect("target has no file stem") + != "x86_64-unknown-uefi" + { + panic!("The UEFI bootloader must be compiled for the `x86_64-unknown-uefi.json` target."); + } + + let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set")); + let kernel = PathBuf::from(match env::var("KERNEL") { + Ok(kernel) => kernel, + Err(_) => { + eprintln!( + "The KERNEL environment variable must be set for building the bootloader.\n\n\ + If you use `bootimage` for building you need at least version 0.7.0. You can \ + update `bootimage` by running `cargo install bootimage --force`." + ); + process::exit(1); + } + }); + let kernel_file_name = kernel + .file_name() + .expect("KERNEL has no valid file name") + .to_str() + .expect("kernel file name not valid utf8"); + + // check that the kernel file exists + assert!( + kernel.exists(), + format!("KERNEL does not exist: {}", kernel.display()) + ); + + // get access to llvm tools shipped in the llvm-tools-preview rustup component + let llvm_tools = match llvm_tools::LlvmTools::new() { + Ok(tools) => tools, + Err(llvm_tools::Error::NotFound) => { + eprintln!("Error: llvm-tools not found"); + eprintln!("Maybe the rustup component `llvm-tools-preview` is missing?"); + eprintln!(" Install it through: `rustup component add llvm-tools-preview`"); + process::exit(1); + } + Err(err) => { + eprintln!("Failed to retrieve llvm-tools component: {:?}", err); + process::exit(1); + } + }; + + // check that kernel executable has code in it + let llvm_size = llvm_tools + .tool(&llvm_tools::exe("llvm-size")) + .expect("llvm-size not found in llvm-tools"); + let mut cmd = Command::new(llvm_size); + cmd.arg(&kernel); + let output = cmd.output().expect("failed to run llvm-size"); + let output_str = String::from_utf8_lossy(&output.stdout); + let second_line_opt = output_str.lines().skip(1).next(); + let second_line = second_line_opt.expect("unexpected llvm-size line output"); + let text_size_opt = second_line.split_ascii_whitespace().next(); + let text_size = text_size_opt.expect("unexpected llvm-size output"); + if text_size == "0" { + panic!("Kernel executable has an empty text section. Perhaps the entry point was set incorrectly?\n\n\ + Kernel executable at `{}`\n", kernel.display()); + } + + // strip debug symbols from kernel for faster loading + let stripped_kernel_file_name = format!("kernel_stripped-{}", kernel_file_name); + let stripped_kernel = out_dir.join(&stripped_kernel_file_name); + let objcopy = llvm_tools + .tool(&llvm_tools::exe("llvm-objcopy")) + .expect("llvm-objcopy not found in llvm-tools"); + let mut cmd = Command::new(&objcopy); + cmd.arg("--strip-debug"); + cmd.arg(&kernel); + cmd.arg(&stripped_kernel); + let exit_status = cmd + .status() + .expect("failed to run objcopy to strip debug symbols"); + if !exit_status.success() { + eprintln!("Error: Stripping debug symbols failed"); + process::exit(1); + } + + // write file for including kernel in binary + let file_path = out_dir.join("kernel_info.rs"); + let mut file = File::create(file_path).expect("failed to create kernel_info.rs"); + let kernel_size = fs::metadata(&stripped_kernel) + .expect("Failed to read file metadata of stripped kernel").len(); + file.write_all( + format!( + "const KERNEL_SIZE: usize = {}; const KERNEL_BYTES: [u8; KERNEL_SIZE] = *include_bytes!(\"{}\");", + kernel_size, + stripped_kernel.display(), + ) + .as_bytes(), + ) + .expect("write to kernel_info.rs failed"); + + // Parse the kernel's Cargo.toml which is given to us by bootimage + let mut bootloader_config = BootloaderConfig::default(); + + match env::var("KERNEL_MANIFEST") { + Err(env::VarError::NotPresent) => { + panic!("The KERNEL_MANIFEST environment variable must be set for building the bootloader.\n\n\ + If you use `bootimage` for building you need at least version 0.7.7. You can \ + update `bootimage` by running `cargo install bootimage --force`."); + } + Err(env::VarError::NotUnicode(_)) => { + panic!("The KERNEL_MANIFEST environment variable contains invalid unicode") + } + Ok(path) => { + println!("cargo:rerun-if-changed={}", path); + + let contents = fs::read_to_string(&path).expect(&format!( + "failed to read kernel manifest file (path: {})", + path + )); + + let manifest = contents + .parse::() + .expect("failed to parse kernel's Cargo.toml"); + + let table = manifest + .get("package") + .and_then(|table| table.get("metadata")) + .and_then(|table| table.get("bootloader")) + .and_then(|table| table.as_table()); + + if let Some(table) = table { + parse_to_config(&mut bootloader_config, table); + } + } + } + + // Configure constants for the bootloader + // We leave some variables as Option rather than hardcoding their defaults so that they + // can be calculated dynamically by the bootloader. + 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( + format!( + "const PHYSICAL_MEMORY_OFFSET: Option = {:?}; + const KERNEL_STACK_ADDRESS: Option = {:?}; + const KERNEL_STACK_SIZE: u64 = {}; + const BOOT_INFO_ADDRESS: Option = {:?};", + bootloader_config.physical_memory_offset, + bootloader_config.kernel_stack_address, + bootloader_config.kernel_stack_size.unwrap_or(512), // size is in number of pages + bootloader_config.boot_info_address, + ) + .as_bytes(), + ) + .expect("write to bootloader_config.rs failed"); + + println!("cargo:rerun-if-env-changed=KERNEL"); + println!("cargo:rerun-if-env-changed=KERNEL_MANIFEST"); + println!("cargo:rerun-if-changed={}", kernel.display()); + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index f2e97892..53c25336 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -5,9 +5,10 @@ #![feature(unsafe_block_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)] -static KERNEL: PageAligned<[u8; 1736736]> = PageAligned(*include_bytes!( - "../../../blog_os/post-01/target/x86_64-blog_os/debug/blog_os" -)); +// Defines the constants `KERNEL_BYTES` (array of `u8`) and `KERNEL_SIZE` (`usize`). +include!(concat!(env!("OUT_DIR"), "/kernel_info.rs")); + +static KERNEL: PageAligned<[u8; KERNEL_SIZE]> = PageAligned(KERNEL_BYTES); #[repr(align(4096))] struct PageAligned(T); From dcb422c29539136579e89e56ab30af2c301d338f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 20 Aug 2020 14:15:58 +0200 Subject: [PATCH 040/174] Update to uefi-rs 0.5.0 + custom patch Patch submitted in https://github.com/rust-osdev/uefi-rs/pull/161. --- Cargo.toml | 2 +- src/bin/uefi.rs | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8bcbe26c..6cb91310 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ fixedvec = { version = "0.2.4", optional = true } bit_field = { version = "0.10.0", optional = true } rlibc = { version = "1.0.0", optional = true } log = { version = "0.4.8", optional = true } -uefi = { version = "0.4.4", optional = true } +uefi = { path = "../uefi-rs", optional = true } [dependencies.font8x8] version = "0.2.4" diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 53c25336..f5f663e0 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -19,7 +19,7 @@ use core::{mem, slice}; use uefi::{ prelude::{entry, Boot, Handle, ResultExt, Status, SystemTable}, proto::console::gop::{GraphicsOutput, PixelFormat}, - table::boot::{MemoryDescriptor, MemoryMapIter, MemoryType}, + table::boot::{MemoryDescriptor, MemoryType}, }; use x86_64::{ registers, @@ -104,7 +104,7 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { unsafe fn context_switch( addresses: Addresses, mut page_table: OffsetPageTable, - mut frame_allocator: UefiFrameAllocator, + mut frame_allocator: impl FrameAllocator, ) -> ! { // identity-map current and next frame, so that we don't get an immediate pagefault // after switching the active page table @@ -167,15 +167,20 @@ fn init_logger(st: &SystemTable) -> PhysAddr { PhysAddr::new(framebuffer.as_mut_ptr() as u64) } -struct UefiFrameAllocator<'a> { - memory_map: MemoryMapIter<'a>, +struct UefiFrameAllocator<'a, I> { + original: I, + memory_map: I, current_descriptor: Option<&'a MemoryDescriptor>, next_frame: PhysFrame, } -impl<'a> UefiFrameAllocator<'a> { - fn new(memory_map: MemoryMapIter<'a>) -> Self { +impl<'a, I> UefiFrameAllocator<'a, I> +where + I: Iterator + Clone, +{ + fn new(memory_map: I) -> Self { Self { + original: memory_map.clone(), memory_map, current_descriptor: None, next_frame: PhysFrame::containing_address(PhysAddr::new(0)), @@ -199,7 +204,10 @@ impl<'a> UefiFrameAllocator<'a> { } } -unsafe impl FrameAllocator for UefiFrameAllocator<'_> { +unsafe impl<'a, I> FrameAllocator for UefiFrameAllocator<'a, I> +where + I: Iterator + Clone, +{ fn allocate_frame(&mut self) -> Option> { if let Some(current_descriptor) = self.current_descriptor { match self.allocate_frame_from_descriptor(current_descriptor) { From d9eb62d5d8128b7e666420d93a65efe6e1671cb2 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 20 Aug 2020 14:16:33 +0200 Subject: [PATCH 041/174] Run cargo fmt --- bootloader-lib/src/load_kernel.rs | 3 ++- build.rs | 4 ++-- src/bootinfo/mod.rs | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bootloader-lib/src/load_kernel.rs b/bootloader-lib/src/load_kernel.rs index 9cd00c88..dda7bef6 100644 --- a/bootloader-lib/src/load_kernel.rs +++ b/bootloader-lib/src/load_kernel.rs @@ -180,7 +180,8 @@ where // segments now. // calculate the frame where the last segment page is mapped - let orig_frame: PhysFrame = PhysFrame::containing_address(phys_start_addr + file_size - 1u64); + let orig_frame: PhysFrame = + PhysFrame::containing_address(phys_start_addr + file_size - 1u64); // allocate a new frame to replace `orig_frame` let new_frame = self.frame_allocator.allocate_frame().unwrap(); diff --git a/build.rs b/build.rs index f0bfd7e2..682d4830 100644 --- a/build.rs +++ b/build.rs @@ -298,7 +298,6 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); } - #[cfg(feature = "uefi_bin")] fn main() { use std::{ @@ -397,7 +396,8 @@ fn main() { let file_path = out_dir.join("kernel_info.rs"); let mut file = File::create(file_path).expect("failed to create kernel_info.rs"); let kernel_size = fs::metadata(&stripped_kernel) - .expect("Failed to read file metadata of stripped kernel").len(); + .expect("Failed to read file metadata of stripped kernel") + .len(); file.write_all( format!( "const KERNEL_SIZE: usize = {}; const KERNEL_BYTES: [u8; KERNEL_SIZE] = *include_bytes!(\"{}\");", diff --git a/src/bootinfo/mod.rs b/src/bootinfo/mod.rs index beccac07..51327151 100644 --- a/src/bootinfo/mod.rs +++ b/src/bootinfo/mod.rs @@ -5,7 +5,6 @@ pub use self::memory_map::*; mod memory_map; - /// This structure represents the information that the bootloader passes to the kernel. /// /// The information is passed as an argument to the entry point: From b0ef3f974f12a27f48458aacc23c63196a12028a Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 20 Aug 2020 14:16:58 +0200 Subject: [PATCH 042/174] Update Cargo.lock --- Cargo.lock | 146 ++++++++++++++++++++++++++--------------------------- 1 file changed, 71 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ab2ef52c..b4b07ec4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,263 +2,259 @@ # It is not intended for manual editing. [[package]] name = "anyhow" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" [[package]] name = "bit_field" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" [[package]] name = "bit_field" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bootloader" version = "0.9.8" dependencies = [ - "bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bootloader-lib 0.1.0", - "fixedvec 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "font8x8 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "rlibc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "uefi 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bit_field 0.10.0", + "bootloader-lib", + "fixedvec", + "font8x8", + "llvm-tools", + "log", + "rlibc", + "toml", + "uefi", + "usize_conversions", + "x86_64", + "xmas-elf 0.6.2", ] [[package]] name = "bootloader-lib" version = "0.1.0" dependencies = [ - "conquer-once 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "font8x8 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "spinning_top 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "xmas-elf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "conquer-once", + "font8x8", + "log", + "spinning_top", + "x86_64", + "xmas-elf 0.7.0", ] [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "conquer-once" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96eb12fb69466716fbae9009d389e6a30830ae8975e170eff2d2cff579f9efa3" dependencies = [ - "conquer-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "conquer-util", ] [[package]] name = "conquer-util" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "654fb2472cc369d311c547103a1fa81d467bef370ae7a0680f65939895b1182a" [[package]] name = "fixedvec" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b395ef2adf62bdeefcd1b59ad0dd2225c6c333ec79656ea79ac5285c46d051ea" [[package]] name = "font8x8" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44226c40489fb1d602344a1d8f1b544570c3435e396dda1eda7b5ef010d8f1be" [[package]] name = "llvm-tools" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" [[package]] name = "lock_api" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" dependencies = [ - "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard", ] [[package]] name = "log" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", ] [[package]] name = "proc-macro2" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" dependencies = [ - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid", ] [[package]] name = "quote" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "rlibc" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" [[package]] name = "runner" version = "0.1.0" dependencies = [ - "anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow", ] [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113" [[package]] name = "spinning_top" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "047031d6df5f5ae0092c97aa4f6bb04cfc9c081b4cd4cb9cdb38657994279a00" dependencies = [ - "lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api", ] [[package]] name = "syn" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] name = "toml" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" dependencies = [ - "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "ucs2" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85061f4e43545a613c0da6b87725bf23f8da8613cf2473719c4f71a270c4ce8a" dependencies = [ - "bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bit_field 0.10.0", ] [[package]] name = "uefi" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.5.0" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "ucs2 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "uefi-macros 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "log", + "ucs2", + "uefi-macros", ] [[package]] name = "uefi-macros" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a69fa8dd920e84d783769c44560484ade81f6c765cde2e1cc46c754ddf95947" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "usize_conversions" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" [[package]] name = "x86_64" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "365de37eb7c6da582cbb510dd0f3f1235d24ff6309a8a96e8a9909cc9bfd608f" dependencies = [ - "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bit_field 0.9.0", + "bitflags", ] [[package]] name = "xmas-elf" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22678df5df766e8d1e5d609da69f0c3132d794edf6ab5e75e7abcd2270d4cf58" dependencies = [ - "zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zero", ] [[package]] name = "xmas-elf" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e74de9a366f6ab8c405fa6b371d9ac24943921fa14b3d64afcb202065c405f11" dependencies = [ - "zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zero", ] [[package]] name = "zero" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" -"checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" -"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum conquer-once 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "96eb12fb69466716fbae9009d389e6a30830ae8975e170eff2d2cff579f9efa3" -"checksum conquer-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "654fb2472cc369d311c547103a1fa81d467bef370ae7a0680f65939895b1182a" -"checksum fixedvec 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b395ef2adf62bdeefcd1b59ad0dd2225c6c333ec79656ea79ac5285c46d051ea" -"checksum font8x8 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "44226c40489fb1d602344a1d8f1b544570c3435e396dda1eda7b5ef010d8f1be" -"checksum llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" -"checksum lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" -"checksum log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" -"checksum proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" -"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -"checksum rlibc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" -"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -"checksum serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113" -"checksum spinning_top 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "047031d6df5f5ae0092c97aa4f6bb04cfc9c081b4cd4cb9cdb38657994279a00" -"checksum syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0" -"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" -"checksum ucs2 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "85061f4e43545a613c0da6b87725bf23f8da8613cf2473719c4f71a270c4ce8a" -"checksum uefi 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "08fcd8a8b1c2488ea5adb67f3840584557a7089150c6f209148567397f767e12" -"checksum uefi-macros 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a69fa8dd920e84d783769c44560484ade81f6c765cde2e1cc46c754ddf95947" -"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" -"checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" -"checksum x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "365de37eb7c6da582cbb510dd0f3f1235d24ff6309a8a96e8a9909cc9bfd608f" -"checksum xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22678df5df766e8d1e5d609da69f0c3132d794edf6ab5e75e7abcd2270d4cf58" -"checksum xmas-elf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e74de9a366f6ab8c405fa6b371d9ac24943921fa14b3d64afcb202065c405f11" -"checksum zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f1bc8a6b2005884962297587045002d8cfb8dcec9db332f4ca216ddc5de82c5" +checksum = "5f1bc8a6b2005884962297587045002d8cfb8dcec9db332f4ca216ddc5de82c5" From bb1eaf0e9674a093c40d668bf2852711c088429c Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 20 Aug 2020 14:19:03 +0200 Subject: [PATCH 043/174] Identity-map framebuffer --- src/bin/uefi.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index f5f663e0..c60bc070 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -34,7 +34,7 @@ const PAGE_SIZE: u64 = 4096; #[entry] fn efi_main(image: Handle, st: SystemTable) -> Status { - let framebuffer_addr = init_logger(&st); + let (framebuffer_addr, framebuffer_size) = init_logger(&st); log::info!("Hello World from UEFI bootloader!"); log::info!("Using framebuffer at {:#x}", framebuffer_addr); @@ -90,6 +90,17 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { .flush(); } + // identity-map framebuffer + let framebuffer_start_frame: PhysFrame = PhysFrame::containing_address(framebuffer_addr); + let framebuffer_end_frame = + PhysFrame::containing_address(framebuffer_addr + framebuffer_size - 1u64); + for frame in PhysFrame::range_inclusive(framebuffer_start_frame, framebuffer_end_frame) { + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + unsafe { page_table.identity_map(frame, flags, &mut frame_allocator) } + .unwrap() + .flush(); + } + let addresses = Addresses { page_table: level_4_frame, stack_top: stack_end.start_address(), @@ -139,7 +150,7 @@ struct Addresses { framebuffer_addr: PhysAddr, } -fn init_logger(st: &SystemTable) -> PhysAddr { +fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { let gop = st .boot_services() .locate_protocol::() @@ -164,7 +175,10 @@ fn init_logger(st: &SystemTable) -> PhysAddr { bootloader_lib::init_logger(slice, info); - PhysAddr::new(framebuffer.as_mut_ptr() as u64) + ( + PhysAddr::new(framebuffer.as_mut_ptr() as u64), + framebuffer.size(), + ) } struct UefiFrameAllocator<'a, I> { From eaa29c1ac29988b508d51f3915c158761052913f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 20 Aug 2020 14:37:19 +0200 Subject: [PATCH 044/174] Prototype implementation of a new UEFI memory map --- src/bin/uefi.rs | 87 +++++++++++++++++++++++++++++- src/lib.rs | 2 + src/memory_map.rs | 132 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 src/memory_map.rs diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index c60bc070..0e39533c 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -101,6 +101,34 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { .flush(); } + // reserve two unused frames for context switch + let two_frames = TwoFrames::new(&mut frame_allocator); + + // create memory map + let memory_map = { + use bootloader::memory_map::{MemoryMap, MemoryMapBuilder}; + + let frame = frame_allocator + .allocate_frame() + .expect("frame allocation for memory map failed"); + let memory_map_ptr: *mut MemoryMap = + VirtAddr::new(frame.start_address().as_u64()).as_mut_ptr(); + let memory_map = unsafe { + memory_map_ptr.write(MemoryMap::new()); + &mut *memory_map_ptr + }; + + // identity-map the frame in new page tables + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + unsafe { page_table.identity_map(frame, flags, &mut frame_allocator) } + .unwrap() + .flush(); + + let mut builder = MemoryMapBuilder::new(memory_map); + frame_allocator.construct_memory_map(&mut builder); + builder.finalize() + }; + let addresses = Addresses { page_table: level_4_frame, stack_top: stack_end.start_address(), @@ -108,7 +136,7 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { framebuffer_addr, }; unsafe { - context_switch(addresses, page_table, frame_allocator); + context_switch(addresses, page_table, two_frames); } } @@ -216,6 +244,42 @@ where None } } + + fn construct_memory_map(self, builder: &mut bootloader::memory_map::MemoryMapBuilder) { + use bootloader::memory_map::{MemoryMap, MemoryRegion, MemoryRegionKind}; + + for mut descriptor in self.original.copied() { + let end = descriptor.phys_start + PAGE_SIZE * descriptor.page_count; + let next_free = self.next_frame.start_address().as_u64(); + let kind = match descriptor.ty { + MemoryType::CONVENTIONAL if end <= next_free => MemoryRegionKind::Bootloader, + MemoryType::CONVENTIONAL if descriptor.phys_start >= next_free => { + MemoryRegionKind::Usable + } + MemoryType::CONVENTIONAL => { + // part of the region is used -> add is separately + let used_region = MemoryRegion { + start: descriptor.phys_start, + end: next_free, + kind: MemoryRegionKind::Bootloader, + }; + builder.add_region(used_region); + + // add unused part normally + descriptor.phys_start = next_free; + MemoryRegionKind::Usable + } + MemoryType::RESERVED => MemoryRegionKind::Reserved, + other => continue, + }; + let region = MemoryRegion { + start: descriptor.phys_start, + end, + kind, + }; + builder.add_region(region); + } + } } unsafe impl<'a, I> FrameAllocator for UefiFrameAllocator<'a, I> @@ -246,3 +310,24 @@ where None } } + +pub struct TwoFrames { + frames: [Option; 2], +} + +impl TwoFrames { + pub fn new(frame_allocator: &mut impl FrameAllocator) -> Self { + TwoFrames { + frames: [ + Some(frame_allocator.allocate_frame().unwrap()), + Some(frame_allocator.allocate_frame().unwrap()), + ], + } + } +} + +unsafe impl FrameAllocator for TwoFrames { + fn allocate_frame(&mut self) -> Option> { + self.frames.iter_mut().find_map(|f| f.take()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 90d8eebf..e80cdfef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,8 @@ pub use crate::bootinfo::BootInfo; pub mod bootinfo; +pub mod memory_map; + #[cfg(target_arch = "x86")] compile_error!( "This crate currently does not support 32-bit protected mode. \ diff --git a/src/memory_map.rs b/src/memory_map.rs new file mode 100644 index 00000000..bf1a1c61 --- /dev/null +++ b/src/memory_map.rs @@ -0,0 +1,132 @@ +use core::mem; + +const PAGE_SIZE: usize = 4096; + +pub struct MemoryMapBuilder { + memory_map: &'static mut MemoryMap, + next_free: usize, +} + +impl MemoryMapBuilder { + pub fn new(memory_map: &'static mut MemoryMap) -> Self { + assert_eq!(*memory_map, MemoryMap::new()); + MemoryMapBuilder { + memory_map, + next_free: 0, + } + } + + pub fn add_region(&mut self, new_region: MemoryRegion) -> Result<(), ()> { + let node_index = self.next_free / REGIONS_PER_NODE; + let inner_index = self.next_free % REGIONS_PER_NODE; + let node = self.memory_map.get_node_mut(node_index).ok_or(())?; + let region = node.get_region_mut(inner_index).ok_or(())?; + if *region == MemoryRegion::empty() { + *region = new_region; + Ok(()) + } else { + Err(()) + } + } + + pub fn add_node(&mut self, node: &'static mut MemoryMapNode) { + self.memory_map.add_node(node); + } + + pub fn finalize(self) -> &'static mut MemoryMap { + self.memory_map + } +} + +#[derive(Debug, Eq, PartialEq)] +#[repr(transparent)] +pub struct MemoryMap { + pub head: MemoryMapNode, +} + +impl MemoryMap { + pub fn new() -> Self { + Self { + head: MemoryMapNode::new(), + } + } + + pub fn get_node_mut(&mut self, node_index: usize) -> Option<&mut MemoryMapNode> { + self.head.get_node_mut(node_index) + } + + pub fn add_node(&mut self, node: &'static mut MemoryMapNode) { + self.head.add_node(node); + } +} + +#[derive(Debug, Eq, PartialEq)] +#[repr(C)] +pub struct MemoryMapNode { + regions: [MemoryRegion; REGIONS_PER_NODE], + next: Option<&'static mut MemoryMapNode>, +} + +impl MemoryMapNode { + fn new() -> Self { + MemoryMapNode { + regions: [MemoryRegion::empty(); REGIONS_PER_NODE], + next: None, + } + } + + pub fn get_node_mut(&mut self, node_index: usize) -> Option<&mut MemoryMapNode> { + if node_index == 0 { + Some(self) + } else { + self.next + .as_mut() + .and_then(|node| node.get_node_mut(node_index - 1)) + } + } + + pub fn get_region_mut(&mut self, index: usize) -> Option<&mut MemoryRegion> { + self.regions.get_mut(index) + } + + pub fn add_node(&mut self, node: &'static mut MemoryMapNode) { + match &mut self.next { + None => self.next = Some(node), + Some(next) => next.add_node(node), + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(C)] +pub struct MemoryRegion { + pub start: u64, + pub end: u64, + pub kind: MemoryRegionKind, +} + +impl MemoryRegion { + pub const fn empty() -> Self { + MemoryRegion { + start: 0, + end: 0, + kind: MemoryRegionKind::Empty, + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(C)] +pub enum MemoryRegionKind { + Usable, + Empty, + Reserved, + Bootloader, +} + +const REGIONS_PER_NODE: usize = (PAGE_SIZE - mem::size_of::>()) + / mem::size_of::(); +const _PADDING: usize = 8; +const _ASSERT_SIZE: [(); 4096] = [(); mem::size_of::() + _PADDING]; + +extern "C" fn _assert_ffi(_memory_map: MemoryMap) {} From 62e7a1c4b3810ad5c5e525e33973849695e7d816 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 20 Aug 2020 14:40:09 +0200 Subject: [PATCH 045/174] Start implementing a new boot info struct that works with UEFI --- src/bin/uefi.rs | 38 ++++++++++++++++++++++++++++++++++---- src/boot_info_uefi.rs | 15 +++++++++++++++ src/lib.rs | 1 + 3 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 src/boot_info_uefi.rs diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 0e39533c..a5420c9f 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -101,8 +101,19 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { .flush(); } - // reserve two unused frames for context switch + // reserve two unused frames for context switch and one for the boot info let two_frames = TwoFrames::new(&mut frame_allocator); + let boot_info_frame = frame_allocator.allocate_frame().unwrap(); + // identity-map the boot info frame in new page tables + unsafe { + page_table.identity_map( + boot_info_frame, + PageTableFlags::PRESENT | PageTableFlags::WRITABLE, + &mut frame_allocator, + ) + } + .unwrap() + .flush(); // create memory map let memory_map = { @@ -129,11 +140,30 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { builder.finalize() }; + // create boot info + let boot_info = { + use bootloader::boot_info_uefi::{BootInfo, FrameBufferInfo}; + + let boot_info = BootInfo { + memory_map, + framebuffer: FrameBufferInfo { + start_addr: framebuffer_addr.as_u64(), + len: framebuffer_size, + }, + }; + let ptr: *mut BootInfo = + VirtAddr::new(boot_info_frame.start_address().as_u64()).as_mut_ptr(); + unsafe { + ptr.write(boot_info); + &mut *ptr + } + }; + let addresses = Addresses { page_table: level_4_frame, stack_top: stack_end.start_address(), entry_point, - framebuffer_addr, + boot_info, }; unsafe { context_switch(addresses, page_table, two_frames); @@ -165,7 +195,7 @@ unsafe fn context_switch( in(reg) addresses.page_table.start_address().as_u64(), in(reg) addresses.stack_top.as_u64(), in(reg) addresses.entry_point.as_u64(), - in("rdi") addresses.framebuffer_addr.as_u64(), + in("rdi") addresses.boot_info as *const _ as usize, ); } unreachable!(); @@ -175,7 +205,7 @@ struct Addresses { page_table: PhysFrame, stack_top: VirtAddr, entry_point: VirtAddr, - framebuffer_addr: PhysAddr, + boot_info: &'static mut bootloader::boot_info_uefi::BootInfo, } fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { diff --git a/src/boot_info_uefi.rs b/src/boot_info_uefi.rs new file mode 100644 index 00000000..0a3d4392 --- /dev/null +++ b/src/boot_info_uefi.rs @@ -0,0 +1,15 @@ +use crate::memory_map::MemoryMap; + +#[derive(Debug)] +#[repr(C)] +pub struct BootInfo { + pub memory_map: &'static mut MemoryMap, + pub framebuffer: FrameBufferInfo, +} + +#[derive(Debug)] +#[repr(C)] +pub struct FrameBufferInfo { + pub start_addr: u64, + pub len: usize, +} diff --git a/src/lib.rs b/src/lib.rs index e80cdfef..b34e6399 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ pub use crate::bootinfo::BootInfo; pub mod bootinfo; +pub mod boot_info_uefi; pub mod memory_map; #[cfg(target_arch = "x86")] From b3bde0eea310bb865e0e0c36fa28f496d9bedba5 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 20 Aug 2020 16:48:12 +0200 Subject: [PATCH 046/174] Use upstream version of uefi again (my patch was merged) --- Cargo.lock | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index b4b07ec4..84d5cf3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -195,6 +195,7 @@ dependencies = [ [[package]] name = "uefi" version = "0.5.0" +source = "git+https://github.com/rust-osdev/uefi-rs.git#fa4b0081f0e7d9fdd72caa7f3e5a052ed6ba6de7" dependencies = [ "bitflags", "log", diff --git a/Cargo.toml b/Cargo.toml index 6cb91310..10ee7df8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ fixedvec = { version = "0.2.4", optional = true } bit_field = { version = "0.10.0", optional = true } rlibc = { version = "1.0.0", optional = true } log = { version = "0.4.8", optional = true } -uefi = { path = "../uefi-rs", optional = true } +uefi = { git = "https://github.com/rust-osdev/uefi-rs.git", optional = true } [dependencies.font8x8] version = "0.2.4" From 534dde8de19f201b767536d6e4cb041f08a12fac Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 21 Aug 2020 11:56:46 +0200 Subject: [PATCH 047/174] Refactor and add support for arbitrary-sized memory map --- Cargo.toml | 2 +- src/bin/uefi.rs | 319 ++++++++++++++++++++++++++++++++---------- src/boot_info_uefi.rs | 6 +- src/lib.rs | 1 + src/memory_map.rs | 100 ++----------- 5 files changed, 265 insertions(+), 163 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 10ee7df8..bd4e3c71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ toml = { version = "0.5.1", optional = true } [features] default = [] bios_bin = ["binary", "xmas-elf", "x86_64", "usize_conversions", "fixedvec", "rlibc"] -uefi_bin = ["binary", "x86_64", "rlibc", "log", "uefi", "bootloader-lib"] +uefi_bin = ["binary", "x86_64", "rlibc", "log", "uefi", "bootloader-lib", "usize_conversions"] binary = ["llvm-tools", "toml"] vga_320x200 = ["font8x8"] recursive_page_table = [] diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index a5420c9f..140ae7a6 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -3,6 +3,9 @@ #![feature(abi_efiapi)] #![feature(asm)] #![feature(unsafe_block_in_unsafe_fn)] +#![feature(min_const_generics)] +#![feature(maybe_uninit_extra)] +#![feature(maybe_uninit_slice_assume_init)] #![deny(unsafe_op_in_unsafe_fn)] // Defines the constants `KERNEL_BYTES` (array of `u8`) and `KERNEL_SIZE` (`usize`). @@ -15,12 +18,18 @@ struct PageAligned(T); extern crate rlibc; -use core::{mem, slice}; +use bootloader::boot_info_uefi::{BootInfo, FrameBufferInfo}; +use bootloader::memory_map::{MemoryMap, MemoryRegion}; +use core::{ + mem::{self, MaybeUninit}, + slice, +}; use uefi::{ prelude::{entry, Boot, Handle, ResultExt, Status, SystemTable}, proto::console::gop::{GraphicsOutput, PixelFormat}, table::boot::{MemoryDescriptor, MemoryType}, }; +use usize_conversions::FromUsize; use x86_64::{ registers, structures::paging::{ @@ -54,10 +63,52 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { .expect_success("Failed to exit boot services"); let mut frame_allocator = UefiFrameAllocator::new(memory_map); + let mut page_tables = create_page_tables(&mut frame_allocator); + let mappings = set_up_mappings( + &mut frame_allocator, + &mut page_tables.kernel, + framebuffer_addr, + framebuffer_size, + ); + let (boot_info, two_frames) = create_boot_info( + frame_allocator, + &mut page_tables, + mappings.framebuffer, + framebuffer_size, + ); + switch_to_kernel(page_tables, mappings, boot_info, two_frames); +} - let (mut page_table, level_4_frame) = { - // UEFI identity maps all memory, so physical memory offset is 0 - let phys_offset = VirtAddr::new(0); +/// Creates page table abstraction types for both the bootloader and kernel page tables. +fn create_page_tables(frame_allocator: &mut impl FrameAllocator) -> PageTables { + // UEFI identity-maps all memory, so the offset between physical and virtual addresses is 0 + let phys_offset = VirtAddr::new(0); + + // copy the currently active level 4 page table, because it might be read-only + log::trace!("switching to new level 4 table"); + let mut bootloader_page_table = { + let old_frame = x86_64::registers::control::Cr3::read().0; + let old_table: *const PageTable = + (phys_offset + old_frame.start_address().as_u64()).as_ptr(); + let new_frame = frame_allocator + .allocate_frame() + .expect("Failed to allocate frame for new level 4 table"); + let new_table: *mut PageTable = + (phys_offset + new_frame.start_address().as_u64()).as_mut_ptr(); + // copy the table to the new frame + unsafe { core::ptr::copy_nonoverlapping(old_table, new_table, 1) }; + // the tables are now identical, so we can just load the new one + unsafe { + x86_64::registers::control::Cr3::write( + new_frame, + x86_64::registers::control::Cr3Flags::empty(), + ); + unsafe { OffsetPageTable::new(&mut *new_table, phys_offset) } + } + }; + + // create a new page table hierarchy for the kernel + let (mut kernel_page_table, kernel_level_4_frame) = { // get an unused frame for new level 4 page table let frame: PhysFrame = frame_allocator.allocate_frame().expect("no unused frames"); log::info!("New page table at: {:#?}", &frame); @@ -72,107 +123,187 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { frame, ) }; - log::info!("New page table at: {:?}", level_4_frame); - let entry_point = bootloader_lib::load_kernel(&KERNEL.0, &mut page_table, &mut frame_allocator); + PageTables { + bootloader: bootloader_page_table, + kernel: kernel_page_table, + kernel_level_4_frame, + } +} + +struct PageTables { + bootloader: OffsetPageTable<'static>, + kernel: OffsetPageTable<'static>, + kernel_level_4_frame: PhysFrame, +} + +/// Sets up mappings for a kernel stack and the framebuffer +fn set_up_mappings( + frame_allocator: &mut impl FrameAllocator, + kernel_page_table: &mut OffsetPageTable, + framebuffer_addr: PhysAddr, + framebuffer_size: usize, +) -> Mappings { + let entry_point = bootloader_lib::load_kernel(&KERNEL.0, kernel_page_table, frame_allocator); log::info!("Entry point at: {:#x}", entry_point.as_u64()); // create a stack - let stack_start: Page = Page::containing_address(VirtAddr::new(0xfff00000000)); + let stack_start: Page = kernel_stack_start_location(); let stack_end = stack_start + 20; for page in Page::range(stack_start, stack_end) { let frame = frame_allocator .allocate_frame() .expect("frame allocation failed when mapping a kernel stack"); let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - unsafe { page_table.map_to(page, frame, flags, &mut frame_allocator) } + unsafe { kernel_page_table.map_to(page, frame, flags, frame_allocator) } .unwrap() .flush(); } - // identity-map framebuffer + log::info!("Map framebuffer"); + + // map framebuffer let framebuffer_start_frame: PhysFrame = PhysFrame::containing_address(framebuffer_addr); let framebuffer_end_frame = PhysFrame::containing_address(framebuffer_addr + framebuffer_size - 1u64); - for frame in PhysFrame::range_inclusive(framebuffer_start_frame, framebuffer_end_frame) { + let start_page = frame_buffer_location(); + for (i, frame) in + PhysFrame::range_inclusive(framebuffer_start_frame, framebuffer_end_frame).enumerate() + { + let page = start_page + u64::from_usize(i); let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - unsafe { page_table.identity_map(frame, flags, &mut frame_allocator) } + unsafe { kernel_page_table.map_to(page, frame, flags, frame_allocator) } .unwrap() .flush(); } + let framebuffer_virt_addr = start_page.start_address(); - // reserve two unused frames for context switch and one for the boot info - let two_frames = TwoFrames::new(&mut frame_allocator); - let boot_info_frame = frame_allocator.allocate_frame().unwrap(); - // identity-map the boot info frame in new page tables - unsafe { - page_table.identity_map( - boot_info_frame, - PageTableFlags::PRESENT | PageTableFlags::WRITABLE, - &mut frame_allocator, - ) + Mappings { + framebuffer: framebuffer_virt_addr, + entry_point, + stack_end, } - .unwrap() - .flush(); - - // create memory map - let memory_map = { - use bootloader::memory_map::{MemoryMap, MemoryMapBuilder}; +} - let frame = frame_allocator - .allocate_frame() - .expect("frame allocation for memory map failed"); - let memory_map_ptr: *mut MemoryMap = - VirtAddr::new(frame.start_address().as_u64()).as_mut_ptr(); - let memory_map = unsafe { - memory_map_ptr.write(MemoryMap::new()); - &mut *memory_map_ptr - }; +struct Mappings { + entry_point: VirtAddr, + framebuffer: VirtAddr, + stack_end: Page, +} - // identity-map the frame in new page tables - let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - unsafe { page_table.identity_map(frame, flags, &mut frame_allocator) } +/// Allocates and initializes the boot info struct and the memory map +fn create_boot_info<'a, I>( + mut frame_allocator: UefiFrameAllocator<'a, I>, + page_tables: &mut PageTables, + framebuffer_virt_addr: VirtAddr, + framebuffer_size: usize, +) -> (&'static mut BootInfo, TwoFrames) +where + I: Iterator + Clone, +{ + log::info!("Allocate bootinfo"); + + // allocate and map space for the boot info + let (boot_info, memory_map, memory_regions) = { + let boot_info_addr = boot_info_location(); + let boot_info_end = boot_info_addr + mem::size_of::(); + let memory_map_addr = boot_info_end.align_up(u64::from_usize(mem::align_of::())); + let memory_map_end = memory_map_addr + mem::size_of::(); + let memory_map_regions_addr = + memory_map_end.align_up(u64::from_usize(mem::align_of::())); + let regions = frame_allocator.len(); + let memory_map_regions_end = + memory_map_regions_addr + regions * mem::size_of::(); + + let start_page = Page::containing_address(boot_info_addr); + let end_page = Page::containing_address(memory_map_regions_end - 1u64); + for page in Page::range_inclusive(start_page, end_page) { + log::info!("Mapping page {:?}", page); + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + let frame = frame_allocator + .allocate_frame() + .expect("frame allocation for boot info failed"); + log::info!("1 {:?}", page); + unsafe { + page_tables + .kernel + .map_to(page, frame, flags, &mut frame_allocator) + } .unwrap() .flush(); + log::info!("2 {:?}", page); + // we need to be able to access it too + unsafe { + page_tables + .bootloader + .map_to(page, frame, flags, &mut frame_allocator) + } + .unwrap() + .flush(); + log::info!("Finished mapping page {:?}", page); + } - let mut builder = MemoryMapBuilder::new(memory_map); - frame_allocator.construct_memory_map(&mut builder); - builder.finalize() + let boot_info: &'static mut MaybeUninit = + unsafe { &mut *boot_info_addr.as_mut_ptr() }; + let memory_map: &'static mut MaybeUninit = + unsafe { &mut *memory_map_addr.as_mut_ptr() }; + let memory_regions: &'static mut [MaybeUninit] = + unsafe { slice::from_raw_parts_mut(memory_map_regions_addr.as_mut_ptr(), regions) }; + (boot_info, memory_map, memory_regions) }; + // reserve two unused frames for context switch + let two_frames = TwoFrames::new(&mut frame_allocator); + + log::info!("Create Memory Map"); + + // build memory map + let memory_regions = frame_allocator.construct_memory_map(memory_regions); + + log::info!("Create bootinfo"); + // create boot info - let boot_info = { - use bootloader::boot_info_uefi::{BootInfo, FrameBufferInfo}; + let boot_info = boot_info.write(BootInfo { + memory_regions, + framebuffer: FrameBufferInfo { + start_addr: framebuffer_virt_addr.as_u64(), + len: framebuffer_size, + }, + }); - let boot_info = BootInfo { - memory_map, - framebuffer: FrameBufferInfo { - start_addr: framebuffer_addr.as_u64(), - len: framebuffer_size, - }, - }; - let ptr: *mut BootInfo = - VirtAddr::new(boot_info_frame.start_address().as_u64()).as_mut_ptr(); - unsafe { - ptr.write(boot_info); - &mut *ptr - } - }; + (boot_info, two_frames) +} +fn switch_to_kernel( + page_tables: PageTables, + mappings: Mappings, + boot_info: &'static mut BootInfo, + two_frames: TwoFrames, +) -> ! { + let PageTables { + kernel_level_4_frame, + kernel: kernel_page_table, + .. + } = page_tables; let addresses = Addresses { - page_table: level_4_frame, - stack_top: stack_end.start_address(), - entry_point, + page_table: kernel_level_4_frame, + stack_top: mappings.stack_end.start_address(), + entry_point: mappings.entry_point, boot_info, }; + + log::info!( + "Jumping to kernel entry point at {:?}", + addresses.entry_point + ); unsafe { - context_switch(addresses, page_table, two_frames); + context_switch(addresses, kernel_page_table, two_frames); } } unsafe fn context_switch( addresses: Addresses, - mut page_table: OffsetPageTable, + mut kernel_page_table: OffsetPageTable, mut frame_allocator: impl FrameAllocator, ) -> ! { // identity-map current and next frame, so that we don't get an immediate pagefault @@ -180,13 +311,15 @@ unsafe fn context_switch( let current_addr = PhysAddr::new(registers::read_rip()); let current_frame: PhysFrame = PhysFrame::containing_address(current_addr); for frame in PhysFrame::range_inclusive(current_frame, current_frame + 1) { - unsafe { page_table.identity_map(frame, PageTableFlags::PRESENT, &mut frame_allocator) } - .unwrap() - .flush(); + unsafe { + kernel_page_table.identity_map(frame, PageTableFlags::PRESENT, &mut frame_allocator) + } + .unwrap() + .flush(); } - // we don't need the page table anymore - mem::drop(page_table); + // we don't need the kernel page table anymore + mem::drop(kernel_page_table); // do the context switch unsafe { @@ -255,7 +388,7 @@ where original: memory_map.clone(), memory_map, current_descriptor: None, - next_frame: PhysFrame::containing_address(PhysAddr::new(0)), + next_frame: PhysFrame::containing_address(PhysAddr::new(0x1000)), } } @@ -275,8 +408,17 @@ where } } - fn construct_memory_map(self, builder: &mut bootloader::memory_map::MemoryMapBuilder) { - use bootloader::memory_map::{MemoryMap, MemoryRegion, MemoryRegionKind}; + fn len(&self) -> usize { + self.original.clone().count() // TODO ExactSizeIterator implementation possible? + } + + fn construct_memory_map( + self, + regions: &mut [MaybeUninit], + ) -> &mut [MemoryRegion] { + use bootloader::memory_map::MemoryRegionKind; + + let mut next_index = 0; for mut descriptor in self.original.copied() { let end = descriptor.phys_start + PAGE_SIZE * descriptor.page_count; @@ -293,7 +435,7 @@ where end: next_free, kind: MemoryRegionKind::Bootloader, }; - builder.add_region(used_region); + Self::add_region(used_region, regions, &mut next_index); // add unused part normally descriptor.phys_start = next_free; @@ -307,8 +449,27 @@ where end, kind, }; - builder.add_region(region); + Self::add_region(region, regions, &mut next_index); } + + let initialized = &mut regions[..next_index]; + unsafe { MaybeUninit::slice_get_mut(initialized) } + } + + fn add_region( + region: MemoryRegion, + regions: &mut [MaybeUninit], + next_index: &mut usize, + ) -> Result<(), ()> { + unsafe { + regions + .get_mut(*next_index) + .ok_or(())? + .as_mut_ptr() + .write(region) + }; + *next_index += 1; + Ok(()) } } @@ -361,3 +522,15 @@ unsafe impl FrameAllocator for TwoFrames { self.frames.iter_mut().find_map(|f| f.take()) } } + +fn boot_info_location() -> VirtAddr { + VirtAddr::new(0x_0000_00bb_bbbb_0000) +} + +fn frame_buffer_location() -> Page { + Page::containing_address(VirtAddr::new(0x_0000_00cc_cccc_0000)) +} + +fn kernel_stack_start_location() -> Page { + Page::containing_address(VirtAddr::new(0x_0000_0fff_0000_0000)); +} \ No newline at end of file diff --git a/src/boot_info_uefi.rs b/src/boot_info_uefi.rs index 0a3d4392..7a888606 100644 --- a/src/boot_info_uefi.rs +++ b/src/boot_info_uefi.rs @@ -1,9 +1,9 @@ -use crate::memory_map::MemoryMap; +use crate::memory_map::MemoryRegion; #[derive(Debug)] #[repr(C)] pub struct BootInfo { - pub memory_map: &'static mut MemoryMap, + pub memory_regions: &'static mut [MemoryRegion], pub framebuffer: FrameBufferInfo, } @@ -13,3 +13,5 @@ pub struct FrameBufferInfo { pub start_addr: u64, pub len: usize, } + +extern "C" fn _assert_ffi(_boot_info: &'static mut BootInfo) {} diff --git a/src/lib.rs b/src/lib.rs index b34e6399..5c8f3f03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ #![no_std] #![warn(missing_docs)] +#![feature(min_const_generics)] pub use crate::bootinfo::BootInfo; diff --git a/src/memory_map.rs b/src/memory_map.rs index bf1a1c61..ab4b83f9 100644 --- a/src/memory_map.rs +++ b/src/memory_map.rs @@ -1,26 +1,19 @@ -use core::mem; - -const PAGE_SIZE: usize = 4096; - -pub struct MemoryMapBuilder { - memory_map: &'static mut MemoryMap, +#[derive(Debug, Eq, PartialEq)] +pub struct MemoryMap { + regions: &'static mut [MemoryRegion], next_free: usize, } -impl MemoryMapBuilder { - pub fn new(memory_map: &'static mut MemoryMap) -> Self { - assert_eq!(*memory_map, MemoryMap::new()); - MemoryMapBuilder { - memory_map, +impl MemoryMap { + pub fn new(regions: &'static mut [MemoryRegion]) -> Self { + Self { + regions, next_free: 0, } } pub fn add_region(&mut self, new_region: MemoryRegion) -> Result<(), ()> { - let node_index = self.next_free / REGIONS_PER_NODE; - let inner_index = self.next_free % REGIONS_PER_NODE; - let node = self.memory_map.get_node_mut(node_index).ok_or(())?; - let region = node.get_region_mut(inner_index).ok_or(())?; + let region = self.regions.get_mut(self.next_free).ok_or(())?; if *region == MemoryRegion::empty() { *region = new_region; Ok(()) @@ -29,76 +22,16 @@ impl MemoryMapBuilder { } } - pub fn add_node(&mut self, node: &'static mut MemoryMapNode) { - self.memory_map.add_node(node); + pub fn as_slice(&self) -> &[MemoryRegion] { + &self.regions[..self.next_free] } - pub fn finalize(self) -> &'static mut MemoryMap { - self.memory_map - } -} - -#[derive(Debug, Eq, PartialEq)] -#[repr(transparent)] -pub struct MemoryMap { - pub head: MemoryMapNode, -} - -impl MemoryMap { - pub fn new() -> Self { - Self { - head: MemoryMapNode::new(), - } - } - - pub fn get_node_mut(&mut self, node_index: usize) -> Option<&mut MemoryMapNode> { - self.head.get_node_mut(node_index) - } - - pub fn add_node(&mut self, node: &'static mut MemoryMapNode) { - self.head.add_node(node); - } -} - -#[derive(Debug, Eq, PartialEq)] -#[repr(C)] -pub struct MemoryMapNode { - regions: [MemoryRegion; REGIONS_PER_NODE], - next: Option<&'static mut MemoryMapNode>, -} - -impl MemoryMapNode { - fn new() -> Self { - MemoryMapNode { - regions: [MemoryRegion::empty(); REGIONS_PER_NODE], - next: None, - } - } - - pub fn get_node_mut(&mut self, node_index: usize) -> Option<&mut MemoryMapNode> { - if node_index == 0 { - Some(self) - } else { - self.next - .as_mut() - .and_then(|node| node.get_node_mut(node_index - 1)) - } - } - - pub fn get_region_mut(&mut self, index: usize) -> Option<&mut MemoryRegion> { - self.regions.get_mut(index) - } - - pub fn add_node(&mut self, node: &'static mut MemoryMapNode) { - match &mut self.next { - None => self.next = Some(node), - Some(next) => next.add_node(node), - } + pub fn as_mut_slice(&mut self) -> &mut [MemoryRegion] { + &mut self.regions[..self.next_free] } } #[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[repr(C)] pub struct MemoryRegion { pub start: u64, pub end: u64, @@ -110,23 +43,16 @@ impl MemoryRegion { MemoryRegion { start: 0, end: 0, - kind: MemoryRegionKind::Empty, + kind: MemoryRegionKind::Bootloader, } } } #[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[repr(C)] pub enum MemoryRegionKind { Usable, - Empty, Reserved, Bootloader, } -const REGIONS_PER_NODE: usize = (PAGE_SIZE - mem::size_of::>()) - / mem::size_of::(); -const _PADDING: usize = 8; -const _ASSERT_SIZE: [(); 4096] = [(); mem::size_of::() + _PADDING]; - extern "C" fn _assert_ffi(_memory_map: MemoryMap) {} From 11a2f0522a65b324b980b3e491adbba4228b22ca Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 21 Aug 2020 12:53:24 +0200 Subject: [PATCH 048/174] Add some documentation --- src/bin/uefi.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 140ae7a6..4d63a66a 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -274,6 +274,7 @@ where (boot_info, two_frames) } +/// Switches to the kernel address space and jumps to the kernel entry point. fn switch_to_kernel( page_tables: PageTables, mappings: Mappings, @@ -301,6 +302,9 @@ fn switch_to_kernel( } } +/// Performs the actual context switch +/// +/// This function should stay small because it needs to be identity-mapped. unsafe fn context_switch( addresses: Addresses, mut kernel_page_table: OffsetPageTable, From 258d9df0f090cc518343fc54796c7cd474e5946b Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 21 Aug 2020 13:06:57 +0200 Subject: [PATCH 049/174] Fix typo --- src/bin/uefi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 4d63a66a..f50457e3 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -536,5 +536,5 @@ fn frame_buffer_location() -> Page { } fn kernel_stack_start_location() -> Page { - Page::containing_address(VirtAddr::new(0x_0000_0fff_0000_0000)); -} \ No newline at end of file + Page::containing_address(VirtAddr::new(0x_0000_0fff_0000_0000)) +} From c2c4115c213a773931386fae4e8666d5353622da Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 21 Aug 2020 13:32:39 +0200 Subject: [PATCH 050/174] Add a builder binary The goal is that this replaces as much functionality of bootimage as possible. --- .cargo/config.toml | 5 ++-- Cargo.lock | 57 +++++++++++++++++++++++++++++++++++ Cargo.toml | 7 +++++ src/bin/builder.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 src/bin/builder.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index ee3f4e4e..2c22df37 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,10 +1,9 @@ -[build] -target = "x86_64-bootloader.json" - [target.x86_64-unknown-uefi] runner = "cargo run --manifest-path runner/Cargo.toml --target x86_64-unknown-linux-gnu" [alias] +builder = "run --bin builder --features builder --" + x = "-Z build-std=core,alloc" build-uefi = "x build --target x86_64-unknown-uefi --bin uefi --features uefi_bin" diff --git a/Cargo.lock b/Cargo.lock index 84d5cf3b..683ca6c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,35 @@ version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" +[[package]] +name = "argh" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca1877e24cecacd700d469066e0160c4f8497cc5635367163f50c8beec820154" +dependencies = [ + "argh_derive", + "argh_shared", +] + +[[package]] +name = "argh_derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e742194e0f43fc932bcb801708c2b279d3ec8f527e3acda05a6a9f342c5ef764" +dependencies = [ + "argh_shared", + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "argh_shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ba68f4276a778591e36a0c348a269888f3a177c8d2054969389e3b59611ff5" + [[package]] name = "bit_field" version = "0.9.0" @@ -28,8 +57,10 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" name = "bootloader" version = "0.9.8" dependencies = [ + "argh", "bit_field 0.10.0", "bootloader-lib", + "displaydoc", "fixedvec", "font8x8", "llvm-tools", @@ -75,6 +106,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "654fb2472cc369d311c547103a1fa81d467bef370ae7a0680f65939895b1182a" +[[package]] +name = "displaydoc" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc2ab4d5a16117f9029e9a6b5e4e79f4c67f6519bc134210d4d4a04ba31f41b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "fixedvec" version = "0.2.4" @@ -87,6 +129,15 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44226c40489fb1d602344a1d8f1b544570c3435e396dda1eda7b5ef010d8f1be" +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "llvm-tools" version = "0.1.1" @@ -214,6 +265,12 @@ dependencies = [ "syn", ] +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + [[package]] name = "unicode-xid" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index bd4e3c71..fce88470 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,10 @@ members = [ "runner", ] +[[bin]] +name = "builder" +required-features = ["builder"] + [[bin]] name = "bios" required-features = ["bios_bin"] @@ -31,6 +35,8 @@ bit_field = { version = "0.10.0", optional = true } rlibc = { version = "1.0.0", optional = true } log = { version = "0.4.8", optional = true } uefi = { git = "https://github.com/rust-osdev/uefi-rs.git", optional = true } +argh = { version = "0.1.3", optional = true } +displaydoc = { version = "0.1.7", optional = true } [dependencies.font8x8] version = "0.2.4" @@ -48,6 +54,7 @@ toml = { version = "0.5.1", optional = true } [features] default = [] +builder = ["argh", "displaydoc"] bios_bin = ["binary", "xmas-elf", "x86_64", "usize_conversions", "fixedvec", "rlibc"] uefi_bin = ["binary", "x86_64", "rlibc", "log", "uefi", "bootloader-lib", "usize_conversions"] binary = ["llvm-tools", "toml"] diff --git a/src/bin/builder.rs b/src/bin/builder.rs new file mode 100644 index 00000000..4e73214f --- /dev/null +++ b/src/bin/builder.rs @@ -0,0 +1,74 @@ +use argh::FromArgs; +use std::{fmt, path::PathBuf, process::Command, str::FromStr}; + +#[derive(FromArgs)] +/// Build the bootloader +struct BuildArguments { + /// path to the `Cargo.toml` of the kernel + #[argh(option)] + kernel_manifest: PathBuf, + + /// path to the kernel ELF binary + #[argh(option)] + kernel_binary: PathBuf, + + /// which firmware interface to build + #[argh(option, default = "Firmware::All")] + firmware: Firmware, + + /// whether to run the resulting binary in QEMU + #[argh(switch)] + run: bool, +} + +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +enum Firmware { + Bios, + Uefi, + All, +} + +impl FromStr for Firmware { + type Err = FirmwareParseError; + + fn from_str(s: &str) -> Result { + match s.to_ascii_lowercase().as_str() { + "bios" => Ok(Firmware::Bios), + "uefi" => Ok(Firmware::Uefi), + "all" => Ok(Firmware::All), + _other => Err(FirmwareParseError), + } + } +} + +/// Firmware must be one of `uefi`, `bios`, or `all`. +#[derive(Debug, displaydoc::Display, Eq, PartialEq, Copy, Clone)] +struct FirmwareParseError; + +fn main() { + let args: BuildArguments = argh::from_env(); + + let build_or_run = if args.run { "run" } else { "build" }; + + if args.firmware == Firmware::Uefi || args.firmware == Firmware::All { + let mut cmd = Command::new(env!("CARGO")); + cmd.arg(build_or_run).arg("--bin").arg("uefi"); + cmd.arg("--target").arg("x86_64-unknown-uefi"); + cmd.arg("--features").arg("uefi_bin"); + cmd.arg("-Zbuild-std=core"); + cmd.env("KERNEL", &args.kernel_binary); + cmd.env("KERNEL_MANIFEST", &args.kernel_manifest); + cmd.status(); + } + + if args.firmware == Firmware::Bios || args.firmware == Firmware::All { + let mut cmd = Command::new(env!("CARGO")); + cmd.arg(build_or_run).arg("--bin").arg("bios"); + cmd.arg("--target").arg("x86_64-bootloader.json"); + cmd.arg("--features").arg("bios_bin"); + cmd.arg("-Zbuild-std=core"); + cmd.env("KERNEL", &args.kernel_binary); + cmd.env("KERNEL_MANIFEST", &args.kernel_manifest); + cmd.status(); + } +} From 98a4e03d0ab03f7c326075078802d8080770de02 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 21 Aug 2020 13:35:23 +0200 Subject: [PATCH 051/174] Remove no longer needed MemoryMap type --- src/bin/uefi.rs | 12 ++++-------- src/memory_map.rs | 35 ----------------------------------- 2 files changed, 4 insertions(+), 43 deletions(-) diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index f50457e3..c9d51473 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -19,7 +19,7 @@ struct PageAligned(T); extern crate rlibc; use bootloader::boot_info_uefi::{BootInfo, FrameBufferInfo}; -use bootloader::memory_map::{MemoryMap, MemoryRegion}; +use bootloader::memory_map::MemoryRegion; use core::{ mem::{self, MaybeUninit}, slice, @@ -204,13 +204,11 @@ where log::info!("Allocate bootinfo"); // allocate and map space for the boot info - let (boot_info, memory_map, memory_regions) = { + let (boot_info, memory_regions) = { let boot_info_addr = boot_info_location(); let boot_info_end = boot_info_addr + mem::size_of::(); - let memory_map_addr = boot_info_end.align_up(u64::from_usize(mem::align_of::())); - let memory_map_end = memory_map_addr + mem::size_of::(); let memory_map_regions_addr = - memory_map_end.align_up(u64::from_usize(mem::align_of::())); + boot_info_end.align_up(u64::from_usize(mem::align_of::())); let regions = frame_allocator.len(); let memory_map_regions_end = memory_map_regions_addr + regions * mem::size_of::(); @@ -245,11 +243,9 @@ where let boot_info: &'static mut MaybeUninit = unsafe { &mut *boot_info_addr.as_mut_ptr() }; - let memory_map: &'static mut MaybeUninit = - unsafe { &mut *memory_map_addr.as_mut_ptr() }; let memory_regions: &'static mut [MaybeUninit] = unsafe { slice::from_raw_parts_mut(memory_map_regions_addr.as_mut_ptr(), regions) }; - (boot_info, memory_map, memory_regions) + (boot_info, memory_regions) }; // reserve two unused frames for context switch diff --git a/src/memory_map.rs b/src/memory_map.rs index ab4b83f9..1e78d454 100644 --- a/src/memory_map.rs +++ b/src/memory_map.rs @@ -1,36 +1,3 @@ -#[derive(Debug, Eq, PartialEq)] -pub struct MemoryMap { - regions: &'static mut [MemoryRegion], - next_free: usize, -} - -impl MemoryMap { - pub fn new(regions: &'static mut [MemoryRegion]) -> Self { - Self { - regions, - next_free: 0, - } - } - - pub fn add_region(&mut self, new_region: MemoryRegion) -> Result<(), ()> { - let region = self.regions.get_mut(self.next_free).ok_or(())?; - if *region == MemoryRegion::empty() { - *region = new_region; - Ok(()) - } else { - Err(()) - } - } - - pub fn as_slice(&self) -> &[MemoryRegion] { - &self.regions[..self.next_free] - } - - pub fn as_mut_slice(&mut self) -> &mut [MemoryRegion] { - &mut self.regions[..self.next_free] - } -} - #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct MemoryRegion { pub start: u64, @@ -54,5 +21,3 @@ pub enum MemoryRegionKind { Reserved, Bootloader, } - -extern "C" fn _assert_ffi(_memory_map: MemoryMap) {} From 9e158a2091d5fbb7c8954f0c17bc981e68d4a000 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 21 Aug 2020 13:35:55 +0200 Subject: [PATCH 052/174] Move uefi-specific panic handler to uefi binary --- bootloader-lib/src/lib.rs | 8 -------- src/bin/uefi.rs | 9 +++++++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/bootloader-lib/src/lib.rs b/bootloader-lib/src/lib.rs index da20bd26..15e5c6ea 100644 --- a/bootloader-lib/src/lib.rs +++ b/bootloader-lib/src/lib.rs @@ -28,11 +28,3 @@ pub fn load_kernel( load_kernel::load_kernel(kernel, page_table, frame_allocator).expect("Failed to parse kernel") } -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - unsafe { logger::LOGGER.get().map(|l| l.force_unlock()) }; - log::error!("{}", info); - loop { - unsafe { asm!("cli; hlt") }; - } -} diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index c9d51473..b9a5701b 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -534,3 +534,12 @@ fn frame_buffer_location() -> Page { fn kernel_stack_start_location() -> Page { Page::containing_address(VirtAddr::new(0x_0000_0fff_0000_0000)) } + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + unsafe { logger::LOGGER.get().map(|l| l.force_unlock()) }; + log::error!("{}", info); + loop { + unsafe { asm!("cli; hlt") }; + } +} From d1f6fa720f79ef10c3b9845d56cb41f1c1d6f832 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 21 Aug 2020 13:52:41 +0200 Subject: [PATCH 053/174] Merge bootloader-lib into `bootloader` --- Cargo.lock | 34 +++++--------------- Cargo.toml | 15 ++++----- bootloader-lib/Cargo.toml | 22 ------------- bootloader-lib/src/lib.rs | 30 ------------------ src/bin/uefi.rs | 13 ++++---- src/lib.rs | 36 +++++++++++++++++++++- {bootloader-lib/src => src}/load_kernel.rs | 0 {bootloader-lib/src => src}/logger.rs | 0 8 files changed, 55 insertions(+), 95 deletions(-) delete mode 100644 bootloader-lib/Cargo.toml delete mode 100644 bootloader-lib/src/lib.rs rename {bootloader-lib/src => src}/load_kernel.rs (100%) rename {bootloader-lib/src => src}/logger.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 683ca6c1..b2dcaaa2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,30 +59,19 @@ version = "0.9.8" dependencies = [ "argh", "bit_field 0.10.0", - "bootloader-lib", + "conquer-once", "displaydoc", "fixedvec", "font8x8", "llvm-tools", "log", "rlibc", + "spinning_top", "toml", "uefi", "usize_conversions", "x86_64", - "xmas-elf 0.6.2", -] - -[[package]] -name = "bootloader-lib" -version = "0.1.0" -dependencies = [ - "conquer-once", - "font8x8", - "log", - "spinning_top", - "x86_64", - "xmas-elf 0.7.0", + "xmas-elf", ] [[package]] @@ -146,9 +135,9 @@ checksum = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" [[package]] name = "lock_api" -version = "0.3.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" dependencies = [ "scopeguard", ] @@ -207,9 +196,9 @@ checksum = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113" [[package]] name = "spinning_top" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "047031d6df5f5ae0092c97aa4f6bb04cfc9c081b4cd4cb9cdb38657994279a00" +checksum = "3ae8ba8ba35ec6c69da715181fb4b5451c3974b997596c0443d0bbc18c8cf6cd" dependencies = [ "lock_api", ] @@ -302,15 +291,6 @@ dependencies = [ "zero", ] -[[package]] -name = "xmas-elf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e74de9a366f6ab8c405fa6b371d9ac24943921fa14b3d64afcb202065c405f11" -dependencies = [ - "zero", -] - [[package]] name = "zero" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index fce88470..66b660f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,6 @@ build = "build.rs" [workspace] members = [ - "bootloader-lib", "runner", ] @@ -37,17 +36,15 @@ log = { version = "0.4.8", optional = true } uefi = { git = "https://github.com/rust-osdev/uefi-rs.git", optional = true } argh = { version = "0.1.3", optional = true } displaydoc = { version = "0.1.7", optional = true } +conquer-once = { version = "0.2.1", optional = true, default-features = false } +spinning_top = { version = "0.2.1", optional = true } [dependencies.font8x8] -version = "0.2.4" +version = "0.2.5" default-features = false features = ["unicode"] optional = true -[dependencies.bootloader-lib] -path = "bootloader-lib" -optional = true - [build-dependencies] llvm-tools = { version = "0.1", optional = true } toml = { version = "0.5.1", optional = true } @@ -55,9 +52,9 @@ toml = { version = "0.5.1", optional = true } [features] default = [] builder = ["argh", "displaydoc"] -bios_bin = ["binary", "xmas-elf", "x86_64", "usize_conversions", "fixedvec", "rlibc"] -uefi_bin = ["binary", "x86_64", "rlibc", "log", "uefi", "bootloader-lib", "usize_conversions"] -binary = ["llvm-tools", "toml"] +bios_bin = ["binary", "fixedvec", "rlibc"] +uefi_bin = ["binary", "rlibc", "log", "uefi", "conquer-once", "font8x8", "spinning_top"] +binary = ["llvm-tools", "x86_64", "toml", "xmas-elf", "usize_conversions"] vga_320x200 = ["font8x8"] recursive_page_table = [] map_physical_memory = [] diff --git a/bootloader-lib/Cargo.toml b/bootloader-lib/Cargo.toml deleted file mode 100644 index 593b011b..00000000 --- a/bootloader-lib/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "bootloader-lib" -version = "0.1.0" -authors = ["Philipp Oppermann "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -xmas-elf = "0.7.0" -x86_64 = "0.11.0" -log = "0.4.8" -spinning_top = "0.1.0" - -[dependencies.conquer-once] -version = "0.2.0" -default-features = false - -[dependencies.font8x8] -version = "0.2.5" -default-features = false -features = ["unicode"] diff --git a/bootloader-lib/src/lib.rs b/bootloader-lib/src/lib.rs deleted file mode 100644 index 15e5c6ea..00000000 --- a/bootloader-lib/src/lib.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![no_std] -#![feature(slice_fill)] -#![feature(asm)] -#![feature(unsafe_block_in_unsafe_fn)] -#![deny(unsafe_op_in_unsafe_fn)] - -use core::panic::PanicInfo; -pub use logger::{FrameBufferInfo, PixelFormat}; -use x86_64::{ - structures::paging::{FrameAllocator, MapperAllSizes, Size4KiB}, - VirtAddr, -}; - -mod load_kernel; -mod logger; - -pub fn init_logger(framebuffer: &'static mut [u8], info: FrameBufferInfo) { - let logger = logger::LOGGER.get_or_init(move || logger::LockedLogger::new(framebuffer, info)); - log::set_logger(logger).expect("logger already set"); - log::set_max_level(log::LevelFilter::Trace); -} - -pub fn load_kernel( - kernel: &'static [u8], - page_table: &mut impl MapperAllSizes, - frame_allocator: &mut impl FrameAllocator, -) -> VirtAddr { - load_kernel::load_kernel(kernel, page_table, frame_allocator).expect("Failed to parse kernel") -} - diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index b9a5701b..5fc52ecc 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -21,6 +21,7 @@ extern crate rlibc; use bootloader::boot_info_uefi::{BootInfo, FrameBufferInfo}; use bootloader::memory_map::MemoryRegion; use core::{ + panic::PanicInfo, mem::{self, MaybeUninit}, slice, }; @@ -144,7 +145,7 @@ fn set_up_mappings( framebuffer_addr: PhysAddr, framebuffer_size: usize, ) -> Mappings { - let entry_point = bootloader_lib::load_kernel(&KERNEL.0, kernel_page_table, frame_allocator); + let entry_point = bootloader::load_kernel(&KERNEL.0, kernel_page_table, frame_allocator); log::info!("Entry point at: {:#x}", entry_point.as_u64()); // create a stack @@ -351,12 +352,12 @@ fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { 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()) }; - let info = bootloader_lib::FrameBufferInfo { + let info = bootloader::FrameBufferInfo { horizontal_resolution: mode_info.resolution().0, vertical_resolution: mode_info.resolution().1, pixel_format: match mode_info.pixel_format() { - PixelFormat::RGB => bootloader_lib::PixelFormat::BGR, - PixelFormat::BGR => bootloader_lib::PixelFormat::BGR, + PixelFormat::RGB => bootloader::PixelFormat::BGR, + PixelFormat::BGR => bootloader::PixelFormat::BGR, PixelFormat::Bitmask | PixelFormat::BltOnly => { panic!("Bitmask and BltOnly framebuffers are not supported") } @@ -364,7 +365,7 @@ fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { stride: mode_info.stride(), }; - bootloader_lib::init_logger(slice, info); + bootloader::init_logger(slice, info); ( PhysAddr::new(framebuffer.as_mut_ptr() as u64), @@ -537,7 +538,7 @@ fn kernel_stack_start_location() -> Page { #[panic_handler] fn panic(info: &PanicInfo) -> ! { - unsafe { logger::LOGGER.get().map(|l| l.force_unlock()) }; + unsafe { bootloader::logger::LOGGER.get().map(|l| l.force_unlock()) }; log::error!("{}", info); loop { unsafe { asm!("cli; hlt") }; diff --git a/src/lib.rs b/src/lib.rs index 5c8f3f03..71e752f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,16 +6,34 @@ //! blog for an explanation. #![no_std] -#![warn(missing_docs)] #![feature(min_const_generics)] +#![feature(slice_fill)] +#![feature(asm)] +#![feature(unsafe_block_in_unsafe_fn)] +#![deny(unsafe_op_in_unsafe_fn)] +//#![warn(missing_docs)] pub use crate::bootinfo::BootInfo; +use core::panic::PanicInfo; +#[cfg(feature = "uefi_bin")] +pub use logger::{FrameBufferInfo, PixelFormat}; +#[cfg(feature = "uefi_bin")] +use x86_64::{ + structures::paging::{FrameAllocator, MapperAllSizes, Size4KiB}, + VirtAddr, +}; + pub mod bootinfo; pub mod boot_info_uefi; pub mod memory_map; +#[cfg(feature = "uefi_bin")] +mod load_kernel; +#[cfg(feature = "uefi_bin")] +pub mod logger; + #[cfg(target_arch = "x86")] compile_error!( "This crate currently does not support 32-bit protected mode. \ @@ -44,3 +62,19 @@ macro_rules! entry_point { } }; } + +#[cfg(feature = "uefi_bin")] +pub fn init_logger(framebuffer: &'static mut [u8], info: FrameBufferInfo) { + let logger = logger::LOGGER.get_or_init(move || logger::LockedLogger::new(framebuffer, info)); + log::set_logger(logger).expect("logger already set"); + log::set_max_level(log::LevelFilter::Trace); +} + +#[cfg(feature = "uefi_bin")] +pub fn load_kernel( + kernel: &'static [u8], + page_table: &mut impl MapperAllSizes, + frame_allocator: &mut impl FrameAllocator, +) -> VirtAddr { + load_kernel::load_kernel(kernel, page_table, frame_allocator).expect("Failed to parse kernel") +} diff --git a/bootloader-lib/src/load_kernel.rs b/src/load_kernel.rs similarity index 100% rename from bootloader-lib/src/load_kernel.rs rename to src/load_kernel.rs diff --git a/bootloader-lib/src/logger.rs b/src/logger.rs similarity index 100% rename from bootloader-lib/src/logger.rs rename to src/logger.rs From 0bb58844d26d2592a41b0bf1f97f1adbc5458ac7 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 21 Aug 2020 13:55:14 +0200 Subject: [PATCH 054/174] Merge runner as binary into main bootloader crate --- .cargo/config.toml | 2 +- Cargo.lock | 8 +------- Cargo.toml | 11 ++++++----- runner/Cargo.toml | 10 ---------- runner/src/main.rs => src/bin/runner.rs | 0 5 files changed, 8 insertions(+), 23 deletions(-) delete mode 100644 runner/Cargo.toml rename runner/src/main.rs => src/bin/runner.rs (100%) diff --git a/.cargo/config.toml b/.cargo/config.toml index 2c22df37..1b209e06 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,5 @@ [target.x86_64-unknown-uefi] -runner = "cargo run --manifest-path runner/Cargo.toml --target x86_64-unknown-linux-gnu" +runner = "cargo run --bin runner --features runner" [alias] builder = "run --bin builder --features builder --" diff --git a/Cargo.lock b/Cargo.lock index b2dcaaa2..afd1b058 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,6 +57,7 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" name = "bootloader" version = "0.9.8" dependencies = [ + "anyhow", "argh", "bit_field 0.10.0", "conquer-once", @@ -175,13 +176,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" -[[package]] -name = "runner" -version = "0.1.0" -dependencies = [ - "anyhow", -] - [[package]] name = "scopeguard" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 66b660f2..17441e45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,15 +8,14 @@ repository = "https://github.com/rust-osdev/bootloader" edition = "2018" build = "build.rs" -[workspace] -members = [ - "runner", -] - [[bin]] name = "builder" required-features = ["builder"] +[[bin]] +name = "runner" +required-features = ["runner"] + [[bin]] name = "bios" required-features = ["bios_bin"] @@ -38,6 +37,7 @@ argh = { version = "0.1.3", optional = true } displaydoc = { version = "0.1.7", optional = true } conquer-once = { version = "0.2.1", optional = true, default-features = false } spinning_top = { version = "0.2.1", optional = true } +anyhow = { version = "1.0.32", optional = true } [dependencies.font8x8] version = "0.2.5" @@ -52,6 +52,7 @@ toml = { version = "0.5.1", optional = true } [features] default = [] builder = ["argh", "displaydoc"] +runner = ["anyhow"] bios_bin = ["binary", "fixedvec", "rlibc"] uefi_bin = ["binary", "rlibc", "log", "uefi", "conquer-once", "font8x8", "spinning_top"] binary = ["llvm-tools", "x86_64", "toml", "xmas-elf", "usize_conversions"] diff --git a/runner/Cargo.toml b/runner/Cargo.toml deleted file mode 100644 index 976b96df..00000000 --- a/runner/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "runner" -version = "0.1.0" -authors = ["Philipp Oppermann "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow = "1.0.28" diff --git a/runner/src/main.rs b/src/bin/runner.rs similarity index 100% rename from runner/src/main.rs rename to src/bin/runner.rs From 6005ec705a315303f55190bde39c0f26213c5f91 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 21 Aug 2020 13:57:40 +0200 Subject: [PATCH 055/174] Remove old aliases --- .cargo/config.toml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 1b209e06..01a3adc1 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,10 +3,3 @@ runner = "cargo run --bin runner --features runner" [alias] builder = "run --bin builder --features builder --" - -x = "-Z build-std=core,alloc" - -build-uefi = "x build --target x86_64-unknown-uefi --bin uefi --features uefi_bin" -run-uefi = "x run --target x86_64-unknown-uefi --bin uefi --features uefi_bin" - -build-bios = "x build --target x86_64-bootloader.json --bin bios --features bios_bin" From 41b5d51b321bd5b22c8933f65bae878b8417c833 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 21 Aug 2020 14:18:28 +0200 Subject: [PATCH 056/174] Reserve one more slot for memory regions We might split a partly used region into two. --- 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 5fc52ecc..a5a64a4c 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -210,7 +210,7 @@ where let boot_info_end = boot_info_addr + mem::size_of::(); let memory_map_regions_addr = boot_info_end.align_up(u64::from_usize(mem::align_of::())); - let regions = frame_allocator.len(); + let regions = frame_allocator.len() + 1; // one region might be split into used/unused let memory_map_regions_end = memory_map_regions_addr + regions * mem::size_of::(); From dac768137da62cebb6ca7f7a7c06434a3dbc591f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 21 Aug 2020 15:58:32 +0200 Subject: [PATCH 057/174] Integrate bios binary into new structure; add disk image creation from bootimage --- Cargo.lock | 28 ++++++ Cargo.toml | 9 +- build.rs | 2 + src/{ => asm}/e820.s | 0 src/{ => asm}/stage_1.s | 0 src/{ => asm}/stage_2.s | 0 src/{ => asm}/stage_3.s | 0 src/{ => asm}/video_mode/vga_320x200.s | 0 src/{ => asm}/video_mode/vga_text_80x25.s | 0 src/bin/bios.rs | 43 +++------ src/bin/builder.rs | 81 +++++++++++++++-- src/bin/runner.rs | 1 - src/bin/uefi.rs | 11 ++- src/boot_info.rs | 4 +- src/disk_image.rs | 106 ++++++++++++++++++++++ src/frame_allocator.rs | 8 +- src/lib.rs | 41 ++++++++- src/page_table.rs | 24 ++--- 18 files changed, 292 insertions(+), 66 deletions(-) rename src/{ => asm}/e820.s (100%) rename src/{ => asm}/stage_1.s (100%) rename src/{ => asm}/stage_2.s (100%) rename src/{ => asm}/stage_3.s (100%) rename src/{ => asm}/video_mode/vga_320x200.s (100%) rename src/{ => asm}/video_mode/vga_text_80x25.s (100%) create mode 100644 src/disk_image.rs diff --git a/Cargo.lock b/Cargo.lock index afd1b058..7b59ce4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,10 +64,12 @@ dependencies = [ "displaydoc", "fixedvec", "font8x8", + "json", "llvm-tools", "log", "rlibc", "spinning_top", + "thiserror", "toml", "uefi", "usize_conversions", @@ -128,6 +130,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "json" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" + [[package]] name = "llvm-tools" version = "0.1.1" @@ -208,6 +216,26 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "thiserror" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "toml" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 17441e45..e550df27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,9 @@ displaydoc = { version = "0.1.7", optional = true } conquer-once = { version = "0.2.1", optional = true, default-features = false } spinning_top = { version = "0.2.1", optional = true } anyhow = { version = "1.0.32", optional = true } +llvm-tools = { version = "0.1.1", optional = true } +thiserror = { version = "1.0.20", optional = true } +json = { version = "0.12.4", optional = true } [dependencies.font8x8] version = "0.2.5" @@ -46,16 +49,16 @@ features = ["unicode"] optional = true [build-dependencies] -llvm-tools = { version = "0.1", optional = true } +llvm-tools-build = { version = "0.1", optional = true, package = "llvm-tools" } toml = { version = "0.5.1", optional = true } [features] default = [] -builder = ["argh", "displaydoc"] +builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json"] runner = ["anyhow"] bios_bin = ["binary", "fixedvec", "rlibc"] uefi_bin = ["binary", "rlibc", "log", "uefi", "conquer-once", "font8x8", "spinning_top"] -binary = ["llvm-tools", "x86_64", "toml", "xmas-elf", "usize_conversions"] +binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions"] vga_320x200 = ["font8x8"] recursive_page_table = [] map_physical_memory = [] diff --git a/build.rs b/build.rs index 682d4830..f01dc95b 100644 --- a/build.rs +++ b/build.rs @@ -86,6 +86,7 @@ fn parse_to_config(cfg: &mut BootloaderConfig, table: &toml::value::Table) { #[cfg(feature = "bios_bin")] fn main() { + use llvm_tools_build as llvm_tools; use std::{ env, fs::{self, File}, @@ -300,6 +301,7 @@ fn main() { #[cfg(feature = "uefi_bin")] fn main() { + use llvm_tools_build as llvm_tools; use std::{ env, fs::{self, File}, diff --git a/src/e820.s b/src/asm/e820.s similarity index 100% rename from src/e820.s rename to src/asm/e820.s diff --git a/src/stage_1.s b/src/asm/stage_1.s similarity index 100% rename from src/stage_1.s rename to src/asm/stage_1.s diff --git a/src/stage_2.s b/src/asm/stage_2.s similarity index 100% rename from src/stage_2.s rename to src/asm/stage_2.s diff --git a/src/stage_3.s b/src/asm/stage_3.s similarity index 100% rename from src/stage_3.s rename to src/asm/stage_3.s diff --git a/src/video_mode/vga_320x200.s b/src/asm/video_mode/vga_320x200.s similarity index 100% rename from src/video_mode/vga_320x200.s rename to src/asm/video_mode/vga_320x200.s diff --git a/src/video_mode/vga_text_80x25.s b/src/asm/video_mode/vga_text_80x25.s similarity index 100% rename from src/video_mode/vga_text_80x25.s rename to src/asm/video_mode/vga_text_80x25.s diff --git a/src/bin/bios.rs b/src/bin/bios.rs index 3ccac477..b89e2c07 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -9,7 +9,10 @@ compile_error!("The bootloader crate must be compiled for the `x86_64-bootloader extern crate rlibc; -use bootloader::bootinfo::{BootInfo, FrameRange}; +use bootloader::{ + boot_info, bootinfo::BootInfo, frame_allocator, frame_range, level4_entries, page_table, + printer, +}; use core::convert::TryInto; use core::panic::PanicInfo; use core::{mem, slice}; @@ -17,8 +20,8 @@ use fixedvec::alloc_stack; use usize_conversions::usize_from; use x86_64::instructions::tlb; use x86_64::structures::paging::{ - frame::PhysFrameRange, page_table::PageTableEntry, Mapper, Page, PageTable, PageTableFlags, - PageTableIndex, PhysFrame, RecursivePageTable, Size2MiB, Size4KiB, + page_table::PageTableEntry, Mapper, Page, PageTable, PageTableFlags, PageTableIndex, PhysFrame, + RecursivePageTable, Size2MiB, Size4KiB, }; use x86_64::{PhysAddr, VirtAddr}; @@ -31,15 +34,15 @@ use x86_64::{PhysAddr, VirtAddr}; // KERNEL_STACK_SIZE: The number of pages in the kernel stack. include!(concat!(env!("OUT_DIR"), "/bootloader_config.rs")); -global_asm!(include_str!("stage_1.s")); -global_asm!(include_str!("stage_2.s")); -global_asm!(include_str!("e820.s")); -global_asm!(include_str!("stage_3.s")); +global_asm!(include_str!("../asm/stage_1.s")); +global_asm!(include_str!("../asm/stage_2.s")); +global_asm!(include_str!("../asm/e820.s")); +global_asm!(include_str!("../asm/stage_3.s")); #[cfg(feature = "vga_320x200")] -global_asm!(include_str!("video_mode/vga_320x200.s")); +global_asm!(include_str!("../asm/video_mode/vga_320x200.s")); #[cfg(not(feature = "vga_320x200"))] -global_asm!(include_str!("video_mode/vga_text_80x25.s")); +global_asm!(include_str!("../asm/video_mode/vga_text_80x25.s")); unsafe fn context_switch(boot_info: VirtAddr, entry_point: VirtAddr, stack_pointer: VirtAddr) -> ! { llvm_asm!("call $1; ${:private}.spin.${:uid}: jmp ${:private}.spin.${:uid}" :: @@ -47,14 +50,6 @@ unsafe fn context_switch(boot_info: VirtAddr, entry_point: VirtAddr, stack_point ::core::hint::unreachable_unchecked() } -mod boot_info; -mod frame_allocator; -mod level4_entries; -mod page_table; -mod printer; -#[cfg(feature = "sse")] -mod sse; - pub struct IdentityMappedAddr(PhysAddr); impl IdentityMappedAddr { @@ -386,17 +381,3 @@ pub extern "C" fn eh_personality() { pub extern "C" fn _Unwind_Resume() { loop {} } - -fn phys_frame_range(range: FrameRange) -> PhysFrameRange { - PhysFrameRange { - start: PhysFrame::from_start_address(PhysAddr::new(range.start_addr())).unwrap(), - end: PhysFrame::from_start_address(PhysAddr::new(range.end_addr())).unwrap(), - } -} - -fn frame_range(range: PhysFrameRange) -> FrameRange { - FrameRange::new( - range.start.start_address().as_u64(), - range.end.start_address().as_u64(), - ) -} diff --git a/src/bin/builder.rs b/src/bin/builder.rs index 4e73214f..6dd8c2ee 100644 --- a/src/bin/builder.rs +++ b/src/bin/builder.rs @@ -1,5 +1,13 @@ +use anyhow::{anyhow, Context}; use argh::FromArgs; -use std::{fmt, path::PathBuf, process::Command, str::FromStr}; +use bootloader::disk_image::create_disk_image; +use std::{ + path::{Path, PathBuf}, + process::Command, + str::FromStr, +}; + +type ExitCode = i32; #[derive(FromArgs)] /// Build the bootloader @@ -45,12 +53,11 @@ impl FromStr for Firmware { #[derive(Debug, displaydoc::Display, Eq, PartialEq, Copy, Clone)] struct FirmwareParseError; -fn main() { +fn main() -> anyhow::Result<()> { let args: BuildArguments = argh::from_env(); - let build_or_run = if args.run { "run" } else { "build" }; - if args.firmware == Firmware::Uefi || args.firmware == Firmware::All { + let build_or_run = if args.run { "run" } else { "build" }; let mut cmd = Command::new(env!("CARGO")); cmd.arg(build_or_run).arg("--bin").arg("uefi"); cmd.arg("--target").arg("x86_64-unknown-uefi"); @@ -58,17 +65,77 @@ fn main() { cmd.arg("-Zbuild-std=core"); cmd.env("KERNEL", &args.kernel_binary); cmd.env("KERNEL_MANIFEST", &args.kernel_manifest); - cmd.status(); + assert!(cmd.status()?.success()); } if args.firmware == Firmware::Bios || args.firmware == Firmware::All { let mut cmd = Command::new(env!("CARGO")); - cmd.arg(build_or_run).arg("--bin").arg("bios"); + cmd.arg("build").arg("--bin").arg("bios"); + cmd.arg("--profile").arg("release"); + cmd.arg("-Z").arg("unstable-options"); cmd.arg("--target").arg("x86_64-bootloader.json"); cmd.arg("--features").arg("bios_bin"); cmd.arg("-Zbuild-std=core"); cmd.env("KERNEL", &args.kernel_binary); cmd.env("KERNEL_MANIFEST", &args.kernel_manifest); - cmd.status(); + cmd.env("RUSTFLAGS", "-C opt-level=s"); + assert!(cmd.status()?.success()); + + // Retrieve binary paths + cmd.arg("--message-format").arg("json"); + let output = cmd + .output() + .context("failed to execute kernel build with json output")?; + if !output.status.success() { + return Err(anyhow!("{}", String::from_utf8_lossy(&output.stderr))); + } + let mut executables = Vec::new(); + for line in String::from_utf8(output.stdout) + .context("build JSON output is not valid UTF-8")? + .lines() + { + let mut artifact = json::parse(line).context("build JSON output is not valid JSON")?; + if let Some(executable) = artifact["executable"].take_string() { + executables.push(PathBuf::from(executable)); + } + } + + assert_eq!(executables.len(), 1); + let executable_path = executables.pop().unwrap(); + let executable_name = executable_path.file_name().unwrap().to_str().unwrap(); + let output_bin_path = executable_path + .parent() + .unwrap() + .join(format!("bootimage-{}.bin", executable_name)); + + create_disk_image(&executable_path, &output_bin_path) + .context("Failed to create bootable disk image")?; + + println!( + "Created bootable disk image at {}", + output_bin_path.display() + ); + + if args.run { + bios_run(&output_bin_path)?; + } } + + Ok(()) +} + +fn bios_run(bin_path: &Path) -> anyhow::Result> { + let mut qemu = Command::new("qemu-system-x86_64"); + qemu.arg("-drive") + .arg(format!("format=raw,file={}", bin_path.display())); + qemu.arg("-s"); + qemu.arg("--no-reboot"); + println!("{:?}", qemu); + let exit_status = qemu.status()?; + let ret = if exit_status.success() { + None + } else { + exit_status.code() + }; + Ok(ret) } diff --git a/src/bin/runner.rs b/src/bin/runner.rs index 3a41d08e..e572db4d 100644 --- a/src/bin/runner.rs +++ b/src/bin/runner.rs @@ -57,7 +57,6 @@ fn runner(file_path: &Path) -> anyhow::Result> { qemu.arg("-s"); qemu.arg("-nodefaults"); qemu.arg("-vga").arg("std"); - qemu.arg("-d").arg("int"); qemu.arg("--no-reboot"); println!("{:?}", qemu); let exit_status = qemu.status()?; diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index a5a64a4c..02cdee16 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -21,8 +21,8 @@ extern crate rlibc; use bootloader::boot_info_uefi::{BootInfo, FrameBufferInfo}; use bootloader::memory_map::MemoryRegion; use core::{ - panic::PanicInfo, mem::{self, MaybeUninit}, + panic::PanicInfo, slice, }; use uefi::{ @@ -87,7 +87,7 @@ fn create_page_tables(frame_allocator: &mut impl FrameAllocator) -> Pa // copy the currently active level 4 page table, because it might be read-only log::trace!("switching to new level 4 table"); - let mut bootloader_page_table = { + let bootloader_page_table = { let old_frame = x86_64::registers::control::Cr3::read().0; let old_table: *const PageTable = (phys_offset + old_frame.start_address().as_u64()).as_ptr(); @@ -104,12 +104,12 @@ fn create_page_tables(frame_allocator: &mut impl FrameAllocator) -> Pa new_frame, x86_64::registers::control::Cr3Flags::empty(), ); - unsafe { OffsetPageTable::new(&mut *new_table, phys_offset) } + OffsetPageTable::new(&mut *new_table, phys_offset) } }; // create a new page table hierarchy for the kernel - let (mut kernel_page_table, kernel_level_4_frame) = { + let (kernel_page_table, kernel_level_4_frame) = { // get an unused frame for new level 4 page table let frame: PhysFrame = frame_allocator.allocate_frame().expect("no unused frames"); log::info!("New page table at: {:#?}", &frame); @@ -436,7 +436,8 @@ where end: next_free, kind: MemoryRegionKind::Bootloader, }; - Self::add_region(used_region, regions, &mut next_index); + Self::add_region(used_region, regions, &mut next_index) + .expect("Failed to add memory region"); // add unused part normally descriptor.phys_start = next_free; diff --git a/src/boot_info.rs b/src/boot_info.rs index f50cf2a5..fb7be909 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -1,10 +1,10 @@ use core::slice; -use bootloader::bootinfo::{E820MemoryRegion, MemoryMap, MemoryRegion, MemoryRegionType}; +use crate::bootinfo::{E820MemoryRegion, MemoryMap, MemoryRegion, MemoryRegionType}; use usize_conversions::usize_from; use x86_64::VirtAddr; -pub(crate) fn create_from(memory_map_addr: VirtAddr, entry_count: u64) -> MemoryMap { +pub fn create_from(memory_map_addr: VirtAddr, entry_count: u64) -> MemoryMap { let memory_map_start_ptr = usize_from(memory_map_addr.as_u64()) as *const E820MemoryRegion; let e820_memory_map = unsafe { slice::from_raw_parts(memory_map_start_ptr, usize_from(entry_count)) }; diff --git a/src/disk_image.rs b/src/disk_image.rs new file mode 100644 index 00000000..e274ee64 --- /dev/null +++ b/src/disk_image.rs @@ -0,0 +1,106 @@ +use std::{io, path::Path, process::Command}; +use thiserror::Error; + +pub fn create_disk_image( + bootloader_elf_path: &Path, + output_bin_path: &Path, +) -> Result<(), DiskImageError> { + let llvm_tools = llvm_tools::LlvmTools::new()?; + let objcopy = llvm_tools + .tool(&llvm_tools::exe("llvm-objcopy")) + .ok_or(DiskImageError::LlvmObjcopyNotFound)?; + + // convert bootloader to binary + let mut cmd = Command::new(objcopy); + cmd.arg("-I").arg("elf64-x86-64"); + cmd.arg("-O").arg("binary"); + cmd.arg("--binary-architecture=i386:x86-64"); + cmd.arg(bootloader_elf_path); + cmd.arg(output_bin_path); + let output = cmd.output().map_err(|err| DiskImageError::Io { + message: "failed to execute llvm-objcopy command", + error: err, + })?; + if !output.status.success() { + return Err(DiskImageError::ObjcopyFailed { + stderr: output.stderr, + }); + } + + pad_to_nearest_block_size(output_bin_path)?; + Ok(()) +} + +fn pad_to_nearest_block_size(output_bin_path: &Path) -> Result<(), DiskImageError> { + const BLOCK_SIZE: u64 = 512; + use std::fs::OpenOptions; + let file = OpenOptions::new() + .write(true) + .open(&output_bin_path) + .map_err(|err| DiskImageError::Io { + message: "failed to open boot image", + error: err, + })?; + let file_size = file + .metadata() + .map_err(|err| DiskImageError::Io { + message: "failed to get size of boot image", + error: err, + })? + .len(); + let remainder = file_size % BLOCK_SIZE; + let padding = if remainder > 0 { + BLOCK_SIZE - remainder + } else { + 0 + }; + file.set_len(file_size + padding) + .map_err(|err| DiskImageError::Io { + message: "failed to pad boot image to a multiple of the block size", + error: err, + }) +} + +/// Creating the disk image failed. +#[derive(Debug, Error)] +pub enum DiskImageError { + /// The `llvm-tools-preview` rustup component was not found + #[error( + "Could not find the `llvm-tools-preview` rustup component.\n\n\ + You can install by executing `rustup component add llvm-tools-preview`." + )] + LlvmToolsNotFound, + + /// There was another problem locating the `llvm-tools-preview` rustup component + #[error("Failed to locate the `llvm-tools-preview` rustup component: {0:?}")] + LlvmTools(llvm_tools::Error), + + /// The llvm-tools component did not contain the required `llvm-objcopy` executable + #[error("Could not find `llvm-objcopy` in the `llvm-tools-preview` rustup component.")] + LlvmObjcopyNotFound, + + /// The `llvm-objcopy` command failed + #[error("Failed to run `llvm-objcopy`: {}", String::from_utf8_lossy(.stderr))] + ObjcopyFailed { + /// The output of `llvm-objcopy` to standard error + stderr: Vec, + }, + + /// An unexpected I/O error occurred + #[error("I/O error: {message}:\n{error}")] + Io { + /// Desciption of the failed I/O operation + message: &'static str, + /// The I/O error that occured + error: io::Error, + }, +} + +impl From for DiskImageError { + fn from(err: llvm_tools::Error) -> Self { + match err { + llvm_tools::Error::NotFound => DiskImageError::LlvmToolsNotFound, + other => DiskImageError::LlvmTools(other), + } + } +} diff --git a/src/frame_allocator.rs b/src/frame_allocator.rs index 5b77cdb2..1683fe29 100644 --- a/src/frame_allocator.rs +++ b/src/frame_allocator.rs @@ -1,13 +1,13 @@ use super::{frame_range, phys_frame_range}; -use bootloader::bootinfo::{MemoryMap, MemoryRegion, MemoryRegionType}; +use crate::bootinfo::{MemoryMap, MemoryRegion, MemoryRegionType}; use x86_64::structures::paging::{frame::PhysFrameRange, PhysFrame}; -pub(crate) struct FrameAllocator<'a> { +pub struct FrameAllocator<'a> { pub memory_map: &'a mut MemoryMap, } impl<'a> FrameAllocator<'a> { - pub(crate) fn allocate_frame(&mut self, region_type: MemoryRegionType) -> Option { + pub fn allocate_frame(&mut self, region_type: MemoryRegionType) -> Option { // try to find an existing region of same type that can be enlarged let mut iter = self.memory_map.iter_mut().peekable(); while let Some(region) = iter.next() { @@ -66,7 +66,7 @@ impl<'a> FrameAllocator<'a> { /// Marks the passed region in the memory map. /// /// Panics if a non-usable region (e.g. a reserved region) overlaps with the passed region. - pub(crate) fn mark_allocated_region(&mut self, region: MemoryRegion) { + pub fn mark_allocated_region(&mut self, region: MemoryRegion) { for r in self.memory_map.iter_mut() { if region.range.start_frame_number >= r.range.end_frame_number { continue; diff --git a/src/lib.rs b/src/lib.rs index 71e752f2..cecebec2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ //! [_Writing an OS in Rust_](https://os.phil-opp.com/minimal-rust-kernel/#creating-a-bootimage) //! blog for an explanation. -#![no_std] +#![cfg_attr(not(feature = "builder"), no_std)] #![feature(min_const_generics)] #![feature(slice_fill)] #![feature(asm)] @@ -15,9 +15,13 @@ pub use crate::bootinfo::BootInfo; -use core::panic::PanicInfo; #[cfg(feature = "uefi_bin")] pub use logger::{FrameBufferInfo, PixelFormat}; +#[cfg(feature = "bios_bin")] +use x86_64::{ + structures::paging::{frame::PhysFrameRange, PhysFrame}, + PhysAddr, +}; #[cfg(feature = "uefi_bin")] use x86_64::{ structures::paging::{FrameAllocator, MapperAllSizes, Size4KiB}, @@ -29,11 +33,28 @@ pub mod bootinfo; pub mod boot_info_uefi; pub mod memory_map; +#[cfg(feature = "builder")] +pub mod disk_image; + #[cfg(feature = "uefi_bin")] mod load_kernel; #[cfg(feature = "uefi_bin")] pub mod logger; +#[cfg(feature = "bios_bin")] +pub mod boot_info; +#[cfg(feature = "bios_bin")] +pub mod frame_allocator; +#[cfg(feature = "bios_bin")] +pub mod level4_entries; +#[cfg(feature = "bios_bin")] +pub mod page_table; +#[cfg(feature = "bios_bin")] +pub mod printer; + +#[cfg(all(feature = "bios_bin", feature = "sse"))] +pub mod sse; + #[cfg(target_arch = "x86")] compile_error!( "This crate currently does not support 32-bit protected mode. \ @@ -78,3 +99,19 @@ pub fn load_kernel( ) -> VirtAddr { load_kernel::load_kernel(kernel, page_table, frame_allocator).expect("Failed to parse kernel") } + +#[cfg(feature = "bios_bin")] +pub fn phys_frame_range(range: bootinfo::FrameRange) -> PhysFrameRange { + PhysFrameRange { + start: PhysFrame::from_start_address(PhysAddr::new(range.start_addr())).unwrap(), + end: PhysFrame::from_start_address(PhysAddr::new(range.end_addr())).unwrap(), + } +} + +#[cfg(feature = "bios_bin")] +pub fn frame_range(range: PhysFrameRange) -> bootinfo::FrameRange { + bootinfo::FrameRange::new( + range.start.start_address().as_u64(), + range.end.start_address().as_u64(), + ) +} diff --git a/src/page_table.rs b/src/page_table.rs index ed2ac136..80d1b9c4 100644 --- a/src/page_table.rs +++ b/src/page_table.rs @@ -1,6 +1,6 @@ +use crate::bootinfo::MemoryRegionType; +use crate::bootinfo::TlsTemplate; use crate::frame_allocator::FrameAllocator; -use bootloader::bootinfo::MemoryRegionType; -use bootloader::bootinfo::TlsTemplate; use fixedvec::FixedVec; use x86_64::structures::paging::mapper::{MapToError, MapperFlush, UnmapError}; use x86_64::structures::paging::{ @@ -27,7 +27,7 @@ impl From> for MapKernelError { } } -pub(crate) fn map_kernel( +pub fn map_kernel( kernel_start: PhysAddr, stack_start: Page, stack_size: u64, @@ -65,7 +65,7 @@ pub(crate) fn map_kernel( }) } -pub(crate) fn map_segment( +pub fn map_segment( segment: &ProgramHeader64, kernel_start: PhysAddr, page_table: &mut RecursivePageTable, @@ -191,7 +191,7 @@ pub(crate) fn map_segment( } } -pub(crate) unsafe fn map_page<'a, S>( +pub unsafe fn map_page<'a, S>( page: Page, phys_frame: PhysFrame, flags: PageTableFlags, @@ -210,10 +210,12 @@ where } } - page_table.map_to( - page, - phys_frame, - flags, - &mut PageTableAllocator(frame_allocator), - ) + unsafe { + page_table.map_to( + page, + phys_frame, + flags, + &mut PageTableAllocator(frame_allocator), + ) + } } From dfee1c72d12ee82a5d684a188f036a4d47c10aa4 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 23 Aug 2020 19:37:23 +0200 Subject: [PATCH 058/174] Use `--quiet` for cargo run of builder executable --- .cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 01a3adc1..1991ea6f 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -2,4 +2,4 @@ runner = "cargo run --bin runner --features runner" [alias] -builder = "run --bin builder --features builder --" +builder = "run --bin builder --features builder --quiet --" From 09c4aee6d50f6227de9de8b220d01bff421b5494 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 23 Aug 2020 19:38:54 +0200 Subject: [PATCH 059/174] Update builder executable with more flags --- src/bin/builder.rs | 54 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/bin/builder.rs b/src/bin/builder.rs index 6dd8c2ee..e82a724a 100644 --- a/src/bin/builder.rs +++ b/src/bin/builder.rs @@ -2,6 +2,7 @@ use anyhow::{anyhow, Context}; use argh::FromArgs; use bootloader::disk_image::create_disk_image; use std::{ + fs, path::{Path, PathBuf}, process::Command, str::FromStr, @@ -27,6 +28,22 @@ struct BuildArguments { /// whether to run the resulting binary in QEMU #[argh(switch)] run: bool, + + /// suppress stdout output + #[argh(switch)] + quiet: bool, + + /// build the bootloader with the given cargo features + #[argh(option)] + features: Vec, + + /// use the given path as target directory + #[argh(option)] + target_dir: Option, + + /// place the output binaries at the given path + #[argh(option)] + out_dir: Option, } #[derive(Debug, Eq, PartialEq, Copy, Clone)] @@ -61,8 +78,14 @@ fn main() -> anyhow::Result<()> { let mut cmd = Command::new(env!("CARGO")); cmd.arg(build_or_run).arg("--bin").arg("uefi"); cmd.arg("--target").arg("x86_64-unknown-uefi"); - cmd.arg("--features").arg("uefi_bin"); + cmd.arg("--features").arg(args.features.join(" ") + " uefi_bin"); cmd.arg("-Zbuild-std=core"); + if let Some(target_dir) = &args.target_dir { + cmd.arg("--target-dir").arg(target_dir); + } + if args.quiet { + cmd.arg("--quiet"); + } cmd.env("KERNEL", &args.kernel_binary); cmd.env("KERNEL_MANIFEST", &args.kernel_manifest); assert!(cmd.status()?.success()); @@ -74,8 +97,14 @@ fn main() -> anyhow::Result<()> { cmd.arg("--profile").arg("release"); cmd.arg("-Z").arg("unstable-options"); cmd.arg("--target").arg("x86_64-bootloader.json"); - cmd.arg("--features").arg("bios_bin"); + cmd.arg("--features").arg(args.features.join(" ") + " bios_bin"); cmd.arg("-Zbuild-std=core"); + if let Some(target_dir) = &args.target_dir { + cmd.arg("--target-dir").arg(target_dir); + } + if args.quiet { + cmd.arg("--quiet"); + } cmd.env("KERNEL", &args.kernel_binary); cmd.env("KERNEL_MANIFEST", &args.kernel_manifest); cmd.env("RUSTFLAGS", "-C opt-level=s"); @@ -103,18 +132,27 @@ fn main() -> anyhow::Result<()> { assert_eq!(executables.len(), 1); let executable_path = executables.pop().unwrap(); let executable_name = executable_path.file_name().unwrap().to_str().unwrap(); - let output_bin_path = executable_path + let kernel_name = args.kernel_binary.file_name().unwrap().to_str().unwrap(); + let mut output_bin_path = executable_path .parent() .unwrap() - .join(format!("bootimage-{}.bin", executable_name)); + .join(format!("bootimage-{}-{}.bin", executable_name, kernel_name)); create_disk_image(&executable_path, &output_bin_path) .context("Failed to create bootable disk image")?; - println!( - "Created bootable disk image at {}", - output_bin_path.display() - ); + if let Some(out_dir) = &args.out_dir { + let file = out_dir.join(output_bin_path.file_name().unwrap()); + fs::copy(output_bin_path, &file)?; + output_bin_path = file; + } + + if !args.quiet { + println!( + "Created bootable disk image at {}", + output_bin_path.display() + ); + } if args.run { bios_run(&output_bin_path)?; From 1f1142f67008cfc106fbe69b5b484e10c578a472 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 23 Aug 2020 19:39:59 +0200 Subject: [PATCH 060/174] Make frame allocator generic and move it to lib Our goal is to share this frame allocator with the BIOS executable. --- src/bin/uefi.rs | 143 ++--------------------------- src/legacy_memory_region/bios.rs | 50 ++++++++++ src/legacy_memory_region/mod.rs | 152 +++++++++++++++++++++++++++++++ src/legacy_memory_region/uefi.rs | 23 +++++ src/lib.rs | 4 + 5 files changed, 235 insertions(+), 137 deletions(-) create mode 100644 src/legacy_memory_region/bios.rs create mode 100644 src/legacy_memory_region/mod.rs create mode 100644 src/legacy_memory_region/uefi.rs diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 02cdee16..e43f753b 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -18,6 +18,7 @@ struct PageAligned(T); extern crate rlibc; +use bootloader::legacy_memory_region::LegacyFrameAllocator; use bootloader::boot_info_uefi::{BootInfo, FrameBufferInfo}; use bootloader::memory_map::MemoryRegion; use core::{ @@ -40,8 +41,6 @@ use x86_64::{ PhysAddr, VirtAddr, }; -const PAGE_SIZE: u64 = 4096; - #[entry] fn efi_main(image: Handle, st: SystemTable) -> Status { let (framebuffer_addr, framebuffer_size) = init_logger(&st); @@ -63,7 +62,7 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { .exit_boot_services(image, mmap_storage) .expect_success("Failed to exit boot services"); - let mut frame_allocator = UefiFrameAllocator::new(memory_map); + let mut frame_allocator = LegacyFrameAllocator::new(memory_map.copied()); let mut page_tables = create_page_tables(&mut frame_allocator); let mappings = set_up_mappings( &mut frame_allocator, @@ -193,14 +192,15 @@ struct Mappings { } /// Allocates and initializes the boot info struct and the memory map -fn create_boot_info<'a, I>( - mut frame_allocator: UefiFrameAllocator<'a, I>, +fn create_boot_info( + mut frame_allocator: LegacyFrameAllocator, page_tables: &mut PageTables, framebuffer_virt_addr: VirtAddr, framebuffer_size: usize, ) -> (&'static mut BootInfo, TwoFrames) where - I: Iterator + Clone, + I: ExactSizeIterator + Clone, + D: bootloader::legacy_memory_region::LegacyMemoryRegion, { log::info!("Allocate bootinfo"); @@ -373,137 +373,6 @@ fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { ) } -struct UefiFrameAllocator<'a, I> { - original: I, - memory_map: I, - current_descriptor: Option<&'a MemoryDescriptor>, - next_frame: PhysFrame, -} - -impl<'a, I> UefiFrameAllocator<'a, I> -where - I: Iterator + Clone, -{ - fn new(memory_map: I) -> Self { - Self { - original: memory_map.clone(), - memory_map, - current_descriptor: None, - next_frame: PhysFrame::containing_address(PhysAddr::new(0x1000)), - } - } - - fn allocate_frame_from_descriptor( - &mut self, - descriptor: &MemoryDescriptor, - ) -> Option { - let start_addr = PhysAddr::new(descriptor.phys_start); - let start_frame: PhysFrame = PhysFrame::containing_address(start_addr.align_up(PAGE_SIZE)); - let end_frame = start_frame + descriptor.page_count; - if self.next_frame < end_frame { - let ret = self.next_frame; - self.next_frame += 1; - Some(ret) - } else { - None - } - } - - fn len(&self) -> usize { - self.original.clone().count() // TODO ExactSizeIterator implementation possible? - } - - fn construct_memory_map( - self, - regions: &mut [MaybeUninit], - ) -> &mut [MemoryRegion] { - use bootloader::memory_map::MemoryRegionKind; - - let mut next_index = 0; - - for mut descriptor in self.original.copied() { - let end = descriptor.phys_start + PAGE_SIZE * descriptor.page_count; - let next_free = self.next_frame.start_address().as_u64(); - let kind = match descriptor.ty { - MemoryType::CONVENTIONAL if end <= next_free => MemoryRegionKind::Bootloader, - MemoryType::CONVENTIONAL if descriptor.phys_start >= next_free => { - MemoryRegionKind::Usable - } - MemoryType::CONVENTIONAL => { - // part of the region is used -> add is separately - let used_region = MemoryRegion { - start: descriptor.phys_start, - end: next_free, - kind: MemoryRegionKind::Bootloader, - }; - Self::add_region(used_region, regions, &mut next_index) - .expect("Failed to add memory region"); - - // add unused part normally - descriptor.phys_start = next_free; - MemoryRegionKind::Usable - } - MemoryType::RESERVED => MemoryRegionKind::Reserved, - other => continue, - }; - let region = MemoryRegion { - start: descriptor.phys_start, - end, - kind, - }; - Self::add_region(region, regions, &mut next_index); - } - - let initialized = &mut regions[..next_index]; - unsafe { MaybeUninit::slice_get_mut(initialized) } - } - - fn add_region( - region: MemoryRegion, - regions: &mut [MaybeUninit], - next_index: &mut usize, - ) -> Result<(), ()> { - unsafe { - regions - .get_mut(*next_index) - .ok_or(())? - .as_mut_ptr() - .write(region) - }; - *next_index += 1; - Ok(()) - } -} - -unsafe impl<'a, I> FrameAllocator for UefiFrameAllocator<'a, I> -where - I: Iterator + Clone, -{ - fn allocate_frame(&mut self) -> Option> { - if let Some(current_descriptor) = self.current_descriptor { - match self.allocate_frame_from_descriptor(current_descriptor) { - Some(frame) => return Some(frame), - None => { - self.current_descriptor = None; - } - } - } - - // find next suitable descriptor - while let Some(descriptor) = self.memory_map.next() { - if descriptor.ty != MemoryType::CONVENTIONAL { - continue; - } - if let Some(frame) = self.allocate_frame_from_descriptor(descriptor) { - self.current_descriptor = Some(descriptor); - return Some(frame); - } - } - - None - } -} - pub struct TwoFrames { frames: [Option; 2], } diff --git a/src/legacy_memory_region/bios.rs b/src/legacy_memory_region/bios.rs new file mode 100644 index 00000000..6e3a473b --- /dev/null +++ b/src/legacy_memory_region/bios.rs @@ -0,0 +1,50 @@ +use x86_64::PhysAddr; +use crate::legacy_memory_region::LegacyMemoryRegion; + +impl LegacyMemoryRegion for E820MemoryRegion { + fn start(&self) -> PhysAddr { + PhysAddr::new(self.start_addr) + } + + fn len(&self) -> u64 { + self.len + } + + fn usable(&self) -> bool { + self.region_type == 1 + } + + fn set_start(&mut self, new_start: PhysAddr) { + self.start_addr = new_start.as_u64(); + } +} + + +#[doc(hidden)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C)] +pub struct E820MemoryRegion { + pub start_addr: u64, + pub len: u64, + pub region_type: u32, + pub acpi_extended_attributes: u32, +} + +/* +impl From for MemoryRegion { + fn from(region: E820MemoryRegion) -> MemoryRegion { + let region_type = match region.region_type { + 1 => MemoryRegionType::Usable, + 2 => MemoryRegionType::Reserved, + 3 => MemoryRegionType::AcpiReclaimable, + 4 => MemoryRegionType::AcpiNvs, + 5 => MemoryRegionType::BadMemory, + t => panic!("invalid region type {}", t), + }; + MemoryRegion { + range: FrameRange::new(region.start_addr, region.start_addr + region.len), + region_type, + } + } +} +*/ \ No newline at end of file diff --git a/src/legacy_memory_region/mod.rs b/src/legacy_memory_region/mod.rs new file mode 100644 index 00000000..3b46ca1a --- /dev/null +++ b/src/legacy_memory_region/mod.rs @@ -0,0 +1,152 @@ +use x86_64::{PhysAddr, structures::paging::{FrameAllocator, PhysFrame, Size4KiB}}; +use crate::memory_map::MemoryRegion; +use core::mem::MaybeUninit; + +const PAGE_SIZE: u64 = 4096; + +#[cfg(feature = "bios_bin")] +pub mod bios; +#[cfg(feature = "uefi_bin")] +pub mod uefi; + +pub trait LegacyMemoryRegion: Copy { + fn start(&self) -> PhysAddr; + fn len(&self) -> u64; + fn usable(&self) -> bool; + + fn set_start(&mut self, new_start: PhysAddr); +} + + +pub struct LegacyFrameAllocator { + original: I, + memory_map: I, + current_descriptor: Option, + next_frame: PhysFrame, +} + +impl LegacyFrameAllocator +where + I: ExactSizeIterator + Clone, I::Item: LegacyMemoryRegion, +{ + pub fn new(memory_map: I) -> Self { + Self { + original: memory_map.clone(), + memory_map, + current_descriptor: None, + next_frame: PhysFrame::containing_address(PhysAddr::new(0x1000)), + } + } + + fn allocate_frame_from_descriptor( + &mut self, + descriptor: D, + ) -> Option { + let start_addr = descriptor.start(); + let end_addr = start_addr + descriptor.len(); + let start_frame: PhysFrame = PhysFrame::containing_address(start_addr.align_up(PAGE_SIZE)); + let end_frame = PhysFrame::containing_address(end_addr - 1u64); + if self.next_frame < end_frame { + let ret = self.next_frame; + self.next_frame += 1; + Some(ret) + } else { + None + } + } + + pub fn len(&self) -> usize { + self.original.len() + } + + pub fn construct_memory_map( + self, + regions: &mut [MaybeUninit], + ) -> &mut [MemoryRegion] { + use crate::memory_map::MemoryRegionKind; + + let mut next_index = 0; + + for mut descriptor in self.original { + let end = descriptor.start() + descriptor.len(); + let next_free = self.next_frame.start_address(); + let kind = if descriptor.usable() { + if end <= next_free { + MemoryRegionKind::Bootloader + } else if descriptor.start() >= next_free { + MemoryRegionKind::Usable + } else { + // part of the region is used -> add is separately + let used_region = MemoryRegion { + start: descriptor.start().as_u64(), + end: next_free.as_u64(), + kind: MemoryRegionKind::Bootloader, + }; + Self::add_region(used_region, regions, &mut next_index) + .expect("Failed to add memory region"); + + // add unused part normally + descriptor.set_start(next_free); + MemoryRegionKind::Usable + } + } else { + MemoryRegionKind::Reserved // FIXME more types + }; + + let region = MemoryRegion { + start: descriptor.start().as_u64(), + end: end.as_u64(), + kind, + }; + Self::add_region(region, regions, &mut next_index); + } + + let initialized = &mut regions[..next_index]; + unsafe { MaybeUninit::slice_get_mut(initialized) } + } + + fn add_region( + region: MemoryRegion, + regions: &mut [MaybeUninit], + next_index: &mut usize, + ) -> Result<(), ()> { + unsafe { + regions + .get_mut(*next_index) + .ok_or(())? + .as_mut_ptr() + .write(region) + }; + *next_index += 1; + Ok(()) + } +} + +unsafe impl FrameAllocator for LegacyFrameAllocator +where + I: ExactSizeIterator + Clone, I::Item: LegacyMemoryRegion, +{ + fn allocate_frame(&mut self) -> Option> { + if let Some(current_descriptor) = self.current_descriptor { + match self.allocate_frame_from_descriptor(current_descriptor) { + Some(frame) => return Some(frame), + None => { + self.current_descriptor = None; + } + } + } + + // find next suitable descriptor + while let Some(descriptor) = self.memory_map.next() { + if !descriptor.usable() { + continue; + } + if let Some(frame) = self.allocate_frame_from_descriptor(descriptor) { + self.current_descriptor = Some(descriptor); + return Some(frame); + } + } + + None + } +} diff --git a/src/legacy_memory_region/uefi.rs b/src/legacy_memory_region/uefi.rs new file mode 100644 index 00000000..66f95514 --- /dev/null +++ b/src/legacy_memory_region/uefi.rs @@ -0,0 +1,23 @@ +use crate::legacy_memory_region::LegacyMemoryRegion; +use uefi::table::boot::{MemoryDescriptor, MemoryType}; +use x86_64::PhysAddr; + +const PAGE_SIZE: u64 = 4096; + +impl<'a> LegacyMemoryRegion for MemoryDescriptor { + fn start(&self) -> PhysAddr { + PhysAddr::new(self.phys_start) + } + + fn len(&self) -> u64 { + self.page_count * PAGE_SIZE + } + + fn usable(&self) -> bool { + self.ty == MemoryType::CONVENTIONAL + } + + fn set_start(&mut self, new_start: PhysAddr) { + self.phys_start = new_start.as_u64(); + } +} diff --git a/src/lib.rs b/src/lib.rs index cecebec2..2ddadb8a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ #![feature(slice_fill)] #![feature(asm)] #![feature(unsafe_block_in_unsafe_fn)] +#![feature(maybe_uninit_slice_assume_init)] #![deny(unsafe_op_in_unsafe_fn)] //#![warn(missing_docs)] @@ -52,6 +53,9 @@ pub mod page_table; #[cfg(feature = "bios_bin")] pub mod printer; +#[cfg(feature = "binary")] +pub mod legacy_memory_region; + #[cfg(all(feature = "bios_bin", feature = "sse"))] pub mod sse; From 7aa096a1e5ca287a87f76ad9322df516648ae67f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 24 Aug 2020 10:39:25 +0200 Subject: [PATCH 061/174] Update uefi crate to latest master --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 7b59ce4e..8c8d9863 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,7 +257,7 @@ dependencies = [ [[package]] name = "uefi" version = "0.5.0" -source = "git+https://github.com/rust-osdev/uefi-rs.git#fa4b0081f0e7d9fdd72caa7f3e5a052ed6ba6de7" +source = "git+https://github.com/rust-osdev/uefi-rs.git#af9b2a492d404ff7bfe5ac0293ae5bc3e1d3a1ab" dependencies = [ "bitflags", "log", From bc48c7e6ab6e1cb63bc89c6236d1f701e16bd9be Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 24 Aug 2020 10:40:25 +0200 Subject: [PATCH 062/174] Group binary functionality into new `binary` submodule With `bios` and `uefi` submodules. --- src/bin/uefi.rs | 19 +++++++----- src/binary/bios/memory_descriptor.rs | 1 + .../bios.rs => binary/bios/mod.rs} | 2 +- .../mod.rs => binary/legacy_memory_region.rs} | 14 +++++---- src/binary/mod.rs | 6 ++++ src/{ => binary/uefi}/load_kernel.rs | 0 src/{ => binary/uefi}/logger.rs | 0 .../uefi/memory_descriptor.rs} | 2 +- src/binary/uefi/mod.rs | 20 +++++++++++++ src/lib.rs | 29 ++----------------- 10 files changed, 52 insertions(+), 41 deletions(-) create mode 100644 src/binary/bios/memory_descriptor.rs rename src/{legacy_memory_region/bios.rs => binary/bios/mod.rs} (95%) rename src/{legacy_memory_region/mod.rs => binary/legacy_memory_region.rs} (93%) create mode 100644 src/binary/mod.rs rename src/{ => binary/uefi}/load_kernel.rs (100%) rename src/{ => binary/uefi}/logger.rs (100%) rename src/{legacy_memory_region/uefi.rs => binary/uefi/memory_descriptor.rs} (89%) create mode 100644 src/binary/uefi/mod.rs diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index e43f753b..acb60f48 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -18,7 +18,10 @@ struct PageAligned(T); extern crate rlibc; -use bootloader::legacy_memory_region::LegacyFrameAllocator; +use bootloader::binary::{ + legacy_memory_region::{LegacyMemoryRegion, LegacyFrameAllocator}, + uefi::load_kernel, +}; use bootloader::boot_info_uefi::{BootInfo, FrameBufferInfo}; use bootloader::memory_map::MemoryRegion; use core::{ @@ -144,7 +147,7 @@ fn set_up_mappings( framebuffer_addr: PhysAddr, framebuffer_size: usize, ) -> Mappings { - let entry_point = bootloader::load_kernel(&KERNEL.0, kernel_page_table, frame_allocator); + let entry_point = load_kernel(&KERNEL.0, kernel_page_table, frame_allocator); log::info!("Entry point at: {:#x}", entry_point.as_u64()); // create a stack @@ -200,7 +203,7 @@ fn create_boot_info( ) -> (&'static mut BootInfo, TwoFrames) where I: ExactSizeIterator + Clone, - D: bootloader::legacy_memory_region::LegacyMemoryRegion, + D: LegacyMemoryRegion, { log::info!("Allocate bootinfo"); @@ -352,12 +355,12 @@ fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { 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()) }; - let info = bootloader::FrameBufferInfo { + let info = bootloader::binary::uefi::FrameBufferInfo { horizontal_resolution: mode_info.resolution().0, vertical_resolution: mode_info.resolution().1, pixel_format: match mode_info.pixel_format() { - PixelFormat::RGB => bootloader::PixelFormat::BGR, - PixelFormat::BGR => bootloader::PixelFormat::BGR, + PixelFormat::RGB => bootloader::binary::uefi::PixelFormat::BGR, + PixelFormat::BGR => bootloader::binary::uefi::PixelFormat::BGR, PixelFormat::Bitmask | PixelFormat::BltOnly => { panic!("Bitmask and BltOnly framebuffers are not supported") } @@ -365,7 +368,7 @@ fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { stride: mode_info.stride(), }; - bootloader::init_logger(slice, info); + bootloader::binary::uefi::init_logger(slice, info); ( PhysAddr::new(framebuffer.as_mut_ptr() as u64), @@ -408,7 +411,7 @@ fn kernel_stack_start_location() -> Page { #[panic_handler] fn panic(info: &PanicInfo) -> ! { - unsafe { bootloader::logger::LOGGER.get().map(|l| l.force_unlock()) }; + unsafe { bootloader::binary::uefi::logger::LOGGER.get().map(|l| l.force_unlock()) }; log::error!("{}", info); loop { unsafe { asm!("cli; hlt") }; diff --git a/src/binary/bios/memory_descriptor.rs b/src/binary/bios/memory_descriptor.rs new file mode 100644 index 00000000..feef6d55 --- /dev/null +++ b/src/binary/bios/memory_descriptor.rs @@ -0,0 +1 @@ +pub mod memory_descriptor; \ No newline at end of file diff --git a/src/legacy_memory_region/bios.rs b/src/binary/bios/mod.rs similarity index 95% rename from src/legacy_memory_region/bios.rs rename to src/binary/bios/mod.rs index 6e3a473b..c08ffdb4 100644 --- a/src/legacy_memory_region/bios.rs +++ b/src/binary/bios/mod.rs @@ -1,5 +1,5 @@ use x86_64::PhysAddr; -use crate::legacy_memory_region::LegacyMemoryRegion; +use crate::binary::legacy_memory_region::LegacyMemoryRegion; impl LegacyMemoryRegion for E820MemoryRegion { fn start(&self) -> PhysAddr { diff --git a/src/legacy_memory_region/mod.rs b/src/binary/legacy_memory_region.rs similarity index 93% rename from src/legacy_memory_region/mod.rs rename to src/binary/legacy_memory_region.rs index 3b46ca1a..0fe5c43b 100644 --- a/src/legacy_memory_region/mod.rs +++ b/src/binary/legacy_memory_region.rs @@ -4,10 +4,8 @@ use core::mem::MaybeUninit; const PAGE_SIZE: u64 = 4096; -#[cfg(feature = "bios_bin")] -pub mod bios; -#[cfg(feature = "uefi_bin")] -pub mod uefi; + + pub trait LegacyMemoryRegion: Copy { fn start(&self) -> PhysAddr; @@ -30,11 +28,17 @@ where I: ExactSizeIterator + Clone, I::Item: LegacyMemoryRegion, { pub fn new(memory_map: I) -> Self { + // skip frame 0 because the rust core library does not see 0 as a valid address + let start_frame = PhysFrame::containing_address(PhysAddr::new(0x1000)); + Self::new_starting_at(start_frame, memory_map) + } + + pub fn new_starting_at(frame: PhysFrame, memory_map: I) -> Self { Self { original: memory_map.clone(), memory_map, current_descriptor: None, - next_frame: PhysFrame::containing_address(PhysAddr::new(0x1000)), + next_frame: frame, } } diff --git a/src/binary/mod.rs b/src/binary/mod.rs new file mode 100644 index 00000000..e82ed8f2 --- /dev/null +++ b/src/binary/mod.rs @@ -0,0 +1,6 @@ +#[cfg(feature = "uefi_bin")] +pub mod uefi; +#[cfg(feature = "bios_bin")] +pub mod bios; + +pub mod legacy_memory_region; \ No newline at end of file diff --git a/src/load_kernel.rs b/src/binary/uefi/load_kernel.rs similarity index 100% rename from src/load_kernel.rs rename to src/binary/uefi/load_kernel.rs diff --git a/src/logger.rs b/src/binary/uefi/logger.rs similarity index 100% rename from src/logger.rs rename to src/binary/uefi/logger.rs diff --git a/src/legacy_memory_region/uefi.rs b/src/binary/uefi/memory_descriptor.rs similarity index 89% rename from src/legacy_memory_region/uefi.rs rename to src/binary/uefi/memory_descriptor.rs index 66f95514..cb8370b9 100644 --- a/src/legacy_memory_region/uefi.rs +++ b/src/binary/uefi/memory_descriptor.rs @@ -1,4 +1,4 @@ -use crate::legacy_memory_region::LegacyMemoryRegion; +use crate::binary::legacy_memory_region::LegacyMemoryRegion; use uefi::table::boot::{MemoryDescriptor, MemoryType}; use x86_64::PhysAddr; diff --git a/src/binary/uefi/mod.rs b/src/binary/uefi/mod.rs new file mode 100644 index 00000000..af9ca0e9 --- /dev/null +++ b/src/binary/uefi/mod.rs @@ -0,0 +1,20 @@ +pub use logger::{FrameBufferInfo, PixelFormat}; +use x86_64::{VirtAddr, structures::paging::{FrameAllocator, MapperAllSizes, Size4KiB}}; + +mod load_kernel; +pub mod logger; +mod memory_descriptor; + +pub fn init_logger(framebuffer: &'static mut [u8], info: FrameBufferInfo) { + let logger = logger::LOGGER.get_or_init(move || logger::LockedLogger::new(framebuffer, info)); + log::set_logger(logger).expect("logger already set"); + log::set_max_level(log::LevelFilter::Trace); +} + +pub fn load_kernel( + kernel: &'static [u8], + page_table: &mut impl MapperAllSizes, + frame_allocator: &mut impl FrameAllocator, +) -> VirtAddr { + load_kernel::load_kernel(kernel, page_table, frame_allocator).expect("Failed to parse kernel") +} diff --git a/src/lib.rs b/src/lib.rs index 2ddadb8a..bcabc682 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,8 +16,6 @@ pub use crate::bootinfo::BootInfo; -#[cfg(feature = "uefi_bin")] -pub use logger::{FrameBufferInfo, PixelFormat}; #[cfg(feature = "bios_bin")] use x86_64::{ structures::paging::{frame::PhysFrameRange, PhysFrame}, @@ -34,14 +32,12 @@ pub mod bootinfo; pub mod boot_info_uefi; pub mod memory_map; +#[cfg(feature = "binary")] +pub mod binary; + #[cfg(feature = "builder")] pub mod disk_image; -#[cfg(feature = "uefi_bin")] -mod load_kernel; -#[cfg(feature = "uefi_bin")] -pub mod logger; - #[cfg(feature = "bios_bin")] pub mod boot_info; #[cfg(feature = "bios_bin")] @@ -53,9 +49,6 @@ pub mod page_table; #[cfg(feature = "bios_bin")] pub mod printer; -#[cfg(feature = "binary")] -pub mod legacy_memory_region; - #[cfg(all(feature = "bios_bin", feature = "sse"))] pub mod sse; @@ -88,22 +81,6 @@ macro_rules! entry_point { }; } -#[cfg(feature = "uefi_bin")] -pub fn init_logger(framebuffer: &'static mut [u8], info: FrameBufferInfo) { - let logger = logger::LOGGER.get_or_init(move || logger::LockedLogger::new(framebuffer, info)); - log::set_logger(logger).expect("logger already set"); - log::set_max_level(log::LevelFilter::Trace); -} - -#[cfg(feature = "uefi_bin")] -pub fn load_kernel( - kernel: &'static [u8], - page_table: &mut impl MapperAllSizes, - frame_allocator: &mut impl FrameAllocator, -) -> VirtAddr { - load_kernel::load_kernel(kernel, page_table, frame_allocator).expect("Failed to parse kernel") -} - #[cfg(feature = "bios_bin")] pub fn phys_frame_range(range: bootinfo::FrameRange) -> PhysFrameRange { PhysFrameRange { From e40735e3e07959964befd28ada36ebd301903f94 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 24 Aug 2020 16:03:19 +0200 Subject: [PATCH 063/174] Add support for loading bootloaders >64KiB --- src/asm/stage_1.s | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/asm/stage_1.s b/src/asm/stage_1.s index 9107ff9c..c6301833 100644 --- a/src/asm/stage_1.s +++ b/src/asm/stage_1.s @@ -80,6 +80,12 @@ check_int13h_extensions: load_rest_of_bootloader_from_disk: lea eax, _rest_of_bootloader_start_addr + mov ecx, 0 + +load_from_disk: + lea eax, _rest_of_bootloader_start_addr + add eax, ecx # add offset + # dap buffer segment mov ebx, eax shr ebx, 4 # divide by 16 @@ -91,12 +97,21 @@ load_rest_of_bootloader_from_disk: mov [dap_buffer_addr], ax lea eax, _rest_of_bootloader_start_addr + add eax, ecx # add offset # number of disk blocks to load lea ebx, _rest_of_bootloader_end_addr sub ebx, eax # end - start + jz load_from_disk_done shr ebx, 9 # divide by 512 (block size) + cmp ebx, 127 + jle .continue_loading_from_disk + mov ebx, 127 +.continue_loading_from_disk: mov [dap_blocks], bx + # increase offset + shl ebx, 9 + add ecx, ebx # number of start block lea ebx, _start @@ -108,7 +123,10 @@ load_rest_of_bootloader_from_disk: mov ah, 0x42 int 0x13 jc rest_of_bootloader_load_failed - + + jmp load_from_disk + +load_from_disk_done: # reset segment to 0 mov word ptr [dap_buffer_seg], 0 From 402ba9eff3db2467382c2103f95f606fc8e9a1a3 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 24 Aug 2020 16:03:39 +0200 Subject: [PATCH 064/174] Identity-map first gigabyte in assembly --- src/asm/stage_3.s | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/src/asm/stage_3.s b/src/asm/stage_3.s index 06d753c3..ead9bf36 100644 --- a/src/asm/stage_3.s +++ b/src/asm/stage_3.s @@ -44,40 +44,14 @@ set_up_page_tables: or eax, (1 | 2) mov [_p3], eax # p2 - lea eax, [_p1] - or eax, (1 | 2) - mov [_p2], eax - mov eax, (0x400000 | 1 | 2 | (1 << 7)) - mov ecx, 2 - lea edx, _kernel_size - add edx, 0x400000 # start address - add edx, 0x200000 - 1 # align up - shr edx, 12 + 9 # end huge page number + mov eax, (1 | 2 | (1 << 7)) + mov ecx, 0 map_p2_table: mov [_p2 + ecx * 8], eax add eax, 0x200000 add ecx, 1 - cmp ecx, edx + cmp ecx, 512 jb map_p2_table - # p1 - # start mapping from __page_table_start, as we need to be able to access - # the p4 table from rust. stop mapping at __bootloader_end - lea eax, __page_table_start - and eax, 0xfffff000 - or eax, (1 | 2) - lea ecx, __page_table_start - shr ecx, 12 # start page number - lea edx, __bootloader_end - add edx, 4096 - 1 # align up - shr edx, 12 # end page number - map_p1_table: - mov [_p1 + ecx * 8], eax - add eax, 4096 - add ecx, 1 - cmp ecx, edx - jb map_p1_table - map_framebuffer: - call vga_map_frame_buffer enable_paging: # Write back cache and add a memory fence. I'm not sure if this is From 2952138b9c35b77387c16d0b7b501b559eb88952 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 24 Aug 2020 16:04:54 +0200 Subject: [PATCH 065/174] Run cargo fmt --- src/binary/bios/mod.rs | 5 ++--- src/binary/legacy_memory_region.rs | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/binary/bios/mod.rs b/src/binary/bios/mod.rs index c08ffdb4..a3dc8095 100644 --- a/src/binary/bios/mod.rs +++ b/src/binary/bios/mod.rs @@ -1,5 +1,5 @@ -use x86_64::PhysAddr; use crate::binary::legacy_memory_region::LegacyMemoryRegion; +use x86_64::PhysAddr; impl LegacyMemoryRegion for E820MemoryRegion { fn start(&self) -> PhysAddr { @@ -19,7 +19,6 @@ impl LegacyMemoryRegion for E820MemoryRegion { } } - #[doc(hidden)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(C)] @@ -47,4 +46,4 @@ impl From for MemoryRegion { } } } -*/ \ No newline at end of file +*/ diff --git a/src/binary/legacy_memory_region.rs b/src/binary/legacy_memory_region.rs index 0fe5c43b..4ea776af 100644 --- a/src/binary/legacy_memory_region.rs +++ b/src/binary/legacy_memory_region.rs @@ -1,6 +1,9 @@ -use x86_64::{PhysAddr, structures::paging::{FrameAllocator, PhysFrame, Size4KiB}}; use crate::memory_map::MemoryRegion; use core::mem::MaybeUninit; +use x86_64::{ + structures::paging::{FrameAllocator, PhysFrame, Size4KiB}, + PhysAddr, +}; const PAGE_SIZE: u64 = 4096; @@ -15,7 +18,6 @@ pub trait LegacyMemoryRegion: Copy { fn set_start(&mut self, new_start: PhysAddr); } - pub struct LegacyFrameAllocator { original: I, memory_map: I, @@ -25,7 +27,8 @@ pub struct LegacyFrameAllocator { impl LegacyFrameAllocator where - I: ExactSizeIterator + Clone, I::Item: LegacyMemoryRegion, + I: ExactSizeIterator + Clone, + I::Item: LegacyMemoryRegion, { pub fn new(memory_map: I) -> Self { // skip frame 0 because the rust core library does not see 0 as a valid address @@ -42,10 +45,7 @@ where } } - fn allocate_frame_from_descriptor( - &mut self, - descriptor: D, - ) -> Option { + fn allocate_frame_from_descriptor(&mut self, descriptor: D) -> Option { let start_addr = descriptor.start(); let end_addr = start_addr + descriptor.len(); let start_frame: PhysFrame = PhysFrame::containing_address(start_addr.align_up(PAGE_SIZE)); @@ -80,8 +80,8 @@ where } else if descriptor.start() >= next_free { MemoryRegionKind::Usable } else { - // part of the region is used -> add is separately - let used_region = MemoryRegion { + // part of the region is used -> add is separately + let used_region = MemoryRegion { start: descriptor.start().as_u64(), end: next_free.as_u64(), kind: MemoryRegionKind::Bootloader, @@ -128,7 +128,8 @@ where unsafe impl FrameAllocator for LegacyFrameAllocator where - I: ExactSizeIterator + Clone, I::Item: LegacyMemoryRegion, + I: ExactSizeIterator + Clone, + I::Item: LegacyMemoryRegion, { fn allocate_frame(&mut self) -> Option> { if let Some(current_descriptor) = self.current_descriptor { From d0cf36b860b9fbc389349a0f9115725057562462 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 24 Aug 2020 16:05:52 +0200 Subject: [PATCH 066/174] Increase self.next_frame if smaller than descriptor start Otherwise we would use the undefined frames before the descriptor's start too. --- src/binary/legacy_memory_region.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/binary/legacy_memory_region.rs b/src/binary/legacy_memory_region.rs index 4ea776af..c526aa02 100644 --- a/src/binary/legacy_memory_region.rs +++ b/src/binary/legacy_memory_region.rs @@ -47,9 +47,15 @@ where fn allocate_frame_from_descriptor(&mut self, descriptor: D) -> Option { let start_addr = descriptor.start(); + let start_frame = PhysFrame::containing_address(start_addr); let end_addr = start_addr + descriptor.len(); - let start_frame: PhysFrame = PhysFrame::containing_address(start_addr.align_up(PAGE_SIZE)); let end_frame = PhysFrame::containing_address(end_addr - 1u64); + + // increase self.next_frame to start_frame if smaller + if self.next_frame < start_frame { + self.next_frame = start_frame; + } + if self.next_frame < end_frame { let ret = self.next_frame; self.next_frame += 1; From c47cb9c1f5e83dabd39a6ccb3870ca790299f6c3 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 24 Aug 2020 16:07:09 +0200 Subject: [PATCH 067/174] Move more code from uefi binary to `binary` module of library --- Cargo.toml | 6 +- src/bin/uefi.rs | 302 ++------------------------ src/binary/legacy_memory_region.rs | 9 +- src/binary/{uefi => }/load_kernel.rs | 4 +- src/binary/{uefi => }/logger.rs | 6 +- src/binary/mod.rs | 304 ++++++++++++++++++++++++++- src/binary/uefi/mod.rs | 19 -- 7 files changed, 336 insertions(+), 314 deletions(-) rename src/binary/{uefi => }/load_kernel.rs (99%) rename src/binary/{uefi => }/logger.rs (96%) diff --git a/Cargo.toml b/Cargo.toml index e550df27..50c9ddb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,9 +56,9 @@ toml = { version = "0.5.1", optional = true } default = [] builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json"] runner = ["anyhow"] -bios_bin = ["binary", "fixedvec", "rlibc"] -uefi_bin = ["binary", "rlibc", "log", "uefi", "conquer-once", "font8x8", "spinning_top"] -binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions"] +bios_bin = ["binary", "fixedvec", "rlibc", "vga_320x200"] +uefi_bin = ["binary", "rlibc", "uefi", "font8x8"] +binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", "log", "conquer-once", "spinning_top"] vga_320x200 = ["font8x8"] recursive_page_table = [] map_physical_memory = [] diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index acb60f48..33197265 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -18,29 +18,15 @@ struct PageAligned(T); extern crate rlibc; -use bootloader::binary::{ - legacy_memory_region::{LegacyMemoryRegion, LegacyFrameAllocator}, - uefi::load_kernel, -}; -use bootloader::boot_info_uefi::{BootInfo, FrameBufferInfo}; -use bootloader::memory_map::MemoryRegion; -use core::{ - mem::{self, MaybeUninit}, - panic::PanicInfo, - slice, -}; +use bootloader::binary::legacy_memory_region::LegacyFrameAllocator; +use core::{mem, panic::PanicInfo, slice}; use uefi::{ prelude::{entry, Boot, Handle, ResultExt, Status, SystemTable}, proto::console::gop::{GraphicsOutput, PixelFormat}, table::boot::{MemoryDescriptor, MemoryType}, }; -use usize_conversions::FromUsize; use x86_64::{ - registers, - structures::paging::{ - FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PageTableFlags, PhysFrame, - Size4KiB, - }, + structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB}, PhysAddr, VirtAddr, }; @@ -66,24 +52,22 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { .expect_success("Failed to exit boot services"); let mut frame_allocator = LegacyFrameAllocator::new(memory_map.copied()); - let mut page_tables = create_page_tables(&mut frame_allocator); - let mappings = set_up_mappings( - &mut frame_allocator, - &mut page_tables.kernel, - framebuffer_addr, - framebuffer_size, - ); - let (boot_info, two_frames) = create_boot_info( + + let page_tables = create_page_tables(&mut frame_allocator); + + bootloader::binary::load_and_switch_to_kernel( + &KERNEL.0, frame_allocator, - &mut page_tables, - mappings.framebuffer, + page_tables, + framebuffer_addr, framebuffer_size, ); - switch_to_kernel(page_tables, mappings, boot_info, two_frames); } /// Creates page table abstraction types for both the bootloader and kernel page tables. -fn create_page_tables(frame_allocator: &mut impl FrameAllocator) -> PageTables { +fn create_page_tables( + frame_allocator: &mut impl FrameAllocator, +) -> bootloader::binary::PageTables { // UEFI identity-maps all memory, so the offset between physical and virtual addresses is 0 let phys_offset = VirtAddr::new(0); @@ -127,224 +111,13 @@ fn create_page_tables(frame_allocator: &mut impl FrameAllocator) -> Pa ) }; - PageTables { + bootloader::binary::PageTables { bootloader: bootloader_page_table, kernel: kernel_page_table, kernel_level_4_frame, } } -struct PageTables { - bootloader: OffsetPageTable<'static>, - kernel: OffsetPageTable<'static>, - kernel_level_4_frame: PhysFrame, -} - -/// Sets up mappings for a kernel stack and the framebuffer -fn set_up_mappings( - frame_allocator: &mut impl FrameAllocator, - kernel_page_table: &mut OffsetPageTable, - framebuffer_addr: PhysAddr, - framebuffer_size: usize, -) -> Mappings { - let entry_point = load_kernel(&KERNEL.0, kernel_page_table, frame_allocator); - log::info!("Entry point at: {:#x}", entry_point.as_u64()); - - // create a stack - let stack_start: Page = kernel_stack_start_location(); - let stack_end = stack_start + 20; - for page in Page::range(stack_start, stack_end) { - let frame = frame_allocator - .allocate_frame() - .expect("frame allocation failed when mapping a kernel stack"); - let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - unsafe { kernel_page_table.map_to(page, frame, flags, frame_allocator) } - .unwrap() - .flush(); - } - - log::info!("Map framebuffer"); - - // map framebuffer - let framebuffer_start_frame: PhysFrame = PhysFrame::containing_address(framebuffer_addr); - let framebuffer_end_frame = - PhysFrame::containing_address(framebuffer_addr + framebuffer_size - 1u64); - let start_page = frame_buffer_location(); - for (i, frame) in - PhysFrame::range_inclusive(framebuffer_start_frame, framebuffer_end_frame).enumerate() - { - let page = start_page + u64::from_usize(i); - let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - unsafe { kernel_page_table.map_to(page, frame, flags, frame_allocator) } - .unwrap() - .flush(); - } - let framebuffer_virt_addr = start_page.start_address(); - - Mappings { - framebuffer: framebuffer_virt_addr, - entry_point, - stack_end, - } -} - -struct Mappings { - entry_point: VirtAddr, - framebuffer: VirtAddr, - stack_end: Page, -} - -/// Allocates and initializes the boot info struct and the memory map -fn create_boot_info( - mut frame_allocator: LegacyFrameAllocator, - page_tables: &mut PageTables, - framebuffer_virt_addr: VirtAddr, - framebuffer_size: usize, -) -> (&'static mut BootInfo, TwoFrames) -where - I: ExactSizeIterator + Clone, - D: LegacyMemoryRegion, -{ - log::info!("Allocate bootinfo"); - - // allocate and map space for the boot info - let (boot_info, memory_regions) = { - let boot_info_addr = boot_info_location(); - let boot_info_end = boot_info_addr + mem::size_of::(); - let memory_map_regions_addr = - boot_info_end.align_up(u64::from_usize(mem::align_of::())); - let regions = frame_allocator.len() + 1; // one region might be split into used/unused - let memory_map_regions_end = - memory_map_regions_addr + regions * mem::size_of::(); - - let start_page = Page::containing_address(boot_info_addr); - let end_page = Page::containing_address(memory_map_regions_end - 1u64); - for page in Page::range_inclusive(start_page, end_page) { - log::info!("Mapping page {:?}", page); - let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - let frame = frame_allocator - .allocate_frame() - .expect("frame allocation for boot info failed"); - log::info!("1 {:?}", page); - unsafe { - page_tables - .kernel - .map_to(page, frame, flags, &mut frame_allocator) - } - .unwrap() - .flush(); - log::info!("2 {:?}", page); - // we need to be able to access it too - unsafe { - page_tables - .bootloader - .map_to(page, frame, flags, &mut frame_allocator) - } - .unwrap() - .flush(); - log::info!("Finished mapping page {:?}", page); - } - - let boot_info: &'static mut MaybeUninit = - unsafe { &mut *boot_info_addr.as_mut_ptr() }; - let memory_regions: &'static mut [MaybeUninit] = - unsafe { slice::from_raw_parts_mut(memory_map_regions_addr.as_mut_ptr(), regions) }; - (boot_info, memory_regions) - }; - - // reserve two unused frames for context switch - let two_frames = TwoFrames::new(&mut frame_allocator); - - log::info!("Create Memory Map"); - - // build memory map - let memory_regions = frame_allocator.construct_memory_map(memory_regions); - - log::info!("Create bootinfo"); - - // create boot info - let boot_info = boot_info.write(BootInfo { - memory_regions, - framebuffer: FrameBufferInfo { - start_addr: framebuffer_virt_addr.as_u64(), - len: framebuffer_size, - }, - }); - - (boot_info, two_frames) -} - -/// Switches to the kernel address space and jumps to the kernel entry point. -fn switch_to_kernel( - page_tables: PageTables, - mappings: Mappings, - boot_info: &'static mut BootInfo, - two_frames: TwoFrames, -) -> ! { - let PageTables { - kernel_level_4_frame, - kernel: kernel_page_table, - .. - } = page_tables; - let addresses = Addresses { - page_table: kernel_level_4_frame, - stack_top: mappings.stack_end.start_address(), - entry_point: mappings.entry_point, - boot_info, - }; - - log::info!( - "Jumping to kernel entry point at {:?}", - addresses.entry_point - ); - unsafe { - context_switch(addresses, kernel_page_table, two_frames); - } -} - -/// Performs the actual context switch -/// -/// This function should stay small because it needs to be identity-mapped. -unsafe fn context_switch( - addresses: Addresses, - mut kernel_page_table: OffsetPageTable, - mut frame_allocator: impl FrameAllocator, -) -> ! { - // identity-map current and next frame, so that we don't get an immediate pagefault - // after switching the active page table - let current_addr = PhysAddr::new(registers::read_rip()); - let current_frame: PhysFrame = PhysFrame::containing_address(current_addr); - for frame in PhysFrame::range_inclusive(current_frame, current_frame + 1) { - unsafe { - kernel_page_table.identity_map(frame, PageTableFlags::PRESENT, &mut frame_allocator) - } - .unwrap() - .flush(); - } - - // we don't need the kernel page table anymore - mem::drop(kernel_page_table); - - // do the context switch - unsafe { - asm!( - "mov cr3, {}; mov rsp, {}; push 0; jmp {}", - in(reg) addresses.page_table.start_address().as_u64(), - in(reg) addresses.stack_top.as_u64(), - in(reg) addresses.entry_point.as_u64(), - in("rdi") addresses.boot_info as *const _ as usize, - ); - } - unreachable!(); -} - -struct Addresses { - page_table: PhysFrame, - stack_top: VirtAddr, - entry_point: VirtAddr, - boot_info: &'static mut bootloader::boot_info_uefi::BootInfo, -} - fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { let gop = st .boot_services() @@ -355,12 +128,12 @@ fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { 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()) }; - let info = bootloader::binary::uefi::FrameBufferInfo { + let info = bootloader::binary::logger::FrameBufferInfo { horizontal_resolution: mode_info.resolution().0, vertical_resolution: mode_info.resolution().1, pixel_format: match mode_info.pixel_format() { - PixelFormat::RGB => bootloader::binary::uefi::PixelFormat::BGR, - PixelFormat::BGR => bootloader::binary::uefi::PixelFormat::BGR, + PixelFormat::RGB => bootloader::binary::logger::PixelFormat::BGR, + PixelFormat::BGR => bootloader::binary::logger::PixelFormat::BGR, PixelFormat::Bitmask | PixelFormat::BltOnly => { panic!("Bitmask and BltOnly framebuffers are not supported") } @@ -368,7 +141,7 @@ fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { stride: mode_info.stride(), }; - bootloader::binary::uefi::init_logger(slice, info); + bootloader::binary::init_logger(slice, info); ( PhysAddr::new(framebuffer.as_mut_ptr() as u64), @@ -376,42 +149,13 @@ fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { ) } -pub struct TwoFrames { - frames: [Option; 2], -} - -impl TwoFrames { - pub fn new(frame_allocator: &mut impl FrameAllocator) -> Self { - TwoFrames { - frames: [ - Some(frame_allocator.allocate_frame().unwrap()), - Some(frame_allocator.allocate_frame().unwrap()), - ], - } - } -} - -unsafe impl FrameAllocator for TwoFrames { - fn allocate_frame(&mut self) -> Option> { - self.frames.iter_mut().find_map(|f| f.take()) - } -} - -fn boot_info_location() -> VirtAddr { - VirtAddr::new(0x_0000_00bb_bbbb_0000) -} - -fn frame_buffer_location() -> Page { - Page::containing_address(VirtAddr::new(0x_0000_00cc_cccc_0000)) -} - -fn kernel_stack_start_location() -> Page { - Page::containing_address(VirtAddr::new(0x_0000_0fff_0000_0000)) -} - #[panic_handler] fn panic(info: &PanicInfo) -> ! { - unsafe { bootloader::binary::uefi::logger::LOGGER.get().map(|l| l.force_unlock()) }; + unsafe { + bootloader::binary::logger::LOGGER + .get() + .map(|l| l.force_unlock()) + }; log::error!("{}", info); loop { unsafe { asm!("cli; hlt") }; diff --git a/src/binary/legacy_memory_region.rs b/src/binary/legacy_memory_region.rs index c526aa02..16341b96 100644 --- a/src/binary/legacy_memory_region.rs +++ b/src/binary/legacy_memory_region.rs @@ -5,12 +5,7 @@ use x86_64::{ PhysAddr, }; -const PAGE_SIZE: u64 = 4096; - - - - -pub trait LegacyMemoryRegion: Copy { +pub trait LegacyMemoryRegion: Copy + core::fmt::Debug { fn start(&self) -> PhysAddr; fn len(&self) -> u64; fn usable(&self) -> bool; @@ -108,7 +103,7 @@ where end: end.as_u64(), kind, }; - Self::add_region(region, regions, &mut next_index); + Self::add_region(region, regions, &mut next_index).unwrap(); } let initialized = &mut regions[..next_index]; diff --git a/src/binary/uefi/load_kernel.rs b/src/binary/load_kernel.rs similarity index 99% rename from src/binary/uefi/load_kernel.rs rename to src/binary/load_kernel.rs index dda7bef6..be9ae4b9 100644 --- a/src/binary/uefi/load_kernel.rs +++ b/src/binary/load_kernel.rs @@ -210,7 +210,9 @@ where let last_page = Page::containing_address(virt_start_addr + file_size - 1u64); self.page_table .unmap(last_page.clone()) - .map_err(|_err| "Failed to unmap last segment page because of bss memory")?; + .map_err(|_err| "Failed to unmap last segment page because of bss memory")? + .1 + .ignore(); let flusher = unsafe { self.page_table .map_to(last_page, new_frame, segment_flags, self.frame_allocator) diff --git a/src/binary/uefi/logger.rs b/src/binary/logger.rs similarity index 96% rename from src/binary/uefi/logger.rs rename to src/binary/logger.rs index 4346195b..7476efaf 100644 --- a/src/binary/uefi/logger.rs +++ b/src/binary/logger.rs @@ -11,9 +11,9 @@ pub static LOGGER: OnceCell = OnceCell::uninit(); pub struct LockedLogger(Spinlock); /// Additional vertical space between lines -const LINE_SPACING: usize = 2; +const LINE_SPACING: usize = 0; /// Additional vertical space between separate log messages -const LOG_SPACING: usize = 6; +const LOG_SPACING: usize = 2; impl LockedLogger { pub fn new(framebuffer: &'static mut [u8], info: FrameBufferInfo) -> Self { @@ -120,6 +120,7 @@ impl Logger { let (bytes_per_pixel, color) = match self.info.pixel_format { PixelFormat::RGB => (4, [intensity, intensity, intensity / 2, 0]), PixelFormat::BGR => (4, [intensity / 2, intensity, intensity, 0]), + PixelFormat::U8 => (1, [if intensity > 200 { 0xf } else { 0 }, 0, 0, 0]), }; let byte_offset = pixel_offset * bytes_per_pixel; self.framebuffer[byte_offset..(byte_offset + bytes_per_pixel)] @@ -152,4 +153,5 @@ pub struct FrameBufferInfo { pub enum PixelFormat { RGB, BGR, + U8, } diff --git a/src/binary/mod.rs b/src/binary/mod.rs index e82ed8f2..a75a2191 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -1,6 +1,304 @@ -#[cfg(feature = "uefi_bin")] -pub mod uefi; +use crate::binary::legacy_memory_region::{LegacyFrameAllocator, LegacyMemoryRegion}; +use crate::boot_info::{BootInfo, FrameBufferInfo}; +use crate::memory_map::MemoryRegion; +use core::{ + mem::{self, MaybeUninit}, + slice, +}; +use usize_conversions::FromUsize; +use x86_64::{ + registers, + structures::paging::{ + FrameAllocator, Mapper, OffsetPageTable, Page, PageTableFlags, PhysFrame, Size4KiB, + }, + PhysAddr, VirtAddr, +}; + #[cfg(feature = "bios_bin")] pub mod bios; +#[cfg(feature = "uefi_bin")] +pub mod uefi; + +pub mod legacy_memory_region; +pub mod load_kernel; +pub mod logger; + +pub fn init_logger(framebuffer: &'static mut [u8], info: logger::FrameBufferInfo) { + let logger = logger::LOGGER.get_or_init(move || logger::LockedLogger::new(framebuffer, info)); + log::set_logger(logger).expect("logger already set"); + log::set_max_level(log::LevelFilter::Trace); +} + +pub fn load_and_switch_to_kernel( + kernel_bytes: &[u8], + mut frame_allocator: LegacyFrameAllocator, + mut page_tables: PageTables, + framebuffer_addr: PhysAddr, + framebuffer_size: usize, +) -> ! +where + I: ExactSizeIterator + Clone, + D: LegacyMemoryRegion, +{ + let mappings = set_up_mappings( + kernel_bytes, + &mut frame_allocator, + &mut page_tables.kernel, + framebuffer_addr, + framebuffer_size, + ); + let (boot_info, two_frames) = create_boot_info( + frame_allocator, + &mut page_tables, + mappings.framebuffer, + framebuffer_size, + ); + switch_to_kernel(page_tables, mappings, boot_info, two_frames); +} + +/// Sets up mappings for a kernel stack and the framebuffer +pub fn set_up_mappings( + kernel_bytes: &[u8], + frame_allocator: &mut impl FrameAllocator, + kernel_page_table: &mut OffsetPageTable, + framebuffer_addr: PhysAddr, + framebuffer_size: usize, +) -> Mappings { + let entry_point = load_kernel::load_kernel(kernel_bytes, kernel_page_table, frame_allocator) + .expect("no entry point"); + log::info!("Entry point at: {:#x}", entry_point.as_u64()); + + // create a stack + let stack_start: Page = kernel_stack_start_location(); + let stack_end = stack_start + 20; + for page in Page::range(stack_start, stack_end) { + let frame = frame_allocator + .allocate_frame() + .expect("frame allocation failed when mapping a kernel stack"); + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + unsafe { kernel_page_table.map_to(page, frame, flags, frame_allocator) } + .unwrap() + .flush(); + } + + log::info!("Map framebuffer"); + + // map framebuffer + let framebuffer_start_frame: PhysFrame = PhysFrame::containing_address(framebuffer_addr); + let framebuffer_end_frame = + PhysFrame::containing_address(framebuffer_addr + framebuffer_size - 1u64); + let start_page = frame_buffer_location(); + for (i, frame) in + PhysFrame::range_inclusive(framebuffer_start_frame, framebuffer_end_frame).enumerate() + { + let page = start_page + u64::from_usize(i); + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + unsafe { kernel_page_table.map_to(page, frame, flags, frame_allocator) } + .unwrap() + .flush(); + } + let framebuffer_virt_addr = start_page.start_address(); + + Mappings { + framebuffer: framebuffer_virt_addr, + entry_point, + stack_end, + } +} + +pub struct Mappings { + pub entry_point: VirtAddr, + pub framebuffer: VirtAddr, + pub stack_end: Page, +} + +/// Allocates and initializes the boot info struct and the memory map +pub fn create_boot_info( + mut frame_allocator: LegacyFrameAllocator, + page_tables: &mut PageTables, + framebuffer_virt_addr: VirtAddr, + framebuffer_size: usize, +) -> (&'static mut BootInfo, TwoFrames) +where + I: ExactSizeIterator + Clone, + D: LegacyMemoryRegion, +{ + log::info!("Allocate bootinfo"); + + // allocate and map space for the boot info + let (boot_info, memory_regions) = { + let boot_info_addr = boot_info_location(); + let boot_info_end = boot_info_addr + mem::size_of::(); + let memory_map_regions_addr = + boot_info_end.align_up(u64::from_usize(mem::align_of::())); + let regions = frame_allocator.len() + 1; // one region might be split into used/unused + let memory_map_regions_end = + memory_map_regions_addr + regions * mem::size_of::(); + + let start_page = Page::containing_address(boot_info_addr); + let end_page = Page::containing_address(memory_map_regions_end - 1u64); + for page in Page::range_inclusive(start_page, end_page) { + log::info!("Mapping page {:?}", page); + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + let frame = frame_allocator + .allocate_frame() + .expect("frame allocation for boot info failed"); + log::info!("1 {:?}", page); + unsafe { + page_tables + .kernel + .map_to(page, frame, flags, &mut frame_allocator) + } + .unwrap() + .flush(); + log::info!("2 {:?}", page); + // we need to be able to access it too + unsafe { + page_tables + .bootloader + .map_to(page, frame, flags, &mut frame_allocator) + } + .unwrap() + .flush(); + log::info!("Finished mapping page {:?}", page); + } + + let boot_info: &'static mut MaybeUninit = + unsafe { &mut *boot_info_addr.as_mut_ptr() }; + let memory_regions: &'static mut [MaybeUninit] = + unsafe { slice::from_raw_parts_mut(memory_map_regions_addr.as_mut_ptr(), regions) }; + (boot_info, memory_regions) + }; + + // reserve two unused frames for context switch + let two_frames = TwoFrames::new(&mut frame_allocator); + + log::info!("Create Memory Map"); + + // build memory map + let memory_regions = frame_allocator.construct_memory_map(memory_regions); + + log::info!("Create bootinfo"); + + // create boot info + let boot_info = boot_info.write(BootInfo { + memory_regions, + framebuffer: FrameBufferInfo { + start_addr: framebuffer_virt_addr.as_u64(), + len: framebuffer_size, + }, + }); + + (boot_info, two_frames) +} + +/// Switches to the kernel address space and jumps to the kernel entry point. +pub fn switch_to_kernel( + page_tables: PageTables, + mappings: Mappings, + boot_info: &'static mut BootInfo, + two_frames: TwoFrames, +) -> ! { + let PageTables { + kernel_level_4_frame, + kernel: kernel_page_table, + .. + } = page_tables; + let addresses = Addresses { + page_table: kernel_level_4_frame, + stack_top: mappings.stack_end.start_address(), + entry_point: mappings.entry_point, + boot_info, + }; + + log::info!( + "Jumping to kernel entry point at {:?}", + addresses.entry_point + ); + + unsafe { + context_switch(addresses, kernel_page_table, two_frames); + } +} + +pub struct PageTables { + pub bootloader: OffsetPageTable<'static>, + pub kernel: OffsetPageTable<'static>, + pub kernel_level_4_frame: PhysFrame, +} + +/// Performs the actual context switch +/// +/// This function should stay small because it needs to be identity-mapped. +unsafe fn context_switch( + addresses: Addresses, + mut kernel_page_table: OffsetPageTable, + mut frame_allocator: impl FrameAllocator, +) -> ! { + // identity-map current and next frame, so that we don't get an immediate pagefault + // after switching the active page table + let current_addr = PhysAddr::new(registers::read_rip()); + let current_frame: PhysFrame = PhysFrame::containing_address(current_addr); + for frame in PhysFrame::range_inclusive(current_frame, current_frame + 1) { + unsafe { + kernel_page_table.identity_map(frame, PageTableFlags::PRESENT, &mut frame_allocator) + } + .unwrap() + .flush(); + } + + // we don't need the kernel page table anymore + mem::drop(kernel_page_table); + + // do the context switch + unsafe { + asm!( + "mov cr3, {}; mov rsp, {}; push 0; jmp {}", + in(reg) addresses.page_table.start_address().as_u64(), + in(reg) addresses.stack_top.as_u64(), + in(reg) addresses.entry_point.as_u64(), + in("rdi") addresses.boot_info as *const _ as usize, + ); + } + unreachable!(); +} + +pub struct Addresses { + page_table: PhysFrame, + stack_top: VirtAddr, + entry_point: VirtAddr, + boot_info: &'static mut crate::boot_info::BootInfo, +} + +pub struct TwoFrames { + frames: [Option; 2], +} + +impl TwoFrames { + pub fn new(frame_allocator: &mut impl FrameAllocator) -> Self { + TwoFrames { + frames: [ + Some(frame_allocator.allocate_frame().unwrap()), + Some(frame_allocator.allocate_frame().unwrap()), + ], + } + } +} + +unsafe impl FrameAllocator for TwoFrames { + fn allocate_frame(&mut self) -> Option> { + self.frames.iter_mut().find_map(|f| f.take()) + } +} + +fn boot_info_location() -> VirtAddr { + VirtAddr::new(0x_0000_00bb_bbbb_0000) +} + +fn frame_buffer_location() -> Page { + Page::containing_address(VirtAddr::new(0x_0000_00cc_cccc_0000)) +} -pub mod legacy_memory_region; \ No newline at end of file +fn kernel_stack_start_location() -> Page { + Page::containing_address(VirtAddr::new(0x_0000_0fff_0000_0000)) +} diff --git a/src/binary/uefi/mod.rs b/src/binary/uefi/mod.rs index af9ca0e9..7679880c 100644 --- a/src/binary/uefi/mod.rs +++ b/src/binary/uefi/mod.rs @@ -1,20 +1 @@ -pub use logger::{FrameBufferInfo, PixelFormat}; -use x86_64::{VirtAddr, structures::paging::{FrameAllocator, MapperAllSizes, Size4KiB}}; - -mod load_kernel; -pub mod logger; mod memory_descriptor; - -pub fn init_logger(framebuffer: &'static mut [u8], info: FrameBufferInfo) { - let logger = logger::LOGGER.get_or_init(move || logger::LockedLogger::new(framebuffer, info)); - log::set_logger(logger).expect("logger already set"); - log::set_max_level(log::LevelFilter::Trace); -} - -pub fn load_kernel( - kernel: &'static [u8], - page_table: &mut impl MapperAllSizes, - frame_allocator: &mut impl FrameAllocator, -) -> VirtAddr { - load_kernel::load_kernel(kernel, page_table, frame_allocator).expect("Failed to parse kernel") -} From 90a36ad80c6d0a3fbe114aa52f581f752a80024a Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 24 Aug 2020 16:07:29 +0200 Subject: [PATCH 068/174] Run cargo fmt for builder executable --- src/bin/builder.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bin/builder.rs b/src/bin/builder.rs index e82a724a..ca740917 100644 --- a/src/bin/builder.rs +++ b/src/bin/builder.rs @@ -78,7 +78,8 @@ fn main() -> anyhow::Result<()> { let mut cmd = Command::new(env!("CARGO")); cmd.arg(build_or_run).arg("--bin").arg("uefi"); cmd.arg("--target").arg("x86_64-unknown-uefi"); - cmd.arg("--features").arg(args.features.join(" ") + " uefi_bin"); + cmd.arg("--features") + .arg(args.features.join(" ") + " uefi_bin"); cmd.arg("-Zbuild-std=core"); if let Some(target_dir) = &args.target_dir { cmd.arg("--target-dir").arg(target_dir); @@ -97,7 +98,8 @@ fn main() -> anyhow::Result<()> { cmd.arg("--profile").arg("release"); cmd.arg("-Z").arg("unstable-options"); cmd.arg("--target").arg("x86_64-bootloader.json"); - cmd.arg("--features").arg(args.features.join(" ") + " bios_bin"); + cmd.arg("--features") + .arg(args.features.join(" ") + " bios_bin"); cmd.arg("-Zbuild-std=core"); if let Some(target_dir) = &args.target_dir { cmd.arg("--target-dir").arg(target_dir); From 07697e96f36a09e63eeeca0c357c90121b31ba0b Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 24 Aug 2020 16:08:08 +0200 Subject: [PATCH 069/174] Port bios executable to the new abstractions in the `binary` module --- src/bin/bios.rs | 344 ++++++++++++---------------------- src/boot_info.rs | 42 ++--- src/boot_info_uefi.rs | 17 -- src/frame_allocator.rs | 127 ------------- src/lib.rs | 19 +- src/page_table.rs | 221 ---------------------- src/printer/mod.rs | 11 -- src/printer/vga_320x200.rs | 80 -------- src/printer/vga_text_80x25.rs | 37 ---- src/sse.rs | 17 -- 10 files changed, 141 insertions(+), 774 deletions(-) delete mode 100644 src/boot_info_uefi.rs delete mode 100644 src/frame_allocator.rs delete mode 100644 src/page_table.rs delete mode 100644 src/printer/mod.rs delete mode 100644 src/printer/vga_320x200.rs delete mode 100644 src/printer/vga_text_80x25.rs delete mode 100644 src/sse.rs diff --git a/src/bin/bios.rs b/src/bin/bios.rs index b89e2c07..e88a6b2f 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -1,6 +1,8 @@ #![feature(lang_items)] #![feature(global_asm)] #![feature(llvm_asm)] +#![feature(asm)] +#![feature(slice_fill)] #![no_std] #![no_main] @@ -9,19 +11,12 @@ compile_error!("The bootloader crate must be compiled for the `x86_64-bootloader extern crate rlibc; -use bootloader::{ - boot_info, bootinfo::BootInfo, frame_allocator, frame_range, level4_entries, page_table, - printer, -}; -use core::convert::TryInto; use core::panic::PanicInfo; -use core::{mem, slice}; -use fixedvec::alloc_stack; +use core::slice; use usize_conversions::usize_from; -use x86_64::instructions::tlb; +use x86_64::structures::paging::{FrameAllocator, OffsetPageTable}; use x86_64::structures::paging::{ - page_table::PageTableEntry, Mapper, Page, PageTable, PageTableFlags, PageTableIndex, PhysFrame, - RecursivePageTable, Size2MiB, Size4KiB, + Mapper, PageTable, PageTableFlags, PhysFrame, Size2MiB, Size4KiB, }; use x86_64::{PhysAddr, VirtAddr}; @@ -44,28 +39,6 @@ global_asm!(include_str!("../asm/video_mode/vga_320x200.s")); #[cfg(not(feature = "vga_320x200"))] global_asm!(include_str!("../asm/video_mode/vga_text_80x25.s")); -unsafe fn context_switch(boot_info: VirtAddr, entry_point: VirtAddr, stack_pointer: VirtAddr) -> ! { - llvm_asm!("call $1; ${:private}.spin.${:uid}: jmp ${:private}.spin.${:uid}" :: - "{rsp}"(stack_pointer), "r"(entry_point), "{rdi}"(boot_info) :: "intel"); - ::core::hint::unreachable_unchecked() -} - -pub struct IdentityMappedAddr(PhysAddr); - -impl IdentityMappedAddr { - fn phys(&self) -> PhysAddr { - self.0 - } - - fn virt(&self) -> VirtAddr { - VirtAddr::new(self.0.as_u64()) - } - - fn as_u64(&self) -> u64 { - self.0.as_u64() - } -} - // Symbols defined in `linker.ld` extern "C" { static mmap_ent: usize; @@ -73,11 +46,6 @@ extern "C" { static _kernel_start_addr: usize; static _kernel_end_addr: usize; static _kernel_size: usize; - static __page_table_start: usize; - static __page_table_end: usize; - static __bootloader_end: usize; - static __bootloader_start: usize; - static _p4: usize; } #[no_mangle] @@ -90,176 +58,91 @@ pub unsafe extern "C" fn stage_4() -> ! { let kernel_size = &_kernel_size as *const _ as u64; let memory_map_addr = &_memory_map as *const _ as u64; let memory_map_entry_count = (mmap_ent & 0xff) as u64; // Extract lower 8 bits - let page_table_start = &__page_table_start as *const _ as u64; - let page_table_end = &__page_table_end as *const _ as u64; - let bootloader_start = &__bootloader_start as *const _ as u64; - let bootloader_end = &__bootloader_end as *const _ as u64; - let p4_physical = &_p4 as *const _ as u64; bootloader_main( - IdentityMappedAddr(PhysAddr::new(kernel_start)), + PhysAddr::new(kernel_start), kernel_size, VirtAddr::new(memory_map_addr), memory_map_entry_count, - PhysAddr::new(page_table_start), - PhysAddr::new(page_table_end), - PhysAddr::new(bootloader_start), - PhysAddr::new(bootloader_end), - PhysAddr::new(p4_physical), ) } fn bootloader_main( - kernel_start: IdentityMappedAddr, + kernel_start: PhysAddr, kernel_size: u64, memory_map_addr: VirtAddr, memory_map_entry_count: u64, - page_table_start: PhysAddr, - page_table_end: PhysAddr, - bootloader_start: PhysAddr, - bootloader_end: PhysAddr, - p4_physical: PhysAddr, ) -> ! { - use bootloader::bootinfo::{MemoryRegion, MemoryRegionType}; - use fixedvec::FixedVec; - use xmas_elf::program::{ProgramHeader, ProgramHeader64}; - - printer::Printer.clear_screen(); + use bootloader::binary::{bios::E820MemoryRegion, legacy_memory_region::LegacyFrameAllocator}; - let mut memory_map = boot_info::create_from(memory_map_addr, memory_map_entry_count); + let framebuffer_addr = PhysAddr::new(0xa0000); + let framebuffer_size = 320 * 200; + init_logger(framebuffer_addr, framebuffer_size); - let max_phys_addr = memory_map + let e820_memory_map = { + let ptr = usize_from(memory_map_addr.as_u64()) as *const E820MemoryRegion; + unsafe { slice::from_raw_parts(ptr, usize_from(memory_map_entry_count)) } + }; + let max_phys_addr = e820_memory_map .iter() - .map(|r| r.range.end_addr()) + .map(|r| r.start_addr + r.len) .max() .expect("no physical memory regions found"); - // Extract required information from the ELF file. - let mut preallocated_space = alloc_stack!([ProgramHeader64; 32]); - let mut segments = FixedVec::new(&mut preallocated_space); - let entry_point; + let mut frame_allocator = { + let kernel_end = PhysFrame::containing_address(kernel_start.phys() + kernel_size - 1u64); + let next_free = kernel_end + 1; + LegacyFrameAllocator::new_starting_at(next_free, e820_memory_map.iter().copied()) + }; + + // We identity-map all memory, so the offset between physical and virtual addresses is 0 + let phys_offset = VirtAddr::new(0); + + let mut bootloader_page_table = { + let frame = x86_64::registers::control::Cr3::read().0; + let table: *mut PageTable = (phys_offset + frame.start_address().as_u64()).as_mut_ptr(); + unsafe { OffsetPageTable::new(&mut *table, phys_offset) } + }; + // identity-map remaining physical memory (first gigabyte is already identity-mapped) { - let kernel_start_ptr = usize_from(kernel_start.as_u64()) as *const u8; - let kernel = unsafe { slice::from_raw_parts(kernel_start_ptr, usize_from(kernel_size)) }; - let elf_file = xmas_elf::ElfFile::new(kernel).unwrap(); - xmas_elf::header::sanity_check(&elf_file).unwrap(); - - entry_point = elf_file.header.pt2.entry_point(); - - for program_header in elf_file.program_iter() { - match program_header { - ProgramHeader::Ph64(header) => segments - .push(*header) - .expect("does not support more than 32 program segments"), - ProgramHeader::Ph32(_) => panic!("does not support 32 bit elf files"), - } + let start_frame: PhysFrame = + PhysFrame::containing_address(PhysAddr::new(4096 * 512 * 512)); + let end_frame = PhysFrame::containing_address(PhysAddr::new(max_phys_addr - 1)); + for frame in PhysFrame::range_inclusive(start_frame, end_frame) { + unsafe { + bootloader_page_table + .identity_map( + frame, + PageTableFlags::PRESENT | PageTableFlags::WRITABLE, + &mut frame_allocator, + ) + .unwrap() + .flush() + }; } } - // Mark used virtual addresses - let mut level4_entries = level4_entries::UsedLevel4Entries::new(&segments); - - // Enable support for the no-execute bit in page tables. - enable_nxe_bit(); - - // Create a recursive page table entry - let recursive_index = PageTableIndex::new(level4_entries.get_free_entry().try_into().unwrap()); - let mut entry = PageTableEntry::new(); - entry.set_addr( - p4_physical, - PageTableFlags::PRESENT | PageTableFlags::WRITABLE, - ); - - // Write the recursive entry into the page table - let page_table = unsafe { &mut *(p4_physical.as_u64() as *mut PageTable) }; - page_table[recursive_index] = entry; - tlb::flush_all(); + let page_tables = create_page_tables(&mut frame_allocator); - let recursive_page_table_addr = Page::from_page_table_indices( - recursive_index, - recursive_index, - recursive_index, - recursive_index, - ) - .start_address(); - let page_table = unsafe { &mut *(recursive_page_table_addr.as_mut_ptr()) }; - let mut rec_page_table = - RecursivePageTable::new(page_table).expect("recursive page table creation failed"); - - // Create a frame allocator, which marks allocated frames as used in the memory map. - let mut frame_allocator = frame_allocator::FrameAllocator { - memory_map: &mut memory_map, + let kernel = { + let ptr = kernel_start.as_u64() as *const u8; + unsafe { slice::from_raw_parts(ptr, usize_from(kernel_size)) } }; - // Mark already used memory areas in frame allocator. - { - let zero_frame: PhysFrame = PhysFrame::from_start_address(PhysAddr::new(0)).unwrap(); - frame_allocator.mark_allocated_region(MemoryRegion { - range: frame_range(PhysFrame::range(zero_frame, zero_frame + 1)), - region_type: MemoryRegionType::FrameZero, - }); - let bootloader_start_frame = PhysFrame::containing_address(bootloader_start); - let bootloader_end_frame = PhysFrame::containing_address(bootloader_end - 1u64); - let bootloader_memory_area = - PhysFrame::range(bootloader_start_frame, bootloader_end_frame + 1); - frame_allocator.mark_allocated_region(MemoryRegion { - range: frame_range(bootloader_memory_area), - region_type: MemoryRegionType::Bootloader, - }); - let kernel_start_frame = PhysFrame::containing_address(kernel_start.phys()); - let kernel_end_frame = - PhysFrame::containing_address(kernel_start.phys() + kernel_size - 1u64); - let kernel_memory_area = PhysFrame::range(kernel_start_frame, kernel_end_frame + 1); - frame_allocator.mark_allocated_region(MemoryRegion { - range: frame_range(kernel_memory_area), - region_type: MemoryRegionType::Kernel, - }); - let page_table_start_frame = PhysFrame::containing_address(page_table_start); - let page_table_end_frame = PhysFrame::containing_address(page_table_end - 1u64); - let page_table_memory_area = - PhysFrame::range(page_table_start_frame, page_table_end_frame + 1); - frame_allocator.mark_allocated_region(MemoryRegion { - range: frame_range(page_table_memory_area), - region_type: MemoryRegionType::PageTable, - }); - } + bootloader::binary::load_and_switch_to_kernel( + kernel, + frame_allocator, + page_tables, + framebuffer_addr, + usize_from(framebuffer_size), + ); - // Unmap the ELF file. - let kernel_start_page: Page = Page::containing_address(kernel_start.virt()); - let kernel_end_page: Page = - Page::containing_address(kernel_start.virt() + kernel_size - 1u64); - for page in Page::range_inclusive(kernel_start_page, kernel_end_page) { - rec_page_table.unmap(page).expect("dealloc error").1.flush(); - } + /* + // Mark used virtual addresses + let mut level4_entries = level4_entries::UsedLevel4Entries::new(&segments); - // Map a page for the boot info structure - let boot_info_page = { - let page: Page = match BOOT_INFO_ADDRESS { - Some(addr) => Page::containing_address(VirtAddr::new(addr)), - None => Page::from_page_table_indices( - level4_entries.get_free_entry(), - PageTableIndex::new(0), - PageTableIndex::new(0), - PageTableIndex::new(0), - ), - }; - let frame = frame_allocator - .allocate_frame(MemoryRegionType::BootInfo) - .expect("frame allocation failed"); - let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - unsafe { - page_table::map_page( - page, - frame, - flags, - &mut rec_page_table, - &mut frame_allocator, - ) - } - .expect("Mapping of bootinfo page failed") - .flush(); - page - }; + // Enable support for the no-execute bit in page tables. + enable_nxe_bit(); // If no kernel stack address is provided, map the kernel stack after the boot info page let kernel_stack_address = match KERNEL_STACK_ADDRESS { @@ -267,16 +150,6 @@ fn bootloader_main( None => boot_info_page + 1, }; - // Map kernel segments. - let kernel_memory_info = page_table::map_kernel( - kernel_start.phys(), - kernel_stack_address, - KERNEL_STACK_SIZE, - &segments, - &mut rec_page_table, - &mut frame_allocator, - ) - .expect("kernel mapping failed"); let physical_memory_offset = if cfg!(feature = "map_physical_memory") { let physical_memory_offset = PHYSICAL_MEMORY_OFFSET.unwrap_or_else(|| { @@ -318,18 +191,6 @@ fn bootloader_main( 0 // Value is unused by BootInfo::new, so this doesn't matter }; - // Construct boot info structure. - let mut boot_info = BootInfo::new( - memory_map, - kernel_memory_info.tls_segment, - recursive_page_table_addr.as_u64(), - physical_memory_offset, - ); - boot_info.memory_map.sort(); - - // Write boot info to boot info page. - let boot_info_addr = boot_info_page.start_address(); - unsafe { boot_info_addr.as_mut_ptr::().write(boot_info) }; // Make sure that the kernel respects the write-protection bits, even when in ring 0. enable_write_protect_bit(); @@ -349,8 +210,59 @@ fn bootloader_main( #[cfg(feature = "sse")] sse::enable_sse(); - let entry_point = VirtAddr::new(entry_point); - unsafe { context_switch(boot_info_addr, entry_point, kernel_memory_info.stack_end) }; + */ +} + +fn init_logger(framebuffer_start: PhysAddr, framebuffer_size: u64) { + let ptr = framebuffer_start.as_u64() as *mut u8; + let slice = unsafe { slice::from_raw_parts_mut(ptr, usize_from(framebuffer_size)) }; + slice.fill(0x4); + let info = bootloader::binary::logger::FrameBufferInfo { + horizontal_resolution: 320, + vertical_resolution: 200, + pixel_format: bootloader::binary::logger::PixelFormat::U8, + stride: 320, + }; + + bootloader::binary::init_logger(slice, info); +} + +/// Creates page table abstraction types for both the bootloader and kernel page tables. +fn create_page_tables( + frame_allocator: &mut impl FrameAllocator, +) -> bootloader::binary::PageTables { + // We identity-mapped all memory, so the offset between physical and virtual addresses is 0 + let phys_offset = VirtAddr::new(0); + + // copy the currently active level 4 page table, because it might be read-only + let bootloader_page_table = { + let frame = x86_64::registers::control::Cr3::read().0; + let table: *mut PageTable = (phys_offset + frame.start_address().as_u64()).as_mut_ptr(); + unsafe { OffsetPageTable::new(&mut *table, phys_offset) } + }; + + // create a new page table hierarchy for the kernel + let (kernel_page_table, kernel_level_4_frame) = { + // get an unused frame for new level 4 page table + let frame: PhysFrame = frame_allocator.allocate_frame().expect("no unused frames"); + log::info!("New page table at: {:#?}", &frame); + // get the corresponding virtual address + let addr = phys_offset + frame.start_address().as_u64(); + // initialize a new page table + let ptr = addr.as_mut_ptr(); + unsafe { *ptr = PageTable::new() }; + let level_4_table = unsafe { &mut *ptr }; + ( + unsafe { OffsetPageTable::new(level_4_table, phys_offset) }, + frame, + ) + }; + + bootloader::binary::PageTables { + bootloader: bootloader_page_table, + kernel: kernel_page_table, + kernel_level_4_frame, + } } fn enable_nxe_bit() { @@ -364,20 +276,14 @@ fn enable_write_protect_bit() { } #[panic_handler] -#[no_mangle] -pub fn panic(info: &PanicInfo) -> ! { - use core::fmt::Write; - write!(printer::Printer, "{}", info).unwrap(); - loop {} -} - -#[lang = "eh_personality"] -#[no_mangle] -pub extern "C" fn eh_personality() { - loop {} -} - -#[no_mangle] -pub extern "C" fn _Unwind_Resume() { - loop {} +fn panic(info: &PanicInfo) -> ! { + unsafe { + bootloader::binary::logger::LOGGER + .get() + .map(|l| l.force_unlock()) + }; + log::error!("{}", info); + loop { + unsafe { asm!("cli; hlt") }; + } } diff --git a/src/boot_info.rs b/src/boot_info.rs index fb7be909..7a888606 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -1,31 +1,17 @@ -use core::slice; +use crate::memory_map::MemoryRegion; -use crate::bootinfo::{E820MemoryRegion, MemoryMap, MemoryRegion, MemoryRegionType}; -use usize_conversions::usize_from; -use x86_64::VirtAddr; - -pub fn create_from(memory_map_addr: VirtAddr, entry_count: u64) -> MemoryMap { - let memory_map_start_ptr = usize_from(memory_map_addr.as_u64()) as *const E820MemoryRegion; - let e820_memory_map = - unsafe { slice::from_raw_parts(memory_map_start_ptr, usize_from(entry_count)) }; - - let mut memory_map = MemoryMap::new(); - for region in e820_memory_map { - memory_map.add_region(MemoryRegion::from(*region)); - } - - memory_map.sort(); - - let mut iter = memory_map.iter_mut().peekable(); - while let Some(region) = iter.next() { - if let Some(next) = iter.peek() { - if region.range.end_frame_number > next.range.start_frame_number - && region.region_type == MemoryRegionType::Usable - { - region.range.end_frame_number = next.range.start_frame_number; - } - } - } +#[derive(Debug)] +#[repr(C)] +pub struct BootInfo { + pub memory_regions: &'static mut [MemoryRegion], + pub framebuffer: FrameBufferInfo, +} - memory_map +#[derive(Debug)] +#[repr(C)] +pub struct FrameBufferInfo { + pub start_addr: u64, + pub len: usize, } + +extern "C" fn _assert_ffi(_boot_info: &'static mut BootInfo) {} diff --git a/src/boot_info_uefi.rs b/src/boot_info_uefi.rs deleted file mode 100644 index 7a888606..00000000 --- a/src/boot_info_uefi.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::memory_map::MemoryRegion; - -#[derive(Debug)] -#[repr(C)] -pub struct BootInfo { - pub memory_regions: &'static mut [MemoryRegion], - pub framebuffer: FrameBufferInfo, -} - -#[derive(Debug)] -#[repr(C)] -pub struct FrameBufferInfo { - pub start_addr: u64, - pub len: usize, -} - -extern "C" fn _assert_ffi(_boot_info: &'static mut BootInfo) {} diff --git a/src/frame_allocator.rs b/src/frame_allocator.rs deleted file mode 100644 index 1683fe29..00000000 --- a/src/frame_allocator.rs +++ /dev/null @@ -1,127 +0,0 @@ -use super::{frame_range, phys_frame_range}; -use crate::bootinfo::{MemoryMap, MemoryRegion, MemoryRegionType}; -use x86_64::structures::paging::{frame::PhysFrameRange, PhysFrame}; - -pub struct FrameAllocator<'a> { - pub memory_map: &'a mut MemoryMap, -} - -impl<'a> FrameAllocator<'a> { - pub fn allocate_frame(&mut self, region_type: MemoryRegionType) -> Option { - // try to find an existing region of same type that can be enlarged - let mut iter = self.memory_map.iter_mut().peekable(); - while let Some(region) = iter.next() { - if region.region_type == region_type { - if let Some(next) = iter.peek() { - if next.range.start_frame_number == region.range.end_frame_number - && next.region_type == MemoryRegionType::Usable - && !next.range.is_empty() - { - let frame = phys_frame_range(region.range).end; - region.range.end_frame_number += 1; - iter.next().unwrap().range.start_frame_number += 1; - return Some(frame); - } - } - } - } - - fn split_usable_region<'a, I>(iter: &mut I) -> Option<(PhysFrame, PhysFrameRange)> - where - I: Iterator, - { - for region in iter { - if region.region_type != MemoryRegionType::Usable { - continue; - } - if region.range.is_empty() { - continue; - } - - let frame = phys_frame_range(region.range).start; - region.range.start_frame_number += 1; - return Some((frame, PhysFrame::range(frame, frame + 1))); - } - None - } - - let result = if region_type == MemoryRegionType::PageTable { - // prevent fragmentation when page tables are allocated in between - split_usable_region(&mut self.memory_map.iter_mut().rev()) - } else { - split_usable_region(&mut self.memory_map.iter_mut()) - }; - - if let Some((frame, range)) = result { - self.memory_map.add_region(MemoryRegion { - range: frame_range(range), - region_type, - }); - Some(frame) - } else { - None - } - } - - /// Marks the passed region in the memory map. - /// - /// Panics if a non-usable region (e.g. a reserved region) overlaps with the passed region. - pub fn mark_allocated_region(&mut self, region: MemoryRegion) { - for r in self.memory_map.iter_mut() { - if region.range.start_frame_number >= r.range.end_frame_number { - continue; - } - if region.range.end_frame_number <= r.range.start_frame_number { - continue; - } - - if r.region_type != MemoryRegionType::Usable { - panic!( - "region {:x?} overlaps with non-usable region {:x?}", - region, r - ); - } - - if region.range.start_frame_number == r.range.start_frame_number { - if region.range.end_frame_number < r.range.end_frame_number { - // Case: (r = `r`, R = `region`) - // ----rrrrrrrrrrr---- - // ----RRRR----------- - r.range.start_frame_number = region.range.end_frame_number; - self.memory_map.add_region(region); - } else { - // Case: (r = `r`, R = `region`) - // ----rrrrrrrrrrr---- - // ----RRRRRRRRRRRRRR- - *r = region; - } - } else if region.range.start_frame_number > r.range.start_frame_number { - if region.range.end_frame_number < r.range.end_frame_number { - // Case: (r = `r`, R = `region`) - // ----rrrrrrrrrrr---- - // ------RRRR--------- - let mut behind_r = r.clone(); - behind_r.range.start_frame_number = region.range.end_frame_number; - r.range.end_frame_number = region.range.start_frame_number; - self.memory_map.add_region(behind_r); - self.memory_map.add_region(region); - } else { - // Case: (r = `r`, R = `region`) - // ----rrrrrrrrrrr---- - // -----------RRRR---- or - // -------------RRRR-- - r.range.end_frame_number = region.range.start_frame_number; - self.memory_map.add_region(region); - } - } else { - // Case: (r = `r`, R = `region`) - // ----rrrrrrrrrrr---- - // --RRRR------------- - r.range.start_frame_number = region.range.end_frame_number; - self.memory_map.add_region(region); - } - return; - } - panic!("region {:x?} is not a usable memory region", region); - } -} diff --git a/src/lib.rs b/src/lib.rs index bcabc682..eeb7ae52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ #![feature(asm)] #![feature(unsafe_block_in_unsafe_fn)] #![feature(maybe_uninit_slice_assume_init)] +#![feature(maybe_uninit_extra)] #![deny(unsafe_op_in_unsafe_fn)] //#![warn(missing_docs)] @@ -21,15 +22,10 @@ use x86_64::{ structures::paging::{frame::PhysFrameRange, PhysFrame}, PhysAddr, }; -#[cfg(feature = "uefi_bin")] -use x86_64::{ - structures::paging::{FrameAllocator, MapperAllSizes, Size4KiB}, - VirtAddr, -}; pub mod bootinfo; -pub mod boot_info_uefi; +pub mod boot_info; pub mod memory_map; #[cfg(feature = "binary")] @@ -38,19 +34,8 @@ pub mod binary; #[cfg(feature = "builder")] pub mod disk_image; -#[cfg(feature = "bios_bin")] -pub mod boot_info; -#[cfg(feature = "bios_bin")] -pub mod frame_allocator; #[cfg(feature = "bios_bin")] pub mod level4_entries; -#[cfg(feature = "bios_bin")] -pub mod page_table; -#[cfg(feature = "bios_bin")] -pub mod printer; - -#[cfg(all(feature = "bios_bin", feature = "sse"))] -pub mod sse; #[cfg(target_arch = "x86")] compile_error!( diff --git a/src/page_table.rs b/src/page_table.rs deleted file mode 100644 index 80d1b9c4..00000000 --- a/src/page_table.rs +++ /dev/null @@ -1,221 +0,0 @@ -use crate::bootinfo::MemoryRegionType; -use crate::bootinfo::TlsTemplate; -use crate::frame_allocator::FrameAllocator; -use fixedvec::FixedVec; -use x86_64::structures::paging::mapper::{MapToError, MapperFlush, UnmapError}; -use x86_64::structures::paging::{ - self, Mapper, Page, PageSize, PageTableFlags, PhysFrame, RecursivePageTable, Size4KiB, -}; -use x86_64::{align_up, PhysAddr, VirtAddr}; -use xmas_elf::program::{self, ProgramHeader64}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MemoryInfo { - pub stack_end: VirtAddr, - pub tls_segment: Option, -} - -#[derive(Debug)] -pub enum MapKernelError { - Mapping(MapToError), - MultipleTlsSegments, -} - -impl From> for MapKernelError { - fn from(e: MapToError) -> Self { - MapKernelError::Mapping(e) - } -} - -pub fn map_kernel( - kernel_start: PhysAddr, - stack_start: Page, - stack_size: u64, - segments: &FixedVec, - page_table: &mut RecursivePageTable, - frame_allocator: &mut FrameAllocator, -) -> Result { - let mut tls_segment = None; - for segment in segments { - let tls = map_segment(segment, kernel_start, page_table, frame_allocator)?; - if let Some(tls) = tls { - if tls_segment.replace(tls).is_some() { - return Err(MapKernelError::MultipleTlsSegments); - } - } - } - - // Create a stack - let stack_start = stack_start + 1; // Leave the first page unmapped as a 'guard page' - let stack_end = stack_start + stack_size; // stack_size is in pages - - let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - let region_type = MemoryRegionType::KernelStack; - - for page in Page::range(stack_start, stack_end) { - let frame = frame_allocator - .allocate_frame(region_type) - .ok_or(MapToError::FrameAllocationFailed)?; - unsafe { map_page(page, frame, flags, page_table, frame_allocator)? }.flush(); - } - - Ok(MemoryInfo { - stack_end: stack_end.start_address(), - tls_segment, - }) -} - -pub fn map_segment( - segment: &ProgramHeader64, - kernel_start: PhysAddr, - page_table: &mut RecursivePageTable, - frame_allocator: &mut FrameAllocator, -) -> Result, MapToError> { - let typ = segment.get_type().unwrap(); - match typ { - program::Type::Load => { - let mem_size = segment.mem_size; - let file_size = segment.file_size; - let file_offset = segment.offset; - let phys_start_addr = kernel_start + file_offset; - let virt_start_addr = VirtAddr::new(segment.virtual_addr); - - let start_page: Page = Page::containing_address(virt_start_addr); - let start_frame = PhysFrame::containing_address(phys_start_addr); - let end_frame = PhysFrame::containing_address(phys_start_addr + file_size - 1u64); - - let flags = segment.flags; - let mut page_table_flags = PageTableFlags::PRESENT; - if !flags.is_execute() { - page_table_flags |= PageTableFlags::NO_EXECUTE - }; - if flags.is_write() { - page_table_flags |= PageTableFlags::WRITABLE - }; - - for frame in PhysFrame::range_inclusive(start_frame, end_frame) { - let offset = frame - start_frame; - let page = start_page + offset; - unsafe { map_page(page, frame, page_table_flags, page_table, frame_allocator)? } - .flush(); - } - - if mem_size > file_size { - // .bss section (or similar), which needs to be zeroed - let zero_start = virt_start_addr + file_size; - let zero_end = virt_start_addr + mem_size; - if zero_start.as_u64() & 0xfff != 0 { - // A part of the last mapped frame needs to be zeroed. This is - // not possible since it could already contains parts of the next - // segment. Thus, we need to copy it before zeroing. - - // TODO: search for a free page dynamically - let temp_page: Page = Page::containing_address(VirtAddr::new(0xfeeefeee000)); - let new_frame = frame_allocator - .allocate_frame(MemoryRegionType::Kernel) - .ok_or(MapToError::FrameAllocationFailed)?; - unsafe { - map_page( - temp_page.clone(), - new_frame.clone(), - page_table_flags, - page_table, - frame_allocator, - )? - } - .flush(); - - type PageArray = [u64; Size4KiB::SIZE as usize / 8]; - - let last_page = Page::containing_address(virt_start_addr + file_size - 1u64); - let last_page_ptr = last_page.start_address().as_ptr::(); - let temp_page_ptr = temp_page.start_address().as_mut_ptr::(); - - unsafe { - // copy contents - temp_page_ptr.write(last_page_ptr.read()); - } - - // remap last page - if let Err(e) = page_table.unmap(last_page.clone()) { - return Err(match e { - UnmapError::ParentEntryHugePage => MapToError::ParentEntryHugePage, - UnmapError::PageNotMapped => unreachable!(), - UnmapError::InvalidFrameAddress(_) => unreachable!(), - }); - } - - unsafe { - map_page( - last_page, - new_frame, - page_table_flags, - page_table, - frame_allocator, - )? - } - .flush(); - } - - // Map additional frames. - let start_page: Page = Page::containing_address(VirtAddr::new(align_up( - zero_start.as_u64(), - Size4KiB::SIZE, - ))); - let end_page = Page::containing_address(zero_end); - for page in Page::range_inclusive(start_page, end_page) { - let frame = frame_allocator - .allocate_frame(MemoryRegionType::Kernel) - .ok_or(MapToError::FrameAllocationFailed)?; - unsafe { - map_page(page, frame, page_table_flags, page_table, frame_allocator)? - } - .flush(); - } - - // zero - for offset in file_size..mem_size { - let addr = virt_start_addr + offset; - unsafe { addr.as_mut_ptr::().write(0) }; - } - } - - Ok(None) - } - program::Type::Tls => Ok(Some(TlsTemplate { - start_addr: segment.virtual_addr, - mem_size: segment.mem_size, - file_size: segment.file_size, - })), - _ => Ok(None), - } -} - -pub unsafe fn map_page<'a, S>( - page: Page, - phys_frame: PhysFrame, - flags: PageTableFlags, - page_table: &mut RecursivePageTable<'a>, - frame_allocator: &mut FrameAllocator, -) -> Result, MapToError> -where - S: PageSize, - RecursivePageTable<'a>: Mapper, -{ - struct PageTableAllocator<'a, 'b: 'a>(&'a mut FrameAllocator<'b>); - - unsafe impl<'a, 'b> paging::FrameAllocator for PageTableAllocator<'a, 'b> { - fn allocate_frame(&mut self) -> Option> { - self.0.allocate_frame(MemoryRegionType::PageTable) - } - } - - unsafe { - page_table.map_to( - page, - phys_frame, - flags, - &mut PageTableAllocator(frame_allocator), - ) - } -} diff --git a/src/printer/mod.rs b/src/printer/mod.rs deleted file mode 100644 index 726bb765..00000000 --- a/src/printer/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -#[cfg(not(feature = "vga_320x200"))] -pub use self::vga_text_80x25::*; - -#[cfg(feature = "vga_320x200")] -pub use self::vga_320x200::*; - -#[cfg(feature = "vga_320x200")] -mod vga_320x200; - -#[cfg(not(feature = "vga_320x200"))] -mod vga_text_80x25; diff --git a/src/printer/vga_320x200.rs b/src/printer/vga_320x200.rs deleted file mode 100644 index fb1e36af..00000000 --- a/src/printer/vga_320x200.rs +++ /dev/null @@ -1,80 +0,0 @@ -use core::fmt::{Result, Write}; -use core::sync::atomic::{AtomicUsize, Ordering}; - -const VGA_BUFFER: *mut u8 = 0xa0000 as *mut _; -const SCREEN_WIDTH: usize = 320; -const SCREEN_HEIGHT: usize = 200; - -// must not be 0 so that we don't have a .bss section -pub static X_POS: AtomicUsize = AtomicUsize::new(1); -pub static Y_POS: AtomicUsize = AtomicUsize::new(1); - -pub struct Printer; - -impl Printer { - pub fn clear_screen(&mut self) { - for i in 0..(SCREEN_WIDTH * SCREEN_HEIGHT) { - unsafe { - VGA_BUFFER.offset(i as isize).write_volatile(0); - } - } - - X_POS.store(0, Ordering::SeqCst); - Y_POS.store(0, Ordering::SeqCst); - } - - fn newline(&mut self) { - let y_pos = Y_POS.fetch_add(8, Ordering::SeqCst); - X_POS.store(0, Ordering::SeqCst); - if y_pos >= SCREEN_HEIGHT { - self.clear_screen(); - } - } - - fn write_char(&mut self, c: char) { - use font8x8::UnicodeFonts; - - if c == '\n' { - self.newline(); - return; - } - - let x_pos = X_POS.fetch_add(8, Ordering::SeqCst); - let y_pos = Y_POS.load(Ordering::SeqCst); - - match c { - ' '..='~' => { - let rendered = font8x8::BASIC_FONTS - .get(c) - .expect("character not found in basic font"); - for (y, byte) in rendered.iter().enumerate() { - for (x, bit) in (0..8).enumerate() { - if *byte & (1 << bit) == 0 { - continue; - } - let color = 0xf; - let idx = (y_pos + y) * SCREEN_WIDTH + x_pos + x; - unsafe { - VGA_BUFFER.offset(idx as isize).write_volatile(color); - } - } - } - } - _ => panic!("unprintable character"), - } - - if x_pos + 8 >= SCREEN_WIDTH { - self.newline(); - } - } -} - -impl Write for Printer { - fn write_str(&mut self, s: &str) -> Result { - for c in s.chars() { - self.write_char(c); - } - - Ok(()) - } -} diff --git a/src/printer/vga_text_80x25.rs b/src/printer/vga_text_80x25.rs deleted file mode 100644 index fd689987..00000000 --- a/src/printer/vga_text_80x25.rs +++ /dev/null @@ -1,37 +0,0 @@ -use core::fmt::{Result, Write}; -use core::sync::atomic::{AtomicUsize, Ordering}; - -const VGA_BUFFER: *mut u8 = 0xb8000 as *mut _; -const SCREEN_SIZE: usize = 80 * 25; - -// must not be 0 so that we don't have a .bss section -pub static CURRENT_OFFSET: AtomicUsize = AtomicUsize::new(160); - -pub struct Printer; - -impl Printer { - pub fn clear_screen(&mut self) { - for i in 0..SCREEN_SIZE { - unsafe { - VGA_BUFFER.offset(i as isize).write_volatile(0); - } - } - - CURRENT_OFFSET.store(0, Ordering::Relaxed); - } -} - -impl Write for Printer { - fn write_str(&mut self, s: &str) -> Result { - for byte in s.bytes() { - let index = CURRENT_OFFSET.fetch_add(2, Ordering::Relaxed) as isize; - - unsafe { - VGA_BUFFER.offset(index).write_volatile(byte); - VGA_BUFFER.offset(index + 1).write_volatile(0x4f); - } - } - - Ok(()) - } -} diff --git a/src/sse.rs b/src/sse.rs deleted file mode 100644 index b4e0a9f2..00000000 --- a/src/sse.rs +++ /dev/null @@ -1,17 +0,0 @@ -/// Enables Streaming SIMD Extensions (SSE) support for loaded kernels. -pub fn enable_sse() { - use x86_64::registers::control::{Cr0, Cr0Flags, Cr4, Cr4Flags}; - let mut flags = Cr0::read(); - flags.remove(Cr0Flags::EMULATE_COPROCESSOR); - flags.insert(Cr0Flags::MONITOR_COPROCESSOR); - unsafe { - Cr0::write(flags); - } - - let mut flags = Cr4::read(); - flags.insert(Cr4Flags::OSFXSR); - flags.insert(Cr4Flags::OSXMMEXCPT_ENABLE); - unsafe { - Cr4::write(flags); - } -} From 9713bd703a4617db50f6bbf49b64cba1b93b7c42 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 24 Aug 2020 18:38:27 +0200 Subject: [PATCH 070/174] Refactor build script and redesign configuration --- build.rs | 725 ++++++++++++++++++++------------------------------ src/config.rs | 24 ++ src/lib.rs | 1 + 3 files changed, 318 insertions(+), 432 deletions(-) create mode 100644 src/config.rs diff --git a/build.rs b/build.rs index f01dc95b..b50ea4d0 100644 --- a/build.rs +++ b/build.rs @@ -2,472 +2,333 @@ fn main() {} #[cfg(feature = "binary")] -#[derive(Default)] -struct BootloaderConfig { - physical_memory_offset: Option, - kernel_stack_address: Option, - kernel_stack_size: Option, - boot_info_address: Option, -} - -#[cfg(feature = "binary")] -fn parse_aligned_addr(key: &str, value: &str) -> u64 { - let num = if value.starts_with("0x") { - u64::from_str_radix(&value[2..], 16) - } else { - u64::from_str_radix(&value, 10) - }; - - let num = num.expect(&format!( - "`{}` in the kernel manifest must be an integer (is `{}`)", - key, value - )); - - if num % 0x1000 != 0 { - panic!( - "`{}` in the kernel manifest must be aligned to 4KiB (is `{}`)", - key, value - ); - } else { - num - } +fn main() { + binary::main(); } #[cfg(feature = "binary")] -fn parse_to_config(cfg: &mut BootloaderConfig, table: &toml::value::Table) { - use toml::Value; +mod binary { + pub fn main() { + use llvm_tools_build as llvm_tools; + use std::{ + env, + fs::{self, File}, + io::Write, + path::{Path, PathBuf}, + process::{self, Command}, + }; + use toml::Value; + + let target = env::var("TARGET").expect("TARGET not set"); + let (firmware, expected_target) = if cfg!(feature = "uefi_bin") { + ("UEFI", "x86_64-unknown-uefi") + } else if cfg!(feature = "bios_bin") { + ("BIOS", "x86_64-bootloader") + } else { + panic!( + "Either the `uefi_bin` or `bios_bin` feature must be enabled when \ + the `binary` feature is enabled" + ); + }; + if Path::new(&target) + .file_stem() + .expect("target has no file stem") + != expected_target + { + panic!( + "The {} bootloader must be compiled for the `{}` target.", + firmware, expected_target, + ); + } - for (key, value) in table { - match (key.as_str(), value.clone()) { - ("kernel-stack-address", Value::Integer(i)) - | ("physical-memory-offset", Value::Integer(i)) - | ("boot-info-address", Value::Integer(i)) => { - panic!( - "`{0}` in the kernel manifest must be given as a string, \ - as toml does not support unsigned 64-bit integers (try `{0} = \"{1}\"`)", - key.as_str(), - i - ); - } - ("kernel-stack-address", Value::String(s)) => { - cfg.kernel_stack_address = Some(parse_aligned_addr(key.as_str(), &s)); - } - ("boot-info-address", Value::String(s)) => { - cfg.boot_info_address = Some(parse_aligned_addr(key.as_str(), &s)); - } - #[cfg(not(feature = "map_physical_memory"))] - ("physical-memory-offset", Value::String(_)) => { - panic!( - "`physical-memory-offset` is only supported when the `map_physical_memory` \ - feature of the crate is enabled" + let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set")); + let kernel = PathBuf::from(match env::var("KERNEL") { + Ok(kernel) => kernel, + Err(_) => { + eprintln!( + "The KERNEL environment variable must be set for building the bootloader.\n\n\ + Please use the `cargo builder` command for building." ); + process::exit(1); } - #[cfg(feature = "map_physical_memory")] - ("physical-memory-offset", Value::String(s)) => { - cfg.physical_memory_offset = Some(parse_aligned_addr(key.as_str(), &s)); + }); + let kernel_file_name = kernel + .file_name() + .expect("KERNEL has no valid file name") + .to_str() + .expect("kernel file name not valid utf8"); + + // check that the kernel file exists + assert!( + kernel.exists(), + format!("KERNEL does not exist: {}", kernel.display()) + ); + + // get access to llvm tools shipped in the llvm-tools-preview rustup component + let llvm_tools = match llvm_tools::LlvmTools::new() { + Ok(tools) => tools, + Err(llvm_tools::Error::NotFound) => { + eprintln!("Error: llvm-tools not found"); + eprintln!("Maybe the rustup component `llvm-tools-preview` is missing?"); + eprintln!(" Install it through: `rustup component add llvm-tools-preview`"); + process::exit(1); } - ("kernel-stack-size", Value::Integer(i)) => { - if i <= 0 { - panic!("`kernel-stack-size` in kernel manifest must be positive"); - } else { - cfg.kernel_stack_size = Some(i as u64); - } - } - (s, _) => { - panic!( - "unknown key '{}' in kernel manifest \ - - you may need to update the bootloader crate", - s - ); + Err(err) => { + eprintln!("Failed to retrieve llvm-tools component: {:?}", err); + process::exit(1); } + }; + + // check that kernel executable has code in it + let llvm_size = llvm_tools + .tool(&llvm_tools::exe("llvm-size")) + .expect("llvm-size not found in llvm-tools"); + let mut cmd = Command::new(llvm_size); + cmd.arg(&kernel); + let output = cmd.output().expect("failed to run llvm-size"); + let output_str = String::from_utf8_lossy(&output.stdout); + let second_line_opt = output_str.lines().skip(1).next(); + let second_line = second_line_opt.expect("unexpected llvm-size line output"); + let text_size_opt = second_line.split_ascii_whitespace().next(); + let text_size = text_size_opt.expect("unexpected llvm-size output"); + if text_size == "0" { + panic!("Kernel executable has an empty text section. Perhaps the entry point was set incorrectly?\n\n\ + Kernel executable at `{}`\n", kernel.display()); } - } -} - -#[cfg(feature = "bios_bin")] -fn main() { - use llvm_tools_build as llvm_tools; - use std::{ - env, - fs::{self, File}, - io::Write, - path::{Path, PathBuf}, - process::{self, Command}, - }; - use toml::Value; - let target = env::var("TARGET").expect("TARGET not set"); - if Path::new(&target) - .file_stem() - .expect("target has no file stem") - != "x86_64-bootloader" - { - panic!("The BIOS bootloader must be compiled for the `x86_64-bootloader.json` target."); - } - - let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set")); - let kernel = PathBuf::from(match env::var("KERNEL") { - Ok(kernel) => kernel, - Err(_) => { - eprintln!( - "The KERNEL environment variable must be set for building the bootloader.\n\n\ - If you use `bootimage` for building you need at least version 0.7.0. You can \ - update `bootimage` by running `cargo install bootimage --force`." - ); + // strip debug symbols from kernel for faster loading + let stripped_kernel_file_name = format!("kernel_stripped-{}", kernel_file_name); + let stripped_kernel = out_dir.join(&stripped_kernel_file_name); + let objcopy = llvm_tools + .tool(&llvm_tools::exe("llvm-objcopy")) + .expect("llvm-objcopy not found in llvm-tools"); + let mut cmd = Command::new(&objcopy); + cmd.arg("--strip-debug"); + cmd.arg(&kernel); + cmd.arg(&stripped_kernel); + let exit_status = cmd + .status() + .expect("failed to run objcopy to strip debug symbols"); + if !exit_status.success() { + eprintln!("Error: Stripping debug symbols failed"); process::exit(1); } - }); - let kernel_file_name = kernel - .file_name() - .expect("KERNEL has no valid file name") - .to_str() - .expect("kernel file name not valid utf8"); - // check that the kernel file exists - assert!( - kernel.exists(), - format!("KERNEL does not exist: {}", kernel.display()) - ); - - // get access to llvm tools shipped in the llvm-tools-preview rustup component - let llvm_tools = match llvm_tools::LlvmTools::new() { - Ok(tools) => tools, - Err(llvm_tools::Error::NotFound) => { - eprintln!("Error: llvm-tools not found"); - eprintln!("Maybe the rustup component `llvm-tools-preview` is missing?"); - eprintln!(" Install it through: `rustup component add llvm-tools-preview`"); - process::exit(1); + if cfg!(feature = "uefi_bin") { + // write file for including kernel in binary + let file_path = out_dir.join("kernel_info.rs"); + let mut file = File::create(file_path).expect("failed to create kernel_info.rs"); + let kernel_size = fs::metadata(&stripped_kernel) + .expect("Failed to read file metadata of stripped kernel") + .len(); + file.write_all( + format!( + "const KERNEL_SIZE: usize = {}; const KERNEL_BYTES: [u8; KERNEL_SIZE] = *include_bytes!(\"{}\");", + kernel_size, + stripped_kernel.display(), + ) + .as_bytes(), + ) + .expect("write to kernel_info.rs failed"); } - Err(err) => { - eprintln!("Failed to retrieve llvm-tools component: {:?}", err); - process::exit(1); - } - }; - - // check that kernel executable has code in it - let llvm_size = llvm_tools - .tool(&llvm_tools::exe("llvm-size")) - .expect("llvm-size not found in llvm-tools"); - let mut cmd = Command::new(llvm_size); - cmd.arg(&kernel); - let output = cmd.output().expect("failed to run llvm-size"); - let output_str = String::from_utf8_lossy(&output.stdout); - let second_line_opt = output_str.lines().skip(1).next(); - let second_line = second_line_opt.expect("unexpected llvm-size line output"); - let text_size_opt = second_line.split_ascii_whitespace().next(); - let text_size = text_size_opt.expect("unexpected llvm-size output"); - if text_size == "0" { - panic!("Kernel executable has an empty text section. Perhaps the entry point was set incorrectly?\n\n\ - Kernel executable at `{}`\n", kernel.display()); - } - - // strip debug symbols from kernel for faster loading - let stripped_kernel_file_name = format!("kernel_stripped-{}", kernel_file_name); - let stripped_kernel = out_dir.join(&stripped_kernel_file_name); - let objcopy = llvm_tools - .tool(&llvm_tools::exe("llvm-objcopy")) - .expect("llvm-objcopy not found in llvm-tools"); - let mut cmd = Command::new(&objcopy); - cmd.arg("--strip-debug"); - cmd.arg(&kernel); - cmd.arg(&stripped_kernel); - let exit_status = cmd - .status() - .expect("failed to run objcopy to strip debug symbols"); - if !exit_status.success() { - eprintln!("Error: Stripping debug symbols failed"); - process::exit(1); - } - // wrap the kernel executable as binary in a new ELF file - let stripped_kernel_file_name_replaced = stripped_kernel_file_name - .replace('-', "_") - .replace('.', "_"); - let kernel_bin = out_dir.join(format!("kernel_bin-{}.o", kernel_file_name)); - let kernel_archive = out_dir.join(format!("libkernel_bin-{}.a", kernel_file_name)); - let mut cmd = Command::new(&objcopy); - cmd.arg("-I").arg("binary"); - cmd.arg("-O").arg("elf64-x86-64"); - cmd.arg("--binary-architecture=i386:x86-64"); - cmd.arg("--rename-section").arg(".data=.kernel"); - cmd.arg("--redefine-sym").arg(format!( - "_binary_{}_start=_kernel_start_addr", - stripped_kernel_file_name_replaced - )); - cmd.arg("--redefine-sym").arg(format!( - "_binary_{}_end=_kernel_end_addr", - stripped_kernel_file_name_replaced - )); - cmd.arg("--redefine-sym").arg(format!( - "_binary_{}_size=_kernel_size", - stripped_kernel_file_name_replaced - )); - cmd.current_dir(&out_dir); - cmd.arg(&stripped_kernel_file_name); - cmd.arg(&kernel_bin); - let exit_status = cmd.status().expect("failed to run objcopy"); - if !exit_status.success() { - eprintln!("Error: Running objcopy failed"); - process::exit(1); - } - - // create an archive for linking - let ar = llvm_tools - .tool(&llvm_tools::exe("llvm-ar")) - .unwrap_or_else(|| { - eprintln!("Failed to retrieve llvm-ar component"); - eprint!("This component is available since nightly-2019-03-29,"); - eprintln!("so try updating your toolchain if you're using an older nightly"); - process::exit(1); - }); - let mut cmd = Command::new(ar); - cmd.arg("crs"); - cmd.arg(&kernel_archive); - cmd.arg(&kernel_bin); - let exit_status = cmd.status().expect("failed to run ar"); - if !exit_status.success() { - eprintln!("Error: Running ar failed"); - process::exit(1); - } - - // Parse the kernel's Cargo.toml which is given to us by bootimage - let mut bootloader_config = BootloaderConfig::default(); - - match env::var("KERNEL_MANIFEST") { - Err(env::VarError::NotPresent) => { - panic!("The KERNEL_MANIFEST environment variable must be set for building the bootloader.\n\n\ - If you use `bootimage` for building you need at least version 0.7.7. You can \ - update `bootimage` by running `cargo install bootimage --force`."); - } - Err(env::VarError::NotUnicode(_)) => { - panic!("The KERNEL_MANIFEST environment variable contains invalid unicode") - } - Ok(path) => { - println!("cargo:rerun-if-changed={}", path); - - let contents = fs::read_to_string(&path).expect(&format!( - "failed to read kernel manifest file (path: {})", - path + if cfg!(feature = "bios_bin") { + // wrap the kernel executable as binary in a new ELF file + let stripped_kernel_file_name_replaced = stripped_kernel_file_name + .replace('-', "_") + .replace('.', "_"); + let kernel_bin = out_dir.join(format!("kernel_bin-{}.o", kernel_file_name)); + let kernel_archive = out_dir.join(format!("libkernel_bin-{}.a", kernel_file_name)); + let mut cmd = Command::new(&objcopy); + cmd.arg("-I").arg("binary"); + cmd.arg("-O").arg("elf64-x86-64"); + cmd.arg("--binary-architecture=i386:x86-64"); + cmd.arg("--rename-section").arg(".data=.kernel"); + cmd.arg("--redefine-sym").arg(format!( + "_binary_{}_start=_kernel_start_addr", + stripped_kernel_file_name_replaced )); - - let manifest = contents - .parse::() - .expect("failed to parse kernel's Cargo.toml"); - - let table = manifest - .get("package") - .and_then(|table| table.get("metadata")) - .and_then(|table| table.get("bootloader")) - .and_then(|table| table.as_table()); - - if let Some(table) = table { - parse_to_config(&mut bootloader_config, table); + cmd.arg("--redefine-sym").arg(format!( + "_binary_{}_end=_kernel_end_addr", + stripped_kernel_file_name_replaced + )); + cmd.arg("--redefine-sym").arg(format!( + "_binary_{}_size=_kernel_size", + stripped_kernel_file_name_replaced + )); + cmd.current_dir(&out_dir); + cmd.arg(&stripped_kernel_file_name); + cmd.arg(&kernel_bin); + let exit_status = cmd.status().expect("failed to run objcopy"); + if !exit_status.success() { + eprintln!("Error: Running objcopy failed"); + process::exit(1); } - } - } - // Configure constants for the bootloader - // We leave some variables as Option rather than hardcoding their defaults so that they - // can be calculated dynamically by the bootloader. - 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( - format!( - "const PHYSICAL_MEMORY_OFFSET: Option = {:?}; - const KERNEL_STACK_ADDRESS: Option = {:?}; - const KERNEL_STACK_SIZE: u64 = {}; - const BOOT_INFO_ADDRESS: Option = {:?};", - bootloader_config.physical_memory_offset, - bootloader_config.kernel_stack_address, - bootloader_config.kernel_stack_size.unwrap_or(512), // size is in number of pages - bootloader_config.boot_info_address, - ) - .as_bytes(), - ) - .expect("write to bootloader_config.rs failed"); - - // pass link arguments to rustc - println!("cargo:rustc-link-search=native={}", out_dir.display()); - println!( - "cargo:rustc-link-lib=static=kernel_bin-{}", - kernel_file_name - ); - - println!("cargo:rerun-if-env-changed=KERNEL"); - println!("cargo:rerun-if-env-changed=KERNEL_MANIFEST"); - println!("cargo:rerun-if-changed={}", kernel.display()); - println!("cargo:rerun-if-changed=build.rs"); -} - -#[cfg(feature = "uefi_bin")] -fn main() { - use llvm_tools_build as llvm_tools; - use std::{ - env, - fs::{self, File}, - io::Write, - path::{Path, PathBuf}, - process::{self, Command}, - }; - use toml::Value; - - let target = env::var("TARGET").expect("TARGET not set"); - if Path::new(&target) - .file_stem() - .expect("target has no file stem") - != "x86_64-unknown-uefi" - { - panic!("The UEFI bootloader must be compiled for the `x86_64-unknown-uefi.json` target."); - } + // create an archive for linking + let ar = llvm_tools + .tool(&llvm_tools::exe("llvm-ar")) + .unwrap_or_else(|| { + eprintln!("Failed to retrieve llvm-ar component"); + eprint!("This component is available since nightly-2019-03-29,"); + eprintln!("so try updating your toolchain if you're using an older nightly"); + process::exit(1); + }); + let mut cmd = Command::new(ar); + cmd.arg("crs"); + cmd.arg(&kernel_archive); + cmd.arg(&kernel_bin); + let exit_status = cmd.status().expect("failed to run ar"); + if !exit_status.success() { + eprintln!("Error: Running ar failed"); + process::exit(1); + } - let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set")); - let kernel = PathBuf::from(match env::var("KERNEL") { - Ok(kernel) => kernel, - Err(_) => { - eprintln!( - "The KERNEL environment variable must be set for building the bootloader.\n\n\ - If you use `bootimage` for building you need at least version 0.7.0. You can \ - update `bootimage` by running `cargo install bootimage --force`." + // pass link arguments to rustc + println!("cargo:rustc-link-search=native={}", out_dir.display()); + println!( + "cargo:rustc-link-lib=static=kernel_bin-{}", + kernel_file_name ); - process::exit(1); } - }); - let kernel_file_name = kernel - .file_name() - .expect("KERNEL has no valid file name") - .to_str() - .expect("kernel file name not valid utf8"); - // check that the kernel file exists - assert!( - kernel.exists(), - format!("KERNEL does not exist: {}", kernel.display()) - ); + // Parse configuration from the kernel's Cargo.toml + let config = 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."); + } + Err(env::VarError::NotUnicode(_)) => { + panic!("The KERNEL_MANIFEST environment variable contains invalid unicode") + } + Ok(path) => { + println!("cargo:rerun-if-changed={}", path); - // get access to llvm tools shipped in the llvm-tools-preview rustup component - let llvm_tools = match llvm_tools::LlvmTools::new() { - Ok(tools) => tools, - Err(llvm_tools::Error::NotFound) => { - eprintln!("Error: llvm-tools not found"); - eprintln!("Maybe the rustup component `llvm-tools-preview` is missing?"); - eprintln!(" Install it through: `rustup component add llvm-tools-preview`"); - process::exit(1); - } - Err(err) => { - eprintln!("Failed to retrieve llvm-tools component: {:?}", err); - process::exit(1); - } - }; + let contents = fs::read_to_string(&path).expect(&format!( + "failed to read kernel manifest file (path: {})", + path + )); - // check that kernel executable has code in it - let llvm_size = llvm_tools - .tool(&llvm_tools::exe("llvm-size")) - .expect("llvm-size not found in llvm-tools"); - let mut cmd = Command::new(llvm_size); - cmd.arg(&kernel); - let output = cmd.output().expect("failed to run llvm-size"); - let output_str = String::from_utf8_lossy(&output.stdout); - let second_line_opt = output_str.lines().skip(1).next(); - let second_line = second_line_opt.expect("unexpected llvm-size line output"); - let text_size_opt = second_line.split_ascii_whitespace().next(); - let text_size = text_size_opt.expect("unexpected llvm-size output"); - if text_size == "0" { - panic!("Kernel executable has an empty text section. Perhaps the entry point was set incorrectly?\n\n\ - Kernel executable at `{}`\n", kernel.display()); - } + let manifest = contents + .parse::() + .expect("failed to parse kernel's Cargo.toml"); - // strip debug symbols from kernel for faster loading - let stripped_kernel_file_name = format!("kernel_stripped-{}", kernel_file_name); - let stripped_kernel = out_dir.join(&stripped_kernel_file_name); - let objcopy = llvm_tools - .tool(&llvm_tools::exe("llvm-objcopy")) - .expect("llvm-objcopy not found in llvm-tools"); - let mut cmd = Command::new(&objcopy); - cmd.arg("--strip-debug"); - cmd.arg(&kernel); - cmd.arg(&stripped_kernel); - let exit_status = cmd - .status() - .expect("failed to run objcopy to strip debug symbols"); - if !exit_status.success() { - eprintln!("Error: Stripping debug symbols failed"); - process::exit(1); - } + let table = manifest + .get("package") + .and_then(|table| table.get("metadata")) + .and_then(|table| table.get("bootloader")) + .and_then(|table| table.as_table()); - // write file for including kernel in binary - let file_path = out_dir.join("kernel_info.rs"); - let mut file = File::create(file_path).expect("failed to create kernel_info.rs"); - let kernel_size = fs::metadata(&stripped_kernel) - .expect("Failed to read file metadata of stripped kernel") - .len(); - file.write_all( - format!( - "const KERNEL_SIZE: usize = {}; const KERNEL_BYTES: [u8; KERNEL_SIZE] = *include_bytes!(\"{}\");", - kernel_size, - stripped_kernel.display(), + table.map(|t| Config::parse(t)).unwrap_or_default() + } + }; + + // 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( + format!( + "mod parsed_config {{ + use crate::config::Config; + pub const CONFIG: Config = {:?}; + }}", + config, + ) + .as_bytes(), ) - .as_bytes(), - ) - .expect("write to kernel_info.rs failed"); + .expect("write to bootloader_config.rs failed"); - // Parse the kernel's Cargo.toml which is given to us by bootimage - let mut bootloader_config = BootloaderConfig::default(); + println!("cargo:rerun-if-env-changed=KERNEL"); + println!("cargo:rerun-if-env-changed=KERNEL_MANIFEST"); + println!("cargo:rerun-if-changed={}", kernel.display()); + println!("cargo:rerun-if-changed=build.rs"); + } - match env::var("KERNEL_MANIFEST") { - Err(env::VarError::NotPresent) => { - panic!("The KERNEL_MANIFEST environment variable must be set for building the bootloader.\n\n\ - If you use `bootimage` for building you need at least version 0.7.7. You can \ - update `bootimage` by running `cargo install bootimage --force`."); - } - Err(env::VarError::NotUnicode(_)) => { - panic!("The KERNEL_MANIFEST environment variable contains invalid unicode") - } - Ok(path) => { - println!("cargo:rerun-if-changed={}", path); + include!("src/config.rs"); + + impl Config { + fn parse(table: &toml::value::Table) -> Self { + use toml::Value; + + let mut config = Self::default(); + + for (key, value) in table { + match (key.as_str(), value.clone()) { + ("map-physical-memory", Value::Boolean(b)) => { + config.map_physical_memory = b; + } + ("map-page-table-recursively", Value::Boolean(b)) => { + config.map_page_table_recursively = b; + } + ("kernel-stack-size", Value::Integer(i)) => { + if i <= 0 { + panic!("`kernel-stack-size` in kernel manifest must be positive"); + } else { + config.kernel_stack_size = Some(i as u64); + } + } + + ("physical-memory-offset", Value::Integer(i)) + | ("kernel-stack-address", Value::Integer(i)) + | ("boot-info-address", Value::Integer(i)) + | ("framebuffer-address", Value::Integer(i)) => { + panic!( + "`{0}` in the kernel manifest must be given as a string, \ + as toml does not support unsigned 64-bit integers (try `{0} = \"{1}\"`)", + key.as_str(), + i + ); + } + ("physical-memory-offset", Value::String(s)) => { + config.physical_memory_offset = + Some(Self::parse_aligned_addr(key.as_str(), &s)); + } + ("kernel-stack-address", Value::String(s)) => { + config.kernel_stack_address = + Some(Self::parse_aligned_addr(key.as_str(), &s)); + } + ("boot-info-address", Value::String(s)) => { + config.boot_info_address = Some(Self::parse_aligned_addr(key.as_str(), &s)); + } + ("framebuffer-address", Value::String(s)) => { + config.framebuffer_address = + Some(Self::parse_aligned_addr(key.as_str(), &s)); + } + + (s, _) => { + let help = if s.contains("_") { + "\nkeys use `-` instead of `_`" + } else { + "" + }; + panic!("unknown bootloader configuration key '{}'{}", s, help); + } + } + } - let contents = fs::read_to_string(&path).expect(&format!( - "failed to read kernel manifest file (path: {})", - path - )); + config + } - let manifest = contents - .parse::() - .expect("failed to parse kernel's Cargo.toml"); + fn parse_aligned_addr(key: &str, value: &str) -> u64 { + let num = if value.starts_with("0x") { + u64::from_str_radix(&value[2..], 16) + } else { + u64::from_str_radix(&value, 10) + }; - let table = manifest - .get("package") - .and_then(|table| table.get("metadata")) - .and_then(|table| table.get("bootloader")) - .and_then(|table| table.as_table()); + let num = num.expect(&format!( + "`{}` in the kernel manifest must be an integer (is `{}`)", + key, value + )); - if let Some(table) = table { - parse_to_config(&mut bootloader_config, table); + if num % 0x1000 != 0 { + panic!( + "`{}` in the kernel manifest must be aligned to 4KiB (is `{}`)", + key, value + ); + } else { + num } } } - - // Configure constants for the bootloader - // We leave some variables as Option rather than hardcoding their defaults so that they - // can be calculated dynamically by the bootloader. - 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( - format!( - "const PHYSICAL_MEMORY_OFFSET: Option = {:?}; - const KERNEL_STACK_ADDRESS: Option = {:?}; - const KERNEL_STACK_SIZE: u64 = {}; - const BOOT_INFO_ADDRESS: Option = {:?};", - bootloader_config.physical_memory_offset, - bootloader_config.kernel_stack_address, - bootloader_config.kernel_stack_size.unwrap_or(512), // size is in number of pages - bootloader_config.boot_info_address, - ) - .as_bytes(), - ) - .expect("write to bootloader_config.rs failed"); - - println!("cargo:rerun-if-env-changed=KERNEL"); - println!("cargo:rerun-if-env-changed=KERNEL_MANIFEST"); - println!("cargo:rerun-if-changed={}", kernel.display()); - println!("cargo:rerun-if-changed=build.rs"); } diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 00000000..e361a68b --- /dev/null +++ b/src/config.rs @@ -0,0 +1,24 @@ +#[derive(Debug)] +pub struct Config { + pub map_physical_memory: bool, + pub map_page_table_recursively: bool, + pub kernel_stack_size: Option, + pub physical_memory_offset: Option, + pub kernel_stack_address: Option, + pub boot_info_address: Option, + pub framebuffer_address: Option, +} + +impl Default for Config { + fn default() -> Self { + Config { + map_physical_memory: false, + map_page_table_recursively: false, + physical_memory_offset: None, + kernel_stack_address: None, + kernel_stack_size: None, + boot_info_address: None, + framebuffer_address: None, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index eeb7ae52..5b50fdab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,7 @@ use x86_64::{ }; pub mod bootinfo; +pub mod config; pub mod boot_info; pub mod memory_map; From 87e85b065c0de924f2cb397d41f903108b69efc9 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 24 Aug 2020 18:39:47 +0200 Subject: [PATCH 071/174] Respect configured addresses and fall back to dynamic search for free entry --- Cargo.lock | 7 --- Cargo.toml | 3 +- src/bin/bios.rs | 20 +----- .../level_4_entries.rs} | 17 +++-- src/binary/load_kernel.rs | 12 ++-- src/binary/mod.rs | 62 ++++++++++++++----- src/lib.rs | 3 - 7 files changed, 68 insertions(+), 56 deletions(-) rename src/{level4_entries.rs => binary/level_4_entries.rs} (74%) diff --git a/Cargo.lock b/Cargo.lock index 8c8d9863..f7255a86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,7 +62,6 @@ dependencies = [ "bit_field 0.10.0", "conquer-once", "displaydoc", - "fixedvec", "font8x8", "json", "llvm-tools", @@ -109,12 +108,6 @@ dependencies = [ "syn", ] -[[package]] -name = "fixedvec" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b395ef2adf62bdeefcd1b59ad0dd2225c6c333ec79656ea79ac5285c46d051ea" - [[package]] name = "font8x8" version = "0.2.5" diff --git a/Cargo.toml b/Cargo.toml index 50c9ddb1..70ce9fd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,6 @@ required-features = ["uefi_bin"] xmas-elf = { version = "0.6.2", optional = true } x86_64 = { version = "0.11.0", optional = true } usize_conversions = { version = "0.2.0", optional = true } -fixedvec = { version = "0.2.4", optional = true } bit_field = { version = "0.10.0", optional = true } rlibc = { version = "1.0.0", optional = true } log = { version = "0.4.8", optional = true } @@ -56,7 +55,7 @@ toml = { version = "0.5.1", optional = true } default = [] builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json"] runner = ["anyhow"] -bios_bin = ["binary", "fixedvec", "rlibc", "vga_320x200"] +bios_bin = ["binary", "rlibc", "vga_320x200"] uefi_bin = ["binary", "rlibc", "uefi", "font8x8"] binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", "log", "conquer-once", "spinning_top"] vga_320x200 = ["font8x8"] diff --git a/src/bin/bios.rs b/src/bin/bios.rs index e88a6b2f..d7e06e1d 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -20,15 +20,6 @@ use x86_64::structures::paging::{ }; use x86_64::{PhysAddr, VirtAddr}; -// The bootloader_config.rs file contains some configuration constants set by the build script: -// PHYSICAL_MEMORY_OFFSET: The offset into the virtual address space where the physical memory -// is mapped if the `map_physical_memory` feature is activated. -// -// KERNEL_STACK_ADDRESS: The virtual address of the kernel stack. -// -// KERNEL_STACK_SIZE: The number of pages in the kernel stack. -include!(concat!(env!("OUT_DIR"), "/bootloader_config.rs")); - global_asm!(include_str!("../asm/stage_1.s")); global_asm!(include_str!("../asm/stage_2.s")); global_asm!(include_str!("../asm/e820.s")); @@ -90,7 +81,7 @@ fn bootloader_main( .expect("no physical memory regions found"); let mut frame_allocator = { - let kernel_end = PhysFrame::containing_address(kernel_start.phys() + kernel_size - 1u64); + let kernel_end = PhysFrame::containing_address(kernel_start + kernel_size - 1u64); let next_free = kernel_end + 1; LegacyFrameAllocator::new_starting_at(next_free, e820_memory_map.iter().copied()) }; @@ -138,18 +129,9 @@ fn bootloader_main( ); /* - // Mark used virtual addresses - let mut level4_entries = level4_entries::UsedLevel4Entries::new(&segments); - // Enable support for the no-execute bit in page tables. enable_nxe_bit(); - // If no kernel stack address is provided, map the kernel stack after the boot info page - let kernel_stack_address = match KERNEL_STACK_ADDRESS { - Some(addr) => Page::containing_address(VirtAddr::new(addr)), - None => boot_info_page + 1, - }; - let physical_memory_offset = if cfg!(feature = "map_physical_memory") { let physical_memory_offset = PHYSICAL_MEMORY_OFFSET.unwrap_or_else(|| { diff --git a/src/level4_entries.rs b/src/binary/level_4_entries.rs similarity index 74% rename from src/level4_entries.rs rename to src/binary/level_4_entries.rs index 60ace846..bca14042 100644 --- a/src/level4_entries.rs +++ b/src/binary/level_4_entries.rs @@ -1,17 +1,16 @@ use core::convert::TryInto; -use fixedvec::FixedVec; use x86_64::{ structures::paging::{Page, PageTableIndex}, VirtAddr, }; -use xmas_elf::program::ProgramHeader64; +use xmas_elf::program::ProgramHeader; pub struct UsedLevel4Entries { entry_state: [bool; 512], // whether an entry is in use by the kernel } impl UsedLevel4Entries { - pub fn new(segments: &FixedVec) -> Self { + pub fn new<'a>(segments: impl Iterator>) -> Self { let mut used = UsedLevel4Entries { entry_state: [false; 512], }; @@ -19,9 +18,9 @@ impl UsedLevel4Entries { used.entry_state[0] = true; // TODO: Can we do this dynamically? for segment in segments { - let start_page: Page = Page::containing_address(VirtAddr::new(segment.virtual_addr)); + let start_page: Page = Page::containing_address(VirtAddr::new(segment.virtual_addr())); let end_page: Page = - Page::containing_address(VirtAddr::new(segment.virtual_addr + segment.mem_size)); + Page::containing_address(VirtAddr::new(segment.virtual_addr() + segment.mem_size())); for p4_index in u64::from(start_page.p4_index())..=u64::from(end_page.p4_index()) { used.entry_state[p4_index as usize] = true; @@ -42,4 +41,12 @@ impl UsedLevel4Entries { *entry = true; PageTableIndex::new(idx.try_into().unwrap()) } + + pub fn get_free_address(&mut self) -> VirtAddr { + Page::from_page_table_indices_1gib( + self.get_free_entry(), + PageTableIndex::new(0), + ) + .start_address() + } } diff --git a/src/binary/load_kernel.rs b/src/binary/load_kernel.rs index be9ae4b9..36245bd2 100644 --- a/src/binary/load_kernel.rs +++ b/src/binary/load_kernel.rs @@ -11,8 +11,7 @@ use xmas_elf::{ program::{self, ProgramHeader, Type}, ElfFile, }; - -const PAGE_SIZE: u64 = 4096; +use crate::binary::{PAGE_SIZE, level_4_entries::UsedLevel4Entries}; struct Loader<'a, M, F> { elf_file: ElfFile<'a>, @@ -78,6 +77,10 @@ where fn entry_point(&self) -> VirtAddr { VirtAddr::new(self.elf_file.header.pt2.entry_point()) } + + fn used_level_4_entries(&self) -> UsedLevel4Entries { + UsedLevel4Entries::new(self.elf_file.program_iter()) + } } impl<'a, M, F> Inner<'a, M, F> @@ -256,9 +259,10 @@ pub fn load_kernel( bytes: &[u8], page_table: &mut impl MapperAllSizes, frame_allocator: &mut impl FrameAllocator, -) -> Result { +) -> Result<(VirtAddr, UsedLevel4Entries), &'static str> { let mut loader = Loader::new(bytes, page_table, frame_allocator)?; loader.load_segments()?; + let used_entries = loader.used_level_4_entries(); - Ok(loader.entry_point()) + Ok((loader.entry_point(), used_entries)) } diff --git a/src/binary/mod.rs b/src/binary/mod.rs index a75a2191..2403dfe5 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -13,6 +13,8 @@ use x86_64::{ }, PhysAddr, VirtAddr, }; +use parsed_config::CONFIG; +use level_4_entries::UsedLevel4Entries; #[cfg(feature = "bios_bin")] pub mod bios; @@ -22,6 +24,22 @@ pub mod uefi; pub mod legacy_memory_region; pub mod load_kernel; pub mod logger; +pub mod level_4_entries; + +// Contains the parsed configuration table from the kernel's Cargo.toml. +// +// The layout of the file is the following: +// +// ``` +// mod parsed_config { +// pub const CONFIG: Config = Config { … }; +// } +// ``` +// +// The module file is created by the build script. +include!(concat!(env!("OUT_DIR"), "/bootloader_config.rs")); + +const PAGE_SIZE: u64 = 4096; pub fn init_logger(framebuffer: &'static mut [u8], info: logger::FrameBufferInfo) { let logger = logger::LOGGER.get_or_init(move || logger::LockedLogger::new(framebuffer, info)); @@ -40,7 +58,7 @@ where I: ExactSizeIterator + Clone, D: LegacyMemoryRegion, { - let mappings = set_up_mappings( + let mut mappings = set_up_mappings( kernel_bytes, &mut frame_allocator, &mut page_tables.kernel, @@ -50,7 +68,7 @@ where let (boot_info, two_frames) = create_boot_info( frame_allocator, &mut page_tables, - mappings.framebuffer, + &mut mappings, framebuffer_size, ); switch_to_kernel(page_tables, mappings, boot_info, two_frames); @@ -64,14 +82,18 @@ pub fn set_up_mappings( framebuffer_addr: PhysAddr, framebuffer_size: usize, ) -> Mappings { - let entry_point = load_kernel::load_kernel(kernel_bytes, kernel_page_table, frame_allocator) + let (entry_point, mut used_entries) = load_kernel::load_kernel(kernel_bytes, kernel_page_table, frame_allocator) .expect("no entry point"); log::info!("Entry point at: {:#x}", entry_point.as_u64()); // create a stack - let stack_start: Page = kernel_stack_start_location(); - let stack_end = stack_start + 20; - for page in Page::range(stack_start, stack_end) { + let stack_start_addr = kernel_stack_start_location(&mut used_entries); + let stack_start: Page = Page::containing_address(stack_start_addr); + let stack_end = { + let end_addr = stack_start_addr + CONFIG.kernel_stack_size.unwrap_or(20 * PAGE_SIZE); + Page::containing_address(end_addr - 1u64) + }; + for page in Page::range_inclusive(stack_start, stack_end) { let frame = frame_allocator .allocate_frame() .expect("frame allocation failed when mapping a kernel stack"); @@ -87,7 +109,7 @@ pub fn set_up_mappings( let framebuffer_start_frame: PhysFrame = PhysFrame::containing_address(framebuffer_addr); let framebuffer_end_frame = PhysFrame::containing_address(framebuffer_addr + framebuffer_size - 1u64); - let start_page = frame_buffer_location(); + let start_page = Page::containing_address(frame_buffer_location(&mut used_entries)); for (i, frame) in PhysFrame::range_inclusive(framebuffer_start_frame, framebuffer_end_frame).enumerate() { @@ -103,6 +125,7 @@ pub fn set_up_mappings( framebuffer: framebuffer_virt_addr, entry_point, stack_end, + used_entries, } } @@ -110,13 +133,14 @@ pub struct Mappings { pub entry_point: VirtAddr, pub framebuffer: VirtAddr, pub stack_end: Page, + pub used_entries: UsedLevel4Entries, } /// Allocates and initializes the boot info struct and the memory map pub fn create_boot_info( mut frame_allocator: LegacyFrameAllocator, page_tables: &mut PageTables, - framebuffer_virt_addr: VirtAddr, + mappings: &mut Mappings, framebuffer_size: usize, ) -> (&'static mut BootInfo, TwoFrames) where @@ -127,7 +151,7 @@ where // allocate and map space for the boot info let (boot_info, memory_regions) = { - let boot_info_addr = boot_info_location(); + let boot_info_addr = boot_info_location(&mut mappings.used_entries); let boot_info_end = boot_info_addr + mem::size_of::(); let memory_map_regions_addr = boot_info_end.align_up(u64::from_usize(mem::align_of::())); @@ -184,7 +208,7 @@ where let boot_info = boot_info.write(BootInfo { memory_regions, framebuffer: FrameBufferInfo { - start_addr: framebuffer_virt_addr.as_u64(), + start_addr: mappings.framebuffer.as_u64(), len: framebuffer_size, }, }); @@ -291,14 +315,20 @@ unsafe impl FrameAllocator for TwoFrames { } } -fn boot_info_location() -> VirtAddr { - VirtAddr::new(0x_0000_00bb_bbbb_0000) +fn boot_info_location(used_entries: &mut UsedLevel4Entries) -> VirtAddr { + CONFIG.boot_info_address.map(VirtAddr::new).unwrap_or_else(|| { + used_entries.get_free_address() + }) } -fn frame_buffer_location() -> Page { - Page::containing_address(VirtAddr::new(0x_0000_00cc_cccc_0000)) +fn frame_buffer_location(used_entries: &mut UsedLevel4Entries) -> VirtAddr { + CONFIG.framebuffer_address.map(VirtAddr::new).unwrap_or_else(|| { + used_entries.get_free_address() + }) } -fn kernel_stack_start_location() -> Page { - Page::containing_address(VirtAddr::new(0x_0000_0fff_0000_0000)) +fn kernel_stack_start_location(used_entries: &mut UsedLevel4Entries) -> VirtAddr { + CONFIG.kernel_stack_address.map(VirtAddr::new).unwrap_or_else(|| { + used_entries.get_free_address() + }) } diff --git a/src/lib.rs b/src/lib.rs index 5b50fdab..2e695a84 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,9 +35,6 @@ pub mod binary; #[cfg(feature = "builder")] pub mod disk_image; -#[cfg(feature = "bios_bin")] -pub mod level4_entries; - #[cfg(target_arch = "x86")] compile_error!( "This crate currently does not support 32-bit protected mode. \ From 61f4a7fe259372799ca435a35bd89ad4e6fe3195 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 24 Aug 2020 21:07:21 +0200 Subject: [PATCH 072/174] Run cargo fmt --- src/binary/level_4_entries.rs | 12 +++++------- src/binary/load_kernel.rs | 2 +- src/binary/mod.rs | 27 +++++++++++++++------------ 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/binary/level_4_entries.rs b/src/binary/level_4_entries.rs index bca14042..2172d494 100644 --- a/src/binary/level_4_entries.rs +++ b/src/binary/level_4_entries.rs @@ -19,8 +19,9 @@ impl UsedLevel4Entries { for segment in segments { let start_page: Page = Page::containing_address(VirtAddr::new(segment.virtual_addr())); - let end_page: Page = - Page::containing_address(VirtAddr::new(segment.virtual_addr() + segment.mem_size())); + let end_page: Page = Page::containing_address(VirtAddr::new( + segment.virtual_addr() + segment.mem_size(), + )); for p4_index in u64::from(start_page.p4_index())..=u64::from(end_page.p4_index()) { used.entry_state[p4_index as usize] = true; @@ -43,10 +44,7 @@ impl UsedLevel4Entries { } pub fn get_free_address(&mut self) -> VirtAddr { - Page::from_page_table_indices_1gib( - self.get_free_entry(), - PageTableIndex::new(0), - ) - .start_address() + Page::from_page_table_indices_1gib(self.get_free_entry(), PageTableIndex::new(0)) + .start_address() } } diff --git a/src/binary/load_kernel.rs b/src/binary/load_kernel.rs index 36245bd2..8d055c61 100644 --- a/src/binary/load_kernel.rs +++ b/src/binary/load_kernel.rs @@ -1,3 +1,4 @@ +use crate::binary::{level_4_entries::UsedLevel4Entries, PAGE_SIZE}; use x86_64::{ align_up, structures::paging::{ @@ -11,7 +12,6 @@ use xmas_elf::{ program::{self, ProgramHeader, Type}, ElfFile, }; -use crate::binary::{PAGE_SIZE, level_4_entries::UsedLevel4Entries}; struct Loader<'a, M, F> { elf_file: ElfFile<'a>, diff --git a/src/binary/mod.rs b/src/binary/mod.rs index 2403dfe5..667f2ac8 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -5,6 +5,8 @@ use core::{ mem::{self, MaybeUninit}, slice, }; +use level_4_entries::UsedLevel4Entries; +use parsed_config::CONFIG; use usize_conversions::FromUsize; use x86_64::{ registers, @@ -13,8 +15,6 @@ use x86_64::{ }, PhysAddr, VirtAddr, }; -use parsed_config::CONFIG; -use level_4_entries::UsedLevel4Entries; #[cfg(feature = "bios_bin")] pub mod bios; @@ -22,9 +22,9 @@ pub mod bios; pub mod uefi; pub mod legacy_memory_region; +pub mod level_4_entries; pub mod load_kernel; pub mod logger; -pub mod level_4_entries; // Contains the parsed configuration table from the kernel's Cargo.toml. // @@ -316,19 +316,22 @@ unsafe impl FrameAllocator for TwoFrames { } fn boot_info_location(used_entries: &mut UsedLevel4Entries) -> VirtAddr { - CONFIG.boot_info_address.map(VirtAddr::new).unwrap_or_else(|| { - used_entries.get_free_address() - }) + CONFIG + .boot_info_address + .map(VirtAddr::new) + .unwrap_or_else(|| used_entries.get_free_address()) } fn frame_buffer_location(used_entries: &mut UsedLevel4Entries) -> VirtAddr { - CONFIG.framebuffer_address.map(VirtAddr::new).unwrap_or_else(|| { - used_entries.get_free_address() - }) + CONFIG + .framebuffer_address + .map(VirtAddr::new) + .unwrap_or_else(|| used_entries.get_free_address()) } fn kernel_stack_start_location(used_entries: &mut UsedLevel4Entries) -> VirtAddr { - CONFIG.kernel_stack_address.map(VirtAddr::new).unwrap_or_else(|| { - used_entries.get_free_address() - }) + CONFIG + .kernel_stack_address + .map(VirtAddr::new) + .unwrap_or_else(|| used_entries.get_free_address()) } From a1d21368656ca847f22d7a9ba065263f4883712a Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 24 Aug 2020 21:07:40 +0200 Subject: [PATCH 073/174] Implement support for mapping physical memory --- src/binary/legacy_memory_region.rs | 8 +++++++ src/binary/mod.rs | 36 +++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/binary/legacy_memory_region.rs b/src/binary/legacy_memory_region.rs index 16341b96..82605c03 100644 --- a/src/binary/legacy_memory_region.rs +++ b/src/binary/legacy_memory_region.rs @@ -64,6 +64,14 @@ where self.original.len() } + pub fn max_phys_addr(&self) -> PhysAddr { + self.original + .clone() + .map(|r| r.start() + r.len()) + .max() + .unwrap() + } + pub fn construct_memory_map( self, regions: &mut [MaybeUninit], diff --git a/src/binary/mod.rs b/src/binary/mod.rs index 667f2ac8..38ee7b2e 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -11,7 +11,8 @@ use usize_conversions::FromUsize; use x86_64::{ registers, structures::paging::{ - FrameAllocator, Mapper, OffsetPageTable, Page, PageTableFlags, PhysFrame, Size4KiB, + FrameAllocator, Mapper, OffsetPageTable, Page, PageTableFlags, PhysFrame, Size2MiB, + Size4KiB, }, PhysAddr, VirtAddr, }; @@ -75,15 +76,20 @@ where } /// Sets up mappings for a kernel stack and the framebuffer -pub fn set_up_mappings( +pub fn set_up_mappings( kernel_bytes: &[u8], - frame_allocator: &mut impl FrameAllocator, + frame_allocator: &mut LegacyFrameAllocator, kernel_page_table: &mut OffsetPageTable, framebuffer_addr: PhysAddr, framebuffer_size: usize, -) -> Mappings { - let (entry_point, mut used_entries) = load_kernel::load_kernel(kernel_bytes, kernel_page_table, frame_allocator) - .expect("no entry point"); +) -> Mappings +where + I: ExactSizeIterator + Clone, + D: LegacyMemoryRegion, +{ + let (entry_point, mut used_entries) = + load_kernel::load_kernel(kernel_bytes, kernel_page_table, frame_allocator) + .expect("no entry point"); log::info!("Entry point at: {:#x}", entry_point.as_u64()); // create a stack @@ -121,6 +127,24 @@ pub fn set_up_mappings( } let framebuffer_virt_addr = start_page.start_address(); + if CONFIG.map_physical_memory { + let offset = CONFIG + .physical_memory_offset + .map(VirtAddr::new) + .unwrap_or_else(|| used_entries.get_free_address()); + + let start_frame = PhysFrame::containing_address(PhysAddr::new(0)); + let max_phys = frame_allocator.max_phys_addr(); + let end_frame: PhysFrame = PhysFrame::containing_address(max_phys - 1u64); + for frame in PhysFrame::range_inclusive(start_frame, end_frame) { + let page = Page::containing_address(offset + frame.start_address().as_u64()); + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + unsafe { kernel_page_table.map_to(page, frame, flags, frame_allocator) } + .unwrap() + .ignore(); + } + } + Mappings { framebuffer: framebuffer_virt_addr, entry_point, From 6bee40612ec6df0d3e4eea03e8d688c3a921178b Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 30 Aug 2020 14:22:16 +0200 Subject: [PATCH 074/174] Set up a 1024x768 framebuffer for BIOS executable --- src/asm/video_mode/vga_320x200.s | 8 ++++++-- src/bin/bios.rs | 17 +++++++++-------- src/binary/logger.rs | 10 ++++++---- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/asm/video_mode/vga_320x200.s b/src/asm/video_mode/vga_320x200.s index 17d4a743..b1d45069 100644 --- a/src/asm/video_mode/vga_320x200.s +++ b/src/asm/video_mode/vga_320x200.s @@ -3,9 +3,13 @@ .code16 config_video_mode: - mov ah, 0 - mov al, 0x13 # 320x200 256 color graphics + push bx + push ax + mov ax, 0x4F02 + mov bx, 0x0118 int 0x10 + pop bx + pop ax ret .code32 diff --git a/src/bin/bios.rs b/src/bin/bios.rs index d7e06e1d..8c3e3e45 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -66,10 +66,6 @@ fn bootloader_main( ) -> ! { use bootloader::binary::{bios::E820MemoryRegion, legacy_memory_region::LegacyFrameAllocator}; - let framebuffer_addr = PhysAddr::new(0xa0000); - let framebuffer_size = 320 * 200; - init_logger(framebuffer_addr, framebuffer_size); - let e820_memory_map = { let ptr = usize_from(memory_map_addr.as_u64()) as *const E820MemoryRegion; unsafe { slice::from_raw_parts(ptr, usize_from(memory_map_entry_count)) } @@ -113,6 +109,10 @@ fn bootloader_main( } } + let framebuffer_addr = PhysAddr::new(0xfd000000); + let framebuffer_size = 1024 * 768; + init_logger(framebuffer_addr, framebuffer_size); + let page_tables = create_page_tables(&mut frame_allocator); let kernel = { @@ -200,10 +200,11 @@ fn init_logger(framebuffer_start: PhysAddr, framebuffer_size: u64) { let slice = unsafe { slice::from_raw_parts_mut(ptr, usize_from(framebuffer_size)) }; slice.fill(0x4); let info = bootloader::binary::logger::FrameBufferInfo { - horizontal_resolution: 320, - vertical_resolution: 200, - pixel_format: bootloader::binary::logger::PixelFormat::U8, - stride: 320, + horizontal_resolution: 1024, + vertical_resolution: 768, + pixel_format: bootloader::binary::logger::PixelFormat::RGB, + bytes_per_pixel: 3, + stride: 1024, }; bootloader::binary::init_logger(slice, info); diff --git a/src/binary/logger.rs b/src/binary/logger.rs index 7476efaf..be918572 100644 --- a/src/binary/logger.rs +++ b/src/binary/logger.rs @@ -117,11 +117,12 @@ impl Logger { fn write_pixel(&mut self, x: usize, y: usize, intensity: u8) { let pixel_offset = y * self.info.stride + x; - let (bytes_per_pixel, color) = match self.info.pixel_format { - PixelFormat::RGB => (4, [intensity, intensity, intensity / 2, 0]), - PixelFormat::BGR => (4, [intensity / 2, intensity, intensity, 0]), - PixelFormat::U8 => (1, [if intensity > 200 { 0xf } else { 0 }, 0, 0, 0]), + let color = match self.info.pixel_format { + PixelFormat::RGB => [intensity, intensity, intensity / 2, 0], + PixelFormat::BGR => [intensity / 2, intensity, intensity, 0], + PixelFormat::U8 => [if intensity > 200 { 0xf } else { 0 }, 0, 0, 0], }; + let bytes_per_pixel = self.info.bytes_per_pixel; let byte_offset = pixel_offset * bytes_per_pixel; self.framebuffer[byte_offset..(byte_offset + bytes_per_pixel)] .copy_from_slice(&color[..bytes_per_pixel]); @@ -146,6 +147,7 @@ pub struct FrameBufferInfo { pub horizontal_resolution: usize, pub vertical_resolution: usize, pub pixel_format: PixelFormat, + pub bytes_per_pixel: usize, pub stride: usize, } From 1ae1a21c88ff6804398e1e8c3f6b3dd962f31838 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 30 Aug 2020 19:49:02 +0200 Subject: [PATCH 075/174] Export new BootInfo struct --- Cargo.toml | 6 ++++++ src/lib.rs | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 70ce9fd9..364f33cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,12 @@ repository = "https://github.com/rust-osdev/bootloader" edition = "2018" build = "build.rs" +[workspace] +members = [ + "tests/test_kernel", + "tests/runner", +] + [[bin]] name = "builder" required-features = ["builder"] diff --git a/src/lib.rs b/src/lib.rs index 2e695a84..20a75611 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ #![deny(unsafe_op_in_unsafe_fn)] //#![warn(missing_docs)] -pub use crate::bootinfo::BootInfo; +pub use crate::boot_info::BootInfo; #[cfg(feature = "bios_bin")] use x86_64::{ @@ -55,9 +55,9 @@ compile_error!("This crate only supports the x86_64 architecture."); macro_rules! entry_point { ($path:path) => { #[export_name = "_start"] - pub extern "C" fn __impl_start(boot_info: &'static $crate::bootinfo::BootInfo) -> ! { + pub extern "C" fn __impl_start(boot_info: &'static $crate::boot_info::BootInfo) -> ! { // validate the signature of the program entry point - let f: fn(&'static $crate::bootinfo::BootInfo) -> ! = $path; + let f: fn(&'static $crate::boot_info::BootInfo) -> ! = $path; f(boot_info) } From 053adfd99873c62d61b1363ebacbbc5ac2a2c047 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 30 Aug 2020 20:15:33 +0200 Subject: [PATCH 076/174] Remove old example kernel --- example-kernel/.cargo/config | 2 -- example-kernel/.gitignore | 2 -- example-kernel/Cargo.lock | 32 ----------------------- example-kernel/Cargo.toml | 8 ------ example-kernel/src/main.rs | 31 ---------------------- example-kernel/x86_64-example-kernel.json | 15 ----------- 6 files changed, 90 deletions(-) delete mode 100644 example-kernel/.cargo/config delete mode 100644 example-kernel/.gitignore delete mode 100644 example-kernel/Cargo.lock delete mode 100644 example-kernel/Cargo.toml delete mode 100644 example-kernel/src/main.rs delete mode 100644 example-kernel/x86_64-example-kernel.json diff --git a/example-kernel/.cargo/config b/example-kernel/.cargo/config deleted file mode 100644 index ab792469..00000000 --- a/example-kernel/.cargo/config +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "x86_64-example-kernel.json" diff --git a/example-kernel/.gitignore b/example-kernel/.gitignore deleted file mode 100644 index eccd7b4a..00000000 --- a/example-kernel/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target/ -**/*.rs.bk diff --git a/example-kernel/Cargo.lock b/example-kernel/Cargo.lock deleted file mode 100644 index c659370f..00000000 --- a/example-kernel/Cargo.lock +++ /dev/null @@ -1,32 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "bit_field" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "example-kernel" -version = "0.1.0" -dependencies = [ - "x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "x86_64" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" -"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" -"checksum x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "365de37eb7c6da582cbb510dd0f3f1235d24ff6309a8a96e8a9909cc9bfd608f" diff --git a/example-kernel/Cargo.toml b/example-kernel/Cargo.toml deleted file mode 100644 index c3e50263..00000000 --- a/example-kernel/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "example-kernel" -version = "0.1.0" -authors = ["Philipp Oppermann "] -edition = "2018" - -[dependencies] -x86_64 = "0.11.0" diff --git a/example-kernel/src/main.rs b/example-kernel/src/main.rs deleted file mode 100644 index a953f2e9..00000000 --- a/example-kernel/src/main.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![no_std] // don't link the Rust standard library -#![no_main] // disable all Rust-level entry points - -use core::panic::PanicInfo; - -static HELLO: &[u8] = b"Hello World!"; - -#[no_mangle] // don't mangle the name of this function -pub extern "C" fn _start() -> ! { - // this function is the entry point, since the linker looks for a function - // named `_start` by default - - let vga_buffer = 0xb8000 as *mut u8; - - // print `HELLO` to the screen (see - // https://os.phil-opp.com/minimal-rust-kernel/#printing-to-screen) - for (i, &byte) in HELLO.iter().enumerate() { - unsafe { - *vga_buffer.offset(i as isize * 2) = byte; - *vga_buffer.offset(i as isize * 2 + 1) = 0xb; - } - } - - loop {} -} - -/// This function is called on panic. -#[panic_handler] -fn panic(_info: &PanicInfo) -> ! { - loop {} -} diff --git a/example-kernel/x86_64-example-kernel.json b/example-kernel/x86_64-example-kernel.json deleted file mode 100644 index 9afe809f..00000000 --- a/example-kernel/x86_64-example-kernel.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "llvm-target": "x86_64-unknown-none", - "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", - "arch": "x86_64", - "target-endian": "little", - "target-pointer-width": "64", - "target-c-int-width": "32", - "os": "none", - "executables": true, - "linker-flavor": "ld.lld", - "linker": "rust-lld", - "panic-strategy": "abort", - "disable-redzone": true, - "features": "-mmx,-sse,+soft-float" - } From 03dee1b3eae89de35faa67bd88a6d43c47cdb2fe Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 30 Aug 2020 20:21:01 +0200 Subject: [PATCH 077/174] Create a test framework --- Cargo.lock | 79 ++++++++++++++----- Cargo.toml | 2 +- tests/default_settings.rs | 22 ++++++ tests/runner/Cargo.toml | 11 +++ tests/runner/src/main.rs | 76 ++++++++++++++++++ .../default_settings/.cargo/config.toml | 10 +++ .../test_kernels/default_settings/.gitignore | 1 + .../test_kernels/default_settings/Cargo.toml | 9 +++ .../default_settings/src/bin/basic_boot.rs | 18 +++++ .../default_settings/src/bin/should_panic.rs | 20 +++++ .../test_kernels/default_settings/src/lib.rs | 21 +++++ .../x86_64-example-kernel.json | 15 ++++ 12 files changed, 265 insertions(+), 19 deletions(-) create mode 100644 tests/default_settings.rs create mode 100644 tests/runner/Cargo.toml create mode 100644 tests/runner/src/main.rs create mode 100644 tests/test_kernels/default_settings/.cargo/config.toml create mode 100644 tests/test_kernels/default_settings/.gitignore create mode 100644 tests/test_kernels/default_settings/Cargo.toml create mode 100644 tests/test_kernels/default_settings/src/bin/basic_boot.rs create mode 100644 tests/test_kernels/default_settings/src/bin/should_panic.rs create mode 100644 tests/test_kernels/default_settings/src/lib.rs create mode 100644 tests/test_kernels/default_settings/x86_64-example-kernel.json diff --git a/Cargo.lock b/Cargo.lock index f7255a86..a6724689 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,9 +43,9 @@ checksum = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" [[package]] name = "bit_field" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" +checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" [[package]] name = "bitflags" @@ -59,7 +59,7 @@ version = "0.9.8" dependencies = [ "anyhow", "argh", - "bit_field 0.10.0", + "bit_field 0.10.1", "conquer-once", "displaydoc", "font8x8", @@ -72,10 +72,19 @@ dependencies = [ "toml", "uefi", "usize_conversions", - "x86_64", + "x86_64 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "xmas-elf", ] +[[package]] +name = "bootloader-locator" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaaa9db3339d32c2622f2e5d0731eb82a468d3439797c9d4fe426744fe2bd551" +dependencies = [ + "json", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -129,12 +138,29 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" +[[package]] +name = "kernel" +version = "0.1.0" +dependencies = [ + "bootloader", + "x86_64 0.11.2 (git+https://github.com/rust-osdev/x86_64.git?branch=nop)", +] + [[package]] name = "llvm-tools" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" +[[package]] +name = "locate-cargo-manifest" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf4c572ea735347f84c196204b41411e1c266006297043db6cf7c6235d3edff" +dependencies = [ + "json", +] + [[package]] name = "lock_api" version = "0.4.1" @@ -177,6 +203,14 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" +[[package]] +name = "runner" +version = "0.1.0" +dependencies = [ + "bootloader-locator", + "locate-cargo-manifest", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -185,24 +219,24 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.98" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113" +checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" [[package]] name = "spinning_top" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8ba8ba35ec6c69da715181fb4b5451c3974b997596c0443d0bbc18c8cf6cd" +checksum = "7e529d73e80d64b5f2631f9035113347c578a1c9c7774b83a2b880788459ab36" dependencies = [ "lock_api", ] [[package]] name = "syn" -version = "1.0.35" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0" +checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9" dependencies = [ "proc-macro2", "quote", @@ -231,9 +265,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.1" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" dependencies = [ "serde", ] @@ -244,13 +278,13 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85061f4e43545a613c0da6b87725bf23f8da8613cf2473719c4f71a270c4ce8a" dependencies = [ - "bit_field 0.10.0", + "bit_field 0.10.1", ] [[package]] name = "uefi" version = "0.5.0" -source = "git+https://github.com/rust-osdev/uefi-rs.git#af9b2a492d404ff7bfe5ac0293ae5bc3e1d3a1ab" +source = "git+https://github.com/rust-osdev/uefi-rs.git#e482874f3d92e258eb9d7caa9782ecc679417287" dependencies = [ "bitflags", "log", @@ -260,9 +294,9 @@ dependencies = [ [[package]] name = "uefi-macros" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a69fa8dd920e84d783769c44560484ade81f6c765cde2e1cc46c754ddf95947" +checksum = "3dcca10ca861f34a320d178f3fdb29ffbf05087fc2c70d2a99860e3329bee1a8" dependencies = [ "proc-macro2", "quote", @@ -289,9 +323,18 @@ checksum = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" [[package]] name = "x86_64" -version = "0.11.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "365de37eb7c6da582cbb510dd0f3f1235d24ff6309a8a96e8a9909cc9bfd608f" +checksum = "88ca13fe418403c0df903ee7eb3bf1a51a742bb339b42a329642f386224b8e64" +dependencies = [ + "bit_field 0.9.0", + "bitflags", +] + +[[package]] +name = "x86_64" +version = "0.11.2" +source = "git+https://github.com/rust-osdev/x86_64.git?branch=nop#0b2bf80eeb22f9e8452b604624704e52d895b6d0" dependencies = [ "bit_field 0.9.0", "bitflags", diff --git a/Cargo.toml b/Cargo.toml index 364f33cc..57b95162 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,8 @@ build = "build.rs" [workspace] members = [ - "tests/test_kernel", "tests/runner", + "tests/test_kernels/default_settings", ] [[bin]] diff --git a/tests/default_settings.rs b/tests/default_settings.rs new file mode 100644 index 00000000..e183cf7e --- /dev/null +++ b/tests/default_settings.rs @@ -0,0 +1,22 @@ +use std::process::Command; + +#[test] +fn basic_boot() { + run_test_binary("basic_boot"); +} + + +#[test] +fn should_panic() { + run_test_binary("should_panic"); +} + +fn run_test_binary(bin_name: &str) { + let mut cmd = Command::new(env!("CARGO")); + cmd.current_dir("tests/test_kernels/default_settings"); + cmd.arg("run"); + cmd.arg("--bin").arg(bin_name); + cmd.arg("--target").arg(" x86_64-example-kernel.json"); + cmd.arg("-Zbuild-std=core"); + assert!(cmd.status().unwrap().success()); +} \ No newline at end of file diff --git a/tests/runner/Cargo.toml b/tests/runner/Cargo.toml new file mode 100644 index 00000000..93550faa --- /dev/null +++ b/tests/runner/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "runner" +version = "0.1.0" +authors = ["Philipp Oppermann "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bootloader-locator = "0.0.4" +locate-cargo-manifest = "0.2.1" diff --git a/tests/runner/src/main.rs b/tests/runner/src/main.rs new file mode 100644 index 00000000..f51ee64c --- /dev/null +++ b/tests/runner/src/main.rs @@ -0,0 +1,76 @@ +use std::{ + path::{Path, PathBuf}, + process::Command, +}; + +const QEMU_ARGS: &[&str] = &[ + "-device", + "isa-debug-exit,iobase=0xf4,iosize=0x04", + "-serial", + "stdio", + "-display", + "none", + "--no-reboot", +]; + +fn main() { + let kernel_binary_path = { + let path = PathBuf::from(std::env::args().nth(1).unwrap()); + path.canonicalize().unwrap() + }; + + let disk_image = create_disk_image(&kernel_binary_path, true); + + let mut run_cmd = Command::new("qemu-system-x86_64"); + run_cmd + .arg("-drive") + .arg(format!("format=raw,file={}", disk_image.display())); + run_cmd.args(QEMU_ARGS); + + let exit_status = run_cmd.status().unwrap(); + match exit_status.code() { + Some(33) => {} // success + Some(35) => panic!("Test failed"), // success + other => panic!("Test failed with unexpected exit code `{:?}`", other), + } +} + +pub fn create_disk_image(kernel_binary_path: &Path, bios_only: bool) -> PathBuf { + let bootloader_manifest_path = bootloader_locator::locate_bootloader("bootloader").unwrap(); + let kernel_manifest_path = locate_cargo_manifest::locate_manifest().unwrap(); + + let mut build_cmd = Command::new(env!("CARGO")); + build_cmd.current_dir(bootloader_manifest_path.parent().unwrap()); + build_cmd.arg("builder"); + build_cmd + .arg("--kernel-manifest") + .arg(&kernel_manifest_path); + build_cmd.arg("--kernel-binary").arg(&kernel_binary_path); + build_cmd + .arg("--target-dir") + .arg(kernel_manifest_path.parent().unwrap().join("target")); + build_cmd + .arg("--out-dir") + .arg(kernel_binary_path.parent().unwrap()); + //build_cmd.arg("--quiet"); + if bios_only { + build_cmd.arg("--firmware").arg("bios"); + } + + if !build_cmd.status().unwrap().success() { + panic!("build failed"); + } + + let kernel_binary_name = kernel_binary_path.file_name().unwrap().to_str().unwrap(); + let disk_image = kernel_binary_path + .parent() + .unwrap() + .join(format!("bootimage-bios-{}.bin", kernel_binary_name)); + if !disk_image.exists() { + panic!( + "Disk image does not exist at {} after bootloader build", + disk_image.display() + ); + } + disk_image +} diff --git a/tests/test_kernels/default_settings/.cargo/config.toml b/tests/test_kernels/default_settings/.cargo/config.toml new file mode 100644 index 00000000..08a49f5a --- /dev/null +++ b/tests/test_kernels/default_settings/.cargo/config.toml @@ -0,0 +1,10 @@ +[unstable] +# TODO: Uncomment once https://github.com/rust-lang/cargo/issues/8643 is merged +# build-std = ["core"] + +[build] +# TODO: Uncomment once https://github.com/rust-lang/cargo/issues/8643 is merged +# target = "x86_64-example-kernel.json" + +[target.'cfg(target_os = "none")'] +runner = "cargo run --manifest-path ../../runner/Cargo.toml" \ No newline at end of file diff --git a/tests/test_kernels/default_settings/.gitignore b/tests/test_kernels/default_settings/.gitignore new file mode 100644 index 00000000..1de56593 --- /dev/null +++ b/tests/test_kernels/default_settings/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/tests/test_kernels/default_settings/Cargo.toml b/tests/test_kernels/default_settings/Cargo.toml new file mode 100644 index 00000000..48270847 --- /dev/null +++ b/tests/test_kernels/default_settings/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "kernel" +version = "0.1.0" +authors = ["Philipp Oppermann "] +edition = "2018" + +[dependencies] +bootloader = { path = "../../.." } +x86_64 = { git = "https://github.com/rust-osdev/x86_64.git", branch = "nop" } diff --git a/tests/test_kernels/default_settings/src/bin/basic_boot.rs b/tests/test_kernels/default_settings/src/bin/basic_boot.rs new file mode 100644 index 00000000..c297cc81 --- /dev/null +++ b/tests/test_kernels/default_settings/src/bin/basic_boot.rs @@ -0,0 +1,18 @@ +#![no_std] // don't link the Rust standard library +#![no_main] // disable all Rust-level entry points + +use bootloader::{entry_point, BootInfo}; +use core::panic::PanicInfo; +use kernel::{exit_qemu, QemuExitCode}; + +entry_point!(kernel_main); + +fn kernel_main(_boot_info: &'static BootInfo) -> ! { + exit_qemu(QemuExitCode::Success); +} + +/// This function is called on panic. +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + exit_qemu(QemuExitCode::Failed); +} diff --git a/tests/test_kernels/default_settings/src/bin/should_panic.rs b/tests/test_kernels/default_settings/src/bin/should_panic.rs new file mode 100644 index 00000000..b95891d3 --- /dev/null +++ b/tests/test_kernels/default_settings/src/bin/should_panic.rs @@ -0,0 +1,20 @@ +#![no_std] // don't link the Rust standard library +#![no_main] // disable all Rust-level entry points + +use bootloader::{entry_point, BootInfo}; +use core::panic::PanicInfo; +use kernel::{exit_qemu, QemuExitCode}; + +entry_point!(kernel_main); + +fn kernel_main(_boot_info: &'static BootInfo) -> ! { + panic!(); + + exit_qemu(QemuExitCode::Failed); +} + +/// This function is called on panic. +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + exit_qemu(QemuExitCode::Success); +} diff --git a/tests/test_kernels/default_settings/src/lib.rs b/tests/test_kernels/default_settings/src/lib.rs new file mode 100644 index 00000000..b399ff58 --- /dev/null +++ b/tests/test_kernels/default_settings/src/lib.rs @@ -0,0 +1,21 @@ +#![no_std] + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum QemuExitCode { + Success = 0x10, + Failed = 0x11, +} + +pub fn exit_qemu(exit_code: QemuExitCode) -> ! { + use x86_64::instructions::{nop, port::Port}; + + unsafe { + let mut port = Port::new(0xf4); + port.write(exit_code as u32); + } + + loop { + nop(); + } +} diff --git a/tests/test_kernels/default_settings/x86_64-example-kernel.json b/tests/test_kernels/default_settings/x86_64-example-kernel.json new file mode 100644 index 00000000..9afe809f --- /dev/null +++ b/tests/test_kernels/default_settings/x86_64-example-kernel.json @@ -0,0 +1,15 @@ +{ + "llvm-target": "x86_64-unknown-none", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "arch": "x86_64", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": "32", + "os": "none", + "executables": true, + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "panic-strategy": "abort", + "disable-redzone": true, + "features": "-mmx,-sse,+soft-float" + } From 772ccc9846c9d74b5d201d8a5a48a9e8951171f8 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 30 Aug 2020 20:23:35 +0200 Subject: [PATCH 078/174] Fix name of panic=abort setting in target JSON file --- x86_64-bootloader.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x86_64-bootloader.json b/x86_64-bootloader.json index ad13109d..074ea1bc 100644 --- a/x86_64-bootloader.json +++ b/x86_64-bootloader.json @@ -15,7 +15,7 @@ "os": "none", "features": "-mmx,-sse,+soft-float", "disable-redzone": true, - "panic": "abort", + "panic-strategy": "abort", "executables": true, "relocation_model": "static" } From 0eeb833bdc0a5b104b8394c9941334ee8b421cf2 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 30 Aug 2020 20:48:31 +0200 Subject: [PATCH 079/174] Fix module structure --- src/bin/bios.rs | 4 ++- src/binary/bios/memory_descriptor.rs | 50 +++++++++++++++++++++++++++- src/binary/bios/mod.rs | 50 +--------------------------- 3 files changed, 53 insertions(+), 51 deletions(-) diff --git a/src/bin/bios.rs b/src/bin/bios.rs index 8c3e3e45..1ef6e09d 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -64,7 +64,9 @@ fn bootloader_main( memory_map_addr: VirtAddr, memory_map_entry_count: u64, ) -> ! { - use bootloader::binary::{bios::E820MemoryRegion, legacy_memory_region::LegacyFrameAllocator}; + use bootloader::binary::{ + bios::memory_descriptor::E820MemoryRegion, legacy_memory_region::LegacyFrameAllocator, + }; let e820_memory_map = { let ptr = usize_from(memory_map_addr.as_u64()) as *const E820MemoryRegion; diff --git a/src/binary/bios/memory_descriptor.rs b/src/binary/bios/memory_descriptor.rs index feef6d55..a3dc8095 100644 --- a/src/binary/bios/memory_descriptor.rs +++ b/src/binary/bios/memory_descriptor.rs @@ -1 +1,49 @@ -pub mod memory_descriptor; \ No newline at end of file +use crate::binary::legacy_memory_region::LegacyMemoryRegion; +use x86_64::PhysAddr; + +impl LegacyMemoryRegion for E820MemoryRegion { + fn start(&self) -> PhysAddr { + PhysAddr::new(self.start_addr) + } + + fn len(&self) -> u64 { + self.len + } + + fn usable(&self) -> bool { + self.region_type == 1 + } + + fn set_start(&mut self, new_start: PhysAddr) { + self.start_addr = new_start.as_u64(); + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C)] +pub struct E820MemoryRegion { + pub start_addr: u64, + pub len: u64, + pub region_type: u32, + pub acpi_extended_attributes: u32, +} + +/* +impl From for MemoryRegion { + fn from(region: E820MemoryRegion) -> MemoryRegion { + let region_type = match region.region_type { + 1 => MemoryRegionType::Usable, + 2 => MemoryRegionType::Reserved, + 3 => MemoryRegionType::AcpiReclaimable, + 4 => MemoryRegionType::AcpiNvs, + 5 => MemoryRegionType::BadMemory, + t => panic!("invalid region type {}", t), + }; + MemoryRegion { + range: FrameRange::new(region.start_addr, region.start_addr + region.len), + region_type, + } + } +} +*/ diff --git a/src/binary/bios/mod.rs b/src/binary/bios/mod.rs index a3dc8095..a5c46950 100644 --- a/src/binary/bios/mod.rs +++ b/src/binary/bios/mod.rs @@ -1,49 +1 @@ -use crate::binary::legacy_memory_region::LegacyMemoryRegion; -use x86_64::PhysAddr; - -impl LegacyMemoryRegion for E820MemoryRegion { - fn start(&self) -> PhysAddr { - PhysAddr::new(self.start_addr) - } - - fn len(&self) -> u64 { - self.len - } - - fn usable(&self) -> bool { - self.region_type == 1 - } - - fn set_start(&mut self, new_start: PhysAddr) { - self.start_addr = new_start.as_u64(); - } -} - -#[doc(hidden)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub struct E820MemoryRegion { - pub start_addr: u64, - pub len: u64, - pub region_type: u32, - pub acpi_extended_attributes: u32, -} - -/* -impl From for MemoryRegion { - fn from(region: E820MemoryRegion) -> MemoryRegion { - let region_type = match region.region_type { - 1 => MemoryRegionType::Usable, - 2 => MemoryRegionType::Reserved, - 3 => MemoryRegionType::AcpiReclaimable, - 4 => MemoryRegionType::AcpiNvs, - 5 => MemoryRegionType::BadMemory, - t => panic!("invalid region type {}", t), - }; - MemoryRegion { - range: FrameRange::new(region.start_addr, region.start_addr + region.len), - region_type, - } - } -} -*/ +pub mod memory_descriptor; From 1353705070c286d451a32e7be9bdc346981d0df6 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 30 Aug 2020 20:51:15 +0200 Subject: [PATCH 080/174] Enable NXE bit before loading kernel --- src/bin/bios.rs | 5 ----- src/binary/mod.rs | 8 ++++++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/bin/bios.rs b/src/bin/bios.rs index 1ef6e09d..61292398 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -250,11 +250,6 @@ fn create_page_tables( } } -fn enable_nxe_bit() { - use x86_64::registers::control::{Efer, EferFlags}; - unsafe { Efer::update(|efer| *efer |= EferFlags::NO_EXECUTE_ENABLE) } -} - fn enable_write_protect_bit() { use x86_64::registers::control::{Cr0, Cr0Flags}; unsafe { Cr0::update(|cr0| *cr0 |= Cr0Flags::WRITE_PROTECT) }; diff --git a/src/binary/mod.rs b/src/binary/mod.rs index 38ee7b2e..c18adfa1 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -87,6 +87,9 @@ where I: ExactSizeIterator + Clone, D: LegacyMemoryRegion, { + // Enable support for the no-execute bit in page tables. + enable_nxe_bit(); + let (entry_point, mut used_entries) = load_kernel::load_kernel(kernel_bytes, kernel_page_table, frame_allocator) .expect("no entry point"); @@ -359,3 +362,8 @@ fn kernel_stack_start_location(used_entries: &mut UsedLevel4Entries) -> VirtAddr .map(VirtAddr::new) .unwrap_or_else(|| used_entries.get_free_address()) } + +fn enable_nxe_bit() { + use x86_64::registers::control::{Efer, EferFlags}; + unsafe { Efer::update(|efer| *efer |= EferFlags::NO_EXECUTE_ENABLE) } +} From aee910525fd03db2373bf37c9e032c2a98688b78 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 30 Aug 2020 20:51:25 +0200 Subject: [PATCH 081/174] Run cargo fmt --- tests/default_settings.rs | 3 +-- tests/test_kernels/default_settings/src/bin/should_panic.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/default_settings.rs b/tests/default_settings.rs index e183cf7e..edbe95ab 100644 --- a/tests/default_settings.rs +++ b/tests/default_settings.rs @@ -5,7 +5,6 @@ fn basic_boot() { run_test_binary("basic_boot"); } - #[test] fn should_panic() { run_test_binary("should_panic"); @@ -19,4 +18,4 @@ fn run_test_binary(bin_name: &str) { cmd.arg("--target").arg(" x86_64-example-kernel.json"); cmd.arg("-Zbuild-std=core"); assert!(cmd.status().unwrap().success()); -} \ No newline at end of file +} diff --git a/tests/test_kernels/default_settings/src/bin/should_panic.rs b/tests/test_kernels/default_settings/src/bin/should_panic.rs index b95891d3..46c141dc 100644 --- a/tests/test_kernels/default_settings/src/bin/should_panic.rs +++ b/tests/test_kernels/default_settings/src/bin/should_panic.rs @@ -9,7 +9,7 @@ entry_point!(kernel_main); fn kernel_main(_boot_info: &'static BootInfo) -> ! { panic!(); - + exit_qemu(QemuExitCode::Failed); } From 672a2c1d77410f4b8e45690f03513f42bf353afd Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 30 Aug 2020 20:51:50 +0200 Subject: [PATCH 082/174] Pass additional command line arguments to runner for test kernels --- tests/runner/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/runner/src/main.rs b/tests/runner/src/main.rs index f51ee64c..1b071580 100644 --- a/tests/runner/src/main.rs +++ b/tests/runner/src/main.rs @@ -26,6 +26,7 @@ fn main() { .arg("-drive") .arg(format!("format=raw,file={}", disk_image.display())); run_cmd.args(QEMU_ARGS); + run_cmd.args(std::env::args().skip(2).collect::>()); let exit_status = run_cmd.status().unwrap(); match exit_status.code() { From 36d697ef7fd5b29893fbab3f653cba54616fa3ee Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 30 Aug 2020 21:03:24 +0200 Subject: [PATCH 083/174] Update CI script for new test framework --- .github/workflows/build.yml | 194 +++++++++++++++--------------------- 1 file changed, 81 insertions(+), 113 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b6264eec..4510eca4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,133 +2,101 @@ name: Build on: push: - branches: - - '*' - - '!staging.tmp' - tags: - - '*' pull_request: jobs: - test: - name: "Test" - - strategy: - matrix: - platform: [ - ubuntu-latest, - macos-latest, - windows-latest - ] + check: + name: Check - runs-on: ${{ matrix.platform }} - timeout-minutes: 15 + runs-on: ubuntu-latest + timeout-minutes: 10 steps: - - name: "Checkout Repository" - uses: actions/checkout@v1 - - - name: Install Rustup - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly - echo ::add-path::$HOME/.cargo/bin - if: runner.os == 'macOS' - - - name: "Print Rust Version" - run: | - rustc -Vv - cargo -Vv - - - name: "Install Rustup Components" - run: rustup component add rust-src llvm-tools-preview - - name: "Install cargo-xbuild" - run: cargo install cargo-xbuild --debug - - name: "Install cargo-binutils" - run: cargo install cargo-binutils --version 0.1.7 --debug - - - run: cargo xbuild - working-directory: test-kernel - name: 'Build Test Kernel' - - - name: 'Build Bootloader' - run: cargo xbuild --bin bootloader --features binary --release - env: - KERNEL: "test-kernel/target/x86_64-test-kernel/debug/test-kernel" - KERNEL_MANIFEST: "test-kernel/Cargo.toml" - - - name: 'Convert Bootloader ELF to Binary' - run: cargo objcopy -- -I elf64-x86-64 -O binary --binary-architecture=i386:x86-64 target/x86_64-bootloader/release/bootloader target/x86_64-bootloader/release/bootloader.bin - - # install QEMU - - name: Install QEMU (Linux) - run: sudo apt update && sudo apt install qemu-system-x86 - if: runner.os == 'Linux' - - name: Install QEMU (macOS) - run: brew install qemu - if: runner.os == 'macOS' - env: - HOMEBREW_NO_AUTO_UPDATE: 1 - HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: 1 - HOMEBREW_NO_INSTALL_CLEANUP: 1 - - name: Install Scoop (Windows) - run: | - Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh') - echo ::add-path::$HOME\scoop\shims - if: runner.os == 'Windows' - shell: pwsh - - name: Install QEMU (Windows) - run: scoop install qemu - if: runner.os == 'Windows' - shell: pwsh - - name: "Print QEMU Version" - run: qemu-system-x86_64 --version - - - name: 'Run Test Kernel with Bootloader' - run: | - qemu-system-x86_64 -drive format=raw,file=target/x86_64-bootloader/release/bootloader.bin -device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none - if [ $? -eq 123 ]; then (exit 0); else (exit 1); fi - shell: 'bash {0}' + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + override: true + - name: "Run `cargo check`" + uses: actions-rs/cargo@v1 + with: + command: check + test: + name: Test - build_example_kernel: - name: "Build Example Kernel" strategy: matrix: - platform: [ - ubuntu-latest, - macos-latest, - windows-latest - ] - runs-on: ${{ matrix.platform }} - timeout-minutes: 10 + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 steps: - - uses: actions/checkout@v1 - - name: Install Rustup - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly - echo ::add-path::$HOME/.cargo/bin - if: runner.os == 'macOS' - - name: "Install Rustup Components" - run: rustup component add rust-src llvm-tools-preview - - name: "Install cargo-xbuild" - run: cargo install cargo-xbuild --debug - - name: 'Build Example Kernel' - run: cargo xbuild - working-directory: example-kernel - + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + override: true - check_formatting: - name: "Check Formatting" + - name: "Install Rustup Components" + run: rustup component add rust-src llvm-tools-preview + + # install QEMU + - name: Install QEMU (Linux) + run: sudo apt update && sudo apt install qemu-system-x86 + if: runner.os == 'Linux' + - name: Install QEMU (macOS) + run: brew install qemu + if: runner.os == 'macOS' + env: + HOMEBREW_NO_AUTO_UPDATE: 1 + HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: 1 + HOMEBREW_NO_INSTALL_CLEANUP: 1 + - name: Install Scoop (Windows) + run: | + Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh') + echo ::add-path::$HOME\scoop\shims + if: runner.os == 'Windows' + shell: pwsh + - name: Install QEMU (Windows) + run: scoop install qemu + if: runner.os == 'Windows' + shell: pwsh + - name: "Print QEMU Version" + run: qemu-system-x86_64 --version + + - name: Run `cargo test` + uses: actions-rs/cargo@v1 + with: + command: test + + fmt: + name: Check Formatting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + override: true + - run: rustup component add rustfmt + - name: Run `cargo fmt --all -- --check` + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + clippy: + name: Clippy runs-on: ubuntu-latest - timeout-minutes: 2 steps: - - uses: actions/checkout@v1 - - name: "Use the latest Rust nightly with rustfmt" - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: profile: minimal - components: rustfmt override: true - - run: cargo fmt -- --check + - run: rustup component add clippy + - name: Run `cargo clippy` + uses: actions-rs/cargo@v1 + with: + command: clippy From 57aad9f3b5de422c8e6245df2681e929655e8168 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 30 Aug 2020 21:13:41 +0200 Subject: [PATCH 084/174] Also create uefi images in test framework --- tests/runner/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runner/src/main.rs b/tests/runner/src/main.rs index 1b071580..9ce1bc26 100644 --- a/tests/runner/src/main.rs +++ b/tests/runner/src/main.rs @@ -19,7 +19,7 @@ fn main() { path.canonicalize().unwrap() }; - let disk_image = create_disk_image(&kernel_binary_path, true); + let disk_image = create_disk_image(&kernel_binary_path, false); let mut run_cmd = Command::new("qemu-system-x86_64"); run_cmd From a4e69dbe10d96c4927b395a6983f4e04af9eb1ab Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 30 Aug 2020 21:13:55 +0200 Subject: [PATCH 085/174] Remove unreachable statement in test --- tests/test_kernels/default_settings/src/bin/should_panic.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_kernels/default_settings/src/bin/should_panic.rs b/tests/test_kernels/default_settings/src/bin/should_panic.rs index 46c141dc..bcf50f07 100644 --- a/tests/test_kernels/default_settings/src/bin/should_panic.rs +++ b/tests/test_kernels/default_settings/src/bin/should_panic.rs @@ -9,8 +9,6 @@ entry_point!(kernel_main); fn kernel_main(_boot_info: &'static BootInfo) -> ! { panic!(); - - exit_qemu(QemuExitCode::Failed); } /// This function is called on panic. From 47bdc02ed7ddeaa435c3d35bb81f10cc436710fd Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 30 Aug 2020 21:20:30 +0200 Subject: [PATCH 086/174] Init new FramebufferInfo::bytes_per_pixel field for uefi executable --- src/bin/uefi.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 33197265..a409628a 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -138,6 +138,7 @@ fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { panic!("Bitmask and BltOnly framebuffers are not supported") } }, + bytes_per_pixel: 4, stride: mode_info.stride(), }; From 41d6d77d0f0326f979ded747a237cf1fbbbe8b8b Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 31 Aug 2020 08:11:17 +0200 Subject: [PATCH 087/174] Use raw strings for include path to fix build on Windows Windows paths use backslashes as path separator, which are interpreted as escape values in normal strings. By using raw strings, we fix this problem. --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index b50ea4d0..05c82f29 100644 --- a/build.rs +++ b/build.rs @@ -123,7 +123,7 @@ mod binary { .len(); file.write_all( format!( - "const KERNEL_SIZE: usize = {}; const KERNEL_BYTES: [u8; KERNEL_SIZE] = *include_bytes!(\"{}\");", + "const KERNEL_SIZE: usize = {}; const KERNEL_BYTES: [u8; KERNEL_SIZE] = *include_bytes!(r\"{}\");", kernel_size, stripped_kernel.display(), ) From 3237429457d3df58080fa284d9c2b7138e3fff3a Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 9 Sep 2020 14:41:53 +0200 Subject: [PATCH 088/174] Pass more framebuffer information to kernel --- src/bin/bios.rs | 16 +++++++---- src/bin/uefi.rs | 20 ++++++------- src/binary/logger.rs | 17 +---------- src/binary/mod.rs | 68 ++++++++++++++++++++++++++------------------ src/boot_info.rs | 44 ++++++++++++++++++++++++++-- src/config.rs | 2 ++ 6 files changed, 104 insertions(+), 63 deletions(-) diff --git a/src/bin/bios.rs b/src/bin/bios.rs index 61292398..8a2938d6 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -11,6 +11,7 @@ compile_error!("The bootloader crate must be compiled for the `x86_64-bootloader extern crate rlibc; +use bootloader::boot_info::FrameBufferInfo; use core::panic::PanicInfo; use core::slice; use usize_conversions::usize_from; @@ -113,7 +114,7 @@ fn bootloader_main( let framebuffer_addr = PhysAddr::new(0xfd000000); let framebuffer_size = 1024 * 768; - init_logger(framebuffer_addr, framebuffer_size); + let framebuffer_info = init_logger(framebuffer_addr, framebuffer_size); let page_tables = create_page_tables(&mut frame_allocator); @@ -127,7 +128,7 @@ fn bootloader_main( frame_allocator, page_tables, framebuffer_addr, - usize_from(framebuffer_size), + framebuffer_info, ); /* @@ -197,19 +198,22 @@ fn bootloader_main( */ } -fn init_logger(framebuffer_start: PhysAddr, framebuffer_size: u64) { +fn init_logger(framebuffer_start: PhysAddr, framebuffer_size: usize) -> FrameBufferInfo { let ptr = framebuffer_start.as_u64() as *mut u8; - let slice = unsafe { slice::from_raw_parts_mut(ptr, usize_from(framebuffer_size)) }; + let slice = unsafe { slice::from_raw_parts_mut(ptr, framebuffer_size) }; slice.fill(0x4); - let info = bootloader::binary::logger::FrameBufferInfo { + let info = bootloader::boot_info::FrameBufferInfo { + byte_len: framebuffer_size, horizontal_resolution: 1024, vertical_resolution: 768, - pixel_format: bootloader::binary::logger::PixelFormat::RGB, + pixel_format: bootloader::boot_info::PixelFormat::RGB, bytes_per_pixel: 3, stride: 1024, }; bootloader::binary::init_logger(slice, info); + + info } /// Creates page table abstraction types for both the bootloader and kernel page tables. diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index a409628a..8b0de55c 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -18,7 +18,7 @@ struct PageAligned(T); extern crate rlibc; -use bootloader::binary::legacy_memory_region::LegacyFrameAllocator; +use bootloader::{binary::legacy_memory_region::LegacyFrameAllocator, boot_info::FrameBufferInfo}; use core::{mem, panic::PanicInfo, slice}; use uefi::{ prelude::{entry, Boot, Handle, ResultExt, Status, SystemTable}, @@ -32,7 +32,7 @@ use x86_64::{ #[entry] fn efi_main(image: Handle, st: SystemTable) -> Status { - let (framebuffer_addr, framebuffer_size) = init_logger(&st); + let (framebuffer_addr, framebuffer_info) = init_logger(&st); log::info!("Hello World from UEFI bootloader!"); log::info!("Using framebuffer at {:#x}", framebuffer_addr); @@ -60,7 +60,7 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { frame_allocator, page_tables, framebuffer_addr, - framebuffer_size, + framebuffer_info, ); } @@ -118,7 +118,7 @@ fn create_page_tables( } } -fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { +fn init_logger(st: &SystemTable) -> (PhysAddr, FrameBufferInfo) { let gop = st .boot_services() .locate_protocol::() @@ -128,12 +128,13 @@ fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { 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()) }; - let info = bootloader::binary::logger::FrameBufferInfo { + let info = FrameBufferInfo { + byte_len: framebuffer.size(), horizontal_resolution: mode_info.resolution().0, vertical_resolution: mode_info.resolution().1, pixel_format: match mode_info.pixel_format() { - PixelFormat::RGB => bootloader::binary::logger::PixelFormat::BGR, - PixelFormat::BGR => bootloader::binary::logger::PixelFormat::BGR, + PixelFormat::RGB => bootloader::boot_info::PixelFormat::BGR, + PixelFormat::BGR => bootloader::boot_info::PixelFormat::BGR, PixelFormat::Bitmask | PixelFormat::BltOnly => { panic!("Bitmask and BltOnly framebuffers are not supported") } @@ -144,10 +145,7 @@ fn init_logger(st: &SystemTable) -> (PhysAddr, usize) { bootloader::binary::init_logger(slice, info); - ( - PhysAddr::new(framebuffer.as_mut_ptr() as u64), - framebuffer.size(), - ) + (PhysAddr::new(framebuffer.as_mut_ptr() as u64), info) } #[panic_handler] diff --git a/src/binary/logger.rs b/src/binary/logger.rs index be918572..68fe97f9 100644 --- a/src/binary/logger.rs +++ b/src/binary/logger.rs @@ -1,3 +1,4 @@ +use crate::boot_info::{FrameBufferInfo, PixelFormat}; use conquer_once::spin::OnceCell; use core::{ fmt::{self, Write}, @@ -141,19 +142,3 @@ impl fmt::Write for Logger { Ok(()) } } - -#[derive(Debug, Clone, Copy)] -pub struct FrameBufferInfo { - pub horizontal_resolution: usize, - pub vertical_resolution: usize, - pub pixel_format: PixelFormat, - pub bytes_per_pixel: usize, - pub stride: usize, -} - -#[derive(Debug, Clone, Copy)] -pub enum PixelFormat { - RGB, - BGR, - U8, -} diff --git a/src/binary/mod.rs b/src/binary/mod.rs index c18adfa1..a8fea1ac 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -1,5 +1,5 @@ use crate::binary::legacy_memory_region::{LegacyFrameAllocator, LegacyMemoryRegion}; -use crate::boot_info::{BootInfo, FrameBufferInfo}; +use crate::boot_info::{BootInfo, FrameBuffer, FrameBufferInfo}; use crate::memory_map::MemoryRegion; use core::{ mem::{self, MaybeUninit}, @@ -42,7 +42,7 @@ include!(concat!(env!("OUT_DIR"), "/bootloader_config.rs")); const PAGE_SIZE: u64 = 4096; -pub fn init_logger(framebuffer: &'static mut [u8], info: logger::FrameBufferInfo) { +pub fn init_logger(framebuffer: &'static mut [u8], info: FrameBufferInfo) { let logger = logger::LOGGER.get_or_init(move || logger::LockedLogger::new(framebuffer, info)); log::set_logger(logger).expect("logger already set"); log::set_max_level(log::LevelFilter::Trace); @@ -53,7 +53,7 @@ pub fn load_and_switch_to_kernel( mut frame_allocator: LegacyFrameAllocator, mut page_tables: PageTables, framebuffer_addr: PhysAddr, - framebuffer_size: usize, + framebuffer_info: FrameBufferInfo, ) -> ! where I: ExactSizeIterator + Clone, @@ -64,13 +64,13 @@ where &mut frame_allocator, &mut page_tables.kernel, framebuffer_addr, - framebuffer_size, + framebuffer_info.byte_len, ); let (boot_info, two_frames) = create_boot_info( frame_allocator, &mut page_tables, &mut mappings, - framebuffer_size, + framebuffer_info, ); switch_to_kernel(page_tables, mappings, boot_info, two_frames); } @@ -115,22 +115,27 @@ where log::info!("Map framebuffer"); // map framebuffer - let framebuffer_start_frame: PhysFrame = PhysFrame::containing_address(framebuffer_addr); - let framebuffer_end_frame = - PhysFrame::containing_address(framebuffer_addr + framebuffer_size - 1u64); - let start_page = Page::containing_address(frame_buffer_location(&mut used_entries)); - for (i, frame) in - PhysFrame::range_inclusive(framebuffer_start_frame, framebuffer_end_frame).enumerate() - { - let page = start_page + u64::from_usize(i); - let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - unsafe { kernel_page_table.map_to(page, frame, flags, frame_allocator) } - .unwrap() - .flush(); - } - let framebuffer_virt_addr = start_page.start_address(); + let framebuffer_virt_addr = if CONFIG.map_framebuffer { + let framebuffer_start_frame: PhysFrame = PhysFrame::containing_address(framebuffer_addr); + let framebuffer_end_frame = + PhysFrame::containing_address(framebuffer_addr + framebuffer_size - 1u64); + let start_page = Page::containing_address(frame_buffer_location(&mut used_entries)); + for (i, frame) in + PhysFrame::range_inclusive(framebuffer_start_frame, framebuffer_end_frame).enumerate() + { + let page = start_page + u64::from_usize(i); + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + unsafe { kernel_page_table.map_to(page, frame, flags, frame_allocator) } + .unwrap() + .flush(); + } + let framebuffer_virt_addr = start_page.start_address(); + Some(framebuffer_virt_addr) + } else { + None + }; - if CONFIG.map_physical_memory { + let physical_memory_offset = if CONFIG.map_physical_memory { let offset = CONFIG .physical_memory_offset .map(VirtAddr::new) @@ -146,21 +151,27 @@ where .unwrap() .ignore(); } - } + + Some(offset) + } else { + None + }; Mappings { framebuffer: framebuffer_virt_addr, entry_point, stack_end, used_entries, + physical_memory_offset, } } pub struct Mappings { pub entry_point: VirtAddr, - pub framebuffer: VirtAddr, pub stack_end: Page, pub used_entries: UsedLevel4Entries, + pub framebuffer: Option, + pub physical_memory_offset: Option, } /// Allocates and initializes the boot info struct and the memory map @@ -168,7 +179,7 @@ pub fn create_boot_info( mut frame_allocator: LegacyFrameAllocator, page_tables: &mut PageTables, mappings: &mut Mappings, - framebuffer_size: usize, + framebuffer_info: FrameBufferInfo, ) -> (&'static mut BootInfo, TwoFrames) where I: ExactSizeIterator + Clone, @@ -234,10 +245,13 @@ where // create boot info let boot_info = boot_info.write(BootInfo { memory_regions, - framebuffer: FrameBufferInfo { - start_addr: mappings.framebuffer.as_u64(), - len: framebuffer_size, - }, + framebuffer: mappings.framebuffer.map(|addr| FrameBuffer { + buffer_start: addr.as_u64(), + buffer_byte_len: framebuffer_info.byte_len, + info: framebuffer_info, + }), + physical_memory_offset: mappings.physical_memory_offset.map(VirtAddr::as_u64), + _non_exhaustive: (), }); (boot_info, two_frames) diff --git a/src/boot_info.rs b/src/boot_info.rs index 7a888606..7bd03cdc 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -1,17 +1,55 @@ use crate::memory_map::MemoryRegion; +use core::slice; #[derive(Debug)] #[repr(C)] pub struct BootInfo { pub memory_regions: &'static mut [MemoryRegion], - pub framebuffer: FrameBufferInfo, + pub framebuffer: Option, + pub physical_memory_offset: Option, + pub(crate) _non_exhaustive: (), } #[derive(Debug)] #[repr(C)] +pub struct FrameBuffer { + pub(crate) buffer_start: u64, + pub(crate) buffer_byte_len: usize, + pub(crate) info: FrameBufferInfo, +} + +impl FrameBuffer { + pub fn buffer(&mut self) -> &mut [u8] { + unsafe { self.create_buffer() } + } + + unsafe fn create_buffer<'a>(&self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.buffer_start as *mut u8, self.buffer_byte_len) } + } + + pub fn info(&self) -> FrameBufferInfo { + self.info + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] pub struct FrameBufferInfo { - pub start_addr: u64, - pub len: usize, + pub byte_len: usize, + pub horizontal_resolution: usize, + pub vertical_resolution: usize, + pub pixel_format: PixelFormat, + pub bytes_per_pixel: usize, + pub stride: usize, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +#[non_exhaustive] +pub enum PixelFormat { + RGB, + BGR, + U8, } extern "C" fn _assert_ffi(_boot_info: &'static mut BootInfo) {} diff --git a/src/config.rs b/src/config.rs index e361a68b..f36c4214 100644 --- a/src/config.rs +++ b/src/config.rs @@ -6,6 +6,7 @@ pub struct Config { pub physical_memory_offset: Option, pub kernel_stack_address: Option, pub boot_info_address: Option, + pub map_framebuffer: bool, pub framebuffer_address: Option, } @@ -18,6 +19,7 @@ impl Default for Config { kernel_stack_address: None, kernel_stack_size: None, boot_info_address: None, + map_framebuffer: true, framebuffer_address: None, } } From 1ae2bbc2c29b3f2bf9dd7a749cb0af43a16fdfc2 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 9 Sep 2020 14:42:24 +0200 Subject: [PATCH 089/174] Remove some old helper functions that are no longer needed --- src/lib.rs | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 20a75611..e9f2edfc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,27 +55,11 @@ compile_error!("This crate only supports the x86_64 architecture."); macro_rules! entry_point { ($path:path) => { #[export_name = "_start"] - pub extern "C" fn __impl_start(boot_info: &'static $crate::boot_info::BootInfo) -> ! { + pub extern "C" fn __impl_start(boot_info: &'static mut $crate::boot_info::BootInfo) -> ! { // validate the signature of the program entry point - let f: fn(&'static $crate::boot_info::BootInfo) -> ! = $path; + let f: fn(&'static mut $crate::boot_info::BootInfo) -> ! = $path; f(boot_info) } }; } - -#[cfg(feature = "bios_bin")] -pub fn phys_frame_range(range: bootinfo::FrameRange) -> PhysFrameRange { - PhysFrameRange { - start: PhysFrame::from_start_address(PhysAddr::new(range.start_addr())).unwrap(), - end: PhysFrame::from_start_address(PhysAddr::new(range.end_addr())).unwrap(), - } -} - -#[cfg(feature = "bios_bin")] -pub fn frame_range(range: PhysFrameRange) -> bootinfo::FrameRange { - bootinfo::FrameRange::new( - range.start.start_address().as_u64(), - range.end.start_address().as_u64(), - ) -} From 8ff1624d8b318756ade282c8ac4b1e8ddfe6768f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 9 Sep 2020 15:09:41 +0200 Subject: [PATCH 090/174] Include .bss input sections in bootloader section Otherwise they are not loaded, which results in undefined behavior when bss state is used, e.g. on `log::set_logger`. --- linker.ld | 1 + 1 file changed, 1 insertion(+) diff --git a/linker.ld b/linker.ld index 4e58320a..33570c2e 100644 --- a/linker.ld +++ b/linker.ld @@ -37,6 +37,7 @@ SECTIONS { *(.text .text.*) *(.rodata .rodata.*) *(.data .data.*) + *(.bss .bss.*) *(.got) . = ALIGN(512); _rest_of_bootloader_end_addr = .; From 86e2b0541ef44decbc022a9e4dd62182e0534c1f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 9 Sep 2020 15:18:57 +0200 Subject: [PATCH 091/174] Parse map-framebuffer option in build.rs --- build.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.rs b/build.rs index 05c82f29..20571b65 100644 --- a/build.rs +++ b/build.rs @@ -260,6 +260,9 @@ mod binary { ("map-page-table-recursively", Value::Boolean(b)) => { config.map_page_table_recursively = b; } + ("map-framebuffer", Value::Boolean(b)) => { + config.map_framebuffer = b; + } ("kernel-stack-size", Value::Integer(i)) => { if i <= 0 { panic!("`kernel-stack-size` in kernel manifest must be positive"); From f2a29b7656b5ba88e2944029fb2523203bdf21ee Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 9 Sep 2020 15:19:08 +0200 Subject: [PATCH 092/174] Fix BIOS framebuffer size --- src/bin/bios.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/bios.rs b/src/bin/bios.rs index 8a2938d6..b777f29a 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -113,7 +113,7 @@ fn bootloader_main( } let framebuffer_addr = PhysAddr::new(0xfd000000); - let framebuffer_size = 1024 * 768; + let framebuffer_size = 1024 * 768 * 3; let framebuffer_info = init_logger(framebuffer_addr, framebuffer_size); let page_tables = create_page_tables(&mut frame_allocator); From 86fd6712f7b79572e19ded0ff85171ee24e5c4c5 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 9 Sep 2020 18:56:47 +0200 Subject: [PATCH 093/174] Update to latest Rust nightly --- src/bin/uefi.rs | 1 - src/binary/legacy_memory_region.rs | 2 +- src/lib.rs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 8b0de55c..1f927fd4 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -5,7 +5,6 @@ #![feature(unsafe_block_in_unsafe_fn)] #![feature(min_const_generics)] #![feature(maybe_uninit_extra)] -#![feature(maybe_uninit_slice_assume_init)] #![deny(unsafe_op_in_unsafe_fn)] // Defines the constants `KERNEL_BYTES` (array of `u8`) and `KERNEL_SIZE` (`usize`). diff --git a/src/binary/legacy_memory_region.rs b/src/binary/legacy_memory_region.rs index 82605c03..edde91c5 100644 --- a/src/binary/legacy_memory_region.rs +++ b/src/binary/legacy_memory_region.rs @@ -115,7 +115,7 @@ where } let initialized = &mut regions[..next_index]; - unsafe { MaybeUninit::slice_get_mut(initialized) } + unsafe { MaybeUninit::slice_assume_init_mut(initialized) } } fn add_region( diff --git a/src/lib.rs b/src/lib.rs index e9f2edfc..363c5bbd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,8 +10,8 @@ #![feature(slice_fill)] #![feature(asm)] #![feature(unsafe_block_in_unsafe_fn)] -#![feature(maybe_uninit_slice_assume_init)] #![feature(maybe_uninit_extra)] +#![feature(maybe_uninit_slice)] #![deny(unsafe_op_in_unsafe_fn)] //#![warn(missing_docs)] From d782493df2ca60712e2912826b35f8b0880a5834 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 9 Sep 2020 18:57:08 +0200 Subject: [PATCH 094/174] Reduce and improve log output --- src/binary/mod.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/binary/mod.rs b/src/binary/mod.rs index a8fea1ac..334fc972 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -112,10 +112,10 @@ where .flush(); } - log::info!("Map framebuffer"); - // map framebuffer let framebuffer_virt_addr = if CONFIG.map_framebuffer { + log::info!("Map framebuffer"); + let framebuffer_start_frame: PhysFrame = PhysFrame::containing_address(framebuffer_addr); let framebuffer_end_frame = PhysFrame::containing_address(framebuffer_addr + framebuffer_size - 1u64); @@ -136,6 +136,7 @@ where }; let physical_memory_offset = if CONFIG.map_physical_memory { + log::info!("Map physical memory"); let offset = CONFIG .physical_memory_offset .map(VirtAddr::new) @@ -200,12 +201,10 @@ where let start_page = Page::containing_address(boot_info_addr); let end_page = Page::containing_address(memory_map_regions_end - 1u64); for page in Page::range_inclusive(start_page, end_page) { - log::info!("Mapping page {:?}", page); let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; let frame = frame_allocator .allocate_frame() .expect("frame allocation for boot info failed"); - log::info!("1 {:?}", page); unsafe { page_tables .kernel @@ -213,7 +212,6 @@ where } .unwrap() .flush(); - log::info!("2 {:?}", page); // we need to be able to access it too unsafe { page_tables @@ -222,7 +220,6 @@ where } .unwrap() .flush(); - log::info!("Finished mapping page {:?}", page); } let boot_info: &'static mut MaybeUninit = From 694d3139fe24bce0184da3bb9096e6540ee1fa3d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 20 Sep 2020 21:20:04 +0200 Subject: [PATCH 095/174] Pass RSDP address in boot info --- Cargo.lock | 9 +++++++++ Cargo.toml | 3 ++- src/bin/bios.rs | 44 +++++++++++++++++++++++++++++++++++++++++--- src/bin/uefi.rs | 22 ++++++++++++++++++---- src/binary/mod.rs | 23 +++++++++++++++-------- src/boot_info.rs | 1 + 6 files changed, 86 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a6724689..cbba5f33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,7 @@ dependencies = [ "llvm-tools", "log", "rlibc", + "rsdp_search", "spinning_top", "thiserror", "toml", @@ -203,6 +204,14 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" +[[package]] +name = "rsdp_search" +version = "0.1.0" +source = "git+https://github.com/IsaacWoods/acpi.git?branch=new#72b87e55da3a386ecb313d9c7b1b41a82ff9d704" +dependencies = [ + "log", +] + [[package]] name = "runner" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 57b95162..e790d3cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ anyhow = { version = "1.0.32", optional = true } llvm-tools = { version = "0.1.1", optional = true } thiserror = { version = "1.0.20", optional = true } json = { version = "0.12.4", optional = true } +rsdp_search = { git = "https://github.com/IsaacWoods/acpi.git", branch = "new", optional = true } [dependencies.font8x8] version = "0.2.5" @@ -61,7 +62,7 @@ toml = { version = "0.5.1", optional = true } default = [] builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json"] runner = ["anyhow"] -bios_bin = ["binary", "rlibc", "vga_320x200"] +bios_bin = ["binary", "rlibc", "vga_320x200", "rsdp_search"] uefi_bin = ["binary", "rlibc", "uefi", "font8x8"] binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", "log", "conquer-once", "spinning_top"] vga_320x200 = ["font8x8"] diff --git a/src/bin/bios.rs b/src/bin/bios.rs index b777f29a..d8a29796 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -11,7 +11,7 @@ compile_error!("The bootloader crate must be compiled for the `x86_64-bootloader extern crate rlibc; -use bootloader::boot_info::FrameBufferInfo; +use bootloader::{binary::SystemInfo, boot_info::FrameBufferInfo}; use core::panic::PanicInfo; use core::slice; use usize_conversions::usize_from; @@ -123,12 +123,17 @@ fn bootloader_main( unsafe { slice::from_raw_parts(ptr, usize_from(kernel_size)) } }; + let system_info = SystemInfo { + framebuffer_addr, + framebuffer_info, + rsdp_addr: detect_rsdp(), + }; + bootloader::binary::load_and_switch_to_kernel( kernel, frame_allocator, page_tables, - framebuffer_addr, - framebuffer_info, + system_info, ); /* @@ -259,6 +264,39 @@ fn enable_write_protect_bit() { unsafe { Cr0::update(|cr0| *cr0 |= Cr0Flags::WRITE_PROTECT) }; } +fn detect_rsdp() -> Option { + use core::ptr::NonNull; + use rsdp_search::{ + handler::{AcpiHandler, PhysicalMapping}, + Rsdp, + }; + + struct IdentityMapped; + impl AcpiHandler for IdentityMapped { + unsafe fn map_physical_region( + &self, + physical_address: usize, + size: usize, + ) -> PhysicalMapping { + PhysicalMapping { + physical_start: physical_address, + virtual_start: NonNull::new(physical_address as *mut _).unwrap(), + region_length: size, + mapped_length: size, + handler: Self, + } + } + + fn unmap_physical_region(&self, _region: &PhysicalMapping) {} + } + + unsafe { + Rsdp::search_for_on_bios(IdentityMapped) + .ok() + .map(|mapping| PhysAddr::new(mapping.physical_start as u64)) + } +} + #[panic_handler] fn panic(info: &PanicInfo) -> ! { unsafe { diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 1f927fd4..32a45a78 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -17,7 +17,10 @@ struct PageAligned(T); extern crate rlibc; -use bootloader::{binary::legacy_memory_region::LegacyFrameAllocator, boot_info::FrameBufferInfo}; +use bootloader::{ + binary::{legacy_memory_region::LegacyFrameAllocator, SystemInfo}, + boot_info::FrameBufferInfo, +}; use core::{mem, panic::PanicInfo, slice}; use uefi::{ prelude::{entry, Boot, Handle, ResultExt, Status, SystemTable}, @@ -46,7 +49,7 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { }; log::trace!("exiting boot services"); - let (_st, memory_map) = st + let (system_table, memory_map) = st .exit_boot_services(image, mmap_storage) .expect_success("Failed to exit boot services"); @@ -54,12 +57,23 @@ fn efi_main(image: Handle, st: SystemTable) -> Status { let page_tables = create_page_tables(&mut frame_allocator); + let system_info = SystemInfo { + framebuffer_addr, + framebuffer_info, + rsdp_addr: { + use uefi::table::cfg; + let mut config_entries = system_table.config_table().iter(); + config_entries + .find(|entry| matches!(entry.guid, cfg::ACPI_GUID | cfg::ACPI2_GUID)) + .map(|entry| PhysAddr::new(entry.address as u64)) + }, + }; + bootloader::binary::load_and_switch_to_kernel( &KERNEL.0, frame_allocator, page_tables, - framebuffer_addr, - framebuffer_info, + system_info, ); } diff --git a/src/binary/mod.rs b/src/binary/mod.rs index 334fc972..72d08c1a 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -48,12 +48,18 @@ pub fn init_logger(framebuffer: &'static mut [u8], info: FrameBufferInfo) { log::set_max_level(log::LevelFilter::Trace); } +#[derive(Debug, Copy, Clone)] +pub struct SystemInfo { + pub framebuffer_addr: PhysAddr, + pub framebuffer_info: FrameBufferInfo, + pub rsdp_addr: Option, +} + pub fn load_and_switch_to_kernel( kernel_bytes: &[u8], mut frame_allocator: LegacyFrameAllocator, mut page_tables: PageTables, - framebuffer_addr: PhysAddr, - framebuffer_info: FrameBufferInfo, + system_info: SystemInfo, ) -> ! where I: ExactSizeIterator + Clone, @@ -63,14 +69,14 @@ where kernel_bytes, &mut frame_allocator, &mut page_tables.kernel, - framebuffer_addr, - framebuffer_info.byte_len, + system_info.framebuffer_addr, + system_info.framebuffer_info.byte_len, ); let (boot_info, two_frames) = create_boot_info( frame_allocator, &mut page_tables, &mut mappings, - framebuffer_info, + system_info, ); switch_to_kernel(page_tables, mappings, boot_info, two_frames); } @@ -180,7 +186,7 @@ pub fn create_boot_info( mut frame_allocator: LegacyFrameAllocator, page_tables: &mut PageTables, mappings: &mut Mappings, - framebuffer_info: FrameBufferInfo, + system_info: SystemInfo, ) -> (&'static mut BootInfo, TwoFrames) where I: ExactSizeIterator + Clone, @@ -244,10 +250,11 @@ where memory_regions, framebuffer: mappings.framebuffer.map(|addr| FrameBuffer { buffer_start: addr.as_u64(), - buffer_byte_len: framebuffer_info.byte_len, - info: framebuffer_info, + buffer_byte_len: system_info.framebuffer_info.byte_len, + info: system_info.framebuffer_info, }), physical_memory_offset: mappings.physical_memory_offset.map(VirtAddr::as_u64), + rsdp_addr: system_info.rsdp_addr.map(|addr| addr.as_u64()), _non_exhaustive: (), }); diff --git a/src/boot_info.rs b/src/boot_info.rs index 7bd03cdc..19add652 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -7,6 +7,7 @@ pub struct BootInfo { pub memory_regions: &'static mut [MemoryRegion], pub framebuffer: Option, pub physical_memory_offset: Option, + pub rsdp_addr: Option, pub(crate) _non_exhaustive: (), } From 3b761790c350b4aaddb3aab6e749807684bbf92c Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 21 Sep 2020 12:16:54 +0200 Subject: [PATCH 096/174] Run cargo update --- Cargo.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbba5f33..487587e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,7 +73,7 @@ dependencies = [ "toml", "uefi", "usize_conversions", - "x86_64 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "x86_64 0.11.7", "xmas-elf", ] @@ -144,7 +144,7 @@ name = "kernel" version = "0.1.0" dependencies = [ "bootloader", - "x86_64 0.11.2 (git+https://github.com/rust-osdev/x86_64.git?branch=nop)", + "x86_64 0.11.2", ] [[package]] @@ -155,9 +155,9 @@ checksum = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" [[package]] name = "locate-cargo-manifest" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf4c572ea735347f84c196204b41411e1c266006297043db6cf7c6235d3edff" +checksum = "db985b63431fe09e8d71f50aeceffcc31e720cb86be8dad2f38d084c5a328466" dependencies = [ "json", ] @@ -182,9 +182,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.19" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" +checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" dependencies = [ "unicode-xid", ] @@ -228,9 +228,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" +checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" [[package]] name = "spinning_top" @@ -243,9 +243,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.39" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9" +checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" dependencies = [ "proc-macro2", "quote", @@ -293,7 +293,7 @@ dependencies = [ [[package]] name = "uefi" version = "0.5.0" -source = "git+https://github.com/rust-osdev/uefi-rs.git#e482874f3d92e258eb9d7caa9782ecc679417287" +source = "git+https://github.com/rust-osdev/uefi-rs.git#b5dbb720e7460ca94f465745ce24f9e2aa2347df" dependencies = [ "bitflags", "log", @@ -333,8 +333,7 @@ checksum = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" [[package]] name = "x86_64" version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ca13fe418403c0df903ee7eb3bf1a51a742bb339b42a329642f386224b8e64" +source = "git+https://github.com/rust-osdev/x86_64.git?branch=nop#0b2bf80eeb22f9e8452b604624704e52d895b6d0" dependencies = [ "bit_field 0.9.0", "bitflags", @@ -342,8 +341,9 @@ dependencies = [ [[package]] name = "x86_64" -version = "0.11.2" -source = "git+https://github.com/rust-osdev/x86_64.git?branch=nop#0b2bf80eeb22f9e8452b604624704e52d895b6d0" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa580d2cf2a6a8c55f6283d6d06271b1ccab4d93cb3741edab290d5408d848c4" dependencies = [ "bit_field 0.9.0", "bitflags", From 450b83b9ffb163afa4358303fd417cc052e3bc6e Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 25 Sep 2020 15:54:52 +0200 Subject: [PATCH 097/174] Update to x86_64 0.12.1 --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 487587e4..05bfa17a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,7 +73,7 @@ dependencies = [ "toml", "uefi", "usize_conversions", - "x86_64 0.11.7", + "x86_64 0.12.1", "xmas-elf", ] @@ -341,9 +341,9 @@ dependencies = [ [[package]] name = "x86_64" -version = "0.11.7" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa580d2cf2a6a8c55f6283d6d06271b1ccab4d93cb3741edab290d5408d848c4" +checksum = "238a5798f77641af3c4f242bf985807f312a480cd4e35ed7255fad4b2ccb9d4f" dependencies = [ "bit_field 0.9.0", "bitflags", diff --git a/Cargo.toml b/Cargo.toml index e790d3cb..2a27cdab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ required-features = ["uefi_bin"] [dependencies] xmas-elf = { version = "0.6.2", optional = true } -x86_64 = { version = "0.11.0", optional = true } +x86_64 = { version = "0.12.1", optional = true } usize_conversions = { version = "0.2.0", optional = true } bit_field = { version = "0.10.0", optional = true } rlibc = { version = "1.0.0", optional = true } From cb8345bd3c45fc3c56a631a3b416137c45f828f9 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 25 Sep 2020 15:55:31 +0200 Subject: [PATCH 098/174] Use published `rsdp` crate --- Cargo.lock | 9 +++++---- Cargo.toml | 4 ++-- src/bin/bios.rs | 3 ++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 05bfa17a..e9f1f7d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,7 +67,7 @@ dependencies = [ "llvm-tools", "log", "rlibc", - "rsdp_search", + "rsdp", "spinning_top", "thiserror", "toml", @@ -205,9 +205,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" [[package]] -name = "rsdp_search" -version = "0.1.0" -source = "git+https://github.com/IsaacWoods/acpi.git?branch=new#72b87e55da3a386ecb313d9c7b1b41a82ff9d704" +name = "rsdp" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b26cb0132c85db7152fc291c9680a9240d4ef482287667819dbaec1d665dbd" dependencies = [ "log", ] diff --git a/Cargo.toml b/Cargo.toml index 2a27cdab..706ed4ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ anyhow = { version = "1.0.32", optional = true } llvm-tools = { version = "0.1.1", optional = true } thiserror = { version = "1.0.20", optional = true } json = { version = "0.12.4", optional = true } -rsdp_search = { git = "https://github.com/IsaacWoods/acpi.git", branch = "new", optional = true } +rsdp = { version = "1.0.0", optional = true } [dependencies.font8x8] version = "0.2.5" @@ -62,7 +62,7 @@ toml = { version = "0.5.1", optional = true } default = [] builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json"] runner = ["anyhow"] -bios_bin = ["binary", "rlibc", "vga_320x200", "rsdp_search"] +bios_bin = ["binary", "rlibc", "vga_320x200", "rsdp"] uefi_bin = ["binary", "rlibc", "uefi", "font8x8"] binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", "log", "conquer-once", "spinning_top"] vga_320x200 = ["font8x8"] diff --git a/src/bin/bios.rs b/src/bin/bios.rs index d8a29796..d3f7abd5 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -266,11 +266,12 @@ fn enable_write_protect_bit() { fn detect_rsdp() -> Option { use core::ptr::NonNull; - use rsdp_search::{ + use rsdp::{ handler::{AcpiHandler, PhysicalMapping}, Rsdp, }; + #[derive(Clone)] struct IdentityMapped; impl AcpiHandler for IdentityMapped { unsafe fn map_physical_region( From 53e5c25cbf0c91f146e118569a9a8aea81ca7838 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 27 Sep 2020 22:25:52 +0200 Subject: [PATCH 099/174] Remove commented out BIOS code that was already reimplemented in the new version --- src/bin/bios.rs | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/src/bin/bios.rs b/src/bin/bios.rs index d3f7abd5..6bfcd44c 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -140,48 +140,6 @@ fn bootloader_main( // Enable support for the no-execute bit in page tables. enable_nxe_bit(); - - let physical_memory_offset = if cfg!(feature = "map_physical_memory") { - let physical_memory_offset = PHYSICAL_MEMORY_OFFSET.unwrap_or_else(|| { - // If offset not manually provided, find a free p4 entry and map memory here. - // One level 4 entry spans 2^48/512 bytes (over 500gib) so this should suffice. - assert!(max_phys_addr < (1 << 48) / 512); - Page::from_page_table_indices_1gib( - level4_entries.get_free_entry(), - PageTableIndex::new(0), - ) - .start_address() - .as_u64() - }); - - let virt_for_phys = - |phys: PhysAddr| -> VirtAddr { VirtAddr::new(phys.as_u64() + physical_memory_offset) }; - - let start_frame = PhysFrame::::containing_address(PhysAddr::new(0)); - let end_frame = PhysFrame::::containing_address(PhysAddr::new(max_phys_addr)); - - for frame in PhysFrame::range_inclusive(start_frame, end_frame) { - let page = Page::containing_address(virt_for_phys(frame.start_address())); - let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - unsafe { - page_table::map_page( - page, - frame, - flags, - &mut rec_page_table, - &mut frame_allocator, - ) - } - .expect("Mapping of bootinfo page failed") - .flush(); - } - - physical_memory_offset - } else { - 0 // Value is unused by BootInfo::new, so this doesn't matter - }; - - // Make sure that the kernel respects the write-protection bits, even when in ring 0. enable_write_protect_bit(); From 34a5da852ba8f6b0abfafb5c5a68adc4cae638fa Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 27 Sep 2020 22:27:16 +0200 Subject: [PATCH 100/174] Re-add support for setting up a recursive page table mapping --- Cargo.lock | 3 +-- Cargo.toml | 2 +- build.rs | 11 +++++++++++ src/binary/mod.rs | 35 +++++++++++++++++++++++++++++++---- src/boot_info.rs | 1 + src/config.rs | 2 ++ 6 files changed, 47 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e9f1f7d3..bc2e90b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -343,8 +343,7 @@ dependencies = [ [[package]] name = "x86_64" version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238a5798f77641af3c4f242bf985807f312a480cd4e35ed7255fad4b2ccb9d4f" +source = "git+https://github.com/rust-osdev/x86_64.git?branch=access-level-4-table#b2868c966547fab1469ddd86e85f5611bc18797e" dependencies = [ "bit_field 0.9.0", "bitflags", diff --git a/Cargo.toml b/Cargo.toml index 706ed4ca..1ce55d3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ required-features = ["uefi_bin"] [dependencies] xmas-elf = { version = "0.6.2", optional = true } -x86_64 = { version = "0.12.1", optional = true } +x86_64 = { git = "https://github.com/rust-osdev/x86_64.git", branch = "access-level-4-table", optional = true } usize_conversions = { version = "0.2.0", optional = true } bit_field = { version = "0.10.0", optional = true } rlibc = { version = "1.0.0", optional = true } diff --git a/build.rs b/build.rs index 20571b65..0ee50253 100644 --- a/build.rs +++ b/build.rs @@ -248,6 +248,7 @@ mod binary { impl Config { fn parse(table: &toml::value::Table) -> Self { + use std::convert::TryFrom; use toml::Value; let mut config = Self::default(); @@ -271,6 +272,16 @@ mod binary { } } + ("recursive-page-table-index", Value::Integer(i)) => { + let index = match u16::try_from(i) { + Ok(index) if index >= 0 && index < 512 => index, + _other => panic!( + "`recursive-page-table-index` must be a number between 0 and 512" + ), + }; + config.recursive_index = Some(index); + } + ("physical-memory-offset", Value::Integer(i)) | ("kernel-stack-address", Value::Integer(i)) | ("boot-info-address", Value::Integer(i)) diff --git a/src/binary/mod.rs b/src/binary/mod.rs index 72d08c1a..dd7667b7 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -11,8 +11,8 @@ use usize_conversions::FromUsize; use x86_64::{ registers, structures::paging::{ - FrameAllocator, Mapper, OffsetPageTable, Page, PageTableFlags, PhysFrame, Size2MiB, - Size4KiB, + FrameAllocator, Mapper, OffsetPageTable, Page, PageTableFlags, PageTableIndex, PhysFrame, + Size2MiB, Size4KiB, }, PhysAddr, VirtAddr, }; @@ -68,7 +68,7 @@ where let mut mappings = set_up_mappings( kernel_bytes, &mut frame_allocator, - &mut page_tables.kernel, + &mut page_tables, system_info.framebuffer_addr, system_info.framebuffer_info.byte_len, ); @@ -85,7 +85,7 @@ where pub fn set_up_mappings( kernel_bytes: &[u8], frame_allocator: &mut LegacyFrameAllocator, - kernel_page_table: &mut OffsetPageTable, + page_tables: &mut PageTables, framebuffer_addr: PhysAddr, framebuffer_size: usize, ) -> Mappings @@ -93,6 +93,8 @@ where I: ExactSizeIterator + Clone, D: LegacyMemoryRegion, { + let kernel_page_table = &mut page_tables.kernel; + // Enable support for the no-execute bit in page tables. enable_nxe_bit(); @@ -164,12 +166,35 @@ where None }; + let recursive_index = if CONFIG.map_page_table_recursively { + log::info!("Map page table recursively"); + let index = CONFIG + .recursive_index + .map(PageTableIndex::new) + .unwrap_or_else(|| used_entries.get_free_entry()); + + let entry = &mut kernel_page_table.level_4_table()[index]; + if !entry.is_unused() { + panic!( + "Could not set up recursive mapping: index {} already in use", + u16::from(index) + ); + } + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + entry.set_frame(page_tables.kernel_level_4_frame, flags); + + Some(index) + } else { + None + }; + Mappings { framebuffer: framebuffer_virt_addr, entry_point, stack_end, used_entries, physical_memory_offset, + recursive_index, } } @@ -179,6 +204,7 @@ pub struct Mappings { pub used_entries: UsedLevel4Entries, pub framebuffer: Option, pub physical_memory_offset: Option, + pub recursive_index: Option, } /// Allocates and initializes the boot info struct and the memory map @@ -254,6 +280,7 @@ where info: system_info.framebuffer_info, }), physical_memory_offset: mappings.physical_memory_offset.map(VirtAddr::as_u64), + recursive_index: mappings.recursive_index.map(Into::into), rsdp_addr: system_info.rsdp_addr.map(|addr| addr.as_u64()), _non_exhaustive: (), }); diff --git a/src/boot_info.rs b/src/boot_info.rs index 19add652..44b9c9a0 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -7,6 +7,7 @@ pub struct BootInfo { pub memory_regions: &'static mut [MemoryRegion], pub framebuffer: Option, pub physical_memory_offset: Option, + pub recursive_index: Option, pub rsdp_addr: Option, pub(crate) _non_exhaustive: (), } diff --git a/src/config.rs b/src/config.rs index f36c4214..dfea157d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,6 +4,7 @@ pub struct Config { pub map_page_table_recursively: bool, pub kernel_stack_size: Option, pub physical_memory_offset: Option, + pub recursive_index: Option, pub kernel_stack_address: Option, pub boot_info_address: Option, pub map_framebuffer: bool, @@ -16,6 +17,7 @@ impl Default for Config { map_physical_memory: false, map_page_table_recursively: false, physical_memory_offset: None, + recursive_index: None, kernel_stack_address: None, kernel_stack_size: None, boot_info_address: None, From 5884d15de3a08146c7cb400d9ba64c93268692fa Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 27 Sep 2020 22:28:04 +0200 Subject: [PATCH 101/174] Remove more commented out code that was already reimplmented --- src/bin/bios.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/bin/bios.rs b/src/bin/bios.rs index 6bfcd44c..c5f09605 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -137,24 +137,9 @@ fn bootloader_main( ); /* - // Enable support for the no-execute bit in page tables. - enable_nxe_bit(); - // Make sure that the kernel respects the write-protection bits, even when in ring 0. enable_write_protect_bit(); - if cfg!(not(feature = "recursive_page_table")) { - // unmap recursive entry - rec_page_table - .unmap(Page::::containing_address( - recursive_page_table_addr, - )) - .expect("error deallocating recursive entry") - .1 - .flush(); - mem::drop(rec_page_table); - } - #[cfg(feature = "sse")] sse::enable_sse(); From 7be32214b949053a620d047d85f0db927c102854 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 28 Sep 2020 10:18:09 +0200 Subject: [PATCH 102/174] Use new `uefi` crates.io release --- Cargo.lock | 5 +++-- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc2e90b9..49942f08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,8 +293,9 @@ dependencies = [ [[package]] name = "uefi" -version = "0.5.0" -source = "git+https://github.com/rust-osdev/uefi-rs.git#b5dbb720e7460ca94f465745ce24f9e2aa2347df" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43cbf498e073acb4d1f5bd59b5347757b495163ecfce9d18db81c44b99a542fa" dependencies = [ "bitflags", "log", diff --git a/Cargo.toml b/Cargo.toml index 1ce55d3b..dc6216f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ usize_conversions = { version = "0.2.0", optional = true } bit_field = { version = "0.10.0", optional = true } rlibc = { version = "1.0.0", optional = true } log = { version = "0.4.8", optional = true } -uefi = { git = "https://github.com/rust-osdev/uefi-rs.git", optional = true } +uefi = { version = "0.6.0", optional = true } argh = { version = "0.1.3", optional = true } displaydoc = { version = "0.1.7", optional = true } conquer-once = { version = "0.2.1", optional = true, default-features = false } From dfa82bdfcdaa9e9ebd199dde9bc87fe23ec7e483 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 1 Oct 2020 14:12:24 +0200 Subject: [PATCH 103/174] Use compiler_builtins mem feature instead of rlibc Also: Update x86_64 to 0.12.2 to fix build on latest nightly. --- Cargo.lock | 21 +++++++-------------- Cargo.toml | 5 ++--- src/bin/bios.rs | 2 -- src/bin/builder.rs | 2 ++ src/bin/uefi.rs | 2 -- 5 files changed, 11 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 49942f08..84fcc0f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,14 +66,13 @@ dependencies = [ "json", "llvm-tools", "log", - "rlibc", "rsdp", "spinning_top", "thiserror", "toml", "uefi", "usize_conversions", - "x86_64 0.12.1", + "x86_64 0.12.2", "xmas-elf", ] @@ -182,9 +181,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.21" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] @@ -198,12 +197,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rlibc" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" - [[package]] name = "rsdp" version = "1.0.0" @@ -244,9 +237,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" +checksum = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228" dependencies = [ "proc-macro2", "quote", @@ -343,8 +336,8 @@ dependencies = [ [[package]] name = "x86_64" -version = "0.12.1" -source = "git+https://github.com/rust-osdev/x86_64.git?branch=access-level-4-table#b2868c966547fab1469ddd86e85f5611bc18797e" +version = "0.12.2" +source = "git+https://github.com/rust-osdev/x86_64.git?branch=access-level-4-table#4f42c9bf723b7a738e03e8a496420c26c11ba8e2" dependencies = [ "bit_field 0.9.0", "bitflags", diff --git a/Cargo.toml b/Cargo.toml index dc6216f2..94cccd27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,6 @@ xmas-elf = { version = "0.6.2", optional = true } x86_64 = { git = "https://github.com/rust-osdev/x86_64.git", branch = "access-level-4-table", optional = true } usize_conversions = { version = "0.2.0", optional = true } bit_field = { version = "0.10.0", optional = true } -rlibc = { version = "1.0.0", optional = true } log = { version = "0.4.8", optional = true } uefi = { version = "0.6.0", optional = true } argh = { version = "0.1.3", optional = true } @@ -62,8 +61,8 @@ toml = { version = "0.5.1", optional = true } default = [] builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json"] runner = ["anyhow"] -bios_bin = ["binary", "rlibc", "vga_320x200", "rsdp"] -uefi_bin = ["binary", "rlibc", "uefi", "font8x8"] +bios_bin = ["binary", "vga_320x200", "rsdp"] +uefi_bin = ["binary", "uefi", "font8x8"] binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", "log", "conquer-once", "spinning_top"] vga_320x200 = ["font8x8"] recursive_page_table = [] diff --git a/src/bin/bios.rs b/src/bin/bios.rs index c5f09605..e644e62d 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -9,8 +9,6 @@ #[cfg(not(target_os = "none"))] compile_error!("The bootloader crate must be compiled for the `x86_64-bootloader.json` target"); -extern crate rlibc; - use bootloader::{binary::SystemInfo, boot_info::FrameBufferInfo}; use core::panic::PanicInfo; use core::slice; diff --git a/src/bin/builder.rs b/src/bin/builder.rs index ca740917..10c43c32 100644 --- a/src/bin/builder.rs +++ b/src/bin/builder.rs @@ -81,6 +81,7 @@ fn main() -> anyhow::Result<()> { cmd.arg("--features") .arg(args.features.join(" ") + " uefi_bin"); cmd.arg("-Zbuild-std=core"); + cmd.arg("-Zbuild-std-features=compiler-builtins-mem"); if let Some(target_dir) = &args.target_dir { cmd.arg("--target-dir").arg(target_dir); } @@ -101,6 +102,7 @@ fn main() -> anyhow::Result<()> { cmd.arg("--features") .arg(args.features.join(" ") + " bios_bin"); cmd.arg("-Zbuild-std=core"); + cmd.arg("-Zbuild-std-features=compiler-builtins-mem"); if let Some(target_dir) = &args.target_dir { cmd.arg("--target-dir").arg(target_dir); } diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 32a45a78..8b3f057e 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -15,8 +15,6 @@ static KERNEL: PageAligned<[u8; KERNEL_SIZE]> = PageAligned(KERNEL_BYTES); #[repr(align(4096))] struct PageAligned(T); -extern crate rlibc; - use bootloader::{ binary::{legacy_memory_region::LegacyFrameAllocator, SystemInfo}, boot_info::FrameBufferInfo, From 119beee93ce9709422856d6c0570c067c915d7c7 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 30 Oct 2020 13:04:02 +0100 Subject: [PATCH 104/174] Don't add support for enabling SSE for now --- Cargo.toml | 1 - src/bin/bios.rs | 3 --- 2 files changed, 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 94cccd27..ebe6cec6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,6 @@ binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", vga_320x200 = ["font8x8"] recursive_page_table = [] map_physical_memory = [] -sse = ["bit_field"] [profile.dev] panic = "abort" diff --git a/src/bin/bios.rs b/src/bin/bios.rs index e644e62d..04a53bd4 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -138,9 +138,6 @@ fn bootloader_main( // Make sure that the kernel respects the write-protection bits, even when in ring 0. enable_write_protect_bit(); - #[cfg(feature = "sse")] - sse::enable_sse(); - */ } From 3e49682ad186b9e32a7c9b1afe310ff583705d80 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 30 Oct 2020 13:06:45 +0100 Subject: [PATCH 105/174] Make the kernel respect the write protection bits by default --- src/bin/bios.rs | 11 ----------- src/binary/mod.rs | 7 +++++++ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/bin/bios.rs b/src/bin/bios.rs index 04a53bd4..d0a77278 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -133,12 +133,6 @@ fn bootloader_main( page_tables, system_info, ); - - /* - // Make sure that the kernel respects the write-protection bits, even when in ring 0. - enable_write_protect_bit(); - - */ } fn init_logger(framebuffer_start: PhysAddr, framebuffer_size: usize) -> FrameBufferInfo { @@ -197,11 +191,6 @@ fn create_page_tables( } } -fn enable_write_protect_bit() { - use x86_64::registers::control::{Cr0, Cr0Flags}; - unsafe { Cr0::update(|cr0| *cr0 |= Cr0Flags::WRITE_PROTECT) }; -} - fn detect_rsdp() -> Option { use core::ptr::NonNull; use rsdp::{ diff --git a/src/binary/mod.rs b/src/binary/mod.rs index dd7667b7..940673af 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -97,6 +97,8 @@ where // Enable support for the no-execute bit in page tables. enable_nxe_bit(); + // Make the kernel respect the write-protection bits even when in ring 0 by default + enable_write_protect_bit(); let (entry_point, mut used_entries) = load_kernel::load_kernel(kernel_bytes, kernel_page_table, frame_allocator) @@ -412,3 +414,8 @@ fn enable_nxe_bit() { use x86_64::registers::control::{Efer, EferFlags}; unsafe { Efer::update(|efer| *efer |= EferFlags::NO_EXECUTE_ENABLE) } } + +fn enable_write_protect_bit() { + use x86_64::registers::control::{Cr0, Cr0Flags}; + unsafe { Cr0::update(|cr0| *cr0 |= Cr0Flags::WRITE_PROTECT) }; +} From 3dbe8d41432fb8d09a74762dfadbdc1d63a9f054 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 30 Oct 2020 13:55:20 +0100 Subject: [PATCH 106/174] Removed unneeded `>= 0` check in build script --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 0ee50253..28e3d091 100644 --- a/build.rs +++ b/build.rs @@ -274,7 +274,7 @@ mod binary { ("recursive-page-table-index", Value::Integer(i)) => { let index = match u16::try_from(i) { - Ok(index) if index >= 0 && index < 512 => index, + Ok(index) if index < 512 => index, _other => panic!( "`recursive-page-table-index` must be a number between 0 and 512" ), From 15ce0e4083a94f571276566fb56f0500c61642f7 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 30 Oct 2020 13:58:16 +0100 Subject: [PATCH 107/174] Report UEFI/BIOS memory type IDs in memory map Also: Reuse some UEFI regions that are no longer needed as `Usable` before switching to kernel. --- src/binary/bios/memory_descriptor.rs | 28 +++---------- src/binary/legacy_memory_region.rs | 63 +++++++++++++++++----------- src/binary/uefi/memory_descriptor.rs | 9 ++-- src/memory_map.rs | 14 ++++++- 4 files changed, 63 insertions(+), 51 deletions(-) diff --git a/src/binary/bios/memory_descriptor.rs b/src/binary/bios/memory_descriptor.rs index a3dc8095..cb5b058f 100644 --- a/src/binary/bios/memory_descriptor.rs +++ b/src/binary/bios/memory_descriptor.rs @@ -1,4 +1,4 @@ -use crate::binary::legacy_memory_region::LegacyMemoryRegion; +use crate::{binary::legacy_memory_region::LegacyMemoryRegion, memory_map::MemoryRegionKind}; use x86_64::PhysAddr; impl LegacyMemoryRegion for E820MemoryRegion { @@ -10,8 +10,11 @@ impl LegacyMemoryRegion for E820MemoryRegion { self.len } - fn usable(&self) -> bool { - self.region_type == 1 + fn kind(&self) -> MemoryRegionKind { + match self.region_type { + 1 => MemoryRegionKind::Usable, + other => MemoryRegionKind::UnknownBios(other), + } } fn set_start(&mut self, new_start: PhysAddr) { @@ -28,22 +31,3 @@ pub struct E820MemoryRegion { pub region_type: u32, pub acpi_extended_attributes: u32, } - -/* -impl From for MemoryRegion { - fn from(region: E820MemoryRegion) -> MemoryRegion { - let region_type = match region.region_type { - 1 => MemoryRegionType::Usable, - 2 => MemoryRegionType::Reserved, - 3 => MemoryRegionType::AcpiReclaimable, - 4 => MemoryRegionType::AcpiNvs, - 5 => MemoryRegionType::BadMemory, - t => panic!("invalid region type {}", t), - }; - MemoryRegion { - range: FrameRange::new(region.start_addr, region.start_addr + region.len), - region_type, - } - } -} -*/ diff --git a/src/binary/legacy_memory_region.rs b/src/binary/legacy_memory_region.rs index edde91c5..4b4fb0f7 100644 --- a/src/binary/legacy_memory_region.rs +++ b/src/binary/legacy_memory_region.rs @@ -1,4 +1,4 @@ -use crate::memory_map::MemoryRegion; +use crate::memory_map::{MemoryRegion, MemoryRegionKind}; use core::mem::MaybeUninit; use x86_64::{ structures::paging::{FrameAllocator, PhysFrame, Size4KiB}, @@ -8,7 +8,7 @@ use x86_64::{ pub trait LegacyMemoryRegion: Copy + core::fmt::Debug { fn start(&self) -> PhysAddr; fn len(&self) -> u64; - fn usable(&self) -> bool; + fn kind(&self) -> MemoryRegionKind; fn set_start(&mut self, new_start: PhysAddr); } @@ -76,34 +76,47 @@ where self, regions: &mut [MaybeUninit], ) -> &mut [MemoryRegion] { - use crate::memory_map::MemoryRegionKind; - let mut next_index = 0; for mut descriptor in self.original { let end = descriptor.start() + descriptor.len(); let next_free = self.next_frame.start_address(); - let kind = if descriptor.usable() { - if end <= next_free { - MemoryRegionKind::Bootloader - } else if descriptor.start() >= next_free { - MemoryRegionKind::Usable - } else { - // part of the region is used -> add is separately - let used_region = MemoryRegion { - start: descriptor.start().as_u64(), - end: next_free.as_u64(), - kind: MemoryRegionKind::Bootloader, - }; - Self::add_region(used_region, regions, &mut next_index) - .expect("Failed to add memory region"); - - // add unused part normally - descriptor.set_start(next_free); - MemoryRegionKind::Usable + let kind = match descriptor.kind() { + MemoryRegionKind::Usable => { + if end <= next_free { + MemoryRegionKind::Bootloader + } else if descriptor.start() >= next_free { + MemoryRegionKind::Usable + } else { + // part of the region is used -> add is separately + let used_region = MemoryRegion { + start: descriptor.start().as_u64(), + end: next_free.as_u64(), + kind: MemoryRegionKind::Bootloader, + }; + Self::add_region(used_region, regions, &mut next_index) + .expect("Failed to add memory region"); + + // add unused part normally + descriptor.set_start(next_free); + MemoryRegionKind::Usable + } + } + // some mappings created by the UEFI firmware become usable again at this point + #[cfg(feature = "uefi_bin")] + MemoryRegionKind::UnknownUefi(other) => { + use uefi::table::boot::MemoryType as M; + match M::custom(other) { + M::LOADER_CODE + | M::LOADER_DATA + | M::BOOT_SERVICES_CODE + | M::BOOT_SERVICES_DATA + | M::RUNTIME_SERVICES_CODE + | M::RUNTIME_SERVICES_DATA => MemoryRegionKind::Usable, + other => MemoryRegionKind::UnknownUefi(other.0), + } } - } else { - MemoryRegionKind::Reserved // FIXME more types + other => other, }; let region = MemoryRegion { @@ -152,7 +165,7 @@ where // find next suitable descriptor while let Some(descriptor) = self.memory_map.next() { - if !descriptor.usable() { + if descriptor.kind() != MemoryRegionKind::Usable { continue; } if let Some(frame) = self.allocate_frame_from_descriptor(descriptor) { diff --git a/src/binary/uefi/memory_descriptor.rs b/src/binary/uefi/memory_descriptor.rs index cb8370b9..ff6de999 100644 --- a/src/binary/uefi/memory_descriptor.rs +++ b/src/binary/uefi/memory_descriptor.rs @@ -1,4 +1,4 @@ -use crate::binary::legacy_memory_region::LegacyMemoryRegion; +use crate::{binary::legacy_memory_region::LegacyMemoryRegion, memory_map::MemoryRegionKind}; use uefi::table::boot::{MemoryDescriptor, MemoryType}; use x86_64::PhysAddr; @@ -13,8 +13,11 @@ impl<'a> LegacyMemoryRegion for MemoryDescriptor { self.page_count * PAGE_SIZE } - fn usable(&self) -> bool { - self.ty == MemoryType::CONVENTIONAL + fn kind(&self) -> MemoryRegionKind { + match self.ty { + MemoryType::CONVENTIONAL => MemoryRegionKind::Usable, + other => MemoryRegionKind::UnknownUefi(other.0), + } } fn set_start(&mut self, new_start: PhysAddr) { diff --git a/src/memory_map.rs b/src/memory_map.rs index 1e78d454..d24a8169 100644 --- a/src/memory_map.rs +++ b/src/memory_map.rs @@ -16,8 +16,20 @@ impl MemoryRegion { } #[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[non_exhaustive] pub enum MemoryRegionKind { + /// Unused conventional memory, can be used by the kernel. Usable, - Reserved, + /// Memory mappings created by the bootloader, including the kernel and boot info mappings. + /// + /// This memory should _not_ be used by the kernel. Bootloader, + /// An unknown memory region reported by the UEFI firmware. + /// + /// This should only be used if the UEFI memory type is known as usable. + UnknownUefi(u32), + /// An unknown memory region reported by the BIOS firmware. + /// + /// This should only be used if the BIOS memory type is known as usable. + UnknownBios(u32), } From bf68296d97f5ecac51ffc6480e53c478860bd1aa Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 30 Oct 2020 13:59:03 +0100 Subject: [PATCH 108/174] Remove some unused imports --- src/lib.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 363c5bbd..35d88029 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,12 +17,6 @@ pub use crate::boot_info::BootInfo; -#[cfg(feature = "bios_bin")] -use x86_64::{ - structures::paging::{frame::PhysFrameRange, PhysFrame}, - PhysAddr, -}; - pub mod bootinfo; pub mod config; From 76df3e57f33dd4884f32738afd6fd54a41d23911 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 30 Oct 2020 14:34:27 +0100 Subject: [PATCH 109/174] Clarify that the `BootInfo` struct is _not_ FFI-safe --- src/boot_info.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/boot_info.rs b/src/boot_info.rs index 44b9c9a0..3b84b029 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -2,7 +2,6 @@ use crate::memory_map::MemoryRegion; use core::slice; #[derive(Debug)] -#[repr(C)] pub struct BootInfo { pub memory_regions: &'static mut [MemoryRegion], pub framebuffer: Option, @@ -13,7 +12,6 @@ pub struct BootInfo { } #[derive(Debug)] -#[repr(C)] pub struct FrameBuffer { pub(crate) buffer_start: u64, pub(crate) buffer_byte_len: usize, @@ -35,7 +33,6 @@ impl FrameBuffer { } #[derive(Debug, Clone, Copy)] -#[repr(C)] pub struct FrameBufferInfo { pub byte_len: usize, pub horizontal_resolution: usize, @@ -46,7 +43,6 @@ pub struct FrameBufferInfo { } #[derive(Debug, Clone, Copy)] -#[repr(C)] #[non_exhaustive] pub enum PixelFormat { RGB, @@ -54,4 +50,8 @@ pub enum PixelFormat { U8, } +/// Check that the _pointer_ is FFI-safe. +/// +/// Note that the `BootInfo` struct is not FFI-safe, so it needs to be compiled by the same Rust +/// compiler as the kernel in order to be safely accessed. extern "C" fn _assert_ffi(_boot_info: &'static mut BootInfo) {} From 4c7f3f2e7ee9f67ff08650b4cf43ed17f6fa9099 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 30 Oct 2020 14:57:53 +0100 Subject: [PATCH 110/174] Add support for TLS templates --- src/binary/load_kernel.rs | 32 +++++++++++++++++++++++--------- src/binary/mod.rs | 7 +++++-- src/boot_info.rs | 22 ++++++++++++++++++++++ 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/binary/load_kernel.rs b/src/binary/load_kernel.rs index 8d055c61..ca641c3e 100644 --- a/src/binary/load_kernel.rs +++ b/src/binary/load_kernel.rs @@ -1,4 +1,7 @@ -use crate::binary::{level_4_entries::UsedLevel4Entries, PAGE_SIZE}; +use crate::{ + binary::{level_4_entries::UsedLevel4Entries, PAGE_SIZE}, + boot_info::TlsTemplate, +}; use x86_64::{ align_up, structures::paging::{ @@ -54,12 +57,19 @@ where Ok(loader) } - fn load_segments(&mut self) -> Result<(), &'static str> { + fn load_segments(&mut self) -> Result, &'static str> { + let mut tls_template = None; for program_header in self.elf_file.program_iter() { program::sanity_check(program_header, &self.elf_file)?; match program_header.get_type()? { Type::Load => self.inner.handle_load_segment(program_header)?, - Type::Tls => self.inner.handle_tls_segment(program_header)?, + Type::Tls => { + if tls_template.is_none() { + tls_template = Some(self.inner.handle_tls_segment(program_header)?); + } else { + return Err("multiple TLS segments not supported"); + } + } Type::Null | Type::Dynamic | Type::Interp @@ -71,7 +81,7 @@ where | Type::ProcessorSpecific(_) => {} } } - Ok(()) + Ok(tls_template) } fn entry_point(&self) -> VirtAddr { @@ -250,8 +260,12 @@ where Ok(()) } - fn handle_tls_segment(&mut self, segment: ProgramHeader) -> Result<(), &'static str> { - todo!() + fn handle_tls_segment(&mut self, segment: ProgramHeader) -> Result { + Ok(TlsTemplate { + start_addr: segment.virtual_addr(), + mem_size: segment.mem_size(), + file_size: segment.file_size(), + }) } } @@ -259,10 +273,10 @@ pub fn load_kernel( bytes: &[u8], page_table: &mut impl MapperAllSizes, frame_allocator: &mut impl FrameAllocator, -) -> Result<(VirtAddr, UsedLevel4Entries), &'static str> { +) -> Result<(VirtAddr, Option, UsedLevel4Entries), &'static str> { let mut loader = Loader::new(bytes, page_table, frame_allocator)?; - loader.load_segments()?; + let tls_template = loader.load_segments()?; let used_entries = loader.used_level_4_entries(); - Ok((loader.entry_point(), used_entries)) + Ok((loader.entry_point(), tls_template, used_entries)) } diff --git a/src/binary/mod.rs b/src/binary/mod.rs index 940673af..bb65700f 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -1,5 +1,5 @@ use crate::binary::legacy_memory_region::{LegacyFrameAllocator, LegacyMemoryRegion}; -use crate::boot_info::{BootInfo, FrameBuffer, FrameBufferInfo}; +use crate::boot_info::{BootInfo, FrameBuffer, FrameBufferInfo, TlsTemplate}; use crate::memory_map::MemoryRegion; use core::{ mem::{self, MaybeUninit}, @@ -100,7 +100,7 @@ where // Make the kernel respect the write-protection bits even when in ring 0 by default enable_write_protect_bit(); - let (entry_point, mut used_entries) = + let (entry_point, tls_template, mut used_entries) = load_kernel::load_kernel(kernel_bytes, kernel_page_table, frame_allocator) .expect("no entry point"); log::info!("Entry point at: {:#x}", entry_point.as_u64()); @@ -197,6 +197,7 @@ where used_entries, physical_memory_offset, recursive_index, + tls_template, } } @@ -207,6 +208,7 @@ pub struct Mappings { pub framebuffer: Option, pub physical_memory_offset: Option, pub recursive_index: Option, + pub tls_template: Option, } /// Allocates and initializes the boot info struct and the memory map @@ -284,6 +286,7 @@ where physical_memory_offset: mappings.physical_memory_offset.map(VirtAddr::as_u64), recursive_index: mappings.recursive_index.map(Into::into), rsdp_addr: system_info.rsdp_addr.map(|addr| addr.as_u64()), + tls_template: mappings.tls_template, _non_exhaustive: (), }); diff --git a/src/boot_info.rs b/src/boot_info.rs index 3b84b029..9dec3a03 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -8,6 +8,7 @@ pub struct BootInfo { pub physical_memory_offset: Option, pub recursive_index: Option, pub rsdp_addr: Option, + pub tls_template: Option, pub(crate) _non_exhaustive: (), } @@ -50,6 +51,27 @@ pub enum PixelFormat { U8, } +/// Information about the thread local storage (TLS) template. +/// +/// This template can be used to set up thread local storage for threads. For +/// each thread, a new memory location of size `mem_size` must be initialized. +/// Then the first `file_size` bytes of this template needs to be copied to the +/// location. The additional `mem_size - file_size` bytes must be initialized with +/// zero. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct TlsTemplate { + /// The virtual start address of the thread local storage template. + pub start_addr: u64, + /// The number of data bytes in the template. + /// + /// Corresponds to the length of the `.tdata` section. + pub file_size: u64, + /// The total number of bytes that the TLS segment should have in memory. + /// + /// Corresponds to the combined length of the `.tdata` and `.tbss` sections. + pub mem_size: u64, +} + /// Check that the _pointer_ is FFI-safe. /// /// Note that the `BootInfo` struct is not FFI-safe, so it needs to be compiled by the same Rust From e0b68c68e232c320cefb95a12f3dbc4350e53b79 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 30 Oct 2020 15:08:32 +0100 Subject: [PATCH 111/174] Delete the old `bootinfo` module --- src/boot_info.rs | 36 ++++++ src/bootinfo/memory_map.rs | 236 ------------------------------------- src/bootinfo/mod.rs | 119 ------------------- src/lib.rs | 1 - 4 files changed, 36 insertions(+), 356 deletions(-) delete mode 100644 src/bootinfo/memory_map.rs delete mode 100644 src/bootinfo/mod.rs diff --git a/src/boot_info.rs b/src/boot_info.rs index 9dec3a03..9c8e861b 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -1,13 +1,49 @@ use crate::memory_map::MemoryRegion; use core::slice; +/// This structure represents the information that the bootloader passes to the kernel. +/// +/// The information is passed as an argument to the entry point. The entry point function must +/// have the following signature: +/// +/// ```ignore +/// pub extern "C" fn(boot_info: &'static BootInfo) -> !; +/// ``` +/// +/// Note that no type checking occurs for the entry point function, so be careful to +/// use the correct argument types. To ensure that the entry point function has the correct +/// signature, use the [`entry_point`] macro. #[derive(Debug)] pub struct BootInfo { + /// A map of the physical memory regions of the underlying machine. + /// + /// The bootloader queries this information from the BIOS/UEFI firmware and translates this + /// information to Rust types. It also marks any memory regions that the bootloader uses in + /// the memory map before passing it to the kernel. Regions marked as usable can be freely + /// used by the kernel. pub memory_regions: &'static mut [MemoryRegion], + /// Information about the framebuffer for screen output if available. pub framebuffer: Option, + /// The virtual address at which the mapping of the physical memory starts. + /// + /// Physical addresses can be converted to virtual addresses by adding this offset to them. + /// + /// The mapping of the physical memory allows to access arbitrary physical frames. Accessing + /// frames that are also mapped at other virtual addresses can easily break memory safety and + /// cause undefined behavior. Only frames reported as `USABLE` by the memory map in the `BootInfo` + /// can be safely accessed. + /// + /// Only available if the `map-physical-memory` config option is enabled. pub physical_memory_offset: Option, + /// The virtual address of the recursively mapped level 4 page table. + /// + /// Only available if the `map-page-table-recursively` config option is enabled. pub recursive_index: Option, + /// The address of the `RSDP` data structure, which can be use to find the ACPI tables. + /// + /// This field is `None` if no `RSDP` was found (for BIOS) or reported (for UEFI). pub rsdp_addr: Option, + /// The thread local storage (TLS) template of the kernel executable, if present. pub tls_template: Option, pub(crate) _non_exhaustive: (), } diff --git a/src/bootinfo/memory_map.rs b/src/bootinfo/memory_map.rs deleted file mode 100644 index 134188ed..00000000 --- a/src/bootinfo/memory_map.rs +++ /dev/null @@ -1,236 +0,0 @@ -use core::fmt; -use core::ops::{Deref, DerefMut}; - -const PAGE_SIZE: u64 = 4096; - -const MAX_MEMORY_MAP_SIZE: usize = 64; - -/// A map of the physical memory regions of the underlying machine. -#[repr(C)] -pub struct MemoryMap { - entries: [MemoryRegion; MAX_MEMORY_MAP_SIZE], - // u64 instead of usize so that the structure layout is platform - // independent - next_entry_index: u64, -} - -#[doc(hidden)] -#[allow(clippy::new_without_default)] -impl MemoryMap { - pub fn new() -> Self { - MemoryMap { - entries: [MemoryRegion::empty(); MAX_MEMORY_MAP_SIZE], - next_entry_index: 0, - } - } - - pub fn add_region(&mut self, region: MemoryRegion) { - assert!( - self.next_entry_index() < MAX_MEMORY_MAP_SIZE, - "too many memory regions in memory map" - ); - self.entries[self.next_entry_index()] = region; - self.next_entry_index += 1; - self.sort(); - } - - pub fn sort(&mut self) { - use core::cmp::Ordering; - - self.entries.sort_unstable_by(|r1, r2| { - if r1.range.is_empty() { - Ordering::Greater - } else if r2.range.is_empty() { - Ordering::Less - } else { - let ordering = r1 - .range - .start_frame_number - .cmp(&r2.range.start_frame_number); - - if ordering == Ordering::Equal { - r1.range.end_frame_number.cmp(&r2.range.end_frame_number) - } else { - ordering - } - } - }); - if let Some(first_zero_index) = self.entries.iter().position(|r| r.range.is_empty()) { - self.next_entry_index = first_zero_index as u64; - } - } - - fn next_entry_index(&self) -> usize { - self.next_entry_index as usize - } -} - -impl Deref for MemoryMap { - type Target = [MemoryRegion]; - - fn deref(&self) -> &Self::Target { - &self.entries[0..self.next_entry_index()] - } -} - -impl DerefMut for MemoryMap { - fn deref_mut(&mut self) -> &mut Self::Target { - let next_index = self.next_entry_index(); - &mut self.entries[0..next_index] - } -} - -impl fmt::Debug for MemoryMap { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -/// Represents a region of physical memory. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub struct MemoryRegion { - /// The range of frames that belong to the region. - pub range: FrameRange, - /// The type of the region. - pub region_type: MemoryRegionType, -} - -#[doc(hidden)] -impl MemoryRegion { - pub fn empty() -> Self { - MemoryRegion { - range: FrameRange { - start_frame_number: 0, - end_frame_number: 0, - }, - region_type: MemoryRegionType::Empty, - } - } -} - -/// A range of frames with an exclusive upper bound. -#[derive(Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub struct FrameRange { - /// The frame _number_ of the first 4KiB frame in the region. - /// - /// To convert this frame number to a physical address, multiply it with the - /// page size (4KiB). - pub start_frame_number: u64, - /// The frame _number_ of the first 4KiB frame that does no longer belong to the region. - /// - /// To convert this frame number to a physical address, multiply it with the - /// page size (4KiB). - pub end_frame_number: u64, -} - -impl FrameRange { - /// Create a new FrameRange from the passed start_addr and end_addr. - /// - /// The end_addr is exclusive. - pub fn new(start_addr: u64, end_addr: u64) -> Self { - let last_byte = end_addr - 1; - FrameRange { - start_frame_number: start_addr / PAGE_SIZE, - end_frame_number: (last_byte / PAGE_SIZE) + 1, - } - } - - /// Returns true if the frame range contains no frames. - pub fn is_empty(&self) -> bool { - self.start_frame_number == self.end_frame_number - } - - /// Returns the physical start address of the memory region. - pub fn start_addr(&self) -> u64 { - self.start_frame_number * PAGE_SIZE - } - - /// Returns the physical end address of the memory region. - pub fn end_addr(&self) -> u64 { - self.end_frame_number * PAGE_SIZE - } -} - -impl fmt::Debug for FrameRange { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "FrameRange({:#x}..{:#x})", - self.start_addr(), - self.end_addr() - ) - } -} - -/// Represents possible types for memory regions. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub enum MemoryRegionType { - /// Unused memory, can be freely used by the kernel. - Usable, - /// Memory that is already in use. - InUse, - /// Memory reserved by the hardware. Not usable. - Reserved, - /// ACPI reclaimable memory - AcpiReclaimable, - /// ACPI NVS memory - AcpiNvs, - /// Area containing bad memory - BadMemory, - /// Memory used for loading the kernel. - Kernel, - /// Memory used for the kernel stack. - KernelStack, - /// Memory used for creating page tables. - PageTable, - /// Memory used by the bootloader. - Bootloader, - /// Frame at address zero. - /// - /// (shouldn't be used because it's easy to make mistakes related to null pointers) - FrameZero, - /// An empty region with size 0 - Empty, - /// Memory used for storing the boot information. - BootInfo, - /// Memory used for storing the supplied package - Package, - /// Additional variant to ensure that we can add more variants in the future without - /// breaking backwards compatibility. - #[doc(hidden)] - NonExhaustive, -} - -#[doc(hidden)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub struct E820MemoryRegion { - pub start_addr: u64, - pub len: u64, - pub region_type: u32, - pub acpi_extended_attributes: u32, -} - -impl From for MemoryRegion { - fn from(region: E820MemoryRegion) -> MemoryRegion { - let region_type = match region.region_type { - 1 => MemoryRegionType::Usable, - 2 => MemoryRegionType::Reserved, - 3 => MemoryRegionType::AcpiReclaimable, - 4 => MemoryRegionType::AcpiNvs, - 5 => MemoryRegionType::BadMemory, - t => panic!("invalid region type {}", t), - }; - MemoryRegion { - range: FrameRange::new(region.start_addr, region.start_addr + region.len), - region_type, - } - } -} - -extern "C" { - fn _improper_ctypes_check_memory_map(_memory_map: MemoryMap); -} diff --git a/src/bootinfo/mod.rs b/src/bootinfo/mod.rs deleted file mode 100644 index 51327151..00000000 --- a/src/bootinfo/mod.rs +++ /dev/null @@ -1,119 +0,0 @@ -//! Provides boot information to the kernel. - -#![deny(improper_ctypes)] - -pub use self::memory_map::*; - -mod memory_map; -/// This structure represents the information that the bootloader passes to the kernel. -/// -/// The information is passed as an argument to the entry point: -/// -/// ```ignore -/// pub extern "C" fn _start(boot_info: &'static BootInfo) -> ! { -/// // […] -/// } -/// ``` -/// -/// Note that no type checking occurs for the entry point function, so be careful to -/// use the correct argument types. To ensure that the entry point function has the correct -/// signature, use the [`entry_point`] macro. -#[derive(Debug)] -#[repr(C)] -pub struct BootInfo { - /// A map of the physical memory regions of the underlying machine. - /// - /// The bootloader queries this information from the BIOS/UEFI firmware and translates this - /// information to Rust types. It also marks any memory regions that the bootloader uses in - /// the memory map before passing it to the kernel. Regions marked as usable can be freely - /// used by the kernel. - pub memory_map: MemoryMap, - /// The virtual address of the recursively mapped level 4 page table. - #[cfg(feature = "recursive_page_table")] - pub recursive_page_table_addr: u64, - /// The offset into the virtual address space where the physical memory is mapped. - /// - /// Physical addresses can be converted to virtual addresses by adding this offset to them. - /// - /// The mapping of the physical memory allows to access arbitrary physical frames. Accessing - /// frames that are also mapped at other virtual addresses can easily break memory safety and - /// cause undefined behavior. Only frames reported as `USABLE` by the memory map in the `BootInfo` - /// can be safely accessed. - #[cfg(feature = "map_physical_memory")] - pub physical_memory_offset: u64, - tls_template: TlsTemplate, - _non_exhaustive: u8, // `()` is not FFI safe -} - -impl BootInfo { - /// Create a new boot information structure. This function is only for internal purposes. - #[allow(unused_variables)] - #[doc(hidden)] - pub fn new( - memory_map: MemoryMap, - tls_template: Option, - recursive_page_table_addr: u64, - physical_memory_offset: u64, - ) -> Self { - let tls_template = tls_template.unwrap_or(TlsTemplate { - start_addr: 0, - file_size: 0, - mem_size: 0, - }); - BootInfo { - memory_map, - tls_template, - #[cfg(feature = "recursive_page_table")] - recursive_page_table_addr, - #[cfg(feature = "map_physical_memory")] - physical_memory_offset, - _non_exhaustive: 0, - } - } - - /// Returns information about the thread local storage segment of the kernel. - /// - /// Returns `None` if the kernel has no thread local storage segment. - /// - /// (The reason this is a method instead of a normal field is that `Option` - /// is not FFI-safe.) - pub fn tls_template(&self) -> Option { - if self.tls_template.mem_size > 0 { - Some(self.tls_template) - } else { - None - } - } - - /// Returns the index into the page tables that recursively maps the page tables themselves. - #[cfg(feature = "recursive_page_table")] - pub fn recursive_index(&self) -> u16 { - ((self.recursive_page_table_addr >> 12) & 0x1FF) as u16 - } -} - -/// Information about the thread local storage (TLS) template. -/// -/// This template can be used to set up thread local storage for threads. For -/// each thread, a new memory location of size `mem_size` must be initialized. -/// Then the first `file_size` bytes of this template needs to be copied to the -/// location. The additional `mem_size - file_size` bytes must be initialized with -/// zero. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub struct TlsTemplate { - /// The virtual start address of the thread local storage template. - pub start_addr: u64, - /// The number of data bytes in the template. - /// - /// Corresponds to the length of the `.tdata` section. - pub file_size: u64, - /// The total number of bytes that the TLS segment should have in memory. - /// - /// Corresponds to the combined length of the `.tdata` and `.tbss` sections. - pub mem_size: u64, -} - -extern "C" { - fn _improper_ctypes_check_bootinfo(_boot_info: BootInfo); -} diff --git a/src/lib.rs b/src/lib.rs index 35d88029..566bd819 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,6 @@ pub use crate::boot_info::BootInfo; -pub mod bootinfo; pub mod config; pub mod boot_info; From cbfc6be6207fdf9d07d22a6ba48e1dcdd58823f5 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 30 Oct 2020 15:10:37 +0100 Subject: [PATCH 112/174] Rename `memory_map` module to `memory_region` --- src/binary/bios/memory_descriptor.rs | 2 +- src/binary/legacy_memory_region.rs | 2 +- src/binary/mod.rs | 2 +- src/binary/uefi/memory_descriptor.rs | 2 +- src/boot_info.rs | 2 +- src/lib.rs | 2 +- src/{memory_map.rs => memory_region.rs} | 0 7 files changed, 6 insertions(+), 6 deletions(-) rename src/{memory_map.rs => memory_region.rs} (100%) diff --git a/src/binary/bios/memory_descriptor.rs b/src/binary/bios/memory_descriptor.rs index cb5b058f..519ff65f 100644 --- a/src/binary/bios/memory_descriptor.rs +++ b/src/binary/bios/memory_descriptor.rs @@ -1,4 +1,4 @@ -use crate::{binary::legacy_memory_region::LegacyMemoryRegion, memory_map::MemoryRegionKind}; +use crate::{binary::legacy_memory_region::LegacyMemoryRegion, memory_region::MemoryRegionKind}; use x86_64::PhysAddr; impl LegacyMemoryRegion for E820MemoryRegion { diff --git a/src/binary/legacy_memory_region.rs b/src/binary/legacy_memory_region.rs index 4b4fb0f7..c2e51ddb 100644 --- a/src/binary/legacy_memory_region.rs +++ b/src/binary/legacy_memory_region.rs @@ -1,4 +1,4 @@ -use crate::memory_map::{MemoryRegion, MemoryRegionKind}; +use crate::memory_region::{MemoryRegion, MemoryRegionKind}; use core::mem::MaybeUninit; use x86_64::{ structures::paging::{FrameAllocator, PhysFrame, Size4KiB}, diff --git a/src/binary/mod.rs b/src/binary/mod.rs index bb65700f..a73230c8 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -1,6 +1,6 @@ use crate::binary::legacy_memory_region::{LegacyFrameAllocator, LegacyMemoryRegion}; use crate::boot_info::{BootInfo, FrameBuffer, FrameBufferInfo, TlsTemplate}; -use crate::memory_map::MemoryRegion; +use crate::memory_region::MemoryRegion; use core::{ mem::{self, MaybeUninit}, slice, diff --git a/src/binary/uefi/memory_descriptor.rs b/src/binary/uefi/memory_descriptor.rs index ff6de999..6be0d208 100644 --- a/src/binary/uefi/memory_descriptor.rs +++ b/src/binary/uefi/memory_descriptor.rs @@ -1,4 +1,4 @@ -use crate::{binary::legacy_memory_region::LegacyMemoryRegion, memory_map::MemoryRegionKind}; +use crate::{binary::legacy_memory_region::LegacyMemoryRegion, memory_region::MemoryRegionKind}; use uefi::table::boot::{MemoryDescriptor, MemoryType}; use x86_64::PhysAddr; diff --git a/src/boot_info.rs b/src/boot_info.rs index 9c8e861b..d95ced10 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -1,4 +1,4 @@ -use crate::memory_map::MemoryRegion; +use crate::memory_region::MemoryRegion; use core::slice; /// This structure represents the information that the bootloader passes to the kernel. diff --git a/src/lib.rs b/src/lib.rs index 566bd819..2f56fc80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ pub use crate::boot_info::BootInfo; pub mod config; pub mod boot_info; -pub mod memory_map; +pub mod memory_region; #[cfg(feature = "binary")] pub mod binary; diff --git a/src/memory_map.rs b/src/memory_region.rs similarity index 100% rename from src/memory_map.rs rename to src/memory_region.rs From 464e80e110d0d43d5f9880695ed013ca662dc530 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 30 Oct 2020 15:34:24 +0100 Subject: [PATCH 113/174] Fix test framework --- Cargo.lock | 9 +++++---- tests/test_kernels/default_settings/Cargo.toml | 2 +- .../test_kernels/default_settings/src/bin/basic_boot.rs | 2 +- .../default_settings/src/bin/should_panic.rs | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 84fcc0f8..b2bcc81e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,7 +72,7 @@ dependencies = [ "toml", "uefi", "usize_conversions", - "x86_64 0.12.2", + "x86_64 0.12.2 (git+https://github.com/rust-osdev/x86_64.git?branch=access-level-4-table)", "xmas-elf", ] @@ -143,7 +143,7 @@ name = "kernel" version = "0.1.0" dependencies = [ "bootloader", - "x86_64 0.11.2", + "x86_64 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -327,8 +327,9 @@ checksum = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" [[package]] name = "x86_64" -version = "0.11.2" -source = "git+https://github.com/rust-osdev/x86_64.git?branch=nop#0b2bf80eeb22f9e8452b604624704e52d895b6d0" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5b4b42dbabe13b69023e1a1407d395f1a1a33df76e9a9efdbe303acc907e292" dependencies = [ "bit_field 0.9.0", "bitflags", diff --git a/tests/test_kernels/default_settings/Cargo.toml b/tests/test_kernels/default_settings/Cargo.toml index 48270847..4ee0b366 100644 --- a/tests/test_kernels/default_settings/Cargo.toml +++ b/tests/test_kernels/default_settings/Cargo.toml @@ -6,4 +6,4 @@ edition = "2018" [dependencies] bootloader = { path = "../../.." } -x86_64 = { git = "https://github.com/rust-osdev/x86_64.git", branch = "nop" } +x86_64 = "0.12.2" diff --git a/tests/test_kernels/default_settings/src/bin/basic_boot.rs b/tests/test_kernels/default_settings/src/bin/basic_boot.rs index c297cc81..19084b97 100644 --- a/tests/test_kernels/default_settings/src/bin/basic_boot.rs +++ b/tests/test_kernels/default_settings/src/bin/basic_boot.rs @@ -7,7 +7,7 @@ use kernel::{exit_qemu, QemuExitCode}; entry_point!(kernel_main); -fn kernel_main(_boot_info: &'static BootInfo) -> ! { +fn kernel_main(_boot_info: &'static mut BootInfo) -> ! { exit_qemu(QemuExitCode::Success); } diff --git a/tests/test_kernels/default_settings/src/bin/should_panic.rs b/tests/test_kernels/default_settings/src/bin/should_panic.rs index bcf50f07..0205f891 100644 --- a/tests/test_kernels/default_settings/src/bin/should_panic.rs +++ b/tests/test_kernels/default_settings/src/bin/should_panic.rs @@ -7,7 +7,7 @@ use kernel::{exit_qemu, QemuExitCode}; entry_point!(kernel_main); -fn kernel_main(_boot_info: &'static BootInfo) -> ! { +fn kernel_main(_boot_info: &'static mut BootInfo) -> ! { panic!(); } From 924131f8ab3a030ed459e9e600aa57a39d4ee7ac Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 30 Oct 2020 15:34:39 +0100 Subject: [PATCH 114/174] Remove old `test-kernel` --- test-kernel/.cargo/config | 2 -- test-kernel/.gitignore | 2 -- test-kernel/Cargo.lock | 32 ----------------------------- test-kernel/Cargo.toml | 8 -------- test-kernel/src/main.rs | 28 ------------------------- test-kernel/x86_64-test-kernel.json | 15 -------------- 6 files changed, 87 deletions(-) delete mode 100644 test-kernel/.cargo/config delete mode 100644 test-kernel/.gitignore delete mode 100644 test-kernel/Cargo.lock delete mode 100644 test-kernel/Cargo.toml delete mode 100644 test-kernel/src/main.rs delete mode 100644 test-kernel/x86_64-test-kernel.json diff --git a/test-kernel/.cargo/config b/test-kernel/.cargo/config deleted file mode 100644 index 92e8659b..00000000 --- a/test-kernel/.cargo/config +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "x86_64-test-kernel.json" diff --git a/test-kernel/.gitignore b/test-kernel/.gitignore deleted file mode 100644 index eccd7b4a..00000000 --- a/test-kernel/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target/ -**/*.rs.bk diff --git a/test-kernel/Cargo.lock b/test-kernel/Cargo.lock deleted file mode 100644 index da989002..00000000 --- a/test-kernel/Cargo.lock +++ /dev/null @@ -1,32 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "bit_field" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "test-kernel" -version = "0.1.0" -dependencies = [ - "x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "x86_64" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" -"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" -"checksum x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "365de37eb7c6da582cbb510dd0f3f1235d24ff6309a8a96e8a9909cc9bfd608f" diff --git a/test-kernel/Cargo.toml b/test-kernel/Cargo.toml deleted file mode 100644 index 3c2be212..00000000 --- a/test-kernel/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "test-kernel" -version = "0.1.0" -authors = ["Philipp Oppermann "] -edition = "2018" - -[dependencies] -x86_64 = "0.11.0" diff --git a/test-kernel/src/main.rs b/test-kernel/src/main.rs deleted file mode 100644 index f0352e22..00000000 --- a/test-kernel/src/main.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![no_std] // don't link the Rust standard library -#![no_main] // disable all Rust-level entry points - -use core::panic::PanicInfo; - -/// This function is called on panic. -#[panic_handler] -fn panic(_info: &PanicInfo) -> ! { - loop {} -} - -#[no_mangle] // don't mangle the name of this function -pub extern "C" fn _start() -> ! { - // this function is the entry point, since the linker looks for a function - // named `_start` by default - - // exit QEMU (see https://os.phil-opp.com/integration-tests/#shutting-down-qemu) - unsafe { exit_qemu(); } - - loop {} -} - -pub unsafe fn exit_qemu() { - use x86_64::instructions::port::Port; - - let mut port = Port::::new(0xf4); - port.write(61); // exit code is (61 << 1) | 1 = 123 -} diff --git a/test-kernel/x86_64-test-kernel.json b/test-kernel/x86_64-test-kernel.json deleted file mode 100644 index 9afe809f..00000000 --- a/test-kernel/x86_64-test-kernel.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "llvm-target": "x86_64-unknown-none", - "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", - "arch": "x86_64", - "target-endian": "little", - "target-pointer-width": "64", - "target-c-int-width": "32", - "os": "none", - "executables": true, - "linker-flavor": "ld.lld", - "linker": "rust-lld", - "panic-strategy": "abort", - "disable-redzone": true, - "features": "-mmx,-sse,+soft-float" - } From bc3a1362d848877b13699eada493ea86a42e32c4 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 30 Oct 2020 15:50:37 +0100 Subject: [PATCH 115/174] Update to master version of x86_64 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2bcc81e..b5945416 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,7 +72,7 @@ dependencies = [ "toml", "uefi", "usize_conversions", - "x86_64 0.12.2 (git+https://github.com/rust-osdev/x86_64.git?branch=access-level-4-table)", + "x86_64 0.12.2 (git+https://github.com/rust-osdev/x86_64.git)", "xmas-elf", ] @@ -338,7 +338,7 @@ dependencies = [ [[package]] name = "x86_64" version = "0.12.2" -source = "git+https://github.com/rust-osdev/x86_64.git?branch=access-level-4-table#4f42c9bf723b7a738e03e8a496420c26c11ba8e2" +source = "git+https://github.com/rust-osdev/x86_64.git#b6ad4d63408b0a2779e1dd9f236a8bf43fc92fd5" dependencies = [ "bit_field 0.9.0", "bitflags", diff --git a/Cargo.toml b/Cargo.toml index ebe6cec6..95d08a34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ required-features = ["uefi_bin"] [dependencies] xmas-elf = { version = "0.6.2", optional = true } -x86_64 = { git = "https://github.com/rust-osdev/x86_64.git", branch = "access-level-4-table", optional = true } +x86_64 = { git = "https://github.com/rust-osdev/x86_64.git", optional = true } usize_conversions = { version = "0.2.0", optional = true } bit_field = { version = "0.10.0", optional = true } log = { version = "0.4.8", optional = true } From 9ddeaaa73562a055819ba74057b02b609c3fbbad Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 30 Oct 2020 15:56:22 +0100 Subject: [PATCH 116/174] Use the `components` key of the `toolchain` action Instead of installing the rustup components manually. This way, we choose a nightly that has the required components, so that no error occurs when the latest nightly misses some components. --- .github/workflows/build.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4510eca4..f2982303 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,9 +37,7 @@ jobs: with: profile: minimal override: true - - - name: "Install Rustup Components" - run: rustup component add rust-src llvm-tools-preview + components: rust-src, llvm-tools-preview # install QEMU - name: Install QEMU (Linux) @@ -79,7 +77,7 @@ jobs: with: profile: minimal override: true - - run: rustup component add rustfmt + components: rustfmt - name: Run `cargo fmt --all -- --check` uses: actions-rs/cargo@v1 with: @@ -95,7 +93,7 @@ jobs: with: profile: minimal override: true - - run: rustup component add clippy + components: clippy - name: Run `cargo clippy` uses: actions-rs/cargo@v1 with: From aae44b297c1f084b8cd775581e1ae9562e47d33e Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 30 Oct 2020 21:52:23 +0100 Subject: [PATCH 117/174] Reimplement custom config parsing with serde --- Cargo.lock | 19 +++++- Cargo.toml | 3 +- build.rs | 168 ++++++++++++++++++++++++-------------------------- src/config.rs | 2 +- 4 files changed, 99 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bffddb40..7332d8c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,7 @@ dependencies = [ "llvm-tools", "log", "rsdp", + "serde", "spinning_top", "thiserror", "toml", @@ -222,9 +223,23 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.116" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "spinning_top" diff --git a/Cargo.toml b/Cargo.toml index 526e1e9b..951e7467 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ optional = true [build-dependencies] llvm-tools-build = { version = "0.1", optional = true, package = "llvm-tools" } toml = { version = "0.5.1", optional = true } +serde = { version = "1.0", features = ["derive"], optional = true} [features] default = [] @@ -63,7 +64,7 @@ builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json"] runner = ["anyhow"] bios_bin = ["binary", "vga_320x200", "rsdp"] uefi_bin = ["binary", "uefi", "font8x8"] -binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", "log", "conquer-once", "spinning_top"] +binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", "log", "conquer-once", "spinning_top", "serde"] vga_320x200 = ["font8x8"] recursive_page_table = [] map_physical_memory = [] diff --git a/build.rs b/build.rs index 28e3d091..3c797f28 100644 --- a/build.rs +++ b/build.rs @@ -8,6 +8,8 @@ fn main() { #[cfg(feature = "binary")] mod binary { + use std::fmt; + pub fn main() { use llvm_tools_build as llvm_tools; use std::{ @@ -193,7 +195,7 @@ mod binary { } // Parse configuration from the kernel's Cargo.toml - let config = match env::var("KERNEL_MANIFEST") { + let config: Config = 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."); @@ -213,13 +215,14 @@ mod binary { .parse::() .expect("failed to parse kernel's Cargo.toml"); - let table = manifest + let config_table = manifest .get("package") .and_then(|table| table.get("metadata")) .and_then(|table| table.get("bootloader")) - .and_then(|table| table.as_table()); + .cloned() + .unwrap_or_else(|| toml::Value::Table(toml::map::Map::new())); - table.map(|t| Config::parse(t)).unwrap_or_default() + config_table.try_into().expect("failed to parse config") } }; @@ -244,105 +247,92 @@ mod binary { println!("cargo:rerun-if-changed=build.rs"); } - include!("src/config.rs"); + fn val_true() -> bool { + true + } - impl Config { - fn parse(table: &toml::value::Table) -> Self { - use std::convert::TryFrom; - use toml::Value; + /// Must be always identical with the struct in `src/config.rs` + /// + /// This copy is needed because we can't derive Deserialize in the `src/config.rs` + /// module itself, since cargo currently unifies dependencies (the `toml` crate enables + /// serde's standard feature). + #[derive(Debug, serde::Deserialize)] + #[serde(rename_all = "kebab-case", deny_unknown_fields)] + struct Config { + #[serde(default)] + pub map_physical_memory: bool, + #[serde(default)] + pub map_page_table_recursively: bool, + #[serde(default = "val_true")] + pub map_framebuffer: bool, + pub kernel_stack_size: Option, + pub physical_memory_offset: Option, + pub recursive_index: Option, + pub kernel_stack_address: Option, + pub boot_info_address: Option, + pub framebuffer_address: Option, + } - let mut config = Self::default(); + struct AlignedAddress(u64); - for (key, value) in table { - match (key.as_str(), value.clone()) { - ("map-physical-memory", Value::Boolean(b)) => { - config.map_physical_memory = b; - } - ("map-page-table-recursively", Value::Boolean(b)) => { - config.map_page_table_recursively = b; - } - ("map-framebuffer", Value::Boolean(b)) => { - config.map_framebuffer = b; - } - ("kernel-stack-size", Value::Integer(i)) => { - if i <= 0 { - panic!("`kernel-stack-size` in kernel manifest must be positive"); - } else { - config.kernel_stack_size = Some(i as u64); - } - } + impl fmt::Debug for AlignedAddress { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } + } - ("recursive-page-table-index", Value::Integer(i)) => { - let index = match u16::try_from(i) { - Ok(index) if index < 512 => index, - _other => panic!( - "`recursive-page-table-index` must be a number between 0 and 512" - ), - }; - config.recursive_index = Some(index); - } + impl<'de> serde::Deserialize<'de> for AlignedAddress { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_str(AlignedAddressVisitor) + } + } - ("physical-memory-offset", Value::Integer(i)) - | ("kernel-stack-address", Value::Integer(i)) - | ("boot-info-address", Value::Integer(i)) - | ("framebuffer-address", Value::Integer(i)) => { - panic!( - "`{0}` in the kernel manifest must be given as a string, \ - as toml does not support unsigned 64-bit integers (try `{0} = \"{1}\"`)", - key.as_str(), - i - ); - } - ("physical-memory-offset", Value::String(s)) => { - config.physical_memory_offset = - Some(Self::parse_aligned_addr(key.as_str(), &s)); - } - ("kernel-stack-address", Value::String(s)) => { - config.kernel_stack_address = - Some(Self::parse_aligned_addr(key.as_str(), &s)); - } - ("boot-info-address", Value::String(s)) => { - config.boot_info_address = Some(Self::parse_aligned_addr(key.as_str(), &s)); - } - ("framebuffer-address", Value::String(s)) => { - config.framebuffer_address = - Some(Self::parse_aligned_addr(key.as_str(), &s)); - } + /// Helper struct for implementing the `optional_version_deserialize` function. + struct AlignedAddressVisitor; - (s, _) => { - let help = if s.contains("_") { - "\nkeys use `-` instead of `_`" - } else { - "" - }; - panic!("unknown bootloader configuration key '{}'{}", s, help); - } - } - } + impl serde::de::Visitor<'_> for AlignedAddressVisitor { + type Value = AlignedAddress; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + formatter, + "a page-aligned memory address, either as integer or as decimal or hexadecimal \ + string (e.g. \"0xffff0000\"); large addresses must be given as string because \ + TOML does not support unsigned 64-bit integers" + ) + } - config + fn visit_u64(self, num: u64) -> Result + where + E: serde::de::Error, + { + if num % 0x1000 == 0 { + Ok(AlignedAddress(num)) + } else { + Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Unsigned(num), + &self, + )) + } } - fn parse_aligned_addr(key: &str, value: &str) -> u64 { + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { let num = if value.starts_with("0x") { u64::from_str_radix(&value[2..], 16) } else { u64::from_str_radix(&value, 10) - }; - - let num = num.expect(&format!( - "`{}` in the kernel manifest must be an integer (is `{}`)", - key, value - )); - - if num % 0x1000 != 0 { - panic!( - "`{}` in the kernel manifest must be aligned to 4KiB (is `{}`)", - key, value - ); - } else { - num } + .map_err(|_err| { + serde::de::Error::invalid_value(serde::de::Unexpected::Str(value), &self) + })?; + + self.visit_u64(num) } } } diff --git a/src/config.rs b/src/config.rs index dfea157d..90e2d5bf 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,12 +16,12 @@ impl Default for Config { Config { map_physical_memory: false, map_page_table_recursively: false, + map_framebuffer: true, physical_memory_offset: None, recursive_index: None, kernel_stack_address: None, kernel_stack_size: None, boot_info_address: None, - map_framebuffer: true, framebuffer_address: None, } } From 06f41a28c3227ea28e3d99c12237461b92130d07 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 9 Jan 2021 20:07:50 +0100 Subject: [PATCH 118/174] Update uefi crate to v0.7.0 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7332d8c2..15303bf2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -301,9 +301,9 @@ dependencies = [ [[package]] name = "uefi" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cbf498e073acb4d1f5bd59b5347757b495163ecfce9d18db81c44b99a542fa" +checksum = "bf8b4606744665c071d73d84b4ba9763b464f3500b73d8e2f13ef6f31c99f1be" dependencies = [ "bitflags", "log", diff --git a/Cargo.toml b/Cargo.toml index 951e7467..dd5828b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ x86_64 = { git = "https://github.com/rust-osdev/x86_64.git", optional = true } usize_conversions = { version = "0.2.0", optional = true } bit_field = { version = "0.10.0", optional = true } log = { version = "0.4.8", optional = true } -uefi = { version = "0.6.0", optional = true } +uefi = { version = "0.7.0", optional = true } argh = { version = "0.1.3", optional = true } displaydoc = { version = "0.1.7", optional = true } conquer-once = { version = "0.2.1", optional = true, default-features = false } From d169a3198a84c413ab57245673710ae84d025c58 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 9 Jan 2021 20:08:07 +0100 Subject: [PATCH 119/174] Remove stabilized features --- src/bin/bios.rs | 1 - src/bin/uefi.rs | 1 - src/lib.rs | 2 -- 3 files changed, 4 deletions(-) diff --git a/src/bin/bios.rs b/src/bin/bios.rs index d0a77278..95de2461 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -2,7 +2,6 @@ #![feature(global_asm)] #![feature(llvm_asm)] #![feature(asm)] -#![feature(slice_fill)] #![no_std] #![no_main] diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 8b3f057e..3ba44456 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -3,7 +3,6 @@ #![feature(abi_efiapi)] #![feature(asm)] #![feature(unsafe_block_in_unsafe_fn)] -#![feature(min_const_generics)] #![feature(maybe_uninit_extra)] #![deny(unsafe_op_in_unsafe_fn)] diff --git a/src/lib.rs b/src/lib.rs index 2f56fc80..35c3cc0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,8 +6,6 @@ //! blog for an explanation. #![cfg_attr(not(feature = "builder"), no_std)] -#![feature(min_const_generics)] -#![feature(slice_fill)] #![feature(asm)] #![feature(unsafe_block_in_unsafe_fn)] #![feature(maybe_uninit_extra)] From 0c33cec02a897585bf21d21bac5e1e72854b0a18 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 9 Jan 2021 20:16:36 +0100 Subject: [PATCH 120/174] Add bootloader version to boot info --- src/binary/mod.rs | 4 ++++ src/boot_info.rs | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/binary/mod.rs b/src/binary/mod.rs index a73230c8..b9b68ba6 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -277,6 +277,10 @@ where // create boot info let boot_info = boot_info.write(BootInfo { + version_major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(), + version_minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(), + version_patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(), + pre_release: !env!("CARGO_PKG_VERSION_PRE").is_empty(), memory_regions, framebuffer: mappings.framebuffer.map(|addr| FrameBuffer { buffer_start: addr.as_u64(), diff --git a/src/boot_info.rs b/src/boot_info.rs index d95ced10..87ef73da 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -15,6 +15,17 @@ use core::slice; /// signature, use the [`entry_point`] macro. #[derive(Debug)] pub struct BootInfo { + /// Bootloader version (major). + pub version_major: u16, + /// Bootloader version (minor). + pub version_minor: u16, + /// Bootloader version (patch). + pub version_patch: u16, + /// Whether the bootloader version is a pre-release. + /// + /// We can't store the full prerelease string of the version number since it could be + /// arbitrarily long. + pub pre_release: bool, /// A map of the physical memory regions of the underlying machine. /// /// The bootloader queries this information from the BIOS/UEFI firmware and translates this From 86d1db72fd334e34dcfc17c78540b8365a974199 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 9 Jan 2021 20:50:46 +0100 Subject: [PATCH 121/174] Make boot info FFI-safe --- src/binary/mod.rs | 13 +++--- src/boot_info.rs | 96 ++++++++++++++++++++++++++++++++++++++------ src/memory_region.rs | 2 + 3 files changed, 92 insertions(+), 19 deletions(-) diff --git a/src/binary/mod.rs b/src/binary/mod.rs index b9b68ba6..6ceac412 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -281,17 +281,16 @@ where version_minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(), version_patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(), pre_release: !env!("CARGO_PKG_VERSION_PRE").is_empty(), - memory_regions, + memory_regions: memory_regions.into(), framebuffer: mappings.framebuffer.map(|addr| FrameBuffer { buffer_start: addr.as_u64(), buffer_byte_len: system_info.framebuffer_info.byte_len, info: system_info.framebuffer_info, - }), - physical_memory_offset: mappings.physical_memory_offset.map(VirtAddr::as_u64), - recursive_index: mappings.recursive_index.map(Into::into), - rsdp_addr: system_info.rsdp_addr.map(|addr| addr.as_u64()), - tls_template: mappings.tls_template, - _non_exhaustive: (), + }).into(), + physical_memory_offset: mappings.physical_memory_offset.map(VirtAddr::as_u64).into(), + recursive_index: mappings.recursive_index.map(Into::into).into(), + rsdp_addr: system_info.rsdp_addr.map(|addr| addr.as_u64()).into(), + tls_template: mappings.tls_template.into(), }); (boot_info, two_frames) diff --git a/src/boot_info.rs b/src/boot_info.rs index 87ef73da..1e03f34a 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -1,5 +1,5 @@ use crate::memory_region::MemoryRegion; -use core::slice; +use core::{ops, slice}; /// This structure represents the information that the bootloader passes to the kernel. /// @@ -14,6 +14,8 @@ use core::slice; /// use the correct argument types. To ensure that the entry point function has the correct /// signature, use the [`entry_point`] macro. #[derive(Debug)] +#[repr(C)] +#[non_exhaustive] pub struct BootInfo { /// Bootloader version (major). pub version_major: u16, @@ -32,9 +34,9 @@ pub struct BootInfo { /// information to Rust types. It also marks any memory regions that the bootloader uses in /// the memory map before passing it to the kernel. Regions marked as usable can be freely /// used by the kernel. - pub memory_regions: &'static mut [MemoryRegion], + pub memory_regions: MemoryRegions, /// Information about the framebuffer for screen output if available. - pub framebuffer: Option, + pub framebuffer: Optional, /// The virtual address at which the mapping of the physical memory starts. /// /// Physical addresses can be converted to virtual addresses by adding this offset to them. @@ -45,21 +47,63 @@ pub struct BootInfo { /// can be safely accessed. /// /// Only available if the `map-physical-memory` config option is enabled. - pub physical_memory_offset: Option, + pub physical_memory_offset: Optional, /// The virtual address of the recursively mapped level 4 page table. /// /// Only available if the `map-page-table-recursively` config option is enabled. - pub recursive_index: Option, + pub recursive_index: Optional, /// The address of the `RSDP` data structure, which can be use to find the ACPI tables. /// /// This field is `None` if no `RSDP` was found (for BIOS) or reported (for UEFI). - pub rsdp_addr: Option, + pub rsdp_addr: Optional, /// The thread local storage (TLS) template of the kernel executable, if present. - pub tls_template: Option, - pub(crate) _non_exhaustive: (), + pub tls_template: Optional, +} + +/// FFI-safe slice of [`MemoryRegion`] structs, semantically equivalent to +/// `&'static mut [MemoryRegion]`. +/// +/// This type implements the [`Deref`][core::ops::Deref] and [`DerefMut`][core::ops::DerefMut] +/// traits, so it can be used like a `&mut [MemoryRegion]` slice. It also implements [`From`] +/// and [`Into`] for easy conversions from and to `&'static mut [MemoryRegion]`. +#[derive(Debug)] +#[repr(C)] +pub struct MemoryRegions { + pub(crate) ptr: *mut MemoryRegion, + pub(crate) len: usize, +} + +impl ops::Deref for MemoryRegions { + type Target = [MemoryRegion]; + + fn deref(&self) -> &Self::Target { + unsafe { slice::from_raw_parts(self.ptr, self.len) } + } +} + +impl ops::DerefMut for MemoryRegions { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { slice::from_raw_parts_mut(self.ptr, self.len )} + } +} + +impl From<&'static mut [MemoryRegion]> for MemoryRegions { + fn from(regions: &'static mut [MemoryRegion]) -> Self { + MemoryRegions { + ptr: regions.as_mut_ptr(), + len: regions.len(), + } + } +} + +impl Into<&'static mut [MemoryRegion]> for MemoryRegions { + fn into(self) -> &'static mut [MemoryRegion] { + unsafe { slice::from_raw_parts_mut(self.ptr, self.len )} + } } #[derive(Debug)] +#[repr(C)] pub struct FrameBuffer { pub(crate) buffer_start: u64, pub(crate) buffer_byte_len: usize, @@ -81,6 +125,7 @@ impl FrameBuffer { } #[derive(Debug, Clone, Copy)] +#[repr(C)] pub struct FrameBufferInfo { pub byte_len: usize, pub horizontal_resolution: usize, @@ -92,6 +137,7 @@ pub struct FrameBufferInfo { #[derive(Debug, Clone, Copy)] #[non_exhaustive] +#[repr(C)] pub enum PixelFormat { RGB, BGR, @@ -106,6 +152,7 @@ pub enum PixelFormat { /// location. The additional `mem_size - file_size` bytes must be initialized with /// zero. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C)] pub struct TlsTemplate { /// The virtual start address of the thread local storage template. pub start_addr: u64, @@ -119,8 +166,33 @@ pub struct TlsTemplate { pub mem_size: u64, } -/// Check that the _pointer_ is FFI-safe. +/// FFI-safe variant of [`Option`]. /// -/// Note that the `BootInfo` struct is not FFI-safe, so it needs to be compiled by the same Rust -/// compiler as the kernel in order to be safely accessed. -extern "C" fn _assert_ffi(_boot_info: &'static mut BootInfo) {} +/// Implements the [`From`] and [`Into`] traits for easy conversion to and from [`Option`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(C)] +pub enum Optional { + Some(T), + None, +} + +impl From> for Optional { + fn from(v: Option) -> Self { + match v { + Some(v) => Optional::Some(v), + None => Optional::None, + } + } +} + +impl Into> for Optional { + fn into(self) -> Option { + match self { + Optional::Some(v) => Some(v), + Optional::None => None, + } + } +} + +/// Check that bootinfo is FFI-safe +extern "C" fn _assert_ffi(_boot_info: BootInfo) {} diff --git a/src/memory_region.rs b/src/memory_region.rs index d24a8169..a56166ef 100644 --- a/src/memory_region.rs +++ b/src/memory_region.rs @@ -1,4 +1,5 @@ #[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(C)] pub struct MemoryRegion { pub start: u64, pub end: u64, @@ -17,6 +18,7 @@ impl MemoryRegion { #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] +#[repr(C)] pub enum MemoryRegionKind { /// Unused conventional memory, can be used by the kernel. Usable, From 92b069a3414f423789e9921107120c7231608360 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 10 Jan 2021 14:06:21 +0100 Subject: [PATCH 122/174] Start providing API docs for all public items --- src/binary/legacy_memory_region.rs | 19 ++++++++ src/binary/level_4_entries.rs | 14 ++++++ src/binary/load_kernel.rs | 4 ++ src/binary/logger.rs | 10 ++++- src/binary/mod.rs | 71 +++++++++++++++++++++++++++--- src/lib.rs | 2 +- 6 files changed, 113 insertions(+), 7 deletions(-) diff --git a/src/binary/legacy_memory_region.rs b/src/binary/legacy_memory_region.rs index c2e51ddb..48590116 100644 --- a/src/binary/legacy_memory_region.rs +++ b/src/binary/legacy_memory_region.rs @@ -5,9 +5,13 @@ use x86_64::{ PhysAddr, }; +/// Abstraction trait for a memory region returned by the UEFI or BIOS firmware. pub trait LegacyMemoryRegion: Copy + core::fmt::Debug { + /// Returns the physical start address of the region. fn start(&self) -> PhysAddr; + /// Returns the size of the region in bytes. fn len(&self) -> u64; + /// Returns the type of the region, e.g. whether it is usable or reserved. fn kind(&self) -> MemoryRegionKind; fn set_start(&mut self, new_start: PhysAddr); @@ -25,12 +29,19 @@ where I: ExactSizeIterator + Clone, I::Item: LegacyMemoryRegion, { + /// Creates a new frame allocator based on the given legacy memory regions. + /// + /// Skips the frame at physical address zero to avoid potential problems. For example + /// identity-mapping the frame at address zero is not valid in Rust, because Rust's `core` + /// library assumes that references can never point to virtual address `0`. pub fn new(memory_map: I) -> Self { // skip frame 0 because the rust core library does not see 0 as a valid address let start_frame = PhysFrame::containing_address(PhysAddr::new(0x1000)); Self::new_starting_at(start_frame, memory_map) } + /// Creates a new frame allocator based on the given legacy memory regions. Skips any frames + /// before the given `frame`. pub fn new_starting_at(frame: PhysFrame, memory_map: I) -> Self { Self { original: memory_map.clone(), @@ -72,6 +83,14 @@ where .unwrap() } + /// Converts this type to a boot info memory map. + /// + /// The memory map is placed in the given `regions` slice. The length of the given slice + /// must be at least the value returned by [`len`]. Be aware that the value returned by + /// `len` might increase by 1 whenever [`allocate_frame`] is called, so the length should be + /// queried as late as possible. + /// + /// The return slice is a subslice of `regions`, shortened to the actual number of regions. pub fn construct_memory_map( self, regions: &mut [MaybeUninit], diff --git a/src/binary/level_4_entries.rs b/src/binary/level_4_entries.rs index 2172d494..c231cae2 100644 --- a/src/binary/level_4_entries.rs +++ b/src/binary/level_4_entries.rs @@ -5,11 +5,17 @@ use x86_64::{ }; use xmas_elf::program::ProgramHeader; +/// Keeps track of used entries in a level 4 page table. +/// +/// Useful for determining a free virtual memory block, e.g. for mapping additional data. pub struct UsedLevel4Entries { entry_state: [bool; 512], // whether an entry is in use by the kernel } impl UsedLevel4Entries { + /// Initializes a new instance from the given ELF program segments. + /// + /// Marks the virtual address range of all segments as used. pub fn new<'a>(segments: impl Iterator>) -> Self { let mut used = UsedLevel4Entries { entry_state: [false; 512], @@ -31,6 +37,10 @@ impl UsedLevel4Entries { used } + /// Returns a unused level 4 entry and marks it as used. + /// + /// Since this method marks each returned index as used, it can be used multiple times + /// to determine multiple unused virtual memory regions. pub fn get_free_entry(&mut self) -> PageTableIndex { let (idx, entry) = self .entry_state @@ -43,6 +53,10 @@ impl UsedLevel4Entries { PageTableIndex::new(idx.try_into().unwrap()) } + /// Returns the virtual start address of an unused level 4 entry and marks it as used. + /// + /// This is a convenience method around [`get_free_entry`], so all of its docs applies here + /// too. pub fn get_free_address(&mut self) -> VirtAddr { Page::from_page_table_indices_1gib(self.get_free_entry(), PageTableIndex::new(0)) .start_address() diff --git a/src/binary/load_kernel.rs b/src/binary/load_kernel.rs index ca641c3e..bc0fa812 100644 --- a/src/binary/load_kernel.rs +++ b/src/binary/load_kernel.rs @@ -269,6 +269,10 @@ where } } +/// Loads the kernel ELF file given in `bytes` in the given `page_table`. +/// +/// Returns the kernel entry point address, it's thread local storage template (if any), +/// and a structure describing which level 4 page table entries are in use. pub fn load_kernel( bytes: &[u8], page_table: &mut impl MapperAllSizes, diff --git a/src/binary/logger.rs b/src/binary/logger.rs index 68fe97f9..ffb72651 100644 --- a/src/binary/logger.rs +++ b/src/binary/logger.rs @@ -7,8 +7,10 @@ use core::{ use font8x8::UnicodeFonts; use spinning_top::Spinlock; +/// The global logger instance used for the `log` crate. pub static LOGGER: OnceCell = OnceCell::uninit(); +/// A [`Logger`] instance protected by a spinlock. pub struct LockedLogger(Spinlock); /// Additional vertical space between lines @@ -17,10 +19,14 @@ const LINE_SPACING: usize = 0; const LOG_SPACING: usize = 2; impl LockedLogger { + /// Create a new instance that logs to the given framebuffer. pub fn new(framebuffer: &'static mut [u8], info: FrameBufferInfo) -> Self { LockedLogger(Spinlock::new(Logger::new(framebuffer, info))) } + /// Force-unlocks the logger to prevent a deadlock. + /// + /// This method is not memory safe and should be only used when absolutely necessary. pub unsafe fn force_unlock(&self) { unsafe { self.0.force_unlock() }; } @@ -40,6 +46,7 @@ impl log::Log for LockedLogger { fn flush(&self) {} } +/// Allows logging text to a pixel-based framebuffer. pub struct Logger { framebuffer: &'static mut [u8], info: FrameBufferInfo, @@ -48,6 +55,7 @@ pub struct Logger { } impl Logger { + /// Creates a new logger that uses the given framebuffer. pub fn new(framebuffer: &'static mut [u8], info: FrameBufferInfo) -> Self { let mut logger = Self { framebuffer, @@ -72,7 +80,7 @@ impl Logger { self.x_pos = 0; } - /// Erases all text on the screen + /// Erases all text on the screen. pub fn clear(&mut self) { self.x_pos = 0; self.y_pos = 0; diff --git a/src/binary/mod.rs b/src/binary/mod.rs index 6ceac412..85d0d5d0 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -23,8 +23,11 @@ pub mod bios; pub mod uefi; pub mod legacy_memory_region; +/// Provides a type to keep track of used entries in a level 4 page table. pub mod level_4_entries; +/// Implements a loader for the kernel ELF binary. pub mod load_kernel; +/// Provides a logger type that logs output as text to pixel-based framebuffers. pub mod logger; // Contains the parsed configuration table from the kernel's Cargo.toml. @@ -42,19 +45,29 @@ include!(concat!(env!("OUT_DIR"), "/bootloader_config.rs")); const PAGE_SIZE: u64 = 4096; +/// Initialize a text-based logger using the given pixel-based framebuffer as output. pub fn init_logger(framebuffer: &'static mut [u8], info: FrameBufferInfo) { let logger = logger::LOGGER.get_or_init(move || logger::LockedLogger::new(framebuffer, info)); log::set_logger(logger).expect("logger already set"); log::set_max_level(log::LevelFilter::Trace); } +/// Required system information that should be queried from the BIOS or UEFI firmware. #[derive(Debug, Copy, Clone)] pub struct SystemInfo { + /// Start address of the pixel-based framebuffer. pub framebuffer_addr: PhysAddr, + /// Information about the framebuffer, including layout and pixel format. pub framebuffer_info: FrameBufferInfo, + /// Address of the _Root System Description Pointer_ structure of the ACPI standard. pub rsdp_addr: Option, } +/// Loads the kernel ELF executable into memory and switches to it. +/// +/// This function is a convenience function that first calls [`set_up_mappings`], then +/// [`create_boot_info`], and finally [`switch_to_kernel`]. The given arguments are passed +/// directly to these functions, so see their docs for more info. pub fn load_and_switch_to_kernel( kernel_bytes: &[u8], mut frame_allocator: LegacyFrameAllocator, @@ -81,7 +94,20 @@ where switch_to_kernel(page_tables, mappings, boot_info, two_frames); } -/// Sets up mappings for a kernel stack and the framebuffer +/// Sets up mappings for a kernel stack and the framebuffer. +/// +/// The `kernel_bytes` slice should contain the raw bytes of the kernel ELF executable. The +/// `frame_allocator` argument should be created from the memory map. The `page_tables` +/// argument should point to the bootloader and kernel page tables. The function tries to parse +/// the ELF file and create all specified mappings in the kernel-level page table. +/// +/// The `framebuffer_addr` and `framebuffer_size` fields should be set to the start address and +/// byte length the pixel-based framebuffer. These arguments are required because the functions +/// maps this framebuffer in the kernel-level page table, unless the `map_framebuffer` config +/// option is disabled. +/// +/// This function reacts to unexpected situations (e.g. invalid kernel ELF file) with a panic, so +/// errors are not recoverable. pub fn set_up_mappings( kernel_bytes: &[u8], frame_allocator: &mut LegacyFrameAllocator, @@ -201,17 +227,31 @@ where } } +/// Contains the addresses of all memory mappings set up by [`set_up_mappings`]. pub struct Mappings { + /// The entry point address of the kernel. pub entry_point: VirtAddr, + /// The stack end page of the kernel. pub stack_end: Page, + /// Keeps track of used entries in the level 4 page table, useful for finding a free + /// virtual memory when needed. pub used_entries: UsedLevel4Entries, + /// The start address of the framebuffer, if any. pub framebuffer: Option, + /// The start address of the physical memory mapping, if enabled. pub physical_memory_offset: Option, + /// The level 4 page table index of the recursive mapping, if enabled. pub recursive_index: Option, + /// The thread local storage template of the kernel executable, if it contains one. pub tls_template: Option, } -/// Allocates and initializes the boot info struct and the memory map +/// Allocates and initializes the boot info struct and the memory map. +/// +/// The boot info and memory map are mapped to both the kernel and bootloader +/// address space at the same address. This makes it possible to return a Rust +/// reference that is valid in both address spaces. The necessary physical frames +/// are taken from the given `frame_allocator`. pub fn create_boot_info( mut frame_allocator: LegacyFrameAllocator, page_tables: &mut PageTables, @@ -325,15 +365,26 @@ pub fn switch_to_kernel( } } +/// Provides access to the page tables of the bootloader and kernel address space. pub struct PageTables { + /// Provides access to the page tables of the bootloader address space. pub bootloader: OffsetPageTable<'static>, + /// Provides access to the page tables of the kernel address space (not active). pub kernel: OffsetPageTable<'static>, + /// The physical frame where the level 4 page table of the kernel address space is stored. + /// + /// Must be the page table that the `kernel` field of this struct refers to. + /// + /// This frame is loaded into the `CR3` register on the final context switch to the kernel. pub kernel_level_4_frame: PhysFrame, } -/// Performs the actual context switch +/// Performs the actual context switch. /// -/// This function should stay small because it needs to be identity-mapped. +/// This function uses the given `frame_allocator` to identity map itself in the kernel-level +/// page table. This is required to avoid a page fault after the context switch. Since this +/// function is relatively small, only up to two physical frames are required from the frame +/// allocator, so the [`TwoFrames`] type can be used here. unsafe fn context_switch( addresses: Addresses, mut kernel_page_table: OffsetPageTable, @@ -367,18 +418,28 @@ unsafe fn context_switch( unreachable!(); } -pub struct Addresses { +/// Memory addresses required for the context switch. +struct Addresses { page_table: PhysFrame, stack_top: VirtAddr, entry_point: VirtAddr, boot_info: &'static mut crate::boot_info::BootInfo, } +/// Used for reversing two physical frames for identity mapping the context switch function. +/// +/// In order to prevent a page fault, the context switch function must be mapped identically in +/// both address spaces. The context switch function is small, so this mapping requires only +/// two physical frames (one frame is not enough because the linker might place the function +/// directly before a page boundary). Since the frame allocator no longer exists when the +/// context switch function is invoked, we use this type to reserve two physical frames +/// beforehand. pub struct TwoFrames { frames: [Option; 2], } impl TwoFrames { + /// Creates a new instance by allocating two physical frames from the given frame allocator. pub fn new(frame_allocator: &mut impl FrameAllocator) -> Self { TwoFrames { frames: [ diff --git a/src/lib.rs b/src/lib.rs index 35c3cc0a..952a8677 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ #![feature(maybe_uninit_extra)] #![feature(maybe_uninit_slice)] #![deny(unsafe_op_in_unsafe_fn)] -//#![warn(missing_docs)] +#![warn(missing_docs)] pub use crate::boot_info::BootInfo; From 391f7435746c5fd1bc43bec39ebfec8902db669a Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 10 Jan 2021 14:28:02 +0100 Subject: [PATCH 123/174] Remove `LegacyMemoryRegion::set_start` method It is not really needed and might be dangerous to use, as it adjusts only the start address, not the length. --- src/binary/bios/memory_descriptor.rs | 4 ---- src/binary/legacy_memory_region.rs | 13 ++++++------- src/binary/uefi/memory_descriptor.rs | 4 ---- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/binary/bios/memory_descriptor.rs b/src/binary/bios/memory_descriptor.rs index 519ff65f..18bcd837 100644 --- a/src/binary/bios/memory_descriptor.rs +++ b/src/binary/bios/memory_descriptor.rs @@ -16,10 +16,6 @@ impl LegacyMemoryRegion for E820MemoryRegion { other => MemoryRegionKind::UnknownBios(other), } } - - fn set_start(&mut self, new_start: PhysAddr) { - self.start_addr = new_start.as_u64(); - } } #[doc(hidden)] diff --git a/src/binary/legacy_memory_region.rs b/src/binary/legacy_memory_region.rs index 48590116..e497b1db 100644 --- a/src/binary/legacy_memory_region.rs +++ b/src/binary/legacy_memory_region.rs @@ -13,8 +13,6 @@ pub trait LegacyMemoryRegion: Copy + core::fmt::Debug { fn len(&self) -> u64; /// Returns the type of the region, e.g. whether it is usable or reserved. fn kind(&self) -> MemoryRegionKind; - - fn set_start(&mut self, new_start: PhysAddr); } pub struct LegacyFrameAllocator { @@ -97,8 +95,9 @@ where ) -> &mut [MemoryRegion] { let mut next_index = 0; - for mut descriptor in self.original { - let end = descriptor.start() + descriptor.len(); + for descriptor in self.original { + let mut start = descriptor.start(); + let end = start + descriptor.len(); let next_free = self.next_frame.start_address(); let kind = match descriptor.kind() { MemoryRegionKind::Usable => { @@ -107,7 +106,7 @@ where } else if descriptor.start() >= next_free { MemoryRegionKind::Usable } else { - // part of the region is used -> add is separately + // part of the region is used -> add it separately let used_region = MemoryRegion { start: descriptor.start().as_u64(), end: next_free.as_u64(), @@ -117,7 +116,7 @@ where .expect("Failed to add memory region"); // add unused part normally - descriptor.set_start(next_free); + start = next_free; MemoryRegionKind::Usable } } @@ -139,7 +138,7 @@ where }; let region = MemoryRegion { - start: descriptor.start().as_u64(), + start: start.as_u64(), end: end.as_u64(), kind, }; diff --git a/src/binary/uefi/memory_descriptor.rs b/src/binary/uefi/memory_descriptor.rs index 6be0d208..c423754d 100644 --- a/src/binary/uefi/memory_descriptor.rs +++ b/src/binary/uefi/memory_descriptor.rs @@ -19,8 +19,4 @@ impl<'a> LegacyMemoryRegion for MemoryDescriptor { other => MemoryRegionKind::UnknownUefi(other.0), } } - - fn set_start(&mut self, new_start: PhysAddr) { - self.phys_start = new_start.as_u64(); - } } From aac18250a2fdacc2993b917610cec900f9a9176d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 10 Jan 2021 15:07:00 +0100 Subject: [PATCH 124/174] Document the remaining API items --- src/binary/bios/memory_descriptor.rs | 3 ++ src/binary/bios/mod.rs | 1 + src/binary/legacy_memory_region.rs | 12 ++++++-- src/binary/mod.rs | 5 +++- src/boot_info.rs | 29 ++++++++++++++++++++ src/config.rs | 41 ++++++++++++++++++++++++++-- src/disk_image.rs | 1 + src/lib.rs | 11 ++++++++ src/memory_region.rs | 8 ++++++ 9 files changed, 105 insertions(+), 6 deletions(-) diff --git a/src/binary/bios/memory_descriptor.rs b/src/binary/bios/memory_descriptor.rs index 18bcd837..4217b608 100644 --- a/src/binary/bios/memory_descriptor.rs +++ b/src/binary/bios/memory_descriptor.rs @@ -18,6 +18,9 @@ impl LegacyMemoryRegion for E820MemoryRegion { } } +/// A physical memory region returned by an `e820` BIOS call. +/// +/// See http://wiki.osdev.org/Detecting_Memory_(x86)#Getting_an_E820_Memory_Map for more info. #[doc(hidden)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(C)] diff --git a/src/binary/bios/mod.rs b/src/binary/bios/mod.rs index a5c46950..3a4aeab9 100644 --- a/src/binary/bios/mod.rs +++ b/src/binary/bios/mod.rs @@ -1 +1,2 @@ +/// Provides an abstraction type for a BIOS-provided memory region. pub mod memory_descriptor; diff --git a/src/binary/legacy_memory_region.rs b/src/binary/legacy_memory_region.rs index e497b1db..7fc8934f 100644 --- a/src/binary/legacy_memory_region.rs +++ b/src/binary/legacy_memory_region.rs @@ -15,6 +15,7 @@ pub trait LegacyMemoryRegion: Copy + core::fmt::Debug { fn kind(&self) -> MemoryRegionKind; } +/// A physical frame allocator based on a BIOS or UEFI provided memory map. pub struct LegacyFrameAllocator { original: I, memory_map: I, @@ -69,10 +70,17 @@ where } } + /// Returns the number of memory regions in the underlying memory map. + /// + /// The function always returns the same value, i.e. the length doesn't + /// change after calls to `allocate_frame`. pub fn len(&self) -> usize { self.original.len() } + /// Returns the largest detected physical memory address. + /// + /// Useful for creating a mapping for all physical memory. pub fn max_phys_addr(&self) -> PhysAddr { self.original .clone() @@ -84,9 +92,7 @@ where /// Converts this type to a boot info memory map. /// /// The memory map is placed in the given `regions` slice. The length of the given slice - /// must be at least the value returned by [`len`]. Be aware that the value returned by - /// `len` might increase by 1 whenever [`allocate_frame`] is called, so the length should be - /// queried as late as possible. + /// must be at least the value returned by [`len`] pluse 1. /// /// The return slice is a subslice of `regions`, shortened to the actual number of regions. pub fn construct_memory_map( diff --git a/src/binary/mod.rs b/src/binary/mod.rs index 85d0d5d0..4392b56d 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -17,11 +17,14 @@ use x86_64::{ PhysAddr, VirtAddr, }; +/// Provides BIOS-specific types and trait implementations. #[cfg(feature = "bios_bin")] pub mod bios; +/// Provides UEFI-specific trait implementations. #[cfg(feature = "uefi_bin")] -pub mod uefi; +mod uefi; +/// Provides a frame allocator based on a BIOS or UEFI memory map. pub mod legacy_memory_region; /// Provides a type to keep track of used entries in a level 4 page table. pub mod level_4_entries; diff --git a/src/boot_info.rs b/src/boot_info.rs index 1e03f34a..abb8f1f7 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -102,6 +102,7 @@ impl Into<&'static mut [MemoryRegion]> for MemoryRegions { } } +/// A pixel-based framebuffer that controls the screen output. #[derive(Debug)] #[repr(C)] pub struct FrameBuffer { @@ -111,6 +112,7 @@ pub struct FrameBuffer { } impl FrameBuffer { + /// Returns the raw bytes of the framebuffer. pub fn buffer(&mut self) -> &mut [u8] { unsafe { self.create_buffer() } } @@ -119,28 +121,53 @@ impl FrameBuffer { unsafe { slice::from_raw_parts_mut(self.buffer_start as *mut u8, self.buffer_byte_len) } } + /// Returns layout and pixel format information of the framebuffer. pub fn info(&self) -> FrameBufferInfo { self.info } } +/// Describes the layout and pixel format of a framebuffer. #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct FrameBufferInfo { + /// The total size in bytes. pub byte_len: usize, + /// The width in pixels. pub horizontal_resolution: usize, + /// The height in pixels. pub vertical_resolution: usize, + /// The color format of each pixel. pub pixel_format: PixelFormat, + /// The number of bytes per pixel. pub bytes_per_pixel: usize, + /// Number of bytes between the start of a line and the start of the next. + /// + /// Some framebuffers use additional padding bytes at the end of a line, so this + /// value might be larger than `horizontal_resolution * bytes_per_pixel`. It is + /// therefore recommended to use this field for calculating the start address of a line. pub stride: usize, } +/// Color format of pixels in the framebuffer. #[derive(Debug, Clone, Copy)] #[non_exhaustive] #[repr(C)] pub enum PixelFormat { + /// One byte red, then one byte green, then one byte blue. + /// + /// Length might be larger than 3, check [`bytes_per_pixel`][FrameBufferInfo::bytes_per_pixel] + /// for this. RGB, + /// One byte blue, then one byte green, then one byte red. + /// + /// Length might be larger than 3, check [`bytes_per_pixel`][FrameBufferInfo::bytes_per_pixel] + /// for this. BGR, + /// A single byte, representing the grayscale value. + /// + /// Length might be larger than 1, check [`bytes_per_pixel`][FrameBufferInfo::bytes_per_pixel] + /// for this. U8, } @@ -172,7 +199,9 @@ pub struct TlsTemplate { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] pub enum Optional { + /// Some value `T` Some(T), + /// No value None, } diff --git a/src/config.rs b/src/config.rs index 90e2d5bf..3d963c0c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,13 +1,50 @@ +/// Allows configuring the bootloader behavior. +/// +/// To control these, use a `[package.metadata.bootloader]` table in the `Cargo.toml` of +/// your kernel. #[derive(Debug)] pub struct Config { + /// Whether to create a virtual mapping of the complete physical memory. + /// + /// Defaults to `false`. pub map_physical_memory: bool, - pub map_page_table_recursively: bool, - pub kernel_stack_size: Option, + /// Map the physical memory at a specified virtual address. + /// + /// If not given, the bootloader searches for a free virtual address dynamically. + /// + /// Only considered if `map_physical_memory` is `true`. pub physical_memory_offset: Option, + /// Whether to create a recursive entry in the level 4 page table. + /// + /// Defaults to `false`. + pub map_page_table_recursively: bool, + /// Create the recursive mapping in at the given entry of the level 4 page table. + /// + /// If not given, the bootloader searches for a free level 4 entry dynamically. + /// + /// Only considered if `map_page_table_recursively` is `true`. pub recursive_index: Option, + /// Use the given stack size for the kernel. + /// + /// Defaults to at least 80KiB if not given. + pub kernel_stack_size: Option, + /// Create the kernel stack at the given virtual address. + /// + /// Looks for a free virtual memory region dynamically if not given. pub kernel_stack_address: Option, + /// Create the boot information at the given virtual address. + /// + /// Looks for a free virtual memory region dynamically if not given. pub boot_info_address: Option, + /// Whether to map the framebuffer to virtual memory. + /// + /// Defaults to `true`. pub map_framebuffer: bool, + /// Map the framebuffer memory at the specified virtual address. + /// + /// If not given, the bootloader searches for a free virtual memory region dynamically. + /// + /// Only considered if `map_framebuffer` is `true`. pub framebuffer_address: Option, } diff --git a/src/disk_image.rs b/src/disk_image.rs index e274ee64..7b8e3610 100644 --- a/src/disk_image.rs +++ b/src/disk_image.rs @@ -1,6 +1,7 @@ use std::{io, path::Path, process::Command}; use thiserror::Error; +/// Creates a bootable disk image from the given bootloader executable. pub fn create_disk_image( bootloader_elf_path: &Path, output_bin_path: &Path, diff --git a/src/lib.rs b/src/lib.rs index 952a8677..f50378c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,14 +15,25 @@ pub use crate::boot_info::BootInfo; +/// Configuration options for the bootloader. pub mod config; +/// Contains the boot information struct sent by the bootloader to the kernel on startup. pub mod boot_info; +/// Provides a memory region type that is used in the memory map of the boot info structure. pub mod memory_region; +/// Contains the actual bootloader implementation ("bootloader as a binary"). +/// +/// Useful for reusing part of the bootloader implementation for other crates. +/// +/// Only available when the `binary` feature is enabled. #[cfg(feature = "binary")] pub mod binary; +/// Provides a function to turn a bootloader executable into a disk image. +/// +/// Used by the `builder` binary. Only available when the `builder` feature is enabled. #[cfg(feature = "builder")] pub mod disk_image; diff --git a/src/memory_region.rs b/src/memory_region.rs index a56166ef..e0fad17e 100644 --- a/src/memory_region.rs +++ b/src/memory_region.rs @@ -1,12 +1,19 @@ +/// Represent a physical memory region. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(C)] pub struct MemoryRegion { + /// The physical start address of the region. pub start: u64, + /// The physical end address (exclusive) of the region. pub end: u64, + /// The memory type of the memory region. + /// + /// Only [`Usable`][MemoryRegionKind::Usable] regions can be freely used. pub kind: MemoryRegionKind, } impl MemoryRegion { + /// Creates a new empty memory region (with length 0). pub const fn empty() -> Self { MemoryRegion { start: 0, @@ -16,6 +23,7 @@ impl MemoryRegion { } } +/// Represents the different types of memory. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] #[repr(C)] From 7d93994c8b8e3a34c617ed5eae1f82da8245efd9 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 10 Jan 2021 15:07:36 +0100 Subject: [PATCH 125/174] Run cargo fmt --- src/binary/mod.rs | 21 ++++++++++++--------- src/boot_info.rs | 8 ++++---- src/config.rs | 2 +- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/binary/mod.rs b/src/binary/mod.rs index 4392b56d..f1457a13 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -30,7 +30,7 @@ pub mod legacy_memory_region; pub mod level_4_entries; /// Implements a loader for the kernel ELF binary. pub mod load_kernel; -/// Provides a logger type that logs output as text to pixel-based framebuffers. +/// Provides a logger type that logs output as text to pixel-based framebuffers. pub mod logger; // Contains the parsed configuration table from the kernel's Cargo.toml. @@ -55,12 +55,12 @@ pub fn init_logger(framebuffer: &'static mut [u8], info: FrameBufferInfo) { log::set_max_level(log::LevelFilter::Trace); } -/// Required system information that should be queried from the BIOS or UEFI firmware. +/// Required system information that should be queried from the BIOS or UEFI firmware. #[derive(Debug, Copy, Clone)] pub struct SystemInfo { /// Start address of the pixel-based framebuffer. pub framebuffer_addr: PhysAddr, - /// Information about the framebuffer, including layout and pixel format. + /// Information about the framebuffer, including layout and pixel format. pub framebuffer_info: FrameBufferInfo, /// Address of the _Root System Description Pointer_ structure of the ACPI standard. pub rsdp_addr: Option, @@ -325,11 +325,14 @@ where version_patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(), pre_release: !env!("CARGO_PKG_VERSION_PRE").is_empty(), memory_regions: memory_regions.into(), - framebuffer: mappings.framebuffer.map(|addr| FrameBuffer { - buffer_start: addr.as_u64(), - buffer_byte_len: system_info.framebuffer_info.byte_len, - info: system_info.framebuffer_info, - }).into(), + framebuffer: mappings + .framebuffer + .map(|addr| FrameBuffer { + buffer_start: addr.as_u64(), + buffer_byte_len: system_info.framebuffer_info.byte_len, + info: system_info.framebuffer_info, + }) + .into(), physical_memory_offset: mappings.physical_memory_offset.map(VirtAddr::as_u64).into(), recursive_index: mappings.recursive_index.map(Into::into).into(), rsdp_addr: system_info.rsdp_addr.map(|addr| addr.as_u64()).into(), @@ -421,7 +424,7 @@ unsafe fn context_switch( unreachable!(); } -/// Memory addresses required for the context switch. +/// Memory addresses required for the context switch. struct Addresses { page_table: PhysFrame, stack_top: VirtAddr, diff --git a/src/boot_info.rs b/src/boot_info.rs index abb8f1f7..79f4d124 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -83,7 +83,7 @@ impl ops::Deref for MemoryRegions { impl ops::DerefMut for MemoryRegions { fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { slice::from_raw_parts_mut(self.ptr, self.len )} + unsafe { slice::from_raw_parts_mut(self.ptr, self.len) } } } @@ -98,7 +98,7 @@ impl From<&'static mut [MemoryRegion]> for MemoryRegions { impl Into<&'static mut [MemoryRegion]> for MemoryRegions { fn into(self) -> &'static mut [MemoryRegion] { - unsafe { slice::from_raw_parts_mut(self.ptr, self.len )} + unsafe { slice::from_raw_parts_mut(self.ptr, self.len) } } } @@ -133,7 +133,7 @@ impl FrameBuffer { pub struct FrameBufferInfo { /// The total size in bytes. pub byte_len: usize, - /// The width in pixels. + /// The width in pixels. pub horizontal_resolution: usize, /// The height in pixels. pub vertical_resolution: usize, @@ -149,7 +149,7 @@ pub struct FrameBufferInfo { pub stride: usize, } -/// Color format of pixels in the framebuffer. +/// Color format of pixels in the framebuffer. #[derive(Debug, Clone, Copy)] #[non_exhaustive] #[repr(C)] diff --git a/src/config.rs b/src/config.rs index 3d963c0c..e6d64cde 100644 --- a/src/config.rs +++ b/src/config.rs @@ -19,7 +19,7 @@ pub struct Config { /// Defaults to `false`. pub map_page_table_recursively: bool, /// Create the recursive mapping in at the given entry of the level 4 page table. - /// + /// /// If not given, the bootloader searches for a free level 4 entry dynamically. /// /// Only considered if `map_page_table_recursively` is `true`. From 44c7616a517bd45c5db9a33a235019b542051e7e Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 10 Jan 2021 17:47:12 +0100 Subject: [PATCH 126/174] Add serial output to test framework For printing error messages. --- Cargo.lock | 11 +++++++++++ tests/default_settings.rs | 1 + tests/test_kernels/default_settings/Cargo.toml | 1 + tests/test_kernels/default_settings/src/lib.rs | 6 ++++++ 4 files changed, 19 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 15303bf2..88acfa7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -144,6 +144,7 @@ name = "kernel" version = "0.1.0" dependencies = [ "bootloader", + "uart_16550", "x86_64 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -290,6 +291,16 @@ dependencies = [ "serde", ] +[[package]] +name = "uart_16550" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb00b16a4c8acbfc4eb8cfeda1f9c0507c7e87c6563edce64a236af93acf70c" +dependencies = [ + "bitflags", + "x86_64 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ucs2" version = "0.3.1" diff --git a/tests/default_settings.rs b/tests/default_settings.rs index edbe95ab..1d009c55 100644 --- a/tests/default_settings.rs +++ b/tests/default_settings.rs @@ -17,5 +17,6 @@ fn run_test_binary(bin_name: &str) { cmd.arg("--bin").arg(bin_name); cmd.arg("--target").arg(" x86_64-example-kernel.json"); cmd.arg("-Zbuild-std=core"); + cmd.arg("-Zbuild-std-features=compiler-builtins-mem"); assert!(cmd.status().unwrap().success()); } diff --git a/tests/test_kernels/default_settings/Cargo.toml b/tests/test_kernels/default_settings/Cargo.toml index 4ee0b366..ba7be44c 100644 --- a/tests/test_kernels/default_settings/Cargo.toml +++ b/tests/test_kernels/default_settings/Cargo.toml @@ -7,3 +7,4 @@ edition = "2018" [dependencies] bootloader = { path = "../../.." } x86_64 = "0.12.2" +uart_16550 = "0.2.10" diff --git a/tests/test_kernels/default_settings/src/lib.rs b/tests/test_kernels/default_settings/src/lib.rs index b399ff58..4e46fdb6 100644 --- a/tests/test_kernels/default_settings/src/lib.rs +++ b/tests/test_kernels/default_settings/src/lib.rs @@ -19,3 +19,9 @@ pub fn exit_qemu(exit_code: QemuExitCode) -> ! { nop(); } } + +pub fn serial() -> uart_16550::SerialPort { + let mut port = unsafe { uart_16550::SerialPort::new(0x3F8) }; + port.init(); + port +} From 3aafb30a40126ff247f4be22728b24c2aebbd3cf Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 10 Jan 2021 17:47:43 +0100 Subject: [PATCH 127/174] Add method to get immutable slice of framebuffer bytes --- src/boot_info.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/boot_info.rs b/src/boot_info.rs index 79f4d124..040ff2f7 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -112,8 +112,13 @@ pub struct FrameBuffer { } impl FrameBuffer { - /// Returns the raw bytes of the framebuffer. - pub fn buffer(&mut self) -> &mut [u8] { + /// Returns the raw bytes of the framebuffer as slice. + pub fn buffer(&self) -> &[u8] { + unsafe { self.create_buffer() } + } + + /// Returns the raw bytes of the framebuffer as mutable slice. + pub fn buffer_mut(&mut self) -> &mut [u8] { unsafe { self.create_buffer() } } From ba526a550e19ff04d750f71108bb5216499fa1a0 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 10 Jan 2021 17:48:03 +0100 Subject: [PATCH 128/174] Implement Eq for PixelFormat --- src/boot_info.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boot_info.rs b/src/boot_info.rs index 040ff2f7..68bd66f9 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -155,7 +155,7 @@ pub struct FrameBufferInfo { } /// Color format of pixels in the framebuffer. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[non_exhaustive] #[repr(C)] pub enum PixelFormat { From 6aaf882c6cdefbf1c5306800a519bf78dbf2ae43 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 10 Jan 2021 17:48:23 +0100 Subject: [PATCH 129/174] Add more conversion methods for `Optional` --- src/boot_info.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/boot_info.rs b/src/boot_info.rs index 68bd66f9..70fca136 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -210,6 +210,35 @@ pub enum Optional { None, } +impl Optional { + /// Converts the `Optional` to an [`Option`]. + pub fn into_option(self) -> Option { + self.into() + } + + /// Converts from `&Optional` to `Option<&T>`. + /// + /// For convenience, this method directly performs the conversion to the standard + /// [`Option`] type. + pub const fn as_ref(&self) -> Option<&T> { + match self { + Self::Some(x) => Some(x), + Self::None => None, + } + } + + /// Converts from `&mut Optional` to `Option<&mut T>`. + /// + /// For convenience, this method directly performs the conversion to the standard + /// [`Option`] type. + pub fn as_mut(&mut self) -> Option<&mut T> { + match self { + Self::Some(x) => Some(x), + Self::None => None, + } + } +} + impl From> for Optional { fn from(v: Option) -> Self { match v { From dc1267b73faeb40fd0ee33f03331f3439f545b34 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 10 Jan 2021 17:48:45 +0100 Subject: [PATCH 130/174] Add a test that checks boot info values --- tests/default_settings.rs | 5 ++ .../src/bin/check_boot_info.rs | 46 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 tests/test_kernels/default_settings/src/bin/check_boot_info.rs diff --git a/tests/default_settings.rs b/tests/default_settings.rs index 1d009c55..ed853331 100644 --- a/tests/default_settings.rs +++ b/tests/default_settings.rs @@ -10,6 +10,11 @@ fn should_panic() { run_test_binary("should_panic"); } +#[test] +fn check_boot_info() { + run_test_binary("check_boot_info"); +} + fn run_test_binary(bin_name: &str) { let mut cmd = Command::new(env!("CARGO")); cmd.current_dir("tests/test_kernels/default_settings"); diff --git a/tests/test_kernels/default_settings/src/bin/check_boot_info.rs b/tests/test_kernels/default_settings/src/bin/check_boot_info.rs new file mode 100644 index 00000000..5ed7fcd0 --- /dev/null +++ b/tests/test_kernels/default_settings/src/bin/check_boot_info.rs @@ -0,0 +1,46 @@ +#![no_std] // don't link the Rust standard library +#![no_main] // disable all Rust-level entry points + +use bootloader::{boot_info::PixelFormat, entry_point, BootInfo}; +use core::panic::PanicInfo; +use kernel::{exit_qemu, QemuExitCode}; + +entry_point!(kernel_main); + +fn kernel_main(boot_info: &'static mut BootInfo) -> ! { + // check memory regions + assert!(boot_info.memory_regions.len() > 4); + + // check framebuffer + let framebuffer = boot_info.framebuffer.as_ref().unwrap(); + assert_eq!(framebuffer.info().byte_len, framebuffer.buffer().len()); + assert_eq!(framebuffer.info().horizontal_resolution, 1024); + assert_eq!(framebuffer.info().vertical_resolution, 768); + assert_eq!(framebuffer.info().bytes_per_pixel, 3); + assert_eq!(framebuffer.info().stride, 1024); + assert_eq!(framebuffer.info().pixel_format, PixelFormat::RGB); + assert_eq!(framebuffer.buffer().len(), 1024 * 768 * 3); + + // check defaults for optional features + assert_eq!(boot_info.physical_memory_offset.into_option(), None); + assert_eq!(boot_info.recursive_index.into_option(), None); + + // check rsdp_addr + let rsdp = boot_info.rsdp_addr.into_option().unwrap(); + assert!(rsdp > 0x000E0000); + assert!(rsdp < 0x000FFFFF); + + // the test kernel has no TLS template + assert_eq!(boot_info.tls_template.into_option(), None); + + exit_qemu(QemuExitCode::Success); +} + +/// This function is called on panic. +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + use core::fmt::Write; + + let _ = writeln!(kernel::serial(), "PANIC: {}", info); + exit_qemu(QemuExitCode::Failed); +} From 2ac0c8260ca2838ec461ea3a390a383f3cc82958 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Jan 2021 16:18:21 +0100 Subject: [PATCH 131/174] Create FAT file system image from `.efi` image Can be run in QEMU through `qemu-system-x86_64 -drive file=bootimage-uefi-blog_os.fat -bios ovmf/OVMF_CODE.fd`. --- Cargo.lock | 102 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 +- src/bin/builder.rs | 94 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 195 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88acfa7e..f7a15157 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1ba68f4276a778591e36a0c348a269888f3a177c8d2054969389e3b59611ff5" +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + [[package]] name = "bit_field" version = "0.9.0" @@ -62,6 +68,7 @@ dependencies = [ "bit_field 0.10.1", "conquer-once", "displaydoc", + "fatfs", "font8x8", "json", "llvm-tools", @@ -86,12 +93,31 @@ dependencies = [ "json", ] +[[package]] +name = "byteorder" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" + [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + [[package]] name = "conquer-once" version = "0.2.1" @@ -118,6 +144,18 @@ dependencies = [ "syn", ] +[[package]] +name = "fatfs" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93079df23039e52059e1f03b4c29fb0c72da2c792aad91bb2236c9fb81d3592e" +dependencies = [ + "bitflags", + "byteorder", + "chrono", + "log", +] + [[package]] name = "font8x8" version = "0.2.5" @@ -148,6 +186,12 @@ dependencies = [ "x86_64 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "libc" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" + [[package]] name = "llvm-tools" version = "0.1.1" @@ -181,6 +225,25 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "proc-macro2" version = "1.0.24" @@ -282,6 +345,17 @@ dependencies = [ "syn", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + [[package]] name = "toml" version = "0.5.6" @@ -351,6 +425,34 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "x86_64" version = "0.12.2" diff --git a/Cargo.toml b/Cargo.toml index dd5828b7..29b4a372 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ llvm-tools = { version = "0.1.1", optional = true } thiserror = { version = "1.0.20", optional = true } json = { version = "0.12.4", optional = true } rsdp = { version = "1.0.0", optional = true } +fatfs = { version = "0.3.4", optional = true } [dependencies.font8x8] version = "0.2.5" @@ -60,7 +61,7 @@ serde = { version = "1.0", features = ["derive"], optional = true} [features] default = [] -builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json"] +builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json", "fatfs"] runner = ["anyhow"] bios_bin = ["binary", "vga_320x200", "rsdp"] uefi_bin = ["binary", "uefi", "font8x8"] diff --git a/src/bin/builder.rs b/src/bin/builder.rs index 10c43c32..640d9d19 100644 --- a/src/bin/builder.rs +++ b/src/bin/builder.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Context}; use argh::FromArgs; use bootloader::disk_image::create_disk_image; use std::{ - fs, + fs, io, path::{Path, PathBuf}, process::Command, str::FromStr, @@ -66,6 +66,22 @@ impl FromStr for Firmware { } } +impl Firmware { + fn uefi(&self) -> bool { + match self { + Firmware::Bios => false, + Firmware::Uefi | Firmware::All => true, + } + } + + fn bios(&self) -> bool { + match self { + Firmware::Bios | Firmware::All => true, + Firmware::Uefi => false, + } + } +} + /// Firmware must be one of `uefi`, `bios`, or `all`. #[derive(Debug, displaydoc::Display, Eq, PartialEq, Copy, Clone)] struct FirmwareParseError; @@ -73,7 +89,7 @@ struct FirmwareParseError; fn main() -> anyhow::Result<()> { let args: BuildArguments = argh::from_env(); - if args.firmware == Firmware::Uefi || args.firmware == Firmware::All { + if args.firmware.uefi() { let build_or_run = if args.run { "run" } else { "build" }; let mut cmd = Command::new(env!("CARGO")); cmd.arg(build_or_run).arg("--bin").arg("uefi"); @@ -91,9 +107,81 @@ fn main() -> anyhow::Result<()> { cmd.env("KERNEL", &args.kernel_binary); cmd.env("KERNEL_MANIFEST", &args.kernel_manifest); assert!(cmd.status()?.success()); + + // Retrieve binary paths + cmd.arg("--message-format").arg("json"); + let output = cmd + .output() + .context("failed to execute kernel build with json output")?; + if !output.status.success() { + return Err(anyhow!("{}", String::from_utf8_lossy(&output.stderr))); + } + let mut executables = Vec::new(); + for line in String::from_utf8(output.stdout) + .context("build JSON output is not valid UTF-8")? + .lines() + { + let mut artifact = json::parse(line).context("build JSON output is not valid JSON")?; + if let Some(executable) = artifact["executable"].take_string() { + executables.push(PathBuf::from(executable)); + } + } + + assert_eq!(executables.len(), 1); + let executable_path = executables.pop().unwrap(); + let executable_name = executable_path.file_stem().unwrap().to_str().unwrap(); + let kernel_name = args.kernel_binary.file_name().unwrap().to_str().unwrap(); + + if let Some(out_dir) = &args.out_dir { + let efi_file = + out_dir.join(format!("bootimage-{}-{}.efi", executable_name, kernel_name)); + fs::copy(&executable_path, &efi_file).context("failed to copy efi file to out dir")?; + + let efi_size = fs::metadata(&efi_file) + .context("failed to read metadata of efi file")? + .len(); + + // create fat partition + { + const MB: u64 = 1024 * 1024; + + let fat_path = efi_file.with_extension("fat"); + dbg!(&fat_path); + let fat_file = fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&fat_path) + .context("Failed to create UEFI FAT file")?; + let efi_size_rounded = ((efi_size - 1) / MB + 1) * MB; + fat_file + .set_len(dbg!(efi_size_rounded)) + .context("failed to set UEFI FAT file length")?; + + // create new FAT partition + fatfs::format_volume(&fat_file, fatfs::FormatVolumeOptions::new()) + .context("Failed to format UEFI FAT file")?; + + // copy EFI file to FAT filesystem + let partition = fatfs::FileSystem::new(&fat_file, fatfs::FsOptions::new()) + .context("Failed to open FAT file system of UEFI FAT file")?; + let root_dir = partition.root_dir(); + root_dir.create_dir("efi")?; + root_dir.create_dir("efi/boot")?; + let mut bootx64 = root_dir.create_file("efi/boot/bootx64.efi")?; + bootx64.truncate()?; + io::copy(&mut fs::File::open(&executable_path)?, &mut bootx64)?; + } + + // create gpt disk + { + //todo!() + } + } } - if args.firmware == Firmware::Bios || args.firmware == Firmware::All { + if args.firmware.bios() { let mut cmd = Command::new(env!("CARGO")); cmd.arg("build").arg("--bin").arg("bios"); cmd.arg("--profile").arg("release"); From a818c270f789aca301e4e6e06613dcd48e4e94a0 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 14 Jan 2021 13:31:47 +0100 Subject: [PATCH 132/174] Implementing `From` for foreign types is actually possible The new orphan rules allow this. Since `Into` has a blanked impl for `From`, but not the other way around, it is recommended to always implement `From` if possible. --- src/boot_info.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/boot_info.rs b/src/boot_info.rs index 70fca136..d5b0afd5 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -96,9 +96,9 @@ impl From<&'static mut [MemoryRegion]> for MemoryRegions { } } -impl Into<&'static mut [MemoryRegion]> for MemoryRegions { - fn into(self) -> &'static mut [MemoryRegion] { - unsafe { slice::from_raw_parts_mut(self.ptr, self.len) } +impl From for &'static mut [MemoryRegion] { + fn from(regions: MemoryRegions) -> &'static mut [MemoryRegion] { + unsafe { slice::from_raw_parts_mut(regions.ptr, regions.len) } } } @@ -248,9 +248,9 @@ impl From> for Optional { } } -impl Into> for Optional { - fn into(self) -> Option { - match self { +impl From> for Option { + fn from(optional: Optional) -> Option { + match optional { Optional::Some(v) => Some(v), Optional::None => None, } From f20ac539c8e7e266e1d6b49c5646150ff0967e03 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 14 Jan 2021 13:31:58 +0100 Subject: [PATCH 133/174] Remove bors from this repo --- bors.toml | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 bors.toml diff --git a/bors.toml b/bors.toml deleted file mode 100644 index c8009028..00000000 --- a/bors.toml +++ /dev/null @@ -1,4 +0,0 @@ -status = [ - "Test", "Build Example Kernel", "Check Formatting" -] -delete_merged_branches = true From c7828d311f25acc4b9929ce80aadbea197cf5dd0 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 14 Jan 2021 14:35:47 +0100 Subject: [PATCH 134/174] Create a GPT disk image with an UEFI boot partition --- Cargo.lock | 56 ++++++++++++- Cargo.toml | 3 +- src/bin/builder.rs | 201 ++++++++++++++++++++++++++++++++++----------- 3 files changed, 210 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f7a15157..34f357d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,6 +70,7 @@ dependencies = [ "displaydoc", "fatfs", "font8x8", + "gpt", "json", "llvm-tools", "log", @@ -93,6 +94,12 @@ dependencies = [ "json", ] +[[package]] +name = "build_const" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" + [[package]] name = "byteorder" version = "1.4.2" @@ -105,6 +112,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "chrono" version = "0.4.19" @@ -133,6 +146,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "654fb2472cc369d311c547103a1fa81d467bef370ae7a0680f65939895b1182a" +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +dependencies = [ + "build_const", +] + [[package]] name = "displaydoc" version = "0.1.7" @@ -162,6 +184,29 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44226c40489fb1d602344a1d8f1b544570c3435e396dda1eda7b5ef010d8f1be" +[[package]] +name = "getrandom" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4060f4657be78b8e766215b02b18a2e862d83745545de804638e2b545e81aee6" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "gpt" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b0e3659ffee31427c4aaa68c1e5115c8c86ba71ff11da5a16e5b70f3471d" +dependencies = [ + "bitflags", + "crc", + "log", + "uuid", +] + [[package]] name = "heck" version = "0.3.1" @@ -222,7 +267,7 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", ] [[package]] @@ -425,6 +470,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", +] + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 29b4a372..65d94f42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ thiserror = { version = "1.0.20", optional = true } json = { version = "0.12.4", optional = true } rsdp = { version = "1.0.0", optional = true } fatfs = { version = "0.3.4", optional = true } +gpt = { version = "2.0.0", optional = true } [dependencies.font8x8] version = "0.2.5" @@ -61,7 +62,7 @@ serde = { version = "1.0", features = ["derive"], optional = true} [features] default = [] -builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json", "fatfs"] +builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json", "fatfs", "gpt"] runner = ["anyhow"] bios_bin = ["binary", "vga_320x200", "rsdp"] uefi_bin = ["binary", "uefi", "font8x8"] diff --git a/src/bin/builder.rs b/src/bin/builder.rs index 640d9d19..f4907e51 100644 --- a/src/bin/builder.rs +++ b/src/bin/builder.rs @@ -1,8 +1,10 @@ -use anyhow::{anyhow, Context}; +use anyhow::{anyhow, bail, Context}; use argh::FromArgs; use bootloader::disk_image::create_disk_image; use std::{ - fs, io, + convert::TryFrom, + fs::{self, File}, + io::{self, Seek}, path::{Path, PathBuf}, process::Command, str::FromStr, @@ -129,55 +131,32 @@ fn main() -> anyhow::Result<()> { assert_eq!(executables.len(), 1); let executable_path = executables.pop().unwrap(); - let executable_name = executable_path.file_stem().unwrap().to_str().unwrap(); - let kernel_name = args.kernel_binary.file_name().unwrap().to_str().unwrap(); + + let executable_name = executable_path + .file_stem() + .and_then(|stem| stem.to_str()) + .ok_or_else(|| { + anyhow!( + "executable path `{}` has invalid file stem", + executable_path.display() + ) + })?; + let kernel_name = args + .kernel_binary + .file_name() + .and_then(|name| name.to_str()) + .ok_or_else(|| { + anyhow!( + "kernel binary path `{}` has invalid file name", + args.kernel_binary.display() + ) + })?; if let Some(out_dir) = &args.out_dir { let efi_file = out_dir.join(format!("bootimage-{}-{}.efi", executable_name, kernel_name)); - fs::copy(&executable_path, &efi_file).context("failed to copy efi file to out dir")?; - - let efi_size = fs::metadata(&efi_file) - .context("failed to read metadata of efi file")? - .len(); - - // create fat partition - { - const MB: u64 = 1024 * 1024; - - let fat_path = efi_file.with_extension("fat"); - dbg!(&fat_path); - let fat_file = fs::OpenOptions::new() - .read(true) - .write(true) - .create(true) - .truncate(true) - .open(&fat_path) - .context("Failed to create UEFI FAT file")?; - let efi_size_rounded = ((efi_size - 1) / MB + 1) * MB; - fat_file - .set_len(dbg!(efi_size_rounded)) - .context("failed to set UEFI FAT file length")?; - - // create new FAT partition - fatfs::format_volume(&fat_file, fatfs::FormatVolumeOptions::new()) - .context("Failed to format UEFI FAT file")?; - - // copy EFI file to FAT filesystem - let partition = fatfs::FileSystem::new(&fat_file, fatfs::FsOptions::new()) - .context("Failed to open FAT file system of UEFI FAT file")?; - let root_dir = partition.root_dir(); - root_dir.create_dir("efi")?; - root_dir.create_dir("efi/boot")?; - let mut bootx64 = root_dir.create_file("efi/boot/bootx64.efi")?; - bootx64.truncate()?; - io::copy(&mut fs::File::open(&executable_path)?, &mut bootx64)?; - } - - // create gpt disk - { - //todo!() - } + create_uefi_disk_image(&executable_path, &efi_file) + .context("failed to create UEFI disk image")?; } } @@ -228,7 +207,7 @@ fn main() -> anyhow::Result<()> { let mut output_bin_path = executable_path .parent() .unwrap() - .join(format!("bootimage-{}-{}.bin", executable_name, kernel_name)); + .join(format!("bootimage-{}-{}.img", executable_name, kernel_name)); create_disk_image(&executable_path, &output_bin_path) .context("Failed to create bootable disk image")?; @@ -254,6 +233,132 @@ fn main() -> anyhow::Result<()> { Ok(()) } +fn create_uefi_disk_image(executable_path: &Path, efi_file: &Path) -> anyhow::Result<()> { + fs::copy(&executable_path, &efi_file).context("failed to copy efi file to out dir")?; + + let efi_size = fs::metadata(&efi_file) + .context("failed to read metadata of efi file")? + .len(); + + // create fat partition + let fat_file_path = { + const MB: u64 = 1024 * 1024; + + let fat_path = efi_file.with_extension("fat"); + let fat_file = fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&fat_path) + .context("Failed to create UEFI FAT file")?; + let efi_size_rounded = ((efi_size - 1) / MB + 1) * MB; + fat_file + .set_len(efi_size_rounded) + .context("failed to set UEFI FAT file length")?; + + // create new FAT partition + let format_options = fatfs::FormatVolumeOptions::new().volume_label(*b"FOOO "); + fatfs::format_volume(&fat_file, format_options) + .context("Failed to format UEFI FAT file")?; + + // copy EFI file to FAT filesystem + let partition = fatfs::FileSystem::new(&fat_file, fatfs::FsOptions::new()) + .context("Failed to open FAT file system of UEFI FAT file")?; + let root_dir = partition.root_dir(); + root_dir.create_dir("efi")?; + root_dir.create_dir("efi/boot")?; + let mut bootx64 = root_dir.create_file("efi/boot/bootx64.efi")?; + bootx64.truncate()?; + io::copy(&mut fs::File::open(&executable_path)?, &mut bootx64)?; + + fat_path + }; + + // create gpt disk + { + let image_path = efi_file.with_extension("img"); + let mut image = fs::OpenOptions::new() + .create(true) + .truncate(true) + .read(true) + .write(true) + .open(&image_path) + .context("failed to create UEFI disk image")?; + + let partition_size: u64 = fs::metadata(&fat_file_path) + .context("failed to read metadata of UEFI FAT partition")? + .len(); + let image_size = partition_size + 1024 * 64; + image + .set_len(image_size) + .context("failed to set length of UEFI disk image")?; + + // Create a protective MBR at LBA0 + let mbr = gpt::mbr::ProtectiveMBR::with_lb_size( + u32::try_from((image_size / 512) - 1).unwrap_or(0xFF_FF_FF_FF), + ); + mbr.overwrite_lba0(&mut image) + .context("failed to write protective MBR")?; + + // create new GPT in image file + let block_size = gpt::disk::LogicalBlockSize::Lb512; + let block_size_bytes: u64 = block_size.into(); + let mut disk = gpt::GptConfig::new() + .writable(true) + .initialized(false) + .logical_block_size(block_size) + .create_from_device(Box::new(&mut image), None) + .context("failed to open UEFI disk image")?; + disk.update_partitions(Default::default()) + .context("failed to initialize GPT partition table")?; + + // add add EFI system partition + let partition_id = disk + .add_partition("boot", partition_size, gpt::partition_types::EFI, 0) + .context("failed to add boot partition")?; + + let partition = disk + .partitions() + .get(&partition_id) + .ok_or_else(|| anyhow!("Partition doesn't exist after adding it"))?; + let created_partition_size: u64 = + (partition.last_lba - partition.first_lba + 1u64) * block_size_bytes; + if created_partition_size != partition_size { + bail!( + "Created partition has invalid size (size is {:?}, expected {})", + created_partition_size, + partition_size + ); + } + let start_offset = partition + .bytes_start(block_size) + .context("failed to retrieve partition start offset")?; + + // Write the partition table + disk.write() + .context("failed to write GPT partition table to UEFI image file")?; + + image + .seek(io::SeekFrom::Start(start_offset)) + .context("failed to seek to boot partiiton start")?; + let bytes_written = io::copy( + &mut File::open(&fat_file_path).context("failed to open fat image")?, + &mut image, + ) + .context("failed to write boot partition content")?; + if bytes_written != partition_size { + bail!( + "Invalid number of partition bytes written (expected {}, got {})", + partition_size, + bytes_written + ); + } + } + + Ok(()) +} + fn bios_run(bin_path: &Path) -> anyhow::Result> { let mut qemu = Command::new("qemu-system-x86_64"); qemu.arg("-drive") From ea6475935fa73d7d45e76f111b64c93964dc5eb0 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 14 Jan 2021 14:44:06 +0100 Subject: [PATCH 135/174] The disk image extension was changed from `bin` to `img` --- tests/runner/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runner/src/main.rs b/tests/runner/src/main.rs index 9ce1bc26..1af3db65 100644 --- a/tests/runner/src/main.rs +++ b/tests/runner/src/main.rs @@ -66,7 +66,7 @@ pub fn create_disk_image(kernel_binary_path: &Path, bios_only: bool) -> PathBuf let disk_image = kernel_binary_path .parent() .unwrap() - .join(format!("bootimage-bios-{}.bin", kernel_binary_name)); + .join(format!("bootimage-bios-{}.img", kernel_binary_name)); if !disk_image.exists() { panic!( "Disk image does not exist at {} after bootloader build", From ca0a90a9a6cc46ae916442f6b0d7c198300c79fc Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 14 Jan 2021 15:33:17 +0100 Subject: [PATCH 136/174] Bump version to 0.10.0-alpha-01 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 34f357d2..7b00a86d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,7 +61,7 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bootloader" -version = "0.9.11" +version = "0.10.0-alpha-01" dependencies = [ "anyhow", "argh", diff --git a/Cargo.toml b/Cargo.toml index 65d94f42..d484075c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bootloader" -version = "0.9.11" +version = "0.10.0-alpha-01" authors = ["Philipp Oppermann "] license = "MIT/Apache-2.0" description = "An experimental pure-Rust x86 bootloader." From 8c82e685f1afc9f1f0767e2da15cb19537f850fe Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 14 Jan 2021 15:38:55 +0100 Subject: [PATCH 137/174] Update to x86_64 0.13.1 --- Cargo.lock | 11 ++++++----- Cargo.toml | 2 +- src/binary/load_kernel.rs | 2 +- src/binary/mod.rs | 2 +- tests/test_kernels/default_settings/Cargo.toml | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b00a86d..ffa36419 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,7 +81,7 @@ dependencies = [ "toml", "uefi", "usize_conversions", - "x86_64 0.12.2 (git+https://github.com/rust-osdev/x86_64.git)", + "x86_64 0.13.1", "xmas-elf", ] @@ -228,7 +228,7 @@ version = "0.1.0" dependencies = [ "bootloader", "uart_16550", - "x86_64 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "x86_64 0.13.1", ] [[package]] @@ -417,7 +417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afb00b16a4c8acbfc4eb8cfeda1f9c0507c7e87c6563edce64a236af93acf70c" dependencies = [ "bitflags", - "x86_64 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "x86_64 0.12.2", ] [[package]] @@ -519,8 +519,9 @@ dependencies = [ [[package]] name = "x86_64" -version = "0.12.2" -source = "git+https://github.com/rust-osdev/x86_64.git#b6ad4d63408b0a2779e1dd9f236a8bf43fc92fd5" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d74944372d63f31dd39fce1ef8036143484ff4cd4efcd743ef50135839de4e7" dependencies = [ "bit_field 0.9.0", "bitflags", diff --git a/Cargo.toml b/Cargo.toml index d484075c..22fe6ebd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ required-features = ["uefi_bin"] [dependencies] xmas-elf = { version = "0.6.2", optional = true } -x86_64 = { git = "https://github.com/rust-osdev/x86_64.git", optional = true } +x86_64 = { version = "0.13.1", optional = true } usize_conversions = { version = "0.2.0", optional = true } bit_field = { version = "0.10.0", optional = true } log = { version = "0.4.8", optional = true } diff --git a/src/binary/load_kernel.rs b/src/binary/load_kernel.rs index bc0fa812..1e0870c5 100644 --- a/src/binary/load_kernel.rs +++ b/src/binary/load_kernel.rs @@ -5,7 +5,7 @@ use crate::{ use x86_64::{ align_up, structures::paging::{ - FrameAllocator, MapperAllSizes, Page, PageSize, PageTableFlags as Flags, PhysFrame, + mapper::MapperAllSizes, FrameAllocator, Page, PageSize, PageTableFlags as Flags, PhysFrame, Size4KiB, }, PhysAddr, VirtAddr, diff --git a/src/binary/mod.rs b/src/binary/mod.rs index f1457a13..96c068a1 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -398,7 +398,7 @@ unsafe fn context_switch( ) -> ! { // identity-map current and next frame, so that we don't get an immediate pagefault // after switching the active page table - let current_addr = PhysAddr::new(registers::read_rip()); + let current_addr = PhysAddr::new(registers::read_rip().as_u64()); let current_frame: PhysFrame = PhysFrame::containing_address(current_addr); for frame in PhysFrame::range_inclusive(current_frame, current_frame + 1) { unsafe { diff --git a/tests/test_kernels/default_settings/Cargo.toml b/tests/test_kernels/default_settings/Cargo.toml index ba7be44c..eddadcf2 100644 --- a/tests/test_kernels/default_settings/Cargo.toml +++ b/tests/test_kernels/default_settings/Cargo.toml @@ -6,5 +6,5 @@ edition = "2018" [dependencies] bootloader = { path = "../../.." } -x86_64 = "0.12.2" +x86_64 = "0.13.1" uart_16550 = "0.2.10" From ec33e725a12e0ef5de23251e1533011f12c04185 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 15 Jan 2021 18:35:32 +0100 Subject: [PATCH 138/174] Build uefi binary with optimizations --- src/bin/builder.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bin/builder.rs b/src/bin/builder.rs index f4907e51..365c3b7a 100644 --- a/src/bin/builder.rs +++ b/src/bin/builder.rs @@ -95,6 +95,7 @@ fn main() -> anyhow::Result<()> { let build_or_run = if args.run { "run" } else { "build" }; let mut cmd = Command::new(env!("CARGO")); cmd.arg(build_or_run).arg("--bin").arg("uefi"); + cmd.arg("--release"); cmd.arg("--target").arg("x86_64-unknown-uefi"); cmd.arg("--features") .arg(args.features.join(" ") + " uefi_bin"); From 14c4e62adb6e05128755646d7fd5f6990a2385c9 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 15 Jan 2021 18:36:06 +0100 Subject: [PATCH 139/174] Only copy first level 4 entry to bootloader page table --- src/bin/uefi.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 3ba44456..71e3bcb2 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -84,17 +84,29 @@ fn create_page_tables( // copy the currently active level 4 page table, because it might be read-only log::trace!("switching to new level 4 table"); let bootloader_page_table = { - let old_frame = x86_64::registers::control::Cr3::read().0; - let old_table: *const PageTable = - (phys_offset + old_frame.start_address().as_u64()).as_ptr(); + let old_table = { + let frame = x86_64::registers::control::Cr3::read().0; + let ptr: *const PageTable = (phys_offset + frame.start_address().as_u64()).as_ptr(); + unsafe { &*ptr } + }; let new_frame = frame_allocator .allocate_frame() .expect("Failed to allocate frame for new level 4 table"); - let new_table: *mut PageTable = - (phys_offset + new_frame.start_address().as_u64()).as_mut_ptr(); - // copy the table to the new frame - unsafe { core::ptr::copy_nonoverlapping(old_table, new_table, 1) }; - // the tables are now identical, so we can just load the new one + let new_table: &mut PageTable = { + let ptr: *mut PageTable = + (phys_offset + new_frame.start_address().as_u64()).as_mut_ptr(); + // create a new, empty page table + unsafe { + ptr.write(PageTable::new()); + &mut *ptr + } + }; + + // copy the first entry (we don't need to access more than 512 GiB; also, some UEFI + // implementations seem to create an level 4 table entry 0 in all slots) + new_table[0] = old_table[0].clone(); + + // the first level 4 table entry is now identical, so we can just load the new one unsafe { x86_64::registers::control::Cr3::write( new_frame, From 3c80c4137fcd052d06de6794d469646388d825df Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 15 Jan 2021 18:44:35 +0100 Subject: [PATCH 140/174] Fix: Don't use `MemoryType::custom` for matching against known types --- src/binary/legacy_memory_region.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/binary/legacy_memory_region.rs b/src/binary/legacy_memory_region.rs index 7fc8934f..74c79915 100644 --- a/src/binary/legacy_memory_region.rs +++ b/src/binary/legacy_memory_region.rs @@ -130,7 +130,7 @@ where #[cfg(feature = "uefi_bin")] MemoryRegionKind::UnknownUefi(other) => { use uefi::table::boot::MemoryType as M; - match M::custom(other) { + match M(other) { M::LOADER_CODE | M::LOADER_DATA | M::BOOT_SERVICES_CODE From 83ad380cdb376cab0bd70e154c70b571a283030f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 8 Feb 2021 11:24:47 +0100 Subject: [PATCH 141/174] Update to latest x86_64 and uart_16550 versions --- Cargo.lock | 20 +++++-------------- .../test_kernels/default_settings/Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60a15250..0179ea40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,7 +81,7 @@ dependencies = [ "toml", "uefi", "usize_conversions", - "x86_64 0.13.1", + "x86_64", "xmas-elf", ] @@ -228,7 +228,7 @@ version = "0.1.0" dependencies = [ "bootloader", "uart_16550", - "x86_64 0.13.1", + "x86_64", ] [[package]] @@ -412,12 +412,12 @@ dependencies = [ [[package]] name = "uart_16550" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb00b16a4c8acbfc4eb8cfeda1f9c0507c7e87c6563edce64a236af93acf70c" +checksum = "8d45a3c9181dc9ba7d35d02b3c36d1604155289d935b7946510fb3d2f4b976d9" dependencies = [ "bitflags", - "x86_64 0.12.2", + "x86_64", ] [[package]] @@ -517,16 +517,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "x86_64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d74944372d63f31dd39fce1ef8036143484ff4cd4efcd743ef50135839de4e7" -dependencies = [ - "bit_field 0.9.0", - "bitflags", -] - [[package]] name = "xmas-elf" version = "0.6.2" diff --git a/tests/test_kernels/default_settings/Cargo.toml b/tests/test_kernels/default_settings/Cargo.toml index eddadcf2..ba844483 100644 --- a/tests/test_kernels/default_settings/Cargo.toml +++ b/tests/test_kernels/default_settings/Cargo.toml @@ -6,5 +6,5 @@ edition = "2018" [dependencies] bootloader = { path = "../../.." } -x86_64 = "0.13.1" +x86_64 = "0.13.2" uart_16550 = "0.2.10" From 7d5adcf192a6d50318c985c54491416c25a482c4 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 8 Feb 2021 11:41:27 +0100 Subject: [PATCH 142/174] Bump version to 0.10.0-alpha-02 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0179ea40..7e84792f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,7 +61,7 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bootloader" -version = "0.10.0-alpha-01" +version = "0.10.0-alpha-02" dependencies = [ "anyhow", "argh", diff --git a/Cargo.toml b/Cargo.toml index 7500ffc6..098c7e62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bootloader" -version = "0.10.0-alpha-01" +version = "0.10.0-alpha-02" authors = ["Philipp Oppermann "] license = "MIT/Apache-2.0" description = "An experimental pure-Rust x86 bootloader." From f43bd4c10edd6bd5d88b32c95bbd31357fbb75ab Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 8 Feb 2021 11:46:43 +0100 Subject: [PATCH 143/174] Fix `non_fmt_panic` warning --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 3c797f28..216dfa45 100644 --- a/build.rs +++ b/build.rs @@ -63,7 +63,7 @@ mod binary { // check that the kernel file exists assert!( kernel.exists(), - format!("KERNEL does not exist: {}", kernel.display()) + "KERNEL does not exist: {}", kernel.display() ); // get access to llvm tools shipped in the llvm-tools-preview rustup component From f2383374ba0626b1d3a840f4c7e583fdcba5a51a Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 8 Feb 2021 12:21:27 +0100 Subject: [PATCH 144/174] Run cargo fmt --- build.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 216dfa45..c36dd8a3 100644 --- a/build.rs +++ b/build.rs @@ -63,7 +63,8 @@ mod binary { // check that the kernel file exists assert!( kernel.exists(), - "KERNEL does not exist: {}", kernel.display() + "KERNEL does not exist: {}", + kernel.display() ); // get access to llvm tools shipped in the llvm-tools-preview rustup component From d55f1c87c34e8bba61adc6abffa78ba431aac69f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 21 Feb 2021 13:31:06 +0100 Subject: [PATCH 145/174] Improve reporting of config parse errors Instead of panicking in the build script, emit a `compile_error!` invocation so that error is shown as normal compilation error. Also, include the path to the Cargo.toml where the error occurred. --- build.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/build.rs b/build.rs index c36dd8a3..0fd34f44 100644 --- a/build.rs +++ b/build.rs @@ -196,7 +196,7 @@ mod binary { } // Parse configuration from the kernel's Cargo.toml - let config: Config = match env::var("KERNEL_MANIFEST") { + let config = 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."); @@ -223,7 +223,15 @@ mod binary { .cloned() .unwrap_or_else(|| toml::Value::Table(toml::map::Map::new())); - config_table.try_into().expect("failed to parse config") + config_table + .try_into::() + .map(|c| format!("{:?}", c)) + .unwrap_or_else(|err| { + format!( + "compile_error!(\"failed to parse bootloader config in {}:\n\n{}\")", + path,err.to_string().escape_default(), + ) + }) } }; @@ -234,7 +242,7 @@ mod binary { format!( "mod parsed_config {{ use crate::config::Config; - pub const CONFIG: Config = {:?}; + pub const CONFIG: Config = {}; }}", config, ) From 2511448ea4817a02bc22e662ebaae377e820c82b Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 21 Feb 2021 13:31:31 +0100 Subject: [PATCH 146/174] Make doc test runnable --- src/boot_info.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/boot_info.rs b/src/boot_info.rs index d5b0afd5..b4ce688b 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -6,8 +6,10 @@ use core::{ops, slice}; /// The information is passed as an argument to the entry point. The entry point function must /// have the following signature: /// -/// ```ignore -/// pub extern "C" fn(boot_info: &'static BootInfo) -> !; +/// ``` +/// # use bootloader::BootInfo; +/// # type _SIGNATURE = +/// extern "C" fn(boot_info: &'static BootInfo) -> !; /// ``` /// /// Note that no type checking occurs for the entry point function, so be careful to From d2053b0d37a4aa2b435eaba96646ff18e3ee2caf Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 21 Feb 2021 13:32:56 +0100 Subject: [PATCH 147/174] Rename default_settings test kernel and target file This ensures that there are no conflicts with other test kernels. --- Cargo.lock | 20 ++++++++++--------- tests/default_settings.rs | 2 +- .../test_kernels/default_settings/Cargo.toml | 2 +- .../default_settings/src/bin/basic_boot.rs | 2 +- .../src/bin/check_boot_info.rs | 4 ++-- .../default_settings/src/bin/should_panic.rs | 2 +- ...rnel.json => x86_64-default_settings.json} | 0 7 files changed, 17 insertions(+), 15 deletions(-) rename tests/test_kernels/default_settings/{x86_64-example-kernel.json => x86_64-default_settings.json} (100%) diff --git a/Cargo.lock b/Cargo.lock index 7e84792f..e4201596 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "anyhow" version = "1.0.32" @@ -222,15 +224,6 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" -[[package]] -name = "kernel" -version = "0.1.0" -dependencies = [ - "bootloader", - "uart_16550", - "x86_64", -] - [[package]] name = "libc" version = "0.2.82" @@ -370,6 +363,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "test_kernel_default_settings" +version = "0.1.0" +dependencies = [ + "bootloader", + "uart_16550", + "x86_64", +] + [[package]] name = "thiserror" version = "1.0.20" diff --git a/tests/default_settings.rs b/tests/default_settings.rs index ed853331..cf0a817a 100644 --- a/tests/default_settings.rs +++ b/tests/default_settings.rs @@ -20,7 +20,7 @@ fn run_test_binary(bin_name: &str) { cmd.current_dir("tests/test_kernels/default_settings"); cmd.arg("run"); cmd.arg("--bin").arg(bin_name); - cmd.arg("--target").arg(" x86_64-example-kernel.json"); + cmd.arg("--target").arg("x86_64-default_settings.json"); cmd.arg("-Zbuild-std=core"); cmd.arg("-Zbuild-std-features=compiler-builtins-mem"); assert!(cmd.status().unwrap().success()); diff --git a/tests/test_kernels/default_settings/Cargo.toml b/tests/test_kernels/default_settings/Cargo.toml index ba844483..2a4659c6 100644 --- a/tests/test_kernels/default_settings/Cargo.toml +++ b/tests/test_kernels/default_settings/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "kernel" +name = "test_kernel_default_settings" version = "0.1.0" authors = ["Philipp Oppermann "] edition = "2018" diff --git a/tests/test_kernels/default_settings/src/bin/basic_boot.rs b/tests/test_kernels/default_settings/src/bin/basic_boot.rs index 19084b97..50157689 100644 --- a/tests/test_kernels/default_settings/src/bin/basic_boot.rs +++ b/tests/test_kernels/default_settings/src/bin/basic_boot.rs @@ -3,7 +3,7 @@ use bootloader::{entry_point, BootInfo}; use core::panic::PanicInfo; -use kernel::{exit_qemu, QemuExitCode}; +use test_kernel_default_settings::{exit_qemu, QemuExitCode}; entry_point!(kernel_main); diff --git a/tests/test_kernels/default_settings/src/bin/check_boot_info.rs b/tests/test_kernels/default_settings/src/bin/check_boot_info.rs index 5ed7fcd0..a2997e6c 100644 --- a/tests/test_kernels/default_settings/src/bin/check_boot_info.rs +++ b/tests/test_kernels/default_settings/src/bin/check_boot_info.rs @@ -3,7 +3,7 @@ use bootloader::{boot_info::PixelFormat, entry_point, BootInfo}; use core::panic::PanicInfo; -use kernel::{exit_qemu, QemuExitCode}; +use test_kernel_default_settings::{exit_qemu, QemuExitCode}; entry_point!(kernel_main); @@ -41,6 +41,6 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! { fn panic(info: &PanicInfo) -> ! { use core::fmt::Write; - let _ = writeln!(kernel::serial(), "PANIC: {}", info); + let _ = writeln!(test_kernel_default_settings::serial(), "PANIC: {}", info); exit_qemu(QemuExitCode::Failed); } diff --git a/tests/test_kernels/default_settings/src/bin/should_panic.rs b/tests/test_kernels/default_settings/src/bin/should_panic.rs index 0205f891..48546907 100644 --- a/tests/test_kernels/default_settings/src/bin/should_panic.rs +++ b/tests/test_kernels/default_settings/src/bin/should_panic.rs @@ -3,7 +3,7 @@ use bootloader::{entry_point, BootInfo}; use core::panic::PanicInfo; -use kernel::{exit_qemu, QemuExitCode}; +use test_kernel_default_settings::{exit_qemu, QemuExitCode}; entry_point!(kernel_main); diff --git a/tests/test_kernels/default_settings/x86_64-example-kernel.json b/tests/test_kernels/default_settings/x86_64-default_settings.json similarity index 100% rename from tests/test_kernels/default_settings/x86_64-example-kernel.json rename to tests/test_kernels/default_settings/x86_64-default_settings.json From 6a0fd74ecb052ef3f1fa7ce3e556c895c66dfc4e Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 21 Feb 2021 13:33:31 +0100 Subject: [PATCH 148/174] Add a test for the `map-physical-memory` config key --- Cargo.lock | 9 ++++ Cargo.toml | 1 + tests/map_phys_mem.rs | 22 +++++++++ .../map_phys_mem/.cargo/config.toml | 10 ++++ tests/test_kernels/map_phys_mem/.gitignore | 1 + tests/test_kernels/map_phys_mem/Cargo.toml | 13 ++++++ .../map_phys_mem/src/bin/access_phys_mem.rs | 26 +++++++++++ .../map_phys_mem/src/bin/check_boot_info.rs | 46 +++++++++++++++++++ tests/test_kernels/map_phys_mem/src/lib.rs | 27 +++++++++++ .../map_phys_mem/x86_64-map_phys_mem.json | 15 ++++++ 10 files changed, 170 insertions(+) create mode 100644 tests/map_phys_mem.rs create mode 100644 tests/test_kernels/map_phys_mem/.cargo/config.toml create mode 100644 tests/test_kernels/map_phys_mem/.gitignore create mode 100644 tests/test_kernels/map_phys_mem/Cargo.toml create mode 100644 tests/test_kernels/map_phys_mem/src/bin/access_phys_mem.rs create mode 100644 tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs create mode 100644 tests/test_kernels/map_phys_mem/src/lib.rs create mode 100644 tests/test_kernels/map_phys_mem/x86_64-map_phys_mem.json diff --git a/Cargo.lock b/Cargo.lock index e4201596..b02f3c6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -372,6 +372,15 @@ dependencies = [ "x86_64", ] +[[package]] +name = "test_kernel_map_phys_mem" +version = "0.1.0" +dependencies = [ + "bootloader", + "uart_16550", + "x86_64", +] + [[package]] name = "thiserror" version = "1.0.20" diff --git a/Cargo.toml b/Cargo.toml index 098c7e62..87d8ac32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ build = "build.rs" members = [ "tests/runner", "tests/test_kernels/default_settings", + "tests/test_kernels/map_phys_mem", ] [[bin]] diff --git a/tests/map_phys_mem.rs b/tests/map_phys_mem.rs new file mode 100644 index 00000000..f7e1b61a --- /dev/null +++ b/tests/map_phys_mem.rs @@ -0,0 +1,22 @@ +use std::process::Command; + +#[test] +fn check_boot_info() { + run_test_binary("check_boot_info"); +} + +#[test] +fn access_phys_mem() { + run_test_binary("access_phys_mem"); +} + +fn run_test_binary(bin_name: &str) { + let mut cmd = Command::new(env!("CARGO")); + cmd.current_dir("tests/test_kernels/map_phys_mem"); + cmd.arg("run"); + cmd.arg("--bin").arg(bin_name); + cmd.arg("--target").arg("x86_64-map_phys_mem.json"); + cmd.arg("-Zbuild-std=core"); + cmd.arg("-Zbuild-std-features=compiler-builtins-mem"); + assert!(cmd.status().unwrap().success()); +} diff --git a/tests/test_kernels/map_phys_mem/.cargo/config.toml b/tests/test_kernels/map_phys_mem/.cargo/config.toml new file mode 100644 index 00000000..08a49f5a --- /dev/null +++ b/tests/test_kernels/map_phys_mem/.cargo/config.toml @@ -0,0 +1,10 @@ +[unstable] +# TODO: Uncomment once https://github.com/rust-lang/cargo/issues/8643 is merged +# build-std = ["core"] + +[build] +# TODO: Uncomment once https://github.com/rust-lang/cargo/issues/8643 is merged +# target = "x86_64-example-kernel.json" + +[target.'cfg(target_os = "none")'] +runner = "cargo run --manifest-path ../../runner/Cargo.toml" \ No newline at end of file diff --git a/tests/test_kernels/map_phys_mem/.gitignore b/tests/test_kernels/map_phys_mem/.gitignore new file mode 100644 index 00000000..1de56593 --- /dev/null +++ b/tests/test_kernels/map_phys_mem/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/tests/test_kernels/map_phys_mem/Cargo.toml b/tests/test_kernels/map_phys_mem/Cargo.toml new file mode 100644 index 00000000..e69fd73d --- /dev/null +++ b/tests/test_kernels/map_phys_mem/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "test_kernel_map_phys_mem" +version = "0.1.0" +authors = ["Philipp Oppermann "] +edition = "2018" + +[dependencies] +bootloader = { path = "../../.." } +x86_64 = "0.13.2" +uart_16550 = "0.2.10" + +[package.metadata.bootloader] +map-physical-memory = true diff --git a/tests/test_kernels/map_phys_mem/src/bin/access_phys_mem.rs b/tests/test_kernels/map_phys_mem/src/bin/access_phys_mem.rs new file mode 100644 index 00000000..5d4bd4cd --- /dev/null +++ b/tests/test_kernels/map_phys_mem/src/bin/access_phys_mem.rs @@ -0,0 +1,26 @@ +#![no_std] // don't link the Rust standard library +#![no_main] // disable all Rust-level entry points + +use bootloader::{entry_point, BootInfo}; +use core::panic::PanicInfo; +use test_kernel_map_phys_mem::{QemuExitCode, exit_qemu, serial}; + +entry_point!(kernel_main); + +fn kernel_main(boot_info: &'static mut BootInfo) -> ! { + let phys_mem_offset = boot_info.physical_memory_offset.into_option().unwrap(); + + let ptr = phys_mem_offset as *const u64; + let _ = unsafe { *ptr }; + + exit_qemu(QemuExitCode::Success); +} + +/// This function is called on panic. +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + use core::fmt::Write; + + let _ = writeln!(serial(), "PANIC: {}", info); + exit_qemu(QemuExitCode::Failed); +} diff --git a/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs b/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs new file mode 100644 index 00000000..e9590217 --- /dev/null +++ b/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs @@ -0,0 +1,46 @@ +#![no_std] // don't link the Rust standard library +#![no_main] // disable all Rust-level entry points + +use bootloader::{boot_info::PixelFormat, entry_point, BootInfo}; +use core::panic::PanicInfo; +use test_kernel_map_phys_mem::{QemuExitCode, exit_qemu, serial}; + +entry_point!(kernel_main); + +fn kernel_main(boot_info: &'static mut BootInfo) -> ! { + // check memory regions + assert!(boot_info.memory_regions.len() > 4); + + // check framebuffer + let framebuffer = boot_info.framebuffer.as_ref().unwrap(); + assert_eq!(framebuffer.info().byte_len, framebuffer.buffer().len()); + assert_eq!(framebuffer.info().horizontal_resolution, 1024); + assert_eq!(framebuffer.info().vertical_resolution, 768); + assert_eq!(framebuffer.info().bytes_per_pixel, 3); + assert_eq!(framebuffer.info().stride, 1024); + assert_eq!(framebuffer.info().pixel_format, PixelFormat::RGB); + assert_eq!(framebuffer.buffer().len(), 1024 * 768 * 3); + + // check defaults for optional features + assert!(matches!(boot_info.physical_memory_offset.into_option(), Some(_))); + assert_eq!(boot_info.recursive_index.into_option(), None); + + // check rsdp_addr + let rsdp = boot_info.rsdp_addr.into_option().unwrap(); + assert!(rsdp > 0x000E0000); + assert!(rsdp < 0x000FFFFF); + + // the test kernel has no TLS template + assert_eq!(boot_info.tls_template.into_option(), None); + + exit_qemu(QemuExitCode::Success); +} + +/// This function is called on panic. +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + use core::fmt::Write; + + let _ = writeln!(serial(), "PANIC: {}", info); + exit_qemu(QemuExitCode::Failed); +} diff --git a/tests/test_kernels/map_phys_mem/src/lib.rs b/tests/test_kernels/map_phys_mem/src/lib.rs new file mode 100644 index 00000000..4e46fdb6 --- /dev/null +++ b/tests/test_kernels/map_phys_mem/src/lib.rs @@ -0,0 +1,27 @@ +#![no_std] + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum QemuExitCode { + Success = 0x10, + Failed = 0x11, +} + +pub fn exit_qemu(exit_code: QemuExitCode) -> ! { + use x86_64::instructions::{nop, port::Port}; + + unsafe { + let mut port = Port::new(0xf4); + port.write(exit_code as u32); + } + + loop { + nop(); + } +} + +pub fn serial() -> uart_16550::SerialPort { + let mut port = unsafe { uart_16550::SerialPort::new(0x3F8) }; + port.init(); + port +} diff --git a/tests/test_kernels/map_phys_mem/x86_64-map_phys_mem.json b/tests/test_kernels/map_phys_mem/x86_64-map_phys_mem.json new file mode 100644 index 00000000..9afe809f --- /dev/null +++ b/tests/test_kernels/map_phys_mem/x86_64-map_phys_mem.json @@ -0,0 +1,15 @@ +{ + "llvm-target": "x86_64-unknown-none", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "arch": "x86_64", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": "32", + "os": "none", + "executables": true, + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "panic-strategy": "abort", + "disable-redzone": true, + "features": "-mmx,-sse,+soft-float" + } From 3a3169bb49c7b6cc738e266c4746a46b9cb04230 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 21 Feb 2021 13:34:15 +0100 Subject: [PATCH 149/174] Bump version to 0.10.0-alpha-03 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b02f3c6f..d937b3c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,7 +63,7 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bootloader" -version = "0.10.0-alpha-02" +version = "0.10.0-alpha-03" dependencies = [ "anyhow", "argh", diff --git a/Cargo.toml b/Cargo.toml index 87d8ac32..096ab423 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bootloader" -version = "0.10.0-alpha-02" +version = "0.10.0-alpha-03" authors = ["Philipp Oppermann "] license = "MIT/Apache-2.0" description = "An experimental pure-Rust x86 bootloader." From 3c1dfc432b87a7d5c4f038d23c48aa9ec8dc7ecd Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 21 Feb 2021 13:41:00 +0100 Subject: [PATCH 150/174] Run cargo fmt --- build.rs | 3 ++- tests/test_kernels/map_phys_mem/src/bin/access_phys_mem.rs | 2 +- tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs | 7 +++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/build.rs b/build.rs index 0fd34f44..5a3474f4 100644 --- a/build.rs +++ b/build.rs @@ -229,7 +229,8 @@ mod binary { .unwrap_or_else(|err| { format!( "compile_error!(\"failed to parse bootloader config in {}:\n\n{}\")", - path,err.to_string().escape_default(), + path, + err.to_string().escape_default(), ) }) } diff --git a/tests/test_kernels/map_phys_mem/src/bin/access_phys_mem.rs b/tests/test_kernels/map_phys_mem/src/bin/access_phys_mem.rs index 5d4bd4cd..eec06f36 100644 --- a/tests/test_kernels/map_phys_mem/src/bin/access_phys_mem.rs +++ b/tests/test_kernels/map_phys_mem/src/bin/access_phys_mem.rs @@ -3,7 +3,7 @@ use bootloader::{entry_point, BootInfo}; use core::panic::PanicInfo; -use test_kernel_map_phys_mem::{QemuExitCode, exit_qemu, serial}; +use test_kernel_map_phys_mem::{exit_qemu, serial, QemuExitCode}; entry_point!(kernel_main); diff --git a/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs b/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs index e9590217..41363457 100644 --- a/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs +++ b/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs @@ -3,7 +3,7 @@ use bootloader::{boot_info::PixelFormat, entry_point, BootInfo}; use core::panic::PanicInfo; -use test_kernel_map_phys_mem::{QemuExitCode, exit_qemu, serial}; +use test_kernel_map_phys_mem::{exit_qemu, serial, QemuExitCode}; entry_point!(kernel_main); @@ -22,7 +22,10 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! { assert_eq!(framebuffer.buffer().len(), 1024 * 768 * 3); // check defaults for optional features - assert!(matches!(boot_info.physical_memory_offset.into_option(), Some(_))); + assert!(matches!( + boot_info.physical_memory_offset.into_option(), + Some(_) + )); assert_eq!(boot_info.recursive_index.into_option(), None); // check rsdp_addr From 38fd48622c3a6f22d64a65528a56d2471168cb78 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 22 Feb 2021 19:10:33 +0100 Subject: [PATCH 151/174] Check that the `--kernel-manifest` path points to a file named `Cargo.toml` --- build.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/build.rs b/build.rs index 5a3474f4..e288da1f 100644 --- a/build.rs +++ b/build.rs @@ -204,6 +204,15 @@ mod binary { Err(env::VarError::NotUnicode(_)) => { panic!("The KERNEL_MANIFEST environment variable contains invalid unicode") } + Ok(path) + if Path::new(&path).file_name().and_then(|s| s.to_str()) != Some("Cargo.toml") => + { + format!( + "compile_error!(\"The given `--kernel-manifest` path `{}` does not \ + point to a `Cargo.toml`\")", + path, + ) + } Ok(path) => { println!("cargo:rerun-if-changed={}", path); From 9a8ace78650d75189d567618a90a4f039525f369 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 22 Feb 2021 19:12:43 +0100 Subject: [PATCH 152/174] Check that the `--kernel-manifest` path exists --- build.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.rs b/build.rs index e288da1f..b1214ad6 100644 --- a/build.rs +++ b/build.rs @@ -213,6 +213,12 @@ mod binary { path, ) } + Ok(path) if !Path::new(&path).exists() => { + format!( + "compile_error!(\"The given `--kernel-manifest` path `{}` does not exist`\")", + path, + ) + } Ok(path) => { println!("cargo:rerun-if-changed={}", path); From 873351c575bdefd1c6c78b27de2bc0494698c0d5 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 22 Feb 2021 19:13:31 +0100 Subject: [PATCH 153/174] Check that the kernel Cargo.toml depends on the bootloader If not it is likely the wrong Cargo.toml. --- build.rs | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/build.rs b/build.rs index b1214ad6..dd2d03c4 100644 --- a/build.rs +++ b/build.rs @@ -231,23 +231,37 @@ mod binary { .parse::() .expect("failed to parse kernel's Cargo.toml"); - let config_table = manifest - .get("package") - .and_then(|table| table.get("metadata")) - .and_then(|table| table.get("bootloader")) - .cloned() - .unwrap_or_else(|| toml::Value::Table(toml::map::Map::new())); + if manifest + .get("dependencies") + .and_then(|d| d.get("bootloader")) + .is_some() + { + // it seems to be the correct Cargo.toml + let config_table = manifest + .get("package") + .and_then(|table| table.get("metadata")) + .and_then(|table| table.get("bootloader")) + .cloned() + .unwrap_or_else(|| toml::Value::Table(toml::map::Map::new())); - config_table - .try_into::() - .map(|c| format!("{:?}", c)) - .unwrap_or_else(|err| { - format!( - "compile_error!(\"failed to parse bootloader config in {}:\n\n{}\")", - path, - err.to_string().escape_default(), - ) - }) + config_table + .try_into::() + .map(|c| format!("{:?}", c)) + .unwrap_or_else(|err| { + format!( + "compile_error!(\"failed to parse bootloader config in {}:\n\n{}\")", + path, + err.to_string().escape_default(), + ) + }) + } else { + format!( + "compile_error!(\"no bootloader dependency in {}\n\n The \ + `--kernel-manifest` path should point to the `Cargo.toml` \ + of the kernel.\")", + path, + ) + } } }; From 15fda2342c06a4f3859cbf31125325e01d0f402d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 24 Mar 2021 13:36:14 +0100 Subject: [PATCH 154/174] Remove stabilized feature --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f50378c3..c7785e88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,6 @@ #![cfg_attr(not(feature = "builder"), no_std)] #![feature(asm)] -#![feature(unsafe_block_in_unsafe_fn)] #![feature(maybe_uninit_extra)] #![feature(maybe_uninit_slice)] #![deny(unsafe_op_in_unsafe_fn)] From 7f7fec78ffb7125a7eb0312698714d7897bf9fb9 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 24 Mar 2021 18:28:07 +0100 Subject: [PATCH 155/174] Set up VESA mode properly instead of hardcoding it Use the vesa init code from Redox. Comment out prompting the user for resolution for now (we can make it configurable later). --- Cargo.toml | 7 +- src/asm/stage_2.s | 69 +++++- src/asm/stage_3.s | 4 - src/asm/vesa.s | 329 ++++++++++++++++++++++++++++ src/asm/video_mode/vga_320x200.s | 94 -------- src/asm/video_mode/vga_text_80x25.s | 90 -------- src/bin/bios.rs | 52 +++-- src/bin/uefi.rs | 1 - src/binary/mod.rs | 1 + src/boot_info.rs | 6 +- 10 files changed, 442 insertions(+), 211 deletions(-) create mode 100644 src/asm/vesa.s delete mode 100644 src/asm/video_mode/vga_320x200.s delete mode 100644 src/asm/video_mode/vga_text_80x25.s diff --git a/Cargo.toml b/Cargo.toml index 096ab423..f5face54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,10 +65,9 @@ serde = { version = "1.0", features = ["derive"], optional = true} default = [] builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json", "fatfs", "gpt"] runner = ["anyhow"] -bios_bin = ["binary", "vga_320x200", "rsdp"] -uefi_bin = ["binary", "uefi", "font8x8"] -binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", "log", "conquer-once", "spinning_top", "serde"] -vga_320x200 = ["font8x8"] +bios_bin = ["binary", "rsdp"] +uefi_bin = ["binary", "uefi"] +binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", "log", "conquer-once", "spinning_top", "serde", "font8x8"] recursive_page_table = [] map_physical_memory = [] diff --git a/src/asm/stage_2.s b/src/asm/stage_2.s index f5ad1141..b3704f66 100644 --- a/src/asm/stage_2.s +++ b/src/asm/stage_2.s @@ -87,7 +87,7 @@ create_memory_map: call do_e820 video_mode_config: - call config_video_mode + call vesa enter_protected_mode_again: cli @@ -103,3 +103,70 @@ enter_protected_mode_again: spin32: jmp spin32 + + + +# print a string and a newline +# IN +# esi: points at zero-terminated String +vga_println: + push eax + push ebx + push ecx + push edx + + call vga_print + + # newline + mov edx, 0 + mov eax, vga_position + mov ecx, 80 * 2 + div ecx + add eax, 1 + mul ecx + mov vga_position, eax + + pop edx + pop ecx + pop ebx + pop eax + + ret + +# print a string +# IN +# esi: points at zero-terminated String +# CLOBBER +# ah, ebx +vga_print: + cld +vga_print_loop: + # note: if direction flag is set (via std) + # this will DECREMENT the ptr, effectively + # reading/printing in reverse. + lodsb al, BYTE PTR [esi] + test al, al + jz vga_print_done + call vga_print_char + jmp vga_print_loop +vga_print_done: + ret + + +# print a character +# IN +# al: character to print +# CLOBBER +# ah, ebx +vga_print_char: + mov ebx, vga_position + mov ah, 0x0f + mov [ebx + 0xa0000], ax + + add ebx, 2 + mov [vga_position], ebx + + ret + +vga_position: + .double 0 diff --git a/src/asm/stage_3.s b/src/asm/stage_3.s index 3784d035..200dc1fc 100644 --- a/src/asm/stage_3.s +++ b/src/asm/stage_3.s @@ -13,9 +13,6 @@ stage_3: mov es, bx # set extra segment mov ss, bx # set stack segment - mov si, offset boot_third_stage_str - call vga_println - check_cpu: call check_cpuid call check_long_mode @@ -168,6 +165,5 @@ gdt_64_pointer: .word gdt_64_pointer - gdt_64 - 1 # 16-bit Size (Limit) of GDT. .long gdt_64 # 32-bit Base Address of GDT. (CPU will zero extend to 64-bit) -boot_third_stage_str: .asciz "Booting (third stage)..." no_cpuid_str: .asciz "Error: CPU does not support CPUID" no_long_mode_str: .asciz "Error: CPU does not support long mode" diff --git a/src/asm/vesa.s b/src/asm/vesa.s new file mode 100644 index 00000000..eaa59241 --- /dev/null +++ b/src/asm/vesa.s @@ -0,0 +1,329 @@ +# Code originally taken from https://gitlab.redox-os.org/redox-os/bootloader +# +# Copyright (c) 2017 Redox OS, licensed under MIT License + +.section .boot, "awx" +.intel_syntax noprefix +.code16 + +vesa: +vesa_getcardinfo: + mov ax, 0x4F00 + mov di, offset VBECardInfo + int 0x10 + cmp ax, 0x4F + je vesa_findmode + mov eax, 1 + ret +vesa_resetlist: + # if needed, reset mins/maxes/stuff + xor cx, cx + mov [vesa_minx], cx + mov [vesa_miny], cx + mov [config_xres], cx + mov [config_yres], cx +vesa_findmode: + mov si, [VBECardInfo_videomodeptr] + mov ax, [VBECardInfo_videomodeptr+2] + mov fs, ax + sub si, 2 +vesa_searchmodes: + add si, 2 + mov cx, fs:[si] + cmp cx, 0xFFFF + jne vesa_getmodeinfo + cmp word ptr [vesa_goodmode], 0 + je vesa_resetlist + jmp vesa_findmode +vesa_getmodeinfo: + push esi + mov [vesa_currentmode], cx + mov ax, 0x4F01 + mov di, offset VBEModeInfo + int 0x10 + pop esi + cmp ax, 0x4F + je vesa_foundmode + mov eax, 1 + ret +vesa_foundmode: + # check minimum values, really not minimums from an OS perspective but ugly for users + cmp byte ptr [VBEModeInfo_bitsperpixel], 32 + jb vesa_searchmodes +vesa_testx: + mov cx, [VBEModeInfo_xresolution] + cmp word ptr [config_xres], 0 + je vesa_notrequiredx + cmp cx, [config_xres] + je vesa_testy + jmp vesa_searchmodes +vesa_notrequiredx: + cmp cx, [vesa_minx] + jb vesa_searchmodes +vesa_testy: + mov cx, [VBEModeInfo_yresolution] + cmp word ptr [config_yres], 0 + je vesa_notrequiredy + cmp cx, [config_yres] + jne vesa_searchmodes # as if there weren't enough warnings, USE WITH CAUTION + cmp word ptr [config_xres], 0 + jnz vesa_setmode + jmp vesa_testgood +vesa_notrequiredy: + cmp cx, [vesa_miny] + jb vesa_searchmodes +vesa_testgood: + mov al, 13 + call print_char + mov cx, [vesa_currentmode] + mov [vesa_goodmode], cx + push esi + # call print_dec + # mov al, ':' + # call print_char + mov cx, [VBEModeInfo_xresolution] + call print_dec + mov al, 'x' + call print_char + mov cx, [VBEModeInfo_yresolution] + call print_dec + mov al, '@' + call print_char + xor ch, ch + mov cl, [VBEModeInfo_bitsperpixel] + call print_dec +vesa_confirm_mode: + mov si, offset vesa_modeok + call print + # xor ax, ax + # int 0x16 # read key press + pop esi + cmp al, al # originally `cmp al, 'y'` to compare key press + je vesa_setmode + cmp al, 's' + je vesa_savemode + jmp vesa_searchmodes +vesa_savemode: + mov cx, [VBEModeInfo_xresolution] + mov [config_xres], cx + mov cx, [VBEModeInfo_yresolution] + mov [config_yres], cx + # call save_config +vesa_setmode: + mov bx, [vesa_currentmode] + cmp bx, 0 + je vesa_nomode + or bx, 0x4000 + mov ax, 0x4F02 + int 0x10 +vesa_nomode: + cmp ax, 0x4F + je vesa_returngood + mov eax, 1 + ret +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 + +vesa_goodmode: .2byte 0 +vesa_currentmode: .2byte 0 +# useful functions + +# print a number in decimal +# IN +# cx: the number +# CLOBBER +# al, cx, si +print_dec: + mov si, offset print_dec_number +print_dec_clear: + mov al, '0' + mov [si], al + inc si + cmp si, offset print_dec_numberend + jb print_dec_clear + dec si + call convert_dec + mov si, offset print_dec_number +print_dec_lp: + lodsb + cmp si, offset print_dec_numberend + jae print_dec_end + cmp al, '0' + jbe print_dec_lp +print_dec_end: + dec si + call print + ret + +print_dec_number: .skip 7, 0 +print_dec_numberend: .skip 1, 0 + +convert_dec: + dec si + mov bx, si # place to convert into must be in si, number to convert must be in cx +convert_dec_cnvrt: + mov si, bx + sub si, 4 +convert_dec_ten4: inc si + cmp cx, 10000 + jb convert_dec_ten3 + sub cx, 10000 + inc byte ptr [si] + jmp convert_dec_cnvrt +convert_dec_ten3: inc si + cmp cx, 1000 + jb convert_dec_ten2 + sub cx, 1000 + inc byte ptr [si] + jmp convert_dec_cnvrt +convert_dec_ten2: inc si + cmp cx, 100 + jb convert_dec_ten1 + sub cx, 100 + inc byte ptr [si] + jmp convert_dec_cnvrt +convert_dec_ten1: inc si + cmp cx, 10 + jb convert_dec_ten0 + sub cx, 10 + inc byte ptr [si] + jmp convert_dec_cnvrt +convert_dec_ten0: inc si + cmp cx, 1 + jb convert_dec_return + sub cx, 1 + inc byte ptr [si] + jmp convert_dec_cnvrt +convert_dec_return: + ret + + +VBECardInfo: + VBECardInfo_signature: .skip 4, 0 + VBECardInfo_version: .skip 2, 0 + VBECardInfo_oemstring: .skip 4, 0 + VBECardInfo_capabilities: .skip 4, 0 + VBECardInfo_videomodeptr: .skip 4, 0 + VBECardInfo_totalmemory: .skip 2, 0 + VBECardInfo_oemsoftwarerev: .skip 2, 0 + VBECardInfo_oemvendornameptr: .skip 4, 0 + VBECardInfo_oemproductnameptr: .skip 4, 0 + VBECardInfo_oemproductrevptr: .skip 4, 0 + VBECardInfo_reserved: .skip 222, 0 + VBECardInfo_oemdata: .skip 256, 0 + +VBEModeInfo: + VBEModeInfo_attributes: .skip 2, 0 + VBEModeInfo_winA: .skip 1, 0 + VBEModeInfo_winB: .skip 1, 0 + VBEModeInfo_granularity: .skip 2, 0 + VBEModeInfo_winsize: .skip 2, 0 + VBEModeInfo_segmentA: .skip 2, 0 + VBEModeInfo_segmentB: .skip 2, 0 + VBEModeInfo_winfuncptr: .skip 4, 0 + VBEModeInfo_bytesperscanline: .skip 2, 0 + VBEModeInfo_xresolution: .skip 2, 0 + VBEModeInfo_yresolution: .skip 2, 0 + VBEModeInfo_xcharsize: .skip 1, 0 + VBEModeInfo_ycharsize: .skip 1, 0 + VBEModeInfo_numberofplanes: .skip 1, 0 + VBEModeInfo_bitsperpixel: .skip 1, 0 + VBEModeInfo_numberofbanks: .skip 1, 0 + VBEModeInfo_memorymodel: .skip 1, 0 + VBEModeInfo_banksize: .skip 1, 0 + VBEModeInfo_numberofimagepages: .skip 1, 0 + VBEModeInfo_unused: .skip 1, 0 + VBEModeInfo_redmasksize: .skip 1, 0 + VBEModeInfo_redfieldposition: .skip 1, 0 + VBEModeInfo_greenmasksize: .skip 1, 0 + VBEModeInfo_greenfieldposition: .skip 1, 0 + VBEModeInfo_bluemasksize: .skip 1, 0 + VBEModeInfo_bluefieldposition: .skip 1, 0 + VBEModeInfo_rsvdmasksize: .skip 1, 0 + VBEModeInfo_rsvdfieldposition: .skip 1, 0 + VBEModeInfo_directcolormodeinfo: .skip 1, 0 + VBEModeInfo_physbaseptr: .skip 4, 0 + VBEModeInfo_offscreenmemoryoffset: .skip 4, 0 + VBEModeInfo_offscreenmemsize: .skip 2, 0 + VBEModeInfo_reserved: .skip 206, 0 + +# VBE.ModeAttributes: +# ModeAttributes_available equ 1 << 0 +# ModeAttributes_bios equ 1 << 2 +# ModeAttributes_color equ 1 << 3 +# ModeAttributes_graphics equ 1 << 4 +# ModeAttributes_vgacompatible equ 1 << 5 +# ModeAttributes_notbankable equ 1 << 6 +# ModeAttributes_linearframebuffer equ 1 << 7 + +VBEEDID: + VBEEDID_header: .skip 8, 0 + VBEEDID_manufacturer: .skip 2, 0 + VBEEDID_productid: .skip 2, 0 + VBEEDID_serial: .skip 4, 0 + VBEEDID_manufactureweek: .skip 1, 0 + VBEEDID_manufactureyear: .skip 1, 0 + VBEEDID_version: .skip 1, 0 + VBEEDID_revision: .skip 1, 0 + VBEEDID_input: .skip 1, 0 + VBEEDID_horizontalsize: .skip 1, 0 + VBEEDID_verticalsize: .skip 1, 0 + VBEEDID_gamma: .skip 1, 0 + VBEEDID_displaytype: .skip 1, 0 + VBEEDID_chromaticity: .skip 10, 0 + VBEEDID_timingI: .skip 1, 0 + VBEEDID_timingII: .skip 1, 0 + VBEEDID_timingreserved: .skip 1, 0 + VBEEDID_standardtiming: .skip 16, 0 # format: db (horizontal-248)/8, aspectratio | verticalfrequency - 60 + # VBEEDID_standardtiming_aspect.16.10 equ 0 # mul horizontal by 10, shr 4 to get vertical resolution + # VBEEDID_standardtiming_aspect.4.3 equ 1 << 6 # mul horizontal by 3, shr 2 to get vertical resolution + # VBEEDID_standardtiming_aspect.5.4 equ 2 << 6 # shl horizontal by 2, div by 5 to get vertical resolution + # VBEEDID_standardtiming_aspect.16.9 equ 3 << 6 # mul horizontal by 9, shr by 4 to get vertical resolution + VBEEDID_descriptorblock1: .skip 18, 0 + VBEEDID_descriptorblock2: .skip 18, 0 + VBEEDID_descriptorblock3: .skip 18, 0 + VBEEDID_descriptorblock4: .skip 18, 0 + VBEEDID_extensionflag: .skip 1, 0 + VBEEDID_checksum: .skip 1, 0 + +config: + config_xres: .2byte 0 + config_yres: .2byte 0 + +# print a string +# IN +# si: points at zero-terminated String +# CLOBBER +# si, ax +print: + pushf + cld +print_loop: + lodsb + test al, al + jz print_done + call print_char + jmp print_loop +print_done: + popf + ret + + +# print a character +# IN +# al: character to print +print_char: + pusha + mov bx, 7 + mov ah, 0x0e + int 0x10 + popa + ret diff --git a/src/asm/video_mode/vga_320x200.s b/src/asm/video_mode/vga_320x200.s deleted file mode 100644 index b1d45069..00000000 --- a/src/asm/video_mode/vga_320x200.s +++ /dev/null @@ -1,94 +0,0 @@ -.section .boot, "awx" -.intel_syntax noprefix -.code16 - -config_video_mode: - push bx - push ax - mov ax, 0x4F02 - mov bx, 0x0118 - int 0x10 - pop bx - pop ax - ret - -.code32 - -vga_map_frame_buffer: - mov eax, 0xa0000 - or eax, (1 | 2) -vga_map_frame_buffer_loop: - mov ecx, eax - shr ecx, 12 - mov [_p1 + ecx * 8], eax - - add eax, 4096 - cmp eax, 0xc0000 - jl vga_map_frame_buffer_loop - - ret - -# print a string and a newline -# IN -# esi: points at zero-terminated String -vga_println: - push eax - push ebx - push ecx - push edx - - call vga_print - - # newline - mov edx, 0 - mov eax, vga_position - mov ecx, 80 * 2 - div ecx - add eax, 1 - mul ecx - mov vga_position, eax - - pop edx - pop ecx - pop ebx - pop eax - - ret - -# print a string -# IN -# esi: points at zero-terminated String -# CLOBBER -# ah, ebx -vga_print: - cld -vga_print_loop: - # note: if direction flag is set (via std) - # this will DECREMENT the ptr, effectively - # reading/printing in reverse. - lodsb al, BYTE PTR [esi] - test al, al - jz vga_print_done - call vga_print_char - jmp vga_print_loop -vga_print_done: - ret - - -# print a character -# IN -# al: character to print -# CLOBBER -# ah, ebx -vga_print_char: - mov ebx, vga_position - mov ah, 0x0f - mov [ebx + 0xa0000], ax - - add ebx, 2 - mov [vga_position], ebx - - ret - -vga_position: - .double 0 diff --git a/src/asm/video_mode/vga_text_80x25.s b/src/asm/video_mode/vga_text_80x25.s deleted file mode 100644 index 0ddb0d6f..00000000 --- a/src/asm/video_mode/vga_text_80x25.s +++ /dev/null @@ -1,90 +0,0 @@ -.section .boot, "awx" -.intel_syntax noprefix -.code16 - -config_video_mode: - mov ah, 0 - mov al, 0x03 # 80x25 16 color text - int 0x10 - ret - -.code32 - -vga_map_frame_buffer: - mov eax, 0xa0000 - or eax, (1 | 2) -vga_map_frame_buffer_loop: - mov ecx, eax - shr ecx, 12 - mov [_p1 + ecx * 8], eax - - add eax, 4096 - cmp eax, 0xc0000 - jl vga_map_frame_buffer_loop - - ret - -# print a string and a newline -# IN -# esi: points at zero-terminated String -vga_println: - push eax - push ebx - push ecx - push edx - - call vga_print - - # newline - mov edx, 0 - mov eax, vga_position - mov ecx, 80 * 2 - div ecx - add eax, 1 - mul ecx - mov vga_position, eax - - pop edx - pop ecx - pop ebx - pop eax - - ret - -# print a string -# IN -# esi: points at zero-terminated String -# CLOBBER -# ah, ebx -vga_print: - cld -vga_print_loop: - # note: if direction flag is set (via std) - # this will DECREMENT the ptr, effectively - # reading/printing in reverse. - lodsb al, BYTE PTR [esi] - test al, al - jz vga_print_done - call vga_print_char - jmp vga_print_loop -vga_print_done: - ret - - -# print a character -# IN -# al: character to print -# CLOBBER -# ah, ebx -vga_print_char: - mov ebx, vga_position - mov ah, 0x0f - mov [ebx + 0xb8000], ax - - add ebx, 2 - mov [vga_position], ebx - - ret - -vga_position: - .double 0 diff --git a/src/bin/bios.rs b/src/bin/bios.rs index 95de2461..4a9f854a 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -20,13 +20,19 @@ 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!("../asm/vesa.s")); global_asm!(include_str!("../asm/e820.s")); global_asm!(include_str!("../asm/stage_3.s")); -#[cfg(feature = "vga_320x200")] -global_asm!(include_str!("../asm/video_mode/vga_320x200.s")); -#[cfg(not(feature = "vga_320x200"))] -global_asm!(include_str!("../asm/video_mode/vga_text_80x25.s")); +// values defined in `vesa.s` +extern "C" { + static VBEModeInfo_physbaseptr: u32; + static VBEModeInfo_bytesperscanline: u16; + static VBEModeInfo_xresolution: u16; + static VBEModeInfo_yresolution: u16; + static VBEModeInfo_bitsperpixel: u8; + // TODO color values (e.g. RGB or BGR) +} // Symbols defined in `linker.ld` extern "C" { @@ -109,9 +115,20 @@ fn bootloader_main( } } - let framebuffer_addr = PhysAddr::new(0xfd000000); - let framebuffer_size = 1024 * 768 * 3; - let framebuffer_info = init_logger(framebuffer_addr, framebuffer_size); + let framebuffer_addr = PhysAddr::new(unsafe { VBEModeInfo_physbaseptr }.into()); + let framebuffer_info = unsafe { + let framebuffer_size = + usize::from(VBEModeInfo_yresolution) * usize::from(VBEModeInfo_bytesperscanline); + let bytes_per_pixel = VBEModeInfo_bitsperpixel / 8; + init_logger( + framebuffer_addr, + framebuffer_size.into(), + VBEModeInfo_xresolution.into(), + VBEModeInfo_yresolution.into(), + bytes_per_pixel.into(), + (VBEModeInfo_bytesperscanline / u16::from(bytes_per_pixel)).into(), + ) + }; let page_tables = create_page_tables(&mut frame_allocator); @@ -134,17 +151,24 @@ fn bootloader_main( ); } -fn init_logger(framebuffer_start: PhysAddr, framebuffer_size: usize) -> FrameBufferInfo { +fn init_logger( + framebuffer_start: PhysAddr, + framebuffer_size: usize, + horizontal_resolution: usize, + vertical_resolution: usize, + bytes_per_pixel: usize, + stride: usize, +) -> FrameBufferInfo { let ptr = framebuffer_start.as_u64() as *mut u8; let slice = unsafe { slice::from_raw_parts_mut(ptr, framebuffer_size) }; - slice.fill(0x4); + slice.fill(0xff); let info = bootloader::boot_info::FrameBufferInfo { byte_len: framebuffer_size, - horizontal_resolution: 1024, - vertical_resolution: 768, - pixel_format: bootloader::boot_info::PixelFormat::RGB, - bytes_per_pixel: 3, - stride: 1024, + horizontal_resolution, + vertical_resolution, + bytes_per_pixel, + stride, + pixel_format: bootloader::boot_info::PixelFormat::RGB, // TODO: don't hardcode }; bootloader::binary::init_logger(slice, info); diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index 71e3bcb2..c637d570 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -2,7 +2,6 @@ #![no_main] #![feature(abi_efiapi)] #![feature(asm)] -#![feature(unsafe_block_in_unsafe_fn)] #![feature(maybe_uninit_extra)] #![deny(unsafe_op_in_unsafe_fn)] diff --git a/src/binary/mod.rs b/src/binary/mod.rs index 96c068a1..edde0391 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -53,6 +53,7 @@ pub fn init_logger(framebuffer: &'static mut [u8], info: FrameBufferInfo) { let logger = logger::LOGGER.get_or_init(move || logger::LockedLogger::new(framebuffer, info)); log::set_logger(logger).expect("logger already set"); log::set_max_level(log::LevelFilter::Trace); + log::info!("Framebuffer info: {:?}", info); } /// Required system information that should be queried from the BIOS or UEFI firmware. diff --git a/src/boot_info.rs b/src/boot_info.rs index b4ce688b..e0aa0598 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -148,10 +148,10 @@ pub struct FrameBufferInfo { pub pixel_format: PixelFormat, /// The number of bytes per pixel. pub bytes_per_pixel: usize, - /// Number of bytes between the start of a line and the start of the next. + /// Number of pixels between the start of a line and the start of the next. /// - /// Some framebuffers use additional padding bytes at the end of a line, so this - /// value might be larger than `horizontal_resolution * bytes_per_pixel`. It is + /// Some framebuffers use additional padding at the end of a line, so this + /// value might be larger than `horizontal_resolution`. It is /// therefore recommended to use this field for calculating the start address of a line. pub stride: usize, } From b7ff05b4ab2ce853a9e92e765ca77e51ec794599 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 24 Mar 2021 18:46:59 +0100 Subject: [PATCH 156/174] Adjust tests for new vesa mode detection --- .../src/bin/check_boot_info.rs | 32 ++++++++++++++++--- .../map_phys_mem/src/bin/check_boot_info.rs | 32 ++++++++++++++++--- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/tests/test_kernels/default_settings/src/bin/check_boot_info.rs b/tests/test_kernels/default_settings/src/bin/check_boot_info.rs index a2997e6c..2afa8e44 100644 --- a/tests/test_kernels/default_settings/src/bin/check_boot_info.rs +++ b/tests/test_kernels/default_settings/src/bin/check_boot_info.rs @@ -14,12 +14,34 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! { // check framebuffer let framebuffer = boot_info.framebuffer.as_ref().unwrap(); assert_eq!(framebuffer.info().byte_len, framebuffer.buffer().len()); - assert_eq!(framebuffer.info().horizontal_resolution, 1024); - assert_eq!(framebuffer.info().vertical_resolution, 768); - assert_eq!(framebuffer.info().bytes_per_pixel, 3); - assert_eq!(framebuffer.info().stride, 1024); + if ![640, 1024].contains(&framebuffer.info().horizontal_resolution) { + panic!( + "unexpected horizontal_resolution `{}`", + framebuffer.info().horizontal_resolution + ); + } + if ![480, 768].contains(&framebuffer.info().vertical_resolution) { + panic!( + "unexpected vertical_resolution `{}`", + framebuffer.info().vertical_resolution + ); + } + if ![3, 4].contains(&framebuffer.info().bytes_per_pixel) { + panic!( + "unexpected bytes_per_pixel `{}`", + framebuffer.info().bytes_per_pixel + ); + } + if ![640, 1024].contains(&framebuffer.info().stride) { + panic!("unexpected stride `{}`", framebuffer.info().stride); + } assert_eq!(framebuffer.info().pixel_format, PixelFormat::RGB); - assert_eq!(framebuffer.buffer().len(), 1024 * 768 * 3); + assert_eq!( + framebuffer.buffer().len(), + framebuffer.info().stride + * framebuffer.info().vertical_resolution + * framebuffer.info().bytes_per_pixel + ); // check defaults for optional features assert_eq!(boot_info.physical_memory_offset.into_option(), None); diff --git a/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs b/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs index 41363457..90155097 100644 --- a/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs +++ b/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs @@ -14,12 +14,34 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! { // check framebuffer let framebuffer = boot_info.framebuffer.as_ref().unwrap(); assert_eq!(framebuffer.info().byte_len, framebuffer.buffer().len()); - assert_eq!(framebuffer.info().horizontal_resolution, 1024); - assert_eq!(framebuffer.info().vertical_resolution, 768); - assert_eq!(framebuffer.info().bytes_per_pixel, 3); - assert_eq!(framebuffer.info().stride, 1024); + if ![640, 1024].contains(&framebuffer.info().horizontal_resolution) { + panic!( + "unexpected horizontal_resolution `{}`", + framebuffer.info().horizontal_resolution + ); + } + if ![480, 768].contains(&framebuffer.info().vertical_resolution) { + panic!( + "unexpected vertical_resolution `{}`", + framebuffer.info().vertical_resolution + ); + } + if ![3, 4].contains(&framebuffer.info().bytes_per_pixel) { + panic!( + "unexpected bytes_per_pixel `{}`", + framebuffer.info().bytes_per_pixel + ); + } + if ![640, 1024].contains(&framebuffer.info().stride) { + panic!("unexpected stride `{}`", framebuffer.info().stride); + } assert_eq!(framebuffer.info().pixel_format, PixelFormat::RGB); - assert_eq!(framebuffer.buffer().len(), 1024 * 768 * 3); + assert_eq!( + framebuffer.buffer().len(), + framebuffer.info().stride + * framebuffer.info().vertical_resolution + * framebuffer.info().bytes_per_pixel + ); // check defaults for optional features assert!(matches!( From 58564910a743ec48e6c1b3113151d96c7b54ca63 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 11:13:28 +0100 Subject: [PATCH 157/174] Detect actual pixel format instead of hardcoding it (BIOS) --- src/bin/bios.rs | 33 +++++++++++++++++++++++++++++---- src/bin/uefi.rs | 2 ++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/bin/bios.rs b/src/bin/bios.rs index 4a9f854a..f0a2954c 100644 --- a/src/bin/bios.rs +++ b/src/bin/bios.rs @@ -8,7 +8,10 @@ #[cfg(not(target_os = "none"))] compile_error!("The bootloader crate must be compiled for the `x86_64-bootloader.json` target"); -use bootloader::{binary::SystemInfo, boot_info::FrameBufferInfo}; +use bootloader::{ + binary::SystemInfo, + boot_info::{FrameBufferInfo, PixelFormat}, +}; use core::panic::PanicInfo; use core::slice; use usize_conversions::usize_from; @@ -31,7 +34,9 @@ extern "C" { static VBEModeInfo_xresolution: u16; static VBEModeInfo_yresolution: u16; static VBEModeInfo_bitsperpixel: u8; - // TODO color values (e.g. RGB or BGR) + static VBEModeInfo_redfieldposition: u8; + static VBEModeInfo_greenfieldposition: u8; + static VBEModeInfo_bluefieldposition: u8; } // Symbols defined in `linker.ld` @@ -116,6 +121,7 @@ fn bootloader_main( } let framebuffer_addr = PhysAddr::new(unsafe { VBEModeInfo_physbaseptr }.into()); + let mut error = None; let framebuffer_info = unsafe { let framebuffer_size = usize::from(VBEModeInfo_yresolution) * usize::from(VBEModeInfo_bytesperscanline); @@ -127,9 +133,27 @@ fn bootloader_main( VBEModeInfo_yresolution.into(), bytes_per_pixel.into(), (VBEModeInfo_bytesperscanline / u16::from(bytes_per_pixel)).into(), + match ( + VBEModeInfo_redfieldposition, + VBEModeInfo_greenfieldposition, + VBEModeInfo_bluefieldposition, + ) { + (0, 8, 16) => PixelFormat::RGB, + (16, 8, 0) => PixelFormat::BGR, + (r, g, b) => { + error = Some(("invalid rgb field positions", r, g, b)); + PixelFormat::RGB // default to RBG so that we can print something + } + }, ) }; + log::info!("BIOS boot"); + + if let Some((msg, r, g, b)) = error { + panic!("{}: r: {}, g: {}, b: {}", msg, r, g, b); + } + let page_tables = create_page_tables(&mut frame_allocator); let kernel = { @@ -158,17 +182,18 @@ fn init_logger( vertical_resolution: usize, bytes_per_pixel: usize, stride: usize, + pixel_format: PixelFormat, ) -> FrameBufferInfo { let ptr = framebuffer_start.as_u64() as *mut u8; let slice = unsafe { slice::from_raw_parts_mut(ptr, framebuffer_size) }; - slice.fill(0xff); + let info = bootloader::boot_info::FrameBufferInfo { byte_len: framebuffer_size, horizontal_resolution, vertical_resolution, bytes_per_pixel, stride, - pixel_format: bootloader::boot_info::PixelFormat::RGB, // TODO: don't hardcode + pixel_format, }; bootloader::binary::init_logger(slice, info); diff --git a/src/bin/uefi.rs b/src/bin/uefi.rs index c637d570..916e28a5 100644 --- a/src/bin/uefi.rs +++ b/src/bin/uefi.rs @@ -164,6 +164,8 @@ fn init_logger(st: &SystemTable) -> (PhysAddr, FrameBufferInfo) { stride: mode_info.stride(), }; + log::info!("UEFI boot"); + bootloader::binary::init_logger(slice, info); (PhysAddr::new(framebuffer.as_mut_ptr() as u64), info) From 2d7e0648628118070350baafb838016b9f297cc7 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 12:30:53 +0100 Subject: [PATCH 158/174] Adjust tests for proper pixel format --- tests/test_kernels/default_settings/src/bin/check_boot_info.rs | 2 +- tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_kernels/default_settings/src/bin/check_boot_info.rs b/tests/test_kernels/default_settings/src/bin/check_boot_info.rs index 2afa8e44..fab65b53 100644 --- a/tests/test_kernels/default_settings/src/bin/check_boot_info.rs +++ b/tests/test_kernels/default_settings/src/bin/check_boot_info.rs @@ -35,7 +35,7 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! { if ![640, 1024].contains(&framebuffer.info().stride) { panic!("unexpected stride `{}`", framebuffer.info().stride); } - assert_eq!(framebuffer.info().pixel_format, PixelFormat::RGB); + assert_eq!(framebuffer.info().pixel_format, PixelFormat::BGR); assert_eq!( framebuffer.buffer().len(), framebuffer.info().stride diff --git a/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs b/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs index 90155097..5099c49b 100644 --- a/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs +++ b/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs @@ -35,7 +35,7 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! { if ![640, 1024].contains(&framebuffer.info().stride) { panic!("unexpected stride `{}`", framebuffer.info().stride); } - assert_eq!(framebuffer.info().pixel_format, PixelFormat::RGB); + assert_eq!(framebuffer.info().pixel_format, PixelFormat::BGR); assert_eq!( framebuffer.buffer().len(), framebuffer.info().stride From f7478eba3034c98bde0c7725ce21a7b56a473d61 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 12:32:13 +0100 Subject: [PATCH 159/174] Use `quote` crate for creating `Config` struct instead of debug impls --- Cargo.lock | 2 + Cargo.toml | 7 +++- build.rs | 107 +++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 83 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d937b3c6..7650dde1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,6 +76,8 @@ dependencies = [ "json", "llvm-tools", "log", + "proc-macro2", + "quote", "rsdp", "serde", "spinning_top", diff --git a/Cargo.toml b/Cargo.toml index f5face54..ce55e41f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,8 @@ optional = true llvm-tools-build = { version = "0.1", optional = true, package = "llvm-tools" } toml = { version = "0.5.1", optional = true } serde = { version = "1.0", features = ["derive"], optional = true} +quote = { version = "1.0", optional = true} +proc-macro2 = { version = "1.0", optional = true} [features] default = [] @@ -67,9 +69,12 @@ builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json", "f runner = ["anyhow"] bios_bin = ["binary", "rsdp"] uefi_bin = ["binary", "uefi"] -binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", "log", "conquer-once", "spinning_top", "serde", "font8x8"] recursive_page_table = [] map_physical_memory = [] +binary = [ + "llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", "log", "conquer-once", + "spinning_top", "serde", "font8x8", "quote", "proc-macro2", +] [profile.dev] panic = "abort" diff --git a/build.rs b/build.rs index dd2d03c4..754daeb0 100644 --- a/build.rs +++ b/build.rs @@ -8,7 +8,7 @@ fn main() { #[cfg(feature = "binary")] mod binary { - use std::fmt; + use quote::quote; pub fn main() { use llvm_tools_build as llvm_tools; @@ -207,17 +207,21 @@ mod binary { Ok(path) if Path::new(&path).file_name().and_then(|s| s.to_str()) != Some("Cargo.toml") => { - format!( - "compile_error!(\"The given `--kernel-manifest` path `{}` does not \ - point to a `Cargo.toml`\")", + let err = format!( + "The given `--kernel-manifest` path `{}` does not \ + point to a `Cargo.toml`", path, - ) + ); + quote! { compile_error!(#err) } } Ok(path) if !Path::new(&path).exists() => { - format!( - "compile_error!(\"The given `--kernel-manifest` path `{}` does not exist`\")", - path, - ) + let err = format!( + "The given `--kernel-manifest` path `{}` does not exist.", + path + ); + quote! { + compile_error!(#err); + } } Ok(path) => { println!("cargo:rerun-if-changed={}", path); @@ -245,22 +249,28 @@ mod binary { .unwrap_or_else(|| toml::Value::Table(toml::map::Map::new())); config_table - .try_into::() - .map(|c| format!("{:?}", c)) + .try_into::() + .map(|c| quote! { #c }) .unwrap_or_else(|err| { - format!( - "compile_error!(\"failed to parse bootloader config in {}:\n\n{}\")", + let err = format!( + "failed to parse bootloader config in {}:\n\n{}", path, - err.to_string().escape_default(), - ) + err.to_string().escape_default() + ); + quote! { + compile_error!(#err); + } }) } else { - format!( - "compile_error!(\"no bootloader dependency in {}\n\n The \ - `--kernel-manifest` path should point to the `Cargo.toml` \ - of the kernel.\")", - path, - ) + let err = format!( + "no bootloader dependency in {}\n\n The \ + `--kernel-manifest` path should point to the `Cargo.toml` \ + of the kernel.", + path + ); + quote! { + compile_error!(#err); + } } } }; @@ -269,13 +279,13 @@ mod binary { 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( - format!( - "mod parsed_config {{ + quote::quote! { + mod parsed_config { use crate::config::Config; - pub const CONFIG: Config = {}; - }}", - config, - ) + pub const CONFIG: Config = #config; + } + } + .to_string() .as_bytes(), ) .expect("write to bootloader_config.rs failed"); @@ -294,10 +304,11 @@ mod binary { /// /// This copy is needed because we can't derive Deserialize in the `src/config.rs` /// module itself, since cargo currently unifies dependencies (the `toml` crate enables - /// serde's standard feature). + /// serde's standard feature). Also, it allows to separate the parsing special cases + /// such as `AlignedAddress` more cleanly. #[derive(Debug, serde::Deserialize)] #[serde(rename_all = "kebab-case", deny_unknown_fields)] - struct Config { + struct ParsedConfig { #[serde(default)] pub map_physical_memory: bool, #[serde(default)] @@ -312,11 +323,43 @@ mod binary { pub framebuffer_address: Option, } + /// Convert to tokens suitable for initializing the `Config` struct. + impl quote::ToTokens for ParsedConfig { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + fn optional(value: Option) -> proc_macro2::TokenStream { + value.map(|v| quote!(Some(#v))).unwrap_or(quote!(None)) + } + + let map_physical_memory = self.map_physical_memory; + let map_page_table_recursively = self.map_page_table_recursively; + let map_framebuffer = self.map_framebuffer; + let kernel_stack_size = optional(self.kernel_stack_size); + let physical_memory_offset = optional(self.physical_memory_offset); + let recursive_index = optional(self.recursive_index); + 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); + + tokens.extend(quote! { Config { + map_physical_memory: #map_physical_memory, + map_page_table_recursively: #map_page_table_recursively, + map_framebuffer: #map_framebuffer, + kernel_stack_size: #kernel_stack_size, + physical_memory_offset: #physical_memory_offset, + recursive_index: #recursive_index, + kernel_stack_address: #kernel_stack_address, + boot_info_address: #boot_info_address, + framebuffer_address: #framebuffer_address, + }}); + } + } + + #[derive(Debug, Clone, Copy)] struct AlignedAddress(u64); - impl fmt::Debug for AlignedAddress { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) + impl quote::ToTokens for AlignedAddress { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + self.0.to_tokens(tokens); } } From 373169a8efe5331b76890af84845d7c928a755f5 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 14:26:30 +0100 Subject: [PATCH 160/174] Move memory region structs to `boot_info` module --- src/binary/bios/memory_descriptor.rs | 2 +- src/binary/legacy_memory_region.rs | 2 +- src/binary/mod.rs | 7 +++-- src/binary/uefi/memory_descriptor.rs | 2 +- src/boot_info.rs | 47 +++++++++++++++++++++++++++- src/lib.rs | 2 -- src/memory_region.rs | 45 -------------------------- 7 files changed, 53 insertions(+), 54 deletions(-) delete mode 100644 src/memory_region.rs diff --git a/src/binary/bios/memory_descriptor.rs b/src/binary/bios/memory_descriptor.rs index 4217b608..fcd80c12 100644 --- a/src/binary/bios/memory_descriptor.rs +++ b/src/binary/bios/memory_descriptor.rs @@ -1,4 +1,4 @@ -use crate::{binary::legacy_memory_region::LegacyMemoryRegion, memory_region::MemoryRegionKind}; +use crate::{binary::legacy_memory_region::LegacyMemoryRegion, boot_info::MemoryRegionKind}; use x86_64::PhysAddr; impl LegacyMemoryRegion for E820MemoryRegion { diff --git a/src/binary/legacy_memory_region.rs b/src/binary/legacy_memory_region.rs index 74c79915..831f9a94 100644 --- a/src/binary/legacy_memory_region.rs +++ b/src/binary/legacy_memory_region.rs @@ -1,4 +1,4 @@ -use crate::memory_region::{MemoryRegion, MemoryRegionKind}; +use crate::boot_info::{MemoryRegion, MemoryRegionKind}; use core::mem::MaybeUninit; use x86_64::{ structures::paging::{FrameAllocator, PhysFrame, Size4KiB}, diff --git a/src/binary/mod.rs b/src/binary/mod.rs index edde0391..de5d7de6 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -1,6 +1,7 @@ -use crate::binary::legacy_memory_region::{LegacyFrameAllocator, LegacyMemoryRegion}; -use crate::boot_info::{BootInfo, FrameBuffer, FrameBufferInfo, TlsTemplate}; -use crate::memory_region::MemoryRegion; +use crate::{ + binary::legacy_memory_region::{LegacyFrameAllocator, LegacyMemoryRegion}, + boot_info::{BootInfo, FrameBuffer, FrameBufferInfo, MemoryRegion, TlsTemplate}, +}; use core::{ mem::{self, MaybeUninit}, slice, diff --git a/src/binary/uefi/memory_descriptor.rs b/src/binary/uefi/memory_descriptor.rs index c423754d..7d1c3a53 100644 --- a/src/binary/uefi/memory_descriptor.rs +++ b/src/binary/uefi/memory_descriptor.rs @@ -1,4 +1,4 @@ -use crate::{binary::legacy_memory_region::LegacyMemoryRegion, memory_region::MemoryRegionKind}; +use crate::{binary::legacy_memory_region::LegacyMemoryRegion, boot_info::MemoryRegionKind}; use uefi::table::boot::{MemoryDescriptor, MemoryType}; use x86_64::PhysAddr; diff --git a/src/boot_info.rs b/src/boot_info.rs index e0aa0598..08fa69d4 100644 --- a/src/boot_info.rs +++ b/src/boot_info.rs @@ -1,4 +1,3 @@ -use crate::memory_region::MemoryRegion; use core::{ops, slice}; /// This structure represents the information that the bootloader passes to the kernel. @@ -104,6 +103,52 @@ impl From for &'static mut [MemoryRegion] { } } +/// Represent a physical memory region. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(C)] +pub struct MemoryRegion { + /// The physical start address of the region. + pub start: u64, + /// The physical end address (exclusive) of the region. + pub end: u64, + /// The memory type of the memory region. + /// + /// Only [`Usable`][MemoryRegionKind::Usable] regions can be freely used. + pub kind: MemoryRegionKind, +} + +impl MemoryRegion { + /// Creates a new empty memory region (with length 0). + pub const fn empty() -> Self { + MemoryRegion { + start: 0, + end: 0, + kind: MemoryRegionKind::Bootloader, + } + } +} + +/// Represents the different types of memory. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[non_exhaustive] +#[repr(C)] +pub enum MemoryRegionKind { + /// Unused conventional memory, can be used by the kernel. + Usable, + /// Memory mappings created by the bootloader, including the kernel and boot info mappings. + /// + /// This memory should _not_ be used by the kernel. + Bootloader, + /// An unknown memory region reported by the UEFI firmware. + /// + /// This should only be used if the UEFI memory type is known as usable. + UnknownUefi(u32), + /// An unknown memory region reported by the BIOS firmware. + /// + /// This should only be used if the BIOS memory type is known as usable. + UnknownBios(u32), +} + /// A pixel-based framebuffer that controls the screen output. #[derive(Debug)] #[repr(C)] diff --git a/src/lib.rs b/src/lib.rs index c7785e88..9f319023 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,8 +19,6 @@ pub mod config; /// Contains the boot information struct sent by the bootloader to the kernel on startup. pub mod boot_info; -/// Provides a memory region type that is used in the memory map of the boot info structure. -pub mod memory_region; /// Contains the actual bootloader implementation ("bootloader as a binary"). /// diff --git a/src/memory_region.rs b/src/memory_region.rs deleted file mode 100644 index e0fad17e..00000000 --- a/src/memory_region.rs +++ /dev/null @@ -1,45 +0,0 @@ -/// Represent a physical memory region. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[repr(C)] -pub struct MemoryRegion { - /// The physical start address of the region. - pub start: u64, - /// The physical end address (exclusive) of the region. - pub end: u64, - /// The memory type of the memory region. - /// - /// Only [`Usable`][MemoryRegionKind::Usable] regions can be freely used. - pub kind: MemoryRegionKind, -} - -impl MemoryRegion { - /// Creates a new empty memory region (with length 0). - pub const fn empty() -> Self { - MemoryRegion { - start: 0, - end: 0, - kind: MemoryRegionKind::Bootloader, - } - } -} - -/// Represents the different types of memory. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[non_exhaustive] -#[repr(C)] -pub enum MemoryRegionKind { - /// Unused conventional memory, can be used by the kernel. - Usable, - /// Memory mappings created by the bootloader, including the kernel and boot info mappings. - /// - /// This memory should _not_ be used by the kernel. - Bootloader, - /// An unknown memory region reported by the UEFI firmware. - /// - /// This should only be used if the UEFI memory type is known as usable. - UnknownUefi(u32), - /// An unknown memory region reported by the BIOS firmware. - /// - /// This should only be used if the BIOS memory type is known as usable. - UnknownBios(u32), -} From cbf6b61cb5c5463bb9f7ae1c972321e7dbeec040 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 14:26:59 +0100 Subject: [PATCH 161/174] Make config module private and reexport `Config` --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 9f319023..b33815fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,9 +13,10 @@ #![warn(missing_docs)] pub use crate::boot_info::BootInfo; +pub use crate::config::Config; /// Configuration options for the bootloader. -pub mod config; +mod config; /// Contains the boot information struct sent by the bootloader to the kernel on startup. pub mod boot_info; From 285530413dc87d5285a50c8f4fd9449048613793 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 14:29:50 +0100 Subject: [PATCH 162/174] Remove unused `runner` binary --- Cargo.toml | 4 --- src/bin/runner.rs | 69 ----------------------------------------------- 2 files changed, 73 deletions(-) delete mode 100644 src/bin/runner.rs diff --git a/Cargo.toml b/Cargo.toml index ce55e41f..650617a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,10 +19,6 @@ members = [ name = "builder" required-features = ["builder"] -[[bin]] -name = "runner" -required-features = ["runner"] - [[bin]] name = "bios" required-features = ["bios_bin"] diff --git a/src/bin/runner.rs b/src/bin/runner.rs deleted file mode 100644 index e572db4d..00000000 --- a/src/bin/runner.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::{ - env, fs, - path::Path, - process::{exit, Command}, -}; - -type ExitCode = i32; - -fn main() { - let args: Vec = env::args().collect(); - if args.len() > 2 { - eprintln!("too many arguments passed: {:?}", args); - exit(1); - } - if args.len() < 2 { - eprintln!("not enough arguments passed: {:?}", args); - exit(1); - } - let file_path = Path::new(&args[1]); - if !file_path.exists() { - eprintln!("file does not exist: {:?}", file_path); - exit(1); - } - - match runner(file_path) { - Err(err) => { - eprintln!("ERROR: {:?}", err); - exit(1); - } - Ok(Some(exit_code)) => exit(exit_code), - Ok(None) => {} - } -} - -fn runner(file_path: &Path) -> anyhow::Result> { - let uefi_partition_dir = Path::new("target/uefi_esp"); - let boot_dir = uefi_partition_dir.join("EFI").join("BOOT"); - fs::create_dir_all(&boot_dir)?; - fs::copy(file_path, boot_dir.join("BootX64.efi"))?; - - let ovmf_code = Path::new("ovmf/OVMF_CODE.fd").canonicalize()?; - let ovmf_vars = Path::new("ovmf/OVMF_VARS.fd").canonicalize()?; - - let mut qemu = Command::new("qemu-system-x86_64"); - qemu.arg("-drive").arg(format!( - "if=pflash,format=raw,file={},readonly=on", - ovmf_code.display() - )); - qemu.arg("-drive").arg(format!( - "if=pflash,format=raw,file={},readonly=on", - ovmf_vars.display() - )); - qemu.arg("-drive").arg(format!( - "format=raw,file=fat:rw:{}", - uefi_partition_dir.canonicalize()?.display() - )); - qemu.arg("-s"); - qemu.arg("-nodefaults"); - qemu.arg("-vga").arg("std"); - qemu.arg("--no-reboot"); - println!("{:?}", qemu); - let exit_status = qemu.status()?; - let ret = if exit_status.success() { - None - } else { - exit_status.code() - }; - Ok(ret) -} From bbb97cf630d40ed90663e3423572fcde50ab2e5e Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 14:30:04 +0100 Subject: [PATCH 163/174] Remove unused features --- .cargo/config.toml | 2 +- Cargo.toml | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 1991ea6f..7e0eed7a 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,5 @@ [target.x86_64-unknown-uefi] -runner = "cargo run --bin runner --features runner" +runner = "cargo run -p runner" [alias] builder = "run --bin builder --features builder --quiet --" diff --git a/Cargo.toml b/Cargo.toml index 650617a4..f65eb131 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,11 +62,8 @@ proc-macro2 = { version = "1.0", optional = true} [features] default = [] builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json", "fatfs", "gpt"] -runner = ["anyhow"] bios_bin = ["binary", "rsdp"] uefi_bin = ["binary", "uefi"] -recursive_page_table = [] -map_physical_memory = [] binary = [ "llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", "log", "conquer-once", "spinning_top", "serde", "font8x8", "quote", "proc-macro2", From c3526fff2b68d17482969af286c078b43d2a0469 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 14:57:16 +0100 Subject: [PATCH 164/174] Don't include a `;` after `compile_error` --- build.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.rs b/build.rs index 754daeb0..6aee82a8 100644 --- a/build.rs +++ b/build.rs @@ -220,7 +220,7 @@ mod binary { path ); quote! { - compile_error!(#err); + compile_error!(#err) } } Ok(path) => { @@ -258,7 +258,7 @@ mod binary { err.to_string().escape_default() ); quote! { - compile_error!(#err); + compile_error!(#err) } }) } else { @@ -269,7 +269,7 @@ mod binary { path ); quote! { - compile_error!(#err); + compile_error!(#err) } } } From 512d5ae973f95c4355dc78314a3bafd66bc14513 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 14:57:29 +0100 Subject: [PATCH 165/174] Don't escape serde error messages --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 6aee82a8..65330437 100644 --- a/build.rs +++ b/build.rs @@ -255,7 +255,7 @@ mod binary { let err = format!( "failed to parse bootloader config in {}:\n\n{}", path, - err.to_string().escape_default() + err.to_string() ); quote! { compile_error!(#err) From 88e20d3dac190f769d9442388a34c54f6befd239 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 14:57:54 +0100 Subject: [PATCH 166/174] Improve error messages on config parse errors --- build.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/build.rs b/build.rs index 65330437..d7fc55d6 100644 --- a/build.rs +++ b/build.rs @@ -394,10 +394,7 @@ mod binary { if num % 0x1000 == 0 { Ok(AlignedAddress(num)) } else { - Err(serde::de::Error::invalid_value( - serde::de::Unexpected::Unsigned(num), - &self, - )) + Err(serde::de::Error::custom(format!("address {:#x} is not page aligned", num))) } } @@ -411,7 +408,7 @@ mod binary { u64::from_str_radix(&value, 10) } .map_err(|_err| { - serde::de::Error::invalid_value(serde::de::Unexpected::Str(value), &self) + serde::de::Error::custom(format!("string \"{}\" is not a valid memory address", value)) })?; self.visit_u64(num) From a2d55d4323f5a4139e1cbb55bc9f9be6c01cb4da Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 14:58:12 +0100 Subject: [PATCH 167/174] Allow grouping address values with `_` --- build.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.rs b/build.rs index d7fc55d6..95da796c 100644 --- a/build.rs +++ b/build.rs @@ -402,6 +402,9 @@ mod binary { where E: serde::de::Error, { + // ignore any `_` (used for digit grouping) + let value = &value.replace('_', ""); + let num = if value.starts_with("0x") { u64::from_str_radix(&value[2..], 16) } else { From 4431c3bd1651e2e047ed66cb99a8980647b72e47 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 14:58:36 +0100 Subject: [PATCH 168/174] Set `physical-memory-offset` in config for test --- tests/test_kernels/map_phys_mem/Cargo.toml | 1 + tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_kernels/map_phys_mem/Cargo.toml b/tests/test_kernels/map_phys_mem/Cargo.toml index e69fd73d..1d264567 100644 --- a/tests/test_kernels/map_phys_mem/Cargo.toml +++ b/tests/test_kernels/map_phys_mem/Cargo.toml @@ -11,3 +11,4 @@ uart_16550 = "0.2.10" [package.metadata.bootloader] map-physical-memory = true +physical-memory-offset = "0x0000_4000_0000_0000" diff --git a/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs b/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs index 5099c49b..361c4fb4 100644 --- a/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs +++ b/tests/test_kernels/map_phys_mem/src/bin/check_boot_info.rs @@ -44,10 +44,10 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! { ); // check defaults for optional features - assert!(matches!( + assert_eq!( boot_info.physical_memory_offset.into_option(), - Some(_) - )); + Some(0x0000_4000_0000_0000), + ); assert_eq!(boot_info.recursive_index.into_option(), None); // check rsdp_addr From ba9d943dbb18ef756979f1d2c14df297c1003b45 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 15:21:51 +0100 Subject: [PATCH 169/174] Allow specifying addresses as TOML integers too --- build.rs | 21 +++++++++++++++++++-- tests/test_kernels/map_phys_mem/Cargo.toml | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/build.rs b/build.rs index 95da796c..e8b06b05 100644 --- a/build.rs +++ b/build.rs @@ -9,6 +9,7 @@ fn main() { #[cfg(feature = "binary")] mod binary { use quote::quote; + use std::convert::TryInto; pub fn main() { use llvm_tools_build as llvm_tools; @@ -394,10 +395,23 @@ mod binary { if num % 0x1000 == 0 { Ok(AlignedAddress(num)) } else { - Err(serde::de::Error::custom(format!("address {:#x} is not page aligned", num))) + Err(serde::de::Error::custom(format!( + "address {:#x} is not page aligned", + num + ))) } } + fn visit_i64(self, num: i64) -> Result + where + E: serde::de::Error, + { + let unsigned: u64 = num + .try_into() + .map_err(|_| serde::de::Error::custom(format!("address {} is negative", num)))?; + self.visit_u64(unsigned) + } + fn visit_str(self, value: &str) -> Result where E: serde::de::Error, @@ -411,7 +425,10 @@ mod binary { u64::from_str_radix(&value, 10) } .map_err(|_err| { - serde::de::Error::custom(format!("string \"{}\" is not a valid memory address", value)) + serde::de::Error::custom(format!( + "string \"{}\" is not a valid memory address", + value + )) })?; self.visit_u64(num) diff --git a/tests/test_kernels/map_phys_mem/Cargo.toml b/tests/test_kernels/map_phys_mem/Cargo.toml index 1d264567..03985391 100644 --- a/tests/test_kernels/map_phys_mem/Cargo.toml +++ b/tests/test_kernels/map_phys_mem/Cargo.toml @@ -11,4 +11,4 @@ uart_16550 = "0.2.10" [package.metadata.bootloader] map-physical-memory = true -physical-memory-offset = "0x0000_4000_0000_0000" +physical-memory-offset = 0x0000_4000_0000_0000 From fb4813dad03a7ab2d84a2b03d53c7a4a6a55a412 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 15:22:13 +0100 Subject: [PATCH 170/174] Remove unused `Default` implementation for `Config` --- src/config.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/config.rs b/src/config.rs index e6d64cde..377322a0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -47,19 +47,3 @@ pub struct Config { /// Only considered if `map_framebuffer` is `true`. pub framebuffer_address: Option, } - -impl Default for Config { - fn default() -> Self { - Config { - map_physical_memory: false, - map_page_table_recursively: false, - map_framebuffer: true, - physical_memory_offset: None, - recursive_index: None, - kernel_stack_address: None, - kernel_stack_size: None, - boot_info_address: None, - framebuffer_address: None, - } - } -} From 536e0f6b53b8dcd53b6125c3383dec3bdb7a3cc3 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 15:22:48 +0100 Subject: [PATCH 171/174] Add docs for crate and `Config` struct --- Cargo.toml | 2 +- src/config.rs | 31 ++++++++++++++++++++++++++++- src/lib.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 79 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f65eb131..76085f98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "bootloader" version = "0.10.0-alpha-03" authors = ["Philipp Oppermann "] license = "MIT/Apache-2.0" -description = "An experimental pure-Rust x86 bootloader." +description = "An experimental x86_64 bootloader that works on both BIOS and UEFI systems." repository = "https://github.com/rust-osdev/bootloader" edition = "2018" build = "build.rs" diff --git a/src/config.rs b/src/config.rs index 377322a0..6859f947 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,36 @@ /// Allows configuring the bootloader behavior. /// /// To control these, use a `[package.metadata.bootloader]` table in the `Cargo.toml` of -/// your kernel. +/// your kernel. The naming convention for all config fields is `kebab-case`, otherwise the +/// config keys correspond to the field names of this struct (i.e. just replace `_` with `-`). +/// Unknown config keys lead to an error. +/// +/// ## Example +/// +/// To map the complete physical memory starting at virtual address `0x0000_4000_0000_0000`, add +/// the following to your kernel's `Cargo.toml`: +/// +/// ```toml +/// [package.metadata.bootloader] +/// map-physical-memory = true +/// physical-memory-offset = 0x0000_4000_0000_0000 +/// ``` +/// +/// ## Memory Addresses +/// +/// Memory addresses must be positive and page aligned. Since TOML does not support unsigned 64-bit +/// integers, we also support string input to specify addresses larger than `i64::MAX`. For example: +/// +/// ```toml +/// physical-memory-offset = "0xf000_0000_0000_0000" +/// ``` +/// +/// The above example would fail if the address was specified as integer instead (i.e. without +/// the quotes). +/// +/// All memory addresses are optional, even if their corresponding switch is enabled. If no +/// address is specified, the bootloader will choose an unused entry of the level 4 page table +/// at runtime. #[derive(Debug)] pub struct Config { /// Whether to create a virtual mapping of the complete physical memory. diff --git a/src/lib.rs b/src/lib.rs index b33815fc..18d71ef1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,51 @@ -//! This library part of the bootloader allows kernels to retrieve information from the bootloader. -//! -//! To combine your kernel with the bootloader crate you need a tool such -//! as [`bootimage`](https://github.com/rust-osdev/bootimage). See the -//! [_Writing an OS in Rust_](https://os.phil-opp.com/minimal-rust-kernel/#creating-a-bootimage) -//! blog for an explanation. +/*! +An experimental x86_64 bootloader that works on both BIOS and UEFI systems. + +To use this crate, specify it as a dependency in the `Cargo.toml` of your operating system +kernel. Then you can use the [`entry_point`] macro to mark your entry point function. This +gives you access to the [`BootInfo`] struct, which is passed by the bootloader. + +## Disk Image Creation + +Including the `bootloader` crate as a dependency makes the kernel binary suitable for booting, +but does not create any bootable disk images. To create them, two additional steps are needed: + +1. **Locate the source code of the `bootloader` dependency** on your local system. By using the + dependency source code directly, we ensure that the kernel and bootloader use the same version + of the [`BootInfo`] struct. + - When creating a builder binary written in Rust, the + [`bootloader_locator`](https://docs.rs/bootloader-locator/0.0.4/bootloader_locator/) crate can + be used to automate this step. + - Otherwise, the + [`cargo metadata`](https://doc.rust-lang.org/cargo/commands/cargo-metadata.html) subcommand + can be used to locate the dependency. The command outputs a JSON object with various metadata + for the current package. To find the `bootloader` source path in it, first look for the + "bootloader" dependency under `resolve.nodes.deps` to find out its ID (in the `pkg` field). + Then use that ID to find the bootloader in `packages`. Its `manifest_path` field contains the + local path to the `Cargo.toml` of the bootloader. +2. **Run the following command** in the source code directory of the `bootloader` dependency to create + the bootable disk images: + + ```notrust + cargo builder --kernel-manifest path/to/kernel/Cargo.toml --kernel-binary path/to/kernel_bin + ``` + + The `--kernel-manifest` argument should point to the `Cargo.toml` of your kernel. It is used + for applying configuration settings. The `--kernel-binary` argument should point to the kernel + executable that should be used for the bootable disk images. + + In addition to the `--kernel-manifest` and `--kernel-binary` arguments, it is recommended to also + set the `--target-dir` and `--out-dir` arguments. The former specifies the directory that should + used for cargo build artifacts and the latter specfies the directory where the resulting disk + images should be placed. It is recommended to set `--target-dir` to the `target` folder of your + kernel and `--out-dir` to the the parent folder of `--kernel-binary`. + +## Configuration + +The bootloader can be configured through a `[package.metadata.bootloader]` table in the +`Cargo.toml` of the kernel (the one passed as `--kernel-manifest`). See the [`Config`] struct +for all possible configuration options. +*/ #![cfg_attr(not(feature = "builder"), no_std)] #![feature(asm)] From 2058af6fcece4424ea2cbc7859a3ac79b79a7078 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 15:35:07 +0100 Subject: [PATCH 172/174] Rename the output images from `bootimage-*` to `boot-*` To avoid confusion with the `bootimage` crate. --- src/bin/builder.rs | 5 ++--- tests/runner/src/main.rs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/bin/builder.rs b/src/bin/builder.rs index 365c3b7a..37d39041 100644 --- a/src/bin/builder.rs +++ b/src/bin/builder.rs @@ -154,8 +154,7 @@ fn main() -> anyhow::Result<()> { })?; if let Some(out_dir) = &args.out_dir { - let efi_file = - out_dir.join(format!("bootimage-{}-{}.efi", executable_name, kernel_name)); + let efi_file = out_dir.join(format!("boot-{}-{}.efi", executable_name, kernel_name)); create_uefi_disk_image(&executable_path, &efi_file) .context("failed to create UEFI disk image")?; } @@ -208,7 +207,7 @@ fn main() -> anyhow::Result<()> { let mut output_bin_path = executable_path .parent() .unwrap() - .join(format!("bootimage-{}-{}.img", executable_name, kernel_name)); + .join(format!("boot-{}-{}.img", executable_name, kernel_name)); create_disk_image(&executable_path, &output_bin_path) .context("Failed to create bootable disk image")?; diff --git a/tests/runner/src/main.rs b/tests/runner/src/main.rs index 1af3db65..5a5fc7a7 100644 --- a/tests/runner/src/main.rs +++ b/tests/runner/src/main.rs @@ -66,7 +66,7 @@ pub fn create_disk_image(kernel_binary_path: &Path, bios_only: bool) -> PathBuf let disk_image = kernel_binary_path .parent() .unwrap() - .join(format!("bootimage-bios-{}.img", kernel_binary_name)); + .join(format!("boot-bios-{}.img", kernel_binary_name)); if !disk_image.exists() { panic!( "Disk image does not exist at {} after bootloader build", From eccb89d61a3e390b36f767d6d8780187bd962e58 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Mar 2021 15:35:20 +0100 Subject: [PATCH 173/174] Document the created disk images --- src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 18d71ef1..27b916de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,22 @@ but does not create any bootable disk images. To create them, two additional ste images should be placed. It is recommended to set `--target-dir` to the `target` folder of your kernel and `--out-dir` to the the parent folder of `--kernel-binary`. +This will result in the following files, which are placed in the specified `--out-dir`: + +- A disk image suitable for BIOS booting, named `boot-bios-.img`, where `` is the + name of your kernel executable. This image can be started in QEMU or booted on a real machine + after burning it to an USB stick.. +- A disk image suitable for UEFI booting, named `boot-uefi-.img`. Like the BIOS disk image, + this can be started in QEMU (requires OVMF) and burned to an USB stick to run it on a real + machine. +- Intermediate UEFI files + - A FAT partition image named `boot-uefi-.fat`, which can be directly started in QEMU + or written as an EFI system partition to a GPT-formatted disk. + - An EFI file named `boot-uefi-.efi`. This executable is the combination of the + bootloader and kernel executables. It can be started in QEMU or used to construct a bootable + disk image: Create an EFI system partition formatted with the FAT filesystem and place the + EFI file under `efi\boot\bootx64.efi` on that filesystem. + ## Configuration The bootloader can be configured through a `[package.metadata.bootloader]` table in the From d741a8e1d8337d1b037d9454f7202e8974ff6aea Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 5 Apr 2021 14:27:10 +0200 Subject: [PATCH 174/174] Rewrite Readme --- README.md | 210 ++++++++++++------------------------------------------ 1 file changed, 46 insertions(+), 164 deletions(-) diff --git a/README.md b/README.md index 5a5db560..bed48967 100644 --- a/README.md +++ b/README.md @@ -1,190 +1,72 @@ # bootloader -[![Build Status](https://dev.azure.com/rust-osdev/bootloader/_apis/build/status/rust-osdev.bootloader?branchName=master)](https://dev.azure.com/rust-osdev/bootloader/_build/latest?definitionId=1&branchName=master) [![Join the chat at https://gitter.im/rust-osdev/bootloader](https://badges.gitter.im/rust-osdev/bootloader.svg)](https://gitter.im/rust-osdev/bootloader?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -An experimental x86 bootloader written in Rust and inline assembly. - -Written for the [second edition](https://github.com/phil-opp/blog_os/issues/360) of the [Writing an OS in Rust](https://os.phil-opp.com) series. - -## Design - -When you press the power button the computer loads the BIOS from some flash memory stored on the motherboard. -The BIOS initializes and self tests the hardware then loads the first 512 bytes into memory from the media device -(i.e. the cdrom or floppy disk). If the last two bytes equal 0xAA55 then the BIOS will jump to location 0x7C00 effectively -transferring control to the bootloader. At this point the CPU is running in 16 bit mode, -meaning only the 16 bit registers are available. Also since the BIOS only loads the first 512 bytes this means our bootloader -code has to stay below that limit, otherwise we’ll hit uninitialised memory! -Using [Bios interrupt calls](https://en.wikipedia.org/wiki/BIOS_interrupt_call) the bootloader prints debug information to the screen. -For more information on how to write a bootloader click [here](http://3zanders.co.uk/2017/10/13/writing-a-bootloader/). -The assembler files get imported through the [global_asm feature](https://doc.rust-lang.org/unstable-book/library-features/global-asm.html). -The assembler syntax definition used is the one llvm uses: [GNU Assembly](http://microelectronics.esa.int/erc32/doc/as.pdf). - -* stage_1.s -This stage initializes the stack, enables the A20 line, loads the rest of -the bootloader from disk, and jumps to stage_2. - -* stage_2.s -This stage sets the target operating mode, loads the kernel from disk, -creates an e820 memory map, enters protected mode, and jumps to the -third stage. - -* stage_3.s -This stage performs some checks on the CPU (cpuid, long mode), sets up an -initial page table mapping (identity map the bootloader, map the P4 -recursively, map the kernel blob to 4MB), enables paging, switches to long -mode, and jumps to stage_4. - - -## Build chain -The file `.cargo/config` defines an llvm target file called `x86_64-bootloader.json`. -This file defines the architecture sets freestanding flags and tells llvm to use the linker script `linker.ld`. - -The linker script tells the linker at which offsets the sections should be mapped to. In our case it tells the linker -that the bootloader asm files stage_0-3.s should be mapped to the very beginning of the executable. Read more about linker scripts -[here](https://www.sourceware.org/binutils/docs/ld/Scripts.html) - -Another important role plays the file `build.rs`. -Placing a file named `build.rs` in the root of a package will cause -Cargo to compile that script and execute it just before building the package. -You can read more about it [here](https://doc.rust-lang.org/cargo/reference/build-scripts.html). -The `build.rs` file execute the llvm tools you installed with `rustup component add llvm-tools-preview` -in this order: - -* Check size of .text section of the kernel if it's too small throw an error -```bash -llvm-size "../../target/x86_64-os/debug/svm_kernel" -``` - -* Strip debug symbols from kernel to make loading faster -```bash -llvm-objcopy "--strip-debug" "../../target/x86_64-os/debug/svm_kernel" "target/x86_64-bootloader/debug/build/bootloader-c8df27c930d8f65a/out/kernel_stripped-svm_kernel" -``` -* Rename the .data section to .kernel in the stripped kernel. - Objcopy when using `--binary-architecture` flag creates three synthetic symbols - `_binary_objfile_start`, `_binary_objfile_end`, `_binary_objfile_size.`. -These symbols use the project / binary name which is why we have to rename them to something more generic -to be able to reference them. -```bash -llvm-objcopy "-I" "binary" "-O" "elf64-x86-64" "--binary-architecture=i386:x86-64" "--rename-section" ".data=.kernel" "--redefine-sym" "_binary_kernel_stripped_svm_kernel_start=_kernel_start_addr" "--redefine-sym" "_binary_kernel_stripped_svm_kernel_end=_kernel_end_addr" "--redefine-sym" "_binary_kernel_stripped_svm_kernel_size=_kernel_size" "target/x86_64-bootloader/debug/build/bootloader-c8df27c930d8f65a/out/kernel_stripped-svm_kernel" "target/x86_64-bootloader/debug/build/bootloader-c8df27c930d8f65a/out/kernel_bin-svm_kernel.o" -``` -* Now create a static library out of it -```bash -llvm-ar "crs" "bootloader/target/x86_64-bootloader/debug/build/bootloader-c8df27c930d8f65a/out/libkernel_bin-svm_kernel.a" "target/x86_64-bootloader/debug/build/bootloader-c8df27c930d8f65a/out/kernel_bin-svm_kernel.o" -``` -Afterwards `build.rs` tells cargo to use the newly created static library to link against your kernel, with the help of the linker script everything gets placed correctly in the -resulting elf file. -The last step is to strip away the elf header from the resulting elf binary so that the bios can jump directly to the bootloader `stage_1.s`. This is done with: -```bash -cargo objcopy -- -I elf64-x86-64 -O binary --binary-architecture=i386:x86-64 \ - target/x86_64-bootloader/release/bootloader target/x86_64-bootloader/release/bootloader.bin -``` - - -## Configuration - -The bootloader exposes a few variables which can be configured through the `Cargo.toml` of your kernel: - -```toml -[package.metadata.bootloader] -# The address at which the kernel stack is placed. If not provided, the bootloader -# dynamically searches for a location. -kernel-stack-address = "0xFFFFFF8000000000" - -# The size of the kernel stack, given in number of 4KiB pages. Defaults to 512. -kernel-stack-size = 128 - -# The virtual address offset from which physical memory is mapped, as described in -# https://os.phil-opp.com/paging-implementation/#map-the-complete-physical-memory -# Only applies if the `map_physical_memory` feature of the crate is enabled. -# If not provided, the bootloader dynamically searches for a location. -physical-memory-offset = "0xFFFF800000000000" - -# The address at which the bootinfo struct will be placed. if not provided, -# the bootloader will dynamically search for a location. -boot-info-address = "0xFFFFFFFF80000000" -``` - -Note that the addresses **must** be given as strings (in either hex or decimal format), as [TOML](https://github.com/toml-lang/toml) does not support unsigned 64-bit integers. +[![Docs](https://docs.rs/bootloader/badge.svg)](https://docs.rs/bootloader) +[![Build Status](https://github.com/rust-osdev/bootloader/actions/workflows/build.yml/badge.svg)](https://github.com/rust-osdev/bootloader/actions/workflows/build.yml) +[![Join the chat at https://gitter.im/rust-osdev/bootloader](https://badges.gitter.im/rust-osdev/bootloader.svg)](https://gitter.im/rust-osdev/bootloader?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +An experimental x86_64 bootloader that works on both BIOS and UEFI systems. Written in Rust and some inline assembly, buildable on all platforms without additional build-time dependencies (just some `rustup` components). ## Requirements You need a nightly [Rust](https://www.rust-lang.org) compiler and [cargo xbuild](https://github.com/rust-osdev/cargo-xbuild). You also need the `llvm-tools-preview` component, which can be installed through `rustup component add llvm-tools-preview`. -## Build - -The simplest way to use the bootloader is in combination with the [bootimage](https://github.com/rust-osdev/bootimage) tool. This crate **requires at least bootimage 0.7.7**. With the tool installed, you can add a normal cargo dependency on the `bootloader` crate to your kernel and then run `bootimage build` to create a bootable disk image. You can also execute `bootimage run` to run your kernel in [QEMU](https://www.qemu.org/) (needs to be installed). - -To compile the bootloader manually, you need to invoke `cargo xbuild` with two environment variables: -* `KERNEL`: points to your kernel executable (in the ELF format) -* `KERNEL_MANIFEST`: points to the `Cargo.toml` describing your kernel - -For example: -``` -KERNEL=/path/to/your/kernel/target/debug/your_kernel KERNEL_MANIFEST=/path/to/your/kernel/Cargo.toml cargo xbuild -``` - -As an example, you can build the bootloader with example kernel from the `example-kernel` directory with the following commands: - -``` -cd example-kernel -cargo xbuild -cd .. -KERNEL=example-kernel/target/x86_64-example-kernel/debug/example-kernel KERNEL_MANIFEST=example-kernel/Cargo.toml cargo xbuild --release --features binary -``` +## Usage -The `binary` feature is required to enable the dependencies required for compiling the bootloader executable. The command results in a bootloader executable at `target/x86_64-bootloader.json/release/bootloader`. This executable is still an ELF file, which can't be run directly. +See our [documentation](https://docs.rs/bootloader). -## Run +## Architecture -To run the compiled bootloader executable, you need to convert it to a binary file. You can use the `llvm-objcopy` tools that ships with the `llvm-tools-preview` rustup component. The easiest way to use this tool is using [`cargo-binutils`](https://github.com/rust-embedded/cargo-binutils), which can be installed through `cargo install cargo-binutils`. Then you can perform the conversion with the following command: +This project consists of three separate entities: -``` -cargo objcopy -- -I elf64-x86-64 -O binary --binary-architecture=i386:x86-64 \ - target/x86_64-bootloader/release/bootloader target/x86_64-bootloader/release/bootloader.bin -``` +- A library with the entry point and boot info definitions that kernels can include as a normal cargo dependency. +- BIOS and UEFI binaries that contain the actual bootloader implementation. +- A `builder` binary to simplify the build process of the BIOS and UEFI binaries. -You can run the `bootloader.bin` file using [QEMU](https://www.qemu.org/): +These three entities are currently all combined in a single crate using cargo feature flags. The reason for this is that the kernel and bootloader must use the exact same version of the `BootInfo` struct to prevent undefined behavior (we did not stabilize the boot info format yet, so it might change between versions). -``` -qemu-system-x86_64 -drive format=raw,file=target/x86_64-bootloader/release/bootloader.bin -``` +### Build and Boot -Or burn it to an USB drive to boot it on real hardware: +The build and boot process works the following way: -``` -dd if=target/x86_64-bootloader/release/bootloader.bin of=/dev/sdX && sync -``` +- The `builder` binary is a small command line tool that takes the path to the kernel manifest and binary as arguments. Optionally, it allows to override the cargo target and output dirs. It also accepts a `--quiet` switch and allows to only build the BIOS or UEFI binary instead of both. +- After parsing the arguments, the `builder` binary invokes the actual build command for the BIOS/UEFI binaries, which includes the correct `--target` and `--features` arguments (and `-Zbuild-std`). The kernel manifest and binary paths are passed as `KERNEL_MANIFEST` and `KERNEL` environment variables. +- The next step in the build process is the `build.rs` build script. It only does something when building the BIOS/UEFI binaries (indicated by the `binary` feature), otherwise it is a no-op. + - The script first runs some sanity checks, e.g. the kernel manifest and binary should be specified in env variables and should exist, the correct target triple should be used, and the `llvm-tools` rustup component should be installed. + - Then it copies the kernel executable and strips the debug symbols from it to make it smaller. This does not affect the original kernel binary. The stripped binary is then converted to a byte array and provided to the BIOS/UEFI binaries, either as a Rust `static` or through a linker argument. + - Next, the bootloader configuration is parsed, which can be specified in a `package.metadata.bootloader` table in the kernel manifest file. This requires some custom string parsing since TOML does not support unsigned 64-bit integers. Parse errors are turned into `compile_error!` calls to give nicer error messages. + - After parsing the configuration, it is written as a Rust struct definition into a new `bootloader_config.rs` file in the cargo `OUT_DIR`. This file is then included by the UEFI/BIOS binaries. +- After the build script, the compilation continues with either the `bin/uefi.rs` or the `bin/bios.rs`: + - The `bin/uefi.rs` specifies an UEFI entry point function called `efi_main`. It uses the [`uefi`](https://docs.rs/uefi/0.8.0/uefi/) crate to set up a pixel-based framebuffer using the UEFI GOP protocol. Then it exits the UEFI boot services and stores the physical memory map. The final step is to create some page table abstractions and call into `load_and_switch_to_kernel` function that is shared with the BIOS boot code. + - The `bin/bios.rs` function does not provide a direct entry point. Instead it includes several assembly files (`asm/stage-*.rs`) that implement the CPU initialization (from real mode to long mode), the framebuffer setup (via VESA), and the memory map creation (via a BIOS call). The assembly stages are explained in more detail below. After the assembly stages, the execution jumps to the `bootloader_main` function in `bios.rs`. There we set up some additional identity mapping, translate the memory map and framebuffer into Rust structs, detect the RSDP table, and create some page table abstractions. Then we call into the `load_and_switch_to_kernel` function like the `bin/uefi.rs`. +- The common `load_and_switch_to_kernel` function is defined in `src/binary/mod.rs`. This is also the file that includes the `bootloader_config.rs` generated by the build script. The `load_and_switch_to_kernel` functions performs the following steps: + - Parse the kernel binary and map it in a new page table. This includes setting up the correct permissions for each page, initializing `.bss` sections, and allocating a stack with guard page. The relevant functions for these steps are `set_up_mappings` and `load_kernel`. + - Create the `BootInfo` struct, which abstracts over the differences between BIOS and UEFI booting. This step is implemented in the `create_boot_info` function. + - Do a context switch and jump to the kernel entry point function. This involves identity-mapping the context switch function itself in both the kernel and bootloader page tables to prevent a page fault after switching page tables. This switch step is implemented in the `switch_to_kernel` and `context_switch` functions. +- As a last step after a successful build, the `builder` binary turns the compiled bootloader executable (includes the kernel) into a bootable disk image. For UEFI, this means that a FAT partition and a GPT disk image are created. For BIOS, the `llvm-objcopy` tool is used to convert the `bootloader` executable to a flat binary, as it already contains a basic MBR. -Where sdX is the device name of your USB stick. **Be careful** to choose the correct device name, because everything on that device is overwritten. +### BIOS Assembly Stages -## Debugging -Set a breakpoint at address `0x7c00`. Disassemble instructions with gdb: -```bash -qemu-system-x86_64 -drive format=raw,file=target/x86_64-bootloader/release/bootloader.bin -s -S -``` -``` -(gdb) target remote: 1234 -(gdb) b *0x7c00 -(gdb) x/i $rip -``` +When you press the power button the computer loads the BIOS from some flash memory stored on the motherboard. The BIOS initializes and self tests the hardware then loads the first 512 bytes into memory from the media device (i.e. the cdrom or floppy disk). If the last two bytes equal 0xAA55 then the BIOS will jump to location 0x7C00 effectively transferring control to the bootloader. -If you use the `-enable-kvm` flag you need to use hardware breakpoints `hb`. +At this point the CPU is running in 16 bit mode, meaning only the 16 bit registers are available. Also since the BIOS only loads the first 512 bytes this means our bootloader code has to stay below that limit, otherwise we’ll hit uninitialised memory! Using [Bios interrupt calls](https://en.wikipedia.org/wiki/BIOS_interrupt_call) the bootloader prints debug information to the screen. -## Features -The bootloader crate can be configured through some cargo features: +For more information on how to write a bootloader click [here](http://3zanders.co.uk/2017/10/13/writing-a-bootloader/). The assembler files get imported through the [global_asm feature](https://doc.rust-lang.org/unstable-book/library-features/global-asm.html). The assembler syntax definition used is the one llvm uses: [GNU Assembly](http://microelectronics.esa.int/erc32/doc/as.pdf). -- `vga_320x200`: This feature switches the VGA hardware to mode 0x13, a graphics mode with resolution 320x200 and 256 colors per pixel. The framebuffer is linear and lives at address `0xa0000`. -- `recursive_page_table`: Maps the level 4 page table recursively and adds the [`recursive_page_table_address`](https://docs.rs/bootloader/0.4.0/bootloader/bootinfo/struct.BootInfo.html#structfield.recursive_page_table_addr) field to the passed `BootInfo`. -- `map_physical_memory`: Maps the complete physical memory in the virtual address space and passes a [`physical_memory_offset`](https://docs.rs/bootloader/0.4.0/bootloader/bootinfo/struct.BootInfo.html#structfield.physical_memory_offset) field in the `BootInfo`. -- `sse` enables sse instruction support -- The virtual address where the physical memory should be mapped is configurable by setting the `physical-memory-offset` field in the kernel's `Cargo.toml`, as explained in [Configuration](#Configuration). +The purposes of the individual assembly stages in this project are the following: +- stage_1.s: This stage initializes the stack, enables the A20 line, loads the rest of the bootloader from disk, and jumps to stage_2. +- stage_2.s: This stage sets the target operating mode, loads the kernel from disk,creates an e820 memory map, enters protected mode, and jumps to the third stage. +- stage_3.s: This stage performs some checks on the CPU (cpuid, long mode), sets up an initial page table mapping (identity map the bootloader, map the P4 recursively, map the kernel blob to 4MB), enables paging, switches to long mode, and jumps to stage_4. -## Advanced Documentation -See these guides for advanced usage of this crate: +## Future Plans -- [Chainloading](doc/chainloading.md) -- Higher Half Kernel - TODO +- [ ] 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. +- [ ] Stabilize the boot info format and make it possible to check the version at runtime. +- [ ] Instead of searching the bootloader source in the cargo cache on building, use the upcoming ["artifact dependencies"](https://github.com/rust-lang/cargo/issues/9096) feature of cargo to download the builder binary separately. Requires doing a boot info version check on build time. +- [ ] Transform this "Future Plans" list into issues and a roadmap. ## License