Extend redbear-info for VirtIO VM networking

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-14 12:07:52 +01:00
parent 476285c925
commit 42db2357d7
@@ -16,6 +16,8 @@ const BLUE: &str = "\x1b[34m";
const DIVIDER: &str = "═══════════════════════════════════════════════════════════════════";
const RTL8125_VENDOR_ID: u16 = 0x10ec;
const RTL8125_DEVICE_ID: u16 = 0x8125;
const VIRTIO_NET_VENDOR_ID: u16 = 0x1af4;
const VIRTIO_NET_DEVICE_ID: u16 = 0x1000;
#[derive(Clone, Copy, PartialEq, Eq)]
enum OutputMode {
@@ -67,6 +69,7 @@ struct HardwareReport {
usb_controllers: usize,
drm_cards: usize,
rtl8125_present: bool,
virtio_net_present: bool,
}
struct IntegrationCheck {
@@ -238,6 +241,16 @@ const INTEGRATIONS: &[IntegrationCheck] = &[
note: "This only becomes functional when 10ec:8125 hardware is present and a network.* scheme is live.",
functional_probe: Some(probe_rtl8125_path),
},
IntegrationCheck {
name: "virtio-net-vm-path",
category: "Networking",
description: "VirtIO network support for QEMU and other virtualized baselines",
artifact_path: Some("/usr/lib/drivers/virtio-netd"),
control_path: Some("/scheme/pci"),
test_hint: "redbear-info --verbose",
note: "This becomes functional when a VirtIO NIC (1af4:1000) is present and a network.* scheme is live.",
functional_probe: Some(probe_virtio_net_path),
},
];
fn main() {
@@ -432,11 +445,33 @@ fn collect_hardware(runtime: &Runtime, network: &NetworkReport) -> HardwareRepor
.iter()
.any(|name| name.contains("rtl8125"));
let virtio_net_present = runtime
.read_dir_names("/scheme/pci")
.unwrap_or_default()
.into_iter()
.any(|entry| {
let config_path = format!("/scheme/pci/{entry}/config");
let Some(bytes) = read_bytes(runtime, &config_path) else {
return false;
};
if bytes.len() < 4 {
return false;
}
let vendor = u16::from_le_bytes([bytes[0], bytes[1]]);
let device = u16::from_le_bytes([bytes[2], bytes[3]]);
vendor == VIRTIO_NET_VENDOR_ID && device == VIRTIO_NET_DEVICE_ID
})
|| network
.network_schemes
.iter()
.any(|name| name.contains("virtio") || name.contains("eth0"));
HardwareReport {
pci_devices,
usb_controllers,
drm_cards,
rtl8125_present,
virtio_net_present,
}
}
@@ -628,6 +663,14 @@ fn print_table(report: &Report<'_>, verbose: bool) {
"no"
}
);
println!(
" VirtIO NIC seen: {}",
if report.hardware.virtio_net_present {
"yes"
} else {
"no"
}
);
println!();
print_section_header("Integrations");
@@ -796,6 +839,13 @@ fn print_json(report: &Report<'_>) {
&mut out,
"rtl8125_present",
report.hardware.rtl8125_present,
true,
4,
);
push_json_bool_field(
&mut out,
"virtio_net_present",
report.hardware.virtio_net_present,
false,
4,
);
@@ -1031,6 +1081,31 @@ fn probe_rtl8125_path(
)
}
fn probe_virtio_net_path(
_runtime: &Runtime,
network: &NetworkReport,
hardware: &HardwareReport,
_check: &IntegrationCheck,
) -> Option<String> {
if !hardware.virtio_net_present {
return None;
}
Some(
if network
.network_schemes
.iter()
.any(|name| name.contains("virtio") || name.contains("eth0"))
{
"VirtIO NIC seen and network scheme surface is visible".to_string()
} else if network.connected {
"VirtIO NIC seen and native network stack reports a configured address".to_string()
} else {
"VirtIO NIC seen through /scheme/pci".to_string()
},
)
}
fn push_json_string(output: &mut String, value: &str) {
output.push('"');
for ch in value.chars() {
@@ -1271,6 +1346,33 @@ mod tests {
fs::remove_dir_all(root).unwrap();
}
#[test]
fn virtio_net_hardware_detection_parses_pci_config() {
let root = temp_root();
create_dir(&root, "/scheme/pci/0000--00--03.0");
let config = [
(VIRTIO_NET_VENDOR_ID & 0xff) as u8,
(VIRTIO_NET_VENDOR_ID >> 8) as u8,
(VIRTIO_NET_DEVICE_ID & 0xff) as u8,
(VIRTIO_NET_DEVICE_ID >> 8) as u8,
0,
0,
0,
0,
];
let path = root.join("scheme/pci/0000--00--03.0/config");
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).unwrap();
}
fs::write(&path, config).unwrap();
let network = collect_network(&Runtime::from_root(root.clone()));
let hardware = collect_hardware(&Runtime::from_root(root.clone()), &network);
assert!(hardware.virtio_net_present);
fs::remove_dir_all(root).unwrap();
}
#[test]
fn json_output_contains_network_and_integration_state() {
let root = temp_root();
@@ -1288,6 +1390,7 @@ mod tests {
);
let report = collect_report(&Runtime::from_root(root.clone()));
assert!(!report.hardware.virtio_net_present);
let mut output = String::new();
output.push_str("{");
push_json_string_field(