u-boot的重要细节

主要分析流程中各函数的功能。按启动顺序罗列一下启动函数执行细节。按照函数start_armboot流程进行分析:
    1)DECLARE_GLOBAL_DATA_PTR;
     这个宏定义在include/global_data.h中:
     #define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")
     声明一个寄存器变量 gd 占用r8。这个宏在所有需要引用全局数据指针gd_t *gd的源码中都有申明。
     这个申明也避免编译器把r8分配给其它的变量. 所以gd就是r8,这个指针变量不占用内存。
    2)gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
    对全局数据区进行地址分配,_armboot_start为0x3f000000,CFG_MALLOC_LEN是堆大小+环境数据区大小,config/smdk2410.h中CFG_MALLOC_LEN大小定义为192KB.
    3)gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
    分配板子数据区bd首地址。
    这样结合start.s中栈的分配,
    stack_setup:
    ldr r0, _TEXT_BASE  /* upper 128 KiB: relocated uboot   */
    sub r0, r0, #CFG_MALLOC_LEN /* malloc area                      */
    sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfoCFG_GBL_DATA_SIZE =128B */
    #ifdef CONFIG_USE_IRQ
    sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
    #endif
    sub sp, r0, #12  /* leave 3 words for abort-stack    */
  不难得出上文所述的内存分配结构。
  下面几个函数是初始化序列表init_sequence[]中的函数:
   4)cpu_init();定义于cpu/arm920t/cpu.c
  分配IRQ,FIQ栈底地址,由于没有定义CONFIG_USE_IRQ,所以相当于空实现。
   5)board_init;极级初始化,定义于board/smdk2410/smdk2410.c
   设置PLL时钟,GPIO,使能I/D cache.
    设置bd信息:gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;//板子的ID,没啥意义。
           gd->bd->bi_boot_params = 0x30000100;//内核启动参数存放地址
     6)interrupt_init;定义于cpu/arm920t/s3c24x0/interrupt.c
     初始化2410的PWM timer 4,使其能自动装载计数值,恒定的产生时间中断信号,但是中断被屏蔽了用不上。
     7)env_init;定义于common/env_flash.c(搜索的时候发现别的文件也定义了这个函数,而且没有宏定义保证只有一个被编译,这是个问题,有高手知道指点一下!)
  功能:指定环境区的地址。default_environment是默认的环境参数设置。
   gd->env_addr  = (ulong)&default_environment[0];
   gd->env_valid = 0;
8)init_baudrate;初始化全局数据区中波特率的值
  gd->bd->bi_baudrate = gd->baudrate =(i > 0)
   ? (int) simple_strtoul (tmp, NULL, 10)
   : CONFIG_BAUDRATE;
    9)serial_init; 串口通讯设置 定义于cpu/arm920t/s3c24x0/serial.c
     根据bd中波特率值和pclk,设置串口寄存器。
    10)console_init_f;控制台前期初始化common/console.c
    由于标准设备还没有初始化(gd->flags & GD_FLG_DEVINIT=0),这时控制台使用串口作为控制台
    函数只有一句:gd->have_console = 1;
    10)dram_init,初始化内存RAM信息。board/smdk2410/smdk2410.c
    其实就是给gd->bd中内存信息表赋值而已。
    gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
 gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
 初始化序列表init_sequence[]主要函数分析结束。
   11)flash_init;定义在board/smdk2410/flash.c
   这个文件与具体平台关系密切,smdk2410使用的flash与FS2410不一样,所以移植时这个程序就得重写。
   flash_init()是必须重写的函数,它做哪些操作呢?
   首先是有一个变量flash_info_t flash_info[CFG_MAX_FLASH_BANKS]来记录flash的信息。 flash_info_t定义
   typedef struct {
    ulong size;   /* 总大小BYTE  */
    ushort sector_count;  /* 总的sector数*/
    ulong flash_id;  /* combined device & manufacturer code */
    ulong start[CFG_MAX_FLASH_SECT];   /* 每个sector的起始物理地址。 */
    uchar protect[CFG_MAX_FLASH_SECT]; /* 每个sector的保护状态,如果置1,在执行erase操作的时候将跳过对应sector*/
     #ifdef CFG_FLASH_CFI //我不管CFI接口。
    .....
     #endif
   } flash_info_t;
    flash_init()的操作就是读取ID号,ID号指明了生产商和设备号,根据这些信息设置size,sector_count,flash_id.以及start[]、protect[]。
    12)把视频帧缓冲区设置在bss_end后面。
     addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
   size = vfd_setmem (addr);
   gd->fb_base = addr;
   13)mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
   设置heap区,供malloc使用。下面的变量和函数定义在lib_arm/board.c
   malloc可用内存由mem_malloc_start,mem_malloc_end指定。而当前分配的位置则是mem_malloc_brk。
   mem_malloc_init负责初始化这三个变量。malloc则通过sbrk函数来使用和管理这片内存。
    static ulong mem_malloc_start = 0;
    static ulong mem_malloc_end = 0;
    static ulong mem_malloc_brk = 0;
    static
    void mem_malloc_init (ulong dest_addr)
    {
     mem_malloc_start = dest_addr;
     mem_malloc_end = dest_addr + CFG_MALLOC_LEN;
     mem_malloc_brk = mem_malloc_start;
   
     memset ((void *) mem_malloc_start, 0,
       mem_malloc_end - mem_malloc_start);
    }
    void *sbrk (ptrdiff_t increment)
    {
     ulong old = mem_malloc_brk;
     ulong new = old + increment;
   
     if ((new < mem_malloc_start) || (new > mem_malloc_end)) {
      return (NULL);
     }
     mem_malloc_brk = new;
     return ((void *) old);
    }
14)env_relocate() 环境参数区重定位
  由于初始化了heap区,所以可以通过malloc()重新分配一块环境参数区,
  但是没有必要,因为默认的环境参数已经重定位到RAM中了。
  /**这里发现个问题,ENV_IS_EMBEDDED是否有定义还没搞清楚,而且CFG_MALLOC_LEN也没有定义,也就是说如果ENV_IS_EMBEDDED没有定义则执行malloc,是不是应该有问题?**/
   15)IP,MAC地址的初始化。主要是从环境中读,然后赋给gd->bd对应域就OK。
   16)devices_init ();定义于common/devices.c
  int devices_init (void)//我去掉了编译选项,注释掉的是因为对应的编译选项没有定义。
   {
     devlist = ListCreate (sizeof (device_t));//创建设备列表
    i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE);//初始化i2c接口,i2c没有注册到devlist中去。
    //drv_lcd_init ();
    //drv_video_init ();
    //drv_keyboard_init ();
    //drv_logbuff_init ();
    drv_system_init ();  //这里其实是定义了一个串口设备,并且注册到devlist中。
    //serial_devices_init ();
    //drv_usbtty_init ();
    //drv_nc_init ();
   }
  经过devices_init(),创建了devlist,但是只有一个串口设备注册在内。显然,devlist中的设备都是可以做为console的。
16) jumptable_init ();初始化gd->jt。1.1.6版本的jumptable只起登记函数地址的作用。并没有其他作用。
17)console_init_r ();后期控制台初始化
     主要过程:查看环境参数stdin,stdout,stderr中对标准IO的指定的设备名称,再按照环境指定的名称搜索devlist,将搜到的设备指针赋给标准IO数组stdio_devices[]。置gd->flag标志GD_FLG_DEVINIT。这个标志影响putc,getc函数的实现,未定义此标志时直接由串口serial_getc和serial_putc实现,定义以后通过标准设备数组stdio_devices[]中的putc和getc来实现IO。
下面是相关代码:
    void putc (const char c)
         {
         #ifdef CONFIG_SILENT_CONSOLE
          if (gd->flags & GD_FLG_SILENT)//GD_FLG_SILENT无输出标志
           return;
         #endif
          if (gd->flags & GD_FLG_DEVINIT) {//设备list已经初始化
           /* Send to the standard output */
           fputc (stdout, c);
          } else {
           /* Send directly to the handler */
           serial_putc (c);//未初始化时直接从串口输出。
          }
         }
       void fputc (int file, const char c)
        {
         if (file < MAX_FILES)
          stdio_devices[file]->putc (c);
        }
为什么要使用devlist,std_device[]?
为了更灵活地实现标准IO重定向,任何可以作为标准IO的设备,如USB键盘,LCD屏,串口等都可以对应一个device_t的结构体变量,只需要实现getc和putc等函数,就能加入到devlist列表中去,也就可以被assign为标准IO设备std_device中去。如函数
int console_assign (int file, char *devname); /* Assign the console 重定向标准输入输出*/
这个函数功能就是把名为devname的设备重定向为标准IO文件file(stdin,stdout,stderr)。其执行过程是在devlist中查找devname的设备,返回这个设备的device_t指针,并把指针值赋给std_device[file]。

18)enable_interrupts(),使能中断。由于CONFIG_USE_IRQ没有定义,空实现。
    #ifdef CONFIG_USE_IRQ
    /* enable IRQ interrupts */
    void enable_interrupts (void)
    {
     unsigned long temp;
     __asm__ __volatile__("mrs %0, cpsr\n"
            "bic %0, %0, #0x80\n"
            "msr cpsr_c, %0"
            : "=r" (temp)
            :
            : "memory");
    }
    #else
        void enable_interrupts (void)
    {  
    }  
  19)设置CS8900的MAC地址。
  cs8900_get_enetaddr (gd->bd->bi_enetaddr);  
   20)初始化以太网。
  eth_initialize(gd->bd);//bd中已经IP,MAC已经初始化
  21)main_loop ();定义于common/main.c
  至此所有初始化工作已经完毕。main_loop在标准转入设备中接受命令行,然后分析,查找,执行。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值