ENTRY(CONFIG_KERNEL_ENTRY)
config KERNEL_ENTRY
string "Kernel entry symbol"
default "__start"
help
Code entry symbol, to be set at linking phase.
虽然通过 ENTRY 指定了入口函数,但是Cortex-M系列处理器并不会查找该符号的位置,使用 ENTRY 只是为了在动态加载程序时可以找到入口,Cortex-M系列处理器是通过中断向量表来确定程序入口。
在 Cortex-M 系列 MCU 中,如果设置为从 flash 启动,flash 前1K 用于存放中断向量表,其中第一个字为程序栈顶指针,第二个字为复位向量,即 ResetHandler 的地址,boot会将第一个字用于初始化栈顶,第二个字作为中保存的地址作为程序跳转地址,从而跳转到程序中运行,详细说明可参考异常模型, 该机制为Cortex-M 系列 MCU 中独有,与此相对的,不同的芯片在系统启动时对可执行程序都有一定的格式要求,需要在程序的头部添加符合操作规范的头。
上电启动后的第一段程序必须符合相应的规范才能被正确执行。
SECTION_SUBSEC_FUNC(TEXT,_reset_section,__start)
#if defined(CONFIG_DEBUG_THREAD_INFO)
/* Clear z_sys_post_kernel flag for RTOS aware debuggers */
movs.n r0, #0
ldr r1, =z_sys_post_kernel
strb r0, [r1]
#endif /* CONFIG_DEBUG_THREAD_INFO */
#if defined(CONFIG_INIT_ARCH_HW_AT_BOOT)
/* 复位CONTROL寄存器,在特权模式和非特权模式均使用MSP,
* 当切换栈指针之后必须使用ISB指令刷新流水线,
* 以保证在ISB之后执行的指令都使用新的栈
*/
movs.n r0, #0
msr CONTROL, r0
isb
#if defined(CONFIG_CPU_CORTEX_M_HAS_SPLIM)
/* 堆栈限制寄存器分别限制 MSP 和 PSP 寄存器可以下降的程度,此处设置为0 */
movs.n r0, #0
msr MSPLIM, r0
msr PSPLIM, r0
#endif /* CONFIG_CPU_CORTEX_M_HAS_SPLIM */
#endif /* CONFIG_INIT_ARCH_HW_AT_BOOT */
#if defined(CONFIG_PM_S2RAM)
/* 低功耗相关初始化 */
bl arch_pm_s2ram_resume
#endif /* CONFIG_PM_S2RAM */
#if defined(CONFIG_PLATFORM_SPECIFIC_INIT)
/* 针对内存,cache,jtag,时钟,中断的一些特殊配置 */
bl z_arm_platform_init
#endif
#if defined(CONFIG_INIT_ARCH_HW_AT_BOOT)
#if defined(CONFIG_CPU_HAS_ARM_MPU)
/* 操作系统未运行之前使用平坦内存模型,所有内存均不受保护,
* 为避免在初始化过程中触发读写保护进入异常,一定要关闭MPU
*/
movs.n r0, #0
ldr r1, =_SCS_MPU_CTRL
str r0, [r1]
dsb
#endif /* CONFIG_CPU_HAS_ARM_MPU */
/* ARM32使用满递减栈,栈顶指针初始时刻指向高地址,
* 将MSP指向 z_main_stack 的末尾,以便后续进行函数调用
*/
ldr r0, =z_main_stack + CONFIG_MAIN_STACK_SIZE
msr msp, r0
/* 清除MPU配置,关闭所有中断,清除被挂起的中断,重置Cache配置等 */
bl z_arm_init_arch_hw_at_boot
#endif /* CONFIG_INIT_ARCH_HW_AT_BOOT */
/* 屏蔽中断 */
#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
/* 开启看门狗 */
bl z_arm_watchdog_init
#endif
#ifdef CONFIG_INIT_STACKS
/* 将栈全部设置为0xaa,可用于监测剩余栈容量 */
ldr r0, =z_interrupt_stacks
ldr r1, =0xaa
ldr r2, =CONFIG_ISR_STACK_SIZE + MPU_GUARD_ALIGN_AND_SIZE
bl z_early_memset
#endif
/* 初始化PSP,将CONTROL的SPSEL位设置为1(在特权模式下使用MSP,非特权模式下使用PSP)
* 后续操作将使用 z_interrupt_stacks 作为栈进行初始化
*/
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
isb
bl z_arm_prep_c
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_ARMV7_A)) && defined(CONFIG_INIT_STACKS))
z_arm_init_stacks();
#endif
z_arm_interrupt_init();
z_cstart();
CODE_UNREACHABLE;
}
FUNC_NO_STACK_PROTECTOR
FUNC_NORETURN void z_cstart(void)
{
/* 代码覆盖率测试相关 */
gcov_static_init();
/* 调用初始化级别为 INIT_LEVEL_EARLY 的函数进行初始化 */
z_sys_init_run_level(INIT_LEVEL_EARLY);
/* z_arm_interrupt_stack_setup 初始化MSP
* z_arm_exc_setup 初始化PENDSV、SysTick等中断的优先级,其中PENDSV优先级为最低
* z_arm_fault_init 初始化 Fault 中断。
* z_arm_cpu_idle_init 初始化 idle 线程
* z_arm_clear_faults 清除所有故障标志
* z_arm_mpu_init 初始化MPU
* z_arm_mmu_init 初始化MMU
*/
arch_kernel_init();
/* 日志初始化 */
LOG_CORE_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
/* 初始化驱动中的静态节点 */
z_device_state_init();
/* 其他的硬件初始化 */
z_sys_init_run_level(INIT_LEVEL_PRE_KERNEL_1);
z_sys_init_run_level(INIT_LEVEL_PRE_KERNEL_2);
#ifdef CONFIG_STACK_CANARIES
/* CONFIG_STACK_CANARIES 用于开启堆栈金丝雀功能,这是一种安全特性,有助于监测堆栈溢出,
* 当启动该功能时,系统启动时会生成一个随机数并保存在 __stack_chk_guard 中,
* 在函数返回之前会检查该值确保它没有被缓冲区溢出所覆盖。
*/
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_TIMING_FUNCTIONS_NEED_AT_BOOT
/* timing_init 函数用于初始化系统计时器 */
timing_init();
timing_start();
#endif
#ifdef CONFIG_MULTITHREADING
/* CONFIG_MULTITHREADING为y时,使用多线程,否则只会有一个 main 线程,
* 默认情况下都启用多线程,通过将 main 线程添加到就绪队列中然后开启任务调度
*/
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 */
}
void arch_switch_to_main_thread(struct k_thread *main_thread, char *stack_ptr,
k_thread_entry_t _main)
{
z_arm_prepare_switch_to_main();
/* 将_current 指向 main_thread, _current 始终指向正在运行的线程 */
_current = main_thread;
#if defined(CONFIG_THREAD_LOCAL_STORAGE) && defined(CONFIG_CPU_CORTEX_M)
/* On Cortex-M, TLS uses a global variable as pointer to
* the thread local storage area. So this needs to point
* to the main thread's TLS area before switching to any
* thread for the first time, as the pointer is only set
* during context switching.
*/
extern uintptr_t z_arm_tls_ptr;
z_arm_tls_ptr = main_thread->tls;
#endif
#ifdef CONFIG_INSTRUMENT_THREAD_SWITCHING
z_thread_mark_switched_in();
#endif
/* the ready queue cache already contains the main thread */
#if defined(CONFIG_MPU_STACK_GUARD) || defined(CONFIG_USERSPACE)
/*
* If stack protection is enabled, make sure to set it
* before jumping to thread entry function
*/
z_arm_configure_dynamic_mpu_regions(main_thread);
#endif
#if defined(CONFIG_BUILTIN_STACK_GUARD)
/* Set PSPLIM register for built-in stack guarding of main thread. */
#if defined(CONFIG_CPU_CORTEX_M_HAS_SPLIM)
/* 堆栈限制寄存器分别限制 MSP 和 PSP 寄存器可以下降的程度,此处设置为main线程的栈起始地址 */
__set_PSPLIM(main_thread->stack_info.start);
#else
#error "Built-in PSP limit checks not supported by HW"
#endif
#endif /* CONFIG_BUILTIN_STACK_GUARD */
/* 设置PSP,然后开启中断,然后跳转到 z_thread_entry,z_thread_entry有四个参数
* 其中_main和stack_ptr作为调用 z_thread_entry 函数的前2个参数,后面两个参数为0
*/
__asm__ volatile (
"mov r0, %0\n\t" /* Store _main in R0 */
#if defined(CONFIG_CPU_CORTEX_M)
"msr PSP, %1\n\t" /* __set_PSP(stack_ptr) */
#endif
"movs r1, #0\n\t"
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) \
|| defined(CONFIG_ARMV7_R) \
|| defined(CONFIG_AARCH32_ARMV8_R) \
|| defined(CONFIG_ARMV7_A)
"cpsie i\n\t" /* __enable_irq() */
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
"cpsie if\n\t" /* __enable_irq(); __enable_fault_irq() */
"msr BASEPRI, r1\n\t" /* __set_BASEPRI(0) */
#else
#error Unknown ARM architecture
#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */
"isb\n\t"
"movs r2, #0\n\t"
"movs r3, #0\n\t"
"bl z_thread_entry\n\t" /* z_thread_entry(_main, 0, 0, 0); */
:
: "r" (_main), "r" (stack_ptr)
: "r0" /* not to be overwritten by msr PSP, %1 */
);
CODE_UNREACHABLE;
}
FUNC_NORETURN void z_thread_entry(k_thread_entry_t entry,
void *p1, void *p2, void *p3)
{
#ifdef CONFIG_THREAD_LOCAL_STORAGE
z_tls_current = z_current_get();
#endif
entry(p1, p2, p3);
k_thread_abort(k_current_get());
/*
* Compiler can't tell that k_thread_abort() won't return and issues a
* warning unless we tell it that control never gets this far.
*/
CODE_UNREACHABLE; /* LCOV_EXCL_LINE */
}
static void bg_thread_main(void *unused1, void *unused2, void *unused3)
{
ARG_UNUSED(unused1);
ARG_UNUSED(unused2);
ARG_UNUSED(unused3);
#ifdef CONFIG_MMU
/* Invoked here such that backing store or eviction algorithms may
* initialize kernel objects, and that all POST_KERNEL and later tasks
* may perform memory management tasks (except for z_phys_map() which
* is allowed at any time)
*/
z_mem_manage_init();
#endif /* CONFIG_MMU */
z_sys_post_kernel = true;
/* 调用优先级为INIT_LEVEL_POST_KERNEL的初始化函数,
* 此时内核已经开始运行,可以使用操作系统API
*/
z_sys_init_run_level(INIT_LEVEL_POST_KERNEL);
#if CONFIG_STACK_POINTER_RANDOM
z_stack_adjust_initialized = 1;
#endif
/* 从控制台输出系统启动标识 */
boot_banner();
#if defined(CONFIG_CPP)
/* 初始化CPP运行环境 */
void z_cpp_init_static(void);
z_cpp_init_static();
#endif
/* 调用优先级为 INIT_LEVEL_APPLICATION 的初始化函数 */
z_sys_init_run_level(INIT_LEVEL_APPLICATION);
/* Zephyr支持静态创建线程,线程对应的信息在编译时确定,
* 随代码一起被编译到程序中,系统启动之后从对应地址将线程的信息从flash中读出,
* 创建并初始化线程并将其添加到就绪队列中等待操作系统调度。
*/
z_init_static_threads();
#ifdef CONFIG_KERNEL_COHERENCE
__ASSERT_NO_MSG(arch_mem_coherent(&_kernel));
#endif
#ifdef CONFIG_SMP
if (!IS_ENABLED(CONFIG_SMP_BOOT_DELAY)) {
z_smp_init();
}
z_sys_init_run_level(INIT_LEVEL_SMP);
#endif
#ifdef CONFIG_MMU
z_mem_manage_boot_finish();
#endif /* CONFIG_MMU */
#ifdef CONFIG_CPP_MAIN
extern int main(void);
#else
extern void main(void);
#endif
/* 跳转到main函数 */
(void)main();
/* Mark nonessential 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
}