linux 启动 x,(1)linux启动过程
head.S是linux啟動后的第一個文件,主要完成以下功能:
1、檢查處理器信息,并保存;
2、檢查平臺號,并保存;
3、創建頁表,并開啟MMU功能;
4、對內核data section、bbs section作調整和初始化,保存必要的變量,設置棧指針跳到start_kernel;
實現過程:
//定義進程0的頁表基地址,位于內核代碼前16k,注意這是一個虛擬地址。
.globl?swapper_pg_dir
.equ?swapper_pg_dir, TEXTADDR - 0x4000
//這個宏用于計算內核頁表的基地址,是物理地址。
.macro?pgtbl, rd, phys
adr?\rd, stext
sub?\rd, \rd, #0x4000
.endm
//定義所屬為init段
__INIT
//定義個函數地址
.type?stext, %function
ENTRY(stext)
//設置處理器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'
//創建頁表。
bl?__create_page_tables
//把__switch_data地址處的內容放到r13,也就是r13=_mmap_swithced,在__enable_mmu之后會返回到這里執行。要注意這個r13放的可以虛擬地址,在打開MMU之后跳到這。
ldr?r13, __switch_data
//在PROCINFO_INITFUNC被調用之后執行_enable_mmu
adr?lr, __enable_mmu??@ return (PIC) address
//調用處理器相關的初始化函數(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
從下面開始說上面的子調用:
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轉化為物理地址。
add?r5, r5, r3???@ convert virt addresses to
add?r6, r6, r3???@ physical address space
//從協處理器讀出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函數調用的API,如此學一下如何寫被C調用的匯編函數。
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傳過來的機器號和從內核里定義的相比較看是否相等,如果相等會把這個mach_info存放到r5里,mach_info在文件arch/arm/mach-s3c2410/mach-smdk2410.c里,而機器號在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、創建面表
/*
* 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宏計算出面表的物理地址,在內核代碼前16k
pgtbl?r4, r5????@ page table address
/*
* Clear the 16K level 1 swapper page table
*/
//把16K頁表內容清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
//從處理器信息結構讀出MMU參數。
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功能時不出問題,把當前物理地址的1Mb范圍內與虛擬地址做相等映射。
mov?r6, pc, lsr #20???@ start of kernel section
orr?r3, r7, r6, lsl #20??@ flags + kernel base
//[r4, r6, lsl #2]代表頁表的項所在的地址,這個#2是因為每個頁表項占用4個字節,r3代表向相應的頁表項地址所填寫的內容,也就是要映射的虛擬地址。
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.
*/
//把內核的前4MB虛擬地址映射到相應的物理地址。
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.
*/
//把內核開始地址映射到物理ram的開始處。
add?r0, r4, #VIRT_OFFSET >> 18//頁表項的偏移。
orr?r6, r5, r7//物理ram的開如地址。
str?r6, [r0]
mov?pc, lr
3、調用處理器相關的初始化函數
__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,清除并設置,最終目錄如下,使能MMU,禁止內存地址對齊檢查功能,使能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
//設置地址對齊檢查功能。
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
//設置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
//把頁表基址設置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);
//平臺相關初始化,SDRAM、CPU。
setup_arch(&command_line);
//smp
setup_per_cpu_areas();
smp_prepare_boot_cpu();
//進程調度隊列初始化。
sched_init();
preempt_disable();
//設置每個節點的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();
//初始化中斷向量表,調用平臺中斷初始化函數。
init_IRQ();
//進程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
//虛擬文件系統數據結構分配。
vfs_caches_init_early();
//把bootmem不用的內存回收到頁框分配器。
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
//進程創建數據結構初始化。
fork_init(num_physpages);
//多種SLAB分配器的分配。
proc_caches_init();
buffer_init();
unnamed_dev_init();
//空。
key_init();
security_init();
//文件系統相關數據初始化。
vfs_caches_init(num_physpages);
//??
radix_tree_init();
//信號。
signals_init();
/* rootfs populating might need page-writeback */
page_writeback_init();
//PROC文件系統。
#ifdef CONFIG_PROC_FS
proc_root_init();
#endif
cpuset_init();
check_bugs();
acpi_early_init();
//啟動INIT進程作剩余部分初始化。
rest_init();
}
總結
以上是生活随笔為你收集整理的linux 启动 x,(1)linux启动过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: coffeescript html5,H
- 下一篇: c++ 标准库类型string