asdf
This commit is contained in:
commit
354012852a
13
.cargo/config
Normal file
13
.cargo/config
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[build]
|
||||||
|
target = "x86_64-unknown-uefi"
|
||||||
|
|
||||||
|
[unstable]
|
||||||
|
build-std = ["core", "compiler_builtins", "alloc"]
|
||||||
|
build-std-features = ["compiler-builtins-mem"]
|
||||||
|
|
||||||
|
[alias]
|
||||||
|
qemu = "run --target x86_64-unknown-uefi"
|
||||||
|
|
||||||
|
[target.'x86_64-unknown-uefi']
|
||||||
|
rustflags = ["-Ctarget-feature=+sse,+mmx,+sse2,-soft-float"]
|
||||||
|
runner = "./qemu.sh"
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
/esp/EFI/BOOT/BOOTX64.EFI
|
||||||
|
/Cargo.lock
|
||||||
|
/target/
|
2
Cargo.toml
Normal file
2
Cargo.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[workspace]
|
||||||
|
members = ["bootloader"]
|
31
bootloader/Cargo.toml
Normal file
31
bootloader/Cargo.toml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
[package]
|
||||||
|
name = "bootloader"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
log = "0.4"
|
||||||
|
anyhow = {version = "1.0", default-features = false}
|
||||||
|
thiserror = {path = "../../nostd/thiserror", default-features = false}
|
||||||
|
itertools = {version = "0.10", default-features = false, features = ["use_alloc"]}
|
||||||
|
|
||||||
|
volatile = "0.4"
|
||||||
|
bitfield = "0.14"
|
||||||
|
bytemuck = "1.12.3"
|
||||||
|
|
||||||
|
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"
|
||||||
|
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"]}
|
23
bootloader/src/context.rs
Normal file
23
bootloader/src/context.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
use uefi::{
|
||||||
|
prelude::BootServices, proto::loaded_image::LoadedImage, table::boot::ScopedProtocol, Handle,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
|
pub struct Context<'a> {
|
||||||
|
handle: Handle,
|
||||||
|
image: ScopedProtocol<'a, LoadedImage>,
|
||||||
|
boot_services: &'a BootServices,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Context<'a> {
|
||||||
|
pub fn new(handle: Handle, boot_services: &'a BootServices) -> Result<Self> {
|
||||||
|
let image = boot_services.open_protocol_exclusive::<LoadedImage>(handle)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
handle,
|
||||||
|
boot_services,
|
||||||
|
image,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
49
bootloader/src/error.rs
Normal file
49
bootloader/src/error.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
use core::fmt::Display;
|
||||||
|
|
||||||
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("SSE/SSE2 not available on this CPU")]
|
||||||
|
NoSSE,
|
||||||
|
#[error("CpuId failed to retrieve features")]
|
||||||
|
NoCpuIdFeatures,
|
||||||
|
#[error("could not find aligned subslice in slice")]
|
||||||
|
NoAlignedSubslice,
|
||||||
|
#[error("No Mode found for this GOP")]
|
||||||
|
NoModeFound,
|
||||||
|
#[error("{0}")]
|
||||||
|
FontError(&'static str),
|
||||||
|
#[error(transparent)]
|
||||||
|
UefiError(#[from] UefiError),
|
||||||
|
#[error(transparent)]
|
||||||
|
LayoutError(#[from] alloc::alloc::LayoutError),
|
||||||
|
#[error(transparent)]
|
||||||
|
AllocError(#[from] alloc::alloc::AllocError),
|
||||||
|
#[error("FromStrWithBufError: {0:?}")]
|
||||||
|
FromStrWithBufError(uefi::data_types::FromStrWithBufError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub struct UefiError {
|
||||||
|
inner: uefi::Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<uefi::data_types::FromStrWithBufError> for Error {
|
||||||
|
fn from(value: uefi::data_types::FromStrWithBufError) -> Self {
|
||||||
|
Self::FromStrWithBufError(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<uefi::Error> for Error {
|
||||||
|
fn from(inner: uefi::Error) -> Self {
|
||||||
|
Self::UefiError(UefiError { inner })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for UefiError {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "UefiStatus: {:?}", self.inner.status())
|
||||||
|
}
|
||||||
|
}
|
33
bootloader/src/fpu.rs
Normal file
33
bootloader/src/fpu.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
use core::arch::asm;
|
||||||
|
|
||||||
|
use crate::error::{Error, Result};
|
||||||
|
use raw_cpuid::CpuId;
|
||||||
|
use x86_64::registers::control::{Cr0, Cr0Flags};
|
||||||
|
|
||||||
|
pub fn enable_fpu() -> Result<()> {
|
||||||
|
let cpuid = CpuId::new();
|
||||||
|
|
||||||
|
if let Some(features) = cpuid.get_feature_info() {
|
||||||
|
if features.has_fpu() && features.has_sse() && features.has_sse2() {
|
||||||
|
let mut cr0 = Cr0::read();
|
||||||
|
|
||||||
|
cr0.set(Cr0Flags::MONITOR_COPROCESSOR, true);
|
||||||
|
cr0.set(Cr0Flags::EXTENSION_TYPE, true);
|
||||||
|
|
||||||
|
cr0.set(Cr0Flags::EMULATE_COPROCESSOR, false);
|
||||||
|
cr0.set(Cr0Flags::TASK_SWITCHED, false);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
Cr0::write(cr0);
|
||||||
|
asm!("fninit");
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("FPU enabled");
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::NoSSE)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(Error::NoCpuIdFeatures)
|
||||||
|
}
|
||||||
|
}
|
255
bootloader/src/fs.rs
Normal file
255
bootloader/src/fs.rs
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
use core::{
|
||||||
|
alloc::{AllocError, Allocator, Layout},
|
||||||
|
borrow::{Borrow, BorrowMut},
|
||||||
|
cell::{Ref, RefCell, RefMut},
|
||||||
|
mem::MaybeUninit,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
ptr::NonNull,
|
||||||
|
};
|
||||||
|
|
||||||
|
use alloc::{borrow::ToOwned, boxed::Box, sync::Arc, vec, vec::Vec};
|
||||||
|
use log::{debug, info};
|
||||||
|
use uefi::{
|
||||||
|
data_types::Align,
|
||||||
|
proto::media::{
|
||||||
|
file::{Directory as UefiDirectory, File, FileAttribute, FileHandle, FileInfo, FileMode},
|
||||||
|
fs::SimpleFileSystem,
|
||||||
|
},
|
||||||
|
table::boot::ScopedProtocol,
|
||||||
|
CStr16, ResultExt, Status,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::error::{Error, Result};
|
||||||
|
|
||||||
|
pub struct FileSystem<'a> {
|
||||||
|
inner: RefCell<ScopedProtocol<'a, SimpleFileSystem>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FileInfoIterator {
|
||||||
|
dir: Directory,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileInfoIterator {
|
||||||
|
pub fn new(dir: Directory) -> Self {
|
||||||
|
Self { dir }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_entry(&mut self) -> Result<Option<Box<FileInfo>>> {
|
||||||
|
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::<FileInfo>::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 alloc::boxed::Box;
|
||||||
|
use uefi::{
|
||||||
|
data_types::Align,
|
||||||
|
proto::media::file::{File, FileAttribute, FileHandle, FileInfo, FileMode},
|
||||||
|
ResultExt, Status,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
|
use super::Directory;
|
||||||
|
|
||||||
|
pub struct DirectoryEntry {
|
||||||
|
dir: Directory,
|
||||||
|
file_info: Box<FileInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Debug for DirectoryEntry {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("DirectoryEntry")
|
||||||
|
.field("file_info", &self.file_info)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirectoryEntry {
|
||||||
|
pub fn open(
|
||||||
|
&self,
|
||||||
|
open_mode: FileMode,
|
||||||
|
attribs: Option<FileAttribute>,
|
||||||
|
) -> Result<FileHandle> {
|
||||||
|
let handle = self.dir.get_mut().open(
|
||||||
|
self.file_info.file_name(),
|
||||||
|
open_mode,
|
||||||
|
attribs.unwrap_or(FileAttribute::empty()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(handle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<FileInfo> for DirectoryEntry {
|
||||||
|
fn as_ref(&self) -> &FileInfo {
|
||||||
|
&self.file_info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for DirectoryEntry {
|
||||||
|
type Target = FileInfo;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.file_info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Directory {
|
||||||
|
pub fn next_entry(&mut self) -> Result<Option<Box<FileInfo>>> {
|
||||||
|
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::<FileInfo>::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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for Directory {
|
||||||
|
type Item = DirectoryEntry;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.next_entry()
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.map(|file_info| DirectoryEntry {
|
||||||
|
dir: self.clone(),
|
||||||
|
file_info,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for FileInfoIterator {
|
||||||
|
type Item = Box<FileInfo>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.next_entry().ok().flatten()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Directory {
|
||||||
|
dir: Arc<RefCell<UefiDirectory>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Directory {
|
||||||
|
pub fn get(&self) -> Ref<'_, UefiDirectory> {
|
||||||
|
RefCell::borrow(&self.dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut<'a>(&'a self) -> RefMut<'a, UefiDirectory> {
|
||||||
|
RefCell::borrow_mut(&self.dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open(
|
||||||
|
&self,
|
||||||
|
filename: &CStr16,
|
||||||
|
open_mode: FileMode,
|
||||||
|
attributes: FileAttribute,
|
||||||
|
) -> uefi::Result<FileHandle> {
|
||||||
|
self.get_mut().open(filename, open_mode, attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_entry<'buf>(
|
||||||
|
&mut self,
|
||||||
|
buffer: &'buf mut [u8],
|
||||||
|
) -> uefi::Result<Option<&'buf mut FileInfo>, Option<usize>> {
|
||||||
|
self.get_mut().read_entry(buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UefiDirectory> for Directory {
|
||||||
|
fn from(value: UefiDirectory) -> Self {
|
||||||
|
Self {
|
||||||
|
dir: Arc::new(RefCell::new(value)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FileSystem<'a> {
|
||||||
|
pub fn new(inner: ScopedProtocol<'a, SimpleFileSystem>) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: RefCell::new(inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open(
|
||||||
|
&self,
|
||||||
|
path: &str,
|
||||||
|
open_mode: FileMode,
|
||||||
|
attribs: Option<FileAttribute>,
|
||||||
|
) -> Result<FileHandle> {
|
||||||
|
let mut buf = vec![0; path.len() + 1];
|
||||||
|
let cstr = CStr16::from_str_with_buf(path, &mut buf)?;
|
||||||
|
Ok(self.root_dir()?.get_mut().open(
|
||||||
|
cstr,
|
||||||
|
open_mode,
|
||||||
|
attribs.unwrap_or(FileAttribute::empty()),
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn root_dir(&self) -> Result<Directory> {
|
||||||
|
Ok(self.inner.borrow_mut().open_volume()?.into())
|
||||||
|
}
|
||||||
|
}
|
96
bootloader/src/graphics.rs
Normal file
96
bootloader/src/graphics.rs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
use alloc::{borrow::ToOwned, vec::Vec};
|
||||||
|
use itertools::Itertools;
|
||||||
|
use terminal::image::{EncodableLayout, RgbaImage};
|
||||||
|
use uefi::{proto::console::gop, table::boot::ScopedProtocol};
|
||||||
|
|
||||||
|
use crate::{error::Error, Result};
|
||||||
|
|
||||||
|
pub struct GraphicsOutput<'a, 'b> {
|
||||||
|
gop: ScopedProtocol<'a, gop::GraphicsOutput<'b>>,
|
||||||
|
info: gop::ModeInfo,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
backbuffer: Vec<gop::BltPixel>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> GraphicsOutput<'a, 'b> {
|
||||||
|
pub fn from_gop(mut gop: ScopedProtocol<'a, gop::GraphicsOutput<'b>>) -> Result<Self> {
|
||||||
|
let mode = {
|
||||||
|
let mode = gop
|
||||||
|
.modes()
|
||||||
|
.map(|mode| {
|
||||||
|
let info = mode.info().to_owned();
|
||||||
|
(mode, info.resolution().0 * info.resolution().1)
|
||||||
|
})
|
||||||
|
.sorted_by(|a, b| a.1.cmp(&b.1))
|
||||||
|
.next();
|
||||||
|
|
||||||
|
mode.map(|(mode, _)| mode)
|
||||||
|
}
|
||||||
|
.ok_or(Error::NoModeFound)?;
|
||||||
|
|
||||||
|
gop.set_mode(&mode)?;
|
||||||
|
|
||||||
|
let info = mode.info();
|
||||||
|
//let pixel_width =
|
||||||
|
|
||||||
|
Ok(Self::new(gop, *info))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolution(&self) -> (usize, usize) {
|
||||||
|
self.info.resolution()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_gop_with_mode(
|
||||||
|
mut gop: ScopedProtocol<'a, gop::GraphicsOutput<'b>>,
|
||||||
|
mode: gop::Mode,
|
||||||
|
) -> Result<Self> {
|
||||||
|
gop.set_mode(&mode)?;
|
||||||
|
|
||||||
|
Ok(Self::new(gop, *mode.info()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(gop: ScopedProtocol<'a, gop::GraphicsOutput<'b>>, info: gop::ModeInfo) -> Self {
|
||||||
|
let black = gop::BltPixel::new(0, 0, 0);
|
||||||
|
Self {
|
||||||
|
gop,
|
||||||
|
info,
|
||||||
|
backbuffer: alloc::vec![black; info.stride() * info.resolution().1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn image_as_bltpixels(image: &RgbaImage) -> &[gop::BltPixel] {
|
||||||
|
let len = (image.width() * image.height()) as usize;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
core::slice::from_raw_parts(image.as_bytes().as_ptr() as *const gop::BltPixel, len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blt_image(&mut self, image: &RgbaImage) -> uefi::Result<()> {
|
||||||
|
let (width, height) = image.dimensions();
|
||||||
|
let op = gop::BltOp::BufferToVideo {
|
||||||
|
buffer: Self::image_as_bltpixels(image),
|
||||||
|
src: gop::BltRegion::Full,
|
||||||
|
dest: (0, 0),
|
||||||
|
dims: (width as usize, height as usize),
|
||||||
|
};
|
||||||
|
self.gop.blt(op)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blt_image_position(
|
||||||
|
&mut self,
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
image: &RgbaImage,
|
||||||
|
) -> uefi::Result<()> {
|
||||||
|
let op = gop::BltOp::BufferToVideo {
|
||||||
|
buffer: Self::image_as_bltpixels(image),
|
||||||
|
src: gop::BltRegion::Full,
|
||||||
|
dest: (x, y),
|
||||||
|
dims: (image.width() as usize, image.height() as usize),
|
||||||
|
};
|
||||||
|
self.gop.blt(op)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
51
bootloader/src/input.rs
Normal file
51
bootloader/src/input.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
use ucs2::decode::traits::DecodeUCS2Char;
|
||||||
|
use uefi::{
|
||||||
|
proto::console::text::{Input as Stdin, Key, ScanCode},
|
||||||
|
table::{Boot, SystemTable},
|
||||||
|
Char16, Event,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum CharOrScanCode {
|
||||||
|
Char(char),
|
||||||
|
ScanCode(ScanCode),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Input {
|
||||||
|
st: SystemTable<Boot>,
|
||||||
|
event: [Event; 1],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Input {
|
||||||
|
pub fn new(st: &SystemTable<Boot>) -> Self {
|
||||||
|
let (st, event) = unsafe {
|
||||||
|
let mut st = st.unsafe_clone();
|
||||||
|
let event = [st.stdin().wait_for_key_event().unsafe_clone()];
|
||||||
|
(st, event)
|
||||||
|
};
|
||||||
|
|
||||||
|
Self { st, event }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stdin(&mut self) -> &mut Stdin {
|
||||||
|
self.st.stdin()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_char16(ch: Char16) -> Option<char> {
|
||||||
|
DecodeUCS2Char::decode_ucs2(u16::from(ch)).into_char()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_key(&mut self) -> Option<CharOrScanCode> {
|
||||||
|
let ref mut events = self.event;
|
||||||
|
|
||||||
|
self.st.boot_services().wait_for_event(events).ok()?;
|
||||||
|
|
||||||
|
if let Some(key) = self.stdin().read_key().ok()? {
|
||||||
|
match key {
|
||||||
|
Key::Printable(ch) => Some(CharOrScanCode::Char(Self::decode_char16(ch)?)),
|
||||||
|
Key::Special(ch) => Some(CharOrScanCode::ScanCode(ch)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
169
bootloader/src/main.rs
Normal file
169
bootloader/src/main.rs
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
#![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)]
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use alloc::{collections::BTreeMap, string::String, vec};
|
||||||
|
use graphics::GraphicsOutput;
|
||||||
|
use log::info;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use uefi::{
|
||||||
|
prelude::*,
|
||||||
|
proto::{
|
||||||
|
console::gop,
|
||||||
|
media::file::{File, FileAttribute, FileMode},
|
||||||
|
},
|
||||||
|
table::{
|
||||||
|
boot::{OpenProtocolAttributes, OpenProtocolParams, SearchType},
|
||||||
|
Boot, SystemTable,
|
||||||
|
},
|
||||||
|
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;
|
||||||
|
|
||||||
|
#[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,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Context {
|
||||||
|
handle: Handle,
|
||||||
|
st: SystemTable<Boot>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn new(handle: Handle, mut st: SystemTable<Boot>) -> uefi::Result<Self> {
|
||||||
|
uefi_services::init(&mut st)?;
|
||||||
|
Ok(Self { handle, st })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fs(&self) -> Result<fs::FileSystem> {
|
||||||
|
let file_system = self.st.boot_services().get_image_file_system(self.handle)?;
|
||||||
|
|
||||||
|
Ok(fs::FileSystem::new(file_system))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn system_table(&self) -> &SystemTable<Boot> {
|
||||||
|
&self.st
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(&self) -> Handle {
|
||||||
|
self.handle
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn system_table_mut(&mut self) -> &mut SystemTable<Boot> {
|
||||||
|
&mut self.st
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn boot_services(&self) -> &BootServices {
|
||||||
|
self.st.boot_services()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_graphics_output(&self) -> Result<GraphicsOutput> {
|
||||||
|
let handles = self
|
||||||
|
.boot_services()
|
||||||
|
.locate_handle_buffer(SearchType::ByProtocol(&gop::GraphicsOutput::GUID))?;
|
||||||
|
|
||||||
|
let gops = handles.handles().iter().filter_map(|handle| unsafe {
|
||||||
|
self.boot_services()
|
||||||
|
.open_protocol::<gop::GraphicsOutput>(
|
||||||
|
OpenProtocolParams {
|
||||||
|
handle: handle.clone(),
|
||||||
|
agent: self.handle,
|
||||||
|
controller: None,
|
||||||
|
},
|
||||||
|
OpenProtocolAttributes::GetProtocol,
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
});
|
||||||
|
|
||||||
|
let (_, (gop, mode)) = gops
|
||||||
|
.map(|gop| {
|
||||||
|
let (pixel_count, mode) = gop
|
||||||
|
.modes()
|
||||||
|
.map(|mode| {
|
||||||
|
let (x, y) = mode.info().resolution();
|
||||||
|
(x * y, mode)
|
||||||
|
})
|
||||||
|
.collect::<BTreeMap<_, _>>()
|
||||||
|
.pop_last()
|
||||||
|
.expect("gop has 0 modes !?");
|
||||||
|
|
||||||
|
(pixel_count, (gop, mode))
|
||||||
|
})
|
||||||
|
.collect::<BTreeMap<_, _>>()
|
||||||
|
.pop_last()
|
||||||
|
.expect("no GOP found!");
|
||||||
|
|
||||||
|
GraphicsOutput::from_gop_with_mode(gop, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(mut self) -> Result<Status> {
|
||||||
|
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 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 display = self.get_graphics_output()?;
|
||||||
|
let (width, height) = display.resolution();
|
||||||
|
|
||||||
|
info!("creating terminal");
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
|
||||||
|
Ok(Status::SUCCESS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn efi_entry(handle: Handle, system_table: SystemTable<Boot>) -> Status {
|
||||||
|
let ctx = Context::new(handle, system_table).unwrap();
|
||||||
|
ctx.run().unwrap()
|
||||||
|
}
|
3
esp/EFI/BOOT/FREELDR.INI
Normal file
3
esp/EFI/BOOT/FREELDR.INI
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[FREELOADER]
|
||||||
|
TimeOut=10
|
||||||
|
DefaultOS=Windows
|
3
esp/test.ini
Normal file
3
esp/test.ini
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[FREELOADER]
|
||||||
|
TimeOut=10
|
||||||
|
DefaultOS=Windows
|
16
qemu.sh
Executable file
16
qemu.sh
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
mkdir -vp esp/EFI/BOOT
|
||||||
|
cp $1 esp/EFI/BOOT/BOOTX64.EFI
|
||||||
|
|
||||||
|
exec qemu-system-x86_64 -accel kvm \
|
||||||
|
-m 4G \
|
||||||
|
-cpu host \
|
||||||
|
-smp 2,sockets=1,dies=1,cores=2,threads=1 \
|
||||||
|
-vga virtio \
|
||||||
|
-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
|
3
rust-toolchain.toml
Normal file
3
rust-toolchain.toml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
||||||
|
components = ["rust-src"]
|
Loading…
Reference in a new issue