uboot源码分析

uboot源码分析


1.U-Boot系统加载器

U-Boot是一个规模庞大的开源Bootloader软件,最初是由denx(www.denx.de发起。U-Boot的前身是PPCBoot,目前是SourceForge(www.sourceforge.net)的一个项目。

最初的U-Boot仅支持PowerPC架构的系统,称做PPCBoot。从0.3.2官方版本之后开始逐步支持多种架构的处理器,目前可以支持PowerPC(MPC5xx、MPC8xx、MPC82xx、MPC7xx、MPC74xx)、ARM(ARM7、ARM9、StrongARM、Xscale)、MIPS(4kc、5kc)、X86等处理器,支持的嵌入式操作系统有Linux、Vx-Works、NetBSD、QNX、RTEMS、ARTOS、LynxOS等,是PowerPC、ARM9、Xscale、X86等系统通用的Boot方案。

U-Boot支持的处理器和操作系统很多,但是它对PowerPC系列处理器和Linux操作系统支持最好。U-Boot支持的功能也较多,对于嵌入式开发常用的查看、修改内存,从网络下载操作系统镜像等功能都提供了很好的支持。U-Boot的项目更新较快,支持的目标板众多,是学习底层开发很好的示例。

2.ViVi系统加载器

ViVi是韩国的mizi公司专门针对ARM9处理器设计的一款Bootloader。它的特点是操作简便,同时提供了完备的命令体系,目前在三星系列的ARM9处理器上ViVi也比较流行。

与U-Boot相比,由于ViVi支持的处理器单一,ViVi的代码也要小很多。同时,ViVi的软件架构和配置方法采用和Linux内核类似的风格,对于有过配置编译Linux内核经验的读者,ViVi更容易上手。

与其他的Bootloader一样,ViVi有两种工作模式:启动加载模式和下载模式。使用启动加载模式,在目标板上电后,ViVi会从预先配置好的Flash分区读取Linux或者其他系统的镜像并且启动系统;使用下载模式,ViVi向用户提供了一个命令行接口,通过该接口用户可以使用ViVi提供的命令。ViVi主要提供了5个命令如下:

Load:把二进制文件载入Flash或RAM。

Part:操作MTD分区信息。显示、增加、删除、复位、保存MTD分区。

Param:设置参数。

Boot:启动系统。

Flash:管理Flash,如删除Flash的数据。

与Linux内核的组织类似,ViVi的源代码主要包括arch、init、lib、drivers和include等几个目录,共200多个代码文件。各目录的具体功能请参考ViVi相关的信息。

=====================================================================》》》

=====================================================================》》》

=====================================================================》》》

基本目录分类:

common目录是与体系结构无关的文件,包括实现各种命令的C语言源代码文件。

cpu目录存放与CPU相关的文件,每种CPU需要的代码文件存放在以CPU名称命名的子目录下,arm920t存放了arm920t为内核的CPU相关的文件。在每个特定的子目录下都包括cpu.c、interrupt.c和start.S这3个文件,这3个文件是CPU初始化以及配置中断的代码。U-Boot自带了很多CPU相关的代码,用户可以在现有CPU支持的基础上修改自己所需要的配置。

通用设备的驱动程序存放在drivers目录下。U-Boot自带了许多设备的驱动,包括显示芯片、网络接口控制器、USB控制器、I2C器件等,对于大多数用户而言已经够用,用户也可以按照自己的需求增加或者修改设备驱动。

fs存放支持的文件系统代码,U-Boot目前支持cramfs、ext2、fat、jffs、reiserfs、yaffs等多种常见的文件系统。

net目录是与网络协议有关的代码,比如BOOTP协议、TFTP协议、RARP协议等。

post存放与硬件自检有关的代码。

rtc目录存放与硬件实时时钟相关的代码。

tools目录存放U-Boot编译过程中用到的一些工具代码。// 例如:mkimage

==========================================





hao:
start_armboot => bootm.c
向量表在_start开始阶段已经汇编搞定。

其实主要就是个寄存器和内存的基本处理。

===》》》
列出了U-Boot在ARM处理器启动过程中的几个关键点,
从图中看出U-Boot的启动代码分布在start.S、low_level_init.S、board.c和main.c文件中。

start.S是U-Boot整个程序的入口,该文件使用汇编语言编写,不同体系结构的启动代码是不同的;

low_level_init.S特定开发板的设置代码

board.c包含开发板底层设备驱动

main.c是一个与平台无关的代码,U-Boot应用程序的入口在此文件中


取出CPSR寄存器的值,CPSR寄存器保存当前系统状态,

使用比特清除命令清空了CPSR寄存器的中断控制位,表示清除中断。

设置了CPSR寄存器的处理器模式位为管理模式,然后在第117行写入CPSR的值强制切换处理器为超级保护模式。

定义看门狗控制器有关的变量,

根据平台设置看门狗定时器。

设置时钟分频寄存器的值。

需要根据CONFIG_SKIP_LOWLEVEL_INIT宏的值是否跳转到cpu_init_crit标号执行

===========cpu_init_crit==========

cpu_init_crit标号处的代码初始化ARM处理器关键的寄存器


  1. 228
  2. 238
  3. 239
  4. 240 #ifndefCONFIG_SKIP_LOWLEVEL_INIT
  5. 241cpu_init_crit:
  6. 242
  7. 245 mov r0,#0
  8. 246 mcr p15,0, r0, c7, c7, 0//1.刷新cache
  9. 247 mcr p15,0, r0, c8, c7, 0//2.刷新TLB
  10. 248
  11. 249
  12. 252 mrc p15,0, r0, c1, c0, 0
  13. 253 bic r0,r0, #0x00002300 @ clear bits13,9:8 (--V- --RS)
  14. 254 bic r0,r0, #0x00000087 @ clear bits7,2:0 (B--- -CAM)
  15. 255 orr r0,r0, #0x00000002 @ set bit2 (A) Align
  16. 256 orr r0,r0, #0x00001000 @ set bit12 (I) I-Cache
  17. 257 mcr p15,0, r0, c1, c0, 0
  18. 258
  19. 259
  20. 264 mov ip,lr
  21. 265 bllowlevel_init//跳转到lowlevel_init
  22. 266 mov lr,ip
  23. 267 mov pc,lr
  24. 268 #endif

==>2.

TLB的作用是在处理器访问内存数据的时候做地址转换。TLB的全称是TranslationLookaside Buffer,可以翻译做旁路缓冲。

TLB中存放了一些页表文件,文件中记录了虚拟地址和物理地址的映射关系。当应用程序访问一个虚拟地址的时候,会从TLB中查询出对应的物理地址,然后访问物理地址。TLB通常是一个分层结构,使用与Cache类似的原理。处理器使用一定的算法把最常用的页表放在最先访问的层次。

==>3.

程序第252~257行关闭MMU。MMU是内存管理单元(MemoryManagementUnit)的缩写。在现代计算机体系结构上,MMU被广泛应用。使用MMU技术可以向应用程序提供一个巨大的虚拟地址空间。在U-Boot初始化的时候,程序看到的地址都是物理地址,无须使用MMU。


=========================lowlevel_init=========================

位于board/smdk2410/lowlevel_init.S文件
开发板相关的初始化配置

  1. 133 lowlevel_init:
  2. 134
  3. 135
  4. 136
  5. 137 ldrr0, =SMRDATA//读取SMRDATA变量地址
  6. 138 ldr r1, _TEXT_BASE//读取_TEXT_BASE变量地址
  7. 139 subr0, r0, r1//得出相对偏移
  8. 140 ldrr1, =BWSCON//主要是了解BANK的位宽,16位
  9. 141 addr2,r0,#13*4//得到SMRDATA占用的大小,结尾处的偏移
  10. 142 0:
  11. 143 ldrr3, [r0], #4//加载SMRDATA到内存相当于一个while循环
  12. 144 strr3, [r1], #4
  13. 145 cmpr2, r0
  14. 146 bne0b//循环,相当于while循环
  15. 147
  16. 148
  17. 149 mov pc, lr
  18. 152
  19. 153
  20. 154 SMRDATA://定义SMRDATA值
  21. 155 .word(0+(B1_BWSCON<</SPAN><</SPAN>4)+(B2_BWSCON<</SPAN><</SPAN>8)+(B3_BWSCON<</SPAN><</SPAN>12)+(B4_BWSCON
  22. <</SPAN><</SPAN>16)+(B5_BWSCON<</SPAN><</SPAN>20)+(B6_BWSCON<</SPAN><</SPAN>24)+(B7_BWSCON<</SPAN><</SPAN>28))
  23. 156 .word((B0_Tacs<</SPAN><</SPAN>13)+(B0_Tcos<</SPAN><</SPAN>11)+(B0_Tacc<</SPAN><</SPAN>8)+(B0_Tcoh<</SPAN><</SPAN>6)+
  24. (B0_Tah<</SPAN><</SPAN>4)+(B0_Tacp<</SPAN><</SPAN>2)+(B0_PMC))
  25. 157 .word((B1_Tacs<</SPAN><</SPAN>13)+(B1_Tcos<</SPAN><</SPAN>11)+(B1_Tacc<</SPAN><</SPAN>8)+(B1_Tcoh<</SPAN><</SPAN>6)+
  26. (B1_Tah<</SPAN><</SPAN>4)+(B1_Tacp<</SPAN><</SPAN>2)+(B1_PMC))
  27. 158 .word((B2_Tacs<</SPAN><</SPAN>13)+(B2_Tcos<</SPAN><</SPAN>11)+(B2_Tacc<</SPAN><</SPAN>8)+(B2_Tcoh<</SPAN><</SPAN>6)+
  28. (B2_Tah<</SPAN><</SPAN>4)+(B2_Tacp<</SPAN><</SPAN>2)+(B2_PMC))
  29. 159 .word((B3_Tacs<</SPAN><</SPAN>13)+(B3_Tcos<</SPAN><</SPAN>11)+(B3_Tacc<</SPAN><</SPAN>8)+(B3_Tcoh<</SPAN><</SPAN>6)+
  30. (B3_Tah<</SPAN><</SPAN>4)+(B3_Tacp<</SPAN><</SPAN>2)+(B3_PMC))
  31. 160 .word((B4_Tacs<</SPAN><</SPAN>13)+(B4_Tcos<</SPAN><</SPAN>11)+(B4_Tacc<</SPAN><</SPAN>8)+(B4_Tcoh<</SPAN><</SPAN>6)+
  32. (B4_Tah<</SPAN><</SPAN>4)+(B4_Tacp<</SPAN><</SPAN>2)+(B4_PMC))
  33. 161 .word((B5_Tacs<</SPAN><</SPAN>13)+(B5_Tcos<</SPAN><</SPAN>11)+(B5_Tacc<</SPAN><</SPAN>8)+(B5_Tcoh<</SPAN><</SPAN>6)+
  34. (B5_Tah<</SPAN><</SPAN>4)+(B5_Tacp<</SPAN><</SPAN>2)+(B5_PMC))
  35. 162 .word((B6_MT<</SPAN><</SPAN>15)+(B6_Trcd<</SPAN><</SPAN>2)+(B6_SCAN))
  36. 163 .word((B7_MT<</SPAN><</SPAN>15)+(B7_Trcd<</SPAN><</SPAN>2)+(B7_SCAN))
  37. 164 .word((REFEN<</SPAN><</SPAN>23)+(TREFMD<</SPAN><</SPAN>22)+(Trp<</SPAN><</SPAN>20)+(Trc<</SPAN><</SPAN>18)+
  38. (Tchr<</SPAN><</SPAN>16)+REFCNT)
  39. 165 .word 0x32
  40. 166 .word 0x30
  41. 167 .word 0x30

程序第137~141行计算SMRDATA需要加载的内存地址和大小。首先在137行读取SMRDATA的变量地址,之后计算存放的内存地址并且记录在r0寄存器,然后根据总线宽度计算需要加载的SMRDATA大小,并且把加载结束地址存放在r2寄存器。

程序第142~146行复制SMRDATA到内存SMRDATA是开发板上内存映射的配置

正式开始了第二阶段:

relocate部分的代码负责把U-BootStage2的代码从Flash存储器加载到内存,代码如下:

  1. 163#ifndef CONFIG_SKIP_RELOCATE_UBOOT
  2. 164 relocate:
  3. 165 adr r0, _start
  4. //获取当前代码存放地址00000000
  5. 166 ldr r1, _TEXT_BASE
  6. //获取内存存放代码地址33f80000
  7. 167 cmpr0, r1
  8. //地址相同说明程序已经在内存中则不需要加载
  9. 168 beqstack_setup
  10. 169//开始加载
  11. 170 ldr r2,_armboot_start//获取stage2代码存放地址
  12. 171 ldr r3, _bss_start//获取内存代码段起始地址
  13. 172 sub r2, r3, r2
  14. //不包括向量表,U-BOOT的整个大小
  15. 173 add r2, r0,r2
  16. 33f80000size//计算stage2代码结束地址
  17. 174
  18. 175 copy_loop:
  19. 176 ldmia r0!,{r3-r10}
  20. //从Flash复制代码到内存
  21. 177 stmia r1!, {r3-r10}
  22. 178 cmp r0, r2
  23. 179 ble copy_loop
  24. 180 #endif
  25. 181
  26. 182
  27. //在内存中建立堆栈
  28. 183 stack_setup:
  29. 184 ldr r0, _TEXT_BASE
  30. 185 sub r0, r0,#CFG_MALLOC_LEN //分配内存区域
  31. 186 sub r0, r0,#CFG_GBL_DATA_SIZE
  32. 187 #ifdef CONFIG_USE_IRQ
  33. 188 sub r0, r0,#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
  34. 189 #endif
  35. 190 sub sp, r0,#12
  36. 191
  37. 192 clear_bss://初始化内存bss段内容为0
  38. 193 ldr r0, _bss_start
  39. //查找bss段起始地址
  40. 194 ldr r1, _bss_end//查找bss段结束地址
  41. 195 mov r2,#0x00000000//清空bss段内容
  42. 196
  43. 197 clbss_l: str r2, [r0]
  44. 198 add r0, r0,#4
  45. 199 cmp r0, r1
  46. 200 ble clbss_l
  47. 223ldr pc, _start_armboot//设置程序指针为start_armboot()函数地址
  48. 224
  49. 225 _start_armboot:
  50. .word start_armboot//这里是个C的函数名字,也就是入口地址

代码解释:

程序首先在165~168行检查当前是否在内存中执行代码,根据结果决定是否需要从Flash存储器加载代码。程序通过获取_start和_TEXT_BASE所在的地址比较,如果地址相同说明程序已经在内存中,无须加载。

程序第170~173行计算要加载的Stage2代码起始地址和长度,然后在第176~179行循环复制Flash的数据到内存,每次可以复制8个字长的数据。

Stage2程序复制完毕后,程序第184~190行设置系统堆栈,最后在第193~200行清空内存bss段的内容。

relocate程序最后在223行设置程序指针寄存器为start_armboot()函数地址,程序跳转到Stage2部分执行。请注意第225行的定义,_start_armboot全局变量的值是C语言函数start_armboot()函数的地址,使用这种方式可以在汇编中调用C语言编写的函数。

 

main_loop()函数

main_loop()函数做的都是与具体平台无关的工作,主要包括初始化启动次数限制机制、设置软件版本号、打印启动信息、解析命令等。

(1)设置启动次数有关参数。在进入main_loop()函数后,首先是根据配置加载已经保留的启动次数,并且根据配置判断是否超过启动次数。代码如下:

  1. 295void main_loop(void)
  2. 296 {
  3. 297 #ifndefCFG_HUSH_PARSER
  4. 298static charlastcommand[CFG_CBSIZE]= { 0,};
  5. 299int len;
  6. 300intrc =1;
  7. 301int flag;
  8. 302 #endif
  9. 303
  10. 304#if defined(CONFIG_BOOTDELAY)&& (CONFIG_BOOTDELAY>=0)
  11. 305char *s;
  12. 306int bootdelay;
  13. 307 #endif
  14. 308 #ifdefCONFIG_PREBOOT
  15. 309char *p;
  16. 310 #endif
  17. 311 #ifdefCONFIG_BOOTCOUNT_LIMIT
  18. 312unsigned longbootcount =0;
  19. 313unsigned longbootlimit =0;
  20. 314char *bcs;
  21. 315char bcs_set[16];
  22. 316 #endif
  23. 317
  24. 318 #ifdefined(CONFIG_VFD)&&defined(VFD_TEST_LOGO)
  25. 319ulongbmp =0;
  26. 320extern inttrab_vfd(ulong bitmap);
  27. 321
  28. 322 #ifdefCONFIG_MODEM_SUPPORT
  29. 323if (do_mdm_init)
  30. 324bmp=1;
  31. 325 #endif
  32. 326trab_vfd (bmp);
  33. 327 #endif
  34. 328
  35. 329 #ifdefCONFIG_BOOTCOUNT_LIMIT
  36. 330bootcount=bootcount_load();//加载保存的启动次数
  37. 331 bootcount++;//启动次数加1
  38. 332 bootcount_store(bootcount);//更新启动次数
  39. 333 sprintf(bcs_set, "%lu", bootcount); //打印启动次数
  40. 334 setenv("bootcount", bcs_set);
  41. 335bcs=getenv ("bootlimit");
  42. 336bootlimit=bcs ? simple_strtoul(bcs,NULL, 10) : 0;
  43. //转换启动次数字符串为UINT类型
  44. 337 #endif

第329~337行是启动次数限制功能,启动次数限制可以被用户设置一个启动次数,然后保存在Flash存储器的特定位置,当到达启动次数后,U-Boot无法启动。该功能适合一些商业产品,通过配置不同的License限制用户重新启动系统。

(2)程序第339~348行是Modem功能。如果系统中有Modem,打开该功能可以接受其他用户通过电话网络的拨号请求。Modem功能通常供一些远程控制的系统使用,代码如下:

  1. 339 #ifdef CONFIG_MODEM_SUPPORT
  2. 340 debug ("DEBUG:main_loop:do_mdm_init=%d\n",do_mdm_init);
  3. 341 if (do_mdm_init) {//判断是否需要初始化Modem
  4. 342 char*str =strdup(getenv("mdm_cmd"));//获取Modem参数
  5. 343 setenv("preboot", str);
  6. 344 if (str!= NULL)
  7. 345 free(str);
  8. 346 mdm_init();//初始化Modem
  9. 347 }
  10. 348 #endif

(3)接下来设置U-Boot的版本号初始化命令自动完成功能等。代码如下:

  1. 350#ifdef CONFIG_VERSION_VARIABLE
  2. 351{
  3. 352externcharversion_string[];
  4. 353
  5. 354setenv("ver",version_string);
  6. //设置版本号
  7. 355}
  8. 356 #endif
  9. 357
  10. 358 #ifdefCFG_HUSH_PARSER
  11. 359u_boot_hush_start();//初始化Hash功能
  12. 360 #endif
  13. 361
  14. 362#ifdefCONFIG_AUTO_COMPLETE
  15. 363install_auto_complete();//初始化命令自动完成功能
  16. 364#endif
  17. 365
  18. 366 #ifdefCONFIG_PREBOOT
  19. 367if((p =getenv("preboot"))!= NULL) {
  20. 368 #ifdef CONFIG_AUTOBOOT_KEYED
  21. 369intprev =disable_ctrlc(1);
  22. //关闭Crtl+C组合键
  23. 370 #endif
  24. 371
  25. 372 #ifndef CFG_HUSH_PARSER
  26. 373run_command(p,0); //运行Boot参数
  27. 374 #else
  28. 375parse_string_outer(p,FLAG_PARSE_SEMICOLON|
  29. 376FLAG_EXIT_FROM_LOOP);
  30. 377 #endif
  31. 378
  32. 379 #ifdef CONFIG_AUTOBOOT_KEYED
  33. 380disable_ctrlc(prev);
  34. //恢复Ctrl+C组合键
  35. 381 #endif
  36. 382}
  37. 383 #endif

程序第350~356行是动态版本号功能支持代码,version_string变量是在其他文件定义的一个字符串变量,当用户改变U-Boot版本的时候会更新该变量。打开动态版本支持功能后,U-Boot在启动的时候会显示最新的版本号。

程序第363行设置命令行自动完成功能,该功能与Linux的shell类似,当用户输入一部分命令后,可以通过按下键盘上的Tab键补全命令的剩余部分

main_loop()函数不同的功能使用宏开关控制不仅能提高代码模块化,更主要的是针对嵌入式系统Flash存储器大小设计的。在嵌入式系统上,不同的系统Flash存储空间不同。对于一些Flash空间比较紧张的设备来说,通过宏开关关闭一些不是特别必要的功能如命令行自动完成,可以减小U-Boot编译后的文件大小。

(4)在进入主循环之前,如果配置了启动延迟功能,需要等待用户从串口或者网络接口输入。如果用户按下任意键打断,启动流程,会向终端打印出一个启动菜单。代码如下:

  1. 385 #if defined(CONFIG_BOOTDELAY)&& (CONFIG_BOOTDELAY>=0)
  2. 386 s= getenv("bootdelay");
  3. 387 bootdelay= s?(int)simple_strtol(s, NULL, 10) :CONFIG_BOOTDELAY;
  4. //启动延迟
  5. 388
  6. 389 debug ("###main_loop entered:bootdelay=%d\n\n",bootdelay);
  7. 390
  8. 391 # ifdef CONFIG_BOOT_RETRY_TIME
  9. 392 init_cmd_timeout ();//初始化命令行超时机制
  10. 393 # endif
  11. 394
  12. 395 #ifdef CONFIG_BOOTCOUNT_LIMIT//一般不会检查这破玩意。
  13. 396 if (bootlimit&& (bootcount >bootlimit)){ // 检查是否超出启动次数限制
  14. 397 printf("Warning: Bootlimit (%u) exceeded.Using altbootcmd.\n",
  15. 398(unsigned)bootlimit);
  16. 399s= getenv("altbootcmd");
  17. 400 }
  18. 401 else
  19. 402 #endif
  20. 403s= getenv("bootcmd");// 获取启动命令参数
  21. 404
  22. 405 debug ("###main_loop:bootcmd=\"%s\"\n",s? s :"<</SPAN>UNDEFINED>");
  23. 406
  24. 407 if (bootdelay>= 0 &&s && !abortboot (bootdelay)){
  25. //检查是否支持启动延迟功能
  26. 408 # ifdef CONFIG_AUTOBOOT_KEYED
  27. 409 intprev =disable_ctrlc(1);
  28. //关闭Ctrl+C组合键
  29. 410 # endif
  30. 411
  31. 412 # ifndef CFG_HUSH_PARSER
  32. 413 run_command(s, 0); //运行启动命令行
  33. 414 # else
  34. 415parse_string_outer(s,FLAG_PARSE_SEMICOLON|
  35. 416FLAG_EXIT_FROM_LOOP);
  36. 417 # endif
  37. 418
  38. 419 # ifdef CONFIG_AUTOBOOT_KEYED
  39. 420disable_ctrlc(prev);
  40. //打开Ctrl+C组合键
  41. 421 # endif
  42. 422 }
  43. 423
  44. 424 # ifdef CONFIG_MENUKEY
  45. 425 if(menukey ==CONFIG_MENUKEY){ // 检查是否支持菜单键
  46. 426s=getenv("menucmd");
  47. 427 if(s) {
  48. 428 # ifndef CFG_HUSH_PARSER
  49. 429 run_command(s, 0);
  50. 430 # else
  51. 431parse_string_outer(s,FLAG_PARSE_SEMICOLON|
  52. 432FLAG_EXIT_FROM_LOOP);
  53. 433 # endif
  54. 434 }
  55. 435 }
  56. 436 #endif
  57. 437 #endif
  58. 438
  59. 439 #ifdef CONFIG_AMIGAONEG3SE
  60. 440 {
  61. 441extern voidvideo_banner(void);
  62. 442video_banner();//打印启动图标
  63. 443 }
  64. 444 #endif

(5)在各功能设置完毕后,程序第454行进入一个for死循环,该循环不断使用readline()函数(第463行)从控制台(一般是串口)读取用户的输入,然后解析。有关如何解析命令请参考U-Boot代码中run_command()函数的定义,

  1. 446
  2. 449 #ifdef CFG_HUSH_PARSER
  3. 450 parse_file_outer();
  4. 451
  5. 452 for (;;);
  6. 453 #else
  7. 454for (;;) {//进入命令行循环
  8. 455 #ifdefCONFIG_BOOT_RETRY_TIME
  9. 456 if (rc>=0) {
  10. 457
  11. 460reset_cmd_timeout();//设置命令行超时
  12. 461 }
  13. 462 #endif
  14. 463len=readline (CFG_PROMPT);//读取命令
  15. 464
  16. 465flag= 0;
  17. 466 if (len> 0)
  18. 467strcpy(lastcommand,console_buffer);
  19. 468 else if(len == 0)
  20. 469 flag|= CMD_FLAG_REPEAT;
  21. 470 #ifdefCONFIG_BOOT_RETRY_TIME
  22. 471else if(len==-2) {
  23. 472
  24. 474puts("\nTimedout waiting forcommand\n");
  25. 475 #ifdef CONFIG_RESET_TO_RETRY
  26. 476
  27. 477do_reset(NULL,0, 0, NULL);
  28. 478 #else
  29. 479return;
  30. 480 #endif
  31. 481}
  32. 482 #endif
  33. 483
  34. 484 if(len ==-1)
  35. 485 puts("<</SPAN>INTERRUPT>\n");
  36. 486 else
  37. 487rc=run_command(lastcommand,flag);//运行命令
  38. 488
  39. 489 if (rc<</SPAN>=0) {
  40. 490
  41. 491lastcommand[0]= 0;
  42. 492 }
  43. 493 }// deadloop
  44. 494 #endif
  45. 495 }

U-BOOT的功能设计 基本就在这里

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值