Compare commits

...

4 Commits

Author SHA1 Message Date
Gerard Marull-Paretas
9ff30a149d init: remove support for devices
Devices no longer use SYS_INIT infrastructure.

Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
2023-09-21 12:43:04 +00:00
Fabio Baltieri
db747c085f scripts: check_init_priorities: handle init and device decoupling
Fix check_init_priorities.py to handle the decoupling between SYS_INIT
and devices.

Signed-off-by: Fabio Baltieri <fabiobaltieri@google.com>
2023-09-21 12:43:04 +00:00
Gerard Marull-Paretas
c55650c200 [RFC] device: devices no longer depend on SYS_INIT
Background
----------

Nowadays, devices rely on the `init.h` (i.e. `SYS_INIT`) infrastructure
to get initialized automatically. The `init.h` infrastructure is a basic
mechanism that allows to register an init function when the system is
initialized (before `main`). It provides with multiple initialization
levels: `PRE_KERNEL_*`, `POST_KERNEL`, etc., and within each level a
numeric priority (0-99). Using this information, each registered init
entry is sorted by the linker so that the Kernel can later iterate over
them in the correct order. This all sounds nice and simple, but when it
comes to devices, this mechanism has proven to be insufficient.

Before starting with the changes proposed in this patch, let's first dig
into the implementation details of the current model. When devices are
defined, using any of the `DEVICE_*DEFINE` macros, they also create an
init entry using the internal `init.h` APIs (see
`Z_DEVICE_INIT_ENTRY_DEFINE`). This entry, stores a pointer to the
device init call and to the device itself. As the reader can imagine,
this implies a coupling between `init.h` and `device.h`.  The only link
between a device and an init entry is the device pointer stored in the
init entry. This allows the Kernel init machinery to call the init
function with the right device pointer. However, there is no direct
relationship between a device and its init function, that is, `struct
device` does not keep the device init function reference. This is not a
problem nowadays, but it could be a problem if features like deferred
initialization or init/de-init have to be implemented. However, in
reality, this is a _secondary_ problem. The most problematic issue we
have today is that devices are mixed with `SYS_INIT` calls. They are all
part of the same init block, and are treated equally. So for example,
one can theoretically have a system where the init sequence can be like:

```
- PRE_KERNEL_1
  - init call 1
  - device 0
  - init call 2
  - init call 3
  - device 1
  - device 2
  - init call 4
- PRE_KERNEL_2
  - init call 5
  - device 3
  ...
```

This is problematic because:

(1) Init calls can depend on devices, but dependency is not tracked
    anywhere. So the user must check that init priorities are correct to
    avoid runtime failures.
(2) Device drivers can have multiple instances, and each instance may
    require a different set of priorities, while `SYS_INIT` calls are
    singletons.
(3) Devices don't likely need so many init priorities (`SMP`?
    `APPLICATION`?)

(1) is particularly important because init calls do not have a specific
purpose. They usage ranges from SoC init code, to system services. So
it's _unpredictable_ what can happen in there. (2) is a tangential
topic, actually not fixed by this patch, even though it helps. (3) is
more of a post-patch cleanup we need.

So, what does this patch propose...?
------------------------------------

**First, this patch is still a HACK, so please, focus on the description
rather than with the implementation**

So it's about providing devices with their own init infrastructure,
minimizing the coupling with init.h. This patch groups devices in a
separate section, but, keeps the same init levels as before. Therefore,
the list above would look like:

```
/* devices */
- PRE_KERNEL_1
  - device 0
  - device 1
  - device 2
- PRE_KERNEL_2
  - device 3
  ...

/* init calls */
- PRE_KERNEL_1
  - init call 1
  - init call 2
  - init call 3
  - init call 4
- PRE_KERNEL_2
  - init call 5
  ...
```

This means that **it is no longer possible** to mix init calls and
devices within the same level. So how does it work now? Within each
level, devices are initialized first, then init calls. It is done this
way because I believe it is init calls who depend on devices and not
vice versa. I may be wrong, and so the whole proposal would need a
rework. So the init order would look like:

```
- PRE_KERNEL_1
  - device 0
  - device 1
  - device 2
  - init call 1
  - init call 2
  - init call 3
  - init call 4
- PRE_KERNEL_2
  - device 3
  - init call 5
  ...
```

Why does this matter for future development?
--------------------------------------------

First, we have devices grouped at least on a level basis. This means
that we could likely start using devicetree ordinals, by reducing the
source of problems to the init level only. We can also re-think device
init levels (probably init levels as well, hey `PRE_KERNEL_1/2`). For
example, there could be pre-kernel and post-kernel devices only. The
other side effect of this change is that `struct device` stores the
device init call, so we can potentially do things like defer device
initialization or implement things like init/de-init. They all come with
their own complexity, of course, but this patch could be a step forward.

Problems
--------

This is a breaking change. Sorry, guys, another one to the list. The API
does not change, so builds should continue to work. However, the system
changes its behavior, so if anyone was relying on mixed init call/device
sequences, things will break. This is why it is important to analyze
lots of use cases before moving forward with this proposal.

Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
2023-09-21 12:43:04 +00:00
Gerard Marull-Paretas
dac81a202f device: device handles should not be related with SYS_INIT
Device handles are only meant to identify a device, nothing else.
Reserve negative values for any potential future use, though.

Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
2023-09-21 12:43:04 +00:00
7 changed files with 239 additions and 298 deletions

View File

@@ -10,7 +10,6 @@
#include <stdint.h>
#include <zephyr/devicetree.h>
#include <zephyr/init.h>
#include <zephyr/linker/sections.h>
#include <zephyr/sys/device_mmio.h>
#include <zephyr/sys/iterable_sections.h>
@@ -51,9 +50,7 @@ extern "C" {
* than a pointer. The device.h API mainly uses handles to store lists of
* multiple devices in a compact way.
*
* 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.
* The negative, extreme values and zero have special significance.
*
* @see device_handle_get()
* @see device_from_handle()
@@ -323,25 +320,6 @@ typedef int16_t device_handle_t;
#define DEVICE_DECLARE(dev_id) \
static const struct device DEVICE_NAME_GET(dev_id)
/**
* @brief Get a @ref init_entry reference from a devicetree node.
*
* @param node_id A devicetree node identifier
*
* @return A pointer to the @ref init_entry object created for that node
*/
#define DEVICE_INIT_DT_GET(node_id) \
(&Z_INIT_ENTRY_NAME(DEVICE_DT_NAME_GET(node_id)))
/**
* @brief Get a @ref init_entry reference from a device identifier.
*
* @param dev_id Device identifier.
*
* @return A pointer to the init_entry object created for that device
*/
#define DEVICE_INIT_GET(dev_id) (&Z_INIT_ENTRY_NAME(DEVICE_NAME_GET(dev_id)))
/**
* @brief Runtime device dynamic structure (in RAM) per driver instance
*
@@ -388,6 +366,8 @@ struct device {
struct device_state *state;
/** Address of the device instance private data */
void *data;
/** Initialization function */
int (*init)(const struct device *dev);
#if defined(CONFIG_DEVICE_DEPS) || defined(__DOXYGEN__)
/**
* Optional pointer to dependencies associated with the device.
@@ -848,17 +828,6 @@ static inline bool z_impl_device_is_ready(const struct device *dev)
#endif /* CONFIG_DEVICE_DEPS */
/**
* @brief Init sub-priority of the device
*
* The sub-priority is defined by the devicetree ordinal, which ensures that
* multiple drivers running at the same priority level run in an order that
* respects the devicetree dependencies.
*/
#define Z_DEVICE_INIT_SUB_PRIO(node_id) \
COND_CODE_1(DT_NODE_EXISTS(node_id), \
(DT_DEP_ORD_STR_SORTABLE(node_id)), (0))
/**
* @brief Maximum device name length.
*
@@ -880,6 +849,7 @@ static inline bool z_impl_device_is_ready(const struct device *dev)
* @brief Initializer for @ref device.
*
* @param name_ Name of the device.
* @param init_ Device init function.
* @param pm_ Reference to @ref pm_device (optional).
* @param data_ Reference to device data.
* @param config_ Reference to device config.
@@ -887,9 +857,10 @@ static inline bool z_impl_device_is_ready(const struct device *dev)
* @param state_ Reference to device state.
* @param deps_ Reference to device dependencies.
*/
#define Z_DEVICE_INIT(name_, pm_, data_, config_, api_, state_, deps_) \
#define Z_DEVICE_INIT(name_, init_, pm_, data_, config_, api_, state_, deps_) \
{ \
.name = name_, \
.init = init_, \
.config = (config_), \
.api = (api_), \
.state = (state_), \
@@ -904,8 +875,10 @@ static inline bool z_impl_device_is_ready(const struct device *dev)
* @param level Initialization level
* @param prio Initialization priority
*/
#define Z_DEVICE_SECTION_NAME(level, prio) \
_CONCAT(INIT_LEVEL_ORD(level), _##prio)
#define Z_DEVICE_SECTION_NAME(node_id, level, prio) \
_CONCAT(level##_##prio##_, \
COND_CODE_1(DT_NODE_EXISTS(node_id), \
(DT_DEP_ORD_STR_SORTABLE(node_id)), (0)))
/**
* @brief Define a @ref device
@@ -914,6 +887,7 @@ static inline bool z_impl_device_is_ready(const struct device *dev)
* software device).
* @param dev_id Device identifier (used to name the defined @ref device).
* @param name Name of the device.
* @param init_fn Device init function.
* @param pm Reference to @ref pm_device associated with the device.
* (optional).
* @param data Reference to device data.
@@ -923,32 +897,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) \
#define Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_fn, 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), \
Z_DEVICE_SECTION_NAME(node_id, level, prio), \
DEVICE_NAME_GET(dev_id)) = \
Z_DEVICE_INIT(name, pm, data, config, api, state, deps)
/**
* @brief Define the init entry for a device.
*
* @param node_id Devicetree node id for the device (DT_INVALID_NODE if a
* software device).
* @param dev_id Device identifier.
* @param init_fn_ Device init function.
* @param level Initialization level.
* @param prio Initialization priority.
*/
#define Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, init_fn_, level, prio) \
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), \
}
Z_DEVICE_INIT(name, init_fn, pm, data, config, api, state, deps)
/**
* @brief Define a @ref device and all other required objects.
@@ -978,10 +933,9 @@ static inline bool z_impl_device_is_ready(const struct device *dev)
IF_ENABLED(CONFIG_DEVICE_DEPS, \
(Z_DEVICE_DEPS_DEFINE(node_id, dev_id, __VA_ARGS__);)) \
\
Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, pm, data, config, level, \
prio, api, state, Z_DEVICE_DEPS_NAME(dev_id)); \
\
Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, init_fn, level, prio)
Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_fn, pm, data, config, \
level, prio, api, state, \
Z_DEVICE_DEPS_NAME(dev_id))
#if defined(CONFIG_HAS_DTS) || defined(__DOXYGEN__)
/**

View File

@@ -42,81 +42,14 @@ extern "C" {
* SMP.
*
* Initialization priority can take a value in the range of 0 to 99.
*
* @note The same infrastructure is used by devices.
* @{
*/
struct device;
/**
* @brief Initialization function for init entries.
*
* Init entries support both the system initialization and the device
* APIs. Each API has its own init function signature; hence, we have a
* union to cover both.
*/
union init_function {
/**
* System initialization function.
*
* @retval 0 On success
* @retval -errno If init fails.
*/
int (*sys)(void);
/**
* Device initialization function.
*
* @param dev Device instance.
*
* @retval 0 On success
* @retval -errno If device initialization fails.
*/
int (*dev)(const struct device *dev);
};
/**
* @brief Structure to store initialization entry information.
*
* @internal
* Init entries need to be defined following these rules:
*
* - Their name must be set using Z_INIT_ENTRY_NAME().
* - They must be placed in a special init section, given by
* Z_INIT_ENTRY_SECTION().
* - They must be aligned, e.g. using Z_DECL_ALIGN().
*
* See SYS_INIT_NAMED() for an example.
* @endinternal
*/
struct init_entry {
/** Initialization function. */
union init_function init_fn;
/**
* 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;
};
/** System initialization function. */
typedef int (*sys_init_fn_t)(void);
/** @cond INTERNAL_HIDDEN */
/* Helper definitions to evaluate level equality */
#define Z_INIT_EARLY_EARLY 1
#define Z_INIT_PRE_KERNEL_1_PRE_KERNEL_1 1
#define Z_INIT_PRE_KERNEL_2_PRE_KERNEL_2 1
#define Z_INIT_POST_KERNEL_POST_KERNEL 1
#define Z_INIT_APPLICATION_APPLICATION 1
#define Z_INIT_SMP_SMP 1
/* Init level ordinals */
#define Z_INIT_ORD_EARLY 0
#define Z_INIT_ORD_PRE_KERNEL_1 1
#define Z_INIT_ORD_PRE_KERNEL_2 2
#define Z_INIT_ORD_POST_KERNEL 3
#define Z_INIT_ORD_APPLICATION 4
#define Z_INIT_ORD_SMP 5
/**
* @brief Obtain init entry name.
*
@@ -131,29 +64,12 @@ struct init_entry {
* linker scripts to sort them according to the specified
* level/priority/sub-priority.
*/
#define Z_INIT_ENTRY_SECTION(level, prio, sub_prio) \
__attribute__((__section__( \
".z_init_" #level STRINGIFY(prio)"_" STRINGIFY(sub_prio)"_")))
#define Z_INIT_ENTRY_SECTION(level, prio) \
__attribute__( \
(__section__(".z_init_" #level STRINGIFY(prio)"_")))
/** @endcond */
/**
* @brief Obtain the ordinal for an init level.
*
* @param level Init level (EARLY, PRE_KERNEL_1, PRE_KERNEL_2, POST_KERNEL,
* APPLICATION, SMP).
*
* @return Init level ordinal.
*/
#define INIT_LEVEL_ORD(level) \
COND_CODE_1(Z_INIT_EARLY_##level, (Z_INIT_ORD_EARLY), \
(COND_CODE_1(Z_INIT_PRE_KERNEL_1_##level, (Z_INIT_ORD_PRE_KERNEL_1), \
(COND_CODE_1(Z_INIT_PRE_KERNEL_2_##level, (Z_INIT_ORD_PRE_KERNEL_2), \
(COND_CODE_1(Z_INIT_POST_KERNEL_##level, (Z_INIT_ORD_POST_KERNEL), \
(COND_CODE_1(Z_INIT_APPLICATION_##level, (Z_INIT_ORD_APPLICATION), \
(COND_CODE_1(Z_INIT_SMP_##level, (Z_INIT_ORD_SMP), \
(ZERO_OR_COMPILE_ERROR(0)))))))))))))
/**
* @brief Register an initialization function.
*
@@ -180,19 +96,16 @@ struct init_entry {
* same init function.
*
* @param name Unique name for SYS_INIT entry.
* @param init_fn_ See SYS_INIT().
* @param init_fn See SYS_INIT().
* @param level See SYS_INIT().
* @param prio See SYS_INIT().
*
* @see SYS_INIT()
*/
#define SYS_INIT_NAMED(name, init_fn_, level, prio) \
static const Z_DECL_ALIGN(struct init_entry) \
Z_INIT_ENTRY_SECTION(level, prio, 0) __used __noasan \
Z_INIT_ENTRY_NAME(name) = { \
.init_fn = {.sys = (init_fn_)}, \
.dev = NULL, \
}
#define SYS_INIT_NAMED(name, init_fn, level, prio) \
static const Z_DECL_ALIGN(sys_init_fn_t) \
Z_INIT_ENTRY_SECTION(level, prio) __used __noasan \
Z_INIT_ENTRY_NAME(name) = init_fn
/** @} */

View File

@@ -20,7 +20,30 @@
__init_end = .;
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
ITERABLE_SECTION_ROM_NUMERIC(device, 4)
/* HACK: needs a new iterable "sub-section" API */
SECTION_PROLOGUE(device_area,,)
{
_device_list_start = .;
_device_list_EARLY_start = .;
KEEP(*(SORT(._device.static.EARLY_?_*)));
KEEP(*(SORT(._device.static.EARLY_??_*)));
_device_list_PRE_KERNEL_1_start = .;
KEEP(*(SORT(._device.static.PRE_KERNEL_1_?_*)));
KEEP(*(SORT(._device.static.PRE_KERNEL_1_??_*)));
_device_list_PRE_KERNEL_2_start = .;
KEEP(*(SORT(._device.static.PRE_KERNEL_2_?_*)));
KEEP(*(SORT(._device.static.PRE_KERNEL_2_??_*)));
_device_list_POST_KERNEL_start = .;
KEEP(*(SORT(._device.static.POST_KERNEL_?_*)));
KEEP(*(SORT(._device.static.POST_KERNEL_??_*)));
_device_list_APPLICATION_start = .;
KEEP(*(SORT(._device.static.APPLICATION_?_*)));
KEEP(*(SORT(._device.static.APPLICATION_??_*)));
_device_list_SMP_start = .;
KEEP(*(SORT(._device.static.SMP_?_*)));
KEEP(*(SORT(._device.static.SMP_??_*)));
_device_list_end = .;
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
#if defined(CONFIG_GEN_SW_ISR_TABLE) && !defined(CONFIG_DYNAMIC_INTERRUPTS)
SECTION_PROLOGUE(sw_isr_table,,)

View File

@@ -61,13 +61,21 @@ static K_KERNEL_PINNED_STACK_ARRAY_DEFINE(z_idle_stacks,
CONFIG_IDLE_STACK_SIZE);
#endif /* CONFIG_MULTITHREADING */
extern const struct init_entry __init_start[];
extern const struct init_entry __init_EARLY_start[];
extern const struct init_entry __init_PRE_KERNEL_1_start[];
extern const struct init_entry __init_PRE_KERNEL_2_start[];
extern const struct init_entry __init_POST_KERNEL_start[];
extern const struct init_entry __init_APPLICATION_start[];
extern const struct init_entry __init_end[];
extern const sys_init_fn_t __init_start[];
extern const sys_init_fn_t __init_EARLY_start[];
extern const sys_init_fn_t __init_PRE_KERNEL_1_start[];
extern const sys_init_fn_t __init_PRE_KERNEL_2_start[];
extern const sys_init_fn_t __init_POST_KERNEL_start[];
extern const sys_init_fn_t __init_APPLICATION_start[];
extern const sys_init_fn_t __init_end[];
extern const struct device _device_list_EARLY_start[];
extern const struct device _device_list_PRE_KERNEL_1_start[];
extern const struct device _device_list_PRE_KERNEL_2_start[];
extern const struct device _device_list_POST_KERNEL_start[];
extern const struct device _device_list_APPLICATION_start[];
/* FIXME: iterable sections used in device.h do not use const? */
extern struct device _device_list_end[];
enum init_level {
INIT_LEVEL_EARLY = 0,
@@ -81,7 +89,9 @@ enum init_level {
};
#ifdef CONFIG_SMP
extern const struct init_entry __init_SMP_start[];
extern const sys_init_fn_t __init_SMP_start[];
extern const struct device _device_list_SMP_start[];
#endif
/*
@@ -230,19 +240,33 @@ __pinned_bss
bool z_sys_post_kernel;
/**
* @brief Execute all the init entry initialization functions at a given level
* @brief Execute all devices and init entry initialization functions at a given
* level
*
* @details Invokes the initialization routine for each init entry object
* created by the INIT_ENTRY_DEFINE() macro using the specified level.
* The linker script places the init entry objects in memory in the order
* they need to be invoked, with symbols indicating where one level leaves
* off and the next one begins.
* @details Invokes the initialization routine for each device and init entry
* object created by the SYS_INIT() macro using the specified level. Devices are
* initialized first, then init entry objects. The linker script places the init
* entry objects in memory in the order they need to be invoked, with symbols
* indicating where one level leaves off and the next one begins.
*
* @param level init level to run.
*/
static void z_sys_init_run_level(enum init_level level)
{
static const struct init_entry *levels[] = {
static const struct device *dev_levels[] = {
_device_list_EARLY_start,
_device_list_PRE_KERNEL_1_start,
_device_list_PRE_KERNEL_2_start,
_device_list_POST_KERNEL_start,
_device_list_APPLICATION_start,
#ifdef CONFIG_SMP
_device_list_SMP_start,
#endif
/* End marker */
_device_list_end,
};
static const sys_init_fn_t *levels[] = {
__init_EARLY_start,
__init_PRE_KERNEL_1_start,
__init_PRE_KERNEL_2_start,
@@ -254,39 +278,36 @@ static void z_sys_init_run_level(enum init_level level)
/* End marker */
__init_end,
};
const struct init_entry *entry;
for (entry = levels[level]; entry < levels[level+1]; entry++) {
const struct device *dev = entry->dev;
for (const struct device *dev = dev_levels[level]; dev < dev_levels[level+1]; dev++) {
int rc = 0;
if (dev != NULL) {
int rc = 0;
if (entry->init_fn.dev != NULL) {
rc = entry->init_fn.dev(dev);
/* Mark device initialized. If initialization
* failed, record the error condition.
*/
if (rc != 0) {
if (rc < 0) {
rc = -rc;
}
if (rc > UINT8_MAX) {
rc = UINT8_MAX;
}
dev->state->init_res = rc;
if (dev->init != NULL) {
rc = dev->init(dev);
/* Mark device initialized. If initialization failed,
* record the error condition.
*/
if (rc != 0) {
if (rc < 0) {
rc = -rc;
}
if (rc > UINT8_MAX) {
rc = UINT8_MAX;
}
dev->state->init_res = rc;
}
dev->state->initialized = true;
if (rc == 0) {
/* Run automatic device runtime enablement */
(void)pm_device_runtime_auto_enable(dev);
}
} else {
(void)entry->init_fn.sys();
}
dev->state->initialized = true;
if (rc == 0) {
/* Run automatic device runtime enablement */
(void)pm_device_runtime_auto_enable(dev);
}
}
for (const sys_init_fn_t *entry = levels[level]; entry < levels[level+1]; entry++) {
(void)(*entry)();
}
}

View File

@@ -105,8 +105,14 @@ class ZephyrInitLevels:
def __init__(self, file_path):
self.file_path = file_path
self._elf = ELFFile(open(file_path, "rb"))
self.initlevels = {}
for level in _DEVICE_INIT_LEVELS:
self.initlevels[level] = []
self._load_objects()
self._load_level_addr()
self._process_devices()
self._process_initlevels()
def _load_objects(self):
@@ -124,27 +130,37 @@ class ZephyrInitLevels:
self._objects[sym.entry.st_value] = (
sym.name, sym.entry.st_size, sym.entry.st_shndx)
def _load_level_addr(self):
def _find_level_addr(self, prefix, levels):
"""Find the address associated with known init levels."""
self._init_level_addr = {}
addrs = {}
end = None
for section in self._elf.iter_sections():
if not isinstance(section, SymbolTableSection):
continue
for sym in section.iter_symbols():
for level in _DEVICE_INIT_LEVELS:
name = f"__init_{level}_start"
for level in levels:
name = f"{prefix}_{level}_start"
if sym.name == name:
self._init_level_addr[level] = sym.entry.st_value
elif sym.name == "__init_end":
self._init_level_end = sym.entry.st_value
addrs[level] = sym.entry.st_value
elif sym.name == f"{prefix}_end":
end = sym.entry.st_value
if len(self._init_level_addr) != len(_DEVICE_INIT_LEVELS):
raise ValueError(f"Missing init symbols, found: {self._init_level_addr}")
if len(addrs) != len(levels):
raise ValueError(f"Missing level symbols, found: {addrs}")
if not self._init_level_end:
raise ValueError(f"Missing init section end symbol")
if not end:
raise ValueError(f"Missing level section end symbol")
return addrs, end
def _load_level_addr(self):
"""Load the address level for both init and device sections."""
self._init_level_addr, self._init_level_end = self._find_level_addr(
"__init", _DEVICE_INIT_LEVELS)
self._device_level_addr, self._device_level_end = self._find_level_addr(
"_device_list", _DEVICE_INIT_LEVELS)
def _device_ord_from_name(self, sym_name):
"""Find a device ordinal from a symbol name."""
@@ -185,19 +201,16 @@ class ZephyrInitLevels:
return int.from_bytes(data[start:stop], byteorder="little")
def _process_initlevels(self):
"""Process the init level and find the init functions and devices."""
def _process_devices(self):
"""Process the init level and find the devices."""
self.devices = {}
self.initlevels = {}
for i, level in enumerate(_DEVICE_INIT_LEVELS):
start = self._init_level_addr[level]
start = self._device_level_addr[level]
if i + 1 == len(_DEVICE_INIT_LEVELS):
stop = self._init_level_end
stop = self._device_level_end
else:
stop = self._init_level_addr[_DEVICE_INIT_LEVELS[i + 1]]
self.initlevels[level] = []
stop = self._device_level_addr[_DEVICE_INIT_LEVELS[i + 1]]
priority = 0
addr = start
@@ -206,12 +219,11 @@ class ZephyrInitLevels:
raise ValueError(f"no symbol at addr {addr:08x}")
obj, size, shidx = self._objects[addr]
arg0_name = self._object_name(self._initlevel_pointer(addr, 0, shidx))
arg1_name = self._object_name(self._initlevel_pointer(addr, 1, shidx))
init_name = self._object_name(self._initlevel_pointer(addr, 5, shidx))
self.initlevels[level].append(f"{obj}: {arg0_name}({arg1_name})")
self.initlevels[level].append(f"DEVICE {init_name}({obj})")
ordinal = self._device_ord_from_name(arg1_name)
ordinal = self._device_ord_from_name(obj)
if ordinal:
prio = Priority(level, priority)
self.devices[ordinal] = prio
@@ -219,6 +231,27 @@ class ZephyrInitLevels:
addr += size
priority += 1
def _process_initlevels(self):
"""Process the init level and find the init functions."""
for i, level in enumerate(_DEVICE_INIT_LEVELS):
start = self._init_level_addr[level]
if i + 1 == len(_DEVICE_INIT_LEVELS):
stop = self._init_level_end
else:
stop = self._init_level_addr[_DEVICE_INIT_LEVELS[i + 1]]
addr = start
while addr < stop:
if addr not in self._objects:
raise ValueError(f"no symbol at addr {addr:08x}")
_, size, shidx = self._objects[addr]
init_name = self._object_name(self._initlevel_pointer(addr, 0, shidx))
self.initlevels[level].append(f"SYS_INIT {init_name}()")
addr += size
class Validator():
"""Validates the initialization priorities.

View File

@@ -89,7 +89,7 @@ class testZephyrInitLevels(unittest.TestCase):
self.assertDictEqual(obj._objects, {0xaa: ("a", 4, 1), 0xbb: ("b", 8, 2)})
@mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
def test_load_level_addr(self, mock_zilinit):
def test_find_level_addr(self, mock_zilinit):
mock_elf = mock.Mock()
sts = mock.Mock(spec=SymbolTableSection)
@@ -97,40 +97,40 @@ class testZephyrInitLevels(unittest.TestCase):
mock_elf.iter_sections.return_value = [sts, rel]
s0 = mock.Mock()
s0.name = "__init_EARLY_start"
s0.name = "prefix_EARLY_start"
s0.entry.st_value = 0x00
s1 = mock.Mock()
s1.name = "__init_PRE_KERNEL_1_start"
s1.name = "prefix_PRE_KERNEL_1_start"
s1.entry.st_value = 0x11
s2 = mock.Mock()
s2.name = "__init_PRE_KERNEL_2_start"
s2.name = "prefix_PRE_KERNEL_2_start"
s2.entry.st_value = 0x22
s3 = mock.Mock()
s3.name = "__init_POST_KERNEL_start"
s3.name = "prefix_POST_KERNEL_start"
s3.entry.st_value = 0x33
s4 = mock.Mock()
s4.name = "__init_APPLICATION_start"
s4.name = "prefix_APPLICATION_start"
s4.entry.st_value = 0x44
s5 = mock.Mock()
s5.name = "__init_SMP_start"
s5.name = "prefix_SMP_start"
s5.entry.st_value = 0x55
s6 = mock.Mock()
s6.name = "__init_end"
s6.name = "prefix_end"
s6.entry.st_value = 0x66
sts.iter_symbols.return_value = [s0, s1, s2, s3, s4, s5, s6]
obj = check_init_priorities.ZephyrInitLevels("")
obj._elf = mock_elf
obj._load_level_addr()
addrs, end = obj._find_level_addr("prefix", check_init_priorities._DEVICE_INIT_LEVELS)
self.assertDictEqual(obj._init_level_addr, {
self.assertDictEqual(addrs, {
"EARLY": 0x00,
"PRE_KERNEL_1": 0x11,
"PRE_KERNEL_2": 0x22,
@@ -138,7 +138,7 @@ class testZephyrInitLevels(unittest.TestCase):
"APPLICATION": 0x44,
"SMP": 0x55,
})
self.assertEqual(obj._init_level_end, 0x66)
self.assertEqual(end, 0x66)
@mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
def test_device_ord_from_name(self, mock_zilinit):
@@ -191,11 +191,59 @@ class testZephyrInitLevels(unittest.TestCase):
self.assertEqual(obj._initlevel_pointer(0x108, 0, 0), 2)
self.assertEqual(obj._initlevel_pointer(0x108, 1, 0), 3)
@mock.patch("check_init_priorities.ZephyrInitLevels._object_name")
@mock.patch("check_init_priorities.ZephyrInitLevels._initlevel_pointer")
@mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
def test_process_devices(self, mock_zilinit, mock_ip, mock_on):
obj = check_init_priorities.ZephyrInitLevels("")
obj.initlevels = {
"PRE_KERNEL_2": [],
"POST_KERNEL": [],
}
obj._device_level_addr = {
"EARLY": 0x00,
"PRE_KERNEL_1": 0x00,
"PRE_KERNEL_2": 0x00,
"POST_KERNEL": 0x08,
"APPLICATION": 0x0c,
"SMP": 0x0c,
}
obj._device_level_end = 0x0c
obj._objects = {
0x00: ("__device_dts_ord_11", 4, 0),
0x04: ("__device_dts_ord_22", 4, 0),
0x08: ("__device_dts_ord_33", 4, 0),
}
mock_ip.side_effect = lambda *args: args
mock_on.side_effect = lambda *args: f"dev_init_fn_{args[0][0]}"
obj._process_devices()
self.assertDictEqual(obj.initlevels, {
"PRE_KERNEL_2": [
"DEVICE dev_init_fn_0(__device_dts_ord_11)",
"DEVICE dev_init_fn_4(__device_dts_ord_22)",
],
"POST_KERNEL": [
"DEVICE dev_init_fn_8(__device_dts_ord_33)",
],
})
self.assertDictEqual(obj.devices, {
11: check_init_priorities.Priority("PRE_KERNEL_2", 0),
22: check_init_priorities.Priority("PRE_KERNEL_2", 1),
33: check_init_priorities.Priority("POST_KERNEL", 0),
})
@mock.patch("check_init_priorities.ZephyrInitLevels._object_name")
@mock.patch("check_init_priorities.ZephyrInitLevels._initlevel_pointer")
@mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
def test_process_initlevels(self, mock_zilinit, mock_ip, mock_on):
obj = check_init_priorities.ZephyrInitLevels("")
obj.initlevels = {
"PRE_KERNEL_2": [],
"POST_KERNEL": [],
}
obj._init_level_addr = {
"EARLY": 0x00,
"PRE_KERNEL_1": 0x00,
@@ -212,32 +260,18 @@ class testZephyrInitLevels(unittest.TestCase):
}
mock_ip.side_effect = lambda *args: args
def mock_obj_name(*args):
if args[0] == (0, 0, 0):
return "i0"
elif args[0] == (0, 1, 0):
return "__device_dts_ord_11"
elif args[0] == (4, 0, 0):
return "i1"
elif args[0] == (4, 1, 0):
return "__device_dts_ord_22"
return f"name_{args[0][0]}_{args[0][1]}"
mock_on.side_effect = mock_obj_name
mock_on.side_effect = lambda *args: f"init_fn_{args[0][0]}"
obj._process_initlevels()
self.assertDictEqual(obj.initlevels, {
"EARLY": [],
"PRE_KERNEL_1": [],
"PRE_KERNEL_2": ["a: i0(__device_dts_ord_11)", "b: i1(__device_dts_ord_22)"],
"POST_KERNEL": ["c: name_8_0(name_8_1)"],
"APPLICATION": [],
"SMP": [],
})
self.assertDictEqual(obj.devices, {
11: check_init_priorities.Priority("PRE_KERNEL_2", 0),
22: check_init_priorities.Priority("PRE_KERNEL_2", 1),
"PRE_KERNEL_2": [
"SYS_INIT init_fn_0()",
"SYS_INIT init_fn_4()",
],
"POST_KERNEL": [
"SYS_INIT init_fn_8()",
],
})
class testValidator(unittest.TestCase):

View File

@@ -62,43 +62,6 @@ DEVICE_DT_DEFINE(TEST_NOLABEL, dev_init, NULL,
#define DEV_HDL(node_id) device_handle_get(DEVICE_DT_GET(node_id))
#define DEV_HDL_NAME(name) device_handle_get(DEVICE_GET(name))
ZTEST(devicetree_devices, test_init_get)
{
/* Check device pointers */
zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO)->dev,
DEVICE_DT_GET(TEST_GPIO), NULL);
zassert_equal(DEVICE_INIT_DT_GET(TEST_I2C)->dev,
DEVICE_DT_GET(TEST_I2C), NULL);
zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVA)->dev,
DEVICE_DT_GET(TEST_DEVA), NULL);
zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVB)->dev,
DEVICE_DT_GET(TEST_DEVB), NULL);
zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIOX)->dev,
DEVICE_DT_GET(TEST_GPIOX), NULL);
zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVC)->dev,
DEVICE_DT_GET(TEST_DEVC), NULL);
zassert_equal(DEVICE_INIT_DT_GET(TEST_PARTITION)->dev,
DEVICE_DT_GET(TEST_PARTITION), NULL);
zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO_INJECTED)->dev,
DEVICE_DT_GET(TEST_GPIO_INJECTED), NULL);
zassert_equal(DEVICE_INIT_GET(manual_dev)->dev,
DEVICE_GET(manual_dev), NULL);
zassert_equal(DEVICE_INIT_DT_GET(TEST_NOLABEL)->dev,
DEVICE_DT_GET(TEST_NOLABEL), NULL);
/* Check init functions */
zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO)->init_fn.dev, dev_init);
zassert_equal(DEVICE_INIT_DT_GET(TEST_I2C)->init_fn.dev, dev_init);
zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVA)->init_fn.dev, dev_init);
zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVB)->init_fn.dev, dev_init);
zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIOX)->init_fn.dev, dev_init);
zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVC)->init_fn.dev, dev_init);
zassert_equal(DEVICE_INIT_DT_GET(TEST_PARTITION)->init_fn.dev, dev_init);
zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO_INJECTED)->init_fn.dev, dev_init);
zassert_equal(DEVICE_INIT_GET(manual_dev)->init_fn.dev, dev_init);
zassert_equal(DEVICE_INIT_DT_GET(TEST_NOLABEL)->init_fn.dev, dev_init);
}
ZTEST(devicetree_devices, test_init_order)
{
zassert_equal(init_order[0], DEV_HDL(TEST_GPIO));