1#![cfg_attr(all(not(test), not(feature = "mockall")), no_std)]
14
15extern crate alloc;
16
17pub mod variable_services;
19
20#[cfg(any(test, feature = "mockall"))]
21use mockall::automock;
22
23use alloc::vec::Vec;
24use core::{
25 ffi::c_void,
26 marker::PhantomData,
27 ptr,
28 sync::atomic::{AtomicPtr, Ordering},
29};
30
31use r_efi::efi;
32use variable_services::{GetVariableStatus, VariableInfo};
33
34#[derive(Debug)]
40pub struct StandardRuntimeServices<'a> {
41 efi_runtime_services: AtomicPtr<efi::RuntimeServices>,
42 _lifetime_marker: PhantomData<&'a efi::RuntimeServices>,
43}
44
45impl<'a> StandardRuntimeServices<'a> {
46 pub const fn new(efi_runtime_services: &'a efi::RuntimeServices) -> Self {
48 Self {
50 efi_runtime_services: AtomicPtr::new(efi_runtime_services as *const _ as *mut _),
51 _lifetime_marker: PhantomData,
52 }
53 }
54
55 pub const fn new_uninit() -> Self {
58 Self { efi_runtime_services: AtomicPtr::new(ptr::null_mut()), _lifetime_marker: PhantomData }
59 }
60
61 pub fn initialize(&'a self, efi_runtime_services: &'a efi::RuntimeServices) {
65 if self.efi_runtime_services.load(Ordering::Relaxed).is_null() {
66 self.efi_runtime_services.store(efi_runtime_services as *const _ as *mut _, Ordering::SeqCst)
68 } else {
69 debug_assert!(false, "Runtime services is already initialized.");
70 }
71 }
72
73 fn efi_runtime_services(&self) -> &efi::RuntimeServices {
76 unsafe {
78 self.efi_runtime_services
79 .load(Ordering::SeqCst)
80 .as_ref::<'a>()
81 .expect("Runtime services is not initialized.")
82 }
83 }
84}
85
86unsafe impl Sync for StandardRuntimeServices<'static> {}
88unsafe impl Send for StandardRuntimeServices<'static> {}
90
91#[cfg_attr(any(test, feature = "mockall"), automock)]
92
93pub trait RuntimeServices: Sized {
95 fn set_variable<T>(&self, name: &[u16], namespace: &efi::Guid, attributes: u32, data: &T) -> Result<(), efi::Status>
100 where
101 T: AsRef<[u8]> + 'static,
102 {
103 if !name.iter().position(|&c| c == 0).is_some() {
104 debug_assert!(false, "Name passed into set_variable is not null-terminated.");
105 return Err(efi::Status::INVALID_PARAMETER);
106 }
107
108 let mut name_vec = name.to_vec();
110
111 unsafe { self.set_variable_unchecked(name_vec.as_mut_slice(), namespace, attributes, data.as_ref()) }
112 }
113
114 fn get_variable<T>(
121 &self,
122 name: &[u16],
123 namespace: &efi::Guid,
124 size_hint: Option<usize>,
125 ) -> Result<(T, u32), efi::Status>
126 where
127 T: TryFrom<Vec<u8>> + 'static,
128 {
129 if !name.iter().position(|&c| c == 0).is_some() {
130 debug_assert!(false, "Name passed into get_variable is not null-terminated.");
131 return Err(efi::Status::INVALID_PARAMETER);
132 }
133
134 let mut name_vec = name.to_vec();
136
137 let mut data = Vec::<u8>::new();
140 if size_hint.is_some() {
141 data.resize(size_hint.unwrap(), 0);
142 }
143
144 let mut first_attempt = true;
150 loop {
151 unsafe {
152 let status = self.get_variable_unchecked(
153 name_vec.as_mut_slice(),
154 namespace,
155 if data.len() == 0 { None } else { Some(&mut data) },
156 );
157
158 match status {
159 GetVariableStatus::Success { data_size: _, attributes } => match T::try_from(data) {
160 Ok(d) => return Ok((d, attributes)),
161 Err(_) => return Err(efi::Status::INVALID_PARAMETER),
162 },
163 GetVariableStatus::BufferTooSmall { data_size, attributes: _ } => {
164 if first_attempt {
165 first_attempt = false;
166 data.resize(data_size, 10);
167 } else {
168 return Err(efi::Status::BUFFER_TOO_SMALL);
169 }
170 }
171 GetVariableStatus::Error(e) => {
172 return Err(e);
173 }
174 }
175 }
176 }
177 }
178
179 fn get_variable_size_and_attributes(
181 &self,
182 name: &[u16],
183 namespace: &efi::Guid,
184 ) -> Result<(usize, u32), efi::Status> {
185 if !name.iter().position(|&c| c == 0).is_some() {
186 debug_assert!(false, "Name passed into set_variable is not null-terminated.");
187 return Err(efi::Status::INVALID_PARAMETER);
188 }
189
190 let mut name_vec = name.to_vec();
192
193 unsafe {
194 match self.get_variable_unchecked(name_vec.as_mut_slice(), namespace, None) {
195 GetVariableStatus::BufferTooSmall { data_size, attributes } => Ok((data_size, attributes)),
196 GetVariableStatus::Error(e) => Err(e),
197 GetVariableStatus::Success { data_size, attributes } => {
198 debug_assert!(false, "GetVariable call with zero-sized buffer returned Success.");
199 Ok((data_size, attributes))
200 }
201 }
202 }
203 }
204
205 fn get_next_variable_name(
214 &self,
215 prev_name: &[u16],
216 prev_namespace: &efi::Guid,
217 ) -> Result<(Vec<u16>, efi::Guid), efi::Status> {
218 if prev_name.len() == 0 {
219 debug_assert!(false, "Zero-length name passed into get_next_variable_name.");
220 return Err(efi::Status::INVALID_PARAMETER);
221 }
222
223 let mut next_name = Vec::<u16>::new();
224 let mut next_namespace: efi::Guid = efi::Guid::from_bytes(&[0x0; 16]);
225
226 unsafe {
227 self.get_next_variable_name_unchecked(&prev_name, &prev_namespace, &mut next_name, &mut next_namespace)?;
228 };
229
230 Ok((next_name, next_namespace))
231 }
232
233 fn query_variable_info(&self, attributes: u32) -> Result<VariableInfo, efi::Status>;
238
239 unsafe fn set_variable_unchecked(
245 &self,
246 name: &mut [u16],
247 namespace: &efi::Guid,
248 attributes: u32,
249 data: &[u8],
250 ) -> Result<(), efi::Status>;
251
252 unsafe fn get_variable_unchecked<'a>(
258 &self,
259 name: &mut [u16],
260 namespace: &efi::Guid,
261 data: Option<&'a mut [u8]>,
262 ) -> GetVariableStatus;
263
264 unsafe fn get_next_variable_name_unchecked(
274 &self,
275 prev_name: &[u16],
276 prev_namespace: &efi::Guid,
277 next_name: &mut Vec<u16>,
278 next_namespace: &mut efi::Guid,
279 ) -> Result<(), efi::Status>;
280}
281
282impl RuntimeServices for StandardRuntimeServices<'_> {
283 unsafe fn set_variable_unchecked(
284 &self,
285 name: &mut [u16],
286 namespace: &efi::Guid,
287 attributes: u32,
288 data: &[u8],
289 ) -> Result<(), efi::Status> {
290 let set_variable = self.efi_runtime_services().set_variable;
291 if set_variable as usize == 0 {
292 debug_assert!(false, "SetVariable has not initialized in the Runtime Services Table.");
293 return Err(efi::Status::NOT_FOUND);
294 }
295
296 let status = set_variable(
297 name.as_mut_ptr(),
298 namespace as *const _ as *mut _,
299 attributes,
300 data.len(),
301 data.as_ptr() as *mut c_void,
302 );
303
304 if status.is_error() {
305 Err(status)
306 } else {
307 Ok(())
308 }
309 }
310
311 unsafe fn get_variable_unchecked(
312 &self,
313 name: &mut [u16],
314 namespace: &efi::Guid,
315 data: Option<&mut [u8]>,
316 ) -> GetVariableStatus {
317 let get_variable = self.efi_runtime_services().get_variable;
318 if get_variable as usize == 0 {
319 debug_assert!(false, "GetVariable has not initialized in the Runtime Services Table.");
320 return GetVariableStatus::Error(efi::Status::NOT_FOUND);
321 }
322
323 let mut data_size: usize = match data {
324 Some(ref d) => d.len(),
325 None => 0,
326 };
327 let mut attributes: u32 = 0;
328
329 let status = get_variable(
330 name.as_mut_ptr(),
331 namespace as *const _ as *mut _,
332 ptr::addr_of_mut!(attributes),
333 ptr::addr_of_mut!(data_size),
334 match data {
335 Some(d) => d.as_ptr() as *mut c_void,
336 None => ptr::null_mut() as *mut c_void,
337 },
338 );
339
340 if status == efi::Status::BUFFER_TOO_SMALL {
341 return GetVariableStatus::BufferTooSmall { data_size: data_size, attributes: attributes };
342 } else if status.is_error() {
343 return GetVariableStatus::Error(status);
344 }
345
346 GetVariableStatus::Success { data_size: data_size, attributes: attributes }
347 }
348
349 unsafe fn get_next_variable_name_unchecked(
350 &self,
351 prev_name: &[u16],
352 prev_namespace: &efi::Guid,
353 next_name: &mut Vec<u16>,
354 next_namespace: &mut efi::Guid,
355 ) -> Result<(), efi::Status> {
356 let get_next_variable_name = self.efi_runtime_services().get_next_variable_name;
357 if get_next_variable_name as usize == 0 {
358 debug_assert!(false, "GetNextVariableName has not initialized in the Runtime Services Table.");
359 return Err(efi::Status::NOT_FOUND);
360 }
361
362 if next_name.len() < prev_name.len() {
364 next_name.resize(prev_name.len(), 0);
365 }
366 next_name[..prev_name.len()].clone_from_slice(prev_name);
367 next_namespace.clone_from(prev_namespace);
368
369 let mut next_name_size: usize = next_name.len();
370
371 let mut first_try: bool = true;
375 loop {
376 let status =
377 get_next_variable_name(ptr::addr_of_mut!(next_name_size), next_name.as_mut_ptr(), next_namespace);
378
379 if status == efi::Status::BUFFER_TOO_SMALL && first_try {
380 first_try = false;
381
382 assert!(
383 next_name_size > next_name.len(),
384 "get_next_variable_name requested smaller buffer on BUFFER_TOO_SMALL."
385 );
386
387 next_name.resize(next_name_size, 0);
389
390 next_name[..prev_name.len()].clone_from_slice(prev_name);
392 next_namespace.clone_from(prev_namespace);
393 } else if status.is_error() {
394 return Err(status);
395 } else {
396 return Ok(());
397 }
398 }
399 }
400
401 fn query_variable_info(&self, attributes: u32) -> Result<VariableInfo, efi::Status> {
402 let query_variable_info = self.efi_runtime_services().query_variable_info;
403 if query_variable_info as usize == 0 {
404 debug_assert!(false, "QueryVariableInfo has not initialized in the Runtime Services Table.");
405 return Err(efi::Status::NOT_FOUND);
406 }
407
408 let mut var_info = VariableInfo {
409 maximum_variable_storage_size: 0,
410 remaining_variable_storage_size: 0,
411 maximum_variable_size: 0,
412 };
413
414 let status = query_variable_info(
415 attributes,
416 ptr::addr_of_mut!(var_info.maximum_variable_storage_size),
417 ptr::addr_of_mut!(var_info.remaining_variable_storage_size),
418 ptr::addr_of_mut!(var_info.maximum_variable_size),
419 );
420
421 if status.is_error() {
422 return Err(status);
423 } else {
424 return Ok(var_info);
425 }
426 }
427}
428
429#[cfg(test)]
430pub(crate) mod test {
431 use efi;
432
433 use super::*;
434 use core::{mem, slice};
435
436 macro_rules! runtime_services {
437 ($($efi_services:ident = $efi_service_fn:ident),*) => {{
438 static RUNTIME_SERVICE: StandardRuntimeServices = StandardRuntimeServices::new_uninit();
439 let efi_runtime_services = unsafe {
440 #[allow(unused_mut)]
441 let mut rs = mem::MaybeUninit::<efi::RuntimeServices>::zeroed();
442 $(
443 rs.assume_init_mut().$efi_services = $efi_service_fn;
444 )*
445 rs.assume_init()
446 };
447 RUNTIME_SERVICE.initialize(&efi_runtime_services);
448 &RUNTIME_SERVICE
449 }};
450 }
451
452 pub(crate) use runtime_services;
453
454 #[test]
455 #[should_panic(expected = "Runtime services is not initialized.")]
456 fn test_that_accessing_uninit_runtime_services_should_panic() {
457 let rs = StandardRuntimeServices::new_uninit();
458 rs.efi_runtime_services();
459 }
460
461 #[test]
462 #[should_panic(expected = "Runtime services is already initialized.")]
463 fn test_that_initializing_runtime_services_multiple_time_should_panic() {
464 let efi_rs = unsafe { mem::MaybeUninit::<efi::RuntimeServices>::zeroed().as_ptr().as_ref().unwrap() };
465 let rs = StandardRuntimeServices::new_uninit();
466 rs.initialize(efi_rs);
467 rs.initialize(efi_rs);
468 }
469
470 pub const DUMMY_FIRST_NAME: [u16; 3] = [0x1000, 0x1020, 0x0000];
471 pub const DUMMY_NON_NULL_TERMINATED_NAME: [u16; 3] = [0x1000, 0x1020, 0x1040];
472 pub const DUMMY_EMPTY_NAME: [u16; 1] = [0x0000];
473 pub const DUMMY_ZERO_LENGTH_NAME: [u16; 0] = [];
474 pub const DUMMY_SECOND_NAME: [u16; 5] = [0x1001, 0x1022, 0x1043, 0x1064, 0x0000];
475 pub const DUMMY_UNKNOWN_NAME: [u16; 3] = [0x2000, 0x2020, 0x0000];
476
477 pub const DUMMY_NODE: [u8; 6] = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
478 pub const DUMMY_FIRST_NAMESPACE: efi::Guid = efi::Guid::from_fields(0, 0, 0, 0, 0, &DUMMY_NODE);
479 pub const DUMMY_SECOND_NAMESPACE: efi::Guid = efi::Guid::from_fields(1, 0, 0, 0, 0, &DUMMY_NODE);
480
481 pub const DUMMY_ATTRIBUTES: u32 = 0x1234;
482 pub const DUMMY_INVALID_ATTRIBUTES: u32 = 0x2345;
483
484 pub const DUMMY_DATA: u32 = 0xDEADBEEF;
485 pub const DUMMY_DATA_REPR_SIZE: usize = mem::size_of::<u32>();
486
487 pub const DUMMY_MAXIMUM_VARIABLE_STORAGE_SIZE: u64 = 0x11111111_11111111;
488 pub const DUMMY_REMAINING_VARIABLE_STORAGE_SIZE: u64 = 0x22222222_22222222;
489 pub const DUMMY_MAXIMUM_VARIABLE_SIZE: u64 = 0x33333333_33333333;
490
491 #[derive(Debug)]
492 pub struct DummyVariableType {
493 pub value: u32,
494 }
495
496 impl AsRef<[u8]> for DummyVariableType {
497 fn as_ref(&self) -> &[u8] {
498 unsafe { slice::from_raw_parts::<u8>(ptr::addr_of!(self.value) as *mut u8, mem::size_of::<u32>()) }
499 }
500 }
501
502 impl TryFrom<Vec<u8>> for DummyVariableType {
503 type Error = &'static str;
504
505 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
506 assert!(value.len() == mem::size_of::<u32>());
507
508 Ok(DummyVariableType { value: u32::from_ne_bytes(value[0..4].try_into().unwrap()) })
509 }
510 }
511
512 pub extern "efiapi" fn mock_efi_get_variable(
520 name: *mut u16,
521 namespace: *mut efi::Guid,
522 attributes: *mut u32,
523 data_size: *mut usize,
524 data: *mut c_void,
525 ) -> efi::Status {
526 unsafe {
527 if DUMMY_UNKNOWN_NAME.iter().enumerate().all(|(i, &c)| *name.offset(i as isize) == c) {
528 return efi::Status::NOT_FOUND;
529 }
530
531 assert_eq!(
534 DUMMY_FIRST_NAME.iter().enumerate().all(|(i, &c)| *name.offset(i as isize) == c),
535 true,
536 "Variable name does not match expected."
537 );
538
539 assert_eq!(*namespace, DUMMY_FIRST_NAMESPACE);
540
541 *attributes = DUMMY_ATTRIBUTES;
542
543 if *data_size < DUMMY_DATA_REPR_SIZE {
544 *data_size = DUMMY_DATA_REPR_SIZE;
545 return efi::Status::BUFFER_TOO_SMALL;
546 }
547
548 *data_size = DUMMY_DATA_REPR_SIZE;
549 *(data as *mut u32) = DUMMY_DATA;
550 }
551
552 efi::Status::SUCCESS
553 }
554
555 pub extern "efiapi" fn mock_efi_set_variable(
562 name: *mut u16,
563 namespace: *mut efi::Guid,
564 attributes: u32,
565 data_size: usize,
566 data: *mut c_void,
567 ) -> efi::Status {
568 unsafe {
569 if *name == 0 {
571 return efi::Status::INVALID_PARAMETER;
572 }
573
574 if DUMMY_UNKNOWN_NAME.iter().enumerate().all(|(i, &c)| *name.offset(i as isize) == c) {
575 return efi::Status::NOT_FOUND;
576 }
577
578 assert_eq!(
581 DUMMY_FIRST_NAME.iter().enumerate().all(|(i, &c)| *name.offset(i as isize) == c),
582 true,
583 "Variable name does not match expected."
584 );
585
586 assert_eq!(*namespace, DUMMY_FIRST_NAMESPACE);
587 assert_eq!(attributes, DUMMY_ATTRIBUTES);
588 assert_eq!(data_size, DUMMY_DATA_REPR_SIZE);
589 assert_eq!(*(data as *mut u32), DUMMY_DATA);
590 }
591
592 efi::Status::SUCCESS
593 }
594
595 pub extern "efiapi" fn mock_efi_get_next_variable_name(
604 name_size: *mut usize,
605 name: *mut u16,
606 namespace: *mut efi::Guid,
607 ) -> efi::Status {
608 unsafe {
610 if !slice::from_raw_parts(name, *name_size).iter().position(|&c| c == 0).is_some() {
612 return efi::Status::INVALID_PARAMETER;
613 }
614
615 if DUMMY_UNKNOWN_NAME.iter().enumerate().all(|(i, &c)| *name.offset(i as isize) == c) {
616 return efi::Status::NOT_FOUND;
617 }
618
619 if *name == 0 {
621 if *name_size < DUMMY_FIRST_NAME.len() {
622 *name_size = DUMMY_FIRST_NAME.len();
623 return efi::Status::BUFFER_TOO_SMALL;
624 }
625
626 *name_size = DUMMY_FIRST_NAME.len();
627 ptr::copy_nonoverlapping(DUMMY_FIRST_NAME.as_ptr(), name, DUMMY_FIRST_NAME.len());
628 *namespace = DUMMY_FIRST_NAMESPACE;
629
630 return efi::Status::SUCCESS;
631 }
632
633 if DUMMY_FIRST_NAME.iter().enumerate().all(|(i, &c)| *name.offset(i as isize) == c) {
635 assert_eq!(*namespace, DUMMY_FIRST_NAMESPACE);
636
637 if *name_size < DUMMY_SECOND_NAME.len() {
638 *name_size = DUMMY_SECOND_NAME.len();
639 return efi::Status::BUFFER_TOO_SMALL;
640 }
641
642 *name_size = DUMMY_SECOND_NAME.len();
643 ptr::copy_nonoverlapping(DUMMY_SECOND_NAME.as_ptr(), name, DUMMY_SECOND_NAME.len());
644 *namespace = DUMMY_SECOND_NAMESPACE;
645
646 return efi::Status::SUCCESS;
647 }
648
649 if DUMMY_SECOND_NAME.iter().enumerate().all(|(i, &c)| *name.offset(i as isize) == c) {
652 assert_eq!(*namespace, DUMMY_SECOND_NAMESPACE);
653 return efi::Status::NOT_FOUND;
654 }
655
656 assert!(false, "Variable name does not match any of expected.");
658 }
659
660 efi::Status::SUCCESS
661 }
662
663 pub extern "efiapi" fn mock_efi_query_variable_info(
671 attributes: u32,
672 maximum_variable_storage_size: *mut u64,
673 remaining_variable_storage_size: *mut u64,
674 maximum_variable_size: *mut u64,
675 ) -> efi::Status {
676 if attributes == DUMMY_INVALID_ATTRIBUTES {
677 return efi::Status::INVALID_PARAMETER;
678 }
679
680 assert_eq!(attributes, DUMMY_ATTRIBUTES);
683
684 unsafe {
685 *maximum_variable_storage_size = DUMMY_MAXIMUM_VARIABLE_STORAGE_SIZE;
686 *remaining_variable_storage_size = DUMMY_REMAINING_VARIABLE_STORAGE_SIZE;
687 *maximum_variable_size = DUMMY_MAXIMUM_VARIABLE_SIZE;
688 }
689
690 efi::Status::SUCCESS
691 }
692
693 #[test]
694 fn test_get_variable() {
695 let rs: &StandardRuntimeServices<'_> = runtime_services!(get_variable = mock_efi_get_variable);
696
697 let status = rs.get_variable::<DummyVariableType>(&DUMMY_FIRST_NAME, &DUMMY_FIRST_NAMESPACE, None);
698
699 assert!(status.is_ok());
700 let (data, attributes) = status.unwrap();
701 assert_eq!(attributes, DUMMY_ATTRIBUTES);
702 assert_eq!(data.value, DUMMY_DATA);
703 }
704
705 #[test]
706 #[should_panic(expected = "Name passed into get_variable is not null-terminated.")]
707 fn test_get_variable_non_terminated() {
708 let rs: &StandardRuntimeServices<'_> = runtime_services!(get_variable = mock_efi_get_variable);
709
710 let _ = rs.get_variable::<DummyVariableType>(&DUMMY_NON_NULL_TERMINATED_NAME, &DUMMY_FIRST_NAMESPACE, None);
711 }
712
713 #[test]
714 fn test_get_variable_low_size_hint() {
715 let rs: &StandardRuntimeServices<'_> = runtime_services!(get_variable = mock_efi_get_variable);
716
717 let status = rs.get_variable::<DummyVariableType>(&DUMMY_FIRST_NAME, &DUMMY_FIRST_NAMESPACE, Some(1));
718
719 assert!(status.is_ok());
720 let (data, attributes) = status.unwrap();
721 assert_eq!(attributes, DUMMY_ATTRIBUTES);
722 assert_eq!(data.value, DUMMY_DATA);
723 }
724
725 #[test]
726 fn test_get_variable_not_found() {
727 let rs: &StandardRuntimeServices<'_> = runtime_services!(get_variable = mock_efi_get_variable);
728
729 let status = rs.get_variable::<DummyVariableType>(&DUMMY_UNKNOWN_NAME, &DUMMY_FIRST_NAMESPACE, Some(1));
730
731 assert!(status.is_err());
732 assert_eq!(status.unwrap_err(), efi::Status::NOT_FOUND);
733 }
734
735 #[test]
736 fn test_get_variable_size_and_attributes() {
737 let rs: &StandardRuntimeServices<'_> = runtime_services!(get_variable = mock_efi_get_variable);
738
739 let status = rs.get_variable_size_and_attributes(&DUMMY_FIRST_NAME, &DUMMY_FIRST_NAMESPACE);
740
741 assert!(status.is_ok());
742 let (size, attributes) = status.unwrap();
743 assert_eq!(size, DUMMY_DATA_REPR_SIZE);
744 assert_eq!(attributes, DUMMY_ATTRIBUTES);
745 }
746
747 #[test]
748 fn test_set_variable() {
749 let rs: &StandardRuntimeServices<'_> = runtime_services!(set_variable = mock_efi_set_variable);
750
751 let mut data = DummyVariableType { value: DUMMY_DATA };
752
753 let status = rs.set_variable::<DummyVariableType>(
754 &DUMMY_FIRST_NAME,
755 &DUMMY_FIRST_NAMESPACE,
756 DUMMY_ATTRIBUTES,
757 &mut data,
758 );
759
760 assert!(status.is_ok());
761 }
762
763 #[test]
764 #[should_panic(expected = "Name passed into set_variable is not null-terminated.")]
765 fn test_set_variable_non_terminated() {
766 let rs: &StandardRuntimeServices<'_> = runtime_services!(set_variable = mock_efi_set_variable);
767
768 let mut data = DummyVariableType { value: DUMMY_DATA };
769
770 let _ = rs.set_variable::<DummyVariableType>(
771 &DUMMY_NON_NULL_TERMINATED_NAME,
772 &DUMMY_FIRST_NAMESPACE,
773 DUMMY_ATTRIBUTES,
774 &mut data,
775 );
776 }
777
778 #[test]
779 fn test_set_variable_empty_name() {
780 let rs: &StandardRuntimeServices<'_> = runtime_services!(set_variable = mock_efi_set_variable);
781
782 let mut data = DummyVariableType { value: DUMMY_DATA };
783
784 let status = rs.set_variable::<DummyVariableType>(
785 &DUMMY_EMPTY_NAME,
786 &DUMMY_FIRST_NAMESPACE,
787 DUMMY_ATTRIBUTES,
788 &mut data,
789 );
790
791 assert!(status.is_err());
792 assert_eq!(status.unwrap_err(), efi::Status::INVALID_PARAMETER);
793 }
794
795 #[test]
796 fn test_set_variable_not_found() {
797 let rs: &StandardRuntimeServices<'_> = runtime_services!(set_variable = mock_efi_set_variable);
798
799 let mut data = DummyVariableType { value: DUMMY_DATA };
800
801 let status = rs.set_variable::<DummyVariableType>(
802 &DUMMY_UNKNOWN_NAME,
803 &DUMMY_FIRST_NAMESPACE,
804 DUMMY_ATTRIBUTES,
805 &mut data,
806 );
807
808 assert!(status.is_err());
809 assert_eq!(status.unwrap_err(), efi::Status::NOT_FOUND);
810 }
811
812 #[test]
813 fn test_get_next_variable_name() {
814 assert!(DUMMY_SECOND_NAME.len() > DUMMY_FIRST_NAME.len());
816
817 let rs: &StandardRuntimeServices<'_> =
818 runtime_services!(get_next_variable_name = mock_efi_get_next_variable_name);
819
820 let status = rs.get_next_variable_name(&DUMMY_FIRST_NAME, &DUMMY_FIRST_NAMESPACE);
821
822 assert!(status.is_ok());
823
824 let (next_name, next_guid) = status.unwrap();
825
826 assert_eq!(next_name, DUMMY_SECOND_NAME);
827 assert_eq!(next_guid, DUMMY_SECOND_NAMESPACE);
828 }
829
830 #[test]
831 fn test_get_next_variable_name_non_terminated() {
832 let rs: &StandardRuntimeServices<'_> =
833 runtime_services!(get_next_variable_name = mock_efi_get_next_variable_name);
834
835 let status = rs.get_next_variable_name(&DUMMY_NON_NULL_TERMINATED_NAME, &DUMMY_FIRST_NAMESPACE);
836
837 assert!(status.is_err());
838 assert_eq!(status.unwrap_err(), efi::Status::INVALID_PARAMETER);
839 }
840
841 #[test]
842 #[should_panic(expected = "Zero-length name passed into get_next_variable_name.")]
843 fn test_get_next_variable_name_zero_length_name() {
844 let rs: &StandardRuntimeServices<'_> =
845 runtime_services!(get_next_variable_name = mock_efi_get_next_variable_name);
846
847 let _ = rs.get_next_variable_name(&DUMMY_ZERO_LENGTH_NAME, &DUMMY_FIRST_NAMESPACE);
848 }
849
850 #[test]
851 fn test_get_next_variable_name_not_found() {
852 let rs: &StandardRuntimeServices<'_> =
853 runtime_services!(get_next_variable_name = mock_efi_get_next_variable_name);
854
855 let status = rs.get_next_variable_name(&DUMMY_UNKNOWN_NAME, &DUMMY_FIRST_NAMESPACE);
856
857 assert!(status.is_err());
858 assert_eq!(status.unwrap_err(), efi::Status::NOT_FOUND);
859 }
860
861 #[test]
862 fn test_query_variable_info() {
863 let rs: &StandardRuntimeServices<'_> = runtime_services!(query_variable_info = mock_efi_query_variable_info);
864
865 let status = rs.query_variable_info(DUMMY_ATTRIBUTES);
866
867 assert!(status.is_ok());
868 let variable_info = status.unwrap();
869 assert_eq!(variable_info.maximum_variable_storage_size, DUMMY_MAXIMUM_VARIABLE_STORAGE_SIZE);
870 assert_eq!(variable_info.remaining_variable_storage_size, DUMMY_REMAINING_VARIABLE_STORAGE_SIZE);
871 assert_eq!(variable_info.maximum_variable_size, DUMMY_MAXIMUM_VARIABLE_SIZE);
872 }
873
874 #[test]
875 fn test_query_variable_info_invalid_attributes() {
876 let rs: &StandardRuntimeServices<'_> = runtime_services!(query_variable_info = mock_efi_query_variable_info);
877
878 let status = rs.query_variable_info(DUMMY_INVALID_ATTRIBUTES);
879
880 assert!(status.is_err());
881 assert_eq!(status.unwrap_err(), efi::Status::INVALID_PARAMETER);
882 }
883}