diff --git a/.codespellrc b/.codespellrc
index 14770824d320c..4ed2e6b695cf6 100644
--- a/.codespellrc
+++ b/.codespellrc
@@ -9,6 +9,7 @@ exclude-file = .codespell-ignore-lines
skip =
LICENSE,
*/CODEOWNERS,
+ */system/zbus/images/*,
# Ignore seemingly misspelled words.
# lowercase: case insensitive
diff --git a/Documentation/applications/system/zbus/images/zbus_anatomy.svg b/Documentation/applications/system/zbus/images/zbus_anatomy.svg
new file mode 100644
index 0000000000000..e9bc6c79cefc9
--- /dev/null
+++ b/Documentation/applications/system/zbus/images/zbus_anatomy.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/Documentation/applications/system/zbus/images/zbus_observation_mask.svg b/Documentation/applications/system/zbus/images/zbus_observation_mask.svg
new file mode 100644
index 0000000000000..4405a8f3e4aed
--- /dev/null
+++ b/Documentation/applications/system/zbus/images/zbus_observation_mask.svg
@@ -0,0 +1,152 @@
+
diff --git a/Documentation/applications/system/zbus/images/zbus_operations.svg b/Documentation/applications/system/zbus/images/zbus_operations.svg
new file mode 100644
index 0000000000000..419cc6c3d5d5f
--- /dev/null
+++ b/Documentation/applications/system/zbus/images/zbus_operations.svg
@@ -0,0 +1,49 @@
+
diff --git a/Documentation/applications/system/zbus/images/zbus_overview.svg b/Documentation/applications/system/zbus/images/zbus_overview.svg
new file mode 100644
index 0000000000000..24861bbbe3375
--- /dev/null
+++ b/Documentation/applications/system/zbus/images/zbus_overview.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/Documentation/applications/system/zbus/images/zbus_type_of_observers.svg b/Documentation/applications/system/zbus/images/zbus_type_of_observers.svg
new file mode 100644
index 0000000000000..1bc29e3dadf16
--- /dev/null
+++ b/Documentation/applications/system/zbus/images/zbus_type_of_observers.svg
@@ -0,0 +1,18 @@
+
diff --git a/Documentation/applications/system/zbus/index.rst b/Documentation/applications/system/zbus/index.rst
new file mode 100644
index 0000000000000..ab7eac19cb63c
--- /dev/null
+++ b/Documentation/applications/system/zbus/index.rst
@@ -0,0 +1,210 @@
+==========================
+``zbus`` ZBus message bus
+==========================
+
+Port of the `zbus `_
+message bus to NuttX, built entirely on native NuttX primitives. ZBus
+implements many-to-many communication through **channels** (typed shared
+messages) observed by **observers**, keeping publishers and consumers
+fully decoupled.
+
+.. figure:: images/zbus_overview.svg
+ :alt: zbus usage overview
+ :width: 75%
+
+ A typical zbus application architecture.
+
+Channels and observers are defined statically, in any source file, with
+declarative macros. The definitions are collected at link time through
+iterable sections (see the Iterable Sections component documentation) --
+there is no runtime registration and no central list to maintain.
+
+.. figure:: images/zbus_anatomy.svg
+ :alt: zbus anatomy
+ :width: 70%
+
+ ZBus anatomy: channels, observers and observations.
+
+Observer types
+==============
+
+.. figure:: images/zbus_type_of_observers.svg
+ :alt: zbus observer types
+ :width: 70%
+
+ The four observer types.
+
+======================== ===================================================
+Type Behavior
+======================== ===================================================
+Listener Callback executed synchronously in the publisher
+ context (``ZBUS_LISTENER_DEFINE``).
+Subscriber Receives channel references through a message
+ queue; waits with ``zbus_sub_wait()``
+ (``ZBUS_SUBSCRIBER_DEFINE``).
+Message subscriber Receives a *copy* of every published message, in
+ order; waits with ``zbus_sub_wait_msg()``
+ (``ZBUS_MSG_SUBSCRIBER_DEFINE``,
+ ``CONFIG_ZBUS_MSG_SUBSCRIBER``).
+Async listener Callback executed on a dedicated task with a
+ copy of the message
+ (``ZBUS_ASYNC_LISTENER_DEFINE``,
+ ``CONFIG_ZBUS_ASYNC_LISTENER``).
+======================== ===================================================
+
+Runtime observers (``zbus_chan_add_obs()``/``zbus_chan_rm_obs()``,
+``CONFIG_ZBUS_RUNTIME_OBSERVERS``), per-observation notification masks,
+observer enable/disable, message validators and channel user data are
+also supported.
+
+.. figure:: images/zbus_observation_mask.svg
+ :alt: zbus observation mask
+ :width: 75%
+
+ Observer enable/disable and per-observation masks: disabling the
+ observer (b) silences every channel; masking observations (c, d)
+ silences individual channels.
+
+Example
+=======
+
+The figure below shows the kind of decoupled architecture zbus enables:
+every block only talks to channels, so each one can be replaced without
+touching the others.
+
+.. figure:: images/zbus_operations.svg
+ :alt: zbus sensor-based application
+ :width: 85%
+
+ A sensor-based application built on zbus.
+
+.. code-block:: c
+
+ #include
+
+ struct acc_msg
+ {
+ int x;
+ int y;
+ int z;
+ };
+
+ static void listener_cb(const struct zbus_channel *chan)
+ {
+ const struct acc_msg *msg = zbus_chan_const_msg(chan);
+ printf("x=%d y=%d z=%d\n", msg->x, msg->y, msg->z);
+ }
+
+ ZBUS_LISTENER_DEFINE(acc_listener, listener_cb);
+ ZBUS_SUBSCRIBER_DEFINE(acc_subscriber, 4);
+
+ ZBUS_CHAN_DEFINE(acc_chan, /* Name */
+ struct acc_msg, /* Message type */
+ NULL, /* Validator */
+ NULL, /* User data */
+ ZBUS_OBSERVERS(acc_listener, /* Observers, in */
+ acc_subscriber),/* priority order */
+ ZBUS_MSG_INIT(.x = 0, .y = 0, .z = 0));
+
+ /* Publisher: */
+
+ struct acc_msg msg = { 1, 10, 100 };
+ zbus_chan_pub(&acc_chan, &msg, 1000);
+
+ /* Subscriber thread: */
+
+ const struct zbus_channel *chan;
+ if (zbus_sub_wait(&acc_subscriber, &chan, ZBUS_FOREVER) == 0)
+ {
+ zbus_chan_read(chan, &msg, 500);
+ }
+
+A complete runnable example is available in ``apps/examples/zbus``
+(``CONFIG_EXAMPLES_ZBUS``), and a cmocka test suite covering the whole
+API in ``apps/testing/zbus`` (``CONFIG_TESTING_ZBUS``).
+
+Not ported
+==========
+
+The following Zephyr zbus features are **not available** in this port:
+
+* **Multi-domain proxy agent** (``CONFIG_ZBUS_PROXY_AGENT``): bridges
+ channels between domains/cores over IPC. Experimental upstream and
+ tied to the Zephyr IPC service; a NuttX equivalent would be built on
+ rpmsg and is left as future work.
+* **Publishing from interrupt handlers**: the Zephyr original allows
+ ``zbus_chan_pub()`` from ISRs with ``K_NO_WAIT``. This port is a
+ userspace library and its primitives (semaphores, message queues, lazy
+ initialization) are not ISR-safe: interrupt handling belongs to the
+ driver, which should hand the data to a thread (the usual NuttX
+ pattern) that then publishes it. ``test_timer_driven_publisher`` in
+ ``apps/testing/zbus`` shows the pattern with a kernel timer interrupt
+ delivering a signal to a sampling thread.
+* **Priority boost (Highest Locker Protocol)**
+ (``CONFIG_ZBUS_PRIORITY_BOOST``): the Zephyr hand-rolled protection
+ against priority inversion during the notification process. Not
+ needed: enable the native ``CONFIG_PRIORITY_INHERITANCE`` so the
+ channel semaphores get equivalent protection from the kernel.
+* **net_buf pools and pool isolation**
+ (``CONFIG_ZBUS_MSG_SUBSCRIBER_BUF_*``): obsolete by design in this
+ port -- message queues copy the payload on ``mq_send``, so no shared
+ reference-counted buffers exist at all.
+* **Static/user-provided runtime observer nodes**
+ (``CONFIG_ZBUS_RUNTIME_OBSERVERS_NODE_ALLOC_STATIC/NONE``): runtime
+ observer nodes are always heap-allocated in this port.
+
+Differences from the Zephyr original
+====================================
+
+* Timeouts are plain milliseconds (``int32_t``): ``ZBUS_NO_WAIT`` (0) and
+ ``ZBUS_FOREVER`` (-1) replace ``K_NO_WAIT``/``K_FOREVER``.
+* Subscriber queues are POSIX message queues opened lazily on first API
+ use through the kernel ``file_mq_*`` interface, making them usable from
+ any task (a ``mqd_t`` descriptor would die with the opening task).
+* Initialization is lazy (``pthread_once`` on the first API call)
+ replacing the Zephyr ``SYS_INIT`` hook; no explicit init call is
+ needed.
+* Async listeners run on a dedicated task per listener (spawned on
+ first use; priority and stack size are configurable) instead of the
+ Zephyr system work queue.
+
+Configuration
+=============
+
+Requirements:
+
+* The board linker script must provide the zbus iterable sections, either
+ by including ```` inside ``.text`` (see the
+ ``linum-stm32h753bi`` board) or through
+ ``CONFIG_ITERABLE_SECTIONS_LINKER_INSERT``
+ (zero-touch mode; see its help text for the MEMORY-layout constraint).
+* ``CONFIG_MQ_MAXMSGSIZE`` must be at least
+ ``CONFIG_ZBUS_MSG_SUBSCRIBER_MAX_MSG_SIZE`` plus the size of a pointer
+ when message subscribers or async listeners are used, otherwise their
+ queues fail to open with ``-EINVAL``.
+* FLAT build (the library uses the kernel ``file_mq_*`` interface
+ directly).
+
+Main options:
+
+* ``CONFIG_ZBUS`` -- enable the library.
+* ``CONFIG_ZBUS_CHANNEL_NAME`` / ``CONFIG_ZBUS_OBSERVER_NAME`` -- name
+ fields and lookup by name.
+* ``CONFIG_ZBUS_CHANNEL_ID`` -- numeric channel identifiers
+ (``ZBUS_CHAN_DEFINE_WITH_ID``, ``zbus_chan_from_id()``).
+* ``CONFIG_ZBUS_MSG_SUBSCRIBER`` -- message subscribers
+ (+ ``_MAX_MSG_SIZE``, ``_QUEUE_SIZE``).
+* ``CONFIG_ZBUS_ASYNC_LISTENER`` -- async listeners (+ ``_PRIORITY``,
+ ``_STACKSIZE`` of their tasks).
+* ``CONFIG_ZBUS_RUNTIME_OBSERVERS`` -- runtime observers.
+* ``CONFIG_ZBUS_CHANNEL_PUBLISH_STATS`` -- publish timestamp/count.
+* ``CONFIG_ZBUS_ASSERT_MOCK`` -- invalid parameters return ``-EFAULT``
+ instead of asserting (for tests).
+
+Credits
+=======
+
+The zbus design and the diagrams in this page come from the upstream
+`Zephyr zbus documentation
+`_
+by Rodrigo Peixoto and contributors (Apache License 2.0).
diff --git a/Documentation/components/index.rst b/Documentation/components/index.rst
index fe60af7b9851c..edd1415ede23d 100644
--- a/Documentation/components/index.rst
+++ b/Documentation/components/index.rst
@@ -13,6 +13,7 @@ case, you can head to the :doc:`reference <../reference/index>`.
binfmt.rst
concurrency/index.rst
+ iterable_sections.rst
drivers/index.rst
nxflat.rst
nxgraphics/index.rst
diff --git a/Documentation/components/iterable_sections.rst b/Documentation/components/iterable_sections.rst
new file mode 100644
index 0000000000000..4659d4ad166ae
--- /dev/null
+++ b/Documentation/components/iterable_sections.rst
@@ -0,0 +1,170 @@
+=================
+Iterable Sections
+=================
+
+Iterable sections provide **link-time registration** of ``struct``
+instances: an instance defined with :c:macro:`STRUCT_SECTION_ITERABLE` in
+any compilation unit is placed in a dedicated linker input section. The
+linker collects all instances into a contiguous, name-sorted array
+delimited by ``__list_start``/``__list_end`` symbols, which
+the code can then iterate like a plain C array -- no runtime registration
+calls, no central list to maintain.
+
+This is the same mechanism used by the Zephyr RTOS ``STRUCT_SECTION_*``
+macros. The first user of this infrastructure is the zbus message bus
+port (``apps/system/zbus``, from nuttx-apps).
+
+C API
+=====
+
+The macros are provided by ``include/nuttx/iterable_sections.h``:
+
+.. code-block:: c
+
+ #include
+
+ struct my_entry
+ {
+ const char *name;
+ int value;
+ };
+
+ /* In any .c file (const places the instance in ROM): */
+
+ const STRUCT_SECTION_ITERABLE(my_entry, entry_foo) =
+ {
+ .name = "foo",
+ .value = 42,
+ };
+
+ /* In the file that iterates: declare the section boundaries once, at
+ * file scope, then loop with a caller-declared pointer.
+ */
+
+ STRUCT_SECTION_DECLARE(my_entry);
+
+ void print_entries(void)
+ {
+ FAR struct my_entry *entry;
+
+ STRUCT_SECTION_FOREACH(my_entry, entry)
+ {
+ printf("%s = %d\n", entry->name, entry->value);
+ }
+ }
+
+Available macros:
+
+* ``STRUCT_SECTION_ITERABLE(type, varname)`` -- define an instance inside
+ the iterable section ``._.static.``. The variable name
+ is part of the input section name, so the linker's ``SORT_BY_NAME()``
+ defines the iteration order (instances may encode ordering in their
+ names).
+* ``STRUCT_SECTION_DECLARE(type)`` -- declare the boundary symbols (file
+ scope), required before iterating.
+* ``STRUCT_SECTION_FOREACH(type, iterator)`` -- for-loop over all
+ instances; ``iterator`` is a pointer declared by the caller, as with
+ ``list_for_every_entry()``.
+* ``STRUCT_SECTION_GET(type, i, dst)`` -- random access by index.
+* ``STRUCT_SECTION_COUNT(type, dst)`` -- number of instances.
+* ``STRUCT_SECTION_START/END/START_EXTERN/END_EXTERN`` -- direct access
+ to the boundary symbols.
+
+Linker integration
+==================
+
+The collection step needs linker script support. Two mechanisms are
+available; both rely on the fact that the linker scripts listed in
+``ARCHSCRIPT`` are preprocessed with CPP (arm, arm64, risc-v, xtensa,
+x86_64 and tricore), so ``#include`` and ``#ifdef CONFIG_*`` work inside
+them.
+
+Board script include (first-class mechanism)
+--------------------------------------------
+
+The board linker script includes the central fragments, which expand to
+nothing unless a subsystem using iterable sections is enabled:
+
+.. code-block:: text
+
+ .text :
+ {
+ ...
+ *(.gnu.linkonce.r.*)
+ #include
+ _etext = ABSOLUTE(.);
+ } > flash
+
+ .data :
+ {
+ _sdata = ABSOLUTE(.);
+ ...
+ #include
+ . = ALIGN(4);
+ _edata = ABSOLUTE(.);
+ } > sram AT > flash
+
+* ``common-rom.ld`` collects the read-only (``const``) iterable sections
+ and must be included inside the read-only output section (typically
+ ``.text``, before ``_etext``).
+* ``common-ram.ld`` collects mutable *initialized* iterable sections and
+ must be included inside ``.data`` (between ``_sdata`` and ``_edata``)
+ so the startup FLASH-to-RAM copy initializes the entries.
+* Subsystems add their sections to these central files, guarded by their
+ Kconfig option (see ``include/nuttx/linker/common-rom.ld`` for the zbus
+ example).
+
+Supplementary INSERT script (zero-touch mode)
+---------------------------------------------
+
+With ``CONFIG_ITERABLE_SECTIONS_LINKER_INSERT`` the central script
+``include/nuttx/linker/common-insert.ld`` is added to the ``ARCHSCRIPT``
+list by ``tools/Config.mk`` and supplements the board script through the
+GNU ld ``INSERT AFTER`` command, so **no board script modification is
+needed**. Subsystems add their fragment (a ``SECTIONS { ... } INSERT
+AFTER .text`` block, see ``include/nuttx/linker/zbus.ld``) to that
+central file, guarded by their Kconfig option.
+
+This mode has constraints, discovered the hard way and worth knowing
+before choosing it:
+
+* GNU ld only (``INSERT`` is not supported by the macOS ld64).
+* The INSERT script must come *before* the board script on the linker
+ command line. Adding it via ``ARCHSCRIPT`` from ``tools/Config.mk``
+ guarantees that, because ``Config.mk`` is included by the board
+ ``Make.defs`` before it appends its own script. (The reversed order
+ fails with ``.text not found for insert``.)
+* GNU ld assigns an INSERTed output section to a ``MEMORY`` region by
+ *attribute matching in declaration order*, not by inheriting the anchor
+ section's region. The ROM/flash region must therefore be the first
+ region compatible with read-only sections. Boards declaring a generic
+ ``rwx`` region at a lower address first (e.g. an ITCM at ``0x0``) are
+ incompatible with this mode and must use the board script include.
+* Giving the inserted section an explicit address is **not** a fix: a
+ section with an explicit address does not consume the memory region,
+ so the next region-allocated section overlaps it.
+
+Alignment rules
+===============
+
+Instances are aligned to the natural alignment of their type
+(``STRUCT_SECTION_ITERABLE`` adds ``__aligned__(__alignof__(type))``), and
+``sizeof`` is always a multiple of ``alignof``, so the collected section
+can be indexed as a plain array with no padding between entries from
+different compilation units. The fragments additionally align the list
+boundaries to 4 bytes.
+
+Adding a new iterable type
+==========================
+
+1. Define the instances with ``STRUCT_SECTION_ITERABLE(mytype, name)``.
+2. Add ``ITERABLE_SECTION(mytype)`` to
+ ``include/nuttx/linker/common-rom.ld`` (const) or ``common-ram.ld``
+ (mutable initialized), guarded by the subsystem Kconfig option.
+3. Iterate with ``STRUCT_SECTION_FOREACH(mytype, it)`` after
+ ``STRUCT_SECTION_DECLARE(mytype);`` at file scope.
+
+Caveat on generated linker scripts: the preprocessed ``.ld.tmp`` files
+only depend on the board script and ``.config``; after editing the
+central fragments during development, remove the ``.tmp`` files (or run
+``make clean``) to force regeneration.
diff --git a/Documentation/platforms/arm/stm32h7/boards/linum-stm32h753bi/index.rst b/Documentation/platforms/arm/stm32h7/boards/linum-stm32h753bi/index.rst
index 9b469c9c80af3..01e0e026c1790 100644
--- a/Documentation/platforms/arm/stm32h7/boards/linum-stm32h753bi/index.rst
+++ b/Documentation/platforms/arm/stm32h7/boards/linum-stm32h753bi/index.rst
@@ -1298,3 +1298,28 @@ This example demonstrates how to use the CAN-FD peripherals can0 and can1 with t
can0 051 [8] 00 11 22 33 44 55 66 77
can0 051 [16] 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
+
+zbus
+----
+
+Enables the zbus message bus library (``apps/system/zbus``, a port of the
+Zephyr zbus — see :doc:`its documentation
+`) with all observer types (listeners,
+subscribers, message subscribers, async listeners, ISR publisher and
+runtime observers), together with its example application::
+
+ nsh> zbus
+ zbus: publishing 5 messages to acc_chan
+ zbus: listener: x=1 y=10 z=100
+ zbus: subscriber: x=1 y=10 z=100
+ ...
+ zbus: done
+
+The cmocka test suite from ``apps/testing/zbus`` is also included and can
+be used to validate the whole zbus API on the board::
+
+ nsh> cmocka_zbus_test
+ [==========] tests: Running 16 test(s).
+ ...
+ [==========] tests: 16 test(s) run.
+ [ PASSED ] 16 test(s).
diff --git a/Kconfig b/Kconfig
index e0cfa1c37bec6..1b1cb0003b76f 100644
--- a/Kconfig
+++ b/Kconfig
@@ -2957,6 +2957,29 @@ config DEBUG_LINK_MAP
and debugging magic section games, and for seeing which
pieces of code get eliminated with DEBUG_OPT_UNUSED_SECTIONS.
+config ITERABLE_SECTIONS_LINKER_INSERT
+ bool "Collect iterable sections through a supplementary INSERT linker script"
+ default n
+ depends on ARCH_TOOLCHAIN_GNU
+ ---help---
+ Zero-touch mode for link-time iterable sections
+ (include/nuttx/iterable_sections.h): instead of the board linker
+ script including , the supplementary
+ script is added to ARCHSCRIPT and
+ supplements the board script with GNU ld "INSERT AFTER .text";
+ subsystems add their INSERT fragments to that central file.
+
+ Leave disabled for boards whose linker script already includes
+ the common fragments.
+
+ Constraints: requires GNU ld, a board script with an output
+ section named ".text", and a MEMORY layout where the ROM/flash
+ region is the first region compatible with read-only sections
+ (GNU ld assigns INSERTed sections to a region by attribute
+ matching, in declaration order). Boards declaring a generic
+ rwx region at a lower address first (e.g. ITCM at 0x0) must use
+ the common-rom.ld include instead.
+
config CCACHE
bool "Use ccache"
default n
diff --git a/boards/arm/stm32h7/linum-stm32h753bi/configs/zbus/defconfig b/boards/arm/stm32h7/linum-stm32h753bi/configs/zbus/defconfig
new file mode 100644
index 0000000000000..ea87116489927
--- /dev/null
+++ b/boards/arm/stm32h7/linum-stm32h753bi/configs/zbus/defconfig
@@ -0,0 +1,74 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed .config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that includes your
+# modifications.
+#
+# CONFIG_STANDARD_SERIAL is not set
+CONFIG_ALLOW_MIT_COMPONENTS=y
+CONFIG_ARCH="arm"
+CONFIG_ARCH_BOARD="linum-stm32h753bi"
+CONFIG_ARCH_BOARD_LINUM_STM32H753BI=y
+CONFIG_ARCH_CHIP="stm32h7"
+CONFIG_ARCH_CHIP_STM32=y
+CONFIG_ARCH_CHIP_STM32H753BI=y
+CONFIG_ARCH_CHIP_STM32H7=y
+CONFIG_ARCH_CHIP_STM32H7_CORTEXM7=y
+CONFIG_ARCH_INTERRUPTSTACK=2048
+CONFIG_ARCH_SETJMP_H=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_ARMV7M_DCACHE=y
+CONFIG_ARMV7M_DCACHE_WRITETHROUGH=y
+CONFIG_ARMV7M_DTCM=y
+CONFIG_ARMV7M_ICACHE=y
+CONFIG_BOARD_LOOPSPERMSEC=43103
+CONFIG_BUILTIN=y
+CONFIG_DEBUG_FEATURES=y
+CONFIG_DEBUG_SYMBOLS=y
+CONFIG_EXAMPLES_ALARM=y
+CONFIG_EXAMPLES_ZBUS=y
+CONFIG_FS_PROCFS=y
+CONFIG_IDLETHREAD_STACKSIZE=2048
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_INIT_STACKSIZE=4096
+CONFIG_INTELHEX_BINARY=y
+CONFIG_LIBM=y
+CONFIG_LINE_MAX=64
+CONFIG_MM_REGIONS=4
+CONFIG_MQ_MAXMSGSIZE=96
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_DISABLE_IFUPDOWN=y
+CONFIG_NSH_DISABLE_VCONFIG=y
+CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_READLINE=y
+CONFIG_PREALLOC_TIMERS=4
+CONFIG_RAM_SIZE=245760
+CONFIG_RAM_START=0x20010000
+CONFIG_RAW_BINARY=y
+CONFIG_RR_INTERVAL=200
+CONFIG_RTC_ALARM=y
+CONFIG_RTC_DATETIME=y
+CONFIG_RTC_DRIVER=y
+CONFIG_SCHED_CPULOAD_SYSCLK=y
+CONFIG_SCHED_WAITPID=y
+CONFIG_STACK_COLORATION=y
+CONFIG_START_DAY=6
+CONFIG_START_MONTH=12
+CONFIG_START_YEAR=2011
+CONFIG_STM32_PWR=y
+CONFIG_STM32_RTC=y
+CONFIG_STM32_USART1=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_TASK_NAME_SIZE=20
+CONFIG_TESTING_CMOCKA=y
+CONFIG_TESTING_ZBUS=y
+CONFIG_USART1_SERIAL_CONSOLE=y
+CONFIG_ZBUS=y
+CONFIG_ZBUS_ASYNC_LISTENER=y
+CONFIG_ZBUS_CHANNEL_ID=y
+CONFIG_ZBUS_CHANNEL_NAME=y
+CONFIG_ZBUS_CHANNEL_PUBLISH_STATS=y
+CONFIG_ZBUS_MSG_SUBSCRIBER=y
+CONFIG_ZBUS_OBSERVER_NAME=y
+CONFIG_ZBUS_RUNTIME_OBSERVERS=y
diff --git a/boards/arm/stm32h7/linum-stm32h753bi/scripts/flash.ld b/boards/arm/stm32h7/linum-stm32h753bi/scripts/flash.ld
index b726e5c457607..036b6b4c5ebf5 100644
--- a/boards/arm/stm32h7/linum-stm32h753bi/scripts/flash.ld
+++ b/boards/arm/stm32h7/linum-stm32h753bi/scripts/flash.ld
@@ -126,6 +126,7 @@ SECTIONS
*(.got)
*(.gcc_except_table)
*(.gnu.linkonce.r.*)
+#include
_etext = ABSOLUTE(.);
} > flash
@@ -156,6 +157,7 @@ SECTIONS
*(.data .data.*)
*(.gnu.linkonce.d.*)
CONSTRUCTORS
+#include
. = ALIGN(4);
_edata = ABSOLUTE(.);
} > sram AT > flash
diff --git a/include/nuttx/iterable_sections.h b/include/nuttx/iterable_sections.h
new file mode 100644
index 0000000000000..a997d936cc9da
--- /dev/null
+++ b/include/nuttx/iterable_sections.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+ * include/nuttx/iterable_sections.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* Iterable sections: link-time registration of struct instances.
+ *
+ * A struct instance defined with STRUCT_SECTION_ITERABLE() in any
+ * compilation unit is placed in a dedicated input section named
+ * "._.static.". The board linker script collects
+ * these input sections (sorted by name) into a contiguous array delimited
+ * by the __list_start/__list_end symbols by
+ * including (const data, inside the .text or
+ * .rodata output section) and (mutable
+ * initialized data, inside the .data output section, so that the startup
+ * FLASH-to-RAM copy initializes it).
+ *
+ * The collection is only available on architectures whose linker scripts
+ * are preprocessed with CPP (arm, arm64, risc-v, xtensa, x86_64, tricore)
+ * and on boards whose scripts include the common-*.ld fragments.
+ */
+
+#ifndef __INCLUDE_NUTTX_ITERABLE_SECTIONS_H
+#define __INCLUDE_NUTTX_ITERABLE_SECTIONS_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include
+#include
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Define a struct instance inside an iterable section. A "const"
+ * qualifier may be prepended at the point of use to place the instance in
+ * ROM. Each instance is aligned to the natural alignment of its type so
+ * that the collected section can be indexed as a plain C array. The
+ * variable name is part of the input section name so that the linker's
+ * SORT_BY_NAME() defines the iteration order (instances may encode
+ * ordering in their names).
+ */
+
+#define STRUCT_SECTION_ITERABLE(struct_type, varname) \
+ struct struct_type varname \
+ used_data \
+ aligned_data(__alignof__(struct struct_type)) \
+ locate_data("._" #struct_type ".static." #varname)
+
+/* Start/end symbols provided by the linker script fragments */
+
+#define STRUCT_SECTION_START(struct_type) _##struct_type##_list_start
+#define STRUCT_SECTION_END(struct_type) _##struct_type##_list_end
+
+#define STRUCT_SECTION_START_EXTERN(struct_type) \
+ extern struct struct_type STRUCT_SECTION_START(struct_type)[]
+#define STRUCT_SECTION_END_EXTERN(struct_type) \
+ extern struct struct_type STRUCT_SECTION_END(struct_type)[]
+
+/* Declare both boundary symbols of an iterable section. Place it at file
+ * scope (followed by a semicolon) in every file that iterates with
+ * STRUCT_SECTION_FOREACH.
+ */
+
+#define STRUCT_SECTION_DECLARE(struct_type) \
+ STRUCT_SECTION_START_EXTERN(struct_type); \
+ STRUCT_SECTION_END_EXTERN(struct_type)
+
+/* Iterate over every instance of an iterable section. "iterator" is a
+ * pointer variable (FAR struct struct_type *) declared by the caller, as
+ * with list_for_every_entry(); the boundary symbols must be in scope
+ * (STRUCT_SECTION_DECLARE).
+ */
+
+#define STRUCT_SECTION_FOREACH(struct_type, iterator) \
+ for ((iterator) = STRUCT_SECTION_START(struct_type); \
+ (iterator) < STRUCT_SECTION_END(struct_type); \
+ (iterator)++)
+
+/* Get the i-th element of an iterable section (no bounds checking) */
+
+#define STRUCT_SECTION_GET(struct_type, i, dst) \
+ do \
+ { \
+ STRUCT_SECTION_START_EXTERN(struct_type); \
+ *(dst) = &STRUCT_SECTION_START(struct_type)[i]; \
+ } \
+ while (0)
+
+/* Number of elements in an iterable section */
+
+#define STRUCT_SECTION_COUNT(struct_type, dst) \
+ do \
+ { \
+ STRUCT_SECTION_START_EXTERN(struct_type); \
+ STRUCT_SECTION_END_EXTERN(struct_type); \
+ *(dst) = STRUCT_SECTION_END(struct_type) - \
+ STRUCT_SECTION_START(struct_type); \
+ } \
+ while (0)
+
+#endif /* __INCLUDE_NUTTX_ITERABLE_SECTIONS_H */
diff --git a/include/nuttx/linker/common-insert.ld b/include/nuttx/linker/common-insert.ld
new file mode 100644
index 0000000000000..c652ecfe0a1b0
--- /dev/null
+++ b/include/nuttx/linker/common-insert.ld
@@ -0,0 +1,38 @@
+/****************************************************************************
+ * include/nuttx/linker/common-insert.ld
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* Supplementary linker script for the iterable sections "zero-touch" mode
+ * (CONFIG_ITERABLE_SECTIONS_LINKER_INSERT): added to ARCHSCRIPT by
+ * tools/Config.mk, it supplements -- does not replace -- the board linker
+ * script through the GNU ld INSERT command, so boards need no edit.
+ *
+ * Subsystem fragments are included below, each guarded by its Kconfig
+ * option; every fragment provides its own SECTIONS { ... } INSERT AFTER
+ * block. See Documentation/components/iterable_sections.rst for the
+ * constraints of this mode (GNU ld, command-line ordering, MEMORY layout).
+ */
+
+#include
+
+#ifdef CONFIG_ZBUS
+# include
+#endif
diff --git a/include/nuttx/linker/common-ram.ld b/include/nuttx/linker/common-ram.ld
new file mode 100644
index 0000000000000..8145ccb16bfd5
--- /dev/null
+++ b/include/nuttx/linker/common-ram.ld
@@ -0,0 +1,43 @@
+/****************************************************************************
+ * include/nuttx/linker/common-ram.ld
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* Mutable (initialized) iterable sections. Boards opt in by adding,
+ * INSIDE their .data output section (between _sdata and _edata, so the
+ * startup FLASH-to-RAM copy initializes the entries):
+ *
+ * #include
+ *
+ * Every block below is guarded by its subsystem's Kconfig option, so this
+ * file expands to nothing on configurations that do not use iterable
+ * sections (zero binary impact).
+ */
+
+#include
+#include
+
+/* Extension point for subsystems that need initialized RAM iterable
+ * sections; blocks are added below, each guarded by its Kconfig option.
+ */
+
+/* zbus needs no RAM iterable sections: notification masks live in .bss
+ * and are initialized at runtime from ROM-preserved values.
+ */
diff --git a/include/nuttx/linker/common-rom.ld b/include/nuttx/linker/common-rom.ld
new file mode 100644
index 0000000000000..6e4ea2dbafcf0
--- /dev/null
+++ b/include/nuttx/linker/common-rom.ld
@@ -0,0 +1,53 @@
+/****************************************************************************
+ * include/nuttx/linker/common-rom.ld
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* Read-only iterable sections. Boards opt in by adding, INSIDE their
+ * read-only output section (typically .text, before _etext):
+ *
+ * #include
+ *
+ * Every block below is guarded by its subsystem's Kconfig option, so this
+ * file expands to nothing on configurations that do not use iterable
+ * sections (zero binary impact).
+ */
+
+#include
+#include
+
+/* Subsystem blocks are added below, each guarded by its Kconfig option:
+ *
+ * #ifdef CONFIG_MYSUBSYS
+ * ITERABLE_SECTION(mysubsys_entry)
+ * #endif
+ */
+
+/* zbus message bus. In the INSERT mode
+ * (CONFIG_ITERABLE_SECTIONS_LINKER_INSERT) the zbus sections come from
+ * through common-insert.ld instead, so they must
+ * not be emitted here too.
+ */
+
+#if defined(CONFIG_ZBUS) && !defined(CONFIG_ITERABLE_SECTIONS_LINKER_INSERT)
+ITERABLE_SECTION(zbus_channel)
+ITERABLE_SECTION(zbus_observer)
+ITERABLE_SECTION(zbus_channel_observation)
+#endif
diff --git a/include/nuttx/linker/iterable_sections.ld b/include/nuttx/linker/iterable_sections.ld
new file mode 100644
index 0000000000000..433e68e19b9d1
--- /dev/null
+++ b/include/nuttx/linker/iterable_sections.ld
@@ -0,0 +1,43 @@
+/****************************************************************************
+ * include/nuttx/linker/iterable_sections.ld
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* CPP macro emitting the linker statements that collect one iterable
+ * section (see include/nuttx/iterable_sections.h). This file is meant to
+ * be included from linker scripts that are preprocessed with CPP (the
+ * ARCHSCRIPT .tmp rule).
+ *
+ * The macro must be expanded INSIDE an output section (e.g. .text or
+ * .data). KEEP() protects the entries from --gc-sections and
+ * SORT_BY_NAME() defines the iteration order.
+ */
+
+#ifndef __INCLUDE_NUTTX_LINKER_ITERABLE_SECTIONS_LD
+#define __INCLUDE_NUTTX_LINKER_ITERABLE_SECTIONS_LD
+
+#define ITERABLE_SECTION(name) \
+ . = ALIGN(4); \
+ _##name##_list_start = .; \
+ KEEP(*(SORT_BY_NAME(._##name.static.*))); \
+ _##name##_list_end = .; \
+ . = ALIGN(4);
+
+#endif /* __INCLUDE_NUTTX_LINKER_ITERABLE_SECTIONS_LD */
diff --git a/include/nuttx/linker/zbus.ld b/include/nuttx/linker/zbus.ld
new file mode 100644
index 0000000000000..89e1d7bc03422
--- /dev/null
+++ b/include/nuttx/linker/zbus.ld
@@ -0,0 +1,67 @@
+/****************************************************************************
+ * include/nuttx/linker/zbus.ld
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* Supplementary linker script providing the zbus iterable sections without
+ * editing the board linker script ("zero-touch" mode). Included by
+ * , which CONFIG_ITERABLE_SECTIONS_LINKER_INSERT
+ * adds to ARCHSCRIPT (see tools/Config.mk). The INSERT command makes this
+ * script supplement -- not replace -- the board script.
+ *
+ * Constraints of this mode:
+ * - GNU ld only (INSERT is not supported by macOS ld64), and the INSERT
+ * script must come BEFORE the board script on the command line (the
+ * ARCHSCRIPT hook guarantees that ordering).
+ * - The board script must define an output section named ".text".
+ * - The ROM/flash region must be the first MEMORY region compatible
+ * with read-only sections: GNU ld assigns the INSERTed section to a
+ * region by attribute matching in declaration order, so a board that
+ * declares a generic rwx region at a lower address first (e.g. ITCM
+ * at 0x0) would pull these sections into the wrong region. Such
+ * boards must use the include instead.
+ *
+ * Boards that include in their script should
+ * NOT enable the INSERT mode (the common-rom.ld zbus block is disabled
+ * when this mode is selected, so enabling it by mistake is harmless but
+ * pointless).
+ */
+
+SECTIONS
+{
+ .zbus : SUBALIGN(4)
+ {
+ . = ALIGN(4);
+ _zbus_channel_list_start = .;
+ KEEP(*(SORT_BY_NAME(._zbus_channel.static.*)));
+ _zbus_channel_list_end = .;
+
+ . = ALIGN(4);
+ _zbus_observer_list_start = .;
+ KEEP(*(SORT_BY_NAME(._zbus_observer.static.*)));
+ _zbus_observer_list_end = .;
+
+ . = ALIGN(4);
+ _zbus_channel_observation_list_start = .;
+ KEEP(*(SORT_BY_NAME(._zbus_channel_observation.static.*)));
+ _zbus_channel_observation_list_end = .;
+ }
+}
+INSERT AFTER .text;
diff --git a/tools/Config.mk b/tools/Config.mk
index bc5d2ffd54256..ed6b3dac538d7 100644
--- a/tools/Config.mk
+++ b/tools/Config.mk
@@ -861,3 +861,17 @@ LOWERMAP = A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q R
UPPER_CASE = $(call ULMAP,$(UPPERMAP),$(1))
LOWER_CASE = $(call ULMAP,$(LOWERMAP),$(1))
+
+# Iterable sections "zero-touch" mode: supplement the board linker script
+# with the central INSERT fragment (include/nuttx/linker/common-insert.ld,
+# which includes the per-subsystem fragments) instead of requiring the
+# board script to include common-rom.ld.
+#
+# The fragment is added through ARCHSCRIPT (not EXTRALINKCMDS) because GNU
+# ld requires the INSERT script to come BEFORE the script that defines the
+# target section on the command line; this file is included by the board
+# Make.defs before it appends its own script, so the fragment lands first.
+
+ifeq ($(CONFIG_ITERABLE_SECTIONS_LINKER_INSERT),y)
+ ARCHSCRIPT += $(TOPDIR)$(DELIM)include$(DELIM)nuttx$(DELIM)linker$(DELIM)common-insert.ld
+endif