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