개발/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