<kernel v5.0>
smp_prepare_boot_cpu() – ARM32
arch/arm/kernel/smp.c
/* * Mark the boot cpu "online" so that it can call console drivers in * printk() and can access its per-cpu storage. */ void __init smp_prepare_boot_cpu(void) { set_my_cpu_offset(per_cpu_offset(smp_processor_id())); }
smp 시스템에서 부트 cpu에 대한 사전 준비를 수행한다.
- TPIDRPRW 레지스터를 사용하여 현재 cpu에 대한 per-cpu offset 값을 저장하여 per-cpu 변수에 대한 빠른 access를 가능하게 한다.
- 참고: Per-cpu | 문c
smp_prepare_boot_cpu() – ARM64
arch/arm64/kernel/smp.c
void __init smp_prepare_boot_cpu(void) { set_my_cpu_offset(per_cpu_offset(smp_processor_id())); /* * Initialise the static keys early as they may be enabled by the * cpufeature code. */ jump_label_init(); cpuinfo_store_boot_cpu(); }
smp 시스템에서 부트 cpu에 대한 사전 준비를 수행한다.
- 코드 라인 3에서 per-cpu를 사용하기 전에 부트 cpu에 대한 offset 값을 tpidr에 기록한다.
- 참고: smp_setup_processor_id() | 문c
- 코드 라인 8에서 static 키를 사용하기 위해 jump 라벨 엔트리들에 대한 초기화를 수행한다.
- 코드 라인 9에서 부트 cpu에 대한 arm64 cpu 정보를 읽어온다.
ARM64 CPU 정보
cpuinfo_store_boot_cpu() – ARM64
arch/arm64/kernel/cpuinfo.c
void __init cpuinfo_store_boot_cpu(void) { struct cpuinfo_arm64 *info = &per_cpu(cpu_data, 0); __cpuinfo_store_cpu(info); boot_cpu_data = *info; init_cpu_features(&boot_cpu_data); }
부트업 cpu에 대해 각종 시스템 레지스터 값들을 읽어 cpuinfo_arm64에 저장하고, cpu features들을 초기화한다.
- 참고: CPU Capabilities – ARM64 | 문c
__cpuinfo_store_cpu()
arch/arm64/kernel/cpuinfo.c
static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) { info->reg_cntfrq = arch_timer_get_cntfrq(); /* * Use the effective value of the CTR_EL0 than the raw value * exposed by the CPU. CTR_E0.IDC field value must be interpreted * with the CLIDR_EL1 fields to avoid triggering false warnings * when there is a mismatch across the CPUs. Keep track of the * effective value of the CTR_EL0 in our internal records for * acurate sanity check and feature enablement. */ info->reg_ctr = read_cpuid_effective_cachetype(); info->reg_dczid = read_cpuid(DCZID_EL0); info->reg_midr = read_cpuid_id(); info->reg_revidr = read_cpuid(REVIDR_EL1); info->reg_id_aa64dfr0 = read_cpuid(ID_AA64DFR0_EL1); info->reg_id_aa64dfr1 = read_cpuid(ID_AA64DFR1_EL1); info->reg_id_aa64isar0 = read_cpuid(ID_AA64ISAR0_EL1); info->reg_id_aa64isar1 = read_cpuid(ID_AA64ISAR1_EL1); info->reg_id_aa64mmfr0 = read_cpuid(ID_AA64MMFR0_EL1); info->reg_id_aa64mmfr1 = read_cpuid(ID_AA64MMFR1_EL1); info->reg_id_aa64mmfr2 = read_cpuid(ID_AA64MMFR2_EL1); info->reg_id_aa64pfr0 = read_cpuid(ID_AA64PFR0_EL1); info->reg_id_aa64pfr1 = read_cpuid(ID_AA64PFR1_EL1); info->reg_id_aa64zfr0 = read_cpuid(ID_AA64ZFR0_EL1); /* Update the 32bit ID registers only if AArch32 is implemented */ if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) { info->reg_id_dfr0 = read_cpuid(ID_DFR0_EL1); info->reg_id_isar0 = read_cpuid(ID_ISAR0_EL1); info->reg_id_isar1 = read_cpuid(ID_ISAR1_EL1); info->reg_id_isar2 = read_cpuid(ID_ISAR2_EL1); info->reg_id_isar3 = read_cpuid(ID_ISAR3_EL1); info->reg_id_isar4 = read_cpuid(ID_ISAR4_EL1); info->reg_id_isar5 = read_cpuid(ID_ISAR5_EL1); info->reg_id_mmfr0 = read_cpuid(ID_MMFR0_EL1); info->reg_id_mmfr1 = read_cpuid(ID_MMFR1_EL1); info->reg_id_mmfr2 = read_cpuid(ID_MMFR2_EL1); info->reg_id_mmfr3 = read_cpuid(ID_MMFR3_EL1); info->reg_id_pfr0 = read_cpuid(ID_PFR0_EL1); info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1); info->reg_mvfr0 = read_cpuid(MVFR0_EL1); info->reg_mvfr1 = read_cpuid(MVFR1_EL1); info->reg_mvfr2 = read_cpuid(MVFR2_EL1); } if (IS_ENABLED(CONFIG_ARM64_SVE) && id_aa64pfr0_sve(info->reg_id_aa64pfr0)) info->reg_zcr = read_zcr_features(); cpuinfo_detect_icache_policy(info); }
현재 cpu에 대한 각종 시스템 레지스터 값들을 읽어 cpuinfo_arm64에 저장한다.
read_cpuid_effective_cachetype()
arch/arm64/include/asm/cache.h
/* * Read the effective value of CTR_EL0. * * According to ARM ARM for ARMv8-A (ARM DDI 0487C.a), * section D10.2.33 "CTR_EL0, Cache Type Register" : * * CTR_EL0.IDC reports the data cache clean requirements for * instruction to data coherence. * * 0 - dcache clean to PoU is required unless : * (CLIDR_EL1.LoC == 0) || (CLIDR_EL1.LoUIS == 0 && CLIDR_EL1.LoUU == 0) * 1 - dcache clean to PoU is not required for i-to-d coherence. * * This routine provides the CTR_EL0 with the IDC field updated to the * effective state. */
static inline u32 __attribute_const__ read_cpuid_effective_cachetype(void) { u32 ctr = read_cpuid_cachetype(); if (!(ctr & BIT(CTR_IDC_SHIFT))) { u64 clidr = read_sysreg(clidr_el1); if (CLIDR_LOC(clidr) == 0 || (CLIDR_LOUIS(clidr) == 0 && CLIDR_LOUU(clidr) == 0)) ctr |= BIT(CTR_IDC_SHIFT); } return ctr; }
Cache 타입 레지스터 값을 읽어온다. (실제 PoU 동작이 필요 없는 경우 읽어온 idc 필드를 수정한다.)
- CTR 레지스터의 IDC 필드가 0이면 PoU를 위해 데이터 캐시의 clean이 필요한 경우이다. 만일 CLIDR 레지스터를 읽어 LoC=0 또는 LoU=0이면 idc 비트를 반대로 1로 설정한다. 이렇게 하여 데이터 캐시의 clean이 필요 없다라고 설정한다.
- 참고: ARM64 시스템 주요 레지스터 | 문c
read_cpuid_cachetype()
arch/arm64/include/asm/cputype.h
static inline u32 __attribute_const__ read_cpuid_cachetype(void) { return read_cpuid(CTR_EL0); }
Cache 타입 레지스터 값을 읽어온다.
cpuinfo_detect_icache_policy()
arch/arm64/kernel/cpuinfo.c
static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info) { unsigned int cpu = smp_processor_id(); u32 l1ip = CTR_L1IP(info->reg_ctr); switch (l1ip) { case ICACHE_POLICY_PIPT: break; case ICACHE_POLICY_VPIPT: set_bit(ICACHEF_VPIPT, &__icache_flags); break; default: /* Fallthrough */ case ICACHE_POLICY_VIPT: /* Assume aliasing */ set_bit(ICACHEF_ALIASING, &__icache_flags); } pr_info("Detected %s I-cache on CPU%d\n", icache_policy_str[l1ip], cpu); }
인스트럭션 캐시 타입을 알아와서 반환한다. 캐시 타입은 PIPT, VPIPT 또는 VIPT 중 하나이다.
다음은 Cortext A-72 코어를 가진 ARM64 시스템에서 명령 캐시 타입을 인식하여 보여주는 로그이다.
[ 0.000000] Detected VIPT I-cache on CPU0
참고
- ARM64 시스템 주요 레지스터 | 문c
- CPU Capabilities – ARM64 | 문c