uboot学习笔记(三)启动源码分析之第二阶段(c语言部分)

   uboot学习笔记(三)启动源码分析之第二阶段(c语言部分)

/**
* ../u-boot-2009.08\lib_arm
* called by cpu/arm926ejs/start.S
*uboot 第二阶段代码的开始。
*start_armboot函数是uboot从flash跳转到RAM执行的第一个函数
*/

 
void start_armboot (void)
{
       init_fnc_t**init_fnc_ptr;
       char*s;
 
#if defined(CONFIG_VFD) ||defined(CONFIG_LCD)
       unsignedlong addr;
#endif
 
       /*gd指向uboot全局环境变量配置结构体,*/
       /*Pointer is writable since we allocated a register for it */
       gd= (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
      
       /*内存屏障,这句话告诉编译器,不要优化代码*/
       /*告诉编译器寄存器和cache缓存的值都无效了,需要重新访问内存*/
       /*它会把当前寄存器的值重新回写(同步)到内存,需要的时候再从内存读出*/
       /*"":: :"memory"  汇编指令,此处表示空语句*/
       /*Memory描述符告知GCC:
    1)不要将该段内嵌汇编指令与前面的指令重新排序;也就是在执行内嵌汇编代码之前,它前面的指令都执行完毕
    2)不要将变量缓存到寄存器,因为这段代码可能会用到内存变量,而这些内存变量会以不可预知的方式发生改变,
        因此GCC插入必要的代码先将缓存到寄存器的变量值写回内存,如果后面又访问这些变量,需要重新访问内存。
 
       如果汇编指令修改了内存,但是GCC 本身却察觉不到,因为在输出部分没有描述,
       此时就需要在修改描述部分增加"memory",告诉GCC 内存已经被修改,GCC 得知这个信息后,
       就会在这段指令之前,插入必要的指令将前面因为优化Cache 到寄存器中的变量值先写回内存,
       如果以后又要使用这些变量再重新读取。
 
    使用"volatile"也可以达到这个目的,但是我们在每个变量前增加该关键字,不如使用"memory"方便*/
       /*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));
       memset(gd->bd, 0, sizeof (bd_t));
 
       gd->flags|= GD_FLG_RELOC;//标记表示已经重定向到内存
       /*uboot的 code + data的大小*/     
       monitor_flash_len= _bss_start - _armboot_start;
       /*遍历init_sequence数组的函数指针,执行硬件初始化函数*/
       /*初始化硬件包含cpu,timer,interrput,console,dram,i2c...*/
       for(init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
              if((*init_fnc_ptr)() != 0) {
                     hang();  //失败处理
              }
       }
 
       /*清malloc堆区,为malloc库函数做准备,不然malloc函数不能用*/
       /*armboot_start is defined in the board-specific linker script */
       mem_malloc_init(_armboot_start - CONFIG_SYS_MALLOC_LEN);
 
#ifndef CONFIG_SYS_NO_FLASH
       /*configure available FLASH banks */
       display_flash_config(flash_init ());
#endif /* CONFIG_SYS_NO_FLASH */
       /*初始化显示缓存*/
#ifdef CONFIG_VFD
#     ifndefPAGE_SIZE
#       define PAGE_SIZE 4096
#     endif
       /*
        * reserve memory for VFD display (always fullpages)
        */
       /*bss_end is defined in the board-specific linker script */
       addr= (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
       vfd_setmem(addr);
       gd->fb_base= addr;
#endif /* CONFIG_VFD */
 
#ifdef CONFIG_LCD
       /*board init may have inited fb_base */
       if(!gd->fb_base) {
#  ifndefPAGE_SIZE
#define PAGE_SIZE 4096
# endif
              /*
               * reserve memory for LCD display (always fullpages)
               */
              /*bss_end is defined in the board-specific linker script */
              addr= (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
              lcd_setmem(addr);
              gd->fb_base= addr;
       }
#endif /* CONFIG_LCD */
/*nand初始化*/
#if defined(CONFIG_CMD_NAND)
       puts("NAND:  ");
       nand_init();           /* go init the NAND */
#endif
 
#if defined(CONFIG_CMD_ONENAND)
       onenand_init();
#endif
 
#ifdef CONFIG_HAS_DATAFLASH
       AT91F_DataflashInit();
       dataflash_print_info();
#endif
/*sd 卡支持*/
#ifdef CONFIG_GENERIC_MMC
       puts("MMC:   ");
       mmc_initialize(gd->bd);
#endif
/*重定位环境变量把flash中保存的环境变量拷贝到ram的堆区,并赋值给gd->env_addr地址*/
/*见common/env_common.c 下的void env_relocate (void)*/
/*见common/env_flash.c下的void env_relocate_spec (void)*/
       /*initialize environment */
       env_relocate(); 
#ifdef CONFIG_VFD
       /*must do this after the framebuffer is allocated */
       drv_vfd_init();
#endif /* CONFIG_VFD */
 
#ifdef CONFIG_SERIAL_MULTI
       serial_initialize();
#endif
       //ddr2信息
       getddr2_information();
 
       /*IP Address */
       gd->bd->bi_ip_addr= getenv_IPaddr ("ipaddr");
 
       stdio_init();   /* get the devices list going. */
 
       jumptable_init();
 
#if defined(CONFIG_API)
       /*Initialize API */
       api_init();
#endif
       /*初始化终端 as a device*/
       console_init_r();    /* fully init console as a device*/
 
#if defined(CONFIG_ARCH_MISC_INIT)
       /*miscellaneous arch dependent initialisations */
       arch_misc_init();
#endif
#if defined(CONFIG_MISC_INIT_R)
       /*miscellaneous platform dependent initialisations */
       misc_init_r();
#endif
       /*第一阶段汇编代码关闭了中断,现在打开*/
       /*enable exceptions */
       enable_interrupts();
 
       /*Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_TI_EMAC
       /*XXX: this needs to be moved to board init */
extern void davinci_eth_set_mac_addr (constu_int8_t *addr);
       if(getenv ("ethaddr")) {
              ucharenetaddr[6];
              eth_getenv_enetaddr("ethaddr",enetaddr);
              davinci_eth_set_mac_addr(enetaddr);
       }
#endif
 
#ifdef CONFIG_DRIVER_CS8900
       /*XXX: this needs to be moved to board init */
       cs8900_get_enetaddr();
#endif
 
#if defined(CONFIG_DRIVER_SMC91111) ||defined (CONFIG_DRIVER_LAN91C96)
       /*XXX: this needs to be moved to board init */
       if(getenv ("ethaddr")) {
              ucharenetaddr[6];
              eth_getenv_enetaddr("ethaddr",enetaddr);
              smc_set_mac_addr(enetaddr);
       }
#endif /* CONFIG_DRIVER_SMC91111 ||CONFIG_DRIVER_LAN91C96 */
 
#if defined(CONFIG_ENC28J60_ETH) &&!defined(CONFIG_ETHADDR)
       externvoid enc_set_mac_addr (void);
       enc_set_mac_addr();
#endif /* CONFIG_ENC28J60_ETH &&!CONFIG_ETHADDR*/
 
       /*Initialize from environment */
       if((s = getenv ("loadaddr")) != NULL) {
              load_addr= simple_strtoul (s, NULL, 16);
       }
#if defined(CONFIG_CMD_NET)
       if((s = getenv ("bootfile")) != NULL) {
              copy_filename(BootFile, s, sizeof (BootFile));
       }
#endif
 
#ifdef BOARD_LATE_INIT
       board_late_init();
#endif
 
#ifdef CONFIG_ANDROID_RECOVERY
       check_recovery_mode();
#endif
 
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
       puts("Net:   ");
#endif
       eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
       debug("Reset Ethernet PHY\n");
       reset_phy();
#endif
#endif
       /*初始化到此结束,死循环与用户交互界面common/main.c */
       /*main_loop() can return to retry autoboot, if so just run it again. */
       for(;;) {
//主循环main_loop 实现 bootdelay延时时间到之前无输入,则加载bootcmd进入自启动模式;如果有输入则进入交互模式
              main_loop();
       }
 
       /*NOTREACHED - no way out of command loop except booting */
}
 
void main_loop (void)
{
 
#if defined(CONFIG_BOOTDELAY) &&(CONFIG_BOOTDELAY >= 0)
       s= getenv ("bootdelay");
       bootdelay= s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
 
       debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);
 
# ifdef CONFIG_BOOT_RETRY_TIME
       init_cmd_timeout();
# endif    /*CONFIG_BOOT_RETRY_TIME */
 
              //获取自启动命令如 bootcmd = tftp 20008000zImage ; go zImage 表示从网络加载内核运行,否则进入交互模式
 
              s= getenv ("bootcmd");
 
       debug("### main_loop: bootcmd=\"%s\"\n", s ? s :"");
       //如果延时大于等于零,并且没有在延时过程中接收到按键,则引导内核。进入自启动模式
       /*abortboot做倒计时功能*/
       if(bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
              intprev = disable_ctrlc(1);    /* disableControl C checking */
# endif
 
# ifndef CONFIG_SYS_HUSH_PARSER
              run_command(s, 0); //执行命令s
# else
              parse_string_outer(s,FLAG_PARSE_SEMICOLON |
                               FLAG_EXIT_FROM_LOOP);
# endif
       ……
       }
 
       /*
        * Main Loopfor Monitor Command Processing
        */
       For(;;){
              ……;
}
}
 
U_BOOT_CMD(
       bootm,    CONFIG_SYS_MAXARGS, 1,    do_bootm,
       "bootapplication image from memory",
       "[addr[arg ...]]\n    - boot application imagestored in memory\n"
       "\tpassingarguments 'arg ...'; when booting a Linux kernel,\n"
       "\t'arg'can be the address of an initrd image\n"
}
 
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc,char *argv[])
{
       ulong             iflag;
       ulong             load_end = 0;
       int          ret;
       boot_os_fn     *boot_fn;
 
       /*relocate boot function table */
       if(!relocated) {
              inti;
              for(i = 0; i < ARRAY_SIZE(boot_os); i++)
                     if(boot_os[i] != NULL)
                            boot_os[i]+= gd->reloc_off;
              relocated= 1;
       }
 
       /*determine if we have a sub command */
       if(argc > 1) {
              char*endp;
 
              simple_strtoul(argv[1],&endp, 16);
              /*endp pointing to NULL means that argv[1] was just a
               * valid number, pass it along to the normalbootm processing
               *
               * If endp is ':' or '#' assume a FITidentifier so pass
               * along for normal processing.
               *
               * Right now we assume the first arg shouldnever be '-'
               */
              if((*endp != 0) && (*endp != ':') && (*endp != '#'))
                     returndo_bootm_subcommand(cmdtp, flag, argc, argv);
       }
//用于获取内核头信息,起始地址和长度。
/*bootm_start会调用boot_get_kernel函数找内核映象文件的起始地址load_addr的头信息,然后解析。* 内核地址定义在一个宏:ulong load_addr =CONFIG_SYS_LOAD_ADDR; /* Default Load Address */
       if(bootm_start(cmdtp, flag, argc, argv))
              return1;
 
       /*
        * We have reached the point of no return: weare going to
        * overwrite all exception vector code, so wecannot easily
        * recover from any failures any more...
        */
       iflag= disable_interrupts();
 
/*搬移内核到内存*/
/*从image_start拷贝到load地址*/
       ret= bootm_load_os(images.os, &load_end, 1);
       ……
 
#ifdef CONFIG_SILENT_CONSOLE
       if(images.os.os == IH_OS_LINUX)
              fixup_silent_linux();
#endif
       /*根据系统类型找boot回调函数*/
       boot_fn= boot_os[images.os.os];
       ……
/*这里实际上是调用do_bootm_linux函数启动内核*/
       boot_fn(0,argc, argv, &images);
 
       do_reset(cmdtp, flag, argc, argv);
 
       return1;
}
 
二、添加自己的UBOOT命令
typedefstruct cmd_tbl_s cmd_tbl_t;
//定义 section 属性的结构体。编译的时候会单独生成一个名为.u_boot_cmd 的 section 段
#defineStruct_Section __attribute__ ((unused,section (".u_boot_cmd")))
//这个宏定义一个命令结构体变量。并用name,maxargs,rep,cmd,usage,help 初始化各个域。。。 。
#defineU_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 如何添加一个命令?
1) CFG_CMD_xxx 命令选项位标志 在include/cmd_confdefs.h 中定义 每个板子的配置文件 如include/config/smdk2410.h) 中都可以定义 u-boot 需要的命令,如果要添加一个命令 ,必须添加相应的命令选项如下:
 #define CONFIG_COMMANDS /
 (CONFIG_CMD_DFL | /
 CFG_CMD_CACHE | /
 /*CFG_CMD_NAND |*/ /
 /*CFG_CMD_EEPROM |*/ /
/*CFG_CMD_I2C|*/ /
 /*CFG_CMD_USB |*//
 CFG_CMD_REGINFO | /
 CFG_CMD_DATE | /
 CFG_CMD_ELF)
定义这个选项主要是为了编译命令需要的源文件 ,大部分命令都在都在common 文件夹下对应一个源文件 cmd_*.c
例如: cmd_cache.c 实现 cache 命令 文件开头就有一行编译条件:
#if(CONFIG_COMMANDS&CFG_CMD_CACHE)
 也就是说 如果配置头文件中CONFIG_COMMANDS 不或上相应命令的选项这里就不会被编译 。
 
2)定义命令结构体变量 如 :
U_BOOT_CMD
(dcache, 2, 1, do_dcache,
 "dcache - enable or disable data cache/n",
"[on,off]/n"
 " - enable or disable data (writethrough)cache/n"
);
其实就是定义了一个 cmd_tbl_t 类型的结构体变量,这个结构体变量名为 __u_boot_cmd_dcache。 其中变量的五个域初始化为括号的内容。分别指明了命令名 参数个数 重复数 ,执行命令的函数命令提示每个命令都对应这样一个变量 ,同时这个结构体变量的section 属性为.u_boot_cmd.也就是说每个变量编译结束在目标文件中都会有一个.u_boot_cmd 的 section.一个 section 是连接时的一个输入段 ,如.text,.bss,.data等都是 section 名 最后由链接程序把所有的.u_boot_cmd段连接在一起 ,这样就组成了一个命令结构体数组 u-boot.lds 中相应脚本如下:. = .; __u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) }__u_boot_cmd_end = .; 可以看到所有的命令结构体变量集中在__u_boot_cmd_start 开始到__u_boot_cmd_end 结束的连续地址范围内 这样形成一个cmd_tbl_t 类型的数组 ,run_command函数就是在这个数组中查找命令的 函数并执行
 
3)实现命令处理函数 。
命令处理函数的格式: void function (cmd_tbl_t*cmdtp, int flag, int argc, char *argv[])
总体来说,如果要实现自己的命令,应该在应该在 include/com_confdefs.h中定义一个命令选项标志位。在板子的配置文件中添加命令自己的选项。按照 u-boot的风格,可以在 common/下面添加自己的 cmd_*.c,并且定义自己的命令结构体变量,如 U_BOOT_CMD( mycommand, 2, 1,do_mycommand, "my command!/n", ".../n" " ../n" );然后实现自己的命令处理函数 do_mycommand(cmd_tbl_t *cmdtp,int flag, int argc, char *argv[])。
 
三、UBOOT重要结构体
 
UBOOT重要结构体
/*==========uboot结构体 gd和 bd===================*/
//在gd中部署bd空间
//--include/asm-arm.u/u-boot.h
typedef struct bd_info {   /*板级配置信息结构体 bd*/
              int                bi_baudrate;        /* 串口通讯波特率 */
              unsignedlong     bi_ip_addr;          /* 板子自己IP 地址  对应环境变量 ipaddr*/
              structenvironment_s        *bi_env;   /* 环境变量开始地址 */
              ulong            bi_arch_number;      /* 开发板的机器码 */
              ulong            bi_boot_params;      /* 内核参数的开始地址 */  见board/samsung/fsc100/fsc100.c   设置它的开发板的机器码和内核启动参数地址=PHYS_SDRAM_1+0x100=0x20000000+0x100  见include/configs/fsc100.h  
                                                                            
//在传递参数到内核时采用了param_struct方式:  见common/cmd_boot.c go命令的优化(xx自己改的)
              struct                         /* RAM配置信息 */
              {
                      ulong start;
                      ulong size;
              }bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t
 
 
typedef    struct      global_data { /*gd结构体*/
       bd_t        *bd;
       unsignedlong flags;             /*重定位标志*/
       unsignedlong baudrate;  /*波特率*/  
       unsignedlong have_console; /* serial_init() was called */
       unsignedlong env_addr;       /* 环境参数结构体地址 */
       unsignedlong env_valid;      /* 环境参数CRC效验 */
       unsignedlong fb_base;  /* base address of frame buffer */
#ifdef CONFIG_VFD
       unsignedchar  vfd_type; /* display type */
#endif
#ifdef CONFIG_FSL_ESDHC
       unsignedlong sdhc_clk;
#endif
#if 0
       unsignedlong cpu_clk;  /* CPU clock in Hz!             */
       unsignedlong bus_clk;
       phys_size_t     ram_size; /*RAM size */
       unsignedlong reset_status;    /* reset status register at boot */
#endif
       void        **jt;        /*jump table 跳转表,函数调用登记*/
} gd_t;/*include/asm-arm/global.h*/


你可能感兴趣的:(bootloader)