1use alloc::string::String;
5use alloc::vec::Vec;
6use core::fmt::Display;
7use core::mem::size_of;
8use log::{debug, error};
9use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
10
11use uefi::data_types::FromStrError;
12use uefi::{CString16, Error, Status};
13use virtfw_libefi::efivar::types::{EfiVar, EfiVarAttr, EfiVarId};
14use virtfw_libefi::guids::RawGuid;
15
16use crate::mm::core::MmCoreHeader;
17use crate::mm::util;
18use crate::store::EfiVarStore;
19
20const GET_VARIABLE: u64 = 1;
21const GET_NEXT_VARIABLE_NAME: u64 = 2;
22const SET_VARIABLE: u64 = 3;
23const QUERY_VARIABLE_INFO: u64 = 4;
24const READY_TO_BOOT: u64 = 5;
25const EXIT_BOOT_SERVICE: u64 = 6;
26const LOCK_VARIABLE: u64 = 8;
27const GET_PAYLOAD_SIZE: u64 = 11;
28
29#[repr(C)]
31#[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)]
32pub struct MmVariableHeader {
33 pub function: u64,
34 pub status: u64,
35}
36
37impl MmVariableHeader {
38 pub fn from_data(data: &[u8]) -> Result<(Self, &[u8]), Error<&'static str>> {
39 Self::read_from_prefix(data).or(Err(Error::new(
40 Status::BAD_BUFFER_SIZE,
41 "read MmVariableHeader",
42 )))
43 }
44
45 pub fn new(nr: u64, status: Option<Status>) -> Self {
46 Self {
47 function: nr,
48 status: status.unwrap_or(Status::SUCCESS).0 as u64,
49 }
50 }
51}
52
53#[repr(C, packed)]
55#[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)]
56struct MmVariableAccess {
57 guid: RawGuid,
58 data_size: u64,
59 name_size: u64,
60 attributes: EfiVarAttr,
61 }
64
65impl MmVariableAccess {
66 fn from_data(data: &[u8]) -> Result<(Self, &[u8]), Error<&'static str>> {
67 Self::read_from_prefix(data).or(Err(Error::new(
68 Status::BAD_BUFFER_SIZE,
69 "read MmVariableAccess",
70 )))
71 }
72}
73
74#[repr(C, packed)]
76#[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)]
77struct MmNextVariable {
78 guid: RawGuid,
79 name_size: u64,
80 }
82
83impl MmNextVariable {
84 fn from_data(data: &[u8]) -> Result<(Self, &[u8]), Error<&'static str>> {
85 Self::read_from_prefix(data).or(Err(Error::new(
86 Status::BAD_BUFFER_SIZE,
87 "read MmNextVariable",
88 )))
89 }
90}
91
92#[repr(C, packed)]
94#[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)]
95struct MmVariableInfo {
96 max_storage_size: u64,
97 free_storage_size: u64,
98 max_variable_size: u64,
99 attributes: u32,
100}
101
102#[repr(C, packed)]
104#[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)]
105struct MmGetPayloadSize {
106 payload_size: u64,
107}
108
109#[derive(Debug, Eq, PartialEq)]
111pub enum MmVariableParsedRequest {
112 GetVariable(EfiVarId),
113 GetNextVariableName(EfiVarId),
114 SetVariable(EfiVar),
115 QueryVariableInfo,
116 ReadyToBoot,
117 ExitBootService,
118 LockVariable(EfiVarId),
119 GetPayloadSize,
120 Unknown { function: u64 },
121}
122
123impl MmVariableParsedRequest {
124 fn parse_access_id(body: &[u8]) -> Result<EfiVarId, Error<&'static str>> {
125 let (access, namedata) = MmVariableAccess::from_data(body)?;
126 let ns = access.name_size as usize;
127 let Some(slice) = &namedata.get(0..ns) else {
128 return Err(Error::new(Status::BAD_BUFFER_SIZE, "invalid name size"));
129 };
130 let name = util::cstr16_from_bytes_with_nul(slice)?;
131 let guid = access.guid.into();
132 let id = EfiVarId::new(String::from(name), guid);
133 Ok(id)
134 }
135
136 fn parse_access_var(body: &[u8]) -> Result<EfiVar, Error<&'static str>> {
137 let (access, namedata) = MmVariableAccess::from_data(body)?;
138 let ns = access.name_size as usize;
139 let ds = access.data_size as usize;
140 let Some(slice) = &namedata.get(0..ns) else {
141 return Err(Error::new(Status::BAD_BUFFER_SIZE, "invalid name size"));
142 };
143 let name = util::cstr16_from_bytes_with_nul(slice)?;
144 let Some(data) = &namedata.get(ns..ns + ds) else {
145 return Err(Error::new(Status::BAD_BUFFER_SIZE, "invalid data size"));
146 };
147 let guid = access.guid.into();
148 let id = EfiVarId::new(String::from(name), guid);
149 let var = EfiVar::new_from_slice(id, access.attributes, data);
150 Ok(var)
151 }
152
153 fn parse_next(body: &[u8]) -> Result<EfiVarId, Error<&'static str>> {
154 let (next, namebytes) = MmNextVariable::from_data(body)?;
155 let ns = next.name_size as usize;
156 let Some(slice) = &namebytes.get(0..ns) else {
157 return Err(Error::new(Status::BAD_BUFFER_SIZE, "invalid name size"));
158 };
159 let name = util::cstr16_from_bytes_until_nul(slice)?;
160 let guid = next.guid.into();
161 let id = EfiVarId::new(String::from(name), guid);
162 Ok(id)
163 }
164
165 pub fn new(function: u64, body: &[u8]) -> Result<Self, Error<&'static str>> {
166 let req = match function {
167 GET_VARIABLE => {
168 let id = MmVariableParsedRequest::parse_access_id(body)?;
169 MmVariableParsedRequest::GetVariable(id)
170 }
171 GET_NEXT_VARIABLE_NAME | LOCK_VARIABLE => {
172 let id = MmVariableParsedRequest::parse_next(body)?;
173 if function == GET_NEXT_VARIABLE_NAME {
174 MmVariableParsedRequest::GetNextVariableName(id)
175 } else {
176 MmVariableParsedRequest::LockVariable(id)
177 }
178 }
179 SET_VARIABLE => {
180 let var = MmVariableParsedRequest::parse_access_var(body)?;
181 MmVariableParsedRequest::SetVariable(var)
182 }
183 QUERY_VARIABLE_INFO => MmVariableParsedRequest::QueryVariableInfo,
184 READY_TO_BOOT => MmVariableParsedRequest::ReadyToBoot,
185 EXIT_BOOT_SERVICE => MmVariableParsedRequest::ExitBootService,
186 GET_PAYLOAD_SIZE => MmVariableParsedRequest::GetPayloadSize,
187 _ => MmVariableParsedRequest::Unknown { function },
188 };
189 Ok(req)
190 }
191
192 fn serialize_access_id(id: &EfiVarId) -> Result<Vec<u8>, FromStrError> {
194 let name = CString16::try_from(id.name())?;
195 let access = MmVariableAccess {
196 guid: id.guid().into(),
197 attributes: EfiVarAttr::new(),
198 name_size: name.num_bytes() as u64,
199 data_size: 0,
200 };
201 let mut blob = Vec::new();
202 blob.extend_from_slice(access.as_bytes());
203 blob.extend_from_slice(name.as_bytes());
204 Ok(blob)
205 }
206
207 fn serialize_access_var(var: &EfiVar) -> Result<Vec<u8>, FromStrError> {
209 let name = CString16::try_from(var.name())?;
210 let access = MmVariableAccess {
211 guid: var.guid().into(),
212 attributes: var.attr(),
213 name_size: name.num_bytes() as u64,
214 data_size: var.data().len() as u64,
215 };
216 let mut blob = Vec::new();
217 blob.extend_from_slice(access.as_bytes());
218 blob.extend_from_slice(name.as_bytes());
219 blob.extend_from_slice(var.data().as_bytes());
220 Ok(blob)
221 }
222
223 fn serialize_next(id: &EfiVarId) -> Result<Vec<u8>, FromStrError> {
225 let name = CString16::try_from(id.name())?;
226 let next = MmNextVariable {
227 guid: id.guid().into(),
228 name_size: name.num_bytes() as u64,
229 };
230 let mut blob = Vec::new();
231 blob.extend_from_slice(next.as_bytes());
232 blob.extend_from_slice(name.as_bytes());
233 Ok(blob)
234 }
235}
236
237impl TryFrom<&MmVariableParsedRequest> for Vec<u8> {
238 type Error = FromStrError;
239 fn try_from(value: &MmVariableParsedRequest) -> Result<Vec<u8>, FromStrError> {
240 let (function, data) = match value {
241 MmVariableParsedRequest::GetVariable(id) => (
242 GET_VARIABLE,
243 MmVariableParsedRequest::serialize_access_id(id)?,
244 ),
245 MmVariableParsedRequest::SetVariable(var) => (
246 SET_VARIABLE,
247 MmVariableParsedRequest::serialize_access_var(var)?,
248 ),
249 MmVariableParsedRequest::GetNextVariableName(id) => (
250 GET_NEXT_VARIABLE_NAME,
251 MmVariableParsedRequest::serialize_next(id)?,
252 ),
253 MmVariableParsedRequest::LockVariable(id) => {
254 (LOCK_VARIABLE, MmVariableParsedRequest::serialize_next(id)?)
255 }
256 MmVariableParsedRequest::QueryVariableInfo => (QUERY_VARIABLE_INFO, Vec::new()),
257 MmVariableParsedRequest::ReadyToBoot => (READY_TO_BOOT, Vec::new()),
258 MmVariableParsedRequest::ExitBootService => (EXIT_BOOT_SERVICE, Vec::new()),
259 MmVariableParsedRequest::GetPayloadSize => (GET_PAYLOAD_SIZE, Vec::new()),
260 MmVariableParsedRequest::Unknown { function } => (*function, Vec::new()),
261 };
262
263 let hdr = MmVariableHeader::new(function, None);
264 let mut blob = Vec::new();
265 blob.extend_from_slice(hdr.as_bytes());
266 blob.extend_from_slice(&data);
267 Ok(blob)
268 }
269}
270
271impl Display for MmVariableParsedRequest {
272 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
273 match self {
274 MmVariableParsedRequest::GetVariable(id) => {
275 write!(f, "request/var: get-variable {}", id.name())
276 }
277 MmVariableParsedRequest::GetNextVariableName(id) => {
278 write!(f, "request/var: get-next-variable-name {}", id.name())
279 }
280 MmVariableParsedRequest::SetVariable(var) => {
281 write!(
282 f,
283 "request/var: set-variable {} {} {} data bytes",
284 var.name(),
285 var.attr(),
286 var.data().len()
287 )
288 }
289 MmVariableParsedRequest::QueryVariableInfo => {
290 write!(f, "request/var: query-variable-info")
291 }
292 MmVariableParsedRequest::ReadyToBoot => {
293 write!(f, "request/var: ready-to-boot")
294 }
295 MmVariableParsedRequest::ExitBootService => {
296 write!(f, "request/var: exit-boot-service")
297 }
298 MmVariableParsedRequest::LockVariable(id) => {
299 write!(f, "request/var: lock-variable {}", id.name())
300 }
301 MmVariableParsedRequest::GetPayloadSize => {
302 write!(f, "request/var: get-payload-size")
303 }
304 MmVariableParsedRequest::Unknown { function } => {
305 write!(f, "request/var: unknown function #{function}")
306 }
307 }
308 }
309}
310
311#[derive(Debug)]
313pub struct MmVariableRequest<'a> {
314 pub data: &'a [u8],
315 pub function: u64,
316 pub parsed: Option<MmVariableParsedRequest>,
317}
318
319impl<'a> MmVariableRequest<'a> {
320 pub fn new(data: &'a [u8]) -> Result<Self, Error<&'static str>> {
321 let (header, body) = MmVariableHeader::from_data(data)?;
322 let req = match MmVariableParsedRequest::new(header.function, body) {
323 Ok(req) => {
324 debug!("variable/req/ok: {req}");
325 Some(req)
326 }
327 Err(e) => {
328 debug!("variable/req/err: {e}");
329 None
330 }
331 };
332 Ok(MmVariableRequest {
333 data,
334 function: header.function,
335 parsed: req,
336 })
337 }
338
339 fn response(&self, status: Status, reply: Option<&[u8]>) -> Vec<u8> {
340 let mut status = status;
341
342 let limit = self.data.len() - core::mem::size_of::<MmVariableHeader>();
343 if let Some(r) = reply {
344 if limit < r.len() {
345 status = Status::BUFFER_TOO_SMALL;
346 }
347 }
348
349 debug!(
350 "variable/rsp: {}, {} data byte(s) ({} available)",
351 status,
352 if let Some(r) = reply { r.len() } else { 0 },
353 limit,
354 );
355
356 let hdr = MmVariableHeader::new(self.function, Some(status));
357 let mut rsp = hdr.as_bytes().to_vec();
358 if let Some(r) = reply {
359 if limit < r.len() {
360 rsp.extend_from_slice(&r[..limit]);
361 } else {
362 rsp.extend_from_slice(r);
363 }
364 }
365
366 rsp
367 }
368
369 fn get_variable(&self, store: &EfiVarStore, id: &EfiVarId) -> Vec<u8> {
370 let res = store.get(id);
371 if let Err(e) = res {
372 debug!("variable/get/error: {e}");
373 return self.response(e.status(), None);
374 }
375 let var = res.unwrap();
376
377 let reply = MmVariableParsedRequest::serialize_access_var(var);
378 match reply {
379 Err(_) => self.response(Status::DEVICE_ERROR, None),
380 Ok(r) => self.response(Status::SUCCESS, Some(&r)),
381 }
382 }
383
384 fn get_next_variable_name(&self, store: &EfiVarStore, id: &EfiVarId) -> Vec<u8> {
385 let res = store.get_next(id);
386 if let Err(e) = res {
387 return self.response(e.status(), None);
388 }
389 let next_id = res.unwrap();
390 debug!("variable/next: {} -> {}", id.name(), next_id.name());
391
392 let reply = MmVariableParsedRequest::serialize_next(next_id);
393 match reply {
394 Err(_) => self.response(Status::DEVICE_ERROR, None),
395 Ok(r) => self.response(Status::SUCCESS, Some(&r)),
396 }
397 }
398
399 fn set_variable(&self, store: &mut EfiVarStore, var: &EfiVar) -> Vec<u8> {
400 let res = store.set(var.clone());
401 if let Err(e) = res {
402 debug!("variable/set/error: {e}");
403 return self.response(e.status(), None);
404 }
405 self.response(Status::SUCCESS, None)
406 }
407
408 fn query_variable_info(&self, _store: &EfiVarStore) -> Vec<u8> {
409 let reply = MmVariableInfo {
410 max_storage_size: 256 * 1024, free_storage_size: 0, max_variable_size: 64 * 1024, attributes: 0,
414 };
415 self.response(Status::SUCCESS, Some(reply.as_bytes()))
416 }
417
418 fn lock_variable(&self, store: &mut EfiVarStore, id: &EfiVarId) -> Vec<u8> {
419 let res = store.lock(id.name(), id.guid());
420 if let Err(e) = res {
421 debug!("variable/lock/error: {e}");
422 return self.response(e.status(), None);
423 }
424 self.response(Status::SUCCESS, None)
425 }
426
427 fn ready_to_boot(&self, store: &mut EfiVarStore) -> Vec<u8> {
428 store.ready_to_boot();
429 self.response(Status::SUCCESS, None)
430 }
431
432 fn exit_boot_service(&self, store: &mut EfiVarStore) -> Vec<u8> {
433 store.exit_boot_service();
434 self.response(Status::SUCCESS, None)
435 }
436
437 fn get_payload_size(&self) -> Vec<u8> {
438 let mut payload_size = 64 * 1024; payload_size -= size_of::<MmCoreHeader>() as u64;
440 payload_size -= size_of::<MmVariableHeader>() as u64;
441 let reply = MmGetPayloadSize { payload_size };
442 self.response(Status::SUCCESS, Some(reply.as_bytes()))
443 }
444
445 fn process(&self, store: &mut EfiVarStore) -> Vec<u8> {
446 if self.parsed.is_none() {
447 return self.response(Status::INVALID_PARAMETER, None);
448 }
449 let parsed = self.parsed.as_ref().unwrap();
450 match parsed {
451 MmVariableParsedRequest::GetVariable(id) => self.get_variable(store, id),
452 MmVariableParsedRequest::GetNextVariableName(id) => {
453 self.get_next_variable_name(store, id)
454 }
455 MmVariableParsedRequest::SetVariable(var) => self.set_variable(store, var),
456 MmVariableParsedRequest::QueryVariableInfo => self.query_variable_info(store),
457 MmVariableParsedRequest::ReadyToBoot => self.ready_to_boot(store),
458 MmVariableParsedRequest::ExitBootService => self.exit_boot_service(store),
459 MmVariableParsedRequest::LockVariable(id) => self.lock_variable(store, id),
460 MmVariableParsedRequest::GetPayloadSize => self.get_payload_size(),
461 _ => self.response(Status::INVALID_PARAMETER, None),
462 }
463 }
464}
465
466pub fn variable_request(store: &mut EfiVarStore, req: &[u8]) -> Vec<u8> {
467 let res = MmVariableRequest::new(req);
468 if let Err(e) = res.as_ref() {
469 error!("{e}");
470 return Vec::new();
471 }
472
473 let vreq = res.unwrap();
474 vreq.process(store)
475}