Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

devicetree-based device definitions and dependency representations reboot #32127

Merged
merged 5 commits into from
Feb 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,19 @@ if(CONFIG_GEN_ISR_TABLES)
set_property(GLOBAL APPEND PROPERTY GENERATED_KERNEL_SOURCE_FILES isr_tables.c)
endif()

# dev_handles.c is generated from ${ZEPHYR_PREBUILT_EXECUTABLE} by
# gen_handles.py
add_custom_command(
OUTPUT dev_handles.c
COMMAND
${PYTHON_EXECUTABLE}
${ZEPHYR_BASE}/scripts/gen_handles.py
--output-source dev_handles.c
--kernel $<TARGET_FILE:${ZEPHYR_PREBUILT_EXECUTABLE}>
DEPENDS ${ZEPHYR_PREBUILT_EXECUTABLE}
)
set_property(GLOBAL APPEND PROPERTY GENERATED_KERNEL_SOURCE_FILES dev_handles.c)

if(CONFIG_CODE_DATA_RELOCATION)
# @Intent: Linker script to relocate .text, data and .bss sections
toolchain_ld_relocation()
Expand Down
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@
/scripts/pylib/twister/expr_parser.py @nashif
/scripts/schemas/twister/ @nashif
/scripts/gen_app_partitions.py @dcpleung @nashif
/scripts/gen_handles.py @pabigot
/scripts/get_maintainer.py @nashif
/scripts/dts/ @mbolivar-nordic @galak
/scripts/release/ @nashif
Expand Down
22 changes: 19 additions & 3 deletions doc/guides/build/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,16 @@ is skipped.
Final binary
============

In some configurations, the binary from the previous stage is
incomplete, with empty and/or placeholder sections that must be filled
in by, essentially, reflection. When :ref:`usermode_api` is enabled:
The binary from the previous stage is incomplete, with empty and/or
placeholder sections that must be filled in by, essentially, reflection.

Device dependencies
The *gen_handles.py* script scans the first-pass binary to determine
relationships between devices that were recorded from devicetree data,
and replaces the encoded relationships with values that are optimized to
locate the devices actually present in the application.

When :ref:`usermode_api` is enabled:

Kernel object hashing
The *gen_kobject_list.py* scans the *ELF DWARF*
Expand Down Expand Up @@ -202,6 +209,15 @@ The following is a detailed description of the scripts used during the build pro
:start-after: """
:end-before: """

.. _gen_handles.py:

:zephyr_file:`scripts/gen_handles.py`
==========================================

.. include:: ../../../scripts/gen_handles.py
:start-after: """
:end-before: """

.. _gen_kobject_list.py:

:zephyr_file:`scripts/gen_kobject_list.py`
Expand Down
190 changes: 186 additions & 4 deletions include/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,37 @@

#include <init.h>
#include <sys/device_mmio.h>
#include <sys/util.h>

#ifdef __cplusplus
extern "C" {
#endif

/** @brief Type used to represent devices and functions.
*
* The extreme values and zero have special significance. Negative
* values identify functionality that does not correspond to a Zephyr
* device, such as the system clock or a SYS_INIT() function.
*/
typedef int16_t device_handle_t;

/** @brief Flag value used in lists of device handles to separate
* distinct groups.
*
* This is the minimum value for the device_handle_t type.
*/
#define DEVICE_HANDLE_SEP INT16_MIN

/** @brief Flag value used in lists of device handles to indicate the
* end of the list.
*
* This is the maximum value for the device_handle_t type.
*/
#define DEVICE_HANDLE_ENDS INT16_MAX

/** @brief Flag value used to identify an unknown device. */
#define DEVICE_HANDLE_NULL 0

#define Z_DEVICE_MAX_NAME_LEN 48

/**
Expand Down Expand Up @@ -168,11 +194,13 @@ extern "C" {
* used by the driver. Can be NULL.
*/
#define DEVICE_DT_DEFINE(node_id, init_fn, pm_control_fn, \
data_ptr, cfg_ptr, level, prio, api_ptr) \
data_ptr, cfg_ptr, level, prio, \
api_ptr, ...) \
Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_NAME(node_id), \
DT_PROP_OR(node_id, label, ""), init_fn, \
pm_control_fn, \
data_ptr, cfg_ptr, level, prio, api_ptr)
data_ptr, cfg_ptr, level, prio, \
api_ptr, __VA_ARGS__)

/**
* @def DEVICE_DT_INST_DEFINE
Expand Down Expand Up @@ -335,6 +363,14 @@ struct device {
struct device_state * const state;
/** Address of the device instance private data */
void * const data;
/** optional pointer to handles associated with the device.
*
* This encodes a sequence of sets of device handles that have
* some relationship to this node. The individual sets are
* extracted with dedicated API, such as
* device_required_handles_get().
*/
const device_handle_t *const handles;
#ifdef CONFIG_PM_DEVICE
/** Power Management function */
int (*device_pm_control)(const struct device *dev, uint32_t command,
Expand All @@ -344,6 +380,86 @@ struct device {
#endif
};

/**
* @brief Get the handle for a given device
*
* @param dev the device for which a handle is desired.
*
* @return the handle for the device, or DEVICE_HANDLE_NULL if the
* device does not have an associated handle.
*/
static inline device_handle_t
device_handle_get(const struct device *dev)
{
device_handle_t ret = DEVICE_HANDLE_NULL;
extern const struct device __device_start[];

/* TODO: If/when devices can be constructed that are not part of the
* fixed sequence we'll need another solution.
*/
if (dev != NULL) {
ret = 1 + (device_handle_t)(dev - __device_start);
}

return ret;
}

/**
* @brief Get the device corresponding to a handle.
*
* @param dev_handle the device handle
*
* @return the device that has that handle, or a null pointer if @p
* dev_handle does not identify a device.
*/
static inline const struct device *
device_from_handle(device_handle_t dev_handle)
{
extern const struct device __device_start[];
extern const struct device __device_end[];
const struct device *dev = NULL;
size_t numdev = __device_end - __device_start;

if ((dev_handle > 0) && ((size_t)dev_handle < numdev)) {
dev = &__device_start[dev_handle - 1];
}

return dev;
}

/**
* @brief Get the set of handles for devicetree dependencies of this device.
*
* These are the device dependencies inferred from devicetree.
*
* @param dev the device for which dependencies are desired.
*
* @param count pointer to a place to store the number of devices provided at
* the returned pointer. The value is not set if the call returns a null
* pointer. The value may be set to zero.
*
* @return a pointer to a sequence of @p *count device handles, or a null
* pointer if @p dh does not provide dependency information.
*/
static inline const device_handle_t *
device_required_handles_get(const struct device *dev,
size_t *count)
{
const device_handle_t *rv = dev->handles;

if (rv != NULL) {
size_t i = 0;

while ((rv[i] != DEVICE_HANDLE_ENDS)
&& (rv[i] != DEVICE_HANDLE_SEP)) {
++i;
}
*count = i;
}

return rv;
}

/**
* @brief Retrieve the device structure for a driver by name
*
Expand Down Expand Up @@ -776,9 +892,75 @@ static inline int device_pm_put_sync(const struct device *dev) { return -ENOTSUP
*/
#define Z_DEVICE_STATE_NAME(dev_name) _CONCAT(__devstate_, dev_name)

/** Synthesize the name of the object that holds device ordinal and
* dependency data. If the object doesn't come from a devicetree
* node, use dev_name.
*/
#define Z_DEVICE_HANDLE_NAME(node_id, dev_name) \
_CONCAT(__devicehdl_, \
COND_CODE_1(DT_NODE_EXISTS(node_id), \
(node_id), \
(dev_name)))

#define Z_DEVICE_EXTRA_HANDLES(...) \
FOR_EACH_NONEMPTY_TERM(IDENTITY, (,), __VA_ARGS__)

/* Construct objects that are referenced from struct device. These
* include power management and dependency handles.
*/
#define Z_DEVICE_DEFINE_PRE(node_id, dev_name, ...) \
Z_DEVICE_DEFINE_HANDLES(node_id, dev_name, __VA_ARGS__)


/* Initial build provides a record that associates the device object
* with its devicetree ordinal, and provides the dependency ordinals.
* These are provided as weak definitions (to prevent the reference
* from being captured when the original object file is compiled), and
* in a distinct pass1 section (which will be replaced by
* postprocessing).
*
* It is also (experimentally) necessary to provide explicit alignment
* on each object. Otherwise x86-64 builds will introduce padding
* between objects in the same input section in individual object
* files, which will be retained in subsequent links both wasting
* space and resulting in aggregate size changes relative to pass2
* when all objects will be in the same input section.
*
* The build assert will fail if device_handle_t changes size, which
* means the alignment directives in the linker scripts and in
* `gen_handles.py` must be updated.
*/
BUILD_ASSERT(sizeof(device_handle_t) == 2, "fix the linker scripts");
#define Z_DEVICE_DEFINE_HANDLES(node_id, dev_name, ...) \
extern const device_handle_t \
Z_DEVICE_HANDLE_NAME(node_id, dev_name)[]; \
const device_handle_t \
__aligned(sizeof(device_handle_t)) \
__attribute__((__weak__, \
__section__(".__device_handles_pass1"))) \
Z_DEVICE_HANDLE_NAME(node_id, dev_name)[] = { \
COND_CODE_1(DT_NODE_EXISTS(node_id), ( \
DT_DEP_ORD(node_id), \
DT_REQUIRES_DEP_ORDS(node_id) \
), ( \
DEVICE_HANDLE_NULL, \
)) \
DEVICE_HANDLE_SEP, \
Z_DEVICE_EXTRA_HANDLES(__VA_ARGS__) \
DEVICE_HANDLE_ENDS, \
};

#define Z_DEVICE_DEFINE_INIT(node_id, dev_name, pm_control_fn) \
.handles = Z_DEVICE_HANDLE_NAME(node_id, dev_name), \
Z_DEVICE_DEFINE_PM_INIT(dev_name, pm_control_fn)

/* Like DEVICE_DEFINE but takes a node_id AND a dev_name, and trailing
* dependency handles that come from outside devicetree.
*/
#define Z_DEVICE_DEFINE(node_id, dev_name, drv_name, init_fn, pm_control_fn, \
data_ptr, cfg_ptr, level, prio, api_ptr) \
data_ptr, cfg_ptr, level, prio, api_ptr, ...) \
static struct device_state Z_DEVICE_STATE_NAME(dev_name); \
Z_DEVICE_DEFINE_PRE(node_id, dev_name, __VA_ARGS__) \
COND_CODE_1(DT_NODE_EXISTS(node_id), (), (static)) \
const Z_DECL_ALIGN(struct device) \
DEVICE_NAME_GET(dev_name) __used \
Expand All @@ -788,7 +970,7 @@ static inline int device_pm_put_sync(const struct device *dev) { return -ENOTSUP
.api = (api_ptr), \
.state = &Z_DEVICE_STATE_NAME(dev_name), \
.data = (data_ptr), \
Z_DEVICE_DEFINE_PM_INIT(dev_name, pm_control_fn) \
Z_DEVICE_DEFINE_INIT(node_id, dev_name, pm_control_fn) \
}; \
BUILD_ASSERT(sizeof(Z_STRINGIFY(drv_name)) <= Z_DEVICE_MAX_NAME_LEN, \
Z_STRINGIFY(DEVICE_GET_NAME(drv_name)) " too long"); \
Expand Down
11 changes: 11 additions & 0 deletions include/linker/common-rom.ld
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,14 @@
} GROUP_LINK_IN(ROMABLE_REGION)

Z_ITERABLE_SECTION_ROM(tracing_backend, 4)

SECTION_DATA_PROLOGUE(device_handles,,)
{
__device_handles_start = .;
#ifdef LINKER_PASS2
KEEP(*(SORT(.__device_handles_pass2*)));
#else /* LINKER_PASS2 */
KEEP(*(SORT(.__device_handles_pass1*)));
#endif /* LINKER_PASS2 */
__device_handles_end = .;
} GROUP_LINK_IN(ROMABLE_REGION)
4 changes: 4 additions & 0 deletions kernel/include/kernel_offsets.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,9 @@ GEN_ABSOLUTE_SYM(K_THREAD_SIZEOF, sizeof(struct k_thread));
/* size of the device structure. Used by linker scripts */
GEN_ABSOLUTE_SYM(_DEVICE_STRUCT_SIZEOF, sizeof(const struct device));

/* member offsets in the device structure. Used in image post-processing */
GEN_ABSOLUTE_SYM(_DEVICE_STRUCT_HANDLES_OFFSET,
offsetof(struct device, handles));

/* LCOV_EXCL_STOP */
#endif /* ZEPHYR_KERNEL_INCLUDE_KERNEL_OFFSETS_H_ */
Loading