U-Boot启动过程源码分析(3)-启动Linux

本文详细介绍了U-Boot如何通过设置标记列表向Linux内核传递启动参数,并最终调用内核的过程。包括ATAG_CORE、ATAG_MEM等标记的作用及设置方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

U-Boot启动Linux过程
U-Boot使用标记列表(tagged list)的方式向Linux传递参数。标记的数据结构式是tag,在U-Boot源代码目录include/asm-arm/setup.h中定义如下:

struct tag_header {
       u32 size;       /* 表示tag数据结构的联合u实质存放的数据的大小*/
       u32 tag;        /* 表示标记的类型 */
};

struct tag {
       struct tag_header hdr;
       union {
              struct tag_core           core;
              struct tag_mem32      mem;
              struct tag_videotext   videotext;
              struct tag_ramdisk     ramdisk;
              struct tag_initrd  initrd;
              struct tag_serialnr       serialnr;
              struct tag_revision      revision;
              struct tag_videolfb     videolfb;
              struct tag_cmdline     cmdline;
              struct tag_acorn  acorn;  / * Acorn specific */
              struct tag_memclk      memclk;  / * DC21285 specific   */
       } u;
};

U-Boot使用命令bootm来启动已经加载到内存中的内核(bootm是从内存、ROM、NOR Flash中启动内核;bootp从网络启动;nboot从NAND Flash启动他们都是先把内核映像从各种媒介中读出,放在指定位置,然后设置标记列表给内核传递参数;最后跳到内核入口去执行)。而bootm命令实际上调用的是do_bootm函数。对于Linux内核,do_bootm函数会调用do_bootm_linux函数来设置标记列表和启动内核。do_bootm_linux函数在lib_arm/bootm.c 中定义如下:

int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
{
     bd_t       *bd = gd->bd;
     char       *s;
     int   machid = bd->bi_arch_number;
     void (*theKernel)(int zero, int arch, uint params); 
#ifdef CONFIG_CMDLINE_TAG
     char *commandline = getenv ("bootargs");   /* U-Boot环境变量bootargs */
#endif
       … …
    theKernel = (void (*)(int, int, uint))images->ep; 
    /* 获取内核入口地址 */
       … …
#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
    defined (CONFIG_CMDLINE_TAG) || \
    defined (CONFIG_INITRD_TAG) || \
    defined (CONFIG_SERIAL_TAG) || \
    defined (CONFIG_REVISION_TAG) || \
    defined (CONFIG_LCD) || \
    defined (CONFIG_VFD)
    setup_start_tag (bd);                                     /* 设置ATAG_CORE标志 */
       … …
#ifdef CONFIG_SETUP_MEMORY_TAGS
    setup_memory_tags (bd);        /* 设置内存标记 */
#endif
#ifdef CONFIG_CMDLINE_TAG
    setup_commandline_tag (bd, commandline); 
     /* 设置命令行标记 */
#endif
       … …
    setup_end_tag (bd);    /* 设置ATAG_NONE标志 */          
#endif
  /* we assume that the kernel is in place */
    printf ("\nStarting kernel ...\n\n");
       … …
    cleanup_before_linux ();          /* 启动内核前对CPU作最后的设置 */
    theKernel (0, machid, bd->bi_boot_params);     
         /* 调用内核 */
     /* does not return */
    return 1;
 }

其中的setup_start_tag,setup_memory_tags,setup_end_tag函数,就是打标记,在lib_arm/bootm.c中定义如下:
(1)setup_start_tag函数

static void setup_start_tag (bd_t *bd)
{
       params = (struct tag *) bd->bi_boot_params; 
        /* 内核的参数的开始地址 */
        params->hdr.tag = ATAG_CORE;
       params->hdr.size = tag_size (tag_core);
       params->u.core.flags = 0;
       params->u.core.pagesize = 0;
       params->u.core.rootdev = 0;
       params = tag_next (params);
}

标记列表必须以ATAG_CORE开始,setup_start_tag函数在内核的参数的开始地址设置了一个ATAG_CORE标记。

(2)setup_memory_tags函数

static void setup_memory_tags (bd_t *bd)
{
       int i;
/*设置一个内存标记 */
       for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {    
              params->hdr.tag = ATAG_MEM;
              params->hdr.size = tag_size (tag_mem32);
               params->u.mem.start = bd->bi_dram[i].start;
              params->u.mem.size = bd->bi_dram[i].size;
              params = tag_next (params);
       }
}

setup_memory_tags函数设置了一个ATAG_MEM标记,该标记包含内存起始地址,内存大小这两个参数。

(3)setup_end_tag函数

static void setup_end_tag (bd_t *bd)
{
       params->hdr.tag = ATAG_NONE;
       params->hdr.size = 0;
}

标记列表必须以标记ATAG_NONE结束,setup_end_tag函数设置了一个ATAG_NONE标记,表示标记列表的结束。U-Boot设置好标记列表后就要调用内核了。但调用内核前,CPU必须满足下面的条件:

1)CPU寄存器的设置
       r0=0
       r1=机器码
       r2=内核参数标记列表在RAM中的起始地址
(2)CPU工作模式
       禁止IRQ与FIQ中断
       CPU为SVC模式
(3)使数据Cache与指令Cache失效

do_bootm_linux中调用的cleanup_before_linux函数完成了禁止中断和使Cache失效的功能。cleanup_before_linux函数在cpu/arm920t/cpu.中定义:

int cleanup_before_linux (void)
{
       /*
        * this function is called just before we call linux
        * it prepares the processor for linux
        *
        * we turn off caches etc ...
        */

       disable_interrupts ();         /* 禁止FIQ/IRQ中断 */

       /* turn off I/D-cache */
       icache_disable();               /* 使指令Cache失效 */
       dcache_disable();              /* 使数据Cache失效 */
       /* flush I/D-cache */
       cache_flush();                    /* 刷新Cache */

       return 0;
}

由于U-Boot启动以来就一直工作在SVC模式,因此CPU的工作模式就无需设置了。
do_bootm_linux中:

  void  (*theKernel)(int zero, int arch, uint params);
        … …
  theKernel = (void (*)(int, int, uint))images->ep;
        … …
  theKernel (0, machid, bd->bi_boot_params);

内核的入口地址“images->ep”强制类型转换为函数指针。根据ATPCS规则,函数的参数个数不超过4个时,使用r0~r3这4个寄存器来传递参数。因此函数调用则会将0放入r0,机器码machid放入r1,内核参数地址bd->bi_boot_params放入r2,从而完成了寄存器的设置,最后转到内核的入口地址。
到这里,U-Boot的工作就结束了,系统跳转到Linux内核代码执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值