tan-cli 0.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. tan/__init__.py +0 -0
  2. tan/__main__.py +120 -0
  3. tan/cli.py +939 -0
  4. tan/command_table.py +265 -0
  5. tan/commands/__init__.py +8 -0
  6. tan/commands/bootstrap_cmd.py +4856 -0
  7. tan/commands/bootstrap_patches.py +164 -0
  8. tan/commands/bootstrap_toolchain_fetch.py +155 -0
  9. tan/commands/bootstrap_toolchain_pin.py +36 -0
  10. tan/commands/build/__init__.py +1 -0
  11. tan/commands/build/configure_inputs.py +277 -0
  12. tan/commands/build/execute.py +1852 -0
  13. tan/commands/build/host_python.py +59 -0
  14. tan/commands/build/link_stale.py +78 -0
  15. tan/commands/build/manifest.py +428 -0
  16. tan/commands/build/materialise.py +81 -0
  17. tan/commands/build/token_substitution.py +381 -0
  18. tan/commands/build/toolchain.py +482 -0
  19. tan/commands/build/user_defines_wipe.py +115 -0
  20. tan/commands/build/workspace_patches.py +65 -0
  21. tan/commands/build_cmd.py +2236 -0
  22. tan/commands/build_output.py +331 -0
  23. tan/commands/clean_cmd.py +1183 -0
  24. tan/commands/completion_cmd.py +786 -0
  25. tan/commands/debug_config_cmd.py +2425 -0
  26. tan/commands/diff_cmd.py +940 -0
  27. tan/commands/doctor_cmd.py +4767 -0
  28. tan/commands/examples_cmd.py +644 -0
  29. tan/commands/explain_cmd.py +1319 -0
  30. tan/commands/faultdecode_cmd.py +931 -0
  31. tan/commands/flash_atoc_guard.py +240 -0
  32. tan/commands/flash_cmd.py +5731 -0
  33. tan/commands/flash_mram_guard.py +150 -0
  34. tan/commands/flash_ram.py +578 -0
  35. tan/commands/flash_raw.py +583 -0
  36. tan/commands/generate_cmd.py +1456 -0
  37. tan/commands/image_cmd.py +753 -0
  38. tan/commands/init_cmd.py +1740 -0
  39. tan/commands/inspect_cmd.py +377 -0
  40. tan/commands/kconfig_cmd.py +734 -0
  41. tan/commands/model_cmd.py +1514 -0
  42. tan/commands/model_device_cmd.py +351 -0
  43. tan/commands/model_device_live.py +234 -0
  44. tan/commands/model_host_cmd.py +430 -0
  45. tan/commands/model_zoo_cmd.py +363 -0
  46. tan/commands/monitor_actions.py +125 -0
  47. tan/commands/monitor_cmd.py +626 -0
  48. tan/commands/monitor_session.py +505 -0
  49. tan/commands/new_som_cmd.py +1522 -0
  50. tan/commands/pinmux_cmd.py +568 -0
  51. tan/commands/presets_cmd.py +1123 -0
  52. tan/commands/probe_cmd.py +421 -0
  53. tan/commands/reset_cmd.py +354 -0
  54. tan/commands/reset_confirm.py +168 -0
  55. tan/commands/run_cmd.py +637 -0
  56. tan/commands/scaffold_cmd.py +490 -0
  57. tan/commands/sdk_cmd.py +1723 -0
  58. tan/commands/size_cmd.py +1020 -0
  59. tan/commands/support_bundle_cmd.py +1073 -0
  60. tan/commands/trace_cmd.py +341 -0
  61. tan/commands/validate_cmd.py +2009 -0
  62. tan/commands/west_forward_cmd.py +736 -0
  63. tan/commands/workspace_patch_check.py +296 -0
  64. tan/core/__init__.py +1 -0
  65. tan/core/alp_conf_pregen.py +100 -0
  66. tan/core/artifact_provenance.py +240 -0
  67. tan/core/atoc_guard.py +276 -0
  68. tan/core/atoc_guard_messages.py +232 -0
  69. tan/core/atoc_replacement.py +118 -0
  70. tan/core/atomic_write.py +178 -0
  71. tan/core/board_context.py +351 -0
  72. tan/core/board_diagnostic.py +111 -0
  73. tan/core/board_files.py +221 -0
  74. tan/core/board_validator.py +542 -0
  75. tan/core/board_validator_compat.py +196 -0
  76. tan/core/board_validator_run.py +129 -0
  77. tan/core/board_validator_schema.py +186 -0
  78. tan/core/board_validator_skew.py +46 -0
  79. tan/core/board_yaml_edit.py +295 -0
  80. tan/core/board_yaml_pos.py +118 -0
  81. tan/core/bootstrap.py +2572 -0
  82. tan/core/build_plan.py +625 -0
  83. tan/core/consent.py +104 -0
  84. tan/core/console_filter.py +245 -0
  85. tan/core/debug_launch.py +1803 -0
  86. tan/core/devicetree_lint.py +168 -0
  87. tan/core/dir_removal.py +229 -0
  88. tan/core/doctor_git.py +172 -0
  89. tan/core/doctor_libraries.py +253 -0
  90. tan/core/doctor_render.py +185 -0
  91. tan/core/doctor_scope.py +66 -0
  92. tan/core/doctor_setools.py +237 -0
  93. tan/core/doctor_stale.py +180 -0
  94. tan/core/document_guards.py +723 -0
  95. tan/core/dp_id.py +97 -0
  96. tan/core/elf_load.py +63 -0
  97. tan/core/error_catalog.py +171 -0
  98. tan/core/example_catalog.py +499 -0
  99. tan/core/example_facets.py +151 -0
  100. tan/core/faultdecode.py +521 -0
  101. tan/core/flash_plan.py +2501 -0
  102. tan/core/flow_d_report.py +381 -0
  103. tan/core/flow_d_uptime.py +164 -0
  104. tan/core/fs_confine.py +130 -0
  105. tan/core/global_flags.py +230 -0
  106. tan/core/host_python.py +210 -0
  107. tan/core/image_bundle.py +247 -0
  108. tan/core/inert.py +147 -0
  109. tan/core/jlink_binary.py +128 -0
  110. tan/core/jlink_probe.py +481 -0
  111. tan/core/jsonc_splice.py +404 -0
  112. tan/core/launch_provenance.py +257 -0
  113. tan/core/link_refusal.py +76 -0
  114. tan/core/metadata_schema.py +376 -0
  115. tan/core/model_check.py +227 -0
  116. tan/core/model_device.py +449 -0
  117. tan/core/model_doctor.py +198 -0
  118. tan/core/model_host.py +48 -0
  119. tan/core/model_list.py +112 -0
  120. tan/core/model_zoo.py +353 -0
  121. tan/core/module_template.py +340 -0
  122. tan/core/mram_link.py +87 -0
  123. tan/core/new_som_ospi.py +94 -0
  124. tan/core/os_class.py +159 -0
  125. tan/core/patch_paths.py +87 -0
  126. tan/core/pending.py +40 -0
  127. tan/core/plain_zephyr_plan.py +138 -0
  128. tan/core/plan_exec.py +373 -0
  129. tan/core/plan_tokens.py +600 -0
  130. tan/core/probe.py +155 -0
  131. tan/core/probe_plan.py +237 -0
  132. tan/core/proxy.py +168 -0
  133. tan/core/publish.py +31 -0
  134. tan/core/python_floor.py +178 -0
  135. tan/core/ram_run.py +694 -0
  136. tan/core/ram_watch.py +231 -0
  137. tan/core/raw_write.py +160 -0
  138. tan/core/reset_plan.py +56 -0
  139. tan/core/run.py +161 -0
  140. tan/core/scaffold.py +2203 -0
  141. tan/core/scaffold_selftest_identity.py +225 -0
  142. tan/core/sdk_default_registry.py +565 -0
  143. tan/core/sdk_discovery.py +996 -0
  144. tan/core/sdk_removal.py +465 -0
  145. tan/core/serial_actions.py +175 -0
  146. tan/core/serial_capture.py +185 -0
  147. tan/core/serial_url.py +45 -0
  148. tan/core/setools.py +608 -0
  149. tan/core/setools_scratch.py +243 -0
  150. tan/core/shapes.py +194 -0
  151. tan/core/size.py +610 -0
  152. tan/core/som_buildability.py +204 -0
  153. tan/core/som_schema_version.py +62 -0
  154. tan/core/subprocess_env.py +201 -0
  155. tan/core/sysbuild_image.py +94 -0
  156. tan/core/system_manifest.py +827 -0
  157. tan/core/text_layout.py +73 -0
  158. tan/core/timestamp.py +182 -0
  159. tan/core/tool_lookup.py +383 -0
  160. tan/core/toolchain_pin.py +117 -0
  161. tan/core/toolchain_provision.py +1224 -0
  162. tan/core/trusted_path.py +134 -0
  163. tan/core/uboot_breakin.py +154 -0
  164. tan/core/uri_reference.py +415 -0
  165. tan/core/user_defines.py +152 -0
  166. tan/core/venv.py +332 -0
  167. tan/core/west_patches.py +143 -0
  168. tan/core/west_workspace_refusal.py +159 -0
  169. tan/core/zephyr_env.py +124 -0
  170. tan/env.py +185 -0
  171. tan/envelope.py +647 -0
  172. tan/exit_codes.py +12 -0
  173. tan/lazy_typer.py +185 -0
  174. tan/model/__init__.py +2 -0
  175. tan/model/_gen_fixture.py +132 -0
  176. tan/model/adapters/__init__.py +116 -0
  177. tan/model/adapters/cpu.py +26 -0
  178. tan/model/adapters/deepx.py +94 -0
  179. tan/model/adapters/drpai.py +242 -0
  180. tan/model/adapters/ethos_u.py +951 -0
  181. tan/model/adapters/executorch.py +55 -0
  182. tan/model/analyze.py +694 -0
  183. tan/model/build.py +304 -0
  184. tan/model/check.py +682 -0
  185. tan/model/manifest.py +625 -0
  186. tan/model/measure.py +216 -0
  187. tan/model/npu_ops_validate.py +130 -0
  188. tan/model/package.py +125 -0
  189. tan/model/perf.py +635 -0
  190. tan/model/perf_apply.py +721 -0
  191. tan/model/prep.py +193 -0
  192. tan/model/sram_fit.py +502 -0
  193. tan/model/targets.py +374 -0
  194. tan/model/tensorio.py +287 -0
  195. tan/net.py +73 -0
  196. tan/output_format.py +213 -0
  197. tan/planner/__init__.py +219 -0
  198. tan/planner/aperture.py +202 -0
  199. tan/planner/buildplan.py +1092 -0
  200. tan/planner/camera_owner.py +192 -0
  201. tan/planner/cameras.py +132 -0
  202. tan/planner/carveout.py +755 -0
  203. tan/planner/cli.py +274 -0
  204. tan/planner/headers.py +315 -0
  205. tan/planner/kconfig.py +2435 -0
  206. tan/planner/kconfig_symbols.py +473 -0
  207. tan/planner/libraries.py +677 -0
  208. tan/planner/link_target.py +237 -0
  209. tan/planner/linux_ownership.py +216 -0
  210. tan/planner/loader.py +1525 -0
  211. tan/planner/manifest.py +116 -0
  212. tan/planner/memory.py +161 -0
  213. tan/planner/memregion.py +56 -0
  214. tan/planner/models.py +533 -0
  215. tan/planner/orchestrator.py +874 -0
  216. tan/planner/ownership.py +203 -0
  217. tan/planner/partition.py +1160 -0
  218. tan/planner/paths.py +32 -0
  219. tan/planner/plain_slice.py +139 -0
  220. tan/planner/project_emit/__init__.py +60 -0
  221. tan/planner/project_emit/bom_netlist.py +375 -0
  222. tan/planner/project_emit/dts.py +580 -0
  223. tan/planner/project_emit/hw_info.py +221 -0
  224. tan/planner/project_emit/native_sim.py +136 -0
  225. tan/planner/project_emit/west_libs.py +213 -0
  226. tan/planner/project_loader.py +264 -0
  227. tan/planner/sdk_capability.py +203 -0
  228. tan/planner/sdk_compat.py +395 -0
  229. tan/planner/secure.py +350 -0
  230. tan/planner/slugs.py +394 -0
  231. tan/planner/som_metadata.py +325 -0
  232. tan/planner/strict_loaders.py +131 -0
  233. tan/planner/template.py +1037 -0
  234. tan/planner/template_pins.py +535 -0
  235. tan/planner/template_rewrite.py +714 -0
  236. tan/planner/topology.py +208 -0
  237. tan/planner/validate.py +365 -0
  238. tan/planner/whole_device_alias.py +67 -0
  239. tan/planner/zephyr_board.py +3510 -0
  240. tan/planner_cli.py +97 -0
  241. tan/planner_emit.py +617 -0
  242. tan/planner_root.py +119 -0
  243. tan/soc_ref.py +58 -0
  244. tan/templates/__init__.py +34 -0
  245. tan/templates/vendored/MANIFEST.md +1862 -0
  246. tan/templates/vendored/diagnostics/E1M-AEN801/CMakeLists.txt +8 -0
  247. tan/templates/vendored/diagnostics/E1M-AEN801/README.md +112 -0
  248. tan/templates/vendored/diagnostics/E1M-AEN801/board.yaml +38 -0
  249. tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.conf +11 -0
  250. tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.overlay +29 -0
  251. tan/templates/vendored/diagnostics/E1M-AEN801/prj.conf +5 -0
  252. tan/templates/vendored/diagnostics/E1M-AEN801/src/main.c +266 -0
  253. tan/templates/vendored/diagnostics/E1M-AEN801/testcase.yaml +53 -0
  254. tan/templates/vendored/diagnostics/E1M-V2N101/CMakeLists.txt +8 -0
  255. tan/templates/vendored/diagnostics/E1M-V2N101/README.md +112 -0
  256. tan/templates/vendored/diagnostics/E1M-V2N101/board.yaml +38 -0
  257. tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.conf +11 -0
  258. tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.overlay +29 -0
  259. tan/templates/vendored/diagnostics/E1M-V2N101/prj.conf +5 -0
  260. tan/templates/vendored/diagnostics/E1M-V2N101/src/main.c +266 -0
  261. tan/templates/vendored/diagnostics/E1M-V2N101/testcase.yaml +53 -0
  262. tan/templates/vendored/edge-ai/E1M-AEN801/CMakeLists.txt +10 -0
  263. tan/templates/vendored/edge-ai/E1M-AEN801/README.md +75 -0
  264. tan/templates/vendored/edge-ai/E1M-AEN801/board.yaml +38 -0
  265. tan/templates/vendored/edge-ai/E1M-AEN801/prj.conf +8 -0
  266. tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.c +371 -0
  267. tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.h +178 -0
  268. tan/templates/vendored/edge-ai/E1M-AEN801/src/main.c +373 -0
  269. tan/templates/vendored/edge-ai/E1M-AEN801/testcase.yaml +62 -0
  270. tan/templates/vendored/edge-ai/E1M-V2N101/CMakeLists.txt +10 -0
  271. tan/templates/vendored/edge-ai/E1M-V2N101/README.md +75 -0
  272. tan/templates/vendored/edge-ai/E1M-V2N101/board.yaml +37 -0
  273. tan/templates/vendored/edge-ai/E1M-V2N101/prj.conf +8 -0
  274. tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.c +371 -0
  275. tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.h +178 -0
  276. tan/templates/vendored/edge-ai/E1M-V2N101/src/main.c +373 -0
  277. tan/templates/vendored/edge-ai/E1M-V2N101/testcase.yaml +62 -0
  278. tan/templates/vendored/iot/E1M-AEN801/CMakeLists.txt +14 -0
  279. tan/templates/vendored/iot/E1M-AEN801/README.md +103 -0
  280. tan/templates/vendored/iot/E1M-AEN801/board.yaml +69 -0
  281. tan/templates/vendored/iot/E1M-AEN801/prj.conf +15 -0
  282. tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.c +196 -0
  283. tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.h +173 -0
  284. tan/templates/vendored/iot/E1M-AEN801/src/main.c +262 -0
  285. tan/templates/vendored/iot/E1M-AEN801/testcase.yaml +68 -0
  286. tan/templates/vendored/minimal/E1M-AEN801/CMakeLists.txt +8 -0
  287. tan/templates/vendored/minimal/E1M-AEN801/README.md +108 -0
  288. tan/templates/vendored/minimal/E1M-AEN801/board.yaml +32 -0
  289. tan/templates/vendored/minimal/E1M-AEN801/prj.conf +8 -0
  290. tan/templates/vendored/minimal/E1M-AEN801/src/main.c +111 -0
  291. tan/templates/vendored/minimal/E1M-AEN801/testcase.yaml +49 -0
  292. tan/templates/vendored/minimal/E1M-V2N101/CMakeLists.txt +8 -0
  293. tan/templates/vendored/minimal/E1M-V2N101/README.md +108 -0
  294. tan/templates/vendored/minimal/E1M-V2N101/board.yaml +32 -0
  295. tan/templates/vendored/minimal/E1M-V2N101/prj.conf +8 -0
  296. tan/templates/vendored/minimal/E1M-V2N101/src/main.c +111 -0
  297. tan/templates/vendored/minimal/E1M-V2N101/testcase.yaml +49 -0
  298. tan/templates/vendored/multicore-mailbox/E1M-AEN801/CMakeLists.txt +8 -0
  299. tan/templates/vendored/multicore-mailbox/E1M-AEN801/README.md +120 -0
  300. tan/templates/vendored/multicore-mailbox/E1M-AEN801/board.yaml +57 -0
  301. tan/templates/vendored/multicore-mailbox/E1M-AEN801/boards/native_sim_native_64.overlay +40 -0
  302. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/CMakeLists.txt +13 -0
  303. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/main.c +171 -0
  304. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/prj.conf +17 -0
  305. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/testcase.yaml +23 -0
  306. tan/templates/vendored/multicore-mailbox/E1M-AEN801/prj.conf +22 -0
  307. tan/templates/vendored/multicore-mailbox/E1M-AEN801/src/main.c +185 -0
  308. tan/templates/vendored/multicore-mailbox/E1M-AEN801/testcase.yaml +26 -0
  309. tan/templates/vendored/sensor/E1M-AEN801/CMakeLists.txt +8 -0
  310. tan/templates/vendored/sensor/E1M-AEN801/README.md +112 -0
  311. tan/templates/vendored/sensor/E1M-AEN801/board.yaml +57 -0
  312. tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.conf +10 -0
  313. tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.overlay +28 -0
  314. tan/templates/vendored/sensor/E1M-AEN801/prj.conf +5 -0
  315. tan/templates/vendored/sensor/E1M-AEN801/src/main.c +186 -0
  316. tan/templates/vendored/sensor/E1M-AEN801/testcase.yaml +52 -0
  317. tan/templates/vendored/sensor/E1M-V2N101/CMakeLists.txt +8 -0
  318. tan/templates/vendored/sensor/E1M-V2N101/README.md +112 -0
  319. tan/templates/vendored/sensor/E1M-V2N101/board.yaml +57 -0
  320. tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.conf +10 -0
  321. tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.overlay +28 -0
  322. tan/templates/vendored/sensor/E1M-V2N101/prj.conf +5 -0
  323. tan/templates/vendored/sensor/E1M-V2N101/src/main.c +186 -0
  324. tan/templates/vendored/sensor/E1M-V2N101/testcase.yaml +52 -0
  325. tan/version.py +102 -0
  326. tan_cli-0.7.0.dist-info/METADATA +125 -0
  327. tan_cli-0.7.0.dist-info/RECORD +330 -0
  328. tan_cli-0.7.0.dist-info/WHEEL +5 -0
  329. tan_cli-0.7.0.dist-info/entry_points.txt +2 -0
  330. tan_cli-0.7.0.dist-info/top_level.txt +1 -0
tan/planner/secure.py ADDED
@@ -0,0 +1,350 @@
1
+ #!/usr/bin/env python3
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Secure-boot config emitters -- sysbuild.conf + the TF-M child-image overlay.
4
+
5
+ `emit_sysbuild_conf` renders the sysbuild.conf overlay from board.yaml's `boot:`
6
+ block (the MCUboot wiring); `emit_tfm_sysbuild_conf` renders the TF-M sysbuild
7
+ child-image overlay from `security.psa:`. Pure string templating off the parsed
8
+ BoardProject -- the leaf-most emit seam (#285), a sink the build-plan emitter
9
+ imports later.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from pathlib import Path
15
+ from typing import Optional
16
+
17
+ from .models import BoardProject, OrchestratorError
18
+ from .paths import REPO
19
+
20
+ # SoM-preset `family:` (metadata/e1m_modules/*.yaml, e.g.
21
+ # "alif-ensemble") -> the short slug the curated
22
+ # zephyr/sysbuild/<slug>/sysbuild.conf directory uses. Mirrors the
23
+ # alif-ensemble / renesas-rzv2n family-token `startswith` match already
24
+ # duplicated in kconfig.py / validate.py. Only families that actually
25
+ # ship a curated file are listed -- renesas-rzv2n boots via
26
+ # U-Boot/Yocto, not sysbuild, so it has none today; adding one is a
27
+ # metadata-only change (drop the file under zephyr/sysbuild/<slug>/),
28
+ # no code change needed here.
29
+ _FAMILY_SYSBUILD_DIRS: dict[str, str] = {
30
+ "alif-ensemble": "aen",
31
+ }
32
+
33
+ # The per-family `boot.method:` default `board.schema.json` documents but
34
+ # nothing implemented (#562): "the SoM family supplies the default
35
+ # (AEN -> mcuboot, V2N/V2N-M1 -> none on the Zephyr slice since
36
+ # U-Boot owns boot on Linux)". `emit_sysbuild_conf` used to hard-default
37
+ # every family to `mcuboot`, so a Renesas project that omitted `method:`
38
+ # was pushed down the MCUboot path -- and, with `rsa3072` (a value
39
+ # `validate.py` PERMITS for renesas-rzv2n precisely because its boot
40
+ # chain is the U-Boot/FIT stack, not sysbuild), hard-raised and took the
41
+ # whole build-plan emit with it. Same `family:`-token `startswith` match
42
+ # as `_FAMILY_SYSBUILD_DIRS` above and `validate._boot_signing_supported_
43
+ # for_family`. `boot.method:` has no other consumer anywhere in the
44
+ # tree, so this emitter is the only site the default has to exist at.
45
+ _FAMILY_BOOT_METHOD_DEFAULTS: dict[str, str] = {
46
+ "alif-ensemble": "mcuboot",
47
+ "renesas-rzv2n": "none",
48
+ }
49
+
50
+ # What an UNRECOGNISED `family:` token defaults to. Deliberately the
51
+ # historical value, so an in-development preset whose family string is
52
+ # not in the table above emits exactly what it emitted before #562 --
53
+ # the fix is "recognised families get their documented default", not
54
+ # "the default silently flips for everyone".
55
+ _BOOT_METHOD_FALLBACK = "mcuboot"
56
+
57
+
58
+ def _default_boot_method(project: BoardProject) -> str:
59
+ """The `boot.method:` this project's SoM family implies."""
60
+ family = (project.som_preset.get("family") or "").lower()
61
+ return next((v for k, v in _FAMILY_BOOT_METHOD_DEFAULTS.items()
62
+ if family.startswith(k)), _BOOT_METHOD_FALLBACK)
63
+
64
+
65
+ def _boot_target_is_single_slot(project: BoardProject) -> bool:
66
+ """Whether this project resolves to a disjoint-slot0 boot shape on
67
+ ANY of its Zephyr M55 cores -- i.e. any MCUboot swap mode (scratch,
68
+ move, overwrite) has no slot1/scratch partition to swap into.
69
+
70
+ Reuses `zephyr_board._aen_role_slot0_map` -- the SAME resolver the
71
+ board-DT generator and `loader._resolve_slot0_load_address` already
72
+ call to answer this exact question -- instead of re-deriving the
73
+ answer from `memory_map:` region NAMES. The two disagree in general:
74
+ `som-preset-v2.schema.json` documents `memory_map:` as a build-policy
75
+ override "ONLY for non-stock partitioning", not something only a
76
+ disjoint-slot0 SoM sets. A `memory_map:` present for an unrelated
77
+ reason (an rpmsg carve-out, say) that declares no `<role>_slot0`
78
+ region makes the partition emitter fall through to the STOCK two-slot
79
+ layout -- a REAL `image-1` + `image-scratch` -- regardless of what its
80
+ own region names happen to be, so a check that scans those names for
81
+ "slot1"/"scratch" can answer True (single-slot) on a target that
82
+ generates both. `_aen_role_slot0_map` asks the real question: does
83
+ THIS role have its own `<role>_slot0` region (the only shape that
84
+ drops slot1/scratch, alp-sdk#1069 -- every AEN preset sets one: each
85
+ SoM's M55 cores share one physical App MRAM, so slot0 is split into
86
+ per-core windows and the secondary/scratch slot was dropped rather
87
+ than forced to fit)?
88
+
89
+ Only `m55_he`/`m55_hp` roles are AEN slot0-XIP cores (same convention
90
+ as `loader._resolve_slot0_load_address`); a project with neither in
91
+ `cores:` -- any non-AEN family, or an AEN project with no Zephyr M55
92
+ slice -- is never single-slot by this check.
93
+
94
+ Lazy import for the same circularity reason
95
+ `loader._resolve_slot0_load_address` documents: `zephyr_board.py`
96
+ imports from `loader.py` at module scope.
97
+ """
98
+ memory_map = project.som_preset.get("memory_map")
99
+ if not memory_map:
100
+ return False
101
+ from .zephyr_board import ( # noqa: PLC0415
102
+ ZephyrBoardEmitError,
103
+ _aen_role_slot0_map,
104
+ )
105
+ for core_id in project.cores:
106
+ if not core_id.startswith("m55_"):
107
+ continue
108
+ role = core_id[len("m55_"):]
109
+ if role not in ("he", "hp"):
110
+ continue
111
+ try:
112
+ if _aen_role_slot0_map(memory_map, role) is not None:
113
+ return True
114
+ except ZephyrBoardEmitError as exc:
115
+ raise OrchestratorError(
116
+ f"cannot resolve boot.swap_algorithm default for "
117
+ f"{project.sku}: {exc}") from exc
118
+ return False
119
+
120
+
121
+ def _has_zephyr_slice(project: BoardProject) -> bool:
122
+ """Whether any core in this project builds under Zephyr.
123
+
124
+ sysbuild is a Zephyr-build concept: `build/alp_sysbuild.conf` is
125
+ consumed only as `west build --sysbuild -- -DSB_CONF_FILE=<path>`.
126
+ A project with no Zephyr slice has no sysbuild configure for the
127
+ overlay to reach, so emitting one is dead output at best -- and at
128
+ worst (#562) a hard refusal of a project that never runs sysbuild.
129
+ """
130
+ return any(s.os == "zephyr" for s in project.cores.values())
131
+
132
+
133
+ def sysbuild_family_base_conf(project: BoardProject) -> Optional[Path]:
134
+ """Path to the curated `zephyr/sysbuild/<family>/sysbuild.conf` for
135
+ this project's SoM family, or None when the family has no curated
136
+ profile on disk.
137
+
138
+ This is the LAYERING base (issue #807): a customer `boot:` overlay
139
+ from `emit_sysbuild_conf` builds ON TOP of this file via a
140
+ semicolon-separated `SB_CONF_FILE` list rather than replacing it,
141
+ so a `boot:` block no longer forks family boot policy into two
142
+ divergent places. Verified against the pinned Zephyr 4.4.0's
143
+ `share/sysbuild/cmake/modules/sysbuild_kconfig.cmake`: `SB_CONF_FILE`
144
+ accepts a CMake list (`foreach(conf_file ${conf_file_expanded})`
145
+ splits on the list separator) and every listed file lands in the
146
+ merged sysbuild `.config`, later files winning on a repeated
147
+ symbol -- confirmed empirically via a `native_sim` sysbuild
148
+ configure with two `;`-joined conf files, both symbols present in
149
+ the merged `.config`.
150
+ """
151
+ family = (project.som_preset.get("family") or "").lower()
152
+ slug = next((v for k, v in _FAMILY_SYSBUILD_DIRS.items()
153
+ if family.startswith(k)), None)
154
+ if slug is None:
155
+ return None
156
+ base = REPO / "zephyr" / "sysbuild" / slug / "sysbuild.conf"
157
+ return base if base.is_file() else None
158
+
159
+
160
+ def emit_sysbuild_conf(project: BoardProject) -> str:
161
+ """Emit the sysbuild.conf overlay for the project's `boot:` block.
162
+
163
+ Returns a SB_CONFIG_*-style snippet the build wires into
164
+ `build/alp_sysbuild.conf` (consumed via `west build --sysbuild
165
+ -- -DSB_CONF_FILE=<abs path>`). Returns an empty string when the project
166
+ does not declare a `boot:` block (the SDK's stock per-family
167
+ defaults at zephyr/sysbuild/<family>/sysbuild.conf apply), and when
168
+ the project has NO Zephyr slice at all (#562) -- sysbuild exists
169
+ only inside a Zephyr build, so a Yocto-only project has nothing to
170
+ apply the overlay to. On such a project the `boot:` block governs
171
+ the U-Boot/FIT chain instead, which this emitter has never rendered;
172
+ the previous behaviour was to render it as MCUboot anyway and, on
173
+ `rsa3072`, to raise below and fail the project's entire build-plan
174
+ emit with sysbuild advice for a platform that never runs sysbuild.
175
+
176
+ `boot.method:` is optional and the SoM family supplies its default
177
+ (`_FAMILY_BOOT_METHOD_DEFAULTS` -- the value board.schema.json has
178
+ always documented). It is NOT an unconditional `mcuboot`.
179
+
180
+ Every `SB_CONFIG_*` symbol emitted here MUST exist in the pinned
181
+ Zephyr's `share/sysbuild/**/Kconfig*` -- sysbuild treats an
182
+ undefined-symbol warning as FATAL, so one invented name aborts the
183
+ whole `boot:` configure (issue #807). Slot/scratch partition
184
+ *sizes* have no sysbuild expression at all: MCUboot takes slot
185
+ geometry from the board DT partitions, per the Zephyr 4.4 Kconfig
186
+ help text on every `MCUBOOT_MODE_*` choice -- that's also why
187
+ `boot.slots` / `boot.scratch_size_kib` were dropped from the
188
+ schema rather than fixed (see board.schema.json for the full
189
+ rationale).
190
+ """
191
+ boot = project.boot or {}
192
+ if not boot:
193
+ return ""
194
+ if not _has_zephyr_slice(project):
195
+ return ""
196
+ method = (boot.get("method") or _default_boot_method(project)).lower()
197
+ if method == "none":
198
+ return ("# Auto-generated from board.yaml `boot:` block.\n"
199
+ "SB_CONFIG_BOOTLOADER_MCUBOOT=n\n")
200
+ if method != "mcuboot":
201
+ return ""
202
+
203
+ lines: list[str] = [
204
+ "# Auto-generated by scripts/alp_orchestrate.py from "
205
+ "board.yaml `boot:`.",
206
+ "# Drives the sysbuild MCUboot child image. Customers who",
207
+ "# omit `boot:` get the SDK's stock per-family defaults.",
208
+ "",
209
+ "SB_CONFIG_BOOTLOADER_MCUBOOT=y",
210
+ ]
211
+ sign = boot.get("signing") or {}
212
+ algo = (sign.get("algorithm") or "").lower()
213
+ if algo == "rsa3072":
214
+ # sysbuild's BOOT_SIGNATURE_TYPE_RSA choice has no key-length
215
+ # knob -- length is an mcuboot-image CONFIG_BOOT_SIGNATURE_
216
+ # TYPE_RSA_LEN symbol (bootloader/mcuboot/boot/zephyr/Kconfig),
217
+ # a different Kconfig namespace sysbuild never forwards into.
218
+ # Until a per-image `sysbuild/mcuboot.conf` overlay seam exists
219
+ # (see docs/secure-boot.md), rsa3072 has no honest emit -- fail
220
+ # loud rather than silently ship rsa2048's default length.
221
+ raise OrchestratorError(
222
+ "boot.signing.algorithm: rsa3072 has no sysbuild "
223
+ "expression yet -- SB_CONFIG_BOOT_SIGNATURE_TYPE_RSA has "
224
+ "no key-length knob (that's an mcuboot-image CONFIG_BOOT_"
225
+ "SIGNATURE_TYPE_RSA_LEN symbol, a different Kconfig "
226
+ "namespace sysbuild doesn't forward). Use rsa2048 (mcuboot's "
227
+ "RSA default length) or ecdsa_p256/ed25519 until a per-image "
228
+ "sysbuild/mcuboot.conf overlay seam ships.")
229
+ algo_kc = {
230
+ "ecdsa_p256": "SB_CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y",
231
+ "rsa2048": "SB_CONFIG_BOOT_SIGNATURE_TYPE_RSA=y",
232
+ "ed25519": "SB_CONFIG_BOOT_SIGNATURE_TYPE_ED25519=y",
233
+ }.get(algo)
234
+ if algo_kc:
235
+ lines.append(algo_kc)
236
+ if sign.get("key_file"):
237
+ lines.append(f'SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="{sign["key_file"]}"')
238
+ swap_explicit = boot.get("swap_algorithm")
239
+ swap = (swap_explicit or "").lower()
240
+ single_slot = _boot_target_is_single_slot(project)
241
+ if swap_explicit and swap in ("scratch", "move", "overwrite") and single_slot:
242
+ # alp-sdk#1413: this SKU's `memory_map:` has no slot1/scratch
243
+ # region (see `_boot_target_is_single_slot`) -- every two-slot
244
+ # MCUboot swap mode needs one. An explicit request for one here
245
+ # is not a default that quietly drifted, it is a `boot:` block
246
+ # asking for a partition that does not exist on this target's DT,
247
+ # so fail loud rather than emit a config that cannot boot.
248
+ raise OrchestratorError(
249
+ f"boot.swap_algorithm: {swap} needs a slot1/scratch "
250
+ f"partition that {project.sku}'s disjoint-slot0 "
251
+ "`memory_map:` (metadata/e1m_modules/"
252
+ f"{project.sku}.yaml, alp-sdk#1069) doesn't declare -- this "
253
+ "single-slot target only supports the stock "
254
+ "single-app boot. Drop `boot.swap_algorithm:` (it now "
255
+ "defaults correctly on single-slot targets) rather than "
256
+ "setting it explicitly.")
257
+ if not swap_explicit:
258
+ # Historically defaulted unconditionally to "scratch"
259
+ # (alp-sdk#1413): wrong on a single-slot target, whose DT has no
260
+ # scratch partition to swap into. Single-slot targets default to
261
+ # the single-app boot their curated `zephyr/sysbuild/aen/
262
+ # sysbuild.conf` base already ships; every other target keeps the
263
+ # historical "scratch" default.
264
+ swap = "none" if single_slot else "scratch"
265
+ swap_kc = {
266
+ "scratch": "SB_CONFIG_MCUBOOT_MODE_SWAP_SCRATCH=y",
267
+ "move": "SB_CONFIG_MCUBOOT_MODE_SWAP_USING_MOVE=y",
268
+ "overwrite": "SB_CONFIG_MCUBOOT_MODE_OVERWRITE_ONLY=y",
269
+ "none": "SB_CONFIG_MCUBOOT_MODE_SINGLE_APP=y",
270
+ }.get(swap)
271
+ if swap_kc:
272
+ lines.append(swap_kc)
273
+ return "\n".join(lines) + "\n"
274
+
275
+
276
+ def emit_tfm_sysbuild_conf(project: BoardProject) -> str:
277
+ """Emit the TF-M sysbuild child-image overlay for `security.psa:`.
278
+
279
+ Returns a `SB_CONFIG_*` + `CONFIG_PSA_CRYPTO_*` snippet the build
280
+ wires into `build/sysbuild/tfm/tfm.conf` (picked up by sysbuild
281
+ convention from its `sysbuild/tfm/` path). Returns an empty
282
+ string when the project omits `security.psa:` or sets
283
+ `tfm: false` -- PSA Crypto then runs entirely non-secure
284
+ (mbedTLS-only) and no child image is built.
285
+
286
+ The TF-M boundary lives on the same M55-HP core as the non-secure
287
+ app via Armv8-M TrustZone-M; see ADR 0013.
288
+ """
289
+ security = project.security or {}
290
+ psa = security.get("psa") or {}
291
+ if not psa or not psa.get("tfm"):
292
+ return ""
293
+
294
+ # TF-M build type follows the project's MCUboot build_type when
295
+ # set so the secure + bootloader artefacts ship the same flavour.
296
+ # Default is Release; debug builds use MinSizeRel for parity with
297
+ # the upstream sysbuild template.
298
+ boot = project.boot or {}
299
+ build_type = (boot.get("build_type") or "Release")
300
+ if build_type.lower() == "debug":
301
+ tfm_build_type = "Debug"
302
+ elif build_type.lower() in ("minsizerel", "min_size_rel"):
303
+ tfm_build_type = "MinSizeRel"
304
+ elif build_type.lower() == "release":
305
+ tfm_build_type = "Release"
306
+ else:
307
+ # Pass through verbatim if the customer set an exact CMake
308
+ # build-type string we don't pre-canonicalise.
309
+ tfm_build_type = build_type
310
+
311
+ lines: list[str] = [
312
+ "# Auto-generated by scripts/alp_orchestrate.py from "
313
+ "board.yaml `security.psa:`.",
314
+ "# Drives the sysbuild TF-M child image on the M55-HP core via "
315
+ "Armv8-M TrustZone-M.",
316
+ "# See docs/adr/0013-tfm-boundary-m55-hp-trustzone.md and "
317
+ "docs/security-audit-plan.md.",
318
+ "",
319
+ "SB_CONFIG_TFM=y",
320
+ f"SB_CONFIG_TFM_BUILD_TYPE={tfm_build_type}",
321
+ ]
322
+
323
+ persistent_slots = psa.get("persistent_slots")
324
+ if persistent_slots is None:
325
+ # Schema default mirrors v0.6: 16 slots.
326
+ persistent_slots = 16
327
+ lines.append(
328
+ f"CONFIG_PSA_CRYPTO_PERSISTENT_SLOT_COUNT={int(persistent_slots)}"
329
+ )
330
+
331
+ its = psa.get("its_storage")
332
+ if its:
333
+ lines.append(f'CONFIG_PSA_CRYPTO_ITS_BACKING_STORE="{its}"')
334
+ ps = psa.get("ps_storage")
335
+ if ps:
336
+ lines.append(f'CONFIG_PSA_CRYPTO_PS_BACKING_STORE="{ps}"')
337
+
338
+ att_root = (psa.get("attestation_root") or "none").lower()
339
+ if att_root == "optiga_trust_m":
340
+ lines.append(
341
+ "# Attestation root anchored in OPTIGA Trust M (on-module "
342
+ "secure element).")
343
+ lines.append(
344
+ "# See src/security/optiga_trust_m_bridge.c for the "
345
+ "PSA <-> OPTIGA bridge driver.")
346
+ lines.append("CONFIG_ALP_SDK_PSA_ATTESTATION_OPTIGA=y")
347
+ elif att_root == "tfm_internal":
348
+ lines.append("CONFIG_ALP_SDK_PSA_ATTESTATION_TFM_INTERNAL=y")
349
+
350
+ return "\n".join(lines) + "\n"