日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux 启动 x,(1)linux启动过程

發(fā)布時間:2025/3/12 linux 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux 启动 x,(1)linux启动过程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

head.S是linux啟動后的第一個文件,主要完成以下功能:

1、檢查處理器信息,并保存;

2、檢查平臺號,并保存;

3、創(chuàng)建頁表,并開啟MMU功能;

4、對內(nèi)核data section、bbs section作調(diào)整和初始化,保存必要的變量,設(shè)置棧指針跳到start_kernel;

實(shí)現(xiàn)過程:

//定義進(jìn)程0的頁表基地址,位于內(nèi)核代碼前16k,注意這是一個虛擬地址。

.globl?swapper_pg_dir

.equ?swapper_pg_dir, TEXTADDR - 0x4000

//這個宏用于計算內(nèi)核頁表的基地址,是物理地址。

.macro?pgtbl, rd, phys

adr?\rd, stext

sub?\rd, \rd, #0x4000

.endm

//定義所屬為init段

__INIT

//定義個函數(shù)地址

.type?stext, %function

ENTRY(stext)

//設(shè)置處理器SVC模式,禁止IRQ中斷、FIQ中斷。

msr?cpsr_c, #PSR_F_BIT | PSR_I_BIT | MODE_SVC

//查找處理器類型并判斷是否有效。

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'

//創(chuàng)建頁表。

bl?__create_page_tables

//把__switch_data地址處的內(nèi)容放到r13,也就是r13=_mmap_swithced,在__enable_mmu之后會返回到這里執(zhí)行。要注意這個r13放的可以虛擬地址,在打開MMU之后跳到這。

ldr?r13, __switch_data

//在PROCINFO_INITFUNC被調(diào)用之后執(zhí)行_enable_mmu

adr?lr, __enable_mmu??@ return (PIC) address

//調(diào)用處理器相關(guān)的初始化函數(shù)(arch/arm/mm/proc-arm920.S -arm920_setup)。

add?pc, r10, #PROCINFO_INITFUNC

//

.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, 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:

//r4=_data_loc,r5=_data_start,r6=_bss_start,r7=_end

adr?r3, __switch_data + 4

ldmia?r3!, {r4, r5, r6, r7}

//_data_loc==_data_start,不用移動。

cmp?r4, r5????@ Copy data segment if needed

1:?cmpne?r5, r6

ldrne?fp, [r4], #4

strne?fp, [r5], #4

bne?1b

//清除BSS段。

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

從下面開始說上面的子調(diào)用:

1、CPU信息和平臺信息的檢查

/*

* Read processor ID register (CP#15, CR0), and look up in the linker-built

* supported processor list.? Note that we can't use the absolute addresses

* for the __proc_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.

*

* Returns:

*?r3, r4, r6 corrupted

*?r5 = proc_info pointer in physical address space

*?r9 = cpuid

*/

.type?__lookup_processor_type, %function

__lookup_processor_type:

//把下面紅色的3位置處的地址放到r3,是物理地址。

adr?r3, 3f

//把紅3放到r9,r5=__proc_info_begin,r6=__proc_info_endldmda?r3, {r5, r6, r9}

//計算物理地址與虛擬地址的偏移放到r3.

sub?r3, r3, r9???@ get offset between virt&phys

//把r5 r6轉(zhuǎn)化為物理地址。

add?r5, r5, r3???@ convert virt addresses to

add?r6, r6, r3???@ physical address space

//從協(xié)處理器讀出cpu的ID放到r9.

mrc?p15, 0, r9, c0, c0??@ get processor id

//把proc_info_list里的cpu_val和cpu_mask讀到r3和r4.處理器信息存放在(arch/arm/mm/arm/proc-arm920.S cpu_val=0x41009200,cpu_mask=0xff00fff0).判斷是否cpu_val==cpu_mask&r9)如果失敗r5=0。

1:?ldmia?r5, {r3, r4}???@ value, mask

and?r4, r4, r9???@ mask wanted bits

teq?r3, r4

beq?2f

add?r5, r5, #PROC_INFO_SZ??@ sizeof(proc_info_list)

cmp?r5, r6

blo?1b

mov?r5, #0????@ unknown processor

2:?mov?pc, lr

/*

* This provides a C-API version of the above function.

*/

//這個是C函數(shù)調(diào)用的API,如此學(xué)一下如何寫被C調(diào)用的匯編函數(shù)。

ENTRY(lookup_processor_type)

stmfd?sp!, {r4 - r6, r9, lr}

bl?__lookup_processor_type

mov?r0, r5

ldmfd?sp!, {r4 - r6, r9, pc}

/*

* Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for

* more information about the __proc_info and __arch_info structures.

*/

.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

*/

//這個和上面那個__lookup_processor_type語法格式是完全一樣的,把loader傳過來的機(jī)器號和從內(nèi)核里定義的相比較看是否相等,如果相等會把這個mach_info存放到r5里,mach_info在文件arch/arm/mach-s3c2410/mach-smdk2410.c里,而機(jī)器號在include/asm/mach-type.h里。

.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

/*

* This provides a C-API version of the above function.

*/

ENTRY(lookup_machine_type)

stmfd?sp!, {r4 - r6, lr}

mov?r1, r0

bl?__lookup_machine_type

mov?r0, r5

ldmfd?sp!, {r4 - r6, pc}

2、創(chuàng)建面表

/*

* Setup the initial page tables.? We only setup the barest

* amount which are required to get the kernel running, which

* generally means mapping in the kernel code.

*

* r8? = machinfo

* r9? = cpuid

* r10 = procinfo

*

* Returns:

*? r0, r3, r5, r6, r7 corrupted

*? r4 = physical page table address

*/

.type?__create_page_tables, %function

__create_page_tables:

//把SDRAM的物理地址放到r5.

ldr?r5, [r8, #MACHINFO_PHYSRAM]?@ physram

//用pgtbl宏計算出面表的物理地址,在內(nèi)核代碼前16k

pgtbl?r4, r5????@ page table address

/*

* Clear the 16K level 1 swapper page table

*/

//把16K頁表內(nèi)容清0.

mov?r0, r4

mov?r3, #0

add?r6, r0, #0x4000

1:?str?r3, [r0], #4

str?r3, [r0], #4

str?r3, [r0], #4

str?r3, [r0], #4

teq?r0, r6

bne?1b

//從處理器信息結(jié)構(gòu)讀出MMU參數(shù)。

ldr?r7, [r10, #PROCINFO_MMUFLAGS]?@ mmuflags

/*

* Create identity mapping for first MB of kernel to

* cater for the MMU enable.? This identity mapping

* will be removed by paging_init().? We use our current program

* counter to determine corresponding section base address.

*/

//為了打開MMU功能時不出問題,把當(dāng)前物理地址的1Mb范圍內(nèi)與虛擬地址做相等映射。

mov?r6, pc, lsr #20???@ start of kernel section

orr?r3, r7, r6, lsl #20??@ flags + kernel base

//[r4, r6, lsl #2]代表頁表的項所在的地址,這個#2是因?yàn)槊總€頁表項占用4個字節(jié),r3代表向相應(yīng)的頁表項地址所填寫的內(nèi)容,也就是要映射的虛擬地址。

str?r3, [r4, r6, lsl #2]??@ identity mapping

/*

* Now setup the pagetables for our kernel direct

* mapped region.? We round TEXTADDR down to the

* nearest megabyte boundary.? It is assumed that

* the kernel fits within 4 contigous 1MB sections.

*/

//把內(nèi)核的前4MB虛擬地址映射到相應(yīng)的物理地址。

add?r0, r4,? #(TEXTADDR & 0xff000000) >> 18

str?r3, [r0, #(TEXTADDR & 0x00f00000) >> 18]!

add?r3, r3, #1 << 20

str?r3, [r0, #4]!???@ KERNEL + 1MB

add?r3, r3, #1 << 20

str?r3, [r0, #4]!???@ KERNEL + 2MB

add?r3, r3, #1 << 20

str?r3, [r0, #4]???@ KERNEL + 3MB

/*

* Then map first 1MB of ram in case it contains our boot params.

*/

//把內(nèi)核開始地址映射到物理ram的開始處。

add?r0, r4, #VIRT_OFFSET >> 18//頁表項的偏移。

orr?r6, r5, r7//物理ram的開如地址。

str?r6, [r0]

mov?pc, lr

3、調(diào)用處理器相關(guān)的初始化函數(shù)

__arm920_setup:

mov?r0, #0

mcr?p15, 0, r0, c7, c7??@ invalidate I,D caches on v4

mcr?p15, 0, r0, c7, c10, 4??@ drain write buffer on v4

mcr?p15, 0, r0, c8, c7??@ invalidate I,D TLBs on v4

//讀出cp15的c1寄存器到r0,清除并設(shè)置,最終目錄如下,使能MMU,禁止內(nèi)存地址對齊檢查功能,使能cache,禁止寫入緩存,控制中斷向量表的地址為高端。

mrc?p15, 0, r0, c1, c0??@ get control register v4

ldr?r5, arm920_cr1_clear

bic?r0, r0, r5

ldr?r5, arm920_cr1_set

orr?r0, r0, r5

mov?pc, lr

//這里的arm920_cr1_clear=0x3f3f, arm920_cr1_set=0x3135

4、打開MMU

/*

* Setup common bits before finally enabling the MMU.? Essentially

* this is just loading the page table pointer and domain access

* registers.

*/

.type?__enable_mmu, %function

__enable_mmu:

#ifdef CONFIG_ALIGNMENT_TRAP

//設(shè)置地址對齊檢查功能。

orr?r0, r0, CR_A

#else

bic?r0, r0, #CR_A

#endif

#ifdef CONFIG_CPU_DCACHE_DISABLE

bic?r0, r0, #CR_C

#endif

#ifdef CONFIG_CPU_BPREDICT_DISABLE

bic?r0, r0, #CR_Z

#endif

#ifdef CONFIG_CPU_ICACHE_DISABLE

bic?r0, r0, #CR_I

#endif

//設(shè)置MMU中的域

mov?r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \

domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \

domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \

domain_val(DOMAIN_IO, DOMAIN_CLIENT))

mcr?p15, 0, r5, c3, c0, 0??@ load domain access register

//把頁表基址設(shè)置MMU到MMU的C2。

mcr?p15, 0, r4, c2, c0, 0??@ load page table pointer

b?__turn_mmu_on

/*

* Enable the MMU.? This completely changes the structure of the visible

* memory space.? You will not be able to trace execution through this.

* If you have an enquiry about this, *please* check the linux-arm-kernel

* mailing list archives BEFORE sending another post to the list.

*

*? r0? = cp#15 control register

*? r13 = *virtual* address to jump to upon completion

*

* other registers depend on the function called upon completion

*/

.align?5

.type?__turn_mmu_on, %function

__turn_mmu_on:

mov?r0, r0

//打開MMU

mcr?p15, 0, r0, c1, c0, 0??@ write control reg

mrc?p15, 0, r3, c0, c0, 0??@ read id reg

mov?r3, r3

mov?r3, r3

mov?pc, r13

asmlinkage void __init start_kernel(void)

{

char * command_line;

extern struct kernel_param __start___param[], __stop___param[];

/*

* Interrupts are still disabled. Do necessary setups, then

* enable them

*/

lock_kernel();

page_address_init();

printk(KERN_NOTICE);

printk(linux_banner);

//平臺相關(guān)初始化,SDRAM、CPU。

setup_arch(&command_line);

//smp

setup_per_cpu_areas();

smp_prepare_boot_cpu();

//進(jìn)程調(diào)度隊列初始化。

sched_init();

preempt_disable();

//設(shè)置每個節(jié)點(diǎn)的zonelist。

build_all_zonelists();

//smp

page_alloc_init();

printk(KERN_NOTICE "Kernel command line: %s\n", saved_command_line);

parse_early_param();

parse_args("Booting kernel", command_line, __start___param,

__stop___param - __start___param,

&unknown_bootoption);

sort_main_extable();

//copy 中斷向量表。

trap_init();

rcu_init();

//初始化中斷向量表,調(diào)用平臺中斷初始化函數(shù)。

init_IRQ();

//進(jìn)程PID哈希表。

pidhash_init();

//定時器軟中斷。

init_timers();

//軟中斷tasklet.

softirq_init();

//初始化時鐘中斷。

time_init();

//控制臺初始化,開始打印。

console_init();

if (panic_later)

panic(panic_later, panic_param);

profile_init();

//打開CPU的中斷。

local_irq_enable();

#ifdef CONFIG_BLK_DEV_INITRD

if (initrd_start && !initrd_below_start_ok &&

initrd_start < min_low_pfn << PAGE_SHIFT) {

printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "

"disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT);

initrd_start = 0;

}

#endif

//虛擬文件系統(tǒng)數(shù)據(jù)結(jié)構(gòu)分配。

vfs_caches_init_early();

//把bootmem不用的內(nèi)存回收到頁框分配器。

mem_init();

//slab分配器初化。

kmem_cache_init();

//NUMA

setup_per_cpu_pageset();

numa_policy_init();

if (late_time_init)

late_time_init();

calibrate_delay();

//PID位圖初始化。

pidmap_init();

//空

pgtable_cache_init();

prio_tree_init();

anon_vma_init();

#ifdef CONFIG_X86

if (efi_enabled)

efi_enter_virtual_mode();

#endif

//進(jìn)程創(chuàng)建數(shù)據(jù)結(jié)構(gòu)初始化。

fork_init(num_physpages);

//多種SLAB分配器的分配。

proc_caches_init();

buffer_init();

unnamed_dev_init();

//空。

key_init();

security_init();

//文件系統(tǒng)相關(guān)數(shù)據(jù)初始化。

vfs_caches_init(num_physpages);

//??

radix_tree_init();

//信號。

signals_init();

/* rootfs populating might need page-writeback */

page_writeback_init();

//PROC文件系統(tǒng)。

#ifdef CONFIG_PROC_FS

proc_root_init();

#endif

cpuset_init();

check_bugs();

acpi_early_init();

//啟動INIT進(jìn)程作剩余部分初始化。

rest_init();

}

總結(jié)

以上是生活随笔為你收集整理的linux 启动 x,(1)linux启动过程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。