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启动过程相关推荐

  1. linux 启动脚本 tty,Linux启动过程简介

    许多人对Linux的启动过程感到很神秘,因为所有的启动信息都在屏幕上一闪而过.其实, Linux的启动过程并不象启动信息所显示的那样复杂,它主要分成两个阶段: 1.启动内核.在这个阶段,内核装入内存并 ...

  2. Analyzing the Linux boot process-分析Linux启动过程

    本文翻译自Analyzing the Linux boot process. 箴言:了解运行良好的系统是将来处理不可避免的故障的很好准备 开源软件领域中流行的最为古老笑话:"the code ...

  3. linux内核启动分析 三,Linux内核分析 实验三:跟踪分析Linux内核的启动过程

    贺邦 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 一. 实验过程 ...

  4. Linux入职基础-4.10_系统启动过程(3):Linux内核(vmlinuz)启动

    Linux系统启动过程(3):Linux内核(vmlinuz)启动 上节回顾:详解见上一篇<内核的引导程序>,内核模块在获取控制权后开始工作,内核(vmlinuz-2.6.18-238.e ...

  5. linux启动redis进程,Linux安装Redis实现过程及报错解决方案

    今天安装redis出现些之前安装不曾出现错误,一并在此做个记录 一.安装redis及出现错误 我们这里选择稳定版6.0.1版本 正常安装步骤如下: 这里报了如下错误 make[1]: *** [ser ...

  6. linux启动时间极限优化,Linux启动时间的极限优化

    在上次完成嵌入式应用的Linux裁减后,Linux的启动时间仍需要7s左右,虽然勉强可以接受,但仍然没有达到我个人所追求的目标--2s以内.况且,在实际的商用环境中,设备可靠性的要求可是"5 ...

  7. Linux启动跟windows启动,Windows,Linux启动机制简介

    前言 本文内容只集中在操作系统启动原理的讲解上,不涉及启动的技术细节,因为这些细节都可以通过网络或者相关代码了解.只有了解了启动原理,才能在分析和解决有关启动的问题时具有针对性,不会有无从下手的感觉. ...

  8. 嵌入式linux启动过程分析,嵌入式Linux裸机开发(二)——S5PV210启动过程分析

    嵌入式Linux裸机开发(二)--S5PV210启动过程分析 一.iROM启动方式简介 友善之臂Smart210开发板的SoC为三星S5PV210,S5PV210采用iROM启动方式进行启动,通过查阅 ...

  9. linux 主进程 等待,Linux启动与进程

    操作系统中,前台进程和后台进程有什么区别?特征是什么? 后台程序基本上不和用户交互,优先级别稍微低一点 前台的程序和用户交互,需要较高的响应速度,优先级别稍微高一点 直接从后台手工启动一个进程用得比较 ...

  10. 安装Linux双系统取消快速启动,为什么在双启动时禁用Windows 8上的快速启动?

    问题描述 如果你和Ubuntu一起安装,为什么每个人都一直提到在Windows 8上禁用快速启动?是仅针对UEFI计算机推荐的内容还是对旧版BIOS计算机的建议?是因为它使Windows分区无法从Li ...

最新文章

  1. SQL学习之计算字段的用法与解析
  2. Alluxio及其典型应用场景
  3. imp 只导入索引_使用imp导入表和索引至不同表空间方法
  4. JS滚动条到网页底部自动加载更多内容
  5. JS数组reduce()方法
  6. ubuntu安装phpmyadmin
  7. 从些知道公钥密钥了,呜呜,激动ing~
  8. WPF管理系统自定义分页控件 - WPF特工队内部资料
  9. 拓端tecdat|新能源车主数据图鉴
  10. 有趣 的java代码_[分享]几段有趣的JAVA代码
  11. 苹果iPhone一键解锁破解流程(新机篇)
  12. UNITY单击和双击实现
  13. 智慧工厂数字孪生建设方案
  14. 适合c语言初学者的刷题网站
  15. python结合正则表达式及校验码生成算法校验:电话号码、营业执照、组织机构代码证、税务登记证、统一社会信用代码证、非盈利性企业登记证号码的函数
  16. !function(){}() ” 是什么意思
  17. Git GUI Here 设置成中文界面
  18. update select 语句和merge into语句
  19. Mac 安装 IntelliJ IDEA 以及激活方法
  20. Python/虚数or复数的表示

热门文章

  1. 第十章数据库恢复技术
  2. 操作系统(王道笔记第二章)
  3. 【STM32】修改芯片型号后报 Error 的解决方案
  4. Kubernetes 部署 Ingress 控制器 Traefik v2.1
  5. 【NOIP2015提高组】子串 区间DP+滚动数组优化
  6. java通过POI技术将html转成word
  7. m1笔记本android开发,Apple M1设备开发Android小tips
  8. 不知道工作组名称怎样加入_剩米饭不知道怎样做?试试泡菜炒饭,再也不用担心米饭做多了...
  9. 打印更无缝:微软改善Win11中通用打印体验
  10. 笔记:区分文件头lang=”zh”和lang=”zh-cn”的使用??