DAY 1
学习一定要利用好三分钟热血来学习。
寄存器级别的操作是stm32工程师熟悉的也是这次linux学习的重点。
u-boot 启动流程详解
通过分析 uboot 的启动流程可以了解 Linux 内核是如何被启动的。
要分析 uboot 的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由
链接脚本来决定的,所以通过链接脚本可以找到程序的入口。
- u-boot.lds 就是u-boot链接文件。 该文件相当于STM32开发中的.sct 分散加载文件。
定义中断向量表 起始地址也是 0X87800000,说明整个 uboot 的起始地址就是 0X87800000
STM32中断向量表也是可设置的,默认是0x80000000
将 arch/arm/cpu/armv7/start.s 编译出来的代码放到中断向量表后面
在 u-boot.lds 中有一些跟地址有关的“变量”需要我们注意一下
“变量”值可以在 u-boot.map 文件中查找,除了__image_copy_start
以外,其他的变量值每次编译的时候可能会变化,如果修改了 uboot 代码、修改了 uboot 配置、选用不同的优化等级等等都会影响到这些值。所以,一切以实际值为准! - u-boot.map 是 uboot 的映射文件,可以从此文件看到某个文件或者函数链接到了哪个地址。STM32也有.map文件。一样的用法。
/*
* Copyright (c) 2004-2008 Texas Instruments
*
* (C) Copyright 2002
* Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
- OUTPUT_FORMAT(“elf32-littlearm”, “elf32-littlearm”, “elf32-littlearm”)
这行定义了输出文件的格式。在这里,它指定了三种可能的格式,但通常它们都设置为相同的格式。在这种情况下,输出文件的格式为elf32-littlearm,这是一个32位的小端(little-endian)ARM ELF格式。 - OUTPUT_ARCH(arm)
这行指定了目标架构为ARM。 - ENTRY(_start)
这指定了程序的入口点(即程序开始执行的地方)为_start符号。
从 u-boot.lds 中我们已经知道了入口点是 arch/arm/lib/vectors.S 文件中的_start,stm32类似是Reset_Handler的调用
_start代码如下:`
48 _start:
49
50 #ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
51 .word CONFIG_SYS_DV_NOR_BOOT_CFG
52 #endif
53
54 b reset
55 ldr pc, _undefined_instruction
56 ldr pc, _software_interrupt
57 ldr pc, _prefetch_abort
58 ldr pc, _data_abort
59 ldr pc, _not_used
60 ldr pc, _irq
61 ldr pc, _fiq`
- 第 48 行_start 开始的是中断向量表,其中 54~61 行就是中断向量表 与STM32 startup_stm32xx.s里面一样。54 行跳转到 reset 函数里面,reset 函数在 arch/arm/cpu/armv7/start.S 里面,代码如下:
32 .globl reset
33 .globl save_boot_params_ret
34
35 reset:
36 /* Allow the board to save important registers */
37 b save_boot_params
- 第 35 行就是 reset 函数。
- 第 37 行从 reset 函数跳转到了 save_boot_params 函数,而 save_boot_params 函数同样定义
在 start.S 里面,定义如下:
91 /******************************************************************
92 *
93 * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3)
94 * __attribute__((weak));
95 *
96 * Stack pointer is not yet initialized at this moment
97 * Don't save anything to stack even if compiled with -O0
98 *
99 ******************************************************************/
100 ENTRY(save_boot_params)
101 b save_boot_params_ret @ back to my caller
save_boot_params 函数也是只有一句跳转语句,跳转到 save_boot_params_ret 函数,
save_boot_params_ret 函数代码如下:
38 save_boot_params_ret:
39 /*
40 * disable interrupts (FIQ and IRQ), also set the cpu to SVC32
41 * mode, except if in HYP mode already
42 */
43 mrs r0, cpsr
44 and r1, r0, #0x1f @ mask mode bits
45 teq r1, #0x1a @ test for HYP mode
46 bicne r0, r0, #0x1f @ clear all mode bits
47 orrne r0, r0, #0x13 @ set SVC mode
48 orr r0, r0, #0xc0 @ disable FIQ and IRQ
49 msr cpsr,r0
-
第 43 行,读取寄存器 cpsr 中的值,并保存到 r0 寄存器中。
-
第 44 行,将寄存器 r0 中的值与 0X1F 进行与运算,结果保存到 r1 寄存器中,目的就是提取 cpsr 的 bit0~bit4 这 5 位,这 5 位为 M4 M3 M2 M1 M0,M[4:0]这五位用来设置处理器的工作模式,如下所示:
-
第 45 行,判断 r1 寄存器的值是否等于 0X1A(0b11010),也就是判断当前处理器模式是否处于 Hyp 模式。
-
第 46 行,如果 r1 和 0X1A 不相等,也就是 CPU 不处于 Hyp 模式的话就将 r0 寄存器的bit0~5 进行清零,其实就是清除模式位
-
第 47 行,如果处理器不处于 Hyp 模式的话就将 r0 的寄存器的值与 0x13 进行或运算,0x13=0b10011,也就是设置处理器进入 SVC 模式。
-
第 48 行,r0 寄存器的值再与 0xC0 进行或运算,那么 r0 寄存器此时的值就是 0xD3,cpsr的 I 为和 F 位分别控制 IRQ 和 FIQ 这两个中断的开关,设置为 1 就关闭了 FIQ 和 IRQ!
-
第 49 行,将 r0 寄存器写回到 cpsr 寄存器中。完成设置 CPU 处于 SVC32 模式,并且关闭FIQ 和 IRQ 这两个中断