WDS1期第10课 内核 4 内核启动 分区

内核,处理uboot传入的参数,启动内核,挂接根文件系统,启动应用程序。
最终目的是启动应用程序。

在arch/arm中,head.S有两个,arch/arm/kernel/head.Sarch/arm/boot/compressed/head.S,内核可能编译后很大,所以也可以压缩内核成 自解压代码+压缩的内核,程序运行时从自解压代码开始然后解压后面的内核。

https://blog.csdn.net/hpu11/article/details/53326609

插播C语言的__artribute__:
告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。这个功能是跟操作系统没关系,跟编译器有关。
GNU C的一大特色就是__attribute__机制。__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。
__attribute__语法格式为:
__attribute__ ((attribute-list))
其位置约束为:声明的尾部,“;”之前。
typedef struct {
    double x;
    double y;
} __attribute__((packed)) position_t;
1. 编译器不优化对齐
2. 使编译器错误检查更强大

packed是类型属性(Type Attribute)的一个参数,使用packed可以减小对象占用的空间。

head.S的第一阶段工作

在这里插入图片描述
读head.S, __lookup_processor_type查看此内核支持哪些cpu,支持哪些单板__lookup_machine_type,如果不支持将跳到死循环。__lookup_machine_type

	.section ".text.head", "ax"
	.type	stext, %function
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
	movs	r10, r5				@ invalid processor (r5=0)?
	beq	__error_p			@ yes, error 'p'
	bl	__lookup_machine_type		@ r5=machinfo
	movs	r8, r5				@ invalid machine (r5=0)?
	beq	__error_a			@ yes, error 'a'
	bl	__create_page_tables

在include/asm-arm/mach-types.h中定义机器id,在uboot中启动内核的函数的参数包括id theKernel (0, bd->bi_arch_number, bd->bi_boot_params);。根据交互规则bi_arch_number存放在r1寄存器里的,

#define MACH_TYPE_TOTO                 361
#define MACH_TYPE_S3C2440              362
#define MACH_TYPE_KS8695P              363

__lookup_machine_type在arch/arm/kernel/head-common.S中,r3=3b的地址,3b指3: .long .这段代码,r4=".","."是虚拟地址, r5=__arch_info_beginr6=__arch_info_end
sub r3, r3, r4虚拟地址-物理地址=offset,MMU还没有启动,所以前面的3对应标号是实际物理地址,r5r6对应加上r3(offset)变成__arch_info_begin __arch_info_end的真正物理地址。

3:	.long	.
	.long	__arch_info_begin
	.long	__arch_info_end
	
	...
	
	.type	__lookup_machine_type, %function
__lookup_machine_type:
	adr	r3, 3b
	ldmia	r3, {r4, r5, r6}
	sub	r3, r3, r4			@ get offset between virt&phys
	add	r5, r5, r3			@ convert virt addresses to
	add	r6, r6, r3			@ physical address space
1:	ldr	r3, [r5, #MACHINFO_TYPE]	@ get machine type
	teq	r3, r1				@ matches loader number?
	beq	2f				@ found
	add	r5, r5, #SIZEOF_MACHINE_DESC	@ next machine_desc
	cmp	r5, r6
	blo	1b
	mov	r5, #0				@ unknown machine
2:	mov	pc, lr

__arch_info_begin __arch_info_end这俩东西不在内核定义,在链接脚本里面arch/arm/kernel/vmlinux.lds,架构相关的初始化信息,

  __arch_info_begin = .;
   *(.arch.info.init)
  __arch_info_end = .;

.arch.info.init在include/asm-arm/mach/arch.h中涉及到,其中定义了个宏MACHINE_START,

/*
 * Set of macros to define architecture features.  This is built into
 * a table by the linker.
 */
#define MACHINE_START(_type,_name)			\
static const struct machine_desc __mach_desc_##_type	\
 __used							\
 __attribute__((__section__(".arch.info.init"))) = {	\
	.nr		= MACH_TYPE_##_type,		\
	.name		= _name,

#define MACHINE_END				\
};

MACHINE_START这个宏在arch/arm/mach-s3c2440/mach-smdk2440.c中使用。按之前分析uboot的思路,这个应该会定义一个结构体,这个结构体会被强制设置属性,段被设置为".arch.info.init",每个这样的结构体会被链接脚本组合成一块,

MACHINE_START(S3C2440, "SMDK2440")
	/* 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,

	.init_irq	= s3c24xx_init_irq,
	.map_io		= smdk2440_map_io,
	.init_machine	= smdk2440_machine_init,
	.timer		= &s3c24xx_timer,
MACHINE_END

将上面展开来看,定义一个静态的机器描述的结构体对象,名字为__mach_desc_S3C2440,

static const struct machine_desc __mach_desc_S3C2440	
 __used							
 __attribute__((__section__(".arch.info.init"))) = {	
	.nr		= MACH_TYPE_S3C2440,		
	.name		= "SMDK2440", // 后面的跟着写进来 
	/* 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,

	.init_irq	= s3c24xx_init_irq,
	.map_io		= smdk2440_map_io,
	.init_machine	= smdk2440_machine_init,
	.timer		= &s3c24xx_timer,
};

自然会查看这个结构体究竟是什么组合,于是去找machine_desc的定义,在include/asm-arm/mach/arch.h下,nr机器id,物理io,uboot传入的参数boot_params等等。所以内核支持多少单板就会有多少由宏定义生成这样的struct,这个结构体被定义了一个属性就是段被强制设置为".arch.info.init",

struct machine_desc {
	/*
	 * Note! The first four elements are used
	 * by assembler code in head-armv.S
	 */
	unsigned int		nr;		/* architecture number	*/
	unsigned int		phys_io;	/* start of physical io	*/
	unsigned int		io_pg_offst;	/* byte offset for io 
						 * page tabe entry	*/

	const char		*name;		/* architecture name	*/
	unsigned long		boot_params;	/* tagged list		*/

	unsigned int		video_start;	/* start of video RAM	*/
	unsigned int		video_end;	/* end of video RAM	*/

	unsigned int		reserve_lp0 :1;	/* never has lp0	*/
	unsigned int		reserve_lp1 :1;	/* never has lp1	*/
	unsigned int		reserve_lp2 :1;	/* never has lp2	*/
	unsigned int		soft_reboot :1;	/* soft reboot		*/
	void			(*fixup)(struct machine_desc *,
					 struct tag *, char **,
					 struct meminfo *);
	void			(*map_io)(void);/* IO mapping function	*/
	void			(*init_irq)(void);
	struct sys_timer	*timer;		/* system tick timer	*/
	void			(*init_machine)(void);
};

这些结构体被放在这里,然后内核启动从begin读到end,然后把这个读到的id和uboot传入的参数比较,相等则支持该单板,

__arch_info_begin = .;
   *(.arch.info.init)
  __arch_info_end = .;

比较在这里进行,r5是begin,r1为传入的参数,

	.type	__lookup_machine_type, %function
__lookup_machine_type:
	adr	r3, 3b
	ldmia	r3, {r4, r5, r6}
	sub	r3, r3, r4			@ get offset between virt&phys
	add	r5, r5, r3			@ convert virt addresses to
	add	r6, r6, r3			@ physical address space
1:	ldr	r3, [r5, #MACHINFO_TYPE]	@ get machine type
	teq	r3, r1				@ matches loader number?
	beq	2f				@ found
	add	r5, r5, #SIZEOF_MACHINE_DESC	@ next machine_desc
	cmp	r5, r6
	blo	1b
	mov	r5, #0				@ unknown machine
2:	mov	pc, lr

比较完相等之后回到head.S,继续往下执行,创建页表,

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
	movs	r10, r5				@ invalid processor (r5=0)?
	beq	__error_p			@ yes, error 'p'
	bl	__lookup_machine_type		@ r5=machinfo
	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 has been enabled
	adr	lr, __enable_mmu		@ return (PIC) address
	add	pc, r10, #PROCINFO_INITFUNC

为什么创建页表,内核的链接地址是. = (0xc0000000) + 0x00008000;,但这个地址并不对应于真实存在的内存,真实的内存是从0x30000000开始,所以这里需要建立页表去启动MMU。
start_kernel是内核的第一个c函数。
在head.S中第一部分完成了,

  1. 判断是否支持该cpu (__lookup_processor_type)
  2. 判断是否支持该单板 (__lookup_machine_type)
  3. 创建页表 (__create_page_tables)
  4. 使能mmu (__switch_data)
  5. 跳转到 start_kernel (b start_kernel)

head.S 跳转到 start_kernel

在这里插入图片描述
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
之前机器id参数已经在head.S的第一部分处理完成,接下来是第二个参数是uboot传过来的参数bd->bi_boot_params要在start_kernel中处理,在init/main.c中,首先各种初始化,打印内核版本信息, printk(linux_banner);
其中 setup_arch(&command_line); setup_command_line(command_line);两个就是用来处理uboot传入的参数的,内存多大,内存起始地址,命令行参数(bootargs传入)。
那两个函数在arch/arm/kernel/setup.c和init/main.c中定义。

asmlinkage void __init start_kernel(void)
{
	char * command_line;
	extern struct kernel_param __start___param[], __stop___param[];

	smp_setup_processor_id();

	/*
	 * Need to run as early as possible, to initialize the
	 * lockdep hash:
	 */
	unwind_init();
	lockdep_init();

	local_irq_disable();
	early_boot_irqs_off();
	early_init_irq_lock_class();

/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
	lock_kernel();
	tick_init();
	boot_cpu_init();
	page_address_init();
	printk(KERN_NOTICE);
	printk(linux_banner);
	setup_arch(&command_line);
	setup_command_line(command_line);
	unwind_setup();
	setup_per_cpu_areas();
	smp_prepare_boot_cpu();	/* arch-specific boot-cpu hooks */

setup_arch里,machine_desc跟前面分析的自定义结构是一样的,
boot_params和前面的.boot_params = S3C2410_SDRAM_PA + 0x100, == 0x30000100,与uboot的board/100ask24x0/100ask24x0.c文件中的,gd->bd->bi_boot_params = 0x30000100;对应。接着tag,取出tag,解析tag。
命令行参数(bootargs),如果没有uboot没有传入命令行,那么使用默认的命令行default_command_line,

void __init setup_arch(char **cmdline_p)
{
	struct tag *tags = (struct tag *)&init_tags;
	struct machine_desc *mdesc;
	char *from = default_command_line;

	setup_processor();
	mdesc = setup_machine(machine_arch_type);
	machine_name = mdesc->name;

	if (mdesc->soft_reboot)
		reboot_setup("s");

	if (mdesc->boot_params)
		tags = phys_to_virt(mdesc->boot_params);

	/*
	 * If we have the old style parameters, convert them to
	 * a tag list.
	 */
	if (tags->hdr.tag != ATAG_CORE)
		convert_to_tag_list(tags);
	if (tags->hdr.tag != ATAG_CORE)
		tags = (struct tag *)&init_tags;

	if (mdesc->fixup)
		mdesc->fixup(mdesc, tags, &from, &meminfo);

	if (tags->hdr.tag == ATAG_CORE) {
		if (meminfo.nr_banks != 0)
			squash_mem_tags(tags);
		parse_tags(tags);
	}

	init_mm.start_code = (unsigned long) &_text;
	init_mm.end_code   = (unsigned long) &_etext;
	init_mm.end_data   = (unsigned long) &_edata;
	init_mm.brk	   = (unsigned long) &_end;

	memcpy(boot_command_line, from, COMMAND_LINE_SIZE);
	boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
	parse_cmdline(cmdline_p, from);
	paging_init(&meminfo, mdesc);
	request_standard_resources(&meminfo, mdesc);

#ifdef CONFIG_SMP
	smp_init_cpus();
#endif

	cpu_init();

	/*
	 * Set up various architecture-specific pointers
	 */
	init_arch_irq = mdesc->init_irq;
	system_timer = mdesc->timer;
	init_machine = mdesc->init_machine;

#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
	conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
	conswitchp = &dummy_con;
#endif
#endif

#if	defined(CONFIG_KGDB)
	extern void __init early_trap_init(void);
	early_trap_init();
#endif
}

内核启动流程,缩进调用关系,

start_kernel
	setup_arch			//解析uboot传入的启动参数
	setup_command_line  //解析uboot传入的启动参数
	rest_init // 内核
		kernel_init
			prepare_namespace
				mount_root // 挂接根文件系统(识别)
			init_post(); // 执行应用程序 

以prepare_namespace分析参数如何处理,根文件系统放在第三分区,
bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200
在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);

static char * __initdata root_mount_data;
static int __init root_data_setup(char *str)
{
	root_mount_data = str;
	return 1;
}

在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")))	\
		__attribute__((aligned((sizeof(long)))))	\
		= { __setup_str_##unique_id, fn, early }

#define __setup_null_param(str, unique_id)			\
	__setup_param(str, unique_id, NULL, 0)

#define __setup(str, fn)					\
	__setup_param(str, fn, fn, 0)

链接脚本中,

  __setup_start = .;
   *(.init.setup)
  __setup_end = .;

查看__setup_start在哪里被使用,在init/main.c中,obsolete_checksetup,do_early_param调用,parse_early_param调用do_early_param,
__setup_start被使用,然后从start到end调用函数(用early标志的函数)

/* Check for early params. */
static int __init do_early_param(char *param, char *val)
{
	struct obs_kernel_param *p;

	for (p = __setup_start; p < __setup_end; p++) {
		if (p->early && strcmp(param, p->str) == 0) {
			if (p->setup_func(val) != 0)
				printk(KERN_WARNING
				       "Malformed early option '%s'\n", param);
		}
	}
	/* We accept everything at this stage. */
	return 0;
}

内核启动流程,缩进调用关系,

start_kernel
	setup_arch			//解析uboot传入的启动参数
	setup_command_line  //解析uboot传入的启动参数
	parse_early_param
		do_early_param       // 从start到end调用early函数
	unknown_bootoption
		obsolete_checksetup  // 从start到end调用非early函数(__setup设置为)
	rest_init // 内核
		kernel_init
			prepare_namespace
				mount_root // 挂接根文件系统(识别)
			init_post(); // 执行应用程序 

uboot传入的参数被内核保存到一个字符串里,最后调用函数一一分析,就用__setup("root=", root_dev_setup);定义在结构体里,结构体被加了段属性,很多结构体被链接脚本链接到一起。
flash没有分区表,boot-环境变量-kernel-sysfile,在代码里写死,代码在arch/arm/plat-s3c24xx/common-smdk.c中,

static struct mtd_partition smdk_default_nand_part[] = {
	[0] = {
        .name   = "bootloader",
        .size   = 0x00040000,
		.offset	= 0,
	},
	[1] = {
        .name   = "params",
        .offset = MTDPART_OFS_APPEND,
        .size   = 0x00020000,
	},
	[2] = {
        .name   = "kernel",
        .offset = MTDPART_OFS_APPEND,
        .size   = 0x00200000,
	},
	[3] = {
        .name   = "root",
        .offset = MTDPART_OFS_APPEND,
        .size   = MTDPART_SIZ_FULL,
	}
};

参考优秀总结:

https://www.cnblogs.com/lcw/p/3337937.html
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值