uboot启动流程
- 格式:docx
- 大小:216.37 KB
- 文档页数:44
IMX6uboot的启动流程⽹上看了后,做了个记录,主要是⼀个流程,具体代码没有分析,有空再细看。
cpu在上电之后,它们会⼲些什么?答:检查电压⼤⼩,确定启动模式等。
简单的检查之后呢?答:⼀般从固化在cpu内部的rom⾥⾯执⾏⼀⼩段code。
这⼀⼩段code具体做了些什么呢?各个cpu⼚商会不同,具体我也不知道。
但是我们应该知道,这⼩段code必须完成确认启动模式,并初始化启动设备,搬移烧录在启动设备⾥⾯的代码到ddr⾥⾯。
ok,搬移了代码后,cpu如何识别代码?将doc,txt⽂件烧进去⾏么?答:当然不⾏,烧录的⽂件也是有格式要求的。
格式在哪⾥定呢?稍等,先要知道⽣成的uboot.bin⽂件需要有个指导⽂件,就是uboot.lds,它的作⽤是在编译过程中,决定各个可执⾏程序段的位置。
其代码如下:1 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")2 OUTPUT_ARCH(arm)3 ENTRY(_start)4 SECTIONS5 {6 . = 0x00000000;78 . = ALIGN(4);9 .text :10 {11/* WARNING - the following is hand-optimized to fit within */12/* the sector layout of our flash chips! XXX FIXME XXX */13 board/freescale/mx6q_sabreauto/flash_header.o (.text.flasheader)14 cpu/arm_cortexa8/start.o15 board/freescale/mx6q_sabreauto/libmx6q_sabreauto.a (.text)16 lib_arm/libarm.a (.text)17 net/libnet.a (.text)18 drivers/mtd/libmtd.a (.text)19 drivers/mmc/libmmc.a (.text)2021 . = DEFINED(env_offset) ? env_offset : .;22 common/env_embedded.o(.text)2324 *(.text)25 }2627 . = ALIGN(4);28 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }2930 . = ALIGN(4);31 .data : { *(.data) }3233 . = ALIGN(4);34 .got : { *(.got) }3536 . = .;37 __u_boot_cmd_start = .;38 .u_boot_cmd : { *(.u_boot_cmd) }39 __u_boot_cmd_end = .;4041 . = ALIGN(4);42 _end_of_copy = .; /* end_of ROM copy code here */43 __bss_start = .;44 .bss : { *(.bss) }45 _end = .;46 }View Code代码咱不分析,只看.text :{/* WARNING - the following is hand-optimized to fit within *//* the sector layout of our flash chips! XXX FIXME XXX */board/freescale/mx6q_sabreauto/flash_header.o (.text.flasheader)......}它的第⼀要存储的⽂件是flash_header的内容。
[精]UBOOT2017+FIT启动流程详尽分析开发环境:Nanopi-neo-plus2软件版本:uboot-2017软件版本:linux-4.14买这个板⼦有⼀段时间了,并没有全⾝⼼的投⼊在上⾯,有时间了的话就搞⼀搞,这篇随笔算是对这个版本的 uboot 启动流程做个⼤概的梳理和记录,体系结构相关的内容不作分析。
我这⾥会从 SPL(Secondary programloader) 阶段开始⼊⼿,按流程整理出 SPL 是怎样⼀步⼀步启动的 uboot,⽽ uboot ⼜是怎样加载并启动的 kernel。
废话不多说,以内容为重点来打通整体脉络。
⼀、从SPL⼊⼝点开始:阅读uboot源码时需注意:源码中存在众多 CONFIG_SPL_BUILD 宏的区分,使⽤了该宏的代码段,只有在 SPL 阶段时才会被编译进程序。
[ start.S armV8 ]_start:b resetreset: ... bl lowlevel_init ... bl _main[ lowlevel_init.S armV8 ]ENTRY(lowlevel_init) ldr w0, =CONFIG_SPL_STACK bic sp, x0, #0xf stp x29, x30, [sp, #-16]! bl s_initENDPROC(lowlevel_init)[ crt0_64.S arm/lib ]ENTRY(_main) ldr x0, =(CONFIG_SPL_STACK) bic sp, x0, #0xf ... mov x18, x0 bl board_init_f_init_reserve mov x0, #0 bl board_init_f ... mov x0, x18 /* gd_t */ ldr x1, [x18, #GD_RELOCADDR] /* dest_addr */ b board_init_r /* PC relative jump */ENDPROC(_main)[ Board.c mach-sunxi ]void board_init_f(ulong dummy){spl_init();preloader_console_init();#ifdef CONFIG_SPL_I2C_SUPPORT/* Needed early by sunxi_board_init if PMU is enabled */i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);#endifsunxi_board_init();#ifdef CONFIG_SPL_BUILDspl_mem_test();#endif}[ spl.c spl ]void board_init_r(gd_t *dummy1, ulong dummy2){...board_boot_order(spl_boot_list);if (boot_from_devices(&spl_image, spl_boot_list,ARRAY_SIZE(spl_boot_list))) {puts("SPL: failed to boot from all boot devices\n");hang();} switch (spl_image.os) { case IH_OS_U_BOOT: debug("Jumping to U-Boot\n"); break;#ifdef CONFIG_SPL_OS_BOOT case IH_OS_LINUX: debug("Jumping to Linux\n"); spl_fixup_fdt(); spl_board_prepare_for_linux(); jump_to_image_linux(&spl_image);#endif default: debug("Unsupported OS image.. Jumping nevertheless..\n"); } ... if (CONFIG_IS_ENABLED(ATF_SUPPORT)) { debug("loaded - jumping to U-Boot via ATF BL31.\n"); bl31_entry(); } debug("loaded - jumping to U-Boot...\n"); spl_board_prepare_for_boot(); jump_to_image_no_args(&spl_image);}[ spl.c spl ]board_boot_order(spl_boot_list);==>boot_source = readb(SPL_ADDR + 0x28);switch (boot_source) {case SUNXI_BOOTED_FROM_MMC0:return BOOT_DEVICE_MMC1;case SUNXI_BOOTED_FROM_NAND:return BOOT_DEVICE_NAND;case SUNXI_BOOTED_FROM_MMC2:return BOOT_DEVICE_MMC2;case SUNXI_BOOTED_FROM_SPI:return BOOT_DEVICE_SPI;};[ spl.c spl ]static int boot_from_devices(struct spl_image_info *spl_image,u32 spl_boot_list[], int count){loader = spl_ll_find_loader(spl_boot_list[i]);==> struct spl_image_loader *drv = ll_entry_start(struct spl_image_loader, spl_image_loader); const int n_ents = ll_entry_count(struct spl_image_loader, spl_image_loader); struct spl_image_loader *entry; for (entry = drv; entry != drv + n_ents; entry++) { if (boot_device == entry->boot_device) return entry; } ... if (loader && !spl_load_image(spl_image, loader)) return 0;}代码中使⽤了 ll_entry_start 宏,就可以联想到 ll_entry_declare 声明,随后通过搜索可找到#define SPL_LOAD_IMAGE(__name) \ll_entry_declare(struct spl_image_loader, __name, spl_image_loader)宏定义,进⼀步找到#define SPL_LOAD_IMAGE_METHOD(_name, _priority, _boot_device, _method) \SPL_LOAD_IMAGE(_method ## _priority ## _boot_device) = { \.name = _name, \.boot_device = _boot_device, \.load_image = _method, \}我们假设设备以SD卡的⽅式启动,SD对应着 BOOT_DEVICE_MMC1,那么通过搜索 SPL_LOAD_IMAGE_METHOD 筛选 BOOT_DEVICE_MMC1 就可以定位到驱动的本源,即[ spl_mmc.c spl ]SPL_LOAD_IMAGE_METHOD("MMC1", 0, BOOT_DEVICE_MMC1, spl_mmc_load_image);继续分析启动流程spl_load_image(spl_image, loader)==>int spl_mmc_load_image(struct spl_image_info *spl_image,struct spl_boot_device *bootdev){...spl_boot_mode(bootdev->boot_device);return MMCSD_MODE_RAW;.../* 通过宏 CONFIG_SPL_OS_BOOT 选择,spl直接启动OS还是启动uboot,这⾥返回1,启动uboot */spl_start_uboot()return1;...mmc_load_image_raw_sector(spl_image, mmc, CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR);...mmc_load_legacy(spl_image, mmc, sector, header);spl_parse_image_header(spl_image, header);spl_set_header_raw_uboot(spl_image); spl_image->size = CONFIG_SYS_MONITOR_LEN; spl_image->entry_point = CONFIG_SYS_UBOOT_START; spl_image->load_addr = CONFIG_SYS_TEXT_BASE; spl_image->os = IH_OS_U_BOOT; spl_image->name = "U-Boot"; ... }回到 board_init_r 继续分析,spl_image->os 赋值为 IH_OS_U_BOOT ,所以 break 直接跳出;接下来有执⾏ATF的bl31部分(这部分暂不做分析),最后执⾏ jump_to_image_no_args 跳转到 spl_image->entry_point ,也就是正式的uboot阶段。
U-Boot启动代码分析U-boot的启动顺序分为stage1和stage2两部分,见下图。
依赖于CPU体系结构的代码(如设备初始化代码等)通常放在stage1中用汇编语言实现,而在stage2则通常由C语言实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。
以下主要梳理了stage2阶段函数的调用顺序以及每个函数的功能。
U-boot的启动顺序C语言代码部分lib_arm/board.c中的start_armboot既是C语言开始的函数也是整个启动代码中C语言的主函数,同时还是整个U-boot的主函数,该函数只要完成如下操作。
(1)调用一系列的初始化函数。
(2)初始化Flash设备。
(3)初始化系统内存分配函数(4)如果目标系统拥有NAND设备,则初始化NAND设备(5)如果目标系统有显示设备,则初始化该类设备。
(6)初始化相关网络设备,填写IP、MAC地址等。
(7)进入命令循环(即整个Boot的工作循环),接收用户从串口输入的命令,然后进行相应的工作。
下面结合源码分析函数调用顺序以及函功能:代码:void start_armboot (void){init_fnc_t **init_fnc_ptr;char *s;int mmc_exist = 0;#if defined(CONFIG_VFD) || defined(CONFIG_LCD)unsigned long addr;#endif注释:从U-boot stage1中start.s程序调到这里执行start_armboot函数,这一段代码进行了变量声明,其中定义了一个名为init_fnc_ptr的双重指针。
如果CONFIG_VFD或者CONFIG_LCD被定义了则声明一无符号长整型变量addr,本开发板中没有定义无需声明addr。
代码:/* Pointer is writable since we allocated a register for it */gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));/* compiler optimization barrier needed for GCC >= 3.4 */__asm__ __volatile__("": : :"memory"); //内存屏障,告诉编译器内存被修改过了memset ((void*)gd, 0, sizeof (gd_t));gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); //指向gd之前memset (gd->bd, 0, sizeof (bd_t));// gd->flags |= GD_FLG_RELOC;monitor_flash_len = _bss_start - _armboot_start; //u-boot映像的大小其中_armboot_start为code start ,_bss_start为code + data end == BSS start.注释:gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));//内存强制转换,gd为全局环境变量,gd指向uboot之前的地址;memset ():void * memset(void * s,char c,size_t count)将指针s所指地址以及之后count个地址中数值赋值为c。
uboot启动流程分析Uboot启动流程分析。
Uboot是一种常用的嵌入式系统启动加载程序,它的启动流程对于嵌入式系统的正常运行至关重要。
本文将对Uboot启动流程进行分析,以便更好地理解其工作原理。
首先,Uboot的启动流程可以分为以下几个步骤,Reset、初始化、设备初始化、加载内核。
接下来我们将逐一进行详细的分析。
Reset阶段是整个启动流程的起点,当系统上电或者复位时,CPU会跳转到Uboot的入口地址开始执行。
在这个阶段,Uboot会进行一些基本的硬件初始化工作,包括设置栈指针、初始化CPU寄存器等。
接着是初始化阶段,Uboot会进行一系列的初始化工作,包括初始化串口、初始化内存控制器、初始化时钟等。
这些初始化工作是为了确保系统能够正常地运行,并为后续的工作做好准备。
设备初始化阶段是Uboot启动流程中的一个重要环节,它包括对外设的初始化和检测。
在这个阶段,Uboot会初始化各种外设,如网卡、存储设备等,并对其进行检测,以确保它们能够正常工作。
最后一个阶段是加载内核,Uboot会从存储设备中加载操作系统的内核镜像到内存中,并跳转到内核的入口地址开始执行。
在这个过程中,Uboot会进行一些必要的设置,如传递启动参数给内核,并最终将控制权交给内核。
总的来说,Uboot的启动流程是一个非常重要的过程,它涉及到系统的硬件初始化、外设的初始化和内核的加载等工作。
只有当这些工作都顺利完成时,系统才能够正常地启动运行。
因此,对Uboot启动流程的深入理解对于嵌入式系统的开发和调试具有重要意义。
通过本文对Uboot启动流程的分析,相信读者对Uboot的工作原理有了更清晰的认识。
希望本文能够对大家有所帮助,谢谢阅读!。
am335xu-boot启动过程分析 u-boot属于两阶段的bootloader,第⼀阶段的⽂件为 arch/arm/cpu/armv7/start.S 和 arch/arm/cpu/armv7/lowlevel_init.S,前者是平台相关的,后者是开发板相关的。
1. u-boot第⼀阶段代码分析 (1)硬件设备初始化 将CPU的⼯作模式设为管理模式(SVC); 关闭中断; 禁⽤MMU,TLB ; 板级初始化; (2)为加载Bootloader的第⼆阶段代码准备RAM空间 加载u-boot.img,跳转到u-boot.img; 上述⼯作,也就是uboot-spl代码流程的核⼼。
代码如下:arch/arm/cpu/armv7/start.S1/*2 * the actual reset code3*/4reset:5 bl save_boot_params6/*7 * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,8 * except if in HYP mode already9*/10 mrs r0, cpsr11 and r1, r0, #0x1f @ mask mode bits12 teq r1, #0x1a @ test for HYP mode13 bicne r0, r0, #0x1f @ clear all mode bits14 orrne r0, r0, #0x13 @ set SVC mode15 orr r0, r0, #0xc0 @ disable FIQ and IRQ16 msr cpsr,r017@@ 以上通过设置CPSR寄存器⾥设置CPU为SVC模式,禁⽌中断18@@ 具体操作可以参考《[kernel 启动流程] (第⼆章)第⼀阶段之——设置SVC、关闭中断》的分析1920/* the mask ROM code should have PLL and others stable */21#ifndef CONFIG_SKIP_LOWLEVEL_INIT22 bl cpu_init_cp1523@@ 调⽤cpu_init_cp15,初始化协处理器CP15,从⽽禁⽤MMU和TLB。
基于MPC83xx 的U-boot 启动流程分析和移植董 闯北京邮电大学信息与通信工程学院,北京(100876)E-mail :donix.dong@摘 要:本文首先引入Bootloader 的概念,接着介绍U-boot 这种引导程序,并以Freescale 32位微处理器MPC83xx 为例,结合代码详细分析了U-boot 的启动的各个阶段及最终引导Linux 内核的过程,最后,建立交叉编译环境,针对TC8313E 目标板,给出U-boot 移植与编译的基本步骤。
关键词:U-boot;MPC83xx;交叉编译;移植;嵌入式系统中图分类号:TP393.051.引言引导程序(Bootloader)是系统加电后运行的第一段软件代码,类似于PC 机中的引导加载程序BIOS 。
虽然引导程序仅在系统启动时运行非常短的时间,但对于嵌入式系统来说,这是一个非常重要的组成部分。
通过这段小程序,初始化必要的硬件设备,创建内核需要的一些信息并将这些信息传递给内核,从而将系统的软、硬件环境配置到一个合适的状态,最终调用操作系统内核,真正起到引导和加载内核的作用。
2. U-boot 介绍目前,嵌入式领域里出现了很多种类的Bootloader ,如Armboot 、Blob 、Redboot 、vivi 和U-boot 等,其中U-boot 是使用最广泛,功能最完善的。
U-boot (Universal Boot Loader)是从PPCBOOT 发展演化而来[1],其源码目录、编译形式与Linux 内核很相似,事实上,不少U-boot 源码就是相应的Linux 内核源程序的简化,尤其是一些设备的驱动程序,这从U-boot 源码的注释中就能体现。
U-boot 中Universal 有两层含义,一是U-boot 除了支持PowerPC 系列的处理器外,还能支持MIPS 、x86、ARM 、NIOS 、XScale 等诸多常用系列的处理器;另外一层含义则是U-boot 不仅仅支持嵌入式Linux 操作系统的引导,还支持OpenBSD, NetBSD, FreeBSD, SVR4, Solaris, VxWorks, LynxOS, pSOS, lrix, RTEMS, QNX, ARTOS 等操作系统的引导。
海思uboot启动流程详细分析(⼀)第⼀阶段 start.S⾸先我们可以在u-boot.lds中看到ENTRY(_start),即指定了⼊⼝_start,_start也就是整个start.S的最开始;1. reset在arch\arm\cpu\armv8\hi3559av100中的start.S注意x30在ARMV8中代表lr寄存器reset:/** Could be EL3/EL2/EL1, Initial State:* Little Endian, MMU Disabled, i/dCache Disabled*/adr x0, vectorsswitch_el x1, 3f, 2f, 1f3: msr vbar_el3, x0mrs x0, scr_el3orr x0, x0, #0xf /* SCR_EL3.NS|IRQ|FIQ|EA */msr scr_el3, x0msr cptr_el3, xzr /* Enable FP/SIMD */#ifdef COUNTER_FREQUENCYldr x0, =COUNTER_FREQUENCYmsr cntfrq_el0, x0 /* Initialize CNTFRQ */#endifb 0f2: msr vbar_el2, x0mov x0, #0x33ffmsr cptr_el2, x0 /* Enable FP/SIMD */b 0f1: msr vbar_el1, x0mov x0, #3 << 20msr cpacr_el1, x0 /* Enable FP/SIMD */0:/** Cache/BPB/TLB Invalidate* i-cache is invalidated before enabled in icache_enable()* tlb is invalidated before mmu is enabled in dcache_enable()* d-cache is invalidated before enabled in dcache_enable()*//** read system register REG_SC_GEN2* check if ziju flag*/ldr x0, =SYS_CTRL_REG_BASEldr w1, [x0, #REG_SC_GEN2]ldr w2, =0x7a696a75 /* magic for "ziju" */cmp w1, w2bne normal_start_flowmov x1, sp /* save sp */str w1, [x0, #REG_SC_GEN2] /* clear ziju flag */adr x0, vectors,其中的vectors代表了异常向量表主要做了如下事情:1)reset SCTRL寄存器具体可参考reset_sctrl函数,由CONFIG_SYS_RESET_SCTRL控制,⼀般不需要打开。
1.1U-Boot工作过程U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下:(1)第一阶段的功能硬件设备初始化加载U-Boot第二阶段代码到RAM空间设置好栈跳转到第二阶段代码入口(2)第二阶段的功能初始化本阶段使用的硬件设备检测系统内存映射将内核从Flash读取到RAM中为内核设置启动参数调用内核1.1.1U-Boot启动第一阶段代码分析第一阶段对应的文件是cpu/arm920t/start.S和board/samsung/mini2440/lowlevel_init.S。
U-Boot启动第一阶段流程如下:图 2.1 U-Boot启动第一阶段流程根据cpu/arm920t/u-boot.lds中指定的连接方式:ENTRY(_start)SECTIONS{. = 0x00000000;. = ALIGN(4);.text :{cpu/arm920t/start.o (.text)board/samsung/mini2440/lowlevel_init.o (.text)board/samsung/mini2440/nand_read.o (.text)*(.text)}… …}第一个链接的是cpu/arm920t/start.o,因此u-boot.bin的入口代码在cpu/arm920t/start.o中,其源代码在cpu/arm920t/start.S中。
下面我们来分析cpu/arm920t/start.S的执行。
1.硬件设备初始化(1)设置异常向量cpu/arm920t/start.S开头有如下的代码:.globl _start_start: b start_code /* 复位*/ ldr pc, _undefined_instruction /* 未定义指令向量 */ldr pc, _software_interrupt /* 软件中断向量 */ldr pc, _prefetch_abort /* 预取指令异常向量 */ldr pc, _data_abort /* 数据操作异常向量 */ldr pc, _not_used /* 未使用 */ldr pc, _irq /* irq中断向量 */ ldr pc, _fiq /* fiq中断向量 */ /* 中断向量表入口地址 */_undefined_instruction: .word undefined_instruction_software_interrupt: .word software_interrupt_prefetch_abort: .word prefetch_abort_data_abort: .word data_abort_not_used: .word not_used_irq: .word irq_fiq: .word fiq.balignl 16,0xdeadbeef以上代码设置了ARM异常向量表,各个异常向量介绍如下:表 2.1 ARM异常向量表在cpu/arm920t/start.S中还有这些异常对应的异常处理程序。
基于imx6ull的uboot2017启动流程1、Uboot2017编译配置流程/Uboot-2017.03$ make distclean/Uboot-2017.03$ make mx6ull_14x14_evk_defconfig/Uboot-2017.03$ make最后会编译出u-boot-dtb.imx⽂件,可以烧⼊板⼦中启动了。
2、Uboot2017的启动流程分析 1)内核启动的第⼀阶段⾸先,经过上⾯的编译后,会在顶层⽬录下发现⾃动⽣成了u-boot.lds链接⽂件,有了这个⽂件后,我们就会很容易找到我们需要分析的第⼀个⽂件是什么:因此,我们就可以从arch/arm/cpu/armv7/start.S这个⽂件出发,去分析;但是当打开start.S这个⽂件时,并不会发现有程序的⼊⼝,也就是上图的_start这个标志,这个标志定义在arch/arm/lib/vectors.S⽂件中:从上⾯也可以看到,主要定义了异常与中断的跳转函数,⽽第⼀个跳转到的是reset标志,可以发现,reset标志是定义在start.S中的,然后顺着程序向下执⾏:1)将CPU从⽤户模式切换到管理员模式2)禁⽌中断这个函数是对CPU进⾏初始化搜索_main,该函数定义在crt0.S中,这个函数是第⼀阶段的关键,第⼀阶段中最主要的函数是为了调⽤board_init_f:由于此时RAM还并没有初始化,不能使⽤,因此使⽤gd结构体进⾏数据的传输1)设置c代码的运⾏环境,为调⽤board_init_f作准备 a) 设置堆栈 b) 调⽤board_init_f_alloc_reserve接⼝,从堆栈开始的地⽅,为uboot的gd结构分配空间 c) 调⽤board_init_f_init_reserve接⼝,对gd进⾏初始化2)调⽤board_init_f函数,完成⼀些前期的初始化⼯作 a)点亮⼀个Debug⽤的LED灯,表⽰u-boot已经活了 b)初始化DRAM,DDR等system范围的RAM等 c)计算后续代码需要使⽤的⼀些参数,包括relocation destination,the future stack,the future GD location等.3).如果当前是SPL(由CONFIG_SPL_BUILD控制),则_main函数结束,直接返回.如果是正常的u-boot,则继续执⾏后续的动作4).根据board_init_f指定的参数,执⾏u-boot的relocation操作5).清除BSS段6).调⽤board_init_r函数,执⾏后续的初始化操作#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)ldr sp, =(CONFIG_SPL_STACK)#elseldr sp, =(CONFIG_SYS_INIT_SP_ADDR)#endif#if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */mov r3, spbic r3, r3, #7mov sp, r3#elsebic sp, sp, #7 /* 8-byte alignment for ABI compliance */#endifmov r0, spbl board_init_f_alloc_reservemov sp, r0/* set up gd here, outside any C code */mov r9, r0bl board_init_f_init_reservemov r0, #0bl board_init_f 在board_init_f中会顺序的执⾏init_sequence_f中定义的函数指针,函数指针中,⽐较关键的是初始化时钟、初始化内核启动的环境参数、设置串⼝波特率、初始化串⼝、初始化i2c、初始化SPI、初始化ram、设置重定位地址、重定位get_clocksenv_initinit_baud_rateserial_initinit_func_i2cinit_func_spidram_initjump_to_copy.... jump_to_copy函数调⽤crt0.S⽂件中的重定位函数,之后便进⾏清除bss段,之后便调⽤board_init_r函数,进⼊内核启动的第⼆阶段。
(一)U-Boot启动过程--详细版的完全分析我们知道,bootloader是系统上电后最初加载运行的代码。
它提供了处理器上电复位后最开始需要执行的初始化代码。
在PC机上引导程序一般由BIOS开始执行,然后读取硬盘中位于MBR(Main Boot Record,主引导记录)中的Bootloader(例如LILO或GRUB),并进一步引导操作系统的启动。
然而在嵌入式系统中通常没有像BIOS那样的固件程序,因此整个系统的加载启动就完全由bootloader来完成。
它主要的功能是加载与引导内核映像一个嵌入式的存储设备通过通常包括四个分区:第一分区:存放的当然是u-boot第二个分区:存放着u-boot要传给系统内核的参数第三个分区:是系统内核(kernel)第四个分区:则是根文件系统如下图所示:u-boot是一种普遍用于嵌入式系统中的Bootloader。
Bootloader介绍Bootloader是进行嵌入式开发必然会接触的一个概念,它是嵌入式学院<嵌入式工程师职业培训班>二期课程中嵌入式linux系统开发方面的重要内容。
本篇文章主要讲解Bootloader 的基本概念以及内部原理,这部分内容的掌握将对嵌入式linux系统开发的学习非常有帮助!Bootloader的定义:Bootloader是在操作系统运行之前执行的一小段程序,通过这一小段程序,我们可以初始化硬件设备、建立内存空间的映射表,从而建立适当的系统软硬件环境,为最终调用操作系统内核做好准备。
意思就是说如果我们要想让一个操作系统在我们的板子上运转起来,我们就必须首先对我们的板子进行一些基本配置和初始化,然后才可以将操作系统引导进来运行。
具体在Bootloader中完成了哪些操作我们会在后面分析到,这里我们先来回忆一下PC的体系结构:PC机中的引导加载程序是由BIOS和位于硬盘MBR中的OS Boot Loader(比如LILO和GRUB等)一起组成的,BIOS在完成硬件检测和资源分配后,将硬盘MBR中的Boot Loader读到系统的RAM中,然后将控制权交给OS Boot Loader。
U-Boot工作过程U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下:(1)第一阶段的功能硬件设备初始化加载U-Boot第二阶段代码到RAM空间设置好栈跳转到第二阶段代码入口(2)第二阶段的功能初始化本阶段使用的硬件设备检测系统内存映射将内核从Flash读取到RAM中为内核设置启动参数调用内核1.1.1 U-Boot启动第一阶段代码分析第一阶段对应的文件是cpu/arm920t/和board/samsung/mini2440/。
U-Boot启动第一阶段流程如下:图 U-Boot启动第一阶段流程根据cpu/arm920t/中指定的连接方式:ENTRY(_start)SECTIONS{. = 0x00000000;. = ALIGN(4);.text :{cpu/arm920t/ (.text)board/samsung/mini2440/ (.text)board/samsung/mini2440/ (.text)*(.text)}… …}第一个链接的是cpu/arm920t/,因此的入口代码在cpu/arm920t/中,其源代码在cpu/arm920t/中。
下面我们来分析cpu/arm920t/的执行。
1. 硬件设备初始化(1)设置异常向量cpu/arm920t/开头有如下的代码:.globl _start_start: b start_code /* 复位*/ldr pc, _undefined_instruction /*未定义指令向量 */ldr pc, _software_interrupt /* 软件中断向量 */ldr pc, _prefetch_abort /* 预取指令异常向量 */ldr pc, _data_abort /* 数据操作异常向量 */ldr pc, _not_used /* 未使用 */ldr pc, _irq /* irq中断向量 */ldr pc, _fiq /* fiq中断向量 */ /* 中断向量表入口地址 */_undefined_instruction: .word undefined_instruction_software_interrupt: .word software_interrupt_prefetch_abort: .word prefetch_abort_data_abort: .word data_abort_not_used: .word not_used_irq: .word irq_fiq: .word fiq.balignl 16,0xdeadbeef以上代码设置了ARM异常向量表,各个异常向量介绍如下:表 ARM异常向量表在cpu/arm920t/中还有这些异常对应的异常处理程序。
当一个异常产生时,CPU根据异常号在异常向量表中找到对应的异常向量,然后执行异常向量处的跳转指令,CPU就跳转到对应的异常处理程序执行。
其中复位异常向量的指令“b start_code”决定了U-Boot启动后将自动跳转到标号“start_code”处执行。
(2)CPU进入SVC模式start_code:/** set the cpu to SVC32 mode*/mrs r0, cpsrbic r0, r0, #0x1f /*工作模式位清零 */orr r0, r0, #0xd3 /*工作模式位设置为“10011”(管理模式),并将中断禁止位和快中断禁止位置1 */msr cpsr, r0以上代码将CPU的工作模式位设置为管理模式,并将中断禁止位和快中断禁止位置一,从而屏蔽了IRQ和FIQ中断。
(3)设置控制寄存器地址# if defined(CONFIG_S3C2400)# define pWTCON 0x# define INTMSK 0x# define CLKDIVN 0x#else /* s3c2410与s3c2440下面4个寄存器地址相同 */# define pWTCON 0x /* WATCHDOG控制寄存器地址*/# define INTMSK 0x4A000008 /* INTMSK寄存器地址 */# define INTSUBMSK 0x4A00001C /* INTSUBMSK寄存器地址 */# define CLKDIVN 0x4C000014 /* CLKDIVN寄存器地址 */# endif对与s3c2440开发板,以上代码完成了WATCHDOG,INTMSK,INTSUBMSK,CLKDIVN四个寄存器的地址的设置。
各个寄存器地址参见参考文献[4] 。
(4)关闭看门狗ldr r0, =pWTCONmov r1, #0x0str r1, [r0] /* 看门狗控制器的最低位为0时,看门狗不输出复位信号 */以上代码向看门狗控制寄存器写入0,关闭看门狗。
否则在U-Boot启动过程中,CPU 将不断重启。
(5)屏蔽中断/** mask all IRQs by setting all bits in the INTMR - default*/mov r1, #0xffffffff /* 某位被置1则对应的中断被屏蔽 */ldr r0, =INTMSKstr r1, [r0]INTMSK是主中断屏蔽寄存器,每一位对应SRCPND(中断源引脚寄存器)中的一位,表明SRCPND相应位代表的中断请求是否被CPU所处理。
根据参考文献4,INTMSK寄存器是一个32位的寄存器,每位对应一个中断,向其中写入0xffffffff就将INTMSK寄存器全部位置一,从而屏蔽对应的中断。
# if defined(CONFIG_S3C2440)ldr r1, =0x7fffldr r0, =INTSUBMSKstr r1, [r0]# endifINTSUBMSK每一位对应SUBSRCPND中的一位,表明SUBSRCPND相应位代表的中断请求是否被CPU所处理。
根据参考文献4,INTSUBMSK寄存器是一个32位的寄存器,但是只使用了低15位。
向其中写入0x7fff就是将INTSUBMSK寄存器全部有效位(低15位)置一,从而屏蔽对应的中断。
(6)设置MPLLCON,UPLLCON, CLKDIVN# if defined(CONFIG_S3C2440)#define MPLLCON 0x4C000004#define UPLLCON 0x4C000008ldr r0, =CLKDIVNmov r1, #5str r1, [r0]ldr r0, =MPLLCONldr r1, =0x7F021str r1, [r0]ldr r0, =UPLLCONldr r1, =0x38022str r1, [r0]# else/* FCLK:HCLK:PCLK = 1:2:4 *//* default FCLK is 120 MHz ! */ldr r0, =CLKDIVNmov r1, #3str r1, [r0]#endifCPU上电几毫秒后,晶振输出稳定,FCLK=Fin(晶振频率),CPU开始执行指令。
但实际上,FCLK可以高于Fin,为了提高系统时钟,需要用软件来启用PLL。
这就需要设置CLKDIVN,MPLLCON,UPLLCON这3个寄存器。
CLKDIVN寄存器用于设置FCLK,HCLK,PCLK三者间的比例,可以根据表来设置。
表 S3C2440 的CLKDIVN寄存器格式设置CLKDIVN为5,就将HDIVN设置为二进制的10,由于CAMDIVN[9]没有被改变过,取默认值0,因此HCLK = FCLK/4。
PDIVN被设置为1,因此PCLK= HCLK/2。
因此分频比FCLK:HCLK:PCLK = 1:4:8 。
MPLLCON寄存器用于设置FCLK与Fin的倍数。
MPLLCON的位[19:12]称为MDIV,位[9:4]称为PDIV,位[1:0]称为SDIV。
对于S3C2440,FCLK与Fin的关系如下面公式:MPLL(FCLK) = (2×m×Fin)/(p×)其中: m=MDIC+8,p=PDIV+2,s=SDIVMPLLCON与UPLLCON的值可以根据参考文献4中“PLL VALUE SELECTION TABLE”设置。
该表部分摘录如下:表推荐PLL值当mini2440系统主频设置为405MHZ,USB时钟频率设置为48MHZ时,系统可以稳定运行,因此设置MPLLCON与UPLLCON为:MPLLCON=(0x7f<<12) | (0x02<<4) | (0x01) = 0x7f021UPLLCON=(0x38<<12) | (0x02<<4) | (0x02) = 0x38022(7)关闭MMU,cache接着往下看:#ifndef CONFIG_SKIP_LOWLEVEL_INITbl cpu_init_crit#endifcpu_init_crit这段代码在U-Boot正常启动时才需要执行,若将U-Boot从RAM中启动则应该注释掉这段代码。
下面分析一下cpu_init_crit到底做了什么:320 #ifndef CONFIG_SKIP_LOWLEVEL_INIT321 cpu_init_crit:322 /*323 * 使数据cache与指令cache无效 */324 */325 mov r0, #0326 mcr p15, 0, r0, c7, c7, 0 /* 向c7写入0将使ICache与DCache无效*/ 327 mcr p15, 0, r0, c8, c7, 0 /* 向c8写入0将使TLB失效 */328329 /*330 * disable MMU stuff and caches331 */332 mrc p15, 0, r0, c1, c0, 0 /* 读出控制寄存器到r0中 */333 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)334 bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)335 orr r0, r0, #0x00000002 @ set bit 2 (A) Align336 orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache337 mcr p15, 0, r0, c1, c0, 0 /* 保存r0到控制寄存器 */338339 /*340 * before relocating, we have to setup RAM timing341 * because memory timing is board-dependend, you will342 * find a in your board directory.343 */344 mov ip, lr345346 bl lowlevel_init347348 mov lr, ip349 mov pc, lr350 #endif /* CONFIG_SKIP_LOWLEVEL_INIT */代码中的c0,c1,c7,c8都是ARM920T的协处理器CP15的寄存器。