개발/arm64 linux 6 분석

proc.S __cpu_setup

clucle 2025. 5. 10. 15:39

| mov_q | move an immediate constant into a 64-bit register |

arch/arm64/mm/proc.S

isb : 인스트럭션 fetch 됐던걸, 이전 명령 실행 전까지 미룸

msr SCTLR_EL1, x0   ; 시스템 제어 레지스터 변경
// 파이프라인에는 다음 명령들이 이미 들어왔음!
add x1, x1, #1 // 엔디안값이 SCTLR 에서 바뀌었다면?? (정확한 예시인지는 잘 모르겠음) 확인해보고 싶다
->
msr SCTLR_EL1, x0
isb

dsb nsh

  • dsb: data synchronization barrier: 모든 이전 명령이 끝날때까지 대기
  • nsh: non-shareable: 현재 코어내에서만 동기화

tlbi vmalle1

  • 가상 주소를 물리 주소로 바꾸기 위해 캐시 테이블을 무효화
  • vm: virtual machine id 기반

capacr_el1

  • CPACR_EL1, Architectural Feature Access Control Register

  • SIMD: Single Instruction, Multiple Data: 단일 명령어로 여러 데이터를 동시에 처리
  • FPEN: Advanced SIMD 와 Floating Point 레지스터에 대한 접근 제어
  • SVE: Scalable Vector Extension. 벡터 연산 확장
  • SVM: Scalable Matrix Extension: 행렬 연산 확장
  • SMEN: SME, SVE 에서 특정 조건에 trap 할지

mdscr_el1

  • MDSCR_EL1, Monitor Debug System Control Register
  • Debug Communication Channel (DCC)
  • TDCC[12] 필드를 1으로 설정시, el0 에서 접근시 trap 됨

reset_pmuserenr_el0, reset_amuserenr_el0

  • linux/arch/arm64/include/asm/assembler.h
  • PMU: Performance Monitoring Unit
  • AMU: Activity Monitoring Unit

alias

  • arm arch 에서는 레지스터를 별칭을 사용해서 접근 할 수 있음
foo .req x17
mov foo, #0xff
.unreq foo

MAIR: Memory Attribute Indirection Register

page table 을 찾을때 슬롯의 번호
#define MT_NORMAL        0
#define MT_NORMAL_TAGGED    1
#define MT_NORMAL_NC        2
#define MT_DEVICE_nGnRnE    3
#define MT_DEVICE_nGnRE        4

각 슬롯의 메모리 속성을 미리 매크로로 지정.
Attr[7:0] = [OuterAttr(3:0) << 4] | [InnerAttr(3:0)]

/* MAIR_ELx memory attributes (used by Linux) */
#define MAIR_ATTR_DEVICE_nGnRnE        UL(0x00) // 완전 비순차 (안전) device memory (strongly ordered)
#define MAIR_ATTR_DEVICE_nGnRE        UL(0x04) // Early Write ack 허용
#define MAIR_ATTR_NORMAL_NC        UL(0x44) Inner/Outer: Non-cacheable
#define MAIR_ATTR_NORMAL_TAGGED        UL(0xf0)     Outer: 1111 (WB RW-alloc), Inner: 0000 (Strongly ordered / uncached).
#define MAIR_ATTR_NORMAL        UL(0xff)
#define MAIR_ATTR_MASK            UL(0xff)

/* Position the attr at the correct index */
#define MAIR_ATTRIDX(attr, idx)        ((attr) << ((idx) * 8))

TCR_EL1, Translation Control Register (EL1)

  • MMU 설정을 제어하는 레지스터
  • 페이지 크기를 선택하거나, 변환 범위 지정, 캐시 정책등을 설정함

ID_AA64MMFR0_EL1, AArch64 Memory Model Feature Register 0

- PARange[3:0] : Physical Address range supported.

  • 0b0101 48 bits, 256TB.

ID_AA64MMFR1_EL1: AArch64 Memory Model Feature Register 1, EL1

- HAFDBS: Indicates the support for hardware updates to Access flag and dirty state in translation tables.

  • Access Flag Dirty Bit Management
  • 소프트웨어적으로 페이지 폴트같은걸 확인했었는데, 하드웨어가 서포트 해서 성능 향상을 시킨 기능?

SYS_ID_AA64MMFR3_EL1:

  • S1PIE: Stage 1 Permission Indirection. Indicates support for Permission Indirection at stage 1.

  • Permission Indirection : he PIIndex field indexes into the Permission Indirection Register (PIR) for the appropriate exception level, so that the OS (EL1), Hypervisor (EL2), or firmware (EL3) can set the stage 1 translation base permissions:

  • 권한을 직접 PTE(페이지 테이블 엔트리)에 저장하지 않고, 간접 테이블(PIR: Permission Indirection Register)을 통해 지정하는 메커니즘

  • PIE: Permission Indirection Extension


/*
 *    __cpu_setup
 *
 *    Initialise the processor for turning the MMU on.
 *  mmu 를 켜기 위해서 프로세서(cpu) 를 초기화 한다
 *
 * Output:
 *    Return in x0 the value of the SCTLR_EL1 register.
 *  el1 에 해당하는 시스템 컨트롤 레지스터 값을 반환한다
 */
    .pushsection ".idmap.text", "a"
SYM_FUNC_START(__cpu_setup)
    tlbi    vmalle1                // Invalidate local TLB,
    dsb    nsh // 이 코에어서만 tlb invalidation 을 기다림

    msr    cpacr_el1, xzr            // Reset cpacr_el1, 특정 명령시 trap 하게 만드는 레지스터. 0으로 넣으면 trap 이 발생함.
    mov    x1, #1 << 12            // Reset mdscr_el1 and disable, tdcc: 0b0    This control does not cause any instructions to be trapped.
    msr    mdscr_el1, x1            // access to the DCC from EL0, monitoring 사용하더라도 trap 되지 않도록 설정
    reset_pmuserenr_el0 x1            // Disable PMU access from EL0
    reset_amuserenr_el0 x1            // Disable AMU access from EL0

    /*
     * Default values for VMSA control registers. These will be adjusted
     * below depending on detected CPU features.
     */
    // .req 로 alias 사용
    // using mair = x17
    // using tcr = x16
    mair    .req    x17
    tcr    .req    x16
    mov_q    mair, MAIR_EL1_SET // mair = MAIR_EL1_SET (상수)
    // 주소 변환을 위한 레지스터 설정
    mov_q    tcr, TCR_T0SZ(IDMAP_VA_BITS) | TCR_T1SZ(VA_BITS_MIN) | TCR_CACHE_FLAGS | \
             TCR_SHARED | TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
             TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS | TCR_MTE_FLAGS

    // 특정 cpu 의 결함을 회피하기 위해 tcr 비트 수정
    tcr_clear_errata_bits tcr, x9, x5

#ifdef CONFIG_ARM64_VA_BITS_52
    mov        x9, #64 - VA_BITS // x9 를 저장할 필요가 있을까?
alternative_if ARM64_HAS_VA52 // VA52 비트를 실제로 지원하는 경우에만 실행
    tcr_set_t1sz    tcr, x9
#ifdef CONFIG_ARM64_LPA2 // Large Physical Address Extension 2 를 지원한다면
    orr        tcr, tcr, #TCR_DS
#endif
alternative_else_nop_endif
#endif

    /*
     * Set the IPS bits in TCR_EL1.
     * IPS: tcr[34:32], Itermediate Physical Address Size
     */
    tcr_compute_pa_size tcr, #TCR_IPS_SHIFT, x5, x6
#ifdef CONFIG_ARM64_HW_AFDBM
    /*
     * Enable hardware update of the Access Flags bit.
     * Hardware dirty bit management is enabled later,
     * via capabilities.
     */
    mrs    x9, ID_AA64MMFR1_EL1
    and    x9, x9, ID_AA64MMFR1_EL1_HAFDBS_MASK // HAFDBS 마스크와 and
    cbz    x9, 1f // 0이라면 1로 점프. 값이 있었다면, tcr 에 Hardware AccessFlag 를 켜줌

    orr    tcr, tcr, #TCR_HA        // hardware Access flag update
1:
#endif    /* CONFIG_ARM64_HW_AFDBM */
    msr    mair_el1, mair
    msr    tcr_el1, tcr

    mrs_s    x1, SYS_ID_AA64MMFR3_EL1
    ubfx    x1, x1, #ID_AA64MMFR3_EL1_S1PIE_SHIFT, #4 // 권한을 PTE 에서 사용하지 않고, 간접 테이블을 사용
    cbz    x1, .Lskip_indirection // 지원하지 않기때문에, 레지스터 설정을 하지 않음

    /*
     * The PROT_* macros describing the various memory types may resolve to
     * C expressions if they include the PTE_MAYBE_* macros, and so they
     * can only be used from C code. The PIE_E* constants below are also
     * defined in terms of those macros, but will mask out those
     * PTE_MAYBE_* constants, whether they are set or not. So #define them
     * as 0x0 here so we can evaluate the PIE_E* constants in asm context.
     */

#define PTE_MAYBE_NG        0 // pte 에 ng 와 shared 를 잠시 끈체로 설정
#define PTE_MAYBE_SHARED    0

    mov_q    x0, PIE_E0 // 유저 페이징에 대한 권한 매핑
    msr    REG_PIRE0_EL1, x0
    mov_q    x0, PIE_E1 // 커널 페이지에 대한 권한 매핑
    msr    REG_PIR_EL1, x0

#undef PTE_MAYBE_NG
#undef PTE_MAYBE_SHARED

    mov    x0, TCR2_EL1x_PIE // PIE 활성화
    msr    REG_TCR2_EL1, x0

.Lskip_indirection:

    /*
     * Prepare SCTLR
     */
    mov_q    x0, INIT_SCTLR_EL1_MMU_ON // 이제 mmu 를 활성화 (SCTLR_ELx_M)
    ret                    // return to head.S

    .unreq    mair
    .unreq    tcr
SYM_FUNC_END(__cpu_setup)
/*
 * tcr_compute_pa_size - set TCR.(I)PS to the highest supported
 * ID_AA64MMFR0_EL1.PARange value
 * 
 *  cpu 가 지원하는 것과, 커널이 지원하는 것중 작은것으로 설정
 *
 *    tcr:        register with the TCR_ELx value to be updated
 *    pos:        IPS or PS bitfield position
 *    tmp{0,1}:    temporary registers
 */
    .macro    tcr_compute_pa_size, tcr, pos, tmp0, tmp1
    mrs    \tmp0, ID_AA64MMFR0_EL1
    // Narrow PARange to fit the PS field in TCR_ELx
    ubfx    \tmp0, \tmp0, #ID_AA64MMFR0_EL1_PARANGE_SHIFT, #3 // PARange[3:0] 가져오기. Physical Address range supported.

    mov    \tmp1, #ID_AA64MMFR0_EL1_PARANGE_MAX // PA_BITS 최대 수를 비트로 가져옴
#ifdef CONFIG_ARM64_LPA2
alternative_if_not ARM64_HAS_VA52
    mov    \tmp1, #ID_AA64MMFR0_EL1_PARANGE_48 // 지원하지 않는경우 42
alternative_else_nop_endif
#endif
    cmp    \tmp0, \tmp1
    csel    \tmp0, \tmp1, \tmp0, hi // tmp0 가 더크면 tmp1 을 저장. 더 작은값을 설정
    bfi    \tcr, \tmp0, \pos, #3 // tcr 에 PA_RANGE 를 설정
    .endm