From afc59112a96c884abf39100b06e89bde4effea29 Mon Sep 17 00:00:00 2001 From: Christopher Friedt Date: Fri, 22 Sep 2023 15:08:11 -0400 Subject: [PATCH] device: support for mutable devices Add support for mutable devices. Mutable devices are those which can be modified after declaration, in-place, in kernel mode. In order for a device to be mutable, the following must be true * `CONFIG_DEVICE_MUTABLE` must be y-selected * the Devicetree bindings for the device must include `mutable.yaml` * the Devicetree node must include the `zephyr,mutable` property Signed-off-by: Christopher Friedt --- cmake/linker_script/common/common-ram.cmake | 4 +++ dts/bindings/base/mutable.yaml | 13 +++++++ include/zephyr/device.h | 40 ++++++++++++--------- include/zephyr/init.h | 18 +++++++++- include/zephyr/linker/common-ram.ld | 4 +++ kernel/Kconfig | 7 ++++ 6 files changed, 68 insertions(+), 18 deletions(-) create mode 100644 dts/bindings/base/mutable.yaml diff --git a/cmake/linker_script/common/common-ram.cmake b/cmake/linker_script/common/common-ram.cmake index a116977ce8c..7e639043bce 100644 --- a/cmake/linker_script/common/common-ram.cmake +++ b/cmake/linker_script/common/common-ram.cmake @@ -128,3 +128,7 @@ if(CONFIG_USB_HOST_STACK) zephyr_iterable_section(NAME usbh_contex GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4) zephyr_iterable_section(NAME usbh_class_data GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4) endif() + +if(CONFIG_DEVICE_MUTABLE) + zephyr_iterable_section(NAME device_mutable GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4) +endif() diff --git a/dts/bindings/base/mutable.yaml b/dts/bindings/base/mutable.yaml new file mode 100644 index 00000000000..0e2d1cad3b0 --- /dev/null +++ b/dts/bindings/base/mutable.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2023, Meta +# SPDX-License-Identifier: Apache-2.0 + +# Properties for Mutable devices + +properties: + zephyr,mutable: + type: boolean + description: | + True iff the device structure may be mutated. + + Inherit this binding for devices that are runtime-modifiable, in-place. + This places the device structure into SRAM rather than Flash. diff --git a/include/zephyr/device.h b/include/zephyr/device.h index 630a059c155..77c0537ffdf 100644 --- a/include/zephyr/device.h +++ b/include/zephyr/device.h @@ -41,6 +41,10 @@ extern "C" { */ #define Z_DEVICE_DEPS_ENDS INT16_MAX +/** @brief Determine if a DT node is mutable */ +#define Z_DEVICE_IS_MUTABLE(node_id) \ + COND_CODE_1(IS_ENABLED(CONFIG_DEVICE_MUTABLE), (DT_PROP(node_id, zephyr_mutable)), (0)) + /** @endcond */ /** @@ -924,12 +928,13 @@ static inline bool z_impl_device_is_ready(const struct device *dev) * @param api Reference to device API. * @param ... Optional dependencies, manually specified. */ -#define Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, pm, data, config, level, \ - prio, api, state, deps) \ - COND_CODE_1(DT_NODE_EXISTS(node_id), (), (static)) \ - const STRUCT_SECTION_ITERABLE_NAMED(device, \ - Z_DEVICE_SECTION_NAME(level, prio), \ - DEVICE_NAME_GET(dev_id)) = \ +#define Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, pm, data, config, level, prio, api, state, \ + deps) \ + COND_CODE_1(DT_NODE_EXISTS(node_id), (), (static)) \ + COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), (), (const)) \ + STRUCT_SECTION_ITERABLE_NAMED_ALTERNATE( \ + device, COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), (device_mutable), (device)), \ + Z_DEVICE_SECTION_NAME(level, prio), DEVICE_NAME_GET(dev_id)) = \ Z_DEVICE_INIT(name, pm, data, config, api, state, deps) /* deprecated device initialization levels */ @@ -961,15 +966,15 @@ static inline bool z_impl_device_is_ready(const struct device *dev) * @param level Initialization level. * @param prio Initialization priority. */ -#define Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, init_fn_, level, prio) \ - Z_DEVICE_LEVEL_CHECK_DEPRECATED_LEVEL(level) \ - \ - static const Z_DECL_ALIGN(struct init_entry) __used __noasan \ - Z_INIT_ENTRY_SECTION(level, prio, \ - Z_DEVICE_INIT_SUB_PRIO(node_id)) \ - Z_INIT_ENTRY_NAME(DEVICE_NAME_GET(dev_id)) = { \ - .init_fn = {.dev = (init_fn_)}, \ - .dev = &DEVICE_NAME_GET(dev_id), \ +#define Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, init_fn_, level, prio) \ + Z_DEVICE_LEVEL_CHECK_DEPRECATED_LEVEL(level) \ + \ + static const Z_DECL_ALIGN(struct init_entry) __used __noasan Z_INIT_ENTRY_SECTION( \ + level, prio, Z_DEVICE_INIT_SUB_PRIO(node_id)) \ + Z_INIT_ENTRY_NAME(DEVICE_NAME_GET(dev_id)) = { \ + .init_fn = {COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), (.dev_rw), (.dev)) = \ + (init_fn_)}, \ + .dev = &DEVICE_NAME_GET(dev_id), \ } /** @@ -1015,8 +1020,9 @@ static inline bool z_impl_device_is_ready(const struct device *dev) * don't have a corresponding @ref device allocated. There's no way to figure * that out until after we've built the zephyr image, though. */ -#define Z_MAYBE_DEVICE_DECLARE_INTERNAL(node_id) \ - extern const struct device DEVICE_DT_NAME_GET(node_id); +#define Z_MAYBE_DEVICE_DECLARE_INTERNAL(node_id) \ + extern COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), (), \ + (const)) struct device DEVICE_DT_NAME_GET(node_id); DT_FOREACH_STATUS_OKAY_NODE(Z_MAYBE_DEVICE_DECLARE_INTERNAL) diff --git a/include/zephyr/init.h b/include/zephyr/init.h index 9b0d2993d62..7882b207b8d 100644 --- a/include/zephyr/init.h +++ b/include/zephyr/init.h @@ -73,6 +73,17 @@ union init_function { * @retval -errno If device initialization fails. */ int (*dev)(const struct device *dev); +#ifdef CONFIG_DEVICE_MUTABLE + /** + * Device initialization function (rw). + * + * @param dev Device instance. + * + * @retval 0 On success + * @retval -errno If device initialization fails. + */ + int (*dev_rw)(struct device *dev); +#endif }; /** @@ -96,7 +107,12 @@ struct init_entry { * If the init entry belongs to a device, this fields stores a * reference to it, otherwise it is set to NULL. */ - const struct device *dev; + union { + const struct device *dev; +#ifdef CONFIG_DEVICE_MUTABLE + struct device *dev_rw; +#endif + }; }; /** @cond INTERNAL_HIDDEN */ diff --git a/include/zephyr/linker/common-ram.ld b/include/zephyr/linker/common-ram.ld index c03351abf98..7bb6e55f1c1 100644 --- a/include/zephyr/linker/common-ram.ld +++ b/include/zephyr/linker/common-ram.ld @@ -137,6 +137,10 @@ ITERABLE_SECTION_RAM(zbus_channel_observation_mask, 1) #endif /* CONFIG_ZBUS */ +#if defined(CONFIG_DEVICE_MUTABLE) + ITERABLE_SECTION_RAM(device_mutable, 4) +#endif + #ifdef CONFIG_USERSPACE _static_kernel_objects_end = .; #endif diff --git a/kernel/Kconfig b/kernel/Kconfig index 4a72d76da67..bd157d8bcc7 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -1274,6 +1274,13 @@ config DEVICE_DEPS_DYNAMIC Option that makes it possible to manipulate device dependencies at runtime. +config DEVICE_MUTABLE + bool "Mutable devices [EXPERIMENTAL]" + select EXPERIMENTAL + help + Support mutable devices. Mutable devices are instantiated in SRAM + instead of Flash and are runtime modifiable in kernel mode. + endmenu rsource "Kconfig.vm"