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_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()

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
/** @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)

View file

@ -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 */

View file

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

View file

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