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}