consortium_builder/pipeline/
dts.rs1use consortium_cfg::bridge::LoweredIr;
28use consortium_cfg_common::config::DtsMode;
29
30pub use consortium_cfg::components::dts_render::{RenderedDts, render_linux_dts};
31
32pub const UIO_KCONFIG_NAME: &str = "uio-enable.cfg";
34
35pub fn render_uio_kconfig(ir: &LoweredIr, mode: DtsMode, configfs: bool) -> Option<String> {
52 if ir.uio.is_empty() {
53 return None;
54 }
55
56 let mut out = String::from(
57 "CONFIG_UIO=y\n\
58 CONFIG_UIO_PDRV_GENIRQ=y\n\
59 CONFIG_UIO_DMEM_GENIRQ=y\n",
60 );
61 if mode.is_overlay() {
62 out.push_str("CONFIG_OF_OVERLAY=y\n");
63 if configfs {
64 out.push_str("CONFIG_OF_CONFIGFS=y\n");
65 }
66 }
67 Some(out)
68}
69
70pub fn kernel_bbappend_name(kernel_recipe: &str) -> String {
74 format!("{kernel_recipe}_%.bbappend")
75}
76
77pub fn render_kernel_bbappend(fragments: &[String]) -> Option<String> {
85 if fragments.is_empty() {
86 return None;
87 }
88
89 let src_uri = fragments
90 .iter()
91 .map(|name| format!(" file://{name} \\"))
92 .collect::<Vec<_>>()
93 .join("\n");
94 let merged = fragments
95 .iter()
96 .map(|name| format!(" ${{WORKDIR}}/{name} \\"))
97 .collect::<Vec<_>>()
98 .join("\n");
99
100 Some(format!(
101 "# Auto-generated by Consortium — do not edit by hand.\n\
102 #\n\
103 # Merges the UIO kernel config fragment(s) into the kernel build. Drop this\n\
104 # file and the sibling `.cfg`(s) into your BSP's kernel recipe directory\n\
105 # (e.g. recipes-kernel/linux/).\n\
106 \n\
107 FILESEXTRAPATHS:prepend := \"${{THISDIR}}:\"\n\
108 \n\
109 SRC_URI += \"\\\n{src_uri}\n\"\n\
110 \n\
111 KERNEL_CONFIG_FRAGMENTS:append = \"\\\n{merged}\n\"\n"
112 ))
113}
114
115const UIO_BOOTARG: &str = "uio_pdrv_genirq.of_id=generic-uio";
118
119pub fn render_boot(chip: &str, ir: &LoweredIr) -> Vec<(String, String)> {
135 if ir.uio.is_empty() {
136 return Vec::new();
137 }
138
139 if is_stm32(chip) {
140 vec![
141 (
142 "extlinux-uio.cfg".to_string(),
143 format!(
144 "# Auto-generated by Consortium — append the UIO kernel arg to the\n\
145 # OpenSTLinux extlinux command line. Add to a u-boot bbappend or a\n\
146 # machine/distro .conf so `extlinux.conf` is regenerated with it.\n\
147 UBOOT_EXTLINUX_KERNEL_ARGS:append = \" {UIO_BOOTARG}\"\n"
148 ),
149 ),
150 (
151 "README".to_string(),
152 format!(
153 "Consortium boot-args (STM32 / OpenSTLinux)\n\
154 =========================================\n\n\
155 The `generic-uio` driver only binds the Consortium UIO nodes when the\n\
156 kernel command line carries:\n\n \
157 {UIO_BOOTARG}\n\n\
158 OpenSTLinux boots via extlinux, so this is a build-time change:\n\n\
159 1. Copy `extlinux-uio.cfg` into your u-boot recipe (or a machine/distro\n\
160 `.conf`) so its `UBOOT_EXTLINUX_KERNEL_ARGS:append` takes effect.\n\
161 2. Rebuild; the generated `/boot/mmcN_extlinux/extlinux.conf` `APPEND`\n\
162 line now ends with the UIO arg.\n\n\
163 No manual `setenv` at the U-Boot prompt is required.\n"
164 ),
165 ),
166 ]
167 } else {
168 vec![
169 (
170 "boot.cmd".to_string(),
171 format!(
172 "# Auto-generated by Consortium — U-Boot boot script source.\n\
173 # Appends the UIO kernel arg to the board's bootargs, then boots as usual.\n\
174 # Compile and stage it (no U-Boot rebuild needed):\n\
175 # mkimage -A arm64 -O linux -T script -C none -d boot.cmd boot.scr\n\
176 # then add `boot.scr` to IMAGE_BOOT_FILES so wic writes it to the FAT\n\
177 # boot partition; the i.MX distro bootcmd loads and runs it.\n\
178 setenv bootargs \"${{bootargs}} {UIO_BOOTARG}\"\n\
179 run distro_bootcmd\n"
180 ),
181 ),
182 (
183 "README".to_string(),
184 format!(
185 "Consortium boot-args (NXP i.MX)\n\
186 ===============================\n\n\
187 The `generic-uio` driver only binds the Consortium UIO nodes when the\n\
188 kernel command line carries:\n\n \
189 {UIO_BOOTARG}\n\n\
190 i.MX U-Boot uses a scripted boot (no extlinux.conf), so ship a boot\n\
191 script rather than editing a `.conf`:\n\n\
192 1. Compile the script:\n\
193 mkimage -A arm64 -O linux -T script -C none -d boot.cmd boot.scr\n\
194 2. Stage `boot.scr` on the FAT boot partition, e.g. in your image recipe:\n\
195 IMAGE_BOOT_FILES += \"boot.scr\"\n\
196 3. Boot; U-Boot's distro bootcmd loads `boot.scr`, which appends the UIO\n\
197 arg to `bootargs` before booting.\n\n\
198 Alternative (baked into U-Boot): append the arg to `mmcargs` /\n\
199 `CONFIG_EXTRA_ENV_SETTINGS` via a `u-boot_%.bbappend`.\n"
200 ),
201 ),
202 ]
203 }
204}
205
206fn is_stm32(chip: &str) -> bool {
210 chip.to_ascii_lowercase().starts_with("stm32")
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 fn ir_with_uio() -> LoweredIr {
218 use consortium_cfg::components::uio::{UioBacking, UioDevice};
219 LoweredIr::empty_with_uio(vec![(
220 "ipc-shm".to_string(),
221 UioDevice {
222 mmap: vec![(0x2040_0000, 0x8000)],
223 irq: None,
224 backing: UioBacking::ReservedMemory,
225 interrupt_parent: None,
226 clock: None,
227 },
228 )])
229 }
230
231 #[test]
232 fn kconfig_merge_mode_uses_genirq_symbols_without_overlay() {
233 let cfg = render_uio_kconfig(&ir_with_uio(), DtsMode::Merge, false).unwrap();
234 assert!(cfg.contains("CONFIG_UIO=y"));
235 assert!(cfg.contains("CONFIG_UIO_PDRV_GENIRQ=y"));
236 assert!(cfg.contains("CONFIG_UIO_DMEM_GENIRQ=y"));
237 assert!(!cfg.contains("GENERIC"));
239 assert!(!cfg.contains("CONFIG_OF_OVERLAY"));
240 assert!(!cfg.contains("CONFIG_OF_CONFIGFS"));
241 }
242
243 #[test]
244 fn kconfig_overlay_mode_enables_of_overlay_and_optional_configfs() {
245 let no_cfgfs = render_uio_kconfig(&ir_with_uio(), DtsMode::Overlay, false).unwrap();
246 assert!(no_cfgfs.contains("CONFIG_OF_OVERLAY=y"));
247 assert!(!no_cfgfs.contains("CONFIG_OF_CONFIGFS"));
248
249 let with_cfgfs = render_uio_kconfig(&ir_with_uio(), DtsMode::Overlay, true).unwrap();
250 assert!(with_cfgfs.contains("CONFIG_OF_CONFIGFS=y"));
251 }
252
253 #[test]
254 fn kconfig_none_without_uio() {
255 let empty = LoweredIr::empty_with_uio(vec![]);
256 assert!(render_uio_kconfig(&empty, DtsMode::Merge, false).is_none());
257 }
258
259 #[test]
260 fn boot_stm32_emits_extlinux_fragment() {
261 let files = render_boot("stm32mp257fak3", &ir_with_uio());
262 let names: Vec<&str> = files.iter().map(|(n, _)| n.as_str()).collect();
263 assert!(names.contains(&"extlinux-uio.cfg"));
264 assert!(names.contains(&"README"));
265 let cfg = &files
266 .iter()
267 .find(|(n, _)| n == "extlinux-uio.cfg")
268 .unwrap()
269 .1;
270 assert!(cfg.contains("UBOOT_EXTLINUX_KERNEL_ARGS:append"));
271 assert!(cfg.contains(UIO_BOOTARG));
272 }
273
274 #[test]
275 fn boot_nxp_emits_boot_cmd() {
276 let files = render_boot("imx95", &ir_with_uio());
277 let names: Vec<&str> = files.iter().map(|(n, _)| n.as_str()).collect();
278 assert!(names.contains(&"boot.cmd"));
279 let cmd = &files.iter().find(|(n, _)| n == "boot.cmd").unwrap().1;
280 assert!(cmd.contains("setenv bootargs"));
281 assert!(cmd.contains(UIO_BOOTARG));
282 assert!(cmd.contains("mkimage"));
283 }
284
285 #[test]
286 fn boot_none_without_uio() {
287 assert!(render_boot("imx95", &LoweredIr::empty_with_uio(vec![])).is_empty());
288 }
289
290 #[test]
291 fn bbappend_name_wildcards_version() {
292 assert_eq!(
293 kernel_bbappend_name("linux-stm32mp"),
294 "linux-stm32mp_%.bbappend"
295 );
296 }
297
298 #[test]
299 fn bbappend_merges_fragments_at_build_time() {
300 let bb = render_kernel_bbappend(&["uio-enable.cfg".to_string()])
301 .expect("fragment present yields a bbappend");
302 assert!(bb.contains("FILESEXTRAPATHS:prepend := \"${THISDIR}:\""));
303 assert!(bb.contains("file://uio-enable.cfg"));
304 assert!(bb.contains("KERNEL_CONFIG_FRAGMENTS:append"));
305 assert!(bb.contains("${WORKDIR}/uio-enable.cfg"));
306 assert!(!bb.contains("do_install"));
308 assert!(!bb.contains("${D}"));
309 }
310
311 #[test]
312 fn bbappend_none_without_fragments() {
313 assert!(render_kernel_bbappend(&[]).is_none());
314 }
315}