1 //! UEFI runtime services.
2 //!
3 //! These services are available both before and after exiting boot
4 //! services. Note that various restrictions apply when calling runtime services
5 //! functions after exiting boot services; see the "Calling Convention" section
6 //! of the UEFI specification for details.
7
8 use crate::data_types::PhysicalAddress;
9 use crate::table::{self, Revision};
10 use crate::{CStr16, Error, Result, Status, StatusExt};
11 use core::fmt::{self, Debug, Display, Formatter};
12 use core::mem;
13 use core::ptr::{self, NonNull};
14 use uefi_raw::table::boot::MemoryDescriptor;
15
16 #[cfg(feature = "alloc")]
17 use {
18 crate::mem::make_boxed, crate::CString16, crate::Guid, alloc::borrow::ToOwned,
19 alloc::boxed::Box, alloc::vec::Vec,
20 };
21
22 #[cfg(all(feature = "unstable", feature = "alloc"))]
23 use alloc::alloc::Global;
24
25 pub use uefi_raw::capsule::{CapsuleBlockDescriptor, CapsuleFlags, CapsuleHeader};
26 pub use uefi_raw::table::runtime::{
27 ResetType, TimeCapabilities, VariableAttributes, VariableVendor,
28 };
29 pub use uefi_raw::time::Daylight;
30
runtime_services_raw_panicking() -> NonNull<uefi_raw::table::runtime::RuntimeServices>31 fn runtime_services_raw_panicking() -> NonNull<uefi_raw::table::runtime::RuntimeServices> {
32 let st = table::system_table_raw_panicking();
33 // SAFETY: valid per requirements of `set_system_table`.
34 let st = unsafe { st.as_ref() };
35 NonNull::new(st.runtime_services).expect("runtime services are not active")
36 }
37
38 /// Query the current time and date information.
get_time() -> Result<Time>39 pub fn get_time() -> Result<Time> {
40 let rt = runtime_services_raw_panicking();
41 let rt = unsafe { rt.as_ref() };
42
43 let mut time = Time::invalid();
44 let time_ptr: *mut Time = &mut time;
45 unsafe { (rt.get_time)(time_ptr.cast(), ptr::null_mut()) }.to_result_with_val(|| time)
46 }
47
48 /// Query the current time and date information and the RTC capabilities.
get_time_and_caps() -> Result<(Time, TimeCapabilities)>49 pub fn get_time_and_caps() -> Result<(Time, TimeCapabilities)> {
50 let rt = runtime_services_raw_panicking();
51 let rt = unsafe { rt.as_ref() };
52
53 let mut time = Time::invalid();
54 let time_ptr: *mut Time = &mut time;
55 let mut caps = TimeCapabilities::default();
56 unsafe { (rt.get_time)(time_ptr.cast(), &mut caps) }.to_result_with_val(|| (time, caps))
57 }
58
59 /// Sets the current local time and date information
60 ///
61 /// During runtime, if a PC-AT CMOS device is present in the platform, the
62 /// caller must synchronize access to the device before calling `set_time`.
63 ///
64 /// # Safety
65 ///
66 /// Undefined behavior could happen if multiple tasks try to
67 /// use this function at the same time without synchronisation.
set_time(time: &Time) -> Result68 pub unsafe fn set_time(time: &Time) -> Result {
69 let rt = runtime_services_raw_panicking();
70 let rt = unsafe { rt.as_ref() };
71
72 let time: *const Time = time;
73 (rt.set_time)(time.cast()).to_result()
74 }
75
76 /// Checks if a variable exists.
77 ///
78 /// Returns `Ok(true)` if the variable exists, `Ok(false)` if the variable does
79 /// not exist, or `Err` if the existence of the variable could not be determined.
80 ///
81 /// # Errors
82 ///
83 /// * [`Status::DEVICE_ERROR`]: variable could not be read due to a hardware error.
84 /// * [`Status::SECURITY_VIOLATION`]: variable could not be read due to an
85 /// authentication error.
86 /// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
87 /// after exiting boot services.
variable_exists(name: &CStr16, vendor: &VariableVendor) -> Result<bool>88 pub fn variable_exists(name: &CStr16, vendor: &VariableVendor) -> Result<bool> {
89 let rt = runtime_services_raw_panicking();
90 let rt = unsafe { rt.as_ref() };
91
92 let attributes = ptr::null_mut();
93 let data = ptr::null_mut();
94 let mut data_size = 0;
95
96 let status = unsafe {
97 (rt.get_variable)(
98 name.as_ptr().cast(),
99 &vendor.0,
100 attributes,
101 &mut data_size,
102 data,
103 )
104 };
105
106 match status {
107 // If the variable exists, the status will be BUFFER_TOO_SMALL because
108 // data_size is 0. Empty variables do not exist, because setting a
109 // variable with empty data deletes the variable. In other words, the
110 // status will never be SUCCESS.
111 Status::BUFFER_TOO_SMALL => Ok(true),
112 Status::NOT_FOUND => Ok(false),
113 _ => Err(Error::from(status)),
114 }
115 }
116
117 /// Gets the contents and attributes of a variable. The size of `buf` must be at
118 /// least as big as the variable's size, although it can be larger.
119 ///
120 /// On success, returns a tuple containing the variable's value (a slice of
121 /// `buf`) and the variable's attributes.
122 ///
123 /// # Errors
124 ///
125 /// * [`Status::NOT_FOUND`]: variable was not found.
126 /// * [`Status::BUFFER_TOO_SMALL`]: `buf` is not large enough. The required size
127 /// will be returned in the error data.
128 /// * [`Status::DEVICE_ERROR`]: variable could not be read due to a hardware error.
129 /// * [`Status::SECURITY_VIOLATION`]: variable could not be read due to an
130 /// authentication error.
131 /// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
132 /// after exiting boot services.
get_variable<'buf>( name: &CStr16, vendor: &VariableVendor, buf: &'buf mut [u8], ) -> Result<(&'buf mut [u8], VariableAttributes), Option<usize>>133 pub fn get_variable<'buf>(
134 name: &CStr16,
135 vendor: &VariableVendor,
136 buf: &'buf mut [u8],
137 ) -> Result<(&'buf mut [u8], VariableAttributes), Option<usize>> {
138 let rt = runtime_services_raw_panicking();
139 let rt = unsafe { rt.as_ref() };
140
141 let mut attributes = VariableAttributes::empty();
142 let mut data_size = buf.len();
143 let status = unsafe {
144 (rt.get_variable)(
145 name.as_ptr().cast(),
146 &vendor.0,
147 &mut attributes,
148 &mut data_size,
149 buf.as_mut_ptr(),
150 )
151 };
152
153 match status {
154 Status::SUCCESS => Ok((&mut buf[..data_size], attributes)),
155 Status::BUFFER_TOO_SMALL => Err(Error::new(status, Some(data_size))),
156 _ => Err(Error::new(status, None)),
157 }
158 }
159
160 /// Gets the contents and attributes of a variable.
161 ///
162 /// # Errors
163 ///
164 /// * [`Status::NOT_FOUND`]: variable was not found.
165 /// * [`Status::DEVICE_ERROR`]: variable could not be read due to a hardware error.
166 /// * [`Status::SECURITY_VIOLATION`]: variable could not be read due to an
167 /// authentication error.
168 /// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
169 /// after exiting boot services.
170 #[cfg(feature = "alloc")]
get_variable_boxed( name: &CStr16, vendor: &VariableVendor, ) -> Result<(Box<[u8]>, VariableAttributes)>171 pub fn get_variable_boxed(
172 name: &CStr16,
173 vendor: &VariableVendor,
174 ) -> Result<(Box<[u8]>, VariableAttributes)> {
175 let mut out_attr = VariableAttributes::empty();
176 let get_var = |buf| {
177 get_variable(name, vendor, buf).map(|(val, attr)| {
178 // `make_boxed` expects only a DST value to be returned (`val` in
179 // this case), so smuggle the `attr` value out via a separate
180 // variable.
181 out_attr = attr;
182 val
183 })
184 };
185 #[cfg(not(feature = "unstable"))]
186 {
187 make_boxed(get_var).map(|val| (val, out_attr))
188 }
189 #[cfg(feature = "unstable")]
190 {
191 make_boxed(get_var, Global).map(|val| (val, out_attr))
192 }
193 }
194
195 /// Gets each variable key (name and vendor) one at a time.
196 ///
197 /// This is used to iterate over variable keys. See [`variable_keys`] for a more
198 /// convenient interface that requires the `alloc` feature.
199 ///
200 /// To get the first variable key, `name` must be initialized to start with a
201 /// null character. The `vendor` value is arbitrary. On success, the first
202 /// variable's name and vendor will be written out to `name` and `vendor`. Keep
203 /// calling `get_next_variable_key` with the same `name` and `vendor` references
204 /// to get the remaining variable keys.
205 ///
206 /// All variable names should be valid strings, but this may not be enforced by
207 /// firmware. To convert to a string, truncate at the first null and call
208 /// [`CStr16::from_u16_with_nul`].
209 ///
210 /// # Errors
211 ///
212 /// * [`Status::NOT_FOUND`]: indicates end of iteration, the last variable keys
213 /// was retrieved by the previous call to `get_next_variable_key`.
214 /// * [`Status::BUFFER_TOO_SMALL`]: `name` is not large enough. The required
215 /// size (in `u16` characters, not bytes) will be returned in the error data.
216 /// * [`Status::INVALID_PARAMETER`]: `name` does not contain a null character, or
217 /// the `name` and `vendor` are not an existing variable.
218 /// * [`Status::DEVICE_ERROR`]: variable could not be read due to a hardware error.
219 /// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
220 /// after exiting boot services.
get_next_variable_key( name: &mut [u16], vendor: &mut VariableVendor, ) -> Result<(), Option<usize>>221 pub fn get_next_variable_key(
222 name: &mut [u16],
223 vendor: &mut VariableVendor,
224 ) -> Result<(), Option<usize>> {
225 let rt = runtime_services_raw_panicking();
226 let rt = unsafe { rt.as_ref() };
227
228 let mut name_size_in_bytes = mem::size_of_val(name);
229
230 let status = unsafe {
231 (rt.get_next_variable_name)(&mut name_size_in_bytes, name.as_mut_ptr(), &mut vendor.0)
232 };
233 match status {
234 Status::SUCCESS => Ok(()),
235 Status::BUFFER_TOO_SMALL => Err(Error::new(
236 status,
237 Some(name_size_in_bytes / mem::size_of::<u16>()),
238 )),
239 _ => Err(Error::new(status, None)),
240 }
241 }
242
243 /// Get an iterator over all UEFI variables.
244 ///
245 /// See [`VariableKeys`] for details.
246 #[cfg(feature = "alloc")]
247 #[must_use]
variable_keys() -> VariableKeys248 pub fn variable_keys() -> VariableKeys {
249 VariableKeys::new()
250 }
251
252 /// Iterator over all UEFI variables.
253 ///
254 /// Each iteration yields a `Result<`[`VariableKey`]`>`. Error values:
255 ///
256 /// * [`Status::DEVICE_ERROR`]: variable could not be read due to a hardware error.
257 /// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
258 /// after exiting boot services.
259 #[cfg(feature = "alloc")]
260 #[derive(Debug)]
261 pub struct VariableKeys {
262 name: Vec<u16>,
263 vendor: VariableVendor,
264 is_done: bool,
265 }
266
267 #[cfg(feature = "alloc")]
268 impl VariableKeys {
new() -> Self269 fn new() -> Self {
270 // Create a the name buffer with a reasonable default capacity, and
271 // initialize it to an empty null-terminated string.
272 let mut name = Vec::with_capacity(32);
273 name.push(0);
274
275 Self {
276 // Give the name buffer a reasonable default capacity.
277 name,
278 // The initial vendor GUID is arbitrary.
279 vendor: VariableVendor(Guid::default()),
280 is_done: false,
281 }
282 }
283 }
284
285 #[cfg(feature = "alloc")]
286 impl Iterator for VariableKeys {
287 type Item = Result<VariableKey>;
288
next(&mut self) -> Option<Result<VariableKey>>289 fn next(&mut self) -> Option<Result<VariableKey>> {
290 if self.is_done {
291 return None;
292 }
293
294 let mut result = get_next_variable_key(&mut self.name, &mut self.vendor);
295
296 // If the name buffer was too small, resize it to be big enough and call
297 // `get_next_variable_key` again.
298 if let Err(err) = &result {
299 if let Some(required_size) = err.data() {
300 self.name.resize(*required_size, 0u16);
301 result = get_next_variable_key(&mut self.name, &mut self.vendor);
302 }
303 }
304
305 match result {
306 Ok(()) => {
307 // Convert the name to a `CStr16`, yielding an error if invalid.
308 let Ok(name) = CStr16::from_u16_until_nul(&self.name) else {
309 return Some(Err(Status::UNSUPPORTED.into()));
310 };
311
312 Some(Ok(VariableKey {
313 name: name.to_owned(),
314 vendor: self.vendor,
315 }))
316 }
317 Err(err) => {
318 if err.status() == Status::NOT_FOUND {
319 // This status indicates the end of the list. The final variable
320 // has already been yielded at this point, so return `None`.
321 self.is_done = true;
322 None
323 } else {
324 // Return the error and end iteration.
325 self.is_done = true;
326 Some(Err(err.to_err_without_payload()))
327 }
328 }
329 }
330 }
331 }
332
333 /// Sets the value of a variable. This can be used to create a new variable,
334 /// update an existing variable, or (when the size of `data` is zero)
335 /// delete a variable.
336 ///
337 /// # Warnings
338 ///
339 /// The [`Status::WARN_RESET_REQUIRED`] warning will be returned when using
340 /// this function to transition the Secure Boot mode to setup mode or audit
341 /// mode if the firmware requires a reboot for that operation.
342 ///
343 /// # Errors
344 ///
345 /// * [`Status::INVALID_PARAMETER`]: invalid attributes, name, or vendor.
346 /// * [`Status::OUT_OF_RESOURCES`]: not enough storage is available to hold
347 /// the variable.
348 /// * [`Status::WRITE_PROTECTED`]: variable is read-only.
349 /// * [`Status::SECURITY_VIOLATION`]: variable could not be written due to an
350 /// authentication error.
351 /// * [`Status::NOT_FOUND`]: attempted to update a non-existent variable.
352 /// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
353 /// after exiting boot services.
set_variable( name: &CStr16, vendor: &VariableVendor, attributes: VariableAttributes, data: &[u8], ) -> Result354 pub fn set_variable(
355 name: &CStr16,
356 vendor: &VariableVendor,
357 attributes: VariableAttributes,
358 data: &[u8],
359 ) -> Result {
360 let rt = runtime_services_raw_panicking();
361 let rt = unsafe { rt.as_ref() };
362
363 unsafe {
364 (rt.set_variable)(
365 name.as_ptr().cast(),
366 &vendor.0,
367 attributes,
368 data.len(),
369 data.as_ptr(),
370 )
371 .to_result()
372 }
373 }
374
375 /// Deletes a UEFI variable.
376 ///
377 /// # Errors
378 ///
379 /// * [`Status::INVALID_PARAMETER`]: invalid name or vendor.
380 /// * [`Status::WRITE_PROTECTED`]: variable is read-only.
381 /// * [`Status::SECURITY_VIOLATION`]: variable could not be deleted due to an
382 /// authentication error.
383 /// * [`Status::NOT_FOUND`]: attempted to delete a non-existent variable.
384 /// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
385 /// after exiting boot services.
delete_variable(name: &CStr16, vendor: &VariableVendor) -> Result386 pub fn delete_variable(name: &CStr16, vendor: &VariableVendor) -> Result {
387 set_variable(name, vendor, VariableAttributes::empty(), &[])
388 }
389
390 /// Get information about UEFI variable storage space for the type
391 /// of variable specified in `attributes`.
392 ///
393 /// This operation is only supported starting with UEFI 2.0.
394 ///
395 /// See [`VariableStorageInfo`] for details of the information returned.
396 ///
397 /// # Errors
398 ///
399 /// * [`Status::INVALID_PARAMETER`]: invalid combination of variable attributes.
400 /// * [`Status::UNSUPPORTED`]: the combination of variable attributes is not
401 /// supported on this platform, or the UEFI version is less than 2.0.
query_variable_info(attributes: VariableAttributes) -> Result<VariableStorageInfo>402 pub fn query_variable_info(attributes: VariableAttributes) -> Result<VariableStorageInfo> {
403 let rt = runtime_services_raw_panicking();
404 let rt = unsafe { rt.as_ref() };
405
406 if rt.header.revision < Revision::EFI_2_00 {
407 return Err(Status::UNSUPPORTED.into());
408 }
409
410 let mut info = VariableStorageInfo::default();
411 unsafe {
412 (rt.query_variable_info)(
413 attributes,
414 &mut info.maximum_variable_storage_size,
415 &mut info.remaining_variable_storage_size,
416 &mut info.maximum_variable_size,
417 )
418 .to_result_with_val(|| info)
419 }
420 }
421
422 /// Passes capsules to the firmware.
423 ///
424 /// Capsules are most commonly used to update system firmware.
425 ///
426 /// # Errors
427 ///
428 /// * [`Status::INVALID_PARAMETER`]: zero capsules were provided, or the
429 /// capsules are invalid.
430 /// * [`Status::DEVICE_ERROR`]: the capsule update was started but failed to a
431 /// device error.
432 /// * [`Status::OUT_OF_RESOURCES`]: before exiting boot services, indicates the
433 /// capsule is compatible with the platform but there are insufficient
434 /// resources to complete the update. After exiting boot services, indicates
435 /// the capsule is compatible with the platform but can only be processed
436 /// before exiting boot services.
437 /// * [`Status::UNSUPPORTED`]: this platform does not support capsule updates
438 /// after exiting boot services.
update_capsule( capsule_header_array: &[&CapsuleHeader], capsule_block_descriptors: &[CapsuleBlockDescriptor], ) -> Result439 pub fn update_capsule(
440 capsule_header_array: &[&CapsuleHeader],
441 capsule_block_descriptors: &[CapsuleBlockDescriptor],
442 ) -> Result {
443 let rt = runtime_services_raw_panicking();
444 let rt = unsafe { rt.as_ref() };
445
446 unsafe {
447 (rt.update_capsule)(
448 capsule_header_array.as_ptr().cast(),
449 capsule_header_array.len(),
450 capsule_block_descriptors.as_ptr() as PhysicalAddress,
451 )
452 .to_result()
453 }
454 }
455
456 /// Tests whether a capsule or capsules can be updated via [`update_capsule`].
457 ///
458 /// See [`CapsuleInfo`] for details of the information returned.
459 ///
460 /// # Errors
461 ///
462 /// * [`Status::OUT_OF_RESOURCES`]: before exiting boot services, indicates the
463 /// capsule is compatible with the platform but there are insufficient
464 /// resources to complete the update. After exiting boot services, indicates
465 /// the capsule is compatible with the platform but can only be processed
466 /// before exiting boot services.
467 /// * [`Status::UNSUPPORTED`]: either the capsule type is not supported by this
468 /// platform, or the platform does not support capsule updates after exiting
469 /// boot services.
query_capsule_capabilities(capsule_header_array: &[&CapsuleHeader]) -> Result<CapsuleInfo>470 pub fn query_capsule_capabilities(capsule_header_array: &[&CapsuleHeader]) -> Result<CapsuleInfo> {
471 let rt = runtime_services_raw_panicking();
472 let rt = unsafe { rt.as_ref() };
473
474 let mut info = CapsuleInfo::default();
475 unsafe {
476 (rt.query_capsule_capabilities)(
477 capsule_header_array.as_ptr().cast(),
478 capsule_header_array.len(),
479 &mut info.maximum_capsule_size,
480 &mut info.reset_type,
481 )
482 .to_result_with_val(|| info)
483 }
484 }
485
486 /// Resets the computer.
487 ///
488 /// See [`ResetType`] for details of the various reset types.
489 ///
490 /// For a normal reset the value of `status` should be
491 /// [`Status::SUCCESS`]. Otherwise, an error code can be used.
492 ///
493 /// The `data` arg is usually `None`. Otherwise, it must contain a UCS-2
494 /// null-terminated string followed by additional binary data. For
495 /// [`ResetType::PLATFORM_SPECIFIC`], the binary data must be a vendor-specific
496 /// [`Guid`] that indicates the type of reset to perform.
497 ///
498 /// This function never returns.
reset(reset_type: ResetType, status: Status, data: Option<&[u8]>) -> !499 pub fn reset(reset_type: ResetType, status: Status, data: Option<&[u8]>) -> ! {
500 let rt = runtime_services_raw_panicking();
501 let rt = unsafe { rt.as_ref() };
502
503 let (size, data) = data
504 .map(|data| (data.len(), data.as_ptr()))
505 .unwrap_or((0, ptr::null()));
506
507 unsafe { (rt.reset_system)(reset_type, status, size, data) }
508 }
509
510 /// Changes the runtime addressing mode of EFI firmware from physical to
511 /// virtual. It is up to the caller to translate the old system table address
512 /// to a new virtual address and provide it for this function.
513 ///
514 /// If successful, this function will call [`set_system_table`] with
515 /// `new_system_table_virtual_addr`.
516 ///
517 /// [`set_system_table`]: table::set_system_table
518 ///
519 /// # Safety
520 ///
521 /// The caller must ensure the memory map is valid.
522 ///
523 /// # Errors
524 ///
525 /// * [`Status::UNSUPPORTED`]: either boot services haven't been exited, the
526 /// firmware's addressing mode is already virtual, or the firmware does not
527 /// support this operation.
528 /// * [`Status::NO_MAPPING`]: `map` is missing a required range.
529 /// * [`Status::NOT_FOUND`]: `map` contains an address that is not in the
530 /// current memory map.
set_virtual_address_map( map: &mut [MemoryDescriptor], new_system_table_virtual_addr: *const uefi_raw::table::system::SystemTable, ) -> Result531 pub unsafe fn set_virtual_address_map(
532 map: &mut [MemoryDescriptor],
533 new_system_table_virtual_addr: *const uefi_raw::table::system::SystemTable,
534 ) -> Result {
535 let rt = runtime_services_raw_panicking();
536 let rt = unsafe { rt.as_ref() };
537
538 // Unsafe Code Guidelines guarantees that there is no padding in an array or a slice
539 // between its elements if the element type is `repr(C)`, which is our case.
540 //
541 // See https://rust-lang.github.io/unsafe-code-guidelines/layout/arrays-and-slices.html
542 let map_size = core::mem::size_of_val(map);
543 let entry_size = core::mem::size_of::<MemoryDescriptor>();
544 let entry_version = MemoryDescriptor::VERSION;
545 let map_ptr = map.as_mut_ptr();
546 (rt.set_virtual_address_map)(map_size, entry_size, entry_version, map_ptr).to_result()?;
547
548 // Update the global system table pointer.
549 table::set_system_table(new_system_table_virtual_addr);
550
551 Ok(())
552 }
553
554 /// Date and time representation.
555 #[derive(Copy, Clone, Eq, PartialEq)]
556 #[repr(transparent)]
557 pub struct Time(uefi_raw::time::Time);
558
559 /// Input parameters for [`Time::new`].
560 #[derive(Copy, Clone, Debug)]
561 pub struct TimeParams {
562 /// Year in the range `1900..=9999`.
563 pub year: u16,
564
565 /// Month in the range `1..=12`.
566 pub month: u8,
567
568 /// Day in the range `1..=31`.
569 pub day: u8,
570
571 /// Hour in the range `0.=23`.
572 pub hour: u8,
573
574 /// Minute in the range `0..=59`.
575 pub minute: u8,
576
577 /// Second in the range `0..=59`.
578 pub second: u8,
579
580 /// Fraction of a second represented as nanoseconds in the range
581 /// `0..=999_999_999`.
582 pub nanosecond: u32,
583
584 /// Offset in minutes from UTC in the range `-1440..=1440`, or
585 /// local time if `None`.
586 pub time_zone: Option<i16>,
587
588 /// Daylight savings time information.
589 pub daylight: Daylight,
590 }
591
592 /// Error returned by [`Time`] methods. A bool value of `true` means
593 /// the specified field is outside its valid range.
594 #[allow(missing_docs)]
595 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
596 pub struct TimeError {
597 pub year: bool,
598 pub month: bool,
599 pub day: bool,
600 pub hour: bool,
601 pub minute: bool,
602 pub second: bool,
603 pub nanosecond: bool,
604 pub timezone: bool,
605 pub daylight: bool,
606 }
607
608 #[cfg(feature = "unstable")]
609 impl core::error::Error for TimeError {}
610
611 impl Display for TimeError {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result612 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
613 if self.year {
614 writeln!(f, "year not within `1900..=9999`")?;
615 }
616 if self.month {
617 writeln!(f, "month not within `1..=12")?;
618 }
619 if self.day {
620 writeln!(f, "day not within `1..=31`")?;
621 }
622 if self.hour {
623 writeln!(f, "hour not within `0..=23`")?;
624 }
625 if self.minute {
626 writeln!(f, "minute not within `0..=59`")?;
627 }
628 if self.second {
629 writeln!(f, "second not within `0..=59`")?;
630 }
631 if self.nanosecond {
632 writeln!(f, "nanosecond not within `0..=999_999_999`")?;
633 }
634 if self.timezone {
635 writeln!(
636 f,
637 "time_zone not `Time::UNSPECIFIED_TIMEZONE` nor within `-1440..=1440`"
638 )?;
639 }
640 if self.daylight {
641 writeln!(f, "unknown bits set for daylight")?;
642 }
643 Ok(())
644 }
645 }
646
647 impl Time {
648 /// Unspecified Timezone/local time.
649 const UNSPECIFIED_TIMEZONE: i16 = uefi_raw::time::Time::UNSPECIFIED_TIMEZONE;
650
651 /// Create a `Time` value. If a field is not in the valid range,
652 /// [`TimeError`] is returned.
new(params: TimeParams) -> core::result::Result<Self, TimeError>653 pub fn new(params: TimeParams) -> core::result::Result<Self, TimeError> {
654 let time = Self(uefi_raw::time::Time {
655 year: params.year,
656 month: params.month,
657 day: params.day,
658 hour: params.hour,
659 minute: params.minute,
660 second: params.second,
661 pad1: 0,
662 nanosecond: params.nanosecond,
663 time_zone: params.time_zone.unwrap_or(Self::UNSPECIFIED_TIMEZONE),
664 daylight: params.daylight,
665 pad2: 0,
666 });
667
668 time.is_valid().map(|_| time)
669 }
670
671 /// Create an invalid `Time` with all fields set to zero. This can
672 /// be used with [`FileInfo`] to indicate a field should not be
673 /// updated when calling [`File::set_info`].
674 ///
675 /// [`FileInfo`]: uefi::proto::media::file::FileInfo
676 /// [`File::set_info`]: uefi::proto::media::file::File::set_info
677 #[must_use]
invalid() -> Self678 pub const fn invalid() -> Self {
679 Self(uefi_raw::time::Time::invalid())
680 }
681
682 /// `Ok()` if all fields are within valid ranges, `Err(TimeError)` otherwise.
is_valid(&self) -> core::result::Result<(), TimeError>683 pub fn is_valid(&self) -> core::result::Result<(), TimeError> {
684 let mut err = TimeError::default();
685 if !(1900..=9999).contains(&self.year()) {
686 err.year = true;
687 }
688 if !(1..=12).contains(&self.month()) {
689 err.month = true;
690 }
691 if !(1..=31).contains(&self.day()) {
692 err.day = true;
693 }
694 if self.hour() > 23 {
695 err.hour = true;
696 }
697 if self.minute() > 59 {
698 err.minute = true;
699 }
700 if self.second() > 59 {
701 err.second = true;
702 }
703 if self.nanosecond() > 999_999_999 {
704 err.nanosecond = true;
705 }
706 if self.time_zone().is_some() && !((-1440..=1440).contains(&self.time_zone().unwrap())) {
707 err.timezone = true;
708 }
709 // All fields are false, i.e., within their valid range.
710 if err == TimeError::default() {
711 Ok(())
712 } else {
713 Err(err)
714 }
715 }
716
717 /// Query the year.
718 #[must_use]
year(&self) -> u16719 pub const fn year(&self) -> u16 {
720 self.0.year
721 }
722
723 /// Query the month.
724 #[must_use]
month(&self) -> u8725 pub const fn month(&self) -> u8 {
726 self.0.month
727 }
728
729 /// Query the day.
730 #[must_use]
day(&self) -> u8731 pub const fn day(&self) -> u8 {
732 self.0.day
733 }
734
735 /// Query the hour.
736 #[must_use]
hour(&self) -> u8737 pub const fn hour(&self) -> u8 {
738 self.0.hour
739 }
740
741 /// Query the minute.
742 #[must_use]
minute(&self) -> u8743 pub const fn minute(&self) -> u8 {
744 self.0.minute
745 }
746
747 /// Query the second.
748 #[must_use]
second(&self) -> u8749 pub const fn second(&self) -> u8 {
750 self.0.second
751 }
752
753 /// Query the nanosecond.
754 #[must_use]
nanosecond(&self) -> u32755 pub const fn nanosecond(&self) -> u32 {
756 self.0.nanosecond
757 }
758
759 /// Query the time offset in minutes from UTC, or None if using local time.
760 #[must_use]
time_zone(&self) -> Option<i16>761 pub const fn time_zone(&self) -> Option<i16> {
762 if self.0.time_zone == Self::UNSPECIFIED_TIMEZONE {
763 None
764 } else {
765 Some(self.0.time_zone)
766 }
767 }
768
769 /// Query the daylight savings time information.
770 #[must_use]
daylight(&self) -> Daylight771 pub const fn daylight(&self) -> Daylight {
772 self.0.daylight
773 }
774 }
775
776 impl Debug for Time {
fmt(&self, f: &mut Formatter) -> fmt::Result777 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
778 write!(
779 f,
780 "{:04}-{:02}-{:02} ",
781 self.0.year, self.0.month, self.0.day
782 )?;
783 write!(
784 f,
785 "{:02}:{:02}:{:02}.{:09}",
786 self.0.hour, self.0.minute, self.0.second, self.0.nanosecond
787 )?;
788 if self.0.time_zone == Self::UNSPECIFIED_TIMEZONE {
789 write!(f, ", Timezone=local")?;
790 } else {
791 write!(f, ", Timezone={}", self.0.time_zone)?;
792 }
793 write!(f, ", Daylight={:?}", self.0.daylight)
794 }
795 }
796
797 impl Display for Time {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result798 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
799 write!(f, "{}", self.0)
800 }
801 }
802
803 /// Error returned from failing to convert a byte slice into a [`Time`].
804 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
805 pub enum TimeByteConversionError {
806 /// One or more fields of the converted [`Time`] is invalid.
807 InvalidFields(TimeError),
808 /// The byte slice is not large enough to hold a [`Time`].
809 InvalidSize,
810 }
811
812 impl Display for TimeByteConversionError {
fmt(&self, f: &mut Formatter) -> fmt::Result813 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
814 match self {
815 Self::InvalidFields(error) => write!(f, "{error}"),
816 Self::InvalidSize => write!(
817 f,
818 "the byte slice is not large enough to hold a Time struct"
819 ),
820 }
821 }
822 }
823
824 impl TryFrom<&[u8]> for Time {
825 type Error = TimeByteConversionError;
826
try_from(bytes: &[u8]) -> core::result::Result<Self, Self::Error>827 fn try_from(bytes: &[u8]) -> core::result::Result<Self, Self::Error> {
828 if mem::size_of::<Self>() <= bytes.len() {
829 let year = u16::from_le_bytes(bytes[0..2].try_into().unwrap());
830 let month = bytes[2];
831 let day = bytes[3];
832 let hour = bytes[4];
833 let minute = bytes[5];
834 let second = bytes[6];
835 let nanosecond = u32::from_le_bytes(bytes[8..12].try_into().unwrap());
836 let time_zone = match i16::from_le_bytes(bytes[12..14].try_into().unwrap()) {
837 Self::UNSPECIFIED_TIMEZONE => None,
838 num => Some(num),
839 };
840 let daylight = Daylight::from_bits(bytes[14]).ok_or(
841 TimeByteConversionError::InvalidFields(TimeError {
842 daylight: true,
843 ..Default::default()
844 }),
845 )?;
846
847 let time_params = TimeParams {
848 year,
849 month,
850 day,
851 hour,
852 minute,
853 second,
854 nanosecond,
855 time_zone,
856 daylight,
857 };
858
859 Self::new(time_params).map_err(TimeByteConversionError::InvalidFields)
860 } else {
861 Err(TimeByteConversionError::InvalidSize)
862 }
863 }
864 }
865
866 /// Unique key for a variable.
867 #[cfg(feature = "alloc")]
868 #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
869 pub struct VariableKey {
870 /// Unique identifier for the vendor.
871 pub vendor: VariableVendor,
872
873 /// Name of the variable, unique with the vendor namespace.
874 pub name: CString16,
875 }
876
877 #[cfg(feature = "alloc")]
878 impl VariableKey {
879 /// Name of the variable.
880 #[deprecated = "Use the VariableKey.name field instead"]
name(&self) -> core::result::Result<&CStr16, crate::data_types::FromSliceWithNulError>881 pub fn name(&self) -> core::result::Result<&CStr16, crate::data_types::FromSliceWithNulError> {
882 Ok(&self.name)
883 }
884 }
885
886 #[cfg(feature = "alloc")]
887 impl Display for VariableKey {
fmt(&self, f: &mut Formatter) -> fmt::Result888 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
889 write!(f, "VariableKey {{ name: \"{}\", vendor: ", self.name)?;
890
891 if self.vendor == VariableVendor::GLOBAL_VARIABLE {
892 write!(f, "GLOBAL_VARIABLE")?;
893 } else {
894 write!(f, "{}", self.vendor.0)?;
895 }
896
897 write!(f, " }}")
898 }
899 }
900
901 /// Information about UEFI variable storage space returned by
902 /// [`query_variable_info`]. Note that the data here is
903 /// limited to a specific type of variable (as specified by the
904 /// `attributes` argument to `query_variable_info`).
905 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
906 pub struct VariableStorageInfo {
907 /// Maximum size in bytes of the storage space available for
908 /// variables of the specified type.
909 pub maximum_variable_storage_size: u64,
910
911 /// Remaining size in bytes of the storage space available for
912 /// variables of the specified type.
913 pub remaining_variable_storage_size: u64,
914
915 /// Maximum size of an individual variable of the specified type.
916 pub maximum_variable_size: u64,
917 }
918
919 /// Information about UEFI variable storage space returned by
920 /// [`query_capsule_capabilities`].
921 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
922 pub struct CapsuleInfo {
923 /// The maximum size in bytes that [`update_capsule`]
924 /// can support as input. Note that the size of an update capsule is composed of
925 /// all [`CapsuleHeader`]s and [CapsuleBlockDescriptor]s.
926 pub maximum_capsule_size: u64,
927
928 /// The type of reset required for the capsule update.
929 pub reset_type: ResetType,
930 }
931