U-BOOT启动kernel的过程
来源:互联网 发布:java上传图片到ftp 编辑:程序博客网 时间:2024/06/09 04:25
u-boot是一种bootloader,它其实就是一段单机程序,在系统上电时自动执行,初始化硬件设备,准备好软件环境,就是为了达到其终极目的——启动内核。
本文记录了以u-boot启动运行在ARM上的Linux为例,拷贝内核镜像文件到SDRAM后,调用do_bootm的过程。话不多说,先上软件流程图:
一、内核镜像文件的检查
内核镜像文件拷贝到SDRAM上之后,需要对镜像文件进行检查,包括Image Magic Number,镜像文件头CRC,内核内容CRC,是否支持当前的CPU,是否需要解压,将内核内容拷贝到内核启动地址。这一些列的操作都是通过common/cmd_bootm.c中的do_bootm()函数来实现的。
@cmd_tbl_t *cmdtp: do_bootm的命令结构体指针
@argc: 参数个数,以”bootm 0x30007FC0”为例,argc = 2
@argv:存放参数,argv[0] = “bootm”, argv[1] = “0x30007FC0”
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 = CFG_BOOTM_LEN; int i, verify; char *name, *s; int (*appl)(int, char *[]); image_header_t *hdr = &header; // 检索环境变量"verify",若检索成功则verify = 0,否则为1 s = getenv ("verify"); verify = (s && (*s == 'n')) ? 0 : 1; // 若参数个数小于2,表明没有传入内核镜像加载地址,则使用默认的加载地址load_addr if (argc < 2) { addr = load_addr; } else { addr = simple_strtoul(argv[1], NULL, 16); } SHOW_BOOT_PROGRESS (1); printf ("## Booting image at %08lx ...\n", addr); // 拷贝镜像文件的文件头到header。 memmove (&header, (char *)addr, sizeof(image_header_t)); // check the Image Magic Number if (ntohl(hdr->ih_magic) != IH_MAGIC) { { puts ("Bad Magic Number\n"); SHOW_BOOT_PROGRESS (-1); return 1; } } SHOW_BOOT_PROGRESS (2); // 将header的地址值赋给data,长度赋给len data = (ulong)&header; len = sizeof(image_header_t); // 读取镜像文件头hcrc的值 checksum = ntohl(hdr->ih_hcrc); hdr->ih_hcrc = 0; // 对镜像文件头做CRC校验。 if (crc32 (0, (uchar *)data, len) != checksum) { puts ("Bad Header Checksum\n"); SHOW_BOOT_PROGRESS (-2); return 1; } SHOW_BOOT_PROGRESS (3); /* for multi-file images we need the data part, too */ // 显示镜像文件头信息。 print_image_hdr ((image_header_t *)addr); // 计算内核的入口地址值,赋给data,内核的大小赋给len data = addr + sizeof(image_header_t); len = ntohl(hdr->ih_size); // 如果需要,校验内核内容的CRC。 if (verify) { puts (" Verifying Checksum ... "); if (crc32 (0, (uchar *)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指向内核的入口地址。 len_ptr = (ulong *)data; if (hdr->ih_arch != IH_CPU_ARM) { printf ("Unsupported Architecture 0x%x\n", hdr->ih_arch); SHOW_BOOT_PROGRESS (-4); return 1; } SHOW_BOOT_PROGRESS (5); // 获取镜像文件的类型,若是内核则 name = "Kernel Image"; switch (hdr->ih_type) { case IH_TYPE_STANDALONE: name = "Standalone Application"; /* A second argument overwrites the load address */ if (argc > 2) { hdr->ih_load = htonl(simple_strtoul(argv[2], NULL, 16)); } break; case IH_TYPE_KERNEL: name = "Kernel Image"; break; case IH_TYPE_MULTI: name = "Multi-File Image"; len = ntohl(len_ptr[0]); /* OS kernel is always the first image */ data += 8; /* kernel_len + terminator */ for (i=1; len_ptr[i]; ++i) data += 4; break; default: printf ("Wrong Image Type for %s command\n", cmdtp->name); SHOW_BOOT_PROGRESS (-5); return 1; } SHOW_BOOT_PROGRESS (6); /* * We have reached the point of no return: we are going to * overwrite all exception vector code, so we cannot easily * recover from any failures any more... */ iflag = disable_interrupts(); // 判断镜像文件是否为压缩文件,若为压缩文件,解压缩。 switch (hdr->ih_comp) { case IH_COMP_NONE: if(ntohl(hdr->ih_load) == data) { printf (" XIP %s ... ", name); } else {#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) size_t l = len; void *to = (void *)ntohl(hdr->ih_load); void *from = (void *)data; printf (" Loading %s ... ", name); while (l > 0) { size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l; WATCHDOG_RESET(); memmove (to, from, tail); to += tail; from += tail; l -= tail; }#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */ memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */ } break; case IH_COMP_GZIP: printf (" Uncompressing %s ... ", name); if (gunzip ((void *)ntohl(hdr->ih_load), unc_len, (uchar *)data, &len) != 0) { puts ("GUNZIP ERROR - must RESET board to recover\n"); SHOW_BOOT_PROGRESS (-6); do_reset (cmdtp, flag, argc, argv); } break;#ifdef CONFIG_BZIP2 case IH_COMP_BZIP2: printf (" Uncompressing %s ... ", name); /* * If we've got less than 4 MB of malloc() space, * use slower decompression algorithm which requires * at most 2300 KB of memory. */ i = BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load), &unc_len, (char *)data, len, CFG_MALLOC_LEN < (4096 * 1024), 0); if (i != BZ_OK) { printf ("BUNZIP2 ERROR %d - must RESET board to recover\n", i); SHOW_BOOT_PROGRESS (-6); udelay(100000); do_reset (cmdtp, flag, argc, argv); } break;#endif /* CONFIG_BZIP2 */ default: if (iflag) enable_interrupts(); printf ("Unimplemented compression type %d\n", hdr->ih_comp); SHOW_BOOT_PROGRESS (-7); return 1; } puts ("OK\n"); SHOW_BOOT_PROGRESS (7); // 对于内核镜像文件,在这里直接跳到下一步操作。 switch (hdr->ih_type) { case IH_TYPE_STANDALONE: if (iflag) enable_interrupts(); /* load (and uncompress), but don't start if "autostart" * is set to "no" */ if (((s = getenv("autostart")) != NULL) && (strcmp(s,"no") == 0)) { char buf[32]; sprintf(buf, "%lX", len); setenv("filesize", buf); return 0; } appl = (int (*)(int, char *[]))ntohl(hdr->ih_ep); (*appl)(argc-1, &argv[1]); return 0; case IH_TYPE_KERNEL: case IH_TYPE_MULTI: /* handled below */ break; default: if (iflag) enable_interrupts(); printf ("Can't boot image type %d\n", hdr->ih_type); SHOW_BOOT_PROGRESS (-8); return 1; } SHOW_BOOT_PROGRESS (8); // 到了这里说明镜像文件是内核文件,判断是什么类型的内核,然后调用相应的启动函数,这里是Linux OS,所以调用do_bootm_linux switch (hdr->ih_os) { default: /* handled by (original) Linux case */ case IH_OS_LINUX:#ifdef CONFIG_SILENT_CONSOLE fixup_silent_linux();#endif // 若bootm传入的命令参数为"bootm 0x30007FC0",则 // cmdtp = store the address where the cmd_bootm struct table. // flag = 0 // argc = 2 // argv[0] = "bootm" // argv[1] = "0x30007FC0" // addr = 0x30007FC0 // len_ptr = 0x30008000 // verify = 1 do_bootm_linux (cmdtp, flag, argc, argv, addr, len_ptr, verify); break; case IH_OS_NETBSD: do_bootm_netbsd (cmdtp, flag, argc, argv, addr, len_ptr, verify); break;#ifdef CONFIG_LYNXKDI case IH_OS_LYNXOS: do_bootm_lynxkdi (cmdtp, flag, argc, argv, addr, len_ptr, verify); break;#endif case IH_OS_RTEMS: do_bootm_rtems (cmdtp, flag, argc, argv, addr, len_ptr, verify); break;#if (CONFIG_COMMANDS & CFG_CMD_ELF) case IH_OS_VXWORKS: do_bootm_vxworks (cmdtp, flag, argc, argv, addr, len_ptr, verify); break; case IH_OS_QNX: do_bootm_qnxelf (cmdtp, flag, argc, argv, addr, len_ptr, verify); break;#endif /* CFG_CMD_ELF */#ifdef CONFIG_ARTOS case IH_OS_ARTOS: do_bootm_artos (cmdtp, flag, argc, argv, addr, len_ptr, verify); break;#endif } SHOW_BOOT_PROGRESS (-9);#ifdef DEBUG puts ("\n## Control returned to monitor - resetting...\n"); do_reset (cmdtp, flag, argc, argv);#endif return 1;}
二、达成终极目标——启动内核
在确认镜像文件无误,拷贝内核到调用入口地址处(如果需要的话)后,调用do_bootm_linux(),设置u-boot传给内核的参数并为启动内核做一些初始化,包括关闭中断,关闭MMU,关闭数据cache,设置CPU为SVC模式,设置R0~R2寄存器的值,最终跳到内核入口地址调用内核。
由于u-boot和内核的交互是单向的,传递参数的办法只有一个:u-boot将参数放在某个约定的地方之后,再启动内核,内核启动后从这个地方获得参数。
// 若bootm传入的命令参数为"bootm 0x30007FC0",则// cmdtp = store the address where the cmd_bootm struct table.// flag = 0// argc = 2// argv[0] = "bootm"// argv[1] = "0x30007FC0"// addr = 0x30007FC0// len_ptr = 0x30008000// verify = 1void 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 // 获取OS的启动参数: 若 bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0 // 则commandline = "noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0" char *commandline = getenv ("bootargs");#endif // theKernel指向内核的入口地址 theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); /* * 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); // 设置传递给内核的参数 TLV格式#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#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 (); // cancled by www.100ask.net }#endif // 在调用内核之前,做一些必要的初始化。 cleanup_before_linux (); // 通过入参,设置CPU寄存器 // R0 = 0 // R1 = 机器类型ID // R2 = 启动参数标记列表在RAM中起始基地址 theKernel (0, bd->bi_arch_number, bd->bi_boot_params);}
// 设置起始TAG参数,参数列表的其实地址为 bd->bi_boot_paramsstatic 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);}
// 关中断,关流水线,清cacheint cleanup_before_linux (void){ /* * this function is called just before we call linux * it prepares the processor for linux * * we turn off caches etc ... */ unsigned long i; disable_interrupts (); /* turn off I/D-cache */ asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); i &= ~(C1_DC | C1_IC); asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i)); /* flush I/D-cache */ i = 0; asm ("mcr p15, 0, %0, c7, c7, 0": :"r" (i)); return (0);}
阅读全文
0 0
- U-BOOT启动kernel的过程
- u-boot分析 六 (从code flow看u-boot到kernel的启动过程)
- u-boot的启动过程
- U-Boot的启动过程
- u-boot 7、启动kernel
- U-Boot启动过程
- u-boot启动过程
- U-Boot启动过程
- U-Boot启动过程
- U-Boot启动过程
- U-Boot启动过程
- u-boot启动过程
- U-boot启动过程
- U-Boot启动过程
- U-Boot启动过程
- U-Boot 启动过程
- U-boot启动过程
- u-boot启动过程
- 在虚拟机上基于Redhat Linux6.5的oracle 12c安装过程
- 高级网络配置
- 2017省赛热身赛总结
- 蓝桥杯 算法训练 表达式计算 JAVA
- Django-pyodbc的安装
- U-BOOT启动kernel的过程
- kotlin学习Day1:JavaActivity和KotlinActivity对比分析
- 素数判断 Java
- leetcode 561 Array Partition I
- 职业规划
- [调研] 防火墙多出口 对于 内部服务器网络的影响
- |BZOJ 2763|最短路|[JLOI2011]飞行路线
- 十六进制转二进制 Java
- nginx实现tomcat动静分离详解