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 <cfriedt@meta.com>
This commit is contained in:
Christopher Friedt 2023-09-22 15:08:11 -04:00 committed by Carles Cufí
commit afc59112a9
6 changed files with 68 additions and 18 deletions

View file

@ -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_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) zephyr_iterable_section(NAME usbh_class_data GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4)
endif() endif()
if(CONFIG_DEVICE_MUTABLE)
zephyr_iterable_section(NAME device_mutable GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4)
endif()

View file

@ -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.

View file

@ -41,6 +41,10 @@ extern "C" {
*/ */
#define Z_DEVICE_DEPS_ENDS INT16_MAX #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 */ /** @endcond */
/** /**
@ -924,12 +928,13 @@ static inline bool z_impl_device_is_ready(const struct device *dev)
* @param api Reference to device API. * @param api Reference to device API.
* @param ... Optional dependencies, manually specified. * @param ... Optional dependencies, manually specified.
*/ */
#define Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, pm, data, config, level, \ #define Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, pm, data, config, level, prio, api, state, \
prio, api, state, deps) \ deps) \
COND_CODE_1(DT_NODE_EXISTS(node_id), (), (static)) \ COND_CODE_1(DT_NODE_EXISTS(node_id), (), (static)) \
const STRUCT_SECTION_ITERABLE_NAMED(device, \ COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), (), (const)) \
Z_DEVICE_SECTION_NAME(level, prio), \ STRUCT_SECTION_ITERABLE_NAMED_ALTERNATE( \
DEVICE_NAME_GET(dev_id)) = \ 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) Z_DEVICE_INIT(name, pm, data, config, api, state, deps)
/* deprecated device initialization levels */ /* 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 level Initialization level.
* @param prio Initialization priority. * @param prio Initialization priority.
*/ */
#define Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, init_fn_, level, prio) \ #define Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, init_fn_, level, prio) \
Z_DEVICE_LEVEL_CHECK_DEPRECATED_LEVEL(level) \ Z_DEVICE_LEVEL_CHECK_DEPRECATED_LEVEL(level) \
\ \
static const Z_DECL_ALIGN(struct init_entry) __used __noasan \ static const Z_DECL_ALIGN(struct init_entry) __used __noasan Z_INIT_ENTRY_SECTION( \
Z_INIT_ENTRY_SECTION(level, prio, \ level, prio, Z_DEVICE_INIT_SUB_PRIO(node_id)) \
Z_DEVICE_INIT_SUB_PRIO(node_id)) \ Z_INIT_ENTRY_NAME(DEVICE_NAME_GET(dev_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 = (init_fn_)}, \ (init_fn_)}, \
.dev = &DEVICE_NAME_GET(dev_id), \ .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 * 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. * that out until after we've built the zephyr image, though.
*/ */
#define Z_MAYBE_DEVICE_DECLARE_INTERNAL(node_id) \ #define Z_MAYBE_DEVICE_DECLARE_INTERNAL(node_id) \
extern const struct device DEVICE_DT_NAME_GET(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) DT_FOREACH_STATUS_OKAY_NODE(Z_MAYBE_DEVICE_DECLARE_INTERNAL)

View file

@ -73,6 +73,17 @@ union init_function {
* @retval -errno If device initialization fails. * @retval -errno If device initialization fails.
*/ */
int (*dev)(const struct device *dev); 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 * If the init entry belongs to a device, this fields stores a
* reference to it, otherwise it is set to NULL. * 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 */ /** @cond INTERNAL_HIDDEN */

View file

@ -137,6 +137,10 @@
ITERABLE_SECTION_RAM(zbus_channel_observation_mask, 1) ITERABLE_SECTION_RAM(zbus_channel_observation_mask, 1)
#endif /* CONFIG_ZBUS */ #endif /* CONFIG_ZBUS */
#if defined(CONFIG_DEVICE_MUTABLE)
ITERABLE_SECTION_RAM(device_mutable, 4)
#endif
#ifdef CONFIG_USERSPACE #ifdef CONFIG_USERSPACE
_static_kernel_objects_end = .; _static_kernel_objects_end = .;
#endif #endif

View file

@ -1274,6 +1274,13 @@ config DEVICE_DEPS_DYNAMIC
Option that makes it possible to manipulate device dependencies at Option that makes it possible to manipulate device dependencies at
runtime. 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 endmenu
rsource "Kconfig.vm" rsource "Kconfig.vm"