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