Image of an arrow

Zephyr + Linux sur STM32MP2: Résoudre la mémoire et la sécurité pour une opération à faible consommation

Avatar

lbeaufils

Introduction

Dans le monde de l’embarqué, la question n’est plus de savoir quel OS utiliser, mais comment les faire fonctionner ensemble ?

Les SoC modernes estompent la frontière entre les microcontrôleurs temps réel et les processeurs d’application. Des puces comme le STM32MP2 de STMicroelectronics intègrent à la fois un Cortex-A35, capable d’exécuter une distribution Linux complète, et un Cortex-M33, conçu pour des tâches temps réel déterministes et à faible latence. Deux cœurs, une seule puce : le meilleur des deux mondes.

Cette architecture hybride est de plus en plus pertinente pour les applications industrielles. Considérons quelques scénarios courants :

    • Sécurité : un cœur temps réel peut surveiller les capteurs et déclencher des réponses de sécurité avec une latence garantie, indépendamment de ce que fait l’OS applicatif.
    • Faible consommation : le processeur applicatif peut être mis en veille pour économiser de l’énergie tandis que le cœur temps réel continue de surveiller l’environnement et décide quand réveiller le système.
    • Délestage : les tâches d’acquisition temps réel critiques (échantillonnage ADC, interrogation de capteurs, contrôle de moteur, etc.) s’exécutent sur le M33, tandis que les traitements lourds, comme la FFT, l’inférence IA ou le rendu UI, s’exécutent sur l’A35 sous Linux.

Cependant, cette puissance a un coût : avoir deux systèmes d’exploitation partageant la même puce introduit des défis de configuration non triviaux : la disposition de la mémoire, les frontières de sécurité et la synchronisation inter-cœurs doivent toutes être soigneusement coordonnées à chaque niveau de la pile.

Notre démo se concentre sur le scénario de faible consommation : Zephyr s’exécute sur le Cortex-M33, lisant en continu les données des capteurs et gérant l’affichage i2c, tandis que Linux sur le Cortex-A35 gère les traitements plus lourds et peut être mis en veille et réveillé de manière transparente, sans perdre un seul échantillon. Ce qui suit documente exactement ce qu’il a fallu faire pour que cela fonctionne.

 [L’introduction est en français, le reste du texte en anglais]


Demo Overview

The demo runs on a STM32MP257F-DK board and is split across two runtimes that communicate via OpenAMP over a shared memory region.

Zephyr: The Real-Time Side (Cortex-M33)

Zephyr owns the real-time responsibilities:

  • Data acquisition (hc-sr04): data is fetched continuously, with strict timing guarantees.
  • RT Displaying (ssd1306): real-time sensor values are displayed in the dedicated screen, giving immediate visual feedback independent of Linux.
  • OpenAMP master: Zephyr acts as the OpenAMP master, initiating and managing the communication channel with Linux.

Linux: The Application Side (Cortex-A35)

Linux handles the heavier, less time-critical workload:

  • Qt6 application: a graphical interface shows date/time to simulate computationally intensive data processing. The kind of task that would be unreasonable on a microcontroller.
  • OpenAMP via serial: Linux communicates with Zephyr through the OpenAMP virtual serial interface, receiving sensor data for processing and display.
  • libgpiod application: monitors the User-1 button to trigger entry into low-power sleep mode (s2idle).
  • Wake-up trigger: a physical interrupt to WAKE-UP button brings the system back from sleep, resuming Linux and re-establishing communication with Zephyr.

OPTEE-OS: The security configuration (Cortex-A35)

OP-TEE is a TEE (Trusted Execution Environment), which runs in the A35 ARM TrustZone. It is responsible for the initialization of security peripherals like the RIF that we will see below.

The goal: demonstrate a credible low-power embedded system where Linux can sleep and wake up, while Zephyr seamlessly keeps running and buffering data.

 


The Challenge Behind: Memory Configuration between the cores

Getting two OSes to share a chip is one thing. Getting them to survive a sleep cycle together is another. Here’s what had to be configured.

State of the Art: The STM32MP2 Security Model and Power Mode

The STM32MP2 introduces a hardware security model built around the concept of TDCID (Trusted Domain Core ID). In this model, the Cortex-A35 acts as the security manager: it controls which cores and which software layers can access which peripherals.
This is enforced by the RIF (Resource Isolation Framework) which is a firewall that grants or denies peripheral access to secure and non-secure worlds. The RIF is configured early in the boot chain, inside OPTEE (the trusted execution environment running on the A35 secure world).

 

For our demo, we use:
  • An open RIF configuration devicetree (which can be found in the OPTEE-OS in-tree files), no strict peripheral lockdown, suitable for a development/demo context.
  • A custom Buildroot system aligned with the latest OpenSTLinux release, ensuring compatibility with upstream version.

Regarding the power mode, with the v6.2.0 of OpenSTLinux, the most low-power mode we can reach with the SoC is the LP_Stop2 mode (see in the table below). In this mode, the VDDCPU (vdd of the A35) is OFF and the DDR is in self-refresh mode. The self-refresh mode refers to the mode where the DDR retains data with very low consumptions, but it cannot be accessed. Only the VDDCORE (vdd of the m33) is kept ON.

 

 

The Sleep Mode and the Memory Problem

When Linux loads the Zephyr firmware onto the M33 via remoteproc, the firmware binary is placed in DDR by default, as specified in the Linux device tree. That works fine in the usual setup. But when the system enters deep sleep, the DDR goes into self-refresh mode. The M33 then tries to reach the DDR which completely breaks the system triggering a panic error.

To resolve this issue, the firmware should use another memory which is kept alive during the A35 sleep mode. So the SRAM1 and RETRAM have been selected. But even in the open RIF configuration, the SRAM1 is configured for Secure usage. So all Linux, OPTEE-OS and Zephyr devicetrees need to be updated to reflect these changes.

The fix is a three-layer change:

1. Move the M33 firmware to SRAM1 and RETRAM (Linux device tree)

The remoteproc memory region is redirected from DDR to SRAM1 and RETRAM, which remains powered during sleep. The memory-region of the m33_rproc node is updated to reflect the change:

/* stm32mp257f-dk-resmem.dtsi */
reserved-memory {
cm33_sram1: cm33-sram1@a043000 {
reg = <0x0 0xa043000 0x0 0x1d000>;
no-map;
};
/* stm32mp257f-dk.dts */
&m33_rproc {
mboxes = <&ipcc1 0x100>, <&ipcc1 0x101>, <&ipcc1 2>;
mbox-names = "vq0", "vq1", "shutdown";
- memory-region = <&cm33_cube_fw>, <&cm33_cube_data>,
+ memory-region = <&cm33_sram1>, <&cm33_retram>,
<&ipc_shmem_1>, <&vdev0vring0>,
<&vdev0vring1>, <&vdev0buffer>,
<&cm33_sram2>;
st,syscfg-nsvtor = <&a35ss_syscfg 0x20a8 0xffffff80>;
+ keep-power-in-suspend;
status = "okay";
};

 

The keep-power-in-suspend property tells the kernel to keep the M33 running through the suspend cycle.

2. Grant non-secure world access to SRAM1 (OP-TEE device tree)

By default, SRAM1 may not be accessible from the non-secure world where Linux and Zephyr operate. The RIF configuration must explicitly open access via the &cm33_sram1 node, using RIF_NSEC (non-secure) flag:

/* stm32mp257f-dk-ca35tdcid-rif.dtsi */
&cm33_sram1 {
st,protreg = <RISABPROT(
RIF_DDCID_DIS, /* domain CID disabled */
RIF_UNUSED,
-   RIF_SEC, /* accessible from secure world */
+   RIF_NSEC, /* accessible from non-secure world */
RIF_NPRIV, /* non-privileged access allowed */
RIF_CFDIS, /* CID filtering disabled */
RIF_UNUSED, RIF_UNUSED, RIF_UNUSED
)>;
};

3. Configure Zephyr to link against SRAM1 and RETRAM addresses (Zephyr devicetree overlay)

Zephyr’s linker must know it is running from SRAM1 and RETRAM, not DDR. The DDR code/data regions are deleted and replaced with the correct SRAM1 and RETRAM addresses in the board overlay:

/* stm32mp257f_dk_stm32mp257fxx_m33.overlay */
+/delete-node/ &ddr_code;
+/delete-node/ &ddr_sys;
+
+/ {
+   /* Remap to SRAM1 physical address */
+   ddr_code: memory0@a043000 {
+     reg = <0xa043000 0x1d000>;
+     ranges = <0x0 0xa043000 0x1d000>;
+     #address-cells = <1>;
+     #size-cells = <1>;
+   };
+
+   /* Remap to RETRAM physical address */
+   ddr_sys: memory1@a080000 {
+     reg = <0xa080000 0x1f000>;
+     ranges = <0x0 0xa080000 0x1f000>;
+     #address-cells = <1>;
+     #size-cells = <1>;
+   };
+};

 

The OpenAMP Vring Problem

Moving the firmware to SRAM1 and RETRAM keeps the M33 alive through sleep. But there’s a second issue: OpenAMP vring buffers.
The vring is the shared memory ring buffer used by OpenAMP to exchange messages between Zephyr and Linux. These buffers are allocated in DDR that is not accessible during the sleep. If Zephyr tries to access OpenAMP vrings during the sleep mode, the OS will panic.

You can see this clearly in the reserved memory declarations, that all vring and IPC buffers live at DDR addresses (0x812xxxxx):

/* stm32mp257f-dk-resmem.dtsi */

ipc_shmem_1: ipc-shmem-1@81200000 {
compatible = "shared-dma-pool";
reg = <0x0 0x81200000 0x0 0xf8000>; /* DDR */
no-map;
};

vdev0vring0: vdev0vring0@812f8000 {
compatible = "shared-dma-pool";
reg = <0x0 0x812f8000 0x0 0x1000>; /* DDR */
no-map;
};

vdev0vring1: vdev0vring1@812f9000 {
compatible = "shared-dma-pool";
reg = <0x0 0x812f9000 0x0 0x1000>; /* DDR */
no-map;

};

vdev0buffer: vdev0buffer@812fa000 {

compatible = "shared-dma-pool";
reg = <0x0 0x812fa000 0x0 0x6000>; /* DDR */
no-map;
};

The solution: a SLEEP/WAKE handshake.

Rather than deinitialize and reinitialize the OpenAMP connection at each sleep cycle, which would be hard to synchronize between OSes, the system uses a coordinated protocol:
  • Before entering deep sleep, Linux sends a SLEEP signal to Zephyr over OpenAMP.
  • Zephyr receives the signal, acknowledges it, and takes over full responsibility: it continues reading
    sensor data and buffers it locally. Zephyr stops sending data through OpenAMP.
  • After waking up, Linux sends a WAKE signal.
  • Zephyr flushes its buffer back to Linux, restoring continuity of data as if the sleep never happened.

 

This approach keeps DDR usage minimal during active operation, allows a clean sleep mode, and ensures no data is lost across the sleep boundary.


Conclusion

The system works. Linux sleeps, Zephyr keeps sampling, and when Linux wakes up, it gets back every data point it missed.

The more interesting takeaway is what it took to get there: a coordinated change spanning the Linux device tree, the OP-TEE security configuration, and Zephyr’s linker map. None of these three layers is hard in isolation, however, together they form a pattern you will encounter on any heterogeneous SoC where power management and inter-core communication need to coexist.

One last note on the demo setup: sleep is triggered by a physical button press, which makes it easy to demonstrate on stage. In a real product, that button can be replaced by any interrupt source (for e.g., a distance threshold detected by Zephyr itself and signalled to Linux over OpenAMP, a timer, an external GPIO, etc). The sleep/wake handshake described here is agnostic to the trigger, so the architecture supports autonomous operation without modification.

Laisser un commentaire

Votre adresse courriel ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire le pourriel. En savoir plus sur comment les données de vos commentaires sont utilisées.


Articles similaires

Image of an arrow

Accélérer le démarrage précoce de Linux avec Yocto multiconfig De Paul Le Guen de Kerneizon Introduction Dans les produits embarqués, il est souvent essentiel de gagner quelques secondes, voire quelques centaines de millisecondes, entre la mise sous tension et la disponibilité de l’application. Dans cet article, je vais présenter un workflow pratique pour mesurer, comparer […]

Dans le monde actuel, où tout, des machines à café aux équipements industriels, est connecté au réseau, connaître et évaluer la sécurité de vos logiciels et de leurs dépendances n’a jamais été aussi crucial. La plupart des vulnérabilités proviennent de petits bogues dans des composants logiciels, et plus récemment (bien que plus rarement) d’attaques sophistiquées […]

Object detection

Déployer un réseau de neurones artificiels convolutionnels sur un système embarqué Introduction L’essor des plateformes embarquées capables de traitement IA a profondément changé la donne : il est désormais possible de déployer des solutions intelligentes sur l’équipement lui-même, sans dépendre du cloud. C’est ce qu’on appelle l’« edge AI ». Lors d’un récent projet, nous […]

Le débogage de microcontrôleurs (MCU) a toujours été essentiel pour produire du code de haute qualité, mais la dépendance aux environnements de développement spécifiques aux fabricants et aux configurations complexes des serveurs de débogage peut parfois nuire à un flux de travail fluide. Bien que VSCode ait révolutionné la façon dont les développeurs écrivent et […]

  Permettre à l’industrie des systèmes embarqués de se conformer aux cyber-réglementations grâce à un outil de gestion des vulnérabilités innovant et open source.   Nuremberg, le 11 mars 2025 – Savoir-faire Linux, société de conseil et d’ingénierie en logiciels libres et Open Source est fière d’annoncer la sortie officielle de VulnScout.io, une solution Open Source […]