riscfetch_core/
hardware.rs

1//! Hardware information reading from /proc and /sys
2
3use crate::parsing::parse_vector_from_isa;
4use crate::types::HardwareIds;
5use std::fs;
6use sysinfo::System;
7
8/// Get raw ISA string (e.g., "rv64imafdcv_zicsr_...")
9pub fn get_isa_string() -> String {
10    if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
11        for line in content.lines() {
12            if line.starts_with("isa") {
13                if let Some(isa) = line.split(':').nth(1) {
14                    return isa.trim().to_string();
15                }
16            }
17        }
18    }
19    "unknown".to_string()
20}
21
22/// Get hardware IDs (mvendorid, marchid, mimpid)
23pub fn get_hardware_ids() -> HardwareIds {
24    let mut ids = HardwareIds::default();
25
26    if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
27        for line in content.lines() {
28            if line.starts_with("mvendorid") {
29                if let Some(val) = line.split(':').nth(1) {
30                    let val = val.trim();
31                    if !val.is_empty() && val != "0x0" {
32                        ids.mvendorid = val.to_string();
33                    }
34                }
35            } else if line.starts_with("marchid") {
36                if let Some(val) = line.split(':').nth(1) {
37                    let val = val.trim();
38                    if !val.is_empty() && val != "0x0" {
39                        ids.marchid = val.to_string();
40                    }
41                }
42            } else if line.starts_with("mimpid") {
43                if let Some(val) = line.split(':').nth(1) {
44                    let val = val.trim();
45                    if !val.is_empty() && val != "0x0" {
46                        ids.mimpid = val.to_string();
47                    }
48                }
49            }
50        }
51    }
52
53    ids
54}
55
56/// Get hart count as formatted string
57pub fn get_hart_count() -> String {
58    if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
59        let count = content
60            .lines()
61            .filter(|line| line.starts_with("processor"))
62            .count();
63        if count > 0 {
64            return format!("{count} hart{}", if count > 1 { "s" } else { "" });
65        }
66    }
67
68    let mut sys = System::new();
69    sys.refresh_cpu_all();
70    let count = sys.cpus().len();
71    format!("{count} hart{}", if count > 1 { "s" } else { "" })
72}
73
74/// Get hart count as number
75pub fn get_hart_count_num() -> usize {
76    if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
77        let count = content
78            .lines()
79            .filter(|line| line.starts_with("processor"))
80            .count();
81        if count > 0 {
82            return count;
83        }
84    }
85
86    let mut sys = System::new();
87    sys.refresh_cpu_all();
88    sys.cpus().len()
89}
90
91/// Get cache information
92pub fn get_cache_info() -> String {
93    let mut cache_parts = Vec::new();
94
95    if let Ok(l1d_size) = fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index0/size") {
96        let size = l1d_size.trim();
97        if !size.is_empty() {
98            cache_parts.push(format!("L1D:{size}"));
99        }
100    }
101
102    if let Ok(l1i_size) = fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index1/size") {
103        let size = l1i_size.trim();
104        if !size.is_empty() {
105            cache_parts.push(format!("L1I:{size}"));
106        }
107    }
108
109    if let Ok(l2_size) = fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index2/size") {
110        let size = l2_size.trim();
111        if !size.is_empty() {
112            cache_parts.push(format!("L2:{size}"));
113        }
114    }
115
116    if let Ok(l3_size) = fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index3/size") {
117        let size = l3_size.trim();
118        if !size.is_empty() {
119            cache_parts.push(format!("L3:{size}"));
120        }
121    }
122
123    cache_parts.join(" ")
124}
125
126/// Get board/model information from device tree
127pub fn get_board_info() -> String {
128    if let Ok(content) = fs::read_to_string("/proc/device-tree/model") {
129        let model = content.trim_matches('\0').trim();
130        if !model.is_empty() {
131            return model.to_string();
132        }
133    }
134
135    if let Ok(content) = fs::read_to_string("/proc/device-tree/compatible") {
136        let parts: Vec<&str> = content.split('\0').collect();
137        if let Some(first) = parts.first() {
138            if !first.is_empty() {
139                return first.to_string();
140            }
141        }
142    }
143
144    String::new()
145}
146
147/// Get vector extension details (VLEN, ELEN)
148pub fn get_vector_detail() -> String {
149    let isa = get_isa_string();
150    let mut result = parse_vector_from_isa(&isa).unwrap_or_default();
151
152    // Try to get actual VLEN from sysfs
153    if !result.is_empty() {
154        if let Ok(vlen) = fs::read_to_string("/sys/devices/system/cpu/cpu0/riscv/vlen") {
155            result.push_str(&format!(", VLEN={}", vlen.trim()));
156        }
157    }
158
159    result
160}