diff --git a/src/fde.rs b/src/fde/custom.rs similarity index 82% rename from src/fde.rs rename to src/fde/custom.rs index 0f21caf..47f8e72 100644 --- a/src/fde.rs +++ b/src/fde/custom.rs @@ -3,261 +3,27 @@ #![allow(clippy::collapsible_if)] #![allow(clippy::collapsible_match)] +use super::*; + use core::{cmp, mem, ptr, slice}; use orbclient::{Color, Renderer}; use orbfont::Text; use std::ffi; use std::prelude::*; use std::proto::Protocol; -use std::uefi::hii::database::HiiHandle; +use std::uefi::hii::StringId; use std::uefi::hii::ifr::{ - HiiValue, IfrAction, IfrCheckbox, IfrNumeric, IfrOneOf, IfrOneOfOption, IfrOpCode, IfrOpHeader, - IfrOrderedList, IfrRef, IfrStatementHeader, IfrSubtitle, IfrTypeValueEnum, + IfrAction, IfrCheckbox, IfrNumeric, IfrOneOf, IfrOpCode, IfrOrderedList, IfrRef, + IfrStatementHeader, IfrSubtitle, IfrTypeValueEnum, }; -use std::uefi::hii::{AnimationId, ImageId, StringId}; -use std::uefi::text::TextInputKey; use crate::display::{Display, Output}; use crate::hii::string::HiiStringProtocol; use crate::key::{Key, raw_key}; use crate::ui::Ui; -// TODO: move to uefi library { -#[repr(C)] -pub struct ListEntry { - Flink: *mut ListEntry, - Blink: *mut ListEntry, -} - -#[allow(dead_code)] -impl ListEntry { - pub fn previous(&self) -> Option<&Self> { - if self.Blink.is_null() { - None - } else { - Some(unsafe { &*self.Blink }) - } - } - - pub fn previous_mut(&mut self) -> Option<&mut Self> { - if self.Blink.is_null() { - None - } else { - Some(unsafe { &mut *self.Blink }) - } - } - - pub fn next(&self) -> Option<&Self> { - if self.Flink.is_null() { - None - } else { - Some(unsafe { &*self.Flink }) - } - } - - pub fn next_mut(&mut self) -> Option<&mut Self> { - if self.Flink.is_null() { - None - } else { - Some(unsafe { &mut *self.Flink }) - } - } - - unsafe fn object_at(&self, offset: usize) -> &T { - let addr = self as *const Self as usize; - unsafe { &*((addr - offset) as *const T) } - } - - unsafe fn object_at_mut(&mut self, offset: usize) -> &mut T { - let addr = self as *mut Self as usize; - unsafe { &mut *((addr - offset) as *mut T) } - } -} - -pub trait ListEntryObject { - unsafe fn object(&self) -> &T; - - #[allow(dead_code)] - unsafe fn object_mut(&mut self) -> &mut T; -} - -macro_rules! list_entry { - ($t:ident, $l:tt) => { - impl ListEntryObject<$t> for ListEntry<$t> { - unsafe fn object(&self) -> &$t { - unsafe { self.object_at(mem::offset_of!($t, $l)) } - } - - unsafe fn object_mut(&mut self) -> &mut $t { - unsafe { self.object_at_mut(mem::offset_of!($t, $l)) } - } - } - }; -} - -pub struct ListEntryIter<'a, T> { - start: Option<&'a ListEntry>, - current: Option<&'a ListEntry>, -} - -impl<'a, T> Iterator for ListEntryIter<'a, T> -where - ListEntry: ListEntryObject, -{ - type Item = &'a T; - fn next(&mut self) -> Option { - let current = self.current.take()?; - let next = current.next(); - if next.map(|x| x as *const _) == self.start.map(|x| x as *const _) { - self.current = None; - return None; - } else { - self.current = next; - } - Some(unsafe { current.object() }) - } -} - -#[repr(transparent)] -pub struct ListHead(ListEntry); - -impl ListHead { - pub fn iter(&self) -> ListEntryIter<'_, T> { - let next = self.0.next(); - ListEntryIter { - start: next, - current: next, - } - } -} -// } TODO: move to uefi library - -#[repr(C)] -pub struct QuestionOption { - pub Signature: usize, - pub Link: ListEntry, - pub OptionOpCodePtr: *const IfrOneOfOption, - pub ImageId: ImageId, - pub AnimationId: AnimationId, -} -list_entry!(QuestionOption, Link); - -impl QuestionOption { - pub fn OptionOpCode(&self) -> Option<&IfrOneOfOption> { - if self.OptionOpCodePtr.is_null() { - None - } else { - Some(unsafe { &*self.OptionOpCodePtr }) - } - } -} - -#[repr(C)] -pub struct StatementErrorInfo { - pub StringId: StringId, - pub TimeOut: u8, -} - -pub type ValidateQuestion = extern "efiapi" fn( - Form: &Form, - Statement: &Statement, - Value: &HiiValue, - ErrorInfo: &mut StatementErrorInfo, -) -> u32; - -pub type PasswordCheck = - extern "efiapi" fn(Form: &Form, Statement: &Statement, PasswordString: *const u16) -> Status; - -#[repr(C)] -pub struct Statement { - pub Signature: usize, - pub Version: usize, - pub DisplayLink: ListEntry, - pub OpCodePtr: *const IfrOpHeader, - pub CurrentValue: HiiValue, - pub SettingChangedFlag: bool, - pub NestStatementList: ListHead, - pub OptionListHead: ListHead, - pub Attribute: u32, - pub ValidateQuestion: Option, - pub PasswordCheck: Option, - pub ImageId: ImageId, - pub AnimationId: AnimationId, -} -list_entry!(Statement, DisplayLink); - -impl Statement { - pub fn OpCode(&self) -> Option<&IfrOpHeader> { - if self.OpCodePtr.is_null() { - None - } else { - Some(unsafe { &*self.OpCodePtr }) - } - } -} - -#[repr(C)] -pub struct ScreenDescriptor { - pub LeftColumn: usize, - pub RightColumn: usize, - pub TopRow: usize, - pub BottomRow: usize, -} - -#[repr(C)] -pub struct HotKey { - pub Signature: usize, - pub Link: ListEntry, - pub KeyData: *const TextInputKey, - pub Action: u32, - pub DefaultId: u16, - pub HelpString: *const u16, -} -list_entry!(HotKey, Link); - -#[repr(C)] -pub struct Form { - pub Signature: usize, - pub Version: usize, - pub StatementListHead: ListHead, - pub StatementListOSF: ListHead, - pub ScreenDimensions: *const ScreenDescriptor, - pub FormSetGuid: Guid, - pub HiiHandle: HiiHandle, - pub FormId: u16, - pub FormTitle: StringId, - pub Attribute: u32, - pub SettingChangedFlag: bool, - pub HighLightedStatement: *const Statement, - pub FormRefreshEvent: Event, - pub HotKeyListHead: ListHead, - pub ImageId: ImageId, - pub AnimationId: AnimationId, - pub BrowserStatus: u32, - pub ErrorString: *const u16, -} - const FRONT_PAGE_FORM_ID: u16 = 0x7600; -const BROWSER_ACTION_NONE: u32 = 1 << 16; -const BROWSER_ACTION_FORM_EXIT: u32 = 1 << 17; - -#[repr(C)] -pub struct UserInput { - pub SelectedStatement: *const Statement, - pub InputValue: HiiValue, - pub Action: u32, - pub DefaultId: u16, -} - -#[repr(C)] -#[allow(non_snake_case)] -pub struct Fde { - pub FormDisplay: extern "efiapi" fn(FormData: &Form, UserInputData: &mut UserInput) -> Status, - pub ExitDisplay: extern "efiapi" fn(), - pub ConfirmDataChange: extern "efiapi" fn() -> usize, -} - static mut DISPLAY: *mut Display = ptr::null_mut(); #[allow(dead_code)] @@ -980,8 +746,17 @@ fn form_display_inner(form: &Form, user_input: &mut UserInput) -> Result<()> { Ok(()) } -extern "efiapi" fn form_display(form: &Form, user_input: &mut UserInput) -> Status { - form_display_inner(form, user_input).into() +extern "efiapi" fn form_display(form: *const Form, user_input: *mut UserInput) -> Status { + if form.is_null() || user_input.is_null() { + return Status::INVALID_PARAMETER; + } + + // SAFETY: Pointer is valid. + let form = unsafe { &*form }; + // SAFETY: Pointer is valid. + let input = unsafe { &mut *user_input }; + + form_display_inner(form, input).into() } extern "efiapi" fn exit_display() {} @@ -990,22 +765,24 @@ extern "efiapi" fn confirm_data_change() -> usize { 0 } -impl Fde { - pub fn install() -> Result<()> { - let guid = guid!("9bbe29e9-fda1-41ec-ad52-452213742d2e"); - - let uefi = unsafe { std::system_table_mut() }; - - let current = unsafe { - let mut interface = 0; - Result::from((uefi.BootServices.LocateProtocol)(&guid, 0, &mut interface))?; - &mut *(interface as *mut Fde) - }; +// TODO: Implement HII Popup and correctly install the protocols instead of +// modifying the function pointers of the edk2 installed protocol. +pub fn install() -> Result<()> { + let uefi = unsafe { std::system_table_mut() }; + + let current = unsafe { + let mut interface = 0; + Result::from((uefi.BootServices.LocateProtocol)( + &FormDisplayEngine::GUID, + 0, + &mut interface, + ))?; + &mut *(interface as *mut FormDisplayEngine) + }; - current.FormDisplay = form_display; - current.ExitDisplay = exit_display; - current.ConfirmDataChange = confirm_data_change; + current.FormDisplay = form_display; + current.ExitDisplay = exit_display; + current.ConfirmDataChange = confirm_data_change; - Ok(()) - } + Ok(()) } diff --git a/src/fde/list.rs b/src/fde/list.rs new file mode 100644 index 0000000..03807dd --- /dev/null +++ b/src/fde/list.rs @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-3.0-only + +// `LIST_ENTRY` implemenation +// TODO: Move to uefi library + +#[repr(C)] +pub struct ListEntry { + pub Flink: *mut ListEntry, + pub Blink: *mut ListEntry, +} + +#[allow(dead_code)] +impl ListEntry { + pub fn previous(&self) -> Option<&Self> { + if self.Blink.is_null() { + None + } else { + Some(unsafe { &*self.Blink }) + } + } + + pub fn previous_mut(&mut self) -> Option<&mut Self> { + if self.Blink.is_null() { + None + } else { + Some(unsafe { &mut *self.Blink }) + } + } + + pub fn next(&self) -> Option<&Self> { + if self.Flink.is_null() { + None + } else { + Some(unsafe { &*self.Flink }) + } + } + + pub fn next_mut(&mut self) -> Option<&mut Self> { + if self.Flink.is_null() { + None + } else { + Some(unsafe { &mut *self.Flink }) + } + } + + pub unsafe fn object_at(&self, offset: usize) -> &T { + let addr = self as *const Self as usize; + unsafe { &*((addr - offset) as *const T) } + } + + pub unsafe fn object_at_mut(&mut self, offset: usize) -> &mut T { + let addr = self as *mut Self as usize; + unsafe { &mut *((addr - offset) as *mut T) } + } +} + +pub trait ListEntryObject { + unsafe fn object(&self) -> &T; + + #[allow(dead_code)] + unsafe fn object_mut(&mut self) -> &mut T; +} + +macro_rules! list_entry { + ($t:ident, $l:tt) => { + impl ListEntryObject<$t> for ListEntry<$t> { + unsafe fn object(&self) -> &$t { + unsafe { self.object_at(core::mem::offset_of!($t, $l)) } + } + + unsafe fn object_mut(&mut self) -> &mut $t { + unsafe { self.object_at_mut(core::mem::offset_of!($t, $l)) } + } + } + }; +} + +pub struct ListEntryIter<'a, T> { + start: Option<&'a ListEntry>, + current: Option<&'a ListEntry>, +} + +impl<'a, T> Iterator for ListEntryIter<'a, T> +where + ListEntry: ListEntryObject, +{ + type Item = &'a T; + fn next(&mut self) -> Option { + let current = self.current.take()?; + let next = current.next(); + if next.map(|x| x as *const _) == self.start.map(|x| x as *const _) { + self.current = None; + return None; + } else { + self.current = next; + } + Some(unsafe { current.object() }) + } +} + +#[repr(transparent)] +pub struct ListHead(ListEntry); + +impl ListHead { + pub fn iter(&self) -> ListEntryIter<'_, T> { + let next = self.0.next(); + ListEntryIter { + start: next, + current: next, + } + } +} diff --git a/src/fde/mod.rs b/src/fde/mod.rs new file mode 100644 index 0000000..9c41773 --- /dev/null +++ b/src/fde/mod.rs @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-3.0-only + +//! edk2 Form Display Engine (FDE) Protocol + +pub mod custom; +#[macro_use] +pub mod list; + +use list::*; +use std::prelude::*; +use std::uefi::hii::database::HiiHandle; +use std::uefi::hii::ifr::{HiiValue, IfrOneOfOption, IfrOpHeader}; +use std::uefi::hii::{AnimationId, ImageId, StringId}; +use std::uefi::text::TextInputKey; + +// TODO: Move to FormBrowser2 +#[repr(C)] +pub struct ScreenDescriptor { + pub LeftColumn: usize, + pub RightColumn: usize, + pub TopRow: usize, + pub BottomRow: usize, +} + +// Custom browser actions defined by FDE. +pub const BROWSER_ACTION_NONE: u32 = 1 << 16; +pub const BROWSER_ACTION_FORM_EXIT: u32 = 1 << 17; + +/// `DISPLAY_QUESTION_OPTION` +#[repr(C)] +pub struct QuestionOption { + pub Signature: usize, + pub Link: ListEntry, + pub OptionOpCodePtr: *const IfrOneOfOption, + pub ImageId: ImageId, + pub AnimationId: AnimationId, +} +list_entry!(QuestionOption, Link); + +#[allow(dead_code)] +impl QuestionOption { + pub const SIGNATURE: u32 = u32::from_le_bytes(*b"QOPT"); + + pub fn OptionOpCode(&self) -> Option<&IfrOneOfOption> { + if self.OptionOpCodePtr.is_null() { + None + } else { + Some(unsafe { &*self.OptionOpCodePtr }) + } + } +} + +/// `STATEMENT_ERROR_INFO` +#[repr(C)] +pub struct StatementErrorInfo { + pub StringId: StringId, + pub TimeOut: u8, +} + +/// `FORM_DISPLAY_ENGINE_STATEMENT` +#[repr(C)] +pub struct Statement { + pub Signature: usize, + pub Version: usize, + pub DisplayLink: ListEntry, + pub OpCodePtr: *const IfrOpHeader, + pub CurrentValue: HiiValue, + pub SettingChangedFlag: bool, + pub NestStatementList: ListHead, + pub OptionListHead: ListHead, + pub Attribute: u32, + pub ValidateQuestion: unsafe extern "efiapi" fn( + form: *const Form, + statement: *const Self, + value: *const HiiValue, + error_info: *mut StatementErrorInfo, + ) -> u32, + pub PasswordCheck: unsafe extern "efiapi" fn( + form: *const Form, + statement: *const Self, + password_string: *const u16, + ) -> Status, + pub ImageId: ImageId, + pub AnimationId: AnimationId, +} +list_entry!(Statement, DisplayLink); + +#[allow(dead_code)] +impl Statement { + pub const SIGNATURE: u32 = u32::from_le_bytes(*b"FSTA"); + pub const VERSION_1: usize = 0x1_0000; + + pub fn OpCode(&self) -> Option<&IfrOpHeader> { + if self.OpCodePtr.is_null() { + None + } else { + Some(unsafe { &*self.OpCodePtr }) + } + } +} + +/// `BROWSER_HOT_KEY` +#[repr(C)] +pub struct HotKey { + pub Signature: usize, + pub Link: ListEntry, + pub KeyData: *const TextInputKey, + pub Action: u32, + pub DefaultId: u16, + pub HelpString: *const u16, +} +list_entry!(HotKey, Link); + +#[allow(dead_code)] +impl HotKey { + pub const SIGNATURE: u32 = u32::from_le_bytes(*b"BHKS"); +} + +/// `FORM_DISPLAY_ENGINE_FORM` +#[repr(C)] +pub struct Form { + pub Signature: usize, + pub Version: usize, + pub StatementListHead: ListHead, + pub StatementListOSF: ListHead, + pub ScreenDimensions: *const ScreenDescriptor, + pub FormSetGuid: Guid, + pub HiiHandle: HiiHandle, + pub FormId: u16, + pub FormTitle: StringId, + pub Attribute: u32, + pub SettingChangedFlag: bool, + pub HighLightedStatement: *const Statement, + pub FormRefreshEvent: Event, + pub HotKeyListHead: ListHead, + pub ImageId: ImageId, + pub AnimationId: AnimationId, + pub BrowserStatus: u32, + pub ErrorString: *const u16, +} + +#[allow(dead_code)] +impl Form { + pub const SIGNATURE: u32 = u32::from_le_bytes(*b"FFRM"); + pub const VERSION_1: usize = 0x1_0000; +} + +/// `USER_INPUT` +#[repr(C)] +pub struct UserInput { + pub SelectedStatement: *const Statement, + pub InputValue: HiiValue, + pub Action: u32, + pub DefaultId: u16, +} + +/// `EDKII_FORM_DISPLAY_ENGINE_PROTOCOL` +#[derive(Debug)] +#[repr(C)] +pub struct FormDisplayEngine { + pub FormDisplay: unsafe extern "efiapi" fn( + form_data: *const Form, + user_input_data: *mut UserInput, + ) -> Status, + pub ExitDisplay: unsafe extern "efiapi" fn(), + pub ConfirmDataChange: unsafe extern "efiapi" fn() -> usize, +} + +impl FormDisplayEngine { + pub const GUID: Guid = guid!("9bbe29e9-fda1-41ec-ad52-452213742d2e"); +} diff --git a/src/main.rs b/src/main.rs index 14778c3..ef57847 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,8 +26,8 @@ pub extern "C" fn main() -> Status { let _ = (uefi.BootServices.SetWatchdogTimer)(0, 0, 0, ptr::null()); - if let Err(err) = fde::Fde::install() { - println!("Fde error: {:?}", err); + if let Err(err) = fde::custom::install() { + println!("FDE error: {:?}", err); let _ = key::key(true); } @@ -36,5 +36,5 @@ pub extern "C" fn main() -> Status { let _ = key::key(true); } - Status(0) + Status::SUCCESS }