kernel/head.S – __enable_mmu:

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

 

댓글 남기기