回味经典——uboot1.1.6 之 第二阶段 第三阶段

时间:2020-12-11 16:33:40

转自:http://blog.csdn.net/lizuobin2/article/details/52061530

上篇文章说到,再清 BSS 段之后,CPU 跳转到 sdram 里的 start_armboot() 函数,本文,分析 uboot 流程的第二阶段、第三阶段。

    start_armboot函数在lib_arm/board.c中定义,是U-Boot第二阶段代码的入口。第二阶段的主要工作是进行单板级别的初始化,初始化 nandflash 、norflash 、初始化串口、设置环境变量、最终跳转到 main_loop 里,接收串口传递进来的各种命令。

    第三阶段主要的工作就是设置 uboot 将要传递给内核的 tag 以及解析 uboot 头部里包含的信息,最终跳转到内核起始地址去执行,将主控权交给内核。


一、gd 结构体

[cpp] view plain copy
  1. /* Pointer is writable since we allocated a register for it */  
  2.     gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));  
  3.   
  4.     memset ((void*)gd, 0, sizeof (gd_t));  
  5.     gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));  
  6.     memset (gd->bd, 0, sizeof (bd_t));  
    _armboot_start 是 start.S 中的汇编语言的标号 [cpp] view plain copy
  1. .globl _armboot_start  
  2. _armboot_start:  
  3.     .word _start  
    汇编中我们可以通过不同的指令获得一个标号的地址或者标号地址处存放的数据,在 C 中,直接访问一个标号表示获取标号地址处存放的数据,一个无符号整形变量 [cpp] view plain copy
  1. extern ulong _armboot_start;    /* code start */  
  2. extern ulong _bss_start;    /* code + data end == BSS start */  
    前边第一篇文章我们分析过堆栈的划分,为 gd 结构体 留出了128K 的空间,根据上边的代码我们可以知道这 128K 有高到低先放一个 gd_t 类型的结构体,紧挨着又放了一个它的成员 bd_t 类型的结构。 [cpp] view plain copy
  1. typedef struct  global_data {  
  2.     bd_t        *bd;  
  3.     unsigned long   flags;  
  4.     unsigned long   baudrate;   //波特率  
  5.     unsigned long   have_console;   /* serial_init() was called */  
  6.     unsigned long   reloc_off;  /* Relocation Offset */  
  7.     unsigned long   env_addr;   /* Address  of Environment struct */  
  8.     unsigned long   env_valid;  /* Checksum of Environment valid? */  
  9.     unsigned long   fb_base;    /* base address of frame buffer */  
  10. #ifdef CONFIG_VFD  
  11.     unsigned char   vfd_type;   /* display type */  
  12. #endif  
  13. #if 0  
  14.     unsigned long   cpu_clk;    /* CPU clock in Hz!     */  
  15.     unsigned long   bus_clk;  
  16.     unsigned long   ram_size;   /* RAM size */  
  17.     unsigned long   reset_status;   /* reset status register at boot */  
  18. #endif  
  19.     void        **jt;           /* jump table */  
  20. } gd_t;  
[cpp] view plain copy
  1. typedef struct bd_info {  
  2.     int         bi_baudrate;    /* serial console baudrate 串口波特率*/  
  3.     unsigned long   bi_ip_addr; /* IP Address */  
  4.     unsigned char   bi_enetaddr[6]; /* Ethernet adress 以太网地址*/  
  5.     struct environment_s           *bi_env;  
  6.     ulong           bi_arch_number; /* unique id for this board 机器ID*/  
  7.     ulong           bi_boot_params; /* where this board expects params 参数区地址*/  
  8.     struct              /* RAM configuration */  
  9.     {  
  10.     ulong start;  
  11.     ulong size;  
  12.     }           bi_dram[CONFIG_NR_DRAM_BANKS];  
  13. #ifdef CONFIG_HAS_ETH1  
  14.     /* second onboard ethernet port */  
  15.     unsigned char   bi_enet1addr[6];  
  16. #endif  
  17. } bd_t;  
    前边的 gd 是个全局变量:

    #define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")


二、初始化工作

    前边分配完了 gd 结构体之后,就开始一系列的初始化工作,初始化函数定义在 init_sequence 中    

[cpp] view plain copy
  1. for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {  
  2.     if ((*init_fnc_ptr)() != 0) {  
  3.         hang ();  
  4.     }  
  5. }  
[cpp] view plain copy
  1. init_fnc_t *init_sequence[] = {  
  2.     cpu_init,       /* basic cpu dependent setup */  
  3.     board_init,     /* basic board dependent setup */  
  4.     interrupt_init,     /* set up exceptions */  
  5.     env_init,       /* initialize environment */  
  6.     init_baudrate,      /* initialze baudrate settings */  
  7.     serial_init,        /* serial communications setup */  
  8.     console_init_f,     /* stage 1 init of console */  
  9.     display_banner,     /* say that we are here */  
  10. #if defined(CONFIG_DISPLAY_CPUINFO)  
  11.     print_cpuinfo,      /* display cpu info (and speed) */  
  12. #endif  
  13. #if defined(CONFIG_DISPLAY_BOARDINFO)  
  14.     checkboard,     /* display board info */  
  15. #endif  
  16.     dram_init,      /* configure available RAM banks */  
  17.     display_dram_config,  
  18.     NULL,  
  19. };  
 2.1 cpu_init [cpp] view plain copy
  1. int cpu_init (void)  
  2. {  
  3.     /* 
  4.      * setup up stacks if necessary 
  5.      */  
  6. #ifdef CONFIG_USE_IRQ  
  7.     IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;  
  8.     FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;  
  9. #endif  
  10.     return 0;  
  11. }  
    如果使用 irq 的话,将这两个宏指向之前分配的栈空间

  2.2 board_init

[cpp] view plain copy
  1. int board_init (void)  
  2. {  
  3.     //获得时钟寄存器的地址,s3c24x0通用无需修改。  
  4.     S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();  
  5.       
  6.     //获得GPIO寄存器的地址,s3c24x0不通用需要增加2440GPIO寄存器。  
  7.     S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();  
  8.   
  9.     /* to reduce PLL lock time, adjust the LOCKTIME register */  
  10.     clk_power->LOCKTIME = 0xFFFFFF;  
  11.   
  12.     /* configure MPLL */  
  13.     /*MPLL初始化 参数位于./board/smdk2410/smdk2410.c中,2440应有自己的文件以及定义*/  
  14.     clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);  
  15.   
  16.     /* some delay between MPLL and UPLL */  
  17.     delay (4000);  
  18.   
  19.     /* configure UPLL */  
  20.     clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);  
  21.   
  22.     /* some delay between MPLL and UPLL */  
  23.     delay (8000);  
  24.   
  25.     /* set up the I/O ports */  
  26.     gpio->GPACON = 0x007FFFFF;  
  27.     gpio->GPBCON = 0x00044555;  
  28.     gpio->GPBUP = 0x000007FF;  
  29.     gpio->GPCCON = 0xAAAAAAAA;  
  30.     gpio->GPCUP = 0x0000FFFF;  
  31.     gpio->GPDCON = 0xAAAAAAAA;  
  32.     gpio->GPDUP = 0x0000FFFF;  
  33.     gpio->GPECON = 0xAAAAAAAA;  
  34.     gpio->GPEUP = 0x0000FFFF;  
  35.     gpio->GPFCON = 0x000055AA;  
  36.     gpio->GPFUP = 0x000000FF;  
  37.     gpio->GPGCON = 0xFF95FFBA;  
  38.     gpio->GPGUP = 0x0000FFFF;  
  39.     gpio->GPHCON = 0x002AFAAA;  
  40.     gpio->GPHUP = 0x000007FF;  
  41.   
  42.     /* arch number of SMDK2410-Board */  
  43.     gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;  
  44.     //#define MACH_TYPE_SMDK2410 193  位于./include/asm-asm/Mach-types.h中定义  
  45.   
  46.     /* adress of boot parameters */  
  47.     gd->bd->bi_boot_params = 0x30000100; //内核启动参数存放地址  
  48.   
  49.     //开数据 指令cache  
  50.     icache_enable();  
  51.     dcache_enable();  
  52.   
  53.     return 0;  
  54. }  
    重点工作,向 gd 结构体中记录了 机器ID 以及 tag 的存放地址,这俩都是要传递给内核的!!!

  2.3 interrupt_init PWM的初始化,无关紧要

  2.4 env_init 

[cpp] view plain copy
  1. typedef struct environment_s {  
  2.     unsigned long   crc;        /* CRC32 over data bytes    */  
  3.     unsigned char   flags;      /* active/obsolete flags    */  
  4.     unsigned char   data[ENV_SIZE]; /* Environment data     */  
  5. } env_t;  
[cpp] view plain copy
  1. env_t *env_ptr = (env_t *)CFG_ENV_ADDR; // 0+0x070000  
  2. static env_t *flash_addr = (env_t *)CFG_ENV_ADDR;   // 0+0x070000  
    环境变量用一个 environment_s 结构来描述,它被放置在 norflash 的 0x70000 起始的地址处,crc 是环境变量的校验和,全部的环境变量都以字符串的形式存放在 data 数组中,两个环境变量之间用 “\0” 隔开。 [cpp] view plain copy
  1. int  env_init(void)  
  2. {     
  3.     if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {  
  4.         gd->env_addr  = (ulong)&(env_ptr->data);  
  5.         gd->env_valid = 1;  
  6.         return(0);  
  7.     }  
  8.     //如果校验失败,则使用默认的环境变量  
  9.     gd->env_addr  = (ulong)&default_environment[0]; //将默认环境变量的地址赋给全局指针gd->env_addr  
  10.     gd->env_valid = 0;  
  11.     return (0);  
  12. }  
    现在env_ptr指向 Norfalsh 中的0x070000,进行校验,判断环境变量是否可用。

    1、uboot 第一次启动,那么 norflash 这个地址处并没有任何东西,校验失败,则使用默认的环境变量,使全局指针 gd->env_addr 指向内存中的默认环境变量,并设置标志位 gd->env_valid 为 0 。

    2、uboot 非第一次启动,那么校验成功,将全局指针 gd->env_addr 指向环境变量,并使标志位 gd->env_valid 置一。
    默认环境变量的 定义 CONFIG_BOOTARGS 等宏在 Smdk2410.h (include\configs)

[cpp] view plain copy
  1. uchar default_environment[] = {  
  2. #ifdef  CONFIG_BOOTARGS  
  3.     "bootargs=" CONFIG_BOOTARGS         "\0"  
  4. #endif  
  5. #ifdef  CONFIG_BOOTCOMMAND  
  6.     "bootcmd="  CONFIG_BOOTCOMMAND      "\0"  
  7. #endif  
  8. #ifdef  CONFIG_RAMBOOTCOMMAND  
  9.     "ramboot="  CONFIG_RAMBOOTCOMMAND       "\0"  
  10. #endif  
  11. #ifdef  CONFIG_NFSBOOTCOMMAND  
  12.     "nfsboot="  CONFIG_NFSBOOTCOMMAND       "\0"  
  13. #endif  
  14. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)  
  15.     "bootdelay="    MK_STR(CONFIG_BOOTDELAY)    "\0"  
  16. #endif  
  17. #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)  
  18.     "baudrate=" MK_STR(CONFIG_BAUDRATE)     "\0"  
  19. #endif  
  20. #ifdef  CONFIG_LOADS_ECHO  
  21.     "loads_echo="   MK_STR(CONFIG_LOADS_ECHO)   "\0"  
  22. #endif  
  23. #ifdef  CONFIG_ETHADDR  
  24.     "ethaddr="  MK_STR(CONFIG_ETHADDR)      "\0"  
  25. #endif  
  26. #ifdef  CONFIG_ETH1ADDR  
  27.     "eth1addr=" MK_STR(CONFIG_ETH1ADDR)     "\0"  
  28. #endif  
  29. #ifdef  CONFIG_ETH2ADDR  
  30.     "eth2addr=" MK_STR(CONFIG_ETH2ADDR)     "\0"  
  31. #endif  
  32. #ifdef  CONFIG_ETH3ADDR  
  33.     "eth3addr=" MK_STR(CONFIG_ETH3ADDR)     "\0"  
  34. #endif  
  35. #ifdef  CONFIG_IPADDR  
  36.     "ipaddr="   MK_STR(CONFIG_IPADDR)       "\0"  
  37. #endif  
  38. #ifdef  CONFIG_SERVERIP  
  39.     "serverip=" MK_STR(CONFIG_SERVERIP)     "\0"  
  40. #endif  
  41. #ifdef  CFG_AUTOLOAD  
  42.     "autoload=" CFG_AUTOLOAD            "\0"  
  43. #endif  
  44. #ifdef  CONFIG_PREBOOT  
  45.     "preboot="  CONFIG_PREBOOT          "\0"  
  46. #endif  
  47. #ifdef  CONFIG_ROOTPATH  
  48.     "rootpath=" MK_STR(CONFIG_ROOTPATH)     "\0"  
  49. #endif  
  50. #ifdef  CONFIG_GATEWAYIP  
  51.     "gatewayip="    MK_STR(CONFIG_GATEWAYIP)    "\0"  
  52. #endif  
  53. #ifdef  CONFIG_NETMASK  
  54.     "netmask="  MK_STR(CONFIG_NETMASK)      "\0"  
  55. #endif  
  56. #ifdef  CONFIG_HOSTNAME  
  57.     "hostname=" MK_STR(CONFIG_HOSTNAME)     "\0"  
  58. #endif  
  59. #ifdef  CONFIG_BOOTFILE  
  60.     "bootfile=" MK_STR(CONFIG_BOOTFILE)     "\0"  
  61. #endif  
  62. #ifdef  CONFIG_LOADADDR  
  63.     "loadaddr=" MK_STR(CONFIG_LOADADDR)     "\0"  
  64. #endif  
  65. #ifdef  CONFIG_CLOCKS_IN_MHZ  
  66.     "clocks_in_mhz=1\0"  
  67. #endif  
  68. #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)  
  69.     "pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY)    "\0"  
  70. #endif  
  71. #ifdef  CONFIG_EXTRA_ENV_SETTINGS  
  72.     CONFIG_EXTRA_ENV_SETTINGS  
  73. #endif  
  74.     "\0"  
  75. };  
    以 "bootargs="  CONFIG_BOOTARGS  "\0" 为例:

    #define CONFIG_BOOTARGS    "root=ramfs devfs=mount console=ttySA0,9600"
    确实如同前边说所,data 里边放置的都是一个个字符串,两个环境变量之间 通过 “\0”隔开。

  2.5 init_baudrate 设置波特率 首先从环境变量中读取,如果没有则设置为 115200

  2.6 serial_init 串口初始化

[cpp] view plain copy
  1. int serial_init (void)  
  2. {  
  3.     serial_setbrg ();  
  4.   
  5.     return (0);  
  6. }  
[cpp] view plain copy
  1. void serial_setbrg (void)  
  2. {  
  3.     S3C24X0_UART * const uart = S3C24X0_GetBase_UART(UART_NR);  
  4.     int i;  
  5.     unsigned int reg = 0;  
  6.   
  7.     /* value is calculated so : (int)(PCLK/16./baudrate) -1 */  
  8.     reg = get_PCLK() / (16 * gd->baudrate) - 1;  
  9.   
  10.     /* FIFO enable, Tx/Rx FIFO clear */  
  11.     uart->UFCON = 0x07;  
  12.     uart->UMCON = 0x0;  
  13.     /* Normal,No parity,1 stop,8 bit */  
  14.     uart->ULCON = 0x3;  
  15.     /* 
  16.      * tx=level,rx=edge,disable timeout int.,enable rx error int., 
  17.      * normal,interrupt or polling 
  18.      */  
  19.     uart->UCON = 0x245;  
  20.     uart->UBRDIV = reg;  
  21.   
  22. #ifdef CONFIG_HWFLOW  
  23.     uart->UMCON = 0x1; /* RTS up */  
  24. #endif  
  25.     for (i = 0; i < 100; i++);  
  26. }  
    串口控制寄存器的设置.....以前移植的时候遇到一个问题,get_PCLK() 这个函数需要修改,2410 和 2440 不一样。

  2.7 console_init_f 控制台初始化,无关紧要

  2.8 display_banner 打印代码段 BSS段等地址信息

  2.9 dram_init 

[cpp] view plain copy
  1. int dram_init (void)  
  2. {  
  3.     gd->bd->bi_dram[0].start = PHYS_SDRAM_1;  
  4.     gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;  
  5.   
  6.     return 0;  
  7. }  
    在全局指针 gd 中标记内存范围

  2.A display_dram_config 打印 sdram 信息

  2.B norflash 初始化

[cpp] view plain copy
  1. #ifndef CFG_NO_FLASH  
  2.     /* configure available FLASH banks */  
  3.     size = flash_init ();  
  2.C nandflash 初始化 [cpp] view plain copy
  1. #if (CONFIG_COMMANDS & CFG_CMD_NAND)  
  2.     puts ("NAND:  ");  
  3.     nand_init();        /* go init the NAND */  
 2.D 环境变量重定位 [cpp] view plain copy
  1. /* initialize environment */  
  2. env_relocate ();  
[cpp] view plain copy
  1. void env_relocate (void)  
  2. {  
  3.     DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,  
  4.         gd->reloc_off);  
  5.   
  6.       
  7.     //在sdram中开辟一开内存空间并使env_ptr指向它  
  8.     env_ptr = (env_t *)malloc (CFG_ENV_SIZE);  
  9.     DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);  
  10.   
  11.     /* 
  12.      * After relocation to RAM, we can always use the "memory" functions 
  13.      */  
  14.     env_get_char = env_get_char_memory;  
  15.   
  16.     if (gd->env_valid == 0) // 开始的时候使用的默认环境变量,前边提过了  
  17.         puts ("*** Warning - bad CRC, using default environment\n\n");  
  18.         SHOW_BOOT_PROGRESS (-1);  
  19.   
  20.   
  21.         if (sizeof(default_environment) > ENV_SIZE)  
  22.         {  
  23.             puts ("*** Error - default environment is too large\n\n");  
  24.             return;  
  25.         }  
  26.         //向sdram中的env_ptr地址拷贝默认的默认环境变量。  
  27.         memset (env_ptr, 0, sizeof(env_t));//写之前 先初始化  
  28.         memcpy (env_ptr->data,  
  29.             default_environment,  
  30.             sizeof(default_environment));  
  31. #ifdef CFG_REDUNDAND_ENVIRONMENT  
  32.         env_ptr->flags = 0xFF;  
  33. #endif  
  34.         env_crc_update ();  //env_ptr->crc写入校验值  
  35.         gd->env_valid = 1;   //告诉全局gd env可用  
  36.     }  
  37.     else {  
  38.         //如果,使用的非默认的env,也就是 nor中有  
  39.         env_relocate_spec ();   //将norflash中的env拷贝到sdram  
  40.     }  
  41.     gd->env_addr = (ulong)&(env_ptr->data); //将sdram中 env->data地址告诉gd  
  42.   
  43. #ifdef CONFIG_AMIGAONEG3SE  
  44.     disable_nvram();  
  45. #endif  
  46. }  
  47.   
  48. #ifdef CONFIG_AUTO_COMPLETE  
[cpp] view plain copy
  1. void env_relocate_spec (void)  
  2. {  
  3.     //注意次函数从env_relocate()中调用时,env_ptr已经改变为新在内存中开辟的地址  
  4.     //因此次函数功能:将Nor中的env,拷贝到sdram  
  5.     memcpy (env_ptr, (void*)flash_addr, CFG_ENV_SIZE);  
  6. }  
    重定位之后,我们在uboot命令行读取到的环境变量都是内存中的,修改后要写回 norflash [cpp] view plain copy
  1. #ifdef CMD_SAVEENV  
  2.   
  3. int saveenv(void)  
  4. {  
  5.     int len, rc;  
  6.     ulong   end_addr;  
  7.     ulong   flash_sect_addr;  
  8.   
  9.     uchar *env_buffer = (uchar *)env_ptr;//指向flash中的 0+0x070000  
  10.   
  11.     int rcode = 0;  
  12.   
  13.     flash_sect_addr = (ulong)flash_addr; //flash_sect_addr保存的是env_ptr的地址,也就是0x070000  
  14.     len  = CFG_ENV_SIZE;  
  15.   
  16.     end_addr = flash_sect_addr + len - 1;   //env的末地址  
  17.   
  18.   
  19.     debug ("Protect off %08lX ... %08lX\n",  
  20.         (ulong)flash_sect_addr, end_addr);  
  21.     //后边是一些 擦除 写操作,可以看出nandflash的擦除写都是按sect来的,那么env在Nar中的偏移地址肯定得是sect对齐的。  
  22.     if (flash_sect_protect (0, flash_sect_addr, end_addr))  
  23.         return 1;  
  24.   
  25.     puts ("Erasing Flash...");  
  26.     if (flash_sect_erase (flash_sect_addr, end_addr))  
  27.         return 1;  
  28.   
  29.     puts ("Writing to Flash... ");  
  30.     rc = flash_write((char *)env_buffer, flash_sect_addr, len);  
  31.     if (rc != 0) {  
  32.         flash_perror (rc);  
  33.         rcode = 1;  
  34.     } else {  
  35.         puts ("done\n");  
  36.     }  
  37.   
  38.     /* try to re-protect */  
  39.     (void) flash_sect_protect (1, flash_sect_addr, end_addr);  
  40.     return rcode;  
  41. }  
 2.E 从环境变量中获取 ip mac 地址放入全局 gd 结构 [cpp] view plain copy
  1. /* IP Address */  
  2.     gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");  
  3.   
  4.     /* MAC Address */  
  5.     {  
  6.         int i;  
  7.         ulong reg;  
  8.         char *s, *e;  
  9.         char tmp[64];  
  10.   
  11.         i = getenv_r ("ethaddr", tmp, sizeof (tmp));  
  12.         s = (i > 0) ? tmp : NULL;  
  13.   
  14.         for (reg = 0; reg < 6; ++reg) {  
  15.             gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;  
  16.             if (s)  
  17.                 s = (*e) ? e + 1 : e;  
  18.         }  
  19.   
  20. #ifdef CONFIG_HAS_ETH1  
  21.         i = getenv_r ("eth1addr", tmp, sizeof (tmp));  
  22.         s = (i > 0) ? tmp : NULL;  
  23.   
  24.         for (reg = 0; reg < 6; ++reg) {  
  25.             gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;  
  26.             if (s)  
  27.                 s = (*e) ? e + 1 : e;  
  28.         }  
    有意思的是,这里用了一个{ },大概是为了防止变量名字冲突?

  2.F devices_init (); 没研究过,不知道干啥的,应该没啥影响

  2.G jumptable_init () 

[cpp] view plain copy
  1. void jumptable_init (void)  
  2. {  
  3.     int i;  
  4.   
  5.     gd->jt = (void **) malloc (XF_MAX * sizeof (void *));  
  6.     for (i = 0; i < XF_MAX; i++)  
  7.         gd->jt[i] = (void *) dummy;  
  8.   
  9.     gd->jt[XF_get_version] = (void *) get_version;  
  10.     gd->jt[XF_malloc] = (void *) malloc;  
  11.     gd->jt[XF_free] = (void *) free;  
  12.     gd->jt[XF_getenv] = (void *) getenv;  
  13.     gd->jt[XF_setenv] = (void *) setenv;  
  14.     gd->jt[XF_get_timer] = (void *) get_timer;  
  15.     gd->jt[XF_simple_strtoul] = (void *) simple_strtoul;  
  16.     gd->jt[XF_udelay] = (void *) udelay;  
  17. #if defined(CONFIG_I386) || defined(CONFIG_PPC)  
  18.     gd->jt[XF_install_hdlr] = (void *) irq_install_handler;  
  19.     gd->jt[XF_free_hdlr] = (void *) irq_free_handler;  
  20. #endif  /* I386 || PPC */  
  21. #if (CONFIG_COMMANDS & CFG_CMD_I2C)  
  22.     gd->jt[XF_i2c_write] = (void *) i2c_write;  
  23.     gd->jt[XF_i2c_read] = (void *) i2c_read;  
  24. #endif  /* CFG_CMD_I2C */  
    在全局结构 gd 中记录 这些函数,不太懂。

  2.H enable_interrupts 在 cpsr 中使能 irq fiq

[cpp] view plain copy
  1. void enable_interrupts (void)  
  2. {  
  3.     unsigned long temp;  
  4.     __asm__ __volatile__("mrs %0, cpsr\n"  
  5.                  "bic %0, %0, #0x80\n"  
  6.                  "msr cpsr_c, %0"  
  7.                  : "=r" (temp)  
  8.                  :  
  9.                  : "memory");  
  10. }  
  2.I 网卡初始化 [cpp] view plain copy
  1. #if (CONFIG_COMMANDS & CFG_CMD_NET)  
  2. #if defined(CONFIG_NET_MULTI)  
  3.     puts ("Net:   ");  
  4. #endif  
  5.     eth_initialize(gd->bd);  
 2.J 跳转到 uboot 菜单 [cpp] view plain copy
  1. for (;;) {  
  2.     main_loop ();  
  3. }  


三、第三阶段 启动内核

    我们在 uboot 中输入 bootm 命令,U-Boot接收输入的字符串“bootm”,传递给 run_command 函数。run_command 函数调用 common/command.c 中实现的 find_cmd 函数在 __u_boot_cmd_start 与 __u_boot_cmd_end间查找命令,并返回 bootm 命令的 cmd_tbl_t 结构。然后 run_command 函数使用返回的 cmd_tbl_t 结构中的函数指针调用 bootm 命令的响应函数 do_bootm。

[cpp] view plain copy
  1. int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])  
  2. {  
  3.     ......  
  4.     image_header_t *hdr = &header;  //uimage 是内核加了一个4K的头部,这个头部的内容是按照结构体image_header_t来放在,是image传递给Uboot的信息。  
  5.     ......  
  6.     if (argc < 2) {  
  7.         addr = load_addr;   //如果bootm的参数小于2 则使用默认的加载地址  
  8.     } else {  
  9.         addr = simple_strtoul(argv[1], NULL, 16);  
  10.     }  
  11.     ......  
  12.     switch (hdr->ih_comp) {  
  13.     case IH_COMP_NONE:  
  14.         if(ntohl(hdr->ih_load) == addr) {      
  15. /* 
  16.     这里判断“uimage头部里指定的加载地址”与bootm指定的加载地址是否相等,不相等则需要移动 
  17.     判断的方式有两种 
  18.     1、判断 uimage头部里指定的加载地址 == bootm指定的加载地址 (hdr->ih_load == addr) 
  19.     此时: 
  20.         实际存放的地址  == uimage加载地址 == uimage连接地址-64字节 
  21.         bootm == 实际存放的地址 
  22.     例子: 
  23.         实际存放在  0x30007fc0 
  24.         bootm       0x30007fc0 
  25.         加载地址    0x30007fc0 
  26.         连接地址    0x30008000 
  27.         1、uboot根据Bootm指定的0x30007fc0去找image,实际地址为0x30007fc0,找到头部 
  28.         2、读出头部里的加载地址,判断是否和Bootm相等,相等则不移动,启动 
  29.     2、判断 uimage头部里指定的加载地址 == bootm指定的加载地址 + 64字节 (hdr->ih_load == addr+64字节) 
  30.     此时:  
  31.         实际存放地址+64字节 == uimage加载地址 == uimage连接地址 
  32.         bootm == 实际存放的地址 
  33.     例子:  
  34.         实际存放在  0x30007fc0 
  35.         bootm       0x30007fc0 
  36.         加载地址    0x30008000 
  37.         连接地址    0x30008000 
  38.         1、uboot根据Bootm指定的0x30007fc0去找image,实际地址为0x30007fc0,找到头部 
  39.         2、读出头部里的加载地址,判断是否和Bootm + 字节相等,相等则不移动,启动 
  40.      
  41.     首先bootm的地址要和我们 实际存放(不管它的加载地址是多少) 整个uimage的首地址吻合,这样就可以找到头部。 
  42.     这里存在两种情况,我们可以看到 Uboot源码里 
  43.     1、 hdr->ih_load == addr  
  44.     也就是说判断的是bootm_addr 与uimage里的ih_load加载地址是否相等,这样的话,我们在制作uimage的时候就应该让ih_load=bootmaddr 
  45.     那么uimage里的另一个参数,连接地址就应该等于bootm+4K 
  46.     2、hdr->ih_load == addr+64字节  友善以及韦东山老师的uboot里判断条件都被改成了这样 
  47.     那么,uimage里的Load地址和连接地址应该相等,等于bootm+64字节 
  48. */  
  49.             printf ("   XIP %s ... ", name);  
  50.         } else {  
  51.     ......do_bootm_linux  (cmdtp, flag, argc, argv,addr, len_ptr, verify);  
  52. }  
[cpp] view plain copy
  1. lib_arm/armlinux.c  
  2. void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],  
  3.              ulong addr, ulong *len_ptr, int verify)  
  4. {  
  5.     ulong len = 0, checksum;  
  6.     ulong initrd_start, initrd_end;  
  7.     ulong data;  
  8.     /* 启动内核的函数指针 */  
  9.     void (*theKernel)(int zero, int arch, uint params);  
  10.     image_header_t *hdr = &header;  
  11.     bd_t *bd = gd->bd;  
  12.     /* 获得命令行参数 */  
  13. #ifdef CONFIG_CMDLINE_TAG  
  14.     char *commandline = getenv ("bootargs");  
  15. #endif  
  16.   
  17.     theKernel = (void (*)(intint, uint))ntohl(hdr->ih_ep)  
  18.   
  19. #if defined (CONFIG_SETUP_MEMORY_TAGS) || \  
  20.     defined (CONFIG_CMDLINE_TAG) || \  
  21.     defined (CONFIG_INITRD_TAG) || \  
  22.     defined (CONFIG_SERIAL_TAG) || \  
  23.     defined (CONFIG_REVISION_TAG) || \  
  24.     defined (CONFIG_LCD) || \  
  25.     defined (CONFIG_VFD)  
  26.     setup_start_tag (bd);   // 标记必须以它起始  
  27.     /* 在/include/config/smdk2410.h这些宏一个都没定义,因此,uboot一个标记都没给内核放,这样是不对滴,最起码的,内存标记以及cmdline应该告诉内核。不然跳到内核后调用串口驱动的初始化,会让你的串口啥都打印不出来。关于什么是 tag 以及原理,在自己写bootloader一文中介绍过了。 
  28.      */  
  29. #ifdef CONFIG_SERIAL_TAG  
  30.     setup_serial_tag (¶ms);  
  31. #endif  
  32. #ifdef CONFIG_REVISION_TAG  
  33.     setup_revision_tag (¶ms);  
  34. #endif  
  35. #ifdef CONFIG_SETUP_MEMORY_TAGS  
  36.     setup_memory_tags (bd);     /* 必须放 */  
  37. #endif  
  38. #ifdef CONFIG_CMDLINE_TAG  
  39.     setup_commandline_tag (bd, commandline);    /* 必须放 */  
  40. #endif  
  41. #ifdef CONFIG_INITRD_TAG  
  42.     if (initrd_start && initrd_end)  
  43.         setup_initrd_tag (bd, initrd_start, initrd_end);  
  44. #endif  
  45. #if defined (CONFIG_VFD) || defined (CONFIG_LCD)  
  46.     setup_videolfb_tag ((gd_t *) gd);  
  47. #endif  
  48.     setup_end_tag (bd); /* 标记必须以它结尾 */  
  49. #endif  
  50.   
  51.     /* 这句话很常见~~~也经常从此没了下文~~~ */  
  52.     printf ("\nStarting kernel ...\n\n");  
  53.   
  54.     cleanup_before_linux ();  
  55.     /* 启动内核 第1个参数 0 ,第二个参数 机器ID 第三个参数 tag 地址 */  
  56.     theKernel (0, bd->bi_arch_number, bd->bi_boot_params);  
  57. }  
    至此,uboot 的生命周期结束,控制权交给内核~


四、添加自定义命令

  下面以添加menu命令(启动菜单)为例讲解U-Boot添加命令的方法。
    1、建立common/cmd_menu.c
    习惯上通用命令源代码放在common目录下,与开发板专有命令源代码则放在board/<board_dir>目录下,并且习惯以“cmd_<命令名>.c”为文件名。

    2、定义“menu”命令
      在 cmd_menu.c中使用如下的代码定义“menu”命令:参考 cmd_bootm.c

[cpp] view plain copy
  1. U_BOOT_CMD(  
  2.     bootm,  CFG_MAXARGS,    1,  do_bootm,  
  3.     "bootm   - boot application image from memory\n",  
  4.     "[addr [arg ...]]\n    - boot application image stored in memory\n"  
  5.     "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"  
  6.     "\t'arg' can be the address of an initrd image\n"  
  7. #ifdef CONFIG_OF_FLAT_TREE  
  8.     "\tWhen booting a Linux kernel which requires a flat device-tree\n"  
  9.     "\ta third argument is required which is the address of the of the\n"  
  10.     "\tdevice-tree blob. To boot that kernel without an initrd image,\n"  
  11.     "\tuse a '-' for the second argument. If you do not pass a third\n"  
  12.     "\ta bd_info struct will be passed instead\n"  
  13. #endif  
  14. );  
[cpp] view plain copy
  1. U_BOOT_CMD(  
  2.     menu,    3,    0,    do_menu,  
  3.     "menu - display a menu, to select the items to do something\n",  
  4.     " - display a menu, to select the items to do something"  
  5. );  
    U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)
    各个参数的意义如下:
        name:命令名,非字符串,但在U_BOOT_CMD中用“#”符号转化为字符串
        maxargs:命令的最大参数个数
        rep:是否自动重复(按Enter键是否会重复执行)
        cmd:该命令对应的响应函数
        usage:简短的使用说明(字符串)
        help:较详细的使用说明(字符串)
    U_BOOT_CMD宏在include/command.h中定义,分为有无帮助信息两种:

[cpp] view plain copy
  1. #define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))  
  2.   
  3. #ifdef  CFG_LONGHELP  
  4.   
  5. #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \  
  6. cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}  
  7.   
  8. #else   /* no long help info */  
  9.   
  10. #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \  
  11. cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}  
  12.   
  13. #endif  /* CFG_LONGHELP */  
    定义一个 cmd_tbl_t 类型的结构体,比如 bootm 名字叫 __u_boot_cmd_bootm 放在 .u_boot_cmd 段。执行命令时就可以在“u_boot_cmd”段查找到对应的 cmd_tbl_t 类型结构体。
[cpp] view plain copy
  1. struct cmd_tbl_s {  
  2.        char              *name;          /* 命令名 */  
  3.        int          maxargs;       /* 最大参数个数 */  
  4.        int          repeatable;    /* 是否自动重复 */  
  5.        int          (*cmd)(struct cmd_tbl_s *, intintchar *[]);  /*  响应函数 */  
  6.        char              *usage;         /* 简短的帮助信息 */  
  7. #ifdef    CONFIG_SYS_LONGHELP  
  8.        char              *help;           /*  较详细的帮助信息 */  
  9. #endif  
  10. #ifdef CONFIG_AUTO_COMPLETE  
  11.   
  12.        /* 自动补全参数 */  
  13.        int          (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);  
  14. #endif  
  15. };  
  16. typedef struct cmd_tbl_s  cmd_tbl_t;  
    一个cmd_tbl_t结构体变量包含了调用一条命令的所需要的信息。

  3、实现命令的函数
    在cmd_menu.c中添加“menu”命令的响应函数的实现。具体的实现代码略:

[cpp] view plain copy
  1. int do_menu (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])  
  2. {  
  3.     /* 实现代码略 */  
  4. }  
  4、将common/cmd_menu.c编译进u-boot.bin
    在common/Makefile中加入如下代码:
        COBJS-$(CONFIG_BOOT_MENU) += cmd_menu.o
    在include/configs/smdk2410.h加入如代码:
        #define CONFIG_BOOT_MENU 1
    重新编译下载U-Boot就可以使用menu命令了