- __cpu_setup。定义kernel\arch\arm64\mm\proc.S中。
#define MAIR(attr, mt) ((attr) << ((mt) * 8))
/*
* __cpu_setup
*
* Initialise the processor for turning the MMU on. Return in x0 the
* value of the SCTLR_EL1 register.
*/
ENTRY(__cpu_setup)
ic iallu // I+BTB cache invalidate // 使instruction和BTB cache中的所有的cache line是无效,直到PoU // BTB(Branch Target Buffer)
tlbi vmalle1is // invalidate I + D TLBs // 使TLB内容无效vm-all-e1-is,vmall表示invalidate all TLB entry,e1表示EL1,is表示inner sharebility
dsb ish // dsb 数据同步屏蔽指令,确保前面的指令执行完成,ish表示inner shareable
mov x0, #3 << 20
msr cpacr_el1, x0 // Enable FP/ASIMD // 使能SIMD and floating-point
msr mdscr_el1, xzr // Reset mdscr_el1 // Monitor Debug System Control Register
/*
* Memory region attributes for LPAE:
*
* n = AttrIndx[2:0] // 最重要的memory attributes指向MAIR_ELx中具体的memory attribute
* n MAIR
* DEVICE_nGnRnE 000 00000000 // G:Gathering 表示对多个memory的访问是否可以合并
* DEVICE_nGnRE 001 00000100 // R: Re-ordering 表示是否允许处理器对内存访问指令进行重排
* DEVICE_GRE 010 00001100 // E: Early Write Acknowledgement
* NORMAL_NC 011 01000100 // 前面加n表示不允许
* NORMAL 100 11111111 // C: 表示是否cacheable
*/
ldr x5, =MAIR(0x00, MT_DEVICE_nGnRnE) | \
MAIR(0x04, MT_DEVICE_nGnRE) | \
MAIR(0x0c, MT_DEVICE_GRE) | \
MAIR(0x44, MT_NORMAL_NC) | \
MAIR(0xff, MT_NORMAL)
msr mair_el1, x5 // MAIR_EL1(Memory Attribute Indirection Register (EL1))
/*
* Prepare SCTLR
*/
adr x5, crval // crval的定义在底下
ldp w5, w6, [x5] // w5 =
0x000802c2 // w6 =
0x0405d03d
mrs x0, sctlr_el1 // System Control Register (EL1) // x0将作为传递给__enable_mmu时的参数
bic x0, x0, x5 // clear bits // 清0,
0x000802c2中为1的位
orr x0, x0, x6 // set bits // 置1,
0x0405d03d中为1的位
/*
* Set/prepare TCR and TTBR. We use 512GB (39-bit) address range for
* both user and kernel.
*/
ldr x10, =TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \
TCR_TG_FLAGS | TCR_ASID16 | TCR_TBI0 // ldr x10, 0xffffffc000d0ca38
/*
* Read the PARange bits from ID_AA64MMFR0_EL1 and set the IPS bits in
* TCR_EL1.
*/
mrs x9, ID_AA64MMFR0_EL1
bfi x10, x9, #32, #3 // 将x9的[34:32]bit写入到
x10的[34:32]bit
msr tcr_el1, x10
ret // return to head.S // 由于lr保存的是
__enable_mmu,因此返回到
__enable_mmu函数继续执行
ENDPROC(__cpu_setup)
/*
* n n T
* U E WT T UD US IHBS
* CE0 XWHW CZ ME TEEA S
* .... .IEE .... NEAI TE.I ..AD DEN0 ACAM
* 0011 0... 1101 ..0. ..0. 10.. .... .... < hardware reserved
* .... .1.. .... 01.1 11.1 ..01 0001 1101 < software settings
*/
.type crval, #object
crval:
.word 0x000802c2 // clear // SCTLR_EL1寄存器中需要清0的bit
.word 0x0405d03d // set // SCTLR_EL1寄存器中需要置1的bit
- __enable_mmu。
/*
* Setup common bits before finally enabling the MMU. Essentially this is just
* loading the page table pointer and vector base registers.
*
* On entry to this code, x0 must contain the SCTLR_EL1 value for turning on
* the MMU.
*/
// 要求x0中保存SCTLR_EL1的值
__enable_mmu:
ldr x5, =vectors //
vectors:
EL1状态的异常向量表 ,
定义在kernel\arch\arm64\kernel\entry.S中
msr vbar_el1, x5 // VBAR_EL1 Vector Base Address Register (EL1),保存EL1状态的异常向量表。
msr ttbr0_el1, x25 // load TTBR0 // idmap_pg_dir 用于用户空间的进程,在进程切换的时候,其地址空间的切换实际就是修改TTBR0的值
msr ttbr1_el1, x26 // load TTBR1 //
swapper_pg_dir
用于kernel space,所有的内核线程都是共享一个空间
isb // 指令同步屏障
b __turn_mmu_on
ENDPROC(__enable_mmu)
.align 4
__turn_mmu_on:
msr sctlr_el1, x0 // 配置
sctlr_el1,系统控制寄存器
isb
// 指令同步屏障
br x27 // 跳转到__mmap_switched执行
ENDPROC(__turn_mmu_on)
- __mmap_switched。
/*
* The following fragment of code is executed with the MMU on in MMU mode, and
* uses absolute addresses; this is not position independent.
*/
__mmap_switched:
adr x3, __switch_data + 8 // 跳过
__mmap_switched
ldp x6, x7, [x3], #16 // x6 =
__bss_start // x7 =
__bss_stop
1: cmp x6, x7
b.hs 2f
str xzr, [x6], #8 // Clear BSS // 循环清除bss段,BSS段用来存放程序中未初始化的全局变量和静态变量
b 1b
2:
ldp x4, x5, [x3], #16 // x4 =
processor_id // x5 =
__fdt_pointer
ldr x6, [x3], #8 // x6 =
memstart_addr
ldr x16, [x3] //
init_thread_union + THREAD_START_SP // sp //
THREAD_START_SP =
THREAD_SIZE - 16
mov sp, x16 // 跳转到c语言前,必须设置好栈
str x22, [x4] // Save processor ID // 将x22保存的
当前cpu id
保存到
processor_id(
在
kernel/arch/arm64
/kernel/setup.c中定义的
)中
str x21, [x5] // Save FDT pointer // 将x21暂存的
device tree的地址保存到
__fdt_pointer(在
kernel/arch/arm64
/kernel/setup.c中定义的)中
str x24, [x6] // Save PHYS_OFFSET // 将
x24保存的kernel起始的地址
保存到
memstart_addr(在kernel/arch/arm64/mm/init.c中定义的)
中
mov x29, #0 // 将x29清0,还不清楚为什么要特别的做这一步
b start_kernel // 长出一口气,终于到C语言了。
ENDPROC(__mmap_switched)
.align 3
.type __switch_data, %object
__switch_data:
.quad __mmap_switched
.quad __bss_start // x6
.quad __bss_stop // x7
.quad processor_id // x4
.quad __fdt_pointer // x5
.quad memstart_addr // x6 // phys_addr_t memstart_addr __read_mostly = 0;
.quad init_thread_union + THREAD_START_SP // sp // init_thread_union
d定义在kernel/init/init_task.c中
#define THREAD_SIZE 16384
#define THREAD_START_SP (THREAD_SIZE - 16)
// 进入start_kernel之前瞥一眼
init_thread_union这个联合体和init_task这个结构体,这里我们只需知道init_task 是0号swapper进程的task_struct,esp指针指向这个init_thread_union以上(16384 - 16)作为0号swapper进程的栈顶
union thread_union init_thread_union __init_task_data =
{ INIT_THREAD_INFO(init_task) };
#define __init_task_data __attribute__((__section__(".data..init_task")))