• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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