hvlite_pcat_locator/
lib.rs1#![forbid(unsafe_code)]
7
8use anyhow::Context;
9use mesh::MeshPayload;
10use std::fs::File;
11use std::path::Path;
12use std::path::PathBuf;
13use std::process::Command;
14
15mod resource_dll_parser;
16
17#[derive(Debug, MeshPayload)]
18pub struct RomFileLocation {
20 pub file: File,
22 pub start: u64,
24 pub len: usize,
26}
27
28fn system32_path() -> String {
30 let windows_dir = std::env::var("SystemRoot").unwrap_or(String::from(r"C:\Windows"));
33 format!("{windows_dir}\\System32")
34}
35
36pub fn find_pcat_bios(command_line_path: Option<&Path>) -> anyhow::Result<RomFileLocation> {
39 const NAME: &str = "pcat_firmware";
40 const BIOS_DESCRIPTOR: DllResourceDescriptor = DllResourceDescriptor::new(b"VMFW", 13500);
41 const EXPECTED_BIOS_SIZE: usize = 256 * 1024;
42
43 if let Some(p) = command_line_path {
44 parse_rom_file(p, NAME, BIOS_DESCRIPTOR, EXPECTED_BIOS_SIZE)
45 } else {
46 let system32_path = system32_path();
47 let default_pcat_firmware_file = format!(r"{system32_path}\vmfirmwarepcat.dll");
49
50 let result = match parse_rom_file(
51 &translate_path(Path::new(default_pcat_firmware_file.as_str()))?,
52 NAME,
53 BIOS_DESCRIPTOR,
54 EXPECTED_BIOS_SIZE,
55 ) {
56 Ok(r) => r,
57 Err(_) => {
58 let legacy_pcat_firmware_file = format!(r"{system32_path}\vmfirmware.dll");
60
61 parse_rom_file(
62 &translate_path(Path::new(legacy_pcat_firmware_file.as_str()))?,
63 NAME,
64 BIOS_DESCRIPTOR,
65 EXPECTED_BIOS_SIZE,
66 )?
67 }
68 };
69 Ok(result)
70 }
71}
72
73pub fn find_svga_bios(command_line_path: Option<&Path>) -> anyhow::Result<RomFileLocation> {
76 const SVGA_BIOS_DESCRIPTOR: DllResourceDescriptor = DllResourceDescriptor::new(b"BIOS", 13501);
77 const NAME: &str = "vga_firmware";
78 const EXPECTED_SIZE: usize = 48 * 1024;
79
80 if let Some(p) = command_line_path {
81 parse_rom_file(p, NAME, SVGA_BIOS_DESCRIPTOR, EXPECTED_SIZE)
82 } else {
83 let system32_path = system32_path();
84 let default_svga_firmware_file = format!(r"{system32_path}\vmemulateddevices.dll");
86
87 parse_rom_file(
88 &translate_path(Path::new(default_svga_firmware_file.as_str()))?,
89 NAME,
90 SVGA_BIOS_DESCRIPTOR,
91 EXPECTED_SIZE,
92 )
93 }
94}
95
96fn translate_path(path: &Path) -> anyhow::Result<PathBuf> {
98 let file_path = if cfg!(windows) {
99 path.into()
100 } else if cfg!(target_os = "linux") {
101 let output = Command::new("wslpath")
103 .arg(path)
104 .output()
105 .context("Failed to translate path to windows. Are you not on WSL?")?;
106
107 String::from_utf8_lossy(&output.stdout).trim().into()
108 } else {
109 anyhow::bail!("No path specified for firmware and no default is configured for this OS.")
110 };
111
112 Ok(file_path)
113}
114
115fn parse_rom_file(
118 file_path: &Path,
119 firmware_name: &str,
120 descriptor: DllResourceDescriptor,
121 expected_len: usize,
122) -> anyhow::Result<RomFileLocation> {
123 tracing::debug!(
124 ?file_path,
125 ?firmware_name,
126 "Attempting to load firmware file."
127 );
128
129 let file = fs_err::File::open(file_path)?;
130
131 let (start, len) = if let Some(maybe_resource) =
132 resource_dll_parser::try_find_resource_from_dll(&file, &descriptor)?
133 {
134 maybe_resource
135 } else {
136 (0, file.metadata()?.len() as usize)
137 };
138
139 if len != expected_len {
140 tracing::warn!(
141 firmware_name,
142 len,
143 expected_len,
144 "ROM data length does not match expected length, trying anyways",
145 );
146 }
147
148 Ok(RomFileLocation {
149 file: file.into(),
150 start,
151 len,
152 })
153}
154
155pub(crate) struct DllResourceDescriptor {
156 resource_type: [u8; 8],
158 id: u32,
159}
160
161impl DllResourceDescriptor {
162 const fn new(resource_type: &[u8; 4], id: u32) -> Self {
163 Self {
164 id,
165 resource_type: [
167 resource_type[0],
168 0,
169 resource_type[1],
170 0,
171 resource_type[2],
172 0,
173 resource_type[3],
174 0,
175 ],
176 }
177 }
178}