__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 명령에 포함되었다.