1pub use nvram_services_ext::NvramServicesExt;
15
16use bitfield_struct::bitfield;
17use guid::Guid;
18use inspect::Inspect;
19use mesh::payload::Protobuf;
20use std::borrow::Cow;
21use thiserror::Error;
22use ucs2::Ucs2LeSlice;
23use ucs2::Ucs2ParseError;
24use uefi_nvram_specvars::signature_list;
25use uefi_nvram_specvars::signature_list::ParseSignatureLists;
26use uefi_nvram_storage::NextVariable;
27use uefi_nvram_storage::NvramStorageError;
28use uefi_nvram_storage::VmmNvramStorage;
29use uefi_specs::uefi::common::EfiStatus;
30use uefi_specs::uefi::nvram::EfiVariableAttributes;
31use uefi_specs::uefi::time::EFI_TIME;
32use zerocopy::FromBytes;
33use zerocopy::FromZeros;
34
35#[cfg(feature = "fuzzing")]
36pub mod auth_var_crypto;
37#[cfg(not(feature = "fuzzing"))]
38mod auth_var_crypto;
39mod nvram_services_ext;
40
41#[derive(Debug, Error)]
42pub enum NvramError {
43 #[error("storage backend error")]
44 NvramStorage(#[source] NvramStorageError),
45 #[error("variable name cannot be null/None")]
46 NameNull,
47 #[error("variable data of non-zero len cannot be null")]
48 DataNull,
49 #[error("variable name validation failed")]
50 NameValidation(#[from] Ucs2ParseError),
51 #[error("cannot pass empty string to SetVariable")]
52 NameEmpty,
53 #[error("attributes include non-spec values")]
54 AttributeNonSpec,
55 #[error("invalid runtime access")]
56 InvalidRuntimeAccess,
57 #[error("invalid attr: hardware error records are not supported")]
58 UnsupportedHardwareErrorRecord,
59 #[error("invalid attr: enhanced authenticated access unsupported")]
60 UnsupportedEnhancedAuthAccess,
61 #[error("invalid attr: volatile variables unsupported")]
62 UnsupportedVolatile,
63 #[error("attribute mismatch with existing variable")]
64 AttributeMismatch,
65 #[error("authenticated variable error")]
66 AuthError(#[from] AuthError),
67 #[error("updating SetupMode variable")]
68 UpdateSetupMode(#[source] NvramStorageError),
69 #[error("parsing signature list")]
70 SignatureList(#[from] signature_list::ParseError),
71}
72
73#[derive(Debug, Error)]
74pub enum AuthError {
75 #[error("data too short (cannot extract EFI_VARIABLE_AUTHENTICATION_2 header)")]
76 NotEnoughHdrData,
77 #[error("data too short (cannot extract WIN_CERTIFICATE_UEFI_GUID cert)")]
78 NotEnoughCertData,
79 #[error("invalid WIN_CERTIFICATE Header")]
80 InvalidWinCertHeader,
81 #[error("invalid WIN_CERTIFICATE_UEFI_GUID Header")]
82 InvalidWinCertUefiGuidHeader,
83 #[error("incorrect cert type (must be WIN_CERTIFICATE_UEFI_GUID)")]
84 IncorrectCertType,
85 #[error("incorrect timestamp values")]
86 IncorrectTimestamp,
87 #[error("new timestamp is not later than current timestamp")]
88 OldTimestamp,
89
90 #[error("current implementation cannot authenticate specified var")]
91 UnsupportedAuthVar,
92
93 #[error("could not verify auth var")]
94 CryptoError,
95
96 #[error("error in crypto payload format")]
97 CryptoFormat(#[source] auth_var_crypto::FormatError),
98}
99
100#[bitfield(u32)]
109#[derive(PartialEq)]
110pub struct SupportedAttrs {
111 pub non_volatile: bool,
112 pub bootservice_access: bool,
113 pub runtime_access: bool,
114 pub hardware_error_record: bool,
115 _reserved: bool,
116 pub time_based_authenticated_write_access: bool,
117 #[bits(26)]
118 _reserved2: u32,
119}
120
121impl SupportedAttrs {
122 pub fn contains_unsupported_bits(&self) -> bool {
123 u32::from(*self)
124 & !u32::from(
125 Self::new()
126 .with_non_volatile(true)
127 .with_bootservice_access(true)
128 .with_runtime_access(true)
129 .with_hardware_error_record(true)
130 .with_time_based_authenticated_write_access(true),
131 )
132 != 0
133 }
134}
135
136#[derive(Debug, Clone, Copy)]
138pub struct ParsedAuthVar<'a> {
139 pub name: &'a Ucs2LeSlice,
140 pub vendor: Guid,
141 pub attr: u32,
142 pub timestamp: EFI_TIME,
143 pub pkcs7_data: &'a [u8],
144 pub var_data: &'a [u8],
145}
146
147#[derive(Debug)]
153pub struct NvramResult<T>(pub T, pub EfiStatus, pub Option<NvramError>);
154
155impl<T> NvramResult<T> {
156 pub fn is_success(&self) -> bool {
157 matches!(self.1, EfiStatus::SUCCESS)
158 }
159}
160
161impl<T> std::fmt::Display for NvramResult<T> {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 match &self.2 {
164 Some(_) => write!(f, "{:?} (with error context)", self.1),
165 None => write!(f, "{:?}", self.1),
166 }
167 }
168}
169
170impl<T> std::error::Error for NvramResult<T>
171where
172 T: std::fmt::Debug,
173{
174 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
175 self.2
176 .as_ref()
177 .map(|s| s as &(dyn std::error::Error + 'static))
178 }
179}
180
181#[derive(Clone, Copy, Debug, Protobuf, Inspect)]
182enum RuntimeState {
183 PreBoot,
189 Boot,
191 Runtime,
193}
194
195impl RuntimeState {
196 fn is_pre_boot(&self) -> bool {
197 matches!(&self, RuntimeState::PreBoot)
198 }
199
200 fn is_boot(&self) -> bool {
201 matches!(&self, RuntimeState::Boot)
202 }
203
204 fn is_runtime(&self) -> bool {
205 matches!(&self, RuntimeState::Runtime)
206 }
207}
208
209#[derive(Debug, Inspect)]
226pub struct NvramSpecServices<S: VmmNvramStorage> {
227 storage: S,
228 runtime_state: RuntimeState,
229}
230
231impl<S: VmmNvramStorage> NvramSpecServices<S> {
232 pub fn new(storage: S) -> NvramSpecServices<S> {
234 NvramSpecServices {
235 storage,
236 runtime_state: RuntimeState::PreBoot,
237 }
238 }
239
240 pub async fn is_empty(&mut self) -> Result<bool, NvramStorageError> {
242 self.storage.is_empty().await
243 }
244
245 pub async fn update_setup_mode(&mut self) -> Result<(), NvramStorageError> {
259 use uefi_specs::uefi::nvram::vars::PK;
260 use uefi_specs::uefi::nvram::vars::SETUP_MODE;
261
262 let (pk_vendor, pk_name) = PK();
263 let (setup_mode_vendor, setup_mode_name) = SETUP_MODE();
264
265 let attr = EfiVariableAttributes::DEFAULT_ATTRIBUTES;
266 let timestamp = EFI_TIME::new_zeroed();
267 let data = match self.storage.get_variable(pk_name, pk_vendor).await? {
268 Some(_) => [0x00],
269 None => [0x01],
270 };
271
272 self.storage
273 .set_variable(
274 setup_mode_name,
275 setup_mode_vendor,
276 attr.into(),
277 data.to_vec(),
278 timestamp,
279 )
280 .await?;
281
282 Ok(())
283 }
284
285 pub fn exit_boot_services(&mut self) {
289 assert!(self.runtime_state.is_boot());
290 tracing::trace!("NVRAM has entered runtime mode");
291 self.runtime_state = RuntimeState::Runtime;
292 }
293
294 pub fn reset(&mut self) {
296 self.runtime_state = RuntimeState::PreBoot;
297 }
298
299 pub fn prepare_for_boot(&mut self) {
302 assert!(self.runtime_state.is_pre_boot());
303 tracing::trace!("NVRAM has entered boot mode");
304 self.runtime_state = RuntimeState::Boot;
305 }
306
307 async fn get_setup_mode(&mut self) -> Result<bool, NvramStorageError> {
308 use uefi_specs::uefi::nvram::vars::SETUP_MODE;
309
310 let (setup_mode_vendor, setup_mode_name) = SETUP_MODE();
311 let in_setup_mode = match self
312 .storage
313 .get_variable(setup_mode_name, setup_mode_vendor)
314 .await?
315 {
316 None => false,
317 Some((_, data, _)) => data.first().map(|b| *b == 0x01).unwrap_or(false),
318 };
319
320 Ok(in_setup_mode)
321 }
322
323 pub async fn uefi_get_variable(
344 &mut self,
345 name: Option<&[u8]>,
346 in_vendor: Guid,
347 out_attr: &mut u32,
348 in_out_data_size: &mut u32,
349 data_is_null: bool,
350 ) -> NvramResult<Option<Vec<u8>>> {
351 let name = match name {
352 Some(name) => {
353 Ucs2LeSlice::from_slice_with_nul(name).map_err(NvramError::NameValidation)
354 }
355 None => Err(NvramError::NameNull),
356 };
357
358 let name = match name {
359 Ok(name) => name,
360 Err(e) => return NvramResult(None, EfiStatus::INVALID_PARAMETER, Some(e)),
361 };
362
363 tracing::trace!(
364 ?in_vendor,
365 ?name,
366 in_out_data_size,
367 data_is_null,
368 "Get NVRAM variable",
369 );
370
371 let (attr, data) = match self.get_variable_inner(name, in_vendor).await {
372 Ok(Some((attr, data, _))) => (attr, data),
373 Ok(None) => return NvramResult(None, EfiStatus::NOT_FOUND, None),
374 Err((status, err)) => return NvramResult(None, status, err),
375 };
376
377 if self.runtime_state.is_runtime() && !attr.runtime_access() {
378 return NvramResult(
385 None,
386 EfiStatus::NOT_FOUND,
387 Some(NvramError::InvalidRuntimeAccess),
388 );
389 }
390
391 *out_attr = attr.into();
392 match (*in_out_data_size, data_is_null) {
393 (0, true) => *in_out_data_size = data.len() as u32,
394 (_, true) => return NvramResult(None, EfiStatus::INVALID_PARAMETER, None),
395 (_, false) => {
396 let guest_buf_len = *in_out_data_size as usize;
397 *in_out_data_size = data.len() as u32;
398 if guest_buf_len < data.len() {
399 return NvramResult(None, EfiStatus::BUFFER_TOO_SMALL, None);
400 }
401 }
402 }
403
404 NvramResult(Some(data), EfiStatus::SUCCESS, None)
405 }
406
407 async fn get_variable_inner(
408 &mut self,
409 name: &Ucs2LeSlice,
410 vendor: Guid,
411 ) -> Result<Option<(SupportedAttrs, Vec<u8>, EFI_TIME)>, (EfiStatus, Option<NvramError>)> {
412 match self.storage.get_variable(name, vendor).await {
413 Ok(None) => Ok(None),
414 Ok(Some((attr, data, timestamp))) => {
415 let attr = SupportedAttrs::from(attr);
416 assert!(
417 !attr.contains_unsupported_bits(),
418 "underlying storage should only ever contain valid attributes"
419 );
420
421 Ok(Some((attr, data, timestamp)))
422 }
423 Err(e) => {
424 let status = match &e {
425 NvramStorageError::Deserialize => EfiStatus::DEVICE_ERROR,
426 _ => panic!("unexpected NvramStorageError from get_variable"),
427 };
428 Err((status, Some(NvramError::NvramStorage(e))))
429 }
430 }
431 }
432
433 pub async fn uefi_set_variable(
454 &mut self,
455 name: Option<&[u8]>,
456 in_vendor: Guid,
457 in_attr: u32,
458 in_data_size: u32,
459 data: Option<Vec<u8>>,
460 ) -> NvramResult<()> {
461 let name = match name {
462 Some(name) => {
463 Ucs2LeSlice::from_slice_with_nul(name).map_err(NvramError::NameValidation)
464 }
465 None => Err(NvramError::NameNull),
466 };
467
468 let name = match name {
469 Ok(name) => name,
470 Err(e) => return NvramResult((), EfiStatus::INVALID_PARAMETER, Some(e)),
471 };
472
473 if name.as_bytes() == [0, 0] {
474 return NvramResult(
475 (),
476 EfiStatus::INVALID_PARAMETER,
477 Some(NvramError::NameEmpty),
478 );
479 }
480
481 tracing::trace!(
482 %in_vendor,
483 %name,
484 in_attr,
485 in_data_size,
486 data = if data.is_some() { "Some([..])" } else { "None" },
487 "Set NVRAM variable",
488 );
489
490 let attr = {
492 let attr = EfiVariableAttributes::from(in_attr);
494 if attr.contains_unsupported_bits() {
495 return NvramResult(
496 (),
497 EfiStatus::INVALID_PARAMETER,
498 Some(NvramError::AttributeNonSpec),
499 );
500 }
501
502 if attr.runtime_access() && !attr.bootservice_access() {
509 return NvramResult((), EfiStatus::INVALID_PARAMETER, None);
510 }
511
512 if attr.time_based_authenticated_write_access() && attr.enhanced_authenticated_access()
519 {
520 return NvramResult((), EfiStatus::INVALID_PARAMETER, None);
521 }
522
523 attr
524 };
525
526 {
529 if attr.hardware_error_record() {
530 return NvramResult(
531 (),
532 EfiStatus::UNSUPPORTED,
533 Some(NvramError::UnsupportedHardwareErrorRecord),
534 );
535 }
536
537 if attr.enhanced_authenticated_access() {
538 return NvramResult(
539 (),
540 EfiStatus::UNSUPPORTED,
541 Some(NvramError::UnsupportedEnhancedAuthAccess),
542 );
543 }
544
545 if attr.authenticated_write_access() {
551 return NvramResult((), EfiStatus::UNSUPPORTED, None);
552 }
553 }
554
555 let in_setup_mode = match self.get_setup_mode().await {
563 Ok(val) => val,
564 Err(err) => {
565 return NvramResult(
566 (),
567 EfiStatus::DEVICE_ERROR,
568 Some(NvramError::NvramStorage(err)),
569 );
570 }
571 };
572
573 if self.runtime_state.is_runtime() {
581 let missing_access_attrs = !(attr.runtime_access() || attr.bootservice_access());
595
596 if !missing_access_attrs {
597 if !attr.runtime_access() || !attr.non_volatile() {
598 return NvramResult(
599 (),
600 EfiStatus::INVALID_PARAMETER,
601 Some(NvramError::InvalidRuntimeAccess),
602 );
603 }
604 }
605 }
606
607 if !self.runtime_state.is_pre_boot() {
612 use uefi_specs::hyperv::nvram::vars as hyperv_vars;
613 use uefi_specs::uefi::nvram::vars as spec_vars;
614
615 #[rustfmt::skip]
616 let read_only_vars = [
617 spec_vars::SECURE_BOOT(),
622 spec_vars::SETUP_MODE(),
623 spec_vars::DBDEFAULT(),
624 hyperv_vars::SECURE_BOOT_ENABLE(),
627 hyperv_vars::CURRENT_POLICY(),
628 hyperv_vars::OS_LOADER_INDICATIONS_SUPPORTED(),
629 ];
630
631 let is_readonly = read_only_vars.into_iter().any(|v| {
632 v == (in_vendor, name)
647 });
648
649 if is_readonly {
650 return NvramResult((), EfiStatus::WRITE_PROTECTED, None);
651 }
652 }
653
654 let existing_var = match self.get_variable_inner(name, in_vendor).await {
658 Ok(v) => v,
659 Err((status, err)) => return NvramResult((), status, err),
660 };
661
662 let (in_data_size, data, timestamp) = {
663 if !attr.time_based_authenticated_write_access() {
664 let timestamp = EFI_TIME::new_zeroed();
666
667 (in_data_size, data, timestamp)
668 } else {
669 use uefi_specs::uefi::nvram::EFI_VARIABLE_AUTHENTICATION_2;
673 use uefi_specs::uefi::signing::EFI_CERT_TYPE_PKCS7_GUID;
674 use uefi_specs::uefi::signing::WIN_CERT_TYPE_EFI_GUID;
675 use uefi_specs::uefi::signing::WIN_CERTIFICATE_UEFI_GUID;
676
677 tracing::trace!(
678 "variable is attempting to use TIME_BASED_AUTHENTICATED_WRITE_ACCESS"
679 );
680
681 let data = match data {
683 Some(data) => data,
684 None => {
685 return NvramResult(
686 (),
687 EfiStatus::INVALID_PARAMETER,
688 Some(NvramError::DataNull),
689 );
690 }
691 };
692
693 let auth_hdr =
696 match EFI_VARIABLE_AUTHENTICATION_2::read_from_prefix(data.as_slice()).ok() {
697 Some((hdr, _)) => hdr,
698 None => {
699 return NvramResult(
700 (),
701 EfiStatus::SECURITY_VIOLATION,
702 Some(NvramError::AuthError(AuthError::NotEnoughHdrData)),
703 );
704 }
705 };
706 let timestamp = auth_hdr.timestamp;
707 let auth_info = auth_hdr.auth_info;
708
709 let (pkcs7_data, var_data) = {
712 let auth_info_offset = size_of_val(&auth_hdr.timestamp);
713
714 if data[auth_info_offset..].len() < (auth_info.header.length as usize) {
717 return NvramResult(
718 (),
719 EfiStatus::SECURITY_VIOLATION,
720 Some(NvramError::AuthError(AuthError::NotEnoughCertData)),
721 );
722 }
723 let (auth_info_hdr_and_cert, var_data) =
724 data[auth_info_offset..].split_at(auth_info.header.length as usize);
725
726 let pkcs7_data = match auth_info_hdr_and_cert
729 .get(size_of::<WIN_CERTIFICATE_UEFI_GUID>()..)
730 {
731 Some(data) => data,
732 None => {
733 return NvramResult(
734 (),
735 EfiStatus::SECURITY_VIOLATION,
736 Some(NvramError::AuthError(AuthError::NotEnoughCertData)),
737 );
738 }
739 };
740
741 (pkcs7_data, var_data)
742 };
743
744 if auth_info.header.revision != 0x0200 {
746 return NvramResult(
747 (),
748 EfiStatus::SECURITY_VIOLATION,
749 Some(NvramError::AuthError(AuthError::InvalidWinCertHeader)),
750 );
751 }
752
753 if auth_info.header.certificate_type != WIN_CERT_TYPE_EFI_GUID
755 || auth_info.cert_type != EFI_CERT_TYPE_PKCS7_GUID
756 {
757 return NvramResult(
758 (),
759 EfiStatus::SECURITY_VIOLATION,
760 Some(NvramError::AuthError(AuthError::IncorrectCertType)),
761 );
762 }
763
764 if timestamp.pad1 != 0
766 || timestamp.nanosecond != 0
767 || timestamp.timezone.0 != 0
768 || u8::from(timestamp.daylight) != 0
769 || timestamp.pad2 != 0
770 {
771 return NvramResult(
772 (),
773 EfiStatus::SECURITY_VIOLATION,
774 Some(NvramError::AuthError(AuthError::IncorrectTimestamp)),
775 );
776 }
777
778 let orig_timestamp = timestamp; let timestamp = {
783 let mut timestamp = timestamp;
784 if let Some((_, _, existing_timestamp)) = existing_var {
785 let is_newer = (
786 timestamp.year,
787 timestamp.month,
788 timestamp.day,
789 timestamp.hour,
790 timestamp.minute,
791 timestamp.second,
792 timestamp.nanosecond,
793 )
794 .cmp(&(
795 existing_timestamp.year,
796 existing_timestamp.month,
797 existing_timestamp.day,
798 existing_timestamp.hour,
799 existing_timestamp.minute,
800 existing_timestamp.second,
801 existing_timestamp.nanosecond,
802 ))
803 .is_gt();
804
805 if !is_newer {
806 if !attr.append_write() {
807 return NvramResult(
808 (),
809 EfiStatus::SECURITY_VIOLATION,
810 Some(NvramError::AuthError(AuthError::OldTimestamp)),
811 );
812 } else {
813 timestamp = existing_timestamp
814 }
815 }
816 }
817 timestamp
818 };
819
820 let pk_var = {
822 let (pk_vendor, pk_name) = uefi_specs::uefi::nvram::vars::PK();
823 match self.get_variable_inner(pk_name, pk_vendor).await {
824 Ok(v) => v,
825 Err((status, err)) => return NvramResult((), status, err),
826 }
827 };
828
829 let bypass_auth = self.runtime_state.is_pre_boot()
840 || (in_setup_mode
841 && uefi_specs::uefi::nvram::is_secure_boot_policy_var(in_vendor, name));
842
843 if pk_var.is_some() && !bypass_auth {
844 tracing::trace!("pk exists, attempting to actually authenticate var...");
845
846 let parsed_auth_var = ParsedAuthVar {
847 name,
848 vendor: in_vendor,
849 attr: attr.into(),
850 timestamp: orig_timestamp,
851 pkcs7_data,
852 var_data,
853 };
854
855 enum AuthVarKind {
858 Db,
859 PkKek,
860 Unsupported,
861 }
862
863 let var_kind = match (in_vendor, name) {
864 v if v == uefi_specs::uefi::nvram::vars::DB() => AuthVarKind::Db,
865 v if v == uefi_specs::uefi::nvram::vars::DBX() => AuthVarKind::Db,
866 v if v == uefi_specs::uefi::nvram::vars::PK() => AuthVarKind::PkKek,
867 v if v == uefi_specs::uefi::nvram::vars::KEK() => AuthVarKind::PkKek,
868 _ => AuthVarKind::Unsupported,
873 };
874
875 let auth_res = match var_kind {
876 AuthVarKind::Db => {
877 match self
884 .authenticate_var(
885 uefi_specs::uefi::nvram::vars::KEK(),
886 parsed_auth_var,
887 )
888 .await
889 {
890 Ok(res) => Ok(res),
891 Err(_) => {
893 self.authenticate_var(
894 uefi_specs::uefi::nvram::vars::PK(),
895 parsed_auth_var,
896 )
897 .await
898 }
899 }
900 }
901 AuthVarKind::PkKek => {
902 self.authenticate_var(
907 uefi_specs::uefi::nvram::vars::PK(),
908 parsed_auth_var,
909 )
910 .await
911 }
912 AuthVarKind::Unsupported => {
913 return NvramResult(
916 (),
917 EfiStatus::SECURITY_VIOLATION,
918 Some(NvramError::AuthError(AuthError::UnsupportedAuthVar)),
919 );
920 }
921 };
922
923 if let Err((status, err)) = auth_res {
924 return NvramResult((), status, err);
925 }
926 }
927
928 let total_auth_hdr_len =
932 size_of_val(&auth_hdr.timestamp) + (auth_info.header.length as usize);
933
934 (
935 in_data_size - total_auth_hdr_len as u32,
936 Some({
937 let mut data = data;
938 data.drain(..total_auth_hdr_len);
939 data
940 }),
941 timestamp,
942 )
943 }
944 };
945
946 #[derive(Debug)]
954 enum VariableOperation {
955 Set,
956 Append,
957 Delete,
958 }
959
960 let op = {
961 let is_doing_append = attr.append_write();
962 let is_doing_delete = {
963 let missing_access_attrs = !(attr.runtime_access() || attr.bootservice_access());
968
969 let zero_data_size = in_data_size == 0 && !is_doing_append;
977
978 missing_access_attrs || zero_data_size
979 };
980
981 if is_doing_append {
983 VariableOperation::Append
984 } else if is_doing_delete {
985 VariableOperation::Delete
986 } else {
987 VariableOperation::Set
988 }
989 };
990
991 tracing::trace!(?op, "SetVariable is performing");
992
993 let attr = attr.with_append_write(false);
995
996 let attr = SupportedAttrs::from(u32::from(attr));
999
1000 if let Some((existing_attr, _, _)) = existing_var {
1008 let missing_access_attrs = !(attr.runtime_access() || attr.bootservice_access());
1010 let is_delete_with_no_access =
1011 matches!(op, VariableOperation::Delete) && missing_access_attrs;
1012
1013 let requires_attr_match =
1019 existing_attr.time_based_authenticated_write_access() || !is_delete_with_no_access;
1020
1021 if requires_attr_match && attr != existing_attr {
1022 return NvramResult(
1023 (),
1024 EfiStatus::INVALID_PARAMETER,
1025 Some(NvramError::AttributeMismatch),
1026 );
1027 }
1028 }
1029
1030 let res = match op {
1031 VariableOperation::Append => {
1032 if !attr.non_volatile() {
1035 return NvramResult(
1036 (),
1037 EfiStatus::UNSUPPORTED,
1038 Some(NvramError::UnsupportedVolatile),
1039 );
1040 }
1041
1042 let mut data = match (in_data_size, data) {
1046 (0, _) => return NvramResult((), EfiStatus::SUCCESS, None),
1049 (_, None) => {
1051 return NvramResult((), EfiStatus::SUCCESS, Some(NvramError::DataNull));
1052 }
1053 (_, Some(data)) => data,
1054 };
1055
1056 if let Some((existing_attr, existing_data, _)) = existing_var {
1057 if self.runtime_state.is_runtime() && !existing_attr.runtime_access() {
1059 return NvramResult(
1061 (),
1062 EfiStatus::NOT_FOUND,
1063 Some(NvramError::InvalidRuntimeAccess),
1064 );
1065 }
1066
1067 if attr.time_based_authenticated_write_access() {
1078 use signature_list::SignatureDataPayload;
1079
1080 let existing_signatures = ParseSignatureLists::new(&existing_data)
1081 .collect_signature_set()
1082 .expect("existing var must contain valid list of EFI_SIGNATURE_LIST");
1083
1084 let filtered_signatures = ParseSignatureLists::new(&data)
1089 .collect_signature_lists(|header, sig| {
1090 let sig: &[u8] = match sig {
1091 SignatureDataPayload::X509(buf) => buf,
1092 SignatureDataPayload::Sha256(buf) => buf,
1093 };
1094
1095 !existing_signatures.contains(&(header, Cow::Borrowed(sig)))
1096 });
1097
1098 let filtered_signatures = match filtered_signatures {
1100 Ok(sigs) => sigs,
1101 Err(e) => {
1102 return NvramResult(
1103 (),
1104 EfiStatus::INVALID_PARAMETER,
1105 Some(NvramError::SignatureList(e)),
1106 );
1107 }
1108 };
1109
1110 let mut new_data = Vec::new();
1111 for list in filtered_signatures {
1112 list.extend_as_spec_signature_list(&mut new_data);
1113 }
1114
1115 data = new_data;
1117 }
1118 }
1119
1120 match self
1122 .storage
1123 .append_variable(name, in_vendor, data.clone(), timestamp)
1124 .await
1125 {
1126 Ok(true) => NvramResult((), EfiStatus::SUCCESS, None),
1127 Ok(false) => NvramResult((), EfiStatus::NOT_FOUND, None),
1128 Err(e) => {
1129 let status = match &e {
1130 NvramStorageError::Commit(_) => EfiStatus::DEVICE_ERROR,
1131 NvramStorageError::OutOfSpace => EfiStatus::OUT_OF_RESOURCES,
1132 NvramStorageError::VariableNameTooLong => EfiStatus::INVALID_PARAMETER,
1133 NvramStorageError::VariableDataTooLong => EfiStatus::INVALID_PARAMETER,
1134 _ => {
1135 panic!("unexpected NvramStorageError from append_variable")
1136 }
1137 };
1138
1139 NvramResult((), status, Some(NvramError::NvramStorage(e)))
1140 }
1141 }
1142 }
1143 VariableOperation::Delete => {
1144 if let Some((existing_attr, _, _)) = existing_var {
1145 if self.runtime_state.is_runtime() && !existing_attr.runtime_access() {
1147 return NvramResult(
1149 (),
1150 EfiStatus::NOT_FOUND,
1151 Some(NvramError::InvalidRuntimeAccess),
1152 );
1153 }
1154 }
1155
1156 match self.storage.remove_variable(name, in_vendor).await {
1158 Ok(true) => NvramResult((), EfiStatus::SUCCESS, None),
1159 Ok(false) => NvramResult((), EfiStatus::NOT_FOUND, None),
1160 Err(e) => {
1161 let status = match &e {
1162 NvramStorageError::Commit(_) => EfiStatus::DEVICE_ERROR,
1163 NvramStorageError::OutOfSpace => EfiStatus::OUT_OF_RESOURCES,
1164 NvramStorageError::VariableNameTooLong => EfiStatus::INVALID_PARAMETER,
1165 NvramStorageError::VariableDataTooLong => EfiStatus::INVALID_PARAMETER,
1166 _ => {
1167 panic!("unexpected NvramStorageError from remove_variable")
1168 }
1169 };
1170
1171 NvramResult((), status, Some(NvramError::NvramStorage(e)))
1172 }
1173 }
1174 }
1175 VariableOperation::Set => {
1176 if !attr.non_volatile() {
1186 use uefi_specs::hyperv::nvram::vars as hyperv_vars;
1187 use uefi_specs::uefi::nvram::vars::DBDEFAULT;
1188 use uefi_specs::uefi::nvram::vars::SECURE_BOOT;
1189 let allowed_volatile = [
1190 hyperv_vars::OS_LOADER_INDICATIONS(),
1191 hyperv_vars::OS_LOADER_INDICATIONS_SUPPORTED(),
1192 DBDEFAULT(),
1193 SECURE_BOOT(),
1194 ];
1195
1196 let is_allowed = allowed_volatile.into_iter().any(|v| v == (in_vendor, name));
1197
1198 if !is_allowed {
1199 return NvramResult(
1200 (),
1201 EfiStatus::UNSUPPORTED,
1202 Some(NvramError::UnsupportedVolatile),
1203 );
1204 }
1205 }
1206
1207 let data = match data {
1209 Some(data) => data,
1210 None => {
1211 return NvramResult(
1212 (),
1213 EfiStatus::INVALID_PARAMETER,
1214 Some(NvramError::DataNull),
1215 );
1216 }
1217 };
1218
1219 if let Some((existing_attr, _, _)) = existing_var {
1220 if self.runtime_state.is_runtime() && !existing_attr.runtime_access() {
1222 return NvramResult(
1240 (),
1241 EfiStatus::WRITE_PROTECTED,
1242 Some(NvramError::InvalidRuntimeAccess),
1243 );
1244 }
1245 }
1246
1247 match self
1249 .storage
1250 .set_variable(name, in_vendor, attr.into(), data.clone(), timestamp)
1251 .await
1252 {
1253 Ok(_) => NvramResult((), EfiStatus::SUCCESS, None),
1254 Err(e) => {
1255 let status = match &e {
1256 NvramStorageError::Commit(_) => EfiStatus::DEVICE_ERROR,
1257 NvramStorageError::OutOfSpace => EfiStatus::OUT_OF_RESOURCES,
1258 NvramStorageError::VariableNameTooLong => EfiStatus::INVALID_PARAMETER,
1259 NvramStorageError::VariableDataTooLong => EfiStatus::INVALID_PARAMETER,
1260 _ => panic!("unexpected NvramStorageError from set_variable"),
1261 };
1262
1263 NvramResult((), status, Some(NvramError::NvramStorage(e)))
1264 }
1265 }
1266 }
1267 };
1268
1269 if res.is_success() && (in_vendor, name) == uefi_specs::uefi::nvram::vars::PK() {
1272 if let Err(e) = self.update_setup_mode().await {
1273 return NvramResult(
1274 (),
1275 EfiStatus::DEVICE_ERROR,
1276 Some(NvramError::UpdateSetupMode(e)),
1277 );
1278 }
1279 }
1280
1281 res
1282 }
1283
1284 async fn authenticate_var(
1287 &mut self,
1288 (key_var_name, key_var_vendor): (Guid, &Ucs2LeSlice),
1289 auth_var: ParsedAuthVar<'_>,
1290 ) -> Result<(), (EfiStatus, Option<NvramError>)> {
1291 let signature_lists = match self
1292 .get_variable_inner(key_var_vendor, key_var_name)
1293 .await?
1294 {
1295 Some((_, data, _)) => data,
1296 None => return Err((EfiStatus::SECURITY_VIOLATION, None)),
1297 };
1298
1299 match auth_var_crypto::authenticate_variable(&signature_lists, auth_var) {
1301 Ok(true) => Ok(()),
1302 Ok(false) => Err((
1303 EfiStatus::SECURITY_VIOLATION,
1304 Some(NvramError::AuthError(AuthError::CryptoError)),
1305 )),
1306 Err(e) if e.key_var_error() => {
1307 panic!("existing signature list must contain valid data: {}", e)
1308 }
1309 Err(e) => Err((
1311 EfiStatus::SECURITY_VIOLATION,
1312 Some(NvramError::AuthError(AuthError::CryptoFormat(e))),
1313 )),
1314 }
1315 }
1316
1317 pub async fn uefi_get_next_variable(
1334 &mut self,
1335 in_out_name_size: &mut u32,
1336 name: Option<&[u8]>,
1337 vendor: Guid,
1338 ) -> NvramResult<Option<(Vec<u8>, Guid)>> {
1339 let name = match name {
1340 Some(name) => {
1341 Ucs2LeSlice::from_slice_with_nul(name).map_err(NvramError::NameValidation)
1342 }
1343 None => Err(NvramError::NameNull),
1344 };
1345
1346 let name = match name {
1347 Ok(name) => name,
1348 Err(e) => return NvramResult(None, EfiStatus::INVALID_PARAMETER, Some(e)),
1349 };
1350
1351 tracing::trace!(?vendor, ?name, in_out_name_size, "Next NVRAM variable",);
1352
1353 let mut res = if name.as_bytes() == [0, 0] {
1356 self.storage.next_variable(None).await
1357 } else {
1358 self.storage.next_variable(Some((name, vendor))).await
1359 };
1360
1361 loop {
1362 match res {
1363 Ok(NextVariable::EndOfList) => {
1364 return NvramResult(None, EfiStatus::NOT_FOUND, None);
1365 }
1366 Ok(NextVariable::InvalidKey) => {
1367 return NvramResult(None, EfiStatus::INVALID_PARAMETER, None);
1368 }
1369 Ok(NextVariable::Exists { name, vendor, attr }) => {
1370 let attr = EfiVariableAttributes::from(attr);
1371 assert!(
1372 !attr.contains_unsupported_bits(),
1373 "underlying storage should only ever contain valid attributes"
1374 );
1375
1376 if self.runtime_state.is_runtime() && !attr.runtime_access() {
1384 res = self
1385 .storage
1386 .next_variable(Some((name.as_ref(), vendor)))
1387 .await;
1388 continue;
1389 }
1390
1391 let guest_buf_len = *in_out_name_size as usize;
1392 *in_out_name_size = name.as_bytes().len() as u32;
1393 if guest_buf_len < name.as_bytes().len() {
1394 return NvramResult(None, EfiStatus::BUFFER_TOO_SMALL, None);
1395 }
1396
1397 return NvramResult(
1398 Some((name.into_inner(), vendor)),
1399 EfiStatus::SUCCESS,
1400 None,
1401 );
1402 }
1403 Err(e) => {
1404 let status = match &e {
1405 NvramStorageError::Deserialize => EfiStatus::DEVICE_ERROR,
1406 _ => panic!("unexpected NvramStorageError from next_variable"),
1407 };
1408
1409 return NvramResult(None, status, Some(NvramError::NvramStorage(e)));
1410 }
1411 }
1412 }
1413 }
1414}
1415
1416mod save_restore {
1417 use super::*;
1418 use vmcore::save_restore::RestoreError;
1419 use vmcore::save_restore::SaveError;
1420 use vmcore::save_restore::SaveRestore;
1421
1422 mod state {
1423 use mesh::payload::Protobuf;
1424 use uefi_nvram_storage::in_memory::InMemoryNvram;
1425 use vmcore::save_restore::SaveRestore;
1426
1427 #[derive(Protobuf)]
1428 #[mesh(package = "firmware.uefi.nvram.spec")]
1429 pub enum SavedRuntimeState {
1430 #[mesh(1)]
1431 PreBoot,
1432 #[mesh(2)]
1433 Boot,
1434 #[mesh(3)]
1435 Runtime,
1436 }
1437
1438 #[derive(Protobuf)]
1439 #[mesh(package = "firmware.uefi.nvram.spec")]
1440 pub struct SavedState {
1441 #[mesh(1)]
1442 pub runtime_state: SavedRuntimeState,
1443 #[mesh(2)]
1444 pub storage: <InMemoryNvram as SaveRestore>::SavedState,
1445 }
1446 }
1447
1448 impl<S: VmmNvramStorage> SaveRestore for NvramSpecServices<S> {
1449 type SavedState = state::SavedState;
1450
1451 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
1452 Ok(state::SavedState {
1453 runtime_state: match self.runtime_state {
1454 RuntimeState::PreBoot => state::SavedRuntimeState::PreBoot,
1455 RuntimeState::Boot => state::SavedRuntimeState::Boot,
1456 RuntimeState::Runtime => state::SavedRuntimeState::Runtime,
1457 },
1458 storage: self.storage.save()?,
1459 })
1460 }
1461
1462 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
1463 let state::SavedState {
1464 runtime_state,
1465 storage,
1466 } = state;
1467
1468 self.runtime_state = match runtime_state {
1469 state::SavedRuntimeState::PreBoot => RuntimeState::PreBoot,
1470 state::SavedRuntimeState::Boot => RuntimeState::Boot,
1471 state::SavedRuntimeState::Runtime => RuntimeState::Runtime,
1472 };
1473 self.storage.restore(storage)?;
1474
1475 Ok(())
1476 }
1477 }
1478}
1479
1480#[cfg(test)]
1481mod test {
1482 use super::*;
1483 use uefi_nvram_storage::in_memory::InMemoryNvram;
1484 use pal_async::async_test;
1489 use wchar::wchz;
1490
1491 use zerocopy::IntoBytes;
1492
1493 #[async_trait::async_trait]
1496 trait NvramServicesTestExt {
1497 async fn set_test_var(&mut self, name: &[u8], attr: u32, data: &[u8]) -> NvramResult<()>;
1498 async fn get_test_var(&mut self, name: &[u8]) -> NvramResult<(u32, Option<Vec<u8>>)>;
1499 async fn get_next_test_var(
1500 &mut self,
1501 name: Option<Vec<u8>>,
1502 ) -> NvramResult<Option<Vec<u8>>>;
1503 }
1504
1505 #[async_trait::async_trait]
1506 impl<S: VmmNvramStorage> NvramServicesTestExt for NvramSpecServices<S> {
1507 async fn set_test_var(&mut self, name: &[u8], attr: u32, data: &[u8]) -> NvramResult<()> {
1508 let vendor = Guid::default();
1509
1510 self.uefi_set_variable(
1511 Some(name),
1512 vendor,
1513 attr,
1514 data.len() as u32,
1515 Some(data.to_vec()),
1516 )
1517 .await
1518 }
1519
1520 async fn get_test_var(&mut self, name: &[u8]) -> NvramResult<(u32, Option<Vec<u8>>)> {
1521 let vendor = Guid::default();
1522
1523 let mut attr = 0;
1524 let NvramResult(data, status, err) = self
1525 .uefi_get_variable(Some(name), vendor, &mut attr, &mut 256, false)
1526 .await;
1527
1528 NvramResult((attr, data), status, err)
1529 }
1530
1531 async fn get_next_test_var(
1532 &mut self,
1533 name: Option<Vec<u8>>,
1534 ) -> NvramResult<Option<Vec<u8>>> {
1535 let vendor = Guid::default();
1536
1537 let NvramResult(name_guid, status, err) = self
1538 .uefi_get_next_variable(&mut 256, name.as_deref(), vendor)
1539 .await;
1540
1541 NvramResult(name_guid.map(|(n, _)| n.clone()), status, err)
1542 }
1543 }
1544
1545 trait NvramRetTestExt<T> {
1546 fn unwrap_efi_success(self) -> T;
1547 }
1548
1549 impl<T> NvramRetTestExt<T> for NvramResult<T> {
1550 #[track_caller]
1551 fn unwrap_efi_success(self) -> T {
1552 let NvramResult(val, status, err) = self;
1553 if let Some(err) = err {
1554 panic!("{}", err)
1555 }
1556 assert_eq!(status, EfiStatus::SUCCESS);
1557 val
1558 }
1559 }
1560
1561 #[async_test]
1562 async fn runtime_vars() {
1563 let nvram_storage = InMemoryNvram::new();
1564 let mut nvram = NvramSpecServices::new(nvram_storage);
1565
1566 nvram.prepare_for_boot();
1567
1568 let name1 = wchz!(u16, "var1").as_bytes();
1569 let name2 = wchz!(u16, "var2").as_bytes();
1570 let name3 = wchz!(u16, "var3").as_bytes();
1571 let name4 = wchz!(u16, "var4").as_bytes();
1572
1573 let dummy_data = b"dummy data".to_vec();
1574
1575 let runtime_attr = (EfiVariableAttributes::DEFAULT_ATTRIBUTES).into();
1576 let no_runtime_attr = EfiVariableAttributes::DEFAULT_ATTRIBUTES
1577 .with_runtime_access(false)
1578 .into();
1579
1580 nvram
1582 .set_test_var(name1, runtime_attr, &dummy_data)
1583 .await
1584 .unwrap_efi_success();
1585 nvram
1586 .set_test_var(name2, no_runtime_attr, &dummy_data)
1587 .await
1588 .unwrap_efi_success();
1589 nvram
1590 .set_test_var(name3, runtime_attr, &dummy_data)
1591 .await
1592 .unwrap_efi_success();
1593 nvram
1594 .set_test_var(name4, no_runtime_attr, &dummy_data)
1595 .await
1596 .unwrap_efi_success();
1597
1598 let (attr, data) = nvram.get_test_var(name1).await.unwrap_efi_success();
1602 assert_eq!(attr, runtime_attr);
1603 assert_eq!(data, Some(dummy_data.clone()));
1604
1605 let (attr, data) = nvram.get_test_var(name2).await.unwrap_efi_success();
1606 assert_eq!(attr, no_runtime_attr);
1607 assert_eq!(data, Some(dummy_data.clone()));
1608
1609 let (attr, data) = nvram.get_test_var(name3).await.unwrap_efi_success();
1610 assert_eq!(attr, runtime_attr);
1611 assert_eq!(data, Some(dummy_data.clone()));
1612
1613 let (attr, data) = nvram.get_test_var(name4).await.unwrap_efi_success();
1614 assert_eq!(attr, no_runtime_attr);
1615 assert_eq!(data, Some(dummy_data.clone()));
1616
1617 let mut name = Some(wchz!(u16, "").as_bytes().into());
1619 name = nvram.get_next_test_var(name).await.unwrap_efi_success();
1620 assert_eq!(name, Some(name1.into()));
1621
1622 name = nvram.get_next_test_var(name).await.unwrap_efi_success();
1623 assert_eq!(name, Some(name2.into()));
1624
1625 name = nvram.get_next_test_var(name).await.unwrap_efi_success();
1626 assert_eq!(name, Some(name3.into()));
1627
1628 name = nvram.get_next_test_var(name).await.unwrap_efi_success();
1629 assert_eq!(name, Some(name4.into()));
1630
1631 let NvramResult(name, status, err) = nvram.get_next_test_var(name).await;
1632 assert!(name.is_none());
1633 assert_eq!(status, EfiStatus::NOT_FOUND);
1634 assert!(err.is_none());
1635
1636 nvram.exit_boot_services();
1638
1639 let NvramResult(_, status, err) = nvram
1641 .set_test_var(
1642 wchz!(u16, "non-volatile").as_bytes(),
1643 no_runtime_attr,
1644 &dummy_data,
1645 )
1646 .await;
1647 assert_eq!(status, EfiStatus::INVALID_PARAMETER);
1648 assert!(matches!(err, Some(NvramError::InvalidRuntimeAccess)));
1649
1650 let (attr, data) = nvram.get_test_var(name1).await.unwrap_efi_success();
1652 assert_eq!(attr, runtime_attr);
1653 assert_eq!(data, Some(dummy_data.clone()));
1654
1655 let NvramResult((attr, data), status, err) = nvram.get_test_var(name2).await;
1656 assert_eq!(attr, 0);
1657 assert_eq!(data, None);
1658 assert_eq!(status, EfiStatus::NOT_FOUND);
1659 assert!(matches!(err, Some(NvramError::InvalidRuntimeAccess)));
1660
1661 let (attr, data) = nvram.get_test_var(name3).await.unwrap_efi_success();
1662 assert_eq!(attr, runtime_attr);
1663 assert_eq!(data, Some(dummy_data));
1664
1665 let NvramResult((attr, data), status, err) = nvram.get_test_var(name4).await;
1666 assert_eq!(attr, 0);
1667 assert_eq!(data, None);
1668 assert_eq!(status, EfiStatus::NOT_FOUND);
1669 assert!(matches!(err, Some(NvramError::InvalidRuntimeAccess)));
1670
1671 let mut name = Some(wchz!(u16, "").as_bytes().into());
1673 name = nvram.get_next_test_var(name).await.unwrap_efi_success();
1674 assert_eq!(name, Some(name1.into()));
1675
1676 name = nvram.get_next_test_var(name).await.unwrap_efi_success();
1679 assert_eq!(name, Some(name3.into()));
1680
1681 let NvramResult(name, status, err) = nvram.get_next_test_var(name).await;
1684 assert!(name.is_none());
1685 assert_eq!(status, EfiStatus::NOT_FOUND);
1686 assert!(err.is_none());
1687 }
1688
1689 #[async_test]
1690 async fn delete_with_mismatched_attributes_fails() {
1691 let nvram_storage = InMemoryNvram::new();
1692 let mut nvram = NvramSpecServices::new(nvram_storage);
1693
1694 nvram.prepare_for_boot();
1695
1696 let name = wchz!(u16, "TestVar").as_bytes();
1697 let dummy_data = b"test data".to_vec();
1698
1699 let original_attr = EfiVariableAttributes::DEFAULT_ATTRIBUTES.into();
1701
1702 nvram
1703 .set_test_var(name, original_attr, &dummy_data)
1704 .await
1705 .unwrap_efi_success();
1706
1707 let (attr, data) = nvram.get_test_var(name).await.unwrap_efi_success();
1709 assert_eq!(attr, original_attr);
1710 assert_eq!(data, Some(dummy_data.clone()));
1711
1712 let wrong_attr = EfiVariableAttributes::DEFAULT_ATTRIBUTES
1714 .with_runtime_access(false)
1715 .into();
1716 let NvramResult(_, status, err) = nvram.set_test_var(name, wrong_attr, &[]).await;
1717 assert_eq!(status, EfiStatus::INVALID_PARAMETER);
1718 assert!(matches!(err, Some(NvramError::AttributeMismatch)));
1719
1720 let (attr, data) = nvram.get_test_var(name).await.unwrap_efi_success();
1722 assert_eq!(attr, original_attr);
1723 assert_eq!(data, Some(dummy_data.clone()));
1724
1725 nvram
1727 .set_test_var(name, original_attr, &[])
1728 .await
1729 .unwrap_efi_success();
1730
1731 let NvramResult((attr, data), status, err) = nvram.get_test_var(name).await;
1733 assert_eq!(attr, 0);
1734 assert_eq!(data, None);
1735 assert_eq!(status, EfiStatus::NOT_FOUND);
1736 assert!(err.is_none());
1737 }
1738
1739 #[async_test]
1740 async fn delete_non_authenticated_variable_with_attributes_requires_match() {
1741 let nvram_storage = InMemoryNvram::new();
1742 let mut nvram = NvramSpecServices::new(nvram_storage);
1743
1744 nvram.prepare_for_boot();
1745
1746 let name = wchz!(u16, "RegularVar").as_bytes();
1747 let dummy_data = b"regular data".to_vec();
1748
1749 let regular_attr = EfiVariableAttributes::DEFAULT_ATTRIBUTES.into();
1751
1752 nvram
1753 .set_test_var(name, regular_attr, &dummy_data)
1754 .await
1755 .unwrap_efi_success();
1756
1757 let (attr, data) = nvram.get_test_var(name).await.unwrap_efi_success();
1759 assert_eq!(attr, regular_attr);
1760 assert_eq!(data, Some(dummy_data.clone()));
1761
1762 let wrong_attr = EfiVariableAttributes::DEFAULT_ATTRIBUTES
1764 .with_runtime_access(false)
1765 .into();
1766 let NvramResult(_, status, err) = nvram.set_test_var(name, wrong_attr, &[]).await;
1767 assert_eq!(status, EfiStatus::INVALID_PARAMETER);
1768 assert!(matches!(err, Some(NvramError::AttributeMismatch)));
1769
1770 let (attr, data) = nvram.get_test_var(name).await.unwrap_efi_success();
1772 assert_eq!(attr, regular_attr);
1773 assert_eq!(data, Some(dummy_data.clone()));
1774
1775 let no_access_attr = 0; nvram
1778 .set_test_var(name, no_access_attr, &dummy_data)
1779 .await
1780 .unwrap_efi_success();
1781
1782 let NvramResult((attr, data), status, err) = nvram.get_test_var(name).await;
1784 assert_eq!(attr, 0);
1785 assert_eq!(data, None);
1786 assert_eq!(status, EfiStatus::NOT_FOUND);
1787 assert!(err.is_none());
1788 }
1789
1790 #[async_test]
1791 async fn delete_non_authenticated_variable_with_no_access_attributes() {
1792 let nvram_storage = InMemoryNvram::new();
1793 let mut nvram = NvramSpecServices::new(nvram_storage);
1794
1795 nvram.prepare_for_boot();
1796
1797 let name = wchz!(u16, "TestVar").as_bytes();
1798 let dummy_data = b"test data".to_vec();
1799
1800 let regular_attr = EfiVariableAttributes::DEFAULT_ATTRIBUTES.into();
1802
1803 nvram
1804 .set_test_var(name, regular_attr, &dummy_data)
1805 .await
1806 .unwrap_efi_success();
1807
1808 let no_access_attr = 0;
1811 nvram
1812 .set_test_var(name, no_access_attr, &dummy_data)
1813 .await
1814 .unwrap_efi_success();
1815
1816 let NvramResult((attr, data), status, err) = nvram.get_test_var(name).await;
1818 assert_eq!(attr, 0);
1819 assert_eq!(data, None);
1820 assert_eq!(status, EfiStatus::NOT_FOUND);
1821 assert!(err.is_none());
1822 }
1823
1824 #[async_test]
1825 async fn delete_with_zero_data_size() {
1826 let nvram_storage = InMemoryNvram::new();
1827 let mut nvram = NvramSpecServices::new(nvram_storage);
1828
1829 nvram.prepare_for_boot();
1830
1831 let name = wchz!(u16, "ZeroDataVar").as_bytes();
1832 let dummy_data = b"some data".to_vec();
1833
1834 let attr = EfiVariableAttributes::DEFAULT_ATTRIBUTES.into();
1836
1837 nvram
1838 .set_test_var(name, attr, &dummy_data)
1839 .await
1840 .unwrap_efi_success();
1841
1842 nvram
1844 .set_test_var(name, attr, &[])
1845 .await
1846 .unwrap_efi_success();
1847
1848 let NvramResult((ret_attr, data), status, err) = nvram.get_test_var(name).await;
1850 assert_eq!(ret_attr, 0);
1851 assert_eq!(data, None);
1852 assert_eq!(status, EfiStatus::NOT_FOUND);
1853 assert!(err.is_none());
1854 }
1855
1856 #[async_test]
1857 async fn delete_runtime_variable_at_runtime_requires_runtime_access() {
1858 let nvram_storage = InMemoryNvram::new();
1859 let mut nvram = NvramSpecServices::new(nvram_storage);
1860
1861 nvram.prepare_for_boot();
1862
1863 let name1 = wchz!(u16, "RuntimeVar").as_bytes();
1864 let name2 = wchz!(u16, "BootVar").as_bytes();
1865 let dummy_data = b"data".to_vec();
1866
1867 let runtime_attr = EfiVariableAttributes::DEFAULT_ATTRIBUTES.into();
1869 nvram
1870 .set_test_var(name1, runtime_attr, &dummy_data)
1871 .await
1872 .unwrap_efi_success();
1873
1874 let boot_attr = EfiVariableAttributes::DEFAULT_ATTRIBUTES
1876 .with_runtime_access(false)
1877 .into();
1878 nvram
1879 .set_test_var(name2, boot_attr, &dummy_data)
1880 .await
1881 .unwrap_efi_success();
1882
1883 nvram.exit_boot_services();
1885
1886 nvram
1888 .set_test_var(name1, runtime_attr, &[])
1889 .await
1890 .unwrap_efi_success();
1891
1892 let NvramResult(_, status, err) = nvram.set_test_var(name2, boot_attr, &[]).await;
1896 assert_eq!(status, EfiStatus::INVALID_PARAMETER);
1897 assert!(matches!(err, Some(NvramError::InvalidRuntimeAccess)));
1898
1899 let NvramResult(_, status, err) = nvram.set_test_var(name2, 0, &dummy_data).await;
1903 assert_eq!(status, EfiStatus::NOT_FOUND);
1904 assert!(matches!(err, Some(NvramError::InvalidRuntimeAccess)));
1905
1906 let NvramResult((attr, data), status, err) = nvram.get_test_var(name2).await;
1909 assert_eq!(attr, 0);
1910 assert_eq!(data, None);
1911 assert_eq!(status, EfiStatus::NOT_FOUND);
1912 assert!(matches!(err, Some(NvramError::InvalidRuntimeAccess)));
1913 }
1914
1915 #[async_test]
1916 async fn append_requires_attribute_match() {
1917 let nvram_storage = InMemoryNvram::new();
1918 let mut nvram = NvramSpecServices::new(nvram_storage);
1919
1920 nvram.prepare_for_boot();
1921
1922 let name = wchz!(u16, "AppendVar").as_bytes();
1923 let initial_data = b"initial".to_vec();
1924 let append_data = b"appended".to_vec();
1925
1926 let attr = EfiVariableAttributes::DEFAULT_ATTRIBUTES.into();
1928 nvram
1929 .set_test_var(name, attr, &initial_data)
1930 .await
1931 .unwrap_efi_success();
1932
1933 let wrong_attr = EfiVariableAttributes::DEFAULT_ATTRIBUTES
1935 .with_runtime_access(false)
1936 .with_append_write(true)
1937 .into();
1938 let NvramResult(_, status, err) = nvram.set_test_var(name, wrong_attr, &append_data).await;
1939 assert_eq!(status, EfiStatus::INVALID_PARAMETER);
1940 assert!(matches!(err, Some(NvramError::AttributeMismatch)));
1941
1942 let append_attr = EfiVariableAttributes::DEFAULT_ATTRIBUTES
1944 .with_append_write(true)
1945 .into();
1946 nvram
1947 .set_test_var(name, append_attr, &append_data)
1948 .await
1949 .unwrap_efi_success();
1950
1951 let (ret_attr, data) = nvram.get_test_var(name).await.unwrap_efi_success();
1953 assert_eq!(ret_attr, attr); let mut expected = initial_data;
1955 expected.extend_from_slice(&append_data);
1956 assert_eq!(data, Some(expected));
1957 }
1958}