diff --git a/cmake/linker_script/common/common-rom.cmake b/cmake/linker_script/common/common-rom.cmake index e4fa5e6a55d06..a6d3e09bf2644 100644 --- a/cmake/linker_script/common/common-rom.cmake +++ b/cmake/linker_script/common/common-rom.cmake @@ -12,6 +12,7 @@ zephyr_linker_section_obj_level(SECTION init LEVEL APPLICATION) zephyr_linker_section_obj_level(SECTION init LEVEL SMP) zephyr_iterable_section(NAME device NUMERIC KVMA RAM_REGION GROUP RODATA_REGION) +zephyr_iterable_section(NAME service NUMERIC KVMA RAM_REGION GROUP RODATA_REGION) if(CONFIG_GEN_SW_ISR_TABLE AND NOT CONFIG_DYNAMIC_INTERRUPTS) # ld align has been changed to subalign to provide identical behavior scatter vs. ld. diff --git a/include/zephyr/device.h b/include/zephyr/device.h index 07fea3c2d1fc7..152b1257af6b7 100644 --- a/include/zephyr/device.h +++ b/include/zephyr/device.h @@ -1263,7 +1263,6 @@ device_get_dt_nodelabels(const struct device *dev) 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 = NULL, \ .dev = (const struct device *)&DEVICE_NAME_GET(dev_id), \ } diff --git a/include/zephyr/init.h b/include/zephyr/init.h index d7f254c3c51b1..75c9d29092aad 100644 --- a/include/zephyr/init.h +++ b/include/zephyr/init.h @@ -13,6 +13,8 @@ #include #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -65,15 +67,14 @@ struct device; */ struct init_entry { /** - * If the init function belongs to a SYS_INIT, this field stored the - * initialization function, otherwise it is set to NULL. - */ - int (*init_fn)(void); - /** - * If the init entry belongs to a device, this fields stores a - * reference to it, otherwise it is set to NULL. + * An init entry can be about a device or a service, _init_object + * will be used to differentiate depending on relative sections. */ - const struct device *dev; + union { + const void *_init_object; + const struct device *dev; + const struct service *srv; + }; }; /** @cond INTERNAL_HIDDEN */ @@ -164,9 +165,12 @@ struct init_entry { * @see SYS_INIT() */ #define SYS_INIT_NAMED(name, init_fn_, level, prio) \ + Z_SERVICE_DEFINE(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 = (init_fn_), .dev = NULL} \ + Z_INIT_ENTRY_NAME(name) = { \ + .srv = (const struct service *)&Z_SERVICE_NAME_GET(name) \ + } /** @} */ diff --git a/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld b/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld index 656a4b075d99e..2bb7d2c509ce2 100644 --- a/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld +++ b/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld @@ -22,6 +22,8 @@ ITERABLE_SECTION_ROM_NUMERIC(device, Z_LINK_ITERABLE_SUBALIGN) + ITERABLE_SECTION_ROM_NUMERIC(service, Z_LINK_ITERABLE_SUBALIGN) + #if defined(CONFIG_GEN_SW_ISR_TABLE) && defined(CONFIG_SHARED_INTERRUPTS) /* since z_shared_isr() is not referenced anywhere when * zephyr_pre0.elf is built, the linker will end up dropping it. diff --git a/include/zephyr/service.h b/include/zephyr/service.h new file mode 100644 index 0000000000000..cbeaeecefcf6c --- /dev/null +++ b/include/zephyr/service.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024 Tomasz Bursztyka. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_SERVICE_H_ +#define ZEPHYR_INCLUDE_SERVICE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup service System service + * @ingroup os_services + * + * A system service is a one-shot initialized software component, instantiated + * via SYS_INIT() or SYS_INIT_NAMED(). It does not provide any public API. + * + * @{ + */ + +/** + * @brief Structure to store service instance + * + * This will be defined through SYS_INIT/SYS_INIT_NAMED + */ +struct service { + /** + * Initialization function + */ + int (*init)(void); +}; + +/** @cond INTERNAL_HIDDEN */ + +/** + * @brief Expands to the name of a global service object + * + * @param name Service name. + * + * @return The full name of the service object defined by the service + * definition macro. + */ +#define Z_SERVICE_NAME_GET(name) _CONCAT(__service_, name) + +/** + * @brief Initializer for @ref service + * + * @param init_fn Initialization function (mandatory). + */ +#define Z_SERVICE_INIT(init_fn) \ + { \ + .init = (init_fn) \ + } + +/** + * @brief Service section name (used for sorting purposes). + * + * @param level Initialization level. + * @param prio Initialization priority. + */ +#define Z_SERVICE_SECTION_NAME(level, prio) \ + _CONCAT(INIT_LEVEL_ORD(level), _##prio) + +/** + * @brief Define a @ref service + * + * @param name Service name. + * @param init_fn Initialization function. + * @param level Initialization level. + * @param prio Initialization priority. + */ +#define Z_SERVICE_DEFINE(name, init_fn, level, prio) \ + static const \ + STRUCT_SECTION_ITERABLE_NAMED_ALTERNATE( \ + service, service, \ + Z_SERVICE_SECTION_NAME(level, prio), \ + Z_SERVICE_NAME_GET(name)) = Z_SERVICE_INIT(init_fn) + +/** @endcond */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_SERVICE_H_ */ diff --git a/kernel/init.c b/kernel/init.c index dd9f3d7258f58..2ab9804c7cf0e 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -136,6 +136,9 @@ enum init_level { extern const struct init_entry __init_SMP_start[]; #endif /* CONFIG_SMP */ +TYPE_SECTION_START_EXTERN(struct service, service); +TYPE_SECTION_END_EXTERN(struct service, service); + /* * storage space for the interrupt stack * @@ -337,6 +340,12 @@ static int do_device_init(const struct device *dev) return rc; } +static inline bool is_entry_about_service(const void *obj) +{ + return (obj >= (void *)_service_list_start && + obj < (void *)_service_list_end); +} + /** * @brief Execute all the init entry initialization functions at a given level * @@ -365,17 +374,26 @@ static void z_sys_init_run_level(enum init_level level) const struct init_entry *entry; for (entry = levels[level]; entry < levels[level+1]; entry++) { - const struct device *dev = entry->dev; int result = 0; + if (unlikely(entry->_init_object == NULL)) { + continue; + } + sys_trace_sys_init_enter(entry, level); - if (dev != NULL) { + + if (is_entry_about_service(entry->_init_object)) { + const struct service *srv = entry->srv; + + result = srv->init(); + } else { + const struct device *dev = entry->dev; + if ((dev->flags & DEVICE_FLAG_INIT_DEFERRED) == 0U) { result = do_device_init(dev); } - } else { - result = entry->init_fn(); } + sys_trace_sys_init_exit(entry, level, result); } } diff --git a/scripts/build/check_init_priorities.py b/scripts/build/check_init_priorities.py index dc028aa31de28..1ae2772a54948 100755 --- a/scripts/build/check_init_priorities.py +++ b/scripts/build/check_init_priorities.py @@ -202,15 +202,14 @@ def _process_initlevels(self): 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)) + arg_name = self._object_name(self._initlevel_pointer(addr, 0, shidx)) - self.initlevels[level].append(f"{obj}: {arg0_name}({arg1_name})") + self.initlevels[level].append(f"{obj}: {arg_name}") - ordinal = self._device_ord_from_name(arg1_name) + ordinal = self._device_ord_from_name(arg_name) if ordinal: prio = Priority(level, priority) - self.devices[ordinal] = (prio, arg0_name) + self.devices[ordinal] = prio addr += size priority += 1 @@ -256,8 +255,8 @@ def _check_dep(self, dev_ord, dep_ord): self.log.info(f"Ignoring priority: {dev_node._binding.compatible}") return - dev_prio, dev_init = self._obj.devices.get(dev_ord, (None, None)) - dep_prio, dep_init = self._obj.devices.get(dep_ord, (None, None)) + dev_prio = self._obj.devices.get(dev_ord, None) + dep_prio = self._obj.devices.get(dep_ord, None) if not dev_prio or not dep_prio: return @@ -272,12 +271,12 @@ def _check_dep(self, dev_ord, dep_ord): "the devicetree dependencies.") self.errors += 1 self.log.error( - f"{dev_node.path} <{dev_init}> is initialized before its dependency " - f"{dep_node.path} <{dep_init}> ({dev_prio} < {dep_prio})") + f"{dev_node.path} is initialized before its dependency " + f"{dep_node.path} ({dev_prio} < {dep_prio})") else: self.log.info( - f"{dev_node.path} <{dev_init}> {dev_prio} > " - f"{dep_node.path} <{dep_init}> {dep_prio}") + f"{dev_node.path} {dev_prio} > " + f"{dep_node.path} {dep_prio}") def check_edt(self): """Scan through all known devices and validate the init priorities.""" diff --git a/scripts/build/check_init_priorities_test.py b/scripts/build/check_init_priorities_test.py index 16cf9af740650..e249321a62082 100755 --- a/scripts/build/check_init_priorities_test.py +++ b/scripts/build/check_init_priorities_test.py @@ -215,12 +215,8 @@ def test_process_initlevels(self, mock_zilinit, mock_ip, mock_on): 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 @@ -230,14 +226,15 @@ def mock_obj_name(*args): 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)"], + "PRE_KERNEL_2": ["a: __device_dts_ord_11", "b: __device_dts_ord_22"], + "POST_KERNEL": ["c: name_8_0"], "APPLICATION": [], "SMP": [], }) + self.assertDictEqual(obj.devices, { - 11: (check_init_priorities.Priority("PRE_KERNEL_2", 0), "i0"), - 22: (check_init_priorities.Priority("PRE_KERNEL_2", 1), "i1"), + 11: check_init_priorities.Priority("PRE_KERNEL_2", 0), + 22: check_init_priorities.Priority("PRE_KERNEL_2", 1), }) class testValidator(unittest.TestCase): @@ -303,14 +300,14 @@ def test_check(self, mock_vinit): validator._ord2node[2]._binding = None validator._ord2node[2].path = "/2" - validator._obj.devices = {1: (10, "i1"), 2: (20, "i2")} + validator._obj.devices = {1: 10, 2: 20} validator._check_dep(2, 1) validator._check_dep(1, 2) - validator.log.info.assert_called_once_with("/2 20 > /1 10") + validator.log.info.assert_called_once_with("/2 20 > /1 10") validator.log.error.assert_has_calls([ - mock.call("/1 is initialized before its dependency /2 (10 < 20)") + mock.call("/1 is initialized before its dependency /2 (10 < 20)") ]) self.assertEqual(validator.errors, 1) @@ -327,7 +324,7 @@ def test_check_same_prio_assert(self, mock_vinit): validator._ord2node[2]._binding = None validator._ord2node[2].path = "/2" - validator._obj.devices = {1: (10, "i1"), 2: (10, "i2")} + validator._obj.devices = {1: 10, 2: 10,} with self.assertRaises(ValueError): validator._check_dep(1, 2) diff --git a/subsys/net/lib/sockets/sockets_service.c b/subsys/net/lib/sockets/sockets_service.c index aac226f64f74e..cb9fc30674534 100644 --- a/subsys/net/lib/sockets/sockets_service.c +++ b/subsys/net/lib/sockets/sockets_service.c @@ -28,7 +28,7 @@ static K_CONDVAR_DEFINE(wait_start); STRUCT_SECTION_START_EXTERN(net_socket_service_desc); STRUCT_SECTION_END_EXTERN(net_socket_service_desc); -static struct service { +static struct service_context { struct zsock_pollfd events[CONFIG_ZVFS_POLL_MAX]; int count; } ctx; diff --git a/tests/misc/check_init_priorities/validate_check_init_priorities_output.py b/tests/misc/check_init_priorities/validate_check_init_priorities_output.py index f5d322164c6f6..7dfbc054f4f29 100755 --- a/tests/misc/check_init_priorities/validate_check_init_priorities_output.py +++ b/tests/misc/check_init_priorities/validate_check_init_priorities_output.py @@ -9,10 +9,10 @@ REFERENCE_OUTPUT = [ "ERROR: Device initialization priority validation failed, the sequence of initialization calls does not match the devicetree dependencies.", - "ERROR: /i2c@11112222/test-i2c-dev@10 is initialized before its dependency /gpio@ffff (PRE_KERNEL_1+0 < PRE_KERNEL_1+1)", - "ERROR: /i2c@11112222/test-i2c-dev@10 is initialized before its dependency /i2c@11112222 (PRE_KERNEL_1+0 < PRE_KERNEL_1+2)", - "INFO: /i2c@11112222/test-i2c-dev@11 PRE_KERNEL_1+3 > /gpio@ffff PRE_KERNEL_1+1", - "INFO: /i2c@11112222/test-i2c-dev@11 PRE_KERNEL_1+3 > /i2c@11112222 PRE_KERNEL_1+2", + "ERROR: /i2c@11112222/test-i2c-dev@10 is initialized before its dependency /gpio@ffff (PRE_KERNEL_1+0 < PRE_KERNEL_1+1)", + "ERROR: /i2c@11112222/test-i2c-dev@10 is initialized before its dependency /i2c@11112222 (PRE_KERNEL_1+0 < PRE_KERNEL_1+2)", + "INFO: /i2c@11112222/test-i2c-dev@11 PRE_KERNEL_1+3 > /gpio@ffff PRE_KERNEL_1+1", + "INFO: /i2c@11112222/test-i2c-dev@11 PRE_KERNEL_1+3 > /i2c@11112222 PRE_KERNEL_1+2", ] if len(sys.argv) != 2: