use clap::Parser;
use log::{error, info};
use std::process::ExitCode;
use uguid::Guid;
use virtfw_libefi::guids;
use virtfw_varstore::json::JsonEfiVarStore;
use virtfw_varstore::mm::core::MmCore;
use virtfw_varstore::mm::util;
use virtfw_varstore::mm::variable::{MmVariableHeader, MmVariableRequest};
use virtfw_varstore::pcap::parse::{VarstorePcapItem, VarstorePcapParser};
use virtfw_varstore::pcap::replay::replay_pcap;
use virtfw_varstore::store::EfiVarStore;
#[derive(Parser, Debug)]
#[command(version, name = "pcap-replay", about = "dump varstore pcap files", long_about = None)]
struct Args {
#[arg(short, long)]
debug: bool,
#[arg(short, long)]
print: bool,
#[arg(long)]
replay: bool,
#[arg(long, value_name = "PCAP")]
pcap: Option<String>,
#[arg(long, value_name = "JSON")]
json: Option<String>,
}
fn print_request(data: &[u8]) {
let core = MmCore::new(data).unwrap();
let guid = Guid::from_bytes(core.guid);
match guid {
guids::EfiSmmVariableProtocol => {
let var_res = MmVariableRequest::new(core.data);
if let Err(e) = var_res.as_ref() {
error!("variable/req/parse: {e}");
return;
}
let parsed = var_res.unwrap().parsed;
if parsed.is_none() {
error!("variable/req/parse: error");
return;
}
println!("variable/req: {}", parsed.unwrap());
}
_ => {
println!("core/req: {}", core);
}
}
}
fn print_reply(data: &[u8]) {
let core = MmCore::new(data).unwrap();
let guid = Guid::from_bytes(core.guid);
match guid {
guids::EfiSmmVariableProtocol => {
let hdr_res = MmVariableHeader::from_data(core.data);
if let Err(e) = hdr_res.as_ref() {
error!("{e}");
};
let status = util::u64_to_status(hdr_res.unwrap().0.status);
println!("variable/rsp: {}", status);
}
_ => {
println!("core/rsp: {}", core);
}
}
}
fn print_pcap(filename: &str) -> std::io::Result<()> {
let parser = VarstorePcapParser::new_file(filename)?;
for p in parser {
match p {
VarstorePcapItem::Request(d) => {
print_request(&d);
}
VarstorePcapItem::Reply(d) => {
print_reply(&d);
}
_ => {
println!("{}", p);
}
}
}
Ok(())
}
fn main() -> ExitCode {
let cfg = Args::parse();
let levelfilter = if cfg.debug {
log::LevelFilter::Debug
} else {
log::LevelFilter::Info
};
env_logger::Builder::from_default_env()
.filter_module(module_path!(), levelfilter)
.filter_module("virtfw_libefi", levelfilter)
.filter_module("virtfw_varstore", levelfilter)
.format_timestamp(None)
.format_target(false)
.init();
if let Some(pcap) = cfg.pcap {
if cfg.print {
let res = print_pcap(&pcap);
if let Err(e) = res {
error!("{e}");
} else {
info!("ok");
}
}
if cfg.replay {
let mut store = EfiVarStore::new();
if let Some(filename) = cfg.json {
let Ok(content) = std::fs::read_to_string(filename) else {
error!("read json file");
return ExitCode::from(1);
};
let Ok(jstore) = serde_json::from_str::<JsonEfiVarStore>(&content) else {
error!("parse json file");
return ExitCode::from(1);
};
store.json_import(&jstore);
}
let parser = VarstorePcapParser::new_file(&pcap).expect("open pcap file");
replay_pcap(parser, &mut store);
}
}
ExitCode::from(0)
}