Skip to main content

consortium_data/
database.rs

1// Copyright 2026 Ethan Wu
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// SPDX-License-Identifier: Apache-2.0
16
17//! Chip-family databases, embedded at compile time and resolved from a chip
18//! part number declared in `[profile] chip`.
19
20pub use consortium_cfg_common::database::*;
21
22use crate::error::DataError;
23use crate::family::{matches, normalize};
24
25/// A chip-family database bundled into the binary, paired with the part-number
26/// prefix that selects it.
27struct Embedded {
28    /// Lowercase part-number prefix that selects this family (e.g. `stm32mp25`).
29    prefix: &'static str,
30    /// The raw YAML definition.
31    yaml: &'static str,
32}
33
34/// All bundled chip-family databases.
35///
36/// Ordered most-specific prefix first so that, for example, `stm32mp25` is
37/// preferred over a hypothetical shorter `stm32mp` prefix.
38const DATABASES: &[Embedded] = &[
39    Embedded {
40        prefix: "stm32mp25",
41        yaml: include_str!("../database/memory/stm32mp25.yaml"),
42    },
43    Embedded {
44        prefix: "stm32mp23",
45        yaml: include_str!("../database/memory/stm32mp23.yaml"),
46    },
47    Embedded {
48        prefix: "stm32mp21",
49        yaml: include_str!("../database/memory/stm32mp21.yaml"),
50    },
51    Embedded {
52        prefix: "mimx952",
53        yaml: include_str!("../database/memory/mimx952.yaml"),
54    },
55    Embedded {
56        prefix: "mimx95",
57        yaml: include_str!("../database/memory/mimx95.yaml"),
58    },
59    Embedded {
60        prefix: "mimx93",
61        yaml: include_str!("../database/memory/mimx93.yaml"),
62    },
63];
64
65/// Resolves and parses the chip-family database for a chip part number.
66///
67/// The lookup is a case-insensitive family match: `stm32mp257fak3` resolves to
68/// the `stm32mp25` family. A part number of another die that only shares a
69/// prefix, such as an i.MX 952 `mimx9529…`, matches nothing (see
70/// `crate::family`). Common i.MX aliases (`imx95`, `imx93`) are accepted
71/// in addition to the canonical `mimx*` names.
72///
73/// # Errors
74///
75/// Returns [`DataError::UnknownChip`] if no family matches, or
76/// [`DataError::DatabaseParse`] if the bundled YAML fails to deserialize.
77pub fn load(chip: &str) -> Result<ChipDatabase, DataError> {
78    let needle = normalize(chip);
79    let entry = DATABASES
80        .iter()
81        .find(|entry| matches(&needle, entry.prefix))
82        .ok_or_else(|| DataError::UnknownChip(chip.to_string()))?;
83
84    serde_yaml::from_str(entry.yaml)
85        .map_err(|err| DataError::DatabaseParse(chip.to_string(), err.to_string()))
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn resolves_stm32mp25_from_full_part_number() {
94        let database = load("stm32mp257fak3").expect("stm32mp25 family should resolve");
95        assert_eq!(database.name, "STM32MP25");
96    }
97
98    #[test]
99    fn accepts_imx_alias() {
100        assert!(load("imx95").is_ok());
101    }
102
103    /// Every bundled database parses, and its `name` identifies the family its
104    /// own lookup prefix selects.
105    ///
106    /// This is the invariant that a copy-pasted header breaks: `mimx95.yaml`
107    /// once carried `name: MIMX93` (and the i.MX 93 TRM link) while holding
108    /// genuine i.MX 95 data, so every consumer that reported the chip family
109    /// reported the wrong one.
110    #[test]
111    fn every_bundled_database_names_its_own_prefix() {
112        for entry in DATABASES {
113            let database: ChipDatabase = serde_yaml::from_str(entry.yaml)
114                .unwrap_or_else(|error| panic!("`{}` should parse: {error}", entry.prefix));
115            assert_eq!(
116                database.name.to_ascii_lowercase(),
117                entry.prefix,
118                "`{}` database is named `{}`",
119                entry.prefix,
120                database.name
121            );
122        }
123    }
124
125    #[test]
126    fn resolves_imx_families_from_full_part_numbers() {
127        assert_eq!(load("mimx9596xvt").unwrap().name, "MIMX95");
128        assert_eq!(load("mimx9352").unwrap().name, "MIMX93");
129        assert_eq!(load("mimx9529avzxn").unwrap().name, "MIMX952");
130        assert_eq!(load("imx952").unwrap().name, "MIMX952");
131        assert_eq!(load("imx93").unwrap().name, "MIMX93");
132    }
133
134    /// i.MX 937 is a separate die whose part numbers merely start with
135    /// `mimx93`; resolving it to that family would hand it the wrong memory map.
136    #[test]
137    fn rejects_other_dies_sharing_an_imx_prefix() {
138        for chip in ["mimx9375", "mimx9371xxvtx", "imx937"] {
139            assert!(
140                matches!(load(chip), Err(DataError::UnknownChip(_))),
141                "{chip} should not resolve"
142            );
143        }
144    }
145
146    #[test]
147    fn rejects_unknown_chip() {
148        assert!(matches!(load("acme9000"), Err(DataError::UnknownChip(_))));
149    }
150}