linux-内核启动流程分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 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);

猜你喜欢

转载自blog.csdn.net/andrewgithub/article/details/82936861