1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! This file provides some utilities built on EFI APIs.
16
17 use crate::{EfiEntry, Event, EventType};
18 use core::{future::Future, time::Duration};
19 use efi_types::{EFI_TIMER_DELAY_TIMER_PERIODIC, EFI_TIMER_DELAY_TIMER_RELATIVE};
20 use gbl_async::{select, yield_now};
21 use liberror::Result;
22
23 /// `Timeout` provide APIs for checking timeout.
24 pub struct Timeout<'a> {
25 efi_entry: &'a EfiEntry,
26 timer: Event<'a, 'static>,
27 }
28
29 impl<'a> Timeout<'a> {
30 /// Creates a new instance and starts the timeout timer.
new(efi_entry: &'a EfiEntry, timeout: Duration) -> Result<Self>31 pub fn new(efi_entry: &'a EfiEntry, timeout: Duration) -> Result<Self> {
32 let bs = efi_entry.system_table().boot_services();
33 let timer = bs.create_event(EventType::Timer)?;
34 bs.set_timer(&timer, EFI_TIMER_DELAY_TIMER_RELATIVE, timeout)?;
35 Ok(Self { efi_entry, timer })
36 }
37
38 /// Checks if it has timeout.
check(&self) -> Result<bool>39 pub fn check(&self) -> Result<bool> {
40 Ok(self.efi_entry.system_table().boot_services().check_event(&self.timer)?)
41 }
42
43 /// Resets the timeout.
reset(&self, timeout: Duration) -> Result<()>44 pub fn reset(&self, timeout: Duration) -> Result<()> {
45 let bs = self.efi_entry.system_table().boot_services();
46 bs.set_timer(&self.timer, EFI_TIMER_DELAY_TIMER_RELATIVE, timeout)?;
47 Ok(())
48 }
49 }
50
51 /// Waits for a given amount of time.
wait(efi_entry: &EfiEntry, duration: Duration) -> Result<()>52 pub async fn wait(efi_entry: &EfiEntry, duration: Duration) -> Result<()> {
53 // EFI boot service has a `stall` API. But it's not async.
54 let timeout = Timeout::new(efi_entry, duration)?;
55 while !timeout.check()? {
56 yield_now().await;
57 }
58 Ok(())
59 }
60
61 /// Runs a future with timeout.
62 ///
63 /// # Returns
64 ///
65 /// * Returns Ok(Some(R)) if the future finishes before timeout.
66 /// * Returns Ok(None) if the future didn't finish before timeout.
67 /// * Returns Err if internal error occurs while handling EFI timer event.
with_timeout<F: Future<Output = R>, R>( efi_entry: &EfiEntry, fut: F, timeout: Duration, ) -> Result<Option<R>>68 pub async fn with_timeout<F: Future<Output = R>, R>(
69 efi_entry: &EfiEntry,
70 fut: F,
71 timeout: Duration,
72 ) -> Result<Option<R>> {
73 let (timeout_res, res) = select(wait(efi_entry, timeout), fut).await;
74 match timeout_res {
75 Some(Err(e)) => return Err(e),
76 _ => Ok(res),
77 }
78 }
79
80 /// Wrapper helping for a periodic timer.
81 pub struct RecurringTimer<'a> {
82 efi_entry: &'a EfiEntry,
83 timer: Event<'a, 'static>,
84 }
85
86 impl<'a> RecurringTimer<'a> {
87 /// Constructs and starts a new periodic timer.
new(efi_entry: &'a EfiEntry, timeout: Duration) -> Result<Self>88 pub fn new(efi_entry: &'a EfiEntry, timeout: Duration) -> Result<Self> {
89 let bs = efi_entry.system_table().boot_services();
90 let timer = bs.create_event(EventType::Timer)?;
91 bs.set_timer(&timer, EFI_TIMER_DELAY_TIMER_PERIODIC, timeout)?;
92 Ok(Self { efi_entry, timer })
93 }
94
95 /// Checks whether the timer has expried.
check(&self) -> Result<bool>96 pub fn check(&self) -> Result<bool> {
97 Ok(self.efi_entry.system_table().boot_services().check_event(&self.timer)?)
98 }
99
100 /// Waits asynchronously until the next tick.
wait(&self) -> Result<()>101 pub async fn wait(&self) -> Result<()> {
102 while !self.check()? {
103 yield_now().await;
104 }
105 Ok(())
106 }
107 }
108