diff --git a/AdvLoggerPkg/Crates/RustAdvancedLoggerDxe/src/lib.rs b/AdvLoggerPkg/Crates/RustAdvancedLoggerDxe/src/lib.rs index 432a1c9689..b7e3042f48 100644 --- a/AdvLoggerPkg/Crates/RustAdvancedLoggerDxe/src/lib.rs +++ b/AdvLoggerPkg/Crates/RustAdvancedLoggerDxe/src/lib.rs @@ -33,14 +33,14 @@ extern crate std; //allow rustdoc links to reference std (e.g. println docs below). use core::{ - ffi::c_void, - fmt::{self, Write}, - ptr, - sync::atomic::{AtomicPtr, Ordering}, + ffi::c_void, + fmt::{self, Write}, + ptr, + sync::atomic::{AtomicPtr, Ordering}, }; use r_efi::{ - efi::{Guid, Status}, - system::BootServices, + efi::{Guid, Status}, + system::BootServices, }; //Global static logger instance - this is a singleton. @@ -59,114 +59,114 @@ pub const DEBUG_ERROR: usize = 0x80000000; // AdvancedLogger protocol definition. Mirrors C definition in AdvLoggerPkg/Include/Protocol/AdvancedLogger.h const ADVANCED_LOGGER_PROTOCOL_GUID: Guid = - Guid::from_fields(0x434f695c, 0xef26, 0x4a12, 0x9e, 0xba, &[0xdd, 0xef, 0x00, 0x97, 0x49, 0x7c]); + Guid::from_fields(0x434f695c, 0xef26, 0x4a12, 0x9e, 0xba, &[0xdd, 0xef, 0x00, 0x97, 0x49, 0x7c]); type AdvancedLoggerWriteProtocol = extern "efiapi" fn(*const AdvancedLoggerProtocol, usize, *const u8, usize); #[repr(C)] struct AdvancedLoggerProtocol { - signature: u32, - version: u32, - write_log: AdvancedLoggerWriteProtocol, + signature: u32, + version: u32, + write_log: AdvancedLoggerWriteProtocol, } // Private un-synchronized AdvancedLogger wrapper. Provides implementation of fmt::Write for AdvancedLogger. #[derive(Debug)] struct AdvancedLogger { - protocol: AtomicPtr, + protocol: AtomicPtr, } impl AdvancedLogger { - // creates a new AdvancedLogger - const fn new() -> Self { - AdvancedLogger { protocol: AtomicPtr::new(ptr::null_mut()) } - } + // creates a new AdvancedLogger + const fn new() -> Self { + AdvancedLogger { protocol: AtomicPtr::new(ptr::null_mut()) } + } - // initialize the AdvancedLogger by acquiring a pointer to the AdvancedLogger protocol. - fn init(&self, bs: *mut BootServices) { - assert!(!bs.is_null(), "BootServices should not be NULL"); - let boot_services = unsafe { &mut ptr::read(bs) }; + // initialize the AdvancedLogger by acquiring a pointer to the AdvancedLogger protocol. + fn init(&self, bs: *mut BootServices) { + assert!(!bs.is_null(), "BootServices should not be NULL"); + let boot_services = unsafe { &mut ptr::read(bs) }; - let mut ptr: *mut c_void = ptr::null_mut(); + let mut ptr: *mut c_void = ptr::null_mut(); - let status = (boot_services.locate_protocol)( - &ADVANCED_LOGGER_PROTOCOL_GUID as *const _ as *mut _, - ptr::null_mut(), - ptr::addr_of_mut!(ptr), - ); + let status = (boot_services.locate_protocol)( + &ADVANCED_LOGGER_PROTOCOL_GUID as *const _ as *mut _, + ptr::null_mut(), + ptr::addr_of_mut!(ptr), + ); - self.protocol.store( - if status == Status::SUCCESS { ptr as *mut AdvancedLoggerProtocol } else { ptr::null_mut() }, - Ordering::SeqCst, - ) - } + self.protocol.store( + if status == Status::SUCCESS { ptr as *mut AdvancedLoggerProtocol } else { ptr::null_mut() }, + Ordering::SeqCst, + ) + } - // log the debug output in `args` at the given log level. - fn log(&self, level: usize, args: fmt::Arguments) { - let logger_ptr = self.protocol.load(Ordering::SeqCst); - if let Some(protocol) = unsafe { logger_ptr.as_mut() } { - let mut log_transaction = LogTransactor { protocol, level }; - log_transaction.write_fmt(args).expect("Printing to log failed."); + // log the debug output in `args` at the given log level. + fn log(&self, level: usize, args: fmt::Arguments) { + let logger_ptr = self.protocol.load(Ordering::SeqCst); + if let Some(protocol) = unsafe { logger_ptr.as_mut() } { + let mut log_transaction = LogTransactor { protocol, level }; + log_transaction.write_fmt(args).expect("Printing to log failed."); + } } - } } struct LogTransactor<'a> { - protocol: &'a mut AdvancedLoggerProtocol, - level: usize, + protocol: &'a mut AdvancedLoggerProtocol, + level: usize, } impl<'a> fmt::Write for LogTransactor<'a> { - fn write_str(&mut self, s: &str) -> fmt::Result { - (self.protocol.write_log)( - self.protocol as *const AdvancedLoggerProtocol, - self.level, - s.as_ptr(), - s.as_bytes().len(), - ); - Ok(()) - } + fn write_str(&mut self, s: &str) -> fmt::Result { + (self.protocol.write_log)( + self.protocol as *const AdvancedLoggerProtocol, + self.level, + s.as_ptr(), + s.as_bytes().len(), + ); + Ok(()) + } } /// Initializes the logging subsystem. The `debug` and `debugln` macros may be called before calling this function, but /// output is discarded if the logger has not yet been initialized via this routine. pub fn init_debug(bs: *mut BootServices) { - LOGGER.init(bs); + LOGGER.init(bs); } #[doc(hidden)] pub fn _log(level: usize, args: fmt::Arguments) { - LOGGER.log(level, args) + LOGGER.log(level, args) } #[cfg(not(feature = "std"))] mod no_std_debug { - /// Prints to the AdvancedLogger log at the specified level. - /// - /// This macro uses the same syntax as rust std [`std::println!`] macro, with the addition of a level argument that - /// indicates what debug level the output is to be written at. - /// - /// See [`std::fmt`] for details on format strings. - /// - /// ```no_run - /// use rust_advanced_logger_dxe::{init_debug, debug, DEBUG_INFO}; - /// use r_efi::efi::Status; - /// pub extern "efiapi" fn efi_main( - /// _image_handle: *const core::ffi::c_void, - /// _system_table: *const r_efi::system::SystemTable, - /// ) -> u64 { - /// - /// //Initialize debug logging - no output without this. - /// init_debug(unsafe { (*_system_table).boot_services}); - /// - /// debug!(DEBUG_INFO, "Hello, World. This is {:} in {:}. ", "rust", "UEFI"); - /// debug!(DEBUG_INFO, "Better add our own newline.\n"); - /// - /// Status::SUCCESS.as_usize() as u64 - /// } - /// ``` - #[macro_export] - macro_rules! debug { + /// Prints to the AdvancedLogger log at the specified level. + /// + /// This macro uses the same syntax as rust std [`std::println!`] macro, with the addition of a level argument that + /// indicates what debug level the output is to be written at. + /// + /// See [`std::fmt`] for details on format strings. + /// + /// ```no_run + /// use rust_advanced_logger_dxe::{init_debug, debug, DEBUG_INFO}; + /// use r_efi::efi::Status; + /// pub extern "efiapi" fn efi_main( + /// _image_handle: *const core::ffi::c_void, + /// _system_table: *const r_efi::system::SystemTable, + /// ) -> u64 { + /// + /// //Initialize debug logging - no output without this. + /// init_debug(unsafe { (*_system_table).boot_services}); + /// + /// debug!(DEBUG_INFO, "Hello, World. This is {:} in {:}. ", "rust", "UEFI"); + /// debug!(DEBUG_INFO, "Better add our own newline.\n"); + /// + /// Status::SUCCESS.as_usize() as u64 + /// } + /// ``` + #[macro_export] + macro_rules! debug { ($level:expr, $($arg:tt)*) => { $crate::_log($level, format_args!($($arg)*)) } @@ -175,32 +175,32 @@ mod no_std_debug { #[cfg(feature = "std")] mod std_debug { - /// Prints to the console log. - /// - /// This macro uses the same syntax as rust std [`std::println!`] macro, with the addition of a level argument that - /// indicates what debug level the output is to be written at. - /// - /// See [`std::fmt`] for details on format strings. - /// - /// ```no_run - /// use rust_advanced_logger_dxe::{init_debug, debug, DEBUG_INFO}; - /// use r_efi::efi::Status; - /// pub extern "efiapi" fn efi_main( - /// _image_handle: *const core::ffi::c_void, - /// _system_table: *const r_efi::system::SystemTable, - /// ) -> u64 { - /// - /// //Initialize debug logging - no output without this. - /// init_debug(unsafe { (*_system_table).boot_services}); - /// - /// debug!(DEBUG_INFO, "Hello, World. This is {:} in {:}. ", "rust", "UEFI"); - /// debug!(DEBUG_INFO, "Better add our own newline.\n"); - /// - /// Status::SUCCESS.as_usize() as u64 - /// } - /// ``` - #[macro_export] - macro_rules! debug { + /// Prints to the console log. + /// + /// This macro uses the same syntax as rust std [`std::println!`] macro, with the addition of a level argument that + /// indicates what debug level the output is to be written at. + /// + /// See [`std::fmt`] for details on format strings. + /// + /// ```no_run + /// use rust_advanced_logger_dxe::{init_debug, debug, DEBUG_INFO}; + /// use r_efi::efi::Status; + /// pub extern "efiapi" fn efi_main( + /// _image_handle: *const core::ffi::c_void, + /// _system_table: *const r_efi::system::SystemTable, + /// ) -> u64 { + /// + /// //Initialize debug logging - no output without this. + /// init_debug(unsafe { (*_system_table).boot_services}); + /// + /// debug!(DEBUG_INFO, "Hello, World. This is {:} in {:}. ", "rust", "UEFI"); + /// debug!(DEBUG_INFO, "Better add our own newline.\n"); + /// + /// Status::SUCCESS.as_usize() as u64 + /// } + /// ``` + #[macro_export] + macro_rules! debug { ($level:expr, $($arg:tt)*) => { let _ = $level; std::print!($($arg)*) @@ -238,104 +238,104 @@ macro_rules! debugln { /// Yields a &'static str that is the name of the containing function. #[macro_export] macro_rules! function { - () => {{ - fn f() {} - fn type_name_of(_: T) -> &'static str { - core::any::type_name::() - } - let name = type_name_of(f); - name.strip_suffix("::f").unwrap() - }}; + () => {{ + fn f() {} + fn type_name_of(_: T) -> &'static str { + core::any::type_name::() + } + let name = type_name_of(f); + name.strip_suffix("::f").unwrap() + }}; } #[cfg(test)] mod tests { - extern crate std; - use crate::{ - debug, init_debug, AdvancedLogger, AdvancedLoggerProtocol, ADVANCED_LOGGER_PROTOCOL_GUID, DEBUG_ERROR, DEBUG_INFO, - DEBUG_INIT, DEBUG_VERBOSE, DEBUG_WARN, LOGGER, - }; - use core::{ffi::c_void, mem::MaybeUninit, slice::from_raw_parts, sync::atomic::Ordering}; - use r_efi::{ - efi::{Guid, Status}, - system::BootServices, - }; - use std::{println, str}; - - static ADVANCED_LOGGER_INSTANCE: AdvancedLoggerProtocol = - AdvancedLoggerProtocol { signature: 0, version: 0, write_log: mock_advanced_logger_write }; - - extern "efiapi" fn mock_advanced_logger_write( - this: *const AdvancedLoggerProtocol, - error_level: usize, - buffer: *const u8, - buffer_size: usize, - ) { - assert_eq!(this, &ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocol); - //to avoid dealing with complicated mock state, we assume that tests will produce the same output string: - //"This is a test.\n", where depends on the debug level (e.g. "DEBUG_INFO"). In addition, - //the string might be built with multiple calls to write, so we just check that it is a substring - //of what we expect. - let buf: &[u8] = unsafe { from_raw_parts(buffer, buffer_size) }; - let str = str::from_utf8(buf).unwrap(); - println!("buffer {buffer:?}:{buffer_size:?}, str: {str:?}"); - match error_level { - DEBUG_INIT => assert!("This is a DEBUG_INIT test.\n".contains(str)), - DEBUG_WARN => assert!("This is a DEBUG_WARN test.\n".contains(str)), - DEBUG_INFO => assert!("This is a DEBUG_INFO test.\n".contains(str)), - DEBUG_VERBOSE => assert!("This is a DEBUG_VERBOSE test.\n".contains(str)), - DEBUG_ERROR => assert!("This is a DEBUG_ERROR test.\n".contains(str)), - _ => panic!("Unrecognized error string."), + extern crate std; + use crate::{ + debug, init_debug, AdvancedLogger, AdvancedLoggerProtocol, ADVANCED_LOGGER_PROTOCOL_GUID, DEBUG_ERROR, + DEBUG_INFO, DEBUG_INIT, DEBUG_VERBOSE, DEBUG_WARN, LOGGER, + }; + use core::{ffi::c_void, mem::MaybeUninit, slice::from_raw_parts, sync::atomic::Ordering}; + use r_efi::{ + efi::{Guid, Status}, + system::BootServices, + }; + use std::{println, str}; + + static ADVANCED_LOGGER_INSTANCE: AdvancedLoggerProtocol = + AdvancedLoggerProtocol { signature: 0, version: 0, write_log: mock_advanced_logger_write }; + + extern "efiapi" fn mock_advanced_logger_write( + this: *const AdvancedLoggerProtocol, + error_level: usize, + buffer: *const u8, + buffer_size: usize, + ) { + assert_eq!(this, &ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocol); + //to avoid dealing with complicated mock state, we assume that tests will produce the same output string: + //"This is a test.\n", where depends on the debug level (e.g. "DEBUG_INFO"). In addition, + //the string might be built with multiple calls to write, so we just check that it is a substring + //of what we expect. + let buf: &[u8] = unsafe { from_raw_parts(buffer, buffer_size) }; + let str = str::from_utf8(buf).unwrap(); + println!("buffer {buffer:?}:{buffer_size:?}, str: {str:?}"); + match error_level { + DEBUG_INIT => assert!("This is a DEBUG_INIT test.\n".contains(str)), + DEBUG_WARN => assert!("This is a DEBUG_WARN test.\n".contains(str)), + DEBUG_INFO => assert!("This is a DEBUG_INFO test.\n".contains(str)), + DEBUG_VERBOSE => assert!("This is a DEBUG_VERBOSE test.\n".contains(str)), + DEBUG_ERROR => assert!("This is a DEBUG_ERROR test.\n".contains(str)), + _ => panic!("Unrecognized error string."), + } } - } - extern "efiapi" fn mock_locate_protocol( - protocol: *mut Guid, - _registration: *mut c_void, - interface: *mut *mut c_void, - ) -> Status { - let protocol = unsafe { protocol.as_mut().unwrap() }; - assert_eq!(protocol, &ADVANCED_LOGGER_PROTOCOL_GUID); - assert!(!interface.is_null()); - unsafe { - interface.write(&ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocol as *mut c_void); + extern "efiapi" fn mock_locate_protocol( + protocol: *mut Guid, + _registration: *mut c_void, + interface: *mut *mut c_void, + ) -> Status { + let protocol = unsafe { protocol.as_mut().unwrap() }; + assert_eq!(protocol, &ADVANCED_LOGGER_PROTOCOL_GUID); + assert!(!interface.is_null()); + unsafe { + interface.write(&ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocol as *mut c_void); + } + Status::SUCCESS } - Status::SUCCESS - } - fn mock_boot_services() -> BootServices { - let boot_services = MaybeUninit::zeroed(); - let mut boot_services: BootServices = unsafe { boot_services.assume_init() }; - boot_services.locate_protocol = mock_locate_protocol; - boot_services - } + fn mock_boot_services() -> BootServices { + let boot_services = MaybeUninit::zeroed(); + let mut boot_services: BootServices = unsafe { boot_services.assume_init() }; + boot_services.locate_protocol = mock_locate_protocol; + boot_services + } - #[test] - fn init_should_initialize_logger() { - let mut boot_services = mock_boot_services(); - static TEST_LOGGER: AdvancedLogger = AdvancedLogger::new(); - TEST_LOGGER.init(&mut boot_services); + #[test] + fn init_should_initialize_logger() { + let mut boot_services = mock_boot_services(); + static TEST_LOGGER: AdvancedLogger = AdvancedLogger::new(); + TEST_LOGGER.init(&mut boot_services); - assert_eq!( - TEST_LOGGER.protocol.load(Ordering::SeqCst) as *const AdvancedLoggerProtocol, - &ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocol - ); - } + assert_eq!( + TEST_LOGGER.protocol.load(Ordering::SeqCst) as *const AdvancedLoggerProtocol, + &ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocol + ); + } - #[test] - fn debug_macro_should_log_things() { - let mut boot_services = mock_boot_services(); - init_debug(&mut boot_services); - - assert_eq!( - LOGGER.protocol.load(Ordering::SeqCst) as *const AdvancedLoggerProtocol, - &ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocol - ); - - debugln!(DEBUG_INIT, "This is a DEBUG_INIT test."); - debugln!(DEBUG_WARN, "This is a {:} test.", "DEBUG_WARN"); - debug!(DEBUG_INFO, "This {:} a {:} test.\n", "is", "DEBUG_INFO"); - debug!(DEBUG_VERBOSE, "This {:} {:} {:} test.\n", "is", "a", "DEBUG_VERBOSE"); - debug!(DEBUG_ERROR, "{:}", "This is a DEBUG_ERROR test.\n"); - } + #[test] + fn debug_macro_should_log_things() { + let mut boot_services = mock_boot_services(); + init_debug(&mut boot_services); + + assert_eq!( + LOGGER.protocol.load(Ordering::SeqCst) as *const AdvancedLoggerProtocol, + &ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocol + ); + + debugln!(DEBUG_INIT, "This is a DEBUG_INIT test."); + debugln!(DEBUG_WARN, "This is a {:} test.", "DEBUG_WARN"); + debug!(DEBUG_INFO, "This {:} a {:} test.\n", "is", "DEBUG_INFO"); + debug!(DEBUG_VERBOSE, "This {:} {:} {:} test.\n", "is", "a", "DEBUG_VERBOSE"); + debug!(DEBUG_ERROR, "{:}", "This is a DEBUG_ERROR test.\n"); + } } diff --git a/HidPkg/Crates/HidIo/src/lib.rs b/HidPkg/Crates/HidIo/src/lib.rs index fbaceb64f0..0db62b829d 100644 --- a/HidPkg/Crates/HidIo/src/lib.rs +++ b/HidPkg/Crates/HidIo/src/lib.rs @@ -8,147 +8,147 @@ #![no_std] pub mod protocol { - use core::ffi::c_void; + use core::ffi::c_void; - use r_efi::efi::{Guid, Status}; + use r_efi::efi::{Guid, Status}; - /// HidIo interface GUID: 3EA93936-6BF4-49D6-AA50-D9F5B9AD8CFF - pub const GUID: Guid = - Guid::from_fields(0x3ea93936, 0x6bf4, 0x49d6, 0xaa, 0x50, &[0xd9, 0xf5, 0xb9, 0xad, 0x8c, 0xff]); + /// HidIo interface GUID: 3EA93936-6BF4-49D6-AA50-D9F5B9AD8CFF + pub const GUID: Guid = + Guid::from_fields(0x3ea93936, 0x6bf4, 0x49d6, 0xaa, 0x50, &[0xd9, 0xf5, 0xb9, 0xad, 0x8c, 0xff]); - #[derive(Debug, PartialEq, Eq, Clone, Copy)] - #[repr(C)] - pub enum HidReportType { - InputReport = 1, - OutputReport = 2, - Feature = 3, - } + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + #[repr(C)] + pub enum HidReportType { + InputReport = 1, + OutputReport = 2, + Feature = 3, + } - /// Retrieve the HID Report Descriptor from the device. - /// - /// # Arguments - /// - /// * `this` - A pointer to the HidIo Instance - /// * `report_descriptor_size` - On input, the size of the buffer allocated to hold the descriptor. On output, the - /// actual size of the descriptor. May be set to zero to query the required size for the - /// descriptor. - /// * `report_descriptor_buffer` - A pointer to the buffer to hold the descriptor. May be NULL if ReportDescriptorSize - /// is zero. - /// # Return values - /// * `Status::SUCCESS` - Report descriptor successfully returned. - /// * `Status::BUFFER_TOO_SMALL` - The provided buffer is not large enough to hold the descriptor. - /// * `Status::INVALID_PARAMETER` - Invalid input parameters. - /// * `Status::NOT_FOUND` - The device does not have a report descriptor. - /// * Other - Unexpected error reading descriptor. - /// - pub type HidIoGetReportDescriptor = extern "efiapi" fn( - this: *const Protocol, - report_descriptor_size: *mut usize, - report_descriptor_buffer: *mut c_void, - ) -> Status; + /// Retrieve the HID Report Descriptor from the device. + /// + /// # Arguments + /// + /// * `this` - A pointer to the HidIo Instance + /// * `report_descriptor_size` - On input, the size of the buffer allocated to hold the descriptor. On output, the + /// actual size of the descriptor. May be set to zero to query the required size for the + /// descriptor. + /// * `report_descriptor_buffer` - A pointer to the buffer to hold the descriptor. May be NULL if ReportDescriptorSize + /// is zero. + /// # Return values + /// * `Status::SUCCESS` - Report descriptor successfully returned. + /// * `Status::BUFFER_TOO_SMALL` - The provided buffer is not large enough to hold the descriptor. + /// * `Status::INVALID_PARAMETER` - Invalid input parameters. + /// * `Status::NOT_FOUND` - The device does not have a report descriptor. + /// * Other - Unexpected error reading descriptor. + /// + pub type HidIoGetReportDescriptor = extern "efiapi" fn( + this: *const Protocol, + report_descriptor_size: *mut usize, + report_descriptor_buffer: *mut c_void, + ) -> Status; - /// Retrieves a single report from the device. - /// - /// # Arguments - /// - /// * `this` - A pointer to the HidIo Instance - /// * `report_id` - Specifies which report to return if the device supports multiple input reports. Set to zero if - /// ReportId is not present. - /// * `report_type` - Indicates the type of report type to retrieve. 1-Input, 3-Feature. - /// * `report_buffer_size` - Indicates the size of the provided buffer to receive the report. - /// * `report_buffer` - Pointer to the buffer to receive the report. - /// - /// # Return values - /// * `Status::SUCCESS` - Report successfully returned. - /// * `Status::OUT_OF_RESOURCES` - The provided buffer is not large enough to hold the report. - /// * `Status::INVALID_PARAMETER` - Invalid input parameters. - /// * Other - Unexpected error reading report. - /// - pub type HidIoGetReport = extern "efiapi" fn( - this: *const Protocol, - report_id: u8, - report_type: HidReportType, - report_buffer_size: usize, - report_buffer: *mut c_void, - ) -> Status; + /// Retrieves a single report from the device. + /// + /// # Arguments + /// + /// * `this` - A pointer to the HidIo Instance + /// * `report_id` - Specifies which report to return if the device supports multiple input reports. Set to zero if + /// ReportId is not present. + /// * `report_type` - Indicates the type of report type to retrieve. 1-Input, 3-Feature. + /// * `report_buffer_size` - Indicates the size of the provided buffer to receive the report. + /// * `report_buffer` - Pointer to the buffer to receive the report. + /// + /// # Return values + /// * `Status::SUCCESS` - Report successfully returned. + /// * `Status::OUT_OF_RESOURCES` - The provided buffer is not large enough to hold the report. + /// * `Status::INVALID_PARAMETER` - Invalid input parameters. + /// * Other - Unexpected error reading report. + /// + pub type HidIoGetReport = extern "efiapi" fn( + this: *const Protocol, + report_id: u8, + report_type: HidReportType, + report_buffer_size: usize, + report_buffer: *mut c_void, + ) -> Status; - /// Sends a single report to the device. - /// - /// # Arguments - /// - /// * `this` - A pointer to the HidIo Instance - /// * `report_id` - Specifies which report to send if the device supports multiple input reports. Set to zero if - /// ReportId is not present. - /// * `report_type` - Indicates the type of report type to retrieve. 2-Output, 3-Feature. - /// * `report_buffer_size` - Indicates the size of the provided buffer holding the report to send. - /// * `report_buffer` - Pointer to the buffer holding the report to send. - /// - /// # Return values - /// * `Status::SUCCESS` - Report successfully transmitted. - /// * `Status::INVALID_PARAMETER` - Invalid input parameters. - /// * Other - Unexpected error transmitting report. - /// - pub type HidIoSetReport = extern "efiapi" fn( - this: *const Protocol, - report_id: u8, - report_type: HidReportType, - report_buffer_size: usize, - report_buffer: *mut c_void, - ) -> Status; + /// Sends a single report to the device. + /// + /// # Arguments + /// + /// * `this` - A pointer to the HidIo Instance + /// * `report_id` - Specifies which report to send if the device supports multiple input reports. Set to zero if + /// ReportId is not present. + /// * `report_type` - Indicates the type of report type to retrieve. 2-Output, 3-Feature. + /// * `report_buffer_size` - Indicates the size of the provided buffer holding the report to send. + /// * `report_buffer` - Pointer to the buffer holding the report to send. + /// + /// # Return values + /// * `Status::SUCCESS` - Report successfully transmitted. + /// * `Status::INVALID_PARAMETER` - Invalid input parameters. + /// * Other - Unexpected error transmitting report. + /// + pub type HidIoSetReport = extern "efiapi" fn( + this: *const Protocol, + report_id: u8, + report_type: HidReportType, + report_buffer_size: usize, + report_buffer: *mut c_void, + ) -> Status; - /// Report received callback function. - /// - /// # Arguments - /// - /// * `report_buffer_size` - Indicates the size of the provided buffer holding the received report. - /// * `report_buffer` - Pointer to the buffer holding the report. - /// * `context` - Context provided when the callback was registered. - /// - pub type HidIoReportCallback = - extern "efiapi" fn(report_buffer_size: u16, report_buffer: *mut c_void, context: *mut c_void); + /// Report received callback function. + /// + /// # Arguments + /// + /// * `report_buffer_size` - Indicates the size of the provided buffer holding the received report. + /// * `report_buffer` - Pointer to the buffer holding the report. + /// * `context` - Context provided when the callback was registered. + /// + pub type HidIoReportCallback = + extern "efiapi" fn(report_buffer_size: u16, report_buffer: *mut c_void, context: *mut c_void); - /// Registers a callback function to receive asynchronous input reports from the device. The device driver will do any - /// necessary initialization to configure the device to send reports. - /// - /// # Arguments - /// - /// * `this` - A pointer to the HidIo Instance - /// * `callback` - Callback function to handle reports as they are received. - /// * `context` - Context that will be provided to the callback function. - /// - /// # Return values - /// * `Status::SUCCESS` - Callback successfully registered. - /// * `Status::INVALID_PARAMETER` - Invalid input parameters. - /// * `Status::ALREADY_STARTED` - Callback function is already registered. - /// * Other - Unexpected error registering callback or initiating report generation from device. - /// - pub type HidIoRegisterReportCallback = - extern "efiapi" fn(this: *const Protocol, callback: HidIoReportCallback, context: *mut c_void) -> Status; + /// Registers a callback function to receive asynchronous input reports from the device. The device driver will do any + /// necessary initialization to configure the device to send reports. + /// + /// # Arguments + /// + /// * `this` - A pointer to the HidIo Instance + /// * `callback` - Callback function to handle reports as they are received. + /// * `context` - Context that will be provided to the callback function. + /// + /// # Return values + /// * `Status::SUCCESS` - Callback successfully registered. + /// * `Status::INVALID_PARAMETER` - Invalid input parameters. + /// * `Status::ALREADY_STARTED` - Callback function is already registered. + /// * Other - Unexpected error registering callback or initiating report generation from device. + /// + pub type HidIoRegisterReportCallback = + extern "efiapi" fn(this: *const Protocol, callback: HidIoReportCallback, context: *mut c_void) -> Status; - /// Unregisters a previously registered callback function. The device driver will do any necessary initialization to - /// configure the device to stop sending reports. - /// - /// # Arguments - /// - /// * `this` - A pointer to the HidIo Instance - /// * `callback` - Callback function to unregister. - /// - /// # Return values - /// * `Status::SUCCESS` - Callback successfully unregistered. - /// * `Status::INVALID_PARAMETER` - Invalid input parameters. - /// * `Status::NOT_STARTED` - Callback function was not previously registered. - /// * Other - Unexpected error unregistering report or disabling report generation from device. - /// - pub type HidIoUnregisterReportCallback = - extern "efiapi" fn(this: *const Protocol, callback: HidIoReportCallback) -> Status; + /// Unregisters a previously registered callback function. The device driver will do any necessary initialization to + /// configure the device to stop sending reports. + /// + /// # Arguments + /// + /// * `this` - A pointer to the HidIo Instance + /// * `callback` - Callback function to unregister. + /// + /// # Return values + /// * `Status::SUCCESS` - Callback successfully unregistered. + /// * `Status::INVALID_PARAMETER` - Invalid input parameters. + /// * `Status::NOT_STARTED` - Callback function was not previously registered. + /// * Other - Unexpected error unregistering report or disabling report generation from device. + /// + pub type HidIoUnregisterReportCallback = + extern "efiapi" fn(this: *const Protocol, callback: HidIoReportCallback) -> Status; - /// The HID_IO protocol provides a set of services for interacting with a HID device. - #[repr(C)] - pub struct Protocol { - pub get_report_descriptor: HidIoGetReportDescriptor, - pub get_report: HidIoGetReport, - pub set_report: HidIoSetReport, - pub register_report_callback: HidIoRegisterReportCallback, - pub unregister_report_callback: HidIoUnregisterReportCallback, - } + /// The HID_IO protocol provides a set of services for interacting with a HID device. + #[repr(C)] + pub struct Protocol { + pub get_report_descriptor: HidIoGetReportDescriptor, + pub get_report: HidIoGetReport, + pub set_report: HidIoSetReport, + pub register_report_callback: HidIoRegisterReportCallback, + pub unregister_report_callback: HidIoUnregisterReportCallback, + } } diff --git a/HidPkg/Crates/HiiKeyboardLayout/src/lib.rs b/HidPkg/Crates/HiiKeyboardLayout/src/lib.rs index fb492651cc..dee908f901 100644 --- a/HidPkg/Crates/HiiKeyboardLayout/src/lib.rs +++ b/HidPkg/Crates/HiiKeyboardLayout/src/lib.rs @@ -36,45 +36,45 @@ extern crate num_derive; use num_traits::FromPrimitive; use r_efi::{ - efi, - hii::{self, PACKAGE_END}, - protocols::hii_database::*, + efi, + hii::{self, PACKAGE_END}, + protocols::hii_database::*, }; use scroll::{ctx, Pread, Pwrite}; /// GUID for default keyboard layout. pub const DEFAULT_KEYBOARD_LAYOUT_GUID: efi::Guid = - efi::Guid::from_fields(0x3a4d7a7c, 0x18a, 0x4b42, 0x81, 0xb3, &[0xdc, 0x10, 0xe3, 0xb5, 0x91, 0xbd]); + efi::Guid::from_fields(0x3a4d7a7c, 0x18a, 0x4b42, 0x81, 0xb3, &[0xdc, 0x10, 0xe3, 0xb5, 0x91, 0xbd]); /// HII Keyboard Package List /// Refer to UEFI spec version 2.10 section 33.3.1.2 which defines the generic header structure. This implementation /// only supports HII Keyboard Packages; other HII package types (or mixes) are not supported. #[derive(Debug, PartialEq, Eq)] pub struct HiiKeyboardPkgList { - /// The GUID associated with this package list. - pub package_list_guid: efi::Guid, - /// The HiiKeyboardPkg contained in this package list. - pub package: HiiKeyboardPkg, + /// The GUID associated with this package list. + pub package_list_guid: efi::Guid, + /// The HiiKeyboardPkg contained in this package list. + pub package: HiiKeyboardPkg, } /// HII Keyboard Package /// Refer to UEFI spec version 2.10 section 33.3.9 which defines the keyboard package structure. #[derive(Debug, PartialEq, Eq)] pub struct HiiKeyboardPkg { - /// The list of keyboard layouts in this package. - pub layouts: Vec, + /// The list of keyboard layouts in this package. + pub layouts: Vec, } /// HII Keyboard Layout /// Refer to UEFI spec version 2.10 section 34.8.10 which defines the keyboard layout structure. #[derive(Debug, Clone, PartialEq, Eq)] pub struct HiiKeyboardLayout { - /// The unique ID associated with this keyboard layout. - pub guid: efi::Guid, - /// A list of key descriptors - pub keys: Vec, - /// A list of descriptions for this keyboard layout. - pub descriptions: Vec, + /// The unique ID associated with this keyboard layout. + pub guid: efi::Guid, + /// A list of key descriptors + pub keys: Vec, + /// A list of descriptions for this keyboard layout. + pub descriptions: Vec, } /// HII Key descriptor @@ -82,22 +82,22 @@ pub struct HiiKeyboardLayout { #[derive(Debug, Pread, Pwrite, PartialEq, Eq, Clone, Copy)] #[repr(C)] pub struct HiiKeyDescriptor { - /// Describes the physical key on the keyboard. - pub key: EfiKey, - /// Unicode character for the key (note: UEFI only supports UCS-2 encoding). - pub unicode: u16, - /// Unicode character for the key with the shift key being held down. - pub shifted_unicode: u16, - /// Unicode character for the key with the Alt-GR being held down. - pub alt_gr_unicode: u16, - /// Unicode character for the key with the Alt-GR and shift keys being held down. - pub shifted_alt_gr_unicode: u16, - /// Modifier keys are defined to allow for special functionality that is not necessarily accomplished by a printable - /// character. Many of these modifier keys are flags to toggle certain state bits on and off inside of a keyboard - /// driver. See [`r_efi::protocols::hii_database`] for modifier definitions. - pub modifier: u16, - /// Indicates what modifiers affect this key. See [`r_efi::protocols::hii_database`] for "affected by" definitions. - pub affected_attribute: u16, + /// Describes the physical key on the keyboard. + pub key: EfiKey, + /// Unicode character for the key (note: UEFI only supports UCS-2 encoding). + pub unicode: u16, + /// Unicode character for the key with the shift key being held down. + pub shifted_unicode: u16, + /// Unicode character for the key with the Alt-GR being held down. + pub alt_gr_unicode: u16, + /// Unicode character for the key with the Alt-GR and shift keys being held down. + pub shifted_alt_gr_unicode: u16, + /// Modifier keys are defined to allow for special functionality that is not necessarily accomplished by a printable + /// character. Many of these modifier keys are flags to toggle certain state bits on and off inside of a keyboard + /// driver. See [`r_efi::protocols::hii_database`] for modifier definitions. + pub modifier: u16, + /// Indicates what modifiers affect this key. See [`r_efi::protocols::hii_database`] for "affected by" definitions. + pub affected_attribute: u16, } /// Non-Spacing HII Key Descriptor variant. Used for "non-spacing" keys. @@ -105,10 +105,10 @@ pub struct HiiKeyDescriptor { /// are used. #[derive(Debug, PartialEq, Eq, Clone)] pub struct HiiNsKeyDescriptor { - /// The descriptor for the "non-spacing key" itself. - pub descriptor: HiiKeyDescriptor, - /// The list of descriptors that are active if the "non-spacing" key has been pressed. - pub dependent_keys: Vec, + /// The descriptor for the "non-spacing key" itself. + pub descriptor: HiiKeyDescriptor, + /// The list of descriptors that are active if the "non-spacing" key has been pressed. + pub dependent_keys: Vec, } /// HII Key descriptor enumeration. @@ -116,8 +116,8 @@ pub struct HiiNsKeyDescriptor { /// Refer to UEFI spec version 2.10 section 33.2.4.3 #[derive(Debug, Clone, PartialEq, Eq)] pub enum HiiKey { - Key(HiiKeyDescriptor), - NsKey(HiiNsKeyDescriptor), + Key(HiiKeyDescriptor), + NsKey(HiiNsKeyDescriptor), } /// Enumeration of physical keys. @@ -137,332 +137,332 @@ pub enum EfiKey { } impl TryFrom for EfiKey { - type Error = &'static str; - fn try_from(value: u32) -> Result { - ::from_u32(value).ok_or("Invalid EfiKey enum value") - } + type Error = &'static str; + fn try_from(value: u32) -> Result { + ::from_u32(value).ok_or("Invalid EfiKey enum value") + } } /// Description for a keyboard layout. /// Refer to UEFI spec version 2.10 section 34.8.10 #[derive(Debug, Clone, PartialEq, Eq)] pub struct HiiKeyboardDescription { - /// The language code for the description (e.g. "en-US") - pub language: String, - /// The description (e.g. "English Keyboard") - pub description: String, + /// The language code for the description (e.g. "en-US") + pub language: String, + /// The description (e.g. "English Keyboard") + pub description: String, } impl ctx::TryFromCtx<'_> for HiiKeyboardPkgList { - type Error = scroll::Error; - fn try_from_ctx(src: &'_ [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> { - //Note: This is not a general purpose HII package list reader: it only supports a package list with a single - //keyboard layout package in it. - let offset = &mut 0; - //EFI_HII_PACAKGE_LIST_HEADER::PackageListGuid - let guid = efi::Guid::from_fields( - src.gread(offset)?, - src.gread(offset)?, - src.gread(offset)?, - src.gread(offset)?, - src.gread(offset)?, - src.gread_with::<&[u8]>(offset, 6)?.try_into().unwrap(), - ); - //EFI_HII_PACKAGE_LIST_HEADER::PackageLength - let _package_length: u32 = src.gread(offset)?; - - //Read HiiKeyboard Pkg - let hii_keyboard_pkg: HiiKeyboardPkg = src.gread(offset)?; - - //Read EFI_HHI_PACAKGE_END package - let _pkg_end_length_type: u32 = src.gread(offset)?; - - Ok((HiiKeyboardPkgList { package_list_guid: guid, package: hii_keyboard_pkg }, *offset)) - } + type Error = scroll::Error; + fn try_from_ctx(src: &'_ [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> { + //Note: This is not a general purpose HII package list reader: it only supports a package list with a single + //keyboard layout package in it. + let offset = &mut 0; + //EFI_HII_PACAKGE_LIST_HEADER::PackageListGuid + let guid = efi::Guid::from_fields( + src.gread(offset)?, + src.gread(offset)?, + src.gread(offset)?, + src.gread(offset)?, + src.gread(offset)?, + src.gread_with::<&[u8]>(offset, 6)?.try_into().unwrap(), + ); + //EFI_HII_PACKAGE_LIST_HEADER::PackageLength + let _package_length: u32 = src.gread(offset)?; + + //Read HiiKeyboard Pkg + let hii_keyboard_pkg: HiiKeyboardPkg = src.gread(offset)?; + + //Read EFI_HHI_PACAKGE_END package + let _pkg_end_length_type: u32 = src.gread(offset)?; + + Ok((HiiKeyboardPkgList { package_list_guid: guid, package: hii_keyboard_pkg }, *offset)) + } } impl ctx::TryIntoCtx for &HiiKeyboardPkgList { - type Error = scroll::Error; - fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { - let offset = &mut 0; - //EFI_HII_PACKAGE_LIST_HEADER::PackageListGuid - dest.gwrite(&self.package_list_guid.as_bytes()[..], offset)?; + type Error = scroll::Error; + fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { + let offset = &mut 0; + //EFI_HII_PACKAGE_LIST_HEADER::PackageListGuid + dest.gwrite(&self.package_list_guid.as_bytes()[..], offset)?; - //EFI_HII_PACKAGE_LIST_HEADER::PackageLength will be updated at the end. - let mut package_length_offset = *offset; - *offset += 4; + //EFI_HII_PACKAGE_LIST_HEADER::PackageLength will be updated at the end. + let mut package_length_offset = *offset; + *offset += 4; - //Write HiiKeyboardPkg - dest.gwrite(&self.package, offset)?; + //Write HiiKeyboardPkg + dest.gwrite(&self.package, offset)?; - //EFI_HII_PACKAGE_END - let length_type: u32 = 4 | ((PACKAGE_END as u32) << 24); - dest.gwrite(length_type, offset)?; + //EFI_HII_PACKAGE_END + let length_type: u32 = 4 | ((PACKAGE_END as u32) << 24); + dest.gwrite(length_type, offset)?; - //go back and update EFI_HII_PACKAGE_LIST_HEADER::PackageLength - dest.gwrite(*offset as u32, &mut package_length_offset)?; + //go back and update EFI_HII_PACKAGE_LIST_HEADER::PackageLength + dest.gwrite(*offset as u32, &mut package_length_offset)?; - Ok(*offset) - } + Ok(*offset) + } } impl ctx::TryFromCtx<'_> for HiiKeyboardPkg { - type Error = scroll::Error; - fn try_from_ctx(src: &'_ [u8], ctx: ()) -> Result<(Self, usize), Self::Error> { - let offset = &mut 0; - //EFI_HII_KEYBOARD_PACKAGE_HDR::Header (bitfield as single u32) - let length_type: u32 = src.gread(offset)?; - let pkg_type = (length_type >> 24) as u8; - if pkg_type != hii::PACKAGE_KEYBOARD_LAYOUT { - return Err(scroll::Error::BadInput { size: 0, msg: "Unsupported Pkg Type" }); - } - //EFI_HII_KEYBOARD_PACKAGE_HDR::LayoutCount - let layout_count: u16 = src.gread(offset)?; + type Error = scroll::Error; + fn try_from_ctx(src: &'_ [u8], ctx: ()) -> Result<(Self, usize), Self::Error> { + let offset = &mut 0; + //EFI_HII_KEYBOARD_PACKAGE_HDR::Header (bitfield as single u32) + let length_type: u32 = src.gread(offset)?; + let pkg_type = (length_type >> 24) as u8; + if pkg_type != hii::PACKAGE_KEYBOARD_LAYOUT { + return Err(scroll::Error::BadInput { size: 0, msg: "Unsupported Pkg Type" }); + } + //EFI_HII_KEYBOARD_PACKAGE_HDR::LayoutCount + let layout_count: u16 = src.gread(offset)?; - //EFI_HII_KEYBOARD_PACKAGE_HDR::Layout[] array into vector. - let mut layouts = vec![]; - for _ in 0..layout_count { - layouts.push(src.gread_with(offset, ctx)?); - } + //EFI_HII_KEYBOARD_PACKAGE_HDR::Layout[] array into vector. + let mut layouts = vec![]; + for _ in 0..layout_count { + layouts.push(src.gread_with(offset, ctx)?); + } - Ok((HiiKeyboardPkg { layouts }, *offset)) - } + Ok((HiiKeyboardPkg { layouts }, *offset)) + } } impl ctx::TryIntoCtx for &HiiKeyboardPkg { - type Error = scroll::Error; - fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { - let offset = &mut 0; - //EFI_HII_KEYBOARD_PKG_HDR::Header::Length will be updated at the end. - *offset += 4; - //EFI_HII_KEYBOARD_PKG_HDR::LayoutCount - dest.gwrite(self.layouts.len() as u16, offset)?; - //EFI_HII_KEYBOARD_PKG_HDR::Layout[] - for layout in &self.layouts { - dest.gwrite(layout, offset)?; - } - //update EFI_HII_KEYBOARD_PKG_HEADER at offset zero. - let length = *offset; - let length_type: u32 = (hii::PACKAGE_KEYBOARD_LAYOUT as u32) << 24; - let length_type = length_type | (length & 0xFFFFFF) as u32; - dest.gwrite(length_type, &mut 0)?; + type Error = scroll::Error; + fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { + let offset = &mut 0; + //EFI_HII_KEYBOARD_PKG_HDR::Header::Length will be updated at the end. + *offset += 4; + //EFI_HII_KEYBOARD_PKG_HDR::LayoutCount + dest.gwrite(self.layouts.len() as u16, offset)?; + //EFI_HII_KEYBOARD_PKG_HDR::Layout[] + for layout in &self.layouts { + dest.gwrite(layout, offset)?; + } + //update EFI_HII_KEYBOARD_PKG_HEADER at offset zero. + let length = *offset; + let length_type: u32 = (hii::PACKAGE_KEYBOARD_LAYOUT as u32) << 24; + let length_type = length_type | (length & 0xFFFFFF) as u32; + dest.gwrite(length_type, &mut 0)?; - Ok(*offset) - } + Ok(*offset) + } } impl ctx::TryFromCtx<'_> for HiiKeyboardLayout { - type Error = scroll::Error; - fn try_from_ctx(src: &'_ [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> { - let offset = &mut 0; - //EFI_HII_KEYBOARD_LAYOUT::LayoutLength - let _layout_length: u16 = src.gread(offset)?; - //EFI_HII_KEYBOARD_LAYOUT::Guid - let guid = efi::Guid::from_fields( - src.gread(offset)?, - src.gread(offset)?, - src.gread(offset)?, - src.gread(offset)?, - src.gread(offset)?, - src.gread_with::<&[u8]>(offset, 6)?.try_into().unwrap(), - ); - //EFI_HII_KEYBOARD_LAYOUT::LayoutDescriptorStringOffset - let layout_descriptor_string_offset: u32 = src.gread(offset)?; - //EFI_HII_KEYBOARD_LAYOUT::DescriptorCount - let _descriptor_count: u8 = src.gread(offset)?; - - //EFI_HII_KEYBOARD_LAYOUT::Descriptors[] array into vector. Note: descriptor_count is not used since ns_keys - //may consume multiple descriptors which are included in the count, resulting in a vector of "real" descriptors that - //is smaller than the descriptor_count. - let mut descriptors = vec![]; - while *offset < layout_descriptor_string_offset as usize { - descriptors.push(src.gread(offset)?); - } + type Error = scroll::Error; + fn try_from_ctx(src: &'_ [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> { + let offset = &mut 0; + //EFI_HII_KEYBOARD_LAYOUT::LayoutLength + let _layout_length: u16 = src.gread(offset)?; + //EFI_HII_KEYBOARD_LAYOUT::Guid + let guid = efi::Guid::from_fields( + src.gread(offset)?, + src.gread(offset)?, + src.gread(offset)?, + src.gread(offset)?, + src.gread(offset)?, + src.gread_with::<&[u8]>(offset, 6)?.try_into().unwrap(), + ); + //EFI_HII_KEYBOARD_LAYOUT::LayoutDescriptorStringOffset + let layout_descriptor_string_offset: u32 = src.gread(offset)?; + //EFI_HII_KEYBOARD_LAYOUT::DescriptorCount + let _descriptor_count: u8 = src.gread(offset)?; + + //EFI_HII_KEYBOARD_LAYOUT::Descriptors[] array into vector. Note: descriptor_count is not used since ns_keys + //may consume multiple descriptors which are included in the count, resulting in a vector of "real" descriptors that + //is smaller than the descriptor_count. + let mut descriptors = vec![]; + while *offset < layout_descriptor_string_offset as usize { + descriptors.push(src.gread(offset)?); + } - //EFI_DESCRIPTION_STRING_BUNDLE::DescriptionCount - let description_count: u16 = src.gread(offset)?; - let mut descriptions = vec![]; - //EFI_DESCRIPTION_STRING_BUNDLE::DescriptionString[] - for _ in 0..description_count { - descriptions.push(src.gread(offset)?); - } + //EFI_DESCRIPTION_STRING_BUNDLE::DescriptionCount + let description_count: u16 = src.gread(offset)?; + let mut descriptions = vec![]; + //EFI_DESCRIPTION_STRING_BUNDLE::DescriptionString[] + for _ in 0..description_count { + descriptions.push(src.gread(offset)?); + } - Ok((HiiKeyboardLayout { guid, keys: descriptors, descriptions }, *offset)) - } + Ok((HiiKeyboardLayout { guid, keys: descriptors, descriptions }, *offset)) + } } impl ctx::TryIntoCtx for &HiiKeyboardLayout { - type Error = scroll::Error; - fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { - let offset = &mut 0; - //EFI_HII_KEYBOARD_LAYOUT::LayoutLength will be updated at the end. - *offset += 2; - //EFI_HII_KEYBOARD_LAYOUT::Guid - dest.gwrite(&self.guid.as_bytes()[..], offset)?; - //EFI_HII_KEYBOARD_LAYOUT::LayoutDescriptorStringOffset will be updated after writing out the descriptors. - let mut descriptor_string_offset = *offset; - *offset += 4; - - //EFI_HII_KEYBOARD_LAYOUT::DescriptorCount will be updated after writing out the descriptors. - let mut descriptor_count_offset = *offset; - *offset += 1; - - let descriptor_start = *offset; - //EFI_HII_KEYBOARD_LAYOUT::Descriptors[] - for descriptor in &self.keys { - //Note: may expand to more than one descriptor due to non-spacing keys. - dest.gwrite(descriptor, offset)?; - } + type Error = scroll::Error; + fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { + let offset = &mut 0; + //EFI_HII_KEYBOARD_LAYOUT::LayoutLength will be updated at the end. + *offset += 2; + //EFI_HII_KEYBOARD_LAYOUT::Guid + dest.gwrite(&self.guid.as_bytes()[..], offset)?; + //EFI_HII_KEYBOARD_LAYOUT::LayoutDescriptorStringOffset will be updated after writing out the descriptors. + let mut descriptor_string_offset = *offset; + *offset += 4; + + //EFI_HII_KEYBOARD_LAYOUT::DescriptorCount will be updated after writing out the descriptors. + let mut descriptor_count_offset = *offset; + *offset += 1; + + let descriptor_start = *offset; + //EFI_HII_KEYBOARD_LAYOUT::Descriptors[] + for descriptor in &self.keys { + //Note: may expand to more than one descriptor due to non-spacing keys. + dest.gwrite(descriptor, offset)?; + } - //Go back and update EFI_HII_KEYBOARD_LAYOUT::DescriptorCount - let descriptor_count = (*offset - descriptor_start) / mem::size_of::(); - dest.gwrite(descriptor_count as u8, &mut descriptor_count_offset)?; + //Go back and update EFI_HII_KEYBOARD_LAYOUT::DescriptorCount + let descriptor_count = (*offset - descriptor_start) / mem::size_of::(); + dest.gwrite(descriptor_count as u8, &mut descriptor_count_offset)?; - //Go back and update EFI_HII_KEYBOARD_LAYOUT::LayoutDescriptorStringOffset. - dest.gwrite(*offset as u32, &mut descriptor_string_offset)?; + //Go back and update EFI_HII_KEYBOARD_LAYOUT::LayoutDescriptorStringOffset. + dest.gwrite(*offset as u32, &mut descriptor_string_offset)?; - //EFI_DESCRIPTION_STRING_BUNDLE::DescriptionCount - dest.gwrite(self.descriptions.len() as u16, offset)?; + //EFI_DESCRIPTION_STRING_BUNDLE::DescriptionCount + dest.gwrite(self.descriptions.len() as u16, offset)?; - //EFI_DESCRIPTION_STRING_BUNDLE::DescriptionString[] - for description in &self.descriptions { - dest.gwrite(description, offset)?; - } + //EFI_DESCRIPTION_STRING_BUNDLE::DescriptionString[] + for description in &self.descriptions { + dest.gwrite(description, offset)?; + } - //Go back and update EFI_HII_KEYBOARD_LAYOUT::LayoutLength - dest.gwrite(*offset as u16, &mut 0)?; + //Go back and update EFI_HII_KEYBOARD_LAYOUT::LayoutLength + dest.gwrite(*offset as u16, &mut 0)?; - Ok(*offset) - } + Ok(*offset) + } } impl ctx::TryFromCtx<'_> for HiiKey { - type Error = scroll::Error; - fn try_from_ctx(src: &'_ [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> { - let offset = &mut 0; - let descriptor: HiiKeyDescriptor = src.gread(offset)?; - if descriptor.modifier == NS_KEY_MODIFIER { - //For Non-Spacing keys, consume descriptors until we find one without EFI_NS_KEY_DEPENDENCY_MODIFIER or run out. - //Refer to UEFI spec 2.10 section 33.2.4.3 for details. - let mut dependent_keys = vec![]; - while let Ok(dependent_key) = src.pread::(*offset) { - if dependent_key.modifier == NS_KEY_DEPENDENCY_MODIFIER { - //found a dependent descriptor. Re-read it with gread to update offset. - dependent_keys.push(src.gread(offset)?); + type Error = scroll::Error; + fn try_from_ctx(src: &'_ [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> { + let offset = &mut 0; + let descriptor: HiiKeyDescriptor = src.gread(offset)?; + if descriptor.modifier == NS_KEY_MODIFIER { + //For Non-Spacing keys, consume descriptors until we find one without EFI_NS_KEY_DEPENDENCY_MODIFIER or run out. + //Refer to UEFI spec 2.10 section 33.2.4.3 for details. + let mut dependent_keys = vec![]; + while let Ok(dependent_key) = src.pread::(*offset) { + if dependent_key.modifier == NS_KEY_DEPENDENCY_MODIFIER { + //found a dependent descriptor. Re-read it with gread to update offset. + dependent_keys.push(src.gread(offset)?); + } else { + //found a descriptor without EFI_NS_KEY_DEPENDENCY_MODIFIER + break; + } + } + Ok((HiiKey::NsKey(HiiNsKeyDescriptor { descriptor, dependent_keys }), *offset)) } else { - //found a descriptor without EFI_NS_KEY_DEPENDENCY_MODIFIER - break; + Ok((HiiKey::Key(descriptor), *offset)) } - } - Ok((HiiKey::NsKey(HiiNsKeyDescriptor { descriptor, dependent_keys }), *offset)) - } else { - Ok((HiiKey::Key(descriptor), *offset)) } - } } impl ctx::TryIntoCtx for &HiiKey { - type Error = scroll::Error; - fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { - let offset = &mut 0; - match self { - HiiKey::Key(descriptor) => { - dest.gwrite(descriptor, offset)?; - } - HiiKey::NsKey(ns_descriptor) => { - dest.gwrite(&ns_descriptor.descriptor, offset)?; - for descriptor in &ns_descriptor.dependent_keys { - dest.gwrite(descriptor, offset)?; + type Error = scroll::Error; + fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { + let offset = &mut 0; + match self { + HiiKey::Key(descriptor) => { + dest.gwrite(descriptor, offset)?; + } + HiiKey::NsKey(ns_descriptor) => { + dest.gwrite(&ns_descriptor.descriptor, offset)?; + for descriptor in &ns_descriptor.dependent_keys { + dest.gwrite(descriptor, offset)?; + } + } } - } + Ok(*offset) } - Ok(*offset) - } } impl ctx::TryFromCtx<'_> for HiiKeyboardDescription { - type Error = scroll::Error; - fn try_from_ctx(src: &'_ [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> { - let offset = &mut 0; - //consume u16 characters until NULL. - let mut desc_chars = vec![]; - loop { - let desc_char: u16 = src.gread(offset)?; - if desc_char == 0 { - break; - } - desc_chars.push(desc_char); - } - //convert to string. Note: UEFI spec uses UCS-2 encoding, so all valid inputs should translate to UTF-16 without - //error. - let desc_string = String::from_utf16(&desc_chars) - .map_err(|_| scroll::Error::BadInput { size: 0, msg: "Invalid string in keyboard description." })?; - - //split the resulting string on the first space - this gives us language and description. - if let Some((lang, desc)) = desc_string.split_once(' ') { - Ok((HiiKeyboardDescription { language: String::from(lang), description: String::from(desc) }, *offset)) - } else { - Err(scroll::Error::BadInput { size: 0, msg: "No space in keyboard description." }) + type Error = scroll::Error; + fn try_from_ctx(src: &'_ [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> { + let offset = &mut 0; + //consume u16 characters until NULL. + let mut desc_chars = vec![]; + loop { + let desc_char: u16 = src.gread(offset)?; + if desc_char == 0 { + break; + } + desc_chars.push(desc_char); + } + //convert to string. Note: UEFI spec uses UCS-2 encoding, so all valid inputs should translate to UTF-16 without + //error. + let desc_string = String::from_utf16(&desc_chars) + .map_err(|_| scroll::Error::BadInput { size: 0, msg: "Invalid string in keyboard description." })?; + + //split the resulting string on the first space - this gives us language and description. + if let Some((lang, desc)) = desc_string.split_once(' ') { + Ok((HiiKeyboardDescription { language: String::from(lang), description: String::from(desc) }, *offset)) + } else { + Err(scroll::Error::BadInput { size: 0, msg: "No space in keyboard description." }) + } } - } } impl ctx::TryIntoCtx for &HiiKeyboardDescription { - type Error = scroll::Error; - fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { - let offset = &mut 0; - //Format as EFI_DESCRIPTION_STRING per UEFI spec 2.10 section 34.8.10. - let desc_string = format!("{} {}", self.language, self.description); - let mut characters: Vec = desc_string.encode_utf16().collect(); - characters.push(0); - for character in characters { - dest.gwrite(character, offset)?; + type Error = scroll::Error; + fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { + let offset = &mut 0; + //Format as EFI_DESCRIPTION_STRING per UEFI spec 2.10 section 34.8.10. + let desc_string = format!("{} {}", self.language, self.description); + let mut characters: Vec = desc_string.encode_utf16().collect(); + characters.push(0); + for character in characters { + dest.gwrite(character, offset)?; + } + Ok(*offset) } - Ok(*offset) - } } impl ctx::TryFromCtx<'_, scroll::Endian> for EfiKey { - type Error = scroll::Error; - fn try_from_ctx(src: &'_ [u8], _ctx: scroll::Endian) -> Result<(Self, usize), Self::Error> { - let offset = &mut 0; - let efi_key = - EfiKey::try_from(src.gread::(offset)?).map_err(|err| scroll::Error::BadInput { size: 0, msg: err })?; - Ok((efi_key, *offset)) - } + type Error = scroll::Error; + fn try_from_ctx(src: &'_ [u8], _ctx: scroll::Endian) -> Result<(Self, usize), Self::Error> { + let offset = &mut 0; + let efi_key = + EfiKey::try_from(src.gread::(offset)?).map_err(|err| scroll::Error::BadInput { size: 0, msg: err })?; + Ok((efi_key, *offset)) + } } impl ctx::TryIntoCtx for &EfiKey { - type Error = scroll::Error; - fn try_into_ctx(self, dest: &mut [u8], _ctx: scroll::Endian) -> Result { - let offset = &mut 0; - dest.gwrite(*self as u32, offset)?; - Ok(*offset) - } + type Error = scroll::Error; + fn try_into_ctx(self, dest: &mut [u8], _ctx: scroll::Endian) -> Result { + let offset = &mut 0; + dest.gwrite(*self as u32, offset)?; + Ok(*offset) + } } // Convenience macro for defining HiiKey::Key structures. macro_rules! key { - ($key:expr, $unicode:literal, $shifted:literal, $alt_gr:literal, $shifted_alt_gr:literal, $modifier:expr, $affected:expr ) => { - HiiKey::Key(key_descriptor!($key, $unicode, $shifted, $alt_gr, $shifted_alt_gr, $modifier, $affected)) - }; + ($key:expr, $unicode:literal, $shifted:literal, $alt_gr:literal, $shifted_alt_gr:literal, $modifier:expr, $affected:expr ) => { + HiiKey::Key(key_descriptor!($key, $unicode, $shifted, $alt_gr, $shifted_alt_gr, $modifier, $affected)) + }; } // convenience macro for defining HiiKeyDescriptor structures. // note: for unicode characters, these are encoded as u16 for compliance with UEFI spec. UEFI only supports UCS-2 // encoding - so unicode characters that require more than two bytes under UTF-16 are not supported (and will panic). macro_rules! key_descriptor { - ($key:expr, $unicode:literal, $shifted:literal, $alt_gr:literal, $shifted_alt_gr:literal, $modifier:expr, $affected:expr ) => { - HiiKeyDescriptor { - key: $key, - unicode: $unicode.encode_utf16(&mut [0u16; 1])[0], - shifted_unicode: $shifted.encode_utf16(&mut [0u16; 1])[0], - alt_gr_unicode: $alt_gr.encode_utf16(&mut [0u16; 1])[0], - shifted_alt_gr_unicode: $shifted_alt_gr.encode_utf16(&mut [0u16; 1])[0], - modifier: $modifier, - affected_attribute: $affected, - } - }; + ($key:expr, $unicode:literal, $shifted:literal, $alt_gr:literal, $shifted_alt_gr:literal, $modifier:expr, $affected:expr ) => { + HiiKeyDescriptor { + key: $key, + unicode: $unicode.encode_utf16(&mut [0u16; 1])[0], + shifted_unicode: $shifted.encode_utf16(&mut [0u16; 1])[0], + alt_gr_unicode: $alt_gr.encode_utf16(&mut [0u16; 1])[0], + shifted_alt_gr_unicode: $shifted_alt_gr.encode_utf16(&mut [0u16; 1])[0], + modifier: $modifier, + affected_attribute: $affected, + } + }; } /// Returns a default HiiKeyboardLayout (which is a standard US-104 layout) @@ -588,22 +588,22 @@ pub fn get_default_keyboard_layout() -> HiiKeyboardLayout { /// returns a default keyboard layout package pub fn get_default_keyboard_pkg() -> HiiKeyboardPkg { - HiiKeyboardPkg { layouts: vec![get_default_keyboard_layout()] } + HiiKeyboardPkg { layouts: vec![get_default_keyboard_layout()] } } /// Returns a default keyboard layout package list. pub fn get_default_keyboard_pkg_list() -> HiiKeyboardPkgList { - HiiKeyboardPkgList { - package_list_guid: efi::Guid::from_fields( - 0xc0f3b43, - 0x44de, - 0x4907, - 0xb4, - 0x78, - &[0x22, 0x5f, 0x6f, 0x62, 0x89, 0xdc], - ), - package: get_default_keyboard_pkg(), - } + HiiKeyboardPkgList { + package_list_guid: efi::Guid::from_fields( + 0xc0f3b43, + 0x44de, + 0x4907, + 0xb4, + 0x78, + &[0x22, 0x5f, 0x6f, 0x62, 0x89, 0xdc], + ), + package: get_default_keyboard_pkg(), + } } /// Returns a default keyboard layout package list as a byte vector. @@ -621,106 +621,107 @@ pub fn get_default_keyboard_pkg_list() -> HiiKeyboardPkgList { /// ); /// ``` pub fn get_default_keyboard_pkg_list_buffer() -> Vec { - let mut buffer = vec![0u8; 4096]; - - let result = buffer.pwrite(&get_default_keyboard_pkg_list(), 0); - if let Ok(buffer_size) = result { - buffer.resize(buffer_size, 0); - buffer - } else { - panic!("Unexpected error serializing HII Keyboard Package List: {:?}", result); - } + let mut buffer = vec![0u8; 4096]; + + let result = buffer.pwrite(&get_default_keyboard_pkg_list(), 0); + if let Ok(buffer_size) = result { + buffer.resize(buffer_size, 0); + buffer + } else { + panic!("Unexpected error serializing HII Keyboard Package List: {:?}", result); + } } /// Defines errors that can occur while parsing keyboard layout. pub enum LayoutError { - /// Malformed key buffer - ParseError(scroll::Error), + /// Malformed key buffer + ParseError(scroll::Error), } /// Returns a HiiKeyboardLayout structure parsed from the given buffer. pub fn keyboard_layout_from_buffer(buffer: &[u8]) -> Result { - buffer.pread::(0).map_err(LayoutError::ParseError) + buffer.pread::(0).map_err(LayoutError::ParseError) } #[cfg(test)] mod tests { - extern crate std; - use core::mem; - use std::{fs::File, io::Read}; + extern crate std; + use core::mem; + use std::{fs::File, io::Read}; + + use alloc::{vec, vec::Vec}; + use r_efi::{ + efi, + protocols::hii_database::{ + AFFECTED_BY_CAPS_LOCK, AFFECTED_BY_STANDARD_SHIFT, NS_KEY_DEPENDENCY_MODIFIER, NS_KEY_MODIFIER, + }, + }; + use scroll::{Pread, Pwrite}; - use alloc::{vec, vec::Vec}; - use r_efi::{ - efi, - protocols::hii_database::{ - AFFECTED_BY_CAPS_LOCK, AFFECTED_BY_STANDARD_SHIFT, NS_KEY_DEPENDENCY_MODIFIER, NS_KEY_MODIFIER, - }, - }; - use scroll::{Pread, Pwrite}; - - use crate::{ - get_default_keyboard_pkg, EfiKey, HiiKey, HiiKeyDescriptor, HiiKeyboardPkg, HiiKeyboardPkgList, HiiNsKeyDescriptor, - }; - - macro_rules! test_collateral { - ($fname:expr) => { - concat!(env!("CARGO_MANIFEST_DIR"), "/resources/test/", $fname) + use crate::{ + get_default_keyboard_pkg, EfiKey, HiiKey, HiiKeyDescriptor, HiiKeyboardPkg, HiiKeyboardPkgList, + HiiNsKeyDescriptor, }; - } - #[test] - fn hii_keyboard_package_serialize_deserialize_should_produce_consistent_results() { - let mut buffer = [0u8; 4096]; + macro_rules! test_collateral { + ($fname:expr) => { + concat!(env!("CARGO_MANIFEST_DIR"), "/resources/test/", $fname) + }; + } - let package = get_default_keyboard_pkg(); - buffer.pwrite(&package, 0).unwrap(); + #[test] + fn hii_keyboard_package_serialize_deserialize_should_produce_consistent_results() { + let mut buffer = [0u8; 4096]; - let package2: HiiKeyboardPkg = buffer.pread(0).unwrap(); - assert_eq!(package, package2); - } + let package = get_default_keyboard_pkg(); + buffer.pwrite(&package, 0).unwrap(); + + let package2: HiiKeyboardPkg = buffer.pread(0).unwrap(); + assert_eq!(package, package2); + } - #[test] - fn default_layout_should_match_reference_binary() { - let mut test_file = File::open(test_collateral!("KeyboardLayout.bin")).expect("failed to open test file."); - let mut file_buffer = Vec::new(); + #[test] + fn default_layout_should_match_reference_binary() { + let mut test_file = File::open(test_collateral!("KeyboardLayout.bin")).expect("failed to open test file."); + let mut file_buffer = Vec::new(); - test_file.read_to_end(&mut file_buffer).expect("failed to read test file"); + test_file.read_to_end(&mut file_buffer).expect("failed to read test file"); - let file_pkg: HiiKeyboardPkg = file_buffer.pread(0).unwrap(); - let test_pkg = get_default_keyboard_pkg(); - assert_eq!(file_pkg, test_pkg); + let file_pkg: HiiKeyboardPkg = file_buffer.pread(0).unwrap(); + let test_pkg = get_default_keyboard_pkg(); + assert_eq!(file_pkg, test_pkg); - let mut test_buffer = [0u8; 4096]; - let pkg_buffer_size = test_buffer.pwrite(&test_pkg, 0).unwrap(); + let mut test_buffer = [0u8; 4096]; + let pkg_buffer_size = test_buffer.pwrite(&test_pkg, 0).unwrap(); - assert_eq!(file_buffer, test_buffer[0..pkg_buffer_size]); - } - #[test] - fn ns_key_test_should_match_reference_ns_key_binary() { - let mut test_file = File::open(test_collateral!("KeyboardLayoutNs.bin")).expect("failed to open test file."); - let mut file_buffer = Vec::new(); + assert_eq!(file_buffer, test_buffer[0..pkg_buffer_size]); + } + #[test] + fn ns_key_test_should_match_reference_ns_key_binary() { + let mut test_file = File::open(test_collateral!("KeyboardLayoutNs.bin")).expect("failed to open test file."); + let mut file_buffer = Vec::new(); - test_file.read_to_end(&mut file_buffer).expect("failed to read test file"); + test_file.read_to_end(&mut file_buffer).expect("failed to read test file"); - let ns_file_pkg: HiiKeyboardPkg = file_buffer.pread(0).unwrap(); + let ns_file_pkg: HiiKeyboardPkg = file_buffer.pread(0).unwrap(); - let mut test_buffer = [0u8; 4096]; - let pkg_buffer_size = test_buffer.pwrite(&ns_file_pkg, 0).unwrap(); + let mut test_buffer = [0u8; 4096]; + let pkg_buffer_size = test_buffer.pwrite(&ns_file_pkg, 0).unwrap(); - assert_eq!(file_buffer, test_buffer[0..pkg_buffer_size]); + assert_eq!(file_buffer, test_buffer[0..pkg_buffer_size]); - //Start with the default key layout and reconstruct it to match the reference ns key layout. - let mut test_pkg = get_default_keyboard_pkg(); + //Start with the default key layout and reconstruct it to match the reference ns key layout. + let mut test_pkg = get_default_keyboard_pkg(); - let keys = &mut test_pkg.layouts[0].keys; + let keys = &mut test_pkg.layouts[0].keys; - let (index, _) = keys - .iter() - .enumerate() - .find(|(_, element)| if let HiiKey::Key(key) = element { key.key == EfiKey::E0 } else { false }) - .unwrap(); + let (index, _) = keys + .iter() + .enumerate() + .find(|(_, element)| if let HiiKey::Key(key) = element { key.key == EfiKey::E0 } else { false }) + .unwrap(); - #[rustfmt::skip] + #[rustfmt::skip] let ns_key = HiiKey::NsKey(HiiNsKeyDescriptor { descriptor: key_descriptor!(EfiKey::E0, '\0', '\0', '\0', '\0', NS_KEY_MODIFIER, 0), @@ -732,48 +733,48 @@ mod tests { key_descriptor!(EfiKey::D7, '\u{00FB}', '\u{00CB}', '\0', '\0', NS_KEY_DEPENDENCY_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK) ]}); - keys[index] = ns_key; + keys[index] = ns_key; - assert_eq!(ns_file_pkg, test_pkg); + assert_eq!(ns_file_pkg, test_pkg); - let mut test_buffer = [0u8; 4096]; - let pkg_buffer_size = test_buffer.pwrite(&test_pkg, 0).unwrap(); + let mut test_buffer = [0u8; 4096]; + let pkg_buffer_size = test_buffer.pwrite(&test_pkg, 0).unwrap(); - assert_eq!(file_buffer, test_buffer[0..pkg_buffer_size]); - } + assert_eq!(file_buffer, test_buffer[0..pkg_buffer_size]); + } - #[test] - fn package_list_serialization_should_generate_package_list() { - let mut test_file = File::open(test_collateral!("KeyboardLayout.bin")).expect("failed to open test file."); - let mut file_buffer = Vec::new(); - - test_file.read_to_end(&mut file_buffer).expect("failed to read test file"); - - let file_pkg: HiiKeyboardPkg = file_buffer.pread(0).unwrap(); - - let pkg_list = HiiKeyboardPkgList { - package_list_guid: efi::Guid::from_fields( - 0xc0f3b43, - 0x44de, - 0x4907, - 0xb4, - 0x78, - &[0x22, 0x5f, 0x6f, 0x62, 0x89, 0xdc], - ), - package: file_pkg, - }; + #[test] + fn package_list_serialization_should_generate_package_list() { + let mut test_file = File::open(test_collateral!("KeyboardLayout.bin")).expect("failed to open test file."); + let mut file_buffer = Vec::new(); - let mut test_buffer = [0u8; 4096]; - let pkg_list_buffer_size = test_buffer.pwrite(&pkg_list, 0).unwrap(); - let guid_size = mem::size_of::(); + test_file.read_to_end(&mut file_buffer).expect("failed to read test file"); - assert_eq!(&test_buffer[0..guid_size], pkg_list.package_list_guid.as_bytes()); - assert_eq!(&test_buffer[guid_size..guid_size + 4], (pkg_list_buffer_size as u32).to_le_bytes()); - assert_eq!(&test_buffer[guid_size + 4..guid_size + 4 + file_buffer.len()], file_buffer); - assert_eq!(&test_buffer[guid_size + 4 + file_buffer.len()..pkg_list_buffer_size], 0xDF000004u32.to_le_bytes()); + let file_pkg: HiiKeyboardPkg = file_buffer.pread(0).unwrap(); - let deserialized_pkg_list: HiiKeyboardPkgList = test_buffer.pread(0).unwrap(); + let pkg_list = HiiKeyboardPkgList { + package_list_guid: efi::Guid::from_fields( + 0xc0f3b43, + 0x44de, + 0x4907, + 0xb4, + 0x78, + &[0x22, 0x5f, 0x6f, 0x62, 0x89, 0xdc], + ), + package: file_pkg, + }; - assert_eq!(pkg_list, deserialized_pkg_list); - } + let mut test_buffer = [0u8; 4096]; + let pkg_list_buffer_size = test_buffer.pwrite(&pkg_list, 0).unwrap(); + let guid_size = mem::size_of::(); + + assert_eq!(&test_buffer[0..guid_size], pkg_list.package_list_guid.as_bytes()); + assert_eq!(&test_buffer[guid_size..guid_size + 4], (pkg_list_buffer_size as u32).to_le_bytes()); + assert_eq!(&test_buffer[guid_size + 4..guid_size + 4 + file_buffer.len()], file_buffer); + assert_eq!(&test_buffer[guid_size + 4 + file_buffer.len()..pkg_list_buffer_size], 0xDF000004u32.to_le_bytes()); + + let deserialized_pkg_list: HiiKeyboardPkgList = test_buffer.pread(0).unwrap(); + + assert_eq!(pkg_list, deserialized_pkg_list); + } } diff --git a/HidPkg/UefiHidDxe/src/driver_binding.rs b/HidPkg/UefiHidDxe/src/driver_binding.rs index 98edcaf66b..57cf3f31b1 100644 --- a/HidPkg/UefiHidDxe/src/driver_binding.rs +++ b/HidPkg/UefiHidDxe/src/driver_binding.rs @@ -12,8 +12,8 @@ use core::ffi::c_void; use alloc::boxed::Box; use r_efi::{ - efi, - protocols::{device_path, driver_binding}, + efi, + protocols::{device_path, driver_binding}, }; use rust_advanced_logger_dxe::{debugln, DEBUG_ERROR, DEBUG_INFO}; @@ -23,128 +23,130 @@ use crate::{hid, BOOT_SERVICES}; /// /// Installs this drivers driver binding on the given image handle. pub fn initialize_driver_binding(image_handle: efi::Handle) -> Result<(), efi::Status> { - let boot_services = unsafe { BOOT_SERVICES.as_mut().ok_or(efi::Status::NOT_READY)? }; - - let mut driver_binding_handle = image_handle; - let driver_binding_ptr = Box::into_raw(Box::new(driver_binding::Protocol { - supported: uefi_hid_driver_binding_supported, - start: uefi_hid_driver_binding_start, - stop: uefi_hid_driver_binding_stop, - version: 1, - image_handle: driver_binding_handle, - driver_binding_handle, - })); - - let status = (boot_services.install_protocol_interface)( - core::ptr::addr_of_mut!(driver_binding_handle), - &driver_binding::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - efi::NATIVE_INTERFACE, - driver_binding_ptr as *mut c_void, - ); - - if status.is_error() { - drop(unsafe { Box::from_raw(driver_binding_ptr) }); - return Err(status); - } - - Ok(()) + let boot_services = unsafe { BOOT_SERVICES.as_mut().ok_or(efi::Status::NOT_READY)? }; + + let mut driver_binding_handle = image_handle; + let driver_binding_ptr = Box::into_raw(Box::new(driver_binding::Protocol { + supported: uefi_hid_driver_binding_supported, + start: uefi_hid_driver_binding_start, + stop: uefi_hid_driver_binding_stop, + version: 1, + image_handle: driver_binding_handle, + driver_binding_handle, + })); + + let status = (boot_services.install_protocol_interface)( + core::ptr::addr_of_mut!(driver_binding_handle), + &driver_binding::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + efi::NATIVE_INTERFACE, + driver_binding_ptr as *mut c_void, + ); + + if status.is_error() { + drop(unsafe { Box::from_raw(driver_binding_ptr) }); + return Err(status); + } + + Ok(()) } // Checks whether the given controller supports HidIo. Implements driver_binding::supported. extern "efiapi" fn uefi_hid_driver_binding_supported( - this: *mut driver_binding::Protocol, - controller: efi::Handle, - _remaining_device_path: *mut device_path::Protocol, + this: *mut driver_binding::Protocol, + controller: efi::Handle, + _remaining_device_path: *mut device_path::Protocol, ) -> efi::Status { - // retrieve a reference to boot services. - // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. - let boot_services = unsafe { - match BOOT_SERVICES.as_mut() { - Some(boot_services) => boot_services, - None => return efi::Status::NOT_READY, + // retrieve a reference to boot services. + // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + let boot_services = unsafe { + match BOOT_SERVICES.as_mut() { + Some(boot_services) => boot_services, + None => return efi::Status::NOT_READY, + } + }; + + // retrieve a reference to the driver binding. + // Safety: the driver binding pointer passed here must point to the binding installed in initialize_driver_binding. + let driver_binding = unsafe { + match this.as_mut() { + Some(driver_binding) => driver_binding, + None => return efi::Status::INVALID_PARAMETER, + } + }; + + // Check to see if this controller is supported by attempting to open HidIo on it. + let mut hid_io_ptr: *mut hid_io::protocol::Protocol = core::ptr::null_mut(); + let status = (boot_services.open_protocol)( + controller, + &hid_io::protocol::GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::addr_of_mut!(hid_io_ptr) as *mut *mut c_void, + driver_binding.driver_binding_handle, + controller, + efi::OPEN_PROTOCOL_BY_DRIVER, + ); + + // if HidIo could not be opened then it is either in use or not present. + if status.is_error() { + return status; } - }; - - // retrieve a reference to the driver binding. - // Safety: the driver binding pointer passed here must point to the binding installed in initialize_driver_binding. - let driver_binding = unsafe { - match this.as_mut() { - Some(driver_binding) => driver_binding, - None => return efi::Status::INVALID_PARAMETER, + + // HidIo is available, so this controller is supported. Further checking that requires actual device interaction is + // done in uefi_hid_driver_binding_start. close the protocol used for the supported test and exit with success. + let status = (boot_services.close_protocol)( + controller, + &hid_io::protocol::GUID as *const efi::Guid as *mut efi::Guid, + driver_binding.driver_binding_handle, + controller, + ); + if status.is_error() { + debugln!(DEBUG_ERROR, "Unexpected error from CloseProtocol: {:?}", status); + //message, but no further action to handle. } - }; - - // Check to see if this controller is supported by attempting to open HidIo on it. - let mut hid_io_ptr: *mut hid_io::protocol::Protocol = core::ptr::null_mut(); - let status = (boot_services.open_protocol)( - controller, - &hid_io::protocol::GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::addr_of_mut!(hid_io_ptr) as *mut *mut c_void, - driver_binding.driver_binding_handle, - controller, - efi::OPEN_PROTOCOL_BY_DRIVER, - ); - - // if HidIo could not be opened then it is either in use or not present. - if status.is_error() { - return status; - } - - // HidIo is available, so this controller is supported. Further checking that requires actual device interaction is - // done in uefi_hid_driver_binding_start. close the protocol used for the supported test and exit with success. - let status = (boot_services.close_protocol)( - controller, - &hid_io::protocol::GUID as *const efi::Guid as *mut efi::Guid, - driver_binding.driver_binding_handle, - controller, - ); - if status.is_error() { - debugln!(DEBUG_ERROR, "Unexpected error from CloseProtocol: {:?}", status); //message, but no further action to handle. - } - - efi::Status::SUCCESS + + efi::Status::SUCCESS } // Start this driver managing given handle. Implements driver_binding::start. extern "efiapi" fn uefi_hid_driver_binding_start( - this: *mut driver_binding::Protocol, - controller: efi::Handle, - _remaining_device_path: *mut device_path::Protocol, + this: *mut driver_binding::Protocol, + controller: efi::Handle, + _remaining_device_path: *mut device_path::Protocol, ) -> efi::Status { - // retrieve a reference to the driver binding. - // Safety: the driver binding pointer passed here must point to the binding installed in initialize_driver_binding. - let driver_binding = unsafe { - match this.as_mut() { - Some(driver_binding) => driver_binding, - None => return efi::Status::INVALID_PARAMETER, + // retrieve a reference to the driver binding. + // Safety: the driver binding pointer passed here must point to the binding installed in initialize_driver_binding. + let driver_binding = unsafe { + match this.as_mut() { + Some(driver_binding) => driver_binding, + None => return efi::Status::INVALID_PARAMETER, + } + }; + + // initialize the hid stack + let status = hid::initialize(controller, driver_binding); + if let Err(status) = status { + debugln!(DEBUG_INFO, "[hid::driver_binding_start] failed to initialize hid: {:x?}", status); + return status; } - }; - // initialize the hid stack - let status = hid::initialize(controller, driver_binding); - if let Err(status) = status { - debugln!(DEBUG_INFO, "[hid::driver_binding_start] failed to initialize hid: {:x?}", status); - return status; - } - - efi::Status::SUCCESS + efi::Status::SUCCESS } // Stops this driver from managing the given handle. Implements driver_binding::stop. extern "efiapi" fn uefi_hid_driver_binding_stop( - this: *mut driver_binding::Protocol, - controller: efi::Handle, - _num_children: usize, - _child_handle_buffer: *mut efi::Handle, + this: *mut driver_binding::Protocol, + controller: efi::Handle, + _num_children: usize, + _child_handle_buffer: *mut efi::Handle, ) -> efi::Status { - let driver_binding = unsafe { this.as_mut().expect("driver binding pointer is bad in uefi_hid_driver_binding_stop") }; - - //destroy the hid stack. - let status = hid::destroy(controller, driver_binding); - if let Err(status) = status { - debugln!(DEBUG_INFO, "[hid::driver_binding_stop] failed to destroy hid: {:x?}", status); - return status; - } + let driver_binding = + unsafe { this.as_mut().expect("driver binding pointer is bad in uefi_hid_driver_binding_stop") }; + + //destroy the hid stack. + let status = hid::destroy(controller, driver_binding); + if let Err(status) = status { + debugln!(DEBUG_INFO, "[hid::driver_binding_stop] failed to destroy hid: {:x?}", status); + return status; + } - efi::Status::SUCCESS + efi::Status::SUCCESS } diff --git a/HidPkg/UefiHidDxe/src/hid.rs b/HidPkg/UefiHidDxe/src/hid.rs index f2703595b6..21e8d46fda 100644 --- a/HidPkg/UefiHidDxe/src/hid.rs +++ b/HidPkg/UefiHidDxe/src/hid.rs @@ -17,204 +17,212 @@ use rust_advanced_logger_dxe::{debugln, DEBUG_ERROR, DEBUG_WARN}; use crate::{keyboard, keyboard::KeyboardContext, pointer, pointer::PointerContext, BOOT_SERVICES}; pub struct HidContext { - pub hid_io: *mut hid_io::protocol::Protocol, - pub keyboard_context: *mut KeyboardContext, - pub pointer_context: *mut PointerContext, + pub hid_io: *mut hid_io::protocol::Protocol, + pub keyboard_context: *mut KeyboardContext, + pub pointer_context: *mut PointerContext, } /// Initialize HID support /// /// Reads the HID Report Descriptor from the device, parse it, and initialize keyboard and pointer handlers for it. pub fn initialize(controller: efi::Handle, driver_binding: &driver_binding::Protocol) -> Result<(), efi::Status> { - // retrieve a reference to boot services. - // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. - // Caller should have ensured this, so just expect on failure. - let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; - - // retrieve the HidIo instance for the given controller. - let mut hid_io_ptr: *mut hid_io::protocol::Protocol = core::ptr::null_mut(); - let status = (boot_services.open_protocol)( - controller, - &hid_io::protocol::GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::addr_of_mut!(hid_io_ptr) as *mut *mut c_void, - driver_binding.driver_binding_handle, - controller, - system::OPEN_PROTOCOL_BY_DRIVER, - ); - if status.is_error() { - debugln!(DEBUG_ERROR, "[hid::initialize] Unexpected error opening HidIo protocol: {:#?}", status); - return Err(status); - } - - let hid_io = unsafe { hid_io_ptr.as_mut().expect("bad hidio pointer.") }; - - //determine report descriptor size. - let mut report_descriptor_size: usize = 0; - let status = - (hid_io.get_report_descriptor)(hid_io_ptr, core::ptr::addr_of_mut!(report_descriptor_size), core::ptr::null_mut()); - - match status { - efi::Status::BUFFER_TOO_SMALL => (), - _ => { - let _ = release_hid_io(controller, driver_binding); - return Err(efi::Status::DEVICE_ERROR); + // retrieve a reference to boot services. + // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + // Caller should have ensured this, so just expect on failure. + let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; + + // retrieve the HidIo instance for the given controller. + let mut hid_io_ptr: *mut hid_io::protocol::Protocol = core::ptr::null_mut(); + let status = (boot_services.open_protocol)( + controller, + &hid_io::protocol::GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::addr_of_mut!(hid_io_ptr) as *mut *mut c_void, + driver_binding.driver_binding_handle, + controller, + system::OPEN_PROTOCOL_BY_DRIVER, + ); + if status.is_error() { + debugln!(DEBUG_ERROR, "[hid::initialize] Unexpected error opening HidIo protocol: {:#?}", status); + return Err(status); } - } - //read report descriptor. - let mut report_descriptor_buffer = vec![0u8; report_descriptor_size]; - let report_descriptor_buffer_ptr = report_descriptor_buffer.as_mut_ptr(); + let hid_io = unsafe { hid_io_ptr.as_mut().expect("bad hidio pointer.") }; + + //determine report descriptor size. + let mut report_descriptor_size: usize = 0; + let status = (hid_io.get_report_descriptor)( + hid_io_ptr, + core::ptr::addr_of_mut!(report_descriptor_size), + core::ptr::null_mut(), + ); + + match status { + efi::Status::BUFFER_TOO_SMALL => (), + _ => { + let _ = release_hid_io(controller, driver_binding); + return Err(efi::Status::DEVICE_ERROR); + } + } - let status = (hid_io.get_report_descriptor)( - hid_io_ptr, - core::ptr::addr_of_mut!(report_descriptor_size), - report_descriptor_buffer_ptr as *mut c_void, - ); + //read report descriptor. + let mut report_descriptor_buffer = vec![0u8; report_descriptor_size]; + let report_descriptor_buffer_ptr = report_descriptor_buffer.as_mut_ptr(); - if status.is_error() { - let _ = release_hid_io(controller, driver_binding); - return Err(status); - } + let status = (hid_io.get_report_descriptor)( + hid_io_ptr, + core::ptr::addr_of_mut!(report_descriptor_size), + report_descriptor_buffer_ptr as *mut c_void, + ); - // parse the descriptor - let descriptor = hidparser::parse_report_descriptor(&report_descriptor_buffer).map_err(|err| { - debugln!(DEBUG_WARN, "[hid::initialize] failed to parse report descriptor: {:x?}.", err); - let _ = release_hid_io(controller, driver_binding); - efi::Status::DEVICE_ERROR - })?; - - // create hid context - let hid_context_ptr = Box::into_raw(Box::new(HidContext { - hid_io: hid_io_ptr, - keyboard_context: core::ptr::null_mut(), - pointer_context: core::ptr::null_mut(), - })); - - //initialize report handlers - let keyboard = keyboard::initialize(controller, &descriptor, hid_context_ptr); - let pointer = pointer::initialize(controller, &descriptor, hid_context_ptr); - - if keyboard.is_err() && pointer.is_err() { - debugln!(DEBUG_WARN, "[hid::initialize] no devices supported"); - //no devices supported. - let _ = release_hid_io(controller, driver_binding); - unsafe { drop(Box::from_raw(hid_context_ptr)) }; - Err(efi::Status::UNSUPPORTED)?; - } + if status.is_error() { + let _ = release_hid_io(controller, driver_binding); + return Err(status); + } + + // parse the descriptor + let descriptor = hidparser::parse_report_descriptor(&report_descriptor_buffer).map_err(|err| { + debugln!(DEBUG_WARN, "[hid::initialize] failed to parse report descriptor: {:x?}.", err); + let _ = release_hid_io(controller, driver_binding); + efi::Status::DEVICE_ERROR + })?; + + // create hid context + let hid_context_ptr = Box::into_raw(Box::new(HidContext { + hid_io: hid_io_ptr, + keyboard_context: core::ptr::null_mut(), + pointer_context: core::ptr::null_mut(), + })); + + //initialize report handlers + let keyboard = keyboard::initialize(controller, &descriptor, hid_context_ptr); + let pointer = pointer::initialize(controller, &descriptor, hid_context_ptr); + + if keyboard.is_err() && pointer.is_err() { + debugln!(DEBUG_WARN, "[hid::initialize] no devices supported"); + //no devices supported. + let _ = release_hid_io(controller, driver_binding); + unsafe { drop(Box::from_raw(hid_context_ptr)) }; + Err(efi::Status::UNSUPPORTED)?; + } - // register for input reports - let status = (hid_io.register_report_callback)(hid_io_ptr, on_input_report, hid_context_ptr as *mut c_void); + // register for input reports + let status = (hid_io.register_report_callback)(hid_io_ptr, on_input_report, hid_context_ptr as *mut c_void); - if status.is_error() { - debugln!(DEBUG_WARN, "[hid::initialize] failed to register for input reports: {:x?}", status); - let _ = destroy(controller, driver_binding); - return Err(status); - } + if status.is_error() { + debugln!(DEBUG_WARN, "[hid::initialize] failed to register for input reports: {:x?}", status); + let _ = destroy(controller, driver_binding); + return Err(status); + } - Ok(()) + Ok(()) } // Handler function for input reports. Dispatches them to the keyboard and pointer modules for handling. extern "efiapi" fn on_input_report(report_buffer_size: u16, report_buffer: *mut c_void, context: *mut c_void) { - let hid_context_ptr = context as *mut HidContext; - let hid_context = unsafe { hid_context_ptr.as_mut().expect("[hid::on_input_report: invalid context pointer") }; + let hid_context_ptr = context as *mut HidContext; + let hid_context = unsafe { hid_context_ptr.as_mut().expect("[hid::on_input_report: invalid context pointer") }; - let report = unsafe { from_raw_parts(report_buffer as *mut u8, report_buffer_size as usize) }; + let report = unsafe { from_raw_parts(report_buffer as *mut u8, report_buffer_size as usize) }; - let keyboard_context = unsafe { hid_context.keyboard_context.as_mut() }; - if let Some(keyboard_context) = keyboard_context { - keyboard_context.handler.process_input_report(report); - } + let keyboard_context = unsafe { hid_context.keyboard_context.as_mut() }; + if let Some(keyboard_context) = keyboard_context { + keyboard_context.handler.process_input_report(report); + } - let pointer_context = unsafe { hid_context.pointer_context.as_mut() }; - if let Some(pointer_context) = pointer_context { - pointer_context.handler.process_input_report(report); - } + let pointer_context = unsafe { hid_context.pointer_context.as_mut() }; + if let Some(pointer_context) = pointer_context { + pointer_context.handler.process_input_report(report); + } } // Unregister report callback from HID layer to shutdown input reports. fn shutdown_input_reports(hid_context: &mut HidContext) -> Result<(), efi::Status> { - let hid_io = - unsafe { hid_context.hid_io.as_mut().expect("hid_context has bad hid_io pointer in hid::shutdown_input_reports") }; - // shutdown input reports. - let status = (hid_io.unregister_report_callback)(hid_context.hid_io, on_input_report); - if status.is_error() { - debugln!(DEBUG_ERROR, "[hid::destroy] unexpected error from hid_io.unregister_report_callback: {:?}", status); - return Err(status); - } - Ok(()) + let hid_io = unsafe { + hid_context.hid_io.as_mut().expect("hid_context has bad hid_io pointer in hid::shutdown_input_reports") + }; + // shutdown input reports. + let status = (hid_io.unregister_report_callback)(hid_context.hid_io, on_input_report); + if status.is_error() { + debugln!(DEBUG_ERROR, "[hid::destroy] unexpected error from hid_io.unregister_report_callback: {:?}", status); + return Err(status); + } + Ok(()) } //Shutdown Keyboard and Pointer handling. fn shutdown_handlers(hid_context: &mut HidContext) -> Result<(), efi::Status> { - // shutdown keyboard. - let mut status = efi::Status::SUCCESS; - match keyboard::deinitialize(hid_context.keyboard_context) { - Err(err) if err != efi::Status::UNSUPPORTED => { - debugln!(DEBUG_ERROR, "[hid::destroy] unexpected error from keyboard::deinitialize: {:?}", err); - status = efi::Status::DEVICE_ERROR; - } - _ => (), - }; - - // shutdown pointer. - match pointer::deinitialize(hid_context.pointer_context) { - Err(err) if err != efi::Status::UNSUPPORTED => { - debugln!(DEBUG_ERROR, "[hid::destroy] unexpected error from pointer::deinitialize: {:?}", err); - status = efi::Status::DEVICE_ERROR; + // shutdown keyboard. + let mut status = efi::Status::SUCCESS; + match keyboard::deinitialize(hid_context.keyboard_context) { + Err(err) if err != efi::Status::UNSUPPORTED => { + debugln!(DEBUG_ERROR, "[hid::destroy] unexpected error from keyboard::deinitialize: {:?}", err); + status = efi::Status::DEVICE_ERROR; + } + _ => (), + }; + + // shutdown pointer. + match pointer::deinitialize(hid_context.pointer_context) { + Err(err) if err != efi::Status::UNSUPPORTED => { + debugln!(DEBUG_ERROR, "[hid::destroy] unexpected error from pointer::deinitialize: {:?}", err); + status = efi::Status::DEVICE_ERROR; + } + _ => (), + }; + + if status.is_error() { + return Err(status); } - _ => (), - }; - if status.is_error() { - return Err(status); - } - - Ok(()) + Ok(()) } // Release HidIo instance by closing the HidIo protocol on the given controller. fn release_hid_io(controller: efi::Handle, driver_binding: &driver_binding::Protocol) -> Result<(), efi::Status> { - // retrieve a reference to boot services. - // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. - // Caller should have ensured this, so just expect on failure. - let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; - - // release HidIo - match (boot_services.close_protocol)( - controller, - &hid_io::protocol::GUID as *const efi::Guid as *mut efi::Guid, - driver_binding.driver_binding_handle, - controller, - ) { - efi::Status::SUCCESS => (), - err => { - debugln!(DEBUG_ERROR, "[hid::release_hid_io] unexpected error from boot_services.close_protocol: {:?}", err); - return Err(efi::Status::DEVICE_ERROR); + // retrieve a reference to boot services. + // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + // Caller should have ensured this, so just expect on failure. + let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; + + // release HidIo + match (boot_services.close_protocol)( + controller, + &hid_io::protocol::GUID as *const efi::Guid as *mut efi::Guid, + driver_binding.driver_binding_handle, + controller, + ) { + efi::Status::SUCCESS => (), + err => { + debugln!( + DEBUG_ERROR, + "[hid::release_hid_io] unexpected error from boot_services.close_protocol: {:?}", + err + ); + return Err(efi::Status::DEVICE_ERROR); + } } - } - Ok(()) + Ok(()) } /// Tears down HID support. /// /// De-initializes keyboard and pointer handlers and releases HidIo instance. pub fn destroy(controller: efi::Handle, driver_binding: &driver_binding::Protocol) -> Result<(), efi::Status> { - let mut context = pointer::attempt_to_retrieve_hid_context(controller, driver_binding); - if context.is_err() { - context = keyboard::attempt_to_retrieve_hid_context(controller, driver_binding); - } + let mut context = pointer::attempt_to_retrieve_hid_context(controller, driver_binding); + if context.is_err() { + context = keyboard::attempt_to_retrieve_hid_context(controller, driver_binding); + } - let hid_context_ptr = context?; - let hid_context = unsafe { hid_context_ptr.as_mut().expect("invalid hid_context_ptr in hid::destroy") }; + let hid_context_ptr = context?; + let hid_context = unsafe { hid_context_ptr.as_mut().expect("invalid hid_context_ptr in hid::destroy") }; - let _ = shutdown_input_reports(hid_context); - let _ = shutdown_handlers(hid_context); - let _ = release_hid_io(controller, driver_binding); + let _ = shutdown_input_reports(hid_context); + let _ = shutdown_handlers(hid_context); + let _ = release_hid_io(controller, driver_binding); - // take back the hid_context (it will be released when it goes out of scope). - unsafe { drop(Box::from_raw(hid_context_ptr)) }; + // take back the hid_context (it will be released when it goes out of scope). + unsafe { drop(Box::from_raw(hid_context_ptr)) }; - Ok(()) + Ok(()) } diff --git a/HidPkg/UefiHidDxe/src/key_queue.rs b/HidPkg/UefiHidDxe/src/key_queue.rs index 26e2da0485..bf8e8dbfa4 100644 --- a/HidPkg/UefiHidDxe/src/key_queue.rs +++ b/HidPkg/UefiHidDxe/src/key_queue.rs @@ -10,14 +10,14 @@ //! use alloc::{ - collections::{BTreeSet, VecDeque}, - vec::Vec, + collections::{BTreeSet, VecDeque}, + vec::Vec, }; use hidparser::report_data_types::Usage; use hii_keyboard_layout::{EfiKey, HiiKey, HiiKeyDescriptor, HiiKeyboardLayout, HiiNsKeyDescriptor}; use r_efi::{ - efi, - protocols::{self, hii_database::*, simple_text_input::InputKey, simple_text_input_ex::*}, + efi, + protocols::{self, hii_database::*, simple_text_input::InputKey, simple_text_input_ex::*}, }; use rust_advanced_logger_dxe::{debugln, DEBUG_WARN}; @@ -41,10 +41,10 @@ const ALT_MODIFIERS: &[u16] = &[LEFT_ALT_MODIFIER, RIGHT_ALT_MODIFIER]; // Defines whether a key stroke represents a key being pressed (KeyDown) or released (KeyUp) #[derive(Debug, PartialEq, Eq)] pub(crate) enum KeyAction { - // Key is being pressed - KeyUp, - // Key is being released - KeyDown, + // Key is being pressed + KeyUp, + // Key is being released + KeyDown, } // A wrapper for the KeyData type that allows definition of the Ord trait and aditional registration matching logic. @@ -52,490 +52,490 @@ pub(crate) enum KeyAction { pub(crate) struct OrdKeyData(pub protocols::simple_text_input_ex::KeyData); impl Ord for OrdKeyData { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - let e = self.0.key.unicode_char.cmp(&other.0.key.unicode_char); - if !e.is_eq() { - return e; - } - let e = self.0.key.scan_code.cmp(&other.0.key.scan_code); - if !e.is_eq() { - return e; - } - let e = self.0.key_state.key_shift_state.cmp(&other.0.key_state.key_shift_state); - if !e.is_eq() { - return e; + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + let e = self.0.key.unicode_char.cmp(&other.0.key.unicode_char); + if !e.is_eq() { + return e; + } + let e = self.0.key.scan_code.cmp(&other.0.key.scan_code); + if !e.is_eq() { + return e; + } + let e = self.0.key_state.key_shift_state.cmp(&other.0.key_state.key_shift_state); + if !e.is_eq() { + return e; + } + return self.0.key_state.key_toggle_state.cmp(&other.0.key_state.key_toggle_state); } - return self.0.key_state.key_toggle_state.cmp(&other.0.key_state.key_toggle_state); - } } impl PartialOrd for OrdKeyData { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } } impl PartialEq for OrdKeyData { - fn eq(&self, other: &Self) -> bool { - self.cmp(other).is_eq() - } + fn eq(&self, other: &Self) -> bool { + self.cmp(other).is_eq() + } } impl Eq for OrdKeyData {} impl OrdKeyData { - // Returns whether this key matches the given registration. Note that this is not a straight compare - UEFI spec - // allows for some degree of wildcard matching. Refer to UEFI spec 2.10 section 12.2.5. - pub(crate) fn matches_registered_key(&self, registration: &Self) -> bool { - // assign names here for brevity below. - let self_char = self.0.key.unicode_char; - let self_scan = self.0.key.scan_code; - let self_shift = self.0.key_state.key_shift_state; - let self_toggle = self.0.key_state.key_toggle_state; - let register_char = registration.0.key.unicode_char; - let register_scan = registration.0.key.scan_code; - let register_shift = registration.0.key_state.key_shift_state; - let register_toggle = registration.0.key_state.key_toggle_state; - - //char and scan must match (per the reference implementation in the EDK2 C code). - if !(register_char == self_char && register_scan == self_scan) { - return false; - } + // Returns whether this key matches the given registration. Note that this is not a straight compare - UEFI spec + // allows for some degree of wildcard matching. Refer to UEFI spec 2.10 section 12.2.5. + pub(crate) fn matches_registered_key(&self, registration: &Self) -> bool { + // assign names here for brevity below. + let self_char = self.0.key.unicode_char; + let self_scan = self.0.key.scan_code; + let self_shift = self.0.key_state.key_shift_state; + let self_toggle = self.0.key_state.key_toggle_state; + let register_char = registration.0.key.unicode_char; + let register_scan = registration.0.key.scan_code; + let register_shift = registration.0.key_state.key_shift_state; + let register_toggle = registration.0.key_state.key_toggle_state; + + //char and scan must match (per the reference implementation in the EDK2 C code). + if !(register_char == self_char && register_scan == self_scan) { + return false; + } - //shift state must be zero or must match. - if !(register_shift == 0 || register_shift == self_shift) { - return false; - } + //shift state must be zero or must match. + if !(register_shift == 0 || register_shift == self_shift) { + return false; + } - //toggle state must be zero or must match. - if !(register_toggle == 0 || register_toggle == self_toggle) { - return false; + //toggle state must be zero or must match. + if !(register_toggle == 0 || register_toggle == self_toggle) { + return false; + } + return true; } - return true; - } } // This structure manages the queue of pending keystrokes #[derive(Debug, Default)] pub(crate) struct KeyQueue { - layout: Option, - active_modifiers: BTreeSet, - active_ns_key: Option, - partial_key_support_active: bool, - key_queue: VecDeque, - registered_keys: BTreeSet, - notified_key_queue: VecDeque, + layout: Option, + active_modifiers: BTreeSet, + active_ns_key: Option, + partial_key_support_active: bool, + key_queue: VecDeque, + registered_keys: BTreeSet, + notified_key_queue: VecDeque, } impl KeyQueue { - // resets the KeyQueue to initial state - pub(crate) fn reset(&mut self) { - self.active_modifiers.clear(); - self.active_ns_key = None; - self.partial_key_support_active = false; - self.key_queue.clear(); - } - - // Processes the given keystroke and updates the KeyQueue accordingly. - pub(crate) fn keystroke(&mut self, key: Usage, action: KeyAction) { - let Some(ref active_layout) = self.layout else { - //nothing to do if no layout. This is unexpected: layout should be initialized with default if not present. - debugln!(DEBUG_WARN, "key_queue::keystroke: Received keystroke without layout."); - return; - }; - - let Some(efi_key) = usage_to_efi_key(key) else { - //unsupported key usage, nothing to do. - return; - }; - - // Check if it is a dependent key of a currently active "non-spacing" (ns) key. - // Non-spacing key handling is described in UEFI spec 2.10 section 33.2.4.3. - let mut current_descriptor: Option = None; - if let Some(ref ns_key) = self.active_ns_key { - for descriptor in &ns_key.dependent_keys { - if descriptor.key == efi_key { - // found a dependent key for a previously active ns key. - // de-activate the ns key and process the dependent descriptor. - current_descriptor = Some(*descriptor); - self.active_ns_key = None; - break; - } - } + // resets the KeyQueue to initial state + pub(crate) fn reset(&mut self) { + self.active_modifiers.clear(); + self.active_ns_key = None; + self.partial_key_support_active = false; + self.key_queue.clear(); } - // If it is not a dependent key of a currently active ns key, then check if it is a regular or ns key. - if current_descriptor.is_none() { - for key in &active_layout.keys { - match key { - HiiKey::Key(descriptor) => { - if descriptor.key == efi_key { - current_descriptor = Some(*descriptor); - break; + // Processes the given keystroke and updates the KeyQueue accordingly. + pub(crate) fn keystroke(&mut self, key: Usage, action: KeyAction) { + let Some(ref active_layout) = self.layout else { + //nothing to do if no layout. This is unexpected: layout should be initialized with default if not present. + debugln!(DEBUG_WARN, "key_queue::keystroke: Received keystroke without layout."); + return; + }; + + let Some(efi_key) = usage_to_efi_key(key) else { + //unsupported key usage, nothing to do. + return; + }; + + // Check if it is a dependent key of a currently active "non-spacing" (ns) key. + // Non-spacing key handling is described in UEFI spec 2.10 section 33.2.4.3. + let mut current_descriptor: Option = None; + if let Some(ref ns_key) = self.active_ns_key { + for descriptor in &ns_key.dependent_keys { + if descriptor.key == efi_key { + // found a dependent key for a previously active ns key. + // de-activate the ns key and process the dependent descriptor. + current_descriptor = Some(*descriptor); + self.active_ns_key = None; + break; + } } - } - HiiKey::NsKey(ns_descriptor) => { - if ns_descriptor.descriptor.key == efi_key { - // if it is an ns_key, set it as the active ns key, and no further processing is needed. - self.active_ns_key = Some(ns_descriptor.clone()); - return; + } + + // If it is not a dependent key of a currently active ns key, then check if it is a regular or ns key. + if current_descriptor.is_none() { + for key in &active_layout.keys { + match key { + HiiKey::Key(descriptor) => { + if descriptor.key == efi_key { + current_descriptor = Some(*descriptor); + break; + } + } + HiiKey::NsKey(ns_descriptor) => { + if ns_descriptor.descriptor.key == efi_key { + // if it is an ns_key, set it as the active ns key, and no further processing is needed. + self.active_ns_key = Some(ns_descriptor.clone()); + return; + } + } + } } - } } - } - } - if current_descriptor.is_none() { - return; //could not find descriptor, nothing to do. - } + if current_descriptor.is_none() { + return; //could not find descriptor, nothing to do. + } - let current_descriptor = current_descriptor.unwrap(); + let current_descriptor = current_descriptor.unwrap(); + + //handle modifiers that are active as long as they are pressed + if KEYBOARD_MODIFIERS.contains(¤t_descriptor.modifier) { + match action { + KeyAction::KeyUp => { + self.active_modifiers.remove(¤t_descriptor.modifier); + } + KeyAction::KeyDown => { + self.active_modifiers.insert(current_descriptor.modifier); + } + } + } - //handle modifiers that are active as long as they are pressed - if KEYBOARD_MODIFIERS.contains(¤t_descriptor.modifier) { - match action { - KeyAction::KeyUp => { - self.active_modifiers.remove(¤t_descriptor.modifier); + //handle modifiers that toggle each time the key is pressed. + if TOGGLE_MODIFIERS.contains(¤t_descriptor.modifier) && action == KeyAction::KeyDown { + if self.active_modifiers.contains(¤t_descriptor.modifier) { + self.active_modifiers.remove(¤t_descriptor.modifier); + } else { + self.active_modifiers.insert(current_descriptor.modifier); + } } - KeyAction::KeyDown => { - self.active_modifiers.insert(current_descriptor.modifier); + + //handle ctrl-alt-delete + if CTRL_MODIFIERS.iter().any(|x| self.active_modifiers.contains(x)) + && ALT_MODIFIERS.iter().any(|x| self.active_modifiers.contains(x)) + && current_descriptor.modifier == DELETE_MODIFIER + { + debugln!(DEBUG_WARN, "Ctrl-Alt-Del pressed, resetting system."); + if let Some(runtime_services) = unsafe { RUNTIME_SERVICES.as_mut() } { + (runtime_services.reset_system)(efi::RESET_WARM, efi::Status::SUCCESS, 0, core::ptr::null_mut()); + } + panic!("Reset failed."); } - } - } - //handle modifiers that toggle each time the key is pressed. - if TOGGLE_MODIFIERS.contains(¤t_descriptor.modifier) && action == KeyAction::KeyDown { - if self.active_modifiers.contains(¤t_descriptor.modifier) { - self.active_modifiers.remove(¤t_descriptor.modifier); - } else { - self.active_modifiers.insert(current_descriptor.modifier); - } - } + if action == KeyAction::KeyUp { + //nothing else to do. + return; + } - //handle ctrl-alt-delete - if CTRL_MODIFIERS.iter().any(|x| self.active_modifiers.contains(x)) - && ALT_MODIFIERS.iter().any(|x| self.active_modifiers.contains(x)) - && current_descriptor.modifier == DELETE_MODIFIER - { - debugln!(DEBUG_WARN, "Ctrl-Alt-Del pressed, resetting system."); - if let Some(runtime_services) = unsafe { RUNTIME_SERVICES.as_mut() } { - (runtime_services.reset_system)(efi::RESET_WARM, efi::Status::SUCCESS, 0, core::ptr::null_mut()); - } - panic!("Reset failed."); - } + // process the keystroke to construct a KeyData item to add to the queue. + let mut key_data = protocols::simple_text_input_ex::KeyData { + key: InputKey { + unicode_char: current_descriptor.unicode, + scan_code: modifier_to_scan(current_descriptor.modifier), + }, + ..Default::default() + }; + + // retrieve relevant modifier state that may need to be applied to the key data. + let shift_active = SHIFT_MODIFIERS.iter().any(|x| self.active_modifiers.contains(x)); + let alt_gr_active = self.active_modifiers.contains(&ALT_GR_MODIFIER); + let caps_lock_active = self.active_modifiers.contains(&CAPS_LOCK_MODIFIER); + let num_lock_active = self.active_modifiers.contains(&NUM_LOCK_MODIFIER); + + // Apply the shift modifier if needed. Track whether it was applied as the shift modifier is removed from the key + // state if it was applied here. + let mut shift_applied: bool = false; + if (current_descriptor.affected_attribute & AFFECTED_BY_STANDARD_SHIFT) != 0 { + if shift_active { + //shift active + if alt_gr_active { + key_data.key.unicode_char = current_descriptor.shifted_alt_gr_unicode; + } else { + key_data.key.unicode_char = current_descriptor.shifted_unicode; + } + shift_applied = true; + } else { + //not shifted. + if alt_gr_active { + key_data.key.unicode_char = current_descriptor.alt_gr_unicode; + } + } + } - if action == KeyAction::KeyUp { - //nothing else to do. - return; - } + // if capslock is active, then invert the shift state of the key. + if (current_descriptor.affected_attribute & AFFECTED_BY_CAPS_LOCK) != 0 && caps_lock_active { + //Note: reference EDK2 implementation does not apply capslock to alt_gr. + if key_data.key.unicode_char == current_descriptor.unicode { + key_data.key.unicode_char = current_descriptor.shifted_unicode; + } else if key_data.key.unicode_char == current_descriptor.shifted_unicode { + key_data.key.unicode_char = current_descriptor.unicode; + } + } - // process the keystroke to construct a KeyData item to add to the queue. - let mut key_data = protocols::simple_text_input_ex::KeyData { - key: InputKey { - unicode_char: current_descriptor.unicode, - scan_code: modifier_to_scan(current_descriptor.modifier), - }, - ..Default::default() - }; - - // retrieve relevant modifier state that may need to be applied to the key data. - let shift_active = SHIFT_MODIFIERS.iter().any(|x| self.active_modifiers.contains(x)); - let alt_gr_active = self.active_modifiers.contains(&ALT_GR_MODIFIER); - let caps_lock_active = self.active_modifiers.contains(&CAPS_LOCK_MODIFIER); - let num_lock_active = self.active_modifiers.contains(&NUM_LOCK_MODIFIER); - - // Apply the shift modifier if needed. Track whether it was applied as the shift modifier is removed from the key - // state if it was applied here. - let mut shift_applied: bool = false; - if (current_descriptor.affected_attribute & AFFECTED_BY_STANDARD_SHIFT) != 0 { - if shift_active { - //shift active - if alt_gr_active { - key_data.key.unicode_char = current_descriptor.shifted_alt_gr_unicode; - } else { - key_data.key.unicode_char = current_descriptor.shifted_unicode; + // for the num pad, numlock (and shift state) controls whether a number key or a control key (e.g. arrow) is queued. + if (current_descriptor.affected_attribute & AFFECTED_BY_NUM_LOCK) != 0 { + if num_lock_active && !shift_active { + key_data.key.scan_code = SCAN_NULL; + } else { + key_data.key.unicode_char = 0x0000; + } + } + + //special handling for unicode 0x1B (ESC). + if key_data.key.unicode_char == 0x01B && key_data.key.scan_code == SCAN_NULL { + key_data.key.scan_code = SCAN_ESC; + key_data.key.unicode_char = 0x0000; } - shift_applied = true; - } else { - //not shifted. - if alt_gr_active { - key_data.key.unicode_char = current_descriptor.alt_gr_unicode; + + if !self.partial_key_support_active && key_data.key.unicode_char == 0 && key_data.key.scan_code == SCAN_NULL { + return; // no further processing required if there is no key or scancode and partial support is not active. } - } - } - // if capslock is active, then invert the shift state of the key. - if (current_descriptor.affected_attribute & AFFECTED_BY_CAPS_LOCK) != 0 && caps_lock_active { - //Note: reference EDK2 implementation does not apply capslock to alt_gr. - if key_data.key.unicode_char == current_descriptor.unicode { - key_data.key.unicode_char = current_descriptor.shifted_unicode; - } else if key_data.key.unicode_char == current_descriptor.shifted_unicode { - key_data.key.unicode_char = current_descriptor.unicode; - } + //initialize key state from active modifiers + key_data.key_state = self.init_key_state(); + + // if shift was applied above, then remove shift from key state. See UEFI spec 2.10 section 12.2.3. + if shift_applied { + key_data.key_state.key_shift_state &= !(LEFT_SHIFT_PRESSED | RIGHT_SHIFT_PRESSED); + } + + // if a callback has been registered matching this key, enqueue it in the callback queue. + if self.is_registered_key(key_data) { + self.notified_key_queue.push_back(key_data); + } + + // enqueue the key data. + self.key_queue.push_back(key_data); } - // for the num pad, numlock (and shift state) controls whether a number key or a control key (e.g. arrow) is queued. - if (current_descriptor.affected_attribute & AFFECTED_BY_NUM_LOCK) != 0 { - if num_lock_active && !shift_active { - key_data.key.scan_code = SCAN_NULL; - } else { - key_data.key.unicode_char = 0x0000; - } + fn is_registered_key(&self, current_key: KeyData) -> bool { + for registered_key in &self.registered_keys { + if OrdKeyData(current_key).matches_registered_key(registered_key) { + return true; + } + } + return false; } - //special handling for unicode 0x1B (ESC). - if key_data.key.unicode_char == 0x01B && key_data.key.scan_code == SCAN_NULL { - key_data.key.scan_code = SCAN_ESC; - key_data.key.unicode_char = 0x0000; + // Creates a KeyState instance initialized based on the current modifier state. + pub(crate) fn init_key_state(&self) -> KeyState { + let mut key_state: KeyState = Default::default(); + + key_state.key_shift_state = SHIFT_STATE_VALID; + key_state.key_toggle_state = TOGGLE_STATE_VALID | KEY_STATE_EXPOSED; + + let key_shift_state = &mut key_state.key_shift_state; + let key_toggle_state = &mut key_state.key_toggle_state; + + for modifier in &self.active_modifiers { + match *modifier { + LEFT_CONTROL_MODIFIER => *key_shift_state |= LEFT_CONTROL_PRESSED, + RIGHT_CONTROL_MODIFIER => *key_shift_state |= RIGHT_CONTROL_PRESSED, + LEFT_ALT_MODIFIER => *key_shift_state |= LEFT_ALT_PRESSED, + RIGHT_ALT_MODIFIER => *key_shift_state |= RIGHT_ALT_PRESSED, + LEFT_SHIFT_MODIFIER => *key_shift_state |= LEFT_SHIFT_PRESSED, + RIGHT_SHIFT_MODIFIER => *key_shift_state |= RIGHT_SHIFT_PRESSED, + LEFT_LOGO_MODIFIER => *key_shift_state |= LEFT_LOGO_PRESSED, + RIGHT_LOGO_MODIFIER => *key_shift_state |= RIGHT_LOGO_PRESSED, + MENU_MODIFIER => *key_shift_state |= MENU_KEY_PRESSED, + SYS_REQUEST_MODIFIER | PRINT_MODIFIER => *key_shift_state |= SYS_REQ_PRESSED, + SCROLL_LOCK_MODIFIER => *key_toggle_state |= SCROLL_LOCK_ACTIVE, + NUM_LOCK_MODIFIER => *key_toggle_state |= NUM_LOCK_ACTIVE, + CAPS_LOCK_MODIFIER => *key_toggle_state |= CAPS_LOCK_ACTIVE, + _ => (), + } + } + key_state } - if !self.partial_key_support_active && key_data.key.unicode_char == 0 && key_data.key.scan_code == SCAN_NULL { - return; // no further processing required if there is no key or scancode and partial support is not active. + // pops and returns the front of the key queue + pub(crate) fn pop_key(&mut self) -> Option { + self.key_queue.pop_front() } - //initialize key state from active modifiers - key_data.key_state = self.init_key_state(); + // returns a copy of the key at the front of the queue + pub(crate) fn peek_key(&self) -> Option { + self.key_queue.front().cloned() + } - // if shift was applied above, then remove shift from key state. See UEFI spec 2.10 section 12.2.3. - if shift_applied { - key_data.key_state.key_shift_state &= !(LEFT_SHIFT_PRESSED | RIGHT_SHIFT_PRESSED); + // pops and returns the front of the notify queue + pub(crate) fn pop_notifiy_key(&mut self) -> Option { + self.notified_key_queue.pop_front() } - // if a callback has been registered matching this key, enqueue it in the callback queue. - if self.is_registered_key(key_data) { - self.notified_key_queue.push_back(key_data); + // returns a copy of the key at the front of the notify queue + pub(crate) fn peek_notify_key(&self) -> Option { + self.key_queue.front().cloned() } - // enqueue the key data. - self.key_queue.push_back(key_data); - } + // set the key toggle state. This allows control of scroll/caps/num locks, as well as whether partial key state is + // exposed. + pub(crate) fn set_key_toggle_state(&mut self, toggle_state: KeyToggleState) { + if (toggle_state & SCROLL_LOCK_ACTIVE) != 0 { + self.active_modifiers.insert(SCROLL_LOCK_MODIFIER); + } else { + self.active_modifiers.remove(&SCROLL_LOCK_MODIFIER); + } - fn is_registered_key(&self, current_key: KeyData) -> bool { - for registered_key in &self.registered_keys { - if OrdKeyData(current_key).matches_registered_key(registered_key) { - return true; - } + if (toggle_state & NUM_LOCK_ACTIVE) != 0 { + self.active_modifiers.insert(NUM_LOCK_MODIFIER); + } else { + self.active_modifiers.remove(&NUM_LOCK_MODIFIER); + } + + if (toggle_state & CAPS_LOCK_ACTIVE) != 0 { + self.active_modifiers.insert(CAPS_LOCK_MODIFIER); + } else { + self.active_modifiers.remove(&CAPS_LOCK_MODIFIER); + } + + self.partial_key_support_active = (toggle_state & KEY_STATE_EXPOSED) != 0; } - return false; - } - - // Creates a KeyState instance initialized based on the current modifier state. - pub(crate) fn init_key_state(&self) -> KeyState { - let mut key_state: KeyState = Default::default(); - - key_state.key_shift_state = SHIFT_STATE_VALID; - key_state.key_toggle_state = TOGGLE_STATE_VALID | KEY_STATE_EXPOSED; - - let key_shift_state = &mut key_state.key_shift_state; - let key_toggle_state = &mut key_state.key_toggle_state; - - for modifier in &self.active_modifiers { - match *modifier { - LEFT_CONTROL_MODIFIER => *key_shift_state |= LEFT_CONTROL_PRESSED, - RIGHT_CONTROL_MODIFIER => *key_shift_state |= RIGHT_CONTROL_PRESSED, - LEFT_ALT_MODIFIER => *key_shift_state |= LEFT_ALT_PRESSED, - RIGHT_ALT_MODIFIER => *key_shift_state |= RIGHT_ALT_PRESSED, - LEFT_SHIFT_MODIFIER => *key_shift_state |= LEFT_SHIFT_PRESSED, - RIGHT_SHIFT_MODIFIER => *key_shift_state |= RIGHT_SHIFT_PRESSED, - LEFT_LOGO_MODIFIER => *key_shift_state |= LEFT_LOGO_PRESSED, - RIGHT_LOGO_MODIFIER => *key_shift_state |= RIGHT_LOGO_PRESSED, - MENU_MODIFIER => *key_shift_state |= MENU_KEY_PRESSED, - SYS_REQUEST_MODIFIER | PRINT_MODIFIER => *key_shift_state |= SYS_REQ_PRESSED, - SCROLL_LOCK_MODIFIER => *key_toggle_state |= SCROLL_LOCK_ACTIVE, - NUM_LOCK_MODIFIER => *key_toggle_state |= NUM_LOCK_ACTIVE, - CAPS_LOCK_MODIFIER => *key_toggle_state |= CAPS_LOCK_ACTIVE, - _ => (), - } + + // Returns a vector of HID usages corresponding to the active LEDs based on the active modifier state. + pub(crate) fn get_active_leds(&self) -> Vec { + self.active_modifiers.iter().cloned().filter_map(|x| modifer_to_led_usage(x)).collect() } - key_state - } - - // pops and returns the front of the key queue - pub(crate) fn pop_key(&mut self) -> Option { - self.key_queue.pop_front() - } - - // returns a copy of the key at the front of the queue - pub(crate) fn peek_key(&self) -> Option { - self.key_queue.front().cloned() - } - - // pops and returns the front of the notify queue - pub(crate) fn pop_notifiy_key(&mut self) -> Option { - self.notified_key_queue.pop_front() - } - - // returns a copy of the key at the front of the notify queue - pub(crate) fn peek_notify_key(&self) -> Option { - self.key_queue.front().cloned() - } - - // set the key toggle state. This allows control of scroll/caps/num locks, as well as whether partial key state is - // exposed. - pub(crate) fn set_key_toggle_state(&mut self, toggle_state: KeyToggleState) { - if (toggle_state & SCROLL_LOCK_ACTIVE) != 0 { - self.active_modifiers.insert(SCROLL_LOCK_MODIFIER); - } else { - self.active_modifiers.remove(&SCROLL_LOCK_MODIFIER); + + // Returns the current keyboard layout that the KeyQueue is using. + pub(crate) fn get_layout(&self) -> Option { + self.layout.clone() } - if (toggle_state & NUM_LOCK_ACTIVE) != 0 { - self.active_modifiers.insert(NUM_LOCK_MODIFIER); - } else { - self.active_modifiers.remove(&NUM_LOCK_MODIFIER); + // Sets the current keyboard layout that the KeyQueue should use. + pub(crate) fn set_layout(&mut self, new_layout: Option) { + self.layout = new_layout; } - if (toggle_state & CAPS_LOCK_ACTIVE) != 0 { - self.active_modifiers.insert(CAPS_LOCK_MODIFIER); - } else { - self.active_modifiers.remove(&CAPS_LOCK_MODIFIER); + // Add a registration key for notifications; if a keystroke matches this key data, it will be added to the notify + // queue in addition to the normal key queue. + pub(crate) fn add_notify_key(&mut self, key_data: OrdKeyData) { + self.registered_keys.insert(key_data); } - self.partial_key_support_active = (toggle_state & KEY_STATE_EXPOSED) != 0; - } - - // Returns a vector of HID usages corresponding to the active LEDs based on the active modifier state. - pub(crate) fn get_active_leds(&self) -> Vec { - self.active_modifiers.iter().cloned().filter_map(|x| modifer_to_led_usage(x)).collect() - } - - // Returns the current keyboard layout that the KeyQueue is using. - pub(crate) fn get_layout(&self) -> Option { - self.layout.clone() - } - - // Sets the current keyboard layout that the KeyQueue should use. - pub(crate) fn set_layout(&mut self, new_layout: Option) { - self.layout = new_layout; - } - - // Add a registration key for notifications; if a keystroke matches this key data, it will be added to the notify - // queue in addition to the normal key queue. - pub(crate) fn add_notify_key(&mut self, key_data: OrdKeyData) { - self.registered_keys.insert(key_data); - } - - // Remove a previously added notify key; keystrokes matching this key data will no longer be added to the notify - // queue. - pub(crate) fn remove_notify_key(&mut self, key_data: &OrdKeyData) { - self.registered_keys.remove(key_data); - } + // Remove a previously added notify key; keystrokes matching this key data will no longer be added to the notify + // queue. + pub(crate) fn remove_notify_key(&mut self, key_data: &OrdKeyData) { + self.registered_keys.remove(key_data); + } } // Helper routine that converts a HID Usage to the corresponding EfiKey. fn usage_to_efi_key(usage: Usage) -> Option { - //Refer to UEFI spec version 2.10 figure 34.3 - match usage.into() { - 0x00070001..=0x00070003 => None, //Keyboard error codes. - 0x00070004 => Some(EfiKey::C1), - 0x00070005 => Some(EfiKey::B5), - 0x00070006 => Some(EfiKey::B3), - 0x00070007 => Some(EfiKey::C3), - 0x00070008 => Some(EfiKey::D3), - 0x00070009 => Some(EfiKey::C4), - 0x0007000A => Some(EfiKey::C5), - 0x0007000B => Some(EfiKey::C6), - 0x0007000C => Some(EfiKey::D8), - 0x0007000D => Some(EfiKey::C7), - 0x0007000E => Some(EfiKey::C8), - 0x0007000F => Some(EfiKey::C9), - 0x00070010 => Some(EfiKey::B7), - 0x00070011 => Some(EfiKey::B6), - 0x00070012 => Some(EfiKey::D9), - 0x00070013 => Some(EfiKey::D10), - 0x00070014 => Some(EfiKey::D1), - 0x00070015 => Some(EfiKey::D4), - 0x00070016 => Some(EfiKey::C2), - 0x00070017 => Some(EfiKey::D5), - 0x00070018 => Some(EfiKey::D7), - 0x00070019 => Some(EfiKey::B4), - 0x0007001A => Some(EfiKey::D2), - 0x0007001B => Some(EfiKey::B2), - 0x0007001C => Some(EfiKey::D6), - 0x0007001D => Some(EfiKey::B1), - 0x0007001E => Some(EfiKey::E1), - 0x0007001F => Some(EfiKey::E2), - 0x00070020 => Some(EfiKey::E3), - 0x00070021 => Some(EfiKey::E4), - 0x00070022 => Some(EfiKey::E5), - 0x00070023 => Some(EfiKey::E6), - 0x00070024 => Some(EfiKey::E7), - 0x00070025 => Some(EfiKey::E8), - 0x00070026 => Some(EfiKey::E9), - 0x00070027 => Some(EfiKey::E10), - 0x00070028 => Some(EfiKey::Enter), - 0x00070029 => Some(EfiKey::Esc), - 0x0007002A => Some(EfiKey::BackSpace), - 0x0007002B => Some(EfiKey::Tab), - 0x0007002C => Some(EfiKey::SpaceBar), - 0x0007002D => Some(EfiKey::E11), - 0x0007002E => Some(EfiKey::E12), - 0x0007002F => Some(EfiKey::D11), - 0x00070030 => Some(EfiKey::D12), - 0x00070031 => Some(EfiKey::D13), - 0x00070032 => Some(EfiKey::C12), - 0x00070033 => Some(EfiKey::C10), - 0x00070034 => Some(EfiKey::C11), - 0x00070035 => Some(EfiKey::E0), - 0x00070036 => Some(EfiKey::B8), - 0x00070037 => Some(EfiKey::B9), - 0x00070038 => Some(EfiKey::B10), - 0x00070039 => Some(EfiKey::CapsLock), - 0x0007003A => Some(EfiKey::F1), - 0x0007003B => Some(EfiKey::F2), - 0x0007003C => Some(EfiKey::F3), - 0x0007003D => Some(EfiKey::F4), - 0x0007003E => Some(EfiKey::F5), - 0x0007003F => Some(EfiKey::F6), - 0x00070040 => Some(EfiKey::F7), - 0x00070041 => Some(EfiKey::F8), - 0x00070042 => Some(EfiKey::F9), - 0x00070043 => Some(EfiKey::F10), - 0x00070044 => Some(EfiKey::F11), - 0x00070045 => Some(EfiKey::F12), - 0x00070046 => Some(EfiKey::Print), - 0x00070047 => Some(EfiKey::SLck), - 0x00070048 => Some(EfiKey::Pause), - 0x00070049 => Some(EfiKey::Ins), - 0x0007004A => Some(EfiKey::Home), - 0x0007004B => Some(EfiKey::PgUp), - 0x0007004C => Some(EfiKey::Del), - 0x0007004D => Some(EfiKey::End), - 0x0007004E => Some(EfiKey::PgDn), - 0x0007004F => Some(EfiKey::RightArrow), - 0x00070050 => Some(EfiKey::LeftArrow), - 0x00070051 => Some(EfiKey::DownArrow), - 0x00070052 => Some(EfiKey::UpArrow), - 0x00070053 => Some(EfiKey::NLck), - 0x00070054 => Some(EfiKey::Slash), - 0x00070055 => Some(EfiKey::Asterisk), - 0x00070056 => Some(EfiKey::Minus), - 0x00070057 => Some(EfiKey::Plus), - 0x00070058 => Some(EfiKey::Enter), - 0x00070059 => Some(EfiKey::One), - 0x0007005A => Some(EfiKey::Two), - 0x0007005B => Some(EfiKey::Three), - 0x0007005C => Some(EfiKey::Four), - 0x0007005D => Some(EfiKey::Five), - 0x0007005E => Some(EfiKey::Six), - 0x0007005F => Some(EfiKey::Seven), - 0x00070060 => Some(EfiKey::Eight), - 0x00070061 => Some(EfiKey::Nine), - 0x00070062 => Some(EfiKey::Zero), - 0x00070063 => Some(EfiKey::Period), - 0x00070064 => Some(EfiKey::B0), - 0x00070065 => Some(EfiKey::A4), - 0x00070066..=0x000700DF => None, // not used by EFI keyboard layout. - 0x000700E0 => Some(EfiKey::LCtrl), - 0x000700E1 => Some(EfiKey::LShift), - 0x000700E2 => Some(EfiKey::LAlt), - 0x000700E3 => Some(EfiKey::A0), - 0x000700E4 => Some(EfiKey::RCtrl), - 0x000700E5 => Some(EfiKey::RShift), - 0x000700E6 => Some(EfiKey::A2), - 0x000700E7 => Some(EfiKey::A3), - _ => None, // all other usages not used by EFI keyboard layout. - } + //Refer to UEFI spec version 2.10 figure 34.3 + match usage.into() { + 0x00070001..=0x00070003 => None, //Keyboard error codes. + 0x00070004 => Some(EfiKey::C1), + 0x00070005 => Some(EfiKey::B5), + 0x00070006 => Some(EfiKey::B3), + 0x00070007 => Some(EfiKey::C3), + 0x00070008 => Some(EfiKey::D3), + 0x00070009 => Some(EfiKey::C4), + 0x0007000A => Some(EfiKey::C5), + 0x0007000B => Some(EfiKey::C6), + 0x0007000C => Some(EfiKey::D8), + 0x0007000D => Some(EfiKey::C7), + 0x0007000E => Some(EfiKey::C8), + 0x0007000F => Some(EfiKey::C9), + 0x00070010 => Some(EfiKey::B7), + 0x00070011 => Some(EfiKey::B6), + 0x00070012 => Some(EfiKey::D9), + 0x00070013 => Some(EfiKey::D10), + 0x00070014 => Some(EfiKey::D1), + 0x00070015 => Some(EfiKey::D4), + 0x00070016 => Some(EfiKey::C2), + 0x00070017 => Some(EfiKey::D5), + 0x00070018 => Some(EfiKey::D7), + 0x00070019 => Some(EfiKey::B4), + 0x0007001A => Some(EfiKey::D2), + 0x0007001B => Some(EfiKey::B2), + 0x0007001C => Some(EfiKey::D6), + 0x0007001D => Some(EfiKey::B1), + 0x0007001E => Some(EfiKey::E1), + 0x0007001F => Some(EfiKey::E2), + 0x00070020 => Some(EfiKey::E3), + 0x00070021 => Some(EfiKey::E4), + 0x00070022 => Some(EfiKey::E5), + 0x00070023 => Some(EfiKey::E6), + 0x00070024 => Some(EfiKey::E7), + 0x00070025 => Some(EfiKey::E8), + 0x00070026 => Some(EfiKey::E9), + 0x00070027 => Some(EfiKey::E10), + 0x00070028 => Some(EfiKey::Enter), + 0x00070029 => Some(EfiKey::Esc), + 0x0007002A => Some(EfiKey::BackSpace), + 0x0007002B => Some(EfiKey::Tab), + 0x0007002C => Some(EfiKey::SpaceBar), + 0x0007002D => Some(EfiKey::E11), + 0x0007002E => Some(EfiKey::E12), + 0x0007002F => Some(EfiKey::D11), + 0x00070030 => Some(EfiKey::D12), + 0x00070031 => Some(EfiKey::D13), + 0x00070032 => Some(EfiKey::C12), + 0x00070033 => Some(EfiKey::C10), + 0x00070034 => Some(EfiKey::C11), + 0x00070035 => Some(EfiKey::E0), + 0x00070036 => Some(EfiKey::B8), + 0x00070037 => Some(EfiKey::B9), + 0x00070038 => Some(EfiKey::B10), + 0x00070039 => Some(EfiKey::CapsLock), + 0x0007003A => Some(EfiKey::F1), + 0x0007003B => Some(EfiKey::F2), + 0x0007003C => Some(EfiKey::F3), + 0x0007003D => Some(EfiKey::F4), + 0x0007003E => Some(EfiKey::F5), + 0x0007003F => Some(EfiKey::F6), + 0x00070040 => Some(EfiKey::F7), + 0x00070041 => Some(EfiKey::F8), + 0x00070042 => Some(EfiKey::F9), + 0x00070043 => Some(EfiKey::F10), + 0x00070044 => Some(EfiKey::F11), + 0x00070045 => Some(EfiKey::F12), + 0x00070046 => Some(EfiKey::Print), + 0x00070047 => Some(EfiKey::SLck), + 0x00070048 => Some(EfiKey::Pause), + 0x00070049 => Some(EfiKey::Ins), + 0x0007004A => Some(EfiKey::Home), + 0x0007004B => Some(EfiKey::PgUp), + 0x0007004C => Some(EfiKey::Del), + 0x0007004D => Some(EfiKey::End), + 0x0007004E => Some(EfiKey::PgDn), + 0x0007004F => Some(EfiKey::RightArrow), + 0x00070050 => Some(EfiKey::LeftArrow), + 0x00070051 => Some(EfiKey::DownArrow), + 0x00070052 => Some(EfiKey::UpArrow), + 0x00070053 => Some(EfiKey::NLck), + 0x00070054 => Some(EfiKey::Slash), + 0x00070055 => Some(EfiKey::Asterisk), + 0x00070056 => Some(EfiKey::Minus), + 0x00070057 => Some(EfiKey::Plus), + 0x00070058 => Some(EfiKey::Enter), + 0x00070059 => Some(EfiKey::One), + 0x0007005A => Some(EfiKey::Two), + 0x0007005B => Some(EfiKey::Three), + 0x0007005C => Some(EfiKey::Four), + 0x0007005D => Some(EfiKey::Five), + 0x0007005E => Some(EfiKey::Six), + 0x0007005F => Some(EfiKey::Seven), + 0x00070060 => Some(EfiKey::Eight), + 0x00070061 => Some(EfiKey::Nine), + 0x00070062 => Some(EfiKey::Zero), + 0x00070063 => Some(EfiKey::Period), + 0x00070064 => Some(EfiKey::B0), + 0x00070065 => Some(EfiKey::A4), + 0x00070066..=0x000700DF => None, // not used by EFI keyboard layout. + 0x000700E0 => Some(EfiKey::LCtrl), + 0x000700E1 => Some(EfiKey::LShift), + 0x000700E2 => Some(EfiKey::LAlt), + 0x000700E3 => Some(EfiKey::A0), + 0x000700E4 => Some(EfiKey::RCtrl), + 0x000700E5 => Some(EfiKey::RShift), + 0x000700E6 => Some(EfiKey::A2), + 0x000700E7 => Some(EfiKey::A3), + _ => None, // all other usages not used by EFI keyboard layout. + } } //These should be defined in r_efi::protocols::simple_text_input @@ -567,40 +567,40 @@ const SCAN_PAUSE: u16 = 0x0048; // helper routine that converts the given modifier to the corresponding SCAN code fn modifier_to_scan(modifier: u16) -> u16 { - match modifier { - INSERT_MODIFIER => SCAN_INSERT, - DELETE_MODIFIER => SCAN_DELETE, - PAGE_DOWN_MODIFIER => SCAN_PAGE_DOWN, - PAGE_UP_MODIFIER => SCAN_PAGE_UP, - HOME_MODIFIER => SCAN_HOME, - END_MODIFIER => SCAN_END, - LEFT_ARROW_MODIFIER => SCAN_LEFT, - RIGHT_ARROW_MODIFIER => SCAN_RIGHT, - DOWN_ARROW_MODIFIER => SCAN_DOWN, - UP_ARROW_MODIFIER => SCAN_UP, - FUNCTION_KEY_ONE_MODIFIER => SCAN_F1, - FUNCTION_KEY_TWO_MODIFIER => SCAN_F2, - FUNCTION_KEY_THREE_MODIFIER => SCAN_F3, - FUNCTION_KEY_FOUR_MODIFIER => SCAN_F4, - FUNCTION_KEY_FIVE_MODIFIER => SCAN_F5, - FUNCTION_KEY_SIX_MODIFIER => SCAN_F6, - FUNCTION_KEY_SEVEN_MODIFIER => SCAN_F7, - FUNCTION_KEY_EIGHT_MODIFIER => SCAN_F8, - FUNCTION_KEY_NINE_MODIFIER => SCAN_F9, - FUNCTION_KEY_TEN_MODIFIER => SCAN_F10, - FUNCTION_KEY_ELEVEN_MODIFIER => SCAN_F11, - FUNCTION_KEY_TWELVE_MODIFIER => SCAN_F12, - PAUSE_MODIFIER => SCAN_PAUSE, - _ => SCAN_NULL, - } + match modifier { + INSERT_MODIFIER => SCAN_INSERT, + DELETE_MODIFIER => SCAN_DELETE, + PAGE_DOWN_MODIFIER => SCAN_PAGE_DOWN, + PAGE_UP_MODIFIER => SCAN_PAGE_UP, + HOME_MODIFIER => SCAN_HOME, + END_MODIFIER => SCAN_END, + LEFT_ARROW_MODIFIER => SCAN_LEFT, + RIGHT_ARROW_MODIFIER => SCAN_RIGHT, + DOWN_ARROW_MODIFIER => SCAN_DOWN, + UP_ARROW_MODIFIER => SCAN_UP, + FUNCTION_KEY_ONE_MODIFIER => SCAN_F1, + FUNCTION_KEY_TWO_MODIFIER => SCAN_F2, + FUNCTION_KEY_THREE_MODIFIER => SCAN_F3, + FUNCTION_KEY_FOUR_MODIFIER => SCAN_F4, + FUNCTION_KEY_FIVE_MODIFIER => SCAN_F5, + FUNCTION_KEY_SIX_MODIFIER => SCAN_F6, + FUNCTION_KEY_SEVEN_MODIFIER => SCAN_F7, + FUNCTION_KEY_EIGHT_MODIFIER => SCAN_F8, + FUNCTION_KEY_NINE_MODIFIER => SCAN_F9, + FUNCTION_KEY_TEN_MODIFIER => SCAN_F10, + FUNCTION_KEY_ELEVEN_MODIFIER => SCAN_F11, + FUNCTION_KEY_TWELVE_MODIFIER => SCAN_F12, + PAUSE_MODIFIER => SCAN_PAUSE, + _ => SCAN_NULL, + } } // helper routine that converts the given modifer to the corresponding HID Usage. fn modifer_to_led_usage(modifier: u16) -> Option { - match modifier { - NUM_LOCK_MODIFIER => Some(Usage::from(0x00080001)), - CAPS_LOCK_MODIFIER => Some(Usage::from(0x00080002)), - SCROLL_LOCK_MODIFIER => Some(Usage::from(0x00080003)), - _ => None, - } + match modifier { + NUM_LOCK_MODIFIER => Some(Usage::from(0x00080001)), + CAPS_LOCK_MODIFIER => Some(Usage::from(0x00080002)), + SCROLL_LOCK_MODIFIER => Some(Usage::from(0x00080003)), + _ => None, + } } diff --git a/HidPkg/UefiHidDxe/src/keyboard.rs b/HidPkg/UefiHidDxe/src/keyboard.rs index 9624bb3ded..b191e6a670 100644 --- a/HidPkg/UefiHidDxe/src/keyboard.rs +++ b/HidPkg/UefiHidDxe/src/keyboard.rs @@ -11,31 +11,31 @@ use core::ffi::c_void; use alloc::{ - boxed::Box, - collections::{BTreeMap, BTreeSet}, - vec, - vec::Vec, + boxed::Box, + collections::{BTreeMap, BTreeSet}, + vec, + vec::Vec, }; use hidparser::{ - report_data_types::{ReportId, Usage}, - ArrayField, ReportDescriptor, ReportField, VariableField, + report_data_types::{ReportId, Usage}, + ArrayField, ReportDescriptor, ReportField, VariableField, }; use hii_keyboard_layout::HiiKeyboardLayout; use memoffset::{offset_of, raw_field}; use r_efi::{ - efi, hii, - protocols::{ - self, driver_binding, - simple_text_input_ex::{KeyData, LEFT_SHIFT_PRESSED, RIGHT_SHIFT_PRESSED}, - }, - system, + efi, hii, + protocols::{ + self, driver_binding, + simple_text_input_ex::{KeyData, LEFT_SHIFT_PRESSED, RIGHT_SHIFT_PRESSED}, + }, + system, }; use rust_advanced_logger_dxe::{debugln, DEBUG_ERROR, DEBUG_WARN}; use crate::{ - hid::HidContext, - key_queue::{self, OrdKeyData}, - BOOT_SERVICES, + hid::HidContext, + key_queue::{self, OrdKeyData}, + BOOT_SERVICES, }; // usages supported by this module @@ -49,32 +49,32 @@ const LED_USAGE_MAX: u32 = 0x00080005; // maps a given field to a routine that handles input from it. #[derive(Debug, Clone)] struct ReportFieldWithHandler { - field: T, - report_handler: fn(handler: &mut KeyboardHandler, field: T, report: &[u8]), + field: T, + report_handler: fn(handler: &mut KeyboardHandler, field: T, report: &[u8]), } // maps a given field to a routine that builds output reports from it. #[derive(Debug, Clone)] struct ReportFieldBuilder { - field: T, - field_builder: fn(&mut KeyboardHandler, field: T, report: &mut [u8]), + field: T, + field_builder: fn(&mut KeyboardHandler, field: T, report: &mut [u8]), } // Defines an input report and the fields of interest in it. #[derive(Debug, Default, Clone)] struct KeyboardReportData { - report_id: Option, - report_size: usize, - relevant_variable_fields: Vec>, - relevant_array_fields: Vec>, + report_id: Option, + report_size: usize, + relevant_variable_fields: Vec>, + relevant_array_fields: Vec>, } // Defines an output report and the fields of interest in it. #[derive(Debug, Default, Clone)] struct KeyboardOutputReportBuilder { - report_id: Option, - report_size: usize, - relevant_variable_fields: Vec>, + report_id: Option, + report_size: usize, + relevant_variable_fields: Vec>, } /// Context structure used to track data for this pointer device. @@ -82,115 +82,115 @@ struct KeyboardOutputReportBuilder { /// remain #[repr(C)], and Rust aliasing and concurrency rules must be manually enforced. #[repr(C)] pub struct KeyboardContext { - simple_text_in: protocols::simple_text_input::Protocol, - simple_text_in_ex: protocols::simple_text_input_ex::Protocol, - pub handler: KeyboardHandler, - controller: efi::Handle, - hid_context: *mut HidContext, + simple_text_in: protocols::simple_text_input::Protocol, + simple_text_in_ex: protocols::simple_text_input_ex::Protocol, + pub handler: KeyboardHandler, + controller: efi::Handle, + hid_context: *mut HidContext, } /// Tracks all the input reports for this device as well as pointer state. #[derive(Debug)] pub struct KeyboardHandler { - input_reports: BTreeMap, KeyboardReportData>, - output_builders: Vec, - report_id_present: bool, - last_keys: BTreeSet, - current_keys: BTreeSet, - led_state: BTreeSet, - key_queue: key_queue::KeyQueue, - notification_callbacks: BTreeMap, - next_notify_handle: usize, - key_notify_event: efi::Event, - hid_io: *mut hid_io::protocol::Protocol, + input_reports: BTreeMap, KeyboardReportData>, + output_builders: Vec, + report_id_present: bool, + last_keys: BTreeSet, + current_keys: BTreeSet, + led_state: BTreeSet, + key_queue: key_queue::KeyQueue, + notification_callbacks: BTreeMap, + next_notify_handle: usize, + key_notify_event: efi::Event, + hid_io: *mut hid_io::protocol::Protocol, } impl KeyboardHandler { - // processes a report descriptor and yields a KeyboardHandler instance if this descriptor describes input - // that can be handled by this KeyboardHandler. - fn process_descriptor(descriptor: &ReportDescriptor) -> Result { - let mut handler: KeyboardHandler = KeyboardHandler { - input_reports: BTreeMap::new(), - output_builders: Vec::new(), - report_id_present: false, - last_keys: BTreeSet::new(), - current_keys: BTreeSet::new(), - led_state: BTreeSet::new(), - key_queue: Default::default(), - notification_callbacks: BTreeMap::new(), - next_notify_handle: 0, - key_notify_event: core::ptr::null_mut(), - hid_io: core::ptr::null_mut(), - }; - - let multiple_reports = - descriptor.input_reports.len() > 1 || descriptor.output_reports.len() > 1 || descriptor.features.len() > 1; - - for report in &descriptor.input_reports { - let mut report_data = KeyboardReportData { report_id: report.report_id, ..Default::default() }; - - handler.report_id_present = report.report_id.is_some(); - - if multiple_reports && !handler.report_id_present { - //Invalid to have None ReportId if multiple reports present. - Err(efi::Status::DEVICE_ERROR)?; - } - - report_data.report_size = report.size_in_bits.div_ceil(8); + // processes a report descriptor and yields a KeyboardHandler instance if this descriptor describes input + // that can be handled by this KeyboardHandler. + fn process_descriptor(descriptor: &ReportDescriptor) -> Result { + let mut handler: KeyboardHandler = KeyboardHandler { + input_reports: BTreeMap::new(), + output_builders: Vec::new(), + report_id_present: false, + last_keys: BTreeSet::new(), + current_keys: BTreeSet::new(), + led_state: BTreeSet::new(), + key_queue: Default::default(), + notification_callbacks: BTreeMap::new(), + next_notify_handle: 0, + key_notify_event: core::ptr::null_mut(), + hid_io: core::ptr::null_mut(), + }; + + let multiple_reports = + descriptor.input_reports.len() > 1 || descriptor.output_reports.len() > 1 || descriptor.features.len() > 1; + + for report in &descriptor.input_reports { + let mut report_data = KeyboardReportData { report_id: report.report_id, ..Default::default() }; + + handler.report_id_present = report.report_id.is_some(); + + if multiple_reports && !handler.report_id_present { + //Invalid to have None ReportId if multiple reports present. + Err(efi::Status::DEVICE_ERROR)?; + } - for field in &report.fields { - match field { - //Variable fields (typically used for modifier Usages) - ReportField::Variable(field) => { - match field.usage.into() { - KEYBOARD_MODIFIER_USAGE_MIN..=KEYBOARD_MODIFIER_USAGE_MAX => { - report_data.relevant_variable_fields.push(ReportFieldWithHandler:: { - field: field.clone(), - report_handler: Self::handle_variable_key, - }) - } - _ => (), // other usages irrelevant. + report_data.report_size = report.size_in_bits.div_ceil(8); + + for field in &report.fields { + match field { + //Variable fields (typically used for modifier Usages) + ReportField::Variable(field) => { + match field.usage.into() { + KEYBOARD_MODIFIER_USAGE_MIN..=KEYBOARD_MODIFIER_USAGE_MAX => { + report_data.relevant_variable_fields.push(ReportFieldWithHandler:: { + field: field.clone(), + report_handler: Self::handle_variable_key, + }) + } + _ => (), // other usages irrelevant. + } + } + //Array fields (typically used for key strokes) + ReportField::Array(field) => { + for usage_list in &field.usage_list { + if usage_list.contains(Usage::from(KEYBOARD_USAGE_MIN)) + || usage_list.contains(Usage::from(KEYBOARD_USAGE_MAX)) + { + report_data.relevant_array_fields.push(ReportFieldWithHandler:: { + field: field.clone(), + report_handler: Self::handle_array_key, + }); + break; + } + } + } + ReportField::Padding(_) => (), // padding irrelevant. + } } - } - //Array fields (typically used for key strokes) - ReportField::Array(field) => { - for usage_list in &field.usage_list { - if usage_list.contains(Usage::from(KEYBOARD_USAGE_MIN)) - || usage_list.contains(Usage::from(KEYBOARD_USAGE_MAX)) - { - report_data.relevant_array_fields.push(ReportFieldWithHandler:: { - field: field.clone(), - report_handler: Self::handle_array_key, - }); - break; - } + if report_data.relevant_variable_fields.len() > 0 || report_data.relevant_array_fields.len() > 0 { + handler.input_reports.insert(report_data.report_id, report_data); } - } - ReportField::Padding(_) => (), // padding irrelevant. } - } - if report_data.relevant_variable_fields.len() > 0 || report_data.relevant_array_fields.len() > 0 { - handler.input_reports.insert(report_data.report_id, report_data); - } - } - for report in &descriptor.output_reports { - let mut report_builder = KeyboardOutputReportBuilder { report_id: report.report_id, ..Default::default() }; + for report in &descriptor.output_reports { + let mut report_builder = KeyboardOutputReportBuilder { report_id: report.report_id, ..Default::default() }; - handler.report_id_present = report.report_id.is_some(); + handler.report_id_present = report.report_id.is_some(); - if multiple_reports && !handler.report_id_present { - //invalid to have None ReportId if multiple reports present. - Err(efi::Status::DEVICE_ERROR)?; - } + if multiple_reports && !handler.report_id_present { + //invalid to have None ReportId if multiple reports present. + Err(efi::Status::DEVICE_ERROR)?; + } - report_builder.report_size = report.size_in_bits / 8; - if (report.size_in_bits % 8) != 0 { - report_builder.report_size += 1; - } + report_builder.report_size = report.size_in_bits / 8; + if (report.size_in_bits % 8) != 0 { + report_builder.report_size += 1; + } - for field in &report.fields { - match field { + for field in &report.fields { + match field { //Variable fields in output reports (typically used for LEDs). ReportField::Variable(field) => { match field.usage.into() { @@ -208,331 +208,331 @@ impl KeyboardHandler { ReportField::Array(_) | // No support for array field report outputs; could be added if required. ReportField::Padding(_) => (), // padding fields irrelevant. } - } - if report_builder.relevant_variable_fields.len() > 0 { - handler.output_builders.push(report_builder); - } - } + } + if report_builder.relevant_variable_fields.len() > 0 { + handler.output_builders.push(report_builder); + } + } - if handler.input_reports.len() > 0 || handler.output_builders.len() > 0 { - Ok(handler) - } else { - Err(efi::Status::UNSUPPORTED) + if handler.input_reports.len() > 0 || handler.output_builders.len() > 0 { + Ok(handler) + } else { + Err(efi::Status::UNSUPPORTED) + } } - } - //Create the Keyboard Context and install the SimpleTextIn/SimpleTextInEx interfaces. - fn install_keyboard_interfaces( - self, - controller: efi::Handle, - hid_context: *mut HidContext, - ) -> Result<*mut KeyboardContext, efi::Status> { - // retrieve a reference to boot services. - // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + //Create the Keyboard Context and install the SimpleTextIn/SimpleTextInEx interfaces. + fn install_keyboard_interfaces( + self, + controller: efi::Handle, + hid_context: *mut HidContext, + ) -> Result<*mut KeyboardContext, efi::Status> { + // retrieve a reference to boot services. + // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + + // Caller should have ensured this, so just expect on failure. + let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; + + // Create keyboard context. This context is shared across FFI boundary, so use Box::into_raw. + // After creation, the only way to access the context (including the handler instance) is via raw pointer. + let context_ptr = Box::into_raw(Box::new(KeyboardContext { + handler: self, + simple_text_in: protocols::simple_text_input::Protocol { + reset: simple_text_in_reset, + read_key_stroke: simple_text_in_read_key_stroke, + wait_for_key: core::ptr::null_mut(), + }, + simple_text_in_ex: protocols::simple_text_input_ex::Protocol { + reset: simple_text_in_ex_reset, + read_key_stroke_ex: simple_text_in_ex_read_key_stroke, + wait_for_key_ex: core::ptr::null_mut(), + set_state: simple_text_in_ex_set_state, + register_key_notify: simple_text_in_ex_register_key_notify, + unregister_key_notify: simple_text_in_ex_unregister_key_notify, + }, + controller, + hid_context, + })); + + let context = unsafe { context_ptr.as_mut().expect("freshly boxed context pointer is null") }; + //create wait_for_key events. + let mut wait_for_key_event: efi::Event = core::ptr::null_mut(); + let status = (boot_services.create_event)( + system::EVT_NOTIFY_WAIT, + system::TPL_NOTIFY, + Some(wait_for_key), + context_ptr as *mut c_void, + core::ptr::addr_of_mut!(wait_for_key_event), + ); + if status.is_error() { + drop(unsafe { Box::from_raw(context_ptr) }); + return Err(status); + } + context.simple_text_in.wait_for_key = wait_for_key_event; + + let mut wait_for_key_ex_event: efi::Event = core::ptr::null_mut(); + let status = (boot_services.create_event)( + system::EVT_NOTIFY_WAIT, + system::TPL_NOTIFY, + Some(wait_for_key), + context_ptr as *mut c_void, + core::ptr::addr_of_mut!(wait_for_key_ex_event), + ); + if status.is_error() { + (boot_services.close_event)(wait_for_key_event); + drop(unsafe { Box::from_raw(context_ptr) }); + return Err(status); + } + context.simple_text_in_ex.wait_for_key_ex = wait_for_key_ex_event; + + //create deferred dispatch event for key notifies. Key notify callbacks are required to be invoked at TPL_CALLBACK + //per UEFI spec 2.10 section 12.2.5. The keyboard handler interfaces may run at a higher TPL, so this event is used + //to dispatch the key notifies at the required TPL level. + let mut key_notify_event: efi::Event = core::ptr::null_mut(); + let status = (boot_services.create_event)( + system::EVT_NOTIFY_SIGNAL, + system::TPL_CALLBACK, + Some(process_key_notifies), + context_ptr as *mut c_void, + core::ptr::addr_of_mut!(key_notify_event), + ); + if status.is_error() { + (boot_services.close_event)(wait_for_key_event); + (boot_services.close_event)(wait_for_key_ex_event); + drop(unsafe { Box::from_raw(context_ptr) }); + return Err(status); + } + context.handler.key_notify_event = key_notify_event; + + //install simple_text_in and simple_text_in_ex + let mut controller = controller; + let simple_text_in_ptr = raw_field!(context_ptr, KeyboardContext, simple_text_in); + let status = (boot_services.install_protocol_interface)( + core::ptr::addr_of_mut!(controller), + &protocols::simple_text_input::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + efi::NATIVE_INTERFACE, + simple_text_in_ptr as *mut c_void, + ); - // Caller should have ensured this, so just expect on failure. - let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; + if status.is_error() { + let _ = deinitialize(context_ptr); + return Err(status); + } - // Create keyboard context. This context is shared across FFI boundary, so use Box::into_raw. - // After creation, the only way to access the context (including the handler instance) is via raw pointer. - let context_ptr = Box::into_raw(Box::new(KeyboardContext { - handler: self, - simple_text_in: protocols::simple_text_input::Protocol { - reset: simple_text_in_reset, - read_key_stroke: simple_text_in_read_key_stroke, - wait_for_key: core::ptr::null_mut(), - }, - simple_text_in_ex: protocols::simple_text_input_ex::Protocol { - reset: simple_text_in_ex_reset, - read_key_stroke_ex: simple_text_in_ex_read_key_stroke, - wait_for_key_ex: core::ptr::null_mut(), - set_state: simple_text_in_ex_set_state, - register_key_notify: simple_text_in_ex_register_key_notify, - unregister_key_notify: simple_text_in_ex_unregister_key_notify, - }, - controller, - hid_context, - })); - - let context = unsafe { context_ptr.as_mut().expect("freshly boxed context pointer is null") }; - //create wait_for_key events. - let mut wait_for_key_event: efi::Event = core::ptr::null_mut(); - let status = (boot_services.create_event)( - system::EVT_NOTIFY_WAIT, - system::TPL_NOTIFY, - Some(wait_for_key), - context_ptr as *mut c_void, - core::ptr::addr_of_mut!(wait_for_key_event), - ); - if status.is_error() { - drop(unsafe { Box::from_raw(context_ptr) }); - return Err(status); - } - context.simple_text_in.wait_for_key = wait_for_key_event; - - let mut wait_for_key_ex_event: efi::Event = core::ptr::null_mut(); - let status = (boot_services.create_event)( - system::EVT_NOTIFY_WAIT, - system::TPL_NOTIFY, - Some(wait_for_key), - context_ptr as *mut c_void, - core::ptr::addr_of_mut!(wait_for_key_ex_event), - ); - if status.is_error() { - (boot_services.close_event)(wait_for_key_event); - drop(unsafe { Box::from_raw(context_ptr) }); - return Err(status); - } - context.simple_text_in_ex.wait_for_key_ex = wait_for_key_ex_event; - - //create deferred dispatch event for key notifies. Key notify callbacks are required to be invoked at TPL_CALLBACK - //per UEFI spec 2.10 section 12.2.5. The keyboard handler interfaces may run at a higher TPL, so this event is used - //to dispatch the key notifies at the required TPL level. - let mut key_notify_event: efi::Event = core::ptr::null_mut(); - let status = (boot_services.create_event)( - system::EVT_NOTIFY_SIGNAL, - system::TPL_CALLBACK, - Some(process_key_notifies), - context_ptr as *mut c_void, - core::ptr::addr_of_mut!(key_notify_event), - ); - if status.is_error() { - (boot_services.close_event)(wait_for_key_event); - (boot_services.close_event)(wait_for_key_ex_event); - drop(unsafe { Box::from_raw(context_ptr) }); - return Err(status); - } - context.handler.key_notify_event = key_notify_event; - - //install simple_text_in and simple_text_in_ex - let mut controller = controller; - let simple_text_in_ptr = raw_field!(context_ptr, KeyboardContext, simple_text_in); - let status = (boot_services.install_protocol_interface)( - core::ptr::addr_of_mut!(controller), - &protocols::simple_text_input::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - efi::NATIVE_INTERFACE, - simple_text_in_ptr as *mut c_void, - ); + let simple_text_in_ex_ptr = raw_field!(context_ptr, KeyboardContext, simple_text_in_ex); + let status = (boot_services.install_protocol_interface)( + core::ptr::addr_of_mut!(controller), + &protocols::simple_text_input_ex::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + efi::NATIVE_INTERFACE, + simple_text_in_ex_ptr as *mut c_void, + ); - if status.is_error() { - let _ = deinitialize(context_ptr); - return Err(status); - } + if status.is_error() { + let _ = deinitialize(context_ptr); + return Err(status); + } - let simple_text_in_ex_ptr = raw_field!(context_ptr, KeyboardContext, simple_text_in_ex); - let status = (boot_services.install_protocol_interface)( - core::ptr::addr_of_mut!(controller), - &protocols::simple_text_input_ex::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - efi::NATIVE_INTERFACE, - simple_text_in_ex_ptr as *mut c_void, - ); + context.handler.hid_io = unsafe { (*hid_context).hid_io }; - if status.is_error() { - let _ = deinitialize(context_ptr); - return Err(status); + Ok(context_ptr) } - context.handler.hid_io = unsafe { (*hid_context).hid_io }; + // Uninstall the SimpleTextIn and SimpleTextInEx interfaces and free the Keyboard context. + fn uninstall_keyboard_interfaces(keyboard_context: *mut KeyboardContext) -> Result<(), efi::Status> { + // retrieve a reference to boot services. + // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + // Caller should have ensured this, so just expect on failure. + let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; - Ok(context_ptr) - } + let simple_text_in_ptr = raw_field!(keyboard_context, KeyboardContext, simple_text_in); + let simple_text_in_ex_ptr = raw_field!(keyboard_context, KeyboardContext, simple_text_in_ex); - // Uninstall the SimpleTextIn and SimpleTextInEx interfaces and free the Keyboard context. - fn uninstall_keyboard_interfaces(keyboard_context: *mut KeyboardContext) -> Result<(), efi::Status> { - // retrieve a reference to boot services. - // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. - // Caller should have ensured this, so just expect on failure. - let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; + let mut overall_status = efi::Status::SUCCESS; - let simple_text_in_ptr = raw_field!(keyboard_context, KeyboardContext, simple_text_in); - let simple_text_in_ex_ptr = raw_field!(keyboard_context, KeyboardContext, simple_text_in_ex); + // close the wait_for_key events + let status = (boot_services.close_event)(unsafe { (*simple_text_in_ptr).wait_for_key }); + if status.is_error() { + overall_status = status; + } + let status = (boot_services.close_event)(unsafe { (*simple_text_in_ex_ptr).wait_for_key_ex }); + if status.is_error() { + overall_status = status; + } + let status = (boot_services.close_event)(unsafe { (*keyboard_context).handler.key_notify_event }); + if status.is_error() { + overall_status = status; + } - let mut overall_status = efi::Status::SUCCESS; + //uninstall the protocol interfaces + let status = (boot_services.uninstall_protocol_interface)( + unsafe { (*keyboard_context).controller }, + &protocols::simple_text_input::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + simple_text_in_ptr as *mut c_void, + ); + if status.is_error() { + overall_status = status; + } + let status = (boot_services.uninstall_protocol_interface)( + unsafe { (*keyboard_context).controller }, + &protocols::simple_text_input_ex::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + simple_text_in_ex_ptr as *mut c_void, + ); + if status.is_error() { + overall_status = status; + } - // close the wait_for_key events - let status = (boot_services.close_event)(unsafe { (*simple_text_in_ptr).wait_for_key }); - if status.is_error() { - overall_status = status; - } - let status = (boot_services.close_event)(unsafe { (*simple_text_in_ex_ptr).wait_for_key_ex }); - if status.is_error() { - overall_status = status; - } - let status = (boot_services.close_event)(unsafe { (*keyboard_context).handler.key_notify_event }); - if status.is_error() { - overall_status = status; - } + // take back the context raw pointer + drop(unsafe { Box::from_raw(keyboard_context) }); - //uninstall the protocol interfaces - let status = (boot_services.uninstall_protocol_interface)( - unsafe { (*keyboard_context).controller }, - &protocols::simple_text_input::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - simple_text_in_ptr as *mut c_void, - ); - if status.is_error() { - overall_status = status; - } - let status = (boot_services.uninstall_protocol_interface)( - unsafe { (*keyboard_context).controller }, - &protocols::simple_text_input_ex::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - simple_text_in_ex_ptr as *mut c_void, - ); - if status.is_error() { - overall_status = status; + if overall_status.is_error() { + Err(overall_status) + } else { + Ok(()) + } } - // take back the context raw pointer - drop(unsafe { Box::from_raw(keyboard_context) }); - - if overall_status.is_error() { - Err(overall_status) - } else { - Ok(()) - } - } - - // helper routine to handle variable keyboard input report fields - fn handle_variable_key(&mut self, field: VariableField, report: &[u8]) { - match field.field_value(report) { - Some(x) if x != 0 => { - self.current_keys.insert(field.usage); - } - None | Some(_) => (), - } - } - - // helper routine to handle array keyboard input report fields - fn handle_array_key(&mut self, field: ArrayField, report: &[u8]) { - match field.field_value(report) { - Some(index) if index != 0 => { - let mut index = (index as u32 - u32::from(field.logical_minimum)) as usize; - let usage = field.usage_list.iter().find_map(|x| { - let range_size = (x.end() - x.start()) as usize; - if index <= range_size { - x.range().nth(index) - } else { - index = index - range_size as usize; - None - } - }); - if let Some(usage) = usage { - self.current_keys.insert(Usage::from(usage)); + // helper routine to handle variable keyboard input report fields + fn handle_variable_key(&mut self, field: VariableField, report: &[u8]) { + match field.field_value(report) { + Some(x) if x != 0 => { + self.current_keys.insert(field.usage); + } + None | Some(_) => (), } - } - None | Some(_) => (), } - } - // process the given input report buffer and handle input from it. - pub fn process_input_report(&mut self, report_buffer: &[u8]) { - if report_buffer.len() == 0 { - return; + // helper routine to handle array keyboard input report fields + fn handle_array_key(&mut self, field: ArrayField, report: &[u8]) { + match field.field_value(report) { + Some(index) if index != 0 => { + let mut index = (index as u32 - u32::from(field.logical_minimum)) as usize; + let usage = field.usage_list.iter().find_map(|x| { + let range_size = (x.end() - x.start()) as usize; + if index <= range_size { + x.range().nth(index) + } else { + index = index - range_size as usize; + None + } + }); + if let Some(usage) = usage { + self.current_keys.insert(Usage::from(usage)); + } + } + None | Some(_) => (), + } } - // determine whether report includes report id byte and adjust the buffer as needed. - let (report_id, report) = match self.report_id_present { - true => (Some(ReportId::from(&report_buffer[0..1])), &report_buffer[1..]), - false => (None, &report_buffer[0..]), - }; + // process the given input report buffer and handle input from it. + pub fn process_input_report(&mut self, report_buffer: &[u8]) { + if report_buffer.len() == 0 { + return; + } - if report.len() == 0 { - return; - } + // determine whether report includes report id byte and adjust the buffer as needed. + let (report_id, report) = match self.report_id_present { + true => (Some(ReportId::from(&report_buffer[0..1])), &report_buffer[1..]), + false => (None, &report_buffer[0..]), + }; - if let Some(report_data) = self.input_reports.get(&report_id).cloned() { - if report.len() != report_data.report_size { - return; - } - - //reset currently active keys to empty set. - self.current_keys.clear(); - - // hand the report data to the handler for each relevant field for field-specific processing. - for field in report_data.relevant_variable_fields { - (field.report_handler)(self, field.field, report); - } - - for field in report_data.relevant_array_fields { - (field.report_handler)(self, field.field, report); - } - - //check if any key state has changed. - if self.last_keys != self.current_keys { - // process keys that are not in both sets: that is the set of keys that have changed. - // XOR on the sets yields a set of keys that are in either last or current keys, but not both. - - // Modifier keys need to be processed first so that normal key processing includes modifiers that showed up in - // the same report. The key sets are sorted by Usage, and modifier keys all have higher usages than normal keys - // - so use a reverse iterator to process the modifier keys first. - for changed_key in (&self.last_keys ^ &self.current_keys).into_iter().rev() { - if self.last_keys.contains(&changed_key) { - //In the last key list, but not in current. This is a key release. - self.key_queue.keystroke(changed_key, key_queue::KeyAction::KeyUp); - } else { - //Not in last, so must be in current. This is a key press. - self.key_queue.keystroke(changed_key, key_queue::KeyAction::KeyDown); - } - } - //after processing all the key strokes, check if any keys were pressed that should trigger the notifier callback - //and if so, signal the event to trigger notify processing at the appropriate TPL. - if self.key_queue.peek_notify_key().is_some() { - let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("bad boot services pointer") }; - (boot_services.signal_event)(self.key_notify_event); + if report.len() == 0 { + return; } - //after processing all the key strokes, send updated LED state if required. - self.generate_led_output_report(); - } - //after all key handling is complete for this report, update the last key set to match the current key set. - self.last_keys = self.current_keys.clone(); - } - } - - //helper routine that updates the fields in the given report buffer for the given field (called for each field for - //every LED usage that was discovered in the output report descriptor). - fn build_led_report(&mut self, field: VariableField, report: &mut [u8]) { - let status = field.set_field_value(self.led_state.contains(&field.usage).into(), report); - if status.is_err() { - debugln!(DEBUG_WARN, "keyobard::build_led_report: failed to set field value: {:?}", status); + if let Some(report_data) = self.input_reports.get(&report_id).cloned() { + if report.len() != report_data.report_size { + return; + } + + //reset currently active keys to empty set. + self.current_keys.clear(); + + // hand the report data to the handler for each relevant field for field-specific processing. + for field in report_data.relevant_variable_fields { + (field.report_handler)(self, field.field, report); + } + + for field in report_data.relevant_array_fields { + (field.report_handler)(self, field.field, report); + } + + //check if any key state has changed. + if self.last_keys != self.current_keys { + // process keys that are not in both sets: that is the set of keys that have changed. + // XOR on the sets yields a set of keys that are in either last or current keys, but not both. + + // Modifier keys need to be processed first so that normal key processing includes modifiers that showed up in + // the same report. The key sets are sorted by Usage, and modifier keys all have higher usages than normal keys + // - so use a reverse iterator to process the modifier keys first. + for changed_key in (&self.last_keys ^ &self.current_keys).into_iter().rev() { + if self.last_keys.contains(&changed_key) { + //In the last key list, but not in current. This is a key release. + self.key_queue.keystroke(changed_key, key_queue::KeyAction::KeyUp); + } else { + //Not in last, so must be in current. This is a key press. + self.key_queue.keystroke(changed_key, key_queue::KeyAction::KeyDown); + } + } + //after processing all the key strokes, check if any keys were pressed that should trigger the notifier callback + //and if so, signal the event to trigger notify processing at the appropriate TPL. + if self.key_queue.peek_notify_key().is_some() { + let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("bad boot services pointer") }; + (boot_services.signal_event)(self.key_notify_event); + } + + //after processing all the key strokes, send updated LED state if required. + self.generate_led_output_report(); + } + //after all key handling is complete for this report, update the last key set to match the current key set. + self.last_keys = self.current_keys.clone(); + } } - } - - //generates and sends an output report for each output report in the report descriptor that indicated an LED usage - //for an LED supported by this driver. - pub fn generate_led_output_report(&mut self) { - let hid_io = unsafe { self.hid_io.as_mut().expect("bad hidio pointer") }; - let current_leds: BTreeSet = self.key_queue.get_active_leds().iter().cloned().collect(); - if current_leds != self.led_state { - self.led_state = current_leds; - for output_builder in self.output_builders.clone() { - let mut report_buffer = vec![0u8; output_builder.report_size]; - for field_builder in &output_builder.relevant_variable_fields { - (field_builder.field_builder)(self, field_builder.field.clone(), report_buffer.as_mut_slice()); + + //helper routine that updates the fields in the given report buffer for the given field (called for each field for + //every LED usage that was discovered in the output report descriptor). + fn build_led_report(&mut self, field: VariableField, report: &mut [u8]) { + let status = field.set_field_value(self.led_state.contains(&field.usage).into(), report); + if status.is_err() { + debugln!(DEBUG_WARN, "keyobard::build_led_report: failed to set field value: {:?}", status); } - let report_id: u32 = output_builder.report_id.unwrap_or(0.into()).into(); - let status = (hid_io.set_report)( - self.hid_io, - report_id as u8, - hid_io::protocol::HidReportType::OutputReport, - report_buffer.len(), - report_buffer.as_mut_ptr() as *mut c_void, - ); - if status.is_error() { - debugln!(DEBUG_WARN, "keyboard::generate_led_report: Set Report failed: {:?}", status); + } + + //generates and sends an output report for each output report in the report descriptor that indicated an LED usage + //for an LED supported by this driver. + pub fn generate_led_output_report(&mut self) { + let hid_io = unsafe { self.hid_io.as_mut().expect("bad hidio pointer") }; + let current_leds: BTreeSet = self.key_queue.get_active_leds().iter().cloned().collect(); + if current_leds != self.led_state { + self.led_state = current_leds; + for output_builder in self.output_builders.clone() { + let mut report_buffer = vec![0u8; output_builder.report_size]; + for field_builder in &output_builder.relevant_variable_fields { + (field_builder.field_builder)(self, field_builder.field.clone(), report_buffer.as_mut_slice()); + } + let report_id: u32 = output_builder.report_id.unwrap_or(0.into()).into(); + let status = (hid_io.set_report)( + self.hid_io, + report_id as u8, + hid_io::protocol::HidReportType::OutputReport, + report_buffer.len(), + report_buffer.as_mut_ptr() as *mut c_void, + ); + if status.is_error() { + debugln!(DEBUG_WARN, "keyboard::generate_led_report: Set Report failed: {:?}", status); + } + } } - } } - } - // indicates whether this keyboard handler has been initialized with a layout. - pub(crate) fn is_layout_installed(&self) -> bool { - self.key_queue.get_layout().is_some() - } + // indicates whether this keyboard handler has been initialized with a layout. + pub(crate) fn is_layout_installed(&self) -> bool { + self.key_queue.get_layout().is_some() + } - // sets the layout to be used by this keyboard handler. - pub(crate) fn set_layout(&mut self, new_layout: Option) { - self.key_queue.set_layout(new_layout); - } + // sets the layout to be used by this keyboard handler. + pub(crate) fn set_layout(&mut self, new_layout: Option) { + self.key_queue.set_layout(new_layout); + } } /// Initializes a keyboard handler on the given `controller` to handle reports described by `descriptor`. @@ -544,491 +544,496 @@ impl KeyboardHandler { /// Otherwise, a [`KeyboardContext`] that can be used to interact with this handler is returned. See [`KeyboardContext`] /// documentation for constraints on interactions with it. pub fn initialize( - controller: efi::Handle, - descriptor: &ReportDescriptor, - hid_context_ptr: *mut HidContext, + controller: efi::Handle, + descriptor: &ReportDescriptor, + hid_context_ptr: *mut HidContext, ) -> Result<*mut KeyboardContext, efi::Status> { - let handler = KeyboardHandler::process_descriptor(descriptor)?; + let handler = KeyboardHandler::process_descriptor(descriptor)?; - let context = handler.install_keyboard_interfaces(controller, hid_context_ptr)?; + let context = handler.install_keyboard_interfaces(controller, hid_context_ptr)?; - if let Err(err) = initialize_keyboard_layout(context) { - let _ = deinitialize(context); - return Err(err); - } + if let Err(err) = initialize_keyboard_layout(context) { + let _ = deinitialize(context); + return Err(err); + } - let hid_context = unsafe { hid_context_ptr.as_mut().expect("[keyboard::initialize]: bad hid context pointer") }; + let hid_context = unsafe { hid_context_ptr.as_mut().expect("[keyboard::initialize]: bad hid context pointer") }; - hid_context.keyboard_context = context; + hid_context.keyboard_context = context; - Ok(context) + Ok(context) } /// De-initializes a keyboard handler described by `context` on the given `controller`. pub fn deinitialize(context: *mut KeyboardContext) -> Result<(), efi::Status> { - if context.is_null() { - return Err(efi::Status::NOT_STARTED); - } - KeyboardHandler::uninstall_keyboard_interfaces(context) + if context.is_null() { + return Err(efi::Status::NOT_STARTED); + } + KeyboardHandler::uninstall_keyboard_interfaces(context) } /// Attempt to retrieve a *mut HidContext for the given controller by locating the simple text input interfaces /// associated with the controller (if any) and deriving a PointerContext from it (which contains a pointer to the /// HidContext). pub fn attempt_to_retrieve_hid_context( - controller: efi::Handle, - driver_binding: &driver_binding::Protocol, + controller: efi::Handle, + driver_binding: &driver_binding::Protocol, ) -> Result<*mut HidContext, efi::Status> { - // retrieve a reference to boot services. - // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. - // Caller should have ensured this, so just expect on failure. - let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; - - let mut simple_text_in_ptr: *mut protocols::simple_text_input::Protocol = core::ptr::null_mut(); - let status = (boot_services.open_protocol)( - controller, - &protocols::simple_text_input::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::addr_of_mut!(simple_text_in_ptr) as *mut *mut c_void, - driver_binding.driver_binding_handle, - controller, - system::OPEN_PROTOCOL_GET_PROTOCOL, - ); - - match status { - efi::Status::SUCCESS => { - // retrieve a reference to the pointer context. - // Safety: `this` must point to an instance of simple_text_in that is contained in a KeyboardContext struct. - // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. - let context_ptr = unsafe { (simple_text_in_ptr as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in)) } - as *mut KeyboardContext; - return Ok(unsafe { (*context_ptr).hid_context }); + // retrieve a reference to boot services. + // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + // Caller should have ensured this, so just expect on failure. + let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; + + let mut simple_text_in_ptr: *mut protocols::simple_text_input::Protocol = core::ptr::null_mut(); + let status = (boot_services.open_protocol)( + controller, + &protocols::simple_text_input::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::addr_of_mut!(simple_text_in_ptr) as *mut *mut c_void, + driver_binding.driver_binding_handle, + controller, + system::OPEN_PROTOCOL_GET_PROTOCOL, + ); + + match status { + efi::Status::SUCCESS => { + // retrieve a reference to the pointer context. + // Safety: `this` must point to an instance of simple_text_in that is contained in a KeyboardContext struct. + // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. + let context_ptr = + unsafe { (simple_text_in_ptr as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in)) } + as *mut KeyboardContext; + return Ok(unsafe { (*context_ptr).hid_context }); + } + err => return Err(err), } - err => return Err(err), - } } // Event handler function for the wait_for_key_event that is part of the simple text input interfaces. The same handler // is used for both simple_text_in and simple_text_in_ex. extern "efiapi" fn wait_for_key(event: efi::Event, context: *mut c_void) { - // retrieve a reference to the - let keyboard_context = unsafe { (context as *mut KeyboardContext).as_mut().expect("Invalid context pointer") }; - - // retrieve a reference to boot services. - // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. - // Caller should have ensured this, so just expect on failure. - let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; - - let old_tpl = (boot_services.raise_tpl)(system::TPL_NOTIFY); - - loop { - if let Some(key_data) = keyboard_context.handler.key_queue.peek_key() { - //skip partials - if key_data.key.unicode_char == 0 && key_data.key.scan_code == 0 { - // consume (and ignore) the partial stroke. - let _ = keyboard_context.handler.key_queue.pop_key(); - continue; - } - //live non-partial key at front of queue; so signal event. - (boot_services.signal_event)(event); + // retrieve a reference to the + let keyboard_context = unsafe { (context as *mut KeyboardContext).as_mut().expect("Invalid context pointer") }; + + // retrieve a reference to boot services. + // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + // Caller should have ensured this, so just expect on failure. + let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; + + let old_tpl = (boot_services.raise_tpl)(system::TPL_NOTIFY); + + loop { + if let Some(key_data) = keyboard_context.handler.key_queue.peek_key() { + //skip partials + if key_data.key.unicode_char == 0 && key_data.key.scan_code == 0 { + // consume (and ignore) the partial stroke. + let _ = keyboard_context.handler.key_queue.pop_key(); + continue; + } + //live non-partial key at front of queue; so signal event. + (boot_services.signal_event)(event); + } + break; } - break; - } - (boot_services.restore_tpl)(old_tpl); + (boot_services.restore_tpl)(old_tpl); } // Event callback function for handling registered key notifications. Iterates over the queue of keys to be notified, // and invokes the registered callback function for each of those keys. extern "efiapi" fn process_key_notifies(_event: efi::Event, context: *mut c_void) { - let keyboard_context = unsafe { (context as *mut KeyboardContext).as_mut().expect("Invalid context pointer") }; + let keyboard_context = unsafe { (context as *mut KeyboardContext).as_mut().expect("Invalid context pointer") }; - // retrieve a reference to boot services. - // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. - // Caller should have ensured this, so just expect on failure. - let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; - - // loop through all the keys in the notification function queue and invoke the associated callback if found - loop { - //Safety: access to the key_queue needs to be at TPL_NOTIFY to ensure mutual exclusion, but this routine runs at - //TPL_CALLBACK. So raise TPL to TPL_NOTIFY for the queue pop and callback function search. - let old_tpl = (boot_services.raise_tpl)(system::TPL_NOTIFY); + // retrieve a reference to boot services. + // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + // Caller should have ensured this, so just expect on failure. + let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; - let (key, callback) = match keyboard_context.handler.key_queue.pop_notifiy_key() { - Some(key) => { - let mut result = (None, None); - for (registered_key, callback) in keyboard_context.handler.notification_callbacks.values() { - if OrdKeyData(key).matches_registered_key(registered_key) { - result = (Some(key), Some(callback)); - break; - } + // loop through all the keys in the notification function queue and invoke the associated callback if found + loop { + //Safety: access to the key_queue needs to be at TPL_NOTIFY to ensure mutual exclusion, but this routine runs at + //TPL_CALLBACK. So raise TPL to TPL_NOTIFY for the queue pop and callback function search. + let old_tpl = (boot_services.raise_tpl)(system::TPL_NOTIFY); + + let (key, callback) = match keyboard_context.handler.key_queue.pop_notifiy_key() { + Some(key) => { + let mut result = (None, None); + for (registered_key, callback) in keyboard_context.handler.notification_callbacks.values() { + if OrdKeyData(key).matches_registered_key(registered_key) { + result = (Some(key), Some(callback)); + break; + } + } + result + } + None => (None, None), + }; + //restore TPL back to TPL_CALLBACK before actually invoking the callback. + (boot_services.restore_tpl)(old_tpl); + + //Invoke the callback, if found. + //Safety: this assumes that a caller doesn't "unregister" a callback and render the pointer invalid at TPL_NOTIFY + //between the search above that returns the callback pointer and the actual callback invocation here. This is a + //spec requirement (see UEFI spec 2.10 table 7.3). + if let (Some(mut key), Some(callback)) = (key, callback) { + let key_data_ptr = &mut key as *mut KeyData; + let _status = callback(key_data_ptr); + } else { + return; } - result - } - None => (None, None), - }; - //restore TPL back to TPL_CALLBACK before actually invoking the callback. - (boot_services.restore_tpl)(old_tpl); - - //Invoke the callback, if found. - //Safety: this assumes that a caller doesn't "unregister" a callback and render the pointer invalid at TPL_NOTIFY - //between the search above that returns the callback pointer and the actual callback invocation here. This is a - //spec requirement (see UEFI spec 2.10 table 7.3). - if let (Some(mut key), Some(callback)) = (key, callback) { - let key_data_ptr = &mut key as *mut KeyData; - let _status = callback(key_data_ptr); - } else { - return; } - } } // resets the keyboard state - part of the simple_text_in protocol interface. extern "efiapi" fn simple_text_in_reset( - this: *mut protocols::simple_text_input::Protocol, - _extended_verification: efi::Boolean, + this: *mut protocols::simple_text_input::Protocol, + _extended_verification: efi::Boolean, ) -> efi::Status { - if this.is_null() { - return efi::Status::INVALID_PARAMETER; - } - // retrieve a reference to the keyboard context. - // Safety: `this` must point to an instance of simple_text)input that is contained in a KeyboardContext struct. - // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. - let context_ptr = - unsafe { (this as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in)) } as *mut KeyboardContext; - - let keyboard_context = unsafe { context_ptr.as_mut().expect("Invalid context pointer") }; - - keyboard_context.handler.last_keys.clear(); - keyboard_context.handler.current_keys.clear(); - keyboard_context.handler.key_queue.reset(); - - efi::Status::SUCCESS + if this.is_null() { + return efi::Status::INVALID_PARAMETER; + } + // retrieve a reference to the keyboard context. + // Safety: `this` must point to an instance of simple_text)input that is contained in a KeyboardContext struct. + // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. + let context_ptr = + unsafe { (this as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in)) } as *mut KeyboardContext; + + let keyboard_context = unsafe { context_ptr.as_mut().expect("Invalid context pointer") }; + + keyboard_context.handler.last_keys.clear(); + keyboard_context.handler.current_keys.clear(); + keyboard_context.handler.key_queue.reset(); + + efi::Status::SUCCESS } // reads a key stroke - part of the simple_text_in protocol interface. extern "efiapi" fn simple_text_in_read_key_stroke( - this: *mut protocols::simple_text_input::Protocol, - key: *mut protocols::simple_text_input::InputKey, + this: *mut protocols::simple_text_input::Protocol, + key: *mut protocols::simple_text_input::InputKey, ) -> efi::Status { - if this.is_null() || key.is_null() { - return efi::Status::INVALID_PARAMETER; - } - // retrieve a reference to the keyboard context. - // Safety: `this` must point to an instance of simple_text)input that is contained in a KeyboardContext struct. - // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. - let context_ptr = - unsafe { (this as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in)) } as *mut KeyboardContext; - - let keyboard_context = unsafe { context_ptr.as_mut().expect("Invalid context pointer") }; - - loop { - if let Some(mut key_data) = keyboard_context.handler.key_queue.pop_key() { - // skip partials - if key_data.key.unicode_char == 0 && key_data.key.scan_code == 0 { - continue; - } - //translate ctrl-alpha to corresponding control value. ctrl-a = 0x0001, ctrl-z = 0x001A - if (key_data.key_state.key_shift_state & (LEFT_SHIFT_PRESSED | RIGHT_SHIFT_PRESSED)) != 0 { - if key_data.key.unicode_char >= 0x0061 && key_data.key.unicode_char <= 0x007a { - //'a' to 'z' - key_data.key.unicode_char = (key_data.key.unicode_char - 0x0061) + 1; - } - if key_data.key.unicode_char >= 0x0041 && key_data.key.unicode_char <= 0x005a { - //'A' to 'Z' - key_data.key.unicode_char = (key_data.key.unicode_char - 0x0041) + 1; + if this.is_null() || key.is_null() { + return efi::Status::INVALID_PARAMETER; + } + // retrieve a reference to the keyboard context. + // Safety: `this` must point to an instance of simple_text)input that is contained in a KeyboardContext struct. + // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. + let context_ptr = + unsafe { (this as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in)) } as *mut KeyboardContext; + + let keyboard_context = unsafe { context_ptr.as_mut().expect("Invalid context pointer") }; + + loop { + if let Some(mut key_data) = keyboard_context.handler.key_queue.pop_key() { + // skip partials + if key_data.key.unicode_char == 0 && key_data.key.scan_code == 0 { + continue; + } + //translate ctrl-alpha to corresponding control value. ctrl-a = 0x0001, ctrl-z = 0x001A + if (key_data.key_state.key_shift_state & (LEFT_SHIFT_PRESSED | RIGHT_SHIFT_PRESSED)) != 0 { + if key_data.key.unicode_char >= 0x0061 && key_data.key.unicode_char <= 0x007a { + //'a' to 'z' + key_data.key.unicode_char = (key_data.key.unicode_char - 0x0061) + 1; + } + if key_data.key.unicode_char >= 0x0041 && key_data.key.unicode_char <= 0x005a { + //'A' to 'Z' + key_data.key.unicode_char = (key_data.key.unicode_char - 0x0041) + 1; + } + } + unsafe { key.write(key_data.key) } + return efi::Status::SUCCESS; + } else { + return efi::Status::NOT_READY; } - } - unsafe { key.write(key_data.key) } - return efi::Status::SUCCESS; - } else { - return efi::Status::NOT_READY; } - } } // resets the keyboard state - part of the simple_text_in_ex protocol interface. extern "efiapi" fn simple_text_in_ex_reset( - this: *mut protocols::simple_text_input_ex::Protocol, - _extended_verification: efi::Boolean, + this: *mut protocols::simple_text_input_ex::Protocol, + _extended_verification: efi::Boolean, ) -> efi::Status { - if this.is_null() { - return efi::Status::INVALID_PARAMETER; - } - // retrieve a reference to the keyboard context. - // Safety: `this` must point to an instance of simple_text)input that is contained in a KeyboardContext struct. - // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. - let context_ptr = - unsafe { (this as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in_ex)) } as *mut KeyboardContext; - - let keyboard_context = unsafe { context_ptr.as_mut().expect("Invalid context pointer") }; - - keyboard_context.handler.last_keys.clear(); - keyboard_context.handler.current_keys.clear(); - keyboard_context.handler.key_queue.reset(); - - efi::Status::SUCCESS + if this.is_null() { + return efi::Status::INVALID_PARAMETER; + } + // retrieve a reference to the keyboard context. + // Safety: `this` must point to an instance of simple_text)input that is contained in a KeyboardContext struct. + // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. + let context_ptr = + unsafe { (this as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in_ex)) } as *mut KeyboardContext; + + let keyboard_context = unsafe { context_ptr.as_mut().expect("Invalid context pointer") }; + + keyboard_context.handler.last_keys.clear(); + keyboard_context.handler.current_keys.clear(); + keyboard_context.handler.key_queue.reset(); + + efi::Status::SUCCESS } // reads a key stroke - part of the simple_text_in_ex protocol interface. extern "efiapi" fn simple_text_in_ex_read_key_stroke( - this: *mut protocols::simple_text_input_ex::Protocol, - key_data: *mut protocols::simple_text_input_ex::KeyData, + this: *mut protocols::simple_text_input_ex::Protocol, + key_data: *mut protocols::simple_text_input_ex::KeyData, ) -> efi::Status { - if this.is_null() || key_data.is_null() { - return efi::Status::INVALID_PARAMETER; - } - // retrieve a reference to the keyboard context. - // Safety: `this` must point to an instance of simple_text)input that is contained in a KeyboardContext struct. - // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. - let context_ptr = - unsafe { (this as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in_ex)) } as *mut KeyboardContext; - - let keyboard_context = unsafe { context_ptr.as_mut().expect("Invalid context pointer") }; - - if let Some(key) = keyboard_context.handler.key_queue.pop_key() { - unsafe { key_data.write(key) }; - efi::Status::SUCCESS - } else { - let mut key: KeyData = Default::default(); - key.key_state = keyboard_context.handler.key_queue.init_key_state(); - unsafe { key_data.write(key) }; - efi::Status::NOT_READY - } + if this.is_null() || key_data.is_null() { + return efi::Status::INVALID_PARAMETER; + } + // retrieve a reference to the keyboard context. + // Safety: `this` must point to an instance of simple_text)input that is contained in a KeyboardContext struct. + // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. + let context_ptr = + unsafe { (this as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in_ex)) } as *mut KeyboardContext; + + let keyboard_context = unsafe { context_ptr.as_mut().expect("Invalid context pointer") }; + + if let Some(key) = keyboard_context.handler.key_queue.pop_key() { + unsafe { key_data.write(key) }; + efi::Status::SUCCESS + } else { + let mut key: KeyData = Default::default(); + key.key_state = keyboard_context.handler.key_queue.init_key_state(); + unsafe { key_data.write(key) }; + efi::Status::NOT_READY + } } // sets the keyboard state - part of the simple_text_in_ex protocol interface. extern "efiapi" fn simple_text_in_ex_set_state( - this: *mut protocols::simple_text_input_ex::Protocol, - key_toggle_state: *mut protocols::simple_text_input_ex::KeyToggleState, + this: *mut protocols::simple_text_input_ex::Protocol, + key_toggle_state: *mut protocols::simple_text_input_ex::KeyToggleState, ) -> efi::Status { - if this.is_null() || key_toggle_state.is_null() { - return efi::Status::INVALID_PARAMETER; - } - // retrieve a reference to the keyboard context. - // Safety: `this` must point to an instance of simple_text)input that is contained in a KeyboardContext struct. - // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. - let context_ptr = - unsafe { (this as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in_ex)) } as *mut KeyboardContext; + if this.is_null() || key_toggle_state.is_null() { + return efi::Status::INVALID_PARAMETER; + } + // retrieve a reference to the keyboard context. + // Safety: `this` must point to an instance of simple_text)input that is contained in a KeyboardContext struct. + // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. + let context_ptr = + unsafe { (this as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in_ex)) } as *mut KeyboardContext; - let keyboard_context = unsafe { context_ptr.as_mut().expect("Invalid context pointer") }; + let keyboard_context = unsafe { context_ptr.as_mut().expect("Invalid context pointer") }; - let key_toggle_state = unsafe { key_toggle_state.as_mut().expect("Invalid key toggle state pointer") }; + let key_toggle_state = unsafe { key_toggle_state.as_mut().expect("Invalid key toggle state pointer") }; - keyboard_context.handler.key_queue.set_key_toggle_state(*key_toggle_state); + keyboard_context.handler.key_queue.set_key_toggle_state(*key_toggle_state); - keyboard_context.handler.generate_led_output_report(); + keyboard_context.handler.generate_led_output_report(); - efi::Status::SUCCESS + efi::Status::SUCCESS } // registers a key notification callback function - part of the simple_text_in_ex protocol interface. extern "efiapi" fn simple_text_in_ex_register_key_notify( - this: *mut protocols::simple_text_input_ex::Protocol, - key_data_ptr: *mut protocols::simple_text_input_ex::KeyData, - key_notification_function: protocols::simple_text_input_ex::KeyNotifyFunction, - notify_handle: *mut *mut c_void, + this: *mut protocols::simple_text_input_ex::Protocol, + key_data_ptr: *mut protocols::simple_text_input_ex::KeyData, + key_notification_function: protocols::simple_text_input_ex::KeyNotifyFunction, + notify_handle: *mut *mut c_void, ) -> efi::Status { - //TODO: should check key_notification_function against NULL, but the RUST way to do that would be to declare it - //as Option - which would require a change to r_efi to do properly. - if this.is_null() || key_data_ptr.is_null() || notify_handle.is_null() { - return efi::Status::INVALID_PARAMETER; - } - - // retrieve a reference to the keyboard context. - // Safety: `this` must point to an instance of simple_text)input that is contained in a KeyboardContext struct. - // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. - let context_ptr = - unsafe { (this as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in_ex)) } as *mut KeyboardContext; - - let keyboard_context = unsafe { context_ptr.as_mut().expect("Invalid context pointer") }; - - let key_data = OrdKeyData(*unsafe { key_data_ptr.as_mut().expect("Bad key_data_ptr") }); - - for (handle, entry) in &keyboard_context.handler.notification_callbacks { - if entry.0 == key_data && entry.1 == key_notification_function { - //if callback already exists, just return current handle - unsafe { notify_handle.write(*handle as *mut c_void) }; - return efi::Status::SUCCESS; + //TODO: should check key_notification_function against NULL, but the RUST way to do that would be to declare it + //as Option - which would require a change to r_efi to do properly. + if this.is_null() || key_data_ptr.is_null() || notify_handle.is_null() { + return efi::Status::INVALID_PARAMETER; } - } - // key_data/callback combo doesn't already exist; create a new registration for it. - keyboard_context.handler.next_notify_handle += 1; + // retrieve a reference to the keyboard context. + // Safety: `this` must point to an instance of simple_text)input that is contained in a KeyboardContext struct. + // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. + let context_ptr = + unsafe { (this as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in_ex)) } as *mut KeyboardContext; + + let keyboard_context = unsafe { context_ptr.as_mut().expect("Invalid context pointer") }; - keyboard_context - .handler - .notification_callbacks - .insert(keyboard_context.handler.next_notify_handle, (key_data.clone(), key_notification_function)); + let key_data = OrdKeyData(*unsafe { key_data_ptr.as_mut().expect("Bad key_data_ptr") }); + + for (handle, entry) in &keyboard_context.handler.notification_callbacks { + if entry.0 == key_data && entry.1 == key_notification_function { + //if callback already exists, just return current handle + unsafe { notify_handle.write(*handle as *mut c_void) }; + return efi::Status::SUCCESS; + } + } - keyboard_context.handler.key_queue.add_notify_key(key_data); + // key_data/callback combo doesn't already exist; create a new registration for it. + keyboard_context.handler.next_notify_handle += 1; - efi::Status::SUCCESS + keyboard_context + .handler + .notification_callbacks + .insert(keyboard_context.handler.next_notify_handle, (key_data.clone(), key_notification_function)); + + keyboard_context.handler.key_queue.add_notify_key(key_data); + + efi::Status::SUCCESS } // unregisters a key notification callback function - part of the simple_text_in_ex protocol interface. extern "efiapi" fn simple_text_in_ex_unregister_key_notify( - this: *mut protocols::simple_text_input_ex::Protocol, - notification_handle: *mut c_void, + this: *mut protocols::simple_text_input_ex::Protocol, + notification_handle: *mut c_void, ) -> efi::Status { - if this.is_null() || notification_handle as usize == 0 { - return efi::Status::INVALID_PARAMETER; - } + if this.is_null() || notification_handle as usize == 0 { + return efi::Status::INVALID_PARAMETER; + } - // retrieve a reference to the keyboard context. - // Safety: `this` must point to an instance of simple_text)input that is contained in a KeyboardContext struct. - // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. - let context_ptr = - unsafe { (this as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in_ex)) } as *mut KeyboardContext; + // retrieve a reference to the keyboard context. + // Safety: `this` must point to an instance of simple_text)input that is contained in a KeyboardContext struct. + // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. + let context_ptr = + unsafe { (this as *mut u8).sub(offset_of!(KeyboardContext, simple_text_in_ex)) } as *mut KeyboardContext; - let keyboard_context = unsafe { context_ptr.as_mut().expect("Invalid context pointer") }; + let keyboard_context = unsafe { context_ptr.as_mut().expect("Invalid context pointer") }; - let handle = notification_handle as usize; + let handle = notification_handle as usize; - let entry = keyboard_context.handler.notification_callbacks.remove(&handle); - let Some(entry) = entry else { - return efi::Status::INVALID_PARAMETER; - }; + let entry = keyboard_context.handler.notification_callbacks.remove(&handle); + let Some(entry) = entry else { + return efi::Status::INVALID_PARAMETER; + }; - let other_handlers_exist = - keyboard_context.handler.notification_callbacks.values().any(|(key, _callback)| *key == entry.0); + let other_handlers_exist = + keyboard_context.handler.notification_callbacks.values().any(|(key, _callback)| *key == entry.0); - if !other_handlers_exist { - keyboard_context.handler.key_queue.remove_notify_key(&entry.0); - } + if !other_handlers_exist { + keyboard_context.handler.key_queue.remove_notify_key(&entry.0); + } - efi::Status::SUCCESS + efi::Status::SUCCESS } // Initializes keyboard layout support. Creates an event to fire a callback when a new keyboard layout is installed // into HII database, and then installs a default keyboard layout if one is not already present. pub(crate) fn initialize_keyboard_layout(context_ptr: *mut KeyboardContext) -> Result<(), efi::Status> { - // retrieve a reference to boot services. - // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. - // Caller should have ensured this, so just expect on failure. - let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; - - //create layout update event. - let mut layout_change_event: efi::Event = core::ptr::null_mut(); - let status = (boot_services.create_event_ex)( - system::EVT_NOTIFY_SIGNAL, - system::TPL_NOTIFY, - Some(on_layout_update), - context_ptr as *mut c_void, - &protocols::hii_database::SET_KEYBOARD_LAYOUT_EVENT_GUID, - core::ptr::addr_of_mut!(layout_change_event), - ); - if status.is_error() { - Err(status)?; - } - - //signal event to pick up any existing layout. - let status = (boot_services.signal_event)(layout_change_event); - if status.is_error() { - Err(status)?; - } - - //if no layout installed, install the default layout. - if !unsafe { (*context_ptr).handler.is_layout_installed() } { - install_default_layout(boot_services)?; - } - - Ok(()) + // retrieve a reference to boot services. + // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + // Caller should have ensured this, so just expect on failure. + let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; + + //create layout update event. + let mut layout_change_event: efi::Event = core::ptr::null_mut(); + let status = (boot_services.create_event_ex)( + system::EVT_NOTIFY_SIGNAL, + system::TPL_NOTIFY, + Some(on_layout_update), + context_ptr as *mut c_void, + &protocols::hii_database::SET_KEYBOARD_LAYOUT_EVENT_GUID, + core::ptr::addr_of_mut!(layout_change_event), + ); + if status.is_error() { + Err(status)?; + } + + //signal event to pick up any existing layout. + let status = (boot_services.signal_event)(layout_change_event); + if status.is_error() { + Err(status)?; + } + + //if no layout installed, install the default layout. + if !unsafe { (*context_ptr).handler.is_layout_installed() } { + install_default_layout(boot_services)?; + } + + Ok(()) } // Installs a default keyboard layout into the HII database. pub(crate) fn install_default_layout(boot_services: &mut system::BootServices) -> Result<(), efi::Status> { - let mut hii_database_protocol_ptr: *mut protocols::hii_database::Protocol = core::ptr::null_mut(); + let mut hii_database_protocol_ptr: *mut protocols::hii_database::Protocol = core::ptr::null_mut(); - let status = (boot_services.locate_protocol)( - &protocols::hii_database::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::null_mut(), - core::ptr::addr_of_mut!(hii_database_protocol_ptr) as *mut *mut c_void, - ); + let status = (boot_services.locate_protocol)( + &protocols::hii_database::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::null_mut(), + core::ptr::addr_of_mut!(hii_database_protocol_ptr) as *mut *mut c_void, + ); - if status.is_error() { - debugln!( + if status.is_error() { + debugln!( DEBUG_ERROR, "keyboard::install_default_layout: Could not locate hii_database protocol to install keyboard layout: {:x?}", status ); - Err(status)?; - } - - let hii_database_protocol = - unsafe { hii_database_protocol_ptr.as_mut().expect("Bad pointer returned from successful locate protocol.") }; - - let mut hii_handle: hii::Handle = core::ptr::null_mut(); - let status = (hii_database_protocol.new_package_list)( - hii_database_protocol_ptr, - hii_keyboard_layout::get_default_keyboard_pkg_list_buffer().as_ptr() as *const hii::PackageListHeader, - core::ptr::null_mut(), - core::ptr::addr_of_mut!(hii_handle), - ); - - if status.is_error() { - debugln!(DEBUG_ERROR, "keyboard::install_default_layout: Failed to install keyboard layout package: {:x?}", status); - Err(status)?; - } - - let status = (hii_database_protocol.set_keyboard_layout)( - hii_database_protocol_ptr, - &hii_keyboard_layout::DEFAULT_KEYBOARD_LAYOUT_GUID as *const efi::Guid as *mut efi::Guid, - ); - - if status.is_error() { - debugln!(DEBUG_ERROR, "keyboard::install_default_layout: Failed to set keyboard layout: {:x?}", status); - Err(status)?; - } - - Ok(()) + Err(status)?; + } + + let hii_database_protocol = + unsafe { hii_database_protocol_ptr.as_mut().expect("Bad pointer returned from successful locate protocol.") }; + + let mut hii_handle: hii::Handle = core::ptr::null_mut(); + let status = (hii_database_protocol.new_package_list)( + hii_database_protocol_ptr, + hii_keyboard_layout::get_default_keyboard_pkg_list_buffer().as_ptr() as *const hii::PackageListHeader, + core::ptr::null_mut(), + core::ptr::addr_of_mut!(hii_handle), + ); + + if status.is_error() { + debugln!( + DEBUG_ERROR, + "keyboard::install_default_layout: Failed to install keyboard layout package: {:x?}", + status + ); + Err(status)?; + } + + let status = (hii_database_protocol.set_keyboard_layout)( + hii_database_protocol_ptr, + &hii_keyboard_layout::DEFAULT_KEYBOARD_LAYOUT_GUID as *const efi::Guid as *mut efi::Guid, + ); + + if status.is_error() { + debugln!(DEBUG_ERROR, "keyboard::install_default_layout: Failed to set keyboard layout: {:x?}", status); + Err(status)?; + } + + Ok(()) } // Event callback that is fired on keyboard layout update event in HII. extern "efiapi" fn on_layout_update(_event: efi::Event, context: *mut c_void) { - let keyboard_context = unsafe { (context as *mut KeyboardContext).as_mut().expect("Bad context pointer") }; - - // retrieve a reference to boot services. - // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. - // Caller should have ensured this, so just expect on failure. - let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; - - let mut hii_database_protocol_ptr: *mut protocols::hii_database::Protocol = core::ptr::null_mut(); - let status = (boot_services.locate_protocol)( - &protocols::hii_database::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::null_mut(), - core::ptr::addr_of_mut!(hii_database_protocol_ptr) as *mut *mut c_void, - ); - - if status.is_error() { - //nothing to do if there is no hii protocol. - return; - } - - let hii_database_protocol = - unsafe { hii_database_protocol_ptr.as_mut().expect("Bad pointer returned from successful locate protocol.") }; - - let mut keyboard_layout_buffer = vec![0u8; 4096]; - let mut layout_buffer_len: u16 = keyboard_layout_buffer.len() as u16; - - let status = (hii_database_protocol.get_keyboard_layout)( - hii_database_protocol_ptr, - core::ptr::null_mut(), - &mut layout_buffer_len as *mut u16, - keyboard_layout_buffer.as_mut_ptr() as *mut protocols::hii_database::KeyboardLayout<0>, - ); - - if status.is_error() { - return; - } - - let keyboard_layout = hii_keyboard_layout::keyboard_layout_from_buffer(&keyboard_layout_buffer); - match keyboard_layout { - Ok(keyboard_layout) => { - keyboard_context.handler.set_layout(Some(keyboard_layout)); + let keyboard_context = unsafe { (context as *mut KeyboardContext).as_mut().expect("Bad context pointer") }; + + // retrieve a reference to boot services. + // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + // Caller should have ensured this, so just expect on failure. + let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; + + let mut hii_database_protocol_ptr: *mut protocols::hii_database::Protocol = core::ptr::null_mut(); + let status = (boot_services.locate_protocol)( + &protocols::hii_database::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::null_mut(), + core::ptr::addr_of_mut!(hii_database_protocol_ptr) as *mut *mut c_void, + ); + + if status.is_error() { + //nothing to do if there is no hii protocol. + return; + } + + let hii_database_protocol = + unsafe { hii_database_protocol_ptr.as_mut().expect("Bad pointer returned from successful locate protocol.") }; + + let mut keyboard_layout_buffer = vec![0u8; 4096]; + let mut layout_buffer_len: u16 = keyboard_layout_buffer.len() as u16; + + let status = (hii_database_protocol.get_keyboard_layout)( + hii_database_protocol_ptr, + core::ptr::null_mut(), + &mut layout_buffer_len as *mut u16, + keyboard_layout_buffer.as_mut_ptr() as *mut protocols::hii_database::KeyboardLayout<0>, + ); + + if status.is_error() { + return; } - Err(_) => { - debugln!(DEBUG_WARN, "keyboard::on_layout_update: Could not parse keyboard layout buffer."); - return; + + let keyboard_layout = hii_keyboard_layout::keyboard_layout_from_buffer(&keyboard_layout_buffer); + match keyboard_layout { + Ok(keyboard_layout) => { + keyboard_context.handler.set_layout(Some(keyboard_layout)); + } + Err(_) => { + debugln!(DEBUG_WARN, "keyboard::on_layout_update: Could not parse keyboard layout buffer."); + return; + } } - } } diff --git a/HidPkg/UefiHidDxe/src/main.rs b/HidPkg/UefiHidDxe/src/main.rs index 7d1eb2ee85..302ebda836 100644 --- a/HidPkg/UefiHidDxe/src/main.rs +++ b/HidPkg/UefiHidDxe/src/main.rs @@ -33,22 +33,22 @@ static mut RUNTIME_SERVICES: *mut system::RuntimeServices = core::ptr::null_mut( #[no_mangle] pub extern "efiapi" fn efi_main(image_handle: efi::Handle, system_table: *const system::SystemTable) -> efi::Status { - // Safety: This block is unsafe because it assumes that system_table and (*system_table).boot_services are correct, - // and because it mutates/accesses the global BOOT_SERVICES static. - unsafe { - BOOT_SERVICES = (*system_table).boot_services; - RUNTIME_SERVICES = (*system_table).runtime_services; - GLOBAL_ALLOCATOR.init(BOOT_SERVICES); - init_debug(BOOT_SERVICES); - } - - let status = initialize_driver_binding(image_handle); - - if status.is_err() { - debugln!(DEBUG_ERROR, "[UefiHidMain]: failed to initialize driver binding.\n"); - } - - efi::Status::SUCCESS + // Safety: This block is unsafe because it assumes that system_table and (*system_table).boot_services are correct, + // and because it mutates/accesses the global BOOT_SERVICES static. + unsafe { + BOOT_SERVICES = (*system_table).boot_services; + RUNTIME_SERVICES = (*system_table).runtime_services; + GLOBAL_ALLOCATOR.init(BOOT_SERVICES); + init_debug(BOOT_SERVICES); + } + + let status = initialize_driver_binding(image_handle); + + if status.is_err() { + debugln!(DEBUG_ERROR, "[UefiHidMain]: failed to initialize driver binding.\n"); + } + + efi::Status::SUCCESS } //Workaround for https://github.com/rust-lang/rust/issues/98254 @@ -58,6 +58,6 @@ pub extern "efiapi" fn __chkstk() {} #[panic_handler] fn panic(info: &PanicInfo) -> ! { - debugln!(DEBUG_ERROR, "Panic: {:?}", info); - loop {} + debugln!(DEBUG_ERROR, "Panic: {:?}", info); + loop {} } diff --git a/HidPkg/UefiHidDxe/src/pointer.rs b/HidPkg/UefiHidDxe/src/pointer.rs index 480aa8b920..cead4923bd 100644 --- a/HidPkg/UefiHidDxe/src/pointer.rs +++ b/HidPkg/UefiHidDxe/src/pointer.rs @@ -8,20 +8,20 @@ //! SPDX-License-Identifier: BSD-2-Clause-Patent //! use alloc::{ - boxed::Box, - collections::{BTreeMap, BTreeSet}, - vec::Vec, + boxed::Box, + collections::{BTreeMap, BTreeSet}, + vec::Vec, }; use core::ffi::c_void; use hidparser::{ - report_data_types::{ReportId, Usage}, - ReportDescriptor, ReportField, VariableField, + report_data_types::{ReportId, Usage}, + ReportDescriptor, ReportField, VariableField, }; use memoffset::{offset_of, raw_field}; use r_efi::{ - efi, - protocols::{absolute_pointer, driver_binding}, - system, + efi, + protocols::{absolute_pointer, driver_binding}, + system, }; use rust_advanced_logger_dxe::{debugln, DEBUG_INFO, DEBUG_WARN}; @@ -42,16 +42,16 @@ const AXIS_RESOLUTION: u64 = 1024; // Maps a given field to a routine that handles input from it. #[derive(Debug, Clone)] struct ReportFieldWithHandler { - field: VariableField, - report_handler: fn(&mut PointerHandler, field: VariableField, report: &[u8]), + field: VariableField, + report_handler: fn(&mut PointerHandler, field: VariableField, report: &[u8]), } // Defines a report and the fields of interest within it. #[derive(Debug, Default, Clone)] struct PointerReportData { - report_id: Option, - report_size: usize, - relevant_fields: Vec, + report_id: Option, + report_size: usize, + relevant_fields: Vec, } /// Context structure used to track data for this pointer device. @@ -59,326 +59,337 @@ struct PointerReportData { /// remain #[repr(C)], and Rust aliasing and concurrency rules must be manually enforced. #[repr(C)] pub struct PointerContext { - absolute_pointer: absolute_pointer::Protocol, - pub handler: PointerHandler, - controller: efi::Handle, - hid_context: *mut HidContext, + absolute_pointer: absolute_pointer::Protocol, + pub handler: PointerHandler, + controller: efi::Handle, + hid_context: *mut HidContext, } /// Tracks all the input reports for this device as well as pointer state. #[derive(Debug, Default)] pub struct PointerHandler { - input_reports: BTreeMap, PointerReportData>, - supported_usages: BTreeSet, - report_id_present: bool, - state_changed: bool, - current_state: absolute_pointer::State, + input_reports: BTreeMap, PointerReportData>, + supported_usages: BTreeSet, + report_id_present: bool, + state_changed: bool, + current_state: absolute_pointer::State, } impl PointerHandler { - // processes a report descriptor and yields a PointerHandler instance if this descriptor describes input - // that can be handled by this PointerHandler. - fn process_descriptor(descriptor: &ReportDescriptor) -> Result { - let mut handler: PointerHandler = Default::default(); - let multiple_reports = descriptor.input_reports.len() > 1; - - for report in &descriptor.input_reports { - let mut report_data = - PointerReportData { report_id: report.report_id, report_size: report.size_in_bits / 8, ..Default::default() }; - - handler.report_id_present = report.report_id.is_some(); - - if multiple_reports && !handler.report_id_present { - //invalid to have None ReportId if multiple reports present. - Err(efi::Status::DEVICE_ERROR)?; - } - - for field in &report.fields { - match field { - ReportField::Variable(field) => { - match field.usage.into() { - GENERIC_DESKTOP_X => { - let field_handler = - ReportFieldWithHandler { field: field.clone(), report_handler: Self::x_axis_handler }; - report_data.relevant_fields.push(field_handler); - handler.supported_usages.insert(field.usage); - //debugln!(DEBUG_INFO, "x-axis field {:#?}", field); - } - GENERIC_DESKTOP_Y => { - let field_handler = - ReportFieldWithHandler { field: field.clone(), report_handler: Self::y_axis_handler }; - report_data.relevant_fields.push(field_handler); - handler.supported_usages.insert(field.usage); - //debugln!(DEBUG_INFO, "y-axis field {:#?}", field); - } - GENERIC_DESKTOP_Z | GENERIC_DESKTOP_WHEEL => { - let field_handler = - ReportFieldWithHandler { field: field.clone(), report_handler: Self::z_axis_handler }; - report_data.relevant_fields.push(field_handler); - handler.supported_usages.insert(field.usage); - //debugln!(DEBUG_INFO, "z-axis field {:#?}", field); - } - BUTTON_MIN..=BUTTON_MAX => { - let field_handler = - ReportFieldWithHandler { field: field.clone(), report_handler: Self::button_handler }; - report_data.relevant_fields.push(field_handler); - handler.supported_usages.insert(field.usage); - //debugln!(DEBUG_INFO, "button field {:#?}", field); - } - _ => (), //other usages irrelevant + // processes a report descriptor and yields a PointerHandler instance if this descriptor describes input + // that can be handled by this PointerHandler. + fn process_descriptor(descriptor: &ReportDescriptor) -> Result { + let mut handler: PointerHandler = Default::default(); + let multiple_reports = descriptor.input_reports.len() > 1; + + for report in &descriptor.input_reports { + let mut report_data = PointerReportData { + report_id: report.report_id, + report_size: report.size_in_bits / 8, + ..Default::default() + }; + + handler.report_id_present = report.report_id.is_some(); + + if multiple_reports && !handler.report_id_present { + //invalid to have None ReportId if multiple reports present. + Err(efi::Status::DEVICE_ERROR)?; } - } - _ => (), // other field types irrelevant - } - } - if report_data.relevant_fields.len() > 0 { - handler.input_reports.insert(report_data.report_id, report_data); - } - } - - if handler.input_reports.len() > 0 { - Ok(handler) - } else { - debugln!(DEBUG_INFO, "No relevant fields for handler: {:#?}", handler); - Err(efi::Status::UNSUPPORTED) - } - } + for field in &report.fields { + match field { + ReportField::Variable(field) => { + match field.usage.into() { + GENERIC_DESKTOP_X => { + let field_handler = ReportFieldWithHandler { + field: field.clone(), + report_handler: Self::x_axis_handler, + }; + report_data.relevant_fields.push(field_handler); + handler.supported_usages.insert(field.usage); + //debugln!(DEBUG_INFO, "x-axis field {:#?}", field); + } + GENERIC_DESKTOP_Y => { + let field_handler = ReportFieldWithHandler { + field: field.clone(), + report_handler: Self::y_axis_handler, + }; + report_data.relevant_fields.push(field_handler); + handler.supported_usages.insert(field.usage); + //debugln!(DEBUG_INFO, "y-axis field {:#?}", field); + } + GENERIC_DESKTOP_Z | GENERIC_DESKTOP_WHEEL => { + let field_handler = ReportFieldWithHandler { + field: field.clone(), + report_handler: Self::z_axis_handler, + }; + report_data.relevant_fields.push(field_handler); + handler.supported_usages.insert(field.usage); + //debugln!(DEBUG_INFO, "z-axis field {:#?}", field); + } + BUTTON_MIN..=BUTTON_MAX => { + let field_handler = ReportFieldWithHandler { + field: field.clone(), + report_handler: Self::button_handler, + }; + report_data.relevant_fields.push(field_handler); + handler.supported_usages.insert(field.usage); + //debugln!(DEBUG_INFO, "button field {:#?}", field); + } + _ => (), //other usages irrelevant + } + } + _ => (), // other field types irrelevant + } + } - // Create PointerContext structure and install Absolute Pointer interface. - fn install_pointer_interfaces( - self, - controller: efi::Handle, - hid_context: *mut HidContext, - ) -> Result<*mut PointerContext, efi::Status> { - // retrieve a reference to boot services. - // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. - // Caller should have ensured this, so just expect on failure. - let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; + if report_data.relevant_fields.len() > 0 { + handler.input_reports.insert(report_data.report_id, report_data); + } + } - // Create pointer context. This context is shared across FFI boundary, so use Box::into_raw. - // After creation, the only way to access the context (including the handler instance) is via a raw pointer. - let context_ptr = Box::into_raw(Box::new(PointerContext { - absolute_pointer: absolute_pointer::Protocol { - reset: absolute_pointer_reset, - get_state: absolute_pointer_get_state, - mode: Box::into_raw(Box::new(self.initialize_mode())), - wait_for_input: core::ptr::null_mut(), - }, - handler: self, - controller: controller, - hid_context: hid_context, - })); - - let context = unsafe { context_ptr.as_mut().expect("freshly boxed context pointer is null.") }; - - // create event for wait_for_input. - let mut wait_for_pointer_input_event: efi::Event = core::ptr::null_mut(); - let status = (boot_services.create_event)( - system::EVT_NOTIFY_WAIT, - system::TPL_NOTIFY, - Some(wait_for_pointer), - context_ptr as *mut c_void, - core::ptr::addr_of_mut!(wait_for_pointer_input_event), - ); - if status.is_error() { - drop(unsafe { Box::from_raw(context_ptr) }); - return Err(status); + if handler.input_reports.len() > 0 { + Ok(handler) + } else { + debugln!(DEBUG_INFO, "No relevant fields for handler: {:#?}", handler); + Err(efi::Status::UNSUPPORTED) + } } - context.absolute_pointer.wait_for_input = wait_for_pointer_input_event; - - // install the absolute_pointer protocol. - let mut controller = controller; - let absolute_pointer_ptr = raw_field!(context_ptr, PointerContext, absolute_pointer); - let status = (boot_services.install_protocol_interface)( - core::ptr::addr_of_mut!(controller), - &absolute_pointer::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - efi::NATIVE_INTERFACE, - absolute_pointer_ptr as *mut c_void, - ); - if status.is_error() { - let _ = deinitialize(context_ptr); - return Err(status); + // Create PointerContext structure and install Absolute Pointer interface. + fn install_pointer_interfaces( + self, + controller: efi::Handle, + hid_context: *mut HidContext, + ) -> Result<*mut PointerContext, efi::Status> { + // retrieve a reference to boot services. + // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + // Caller should have ensured this, so just expect on failure. + let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; + + // Create pointer context. This context is shared across FFI boundary, so use Box::into_raw. + // After creation, the only way to access the context (including the handler instance) is via a raw pointer. + let context_ptr = Box::into_raw(Box::new(PointerContext { + absolute_pointer: absolute_pointer::Protocol { + reset: absolute_pointer_reset, + get_state: absolute_pointer_get_state, + mode: Box::into_raw(Box::new(self.initialize_mode())), + wait_for_input: core::ptr::null_mut(), + }, + handler: self, + controller: controller, + hid_context: hid_context, + })); + + let context = unsafe { context_ptr.as_mut().expect("freshly boxed context pointer is null.") }; + + // create event for wait_for_input. + let mut wait_for_pointer_input_event: efi::Event = core::ptr::null_mut(); + let status = (boot_services.create_event)( + system::EVT_NOTIFY_WAIT, + system::TPL_NOTIFY, + Some(wait_for_pointer), + context_ptr as *mut c_void, + core::ptr::addr_of_mut!(wait_for_pointer_input_event), + ); + if status.is_error() { + drop(unsafe { Box::from_raw(context_ptr) }); + return Err(status); + } + context.absolute_pointer.wait_for_input = wait_for_pointer_input_event; + + // install the absolute_pointer protocol. + let mut controller = controller; + let absolute_pointer_ptr = raw_field!(context_ptr, PointerContext, absolute_pointer); + let status = (boot_services.install_protocol_interface)( + core::ptr::addr_of_mut!(controller), + &absolute_pointer::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + efi::NATIVE_INTERFACE, + absolute_pointer_ptr as *mut c_void, + ); + + if status.is_error() { + let _ = deinitialize(context_ptr); + return Err(status); + } + + Ok(context_ptr) } - Ok(context_ptr) - } + // Initializes the absolute_pointer mode structure. + fn initialize_mode(&self) -> absolute_pointer::Mode { + let mut mode: absolute_pointer::Mode = Default::default(); - // Initializes the absolute_pointer mode structure. - fn initialize_mode(&self) -> absolute_pointer::Mode { - let mut mode: absolute_pointer::Mode = Default::default(); + if self.supported_usages.contains(&Usage::from(GENERIC_DESKTOP_X)) { + mode.absolute_max_x = AXIS_RESOLUTION; + mode.absolute_min_x = 0; + } else { + debugln!(DEBUG_WARN, "No x-axis usages found in the report descriptor."); + } - if self.supported_usages.contains(&Usage::from(GENERIC_DESKTOP_X)) { - mode.absolute_max_x = AXIS_RESOLUTION; - mode.absolute_min_x = 0; - } else { - debugln!(DEBUG_WARN, "No x-axis usages found in the report descriptor."); - } + if self.supported_usages.contains(&Usage::from(GENERIC_DESKTOP_Y)) { + mode.absolute_max_y = AXIS_RESOLUTION; + mode.absolute_min_y = 0; + } else { + debugln!(DEBUG_WARN, "No y-axis usages found in the report descriptor."); + } - if self.supported_usages.contains(&Usage::from(GENERIC_DESKTOP_Y)) { - mode.absolute_max_y = AXIS_RESOLUTION; - mode.absolute_min_y = 0; - } else { - debugln!(DEBUG_WARN, "No y-axis usages found in the report descriptor."); - } + if (self.supported_usages.contains(&Usage::from(GENERIC_DESKTOP_Z))) + || (self.supported_usages.contains(&Usage::from(GENERIC_DESKTOP_WHEEL))) + { + mode.absolute_max_z = AXIS_RESOLUTION; + mode.absolute_min_z = 0; + //TODO: Z-axis is interpreted as pressure data. This is for compat with reference implementation in C, but + //could consider e.g. looking for actual digitizer tip pressure usages or something. + mode.attributes = mode.attributes | 0x02; + } else { + debugln!(DEBUG_INFO, "No z-axis usages found in the report descriptor."); + } - if (self.supported_usages.contains(&Usage::from(GENERIC_DESKTOP_Z))) - || (self.supported_usages.contains(&Usage::from(GENERIC_DESKTOP_WHEEL))) - { - mode.absolute_max_z = AXIS_RESOLUTION; - mode.absolute_min_z = 0; - //TODO: Z-axis is interpreted as pressure data. This is for compat with reference implementation in C, but - //could consider e.g. looking for actual digitizer tip pressure usages or something. - mode.attributes = mode.attributes | 0x02; - } else { - debugln!(DEBUG_INFO, "No z-axis usages found in the report descriptor."); - } + let button_count = self.supported_usages.iter().filter(|x| x.page() == BUTTON_PAGE).count(); - let button_count = self.supported_usages.iter().filter(|x| x.page() == BUTTON_PAGE).count(); + if button_count > 1 { + mode.attributes = mode.attributes | 0x01; // alternate button exists. + } - if button_count > 1 { - mode.attributes = mode.attributes | 0x01; // alternate button exists. + mode } - mode - } + // Helper routine that handles projecting relative and absolute axis reports onto the fixed + // absolute report axis that this driver produces. + fn resolve_axis(current_value: u64, field: VariableField, report: &[u8]) -> Option { + if field.attributes.relative { + //for relative, just update and clamp the current state. + let new_value = current_value as i64 + field.field_value(report)?; + return Some(new_value.clamp(0, AXIS_RESOLUTION as i64) as u64); + } else { + //for absolute, project onto 0..AXIS_RESOLUTION + let mut new_value = field.field_value(report)?; - // Helper routine that handles projecting relative and absolute axis reports onto the fixed - // absolute report axis that this driver produces. - fn resolve_axis(current_value: u64, field: VariableField, report: &[u8]) -> Option { - if field.attributes.relative { - //for relative, just update and clamp the current state. - let new_value = current_value as i64 + field.field_value(report)?; - return Some(new_value.clamp(0, AXIS_RESOLUTION as i64) as u64); - } else { - //for absolute, project onto 0..AXIS_RESOLUTION - let mut new_value = field.field_value(report)?; + //translate to zero. + new_value = new_value.checked_sub(i32::from(field.logical_minimum) as i64)?; - //translate to zero. - new_value = new_value.checked_sub(i32::from(field.logical_minimum) as i64)?; + //scale to AXIS_RESOLUTION + new_value = (new_value * AXIS_RESOLUTION as i64 * 1000) / (field.field_range()? as i64 * 1000); - //scale to AXIS_RESOLUTION - new_value = (new_value * AXIS_RESOLUTION as i64 * 1000) / (field.field_range()? as i64 * 1000); - - return Some(new_value.clamp(0, AXIS_RESOLUTION as i64) as u64); + return Some(new_value.clamp(0, AXIS_RESOLUTION as i64) as u64); + } } - } - // handles x_axis inputs - fn x_axis_handler(&mut self, field: VariableField, report: &[u8]) { - if let Some(x_value) = Self::resolve_axis(self.current_state.current_x, field, report) { - self.current_state.current_x = x_value; - self.state_changed = true; + // handles x_axis inputs + fn x_axis_handler(&mut self, field: VariableField, report: &[u8]) { + if let Some(x_value) = Self::resolve_axis(self.current_state.current_x, field, report) { + self.current_state.current_x = x_value; + self.state_changed = true; + } } - } - // handles y_axis inputs - fn y_axis_handler(&mut self, field: VariableField, report: &[u8]) { - if let Some(y_value) = Self::resolve_axis(self.current_state.current_y, field, report) { - self.current_state.current_y = y_value; - self.state_changed = true; + // handles y_axis inputs + fn y_axis_handler(&mut self, field: VariableField, report: &[u8]) { + if let Some(y_value) = Self::resolve_axis(self.current_state.current_y, field, report) { + self.current_state.current_y = y_value; + self.state_changed = true; + } } - } - // handles z_axis inputs - fn z_axis_handler(&mut self, field: VariableField, report: &[u8]) { - if let Some(z_value) = Self::resolve_axis(self.current_state.current_z, field, report) { - self.current_state.current_z = z_value; - self.state_changed = true; + // handles z_axis inputs + fn z_axis_handler(&mut self, field: VariableField, report: &[u8]) { + if let Some(z_value) = Self::resolve_axis(self.current_state.current_z, field, report) { + self.current_state.current_z = z_value; + self.state_changed = true; + } } - } - // handles button inputs - fn button_handler(&mut self, field: VariableField, report: &[u8]) { - let shift: u32 = field.usage.into(); - if (shift < BUTTON_MIN) || (shift > BUTTON_MAX) { - return; - } + // handles button inputs + fn button_handler(&mut self, field: VariableField, report: &[u8]) { + let shift: u32 = field.usage.into(); + if (shift < BUTTON_MIN) || (shift > BUTTON_MAX) { + return; + } - if let Some(button_value) = field.field_value(report) { - let button_value = button_value as u32; + if let Some(button_value) = field.field_value(report) { + let button_value = button_value as u32; - let shift = shift - BUTTON_MIN; - if shift > u32::BITS { - return; - } - let button_value = button_value << shift; + let shift = shift - BUTTON_MIN; + if shift > u32::BITS { + return; + } + let button_value = button_value << shift; - self.current_state.active_buttons = self.current_state.active_buttons + self.current_state.active_buttons = self.current_state.active_buttons & !(1 << shift) // zero the relevant bit in the button state field. | button_value; // or in the current button state into that bit position. - self.state_changed = true; + self.state_changed = true; + } } - } - /// Processes the given input report buffer and handles input from it. - pub fn process_input_report(&mut self, report_buffer: &[u8]) { - if report_buffer.len() == 0 { - return; - } + /// Processes the given input report buffer and handles input from it. + pub fn process_input_report(&mut self, report_buffer: &[u8]) { + if report_buffer.len() == 0 { + return; + } - // determine whether report includes report id byte and adjust the buffer as needed. - let (report_id, report) = match self.report_id_present { - true => (Some(ReportId::from(&report_buffer[0..1])), &report_buffer[1..]), - false => (None, &report_buffer[0..]), - }; + // determine whether report includes report id byte and adjust the buffer as needed. + let (report_id, report) = match self.report_id_present { + true => (Some(ReportId::from(&report_buffer[0..1])), &report_buffer[1..]), + false => (None, &report_buffer[0..]), + }; - if report.len() == 0 { - return; - } + if report.len() == 0 { + return; + } - if let Some(report_data) = self.input_reports.get(&report_id).cloned() { - if report.len() != report_data.report_size { - return; - } + if let Some(report_data) = self.input_reports.get(&report_id).cloned() { + if report.len() != report_data.report_size { + return; + } - // hand the report data to the handler for each relevant field for field-specific processing. - for field in report_data.relevant_fields { - (field.report_handler)(self, field.field, report); - } + // hand the report data to the handler for each relevant field for field-specific processing. + for field in report_data.relevant_fields { + (field.report_handler)(self, field.field, report); + } + } } - } - // Uninstall the absolute pointer interface and free the Pointer context. - fn uninstall_pointer_interfaces(pointer_context: *mut PointerContext) -> Result<(), efi::Status> { - // retrieve a reference to boot services. - // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. - // Caller should have ensured this, so just expect on failure. - let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; + // Uninstall the absolute pointer interface and free the Pointer context. + fn uninstall_pointer_interfaces(pointer_context: *mut PointerContext) -> Result<(), efi::Status> { + // retrieve a reference to boot services. + // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + // Caller should have ensured this, so just expect on failure. + let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; - let absolute_pointer_ptr = raw_field!(pointer_context, PointerContext, absolute_pointer); + let absolute_pointer_ptr = raw_field!(pointer_context, PointerContext, absolute_pointer); - let mut overall_status = efi::Status::SUCCESS; + let mut overall_status = efi::Status::SUCCESS; - // close the wait_for_input event - let status = (boot_services.close_event)(unsafe { (*absolute_pointer_ptr).wait_for_input }); - if status.is_error() { - overall_status = status; - } + // close the wait_for_input event + let status = (boot_services.close_event)(unsafe { (*absolute_pointer_ptr).wait_for_input }); + if status.is_error() { + overall_status = status; + } - // uninstall absolute pointer protocol - let status = (boot_services.uninstall_protocol_interface)( - unsafe { (*pointer_context).controller }, - &absolute_pointer::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - absolute_pointer_ptr as *mut c_void, - ); - if status.is_error() { - overall_status = status; - } + // uninstall absolute pointer protocol + let status = (boot_services.uninstall_protocol_interface)( + unsafe { (*pointer_context).controller }, + &absolute_pointer::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + absolute_pointer_ptr as *mut c_void, + ); + if status.is_error() { + overall_status = status; + } - // take back the context and mode raw pointers - let context = unsafe { Box::from_raw(pointer_context) }; - drop(unsafe { Box::from_raw(context.absolute_pointer.mode) }); - drop(context); + // take back the context and mode raw pointers + let context = unsafe { Box::from_raw(pointer_context) }; + drop(unsafe { Box::from_raw(context.absolute_pointer.mode) }); + drop(context); - if overall_status.is_error() { - Err(overall_status) - } else { - Ok(()) + if overall_status.is_error() { + Err(overall_status) + } else { + Ok(()) + } } - } } /// Initializes a pointer handler on the given `controller` to handle reports described by `descriptor`. @@ -390,131 +401,132 @@ impl PointerHandler { /// Otherwise, a [`PointerContext`] that can be used to interact with this handler is returned. See [`PointerContext`] /// documentation for constraints on interactions with it. pub fn initialize( - controller: efi::Handle, - descriptor: &ReportDescriptor, - hid_context_ptr: *mut HidContext, + controller: efi::Handle, + descriptor: &ReportDescriptor, + hid_context_ptr: *mut HidContext, ) -> Result<*mut PointerContext, efi::Status> { - let handler = PointerHandler::process_descriptor(descriptor)?; + let handler = PointerHandler::process_descriptor(descriptor)?; - let context = handler.install_pointer_interfaces(controller, hid_context_ptr)?; + let context = handler.install_pointer_interfaces(controller, hid_context_ptr)?; - let hid_context = unsafe { hid_context_ptr.as_mut().expect("[pointer::initialize]: bad hid context pointer") }; + let hid_context = unsafe { hid_context_ptr.as_mut().expect("[pointer::initialize]: bad hid context pointer") }; - hid_context.pointer_context = context; + hid_context.pointer_context = context; - Ok(context) + Ok(context) } /// De-initializes a pointer handler described by `context` on the given `controller`. pub fn deinitialize(context: *mut PointerContext) -> Result<(), efi::Status> { - if context.is_null() { - return Err(efi::Status::NOT_STARTED); - } - PointerHandler::uninstall_pointer_interfaces(context) + if context.is_null() { + return Err(efi::Status::NOT_STARTED); + } + PointerHandler::uninstall_pointer_interfaces(context) } /// Attempt to retrieve a *mut HidContext for the given controller by locating the absolute_pointer interface associated /// with the controller (if any) and deriving a PointerContext from it (which contains a pointer to the HidContext). pub fn attempt_to_retrieve_hid_context( - controller: efi::Handle, - driver_binding: &driver_binding::Protocol, + controller: efi::Handle, + driver_binding: &driver_binding::Protocol, ) -> Result<*mut HidContext, efi::Status> { - // retrieve a reference to boot services. - // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. - // Caller should have ensured this, so just expect on failure. - let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; - - let mut absolute_pointer_ptr: *mut absolute_pointer::Protocol = core::ptr::null_mut(); - let status = (boot_services.open_protocol)( - controller, - &absolute_pointer::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::addr_of_mut!(absolute_pointer_ptr) as *mut *mut c_void, - driver_binding.driver_binding_handle, - controller, - system::OPEN_PROTOCOL_GET_PROTOCOL, - ); - - match status { - efi::Status::SUCCESS => { - // retrieve a reference to the pointer context. - // Safety: `absolute_pointer_ptr` must point to an instance of absolute_pointer that is contained in a - // PointerContext struct. The following is the equivalent of the `CR` (contained record) macro in the EDK2 C - // reference implementation. - let context_ptr = unsafe { (absolute_pointer_ptr as *mut u8).sub(offset_of!(PointerContext, absolute_pointer)) } - as *mut PointerContext; - return Ok(unsafe { (*context_ptr).hid_context }); + // retrieve a reference to boot services. + // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + // Caller should have ensured this, so just expect on failure. + let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; + + let mut absolute_pointer_ptr: *mut absolute_pointer::Protocol = core::ptr::null_mut(); + let status = (boot_services.open_protocol)( + controller, + &absolute_pointer::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::addr_of_mut!(absolute_pointer_ptr) as *mut *mut c_void, + driver_binding.driver_binding_handle, + controller, + system::OPEN_PROTOCOL_GET_PROTOCOL, + ); + + match status { + efi::Status::SUCCESS => { + // retrieve a reference to the pointer context. + // Safety: `absolute_pointer_ptr` must point to an instance of absolute_pointer that is contained in a + // PointerContext struct. The following is the equivalent of the `CR` (contained record) macro in the EDK2 C + // reference implementation. + let context_ptr = + unsafe { (absolute_pointer_ptr as *mut u8).sub(offset_of!(PointerContext, absolute_pointer)) } + as *mut PointerContext; + return Ok(unsafe { (*context_ptr).hid_context }); + } + err => return Err(err), } - err => return Err(err), - } } // event handler for wait_for_pointer event that is part of the absolute pointer interface. extern "efiapi" fn wait_for_pointer(event: efi::Event, context: *mut c_void) { - // retrieve a reference to boot services. - // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. - // Caller should have ensured this, so just expect on failure. - let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; - - // retrieve a reference to pointer context. - // Safety: Pointer Context must have been initialized before this event could fire, so just expect on failure. - let pointer_context = unsafe { (context as *mut PointerContext).as_mut().expect("Pointer Context is bad.") }; - - if pointer_context.handler.state_changed { - (boot_services.signal_event)(event); - } + // retrieve a reference to boot services. + // Safety: BOOT_SERVICES must have been initialized to point to the UEFI Boot Services table. + // Caller should have ensured this, so just expect on failure. + let boot_services = unsafe { BOOT_SERVICES.as_mut().expect("BOOT_SERVICES not properly initialized") }; + + // retrieve a reference to pointer context. + // Safety: Pointer Context must have been initialized before this event could fire, so just expect on failure. + let pointer_context = unsafe { (context as *mut PointerContext).as_mut().expect("Pointer Context is bad.") }; + + if pointer_context.handler.state_changed { + (boot_services.signal_event)(event); + } } // resets the pointer state - part of the absolute pointer interface. extern "efiapi" fn absolute_pointer_reset( - this: *mut absolute_pointer::Protocol, - _extended_verification: bool, + this: *mut absolute_pointer::Protocol, + _extended_verification: bool, ) -> efi::Status { - if this.is_null() { - return efi::Status::INVALID_PARAMETER; - } - // retrieve a reference to the pointer context. - // Safety: `this` must point to an instance of absolute_pointer that is contained in a PointerContext struct. - // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. - let context_ptr = - unsafe { (this as *mut u8).sub(offset_of!(PointerContext, absolute_pointer)) } as *mut PointerContext; + if this.is_null() { + return efi::Status::INVALID_PARAMETER; + } + // retrieve a reference to the pointer context. + // Safety: `this` must point to an instance of absolute_pointer that is contained in a PointerContext struct. + // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. + let context_ptr = + unsafe { (this as *mut u8).sub(offset_of!(PointerContext, absolute_pointer)) } as *mut PointerContext; - let pointer_context = unsafe { context_ptr.as_mut().expect("Context pointer is bad.") }; + let pointer_context = unsafe { context_ptr.as_mut().expect("Context pointer is bad.") }; - pointer_context.handler.current_state = Default::default(); + pointer_context.handler.current_state = Default::default(); - //stick the pointer in the middle of the screen. - pointer_context.handler.current_state.current_x = AXIS_RESOLUTION / 2; - pointer_context.handler.current_state.current_y = AXIS_RESOLUTION / 2; - pointer_context.handler.current_state.current_z = 0; + //stick the pointer in the middle of the screen. + pointer_context.handler.current_state.current_x = AXIS_RESOLUTION / 2; + pointer_context.handler.current_state.current_y = AXIS_RESOLUTION / 2; + pointer_context.handler.current_state.current_z = 0; - pointer_context.handler.state_changed = false; + pointer_context.handler.state_changed = false; - efi::Status::SUCCESS + efi::Status::SUCCESS } // returns the current pointer state in the `state` buffer provided by the caller - part of the absolute pointer // interface. extern "efiapi" fn absolute_pointer_get_state( - this: *mut absolute_pointer::Protocol, - state: *mut absolute_pointer::State, + this: *mut absolute_pointer::Protocol, + state: *mut absolute_pointer::State, ) -> efi::Status { - if this.is_null() || state.is_null() { - return efi::Status::INVALID_PARAMETER; - } - // retrieve a reference to the pointer context. - // Safety: `this` must point to an instance of absolute_pointer that is contained in a PointerContext struct. - // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. - let context_ptr = - unsafe { (this as *mut u8).sub(offset_of!(PointerContext, absolute_pointer)) } as *mut PointerContext; - - let pointer_context = unsafe { context_ptr.as_mut().expect("Context pointer is bad.") }; - - // Safety: state pointer is assumed to be a valid buffer to receive the current state. - if pointer_context.handler.state_changed { - unsafe { state.write(pointer_context.handler.current_state) }; - pointer_context.handler.state_changed = false; - efi::Status::SUCCESS - } else { - efi::Status::NOT_READY - } + if this.is_null() || state.is_null() { + return efi::Status::INVALID_PARAMETER; + } + // retrieve a reference to the pointer context. + // Safety: `this` must point to an instance of absolute_pointer that is contained in a PointerContext struct. + // the following is the equivalent of the `CR` (contained record) macro in the EDK2 reference implementation. + let context_ptr = + unsafe { (this as *mut u8).sub(offset_of!(PointerContext, absolute_pointer)) } as *mut PointerContext; + + let pointer_context = unsafe { context_ptr.as_mut().expect("Context pointer is bad.") }; + + // Safety: state pointer is assumed to be a valid buffer to receive the current state. + if pointer_context.handler.state_changed { + unsafe { state.write(pointer_context.handler.current_state) }; + pointer_context.handler.state_changed = false; + efi::Status::SUCCESS + } else { + efi::Status::NOT_READY + } } diff --git a/HidPkg/UefiHidDxeV2/src/boot_services.rs b/HidPkg/UefiHidDxeV2/src/boot_services.rs index 55d38ca03d..cd19af0518 100644 --- a/HidPkg/UefiHidDxeV2/src/boot_services.rs +++ b/HidPkg/UefiHidDxeV2/src/boot_services.rs @@ -18,352 +18,358 @@ use r_efi::efi; /// Abstracts UEFI Boot Services used in this crate. #[cfg_attr(test, automock)] pub trait UefiBootServices { - fn create_event( - &self, - r#type: u32, - notify_tpl: efi::Tpl, - notify_function: Option, - notify_context: *mut c_void, - event: *mut efi::Event, - ) -> efi::Status; - - fn create_event_ex( - &self, - r#type: u32, - notify_tpl: efi::Tpl, - notify_function: Option, - notify_context: *const c_void, - event_group: *const efi::Guid, - event: *mut efi::Event, - ) -> efi::Status; - - fn close_event(&self, event: efi::Event) -> efi::Status; - - fn signal_event(&self, event: efi::Event) -> efi::Status; - - fn raise_tpl(&self, new_tpl: efi::Tpl) -> efi::Tpl; - - fn restore_tpl(&self, old_tpl: efi::Tpl); - - fn install_protocol_interface( - &self, - handle: *mut efi::Handle, - protocol: *mut efi::Guid, - interface_type: efi::InterfaceType, - interface: *mut c_void, - ) -> efi::Status; - - fn uninstall_protocol_interface( - &self, - handle: efi::Handle, - protocol: *mut efi::Guid, - interface: *mut c_void, - ) -> efi::Status; - - fn open_protocol( - &self, - handle: efi::Handle, - protocol: *mut efi::Guid, - interface: *mut *mut c_void, - agent_handle: efi::Handle, - controller_handle: efi::Handle, - attributes: u32, - ) -> efi::Status; - - fn close_protocol( - &self, - handle: efi::Handle, - protocol: *mut efi::Guid, - agent_handle: efi::Handle, - controller_handle: efi::Handle, - ) -> efi::Status; - - fn locate_protocol( - &self, - protocol: *mut efi::Guid, - registration: *mut c_void, - interface: *mut *mut c_void, - ) -> efi::Status; + fn create_event( + &self, + r#type: u32, + notify_tpl: efi::Tpl, + notify_function: Option, + notify_context: *mut c_void, + event: *mut efi::Event, + ) -> efi::Status; + + fn create_event_ex( + &self, + r#type: u32, + notify_tpl: efi::Tpl, + notify_function: Option, + notify_context: *const c_void, + event_group: *const efi::Guid, + event: *mut efi::Event, + ) -> efi::Status; + + fn close_event(&self, event: efi::Event) -> efi::Status; + + fn signal_event(&self, event: efi::Event) -> efi::Status; + + fn raise_tpl(&self, new_tpl: efi::Tpl) -> efi::Tpl; + + fn restore_tpl(&self, old_tpl: efi::Tpl); + + fn install_protocol_interface( + &self, + handle: *mut efi::Handle, + protocol: *mut efi::Guid, + interface_type: efi::InterfaceType, + interface: *mut c_void, + ) -> efi::Status; + + fn uninstall_protocol_interface( + &self, + handle: efi::Handle, + protocol: *mut efi::Guid, + interface: *mut c_void, + ) -> efi::Status; + + fn open_protocol( + &self, + handle: efi::Handle, + protocol: *mut efi::Guid, + interface: *mut *mut c_void, + agent_handle: efi::Handle, + controller_handle: efi::Handle, + attributes: u32, + ) -> efi::Status; + + fn close_protocol( + &self, + handle: efi::Handle, + protocol: *mut efi::Guid, + agent_handle: efi::Handle, + controller_handle: efi::Handle, + ) -> efi::Status; + + fn locate_protocol( + &self, + protocol: *mut efi::Guid, + registration: *mut c_void, + interface: *mut *mut c_void, + ) -> efi::Status; } /// Provides a concrete implementation of the [`UefiBootServices`] trait. #[derive(Debug)] pub struct StandardUefiBootServices { - boot_services: AtomicPtr, + boot_services: AtomicPtr, } impl StandardUefiBootServices { - /// Creates a new StandardUefiBootServices. - /// Note: attempts to use methods on this instance will panic until [`Self::initialize`] is called. - pub const fn new() -> Self { - Self { boot_services: AtomicPtr::new(core::ptr::null_mut()) } - } - - /// Initializes this instance of [`StandardUefiBootServices`] with a pointer to the boot services table. - pub fn initialize(&self, boot_services: *mut efi::BootServices) { - self.boot_services.store(boot_services, core::sync::atomic::Ordering::SeqCst) - } - - // Returns a reference to the boot services table. Panics if uninitialized. - fn boot_services(&self) -> &efi::BootServices { - let boot_services_ptr = self.boot_services.load(core::sync::atomic::Ordering::SeqCst); - unsafe { boot_services_ptr.as_ref().expect("invalid boot_services pointer") } - } + /// Creates a new StandardUefiBootServices. + /// Note: attempts to use methods on this instance will panic until [`Self::initialize`] is called. + pub const fn new() -> Self { + Self { boot_services: AtomicPtr::new(core::ptr::null_mut()) } + } + + /// Initializes this instance of [`StandardUefiBootServices`] with a pointer to the boot services table. + pub fn initialize(&self, boot_services: *mut efi::BootServices) { + self.boot_services.store(boot_services, core::sync::atomic::Ordering::SeqCst) + } + + // Returns a reference to the boot services table. Panics if uninitialized. + fn boot_services(&self) -> &efi::BootServices { + let boot_services_ptr = self.boot_services.load(core::sync::atomic::Ordering::SeqCst); + unsafe { boot_services_ptr.as_ref().expect("invalid boot_services pointer") } + } } unsafe impl Sync for StandardUefiBootServices {} unsafe impl Send for StandardUefiBootServices {} impl UefiBootServices for StandardUefiBootServices { - fn create_event( - &self, - r#type: u32, - notify_tpl: efi::Tpl, - notify_function: Option, - notify_context: *mut c_void, - event: *mut efi::Event, - ) -> efi::Status { - (self.boot_services().create_event)(r#type, notify_tpl, notify_function, notify_context, event) - } - fn create_event_ex( - &self, - r#type: u32, - notify_tpl: efi::Tpl, - notify_function: Option, - notify_context: *const c_void, - event_group: *const efi::Guid, - event: *mut efi::Event, - ) -> efi::Status { - (self.boot_services().create_event_ex)(r#type, notify_tpl, notify_function, notify_context, event_group, event) - } - fn close_event(&self, event: efi::Event) -> efi::Status { - (self.boot_services().close_event)(event) - } - fn signal_event(&self, event: efi::Event) -> efi::Status { - (self.boot_services().signal_event)(event) - } - fn raise_tpl(&self, new_tpl: efi::Tpl) -> efi::Tpl { - (self.boot_services().raise_tpl)(new_tpl) - } - fn restore_tpl(&self, old_tpl: efi::Tpl) { - (self.boot_services().restore_tpl)(old_tpl) - } - fn install_protocol_interface( - &self, - handle: *mut efi::Handle, - protocol: *mut efi::Guid, - interface_type: efi::InterfaceType, - interface: *mut c_void, - ) -> efi::Status { - (self.boot_services().install_protocol_interface)(handle, protocol, interface_type, interface) - } - fn uninstall_protocol_interface( - &self, - handle: efi::Handle, - protocol: *mut efi::Guid, - interface: *mut c_void, - ) -> efi::Status { - (self.boot_services().uninstall_protocol_interface)(handle, protocol, interface) - } - fn open_protocol( - &self, - handle: efi::Handle, - protocol: *mut efi::Guid, - interface: *mut *mut c_void, - agent_handle: efi::Handle, - controller_handle: efi::Handle, - attributes: u32, - ) -> efi::Status { - (self.boot_services().open_protocol)(handle, protocol, interface, agent_handle, controller_handle, attributes) - } - fn close_protocol( - &self, - handle: efi::Handle, - protocol: *mut efi::Guid, - agent_handle: efi::Handle, - controller_handle: efi::Handle, - ) -> efi::Status { - (self.boot_services().close_protocol)(handle, protocol, agent_handle, controller_handle) - } - fn locate_protocol( - &self, - protocol: *mut efi::Guid, - registration: *mut c_void, - interface: *mut *mut c_void, - ) -> efi::Status { - (self.boot_services().locate_protocol)(protocol, registration, interface) - } + fn create_event( + &self, + r#type: u32, + notify_tpl: efi::Tpl, + notify_function: Option, + notify_context: *mut c_void, + event: *mut efi::Event, + ) -> efi::Status { + (self.boot_services().create_event)(r#type, notify_tpl, notify_function, notify_context, event) + } + fn create_event_ex( + &self, + r#type: u32, + notify_tpl: efi::Tpl, + notify_function: Option, + notify_context: *const c_void, + event_group: *const efi::Guid, + event: *mut efi::Event, + ) -> efi::Status { + (self.boot_services().create_event_ex)(r#type, notify_tpl, notify_function, notify_context, event_group, event) + } + fn close_event(&self, event: efi::Event) -> efi::Status { + (self.boot_services().close_event)(event) + } + fn signal_event(&self, event: efi::Event) -> efi::Status { + (self.boot_services().signal_event)(event) + } + fn raise_tpl(&self, new_tpl: efi::Tpl) -> efi::Tpl { + (self.boot_services().raise_tpl)(new_tpl) + } + fn restore_tpl(&self, old_tpl: efi::Tpl) { + (self.boot_services().restore_tpl)(old_tpl) + } + fn install_protocol_interface( + &self, + handle: *mut efi::Handle, + protocol: *mut efi::Guid, + interface_type: efi::InterfaceType, + interface: *mut c_void, + ) -> efi::Status { + (self.boot_services().install_protocol_interface)(handle, protocol, interface_type, interface) + } + fn uninstall_protocol_interface( + &self, + handle: efi::Handle, + protocol: *mut efi::Guid, + interface: *mut c_void, + ) -> efi::Status { + (self.boot_services().uninstall_protocol_interface)(handle, protocol, interface) + } + fn open_protocol( + &self, + handle: efi::Handle, + protocol: *mut efi::Guid, + interface: *mut *mut c_void, + agent_handle: efi::Handle, + controller_handle: efi::Handle, + attributes: u32, + ) -> efi::Status { + (self.boot_services().open_protocol)(handle, protocol, interface, agent_handle, controller_handle, attributes) + } + fn close_protocol( + &self, + handle: efi::Handle, + protocol: *mut efi::Guid, + agent_handle: efi::Handle, + controller_handle: efi::Handle, + ) -> efi::Status { + (self.boot_services().close_protocol)(handle, protocol, agent_handle, controller_handle) + } + fn locate_protocol( + &self, + protocol: *mut efi::Guid, + registration: *mut c_void, + interface: *mut *mut c_void, + ) -> efi::Status { + (self.boot_services().locate_protocol)(protocol, registration, interface) + } } #[cfg(test)] mod test { - use core::{ffi::c_void, mem::MaybeUninit}; - - use r_efi::efi; - - use super::{StandardUefiBootServices, UefiBootServices}; - - extern "efiapi" fn mock_create_event( - _type: u32, - _notify_tpl: efi::Tpl, - _notify_function: Option, - _notify_context: *mut c_void, - _event: *mut efi::Event, - ) -> efi::Status { - efi::Status::SUCCESS - } - - extern "efiapi" fn mock_create_event_ex( - _type: u32, - _notify_tpl: efi::Tpl, - _notify_function: Option, - _notify_context: *const c_void, - _event_group: *const efi::Guid, - _event: *mut efi::Event, - ) -> efi::Status { - efi::Status::SUCCESS - } - extern "efiapi" fn mock_close_event(_event: efi::Event) -> efi::Status { - efi::Status::SUCCESS - } - - extern "efiapi" fn mock_signal_event(_event: efi::Event) -> efi::Status { - efi::Status::SUCCESS - } - - extern "efiapi" fn mock_raise_tpl(_new_tpl: efi::Tpl) -> efi::Tpl { - efi::TPL_APPLICATION - } - - extern "efiapi" fn mock_restore_tpl(_new_tpl: efi::Tpl) {} - - extern "efiapi" fn mock_install_protocol_interface( - _handle: *mut efi::Handle, - _protocol: *mut efi::Guid, - _interface_type: efi::InterfaceType, - _interface: *mut c_void, - ) -> efi::Status { - efi::Status::SUCCESS - } - - extern "efiapi" fn mock_uninstall_protocol_interface( - _handle: efi::Handle, - _protocol: *mut efi::Guid, - _interface: *mut c_void, - ) -> efi::Status { - efi::Status::SUCCESS - } - - extern "efiapi" fn mock_open_protocol( - _handle: efi::Handle, - _protocol: *mut efi::Guid, - _interface: *mut *mut c_void, - _agent_handle: efi::Handle, - _controller_handle: efi::Handle, - _attributes: u32, - ) -> efi::Status { - efi::Status::SUCCESS - } - - extern "efiapi" fn mock_close_protocol( - _handle: efi::Handle, - _protocol: *mut efi::Guid, - _agent_handle: efi::Handle, - _controller_handle: efi::Handle, - ) -> efi::Status { - efi::Status::SUCCESS - } - - extern "efiapi" fn mock_locate_protocol( - _protocol: *mut efi::Guid, - _registration: *mut c_void, - _interface: *mut *mut c_void, - ) -> efi::Status { - efi::Status::SUCCESS - } - - #[test] - fn standard_uefi_boot_services_should_wrap_boot_services() { - let boot_services = MaybeUninit::::zeroed(); - let mut boot_services = unsafe { boot_services.assume_init() }; - boot_services.create_event = mock_create_event; - boot_services.create_event_ex = mock_create_event_ex; - boot_services.close_event = mock_close_event; - boot_services.signal_event = mock_signal_event; - boot_services.raise_tpl = mock_raise_tpl; - boot_services.restore_tpl = mock_restore_tpl; - boot_services.install_protocol_interface = mock_install_protocol_interface; - boot_services.uninstall_protocol_interface = mock_uninstall_protocol_interface; - boot_services.open_protocol = mock_open_protocol; - boot_services.close_protocol = mock_close_protocol; - boot_services.locate_protocol = mock_locate_protocol; - - const TEST_GUID: efi::Guid = efi::Guid::from_fields(0, 0, 0, 0, 0, &[0, 0, 0, 0, 0, 0]); - let mut event = 1 as efi::Event; - let mut handle = 2 as efi::Handle; - - let test_boot_services = StandardUefiBootServices::new(); - test_boot_services.initialize(&mut boot_services as *mut efi::BootServices); - assert_eq!( - test_boot_services.create_event(0, efi::TPL_NOTIFY, None, core::ptr::null_mut(), core::ptr::addr_of_mut!(event)), - efi::Status::SUCCESS - ); - - assert_eq!( - test_boot_services.create_event_ex( - 0, - efi::TPL_NOTIFY, - None, - core::ptr::null_mut(), - &TEST_GUID as *const efi::Guid, - core::ptr::addr_of_mut!(event), - ), - efi::Status::SUCCESS - ); - - assert_eq!(test_boot_services.close_event(event), efi::Status::SUCCESS); - assert_eq!(test_boot_services.signal_event(event), efi::Status::SUCCESS); - assert_eq!(test_boot_services.raise_tpl(efi::TPL_HIGH_LEVEL), efi::TPL_APPLICATION); - test_boot_services.restore_tpl(efi::TPL_APPLICATION); - assert_eq!( - test_boot_services.install_protocol_interface( - core::ptr::addr_of_mut!(handle), - &TEST_GUID as *const efi::Guid as *mut efi::Guid, - efi::NATIVE_INTERFACE, - core::ptr::null_mut() - ), - efi::Status::SUCCESS - ); - assert_eq!( - test_boot_services.uninstall_protocol_interface( - handle, - &TEST_GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::null_mut() - ), - efi::Status::SUCCESS - ); - assert_eq!( - test_boot_services.open_protocol( - handle, - &TEST_GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::null_mut(), - handle, - handle, - 0 - ), - efi::Status::SUCCESS - ); - assert_eq!( - test_boot_services.close_protocol(handle, &TEST_GUID as *const efi::Guid as *mut efi::Guid, handle, handle), - efi::Status::SUCCESS - ); - assert_eq!( - test_boot_services.locate_protocol( - &TEST_GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::null_mut(), - core::ptr::null_mut() - ), - efi::Status::SUCCESS - ); - } + use core::{ffi::c_void, mem::MaybeUninit}; + + use r_efi::efi; + + use super::{StandardUefiBootServices, UefiBootServices}; + + extern "efiapi" fn mock_create_event( + _type: u32, + _notify_tpl: efi::Tpl, + _notify_function: Option, + _notify_context: *mut c_void, + _event: *mut efi::Event, + ) -> efi::Status { + efi::Status::SUCCESS + } + + extern "efiapi" fn mock_create_event_ex( + _type: u32, + _notify_tpl: efi::Tpl, + _notify_function: Option, + _notify_context: *const c_void, + _event_group: *const efi::Guid, + _event: *mut efi::Event, + ) -> efi::Status { + efi::Status::SUCCESS + } + extern "efiapi" fn mock_close_event(_event: efi::Event) -> efi::Status { + efi::Status::SUCCESS + } + + extern "efiapi" fn mock_signal_event(_event: efi::Event) -> efi::Status { + efi::Status::SUCCESS + } + + extern "efiapi" fn mock_raise_tpl(_new_tpl: efi::Tpl) -> efi::Tpl { + efi::TPL_APPLICATION + } + + extern "efiapi" fn mock_restore_tpl(_new_tpl: efi::Tpl) {} + + extern "efiapi" fn mock_install_protocol_interface( + _handle: *mut efi::Handle, + _protocol: *mut efi::Guid, + _interface_type: efi::InterfaceType, + _interface: *mut c_void, + ) -> efi::Status { + efi::Status::SUCCESS + } + + extern "efiapi" fn mock_uninstall_protocol_interface( + _handle: efi::Handle, + _protocol: *mut efi::Guid, + _interface: *mut c_void, + ) -> efi::Status { + efi::Status::SUCCESS + } + + extern "efiapi" fn mock_open_protocol( + _handle: efi::Handle, + _protocol: *mut efi::Guid, + _interface: *mut *mut c_void, + _agent_handle: efi::Handle, + _controller_handle: efi::Handle, + _attributes: u32, + ) -> efi::Status { + efi::Status::SUCCESS + } + + extern "efiapi" fn mock_close_protocol( + _handle: efi::Handle, + _protocol: *mut efi::Guid, + _agent_handle: efi::Handle, + _controller_handle: efi::Handle, + ) -> efi::Status { + efi::Status::SUCCESS + } + + extern "efiapi" fn mock_locate_protocol( + _protocol: *mut efi::Guid, + _registration: *mut c_void, + _interface: *mut *mut c_void, + ) -> efi::Status { + efi::Status::SUCCESS + } + + #[test] + fn standard_uefi_boot_services_should_wrap_boot_services() { + let boot_services = MaybeUninit::::zeroed(); + let mut boot_services = unsafe { boot_services.assume_init() }; + boot_services.create_event = mock_create_event; + boot_services.create_event_ex = mock_create_event_ex; + boot_services.close_event = mock_close_event; + boot_services.signal_event = mock_signal_event; + boot_services.raise_tpl = mock_raise_tpl; + boot_services.restore_tpl = mock_restore_tpl; + boot_services.install_protocol_interface = mock_install_protocol_interface; + boot_services.uninstall_protocol_interface = mock_uninstall_protocol_interface; + boot_services.open_protocol = mock_open_protocol; + boot_services.close_protocol = mock_close_protocol; + boot_services.locate_protocol = mock_locate_protocol; + + const TEST_GUID: efi::Guid = efi::Guid::from_fields(0, 0, 0, 0, 0, &[0, 0, 0, 0, 0, 0]); + let mut event = 1 as efi::Event; + let mut handle = 2 as efi::Handle; + + let test_boot_services = StandardUefiBootServices::new(); + test_boot_services.initialize(&mut boot_services as *mut efi::BootServices); + assert_eq!( + test_boot_services.create_event( + 0, + efi::TPL_NOTIFY, + None, + core::ptr::null_mut(), + core::ptr::addr_of_mut!(event) + ), + efi::Status::SUCCESS + ); + + assert_eq!( + test_boot_services.create_event_ex( + 0, + efi::TPL_NOTIFY, + None, + core::ptr::null_mut(), + &TEST_GUID as *const efi::Guid, + core::ptr::addr_of_mut!(event), + ), + efi::Status::SUCCESS + ); + + assert_eq!(test_boot_services.close_event(event), efi::Status::SUCCESS); + assert_eq!(test_boot_services.signal_event(event), efi::Status::SUCCESS); + assert_eq!(test_boot_services.raise_tpl(efi::TPL_HIGH_LEVEL), efi::TPL_APPLICATION); + test_boot_services.restore_tpl(efi::TPL_APPLICATION); + assert_eq!( + test_boot_services.install_protocol_interface( + core::ptr::addr_of_mut!(handle), + &TEST_GUID as *const efi::Guid as *mut efi::Guid, + efi::NATIVE_INTERFACE, + core::ptr::null_mut() + ), + efi::Status::SUCCESS + ); + assert_eq!( + test_boot_services.uninstall_protocol_interface( + handle, + &TEST_GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::null_mut() + ), + efi::Status::SUCCESS + ); + assert_eq!( + test_boot_services.open_protocol( + handle, + &TEST_GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::null_mut(), + handle, + handle, + 0 + ), + efi::Status::SUCCESS + ); + assert_eq!( + test_boot_services.close_protocol(handle, &TEST_GUID as *const efi::Guid as *mut efi::Guid, handle, handle), + efi::Status::SUCCESS + ); + assert_eq!( + test_boot_services.locate_protocol( + &TEST_GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::null_mut(), + core::ptr::null_mut() + ), + efi::Status::SUCCESS + ); + } } diff --git a/HidPkg/UefiHidDxeV2/src/driver_binding.rs b/HidPkg/UefiHidDxeV2/src/driver_binding.rs index 5432d5bdd6..d5fc41a339 100644 --- a/HidPkg/UefiHidDxeV2/src/driver_binding.rs +++ b/HidPkg/UefiHidDxeV2/src/driver_binding.rs @@ -25,24 +25,24 @@ use crate::boot_services::UefiBootServices; /// Reference: #[cfg_attr(test, automock)] pub trait DriverBinding { - /// Reference: - fn driver_binding_supported( - &mut self, - boot_services: &'static dyn UefiBootServices, - controller: efi::Handle, - ) -> Result<(), efi::Status>; - /// Reference: - fn driver_binding_start( - &mut self, - boot_services: &'static dyn UefiBootServices, - controller: efi::Handle, - ) -> Result<(), efi::Status>; - /// Reference: - fn driver_binding_stop( - &mut self, - boot_services: &'static dyn UefiBootServices, - controller: efi::Handle, - ) -> Result<(), efi::Status>; + /// Reference: + fn driver_binding_supported( + &mut self, + boot_services: &'static dyn UefiBootServices, + controller: efi::Handle, + ) -> Result<(), efi::Status>; + /// Reference: + fn driver_binding_start( + &mut self, + boot_services: &'static dyn UefiBootServices, + controller: efi::Handle, + ) -> Result<(), efi::Status>; + /// Reference: + fn driver_binding_stop( + &mut self, + boot_services: &'static dyn UefiBootServices, + controller: efi::Handle, + ) -> Result<(), efi::Status>; } /// Manages FFI for a driver binding instance with UEFI core. @@ -65,258 +65,258 @@ pub trait DriverBinding { ///``` #[repr(C)] pub struct UefiDriverBinding { - uefi_binding: protocols::driver_binding::Protocol, - boot_services: &'static dyn UefiBootServices, - binding: Box, + uefi_binding: protocols::driver_binding::Protocol, + boot_services: &'static dyn UefiBootServices, + binding: Box, } impl UefiDriverBinding { - /// Creates a new UefiDriverBinding that manages the given binding. - pub fn new( - boot_services: &'static dyn UefiBootServices, - binding: Box, - handle: efi::Handle, - ) -> Self { - let uefi_binding = protocols::driver_binding::Protocol { - supported: Self::driver_binding_supported, - start: Self::driver_binding_start, - stop: Self::driver_binding_stop, - version: 1, - image_handle: handle, - driver_binding_handle: handle, - }; - Self { uefi_binding, boot_services, binding } - } - - /// Installs the binding with the UEFI core. - pub fn install(self) -> Result<*mut UefiDriverBinding, efi::Status> { - let mut handle = self.uefi_binding.driver_binding_handle; - let uefi_driver_binding_mgr_ptr = Box::into_raw(Box::new(self)); - let boot_services = &unsafe { uefi_driver_binding_mgr_ptr.as_ref().unwrap() }.boot_services; - let status = boot_services.install_protocol_interface( - core::ptr::addr_of_mut!(handle), - &protocols::driver_binding::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - efi::NATIVE_INTERFACE, - uefi_driver_binding_mgr_ptr as *mut c_void, - ); - if status.is_error() { - Err(status) - } else { - Ok(uefi_driver_binding_mgr_ptr) + /// Creates a new UefiDriverBinding that manages the given binding. + pub fn new( + boot_services: &'static dyn UefiBootServices, + binding: Box, + handle: efi::Handle, + ) -> Self { + let uefi_binding = protocols::driver_binding::Protocol { + supported: Self::driver_binding_supported, + start: Self::driver_binding_start, + stop: Self::driver_binding_stop, + version: 1, + image_handle: handle, + driver_binding_handle: handle, + }; + Self { uefi_binding, boot_services, binding } } - } - - /// Uninstalls the binding from the UEFI core. - /// # Safety - /// uefi_binding must be the same pointer returned from [`Self::install`]. - pub unsafe fn uninstall(uefi_binding: *mut UefiDriverBinding) -> Result { - let ptr = uefi_binding; - let binding = Box::from_raw(uefi_binding); - let status = binding.boot_services.uninstall_protocol_interface( - binding.uefi_binding.driver_binding_handle, - &protocols::driver_binding::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - ptr as *mut c_void, - ); - if status.is_error() { - Err(status) - } else { - Ok(*binding) + + /// Installs the binding with the UEFI core. + pub fn install(self) -> Result<*mut UefiDriverBinding, efi::Status> { + let mut handle = self.uefi_binding.driver_binding_handle; + let uefi_driver_binding_mgr_ptr = Box::into_raw(Box::new(self)); + let boot_services = &unsafe { uefi_driver_binding_mgr_ptr.as_ref().unwrap() }.boot_services; + let status = boot_services.install_protocol_interface( + core::ptr::addr_of_mut!(handle), + &protocols::driver_binding::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + efi::NATIVE_INTERFACE, + uefi_driver_binding_mgr_ptr as *mut c_void, + ); + if status.is_error() { + Err(status) + } else { + Ok(uefi_driver_binding_mgr_ptr) + } } - } - - // Driver Binding Supported FFI function - // Handles driver_binding_supported calls from the UEFI core by passing them to the trait implementation. - extern "efiapi" fn driver_binding_supported( - this: *mut protocols::driver_binding::Protocol, - controller: efi::Handle, - _remaining_device_path: *mut protocols::device_path::Protocol, - ) -> efi::Status { - let uefi_binding = unsafe { (this as *mut UefiDriverBinding).as_mut() }.expect("bad this pointer"); - match uefi_binding.binding.driver_binding_supported(uefi_binding.boot_services, controller) { - Ok(_) => efi::Status::SUCCESS, - Err(err) => err, + + /// Uninstalls the binding from the UEFI core. + /// # Safety + /// uefi_binding must be the same pointer returned from [`Self::install`]. + pub unsafe fn uninstall(uefi_binding: *mut UefiDriverBinding) -> Result { + let ptr = uefi_binding; + let binding = Box::from_raw(uefi_binding); + let status = binding.boot_services.uninstall_protocol_interface( + binding.uefi_binding.driver_binding_handle, + &protocols::driver_binding::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + ptr as *mut c_void, + ); + if status.is_error() { + Err(status) + } else { + Ok(*binding) + } } - } - - // Driver Binding Start FFI function - // Handles driver_binding_start calls from the UEFI core by passing them to the trait implementation. - extern "efiapi" fn driver_binding_start( - this: *mut protocols::driver_binding::Protocol, - controller: efi::Handle, - _remaining_device_path: *mut protocols::device_path::Protocol, - ) -> efi::Status { - let uefi_binding = unsafe { (this as *mut UefiDriverBinding).as_mut() }.expect("bad this pointer"); - match uefi_binding.binding.driver_binding_start(uefi_binding.boot_services, controller) { - Ok(_) => efi::Status::SUCCESS, - Err(err) => err, + + // Driver Binding Supported FFI function + // Handles driver_binding_supported calls from the UEFI core by passing them to the trait implementation. + extern "efiapi" fn driver_binding_supported( + this: *mut protocols::driver_binding::Protocol, + controller: efi::Handle, + _remaining_device_path: *mut protocols::device_path::Protocol, + ) -> efi::Status { + let uefi_binding = unsafe { (this as *mut UefiDriverBinding).as_mut() }.expect("bad this pointer"); + match uefi_binding.binding.driver_binding_supported(uefi_binding.boot_services, controller) { + Ok(_) => efi::Status::SUCCESS, + Err(err) => err, + } + } + + // Driver Binding Start FFI function + // Handles driver_binding_start calls from the UEFI core by passing them to the trait implementation. + extern "efiapi" fn driver_binding_start( + this: *mut protocols::driver_binding::Protocol, + controller: efi::Handle, + _remaining_device_path: *mut protocols::device_path::Protocol, + ) -> efi::Status { + let uefi_binding = unsafe { (this as *mut UefiDriverBinding).as_mut() }.expect("bad this pointer"); + match uefi_binding.binding.driver_binding_start(uefi_binding.boot_services, controller) { + Ok(_) => efi::Status::SUCCESS, + Err(err) => err, + } } - } - - // Driver Binding Stop FFI function - // Handles driver_binding_stop calls from the UEFI core by passing them to the trait implementation. - extern "efiapi" fn driver_binding_stop( - this: *mut protocols::driver_binding::Protocol, - controller: efi::Handle, - _num_children: usize, - _child_handle_buffer: *mut efi::Handle, - ) -> efi::Status { - let uefi_binding = unsafe { (this as *mut UefiDriverBinding).as_mut() }.expect("bad this pointer"); - match uefi_binding.binding.driver_binding_stop(uefi_binding.boot_services, controller) { - Ok(_) => efi::Status::SUCCESS, - Err(err) => err, + + // Driver Binding Stop FFI function + // Handles driver_binding_stop calls from the UEFI core by passing them to the trait implementation. + extern "efiapi" fn driver_binding_stop( + this: *mut protocols::driver_binding::Protocol, + controller: efi::Handle, + _num_children: usize, + _child_handle_buffer: *mut efi::Handle, + ) -> efi::Status { + let uefi_binding = unsafe { (this as *mut UefiDriverBinding).as_mut() }.expect("bad this pointer"); + match uefi_binding.binding.driver_binding_stop(uefi_binding.boot_services, controller) { + Ok(_) => efi::Status::SUCCESS, + Err(err) => err, + } } - } } #[cfg(test)] mod test { - use super::{MockDriverBinding, UefiDriverBinding}; - use crate::boot_services::MockUefiBootServices; - use r_efi::{efi, protocols}; - - // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used - // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock - // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. - // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. - // This object needs to outlive anything that uses it - once created, it will live until the end of the program. - fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { - unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } - } - - #[test] - fn new_should_instantiate_new_uefi_driver_binding() { - let boot_services = create_fake_static_boot_service(); - let binding = MockDriverBinding::new(); - let handle = 0x1234 as efi::Handle; - let driver_binding = UefiDriverBinding::new(boot_services, Box::new(binding), handle); - - assert!(driver_binding.uefi_binding.supported == UefiDriverBinding::driver_binding_supported); - assert!(driver_binding.uefi_binding.start == UefiDriverBinding::driver_binding_start); - assert!(driver_binding.uefi_binding.stop == UefiDriverBinding::driver_binding_stop); - assert_eq!(driver_binding.uefi_binding.version, 1); - assert_eq!(driver_binding.uefi_binding.image_handle, handle); - assert_eq!(driver_binding.uefi_binding.driver_binding_handle, handle); - } - - #[test] - fn install_should_install_the_driver_binding() { - let boot_services = create_fake_static_boot_service(); - //expect a call to install_protocol_interface - boot_services - .expect_install_protocol_interface() - .withf(|handle, protocol, interface_type, interface| { - assert_ne!(*handle, core::ptr::null_mut()); - assert_eq!(unsafe { **protocol }, protocols::driver_binding::PROTOCOL_GUID); - assert_eq!(*interface_type, efi::NATIVE_INTERFACE); - assert_ne!(*interface, core::ptr::null_mut()); - true - }) - .returning(|_, _, _, _| efi::Status::SUCCESS); - - let handle = 0x1234 as efi::Handle; - - let binding = MockDriverBinding::new(); - let driver_binding = UefiDriverBinding::new(boot_services, Box::new(binding), handle); - driver_binding.install().unwrap(); - } - - #[test] - fn install_should_report_failures_to_install_the_driver_binding() { - let boot_services = create_fake_static_boot_service(); - - //expect a call to install_protocol_interface - boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::OUT_OF_RESOURCES); - - let handle = 0x1234 as efi::Handle; - - let binding = MockDriverBinding::new(); - let driver_binding = UefiDriverBinding::new(boot_services, Box::new(binding), handle); - assert_eq!(driver_binding.install(), Err(efi::Status::OUT_OF_RESOURCES)); - } - - #[test] - fn uninstall_should_uninstall_the_driver_binding() { - let boot_services = create_fake_static_boot_service(); - //expect a call to install_protocol_interface - boot_services.expect_install_protocol_interface().returning(|handle, protocol, interface_type, interface| { - unsafe { - assert_ne!(handle.read(), core::ptr::null_mut()); - assert_eq!(protocol.read(), protocols::driver_binding::PROTOCOL_GUID); - assert_eq!(interface_type, efi::NATIVE_INTERFACE); - assert_ne!(interface, core::ptr::null_mut()); - } - efi::Status::SUCCESS - }); - boot_services.expect_uninstall_protocol_interface().returning(|handle, protocol, interface| { - assert_ne!(handle, core::ptr::null_mut()); - assert_eq!(unsafe { protocol.read() }, protocols::driver_binding::PROTOCOL_GUID); - assert_ne!(interface, core::ptr::null_mut()); - efi::Status::SUCCESS - }); - let handle = 0x1234 as efi::Handle; - - let binding = MockDriverBinding::new(); - let driver_binding = UefiDriverBinding::new(boot_services, Box::new(binding), handle); - let binding_ptr = driver_binding.install().unwrap(); - - let driver_binding = unsafe { UefiDriverBinding::uninstall(binding_ptr) }.unwrap(); - - assert!(driver_binding.uefi_binding.supported == UefiDriverBinding::driver_binding_supported); - assert!(driver_binding.uefi_binding.start == UefiDriverBinding::driver_binding_start); - assert!(driver_binding.uefi_binding.stop == UefiDriverBinding::driver_binding_stop); - assert_eq!(driver_binding.uefi_binding.version, 1); - assert_eq!(driver_binding.uefi_binding.image_handle, handle); - assert_eq!(driver_binding.uefi_binding.driver_binding_handle, handle); - } - - #[test] - fn uninstall_should_report_failures_to_uninstall_the_driver() { - let boot_services = create_fake_static_boot_service(); - - //expect a call to install_protocol_interface - boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); - boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::INVALID_PARAMETER); - - let handle = 0x1234 as efi::Handle; - - let binding = MockDriverBinding::new(); - let driver_binding = UefiDriverBinding::new(boot_services, Box::new(binding), handle); - let binding_ptr = driver_binding.install().unwrap(); - - assert_eq!(unsafe { UefiDriverBinding::uninstall(binding_ptr) }.err(), Some(efi::Status::INVALID_PARAMETER)); - } - - #[test] - fn driver_binding_should_call_driver_binding_routines() { - let boot_services = create_fake_static_boot_service(); - //expect a call to install_protocol_interface - boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); - - let mut binding = MockDriverBinding::new(); - binding.expect_driver_binding_supported().returning(|_, _| Ok(())); - binding.expect_driver_binding_start().returning(|_, _| Ok(())); - binding.expect_driver_binding_stop().returning(|_, _| Ok(())); - - let handle = 0x1234 as efi::Handle; - let driver_binding = UefiDriverBinding::new(boot_services, Box::new(binding), handle); - let binding_ptr = driver_binding.install().unwrap(); - - let driver_binding_ref = unsafe { binding_ptr.as_ref().unwrap() }; - let this_ptr = binding_ptr as *mut protocols::driver_binding::Protocol; - - let controller_handle = 0x4321 as efi::Handle; - assert_eq!( - (driver_binding_ref.uefi_binding.supported)(this_ptr, controller_handle, core::ptr::null_mut()), - efi::Status::SUCCESS - ); - assert_eq!( - (driver_binding_ref.uefi_binding.start)(this_ptr, controller_handle, core::ptr::null_mut()), - efi::Status::SUCCESS - ); - assert_eq!( - (driver_binding_ref.uefi_binding.stop)(this_ptr, controller_handle, 0, core::ptr::null_mut()), - efi::Status::SUCCESS - ); - } + use super::{MockDriverBinding, UefiDriverBinding}; + use crate::boot_services::MockUefiBootServices; + use r_efi::{efi, protocols}; + + // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used + // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock + // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. + // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. + // This object needs to outlive anything that uses it - once created, it will live until the end of the program. + fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { + unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } + } + + #[test] + fn new_should_instantiate_new_uefi_driver_binding() { + let boot_services = create_fake_static_boot_service(); + let binding = MockDriverBinding::new(); + let handle = 0x1234 as efi::Handle; + let driver_binding = UefiDriverBinding::new(boot_services, Box::new(binding), handle); + + assert!(driver_binding.uefi_binding.supported == UefiDriverBinding::driver_binding_supported); + assert!(driver_binding.uefi_binding.start == UefiDriverBinding::driver_binding_start); + assert!(driver_binding.uefi_binding.stop == UefiDriverBinding::driver_binding_stop); + assert_eq!(driver_binding.uefi_binding.version, 1); + assert_eq!(driver_binding.uefi_binding.image_handle, handle); + assert_eq!(driver_binding.uefi_binding.driver_binding_handle, handle); + } + + #[test] + fn install_should_install_the_driver_binding() { + let boot_services = create_fake_static_boot_service(); + //expect a call to install_protocol_interface + boot_services + .expect_install_protocol_interface() + .withf(|handle, protocol, interface_type, interface| { + assert_ne!(*handle, core::ptr::null_mut()); + assert_eq!(unsafe { **protocol }, protocols::driver_binding::PROTOCOL_GUID); + assert_eq!(*interface_type, efi::NATIVE_INTERFACE); + assert_ne!(*interface, core::ptr::null_mut()); + true + }) + .returning(|_, _, _, _| efi::Status::SUCCESS); + + let handle = 0x1234 as efi::Handle; + + let binding = MockDriverBinding::new(); + let driver_binding = UefiDriverBinding::new(boot_services, Box::new(binding), handle); + driver_binding.install().unwrap(); + } + + #[test] + fn install_should_report_failures_to_install_the_driver_binding() { + let boot_services = create_fake_static_boot_service(); + + //expect a call to install_protocol_interface + boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::OUT_OF_RESOURCES); + + let handle = 0x1234 as efi::Handle; + + let binding = MockDriverBinding::new(); + let driver_binding = UefiDriverBinding::new(boot_services, Box::new(binding), handle); + assert_eq!(driver_binding.install(), Err(efi::Status::OUT_OF_RESOURCES)); + } + + #[test] + fn uninstall_should_uninstall_the_driver_binding() { + let boot_services = create_fake_static_boot_service(); + //expect a call to install_protocol_interface + boot_services.expect_install_protocol_interface().returning(|handle, protocol, interface_type, interface| { + unsafe { + assert_ne!(handle.read(), core::ptr::null_mut()); + assert_eq!(protocol.read(), protocols::driver_binding::PROTOCOL_GUID); + assert_eq!(interface_type, efi::NATIVE_INTERFACE); + assert_ne!(interface, core::ptr::null_mut()); + } + efi::Status::SUCCESS + }); + boot_services.expect_uninstall_protocol_interface().returning(|handle, protocol, interface| { + assert_ne!(handle, core::ptr::null_mut()); + assert_eq!(unsafe { protocol.read() }, protocols::driver_binding::PROTOCOL_GUID); + assert_ne!(interface, core::ptr::null_mut()); + efi::Status::SUCCESS + }); + let handle = 0x1234 as efi::Handle; + + let binding = MockDriverBinding::new(); + let driver_binding = UefiDriverBinding::new(boot_services, Box::new(binding), handle); + let binding_ptr = driver_binding.install().unwrap(); + + let driver_binding = unsafe { UefiDriverBinding::uninstall(binding_ptr) }.unwrap(); + + assert!(driver_binding.uefi_binding.supported == UefiDriverBinding::driver_binding_supported); + assert!(driver_binding.uefi_binding.start == UefiDriverBinding::driver_binding_start); + assert!(driver_binding.uefi_binding.stop == UefiDriverBinding::driver_binding_stop); + assert_eq!(driver_binding.uefi_binding.version, 1); + assert_eq!(driver_binding.uefi_binding.image_handle, handle); + assert_eq!(driver_binding.uefi_binding.driver_binding_handle, handle); + } + + #[test] + fn uninstall_should_report_failures_to_uninstall_the_driver() { + let boot_services = create_fake_static_boot_service(); + + //expect a call to install_protocol_interface + boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); + boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::INVALID_PARAMETER); + + let handle = 0x1234 as efi::Handle; + + let binding = MockDriverBinding::new(); + let driver_binding = UefiDriverBinding::new(boot_services, Box::new(binding), handle); + let binding_ptr = driver_binding.install().unwrap(); + + assert_eq!(unsafe { UefiDriverBinding::uninstall(binding_ptr) }.err(), Some(efi::Status::INVALID_PARAMETER)); + } + + #[test] + fn driver_binding_should_call_driver_binding_routines() { + let boot_services = create_fake_static_boot_service(); + //expect a call to install_protocol_interface + boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); + + let mut binding = MockDriverBinding::new(); + binding.expect_driver_binding_supported().returning(|_, _| Ok(())); + binding.expect_driver_binding_start().returning(|_, _| Ok(())); + binding.expect_driver_binding_stop().returning(|_, _| Ok(())); + + let handle = 0x1234 as efi::Handle; + let driver_binding = UefiDriverBinding::new(boot_services, Box::new(binding), handle); + let binding_ptr = driver_binding.install().unwrap(); + + let driver_binding_ref = unsafe { binding_ptr.as_ref().unwrap() }; + let this_ptr = binding_ptr as *mut protocols::driver_binding::Protocol; + + let controller_handle = 0x4321 as efi::Handle; + assert_eq!( + (driver_binding_ref.uefi_binding.supported)(this_ptr, controller_handle, core::ptr::null_mut()), + efi::Status::SUCCESS + ); + assert_eq!( + (driver_binding_ref.uefi_binding.start)(this_ptr, controller_handle, core::ptr::null_mut()), + efi::Status::SUCCESS + ); + assert_eq!( + (driver_binding_ref.uefi_binding.stop)(this_ptr, controller_handle, 0, core::ptr::null_mut()), + efi::Status::SUCCESS + ); + } } diff --git a/HidPkg/UefiHidDxeV2/src/hid.rs b/HidPkg/UefiHidDxeV2/src/hid.rs index 1652019101..37fbd2dc1d 100644 --- a/HidPkg/UefiHidDxeV2/src/hid.rs +++ b/HidPkg/UefiHidDxeV2/src/hid.rs @@ -44,9 +44,9 @@ use core::ffi::c_void; use crate::{ - boot_services::UefiBootServices, - driver_binding::DriverBinding, - hid_io::{HidIo, HidIoFactory, HidReportReceiver}, + boot_services::UefiBootServices, + driver_binding::DriverBinding, + hid_io::{HidIo, HidIoFactory, HidReportReceiver}, }; use alloc::{boxed::Box, vec::Vec}; @@ -62,8 +62,8 @@ use mockall::automock; /// This is used to specify to a HidFactory how it should instantiate new receivers for HID reports. #[cfg_attr(test, automock)] pub trait HidReceiverFactory { - /// Generates a vector of [`crate::hid_io::HidReportReceiver`] trait objects that can handle reports from the given controller. - fn new_hid_receiver_list(&self, controller: efi::Handle) -> Result>, efi::Status>; + /// Generates a vector of [`crate::hid_io::HidReportReceiver`] trait objects that can handle reports from the given controller. + fn new_hid_receiver_list(&self, controller: efi::Handle) -> Result>, efi::Status>; } // Context structure used to track HID instances being managed. @@ -72,39 +72,39 @@ pub trait HidReceiverFactory { // Wrapping it in HidInstance makes it a fixed size type: *mut HidInstance is a thin pointer that can be cast back and // forth to c_void. struct HidInstance { - _hid_io: Box, + _hid_io: Box, } impl HidInstance { - // This guid {fb719b29-fda7-4359-ac68-0d46c31a7a7e} is used to associate a HidInstance with a given controller by - // installing it as a protocol on the controller handle. - const PRIVATE_HID_CONTEXT_GUID: efi::Guid = - efi::Guid::from_fields(0xfb719b29, 0xfda7, 0x4359, 0xac, 0x68, &[0x0d, 0x46, 0xc3, 0x1a, 0x7a, 0x7e]); - - //create a new hid instance from - fn new(hid_io: Box) -> Self { - HidInstance { _hid_io: hid_io } - } + // This guid {fb719b29-fda7-4359-ac68-0d46c31a7a7e} is used to associate a HidInstance with a given controller by + // installing it as a protocol on the controller handle. + const PRIVATE_HID_CONTEXT_GUID: efi::Guid = + efi::Guid::from_fields(0xfb719b29, 0xfda7, 0x4359, 0xac, 0x68, &[0x0d, 0x46, 0xc3, 0x1a, 0x7a, 0x7e]); + + //create a new hid instance from + fn new(hid_io: Box) -> Self { + HidInstance { _hid_io: hid_io } + } } // Structure used to manage multiple receivers and split reports between them. struct HidSplitter { - receivers: Vec>, + receivers: Vec>, } impl HidReportReceiver for HidSplitter { - //initialize is not expected, since hid splitter is generic for all - // controllers and is fully initialized when constructed. - fn initialize(&mut self, _controller: efi::Handle, _hid_io: &dyn HidIo) -> Result<(), efi::Status> { - panic!("initialize not expected for HidSplitter") - } - - //iterates over the receivers and passes the report to each one. - fn receive_report(&mut self, report: &[u8], hid_io: &dyn HidIo) { - for receiver in &mut self.receivers { - receiver.receive_report(report, hid_io) + //initialize is not expected, since hid splitter is generic for all + // controllers and is fully initialized when constructed. + fn initialize(&mut self, _controller: efi::Handle, _hid_io: &dyn HidIo) -> Result<(), efi::Status> { + panic!("initialize not expected for HidSplitter") + } + + //iterates over the receivers and passes the report to each one. + fn receive_report(&mut self, report: &[u8], hid_io: &dyn HidIo) { + for receiver in &mut self.receivers { + receiver.receive_report(report, hid_io) + } } - } } /// This structure implements provides an implementation of @@ -112,311 +112,312 @@ impl HidReportReceiver for HidSplitter { /// whenever [`HidFactory::driver_binding_start`] is called to manage a given /// controller. pub struct HidFactory { - hid_io_factory: Box, - receiver_factory: Box, - agent: efi::Handle, -} - -impl HidFactory { - /// Creates a new "HidFactory". - /// - /// When a new HidInstance is spawned by - /// [`HidFactory::driver_binding_start`], `hid_io_factory` is used to create a - /// new [`crate::hid_io::HidIo`] instance to interact with the controller, and - /// `receiver_factory` is used to create new - /// [`crate::hid_io::HidReportReceiver`] that will process reports from the - /// managed controller. - /// - /// `agent` is the handle on which the driver binding instance should be - /// installed (expected to be the image_handle for this driver). - pub fn new( hid_io_factory: Box, receiver_factory: Box, agent: efi::Handle, - ) -> Self { - HidFactory { hid_io_factory, receiver_factory, agent } - } +} + +impl HidFactory { + /// Creates a new "HidFactory". + /// + /// When a new HidInstance is spawned by + /// [`HidFactory::driver_binding_start`], `hid_io_factory` is used to create a + /// new [`crate::hid_io::HidIo`] instance to interact with the controller, and + /// `receiver_factory` is used to create new + /// [`crate::hid_io::HidReportReceiver`] that will process reports from the + /// managed controller. + /// + /// `agent` is the handle on which the driver binding instance should be + /// installed (expected to be the image_handle for this driver). + pub fn new( + hid_io_factory: Box, + receiver_factory: Box, + agent: efi::Handle, + ) -> Self { + HidFactory { hid_io_factory, receiver_factory, agent } + } } impl DriverBinding for HidFactory { - /// Verifies the given controller supports HidIo - /// - /// This is done by attempting the instantiation of a HidIo instance on it - /// using the HidIoFactory provided at construction - if that succeeds, the - /// controller is considered supported. Note that the actual HidIo instance - /// constructed for the test is dropped on return. - fn driver_binding_supported( - &mut self, - _boot_services: &'static dyn UefiBootServices, - controller: r_efi::efi::Handle, - ) -> Result<(), efi::Status> { - self.hid_io_factory.new_hid_io(controller, true).map(|_| ()) - } - - /// Starts a new HID instance. - /// - /// Starts Hid support for the given controller. The HidIoFactory provided at - /// construction is used to create a new HidIo trait object to manage the - /// controller, and new receivers are instantiated using the - /// HidReceiverFactory provided at construction. A private "HidInstance" - /// structure is created and associated with the controller to own these - /// objects as long as the instance is "running" - i.e. until - /// [`Self::driver_binding_stop`] is invoked for the controller. - fn driver_binding_start( - &mut self, - boot_services: &'static dyn UefiBootServices, - controller: r_efi::efi::Handle, - ) -> Result<(), efi::Status> { - let mut hid_io = self.hid_io_factory.new_hid_io(controller, true)?; - - let mut hid_splitter = Box::new(HidSplitter { receivers: Vec::new() }); - - for mut receiver in self.receiver_factory.new_hid_receiver_list(controller)? { - if receiver.initialize(controller, hid_io.as_mut()).is_ok() { - hid_splitter.receivers.push(receiver); - } + /// Verifies the given controller supports HidIo + /// + /// This is done by attempting the instantiation of a HidIo instance on it + /// using the HidIoFactory provided at construction - if that succeeds, the + /// controller is considered supported. Note that the actual HidIo instance + /// constructed for the test is dropped on return. + fn driver_binding_supported( + &mut self, + _boot_services: &'static dyn UefiBootServices, + controller: r_efi::efi::Handle, + ) -> Result<(), efi::Status> { + self.hid_io_factory.new_hid_io(controller, true).map(|_| ()) + } + + /// Starts a new HID instance. + /// + /// Starts Hid support for the given controller. The HidIoFactory provided at + /// construction is used to create a new HidIo trait object to manage the + /// controller, and new receivers are instantiated using the + /// HidReceiverFactory provided at construction. A private "HidInstance" + /// structure is created and associated with the controller to own these + /// objects as long as the instance is "running" - i.e. until + /// [`Self::driver_binding_stop`] is invoked for the controller. + fn driver_binding_start( + &mut self, + boot_services: &'static dyn UefiBootServices, + controller: r_efi::efi::Handle, + ) -> Result<(), efi::Status> { + let mut hid_io = self.hid_io_factory.new_hid_io(controller, true)?; + + let mut hid_splitter = Box::new(HidSplitter { receivers: Vec::new() }); + + for mut receiver in self.receiver_factory.new_hid_receiver_list(controller)? { + if receiver.initialize(controller, hid_io.as_mut()).is_ok() { + hid_splitter.receivers.push(receiver); + } + } + + if hid_splitter.receivers.is_empty() { + return Err(efi::Status::UNSUPPORTED); + } + + hid_io.set_report_receiver(hid_splitter)?; + + let hid_instance = Box::into_raw(Box::new(HidInstance::new(hid_io))); + + let mut handle = controller; + let status = boot_services.install_protocol_interface( + core::ptr::addr_of_mut!(handle), + &HidInstance::PRIVATE_HID_CONTEXT_GUID as *const efi::Guid as *mut efi::Guid, + efi::NATIVE_INTERFACE, + hid_instance as *mut c_void, + ); + if status != efi::Status::SUCCESS { + drop(unsafe { Box::from_raw(hid_instance) }); + return Err(status); + } + Ok(()) } - if hid_splitter.receivers.is_empty() { - return Err(efi::Status::UNSUPPORTED); + /// Stops a running HID instance. + /// + /// Stops Hid support for the given controller. The private "HidInstance" + /// created by [`Self::driver_binding_start`] is reclaimed and dropped. + fn driver_binding_stop( + &mut self, + boot_services: &'static dyn UefiBootServices, + controller: r_efi::efi::Handle, + ) -> Result<(), efi::Status> { + let mut hid_instance: *mut HidInstance = core::ptr::null_mut(); + + let status = boot_services.open_protocol( + controller, + &HidInstance::PRIVATE_HID_CONTEXT_GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::addr_of_mut!(hid_instance) as *mut *mut c_void, + self.agent, + controller, + efi::OPEN_PROTOCOL_GET_PROTOCOL, + ); + if status != efi::Status::SUCCESS { + return Err(status); + } + + let status = boot_services.uninstall_protocol_interface( + controller, + &HidInstance::PRIVATE_HID_CONTEXT_GUID as *const efi::Guid as *mut efi::Guid, + hid_instance as *mut c_void, + ); + if status != efi::Status::SUCCESS { + debugln!(DEBUG_ERROR, "hid::driver_binding_stop: unexpected failure return: {:x?}", status); + } + + drop(unsafe { Box::from_raw(hid_instance) }); + Ok(()) } +} + +#[cfg(test)] +mod test { + use core::ffi::c_void; - hid_io.set_report_receiver(hid_splitter)?; + use r_efi::efi; - let hid_instance = Box::into_raw(Box::new(HidInstance::new(hid_io))); + use crate::{ + boot_services::MockUefiBootServices, + driver_binding::DriverBinding, + hid_io::{HidReportReceiver, MockHidIo, MockHidIoFactory, MockHidReportReceiver}, + }; - let mut handle = controller; - let status = boot_services.install_protocol_interface( - core::ptr::addr_of_mut!(handle), - &HidInstance::PRIVATE_HID_CONTEXT_GUID as *const efi::Guid as *mut efi::Guid, - efi::NATIVE_INTERFACE, - hid_instance as *mut c_void, - ); - if status != efi::Status::SUCCESS { - drop(unsafe { Box::from_raw(hid_instance) }); - return Err(status); + use super::{HidFactory, HidSplitter, MockHidReceiverFactory}; + + // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used + // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock + // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. + // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. + // This object needs to outlive anything that uses it - once created, it will live until the end of the program. + fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { + unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } } - Ok(()) - } - - /// Stops a running HID instance. - /// - /// Stops Hid support for the given controller. The private "HidInstance" - /// created by [`Self::driver_binding_start`] is reclaimed and dropped. - fn driver_binding_stop( - &mut self, - boot_services: &'static dyn UefiBootServices, - controller: r_efi::efi::Handle, - ) -> Result<(), efi::Status> { - let mut hid_instance: *mut HidInstance = core::ptr::null_mut(); - - let status = boot_services.open_protocol( - controller, - &HidInstance::PRIVATE_HID_CONTEXT_GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::addr_of_mut!(hid_instance) as *mut *mut c_void, - self.agent, - controller, - efi::OPEN_PROTOCOL_GET_PROTOCOL, - ); - if status != efi::Status::SUCCESS { - return Err(status); + + #[test] + fn driver_binding_supported_should_indicate_support() { + let boot_services = create_fake_static_boot_service(); + + let mut hid_io_factory = Box::new(MockHidIoFactory::new()); + //handle 0x3 should return success. + hid_io_factory + .expect_new_hid_io() + .withf_st(|controller, _| *controller == 0x3 as efi::Handle) + .returning(|_, _| Ok(Box::new(MockHidIo::new()))); + //default for any other handles + hid_io_factory.expect_new_hid_io().returning(|_, _| Err(efi::Status::UNSUPPORTED)); + + let receiver_factory = Box::new(MockHidReceiverFactory::new()); + let agent = 0x1 as efi::Handle; + let mut hid_factory = HidFactory::new(hid_io_factory, receiver_factory, agent); + + let controller = 0x2 as efi::Handle; + assert_eq!(hid_factory.driver_binding_supported(boot_services, controller), Err(efi::Status::UNSUPPORTED)); + + let controller = 0x3 as efi::Handle; + assert!(hid_factory.driver_binding_supported(boot_services, controller).is_ok()); } - let status = boot_services.uninstall_protocol_interface( - controller, - &HidInstance::PRIVATE_HID_CONTEXT_GUID as *const efi::Guid as *mut efi::Guid, - hid_instance as *mut c_void, - ); - if status != efi::Status::SUCCESS { - debugln!(DEBUG_ERROR, "hid::driver_binding_stop: unexpected failure return: {:x?}", status); + #[test] + fn driver_binding_start_should_not_start_when_not_supported() { + let boot_services = create_fake_static_boot_service(); + let mut hid_io_factory = Box::new(MockHidIoFactory::new()); + hid_io_factory + .expect_new_hid_io() + .withf_st(|controller, _| *controller == 0x3 as efi::Handle) + .returning(|_, _| Ok(Box::new(MockHidIo::new()))); + hid_io_factory + .expect_new_hid_io() + .withf_st(|controller, _| *controller == 0x4 as efi::Handle) + .returning(|_, _| Ok(Box::new(MockHidIo::new()))); + //default for any other handles + hid_io_factory.expect_new_hid_io().returning(|_, _| Err(efi::Status::UNSUPPORTED)); + + let mut receiver_factory = Box::new(MockHidReceiverFactory::new()); + receiver_factory + .expect_new_hid_receiver_list() + .withf_st(|controller| *controller == 0x4 as efi::Handle) + .returning(|_| Ok(Vec::new())); + receiver_factory.expect_new_hid_receiver_list().returning(|_| Err(efi::Status::UNSUPPORTED)); + + let agent = 0x1 as efi::Handle; + let mut hid_factory = HidFactory::new(hid_io_factory, receiver_factory, agent); + + // test: no hid_io on the handle. + let controller = 0x02 as efi::Handle; + assert_eq!(hid_factory.driver_binding_start(boot_services, controller), Err(efi::Status::UNSUPPORTED)); + + // test: hid_io present, but failed to retrieve receivers. + let controller = 0x03 as efi::Handle; + assert_eq!(hid_factory.driver_binding_start(boot_services, controller), Err(efi::Status::UNSUPPORTED)); + + // test: hid_io present, empty receiver list. + let controller = 0x04 as efi::Handle; + assert_eq!(hid_factory.driver_binding_start(boot_services, controller), Err(efi::Status::UNSUPPORTED)); + + let boot_services = create_fake_static_boot_service(); + + //test: hid_io present, receiver present, receiver init indicates no support. + let mut hid_io_factory = Box::new(MockHidIoFactory::new()); + hid_io_factory.expect_new_hid_io().returning(|_, _| Ok(Box::new(MockHidIo::new()))); + + let mut receiver_factory = Box::new(MockHidReceiverFactory::new()); + receiver_factory.expect_new_hid_receiver_list().returning(|_| { + let mut hid_receiver = MockHidReportReceiver::new(); + hid_receiver.expect_initialize().returning(|_, _| Err(efi::Status::UNSUPPORTED)); + Ok(vec![Box::new(hid_receiver)]) + }); + + let mut hid_factory = HidFactory::new(hid_io_factory, receiver_factory, agent); + let controller = 0x02 as efi::Handle; + assert_eq!(hid_factory.driver_binding_start(boot_services, controller), Err(efi::Status::UNSUPPORTED)); } - drop(unsafe { Box::from_raw(hid_instance) }); - Ok(()) - } -} + #[test] + fn driver_binding_start_should_start_when_supported() { + let boot_services = create_fake_static_boot_service(); + let agent = 0x1 as efi::Handle; -#[cfg(test)] -mod test { - use core::ffi::c_void; + let mut hid_io_factory = Box::new(MockHidIoFactory::new()); + hid_io_factory.expect_new_hid_io().returning(|_, _| { + let mut hid_io = MockHidIo::new(); + hid_io.expect_set_report_receiver().returning(|_| Ok(())); + Ok(Box::new(hid_io)) + }); - use r_efi::efi; + let mut receiver_factory = Box::new(MockHidReceiverFactory::new()); + receiver_factory.expect_new_hid_receiver_list().returning(|_| { + let mut hid_receiver = MockHidReportReceiver::new(); + hid_receiver.expect_initialize().returning(|_, _| Ok(())); + Ok(vec![Box::new(hid_receiver)]) + }); - use crate::{ - boot_services::MockUefiBootServices, - driver_binding::DriverBinding, - hid_io::{HidReportReceiver, MockHidIo, MockHidIoFactory, MockHidReportReceiver}, - }; - - use super::{HidFactory, HidSplitter, MockHidReceiverFactory}; - - // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used - // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock - // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. - // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. - // This object needs to outlive anything that uses it - once created, it will live until the end of the program. - fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { - unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } - } - - #[test] - fn driver_binding_supported_should_indicate_support() { - let boot_services = create_fake_static_boot_service(); - - let mut hid_io_factory = Box::new(MockHidIoFactory::new()); - //handle 0x3 should return success. - hid_io_factory - .expect_new_hid_io() - .withf_st(|controller, _| *controller == 0x3 as efi::Handle) - .returning(|_, _| Ok(Box::new(MockHidIo::new()))); - //default for any other handles - hid_io_factory.expect_new_hid_io().returning(|_, _| Err(efi::Status::UNSUPPORTED)); - - let receiver_factory = Box::new(MockHidReceiverFactory::new()); - let agent = 0x1 as efi::Handle; - let mut hid_factory = HidFactory::new(hid_io_factory, receiver_factory, agent); - - let controller = 0x2 as efi::Handle; - assert_eq!(hid_factory.driver_binding_supported(boot_services, controller), Err(efi::Status::UNSUPPORTED)); - - let controller = 0x3 as efi::Handle; - assert!(hid_factory.driver_binding_supported(boot_services, controller).is_ok()); - } - - #[test] - fn driver_binding_start_should_not_start_when_not_supported() { - let boot_services = create_fake_static_boot_service(); - let mut hid_io_factory = Box::new(MockHidIoFactory::new()); - hid_io_factory - .expect_new_hid_io() - .withf_st(|controller, _| *controller == 0x3 as efi::Handle) - .returning(|_, _| Ok(Box::new(MockHidIo::new()))); - hid_io_factory - .expect_new_hid_io() - .withf_st(|controller, _| *controller == 0x4 as efi::Handle) - .returning(|_, _| Ok(Box::new(MockHidIo::new()))); - //default for any other handles - hid_io_factory.expect_new_hid_io().returning(|_, _| Err(efi::Status::UNSUPPORTED)); - - let mut receiver_factory = Box::new(MockHidReceiverFactory::new()); - receiver_factory - .expect_new_hid_receiver_list() - .withf_st(|controller| *controller == 0x4 as efi::Handle) - .returning(|_| Ok(Vec::new())); - receiver_factory.expect_new_hid_receiver_list().returning(|_| Err(efi::Status::UNSUPPORTED)); - - let agent = 0x1 as efi::Handle; - let mut hid_factory = HidFactory::new(hid_io_factory, receiver_factory, agent); - - // test: no hid_io on the handle. - let controller = 0x02 as efi::Handle; - assert_eq!(hid_factory.driver_binding_start(boot_services, controller), Err(efi::Status::UNSUPPORTED)); - - // test: hid_io present, but failed to retrieve receivers. - let controller = 0x03 as efi::Handle; - assert_eq!(hid_factory.driver_binding_start(boot_services, controller), Err(efi::Status::UNSUPPORTED)); - - // test: hid_io present, empty receiver list. - let controller = 0x04 as efi::Handle; - assert_eq!(hid_factory.driver_binding_start(boot_services, controller), Err(efi::Status::UNSUPPORTED)); - - let boot_services = create_fake_static_boot_service(); - - //test: hid_io present, receiver present, receiver init indicates no support. - let mut hid_io_factory = Box::new(MockHidIoFactory::new()); - hid_io_factory.expect_new_hid_io().returning(|_, _| Ok(Box::new(MockHidIo::new()))); - - let mut receiver_factory = Box::new(MockHidReceiverFactory::new()); - receiver_factory.expect_new_hid_receiver_list().returning(|_| { - let mut hid_receiver = MockHidReportReceiver::new(); - hid_receiver.expect_initialize().returning(|_, _| Err(efi::Status::UNSUPPORTED)); - Ok(vec![Box::new(hid_receiver)]) - }); - - let mut hid_factory = HidFactory::new(hid_io_factory, receiver_factory, agent); - let controller = 0x02 as efi::Handle; - assert_eq!(hid_factory.driver_binding_start(boot_services, controller), Err(efi::Status::UNSUPPORTED)); - } - - #[test] - fn driver_binding_start_should_start_when_supported() { - let boot_services = create_fake_static_boot_service(); - let agent = 0x1 as efi::Handle; - - let mut hid_io_factory = Box::new(MockHidIoFactory::new()); - hid_io_factory.expect_new_hid_io().returning(|_, _| { - let mut hid_io = MockHidIo::new(); - hid_io.expect_set_report_receiver().returning(|_| Ok(())); - Ok(Box::new(hid_io)) - }); - - let mut receiver_factory = Box::new(MockHidReceiverFactory::new()); - receiver_factory.expect_new_hid_receiver_list().returning(|_| { - let mut hid_receiver = MockHidReportReceiver::new(); - hid_receiver.expect_initialize().returning(|_, _| Ok(())); - Ok(vec![Box::new(hid_receiver)]) - }); - - boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); - - let mut hid_factory = HidFactory::new(hid_io_factory, receiver_factory, agent); - let controller = 0x02 as efi::Handle; - hid_factory.driver_binding_start(boot_services, controller).unwrap(); - - //test note: this will leak a HidInstance. - } - - #[test] - fn driver_binding_start_should_stop_after_start() { - let boot_services = create_fake_static_boot_service(); - let agent = 0x1 as efi::Handle; - - let mut hid_io_factory = Box::new(MockHidIoFactory::new()); - hid_io_factory.expect_new_hid_io().returning(|_, _| { - let mut hid_io = MockHidIo::new(); - hid_io.expect_set_report_receiver().returning(|_| Ok(())); - Ok(Box::new(hid_io)) - }); - - let mut receiver_factory = Box::new(MockHidReceiverFactory::new()); - receiver_factory.expect_new_hid_receiver_list().returning(|_| { - let mut hid_receiver = MockHidReportReceiver::new(); - hid_receiver.expect_initialize().returning(|_, _| Ok(())); - Ok(vec![Box::new(hid_receiver)]) - }); - - static mut HID_INSTANCE_PTR: *mut c_void = core::ptr::null_mut(); - boot_services.expect_install_protocol_interface().returning(|_, _, _, instance| { - unsafe { HID_INSTANCE_PTR = instance }; - efi::Status::SUCCESS - }); - - boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { - unsafe { *interface = HID_INSTANCE_PTR }; - efi::Status::SUCCESS - }); - - boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); - - let mut hid_factory = HidFactory::new(hid_io_factory, receiver_factory, agent); - let controller = 0x02 as efi::Handle; - hid_factory.driver_binding_start(boot_services, controller).unwrap(); - - assert_ne!(unsafe { HID_INSTANCE_PTR }, core::ptr::null_mut()); - - hid_factory.driver_binding_stop(boot_services, controller).unwrap(); - } - - #[test] - fn hid_splitter_should_split_things() { - let mut mock_hid_receiver1 = MockHidReportReceiver::new(); - mock_hid_receiver1.expect_receive_report().returning(|_, _| ()); - let mut mock_hid_receiver2 = MockHidReportReceiver::new(); - mock_hid_receiver2.expect_receive_report().returning(|_, _| ()); - let receivers: Vec> = vec![Box::new(mock_hid_receiver1), Box::new(mock_hid_receiver2)]; - - let mut hid_splitter = HidSplitter { receivers }; - let mock_hid_io = MockHidIo::new(); - hid_splitter.receive_report(&[0, 0, 0, 0], &mock_hid_io); - } + boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); + + let mut hid_factory = HidFactory::new(hid_io_factory, receiver_factory, agent); + let controller = 0x02 as efi::Handle; + hid_factory.driver_binding_start(boot_services, controller).unwrap(); + + //test note: this will leak a HidInstance. + } + + #[test] + fn driver_binding_start_should_stop_after_start() { + let boot_services = create_fake_static_boot_service(); + let agent = 0x1 as efi::Handle; + + let mut hid_io_factory = Box::new(MockHidIoFactory::new()); + hid_io_factory.expect_new_hid_io().returning(|_, _| { + let mut hid_io = MockHidIo::new(); + hid_io.expect_set_report_receiver().returning(|_| Ok(())); + Ok(Box::new(hid_io)) + }); + + let mut receiver_factory = Box::new(MockHidReceiverFactory::new()); + receiver_factory.expect_new_hid_receiver_list().returning(|_| { + let mut hid_receiver = MockHidReportReceiver::new(); + hid_receiver.expect_initialize().returning(|_, _| Ok(())); + Ok(vec![Box::new(hid_receiver)]) + }); + + static mut HID_INSTANCE_PTR: *mut c_void = core::ptr::null_mut(); + boot_services.expect_install_protocol_interface().returning(|_, _, _, instance| { + unsafe { HID_INSTANCE_PTR = instance }; + efi::Status::SUCCESS + }); + + boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { + unsafe { *interface = HID_INSTANCE_PTR }; + efi::Status::SUCCESS + }); + + boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); + + let mut hid_factory = HidFactory::new(hid_io_factory, receiver_factory, agent); + let controller = 0x02 as efi::Handle; + hid_factory.driver_binding_start(boot_services, controller).unwrap(); + + assert_ne!(unsafe { HID_INSTANCE_PTR }, core::ptr::null_mut()); + + hid_factory.driver_binding_stop(boot_services, controller).unwrap(); + } + + #[test] + fn hid_splitter_should_split_things() { + let mut mock_hid_receiver1 = MockHidReportReceiver::new(); + mock_hid_receiver1.expect_receive_report().returning(|_, _| ()); + let mut mock_hid_receiver2 = MockHidReportReceiver::new(); + mock_hid_receiver2.expect_receive_report().returning(|_, _| ()); + let receivers: Vec> = + vec![Box::new(mock_hid_receiver1), Box::new(mock_hid_receiver2)]; + + let mut hid_splitter = HidSplitter { receivers }; + let mock_hid_io = MockHidIo::new(); + hid_splitter.receive_report(&[0, 0, 0, 0], &mock_hid_io); + } } diff --git a/HidPkg/UefiHidDxeV2/src/hid_io.rs b/HidPkg/UefiHidDxeV2/src/hid_io.rs index 2d0b2fd3be..805bbedc8d 100644 --- a/HidPkg/UefiHidDxeV2/src/hid_io.rs +++ b/HidPkg/UefiHidDxeV2/src/hid_io.rs @@ -27,10 +27,10 @@ use crate::boot_services::UefiBootServices; /// Defines an interface to be implemented by logic that wants to receive hid reports. #[cfg_attr(test, automock)] pub trait HidReportReceiver { - /// Initializes the receiver. After this call, the receiver should be prepared to receive reports. - fn initialize(&mut self, controller: efi::Handle, hid_io: &dyn HidIo) -> Result<(), efi::Status>; - /// Called to pass a report to the receiver. - fn receive_report(&mut self, report: &[u8], hid_io: &dyn HidIo); + /// Initializes the receiver. After this call, the receiver should be prepared to receive reports. + fn initialize(&mut self, controller: efi::Handle, hid_io: &dyn HidIo) -> Result<(), efi::Status>; + /// Called to pass a report to the receiver. + fn receive_report(&mut self, report: &[u8], hid_io: &dyn HidIo); } /// Defines an interface to abstract interaction with the HidIo protocol. @@ -38,423 +38,423 @@ pub trait HidReportReceiver { /// Refer to: #[cfg_attr(test, automock)] pub trait HidIo { - /// Returns the parsed report descriptor for the device. - fn get_report_descriptor(&self) -> Result; - /// sends an output report to the device. - fn set_output_report(&self, id: Option, report: &[u8]) -> Result<(), efi::Status>; - /// configures a receiver to receive reports from the device and configures the device to send reports. - fn set_report_receiver(&mut self, receiver: Box) -> Result<(), efi::Status>; - /// removes the receiver and stops the device from sending reports. - fn take_report_receiver(&mut self) -> Option>; + /// Returns the parsed report descriptor for the device. + fn get_report_descriptor(&self) -> Result; + /// sends an output report to the device. + fn set_output_report(&self, id: Option, report: &[u8]) -> Result<(), efi::Status>; + /// configures a receiver to receive reports from the device and configures the device to send reports. + fn set_report_receiver(&mut self, receiver: Box) -> Result<(), efi::Status>; + /// removes the receiver and stops the device from sending reports. + fn take_report_receiver(&mut self) -> Option>; } /// Defines a factory interface for producing HidIo instances on a given controller. #[cfg_attr(test, automock)] pub trait HidIoFactory { - /// Creates a new instance of HidIo on the given controller. If `owned` is set, then the implementation - /// expects to have ownership of the device (to be released when dropped). If not `owned`, then the - /// HidIo instance is being opened for a transient non-owned usage and does not need to be released. - fn new_hid_io(&self, controller: efi::Handle, owned: bool) -> Result, efi::Status>; + /// Creates a new instance of HidIo on the given controller. If `owned` is set, then the implementation + /// expects to have ownership of the device (to be released when dropped). If not `owned`, then the + /// HidIo instance is being opened for a transient non-owned usage and does not need to be released. + fn new_hid_io(&self, controller: efi::Handle, owned: bool) -> Result, efi::Status>; } /// Implements the HidIoFactory interface using UEFI boot services to open HidIo protocols on supported controllers. pub struct UefiHidIoFactory { - boot_services: &'static dyn UefiBootServices, - agent: efi::Handle, + boot_services: &'static dyn UefiBootServices, + agent: efi::Handle, } impl UefiHidIoFactory { - /// Creates a new UefiHidIoFactory. `agent` represents the owner of the factory (typically the image_handle). - pub fn new(boot_services: &'static dyn UefiBootServices, agent: efi::Handle) -> Self { - UefiHidIoFactory { boot_services, agent } - } + /// Creates a new UefiHidIoFactory. `agent` represents the owner of the factory (typically the image_handle). + pub fn new(boot_services: &'static dyn UefiBootServices, agent: efi::Handle) -> Self { + UefiHidIoFactory { boot_services, agent } + } } impl HidIoFactory for UefiHidIoFactory { - /// instantiate a new UefiHidIo instance on the given controller. - fn new_hid_io(&self, controller: efi::Handle, owned: bool) -> Result, efi::Status> { - let hid_io = UefiHidIo::new(self.boot_services, self.agent, controller, owned)?; - Ok(Box::new(hid_io)) - } + /// instantiate a new UefiHidIo instance on the given controller. + fn new_hid_io(&self, controller: efi::Handle, owned: bool) -> Result, efi::Status> { + let hid_io = UefiHidIo::new(self.boot_services, self.agent, controller, owned)?; + Ok(Box::new(hid_io)) + } } /// Implements the HidIo interface on top of the HidIo protocol. pub struct UefiHidIo { - hid_io: &'static mut hid_io::protocol::Protocol, - boot_services: &'static dyn UefiBootServices, - controller: efi::Handle, - agent: efi::Handle, - receiver: Option>, - owned: bool, -} - -impl UefiHidIo { - // creates a new HidIo - private, intended only to be invoked by the [`UefiHidIoFactory`] implementation. - // if `owned`, then the interface is opened BY_DRIVER, otherwise it is opened with GET_PROTOCOL. - fn new( + hid_io: &'static mut hid_io::protocol::Protocol, boot_services: &'static dyn UefiBootServices, - agent: efi::Handle, controller: efi::Handle, + agent: efi::Handle, + receiver: Option>, owned: bool, - ) -> Result { - let mut hid_io_ptr: *mut hid_io::protocol::Protocol = core::ptr::null_mut(); - - let attributes = { - if owned { - efi::OPEN_PROTOCOL_BY_DRIVER - } else { - efi::OPEN_PROTOCOL_GET_PROTOCOL - } - }; +} + +impl UefiHidIo { + // creates a new HidIo - private, intended only to be invoked by the [`UefiHidIoFactory`] implementation. + // if `owned`, then the interface is opened BY_DRIVER, otherwise it is opened with GET_PROTOCOL. + fn new( + boot_services: &'static dyn UefiBootServices, + agent: efi::Handle, + controller: efi::Handle, + owned: bool, + ) -> Result { + let mut hid_io_ptr: *mut hid_io::protocol::Protocol = core::ptr::null_mut(); + + let attributes = { + if owned { + efi::OPEN_PROTOCOL_BY_DRIVER + } else { + efi::OPEN_PROTOCOL_GET_PROTOCOL + } + }; + + let status = boot_services.open_protocol( + controller, + &hid_io::protocol::GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::addr_of_mut!(hid_io_ptr) as *mut *mut c_void, + agent, + controller, + attributes, + ); + + if status.is_error() { + return Err(status); + } - let status = boot_services.open_protocol( - controller, - &hid_io::protocol::GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::addr_of_mut!(hid_io_ptr) as *mut *mut c_void, - agent, - controller, - attributes, - ); - - if status.is_error() { - return Err(status); + let hid_io = unsafe { hid_io_ptr.as_mut().expect("bad hid_io ptr") }; + Ok(Self { hid_io, boot_services, controller, agent, receiver: None, owned }) } - let hid_io = unsafe { hid_io_ptr.as_mut().expect("bad hid_io ptr") }; - Ok(Self { hid_io, boot_services, controller, agent, receiver: None, owned }) - } - - // the report callback FFI interface that is submitted to the HidIo instance to receive callbacks for reports. - extern "efiapi" fn report_callback(report_buffer_size: u16, report_buffer: *mut c_void, context: *mut c_void) { - let hid_io = unsafe { (context as *mut Self).as_mut().expect("bad context") }; - if let Some(mut receiver) = hid_io.receiver.take() { - let report = unsafe { from_raw_parts_mut(report_buffer as *mut u8, report_buffer_size as usize) }; - receiver.receive_report(report, hid_io); - hid_io.receiver = Some(receiver); + // the report callback FFI interface that is submitted to the HidIo instance to receive callbacks for reports. + extern "efiapi" fn report_callback(report_buffer_size: u16, report_buffer: *mut c_void, context: *mut c_void) { + let hid_io = unsafe { (context as *mut Self).as_mut().expect("bad context") }; + if let Some(mut receiver) = hid_io.receiver.take() { + let report = unsafe { from_raw_parts_mut(report_buffer as *mut u8, report_buffer_size as usize) }; + receiver.receive_report(report, hid_io); + hid_io.receiver = Some(receiver); + } } - } } impl Drop for UefiHidIo { - // Closes the HidIo interface if owned. - fn drop(&mut self) { - if self.owned { - let _ = self.take_report_receiver(); - let status = self.boot_services.close_protocol( - self.controller, - &hid_io::protocol::GUID as *const efi::Guid as *mut efi::Guid, - self.agent, - self.controller, - ); - if status.is_error() { - debugln!(DEBUG_ERROR, "Unexpected error closing hid_io: {:x?}", status); - } + // Closes the HidIo interface if owned. + fn drop(&mut self) { + if self.owned { + let _ = self.take_report_receiver(); + let status = self.boot_services.close_protocol( + self.controller, + &hid_io::protocol::GUID as *const efi::Guid as *mut efi::Guid, + self.agent, + self.controller, + ); + if status.is_error() { + debugln!(DEBUG_ERROR, "Unexpected error closing hid_io: {:x?}", status); + } + } } - } } impl HidIo for UefiHidIo { - fn get_report_descriptor(&self) -> Result { - let mut report_descriptor_size: usize = 0; - match (self.hid_io.get_report_descriptor)( - self.hid_io, - core::ptr::addr_of_mut!(report_descriptor_size), - core::ptr::null_mut(), - ) { - efi::Status::BUFFER_TOO_SMALL => (), - efi::Status::SUCCESS => return Err(efi::Status::DEVICE_ERROR), - err => return Err(err), - } + fn get_report_descriptor(&self) -> Result { + let mut report_descriptor_size: usize = 0; + match (self.hid_io.get_report_descriptor)( + self.hid_io, + core::ptr::addr_of_mut!(report_descriptor_size), + core::ptr::null_mut(), + ) { + efi::Status::BUFFER_TOO_SMALL => (), + efi::Status::SUCCESS => return Err(efi::Status::DEVICE_ERROR), + err => return Err(err), + } - let mut report_descriptor_buffer = vec![0u8; report_descriptor_size]; - let report_descriptor_buffer_ptr = report_descriptor_buffer.as_mut_ptr(); + let mut report_descriptor_buffer = vec![0u8; report_descriptor_size]; + let report_descriptor_buffer_ptr = report_descriptor_buffer.as_mut_ptr(); - match (self.hid_io.get_report_descriptor)( - self.hid_io, - core::ptr::addr_of_mut!(report_descriptor_size), - report_descriptor_buffer_ptr as *mut c_void, - ) { - efi::Status::SUCCESS => (), - err => return Err(err), - } + match (self.hid_io.get_report_descriptor)( + self.hid_io, + core::ptr::addr_of_mut!(report_descriptor_size), + report_descriptor_buffer_ptr as *mut c_void, + ) { + efi::Status::SUCCESS => (), + err => return Err(err), + } - hidparser::parse_report_descriptor(&report_descriptor_buffer).map_err(|_| efi::Status::DEVICE_ERROR) - } - - fn set_output_report(&self, id: Option, report: &[u8]) -> Result<(), efi::Status> { - match (self.hid_io.set_report)( - self.hid_io, - id.unwrap_or(0), - HidReportType::OutputReport, - report.len(), - report.as_ptr() as *mut c_void, - ) { - efi::Status::SUCCESS => Ok(()), - err => Err(err), + hidparser::parse_report_descriptor(&report_descriptor_buffer).map_err(|_| efi::Status::DEVICE_ERROR) } - } - fn set_report_receiver(&mut self, receiver: Box) -> Result<(), efi::Status> { - if !self.owned { - return Err(efi::Status::ACCESS_DENIED); + fn set_output_report(&self, id: Option, report: &[u8]) -> Result<(), efi::Status> { + match (self.hid_io.set_report)( + self.hid_io, + id.unwrap_or(0), + HidReportType::OutputReport, + report.len(), + report.as_ptr() as *mut c_void, + ) { + efi::Status::SUCCESS => Ok(()), + err => Err(err), + } } - let self_ptr = self as *mut UefiHidIo; - //always attempt uninstall. Failure is ok if not already installed. This shuts down report callback generation - //(if any) so that callbacks are not occurring while the new receiver is installed. - let _ = (self.hid_io.unregister_report_callback)(self.hid_io, Self::report_callback); + fn set_report_receiver(&mut self, receiver: Box) -> Result<(), efi::Status> { + if !self.owned { + return Err(efi::Status::ACCESS_DENIED); + } + let self_ptr = self as *mut UefiHidIo; - match (self.hid_io.register_report_callback)(self.hid_io, Self::report_callback, self_ptr as *mut c_void) { - efi::Status::SUCCESS => (), - err => return Err(err), - } - self.receiver = Some(receiver); + //always attempt uninstall. Failure is ok if not already installed. This shuts down report callback generation + //(if any) so that callbacks are not occurring while the new receiver is installed. + let _ = (self.hid_io.unregister_report_callback)(self.hid_io, Self::report_callback); + + match (self.hid_io.register_report_callback)(self.hid_io, Self::report_callback, self_ptr as *mut c_void) { + efi::Status::SUCCESS => (), + err => return Err(err), + } + self.receiver = Some(receiver); - Ok(()) - } - fn take_report_receiver(&mut self) -> Option> { - if !self.owned { - return None; + Ok(()) + } + fn take_report_receiver(&mut self) -> Option> { + if !self.owned { + return None; + } + //always attempt uninstall. Failure is ok if not already installed. This shuts down report callback generation + //(if any) so that callbacks are not occurring before the receiver is removed. + let _ = (self.hid_io.unregister_report_callback)(self.hid_io, Self::report_callback); + self.receiver.take() } - //always attempt uninstall. Failure is ok if not already installed. This shuts down report callback generation - //(if any) so that callbacks are not occurring before the receiver is removed. - let _ = (self.hid_io.unregister_report_callback)(self.hid_io, Self::report_callback); - self.receiver.take() - } } #[cfg(test)] mod test { - use core::{ - ffi::c_void, - slice::{from_raw_parts, from_raw_parts_mut}, - }; - - use super::{HidIo, MockHidReportReceiver, UefiHidIo}; - - use crate::boot_services::MockUefiBootServices; - - use r_efi::efi; - - static MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[ - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x06, // USAGE (Keyboard) - 0xa1, 0x01, // COLLECTION (Application) - 0x75, 0x01, // REPORT_SIZE (1) - 0x95, 0x08, // REPORT_COUNT (8) - 0x05, 0x07, // USAGE_PAGE (Key Codes) - 0x19, 0xE0, // USAGE_MINIMUM (224) - 0x29, 0xE7, // USAGE_MAXIMUM (231) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x25, 0x01, // LOGICAL_MAXIMUM (1) - 0x81, 0x02, // INPUT (Data, Var, Abs) (Modifier Byte) - 0xc0, // END_COLLECTION - ]; - - static TEST_REPORT0: &[u8] = &[0x0, 0x1, 0x2, 0x3, 0x4]; - static TEST_REPORT1: &[u8] = &[0x4, 0x3, 0x2, 0x1, 0x0]; - - // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used - // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock - // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. - // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. - // This object needs to outlive anything that uses it - once created, it will live until the end of the program. - fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { - unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } - } - - // Mock the HidIo FFI interface. - fn mock_hid_io() -> hid_io::protocol::Protocol { - extern "efiapi" fn mock_get_report_descriptor( - this: *const hid_io::protocol::Protocol, - report_descriptor_size: *mut usize, - report_descriptor_buffer: *mut c_void, - ) -> efi::Status { - assert_ne!(this, core::ptr::null()); - unsafe { - if *report_descriptor_size < MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR.len() { - *report_descriptor_size = MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR.len(); - return efi::Status::BUFFER_TOO_SMALL; - } else { - *report_descriptor_size = MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR.len(); - let slice = from_raw_parts_mut(report_descriptor_buffer as *mut u8, *report_descriptor_size); - slice.copy_from_slice(MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR); - return efi::Status::SUCCESS; - } - } - } + use core::{ + ffi::c_void, + slice::{from_raw_parts, from_raw_parts_mut}, + }; - extern "efiapi" fn mock_get_report( - _this: *const hid_io::protocol::Protocol, - _report_id: u8, - _report_type: hid_io::protocol::HidReportType, - _report_buffer_size: usize, - _report_buffer: *mut c_void, - ) -> efi::Status { - panic!("This implementation does not use get_report."); + use super::{HidIo, MockHidReportReceiver, UefiHidIo}; + + use crate::boot_services::MockUefiBootServices; + + use r_efi::efi; + + static MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x06, // USAGE (Keyboard) + 0xa1, 0x01, // COLLECTION (Application) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x08, // REPORT_COUNT (8) + 0x05, 0x07, // USAGE_PAGE (Key Codes) + 0x19, 0xE0, // USAGE_MINIMUM (224) + 0x29, 0xE7, // USAGE_MAXIMUM (231) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x81, 0x02, // INPUT (Data, Var, Abs) (Modifier Byte) + 0xc0, // END_COLLECTION + ]; + + static TEST_REPORT0: &[u8] = &[0x0, 0x1, 0x2, 0x3, 0x4]; + static TEST_REPORT1: &[u8] = &[0x4, 0x3, 0x2, 0x1, 0x0]; + + // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used + // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock + // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. + // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. + // This object needs to outlive anything that uses it - once created, it will live until the end of the program. + fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { + unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } } - extern "efiapi" fn mock_set_report( - this: *const hid_io::protocol::Protocol, - report_id: u8, - report_type: hid_io::protocol::HidReportType, - report_buffer_size: usize, - report_buffer: *mut c_void, - ) -> efi::Status { - assert_ne!(this, core::ptr::null()); - assert_eq!(report_type, hid_io::protocol::HidReportType::OutputReport); - assert_ne!(report_buffer_size, 0); - assert_ne!(report_buffer, core::ptr::null_mut()); - - let report_slice = unsafe { from_raw_parts(report_buffer as *mut u8, report_buffer_size) }; - - match report_id { - 0 => { - assert_eq!(report_slice, TEST_REPORT0); - efi::Status::SUCCESS + // Mock the HidIo FFI interface. + fn mock_hid_io() -> hid_io::protocol::Protocol { + extern "efiapi" fn mock_get_report_descriptor( + this: *const hid_io::protocol::Protocol, + report_descriptor_size: *mut usize, + report_descriptor_buffer: *mut c_void, + ) -> efi::Status { + assert_ne!(this, core::ptr::null()); + unsafe { + if *report_descriptor_size < MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR.len() { + *report_descriptor_size = MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR.len(); + return efi::Status::BUFFER_TOO_SMALL; + } else { + *report_descriptor_size = MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR.len(); + let slice = from_raw_parts_mut(report_descriptor_buffer as *mut u8, *report_descriptor_size); + slice.copy_from_slice(MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR); + return efi::Status::SUCCESS; + } + } } - 1 => { - assert_eq!(report_slice, TEST_REPORT1); - efi::Status::SUCCESS + + extern "efiapi" fn mock_get_report( + _this: *const hid_io::protocol::Protocol, + _report_id: u8, + _report_type: hid_io::protocol::HidReportType, + _report_buffer_size: usize, + _report_buffer: *mut c_void, + ) -> efi::Status { + panic!("This implementation does not use get_report."); + } + + extern "efiapi" fn mock_set_report( + this: *const hid_io::protocol::Protocol, + report_id: u8, + report_type: hid_io::protocol::HidReportType, + report_buffer_size: usize, + report_buffer: *mut c_void, + ) -> efi::Status { + assert_ne!(this, core::ptr::null()); + assert_eq!(report_type, hid_io::protocol::HidReportType::OutputReport); + assert_ne!(report_buffer_size, 0); + assert_ne!(report_buffer, core::ptr::null_mut()); + + let report_slice = unsafe { from_raw_parts(report_buffer as *mut u8, report_buffer_size) }; + + match report_id { + 0 => { + assert_eq!(report_slice, TEST_REPORT0); + efi::Status::SUCCESS + } + 1 => { + assert_eq!(report_slice, TEST_REPORT1); + efi::Status::SUCCESS + } + _ => efi::Status::UNSUPPORTED, + } + } + + extern "efiapi" fn mock_register_report_callback( + this: *const hid_io::protocol::Protocol, + callback: hid_io::protocol::HidIoReportCallback, + context: *mut c_void, + ) -> efi::Status { + assert_ne!(this, core::ptr::null()); + assert_ne!(context, core::ptr::null_mut()); + assert!(callback == UefiHidIo::report_callback); + + callback(TEST_REPORT0.len() as u16, TEST_REPORT0.as_ptr() as *mut c_void, context); + + efi::Status::SUCCESS + } + + extern "efiapi" fn mock_unregister_report_callback( + this: *const hid_io::protocol::Protocol, + callback: hid_io::protocol::HidIoReportCallback, + ) -> efi::Status { + assert_ne!(this, core::ptr::null()); + assert!(callback == UefiHidIo::report_callback); + efi::Status::SUCCESS } - _ => efi::Status::UNSUPPORTED, - } - } - extern "efiapi" fn mock_register_report_callback( - this: *const hid_io::protocol::Protocol, - callback: hid_io::protocol::HidIoReportCallback, - context: *mut c_void, - ) -> efi::Status { - assert_ne!(this, core::ptr::null()); - assert_ne!(context, core::ptr::null_mut()); - assert!(callback == UefiHidIo::report_callback); + hid_io::protocol::Protocol { + get_report_descriptor: mock_get_report_descriptor, + get_report: mock_get_report, + set_report: mock_set_report, + register_report_callback: mock_register_report_callback, + unregister_report_callback: mock_unregister_report_callback, + } + } - callback(TEST_REPORT0.len() as u16, TEST_REPORT0.as_ptr() as *mut c_void, context); + #[test] + fn new_should_instantiate_new_uefi_hid_io() { + let boot_services = create_fake_static_boot_service(); + let controller: efi::Handle = 0x1234 as efi::Handle; + let agent: efi::Handle = 0x4321 as efi::Handle; + + boot_services.expect_open_protocol().returning(|handle, protocol, interface, agent, controller, attributes| { + assert_eq!(handle, 0x1234 as efi::Handle); + assert_eq!(unsafe { *protocol }, hid_io::protocol::GUID); + assert_ne!(interface, core::ptr::null_mut()); + assert_eq!(agent, 0x4321 as efi::Handle); + assert_eq!(controller, 0x1234 as efi::Handle); + assert_eq!(attributes, efi::OPEN_PROTOCOL_BY_DRIVER); + + //note: this leaks; but easier than trying to share it between the closure and the environment. + let hid_io = Box::into_raw(Box::new(mock_hid_io())); + unsafe { *interface = hid_io as *mut c_void }; + efi::Status::SUCCESS + }); + + boot_services.expect_close_protocol().returning(|handle, protocol, agent, controller| { + assert_eq!(handle, 0x1234 as efi::Handle); + assert_eq!(unsafe { *protocol }, hid_io::protocol::GUID); + assert_eq!(agent, 0x4321 as efi::Handle); + assert_eq!(controller, 0x1234 as efi::Handle); + efi::Status::SUCCESS + }); + + let uefi_hid_io = UefiHidIo::new(boot_services, agent, controller, true).unwrap(); + drop(uefi_hid_io); + } - efi::Status::SUCCESS + #[test] + fn get_report_descriptor_should_return_report_descriptor() { + let boot_services = create_fake_static_boot_service(); + let controller: efi::Handle = 0x1234 as efi::Handle; + let agent: efi::Handle = 0x4321 as efi::Handle; + + boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { + let hid_io = mock_hid_io(); + //note: this leaks; but easier than trying to share it between the closure and the environment. + unsafe { *interface = Box::into_raw(Box::new(hid_io)) as *mut c_void }; + efi::Status::SUCCESS + }); + + boot_services.expect_close_protocol().returning(|_, _, _, _| efi::Status::SUCCESS); + + let uefi_hid_io = UefiHidIo::new(boot_services, agent, controller, true).unwrap(); + let descriptor = uefi_hid_io.get_report_descriptor().unwrap(); + assert_eq!(descriptor, hidparser::parse_report_descriptor(&MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap()); + drop(uefi_hid_io); } + #[test] + fn set_report_should_set_report() { + let boot_services = create_fake_static_boot_service(); + let controller: efi::Handle = 0x1234 as efi::Handle; + let agent: efi::Handle = 0x4321 as efi::Handle; + + boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { + let hid_io = mock_hid_io(); + unsafe { *interface = Box::into_raw(Box::new(hid_io)) as *mut c_void }; + efi::Status::SUCCESS + }); + + boot_services.expect_close_protocol().returning(|_, _, _, _| efi::Status::SUCCESS); + + let uefi_hid_io = UefiHidIo::new(boot_services, agent, controller, true).unwrap(); + + uefi_hid_io.set_output_report(None, &TEST_REPORT0).unwrap(); + uefi_hid_io.set_output_report(Some(1), &TEST_REPORT1).unwrap(); + assert_eq!(uefi_hid_io.set_output_report(Some(2), &TEST_REPORT0), Err(efi::Status::UNSUPPORTED)); - extern "efiapi" fn mock_unregister_report_callback( - this: *const hid_io::protocol::Protocol, - callback: hid_io::protocol::HidIoReportCallback, - ) -> efi::Status { - assert_ne!(this, core::ptr::null()); - assert!(callback == UefiHidIo::report_callback); - efi::Status::SUCCESS + drop(uefi_hid_io); } - hid_io::protocol::Protocol { - get_report_descriptor: mock_get_report_descriptor, - get_report: mock_get_report, - set_report: mock_set_report, - register_report_callback: mock_register_report_callback, - unregister_report_callback: mock_unregister_report_callback, + #[test] + fn set_receiver_should_install_receiver() { + let boot_services = create_fake_static_boot_service(); + let controller: efi::Handle = 0x1234 as efi::Handle; + let agent: efi::Handle = 0x4321 as efi::Handle; + + boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { + let hid_io = mock_hid_io(); + unsafe { *interface = Box::into_raw(Box::new(hid_io)) as *mut c_void }; + efi::Status::SUCCESS + }); + + boot_services.expect_close_protocol().returning(|_, _, _, _| efi::Status::SUCCESS); + + let mut uefi_hid_io = UefiHidIo::new(boot_services, agent, controller, true).unwrap(); + + let mut mock_receiver = MockHidReportReceiver::new(); + mock_receiver + .expect_receive_report() + .withf(|report, _| { + assert_eq!(report, TEST_REPORT0); + true + }) + .returning(|_, _| ()); + + uefi_hid_io.set_report_receiver(Box::new(mock_receiver)).unwrap(); + + drop(uefi_hid_io); } - } - - #[test] - fn new_should_instantiate_new_uefi_hid_io() { - let boot_services = create_fake_static_boot_service(); - let controller: efi::Handle = 0x1234 as efi::Handle; - let agent: efi::Handle = 0x4321 as efi::Handle; - - boot_services.expect_open_protocol().returning(|handle, protocol, interface, agent, controller, attributes| { - assert_eq!(handle, 0x1234 as efi::Handle); - assert_eq!(unsafe { *protocol }, hid_io::protocol::GUID); - assert_ne!(interface, core::ptr::null_mut()); - assert_eq!(agent, 0x4321 as efi::Handle); - assert_eq!(controller, 0x1234 as efi::Handle); - assert_eq!(attributes, efi::OPEN_PROTOCOL_BY_DRIVER); - - //note: this leaks; but easier than trying to share it between the closure and the environment. - let hid_io = Box::into_raw(Box::new(mock_hid_io())); - unsafe { *interface = hid_io as *mut c_void }; - efi::Status::SUCCESS - }); - - boot_services.expect_close_protocol().returning(|handle, protocol, agent, controller| { - assert_eq!(handle, 0x1234 as efi::Handle); - assert_eq!(unsafe { *protocol }, hid_io::protocol::GUID); - assert_eq!(agent, 0x4321 as efi::Handle); - assert_eq!(controller, 0x1234 as efi::Handle); - efi::Status::SUCCESS - }); - - let uefi_hid_io = UefiHidIo::new(boot_services, agent, controller, true).unwrap(); - drop(uefi_hid_io); - } - - #[test] - fn get_report_descriptor_should_return_report_descriptor() { - let boot_services = create_fake_static_boot_service(); - let controller: efi::Handle = 0x1234 as efi::Handle; - let agent: efi::Handle = 0x4321 as efi::Handle; - - boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { - let hid_io = mock_hid_io(); - //note: this leaks; but easier than trying to share it between the closure and the environment. - unsafe { *interface = Box::into_raw(Box::new(hid_io)) as *mut c_void }; - efi::Status::SUCCESS - }); - - boot_services.expect_close_protocol().returning(|_, _, _, _| efi::Status::SUCCESS); - - let uefi_hid_io = UefiHidIo::new(boot_services, agent, controller, true).unwrap(); - let descriptor = uefi_hid_io.get_report_descriptor().unwrap(); - assert_eq!(descriptor, hidparser::parse_report_descriptor(&MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap()); - drop(uefi_hid_io); - } - #[test] - fn set_report_should_set_report() { - let boot_services = create_fake_static_boot_service(); - let controller: efi::Handle = 0x1234 as efi::Handle; - let agent: efi::Handle = 0x4321 as efi::Handle; - - boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { - let hid_io = mock_hid_io(); - unsafe { *interface = Box::into_raw(Box::new(hid_io)) as *mut c_void }; - efi::Status::SUCCESS - }); - - boot_services.expect_close_protocol().returning(|_, _, _, _| efi::Status::SUCCESS); - - let uefi_hid_io = UefiHidIo::new(boot_services, agent, controller, true).unwrap(); - - uefi_hid_io.set_output_report(None, &TEST_REPORT0).unwrap(); - uefi_hid_io.set_output_report(Some(1), &TEST_REPORT1).unwrap(); - assert_eq!(uefi_hid_io.set_output_report(Some(2), &TEST_REPORT0), Err(efi::Status::UNSUPPORTED)); - - drop(uefi_hid_io); - } - - #[test] - fn set_receiver_should_install_receiver() { - let boot_services = create_fake_static_boot_service(); - let controller: efi::Handle = 0x1234 as efi::Handle; - let agent: efi::Handle = 0x4321 as efi::Handle; - - boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { - let hid_io = mock_hid_io(); - unsafe { *interface = Box::into_raw(Box::new(hid_io)) as *mut c_void }; - efi::Status::SUCCESS - }); - - boot_services.expect_close_protocol().returning(|_, _, _, _| efi::Status::SUCCESS); - - let mut uefi_hid_io = UefiHidIo::new(boot_services, agent, controller, true).unwrap(); - - let mut mock_receiver = MockHidReportReceiver::new(); - mock_receiver - .expect_receive_report() - .withf(|report, _| { - assert_eq!(report, TEST_REPORT0); - true - }) - .returning(|_, _| ()); - - uefi_hid_io.set_report_receiver(Box::new(mock_receiver)).unwrap(); - - drop(uefi_hid_io); - } } diff --git a/HidPkg/UefiHidDxeV2/src/keyboard/key_queue.rs b/HidPkg/UefiHidDxeV2/src/keyboard/key_queue.rs index 0ae881ed43..f7fcd9b068 100644 --- a/HidPkg/UefiHidDxeV2/src/keyboard/key_queue.rs +++ b/HidPkg/UefiHidDxeV2/src/keyboard/key_queue.rs @@ -12,14 +12,14 @@ use core::sync::atomic::Ordering; use alloc::{ - collections::{BTreeSet, VecDeque}, - vec::Vec, + collections::{BTreeSet, VecDeque}, + vec::Vec, }; use hidparser::report_data_types::Usage; use hii_keyboard_layout::{EfiKey, HiiKey, HiiKeyDescriptor, HiiKeyboardLayout, HiiNsKeyDescriptor}; use r_efi::{ - efi, - protocols::{self, hii_database::*, simple_text_input::InputKey, simple_text_input_ex::*}, + efi, + protocols::{self, hii_database::*, simple_text_input::InputKey, simple_text_input_ex::*}, }; use rust_advanced_logger_dxe::{debugln, DEBUG_WARN}; @@ -44,10 +44,10 @@ const ALT_MODIFIERS: &[u16] = &[LEFT_ALT_MODIFIER, RIGHT_ALT_MODIFIER]; // Defines whether a key stroke represents a key being pressed (KeyDown) or released (KeyUp) #[derive(Debug, PartialEq, Eq)] pub(crate) enum KeyAction { - // Key is being pressed - KeyUp, - // Key is being released - KeyDown, + // Key is being pressed + KeyUp, + // Key is being released + KeyDown, } // A wrapper for the KeyData type that allows definition of the Ord trait and additional registration matching logic. @@ -55,497 +55,497 @@ pub(crate) enum KeyAction { pub(crate) struct OrdKeyData(pub protocols::simple_text_input_ex::KeyData); impl Ord for OrdKeyData { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - let e = self.0.key.unicode_char.cmp(&other.0.key.unicode_char); - if !e.is_eq() { - return e; - } - let e = self.0.key.scan_code.cmp(&other.0.key.scan_code); - if !e.is_eq() { - return e; - } - let e = self.0.key_state.key_shift_state.cmp(&other.0.key_state.key_shift_state); - if !e.is_eq() { - return e; + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + let e = self.0.key.unicode_char.cmp(&other.0.key.unicode_char); + if !e.is_eq() { + return e; + } + let e = self.0.key.scan_code.cmp(&other.0.key.scan_code); + if !e.is_eq() { + return e; + } + let e = self.0.key_state.key_shift_state.cmp(&other.0.key_state.key_shift_state); + if !e.is_eq() { + return e; + } + self.0.key_state.key_toggle_state.cmp(&other.0.key_state.key_toggle_state) } - self.0.key_state.key_toggle_state.cmp(&other.0.key_state.key_toggle_state) - } } impl PartialOrd for OrdKeyData { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } } impl PartialEq for OrdKeyData { - fn eq(&self, other: &Self) -> bool { - self.cmp(other).is_eq() - } + fn eq(&self, other: &Self) -> bool { + self.cmp(other).is_eq() + } } impl Eq for OrdKeyData {} impl OrdKeyData { - // Returns whether this key matches the given registration. Note that this is not a straight compare - UEFI spec - // allows for some degree of wildcard matching. Refer to UEFI spec 2.10 section 12.2.5. - pub(crate) fn matches_registered_key(&self, registration: &Self) -> bool { - // assign names here for brevity below. - let self_char = self.0.key.unicode_char; - let self_scan = self.0.key.scan_code; - let self_shift = self.0.key_state.key_shift_state; - let self_toggle = self.0.key_state.key_toggle_state; - let register_char = registration.0.key.unicode_char; - let register_scan = registration.0.key.scan_code; - let register_shift = registration.0.key_state.key_shift_state; - let register_toggle = registration.0.key_state.key_toggle_state; - - //char and scan must match (per the reference implementation in the EDK2 C code). - if !(register_char == self_char && register_scan == self_scan) { - return false; - } + // Returns whether this key matches the given registration. Note that this is not a straight compare - UEFI spec + // allows for some degree of wildcard matching. Refer to UEFI spec 2.10 section 12.2.5. + pub(crate) fn matches_registered_key(&self, registration: &Self) -> bool { + // assign names here for brevity below. + let self_char = self.0.key.unicode_char; + let self_scan = self.0.key.scan_code; + let self_shift = self.0.key_state.key_shift_state; + let self_toggle = self.0.key_state.key_toggle_state; + let register_char = registration.0.key.unicode_char; + let register_scan = registration.0.key.scan_code; + let register_shift = registration.0.key_state.key_shift_state; + let register_toggle = registration.0.key_state.key_toggle_state; + + //char and scan must match (per the reference implementation in the EDK2 C code). + if !(register_char == self_char && register_scan == self_scan) { + return false; + } - //shift state must be zero or must match. - if !(register_shift == 0 || register_shift == self_shift) { - return false; - } + //shift state must be zero or must match. + if !(register_shift == 0 || register_shift == self_shift) { + return false; + } - //toggle state must be zero or must match. - if !(register_toggle == 0 || register_toggle == self_toggle) { - return false; + //toggle state must be zero or must match. + if !(register_toggle == 0 || register_toggle == self_toggle) { + return false; + } + true } - true - } } // This structure manages the queue of pending keystrokes #[derive(Debug, Default)] pub(crate) struct KeyQueue { - layout: Option, - active_modifiers: BTreeSet, - active_ns_key: Option, - partial_key_support_active: bool, - key_queue: VecDeque, - registered_keys: BTreeSet, - notified_key_queue: VecDeque, + layout: Option, + active_modifiers: BTreeSet, + active_ns_key: Option, + partial_key_support_active: bool, + key_queue: VecDeque, + registered_keys: BTreeSet, + notified_key_queue: VecDeque, } impl KeyQueue { - // resets the KeyQueue to initial state - pub(crate) fn reset(&mut self, extended_reset: bool) { - if extended_reset { - self.active_modifiers.clear(); - } else { - let active_leds = self.get_active_led_modifiers(); - self.active_modifiers.retain(|x| active_leds.contains(x)); + // resets the KeyQueue to initial state + pub(crate) fn reset(&mut self, extended_reset: bool) { + if extended_reset { + self.active_modifiers.clear(); + } else { + let active_leds = self.get_active_led_modifiers(); + self.active_modifiers.retain(|x| active_leds.contains(x)); + } + self.active_ns_key = None; + self.partial_key_support_active = false; + self.key_queue.clear(); } - self.active_ns_key = None; - self.partial_key_support_active = false; - self.key_queue.clear(); - } - - // Processes the given keystroke and updates the KeyQueue accordingly. - pub(crate) fn keystroke(&mut self, key: Usage, action: KeyAction) { - let Some(ref active_layout) = self.layout else { - //nothing to do if no layout. This is unexpected: layout should be initialized with default if not present. - debugln!(DEBUG_WARN, "key_queue::keystroke: Received keystroke without layout."); - return; - }; - let Some(efi_key) = usage_to_efi_key(key) else { - //unsupported key usage, nothing to do. - return; - }; + // Processes the given keystroke and updates the KeyQueue accordingly. + pub(crate) fn keystroke(&mut self, key: Usage, action: KeyAction) { + let Some(ref active_layout) = self.layout else { + //nothing to do if no layout. This is unexpected: layout should be initialized with default if not present. + debugln!(DEBUG_WARN, "key_queue::keystroke: Received keystroke without layout."); + return; + }; + + let Some(efi_key) = usage_to_efi_key(key) else { + //unsupported key usage, nothing to do. + return; + }; + + // Check if it is a dependent key of a currently active "non-spacing" (ns) key. + // Non-spacing key handling is described in UEFI spec 2.10 section 33.2.4.3. + let mut current_descriptor: Option = None; + if let Some(ref ns_key) = self.active_ns_key { + for descriptor in &ns_key.dependent_keys { + if descriptor.key == efi_key { + // found a dependent key for a previously active ns key. + // de-activate the ns key and process the dependent descriptor. + current_descriptor = Some(*descriptor); + self.active_ns_key = None; + break; + } + } + } + + // If it is not a dependent key of a currently active ns key, then check if it is a regular or ns key. + if current_descriptor.is_none() { + for key in &active_layout.keys { + match key { + HiiKey::Key(descriptor) => { + if descriptor.key == efi_key { + current_descriptor = Some(*descriptor); + break; + } + } + HiiKey::NsKey(ns_descriptor) => { + if ns_descriptor.descriptor.key == efi_key { + // if it is an ns_key, set it as the active ns key, and no further processing is needed. + self.active_ns_key = Some(ns_descriptor.clone()); + return; + } + } + } + } + } - // Check if it is a dependent key of a currently active "non-spacing" (ns) key. - // Non-spacing key handling is described in UEFI spec 2.10 section 33.2.4.3. - let mut current_descriptor: Option = None; - if let Some(ref ns_key) = self.active_ns_key { - for descriptor in &ns_key.dependent_keys { - if descriptor.key == efi_key { - // found a dependent key for a previously active ns key. - // de-activate the ns key and process the dependent descriptor. - current_descriptor = Some(*descriptor); - self.active_ns_key = None; - break; + if current_descriptor.is_none() { + return; //could not find descriptor, nothing to do. } - } - } - // If it is not a dependent key of a currently active ns key, then check if it is a regular or ns key. - if current_descriptor.is_none() { - for key in &active_layout.keys { - match key { - HiiKey::Key(descriptor) => { - if descriptor.key == efi_key { - current_descriptor = Some(*descriptor); - break; + let current_descriptor = current_descriptor.unwrap(); + + //handle modifiers that are active as long as they are pressed + if KEYBOARD_MODIFIERS.contains(¤t_descriptor.modifier) { + match action { + KeyAction::KeyUp => { + self.active_modifiers.remove(¤t_descriptor.modifier); + } + KeyAction::KeyDown => { + self.active_modifiers.insert(current_descriptor.modifier); + } } - } - HiiKey::NsKey(ns_descriptor) => { - if ns_descriptor.descriptor.key == efi_key { - // if it is an ns_key, set it as the active ns key, and no further processing is needed. - self.active_ns_key = Some(ns_descriptor.clone()); - return; + } + + //handle modifiers that toggle each time the key is pressed. + if TOGGLE_MODIFIERS.contains(¤t_descriptor.modifier) && action == KeyAction::KeyDown { + if self.active_modifiers.contains(¤t_descriptor.modifier) { + self.active_modifiers.remove(¤t_descriptor.modifier); + } else { + self.active_modifiers.insert(current_descriptor.modifier); } - } } - } - } - if current_descriptor.is_none() { - return; //could not find descriptor, nothing to do. - } + //handle ctrl-alt-delete + if CTRL_MODIFIERS.iter().any(|x| self.active_modifiers.contains(x)) + && ALT_MODIFIERS.iter().any(|x| self.active_modifiers.contains(x)) + && current_descriptor.modifier == DELETE_MODIFIER + { + debugln!(DEBUG_WARN, "Ctrl-Alt-Del pressed, resetting system."); + if let Some(runtime_services) = unsafe { RUNTIME_SERVICES.load(Ordering::SeqCst).as_mut() } { + (runtime_services.reset_system)(efi::RESET_WARM, efi::Status::SUCCESS, 0, core::ptr::null_mut()); + } + panic!("Reset failed."); + } - let current_descriptor = current_descriptor.unwrap(); + if action == KeyAction::KeyUp { + //nothing else to do. + return; + } - //handle modifiers that are active as long as they are pressed - if KEYBOARD_MODIFIERS.contains(¤t_descriptor.modifier) { - match action { - KeyAction::KeyUp => { - self.active_modifiers.remove(¤t_descriptor.modifier); + // process the keystroke to construct a KeyData item to add to the queue. + let mut key_data = protocols::simple_text_input_ex::KeyData { + key: InputKey { + unicode_char: current_descriptor.unicode, + scan_code: modifier_to_scan(current_descriptor.modifier), + }, + ..Default::default() + }; + + // retrieve relevant modifier state that may need to be applied to the key data. + let shift_active = SHIFT_MODIFIERS.iter().any(|x| self.active_modifiers.contains(x)); + let alt_gr_active = self.active_modifiers.contains(&ALT_GR_MODIFIER); + let caps_lock_active = self.active_modifiers.contains(&CAPS_LOCK_MODIFIER); + let num_lock_active = self.active_modifiers.contains(&NUM_LOCK_MODIFIER); + + // Apply the shift modifier if needed. Track whether it was applied as the shift modifier is removed from the key + // state if it was applied here. + let mut shift_applied: bool = false; + if (current_descriptor.affected_attribute & AFFECTED_BY_STANDARD_SHIFT) != 0 { + if shift_active { + //shift active + if alt_gr_active { + key_data.key.unicode_char = current_descriptor.shifted_alt_gr_unicode; + } else { + key_data.key.unicode_char = current_descriptor.shifted_unicode; + } + shift_applied = true; + } else { + //not shifted. + if alt_gr_active { + key_data.key.unicode_char = current_descriptor.alt_gr_unicode; + } + } } - KeyAction::KeyDown => { - self.active_modifiers.insert(current_descriptor.modifier); + + // if capslock is active, then invert the shift state of the key. + if (current_descriptor.affected_attribute & AFFECTED_BY_CAPS_LOCK) != 0 && caps_lock_active { + //Note: reference EDK2 implementation does not apply capslock to alt_gr. + if key_data.key.unicode_char == current_descriptor.unicode { + key_data.key.unicode_char = current_descriptor.shifted_unicode; + } else if key_data.key.unicode_char == current_descriptor.shifted_unicode { + key_data.key.unicode_char = current_descriptor.unicode; + } } - } - } - //handle modifiers that toggle each time the key is pressed. - if TOGGLE_MODIFIERS.contains(¤t_descriptor.modifier) && action == KeyAction::KeyDown { - if self.active_modifiers.contains(¤t_descriptor.modifier) { - self.active_modifiers.remove(¤t_descriptor.modifier); - } else { - self.active_modifiers.insert(current_descriptor.modifier); - } - } + // for the num pad, numlock (and shift state) controls whether a number key or a control key (e.g. arrow) is queued. + if (current_descriptor.affected_attribute & AFFECTED_BY_NUM_LOCK) != 0 { + if num_lock_active && !shift_active { + key_data.key.scan_code = SCAN_NULL; + } else { + key_data.key.unicode_char = 0x0000; + } + } - //handle ctrl-alt-delete - if CTRL_MODIFIERS.iter().any(|x| self.active_modifiers.contains(x)) - && ALT_MODIFIERS.iter().any(|x| self.active_modifiers.contains(x)) - && current_descriptor.modifier == DELETE_MODIFIER - { - debugln!(DEBUG_WARN, "Ctrl-Alt-Del pressed, resetting system."); - if let Some(runtime_services) = unsafe { RUNTIME_SERVICES.load(Ordering::SeqCst).as_mut() } { - (runtime_services.reset_system)(efi::RESET_WARM, efi::Status::SUCCESS, 0, core::ptr::null_mut()); - } - panic!("Reset failed."); - } + //special handling for unicode 0x1B (ESC). + if key_data.key.unicode_char == 0x01B && key_data.key.scan_code == SCAN_NULL { + key_data.key.scan_code = SCAN_ESC; + key_data.key.unicode_char = 0x0000; + } - if action == KeyAction::KeyUp { - //nothing else to do. - return; - } + if !self.partial_key_support_active && key_data.key.unicode_char == 0 && key_data.key.scan_code == SCAN_NULL { + return; // no further processing required if there is no key or scancode and partial support is not active. + } - // process the keystroke to construct a KeyData item to add to the queue. - let mut key_data = protocols::simple_text_input_ex::KeyData { - key: InputKey { - unicode_char: current_descriptor.unicode, - scan_code: modifier_to_scan(current_descriptor.modifier), - }, - ..Default::default() - }; + //initialize key state from active modifiers + key_data.key_state = self.init_key_state(); - // retrieve relevant modifier state that may need to be applied to the key data. - let shift_active = SHIFT_MODIFIERS.iter().any(|x| self.active_modifiers.contains(x)); - let alt_gr_active = self.active_modifiers.contains(&ALT_GR_MODIFIER); - let caps_lock_active = self.active_modifiers.contains(&CAPS_LOCK_MODIFIER); - let num_lock_active = self.active_modifiers.contains(&NUM_LOCK_MODIFIER); - - // Apply the shift modifier if needed. Track whether it was applied as the shift modifier is removed from the key - // state if it was applied here. - let mut shift_applied: bool = false; - if (current_descriptor.affected_attribute & AFFECTED_BY_STANDARD_SHIFT) != 0 { - if shift_active { - //shift active - if alt_gr_active { - key_data.key.unicode_char = current_descriptor.shifted_alt_gr_unicode; - } else { - key_data.key.unicode_char = current_descriptor.shifted_unicode; + // if shift was applied above, then remove shift from key state. See UEFI spec 2.10 section 12.2.3. + if shift_applied { + key_data.key_state.key_shift_state &= !(LEFT_SHIFT_PRESSED | RIGHT_SHIFT_PRESSED); } - shift_applied = true; - } else { - //not shifted. - if alt_gr_active { - key_data.key.unicode_char = current_descriptor.alt_gr_unicode; + + // if a callback has been registered matching this key, enqueue it in the callback queue. + if self.is_registered_key(key_data) { + self.notified_key_queue.push_back(key_data); } - } - } - // if capslock is active, then invert the shift state of the key. - if (current_descriptor.affected_attribute & AFFECTED_BY_CAPS_LOCK) != 0 && caps_lock_active { - //Note: reference EDK2 implementation does not apply capslock to alt_gr. - if key_data.key.unicode_char == current_descriptor.unicode { - key_data.key.unicode_char = current_descriptor.shifted_unicode; - } else if key_data.key.unicode_char == current_descriptor.shifted_unicode { - key_data.key.unicode_char = current_descriptor.unicode; - } + // enqueue the key data. + self.key_queue.push_back(key_data); } - // for the num pad, numlock (and shift state) controls whether a number key or a control key (e.g. arrow) is queued. - if (current_descriptor.affected_attribute & AFFECTED_BY_NUM_LOCK) != 0 { - if num_lock_active && !shift_active { - key_data.key.scan_code = SCAN_NULL; - } else { - key_data.key.unicode_char = 0x0000; - } + fn is_registered_key(&self, current_key: KeyData) -> bool { + for registered_key in &self.registered_keys { + if OrdKeyData(current_key).matches_registered_key(registered_key) { + return true; + } + } + false } - //special handling for unicode 0x1B (ESC). - if key_data.key.unicode_char == 0x01B && key_data.key.scan_code == SCAN_NULL { - key_data.key.scan_code = SCAN_ESC; - key_data.key.unicode_char = 0x0000; + // Creates a KeyState instance initialized based on the current modifier state. + pub(crate) fn init_key_state(&self) -> KeyState { + let mut key_shift_state = SHIFT_STATE_VALID; + let mut key_toggle_state = TOGGLE_STATE_VALID; + + if self.partial_key_support_active { + key_toggle_state |= KEY_STATE_EXPOSED; + } + + for modifier in &self.active_modifiers { + match *modifier { + LEFT_CONTROL_MODIFIER => key_shift_state |= LEFT_CONTROL_PRESSED, + RIGHT_CONTROL_MODIFIER => key_shift_state |= RIGHT_CONTROL_PRESSED, + LEFT_ALT_MODIFIER => key_shift_state |= LEFT_ALT_PRESSED, + RIGHT_ALT_MODIFIER => key_shift_state |= RIGHT_ALT_PRESSED, + LEFT_SHIFT_MODIFIER => key_shift_state |= LEFT_SHIFT_PRESSED, + RIGHT_SHIFT_MODIFIER => key_shift_state |= RIGHT_SHIFT_PRESSED, + LEFT_LOGO_MODIFIER => key_shift_state |= LEFT_LOGO_PRESSED, + RIGHT_LOGO_MODIFIER => key_shift_state |= RIGHT_LOGO_PRESSED, + MENU_MODIFIER => key_shift_state |= MENU_KEY_PRESSED, + SYS_REQUEST_MODIFIER | PRINT_MODIFIER => key_shift_state |= SYS_REQ_PRESSED, + SCROLL_LOCK_MODIFIER => key_toggle_state |= SCROLL_LOCK_ACTIVE, + NUM_LOCK_MODIFIER => key_toggle_state |= NUM_LOCK_ACTIVE, + CAPS_LOCK_MODIFIER => key_toggle_state |= CAPS_LOCK_ACTIVE, + _ => (), + } + } + KeyState { key_shift_state, key_toggle_state } } - if !self.partial_key_support_active && key_data.key.unicode_char == 0 && key_data.key.scan_code == SCAN_NULL { - return; // no further processing required if there is no key or scancode and partial support is not active. + // pops and returns the front of the key queue + pub(crate) fn pop_key(&mut self) -> Option { + self.key_queue.pop_front() } - //initialize key state from active modifiers - key_data.key_state = self.init_key_state(); + // returns a copy of the key at the front of the queue + pub(crate) fn peek_key(&self) -> Option { + self.key_queue.front().cloned() + } - // if shift was applied above, then remove shift from key state. See UEFI spec 2.10 section 12.2.3. - if shift_applied { - key_data.key_state.key_shift_state &= !(LEFT_SHIFT_PRESSED | RIGHT_SHIFT_PRESSED); + // pops and returns the front of the notify queue + pub(crate) fn pop_notify_key(&mut self) -> Option { + self.notified_key_queue.pop_front() } - // if a callback has been registered matching this key, enqueue it in the callback queue. - if self.is_registered_key(key_data) { - self.notified_key_queue.push_back(key_data); + // returns a copy of the key at the front of the notify queue + pub(crate) fn peek_notify_key(&self) -> Option { + self.key_queue.front().cloned() } - // enqueue the key data. - self.key_queue.push_back(key_data); - } + // set the key toggle state. This allows control of scroll/caps/num locks, as well as whether partial key state is + // exposed. + pub(crate) fn set_key_toggle_state(&mut self, toggle_state: KeyToggleState) { + if (toggle_state & SCROLL_LOCK_ACTIVE) != 0 { + self.active_modifiers.insert(SCROLL_LOCK_MODIFIER); + } else { + self.active_modifiers.remove(&SCROLL_LOCK_MODIFIER); + } - fn is_registered_key(&self, current_key: KeyData) -> bool { - for registered_key in &self.registered_keys { - if OrdKeyData(current_key).matches_registered_key(registered_key) { - return true; - } - } - false - } + if (toggle_state & NUM_LOCK_ACTIVE) != 0 { + self.active_modifiers.insert(NUM_LOCK_MODIFIER); + } else { + self.active_modifiers.remove(&NUM_LOCK_MODIFIER); + } - // Creates a KeyState instance initialized based on the current modifier state. - pub(crate) fn init_key_state(&self) -> KeyState { - let mut key_shift_state = SHIFT_STATE_VALID; - let mut key_toggle_state = TOGGLE_STATE_VALID; + if (toggle_state & CAPS_LOCK_ACTIVE) != 0 { + self.active_modifiers.insert(CAPS_LOCK_MODIFIER); + } else { + self.active_modifiers.remove(&CAPS_LOCK_MODIFIER); + } - if self.partial_key_support_active { - key_toggle_state |= KEY_STATE_EXPOSED; + self.partial_key_support_active = (toggle_state & KEY_STATE_EXPOSED) != 0; } - for modifier in &self.active_modifiers { - match *modifier { - LEFT_CONTROL_MODIFIER => key_shift_state |= LEFT_CONTROL_PRESSED, - RIGHT_CONTROL_MODIFIER => key_shift_state |= RIGHT_CONTROL_PRESSED, - LEFT_ALT_MODIFIER => key_shift_state |= LEFT_ALT_PRESSED, - RIGHT_ALT_MODIFIER => key_shift_state |= RIGHT_ALT_PRESSED, - LEFT_SHIFT_MODIFIER => key_shift_state |= LEFT_SHIFT_PRESSED, - RIGHT_SHIFT_MODIFIER => key_shift_state |= RIGHT_SHIFT_PRESSED, - LEFT_LOGO_MODIFIER => key_shift_state |= LEFT_LOGO_PRESSED, - RIGHT_LOGO_MODIFIER => key_shift_state |= RIGHT_LOGO_PRESSED, - MENU_MODIFIER => key_shift_state |= MENU_KEY_PRESSED, - SYS_REQUEST_MODIFIER | PRINT_MODIFIER => key_shift_state |= SYS_REQ_PRESSED, - SCROLL_LOCK_MODIFIER => key_toggle_state |= SCROLL_LOCK_ACTIVE, - NUM_LOCK_MODIFIER => key_toggle_state |= NUM_LOCK_ACTIVE, - CAPS_LOCK_MODIFIER => key_toggle_state |= CAPS_LOCK_ACTIVE, - _ => (), - } + fn get_active_led_modifiers(&self) -> Vec { + self.active_modifiers.iter().cloned().filter(|x| modifier_to_led_usage(*x).is_some()).collect() } - KeyState { key_shift_state, key_toggle_state } - } - - // pops and returns the front of the key queue - pub(crate) fn pop_key(&mut self) -> Option { - self.key_queue.pop_front() - } - - // returns a copy of the key at the front of the queue - pub(crate) fn peek_key(&self) -> Option { - self.key_queue.front().cloned() - } - - // pops and returns the front of the notify queue - pub(crate) fn pop_notify_key(&mut self) -> Option { - self.notified_key_queue.pop_front() - } - - // returns a copy of the key at the front of the notify queue - pub(crate) fn peek_notify_key(&self) -> Option { - self.key_queue.front().cloned() - } - - // set the key toggle state. This allows control of scroll/caps/num locks, as well as whether partial key state is - // exposed. - pub(crate) fn set_key_toggle_state(&mut self, toggle_state: KeyToggleState) { - if (toggle_state & SCROLL_LOCK_ACTIVE) != 0 { - self.active_modifiers.insert(SCROLL_LOCK_MODIFIER); - } else { - self.active_modifiers.remove(&SCROLL_LOCK_MODIFIER); + // Returns a vector of HID usages corresponding to the active LEDs based on the active modifier state. + pub(crate) fn get_active_leds(&self) -> Vec { + self.active_modifiers.iter().cloned().filter_map(modifier_to_led_usage).collect() } - if (toggle_state & NUM_LOCK_ACTIVE) != 0 { - self.active_modifiers.insert(NUM_LOCK_MODIFIER); - } else { - self.active_modifiers.remove(&NUM_LOCK_MODIFIER); + // Returns the current keyboard layout that the KeyQueue is using. + pub(crate) fn get_layout(&self) -> Option { + self.layout.clone() } - if (toggle_state & CAPS_LOCK_ACTIVE) != 0 { - self.active_modifiers.insert(CAPS_LOCK_MODIFIER); - } else { - self.active_modifiers.remove(&CAPS_LOCK_MODIFIER); + // Sets the current keyboard layout that the KeyQueue should use. + pub(crate) fn set_layout(&mut self, new_layout: Option) { + self.layout = new_layout; } - self.partial_key_support_active = (toggle_state & KEY_STATE_EXPOSED) != 0; - } - - fn get_active_led_modifiers(&self) -> Vec { - self.active_modifiers.iter().cloned().filter(|x| modifier_to_led_usage(*x).is_some()).collect() - } - // Returns a vector of HID usages corresponding to the active LEDs based on the active modifier state. - pub(crate) fn get_active_leds(&self) -> Vec { - self.active_modifiers.iter().cloned().filter_map(modifier_to_led_usage).collect() - } - - // Returns the current keyboard layout that the KeyQueue is using. - pub(crate) fn get_layout(&self) -> Option { - self.layout.clone() - } - - // Sets the current keyboard layout that the KeyQueue should use. - pub(crate) fn set_layout(&mut self, new_layout: Option) { - self.layout = new_layout; - } - - // Add a registration key for notifications; if a keystroke matches this key data, it will be added to the notify - // queue in addition to the normal key queue. - pub(crate) fn add_notify_key(&mut self, key_data: OrdKeyData) { - self.registered_keys.insert(key_data); - } - - // Remove a previously added notify key; keystrokes matching this key data will no longer be added to the notify - // queue. - pub(crate) fn remove_notify_key(&mut self, key_data: &OrdKeyData) { - self.registered_keys.remove(key_data); - } + // Add a registration key for notifications; if a keystroke matches this key data, it will be added to the notify + // queue in addition to the normal key queue. + pub(crate) fn add_notify_key(&mut self, key_data: OrdKeyData) { + self.registered_keys.insert(key_data); + } + + // Remove a previously added notify key; keystrokes matching this key data will no longer be added to the notify + // queue. + pub(crate) fn remove_notify_key(&mut self, key_data: &OrdKeyData) { + self.registered_keys.remove(key_data); + } } // Helper routine that converts a HID Usage to the corresponding EfiKey. fn usage_to_efi_key(usage: Usage) -> Option { - //Refer to UEFI spec version 2.10 figure 34.3 - match usage.into() { - 0x00070001..=0x00070003 => None, //Keyboard error codes. - 0x00070004 => Some(EfiKey::C1), - 0x00070005 => Some(EfiKey::B5), - 0x00070006 => Some(EfiKey::B3), - 0x00070007 => Some(EfiKey::C3), - 0x00070008 => Some(EfiKey::D3), - 0x00070009 => Some(EfiKey::C4), - 0x0007000A => Some(EfiKey::C5), - 0x0007000B => Some(EfiKey::C6), - 0x0007000C => Some(EfiKey::D8), - 0x0007000D => Some(EfiKey::C7), - 0x0007000E => Some(EfiKey::C8), - 0x0007000F => Some(EfiKey::C9), - 0x00070010 => Some(EfiKey::B7), - 0x00070011 => Some(EfiKey::B6), - 0x00070012 => Some(EfiKey::D9), - 0x00070013 => Some(EfiKey::D10), - 0x00070014 => Some(EfiKey::D1), - 0x00070015 => Some(EfiKey::D4), - 0x00070016 => Some(EfiKey::C2), - 0x00070017 => Some(EfiKey::D5), - 0x00070018 => Some(EfiKey::D7), - 0x00070019 => Some(EfiKey::B4), - 0x0007001A => Some(EfiKey::D2), - 0x0007001B => Some(EfiKey::B2), - 0x0007001C => Some(EfiKey::D6), - 0x0007001D => Some(EfiKey::B1), - 0x0007001E => Some(EfiKey::E1), - 0x0007001F => Some(EfiKey::E2), - 0x00070020 => Some(EfiKey::E3), - 0x00070021 => Some(EfiKey::E4), - 0x00070022 => Some(EfiKey::E5), - 0x00070023 => Some(EfiKey::E6), - 0x00070024 => Some(EfiKey::E7), - 0x00070025 => Some(EfiKey::E8), - 0x00070026 => Some(EfiKey::E9), - 0x00070027 => Some(EfiKey::E10), - 0x00070028 => Some(EfiKey::Enter), - 0x00070029 => Some(EfiKey::Esc), - 0x0007002A => Some(EfiKey::BackSpace), - 0x0007002B => Some(EfiKey::Tab), - 0x0007002C => Some(EfiKey::SpaceBar), - 0x0007002D => Some(EfiKey::E11), - 0x0007002E => Some(EfiKey::E12), - 0x0007002F => Some(EfiKey::D11), - 0x00070030 => Some(EfiKey::D12), - 0x00070031 => Some(EfiKey::D13), - 0x00070032 => Some(EfiKey::C12), - 0x00070033 => Some(EfiKey::C10), - 0x00070034 => Some(EfiKey::C11), - 0x00070035 => Some(EfiKey::E0), - 0x00070036 => Some(EfiKey::B8), - 0x00070037 => Some(EfiKey::B9), - 0x00070038 => Some(EfiKey::B10), - 0x00070039 => Some(EfiKey::CapsLock), - 0x0007003A => Some(EfiKey::F1), - 0x0007003B => Some(EfiKey::F2), - 0x0007003C => Some(EfiKey::F3), - 0x0007003D => Some(EfiKey::F4), - 0x0007003E => Some(EfiKey::F5), - 0x0007003F => Some(EfiKey::F6), - 0x00070040 => Some(EfiKey::F7), - 0x00070041 => Some(EfiKey::F8), - 0x00070042 => Some(EfiKey::F9), - 0x00070043 => Some(EfiKey::F10), - 0x00070044 => Some(EfiKey::F11), - 0x00070045 => Some(EfiKey::F12), - 0x00070046 => Some(EfiKey::Print), - 0x00070047 => Some(EfiKey::SLck), - 0x00070048 => Some(EfiKey::Pause), - 0x00070049 => Some(EfiKey::Ins), - 0x0007004A => Some(EfiKey::Home), - 0x0007004B => Some(EfiKey::PgUp), - 0x0007004C => Some(EfiKey::Del), - 0x0007004D => Some(EfiKey::End), - 0x0007004E => Some(EfiKey::PgDn), - 0x0007004F => Some(EfiKey::RightArrow), - 0x00070050 => Some(EfiKey::LeftArrow), - 0x00070051 => Some(EfiKey::DownArrow), - 0x00070052 => Some(EfiKey::UpArrow), - 0x00070053 => Some(EfiKey::NLck), - 0x00070054 => Some(EfiKey::Slash), - 0x00070055 => Some(EfiKey::Asterisk), - 0x00070056 => Some(EfiKey::Minus), - 0x00070057 => Some(EfiKey::Plus), - 0x00070058 => Some(EfiKey::Enter), - 0x00070059 => Some(EfiKey::One), - 0x0007005A => Some(EfiKey::Two), - 0x0007005B => Some(EfiKey::Three), - 0x0007005C => Some(EfiKey::Four), - 0x0007005D => Some(EfiKey::Five), - 0x0007005E => Some(EfiKey::Six), - 0x0007005F => Some(EfiKey::Seven), - 0x00070060 => Some(EfiKey::Eight), - 0x00070061 => Some(EfiKey::Nine), - 0x00070062 => Some(EfiKey::Zero), - 0x00070063 => Some(EfiKey::Period), - 0x00070064 => Some(EfiKey::B0), - 0x00070065 => Some(EfiKey::A4), - 0x00070066..=0x000700DF => None, // not used by EFI keyboard layout. - 0x000700E0 => Some(EfiKey::LCtrl), - 0x000700E1 => Some(EfiKey::LShift), - 0x000700E2 => Some(EfiKey::LAlt), - 0x000700E3 => Some(EfiKey::A0), - 0x000700E4 => Some(EfiKey::RCtrl), - 0x000700E5 => Some(EfiKey::RShift), - 0x000700E6 => Some(EfiKey::A2), - 0x000700E7 => Some(EfiKey::A3), - _ => None, // all other usages not used by EFI keyboard layout. - } + //Refer to UEFI spec version 2.10 figure 34.3 + match usage.into() { + 0x00070001..=0x00070003 => None, //Keyboard error codes. + 0x00070004 => Some(EfiKey::C1), + 0x00070005 => Some(EfiKey::B5), + 0x00070006 => Some(EfiKey::B3), + 0x00070007 => Some(EfiKey::C3), + 0x00070008 => Some(EfiKey::D3), + 0x00070009 => Some(EfiKey::C4), + 0x0007000A => Some(EfiKey::C5), + 0x0007000B => Some(EfiKey::C6), + 0x0007000C => Some(EfiKey::D8), + 0x0007000D => Some(EfiKey::C7), + 0x0007000E => Some(EfiKey::C8), + 0x0007000F => Some(EfiKey::C9), + 0x00070010 => Some(EfiKey::B7), + 0x00070011 => Some(EfiKey::B6), + 0x00070012 => Some(EfiKey::D9), + 0x00070013 => Some(EfiKey::D10), + 0x00070014 => Some(EfiKey::D1), + 0x00070015 => Some(EfiKey::D4), + 0x00070016 => Some(EfiKey::C2), + 0x00070017 => Some(EfiKey::D5), + 0x00070018 => Some(EfiKey::D7), + 0x00070019 => Some(EfiKey::B4), + 0x0007001A => Some(EfiKey::D2), + 0x0007001B => Some(EfiKey::B2), + 0x0007001C => Some(EfiKey::D6), + 0x0007001D => Some(EfiKey::B1), + 0x0007001E => Some(EfiKey::E1), + 0x0007001F => Some(EfiKey::E2), + 0x00070020 => Some(EfiKey::E3), + 0x00070021 => Some(EfiKey::E4), + 0x00070022 => Some(EfiKey::E5), + 0x00070023 => Some(EfiKey::E6), + 0x00070024 => Some(EfiKey::E7), + 0x00070025 => Some(EfiKey::E8), + 0x00070026 => Some(EfiKey::E9), + 0x00070027 => Some(EfiKey::E10), + 0x00070028 => Some(EfiKey::Enter), + 0x00070029 => Some(EfiKey::Esc), + 0x0007002A => Some(EfiKey::BackSpace), + 0x0007002B => Some(EfiKey::Tab), + 0x0007002C => Some(EfiKey::SpaceBar), + 0x0007002D => Some(EfiKey::E11), + 0x0007002E => Some(EfiKey::E12), + 0x0007002F => Some(EfiKey::D11), + 0x00070030 => Some(EfiKey::D12), + 0x00070031 => Some(EfiKey::D13), + 0x00070032 => Some(EfiKey::C12), + 0x00070033 => Some(EfiKey::C10), + 0x00070034 => Some(EfiKey::C11), + 0x00070035 => Some(EfiKey::E0), + 0x00070036 => Some(EfiKey::B8), + 0x00070037 => Some(EfiKey::B9), + 0x00070038 => Some(EfiKey::B10), + 0x00070039 => Some(EfiKey::CapsLock), + 0x0007003A => Some(EfiKey::F1), + 0x0007003B => Some(EfiKey::F2), + 0x0007003C => Some(EfiKey::F3), + 0x0007003D => Some(EfiKey::F4), + 0x0007003E => Some(EfiKey::F5), + 0x0007003F => Some(EfiKey::F6), + 0x00070040 => Some(EfiKey::F7), + 0x00070041 => Some(EfiKey::F8), + 0x00070042 => Some(EfiKey::F9), + 0x00070043 => Some(EfiKey::F10), + 0x00070044 => Some(EfiKey::F11), + 0x00070045 => Some(EfiKey::F12), + 0x00070046 => Some(EfiKey::Print), + 0x00070047 => Some(EfiKey::SLck), + 0x00070048 => Some(EfiKey::Pause), + 0x00070049 => Some(EfiKey::Ins), + 0x0007004A => Some(EfiKey::Home), + 0x0007004B => Some(EfiKey::PgUp), + 0x0007004C => Some(EfiKey::Del), + 0x0007004D => Some(EfiKey::End), + 0x0007004E => Some(EfiKey::PgDn), + 0x0007004F => Some(EfiKey::RightArrow), + 0x00070050 => Some(EfiKey::LeftArrow), + 0x00070051 => Some(EfiKey::DownArrow), + 0x00070052 => Some(EfiKey::UpArrow), + 0x00070053 => Some(EfiKey::NLck), + 0x00070054 => Some(EfiKey::Slash), + 0x00070055 => Some(EfiKey::Asterisk), + 0x00070056 => Some(EfiKey::Minus), + 0x00070057 => Some(EfiKey::Plus), + 0x00070058 => Some(EfiKey::Enter), + 0x00070059 => Some(EfiKey::One), + 0x0007005A => Some(EfiKey::Two), + 0x0007005B => Some(EfiKey::Three), + 0x0007005C => Some(EfiKey::Four), + 0x0007005D => Some(EfiKey::Five), + 0x0007005E => Some(EfiKey::Six), + 0x0007005F => Some(EfiKey::Seven), + 0x00070060 => Some(EfiKey::Eight), + 0x00070061 => Some(EfiKey::Nine), + 0x00070062 => Some(EfiKey::Zero), + 0x00070063 => Some(EfiKey::Period), + 0x00070064 => Some(EfiKey::B0), + 0x00070065 => Some(EfiKey::A4), + 0x00070066..=0x000700DF => None, // not used by EFI keyboard layout. + 0x000700E0 => Some(EfiKey::LCtrl), + 0x000700E1 => Some(EfiKey::LShift), + 0x000700E2 => Some(EfiKey::LAlt), + 0x000700E3 => Some(EfiKey::A0), + 0x000700E4 => Some(EfiKey::RCtrl), + 0x000700E5 => Some(EfiKey::RShift), + 0x000700E6 => Some(EfiKey::A2), + 0x000700E7 => Some(EfiKey::A3), + _ => None, // all other usages not used by EFI keyboard layout. + } } //These should be defined in r_efi::protocols::simple_text_input @@ -577,125 +577,127 @@ const SCAN_PAUSE: u16 = 0x0048; // helper routine that converts the given modifier to the corresponding SCAN code fn modifier_to_scan(modifier: u16) -> u16 { - match modifier { - INSERT_MODIFIER => SCAN_INSERT, - DELETE_MODIFIER => SCAN_DELETE, - PAGE_DOWN_MODIFIER => SCAN_PAGE_DOWN, - PAGE_UP_MODIFIER => SCAN_PAGE_UP, - HOME_MODIFIER => SCAN_HOME, - END_MODIFIER => SCAN_END, - LEFT_ARROW_MODIFIER => SCAN_LEFT, - RIGHT_ARROW_MODIFIER => SCAN_RIGHT, - DOWN_ARROW_MODIFIER => SCAN_DOWN, - UP_ARROW_MODIFIER => SCAN_UP, - FUNCTION_KEY_ONE_MODIFIER => SCAN_F1, - FUNCTION_KEY_TWO_MODIFIER => SCAN_F2, - FUNCTION_KEY_THREE_MODIFIER => SCAN_F3, - FUNCTION_KEY_FOUR_MODIFIER => SCAN_F4, - FUNCTION_KEY_FIVE_MODIFIER => SCAN_F5, - FUNCTION_KEY_SIX_MODIFIER => SCAN_F6, - FUNCTION_KEY_SEVEN_MODIFIER => SCAN_F7, - FUNCTION_KEY_EIGHT_MODIFIER => SCAN_F8, - FUNCTION_KEY_NINE_MODIFIER => SCAN_F9, - FUNCTION_KEY_TEN_MODIFIER => SCAN_F10, - FUNCTION_KEY_ELEVEN_MODIFIER => SCAN_F11, - FUNCTION_KEY_TWELVE_MODIFIER => SCAN_F12, - PAUSE_MODIFIER => SCAN_PAUSE, - _ => SCAN_NULL, - } + match modifier { + INSERT_MODIFIER => SCAN_INSERT, + DELETE_MODIFIER => SCAN_DELETE, + PAGE_DOWN_MODIFIER => SCAN_PAGE_DOWN, + PAGE_UP_MODIFIER => SCAN_PAGE_UP, + HOME_MODIFIER => SCAN_HOME, + END_MODIFIER => SCAN_END, + LEFT_ARROW_MODIFIER => SCAN_LEFT, + RIGHT_ARROW_MODIFIER => SCAN_RIGHT, + DOWN_ARROW_MODIFIER => SCAN_DOWN, + UP_ARROW_MODIFIER => SCAN_UP, + FUNCTION_KEY_ONE_MODIFIER => SCAN_F1, + FUNCTION_KEY_TWO_MODIFIER => SCAN_F2, + FUNCTION_KEY_THREE_MODIFIER => SCAN_F3, + FUNCTION_KEY_FOUR_MODIFIER => SCAN_F4, + FUNCTION_KEY_FIVE_MODIFIER => SCAN_F5, + FUNCTION_KEY_SIX_MODIFIER => SCAN_F6, + FUNCTION_KEY_SEVEN_MODIFIER => SCAN_F7, + FUNCTION_KEY_EIGHT_MODIFIER => SCAN_F8, + FUNCTION_KEY_NINE_MODIFIER => SCAN_F9, + FUNCTION_KEY_TEN_MODIFIER => SCAN_F10, + FUNCTION_KEY_ELEVEN_MODIFIER => SCAN_F11, + FUNCTION_KEY_TWELVE_MODIFIER => SCAN_F12, + PAUSE_MODIFIER => SCAN_PAUSE, + _ => SCAN_NULL, + } } // helper routine that converts the given modifier to the corresponding HID Usage. fn modifier_to_led_usage(modifier: u16) -> Option { - match modifier { - NUM_LOCK_MODIFIER => Some(Usage::from(0x00080001)), - CAPS_LOCK_MODIFIER => Some(Usage::from(0x00080002)), - SCROLL_LOCK_MODIFIER => Some(Usage::from(0x00080003)), - _ => None, - } + match modifier { + NUM_LOCK_MODIFIER => Some(Usage::from(0x00080001)), + CAPS_LOCK_MODIFIER => Some(Usage::from(0x00080002)), + SCROLL_LOCK_MODIFIER => Some(Usage::from(0x00080003)), + _ => None, + } } #[cfg(test)] mod test { - use hidparser::report_data_types::Usage; - use hii_keyboard_layout::{EfiKey, HiiKey, HiiKeyDescriptor, HiiNsKeyDescriptor}; - use r_efi::protocols::{ - self, - hii_database::{AFFECTED_BY_CAPS_LOCK, AFFECTED_BY_STANDARD_SHIFT, NS_KEY_DEPENDENCY_MODIFIER, NS_KEY_MODIFIER}, - }; - - use crate::keyboard::key_queue::{OrdKeyData, SCAN_DOWN}; - - use super::KeyQueue; - - // convenience macro for defining HiiKeyDescriptor structures. - // note: for unicode characters, these are encoded as u16 for compliance with UEFI spec. UEFI only supports UCS-2 - // encoding - so unicode characters that require more than two bytes under UTF-16 are not supported (and will panic). - macro_rules! key_descriptor { - ($key:expr, $unicode:literal, $shifted:literal, $alt_gr:literal, $shifted_alt_gr:literal, $modifier:expr, $affected:expr ) => { - HiiKeyDescriptor { - key: $key, - unicode: $unicode.encode_utf16(&mut [0u16; 1])[0], - shifted_unicode: $shifted.encode_utf16(&mut [0u16; 1])[0], - alt_gr_unicode: $alt_gr.encode_utf16(&mut [0u16; 1])[0], - shifted_alt_gr_unicode: $shifted_alt_gr.encode_utf16(&mut [0u16; 1])[0], - modifier: $modifier, - affected_attribute: $affected, - } + use hidparser::report_data_types::Usage; + use hii_keyboard_layout::{EfiKey, HiiKey, HiiKeyDescriptor, HiiNsKeyDescriptor}; + use r_efi::protocols::{ + self, + hii_database::{ + AFFECTED_BY_CAPS_LOCK, AFFECTED_BY_STANDARD_SHIFT, NS_KEY_DEPENDENCY_MODIFIER, NS_KEY_MODIFIER, + }, }; - } - - //most key_queue functionality is covered by tests in keyboard.rs. Here follows a few misc test cases. - - #[test] - fn test_ord_key_comparisons() { - let mut key_data1: protocols::simple_text_input_ex::KeyData = Default::default(); - let mut key_data2: protocols::simple_text_input_ex::KeyData = Default::default(); - - assert_eq!(OrdKeyData(key_data1), OrdKeyData(key_data2)); - key_data1.key.unicode_char = 'a' as u16; - assert_ne!(OrdKeyData(key_data1), OrdKeyData(key_data2)); - key_data2.key.unicode_char = 'a' as u16; - assert_eq!(OrdKeyData(key_data1), OrdKeyData(key_data2)); - - key_data1.key.scan_code = SCAN_DOWN; - assert_ne!(OrdKeyData(key_data1), OrdKeyData(key_data2)); - key_data2.key.scan_code = SCAN_DOWN; - assert_eq!(OrdKeyData(key_data1), OrdKeyData(key_data2)); - - key_data1.key_state.key_shift_state = - protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::LEFT_SHIFT_PRESSED; - assert_ne!(OrdKeyData(key_data1), OrdKeyData(key_data2)); - key_data2.key_state.key_shift_state = - protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::LEFT_SHIFT_PRESSED; - assert_eq!(OrdKeyData(key_data1), OrdKeyData(key_data2)); - - key_data1.key_state.key_toggle_state = - protocols::simple_text_input_ex::TOGGLE_STATE_VALID | protocols::simple_text_input_ex::CAPS_LOCK_ACTIVE; - assert_ne!(OrdKeyData(key_data1), OrdKeyData(key_data2)); - key_data2.key_state.key_toggle_state = - protocols::simple_text_input_ex::TOGGLE_STATE_VALID | protocols::simple_text_input_ex::CAPS_LOCK_ACTIVE; - assert_eq!(OrdKeyData(key_data1), OrdKeyData(key_data2)); - - assert_eq!(OrdKeyData(key_data1).partial_cmp(&OrdKeyData(key_data2)), Some(core::cmp::Ordering::Equal)); - } - - #[test] - fn test_ns_keystroke() { - let mut key_queue = KeyQueue::default(); - - let mut ns_key_layout = hii_keyboard_layout::get_default_keyboard_layout(); - - let keys = &mut ns_key_layout.keys; - - let (index, _) = keys - .iter() - .enumerate() - .find(|(_, element)| if let HiiKey::Key(key) = element { key.key == EfiKey::E0 } else { false }) - .unwrap(); - - #[rustfmt::skip] + + use crate::keyboard::key_queue::{OrdKeyData, SCAN_DOWN}; + + use super::KeyQueue; + + // convenience macro for defining HiiKeyDescriptor structures. + // note: for unicode characters, these are encoded as u16 for compliance with UEFI spec. UEFI only supports UCS-2 + // encoding - so unicode characters that require more than two bytes under UTF-16 are not supported (and will panic). + macro_rules! key_descriptor { + ($key:expr, $unicode:literal, $shifted:literal, $alt_gr:literal, $shifted_alt_gr:literal, $modifier:expr, $affected:expr ) => { + HiiKeyDescriptor { + key: $key, + unicode: $unicode.encode_utf16(&mut [0u16; 1])[0], + shifted_unicode: $shifted.encode_utf16(&mut [0u16; 1])[0], + alt_gr_unicode: $alt_gr.encode_utf16(&mut [0u16; 1])[0], + shifted_alt_gr_unicode: $shifted_alt_gr.encode_utf16(&mut [0u16; 1])[0], + modifier: $modifier, + affected_attribute: $affected, + } + }; + } + + //most key_queue functionality is covered by tests in keyboard.rs. Here follows a few misc test cases. + + #[test] + fn test_ord_key_comparisons() { + let mut key_data1: protocols::simple_text_input_ex::KeyData = Default::default(); + let mut key_data2: protocols::simple_text_input_ex::KeyData = Default::default(); + + assert_eq!(OrdKeyData(key_data1), OrdKeyData(key_data2)); + key_data1.key.unicode_char = 'a' as u16; + assert_ne!(OrdKeyData(key_data1), OrdKeyData(key_data2)); + key_data2.key.unicode_char = 'a' as u16; + assert_eq!(OrdKeyData(key_data1), OrdKeyData(key_data2)); + + key_data1.key.scan_code = SCAN_DOWN; + assert_ne!(OrdKeyData(key_data1), OrdKeyData(key_data2)); + key_data2.key.scan_code = SCAN_DOWN; + assert_eq!(OrdKeyData(key_data1), OrdKeyData(key_data2)); + + key_data1.key_state.key_shift_state = + protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::LEFT_SHIFT_PRESSED; + assert_ne!(OrdKeyData(key_data1), OrdKeyData(key_data2)); + key_data2.key_state.key_shift_state = + protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::LEFT_SHIFT_PRESSED; + assert_eq!(OrdKeyData(key_data1), OrdKeyData(key_data2)); + + key_data1.key_state.key_toggle_state = + protocols::simple_text_input_ex::TOGGLE_STATE_VALID | protocols::simple_text_input_ex::CAPS_LOCK_ACTIVE; + assert_ne!(OrdKeyData(key_data1), OrdKeyData(key_data2)); + key_data2.key_state.key_toggle_state = + protocols::simple_text_input_ex::TOGGLE_STATE_VALID | protocols::simple_text_input_ex::CAPS_LOCK_ACTIVE; + assert_eq!(OrdKeyData(key_data1), OrdKeyData(key_data2)); + + assert_eq!(OrdKeyData(key_data1).partial_cmp(&OrdKeyData(key_data2)), Some(core::cmp::Ordering::Equal)); + } + + #[test] + fn test_ns_keystroke() { + let mut key_queue = KeyQueue::default(); + + let mut ns_key_layout = hii_keyboard_layout::get_default_keyboard_layout(); + + let keys = &mut ns_key_layout.keys; + + let (index, _) = keys + .iter() + .enumerate() + .find(|(_, element)| if let HiiKey::Key(key) = element { key.key == EfiKey::E0 } else { false }) + .unwrap(); + + #[rustfmt::skip] let ns_key = HiiKey::NsKey(HiiNsKeyDescriptor { descriptor: key_descriptor!(EfiKey::E0, '\0', '\0', '\0', '\0', NS_KEY_MODIFIER, 0), @@ -707,25 +709,25 @@ mod test { key_descriptor!(EfiKey::D7, '\u{00FB}', '\u{00CB}', '\0', '\0', NS_KEY_DEPENDENCY_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK) ]}); - keys[index] = ns_key.clone(); + keys[index] = ns_key.clone(); - key_queue.set_layout(Some(ns_key_layout)); + key_queue.set_layout(Some(ns_key_layout)); - let key = Usage::from(0x00070035); //E0 + let key = Usage::from(0x00070035); //E0 - key_queue.keystroke(key, super::KeyAction::KeyDown); - key_queue.keystroke(key, super::KeyAction::KeyUp); + key_queue.keystroke(key, super::KeyAction::KeyDown); + key_queue.keystroke(key, super::KeyAction::KeyUp); - let HiiKey::NsKey(expected_key) = ns_key else { panic!() }; - assert_eq!(key_queue.active_ns_key, Some(expected_key)); + let HiiKey::NsKey(expected_key) = ns_key else { panic!() }; + assert_eq!(key_queue.active_ns_key, Some(expected_key)); - assert!(key_queue.peek_key().is_none()); + assert!(key_queue.peek_key().is_none()); - let key = Usage::from(0x00070004); //C1 - key_queue.keystroke(key, super::KeyAction::KeyDown); - key_queue.keystroke(key, super::KeyAction::KeyUp); + let key = Usage::from(0x00070004); //C1 + key_queue.keystroke(key, super::KeyAction::KeyDown); + key_queue.keystroke(key, super::KeyAction::KeyUp); - let stroke = key_queue.pop_key().unwrap(); - assert_eq!(stroke.key.unicode_char, '\u{00E2}' as u16); - } + let stroke = key_queue.pop_key().unwrap(); + assert_eq!(stroke.key.unicode_char, '\u{00E2}' as u16); + } } diff --git a/HidPkg/UefiHidDxeV2/src/keyboard/mod.rs b/HidPkg/UefiHidDxeV2/src/keyboard/mod.rs index 81886b872c..0fea4402e4 100644 --- a/HidPkg/UefiHidDxeV2/src/keyboard/mod.rs +++ b/HidPkg/UefiHidDxeV2/src/keyboard/mod.rs @@ -16,21 +16,21 @@ mod simple_text_in_ex; use core::ffi::c_void; use alloc::{ - boxed::Box, - collections::{BTreeMap, BTreeSet}, - vec, - vec::Vec, + boxed::Box, + collections::{BTreeMap, BTreeSet}, + vec, + vec::Vec, }; use hidparser::{ - report_data_types::{ReportId, Usage}, - ArrayField, ReportDescriptor, ReportField, VariableField, + report_data_types::{ReportId, Usage}, + ArrayField, ReportDescriptor, ReportField, VariableField, }; use r_efi::{efi, hii, protocols}; use crate::{ - boot_services::UefiBootServices, - hid_io::{HidIo, HidReportReceiver}, - keyboard::key_queue::OrdKeyData, + boot_services::UefiBootServices, + hid_io::{HidIo, HidReportReceiver}, + keyboard::key_queue::OrdKeyData, }; use rust_advanced_logger_dxe::{debugln, function, DEBUG_ERROR, DEBUG_VERBOSE, DEBUG_WARN}; @@ -46,149 +46,149 @@ const LED_USAGE_MAX: u32 = 0x00080005; // maps a given field to a routine that handles input from it. #[derive(Debug, Clone)] struct ReportFieldWithHandler { - field: T, - report_handler: fn(handler: &mut KeyboardHidHandler, field: T, report: &[u8]), + field: T, + report_handler: fn(handler: &mut KeyboardHidHandler, field: T, report: &[u8]), } // maps a given field to a routine that builds output reports from it. #[derive(Debug, Clone)] struct ReportFieldBuilder { - field: T, - field_builder: fn(&mut KeyboardHidHandler, field: T, report: &mut [u8]), + field: T, + field_builder: fn(&mut KeyboardHidHandler, field: T, report: &mut [u8]), } // Defines an input report and the fields of interest in it. #[derive(Debug, Default, Clone)] struct KeyboardReportData { - report_id: Option, - report_size: usize, - relevant_variable_fields: Vec>, - relevant_array_fields: Vec>, + report_id: Option, + report_size: usize, + relevant_variable_fields: Vec>, + relevant_array_fields: Vec>, } // Defines an output report and the fields of interest in it. #[derive(Debug, Default, Clone)] struct KeyboardOutputReportBuilder { - report_id: Option, - report_size: usize, - relevant_variable_fields: Vec>, + report_id: Option, + report_size: usize, + relevant_variable_fields: Vec>, } #[repr(C)] struct LayoutChangeContext { - boot_services: &'static dyn UefiBootServices, - keyboard_handler: *mut KeyboardHidHandler, + boot_services: &'static dyn UefiBootServices, + keyboard_handler: *mut KeyboardHidHandler, } /// Keyboard HID Handler pub struct KeyboardHidHandler { - boot_services: &'static dyn UefiBootServices, - agent: efi::Handle, - controller: Option, - input_reports: BTreeMap, KeyboardReportData>, - output_builders: Vec, - report_id_present: bool, - last_keys: BTreeSet, - current_keys: BTreeSet, - led_state: BTreeSet, - key_queue: key_queue::KeyQueue, - notification_callbacks: BTreeMap, - next_notify_handle: usize, - key_notify_event: efi::Event, - layout_change_event: efi::Event, - layout_context: *mut LayoutChangeContext, + boot_services: &'static dyn UefiBootServices, + agent: efi::Handle, + controller: Option, + input_reports: BTreeMap, KeyboardReportData>, + output_builders: Vec, + report_id_present: bool, + last_keys: BTreeSet, + current_keys: BTreeSet, + led_state: BTreeSet, + key_queue: key_queue::KeyQueue, + notification_callbacks: BTreeMap, + next_notify_handle: usize, + key_notify_event: efi::Event, + layout_change_event: efi::Event, + layout_context: *mut LayoutChangeContext, } impl KeyboardHidHandler { - /// Instantiates a new Keyboard HID handler. `agent` is the handle that owns the handler (typically image_handle) - pub fn new(boot_services: &'static dyn UefiBootServices, agent: efi::Handle) -> Self { - Self { - boot_services, - agent, - controller: None, - input_reports: BTreeMap::new(), - output_builders: Vec::new(), - report_id_present: false, - last_keys: BTreeSet::new(), - current_keys: BTreeSet::new(), - led_state: BTreeSet::new(), - key_queue: Default::default(), - notification_callbacks: BTreeMap::new(), - next_notify_handle: 0, - key_notify_event: core::ptr::null_mut(), - layout_change_event: core::ptr::null_mut(), - layout_context: core::ptr::null_mut(), + /// Instantiates a new Keyboard HID handler. `agent` is the handle that owns the handler (typically image_handle) + pub fn new(boot_services: &'static dyn UefiBootServices, agent: efi::Handle) -> Self { + Self { + boot_services, + agent, + controller: None, + input_reports: BTreeMap::new(), + output_builders: Vec::new(), + report_id_present: false, + last_keys: BTreeSet::new(), + current_keys: BTreeSet::new(), + led_state: BTreeSet::new(), + key_queue: Default::default(), + notification_callbacks: BTreeMap::new(), + next_notify_handle: 0, + key_notify_event: core::ptr::null_mut(), + layout_change_event: core::ptr::null_mut(), + layout_context: core::ptr::null_mut(), + } } - } - - // Processes the report descriptor to determine whether this is a supported device, and if so, extract the information - // required to process reports. - fn process_descriptor(&mut self, descriptor: ReportDescriptor) -> Result<(), efi::Status> { - let multiple_reports = - descriptor.input_reports.len() > 1 || descriptor.output_reports.len() > 1 || descriptor.features.len() > 1; - for report in &descriptor.input_reports { - let mut report_data = KeyboardReportData { report_id: report.report_id, ..Default::default() }; + // Processes the report descriptor to determine whether this is a supported device, and if so, extract the information + // required to process reports. + fn process_descriptor(&mut self, descriptor: ReportDescriptor) -> Result<(), efi::Status> { + let multiple_reports = + descriptor.input_reports.len() > 1 || descriptor.output_reports.len() > 1 || descriptor.features.len() > 1; - self.report_id_present = report.report_id.is_some(); + for report in &descriptor.input_reports { + let mut report_data = KeyboardReportData { report_id: report.report_id, ..Default::default() }; - if multiple_reports && !self.report_id_present { - //Invalid to have None ReportId if multiple reports present. - Err(efi::Status::DEVICE_ERROR)?; - } + self.report_id_present = report.report_id.is_some(); - report_data.report_size = report.size_in_bits.div_ceil(8); + if multiple_reports && !self.report_id_present { + //Invalid to have None ReportId if multiple reports present. + Err(efi::Status::DEVICE_ERROR)?; + } - for field in &report.fields { - match field { - //Variable fields (typically used for modifier Usages) - ReportField::Variable(field) => { - if let KEYBOARD_MODIFIER_USAGE_MIN..=KEYBOARD_MODIFIER_USAGE_MAX = field.usage.into() { - report_data.relevant_variable_fields.push(ReportFieldWithHandler:: { - field: field.clone(), - report_handler: Self::handle_variable_key, - }); + report_data.report_size = report.size_in_bits.div_ceil(8); + + for field in &report.fields { + match field { + //Variable fields (typically used for modifier Usages) + ReportField::Variable(field) => { + if let KEYBOARD_MODIFIER_USAGE_MIN..=KEYBOARD_MODIFIER_USAGE_MAX = field.usage.into() { + report_data.relevant_variable_fields.push(ReportFieldWithHandler:: { + field: field.clone(), + report_handler: Self::handle_variable_key, + }); + } + } + //Array fields (typically used for key strokes) + ReportField::Array(field) => { + for usage_list in &field.usage_list { + if usage_list.contains(Usage::from(KEYBOARD_USAGE_MIN)) + || usage_list.contains(Usage::from(KEYBOARD_USAGE_MAX)) + { + report_data.relevant_array_fields.push(ReportFieldWithHandler:: { + field: field.clone(), + report_handler: Self::handle_array_key, + }); + break; + } + } + } + ReportField::Padding(_) => (), // padding irrelevant. + } } - } - //Array fields (typically used for key strokes) - ReportField::Array(field) => { - for usage_list in &field.usage_list { - if usage_list.contains(Usage::from(KEYBOARD_USAGE_MIN)) - || usage_list.contains(Usage::from(KEYBOARD_USAGE_MAX)) - { - report_data.relevant_array_fields.push(ReportFieldWithHandler:: { - field: field.clone(), - report_handler: Self::handle_array_key, - }); - break; - } + if !(report_data.relevant_variable_fields.is_empty() && report_data.relevant_array_fields.is_empty()) { + self.input_reports.insert(report_data.report_id, report_data); } - } - ReportField::Padding(_) => (), // padding irrelevant. } - } - if !(report_data.relevant_variable_fields.is_empty() && report_data.relevant_array_fields.is_empty()) { - self.input_reports.insert(report_data.report_id, report_data); - } - } - for report in &descriptor.output_reports { - let mut report_builder = KeyboardOutputReportBuilder { report_id: report.report_id, ..Default::default() }; + for report in &descriptor.output_reports { + let mut report_builder = KeyboardOutputReportBuilder { report_id: report.report_id, ..Default::default() }; - self.report_id_present = report.report_id.is_some(); + self.report_id_present = report.report_id.is_some(); - if multiple_reports && !self.report_id_present { - //invalid to have None ReportId if multiple reports present. - Err(efi::Status::DEVICE_ERROR)?; - } + if multiple_reports && !self.report_id_present { + //invalid to have None ReportId if multiple reports present. + Err(efi::Status::DEVICE_ERROR)?; + } - report_builder.report_size = report.size_in_bits / 8; - if (report.size_in_bits % 8) != 0 { - report_builder.report_size += 1; - } + report_builder.report_size = report.size_in_bits / 8; + if (report.size_in_bits % 8) != 0 { + report_builder.report_size += 1; + } - for field in &report.fields { - match field { + for field in &report.fields { + match field { //Variable fields in output reports (typically used for LEDs). ReportField::Variable(field) => { if let LED_USAGE_MIN..=LED_USAGE_MAX = field.usage.into() { @@ -203,1145 +203,1152 @@ impl KeyboardHidHandler { ReportField::Array(_) | // No support for array field report outputs; could be added if required. ReportField::Padding(_) => (), // padding fields irrelevant. } - } - if !report_builder.relevant_variable_fields.is_empty() { - self.output_builders.push(report_builder); - } - } + } + if !report_builder.relevant_variable_fields.is_empty() { + self.output_builders.push(report_builder); + } + } - if self.input_reports.is_empty() && self.output_builders.is_empty() { - Err(efi::Status::UNSUPPORTED) - } else { - Ok(()) + if self.input_reports.is_empty() && self.output_builders.is_empty() { + Err(efi::Status::UNSUPPORTED) + } else { + Ok(()) + } } - } - // Helper routine to handle variable keyboard input report fields - fn handle_variable_key(&mut self, field: VariableField, report: &[u8]) { - match field.field_value(report) { - Some(x) if x != 0 => { - self.current_keys.insert(field.usage); - } - None | Some(_) => (), + // Helper routine to handle variable keyboard input report fields + fn handle_variable_key(&mut self, field: VariableField, report: &[u8]) { + match field.field_value(report) { + Some(x) if x != 0 => { + self.current_keys.insert(field.usage); + } + None | Some(_) => (), + } } - } - - // Helper routine to handle array keyboard input report fields - fn handle_array_key(&mut self, field: ArrayField, report: &[u8]) { - match field.field_value(report) { - Some(index) if index != 0 => { - let mut index = (index as u32 - u32::from(field.logical_minimum)) as usize; - let usage = field.usage_list.iter().find_map(|x| { - let range_size = (x.end() - x.start()) as usize; - if index <= range_size { - x.range().nth(index) - } else { - index -= range_size; - None - } - }); - if let Some(usage) = usage { - self.current_keys.insert(Usage::from(usage)); + + // Helper routine to handle array keyboard input report fields + fn handle_array_key(&mut self, field: ArrayField, report: &[u8]) { + match field.field_value(report) { + Some(index) if index != 0 => { + let mut index = (index as u32 - u32::from(field.logical_minimum)) as usize; + let usage = field.usage_list.iter().find_map(|x| { + let range_size = (x.end() - x.start()) as usize; + if index <= range_size { + x.range().nth(index) + } else { + index -= range_size; + None + } + }); + if let Some(usage) = usage { + self.current_keys.insert(Usage::from(usage)); + } + } + None | Some(_) => (), } - } - None | Some(_) => (), } - } - - // Helper routine that updates the fields in the given report buffer for the given field (called for each field for - // every LED usage that was discovered in the output report descriptor). - fn build_led_report(&mut self, field: VariableField, report: &mut [u8]) { - let status = field.set_field_value(self.led_state.contains(&field.usage).into(), report); - if status.is_err() { - debugln!(DEBUG_WARN, "{:}: failed to set field value: {:?}", function!(), status); + + // Helper routine that updates the fields in the given report buffer for the given field (called for each field for + // every LED usage that was discovered in the output report descriptor). + fn build_led_report(&mut self, field: VariableField, report: &mut [u8]) { + let status = field.set_field_value(self.led_state.contains(&field.usage).into(), report); + if status.is_err() { + debugln!(DEBUG_WARN, "{:}: failed to set field value: {:?}", function!(), status); + } } - } - - // Generates LED output reports - usually there is only one, but this implementation handles an arbitrary number of - // possible output reports. If more than one output report is defined, all will be sent whenever there is a change in - // LEDs. - fn generate_led_output_reports(&mut self) -> Vec<(Option, Vec)> { - let mut output_vec = Vec::new(); - let current_leds: BTreeSet = self.key_queue.get_active_leds().iter().cloned().collect(); - if current_leds != self.led_state { - self.led_state = current_leds; - for output_builder in self.output_builders.clone() { - let mut report_buffer = vec![0u8; output_builder.report_size]; - for field_builder in &output_builder.relevant_variable_fields { - (field_builder.field_builder)(self, field_builder.field.clone(), report_buffer.as_mut_slice()); + + // Generates LED output reports - usually there is only one, but this implementation handles an arbitrary number of + // possible output reports. If more than one output report is defined, all will be sent whenever there is a change in + // LEDs. + fn generate_led_output_reports(&mut self) -> Vec<(Option, Vec)> { + let mut output_vec = Vec::new(); + let current_leds: BTreeSet = self.key_queue.get_active_leds().iter().cloned().collect(); + if current_leds != self.led_state { + self.led_state = current_leds; + for output_builder in self.output_builders.clone() { + let mut report_buffer = vec![0u8; output_builder.report_size]; + for field_builder in &output_builder.relevant_variable_fields { + (field_builder.field_builder)(self, field_builder.field.clone(), report_buffer.as_mut_slice()); + } + output_vec.push((output_builder.report_id, report_buffer)); + } } - output_vec.push((output_builder.report_id, report_buffer)); - } + output_vec } - output_vec - } - - // Installs the FFI interfaces that provide keyboard support to the rest of the system - fn install_protocol_interfaces(&mut self, controller: efi::Handle) -> Result<(), efi::Status> { - simple_text_in::SimpleTextInFfi::install(self.boot_services, controller, self)?; - simple_text_in_ex::SimpleTextInExFfi::install(self.boot_services, controller, self)?; - self.controller = Some(controller); - - // after this point, access to self must be guarded by raising TPL to NOTIFY. - Ok(()) - } - - // Installs an event to be notified when a new layout is installed. This allows the driver to respond dynamically to - // installation of new layouts and handle keys accordingly. - fn install_layout_change_event(&mut self) -> Result<(), efi::Status> { - let context = LayoutChangeContext { boot_services: self.boot_services, keyboard_handler: self as *mut Self }; - let context_ptr = Box::into_raw(Box::new(context)); - - let mut layout_change_event: efi::Event = core::ptr::null_mut(); - let status = self.boot_services.create_event_ex( - efi::EVT_NOTIFY_SIGNAL, - efi::TPL_NOTIFY, - Some(on_layout_update), - context_ptr as *mut c_void, - &protocols::hii_database::SET_KEYBOARD_LAYOUT_EVENT_GUID, - core::ptr::addr_of_mut!(layout_change_event), - ); - if status.is_error() { - Err(status)?; + + // Installs the FFI interfaces that provide keyboard support to the rest of the system + fn install_protocol_interfaces(&mut self, controller: efi::Handle) -> Result<(), efi::Status> { + simple_text_in::SimpleTextInFfi::install(self.boot_services, controller, self)?; + simple_text_in_ex::SimpleTextInExFfi::install(self.boot_services, controller, self)?; + self.controller = Some(controller); + + // after this point, access to self must be guarded by raising TPL to NOTIFY. + Ok(()) } - self.layout_change_event = layout_change_event; - self.layout_context = context_ptr; - - Ok(()) - } - - // Closes the layout change event. - fn uninstall_layout_change_event(&mut self) -> Result<(), efi::Status> { - if !self.layout_change_event.is_null() { - let layout_change_event: efi::Handle = self.layout_change_event; - let status = self.boot_services.close_event(layout_change_event); - if status.is_error() { - //An error here means the event was not closed, so in theory the notification_callback on it could still be fired. - //Mark the instance invalid by setting the keyboard_handler raw pointer to null, but leak the LayoutContext - //instance. Leaking context allows context usage in the callback should it fire. - debugln!(DEBUG_ERROR, "Failed to close layout_change_event event, status: {:x?}", status); - unsafe { - (*self.layout_context).keyboard_handler = core::ptr::null_mut(); + // Installs an event to be notified when a new layout is installed. This allows the driver to respond dynamically to + // installation of new layouts and handle keys accordingly. + fn install_layout_change_event(&mut self) -> Result<(), efi::Status> { + let context = LayoutChangeContext { boot_services: self.boot_services, keyboard_handler: self as *mut Self }; + let context_ptr = Box::into_raw(Box::new(context)); + + let mut layout_change_event: efi::Event = core::ptr::null_mut(); + let status = self.boot_services.create_event_ex( + efi::EVT_NOTIFY_SIGNAL, + efi::TPL_NOTIFY, + Some(on_layout_update), + context_ptr as *mut c_void, + &protocols::hii_database::SET_KEYBOARD_LAYOUT_EVENT_GUID, + core::ptr::addr_of_mut!(layout_change_event), + ); + if status.is_error() { + Err(status)?; } - return Err(status); - } - // safe to drop layout change context. - drop(unsafe { Box::from_raw(self.layout_context) }); - self.layout_context = core::ptr::null_mut(); - self.layout_change_event = core::ptr::null_mut(); + + self.layout_change_event = layout_change_event; + self.layout_context = context_ptr; + + Ok(()) + } + + // Closes the layout change event. + fn uninstall_layout_change_event(&mut self) -> Result<(), efi::Status> { + if !self.layout_change_event.is_null() { + let layout_change_event: efi::Handle = self.layout_change_event; + let status = self.boot_services.close_event(layout_change_event); + if status.is_error() { + //An error here means the event was not closed, so in theory the notification_callback on it could still be fired. + //Mark the instance invalid by setting the keyboard_handler raw pointer to null, but leak the LayoutContext + //instance. Leaking context allows context usage in the callback should it fire. + debugln!(DEBUG_ERROR, "Failed to close layout_change_event event, status: {:x?}", status); + unsafe { + (*self.layout_context).keyboard_handler = core::ptr::null_mut(); + } + return Err(status); + } + // safe to drop layout change context. + drop(unsafe { Box::from_raw(self.layout_context) }); + self.layout_context = core::ptr::null_mut(); + self.layout_change_event = core::ptr::null_mut(); + } + Ok(()) } - Ok(()) - } - - // Installs a default keyboard layout. - fn install_default_layout(&mut self) -> Result<(), efi::Status> { - let mut hii_database_protocol_ptr: *mut protocols::hii_database::Protocol = core::ptr::null_mut(); - - let status = self.boot_services.locate_protocol( - &protocols::hii_database::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::null_mut(), - core::ptr::addr_of_mut!(hii_database_protocol_ptr) as *mut *mut c_void, - ); - if status.is_error() { - debugln!( + + // Installs a default keyboard layout. + fn install_default_layout(&mut self) -> Result<(), efi::Status> { + let mut hii_database_protocol_ptr: *mut protocols::hii_database::Protocol = core::ptr::null_mut(); + + let status = self.boot_services.locate_protocol( + &protocols::hii_database::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::null_mut(), + core::ptr::addr_of_mut!(hii_database_protocol_ptr) as *mut *mut c_void, + ); + if status.is_error() { + debugln!( DEBUG_ERROR, "keyboard::install_default_layout: Could not locate hii_database protocol to install keyboard layout: {:x?}", status ); - Err(status)?; - } + Err(status)?; + } - let hii_database_protocol = - unsafe { hii_database_protocol_ptr.as_mut().expect("Bad pointer returned from successful locate protocol.") }; + let hii_database_protocol = unsafe { + hii_database_protocol_ptr.as_mut().expect("Bad pointer returned from successful locate protocol.") + }; - let mut hii_handle: hii::Handle = core::ptr::null_mut(); - let status = (hii_database_protocol.new_package_list)( - hii_database_protocol_ptr, - hii_keyboard_layout::get_default_keyboard_pkg_list_buffer().as_ptr() as *const hii::PackageListHeader, - core::ptr::null_mut(), - core::ptr::addr_of_mut!(hii_handle), - ); + let mut hii_handle: hii::Handle = core::ptr::null_mut(); + let status = (hii_database_protocol.new_package_list)( + hii_database_protocol_ptr, + hii_keyboard_layout::get_default_keyboard_pkg_list_buffer().as_ptr() as *const hii::PackageListHeader, + core::ptr::null_mut(), + core::ptr::addr_of_mut!(hii_handle), + ); - if status.is_error() { - debugln!( - DEBUG_ERROR, - "keyboard::install_default_layout: Failed to install keyboard layout package: {:x?}", - status - ); - Err(status)?; - } + if status.is_error() { + debugln!( + DEBUG_ERROR, + "keyboard::install_default_layout: Failed to install keyboard layout package: {:x?}", + status + ); + Err(status)?; + } - let status = (hii_database_protocol.set_keyboard_layout)( - hii_database_protocol_ptr, - &hii_keyboard_layout::DEFAULT_KEYBOARD_LAYOUT_GUID as *const efi::Guid as *mut efi::Guid, - ); + let status = (hii_database_protocol.set_keyboard_layout)( + hii_database_protocol_ptr, + &hii_keyboard_layout::DEFAULT_KEYBOARD_LAYOUT_GUID as *const efi::Guid as *mut efi::Guid, + ); + + if status.is_error() { + debugln!(DEBUG_ERROR, "keyboard::install_default_layout: Failed to set keyboard layout: {:x?}", status); + Err(status)?; + } - if status.is_error() { - debugln!(DEBUG_ERROR, "keyboard::install_default_layout: Failed to set keyboard layout: {:x?}", status); - Err(status)?; + Ok(()) } - Ok(()) - } + // Initializes the keyboard layout. If no layout is already installed in the system, a default layout is installed. + fn initialize_keyboard_layout(&mut self) -> Result<(), efi::Status> { + self.install_layout_change_event()?; - // Initializes the keyboard layout. If no layout is already installed in the system, a default layout is installed. - fn initialize_keyboard_layout(&mut self) -> Result<(), efi::Status> { - self.install_layout_change_event()?; + //fake signal event to pick up any existing layout + on_layout_update(self.layout_change_event, self.layout_context as *mut c_void); - //fake signal event to pick up any existing layout - on_layout_update(self.layout_change_event, self.layout_context as *mut c_void); + //install a default layout if no layout is installed. + if self.key_queue.get_layout().is_none() { + self.install_default_layout()?; + } + Ok(()) + } - //install a default layout if no layout is installed. - if self.key_queue.get_layout().is_none() { - self.install_default_layout()?; + /// Resets the keyboard driver state. Clears any pending key state. `extended verification` will also reset toggle + /// state. + pub fn reset(&mut self, hid_io: &dyn HidIo, extended_verification: bool) -> Result<(), efi::Status> { + self.last_keys.clear(); + self.current_keys.clear(); + self.key_queue.reset(extended_verification); + if extended_verification { + self.update_leds(hid_io)?; + } + Ok(()) } - Ok(()) - } - - /// Resets the keyboard driver state. Clears any pending key state. `extended verification` will also reset toggle - /// state. - pub fn reset(&mut self, hid_io: &dyn HidIo, extended_verification: bool) -> Result<(), efi::Status> { - self.last_keys.clear(); - self.current_keys.clear(); - self.key_queue.reset(extended_verification); - if extended_verification { - self.update_leds(hid_io)?; + + /// Called to send LED state to the device if there has been a change in LEDs. + pub fn update_leds(&mut self, hid_io: &dyn HidIo) -> Result<(), efi::Status> { + for (id, output_report) in self.generate_led_output_reports() { + let result = hid_io.set_output_report(id.map(|x| u32::from(x) as u8), &output_report); + if let Err(result) = result { + debugln!(DEBUG_ERROR, "unexpected error sending output report: {:?}", result); + return Err(result); + } + } + Ok(()) } - Ok(()) - } - - /// Called to send LED state to the device if there has been a change in LEDs. - pub fn update_leds(&mut self, hid_io: &dyn HidIo) -> Result<(), efi::Status> { - for (id, output_report) in self.generate_led_output_reports() { - let result = hid_io.set_output_report(id.map(|x| u32::from(x) as u8), &output_report); - if let Err(result) = result { - debugln!(DEBUG_ERROR, "unexpected error sending output report: {:?}", result); - return Err(result); - } + + /// Returns a clone of the keystroke at the front of the keystroke queue. + pub fn peek_key(&mut self) -> Option { + self.key_queue.peek_key() } - Ok(()) - } - - /// Returns a clone of the keystroke at the front of the keystroke queue. - pub fn peek_key(&mut self) -> Option { - self.key_queue.peek_key() - } - - /// Removes and returns the keystroke at the front of the keystroke queue. - pub fn pop_key(&mut self) -> Option { - self.key_queue.pop_key() - } - - /// Returns the current key state (i.e. the SHIFT and TOGGLE state). - pub fn get_key_state(&mut self) -> protocols::simple_text_input_ex::KeyState { - self.key_queue.init_key_state() - } - - /// Sets the current key state. - pub fn set_key_toggle_state(&mut self, toggle_state: u8) { - self.key_queue.set_key_toggle_state(toggle_state); - self.generate_led_output_reports(); - } - - /// Registers a new key notify callback function to be invoked on the specified `key_data` press. - /// - /// Returns a handle that is used to unregister the callback if desired. - pub fn insert_key_notify_callback( - &mut self, - key_data: protocols::simple_text_input_ex::KeyData, - key_notification_function: protocols::simple_text_input_ex::KeyNotifyFunction, - ) -> usize { - let key_data = OrdKeyData(key_data); - for (handle, entry) in &self.notification_callbacks { - if entry.0 == key_data && entry.1 == key_notification_function { - //this callback already exists for this key, so return the current handle. - return *handle; - } + + /// Removes and returns the keystroke at the front of the keystroke queue. + pub fn pop_key(&mut self) -> Option { + self.key_queue.pop_key() } - // key_data/callback combo doesn't exist, create a new registration for it. - self.next_notify_handle += 1; - self.notification_callbacks.insert(self.next_notify_handle, (key_data.clone(), key_notification_function)); - self.key_queue.add_notify_key(key_data); - self.next_notify_handle - } - - /// Unregisters a previously registered key notify callback function. - pub fn remove_key_notify_callback(&mut self, notification_handle: usize) -> Result<(), efi::Status> { - if let Some(entry) = self.notification_callbacks.remove(¬ification_handle) { - let removed_key = entry.0; - if !self.notification_callbacks.values().any(|(key, _)| *key == removed_key) { - // no other handlers exist for the key in the removed entry, so remove it from the key_queue as well. - self.key_queue.remove_notify_key(&removed_key); - } - Ok(()) - } else { - Err(efi::Status::INVALID_PARAMETER) + + /// Returns the current key state (i.e. the SHIFT and TOGGLE state). + pub fn get_key_state(&mut self) -> protocols::simple_text_input_ex::KeyState { + self.key_queue.init_key_state() } - } - - /// Returns the set of keys that have pending callbacks, along with the vector of callback functions associated with - /// each key. - pub fn get_pending_callbacks( - &mut self, - ) -> (Option, Vec) { - if let Some(pending_notify_key) = self.key_queue.pop_notify_key() { - let mut pending_callbacks = Vec::new(); - for (key, callback) in self.notification_callbacks.values() { - if OrdKeyData(pending_notify_key).matches_registered_key(key) { - pending_callbacks.push(*callback); + + /// Sets the current key state. + pub fn set_key_toggle_state(&mut self, toggle_state: u8) { + self.key_queue.set_key_toggle_state(toggle_state); + self.generate_led_output_reports(); + } + + /// Registers a new key notify callback function to be invoked on the specified `key_data` press. + /// + /// Returns a handle that is used to unregister the callback if desired. + pub fn insert_key_notify_callback( + &mut self, + key_data: protocols::simple_text_input_ex::KeyData, + key_notification_function: protocols::simple_text_input_ex::KeyNotifyFunction, + ) -> usize { + let key_data = OrdKeyData(key_data); + for (handle, entry) in &self.notification_callbacks { + if entry.0 == key_data && entry.1 == key_notification_function { + //this callback already exists for this key, so return the current handle. + return *handle; + } } - } - (Some(pending_notify_key), pending_callbacks) - } else { - (None, Vec::new()) + // key_data/callback combo doesn't exist, create a new registration for it. + self.next_notify_handle += 1; + self.notification_callbacks.insert(self.next_notify_handle, (key_data.clone(), key_notification_function)); + self.key_queue.add_notify_key(key_data); + self.next_notify_handle } - } - - /// Returns the agent associated with this KeyboardHidHandler - pub fn get_agent(&self) -> efi::Handle { - self.agent - } - - /// Returns the controller associated with this KeyboardHidHandler. - pub fn get_controller(&self) -> Option { - self.controller - } - - #[cfg(test)] - pub fn set_controller(&mut self, controller: Option) { - self.controller = controller; - } - - #[cfg(test)] - pub fn set_layout(&mut self, layout: Option) { - self.key_queue.set_layout(layout) - } - #[cfg(test)] - pub fn set_notify_event(&mut self, event: efi::Event) { - self.key_notify_event = event; - } -} -impl HidReportReceiver for KeyboardHidHandler { - fn initialize(&mut self, controller: efi::Handle, hid_io: &dyn HidIo) -> Result<(), efi::Status> { - let descriptor = hid_io.get_report_descriptor()?; - self.process_descriptor(descriptor)?; + /// Unregisters a previously registered key notify callback function. + pub fn remove_key_notify_callback(&mut self, notification_handle: usize) -> Result<(), efi::Status> { + if let Some(entry) = self.notification_callbacks.remove(¬ification_handle) { + let removed_key = entry.0; + if !self.notification_callbacks.values().any(|(key, _)| *key == removed_key) { + // no other handlers exist for the key in the removed entry, so remove it from the key_queue as well. + self.key_queue.remove_notify_key(&removed_key); + } + Ok(()) + } else { + Err(efi::Status::INVALID_PARAMETER) + } + } - self.install_protocol_interfaces(controller)?; + /// Returns the set of keys that have pending callbacks, along with the vector of callback functions associated with + /// each key. + pub fn get_pending_callbacks( + &mut self, + ) -> (Option, Vec) + { + if let Some(pending_notify_key) = self.key_queue.pop_notify_key() { + let mut pending_callbacks = Vec::new(); + for (key, callback) in self.notification_callbacks.values() { + if OrdKeyData(pending_notify_key).matches_registered_key(key) { + pending_callbacks.push(*callback); + } + } + (Some(pending_notify_key), pending_callbacks) + } else { + (None, Vec::new()) + } + } - self.initialize_keyboard_layout()?; + /// Returns the agent associated with this KeyboardHidHandler + pub fn get_agent(&self) -> efi::Handle { + self.agent + } - Ok(()) - } + /// Returns the controller associated with this KeyboardHidHandler. + pub fn get_controller(&self) -> Option { + self.controller + } - fn receive_report(&mut self, report: &[u8], hid_io: &dyn HidIo) { - let old_tpl = self.boot_services.raise_tpl(efi::TPL_NOTIFY); + #[cfg(test)] + pub fn set_controller(&mut self, controller: Option) { + self.controller = controller; + } - let mut output_reports = Vec::new(); - 'report_processing: { - if report.is_empty() { - break 'report_processing; - } - // determine whether report includes report id byte and adjust the buffer as needed. - let (report_id, report) = match self.report_id_present { - true => (Some(ReportId::from(&report[0..1])), &report[1..]), - false => (None, &report[0..]), - }; - - if report.is_empty() { - break 'report_processing; - } + #[cfg(test)] + pub fn set_layout(&mut self, layout: Option) { + self.key_queue.set_layout(layout) + } + #[cfg(test)] + pub fn set_notify_event(&mut self, event: efi::Event) { + self.key_notify_event = event; + } +} - if let Some(report_data) = self.input_reports.get(&report_id).cloned() { - if report.len() != report_data.report_size { - //Some devices report extra bytes in their reports. Warn about this, but try and process anyway. - debugln!( - DEBUG_VERBOSE, - "{:?}:{:?} unexpected report length for report_id: {:?}. expected {:?}, actual {:?}", - function!(), - line!(), - report_id, - report_data.report_size, - report.len() - ); - debugln!(DEBUG_VERBOSE, "report: {:x?}", report); - //break 'report_processing; - } +impl HidReportReceiver for KeyboardHidHandler { + fn initialize(&mut self, controller: efi::Handle, hid_io: &dyn HidIo) -> Result<(), efi::Status> { + let descriptor = hid_io.get_report_descriptor()?; + self.process_descriptor(descriptor)?; - //reset currently active keys to empty set. - self.current_keys.clear(); + self.install_protocol_interfaces(controller)?; - // hand the report data to the handler for each relevant field for field-specific processing. - for field in report_data.relevant_variable_fields { - (field.report_handler)(self, field.field, report); - } + self.initialize_keyboard_layout()?; - for field in report_data.relevant_array_fields { - (field.report_handler)(self, field.field, report); - } + Ok(()) + } - //check if any key state has changed. - if self.last_keys != self.current_keys { - // process keys that are not in both sets: that is the set of keys that have changed. - // XOR on the sets yields a set of keys that are in either last or current keys, but not both. - - // Modifier keys need to be processed first so that normal key processing includes modifiers that showed up in - // the same report. The key sets are sorted by Usage, and modifier keys all have higher usages than normal keys - // - so use a reverse iterator to process the modifier keys first. In addition, all released keys should be - // processed first so that pressed keys (which typically generate key stroke events) have the most recent - // key state associated with them. - let mut released_keys = Vec::new(); - let mut pressed_keys = Vec::new(); - for changed_key in (&self.last_keys ^ &self.current_keys).into_iter().rev() { - if self.last_keys.contains(&changed_key) { - //In the last key list, but not in current. This is a key release. - released_keys.push(changed_key); - } else { - //Not in last, so must be in current. This is a key press. - pressed_keys.push(changed_key); - } - } - for key in released_keys { - self.key_queue.keystroke(key, key_queue::KeyAction::KeyUp); - } - for key in pressed_keys { - self.key_queue.keystroke(key, key_queue::KeyAction::KeyDown); - } + fn receive_report(&mut self, report: &[u8], hid_io: &dyn HidIo) { + let old_tpl = self.boot_services.raise_tpl(efi::TPL_NOTIFY); - //after processing all the key strokes, check if any keys were pressed that should trigger the notifier callback - //and if so, signal the event to trigger notify processing at the appropriate TPL. - if self.key_queue.peek_notify_key().is_some() { - self.boot_services.signal_event(self.key_notify_event); - } + let mut output_reports = Vec::new(); + 'report_processing: { + if report.is_empty() { + break 'report_processing; + } + // determine whether report includes report id byte and adjust the buffer as needed. + let (report_id, report) = match self.report_id_present { + true => (Some(ReportId::from(&report[0..1])), &report[1..]), + false => (None, &report[0..]), + }; + + if report.is_empty() { + break 'report_processing; + } - //after processing all the key strokes, send updated LED state if required. - output_reports = self.generate_led_output_reports(); + if let Some(report_data) = self.input_reports.get(&report_id).cloned() { + if report.len() != report_data.report_size { + //Some devices report extra bytes in their reports. Warn about this, but try and process anyway. + debugln!( + DEBUG_VERBOSE, + "{:?}:{:?} unexpected report length for report_id: {:?}. expected {:?}, actual {:?}", + function!(), + line!(), + report_id, + report_data.report_size, + report.len() + ); + debugln!(DEBUG_VERBOSE, "report: {:x?}", report); + //break 'report_processing; + } + + //reset currently active keys to empty set. + self.current_keys.clear(); + + // hand the report data to the handler for each relevant field for field-specific processing. + for field in report_data.relevant_variable_fields { + (field.report_handler)(self, field.field, report); + } + + for field in report_data.relevant_array_fields { + (field.report_handler)(self, field.field, report); + } + + //check if any key state has changed. + if self.last_keys != self.current_keys { + // process keys that are not in both sets: that is the set of keys that have changed. + // XOR on the sets yields a set of keys that are in either last or current keys, but not both. + + // Modifier keys need to be processed first so that normal key processing includes modifiers that showed up in + // the same report. The key sets are sorted by Usage, and modifier keys all have higher usages than normal keys + // - so use a reverse iterator to process the modifier keys first. In addition, all released keys should be + // processed first so that pressed keys (which typically generate key stroke events) have the most recent + // key state associated with them. + let mut released_keys = Vec::new(); + let mut pressed_keys = Vec::new(); + for changed_key in (&self.last_keys ^ &self.current_keys).into_iter().rev() { + if self.last_keys.contains(&changed_key) { + //In the last key list, but not in current. This is a key release. + released_keys.push(changed_key); + } else { + //Not in last, so must be in current. This is a key press. + pressed_keys.push(changed_key); + } + } + for key in released_keys { + self.key_queue.keystroke(key, key_queue::KeyAction::KeyUp); + } + for key in pressed_keys { + self.key_queue.keystroke(key, key_queue::KeyAction::KeyDown); + } + + //after processing all the key strokes, check if any keys were pressed that should trigger the notifier callback + //and if so, signal the event to trigger notify processing at the appropriate TPL. + if self.key_queue.peek_notify_key().is_some() { + self.boot_services.signal_event(self.key_notify_event); + } + + //after processing all the key strokes, send updated LED state if required. + output_reports = self.generate_led_output_reports(); + } + //after all key handling is complete for this report, update the last key set to match the current key set. + self.last_keys = self.current_keys.clone(); + } } - //after all key handling is complete for this report, update the last key set to match the current key set. - self.last_keys = self.current_keys.clone(); - } - } - self.boot_services.restore_tpl(old_tpl); + self.boot_services.restore_tpl(old_tpl); - // if any output reports, send them after releasing handler. - for (id, output_report) in output_reports { - let result = hid_io.set_output_report(id.map(|x| u32::from(x) as u8), &output_report); - if let Err(result) = result { - debugln!(DEBUG_ERROR, "unexpected error sending output report: {:?}", result); - let _ = result; - } + // if any output reports, send them after releasing handler. + for (id, output_report) in output_reports { + let result = hid_io.set_output_report(id.map(|x| u32::from(x) as u8), &output_report); + if let Err(result) = result { + debugln!(DEBUG_ERROR, "unexpected error sending output report: {:?}", result); + let _ = result; + } + } } - } } impl Drop for KeyboardHidHandler { - fn drop(&mut self) { - if let Some(controller) = self.controller { - let status = simple_text_in::SimpleTextInFfi::uninstall(self.boot_services, self.agent, controller); - if status.is_err() { - debugln!(DEBUG_ERROR, "KeyboardHidHandler::drop: Failed to uninstall simple_text_in: {:?}", status); - } - let status = simple_text_in_ex::SimpleTextInExFfi::uninstall(self.boot_services, self.agent, controller); - if status.is_err() { - debugln!(DEBUG_ERROR, "KeyboardHidHandler::drop: Failed to uninstall simple_text_in: {:?}", status); - } - } - let status = self.uninstall_layout_change_event(); - if status.is_err() { - debugln!(DEBUG_ERROR, "KeyboardHidHandler::drop: Failed to close layout_change_event: {:?}", status); + fn drop(&mut self) { + if let Some(controller) = self.controller { + let status = simple_text_in::SimpleTextInFfi::uninstall(self.boot_services, self.agent, controller); + if status.is_err() { + debugln!(DEBUG_ERROR, "KeyboardHidHandler::drop: Failed to uninstall simple_text_in: {:?}", status); + } + let status = simple_text_in_ex::SimpleTextInExFfi::uninstall(self.boot_services, self.agent, controller); + if status.is_err() { + debugln!(DEBUG_ERROR, "KeyboardHidHandler::drop: Failed to uninstall simple_text_in: {:?}", status); + } + } + let status = self.uninstall_layout_change_event(); + if status.is_err() { + debugln!(DEBUG_ERROR, "KeyboardHidHandler::drop: Failed to close layout_change_event: {:?}", status); + } } - } } // handles keyboard layout change event that occurs when a new keyboard layout is set. extern "efiapi" fn on_layout_update(_event: efi::Event, context: *mut c_void) { - let context = unsafe { (context as *mut LayoutChangeContext).as_mut() }.expect("bad context pointer"); - let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); + let context = unsafe { (context as *mut LayoutChangeContext).as_mut() }.expect("bad context pointer"); + let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); - 'layout_processing: { - if context.keyboard_handler.is_null() { - debugln!(DEBUG_ERROR, "on_layout_update invoked with invalid handler"); - break 'layout_processing; - } + 'layout_processing: { + if context.keyboard_handler.is_null() { + debugln!(DEBUG_ERROR, "on_layout_update invoked with invalid handler"); + break 'layout_processing; + } - let keyboard_handler = unsafe { context.keyboard_handler.as_mut() }.expect("bad keyboard handler"); + let keyboard_handler = unsafe { context.keyboard_handler.as_mut() }.expect("bad keyboard handler"); - let mut hii_database_protocol_ptr: *mut protocols::hii_database::Protocol = core::ptr::null_mut(); - let status = context.boot_services.locate_protocol( - &protocols::hii_database::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::null_mut(), - core::ptr::addr_of_mut!(hii_database_protocol_ptr) as *mut *mut c_void, - ); + let mut hii_database_protocol_ptr: *mut protocols::hii_database::Protocol = core::ptr::null_mut(); + let status = context.boot_services.locate_protocol( + &protocols::hii_database::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::null_mut(), + core::ptr::addr_of_mut!(hii_database_protocol_ptr) as *mut *mut c_void, + ); - if status.is_error() { - //nothing to do if there is no hii protocol. - break 'layout_processing; - } + if status.is_error() { + //nothing to do if there is no hii protocol. + break 'layout_processing; + } + + let hii_database_protocol = unsafe { + hii_database_protocol_ptr.as_mut().expect("Bad pointer returned from successful locate protocol.") + }; + + // retrieve keyboard layout size + let mut layout_buffer_len: u16 = 0; + match (hii_database_protocol.get_keyboard_layout)( + hii_database_protocol_ptr, + core::ptr::null_mut(), + &mut layout_buffer_len as *mut u16, + core::ptr::null_mut(), + ) { + efi::Status::NOT_FOUND => break 'layout_processing, + status if status != efi::Status::BUFFER_TOO_SMALL => { + debugln!( + DEBUG_ERROR, + "{:}: unexpected return from get_keyboard_layout when trying to determine length: {:x?}", + function!(), + status + ); + break 'layout_processing; + } + _ => (), + } - let hii_database_protocol = - unsafe { hii_database_protocol_ptr.as_mut().expect("Bad pointer returned from successful locate protocol.") }; - - // retrieve keyboard layout size - let mut layout_buffer_len: u16 = 0; - match (hii_database_protocol.get_keyboard_layout)( - hii_database_protocol_ptr, - core::ptr::null_mut(), - &mut layout_buffer_len as *mut u16, - core::ptr::null_mut(), - ) { - efi::Status::NOT_FOUND => break 'layout_processing, - status if status != efi::Status::BUFFER_TOO_SMALL => { - debugln!( - DEBUG_ERROR, - "{:}: unexpected return from get_keyboard_layout when trying to determine length: {:x?}", - function!(), - status + let mut keyboard_layout_buffer = vec![0u8; layout_buffer_len as usize]; + let status = (hii_database_protocol.get_keyboard_layout)( + hii_database_protocol_ptr, + core::ptr::null_mut(), + &mut layout_buffer_len as *mut u16, + keyboard_layout_buffer.as_mut_ptr() as *mut protocols::hii_database::KeyboardLayout<0>, ); - break 'layout_processing; - } - _ => (), - } - let mut keyboard_layout_buffer = vec![0u8; layout_buffer_len as usize]; - let status = (hii_database_protocol.get_keyboard_layout)( - hii_database_protocol_ptr, - core::ptr::null_mut(), - &mut layout_buffer_len as *mut u16, - keyboard_layout_buffer.as_mut_ptr() as *mut protocols::hii_database::KeyboardLayout<0>, - ); - - if status.is_error() { - debugln!(DEBUG_ERROR, "Unexpected return from get_keyboard_layout: {:x?}", status); - break 'layout_processing; - } + if status.is_error() { + debugln!(DEBUG_ERROR, "Unexpected return from get_keyboard_layout: {:x?}", status); + break 'layout_processing; + } - let keyboard_layout = hii_keyboard_layout::keyboard_layout_from_buffer(&keyboard_layout_buffer); - match keyboard_layout { - Ok(keyboard_layout) => { - keyboard_handler.key_queue.set_layout(Some(keyboard_layout)); - } - Err(_) => { - debugln!(DEBUG_ERROR, "keyboard::on_layout_update: Could not parse keyboard layout buffer."); - break 'layout_processing; - } + let keyboard_layout = hii_keyboard_layout::keyboard_layout_from_buffer(&keyboard_layout_buffer); + match keyboard_layout { + Ok(keyboard_layout) => { + keyboard_handler.key_queue.set_layout(Some(keyboard_layout)); + } + Err(_) => { + debugln!(DEBUG_ERROR, "keyboard::on_layout_update: Could not parse keyboard layout buffer."); + break 'layout_processing; + } + } } - } - context.boot_services.restore_tpl(old_tpl); + context.boot_services.restore_tpl(old_tpl); } #[cfg(test)] mod test { - use core::{ffi::c_void, mem::MaybeUninit, slice::from_raw_parts_mut}; - - use hii_keyboard_layout::HiiKeyboardLayout; - use r_efi::{efi, hii, protocols}; - use scroll::Pwrite; - - use crate::{ - boot_services::MockUefiBootServices, - hid_io::{HidReportReceiver, MockHidIo}, - keyboard::{key_queue::OrdKeyData, on_layout_update, KeyboardHidHandler, LayoutChangeContext}, - }; - - static BOOT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[ - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x06, // USAGE (Keyboard) - 0xa1, 0x01, // COLLECTION (Application) - 0x75, 0x01, // REPORT_SIZE (1) - 0x95, 0x08, // REPORT_COUNT (8) - 0x05, 0x07, // USAGE_PAGE (Key Codes) - 0x19, 0xE0, // USAGE_MINIMUM (224) - 0x29, 0xE7, // USAGE_MAXIMUM (231) - 0x15, 0x00, // LOGICAL_MAXIMUM (0) - 0x25, 0x01, // LOGICAL_MINIMUM (1) - 0x81, 0x02, // INPUT (Data, Var, Abs) (Modifier Byte) - 0x95, 0x01, // REPORT_COUNT (1) - 0x75, 0x08, // REPORT_SIZE (8) - 0x81, 0x03, // INPUT (Const) (Reserved Byte) - 0x95, 0x05, // REPORT_COUNT (5) - 0x75, 0x01, // REPORT_SIZE (1) - 0x05, 0x08, // USAGE_PAGE (LEDs) - 0x19, 0x01, // USAGE_MINIMUM (1) - 0x29, 0x05, // USAGE_MAXIMUM (5) - 0x91, 0x02, // OUTPUT (Data, Var, Abs) (LED report) - 0x95, 0x01, // REPORT_COUNT (1) - 0x75, 0x03, // REPORT_SIZE (3) - 0x91, 0x02, // OUTPUT (Constant) (LED report padding) - 0x95, 0x06, // REPORT_COUNT (6) - 0x75, 0x08, // REPORT_SIZE (8) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0xff, 00, // LOGICAL_MAXIMUM (255) - 0x05, 0x07, // USAGE_PAGE (Key Codes) - 0x19, 0x00, // USAGE_MINIMUM (0) - 0x2a, 0xff, 00, // USAGE_MAXIMUM (255) - 0x81, 0x00, // INPUT (Data, Array) - 0xc0, // END_COLLECTION - ]; - - static MOUSE_REPORT_DESCRIPTOR: &[u8] = &[ - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x02, // USAGE (Mouse) - 0xa1, 0x01, // COLLECTION (Application) - 0x09, 0x01, // USAGE(Pointer) - 0xa1, 0x00, // COLLECTION (Physical) - 0x05, 0x09, // USAGE_PAGE (Button) - 0x19, 0x01, // USAGE_MINIMUM(1) - 0x29, 0x05, // USAGE_MAXIMUM(5) - 0x15, 0x00, // LOGICAL_MINIMUM(0) - 0x25, 0x01, // LOGICAL_MAXIMUM(1) - 0x95, 0x05, // REPORT_COUNT(5) - 0x75, 0x01, // REPORT_SIZE(1) - 0x81, 0x02, // INPUT(Data, Variable, Absolute) - 0x95, 0x01, // REPORT_COUNT(1) - 0x75, 0x03, // REPORT_SIZE(3) - 0x81, 0x01, // INPUT(Constant, Array, Absolute) - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x30, // USAGE (X) - 0x09, 0x31, // USAGE (Y) - 0x09, 0x38, // USAGE (Wheel) - 0x15, 0x81, // LOGICAL_MINIMUM (-127) - 0x25, 0x7f, // LOGICAL_MAXIMUM (127) - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x03, // REPORT_COUNT (3) - 0x81, 0x06, // INPUT(Data, Variable, Relative) - 0xc0, // END_COLLECTION - 0xc0, // END_COLLECTION - ]; - - // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used - // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock - // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. - // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. - // This object needs to outlive anything that uses it - once created, it will live until the end of the program. - fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { - unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } - } - - #[test] - fn keyboard_initialize_should_fail_for_unsupported_descriptors() { - let boot_services = create_fake_static_boot_service(); - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&MOUSE_REPORT_DESCRIPTOR).unwrap())); - - assert_eq!(keyboard_handler.initialize(2 as efi::Handle, &hid_io), Err(efi::Status::UNSUPPORTED)); - } - - #[test] - fn keyboard_initialization_should_succeed_for_supported_descriptors() { - let boot_services = create_fake_static_boot_service(); - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); - boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); - boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); - boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); - - keyboard_handler.key_queue.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); - - assert_eq!(keyboard_handler.initialize(2 as efi::Handle, &hid_io), Ok(())); - } - - #[test] - fn keyboard_should_process_input_reports() { - let boot_services = create_fake_static_boot_service(); - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); - boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); - boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); - boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); - - keyboard_handler.key_queue.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); - keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); - - // press the 'a' key. - let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - assert_eq!(keyboard_handler.key_queue.pop_key().unwrap().key.unicode_char, 'a' as u16); - assert!(keyboard_handler.key_queue.peek_key().is_none()); - - // press the 'shift' key while 'a' remains pressed. - let report: &[u8] = &[0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - assert!(keyboard_handler.key_queue.peek_key().is_none()); - - // holding the 'shift' key while releasing 'a'. - let report: &[u8] = &[0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - assert!(keyboard_handler.key_queue.peek_key().is_none()); - - // holding the 'shift' key while pressing 'a' again. - let report: &[u8] = &[0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - assert_eq!(keyboard_handler.key_queue.pop_key().unwrap().key.unicode_char, 'A' as u16); - assert!(keyboard_handler.key_queue.peek_key().is_none()); - - // release the 'shift' key, press the 'ctrl' key, continue pressing 'a', and press 'b' - let report: &[u8] = &[0x01, 0x00, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - let key_data = keyboard_handler.key_queue.pop_key().unwrap(); - assert_eq!(key_data.key.unicode_char, 'b' as u16); - assert_eq!( - key_data.key_state.key_shift_state, - protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::LEFT_CONTROL_PRESSED - ); - assert!(keyboard_handler.key_queue.peek_key().is_none()); - - // enable partial keystrokes - keyboard_handler.key_queue.set_key_toggle_state(protocols::simple_text_input_ex::KEY_STATE_EXPOSED); - - // release the 'ctrl' key, press 'alt', continue pressing 'a' and 'b' - let report: &[u8] = &[0x04, 0x00, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - let key_data = keyboard_handler.key_queue.pop_key().unwrap(); - assert_eq!(key_data.key.unicode_char, 0); - assert_eq!( - key_data.key_state.key_shift_state, - protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::LEFT_ALT_PRESSED - ); - assert!(keyboard_handler.key_queue.peek_key().is_none()); - - // release all the keys - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - assert!(keyboard_handler.key_queue.peek_key().is_none()); - - // press the right logo key - let report: &[u8] = &[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - let key_data = keyboard_handler.key_queue.pop_key().unwrap(); - assert_eq!(key_data.key.unicode_char, 0); - assert_eq!( - key_data.key_state.key_shift_state, - protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::RIGHT_LOGO_PRESSED - ); - assert!(keyboard_handler.key_queue.peek_key().is_none()); - - // simulate rollover - let report: &[u8] = &[0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]; - keyboard_handler.receive_report(report, &hid_io); - assert!(keyboard_handler.key_queue.peek_key().is_none()); - - // pass some unsupported keys - let report: &[u8] = &[0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0]; - keyboard_handler.receive_report(report, &hid_io); - assert!(keyboard_handler.key_queue.peek_key().is_none()); - - // try all possible modifiers and all possible keys and make sure it doesn't panic. - // some of these may generate LED reports - let mut hid_io = MockHidIo::new(); - hid_io.expect_set_output_report().returning(|_, _| Ok(())); - for i in 0..0xff { - // avoid sending "Delete" keys as it will cause reset if CTRL-ALT are pressed. - if (i == 0x4C) || (i == 0x63) { - continue; - } - let report: &[u8] = &[i, 0x00, i, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); + use core::{ffi::c_void, mem::MaybeUninit, slice::from_raw_parts_mut}; + + use hii_keyboard_layout::HiiKeyboardLayout; + use r_efi::{efi, hii, protocols}; + use scroll::Pwrite; + + use crate::{ + boot_services::MockUefiBootServices, + hid_io::{HidReportReceiver, MockHidIo}, + keyboard::{key_queue::OrdKeyData, on_layout_update, KeyboardHidHandler, LayoutChangeContext}, + }; + + static BOOT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x06, // USAGE (Keyboard) + 0xa1, 0x01, // COLLECTION (Application) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x08, // REPORT_COUNT (8) + 0x05, 0x07, // USAGE_PAGE (Key Codes) + 0x19, 0xE0, // USAGE_MINIMUM (224) + 0x29, 0xE7, // USAGE_MAXIMUM (231) + 0x15, 0x00, // LOGICAL_MAXIMUM (0) + 0x25, 0x01, // LOGICAL_MINIMUM (1) + 0x81, 0x02, // INPUT (Data, Var, Abs) (Modifier Byte) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x08, // REPORT_SIZE (8) + 0x81, 0x03, // INPUT (Const) (Reserved Byte) + 0x95, 0x05, // REPORT_COUNT (5) + 0x75, 0x01, // REPORT_SIZE (1) + 0x05, 0x08, // USAGE_PAGE (LEDs) + 0x19, 0x01, // USAGE_MINIMUM (1) + 0x29, 0x05, // USAGE_MAXIMUM (5) + 0x91, 0x02, // OUTPUT (Data, Var, Abs) (LED report) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x03, // REPORT_SIZE (3) + 0x91, 0x02, // OUTPUT (Constant) (LED report padding) + 0x95, 0x06, // REPORT_COUNT (6) + 0x75, 0x08, // REPORT_SIZE (8) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26, 0xff, 00, // LOGICAL_MAXIMUM (255) + 0x05, 0x07, // USAGE_PAGE (Key Codes) + 0x19, 0x00, // USAGE_MINIMUM (0) + 0x2a, 0xff, 00, // USAGE_MAXIMUM (255) + 0x81, 0x00, // INPUT (Data, Array) + 0xc0, // END_COLLECTION + ]; + + static MOUSE_REPORT_DESCRIPTOR: &[u8] = &[ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x02, // USAGE (Mouse) + 0xa1, 0x01, // COLLECTION (Application) + 0x09, 0x01, // USAGE(Pointer) + 0xa1, 0x00, // COLLECTION (Physical) + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM(1) + 0x29, 0x05, // USAGE_MAXIMUM(5) + 0x15, 0x00, // LOGICAL_MINIMUM(0) + 0x25, 0x01, // LOGICAL_MAXIMUM(1) + 0x95, 0x05, // REPORT_COUNT(5) + 0x75, 0x01, // REPORT_SIZE(1) + 0x81, 0x02, // INPUT(Data, Variable, Absolute) + 0x95, 0x01, // REPORT_COUNT(1) + 0x75, 0x03, // REPORT_SIZE(3) + 0x81, 0x01, // INPUT(Constant, Array, Absolute) + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x09, 0x38, // USAGE (Wheel) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x03, // REPORT_COUNT (3) + 0x81, 0x06, // INPUT(Data, Variable, Relative) + 0xc0, // END_COLLECTION + 0xc0, // END_COLLECTION + ]; + + // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used + // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock + // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. + // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. + // This object needs to outlive anything that uses it - once created, it will live until the end of the program. + fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { + unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } } - } - - #[test] - fn keyboard_should_install_layout_if_not_already_present() { - let boot_services = create_fake_static_boot_service(); - boot_services.expect_create_event().returning(|_, _, _, _, event| unsafe { - event.write(3 as efi::Event); - efi::Status::SUCCESS - }); - boot_services.expect_create_event_ex().returning(|_, _, _, _, _, event| unsafe { - event.write(4 as efi::Event); - efi::Status::SUCCESS - }); - boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); - boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); - boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); - - static mut HANDLER: *mut KeyboardHidHandler = core::ptr::null_mut(); - - extern "efiapi" fn new_package_list( - _this: *const protocols::hii_database::Protocol, - _package_list: *const hii::PackageListHeader, - _driver_handle: efi::Handle, - _handle: *mut hii::Handle, - ) -> efi::Status { - efi::Status::SUCCESS + + #[test] + fn keyboard_initialize_should_fail_for_unsupported_descriptors() { + let boot_services = create_fake_static_boot_service(); + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&MOUSE_REPORT_DESCRIPTOR).unwrap())); + + assert_eq!(keyboard_handler.initialize(2 as efi::Handle, &hid_io), Err(efi::Status::UNSUPPORTED)); } - const TEST_KEYBOARD_GUID: efi::Guid = - efi::Guid::from_fields(0xf1796c10, 0xdafb, 0x4989, 0xa0, 0x82, &[0x75, 0xe9, 0x65, 0x76, 0xbe, 0x52]); - - static mut TEST_KEYBOARD_LAYOUT: HiiKeyboardLayout = - HiiKeyboardLayout { keys: Vec::new(), guid: TEST_KEYBOARD_GUID, descriptions: Vec::new() }; - unsafe { - //make a test keyboard layout that is different than the default. - TEST_KEYBOARD_LAYOUT = hii_keyboard_layout::get_default_keyboard_layout(); - TEST_KEYBOARD_LAYOUT.guid = TEST_KEYBOARD_GUID; - TEST_KEYBOARD_LAYOUT.keys.pop(); - TEST_KEYBOARD_LAYOUT.keys.pop(); - TEST_KEYBOARD_LAYOUT.keys.pop(); - TEST_KEYBOARD_LAYOUT.descriptions[0].description = "Test Keyboard Layout".to_string(); - TEST_KEYBOARD_LAYOUT.descriptions[0].language = "ts-TS".to_string(); + #[test] + fn keyboard_initialization_should_succeed_for_supported_descriptors() { + let boot_services = create_fake_static_boot_service(); + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); + boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); + boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); + boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); + + keyboard_handler.key_queue.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); + + assert_eq!(keyboard_handler.initialize(2 as efi::Handle, &hid_io), Ok(())); } - extern "efiapi" fn get_keyboard_layout( - _this: *const protocols::hii_database::Protocol, - _key_guid: *const efi::Guid, - keyboard_layout_length: *mut u16, - keyboard_layout_ptr: *mut protocols::hii_database::KeyboardLayout, - ) -> efi::Status { - let mut keyboard_layout_buffer = vec![0u8; 4096]; - let buffer_size = keyboard_layout_buffer.pwrite(unsafe { &TEST_KEYBOARD_LAYOUT }, 0).unwrap(); - keyboard_layout_buffer.resize(buffer_size, 0); - unsafe { - if keyboard_layout_length.read() < buffer_size as u16 { - keyboard_layout_length.write(buffer_size as u16); - return efi::Status::BUFFER_TOO_SMALL; - } else { - if keyboard_layout_ptr.is_null() { - panic!("bad keyboard pointer)"); - } - keyboard_layout_length.write(buffer_size as u16); - let slice = from_raw_parts_mut(keyboard_layout_ptr as *mut u8, buffer_size); - slice.copy_from_slice(&keyboard_layout_buffer); - return efi::Status::SUCCESS; + #[test] + fn keyboard_should_process_input_reports() { + let boot_services = create_fake_static_boot_service(); + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); + boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); + boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); + boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); + + keyboard_handler.key_queue.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); + keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); + + // press the 'a' key. + let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + assert_eq!(keyboard_handler.key_queue.pop_key().unwrap().key.unicode_char, 'a' as u16); + assert!(keyboard_handler.key_queue.peek_key().is_none()); + + // press the 'shift' key while 'a' remains pressed. + let report: &[u8] = &[0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + assert!(keyboard_handler.key_queue.peek_key().is_none()); + + // holding the 'shift' key while releasing 'a'. + let report: &[u8] = &[0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + assert!(keyboard_handler.key_queue.peek_key().is_none()); + + // holding the 'shift' key while pressing 'a' again. + let report: &[u8] = &[0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + assert_eq!(keyboard_handler.key_queue.pop_key().unwrap().key.unicode_char, 'A' as u16); + assert!(keyboard_handler.key_queue.peek_key().is_none()); + + // release the 'shift' key, press the 'ctrl' key, continue pressing 'a', and press 'b' + let report: &[u8] = &[0x01, 0x00, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + let key_data = keyboard_handler.key_queue.pop_key().unwrap(); + assert_eq!(key_data.key.unicode_char, 'b' as u16); + assert_eq!( + key_data.key_state.key_shift_state, + protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::LEFT_CONTROL_PRESSED + ); + assert!(keyboard_handler.key_queue.peek_key().is_none()); + + // enable partial keystrokes + keyboard_handler.key_queue.set_key_toggle_state(protocols::simple_text_input_ex::KEY_STATE_EXPOSED); + + // release the 'ctrl' key, press 'alt', continue pressing 'a' and 'b' + let report: &[u8] = &[0x04, 0x00, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + let key_data = keyboard_handler.key_queue.pop_key().unwrap(); + assert_eq!(key_data.key.unicode_char, 0); + assert_eq!( + key_data.key_state.key_shift_state, + protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::LEFT_ALT_PRESSED + ); + assert!(keyboard_handler.key_queue.peek_key().is_none()); + + // release all the keys + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + assert!(keyboard_handler.key_queue.peek_key().is_none()); + + // press the right logo key + let report: &[u8] = &[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + let key_data = keyboard_handler.key_queue.pop_key().unwrap(); + assert_eq!(key_data.key.unicode_char, 0); + assert_eq!( + key_data.key_state.key_shift_state, + protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::RIGHT_LOGO_PRESSED + ); + assert!(keyboard_handler.key_queue.peek_key().is_none()); + + // simulate rollover + let report: &[u8] = &[0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]; + keyboard_handler.receive_report(report, &hid_io); + assert!(keyboard_handler.key_queue.peek_key().is_none()); + + // pass some unsupported keys + let report: &[u8] = &[0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0]; + keyboard_handler.receive_report(report, &hid_io); + assert!(keyboard_handler.key_queue.peek_key().is_none()); + + // try all possible modifiers and all possible keys and make sure it doesn't panic. + // some of these may generate LED reports + let mut hid_io = MockHidIo::new(); + hid_io.expect_set_output_report().returning(|_, _| Ok(())); + for i in 0..0xff { + // avoid sending "Delete" keys as it will cause reset if CTRL-ALT are pressed. + if (i == 0x4C) || (i == 0x63) { + continue; + } + let report: &[u8] = &[i, 0x00, i, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); } - } } - extern "efiapi" fn set_keyboard_layout( - _this: *const protocols::hii_database::Protocol, - _key_guid: *mut efi::Guid, - ) -> efi::Status { - let boot_services = create_fake_static_boot_service(); - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); + #[test] + fn keyboard_should_install_layout_if_not_already_present() { + let boot_services = create_fake_static_boot_service(); + boot_services.expect_create_event().returning(|_, _, _, _, event| unsafe { + event.write(3 as efi::Event); + efi::Status::SUCCESS + }); + boot_services.expect_create_event_ex().returning(|_, _, _, _, _, event| unsafe { + event.write(4 as efi::Event); + efi::Status::SUCCESS + }); + boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); + boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); + boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); + + static mut HANDLER: *mut KeyboardHidHandler = core::ptr::null_mut(); + + extern "efiapi" fn new_package_list( + _this: *const protocols::hii_database::Protocol, + _package_list: *const hii::PackageListHeader, + _driver_handle: efi::Handle, + _handle: *mut hii::Handle, + ) -> efi::Status { + efi::Status::SUCCESS + } - boot_services.expect_locate_protocol().returning(|protocol, _, interface| { + const TEST_KEYBOARD_GUID: efi::Guid = + efi::Guid::from_fields(0xf1796c10, 0xdafb, 0x4989, 0xa0, 0x82, &[0x75, 0xe9, 0x65, 0x76, 0xbe, 0x52]); + + static mut TEST_KEYBOARD_LAYOUT: HiiKeyboardLayout = + HiiKeyboardLayout { keys: Vec::new(), guid: TEST_KEYBOARD_GUID, descriptions: Vec::new() }; unsafe { - match *protocol { - protocols::hii_database::PROTOCOL_GUID => { - let hii_database = MaybeUninit::::zeroed(); - let mut hii_database = hii_database.assume_init(); - hii_database.get_keyboard_layout = get_keyboard_layout; - interface.write(Box::into_raw(Box::new(hii_database)) as *mut c_void); + //make a test keyboard layout that is different than the default. + TEST_KEYBOARD_LAYOUT = hii_keyboard_layout::get_default_keyboard_layout(); + TEST_KEYBOARD_LAYOUT.guid = TEST_KEYBOARD_GUID; + TEST_KEYBOARD_LAYOUT.keys.pop(); + TEST_KEYBOARD_LAYOUT.keys.pop(); + TEST_KEYBOARD_LAYOUT.keys.pop(); + TEST_KEYBOARD_LAYOUT.descriptions[0].description = "Test Keyboard Layout".to_string(); + TEST_KEYBOARD_LAYOUT.descriptions[0].language = "ts-TS".to_string(); + } + + extern "efiapi" fn get_keyboard_layout( + _this: *const protocols::hii_database::Protocol, + _key_guid: *const efi::Guid, + keyboard_layout_length: *mut u16, + keyboard_layout_ptr: *mut protocols::hii_database::KeyboardLayout, + ) -> efi::Status { + let mut keyboard_layout_buffer = vec![0u8; 4096]; + let buffer_size = keyboard_layout_buffer.pwrite(unsafe { &TEST_KEYBOARD_LAYOUT }, 0).unwrap(); + keyboard_layout_buffer.resize(buffer_size, 0); + unsafe { + if keyboard_layout_length.read() < buffer_size as u16 { + keyboard_layout_length.write(buffer_size as u16); + return efi::Status::BUFFER_TOO_SMALL; + } else { + if keyboard_layout_ptr.is_null() { + panic!("bad keyboard pointer)"); + } + keyboard_layout_length.write(buffer_size as u16); + let slice = from_raw_parts_mut(keyboard_layout_ptr as *mut u8, buffer_size); + slice.copy_from_slice(&keyboard_layout_buffer); + return efi::Status::SUCCESS; + } } - unexpected_protocol => panic!("unexpected locate protocol request for {:?}", unexpected_protocol), - } } - efi::Status::SUCCESS - }); - let context = LayoutChangeContext { boot_services: boot_services, keyboard_handler: unsafe { HANDLER } }; - on_layout_update( - 3 as efi::Event, - &context as *const LayoutChangeContext as *mut LayoutChangeContext as *mut c_void, - ); - efi::Status::SUCCESS + extern "efiapi" fn set_keyboard_layout( + _this: *const protocols::hii_database::Protocol, + _key_guid: *mut efi::Guid, + ) -> efi::Status { + let boot_services = create_fake_static_boot_service(); + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); + + boot_services.expect_locate_protocol().returning(|protocol, _, interface| { + unsafe { + match *protocol { + protocols::hii_database::PROTOCOL_GUID => { + let hii_database = MaybeUninit::::zeroed(); + let mut hii_database = hii_database.assume_init(); + hii_database.get_keyboard_layout = get_keyboard_layout; + interface.write(Box::into_raw(Box::new(hii_database)) as *mut c_void); + } + unexpected_protocol => { + panic!("unexpected locate protocol request for {:?}", unexpected_protocol) + } + } + } + efi::Status::SUCCESS + }); + + let context = LayoutChangeContext { boot_services: boot_services, keyboard_handler: unsafe { HANDLER } }; + on_layout_update( + 3 as efi::Event, + &context as *const LayoutChangeContext as *mut LayoutChangeContext as *mut c_void, + ); + efi::Status::SUCCESS + } + + boot_services.expect_locate_protocol().returning(|protocol, _, interface| { + unsafe { + match *protocol { + protocols::hii_database::PROTOCOL_GUID => { + let hii_database = MaybeUninit::::zeroed(); + let mut hii_database = hii_database.assume_init(); + hii_database.new_package_list = new_package_list; + hii_database.set_keyboard_layout = set_keyboard_layout; + hii_database.get_keyboard_layout = get_keyboard_layout; + + interface.write(Box::into_raw(Box::new(hii_database)) as *mut c_void); + } + unexpected_protocol => panic!("unexpected locate protocol request for {:?}", unexpected_protocol), + } + } + efi::Status::SUCCESS + }); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + + unsafe { HANDLER = &mut keyboard_handler as *mut KeyboardHidHandler }; + + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); + + assert_eq!(keyboard_handler.initialize(2 as efi::Handle, &hid_io), Ok(())); } - boot_services.expect_locate_protocol().returning(|protocol, _, interface| { - unsafe { - match *protocol { - protocols::hii_database::PROTOCOL_GUID => { - let hii_database = MaybeUninit::::zeroed(); - let mut hii_database = hii_database.assume_init(); - hii_database.new_package_list = new_package_list; - hii_database.set_keyboard_layout = set_keyboard_layout; - hii_database.get_keyboard_layout = get_keyboard_layout; - - interface.write(Box::into_raw(Box::new(hii_database)) as *mut c_void); - } - unexpected_protocol => panic!("unexpected locate protocol request for {:?}", unexpected_protocol), - } - } - efi::Status::SUCCESS - }); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - - unsafe { HANDLER = &mut keyboard_handler as *mut KeyboardHidHandler }; - - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); - - assert_eq!(keyboard_handler.initialize(2 as efi::Handle, &hid_io), Ok(())); - } - - #[test] - fn reset_should_reset_keyboard() { - let boot_services = create_fake_static_boot_service(); - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); - boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); - boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); - boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); - - hid_io.expect_set_output_report().returning(|_, _| Ok(())); - - keyboard_handler.key_queue.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); - - assert_eq!(keyboard_handler.initialize(2 as efi::Handle, &hid_io), Ok(())); - - //buffer CapsLock + a, b, c - let report: &[u8] = &[0x00, 0x00, 0x39, 0x04, 0x05, 0x06, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - assert!(keyboard_handler.key_queue.peek_key().is_some()); - assert!(!keyboard_handler.led_state.is_empty()); - let prev_led_state = keyboard_handler.led_state.clone(); - assert!(!keyboard_handler.last_keys.is_empty()); - - let hid_io = MockHidIo::new(); - - keyboard_handler.reset(&hid_io, false).unwrap(); - assert!(keyboard_handler.key_queue.peek_key().is_none()); - assert!(keyboard_handler.last_keys.is_empty()); - assert_eq!(keyboard_handler.led_state, prev_led_state); - - let mut hid_io = MockHidIo::new(); - hid_io.expect_set_output_report().returning(|_, report| { - assert_eq!(report, &[0]); - Ok(()) - }); - - keyboard_handler.reset(&hid_io, true).unwrap(); - assert!(keyboard_handler.led_state.is_empty()); - } - - #[test] - fn misc_functions_test() { - let boot_services = create_fake_static_boot_service(); - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); - boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); - boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); - boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); - - keyboard_handler.key_queue.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); - keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); - - // press the 'a' key. - let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - assert_eq!( - OrdKeyData(keyboard_handler.peek_key().unwrap()), - OrdKeyData(keyboard_handler.key_queue.peek_key().unwrap()) - ); - assert_eq!( - OrdKeyData(keyboard_handler.key_queue.peek_key().unwrap()), - OrdKeyData(keyboard_handler.pop_key().unwrap()), - ); - - let mut hid_io = MockHidIo::new(); - hid_io.expect_set_output_report().returning(|_, _| Ok(())); - - //press capslock and release 'a' key - let report: &[u8] = &[0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - assert_eq!( - keyboard_handler.get_key_state().key_shift_state, - keyboard_handler.key_queue.init_key_state().key_shift_state - ); - assert_eq!( - keyboard_handler.get_key_state().key_toggle_state, - keyboard_handler.key_queue.init_key_state().key_toggle_state - ); - - keyboard_handler.set_key_toggle_state( - protocols::simple_text_input_ex::KEY_STATE_EXPOSED | protocols::simple_text_input_ex::CAPS_LOCK_ACTIVE, - ); - assert_eq!( - keyboard_handler.get_key_state().key_toggle_state, - protocols::simple_text_input_ex::KEY_STATE_EXPOSED - | protocols::simple_text_input_ex::TOGGLE_STATE_VALID - | protocols::simple_text_input_ex::CAPS_LOCK_ACTIVE - ); - - assert_eq!(keyboard_handler.get_controller(), keyboard_handler.controller); - assert_eq!(keyboard_handler.get_agent(), keyboard_handler.agent); - } - - #[test] - fn insert_and_remove_key_notifies_should_update_key_notify_structures() { - let boot_services = create_fake_static_boot_service(); - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); - boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); - boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); - boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); - - keyboard_handler.key_queue.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); - keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); - - extern "efiapi" fn mock_key_notify_callback( - _key_data: *mut protocols::simple_text_input_ex::KeyData, - ) -> efi::Status { - efi::Status::SUCCESS + #[test] + fn reset_should_reset_keyboard() { + let boot_services = create_fake_static_boot_service(); + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); + boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); + boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); + boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); + + hid_io.expect_set_output_report().returning(|_, _| Ok(())); + + keyboard_handler.key_queue.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); + + assert_eq!(keyboard_handler.initialize(2 as efi::Handle, &hid_io), Ok(())); + + //buffer CapsLock + a, b, c + let report: &[u8] = &[0x00, 0x00, 0x39, 0x04, 0x05, 0x06, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + assert!(keyboard_handler.key_queue.peek_key().is_some()); + assert!(!keyboard_handler.led_state.is_empty()); + let prev_led_state = keyboard_handler.led_state.clone(); + assert!(!keyboard_handler.last_keys.is_empty()); + + let hid_io = MockHidIo::new(); + + keyboard_handler.reset(&hid_io, false).unwrap(); + assert!(keyboard_handler.key_queue.peek_key().is_none()); + assert!(keyboard_handler.last_keys.is_empty()); + assert_eq!(keyboard_handler.led_state, prev_led_state); + + let mut hid_io = MockHidIo::new(); + hid_io.expect_set_output_report().returning(|_, report| { + assert_eq!(report, &[0]); + Ok(()) + }); + + keyboard_handler.reset(&hid_io, true).unwrap(); + assert!(keyboard_handler.led_state.is_empty()); } - extern "efiapi" fn mock_key_notify_callback2( - _key_data: *mut protocols::simple_text_input_ex::KeyData, - ) -> efi::Status { - efi::Status::SUCCESS + #[test] + fn misc_functions_test() { + let boot_services = create_fake_static_boot_service(); + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); + boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); + boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); + boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); + + keyboard_handler.key_queue.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); + keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); + + // press the 'a' key. + let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + assert_eq!( + OrdKeyData(keyboard_handler.peek_key().unwrap()), + OrdKeyData(keyboard_handler.key_queue.peek_key().unwrap()) + ); + assert_eq!( + OrdKeyData(keyboard_handler.key_queue.peek_key().unwrap()), + OrdKeyData(keyboard_handler.pop_key().unwrap()), + ); + + let mut hid_io = MockHidIo::new(); + hid_io.expect_set_output_report().returning(|_, _| Ok(())); + + //press capslock and release 'a' key + let report: &[u8] = &[0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + assert_eq!( + keyboard_handler.get_key_state().key_shift_state, + keyboard_handler.key_queue.init_key_state().key_shift_state + ); + assert_eq!( + keyboard_handler.get_key_state().key_toggle_state, + keyboard_handler.key_queue.init_key_state().key_toggle_state + ); + + keyboard_handler.set_key_toggle_state( + protocols::simple_text_input_ex::KEY_STATE_EXPOSED | protocols::simple_text_input_ex::CAPS_LOCK_ACTIVE, + ); + assert_eq!( + keyboard_handler.get_key_state().key_toggle_state, + protocols::simple_text_input_ex::KEY_STATE_EXPOSED + | protocols::simple_text_input_ex::TOGGLE_STATE_VALID + | protocols::simple_text_input_ex::CAPS_LOCK_ACTIVE + ); + + assert_eq!(keyboard_handler.get_controller(), keyboard_handler.controller); + assert_eq!(keyboard_handler.get_agent(), keyboard_handler.agent); } - let mut key_data: protocols::simple_text_input_ex::KeyData = Default::default(); - - key_data.key.unicode_char = 'a' as u16; - let handle = keyboard_handler.insert_key_notify_callback(key_data.clone(), mock_key_notify_callback); - assert_eq!(handle, 1); - - key_data.key.unicode_char = 'b' as u16; - let handle = keyboard_handler.insert_key_notify_callback(key_data.clone(), mock_key_notify_callback); - assert_eq!(handle, 2); - - key_data.key.unicode_char = 'c' as u16; - let handle = keyboard_handler.insert_key_notify_callback(key_data.clone(), mock_key_notify_callback); - assert_eq!(handle, 3); - //insert a second callback function tied to same key - let handle = keyboard_handler.insert_key_notify_callback(key_data.clone(), mock_key_notify_callback2); - assert_eq!(handle, 4); - - //insert a key_data/callback pair that is already present. - key_data.key.unicode_char = 'a' as u16; - let handle = keyboard_handler.insert_key_notify_callback(key_data.clone(), mock_key_notify_callback); - assert_eq!(handle, 1); - - //check state after adding callbacks. - assert_eq!(keyboard_handler.next_notify_handle, 4); - assert_eq!(keyboard_handler.notification_callbacks.len(), 4); - key_data.key.unicode_char = 'a' as u16; - assert_eq!(keyboard_handler.notification_callbacks.get(&1).unwrap().0, OrdKeyData(key_data)); - assert!(keyboard_handler.notification_callbacks.get(&1).unwrap().1 == mock_key_notify_callback); - key_data.key.unicode_char = 'b' as u16; - assert_eq!(keyboard_handler.notification_callbacks.get(&2).unwrap().0, OrdKeyData(key_data)); - assert!(keyboard_handler.notification_callbacks.get(&2).unwrap().1 == mock_key_notify_callback); - key_data.key.unicode_char = 'c' as u16; - assert_eq!(keyboard_handler.notification_callbacks.get(&3).unwrap().0, OrdKeyData(key_data)); - assert!(keyboard_handler.notification_callbacks.get(&3).unwrap().1 == mock_key_notify_callback); - assert_eq!(keyboard_handler.notification_callbacks.get(&4).unwrap().0, OrdKeyData(key_data)); - assert!(keyboard_handler.notification_callbacks.get(&4).unwrap().1 == mock_key_notify_callback2); - - //press and release 'c' key - let report: &[u8] = &[0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - key_data.key_state.key_shift_state = protocols::simple_text_input_ex::SHIFT_STATE_VALID; - key_data.key_state.key_toggle_state = protocols::simple_text_input_ex::TOGGLE_STATE_VALID; - let (callback_key_data, callbacks) = keyboard_handler.get_pending_callbacks(); - assert_eq!(OrdKeyData(key_data), OrdKeyData(callback_key_data.unwrap())); - assert!(callbacks.contains( - &(mock_key_notify_callback as extern "efiapi" fn(*mut protocols::simple_text_input_ex::KeyData) -> efi::Status) - )); - assert!(callbacks.contains( - &(mock_key_notify_callback2 as extern "efiapi" fn(*mut protocols::simple_text_input_ex::KeyData) -> efi::Status) - )); - - let (callback_key_data, callbacks) = keyboard_handler.get_pending_callbacks(); - assert!(callback_key_data.is_none()); - assert!(callbacks.is_empty()); - - //remove one of the 'c' callbacks and make sure the other still works. - keyboard_handler.remove_key_notify_callback(4).unwrap(); - - //press and release 'a' key and 'c' key - let report: &[u8] = &[0x00, 0x00, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - loop { - let (callback_key_data, callbacks) = keyboard_handler.get_pending_callbacks(); - if let Some(callback_key_data) = callback_key_data { - match callback_key_data.key.unicode_char { - char if char == 'a' as u16 || char == 'c' as u16 => { - assert!(callbacks.contains( - &(mock_key_notify_callback + #[test] + fn insert_and_remove_key_notifies_should_update_key_notify_structures() { + let boot_services = create_fake_static_boot_service(); + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, _| efi::Status::SUCCESS); + boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); + boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); + boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); + + keyboard_handler.key_queue.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); + keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); + + extern "efiapi" fn mock_key_notify_callback( + _key_data: *mut protocols::simple_text_input_ex::KeyData, + ) -> efi::Status { + efi::Status::SUCCESS + } + + extern "efiapi" fn mock_key_notify_callback2( + _key_data: *mut protocols::simple_text_input_ex::KeyData, + ) -> efi::Status { + efi::Status::SUCCESS + } + + let mut key_data: protocols::simple_text_input_ex::KeyData = Default::default(); + + key_data.key.unicode_char = 'a' as u16; + let handle = keyboard_handler.insert_key_notify_callback(key_data.clone(), mock_key_notify_callback); + assert_eq!(handle, 1); + + key_data.key.unicode_char = 'b' as u16; + let handle = keyboard_handler.insert_key_notify_callback(key_data.clone(), mock_key_notify_callback); + assert_eq!(handle, 2); + + key_data.key.unicode_char = 'c' as u16; + let handle = keyboard_handler.insert_key_notify_callback(key_data.clone(), mock_key_notify_callback); + assert_eq!(handle, 3); + //insert a second callback function tied to same key + let handle = keyboard_handler.insert_key_notify_callback(key_data.clone(), mock_key_notify_callback2); + assert_eq!(handle, 4); + + //insert a key_data/callback pair that is already present. + key_data.key.unicode_char = 'a' as u16; + let handle = keyboard_handler.insert_key_notify_callback(key_data.clone(), mock_key_notify_callback); + assert_eq!(handle, 1); + + //check state after adding callbacks. + assert_eq!(keyboard_handler.next_notify_handle, 4); + assert_eq!(keyboard_handler.notification_callbacks.len(), 4); + key_data.key.unicode_char = 'a' as u16; + assert_eq!(keyboard_handler.notification_callbacks.get(&1).unwrap().0, OrdKeyData(key_data)); + assert!(keyboard_handler.notification_callbacks.get(&1).unwrap().1 == mock_key_notify_callback); + key_data.key.unicode_char = 'b' as u16; + assert_eq!(keyboard_handler.notification_callbacks.get(&2).unwrap().0, OrdKeyData(key_data)); + assert!(keyboard_handler.notification_callbacks.get(&2).unwrap().1 == mock_key_notify_callback); + key_data.key.unicode_char = 'c' as u16; + assert_eq!(keyboard_handler.notification_callbacks.get(&3).unwrap().0, OrdKeyData(key_data)); + assert!(keyboard_handler.notification_callbacks.get(&3).unwrap().1 == mock_key_notify_callback); + assert_eq!(keyboard_handler.notification_callbacks.get(&4).unwrap().0, OrdKeyData(key_data)); + assert!(keyboard_handler.notification_callbacks.get(&4).unwrap().1 == mock_key_notify_callback2); + + //press and release 'c' key + let report: &[u8] = &[0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + key_data.key_state.key_shift_state = protocols::simple_text_input_ex::SHIFT_STATE_VALID; + key_data.key_state.key_toggle_state = protocols::simple_text_input_ex::TOGGLE_STATE_VALID; + let (callback_key_data, callbacks) = keyboard_handler.get_pending_callbacks(); + assert_eq!(OrdKeyData(key_data), OrdKeyData(callback_key_data.unwrap())); + assert!(callbacks.contains( + &(mock_key_notify_callback as extern "efiapi" fn(*mut protocols::simple_text_input_ex::KeyData) -> efi::Status) - )); - } - _ => panic!("unexpected pending callback key"), + )); + assert!(callbacks.contains( + &(mock_key_notify_callback2 + as extern "efiapi" fn(*mut protocols::simple_text_input_ex::KeyData) -> efi::Status) + )); + + let (callback_key_data, callbacks) = keyboard_handler.get_pending_callbacks(); + assert!(callback_key_data.is_none()); + assert!(callbacks.is_empty()); + + //remove one of the 'c' callbacks and make sure the other still works. + keyboard_handler.remove_key_notify_callback(4).unwrap(); + + //press and release 'a' key and 'c' key + let report: &[u8] = &[0x00, 0x00, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + loop { + let (callback_key_data, callbacks) = keyboard_handler.get_pending_callbacks(); + if let Some(callback_key_data) = callback_key_data { + match callback_key_data.key.unicode_char { + char if char == 'a' as u16 || char == 'c' as u16 => { + assert!(callbacks.contains( + &(mock_key_notify_callback + as extern "efiapi" fn(*mut protocols::simple_text_input_ex::KeyData) -> efi::Status) + )); + } + _ => panic!("unexpected pending callback key"), + } + } else { + break; + } } - } else { - break; - } - } - //remove all the callbacks. - keyboard_handler.remove_key_notify_callback(1).unwrap(); - keyboard_handler.remove_key_notify_callback(2).unwrap(); - keyboard_handler.remove_key_notify_callback(3).unwrap(); - - //press and release 'a' key 'b' key, and 'c' key - let report: &[u8] = &[0x00, 0x00, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - let (callback_key_data, callbacks) = keyboard_handler.get_pending_callbacks(); - assert!(callback_key_data.is_none()); - assert!(callbacks.is_empty()); - } + //remove all the callbacks. + keyboard_handler.remove_key_notify_callback(1).unwrap(); + keyboard_handler.remove_key_notify_callback(2).unwrap(); + keyboard_handler.remove_key_notify_callback(3).unwrap(); + + //press and release 'a' key 'b' key, and 'c' key + let report: &[u8] = &[0x00, 0x00, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + let (callback_key_data, callbacks) = keyboard_handler.get_pending_callbacks(); + assert!(callback_key_data.is_none()); + assert!(callbacks.is_empty()); + } } diff --git a/HidPkg/UefiHidDxeV2/src/keyboard/simple_text_in.rs b/HidPkg/UefiHidDxeV2/src/keyboard/simple_text_in.rs index ff0b10e1c5..2b8c7e9170 100644 --- a/HidPkg/UefiHidDxeV2/src/keyboard/simple_text_in.rs +++ b/HidPkg/UefiHidDxeV2/src/keyboard/simple_text_in.rs @@ -15,9 +15,9 @@ use r_efi::{efi, protocols}; use rust_advanced_logger_dxe::{debugln, DEBUG_ERROR}; use crate::{ - boot_services::UefiBootServices, - hid_io::{HidIoFactory, UefiHidIoFactory}, - keyboard::KeyboardHidHandler, + boot_services::UefiBootServices, + hid_io::{HidIoFactory, UefiHidIoFactory}, + keyboard::KeyboardHidHandler, }; /// FFI context @@ -34,608 +34,610 @@ use crate::{ /// simple_text_in protocol structure. #[repr(C)] pub struct SimpleTextInFfi { - simple_text_in: protocols::simple_text_input::Protocol, - boot_services: &'static dyn UefiBootServices, - keyboard_handler: *mut KeyboardHidHandler, + simple_text_in: protocols::simple_text_input::Protocol, + boot_services: &'static dyn UefiBootServices, + keyboard_handler: *mut KeyboardHidHandler, } impl SimpleTextInFfi { - /// Installs the simple text in protocol - pub fn install( - boot_services: &'static dyn UefiBootServices, - controller: efi::Handle, - keyboard_handler: &mut KeyboardHidHandler, - ) -> Result<(), efi::Status> { - //Create simple_text_in context - let simple_text_in_ctx = SimpleTextInFfi { - simple_text_in: protocols::simple_text_input::Protocol { - reset: Self::simple_text_in_reset, - read_key_stroke: Self::simple_text_in_read_key_stroke, - wait_for_key: core::ptr::null_mut(), - }, - boot_services, - keyboard_handler: keyboard_handler as *mut KeyboardHidHandler, - }; + /// Installs the simple text in protocol + pub fn install( + boot_services: &'static dyn UefiBootServices, + controller: efi::Handle, + keyboard_handler: &mut KeyboardHidHandler, + ) -> Result<(), efi::Status> { + //Create simple_text_in context + let simple_text_in_ctx = SimpleTextInFfi { + simple_text_in: protocols::simple_text_input::Protocol { + reset: Self::simple_text_in_reset, + read_key_stroke: Self::simple_text_in_read_key_stroke, + wait_for_key: core::ptr::null_mut(), + }, + boot_services, + keyboard_handler: keyboard_handler as *mut KeyboardHidHandler, + }; + + let simple_text_in_ptr = Box::into_raw(Box::new(simple_text_in_ctx)); + + //create event for wait_for_key + let mut wait_for_key_event: efi::Event = core::ptr::null_mut(); + let status = boot_services.create_event( + efi::EVT_NOTIFY_WAIT, + efi::TPL_NOTIFY, + Some(Self::simple_text_in_wait_for_key), + simple_text_in_ptr as *mut c_void, + core::ptr::addr_of_mut!(wait_for_key_event), + ); + if status.is_error() { + drop(unsafe { Box::from_raw(simple_text_in_ptr) }); + return Err(status); + } - let simple_text_in_ptr = Box::into_raw(Box::new(simple_text_in_ctx)); - - //create event for wait_for_key - let mut wait_for_key_event: efi::Event = core::ptr::null_mut(); - let status = boot_services.create_event( - efi::EVT_NOTIFY_WAIT, - efi::TPL_NOTIFY, - Some(Self::simple_text_in_wait_for_key), - simple_text_in_ptr as *mut c_void, - core::ptr::addr_of_mut!(wait_for_key_event), - ); - if status.is_error() { - drop(unsafe { Box::from_raw(simple_text_in_ptr) }); - return Err(status); - } + unsafe { (*simple_text_in_ptr).simple_text_in.wait_for_key = wait_for_key_event }; + + //install the simple_text_in protocol + let mut controller = controller; + let status = boot_services.install_protocol_interface( + core::ptr::addr_of_mut!(controller), + &protocols::simple_text_input::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + efi::NATIVE_INTERFACE, + simple_text_in_ptr as *mut c_void, + ); + + if status.is_error() { + let _ = boot_services.close_event(wait_for_key_event); + drop(unsafe { Box::from_raw(simple_text_in_ptr) }); + return Err(status); + } - unsafe { (*simple_text_in_ptr).simple_text_in.wait_for_key = wait_for_key_event }; - - //install the simple_text_in protocol - let mut controller = controller; - let status = boot_services.install_protocol_interface( - core::ptr::addr_of_mut!(controller), - &protocols::simple_text_input::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - efi::NATIVE_INTERFACE, - simple_text_in_ptr as *mut c_void, - ); - - if status.is_error() { - let _ = boot_services.close_event(wait_for_key_event); - drop(unsafe { Box::from_raw(simple_text_in_ptr) }); - return Err(status); + Ok(()) } - Ok(()) - } + /// Uninstalls the simple text in protocol + pub fn uninstall( + boot_services: &'static dyn UefiBootServices, + agent: efi::Handle, + controller: efi::Handle, + ) -> Result<(), efi::Status> { + //Controller is set - that means initialize() was called, and there is potential state exposed thru FFI that needs + //to be cleaned up. + let mut simple_text_in_ptr: *mut SimpleTextInFfi = core::ptr::null_mut(); + let status = boot_services.open_protocol( + controller, + &protocols::simple_text_input::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::addr_of_mut!(simple_text_in_ptr) as *mut *mut c_void, + agent, + controller, + efi::OPEN_PROTOCOL_GET_PROTOCOL, + ); + if status.is_error() { + //No protocol is actually installed on this controller, so nothing to clean up. + return Ok(()); + } - /// Uninstalls the simple text in protocol - pub fn uninstall( - boot_services: &'static dyn UefiBootServices, - agent: efi::Handle, - controller: efi::Handle, - ) -> Result<(), efi::Status> { - //Controller is set - that means initialize() was called, and there is potential state exposed thru FFI that needs - //to be cleaned up. - let mut simple_text_in_ptr: *mut SimpleTextInFfi = core::ptr::null_mut(); - let status = boot_services.open_protocol( - controller, - &protocols::simple_text_input::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::addr_of_mut!(simple_text_in_ptr) as *mut *mut c_void, - agent, - controller, - efi::OPEN_PROTOCOL_GET_PROTOCOL, - ); - if status.is_error() { - //No protocol is actually installed on this controller, so nothing to clean up. - return Ok(()); - } + //Attempt to uninstall the simple_text_in interface - this should disconnect any drivers using it and release + //the interface. + let status = boot_services.uninstall_protocol_interface( + controller, + &protocols::simple_text_input::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + simple_text_in_ptr as *mut c_void, + ); + if status.is_error() { + //An error here means some other driver might be holding on to the simple_text_in_ptr. + //Mark the instance invalid by setting the keyboard_handler raw pointer to null, but leak the PointerContext + //instance. Leaking context allows calls through the pointers on absolute_pointer_ptr to continue to resolve + //and return error based on observing keyboard_handler is null. + debugln!(DEBUG_ERROR, "Failed to uninstall simple_text_in interface, status: {:x?}", status); + + unsafe { + (*simple_text_in_ptr).keyboard_handler = core::ptr::null_mut(); + } + //return without tearing down the context. + return Err(status); + } - //Attempt to uninstall the simple_text_in interface - this should disconnect any drivers using it and release - //the interface. - let status = boot_services.uninstall_protocol_interface( - controller, - &protocols::simple_text_input::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - simple_text_in_ptr as *mut c_void, - ); - if status.is_error() { - //An error here means some other driver might be holding on to the simple_text_in_ptr. - //Mark the instance invalid by setting the keyboard_handler raw pointer to null, but leak the PointerContext - //instance. Leaking context allows calls through the pointers on absolute_pointer_ptr to continue to resolve - //and return error based on observing keyboard_handler is null. - debugln!(DEBUG_ERROR, "Failed to uninstall simple_text_in interface, status: {:x?}", status); - - unsafe { - (*simple_text_in_ptr).keyboard_handler = core::ptr::null_mut(); - } - //return without tearing down the context. - return Err(status); + let wait_for_key_event: efi::Handle = unsafe { (*simple_text_in_ptr).simple_text_in.wait_for_key }; + let status = boot_services.close_event(wait_for_key_event); + if status.is_error() { + //An error here means the event was not closed, so in theory the notification_callback on it could still be + //fired. + //Mark the instance invalid by setting the keyboard_handler raw pointer to null, but leak the PointerContext + //instance. Leaking context allows calls through the pointers on simple_text_in_ptr to continue to resolve + //and return error based on observing keyboard_handler is null. + debugln!(DEBUG_ERROR, "Failed to close simple_text_in_ptr.wait_for_key event, status: {:x?}", status); + unsafe { + (*simple_text_in_ptr).keyboard_handler = core::ptr::null_mut(); + } + return Err(status); + } + // None of the parts of simple_text_in_ptr are in use, so it is safe to drop it. + drop(unsafe { Box::from_raw(simple_text_in_ptr) }); + Ok(()) } - let wait_for_key_event: efi::Handle = unsafe { (*simple_text_in_ptr).simple_text_in.wait_for_key }; - let status = boot_services.close_event(wait_for_key_event); - if status.is_error() { - //An error here means the event was not closed, so in theory the notification_callback on it could still be - //fired. - //Mark the instance invalid by setting the keyboard_handler raw pointer to null, but leak the PointerContext - //instance. Leaking context allows calls through the pointers on simple_text_in_ptr to continue to resolve - //and return error based on observing keyboard_handler is null. - debugln!(DEBUG_ERROR, "Failed to close simple_text_in_ptr.wait_for_key event, status: {:x?}", status); - unsafe { - (*simple_text_in_ptr).keyboard_handler = core::ptr::null_mut(); - } - return Err(status); - } - // None of the parts of simple_text_in_ptr are in use, so it is safe to drop it. - drop(unsafe { Box::from_raw(simple_text_in_ptr) }); - Ok(()) - } - - // resets the keyboard state - part of the simple_text_in protocol interface. - extern "efiapi" fn simple_text_in_reset( - this: *mut protocols::simple_text_input::Protocol, - extended_verification: efi::Boolean, - ) -> efi::Status { - if this.is_null() { - return efi::Status::INVALID_PARAMETER; - } - let context = unsafe { (this as *mut SimpleTextInFfi).as_mut() }.expect("bad pointer"); - let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); - let mut status = efi::Status::DEVICE_ERROR; - '_reset_processing: { - let keyboard_handler = unsafe { context.keyboard_handler.as_mut() }; - if let Some(keyboard_handler) = keyboard_handler { - //reset requires an instance of hid_io to allow for LED updating, so use UefiHidIoFactory to build one. - if let Some(controller) = keyboard_handler.get_controller() { - let hid_io = - UefiHidIoFactory::new(context.boot_services, keyboard_handler.get_agent()).new_hid_io(controller, false); - if let Ok(hid_io) = hid_io { - if let Err(err) = keyboard_handler.reset(hid_io.as_ref(), extended_verification.into()) { - status = err; - } else { - status = efi::Status::SUCCESS; + // resets the keyboard state - part of the simple_text_in protocol interface. + extern "efiapi" fn simple_text_in_reset( + this: *mut protocols::simple_text_input::Protocol, + extended_verification: efi::Boolean, + ) -> efi::Status { + if this.is_null() { + return efi::Status::INVALID_PARAMETER; + } + let context = unsafe { (this as *mut SimpleTextInFfi).as_mut() }.expect("bad pointer"); + let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); + let mut status = efi::Status::DEVICE_ERROR; + '_reset_processing: { + let keyboard_handler = unsafe { context.keyboard_handler.as_mut() }; + if let Some(keyboard_handler) = keyboard_handler { + //reset requires an instance of hid_io to allow for LED updating, so use UefiHidIoFactory to build one. + if let Some(controller) = keyboard_handler.get_controller() { + let hid_io = UefiHidIoFactory::new(context.boot_services, keyboard_handler.get_agent()) + .new_hid_io(controller, false); + if let Ok(hid_io) = hid_io { + if let Err(err) = keyboard_handler.reset(hid_io.as_ref(), extended_verification.into()) { + status = err; + } else { + status = efi::Status::SUCCESS; + } + } + } } - } } - } + context.boot_services.restore_tpl(old_tpl); + status } - context.boot_services.restore_tpl(old_tpl); - status - } - - // reads a key stroke - part of the simple_text_in protocol interface. - extern "efiapi" fn simple_text_in_read_key_stroke( - this: *mut protocols::simple_text_input::Protocol, - key: *mut protocols::simple_text_input::InputKey, - ) -> efi::Status { - if this.is_null() || key.is_null() { - return efi::Status::INVALID_PARAMETER; - } - let context = unsafe { (this as *mut SimpleTextInFfi).as_mut() }.expect("bad pointer"); - let status; - let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); - 'read_key_stroke: { - let keyboard_handler = unsafe { context.keyboard_handler.as_mut() }; - if let Some(keyboard_handler) = keyboard_handler { - loop { - if let Some(mut key_data) = keyboard_handler.pop_key() { - // skip partials - if key_data.key.unicode_char == 0 && key_data.key.scan_code == 0 { - continue; - } - //translate ctrl-alpha to corresponding control value. ctrl-a = 0x0001, ctrl-z = 0x001A - const CONTROL_PRESSED: u32 = protocols::simple_text_input_ex::RIGHT_CONTROL_PRESSED - | protocols::simple_text_input_ex::LEFT_CONTROL_PRESSED; - if (key_data.key_state.key_shift_state & CONTROL_PRESSED) != 0 { - if key_data.key.unicode_char >= 0x0061 && key_data.key.unicode_char <= 0x007a { - //'a' to 'z' - key_data.key.unicode_char = (key_data.key.unicode_char - 0x0061) + 1; - } - if key_data.key.unicode_char >= 0x0041 && key_data.key.unicode_char <= 0x005a { - //'A' to 'Z' - key_data.key.unicode_char = (key_data.key.unicode_char - 0x0041) + 1; - } + + // reads a key stroke - part of the simple_text_in protocol interface. + extern "efiapi" fn simple_text_in_read_key_stroke( + this: *mut protocols::simple_text_input::Protocol, + key: *mut protocols::simple_text_input::InputKey, + ) -> efi::Status { + if this.is_null() || key.is_null() { + return efi::Status::INVALID_PARAMETER; + } + let context = unsafe { (this as *mut SimpleTextInFfi).as_mut() }.expect("bad pointer"); + let status; + let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); + 'read_key_stroke: { + let keyboard_handler = unsafe { context.keyboard_handler.as_mut() }; + if let Some(keyboard_handler) = keyboard_handler { + loop { + if let Some(mut key_data) = keyboard_handler.pop_key() { + // skip partials + if key_data.key.unicode_char == 0 && key_data.key.scan_code == 0 { + continue; + } + //translate ctrl-alpha to corresponding control value. ctrl-a = 0x0001, ctrl-z = 0x001A + const CONTROL_PRESSED: u32 = protocols::simple_text_input_ex::RIGHT_CONTROL_PRESSED + | protocols::simple_text_input_ex::LEFT_CONTROL_PRESSED; + if (key_data.key_state.key_shift_state & CONTROL_PRESSED) != 0 { + if key_data.key.unicode_char >= 0x0061 && key_data.key.unicode_char <= 0x007a { + //'a' to 'z' + key_data.key.unicode_char = (key_data.key.unicode_char - 0x0061) + 1; + } + if key_data.key.unicode_char >= 0x0041 && key_data.key.unicode_char <= 0x005a { + //'A' to 'Z' + key_data.key.unicode_char = (key_data.key.unicode_char - 0x0041) + 1; + } + } + unsafe { key.write(key_data.key) } + status = efi::Status::SUCCESS; + } else { + status = efi::Status::NOT_READY; + } + break 'read_key_stroke; + } + } else { + status = efi::Status::DEVICE_ERROR; + break 'read_key_stroke; } - unsafe { key.write(key_data.key) } - status = efi::Status::SUCCESS; - } else { - status = efi::Status::NOT_READY; - } - break 'read_key_stroke; } - } else { - status = efi::Status::DEVICE_ERROR; - break 'read_key_stroke; - } - } - context.boot_services.restore_tpl(old_tpl); - status - } - - // Event handler function for the wait_for_key_event - extern "efiapi" fn simple_text_in_wait_for_key(event: efi::Event, context: *mut c_void) { - if context.is_null() { - debugln!(DEBUG_ERROR, "simple_text_in_wait_for_key invoked with invalid context"); - return; + context.boot_services.restore_tpl(old_tpl); + status } - let context = unsafe { (context as *mut SimpleTextInFfi).as_mut() }.expect("bad pointer"); - let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); - { - if let Some(keyboard_handler) = unsafe { context.keyboard_handler.as_mut() } { - while let Some(key_data) = keyboard_handler.peek_key() { - if key_data.key.unicode_char == 0 && key_data.key.scan_code == 0 { - // consume (and ignore) the partial stroke. - let _ = keyboard_handler.pop_key(); - continue; - } else { - // valid keystroke - context.boot_services.signal_event(event); - break; - } + + // Event handler function for the wait_for_key_event + extern "efiapi" fn simple_text_in_wait_for_key(event: efi::Event, context: *mut c_void) { + if context.is_null() { + debugln!(DEBUG_ERROR, "simple_text_in_wait_for_key invoked with invalid context"); + return; + } + let context = unsafe { (context as *mut SimpleTextInFfi).as_mut() }.expect("bad pointer"); + let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); + { + if let Some(keyboard_handler) = unsafe { context.keyboard_handler.as_mut() } { + while let Some(key_data) = keyboard_handler.peek_key() { + if key_data.key.unicode_char == 0 && key_data.key.scan_code == 0 { + // consume (and ignore) the partial stroke. + let _ = keyboard_handler.pop_key(); + continue; + } else { + // valid keystroke + context.boot_services.signal_event(event); + break; + } + } + } } - } + context.boot_services.restore_tpl(old_tpl); } - context.boot_services.restore_tpl(old_tpl); - } } #[cfg(test)] mod test { - use core::{ - ffi::c_void, - mem::MaybeUninit, - sync::atomic::{AtomicBool, AtomicPtr}, - }; - - use crate::{ - boot_services::MockUefiBootServices, - hid_io::{HidReportReceiver, MockHidIo}, - keyboard::KeyboardHidHandler, - }; - use r_efi::{efi, protocols}; - - use super::SimpleTextInFfi; - - static BOOT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[ - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x06, // USAGE (Keyboard) - 0xa1, 0x01, // COLLECTION (Application) - 0x75, 0x01, // REPORT_SIZE (1) - 0x95, 0x08, // REPORT_COUNT (8) - 0x05, 0x07, // USAGE_PAGE (Key Codes) - 0x19, 0xE0, // USAGE_MINIMUM (224) - 0x29, 0xE7, // USAGE_MAXIMUM (231) - 0x15, 0x00, // LOGICAL_MAXIMUM (0) - 0x25, 0x01, // LOGICAL_MINIMUM (1) - 0x81, 0x02, // INPUT (Data, Var, Abs) (Modifier Byte) - 0x95, 0x01, // REPORT_COUNT (1) - 0x75, 0x08, // REPORT_SIZE (8) - 0x81, 0x03, // INPUT (Const) (Reserved Byte) - 0x95, 0x05, // REPORT_COUNT (5) - 0x75, 0x01, // REPORT_SIZE (1) - 0x05, 0x08, // USAGE_PAGE (LEDs) - 0x19, 0x01, // USAGE_MINIMUM (1) - 0x29, 0x05, // USAGE_MAXIMUM (5) - 0x91, 0x02, // OUTPUT (Data, Var, Abs) (LED report) - 0x95, 0x01, // REPORT_COUNT (1) - 0x75, 0x03, // REPORT_SIZE (3) - 0x91, 0x02, // OUTPUT (Constant) (LED report padding) - 0x95, 0x06, // REPORT_COUNT (6) - 0x75, 0x08, // REPORT_SIZE (8) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0xff, 00, // LOGICAL_MAXIMUM (255) - 0x05, 0x07, // USAGE_PAGE (Key Codes) - 0x19, 0x00, // USAGE_MINIMUM (0) - 0x2a, 0xff, 00, // USAGE_MAXIMUM (255) - 0x81, 0x00, // INPUT (Data, Array) - 0xc0, // END_COLLECTION - ]; - - // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used - // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock - // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. - // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. - // This object needs to outlive anything that uses it - once created, it will live until the end of the program. - fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { - unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } - } - - #[test] - fn install_should_install_simple_text_in_interface() { - let boot_services = create_fake_static_boot_service(); - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, protocol, _, _| { - assert_eq!(unsafe { protocol.read() }, protocols::simple_text_input::PROTOCOL_GUID); - efi::Status::SUCCESS - }); - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - - SimpleTextInFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); - } - - #[test] - fn uninstall_should_uninstall_simple_text_in_interface() { - static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); - let boot_services = create_fake_static_boot_service(); - - // used in install - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); - efi::Status::SUCCESS - }); - - // used in uninstall - boot_services.expect_open_protocol().returning(|_, protocol, interface, _, _, _| { - unsafe { - assert_eq!(protocol.read(), protocols::simple_text_input::PROTOCOL_GUID); - interface.write(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst)); - } - efi::Status::SUCCESS - }); - boot_services.expect_uninstall_protocol_interface().returning(|_, protocol, interface| { - unsafe { - assert_eq!(protocol.read(), protocols::simple_text_input::PROTOCOL_GUID); - assert_eq!(interface, CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst)); - } - efi::Status::SUCCESS - }); - boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - - SimpleTextInFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); - assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); - - SimpleTextInFfi::uninstall(boot_services, 1 as efi::Handle, 2 as efi::Handle).unwrap(); - } - - #[test] - fn reset_should_invoke_reset() { - static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); - let boot_services = create_fake_static_boot_service(); - - // used in install - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); - efi::Status::SUCCESS - }); - - // used in reset - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - - extern "efiapi" fn mock_set_report( - _this: *const hid_io::protocol::Protocol, - _report_id: u8, - _report_type: hid_io::protocol::HidReportType, - _report_buffer_size: usize, - _report_buffer: *mut c_void, - ) -> efi::Status { - efi::Status::SUCCESS + use core::{ + ffi::c_void, + mem::MaybeUninit, + sync::atomic::{AtomicBool, AtomicPtr}, + }; + + use crate::{ + boot_services::MockUefiBootServices, + hid_io::{HidReportReceiver, MockHidIo}, + keyboard::KeyboardHidHandler, + }; + use r_efi::{efi, protocols}; + + use super::SimpleTextInFfi; + + static BOOT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x06, // USAGE (Keyboard) + 0xa1, 0x01, // COLLECTION (Application) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x08, // REPORT_COUNT (8) + 0x05, 0x07, // USAGE_PAGE (Key Codes) + 0x19, 0xE0, // USAGE_MINIMUM (224) + 0x29, 0xE7, // USAGE_MAXIMUM (231) + 0x15, 0x00, // LOGICAL_MAXIMUM (0) + 0x25, 0x01, // LOGICAL_MINIMUM (1) + 0x81, 0x02, // INPUT (Data, Var, Abs) (Modifier Byte) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x08, // REPORT_SIZE (8) + 0x81, 0x03, // INPUT (Const) (Reserved Byte) + 0x95, 0x05, // REPORT_COUNT (5) + 0x75, 0x01, // REPORT_SIZE (1) + 0x05, 0x08, // USAGE_PAGE (LEDs) + 0x19, 0x01, // USAGE_MINIMUM (1) + 0x29, 0x05, // USAGE_MAXIMUM (5) + 0x91, 0x02, // OUTPUT (Data, Var, Abs) (LED report) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x03, // REPORT_SIZE (3) + 0x91, 0x02, // OUTPUT (Constant) (LED report padding) + 0x95, 0x06, // REPORT_COUNT (6) + 0x75, 0x08, // REPORT_SIZE (8) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26, 0xff, 00, // LOGICAL_MAXIMUM (255) + 0x05, 0x07, // USAGE_PAGE (Key Codes) + 0x19, 0x00, // USAGE_MINIMUM (0) + 0x2a, 0xff, 00, // USAGE_MAXIMUM (255) + 0x81, 0x00, // INPUT (Data, Array) + 0xc0, // END_COLLECTION + ]; + + // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used + // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock + // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. + // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. + // This object needs to outlive anything that uses it - once created, it will live until the end of the program. + fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { + unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } + } + + #[test] + fn install_should_install_simple_text_in_interface() { + let boot_services = create_fake_static_boot_service(); + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, protocol, _, _| { + assert_eq!(unsafe { protocol.read() }, protocols::simple_text_input::PROTOCOL_GUID); + efi::Status::SUCCESS + }); + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + + SimpleTextInFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); } - // used in reset and uninstall - boot_services.expect_open_protocol().returning(|_, protocol, interface, _, _, attributes| { - unsafe { - match protocol.read() { - hid_io::protocol::GUID => { - assert_eq!(attributes, efi::OPEN_PROTOCOL_GET_PROTOCOL); - let hid_io = MaybeUninit::::zeroed(); - let mut hid_io = hid_io.assume_init(); - hid_io.set_report = mock_set_report; - // note: this will leak a hid_io instance - interface.write(Box::into_raw(Box::new(hid_io)) as *mut c_void); - } - unrecognized_guid => panic!("Unexpected protocol: {:?}", unrecognized_guid), + #[test] + fn uninstall_should_uninstall_simple_text_in_interface() { + static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); + let boot_services = create_fake_static_boot_service(); + + // used in install + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); + efi::Status::SUCCESS + }); + + // used in uninstall + boot_services.expect_open_protocol().returning(|_, protocol, interface, _, _, _| { + unsafe { + assert_eq!(protocol.read(), protocols::simple_text_input::PROTOCOL_GUID); + interface.write(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst)); + } + efi::Status::SUCCESS + }); + boot_services.expect_uninstall_protocol_interface().returning(|_, protocol, interface| { + unsafe { + assert_eq!(protocol.read(), protocols::simple_text_input::PROTOCOL_GUID); + assert_eq!(interface, CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst)); + } + efi::Status::SUCCESS + }); + boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + + SimpleTextInFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); + assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); + + SimpleTextInFfi::uninstall(boot_services, 1 as efi::Handle, 2 as efi::Handle).unwrap(); + } + + #[test] + fn reset_should_invoke_reset() { + static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); + let boot_services = create_fake_static_boot_service(); + + // used in install + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); + efi::Status::SUCCESS + }); + + // used in reset + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + + extern "efiapi" fn mock_set_report( + _this: *const hid_io::protocol::Protocol, + _report_id: u8, + _report_type: hid_io::protocol::HidReportType, + _report_buffer_size: usize, + _report_buffer: *mut c_void, + ) -> efi::Status { + efi::Status::SUCCESS } - } - efi::Status::SUCCESS - }); - - // used in uninstall. - boot_services.expect_uninstall_protocol_interface().returning(|_, protocol, interface| { - unsafe { - assert_eq!(protocol.read(), protocols::simple_text_input::PROTOCOL_GUID); - assert_eq!(interface, CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst)); - } - efi::Status::SUCCESS - }); - boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - keyboard_handler.set_controller(Some(2 as efi::Handle)); - - SimpleTextInFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); - assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); - - let this = CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst) as *mut protocols::simple_text_input::Protocol; - SimpleTextInFfi::simple_text_in_reset(this, efi::Boolean::FALSE); - - // avoid keyboard drop uninstall flows - keyboard_handler.set_controller(None); - } - - #[test] - fn read_key_stroke_should_read_keystrokes() { - static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); - let boot_services = create_fake_static_boot_service(); - - // used in install - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); - efi::Status::SUCCESS - }); - - // used in reset - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - - // used in keyboard init and uninstall - boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); - boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); - boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); - - keyboard_handler.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); - keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); - - SimpleTextInFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); - assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); - - let report: &[u8] = &[0x00, 0x00, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - //release keys and push ctrl - let report: &[u8] = &[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - //read with simple_text_in - let this = CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst) as *mut protocols::simple_text_input::Protocol; - let mut input_key: protocols::simple_text_input::InputKey = Default::default(); - - let status = SimpleTextInFfi::simple_text_in_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input::InputKey, - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(input_key.unicode_char, 'c' as u16); - assert_eq!(input_key.scan_code, 0); - - let status = SimpleTextInFfi::simple_text_in_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input::InputKey, - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(input_key.unicode_char, 'b' as u16); - assert_eq!(input_key.scan_code, 0); - - let status = SimpleTextInFfi::simple_text_in_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input::InputKey, - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(input_key.unicode_char, 'a' as u16); - assert_eq!(input_key.scan_code, 0); - - let status = SimpleTextInFfi::simple_text_in_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input::InputKey, - ); - assert_eq!(status, efi::Status::NOT_READY); - - //send ctrl-a - expect it to be switched to control-character 0x01 - let report: &[u8] = &[0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - //release keys - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - let status = SimpleTextInFfi::simple_text_in_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input::InputKey, - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(input_key.unicode_char, 0x1); - assert_eq!(input_key.scan_code, 0); - - //send ctrl-shift-Z - expect it to be switched to control-character 0x1A - let report: &[u8] = &[0x03, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - //release keys - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - let status = SimpleTextInFfi::simple_text_in_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input::InputKey, - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(input_key.unicode_char, 0x1a); - assert_eq!(input_key.scan_code, 0); - - keyboard_handler.set_key_toggle_state(protocols::simple_text_input_ex::KEY_STATE_EXPOSED); - - // press the right logo key - let report: &[u8] = &[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - //release keys - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - // press the a key - let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - //release keys - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - // should get only the 'a', partial keystroke should be dropped. - let status = SimpleTextInFfi::simple_text_in_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input::InputKey, - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(input_key.unicode_char, 'a' as u16); - assert_eq!(input_key.scan_code, 0); - - let status = SimpleTextInFfi::simple_text_in_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input::InputKey, - ); - assert_eq!(status, efi::Status::NOT_READY); - } - - #[test] - fn wait_for_key_should_wait_for_key() { - static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); - static RECEIVED_EVENT: AtomicBool = AtomicBool::new(false); - - let boot_services = create_fake_static_boot_service(); - - // used in install - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); - boot_services.expect_install_protocol_interface().returning(|_, protocol, _, interface| { - if unsafe { *protocol } == protocols::simple_text_input::PROTOCOL_GUID { - CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); - } - efi::Status::SUCCESS - }); - boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - - boot_services.expect_signal_event().returning(|_| { - RECEIVED_EVENT.store(true, core::sync::atomic::Ordering::SeqCst); - efi::Status::SUCCESS - }); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); - - keyboard_handler.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); - keyboard_handler.set_controller(Some(2 as efi::Handle)); - keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); - - RECEIVED_EVENT.store(false, core::sync::atomic::Ordering::SeqCst); - SimpleTextInFfi::simple_text_in_wait_for_key( - 3 as efi::Event, - CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), - ); - assert!(!RECEIVED_EVENT.load(core::sync::atomic::Ordering::SeqCst)); - - // press the a key - let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - //release keys - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - SimpleTextInFfi::simple_text_in_wait_for_key( - 3 as efi::Event, - CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), - ); - assert!(RECEIVED_EVENT.load(core::sync::atomic::Ordering::SeqCst)); - - // avoid keyboard drop uninstall flows - keyboard_handler.set_controller(None); - } + + // used in reset and uninstall + boot_services.expect_open_protocol().returning(|_, protocol, interface, _, _, attributes| { + unsafe { + match protocol.read() { + hid_io::protocol::GUID => { + assert_eq!(attributes, efi::OPEN_PROTOCOL_GET_PROTOCOL); + let hid_io = MaybeUninit::::zeroed(); + let mut hid_io = hid_io.assume_init(); + hid_io.set_report = mock_set_report; + // note: this will leak a hid_io instance + interface.write(Box::into_raw(Box::new(hid_io)) as *mut c_void); + } + unrecognized_guid => panic!("Unexpected protocol: {:?}", unrecognized_guid), + } + } + efi::Status::SUCCESS + }); + + // used in uninstall. + boot_services.expect_uninstall_protocol_interface().returning(|_, protocol, interface| { + unsafe { + assert_eq!(protocol.read(), protocols::simple_text_input::PROTOCOL_GUID); + assert_eq!(interface, CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst)); + } + efi::Status::SUCCESS + }); + boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + keyboard_handler.set_controller(Some(2 as efi::Handle)); + + SimpleTextInFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); + assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); + + let this = + CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst) as *mut protocols::simple_text_input::Protocol; + SimpleTextInFfi::simple_text_in_reset(this, efi::Boolean::FALSE); + + // avoid keyboard drop uninstall flows + keyboard_handler.set_controller(None); + } + + #[test] + fn read_key_stroke_should_read_keystrokes() { + static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); + let boot_services = create_fake_static_boot_service(); + + // used in install + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); + efi::Status::SUCCESS + }); + + // used in reset + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + + // used in keyboard init and uninstall + boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); + boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); + boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); + + keyboard_handler.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); + keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); + + SimpleTextInFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); + assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); + + let report: &[u8] = &[0x00, 0x00, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + //release keys and push ctrl + let report: &[u8] = &[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + //read with simple_text_in + let this = + CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst) as *mut protocols::simple_text_input::Protocol; + let mut input_key: protocols::simple_text_input::InputKey = Default::default(); + + let status = SimpleTextInFfi::simple_text_in_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input::InputKey, + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(input_key.unicode_char, 'c' as u16); + assert_eq!(input_key.scan_code, 0); + + let status = SimpleTextInFfi::simple_text_in_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input::InputKey, + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(input_key.unicode_char, 'b' as u16); + assert_eq!(input_key.scan_code, 0); + + let status = SimpleTextInFfi::simple_text_in_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input::InputKey, + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(input_key.unicode_char, 'a' as u16); + assert_eq!(input_key.scan_code, 0); + + let status = SimpleTextInFfi::simple_text_in_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input::InputKey, + ); + assert_eq!(status, efi::Status::NOT_READY); + + //send ctrl-a - expect it to be switched to control-character 0x01 + let report: &[u8] = &[0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + //release keys + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + let status = SimpleTextInFfi::simple_text_in_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input::InputKey, + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(input_key.unicode_char, 0x1); + assert_eq!(input_key.scan_code, 0); + + //send ctrl-shift-Z - expect it to be switched to control-character 0x1A + let report: &[u8] = &[0x03, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + //release keys + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + let status = SimpleTextInFfi::simple_text_in_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input::InputKey, + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(input_key.unicode_char, 0x1a); + assert_eq!(input_key.scan_code, 0); + + keyboard_handler.set_key_toggle_state(protocols::simple_text_input_ex::KEY_STATE_EXPOSED); + + // press the right logo key + let report: &[u8] = &[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + //release keys + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + // press the a key + let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + //release keys + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + // should get only the 'a', partial keystroke should be dropped. + let status = SimpleTextInFfi::simple_text_in_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input::InputKey, + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(input_key.unicode_char, 'a' as u16); + assert_eq!(input_key.scan_code, 0); + + let status = SimpleTextInFfi::simple_text_in_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input::InputKey, + ); + assert_eq!(status, efi::Status::NOT_READY); + } + + #[test] + fn wait_for_key_should_wait_for_key() { + static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); + static RECEIVED_EVENT: AtomicBool = AtomicBool::new(false); + + let boot_services = create_fake_static_boot_service(); + + // used in install + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); + boot_services.expect_install_protocol_interface().returning(|_, protocol, _, interface| { + if unsafe { *protocol } == protocols::simple_text_input::PROTOCOL_GUID { + CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); + } + efi::Status::SUCCESS + }); + boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + + boot_services.expect_signal_event().returning(|_| { + RECEIVED_EVENT.store(true, core::sync::atomic::Ordering::SeqCst); + efi::Status::SUCCESS + }); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); + + keyboard_handler.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); + keyboard_handler.set_controller(Some(2 as efi::Handle)); + keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); + + RECEIVED_EVENT.store(false, core::sync::atomic::Ordering::SeqCst); + SimpleTextInFfi::simple_text_in_wait_for_key( + 3 as efi::Event, + CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), + ); + assert!(!RECEIVED_EVENT.load(core::sync::atomic::Ordering::SeqCst)); + + // press the a key + let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + //release keys + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + SimpleTextInFfi::simple_text_in_wait_for_key( + 3 as efi::Event, + CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), + ); + assert!(RECEIVED_EVENT.load(core::sync::atomic::Ordering::SeqCst)); + + // avoid keyboard drop uninstall flows + keyboard_handler.set_controller(None); + } } diff --git a/HidPkg/UefiHidDxeV2/src/keyboard/simple_text_in_ex.rs b/HidPkg/UefiHidDxeV2/src/keyboard/simple_text_in_ex.rs index 6313bf4996..ea71486418 100644 --- a/HidPkg/UefiHidDxeV2/src/keyboard/simple_text_in_ex.rs +++ b/HidPkg/UefiHidDxeV2/src/keyboard/simple_text_in_ex.rs @@ -14,9 +14,9 @@ use r_efi::{efi, protocols}; use rust_advanced_logger_dxe::{debugln, DEBUG_ERROR}; use crate::{ - boot_services::UefiBootServices, - hid_io::{HidIoFactory, UefiHidIoFactory}, - keyboard::KeyboardHidHandler, + boot_services::UefiBootServices, + hid_io::{HidIoFactory, UefiHidIoFactory}, + keyboard::KeyboardHidHandler, }; /// FFI context @@ -33,987 +33,997 @@ use crate::{ /// simple_text_in_ex protocol structure. #[repr(C)] pub struct SimpleTextInExFfi { - simple_text_in_ex: protocols::simple_text_input_ex::Protocol, - boot_services: &'static dyn UefiBootServices, - key_notify_event: efi::Event, - keyboard_handler: *mut KeyboardHidHandler, + simple_text_in_ex: protocols::simple_text_input_ex::Protocol, + boot_services: &'static dyn UefiBootServices, + key_notify_event: efi::Event, + keyboard_handler: *mut KeyboardHidHandler, } impl SimpleTextInExFfi { - /// Installs the simple text in ex protocol - pub fn install( - boot_services: &'static dyn UefiBootServices, - controller: efi::Handle, - keyboard_handler: &mut KeyboardHidHandler, - ) -> Result<(), efi::Status> { - //Create simple_text_in context - let simple_text_in_ex_ctx = SimpleTextInExFfi { - simple_text_in_ex: protocols::simple_text_input_ex::Protocol { - reset: Self::simple_text_in_ex_reset, - read_key_stroke_ex: Self::simple_text_in_ex_read_key_stroke, - wait_for_key_ex: core::ptr::null_mut(), - set_state: Self::simple_text_in_ex_set_state, - register_key_notify: Self::simple_text_in_ex_register_key_notify, - unregister_key_notify: Self::simple_text_in_ex_unregister_key_notify, - }, - boot_services, - key_notify_event: core::ptr::null_mut(), - keyboard_handler: keyboard_handler as *mut KeyboardHidHandler, - }; + /// Installs the simple text in ex protocol + pub fn install( + boot_services: &'static dyn UefiBootServices, + controller: efi::Handle, + keyboard_handler: &mut KeyboardHidHandler, + ) -> Result<(), efi::Status> { + //Create simple_text_in context + let simple_text_in_ex_ctx = SimpleTextInExFfi { + simple_text_in_ex: protocols::simple_text_input_ex::Protocol { + reset: Self::simple_text_in_ex_reset, + read_key_stroke_ex: Self::simple_text_in_ex_read_key_stroke, + wait_for_key_ex: core::ptr::null_mut(), + set_state: Self::simple_text_in_ex_set_state, + register_key_notify: Self::simple_text_in_ex_register_key_notify, + unregister_key_notify: Self::simple_text_in_ex_unregister_key_notify, + }, + boot_services, + key_notify_event: core::ptr::null_mut(), + keyboard_handler: keyboard_handler as *mut KeyboardHidHandler, + }; + + let simple_text_in_ex_ptr = Box::into_raw(Box::new(simple_text_in_ex_ctx)); + + //create event for wait_for_key + let mut wait_for_key_event: efi::Event = core::ptr::null_mut(); + let status = boot_services.create_event( + efi::EVT_NOTIFY_WAIT, + efi::TPL_NOTIFY, + Some(Self::simple_text_in_ex_wait_for_key), + simple_text_in_ex_ptr as *mut c_void, + core::ptr::addr_of_mut!(wait_for_key_event), + ); + if status.is_error() { + drop(unsafe { Box::from_raw(simple_text_in_ex_ptr) }); + return Err(status); + } - let simple_text_in_ex_ptr = Box::into_raw(Box::new(simple_text_in_ex_ctx)); - - //create event for wait_for_key - let mut wait_for_key_event: efi::Event = core::ptr::null_mut(); - let status = boot_services.create_event( - efi::EVT_NOTIFY_WAIT, - efi::TPL_NOTIFY, - Some(Self::simple_text_in_ex_wait_for_key), - simple_text_in_ex_ptr as *mut c_void, - core::ptr::addr_of_mut!(wait_for_key_event), - ); - if status.is_error() { - drop(unsafe { Box::from_raw(simple_text_in_ex_ptr) }); - return Err(status); - } + unsafe { (*simple_text_in_ex_ptr).simple_text_in_ex.wait_for_key_ex = wait_for_key_event }; + + //Key notifies are required to dispatch at TPL_CALLBACK per UEFI spec 2.10 section 12.2.5. The keyboard handler + //interfaces run at TPL_NOTIFY and issue a boot_services.signal_event() on this event to pend key notifies to be + //serviced at TPL_CALLBACK. + let mut key_notify_event: efi::Event = core::ptr::null_mut(); + let status = boot_services.create_event( + efi::EVT_NOTIFY_SIGNAL, + efi::TPL_CALLBACK, + Some(Self::process_key_notifies), + simple_text_in_ex_ptr as *mut c_void, + core::ptr::addr_of_mut!(key_notify_event), + ); + if status.is_error() { + let _ = boot_services.close_event(wait_for_key_event); + drop(unsafe { Box::from_raw(simple_text_in_ex_ptr) }); + } - unsafe { (*simple_text_in_ex_ptr).simple_text_in_ex.wait_for_key_ex = wait_for_key_event }; - - //Key notifies are required to dispatch at TPL_CALLBACK per UEFI spec 2.10 section 12.2.5. The keyboard handler - //interfaces run at TPL_NOTIFY and issue a boot_services.signal_event() on this event to pend key notifies to be - //serviced at TPL_CALLBACK. - let mut key_notify_event: efi::Event = core::ptr::null_mut(); - let status = boot_services.create_event( - efi::EVT_NOTIFY_SIGNAL, - efi::TPL_CALLBACK, - Some(Self::process_key_notifies), - simple_text_in_ex_ptr as *mut c_void, - core::ptr::addr_of_mut!(key_notify_event), - ); - if status.is_error() { - let _ = boot_services.close_event(wait_for_key_event); - drop(unsafe { Box::from_raw(simple_text_in_ex_ptr) }); - } + unsafe { (*simple_text_in_ex_ptr).key_notify_event = key_notify_event }; + + //install the simple_text_in_ex protocol + let mut controller = controller; + let status = boot_services.install_protocol_interface( + core::ptr::addr_of_mut!(controller), + &protocols::simple_text_input_ex::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + efi::NATIVE_INTERFACE, + simple_text_in_ex_ptr as *mut c_void, + ); + + if status.is_error() { + let _ = boot_services.close_event(wait_for_key_event); + let _ = boot_services.close_event(key_notify_event); + drop(unsafe { Box::from_raw(simple_text_in_ex_ptr) }); + return Err(status); + } - unsafe { (*simple_text_in_ex_ptr).key_notify_event = key_notify_event }; - - //install the simple_text_in_ex protocol - let mut controller = controller; - let status = boot_services.install_protocol_interface( - core::ptr::addr_of_mut!(controller), - &protocols::simple_text_input_ex::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - efi::NATIVE_INTERFACE, - simple_text_in_ex_ptr as *mut c_void, - ); - - if status.is_error() { - let _ = boot_services.close_event(wait_for_key_event); - let _ = boot_services.close_event(key_notify_event); - drop(unsafe { Box::from_raw(simple_text_in_ex_ptr) }); - return Err(status); + Ok(()) } - Ok(()) - } + /// Uninstalls the simple text in ex protocol + pub fn uninstall( + boot_services: &'static dyn UefiBootServices, + agent: efi::Handle, + controller: efi::Handle, + ) -> Result<(), efi::Status> { + //Controller is set - that means initialize() was called, and there is potential state exposed thru FFI that needs + //to be cleaned up. + let mut simple_text_in_ex_ptr: *mut SimpleTextInExFfi = core::ptr::null_mut(); + let status = boot_services.open_protocol( + controller, + &protocols::simple_text_input_ex::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::addr_of_mut!(simple_text_in_ex_ptr) as *mut *mut c_void, + agent, + controller, + efi::OPEN_PROTOCOL_GET_PROTOCOL, + ); + if status.is_error() { + //No protocol is actually installed on this controller, so nothing to clean up. + return Ok(()); + } - /// Uninstalls the simple text in ex protocol - pub fn uninstall( - boot_services: &'static dyn UefiBootServices, - agent: efi::Handle, - controller: efi::Handle, - ) -> Result<(), efi::Status> { - //Controller is set - that means initialize() was called, and there is potential state exposed thru FFI that needs - //to be cleaned up. - let mut simple_text_in_ex_ptr: *mut SimpleTextInExFfi = core::ptr::null_mut(); - let status = boot_services.open_protocol( - controller, - &protocols::simple_text_input_ex::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::addr_of_mut!(simple_text_in_ex_ptr) as *mut *mut c_void, - agent, - controller, - efi::OPEN_PROTOCOL_GET_PROTOCOL, - ); - if status.is_error() { - //No protocol is actually installed on this controller, so nothing to clean up. - return Ok(()); - } + //Attempt to uninstall the simple_text_in interface - this should disconnect any drivers using it and release + //the interface. + let status = boot_services.uninstall_protocol_interface( + controller, + &protocols::simple_text_input_ex::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + simple_text_in_ex_ptr as *mut c_void, + ); + if status.is_error() { + //An error here means some other driver might be holding on to the simple_text_in_ptr. + //Mark the instance invalid by setting the keyboard_handler raw pointer to null, but leak the PointerContext + //instance. Leaking context allows calls through the pointers on absolute_pointer_ptr to continue to resolve + //and return error based on observing keyboard_handler is null. + debugln!(DEBUG_ERROR, "Failed to uninstall simple_text_in interface, status: {:x?}", status); + + unsafe { + (*simple_text_in_ex_ptr).keyboard_handler = core::ptr::null_mut(); + } + //return without tearing down the context. + return Err(status); + } - //Attempt to uninstall the simple_text_in interface - this should disconnect any drivers using it and release - //the interface. - let status = boot_services.uninstall_protocol_interface( - controller, - &protocols::simple_text_input_ex::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - simple_text_in_ex_ptr as *mut c_void, - ); - if status.is_error() { - //An error here means some other driver might be holding on to the simple_text_in_ptr. - //Mark the instance invalid by setting the keyboard_handler raw pointer to null, but leak the PointerContext - //instance. Leaking context allows calls through the pointers on absolute_pointer_ptr to continue to resolve - //and return error based on observing keyboard_handler is null. - debugln!(DEBUG_ERROR, "Failed to uninstall simple_text_in interface, status: {:x?}", status); - - unsafe { - (*simple_text_in_ex_ptr).keyboard_handler = core::ptr::null_mut(); - } - //return without tearing down the context. - return Err(status); - } + let wait_for_key_event: efi::Handle = unsafe { (*simple_text_in_ex_ptr).simple_text_in_ex.wait_for_key_ex }; + let status = boot_services.close_event(wait_for_key_event); + if status.is_error() { + //An error here means the event was not closed, so in theory the notification_callback on it could still be + //fired. + //Mark the instance invalid by setting the keyboard_handler raw pointer to null, but leak the PointerContext + //instance. Leaking context allows calls through the pointers on simple_text_in_ptr to continue to resolve + //and return error based on observing keyboard_handler is null. + debugln!(DEBUG_ERROR, "Failed to close simple_text_in_ptr.wait_for_key event, status: {:x?}", status); + unsafe { + (*simple_text_in_ex_ptr).keyboard_handler = core::ptr::null_mut(); + } + return Err(status); + } - let wait_for_key_event: efi::Handle = unsafe { (*simple_text_in_ex_ptr).simple_text_in_ex.wait_for_key_ex }; - let status = boot_services.close_event(wait_for_key_event); - if status.is_error() { - //An error here means the event was not closed, so in theory the notification_callback on it could still be - //fired. - //Mark the instance invalid by setting the keyboard_handler raw pointer to null, but leak the PointerContext - //instance. Leaking context allows calls through the pointers on simple_text_in_ptr to continue to resolve - //and return error based on observing keyboard_handler is null. - debugln!(DEBUG_ERROR, "Failed to close simple_text_in_ptr.wait_for_key event, status: {:x?}", status); - unsafe { - (*simple_text_in_ex_ptr).keyboard_handler = core::ptr::null_mut(); - } - return Err(status); - } + let key_notify_event: efi::Handle = unsafe { (*simple_text_in_ex_ptr).key_notify_event }; + let status = boot_services.close_event(key_notify_event); + if status.is_error() { + //An error here means the event was not closed, so in theory the notification_callback on it could still be + //fired. + //Mark the instance invalid by setting the keyboard_handler raw pointer to null, but leak the PointerContext + //instance. Leaking context allows calls through the pointers on simple_text_in_ptr to continue to resolve + //and return error based on observing keyboard_handler is null. + debugln!(DEBUG_ERROR, "Failed to close key_notify_event event, status: {:x?}", status); + unsafe { + (*simple_text_in_ex_ptr).keyboard_handler = core::ptr::null_mut(); + } + return Err(status); + } - let key_notify_event: efi::Handle = unsafe { (*simple_text_in_ex_ptr).key_notify_event }; - let status = boot_services.close_event(key_notify_event); - if status.is_error() { - //An error here means the event was not closed, so in theory the notification_callback on it could still be - //fired. - //Mark the instance invalid by setting the keyboard_handler raw pointer to null, but leak the PointerContext - //instance. Leaking context allows calls through the pointers on simple_text_in_ptr to continue to resolve - //and return error based on observing keyboard_handler is null. - debugln!(DEBUG_ERROR, "Failed to close key_notify_event event, status: {:x?}", status); - unsafe { - (*simple_text_in_ex_ptr).keyboard_handler = core::ptr::null_mut(); - } - return Err(status); + // None of the parts of simple_text_in_ptr simple_text_in_ptr are in use, so it is safe to reclaim it. + drop(unsafe { Box::from_raw(simple_text_in_ex_ptr) }); + Ok(()) } - // None of the parts of simple_text_in_ptr simple_text_in_ptr are in use, so it is safe to reclaim it. - drop(unsafe { Box::from_raw(simple_text_in_ex_ptr) }); - Ok(()) - } - - // resets the keyboard state - part of the simple_text_in_ex protocol interface. - extern "efiapi" fn simple_text_in_ex_reset( - this: *mut protocols::simple_text_input_ex::Protocol, - extended_verification: efi::Boolean, - ) -> efi::Status { - if this.is_null() { - return efi::Status::INVALID_PARAMETER; + // resets the keyboard state - part of the simple_text_in_ex protocol interface. + extern "efiapi" fn simple_text_in_ex_reset( + this: *mut protocols::simple_text_input_ex::Protocol, + extended_verification: efi::Boolean, + ) -> efi::Status { + if this.is_null() { + return efi::Status::INVALID_PARAMETER; + } + let context = unsafe { (this as *mut SimpleTextInExFfi).as_mut() }.expect("bad pointer"); + let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); + let mut status = efi::Status::DEVICE_ERROR; + '_reset_processing: { + let keyboard_handler = unsafe { context.keyboard_handler.as_mut() }; + if let Some(keyboard_handler) = keyboard_handler { + //reset requires an instance of hid_io to allow for LED updating, so use UefiHidIoFactory to build one. + if let Some(controller) = keyboard_handler.get_controller() { + let hid_io = UefiHidIoFactory::new(context.boot_services, keyboard_handler.get_agent()) + .new_hid_io(controller, false); + if let Ok(hid_io) = hid_io { + if let Err(err) = keyboard_handler.reset(hid_io.as_ref(), extended_verification.into()) { + status = err; + } else { + status = efi::Status::SUCCESS; + } + } + } + } + } + context.boot_services.restore_tpl(old_tpl); + status } - let context = unsafe { (this as *mut SimpleTextInExFfi).as_mut() }.expect("bad pointer"); - let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); - let mut status = efi::Status::DEVICE_ERROR; - '_reset_processing: { - let keyboard_handler = unsafe { context.keyboard_handler.as_mut() }; - if let Some(keyboard_handler) = keyboard_handler { - //reset requires an instance of hid_io to allow for LED updating, so use UefiHidIoFactory to build one. - if let Some(controller) = keyboard_handler.get_controller() { - let hid_io = - UefiHidIoFactory::new(context.boot_services, keyboard_handler.get_agent()).new_hid_io(controller, false); - if let Ok(hid_io) = hid_io { - if let Err(err) = keyboard_handler.reset(hid_io.as_ref(), extended_verification.into()) { - status = err; + + // reads a key stroke - part of the simple_text_in_ex protocol interface. + extern "efiapi" fn simple_text_in_ex_read_key_stroke( + this: *mut protocols::simple_text_input_ex::Protocol, + key_data: *mut protocols::simple_text_input_ex::KeyData, + ) -> efi::Status { + if this.is_null() || key_data.is_null() { + return efi::Status::INVALID_PARAMETER; + } + let context = unsafe { (this as *mut SimpleTextInExFfi).as_mut() }.expect("bad pointer"); + let mut status = efi::Status::SUCCESS; + let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); + 'read_key_stroke: { + let keyboard_handler = unsafe { context.keyboard_handler.as_mut() }; + if let Some(keyboard_handler) = keyboard_handler { + if let Some(key) = keyboard_handler.pop_key() { + unsafe { key_data.write(key) } + } else { + let key = protocols::simple_text_input_ex::KeyData { + key_state: keyboard_handler.get_key_state(), + ..Default::default() + }; + unsafe { key_data.write(key) }; + status = efi::Status::NOT_READY + } } else { - status = efi::Status::SUCCESS; + status = efi::Status::DEVICE_ERROR; + break 'read_key_stroke; } - } } - } - } - context.boot_services.restore_tpl(old_tpl); - status - } - - // reads a key stroke - part of the simple_text_in_ex protocol interface. - extern "efiapi" fn simple_text_in_ex_read_key_stroke( - this: *mut protocols::simple_text_input_ex::Protocol, - key_data: *mut protocols::simple_text_input_ex::KeyData, - ) -> efi::Status { - if this.is_null() || key_data.is_null() { - return efi::Status::INVALID_PARAMETER; + context.boot_services.restore_tpl(old_tpl); + status } - let context = unsafe { (this as *mut SimpleTextInExFfi).as_mut() }.expect("bad pointer"); - let mut status = efi::Status::SUCCESS; - let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); - 'read_key_stroke: { - let keyboard_handler = unsafe { context.keyboard_handler.as_mut() }; - if let Some(keyboard_handler) = keyboard_handler { - if let Some(key) = keyboard_handler.pop_key() { - unsafe { key_data.write(key) } - } else { - let key = protocols::simple_text_input_ex::KeyData { - key_state: keyboard_handler.get_key_state(), - ..Default::default() - }; - unsafe { key_data.write(key) }; - status = efi::Status::NOT_READY + + // sets the keyboard state - part of the simple_text_in_ex protocol interface. + extern "efiapi" fn simple_text_in_ex_set_state( + this: *mut protocols::simple_text_input_ex::Protocol, + key_toggle_state: *mut protocols::simple_text_input_ex::KeyToggleState, + ) -> efi::Status { + if this.is_null() || key_toggle_state.is_null() { + return efi::Status::INVALID_PARAMETER; } - } else { - status = efi::Status::DEVICE_ERROR; - break 'read_key_stroke; - } - } - context.boot_services.restore_tpl(old_tpl); - status - } - - // sets the keyboard state - part of the simple_text_in_ex protocol interface. - extern "efiapi" fn simple_text_in_ex_set_state( - this: *mut protocols::simple_text_input_ex::Protocol, - key_toggle_state: *mut protocols::simple_text_input_ex::KeyToggleState, - ) -> efi::Status { - if this.is_null() || key_toggle_state.is_null() { - return efi::Status::INVALID_PARAMETER; - } - let context = unsafe { (this as *mut SimpleTextInExFfi).as_mut() }.expect("bad pointer"); - let mut status = efi::Status::DEVICE_ERROR; - let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); - '_set_state_processing: { - if let Some(keyboard_handler) = unsafe { context.keyboard_handler.as_mut() } { - if let Some(controller) = keyboard_handler.get_controller() { - let hid_io = - UefiHidIoFactory::new(context.boot_services, keyboard_handler.get_agent()).new_hid_io(controller, false); - if let Ok(hid_io) = hid_io { - status = efi::Status::SUCCESS; - keyboard_handler.set_key_toggle_state(unsafe { key_toggle_state.read() }); - let result = keyboard_handler.update_leds(hid_io.as_ref()); - if let Err(result) = result { - status = result; + let context = unsafe { (this as *mut SimpleTextInExFfi).as_mut() }.expect("bad pointer"); + let mut status = efi::Status::DEVICE_ERROR; + let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); + '_set_state_processing: { + if let Some(keyboard_handler) = unsafe { context.keyboard_handler.as_mut() } { + if let Some(controller) = keyboard_handler.get_controller() { + let hid_io = UefiHidIoFactory::new(context.boot_services, keyboard_handler.get_agent()) + .new_hid_io(controller, false); + if let Ok(hid_io) = hid_io { + status = efi::Status::SUCCESS; + keyboard_handler.set_key_toggle_state(unsafe { key_toggle_state.read() }); + let result = keyboard_handler.update_leds(hid_io.as_ref()); + if let Err(result) = result { + status = result; + } + } + } } - } } - } - } - context.boot_services.restore_tpl(old_tpl); - status - } - - // registers a key notification callback function - part of the simple_text_in_ex protocol interface. - extern "efiapi" fn simple_text_in_ex_register_key_notify( - this: *mut protocols::simple_text_input_ex::Protocol, - key_data_ptr: *mut protocols::simple_text_input_ex::KeyData, - key_notification_function: protocols::simple_text_input_ex::KeyNotifyFunction, - notify_handle: *mut *mut c_void, - ) -> efi::Status { - if this.is_null() || key_data_ptr.is_null() || notify_handle.is_null() || key_notification_function as usize == 0 { - return efi::Status::INVALID_PARAMETER; + context.boot_services.restore_tpl(old_tpl); + status } - let context = unsafe { (this as *mut SimpleTextInExFfi).as_mut() }.expect("bad pointer"); - let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); - let status; - { - if let Some(keyboard_handler) = unsafe { context.keyboard_handler.as_mut() } { - let key_data = unsafe { key_data_ptr.read() }; - let handle = keyboard_handler.insert_key_notify_callback(key_data, key_notification_function); - unsafe { notify_handle.write(handle as *mut c_void) }; - status = efi::Status::SUCCESS; - } else { - status = efi::Status::DEVICE_ERROR; - } - } - context.boot_services.restore_tpl(old_tpl); - status - } - - // Unregisters a key notification callback function - part of the simple_text_in_ex protocol interface. - extern "efiapi" fn simple_text_in_ex_unregister_key_notify( - this: *mut protocols::simple_text_input_ex::Protocol, - notification_handle: *mut c_void, - ) -> efi::Status { - if this.is_null() { - return efi::Status::INVALID_PARAMETER; - } - let status; - let context = unsafe { (this as *mut SimpleTextInExFfi).as_mut() }.expect("bad pointer"); - let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); - if let Some(keyboard_handler) = unsafe { context.keyboard_handler.as_mut() } { - if let Err(err) = keyboard_handler.remove_key_notify_callback(notification_handle as usize) { - status = err; - } else { - status = efi::Status::SUCCESS; - } - } else { - status = efi::Status::DEVICE_ERROR; - } - context.boot_services.restore_tpl(old_tpl); - status - } - - // Event handler function for the wait_for_key_event - extern "efiapi" fn simple_text_in_ex_wait_for_key(event: efi::Event, context: *mut c_void) { - if context.is_null() { - debugln!(DEBUG_ERROR, "simple_text_in_ex_wait_for_key invoked with invalid context"); - return; - } - let context = unsafe { (context as *mut SimpleTextInExFfi).as_mut() }.expect("bad pointer"); - - let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); - { - if let Some(keyboard_handler) = unsafe { context.keyboard_handler.as_mut() } { - while let Some(key_data) = keyboard_handler.peek_key() { - if key_data.key.unicode_char == 0 && key_data.key.scan_code == 0 { - // consume (and ignore) the partial stroke. - let _ = keyboard_handler.pop_key(); - continue; - } else { - // valid keystroke - context.boot_services.signal_event(event); - break; - } + // registers a key notification callback function - part of the simple_text_in_ex protocol interface. + extern "efiapi" fn simple_text_in_ex_register_key_notify( + this: *mut protocols::simple_text_input_ex::Protocol, + key_data_ptr: *mut protocols::simple_text_input_ex::KeyData, + key_notification_function: protocols::simple_text_input_ex::KeyNotifyFunction, + notify_handle: *mut *mut c_void, + ) -> efi::Status { + if this.is_null() + || key_data_ptr.is_null() + || notify_handle.is_null() + || key_notification_function as usize == 0 + { + return efi::Status::INVALID_PARAMETER; } - } - } - context.boot_services.restore_tpl(old_tpl); - } - // Event callback function for handling registered key notifications. Iterates over the queue of keys to be notified, - // and invokes the registered callback function for each of those keys. - extern "efiapi" fn process_key_notifies(_event: efi::Event, context: *mut c_void) { - if let Some(context) = unsafe { (context as *mut SimpleTextInExFfi).as_mut() } { - loop { - let mut pending_key = None; - let mut pending_callbacks = Vec::new(); + let context = unsafe { (this as *mut SimpleTextInExFfi).as_mut() }.expect("bad pointer"); + let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); + let status; + { + if let Some(keyboard_handler) = unsafe { context.keyboard_handler.as_mut() } { + let key_data = unsafe { key_data_ptr.read() }; + let handle = keyboard_handler.insert_key_notify_callback(key_data, key_notification_function); + unsafe { notify_handle.write(handle as *mut c_void) }; + status = efi::Status::SUCCESS; + } else { + status = efi::Status::DEVICE_ERROR; + } + } + context.boot_services.restore_tpl(old_tpl); + status + } + // Unregisters a key notification callback function - part of the simple_text_in_ex protocol interface. + extern "efiapi" fn simple_text_in_ex_unregister_key_notify( + this: *mut protocols::simple_text_input_ex::Protocol, + notification_handle: *mut c_void, + ) -> efi::Status { + if this.is_null() { + return efi::Status::INVALID_PARAMETER; + } + let status; + let context = unsafe { (this as *mut SimpleTextInExFfi).as_mut() }.expect("bad pointer"); let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); if let Some(keyboard_handler) = unsafe { context.keyboard_handler.as_mut() } { - (pending_key, pending_callbacks) = keyboard_handler.get_pending_callbacks(); + if let Err(err) = keyboard_handler.remove_key_notify_callback(notification_handle as usize) { + status = err; + } else { + status = efi::Status::SUCCESS; + } } else { - debugln!(DEBUG_ERROR, "process_key_notifies event called without a valid keyboard_handler"); + status = efi::Status::DEVICE_ERROR; } context.boot_services.restore_tpl(old_tpl); + status + } - //dispatch notifies (if any) at the TPL this event callback was invoked at. - if let Some(mut pending_key) = pending_key { - let key_ptr = &mut pending_key as *mut protocols::simple_text_input_ex::KeyData; - for callback in pending_callbacks { - let _ = callback(key_ptr); - } - } else { - // no pending notifies to process - break; + // Event handler function for the wait_for_key_event + extern "efiapi" fn simple_text_in_ex_wait_for_key(event: efi::Event, context: *mut c_void) { + if context.is_null() { + debugln!(DEBUG_ERROR, "simple_text_in_ex_wait_for_key invoked with invalid context"); + return; + } + let context = unsafe { (context as *mut SimpleTextInExFfi).as_mut() }.expect("bad pointer"); + + let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); + { + if let Some(keyboard_handler) = unsafe { context.keyboard_handler.as_mut() } { + while let Some(key_data) = keyboard_handler.peek_key() { + if key_data.key.unicode_char == 0 && key_data.key.scan_code == 0 { + // consume (and ignore) the partial stroke. + let _ = keyboard_handler.pop_key(); + continue; + } else { + // valid keystroke + context.boot_services.signal_event(event); + break; + } + } + } + } + context.boot_services.restore_tpl(old_tpl); + } + + // Event callback function for handling registered key notifications. Iterates over the queue of keys to be notified, + // and invokes the registered callback function for each of those keys. + extern "efiapi" fn process_key_notifies(_event: efi::Event, context: *mut c_void) { + if let Some(context) = unsafe { (context as *mut SimpleTextInExFfi).as_mut() } { + loop { + let mut pending_key = None; + let mut pending_callbacks = Vec::new(); + + let old_tpl = context.boot_services.raise_tpl(efi::TPL_NOTIFY); + if let Some(keyboard_handler) = unsafe { context.keyboard_handler.as_mut() } { + (pending_key, pending_callbacks) = keyboard_handler.get_pending_callbacks(); + } else { + debugln!(DEBUG_ERROR, "process_key_notifies event called without a valid keyboard_handler"); + } + context.boot_services.restore_tpl(old_tpl); + + //dispatch notifies (if any) at the TPL this event callback was invoked at. + if let Some(mut pending_key) = pending_key { + let key_ptr = &mut pending_key as *mut protocols::simple_text_input_ex::KeyData; + for callback in pending_callbacks { + let _ = callback(key_ptr); + } + } else { + // no pending notifies to process + break; + } + } } - } } - } } #[cfg(test)] mod test { - use core::{ - ffi::c_void, - mem::MaybeUninit, - sync::atomic::{AtomicBool, AtomicPtr}, - }; + use core::{ + ffi::c_void, + mem::MaybeUninit, + sync::atomic::{AtomicBool, AtomicPtr}, + }; - use r_efi::{efi, protocols}; + use r_efi::{efi, protocols}; - use crate::{ - boot_services::MockUefiBootServices, - hid_io::{HidReportReceiver, MockHidIo}, - keyboard::KeyboardHidHandler, - }; - - use super::SimpleTextInExFfi; - - static BOOT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[ - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x06, // USAGE (Keyboard) - 0xa1, 0x01, // COLLECTION (Application) - 0x75, 0x01, // REPORT_SIZE (1) - 0x95, 0x08, // REPORT_COUNT (8) - 0x05, 0x07, // USAGE_PAGE (Key Codes) - 0x19, 0xE0, // USAGE_MINIMUM (224) - 0x29, 0xE7, // USAGE_MAXIMUM (231) - 0x15, 0x00, // LOGICAL_MAXIMUM (0) - 0x25, 0x01, // LOGICAL_MINIMUM (1) - 0x81, 0x02, // INPUT (Data, Var, Abs) (Modifier Byte) - 0x95, 0x01, // REPORT_COUNT (1) - 0x75, 0x08, // REPORT_SIZE (8) - 0x81, 0x03, // INPUT (Const) (Reserved Byte) - 0x95, 0x05, // REPORT_COUNT (5) - 0x75, 0x01, // REPORT_SIZE (1) - 0x05, 0x08, // USAGE_PAGE (LEDs) - 0x19, 0x01, // USAGE_MINIMUM (1) - 0x29, 0x05, // USAGE_MAXIMUM (5) - 0x91, 0x02, // OUTPUT (Data, Var, Abs) (LED report) - 0x95, 0x01, // REPORT_COUNT (1) - 0x75, 0x03, // REPORT_SIZE (3) - 0x91, 0x02, // OUTPUT (Constant) (LED report padding) - 0x95, 0x06, // REPORT_COUNT (6) - 0x75, 0x08, // REPORT_SIZE (8) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0xff, 00, // LOGICAL_MAXIMUM (255) - 0x05, 0x07, // USAGE_PAGE (Key Codes) - 0x19, 0x00, // USAGE_MINIMUM (0) - 0x2a, 0xff, 00, // USAGE_MAXIMUM (255) - 0x81, 0x00, // INPUT (Data, Array) - 0xc0, // END_COLLECTION - ]; - - // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used - // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock - // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. - // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. - // This object needs to outlive anything that uses it - once created, it will live until the end of the program. - fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { - unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } - } - - #[test] - fn install_should_install_simple_text_in_ex_interface() { - let boot_services = create_fake_static_boot_service(); - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, protocol, _, _| { - assert_eq!(unsafe { protocol.read() }, protocols::simple_text_input_ex::PROTOCOL_GUID); - efi::Status::SUCCESS - }); - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - - SimpleTextInExFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); - } - - #[test] - fn uninstall_should_uninstall_simple_text_in_interface() { - static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); - let boot_services = create_fake_static_boot_service(); - - // used in install - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); - efi::Status::SUCCESS - }); - - // used in uninstall - boot_services.expect_open_protocol().returning(|_, protocol, interface, _, _, _| { - unsafe { - assert_eq!(protocol.read(), protocols::simple_text_input_ex::PROTOCOL_GUID); - interface.write(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst)); - } - efi::Status::SUCCESS - }); - boot_services.expect_uninstall_protocol_interface().returning(|_, protocol, interface| { - unsafe { - assert_eq!(protocol.read(), protocols::simple_text_input_ex::PROTOCOL_GUID); - assert_eq!(interface, CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst)); - } - efi::Status::SUCCESS - }); - boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - - SimpleTextInExFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); - assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); - - SimpleTextInExFfi::uninstall(boot_services, 1 as efi::Handle, 2 as efi::Handle).unwrap(); - } - - #[test] - fn reset_should_invoke_reset() { - static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); - let boot_services = create_fake_static_boot_service(); - - // used in install - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); - efi::Status::SUCCESS - }); - - // used in reset - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - - extern "efiapi" fn mock_set_report( - _this: *const hid_io::protocol::Protocol, - _report_id: u8, - _report_type: hid_io::protocol::HidReportType, - _report_buffer_size: usize, - _report_buffer: *mut c_void, - ) -> efi::Status { - efi::Status::SUCCESS + use crate::{ + boot_services::MockUefiBootServices, + hid_io::{HidReportReceiver, MockHidIo}, + keyboard::KeyboardHidHandler, + }; + + use super::SimpleTextInExFfi; + + static BOOT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x06, // USAGE (Keyboard) + 0xa1, 0x01, // COLLECTION (Application) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x08, // REPORT_COUNT (8) + 0x05, 0x07, // USAGE_PAGE (Key Codes) + 0x19, 0xE0, // USAGE_MINIMUM (224) + 0x29, 0xE7, // USAGE_MAXIMUM (231) + 0x15, 0x00, // LOGICAL_MAXIMUM (0) + 0x25, 0x01, // LOGICAL_MINIMUM (1) + 0x81, 0x02, // INPUT (Data, Var, Abs) (Modifier Byte) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x08, // REPORT_SIZE (8) + 0x81, 0x03, // INPUT (Const) (Reserved Byte) + 0x95, 0x05, // REPORT_COUNT (5) + 0x75, 0x01, // REPORT_SIZE (1) + 0x05, 0x08, // USAGE_PAGE (LEDs) + 0x19, 0x01, // USAGE_MINIMUM (1) + 0x29, 0x05, // USAGE_MAXIMUM (5) + 0x91, 0x02, // OUTPUT (Data, Var, Abs) (LED report) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x03, // REPORT_SIZE (3) + 0x91, 0x02, // OUTPUT (Constant) (LED report padding) + 0x95, 0x06, // REPORT_COUNT (6) + 0x75, 0x08, // REPORT_SIZE (8) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26, 0xff, 00, // LOGICAL_MAXIMUM (255) + 0x05, 0x07, // USAGE_PAGE (Key Codes) + 0x19, 0x00, // USAGE_MINIMUM (0) + 0x2a, 0xff, 00, // USAGE_MAXIMUM (255) + 0x81, 0x00, // INPUT (Data, Array) + 0xc0, // END_COLLECTION + ]; + + // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used + // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock + // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. + // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. + // This object needs to outlive anything that uses it - once created, it will live until the end of the program. + fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { + unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } } - // used in reset and uninstall - boot_services.expect_open_protocol().returning(|_, protocol, interface, _, _, attributes| { - unsafe { - match protocol.read() { - hid_io::protocol::GUID => { - assert_eq!(attributes, efi::OPEN_PROTOCOL_GET_PROTOCOL); - let hid_io = MaybeUninit::::zeroed(); - let mut hid_io = hid_io.assume_init(); - hid_io.set_report = mock_set_report; - // note: this will leak a hid_io instance - interface.write(Box::into_raw(Box::new(hid_io)) as *mut c_void); - } - unrecognized_guid => panic!("Unexpected protocol: {:?}", unrecognized_guid), + #[test] + fn install_should_install_simple_text_in_ex_interface() { + let boot_services = create_fake_static_boot_service(); + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, protocol, _, _| { + assert_eq!(unsafe { protocol.read() }, protocols::simple_text_input_ex::PROTOCOL_GUID); + efi::Status::SUCCESS + }); + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + + SimpleTextInExFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); + } + + #[test] + fn uninstall_should_uninstall_simple_text_in_interface() { + static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); + let boot_services = create_fake_static_boot_service(); + + // used in install + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); + efi::Status::SUCCESS + }); + + // used in uninstall + boot_services.expect_open_protocol().returning(|_, protocol, interface, _, _, _| { + unsafe { + assert_eq!(protocol.read(), protocols::simple_text_input_ex::PROTOCOL_GUID); + interface.write(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst)); + } + efi::Status::SUCCESS + }); + boot_services.expect_uninstall_protocol_interface().returning(|_, protocol, interface| { + unsafe { + assert_eq!(protocol.read(), protocols::simple_text_input_ex::PROTOCOL_GUID); + assert_eq!(interface, CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst)); + } + efi::Status::SUCCESS + }); + boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + + SimpleTextInExFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); + assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); + + SimpleTextInExFfi::uninstall(boot_services, 1 as efi::Handle, 2 as efi::Handle).unwrap(); + } + + #[test] + fn reset_should_invoke_reset() { + static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); + let boot_services = create_fake_static_boot_service(); + + // used in install + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); + efi::Status::SUCCESS + }); + + // used in reset + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + + extern "efiapi" fn mock_set_report( + _this: *const hid_io::protocol::Protocol, + _report_id: u8, + _report_type: hid_io::protocol::HidReportType, + _report_buffer_size: usize, + _report_buffer: *mut c_void, + ) -> efi::Status { + efi::Status::SUCCESS } - } - efi::Status::SUCCESS - }); - - // used in uninstall. - boot_services.expect_uninstall_protocol_interface().returning(|_, protocol, interface| { - unsafe { - assert_eq!(protocol.read(), protocols::simple_text_input_ex::PROTOCOL_GUID); - assert_eq!(interface, CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst)); - } - efi::Status::SUCCESS - }); - boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - keyboard_handler.set_controller(Some(2 as efi::Handle)); - - SimpleTextInExFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); - assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); - - let this = CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst) as *mut protocols::simple_text_input_ex::Protocol; - SimpleTextInExFfi::simple_text_in_ex_reset(this, efi::Boolean::FALSE); - - // avoid keyboard drop uninstall flows - keyboard_handler.set_controller(None); - } - - #[test] - fn read_key_stroke_should_read_keystrokes() { - static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); - let boot_services = create_fake_static_boot_service(); - - // used in install - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); - efi::Status::SUCCESS - }); - - // used in read key stroke - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - - // used in keyboard init and uninstall - boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); - boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); - boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); - - keyboard_handler.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); - keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); - - SimpleTextInExFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); - assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); - - let report: &[u8] = &[0x00, 0x00, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - //release keys and push ctrl - let report: &[u8] = &[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - //read with simple_text_in - let this = CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst) as *mut protocols::simple_text_input_ex::Protocol; - let mut input_key: protocols::simple_text_input_ex::KeyData = Default::default(); - - let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input_ex::KeyData, - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(input_key.key.unicode_char, 'c' as u16); - assert_eq!(input_key.key.scan_code, 0); - - let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input_ex::KeyData, - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(input_key.key.unicode_char, 'b' as u16); - assert_eq!(input_key.key.scan_code, 0); - - let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input_ex::KeyData, - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(input_key.key.unicode_char, 'a' as u16); - assert_eq!(input_key.key.scan_code, 0); - - let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input_ex::KeyData, - ); - assert_eq!(status, efi::Status::NOT_READY); - - //send ctrl-a - expect it not to be switched to control-character 0x01 - let report: &[u8] = &[0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - //release keys - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input_ex::KeyData, - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(input_key.key.unicode_char, 'a' as u16); - assert_eq!(input_key.key.scan_code, 0); - assert_eq!( - input_key.key_state.key_shift_state, - protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::LEFT_CONTROL_PRESSED - ); - - //send ctrl-shift-Z - expect it not to be switched to control-character 0x1A - let report: &[u8] = &[0x03, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - //release keys - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input_ex::KeyData, - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(input_key.key.unicode_char, 'Z' as u16); - assert_eq!(input_key.key.scan_code, 0); - assert_eq!( - input_key.key_state.key_shift_state, - protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::LEFT_CONTROL_PRESSED - ); - - keyboard_handler.set_key_toggle_state(protocols::simple_text_input_ex::KEY_STATE_EXPOSED); - - // press the right logo key - let report: &[u8] = &[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - //release keys - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - // press the a key - let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - //release keys - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - // should get partial keystroke. - let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input_ex::KeyData, - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(input_key.key.unicode_char, 0); - assert_eq!(input_key.key.scan_code, 0); - assert_eq!( - input_key.key_state.key_shift_state, - protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::RIGHT_LOGO_PRESSED - ); - - // followed by 'a' key. - let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( - this, - &mut input_key as *mut protocols::simple_text_input_ex::KeyData, - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(input_key.key.unicode_char, 'a' as u16); - assert_eq!(input_key.key.scan_code, 0); - assert_eq!(input_key.key_state.key_shift_state, protocols::simple_text_input_ex::SHIFT_STATE_VALID); - } - - #[test] - fn set_state_should_set_state() { - static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); - let boot_services = create_fake_static_boot_service(); - - // used in install - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); - efi::Status::SUCCESS - }); - boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); - - // used in set state - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - - // used in keyboard init and uninstall - boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); - extern "efiapi" fn mock_set_report( - _this: *const hid_io::protocol::Protocol, - _report_id: u8, - _report_type: hid_io::protocol::HidReportType, - _report_buffer_size: usize, - _report_buffer: *mut c_void, - ) -> efi::Status { - efi::Status::SUCCESS + + // used in reset and uninstall + boot_services.expect_open_protocol().returning(|_, protocol, interface, _, _, attributes| { + unsafe { + match protocol.read() { + hid_io::protocol::GUID => { + assert_eq!(attributes, efi::OPEN_PROTOCOL_GET_PROTOCOL); + let hid_io = MaybeUninit::::zeroed(); + let mut hid_io = hid_io.assume_init(); + hid_io.set_report = mock_set_report; + // note: this will leak a hid_io instance + interface.write(Box::into_raw(Box::new(hid_io)) as *mut c_void); + } + unrecognized_guid => panic!("Unexpected protocol: {:?}", unrecognized_guid), + } + } + efi::Status::SUCCESS + }); + + // used in uninstall. + boot_services.expect_uninstall_protocol_interface().returning(|_, protocol, interface| { + unsafe { + assert_eq!(protocol.read(), protocols::simple_text_input_ex::PROTOCOL_GUID); + assert_eq!(interface, CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst)); + } + efi::Status::SUCCESS + }); + boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + keyboard_handler.set_controller(Some(2 as efi::Handle)); + + SimpleTextInExFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); + assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); + + let this = + CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst) as *mut protocols::simple_text_input_ex::Protocol; + SimpleTextInExFfi::simple_text_in_ex_reset(this, efi::Boolean::FALSE); + + // avoid keyboard drop uninstall flows + keyboard_handler.set_controller(None); + } + + #[test] + fn read_key_stroke_should_read_keystrokes() { + static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); + let boot_services = create_fake_static_boot_service(); + + // used in install + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); + efi::Status::SUCCESS + }); + + // used in read key stroke + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + + // used in keyboard init and uninstall + boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); + boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); + boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); + + keyboard_handler.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); + keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); + + SimpleTextInExFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); + assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); + + let report: &[u8] = &[0x00, 0x00, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + //release keys and push ctrl + let report: &[u8] = &[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + //read with simple_text_in + let this = + CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst) as *mut protocols::simple_text_input_ex::Protocol; + let mut input_key: protocols::simple_text_input_ex::KeyData = Default::default(); + + let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input_ex::KeyData, + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(input_key.key.unicode_char, 'c' as u16); + assert_eq!(input_key.key.scan_code, 0); + + let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input_ex::KeyData, + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(input_key.key.unicode_char, 'b' as u16); + assert_eq!(input_key.key.scan_code, 0); + + let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input_ex::KeyData, + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(input_key.key.unicode_char, 'a' as u16); + assert_eq!(input_key.key.scan_code, 0); + + let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input_ex::KeyData, + ); + assert_eq!(status, efi::Status::NOT_READY); + + //send ctrl-a - expect it not to be switched to control-character 0x01 + let report: &[u8] = &[0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + //release keys + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input_ex::KeyData, + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(input_key.key.unicode_char, 'a' as u16); + assert_eq!(input_key.key.scan_code, 0); + assert_eq!( + input_key.key_state.key_shift_state, + protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::LEFT_CONTROL_PRESSED + ); + + //send ctrl-shift-Z - expect it not to be switched to control-character 0x1A + let report: &[u8] = &[0x03, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + //release keys + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input_ex::KeyData, + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(input_key.key.unicode_char, 'Z' as u16); + assert_eq!(input_key.key.scan_code, 0); + assert_eq!( + input_key.key_state.key_shift_state, + protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::LEFT_CONTROL_PRESSED + ); + + keyboard_handler.set_key_toggle_state(protocols::simple_text_input_ex::KEY_STATE_EXPOSED); + + // press the right logo key + let report: &[u8] = &[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + //release keys + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + // press the a key + let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + //release keys + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + // should get partial keystroke. + let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input_ex::KeyData, + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(input_key.key.unicode_char, 0); + assert_eq!(input_key.key.scan_code, 0); + assert_eq!( + input_key.key_state.key_shift_state, + protocols::simple_text_input_ex::SHIFT_STATE_VALID | protocols::simple_text_input_ex::RIGHT_LOGO_PRESSED + ); + + // followed by 'a' key. + let status = SimpleTextInExFfi::simple_text_in_ex_read_key_stroke( + this, + &mut input_key as *mut protocols::simple_text_input_ex::KeyData, + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(input_key.key.unicode_char, 'a' as u16); + assert_eq!(input_key.key.scan_code, 0); + assert_eq!(input_key.key_state.key_shift_state, protocols::simple_text_input_ex::SHIFT_STATE_VALID); } - // used in set_state and uninstall - boot_services.expect_open_protocol().returning(|_, protocol, interface, _, _, attributes| { - unsafe { - match protocol.read() { - hid_io::protocol::GUID => { - assert_eq!(attributes, efi::OPEN_PROTOCOL_GET_PROTOCOL); - let hid_io = MaybeUninit::::zeroed(); - let mut hid_io = hid_io.assume_init(); - hid_io.set_report = mock_set_report; - // note: this will leak a hid_io instance - interface.write(Box::into_raw(Box::new(hid_io)) as *mut c_void); - } - _unrecognized_guid => return efi::Status::NOT_FOUND, + #[test] + fn set_state_should_set_state() { + static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); + let boot_services = create_fake_static_boot_service(); + + // used in install + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); + efi::Status::SUCCESS + }); + boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); + + // used in set state + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + + // used in keyboard init and uninstall + boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_signal_event().returning(|_| efi::Status::SUCCESS); + extern "efiapi" fn mock_set_report( + _this: *const hid_io::protocol::Protocol, + _report_id: u8, + _report_type: hid_io::protocol::HidReportType, + _report_buffer_size: usize, + _report_buffer: *mut c_void, + ) -> efi::Status { + efi::Status::SUCCESS } - } - efi::Status::SUCCESS - }); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); - - keyboard_handler.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); - keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); - - SimpleTextInExFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); - assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); - - let this = CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst) as *mut protocols::simple_text_input_ex::Protocol; - let mut key_toggle_state: protocols::simple_text_input_ex::KeyToggleState = - protocols::simple_text_input_ex::KEY_STATE_EXPOSED; - - let status = SimpleTextInExFfi::simple_text_in_ex_set_state(this, core::ptr::addr_of_mut!(key_toggle_state)); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!( - keyboard_handler.get_key_state().key_toggle_state, - key_toggle_state | protocols::simple_text_input_ex::TOGGLE_STATE_VALID - ); - - key_toggle_state = - protocols::simple_text_input_ex::CAPS_LOCK_ACTIVE | protocols::simple_text_input_ex::NUM_LOCK_ACTIVE; - let status = SimpleTextInExFfi::simple_text_in_ex_set_state(this, core::ptr::addr_of_mut!(key_toggle_state)); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!( - keyboard_handler.get_key_state().key_toggle_state, - key_toggle_state | protocols::simple_text_input_ex::TOGGLE_STATE_VALID - ); - } - - #[test] - fn register_key_notify_should_register_key() { - const NOTIFY_EVENT: efi::Event = 1 as efi::Event; - static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); - static KEY_NOTIFIED: AtomicBool = AtomicBool::new(false); - static KEY2_NOTIFIED: AtomicBool = AtomicBool::new(false); - - let boot_services = create_fake_static_boot_service(); - - // used in install - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); - efi::Status::SUCCESS - }); - boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); - - // used in read key stroke - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - - // used in keyboard init and uninstall - boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_signal_event().returning(|event| { - if event == NOTIFY_EVENT { - SimpleTextInExFfi::process_key_notifies(event, CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst)); - } - efi::Status::SUCCESS - }); - boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); - - keyboard_handler.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); - keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); - keyboard_handler.set_notify_event(NOTIFY_EVENT); - - SimpleTextInExFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); - assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); - - extern "efiapi" fn key_notify_callback_a(key_data: *mut protocols::simple_text_input_ex::KeyData) -> efi::Status { - let key = unsafe { key_data.read() }; - assert_eq!(key.key.unicode_char, 'a' as u16); - KEY_NOTIFIED.store(true, core::sync::atomic::Ordering::SeqCst); - efi::Status::SUCCESS + + // used in set_state and uninstall + boot_services.expect_open_protocol().returning(|_, protocol, interface, _, _, attributes| { + unsafe { + match protocol.read() { + hid_io::protocol::GUID => { + assert_eq!(attributes, efi::OPEN_PROTOCOL_GET_PROTOCOL); + let hid_io = MaybeUninit::::zeroed(); + let mut hid_io = hid_io.assume_init(); + hid_io.set_report = mock_set_report; + // note: this will leak a hid_io instance + interface.write(Box::into_raw(Box::new(hid_io)) as *mut c_void); + } + _unrecognized_guid => return efi::Status::NOT_FOUND, + } + } + efi::Status::SUCCESS + }); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); + + keyboard_handler.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); + keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); + + SimpleTextInExFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); + assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); + + let this = + CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst) as *mut protocols::simple_text_input_ex::Protocol; + let mut key_toggle_state: protocols::simple_text_input_ex::KeyToggleState = + protocols::simple_text_input_ex::KEY_STATE_EXPOSED; + + let status = SimpleTextInExFfi::simple_text_in_ex_set_state(this, core::ptr::addr_of_mut!(key_toggle_state)); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!( + keyboard_handler.get_key_state().key_toggle_state, + key_toggle_state | protocols::simple_text_input_ex::TOGGLE_STATE_VALID + ); + + key_toggle_state = + protocols::simple_text_input_ex::CAPS_LOCK_ACTIVE | protocols::simple_text_input_ex::NUM_LOCK_ACTIVE; + let status = SimpleTextInExFfi::simple_text_in_ex_set_state(this, core::ptr::addr_of_mut!(key_toggle_state)); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!( + keyboard_handler.get_key_state().key_toggle_state, + key_toggle_state | protocols::simple_text_input_ex::TOGGLE_STATE_VALID + ); } - extern "efiapi" fn key_notify_callback_a_and_b( - key_data: *mut protocols::simple_text_input_ex::KeyData, - ) -> efi::Status { - let key = unsafe { key_data.read() }; - assert!((key.key.unicode_char == 'a' as u16) || (key.key.unicode_char == 'b' as u16)); - KEY2_NOTIFIED.store(true, core::sync::atomic::Ordering::SeqCst); - efi::Status::SUCCESS + #[test] + fn register_key_notify_should_register_key() { + const NOTIFY_EVENT: efi::Event = 1 as efi::Event; + static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); + static KEY_NOTIFIED: AtomicBool = AtomicBool::new(false); + static KEY2_NOTIFIED: AtomicBool = AtomicBool::new(false); + + let boot_services = create_fake_static_boot_service(); + + // used in install + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); + efi::Status::SUCCESS + }); + boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); + + // used in read key stroke + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + + // used in keyboard init and uninstall + boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_signal_event().returning(|event| { + if event == NOTIFY_EVENT { + SimpleTextInExFfi::process_key_notifies(event, CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst)); + } + efi::Status::SUCCESS + }); + boot_services.expect_open_protocol().returning(|_, _, _, _, _, _| efi::Status::NOT_FOUND); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); + + keyboard_handler.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); + keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); + keyboard_handler.set_notify_event(NOTIFY_EVENT); + + SimpleTextInExFfi::install(boot_services, 2 as efi::Handle, &mut keyboard_handler).unwrap(); + assert_ne!(CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), core::ptr::null_mut()); + + extern "efiapi" fn key_notify_callback_a( + key_data: *mut protocols::simple_text_input_ex::KeyData, + ) -> efi::Status { + let key = unsafe { key_data.read() }; + assert_eq!(key.key.unicode_char, 'a' as u16); + KEY_NOTIFIED.store(true, core::sync::atomic::Ordering::SeqCst); + efi::Status::SUCCESS + } + + extern "efiapi" fn key_notify_callback_a_and_b( + key_data: *mut protocols::simple_text_input_ex::KeyData, + ) -> efi::Status { + let key = unsafe { key_data.read() }; + assert!((key.key.unicode_char == 'a' as u16) || (key.key.unicode_char == 'b' as u16)); + KEY2_NOTIFIED.store(true, core::sync::atomic::Ordering::SeqCst); + efi::Status::SUCCESS + } + + let this = + CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst) as *mut protocols::simple_text_input_ex::Protocol; + + let mut key_data: protocols::simple_text_input_ex::KeyData = Default::default(); + key_data.key.unicode_char = 'a' as u16; + let mut notify_handle = core::ptr::null_mut(); + + let status = SimpleTextInExFfi::simple_text_in_ex_register_key_notify( + this, + core::ptr::addr_of_mut!(key_data), + key_notify_callback_a, + core::ptr::addr_of_mut!(notify_handle), + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(notify_handle as usize, 1); + + notify_handle = core::ptr::null_mut(); + let status = SimpleTextInExFfi::simple_text_in_ex_register_key_notify( + this, + core::ptr::addr_of_mut!(key_data), + key_notify_callback_a_and_b, + core::ptr::addr_of_mut!(notify_handle), + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(notify_handle as usize, 2); + + notify_handle = core::ptr::null_mut(); + key_data.key.unicode_char = 'b' as u16; + let status = SimpleTextInExFfi::simple_text_in_ex_register_key_notify( + this, + core::ptr::addr_of_mut!(key_data), + key_notify_callback_a_and_b, + core::ptr::addr_of_mut!(notify_handle), + ); + assert_eq!(status, efi::Status::SUCCESS); + assert_eq!(notify_handle as usize, 3); + + //send 'b' + let report: &[u8] = &[0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + //release + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + assert!(!KEY_NOTIFIED.load(core::sync::atomic::Ordering::SeqCst)); + assert!(KEY2_NOTIFIED.load(core::sync::atomic::Ordering::SeqCst)); + + KEY_NOTIFIED.store(false, core::sync::atomic::Ordering::SeqCst); + KEY2_NOTIFIED.store(false, core::sync::atomic::Ordering::SeqCst); + + //send 'a' + let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + //release + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + assert!(KEY_NOTIFIED.load(core::sync::atomic::Ordering::SeqCst)); + assert!(KEY2_NOTIFIED.load(core::sync::atomic::Ordering::SeqCst)); + + KEY_NOTIFIED.store(false, core::sync::atomic::Ordering::SeqCst); + KEY2_NOTIFIED.store(false, core::sync::atomic::Ordering::SeqCst); + + //remove the 'a'-only callback + let status = SimpleTextInExFfi::simple_text_in_ex_unregister_key_notify(this, 1 as *mut c_void); + assert_eq!(status, efi::Status::SUCCESS); + + //send 'a' + let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + //release + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + assert!(!KEY_NOTIFIED.load(core::sync::atomic::Ordering::SeqCst)); + assert!(KEY2_NOTIFIED.load(core::sync::atomic::Ordering::SeqCst)); } - let this = CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst) as *mut protocols::simple_text_input_ex::Protocol; - - let mut key_data: protocols::simple_text_input_ex::KeyData = Default::default(); - key_data.key.unicode_char = 'a' as u16; - let mut notify_handle = core::ptr::null_mut(); - - let status = SimpleTextInExFfi::simple_text_in_ex_register_key_notify( - this, - core::ptr::addr_of_mut!(key_data), - key_notify_callback_a, - core::ptr::addr_of_mut!(notify_handle), - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(notify_handle as usize, 1); - - notify_handle = core::ptr::null_mut(); - let status = SimpleTextInExFfi::simple_text_in_ex_register_key_notify( - this, - core::ptr::addr_of_mut!(key_data), - key_notify_callback_a_and_b, - core::ptr::addr_of_mut!(notify_handle), - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(notify_handle as usize, 2); - - notify_handle = core::ptr::null_mut(); - key_data.key.unicode_char = 'b' as u16; - let status = SimpleTextInExFfi::simple_text_in_ex_register_key_notify( - this, - core::ptr::addr_of_mut!(key_data), - key_notify_callback_a_and_b, - core::ptr::addr_of_mut!(notify_handle), - ); - assert_eq!(status, efi::Status::SUCCESS); - assert_eq!(notify_handle as usize, 3); - - //send 'b' - let report: &[u8] = &[0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - //release - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - assert!(!KEY_NOTIFIED.load(core::sync::atomic::Ordering::SeqCst)); - assert!(KEY2_NOTIFIED.load(core::sync::atomic::Ordering::SeqCst)); - - KEY_NOTIFIED.store(false, core::sync::atomic::Ordering::SeqCst); - KEY2_NOTIFIED.store(false, core::sync::atomic::Ordering::SeqCst); - - //send 'a' - let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - //release - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - assert!(KEY_NOTIFIED.load(core::sync::atomic::Ordering::SeqCst)); - assert!(KEY2_NOTIFIED.load(core::sync::atomic::Ordering::SeqCst)); - - KEY_NOTIFIED.store(false, core::sync::atomic::Ordering::SeqCst); - KEY2_NOTIFIED.store(false, core::sync::atomic::Ordering::SeqCst); - - //remove the 'a'-only callback - let status = SimpleTextInExFfi::simple_text_in_ex_unregister_key_notify(this, 1 as *mut c_void); - assert_eq!(status, efi::Status::SUCCESS); - - //send 'a' - let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - //release - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - assert!(!KEY_NOTIFIED.load(core::sync::atomic::Ordering::SeqCst)); - assert!(KEY2_NOTIFIED.load(core::sync::atomic::Ordering::SeqCst)); - } - - #[test] - fn wait_for_key_should_wait_for_key() { - static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); - static RECEIVED_EVENT: AtomicBool = AtomicBool::new(false); - - let boot_services = create_fake_static_boot_service(); - - // used in install - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, protocol, _, interface| { - if unsafe { *protocol } == protocols::simple_text_input_ex::PROTOCOL_GUID { - CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); - } - efi::Status::SUCCESS - }); - boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); - - boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); - boot_services.expect_restore_tpl().returning(|_| ()); - - boot_services.expect_signal_event().returning(|_| { - RECEIVED_EVENT.store(true, core::sync::atomic::Ordering::SeqCst); - efi::Status::SUCCESS - }); - - let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); - - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); - - keyboard_handler.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); - keyboard_handler.set_controller(Some(2 as efi::Handle)); - keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); - - RECEIVED_EVENT.store(false, core::sync::atomic::Ordering::SeqCst); - SimpleTextInExFfi::simple_text_in_ex_wait_for_key( - 3 as efi::Event, - CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), - ); - assert!(!RECEIVED_EVENT.load(core::sync::atomic::Ordering::SeqCst)); - - // press the a key - let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - //release keys - let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - keyboard_handler.receive_report(report, &hid_io); - - SimpleTextInExFfi::simple_text_in_ex_wait_for_key( - 3 as efi::Event, - CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), - ); - assert!(RECEIVED_EVENT.load(core::sync::atomic::Ordering::SeqCst)); - - // avoid keyboard drop uninstall flows - keyboard_handler.set_controller(None); - } + #[test] + fn wait_for_key_should_wait_for_key() { + static CONTEXT_PTR: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); + static RECEIVED_EVENT: AtomicBool = AtomicBool::new(false); + + let boot_services = create_fake_static_boot_service(); + + // used in install + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, protocol, _, interface| { + if unsafe { *protocol } == protocols::simple_text_input_ex::PROTOCOL_GUID { + CONTEXT_PTR.store(interface, core::sync::atomic::Ordering::SeqCst); + } + efi::Status::SUCCESS + }); + boot_services.expect_locate_protocol().returning(|_, _, _| efi::Status::NOT_FOUND); + + boot_services.expect_create_event_ex().returning(|_, _, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_raise_tpl().returning(|_| efi::TPL_APPLICATION); + boot_services.expect_restore_tpl().returning(|_| ()); + + boot_services.expect_signal_event().returning(|_| { + RECEIVED_EVENT.store(true, core::sync::atomic::Ordering::SeqCst); + efi::Status::SUCCESS + }); + + let mut keyboard_handler = KeyboardHidHandler::new(boot_services, 1 as efi::Handle); + + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); + + keyboard_handler.set_layout(Some(hii_keyboard_layout::get_default_keyboard_layout())); + keyboard_handler.set_controller(Some(2 as efi::Handle)); + keyboard_handler.initialize(2 as efi::Handle, &hid_io).unwrap(); + + RECEIVED_EVENT.store(false, core::sync::atomic::Ordering::SeqCst); + SimpleTextInExFfi::simple_text_in_ex_wait_for_key( + 3 as efi::Event, + CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), + ); + assert!(!RECEIVED_EVENT.load(core::sync::atomic::Ordering::SeqCst)); + + // press the a key + let report: &[u8] = &[0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + //release keys + let report: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + keyboard_handler.receive_report(report, &hid_io); + + SimpleTextInExFfi::simple_text_in_ex_wait_for_key( + 3 as efi::Event, + CONTEXT_PTR.load(core::sync::atomic::Ordering::SeqCst), + ); + assert!(RECEIVED_EVENT.load(core::sync::atomic::Ordering::SeqCst)); + + // avoid keyboard drop uninstall flows + keyboard_handler.set_controller(None); + } } diff --git a/HidPkg/UefiHidDxeV2/src/main.rs b/HidPkg/UefiHidDxeV2/src/main.rs index 140c8fb772..c373fe8791 100644 --- a/HidPkg/UefiHidDxeV2/src/main.rs +++ b/HidPkg/UefiHidDxeV2/src/main.rs @@ -14,77 +14,83 @@ #[cfg(target_os = "uefi")] mod uefi_entry { - extern crate alloc; - use core::{panic::PanicInfo, sync::atomic::Ordering}; + extern crate alloc; + use core::{panic::PanicInfo, sync::atomic::Ordering}; - use alloc::{boxed::Box, vec::Vec}; + use alloc::{boxed::Box, vec::Vec}; - use r_efi::{efi, system}; + use r_efi::{efi, system}; - use rust_advanced_logger_dxe::{debugln, init_debug, DEBUG_ERROR}; - use rust_boot_services_allocator_dxe::GLOBAL_ALLOCATOR; - use uefi_hid_dxe_v2::{ - boot_services::UefiBootServices, - driver_binding::UefiDriverBinding, - hid::{HidFactory, HidReceiverFactory}, - hid_io::{HidReportReceiver, UefiHidIoFactory}, - keyboard::KeyboardHidHandler, - pointer::PointerHidHandler, - BOOT_SERVICES, RUNTIME_SERVICES, - }; + use rust_advanced_logger_dxe::{debugln, init_debug, DEBUG_ERROR}; + use rust_boot_services_allocator_dxe::GLOBAL_ALLOCATOR; + use uefi_hid_dxe_v2::{ + boot_services::UefiBootServices, + driver_binding::UefiDriverBinding, + hid::{HidFactory, HidReceiverFactory}, + hid_io::{HidReportReceiver, UefiHidIoFactory}, + keyboard::KeyboardHidHandler, + pointer::PointerHidHandler, + BOOT_SERVICES, RUNTIME_SERVICES, + }; - struct UefiReceivers { - boot_services: &'static dyn UefiBootServices, - agent: efi::Handle, - } - impl HidReceiverFactory for UefiReceivers { - fn new_hid_receiver_list(&self, _controller: efi::Handle) -> Result>, efi::Status> { - let mut receivers: Vec> = Vec::new(); - receivers.push(Box::new(PointerHidHandler::new(self.boot_services, self.agent))); - receivers.push(Box::new(KeyboardHidHandler::new(self.boot_services, self.agent))); - Ok(receivers) + struct UefiReceivers { + boot_services: &'static dyn UefiBootServices, + agent: efi::Handle, } - } - - #[no_mangle] - pub extern "efiapi" fn efi_main(image_handle: efi::Handle, system_table: *const system::SystemTable) -> efi::Status { - // Safety: This block is unsafe because it assumes that system_table and (*system_table).boot_services are correct, - // and because it mutates/accesses the global BOOT_SERVICES static. - unsafe { - BOOT_SERVICES.initialize((*system_table).boot_services); - RUNTIME_SERVICES.store((*system_table).runtime_services, Ordering::SeqCst); - GLOBAL_ALLOCATOR.init((*system_table).boot_services); - init_debug((*system_table).boot_services); + impl HidReceiverFactory for UefiReceivers { + fn new_hid_receiver_list( + &self, + _controller: efi::Handle, + ) -> Result>, efi::Status> { + let mut receivers: Vec> = Vec::new(); + receivers.push(Box::new(PointerHidHandler::new(self.boot_services, self.agent))); + receivers.push(Box::new(KeyboardHidHandler::new(self.boot_services, self.agent))); + Ok(receivers) + } } - let hid_io_factory = Box::new(UefiHidIoFactory::new(&BOOT_SERVICES, image_handle)); - let receiver_factory = Box::new(UefiReceivers { boot_services: &BOOT_SERVICES, agent: image_handle }); - let hid_factory = Box::new(HidFactory::new(hid_io_factory, receiver_factory, image_handle)); + #[no_mangle] + pub extern "efiapi" fn efi_main( + image_handle: efi::Handle, + system_table: *const system::SystemTable, + ) -> efi::Status { + // Safety: This block is unsafe because it assumes that system_table and (*system_table).boot_services are correct, + // and because it mutates/accesses the global BOOT_SERVICES static. + unsafe { + BOOT_SERVICES.initialize((*system_table).boot_services); + RUNTIME_SERVICES.store((*system_table).runtime_services, Ordering::SeqCst); + GLOBAL_ALLOCATOR.init((*system_table).boot_services); + init_debug((*system_table).boot_services); + } + + let hid_io_factory = Box::new(UefiHidIoFactory::new(&BOOT_SERVICES, image_handle)); + let receiver_factory = Box::new(UefiReceivers { boot_services: &BOOT_SERVICES, agent: image_handle }); + let hid_factory = Box::new(HidFactory::new(hid_io_factory, receiver_factory, image_handle)); - let hid_binding = UefiDriverBinding::new(&BOOT_SERVICES, hid_factory, image_handle); - hid_binding.install().expect("failed to install HID driver binding"); + let hid_binding = UefiDriverBinding::new(&BOOT_SERVICES, hid_factory, image_handle); + hid_binding.install().expect("failed to install HID driver binding"); - efi::Status::SUCCESS - } + efi::Status::SUCCESS + } - #[panic_handler] - fn panic(info: &PanicInfo) -> ! { - debugln!(DEBUG_ERROR, "Panic: {:?}", info); - loop {} - } + #[panic_handler] + fn panic(info: &PanicInfo) -> ! { + debugln!(DEBUG_ERROR, "Panic: {:?}", info); + loop {} + } } #[cfg(not(target_os = "uefi"))] fn main() { - //do nothing. + //do nothing. } #[cfg(test)] mod test { - use crate::main; + use crate::main; - #[test] - fn main_should_do_nothing() { - main(); - } + #[test] + fn main_should_do_nothing() { + main(); + } } diff --git a/HidPkg/UefiHidDxeV2/src/pointer/absolute_pointer.rs b/HidPkg/UefiHidDxeV2/src/pointer/absolute_pointer.rs index 59fdbc5b23..5c00c6fb19 100644 --- a/HidPkg/UefiHidDxeV2/src/pointer/absolute_pointer.rs +++ b/HidPkg/UefiHidDxeV2/src/pointer/absolute_pointer.rs @@ -31,571 +31,571 @@ use super::{PointerHidHandler, BUTTON_MAX, BUTTON_MIN, DIGITIZER_SWITCH_MAX, DIG // absolute_pointer structure. #[repr(C)] pub struct PointerContext { - absolute_pointer: protocols::absolute_pointer::Protocol, - boot_services: &'static dyn UefiBootServices, - pointer_handler: *mut PointerHidHandler, + absolute_pointer: protocols::absolute_pointer::Protocol, + boot_services: &'static dyn UefiBootServices, + pointer_handler: *mut PointerHidHandler, } impl Drop for PointerContext { - fn drop(&mut self) { - if !self.absolute_pointer.mode.is_null() { - drop(unsafe { Box::from_raw(self.absolute_pointer.mode) }); + fn drop(&mut self) { + if !self.absolute_pointer.mode.is_null() { + drop(unsafe { Box::from_raw(self.absolute_pointer.mode) }); + } } - } } impl PointerContext { - /// Installs the absolute pointer protocol - pub fn install( - boot_services: &'static dyn UefiBootServices, - controller: efi::Handle, - pointer_handler: &mut PointerHidHandler, - ) -> Result<(), efi::Status> { - // Create pointer context. - let pointer_ctx = PointerContext { - absolute_pointer: protocols::absolute_pointer::Protocol { - reset: Self::absolute_pointer_reset, - get_state: Self::absolute_pointer_get_state, - mode: Box::into_raw(Box::new(Self::initialize_mode(pointer_handler))), - wait_for_input: core::ptr::null_mut(), - }, - boot_services, - pointer_handler: pointer_handler as *mut PointerHidHandler, - }; + /// Installs the absolute pointer protocol + pub fn install( + boot_services: &'static dyn UefiBootServices, + controller: efi::Handle, + pointer_handler: &mut PointerHidHandler, + ) -> Result<(), efi::Status> { + // Create pointer context. + let pointer_ctx = PointerContext { + absolute_pointer: protocols::absolute_pointer::Protocol { + reset: Self::absolute_pointer_reset, + get_state: Self::absolute_pointer_get_state, + mode: Box::into_raw(Box::new(Self::initialize_mode(pointer_handler))), + wait_for_input: core::ptr::null_mut(), + }, + boot_services, + pointer_handler: pointer_handler as *mut PointerHidHandler, + }; + + let absolute_pointer_ptr = Box::into_raw(Box::new(pointer_ctx)); + + // create event for wait_for_input. + let mut wait_for_pointer_input_event: efi::Event = core::ptr::null_mut(); + let status = boot_services.create_event( + efi::EVT_NOTIFY_WAIT, + efi::TPL_NOTIFY, + Some(Self::wait_for_pointer), + absolute_pointer_ptr as *mut c_void, + core::ptr::addr_of_mut!(wait_for_pointer_input_event), + ); + if status.is_error() { + drop(unsafe { Box::from_raw(absolute_pointer_ptr) }); + return Err(status); + } - let absolute_pointer_ptr = Box::into_raw(Box::new(pointer_ctx)); - - // create event for wait_for_input. - let mut wait_for_pointer_input_event: efi::Event = core::ptr::null_mut(); - let status = boot_services.create_event( - efi::EVT_NOTIFY_WAIT, - efi::TPL_NOTIFY, - Some(Self::wait_for_pointer), - absolute_pointer_ptr as *mut c_void, - core::ptr::addr_of_mut!(wait_for_pointer_input_event), - ); - if status.is_error() { - drop(unsafe { Box::from_raw(absolute_pointer_ptr) }); - return Err(status); - } + unsafe { (*absolute_pointer_ptr).absolute_pointer.wait_for_input = wait_for_pointer_input_event }; + + // install the absolute_pointer protocol. + let mut controller = controller; + let status = boot_services.install_protocol_interface( + core::ptr::addr_of_mut!(controller), + &protocols::absolute_pointer::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + efi::NATIVE_INTERFACE, + absolute_pointer_ptr as *mut c_void, + ); + + if status.is_error() { + let _ = boot_services.close_event(wait_for_pointer_input_event); + drop(unsafe { Box::from_raw(absolute_pointer_ptr) }); + return Err(status); + } - unsafe { (*absolute_pointer_ptr).absolute_pointer.wait_for_input = wait_for_pointer_input_event }; - - // install the absolute_pointer protocol. - let mut controller = controller; - let status = boot_services.install_protocol_interface( - core::ptr::addr_of_mut!(controller), - &protocols::absolute_pointer::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - efi::NATIVE_INTERFACE, - absolute_pointer_ptr as *mut c_void, - ); - - if status.is_error() { - let _ = boot_services.close_event(wait_for_pointer_input_event); - drop(unsafe { Box::from_raw(absolute_pointer_ptr) }); - return Err(status); + Ok(()) } - Ok(()) - } + // Initializes the absolute_pointer mode structure. + fn initialize_mode(pointer_handler: &PointerHidHandler) -> protocols::absolute_pointer::Mode { + let mut mode: protocols::absolute_pointer::Mode = Default::default(); - // Initializes the absolute_pointer mode structure. - fn initialize_mode(pointer_handler: &PointerHidHandler) -> protocols::absolute_pointer::Mode { - let mut mode: protocols::absolute_pointer::Mode = Default::default(); + if pointer_handler.supported_usages.contains(&Usage::from(super::GENERIC_DESKTOP_X)) { + mode.absolute_max_x = super::AXIS_RESOLUTION; + mode.absolute_min_x = 0; + } else { + debugln!(DEBUG_WARN, "No x-axis usages found in the report descriptor."); + } - if pointer_handler.supported_usages.contains(&Usage::from(super::GENERIC_DESKTOP_X)) { - mode.absolute_max_x = super::AXIS_RESOLUTION; - mode.absolute_min_x = 0; - } else { - debugln!(DEBUG_WARN, "No x-axis usages found in the report descriptor."); - } + if pointer_handler.supported_usages.contains(&Usage::from(super::GENERIC_DESKTOP_Y)) { + mode.absolute_max_y = super::AXIS_RESOLUTION; + mode.absolute_min_y = 0; + } else { + debugln!(DEBUG_WARN, "No y-axis usages found in the report descriptor."); + } - if pointer_handler.supported_usages.contains(&Usage::from(super::GENERIC_DESKTOP_Y)) { - mode.absolute_max_y = super::AXIS_RESOLUTION; - mode.absolute_min_y = 0; - } else { - debugln!(DEBUG_WARN, "No y-axis usages found in the report descriptor."); - } + if (pointer_handler.supported_usages.contains(&Usage::from(super::GENERIC_DESKTOP_Z))) + || (pointer_handler.supported_usages.contains(&Usage::from(super::GENERIC_DESKTOP_WHEEL))) + { + mode.absolute_max_z = super::AXIS_RESOLUTION; + mode.absolute_min_z = 0; + //TODO: Z-axis is interpreted as pressure data. This is for compat with reference implementation in C, but + //could consider e.g. looking for actual digitizer tip pressure usages or something. + mode.attributes |= 0x02; + } else { + debugln!(DEBUG_INFO, "No z-axis usages found in the report descriptor."); + } - if (pointer_handler.supported_usages.contains(&Usage::from(super::GENERIC_DESKTOP_Z))) - || (pointer_handler.supported_usages.contains(&Usage::from(super::GENERIC_DESKTOP_WHEEL))) - { - mode.absolute_max_z = super::AXIS_RESOLUTION; - mode.absolute_min_z = 0; - //TODO: Z-axis is interpreted as pressure data. This is for compat with reference implementation in C, but - //could consider e.g. looking for actual digitizer tip pressure usages or something. - mode.attributes |= 0x02; - } else { - debugln!(DEBUG_INFO, "No z-axis usages found in the report descriptor."); - } + let button_count = pointer_handler + .supported_usages + .iter() + .filter(|x| matches!((**x).into(), BUTTON_MIN..=BUTTON_MAX | DIGITIZER_SWITCH_MIN..=DIGITIZER_SWITCH_MAX)) + .count(); - let button_count = pointer_handler - .supported_usages - .iter() - .filter(|x| matches!((**x).into(), BUTTON_MIN..=BUTTON_MAX | DIGITIZER_SWITCH_MIN..=DIGITIZER_SWITCH_MAX)) - .count(); + if button_count > 1 { + mode.attributes |= 0x01; // alternate button exists. + } - if button_count > 1 { - mode.attributes |= 0x01; // alternate button exists. + mode } - mode - } + /// Uninstalls the absolute pointer protocol + pub fn uninstall( + boot_services: &'static dyn UefiBootServices, + agent: efi::Handle, + controller: efi::Handle, + ) -> Result<(), efi::Status> { + let mut absolute_pointer_ptr: *mut PointerContext = core::ptr::null_mut(); + let status = boot_services.open_protocol( + controller, + &protocols::absolute_pointer::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + core::ptr::addr_of_mut!(absolute_pointer_ptr) as *mut *mut c_void, + agent, + controller, + efi::OPEN_PROTOCOL_GET_PROTOCOL, + ); + if status.is_error() { + //No protocol is actually installed on this controller, so nothing to clean up. + return Ok(()); + } - /// Uninstalls the absolute pointer protocol - pub fn uninstall( - boot_services: &'static dyn UefiBootServices, - agent: efi::Handle, - controller: efi::Handle, - ) -> Result<(), efi::Status> { - let mut absolute_pointer_ptr: *mut PointerContext = core::ptr::null_mut(); - let status = boot_services.open_protocol( - controller, - &protocols::absolute_pointer::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - core::ptr::addr_of_mut!(absolute_pointer_ptr) as *mut *mut c_void, - agent, - controller, - efi::OPEN_PROTOCOL_GET_PROTOCOL, - ); - if status.is_error() { - //No protocol is actually installed on this controller, so nothing to clean up. - return Ok(()); - } + //Attempt to uninstall the absolute_pointer interface - this should disconnect any drivers using it and release + //the interface. + let status = boot_services.uninstall_protocol_interface( + controller, + &protocols::absolute_pointer::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, + absolute_pointer_ptr as *mut c_void, + ); + if status.is_error() { + //An error here means some other driver might be holding on to the absolute_pointer_ptr. + //Mark the instance invalid by setting the pointer_handler raw pointer to null, but leak the PointerContext + //instance. Leaking context allows calls through the pointers on absolute_pointer_ptr to continue to resolve + //and return error based on observing pointer_handler is null. + debugln!(DEBUG_ERROR, "Failed to uninstall absolute_pointer interface, status: {:x?}", status); + + unsafe { + (*absolute_pointer_ptr).pointer_handler = core::ptr::null_mut(); + } + //return without tearing down the context. + return Err(status); + } - //Attempt to uninstall the absolute_pointer interface - this should disconnect any drivers using it and release - //the interface. - let status = boot_services.uninstall_protocol_interface( - controller, - &protocols::absolute_pointer::PROTOCOL_GUID as *const efi::Guid as *mut efi::Guid, - absolute_pointer_ptr as *mut c_void, - ); - if status.is_error() { - //An error here means some other driver might be holding on to the absolute_pointer_ptr. - //Mark the instance invalid by setting the pointer_handler raw pointer to null, but leak the PointerContext - //instance. Leaking context allows calls through the pointers on absolute_pointer_ptr to continue to resolve - //and return error based on observing pointer_handler is null. - debugln!(DEBUG_ERROR, "Failed to uninstall absolute_pointer interface, status: {:x?}", status); - - unsafe { - (*absolute_pointer_ptr).pointer_handler = core::ptr::null_mut(); - } - //return without tearing down the context. - return Err(status); - } + let wait_for_input_event: efi::Handle = unsafe { (*absolute_pointer_ptr).absolute_pointer.wait_for_input }; + let status = boot_services.close_event(wait_for_input_event); + if status.is_error() { + //An error here means the event was not uninstalled, so in theory the notification_callback on it could still be + //fired. + //Mark the instance invalid by setting the pointer_handler raw pointer to null, but leak the PointerContext + //instance. Leaking context allows calls through the pointers on absolute_pointer_ptr to continue to resolve + //and return error based on observing pointer_handler is null. + debugln!(DEBUG_ERROR, "Failed to close absolute_pointer.wait_for_input event, status: {:x?}", status); + unsafe { + (*absolute_pointer_ptr).pointer_handler = core::ptr::null_mut(); + } + return Err(status); + } - let wait_for_input_event: efi::Handle = unsafe { (*absolute_pointer_ptr).absolute_pointer.wait_for_input }; - let status = boot_services.close_event(wait_for_input_event); - if status.is_error() { - //An error here means the event was not uninstalled, so in theory the notification_callback on it could still be - //fired. - //Mark the instance invalid by setting the pointer_handler raw pointer to null, but leak the PointerContext - //instance. Leaking context allows calls through the pointers on absolute_pointer_ptr to continue to resolve - //and return error based on observing pointer_handler is null. - debugln!(DEBUG_ERROR, "Failed to close absolute_pointer.wait_for_input event, status: {:x?}", status); - unsafe { - (*absolute_pointer_ptr).pointer_handler = core::ptr::null_mut(); - } - return Err(status); + // None of the parts of absolute pointer are in use, so it is safe to reclaim it. + drop(unsafe { Box::from_raw(absolute_pointer_ptr) }); + Ok(()) } - // None of the parts of absolute pointer are in use, so it is safe to reclaim it. - drop(unsafe { Box::from_raw(absolute_pointer_ptr) }); - Ok(()) - } - - // event handler for wait_for_pointer event that is part of the absolute pointer interface. - extern "efiapi" fn wait_for_pointer(event: efi::Event, context: *mut c_void) { - let pointer_ctx = unsafe { (context as *mut PointerContext).as_mut().expect("bad context") }; - // raise to notify to protect access to pointer_handler, and check if event should be signalled. - let old_tpl = pointer_ctx.boot_services.raise_tpl(efi::TPL_NOTIFY); - { - let pointer_handler = unsafe { pointer_ctx.pointer_handler.as_mut() }; - if let Some(pointer_handler) = pointer_handler { - if pointer_handler.state_changed { - pointer_ctx.boot_services.signal_event(event); + // event handler for wait_for_pointer event that is part of the absolute pointer interface. + extern "efiapi" fn wait_for_pointer(event: efi::Event, context: *mut c_void) { + let pointer_ctx = unsafe { (context as *mut PointerContext).as_mut().expect("bad context") }; + // raise to notify to protect access to pointer_handler, and check if event should be signalled. + let old_tpl = pointer_ctx.boot_services.raise_tpl(efi::TPL_NOTIFY); + { + let pointer_handler = unsafe { pointer_ctx.pointer_handler.as_mut() }; + if let Some(pointer_handler) = pointer_handler { + if pointer_handler.state_changed { + pointer_ctx.boot_services.signal_event(event); + } + } else { + // implies that this API was invoked after pointer handler was dropped. + debugln!(DEBUG_ERROR, "absolute_pointer_reset invoked after pointer dropped."); + } } - } else { - // implies that this API was invoked after pointer handler was dropped. - debugln!(DEBUG_ERROR, "absolute_pointer_reset invoked after pointer dropped."); - } - } - pointer_ctx.boot_services.restore_tpl(old_tpl); - } - - // resets the pointer state - part of the absolute pointer interface. - extern "efiapi" fn absolute_pointer_reset( - this: *mut protocols::absolute_pointer::Protocol, - _extended_verification: bool, - ) -> efi::Status { - if this.is_null() { - return efi::Status::INVALID_PARAMETER; + pointer_ctx.boot_services.restore_tpl(old_tpl); } - let pointer_ctx = unsafe { (this as *mut PointerContext).as_mut().expect("bad context") }; - let mut status = efi::Status::SUCCESS; - { - // raise to notify to protect access to pointer_handler and reset pointer handler state - let old_tpl = pointer_ctx.boot_services.raise_tpl(efi::TPL_NOTIFY); - - let pointer_handler = unsafe { pointer_ctx.pointer_handler.as_mut() }; - if let Some(pointer_handler) = pointer_handler { - pointer_handler.reset_state(); - } else { - // implies that this API was invoked after pointer handler was dropped. - debugln!(DEBUG_ERROR, "absolute_pointer_reset invoked after pointer dropped."); - status = efi::Status::DEVICE_ERROR; - } - - pointer_ctx.boot_services.restore_tpl(old_tpl); - } - status - } - - // returns the current pointer state in the `state` buffer provided by the caller - part of the absolute pointer - // interface. - extern "efiapi" fn absolute_pointer_get_state( - this: *mut protocols::absolute_pointer::Protocol, - state: *mut protocols::absolute_pointer::State, - ) -> efi::Status { - if state.is_null() || state.is_null() { - return efi::Status::INVALID_PARAMETER; + + // resets the pointer state - part of the absolute pointer interface. + extern "efiapi" fn absolute_pointer_reset( + this: *mut protocols::absolute_pointer::Protocol, + _extended_verification: bool, + ) -> efi::Status { + if this.is_null() { + return efi::Status::INVALID_PARAMETER; + } + let pointer_ctx = unsafe { (this as *mut PointerContext).as_mut().expect("bad context") }; + let mut status = efi::Status::SUCCESS; + { + // raise to notify to protect access to pointer_handler and reset pointer handler state + let old_tpl = pointer_ctx.boot_services.raise_tpl(efi::TPL_NOTIFY); + + let pointer_handler = unsafe { pointer_ctx.pointer_handler.as_mut() }; + if let Some(pointer_handler) = pointer_handler { + pointer_handler.reset_state(); + } else { + // implies that this API was invoked after pointer handler was dropped. + debugln!(DEBUG_ERROR, "absolute_pointer_reset invoked after pointer dropped."); + status = efi::Status::DEVICE_ERROR; + } + + pointer_ctx.boot_services.restore_tpl(old_tpl); + } + status } - let pointer_ctx = unsafe { (this as *mut PointerContext).as_mut().expect("bad context") }; - let mut status = efi::Status::SUCCESS; - { - // raise to notify to protect access to pointer_handler, and retrieve pointer handler state. - let old_tpl = pointer_ctx.boot_services.raise_tpl(efi::TPL_NOTIFY); - - let pointer_handler = unsafe { pointer_ctx.pointer_handler.as_mut() }; - if let Some(pointer_handler) = pointer_handler { - if pointer_handler.state_changed { - unsafe { - state.write(pointer_handler.current_state); - } - pointer_handler.state_changed = false; - } else { - status = efi::Status::NOT_READY; + // returns the current pointer state in the `state` buffer provided by the caller - part of the absolute pointer + // interface. + extern "efiapi" fn absolute_pointer_get_state( + this: *mut protocols::absolute_pointer::Protocol, + state: *mut protocols::absolute_pointer::State, + ) -> efi::Status { + if state.is_null() || state.is_null() { + return efi::Status::INVALID_PARAMETER; } - } else { - // implies that this API was invoked after pointer handler was dropped. - debugln!(DEBUG_ERROR, "absolute_pointer_get_state invoked after pointer dropped."); - status = efi::Status::DEVICE_ERROR; - } - pointer_ctx.boot_services.restore_tpl(old_tpl); + let pointer_ctx = unsafe { (this as *mut PointerContext).as_mut().expect("bad context") }; + let mut status = efi::Status::SUCCESS; + { + // raise to notify to protect access to pointer_handler, and retrieve pointer handler state. + let old_tpl = pointer_ctx.boot_services.raise_tpl(efi::TPL_NOTIFY); + + let pointer_handler = unsafe { pointer_ctx.pointer_handler.as_mut() }; + if let Some(pointer_handler) = pointer_handler { + if pointer_handler.state_changed { + unsafe { + state.write(pointer_handler.current_state); + } + pointer_handler.state_changed = false; + } else { + status = efi::Status::NOT_READY; + } + } else { + // implies that this API was invoked after pointer handler was dropped. + debugln!(DEBUG_ERROR, "absolute_pointer_get_state invoked after pointer dropped."); + status = efi::Status::DEVICE_ERROR; + } + + pointer_ctx.boot_services.restore_tpl(old_tpl); + } + status } - status - } } #[cfg(test)] mod test { - use core::ffi::c_void; - - use r_efi::{efi, protocols}; - - use super::*; - - use crate::{ - boot_services::MockUefiBootServices, - hid_io::{HidReportReceiver, MockHidIo}, - pointer::CENTER, - }; - - static MOUSE_REPORT_DESCRIPTOR: &[u8] = &[ - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x02, // USAGE (Mouse) - 0xa1, 0x01, // COLLECTION (Application) - 0x09, 0x01, // USAGE(Pointer) - 0xa1, 0x00, // COLLECTION (Physical) - 0x05, 0x09, // USAGE_PAGE (Button) - 0x19, 0x01, // USAGE_MINIMUM(1) - 0x29, 0x05, // USAGE_MAXIMUM(5) - 0x15, 0x00, // LOGICAL_MINIMUM(0) - 0x25, 0x01, // LOGICAL_MAXIMUM(1) - 0x95, 0x05, // REPORT_COUNT(5) - 0x75, 0x01, // REPORT_SIZE(1) - 0x81, 0x02, // INPUT(Data, Variable, Absolute) - 0x95, 0x01, // REPORT_COUNT(1) - 0x75, 0x03, // REPORT_SIZE(3) - 0x81, 0x01, // INPUT(Constant, Array, Absolute) - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x30, // USAGE (X) - 0x09, 0x31, // USAGE (Y) - 0x09, 0x38, // USAGE (Wheel) - 0x15, 0x81, // LOGICAL_MINIMUM (-127) - 0x25, 0x7f, // LOGICAL_MAXIMUM (127) - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x03, // REPORT_COUNT (3) - 0x81, 0x06, // INPUT(Data, Variable, Relative) - 0xc0, // END_COLLECTION - 0xc0, // END_COLLECTION - ]; - - // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used - // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock - // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. - // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. - // This object needs to outlive anything that uses it - once created, it will live until the end of the program. - fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { - unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } - } - - #[test] - fn wait_for_event_should_wait_for_event() { - let boot_services = create_fake_static_boot_service(); - const AGENT_HANDLE: efi::Handle = 0x01 as efi::Handle; - const CONTROLLER_HANDLE: efi::Handle = 0x02 as efi::Handle; - const POINTER_EVENT: efi::Event = 0x03 as efi::Event; - - static mut ABS_PTR_INTERFACE: *mut c_void = core::ptr::null_mut(); - static mut EVENT_CONTEXT: *mut c_void = core::ptr::null_mut(); - static mut EVENT_SIGNALED: bool = false; - - // expected on PointerHidHandler::initialize(). - boot_services.expect_create_event().returning(|_, _, wait_for_ptr, context, event_ptr| { - assert!(wait_for_ptr == Some(PointerContext::wait_for_pointer)); - assert_ne!(context, core::ptr::null_mut()); - unsafe { - EVENT_CONTEXT = context; - event_ptr.write(POINTER_EVENT); - } - efi::Status::SUCCESS - }); - - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - unsafe { ABS_PTR_INTERFACE = interface }; - efi::Status::SUCCESS - }); - - // expected on PointerHidHandler::drop(). - boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { - unsafe { *interface = ABS_PTR_INTERFACE }; - efi::Status::SUCCESS - }); - boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); - boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); - - // expected on PointerHidHandler::receive_report - boot_services.expect_raise_tpl().returning(|new_tpl| { - assert_eq!(new_tpl, efi::TPL_NOTIFY); - efi::TPL_APPLICATION - }); - - boot_services.expect_restore_tpl().returning(|new_tpl| { - assert_eq!(new_tpl, efi::TPL_APPLICATION); - () - }); - - boot_services.expect_signal_event().returning(|event| { - assert_eq!(event, POINTER_EVENT); - unsafe { EVENT_SIGNALED = true }; - efi::Status::SUCCESS - }); - - let agent = AGENT_HANDLE; - let mut pointer_handler = PointerHidHandler::new(boot_services, agent); - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&MOUSE_REPORT_DESCRIPTOR).unwrap())); - - let controller = CONTROLLER_HANDLE; - assert_eq!(pointer_handler.initialize(controller, &hid_io), Ok(())); - - let absolute_pointer = unsafe { (ABS_PTR_INTERFACE as *mut PointerContext).as_mut() }.unwrap(); - assert_eq!(absolute_pointer.absolute_pointer.wait_for_input, POINTER_EVENT); - - // no pointer state change - should not signal event. - PointerContext::wait_for_pointer(POINTER_EVENT, unsafe { EVENT_CONTEXT }); - - assert_eq!(unsafe { EVENT_SIGNALED }, false); - - //click two buttons and move the cursor (+32,+32) - let report: &[u8] = &[0x05, 0x20, 0x20, 0]; - pointer_handler.receive_report(report, &hid_io); - - // pointer state change in place - should signal event. - PointerContext::wait_for_pointer(POINTER_EVENT, unsafe { EVENT_CONTEXT }); - assert_eq!(unsafe { EVENT_SIGNALED }, true); - } - - #[test] - fn absolute_pointer_reset_should_reset_pointer_state() { - let boot_services = create_fake_static_boot_service(); - const AGENT_HANDLE: efi::Handle = 0x01 as efi::Handle; - const CONTROLLER_HANDLE: efi::Handle = 0x02 as efi::Handle; - const EVENT_HANDLE: efi::Handle = 0x03 as efi::Handle; - - static mut ABS_PTR_INTERFACE: *mut c_void = core::ptr::null_mut(); - static mut EVENT_CONTEXT: *mut c_void = core::ptr::null_mut(); - - // expected on PointerHidHandler::initialize(). - boot_services.expect_create_event().returning(|_, _, wait_for_ptr, context, event_ptr| { - assert!(wait_for_ptr == Some(PointerContext::wait_for_pointer)); - assert_ne!(context, core::ptr::null_mut()); - unsafe { - EVENT_CONTEXT = context; - event_ptr.write(EVENT_HANDLE); - } - efi::Status::SUCCESS - }); - - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - unsafe { ABS_PTR_INTERFACE = interface }; - efi::Status::SUCCESS - }); - - // expected on PointerHidHandler::drop(). - boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { - unsafe { *interface = ABS_PTR_INTERFACE }; - efi::Status::SUCCESS - }); - boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); - boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); - - // expected on PointerHidHandler::receive_report - boot_services.expect_raise_tpl().returning(|new_tpl| { - assert_eq!(new_tpl, efi::TPL_NOTIFY); - efi::TPL_APPLICATION - }); - - boot_services.expect_restore_tpl().returning(|new_tpl| { - assert_eq!(new_tpl, efi::TPL_APPLICATION); - () - }); - - let agent = AGENT_HANDLE; - let mut pointer_handler = PointerHidHandler::new(boot_services, agent); - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&MOUSE_REPORT_DESCRIPTOR).unwrap())); - - let controller = CONTROLLER_HANDLE; - assert_eq!(pointer_handler.initialize(controller, &hid_io), Ok(())); - - assert_eq!(pointer_handler.current_state.active_buttons, 0); - assert_eq!(pointer_handler.current_state.current_x, CENTER); - assert_eq!(pointer_handler.current_state.current_y, CENTER); - assert_eq!(pointer_handler.current_state.current_z, 0); - assert_eq!(pointer_handler.state_changed, false); - - //click two buttons and move the cursor (+32,+32,+32) - let report: &[u8] = &[0x05, 0x20, 0x20, 0x20]; - pointer_handler.receive_report(report, &hid_io); - - assert_eq!(pointer_handler.current_state.active_buttons, 0x5); - assert_eq!(pointer_handler.current_state.current_x, CENTER + 0x20); - assert_eq!(pointer_handler.current_state.current_y, CENTER + 0x20); - assert_eq!(pointer_handler.current_state.current_z, 0x20); - assert_eq!(pointer_handler.state_changed, true); - - //reset state - let status = PointerContext::absolute_pointer_reset( - unsafe { ABS_PTR_INTERFACE as *mut protocols::absolute_pointer::Protocol }, - false, - ); - assert_eq!(status, efi::Status::SUCCESS); - - assert_eq!(pointer_handler.current_state.active_buttons, 0); - assert_eq!(pointer_handler.current_state.current_x, CENTER); - assert_eq!(pointer_handler.current_state.current_y, CENTER); - assert_eq!(pointer_handler.current_state.current_z, 0); - assert_eq!(pointer_handler.state_changed, false); - } - - #[test] - fn absolute_pointer_get_state_should_return_current_state_and_clear_changed_flag() { - let boot_services = create_fake_static_boot_service(); - const AGENT_HANDLE: efi::Handle = 0x01 as efi::Handle; - const CONTROLLER_HANDLE: efi::Handle = 0x02 as efi::Handle; - const EVENT_HANDLE: efi::Handle = 0x03 as efi::Handle; - - static mut ABS_PTR_INTERFACE: *mut c_void = core::ptr::null_mut(); - static mut EVENT_CONTEXT: *mut c_void = core::ptr::null_mut(); - - // expected on PointerHidHandler::initialize(). - boot_services.expect_create_event().returning(|_, _, wait_for_ptr, context, event_ptr| { - assert!(wait_for_ptr == Some(PointerContext::wait_for_pointer)); - assert_ne!(context, core::ptr::null_mut()); - unsafe { - EVENT_CONTEXT = context; - event_ptr.write(EVENT_HANDLE); - } - efi::Status::SUCCESS - }); - - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - unsafe { ABS_PTR_INTERFACE = interface }; - efi::Status::SUCCESS - }); - - // expected on PointerHidHandler::drop(). - boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { - unsafe { *interface = ABS_PTR_INTERFACE }; - efi::Status::SUCCESS - }); - boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); - boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); - - // expected on PointerHidHandler::receive_report - boot_services.expect_raise_tpl().returning(|new_tpl| { - assert_eq!(new_tpl, efi::TPL_NOTIFY); - efi::TPL_APPLICATION - }); - - boot_services.expect_restore_tpl().returning(|new_tpl| { - assert_eq!(new_tpl, efi::TPL_APPLICATION); - () - }); - - let agent = AGENT_HANDLE; - let mut pointer_handler = PointerHidHandler::new(boot_services, agent); - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&MOUSE_REPORT_DESCRIPTOR).unwrap())); - - let controller = CONTROLLER_HANDLE; - assert_eq!(pointer_handler.initialize(controller, &hid_io), Ok(())); - - assert_eq!(pointer_handler.current_state.active_buttons, 0); - assert_eq!(pointer_handler.current_state.current_x, CENTER); - assert_eq!(pointer_handler.current_state.current_y, CENTER); - assert_eq!(pointer_handler.current_state.current_z, 0); - assert_eq!(pointer_handler.state_changed, false); - - //click two buttons and move the cursor (+32,+32,+32) - let report: &[u8] = &[0x05, 0x20, 0x20, 0x20]; - pointer_handler.receive_report(report, &hid_io); - - assert_eq!(pointer_handler.current_state.active_buttons, 0x5); - assert_eq!(pointer_handler.current_state.current_x, CENTER + 0x20); - assert_eq!(pointer_handler.current_state.current_y, CENTER + 0x20); - assert_eq!(pointer_handler.current_state.current_z, 0x20); - assert_eq!(pointer_handler.state_changed, true); - - let mut absolute_pointer_state: protocols::absolute_pointer::State = Default::default(); - let status = PointerContext::absolute_pointer_get_state( - unsafe { ABS_PTR_INTERFACE as *mut protocols::absolute_pointer::Protocol }, - &mut absolute_pointer_state as *mut protocols::absolute_pointer::State, - ); - assert_eq!(status, efi::Status::SUCCESS); - - assert_eq!(absolute_pointer_state.current_x, pointer_handler.current_state.current_x); - assert_eq!(absolute_pointer_state.current_y, pointer_handler.current_state.current_y); - assert_eq!(absolute_pointer_state.current_z, pointer_handler.current_state.current_z); - assert_eq!(absolute_pointer_state.active_buttons, pointer_handler.current_state.active_buttons); - assert_eq!(pointer_handler.state_changed, false); - - //if get_state is attempted when there are no changes to state, it should return NOT_READY. - let mut absolute_pointer_state: protocols::absolute_pointer::State = Default::default(); - let status = PointerContext::absolute_pointer_get_state( - unsafe { ABS_PTR_INTERFACE as *mut protocols::absolute_pointer::Protocol }, - &mut absolute_pointer_state as *mut protocols::absolute_pointer::State, - ); - assert_eq!(status, efi::Status::NOT_READY); - } + use core::ffi::c_void; + + use r_efi::{efi, protocols}; + + use super::*; + + use crate::{ + boot_services::MockUefiBootServices, + hid_io::{HidReportReceiver, MockHidIo}, + pointer::CENTER, + }; + + static MOUSE_REPORT_DESCRIPTOR: &[u8] = &[ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x02, // USAGE (Mouse) + 0xa1, 0x01, // COLLECTION (Application) + 0x09, 0x01, // USAGE(Pointer) + 0xa1, 0x00, // COLLECTION (Physical) + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM(1) + 0x29, 0x05, // USAGE_MAXIMUM(5) + 0x15, 0x00, // LOGICAL_MINIMUM(0) + 0x25, 0x01, // LOGICAL_MAXIMUM(1) + 0x95, 0x05, // REPORT_COUNT(5) + 0x75, 0x01, // REPORT_SIZE(1) + 0x81, 0x02, // INPUT(Data, Variable, Absolute) + 0x95, 0x01, // REPORT_COUNT(1) + 0x75, 0x03, // REPORT_SIZE(3) + 0x81, 0x01, // INPUT(Constant, Array, Absolute) + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x09, 0x38, // USAGE (Wheel) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x03, // REPORT_COUNT (3) + 0x81, 0x06, // INPUT(Data, Variable, Relative) + 0xc0, // END_COLLECTION + 0xc0, // END_COLLECTION + ]; + + // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used + // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock + // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. + // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. + // This object needs to outlive anything that uses it - once created, it will live until the end of the program. + fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { + unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } + } + + #[test] + fn wait_for_event_should_wait_for_event() { + let boot_services = create_fake_static_boot_service(); + const AGENT_HANDLE: efi::Handle = 0x01 as efi::Handle; + const CONTROLLER_HANDLE: efi::Handle = 0x02 as efi::Handle; + const POINTER_EVENT: efi::Event = 0x03 as efi::Event; + + static mut ABS_PTR_INTERFACE: *mut c_void = core::ptr::null_mut(); + static mut EVENT_CONTEXT: *mut c_void = core::ptr::null_mut(); + static mut EVENT_SIGNALED: bool = false; + + // expected on PointerHidHandler::initialize(). + boot_services.expect_create_event().returning(|_, _, wait_for_ptr, context, event_ptr| { + assert!(wait_for_ptr == Some(PointerContext::wait_for_pointer)); + assert_ne!(context, core::ptr::null_mut()); + unsafe { + EVENT_CONTEXT = context; + event_ptr.write(POINTER_EVENT); + } + efi::Status::SUCCESS + }); + + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + unsafe { ABS_PTR_INTERFACE = interface }; + efi::Status::SUCCESS + }); + + // expected on PointerHidHandler::drop(). + boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { + unsafe { *interface = ABS_PTR_INTERFACE }; + efi::Status::SUCCESS + }); + boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); + boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); + + // expected on PointerHidHandler::receive_report + boot_services.expect_raise_tpl().returning(|new_tpl| { + assert_eq!(new_tpl, efi::TPL_NOTIFY); + efi::TPL_APPLICATION + }); + + boot_services.expect_restore_tpl().returning(|new_tpl| { + assert_eq!(new_tpl, efi::TPL_APPLICATION); + () + }); + + boot_services.expect_signal_event().returning(|event| { + assert_eq!(event, POINTER_EVENT); + unsafe { EVENT_SIGNALED = true }; + efi::Status::SUCCESS + }); + + let agent = AGENT_HANDLE; + let mut pointer_handler = PointerHidHandler::new(boot_services, agent); + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&MOUSE_REPORT_DESCRIPTOR).unwrap())); + + let controller = CONTROLLER_HANDLE; + assert_eq!(pointer_handler.initialize(controller, &hid_io), Ok(())); + + let absolute_pointer = unsafe { (ABS_PTR_INTERFACE as *mut PointerContext).as_mut() }.unwrap(); + assert_eq!(absolute_pointer.absolute_pointer.wait_for_input, POINTER_EVENT); + + // no pointer state change - should not signal event. + PointerContext::wait_for_pointer(POINTER_EVENT, unsafe { EVENT_CONTEXT }); + + assert_eq!(unsafe { EVENT_SIGNALED }, false); + + //click two buttons and move the cursor (+32,+32) + let report: &[u8] = &[0x05, 0x20, 0x20, 0]; + pointer_handler.receive_report(report, &hid_io); + + // pointer state change in place - should signal event. + PointerContext::wait_for_pointer(POINTER_EVENT, unsafe { EVENT_CONTEXT }); + assert_eq!(unsafe { EVENT_SIGNALED }, true); + } + + #[test] + fn absolute_pointer_reset_should_reset_pointer_state() { + let boot_services = create_fake_static_boot_service(); + const AGENT_HANDLE: efi::Handle = 0x01 as efi::Handle; + const CONTROLLER_HANDLE: efi::Handle = 0x02 as efi::Handle; + const EVENT_HANDLE: efi::Handle = 0x03 as efi::Handle; + + static mut ABS_PTR_INTERFACE: *mut c_void = core::ptr::null_mut(); + static mut EVENT_CONTEXT: *mut c_void = core::ptr::null_mut(); + + // expected on PointerHidHandler::initialize(). + boot_services.expect_create_event().returning(|_, _, wait_for_ptr, context, event_ptr| { + assert!(wait_for_ptr == Some(PointerContext::wait_for_pointer)); + assert_ne!(context, core::ptr::null_mut()); + unsafe { + EVENT_CONTEXT = context; + event_ptr.write(EVENT_HANDLE); + } + efi::Status::SUCCESS + }); + + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + unsafe { ABS_PTR_INTERFACE = interface }; + efi::Status::SUCCESS + }); + + // expected on PointerHidHandler::drop(). + boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { + unsafe { *interface = ABS_PTR_INTERFACE }; + efi::Status::SUCCESS + }); + boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); + boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); + + // expected on PointerHidHandler::receive_report + boot_services.expect_raise_tpl().returning(|new_tpl| { + assert_eq!(new_tpl, efi::TPL_NOTIFY); + efi::TPL_APPLICATION + }); + + boot_services.expect_restore_tpl().returning(|new_tpl| { + assert_eq!(new_tpl, efi::TPL_APPLICATION); + () + }); + + let agent = AGENT_HANDLE; + let mut pointer_handler = PointerHidHandler::new(boot_services, agent); + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&MOUSE_REPORT_DESCRIPTOR).unwrap())); + + let controller = CONTROLLER_HANDLE; + assert_eq!(pointer_handler.initialize(controller, &hid_io), Ok(())); + + assert_eq!(pointer_handler.current_state.active_buttons, 0); + assert_eq!(pointer_handler.current_state.current_x, CENTER); + assert_eq!(pointer_handler.current_state.current_y, CENTER); + assert_eq!(pointer_handler.current_state.current_z, 0); + assert_eq!(pointer_handler.state_changed, false); + + //click two buttons and move the cursor (+32,+32,+32) + let report: &[u8] = &[0x05, 0x20, 0x20, 0x20]; + pointer_handler.receive_report(report, &hid_io); + + assert_eq!(pointer_handler.current_state.active_buttons, 0x5); + assert_eq!(pointer_handler.current_state.current_x, CENTER + 0x20); + assert_eq!(pointer_handler.current_state.current_y, CENTER + 0x20); + assert_eq!(pointer_handler.current_state.current_z, 0x20); + assert_eq!(pointer_handler.state_changed, true); + + //reset state + let status = PointerContext::absolute_pointer_reset( + unsafe { ABS_PTR_INTERFACE as *mut protocols::absolute_pointer::Protocol }, + false, + ); + assert_eq!(status, efi::Status::SUCCESS); + + assert_eq!(pointer_handler.current_state.active_buttons, 0); + assert_eq!(pointer_handler.current_state.current_x, CENTER); + assert_eq!(pointer_handler.current_state.current_y, CENTER); + assert_eq!(pointer_handler.current_state.current_z, 0); + assert_eq!(pointer_handler.state_changed, false); + } + + #[test] + fn absolute_pointer_get_state_should_return_current_state_and_clear_changed_flag() { + let boot_services = create_fake_static_boot_service(); + const AGENT_HANDLE: efi::Handle = 0x01 as efi::Handle; + const CONTROLLER_HANDLE: efi::Handle = 0x02 as efi::Handle; + const EVENT_HANDLE: efi::Handle = 0x03 as efi::Handle; + + static mut ABS_PTR_INTERFACE: *mut c_void = core::ptr::null_mut(); + static mut EVENT_CONTEXT: *mut c_void = core::ptr::null_mut(); + + // expected on PointerHidHandler::initialize(). + boot_services.expect_create_event().returning(|_, _, wait_for_ptr, context, event_ptr| { + assert!(wait_for_ptr == Some(PointerContext::wait_for_pointer)); + assert_ne!(context, core::ptr::null_mut()); + unsafe { + EVENT_CONTEXT = context; + event_ptr.write(EVENT_HANDLE); + } + efi::Status::SUCCESS + }); + + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + unsafe { ABS_PTR_INTERFACE = interface }; + efi::Status::SUCCESS + }); + + // expected on PointerHidHandler::drop(). + boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { + unsafe { *interface = ABS_PTR_INTERFACE }; + efi::Status::SUCCESS + }); + boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); + boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); + + // expected on PointerHidHandler::receive_report + boot_services.expect_raise_tpl().returning(|new_tpl| { + assert_eq!(new_tpl, efi::TPL_NOTIFY); + efi::TPL_APPLICATION + }); + + boot_services.expect_restore_tpl().returning(|new_tpl| { + assert_eq!(new_tpl, efi::TPL_APPLICATION); + () + }); + + let agent = AGENT_HANDLE; + let mut pointer_handler = PointerHidHandler::new(boot_services, agent); + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&MOUSE_REPORT_DESCRIPTOR).unwrap())); + + let controller = CONTROLLER_HANDLE; + assert_eq!(pointer_handler.initialize(controller, &hid_io), Ok(())); + + assert_eq!(pointer_handler.current_state.active_buttons, 0); + assert_eq!(pointer_handler.current_state.current_x, CENTER); + assert_eq!(pointer_handler.current_state.current_y, CENTER); + assert_eq!(pointer_handler.current_state.current_z, 0); + assert_eq!(pointer_handler.state_changed, false); + + //click two buttons and move the cursor (+32,+32,+32) + let report: &[u8] = &[0x05, 0x20, 0x20, 0x20]; + pointer_handler.receive_report(report, &hid_io); + + assert_eq!(pointer_handler.current_state.active_buttons, 0x5); + assert_eq!(pointer_handler.current_state.current_x, CENTER + 0x20); + assert_eq!(pointer_handler.current_state.current_y, CENTER + 0x20); + assert_eq!(pointer_handler.current_state.current_z, 0x20); + assert_eq!(pointer_handler.state_changed, true); + + let mut absolute_pointer_state: protocols::absolute_pointer::State = Default::default(); + let status = PointerContext::absolute_pointer_get_state( + unsafe { ABS_PTR_INTERFACE as *mut protocols::absolute_pointer::Protocol }, + &mut absolute_pointer_state as *mut protocols::absolute_pointer::State, + ); + assert_eq!(status, efi::Status::SUCCESS); + + assert_eq!(absolute_pointer_state.current_x, pointer_handler.current_state.current_x); + assert_eq!(absolute_pointer_state.current_y, pointer_handler.current_state.current_y); + assert_eq!(absolute_pointer_state.current_z, pointer_handler.current_state.current_z); + assert_eq!(absolute_pointer_state.active_buttons, pointer_handler.current_state.active_buttons); + assert_eq!(pointer_handler.state_changed, false); + + //if get_state is attempted when there are no changes to state, it should return NOT_READY. + let mut absolute_pointer_state: protocols::absolute_pointer::State = Default::default(); + let status = PointerContext::absolute_pointer_get_state( + unsafe { ABS_PTR_INTERFACE as *mut protocols::absolute_pointer::Protocol }, + &mut absolute_pointer_state as *mut protocols::absolute_pointer::State, + ); + assert_eq!(status, efi::Status::NOT_READY); + } } diff --git a/HidPkg/UefiHidDxeV2/src/pointer/mod.rs b/HidPkg/UefiHidDxeV2/src/pointer/mod.rs index c352d0ebe4..78e7a07afd 100644 --- a/HidPkg/UefiHidDxeV2/src/pointer/mod.rs +++ b/HidPkg/UefiHidDxeV2/src/pointer/mod.rs @@ -12,20 +12,20 @@ mod absolute_pointer; use alloc::{ - collections::{BTreeMap, BTreeSet}, - vec::Vec, + collections::{BTreeMap, BTreeSet}, + vec::Vec, }; use hidparser::{ - report_data_types::{ReportId, Usage}, - ReportDescriptor, ReportField, VariableField, + report_data_types::{ReportId, Usage}, + ReportDescriptor, ReportField, VariableField, }; use r_efi::{efi, protocols}; use rust_advanced_logger_dxe::{debugln, function, DEBUG_ERROR, DEBUG_VERBOSE}; use crate::{ - boot_services::UefiBootServices, - hid_io::{HidIo, HidReportReceiver}, + boot_services::UefiBootServices, + hid_io::{HidIo, HidReportReceiver}, }; use self::absolute_pointer::PointerContext; @@ -47,639 +47,644 @@ const CENTER: u64 = AXIS_RESOLUTION / 2; // Maps a given field to a routine that handles input from it. #[derive(Debug, Clone)] struct ReportFieldWithHandler { - field: VariableField, - report_handler: fn(&mut PointerHidHandler, field: VariableField, report: &[u8]), + field: VariableField, + report_handler: fn(&mut PointerHidHandler, field: VariableField, report: &[u8]), } // Defines a report and the fields of interest within it. #[derive(Debug, Default, Clone)] struct PointerReportData { - report_id: Option, - report_size: usize, - relevant_fields: Vec, + report_id: Option, + report_size: usize, + relevant_fields: Vec, } /// Pointer HID Handler pub struct PointerHidHandler { - boot_services: &'static dyn UefiBootServices, - agent: efi::Handle, - controller: Option, - input_reports: BTreeMap, PointerReportData>, - supported_usages: BTreeSet, - report_id_present: bool, - state_changed: bool, - current_state: protocols::absolute_pointer::State, + boot_services: &'static dyn UefiBootServices, + agent: efi::Handle, + controller: Option, + input_reports: BTreeMap, PointerReportData>, + supported_usages: BTreeSet, + report_id_present: bool, + state_changed: bool, + current_state: protocols::absolute_pointer::State, } impl PointerHidHandler { - /// Instantiates a new Pointer HID handler. `agent` is the handle that owns the handler (typically image_handle). - pub fn new(boot_services: &'static dyn UefiBootServices, agent: efi::Handle) -> Self { - let mut handler = Self { - boot_services, - agent, - controller: None, - input_reports: BTreeMap::new(), - supported_usages: BTreeSet::new(), - report_id_present: false, - state_changed: false, - current_state: Default::default(), - }; - handler.reset_state(); - handler - } - - // Processes the report descriptor to determine whether this is a supported device, and if so, extract the information - // required to process reports. - fn process_descriptor(&mut self, descriptor: ReportDescriptor) -> Result<(), efi::Status> { - let multiple_reports = descriptor.input_reports.len() > 1; - - for report in &descriptor.input_reports { - let mut report_data = PointerReportData { report_id: report.report_id, ..Default::default() }; - - self.report_id_present = report.report_id.is_some(); - - if multiple_reports && !self.report_id_present { - //invalid to have None ReportId if multiple reports present. - Err(efi::Status::DEVICE_ERROR)?; - } - - report_data.report_size = report.size_in_bits.div_ceil(8); - - for field in &report.fields { - if let ReportField::Variable(field) = field { - match field.usage.into() { - GENERIC_DESKTOP_X => { - let field_handler = ReportFieldWithHandler { field: field.clone(), report_handler: Self::x_axis_handler }; - report_data.relevant_fields.push(field_handler); - self.supported_usages.insert(field.usage); - } - GENERIC_DESKTOP_Y => { - let field_handler = ReportFieldWithHandler { field: field.clone(), report_handler: Self::y_axis_handler }; - report_data.relevant_fields.push(field_handler); - self.supported_usages.insert(field.usage); - } - GENERIC_DESKTOP_Z | GENERIC_DESKTOP_WHEEL => { - let field_handler = ReportFieldWithHandler { field: field.clone(), report_handler: Self::z_axis_handler }; - report_data.relevant_fields.push(field_handler); - self.supported_usages.insert(field.usage); + /// Instantiates a new Pointer HID handler. `agent` is the handle that owns the handler (typically image_handle). + pub fn new(boot_services: &'static dyn UefiBootServices, agent: efi::Handle) -> Self { + let mut handler = Self { + boot_services, + agent, + controller: None, + input_reports: BTreeMap::new(), + supported_usages: BTreeSet::new(), + report_id_present: false, + state_changed: false, + current_state: Default::default(), + }; + handler.reset_state(); + handler + } + + // Processes the report descriptor to determine whether this is a supported device, and if so, extract the information + // required to process reports. + fn process_descriptor(&mut self, descriptor: ReportDescriptor) -> Result<(), efi::Status> { + let multiple_reports = descriptor.input_reports.len() > 1; + + for report in &descriptor.input_reports { + let mut report_data = PointerReportData { report_id: report.report_id, ..Default::default() }; + + self.report_id_present = report.report_id.is_some(); + + if multiple_reports && !self.report_id_present { + //invalid to have None ReportId if multiple reports present. + Err(efi::Status::DEVICE_ERROR)?; } - BUTTON_MIN..=BUTTON_MAX => { - let field_handler = ReportFieldWithHandler { field: field.clone(), report_handler: Self::button_handler }; - report_data.relevant_fields.push(field_handler); - self.supported_usages.insert(field.usage); + + report_data.report_size = report.size_in_bits.div_ceil(8); + + for field in &report.fields { + if let ReportField::Variable(field) = field { + match field.usage.into() { + GENERIC_DESKTOP_X => { + let field_handler = + ReportFieldWithHandler { field: field.clone(), report_handler: Self::x_axis_handler }; + report_data.relevant_fields.push(field_handler); + self.supported_usages.insert(field.usage); + } + GENERIC_DESKTOP_Y => { + let field_handler = + ReportFieldWithHandler { field: field.clone(), report_handler: Self::y_axis_handler }; + report_data.relevant_fields.push(field_handler); + self.supported_usages.insert(field.usage); + } + GENERIC_DESKTOP_Z | GENERIC_DESKTOP_WHEEL => { + let field_handler = + ReportFieldWithHandler { field: field.clone(), report_handler: Self::z_axis_handler }; + report_data.relevant_fields.push(field_handler); + self.supported_usages.insert(field.usage); + } + BUTTON_MIN..=BUTTON_MAX => { + let field_handler = + ReportFieldWithHandler { field: field.clone(), report_handler: Self::button_handler }; + report_data.relevant_fields.push(field_handler); + self.supported_usages.insert(field.usage); + } + DIGITIZER_SWITCH_MIN..=DIGITIZER_SWITCH_MAX => { + let field_handler = + ReportFieldWithHandler { field: field.clone(), report_handler: Self::button_handler }; + report_data.relevant_fields.push(field_handler); + self.supported_usages.insert(field.usage); + } + _ => (), //other usages irrelevant + } + } } - DIGITIZER_SWITCH_MIN..=DIGITIZER_SWITCH_MAX => { - let field_handler = ReportFieldWithHandler { field: field.clone(), report_handler: Self::button_handler }; - report_data.relevant_fields.push(field_handler); - self.supported_usages.insert(field.usage); + + if !report_data.relevant_fields.is_empty() { + self.input_reports.insert(report_data.report_id, report_data); } - _ => (), //other usages irrelevant - } } - } - if !report_data.relevant_fields.is_empty() { - self.input_reports.insert(report_data.report_id, report_data); - } + if !self.input_reports.is_empty() { + Ok(()) + } else { + Err(efi::Status::UNSUPPORTED) + } } - if !self.input_reports.is_empty() { - Ok(()) - } else { - Err(efi::Status::UNSUPPORTED) - } - } - - // Helper routine that handles projecting relative and absolute axis reports onto the fixed - // absolute report axis that this driver produces. - fn resolve_axis(current_value: u64, field: VariableField, report: &[u8]) -> Option { - if field.attributes.relative { - //for relative, just update and clamp the current state. - let new_value = current_value as i64 + field.field_value(report)?; - Some(new_value.clamp(0, AXIS_RESOLUTION as i64) as u64) - } else { - //for absolute, project onto 0..AXIS_RESOLUTION - let mut new_value = field.field_value(report)?; - - //translate to zero. - new_value = new_value.checked_sub(i32::from(field.logical_minimum) as i64)?; - - //scale to AXIS_RESOLUTION - new_value = (new_value * AXIS_RESOLUTION as i64 * 1000) / (field.field_range()? as i64 * 1000); - - Some(new_value.clamp(0, AXIS_RESOLUTION as i64) as u64) + // Helper routine that handles projecting relative and absolute axis reports onto the fixed + // absolute report axis that this driver produces. + fn resolve_axis(current_value: u64, field: VariableField, report: &[u8]) -> Option { + if field.attributes.relative { + //for relative, just update and clamp the current state. + let new_value = current_value as i64 + field.field_value(report)?; + Some(new_value.clamp(0, AXIS_RESOLUTION as i64) as u64) + } else { + //for absolute, project onto 0..AXIS_RESOLUTION + let mut new_value = field.field_value(report)?; + + //translate to zero. + new_value = new_value.checked_sub(i32::from(field.logical_minimum) as i64)?; + + //scale to AXIS_RESOLUTION + new_value = (new_value * AXIS_RESOLUTION as i64 * 1000) / (field.field_range()? as i64 * 1000); + + Some(new_value.clamp(0, AXIS_RESOLUTION as i64) as u64) + } } - } - - // handles x_axis inputs - fn x_axis_handler(&mut self, field: VariableField, report: &[u8]) { - if let Some(x_value) = Self::resolve_axis(self.current_state.current_x, field, report) { - if self.current_state.current_x != x_value { - self.current_state.current_x = x_value; - self.state_changed = true; - } + + // handles x_axis inputs + fn x_axis_handler(&mut self, field: VariableField, report: &[u8]) { + if let Some(x_value) = Self::resolve_axis(self.current_state.current_x, field, report) { + if self.current_state.current_x != x_value { + self.current_state.current_x = x_value; + self.state_changed = true; + } + } } - } - - // handles y_axis inputs - fn y_axis_handler(&mut self, field: VariableField, report: &[u8]) { - if let Some(y_value) = Self::resolve_axis(self.current_state.current_y, field, report) { - if self.current_state.current_y != y_value { - self.current_state.current_y = y_value; - self.state_changed = true; - } + + // handles y_axis inputs + fn y_axis_handler(&mut self, field: VariableField, report: &[u8]) { + if let Some(y_value) = Self::resolve_axis(self.current_state.current_y, field, report) { + if self.current_state.current_y != y_value { + self.current_state.current_y = y_value; + self.state_changed = true; + } + } } - } - - // handles z_axis inputs - fn z_axis_handler(&mut self, field: VariableField, report: &[u8]) { - if let Some(z_value) = Self::resolve_axis(self.current_state.current_z, field, report) { - if self.current_state.current_z != z_value { - self.current_state.current_z = z_value; - self.state_changed = true; - } + + // handles z_axis inputs + fn z_axis_handler(&mut self, field: VariableField, report: &[u8]) { + if let Some(z_value) = Self::resolve_axis(self.current_state.current_z, field, report) { + if self.current_state.current_z != z_value { + self.current_state.current_z = z_value; + self.state_changed = true; + } + } } - } - - // handles button inputs - fn button_handler(&mut self, field: VariableField, report: &[u8]) { - let shift = match field.usage.into() { - x @ BUTTON_MIN..=BUTTON_MAX => x - BUTTON_MIN, - x @ DIGITIZER_SWITCH_MIN..=DIGITIZER_SWITCH_MAX => x - DIGITIZER_SWITCH_MIN, - _ => return, - }; - if let Some(button_value) = field.field_value(report) { - let button_value = button_value as u32; + // handles button inputs + fn button_handler(&mut self, field: VariableField, report: &[u8]) { + let shift = match field.usage.into() { + x @ BUTTON_MIN..=BUTTON_MAX => x - BUTTON_MIN, + x @ DIGITIZER_SWITCH_MIN..=DIGITIZER_SWITCH_MAX => x - DIGITIZER_SWITCH_MIN, + _ => return, + }; - if shift > u32::BITS { - return; - } - let button_value = button_value << shift; + if let Some(button_value) = field.field_value(report) { + let button_value = button_value as u32; + + if shift > u32::BITS { + return; + } + let button_value = button_value << shift; - let new_buttons = self.current_state.active_buttons + let new_buttons = self.current_state.active_buttons & !(1 << shift) // zero the relevant bit in the button state field. | button_value; // or in the current button state into that bit position. - if new_buttons != self.current_state.active_buttons { - self.current_state.active_buttons = new_buttons; - self.state_changed = true; - } + if new_buttons != self.current_state.active_buttons { + self.current_state.active_buttons = new_buttons; + self.state_changed = true; + } + } + } + + fn reset_state(&mut self) { + self.current_state = Default::default(); + // initialize pointer to center of screen + self.current_state.current_x = CENTER; + self.current_state.current_y = CENTER; + self.state_changed = false; } - } - - fn reset_state(&mut self) { - self.current_state = Default::default(); - // initialize pointer to center of screen - self.current_state.current_x = CENTER; - self.current_state.current_y = CENTER; - self.state_changed = false; - } } impl HidReportReceiver for PointerHidHandler { - fn initialize(&mut self, controller: efi::Handle, hid_io: &dyn HidIo) -> Result<(), efi::Status> { - let descriptor = hid_io.get_report_descriptor()?; - self.process_descriptor(descriptor)?; - - PointerContext::install(self.boot_services, controller, self)?; - - self.controller = Some(controller); - - Ok(()) - } - fn receive_report(&mut self, report: &[u8], _hid_io: &dyn HidIo) { - let old_tpl = self.boot_services.raise_tpl(efi::TPL_NOTIFY); - - 'report_processing: { - if report.is_empty() { - break 'report_processing; - } - - // determine whether report includes report id byte and adjust the buffer as needed. - let (report_id, report) = match self.report_id_present { - true => (Some(ReportId::from(&report[0..1])), &report[1..]), - false => (None, &report[0..]), - }; - - if report.is_empty() { - break 'report_processing; - } - - if let Some(report_data) = self.input_reports.get(&report_id).cloned() { - if report.len() != report_data.report_size { - //Some devices report extra bytes in their reports. Warn about this, but try and process anyway. - debugln!( - DEBUG_VERBOSE, - "{:?}:{:?} unexpected report length for report_id: {:?}. expected {:?}, actual {:?}", - function!(), - line!(), - report_id, - report_data.report_size, - report.len() - ); - debugln!(DEBUG_VERBOSE, "report: {:x?}", report); - //break 'report_processing; - } + fn initialize(&mut self, controller: efi::Handle, hid_io: &dyn HidIo) -> Result<(), efi::Status> { + let descriptor = hid_io.get_report_descriptor()?; + self.process_descriptor(descriptor)?; - // hand the report data to the handler for each relevant field for field-specific processing. - for field in report_data.relevant_fields { - (field.report_handler)(self, field.field, report); - } - } + PointerContext::install(self.boot_services, controller, self)?; + + self.controller = Some(controller); + + Ok(()) } + fn receive_report(&mut self, report: &[u8], _hid_io: &dyn HidIo) { + let old_tpl = self.boot_services.raise_tpl(efi::TPL_NOTIFY); + + 'report_processing: { + if report.is_empty() { + break 'report_processing; + } + + // determine whether report includes report id byte and adjust the buffer as needed. + let (report_id, report) = match self.report_id_present { + true => (Some(ReportId::from(&report[0..1])), &report[1..]), + false => (None, &report[0..]), + }; - self.boot_services.restore_tpl(old_tpl); - } + if report.is_empty() { + break 'report_processing; + } + + if let Some(report_data) = self.input_reports.get(&report_id).cloned() { + if report.len() != report_data.report_size { + //Some devices report extra bytes in their reports. Warn about this, but try and process anyway. + debugln!( + DEBUG_VERBOSE, + "{:?}:{:?} unexpected report length for report_id: {:?}. expected {:?}, actual {:?}", + function!(), + line!(), + report_id, + report_data.report_size, + report.len() + ); + debugln!(DEBUG_VERBOSE, "report: {:x?}", report); + //break 'report_processing; + } + + // hand the report data to the handler for each relevant field for field-specific processing. + for field in report_data.relevant_fields { + (field.report_handler)(self, field.field, report); + } + } + } + + self.boot_services.restore_tpl(old_tpl); + } } impl Drop for PointerHidHandler { - fn drop(&mut self) { - if let Some(controller) = self.controller { - let status = PointerContext::uninstall(self.boot_services, self.agent, controller); - if status.is_err() { - debugln!(DEBUG_ERROR, "{:?}: Failed to uninstall absolute_pointer: {:?}", function!(), status); - } + fn drop(&mut self) { + if let Some(controller) = self.controller { + let status = PointerContext::uninstall(self.boot_services, self.agent, controller); + if status.is_err() { + debugln!(DEBUG_ERROR, "{:?}: Failed to uninstall absolute_pointer: {:?}", function!(), status); + } + } } - } } #[cfg(test)] mod test { - use core::{cmp::min, ffi::c_void}; - - use crate::{ - boot_services::MockUefiBootServices, - hid_io::{HidReportReceiver, MockHidIo}, - pointer::{AXIS_RESOLUTION, CENTER}, - }; - use r_efi::efi; - - use super::PointerHidHandler; - - static MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[ - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x06, // USAGE (Keyboard) - 0xa1, 0x01, // COLLECTION (Application) - 0x75, 0x01, // REPORT_SIZE (1) - 0x95, 0x08, // REPORT_COUNT (8) - 0x05, 0x07, // USAGE_PAGE (Key Codes) - 0x19, 0xE0, // USAGE_MINIMUM (224) - 0x29, 0xE7, // USAGE_MAXIMUM (231) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x25, 0x01, // LOGICAL_MAXIMUM (1) - 0x81, 0x02, // INPUT (Data, Var, Abs) (Modifier Byte) - 0xc0, // END_COLLECTION - ]; - - static MOUSE_REPORT_DESCRIPTOR: &[u8] = &[ - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x02, // USAGE (Mouse) - 0xa1, 0x01, // COLLECTION (Application) - 0x09, 0x01, // USAGE(Pointer) - 0xa1, 0x00, // COLLECTION (Physical) - 0x05, 0x09, // USAGE_PAGE (Button) - 0x19, 0x01, // USAGE_MINIMUM(1) - 0x29, 0x05, // USAGE_MAXIMUM(5) - 0x15, 0x00, // LOGICAL_MINIMUM(0) - 0x25, 0x01, // LOGICAL_MAXIMUM(1) - 0x95, 0x05, // REPORT_COUNT(5) - 0x75, 0x01, // REPORT_SIZE(1) - 0x81, 0x02, // INPUT(Data, Variable, Absolute) - 0x95, 0x01, // REPORT_COUNT(1) - 0x75, 0x03, // REPORT_SIZE(3) - 0x81, 0x01, // INPUT(Constant, Array, Absolute) - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x30, // USAGE (X) - 0x09, 0x31, // USAGE (Y) - 0x09, 0x38, // USAGE (Wheel) - 0x15, 0x81, // LOGICAL_MINIMUM (-127) - 0x25, 0x7f, // LOGICAL_MAXIMUM (127) - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x03, // REPORT_COUNT (3) - 0x81, 0x06, // INPUT(Data, Variable, Relative) - 0xc0, // END_COLLECTION - 0xc0, // END_COLLECTION - ]; - - static ABS_POINTER_REPORT_DESCRIPTOR: &[u8] = &[ - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x02, // USAGE (Mouse) - 0xa1, 0x01, // COLLECTION (Application) - 0x09, 0x01, // USAGE(Pointer) - 0xa1, 0x00, // COLLECTION (Physical) - 0x05, 0x09, // USAGE_PAGE (Button) - 0x19, 0x01, // USAGE_MINIMUM(1) - 0x29, 0x05, // USAGE_MAXIMUM(5) - 0x15, 0x00, // LOGICAL_MINIMUM(0) - 0x25, 0x01, // LOGICAL_MAXIMUM(1) - 0x95, 0x05, // REPORT_COUNT(5) - 0x75, 0x01, // REPORT_SIZE(1) - 0x81, 0x02, // INPUT(Data, Variable, Absolute) - 0x95, 0x01, // REPORT_COUNT(1) - 0x75, 0x03, // REPORT_SIZE(3) - 0x81, 0x01, // INPUT(Constant, Array, Absolute) - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x30, // USAGE (X) - 0x09, 0x31, // USAGE (Y) - 0x09, 0x38, // USAGE (Wheel) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0xff, 0x0f, // LOGICAL_MAXIMUM (4095) - 0x75, 0x10, // REPORT_SIZE (16) - 0x95, 0x03, // REPORT_COUNT (3) - 0x81, 0x02, // INPUT(Data, Variable, Absolute) - 0xc0, // END_COLLECTION - 0xc0, // END_COLLECTION - ]; - - // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used - // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock - // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. - // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. - // This object needs to outlive anything that uses it - once created, it will live until the end of the program. - fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { - unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } - } - - #[test] - fn pointer_initialize_should_fail_if_report_descriptor_not_supported() { - let boot_services = create_fake_static_boot_service(); - - let agent = 0x1 as efi::Handle; - let mut pointer_handler = PointerHidHandler::new(boot_services, agent); - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); - - let controller = 0x2 as efi::Handle; - assert_eq!(pointer_handler.initialize(controller, &hid_io), Err(efi::Status::UNSUPPORTED)); - } - - #[test] - fn successful_pointer_initialize_should_install_protocol_and_drop_should_tear_it_down() { - let boot_services = create_fake_static_boot_service(); - static mut ABS_PTR_INTERFACE: *mut c_void = core::ptr::null_mut(); - - // expected on PointerHidHandler::initialize(). - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - unsafe { ABS_PTR_INTERFACE = interface }; - efi::Status::SUCCESS - }); - - // expected on PointerHidHandler::drop(). - boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { - unsafe { *interface = ABS_PTR_INTERFACE }; - efi::Status::SUCCESS - }); - boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); - boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); - - let agent = 0x1 as efi::Handle; - let mut pointer_handler = PointerHidHandler::new(boot_services, agent); - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&MOUSE_REPORT_DESCRIPTOR).unwrap())); - - let controller = 0x2 as efi::Handle; - assert_eq!(pointer_handler.initialize(controller, &hid_io), Ok(())); - - assert_ne!(unsafe { ABS_PTR_INTERFACE }, core::ptr::null_mut()); - } - - #[test] - fn receive_report_should_process_relative_reports() { - let boot_services = create_fake_static_boot_service(); - - static mut ABS_PTR_INTERFACE: *mut c_void = core::ptr::null_mut(); - - // expected on PointerHidHandler::initialize(). - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - unsafe { ABS_PTR_INTERFACE = interface }; - efi::Status::SUCCESS - }); - - // expected on PointerHidHandler::drop(). - boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { - unsafe { *interface = ABS_PTR_INTERFACE }; - efi::Status::SUCCESS - }); - boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); - boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); - - // expected on PointerHidHandler::receive_report - boot_services.expect_raise_tpl().returning(|new_tpl| { - assert_eq!(new_tpl, efi::TPL_NOTIFY); - efi::TPL_APPLICATION - }); - - boot_services.expect_restore_tpl().returning(|new_tpl| { - assert_eq!(new_tpl, efi::TPL_APPLICATION); - () - }); - - let agent = 0x1 as efi::Handle; - let mut pointer_handler = PointerHidHandler::new(boot_services, agent); - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&MOUSE_REPORT_DESCRIPTOR).unwrap())); - - let controller = 0x2 as efi::Handle; - assert_eq!(pointer_handler.initialize(controller, &hid_io), Ok(())); - - assert_eq!(pointer_handler.current_state.active_buttons, 0); - assert_eq!(pointer_handler.current_state.current_x, CENTER); - assert_eq!(pointer_handler.current_state.current_y, CENTER); - assert_eq!(pointer_handler.current_state.current_z, 0); - assert_eq!(pointer_handler.state_changed, false); - - //click two buttons and move the cursor (+32,+32) - let report: &[u8] = &[0x05, 0x20, 0x20, 0]; - pointer_handler.receive_report(report, &hid_io); - - assert_eq!(pointer_handler.current_state.active_buttons, 0x05); - assert_eq!(pointer_handler.current_state.current_x, CENTER + 32); - assert_eq!(pointer_handler.current_state.current_y, CENTER + 32); - assert_eq!(pointer_handler.current_state.current_z, 0); - assert_eq!(pointer_handler.state_changed, true); - - //un-click and move the cursor (+32,-16) and wheel(+32). - let report: &[u8] = &[0x00, 0x20, 0xF0, 0x20]; //0xF0 = -16. - pointer_handler.receive_report(report, &hid_io); - - assert_eq!(pointer_handler.current_state.active_buttons, 0); - assert_eq!(pointer_handler.current_state.current_x, CENTER + 64); - assert_eq!(pointer_handler.current_state.current_y, CENTER + 16); - assert_eq!(pointer_handler.current_state.current_z, 32); - - //un-click and move the cursor (+32,-32) and wheel(+32). - let report: &[u8] = &[0x00, 0x20, 0xE0, 0x20]; //0xE0 = -32. - pointer_handler.receive_report(report, &hid_io); - - assert_eq!(pointer_handler.current_state.active_buttons, 0); - assert_eq!(pointer_handler.current_state.current_x, CENTER + 96); - assert_eq!(pointer_handler.current_state.current_y, CENTER - 16); - assert_eq!(pointer_handler.current_state.current_z, 64); - - //move the cursor (0,127) until is past saturation, and check the value each time - let report: &[u8] = &[0x00, 0x00, 0x7F, 0x0]; //0x7F = +127. - let starting_y = pointer_handler.current_state.current_y; - for i in 0..AXIS_RESOLUTION { - //starts near the center, so moving it well past saturation point - pointer_handler.receive_report(report, &hid_io); - let expected_y = min(AXIS_RESOLUTION, starting_y.saturating_add((i + 1) * 127)); - assert_eq!(pointer_handler.current_state.current_y, expected_y); + use core::{cmp::min, ffi::c_void}; + + use crate::{ + boot_services::MockUefiBootServices, + hid_io::{HidReportReceiver, MockHidIo}, + pointer::{AXIS_RESOLUTION, CENTER}, + }; + use r_efi::efi; + + use super::PointerHidHandler; + + static MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x06, // USAGE (Keyboard) + 0xa1, 0x01, // COLLECTION (Application) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x08, // REPORT_COUNT (8) + 0x05, 0x07, // USAGE_PAGE (Key Codes) + 0x19, 0xE0, // USAGE_MINIMUM (224) + 0x29, 0xE7, // USAGE_MAXIMUM (231) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x81, 0x02, // INPUT (Data, Var, Abs) (Modifier Byte) + 0xc0, // END_COLLECTION + ]; + + static MOUSE_REPORT_DESCRIPTOR: &[u8] = &[ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x02, // USAGE (Mouse) + 0xa1, 0x01, // COLLECTION (Application) + 0x09, 0x01, // USAGE(Pointer) + 0xa1, 0x00, // COLLECTION (Physical) + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM(1) + 0x29, 0x05, // USAGE_MAXIMUM(5) + 0x15, 0x00, // LOGICAL_MINIMUM(0) + 0x25, 0x01, // LOGICAL_MAXIMUM(1) + 0x95, 0x05, // REPORT_COUNT(5) + 0x75, 0x01, // REPORT_SIZE(1) + 0x81, 0x02, // INPUT(Data, Variable, Absolute) + 0x95, 0x01, // REPORT_COUNT(1) + 0x75, 0x03, // REPORT_SIZE(3) + 0x81, 0x01, // INPUT(Constant, Array, Absolute) + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x09, 0x38, // USAGE (Wheel) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x03, // REPORT_COUNT (3) + 0x81, 0x06, // INPUT(Data, Variable, Relative) + 0xc0, // END_COLLECTION + 0xc0, // END_COLLECTION + ]; + + static ABS_POINTER_REPORT_DESCRIPTOR: &[u8] = &[ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x02, // USAGE (Mouse) + 0xa1, 0x01, // COLLECTION (Application) + 0x09, 0x01, // USAGE(Pointer) + 0xa1, 0x00, // COLLECTION (Physical) + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM(1) + 0x29, 0x05, // USAGE_MAXIMUM(5) + 0x15, 0x00, // LOGICAL_MINIMUM(0) + 0x25, 0x01, // LOGICAL_MAXIMUM(1) + 0x95, 0x05, // REPORT_COUNT(5) + 0x75, 0x01, // REPORT_SIZE(1) + 0x81, 0x02, // INPUT(Data, Variable, Absolute) + 0x95, 0x01, // REPORT_COUNT(1) + 0x75, 0x03, // REPORT_SIZE(3) + 0x81, 0x01, // INPUT(Constant, Array, Absolute) + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x09, 0x38, // USAGE (Wheel) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26, 0xff, 0x0f, // LOGICAL_MAXIMUM (4095) + 0x75, 0x10, // REPORT_SIZE (16) + 0x95, 0x03, // REPORT_COUNT (3) + 0x81, 0x02, // INPUT(Data, Variable, Absolute) + 0xc0, // END_COLLECTION + 0xc0, // END_COLLECTION + ]; + + // In this module, the usage model for boot_services is global static, and so &'static dyn UefiBootServices is used + // throughout the API. For testing, each test will have a different set of expectations on the UefiBootServices mock + // object, and the mock object itself expects to be "mut", which makes it hard to handle as a single global static. + // Instead, raw pointers are used to simulate a MockUefiBootServices instance with 'static lifetime. + // This object needs to outlive anything that uses it - once created, it will live until the end of the program. + fn create_fake_static_boot_service() -> &'static mut MockUefiBootServices { + unsafe { Box::into_raw(Box::new(MockUefiBootServices::new())).as_mut().unwrap() } + } + + #[test] + fn pointer_initialize_should_fail_if_report_descriptor_not_supported() { + let boot_services = create_fake_static_boot_service(); + + let agent = 0x1 as efi::Handle; + let mut pointer_handler = PointerHidHandler::new(boot_services, agent); + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&MINIMAL_BOOT_KEYBOARD_REPORT_DESCRIPTOR).unwrap())); + + let controller = 0x2 as efi::Handle; + assert_eq!(pointer_handler.initialize(controller, &hid_io), Err(efi::Status::UNSUPPORTED)); + } + + #[test] + fn successful_pointer_initialize_should_install_protocol_and_drop_should_tear_it_down() { + let boot_services = create_fake_static_boot_service(); + static mut ABS_PTR_INTERFACE: *mut c_void = core::ptr::null_mut(); + + // expected on PointerHidHandler::initialize(). + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + unsafe { ABS_PTR_INTERFACE = interface }; + efi::Status::SUCCESS + }); + + // expected on PointerHidHandler::drop(). + boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { + unsafe { *interface = ABS_PTR_INTERFACE }; + efi::Status::SUCCESS + }); + boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); + boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); + + let agent = 0x1 as efi::Handle; + let mut pointer_handler = PointerHidHandler::new(boot_services, agent); + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&MOUSE_REPORT_DESCRIPTOR).unwrap())); + + let controller = 0x2 as efi::Handle; + assert_eq!(pointer_handler.initialize(controller, &hid_io), Ok(())); + + assert_ne!(unsafe { ABS_PTR_INTERFACE }, core::ptr::null_mut()); + } + + #[test] + fn receive_report_should_process_relative_reports() { + let boot_services = create_fake_static_boot_service(); + + static mut ABS_PTR_INTERFACE: *mut c_void = core::ptr::null_mut(); + + // expected on PointerHidHandler::initialize(). + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + unsafe { ABS_PTR_INTERFACE = interface }; + efi::Status::SUCCESS + }); + + // expected on PointerHidHandler::drop(). + boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { + unsafe { *interface = ABS_PTR_INTERFACE }; + efi::Status::SUCCESS + }); + boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); + boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); + + // expected on PointerHidHandler::receive_report + boot_services.expect_raise_tpl().returning(|new_tpl| { + assert_eq!(new_tpl, efi::TPL_NOTIFY); + efi::TPL_APPLICATION + }); + + boot_services.expect_restore_tpl().returning(|new_tpl| { + assert_eq!(new_tpl, efi::TPL_APPLICATION); + () + }); + + let agent = 0x1 as efi::Handle; + let mut pointer_handler = PointerHidHandler::new(boot_services, agent); + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&MOUSE_REPORT_DESCRIPTOR).unwrap())); + + let controller = 0x2 as efi::Handle; + assert_eq!(pointer_handler.initialize(controller, &hid_io), Ok(())); + + assert_eq!(pointer_handler.current_state.active_buttons, 0); + assert_eq!(pointer_handler.current_state.current_x, CENTER); + assert_eq!(pointer_handler.current_state.current_y, CENTER); + assert_eq!(pointer_handler.current_state.current_z, 0); + assert_eq!(pointer_handler.state_changed, false); + + //click two buttons and move the cursor (+32,+32) + let report: &[u8] = &[0x05, 0x20, 0x20, 0]; + pointer_handler.receive_report(report, &hid_io); + + assert_eq!(pointer_handler.current_state.active_buttons, 0x05); + assert_eq!(pointer_handler.current_state.current_x, CENTER + 32); + assert_eq!(pointer_handler.current_state.current_y, CENTER + 32); + assert_eq!(pointer_handler.current_state.current_z, 0); + assert_eq!(pointer_handler.state_changed, true); + + //un-click and move the cursor (+32,-16) and wheel(+32). + let report: &[u8] = &[0x00, 0x20, 0xF0, 0x20]; //0xF0 = -16. + pointer_handler.receive_report(report, &hid_io); + + assert_eq!(pointer_handler.current_state.active_buttons, 0); + assert_eq!(pointer_handler.current_state.current_x, CENTER + 64); + assert_eq!(pointer_handler.current_state.current_y, CENTER + 16); + assert_eq!(pointer_handler.current_state.current_z, 32); + + //un-click and move the cursor (+32,-32) and wheel(+32). + let report: &[u8] = &[0x00, 0x20, 0xE0, 0x20]; //0xE0 = -32. + pointer_handler.receive_report(report, &hid_io); + + assert_eq!(pointer_handler.current_state.active_buttons, 0); + assert_eq!(pointer_handler.current_state.current_x, CENTER + 96); + assert_eq!(pointer_handler.current_state.current_y, CENTER - 16); + assert_eq!(pointer_handler.current_state.current_z, 64); + + //move the cursor (0,127) until is past saturation, and check the value each time + let report: &[u8] = &[0x00, 0x00, 0x7F, 0x0]; //0x7F = +127. + let starting_y = pointer_handler.current_state.current_y; + for i in 0..AXIS_RESOLUTION { + //starts near the center, so moving it well past saturation point + pointer_handler.receive_report(report, &hid_io); + let expected_y = min(AXIS_RESOLUTION, starting_y.saturating_add((i + 1) * 127)); + assert_eq!(pointer_handler.current_state.current_y, expected_y); + } + + //move the cursor(0,-127) until it saturates at zero, and check the value each time. + let report: &[u8] = &[0x00, 0x00, 0x81, 0x0]; //0x80 = -127. + let starting_y = pointer_handler.current_state.current_y; + for i in 0..AXIS_RESOLUTION { + // starts at max, but moving 127 each time, so well past saturation point. + pointer_handler.receive_report(report, &hid_io); + let expected_y = starting_y.saturating_sub((i + 1) * 127); + assert_eq!(pointer_handler.current_state.current_y, expected_y); + } + } + + #[test] + fn receive_report_should_process_absolute_reports() { + let boot_services = create_fake_static_boot_service(); + static mut ABS_PTR_INTERFACE: *mut c_void = core::ptr::null_mut(); + + // expected on PointerHidHandler::initialize(). + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + unsafe { ABS_PTR_INTERFACE = interface }; + efi::Status::SUCCESS + }); + + // expected on PointerHidHandler::drop(). + boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { + unsafe { *interface = ABS_PTR_INTERFACE }; + efi::Status::SUCCESS + }); + boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); + boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); + + // expected on PointerHidHandler::receive_report + boot_services.expect_raise_tpl().returning(|new_tpl| { + assert_eq!(new_tpl, efi::TPL_NOTIFY); + efi::TPL_APPLICATION + }); + + boot_services.expect_restore_tpl().returning(|new_tpl| { + assert_eq!(new_tpl, efi::TPL_APPLICATION); + () + }); + + let agent = 0x1 as efi::Handle; + let mut pointer_handler = PointerHidHandler::new(boot_services, agent); + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&ABS_POINTER_REPORT_DESCRIPTOR).unwrap())); + + let controller = 0x2 as efi::Handle; + assert_eq!(pointer_handler.initialize(controller, &hid_io), Ok(())); + + assert_eq!(pointer_handler.current_state.active_buttons, 0); + assert_eq!(pointer_handler.current_state.current_x, CENTER); + assert_eq!(pointer_handler.current_state.current_y, CENTER); + assert_eq!(pointer_handler.current_state.current_z, 0); + assert_eq!(pointer_handler.state_changed, false); + + //click two buttons and move the cursor (1024, 1024). + let report: &[u8] = &[0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00]; + pointer_handler.receive_report(report, &hid_io); + + assert_eq!(pointer_handler.current_state.active_buttons, 0x05); + // input x from range 0-4095 projected on to 0-1024 axis is (x/4095) * 1024. For x=1024, result is 256. + assert_eq!(pointer_handler.current_state.current_x, 256); + assert_eq!(pointer_handler.current_state.current_y, 256); + assert_eq!(pointer_handler.current_state.current_z, 0); + assert_eq!(pointer_handler.state_changed, true); } - //move the cursor(0,-127) until it saturates at zero, and check the value each time. - let report: &[u8] = &[0x00, 0x00, 0x81, 0x0]; //0x80 = -127. - let starting_y = pointer_handler.current_state.current_y; - for i in 0..AXIS_RESOLUTION { - // starts at max, but moving 127 each time, so well past saturation point. - pointer_handler.receive_report(report, &hid_io); - let expected_y = starting_y.saturating_sub((i + 1) * 127); - assert_eq!(pointer_handler.current_state.current_y, expected_y); + #[test] + fn bad_reports_should_be_processed_with_best_effort() { + let boot_services = create_fake_static_boot_service(); + static mut ABS_PTR_INTERFACE: *mut c_void = core::ptr::null_mut(); + + // expected on PointerHidHandler::initialize(). + boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); + boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { + unsafe { ABS_PTR_INTERFACE = interface }; + efi::Status::SUCCESS + }); + + // expected on PointerHidHandler::drop(). + boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { + unsafe { *interface = ABS_PTR_INTERFACE }; + efi::Status::SUCCESS + }); + boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); + boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); + + // expected on PointerHidHandler::receive_report + boot_services.expect_raise_tpl().returning(|new_tpl| { + assert_eq!(new_tpl, efi::TPL_NOTIFY); + efi::TPL_APPLICATION + }); + + boot_services.expect_restore_tpl().returning(|new_tpl| { + assert_eq!(new_tpl, efi::TPL_APPLICATION); + () + }); + + let agent = 0x1 as efi::Handle; + let mut pointer_handler = PointerHidHandler::new(boot_services, agent); + let mut hid_io = MockHidIo::new(); + hid_io + .expect_get_report_descriptor() + .returning(|| Ok(hidparser::parse_report_descriptor(&ABS_POINTER_REPORT_DESCRIPTOR).unwrap())); + + let controller = 0x2 as efi::Handle; + assert_eq!(pointer_handler.initialize(controller, &hid_io), Ok(())); + + assert_eq!(pointer_handler.current_state.active_buttons, 0); + assert_eq!(pointer_handler.current_state.current_x, CENTER); + assert_eq!(pointer_handler.current_state.current_y, CENTER); + assert_eq!(pointer_handler.current_state.current_z, 0); + assert_eq!(pointer_handler.state_changed, false); + + //move the cursor (4096, 4096, 0) - changed fields are out of range + let report: &[u8] = &[0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00]; + pointer_handler.receive_report(report, &hid_io); + + assert_eq!(pointer_handler.current_state.active_buttons, 0); + assert_eq!(pointer_handler.current_state.current_x, CENTER); + assert_eq!(pointer_handler.current_state.current_y, CENTER); + assert_eq!(pointer_handler.current_state.current_z, 0); + assert_eq!(pointer_handler.state_changed, false); + + //report too long + let report: &[u8] = &[0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10]; + pointer_handler.receive_report(report, &hid_io); + + assert_eq!(pointer_handler.current_state.active_buttons, 0); + assert_eq!(pointer_handler.current_state.current_x, 0); + assert_eq!(pointer_handler.current_state.current_y, 4); + assert_eq!(pointer_handler.current_state.current_z, 4); + assert_eq!(pointer_handler.state_changed, true); + + //report too short + let report: &[u8] = &[0x00, 0x10, 0x00, 0x10, 0x00, 0x10]; + pointer_handler.receive_report(report, &hid_io); + + assert_eq!(pointer_handler.current_state.active_buttons, 0); + assert_eq!(pointer_handler.current_state.current_x, 4); + assert_eq!(pointer_handler.current_state.current_y, 4); + assert_eq!(pointer_handler.current_state.current_z, 4); + assert_eq!(pointer_handler.state_changed, true); } - } - - #[test] - fn receive_report_should_process_absolute_reports() { - let boot_services = create_fake_static_boot_service(); - static mut ABS_PTR_INTERFACE: *mut c_void = core::ptr::null_mut(); - - // expected on PointerHidHandler::initialize(). - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - unsafe { ABS_PTR_INTERFACE = interface }; - efi::Status::SUCCESS - }); - - // expected on PointerHidHandler::drop(). - boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { - unsafe { *interface = ABS_PTR_INTERFACE }; - efi::Status::SUCCESS - }); - boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); - boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); - - // expected on PointerHidHandler::receive_report - boot_services.expect_raise_tpl().returning(|new_tpl| { - assert_eq!(new_tpl, efi::TPL_NOTIFY); - efi::TPL_APPLICATION - }); - - boot_services.expect_restore_tpl().returning(|new_tpl| { - assert_eq!(new_tpl, efi::TPL_APPLICATION); - () - }); - - let agent = 0x1 as efi::Handle; - let mut pointer_handler = PointerHidHandler::new(boot_services, agent); - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&ABS_POINTER_REPORT_DESCRIPTOR).unwrap())); - - let controller = 0x2 as efi::Handle; - assert_eq!(pointer_handler.initialize(controller, &hid_io), Ok(())); - - assert_eq!(pointer_handler.current_state.active_buttons, 0); - assert_eq!(pointer_handler.current_state.current_x, CENTER); - assert_eq!(pointer_handler.current_state.current_y, CENTER); - assert_eq!(pointer_handler.current_state.current_z, 0); - assert_eq!(pointer_handler.state_changed, false); - - //click two buttons and move the cursor (1024, 1024). - let report: &[u8] = &[0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00]; - pointer_handler.receive_report(report, &hid_io); - - assert_eq!(pointer_handler.current_state.active_buttons, 0x05); - // input x from range 0-4095 projected on to 0-1024 axis is (x/4095) * 1024. For x=1024, result is 256. - assert_eq!(pointer_handler.current_state.current_x, 256); - assert_eq!(pointer_handler.current_state.current_y, 256); - assert_eq!(pointer_handler.current_state.current_z, 0); - assert_eq!(pointer_handler.state_changed, true); - } - - #[test] - fn bad_reports_should_be_processed_with_best_effort() { - let boot_services = create_fake_static_boot_service(); - static mut ABS_PTR_INTERFACE: *mut c_void = core::ptr::null_mut(); - - // expected on PointerHidHandler::initialize(). - boot_services.expect_create_event().returning(|_, _, _, _, _| efi::Status::SUCCESS); - boot_services.expect_install_protocol_interface().returning(|_, _, _, interface| { - unsafe { ABS_PTR_INTERFACE = interface }; - efi::Status::SUCCESS - }); - - // expected on PointerHidHandler::drop(). - boot_services.expect_open_protocol().returning(|_, _, interface, _, _, _| { - unsafe { *interface = ABS_PTR_INTERFACE }; - efi::Status::SUCCESS - }); - boot_services.expect_uninstall_protocol_interface().returning(|_, _, _| efi::Status::SUCCESS); - boot_services.expect_close_event().returning(|_| efi::Status::SUCCESS); - - // expected on PointerHidHandler::receive_report - boot_services.expect_raise_tpl().returning(|new_tpl| { - assert_eq!(new_tpl, efi::TPL_NOTIFY); - efi::TPL_APPLICATION - }); - - boot_services.expect_restore_tpl().returning(|new_tpl| { - assert_eq!(new_tpl, efi::TPL_APPLICATION); - () - }); - - let agent = 0x1 as efi::Handle; - let mut pointer_handler = PointerHidHandler::new(boot_services, agent); - let mut hid_io = MockHidIo::new(); - hid_io - .expect_get_report_descriptor() - .returning(|| Ok(hidparser::parse_report_descriptor(&ABS_POINTER_REPORT_DESCRIPTOR).unwrap())); - - let controller = 0x2 as efi::Handle; - assert_eq!(pointer_handler.initialize(controller, &hid_io), Ok(())); - - assert_eq!(pointer_handler.current_state.active_buttons, 0); - assert_eq!(pointer_handler.current_state.current_x, CENTER); - assert_eq!(pointer_handler.current_state.current_y, CENTER); - assert_eq!(pointer_handler.current_state.current_z, 0); - assert_eq!(pointer_handler.state_changed, false); - - //move the cursor (4096, 4096, 0) - changed fields are out of range - let report: &[u8] = &[0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00]; - pointer_handler.receive_report(report, &hid_io); - - assert_eq!(pointer_handler.current_state.active_buttons, 0); - assert_eq!(pointer_handler.current_state.current_x, CENTER); - assert_eq!(pointer_handler.current_state.current_y, CENTER); - assert_eq!(pointer_handler.current_state.current_z, 0); - assert_eq!(pointer_handler.state_changed, false); - - //report too long - let report: &[u8] = &[0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10]; - pointer_handler.receive_report(report, &hid_io); - - assert_eq!(pointer_handler.current_state.active_buttons, 0); - assert_eq!(pointer_handler.current_state.current_x, 0); - assert_eq!(pointer_handler.current_state.current_y, 4); - assert_eq!(pointer_handler.current_state.current_z, 4); - assert_eq!(pointer_handler.state_changed, true); - - //report too short - let report: &[u8] = &[0x00, 0x10, 0x00, 0x10, 0x00, 0x10]; - pointer_handler.receive_report(report, &hid_io); - - assert_eq!(pointer_handler.current_state.active_buttons, 0); - assert_eq!(pointer_handler.current_state.current_x, 4); - assert_eq!(pointer_handler.current_state.current_y, 4); - assert_eq!(pointer_handler.current_state.current_z, 4); - assert_eq!(pointer_handler.state_changed, true); - } } diff --git a/Makefile.toml b/Makefile.toml index fdcf5643ff..c171fe2f3f 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -88,3 +88,9 @@ clear = true command = "cargo" args = ["tarpaulin", "@@split(INDIVIDUAL_PACKAGE_TARGETS, )", "@@split(COV_FLAGS, )", "--output-dir", "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target"] dependencies = ["individual-package-targets"] + +[tasks.clippy] +description = "Run cargo clippy." +clear = true +command = "cargo" +args = ["clippy", "--all-targets", "--", "-D", "warnings"] diff --git a/MsCorePkg/Crates/RustBootServicesAllocatorDxe/src/lib.rs b/MsCorePkg/Crates/RustBootServicesAllocatorDxe/src/lib.rs index 4455e3d626..f1d6579bea 100644 --- a/MsCorePkg/Crates/RustBootServicesAllocatorDxe/src/lib.rs +++ b/MsCorePkg/Crates/RustBootServicesAllocatorDxe/src/lib.rs @@ -32,9 +32,9 @@ #![feature(allocator_api)] use core::{ - alloc::{GlobalAlloc, Layout}, - ffi::c_void, - sync::atomic::AtomicPtr, + alloc::{GlobalAlloc, Layout}, + ffi::c_void, + sync::atomic::AtomicPtr, }; use r_efi::efi; @@ -47,115 +47,123 @@ const ALLOC_TRACKER_SIG: u32 = 0x706F6F6C; //arbitrary sig // Used to track allocations that need larger alignment than the UEFI Pool alignment (8 bytes). struct AllocationTracker { - signature: u32, - orig_ptr: *mut c_void, + signature: u32, + orig_ptr: *mut c_void, } /// Boot services allocator implementation. Must be initialized with a boot_services pointer before use, /// see [`BootServicesAllocator::init()`]. pub struct BootServicesAllocator { - boot_services: AtomicPtr, + boot_services: AtomicPtr, } impl BootServicesAllocator { - // Create a new instance. const fn to allow static initialization. - const fn new() -> Self { - BootServicesAllocator { boot_services: AtomicPtr::new(core::ptr::null_mut()) } - } - - // implement allocation using EFI boot services AllocatePool() call. - fn boot_services_alloc(&self, layout: Layout, boot_services: &efi::BootServices) -> *mut u8 { - match layout.align() { - 0..=8 => { - //allocate the pointer directly since UEFI pool allocations are 8-byte aligned already. - let mut ptr: *mut c_void = core::ptr::null_mut(); - match (boot_services.allocate_pool)(efi::BOOT_SERVICES_DATA, layout.size(), core::ptr::addr_of_mut!(ptr)) { - efi::Status::SUCCESS => ptr as *mut u8, - _ => core::ptr::null_mut(), - } - } - _ => { - //allocate extra space to align the allocation as requested and include a tracking structure to allow - //recovery of the original pointer for de-allocation. Tracking structure follows the allocation. - let (expanded_layout, tracking_offset) = match layout.extend(Layout::new::()) { - Ok(x) => x, - Err(_) => return core::ptr::null_mut(), - }; - let expanded_size = expanded_layout.size() + expanded_layout.align(); - - let mut orig_ptr: *mut c_void = core::ptr::null_mut(); - let final_ptr = match (boot_services.allocate_pool)( - efi::BOOT_SERVICES_DATA, - expanded_size, - core::ptr::addr_of_mut!(orig_ptr), - ) { - efi::Status::SUCCESS => orig_ptr as *mut u8, - _ => return core::ptr::null_mut(), - }; - - //align the pointer up to the required alignment. - let final_ptr = unsafe { final_ptr.add(final_ptr.align_offset(expanded_layout.align())) }; - - //get a reference to the allocation tracking structure after the allocation and populate it. - let tracker = unsafe { - final_ptr.add(tracking_offset).cast::().as_mut().expect("tracking pointer is invalid") - }; - - tracker.signature = ALLOC_TRACKER_SIG; - tracker.orig_ptr = orig_ptr; + // Create a new instance. const fn to allow static initialization. + const fn new() -> Self { + BootServicesAllocator { boot_services: AtomicPtr::new(core::ptr::null_mut()) } + } - final_ptr - } + // implement allocation using EFI boot services AllocatePool() call. + fn boot_services_alloc(&self, layout: Layout, boot_services: &efi::BootServices) -> *mut u8 { + match layout.align() { + 0..=8 => { + //allocate the pointer directly since UEFI pool allocations are 8-byte aligned already. + let mut ptr: *mut c_void = core::ptr::null_mut(); + match (boot_services.allocate_pool)( + efi::BOOT_SERVICES_DATA, + layout.size(), + core::ptr::addr_of_mut!(ptr), + ) { + efi::Status::SUCCESS => ptr as *mut u8, + _ => core::ptr::null_mut(), + } + } + _ => { + //allocate extra space to align the allocation as requested and include a tracking structure to allow + //recovery of the original pointer for de-allocation. Tracking structure follows the allocation. + let (expanded_layout, tracking_offset) = match layout.extend(Layout::new::()) { + Ok(x) => x, + Err(_) => return core::ptr::null_mut(), + }; + let expanded_size = expanded_layout.size() + expanded_layout.align(); + + let mut orig_ptr: *mut c_void = core::ptr::null_mut(); + let final_ptr = match (boot_services.allocate_pool)( + efi::BOOT_SERVICES_DATA, + expanded_size, + core::ptr::addr_of_mut!(orig_ptr), + ) { + efi::Status::SUCCESS => orig_ptr as *mut u8, + _ => return core::ptr::null_mut(), + }; + + //align the pointer up to the required alignment. + let final_ptr = unsafe { final_ptr.add(final_ptr.align_offset(expanded_layout.align())) }; + + //get a reference to the allocation tracking structure after the allocation and populate it. + let tracker = unsafe { + final_ptr + .add(tracking_offset) + .cast::() + .as_mut() + .expect("tracking pointer is invalid") + }; + + tracker.signature = ALLOC_TRACKER_SIG; + tracker.orig_ptr = orig_ptr; + + final_ptr + } + } } - } - - // implement dealloc (free) using EFI boot services FreePool() call. - fn boot_services_dealloc(&self, boot_services: &efi::BootServices, ptr: *mut u8, layout: Layout) { - match layout.align() { - 0..=8 => { - //pointer was allocated directly, so free it directly. - let _ = (boot_services.free_pool)(ptr as *mut c_void); - } - _ => { - //pointer was potentially adjusted for alignment. Recover tracking structure to retrieve the original - //pointer to free. - let (_, tracking_offset) = match layout.extend(Layout::new::()) { - Ok(x) => x, - Err(_) => return, - }; - let tracker = unsafe { - ptr.add(tracking_offset).cast::().as_mut().expect("tracking pointer is invalid") - }; - debug_assert_eq!(tracker.signature, ALLOC_TRACKER_SIG); - let _ = (boot_services.free_pool)(tracker.orig_ptr); - } + + // implement dealloc (free) using EFI boot services FreePool() call. + fn boot_services_dealloc(&self, boot_services: &efi::BootServices, ptr: *mut u8, layout: Layout) { + match layout.align() { + 0..=8 => { + //pointer was allocated directly, so free it directly. + let _ = (boot_services.free_pool)(ptr as *mut c_void); + } + _ => { + //pointer was potentially adjusted for alignment. Recover tracking structure to retrieve the original + //pointer to free. + let (_, tracking_offset) = match layout.extend(Layout::new::()) { + Ok(x) => x, + Err(_) => return, + }; + let tracker = unsafe { + ptr.add(tracking_offset).cast::().as_mut().expect("tracking pointer is invalid") + }; + debug_assert_eq!(tracker.signature, ALLOC_TRACKER_SIG); + let _ = (boot_services.free_pool)(tracker.orig_ptr); + } + } } - } - /// initializes the allocator instance with a pointer to the UEFI Boot Services table. - pub fn init(&self, boot_services: *mut efi::BootServices) { - self.boot_services.store(boot_services, core::sync::atomic::Ordering::SeqCst); - } + /// initializes the allocator instance with a pointer to the UEFI Boot Services table. + pub fn init(&self, boot_services: *mut efi::BootServices) { + self.boot_services.store(boot_services, core::sync::atomic::Ordering::SeqCst); + } } unsafe impl GlobalAlloc for BootServicesAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - let bs_ptr = self.boot_services.load(core::sync::atomic::Ordering::SeqCst); - if let Some(boot_services) = unsafe { bs_ptr.as_ref() } { - self.boot_services_alloc(layout, boot_services) - } else { - panic!("Attempted allocation on uninitialized allocator") + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let bs_ptr = self.boot_services.load(core::sync::atomic::Ordering::SeqCst); + if let Some(boot_services) = unsafe { bs_ptr.as_ref() } { + self.boot_services_alloc(layout, boot_services) + } else { + panic!("Attempted allocation on uninitialized allocator") + } } - } - - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - let bs_ptr = self.boot_services.load(core::sync::atomic::Ordering::SeqCst); - if let Some(boot_services) = unsafe { bs_ptr.as_ref() } { - self.boot_services_dealloc(boot_services, ptr, layout) - } else { - panic!("Attempted deallocation on uninitialized allocator") + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + let bs_ptr = self.boot_services.load(core::sync::atomic::Ordering::SeqCst); + if let Some(boot_services) = unsafe { bs_ptr.as_ref() } { + self.boot_services_dealloc(boot_services, ptr, layout) + } else { + panic!("Attempted deallocation on uninitialized allocator") + } } - } } unsafe impl Sync for BootServicesAllocator {} @@ -163,101 +171,102 @@ unsafe impl Send for BootServicesAllocator {} #[cfg(test)] mod tests { - extern crate std; + extern crate std; + + use core::{ + alloc::{GlobalAlloc, Layout}, + ffi::c_void, + mem::MaybeUninit, + }; + use std::alloc::System; + + use r_efi::efi; + use std::collections::BTreeMap; + + use crate::{AllocationTracker, BootServicesAllocator, ALLOC_TRACKER_SIG}; + + static ALLOCATION_TRACKER: spin::Mutex> = spin::Mutex::new(BTreeMap::new()); + + extern "efiapi" fn mock_allocate_pool( + pool_type: efi::MemoryType, + size: usize, + buffer: *mut *mut c_void, + ) -> efi::Status { + assert_eq!(pool_type, efi::BOOT_SERVICES_DATA); + + unsafe { + let layout = Layout::from_size_align(size, 8).unwrap(); + let ptr = System.alloc(layout) as *mut c_void; + buffer.write(ptr); + let existing_key = ALLOCATION_TRACKER.lock().insert(ptr as usize, layout); + assert!(existing_key.is_none()); + } - use core::{ - alloc::{GlobalAlloc, Layout}, - ffi::c_void, - mem::MaybeUninit, - }; - use std::alloc::System; - - use r_efi::efi; - use std::collections::BTreeMap; - - use crate::{AllocationTracker, BootServicesAllocator, ALLOC_TRACKER_SIG}; - - static ALLOCATION_TRACKER: spin::Mutex> = spin::Mutex::new(BTreeMap::new()); - - extern "efiapi" fn mock_allocate_pool( - pool_type: efi::MemoryType, - size: usize, - buffer: *mut *mut c_void, - ) -> efi::Status { - assert_eq!(pool_type, efi::BOOT_SERVICES_DATA); - - unsafe { - let layout = Layout::from_size_align(size, 8).unwrap(); - let ptr = System.alloc(layout) as *mut c_void; - buffer.write(ptr); - let existing_key = ALLOCATION_TRACKER.lock().insert(ptr as usize, layout); - assert!(existing_key.is_none()); + efi::Status::SUCCESS } - efi::Status::SUCCESS - } + extern "efiapi" fn mock_free_pool(buffer: *mut c_void) -> efi::Status { + let layout = ALLOCATION_TRACKER.lock().remove(&(buffer as usize)).expect("freeing an un-allocated pointer"); + unsafe { + System.dealloc(buffer as *mut u8, layout); + } - extern "efiapi" fn mock_free_pool(buffer: *mut c_void) -> efi::Status { - let layout = ALLOCATION_TRACKER.lock().remove(&(buffer as usize)).expect("freeing an un-allocated pointer"); - unsafe { - System.dealloc(buffer as *mut u8, layout); + efi::Status::SUCCESS } - efi::Status::SUCCESS - } - - extern "efiapi" fn mock_raise_tpl(_new_tpl: efi::Tpl) -> efi::Tpl { - efi::TPL_APPLICATION - } + extern "efiapi" fn mock_raise_tpl(_new_tpl: efi::Tpl) -> efi::Tpl { + efi::TPL_APPLICATION + } - extern "efiapi" fn mock_restore_tpl(_new_tpl: efi::Tpl) {} + extern "efiapi" fn mock_restore_tpl(_new_tpl: efi::Tpl) {} - fn mock_boot_services() -> efi::BootServices { - let boot_services = MaybeUninit::zeroed(); - let mut boot_services: efi::BootServices = unsafe { boot_services.assume_init() }; - boot_services.allocate_pool = mock_allocate_pool; - boot_services.free_pool = mock_free_pool; - boot_services.raise_tpl = mock_raise_tpl; - boot_services.restore_tpl = mock_restore_tpl; - boot_services - } + fn mock_boot_services() -> efi::BootServices { + let boot_services = MaybeUninit::zeroed(); + let mut boot_services: efi::BootServices = unsafe { boot_services.assume_init() }; + boot_services.allocate_pool = mock_allocate_pool; + boot_services.free_pool = mock_free_pool; + boot_services.raise_tpl = mock_raise_tpl; + boot_services.restore_tpl = mock_restore_tpl; + boot_services + } - #[test] - fn basic_alloc_and_dealloc() { - static ALLOCATOR: BootServicesAllocator = BootServicesAllocator::new(); - ALLOCATOR.init(&mut mock_boot_services()); + #[test] + fn basic_alloc_and_dealloc() { + static ALLOCATOR: BootServicesAllocator = BootServicesAllocator::new(); + ALLOCATOR.init(&mut mock_boot_services()); - let layout = Layout::from_size_align(0x40, 0x8).unwrap(); - let ptr = unsafe { ALLOCATOR.alloc_zeroed(layout) }; - assert!(!ptr.is_null()); - assert!(ALLOCATION_TRACKER.lock().contains_key(&(ptr as usize))); + let layout = Layout::from_size_align(0x40, 0x8).unwrap(); + let ptr = unsafe { ALLOCATOR.alloc_zeroed(layout) }; + assert!(!ptr.is_null()); + assert!(ALLOCATION_TRACKER.lock().contains_key(&(ptr as usize))); - unsafe { ALLOCATOR.dealloc(ptr, layout) }; - assert!(!ALLOCATION_TRACKER.lock().contains_key(&(ptr as usize))); - } + unsafe { ALLOCATOR.dealloc(ptr, layout) }; + assert!(!ALLOCATION_TRACKER.lock().contains_key(&(ptr as usize))); + } - #[test] - fn big_alignment_should_allocate_tracking_structure() { - static ALLOCATOR: BootServicesAllocator = BootServicesAllocator::new(); - ALLOCATOR.init(&mut mock_boot_services()); + #[test] + fn big_alignment_should_allocate_tracking_structure() { + static ALLOCATOR: BootServicesAllocator = BootServicesAllocator::new(); + ALLOCATOR.init(&mut mock_boot_services()); - let layout = Layout::from_size_align(0x40, 0x1000).unwrap(); - let ptr = unsafe { ALLOCATOR.alloc_zeroed(layout) }; - assert!(!ptr.is_null()); - assert_eq!(ptr.align_offset(0x1000), 0); + let layout = Layout::from_size_align(0x40, 0x1000).unwrap(); + let ptr = unsafe { ALLOCATOR.alloc_zeroed(layout) }; + assert!(!ptr.is_null()); + assert_eq!(ptr.align_offset(0x1000), 0); - // reconstruct a reference to the tracker structure at the end of the allocation. - let (_, tracking_offset) = layout.extend(Layout::new::()).unwrap(); - let tracker = - unsafe { ptr.add(tracking_offset).cast::().as_mut().expect("tracking pointer is invalid") }; - assert_eq!(tracker.signature, ALLOC_TRACKER_SIG); + // reconstruct a reference to the tracker structure at the end of the allocation. + let (_, tracking_offset) = layout.extend(Layout::new::()).unwrap(); + let tracker = unsafe { + ptr.add(tracking_offset).cast::().as_mut().expect("tracking pointer is invalid") + }; + assert_eq!(tracker.signature, ALLOC_TRACKER_SIG); - let orig_ptr_addr = tracker.orig_ptr as usize; + let orig_ptr_addr = tracker.orig_ptr as usize; - assert!(ALLOCATION_TRACKER.lock().contains_key(&(orig_ptr_addr))); + assert!(ALLOCATION_TRACKER.lock().contains_key(&(orig_ptr_addr))); - unsafe { ALLOCATOR.dealloc(ptr, layout) }; + unsafe { ALLOCATOR.dealloc(ptr, layout) }; - assert!(!ALLOCATION_TRACKER.lock().contains_key(&(orig_ptr_addr))); - } + assert!(!ALLOCATION_TRACKER.lock().contains_key(&(orig_ptr_addr))); + } } diff --git a/MsCorePkg/HelloWorldRustDxe/src/main.rs b/MsCorePkg/HelloWorldRustDxe/src/main.rs index e925e2c0e8..752c49b2eb 100644 --- a/MsCorePkg/HelloWorldRustDxe/src/main.rs +++ b/MsCorePkg/HelloWorldRustDxe/src/main.rs @@ -16,51 +16,51 @@ #![cfg_attr(target_os = "uefi", no_main)] #[cfg(target_os = "uefi")] mod uefi_entry { - extern crate alloc; + extern crate alloc; - use alloc::vec; - use core::panic::PanicInfo; - use r_efi::efi::Status; - use rust_advanced_logger_dxe::{debugln, init_debug, DEBUG_INFO}; + use alloc::vec; + use core::panic::PanicInfo; + use r_efi::efi::Status; + use rust_advanced_logger_dxe::{debugln, init_debug, DEBUG_INFO}; - #[no_mangle] - pub extern "efiapi" fn efi_main( - _image_handle: *const core::ffi::c_void, - _system_table: *const r_efi::system::SystemTable, - ) -> u64 { - rust_boot_services_allocator_dxe::GLOBAL_ALLOCATOR.init(unsafe { (*_system_table).boot_services }); - init_debug(unsafe { (*_system_table).boot_services }); + #[no_mangle] + pub extern "efiapi" fn efi_main( + _image_handle: *const core::ffi::c_void, + _system_table: *const r_efi::system::SystemTable, + ) -> u64 { + rust_boot_services_allocator_dxe::GLOBAL_ALLOCATOR.init(unsafe { (*_system_table).boot_services }); + init_debug(unsafe { (*_system_table).boot_services }); - debugln!(DEBUG_INFO, "Hello, World. This is Rust in UEFI."); + debugln!(DEBUG_INFO, "Hello, World. This is Rust in UEFI."); - debugln!(DEBUG_INFO, "file: {:} line: {:} as hex: {:x}", file!(), line!(), line!()); + debugln!(DEBUG_INFO, "file: {:} line: {:} as hex: {:x}", file!(), line!(), line!()); - let mut foo = vec!["asdf", "xyzpdq", "abcdefg", "thxyzb"]; + let mut foo = vec!["asdf", "xyzpdq", "abcdefg", "thxyzb"]; - debugln!(DEBUG_INFO, "Unsorted vec: {:?}", foo); - foo.sort(); - debugln!(DEBUG_INFO, "Sorted vec: {:?}", foo); + debugln!(DEBUG_INFO, "Unsorted vec: {:?}", foo); + foo.sort(); + debugln!(DEBUG_INFO, "Sorted vec: {:?}", foo); - Status::SUCCESS.as_usize() as u64 - } + Status::SUCCESS.as_usize() as u64 + } - #[panic_handler] - fn panic(_info: &PanicInfo) -> ! { - loop {} - } + #[panic_handler] + fn panic(_info: &PanicInfo) -> ! { + loop {} + } } // For non-UEFI targets (e.g. compiling for unit test or clippy), supply a "main" function. #[cfg(not(target_os = "uefi"))] fn main() { - //do nothing. + //do nothing. } #[cfg(test)] mod test { - #[test] - fn sample_test() { - assert_eq!(1 + 1, 2); - } + #[test] + fn sample_test() { + assert_eq!(1 + 1, 2); + } } diff --git a/rustfmt.toml b/rustfmt.toml index 3e776a3baf..3108b82deb 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -15,7 +15,7 @@ newline_style = "Windows" # Always use Windows line endings '\r\n' reorder_impl_items = false # Do not force where type and const before macros and methods in impl blocks. reorder_imports = true # Do reorder import and extern crate statements alphabetically for readability. reorder_modules = true # Do reorder mod declarations alphabetically for readability. -tab_spaces = 2 # Use 2 spaces for indentation (Rust default is 4). +tab_spaces = 4 # Use 4 spaces for indentation (Rust default). unstable_features = false # Do not use unstable rustfmt features. use_small_heuristics = "Max" # Set all granular width settings to the same as max_width (do not use heuristics) wrap_comments = false # Leave comment formatting to author's discretion