Skip to content

Commit 314a614

Browse files
committed
implement efivar get_variable()
1 parent 4180a46 commit 314a614

File tree

5 files changed

+204
-13
lines changed

5 files changed

+204
-13
lines changed

src/lib/efivar/efi_guids.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use serde::Deserialize;
55
use std::collections::HashMap;
66
use std::fs::File;
77
use std::io::{BufReader, Error};
8-
use std::str::FromStr;
98

109
pub const DEFAULT_GUIDS_LIST_PATH: &'static str = efi_guids_list_path::VALUE;
1110

@@ -40,7 +39,7 @@ impl EfiGuidList {
4039
match result {
4140
Ok(v) => {
4241
for entry in v {
43-
match EfiGuid::from_str(&entry.guid) {
42+
match entry.guid.parse::<EfiGuid>() {
4443
Ok(g) => {
4544
map.insert(
4645
entry.name.clone(),

src/lib/efivar/efivar/efi_variables.rs

Lines changed: 197 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,111 @@
11
use 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";
614
const EFIVARS_PATH: &'static str = "/sys/firmware/efi/vars";
715

816
pub struct EfiVariables {
917
path: PathBuf,
18+
platform_size: usize,
1019
}
1120

1221
impl 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
}

src/lib/efivar/efivarfs/efi_variables.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@ use crate::efi_variable_attributes::parse_attributes;
33
use std::fs::{self, ReadDir};
44
use std::io;
55
use std::path::PathBuf;
6+
use crate::MIN_VAR_FILE_NAME_LEN;
67

78
const EFIVARFS_PATH: &'static str = "/sys/firmware/efi/efivars";
89

9-
// variable file names have 1 or more characters, a dash, then a UUID (36 characters)
10-
const MIN_VAR_FILE_NAME_LEN: usize = 38;
11-
1210
pub struct EfiVariables {
1311
path: PathBuf,
1412
}

src/lib/efivar/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ pub mod print_mode;
66
pub mod types;
77

88
mod efi_guids_list_path;
9+
10+
// variable file names have 1 or more characters, a dash, then a UUID (36 characters)
11+
const MIN_VAR_FILE_NAME_LEN: usize = 38;

src/lib/efivar/types/efi_guid_error.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub enum EfiGuidError {
1212

1313
impl fmt::Display for EfiGuidError {
1414
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
15-
match self {
15+
return match self {
1616
Self::BadFormat => write!(
1717
f,
1818
"bad format. Correct format is xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
@@ -29,7 +29,7 @@ impl fmt::Display for EfiGuidError {
2929
Self::VecLengthTooShort => {
3030
write!(f, "source vector too short. Vector must have a size of 16")
3131
}
32-
}
32+
};
3333
}
3434
}
3535

0 commit comments

Comments
 (0)