11use crate :: efivarfs;
2- use crate :: types:: EfiVariable ;
3- use std:: io;
4- use std:: path:: PathBuf ;
2+ use crate :: efi_variable_attributes:: parse_attributes;
3+ use crate :: types:: { EfiGuid , EfiVariable } ;
4+ use crate :: MIN_VAR_FILE_NAME_LEN ;
5+ use std:: boxed:: Box ;
6+ use std:: convert:: TryFrom ;
7+ use std:: error:: Error ;
8+ use std:: fs;
9+ use std:: io:: { self , IoSliceMut , Read } ;
10+ use std:: path:: { Path , PathBuf } ;
511
12+ const EFI_VAR_NAME_LEN : usize = 512 ;
13+ const EFIVARS_FW_PLATFORM_SZ_PATH : & ' static str = "/sys/firmware/efi/fw_platform_size" ;
614const EFIVARS_PATH : & ' static str = "/sys/firmware/efi/vars" ;
715
816pub struct EfiVariables {
917 path : PathBuf ,
18+ platform_size : usize ,
1019}
1120
1221impl Default for EfiVariables {
1322 fn default ( ) -> Self {
14- return EfiVariables {
23+ let mut variables = EfiVariables {
1524 path : EFIVARS_PATH . into ( ) ,
25+ platform_size : 0 ,
1626 } ;
27+ variables. set_firmware_platform_size (
28+ EfiVariables :: get_firmware_platform_size ( EFIVARS_FW_PLATFORM_SZ_PATH . into ( ) ) . unwrap ( ) ,
29+ ) ;
30+ return variables;
31+ }
32+ }
33+
34+ type Efi64VariableBuffer = EfiVariableBuffer < 8 > ;
35+ type Efi32VariableBuffer = EfiVariableBuffer < 4 > ;
36+
37+ trait EfiNVariableBuffer {
38+ fn name ( & mut self ) -> & mut [ u8 ] ;
39+ fn guid ( & mut self ) -> & mut [ u8 ] ;
40+ fn data_size ( & mut self ) -> & mut [ u8 ] ;
41+ fn data ( & mut self ) -> & mut [ u8 ] ;
42+ fn status ( & mut self ) -> & mut [ u8 ] ;
43+ fn attributes ( & mut self ) -> & mut [ u8 ] ;
44+ }
45+
46+ struct EfiVariableBuffer < const SIZE : usize > {
47+ name : [ u8 ; EFI_VAR_NAME_LEN * 2 ] ,
48+ guid : [ u8 ; 16 ] ,
49+ data_size : [ u8 ; SIZE ] ,
50+ data : [ u8 ; 1024 ] ,
51+ status : [ u8 ; SIZE ] ,
52+ attributes : [ u8 ; 4 ] ,
53+ }
54+
55+ impl < const SIZE : usize > EfiVariableBuffer < SIZE > {
56+ fn new ( ) -> Self {
57+ return EfiVariableBuffer {
58+ name : [ 0 ; EFI_VAR_NAME_LEN * 2 ] ,
59+ guid : [ 0 ; 16 ] ,
60+ data_size : [ 0 ; SIZE ] ,
61+ data : [ 0 ; 1024 ] ,
62+ status : [ 0 ; SIZE ] ,
63+ attributes : [ 0 ; 4 ] ,
64+ } ;
65+ }
66+ }
67+
68+ impl < const SIZE : usize > TryFrom < fs:: File > for EfiVariableBuffer < SIZE > {
69+ type Error = Box < dyn Error > ;
70+
71+ fn try_from ( mut handle : fs:: File ) -> Result < Self , Self :: Error > {
72+ let mut buffer = Self :: new ( ) ;
73+ let mut vectors = [
74+ IoSliceMut :: new ( & mut buffer. name ) ,
75+ IoSliceMut :: new ( & mut buffer. guid ) ,
76+ IoSliceMut :: new ( & mut buffer. data_size ) ,
77+ IoSliceMut :: new ( & mut buffer. data ) ,
78+ IoSliceMut :: new ( & mut buffer. status ) ,
79+ IoSliceMut :: new ( & mut buffer. attributes ) ,
80+ ] ;
81+ handle. read_vectored ( & mut vectors) ?;
82+ return Ok ( buffer) ;
83+ }
84+ }
85+
86+ impl < const SIZE : usize > EfiNVariableBuffer for EfiVariableBuffer < SIZE > {
87+ fn name ( & mut self ) -> & mut [ u8 ] {
88+ self . name . as_mut_slice ( )
89+ }
90+
91+ fn guid ( & mut self ) -> & mut [ u8 ] {
92+ self . guid . as_mut_slice ( )
93+ }
94+
95+ fn data_size ( & mut self ) -> & mut [ u8 ] {
96+ self . data_size . as_mut_slice ( )
97+ }
98+
99+ fn data ( & mut self ) -> & mut [ u8 ] {
100+ self . data . as_mut_slice ( )
101+ }
102+
103+ fn status ( & mut self ) -> & mut [ u8 ] {
104+ self . status . as_mut_slice ( )
105+ }
106+
107+ fn attributes ( & mut self ) -> & mut [ u8 ] {
108+ self . attributes . as_mut_slice ( )
17109 }
18110}
19111
@@ -29,7 +121,106 @@ impl EfiVariables {
29121 return e. list ( ) ;
30122 }
31123
32- pub fn get_variable ( & self , name : & str ) -> io:: Result < EfiVariable > {
33- return Err ( io:: ErrorKind :: Other . into ( ) ) ;
124+ pub fn get_variable ( & self , name : & str ) -> Result < EfiVariable , Box < dyn Error > > {
125+ if name. len ( ) < MIN_VAR_FILE_NAME_LEN
126+ || name. bytes ( ) . nth ( MIN_VAR_FILE_NAME_LEN - 2 ) . unwrap ( ) != b'-'
127+ {
128+ return Err ( io:: Error :: from ( io:: ErrorKind :: InvalidInput ) . into ( ) ) ;
129+ }
130+ let guid_bytes = & name[ 0 ..MIN_VAR_FILE_NAME_LEN - 2 ] ;
131+ let guid = match EfiGuid :: try_from ( guid_bytes) {
132+ Ok ( g) => Some ( g) ,
133+ Err ( _) => {
134+ return Err ( io:: Error :: from ( io:: ErrorKind :: InvalidInput ) . into ( ) ) ;
135+ }
136+ }
137+ . unwrap ( ) ;
138+ let prefix = & name[ MIN_VAR_FILE_NAME_LEN - 1 ..] ;
139+ let full_path = self
140+ . path
141+ . join ( String :: new ( ) + prefix + & "-" + guid_bytes)
142+ . join ( "raw_var" ) ;
143+ let efi_variable = self . parse_payload ( & full_path) ?;
144+ if * efi_variable. name != * name {
145+ return Err :: < EfiVariable , Box < dyn Error > > ( "Corrupt variable. Reported name does not match name" . into ( ) ) ;
146+ }
147+ if efi_variable. guid != guid {
148+ return Err :: < EfiVariable , Box < dyn Error > > ( "Corrupt variable. Reported guid does not match guid" . into ( ) ) ;
149+ }
150+ return Ok ( efi_variable) ;
151+ }
152+
153+ fn parse_payload ( & self , var_path : & Path ) -> Result < EfiVariable , Box < dyn Error > > {
154+ let mut handle = fs:: File :: open ( var_path) ?;
155+ let mut buffer: Box < dyn EfiNVariableBuffer > = match self . platform_size {
156+ 64 => {
157+ Ok ( Box :: new ( Efi64VariableBuffer :: try_from ( handle) ?) as Box < dyn EfiNVariableBuffer > )
158+ }
159+ 32 => {
160+ Ok ( Box :: new ( Efi32VariableBuffer :: try_from ( handle) ?) as Box < dyn EfiNVariableBuffer > )
161+ }
162+ _ => Err ( format ! ( "Unsupported platform size: {}" , self . platform_size) ) ,
163+ } ?;
164+
165+ let name: Box < str > = String :: from_utf16 (
166+ & ( 0 ..EFI_VAR_NAME_LEN )
167+ . filter_map ( |i| {
168+ let utf16_char =
169+ u16:: from_ne_bytes ( [ buffer. name ( ) [ 2 * i] , buffer. name ( ) [ 2 * i + 1 ] ] ) ;
170+ if utf16_char != 0u16 {
171+ return Some ( utf16_char) ;
172+ }
173+ return None ;
174+ } )
175+ . collect :: < Vec < u16 > > ( ) ,
176+ ) ?. into ( ) ;
177+ let guid = EfiGuid :: try_from ( buffer. guid ( ) as & [ u8 ] ) ?;
178+ let data_size: usize = match TryInto :: < [ u8 ; 8 ] > :: try_into ( buffer. data_size ( ) ) {
179+ Ok ( v) => Ok :: < usize , Box < dyn Error > > ( usize:: from_ne_bytes ( v) ) ,
180+ Err ( _) => Ok :: < usize , Box < dyn Error > > ( u32:: from_ne_bytes ( TryInto :: < [ u8 ; 4 ] > :: try_into (
181+ buffer. data_size ( ) ,
182+ ) ?) as usize ) ,
183+ } ?;
184+ if data_size > 1024 {
185+ return Err :: < EfiVariable , Box < dyn Error > > ( "Corrupt variable. Reported data size exceeds maximum" . into ( ) ) ;
186+ }
187+ let data: Vec < u8 > = buffer. data ( ) [ 0 ..data_size] . into ( ) ;
188+ let status: usize = match TryInto :: < [ u8 ; 8 ] > :: try_into ( buffer. status ( ) ) {
189+ Ok ( v) => Ok :: < usize , Box < dyn Error > > ( usize:: from_ne_bytes ( v) ) ,
190+ Err ( _) => Ok :: < usize , Box < dyn Error > > ( u32:: from_ne_bytes ( TryInto :: < [ u8 ; 4 ] > :: try_into (
191+ buffer. status ( ) ,
192+ ) ?) as usize ) ,
193+ } ?;
194+ if status != 0 {
195+ return Err :: < EfiVariable , Box < dyn Error > > ( format ! ( "Variable read error. Unexpected status code {}" , status) . into ( ) ) ;
196+ }
197+ let attributes = parse_attributes ( u32:: from_ne_bytes ( TryInto :: < [ u8 ; 4 ] > :: try_into ( buffer. attributes ( ) ) ?) ) ;
198+
199+ return Ok ( EfiVariable {
200+ name,
201+ guid,
202+ data,
203+ attributes,
204+ } ) ;
205+ }
206+
207+ fn set_firmware_platform_size ( & mut self , size : usize ) -> Result < ( ) , Box < dyn Error > > {
208+ match size {
209+ 64 => Ok ( self . platform_size = 64 ) ,
210+ 32 => Ok ( self . platform_size = 32 ) ,
211+ _ => Err ( format ! ( "Unsupported platform size: {}" , size) ) ,
212+ } ;
213+ return Ok ( ( ) ) ;
214+ }
215+
216+ fn get_firmware_platform_size ( path : & str ) -> Result < usize , Box < dyn Error > > {
217+ let result = match fs:: read ( path) {
218+ Ok ( bytes) => match String :: from_utf8 ( bytes) {
219+ Ok ( chars) => return Ok ( usize:: from_str_radix ( & chars, 10 ) ?) ,
220+ Err ( e) => Err ( e. into ( ) ) ,
221+ } ,
222+ Err ( e) => Err ( e. into ( ) ) ,
223+ } ;
224+ return result;
34225 }
35226}
0 commit comments