Skip to main content

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}