Linux系统启动流程大致分为三个阶段,uboot,kernel和根文件系统。
uboot的启动又可以分为三个阶段,汇编阶段、C语言阶段和启动内核阶段。
注:
svc模式本身就属于特权模式,本身就可以访问那些受控资源,而且,比sys模式还多了些自己模式下的影子寄存器,所以,相对sys模式来说,可以访问资源的能力相同,但是拥有更多的硬件资源。所以,从理论上来说,虽然可以设置为sys和svc模式的任一种,但是从uboot方面考虑,其要做的事情是初始化系统相关硬件资源,需要获取尽量多的权限,以方便操作硬件,初始化硬件。
从uboot的目的是初始化硬件的角度来说,设置为svc模式,更有利于其工作。因此,此处将CPU设置为SVC模式。
具体函数调用
start.s
// 汇编环境
=> IRQ/FIQ/lowlevel/vbar/errata/cp15/gic // ARM 架构相关的 lowlevel 初始化
=> _main
=> stack // 准备好 C 环境需要的栈
// 【第一阶段】 C 环境初始化,发起一系列的函数调用
=> board_init_f: init_sequence_f[]
initf_malloc
arch_cpu_init // SoC 的 lowlevel 初始化
serial_init // 串口初始化
dram_init // 获取 ddr 容量信息
reserve_mmu // 从 ddr 末尾开始往低地址 reserve 内存
reserve_video
reserve_uboot
reserve_malloc
reserve_global_data
reserve_fdt
reserve_stacks
dram_init_banksize
sysmem_init
setup_reloc // 确定 U-Boot 自身要 reloc 的地址
// 汇编环境
=> relocate_code // 汇编实现 U-Boot 代码的 relocation
// 【第二阶段】 C 环境初始化,发起一系列的函数调用
=> board_init_r: init_sequence_r[]
initr_caches // 使能 MMU 和 I/Dcache
initr_malloc
bidram_initr
sysmem_initr
initr_of_live // 初始化of_live
initr_dm // 初始化dm框架
board_init // 平台初始化,最核心部分
board_debug_uart_init // 串口 iomux、clk 配置
init_kernel_dtb // 切到 kernel dtb
clks_probe // 初始化系统频率
regulators_enable_boot_on // 初始化系统电源
io_domain_init // io-domain 初始化
set_armclk_rate // __weak,ARM 提频(平台有需求才实现)
dvfs_init // 宽温芯片的调频调压
rk_board_init // __weak,由各个具体平台进行实现
console_init_r
board_late_init // 平台 late 初始化
rockchip_set_ethaddr // 设置 mac 地址
rockchip_set_serialno // 设置 serialno
setup_boot_mode // 解析 "reboot xxx" 命令、识别按键和 loader烧写模式、recovery
charge_display // U-Boot 充电
rockchip_show_logo // 显示开机 logo
soc_clk_dump // 打印 clk tree
rk_board_late_init // __weak,由各个具体平台进行实现
run_main_loop // 进入命令行模式,或执行启动命令
定义的数组如下:
static init_fnc_t init_sequence_r[] = {
initr_trace,
initr_reloc,
/* TODO: could x86/PPC have this also perhaps? */
#ifdef CONFIG_ARM
initr_caches,
/* Note: For Freescale LS2 SoCs, new MMU table is created in DDR.
* A temporary mapping of IFC high region is since removed,
* so environmental variables in NOR flash is not availble
* until board_init() is called below to remap IFC to high
* region.
*/
#endif
initr_reloc_global_data,
/*
* Some platform requires to reserve memory regions for some firmware
* to avoid kernel touches it, but U-Boot may have communication with
* firmware by share memory. So that we had better reserve firmware
* region after the initr_caches() which enables MMU and init
* translation table, we need firmware region to be mapped as cacheable
* like other regions, otherwise there would be dcache coherence issue
* between firmware and U-Boot.
*/
board_initr_caches_fixup,
#if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500)
initr_unlock_ram_in_cache,
#endif
initr_barrier,
initr_malloc,
#ifdef CONFIG_BIDRAM
bidram_initr,
#endif
#ifdef CONFIG_SYSMEM
sysmem_initr,
#endif
log_init,
initr_bootstage, /* Needs malloc() but has its own timer */
initr_console_record,
#ifdef CONFIG_SYS_NONCACHED_MEMORY
initr_noncached,
#endif
bootstage_relocate,
interrupt_init,
#ifdef CONFIG_ARM
initr_enable_interrupts,
#endif
interrupt_debugger_init,
#ifdef CONFIG_OF_LIVE
initr_of_live,
#endif
#ifdef CONFIG_DM
initr_dm,
#endif
#ifdef CONFIG_USING_KERNEL_DTB
initr_env_nowhere,
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_R)
board_early_init_r,
#endif
#if defined(CONFIG_ARM) || defined(CONFIG_NDS32) || defined(CONFIG_RISCV)
board_init, /* Setup chipselects */
#endif
#if defined(CONFIG_USING_KERNEL_DTB) && !defined(CONFIG_ENV_IS_NOWHERE)
initr_env_switch,
#endif
/*
* TODO: printing of the clock inforamtion of the board is now
* implemented as part of bdinfo command. Currently only support for
* davinci SOC's is added. Remove this check once all the board
* implement this.
*/
#ifdef CONFIG_CLOCKS
set_cpu_clk_info, /* Setup clock information */
#endif
#ifdef CONFIG_EFI_LOADER
efi_memory_init,
#endif
stdio_init_tables,
initr_serial,
initr_announce,
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_NEEDS_MANUAL_RELOC
initr_manual_reloc_cmdtable,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_MIPS)
initr_trap,
#endif
#ifdef CONFIG_ADDR_MAP
initr_addr_map,
#endif
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_POST
initr_post_backlog,
#endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PCI) && defined(CONFIG_SYS_EARLY_PCI_INIT)
/*
* Do early PCI configuration _before_ the flash gets initialised,
* because PCU ressources are crucial for flash access on some boards.
*/
initr_pci,
#endif
#ifdef CONFIG_ARCH_EARLY_INIT_R
arch_early_init_r,
#endif
power_init_board,
#ifdef CONFIG_MTD_NOR_FLASH
initr_flash,
#endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_X86)
/* initialize higher level parts of CPU like time base and timers */
cpu_init_r,
#endif
#ifdef CONFIG_PPC
initr_spi,
#endif
#ifdef CONFIG_CMD_NAND
initr_nand,
#endif
#ifdef CONFIG_CMD_ONENAND
initr_onenand,
#endif
#ifdef CONFIG_MTD_BLK
initr_mtd_blk,
#endif
#ifdef CONFIG_MMC
initr_mmc,-------------------------------初始化mmc设备
#endif
#ifndef CONFIG_USING_KERNEL_DTB
initr_env,
#endif
#ifdef CONFIG_SYS_BOOTPARAMS_LEN
initr_malloc_bootparams,
#endif
INIT_FUNC_WATCHDOG_RESET
initr_secondary_cpu,
#if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET)
mac_read_from_eeprom,
#endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PCI) && !defined(CONFIG_SYS_EARLY_PCI_INIT)
/*
* Do pci configuration
*/
initr_pci,
#endif
stdio_add_devices,
initr_jumptable,
#ifdef CONFIG_API
initr_api,
#endif
console_init_r, /* fully init console as a device */
#ifdef CONFIG_DISPLAY_BOARDINFO_LATE
console_announce_r,
show_board_info,
#endif
#ifdef CONFIG_ARCH_MISC_INIT
arch_misc_init, /* miscellaneous arch-dependent init */
#endif
#ifdef CONFIG_MISC_INIT_R
misc_init_r, /* miscellaneous platform-dependent init */
#endif
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_CMD_KGDB
initr_kgdb,
#endif
#if defined(CONFIG_MICROBLAZE) || defined(CONFIG_M68K)
timer_init, /* initialize timer */
#endif
#if defined(CONFIG_LED_STATUS)
initr_status_led,
#endif
/* PPC has a udelay(20) here dating from 2002. Why? */
#ifdef CONFIG_CMD_NET
initr_ethaddr,
#endif
#ifdef CONFIG_BOARD_LATE_INIT
board_late_init,
#endif
#if defined(CONFIG_SCSI) && !defined(CONFIG_DM_SCSI)
INIT_FUNC_WATCHDOG_RESET
initr_scsi,
#endif
#ifdef CONFIG_BITBANGMII
initr_bbmii,
#endif
#ifdef CONFIG_CMD_NET
INIT_FUNC_WATCHDOG_RESET
initr_net,
#endif
#ifdef CONFIG_POST
initr_post,
#endif
#if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_IDE)
initr_pcmcia,
#endif
#if defined(CONFIG_IDE)
initr_ide,
#endif
#ifdef CONFIG_LAST_STAGE_INIT
INIT_FUNC_WATCHDOG_RESET
/*
* Some parts can be only initialized if all others (like
* Interrupts) are up and running (i.e. the PC-style ISA
* keyboard).
*/
last_stage_init,
#endif
#ifdef CONFIG_CMD_BEDBUG
INIT_FUNC_WATCHDOG_RESET
initr_bedbug,
#endif
#if defined(CONFIG_PRAM)
initr_mem,
#endif
#ifdef CONFIG_PS2KBD
initr_kbd,
#endif
run_main_loop,
};
在board_init_r函数中的调用如下:
关于CONFIG_USING_KERNEL_DTB的处理如下:
uboot的语言构成:10%的汇编语言;90%的C语言
uboot的启动特性:稳定性;速度
uboot的简化版启动流程:
1、设置状态寄存器 cpsr ,使CPU进入 SVC 特权模式,并且禁止 FIQ 和 IRQ;
2、关闭看门狗、中断、MMU、Cache;
3、初始化部分寄存器和外设(时钟、串口、Flash、内存);
4、自搬移uboot到内存中运行;
5、设置栈空间并初始化global_data;
6、剩余大部分硬件的初始化;
7、搬移Linux内核到内存;
?
__lookup_processor_type:读取出硬件的CPU ID号,__lookup_machine_type:本函数校验的是机器码,
__vet_atags:用来校验uboot通过tag给内核传的参数。
根文件系统是特殊用途的文件系统,文件系统是一些代码,是一套软件,这套软件的功能就是对存储设备的扇区进行管理,将这些扇区的访问变成了对目录和文件名的访问.