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

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值