티스토리 뷰

TTBR

  • Translation Table Base Register
  • 가상 주소를 싥제 주소로 매핑시 사용하는 레지스터

TTBR0_EL1 : 유저 스페이스

  • Holds the base address of the translation table for the initial lookup for stage 1 of the translation of an address from the lower VA range in the EL1&0 translation regime, and other information for this translation regime.

TTBR1: 커널 스페이스

  • Holds the base address of the translation table for the initial lookup for stage 1 of the translation of an address from the higher VA range in the EL1&0 stage 1 translation regime, and other information for this translation regime.

TTBRx_EL1 에서 적절한 base address 를 찾고 VA 상위 비트를 사용해 페이지 테이블을 탐색합니다.

(base_address 로 PGD 를 찾는걸까? pgd = va(ttbr0_el1) ?)

cpu_uninstall_idmap

  • TTBR0 를 부팅 시점에서는 idmapping 용도로만 사용 (identity maaping = VA = PA)
  • TTBR0 에 접근하더라도 pgd 가 아니라 zero page 로 들어가도록 함
  • 그래서 이 설정을 제거할 것
void __init __no_sanitize_address setup_arch(char **cmdline_p)
{
    /*
     * TTBR0 is only used for the identity mapping at this stage. Make it
     * point to zero page to avoid speculatively fetching new entries.
     */
    cpu_uninstall_idmap();
  • 부팅 초기에 사용한 TTBR0_EL1 idmap 을 제거하고, 현재 실행중인 mm 을 설치해야함
  • TCR_EL1.T0SZ (유저 공간의 가상 주소 공간 크기)
  • idmap 은 유저공간과 같은 가상 주소 영역에 위치하지만, global entry (pgd) 를 사용하고, T0SZ 도 다를 수 있다.
  • TLB 를 invalidate 하고 T0SZ 를 설정하기 전까지는 reserved table 을 임시로 설정한다.
  • 현재 task 가 커널 전용 테스크라면 별도 처리를 하지 않는다.
/*
 * Remove the idmap from TTBR0_EL1 and install the pgd of the active mm.
 *
 * The idmap lives in the same VA range as userspace, but uses global entries
 * and may use a different TCR_EL1.T0SZ. To avoid issues resulting from
 * speculative TLB fetches, we must temporarily install the reserved page
 * tables while we invalidate the TLBs and set up the correct TCR_EL1.T0SZ.
 *
 * If current is a not a user task, the mm covers the TTBR1_EL1 page tables,
 * which should not be installed in TTBR0_EL1. In this case we can leave the
 * reserved page tables in place.
 */
static inline void cpu_uninstall_idmap(void)
{
    // arm64 의 current 는 현재 task. 즉 Init_task
    // activie_mm = init_mm
    struct mm_struct *mm = current->active_mm;

    // 임시로 사용하는 예약된 pgd 의 물리 주소를 ttbr0 에 설정
    // t0sz 를 설정하기 전에 문제가 될 수 있으니 미리 변경
    cpu_set_reserved_ttbr0(); 
    local_flush_tlb_all();

    // #define cpu_set_default_tcr_t0sz()    __cpu_set_tcr_t0sz(TCR_T0SZ(vabits_actual))
    // tcr.t0sz 설정. 유저스페이스의 가상 주소 사이즈
    cpu_set_default_tcr_t0sz();

    // pan : privileged access never
    // 커널 공간에서 유저공간 (ttbr0)을 접근하지 않겠다라는 설정
    // !pan 이기 때문에 커널에서 유저 mm 에 접근할 수 있음
    if (mm != &init_mm && !system_uses_ttbr0_pan())
        cpu_switch_mm(mm->pgd, mm);
}

/*
 * Set TTBR0 to reserved_pg_dir. No translations will be possible via TTBR0.
 */
static inline void cpu_set_reserved_ttbr0_nosync(void)
{
    unsigned long ttbr = phys_to_ttbr(__pa_symbol(reserved_pg_dir));

    write_sysreg(ttbr, ttbr0_el1);
}

static inline void cpu_set_reserved_ttbr0(void)
{
    cpu_set_reserved_ttbr0_nosync();
    isb();
}

cpu_switch_mm

context switch 시 메모리 매핑을 전환

infos

  • mm->pgd 는 프로세스의 최상위 페이지 테이블 (커널 가상 주소)
  • ttbr base address 는 물리주소
mm->pgd 를 pa 로 변환 후 ttbr 로 변환해서 레지스터에 등록

#ifdef CONFIG_ARM64_PA_BITS_52
#define phys_to_ttbr(addr)    (((addr) | ((addr) >> 46)) & TTBR_BADDR_MASK_52)
#else
#define phys_to_ttbr(addr)    (addr)
#endif
void cpu_do_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm)
{
    unsigned long ttbr1 = read_sysreg(ttbr1_el1);
    unsigned long asid = ASID(mm);
    unsigned long ttbr0 = phys_to_ttbr(pgd_phys);

    /* Skip CNP for the reserved ASID */
    if (system_supports_cnp() && asid)
        ttbr0 |= TTBR_CNP_BIT;

    /* SW PAN needs a copy of the ASID in TTBR0 for entry */
    if (IS_ENABLED(CONFIG_ARM64_SW_TTBR0_PAN))
        ttbr0 |= FIELD_PREP(TTBR_ASID_MASK, asid);

    /* Set ASID in TTBR1 since TCR.A1 is set */
    ttbr1 &= ~TTBR_ASID_MASK;
    ttbr1 |= FIELD_PREP(TTBR_ASID_MASK, asid);

    cpu_set_reserved_ttbr0_nosync();
    write_sysreg(ttbr1, ttbr1_el1);
    write_sysreg(ttbr0, ttbr0_el1);
    isb();
    post_ttbr_update_workaround();
}

static inline void cpu_switch_mm(pgd_t *pgd, struct mm_struct *mm)
{
    BUG_ON(pgd == swapper_pg_dir);
    cpu_do_switch_mm(virt_to_phys(pgd),mm);
}

'개발 > arm64 linux 6 분석' 카테고리의 다른 글

local_daif_restore (setup_arch)  (0) 2025.05.31
SHADOW_CALL_STACK [SCS] (setup_arch)  (0) 2025.05.31
early_param (setup_arch)  (0) 2025.05.31
jump_label (setup_arch)  (0) 2025.05.31
start_kernel 2 setup_arch  (0) 2025.05.24
댓글