Chinaunix首页 | 论坛 | 博客
  • 博客访问: 244803
  • 博文数量: 181
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 422
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-09 15:34
个人简介

你好 世界

文章分类

全部博文(181)

文章存档

2016年(181)

我的朋友

分类: LINUX

2016-06-10 23:41:34

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

点击(此处)折叠或打开

  1. struct tag_header {

  2.        u32 size; /* 表示tag数据结构的联合u实质存放的数据的大小*/

  3.        u32 tag; /* 表示标记的类型 */

  4. };

  5.  

  6. struct tag {

  7.        struct tag_header hdr;

  8.        union {

  9.               struct tag_core core;

  10.               struct tag_mem32 mem;

  11.               struct tag_videotext videotext;

  12.               struct tag_ramdisk ramdisk;

  13.               struct tag_initrd initrd;

  14.               struct tag_serialnr serialnr;

  15.               struct tag_revision revision;

  16.               struct tag_videolfb videolfb;

  17.               struct tag_cmdline cmdline;

  18.  

  19.               /*

  20.                * Acorn specific

  21.                */

  22.               struct tag_acorn acorn;

  23.               /*

  24.                * DC21285 specific

  25.                */

  26.               struct tag_memclk memclk;

  27.        } u;

  28. };
U-Boot使用命令bootm来启动已经加载到内存中的内核。而bootm命令实际上调用的是do_bootm函数。对于Linux内核,do_bootm函数会调用do_bootm_linux函数来设置标记列表和启动内核。do_bootm_linux函数在lib_arm/bootm.c 中定义如下:

59   int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)

60   {

61       bd_t       *bd = gd->bd;

62       char       *s;

63       int   machid = bd->bi_arch_number;

64       void       (*theKernel)(int zero, int arch, uint params);

65  

66   #ifdef CONFIG_CMDLINE_TAG

67       char *commandline = getenv ("bootargs");   /* U-Boot环境变量bootargs */

68   #endif

       … …

73       theKernel = (void (*)(int, int, uint))images->ep; /* 获取内核入口地址 */

       … …

86   #if defined (CONFIG_SETUP_MEMORY_TAGS) || \

87       defined (CONFIG_CMDLINE_TAG) || \

88       defined (CONFIG_INITRD_TAG) || \

89       defined (CONFIG_SERIAL_TAG) || \

90       defined (CONFIG_REVISION_TAG) || \

91       defined (CONFIG_LCD) || \

92       defined (CONFIG_VFD)

93       setup_start_tag (bd);                                     /* 设置ATAG_CORE标志 */

       … …

100  #ifdef CONFIG_SETUP_MEMORY_TAGS

101      setup_memory_tags (bd);                             /* 设置内存标记 */

102  #endif

103  #ifdef CONFIG_CMDLINE_TAG

104      setup_commandline_tag (bd, commandline);      /* 设置命令行标记 */

105  #endif

       … …

113      setup_end_tag (bd);                               /* 设置ATAG_NONE标志 */          

114  #endif

115 

116      /* we assume that the kernel is in place */

117      printf ("\nStarting kernel ...\n\n");

       … …

126      cleanup_before_linux ();          /* 启动内核前对CPU作最后的设置 */

127 

128      theKernel (0, machid, bd->bi_boot_params);      /* 调用内核 */

129      /* does not return */

130 

131      return 1;

132  }

其中的setup_start_tag,setup_memory_tags,setup_end_tag函数在lib_arm/bootm.c中定义如下:

       (1)setup_start_tag函数



  1. 点击(此处)折叠或打开

    1. static void setup_start_tag (bd_t *bd)

    2. {

    3.        params = (struct tag *) bd->bi_boot_params; /* 内核的参数的开始地址 */

    4.  

    5.        params->hdr.tag = ATAG_CORE;

    6.        params->hdr.size = tag_size (tag_core);

    7.  

    8.        params->u.core.flags = 0;

    9.        params->u.core.pagesize = 0;

    10.        params->u.core.rootdev = 0;

    11.  

    12.        params = tag_next (params);

    13. }

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

       (2)setup_memory_tags函数

点击(此处)折叠或打开

  1. static void setup_memory_tags (bd_t *bd)

  2. {

  3.        int i;

  4. /*设置一个内存标记 */

  5.        for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {

  6.               params->hdr.tag = ATAG_MEM;

  7.               params->hdr.size = tag_size (tag_mem32);

  8.  

  9.               params->u.mem.start = bd->bi_dram[i].start;

  10.               params->u.mem.size = bd->bi_dram[i].size;

  11.  

  12.               params = tag_next (params);

  13.        }

  14. }

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

       (3)setup_end_tag函数

点击(此处)折叠或打开

  1. static void setup_end_tag (bd_t *bd)

  2. {

  3.        params->hdr.tag = ATAG_NONE;

  4.        params->hdr.size = 0;

  5. }

标记列表必须以标记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.中定义:

点击(此处)折叠或打开

  1. int cleanup_before_linux (void)

  2. {

  3.        /*

  4.         * this function is called just before we call linux

  5.         * it prepares the processor for linux

  6.         *

  7.         * we turn off caches etc ...

  8.         */

  9.  

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

  11.  

  12.        /* turn off I/D-cache */

  13.        icache_disable(); /* 使指令Cache失效 */

  14.        dcache_disable(); /* 使数据Cache失效 */

  15.        /* flush I/D-cache */

  16.        cache_flush(); /* 刷新Cache */

  17.  

  18.        return 0;

  19. }

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

do_bootm_linux中:

点击(此处)折叠或打开

  1. void (*theKernel)(int zero, int arch, uint params);

  2. … …

  3. 73 theKernel = (void (*)(int, int, uint))images->ep;

  4. … …

  5. 128 theKernel (0, machid, bd->bi_boot_params);

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

       到这里,U-Boot的工作就结束了,系统跳转到Linux内核代码执行。





阅读(690) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~