linux内核本身被链接在虚拟地址处,因此kernel希望尽快建立页表并且启动MMU进入虚拟地址工作状态,但是kernel本身工作起来后页表体系是非常复杂的,建立起来也不是那么容易的。因此kernel想了一个好办法,就是:建立页表分两步走。第一步,kernel先建立一个段式页表(和uboot之前建立的页表一样,页表以1MB为单位来区分的),这里的函数就是建立段式页表。段式页表本身比较好建立(段式页表1MB一个映射,4GB的空间需要4096个页表项,每个页表项4字节,因此一共需要16KB内存来做页表),坏处是比较粗不能精细管理内存;第二步,再去建立一个细页表(以4KB为单位),然后启动新的细页表,废除第一步建立的段式映射页表。(Linux内核分段分页进行内存管理)
在内核启动的早期,建立的段式页表,并在内核启动的前期使用;在内核启动的后期,就会再次建立细页表并启用。等内核启动工作起来之后就只有细页表了。
start_kernel函数前执行流程图
第91行:初始化栈基指针fp
第92~94:清空bss段数据
第96行:重新设置寄存器值
R4=processor_id(cpu处理器ID地址,其变量定义在arch/arm/kernel/setup.c中)
R5=__machine_arch_type(machine id地址,其变量定义在arch/arm/kernel/setup.c中)
R6=__atags_pointer(dtb指针的地址,其变量定义在arch/arm/kernel/setup.c中)
R7=cr_alignment(cp15的c1寄存器的值的地址,也就是mmu控制寄存器的值,其变量定义在arch/arm/kernel/entry-armv.S中)
sp=init_thread_union + THREAD_START_SP(init_thread_union可在System.map中获取,笔者的是0x809e6000,这样一来相当于SP的初值是0x809e6000 + 0x2000 - 8)
第97~98行:thumb指令的实现,意义同第86行,同时只有一处生效
第99行:把cpu处理器id(r9)放到processor_id变量中
第100行:把mechine id(r1)存放到__machine_arch_type变量中
第101行:把dtb的地址指针(r2)存放到__atags_pointer变量中(此地址为物理地址,使用需转换)
第102~103行:把cp15的c1的寄存器的值(r0)存放到cr_alignment变量中
第104行:跳转到start_kernel开始启动内核
注:系统一复位就处于SVC模式。CPSR当前程序状态寄存器来切换ARM模式。ARM有9种模式:
User:用户模式,非特权模式,大部分程序运行的时候就处于此模式
FIQ:快速中断模式,进入 FIQ 中断异常
IRQ:一般中断模式,进入 IRQ 中断异常
Supevisor(SVC):超级管理员模式,复位或者一个 Supervisor 指令调用
Monitor(MON):监听模式,用户安全扩展模式
Abort(ABT):数据访问中止模式,用户虚拟存储及存储保护
Hyp(HYP):用于虚拟化扩展
Undef(UND):未定义的指令终止模式
System(SYS):系统模式,用于运行特权级的操作系统任务