當(dāng)前位置:首頁 > 芯聞號 > 充電吧
[導(dǎo)讀]我們從u-boot啟動(dòng)內(nèi)核可知道,uboot通過這條命令theKernel (0, bd->bi_arch_number, bd->bi_boot_params);來啟動(dòng)內(nèi)核。那么我們可以

我們從u-boot啟動(dòng)內(nèi)核可知道,uboot通過這條命令theKernel (0, bd->bi_arch_number, bd->bi_boot_params);來啟動(dòng)內(nèi)核。

那么我們可以內(nèi)核啟動(dòng)第一步肯定是處理u-boot傳入的參數(shù)(機(jī)器ID、啟動(dòng)參數(shù)),再通過一系列的步驟達(dá)到最終目的:掛接根文件系統(tǒng)來運(yùn)行應(yīng)用程序

我們來看一下整體流程圖:


1.內(nèi)核引導(dǎo)階段 啟動(dòng)文件head.S和head-common.S 

.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

????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

首先看這段匯編代碼,它主要是用來做一些內(nèi)核啟動(dòng)前的檢測:
?__lookup_processor_type 檢測內(nèi)核是否支持當(dāng)前CPU、__lookup_machine_type檢測是否支持當(dāng)前單板,并且__create_page_tables創(chuàng)建頁表,__enable_mmu使能MMU。
?這里我們首先打開__lookup_machine_type。

.long???__proc_info_begin
????.long???__proc_info_end
3:??.long???.
????.long???__arch_info_begin
????.long???__arch_info_end
/*
?*?Lookup?machine?architecture?in?the?linker-build?list?of?architectures.
?*?Note?that?we?can't?use?the?absolute?addresses?for?the?__arch_info
?*?lists?since?we?aren't?running?with?the?MMU?on?(and?therefore,?we?are
?*?not?in?the?correct?address?space).??We?have?to?calculate?the?offset.
?*
?*??r1?=?machine?architecture?number
?*?Returns:
?*??r3,?r4,?r6?corrupted
?*??r5?=?mach_info?pointer?in?physical?address?space
?*/
????.type???__lookup_machine_type,?%function
__lookup_machine_type:
????adr?r3,?3b??????????????@?r3?=?address?of?3,real?address,phy?address
????ldmia???r3,?{r4,?r5,?r6}@?r4?=?"."?virtual?address?of?3,r5?=?__arch_info_begin,r6?=?????__arch_info_end
????sub?r3,?r3,?r4??????????@?get?offset?between?virt&phys
????add?r5,?r5,?r3??????????@?convert?virt?addresses?to
????add?r6,?r6,?r3??????????@?physical?address?space
????/*
????__arch_info_begin?=?.;
?????*(.arch.info.init)
????__arch_info_end?=?.;
????*/

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

我們在archarmkernel找到__lookup_machine_type被定義在head-common.S文件中。
?開始分析代碼:
?首先,讀出3物理地址給r3。
?然后用ldmia指令將r3對應(yīng)的3條指令的虛擬地址分別存入r4,r5,r6。
?所以現(xiàn)在r4=. ; r5=__arch_info_begin ; r6=__arch_info_end
?然后用r3-r4求出物理地址和虛擬地址的偏移值,再利用這個(gè)偏移值求出r5和r6的實(shí)際物理地址。
?其中__arch_info_begin__arch_info_end定義在內(nèi)核目錄archarmkernel下vmlinux.lds文件中,
?經(jīng)過起始虛擬地址= (0xc0000000) + 0x00008000逐層疊加得到。

SECTIONS
{



?.?=?(0xc0000000)?+?0x00008000;

?.text.head?:?{
??_stext?=?.;
??_sinittext?=?.;
??*(.text.head)
?}

?.init?:?{?/*?Init?code?and?data????????*/
???*(.init.text)
??_einittext?=?.;
??__proc_info_begin?=?.;
???*(.proc.info.init)
??__proc_info_end?=?.;
??__arch_info_begin?=?.;//
???*(.arch.info.init)
??__arch_info_end?=?.;//

這里的__arch_info_begin和__arch_info_end中間存放的是段屬性為.arch.info.init的結(jié)構(gòu)體。
?這里我們可以直接在linux下查詢內(nèi)核中包含.arch.info.init的文件。

/*************Direction:include/asm-arm/arch.h********************/
#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?????????????
};

/*************Direction:arch/arm/mach-s3c2440/mach-smdk2440.c******************/
MACHINE_START(S3C2440,?"SMDK2440")
????/*?Maintainer:?Ben?Dooks*/
????.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

如上:在include/asm-arm/arch.h中找到了定義的結(jié)構(gòu)體類型machine_desc,并且在代碼中它的段屬性被強(qiáng)制定義成了.arch.info.init。
?這樣做的目的是在剛剛我們看到的vmlinux.lds鏈接腳本文件中,可以將具有.arch.info.init段屬性的結(jié)構(gòu)體統(tǒng)一放在__arch_info_begin和__arch_info_end之間。
?非常便于處理。那么現(xiàn)在我們將這個(gè)結(jié)構(gòu)體展開,看看它的內(nèi)容。也就是將arch/arm/mach-s3c2440/mach-smdk2440.c中的參數(shù)傳入。展開后如下:

static?const?struct?machine_desc?__mach_desc_S3C2440????
?__used?????????????????????????
?__attribute__((__section__(".arch.info.init")))?=?{????
????.nr?????=?MACH_TYPE_S3C2440,????????
????.name???????=?"SMDK2440",
????/*?Maintainer:?Ben?Dooks*/
????.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,
};

現(xiàn)在我們看到,定義的結(jié)構(gòu)體類型machine_desc,內(nèi)容為.nr到.timer。
?我們可以看出這個(gè)結(jié)構(gòu)體大概是存儲硬件信息。
?nr存放機(jī)器ID,name存放單板名稱,phys_io存放輸入輸出口,io_pg_offst存放IO的偏移地址,boot_params存放uboot傳給內(nèi)核的啟動(dòng)參數(shù)(TAG),init_irq存放的是中斷初始化信息,map_io為IO的映射表,init_machine存放的是單板的初始化信息,timer存放的是單板的定時(shí)器信息。

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);
};

我們打開arch.h文件,看到對machine_desc結(jié)構(gòu)體的定義確實(shí)和我們剛剛所說的一樣。
?到此處理u-boot傳來的機(jī)器ID結(jié)束。再回到head-common.S文件,這里對mmap_switch定義:

.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

/*
?*?The?following?fragment?of?code?is?executed?with?the?MMU?on?in?MMU?mode,
?*?and?uses?absolute?addresses;?this?is?not?position?independent.
?*
?*??r0??=?cp#15?control?register
?*??r1??=?machine?ID
?*??r9??=?processor?ID
?*/
????.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

mmap_switch做了很多工作,這里我們看到有復(fù)制數(shù)據(jù)段,清BSS段,保存CPU的ID,保存機(jī)器ID,清‘A’位,保存控制寄存器的值,然后就到了C語言段——start_kernel函數(shù)。

2.內(nèi)核啟動(dòng)的第二階段 C語言段—start_kernel

asmlinkage?void?__init?start_kernel(void)
{
????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);
????printk(KERN_NOTICE?"Kernel?command?line:?%sn",?boot_command_line);
????parse_early_param();
????parse_args("Booting?kernel",?static_command_line,?__start___param,
???????????__stop___param?-?__start___param,
???????????&unknown_bootoption);
????init_IRQ();
????profile_init();
????if?(!irqs_disabled())
????????printk("start_kernel():?bug:?interrupts?were?enabled?earlyn");
????early_boot_irqs_on();
????local_irq_enable();
????console_init();

????rest_init();
}

接下來進(jìn)入start_kernel啟動(dòng)內(nèi)核的C函數(shù)。
?上面是start_kernel的部分代碼。
?這部分代碼的主要作用是處理uboot傳遞來的參數(shù),設(shè)置與體系結(jié)構(gòu)相關(guān)的環(huán)境,初始化控制臺,最后執(zhí)行應(yīng)用程序,實(shí)現(xiàn)功能。
?start_kernel函數(shù)框架如下。

內(nèi)核啟動(dòng)流程:
arch/arm/kernel/head.S
start_kernel
????setup_arch(&command_line)???????????//??解析Uboot傳入的啟動(dòng)參數(shù)
????setup_command_line(command_line)????//??解析Uboot傳入的啟動(dòng)參數(shù)
????parse_early_param
????????do_early_param
????????????從__setup_start到__setup_end,調(diào)用early函數(shù)
????unknown_bootoption
????????obsolete_checksetup
????????????從__setup_start到__setup_end,調(diào)用非early函數(shù)
????rest_init
????????kernel_init
????????????prepare_namespace
????????????????mount_root??????//掛接根文件系統(tǒng)
????????????init_post
????????????????//執(zhí)行應(yīng)用程序
這里每一個(gè)退格(TAB)都代表此函數(shù)被上一個(gè)函數(shù)調(diào)用(例如obsolete_checksetup是unknown_bootoption調(diào)用的函數(shù))。 ?setup_arch(&command_line)和setup_command_line(command_line)就是用來處理uboot傳遞進(jìn)來的啟動(dòng)參數(shù)的(處理TAG)。 ?do_early_param從__setup_start到 __setup_end,調(diào)用用early標(biāo)識的函數(shù)(但因?yàn)開_setup_param(str, fn, fn, 0)中early賦值為0,所以不在這里調(diào)用),所以我們主要用obsolete_checksetup,后面會提及。 ?obsolete_checksetup從__setup_start到 __setup_end,調(diào)用用非early標(biāo)識的函數(shù)。 ?mount_root是掛載根文件系統(tǒng),因?yàn)長inux上的應(yīng)用程序最終要在根文件系統(tǒng)上運(yùn)行。最后是init_post中運(yùn)行應(yīng)用程序。
?那么現(xiàn)在就有一個(gè)問題,Linux內(nèi)核是如何接收uboot傳來的根文件系統(tǒng)信息的呢?
bootcmd=nand?read.jffs2?0x30007FC0?kernel;?bootm?0x30007FC0
bootargs=noinitrd?root=/dev/mtdblock3?init=/linuxrc?console=ttySAC0

上面是uboot啟動(dòng)時(shí)打印的環(huán)境變量。其中我們能夠看到根文件系統(tǒng)掛載到第4個(gè)分區(qū):root=/dev/mtdblock3 (從0分區(qū)開始)。
?上面我們提到過,setup_arch(&command_line)和setup_command_line(command_line)就是用來處理uboot傳遞進(jìn)來的啟動(dòng)參數(shù)的(處理TAG)。但這個(gè)處理只是簡單的復(fù)制粘貼而已,這兩個(gè)函數(shù)將TAG保存,但并未進(jìn)行真正的處理。那么真正告訴內(nèi)核在哪里掛載的函數(shù)是什么呢?
?我們通過查看rest_init->kernel_init->prepare_namespace可以看到一個(gè)saved_root_name。查找saved_root_name,發(fā)現(xiàn)在Do_mounts.c文件中有對它的調(diào)用:

static?int?__init?root_dev_setup(char?*line)
{
????strlcpy(saved_root_name,?line,?sizeof(saved_root_name));
????return?1;
}

__setup("root=",?root_dev_setup);//傳入一個(gè)字符串,一個(gè)函數(shù)

根據(jù)我們之前的經(jīng)驗(yàn),我們可以猜測這個(gè)__setup宏,也是定義了一個(gè)結(jié)構(gòu)體。通過查找__setup我們找到了它的宏定義:

Dir:init.h
#define?__setup(str,?fn)????????????????????
????__setup_param(str,?fn,?fn,?0)


#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?}

在init.h文件里,定義__setup等于__setup_param。那么在__setup_param的宏定義里,我們可以知道:
?它先定義了一個(gè)字符串,然后定義了一個(gè)結(jié)構(gòu)體類型obs_kernel_param __setup。
?這個(gè)結(jié)構(gòu)體的段屬性為.init.setup,內(nèi)容為一個(gè)字符串,一個(gè)函數(shù),還有early。
?具備這個(gè)屬性的結(jié)構(gòu)體被鏈接腳本文件放到一起,從__setup_start到 __setup_end搜索調(diào)用。

在vmlinux.lds中

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

但是在Flash里沒有分區(qū),只能和uboot一樣,將分區(qū)在代碼里寫死。一般在啟動(dòng)Linux的時(shí)候,Linux會自動(dòng)打印出分區(qū)的信息。這里我的分區(qū)是這樣的:

Creating?4?MTD?partitions?on?"NAND?256MiB?3,3V?8-bit":
0x00000000-0x00040000?:?"bootloader"
0x00040000-0x00060000?:?"params"
0x00060000-0x00260000?:?"kernel"
0x00260000-0x10000000?:?"root"

我們搜索這個(gè)分區(qū)名grep ""bootloader"" * -nR.在arch/arm/plat-s3c24xx/Common-smdk.c中找到分區(qū)代碼:

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,
????}
};

至此,處理完uboot傳遞的參數(shù),進(jìn)行CPU和單板的校驗(yàn),掛載根文件系統(tǒng)等一系列操作后,最終內(nèi)核執(zhí)行init_post()中的應(yīng)用程序。

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動(dòng)現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動(dòng)力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉