<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