diff --git a/uefi-raw/CHANGELOG.md b/uefi-raw/CHANGELOG.md index d0315e37e..504b94824 100644 --- a/uefi-raw/CHANGELOG.md +++ b/uefi-raw/CHANGELOG.md @@ -1,5 +1,8 @@ # uefi-raw - [Unreleased] +## Added +- Added `ResetNotificationProtocol`. + ## Added - Added `TimestampProtocol`. - Added `DevicePathToTextProtocol` and `DevicePathFromTextProtocol`. diff --git a/uefi-raw/src/protocol/misc.rs b/uefi-raw/src/protocol/misc.rs index a7a3a905a..0147fce87 100644 --- a/uefi-raw/src/protocol/misc.rs +++ b/uefi-raw/src/protocol/misc.rs @@ -1,3 +1,4 @@ +use crate::table::runtime; use crate::{guid, Guid, Status}; #[derive(Debug)] @@ -22,3 +23,25 @@ pub struct TimestampProperties { /// example, a 24-bit counter would have an end value of `0xff_ffff`. pub end_value: u64, } + +/// Properties of Reset Notification. +#[derive(Debug)] +#[repr(C)] +pub struct ResetNotificationProtocol { + pub register_reset_notify: + unsafe extern "efiapi" fn(this: *mut Self, reset_function: ResetSystemFn) -> Status, + pub unregister_reset_notify: + unsafe extern "efiapi" fn(this: *mut Self, reset_function: ResetSystemFn) -> Status, +} + +impl ResetNotificationProtocol { + pub const GUID: Guid = guid!("9da34ae0-eaf9-4bbf-8ec3-fd60226c44be"); +} + +/// Raw reset notification function, to be called if you register it when a ResetSystem() is executed. +pub type ResetSystemFn = unsafe extern "efiapi" fn( + rt: runtime::ResetType, + status: Status, + data_size: usize, + data: *const u8, +); diff --git a/uefi-test-runner/examples/timestamp.rs b/uefi-test-runner/examples/timestamp.rs new file mode 100644 index 000000000..deccc7e98 --- /dev/null +++ b/uefi-test-runner/examples/timestamp.rs @@ -0,0 +1,64 @@ +// ANCHOR: all +// ANCHOR: features +#![no_main] +#![no_std] +// ANCHOR_END: features + +extern crate alloc; + +use log::{info, warn}; + +// ANCHOR: use +use uefi::prelude::*; +use uefi::proto::misc::Timestamp; + +// ANCHOR_END: use + +// ANCHOR: entry +#[entry] +fn main(image_handle: Handle, mut system_table: SystemTable) -> Status { + // ANCHOR_END: entry + // ANCHOR: services + uefi::helpers::init(&mut system_table).unwrap(); + let boot_services = system_table.boot_services(); + // ANCHOR_END: services + + // ANCHOR: params + test_timestamp(boot_services); + // ANCHOR_END: params + + // ANCHOR: stall + boot_services.stall(10_000_000); + // ANCHOR_END: stall + + // ANCHOR: return + Status::SUCCESS +} +// ANCHOR_END: return + +// ANCHOR: test_timestamp +pub fn test_timestamp(bt: &BootServices) { + // ANCHOR_END: test_timestamp + info!("Running loaded Timestamp Protocol test"); + + let handle = bt.get_handle_for_protocol::(); + + match handle { + Ok(handle) => { + let timestamp_proto = bt + .open_protocol_exclusive::(handle) + .expect("Founded Timestamp Protocol but open failed"); + // ANCHOR: text + let timestamp = timestamp_proto.get_timestamp(); + info!("Timestamp Protocol's timestamp: {:?}", timestamp); + + let properties = timestamp_proto.get_properties(); + info!("Timestamp Protocol's properties: {:?}", properties); + // ANCHOR_END: text + } + Err(err) => { + warn!("Failed to found Timestamp Protocol: {:?}", err); + } + } +} +// ANCHOR_END: all diff --git a/uefi-test-runner/src/proto/misc.rs b/uefi-test-runner/src/proto/misc.rs new file mode 100644 index 000000000..c7b1664f7 --- /dev/null +++ b/uefi-test-runner/src/proto/misc.rs @@ -0,0 +1,44 @@ +use uefi::prelude::*; +use uefi::proto::misc::ResetNotification; +use uefi::table::runtime; + +pub fn test(bt: &BootServices) { + test_reset_notification(bt); +} + +pub fn test_reset_notification(bt: &BootServices) { + info!("Running loaded ResetNotification protocol test"); + + let handle = bt + .get_handle_for_protocol::() + .expect("Failed to get handles for `ResetNotification` protocol"); + + let mut reset_notif_proto = bt + .open_protocol_exclusive::(handle) + .expect("Founded ResetNotification Protocol but open failed"); + + // value efi_reset_fn is the type of ResetSystemFn, a function pointer + unsafe extern "efiapi" fn efi_reset_fn( + rt: runtime::ResetType, + status: Status, + data_size: usize, + data: *const u8, + ) { + info!("Inside the event callback, hi, efi_reset_fn"); + info!("rt: {:?} status: {:?}", rt, status); + info!("size: {:?} data: {:?}", data_size, data); + // do what you want + } + + let result = reset_notif_proto.register_reset_notify(efi_reset_fn); + info!( + "ResetNotification Protocol register efi_reset_fn test: {:?}", + result + ); + + let result = reset_notif_proto.unregister_reset_notify(efi_reset_fn); + info!( + "ResetNotification Protocol unregister efi_reset_fn test: {:?}", + result + ); +} diff --git a/uefi-test-runner/src/proto/mod.rs b/uefi-test-runner/src/proto/mod.rs index b232e3cf9..261f439e6 100644 --- a/uefi-test-runner/src/proto/mod.rs +++ b/uefi-test-runner/src/proto/mod.rs @@ -1,5 +1,4 @@ use uefi::prelude::*; - use uefi::proto::loaded_image::LoadedImage; use uefi::{proto, Identify}; @@ -22,6 +21,7 @@ pub fn test(image: Handle, st: &mut SystemTable) { rng::test(bt); shell_params::test(bt); string::test(bt); + misc::test(bt); #[cfg(any( target_arch = "x86", @@ -61,6 +61,7 @@ mod device_path; mod driver; mod loaded_image; mod media; +mod misc; mod network; mod pi; mod rng; diff --git a/uefi/CHANGELOG.md b/uefi/CHANGELOG.md index 6d221044c..6ecb59af5 100644 --- a/uefi/CHANGELOG.md +++ b/uefi/CHANGELOG.md @@ -1,5 +1,8 @@ # uefi - [Unreleased] +## Added +- Added `ResetNotification` protocol. + ## Added - Added `Timestamp` protocol. - Added `UnalignedSlice::as_ptr`. diff --git a/uefi/src/proto/misc.rs b/uefi/src/proto/misc.rs index 339c8ea6e..92666fd08 100644 --- a/uefi/src/proto/misc.rs +++ b/uefi/src/proto/misc.rs @@ -1,10 +1,16 @@ //! Miscellaneous protocols. +use uefi_raw::protocol::misc::{ + ResetNotificationProtocol, ResetSystemFn, TimestampProperties, TimestampProtocol, +}; + use crate::proto::unsafe_protocol; use crate::{Result, StatusExt}; -use uefi_raw::protocol::misc::{TimestampProperties, TimestampProtocol}; /// Protocol for retrieving a high-resolution timestamp counter. +/// **Note:** +/// If your UEFI firmware not support timestamp protocol which first added at UEFI spec 2.4 2013. +/// you also could use `RDTSC` in rust, here is a demo [Slint-UI](https://github.com/slint-ui/slint/blob/2c0ba2bc0f151eba8d1fa17839fa2ac58832ca80/examples/uefi-demo/main.rs#L28-L62) who use uefi-rs. #[derive(Debug)] #[repr(transparent)] #[unsafe_protocol(TimestampProtocol::GUID)] @@ -23,3 +29,55 @@ impl Timestamp { unsafe { (self.0.get_properties)(&mut properties) }.to_result_with_val(|| properties) } } + +/// Protocol to register for a notification when ResetSystem is called. +#[derive(Debug)] +#[repr(transparent)] +#[unsafe_protocol(ResetNotificationProtocol::GUID)] +pub struct ResetNotification(ResetNotificationProtocol); + +impl ResetNotification { + /// Register a notification function to be called when ResetSystem() is called. + /// + /// + /// # Example + /// + /// ```rust + /// use log::info; + /// use uefi::Handle; + /// use uefi::prelude::BootServices; + /// use uefi::proto::misc::{ResetNotification}; + /// use uefi_raw::Status; + /// use uefi_raw::table::runtime; + /// + /// + /// // value efi_reset_fn is the type of ResetSystemFn, a function pointer + /// unsafe extern "efiapi" fn efi_reset_fn( + /// rt: runtime::ResetType, + /// status: Status, + /// data_size: usize, + /// data: *const u8, + /// ){ + /// info!("Inside the event callback"); + /// info!("do what you want"); + /// } + /// + /// pub fn test(image: Handle, bt: &BootServices) { + /// + /// let mut rn = bt + /// .open_protocol_exclusive::(image) + /// .expect("Failed to open Timestamp protocol"); + /// + /// rn.register_reset_notify(efi_reset_fn) + /// .expect("Failed to register a reset notification function!"); + /// } + /// ``` + pub fn register_reset_notify(&mut self, reset_function: ResetSystemFn) -> Result { + unsafe { (self.0.register_reset_notify)(&mut self.0, reset_function) }.to_result() + } + + /// Remove a reset notification function that was previously registered with [`ResetNotification::register_reset_notify`]. + pub fn unregister_reset_notify(&mut self, reset_function: ResetSystemFn) -> Result { + unsafe { (self.0.unregister_reset_notify)(&mut self.0, reset_function) }.to_result() + } +}