传智播客旗下技术交流社区北京校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 庭院深深深几许 金牌黑马   /  2019-5-13 09:47  /  141 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

  能看到这篇文章的人应该都知道U-BOOT具有两个阶段的BOOTLOADER.
  第一阶段:uboot执行最基本的硬件初始化操作,如关闭中断、关闭看门狗以避免处理器被复位(当然如果你在uboot中需要看门狗工作就另当别论了),以及关闭MMU功能、关闭处理器缓存、设置系统时钟、初始化内存等等。
  这一阶段的代码通常以汇编语言写成,如果是从nandflash启动还必须通过NANDFLASH控制器把bootloader代码复制到内存。
  第二阶段:由C语言编写
  需要做的事情:
  1. 初始化各硬件设备。
  2. 检测系统内存
  3. 将内核加载到内存
  4. 准备内核引导参数
  5. 跳转到内核。
  下面针对AT91sam9261 ARM的UBOOT,看看UBOOT的启动过程。
  第一阶段代码
  第一阶段的代码主要包括以下执行步骤。
  1. 一些基本的硬件初始化工作。
  2. 将Uboot映像复制到RAM空间。
  3. 跳转到第二阶段代码的入口点。
  第一阶段的代码放在cpu/arm926ejs/start.s 文件中。
  入口标号为: _start: 主要是异常向量表
  开始标号 : reset: 将CPU设为管理模式
  调用 cpu_init_crit进行CPU初始化:清除指令和数据缓存,禁用MMU和缓存,设置SDRAM控制器,与具体的目标板相关。
  调用lowlevel_init函数设置SDRAM控制器:lowlevel_init函数的实现是与具体的目标板相关的,在cpu/arm926ejs/at91/lowlevel_init.s文件中。
  Lowlevel_init 返回到 cpu_init_crit,再返回到reset标号下面的
  relocate:
  adr r0, _start
  ldr r1, _TEXT_BASE
  cmp r0, r1
  beq stack_setup
  ldr r2, _armboot_start
  ldr r3, _bss_start
  sub r2, r3, r2
  add r2, r0, r2
  接下来将U-BOOT映像从flash复制到内存。
  调用_start_armboot
  进而进入第二阶段:第二阶段中的
  第二阶段开始运行了start_armboot();函数,在lib_arm/board.c 文件中。
  start_armboot是U-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。
  其中:
  for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
  if ((*init_fnc_ptr)() != 0) {
  hang ();
  }
  }
  U-Boot与内核的关系
  U-Boot作为Bootloader,具备多种引导内核启动的方式。常用的go和bootm命令可以直接引导内核映像启动。U-Boot与内核的关系主要是内核启动过程中参数的传递。
  1.go命令的实现
  int do_go (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
  {
  ulong addr, rc;
  int rcode = 0;
  if (argc < 2) {
  printf ("Usage:\n%s\n", cmdtp->usage);
  return 1;
  }
  addr = simple_strtoul(argv[1], NULL, 16);
  printf ("## Starting application at 0xlX ...\n", addr);
  rc = ((ulong (*)(int, char *[]))addr) (--argc, &argv[1]);
  if (rc != 0) rcode = 1;
  printf ("## Application terminated, rc = 0x%lX\n", rc);
  return rcode;
  }
  go命令调用do_go()函数,跳转到某个地址执行的。如果在这个地址准备好了自引导的内核映像,就可以启动了。尽管go命令可以带变参,实际使用时一般不用来传递参数。
  2.bootm命令的实现
  int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
  {
  ulong iflag;
  ulong addr;
  ulong data, len, checksum;
  ulong *len_ptr;
  uint unc_len = 0x400000;
  int i, verify;
  char *name, *s;
  int (*appl)(int, char *[]);
  image_header_t *hdr = &header;
  s = getenv ("verify");
  verify = (s && (*s == 'n')) ? 0 : 1;
  if (argc < 2) {
  addr = load_addr;
  } else {
  addr = simple_strtoul(argv[1], NULL, 16);
  }
  SHOW_BOOT_PROGRESS (1);
  printf ("## Booting image at lx ...\n", addr);
  memmove (&header, (char *)addr, sizeof(image_header_t));
  if (ntohl(hdr->ih_magic) != IH_MAGIC)
  {
  puts ("Bad Magic Number\n");
  SHOW_BOOT_PROGRESS (-1);
  return 1;
  }
  SHOW_BOOT_PROGRESS (2);
  data = (ulong)&header;
  len = sizeof(image_header_t);
  checksum = ntohl(hdr->ih_hcrc);
  hdr->ih_hcrc = 0;
  if(crc32 (0, (char *)data, len) != checksum) {
  puts ("Bad Header Checksum\n");
  SHOW_BOOT_PROGRESS (-2);
  return 1;
  }
  SHOW_BOOT_PROGRESS (3);
  print_image_hdr ((image_header_t *)addr);
  data = addr + sizeof(image_header_t);
  len = ntohl(hdr->ih_size);
  if(verify) {
  puts (" Verifying Checksum ... ");
  if(crc32 (0, (char *)data, len) != ntohl(hdr->ih_dcrc)) {
  printf ("Bad Data CRC\n");
  SHOW_BOOT_PROGRESS (-3);
  return 1;
  }
  puts ("OK\n");
  }
  SHOW_BOOT_PROGRESS (4);
  len_ptr = (ulong *)data;
  ……
  switch (hdr->ih_os) {
  default:
  case IH_OS_LINUX:
  do_bootm_linux (cmdtp, flag, argc, argv,
  addr, len_ptr, verify);
  break;
  ……
  }
  bootm命令调用do_bootm函数。这个函数专门用来引导各种操作系统映像,可以支持引导Linux、vxWorks、QNX等操作系统。引导Linux的时候,调用do_bootm_linux()函数。
  3.do_bootm_linux函数的实现
  void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
  ulong addr, ulong *len_ptr, int verify)
  {
  DECLARE_GLOBAL_DATA_PTR;
  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);
  if(argc >= 3) {
  SHOW_BOOT_PROGRESS (9);
  addr = simple_strtoul (argv[2], NULL, 16);
  printf ("## Loading Ramdisk Image at lx ...\n", addr);
  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, (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);
  if(verify) {
  ulong csum = 0;
  printf (" Verifying Checksum ... ");
  csum = crc32 (0, (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);
  }
  } else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {
  ulong tail = ntohl (len_ptr[0]) % 4;
  int i;
  SHOW_BOOT_PROGRESS (13);
  data = (ulong) (&len_ptr[2]);
  for (i = 1; len_ptr[i]; ++i)
  data += 4;
  data += ntohl (len_ptr[0]);
  if (tail) {
  data += 4 - tail;
  }
  len = ntohl (len_ptr[1]);
  } else {
  SHOW_BOOT_PROGRESS (14);
  len = data = 0;
  }
  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 lx) ...\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);
  #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
  setup_end_tag (bd);
  #endif
  printf ("\nStarting kernel ...\n\n");
  cleanup_before_linux ();
  theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
  }

分享至 : QQ空间
收藏

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马