__enable_mmu:
/*
* Setup common bits before finally enabling the MMU. Essentially
* this is just loading the page table pointer and domain access
* registers.
*
* r0 = cp#15 control register
* r1 = machine ID
* r2 = atags or dtb pointer
* r4 = page table (see ARCH_PGD_SHIFT in asm/memory.h)
* r9 = processor ID
* r13 = *virtual* address to jump to upon completion
*/
__enable_mmu:
#if defined(CONFIG_ALIGNMENT_TRAP) && __LINUX_ARM_ARCH__ < 6
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
#ifndef CONFIG_ARM_LPAE
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
mcr p15, 0, r4, c2, c0, 0 @ load page table pointer
#endif
b __turn_mmu_on
ENDPROC(__enable_mmu)
- bic r0, r0, #CR_A
- r0(SCTLR)의 alignment abort 비트 클리어.
- mov r5, #(..생략..)
- 총 16개의 도메인 중 4개의 도메인 설정을 위해 값을 준비한다.
- DOMAIN_USER, DOMAIN_KERNEL, DOMAIN_TABLE, DOMAIN_IO
- 도메인 마다 2개의 비트를 사용한다.
- 00=DOMAIN_NOACCESS, 01=DOMAIN_CLIENT, 11=DOMAIN_MANAGER
- 총 16개의 도메인 중 4개의 도메인 설정을 위해 값을 준비한다.
- mcr p15, 0, r5, c3, c0, 0
- ARMv7에서는 도메인 설정이 deprecated되어 무시되지만 하위 호환성을 위해 도메인 설정을 한다.
- mcr p15, 0, r4, c2, c0, 0
- TTBR0
- b __turn_mmu_on
- MMU를 켜기위한 준비가 끝났으므로 __turn_mmu_on 루틴으로 이동하여 MMU를 켠다.
__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
* r1 = machine ID
* r2 = atags or dtb pointer
* r9 = processor ID
* r13 = *virtual* address to jump to upon completion
*
* other registers depend on the function called upon completion
*/
.align 5
.pushsection .idmap.text, "ax"
ENTRY(__turn_mmu_on)
mov r0, r0
instr_sync
mcr p15, 0, r0, c1, c0, 0 @ write control reg
mrc p15, 0, r3, c0, c0, 0 @ read id reg
instr_sync
mov r3, r3
mov r3, r13
ret r3
__turn_mmu_on_end:
ENDPROC(__turn_mmu_on)
.popsection
- .pushsection .idmap.text, “ax”
- .idmap.text 섹션 영역은 1:1 identity mapping 영역이라하여 별도의 섹션에 저장.
- MMU를 켜는 순간에 다음으로 수행되는 명령들이 가상주소를 얻기 위해 TLB를 통해 페이지 테이블에 접근하게 되는데 이 가상주소에 대응하는 매핑이 페이지 테이블에 있어야 한다.
- 그래서 __turn_mmu_on 코드 영역에 대해서 1:1 VA=PA identity mapping을 하였었다.
- mcr p15, 0, r0, c1, c0, 0
- 이 순간에 MMU를 on하였다.
- mrc p15, 0, r3, c0, c0, 0
- r3에 IDR 레지스터를 읽어온다.
- mov r3, r13
- r13 레지스터에는 복귀할 주소인 __mmap_switched의 가상 주소가 들어있다.
instr_sync 매크로
arch/arm/include/asm/assembler.h
/*
* Instruction barrier
*/
.macro instr_sync
#if __LINUX_ARM_ARCH__ >= 7
isb
#elif __LINUX_ARM_ARCH__ == 6
mcr p15, 0, r0, c7, c5, 4
#endif
.endm
- ARMv6까지는 instruction 배리어를 수행하기 위해 시스템 레지스터의 기능을 사용했었지만 ARMv7부터는 정식으로 ARM 명령에 포함되었다.