kernel内核启动流程

发布时间 2023-09-05 21:39:11作者: charlie12345
(1)自解压代码
	linux-2.6.22.6\arch\arm\boot\compressed\head.S			
		对比于linux-2.6.22.6\arch\arm\kernel\head.S,  是自解压代码+原本的代码,执行时执行自解压代码的内容
(2)第一阶段:
	ENTRY(stext)
		msr	cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
							@ and irqs disabled
		mrc	p15, 0, r9, c0, c0		@ get processor id
		bl	__lookup_processor_type		@ r5=procinfo r9=cpuid			//判断是否支持这个CPU
		movs	r10, r5				@ invalid processor (r5=0)?
		beq	__error_p			@ yes, error 'p'
		bl	__lookup_machine_type		@ r5=machinfo					//比较遍历内核的机器码的 machine_desc 结构体,与uboot传入的机器码比较,判断是否支持这个单板
		movs	r8, r5				@ invalid machine (r5=0)?
		beq	__error_a			@ yes, error 'a'
		bl	__create_page_tables										//建立页表

		/*
		 * The following calls CPU specific code in a position independent
		 * manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of
		 * xxx_proc_info structure selected by __lookup_machine_type
		 * above.  On return, the CPU will be ready for the MMU to be
		 * turned on, and r0 will hold the CPU control register value.
		 */
		ldr	r13, __switch_data		@ address to jump to after			//后面的mmu使能后,在执行该跳转
							@ mmu has been enabled
		adr	lr, __enable_mmu		@ return (PIC) address				//使能mmu
		add	pc, r10, #PROCINFO_INITFUNC
	
	
					arch/arm/kernel/vmlinux.lds:217:   *(.arch.info.init)		
					include/asm-arm/mach/arch.h:53: __attribute__((__section__(".arch.info.init"))) = {			
					
					#define MACHINE_START(_type,_name)			\								linux-2.6.22.6\include/asm-arm/mach/arch.h
					static const struct machine_desc __mach_desc_##_type	\
					 __used							\
					 __attribute__((__section__(".arch.info.init"))) = {	\				//注意设置为段:.arch.info.init
						.nr		= MACH_TYPE_##_type,		\
						.name		= _name,

					#define MACHINE_END				\
					};

					MACHINE_START(S3C2440, "SMDK2440")					是定义一个结构体,在Linux中被广泛调用用来定义板子机器码对应的结构体   	linux-2.6.22.6\arch\arm\mach-s3c2440\mach-smdk2440.c 
					/* Maintainer: Ben Dooks <ben@fluff.org> */
					.phys_io	= S3C2410_PA_UART,
					.io_pg_offst	= (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
					.boot_params	= S3C2410_SDRAM_PA + 0x100,			//这里等于 0x30000000+0x100

					.init_irq	= s3c24xx_init_irq,
					.map_io		= smdk2440_map_io,
					.init_machine	= smdk2440_machine_init,
					.timer		= &s3c24xx_timer,
					MACHINE_END
	
		
		.type	__switch_data, %object
	__switch_data:
		.long	__mmap_switched
		.long	__data_loc			@ r4
		.long	__data_start			@ r5
		.long	__bss_start			@ r6
		.long	_end				@ r7
		.long	processor_id			@ r4
		.long	__machine_arch_type		@ r5
		.long	cr_alignment			@ r6
		.long	init_thread_union + THREAD_START_SP @ sp

		
		.type	__mmap_switched, %function
	__mmap_switched:
		adr	r3, __switch_data + 4

		ldmia	r3!, {r4, r5, r6, r7}
		cmp	r4, r5				@ Copy data segment if needed
	1:	cmpne	r5, r6
		ldrne	fp, [r4], #4
		strne	fp, [r5], #4
		bne	1b

		mov	fp, #0				@ Clear BSS (and zero fp)
	1:	cmp	r6, r7
		strcc	fp, [r6],#4
		bcc	1b

		ldmia	r3, {r4, r5, r6, sp}
		str	r9, [r4]			@ Save processor ID
		str	r1, [r5]			@ Save machine type
		bic	r4, r0, #CR_A			@ Clear 'A' bit
		stmia	r6, {r0, r4}			@ Save control register values
		b	start_kernel

(3)第二阶段:start_kernel 					linux-2.6.22.6\init\main.c
	asmlinkage void __init start_kernel(void)
	{
		printk(KERN_NOTICE);
		printk(linux_banner);					//打印Linux内核版本信息
		setup_arch(&command_line);				//解析uboot传入的启动参数, 并会调用 machine_desc 的相关初始化函数
		setup_command_line(command_line);		//解析uboot传入的启动参数
		
		parse_early_param();
			| do_early_param
				|从 __setup_start 到 __setup_end 调用 early 函数,
		
		unknown_bootoption
			| obsolete_checksetup
				|从 __setup_start 到 __setup_end 调用 非early 函数
				
		rest_init();							//
			| kernel_init
				| prepare_namespace	
					| mount_root				//挂载根文件系统
				| init_post						//执行应用程序
	}	
		
		
		
	void __init setup_arch(char **cmdline_p)
	{
		char *from = default_command_line;

		if (mdesc->boot_params)
			tags = phys_to_virt(mdesc->boot_params);
			
		parse_cmdline(cmdline_p, from);			//解析,若没有传入命令行参数,则使用默认参数
	}
		
	static void noinline __init_refok rest_init(void)
		__releases(kernel_lock)
	{
			kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);		//创建内核线程

	}
		
			static int __init kernel_init(void * unused)
			{
				prepare_namespace();					
				init_post();							//执行应用程序
			}	
			
					void __init prepare_namespace(void)
					{
						if (saved_root_name[0]) {
							root_device_name = saved_root_name;

					
						mount_root();							//挂载根文件系统
					}
			
					static int noinline init_post(void)			linux-2.6.22.6\init\main.c
					{
						if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
						run_init_process("/sbin/init");
						run_init_process("/etc/init");
						run_init_process("/bin/init");
						run_init_process("/bin/sh");
					}
	
			=============
			
			linux-2.6.22.6\init\do_mounts.c
				static int __init root_dev_setup(char *line)
				{
					strlcpy(saved_root_name, line, sizeof(saved_root_name));
					return 1;
				}

				__setup("root=", root_dev_setup);			//定义一个结构体,不同的参数名 会对应一个处理函数


			linux-2.6.22.6\include\linux\init.h
				#define __setup_param(str, unique_id, fn, early)			\
					static char __setup_str_##unique_id[] __initdata = str;	\
					static struct obs_kernel_param __setup_##unique_id	\
						__attribute_used__				\
						__attribute__((__section__(".init.setup")))	\		//注意设置为段:.init.setup
						__attribute__((aligned((sizeof(long)))))	\
						= { __setup_str_##unique_id, fn, early }

				#define __setup(str, fn)					\
					__setup_param(str, fn, fn, 0)
			
			
			arch/arm/kernel/vmlinux.lds	链接文件中定义
			  __setup_start = .;
			   *(.init.setup)
			  __setup_end = .;
	
	void __init parse_early_param(void)				linux-2.6.22.6\init\main.c
	{
		strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
		parse_args("early options", tmp_cmdline, NULL, 0, do_early_param);
	}
	
			static int __init do_early_param(char *param, char *val)			linux-2.6.22.6\init\main.c
			{
				for (p = __setup_start; p < __setup_end; p++) {
					if (p->early && strcmp(param, p->str) == 0) {				//p->early为0
						if (p->setup_func(val) != 0)

			}		

参考:韦东山Linux教程