Skip to content

Commit d80b8e9

Browse files
JohnAZoidbergclaude
andcommitted
wmi: Add INF file parsing for boot-only drivers
Parse driver versions from INF files in the Windows driver store for drivers that are not loaded at runtime (like AMD DRTM). This allows detection of boot-only drivers that don't appear via WMI. Changes: - Add inf_drivers section to drivers.toml for INF-based detection - Add find_inf_driver_version() to scan driver store directories - Add parse_inf_version() with UTF-8 and UTF-16 LE support - Fix clippy warnings in chromium_ec and esrt modules Detects: AMD DRTM Driver, Realtek MEP Opt-in Driver Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent e1cfb2a commit d80b8e9

4 files changed

Lines changed: 116 additions & 1 deletion

File tree

framework_lib/src/chromium_ec/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ impl Default for CrosEc {
227227
/// Find out which drivers are available
228228
///
229229
/// Depending on the availability we choose the first one as default
230+
#[allow(clippy::vec_init_then_push)]
230231
fn available_drivers() -> Vec<CrosEcDriverType> {
231232
let mut drivers = vec![];
232233

framework_lib/src/drivers.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,11 @@ GeneStor = "Framework SD Expansion Card"
6969
# MediaTek WiFi Drivers
7070
mtkwlex = "RZ616 WiFi Driver"
7171
mtkwecx = "RZ717 WiFi Driver"
72+
73+
# INF Drivers - detected via driver store INF file parsing
74+
# These are boot-only drivers not visible via WMI at runtime
75+
# Format: inf_name (without .inf) = "display_alias"
76+
77+
[inf_drivers]
78+
amddrtm = "AMD DRTM Driver"
79+
rtextfw = "Realtek MEP Opt-in Driver"

framework_lib/src/esrt/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ pub fn get_esrt() -> Option<Esrt> {
438438
let short_guid = guid.trim_matches(|c| c == '{' || c == '}');
439439
let esrt_entry = esrt.open_subkey(&guid).ok()?;
440440
let esrt = EsrtResourceEntry {
441-
fw_class: GUID::parse(&short_guid).ok()?.into(),
441+
fw_class: GUID::parse(short_guid).ok()?.into(),
442442
fw_type: esrt_entry.get_value("Type").ok()?,
443443
fw_version: esrt_entry.get_value("Version").ok()?,
444444
lowest_supported_fw_version: esrt_entry.get_value("LowestSupportedVersion").ok()?,

framework_lib/src/wmi.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::util::Platform;
22
use serde::Deserialize;
33
use std::collections::HashMap;
4+
use std::fs;
5+
use std::path::Path;
46
use wmi::*;
57

68
/// Driver configuration loaded from TOML
@@ -9,6 +11,8 @@ struct DriversConfig {
911
pnp_drivers: HashMap<String, String>,
1012
products: HashMap<String, String>,
1113
system_drivers: HashMap<String, String>,
14+
#[serde(default)]
15+
inf_drivers: HashMap<String, String>,
1216
}
1317

1418
/// Platform-specific baseline configuration
@@ -47,6 +51,93 @@ fn load_baseline_for_platform(platform: &Platform) -> BaselineConfig {
4751
toml::from_str(config_str).unwrap_or_default()
4852
}
4953

54+
const DRIVER_STORE_PATH: &str = r"C:\Windows\System32\DriverStore\FileRepository";
55+
56+
/// Find driver INF file in the driver store and extract version
57+
fn find_inf_driver_version(inf_name: &str) -> Option<String> {
58+
let driver_store = Path::new(DRIVER_STORE_PATH);
59+
if !driver_store.exists() {
60+
return None;
61+
}
62+
63+
// Look for directories matching the INF name pattern (e.g., amddrtm.inf_amd64_*)
64+
let pattern = format!("{}.inf_", inf_name);
65+
let mut best_match: Option<(String, std::time::SystemTime)> = None;
66+
67+
if let Ok(entries) = fs::read_dir(driver_store) {
68+
for entry in entries.flatten() {
69+
if let Ok(name) = entry.file_name().into_string() {
70+
if name.starts_with(&pattern) {
71+
// Only consider directories, not files like .ini
72+
if let Ok(metadata) = entry.metadata() {
73+
if !metadata.is_dir() {
74+
continue;
75+
}
76+
// Get the modification time to find the newest version
77+
if let Ok(modified) = metadata.modified() {
78+
let should_update = match &best_match {
79+
None => true,
80+
Some((_, prev_time)) => modified > *prev_time,
81+
};
82+
if should_update {
83+
best_match =
84+
Some((entry.path().to_string_lossy().to_string(), modified));
85+
}
86+
}
87+
}
88+
}
89+
}
90+
}
91+
}
92+
93+
// If we found a matching directory, read the INF file and extract version
94+
if let Some((dir_path, _)) = best_match {
95+
let inf_file = Path::new(&dir_path).join(format!("{}.inf", inf_name));
96+
if inf_file.exists() {
97+
return parse_inf_version(&inf_file);
98+
}
99+
}
100+
101+
None
102+
}
103+
104+
/// Parse INF file and extract DriverVer version number
105+
fn parse_inf_version(inf_path: &Path) -> Option<String> {
106+
// Try reading as UTF-8 first, then as UTF-16 LE (common for Windows INF files)
107+
let content = match fs::read_to_string(inf_path) {
108+
Ok(c) => c,
109+
Err(_) => {
110+
// Try reading as UTF-16 LE
111+
let bytes = fs::read(inf_path).ok()?;
112+
// Skip BOM if present and convert UTF-16 LE to String
113+
let start = if bytes.len() >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE {
114+
2
115+
} else {
116+
0
117+
};
118+
let u16_chars: Vec<u16> = bytes[start..]
119+
.chunks_exact(2)
120+
.map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
121+
.collect();
122+
String::from_utf16(&u16_chars).ok()?
123+
}
124+
};
125+
126+
// Look for DriverVer line, e.g.: DriverVer = 11/11/2024,1.0.18.4
127+
for line in content.lines() {
128+
let trimmed = line.trim();
129+
if trimmed.to_lowercase().starts_with("driverver") {
130+
// Extract version after the comma
131+
if let Some(comma_pos) = trimmed.find(',') {
132+
let version = trimmed[comma_pos + 1..].trim();
133+
return Some(version.to_string());
134+
}
135+
}
136+
}
137+
138+
None
139+
}
140+
50141
/// Collected driver information for baseline updates
51142
#[derive(Debug, Default)]
52143
pub struct DetectedDrivers {
@@ -257,6 +348,14 @@ pub fn print_drivers_with_baseline(platform: Option<&Platform>) {
257348
print_version_with_baseline(&version, alias, &baseline);
258349
}
259350
}
351+
352+
// INF Drivers (boot-only drivers from driver store)
353+
for (inf_name, alias) in &config.inf_drivers {
354+
if let Some(version) = find_inf_driver_version(inf_name) {
355+
println!(" {}", alias);
356+
print_version_with_baseline(&version, alias, &baseline);
357+
}
358+
}
260359
}
261360

262361
/// Print version with baseline comparison
@@ -362,5 +461,12 @@ pub fn collect_drivers() -> DetectedDrivers {
362461
}
363462
}
364463

464+
// INF Drivers (boot-only drivers from driver store)
465+
for (inf_name, alias) in &config.inf_drivers {
466+
if let Some(version) = find_inf_driver_version(inf_name) {
467+
detected.drivers.insert(alias.clone(), version);
468+
}
469+
}
470+
365471
detected
366472
}

0 commit comments

Comments
 (0)