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