CPUID 명령어

x86 CPUID 기반 런타임 기능 탐지를 부트 초기화와 동적 최적화 경로 관점에서 심층 분석합니다. Leaf/Subleaf 구조와 vendor 확장 해석, Intel·AMD 기능 비트 차이, microcode 업데이트로 달라지는 기능 가시성, Linux 커널의 boot_cpu_data·x86_capability·alternative patching 연계, 취약점(Vulnerability) 완화 기능(IBRS/SSBD 등) 감지, 가상화(Virtualization) 환경에서의 CPUID masking 영향, 사용자 공간(User Space) 노출과 커널 내부 정책 차이까지 안전한 기능 게이팅 설계에 필요한 핵심을 다룹니다.

전제 조건: 어셈블리(Assembly) 종합을 먼저 읽으세요. CPUID는 x86 전용 명령어로, 레지스터(Register) 입출력(I/O) 구조 이해가 필요합니다.
일상 비유: CPUID는 제품 스펙 라벨 읽기와 같습니다. 같은 CPU 아키텍처라도 세대마다 지원 기능이 다르므로, 사용 전 스펙 라벨(CPUID Leaf)을 확인해 호환성을 보장합니다.

핵심 요약

  • Leaf 구조 — EAX에 leaf 번호, ECX에 sub-leaf를 넣고 CPUID 실행하면 EAX/EBX/ECX/EDX에 정보 반환.
  • 주요 Leaf — Leaf 0x01(기본 피처), 0x07(확장 피처), 0x0D(XSAVE), 0x80000001(확장 피처 AMD).
  • 커널 추상화 — x86_capability[] 배열에 피처 비트 통합, cpu_has() 매크로(Macro)로 런타임 확인.
  • Intel vs AMD — 동일 기능도 다른 Leaf/비트 위치 사용, 커널이 벤더별로 추상화.
  • 부팅 초기화 — early_cpu_detect()에서 boot_cpu_data 구조체(Struct) 초기화.

단계별 이해

  1. 기본 CPUID 동작
    EAX 레지스터에 leaf 번호를 넣고 CPUID를 실행하면 4개 레지스터로 CPU 정보가 반환됩니다.
  2. 주요 Leaf 파악
    Leaf 0x01과 0x07이 가장 중요합니다. 각 비트 의미를 Intel/AMD SDM에서 확인하세요.
  3. 커널 추상화 이해
    boot_cpu_data, x86_capability[], cpu_has() 매크로를 통해 커널이 CPUID를 어떻게 추상화하는지 파악합니다.
  4. 벤더 차이 인식
    Intel과 AMD가 동일 기능에 다른 Leaf를 쓰는 경우를 파악하고 커널의 통합 방식을 이해합니다.

CPUID는 x86 프로세서의 기능, 모델 정보, 캐시(Cache) 토폴로지(Topology), 확장 기능 등을 쿼리하는 핵심 명령어입니다. Linux 커널은 부팅 초기에 CPUID를 광범위하게 사용하여 CPU 기능을 탐지하고, 런타임에 최적의 코드 경로를 선택합니다.

CPUID 기본 동작

CPUID 명령어 구조 및 주요 Leaf 참조 입력 레지스터 EAX = leaf 번호 (기능 번호) 0x00, 0x01, 0x07, 0x0D ... ECX = sub-leaf (일부 leaf) 0x07의 경우 ECX=0,1,2... mov $0x01, %eax; cpuid CPUID 명령어 CPUID 완전한 직렬화 명령어 실행 전 파이프라인 flush 권한 수준 무관 실행 가능 HYPERVISOR에서 인터셉트 가능 출력 레지스터 (leaf에 따라 다름) EAX 버전/수량/크기 정보 EBX 벤더 문자열 / 부가 정보 ECX 피처 플래그 (확장 기능) EDX 피처 플래그 (기본 기능) 각 비트의 의미는 Intel SDM Vol.2 참조 커널에서 중요한 CPUID Leaf: Leaf 0x00 벤더 ID + 최대 leaf EBX:EDX:ECX = "GenuineIntel" "AuthenticAMD" EAX = 최대 기본 leaf 수 Leaf 0x01 프로세서 식별 + 피처 EAX: Family/Model /Stepping ECX: SSE4.2, AVX, AES EDX: FPU, SSE, SSE2... Leaf 0x07 (sub-leaf 0) 확장 피처 플래그 EBX: AVX2, BMI1/2 SMEP, SMAP, SHA ECX: AVX-512, UMIP EDX: AVX-512 VP2INT... Leaf 0x0D XSAVE 상태 정보 EAX:EDX: 지원 XSAVE 컴포넌트 비트맵 EBX: XSAVE 영역 크기 ECX: 최대 XSAVE 크기 Leaf 0x80000001 확장 피처 플래그 ECX: LAHF, SVM(AMD) EDX: NX, 1GB Pages RDTSCP, LM(64bit) 0x80000000: 최대 확장 leaf
/* CPUID 명령어 기본 사용법
 *   입력: EAX = leaf (기능 번호), ECX = sub-leaf (일부 leaf)
 *   출력: EAX, EBX, ECX, EDX (leaf에 따라 의미 다름)
 *
 *   Intel SDM Vol. 2A, "CPUID — CPU Identification" 참조
 */

/* Leaf 0: 최대 기본 leaf 번호 + 벤더 문자열 */
mov     $0, %eax
cpuid
/* EAX = 최대 지원 leaf 번호 (예: 0x20)
 * EBX:EDX:ECX = 벤더 ID 문자열
 *   Intel: "GenuineIntel" (EBX=0x756E6547 EDX=0x49656E69 ECX=0x6C65746E)
 *   AMD:   "AuthenticAMD" (EBX=0x68747541 EDX=0x69746E65 ECX=0x444D4163)
 */

/* Leaf 1: 프로세서 식별 + 기능 플래그 */
mov     $1, %eax
cpuid
/* EAX = Version Information (Stepping, Model, Family)
 *   [3:0]   Stepping ID
 *   [7:4]   Model
 *   [11:8]  Family
 *   [13:12] Processor Type
 *   [19:16] Extended Model
 *   [27:20] Extended Family
 * EBX = [7:0] Brand Index, [15:8] CLFLUSH size, [23:16] Max APIC IDs, [31:24] Local APIC ID
 * ECX = Feature flags (SSE3, PCLMULQDQ, VMX, SMX, SSE4.1, SSE4.2, x2APIC, AES-NI, AVX, ...)
 * EDX = Feature flags (FPU, VME, PSE, TSC, MSR, PAE, APIC, SEP, MTRR, SSE, SSE2, HTT, ...)
 */

주요 CPUID Leaf 요약

EAX (Leaf)ECX (Sub-leaf)설명핵심 반환값
0x00-벤더 ID + 최대 기본 leafEBX:EDX:ECX = 벤더 문자열
0x01-프로세서 식별 + 기능 플래그EAX=버전, ECX/EDX=피처 비트
0x02-캐시/TLB 디스크립터 (Intel)바이트별 디스크립터 코드
0x040,1,2,...캐시 파라미터 (Intel)캐시 타입, 크기, 연관도, 라인 크기
0x06-Thermal / Power ManagementTurbo Boost, HWP 지원 여부
0x070확장 기능 플래그AVX2, BMI1/2, AVX-512, SHA, UMIP 등
0x071확장 기능 플래그 2AVX-VNNI, HRESET, LAM 등
0x0A-Performance MonitoringPMC 버전, 카운터 수/비트 폭 → PMU 탐지 상세
0x0B0,1,2Extended TopologySMT/Core/Package 토폴로지
0x0D0,1,2,...XSAVE 상태 영역XSAVE 크기, 지원 컴포넌트 (SSE, AVX, MPX, AVX-512, PKRU 등) → XSAVE 상세
0x0F0,1QoS Monitoring (Intel RDT)L3 캐시 모니터링 지원
0x100,1,2,3QoS Enforcement (Intel RDT)L3 CAT, L2 CAT, MBA 지원
0x120,1,2,...SGX Capability (Intel)SGX 지원 여부, EPC 섹션 정보
0x140,1Intel Processor TracePT 기능, 필터링 범위
0x15-TSC / Core Crystal ClockTSC:Core 비율 (TSC 주파수 계산) → 주파수 탐지 상세
0x16-Processor Frequency기본/최대/버스(Bus) 주파수 (MHz) → 주파수 탐지 상세
0x1F0,1,2,...V2 Extended TopologyModule/Tile/Die 레벨 포함

확장 CPUID (0x80000000+)

EAX (Leaf)설명핵심 반환값
0x80000000최대 확장 leaf 번호EAX = 최대 확장 leaf (예: 0x80000008)
0x80000001확장 기능 플래그ECX/EDX: LAHF, SVM(AMD), NX, 1GB Pages, RDTSCP, Long Mode 등
0x80000002~4프로세서 브랜드 문자열48바이트 ASCII: "Intel(R) Core(TM) i9-..." 등
0x80000005L1 캐시/TLB 정보 (AMD)L1 Data/Instruction 캐시 크기/연관도
0x80000006L2/L3 캐시 정보캐시 크기, 연관도, 라인 크기
0x80000007고급 전력 관리EDX bit 8: Invariant TSC 지원
0x80000008주소 크기 + 코어 수물리/가상 주소(Virtual Address) 비트 수, 코어 수 (AMD)
0x8000000ASVM 기능 (AMD)SVM 리비전, ASID 수, Nested Paging 등
0x8000001EAMD 토폴로지Compute Unit ID, Node ID

인라인 어셈블리로 CPUID 사용

/* 커널의 CPUID 래퍼 — arch/x86/include/asm/processor.h */
static inline void cpuid(unsigned int leaf,
    unsigned int *eax, unsigned int *ebx,
    unsigned int *ecx, unsigned int *edx)
{
    *eax = leaf;
    *ecx = 0;  /* leaf-only 쿼리는 sub-leaf=0 */
    asm volatile ("cpuid"
        : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
        : "0"(*eax), "2"(*ecx));
}

/* sub-leaf 지정 버전 */
static inline void cpuid_count(unsigned int leaf,
    unsigned int subleaf,
    unsigned int *eax, unsigned int *ebx,
    unsigned int *ecx, unsigned int *edx)
{
    *eax = leaf;
    *ecx = subleaf;
    asm volatile ("cpuid"
        : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
        : "0"(*eax), "2"(*ecx));
}

/* 단일 레지스터만 필요한 경우의 편의 함수 */
static inline unsigned int cpuid_eax(unsigned int leaf)
{
    unsigned int eax, ebx, ecx, edx;
    cpuid(leaf, &eax, &ebx, &ecx, &edx);
    return eax;
}
/* cpuid_ebx(), cpuid_ecx(), cpuid_edx()도 동일 패턴 */

Leaf 1 피처 비트 상세 (ECX/EDX)

ECX 피처 플래그 (Leaf 1)

비트이름커널 상수설명
0SSE3X86_FEATURE_SSE3Streaming SIMD Extensions 3
1PCLMULQDQX86_FEATURE_PCLMULQDQCarry-Less Multiplication (CRC, GCM)
5VMXX86_FEATURE_VMXVirtual Machine Extensions (Intel VT-x)
6SMXX86_FEATURE_SMXSafer Mode Extensions (Intel TXT)
9SSSE3X86_FEATURE_SSSE3Supplemental SSE3
12FMAX86_FEATURE_FMAFused Multiply-Add (FMA3)
13CX16X86_FEATURE_CX16CMPXCHG16B 지원
19SSE4.1X86_FEATURE_SSE4_1SSE4.1 명령어
20SSE4.2X86_FEATURE_SSE4_2SSE4.2 (PCMPESTRI, CRC32)
21x2APICX86_FEATURE_X2APICExtended APIC (MSR 기반)
25AES-NIX86_FEATURE_AESAES 명령어 (AESENC/AESDEC)
26XSAVEX86_FEATURE_XSAVEXSAVE/XRSTOR/XSETBV/XGETBV
27OSXSAVEX86_FEATURE_OSXSAVEOS가 XSAVE를 활성화함
28AVXX86_FEATURE_AVXAdvanced Vector Extensions
30RDRANDX86_FEATURE_RDRAND하드웨어 난수 생성
31HypervisorX86_FEATURE_HYPERVISOR하이퍼바이저(Hypervisor) 존재 (VM에서만 set)

EDX 피처 플래그 (Leaf 1)

비트이름커널 상수설명
0FPUX86_FEATURE_FPUx87 FPU 내장
3PSEX86_FEATURE_PSEPage Size Extension (4MB 페이지(Page))
4TSCX86_FEATURE_TSCTime Stamp Counter (RDTSC)
5MSRX86_FEATURE_MSRRDMSR/WRMSR 지원
6PAEX86_FEATURE_PAEPhysical Address Extension (36+ bit)
9APICX86_FEATURE_APIC온칩 APIC
11SEPX86_FEATURE_SEPSYSENTER/SYSEXIT
12MTRRX86_FEATURE_MTRRMemory Type Range Registers
13PGEX86_FEATURE_PGEPage Global Enable
16PATX86_FEATURE_PATPage Attribute Table
19CLFLUSHX86_FEATURE_CLFLUSH캐시 라인(Cache Line) 플러시(Flush)
25SSEX86_FEATURE_SSEStreaming SIMD Extensions
26SSE2X86_FEATURE_SSE2SSE2 (x86-64 필수)
28HTTX86_FEATURE_HTTHyper-Threading Technology

참고: NX는 Leaf 1 EDX가 아니라 확장 Leaf 0x80000001의 EDX bit 20에서 확인합니다.

Leaf 7 Sub-leaf 1 EAX 피처 플래그 (ECX=1)

Leaf 7 Sub-leaf 1(EAX=0x07, ECX=1)은 Ice Lake 이후 추가된 신규 확장 기능을 보고합니다. 커널은 arch/x86/kernel/cpu/common.c에서 cpuid_count(7, 1, ...)으로 이 값을 읽어 x86_capability 배열에 저장합니다.

비트이름커널 상수설명
4AVX-VNNIX86_FEATURE_AVX_VNNIVEX-인코딩 벡터 뉴럴 네트워크 INT8 곱셈누산 (Alder Lake+)
5AVX512_BF16X86_FEATURE_AVX512_BF16BFloat16 변환 명령어 (AI/ML 추론 최적화)
7CMPCCXADDX86_FEATURE_CMPCCXADD비교 조건부 원자 추가 (락 없는 연산, Sapphire Rapids+)
10FZRMX86_FEATURE_FZRMFast Zero-length REP MOVSB
11FSRSX86_FEATURE_FSRSFast Short REP STOSB (짧은 memset 최적화)
12FSRCSX86_FEATURE_FSRCFast Short REP CMPSB/SCASB
17FREDX86_FEATURE_FREDFlexible Return and Event Delivery (인터럽트(Interrupt) 지연(Latency) 단축)
18LKGSX86_FEATURE_LKGSLoad Kernel GS Base (SWAPGS 대체, 보안 강화)
19WRMSRNSX86_FEATURE_WRMSRNSNon-serializing WRMSR (성능 개선)
21AMX-FP16X86_FEATURE_AMX_FP16AMX FP16 행렬 곱셈 (4세대 Xeon Scalable+)
22HRESETX86_FEATURE_HRESETHistory Reset — 분기 예측(Branch Prediction)기 히스토리 선택적 초기화
26LAMX86_FEATURE_LAMLinear Address Masking — 포인터 상위 비트에 태그 저장 (사용자 공간)

Leaf 7 확장 기능 (EBX/ECX/EDX)

CPUID Leaf 7 (Sub-leaf 0) 기능 분류 Leaf 7 / Sub-leaf 0 EBX 주요 비트 FSGSBASE, BMI1/BMI2, INVPCID AVX2, AVX512F/DQ/BW/VL SMEP/SMAP, RDSEED CLFLUSHOPT, CLWB, SHA_NI 성능 + 보안 + ISA 확장 핵심 ECX 주요 비트 AVX512VBMI, UMIP, PKU WAITPKG (UMWAIT/TPAUSE) GFNI, VAES 사용자/벡터/암호 연산 확장 EDX 주요 비트 AVX512_4VNNIW (일부 플랫폼) SPEC_CTRL, STIBP FLUSH_L1D, ARCH_CAPABILITIES SSBD 주로 speculative execution 완화 관련 커널 적용 흐름 1) 부팅 시 CPUID 스캔 → x86_capability 비트셋 채움 2) 대체 코드(alternative)와 static key로 기능 경로 선택 3) 보안 비트(SPEC_CTRL/SSBD 등)는 완화 정책과 함께 적용 4) 사용자 공간은 `/proc/cpuinfo`, `cpuid`, hwcaps로 관찰 5) 실제 사용 가능 여부는 BIOS, microcode, CR4/MSR 설정에 의존

커널 부팅 시 CPUID 처리 흐름

커널 부팅 시 CPUID 처리 3단계 흐름 단계 1 단계 2 단계 3 early_cpu_init() ▸ 벤더 테이블 순회 (x86_cpu_dev 배열) ▸ CPUID leaf 0x00 → 벤더 문자열 확인 → Intel / AMD / Centaur → boot_cpu_data.x86_vendor arch/x86/kernel/cpu/common.c early_identify_cpu() ▸ leaf 0x01 EAX → Family/Model/Stepping ▸ leaf 0x07 (sub-leaf 0) AVX2, SMEP, SMAP, SHA... ▸ leaf 0x80000001 NX, 1GB Pages, RDTSCP... ▸ leaf 0x80000008 물리/가상 주소 비트 수 확인 vendor_init() (Intel/AMD) ▸ intel_init() / amd_init() 벤더 고유 CPUID leaf 해석 ▸ 에라타(Errata) 워크어라운드 Family/Model별 버그 패치 ▸ 마이크로코드 로드 (옵션) /boot/microcode.cpio 또는 intel-microcode 패키지 결과물: struct cpuinfo_x86 boot_cpu_data (전역) 식별 정보 x86: Family (예: 6) x86_model: Model (예: 151) x86_stepping: Stepping x86_vendor: VENDOR_INTEL x86_model_id: 브랜드 문자열 주소 비트 x86_phys_bits: 물리 (예: 46) x86_virt_bits: 가상 (예: 48) x86_clflush_size: 캐시 라인 피처 비트 배열 x86_capability[NCAPINTS] 각 워드 = CPUID leaf의 비트 boot_cpu_has(X86_FEATURE_AVX2) this_cpu_has(X86_FEATURE_SMEP) 런타임 분기 static_cpu_has(): 컴파일 타임 가장 빠름 (jump label) cpu_feature_enabled(): 권장 alternative(): 패치 가능 코드
/* 커널의 CPU 식별 흐름 — arch/x86/kernel/cpu/common.c */

/* 1단계: 부팅 초기 CPU 탐지 (early_cpu_init) */
void early_cpu_init(void)
{
    /* CPU 벤더 테이블에서 일치하는 벤더 찾기 */
    const struct cpu_dev *cdev;
    for (cdev = __x86_cpu_dev_start; cdev < __x86_cpu_dev_end; cdev++) {
        if (cdev->c_detect) cdev->c_detect(&boot_cpu_data);
    }
    early_identify_cpu(&boot_cpu_data);
}

/* 2단계: CPU 식별 상세 */
static void early_identify_cpu(struct cpuinfo_x86 *c)
{
    /* Leaf 0: 벤더 확인 */
    cpuid(0x00, &c->cpuid_level, ...);

    /* Leaf 1: Family/Model/Stepping + 기본 피처 */
    cpuid(0x01, &tfms, &misc, &cap[4], &cap[0]);
    c->x86          = (tfms >> 8) & 0xf;     /* Family */
    c->x86_model    = (tfms >> 4) & 0xf;     /* Model */
    c->x86_stepping = tfms & 0xf;            /* Stepping */

    /* Extended Family/Model (Family ≥ 6 또는 15인 경우) */
    if (c->x86 == 0xf)
        c->x86 += (tfms >> 20) & 0xff;       /* + Extended Family */
    if (c->x86 >= 0x6)
        c->x86_model += ((tfms >> 16) & 0xf) << 4; /* + Extended Model */

    /* Leaf 7: 확장 기능 (AVX2, SMEP, SMAP, ...) */
    cpuid_count(0x07, 0, &eax, &cap[9], &cap[16], &cap[18]);

    /* 확장 leaf: Long Mode, NX, GB Pages, ... */
    cpuid(0x80000001, &eax, &ebx, &cap[6], &cap[1]);

    /* 물리/가상 주소 비트 수 */
    cpuid(0x80000008, &eax, ...);
    c->x86_phys_bits = eax & 0xff;           /* 예: 46 (Intel), 48 (AMD) */
    c->x86_virt_bits = (eax >> 8) & 0xff;    /* 예: 48 또는 57 (LA57) */
}

/* 3단계: 벤더별 초기화 (Intel/AMD 고유 처리) */
/* intel_init(), amd_init() 에서 각 벤더 고유 CPUID leaf 추가 해석 */

x86_capability 배열과 피처 테스트

/* arch/x86/include/asm/cpufeature.h */

/* CPU 피처 비트를 저장하는 구조체 (커널 전역) */
struct cpuinfo_x86 {
    __u8    x86;              /* CPU family */
    __u8    x86_vendor;       /* X86_VENDOR_INTEL, _AMD, ... */
    __u8    x86_model;
    __u8    x86_stepping;
    __u32   x86_capability[NCAPINTS]; /* 피처 비트 배열 */
    char    x86_model_id[64]; /* 브랜드 문자열 */
    __u8    x86_phys_bits;    /* 물리 주소 비트 */
    __u8    x86_virt_bits;    /* 가상 주소 비트 */
    /* ... */
};

/* boot_cpu_data: 부팅 CPU의 정보 (전역 변수) */
extern struct cpuinfo_x86 boot_cpu_data;

/* 피처 존재 여부 확인 */
if (boot_cpu_has(X86_FEATURE_AVX2)) {
    /* AVX2 사용 가능한 코드 경로 */
}

/* static_cpu_has(): 부팅 후 패치되는 최적화 버전 */
/* Alternative 메커니즘으로 NOP ↔ JMP으로 패치됨 */
if (static_cpu_has(X86_FEATURE_XSAVE)) {
    /* 조건 분기 오버헤드 없이 코드 경로 결정 */
}
ℹ️

static_cpu_has()Alternative 명령어 패치(Patch) 메커니즘을 사용합니다. 부팅 시 CPUID 결과에 따라 실제 기계어(Machine Code) 코드를 NOP 또는 JMP으로 바이너리 패치하여, 런타임에 조건 분기 오버헤드(Overhead)를 완전히 제거합니다. .altinstructions 섹션에 패치 정보가 저장됩니다.

Family / Model / Stepping 해석

/* CPUID Leaf 1 EAX에서 Family/Model/Stepping 추출 */
/*
 * EAX 비트 레이아웃:
 *   [3:0]   Stepping ID
 *   [7:4]   Base Model
 *   [11:8]  Base Family
 *   [13:12] Processor Type (00=OEM, 01=OverDrive, 10=Dual, 11=Reserved)
 *   [19:16] Extended Model
 *   [27:20] Extended Family
 *
 * Display Family = Base Family + Extended Family  (if Base Family == 0xF)
 *                = Base Family                     (otherwise)
 * Display Model  = (Extended Model << 4) + Base Model  (if Base Family >= 0x6 또는 0xF)
 *                = Base Model                           (otherwise)
 */

/* 예시: Intel Core i9-14900K (Raptor Lake-S Refresh) */
/* CPUID 1 EAX = 0x000B0671
 *   Base Family = 0x6, Extended Family = 0x0  → Display Family = 6
 *   Base Model  = 0x7, Extended Model  = 0xB  → Display Model  = 0xB7
 *   Stepping    = 0x1
 *   → "Family 6, Model 183, Stepping 1"
 */

/* 예시: AMD Ryzen 9 7950X (Zen 4, Raphael) */
/* CPUID 1 EAX = 0x00A60F12
 *   Base Family = 0xF, Extended Family = 0x19 → Display Family = 25 (0x19)
 *   Base Model  = 0x1, Extended Model  = 0x6  → Display Model  = 0x61
 *   Stepping    = 0x2
 *   → "Family 25, Model 97, Stepping 2"
 */

/* 커널에서 CPU 모델별 분기 예시 */
#define INTEL_FAM6_RAPTORLAKE_S   0xB7
#define INTEL_FAM6_ALDERLAKE      0x97
#define INTEL_FAM6_SAPPHIRERAPIDS 0x8F

switch (c->x86_model) {
case INTEL_FAM6_RAPTORLAKE_S:
    /* Raptor Lake 고유 workaround/최적화 */
    break;
case INTEL_FAM6_SAPPHIRERAPIDS:
    /* Sapphire Rapids 서버 최적화 */
    break;
}
CPU 토폴로지 종합: CPUID 기반 탐지뿐 아니라 AMD CCX/CCD/IOD, Intel Tile/Hybrid, ARM DynamIQ 아키텍처와 커널 sched_domain 계층까지 종합적으로 다루는 CPU 토폴로지 페이지를 참조하세요.

CPUID 기반 토폴로지 탐지

/* Leaf 0x0B (Extended Topology Enumeration)
 * 또는 Leaf 0x1F (V2 Extended Topology — 최신 CPU)
 *
 * Sub-leaf ECX 입력에 따라 토폴로지 레벨 반환:
 *   sub-leaf 0: SMT 레벨
 *   sub-leaf 1: Core 레벨
 *   sub-leaf 2: Module/Die 레벨 (Leaf 0x1F만)
 *
 * 반환값:
 *   EAX[4:0]  = 다음 레벨 APIC ID 시프트 비트 수
 *   EBX[15:0] = 이 레벨의 논리 프로세서 수
 *   ECX[15:8] = 레벨 타입 (1=SMT, 2=Core, 3=Module, 4=Tile, 5=Die)
 *   EDX       = x2APIC ID
 */

/* arch/x86/kernel/cpu/topology.c — 커널의 토폴로지 파싱 */
void detect_extended_topology(struct cpuinfo_x86 *c)
{
    unsigned int eax, ebx, ecx, edx;
    int leaf = 0x1f;  /* V2 먼저 시도 */

    cpuid_count(leaf, 0, &eax, &ebx, &ecx, &edx);
    if (ebx == 0) leaf = 0x0b;  /* V2 미지원이면 V1 사용 */

    /* sub-leaf 0: SMT 레벨 */
    cpuid_count(leaf, 0, &eax, &ebx, &ecx, &edx);
    c->x86_max_cores = ebx & 0xffff;   /* 코어당 SMT 스레드 수 */
    c->topo_smt_shift = eax & 0x1f;

    /* sub-leaf 1: Core 레벨 */
    cpuid_count(leaf, 1, &eax, &ebx, &ecx, &edx);
    c->topo_core_shift = eax & 0x1f;

    /* APIC ID 기반 패키지/코어/스레드 매핑 */
    c->topo.apicid = edx;
    c->topo.pkg_id = edx >> c->topo_core_shift;
    c->topo.core_id = (edx >> c->topo_smt_shift) &
                      ((1 << (c->topo_core_shift - c->topo_smt_shift)) - 1);
}

CPUID 캐시 정보 (Leaf 4)

/* Leaf 0x04: Deterministic Cache Parameters (Intel)
 * Sub-leaf를 0부터 증가시키며 각 캐시 레벨 정보를 열거
 *
 * 반환값:
 *   EAX[4:0]  = Cache Type (1=Data, 2=Instruction, 3=Unified, 0=No more)
 *   EAX[7:5]  = Cache Level (1=L1, 2=L2, 3=L3)
 *   EAX[25:14]= 이 캐시를 공유하는 최대 APIC ID 수
 *   EBX[11:0] = Line Size - 1
 *   EBX[21:12]= Partitions - 1
 *   EBX[31:22]= Associativity (Ways) - 1
 *   ECX        = Number of Sets - 1
 *
 * 캐시 크기 = (Ways+1) × (Partitions+1) × (LineSize+1) × (Sets+1)
 */

/* 예: Intel i9-14900K의 L3 캐시 (sub-leaf 3) */
/* EAX=0x3C004163  EBX=0x03C0003F  ECX=0x00003FFF  EDX=0x00000006
 *   Type: Unified (3)   Level: L3 (3)
 *   Line Size:  64 bytes  (0x3F + 1)
 *   Partitions: 1         (0 + 1)
 *   Ways:       16        (0xF + 1)
 *   Sets:       16384     (0x3FFF + 1)
 *   → Size = 16 × 1 × 64 × 16384 = 16 MB (per tile)
 */

/* arch/x86/kernel/cpu/cacheinfo.c */
static void ci_leaf_init(struct cacheinfo *ci,
    unsigned int eax, unsigned int ebx, unsigned int ecx)
{
    ci->level          = (eax >> 5) & 0x7;
    ci->type           = eax & 0x1f;
    ci->coherency_line_size = (ebx & 0xfff) + 1;
    ci->ways_of_associativity = ((ebx >> 22) & 0x3ff) + 1;
    ci->number_of_sets = ecx + 1;
    ci->size = ci->ways_of_associativity *
               ci->coherency_line_size *
               ci->number_of_sets;    /* 바이트 단위 캐시 크기 */
}

가상화와 CPUID

/* 하이퍼바이저 탐지: Leaf 1 ECX bit 31 (Hypervisor Present) */
static bool detect_hypervisor(void)
{
    unsigned int eax, ebx, ecx, edx;
    cpuid(1, &eax, &ebx, &ecx, &edx);
    return !!(ecx & (1 << 31));  /* Hypervisor bit */
}

/* 하이퍼바이저 전용 CPUID leaf (0x40000000+) */
/* Leaf 0x40000000: 하이퍼바이저 벤더 문자열 */
cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
/* KVM:    EBX:ECX:EDX = "KVMKVMKVM\0\0\0" */
/* Hyper-V: EBX:ECX:EDX = "Microsoft Hv" */
/* VMware: EBX:ECX:EDX = "VMwareVMware" */
/* Xen:    EBX:ECX:EDX = "XenVMMXenVMM" */

/* KVM에서 CPUID 에뮬레이션 — arch/x86/kvm/cpuid.c */
int kvm_emulate_cpuid(struct kvm_vcpu *vcpu)
{
    u32 eax = kvm_rax_read(vcpu);
    u32 ecx = kvm_rcx_read(vcpu);
    struct kvm_cpuid_entry2 *entry;

    /* 게스트에 노출할 CPUID 값 조회 (호스트와 다를 수 있음) */
    entry = kvm_find_cpuid_entry(vcpu, eax, ecx);
    if (entry) {
        kvm_rax_write(vcpu, entry->eax);
        kvm_rbx_write(vcpu, entry->ebx);
        kvm_rcx_write(vcpu, entry->ecx);
        kvm_rdx_write(vcpu, entry->edx);
    }
    return kvm_skip_emulated_instruction(vcpu);
}

/* QEMU/KVM에서 게스트 CPUID 커스터마이징:
 *   -cpu host          → 호스트 CPUID 그대로 전달
 *   -cpu host,-avx512f → AVX-512 기능 숨김
 *   -cpu EPYC          → AMD EPYC 프로필
 */
⚠️

CPUID faulting: 최신 Intel CPU는 MSR_MISC_FEATURES_ENABLES (0x140) bit 0으로 CPUID faulting을 지원합니다. 활성화하면 Ring 3(유저 모드)에서 CPUID 실행 시 #GP 예외가 발생하여, 커널이 가상화된 CPUID 값을 반환할 수 있습니다. 하이퍼바이저 없이도 CPUID 가상화가 가능해집니다.

CPUID와 보안 (Spectre/Meltdown 완화)

/* CPU 취약점 탐지에 CPUID가 핵심적으로 사용됨 */
/* arch/x86/kernel/cpu/bugs.c */

/* 1. CPUID Leaf 7 EDX: 하드웨어 완화 기능 확인 */
/* bit 26: IBRS/IBPB  — Indirect Branch Restricted Speculation */
/* bit 27: STIBP      — Single Thread Indirect Branch Predictor */
/* bit 29: IA32_ARCH_CAPABILITIES MSR 존재 */
/* bit 31: SSBD       — Speculative Store Bypass Disable */

/* 2. IA32_ARCH_CAPABILITIES MSR (CPUID로 존재 확인 후 읽기) */
if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES)) {
    u64 ia32_cap = x86_read_arch_cap_msr();
    /* bit 0: RDCL_NO     → Meltdown에 취약하지 않음 */
    /* bit 1: IBRS_ALL    → IBRS가 모든 코드에서 작동 */
    /* bit 2: RSBA        → 대안 예측기 사용 (RetBleed 관련) */
    /* bit 3: SKIP_L1DFL  → L1D flush 불필요 */
    /* bit 4: SSB_NO      → SSB 취약점 없음 */
    /* bit 5: MDS_NO      → MDS 취약점 없음 */
    /* bit 6: IF_PSCHANGE_MC_NO → PSCHANGE MC 영향 없음 */
    /* bit 7: TSX_CTRL    → TSX 비활성화 가능 */
}

/* 3. /proc/cpuinfo의 "bugs" 필드는 이 탐지 결과에 기반 */
/* 예: bugs: cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass ... */

유저 공간에서 CPUID 활용

# /proc/cpuinfo — 커널이 파싱한 CPUID 결과
cat /proc/cpuinfo | head -30
# processor    : 0
# vendor_id    : GenuineIntel
# cpu family   : 6
# model        : 183
# model name   : 13th Gen Intel(R) Core(TM) i9-13900K
# stepping     : 1
# flags        : fpu vme de pse tsc msr pae mce cx8 apic ... avx2 ...
# bugs         : spectre_v1 spectre_v2 spec_store_bypass ...

# cpuid 명령어 도구 (cpuid 패키지)
cpuid -1 -l 0    # Leaf 0 (벤더 문자열)
cpuid -1 -l 1    # Leaf 1 (피처 플래그)
cpuid -1 -l 7    # Leaf 7 (확장 기능)
cpuid -1 -l 4    # Leaf 4 (캐시 정보)

# /dev/cpu/N/cpuid 디바이스 파일 (CONFIG_X86_CPUID)
# 특정 CPU에 직접 CPUID 실행
# lseek offset = leaf | (subleaf << 32)
dd if=/dev/cpu/0/cpuid bs=16 count=1 skip=0 2>/dev/null | xxd

# C에서 직접 CPUID 호출 (유저 공간)
/* 유저 공간 CPUID 직접 호출 */
#include <cpuid.h>   /* GCC 내장 헤더 */

void check_features(void)
{
    unsigned int eax, ebx, ecx, edx;

    /* GCC __get_cpuid() 내장 함수 */
    __get_cpuid(1, &eax, &ebx, &ecx, &edx);

    if (ecx & (1 << 25)) printf("AES-NI supported\\n");
    if (ecx & (1 << 28)) printf("AVX supported\\n");

    /* Leaf 7 확인 */
    __get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx);
    if (ebx & (1 <<  5)) printf("AVX2 supported\\n");
    if (ebx & (1 << 16)) printf("AVX-512F supported\\n");

    /* 브랜드 문자열 (Leaf 0x80000002~4) */
    char brand[49] = {};
    __get_cpuid(0x80000002, (unsigned int*)&brand[0],
        (unsigned int*)&brand[4], (unsigned int*)&brand[8],
        (unsigned int*)&brand[12]);
    __get_cpuid(0x80000003, (unsigned int*)&brand[16],
        (unsigned int*)&brand[20], (unsigned int*)&brand[24],
        (unsigned int*)&brand[28]);
    __get_cpuid(0x80000004, (unsigned int*)&brand[32],
        (unsigned int*)&brand[36], (unsigned int*)&brand[40],
        (unsigned int*)&brand[44]);
    printf("CPU: %s\\n", brand);
}

Intel vs AMD CPUID 차이점

항목IntelAMD
벤더 문자열GenuineIntelAuthenticAMD
캐시 정보 (상세)Leaf 0x04 (Deterministic)Leaf 0x8000001D (유사 형식)
L1 캐시 (간략)Leaf 0x02 (디스크립터)Leaf 0x80000005 (직접 크기)
토폴로지Leaf 0x0B / 0x1FLeaf 0x8000001E (CU/Node)
가상화 기능VMX (Leaf 1 ECX bit 5)SVM (Leaf 0x8000000A)
전력 관리Leaf 0x06 (HWP)Leaf 0x80000007 (CPB, APM)
주소 크기Leaf 0x80000008Leaf 0x80000008 (동일)
Family 인코딩Family 6 (대부분)Family 0xF+Ext (예: 0x17=Zen, 0x19=Zen3/4, 0x1A=Zen5)
SGXLeaf 0x12 (지원)해당 없음 (SEV 사용)
Encrypted VMTME/MKTME (MSR 기반)SEV/SEV-ES/SEV-SNP (Leaf 0x8000001F)
💡

커널의 벤더 추상화: 커널은 struct cpu_dev 구조체를 통해 Intel/AMD/Hygon/Centaur/Zhaoxin 등 벤더별 CPUID 해석 차이를 추상화합니다. arch/x86/kernel/cpu/intel.carch/x86/kernel/cpu/amd.c에 각 벤더의 초기화 로직이 분리되어 있으며, 공통 피처 플래그는 x86_capability 배열에 통합 저장됩니다.

CPUID 직렬화(Serialization) 효과와 성능 영향

CPUID는 Intel SDM이 정의한 완전한 직렬화 명령어(Serializing Instruction)입니다. 직렬화 명령어는 실행 전 이전 모든 명령어가 완전히 완료될 때까지 파이프라인(Pipeline)을 멈추며, 이후 명령어의 프리페치·비순차 실행도 막습니다. 이 특성 때문에 CPUID는 성능 측정 경계 설정과 정밀 타이밍에 전통적으로 활용됩니다.

명령어직렬화 수준주요 용도비고
CPUID완전 직렬화 (Fully Serializing)정밀 타이밍 경계, 부팅 시 CPU 탐지권한 무관 실행 가능, 런타임 반복 비권장
MFENCE메모리 직렬화 (Store+Load)메모리 순서 보장(Ordering), lock-free 알고리즘파이프라인 비움 없음
LFENCE로드 직렬화 (Load)Spectre 완화, 로드 순서 강제AMD: 스펙실행 방지 효과 있음
IRET완전 직렬화인터럽트 핸들러(Handler) 복귀컨텍스트 전환 포함
WRMSR완전 직렬화 (일부 MSR)MSR 설정 후 즉시 효과 반영MSR 종류에 따라 다름
/* RDTSC 정밀 측정 — CPUID로 파이프라인 직렬화 후 타이밍 시작
 * 출처: Intel SDM Vol. 2B "RDTSC" 사용 지침
 */

/* 측정 시작: cpuid로 이전 명령 완전 완료 보장 */
xor     %eax, %eax
cpuid               /* 파이프라인 flush — 이전 명령 모두 완료 */
rdtsc               /* EAX=TSC[31:0], EDX=TSC[63:32] */
shl     $32, %rdx
or      %rax, %rdx
mov     %rdx, %r8   /* 시작 TSC 저장 */

/* ... 측정할 코드 실행 ... */

/* 측정 종료: rdtscp + lfence (아웃-오브-오더 방지) */
rdtscp              /* ECX=IA32_TSC_AUX (core ID), EAX/EDX=TSC */
shl     $32, %rdx
or      %rax, %rdx
sub     %r8, %rdx   /* 경과 사이클 수 */
lfence              /* 이후 명령이 TSC 읽기 전 실행되지 않도록 */
/* 커널이 CPUID를 부팅 시에만 호출하는 이유:
 * 직렬화로 인한 성능 페널티 — 현대 CPU에서 수백 클럭 사이클 소요
 * 런타임 성능 경로에서는 static_cpu_has() 또는 alternative() 사용 */

/* 잘못된 패턴: 런타임에 CPUID 반복 호출 (성능 저하) */
bool has_avx_bad(void) {
    unsigned int eax, ebx, ecx, edx;
    cpuid_count(7, 0, &eax, &ebx, &ecx, &edx);
    return ebx & (1 << 5);  /* 매번 CPUID 실행 → 수백 사이클 낭비 */
}

/* 올바른 패턴: 부팅 시 저장된 피처 비트 참조 (인라인 비트 검사) */
bool has_avx_good(void) {
    return cpu_feature_enabled(X86_FEATURE_AVX2);
}

RDTSCP vs RDTSC: 현대 커널 측정에서는 RDTSCP를 선호합니다. RDTSCP는 이전 로드 명령을 직렬화(load-serializing)하고 IA32_TSC_AUX에서 코어 ID도 함께 반환합니다. 단, RDTSCP는 완전한 직렬화가 아니므로 이후 명령 직렬화를 위해 LFENCE를 추가합니다. CPUID는 현대 측정에서는 오버헤드가 커 RDTSCP+LFENCE 조합으로 대체됩니다.

Leaf 0x0D: XSAVE 상태 컴포넌트 관리

Leaf 0x0D는 프로세서가 지원하는 XSAVE 상태 컴포넌트의 크기, 오프셋(Offset), 비트맵(Bitmap)을 질의합니다. 커널은 이 정보를 바탕으로 컨텍스트 전환 시 저장/복원할 FPU 상태 영역을 정확하게 할당합니다. CR4.OSXSAVE를 설정해 OS가 XSAVE를 지원함을 프로세서에 알려야만 AVX·AVX-512·AMX 등 확장 상태를 사용할 수 있습니다.

XSTATE 컴포넌트 비트맵 (XCR0 / Leaf 0x0D EAX) bit 0 x87 FPU 512B bit 1 SSE/XMM 256B bit 2 AVX/YMM 256B bit 3~4 MPX (deprecated) 64B+64B bit 5 / 6 / 7 AVX-512 (opmask/ZMM_Hi/Hi16) 64B / 512B / 1024B bit 9 PKRU 4B bit 17~18 AMX (XTILECFG/XTILEDATA) 64B / 8192B CR4.OSXSAVE 설정 흐름 ① CPUID(0x01) ECX bit 26=1 (XSAVE 지원 확인) → ② CR4.OSXSAVE(bit 18) 설정 → OS가 XSAVE 사용 선언 ③ XCR0에 원하는 컴포넌트 비트 설정 (XSETBV) → ④ XSAVE/XRSTOR로 선택된 컴포넌트만 저장/복원 Sub-leaf 0 (ECX=0) EAX 지원 XSAVE 컴포넌트 비트맵 [31:0] EBX 현재 XCR0 기준 XSAVE 영역 크기 (바이트) ECX 지원 가능 최대 XSAVE 영역 크기 EDX 지원 XSAVE 컴포넌트 비트맵 [63:32] Sub-leaf N (N≥2): 컴포넌트 N의 크기+오프셋 EAX=크기, EBX=영역 내 오프셋, ECX[0]=1이면 항상 저장 ECX[1]=1이면 XFD(eXtended Feature Disable)로 지연 저장 가능 Sub-leaf 1 (ECX=1) — XSAVE 기능 플래그 EAX bit 0 XSAVEOPT 지원 (변경된 컴포넌트만 저장) EAX bit 1 XSAVEC 지원 (압축 포맷) EAX bit 2 XGETBV ECX=1 지원 EAX bit 3 XSAVES/XRSTORS (supervisor 상태 저장) EAX bit 4 XFD 지원 (지연 컨텍스트 전환, AMX 최적화) EBX: XSAVES 저장 영역 크기 (supervisor 상태 포함)
/* arch/x86/kernel/fpu/xstate.c — XSAVE 상태 초기화 */
void fpu__init_system_xstate(unsigned int legacy_size)
{
    u32 eax, ebx, ecx, edx;

    /* Leaf 0x0D Sub-leaf 0: 전체 지원 컴포넌트 비트맵 */
    cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
    xfeatures_mask_all = eax + ((u64)edx << 32);

    /* EBX: 현재 XCR0 기준 크기, ECX: 최대 크기 */
    fpu_kernel_cfg.max_size  = ecx;
    fpu_kernel_cfg.used_size = ebx;

    /* Sub-leaf 1: XSAVE 기능 플래그 (XSAVEOPT, XSAVEC 등) */
    cpuid_count(0xd, 1, &eax, &ebx, &ecx, &edx);
    if (eax & (1 << 1))
        setup_force_cpu_cap(X86_FEATURE_XSAVEC);
    if (eax & (1 << 3))
        setup_force_cpu_cap(X86_FEATURE_XSAVES);
    if (eax & (1 << 4))
        setup_force_cpu_cap(X86_FEATURE_XFD);  /* AMX 지연 저장 */

    /* Sub-leaf N (N≥2): 각 컴포넌트의 크기와 XSAVE 영역 내 오프셋 */
    for (int i = 2; i < XFEATURE_MAX; i++) {
        cpuid_count(0xd, i, &eax, &ebx, &ecx, &edx);
        xstate_sizes[i]   = eax;   /* 컴포넌트 크기 (바이트) */
        xstate_offsets[i] = ebx;   /* XSAVE 영역 내 오프셋 */
    }
}

Leaf 0x0A: 성능 모니터링 유닛(PMU) 탐지

Leaf 0x0A는 Intel Performance Monitoring Unit(PMU)의 버전과 카운터 구성을 보고합니다. perf 서브시스템은 이 정보를 사용해 사용 가능한 하드웨어 카운터 수와 비트 폭을 결정합니다. AMD는 다른 메커니즘(Leaf 0x80000022 EPMC)을 사용하므로 Leaf 0x0A는 사실상 Intel 전용입니다.

/* arch/x86/kernel/cpu/perf_event_intel.c — Intel PMU 탐지 */
void x86_pmu_show_pmu_cap(struct x86_pmu *pmu)
{
    unsigned int eax, ebx, ecx, edx;

    cpuid(0x0a, &eax, &ebx, &ecx, &edx);

    pmu->version       = eax & 0xff;          /* EAX[7:0]: PMU 버전 (0이면 미지원) */
    pmu->num_counters  = (eax >> 8) & 0xff;   /* EAX[15:8]: 범용 카운터 수 */
    pmu->counter_bits  = (eax >> 16) & 0xff;  /* EAX[23:16]: 카운터 비트 폭 */

    /* EBX: 사용 불가 이벤트 비트맵 (bit=1이면 해당 이벤트 미지원) */
    /* bit 0=명령 실행, 1=비중단 사이클, 2=참조 사이클, 3=LLC참조,
     * 4=LLC미스, 5=분기 실행, 6=분기 미스 예측 */
    pmu->unavailable   = ebx & 0xff;

    /* EDX: 고정 카운터 정보 */
    pmu->num_counters_fixed  = edx & 0x1f;      /* EDX[4:0]: 고정 카운터 수 */
    pmu->fixed_counter_bits  = (edx >> 5) & 0xff; /* EDX[12:5]: 고정 카운터 비트 폭 */

    pr_info("PMU: version %u, %u counters (%u bits), %u fixed\n",
            pmu->version, pmu->num_counters,
            pmu->counter_bits, pmu->num_counters_fixed);
}
Intel PMU 버전별 지원 기능 (Leaf 0x0A EAX[7:0])
PMU 버전도입 마이크로아키텍처주요 기능
v1Pentium M, Core Solo/Duo범용 카운터 2개, 기본 이벤트 (cycles, instructions, cache-misses, branch-misses)
v2Core 2 (Merom, 2006)3개 고정 카운터 추가 (INST_RETIRED, CPU_CLK_UNHALTED.CORE, REF), PEBS 지원
v3Nehalem (2008)범용 카운터 4개, Off-core Response 이벤트, 멀티스레드 카운터 지원
v4Skylake (2015)인터럽트 억제 개선, LBR(Last Branch Record) 통합, 필터링 기능 강화
v5Sapphire Rapids (2023)Architectural LBR (XSAVE 기반, 최대 32개), PEBS 지연 개선, 정밀 이벤트

Leaf 0x15/0x16: TSC·코어 주파수 탐지

현대 Linux 커널은 Leaf 0x15Leaf 0x16을 통해 HPETPIT를 사용하지 않고 TSC 주파수를 정확하게 계산합니다. Leaf 0x15는 Core Crystal Clock과 TSC의 비율을, Leaf 0x16은 코어·최대·버스 주파수를 MHz 단위로 직접 반환합니다. 두 Leaf가 모두 사용 불가한 경우 커널은 PIT 기반 보정으로 폴백합니다.

/* arch/x86/kernel/tsc.c — TSC 주파수 탐지 (두 Leaf 조합) */
unsigned int native_calibrate_tsc(void)
{
    unsigned int eax_denominator, ebx_numerator, ecx_crystal_hz, edx;

    /* Leaf 0x15: Crystal Clock ↔ TSC 비율
     * TSC_Hz = Crystal_Hz × EBX / EAX */
    cpuid(0x15, &eax_denominator, &ebx_numerator, &ecx_crystal_hz, &edx);

    if (eax_denominator && ebx_numerator && ecx_crystal_hz) {
        return (ecx_crystal_hz / 1000) *
               ebx_numerator / eax_denominator;  /* kHz 단위 반환 */
    }

    /* ECX=0인 플랫폼: Leaf 0x16으로 코어 주파수 직접 참조 */
    unsigned int eax_base_mhz, ebx_max_mhz, ecx_bus_mhz;
    cpuid(0x16, &eax_base_mhz, &ebx_max_mhz, &ecx_bus_mhz, &edx);

    if (eax_base_mhz)
        return eax_base_mhz * 1000;  /* MHz → kHz 변환 */

    return 0;  /* 실패 시 PIT/HPET 기반 보정 폴백 */
}
마이크로아키텍처별 Core Crystal Clock 주파수 (Leaf 0x15 ECX)
마이크로아키텍처코드명Crystal Clock (ECX)비고
Skylake, Kaby LakeSKL, KBL24 MHzECX=24,000,000
Ice Lake, Tiger LakeICL, TGL19.2 MHzECX=19,200,000
Alder Lake, Raptor LakeADL, RPL38.4 MHzECX=38,400,000
Atom Goldmont, TremontGLM, TRM19.2 / 25 MHz모델에 따라 다름
Sapphire RapidsSPR25 MHzECX=25,000,000
AMD (대부분)-ECX=0 (미지원)Leaf 0x16 EAX 사용

Invariant TSC: Leaf 0x80000007 EDX bit 8이 설정된 CPU는 전력 절약 상태(C-state)와 무관하게 TSC가 일정한 속도로 동작합니다. Linux는 이 비트를 확인해 TSC를 클럭소스로 사용할지 결정합니다. /proc/cpuinfoconstant_tsc·nonstop_tsc·tsc_reliable 플래그로 확인할 수 있습니다.

마이크로코드 업데이트와 CPUID 변화

마이크로코드 업데이트는 CPU의 내부 동작을 수정하며, 특히 보안 취약점 완화를 위한 새로운 CPUID 피처 비트를 활성화합니다. Spectre·Meltdown 공개(2018) 이후, 많은 시스템에서 마이크로코드 로드 전후로 CPUID 결과가 달라집니다. Linux 커널은 early microcode 로드 후 CPUID를 재질의해 새 피처를 반영합니다.

/* arch/x86/kernel/cpu/microcode/core.c — 마이크로코드 로드 흐름 */

/* 1단계: early_initcall에서 microcode 로드 */
static int __init microcode_init(void)
{
    microcode_ops = init_intel_microcode();  /* 또는 init_amd_microcode() */
    return microcode_ops->apply_microcode(0);
}

/* 2단계: 마이크로코드 적용 후 피처 비트 재스캔 및 변경 사항 보고 */
void microcode_check(struct cpuinfo_x86 *prev_info)
{
    struct cpuinfo_x86 info;
    get_cpu_cap(&info);   /* CPUID 재질의 */

    for (int i = 0; i < NCAPINTS; i++) {
        u32 changed = info.x86_capability[i] ^ prev_info->x86_capability[i];
        if (changed)
            pr_info("x86/CPU: Word %d: bits %#x changed\n", i, changed);
    }
}

/* 3단계: 보안 완화 재적용 (마이크로코드로 활성화된 MSR 비트 사용) */
void spectre_v2_select_mitigation(void)
{
    /* IBRS: Leaf 7 EDX bit 26 — 마이크로코드 업데이트 후 나타남 */
    if (boot_cpu_has(X86_FEATURE_IBRS))
        wrmsrl(MSR_IA32_SPEC_CTRL, SPEC_CTRL_IBRS);

    /* STIBP: Leaf 7 EDX bit 27 — 형제 스레드 간 분기 예측 격리 */
    if (boot_cpu_has(X86_FEATURE_STIBP))
        wrmsrl(MSR_IA32_SPEC_CTRL, SPEC_CTRL_STIBP);
}

Early vs Late 마이크로코드 로드: initramfs의 마이크로코드 이미지(/boot/intel-ucode.img)는 GRUB가 커널보다 먼저 메모리에 올려 early boot 단계에서 적용됩니다. 늦은 마이크로코드 로드(/dev/cpu/microcode 쓰기)는 이미 수행된 CPUID 기반 초기화를 재실행하지 않으므로, 보안 완화 픽스에는 반드시 early 로드가 필요합니다. AMD는 /lib/firmware/amd-ucode/ 경로를 사용합니다.

CPU 기능 마스킹과 cmdline 제어

Linux 커널은 CPUID로 탐지된 피처라도 커맨드라인 옵션이나 에라타 워크어라운드로 강제로 비활성화하거나 활성화할 수 있습니다. setup_clear_cpu_cap()은 이미 설정된 피처 비트를 제거하며, setup_force_cpu_cap()은 CPUID에 없어도 비트를 강제 설정합니다. 이 메커니즘은 x86_capabilitycpu_caps_cleared/set 비트맵 배열을 통해 동작합니다.

/* arch/x86/kernel/cpu/common.c — 피처 비트 마스킹 메커니즘 */

/* 피처 강제 제거: boot_cpu_data와 이후 초기화되는 모든 CPU에 적용 */
void setup_clear_cpu_cap(unsigned int bit)
{
    clear_cpu_cap(&boot_cpu_data, bit);
    set_bit(bit, cpu_caps_cleared);   /* SMP: 이후 부팅하는 CPU도 동일하게 클리어 */
}

/* cmdline 파싱 예: "noibrs" 옵션 처리 */
static int __init noibrs_setup(char *str)
{
    setup_clear_cpu_cap(X86_FEATURE_IBRS);
    setup_clear_cpu_cap(X86_FEATURE_IBRS_ENHANCED);
    return 1;
}
__setup("noibrs", noibrs_setup);

/* 에라타 마스킹: 특정 Family/Model에서 버그있는 피처 비트 강제 제거 */
static void intel_workarounds(struct cpuinfo_x86 *c)
{
    /* Kaby Lake/Coffee Lake 일부 스테핑: TSX 버그 → 강제 비활성화 */
    if (c->x86 == 6 && c->x86_model == 0x8e &&
        c->x86_stepping < 0xb) {
        setup_clear_cpu_cap(X86_FEATURE_HLE);
        setup_clear_cpu_cap(X86_FEATURE_RTM);
    }
}
주요 CPU 피처 마스킹 cmdline 옵션
cmdline 옵션효과관련 X86_FEATURE_*
noibrsIBRS 비활성화 (Spectre-v2 완화 비적용)X86_FEATURE_IBRS
noibpb간접 분기 예측기 배리어 비활성화X86_FEATURE_IBPB
nox2apicx2APIC 비활성화 (레거시 xAPIC 강제)X86_FEATURE_X2APIC
noptiKernel Page Table Isolation 비활성화 (Meltdown 완화 해제)X86_FEATURE_PTI
mitigations=off모든 CPU 취약점 완화 비활성화 (성능 우선)IBRS, STIBP, SSBD, L1TF 등 다수
tsx=offTSX (HLE/RTM) 강제 비활성화 (TAA/TSX-async-abort)X86_FEATURE_HLE, X86_FEATURE_RTM
nosmtSMT(하이퍼스레딩) 비활성화논리적 CPU 오프라인 처리
spectre_v2=offSpectre-v2 완화 전체 비활성화X86_FEATURE_IBRS, X86_FEATURE_IBPB

CPUID Faulting: 유저 공간 CPUID 제한

CPUID Faulting은 Intel이 Ivy Bridge(3세대 Core)에서 도입한 보안 기능으로, 비특권 유저 공간에서 CPUID 실행 시 #GP(0) 예외를 발생시킵니다. 커널이 이 예외를 처리해 CPUID 결과를 에뮬레이션하거나 제한할 수 있으며, 이를 통해 컨테이너(Container)나 가상 환경에서 CPU 마이크로아키텍처 정보 노출을 방지합니다.

/* arch/x86/kernel/process.c — CPUID Faulting 설정 */

/* MSR_MISC_FEATURES_ENABLES(0x140) bit 0 = CPUID_FAULT */
void enable_cpuid_fault(void)
{
    if (!boot_cpu_has(X86_FEATURE_CPUID_FAULT))
        return;
    wrmsrl(MSR_MISC_FEATURES_ENABLES, MISC_FEATURES_CPUID_FAULT);
    pr_info("x86/CPU: CPUID Faulting enabled\n");
}

/* do_general_protection() 핸들러에서 CPUID 에뮬레이션 처리 */
static bool emulate_cpuid(struct pt_regs *regs)
{
    unsigned int eax = regs->ax, ecx = regs->cx;
    unsigned int r_eax, r_ebx, r_ecx, r_edx;

    /* 커널이 CPUID 결과를 직접 계산해 유저 레지스터에 삽입 */
    cpuid_count(eax, ecx, &r_eax, &r_ebx, &r_ecx, &r_edx);

    /* 민감한 정보 마스킹: 하이퍼바이저 leaf는 숨김 */
    if (eax >= 0x40000000)
        r_eax = r_ebx = r_ecx = r_edx = 0;

    regs->ax = r_eax;  regs->bx = r_ebx;
    regs->cx = r_ecx;  regs->dx = r_edx;
    regs->ip += 2;  /* CPUID = 2바이트 명령어 (0x0F 0xA2) */
    return true;
}
사용자 모드 명령어 제한 비교: CPUID Faulting vs CR4.UMIP
항목CPUID FaultingCR4.UMIP (User-Mode Instruction Prevention)
대상 명령어CPUIDSGDT, SLDT, SIDT, STR, SMSW
활성화 방법MSR_MISC_FEATURES_ENABLES bit 0CR4 bit 11
예외 발생#GP(0) → 커널 에뮬레이션 가능#GP(0) → SIGSEGV 전달
보안 목적CPU 마이크로아키텍처 정보 노출 방지GDT/LDT/IDT 주소 노출 방지 (KASLR 보완)
커널 상수X86_FEATURE_CPUID_FAULTX86_FEATURE_UMIP
도입Ivy Bridge (3세대 Core, 2012)Goldmont (Atom), Cannon Lake+ (2018)

컨테이너 보안 활용: CPUID Faulting은 컨테이너가 호스트 CPU의 정확한 모델·스테핑 정보를 획득하지 못하게 합니다. KVM은 게스트에게 가상화된 CPUID 결과를 에뮬레이션하는 방식으로 유사한 효과를 달성합니다. seccompprctl(PR_SET_TSC, PR_TSC_SIGSEGV)를 조합하면 RDTSC도 제한할 수 있습니다.

AMD 전용 확장 Leaf (SME/SEV/QoS)

AMD는 0x8000001F를 통해 Secure Memory Encryption(SME)Secure Encrypted Virtualization(SEV) 지원 여부를 보고합니다. 이 Leaf는 메모리 암호화(Encryption) C-bit 위치와 물리 주소(Physical Address) 크기 감소량도 반환합니다. Linux 커널의 arch/x86/mm/mem_encrypt_amd.c는 이를 사용해 암호화 마스크를 초기화합니다.

/* arch/x86/mm/mem_encrypt_amd.c — SME/SEV 탐지 및 초기화 */
void amd_get_encrypted_memory_parameters(void)
{
    unsigned int eax, ebx, ecx, edx;

    /* Leaf 0x8000001F: AMD 메모리 암호화 기능 */
    cpuid(0x8000001f, &eax, &ebx, &ecx, &edx);

    if (!(eax & 1))   /* EAX bit 0: SME 지원 여부 */
        return;

    /* EBX[5:0]: C-bit 위치 (물리 주소에서 암호화 비트가 위치하는 비트 번호) */
    sme_me_mask = 1ULL << (ebx & 0x3f);

    /* EBX[11:6]: C-bit 포함으로 인한 물리 주소 비트 수 감소량 */
    unsigned int phys_bits_reduced = (ebx >> 6) & 0x3f;

    pr_info("AMD SME: C-bit at physical address bit %llu "
            "(phys_bits reduced by %u)\n",
            __ffs(sme_me_mask), phys_bits_reduced);

    if (eax & (1 << 1))   /* EAX bit 1: SEV 지원 */
        setup_force_cpu_cap(X86_FEATURE_SEV);

    /* ECX: 동시 실행 가능한 암호화 게스트(ASID) 수 */
    /* EDX: 최소 ASID (최대 동시 SEV 게스트 = ASID_MAX - EDX + 1) */
}
AMD Leaf 0x8000001F EAX 비트 (SME/SEV 기능 플래그)
비트이름설명
0SMESecure Memory Encryption — DRAM 전체 투명 암호화 (AES-128, C-bit 설정 시 활성)
1SEVSecure Encrypted Virtualization — 게스트 메모리 독립 암호화
2PAGE_FLUSH_MSRMSR_AMD64_SME_PA로 물리 페이지 TLB 플러시
3SEV-ESSEV Encrypted State — 게스트 레지스터 상태도 암호화 (VMSA 보호)
4SEV-SNPSEV Secure Nested Paging — 메모리 소유권 검증으로 무결성(Integrity) 보호 추가
5VMPLVM Permission Levels — 게스트 내 권한 계층 4단계 (VMPL0~3)
10SEV-TIOTrusted I/O Virtualization 지원
28VMSA_REGPROTVMSA 레지스터 보호 강화 (SEV-ES 보완)

AMD Leaf 0x80000020은 Platform QoS(L3 캐시 할당, 메모리 대역폭(Bandwidth) 할당)를, 0x80000022는 Extended Performance Monitoring(EPMC)을 보고합니다. EPMC는 AMD Zen 4부터 도입되어 Linux perf가 Intel 수준의 하드웨어 카운터 접근을 AMD에서도 활용할 수 있게 합니다.

ARM64 ID 레지스터와 비교

ARM64(AArch64) 아키텍처는 x86의 CPUID 명령어 대신 시스템 레지스터를 사용해 CPU 기능을 보고합니다. MRS 명령어로 읽는 ID_AA64*_EL1 레지스터들이 x86의 CPUID Leaf에 해당하며, 커널의 arch/arm64/kernel/cpufeature.c가 이를 추상화합니다. ARM64는 각 레지스터의 4비트 필드 값이 클수록 더 새로운 기능 버전을 의미합니다.

/* arch/arm64/kernel/cpufeature.c — ARM64 CPU 기능 탐지 패턴 */

/* x86: boot_cpu_has(X86_FEATURE_AES) 에 대응 */
static bool has_aes(const struct arm64_cpu_capabilities *cap, int scope)
{
    /* ARM64: MRS x0, ID_AA64ISAR0_EL1 로 읽는 값 */
    u64 val = read_sanitised_ftr_reg(SYS_ID_AA64ISAR0_EL1);
    return cpuid_feature_extract_unsigned_field(
               val, ID_AA64ISAR0_EL1_AES_SHIFT) >= 1;
}

/* x86: boot_cpu_has(X86_FEATURE_LSE_ATOMICS) 에 대응 */
static bool has_lse_atomics(const struct arm64_cpu_capabilities *cap, int scope)
{
    u64 val = read_sanitised_ftr_reg(SYS_ID_AA64ISAR0_EL1);
    return cpuid_feature_extract_unsigned_field(
               val, ID_AA64ISAR0_EL1_ATOMIC_SHIFT) >= 2;
}

/* x86: boot_cpu_has(X86_FEATURE_HYPERVISOR) 에 대응 */
static bool has_el2_virtualization(const struct arm64_cpu_capabilities *cap, int scope)
{
    /* EL2(하이퍼바이저) 지원: ID_AA64PFR0_EL1 EL2 필드 */
    u64 val = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
    return cpuid_feature_extract_unsigned_field(
               val, ID_AA64PFR0_EL1_EL2_SHIFT) > 0;
}
x86 CPUID Leaf ↔ ARM64 시스템 레지스터 대응표
기능 범주x86 CPUID LeafARM64 ID 레지스터읽기 명령
암호화 가속Leaf 1 ECX (AES-NI), Leaf 7 EBX (SHA)ID_AA64ISAR0_EL1MRS x0, ID_AA64ISAR0_EL1
메모리 모델·페이지Leaf 1 EDX (PSE), 0x80000001 EDX (1GB)ID_AA64MMFR0_EL1MRS x0, ID_AA64MMFR0_EL1
프로세서 기능·ELLeaf 1 ECX (VMX), Leaf 7 ECX (UMIP)ID_AA64PFR0_EL1MRS x0, ID_AA64PFR0_EL1
디버그·추적Leaf 0x14 (Intel PT)ID_AA64DFR0_EL1MRS x0, ID_AA64DFR0_EL1
SIMD/벡터Leaf 1 ECX (AVX), Leaf 7 (AVX-512)ID_AA64PFR0_EL1, SVE: ID_AA64ZFR0_EL1MRS x0, ID_AA64ZFR0_EL1
원자 연산Leaf 1 ECX (CX16)ID_AA64ISAR0_EL1 ATOMIC 필드MRS x0, ID_AA64ISAR0_EL1
물리 주소 크기Leaf 0x80000008 EAX[7:0]ID_AA64MMFR0_EL1 PARange 필드MRS x0, ID_AA64MMFR0_EL1
가상화Leaf 1 ECX bit 5 (VMX)ID_AA64PFR0_EL1 EL2 필드MRS x0, ID_AA64PFR0_EL1

ARM64의 피처 탐지 철학: ARM64는 CPU ID 레지스터를 4비트 필드(field) 단위로 정의합니다. 값이 클수록 더 새로운 기능 버전을 의미합니다 (예: ATOMIC≥2 → LSE 지원). x86의 단일 피처 비트와 달리 "이 기능의 버전 N 이상"처럼 질의합니다. 또한 ARM64는 모든 코어가 동일한 ID 레지스터 값을 가져야 하므로 x86의 코어별 CPUID 불일치 문제가 없습니다.

ARM64 CPU 식별

ARM64(AArch64) 아키텍처는 x86의 CPUID 명령어 대신 시스템 레지스터(System Registers)를 통해 CPU 기능을 식별합니다. Linux 커널은 부팅 시 이 레지스터들을 읽어 boot_cpu_data에 해당하는 boot_cpu_feature 구조체를 초기화하고, arm64_cpu_capabilities 배열을 통해 기능 탐지와 커널 최적화 경로를 결정합니다.

MIDR_EL1 비트 필드 분해 및 ID_AA64* 레지스터 패밀리 MIDR_EL1 (Main ID Register, EL1) — 32비트 읽기 전용 [31:24] Implementer 제조사 코드 (JEDEC) 0x41=ARM, 0x51=Qualcomm [23:20] Variant 주 리비전 번호 Major Revision [19:16] Architecture 0xF = Arch 정의 레지스터 사용(ID_AA64* 활용) [15:4] PartNum 파트 번호 (코어 종류) 0xD0C=Cortex-A76 등 [3:0] Revision 부 리비전 (패치 수준) Minor Revision (errata) REVIDR_EL1: 에라타 식별 → is_affected_midr_range() 로 cpu_errata 매칭 CTR_EL0: 캐시 타입 레지스터 (EL0 접근 허용, userspace가 직접 읽기 가능) HWCAP_CPUID: ID_AA64* 레지스터 EL0 MRS 트랩 → 커널이 새니타이즈된 값 반환 ID_AA64* 레지스터 패밀리 — cpufeature.c 탐지 흐름 setup_cpu_features() ISA 기능 ID_AA64ISAR0~2 AES, SHA, CRC, RDMA SVE2, SME, PAC, BTI 메모리 모델 ID_AA64MMFR0~4 PA범위, TGran, LPA2 HAFDBS, HPDS, VHE 프로세서 피처 ID_AA64PFR0~2 EL0~EL3 지원 수준 SVE, RAS, SEL2, MTE 디버그 피처 ID_AA64DFR0~1 PMU 버전, Watchpoint BRP, WRP 개수 SVE/SME ID_AA64ZFR0 ID_AA64SMFR0 벡터/행렬 연산 arm64_ftr_reg: 각 ID 레지스터의 필드별 안전 값(safe_val) 정의 → CPU 핫플러그 시 최솟값으로 수렴 arm64_cpu_capabilities: 각 기능 탐지 콜백(matches) + enable 콜백 → system_supports_*() 헬퍼 HWCAP_CPUID (HWCAP2): EL0에서 MRS 시도 시 EL1 트랩 → 커널이 새니타이즈된 ID 레지스터 값 반환 cpu_errata: REVIDR_EL1 + MIDR 범위로 에라타 적용 → is_affected_midr_range() 콜백 CTR_EL0: I/D 캐시 라인 크기·정책 → userspace 직접 읽기 허용 (EL0 트랩 없음) arch/arm64/kernel/cpufeature.c & cpu-errata.c — 핵심 구현 위치

MIDR_EL1 Implementer 코드 및 PartNum 예시

Implementer 코드제조사PartNum 예시코어 예시
0x41ARM Ltd.0xD0C, 0xD40, 0xD44, 0xD4FCortex-A76, Neoverse V1, V2, N2
0x51Qualcomm0x801, 0x802, 0x803, 0x804Kryo 2xx Gold/Silver, Kryo 3xx
0x61Apple0x022, 0x023, 0x024, 0x030Firestorm (M1), Icestorm, Everest (M2)
0x4ENVIDIA0x004, 0x003Carmel (Tegra Xavier), Denver
0xC0Ampere0xAC3AmpereOne (eMAG 계열)
0x48HiSilicon0xD01, 0xD02TaiShan v110, TaiShan v120
0x46Fujitsu0x001A64FX (Post-K)
0x56Marvell0x581ThunderX2

ID_AA64* 레지스터 전체 목록과 커버 기능

레지스터주요 커버 기능주요 필드 예시
ID_AA64ISAR0_EL1암호화·원자성 ISAAES, SHA1, SHA2, CRC32, Atomic (LSE), RDM, SHA3, SM3, SM4, DP, FHM
ID_AA64ISAR1_EL1포인터 인증·분기·벡터DPB, JSCVT, FCMA, LRCPC, GPA/GPI (PAC), FRINTTS, SB, SPECRES, BF16, I8MM
ID_AA64ISAR2_EL1최신 확장 ISAWFXT, RPRES, PAC3, BC (HBC), MOPS (memcpy/memset ISA), CSSC, RPRFM
ID_AA64MMFR0_EL1물리 주소 범위·TGPARange (32~52bit), TGran4/16/64, SNSMem, BigEnd, ASIDBits, ExS
ID_AA64MMFR1_EL1MMU 고급 기능HAFDBS, VMIDBits, VHE, HPDS, LO, PAN, SpecSEI, XNX, TWED, ETS, HCX, AFP
ID_AA64MMFR2_EL1ARMv8.2+ MMU 확장CnP, UAO, LSM, IESB, VARange (LVA), CCIDX, NV, ST, AT, FWB, IDS, TTL, BBM, EVT, E0PD
ID_AA64MMFR3_EL1ARMv8.9+ MMUTCRX, SCTLRX, S1PIE, S1POE, S2POE, AIE, MEC, D128, D128_2, SNERR, ANERR
ID_AA64MMFR4_EL1최신 MMU 확장EIESB, ASID2, HACDBS, FGWTE3, NV_frac, E2H0
ID_AA64PFR0_EL1EL 지원 수준·SVEEL0~EL3 (AArch32/64), FP, AdvSIMD, GIC, RAS, SEL2, SVE, DIT, MPAM, AMU, CSV2, CSV3
ID_AA64PFR1_EL1MTE·SME·RAS 확장BT (BTI), SSBS, MTE, RAS_frac, MPAM_frac, SME, RNDR_trap, CSV2_frac, NMI, MTE_frac, GCS, THE, DF2, PFAR
ID_AA64PFR2_EL1ARMv9.4+ 피처MTEFAR, MTESTOREONLY, MTEPERM, UINJ
ID_AA64DFR0_EL1디버그·PMUDebugVer, TraceVer, PMUVer, BRPs, WRPs, CTX_CMPs, PMSVer, DoubleLock, TraceFilt
ID_AA64DFR1_EL1디버그 확장EBEP, ITE, ABLE, PMICNTR, SPMU, CTX_CMPs_2, BRPs_2, WRPs_2
ID_AA64ZFR0_EL1SVE 기능 (PFR0.SVE=1 시 유효)SVEver, AES, BitPerm, BF16, B16B16, SHA3, SM4, I8MM, F32MM, F64MM
ID_AA64SMFR0_EL1SME 기능 (PFR1.SME=1 시 유효)F32F32, BI32I32, B16F32, F16F32, I8I32, F16F16, I16I32, FA64, LUTv2, SMEver, I16I64, SF8FMA, SF8DP4, SF8DP2

커널 구현: arm64_ftr_reg와 arm64_cpu_capabilities

/* arch/arm64/kernel/cpufeature.c
 * arm64_ftr_reg: ID 레지스터의 필드별 안전 값(최솟값) 관리
 */
struct arm64_ftr_reg {
    const char          *name;
    u64                  strict_mask;  /* 이 비트는 CPU 간 반드시 일치해야 함 */
    u64                  user_mask;    /* userspace에 노출할 비트 마스크 */
    u64                  sys_val;      /* 시스템 안전 값(최솟값으로 수렴) */
    u64                  user_val;     /* userspace 노출 값(새니타이즈) */
    const struct arm64_ftr_bits *ftr_bits; /* 필드별 정의 배열 */
};

/* arm64_cpu_capabilities: 기능 탐지 + enable 콜백 */
struct arm64_cpu_capabilities {
    const char   *desc;
    u16           capability;          /* ARM64_HAS_* 상수 */
    u16           type;                /* SCOPE_SYSTEM / LOCAL_CPU / BOOT_CPU */
    bool         (*matches)(const struct arm64_cpu_capabilities *,
                           int scope);
    void         (*cpu_enable)(const struct arm64_cpu_capabilities *);
    union {
        struct {    /* ID 레지스터 기반 탐지 */
            u32 sys_reg;
            u8  field_pos;
            u8  field_width;
            u8  min_field_value;
            u8  hwcap_type;
            unsigned long hwcap;
        };
        const struct midr_range *midr_range_list; /* MIDR 범위 기반 */
    };
};

/* 사용 예: LSE(Large System Extension) 원자성 명령어 탐지 */
static const struct arm64_cpu_capabilities arm64_features[] = {
    {
        .desc       = "LSE atomic instructions",
        .capability = ARM64_HAS_LSE_ATOMICS,
        .type       = ARM64_CPUCAP_SYSTEM_FEATURE,
        .matches    = has_cpuid_feature,
        .sys_reg    = SYS_ID_AA64ISAR0_EL1,
        .field_pos  = ID_AA64ISAR0_EL1_ATOMIC_SHIFT,
        .min_field_value = 2,
        .hwcap      = HWCAP_ATOMICS,
    },
    /* ... BTI, MTE, SVE, PAC, SSBS, ... */
};

cpu_errata와 MIDR 범위 매칭

/* arch/arm64/kernel/cpu-errata.c
 * is_affected_midr_range(): MIDR_EL1 + REVIDR_EL1 로 에라타 해당 CPU 판별
 */
static bool is_affected_midr_range(const struct arm64_cpu_capabilities *entry,
                                    int scope)
{
    const struct arm64_midr_revidr *fix;
    const struct midr_range *range;
    u32 midr = read_cpuid_id();  /* MIDR_EL1 읽기 */
    u64 revidr;

    if (!is_midr_in_range_list(midr, entry->midr_range_list))
        return false;

    /* REVIDR_EL1: 특정 리비전은 이미 수정됨 — 제외 처리 */
    revidr = read_sysreg(revidr_el1);
    for (fix = entry->fixed_revs; fix && fix->revidr_mask; fix++) {
        if (is_midr_in_range(midr, &fix->range) &&
            (revidr & fix->revidr_mask))
            return false;  /* 수정된 리비전 → 에라타 불필요 */
    }
    return true;
}

/* HWCAP_CPUID: EL0에서 ID 레지스터 MRS 시 EL1 트랩 처리
 * arch/arm64/kernel/traps.c — do_sysreg_emulation()
 * 커널이 user_val(새니타이즈된 값) 반환하여 userspace에 안전하게 노출 */
static int emulate_id_reg(struct pt_regs *regs, u32 sys_reg)
{
    const struct arm64_ftr_reg *regp = get_arm64_ftr_reg(sys_reg);
    if (!regp)
        return -EINVAL;
    pt_regs_write_reg(regs, sys_reg_get_rt(sys_reg), regp->user_val);
    return 0;
}
CTR_EL0 — userspace 직접 접근 가능 레지스터: Cache Type Register(CTR_EL0)는 ARM64에서 예외적으로 EL0(사용자 공간)에서 MRS로 직접 읽을 수 있는 시스템 레지스터입니다. I-캐시 정책(VIPT/PIPT), DIC(D-side inner cacheability), IDC(instruction cache DMA coherency), 캐시 라인 크기 등을 담고 있어 malloc·memcpy 구현에서 자주 활용합니다. glibc는 이를 통해 캐시 라인 정렬 최적화를 수행합니다.
ARM64 피처 탐지 vs x86 CPUID 차이: x86은 단일 CPUID 명령어로 모든 기능을 쿼리하지만, ARM64는 수십 개의 개별 시스템 레지스터를 읽어야 합니다. 또한 ARM64는 멀티 CPU 환경에서 CPU마다 다른 마이크로아키텍처가 혼재할 수 있으므로(big.LITTLE, DynamIQ) arm64_ftr_reg.sys_val이 모든 CPU의 최솟값(안전 값)으로 수렴합니다. 따라서 system_supports_lse_atomics()처럼 시스템 전체 지원 여부를 묻는 헬퍼를 항상 사용해야 합니다.
HWCAP_CPUID 노출 정책: Linux 5.8부터 /proc/cpuinfoFeatures 필드와 AT_HWCAP·AT_HWCAP2를 통해 EL0에 노출되는 ARM64 기능 비트는 커널이 새니타이즈한 값입니다. 가상화 환경에서 하이퍼바이저가 ID 레지스터를 마스킹할 수 있으므로, 사용자 프로그램은 getauxval(AT_HWCAP)을 사용해야 하며 MRS로 직접 읽은 값이 더 많은 기능을 보여주더라도 신뢰해서는 안 됩니다.

RISC-V CPU 식별

RISC-V 아키텍처는 CPU 식별 방식이 x86, ARM64와 근본적으로 다릅니다. CPU 식별 정보는 M-mode(Machine mode) CSR(Control and Status Register)에 있지만, Linux는 S-mode(Supervisor mode)에서 동작하므로 이 레지스터에 직접 접근할 수 없습니다. 대신 Device Tree, SBI(Supervisor Binary Interface), 그리고 런타임 프로빙을 조합하여 CPU 기능을 탐지합니다.

RISC-V CPU 식별 흐름: M-mode CSR → S-mode → Linux hwcap M-mode (펌웨어/OpenSBI) mvendorid JEDEC 제조사 코드 marchid 마이크로아키텍처 ID mimpid 구현 ID (리비전) misa ISA 비트맵 (I/M/A/F/D/C/V) mhartid 하트(HART) ID ⚠ Linux(S-mode)는 M-mode CSR 직접 접근 불가 OpenSBI가 M-mode에서 실행되며 SBI call + DTB를 통해 S-mode에 정보를 전달함 misa[1] XLEN: 32/64/128bit 전달 매커니즘 Device Tree (DTB) cpu@0 노드: riscv,isa = "rv64imafdc" riscv,isa-extensions = <&zba>, <&zbb>, <&zbc>, <&zbs>, ... SBI 확장 프로빙 sbi_probe_extension(extid) SBI_EXT_BASE, RFENCE SBI_EXT_PMU, DBCN SRST, SUSP, NACL ... Linux 커널 (S-mode) riscv_fill_hwcap() → riscv_isa_ext_data 파싱 → elf_hwcap 비트 세트 → ALTERNATIVE() 패치 AT_HWCAP 노출 HWCAP_ISA_I/M/A/F/D/C HWCAP_ISA_V (벡터) + Zba/Zbb/Zbs/Zicbom ... arch/riscv/kernel/ cpufeature.c cpuinfo.c, sbi.c alternative.c misa 비트 레이아웃 (XLEN-1 ~ 0) bit[8]=I(정수기반) bit[12]=M(곱셈) bit[0]=A(원자성) bit[5]=F(단정밀도FP) bit[3]=D(배정밀도FP) bit[2]=C(압축명령) bit[21]=V(벡터) bit[18]=S(수퍼바이저) ⚠ misa는 선택 사항 — 구현체가 0을 반환할 수 있음 → DT의 riscv,isa-extensions가 권위 소스 ALTERNATIVE(): 부팅 시 기능 있으면 NOP→실제명령 패치, 없으면 fallback 유지 (x86 alternative와 동일 개념) riscv_isa_ext_data[]: 확장명 문자열 ↔ hwcap 비트 매핑 테이블 (Zba, Zbb, Zbc, Zbs, Zbkb, Zbkx, Zicbom ...)

M-mode CSR 주요 레지스터

CSR 이름CSR 주소설명비고
mvendorid0xF11JEDEC 제조사 코드0=비상업적/미구현, SiFive=0x489, T-Head=0x5B7
marchid0xF12마이크로아키텍처 ID제조사 정의, 오픈소스는 MSB=0
mimpid0xF13구현 버전 ID펌웨어(Firmware)·RTL 리비전 식별
misa0x301ISA 지원 비트맵구현체가 0 반환 허용, 신뢰도 낮음
mhartid0xF14하드웨어 스레드(Thread) ID부트 HART=0, SMP는 0부터 순차 또는 임의

AT_HWCAP 비트 맵 (RISC-V)

HWCAP 매크로비트ISA 확장의미
HWCAP_ISA_Ibit 8I정수 기반 명령어 세트 (필수)
HWCAP_ISA_Mbit 12M정수 곱셈/나눗셈
HWCAP_ISA_Abit 0A원자성 명령어 (LR/SC, AMO)
HWCAP_ISA_Fbit 5F단정밀도 부동소수점
HWCAP_ISA_Dbit 3D배정밀도 부동소수점
HWCAP_ISA_Cbit 2C압축 명령어(16비트 인코딩)
HWCAP_ISA_Vbit 21V벡터 확장 (RISC-V V 1.0)
HWCAP_ISA_Hbit 7H하이퍼바이저 확장

커널 구현: riscv_fill_hwcap()과 ALTERNATIVE()

/* arch/riscv/kernel/cpufeature.c
 * riscv_isa_ext_data: 확장 이름 문자열 ↔ hwcap 비트 매핑
 */
struct riscv_isa_ext_data {
    const unsigned int  id;          /* RISCV_ISA_EXT_* 상수 */
    const char         *name;        /* DT riscv,isa-extensions의 문자열 */
    const char         *property;    /* DT 속성명 (소문자, 예: "zba") */
    const unsigned int *subset_ext_ids; /* 의존 확장 ID 목록 */
    const unsigned int  subset_ext_size;
};

/* riscv_fill_hwcap(): 부팅 시 DT + misa 파싱하여 elf_hwcap 구성 */
void riscv_fill_hwcap(void)
{
    struct device_node *node;
    const char *isa;

    for_each_of_cpu_node(node) {
        /* 1단계: riscv,isa 문자열 파싱 (레거시) */
        if (!of_property_read_string(node, "riscv,isa", &isa))
            riscv_parse_isa_string(isa, &this_cpu_ptr(&riscv_cpuinfo)->isa);

        /* 2단계: riscv,isa-extensions 배열 파싱 (최신 DT 바인딩) */
        riscv_parse_isa_extensions(node);
    }

    /* 3단계: SBI 확장 프로빙 */
    if (sbi_probe_extension(SBI_EXT_PMU) > 0)
        static_branch_enable(&riscv_isa_ext_keys[RISCV_ISA_EXT_ZICNTR]);

    /* 4단계: elf_hwcap 최종 세트 */
    elf_hwcap = riscv_isa_extension_base(hart_isa);
}

/* ALTERNATIVE() 매크로: 런타임 기능 기반 코드 패치
 * 기능 있으면 NOP를 실제 명령어로 교체, 없으면 fallback 유지
 * arch/riscv/include/asm/alternative.h */
ALTERNATIVE("nop",                       /* fallback: 기능 없을 때 */
             "amoadd.w zero,zero,(a0)", /* 패치: Zaamo 있을 때 */
             0,                           /* vendor ID (0=any) */
             RISCV_ISA_EXT_ZAAMO,        /* 요구 확장 */
             CONFIG_RISCV_ISA_ZAAMO);    /* 컴파일 조건 */
/* SBI 확장 프로빙 — arch/riscv/kernel/sbi.c */
long sbi_probe_extension(int ext)
{
    struct sbiret ret;
    /* ecall: a7=SBI_EXT_BASE, a6=SBI_BASE_PROBE_EXT, a0=ext_id */
    ret = sbi_ecall(SBI_EXT_BASE, SBI_BASE_PROBE_EXT, ext,
                    0, 0, 0, 0, 0);
    if (!ret.error)
        return ret.value;  /* 0=미지원, 양수=지원(버전/기능 플래그) */
    return -EOPNOTSUPP;
}

/* /proc/cpuinfo 출력 예 (RISC-V)
 * processor   : 0
 * hart        : 0
 * isa         : rv64imafdc_zba_zbb_zbc_zbs
 * mmu         : sv48
 * uarch       : sifive,bullet0
 */
riscv,isa-extensions 신뢰도: 리눅스 커널 6.5부터 riscv,isa 문자열보다 riscv,isa-extensions phandle 배열이 권위 소스로 사용됩니다. 이 배열에는 zba, zbb, zbs, zicbom(캐시 관리 명령), zifencei, svnapot(NAPOT 페이지) 등 세부 확장이 명시됩니다. 벤더 SoC DTB가 구식 형식을 사용하는 경우 riscv,isa 레거시 파싱도 병행합니다.
misa 신뢰성 문제: RISC-V 스펙은 misa를 0으로 반환하는 구현체를 허용합니다. 실제로 일부 임베디드 코어(Ibex, CVA6 일부)는 misa=0을 반환합니다. 따라서 리눅스 커널은 misa를 보조 수단으로만 활용하고, Device Tree의 riscv,isa-extensions를 우선합니다. 사용자 공간 프로그램은 getauxval(AT_HWCAP)으로 기능을 확인해야 하며, misa를 직접 읽을 방법도 없습니다(S-mode 접근 불가).
벡터 확장(V) 탐지 특이사항: RISC-V V 확장은 VLEN(벡터 레지스터 길이)이 구현체마다 다릅니다(128~65536비트). Linux는 vlenb CSR(0xC22, S-mode 읽기 가능)을 통해 실제 VLEN을 탐지하고, /proc/cpuinfoisa 필드와 HWCAP_ISA_V로 노출합니다. glibc 2.39+는 이를 통해 RVV 최적화 memcpy/memset을 자동 선택합니다.

PowerPC CPU 식별

PowerPC 아키텍처는 PVR(Processor Version Register)이라는 단일 특수 목적 레지스터로 CPU 버전을 식별합니다. mfspr 명령어로 읽으며, 상위 16비트는 버전(세대/마이크로아키텍처), 하위 16비트는 리비전(스테핑/수정 수준)을 나타냅니다. Linux 커널은 arch/powerpc/kernel/cputable.ccpu_specs[] 테이블로 PVR 값을 CPU 피처 집합에 매핑(Mapping)합니다.

PVR 값과 주요 POWER 프로세서

프로세서PVR 버전 (상위 16비트)PVR 전체 예시주요 특징
POWER70x003F0x003F0101ISA 2.06, VSX, TM(트랜잭셔널 메모리), 8-way SMT
POWER7+0x004A0x004A0100POWER7 개선판, 향상된 CAPI
POWER8E0x004B0x004B0100ISA 2.07, HTM, CAPI, 12-way SMT, ECC enhanced
POWER8NVL0x004C0x004C0100NVLink 지원 POWER8 (Summit 전 세대)
POWER80x004D0x004D0200표준 POWER8, ISA 2.07B
POWER90x004E0x004E1200ISA 3.0, NVLink 2.0, PCIe Gen4, 4-way SMT
POWER100x00800x00801100ISA 3.1, MMA(행렬곱셈 가속), PCIe Gen5, OpenCAPI 5.0
e5500 (Freescale)0x80240x80240010임베디드 Book3E, e5500 코어
e6500 (Freescale)0x80400x80400010AltiVec 지원 임베디드 코어
/* arch/powerpc/kernel/cputable.c
 * struct cpu_spec: PVR 범위 → 기능 집합 매핑 테이블의 핵심 구조체
 */
struct cpu_spec {
    unsigned int   pvr_mask;      /* PVR 비교 시 마스크 (버전부만 또는 전체) */
    unsigned int   pvr_value;     /* 마스크된 PVR 기댓값 */
    char          *cpu_name;      /* /proc/cpuinfo의 "cpu" 필드 */
    unsigned long  cpu_features;  /* CPU_FTR_* 비트마스크 */
    unsigned long  cpu_user_features;  /* AT_HWCAP 비트 (PPC_FEATURE_*) */
    unsigned long  cpu_user_features2; /* AT_HWCAP2 비트 (PPC_FEATURE2_*) */
    unsigned int   mmu_features;  /* MMU_FTR_* 비트마스크 */
    void         (*cpu_setup)(struct seq_file *m,
                              struct cpu_spec *spec);  /* 추가 초기화 콜백 */
    void         (*cpu_restore)(void);  /* 슬립 복구 후 MSR/HID 복원 */
};

/* cpu_specs[] 테이블 항목 예시 (POWER10) */
{
    .pvr_mask           = 0xffff0000,
    .pvr_value          = 0x00800000,
    .cpu_name           = "POWER10",
    .cpu_features       = CPU_FTRS_POWER10,
    .cpu_user_features  = COMMON_USER_POWER10,
    .cpu_user_features2 = COMMON_USER2_POWER10,
    .mmu_features       = MMU_FTRS_POWER10,
    .cpu_setup          = __setup_cpu_power10,
},

/* identify_cpu(): 부팅 초기 PVR 읽어 cpu_specs[] 선형 검색
 * arch/powerpc/kernel/cputable.c */
struct cpu_spec * identify_cpu(unsigned long offset, unsigned int pvr)
{
    struct cpu_spec *s = cpu_specs;
    struct cpu_spec **cur = &cur_cpu_spec;

    for (; s->pvr_mask; s++) {
        if ((pvr & s->pvr_mask) == s->pvr_value) {
            *cur = apply_feature_fixups(s);
            return *cur;
        }
    }
    return NULL;
}
/* PowerPC: PVR 및 PIR 읽기
 * PVR (Processor Version Register) = SPR 287 (0x11F)
 * PIR (Processor ID Register)      = SPR 1023 (0x3FF) — POWER9+
 */

/* PVR 읽기: mfspr rD, 287 */
mfspr   r3, 287             /* r3 = PVR */
rlwinm  r4, r3, 16, 16, 31  /* r4 = Version (상위 16비트 → 하위) */
andi.   r5, r3, 0xFFFF      /* r5 = Revision (하위 16비트) */

/* PIR 읽기: 논리 CPU ID (POWER9 이후 SMT 스레드 포함)
 * PIR[31:8] = core ID, PIR[7:0] = thread ID within core */
mfspr   r3, 1023
rlwinm  r4, r3, 24, 8, 31   /* r4 = core ID */
andi.   r5, r3, 0xFF        /* r5 = thread ID */

/* Device Tree의 cpu@0 노드:
 * reg = <0>;              — HART/PIR에 대응하는 논리 CPU ID
 * cpu-version = <0x004E1200>;  — PVR 값 직접 명시 (일부 플랫폼)
 * ibm,pa-features = <...>;     — PA(PowerPC Architecture) 확장 비트 배열
 * d-cache-size = <0x8000>;
 * i-cache-size = <0x8000>;
 */
AT_HWCAP / AT_HWCAP2 주요 비트 (PowerPC):
  • PPC_FEATURE_HAS_ALTIVEC (bit 28): AltiVec/VMX 벡터 유닛 (POWER6+)
  • PPC_FEATURE_HAS_VSX (bit 7): VSX(Vector-Scalar Extension, POWER7+)
  • PPC_FEATURE2_HAS_HTM: 하드웨어 트랜잭셔널 메모리 (POWER8, 이후 POWER10에서 제거)
  • PPC_FEATURE2_ARCH_3_1: ISA 3.1 (POWER10) — MMA, prefixed instructions
  • PPC_FEATURE2_MMA: 행렬 곱셈 가속기 (POWER10 전용)
  • PPC_FEATURE_TRUE_LE: 진정한 Little-Endian 모드 지원 (POWER8+, ppc64le)
cur_cpu_spec 전역 포인터가 현재 CPU의 cpu_spec을 가리키며, cpu_has_feature(CPU_FTR_*) 매크로로 런타임 기능 확인이 가능합니다.

MIPS CPU 식별

MIPS 아키텍처는 CP0(Coprocessor 0)의 레지스터를 통해 CPU 식별과 기능 탐지를 수행합니다. 핵심은 PRId(Processor ID, CP0 Register 15)Config0~Config5 레지스터 체인입니다. Config 레지스터는 M 비트(bit 31)로 다음 Config 레지스터의 존재를 표시하며, 체인 방식으로 연결됩니다.

PRId 비트 레이아웃 및 Company ID 코드

비트 범위필드명설명예시
[31:24]Company Options회사 정의 옵션 (구현체별 의미 다름)0x00 (보통 미사용)
[23:16]Company IDCPU 제조사 코드0x01=MIPS, 0x0D=Cavium, 0x0E=Loongson 레거시
[15:8]Processor IDCPU 코어 종류0x90=MIPS74K, 0x96=MIPS I6400, 0x29=Cavium CN78xx
[7:0]Revision패치/스테핑 리비전0x01~0x0F
Company ID제조사주요 코어
0x01MIPS Technologies (Wave Computing)MIPS 24K/34K/74K/M5150, I6400, I6500, P5600
0x02BroadcomBCM3302, BCM4710 (MIPS 기반 홈 라우터)
0x0DCavium NetworksOcteon CN3xxx/CN5xxx/CN7xxx/CN8xxx (네트워크 프로세서)
0x0ELoongson (레거시)Loongson 1/2/3 (초기 버전, MIPS64 호환)
0x10NECVR4100, VR4181 (PDA/임베디드)
0x23RMI / NetLogicXLR/XLS (다중코어 네트워크 SoC)
0x30Alchemy (AMD)Au1000/Au1100/Au1200 (저전력 임베디드)

Config 레지스터 체인 구조

/* PRId 읽기: CP0 Register 15, Select 0 */
mfc0    $t0, $15, 0          /* $t0 = PRId */
srl     $t1, $t0, 16         /* $t1 = Company ID (상위) + Processor ID */
andi    $t2, $t0, 0xFF       /* $t2 = Revision [7:0] */

/* Config0: CP0 Register 16, Select 0
 * [31]   M=1 → Config1 존재
 * [15:13] AT: 아키텍처 타입 (0=MIPS32, 1=MIPS64/32addr, 2=MIPS64)
 * [12:10] AR: 아키텍처 리비전 (0=R1, 1=R2, 2=R6)
 * [8:7]  MT: MMU 타입 (0=없음, 1=TLB, 3=FMT/FixedMapping, 4=VTLB+FTLB)
 * [2:0]  K0: kseg0 캐시 속성 (0=Uncached, 3=WriteBack Cached)
 */
mfc0    $t0, $16, 0          /* Config0 */
srl     $t1, $t0, 31         /* M 비트: 1이면 Config1 존재 */
beqz    $t1, no_config1

mfc0    $t0, $16, 1          /* Config1: 캐시 지오메트리, FP, MIPS16, Watch */
/* Config1[30:25] MMUSize: TLB 엔트리 수 - 1
 * Config1[24:22] IS: I-캐시 집합 수 로그2 (0=64, 1=128, ...)
 * Config1[21:19] IL: I-캐시 라인 크기 (1=4B, 2=8B, 3=16B, 4=32B)
 * Config1[18:16] IA: I-캐시 연관도 (0=직접, 1=2-way, ...)
 * Config1[15:13] DS: D-캐시 집합 수, Config1[12:10] DL, Config1[9:7] DA
 * Config1[0]    FP: FPU 지원 여부
 */

mfc0    $t0, $16, 3          /* Config3: ULRI, VInt, MSA, MCU, MtChecker */
/* Config3[28] ULRI: UserLocal 레지스터 구현 여부
 * Config3[5]  VINT: 벡터 인터럽트 지원
 * Config3[4]  VEIC: 외부 인터럽트 컨트롤러 지원
 * Config3[2]  MT: 멀티스레딩(MIPS MT ASE) 지원
 * Config3[1]  SM: SmartMIPS ASE 지원
 */
ext     $t1, $t0, 28, 1       /* ULRI 비트 추출 */

mfc0    $t0, $16, 5          /* Config5: VP(가상 프로세서), FRE, MSAEn */
/* Config5[7] FRE: FR=1 모드에서 레거시 FPU 에뮬레이션
 * Config5[2] VP:  가상 프로세서(MIPS MT 관련)
 * Config5[27] MSAEn: MSA(MIPS SIMD Architecture) 활성화
 */
no_config1:
/* arch/mips/kernel/cpu-probe.c
 * cpu_probe(): 부팅 초기 PRId + Config 체인 읽어 cpuinfo_mips 초기화
 */
void cpu_probe(void)
{
    struct cpuinfo_mips *c = &current_cpu_data();
    unsigned int cpu = smp_processor_id();

    /* PRId 읽기 */
    c->processor_id = read_c0_prid();   /* mfc0 $t, $15, 0 */
    c->fpu_id       = cpu_has_fpu() ? read_32bit_cp1_register(CP1_REVISION) : 0;
    c->cputype      = CPU_UNKNOWN;

    /* Company ID로 분기 */
    switch (c->processor_id & PRID_COMP_MASK) {
    case PRID_COMP_MIPS:      /* 0x010000 */
        cpu_probe_mips(c, cpu);
        break;
    case PRID_COMP_CAVIUM:    /* 0x0D0000 */
        cpu_probe_cavium(c, cpu);
        break;
    case PRID_COMP_LOONGSON:  /* 0x0E0000 — 레거시 */
        cpu_probe_loongson(c, cpu);
        break;
    /* ... */
    }

    /* Config 레지스터 체인 읽기 */
    cpu_probe_config(c);

    /* cpuinfo_mips.ases: CPU_ASE_MIPS16, CPU_ASE_MSA, CPU_ASE_DSP 등 */
    /* cpuinfo_mips.options: MIPS_CPU_FPU, TLB, 4KEX, LLSC 등 */
}

/* cpuinfo_mips 주요 필드 */
struct cpuinfo_mips {
    unsigned long  udelay_val;
    unsigned long  asid_cache;
    unsigned int   processor_id; /* PRId 원본 값 */
    unsigned int   fpu_id;       /* FPU PRId */
    unsigned int   cputype;      /* CPU_24K, CPU_74K, CPU_CAVIUM_OCTEON 등 */
    unsigned long  options;      /* MIPS_CPU_FPU, MIPS_CPU_LLSC, ... */
    unsigned long  ases;         /* MIPS_ASE_MIPS16, MIPS_ASE_MSA, ... */
    unsigned int   tlbsize;      /* TLB 엔트리 수 (Config1에서 읽음) */
    unsigned int   tlbsizevtlb;  /* VTLB 크기 */
    unsigned int   tlbsizeftlbsets; /* FTLB 세트 수 */
    int            icache_size, dcache_size; /* 바이트 단위 */
    int            icache_ways, dcache_ways;
    int            icache_linesz, dcache_linesz;
};
Config 레지스터 체인과 M 비트: MIPS Config 레지스터는 비선형 체인 구조입니다. Config0[31]=M이면 Config1이 존재하고, Config1[31]=M이면 Config2가 존재하는 식입니다. Config3에는 MSA(MIPS SIMD Architecture, 128비트 SIMD), ULRI(UserLocal 레지스터, TLS 포인터 저장용), 가상 인터럽트 등 중요 기능이 기술됩니다. Config5는 MIPS R6에서 추가되어 FRE(FR=1 모드 에뮬레이션)와 같은 하위 호환 기능을 제어합니다. Linux 커널의 cpu_probe_config()는 M 비트를 확인하며 순차적으로 체인을 따라갑니다.

S390x CPU 식별

IBM Z 아키텍처(s390x)는 x86의 CPUID와 개념적으로 가장 유사한 비트맵 기반 기능 열거 방식을 사용합니다. 세 가지 핵심 명령어 — STIDP, STFLE, STSI — 가 CPU 식별, 기능 비트맵, 토폴로지 정보를 각각 담당합니다.

STIDP — 64비트 CPU 식별 더블워드 bits 63–48 CPU 유형 (16-bit) bits 47–32 버전 (16-bit) bits 31–8 시리얼 번호 (24-bit) bits 7–0 예약 (8-bit) STFLE 비트맵 스캔 흐름 r0 = 0 블록 수 초기 설정 STFLE 0(r1) 총 블록 수 확인 r0 = N-1 전체 비트맵 요청 STFLE 0(r1) 전체 저장 비트맵 레이아웃 (각 블록 = 64-bit 더블워드) 블록 0 비트 0–63 블록 1 비트 64–127 블록 2 비트 128–191 블록 3 비트 192–255 bit 129 = LSB 기준 블록2[bit1] = 벡터 명령어 지원 커널 매크로: test_facility(N) → S390_lowcore.stfle_fac_list[N/64] & (1UL << (63 - N%64))

STIDP — CPU 식별 더블워드

STIDP(Store CPU ID) 명령어는 64비트 더블워드를 메모리에 저장합니다. 상위 16비트가 CPU 유형 코드이며, 이 값으로 IBM Z 세대(z13/z14/z15/z16 등)를 구분합니다.

STIDP 더블워드 필드 구성
비트 범위필드명크기설명
63–48CPU Type16-bitIBM Z 세대 식별자 (예: 0x3906 = z14, 0x8561 = z15)
47–32Version16-bit마이크로코드/하드웨어 버전
31–8Serial24-bit시스템 시리얼 번호
7–0(예약)8-bit현재 미사용, 0으로 유지
/* arch/s390/kernel/setup.c — STIDP로 CPU 유형 읽기 */
static void __init setup_cpu_type(void)
{
    struct cpuid cpu_id;

    get_cpu_id(&cpu_id);   /* inline STIDP 래퍼 */

    /* cpu_id.machine = 상위 16비트 (CPU 유형) */
    S390_lowcore.cpu_type = cpu_id.machine;

    pr_info("CPU: Type %04X, Version %04X\n",
            cpu_id.machine, cpu_id.version);
}

/* <asm/processor.h> — cpuid 구조체 */
struct cpuid {
    unsigned int version : 8;
    unsigned int ident   : 24;
    unsigned int machine : 16;
    unsigned int unused  : 16;
} __packed;

STFLE — 기능 비트맵 열거

STFLE(Store Facility List Extended)는 x86 CPUID의 ECX/EDX 플래그와 가장 유사한 개념입니다. 각 비트 하나가 정확히 하나의 하드웨어/펌웨어 기능을 나타내며, 비트맵은 64비트 더블워드(블록) 단위로 확장됩니다. r0에 요청할 블록 수를 지정하고 STFLE를 실행하면 비트맵 전체가 메모리에 기록됩니다.

STFLE 주요 기능 비트 목록
비트 번호기능 이름의미
2z/Architecture 모드64비트 z/Architecture 동작 모드 지원
17MSA (Message Security Assist)AES/SHA 등 암호화 하드웨어 명령어
42DFP (Decimal Floating Point)IEEE 754-2008 10진 부동소수점 지원
49Miscellaneous Insn ExtensionsRISBG 등 비트 조작 확장 명령어
76MSA 확장 3CMAC, GHASH 하드웨어 가속
77MSA 확장 4RSA 보조 연산
129벡터 명령어 (VXF)128비트 SIMD 벡터 연산 (z13+)
134벡터 확장 1 (VXE)벡터 BFP/정수 확장 (z14+)
146MSA 확장 8EdDSA, 512비트 해시(Hash) 가속
155벡터 Packed Decimal10진 데이터 벡터 처리
165NNP 지원신경망 처리 보조 (z16+)
/* arch/s390/kernel/processor.c — STFLE 비트맵 읽기 및 hwcap 설정 */
static void __init setup_hwcaps(void)
{
    /* STFLE 결과는 부팅 초기에 S390_lowcore.stfle_fac_list[]에 저장됨 */
    static const int stfl_bits[6] = { 0, 2, 17, 42, 49, 76 };
    int i;

    for (i = 0; i < ARRAY_SIZE(stfl_bits); i++) {
        if (test_facility(stfl_bits[i]))
            pr_debug("Facility bit %d set\n", stfl_bits[i]);
    }

    /* 벡터 지원 확인 (비트 129) */
    if (test_facility(129))
        elf_hwcap |= HWCAP_S390_VX;

    /* 벡터 확장 확인 (비트 134) */
    if (test_facility(134))
        elf_hwcap |= HWCAP_S390_VXD | HWCAP_S390_VXE;
}

/* test_facility() 매크로: 비트 N이 설정되었는지 확인 */
/* stfle_fac_list[N/64] & (1UL << (63 - N%64))              */

STSI — 시스템 정보 및 토폴로지

STSI(Store System Information)는 LPAR 구성, CPU 토폴로지, 가상화 레이어 정보를 제공합니다. Function Code(FC)와 Selector 1/2 조합으로 다양한 정보 블록을 선택합니다.

x86 CPUID와의 유사성: s390x의 STFLE는 x86 CPUID leaf 1(ECX/EDX), leaf 7(EBX/ECX/EDX)의 기능 비트 방식과 구조적으로 가장 유사합니다. x86이 leaf 인덱스를 EAX에 넣고 호출하는 방식이라면, s390x는 미리 비트맵 전체를 메모리에 복사한 뒤 test_facility(N) 매크로로 개별 비트를 검사합니다. 두 방식 모두 "비트 하나 = 기능 하나"라는 동일한 의미론을 따릅니다.
커널 소스 위치:
  • arch/s390/kernel/processor.csetup_hwcaps(), cpuinfo_S390 proc 출력
  • arch/s390/kernel/setup.c — 부팅 초기 STIDP/STFLE 호출, S390_lowcore 초기화
  • arch/s390/include/asm/facility.htest_facility(), stfle_fac_list 정의
  • arch/s390/include/asm/processor.hstruct cpuid, get_cpu_id() 인라인 어셈블리
/proc/cpuinfofeatures: 행이 STFLE 비트맵을 인간이 읽기 쉬운 문자열로 변환한 결과입니다.

LoongArch CPU 식별

LoongArch는 중국 룽신(Loongson)이 설계한 RISC ISA로, 2021년 리눅스 5.19에 메인라인 병합되었습니다. CPU 기능 열거는 CPUCFG 명령어 하나로 통일되어 있으며, x86 CPUID의 leaf 인덱스 방식과 구조적으로 가장 닮아 있습니다.

CPUCFG 명령어 동작 원리

cpucfg rd, rj 명령어는 rj 레지스터의 값을 인덱스(워드 번호)로 사용하여 해당 구성 워드를 rd에 복사합니다. 인덱스 0부터 시작하며, 정의되지 않은 인덱스를 읽으면 0이 반환됩니다.

CPUCFG 워드 인덱스별 주요 내용
인덱스워드 이름주요 필드 / 비트
0x0PRIDCompany[31:16], ProcType[15:8], Rev[7:0] — 프로세서 ID
0x1아키텍처 기능 IOCSR[0], IOCSR32[1], LAMO[2], FP[3], FPD[4], LSX[6], LASX[7], Crypto[8], LVZ[9], LBT-X86[10], LBT-ARM[11], LBT-MIPS[12]
0x2구현 기능FP64[1], UnalignMem[3], PageSizeSupported[5:0] 등 마이크로아키텍처 옵션
0x3캐시 정보 0I-캐시 레벨 수, 라인 크기, 연관도
0x4캐시 정보 1D-캐시 레벨 수, 라인 크기, 연관도
0x5캐시 정보 2L2 캐시 파라미터
0x6캐시 정보 3L3 캐시 파라미터
0x10성능 특성 0디코드 폭, 실행 유닛 수
0x11–0x14성능 특성 1–4TLB 엔트리 수, 분기 예측 버퍼(Buffer) 크기 등
/* arch/loongarch/kernel/cpu-probe.c — CPUCFG 기반 기능 탐지 */
static void cpu_probe_loongarch(struct cpuinfo_loongarch *c)
{
    uint32_t config;

    /* 워드 0: PRID 읽기 */
    config = read_cpucfg(LOONGARCH_CPUCFG0);
    c->processor_id = config;

    /* 워드 1: 아키텍처 기능 비트맵 */
    config = read_cpucfg(LOONGARCH_CPUCFG1);

    if (config & CPUCFG1_FP)
        set_isa(c, LOONGARCH_ISA_FP32);
    if (config & CPUCFG1_LSX)
        set_isa(c, LOONGARCH_ISA_LSX);   /* 128비트 SIMD */
    if (config & CPUCFG1_LASX)
        set_isa(c, LOONGARCH_ISA_LASX);  /* 256비트 SIMD */
    if (config & CPUCFG1_CRYPTO)
        set_isa(c, LOONGARCH_ISA_CRYPTO);

    /* elf_hwcap 설정 */
    if (cpu_has_fpu())
        cpu_set_fpu_caps(c);
    if (cpu_has_lsx())
        elf_hwcap |= HWCAP_LOONGARCH_LSX;
    if (cpu_has_lasx())
        elf_hwcap |= HWCAP_LOONGARCH_LASX;
}

/* read_cpucfg() 인라인 어셈블리 래퍼 */
static inline uint32_t read_cpucfg(uint32_t reg)
{
    uint32_t __res;
    __asm__ volatile(
        "cpucfg %0, %1\n\t"
        : "=r"(__res) : "r"(reg)
    );
    return __res;
}

HWCAP 및 커널 소스

LoongArch의 elf_hwcap은 CPUCFG 워드 1 비트에서 직접 파생됩니다. /proc/cpuinfoFeatures: 행이 이를 반영하며, 사용자 공간은 getauxval(AT_HWCAP)으로 동일 정보에 접근합니다.

x86 CPUID와의 설계 유사성: LoongArch CPUCFG는 아키텍처 중 x86 CPUID에 가장 가까운 설계를 채택했습니다. EAX에 leaf 번호를 넣고 호출하는 x86 패턴이 LoongArch에서는 rj에 인덱스를 넣는 cpucfg rd, rj로 대응됩니다. 반환 값도 32비트 워드 단위라는 점에서 x86 EAX/EBX/ECX/EDX 각각과 동일한 크기입니다. 커널 코드는 arch/loongarch/kernel/cpu-probe.ccpu_probe()에 집중되어 있으며, CPU 정보 구조체는 struct cpuinfo_loongarch(arch/loongarch/include/asm/cpu-info.h)입니다.

SPARC CPU 식별 (개요)

SPARC(sun4v/sun4u) 리눅스는 현재 유지보수 모드로, 새로운 하드웨어 지원이 추가되지 않습니다. CPU 식별은 특권 레지스터 %ver와 sun4v 하이퍼바이저 제공 Machine Description을 통해 이루어집니다.

VER 레지스터 구조

rdpr %ver, %reg 명령어로 64비트 버전 레지스터를 읽습니다. 이 레지스터는 특권 레벨(SPARC V9 기준 트랩 레벨 0 이상)에서만 접근 가능하므로, 부팅 초기 커널 초기화 단계에서 한 번 읽어 저장해 둡니다.

SPARC V9 VER 레지스터 필드
비트 범위필드명설명
63–48Manuf제조사 코드 (예: 0x003E = Sun/Oracle)
47–32Impl구현(칩) 번호 (예: UltraSPARC T4 = 0x0091)
31–24Mask마스크(스테핑) 번호
15–8MaxTL최대 트랩 레벨 (일반적으로 6)
4–0MaxWin레지스터 윈도우 수 − 1 (일반적으로 7)
/* arch/sparc/kernel/cpu.c — sun4v CPU 탐지 */
void __init sun4v_cpu_probe(void)
{
    switch (sun4v_chip_type) {
    case SUN4V_CHIP_NIAGARA1:
        sparc_pmu_type = "niagara";
        break;
    case SUN4V_CHIP_NIAGARA2:
        sparc_pmu_type = "niagara2";
        break;
    default:
        printk(KERN_WARNING "Unknown sun4v chip type %d\n",
               sun4v_chip_type);
        break;
    }
}

/* VER 레지스터 직접 읽기 (어셈블리) */
register unsigned long ver asm("g0");
__asm__("rdpr %%ver, %0" : "=r"(ver));
manuf = (ver >> 48) & 0xffff;
impl  = (ver >> 32) & 0xffff;
mask  = (ver >> 24) & 0xff;

sun4v Machine Description

UltraSPARC T 시리즈(Niagara) 이후의 sun4v 플랫폼은 하이퍼바이저가 제공하는 Machine Description(MD) 구조체를 통해 CPU 토폴로지, 캐시 크기, 지원 기능을 전달합니다. MD는 DAG(방향성 비순환 그래프) 형태의 노드-속성 구조로, 커널은 arch/sparc/kernel/mdesc.cmdesc_scan()으로 파싱합니다.

유지보수 모드 안내: SPARC 아키텍처(arch/sparc/)는 리눅스 커널에서 유지보수 모드(maintenance mode)로 분류됩니다. 새로운 기능 추가나 새 하드웨어 지원은 사실상 중단되었으며, 보안 패치와 빌드 수정만 반영됩니다. Oracle SPARC M8 이후 세대는 리눅스 메인라인에 공식 지원되지 않습니다. 프로덕션 환경에서는 Solaris 또는 OpenBSD/sparc64를 권장합니다.

Linux 커널 통합 CPU 추상화

Linux 커널은 x86의 CPUID, ARM64의 MRS, RISC-V의 CSR 등 아키텍처마다 서로 다른 CPU 식별 메커니즘을 단일한 추상화 레이어로 통합합니다. 이 레이어를 통해 커널 상위 계층과 사용자 공간은 아키텍처를 의식하지 않고 CPU 기능을 조회하고 활용할 수 있습니다.

하드웨어 식별 명령어 계층 x86 CPUID leaf ARM64 MRS 시스템레지스터 RISC-V CSR + DT + SBI PowerPC PVR + DT S390x STFLE 비트맵 커널 내부 추상화 구조체 계층 cpuinfo_x86 x86_cap_bits[] cpuinfo_arm64 reg_id_aa64*, hwcap riscv_cpuinfo mvendorid, marchid cpuinfo_ppc pvr, cpu_features S390_lowcore stfle_fac_list[] 통합 사용자 공간 인터페이스 /proc/cpuinfo show_cpuinfo() per-arch AT_HWCAP / AT_HWCAP2 ELF 보조 벡터 ALTERNATIVE() 런타임 패치 매크로 cpu_has_*() 커널 내부 API ▲ 단방향 데이터 흐름: 하드웨어 → 커널 추상화 구조체 → 사용자 공간 인터페이스

/proc/cpuinfo 생성 메커니즘

/proc/cpuinfo는 각 아키텍처가 독립적으로 구현한 show_cpuinfo() 콜백(Callback)을 통해 생성됩니다. 이 함수는 struct seq_operations 인터페이스를 통해 /proc 파일시스템(Filesystem)에 등록되며, 커널 부팅 시 수집된 CPU 정보를 아키텍처별 형식으로 출력합니다.

/proc/cpuinfo 아키텍처별 구현 비교
아키텍처 구현 파일 핵심 함수 기능 플래그 필드명 정보 수집 시점
x86 arch/x86/kernel/cpu/proc.c show_cpuinfo() flags:, bugs: identify_cpu() (부팅 초기)
ARM64 arch/arm64/kernel/cpuinfo.c c_show() Features: cpuinfo_store_cpu()
RISC-V arch/riscv/kernel/proc.c c_show() isa: DT 파싱 + SBI 호출
PowerPC arch/powerpc/kernel/setup-common.c show_cpuinfo() cpu:, clock: PVR 레지스터 + DT
S390x arch/s390/kernel/processor.c show_cpuinfo() features: stfle() (STFLE 명령)
LoongArch arch/loongarch/kernel/proc.c c_show() Features: cpu_probe()

x86의 경우 flags: 필드에는 하드웨어가 실제로 지원하는 기능과 커널이 소프트웨어적으로 에뮬레이션하는 기능이 혼합되어 출력됩니다. bugs: 필드는 커널이 알고 있는 CPU 버그(예: Spectre, Meltdown 관련)를 나타냅니다.

/* arch/x86/kernel/cpu/proc.c */
static int show_cpuinfo(struct seq_file *m, void *v)
{
    struct cpuinfo_x86 *c = v;
    unsigned int i;

    seq_printf(m, "processor\t: %u\n", c->cpu_index);
    seq_printf(m, "vendor_id\t: %s\n", c->x86_vendor_id[0]
                  ? c->x86_vendor_id : "unknown");

    /* flags: 필드 — 커널 비트 배열에서 문자열로 변환 */
    seq_puts(m, "flags\t\t:");
    for (i = 0; i < 32 * NCAPINTS; i++) {
        if (cpu_has(c, i) && x86_cap_flags[i] != NULL)
            seq_printf(m, " %s", x86_cap_flags[i]);
    }
    seq_putc(m, '\n');

    /* bugs: 필드 — 알려진 취약점/버그 목록 */
    seq_puts(m, "bugs\t\t:");
    for (i = 0; i < NBUGINTS * 32; i++) {
        if (cpu_bug(c, i) && x86_bug_flags[i])
            seq_printf(m, " %s", x86_bug_flags[i]);
    }
    seq_putc(m, '\n');
    return 0;
}
/proc/cpuinfo 주요 필드명 아키텍처별 매핑
정보 항목 x86 ARM64 RISC-V PowerPC S390x
CPU 모델/이름 model name CPU implementer, CPU part uarch cpu machine
기능 플래그 flags Features isa (없음, DT 참조) features
동작 주파수 cpu MHz CPU max MHz hart + 별도 clock bogomips per cpu
캐시 크기 cache size (없음) (없음) L1 d cache (없음)
버그/취약점 bugs (없음) (없음) (없음) (없음)
프로세서 번호 processor processor processor processor processor

AT_HWCAP / AT_HWCAP2: ELF 보조 벡터

AT_HWCAP은 커널이 사용자 공간 프로그램을 실행할 때 ELF 보조 벡터(auxiliary vector)를 통해 전달하는 하드웨어 기능 비트마스크입니다. 이는 /proc/cpuinfo보다 훨씬 빠르고 안정적인 기능 탐지 방법으로, 동적 링커(Linker)와 런타임 라이브러리가 주로 활용합니다.

커널 공간 elf_hwcap (전역변수) ARM64: arm64_elf_hwcap x86: ELF_HWCAP 매크로 RISC-V: elf_hwcap (부팅 시 초기화) create_elf_tables() fs/binfmt_elf.c NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP); execve() ELF 보조 벡터 (스택) AT_HWCAP = 0x....... AT_HWCAP2 = 0x....... AT_PAGESZ = 4096 AT_NULL = (종료) 사용자 공간 활용 ld.so 동적 링커 HWCAP 기반 라이브러리 선택 (/etc/ld.so.conf) libm/libc HWCAP 분기 getauxval(AT_HWCAP) glibc sys/auxv.h 애플리케이션 직접 조회 파일 I/O 없이 O(1) ifunc 리졸버 __attribute__((ifunc)) 로드 시 최적 구현 선택 memcpy/strlen 등 최적화 /proc/self/auxv 바이너리 파일로 직접 읽기 가능 (디버깅 목적)
아키텍처별 HWCAP 비트 예시
아키텍처 HWCAP 비트 예시 정의 위치 AT_HWCAP2 지원
x86_64 HWCAP_FPU, HWCAP_MMX, HWCAP_SSE arch/x86/include/asm/elf.h 없음 (cpuid 직접 사용)
ARM64 HWCAP_FP, HWCAP_ASIMD, HWCAP_SVE, HWCAP_SHA3 arch/arm64/include/uapi/asm/hwcap.h O (HWCAP2_SVE2 등)
RISC-V COMPAT_HWCAP_ISA_I, HWCAP_ISA_M, HWCAP_ISA_A arch/riscv/include/uapi/asm/hwcap.h O (확장 ISA)
PowerPC PPC_FEATURE_HAS_ALTIVEC, PPC_FEATURE_HAS_VSX arch/powerpc/include/uapi/asm/cputable.h O (PPC_FEATURE2_*)
S390x HWCAP_S390_ESAN3, HWCAP_S390_VXRS arch/s390/include/uapi/asm/hwcap.h 없음
/* 사용자 공간: getauxval()을 이용한 HWCAP 조회 */
#include <sys/auxv.h>
#include <asm/hwcap.h>

void detect_cpu_features(void)
{
    unsigned long hwcap  = getauxval(AT_HWCAP);
    unsigned long hwcap2 = getauxval(AT_HWCAP2);

#if defined(__aarch64__)
    /* ARM64: SVE 지원 여부 확인 */
    if (hwcap & HWCAP_SVE)
        printf("SVE 지원: 벡터 길이 %lu bytes\n",
               getauxval(AT_VECTOR_SIZE_SVE));
    if (hwcap2 & HWCAP2_SVE2)
        printf("SVE2 지원\n");
    if (hwcap & HWCAP_AES)
        printf("AES 하드웨어 가속 사용 가능\n");

#elif defined(__riscv)
    /* RISC-V: 기본 ISA 확장 확인 */
    if (hwcap & COMPAT_HWCAP_ISA_V)
        printf("RISC-V Vector 확장 지원\n");
    if (hwcap & COMPAT_HWCAP_ISA_F)
        printf("단정밀도 부동소수점(F) 지원\n");
#endif
}

ALTERNATIVE() 매크로: 런타임 코드 패칭

ALTERNATIVE() 매크로는 커널이 부팅 시 CPU 기능을 확인한 후 최적화된 코드로 교체하는 런타임 패칭 메커니즘입니다. 이를 통해 단일 커널 이미지가 기능이 다른 여러 CPU에서 최적 성능을 발휘할 수 있습니다.

/* arch/x86/include/asm/alternative.h */
/*
 * ALTERNATIVE(oldinstr, newinstr, feature)
 * - oldinstr: 기본 명령어 (기능 없을 때 사용)
 * - newinstr: 대체 명령어 (기능 있을 때 패칭)
 * - feature : X86_FEATURE_* 비트
 */
#define ALTERNATIVE(oldinstr, newinstr, feature) \
    ALTERNATIVE_2(oldinstr, newinstr, feature, \
                  newinstr, feature)

/* 실사용 예: mfence vs lfence 선택 */
static inline void memory_barrier_before_unstuff(void)
{
    asm volatile(
        ALTERNATIVE("mfence", "lfence", X86_FEATURE_LFENCE_RDTSC)
        : : : "memory"
    );
}

/* arch/arm64/include/asm/alternative.h */
/*
 * ARM64 ALTERNATIVE: 시스템 레지스터 기반 기능 비트 참조
 * ARM64_HAS_* 상수는 idreg 비교 결과를 나타냄
 */
SYM_FUNC_START(__memcpy)
    alternative_if ARM64_HAS_NO_HW_PREFETCH
        prfm pldl1keep, [x1, #448]
    alternative_else_nop_endif
    /* ... 실제 memcpy 구현 ... */
SYM_FUNC_END(__memcpy)
ALTERNATIVE() 패칭 타이밍
커널은 apply_alternatives()start_kernel() 초기에 호출하여 모든 __alt_instructions 섹션의 패치를 일괄 적용합니다. 패칭은 단방향이며, 이후 CPU 핫플러그(Hotplug)로 추가된 CPU가 더 적은 기능을 가지면 문제가 발생할 수 있어 커널은 이를 cpu_die_early()로 처리합니다. RISC-V는 riscv_cpufeature_patch_func()를 통해 동일한 패턴을 구현합니다.
x86 ALTERNATIVE()와 ARM64의 차이
x86은 CPUID 비트를 직접 참조하는 단순한 비트마스크 방식인 반면, ARM64는 시스템 레지스터(ID_AA64ISAR0_EL1 등)의 특정 필드 값을 비교하는 방식을 사용합니다. 이로 인해 ARM64의 ARM64_HAS_* 상수는 단순 비트 번호가 아니라 레지스터, 필드, 최솟값을 인코딩한 복합 값입니다(arm64_ftr_bits[] 테이블 참조).

아키텍처 간 CPU 식별 비교

각 아키텍처는 하드웨어 설계 철학과 역사적 배경에 따라 근본적으로 다른 CPU 식별 메커니즘을 채택했습니다. 이 차이를 이해하는 것은 이식 가능한 커널 코드 작성과 크로스 아키텍처 성능 최적화에 필수적입니다.

아키텍처별 CPU 식별 명령어 및 추상화 계층 아키텍처 식별 명령어/소스 레지스터/출력 권한 수준 커널 구조체 설계 철학 x86/x86_64 비권한 단일 명령 CPUID (EAX=leaf) ECX=subleaf EAX,EBX,ECX,EDX 비권한 (Ring 3) struct cpuinfo_x86 x86_capability[] 단일 명령, 리프 인덱스 ARM64 시스템 레지스터 MRS Xt, sysreg ID_AA64ISAR0_EL1 등 64비트 시스템 레지스터 4비트 필드 버전 관리 EL1 (커널) 전용 struct cpuinfo_arm64 reg_id_aa64isar0 필드 버전 관리 방식 RISC-V 3중 소스 CSR(misa/mvendorid) + DT /cpus/cpu@N + SBI CPPC misa: ISA 비트맵 mvendorid/marchid M-mode(CSR) S-mode(SBI) struct riscv_cpuinfo riscv_isa_extension_* 3중 소스 협력 모델 LoongArch CPUID 유사 CPUCFG rd, rj rj=레지스터 번호 32비트 결과 레지스터 비권한 (PLV3) struct cpuinfo_loongarch loongarch_fpu_mask x86 CPUID 영향, 간결 PowerPC PVR + DT mfspr r, PVR + DT /cpus/cpu@N ibm,pa-features PVR[16:31]=버전 PVR[0:15]=구현 권한 필요 (SPR) struct cpuinfo_ppc cpu_features (u64) PVR 모델 매핑 테이블 S390x 비트맵 방식 STFLE (Store Facility List Ext.) 512비트 비트맵 배열 비트 번호 = 기능 권한 필요 (특권) S390_lowcore stfle_fac_list[4] 비트 번호가 곧 기능 ID MIPS Config 레지스터 MFC0 Rd, Config Config0~Config5 CP0 레지스터 (32비트) 권한 필요 (CP0) struct cpuinfo_mips ases, options 비트맵 레지스터 번호 체계

종합 비교 테이블

아키텍처별 CPU 식별 전체 비교
아키텍처 명령어 레지스터/소스 권한 수준 커널 파일 핵심 구조체 HWCAP 변수
x86/x86_64 CPUID EAX(leaf)/ECX(subleaf) → EAX,EBX,ECX,EDX 비권한 (Ring 0~3) arch/x86/kernel/cpu/common.c struct cpuinfo_x86 ELF_HWCAP (매크로, 제한적)
ARM64 MRS Xt, sysreg ID_AA64ISAR0_EL1, ID_AA64MMFR0_EL1 등 30여 개 EL1 이상 (커널) arch/arm64/kernel/cpufeature.c struct cpuinfo_arm64 elf_hwcap, elf_hwcap2
RISC-V CSR(misa) + DT + SBI mvendorid, marchid, mimpid, misa M-mode(CSR), S-mode(SBI) arch/riscv/kernel/cpufeature.c struct riscv_cpuinfo elf_hwcap
LoongArch CPUCFG rd, rj 레지스터 번호 rj → 결과 rd (32비트) 비권한 (PLV3) arch/loongarch/kernel/cpu-probe.c struct cpuinfo_loongarch elf_hwcap
PowerPC mfspr PVR + DT PVR SPR + ibm,pa-features DT 속성 권한 필요 (SPR) arch/powerpc/kernel/cputable.c struct cpu_spec elf_hwcap, elf_hwcap2
S390x STFLE 512비트 비트맵 배열 (비트 번호 = 기능) 권한 필요 arch/s390/kernel/processor.c S390_lowcore.stfle_fac_list elf_hwcap (제한적)
MIPS MFC0 Config CP0 Config0~Config5 레지스터 권한 필요 (CP0) arch/mips/kernel/cpu-probe.c struct cpuinfo_mips elf_hwcap

설계 철학 심층 비교

각 아키텍처의 CPU 식별 방식은 그 아키텍처가 탄생한 시대와 대상 시장을 반영합니다. 단순함을 추구한 x86, 엄격한 버전 관리를 선택한 ARM64, 개방성을 위해 3중 소스를 도입한 RISC-V, 그리고 비트 번호 자체가 기능 ID인 S390x까지 각기 다른 철학을 지닙니다.

CPU 식별 설계 철학 비교
아키텍처 핵심 설계 원칙 장점 단점/주의점 유사 아키텍처
x86 단일 명령, 리프 인덱스, 비권한 사용자 공간에서 직접 호출 가능, 수십 년간 하위 호환 유지 리프가 700개 이상으로 파편화, subleaf 중첩 복잡 LoongArch (CPUCFG)
ARM64 시스템 레지스터 필드 버전 관리 4비트 필드로 기능 레벨 표현 가능, idreg 위조 감지 가능 EL1 전용, 사용자 공간 직접 조회 불가, 레지스터 수가 많음 없음 (독자 방식)
RISC-V 3중 소스(CSR+DT+SBI) 협력 확장성 최고, 비표준 확장도 DT로 기술 가능 구현 복잡도 높음, 소스 간 불일치 가능성 없음 (오픈 ISA 특유)
LoongArch CPUID와 동일한 비권한 단일 명령 x86 개발자에게 친숙, 빠른 학습 곡선 생태계 역사가 짧아 기능 수가 적음 x86 (CPUID)
PowerPC PVR 모델 번호 + 펌웨어(DT) 협력 IBM 파워 서버의 동적 기능 광고 지원 커널에 CPU 모델별 매핑 테이블 필요, 유지 비용 일부 MIPS
S390x 비트 번호 = 기능 ID (STFLE 비트맵) 새 기능 추가 시 비트 하나만 할당, 극도로 단순한 API 비트맵 크기 한계(현재 512비트), 메인프레임 전용 없음 (IBM 메인프레임 전용)
/* 아키텍처별 CPU 식별 핵심 코드 비교 */

/* 1. x86: CPUID 명령어 직접 호출 (비권한) */
static void cpuid(u32 leaf, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx)
{
    asm volatile("cpuid"
        : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
        : "a"(leaf), "c"(0));
}

/* 2. ARM64: MRS로 시스템 레지스터 읽기 (EL1 전용) */
static inline u64 read_id_aa64isar0_el1(void)
{
    u64 val;
    asm volatile("mrs %0, id_aa64isar0_el1" : "=r"(val));
    return val; /* 4비트 필드 여러 개로 구성 */
}
/* AES 기능: bits[7:4] != 0 이면 지원 */
#define ID_AA64ISAR0_AES_SHIFT  4
#define ID_AA64ISAR0_AES_MASK   0xFULL

/* 3. RISC-V: misa CSR (M-mode) + DT 조합 */
static unsigned long riscv_isa_read(void)
{
    unsigned long misa;
    /* M-mode에서만 접근 가능 */
    asm volatile("csrr %0, misa" : "=r"(misa));
    return misa; /* 비트 A=Atomic, M=Mul/Div, F=Float... */
}

/* 4. S390x: STFLE (비트 번호가 기능 ID) */
static int __stfle(u64 *list, int doublewords)
{
    register unsigned long __nr asm("0") = doublewords - 1;
    asm volatile(".insn s,0xb2b00000,%0" /* STFLE opcode */
        : "=Q"(*list), "+d"(__nr) : : "cc");
    return __nr + 1; /* 기능 비트 45 = "vector" 지원 여부 */
}
LoongArch: x86 CPUID의 가장 충실한 계승자
LoongArch의 CPUCFG 명령어는 x86 CPUID와 거의 동일한 인터페이스를 제공합니다. 레지스터 번호(rj)로 정보 종류를 선택하고, 비권한 상태에서 실행 가능하며, 결과가 32비트 레지스터(rd)에 반환됩니다. 설계진이 x86 생태계 개발자들의 빠른 적응을 의도했음을 알 수 있습니다. 다만 현재(v1.0) 정의된 레지스터 수가 x86보다 훨씬 적어 향후 확장이 예상됩니다.
ARM64 idreg 필드 버전 관리의 함정
ARM64 시스템 레지스터의 각 4비트 필드는 단순히 "있다/없다"가 아니라 기능 레벨(0=없음, 1=기본, 2=강화...)을 나타냅니다. 따라서 cpu_have_feature()는 내부적으로 arm64_ftr_bits[] 테이블을 조회하여 필드 값과 최솟값을 비교합니다. 단순히 비트가 0이 아님을 확인하는 것만으로는 충분하지 않습니다. 또한 커널은 SMP 시스템에서 모든 CPU의 idreg 값 중 "안전한 최솟값"만을 system_cpucaps[]에 기록합니다.

크로스 아키텍처 기능 탐지 실전

실제 코드에서 CPU 기능을 탐지할 때는 커널 내부 API, 사용자 공간 HWCAP, ifunc 리졸버, 그리고 /proc/cpuinfo 파싱이라는 네 가지 방법 중 맥락에 따라 적절한 것을 선택해야 합니다. 이식성과 성능을 동시에 확보하려면 각 방법의 특성과 한계를 명확히 이해해야 합니다.

커널 내부 기능 탐지 패턴

커널 드라이버와 서브시스템 코드에서는 런타임 기능 탐지와 컴파일 타임 가드를 조합하여 사용합니다. 불필요한 런타임 오버헤드를 피하면서 다중 아키텍처를 지원하는 것이 목표입니다.

/*
 * 커널 내부 크로스 아키텍처 기능 탐지 패턴
 * arch/x86, arch/arm64, arch/riscv 모두 동일한 상위 API 지향
 */

/* ── 패턴 1: 컴파일 타임 아키텍처 가드 ─────────────────── */
#ifdef CONFIG_X86
    if (boot_cpu_has(X86_FEATURE_AVX2)) {
        /* x86 AVX2 최적화 경로 */
        aes_encrypt_avx2(ctx, dst, src);
    } else {
        aes_encrypt_generic(ctx, dst, src);
    }
#elif defined(CONFIG_ARM64)
    if (cpu_have_feature(cpu_feature(AES))) {
        /* ARM64 AES 하드웨어 가속 */
        ce_aes_encrypt(ctx, dst, src);
    } else {
        aes_encrypt_generic(ctx, dst, src);
    }
#elif defined(CONFIG_RISCV)
    if (riscv_isa_extension_available(NULL, ZKN)) {
        /* RISC-V Zkn 암호화 확장 */
        zkn_aes_encrypt(ctx, dst, src);
    } else {
        aes_encrypt_generic(ctx, dst, src);
    }
#else
    aes_encrypt_generic(ctx, dst, src);
#endif

/* ── 패턴 2: IS_ENABLED()로 설정 확인 ─────────────────── */
static void init_crypto_engine(void)
{
    /* IS_ENABLED()는 컴파일 타임 상수 → dead code 제거됨 */
    if (IS_ENABLED(CONFIG_CRYPTO_AES_ARM64_CE))
        arm64_ce_aes_register();

    if (IS_ENABLED(CONFIG_CRYPTO_AES_NI_INTEL))
        aesni_intel_register();
}

/* ── 패턴 3: static_cpu_has() — 가장 빠른 런타임 경로 ─── */
static inline void clflush_cache_range(void *addr, unsigned int size)
{
    /*
     * static_cpu_has()는 ALTERNATIVE() 기반:
     * 부팅 후 해당 분기가 무조건 점프 또는 NOP으로 패칭됨
     * → 런타임 조건 분기 비용 0
     */
    if (static_cpu_has(X86_FEATURE_CLFLUSHOPT))
        clflushopt_cache_range(addr, size);
    else
        clflush_cache_range_opt(addr, size);
}
boot_cpu_has() vs static_cpu_has() vs cpu_has() 선택 기준
커널 내부에서는 상황에 따라 세 가지 변형을 구분하여 사용합니다. boot_cpu_has()는 BSP(부트 CPU)의 기능만 참조하며 초기화 코드에서 안전하게 쓸 수 있습니다. cpu_has()는 per-CPU 정보를 참조하여 CPU마다 다를 수 있는 이기종 시스템에서 정확합니다. static_cpu_has()ALTERNATIVE()로 구현되어 런타임 분기 비용이 없지만, 코드 섹션이 읽기 전용(Read-Only)이 된 이후에만 정확합니다. 핫패스(hot path) 코드에서는 반드시 static_cpu_has()를 사용하십시오.

사용자 공간 이식성 탐지: getauxval 패턴

/*
 * 이식 가능한 사용자 공간 CPU 기능 탐지
 * - /proc/cpuinfo 파싱보다 훨씬 빠름 (파일 I/O 없음)
 * - 커널 버전과 무관하게 동작 (ABI 안정)
 * - getauxval(AT_HWCAP)은 O(1) — 보조 벡터 선형 탐색
 */
#include <sys/auxv.h>
#include <stdint.h>
#include <stdbool.h>

/* 아키텍처별 헤더 조건부 포함 */
#if defined(__aarch64__)
#include <asm/hwcap.h>
#elif defined(__riscv)
#include <asm/hwcap.h>
#endif

typedef struct {
    bool has_aes;
    bool has_sha2;
    bool has_simd;    /* NEON/SSE/VMX/V */
    bool has_crc32;
} cpu_caps_t;

static cpu_caps_t detect_cpu_caps(void)
{
    cpu_caps_t caps = {0};
    unsigned long hwcap  = getauxval(AT_HWCAP);
    unsigned long hwcap2 = getauxval(AT_HWCAP2);

    (void)hwcap2; /* 아키텍처에 따라 미사용 경고 억제 */

#if defined(__aarch64__)
    caps.has_aes  = !!(hwcap & HWCAP_AES);
    caps.has_sha2 = !!(hwcap & HWCAP_SHA2);
    caps.has_simd = !!(hwcap & HWCAP_ASIMD);
    caps.has_crc32 = !!(hwcap & HWCAP_CRC32);

#elif defined(__x86_64__)
    /*
     * x86: AT_HWCAP이 제한적 → CPUID 직접 사용 또는
     * __builtin_cpu_supports() (GCC/Clang 내장)
     */
    caps.has_aes  = (int)__builtin_cpu_supports("aes");
    caps.has_sha2 = (int)__builtin_cpu_supports("sha");
    caps.has_simd = (int)__builtin_cpu_supports("avx2");
    caps.has_crc32 = (int)__builtin_cpu_supports("sse4.2");

#elif defined(__riscv)
    /* RISC-V: ISA 확장 비트 */
    caps.has_simd = !!(hwcap & COMPAT_HWCAP_ISA_V);
    caps.has_aes  = false; /* Zkn은 hwcap2로 이동 예정 */

#elif defined(__powerpc64__)
    caps.has_aes  = !!(hwcap2 & PPC_FEATURE2_VEC_CRYPTO);
    caps.has_simd = !!(hwcap  & PPC_FEATURE_HAS_ALTIVEC);
#endif

    return caps;
}

/* ── glibc ifunc 리졸버: 로드 시 최적 구현 선택 ────────── */
static void (*resolve_memcpy(void))(void *, const void *, size_t)
{
    unsigned long hwcap = getauxval(AT_HWCAP);
#if defined(__aarch64__)
    if (hwcap & HWCAP_SVE)
        return __memcpy_aarch64_sve;
    if (hwcap & HWCAP_ASIMD)
        return __memcpy_aarch64_simd;
#elif defined(__x86_64__)
    if (__builtin_cpu_supports("avx512f"))
        return __memcpy_avx512;
    if (__builtin_cpu_supports("avx2"))
        return __memcpy_avx2;
#endif
    return __memcpy_generic;
}
/* ifunc 어트리뷰트로 로드 시 리졸버 자동 호출 */
void *memcpy(void *, const void *, size_t)
    __attribute__((ifunc("resolve_memcpy")));

/proc/cpuinfo 파싱의 함정과 권장 대안

CPU 기능 탐지 방법 비교
방법 속도 이식성 정확성 권장 사용처 주요 문제점
getauxval(AT_HWCAP) O(1), 파일 I/O 없음 Linux 전체 (glibc/musl) 커널이 보증 사용자 공간 기능 탐지의 기본 x86는 HWCAP이 제한적
__builtin_cpu_supports() O(1), 내부 캐시 GCC/Clang, x86/ARM64 컴파일러 구현 의존 x86 상세 기능 (AVX512 등) 컴파일러마다 지원 범위 다름
CPUID 직접 호출 O(1), 직접 명령 x86/LoongArch 전용 하드웨어 직접 보증 x86 저수준 라이브러리 아키텍처 비이식, 가상화 환경 주의
/proc/cpuinfo 파싱 느림 (파일 I/O + 문자열 파싱) Linux 전체 필드명이 아키텍처마다 다름 디버깅(Debugging), 사람이 읽는 도구 아키텍처마다 필드명 다름, 느림
ifunc 리졸버 런타임 0 오버헤드 glibc 지원 플랫폼 로드 시 한 번만 결정 라이브러리 내부 최적 구현 선택 가상 함수 포인터 간접 호출 불가
/proc/self/auxv 읽기 파일 I/O 필요 Linux 전체 커널이 보증 getauxval 없는 환경 (Go 등) 바이너리 파싱 필요, 번거로움
/proc/cpuinfo 파싱 시 반드시 알아야 할 함정
1. 필드명이 아키텍처마다 다릅니다. x86의 기능 필드는 flags:이지만 ARM64는 Features:, RISC-V는 isa:입니다. 대소문자도 다르므로 단순 문자열 검색은 반드시 아키텍처별로 분기해야 합니다.
2. 기능 이름이 아키텍처마다 다릅니다. ARM64 aes와 x86 aes는 같은 이름이지만, ARM64 sha1과 x86 sha_ni는 다른 이름입니다.
3. x86의 flags:는 소프트웨어 에뮬레이션 기능도 포함합니다. 예를 들어 rep_good은 하드웨어 기능이 아니라 커널이 설정한 힌트입니다.
4. 가상화 환경에서 flags:는 하이퍼바이저가 조작할 수 있습니다. KVM이 특정 기능을 숨기거나 추가할 수 있으므로, 보안에 민감한 기능 탐지에는 AT_HWCAP이 더 신뢰할 수 있습니다.
5. 멀티라인 파싱이 필요합니다. /proc/cpuinfo는 CPU 코어마다 동일한 정보를 반복하므로 첫 번째 블록만 파싱하거나 중복 제거 로직이 필요합니다.
크로스 아키텍처 기능 탐지 권장 전략
커널 코드라면 cpu_have_feature() / static_cpu_has() / riscv_isa_extension_available() 등 아키텍처별 API를 #ifdef CONFIG_*로 감싸 사용합니다. 사용자 공간 라이브러리라면 ARM64/RISC-V/PowerPC는 getauxval(AT_HWCAP)를 우선 사용하고, x86은 __builtin_cpu_supports() 또는 CPUID를 직접 호출합니다. /proc/cpuinfo는 디버깅 도구나 사람이 읽는 출력에만 사용하고, 성능에 민감한 경로에서는 절대 피해야 합니다. 다중 아키텍처 지원이 필요한 프로젝트라면 google/cpu_features와 같은 이식성 라이브러리 사용을 검토하십시오.

하이퍼바이저 Leaf (0x40000000+)

x86 CPU는 0x40000000~0x4FFFFFFF 범위를 하이퍼바이저 전용 CPUID 공간으로 예약합니다. 하이퍼바이저는 이 범위를 사용해 자신의 존재와 기능을 게스트 OS에 알립니다. Leaf 1 ECX bit 31이 설정되면 하이퍼바이저가 존재하며, Leaf 0x40000000에서 하이퍼바이저 벤더 이름을 확인할 수 있습니다.

/* arch/x86/kvm/cpuid.c — KVM CPUID 에뮬레이션 */
static void kvm_set_cpuid_entry_hypervisor(struct kvm_cpuid_entry2 *entry)
{
    switch (entry->function) {
    case 0x40000000:
        /* KVM 최대 leaf + 벤더 문자열 "KVMKVMKVM\0\0\0" */
        entry->eax = KVM_CPUID_FEATURES;   /* 최대 하이퍼바이저 leaf = 0x40000001 */
        memcpy(&entry->ebx, "KVMK", 4);
        memcpy(&entry->ecx, "VMKV", 4);
        memcpy(&entry->edx, "M\0\0\0", 4);
        break;

    case 0x40000001:
        /* KVM 파라가상화 기능 플래그 */
        entry->eax = 0;
        if (kvm_clock_supported())
            entry->eax |= (1 << KVM_FEATURE_CLOCKSOURCE2);
        if (kvm_steal_time_supported())
            entry->eax |= (1 << KVM_FEATURE_STEAL_TIME);
        if (kvm_async_pf_supported())
            entry->eax |= (1 << KVM_FEATURE_ASYNC_PF);
        break;
    }
}

/* VMCS/VMCB에서 게스트 CPUID 결과 제어 */
int kvm_emulate_cpuid(struct kvm_vcpu *vcpu)
{
    u32 eax, ebx, ecx, edx;
    kvm_cpuid(vcpu, &eax, &ebx, &ecx, &edx, false);
    /* 게스트 레지스터에 결과 저장 후 RIP += 2 */
    kvm_rax_write(vcpu, eax); kvm_rbx_write(vcpu, ebx);
    kvm_rcx_write(vcpu, ecx); kvm_rdx_write(vcpu, edx);
    return kvm_skip_emulated_instruction(vcpu);
}
주요 하이퍼바이저 벤더 문자열 (Leaf 0x40000000 EBX:ECX:EDX)
하이퍼바이저벤더 문자열기능 Leaf
KVM (Linux)KVMKVMKVM\0\0\00x40000001 (KVM 기능 플래그)
Microsoft Hyper-VMicrosoft Hv0x40000002~0x40000006
VMwareVMwareVMware0x40000010 (포트 I/O 기반)
XenXenVMMXenVMM0x40000001 (Xen 인터페이스 버전)
QEMU TCGTCGTCGTCGTCG0x40000001
KVM 파라가상화 기능 플래그 (Leaf 0x40000001 EAX)
비트상수 이름설명
0KVM_FEATURE_CLOCKSOURCEKVM 클럭소스 v1 (MSR 기반)
1KVM_FEATURE_NOP_IO_DELAYI/O 딜레이 NOP 처리 (성능 개선)
3KVM_FEATURE_CLOCKSOURCE2KVM 클럭소스 v2 (shared memory 기반, 권장)
4KVM_FEATURE_ASYNC_PF비동기 페이지 폴트 (게스트 블로킹 감소)
5KVM_FEATURE_STEAL_TIMESteal Time 통계 (스케줄러(Scheduler) 공정성(Fairness) 개선)
9KVM_FEATURE_PV_TLB_FLUSH파라가상화 TLB 플러시 (VMEXIT 횟수 감소)
11KVM_FEATURE_PV_SEND_IPI파라가상화 IPI 전송
13KVM_FEATURE_POLL_CONTROL게스트가 하이퍼바이저 폴링(Polling) 제어 가능
14KVM_FEATURE_PV_SCHED_YIELDvCPU yield 힌트 전달 (오버커밋 환경 최적화)

하이퍼바이저 탐지 방법: grep hypervisor /proc/cpuinfo로 Leaf 1 ECX bit 31을 확인합니다. cpuid -l 0x40000000 명령으로 벤더 문자열을 직접 읽을 수 있습니다. KVM은 /dev/kvm 존재 여부로도 탐지 가능하며, systemd-detect-virt 명령은 다수의 탐지 방법을 통합합니다.

참고 링크

CPUID와 관련된 다른 주제를 더 깊이 이해하고 싶다면 다음 문서를 참고하세요.