intel_crashlog/collateral.rs
1// Copyright (C) 2025 Intel Corporation
2// SPDX-License-Identifier: MIT
3
4//! Management of the product-specific collateral required to decode the Crash Log records.
5//!
6//! The term 'collateral tree' used in this module defines the data structure that stores the
7//! project-specific collateral files.
8//! Each collateral file is indexed based on the these two keys:
9//! - [`PVSS`]: uniquely identifies a product.
10//! - [`ItemPath`]: defines the location of the item within a given [`PVSS`]
11
12#[cfg(feature = "embedded_collateral_tree")]
13mod embedded;
14#[cfg(feature = "fs_collateral_tree")]
15mod fs;
16mod path;
17mod pvss;
18mod target_info;
19
20use crate::Error;
21use crate::header::Header;
22use crate::utils::Map;
23#[cfg(not(feature = "std"))]
24use alloc::{string::ToString, vec::Vec};
25
26#[cfg(feature = "embedded_collateral_tree")]
27pub use embedded::EmbeddedTree;
28#[cfg(feature = "fs_collateral_tree")]
29pub use fs::FileSystemTree;
30pub use path::ItemPath;
31pub use pvss::PVSS;
32pub use target_info::TargetInfo;
33
34#[derive(Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
35struct ItemIndex {
36 pvss: PVSS,
37 path: ItemPath,
38}
39
40/// A trait representing a data structure that provides a direct access to the product-specific
41/// collateral files.
42pub trait CollateralTree {
43 /// Returns the content of an item in the collateral tree.
44 fn get(&self, pvss: &PVSS, path: &ItemPath) -> Result<Vec<u8>, Error>;
45 /// Returns a list of all the `PVSS` that have an item defined at the given `path`.
46 fn search(&self, path: &ItemPath) -> Result<Vec<PVSS>, Error>;
47}
48
49/// Manages the product-specific collateral files required to decode the Crash Log records.
50#[derive(Default)]
51pub struct CollateralManager<T: CollateralTree> {
52 tree: T,
53 items: Map<ItemIndex, Vec<u8>>,
54 /// Maps the Crash Log product IDs into a data structure that stores various information
55 /// about the associated product.
56 pub target_info: Map<u32, TargetInfo>,
57}
58
59impl<T: CollateralTree> CollateralManager<T> {
60 /// Creates a collateral manager for a given collateral `tree`.
61 pub fn new(tree: T) -> Result<Self, Error> {
62 let mut cm = Self {
63 tree,
64 items: Map::default(),
65 target_info: Map::default(),
66 };
67 cm.update_target_info()?;
68 Ok(cm)
69 }
70
71 /// Returns the content of an item from the collateral tree using the [`PVSS`] of the target.
72 ///
73 /// ```
74 /// use intel_crashlog::prelude::*;
75 /// use intel_crashlog::collateral::PVSS;
76 ///
77 /// let mut cm = CollateralManager::embedded_tree().unwrap();
78 /// let pvss = PVSS {
79 /// product: "XYZ".into(),
80 /// security: "all".into(),
81 /// ..PVSS::default()
82 /// };
83 /// assert!(cm.get_item_with_pvss(pvss, "target_info.json").is_ok());
84 /// ```
85 pub fn get_item_with_pvss(
86 &mut self,
87 pvss: PVSS,
88 path: impl Into<ItemPath>,
89 ) -> Result<&[u8], Error> {
90 let index = ItemIndex {
91 pvss,
92 path: path.into(),
93 };
94
95 if !self.items.contains_key(&index) {
96 let item = self.tree.get(&index.pvss, &index.path)?;
97 self.items.insert(index.clone(), item);
98 }
99
100 self.items
101 .get(&index)
102 .map(|i| i.as_ref())
103 .ok_or(Error::InternalError)
104 }
105
106 /// Similar to [`CollateralManager::get_item_with_pvss`] but ignores the `security` specified
107 /// in the [`PVSS`] and returns the item with the highest security level.
108 ///
109 /// ```
110 /// use intel_crashlog::prelude::*;
111 /// use intel_crashlog::collateral::PVSS;
112 ///
113 /// let mut cm = CollateralManager::embedded_tree().unwrap();
114 /// let pvss = PVSS {
115 /// product: "XYZ".into(),
116 /// ..PVSS::default()
117 /// };
118 /// assert!(cm.get_item_with_pvs(pvss, "target_info.json").is_ok());
119 /// ```
120 pub fn get_item_with_pvs(
121 &mut self,
122 pvss: PVSS,
123 path: impl Into<ItemPath>,
124 ) -> Result<&[u8], Error> {
125 let index = ItemIndex {
126 pvss,
127 path: path.into(),
128 };
129
130 if !self.items.contains_key(&index) {
131 self.fetch_item(&index)?;
132 }
133
134 self.items
135 .get(&index)
136 .map(|i| i.as_ref())
137 .ok_or(Error::InternalError)
138 }
139
140 fn fetch_item(&mut self, index: &ItemIndex) -> Result<(), Error> {
141 let security_levels = ["red", "white", "green", "all"];
142
143 for security in security_levels {
144 let pvss = PVSS {
145 security: security.to_string(),
146 ..index.pvss.clone()
147 };
148 match self.tree.get(&pvss, &index.path) {
149 Ok(item) => {
150 self.items.insert(index.clone(), item);
151 return Ok(());
152 }
153 Err(Error::MissingCollateral(_, item)) => {
154 log::debug!("No {security} {item} defined")
155 }
156 Err(err) => log::warn!("Unexpected error while fetching item: {err}"),
157 }
158 }
159
160 Err(Error::MissingCollateral(
161 index.pvss.clone(),
162 index.path.clone(),
163 ))
164 }
165
166 /// Returns the content of an item from the collateral tree using the Crash Log header.
167 ///
168 /// ```
169 /// use intel_crashlog::prelude::*;
170 ///
171 /// let mut cm = CollateralManager::embedded_tree().unwrap();
172 /// let data = vec![0x08, 0xa1, 0x07, 0x3e, 0x2, 0x0, 0x0, 0x0];
173 /// let header = Header::from_slice(&data).unwrap().unwrap();
174 /// assert!(cm.get_item_with_header(&header, "target_info.json").is_ok());
175 /// ```
176 pub fn get_item_with_header(
177 &mut self,
178 header: &Header,
179 path: impl Into<ItemPath>,
180 ) -> Result<&[u8], Error> {
181 self.get_item_with_pvs(header.pvss(self)?, path)
182 }
183}