1.bootstrap_pagetables页表重写
之前内核启动时设置的临时页表_bootstrap_pagetables, 为了方便使用的是block类型的映射,block映射涵盖的虚拟地址范围非常大,后面对它的映射可能会被ktrr拦截下来,所以需要更改下_bootstrap_pagetables的页表,改为table类型,同时_bootstrap_pagetables需要用到的代码地址映射范围只需要到开启mmu为止就可以。这个地址可以通过_bootstrap_instructions符号来定位。
_arm_vm_init:
ADRL X8, _bootstrap_instructions
AND X21, X8, #0xFFFFFFFFFFFFC000
MOV X0, X21
BL _mmu_kvtop
MOV X20, X0
计算出_bootstrap_instructions的虚拟地址和物理地址。
ADRL X0, _bootstrap_pagetables ; l1 table
BL _mmu_kvtop
CBNZ X0, loc_FFFFFFF007B69070
_bootstrap_pagetables为l1 table地址。
MOV X21, X0 ; l1 table
UBFX X19, X20, #0x24, #3 ; '$' ; l1 index
LDR X8, [X0,X19,LSL#3] ; l1 pte
AND X24, X8, #0xFFFFFFFFF000
MOV X0, X24 ; l2 table paddr
BL _phystokv
MOV X22, X0 ; l2 table vaddr
提取l1 index,pte以及l2 table虚拟地址。
ADRP X9, #_ropage_next@PAGE
LDR X23, [X9,#_ropage_next@PAGEOFF]
CBNZ X23, loc_FFFFFFF007B690AC ; l2 index
ADRL X23, _ropagetable_begin
STR X23, [X9,#_ropage_next@PAGEOFF]
UBFX X26, X20, #0x19, #0xB ; l2 index
ADD X8, X23, #4,LSL#12
STR X8, [X9,#_ropage_next@PAGEOFF] ; _ropage_next += 4096
MOV X0, X23
BL _mmu_kvtop
MOV X25, X0 ; new l3 table
提取l2 index以及从_ropage_next 分配一个物理页作为l3 table。
UBFX X27, X20, #0xE, #0xB ; l3 index
提取l3 index。
MOV X0, X21 ; void *
MOV W1, #0x4000 ; size_t
BL _bzero ; clear l1 talbe
清空l1table所有页表项内容。
ORR X8, X24, #3
STR X8, [X21,X19,LSL#3] ; reset l1 pte with 0x3(table & vaild)
然后重新设置_bootstrap_instructions对应的l1 pte,属性改为table类型。
MOV X0, X22 ; void *
MOV W1, #0x4000 ; size_t
BL _bzero ; clear l2 table
AND X8, X25, #0xFFFFFFFFF000
ORR X8, X8, #3
STR X8, [X22,X26,LSL#3] ; reset l2 talbe pte
清空l2 table所有页表项,然后重新设置_bootstrap_instructions对应的l2 pte,属性改为table类型。
AND X8, X20, #0xFFFFFFFFC000
MOV X9, #0x40000000000683
ORR X8, X8, X9
STR X8, [X23,X27,LSL#3] ; setup l3 pte
重新设置_bootstrap_instructions对应的l3 pte,至此_bootstrap_pagetables已经重新初始化完毕,不在受ktrr影响了。
通常来讲,对ktrr/ctrr的锁定是在devicetree加载和引用结束后才实施的,但也可能在ktrr/ctrr锁定后,也有对devicetree的引用。因此在内核启动阶段对各个内核数据段进行权限设置时,也要把devicetree所在的区域加入到ktrr/ctrr的保护区域。
_arm_vm_prot_init:
ADRP X21, #_segPRELINKTEXTB@PAGE
STR X8, [X24,#_segEXTRADATA@PAGEOFF]
起初_segEXTRADATA保存的是_segPRELINKTEXTB地址,这是kernelcache的最低地址。
BL _SecureDTIsLockedDown
CBZ W0, loc_FFFFFFF007B68054
ADRL X8, _PE_state.deviceTreeHead
LDR X9, [X8]
STR X9, [X24,#_segEXTRADATA@PAGEOFF]
LDR X8, [X8,#(qword_FFFFFFF00772C240 - 0xFFFFFFF00772C230)]
STR X8, [X25,#_segSizeEXTRADATA@PAGEOFF]
调用_SecureDTIsLockedDown,如果返回1,代表devicetree的加载地址不包含在_segPRELINKTEXTB内,因此要把devicetree的加载地址重新写入segEXTRADATA,然后在调用_arm_vm_page_granular_prot做属性调整。
_SecureDTIsLockedDown:
ADRL X20, __mh_execute_header
ADRP X8, #_DTRootNode@PAGE
LDR X20, [X8,#_DTRootNode@PAGEOFF]
CMP X8, #0
判断_DTRootNode地址是否小于__mh_execute_header。
mdscr寄存器是arm调试机制的控制寄存器,开启调试机制可能会绕过ktrr保护,xnu的一个缓解措施是在设置完mdscr后,判断kde位是否开启,如果开启就panic。
__TEXT_EXEC:__text:FFFFFFF008139478 _update_mdscr:
MOV X4, #0
MRS X2, #0, c0, c2, #2
BIC X2, X2, X0
X0保存的是要清楚的bit位。
ORR X2, X2, X1
AND X2, X2, #0xFFFFFFFFFFFFDFFF
MSR #0, c0, c2, #2, X2
X1保存的是要设置的bit位。
ANDS X3, X2, #0x2000
ORR X4, X4, X3
B.NE loc_FFFFFFF008139488
CMP X4, XZR
B.NE loc_FFFFFFF0081394A8
RET
__TEXT_EXEC:__text:FFFFFFF0081394A8
ADRL X0, aMdscrKdeWasSet ; "MDSCR.KDE was set"
B _panic
判断kde位是否开启,如果开启就panic。
_kernel_bootstrap->_machine_init->_rorgn_stash_range
_rorgn_stash_range:
ADRP X8, #_segLOWESTRO@PAGE
LDR X19, [X8,#_segLOWESTRO@PAGEOFF]
MOV X0, X19
BL _mmu_kvtop
CBNZ X0, loc_FFFFFFF007B624E8
loc_FFFFFFF007B624E8 ; CODE XREF: _rorgn_stash_range+1D8↑j
ADRP X8, #_ctrr_begin@PAGE
STR X0, [X8,#_ctrr_begin@PAGEOFF]
将_segLOWESTRO地址写入_ctrr_begin
ADRP X8, #_segHIGHESTRO@PAGE
LDR X19, [X8,#_segHIGHESTRO@PAGEOFF]
CBZ X19, loc_FFFFFFF007B6252C
SUB X8, X0, #1
ADRP X9, #_ctrr_end@PAGE
STR X8, [X9,#_ctrr_end@PAGEOFF]
如果kernelcache binary存在_segHIGHESTRO,则将_segHIGHESTRO - 1存入_ctrr_end。
loc_FFFFFFF007B6252C ; CODE XREF: _rorgn_stash_range+20C↑j
ADRP X8, #_segLASTB@PAGE
LDR X19, [X8,#_segLASTB@PAGEOFF]
MOV X0, X19
ADRP X8, #_segSizeLAST@PAGE
LDR X8, [X8,#_segSizeLAST@PAGEOFF]
ADD X0, X8, X0
SUB X8, X0, #1
ADRP X9, #_ctrr_end@PAGE
STR X8, [X9,#_ctrr_end@PAGEOFF]
如果kernelcache binary不存在_segHIGHESTRO,则将_segLASTB+segSizeLAST-1写入_ctrr_end。
_rorgn_stash_range函数只是计算出了_ctrr_begin和_ctrr_end,真正对内核锁定是在_rorgn_lockdown函数。
_kernel_bootstrap_thread->_machine_lockdown->_rorgn_lockdown
_rorgn_lockdown:
ADRP X9, #_ctrr_begin@PAGE
LDR X9, [X9,#_ctrr_begin@PAGEOFF]
ADRP X10, #_ctrr_end@PAGE
LDR X10, [X10,#_ctrr_end@PAGEOFF]
MSR #4, c15, c2, #3, X9 ; S3_4_C15_C2_3
MSR #4, c15, c2, #4, X10 ; S3_4_C15_C2_4
MOV W11, #0x12
MSR #4, c15, c2, #5, X11 ; S3_4_C15_C2_5
MOV W11, #1
MSR #4, c15, c2, #2, X11 ; S3_4_C15_C2_2
将_ctrr_begin写入S3_4_C15_C2_3寄存器,它保存的是ctrr保护的起始地址,将_ctrr_end写入S3_4_C15_C2_4,它保存的是ctrr保护的结束地址。
将0x12写入S3_4_C15_C2_5寄存器,它保存的是ctrr的控制状态。Xnu source code里已经有对它的描述:
Apple_arm64_regs.h
#define CTRR_CTL_EL1_A_MMUOFF_WRPROTECT (1 << 0)
#define CTRR_CTL_EL1_A_MMUON_WRPROTECT (1 << 1)
#define CTRR_CTL_EL1_B_MMUOFF_WRPROTECT (1 << 2)
#define CTRR_CTL_EL1_B_MMUON_WRPROTECT (1 << 3)
#define CTRR_CTL_EL1_A_PXN (1 << 4)
#define CTRR_CTL_EL1_B_PXN (1 << 5)
#define CTRR_CTL_EL1_A_UXN (1 << 6)
#define CTRR_CTL_EL1_B_UXN (1 << 7)
将1写入S3_4_C15_C2_2寄存器,锁定ctrr。
MRS X11, #0, c4, c2, #2 ; CurrentEL
CMP X11, #8 ; el2
B.NE loc_FFFFFFF007B62DEC
MSR #4, c15, c11, #0, X9 ; S3_4_C15_C11_0
MSR #4, c15, c11, #1, X10 ; S3_4_C15_C11_1
MOV W9, #0x12
MSR #4, c15, c11, #4, X9 ; S3_4_C15_C11_4
MOV W9, #1
MSR #4, c15, c11, #5, X9 ; S3_4_C15_C11_5
可以看到ctrr除了能对el1内核代码做保护,还可以对el2 hypervisor做保护。
S3_4_C15_C11_0代表要保存的el2代码起始地址,S3_4_C15_C11_1代表结束地址,S3_4_C15_C11_4代表控制寄存器,S3_4_C15_C11_5代表锁定寄存器。
Ktrr/ctrr保护的区域范围如下:
* __PRELINK_TEXT <--- First KTRR (ReadOnly) segment
* __PLK_DATA_CONST
* __PLK_TEXT_EXEC
* __TEXT
* __DATA_CONST
* __TEXT_EXEC
* __KLD
* __LAST <--- Last KTRR (ReadOnly) segment
* __DATA
* __BOOTDATA (if present)
* __LINKEDIT
* __PRELINK_DATA (expected populated now)
* __PLK_LINKEDIT
* __PRELINK_INFO
系统重启后的第一件事就是锁定ktrr/ctrr寄存器。
_reset_vector:
ADRL X17, _ctrr_begin
LDR X17, [X17]
CBZ X17, loc_FFFFFFF00813445C
ADRL X19, _ctrr_end
LDR X19, [X19]
CBZ X19, loc_FFFFFFF00813446C
MRS X18, #4, c15, c2, #2
CBNZ X18, loc_FFFFFFF0081344A8
MSR #4, c15, c2, #3, X17 ; S3_4_C15_C2_3
MSR #4, c15, c2, #4, X19 ; S3_4_C15_C2_4
MOV X18, #0x12
MSR #4, c15, c2, #5, X18 ; S3_4_C15_C2_5
MOV X18, #1
MSR #4, c15, c2, #2, X18 ; S3_4_C15_C2_2