#![allow(dead_code)]
use alloc::string::String;
use alloc::vec::Vec;
use log::{debug, error};
use uguid::Guid;
use zerocopy::byteorder::little_endian::{U32, U64};
use zerocopy::{FromBytes, IntoBytes, Unalign};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, Unaligned};
use uefi::{Error, Status};
use virtfw_libefi::efivar::types::EfiVarAttr;
use crate::mm::util;
use crate::store::EfiVarStore;
const VAR_CHECK_POLICY_COMMAND_DISABLE: u32 = 0x01;
const VAR_CHECK_POLICY_COMMAND_IS_ENABLED: u32 = 0x02;
const VAR_CHECK_POLICY_COMMAND_REGISTER: u32 = 0x03;
const VAR_CHECK_POLICY_COMMAND_DUMP: u32 = 0x04;
const VAR_CHECK_POLICY_COMMAND_LOCK: u32 = 0x05;
#[repr(C, packed)]
#[derive(Debug, FromBytes, IntoBytes, Immutable, Unaligned, Clone)]
struct MmVarCheckPolicy {
signature: u32,
revision: u32,
command: Unalign<U32>,
result: Unalign<U64>,
}
impl MmVarCheckPolicy {
pub fn new(data: &[u8]) -> Result<(Self, &[u8]), Error<&'static str>> {
Self::read_from_prefix(data).or(Err(Error::new(
Status::BAD_BUFFER_SIZE,
"read MmVarCheckPolicy",
)))
}
}
#[repr(C, packed)]
#[derive(Debug, IntoBytes, Immutable)]
struct MmVarCheckPolicyEnabled {
state: u8,
}
const VARIABLE_POLICY_ENTRY_REVISION: u32 = 0x00010000;
const VARIABLE_POLICY_TYPE_NO_LOCK: u8 = 0;
const VARIABLE_POLICY_TYPE_LOCK_NOW: u8 = 1;
const VARIABLE_POLICY_TYPE_LOCK_ON_CREATE: u8 = 2;
const VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE: u8 = 3;
#[repr(C, packed)]
#[derive(Debug, FromBytes)]
struct MmVariablePolicyEntry {
version: u32,
size: u16,
offset_to_name: u16,
namespace: [u8; 16],
min_size: u32,
max_size: u32,
attr_must_have: u32,
attr_cant_have: u32,
lock_policy_type: u8,
_padding: [u8; 3],
}
impl MmVariablePolicyEntry {
pub fn new(data: &[u8]) -> Result<(Self, &[u8]), Error<&'static str>> {
Self::read_from_prefix(data).or(Err(Error::new(
Status::BAD_BUFFER_SIZE,
"read VariablePolicyEntry",
)))
}
}
#[repr(C, packed)]
#[derive(Debug, FromBytes)]
struct MmVariableLockOnVarState {
namespace: [u8; 16],
value: u8,
_padding: u8,
}
impl MmVariableLockOnVarState {
pub fn new(data: &[u8]) -> Result<(Self, &[u8]), Error<&'static str>> {
Self::read_from_prefix(data).or(Err(Error::new(
Status::BAD_BUFFER_SIZE,
"read VariableLockOnVarState",
)))
}
}
#[derive(Debug)]
pub enum VariablePolicyType {
NoLock,
LockNow,
LockOnCreate,
LockOnVarState {
namespace: Guid,
name: String,
value: u8,
},
}
#[derive(Debug)]
pub struct VariablePolicyEntry {
pub namespace: Guid,
pub name: String,
pub min_size: u32,
pub max_size: u32,
pub attr_must_have: EfiVarAttr,
pub attr_cant_have: EfiVarAttr,
pub lock_policy_type: VariablePolicyType,
}
impl VariablePolicyEntry {
pub fn new(data: &[u8]) -> Result<Self, Error<&'static str>> {
let (policy, _) = MmVariablePolicyEntry::new(data)?;
let pst = core::mem::size_of::<MmVariablePolicyEntry>();
let nst = policy.offset_to_name as usize;
let end = policy.size as usize;
let name = util::cstr16_from_bytes_with_nul(&data[nst..end])?;
let policy_type = match policy.lock_policy_type {
VARIABLE_POLICY_TYPE_NO_LOCK => VariablePolicyType::NoLock,
VARIABLE_POLICY_TYPE_LOCK_NOW => VariablePolicyType::LockNow,
VARIABLE_POLICY_TYPE_LOCK_ON_CREATE => VariablePolicyType::LockOnCreate,
VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE => {
let (vstate, more) = MmVariableLockOnVarState::new(&data[pst..nst])?;
let vname = util::cstr16_from_bytes_with_nul(more)?;
VariablePolicyType::LockOnVarState {
namespace: Guid::from_bytes(vstate.namespace),
name: String::from(vname),
value: vstate.value,
}
}
_ => {
return Err(Error::new(
Status::INVALID_PARAMETER,
"invalid variable policy type",
))
}
};
Ok(VariablePolicyEntry {
namespace: Guid::from_bytes(policy.namespace),
name: String::from(name),
min_size: policy.min_size,
max_size: policy.max_size,
attr_must_have: policy.attr_must_have.into(),
attr_cant_have: policy.attr_cant_have.into(),
lock_policy_type: policy_type,
})
}
}
impl core::fmt::Display for VariablePolicyEntry {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "policy")?;
write!(f, " name={}", self.name)?;
write!(f, " size=0x{:x}-0x{:x}", self.min_size, self.max_size)?;
write!(f, " must={}", self.attr_must_have)?;
write!(f, " cant={}", self.attr_cant_have)?;
write!(f, " type={:?}", self.lock_policy_type)?;
Ok(())
}
}
#[derive(Debug)]
pub struct MmVarPolicyRequest<'a> {
hdr: MmVarCheckPolicy,
pub body: &'a [u8],
}
impl<'a> MmVarPolicyRequest<'a> {
pub fn new(data: &'a [u8]) -> Result<Self, Error<&'static str>> {
let (hdr, body) = MmVarCheckPolicy::new(data)?;
Ok(MmVarPolicyRequest { hdr, body })
}
fn response(&self, status: Status, _reply: Option<&[u8]>) -> Vec<u8> {
debug!("policy/rsp: {}", status);
let mut hdr = self.hdr.clone();
hdr.result.set((status.0 as u64).into());
let rsp = hdr.as_bytes().to_vec();
rsp
}
fn process(&self, _store: &mut EfiVarStore) -> Vec<u8> {
let cmd: u32 = self.hdr.command.get().into();
match cmd {
VAR_CHECK_POLICY_COMMAND_DISABLE => {
debug!("policy/req: disable [TODO]");
self.response(Status::SUCCESS, None)
}
VAR_CHECK_POLICY_COMMAND_IS_ENABLED => {
debug!("policy/req: is-enabled [TODO]");
let enabled = MmVarCheckPolicyEnabled {
state: 0, };
self.response(Status::SUCCESS, Some(enabled.as_bytes()))
}
VAR_CHECK_POLICY_COMMAND_REGISTER => {
debug!("policy/req: register [TODO]");
let res = VariablePolicyEntry::new(self.body);
if let Err(e) = res {
return self.response(e.status(), None);
}
let policy = res.unwrap();
debug!("{}", policy);
self.response(Status::SUCCESS, None)
}
VAR_CHECK_POLICY_COMMAND_LOCK => {
debug!("policy/req: lock [TODO]");
self.response(Status::SUCCESS, None)
}
_ => {
debug!("policy/req: unsupported command 0x{:x}", cmd);
self.response(Status::UNSUPPORTED, None)
}
}
}
}
pub fn policy_request(store: &mut EfiVarStore, req: &[u8]) -> Vec<u8> {
let res = MmVarPolicyRequest::new(req);
if let Err(e) = res.as_ref() {
error!("{e}");
return Vec::new();
}
let vreq = res.unwrap();
vreq.process(store)
}