Skip to content

Commit 0b67bfd

Browse files
committed
uefi/time: add monotonic counter services
UEFI possess monotonic counters useful for randomness seeding. These APIs are used, e.g. in systemd-stub, to perform randomness seeding. Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
1 parent c558f5a commit 0b67bfd

5 files changed

Lines changed: 80 additions & 0 deletions

File tree

‎uefi-test-runner/src/boot/misc.rs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ pub fn test() {
2828
test_install_configuration_table();
2929
info!("Testing crc32...");
3030
test_calculate_crc32();
31+
info!("Testing monotonic count...");
32+
test_monotonic_count();
33+
}
34+
35+
fn test_monotonic_count() {
36+
let first = boot::get_next_monotonic_count().expect("failed to get monotonic count");
37+
let second = boot::get_next_monotonic_count().expect("failed to get monotonic count");
38+
assert!(
39+
second > first,
40+
"monotonic count did not increase: {first} then {second}"
41+
);
3142
}
3243

3344
fn test_tpl() {

‎uefi-test-runner/src/runtime/mod.rs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
mod vars;
44

5+
use uefi::Status;
56
use uefi::runtime::{self, Daylight, Time, TimeParams};
67

78
pub fn test() {
89
info!("Testing runtime services");
910
vars::test();
1011
test_time();
12+
test_monotonic_count();
1113
}
1214

1315
fn test_time() {
@@ -37,3 +39,13 @@ fn test_time() {
3739
info!("After setting time: {now}");
3840
assert_eq!(now.year(), 2020);
3941
}
42+
43+
fn test_monotonic_count() {
44+
match runtime::get_next_high_monotonic_count() {
45+
Ok(high_count) => info!("High monotonic count: {high_count}"),
46+
Err(err) if matches!(err.status(), Status::UNSUPPORTED) => {
47+
info!("High monotonic count not available: {}", err.status());
48+
}
49+
Err(err) => panic!("failed to get high monotonic count: {err:?}"),
50+
}
51+
}

‎uefi/CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Added
44

5+
- `boot::get_next_monotonic_count` and `runtime::get_next_high_monotonic_count`,
6+
safe wrappers around the UEFI monotonic counter services.
57
- `CStr8::as_slice`, `CStr8::as_slice_with_nul`, `CStr8::num_chars`, `CStr8::is_empty`,
68
`CStr8::num_bytes`, `CStr8::is_ascii`, and `Char8::is_ascii`.
79

‎uefi/src/boot.rs‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,6 +1616,34 @@ pub unsafe fn install_configuration_table(
16161616
unsafe { (bt.install_configuration_table)(guid_entry, table_ptr) }.to_result()
16171617
}
16181618

1619+
/// Returns the next value of the platform's 64-bit monotonic counter.
1620+
///
1621+
/// The counter is guaranteed to return a numerically larger value on every
1622+
/// call for the lifetime of the current boot. It is not related to any time
1623+
/// source, so it must not be used for delays or time measurement.
1624+
///
1625+
/// The platform monotonic counter is split into two 32-bit halves. This boot
1626+
/// service manages all 64 bits; the volatile low half is incremented by each
1627+
/// call, while the non-volatile high half is incremented on reset or low-half
1628+
/// overflow. After exiting boot services, an operating system can extend the
1629+
/// counter at runtime with [`runtime::get_next_high_monotonic_count`].
1630+
///
1631+
/// # Errors
1632+
///
1633+
/// * [`Status::DEVICE_ERROR`]: the counter could not be read due to a hardware
1634+
/// error.
1635+
///
1636+
/// [`runtime::get_next_high_monotonic_count`]: crate::runtime::get_next_high_monotonic_count
1637+
pub fn get_next_monotonic_count() -> Result<u64> {
1638+
let bt = boot_services_raw_panicking();
1639+
// SAFETY: The pointer is not null and we assume it to be initialized.
1640+
let bt = unsafe { bt.as_ref() };
1641+
1642+
let mut count = 0;
1643+
// SAFETY: The memory is valid.
1644+
unsafe { (bt.get_next_monotonic_count)(&mut count) }.to_result_with_val(|| count)
1645+
}
1646+
16191647
/// Sets the watchdog timer.
16201648
///
16211649
/// UEFI will start a 5-minute countdown after a UEFI image is loaded. The

‎uefi/src/runtime/mod.rs‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,33 @@ pub fn get_time_and_caps() -> Result<(Time, TimeCapabilities)> {
7070
unsafe { (rt.get_time)(time_ptr.cast(), &mut caps) }.to_result_with_val(|| (time, caps))
7171
}
7272

73+
/// Returns the next high 32 bits of the platform's monotonic counter.
74+
///
75+
/// The high half of the counter is non-volatile and is incremented by this
76+
/// call. It is intended to be used by an operating system to extend the
77+
/// platform monotonic counter after exiting boot services: snapshot the full
78+
/// 64-bit value with [`boot::get_next_monotonic_count`] before
79+
/// `ExitBootServices`, manage the volatile low 32 bits locally, and call this
80+
/// function to bump the high 32 bits when the low half rolls over.
81+
///
82+
/// # Errors
83+
///
84+
/// * [`Status::DEVICE_ERROR`]: the counter could not be saved due to a hardware
85+
/// error.
86+
/// * [`Status::UNSUPPORTED`]: this platform does not support the monotonic
87+
/// counter at runtime.
88+
///
89+
/// [`boot::get_next_monotonic_count`]: crate::boot::get_next_monotonic_count
90+
pub fn get_next_high_monotonic_count() -> Result<u32> {
91+
let rt = runtime_services_raw_panicking();
92+
// SAFETY: The pointer is not null and we assume it to be initialized.
93+
let rt = unsafe { rt.as_ref() };
94+
95+
let mut high_count = 0;
96+
// SAFETY: The memory is valid.
97+
unsafe { (rt.get_next_high_monotonic_count)(&mut high_count) }.to_result_with_val(|| high_count)
98+
}
99+
73100
/// Sets the current local time and date information
74101
///
75102
/// During runtime, if a PC-AT CMOS device is present in the platform, the

0 commit comments

Comments
 (0)