flowchart TB
subgraph app["Applications"]
BOOT["Boot Loader"]
OS["Operating System"]
UEFI_APP["UEFI App"]
end
subgraph runtime["Runtime Services"]
GET["GetVariable()"]
SET["SetVariable()"]
NEXT["GetNextVariableName()"]
end
subgraph storage["NVRAM Storage"]
VARS["UEFI Variables<br/>• Boot options<br/>• Secure Boot keys<br/>• Custom data"]
end
BOOT --> GET
BOOT --> SET
OS --> GET
OS --> SET
UEFI_APP --> GET
UEFI_APP --> SET
GET --> VARS
SET --> VARS
NEXT --> VARS
Variable Concepts
Property
Description
Name
UTF-16 string identifier
GUID
Vendor namespace (prevents collisions)
Attributes
Access permissions and persistence
Data
Arbitrary byte array
Common Vendor GUIDs
GUID
Purpose
EFI_GLOBAL_VARIABLE
Standard UEFI variables
Custom GUID
Your application’s variables
Variable Attributes
Attribute
Meaning
NON_VOLATILE
Survives reboot
BOOTSERVICE_ACCESS
Available during boot
RUNTIME_ACCESS
Available to OS
Reading Variables
Get a Variable
useuefi::runtime::{self,VariableVendor,VariableAttributes};useuefi::CStr16;fnread_variable(name:&CStr16,vendor:&VariableVendor)->uefi::Result<alloc::vec::Vec<u8>>{// First, get the sizeletsize=runtime::get_variable_size(name,vendor)?;log::info!("Variable size: {} bytes",size);// Allocate buffer and readletmutbuffer=alloc::vec![0u8;size];let(data,attributes)=runtime::get_variable(name,vendor,&mutbuffer)?;log::info!("Attributes: {:?}",attributes);Ok(data.to_vec())}
Read Standard Variables
fnread_boot_order()->uefi::Result{letname=cstr16!("BootOrder");letvendor=VariableVendor::GLOBAL_VARIABLE;letmutbuffer=[0u8;256];matchruntime::get_variable(name,&vendor,&mutbuffer){Ok((data,_attr))=>{// BootOrder is an array of u16 boot option numberslog::info!("Boot Order:");forchunkindata.chunks(2){ifchunk.len()==2{letnum=u16::from_le_bytes([chunk[0],chunk[1]]);log::info!(" Boot{:04X}",num);}}}Err(e)=>log::error!("Failed to read BootOrder: {:?}",e),}Ok(())}fnread_secure_boot_state()->uefi::Result{letname=cstr16!("SecureBoot");letvendor=VariableVendor::GLOBAL_VARIABLE;letmutbuffer=[0u8;4];matchruntime::get_variable(name,&vendor,&mutbuffer){Ok((data,_))=>{letenabled=data.first().map(|&b|b!=0).unwrap_or(false);log::info!("Secure Boot: {}",ifenabled{"Enabled"}else{"Disabled"});}Err(_)=>log::info!("Secure Boot variable not found"),}Ok(())}
Writing Variables
Set a Variable
fnwrite_variable(name:&CStr16,vendor:&VariableVendor,data:&[u8],non_volatile:bool,)->uefi::Result{letmutattributes=VariableAttributes::BOOTSERVICE_ACCESS|VariableAttributes::RUNTIME_ACCESS;ifnon_volatile{attributes|=VariableAttributes::NON_VOLATILE;}runtime::set_variable(name,vendor,attributes,data)?;log::info!("Variable written successfully");Ok(())}
Create Custom Variable
useuefi::Guid;// Define your own vendor GUIDconstMY_VENDOR_GUID:Guid=Guid::from_values(0x12345678,0xabcd,0xef01,[0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0x01],);fnsave_config(config_data:&[u8])->uefi::Result{letname=cstr16!("MyAppConfig");letvendor=VariableVendor(MY_VENDOR_GUID);letattributes=VariableAttributes::NON_VOLATILE|VariableAttributes::BOOTSERVICE_ACCESS|VariableAttributes::RUNTIME_ACCESS;runtime::set_variable(name,&vendor,attributes,config_data)?;log::info!("Configuration saved");Ok(())}fnload_config()->uefi::Result<alloc::vec::Vec<u8>>{letname=cstr16!("MyAppConfig");letvendor=VariableVendor(MY_VENDOR_GUID);letsize=runtime::get_variable_size(name,&vendor)?;letmutbuffer=alloc::vec![0u8;size];let(data,_)=runtime::get_variable(name,&vendor,&mutbuffer)?;log::info!("Configuration loaded: {} bytes",data.len());Ok(data.to_vec())}
Deleting Variables
fndelete_variable(name:&CStr16,vendor:&VariableVendor)->uefi::Result{// Set with empty data and no attributes to deleteruntime::set_variable(name,vendor,VariableAttributes::empty(),&[],)?;log::info!("Variable deleted");Ok(())}
Enumerating Variables
List All Variables
fnlist_all_variables()->uefi::Result{log::info!("All UEFI Variables:");// Start with empty nameletmutname_buf=[0u16;256];letmutname=CStr16::from_u16_with_nul(&name_buf[..1]).unwrap();letmutvendor=VariableVendor::GLOBAL_VARIABLE;loop{matchruntime::get_next_variable_name(&mutname_buf,&mutvendor){Ok(next_name)=>{name=next_name;log::info!(" {} ({:?})",name,vendor.0);}Err(uefi::Status::NOT_FOUND)=>break,// End of listErr(e)=>returnErr(e.into()),}}Ok(())}
Find Variables by Vendor
fnlist_global_variables()->uefi::Result{log::info!("Global UEFI Variables:");letmutname_buf=[0u16;256];letmutvendor=VariableVendor::GLOBAL_VARIABLE;letglobal_guid=VariableVendor::GLOBAL_VARIABLE.0;// Initialize with empty stringname_buf[0]=0;loop{matchruntime::get_next_variable_name(&mutname_buf,&mutvendor){Ok(name)=>{// Only show global variablesifvendor.0==global_guid{log::info!(" {}",name);}}Err(uefi::Status::NOT_FOUND)=>break,Err(e)=>returnErr(e.into()),}}Ok(())}
Structured Data Storage
Save a Struct
#[repr(C,packed)]structAppSettings{version:u32,timeout:u32,flags:u32,}fnsave_settings(settings:&AppSettings)->uefi::Result{letname=cstr16!("Settings");letvendor=VariableVendor(MY_VENDOR_GUID);// Convert struct to bytesletdata=unsafe{core::slice::from_raw_parts(settingsas*const_as*constu8,core::mem::size_of::<AppSettings>(),)};letattributes=VariableAttributes::NON_VOLATILE|VariableAttributes::BOOTSERVICE_ACCESS;runtime::set_variable(name,&vendor,attributes,data)?;Ok(())}fnload_settings()->uefi::Result<AppSettings>{letname=cstr16!("Settings");letvendor=VariableVendor(MY_VENDOR_GUID);letmutbuffer=[0u8;core::mem::size_of::<AppSettings>()];let(data,_)=runtime::get_variable(name,&vendor,&mutbuffer)?;ifdata.len()!=core::mem::size_of::<AppSettings>(){returnErr(uefi::Status::BUFFER_TOO_SMALL.into());}// Convert bytes to structletsettings=unsafe{core::ptr::read_unaligned(data.as_ptr()as*constAppSettings)};Ok(settings)}
Variable Size Limits
fncheck_variable_storage()->uefi::Result{// Query storage infoletinfo=runtime::query_variable_info(VariableAttributes::NON_VOLATILE|VariableAttributes::BOOTSERVICE_ACCESS|VariableAttributes::RUNTIME_ACCESS,)?;log::info!("Variable Storage Info:");log::info!(" Max variable storage: {} bytes",info.maximum_variable_storage_size);log::info!(" Remaining storage: {} bytes",info.remaining_variable_storage_size);log::info!(" Max variable size: {} bytes",info.maximum_variable_size);Ok(())}
Complete Example
#![no_main]#![no_std]externcratealloc;usealloc::vec::Vec;useuefi::prelude::*;useuefi::runtime::{self,VariableVendor,VariableAttributes};useuefi::Guid;constAPP_GUID:Guid=Guid::from_values(0xdeadbeef,0xcafe,0xbabe,[0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0],);#[entry]fnmain(_image:Handle,st:SystemTable<Boot>)->Status{uefi::helpers::init().unwrap();log::info!("=== UEFI Variables Demo ===\n");// Read some standard variableslog::info!("--- Standard Variables ---");// SecureBootletmutbuf=[0u8;4];ifletOk((data,_))=runtime::get_variable(cstr16!("SecureBoot"),&VariableVendor::GLOBAL_VARIABLE,&mutbuf,){letenabled=data.first().map(|&b|b!=0).unwrap_or(false);log::info!("Secure Boot: {}",ifenabled{"Enabled"}else{"Disabled"});}// Create custom variablelog::info!("\n--- Custom Variable ---");letvendor=VariableVendor(APP_GUID);letvar_name=cstr16!("DemoCounter");// Try to read existing counterletmutcounter:u32=0;letmutbuf=[0u8;4];ifletOk((data,_))=runtime::get_variable(var_name,&vendor,&mutbuf){ifdata.len()>=4{counter=u32::from_le_bytes([data[0],data[1],data[2],data[3]]);log::info!("Existing counter: {}",counter);}}else{log::info!("Counter not found, creating new");}// Increment and savecounter+=1;letcounter_bytes=counter.to_le_bytes();letattributes=VariableAttributes::NON_VOLATILE|VariableAttributes::BOOTSERVICE_ACCESS|VariableAttributes::RUNTIME_ACCESS;matchruntime::set_variable(var_name,&vendor,attributes,&counter_bytes){Ok(_)=>log::info!("Counter saved: {}",counter),Err(e)=>log::error!("Failed to save: {:?}",e),}// Query storage infolog::info!("\n--- Storage Info ---");ifletOk(info)=runtime::query_variable_info(attributes){log::info!("Max storage: {} KB",info.maximum_variable_storage_size/1024);log::info!("Remaining: {} KB",info.remaining_variable_storage_size/1024);log::info!("Max variable: {} bytes",info.maximum_variable_size);}Status::SUCCESS}
Best Practices
Use your own GUID for custom variables to avoid conflicts
Check storage space before writing large variables
Handle errors gracefully (variable might not exist)
Use NON_VOLATILE only when persistence is needed
Include RUNTIME_ACCESS if OS needs access
Version your data structures for forward compatibility
Summary
Operation
Function
Read
get_variable()
Write
set_variable()
Delete
set_variable() with empty data
Enumerate
get_next_variable_name()
Check size
get_variable_size()
Query storage
query_variable_info()
Next Steps
Learn about QEMU Testing for debugging UEFI applications.