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::VmmNvramStorage;
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    #[allow(dead_code)]
32    async fn get_variable_ucs2(
33        &mut self,
34        vendor: Guid,
35        name: &Ucs2LeSlice,
36    ) -> Result<(u32, Vec<u8>), (EfiStatus, Option<NvramError>)>;
37
38    /// Set a variable identified by `name` (as a Rust string) + `vendor` with
39    /// the specified `attr` and `data`.
40    async fn set_variable(
41        &mut self,
42        vendor: Guid,
43        name: &str,
44        attr: u32,
45        data: Vec<u8>,
46    ) -> Result<(), (EfiStatus, Option<NvramError>)>;
47
48    /// Set a variable identified by `name` (as a UCS-2 string) + `vendor` with
49    /// the specified `attr` and `data`.
50    async fn set_variable_ucs2(
51        &mut self,
52        vendor: Guid,
53        name: &Ucs2LeSlice,
54        attr: u32,
55        data: Vec<u8>,
56    ) -> Result<(), (EfiStatus, Option<NvramError>)>;
57}
58
59#[async_trait::async_trait]
60impl<S: VmmNvramStorage> NvramServicesExt for NvramSpecServices<S> {
61    async fn get_variable(
62        &mut self,
63        vendor: Guid,
64        name: &str,
65    ) -> Result<(u32, Vec<u8>), (EfiStatus, Option<NvramError>)> {
66        let name = ucs2::Ucs2LeVec::from(name);
67        self.get_variable_ucs2(vendor, &name).await
68    }
69
70    async fn get_variable_ucs2(
71        &mut self,
72        vendor: Guid,
73        name: &Ucs2LeSlice,
74    ) -> Result<(u32, Vec<u8>), (EfiStatus, Option<NvramError>)> {
75        let mut attr = 0;
76        // the low level UEFI APIs includes the `in_out_data_size` parameter so
77        // that it can perform validation logic to ensure the user-provided
78        // buffer is large enough to store the variable data.
79        //
80        // this validation logic isn't relevant to the high level API, as
81        // consumers can simply use the returned Rust reference directly
82        // (without having to copy it into another buffer).
83        let mut in_out_data_size = u32::MAX;
84        let NvramResult(data, status, err) = self
85            .uefi_get_variable(
86                Some(name.as_bytes()),
87                vendor,
88                &mut attr,
89                &mut in_out_data_size,
90                false,
91            )
92            .await;
93
94        if matches!(status, EfiStatus::SUCCESS) {
95            Ok((attr, data.expect("data will not be None on EFI_SUCCESS")))
96        } else {
97            Err((status, err))
98        }
99    }
100
101    async fn set_variable(
102        &mut self,
103        vendor: Guid,
104        name: &str,
105        attr: u32,
106        data: Vec<u8>,
107    ) -> Result<(), (EfiStatus, Option<NvramError>)> {
108        let name = ucs2::Ucs2LeVec::from(name);
109        self.set_variable_ucs2(vendor, &name, attr, data).await
110    }
111
112    async fn set_variable_ucs2(
113        &mut self,
114        vendor: Guid,
115        name: &Ucs2LeSlice,
116        attr: u32,
117        data: Vec<u8>,
118    ) -> Result<(), (EfiStatus, Option<NvramError>)> {
119        let NvramResult((), status, err) = self
120            .uefi_set_variable(
121                Some(name.as_bytes()),
122                vendor,
123                attr,
124                data.len() as u32,
125                Some(data),
126            )
127            .await;
128
129        if matches!(status, EfiStatus::SUCCESS) {
130            Ok(())
131        } else {
132            Err((status, err))
133        }
134    }
135}