在上一章节的[U-boot启动过程]中,我们分析到U-boot倒计时后的参数选择,这一章节我们将对各参数选择后执行的具体功能代码进行分析,让我们更深入的理解代码在内存、Flash之间的搬运过程。
再来查看下各数值代表的含义
0: System Load Linux then write to Flash via Serial. 1: Load system code to SDRAM via TFTP. 2:Load system code then write to Flash via TFTP. 3: Boot system code via Flash (default). 4: Entr boot command line interface. 7: Load Boot Loader code then write to Flash via Serial. 8: System Load UBoot to SDRAM via TFTP. 9: Load Boot Loader code then write to Flash via TFTP.
将其分类下,其实就很明了:
两种文件需要更新:U-boot、System
两种更新方式:Serial、tftp
更新到两个位置:SDRAM、Flash
数值3:从Flash启动系统
数值4:进入U-boot命令行模式
1.进入U-boot命令行模式
case '4': printf(" \n%d: System Enter Boot Command Line Interface.\n", SEL_ENTER_CLI); printf ("\n%s\n", version_string); /* main_loop() can return to retry autoboot, if so just run it again. */ for (;;) { main_loop (); } break;
case 4就是循环执行main_loop()函数,该函数位于common/main.c中
main_loop函数就是一直检测用户所输入的命令,然后进行解析执行对于的代码,那哪些命令才可以被解析正确呢,如果用户想自己加入一个命令呢?
这边使用的是U_BOOT_CMD宏定义,如ping命令的定义如下:
U_BOOT_CMD( ping, 2, 1, do_ping, "ping\t- send ICMP ECHO_REQUEST to network host\n", "pingAddress\n" );
U_BOOT_CMD的定义如下,第一个参数name即要输入的命令,maxargs为最大长度,rep表示可重复性,cmd为输入命令后索要执行的函数如这边的do_ping()函数,usage/help都是提示信息。
define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \ cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
知道U_BOOT_CMD的定义后,我们就知道如何添加自己的命令,以及输入每个命令的具体代码在哪里寻找。
2.两种
上面总结了以下两种:
两种文件需要更新:U-boot、System
两种更新方式:Serial、tftp
更新到两个位置:SDRAM、Flash
解释下就是通过Serial/tftp的方式将U-boot/System烧录到SDRAM/Flash,所以也很直观。
我们先来看下两种方式即Serial和tftp,当选择不同的方式时,就会选择不同的接口函数。
观察代码我们可以发现:
当选择tftp模式时,先通过
tftp_config()
函数设置网络环境,然后再通过do_tftpb()
函数进行数据的上传。当选择serial模式时,则通过
do_load_serial_bin(()
函数进行数据的上传。
这边先普及一个知识,我们数据的转移过程是先通过tftp/serial从本地上传到运行内存SDRAM中,然后在从SDRAM写入介质Flash中,所以上面的这两个接口函数都是将文件上传到运行内存SDRAM中。
两种方式知道了,那接着看两个位置SDRAM和Flash。
SDRAM上面已经说明了,通过
do_tftpb()
和do_load_serial_bin(()
函数即可。由于使用的是spi flash,所以写入Flash则使用接口函数
raspi_erase_write()
来实现。
既然更新方式和更新位置知道了,那剩下的更新文件U-boot和System是如何区分的,细心的你可能会发现在调用do_tftpb()
、do_load_serial_bin(()
、raspi_erase_write()
时都是有带不同的参数的,这些参数就是用来区分我们U-boot/System所要存放的具体地址。
在include/configs/rt2880.h中有如下定义:
#define CFG_BOOTLOADER_SIZE 0x20000 #define CFG_CONFIG_SIZE 0x10000 #define CFG_FACTORY_SIZE 0x10000 #define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_BOOTLOADER_SIZE) #define CFG_FACTORY_ADDR (CFG_FLASH_BASE + CFG_BOOTLOADER_SIZE + CFG_CONFIG_SIZE) #define CFG_KERN_ADDR (CFG_FLASH_BASE + (CFG_BOOTLOADER_SIZE + CFG_CONFIG_SIZE + CFG_FACTORY_SIZE)) #ifdef DUAL_IMAGE_SUPPORT #define CFG_KERN2_ADDR (CFG_FLASH2_BASE + (CFG_BOOTLOADER_SIZE + CFG_CONFIG_SIZE + CFG_FACTORY_SIZE))
可以很直观的看出各信息在Flash中的存放位置,Uboot-config-factory-kernel。
3.从Flash启动系统
if(BootType == '3') { char *argv[2]; sprintf(addr_str, "0x%X", CFG_KERN_ADDR); argv[1] = &addr_str[0]; printf(" \n3: System Boot system code via Flash.\n"); do_bootm(cmdtp, 0, 2, argv); }
启动系统也是比较直观的,调用do_bootm()函数,该函数位于common/cmd_bootm.c中,do_bootm()函数将地址参数传输进去,里面通过raspi_read()函数进行读取特定的位置到内存,然后执行,即所说的启动系统,当然在启动的过程会有一系列的验证等动作。
U-boot启动数值具体说明的分析就到这边,有感悟时会持续会更新。