1. 程式人生 > >學習筆記 --- LINUX核心啟動第二階段分析(不考慮自解壓過程)

學習筆記 --- LINUX核心啟動第二階段分析(不考慮自解壓過程)

上篇文章中分析了Linux核心從head.s啟動:

	.section ".text.head", "ax"
ENTRY(stext)
	setmode	PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ 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	__vet_atags
	bl	__create_page_tables

1 開始設定ARM模式為管理模式,禁止中斷,然後獲取處理器ID

2 呼叫__lookup_processor_type 判斷核心是否支援該處理器,這裡略,不講!

3 處理器ID驗證完後呼叫  __lookup_machine_type  判斷核心是否支援該機器型別:

__lookup_machine_type:
	adr	r3, 4b
	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
ENDPROC(__lookup_machine_type)
第二行4b的位置是:
4:	.long	.
	.long	__arch_info_begin
	.long	__arch_info_end
所以2,3行執行之後:

r4 = .  r5=__arch_info_begin  r6=__arch_info_end
4,5,6行都有解釋,目的就是為了計算出__arch_info_begin 和__arch_info_end在SDRAM裡面實際存放地址,這兩個東西是連結檔案裡面定義的arch.info.init段的起始和結束標誌:

__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
而arch.info.init段存放的是一個結構體,從程式碼可以看出:
/*
 * 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				\
};

該結構體格式為:

struct machine_desc {
	/*
	 * Note! The first four elements are used
	 * by assembler code in head.S, head-common.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);
};
定義的實體:
MACHINE_START(S3C2440, "SMDK2440")
	/* Maintainer: Ben Dooks <[email protected]> */
	.phys_io	= S3C2410_PA_UART,
	.io_pg_offst	= (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
	.boot_params	= S3C2410_SDRAM_PA + 0x100,     //就是0x30000100

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

還有很多很多不同的單板都定義了這個,可以知道,這個arch.info.init段裡面放的就是linux核心支援的所有單板的引數資訊,再回到彙編,7-10行就是判斷每個單板的機器型別(讀到暫存器r3)是否和uboot傳入的單板引數(存在暫存器r1)匹配,如果匹配則核心支援該單板。

4 接著執行__vet_atags :

__vet_atags:
	tst	r2, #0x3			@ aligned?
	bne	1f

	ldr	r5, [r2, #0]			@ is first tag ATAG_CORE?
	cmp	r5, #ATAG_CORE_SIZE
	cmpne	r5, #ATAG_CORE_SIZE_EMPTY
	bne	1f
	ldr	r5, [r2, #4]
	ldr	r6, =ATAG_CORE
	cmp	r5, r6
	bne	1f

	mov	pc, lr				@ atag pointer is ok

1:	mov	r2, #0
	mov	pc, lr
ENDPROC(__vet_atags)
這個函式就是去驗證uboot傳入的TAG引數區域的ATAG_CORE_SIZE與ATAG_CORE是否和核心的匹配,這段TAG引數起始地址存在暫存器r2,讀出來再比較這兩項是否一致,如果一致則通過。

5 接下來建立頁表

6 使能MMU

最後跳轉到 start_kernel 進入C語言程式碼段,開始列印版本資訊,然後執行下面這段程式碼,主要作用就是解析uboot傳入的引數:

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

	unwind_init();

	setup_processor();
	mdesc = setup_machine(machine_arch_type); //設定機器型別,返回machine_desc結構體,這個結構體通過MACHINE_START設定好了
	machine_name = mdesc->name;
	if (mdesc->soft_reboot)
		reboot_setup("s");

	if (__atags_pointer)
		tags = phys_to_virt(__atags_pointer);
	else if (mdesc->boot_params)  //就是結構體裡面的S3C2410_SDRAM_PA + 0x100 = 0x30000100,這個地址存放uboot設定好的TAG引數
		tags = phys_to_virt(mdesc->boot_params); //轉為虛擬地址,返回tags,tags指向uboot存放的TAG引數起始地址

	/*
	 * 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)   //如果TAG引數不匹配,則使用核心預設的引數
		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);
		save_atags(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);  //解析命令列,就是解析uboot傳入的bootargs環境變數,它為TAG引數的一部分
	paging_init(mdesc);
	request_standard_resources(&meminfo, mdesc);

#ifdef CONFIG_SMP
	smp_init_cpus();
#endif

	cpu_init();
	tcm_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
	early_trap_init();
}
上面執行完後,返回bootargs引數到cmdline_p,然後再執行下面函式,將命令列地址存放在全域性指標saved_command_line裡面:
static void __init setup_command_line(char *command_line)
{
	saved_command_line = alloc_bootmem(strlen (boot_command_line)+1);
	static_command_line = alloc_bootmem(strlen (command_line)+1);
	strcpy (saved_command_line, boot_command_line);
	strcpy (static_command_line, command_line);
}
然後下面會用unknown_bootoption函式來解析這個命令列,比如檔案系統掛載到哪個地方,使用串列埠幾作為監控端等;

接下來是一大堆初始化,執行到最後rest_init:

kernel_init ---- prepare_namespace ---- mount_root (掛接根檔案系統)

掛接好之後 init_post:

static noinline int init_post(void)
	__releases(kernel_lock)
{
	/* need to finish all async __init code before freeing the memory */
	async_synchronize_full();
	free_initmem();
	unlock_kernel();
	mark_rodata_ro();
	system_state = SYSTEM_RUNNING;
	numa_default_policy();

	if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)  //開啟終端檔案 ----用於標準printf列印
		printk(KERN_WARNING "Warning: unable to open an initial console.\n");

	(void) sys_dup(0);  //複製終端檔案 用於 標準scanf輸入
	(void) sys_dup(0);  //複製終端檔案 用於 標準error列印

	current->signal->flags |= SIGNAL_UNKILLABLE;

	if (ramdisk_execute_command) {    
		run_init_process(ramdisk_execute_command);
		printk(KERN_WARNING "Failed to execute %s\n",
				ramdisk_execute_command);
	}

	/*
	 * We try each of these until one succeeds.
	 *
	 * The Bourne shell can be used instead of init if we are
	 * trying to recover a really broken machine.
	 */
	if (execute_command) {   //
		run_init_process(execute_command);  //執行uboot傳入的“init=xxx”指定的應用指令碼
		printk(KERN_WARNING "Failed to execute %s.  Attempting "
					"defaults...\n", execute_command);
	}
	run_init_process("/sbin/init");  //如果上面uboot沒有指定,那麼往下執行預設路徑的指令碼
	run_init_process("/etc/init");
	run_init_process("/bin/init");
	run_init_process("/bin/sh");

	panic("No init found.  Try passing init= option to kernel.");
}
上面掛接檔案系統mount_root ,掛接到哪裡是由環境變數bootargs裡面的“root=” 類引數決定的,是通過unknown_bootoption函式解析出來的。

假設uboot傳進來引數為  “root=/dev/mtdblock3” 則表示掛載到mtd裝置的第4個分割槽,至於這個分割槽對應哪裡是在nandflash的驅動裡面指定的:

static struct mtd_partition friendly_arm_default_nand_part[] = {
	[0] = {
		.name	= "uboot",
		.size	= 0x00040000,
		.offset	= 0,
	},
	[1] = {
		.name	= "param",
		.offset = 0x00040000,
		.size	= 0x00020000,
	},
	[2] = {
		.name	= "Kernel",
		.offset = 0x00060000,
		.size	= 0x00500000,
	},
	[3] = {                 
		.name	= "root",
		.offset = 0x00560000,
		.size	= 1024 * 1024 * 1024, 
	},
	[4] = {
		.name	= "nand",
		.offset = 0x00000000,
		.size	= 1024 * 1024 * 1024, //
	}
};

上面可以知道第4個分割槽就是root根檔案系統分割槽,這個在一直nand驅動的時候要和uboot傳進的引數對應起來。




相關推薦

學習筆記 --- LINUX核心啟動第二階段分析(考慮過程)

上篇文章中分析了Linux核心從head.s啟動: .section ".text.head", "ax" ENTRY(stext) setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode

Linux核心啟動第二階段之setup_arch函式分析

轉自:http://blog.chinaunix.net/uid-20672257-id-2383451.html 執行setup_arch()函式 回到start_kernel當中,569行,呼叫setup_arch函式,傳給他的引數是

學習筆記 --- LINUX核心裡面的likely與unlikely

看核心時總遇到if(likely( )){}或是if(unlikely( ))這樣的語句,最初不解其意,現在有所瞭解,所以也想介紹一下。 likely() 與 unlikely()是核心(我看的是2.6.22.6版本,2.6的版本應該都有)中定義的兩個巨集。位於/in

嵌入式 arm平臺kernel啟動第二階段分析

其中:PHYS_OFFSET在arch/arm/mach-s3c2410/include/mach/memory.h定義,為UL(0x30000000),而TEXT_OFFSET在arch/arm/Makefile中定義,為核心映象在記憶體中到記憶體開始位置的偏移(位元組),為$(textofs-y) tex

網易公開課《Linux核心分析學習心得-Linux核心如何裝載和啟動一個可執行程式

實驗 設定斷點sys_execeve,並繼續 程式碼執行到了SyS_execve。在QEMU中執行exec,可以看到只能出現兩句,沒有完全執行完畢。 設定斷點load_elf_binary和start_thread,並執行,可以看到程式碼停在了

儲存管理(二)--學習Linux核心原始碼情景分析第二章(方便理解,內容在註釋中)

        2.7 物理頁面的分配         分配若干頁面時,分配頁面用於DMA(direct memory assess)當然應該是連續的,其實出於物理儲存空間質地一致性考慮,記憶

儲存管理(一)--學習Linux核心原始碼情景分析第二章(方便理解,內容在註釋中)

2.1 Linux記憶體管理基本框架         32位cpu上的頁式記憶體管理是採用兩層對映方式,但在64位cpu上採用兩層對映方式就不太合理了,所以在Linux中頁式管理採用的是三層對映方式:頁面目錄(PGD)、中間目錄(P

七.linux開發之uboot移植(七)——uboot原始碼分析2-啟動第二階段之start_armboot函式分析1

一.uboot啟動第二階段之start_armboot函式簡介 1.start_armboot函式簡介 (1)這個函式在uboot/lib_arm/board.c的第444行開始到908行結束。 (2)、即一個函式組成uboot第二階段 2、

Linux學習筆記之內核啟動流程與模塊機制

oid img 相關 call rootfs _exit alt 執行 分模塊 本文旨在簡單的介紹一下Linux的啟動流程與模塊機制: Linux啟動的C入口位於/Linux.2.6.22.6/init/main.c::start_kernel() 下圖簡要的描述了一下內核

Linux學習筆記第三周第二次課(2月6日)

gid ins 目錄 隨機 useradd 生成 echo tab 設置 3.4 usermod命令更改用戶屬性usermod,更改UID,命令為#usermod -u 編號;更改GID,命令為#usermod -g 編號;更改用戶家目錄,命令為#usermod -d 編號

Linux學習筆記第五周第二次課(3月6日)

yum wget curl -O make ./configure --prefi 7.6 yum更換國內源wget 下載網址,下載文件;安裝wget命令,#yum install -y wget;curl -O 下載網址,下載文件7.7 yum下載rpm包安裝擴展源epel安裝命令#yu

《2.uboot和系統移植-第6部分-2.6.uboot原始碼分析2-啟動第二階段

《2.uboot和系統移植-第6部分-2.6.uboot原始碼分析2-啟動第二階段》 第一部分、章節目錄 2.6.1.start_armboot函式簡介 2.6.2.start_armboot解析1 2.6.3.記憶體使用排布 2.6.4.start_armboot解析2 2.6.5.s

Linux核心啟動過程分析(十)-----RTC驅動分析

參考https://blog.csdn.net/xuao20060793/article/details/46433263這篇博文 RTC驅動分析: Class.c (drivers\rtc):subsys_initcall(rtc_init); static int __init

Linux核心啟動流程分析(一)

1. 依據arch/arm/kernel/vmlinux.lds 生成linux核心原始碼根目錄下的vmlinux,這個vmlinux屬於未壓縮,帶除錯資訊、符號表的最初的核心,大小約23MB;  命令:arm-linux-gnu-ld -o vmlinux -T a

omapl138移植uboot系列之修改移植TI官方移植的Linux核心(啟動核心第二篇)

修改Linux核心原始碼     實際上,剛剛我們已經成功的啟動了TI移植過的Linux核心,但是從串列埠控制檯的現象來看,“Starting Kernel”之後什麼資訊都沒有輸出,這就需要我們在TI移植過的核心原始碼之上進行相應修改,以適合我們的639A板卡。

國嵌視訊學習---linux核心啟動流程

一、核心檔案uImage的構成 uImage:Uboot header和zImage zImage:解壓程式碼和壓縮後的vmlinux映象 二、zImage核心的構成 其中解壓程式碼由Head.s和misc.s組成。 三、vmlinux核心構成 1.啟動程式碼部分:

段式、頁式記憶體管理--學習Linux核心原始碼情景分析》第一章

    不得不說《Linux核心原始碼情景分析》這本書被那麼多人當作經典是有原因的,這裡只是該書的筆記遠不及毛老師描述的清楚。     對第一章做一個總結。這一章主要講解段式和頁式記憶體管理,當然還有一些其他東西。 Linux核心版本號的格式

Hadoop源碼學習筆記之NameNode啟動流程分析二:http server啟動源碼剖析

選擇 stop down gen java類 屬性 功能 集群 aslist NameNodeHttpServer啟動源碼剖析,這一部分主要按以下步驟進行:   一、源碼調用分析   二、偽代碼調用流程梳理   三、http server服務流程圖解 第一步,源碼調用分析

學習筆記 --- LINUX網絡卡驅動框架分析

網絡卡的驅動很簡單,就是填充net_device結構體,其應用層到網路協議層核心已經完成了,我們的工作就是填寫這個net_device,然後註冊就可以了。 修正一下:上面第三步應該是:register_netdev 下面程式碼實現一個虛擬網絡卡,這裡沒有實際的網絡卡,只是

Linux核心啟動過程分析

    1、Linux核心啟動協議     閱讀文件\linux-2.6.35\Documentation\x86\boot.txt     傳統支援Image和zImage核心的啟動裝載記憶體佈局(2.4以前的核心裝載就是這樣的佈局):     |