diff --git a/.cargo/config b/.cargo/config index 3df4c8b..8f12c6b 100644 --- a/.cargo/config +++ b/.cargo/config @@ -6,7 +6,8 @@ build-std = ["core", "compiler_builtins", "alloc"] build-std-features = ["compiler-builtins-mem"] [alias] -qemu = "run --target x86_64-unknown-uefi" +qemu = "run --bin bootloader --target x86_64-unknown-uefi" +test-qemu = "test --bin test --target x86_64-unknown-uefi" [target.'x86_64-unknown-uefi'] rustflags = ["-Ctarget-feature=+sse,+mmx,+sse2,-soft-float"] diff --git a/Cargo.toml b/Cargo.toml index 5504194..6aaf25f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["bootloader"] \ No newline at end of file +members = ["bootloader", "kibble", "btrfs"] \ No newline at end of file diff --git a/bootloader/Cargo.toml b/bootloader/Cargo.toml index 0f4af9a..c78afb5 100644 --- a/bootloader/Cargo.toml +++ b/bootloader/Cargo.toml @@ -5,8 +5,13 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] +[lib] +[[bin]] +name = "read_btrfs" +path = "src/bin/read_btrfs.rs" + +[dependencies] log = "0.4" anyhow = {version = "1.0", default-features = false} thiserror = {path = "../../nostd/thiserror", default-features = false} @@ -15,17 +20,23 @@ itertools = {version = "0.10", default-features = false, features = ["use_alloc" volatile = "0.4" bitfield = "0.14" bytemuck = "1.12.3" +bitflags = "1.3.1" num-complex = {version = "0.4", default-features = false, features = ["libm"]} fontdue = {version = "0.7"} goblin = {version = "0.6", default-features = false, features = ["alloc", "pe32", "pe64"]} -uefi = { version = "0.18", features = ["alloc", "logger", "exts"] } -uefi-services = "0.15" +# uefi = { version = "0.19", features = ["alloc", "logger", "unstable"] } +# uefi-services = "0.16" +uefi = {path = "../../../uefi-rs/uefi", features = ["alloc", "logger", "unstable"] } +uefi-services = {path = "../../../uefi-rs/uefi-services" } raw-cpuid = "10.0" x86_64 = "0.14" ucs2 = {path = "../../nostd/ucs2-rs"} terminal = {path = "../../nostd/terminal"} -serde_ini = {path = "../../nostd/serde-ini"} -serde = {version = "1.0", default-features = false, features = ["alloc", "derive"]} \ No newline at end of file +toml = {path = "../../nostd/toml/crates/toml", default-features = false} +serde = {version = "1.0", default-features = false, features = ["alloc", "derive"]} + +[dev-dependencies] +x86_64 = "0.14" \ No newline at end of file diff --git a/bootloader/src/context.rs b/bootloader/src/context.rs index db573a0..7be57a2 100644 --- a/bootloader/src/context.rs +++ b/bootloader/src/context.rs @@ -1,8 +1,14 @@ use uefi::{ - prelude::BootServices, proto::loaded_image::LoadedImage, table::boot::ScopedProtocol, Handle, + prelude::BootServices, + proto::{device_path::DevicePath, loaded_image::LoadedImage}, + table::boot::ScopedProtocol, + CString16, Handle, }; -use crate::Result; +use crate::{ + device_path::{DevicePathBuf, DevicePathBufBuilder}, + Result, +}; pub struct Context<'a> { handle: Handle, @@ -20,4 +26,57 @@ impl<'a> Context<'a> { image, }) } + + pub fn handle(&self) -> Handle { + self.handle + } + + pub fn image(&self) -> &ScopedProtocol { + &self.image + } + + pub fn boot_services(&self) -> &BootServices { + self.boot_services + } + + pub fn file_path(&self) -> Option<&DevicePath> { + self.image().file_path() + } + + pub fn device_path(&self) -> Result { + let device_path = self + .boot_services() + .open_protocol_exclusive::(self.image().device())?; + + Ok(DevicePathBufBuilder::new() + .with_device_path(&*device_path)? + .finalize()?) + } + + pub fn load_driver(&self, path: &str) -> Result { + let driver_path = CString16::try_from(path)?; + + let driver_device_path = DevicePathBufBuilder::new() + .with_device_path(self.device_path().expect("device path")) + .and_then(|builder| builder.with_file_path(&driver_path)) + .and_then(|builder| builder.finalize()) + .expect("device path"); + + let handle = self + .boot_services() + .load_image( + self.handle(), + uefi::table::boot::LoadImageSource::FromFilePath { + file_path: &driver_device_path, + from_boot_manager: false, + }, + ) + .expect("btrfs_driver_load"); + + self.boot_services() + .start_image(handle) + .expect("start btrfs driver"); + + Ok(handle) + } } diff --git a/bootloader/src/error.rs b/bootloader/src/error.rs index 9e327c4..1414c3c 100644 --- a/bootloader/src/error.rs +++ b/bootloader/src/error.rs @@ -13,6 +13,10 @@ pub enum Error { NoAlignedSubslice, #[error("No Mode found for this GOP")] NoModeFound, + #[error("supplied buffer was too small, needs to be at least {0} bytes wide.")] + BufferTooSmall(usize), + #[error(transparent)] + Utf8Error(#[from] core::str::Utf8Error), #[error("{0}")] FontError(&'static str), #[error(transparent)] @@ -23,6 +27,10 @@ pub enum Error { AllocError(#[from] alloc::alloc::AllocError), #[error("FromStrWithBufError: {0:?}")] FromStrWithBufError(uefi::data_types::FromStrWithBufError), + #[error("FromStrError: {0:?}")] + FromStrError(#[from] uefi::data_types::FromStrError), + #[error("DevicePathBuildError: {0:?}")] + DevicePathBuildError(uefi::proto::device_path::build::BuildError), } #[derive(Debug, thiserror::Error)] @@ -36,6 +44,12 @@ impl From for Error { } } +impl From for Error { + fn from(value: uefi::proto::device_path::build::BuildError) -> Self { + Self::DevicePathBuildError(value) + } +} + impl From for Error { fn from(inner: uefi::Error) -> Self { Self::UefiError(UefiError { inner }) diff --git a/bootloader/src/fs.rs b/bootloader/src/fs.rs index 69cb8a3..25321bf 100644 --- a/bootloader/src/fs.rs +++ b/bootloader/src/fs.rs @@ -1,85 +1,26 @@ -use core::{ - alloc::{AllocError, Allocator, Layout}, - borrow::{Borrow, BorrowMut}, - cell::{Ref, RefCell, RefMut}, - mem::MaybeUninit, - ops::{Deref, DerefMut}, - ptr::NonNull, -}; +use core::cell::{Ref, RefCell, RefMut}; -use alloc::{borrow::ToOwned, boxed::Box, sync::Arc, vec, vec::Vec}; -use log::{debug, info}; +use alloc::{boxed::Box, sync::Arc, vec}; use uefi::{ - data_types::Align, proto::media::{ file::{Directory as UefiDirectory, File, FileAttribute, FileHandle, FileInfo, FileMode}, fs::SimpleFileSystem, }, table::boot::ScopedProtocol, - CStr16, ResultExt, Status, + CStr16, }; -use crate::error::{Error, Result}; +use crate::error::Result; pub struct FileSystem<'a> { inner: RefCell>, } -pub struct FileInfoIterator { - dir: Directory, -} - -impl FileInfoIterator { - pub fn new(dir: Directory) -> Self { - Self { dir } - } - - pub fn next_entry(&mut self) -> Result>> { - let layout = Layout::from_size_align(0, FileInfo::alignment())?; - let mut ptr = alloc::alloc::Global.allocate(layout)?; - - if let Some(error) = self.dir.read_entry(unsafe { ptr.as_mut() }).err() { - match error.status() { - Status::BUFFER_TOO_SMALL => { - let layout = Layout::from_size_align( - error.data().expect("no size given after buffer_too_small!"), - FileInfo::alignment(), - )?; - let mut ptr = alloc::alloc::Global.allocate(layout)?; - - let file_info = self - .dir - .read_entry(unsafe { ptr.as_mut() }) - .map(|info| { - info.map(|info| unsafe { Box::::from_raw(info as *mut _) }) - .ok_or_else(|| unsafe { - alloc::alloc::Global.deallocate(ptr.cast(), layout); - AllocError - }) - }) - .discard_errdata()??; - Ok(Some(file_info)) - } - _ => Err(error).discard_errdata()?, - } - } else { - Ok(None) - } - } -} - pub mod dir { - use core::{ - alloc::{AllocError, Allocator, Layout}, - ops::Deref, - }; + use core::ops::Deref; use alloc::boxed::Box; - use uefi::{ - data_types::Align, - proto::media::file::{File, FileAttribute, FileHandle, FileInfo, FileMode}, - ResultExt, Status, - }; + use uefi::proto::media::file::{File, FileAttribute, FileHandle, FileInfo, FileMode}; use crate::Result; @@ -130,37 +71,7 @@ pub mod dir { impl Directory { pub fn next_entry(&mut self) -> Result>> { - let layout = Layout::from_size_align(0, FileInfo::alignment())?; - let mut ptr = alloc::alloc::Global.allocate(layout)?; - - if let Some(error) = self.read_entry(unsafe { ptr.as_mut() }).err() { - match error.status() { - Status::BUFFER_TOO_SMALL => { - let layout = Layout::from_size_align( - error.data().expect("no size given after buffer_too_small!"), - FileInfo::alignment(), - )?; - let mut ptr = alloc::alloc::Global.allocate(layout)?; - - let file_info = self - .read_entry(unsafe { ptr.as_mut() }) - .map(|info| { - info.map(|info| unsafe { - Box::::from_raw(info as *mut _) - }) - .ok_or_else(|| unsafe { - alloc::alloc::Global.deallocate(ptr.cast(), layout); - AllocError - }) - }) - .discard_errdata()??; - Ok(Some(file_info)) - } - _ => Err(error).discard_errdata()?, - } - } else { - Ok(None) - } + Ok(self.get_entry()?) } } @@ -179,14 +90,6 @@ pub mod dir { } } -impl Iterator for FileInfoIterator { - type Item = Box; - - fn next(&mut self) -> Option { - self.next_entry().ok().flatten() - } -} - #[repr(transparent)] #[derive(Clone)] pub struct Directory { @@ -211,11 +114,8 @@ impl Directory { self.get_mut().open(filename, open_mode, attributes) } - pub fn read_entry<'buf>( - &mut self, - buffer: &'buf mut [u8], - ) -> uefi::Result, Option> { - self.get_mut().read_entry(buffer) + pub fn get_entry(&mut self) -> uefi::Result>> { + self.get_mut().read_entry_boxed() } } diff --git a/bootloader/src/main.rs b/bootloader/src/main.rs index f4ce6cf..e4e4bcc 100644 --- a/bootloader/src/main.rs +++ b/bootloader/src/main.rs @@ -1,58 +1,50 @@ -#![feature(abi_efiapi)] -#![feature(allocator_api)] -#![feature(error_in_core)] -#![feature(alloc_error_handler)] -#![feature(negative_impls)] -#![feature(int_roundings)] -#![feature(core_intrinsics)] -#![feature(iter_intersperse)] +#![feature( + allocator_api, + ptr_metadata, + error_in_core, + alloc_error_handler, + negative_impls, + int_roundings, + core_intrinsics +)] #![no_std] #![no_main] -use alloc::{collections::BTreeMap, string::String, vec}; -use graphics::GraphicsOutput; +use alloc::{collections::BTreeMap, vec}; use log::info; use serde::Deserialize; use uefi::{ prelude::*, proto::{ console::gop, - media::file::{File, FileAttribute, FileMode}, + device_path::DevicePath, + loaded_image::LoadedImage, + media::{ + file::{File, FileAttribute, FileInfo, FileMode}, + fs::SimpleFileSystem, + }, }, table::{ boot::{OpenProtocolAttributes, OpenProtocolParams, SearchType}, Boot, SystemTable, }, - Identify, Status, + CString16, Identify, Status, }; extern crate alloc; -mod context; -mod error; -mod fpu; -mod fs; -mod graphics; -mod input; - -use error::{Error, Result}; - -use crate::fpu::enable_fpu; +use bootloader::{ + device_path::{DevicePathBuf, DevicePathBufBuilder}, + fpu::enable_fpu, + fs, + graphics::GraphicsOutput, + Result, +}; +#[allow(dead_code)] #[derive(Debug, Deserialize)] -#[serde(rename_all = "PascalCase")] pub struct Config { - #[serde(rename = "FREELOADER")] - freeloader: FreeLoaderConfig, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "PascalCase")] -pub struct FreeLoaderConfig { - timeout: u64, - #[serde(rename = "DefaultOS")] - default_os: String, - font_path: String, + timeout: Option, } #[derive(Debug)] @@ -63,10 +55,24 @@ pub struct Context { impl Context { pub fn new(handle: Handle, mut st: SystemTable) -> uefi::Result { - uefi_services::init(&mut st)?; + uefi_services::init(&mut st).expect("services"); + enable_fpu().expect("no FPU/SSE/SSE2!"); Ok(Self { handle, st }) } + pub fn device_path(&self) -> Result { + let loaded_image = self + .boot_services() + .open_protocol_exclusive::(self.handle())?; + let device_path = self + .boot_services() + .open_protocol_exclusive::(loaded_image.device())?; + + Ok(DevicePathBufBuilder::new() + .with_device_path(&*device_path)? + .finalize()?) + } + pub fn fs(&self) -> Result { let file_system = self.st.boot_services().get_image_file_system(self.handle)?; @@ -128,37 +134,80 @@ impl Context { GraphicsOutput::from_gop_with_mode(gop, mode) } + pub fn load_driver(&self, path: &str) -> Result { + let driver_path = CString16::try_from(path)?; + + let driver_device_path = DevicePathBufBuilder::new() + .with_device_path(self.device_path().expect("device path")) + .and_then(|builder| builder.with_file_path(&driver_path)) + .and_then(|builder| builder.finalize()) + .expect("device path"); + + let handle = self + .boot_services() + .load_image( + self.handle(), + uefi::table::boot::LoadImageSource::FromFilePath { + file_path: &driver_device_path, + from_boot_manager: false, + }, + ) + .expect("btrfs_driver_load"); + + self.boot_services() + .start_image(handle) + .expect("start btrfs driver"); + + Ok(handle) + } + pub fn run(mut self) -> Result { info!("Hello, UEFI!"); self.system_table_mut().stdin().reset(false)?; - enable_fpu().expect("no FPU/SSE/SSE2!"); - let fs = self.fs()?; - for entry in fs.root_dir()?.into_iter() { - info!("{:#?}", entry); - if !entry.attribute().contains(FileAttribute::DIRECTORY) { - let mut file = entry - .open(FileMode::Read, None)? - .into_regular_file() - .unwrap(); + let filename = CString16::try_from("config.toml").unwrap(); + let mut config_file = + fs.root_dir()? + .open(&filename, FileMode::Read, FileAttribute::empty())?; - let mut buf = vec![0u8; entry.file_size() as usize]; - _ = file.read(&mut buf).unwrap(); - let s = String::from_utf8_lossy(&buf); - info!("{:?}", s); - } + let mut buffer = vec![0; config_file.get_boxed_info::()?.file_size() as usize]; + config_file + .into_regular_file() + .unwrap() + .read(&mut buffer) + .unwrap(); + + let config = toml::from_str::(&core::str::from_utf8(&buffer).expect("from_utf8")) + .expect("toml parse"); + + info!("config: {:?}", config); + + drop(fs); + + self.load_driver("bootloader\\drivers\\btrfs_x64.efi") + .expect("driver"); + + let handles = self + .boot_services() + .find_handles::() + .expect("SFS handles"); + + for handle in handles { + if let Ok(mut sfs) = self + .boot_services() + .open_protocol_exclusive::(handle) + { + let mut dir = sfs.open_volume().expect("root dir"); + let info = dir.get_boxed_info::(); + + info!("{:?}", info); + }; } - let mut display = self.get_graphics_output()?; - let (width, height) = display.resolution(); - - info!("creating terminal"); - + info!("done"); loop {} - - Ok(Status::SUCCESS) } } diff --git a/esp/EFI/BOOT/FREELDR.INI b/esp/EFI/BOOT/FREELDR.INI deleted file mode 100644 index 0babcc2..0000000 --- a/esp/EFI/BOOT/FREELDR.INI +++ /dev/null @@ -1,3 +0,0 @@ -[FREELOADER] -TimeOut=10 -DefaultOS=Windows diff --git a/qemu.sh b/qemu.sh index a002c3b..cb53188 100755 --- a/qemu.sh +++ b/qemu.sh @@ -2,15 +2,31 @@ mkdir -vp esp/EFI/BOOT cp $1 esp/EFI/BOOT/BOOTX64.EFI +shift -exec qemu-system-x86_64 -accel kvm \ +echo "$(pwd)" + +qemu-system-x86_64 -accel kvm \ -m 4G \ -cpu host \ -smp 2,sockets=1,dies=1,cores=2,threads=1 \ -vga virtio \ + -nodefaults \ -no-reboot \ -serial stdio \ -usb -device usb-mouse \ -drive if=pflash,format=raw,readonly=on,file=/usr/share/ovmf/x64/OVMF_CODE.fd \ -drive if=pflash,format=raw,readonly=on,file=/usr/share/ovmf/x64/OVMF_VARS.fd \ - -drive format=raw,file=fat:rw:esp + -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ + -drive format=qcow2,file=btrfs.qcow2 \ + -drive format=qcow2,file=ntfs.qcow2 \ + -drive format=raw,file=fat:rw:esp $@ \ + -drive format=qcow2,file=windows.qcow2 + +case $? in + 33) + exit 0;; + *) + exit $?;; +esac +