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..e91b2ec593a62
--- /dev/null
+++ b/Documentation/applications/system/zbus/index.rst
@@ -0,0 +1,209 @@
+==========================
+``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_ZBUS_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/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/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/linker/zbus.ld b/include/nuttx/linker/zbus.ld
new file mode 100644
index 0000000000000..d8ee0aa0cc67c
--- /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). Selected by
+ * CONFIG_ZBUS_LINKER_INSERT, which adds this file 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 CONFIG_ZBUS_LINKER_INSERT (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..b86b896bb5346 100644
--- a/tools/Config.mk
+++ b/tools/Config.mk
@@ -861,3 +861,16 @@ 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))
+
+# ZBus iterable sections "zero-touch" mode: supplement the board linker
+# script with the zbus sections via INSERT (see include/nuttx/linker/zbus.ld)
+# 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_ZBUS_LINKER_INSERT),y)
+ ARCHSCRIPT += $(TOPDIR)$(DELIM)include$(DELIM)nuttx$(DELIM)linker$(DELIM)zbus.ld
+endif