firmware_uefi/service/nvram/spec_services/
nvram_services_ext.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use super::NvramError;
5use super::NvramResult;
6use super::NvramSpecServices;
7use guid::Guid;
8use ucs2::Ucs2LeSlice;
9use uefi_nvram_storage::InspectableNvramStorage;
10use uefi_specs::uefi::common::EfiStatus;
11
12/// Extension trait around `NvramServices` that makes it easier to use the API
13/// outside the context of the UEFI device.
14///
15/// This trait provides various helper methods that make it easier to get/set
16/// nvram variables without worrying about the nitty-gritty details of UCS-2
17/// string encoding, pointer sizes/nullness, etc...
18#[async_trait::async_trait]
19pub trait NvramServicesExt {
20    /// Get a variable identified by `name` (as a Rust string) + `vendor`,
21    /// returning the variable's attributes and data.
22    #[allow(dead_code)]
23    async fn get_variable(
24        &mut self,
25        vendor: Guid,
26        name: &str,
27    ) -> Result<(u32, Vec<u8>), (EfiStatus, Option<NvramError>)>;
28
29    /// Get a variable identified by `name` (as a UCS-2 string) + `vendor`,
30    /// returning the variable's attributes and data.
31    async fn get_variable_ucs2(
32        &mut self,
33        vendor: Guid,
34        name: &Ucs2LeSlice,
35    ) -> Result<(u32, Vec<u8>), (EfiStatus, Option<NvramError>)>;
36
37    /// Set a variable identified by `name` (as a Rust string) + `vendor` with
38    /// the specified `attr` and `data`.
39    async fn set_variable(
40        &mut self,
41        vendor: Guid,
42        name: &str,
43        attr: u32,
44        data: Vec<u8>,
45    ) -> Result<(), (EfiStatus, Option<NvramError>)>;
46
47    /// Set a variable identified by `name` (as a UCS-2 string) + `vendor` with
48    /// the specified `attr` and `data`.
49    async fn set_variable_ucs2(
50        &mut self,
51        vendor: Guid,
52        name: &Ucs2LeSlice,
53        attr: u32,
54        data: Vec<u8>,
55    ) -> Result<(), (EfiStatus, Option<NvramError>)>;
56}
57
58#[async_trait::async_trait]
59impl<S: InspectableNvramStorage> NvramServicesExt for NvramSpecServices<S> {
60    async fn get_variable(
61        &mut self,
62        vendor: Guid,
63        name: &str,
64    ) -> Result<(u32, Vec<u8>), (EfiStatus, Option<NvramError>)> {
65        let name = ucs2::Ucs2LeVec::from(name);
66        self.get_variable_ucs2(vendor, &name).await
67    }
68
69    async fn get_variable_ucs2(
70        &mut self,
71        vendor: Guid,
72        name: &Ucs2LeSlice,
73    ) -> Result<(u32, Vec<u8>), (EfiStatus, Option<NvramError>)> {
74        let mut attr = 0;
75        // the low level UEFI APIs includes the `in_out_data_size` parameter so
76        // that it can perform validation logic to ensure the user-provided
77        // buffer is large enough to store the variable data.
78        //
79        // this validation logic isn't relevant to the high level API, as
80        // consumers can simply use the returned Rust reference directly
81        // (without having to copy it into another buffer).
82        let mut in_out_data_size = u32::MAX;
83        let NvramResult(data, status, err) = self
84            .uefi_get_variable(
85                Some(name.as_bytes()),
86                vendor,
87                &mut attr,
88                &mut in_out_data_size,
89                false,
90            )
91            .await;
92
93        if matches!(status, EfiStatus::SUCCESS) {
94            Ok((attr, data.expect("data will not be None on EFI_SUCCESS")))
95        } else {
96            Err((status, err))
97        }
98    }
99
100    async fn set_variable(
101        &mut self,
102        vendor: Guid,
103        name: &str,
104        attr: u32,
105        data: Vec<u8>,
106    ) -> Result<(), (EfiStatus, Option<NvramError>)> {
107        let name = ucs2::Ucs2LeVec::from(name);
108        self.set_variable_ucs2(vendor, &name, attr, data).await
109    }
110
111    async fn set_variable_ucs2(
112        &mut self,
113        vendor: Guid,
114        name: &Ucs2LeSlice,
115        attr: u32,
116        data: Vec<u8>,
117    ) -> Result<(), (EfiStatus, Option<NvramError>)> {
118        let NvramResult((), status, err) = self
119            .uefi_set_variable(
120                Some(name.as_bytes()),
121                vendor,
122                attr,
123                data.len() as u32,
124                Some(data),
125            )
126            .await;
127
128        if matches!(status, EfiStatus::SUCCESS) {
129            Ok(())
130        } else {
131            Err((status, err))
132        }
133    }
134}