intel_crashlog/record/
decode.rs1use super::Record;
5#[cfg(feature = "collateral_manager")]
6use crate::collateral::{CollateralManager, CollateralTree};
7use crate::error::Error;
8use crate::header::record_types;
9use crate::node::Node;
10use crate::node::NodeType;
11#[cfg(not(feature = "std"))]
12use alloc::{borrow::ToOwned, str, string::String, vec::Vec};
13use log::debug;
14#[cfg(feature = "std")]
15use std::str;
16
17const DELIMITER: char = ';';
18
19#[derive(Default, Debug)]
20struct DecodeDefinitionEntry {
21 pub name: String,
22 pub offset: usize,
23 pub size: usize,
24 pub description: String,
25}
26
27impl Record {
28 fn read_field(&self, offset: usize, size: usize) -> Option<u64> {
29 if size > 64 {
30 return None;
32 }
33
34 let mut value = 0;
35 let mut bit = 0;
36
37 while bit < size {
38 let chunk_size = 8;
39 let chunk = (offset + bit) / chunk_size;
40 if chunk >= self.data.len() {
41 return None;
42 }
43
44 let bit_offset = (offset + bit) % chunk_size;
45 let mask = (1 << (size - bit).min(chunk_size)) - 1;
46 value |= ((self.data[chunk] as u64 >> bit_offset) & mask) << bit;
47 bit += chunk_size - bit_offset;
48 }
49
50 Some(value)
51 }
52
53 pub fn decode_with_csv(&self, layout: &[u8], offset: usize) -> Result<Node, Error> {
84 let mut root = Node::root();
85
86 let csv = str::from_utf8(layout)?;
87 let mut columns = Vec::new();
88 let mut current_path = Vec::new();
89
90 for (i, line) in csv.lines().enumerate() {
91 if i == 0 {
92 columns = line.split(DELIMITER).collect();
93 debug!("CSV columns: {columns:?}");
94 continue;
95 }
96
97 let mut entry = DecodeDefinitionEntry::default();
98
99 for (i, field) in line.split(DELIMITER).enumerate() {
100 if let Some(column) = columns.get(i) {
101 match *column {
102 "name" => entry.name = field.into(),
103 "offset" => entry.offset = field.parse()?,
104 "size" => entry.size = field.parse()?,
105 "description" => entry.description = field.into(),
106 _ => (),
107 }
108 }
109 }
110
111 if entry.name.is_empty() {
112 continue;
113 }
114
115 let mut segments = entry.name.split(".");
116 let Some(top) = segments.next() else {
117 continue;
118 };
119
120 if !top.is_empty() {
121 current_path.clear();
123 current_path.push(top.to_owned());
124
125 if root.get(top).is_none() {
126 root.add(Node::record(top));
128 }
129 }
130
131 for segment in segments {
132 if segment.is_empty() {
133 let _ = current_path.pop();
134 } else {
135 current_path.push(segment.to_owned());
136 }
137 }
138
139 let node = root.create_hierarchy_from_iter(¤t_path);
140 node.description = entry.description;
141 if let Some(value) = self.read_field(offset * 8 + entry.offset, entry.size) {
142 node.kind = NodeType::Field { value }
143 }
144 }
145 Ok(root)
146 }
147
148 pub fn decode_without_cm(&self) -> Node {
150 let header = self.decode_header();
151
152 let mut root = Node::root();
153 let record_root = if let Some(custom_root) = self.get_root_path() {
154 root.create_hierarchy(&custom_root)
155 } else {
156 &mut root
157 };
158
159 record_root.merge(header);
160 root
161 }
162
163 fn decode_header(&self) -> Node {
164 let mut record = Node::record(self.header.record_type().unwrap_or("record"));
165 record.add(Node::from(&self.header));
166
167 let mut root = Node::root();
168 root.add(record);
169 root
170 }
171
172 fn get_root_path(&self) -> Option<String> {
173 if let Some(custom_root) = self.header.get_root_path() {
174 return Some(custom_root);
175 }
176
177 if let Some(parent_header) = &self.context.parent_header {
178 return parent_header.get_root_path();
179 }
180
181 None
182 }
183
184 #[cfg(feature = "collateral_manager")]
185 fn get_root_path_using_cm<T: CollateralTree>(
186 &self,
187 cm: &mut CollateralManager<T>,
188 ) -> Option<String> {
189 if let Some(custom_root) = self.header.get_root_path_using_cm(cm) {
190 return Some(custom_root);
191 }
192
193 if let Some(parent_header) = &self.context.parent_header
194 && let Some(parent_custom_root) = parent_header.get_root_path_using_cm(cm)
195 {
196 return Some(parent_custom_root);
197 }
198
199 self.get_root_path()
200 }
201
202 #[cfg(feature = "collateral_manager")]
205 pub fn decode_with_decode_def<T: CollateralTree>(
206 &self,
207 cm: &mut CollateralManager<T>,
208 decode_def: &str,
209 offset: usize,
210 ) -> Result<Node, Error> {
211 let paths = self.header.decode_definitions_paths(cm)?;
212
213 let mut root = Node::root();
214
215 for mut path in paths {
216 path.push(decode_def);
217 let Ok(layout) = cm.get_item_with_header(&self.header, path) else {
218 continue;
219 };
220 root.merge(self.decode_with_csv(layout, offset)?);
221 return Ok(root);
222 }
223
224 Err(Error::MissingDecodeDefinitions(self.header.version.clone()))
225 }
226
227 #[cfg(feature = "collateral_manager")]
230 pub fn decode<T: CollateralTree>(&self, cm: &mut CollateralManager<T>) -> Node {
231 let is_core = ((self.header.version.record_type == record_types::PCORE)
232 || (self.header.version.record_type == record_types::ECORE))
233 && !self.header.version.into_errata().type0_legacy_server_box;
234
235 let record = if is_core {
236 self.decode_as_core_record(cm)
237 } else {
238 self.decode_with_decode_def(cm, "layout.csv", 0)
239 };
240
241 let record_node = match record {
242 Ok(node) => node,
243 Err(err) => {
244 log::warn!("Cannot decode record: {err}. Only the header fields will be decoded.");
245 self.decode_header()
246 }
247 };
248
249 let mut root = Node::root();
250 let record_root = if let Some(custom_root) = self.get_root_path_using_cm(cm) {
251 root.create_hierarchy(&custom_root)
252 } else {
253 &mut root
254 };
255
256 record_root.merge(record_node);
257 root
258 }
259}