logo资料库

uboot启动过程分析.pdf

第1页 / 共11页
第2页 / 共11页
第3页 / 共11页
第4页 / 共11页
第5页 / 共11页
第6页 / 共11页
第7页 / 共11页
第8页 / 共11页
资料共11页,剩余部分请下载后查看
1、uboot 的作用。 相比于 linux 操作系统,uboot 本身不大,能够自启动,作为嵌入式设备的引导 启动,是个好的选择。此外,它具有源码开放、支持多种嵌入式操作系统、丰富 的设备驱动源码等特点。 作用: 1)、为系统启动之前初始化硬件设备、为操作系统准备软件环境。 2)、引导操作系统内核启动。 2、uboot 如何启动内核、如何传参给内核? 2.1、如何启动内核? 在嵌入式设备没有上电运行前,操作系统是放在外存中的(硬盘、外部 flash、 服务器等)。设备一开始上电首先执行的是 uboot(BootLoader),uboot 的运行 有两个阶段: 第一个阶段: uboot 对硬件的初始化,为第二阶段准备运行环境。 1.建立异常向量表。 2.设置 CPU 的模式。禁止中断、cpu 设为 SVC 模式。 3.关开门狗。 4.CPU 时钟初始化。 5.内存初始化。 6.复制第二阶段的代码到内存中。 7.建立映射表并开启 MMU。 8.跳转到指定的内存执行第二阶段。 第二阶段: 初始化串口、网络硬件等,为内核设置启动参数,然后调用内核。 1.初始化 IRQ/FIQ 模式的栈。 2.设置系统时钟、初始化定时器,初始化串口。 3.检查环境参数是否有效,将环境参数读入指定的内存。 4.初始化网络设备。 5.调用内核、启动内核。 第一个阶段启动代码分析: uboot 的启动入口在 uboot/cpu/xxx/start.S 文件中. 1. reset: 2. @;mrs 3. @;bic 4. @;orr 5. @;msr 6. msr cpsr_c, #0xd3 @ I & F disable, Mode: 0x13 - SVC r0,cpsr r0,r0,#0x1f r0,r0,#0xd3 cpsr,r0 第 2-6 行禁止中断(IRQs 和 FIQs),cpu 设置成 SVC 模式。 _TEXT_BASE: 7. 8. .word TEXT_BASE 第 7-8 行的_TEXT_BASE 就是在 Makefile 中指定的 uboot 链接地址,makefile 1
中指定的 TEXT_BASE 如下: x210_sd_config : unconfig @$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110 @echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk 9. _TEXT_PHY_BASE: 10. .word CFG_PHY_UBOOT_BASE 11. bl disable_l2cache 12. bl set_l2cache_auxctrl_cycle 13. bl enable_l2cache 14. bl lowlevel_init /* go setup pll,mux,memory */ 15 ldr r0, =PRO_ID_BASE 16 ldr r1, [r0,#OMR_OFFSET] 15. bic r2, r1, #0xffffffc1 第 9-10 行_TEXT_PHY_BASE 是 uboot 的物理地址。 第 11-14 的目的就是刷新并打开 icache,然后调用 lowlevel_init。 第 15-16 行将 r2 寄存器存储一个特定值,用于指定 uboot 以何种方式来启动。 17 lowlevel_init: ...... ...... ...... /* r0 <- current base addr of code */ /* r1 <- original base addr in ram */ /* r0 <- current base addr of code */ /* compare r0, r1 /* r0 == r1 then skip sdram init */ */ 18 ldr r0, =0xff000fff 19 bic r1, pc, r0 20 ldr r2, _TEXT_BASE 21 bic r2, r2, r0 r1, r2 22 cmp 23 beq 1f 24 25 /* init system clock */ 26 bl system_clock_init 27 28 /* Memory initialize */ 29 bl mem_ctrl_asm_init 30 31 /* get ready to call C functions */ 32 ldr sp, _TEXT_PHY_BASE 33 sub sp, sp, #12 34 mov fp, #0 35 36 ldr r0, =0xff000fff 2 /* setup temp stack pointer */ /* no previous frame, so fp=0 */
/* r0 <- current base addr of code */ /* r1 <- original base addr in ram */ 37 bic r1, pc, r0 38 ldr r2, _TEXT_BASE 39 bic r2, r2, r0 40 cmp r1, r2 after_copy 41 beq /* r0 <- current base addr of code */ /* compare r0, r1 /* r0 == r1 then skip flash copy */ */ 第 18-23 行判断当前执行的代码是在 SRAM 还是在 DDR 中,如果在 SRAM 中, 那就要进行初始化 DDR,进行时钟初始化、代码的复制等,如果在 DDR 中,就可 以直接启动。 第 26 行进行时钟的初始化。 第 29 行初始化内存 DDR,在 x210_sd.h 中,起始地址的定义如下: 42. #define MEMORY_BASE_ADDRESS 43. #define MEMORY_BASE_ADDRESS2 44. #define CFG_PHY_UBOOT_BASE 0x30000000 0x40000000 MEMORY_BASE_ADDRESS + 0x3e00000 由 此 可 知 , uboot 的 可 用 物 理 地 址 为 30000000-4FFFFFFF , 共 有 512M , 30000000-3FFFFFFF 为 DMC0,40000000-4FFFFFFF 为 DMC1。 第 32-33 行设置栈,之前已经设置过一次了,那时是在 SRAM 中,SRAM 内存 空间小,现在 DDR 内存可以用了,重新分配大一些的栈空间,为执行第二阶段做 好运行环境。 第 37-41 行判断是否需要重定位,运行地址是在 SRAM 还是 DDR 中,如果在 SRAM 中,要把 uboot 的第二部分加载到 DDR 中链接地址_TEXT_BASE 处。Uboot 刚开始运行的时候,将第一阶段的代码从 SD 卡复制到 SRAM 中运行,第一阶段进 行各种初始化,给第二阶段准备好软件的运行空间后,将第二阶段复制到链接地 址执行,这过程就是重定位。通过调用 movi_bl2_copy 函数复制第二阶段的代 码,,之后建立转换表,也就是作地址映射,代码如下第 45-51 行,映射完后调 用 enable_mmu 使能 MMU。 45. /* Set the TTB register */ 46. ldr r0, _mmu_table_base 47. ldr r1, =CFG_PHY_UBOOT_BASE 48. ldr r2, =0xfff00000 49. bic r0, r0, r2 50. orr r1, r0, r1 51. mcr p15, 0, r1, c2, c0, 0 52. ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000) 第 52 行是再次设置栈,这次也是在 DDR 中,只是这次放的地方比较规范。 设置栈好后,进行清理 bss 等,然后执行 start_armboot 进入第二阶段。 3
//typedef int (init_fnc_t) (void); 第二阶段: 53. _start_armboot: 54. .word start_armboot 55. 56. init_fnc_t **init_fnc_ptr; 57. 58. #ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */ 59. ulong gd_base; 60. 61. gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - 62. CFG_STACK_SIZE - sizeof(gd_t); 63. #ifdef CONFIG_USE_IRQ 64. gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); 65. #endif 66. gd = (gd_t*)gd_base; 67. #else 68. gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); 69. #endif 70. /* compiler optimization barrier needed for GCC >= 3.4 */ 71. __asm__ __volatile__("": : :"memory"); 72. 73. memset ((void*)gd, 0, sizeof (gd_t)); 74. gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); 75. memset (gd->bd, 0, sizeof (bd_t)); 76. 77. monitor_flash_len = _bss_start - _armboot_start; 78. 79. for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) 80. { 81. if((*init_fnc_ptr)() !=0){ 82. 83. 84. } hang (); } 第 66 行的 gd 变量定义在 DECLARE_GLOBAL_DATA_PTR 宏中,#define DECLARE register volatile gd_t *gd asm ("r8"),gd 是一个 gd_t _GLOBAL_DATA_PTR 指针类型,放在 r8 寄存器中。gd_t 的结构体定义在 include/asm-arm/global _data.h 文件中。Uboot 使用它来存放很多的信息,第 86-110 行就是定义了 gd_t 结构体,它还指向了 bd_t 结构体,其定义在第 111-127 行,它有波特率参数、 DDR 内存大小、IP 地址、机器码、环境变量等。都是一些为启动内核准备内核的 参数。 第 73-75 行为结构体分配内存空间,此分配的空间是有讲究的,此指向的起 始空间通过第 68 行计算得到的。 第 79-84 行 是 进 行 一 些 列 的 初 始 化 , 波 特 率 、 串 口 、 RAM 、 中 断 等 。 4
init_sequence 是一个函数指针数组,里面存放有个初始化函数的函数名。其定 义在 uboot/lib_arm/board.c 文件中 *bd; 86. Typedef struct global_data 87. { 88. bd_t 89. 90. unsigned long flags; 91. unsigned long baudrate; 92. unsigned long have_console; /* serial_init() was called 93. unsigned long reloc_off; /* Relocation Offset */ 94. unsigned long env_addr; /*Address 95. unsigned long env_valid; /* Checksum of Environment valid? */ 96. unsigned long fb_base; 97. #ifdef 98. unsigned char vfd_type; /* display type */ 102. 103. 104. 105. 106. 107. 108. 109. 110. #endif #if 0 unsigned long cpu_clk;/* CPU clock in Hz! unsigned long bus_clk; phys_size_t unsigned long reset_status;/* reset status register at boot #endif /* base address of frame buffer */ ram_size; /* RAM size */ of Environment struct*/ **jt; /* jump table */ void } gd_t; CONFIG_VFD */ */ 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. typedef struct bd_info { *bi_env; /* serial console baudrate */ /* IP Address */ int bi_baudrate; unsigned long bi_ip_addr; unsigned char bi_enetaddr[6]; /* Ethernet adress */ struct environment_s ulong ulong struct { ulong start; ulong size; }bi_dram[CONFIG_NR_DRAM_BANKS]; bi_arch_number;/* unique id for this board */ bi_boot_params;/* where this board expects /* RAM configuration */ #ifdef /* unsigned char CONFIG_HAS_ETH1 second onboard ethernet port */ #endif } bd_t; bi_enet1addr[6]; 5
mmc_exist = mmc_initialize(gd->bd); /* initialize environment */ env_relocate (); /* IP Address gd->bd->bi_ip_addr = */ getenv_IPaddr ("ipaddr"); /****************lxg added**************/ #ifdef CONFIG_MPAD extern int x210_preboot_init(void); x210_preboot_init(); #endif /****************end**********************/ for (;;) { main_loop (); } 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 第 128 行进行初始化 uboot 的堆管理器,这样 uboot 中也可以 malloc、free 这套机制来申请内存和释放内存。 第 130 行是将环境变量从 SD 卡中读到 DDR 中,第一次运行时 SD 卡中是没有 环境变量的,uboot 是使用自定义默认的环境变量,然后将其写入 SD 中,下一 次启动时,就可以直接从 SD 卡读取环境变量了。 第 32 行获取 IP 地址,第 135-138 行进行一些初始化,以及开始显示的 LOGO。 第 141 执行 main_loop 判断用户是否执行下载模式,在 bootdelay 秒内,用 户是否有数据输入,如果没有就执行 bootcmd 命令,然后执行 do_bootm , ->do_bootm_linux->theKernel ,执行 theKernel 就启动内核了。 144. 145. 146. 147. 148. 149. 150. 151. 152. s = getenv ("bootcmd"); debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : ""); int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) do_bootm_linux (cmdtp, flag, argc, argv, &images); theKernel (0, machid, bd->bi_boot_params); 第 151 行的 theKernel 函数参数,各参数对应着在寄存器 R0、R1、R2 中存 放的,R0 = 0,R1 = 机器的 ID,R2 = 启动参数标志列表在 RAM 中的启始基地 址。 给内核传哪些参数? 在 board_init 函数中,有这样两行代码, gd->bd->bi_arch_number = MACH_TYPE; gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100); MACH_TYPE 是板子的机器码,和内核一样。bi_boot_params 就是给内核传参 的存放地址。 setup_start_tag,setup_memory_tags,setup_commandline_tag, setup_initrd_tag,setup_serial_tag,setup_revision_tag,这几个函数都会 6
标有存放的标志,告诉内核当前位置存放的是什么内容。然后调用 tag_next 函 数指向下一个内容。 #define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size)) ATAG_CORE:代码开始标志。ATAG_MEM:内存标志。ATAG_CMDLINE:命令行 标志。ATAG_NONE:结束标志。 3、uboot 的移植。 拿到源码包尝试进行编译,make_name_config,由 Readme 文档知道,根据 使用的开发板,就要执行 make_boadr_name_config 命令进行配置,对应的配置 信息在 include/configs/boadr_name.h 中,提示配置成功后,在 make 编译一 下,就会生成 3 个文件: u-boot.bin:二进制可执行文件,可以直接烧入 ROM、NOR Flash。 u-boot:ELF 格式的可执行文件 u-boot.srec:Motorola S-Record 格式的可执行文件。 一般是将 u-boot.bin 烧录 SD 卡,方便调试。进入 sd_fusing 目录下执行 sd_fusing.sh 脚本,里面可能需要给一些东西,如烧录 sd 的哪个扇区,要烧录 哪个文件名等,都是在该脚本下更改。执行./sd_fusing.sh + 设备名,就可以 烧录。 ---编译之前的工作是确保安装正确的交叉编译链工具,这个在 Makefile 中 更改,如下的第 159 行。 153. 154. 155. 156. 157. 158. 159. 160. ifeq ($(ARCH),arm) #CROSS_COMPILE = arm-linux- #CROSS_COMPILE= /usr/local/arm/4.4.1-eabi-cortex-a8/usr/bin/arm-linux- #CROSS_COMPILE= CROSS_COMPILE= /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi- Endif /usr/local/arm/4.2.2-eabi/usr/bin/arm-linux- 161. 162. 163. 164. 165. smdkv210single_config : unconfig @$(MKCONFIG)$(@:_config=)arm s5pc11x smdkc110 samsung s5pc110 @echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/smdkc110/config.mk make 配 置 的 过 程 , Makefile 中 的 代 码 161-165 行 , 配 置 命 令 “ make smdkv210single_config”,实际的作用就是执行“./mkconfig smdkv210single arm s5pc11x smdkc110 samsung s5pc110”命令,相当于执行./mkconfig $1 $2 $3 $4 $5 $6(这几个变量是在 mkconfig 文件中使用到),几个变量被赋值为: ($1=smdkv210single,$2=arm,$3=s5pc11x,$4= smdkc110,$5= samsung, $4 = s5pc110) BOARD_NAME = $1,ARCH = $2,CPU = $3,BOARD = $4,VENDOR = $5, SOC = $6。 Mkconfig 文件中的代码: 166. APPEND=no # Default: Create new config file 7
167. 168. 169. 170. 171. 172. 173. 174. 175. 176. BOARD_NAME="" # Name to print in make output while [ $# -gt 0 ] ; do case "$1" in --) shift ; break ;; -a) shift ; APPEND=yes ;; -n) shift ; BOARD_NAME="${1%%_config}" ; shift ;; *) Esac break ;; Done [ "${BOARD_NAME}" ] || BOARD_NAME="$1" 第 176 行执行后,BOARD_NAME 的值等于第一个参数,即“smdkv210single”。 这几个变量都在 mkconfig 文件中用到,就是 Makefile 那边传过来的,用来创建 板子相关的头文件等文件。 在移植的时候要根据串口打印的内容进行修改调试,哪里出错,哪里可以正 常,uboot 初始化的硬件正确,传递的参数正确的情况下,就可以正常启动内核 了。Uboot 的配置一般在 include/configs/.h 文件中,修改相关 的配置信息。有关“CONFIG_”开头是设置一些参数,设置 uboot 的功能,选用 文件中的哪一部分,而“CFG_”用来设置更为细节的参数。以下是移植三星 s5pv210 开发板的例子。 1、修改内存相关信息。 177. 178. #define SDRAM_BANK_SIZE #define MEMORY_BASE_ADDRESS 0x10000000 /* 256 MB*/ 内存大小 0x30000000 内存地 在 lowlevel_init.S (board\samsung\smdkc110)文件中,将.set __base, 0x200 修改为.set __base,0x300,此外还要修改 smdkc110 的函数,修改虚拟地 址映射表的基地址 virt_to_phy_smdkc110(ulong addr) return (addr - 0xc0000000 + 0x30000000)。 2、修改 inand 驱动问题 uboot 启动后,出现 unrecognised EXT_CSD structure version 7 的提示 错误。此提示版本错误。解决办法:在 MMC.C 文件中将 ext_csd_struct > 8(大 于 7 就行) 179. 180. 181. 182. 183. 184. 185. ext_csd_struct = ext_csd[EXT_CSD_REV]; if (ext_csd_struct > 8) { printf("unrecognised EXT_CSD structure " "version %d\n", ext_csd_struct); err = -1; goto out; } 3、串口问题 串口输出的 SD checksum error。初始化串口控制器的代码在 lowlevel_init.S 中的 uart_asm_init 中,其中初始化串口的寄存器用 ELFIN_UART_CONSOLE_BASE 宏作为串口 n 的寄存器的基地址,结合偏移量对寄存 8
分享到:
收藏