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