Zephyr 启动流程¶
Zephyr OS 是一个基于小型内核设计的 RTOS,面向资源受限的嵌入式系统。 其应用场景从简单的嵌入式环境传感器、LED可穿戴设备,到复杂的嵌入式控制器、 智能手表以及 IOT 无线应用。
此笔记概要地分析了 Zephyr 的启动流程。
reset.S 汇编初始化¶
reset.S 汇编文件为芯片复位后执行入口,执行一些需要汇编介入的最底层的操作,然后跳转到 z_arm_prep_c
/*
* The entry point is located at the z_arm_reset symbol, which
* is fetched by a XIP image playing the role of a bootloader, which jumps to
* it, not through the reset vector mechanism. Such bootloaders might want to
* search for a __start symbol instead, so create that alias here.
*/
SECTION_SUBSEC_FUNC(TEXT,_reset_section,__start)
#if defined(CONFIG_PLATFORM_SPECIFIC_INIT)
bl z_platform_init
#endif
/* lock interrupts: will get unlocked when switch to main task */
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
cpsid i
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
movs.n r0, #_EXC_IRQ_DEFAULT_PRIO
msr BASEPRI, r0
#else
#error Unknown ARM architecture
#endif
#ifdef CONFIG_WDOG_INIT
/* board-specific watchdog initialization is necessary */
bl z_arm_watchdog_init
#endif
/*
*
* Note: in all Cortex-M variants the interrupt stack area is at
* least equal to CONFIG_ISR_STACK_SIZE + MPU_GUARD_ALIGN_AND_SIZE
* (may be larger due to rounding up for stack pointer aligning
* purposes but this is sufficient during initialization).
*/
#ifdef CONFIG_INIT_STACKS
ldr r0, =z_interrupt_stacks
ldr r1, =0xaa
ldr r2, =CONFIG_ISR_STACK_SIZE + MPU_GUARD_ALIGN_AND_SIZE
bl memset
#endif
/*
* Set PSP and use it to boot without using MSP, so that it
* gets set to z_interrupt_stacks during initialization.
*/
ldr r0, =z_interrupt_stacks
ldr r1, =CONFIG_ISR_STACK_SIZE + MPU_GUARD_ALIGN_AND_SIZE
adds r0, r0, r1
msr PSP, r0
mrs r0, CONTROL
movs r1, #2
orrs r0, r1 /* CONTROL_SPSEL_Msk */
msr CONTROL, r0
/*
* When changing the stack pointer, software must use an ISB instruction
* immediately after the MSR instruction. This ensures that instructions
* after the ISB instruction execute using the new stack pointer.
*/
isb
/*
* 'bl' jumps the furthest of the branch instructions that are
* supported on all platforms. So it is used when jumping to z_arm_prep_c
* (even though we do not intend to return).
*/
bl z_arm_prep_c
z_arm_prep_c C环境初始化¶
z_arm_prep_c() 函数同样也是执行一些底层初始化工作,初始化C的运行环境。
/**
*
* @brief Prepare to and run C code
*
* This routine prepares for the execution of and runs C code.
*
* @return N/A
*/
void z_arm_prep_c(void)
{
relocate_vector_table();
#if defined(CONFIG_CPU_HAS_FPU)
z_arm_floating_point_init();
#endif
z_bss_zero();
z_data_copy();
#if defined(CONFIG_ARMV7_R) && defined(CONFIG_INIT_STACKS)
z_arm_init_stacks();
#endif
z_arm_interrupt_init();
z_cstart();
CODE_UNREACHABLE;
}
z_cstart 内核初始化¶
z_cstart() 函数初始化内核相关功能,并且执行 _SYS_INIT_LEVEL_PRE_KERNEL_1 与 _SYS_INIT_LEVEL_PRE_KERNEL_2 级别的初始化工作。
然后初始化调度器,启动main线程(多线程模式)或者跳转到 bg_thread_main(不启用多线程)。
需要注意的是单线程模式现在处于弃用状态,并且如无意外将会在 2.6 版本移除。
此时内核相关功能无法使用,因此主要是一些跟中断相关的较底层代码会在此阶段运行。
/**
*
* @brief Initialize kernel
*
* This routine is invoked when the system is ready to run C code. The
* processor must be running in 32-bit mode, and the BSS must have been
* cleared/zeroed.
*
* @return Does not return
*/
FUNC_NORETURN void z_cstart(void)
{
/* gcov hook needed to get the coverage report.*/
gcov_static_init();
LOG_CORE_INIT();
/* perform any architecture-specific initialization */
arch_kernel_init();
#if defined(CONFIG_MULTITHREADING)
/* Note: The z_ready_thread() call in prepare_multithreading() requires
* a dummy thread even if CONFIG_ARCH_HAS_CUSTOM_SWAP_TO_MAIN=y
*/
struct k_thread dummy_thread;
z_dummy_thread_init(&dummy_thread);
#endif
/* perform basic hardware initialization */
z_sys_init_run_level(_SYS_INIT_LEVEL_PRE_KERNEL_1);
z_sys_init_run_level(_SYS_INIT_LEVEL_PRE_KERNEL_2);
#ifdef CONFIG_STACK_CANARIES
uintptr_t stack_guard;
z_early_boot_rand_get((uint8_t *)&stack_guard, sizeof(stack_guard));
__stack_chk_guard = stack_guard;
__stack_chk_guard <<= 8;
#endif /* CONFIG_STACK_CANARIES */
#ifdef CONFIG_MULTITHREADING
switch_to_main_thread(prepare_multithreading());
#else
#ifdef ARCH_SWITCH_TO_MAIN_NO_MULTITHREADING
/* Custom ARCH-specific routine to switch to main()
* in the case of no multi-threading.
*/
ARCH_SWITCH_TO_MAIN_NO_MULTITHREADING(bg_thread_main,
NULL, NULL, NULL);
#else
bg_thread_main(NULL, NULL, NULL);
/* LCOV_EXCL_START
* We've already dumped coverage data at this point.
*/
irq_lock();
while (true) {
}
/* LCOV_EXCL_STOP */
#endif
#endif /* CONFIG_MULTITHREADING */
/*
* Compiler can't tell that the above routines won't return and issues
* a warning unless we explicitly tell it that control never gets this
* far.
*/
CODE_UNREACHABLE; /* LCOV_EXCL_LINE */
}
bg_thread_main 启动应用层线程¶
bg_thread_main 将执行 _SYS_INIT_LEVEL_POST_KERNEL 与 _SYS_INIT_LEVEL_APPLICATION 级别的初始化工作,然后跳转到 main()
此时调度器已经运行,并且各种内核服务可用。
大多数驱动会在 _SYS_INIT_LEVEL_POST_KERNEL 阶段初始化,同时也根据紧急程度设定优先级。
如:
#define CONFIG_KERNEL_INIT_PRIORITY_OBJECTS 30
#define CONFIG_KERNEL_INIT_PRIORITY_DEFAULT 40
#define CONFIG_KERNEL_INIT_PRIORITY_DEVICE 50
大多数系统相关服务 _SYS_INIT_LEVEL_APPLICATION,如网络、BSD相关服务、依赖于其他驱动的驱动等。
然后系统将创建与运行定义的线程(多线程模式下),并且进入 main() 循环。
/**
*
* @brief Mainline for kernel's background thread
*
* This routine completes kernel initialization by invoking the remaining
* init functions, then invokes application's main() routine.
*
* @return N/A
*/
static void bg_thread_main(void *unused1, void *unused2, void *unused3)
{
ARG_UNUSED(unused1);
ARG_UNUSED(unused2);
ARG_UNUSED(unused3);
#if defined(CONFIG_BOOT_DELAY) && CONFIG_BOOT_DELAY > 0
static const unsigned int boot_delay = CONFIG_BOOT_DELAY;
#else
static const unsigned int boot_delay;
#endif
z_sys_post_kernel = true;
z_sys_init_run_level(_SYS_INIT_LEVEL_POST_KERNEL);
#if CONFIG_STACK_POINTER_RANDOM
z_stack_adjust_initialized = 1;
#endif
if (boot_delay > 0 && IS_ENABLED(CONFIG_MULTITHREADING)) {
printk("***** delaying boot " STRINGIFY(CONFIG_BOOT_DELAY)
"ms (per build configuration) *****\n");
k_busy_wait(CONFIG_BOOT_DELAY * USEC_PER_MSEC);
}
#if defined(CONFIG_BOOT_BANNER)
#ifdef BUILD_VERSION
printk("*** Booting Zephyr OS build %s %s ***\n",
STRINGIFY(BUILD_VERSION), BOOT_DELAY_BANNER);
#else
printk("*** Booting Zephyr OS version %s %s ***\n",
KERNEL_VERSION_STRING, BOOT_DELAY_BANNER);
#endif
#endif
#ifdef CONFIG_CPLUSPLUS
/* Process the .ctors and .init_array sections */
extern void __do_global_ctors_aux(void);
extern void __do_init_array_aux(void);
__do_global_ctors_aux();
__do_init_array_aux();
#endif
/* Final init level before app starts */
z_sys_init_run_level(_SYS_INIT_LEVEL_APPLICATION);
z_init_static_threads();
#ifdef CONFIG_SMP
z_smp_init();
z_sys_init_run_level(_SYS_INIT_LEVEL_SMP);
#endif
#ifdef CONFIG_BOOT_TIME_MEASUREMENT
z_timestamp_main = k_cycle_get_32();
#endif
extern void main(void);
main();
/* Mark nonessenrial since main() has no more work to do */
z_main_thread.base.user_options &= ~K_ESSENTIAL;
#ifdef CONFIG_COVERAGE_DUMP
/* Dump coverage data once the main() has exited. */
gcov_coverage_dump();
#endif
} /* LCOV_EXCL_LINE ... because we just dumped final coverage data */