smp_prepare_boot_cpu()

<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를 가능하게 한다.

 

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에 기록한다.
  • 코드 라인 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들을 초기화한다.

 

__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

 

참고

 

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다