linux-内核启动流程分析

匿名 (未验证) 提交于 2019-12-02 21:53:52
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/andrewgithub/article/details/82936861
 /******************************************************************************************/ 在主函数 main_loop中下面两行是启动内核的过程  # ifdef CONFIG_MENUKEY 	if (menukey == CONFIG_MENUKEY) { 	    s = getenv("menucmd");                          1 	    if (s) { # ifndef CFG_HUSH_PARSER 		run_command (s, 0);                              2 		 #endif 		} 	} #endif 

其中 getenv函数获取的 run_command要执行的指令就是bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0中的指令

下面这条指令是从nand读取内核:从哪里读?―从kernel分区中读取 == 0x00060000
读到哪里去?―0x30007FC0
分区的名字不重要,分的名字仅仅代表的是,起始地址和长度而已
nand read.jffs2 0x30007FC0 0x00060000 == nand read.jffs2 0x30007FC0 0x00060000 0x00200000;
读取的大小是 0x00200000 2M
使用 nand read.jffs2 可以不用页对其就能够进行读取的操作

bootm 0x30007FC0
0x30007FC0改地址只要不破,对咋堆栈等一些其他的信息就行,可以在随意放入改动,其值会
赋值给uimage的内核地址成员,因为uimage的头部中有加载地址和入口地址,所以地址可以随便放
内核的加载地址是: 30008000
0x30008000 - 0x30007FC0 = 0x40 = 64字节
将环境变量中下载内核的地址设置为 0x30007FC0 之后,下载的内核就可以不用移动直接使用了
因为uimage的头部信息刚好是64字节:

#define IH_NMLEN		32	/* Image Name Length		*/ typedef struct image_header { 	uint32_t	ih_magic;	/* Image Header Magic Number	*/ 	uint32_t	ih_hcrc;	/* Image Header CRC Checksum	*/ 	uint32_t	ih_time;	/* Image Creation Timestamp	*/ 	uint32_t	ih_size;	/* Image Data Size		*/ 	uint32_t	ih_load;	/* 加载地址 表示内核运行的时候要先放在那里*/ 	uint32_t	ih_ep;		/* 进入地址,要运行内核直接跳到这个地址就行了*/ 	uint32_t	ih_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; 总共  4*7+4+32 = 64字节  可以使用 mtd 命令查看;  OpenJTAG> mtd  device nand0 <nandflash0>, # parts = 4  #: name                        size            offset          mask_flags  0: bootloader          0x00040000      0x00000000      0  1: params              0x00020000      0x00040000      0  2: kernel              0x00200000      0x00060000      0  3: root                0x0fda0000      0x00260000      0  active partition: nand0,0 - (bootloader) 0x00040000 @ 0x00000000  defaults: mtdids  : nand0=nandflash0 mtdparts: mtdparts=nandflash0:256k@0(bootloader),128k(params),2m(kernel),-(root) 		 

注:
因为在嵌入式linux中没有PC机中那种非常庞大的文件管理系统,因此采用在源码中将分区写死的形式
为嵌入式linux中的文件进行分区
文件中一般会有此种定义:
```bash
#define MTDPARTS_DEFAULT “mtdparts=nandflash0:256k@0(bootloader),”
“128k(params),”
“2m(kernel),”
“-(root)”

嵌入式内核: uimage=头部+真正的内核 头部是一个结构体: ```c typedef struct image_header { 	uint32_t	ih_magic;	/* Image Header Magic Number	*/ 	uint32_t	ih_hcrc;	/* Image Header CRC Checksum	*/ 	uint32_t	ih_time;	/* Image Creation Timestamp	*/ 	uint32_t	ih_size;	/* Image Data Size		*/ 	uint32_t	ih_load;	/* 加载地址 表示内核运行的时候要先放在那里*/ 	uint32_t	ih_ep;		/* 进入地址,要运行内核直接跳到这个地址就行了*/ 	uint32_t	ih_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; 

1.然后会按照头部信息移动内核到合适的地址
2.启动内核
do_bootm_linux

u-boot 设置内核启动参数
然后跳到内核启动地址启动内核
u-boot和内核的参数交互使用的是将参数放置在和内核约定好的地址(按照双方约定好的格式进行)
定义的格式是TAG,地址是30000100
定义的TAG如下:

#ifdef CONFIG_INITRD_TAG static void setup_initrd_tag (bd_t *bd, ulong initrd_start, ulong initrd_end) { 	/* an ATAG_INITRD node tells the kernel where the compressed 	 * ramdisk can be found. ATAG_RDIMG is a better name, actually. 	 */ 	params->hdr.tag = ATAG_INITRD2; 	params->hdr.size = tag_size (tag_initrd);  	params->u.initrd.start = initrd_start; 	params->u.initrd.size = initrd_end - initrd_start;  	params = tag_next (params); } #endif /* CONFIG_INITRD_TAG */   #if defined (CONFIG_VFD) || defined (CONFIG_LCD) extern ulong calc_fbsize (void); static void setup_videolfb_tag (gd_t *gd) { 	/* An ATAG_VIDEOLFB node tells the kernel where and how large 	 * the framebuffer for video was allocated (among other things). 	 * Note that a _physical_ address is passed ! 	 * 	 * We only use it to pass the address and size, the other entries 	 * in the tag_videolfb are not of interest. 	 */ 	params->hdr.tag = ATAG_VIDEOLFB; 	params->hdr.size = tag_size (tag_videolfb);  	params->u.videolfb.lfb_base = (u32) gd->fb_base; 	/* Fb size is calculated according to parameters for our panel 	 */ 	params->u.videolfb.lfb_size = calc_fbsize();  	params = tag_next (params); } #endif /* CONFIG_VFD || CONFIG_LCD */  #if defined (CONFIG_SETUP_MEMORY_TAGS) || \     defined (CONFIG_CMDLINE_TAG) || \     defined (CONFIG_INITRD_TAG) || \     defined (CONFIG_SERIAL_TAG) || \     defined (CONFIG_REVISION_TAG) || \     defined (CONFIG_VFD) || \     defined (CONFIG_LCD) static void setup_start_tag (bd_t *bd);  # ifdef CONFIG_SETUP_MEMORY_TAGS static void setup_memory_tags (bd_t *bd); # endif static void setup_commandline_tag (bd_t *bd, char *commandline);  #if 0 static void setup_ramdisk_tag (bd_t *bd); #endif # ifdef CONFIG_INITRD_TAG static void setup_initrd_tag (bd_t *bd, ulong initrd_start, 			      ulong initrd_end); # endif static void setup_end_tag (bd_t *bd);  # if defined (CONFIG_VFD) || defined (CONFIG_LCD) static void setup_videolfb_tag (gd_t *gd); # endif 

===============================================================================
主要是下面四个函数:

  setup_memory_tags (bd_t *bd);   setup_memory_tags (bd_t *bd);   setup_commandline_tag (bd_t *bd, char *commandline);   setup_end_tag (bd_t *bd);  函数体如下: setup_start_tag 函数的作用是将  地址执行的前五个字节放置上相关信息 static void setup_start_tag (bd_t *bd) { 	params = (struct tag *) bd->bi_boot_params;   //   bd->bi_boot_params = 30000100 		//gd->bd->bi_boot_params = 0x30000100;  参数放置在 30000100的地址处 	params->hdr.tag = ATAG_CORE;   //#define ATAG_CORE	0x54410001 	params->hdr.size = tag_size (tag_core); // 等于  5 	//#define tag_size(type)	((sizeof(struct tag_header) + sizeof(struct type)) >> 2) 	// >> 2 右移 2 相当于除以4 	  	struct tag_header { 	u32 size; 	u32 tag; };        + 	struct tag_core { 	u32 flags;		/* bit 0 = read-only */ 	u32 pagesize; 	u32 rootdev; };        = 20字节 	   除以4之后刚好是5 	  	struct tag_core { 	u32 flags;		/* bit 0 = read-only */ 	u32 pagesize; 	u32 rootdev; }; 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; }; 	接下来就是将下面的参数按照顺序装进内存中去 	params->u.core.flags = 0; 	params->u.core.pagesize = 0; 	params->u.core.rootdev = 0;  	params = tag_next (params); 	做了一件事就是讲   先后移动  5 个字节  	//#define tag_next(t)	((struct tag *)((u32 *)(t) + (t)->hdr.size)) 	 }==========================================================================================================  setup_memory_tags 函数体如下:   static void setup_memory_tags (bd_t *bd) { 	int i; =========================================================== 	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; }  ==============================================================	 	 	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); 	} } 

===================================================================================

setup_commandline_tag函数:
char *commandline = getenv (“bootargs”);
bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
init=/linuxrc 第一个应用程序是 linuxrc
root=/dev/mtdblock3 根文件系统位于第四个flash分区
console=ttySAC0 指定打印信息输出的地方 ttySAC0 --> 串口 0

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;  //54410009 	params->hdr.size = 		(sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;  	strcpy (params->u.cmdline.cmdline, p);      	params = tag_next (params); } =======================================================================================    setup_end_tag (bd_t *bd);函数:  static void setup_end_tag (bd_t *bd) { 	params->hdr.tag = ATAG_NONE; 	params->hdr.size = 0; } 

=========================================================================================================
在这里设置好之后内核会到这个地址来读取这份参数:

注:使用sourceinsight搜索变量的时候,将 search method设置成 --> Regular expression 

内核启动;
//在这里启动内核
//bi_arch_number 机器 ID
//运行该函数之后 控制权就交给了 内核

	theKernel (0, bd->bi_arch_number, bd->bi_boot_params); 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!