U-Boot与Linux内核的交互

本文详细解析了U-Boot与Linux内核之间的交互机制,重点介绍了标记列表的使用,包括设置ATAG_CORE、ATAG_MEM、ATAG_CMDLINE等标记,以及如何通过bootm命令引导Linux内核。
               

U-Boot与Linux内核的交互

说明:本文所使用的U-Boot的版本是1.1.6,平台是S3C2440。

目录

一、简介
 1.1标记列表
二、设置标记存放的地址
 2.1相关的结构体定义
 2.2标记存放地址的设定
三、标记的设置
 3.1设置标记ATAG_CORE
 3.2设置内存标记ATAG_MEM
 3.3设置命令行标记ATAG_CMDLINE
 3.4设置ATAG_NONE

一、简介

 U-Boot与Linux内核的交互是单向的,U-Boot将各类参数传递给内核。由于他们不能同时运行,传递办法只能有一个个:U-Boot将参数放在某个约定的地方之后,在启动内核,内核启动后从这个地方获得参数。

1.1标记列表

 除了约定好参数存放的地方外,还要规定参数的结构。Linux2.4.x以后的内核都以标记列表(tagged list)的形式来传递参数。标记就是一种数据结构;标记列表就是挨着存放的多个标记。标记列表以标记ATAG_CORE开始,以ATAGE_NONE结束。

 标记的数据结构为tag,它是偶一个tag_header结构和一个联合体(union)组成。tag_header结构体表示标记的类型及长度,比如是表示内存还是表示命令行参数等。对于不同类型的标记使用不同的联合体,比如表示内存=时使用tag_men32,表示命令行时使用tag_cmdline。其定定义在include/asm-arm/setup.c文件中。

/* * The new way of passing information: a list of tagged entries *//* The list ends with an ATAG_NONE node. */#define ATAG_NONE 0x00000000struct tag_header { u32 size; u32 tag;};/* The list must start with an ATAG_CORE node */#define ATAG_CORE 0x54410001struct tag_core { u32 flags;  /* bit 0 = read-only */ u32 pagesize; u32 rootdev;};/* it is allowed to have multiple ATAG_MEM nodes */#define ATAG_MEM 0x54410002struct tag_mem32 { u32 size; u32 start; /* physical start address */};/* VGA text type displays */#define ATAG_VIDEOTEXT 0x54410003struct tag_videotext { u8  x; u8  y; u16  video_page; u8  video_mode; u8  video_cols; u16  video_ega_bx; u8  video_lines; u8  video_isvga; u16  video_points;};/* describes how the ramdisk will be used in kernel */#define ATAG_RAMDISK 0x54410004struct tag_ramdisk { u32 flags; /* bit 0 = load, bit 1 = prompt */ u32 size; /* decompressed ramdisk size in _kilo_ bytes */ u32 start; /* starting block of floppy-based RAM disk image */};/* describes where the compressed ramdisk image lives (virtual address) *//* * this one accidentally used virtual addresses - as such, * its depreciated. */#define ATAG_INITRD 0x54410005/* describes where the compressed ramdisk image lives (physical address) */#define ATAG_INITRD2 0x54420005struct tag_initrd { u32 start; /* physical start address */ u32 size; /* size of compressed ramdisk image in bytes */};/* board serial number. "64 bits should be enough for everybody" */#define ATAG_SERIAL 0x54410006struct tag_serialnr { u32 low; u32 high;};/* board revision */#define ATAG_REVISION 0x54410007struct tag_revision { u32 rev;};/* initial values for vesafb-type framebuffers. see struct screen_info * in include/linux/tty.h */#define ATAG_VIDEOLFB 0x54410008struct tag_videolfb { u16  lfb_width; u16  lfb_height; u16  lfb_depth; u16  lfb_linelength; u32  lfb_base; u32  lfb_size; u8  red_size; u8  red_pos; u8  green_size; u8  green_pos; u8  blue_size; u8  blue_pos; u8  rsvd_size; u8  rsvd_pos;};/* command line: \0 terminated string */#define ATAG_CMDLINE 0x54410009struct tag_cmdline { char cmdline[1]; /* this is the minimum size */};/* acorn RiscPC specific information */#define ATAG_ACORN 0x41000101struct tag_acorn { u32 memc_control_reg; u32 vram_pages; u8 sounddefault; u8 adfsdrives;};/* footbridge memory clock, see arch/arm/mach-footbridge/arch.c */#define ATAG_MEMCLK 0x41000402struct tag_memclk { u32 fmemclk;};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;  /*   * Acorn specific   */  struct tag_acorn acorn;  /*   * DC21285 specific   */  struct tag_memclk memclk; } u;};#define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size))#define tag_size(type) ((sizeof(struct tag_header) + sizeof(struct type)) >> 2) //???

二、设置标记存放的地址

2.1相关的结构体定义

 结构体bd中保存了标记存放的地址。bd结构体是gd结构体的一项,我们先看gd结构体,其定义在include/asm-arm/global_data.h文件中:

typedef struct global_data { bd_t  *bd;//开发板相关参数 ,结构体变量,参考u-boot.h  unsigned long flags;//指示标志,如设备已经初始化标志等 unsigned long baudrate;//串行口通讯速率 unsigned long have_console; /* serial_init() was called 如果执行了该函数,则设置为1 */ unsigned long reloc_off;  /*   *Relocation Offset 重定位偏移,就是实际定向的位置与编译连接时指定的位置之差,一般为0        */ unsigned long env_addr; /* 环境参数地址*/ unsigned long env_valid; /* 环境参数CRC检验有效标志*/ unsigned long fb_base; /*帧缓冲区基地址*/#ifdef CONFIG_VFD unsigned char vfd_type; /* 显示类型*/#endif#if 0 unsigned long cpu_clk; /*cpu时钟*/ unsigned long bus_clk;    //总线时钟 unsigned long ram_size; /* RAM size */ unsigned long reset_status; /* reset status register at boot */#endif void  **jt; /* jump table 跳转表,用来登记"函数调用地址"*/} gd_t;
接来下我们来看一下bd结构体,这个结构体定义在include/asm-arm/u-boot.h文件中:

typedef struct bd_info {    int   bi_baudrate; /* 串口波特率*/    unsigned long bi_ip_addr; /*  IP 地址*/    unsigned char bi_enetaddr[6]; /* MAC地址*/    struct environment_s        *bi_env;    ulong         bi_arch_number; /*  板子的id*/    ulong         bi_boot_params; /* 启动参数*/    struct    /* RAM 配置*/    { ulong start; ulong size;    }bi_dram[CONFIG_NR_DRAM_BANKS];#ifdef CONFIG_HAS_ETH1    /* second onboard ethernet port */    unsigned char   bi_enet1addr[6];#endif} bd_t;

2.2标记存放地址的设定

 在board/smdk2410/smdk2410.c的board_init 函数设置了bi_boot_params 参数:

int board_init (void){ S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();//获取时钟和电源配置寄存器的第一个寄存器的地址,寄存器的地上是连续的 S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();//获取GPIO配置寄存器的第一个寄存器的地址 /* to reduce PLL lock time, adjust the LOCKTIME register */ clk_power->LOCKTIME = 0xFFFFFF; /* configure MPLL */ clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV); /* some delay between MPLL and UPLL */ delay (4000); /* configure UPLL */ clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV); /* some delay between MPLL and UPLL */ delay (8000); /* set up the I/O ports */ gpio->GPACON = 0x007FFFFF; gpio->GPBCON = 0x00044555; gpio->GPBUP = 0x000007FF; gpio->GPCCON = 0xAAAAAAAA; gpio->GPCUP = 0x0000FFFF; gpio->GPDCON = 0xAAAAAAAA; gpio->GPDUP = 0x0000FFFF; gpio->GPECON = 0xAAAAAAAA; gpio->GPEUP = 0x0000FFFF; gpio->GPFCON = 0x000055AA; gpio->GPFUP = 0x000000FF; gpio->GPGCON = 0xFF95FFBA; gpio->GPGUP = 0x0000FFFF; gpio->GPHCON = 0x002AFAAA; gpio->GPHUP = 0x000007FF; /* arch number of SMDK2410-Board */ gd->bd->bi_arch_number = MACH_TYPE_SMDK2410; /* adress of boot parameters */ gd->bd->bi_boot_params = 0x30000100; icache_enable();  //调用cpu/arm920t/cpu.c中的函数 dcache_enable(); return 0;}

三、标记的设置

 U-Boot通过bootm命令引导Linux内核,bootm命令对吼调用do_bootm_linux函数来引导内核。在do_bootm_linux函数就设置了标记,该函数的定义在lib_arm/armlinux.c中:

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],       ulong addr, ulong *len_ptr, int verify){ ulong len = 0, checksum; ulong initrd_start, initrd_end; ulong data; void (*theKernel)(int zero, int arch, uint params); image_header_t *hdr = &header; bd_t *bd = gd->bd;#ifdef CONFIG_CMDLINE_TAG char *commandline = getenv ("bootargs");#endif theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); 设置kernal加载地址 /*  * Check if there is an initrd image  */ 用户自定义了initrd之后需要加载进来,整个过程需要进行头部以及整个数据内部校,类似于内核的加载校验,这里省略了。initial RAM disk  Linux初始 RAM磁盘(initrd)是在系统引导过程中挂载的一个临时根文件系统,用来支持两阶段的引导过程。initrd文件中包含了各种可执行程序和驱动程序,它们可以用来挂载实际的根文件系统,然后再将这个 initrd RAM 磁盘卸载,并释放内存。在很多嵌入式Linux 系统中,initrd 就是最终的根文件系统。 if (argc >= 3) {  SHOW_BOOT_PROGRESS (9);  addr = simple_strtoul (argv[2], NULL, 16);  printf ("## Loading Ramdisk Image at %08lx ...\n", addr);  /* Copy header so we can blank CRC field for re-calculation */#ifdef CONFIG_HAS_DATAFLASH  if (addr_dataflash (addr)) {   read_dataflash (addr, sizeof (image_header_t),     (char *) &header);  } else#endif   memcpy (&header, (char *) addr,    sizeof (image_header_t));  if (ntohl (hdr->ih_magic) != IH_MAGIC) {   printf ("Bad Magic Number\n");   SHOW_BOOT_PROGRESS (-10);   do_reset (cmdtp, flag, argc, argv);  }  data = (ulong) & header;  len = sizeof (image_header_t);  checksum = ntohl (hdr->ih_hcrc);  hdr->ih_hcrc = 0;  if (crc32 (0, (unsigned char *) data, len) != checksum) {   printf ("Bad Header Checksum\n");   SHOW_BOOT_PROGRESS (-11);   do_reset (cmdtp, flag, argc, argv);  }  SHOW_BOOT_PROGRESS (10);  print_image_hdr (hdr);  data = addr + sizeof (image_header_t);  len = ntohl (hdr->ih_size);#ifdef CONFIG_HAS_DATAFLASH  if (addr_dataflash (addr)) {   read_dataflash (data, len, (char *) CFG_LOAD_ADDR);   data = CFG_LOAD_ADDR;  }#endif  if (verify) {   ulong csum = 0;   printf ("   Verifying Checksum ... ");   csum = crc32 (0, (unsigned char *) data, len);   if (csum != ntohl (hdr->ih_dcrc)) {    printf ("Bad Data CRC\n");    SHOW_BOOT_PROGRESS (-12);    do_reset (cmdtp, flag, argc, argv);   }   printf ("OK\n");  }  SHOW_BOOT_PROGRESS (11);  if ((hdr->ih_os != IH_OS_LINUX) ||      (hdr->ih_arch != IH_CPU_ARM) ||      (hdr->ih_type != IH_TYPE_RAMDISK)) {   printf ("No Linux ARM Ramdisk Image\n");   SHOW_BOOT_PROGRESS (-13);   do_reset (cmdtp, flag, argc, argv);  }#if defined(CONFIG_B2) || defined(CONFIG_EVB4510) || defined(CONFIG_ARMADILLO)  /*   *we need to copy the ramdisk to SRAM to let Linux boot   */  memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);  data = ntohl(hdr->ih_load);#endif /* CONFIG_B2 || CONFIG_EVB4510 */  /*   * Now check if we have a multifile image   */ } else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {  ulong tail = ntohl (len_ptr[0]) % 4;  int i;  SHOW_BOOT_PROGRESS (13);  /* skip kernel length and terminator */  data = (ulong) (&len_ptr[2]);  /* skip any additional image length fields */  for (i = 1; len_ptr[i]; ++i)   data += 4;  /* add kernel length, and align */  data += ntohl (len_ptr[0]);  if (tail) {   data += 4 - tail;  }  len = ntohl (len_ptr[1]); } else {  /*   * no initrd image   */  SHOW_BOOT_PROGRESS (14);  len = data = 0; }#ifdef DEBUG if (!data) {  printf ("No initrd\n"); }#endif if (data) {  initrd_start = data;  initrd_end = initrd_start + len; } else {  initrd_start = 0;  initrd_end = 0; } SHOW_BOOT_PROGRESS (15); debug ("## Transferring control to Linux (at address %08lx) ...\n",        (ulong) theKernel);#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);设置各种tag,用于传递参数给Linux#ifdef CONFIG_SERIAL_TAG setup_serial_tag (¶ms);#endif#ifdef CONFIG_REVISION_TAG setup_revision_tag (¶ms);#endif#ifdef CONFIG_SETUP_MEMORY_TAGS setup_memory_tags (bd);#endif#ifdef CONFIG_CMDLINE_TAG setup_commandline_tag (bd, commandline);#endif#ifdef CONFIG_INITRD_TAG if (initrd_start && initrd_end)  setup_initrd_tag (bd, initrd_start, initrd_end);#endif#if defined (CONFIG_VFD) || defined (CONFIG_LCD) setup_videolfb_tag ((gd_t *) gd);#endif setup_end_tag (bd);#endif /* we assume that the kernel is in place */ printf ("\nStarting kernel ...\n\n");打印信息#ifdef CONFIG_USB_DEVICE {  extern void udc_disconnect (void);  udc_disconnect (); }#endif cleanup_before_linux ();启动之前先做一些清理工作cpu/arm920t/cpu.c调用内核需要传递的参数如下:R0:必须为0R1:机器类型ID,本机为ARM(bd->bi_arch_number)R2:启动参数列表在内存中的位置(bd->bi_boot_params) theKernel (0, bd->bi_arch_number, bd->bi_boot_params);}

3.1设置标记ATAG_CORE

 标记列表以标记ATAG_CORE开始

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);//指向当前标记的末尾}

3.2设置内存标记ATAG_MEM

 在board/smdk2410/smdk2410.c的dram_init函数设置了bd的bi_dram结构体:

int dram_init (void){ gd->bd->bi_dram[0].start = PHYS_SDRAM_1; gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; return 0;}

下面是这边内存标记的结构体:

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); }}

3.3设置命令行标记ATAG_CMDLINE

 命令行就是一个字符串,用来控制内核的一些行为。比如“root=/dev/mtdblock2 init=/linuxrc console=ttySAC0 ”表示根文件系统在MTD2分区上系统启动后执行的第一个程序为/linuxrc,控制台是ttySAC0 。

static void setup_commandline_tag (bd_t *bd, char *commandline){ char *p; if (!commandline)  return; /* eat leading white space */ for (p = commandline; *p == ' '; p++); /* skip non-existent command lines so the kernel will still  * use its default command line.  */ if (*p == '\0')  return; params->hdr.tag = ATAG_CMDLINE; params->hdr.size =  (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2; strcpy (params->u.cmdline.cmdline, p); params = tag_next (params);}

3.4设置ATAG_NONE

 标记列表以标记ATAG_NONE介绍。

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

           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.youkuaiyun.com/jiangjunshow

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值