




已阅读5页,还剩32页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
U-Boot启动过程(国嵌)开发板上电后,执行U-Boot的第一条指令,然后顺序执行U-Boot启动函数。看一下board/smdk2410/u-boot.lds这个链接脚本,可以知道目标程序的各部分链接顺序。第一个要链接的是cpu/arm920t/start.o,那么U-Boot的入口指令一定位于这个程序中。下面分两阶段介绍启动流程:第一阶段1cpu/arm920t/start.S这个汇编程序是U-Boot的入口程序,开头就是复位向量的代码。_start: b reset /复位向量 ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq /中断向量 ldr pc, _fiq /中断向量 /* the actual reset code */reset: /复位启动子程序 /* 设置CPU为SVC32模式 */ mrs r0,cpsr bic r0,r0,#0x1f orr r0,r0,#0xd3 msr cpsr,r0/* 关闭看门狗 */ relocate: /* 把U-Boot重新定位到RAM */adr r0, _start /* r0是代码的当前位置 */ldr r1, _TEXT_BASE /*_TEXT_BASE是RAM中的地址 */cmp r0, r1 /* 比较r0和r1,判断当前是从Flash启动,还是RAM */beq stack_setup /* 如果r0等于r1,跳过重定位代码 */* 准备重新定位代码 */ldr r2, _armboot_startldr r3, _bss_startsub r2, r3, r2 /* r2 得到armboot的大小 */add r2, r0, r2 /* r2 得到要复制代码的末尾地址 */copy_loop: /* 重新定位代码 */ldmia r0!, r3-r10 /*从源地址r0复制 */stmia r1!, r3-r10 /* 复制到目的地址r1 */cmp r0, r2 /* 复制数据块直到源数据末尾地址r2 */ble copy_loop /* 初始化堆栈等 */stack_setup:ldr r0, _TEXT_BASE /* 上面是128 KiB重定位的u-boot */sub r0, r0, #CFG_MALLOC_LEN /* 向下是内存分配空间 */sub r0, r0, #CFG_GBL_DATA_SIZE /* 然后是bdinfo结构体地址空间 */#ifdef CONFIG_USE_IRQsub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)#endifsub sp, r0, #12 /* 为abort-stack预留3个字 */clear_bss:ldr r0, _bss_start /* 找到bss段起始地址 */ldr r1, _bss_end /* bss段末尾地址 */mov r2, #0x00000000 /* 清零 */clbss_l:str r2, r0 /* bss段地址空间清零循环. */ add r0, r0, #4 cmp r0, r1bne clbss_l/* 跳转到start_armboot函数入口,_start_armboot字保存函数入口指针 */ldr pc, _start_armboot_start_armboot: .word start_armboot /start_armboot函数在lib_arm/board.c中实现第二阶段2lib_arm/board.cstart_armboot是U-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。3init_sequenceinit_sequence数组保存着基本的初始化函数指针。init_fnc_t *init_sequence = cpu_init, /* 基本的处理器相关配置 - cpu/arm920t/cpu.c */board_init, /* 基本的板级相关配置 - board/smdk2410/smdk2410.c */interrupt_init, /* 初始化中断处理 - cpu/arm920t/s3c24x0/interrupt.c */env_init, /* 初始化环境变量 - common/cmd_flash.c */init_baudrate, /* 初始化波特率设置 - lib_arm/board.c */serial_init, /* 串口通讯设置 - cpu/arm920t/s3c24x0/serial.c */console_init_f, /* 控制台初始化阶段1 - common/console.c */display_banner, /* 打印u-boot信息 - lib_arm/board.c */dram_init, /* 配置可用的RAM - board/smdk2410/smdk2410.c */display_dram_config, /* 显示RAM的配置大小 - lib_arm/board.c */NULL,;void start_armboot (void)/* 顺序执行init_sequence数组中的初始化函数 */ for (init_fnc_ptr = init_sequence; *init_fnc_ptr; +init_fnc_ptr) if (*init_fnc_ptr)() != 0) hang (); /*配置可用的Flash */ size = flash_init (); display_flash_config (size); /* _armboot_start 在u-boot.lds链接脚本中定义 */ mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);/* 配置环境变量*/env_relocate ();/* 从环境变量中获取IP地址 */gd-bd-bi_ip_addr = getenv_IPaddr (ipaddr);/* 以太网接口MAC 地址 */ devices_init (); /* 获取列表中的设备 */ jumptable_init (); console_init_r (); /* 完整地初始化控制台设备 */ enable_interrupts (); /* 使能中断处理 */* 通过环境变量初始化 */ if (s = getenv (loadaddr) != NULL) load_addr = simple_strtoul (s, NULL, 16); /* main_loop()循环不断执行 */for (;) main_loop (); /* 主循环函数处理执行用户命令 - common/main.c */ 命令实现U-Boot作为Bootloader,具备多种引导内核启动的方式。常用的go和bootm命令可以直接引导内核映像启动。U-Boot与内核的关系主要是内核启动过程中参数的传递。1go命令的实现/* common/cmd_boot.c */int do_go (cmd_tbl_t *cmdtp, int flag, int argc, char *argv) ulong addr, rc; int rcode = 0; if (argc usage); return 1; addr = simple_strtoul(argv1, NULL, 16); printf (# Starting application at 0x%08lX .n, addr); rc = (ulong (*)(int, char )addr) (-argc, &argv1); /* 运行程序 */ if (rc != 0) rcode = 1; printf (# Application terminated, rc = 0x%lXn, rc); /*如果是运行linux,这条指令是否能运行?*/ return rcode;go命令调用do_go()函数,跳转到某个地址执行的。如果在这个地址准备好了自引导的内核映像,就可以启动了。尽管go命令可以带变参,实际使用时不用来传递参数。2bootm命令的实现/* common/cmd_bootm.c */int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv) /* 检查头部 */if (crc32 (0, (uchar *)data, len) != checksum) puts (Bad Header Checksumn);SHOW_BOOT_PROGRESS (-2);return 1; /*解压缩*/switch (hdr-ih_comp) case IH_COMP_NONE:if(ntohl(hdr-ih_load) = addr) printf ( XIP %s . , name); else #if defined(CONFIG_HW_WATCHDOG) | defined(CONFIG_WATCHDOG)size_t l = len;void *to = (void *)ntohl(hdr-ih_load);void *from = (void *)data;printf ( Loading %s . , name);while (l 0) size_t tail = (l CHUNKSZ) ? CHUNKSZ : l;WATCHDOG_RESET();memmove (to, from, tail);to += tail;from += tail;l -= tail;#else/* !(CONFIG_HW_WATCHDOG | CONFIG_WATCHDOG) */memmove (void *) ntohl(hdr-ih_load), (uchar *)data, len);#endif/* CONFIG_HW_WATCHDOG | CONFIG_WATCHDOG */break;case IH_COMP_GZIP:printf ( Uncompressing %s . , name);if (gunzip (void *)ntohl(hdr-ih_load), unc_len, (uchar *)data, &len) != 0) puts (GUNZIP ERROR - must RESET board to recovern);SHOW_BOOT_PROGRESS (-6);do_reset (cmdtp, flag, argc, argv);break;#ifdef CONFIG_BZIP2case IH_COMP_BZIP2:printf ( Uncompressing %s . , name);/* * If weve got less than 4 MB of malloc() space, * use slower decompression algorithm which requires * at most 2300 KB of memory. */i = BZ2_bzBuffToBuffDecompress (char*)ntohl(hdr-ih_load),&unc_len, (char *)data, len,CFG_MALLOC_LEN ih_comp);SHOW_BOOT_PROGRESS (-7);return 1; switch (hdr-ih_os) default:/* handled by (original) Linux case */case IH_OS_LINUX: do_bootm_linux (cmdtp, flag, argc, argv, addr, len_ptr, verify); break;case IH_OS_NETBSD: do_bootm_netbsd (cmdtp, flag, argc, argv, addr, len_ptr, verify); break;case IH_OS_RTEMS: do_bootm_rtems (cmdtp, flag, argc, argv, addr, len_ptr, verify); break;case IH_OS_VXWORKS: do_bootm_vxworks (cmdtp, flag, argc, argv, addr, len_ptr, verify); break;case IH_OS_QNX: do_bootm_qnxelf (cmdtp, flag, argc, argv, addr, len_ptr, verify); break;bootm命令调用do_bootm函数。这个函数专门用来引导各种操作系统映像,可以支持引导Linux、vxWorks、QNX等操作系统。引导Linux的时候,调用do_bootm_linux()函数。3do_bootm_linux函数的实现 /* lib_arm/armlinux.c */void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv, ulong addr, ulong *len_ptr, int verify)theKernel = (void (*)(int, int, uint)ntohl(hdr-ih_ep); /* we assume that the kernel is in place */ printf (nStarting kernel .nn); theKernel (0, bd-bi_arch_number, bd-bi_boot_params); /*启动内核,传递启动参数*/ do_bootm_linux()函数是专门引导Linux映像的函数,它还可以处理ramdisk文件系统的映像。这里引导的内核映像和ramdisk映像,必须是U-Boot格式的。U-Boot格式的映像可以通过mkimage工具来转换,其中包含了U-Boot可以识别的符号。 U-Boot启动过程完全分析 1.1 U-Boot工作过程 U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下: (1)第一阶段的功能 硬件设备初始化 加载U-Boot第二阶段代码到RAM空间 设置好栈 跳转到第二阶段代码入口 (2)第二阶段的功能 初始化本阶段使用的硬件设备 检测系统内存映射 将内核从Flash读取到RAM中 为内核设置启动参数 调用内核1.1.1 U-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异常向量表地址 异常 进入模式描述0x00000000 复位管理模式复位电平有效时,产生复位异常,程序跳转到复位处理程序处执行0x00000004 未定义指令未定义模式遇到不能处理的指令时,产生未定义指令异常0x00000008软件中断管理模式执行SWI指令产生,用于用户模式下的程序调用特权操作指令0x0000000c预存指令中止模式处理器预取指令的地址不存在,或该地址不允许当前指令访问,产生指令预取中止异常0x00000010数据操作中止模式处理器数据访问指令的地址不存在,或该地址不允许当前指令访问时,产生数据中止异常0x00000014未使用未使用未使用0x00000018IRQIRQ外部中断请求有效,且CPSR中的I位为0时,产生IRQ异常0x0000001cFIQFIQ快速中断请求引脚有效,且CPSR中的F位为0时,产生FIQ异常 在cpu/arm920t/start.S中还有这些异常对应的异常处理程序。当一个异常产生时,CPU根据异常号在异常向量表中找到对应的异常向量,然后执行异常向量处的跳转指令,CPU就跳转到对应的异常处理程序执行。 其中复位异常向量的指令“b start_code”决定了U-Boot启动后将自动跳转到标号“start_code”处执行。(2)CPU进入SVC模式start_code: /* * set the cpu to SVC32 mode */ mrs r0, cpsr bic r0, r0, #0x1f /*工作模式位清零 */ orr r0, r0, #0xd3 /*工作模式位设置为“10011”(管理模式),并将中断禁止位和快中断禁止位置1 */ msr cpsr, r0 以上代码将CPU的工作模式位设置为管理模式,并将中断禁止位和快中断禁止位置一,从而屏蔽了IRQ和FIQ中断。(3)设置控制寄存器地址# if defined(CONFIG_S3C2400)# define pWTCON 0x15300000# define INTMSK 0x14400008# define CLKDIVN 0x14800014#else /* s3c2410与s3c2440下面4个寄存器地址相同 */# define pWTCON 0x53000000 /* WATCHDOG控制寄存器地址 */# define INTMSK 0x4A000008 /* INTMSK寄存器地址 */# define INTSUBMSK 0x4A00001C /* INTSUBMSK寄存器地址 */# define CLKDIVN 0x4C000014 /* CLKDIVN寄存器地址 */# endif 对与s3c2440开发板,以上代码完成了WATCHDOG,INTMSK,INTSUBMSK,CLKDIVN四个寄存器的地址的设置。各个寄存器地址参见参考文献4 。(4)关闭看门狗 ldr r0, =pWTCON mov r1, #0x0 str 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, =INTMSK str r1, r0 INTMSK是主中断屏蔽寄存器,每一位对应SRCPND(中断源引脚寄存器)中的一位,表明SRCPND相应位代表的中断请求是否被CPU所处理。 根据参考文献4,INTMSK寄存器是一个32位的寄存器,每位对应一个中断,向其中写入0xffffffff就将INTMSK寄存器全部位置一,从而屏蔽对应的中断。# if defined(CONFIG_S3C2440) ldr r1, =0x7fff ldr r0, =INTSUBMSK str r1, r0# endif INTSUBMSK每一位对应SUBSRCPND中的一位,表明SUBSRCPND相应位代表的中断请求是否被CPU所处理。 根据参考文献4,INTSUBMSK寄存器是一个32位的寄存器,但是只使用了低15位。向其中写入0x7fff就是将INTSUBMSK寄存器全部有效位(低15位)置一,从而屏蔽对应的中断。(6)设置MPLLCON,UPLLCON, CLKDIVN# if defined(CONFIG_S3C2440) #define MPLLCON 0x4C000004#define UPLLCON 0x4C000008 ldr r0, =CLKDIVN mov r1, #5 str r1, r0 ldr r0, =MPLLCON ldr r1, =0x7F021 str r1, r0 ldr r0, =UPLLCON ldr r1, =0x38022 str r1, r0# else /* FCLK:HCLK:PCLK = 1:2:4 */ /* default FCLK is 120 MHz ! */ ldr r0, =CLKDIVN mov r1, #3 str r1, r0#endif CPU上电几毫秒后,晶振输出稳定,FCLK=Fin(晶振频率),CPU开始执行指令。但实际上,FCLK可以高于Fin,为了提高系统时钟,需要用软件来启用PLL。这就需要设置CLKDIVN,MPLLCON,UPLLCON这3个寄存器。 CLKDIVN寄存器用于设置FCLK,HCLK,PCLK三者间的比例,可以根据表2.2来设置。表 2.2 S3C2440 的CLKDIVN寄存器格式CLKDIVN位说明初始值HDIVN2:100 : HCLK = FCLK/1.01 : HCLK = FCLK/2.10 : HCLK = FCLK/4 (当 CAMDIVN9 = 0 时)HCLK= FCLK/8 (当 CAMDIVN9 = 1 时)11 : HCLK = FCLK/3 (当 CAMDIVN8 = 0 时)HCLK = FCLK/6 (当 CAMDIVN8 = 1时)00PDIVN00: PCLK = HCLK/1 1: PCLK = HCLK/20 设置CLKDIVN为5,就将HDIVN设置为二进制的10,由于CAMDIVN9没有被改变过,取默认值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) = (2mFin)/(p) 其中: m=MDIC+8,p=PDIV+2,s=SDIV MPLLCON与UPLLCON的值可以根据参考文献4中“PLL VALUE SELECTION TABLE”设置。该表部分摘录如下:表 2.3 推荐PLL值输入频率输出频率MDIVPDIVSDIV12.0000MHz48.00 MHz56(0x38)2212.0000MHz405.00 MHz127(0x7f)21 当mini2440系统主频设置为405MHZ,USB时钟频率设置为48MHZ时,系统可以稳定运行,因此设置MPLLCON与UPLLCON为: MPLLCON=(0x7f12) | (0x024) | (0x01) = 0x7f021 UPLLCON=(0x3812) | (0x024) | (0x02) = 0x38022(7)关闭MMU,cache 接着往下看:#ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit#endif cpu_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失效 */328 329 /*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到控制寄存器 */338 339 /*340 * before relocating, we have to setup RAM timing341 * because memory timing is board-dependend, you will342 * find a lowlevel_init.S in your board directory.343 */344 mov ip, lr345 346 bl lowlevel_init347 348 mov lr, ip349 mov pc, lr350 #endif /* CONFIG_SKIP_LOWLEVEL_INIT */ 代码中的c0,c1,c7,c8都是ARM920T的协处理器CP15的寄存器。其中c7是cache控制寄存器,c8是TLB控制寄存器。325327行代码将0写入c7、c8,使Cache,TLB内容无效。 第332337行代码关闭了MMU。这是通过修改CP15的c1寄存器来实现的,先看CP15的c1寄存器的格式(仅列出代码中用到的位):表 2.3 CP15的c1寄存器格式(部分)1514131211109876543210.VI.RSB.CAM 各个位的意义如下:V : 表示异常向量表所在的位置,0:异常向量在0x00000000;1:异常向量在 0xFFFF0000I : 0 :关闭ICaches;1 :开启ICachesR、S : 用来与页表中的描述符一起确定内存的访问权限B : 0 :CPU为小字节序;1 : CPU为大字节序C : 0:关闭DCaches;1:开启DCachesA : 0:数据访问时不进行地址对齐检查;1:数据访问时进行地址对齐检查M : 0:关闭MMU;1:开启MMU 332337行代码将c1的 M位置零,关闭了MMU。(8)初始化RAM控制寄存器 其中的lowlevel_init就完成了内存初始化的工作,由于内存初始化是依赖于开发板的,因此lowlevel_init的代码一般放在board下面相应的目录中。对于mini2440,lowlevel_init在board/samsung/mini2440/lowlevel_init.S中定义如下:45 #define BWSCON 0x48000000 /* 13个存储控制器的开始地址 */ 129 _TEXT_BASE:130 .word TEXT_BASE131 132 .globl lowlevel_init133 lowlevel_init:134 /* memory control configuration */135 /* make r0 relative the current location so that it */136 /* reads SMRDATA out of FLASH rather than memory ! */137 ldr r0, =SMRDATA138 ldr r1, _TEXT_BASE139 sub r0, r0, r1 /* SMRDATA减 _TEXT_BASE就是13个寄存器的偏移地址 */140 ldr r1, =BWSCON /* Bus Width Status Controller */141 add r2, r0, #13*4142 0:143 ldr r3, r0, #4 /*将13个寄存器的值逐一赋值给对应的寄存器*/144 str r3, r1, #4145 cmp r2, r0146 bne 0b147 148 /* everything is fine now */149 mov pc, lr150 151 .ltorg152 /* the literal pools origin */153 154 SMRDATA: /* 下面是13个寄存器的值 */155 .word 156 .word lowlevel_init初始化了13个寄存器来实现RAM时钟的初始化。lowlevel_init函数对于U-Boot从NAND Flash或NOR Flash启动的情况都是有效的。 U-Boot.lds链接脚本有如下代码: .text : cpu/arm920t/start.o (.text) board/samsung/mini2440/lowlevel_init.o (.text) board/samsung/mini2440/nand_read.o (.text) board/samsung/mini2440/lowlevel_init.o将被链接到cpu/arm920t/start.o后面,因此board/samsung/mini2440/lowlevel_init.o也在U-Boot的前4KB的代码中。 U-Boot在NAND Flash启动时,lowlevel_init.o将自动被读取到CPU内部4KB的内部RAM中。因此第137146行的代码将从CPU内部RAM中复制寄存器的值到相应的寄存器中。 对于U-Boot在NOR Flash启动的情况,由于U-Boot连接时确定的地址是U-Boot在内存中的地址,而此时U-Boot还在NOR Flash中,因此还需要在NOR Flash中读取数据到RAM中。 由于NOR Flash的开始地址是0,而U-Boot的
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论