uboot源码分析——启动linux内核
来源:互联网 发布:知乎top50 编辑:程序博客网 时间:2024/05/29 03:57
如果板子启动以后不按任何键,将会默认启动Linux内核。
我们回到/common/main.c中,首先
s = getenv ("bootcmd");
然后
run_command (s, 0);
bootcmd是一个环境变量,它的值对应一些命令,用来启动linux
"bootcmd="CONFIG_BOOTCOMMAND "\0"
/include/configs/mini2440.h
#define CONFIG_BOOTCOMMAND"nfs 0x30008000 192.168.0.1:/home/tekkaman/working/nfs/zImage.img;bootm"
Nfs这种情况先不讨论,更一般的是这种:
#define CONFIG_BOOTCOMMAND"nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0;bootm"
这是两个命令,先看nand read.jffs2 0x30007FC0 kernel,这个命令的作用是从nandflash的kernel分区中读取数据到SDRAM中的0x30007FC0。
对于mini2440,将整块nandflash分成了很多个区:
/include/configs/mini2440.h
#define MTDPARTS_DEFAULT "mtdparts=nandflash0:384k(bootloader)," \"128k(params)," \"5m(kernel)," \"-(root)"
所以从nand的0地址开始:384KB的uboot,128KB的params,5M的内核,然后是root。
还有另一条命令bootm,解析bootm后uboot最终执行do_bootm函数(uboot怎么样对命令解析并执行的,前面讲过了)。
/common/cmd_bootm.c
/*******************************************************************//* bootm - boot application image from image in memory *//*******************************************************************/
<p>int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])</p><p>{</p><p>...</p><p>}</p>
参数:
cmdtp,命令结构体指针,指向bootm对应的命令结构体
flag,不了解
argc,参数的个数
argv,指向参数
在do_bootm中首先会进行一些relocated和do_bootm_subcommand的操作,这些我们不关心,直接看bootm_start函数。
/common/cmd_bootm.c
static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){...}
参数不用介绍了,在这个文件中定义了一个全局的结构体变量
static bootm_headers_t images; /* pointers to os/initrd/fdt images */
/include/images.h
/* * Legacy and FIT format headers used by do_bootm() and do_bootm_<os>() * routines. */typedef struct bootm_headers {/* * Legacy os image header, if it is a multi component image * then boot_get_ramdisk() and get_fdt() will attempt to get * data from second and third component accordingly. */<span style="color:#ff0000;">image_header_t*legacy_hdr_os;</span> /* image header pointer */<span style="color:#ff0000;">image_header_tlegacy_hdr_os_copy;</span>/* header copy */<span style="color:#ff0000;">ulong legacy_hdr_valid;</span>#if defined(CONFIG_FIT)const char*fit_uname_cfg;/* configuration node unit name */ void *fit_hdr_os;/* os FIT image header */const char*fit_uname_os;/* os subimage node unit name */int fit_noffset_os;/* os subimage node offset */ void *fit_hdr_rd;/* init ramdisk FIT image header */const char*fit_uname_rd;/* init ramdisk subimage node unit name */int fit_noffset_rd;/* init ramdisk subimage node offset */ void *fit_hdr_fdt;/* FDT blob FIT image header */const char*fit_uname_fdt;/* FDT blob subimage node unit name */int fit_noffset_fdt;/* FDT blob subimage node offset */#endif #ifndef USE_HOSTCCimage_info_tos; /* os image info */ulong ep; /* entry point of OS */ ulong rd_start, rd_end;/* ramdisk start/end */ #ifdef CONFIG_OF_LIBFDTchar *ft_addr;/* flat dev tree address */#endifulong ft_len; /* length of flat device tree */ ulong initrd_start;ulong initrd_end;ulong cmdline_start;ulong cmdline_end;bd_t *kbd;#endif <span style="color:#ff0000;">int verify;</span> /* getenv("verify")[0] != 'n' */ #defineBOOTM_STATE_START(0x00000001)#defineBOOTM_STATE_LOADOS(0x00000002)#defineBOOTM_STATE_RAMDISK(0x00000004)#defineBOOTM_STATE_FDT (0x00000008)#defineBOOTM_STATE_OS_CMDLINE(0x00000010)#defineBOOTM_STATE_OS_BD_T(0x00000020)#defineBOOTM_STATE_OS_PREP(0x00000040)#defineBOOTM_STATE_OS_GO(0x00000080)int state; #ifndef USE_HOSTCCstruct lmblmb; /* for memory mgmt */#endif} bootm_headers_t;
我用红色标注了一些比较重要的成员。
看一下image_header_t结构体:
/* * Legacy format image header, * all data in network byte order (aka natural aka bigendian). */typedef struct image_header {uint32_tih_magic;/* Image Header Magic Number*/uint32_tih_hcrc;/* Image Header CRC Checksum*/uint32_tih_time;/* Image Creation Timestamp*/uint32_tih_size;/* Image Data Size */<span style="color:#ff0000;">uint32_tih_load;/* Data Load Address */</span><span style="color:#ff0000;">uint32_tih_ep; /* Entry Point Address */</span>uint32_tih_dcrc;/* Image Data CRC Checksum*/uint8_t ih_os; /* Operating System */uint8_t ih_arch;/* CPU architecture */uint8_t ih_type;/* Image Type */uint8_t ih_comp;/* Compression Type */uint8_t ih_name[IH_NMLEN];/* Image Name */} image_header_t;
再回到bootm_start函数
首先将images清零,然后得到环境变量verify的值并判断是yes还是no,返回给images.verify。
lmb_init(&images.lmb); mem_start = getenv_bootm_low();mem_size = getenv_bootm_size(); lmb_add(&images.lmb, (phys_addr_t)mem_start, mem_size); arch_lmb_reserve(&images.lmb);board_lmb_reserve(&images.lmb);
这几行是lmb(Logical memory blocks)的相关操作
lmb_init(&images.lmb);对images.lmb进行一些初始化,这个成员是干嘛的?可能以后会用到。(Logical memory blocks)
mem_start = getenv_bootm_low();和 mem_size = getenv_bootm_size();分别得到SDRAM的起始地址和大小。
Lmb的内容先略过,以后用到再说。
接下来执行
/* get kernel image header, start address and length */os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,&images, &images.os.image_start, &images.os.image_len);
注释已经说得很明白了get kernel image header, start address and length
首先判断if (argc < 2),如果是一个的话img_addr就采用默认的load_addr,
ulong load_addr = CONFIG_SYS_LOAD_ADDR;/* Default Load Address */#defineCONFIG_SYS_LOAD_ADDR 0x30008000/* default load address*/
否则就将第二个参数转换成16进制的unsigned long。
所以img_addr保存的是image的起始地址。
因为没有dataflash,所以 genimg_get_image (img_addr),不执行。
然后执行genimg_get_format,返回Image的格式。IMAGE_FORMAT_LEGACY或者IMAGE_FORMAT_FIT。
如果返回的不是IMAGE_FORMAT_LEGACY,那么printf ("Wrong Image Type for %s command\n", cmdtp->name);
否则执行image_get_kernel函数,这个函数中有一些函数image_check_magic、image_check_hcrc、image_print_contents、image_check_target_arch,这里做了一些检查和校验(magic number、CRC、架构),打印了一些image的相关信息。
然后执行image_get_type返回image的类型,这里应该是IH_TYPE_KERNEL,os_data和os_len作为输出参数,实参是&images.os.image_start,和&images.os.image_len,得到真正的Image(没有头部)的起始地址和长度。
然后把image的头部拷贝到images->legacy_hdr_os_copy,这么做是防止kernel解压的时候将头部覆盖。
然后将images->legacy_hdr_os指向image。
跳出boot_get_kernel,然后根据genimg_get_format (os_hdr)得到image的格式,对images.os的成员进行赋值。
然后执行images.ep = image_get_ep (&images.legacy_hdr_os_copy);得到entry point。
最后执行images.os.start = (ulong)os_hdr;os_hdr是前面返回的load address。
bootm_start函数执行完毕,返回do_bootm。
然后disable_interrupts、icache_disable、dcache_disable。
执行
bootm_load_os(images.os, &load_end, 1);
参数:
images.os是os相关的结构体,前面有对它的初始化
load_end前面定义为0,这里显然是作为输出参数。
1表示boot_progress
根据os.comp的类型来解压缩,将镜像的数据从images.os.image_start复制到images.os.load 打印:Loading Kernel Image ... OK
接下来看非常重要的一步:
boot_fn = boot_os[images.os.os];
先看boot_fn,do_bootm中定义
boot_os_fn*boot_fn;
看一下boot_os_fn
/* * Continue booting an OS image; caller already has: * - copied image header to global variable `header' * - checked header magic number, checksums (both header & image), * - verified image architecture (PPC) and type (KERNEL or MULTI), * - loaded (first part of) image to header load address, * - disabled interrupts. */typedef int boot_os_fn (int flag, int argc, char *argv[],bootm_headers_t *images); /* pointers to os/initrd/fdt */
所以boot_fn是一个函数指针,参数是:
int flag
int argc
char *argv[]
bootm_headers_t *images
再看boot_os,他是一个函数指针数组
static boot_os_fn *boot_os[] = {#ifdef CONFIG_BOOTM_LINUX[IH_OS_LINUX] = do_bootm_linux,#endif#ifdef CONFIG_BOOTM_NETBSD[IH_OS_NETBSD] = do_bootm_netbsd,#endif#ifdef CONFIG_LYNXKDI[IH_OS_LYNXOS] = do_bootm_lynxkdi,#endif#ifdef CONFIG_BOOTM_RTEMS[IH_OS_RTEMS] = do_bootm_rtems,#endif#if defined(CONFIG_CMD_ELF)[IH_OS_VXWORKS] = do_bootm_vxworks,[IH_OS_QNX] = do_bootm_qnxelf,#endif#ifdef CONFIG_INTEGRITY[IH_OS_INTEGRITY] = do_bootm_integrity,#endif};
boot_os[images.os.os]就表示do_bootm_linux
所以boot_fn指向函数do_bootm_linux。
boot_fn(0, argc, argv, &images);
就是执行do_bootm_linux(0, argc, argv, &images);
看do_bootm_linux函数,
首先定义了一个函数指针
void(*theKernel)(int zero, int arch, uint params);
Commandline指向bootargs的值。
theKernel = (void (*)(int, int, uint))images->ep; //将入口地址ep赋值给theKernel
接下来是设置tag
#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_TAGsetup_serial_tag (¶ms);#endif#ifdef CONFIG_REVISION_TAGsetup_revision_tag (¶ms);#endif#ifdef CONFIG_SETUP_MEMORY_TAGSsetup_memory_tags (bd);#endif#ifdef CONFIG_CMDLINE_TAGsetup_commandline_tag (bd, commandline);#endif#ifdef CONFIG_INITRD_TAGif (images->rd_start && images->rd_end)setup_initrd_tag (bd, images->rd_start, images->rd_end);#endif#if defined (CONFIG_VFD) || defined (CONFIG_LCD)setup_videolfb_tag ((gd_t *) gd);#endifsetup_end_tag (bd);#endif /* we assume that the kernel is in place */printf ("\nStarting kernel ...\n\n"); //#ifdef CONFIG_USB_DEVICE#if 0{extern void udc_disconnect (void);udc_disconnect ();}#endif
设置完参数以后调用theKernel (0, machid, bd->bi_boot_params);
到入口地址启动内核。
- uboot源码分析——启动linux内核
- 6、uboot源码——内核启动分析
- uboot内核启动过程源码分析
- 嵌入式linux开发uboot移植(三)——uboot启动过程源码分析
- 分析uboot启动内核
- uboot分析之uboot启动内核分析
- Linux内核源码分析--内核启动
- uboot启动内核代码分析
- uboot - 启动内核过程分析
- 简单分析uboot启动内核
- 嵌入式linux开发uboot移植(四)——uboot启动内核的机制
- Uboot 源码分析----启动代码
- uboot源码分析-启动第一阶段
- UBOOT之源码分析——向内核传送参数过程分析
- Linux内核源码分析—Linux内核中的嵌入式汇编
- 嵌入式linux开发uboot移植(二)——uboot工程源码目录分析
- Linux内核---16.启动分析4uboot与内核的参数传递
- 简要分析Uboot是如何启动内核!
- oracle的字符集(NLS_LANGUAGE)
- A. 24 Game
- Valid Parentheses- LeetCode
- jQuery Mobile 移动网站开发之日期控件Mobiscroll 2.5 使用说明
- BASE64算法
- uboot源码分析——启动linux内核
- Android 开发笔记
- web架构资料汇总
- USACO Betsy's Tour 解题报告
- android之底部导航
- CentOS系统下Apache负载均衡、Tomcat集群以及项目热部署配置
- 第10周项目2
- Java基础笔记-第十记
- SDWebImage加载图片原理