diff --git a/arch/arc/platforms/generic_arc/generic_arc.cmd b/arch/arc/platforms/generic_arc/generic_arc.cmd index 295bbad5a34..18cc9f511c5 100644 --- a/arch/arc/platforms/generic_arc/generic_arc.cmd +++ b/arch/arc/platforms/generic_arc/generic_arc.cmd @@ -34,12 +34,9 @@ Linker script for the Generic ARC BSPs. #define KENTRY _VxMicroStart #endif +#include #include -#define INIT_LEVEL(level) \ - __initconfig##level##_start = .; \ - *(.initconfig##level##.init) \ - /* physical address of RAM */ #ifdef CONFIG_XIP #define ROMABLE_REGION FLASH @@ -133,17 +130,7 @@ SECTIONS { SECTION_PROLOGUE(initlevel, (OPTIONAL),) { - __initconfig_start = .; - INIT_LEVEL(0) - INIT_LEVEL(1) - INIT_LEVEL(2) - INIT_LEVEL(3) - INIT_LEVEL(4) - INIT_LEVEL(5) - INIT_LEVEL(6) - INIT_LEVEL(7) - KEEP(*(SORT_BY_NAME(".initconfig*"))) - __initconfig_end = .; + DEVICE_INIT_SECTIONS() } GROUP_LINK_IN(RAMABLE_REGION) diff --git a/include/arch/arm/cortex_m/scripts/linker.cmd b/include/arch/arm/cortex_m/scripts/linker.cmd index 9d2025243b3..1e4d6ea9720 100644 --- a/include/arch/arm/cortex_m/scripts/linker.cmd +++ b/include/arch/arm/cortex_m/scripts/linker.cmd @@ -27,12 +27,9 @@ Linker script for the Cortex-M3 platform. #include #include +#include #include -#define INIT_LEVEL(level) \ - __initconfig##level##_start = .; \ - *(.initconfig##level##.init) \ - /* physical address of RAM */ #ifdef CONFIG_XIP #define ROMABLE_REGION FLASH @@ -165,17 +162,7 @@ SECTIONS SECTION_PROLOGUE (initlevel, (OPTIONAL),) { - __initconfig_start = .; - INIT_LEVEL(0) - INIT_LEVEL(1) - INIT_LEVEL(2) - INIT_LEVEL(3) - INIT_LEVEL(4) - INIT_LEVEL(5) - INIT_LEVEL(6) - INIT_LEVEL(7) - KEEP(*(SORT_BY_NAME(".initconfig*"))) - __initconfig_end = .; + DEVICE_INIT_SECTIONS() } GROUP_LINK_IN(RAMABLE_REGION) SECTION_PROLOGUE (_k_task_list, (OPTIONAL),) diff --git a/include/arch/x86/linker-common-sections.h b/include/arch/x86/linker-common-sections.h index 9823d48752d..d7d1efd7eff 100644 --- a/include/arch/x86/linker-common-sections.h +++ b/include/arch/x86/linker-common-sections.h @@ -57,10 +57,6 @@ order when programming the MMU. #include -#define INIT_LEVEL(level) \ - __initconfig##level##_start = .; \ - *(.initconfig##level##.init) \ - /* SECTIONS definitions */ SECTIONS { @@ -136,17 +132,7 @@ SECTIONS SECTION_PROLOGUE (initlevel, (OPTIONAL),) { - __initconfig_start = .; - INIT_LEVEL(0) - INIT_LEVEL(1) - INIT_LEVEL(2) - INIT_LEVEL(3) - INIT_LEVEL(4) - INIT_LEVEL(5) - INIT_LEVEL(6) - INIT_LEVEL(7) - KEEP(*(SORT_BY_NAME(".initconfig*"))) - __initconfig_end = .; + DEVICE_INIT_SECTIONS() KEXEC_PGALIGN_PAD(MMU_PAGE_SIZE) } GROUP_LINK_IN(RAM) diff --git a/include/init.h b/include/init.h index 0def3754d50..298d5e886fa 100644 --- a/include/init.h +++ b/include/init.h @@ -21,96 +21,87 @@ #include #include -#define PRE_KERNEL_CORE 0 -#define PRE_KERNEL_EARLY 1 -#define PRE_KERNEL_LATE 2 -#define NANO_EARLY 3 -#define NANO_LATE 4 -#define MICRO_EARLY 5 -#define MICRO_LATE 6 -#define APP_EARLY 7 -#define APP_LATE 8 - -/** @def __define_initconfig - * - * @brief Define an init object - * - * @details This macro declares an init object to be placed in a - * given init level section in the image. This macro should not be used - * directly. - * - * @param cfg_name Name of the config object created with - * DECLARE_DEVICE_INIT_CONFIG() macro that will be referenced by - * init object. - * - * @param id The init level id where the init object will be placed - * in the image. - * - * @param data The pointer to the driver data for the driver instance. - * @sa DECLARE_DEVICE_INIT_CONFIG() - */ -#define __define_initconfig(cfg_name, id, data) \ - static struct device (__initconfig_##cfg_name) __used \ - __attribute__((__section__(".initconfig" #id ".init"))) = { \ - .config = &(config_##cfg_name),\ - .driver_data = data} /* - * There are four distinct init levels, pre_kernel, nano, micro - * and app. Each init level a unique set of restrictions placed on the - * component being initialized within the level. - * pre_kernel: - * At this level no kernel objects or services are available to - * the component. pre_kernel has three phases, core, early and - * late. The core phase is intended for components that rely - * solely on hardware present in the processor/SOC and do *not* - * rely on services from any other component in the system. The - * early phase can be used by components that do *not* need kernel - * services and may rely on components from the core phase. The - * late phase can be used by components that do *not* need kernel - * services and may rely on components from the core and early - * phases. - * nano: - * At this level nano kernel services are available to the - * component. All services provided by the components initialized - * in the pre_kernel are also available. The nano level has an - * early and late phase. Components in the early phase may rely - * on the nano kernel and pre_kernel services. Components in the - * late phase may rely on, nano kernel, pre_kernel services and - * nano_early services - * micro: - * At this level micro kernel, nano kernel and pre_kernel services - * are available to the component. The micor level has an - * early and late phase. Components in the early phase may rely - * on micro kernel, nano kernel and pre_kernel services. - * Components in the late phase may rely on, micro kernel, nano - * kernel, pre_kernel services and micro_early services - * app: - * The app level is not intended for core kernel components but for - * the application developer to add any components that they wish to - * have initialized automatically during kernel initialization. The - * app level is executed as the final init stage in both nanokernel - * and microkernel configurations. The application component may - * rely any component configured into the system. -*/ - -/* Run on pre_kernel stack; no {micro,nano} kernel objects available */ -#define pre_kernel_core_init(cfg, data) __define_initconfig(cfg, 0, data) -#define pre_kernel_early_init(cfg, data) __define_initconfig(cfg, 1, data) -#define pre_kernel_late_init(cfg, data) __define_initconfig(cfg, 2, data) - -/* Run from nano kernel idle task; no micro kernel objects available */ -#define nano_early_init(cfg, data) __define_initconfig(cfg, 3, data) -#define nano_late_init(cfg, data) __define_initconfig(cfg, 4, data) - -/* Run from micro kernel idle task. */ -#define micro_early_init(cfg, data) __define_initconfig(cfg, 5, data) -#define micro_late_init(cfg, data) __define_initconfig(cfg, 6, data) - -/* Run in the idle task; In a nano kernel only system run after - * nano_late_init(). In a micro kernel system after micro_late_init() + * System initialization levels. The PRIMARY and SECONDARY levels are + * executed in the kernel's initialization context, which uses the interrupt + * stack. The remaining levels are executed in the kernel's main task + * (i.e. the nanokernel's background task or the microkernel's idle task). */ -#define app_early_init(cfg, data) __define_initconfig(cfg, 7, data) -#define app_late_init(cfg, data) __define_initconfig(cfg, 8, data) +#define _SYS_INIT_LEVEL_PRIMARY 0 +#define _SYS_INIT_LEVEL_SECONDARY 1 +#define _SYS_INIT_LEVEL_NANOKERNEL 2 +#define _SYS_INIT_LEVEL_MICROKERNEL 3 +#define _SYS_INIT_LEVEL_APPLICATION 4 + + +/** @def SYS_DEFINE_DEVICE + * + * @brief Define device object + * + * @details This macro defines a device object that is automatically + * configured by the kernel during system initialization. + * + * @param name Device name. + * + * @param data Pointer to the device's configuration data. + * @sa DECLARE_DEVICE_INIT_CONFIG() + * + * @param level The initialization level at which configuration occurs. + * Must be one of the following symbols, which are listed in the order + * they are performed by the kernel: + * + * PRIMARY: Used for devices that have no dependencies, such as those + * that rely solely on hardware present in the processor/SOC. These devices + * cannot use any kernel services during configuration, since they are not + * yet available. + * + * SECONDARY: Used for devices that rely on the initialization of devices + * initialized as part of the PRIMARY level. These devices cannot use any + * kernel services during configuration, since they are not yet available. + * + * NANOKERNEL: Used for devices that require nanokernel services during + * configuration. + * + * MICROKERNEL: Used for devices that require microkernel services during + * configuration. + * + * APPLICATION: Used for application components (i.e. non-kernel components) + * that need automatic configuration. These devices can use all services + * provided by the kernel during configuration. + * + * @param priority The initialization priority of the device, relative to + * other devices of the same initialization level. Specified as an integer + * value in the range 0 to 99; lower values indicate earlier initialization. + * Must be expressed as a hard-coded decimal integer literal without leading + * zeroes (e.g. 32); symbolic expressions are @not permitted. + */ + +#define SYS_DEFINE_DEVICE(name, data, level, priority) \ + static struct device (__initconfig_##name) __used \ + __attribute__((__section__(".init_" #level #priority))) = { \ + .config = &(config_##name),\ + .driver_data = data} + +/* The following legacy APIs are provided for backwards compatibility */ + +#define pre_kernel_core_init(cfg, data) \ + SYS_DEFINE_DEVICE(cfg, data, PRIMARY, 0) +#define pre_kernel_early_init(cfg, data) \ + SYS_DEFINE_DEVICE(cfg, data, SECONDARY, 0) +#define pre_kernel_late_init(cfg, data) \ + SYS_DEFINE_DEVICE(cfg, data, SECONDARY, 50) +#define nano_early_init(cfg, data) \ + SYS_DEFINE_DEVICE(cfg, data, NANOKERNEL, 0) +#define nano_late_init(cfg, data) \ + SYS_DEFINE_DEVICE(cfg, data, NANOKERNEL, 50) +#define micro_early_init(cfg, data) \ + SYS_DEFINE_DEVICE(cfg, data, MICROKERNEL, 0) +#define micro_late_init(cfg, data) \ + SYS_DEFINE_DEVICE(cfg, data, MICROKERNEL, 50) +#define app_early_init(cfg, data) \ + SYS_DEFINE_DEVICE(cfg, data, APPLICATION, 0) +#define app_late_init(cfg, data) \ + SYS_DEFINE_DEVICE(cfg, data, APPLICATION, 50) #endif /* _INIT_H_ */ diff --git a/include/linker-defs.h b/include/linker-defs.h index 21d73e27367..f7c1016630a 100644 --- a/include/linker-defs.h +++ b/include/linker-defs.h @@ -42,6 +42,33 @@ This file may be included by: #endif #ifdef _LINKER + +/* + * generate a symbol to mark the start of the device initialization objects for + * the specified level, then link all of those objects (sorted by priority); + * ensure the objects aren't discarded if there is no direct reference to them + */ + +#define DEVICE_INIT_LEVEL(level) \ + __device_##level##_start = .; \ + KEEP(*(SORT(.init_##level[0-9]))); \ + KEEP(*(SORT(.init_##level[1-9][0-9]))); \ + +/* + * link in device initialization objects for all devices that are automatically + * initialized by the kernel; the objects are sorted in the order they will be + * initialized (i.e. ordered by level, sorted by priority within a level) + */ + +#define DEVICE_INIT_SECTIONS() \ + __device_init_start = .; \ + DEVICE_INIT_LEVEL(PRIMARY) \ + DEVICE_INIT_LEVEL(SECONDARY) \ + DEVICE_INIT_LEVEL(NANOKERNEL) \ + DEVICE_INIT_LEVEL(MICROKERNEL) \ + DEVICE_INIT_LEVEL(APPLICATION) \ + __device_init_end = .; \ + #ifdef CONFIG_X86_32 /* LINKER FILES: defines used by linker script */ /* Should be moved to linker-common-defs.h */ #if defined(CONFIG_XIP) @@ -63,6 +90,7 @@ This file may be included by: #endif #elif defined(_ASMLANGUAGE) + /* Assembly FILES: declaration defined by the linker script */ GDATA(__bss_start) GDATA(__bss_num_words) @@ -72,7 +100,7 @@ GDATA(__data_ram_start) GDATA(__data_num_words) #endif -#else +#else /* ! _ASMLANGUAGE */ #include extern char __bss_start[]; diff --git a/kernel/microkernel/k_init.c b/kernel/microkernel/k_init.c index 9f217f7b368..ed35a2535c9 100644 --- a/kernel/microkernel/k_init.c +++ b/kernel/microkernel/k_init.c @@ -67,8 +67,7 @@ extern int _k_kernel_idle(void); void _main(void) { - _sys_device_do_config_level(NANO_EARLY); - _sys_device_do_config_level(NANO_LATE); + _sys_device_do_config_level(_SYS_INIT_LEVEL_NANOKERNEL); #ifdef CONFIG_BOOT_TIME_MEASUREMENT /* @@ -94,10 +93,8 @@ void _main(void) CONFIG_MICROKERNEL_SERVER_PRIORITY, 0); - _sys_device_do_config_level(MICRO_EARLY); - _sys_device_do_config_level(MICRO_LATE); - _sys_device_do_config_level(APP_EARLY); - _sys_device_do_config_level(APP_LATE); + _sys_device_do_config_level(_SYS_INIT_LEVEL_MICROKERNEL); + _sys_device_do_config_level(_SYS_INIT_LEVEL_APPLICATION); #ifdef CONFIG_WORKLOAD_MONITOR diff --git a/kernel/nanokernel/device.c b/kernel/nanokernel/device.c index 9942e7abfe5..17a5118ab2e 100644 --- a/kernel/nanokernel/device.c +++ b/kernel/nanokernel/device.c @@ -2,36 +2,31 @@ #include #include -extern struct device __initconfig_start[]; -extern struct device __initconfig0_start[]; -extern struct device __initconfig1_start[]; -extern struct device __initconfig2_start[]; -extern struct device __initconfig3_start[]; -extern struct device __initconfig4_start[]; -extern struct device __initconfig5_start[]; -extern struct device __initconfig6_start[]; -extern struct device __initconfig7_start[]; -extern struct device __initconfig_end[]; +extern struct device __device_init_start[]; +extern struct device __device_PRIMARY_start[]; +extern struct device __device_SECONDARY_start[]; +extern struct device __device_NANOKERNEL_start[]; +extern struct device __device_MICROKERNEL_start[]; +extern struct device __device_APPLICATION_start[]; +extern struct device __device_init_end[]; static struct device *config_levels[] = { - __initconfig0_start, - __initconfig1_start, - __initconfig2_start, - __initconfig3_start, - __initconfig4_start, - __initconfig5_start, - __initconfig6_start, - __initconfig7_start, - __initconfig_end, + __device_PRIMARY_start, + __device_SECONDARY_start, + __device_NANOKERNEL_start, + __device_MICROKERNEL_start, + __device_APPLICATION_start, + __device_init_end, }; /** - * @brief Execute all the driver init functions at a given level + * @brief Execute all the device initialization functions at a given level * - * @details Driver init objects are created with the - * __define_initconfig() macro and are placed in RAM by the linker - * script. The {nano|micro}kernel code will execute the init level at - * the appropriate time. + * @details Invokes the initialization routine for each device object + * created by the SYS_DEFINE_DEVICE() macro using the specified level. + * The linker script places the device 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. */ diff --git a/kernel/nanokernel/nano_init.c b/kernel/nanokernel/nano_init.c index 5f1358816de..e06f8894dfd 100644 --- a/kernel/nanokernel/nano_init.c +++ b/kernel/nanokernel/nano_init.c @@ -108,10 +108,8 @@ extern void _Ctors(void); static void _main(void) { - _sys_device_do_config_level(NANO_EARLY); - _sys_device_do_config_level(NANO_LATE); - _sys_device_do_config_level(APP_EARLY); - _sys_device_do_config_level(APP_EARLY); + _sys_device_do_config_level(_SYS_INIT_LEVEL_NANOKERNEL); + _sys_device_do_config_level(_SYS_INIT_LEVEL_APPLICATION); extern void main(void); main(); @@ -259,9 +257,8 @@ FUNC_NORETURN void _Cstart(void) /* perform basic hardware initialization */ - _sys_device_do_config_level(PRE_KERNEL_CORE); - _sys_device_do_config_level(PRE_KERNEL_EARLY); - _sys_device_do_config_level(PRE_KERNEL_LATE); + _sys_device_do_config_level(_SYS_INIT_LEVEL_PRIMARY); + _sys_device_do_config_level(_SYS_INIT_LEVEL_SECONDARY); /* * Initialize random number generator