Skip to content

Commit 62d4c22

Browse files
committed
Add Baseboard type
1 parent afe8f55 commit 62d4c22

File tree

3 files changed

+228
-2
lines changed

3 files changed

+228
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ sudo dmitui
4747

4848
- [x] Firmware (type 0)
4949
- [x] System (type 1)
50+
- [x] Baseboard (type 2)
5051

5152
## ⚖️ License
5253

src/dmi.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod baseboard;
12
mod firmware;
23
mod system;
34

@@ -9,6 +10,7 @@ use std::{
910

1011
use anyhow::{Result, bail};
1112

13+
use crate::dmi::baseboard::Baseboard;
1214
use crate::dmi::firmware::Firmware;
1315
use crate::dmi::system::System;
1416

@@ -25,6 +27,7 @@ use ratatui::{
2527
pub struct DMI {
2628
firmware: Firmware,
2729
system: System,
30+
baseboard: Baseboard,
2831
pub focused_section: FocusedSection,
2932
}
3033

@@ -33,6 +36,7 @@ pub struct DMI {
3336
pub enum FocusedSection {
3437
Firmware,
3538
System,
39+
Baseboard,
3640
}
3741

3842
#[derive(Debug)]
@@ -47,6 +51,7 @@ impl From<[u8; 4]> for Header {
4751
let structure_type = match value[0] {
4852
0 => StructureType::Firmware,
4953
1 => StructureType::System,
54+
2 => StructureType::Baseboard,
5055
127 => StructureType::End,
5156
_ => StructureType::Other,
5257
};
@@ -64,6 +69,7 @@ impl From<[u8; 4]> for Header {
6469
pub enum StructureType {
6570
Firmware = 0,
6671
System = 1,
72+
Baseboard = 2,
6773
End = 127,
6874
Other = 255,
6975
}
@@ -74,6 +80,7 @@ impl DMI {
7480
pub fn new() -> Result<Self> {
7581
let mut firmware: Option<Firmware> = None;
7682
let mut system: Option<System> = None;
83+
let mut baseboard: Option<Baseboard> = None;
7784

7885
let mem_file = File::open("/sys/firmware/dmi/tables/DMI")?;
7986
let mut file = BufReader::new(mem_file);
@@ -128,13 +135,17 @@ impl DMI {
128135
StructureType::System => {
129136
system = Some(System::from((data, text)));
130137
}
138+
StructureType::Baseboard => {
139+
baseboard = Some(Baseboard::from((data, text)));
140+
}
131141
_ => {}
132142
}
133143
}
134144

135145
Ok(Self {
136146
firmware: firmware.unwrap(),
137147
system: system.unwrap(),
148+
baseboard: baseboard.unwrap(),
138149
focused_section: FocusedSection::Firmware,
139150
})
140151
}
@@ -145,11 +156,13 @@ impl DMI {
145156
KeyCode::Down | KeyCode::Char('j') => {}
146157
KeyCode::Tab => match self.focused_section {
147158
FocusedSection::Firmware => self.focused_section = FocusedSection::System,
148-
FocusedSection::System => self.focused_section = FocusedSection::Firmware,
159+
FocusedSection::System => self.focused_section = FocusedSection::Baseboard,
160+
FocusedSection::Baseboard => self.focused_section = FocusedSection::Firmware,
149161
},
150162
KeyCode::BackTab => match self.focused_section {
151-
FocusedSection::Firmware => self.focused_section = FocusedSection::System,
163+
FocusedSection::Firmware => self.focused_section = FocusedSection::Baseboard,
152164
FocusedSection::System => self.focused_section = FocusedSection::Firmware,
165+
FocusedSection::Baseboard => self.focused_section = FocusedSection::System,
153166
},
154167
_ => {}
155168
}
@@ -178,6 +191,16 @@ impl DMI {
178191
Span::from(" System ").fg(Color::DarkGray)
179192
}
180193
}
194+
FocusedSection::Baseboard => {
195+
if is_focused {
196+
Span::styled(
197+
" Baseboard ",
198+
Style::default().bg(Color::Yellow).fg(Color::White).bold(),
199+
)
200+
} else {
201+
Span::from(" Baseboard ").fg(Color::DarkGray)
202+
}
203+
}
181204
}
182205
}
183206

@@ -197,6 +220,7 @@ impl DMI {
197220
.title(Line::from(vec![
198221
self.title_span(FocusedSection::Firmware),
199222
self.title_span(FocusedSection::System),
223+
self.title_span(FocusedSection::Baseboard),
200224
]))
201225
.title_alignment(Alignment::Left)
202226
.padding(Padding::top(1))
@@ -221,6 +245,9 @@ impl DMI {
221245
FocusedSection::System => {
222246
self.system.render(frame, section_block);
223247
}
248+
FocusedSection::Baseboard => {
249+
self.baseboard.render(frame, section_block);
250+
}
224251
}
225252
}
226253
}

src/dmi/baseboard.rs

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
use std::fmt::Display;
2+
3+
use ratatui::{
4+
Frame,
5+
layout::{Constraint, Direction, Layout, Margin, Rect},
6+
style::{Style, Stylize},
7+
widgets::{Block, Cell, List, Padding, Row, Table},
8+
};
9+
10+
#[derive(Debug)]
11+
pub struct Baseboard {
12+
manufacturer: String,
13+
product: String,
14+
version: String,
15+
serial_number: String,
16+
asset_tag: String,
17+
features: Vec<Feaures>,
18+
loacation_in_chassis: String,
19+
board_type: BoardType,
20+
}
21+
22+
#[derive(Debug)]
23+
enum Feaures {
24+
HotSwappable,
25+
Replaceable,
26+
Removable,
27+
RequiresOneDaughterBoard,
28+
HostingBoard,
29+
}
30+
31+
impl Display for Feaures {
32+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33+
match self {
34+
Self::HotSwappable => write!(f, "The board is hot swappable"),
35+
Self::Replaceable => write!(f, "The board is replaceable"),
36+
Self::Removable => write!(f, "The board is removable"),
37+
Self::RequiresOneDaughterBoard => write!(
38+
f,
39+
"The board requires at least one daughter board or auxiliary card to function properly"
40+
),
41+
Self::HostingBoard => write!(
42+
f,
43+
"The board is a hosting board (for example, a motherboard)."
44+
),
45+
}
46+
}
47+
}
48+
49+
#[derive(Debug)]
50+
enum BoardType {
51+
Unknown,
52+
Other,
53+
ServerBlade,
54+
ConnectivitySwitch,
55+
SystemManagementModule,
56+
ProcessorModule,
57+
IOModule,
58+
MemoryModule,
59+
DaughterBoard,
60+
MotherBoard,
61+
ProcessorMemoryModule,
62+
ProcessorIOModule,
63+
InterconnectedModule,
64+
}
65+
impl Display for BoardType {
66+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67+
match self {
68+
Self::Unknown => write!(f, "Unknown"),
69+
Self::Other => write!(f, "Other"),
70+
Self::ServerBlade => write!(f, "Server Blade"),
71+
Self::ConnectivitySwitch => write!(f, "Connectivity Switch"),
72+
Self::SystemManagementModule => write!(f, "System Management Module"),
73+
Self::ProcessorModule => write!(f, "Processor Module"),
74+
Self::IOModule => write!(f, "I/O Module"),
75+
Self::MemoryModule => write!(f, "Memory Module"),
76+
Self::DaughterBoard => write!(f, "Daughter board"),
77+
Self::MotherBoard => write!(f, "Motherboard (includes processor, memory, and I/O)"),
78+
Self::ProcessorMemoryModule => write!(f, "Processor/Memory Module"),
79+
Self::ProcessorIOModule => write!(f, "Processor/IO Module"),
80+
Self::InterconnectedModule => write!(f, "Interconnect board"),
81+
}
82+
}
83+
}
84+
85+
impl From<u8> for BoardType {
86+
fn from(value: u8) -> Self {
87+
match value {
88+
1 => Self::Unknown,
89+
2 => Self::Other,
90+
3 => Self::ServerBlade,
91+
4 => Self::ConnectivitySwitch,
92+
5 => Self::SystemManagementModule,
93+
6 => Self::ProcessorModule,
94+
7 => Self::IOModule,
95+
8 => Self::MemoryModule,
96+
9 => Self::DaughterBoard,
97+
10 => Self::MotherBoard,
98+
11 => Self::ProcessorMemoryModule,
99+
12 => Self::ProcessorIOModule,
100+
13 => Self::InterconnectedModule,
101+
_ => unreachable!(),
102+
}
103+
}
104+
}
105+
106+
impl From<(Vec<u8>, Vec<String>)> for Baseboard {
107+
fn from((data, text): (Vec<u8>, Vec<String>)) -> Self {
108+
let mut features = Vec::new();
109+
110+
if data[5] & 1 != 0 {
111+
features.push(Feaures::HostingBoard);
112+
};
113+
114+
if data[5] & (1 << 1) != 0 {
115+
features.push(Feaures::RequiresOneDaughterBoard);
116+
};
117+
118+
if data[5] & (1 << 2) != 0 {
119+
features.push(Feaures::Removable);
120+
}
121+
122+
if data[5] & (1 << 3) != 0 {
123+
features.push(Feaures::Replaceable);
124+
}
125+
126+
if data[5] & (1 << 4) != 0 {
127+
features.push(Feaures::HotSwappable);
128+
}
129+
130+
Self {
131+
manufacturer: text[data[0].saturating_sub(1) as usize].clone(),
132+
product: text[data[1].saturating_sub(1) as usize].clone(),
133+
version: text[data[2].saturating_sub(1) as usize].clone(),
134+
serial_number: text[data[3].saturating_sub(1) as usize].clone(),
135+
asset_tag: text[data[4].saturating_sub(1) as usize].clone(),
136+
features,
137+
loacation_in_chassis: text[data[8].saturating_sub(1) as usize].clone(),
138+
board_type: BoardType::from(data[9]),
139+
}
140+
}
141+
}
142+
143+
impl Baseboard {
144+
pub fn render(&self, frame: &mut Frame, block: Rect) {
145+
let (infos_block, feaures_block) = {
146+
let chunks = Layout::default()
147+
.direction(Direction::Vertical)
148+
.constraints([Constraint::Length(15), Constraint::Fill(1)])
149+
.split(block);
150+
151+
(chunks[0], chunks[1])
152+
};
153+
154+
let rows = vec![
155+
Row::new(vec![
156+
Cell::from("Manufacturer").bold(),
157+
Cell::from(self.manufacturer.clone()),
158+
]),
159+
Row::new(vec![
160+
Cell::from("Product").bold(),
161+
Cell::from(self.product.clone()),
162+
]),
163+
Row::new(vec![
164+
Cell::from("Version").bold(),
165+
Cell::from(self.version.clone()),
166+
]),
167+
Row::new(vec![
168+
Cell::from("Serial Number").bold(),
169+
Cell::from(self.serial_number.to_string()),
170+
]),
171+
Row::new(vec![
172+
Cell::from("Asset Tag").bold(),
173+
Cell::from(self.asset_tag.to_string()),
174+
]),
175+
Row::new(vec![
176+
Cell::from("Location in Chassis").bold(),
177+
Cell::from(self.loacation_in_chassis.to_string()),
178+
]),
179+
Row::new(vec![
180+
Cell::from("Board Type").bold(),
181+
Cell::from(self.board_type.to_string()),
182+
]),
183+
];
184+
185+
let widths = [Constraint::Length(20), Constraint::Fill(1)];
186+
let table = Table::new(rows, widths).block(Block::new().padding(Padding::uniform(2)));
187+
frame.render_widget(table, infos_block.inner(Margin::new(2, 0)));
188+
189+
let feaures = self.features.iter().map(|feature| format!("* {feature}"));
190+
let list = List::new(feaures).block(
191+
Block::new()
192+
.title(" Features")
193+
.title_style(Style::new().bold())
194+
.padding(Padding::symmetric(2, 1)),
195+
);
196+
frame.render_widget(list, feaures_block.inner(Margin::new(2, 0)));
197+
}
198+
}

0 commit comments

Comments
 (0)