use alloc::vec::Vec;
use vck_common::{handover::payload::HandoverPayload, VckError, VckResult};
use wdk_sys::{ntddk::ExFreePool, GUID, NTSTATUS};
use crate::nt::UnicodeString;
const POOL_FLAG_NON_PAGED: u64 = 0x0000_0000_0000_0040;
const VCK_POOL_TAG: u32 = u32::from_le_bytes(*b"VCKH");
const STATUS_BUFFER_TOO_SMALL: NTSTATUS = 0xC000_0023u32 as i32;
extern "system" {
fn ExGetFirmwareEnvironmentVariable(
variable_name: *mut wdk_sys::UNICODE_STRING,
vendor_guid: *mut GUID,
value: *mut core::ffi::c_void,
value_length: *mut u32,
attributes: *mut u32,
) -> NTSTATUS;
fn ExAllocatePool2(flags: u64, size: u64, tag: u32) -> *mut core::ffi::c_void;
}
fn handover_guid(b: [u8; 16]) -> GUID {
GUID {
Data1: u32::from_le_bytes([b[0], b[1], b[2], b[3]]),
Data2: u16::from_le_bytes([b[4], b[5]]),
Data3: u16::from_le_bytes([b[6], b[7]]),
Data4: [b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]],
}
}
pub fn read_handover<P: HandoverPayload>() -> VckResult<P> {
let bytes = read_handover_variable(P::VAR_NAME, P::VAR_GUID)?;
vck_common::handover::payload::decode_payload::<P>(&bytes)
}
fn read_handover_variable(var_name: &str, var_guid: [u8; 16]) -> VckResult<Vec<u8>> {
let name = UnicodeString::new(var_name);
let mut guid = handover_guid(var_guid);
let mut len: u32 = 0;
let mut dummy = [0u8; 1];
let st = unsafe {
ExGetFirmwareEnvironmentVariable(
name.as_ptr(),
&mut guid,
dummy.as_mut_ptr().cast(),
&mut len,
core::ptr::null_mut(),
)
};
if st != STATUS_BUFFER_TOO_SMALL || len == 0 {
crate::vck_log!("read_handover: size probe status=0x{:08x} len={}", st, len);
return Err(VckError::NotFound("handover variable not present"));
}
let total = len as usize;
let buf =
unsafe { ExAllocatePool2(POOL_FLAG_NON_PAGED, total as u64, VCK_POOL_TAG) as *mut u8 };
if buf.is_null() {
return Err(VckError::Io("ExAllocatePool2(handover var) failed".into()));
}
let mut got = len;
let st = unsafe {
ExGetFirmwareEnvironmentVariable(
name.as_ptr(),
&mut guid,
buf.cast(),
&mut got,
core::ptr::null_mut(),
)
};
if st < 0 {
unsafe {
core::ptr::write_bytes(buf, 0, total);
ExFreePool(buf.cast());
}
crate::vck_log!("read_handover: get status=0x{:08x}", st);
return Err(VckError::Io(
"ExGetFirmwareEnvironmentVariable(get) failed".into(),
));
}
let out = unsafe {
let copy_len = (got as usize).min(total);
let v = core::slice::from_raw_parts(buf, copy_len).to_vec();
core::ptr::write_bytes(buf, 0, total);
ExFreePool(buf.cast());
v
};
crate::vck_log!("read_handover: variable read ok ({} bytes)", out.len());
Ok(out)
}