MSR 레지스터 (Model-Specific Register)

x86 MSR(Model-Specific Register) 가이드입니다. RDMSR/WRMSR 명령과 권한 모델, SYSCALL/APIC/PMU/보안/가상화(Virtualization) 계열 MSR, HWP/EPP·RAPL·APERF/MPERF 기반 전력·성능 제어, Intel/AMD 아키텍처 차이, MTRR/PAT 메모리 속성, Linux 커널 MSR API, Spectre 완화 관련 레지스터, KVM MSR 비트맵(Bitmap)과 msr-tools를 활용한 실전 디버깅(Debugging)까지 폭넓게 다룹니다.

관련 표준: Intel SDM Vol.4 (MSR 전체 목록), AMD APM Vol.2 (AMD MSR) — 모델 특화 레지스터의 비트 필드와 용도를 정의하는 프로세서 규격입니다. 종합 목록은 참고자료 — 표준 & 규격 섹션을 참고하세요.
전제 조건: CPU 토폴로지(Topology)어셈블리(Assembly) 문서를 먼저 읽으세요. CPU 구조 주제는 하드웨어 계층과 명령어 수준 제어가 맞물리므로, 코어/캐시(Cache)/레지스터 경계를 먼저 정리해야 합니다.
일상 비유: MSR은 자동차의 숨겨진 설정 메뉴와 비슷합니다. 일반 운전자는 라디오와 에어컨만 조작하지만, 정비사는 엔진 타이밍, 연료 분사 압력, 터보 부스트 레벨 등을 세밀하게 조정할 수 있습니다. MSR도 마찬가지로 커널만 접근할 수 있는 CPU의 고급 제어 인터페이스입니다.

핵심 요약

  • MSR — Model-Specific Register. CPU 동작 모드, 성능, 전력, 보안을 제어하는 특수 레지스터입니다.
  • RDMSR / WRMSR — MSR을 읽고 쓰는 특권 명령어. ECX에 MSR 번호를 지정합니다.
  • 주요 MSR — TSC(타임스탬프), EFER(Long Mode), APIC_BASE, MTRR(메모리 타입), PAT 등이 있습니다.
  • rdmsr / wrmsr 도구 — 사용자 공간(User Space)에서 /dev/cpu/N/msr을 통해 MSR을 읽고 쓸 수 있습니다.

단계별 이해

  1. MSR 개념 잡기 — 일반 레지스터(범용, 세그먼트)와 달리 MSR은 번호(인덱스)로 식별되는 특수 목적 레지스터입니다.

    Intel SDM Vol.4에 수천 개의 MSR이 문서화되어 있습니다.

  2. 읽기 실습sudo rdmsr 0x10으로 TSC(Time Stamp Counter) 값을 읽어봅니다.

    msr-tools 패키지를 설치하고 modprobe msr로 MSR 드라이버를 로드합니다.

  3. 커널 API — 드라이버/커널 코드에서는 rdmsrl()/wrmsrl() 함수로 MSR에 접근합니다.

    잘못된 MSR 쓰기는 시스템 크래시를 유발할 수 있으므로 주의가 필요합니다.

MSR 개요

MSR(Model-Specific Register)은 x86 프로세서에 내장된 특수 레지스터 집합으로, CPU의 동작 모드 제어, 성능 모니터링, 전력 관리, 보안 완화 등 다양한 기능을 담당합니다. "Model-Specific"이라는 이름처럼 원래는 CPU 모델마다 다른 레지스터 세트를 가졌으나, 현재는 많은 MSR이 Intel/AMD 간에 공통으로(architectural) 정의되어 있습니다.

MSR의 역사와 발전

MSR 주소 공간(Address Space)

MSR은 32비트 주소 공간(0x00000000 ~ 0xFFFFFFFF)을 사용하며, 각 MSR은 64비트(EDX:EAX) 값을 가집니다. 주소 범위는 대략 다음과 같이 구분됩니다:

주소 범위용도예시
0x00000000 ~ 0x00001FFFArchitectural MSRIA32_TSC, IA32_APIC_BASE
0x00000100 ~ 0x000001FFFixed/PMC 영역IA32_MTRR*, PMC
0x00000174 ~ 0x00000176SYSENTER MSRIA32_SYSENTER_CS/ESP/EIP
0x00000186 ~ 0x0000018F성능 이벤트 선택IA32_PERFEVTSEL0~7
0x000001A0기능 제어IA32_MISC_ENABLE
0x00000300 ~ 0x000003FFFixed Counter / PMCIA32_FIXED_CTR0~2
0x00000400 ~ 0x00000477MC(Machine Check) 뱅크IA32_MCi_CTL/STATUS
0x00000480 ~ 0x0000049FVMX 기능 보고IA32_VMX_BASIC 등
0x00000800 ~ 0x000008FFx2APICICR, LVT 등
0xC0000000 ~ 0xC0001FFFAMD/Long mode MSRMSR_EFER, MSR_STAR, MSR_LSTAR
0xC0010000 ~ 0xC001FFFFAMD 전용HWCR, SYSCFG, NB_CFG

접근 권한

RDMSR/WRMSR 명령어는 Ring 0(커널 모드)에서만 실행 가능합니다. 유저 공간에서 실행하면 #GP(General Protection) 예외가 발생합니다. 다만 RDTSC, RDTSCP, RDPMC 등 일부 전용 명령어는 CR4 비트 설정에 따라 Ring 3에서도 실행할 수 있습니다.

팁: 유저 공간에서 MSR에 접근하려면 /dev/cpu/<n>/msr 장치 파일 또는 msr-tools 패키지의 rdmsr/wrmsr 유틸리티를 사용합니다. 단, CONFIG_X86_MSR=y로 커널이 빌드되어야 하며, CAP_SYS_RAWIO 권한이 필요합니다.
MC 뱅크 MSR: 0x400~0x477 범위의 MC 뱅크 MSR(IA32_MCi_CTL/STATUS/ADDR/MISC/CTL2)은 Machine Check Architecture의 핵심입니다. 뱅크별 레지스터 구조, STATUS 비트 필드 상세, MCA 에러 코드 분류 등은 MCE — MCA 뱅크와 레지스터 페이지(Page)에서 다룹니다.

RDMSR / WRMSR 명령어

명령어 형식

명령어입력출력설명
RDMSRECX = MSR 주소EDX:EAX = 64비트 값MSR 읽기
WRMSRECX = MSR 주소, EDX:EAX = 값없음MSR 쓰기
RDTSC없음EDX:EAX = TSC 값IA32_TSC 전용 읽기
RDTSCP없음EDX:EAX = TSC, ECX = TSC_AUX직렬화(Serialization)된 TSC 읽기
RDPMCECX = PMC 인덱스EDX:EAX = 카운터 값PMC 전용 읽기

직렬화와 순서 보장(Ordering)

WRMSR직렬화(serializing) 명령어로 분류되어, 이전의 모든 명령어가 완료된 후에 실행됩니다. 반면 RDMSR은 직렬화 명령어가 아니므로, 정확한 순서 보장이 필요하면 앞에 LFENCE 또는 MFENCE를 배치해야 합니다.

; 직렬화된 MSR 읽기 패턴
lfence                ; 이전 명령어 완료 보장
mov  $0x10, %ecx    ; IA32_TSC = 0x10
rdmsr                 ; EDX:EAX = TSC 값
shl  $32, %rdx
or   %rax, %rdx      ; RDX = 64비트 TSC

CPUID를 통한 MSR 존재 확인

특정 MSR에 접근하기 전에 해당 기능의 존재 여부를 CPUID로 확인해야 합니다. 존재하지 않는 MSR에 접근하면 #GP(0) 예외가 발생합니다.

CPUID 리프비트확인 대상
CPUID.01H:EDXbit 5MSR 명령어 지원 (RDMSR/WRMSR)
CPUID.01H:EDXbit 4TSC 지원
CPUID.01H:ECXbit 15PDCM (IA32_PERF_CAPABILITIES)
CPUID.80000001H:EDXbit 20NX bit (IA32_EFER.NXE)
CPUID.80000001H:EDXbit 11SYSCALL/SYSRET
CPUID.07H:EBXbit 0FSGSBASE 명령어
CPUID.07H:EDXbit 26IA32_SPEC_CTRL / IBRS
CPUID.07H:EDXbit 29IA32_ARCH_CAPABILITIES

시스템 제어 MSR

IA32_EFER (0xC0000080) — Extended Feature Enable Register

Long Mode 활성화와 NX bit 제어를 담당하는 핵심 MSR입니다.

비트이름설명
0SCESYSCALL/SYSRET Enable
8LMELong Mode Enable (IA-32e 활성화)
10LMALong Mode Active (읽기 전용(Read-Only), CR0.PG로 활성화)
11NXENo-Execute Enable (W^X 보안)
12SVMESVM Enable (AMD-V 전용)
13LMSLELong Mode Segment Limit Enable (AMD)
14FFXSRFast FXSAVE/FXRSTOR (AMD)
15TCETranslation Cache Extension (AMD)
/* arch/x86/kernel/cpu/common.c — Long Mode 진입 시 EFER 설정 */
static void setup_efer(void)
{
    u64 efer;
    rdmsrl(MSR_EFER, efer);

    efer |= EFER_SCE;        /* SYSCALL enable */
    if (boot_cpu_has(X86_FEATURE_NX))
        efer |= EFER_NX;     /* NX bit enable */
    wrmsrl(MSR_EFER, efer);
}
코드 설명

arch/x86/kernel/cpu/common.c의 EFER MSR 초기화 함수입니다. rdmsrl/wrmsrlarch/x86/include/asm/msr.h에 정의된 64비트 MSR 접근 매크로로, 내부적으로 RDMSR/WRMSR 명령어를 실행합니다.

  • rdmsrl(MSR_EFER, efer)ECX에 MSR 주소 0xC0000080을 넣고 RDMSR을 실행하여 EDX:EAX로 반환된 64비트 값을 efer 변수에 저장합니다. rdmsrlrdmsr의 64비트 래퍼로, 상위/하위 32비트를 자동 병합합니다.
  • efer |= EFER_SCESYSCALL/SYSRET 명령어를 활성화합니다. Long Mode에서 시스템 콜 진입의 필수 전제 조건입니다.
  • efer |= EFER_NXNX(No-Execute) 비트를 활성화하여 W^X 보안 정책을 적용합니다. CPU가 X86_FEATURE_NX를 지원할 때만 설정합니다.
  • wrmsrl(MSR_EFER, efer)수정된 값을 WRMSR 명령어로 EFER에 다시 씁니다. WRMSR은 직렬화 명령어이므로 이전 명령어가 모두 완료된 후 실행됩니다.

IA32_MISC_ENABLE (0x1A0)

다양한 CPU 기능을 활성화/비활성화하는 범용 제어 레지스터입니다.

비트이름설명
0Fast-Strings EnableREP MOVSB/STOSB 최적화
7Performance Monitoring AvailablePMU 사용 가능 여부
11Branch Trace Storage UnavailableBTS 비활성화 (읽기 전용)
12PEBS UnavailablePEBS 비활성화 (읽기 전용)
16Enhanced SpeedStep EnableEIST/P-state 전환 활성화
18ENABLE_MONITOR_FSMMONITOR/MWAIT 활성화
22Limit CPUID MaxvalCPUID 리프 제한 (레거시 OS 호환)
23xTPR Message DisableTPR 메시지 비활성화
34XD Bit DisableNX bit 전역 비활성화

IA32_FEATURE_CONTROL (0x3A)

VMX(가상화) 활성화 및 BIOS 잠금(Lock)을 제어합니다. BIOS/UEFI 펌웨어(Firmware)가 부팅 시 설정하며, Lock bit이 설정되면 재부팅 전까지 변경 불가합니다.

비트이름설명
0Lock1로 설정 시 이 MSR은 읽기 전용 (재부팅까지)
1Enable VMX in SMXTXT 환경에서 VMX 활성화
2Enable VMX outside SMX일반 환경에서 VMX 활성화
8~14SENTER Local Function EnablesTXT SENTER 리프 활성화
15SENTER Global EnableTXT SENTER 전역 활성화
17SGX Launch Control EnableSGX Launch Enclave 제어
18SGX Global EnableSGX 전역 활성화
/* arch/x86/kernel/cpu/feat_ctl.c — VMX 활성화 확인 */
void init_ia32_feat_ctl(struct cpuinfo_x86 *c)
{
    u64 msr;

    rdmsrl(MSR_IA32_FEAT_CTL, msr);
    if (msr & FEAT_CTL_LOCKED)
        return;  /* BIOS가 이미 잠금 */

    /* Lock 비트 없이 열려 있으면 직접 설정 */
    msr = FEAT_CTL_LOCKED;
    msr |= FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX;
    wrmsrl(MSR_IA32_FEAT_CTL, msr);
}
코드 설명

arch/x86/kernel/cpu/feat_ctl.c에서 IA32_FEATURE_CONTROL(0x3A) MSR을 초기화하는 함수입니다. 이 MSR은 VMX(가상화) 활성화와 BIOS 잠금(Lock)을 제어하며, 인덱스 상수는 arch/x86/include/asm/msr-index.h에 정의되어 있습니다.

  • rdmsrl(MSR_IA32_FEAT_CTL, msr)현재 IA32_FEATURE_CONTROL 값을 읽습니다. BIOS/UEFI가 부팅 시 VMX 비트와 Lock 비트를 설정하는 것이 일반적입니다.
  • msr & FEAT_CTL_LOCKED비트 0(Lock)이 설정되어 있으면 이 MSR은 재부팅 전까지 읽기 전용이 되므로, 더 이상 수정할 수 없어 즉시 반환합니다.
  • msr = FEAT_CTL_LOCKEDLock 비트가 설정되지 않은 경우(BIOS가 초기화하지 않은 경우), 커널이 직접 Lock 비트를 포함한 값을 구성합니다.
  • FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX비트 2를 설정하여 일반 환경(SMX 외부)에서 VMX를 활성화합니다. KVM 등 하이퍼바이저가 VMXON을 실행하려면 이 비트가 필수입니다.

SYSCALL / SYSRET MSR

Long Mode에서 SYSCALL/SYSRET 명령어는 시스템 콜(System Call) 진입/복귀를 위해 3개의 MSR을 사용합니다. 이 MSR들은 IA32_EFER.SCE=1일 때만 유효합니다.

MSR주소용도
MSR_STAR0xC0000081SYSCALL의 CS/SS 선택자 (비트 47:32 = kernel CS, 비트 63:48 = user CS)
MSR_LSTAR0xC0000082SYSCALL 진입점(Entry Point)의 64비트 RIP (Long Mode)
MSR_CSTAR0xC0000083SYSCALL 진입점 RIP (Compatibility Mode, Linux 미사용)
MSR_SYSCALL_MASK0xC0000084SYSCALL 시 RFLAGS에 적용할 마스크 (마스크된 비트 = 0)
/* arch/x86/kernel/cpu/common.c — SYSCALL MSR 초기화 */
void syscall_init(void)
{
    wrmsr(MSR_STAR, 0,
          (__USER32_CS << 16) | __KERNEL_CS);  /* STAR 상위 32비트 */
    wrmsrl(MSR_LSTAR,
          (unsigned long)entry_SYSCALL_64);   /* 시스템 콜 진입점 */

#ifdef CONFIG_IA32_EMULATION
    wrmsrl(MSR_CSTAR,
          (unsigned long)entry_SYSCALL_compat);
#endif

    /* IF, TF, DF, AC 플래그 마스킹 — 인터럽트/디버그 비활성화 */
    wrmsrl(MSR_SYSCALL_MASK,
          X86_EFLAGS_TF | X86_EFLAGS_DF |
          X86_EFLAGS_IF | X86_EFLAGS_AC);
}
SYSCALL 동작: SYSCALL 실행 시 CPU는 (1) RCX에 리턴 주소(RIP) 저장, (2) R11에 RFLAGS 저장, (3) CS/SS를 STAR에서 로드, (4) RIP를 LSTAR에서 로드, (5) RFLAGS를 SYSCALL_MASK로 마스킹합니다. 스택 전환은 CPU가 하지 않으며, 커널이 직접 per-CPU 영역에서 RSP를 교체합니다.

세그먼트 Base MSR (FS/GS)

Long Mode에서 FS/GS 세그먼트의 base 주소는 GDT가 아닌 MSR로 관리됩니다. Linux 커널은 GS_BASE를 per-CPU 데이터 포인터로, FS_BASE를 유저 공간 TLS(Thread-Local Storage)로 사용합니다.

MSR주소용도
IA32_FS_BASE0xC0000100FS 세그먼트 base (유저: TLS/glibc)
IA32_GS_BASE0xC0000101GS 세그먼트 base (현재 활성 GS)
IA32_KERNEL_GS_BASE0xC0000102SWAPGS로 교환할 GS base (대기 값)

SWAPGS 메커니즘

SWAPGS는 IA32_GS_BASE와 IA32_KERNEL_GS_BASE를 원자적(Atomic)으로 교환합니다. 시스템 콜/인터럽트(Interrupt) 진입 시 커널은 SWAPGS를 실행하여 per-CPU 포인터를 GS에 로드하고, 복귀 시 다시 SWAPGS로 유저의 GS를 복원합니다.

; entry_SYSCALL_64 진입부 (arch/x86/entry/entry_64.S)
SYM_CODE_START(entry_SYSCALL_64)
    swapgs                        ; GS = per-CPU base (커널용)
    mov  %rsp, PER_CPU_VAR(cpu_tss_rw + TSS_sp2)
    SWITCH_TO_KERNEL_CR3 scratch_reg=%rsp  ; KPTI: 커널 페이지 테이블 전환
    mov  PER_CPU_VAR(cpu_current_top_of_stack), %rsp
    ; ... 레지스터 저장, 시스템 콜 디스패치

FSGSBASE 명령어 (Linux 5.3+)

FSGSBASE CPU 기능(CPUID.07H:EBX.FSGSBASE[bit 0])이 있으면, RDFSBASE/WRFSBASE/RDGSBASE/WRGSBASE 명령어로 WRMSR 없이 직접 FS/GS base를 읽고 쓸 수 있습니다. 이 명령어들은 비직렬화이며 매우 빠릅니다(~10 사이클 vs WRMSR ~100 사이클).

/* arch/x86/include/asm/fsgsbase.h */
static inline unsigned long rdgsbase(void)
{
    unsigned long gsbase;
    asm volatile("rdgsbase %0" : "=r"(gsbase) :: "memory");
    return gsbase;
}

static inline void wrgsbase(unsigned long gsbase)
{
    asm volatile("wrgsbase %0" :: "r"(gsbase) : "memory");
}
주의: CR4.FSGSBASE=1이어야 FSGSBASE 명령어가 Ring 3에서도 사용 가능합니다. Linux 5.3부터 기본 활성화되며, 그 전에는 WRMSR로만 FS/GS base를 변경했습니다. FSGSBASE 활성화 시 SWAPGS 대신 WRGSBASE를 사용할 수 있지만, Linux은 호환성을 위해 SWAPGS를 유지합니다.

APIC MSR

IA32_APIC_BASE (0x1B)

Local APIC의 기본 주소와 활성화 상태를 제어합니다.

비트이름설명
8BSPBootstrap Processor 플래그 (읽기 전용)
10EXTDx2APIC 모드 활성화
11ENAPIC 전역 활성화
12~35APIC BaseAPIC MMIO base 주소 (기본: 0xFEE00000)

x2APIC 모드 MSR (0x800~0x8FF)

x2APIC 모드에서는 MMIO 대신 MSR을 통해 APIC 레지스터에 접근합니다. 이는 MMIO보다 빠르고 32비트 APIC ID를 지원하여 256 CPU 이상의 시스템을 지원할 수 있습니다.

MSR 주소xAPIC MMIO 오프셋(Offset)이름접근
0x8020x20APIC IDR
0x8030x30APIC VersionR
0x8080x80Task Priority (TPR)R/W
0x80B0xB0EOIW
0x80F0xF0Spurious Interrupt VectorR/W
0x8300x300+0x310ICR (64비트 통합)R/W
0x832~8380x320~380LVT (Timer, Thermal, PMI 등)R/W
0x83FSelf IPIW
/* arch/x86/kernel/apic/x2apic_cluster.c — x2APIC IPI 전송 */
static void x2apic_send_IPI(int cpu, int vector)
{
    u32 dest = per_cpu(x86_cpu_to_logical_apicid, cpu);

    /* x2APIC ICR: MSR 0x830, 하위 32비트 = vector+flags, 상위 32비트 = dest */
    wrmsrl(APIC_BASE_MSR + (APIC_ICR >> 4),
          ((u64)dest << 32) | APIC_DM_FIXED | vector);
}
x2APIC 장점: (1) MSR 접근은 MMIO보다 빠름 (캐시 불가 메모리 접근 불필요), (2) ICR이 64비트 단일 레지스터로 통합되어 원자적 IPI 전송 가능, (3) 32비트 APIC ID로 최대 2^32개 논리 CPU 지원.

성능 모니터링 MSR (PMU)

Performance Monitoring Unit(PMU) MSR은 CPU 이벤트(캐시 미스, 분기 예측(Branch Prediction) 실패, 명령어 은퇴 등)를 하드웨어 수준에서 카운팅합니다. perf 도구가 내부적으로 이 MSR들을 프로그래밍합니다.

Architectural PMU (버전 2+)

MSR주소설명
IA32_PERFEVTSELx0x186 + x이벤트 선택 레지스터 (이벤트/umask/usr/os/edge 등)
IA32_PMCx0x0C1 + x범용 성능 카운터 (48비트)
IA32_FIXED_CTR00x309Instruction Retired (고정 카운터)
IA32_FIXED_CTR10x30AUnhalted Core Cycles (고정 카운터)
IA32_FIXED_CTR20x30BUnhalted Reference Cycles (고정 카운터)
IA32_FIXED_CTR_CTRL0x38D고정 카운터 제어 (OS/USR/PMI 설정)
IA32_PERF_GLOBAL_CTRL0x38F전역 카운터 활성화 비트맵
IA32_PERF_GLOBAL_STATUS0x38E오버플로 상태 (PMI 인터럽트 원인)
IA32_PERF_GLOBAL_OVF_CTRL0x390오버플로 상태 클리어

IA32_PERFEVTSELx 비트 필드

IA32_PERFEVTSELx 비트 필드 (64-bit MSR) 63 ↑ MSB LSB ↑ Reserved [63:32] INV [23] CNT [22] ANY [21] EN [22] INT [20] PC [19] E [18] UnitMask [15:8] Event Select [7:0] 주요 비트: EN[22] — 카운터 활성화 INT[20] — 오버플로 시 PMI 인터럽트 E[18] — OS 이벤트 (Ring 0) USR[16] — Ring 3 이벤트 카운팅 INV[23] — 조건 반전 (threshold 미만 카운팅) CNT[22] — CMASK: 사이클당 이벤트 수 임계값 ANY[21] — 하이퍼스레드 공유 시 모든 스레드 이벤트 합산
/* L3 캐시 미스 이벤트 프로그래밍 예 */
#define PERFEVTSEL_EN      (1ULL << 22)
#define PERFEVTSEL_OS      (1ULL << 17)
#define PERFEVTSEL_USR     (1ULL << 16)

/* Event 0x2E, Umask 0x41 = LLC-misses (Skylake 기준) */
u64 evtsel = 0x2E | (0x41 << 8) |
            PERFEVTSEL_EN | PERFEVTSEL_OS | PERFEVTSEL_USR;
wrmsrl(MSR_P6_EVNTSEL0, evtsel);

/* 카운터 읽기 */
u64 count;
rdmsrl(MSR_P6_PERFCTR0, count);
pr_info("LLC misses: %llu\\n", count);
팁: perf stat -e cache-misses,instructions,cycles ./program 명령은 내부적으로 이 PMU MSR들을 프로그래밍합니다. 커널 드라이버 arch/x86/events/intel/core.c가 MSR 접근을 추상화합니다.

전력/주파수 MSR

현대 x86 CPU는 MSR을 통해 동적 주파수/전압 조절(DVFS)을 제어합니다. Linux의 cpufreq 드라이버(intel_pstate, amd-pstate)가 이 MSR들을 직접 사용하여 CPU의 성능 수준을 조절합니다. 이 섹션에서는 P-State 제어, HWP(Hardware P-States), RAPL(전력 제한), C-State 레지던시, AMD CPPC 등 전력/주파수 관련 MSR 전체를 상세히 다룹니다.

핵심 용어:
  • P-State: Performance State — CPU 주파수/전압 조합. P0이 최고 성능, 숫자가 클수록 저전력.
  • C-State: CPU Idle State — C0(활성), C1(Halt), C3(Sleep), C6(Deep Sleep) 등. 깊을수록 절전.
  • FID: Frequency ID — 주파수 배수(ratio). Base Clock × FID = 실제 주파수.
  • VID: Voltage ID — 전압 식별자. FID와 함께 P-State를 정의.
  • DVFS: Dynamic Voltage and Frequency Scaling — 부하에 따라 주파수와 전압을 동적 조절.
  • EPP: Energy Performance Preference — HWP에서 성능 vs 절전 우선순위 (0=성능, 255=절전).
  • RAPL: Running Average Power Limit — 패키지/코어/DRAM의 전력 소비를 제한하고 측정.
레거시 P-State (EIST) HWP (Speed Shift) 공통 피드백 OS cpufreq governor IA32_PERF_CTL (0x199) CPU P-State 전환 IA32_PERF_STATUS (0x198) 전환 지연: ~10μs OS가 매번 결정 OS: HWP_REQUEST (0x774) Min/Max/Desired + EPP HW 자율 P-State 선택 (부하/온도/전력 종합 판단) HWP_STATUS (0x777) 전환 지연: ~1ms → ~30μs HW가 자율 결정 APERF/MPERF (0xE8/E7) 실제 주파수 측정 RAPL (0x610~0x619) 전력 소비 측정/제한 C-State 레지던시

MSR_PLATFORM_INFO (0xCE) — 플랫폼 주파수 정보

CPU의 기본 주파수 비율(ratio), 최소/최대 비율, TDP 관련 정보를 담고 있는 읽기 전용 MSR입니다. intel_pstate 드라이버가 초기화 시 가장 먼저 읽는 MSR입니다.

비트필드설명
15:8Maximum Non-Turbo Ratio기본 주파수 비율 (Base Frequency = BCLK × 이 값). 예: 0x22 = 34 → 3.4GHz (BCLK 100MHz)
23:16예약됨
28Programmable Ratio Limit for Turbo1이면 MSR_TURBO_RATIO_LIMIT 프로그래밍 가능
29Programmable TDP Limit for Turbo1이면 TDP 제한 변경 가능
30Programmable TJ Offset1이면 TjMax 오프셋 변경 가능
40:32Maximum Efficiency Ratio최소 효율 주파수 비율 (최저 P-State). 예: 0x04 = 400MHz
47:41예약됨
55:48Minimum Operating Ratio절대 최소 동작 비율 (C-State 진입 직전 최저 주파수)
/* drivers/cpufreq/intel_pstate.c — 플랫폼 정보에서 주파수 범위 결정 */
static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
{
    u64 value;

    rdmsrl(MSR_PLATFORM_INFO, value);

    /* Base Frequency ratio (bits 15:8) */
    cpu->pstate.max_pstate = (value >> 8) & 0xFF;
    /* Minimum Efficiency ratio (bits 40:32) */
    cpu->pstate.min_pstate = (value >> 32) & 0xFF;
    /* 예: max_pstate=34 → 3.4GHz, min_pstate=4 → 400MHz (BCLK=100MHz) */

    rdmsrl(MSR_TURBO_RATIO_LIMIT, value);
    cpu->pstate.turbo_pstate = value & 0xFF;  /* 1코어 활성 시 최대 터보 비율 */

    pr_debug("cpu %d: min=%d base=%d turbo=%d\\n",
             cpu->cpu, cpu->pstate.min_pstate,
             cpu->pstate.max_pstate, cpu->pstate.turbo_pstate);
}

IA32_PERF_CTL / IA32_PERF_STATUS — P-State 제어 (레거시 EIST)

Enhanced Intel SpeedStep Technology(EIST)에서 OS가 직접 목표 P-State를 설정하고, 현재 P-State를 읽는 데 사용하는 MSR 쌍입니다. HWP 활성화 시에도 레거시 호환을 위해 존재하지만, 실질적 제어는 HWP MSR이 담당합니다.

IA32_PERF_CTL (0x199) — 목표 P-State 설정

비트필드설명
7:0Target Ratio목표 주파수 비율 (BCLK × ratio = 목표 주파수)
15:8예약됨 (일부 모델에서 VID 인코딩)
16IDA/Turbo Disengage1이면 해당 코어의 터보 부스트 비활성화
31:17예약됨
32IDA/Turbo Disengage (64-bit)일부 모델에서 추가 터보 제어

IA32_PERF_STATUS (0x198) — 현재 P-State (읽기 전용)

비트필드설명
15:0Current Performance State Value현재 동작 중인 P-State 값 (ratio + VID 인코딩)
/* 레거시 P-State 전환 — acpi-cpufreq 드라이버 */
/* drivers/cpufreq/acpi-cpufreq.c */
static void do_drv_write(void *_cmd)
{
    struct drv_cmd *cmd = _cmd;

    /* MSR_IA32_PERF_CTL에 목표 비율 쓰기 */
    wrmsrl(MSR_IA32_PERF_CTL,
           cmd->val | (rdmsrl(MSR_IA32_PERF_CTL) & ~0xFFFFULL));
    /*
     * P-State 전환은 즉각적이지 않음:
     * - Intel: ~10μs (전압/주파수 안정화)
     * - 전환 중 IA32_PERF_STATUS 값이 점진적으로 변경
     * - IA32_MISC_ENABLE[16] (EIST Enable)이 1이어야 동작
     */
}
코드 설명

drivers/cpufreq/acpi-cpufreq.c의 레거시 P-State 전환 코드입니다. IA32_PERF_CTL(0x199) MSR에 목표 주파수 비율을 기록하여 CPU의 동작 주파수를 변경합니다.

  • wrmsrl(MSR_IA32_PERF_CTL, ...)IA32_PERF_CTL의 하위 16비트에 목표 비율(Target Ratio)을 기록합니다. 비트 7:0이 주파수 비율이며, BCLK(보통 100MHz) × ratio = 목표 주파수입니다.
  • rdmsrl(MSR_IA32_PERF_CTL) & ~0xFFFFULL현재 MSR 값의 상위 비트(예: 터보 부스트 비활성화 비트 등)를 보존하기 위해 하위 16비트만 마스킹하고 OR 연산으로 새 비율을 삽입합니다.
  • P-State 전환 지연CPU 내부적으로 전압과 주파수를 동시에 조정하므로 약 10마이크로초(us)의 안정화 시간이 필요합니다. 전환 중에는 IA32_PERF_STATUS(0x198)에서 현재 상태를 확인할 수 있습니다.
주의: IA32_MISC_ENABLE(0x1A0)의 비트 16(Enhanced SpeedStep Enable)이 0이면 EIST가 비활성화되어 IA32_PERF_CTL 쓰기가 무시됩니다. BIOS에서 SpeedStep을 활성화해야 합니다.

MSR_TURBO_RATIO_LIMIT (0x1AD) — 터보 부스트 비율 제한

활성 코어 수에 따른 최대 터보 부스트 비율을 정의합니다. 활성 코어가 적을수록 더 높은 주파수까지 부스트할 수 있습니다 (열/전력 여유분 활용).

비트필드설명
7:01C Max Turbo Ratio1코어 활성 시 최대 비율 (가장 높음)
15:82C Max Turbo Ratio2코어 활성 시 최대 비율
23:163C Max Turbo Ratio3코어 활성 시 최대 비율
31:244C Max Turbo Ratio4코어 활성 시 최대 비율
39:325C Max Turbo Ratio5코어 활성 시 최대 비율
47:406C Max Turbo Ratio6코어 활성 시 최대 비율
55:487C Max Turbo Ratio7코어 활성 시 최대 비율
63:568C Max Turbo Ratio8코어 활성 시 최대 비율
/* 터보 비율 파싱 — turbostat 도구 로직과 유사 */
static void dump_turbo_ratios(void)
{
    u64 turbo_limit;
    int i;

    rdmsrl(MSR_TURBO_RATIO_LIMIT, turbo_limit);
    for (i = 0; i < 8; i++) {
        u8 ratio = (turbo_limit >> (i * 8)) & 0xFF;
        pr_info("%dC turbo: %d (%d MHz)\\n",
               i + 1, ratio, ratio * 100);
    }
    /*
     * 출력 예시 (i7-12700):
     * 1C turbo: 49 (4900 MHz)   ← 단일 코어 최대
     * 2C turbo: 49 (4900 MHz)
     * 3C turbo: 48 (4800 MHz)
     * 4C turbo: 47 (4700 MHz)
     * ...
     * 8C turbo: 46 (4600 MHz)   ← 전코어 터보
     */
}

/* 코어 수가 8 이상인 CPU는 추가 MSR 사용:
 * MSR_TURBO_RATIO_LIMIT1 (0x1AE): 코어 9~16
 * MSR_TURBO_RATIO_LIMIT2 (0x1AF): 코어 17~24
 * MSR_TURBO_RATIO_LIMIT3 (0x1AC): 그룹별 비율 (Atom 계열)
 */
실용 팁: turbostat 도구는 이 MSR을 읽어 코어 수별 최대 터보 주파수를 표시합니다: turbostat --show Core,CPU,Bzy_MHz,TSC_MHz,Busy%,PkgWatt

HWP (Hardware P-States) — Intel Speed Shift

HWP는 Skylake(6세대) 이후 Intel CPU에서 지원하는 하드웨어 자율 P-State 관리 기술입니다. 기존 EIST에서는 OS가 매번 목표 P-State를 결정해야 했으나, HWP에서는 OS가 성능 범위와 선호도만 설정하면 CPU 하드웨어가 부하, 온도, 전력 상태를 종합적으로 판단하여 최적의 P-State를 자율 선택합니다. P-State 전환 응답 시간이 ~10ms에서 ~30μs로 대폭 개선됩니다.

IA32_PM_ENABLE (0x770) — HWP 활성화

비트필드설명
0HWP_ENABLE1로 설정하면 HWP 활성화. 비가역적: 한 번 활성화하면 재부팅 전까지 비활성화 불가.
63:1예약됨 (0으로 유지)
비가역적 MSR: IA32_PM_ENABLE에 1을 쓰면 HWP가 영구(재부팅까지) 활성화됩니다. 다시 0으로 되돌릴 수 없으므로, 커널 드라이버는 부팅 초기에 한 번만 설정합니다.

IA32_HWP_CAPABILITIES (0x771) — 하드웨어 성능 범위 (읽기 전용)

CPU가 보고하는 P-State 범위. OS는 이 범위 내에서 HWP_REQUEST를 설정해야 합니다.

비트필드설명
7:0Highest_Performance최대 성능 수준 (터보 포함). 예: 49 → 4.9GHz
15:8Guaranteed_Performance지속 보장 성능 수준 (Base Freq). 예: 34 → 3.4GHz
23:16Most_Efficient_Performance최고 에너지 효율 성능 수준. 예: 8 → 800MHz
31:24Lowest_Performance절대 최저 성능 수준. 예: 4 → 400MHz
/* IA32_HWP_CAPABILITIES 파싱 */
static void read_hwp_caps(struct cpudata *cpu)
{
    u64 cap;
    rdmsrl(MSR_IA32_HWP_CAPABILITIES, cap);

    cpu->hwp_cap.highest  = HWP_HIGHEST_PERF(cap);      /* bits 7:0   */
    cpu->hwp_cap.guaranteed = HWP_GUARANTEED_PERF(cap);  /* bits 15:8  */
    cpu->hwp_cap.efficient = HWP_MOSTEFFICIENT_PERF(cap); /* bits 23:16 */
    cpu->hwp_cap.lowest   = HWP_LOWEST_PERF(cap);       /* bits 31:24 */

    /*
     * 실제 예시 (Intel i9-13900K):
     *   Highest=49, Guaranteed=34, Efficient=8, Lowest=4
     *   → 4.9GHz 최대, 3.4GHz 보장, 800MHz 효율, 400MHz 최저
     */
}

IA32_HWP_REQUEST (0x774) — OS → HW 성능 요청 (코어별)

OS가 HWP에 전달하는 성능 힌트. 각 논리 코어별로 독립 설정 가능합니다.

비트필드설명
7:0Minimum_Performance허용 최저 성능. 이 이하로 내려가지 않음. 절전 vs 응답 지연(Latency) 트레이드오프.
15:8Maximum_Performance허용 최대 성능. 이 이상 올라가지 않음. 전력/발열 제한에 활용.
23:16Desired_Performance목표 성능 힌트. 0이면 HW가 자율 결정 (자율 모드).
31:24Energy_Performance_PreferenceEPP 값 (0~255): 0=최대 성능, 128=균형, 255=최대 절전.
41:32Activity_Window부하 모니터링 창 크기 (10비트: 7비트 값 + 3비트 지수). 0이면 HW 기본값.
42Package_Control1이면 이 코어의 요청이 패키지 레벨에서 적용
59:43예약됨
63:60예약됨

EPP (Energy Performance Preference) 상세

EPP는 HWP에서 성능과 에너지 효율 사이의 균형점을 OS가 CPU에 전달하는 핵심 파라미터입니다. 값이 작을수록 성능 우선, 클수록 절전 우선입니다.

EPP 값Linux 정책sysfs 값동작 특성
0performanceperformance최대 터보 부스트, 전력 무제한. 고성능 서버/게이밍.
64balance_performancebalance_performance성능 우선이지만 약간의 절전 고려. 데스크톱 기본값.
128default/normaldefault성능과 절전 균형. 일반 워크로드.
192balance_powerbalance_power절전 우선이지만 합리적 성능 유지. 노트북 배터리 모드.
255powerpower최대 절전, 최저 주파수 유지. 극한 배터리 절약.
/* drivers/cpufreq/intel_pstate.c — HWP_REQUEST 설정 (전체 필드) */
static void intel_pstate_hwp_set(struct cpudata *cpu)
{
    u64 value, prev;
    s16 epp;

    rdmsrl_on_cpu(cpu->cpu, MSR_IA32_HWP_REQUEST, &prev);

    value  = HWP_MIN_PERF(cpu->pstate.min_pstate);      /* bits 7:0   */
    value |= HWP_MAX_PERF(cpu->pstate.max_pstate);      /* bits 15:8  */
    value |= HWP_DESIRED_PERF(0);                       /* bits 23:16 — 0 = 자율 모드 */
    epp = intel_pstate_get_epp(cpu);
    value |= HWP_ENERGY_PERF_PREFERENCE(epp);            /* bits 31:24 */

    if (value != prev) {
        wrmsrl_on_cpu(cpu->cpu, MSR_IA32_HWP_REQUEST, value);
        trace_cpu_frequency(cpu->pstate.max_pstate * cpu->pstate.scaling, cpu->cpu);
    }
}
sysfs에서 EPP 설정:
echo "balance_performance" > /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference
cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_available_preferences 로 사용 가능한 값 확인.

IA32_HWP_REQUEST_PKG (0x772) — 패키지 레벨 HWP 요청

패키지(소켓(Socket)) 전체에 대한 HWP 요청을 설정합니다. 코어별 HWP_REQUEST에서 Package_Control 비트(42)가 0인 경우 이 MSR의 값이 기본 적용됩니다.

비트필드설명
7:0Minimum_Performance패키지 전체 최저 성능
15:8Maximum_Performance패키지 전체 최대 성능
23:16Desired_Performance패키지 전체 목표 성능
31:24Energy_Performance_Preference패키지 전체 EPP
41:32Activity_Window패키지 전체 활동 창

IA32_HWP_INTERRUPT (0x773) — HWP 변경 알림 인터럽트

비트필드설명
0EN_Guaranteed_Performance_ChangeGuaranteed Performance가 변경될 때 인터럽트 발생
1EN_Excursion_Minimum성능이 Minimum 이하로 떨어질 때 인터럽트 발생
2EN_Highest_Performance_ChangeHighest Performance가 변경될 때 인터럽트 발생

IA32_HWP_STATUS (0x777) — HWP 상태 피드백

비트필드설명
0Guaranteed_Performance_Change1이면 보장 성능 수준이 변경됨 (W1C: 1 쓰면 클리어)
2Excursion_To_Minimum1이면 성능이 요청 최소치 이하로 내려갔음 (W1C)
4Highest_Performance_Change1이면 최대 성능 수준이 변경됨 (W1C)
/* HWP 인터럽트 핸들러 — 보장 성능 변경 처리 */
void intel_pstate_irq_handler(void)
{
    u64 status;
    rdmsrl(MSR_IA32_HWP_STATUS, status);

    if (status & 0x1) {
        /* Guaranteed Performance 변경 — 열 스로틀링 등으로 발생 */
        u64 cap;
        rdmsrl(MSR_IA32_HWP_CAPABILITIES, cap);
        pr_info("HWP guaranteed perf changed to %llu\\n",
               (cap >> 8) & 0xFF);
        /* 상태 비트 클리어 (W1C) */
        wrmsrl(MSR_IA32_HWP_STATUS, status);
    }
}

APERF / MPERF — 실제 주파수 측정

APERF와 MPERF는 CPU의 실제 동작 주파수와 활용률을 측정하는 핵심 카운터 쌍입니다. 두 카운터 모두 C0 상태(활성)에서만 증가하며, C-State(유휴)에서는 멈춥니다.

MSR주소동작
IA32_MPERF0xE7Maximum Performance 카운터. 최대 비터보 주파수(TSC rate)에 비례하여 증가. C0 상태에서만 증가.
IA32_APERF0xE8Actual Performance 카운터. 실제 동작 클럭에 비례하여 증가. 터보 부스트/스로틀링 반영.

APERF/MPERF를 이용한 핵심 계산

/*
 * 두 시점(t0, t1) 사이의 델타를 이용한 계산:
 *
 * 1) 실제 평균 주파수:
 *    actual_freq = base_freq × (ΔAPERF / ΔMPERF)
 *    - ΔAPERF > ΔMPERF → 터보 부스트 활성 (base_freq 초과)
 *    - ΔAPERF < ΔMPERF → 스로틀링 발생 (base_freq 미만)
 *    - ΔAPERF = ΔMPERF → 정확히 base_freq로 동작
 *
 * 2) C0 레지던시 (CPU 활용률):
 *    busy% = ΔMPERF / ΔTSC × 100
 *    - TSC는 항상 일정 비율로 증가 (invariant TSC)
 *    - MPERF는 C0에서만 증가
 *    → ΔMPERF/ΔTSC = C0 상태에서 보낸 시간 비율
 *
 * 3) 평균 주파수 (유휴 포함):
 *    avg_freq = TSC_freq × (ΔAPERF / ΔTSC)
 *    - 전체 시간 대비 실제 작업량 반영
 */

struct freq_sample {
    u64 aperf, mperf, tsc;
};

static void snapshot(struct freq_sample *s)
{
    unsigned long flags;
    local_irq_save(flags);
    rdmsrl(MSR_IA32_APERF, s->aperf);
    rdmsrl(MSR_IA32_MPERF, s->mperf);
    s->tsc = rdtsc_ordered();
    local_irq_restore(flags);
}

static void calc_freq(struct freq_sample *s0, struct freq_sample *s1)
{
    u64 da = s1->aperf - s0->aperf;
    u64 dm = s1->mperf - s0->mperf;
    u64 dt = s1->tsc   - s0->tsc;
    u64 base_khz = tsc_khz;  /* 커널 전역 변수 */

    pr_info("actual_freq = %llu MHz\\n",
           div64_u64(base_khz * da, dm) / 1000);
    pr_info("C0 busy = %llu.%llu%%\\n",
           dm * 100 / dt, (dm * 10000 / dt) % 100);
    pr_info("avg_freq (incl idle) = %llu MHz\\n",
           div64_u64(base_khz * da, dt) / 1000);
}
turbostat와의 관계: turbostatBzy_MHz 컬럼은 base_freq × ΔAPERF/ΔMPERF를, Busy%ΔMPERF/ΔTSC × 100을 보여줍니다. Avg_MHzbase_freq × ΔAPERF/ΔTSC입니다.

RAPL (Running Average Power Limit) MSR

RAPL은 Intel Sandy Bridge 이후 도입된 전력 측정 및 제한 메커니즘으로, 패키지/코어/DRAM/GPU의 실시간(Real-time) 전력 소비를 모니터링하고, 전력 상한을 설정할 수 있습니다. 커널의 intel_rapl 드라이버와 powercap 프레임워크가 이 MSR들을 추상화합니다.

RAPL 도메인 (Power Domain)

도메인약칭측정/제한 대상주요 MSR 기반 주소
Package (PKG)PKGCPU 패키지 전체 (코어 + Uncore + GPU)0x610 ~ 0x614
Power Plane 0 (PP0)PP0CPU 코어 영역만0x638 ~ 0x63A
Power Plane 1 (PP1)PP1내장 GPU (클라이언트 CPU만)0x640 ~ 0x642
DRAMDRAM메모리 컨트롤러/DRAM (서버 CPU)0x618 ~ 0x61C
Platform (PSys)PSYS전체 시스템 (Skylake+, 노트북)0x64C ~ 0x650

MSR_RAPL_POWER_UNIT (0x606) — 전력 단위

RAPL MSR의 값을 물리 단위로 변환하기 위한 단위 정보입니다. 모든 RAPL 값 해석의 기준이 됩니다.

비트필드설명
3:0Power Units전력 단위 = 1/(2^n) 와트. 보통 n=3 → 0.125W
12:8Energy Status Units에너지 단위 = 1/(2^n) 줄. 보통 n=14 → ~61μJ
19:16Time Units시간 단위 = 1/(2^n) 초. 보통 n=10 → ~976μs
/* RAPL 단위 파싱 */
static void parse_rapl_units(void)
{
    u64 units;
    double power_unit, energy_unit, time_unit;

    rdmsrl(MSR_RAPL_POWER_UNIT, units);

    power_unit  = 1.0 / (1 << (units & 0xF));         /* 보통 0.125W  */
    energy_unit = 1.0 / (1 << ((units >> 8) & 0x1F));  /* 보통 ~61μJ   */
    time_unit   = 1.0 / (1 << ((units >> 16) & 0xF)); /* 보통 ~976μs  */

    /* 예: Power Unit=3 → 1/8=0.125W
     *     Energy Unit=14 → 1/16384 ≈ 61.04μJ
     *     Time Unit=10 → 1/1024 ≈ 976.6μs
     */
}

MSR_PKG_POWER_LIMIT (0x610) — 패키지 전력 제한

패키지 레벨의 전력 제한(PL1/PL2)을 설정합니다. PL1은 장기(sustained) TDP, PL2는 단기(burst) TDP입니다.

비트필드설명
14:0Power Limit 1 (PL1)장기 전력 제한 (Power Units 단위). 예: 600 × 0.125W = 75W TDP
15PL1 Enable1이면 PL1 활성화
16PL1 Clamping Limitation1이면 PL1 미만으로도 클램핑 허용 (더 공격적 제한)
23:17PL1 Time WindowPL1 시간 창 (Time Units 사용). 이 시간 동안의 평균 전력이 PL1 이하여야 함
46:32Power Limit 2 (PL2)단기 전력 제한. 보통 PL1의 1.25배. 예: 93.75W
47PL2 Enable1이면 PL2 활성화
48PL2 Clamping LimitationPL2 클램핑 허용
55:49PL2 Time WindowPL2 시간 창 (보통 ~28ms)
63Lock1이면 이 MSR 잠금 (재부팅까지 변경 불가). BIOS가 설정.
/* drivers/powercap/intel_rapl_msr.c — RAPL 전력 제한 설정 */
static int set_domain_power_limit(struct rapl_domain *rd,
                                  u64 power_limit_uw, int pl_id)
{
    u64 val, mask;
    int shift = (pl_id == 1) ? 0 : 32;

    rdmsrl(rd->msr_power_limit, val);

    /* 와트를 RAPL 단위로 변환 */
    u64 raw = div64_u64(power_limit_uw,
                        rd->rp->power_unit * 1000000ULL);
    mask = 0x7FFFULL << shift;
    val = (val & ~mask) | ((raw & 0x7FFF) << shift);
    val |= (1ULL << (15 + shift));  /* Enable bit */

    wrmsrl(rd->msr_power_limit, val);
    return 0;
}

MSR_PKG_ENERGY_STATUS (0x611) — 패키지 에너지 소비 (읽기 전용)

패키지의 누적 에너지 소비량입니다. 32비트 카운터로, 오버플로우됩니다.

비트필드설명
31:0Total Energy Consumed누적 에너지 (Energy Units 단위). 두 시점의 차이로 전력 계산.
/* 패키지 전력 측정 (두 시점 간 에너지 차이) */
static u64 measure_pkg_power_mw(unsigned int interval_ms)
{
    u64 e0, e1, units;
    u64 energy_unit;

    rdmsrl(MSR_RAPL_POWER_UNIT, units);
    energy_unit = 1000000ULL >> ((units >> 8) & 0x1F);  /* μJ 단위 */

    rdmsrl(MSR_PKG_ENERGY_STATUS, e0);
    msleep(interval_ms);
    rdmsrl(MSR_PKG_ENERGY_STATUS, e1);

    /* 32비트 오버플로우 처리 */
    u64 delta = (e1 - e0) & 0xFFFFFFFF;
    /* mW = (delta × energy_unit_μJ) / interval_ms */
    return div64_u64(delta * energy_unit, interval_ms);
}
/*
 * 오버플로우 주기 예시:
 *   Energy Unit=14 → 61μJ/count
 *   200W 소비 시: 2^32 × 61μJ / 200W ≈ 1311초 ≈ ~22분
 *   커널은 주기적으로 읽어 오버플로우를 추적해야 함
 */

RAPL Lock 메커니즘 상세

RAPL Power Limit MSR의 최상위 비트(bit 63)는 Lock 비트로, 한 번 1로 설정되면 시스템 재부팅 전까지 해당 MSR을 다시 수정할 수 없도록 영구 잠금합니다. 이 메커니즘은 보안 강화(악의적 전력 조작 방지)와 OEM 전력 정책 보호(사용자/OS가 제조사 설정을 덮어쓰지 못하도록)를 목적으로 합니다.

Lock 비트 배치 (도메인별)
도메인Power Limit MSRLock 비트설명
PKG (패키지)0x61063패키지 전체 전력 제한 잠금. 가장 중요한 Lock.
PP0 (Core)0x63831코어 도메인 전력 제한 잠금 (32비트 구조).
PP1 (GPU)0x64031내장 GPU 전력 제한 잠금 (클라이언트 CPU).
DRAM0x61863DRAM 전력 제한 잠금 (서버 CPU).
PSys (Platform)0x64C63플랫폼 전체 전력 제한 잠금 (Skylake+ 노트북).
주의: Lock 비트는 Write-Once Sticky Bit입니다. 한 번 1로 설정하면 AC 전원 차단 + 배터리 제거 또는 시스템 재부팅 전까지 절대 0으로 되돌릴 수 없습니다. 잘못된 전력 제한값을 설정 후 Lock하면, 시스템이 의도치 않게 낮은 전력으로 고정될 수 있습니다.
Lock이 설정되는 시점

RAPL Lock은 일반적으로 BIOS/UEFI 펌웨어가 POST(Power-On Self-Test) 또는 부팅 초기 단계에서 설정합니다. 다만 실제 기본값은 OEM, BIOS 버전, 마이크로코드, 하이퍼바이저(Hypervisor) 정책에 따라 달라지므로 아래 표는 대표적인 경향으로 읽어야 합니다:

시스템 유형Lock 정책이유
노트북/태블릿기본 Lock인 경우가 많음열 설계 전력(TDP) 보호. 사용자가 전력 제한을 높여 과열/화재를 유발하지 않도록 제한.
데스크톱 (OEM)대부분 Lock전원 공급 장치(PSU) 용량 보호. 과다 전력 소비로 인한 시스템 불안정 방지.
서버 (Dell, HPE 등)선택적 Lock데이터센터 전력 예산 관리. BIOS 옵션으로 제어 가능한 경우 많음.
자작 PC (DIY)보통 Unlock오버클러킹/언더볼팅 유연성 제공. 매더보드 제조사가 사용자 제어 허용.
클라우드 VM호스트 정책 의존게스트가 호스트 전력 정책에 간섭하지 못하게 대개 숨기거나 읽기 전용/합성 값으로 제한.
BIOS 옵션 확인: 일부 매더보드(ASUS, MSI 등)는 BIOS에서 CFG Lock, Overclocking Lock, RAPL Lock 유사 옵션을 제공합니다. 하지만 명칭과 실제 동작은 vendor/model마다 다르며, 옵션이 있어도 모든 RAPL 도메인이 동일하게 풀리는 것은 아닙니다.
보안 관점: Plundervolt 및 CVE-2019-11157

2019년, 연구자들은 Plundervolt 공격을 발표했습니다. 이 공격은 RAPL MSR을 통해 CPU 전압을 비정상적으로 낮춰, Intel SGX(Software Guard Extensions) enclave의 메모리 무결성(Integrity)을 파괴하고 암호키를 유출하는 취약점(Vulnerability)입니다.

/* 개념적 점검 흐름: 부팅 시 firmware lock 상태 확인 */
static int check_rapl_lock(void)
{
    u64 pkg_limit;

    rdmsrl(MSR_PKG_POWER_LIMIT, pkg_limit);
    if (pkg_limit & (1ULL << 63))
        pr_info("RAPL locked by firmware\\n");
    else
        pr_warn("RAPL writable: verify platform security policy\\n");

    return 0;
}
Plundervolt 이후 경향: 최근 플랫폼일수록 BIOS/마이크로코드가 undervolting 또는 RAPL 관련 쓰기를 더 보수적으로 잠그는 경우가 많습니다. 다만 "모든 CPU가 항상 기본 Lock"으로 단정할 수는 없으므로, 실제 장비에서는 반드시 MSR 또는 powercap 노출 상태를 직접 확인해야 합니다.
Lock 상태 확인 방법

다음 방법으로 현재 시스템의 RAPL Lock 상태를 확인할 수 있습니다:

# 방법 1: rdmsr로 직접 확인 (요구: msr 모듈 로드)
sudo modprobe msr
sudo rdmsr -f 63:63 0x610   # PKG Lock 비트 (1=Locked, 0=Unlocked)
sudo rdmsr -f 31:31 0x638   # PP0 (Core) Lock 비트
sudo rdmsr -f 63:63 0x618   # DRAM Lock 비트 (서버 CPU만)

# 방법 2: x86_energy_perf_policy 도구
sudo x86_energy_perf_policy -r  # "MSR 0x610 ... Lock=1" 출력

# 방법 3: turbostat (모든 전력 관련 MSR 덤프)
sudo turbostat --Summary --quiet --show PkgWatt,RAMWatt --interval 1
# 출력에 "RAPL locked" 메시지가 있으면 Lock됨

# 방법 4: 커널 로그 확인
dmesg | grep -i rapl
# 출력 예시: "intel_rapl_common: RAPL package-0 domain package locked by BIOS"
Lock된 MSR에 쓰기 시도 시 동작

RAPL Lock 비트가 설정된 상태에서 해당 MSR에 WRMSR 명령을 실행하면, 다음과 같은 동작이 발생합니다:

/* drivers/powercap/intel_rapl_msr.c — Lock 확인 후 쓰기 */
static int rapl_msr_write_raw(int cpu, struct rapl_domain *rd,
                              u64 val)
{
    u64 current;
    int ret;

    /* Lock 비트 확인 */
    ret = rdmsrl_on_cpu(cpu, rd->msr_power_limit, &current);
    if (ret)
        return ret;

    if (current & rd->lock_bit) {
        pr_debug("RAPL: Domain %s locked, write denied\\n", rd->name);
        return -EACCES;  /* Permission denied */
    }

    /* Lock되지 않았으면 쓰기 실행 */
    return wrmsrl_on_cpu(cpu, rd->msr_power_limit, val);
}

/* 사용자 공간에서 powercap sysfs 쓰기 시 */
// # echo 50000000 > /sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw
// 출력: bash: echo: write error: Permission denied (EACCES)
강제 쓰기 시도 금지: /dev/cpu/0/msr에 직접 쓰기를 시도해도 Lock 비트가 설정된 플랫폼에서는 보통 #GP, 쓰기 실패, 읽기 전용 노출 중 하나로 귀결됩니다. 일반적인 OS 경로만으로 firmware/hardware lock을 우회할 수 있다고 기대하면 안 됩니다.
OEM별 RAPL Lock 정책 경향

아래 표는 현장에서 자주 보이는 경향을 정리한 것입니다. 같은 제조사라도 제품군, BIOS 옵션, 출시 시점에 따라 결과가 달라질 수 있습니다.

제조사제품군Lock 정책BIOS 옵션
DellLatitude, XPS대체로 Lock모델 의존
DellPowerEdge 서버선택적 (BIOS)System Profile에서 제어
HPEliteBook, ProBook대체로 Lock모델 의존
HPProLiant 서버선택적Power Regulator 옵션
LenovoThinkPad대체로 Lock모델 의존
LenovoThinkSystem 서버선택적Operating Mode 설정
ASUSZenBook, VivoBook대부분 Lock일부 모델에 CFG Lock 옵션
ASUSROG (게이밍)Unlock 가능Advanced\Power 메뉴
MSI게이밍/워크스테이션Unlock 가능OC Settings 메뉴
GigabyteAORUS, 매더보드Unlock 기본Tweaker\Advanced CPU Settings
Supermicro서버 매더보드Unlock 기본IPMI로 동적 제어 가능
AppleMacBook (Intel)강한 제한 경향사용자 노출 BIOS 없음
서버 환경 동적 제어: HPE iLO, Dell iDRAC, Supermicro IPMI는 OS 실행 중에도 대역외(out-of-band) 관리 인터페이스를 통해 전력 제한(Power Capping)을 설정할 수 있습니다. 이는 RAPL MSR을 우회하여 BMC(Baseboard Management Controller)가 직접 전력 관리 회로를 제어하는 방식입니다.
커널의 Lock 감지 및 처리

Linux 커널의 intel_rapl 드라이버는 초기화 시 각 도메인의 Lock 상태를 검사하고, Lock된 도메인은 읽기 전용으로 표시합니다:

/* drivers/powercap/intel_rapl_common.c — RAPL 도메인 초기화 */
static int rapl_detect_domains(struct rapl_package *rp, int cpu)
{
    struct rapl_domain *rd;
    u64 locked;
    int i;

    for (i = 0; i < RAPL_DOMAIN_MAX; i++) {
        rd = &rp->domains[i];
        if (!rd->msr_power_limit)
            continue;

        /* Lock 비트 읽기 */
        rdmsrl(rd->msr_power_limit, locked);
        if (locked & rd->lock_bit) {
            rd->locked = true;
            pr_info("RAPL: package %d domain %s locked by BIOS\\n",
                   rp->id, rd->name);
        }
    }

    return 0;
}

/* sysfs 속성을 읽기 전용으로 설정 */
static umode_t power_limit_attr_visible(struct kobject *kobj,
                                        struct attribute *attr, int n)
{
    struct rapl_domain *rd = to_rapl_domain(kobj);

    if (rd->locked)
        return S_IRUGO;  /* 읽기 전용 (0444) */
    else
        return S_IRUGO | S_IWUSR;  /* 읽기+쓰기 (0644) */
}

Lock된 도메인의 sysfs 파일은 권한이 -r--r--r--로 설정되어, 쓰기 시도 시 명확히 거부됩니다:

# Lock된 시스템
$ ls -l /sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw
-r--r--r-- 1 root root 4096  # 읽기 전용!

# Unlock된 시스템
$ ls -l /sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw
-rw-r--r-- 1 root root 4096  # root 쓰기 가능
가상화 환경에서의 RAPL Lock

가상 머신 내에서 RAPL MSR 접근은 하이퍼바이저 정책, 노출 CPU 모델, 버전에 따라 크게 달라집니다:

하이퍼바이저RAPL MSR 노출Lock 상태주의사항
KVM/QEMU버전/설정 의존정책 의존읽기 전용 에너지 카운터만 노출하거나, 0/합성 값을 반환하거나, 아예 숨길 수 있음
VMware ESXi대체로 제한적정책 의존제품 버전과 가상 하드웨어 설정에 따라 다름
Hyper-V대체로 제한적정책 의존Synthetic 인터페이스 또는 비노출 형태가 일반적
XenDom0 우선DomU 제한적DomU에 어떤 값을 노출할지는 버전/정책 의존
/* 개념적 의사 코드: 하이퍼바이저의 RAPL MSR 노출 정책 */
static int hypervisor_get_msr(struct vcpu *vcpu, struct msr_data *msr_info)
{
    switch (msr_info->index) {
    case MSR_PKG_POWER_LIMIT:
        if (!vcpu->rapl_passthrough)
            return inject_gp_or_return_synthetic_value(msr_info);
        return emulate_guest_visible_limit(vcpu, msr_info);
    case MSR_PKG_ENERGY_STATUS:
        return emulate_or_hide_energy_counter(vcpu, msr_info);
    }
    return -EOPNOTSUPP;
}
클라우드 보안: 퍼블릭 클라우드는 대체로 게스트 VM에 RAPL 제어면을 직접 노출하지 않거나 의미 없는 합성 값으로 제한합니다. 다만 실제 노출 여부는 상품군, 가상 CPU 모델, 세대별 정책에 따라 달라질 수 있습니다.
Lock 우회 시도 및 불가능성

다음과 같은 시도들은 일반적인 운영체제/하이퍼바이저 경로에서는 성공하기 어렵습니다:

실질적인 해제 경로는 시스템 재부팅 후 BIOS/UEFI 설정에서 Lock 정책을 변경하는 것입니다. 다만 많은 플랫폼은 해당 옵션을 노출하지 않거나, 마이크로코드 정책 때문에 사용자가 해제할 수 없을 수 있습니다.

RAPL Lock 요약 체크리스트
질문답변
Lock 비트 위치는?PKG/DRAM/PSys: bit 63, PP0/PP1: bit 31
누가 설정하는가?BIOS/UEFI 펌웨어 (POST 단계)
재부팅 없이 해제 가능?불가능 (Write-Once Sticky)
Lock 시 쓰기하면?#GP(0) 예외 또는 조용히 무시
보안 목적은?Plundervolt 공격 방지, 열 설계 보호
서버에서 동적 제어 가능?가능 (IPMI/iLO로 대역외 제어)
가상화 환경에서는?하이퍼바이저 정책 의존 (비노출/읽기 전용/합성 값 가능)
확인 명령어는?rdmsr -f 63:63 0x610

RAPL 도메인별 MSR 주소 정리

용도PKGPP0 (Core)PP1 (GPU)DRAMPSys
Power Limit0x6100x6380x6400x6180x64C
Energy Status0x6110x6390x6410x6190x64D
Power Info0x6140x63A0x6420x61C
Perf Status0x63B0x61B
Policy0x63C0x642
sysfs에서 RAPL 접근:
/sys/class/powercap/intel-rapl:0/energy_uj — 패키지 누적 에너지 (μJ)
/sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw — PL1 (μW)
/sys/class/powercap/intel-rapl:0/constraint_1_power_limit_uw — PL2 (μW)
perf stat -e power/energy-pkg/도 동일한 MSR을 읽습니다.

C-State 관련 MSR

C-State MSR은 CPU 유휴 상태(Idle State) 제어와 각 C-State의 레지던시(체류 시간) 측정에 사용됩니다. cpuidle 드라이버(intel_idle)와 turbostat이 이 MSR들을 활용합니다.

MSR_PKG_CST_CONFIG_CONTROL (0xE2) — 패키지 C-State 제어

비트필드설명
3:0Package C-State Limit허용 최대 패키지 C-State. 0=C0, 1=C2, 2=C6, 3=C6N, 7=제한 없음 (모델별 상이)
10I/O MWAIT Redirection Enable1이면 I/O 명령어를 MWAIT C-State로 리다이렉션
15CFG Lock1이면 이 MSR 잠금 (재부팅까지 변경 불가). BIOS가 설정.
25C3 State Auto Demotion Enable1이면 C3 자동 디모션 활성화
26C1 State Auto Demotion Enable1이면 C1 자동 디모션 활성화
27Enable C3 Undemotion1이면 C3 언디모션 활성화
28Enable C1 Undemotion1이면 C1 언디모션 활성화
C-State 디모션/언디모션: CPU가 C6 진입을 요청해도 짧은 유휴 기간이 예상되면 자동으로 C3이나 C1으로 "디모션"하여 진입/복귀 지연을 줄입니다. 반대로 "언디모션"은 이전에 디모션된 코어를 다시 깊은 C-State로 올립니다.

C-State 레지던시 MSR (코어/패키지)

MSR주소범위설명
MSR_CORE_C1_RES0x660코어코어 C1 레지던시 카운터 (TSC 단위)
MSR_CORE_C3_RESIDENCY0x3FC코어코어 C3 레지던시 카운터
MSR_CORE_C6_RESIDENCY0x3FD코어코어 C6 레지던시 카운터
MSR_CORE_C7_RESIDENCY0x3FE코어코어 C7 레지던시 카운터
MSR_PKG_C2_RESIDENCY0x60D패키지패키지 C2 레지던시 카운터
MSR_PKG_C3_RESIDENCY0x3F8패키지패키지 C3 레지던시 카운터
MSR_PKG_C6_RESIDENCY0x3F9패키지패키지 C6 레지던시 카운터
MSR_PKG_C7_RESIDENCY0x3FA패키지패키지 C7 레지던시 카운터
MSR_PKG_C8_RESIDENCY0x630패키지패키지 C8 레지던시 (Haswell+)
MSR_PKG_C9_RESIDENCY0x631패키지패키지 C9 레지던시 (Haswell+)
MSR_PKG_C10_RESIDENCY0x632패키지패키지 C10 레지던시 (가장 깊은 유휴)
/* C-State 레지던시 비율 측정 */
static void measure_cstate_residency(unsigned int interval_ms)
{
    u64 tsc0, tsc1, c6_0, c6_1, c7_0, c7_1;

    tsc0 = rdtsc_ordered();
    rdmsrl(MSR_CORE_C6_RESIDENCY, c6_0);
    rdmsrl(MSR_CORE_C7_RESIDENCY, c7_0);

    msleep(interval_ms);

    tsc1 = rdtsc_ordered();
    rdmsrl(MSR_CORE_C6_RESIDENCY, c6_1);
    rdmsrl(MSR_CORE_C7_RESIDENCY, c7_1);

    u64 dt  = tsc1 - tsc0;
    u64 dc6 = c6_1 - c6_0;
    u64 dc7 = c7_1 - c7_0;

    pr_info("C6 residency: %llu.%02llu%%\\n",
           dc6 * 100 / dt, (dc6 * 10000 / dt) % 100);
    pr_info("C7 residency: %llu.%02llu%%\\n",
           dc7 * 100 / dt, (dc7 * 10000 / dt) % 100);
    /*
     * turbostat 출력과 대응:
     *   CPU%c1 = C1 residency %, CPU%c6 = C6 residency %
     *   Pkg%pc2 = Package C2 %, Pkg%pc6 = Package C6 %
     *   이상적인 유휴 서버: Pkg%pc6 > 90%
     */
}

IA32_ENERGY_PERF_BIAS (0x1B0) — 에너지 성능 편향

HWP의 EPP보다 이전에 도입된(Sandy Bridge+) 레거시 에너지 성능 힌트입니다. HWP가 활성화되지 않은 시스템에서 CPU의 자율적 전력 관리 결정에 힌트를 제공합니다.

비트필드설명
3:0Energy Policy Hint0=최대 성능, 7=균형, 15=최대 절전
63:4예약됨
Linux 상수의미
0ENERGY_PERF_BIAS_PERFORMANCE최대 성능 — 전력 무시
4ENERGY_PERF_BIAS_BALANCE_PERFORMANCE성능 우선 균형
6ENERGY_PERF_BIAS_NORMAL일반 (커널 기본값)
8ENERGY_PERF_BIAS_BALANCE_POWERSAVE절전 우선 균형
15ENERGY_PERF_BIAS_POWERSAVE최대 절전
EPB vs EPP: EPB(4비트, 0~15)는 레거시, EPP(8비트, 0~255)는 HWP에서 사용. HWP 활성화 시 EPP가 우선하며, EPB는 HWP가 아닌 하드웨어 결정(터보/C-State)에만 영향. /sys/devices/system/cpu/cpu0/power/energy_perf_bias로 설정 가능.

IA32_CLOCK_MODULATION (0x19A) — 클럭 변조 (레거시 스로틀링)

소프트웨어 제어 클럭 변조(Software Controlled Clock Modulation, aka duty cycling)를 설정합니다. 이는 가장 초기의 전력 관리 기법으로, CPU 클럭을 일정 비율로 on/off 하여 전력을 줄입니다. 현대 CPU에서는 DVFS가 훨씬 효율적이므로 거의 사용되지 않습니다.

비트필드설명
0Extended On-Demand Clock Modulation Enable1이면 fine-grained duty cycle 사용 (3:1 비트 포함 4비트)
3:1On-Demand Clock Modulation Duty Cycle듀티 사이클: 001=12.5%, 010=25%, ..., 111=87.5%
4On-Demand Clock Modulation Enable1이면 클럭 변조 활성화

AMD P-State MSR (CPPC)

AMD Zen 아키텍처 CPU는 ACPI CPPC(Collaborative Processor Performance Control) 프레임워크를 기반으로 P-State를 관리합니다. Linux의 amd-pstate 드라이버(5.17+)가 이 MSR들을 사용합니다.

MSR_AMD_CPPC_ENABLE (0xC00102B1) — CPPC 활성화

비트필드설명
0CPPC Enable1이면 CPPC(하드웨어 자율 P-State) 활성화

MSR_AMD_CPPC_CAP1 (0xC00102B0) — CPPC 성능 범위

비트필드설명
7:0Highest Performance최대 성능 수준 (부스트 포함)
15:8Nominal Performance공칭 성능 수준 (Base Frequency 상당)
23:16Lowest Nonlinear Performance에너지 효율이 급격히 떨어지기 시작하는 하한
31:24Lowest Performance절대 최저 성능 수준

MSR_AMD_CPPC_REQ (0xC00102B3) — CPPC 성능 요청

비트필드설명
7:0Maximum Performance허용 최대 성능
15:8Minimum Performance허용 최저 성능
23:16Desired Performance목표 성능 (0=자율)
31:24Energy Performance PreferenceEPP (0=성능, 255=절전) — Intel HWP EPP와 동일 의미

MSR_AMD_PSTATE_DEF (0xC0010064 ~ 0xC0010069) — P-State 정의

AMD CPU는 최대 8개의 하드웨어 P-State 정의를 MSR에 보관합니다 (P0~P7). 각 MSR은 FID(주파수), DID(분주비), VID(전압)를 인코딩합니다.

비트필드설명
7:0CpuFidCPU Frequency ID
13:8CpuDfsIdCPU DFS (Divider) ID
21:14CpuVidCPU Voltage ID
22IddDiv전류 분배기
29:23IddValue전류 값
63PstateEn1이면 이 P-State 유효
/* AMD P-State 주파수 계산 */
static u32 amd_pstate_freq_mhz(u64 pstate_def)
{
    u32 fid = pstate_def & 0xFF;
    u32 did = (pstate_def >> 8) & 0x3F;

    if (did == 0)
        return 0;  /* 0으로 나눗셈 방지 */
    /* Frequency (MHz) = 200 × FID / DID */
    return 200 * fid / did;
    /*
     * 예: Ryzen 9 7950X P0
     *   FID=0xB0 (176), DID=0x08 (8)
     *   Freq = 200 × 176 / 8 = 4400 MHz
     */
}

/* drivers/cpufreq/amd-pstate.c — CPPC 성능 요청 */
static void amd_pstate_update(struct amd_cpudata *cpudata,
                              u32 min_perf, u32 des_perf,
                              u32 max_perf, bool fast_switch)
{
    u64 value = READ_ONCE(cpudata->cppc_req_cached);

    value &= ~(AMD_CPPC_MIN_PERF_MASK | AMD_CPPC_DES_PERF_MASK |
               AMD_CPPC_MAX_PERF_MASK);
    value |= AMD_CPPC_MIN_PERF(min_perf);
    value |= AMD_CPPC_DES_PERF(des_perf);
    value |= AMD_CPPC_MAX_PERF(max_perf);

    if (fast_switch)
        wrmsrl(MSR_AMD_CPPC_REQ, value);
    else
        wrmsrl_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ, value);
}
amd-pstate 드라이버 모드:
amd-pstate=passive: OS governor가 목표 주파수 결정 → 드라이버가 CPPC MSR에 변환하여 전달.
amd-pstate=active: amd-pstate-epp 드라이버 사용, EPP 기반 하드웨어 자율 관리 (Intel HWP와 유사).
amd-pstate=guided: OS가 min/max만 설정, 그 범위 내에서 하드웨어가 자율 결정.

cpufreq 드라이버와 MSR 매핑(Mapping)

Linux 커널의 cpufreq 프레임워크는 MSR을 직접 노출하지 않고, sysfs를 통해 추상화합니다. 각 sysfs 파일이 어떤 MSR을 읽고 쓰는지 이해하면 디버깅에 유용합니다.

sysfs 경로Intel MSRAMD MSR설명
scaling_min_freqHWP_REQUEST[7:0]CPPC_REQ[15:8]최소 주파수 설정
scaling_max_freqHWP_REQUEST[15:8]CPPC_REQ[7:0]최대 주파수 설정
scaling_cur_freqAPERF/MPERF 계산APERF/MPERF 계산현재 실제 주파수
cpuinfo_min_freqPLATFORM_INFO[40:32]CPPC_CAP1[31:24]하드웨어 최소 주파수
cpuinfo_max_freqTURBO_RATIO_LIMIT[7:0]CPPC_CAP1[7:0]하드웨어 최대 주파수
base_frequencyPLATFORM_INFO[15:8]CPPC_CAP1[15:8]기본 주파수 (비터보)
energy_performance_preferenceHWP_REQUEST[31:24]CPPC_REQ[31:24]EPP 값
# 전력/주파수 관련 주요 확인 명령어

# 현재 주파수 정책 확인
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver      # intel_pstate 또는 amd-pstate
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor     # performance, powersave 등

# HWP/EPP 확인 (Intel)
cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference
cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_available_preferences

# RAPL 전력 확인
cat /sys/class/powercap/intel-rapl:0/name                     # package-0
cat /sys/class/powercap/intel-rapl:0/energy_uj                # 누적 에너지 (μJ)
cat /sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw  # PL1 (μW)

# turbostat으로 종합 확인
turbostat --show Core,CPU,Avg_MHz,Busy%,Bzy_MHz,TSC_MHz,PkgWatt,CorWatt,Pkg%pc2,Pkg%pc6

# MSR 직접 읽기 (msr-tools)
rdmsr -p 0 0xCE                # MSR_PLATFORM_INFO
rdmsr -p 0 0x1AD               # MSR_TURBO_RATIO_LIMIT
rdmsr -p 0 0x774 -f 31:24      # HWP_REQUEST의 EPP 필드만
rdmsr -p 0 0x611               # PKG_ENERGY_STATUS (RAPL)

열 관리(Thermal Management) MSR

CPU 온도 모니터링과 열 스로틀링(thermal throttling) 제어를 위한 MSR입니다.

MSR주소설명
IA32_THERM_STATUS0x19C코어별 열 상태 및 온도 읽기
IA32_THERM_INTERRUPT0x19B열 인터럽트 임계값 설정
IA32_PACKAGE_THERM_STATUS0x1B1패키지 전체 열 상태
IA32_PACKAGE_THERM_INTERRUPT0x1B2패키지 열 인터럽트 설정
IA32_TEMPERATURE_TARGET0x1A2TjMax (최대 허용 온도, 읽기 전용)

온도 계산 방법

Intel CPU는 TjMax로부터의 거리(Digital Readout)를 보고합니다:

/* drivers/hwmon/coretemp.c — 코어 온도 읽기 */
static int get_core_temp(int cpu)
{
    u32 eax, edx;
    int tjmax, temp;

    /* TjMax 읽기 (보통 100°C) */
    rdmsr_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
    tjmax = (eax >> 16) & 0xFF;

    /* 현재 온도 = TjMax - Digital Readout */
    rdmsr_on_cpu(cpu, MSR_IA32_THERM_STATUS, &eax, &edx);
    if (eax & 0x80000000) {  /* Reading Valid 비트 */
        temp = tjmax - ((eax >> 16) & 0x7F);
        return temp;  /* 섭씨 온도 */
    }
    return -1;
}
참고: /sys/class/hwmon/hwmon*/temp*_input으로 유저 공간에서 읽을 수 있으며, 값은 밀리도(m°C) 단위입니다 (예: 65000 = 65°C). coretemp 드라이버가 위 MSR을 추상화합니다.

TSC 관련 MSR

TSC(Time Stamp Counter)는 가장 자주 사용되는 MSR 중 하나로, 고해상도 시간 측정의 기반입니다.

MSR주소설명
IA32_TSC0x10TSC 카운터 값 (RDMSR 또는 RDTSC로 읽기)
IA32_TSC_ADJUST0x3BTSC 보정 오프셋 (가상화/핫플러그(Hotplug) 시 동기화용)
IA32_TSC_DEADLINE0x6E0APIC TSC-Deadline 모드 타이머(Timer) 목표값
IA32_TSC_AUX0xC0000103RDTSCP로 읽는 보조 값 (보통 CPU ID)

TSC-Deadline 모드

TSC-Deadline 모드는 APIC 타이머의 최신 모드로, 지정한 TSC 값에 도달하면 인터럽트를 발생시킵니다. 기존 one-shot/periodic 모드보다 정밀합니다.

/* arch/x86/kernel/apic/apic.c — TSC-Deadline 타이머 설정 */
static void setup_APIC_tsc_deadline(u64 deadline)
{
    /* LVT Timer에 TSC-Deadline 모드 설정 */
    apic_write(APIC_LVTT, APIC_LVT_TIMER_TSCDEADLINE | LOCAL_TIMER_VECTOR);
    /* 목표 TSC 값 쓰기 — 이 값에 도달하면 인터럽트 */
    wrmsrl(MSR_IA32_TSC_DEADLINE, deadline);
}
코드 설명

arch/x86/kernel/apic/apic.c의 TSC-Deadline 타이머 설정 코드입니다. MSR_IA32_TSC_DEADLINE(0x6E0)은 APIC 타이머의 최신 모드로, TSC 값 기반의 정밀한 인터럽트 스케줄링을 지원합니다.

  • apic_write(APIC_LVTT, ...)LVT(Local Vector Table) Timer 레지스터에 TSC-Deadline 모드 플래그와 인터럽트 벡터를 설정합니다. 이 설정이 선행되어야 MSR_IA32_TSC_DEADLINE 쓰기가 유효합니다.
  • wrmsrl(MSR_IA32_TSC_DEADLINE, deadline)목표 TSC 값을 MSR에 기록합니다. CPU의 TSC가 이 값에 도달하면 로컬 APIC 타이머 인터럽트가 발생합니다. one-shot 방식으로 동작하므로, 다음 인터럽트를 위해서는 새로운 deadline을 다시 써야 합니다. IA32_TSC(0x10)와 연동하여 나노초 단위 정밀도를 제공합니다.

TSC_ADJUST — 가상화 환경 TSC 동기화

KVM은 vCPU 마이그레이션 시 IA32_TSC_ADJUST를 사용하여 게스트가 인식하는 TSC 값을 보정합니다. 게스트가 보는 TSC = 물리 TSC + TSC_OFFSET + TSC_ADJUST입니다.

보안 MSR (Spectre/Meltdown 완화)

2018년 Spectre/Meltdown 취약점 발견 이후, CPU 벤더들은 소프트웨어 기반 완화를 지원하기 위한 MSR을 대거 추가했습니다.

IA32_ARCH_CAPABILITIES (0x10A)

CPU가 특정 취약점에 하드웨어적으로 면역인지 보고합니다(읽기 전용). 커널은 이 MSR을 읽어 불필요한 완화를 건너뜁니다.

비트이름의미
0RDCL_NOMeltdown(Rogue Data Cache Load) 면역
1IBRS_ALLIBRS가 모든 분기에 적용 (Enhanced IBRS)
2RSBAReturn Stack Buffer 대안 사용 (retpoline 주의)
3SKIP_L1DFL_VMENTRYVM 진입 시 L1D flush 불필요
4SSB_NOSpeculative Store Bypass 면역
5MDS_NOMicroarchitectural Data Sampling 면역
6IF_PSCHANGE_MC_NO페이지 크기 변경 시 MC 면역
7TSX_CTRLIA32_TSX_CTRL MSR 존재
8TAA_NOTSX Asynchronous Abort 면역

추론 실행 제어 MSR

MSR주소설명
IA32_SPEC_CTRL0x48IBRS(bit 0), STIBP(bit 1), SSBD(bit 2) 제어
IA32_PRED_CMD0x49IBPB(bit 0) — 분기 예측기 배리어 (쓰기 전용)
IA32_FLUSH_CMD0x10BL1D_FLUSH(bit 0) — L1 데이터 캐시 플러시 (쓰기 전용)

각 완화 기법의 동작

/* arch/x86/kernel/cpu/bugs.c — Spectre v2 완화 */

/* IBRS: 간접 분기가 하위 권한 예측을 사용하지 않음 */
static void spec_ctrl_enable_ibrs(void)
{
    u64 msr = this_cpu_read(x86_spec_ctrl_current);
    msr |= SPEC_CTRL_IBRS;
    native_wrmsrl(MSR_IA32_SPEC_CTRL, msr);
}

/* IBPB: 분기 예측기 전체 무효화 (컨텍스트 스위치 시) */
static void indirect_branch_prediction_barrier(void)
{
    native_wrmsrl(MSR_IA32_PRED_CMD, PRED_CMD_IBPB);
}

/* SSBD: 추론적 저장-로드 바이패스 비활성화 (Spectre v4) */
static void ssb_disable(void)
{
    u64 msr = this_cpu_read(x86_spec_ctrl_current);
    msr |= SPEC_CTRL_SSBD;
    native_wrmsrl(MSR_IA32_SPEC_CTRL, msr);
}

/* L1D Flush: VM 진입 전 L1 데이터 캐시 플러시 (L1TF 완화) */
static void l1d_flush_force(void)
{
    native_wrmsrl(MSR_IA32_FLUSH_CMD, L1D_FLUSH);
}
코드 설명

arch/x86/kernel/cpu/bugs.c의 Spectre/Meltdown 완화 코드입니다. 추론 실행(Speculative Execution) 취약점을 소프트웨어로 완화하기 위해 전용 MSR을 사용합니다. MSR 인덱스는 arch/x86/include/asm/msr-index.h에 정의되어 있습니다.

  • native_wrmsrl(MSR_IA32_SPEC_CTRL, msr)IA32_SPEC_CTRL(0x48)에 IBRS(비트 0) 또는 SSBD(비트 2) 플래그를 설정합니다. native_wrmsrl은 paravirt 계층을 우회하여 직접 WRMSR을 실행하는 변형으로, 보안에 민감한 경로에서 사용됩니다.
  • native_wrmsrl(MSR_IA32_PRED_CMD, PRED_CMD_IBPB)IA32_PRED_CMD(0x49)는 쓰기 전용 MSR로, 비트 0에 1을 쓰면 분기 예측기(Branch Predictor)가 전체 무효화됩니다. 컨텍스트 스위치 시 이전 프로세스의 분기 예측 정보가 새 프로세스에 누출되는 것을 방지합니다.
  • native_wrmsrl(MSR_IA32_FLUSH_CMD, L1D_FLUSH)IA32_FLUSH_CMD(0x10B)는 쓰기 전용 MSR로, L1 데이터 캐시를 즉시 플러시합니다. VM 진입 전에 호스트 데이터가 L1D에 남아 있으면 게스트가 L1TF(L1 Terminal Fault) 공격으로 읽을 수 있으므로, KVM이 VM entry 직전에 호출합니다.
성능 영향: IBRS/STIBP는 분기 예측 성능을 저하시킵니다. Enhanced IBRS(IA32_ARCH_CAPABILITIES.IBRS_ALL=1)가 있는 최신 CPU에서는 한 번 설정하면 지속되어 오버헤드(Overhead)가 적습니다. IBPB는 컨텍스트 스위치마다 발생하므로 프로세스(Process) 전환 비용이 증가합니다.

가상화 MSR

Intel VMX 관련 MSR

VMX(Virtual Machine Extensions) 기능을 보고하고 제어하는 MSR입니다. KVM 모듈이 VMX 초기화 시 이 MSR들을 읽어 지원되는 기능을 확인합니다.

MSR주소설명
IA32_VMX_BASIC0x480VMCS 크기, 메모리 타입, 기본 기능
IA32_VMX_PINBASED_CTLS0x481Pin-Based VM-Execution Controls 허용 비트
IA32_VMX_PROCBASED_CTLS0x482Primary Processor-Based Controls 허용 비트
IA32_VMX_PROCBASED_CTLS20x48BSecondary Processor-Based Controls
IA32_VMX_EXIT_CTLS0x483VM-Exit Controls 허용 비트
IA32_VMX_ENTRY_CTLS0x484VM-Entry Controls 허용 비트
IA32_VMX_EPT_VPID_CAP0x48CEPT/VPID 기능 보고
IA32_VMX_CR0_FIXED0/10x486/487VMX 모드에서 CR0 필수/허용 비트
IA32_VMX_CR4_FIXED0/10x488/489VMX 모드에서 CR4 필수/허용 비트

MSR 비트맵 (Intel VT-x)

VMCS의 MSR 비트맵은 게스트의 RDMSR/WRMSR이 VM exit를 유발할지 결정하는 4KB 비트맵입니다. 비트가 0이면 게스트가 직접 접근하고, 1이면 VM exit가 발생하여 KVM이 에뮬레이션합니다.

/* arch/x86/kvm/vmx/vmx.c — MSR 비트맵 설정 */
static void vmx_set_msr_bitmap_read(unsigned long *msr_bitmap,
                                     u32 msr, bool intercept)
{
    int f;
    if (msr <= 0x1FFF)
        f = 0;                 /* 하위 MSR 영역: 오프셋 0 */
    else if (msr >= 0xC0000000 && msr <= 0xC0001FFF)
        f = 1024;              /* 상위 MSR 영역: 오프셋 1024바이트 */
    else
        return;               /* 범위 밖 — 항상 intercept */

    msr &= 0x1FFF;
    if (intercept)
        __set_bit(msr, msr_bitmap + f / sizeof(long));
    else
        __clear_bit(msr, msr_bitmap + f / sizeof(long));
}

AMD SVM MSRPM (MSR Permission Map)

AMD SVM은 8KB MSRPM을 사용하며, 각 MSR에 대해 2비트(읽기/쓰기 각각)를 할당합니다.

MSRPM 오프셋MSR 범위비트 할당
0x000 ~ 0x7FF0x00000000 ~ 0x00001FFFbit 0 = read intercept, bit 1 = write intercept
0x800 ~ 0xFFF0xC0000000 ~ 0xC0001FFF위와 동일
0x1000 ~ 0x17FF0xC0010000 ~ 0xC0011FFF위와 동일

KVM의 MSR 에뮬레이션

/* arch/x86/kvm/x86.c — 게스트 MSR 읽기 에뮬레이션 */
int kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data)
{
    switch (index) {
    case MSR_IA32_TSC:
        *data = kvm_read_l1_tsc(vcpu, rdtsc());
        break;
    case MSR_IA32_SPEC_CTRL:
        *data = vcpu->arch.spec_ctrl;
        break;
    case MSR_IA32_ARCH_CAPABILITIES:
        *data = vcpu->arch.arch_capabilities;
        break;
    /* ... 수십 개의 MSR 에뮬레이션 */
    default:
        return kvm_x86_ops.get_msr(vcpu, index, data);
    }
    return 0;
}
성능 팁: MSR 비트맵에서 TSC, APERF/MPERF, SPEC_CTRL 등 빈번히 접근하는 MSR은 직접 통과(passthrough)로 설정하면 VM exit 오버헤드를 줄일 수 있습니다. 단, 보안 MSR은 반드시 인터셉트해야 합니다.

MTRR / PAT MSR

MTRR (Memory Type Range Registers)

MTRR은 물리 메모리(Physical Memory) 영역의 캐시 정책(Uncacheable, Write-Combining, Write-Through, Write-Back 등)을 설정합니다. 주로 BIOS가 초기화하며, 그래픽 프레임버퍼 등 MMIO 영역에 Write-Combining을 설정합니다.

MSR주소설명
IA32_MTRRCAP0xFEMTRR 기능: 가변 범위 수, FIX 지원, WC 지원
IA32_MTRR_DEF_TYPE0x2FF기본 메모리 타입 및 MTRR 활성화
IA32_MTRR_FIX64K_000000x250고정 범위: 0x00000~0x7FFFF (64KB 단위)
IA32_MTRR_FIX16K_800000x258고정 범위: 0x80000~0xBFFFF (16KB 단위)
IA32_MTRR_FIX4K_C00000x268~26F고정 범위: 0xC0000~0xFFFFF (4KB 단위)
IA32_MTRR_PHYSBASEn0x200+2n가변 범위 n의 base 주소 및 타입
IA32_MTRR_PHYSMASKn0x201+2n가변 범위 n의 마스크 및 유효 비트

메모리 타입 인코딩

약칭설명
0UCUncacheable — 캐시 사용 안 함
1WCWrite-Combining — 쓰기 결합 (프레임버퍼용)
4WTWrite-Through — 쓰기 즉시 반영
5WPWrite-Protect — 읽기는 캐시, 쓰기는 UC처럼
6WBWrite-Back — 가장 빠름 (일반 RAM)

PAT (Page Attribute Table)

PAT는 MTRR의 페이지 단위 확장으로, 페이지 테이블(Page Table) 엔트리의 PAT/PCD/PWT 비트 조합으로 8개 메모리 타입 중 하나를 선택합니다.

MSR주소설명
IA32_PAT0x2778개 PAT 엔트리 (각 8비트, 총 64비트)
/* arch/x86/mm/pat/memtype.c — PAT 초기화 */
void pat_init(void)
{
    u64 pat;

    /* Linux 기본 PAT 설정:
     * PAT0=WB PAT1=WC PAT2=UC- PAT3=UC
     * PAT4=WB PAT5=WC PAT6=UC- PAT7=UC */
    pat = PAT(0, WB) | PAT(1, WC) | PAT(2, UC_MINUS) | PAT(3, UC) |
          PAT(4, WB) | PAT(5, WC) | PAT(6, UC_MINUS) | PAT(7, UC);
    wrmsrl(MSR_IA32_CR_PAT, pat);
}
코드 설명

arch/x86/mm/pat/memtype.c의 PAT(Page Attribute Table) 초기화 코드입니다. IA32_PAT(0x277) MSR은 64비트를 8개 엔트리(각 8비트)로 나누어, 페이지 테이블 엔트리의 PAT/PCD/PWT 비트 조합에 따라 메모리 캐시 타입을 결정합니다.

  • PAT(n, type) 매크로8비트 메모리 타입 코드를 PAT MSR의 해당 엔트리 위치(n×8비트)에 배치합니다. WB=6(Write-Back), WC=1(Write-Combining), UC_MINUS=7, UC=0(Uncacheable)입니다.
  • PAT0=WB, PAT1=WC리눅스는 BIOS 기본값 대신 자체 PAT 배치를 사용합니다. PAT0(WB)은 일반 RAM, PAT1(WC)은 프레임버퍼나 GPU 메모리에 사용됩니다. MTRR과 달리 PAT는 페이지 단위로 캐시 정책을 세밀하게 제어할 수 있습니다.
  • wrmsrl(MSR_IA32_CR_PAT, pat)조합된 64비트 값을 PAT MSR에 기록합니다. 모든 CPU에서 동일한 PAT 설정을 사용해야 하므로, 부팅 시 각 CPU가 개별적으로 이 함수를 호출합니다.

Linux 커널 MSR API

Linux 커널은 다양한 MSR 접근 래퍼 함수를 제공합니다. 모두 arch/x86/include/asm/msr.h에 정의되어 있습니다.

기본 접근 함수

함수서명설명
rdmsrrdmsr(msr, low, high)32비트 쌍으로 읽기
wrmsrwrmsr(msr, low, high)32비트 쌍으로 쓰기
rdmsrlrdmsrl(msr, val)64비트 값으로 읽기
wrmsrlwrmsrl(msr, val)64비트 값으로 쓰기

안전한(safe) 변형

존재하지 않는 MSR에 접근하면 #GP 예외가 발생합니다. safe 변형은 예외를 잡아 에러 코드를 반환합니다.

함수반환값설명
rdmsrl_safeint (0=성공, -EIO=실패)안전한 64비트 읽기
wrmsrl_safeint (0=성공, -EIO=실패)안전한 64비트 쓰기
rdmsr_safeint안전한 32비트 쌍 읽기
wrmsr_safeint안전한 32비트 쌍 쓰기
/* safe 변형 사용 예 — MSR 존재 여부 불확실할 때 */
u64 val;
int err;

err = rdmsrl_safe(MSR_IA32_ARCH_CAPABILITIES, &val);
if (err) {
    pr_info("IA32_ARCH_CAPABILITIES not supported\\n");
} else {
    if (val & ARCH_CAP_RDCL_NO)
        pr_info("CPU is not vulnerable to Meltdown\\n");
}
코드 설명

arch/x86/include/asm/msr.h에 정의된 rdmsrl_safe의 사용 예입니다. safe 변형은 존재하지 않는 MSR에 접근할 때 발생하는 #GP(0) 예외를 내부적으로 처리하여, 커널 패닉 없이 에러 코드를 반환합니다.

  • rdmsrl_safe(MSR_IA32_ARCH_CAPABILITIES, &val)성공 시 0을 반환하고 val에 MSR 값을 저장합니다. 실패 시 -EIO를 반환합니다. 내부적으로 arch/x86/lib/msr.c의 예외 테이블(exception table) 메커니즘을 사용하여 #GP 예외 발생 시 에러 경로로 분기합니다.
  • if (err)구형 CPU나 일부 가상화 환경에서는 IA32_ARCH_CAPABILITIES(0x10A)가 존재하지 않습니다. safe 변형을 쓰지 않으면 #GP로 커널이 중단되므로, MSR 존재 여부가 불확실한 경우 반드시 safe 변형을 사용해야 합니다.
  • ARCH_CAP_RDCL_NO비트 0을 검사하여 CPU가 Meltdown(Rogue Data Cache Load) 취약점에 하드웨어적으로 면역인지 확인합니다. 면역이면 커널이 KPTI(Kernel Page Table Isolation) 완화를 생략하여 성능 손실을 방지합니다.

Cross-CPU MSR 접근

다른 CPU의 MSR을 읽고 쓰려면 IPI를 통해 해당 CPU에서 실행해야 합니다.

함수설명
rdmsr_on_cpu(cpu, msr, &lo, &hi)특정 CPU에서 MSR 읽기
wrmsr_on_cpu(cpu, msr, lo, hi)특정 CPU에서 MSR 쓰기
rdmsrl_on_cpu(cpu, msr, &val)64비트 버전
wrmsrl_on_cpu(cpu, msr, val)64비트 버전
rdmsr_safe_on_cpu안전한 Cross-CPU 읽기
주의: Cross-CPU MSR 접근은 IPI를 발생시키므로 인터럽트 비활성화 상태에서는 사용할 수 없습니다. 또한 대상 CPU가 오프라인이면 실패합니다.

/dev/cpu/N/msr — 유저 공간 접근

CONFIG_X86_MSR=y일 때 커널은 각 CPU별로 /dev/cpu/<N>/msr 캐릭터 디바이스를 생성합니다. pread()/pwrite()의 오프셋이 MSR 주소, 데이터가 8바이트 MSR 값입니다.

/* 유저 공간 MSR 읽기 예 (root 또는 CAP_SYS_RAWIO 필요) */
#include <fcntl.h>
#include <unistd.h>

int fd = open("/dev/cpu/0/msr", O_RDONLY);
uint64_t tsc;
pread(fd, &tsc, sizeof(tsc), 0x10);  /* IA32_TSC */
printf("TSC = %llu\\n", tsc);
close(fd);
코드 설명

유저 공간에서 /dev/cpu/0/msr 장치 파일을 통해 MSR을 읽는 예제입니다. arch/x86/kernel/msr.c의 MSR 캐릭터 디바이스 드라이버가 이 인터페이스를 제공하며, CONFIG_X86_MSR=y로 빌드해야 합니다.

  • open("/dev/cpu/0/msr", O_RDONLY)CPU 0의 MSR 장치를 엽니다. 파일 경로의 숫자가 CPU 번호를 나타냅니다. root 권한 또는 CAP_SYS_RAWIO 케이퍼빌리티가 필요합니다. 커널 5.10 이후 msr 모듈의 기본 접근이 제한되어 allow_writes=on 파라미터가 필요할 수 있습니다.
  • pread(fd, &tsc, sizeof(tsc), 0x10)pread의 오프셋(0x10)이 MSR 주소(IA32_TSC)에 해당합니다. 드라이버 내부에서 해당 CPU에 IPI를 보내 RDMSR을 실행하고, 8바이트(64비트) MSR 값을 유저 버퍼에 복사합니다. 쓰기는 pwrite로 수행합니다.

AMD 전용 MSR

AMD 프로세서는 0xC0010000~0xC001FFFF 범위에 고유 MSR을 가지고 있습니다.

MSR주소설명
MSR_AMD64_HWCR0xC0010015하드웨어 구성: SMM Lock, TSC Freq Sel, TLB Flush Filter 등
MSR_AMD64_SYSCFG0xC0010010시스템 구성: MTRR 확장, Tom2 활성화, IORRs 등
MSR_AMD64_NB_CFG0xC001001FNorthbridge 구성: 초기 APIC ID 크기 등
MSR_AMD64_PATCH_LEVEL0x0000008B마이크로코드 패치(Patch) 수준
MSR_AMD64_PATCH_LOADER0xC0010020마이크로코드 로더(Loader) 트리거
MSR_AMD64_OSVW_ID_LENGTH0xC0010140OS Visible Workarounds 길이
MSR_AMD64_OSVW_STATUS0xC0010141OS Visible Workarounds 상태
MSR_AMD64_DE_CFG0xC0011029Decode Config (LFENCE 직렬화 등)
MSR_AMD64_LS_CFG0xC0011020Load/Store Config (SSB 비활성화 등)
MSR_AMD_PPIN0xC00102F1Protected Processor ID Number

AMD LFENCE 직렬화

/* arch/x86/kernel/cpu/amd.c — LFENCE를 직렬화 명령어로 설정 */
static void init_amd_lfence(struct cpuinfo_x86 *c)
{
    u64 val;
    rdmsrl(MSR_AMD64_DE_CFG, val);
    if (!(val & BIT(1))) {
        val |= BIT(1);   /* LFENCE를 dispatch serializing으로 */
        wrmsrl(MSR_AMD64_DE_CFG, val);
    }
    set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
}

AMD SVM 관련 MSR

MSR주소설명
MSR_VM_CR0xC0010114SVM 잠금 및 활성화 제어
MSR_VM_HSAVE_PA0xC0010117호스트 상태 저장 영역 물리 주소(Physical Address)
MSR_AMD64_SEV0xC0010131SEV(Secure Encrypted Virtualization) 상태
MSR_AMD64_SEV_ES0xC0010131SEV-ES/SEV-SNP 비트 포함

실전 예제

예제 1: 현재 CPU 주파수 읽기 (APERF/MPERF)

#include <linux/module.h>
#include <asm/msr.h>
#include <asm/cpu_device_id.h>

static int __init freq_init(void)
{
    u64 aperf0, aperf1, mperf0, mperf1;
    u64 tsc_khz_val = tsc_khz;
    unsigned long flags;

    local_irq_save(flags);
    rdmsrl(MSR_IA32_APERF, aperf0);
    rdmsrl(MSR_IA32_MPERF, mperf0);
    local_irq_restore(flags);

    msleep(100);  /* 100ms 대기 */

    local_irq_save(flags);
    rdmsrl(MSR_IA32_APERF, aperf1);
    rdmsrl(MSR_IA32_MPERF, mperf1);
    local_irq_restore(flags);

    /* freq = tsc_freq * delta_aperf / delta_mperf */
    u64 freq_khz = div64_u64(tsc_khz_val * (aperf1 - aperf0),
                              (mperf1 - mperf0));
    pr_info("CPU %d actual freq: %llu MHz\\n",
           smp_processor_id(), freq_khz / 1000);

    return 0;
}

module_init(freq_init);
MODULE_LICENSE("GPL");

예제 2: PMU 카운터로 IPC 측정

/* 고정 카운터로 Instructions Per Cycle 측정 */
static void measure_ipc(void)
{
    u64 inst0, inst1, cycles0, cycles1;

    /* 고정 카운터 활성화: OS + USR 모드 */
    wrmsrl(MSR_CORE_PERF_FIXED_CTR_CTRL, 0x33);  /* CTR0, CTR1 활성화 */
    wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL,
          (1ULL << 32) | (1ULL << 33));  /* Fixed CTR0,1 전역 활성화 */

    rdmsrl(MSR_CORE_PERF_FIXED_CTR0, inst0);    /* Instructions Retired */
    rdmsrl(MSR_CORE_PERF_FIXED_CTR1, cycles0);  /* Unhalted Cycles */

    /* ... 측정 대상 코드 ... */

    rdmsrl(MSR_CORE_PERF_FIXED_CTR0, inst1);
    rdmsrl(MSR_CORE_PERF_FIXED_CTR1, cycles1);

    pr_info("IPC = %llu.%02llu\\n",
           (inst1 - inst0) / (cycles1 - cycles0),
           ((inst1 - inst0) * 100 / (cycles1 - cycles0)) % 100);
}

예제 3: CPU 코어 온도 읽기

static int read_core_temp(int cpu)
{
    u32 eax, edx;
    int tjmax, offset;

    /* TjMax (보통 100°C) */
    rdmsr_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
    tjmax = (eax >> 16) & 0xFF;

    /* Digital Readout */
    rdmsr_on_cpu(cpu, MSR_IA32_THERM_STATUS, &eax, &edx);
    if (!(eax & (1 << 31)))
        return -1;  /* 값이 유효하지 않음 */

    offset = (eax >> 16) & 0x7F;
    return tjmax - offset;  /* °C */
}

예제 4: TSC 기반 정밀 시간 측정

static inline u64 rdtsc_ordered(void)
{
    /* LFENCE + RDTSC 로 순서 보장 */
    barrier();
    return rdtsc();
}

static void benchmark(void)
{
    u64 t0, t1;
    unsigned long flags;

    local_irq_save(flags);
    t0 = rdtsc_ordered();

    /* 측정 대상 코드 */
    some_function();

    t1 = rdtsc_ordered();
    local_irq_restore(flags);

    pr_info("Elapsed: %llu cycles (%llu ns)\\n",
           t1 - t0,
           div64_u64((t1 - t0) * 1000000, tsc_khz));
}

MSR 디버깅

msr-tools 패키지

유저 공간에서 MSR을 직접 읽고 쓸 수 있는 도구입니다.

# 설치 (Debian/Ubuntu)
sudo apt install msr-tools

# msr 커널 모듈 로드
sudo modprobe msr

# IA32_TSC (0x10) 읽기 — CPU 0
sudo rdmsr -p 0 0x10

# IA32_EFER (0xC0000080) 읽기 — 모든 CPU
sudo rdmsr -a 0xC0000080

# IA32_MISC_ENABLE (0x1A0) 읽기 — 비트별 출력
sudo rdmsr -p 0 -b 0x1A0

# IA32_SPEC_CTRL (0x48) 읽기 — Spectre 완화 상태
sudo rdmsr -p 0 0x48

# MSR 쓰기 (주의: 잘못된 값은 시스템 크래시 유발)
sudo wrmsr -p 0 0x48 0x7   # IBRS + STIBP + SSBD 활성화

perf 도구를 이용한 PMU 확인

# PMU 하드웨어 이벤트 목록
perf list hw cache

# PMU 카운터 직접 지정 (event=0x2E, umask=0x41 = LLC-misses)
perf stat -e r412e ./program

# MSR 기반 이벤트 추적
perf stat -e cycles,instructions,cache-misses,branch-misses ./program

# Architectural MSR 기능 확인
perf stat -e cpu/event=0x00,umask=0x01/ -- sleep 1

커널 로그에서 MSR 관련 정보

# 부팅 시 MSR 관련 메시지
dmesg | grep -i msr

# Spectre/Meltdown 완화 상태
cat /sys/devices/system/cpu/vulnerabilities/*

# MTRR 설정 확인
cat /proc/mtrr

# PAT 설정 확인
dmesg | grep -i pat

일반적 함정과 주의사항

MSR 디버깅 시 주의:
  • #GP 예외: 존재하지 않는 MSR에 RDMSR/WRMSR 시 발생. 반드시 CPUID로 확인하거나 *_safe() 변형 사용
  • Reserved 비트: MSR의 예약 비트에 1을 쓰면 #GP 발생. 항상 Read-Modify-Write 패턴 사용
  • per-CPU 특성: 대부분의 MSR은 코어별로 독립적. CPU 0에서 쓴 값이 CPU 1에 적용되지 않음
  • Lock 비트: IA32_FEATURE_CONTROL 등의 Lock 비트가 설정되면 재부팅 전까지 변경 불가
  • 가상화 환경: VM 안에서 일부 MSR은 하이퍼바이저가 에뮬레이션하므로 물리 하드웨어와 값이 다를 수 있음
  • Lockdown LSM: Secure Boot 환경에서 /dev/cpu/*/msr을 통한 쓰기가 차단될 수 있음

MSR 주요 레퍼런스 테이블

자주 사용되는 ~70개 주요 MSR의 종합 레퍼런스입니다. 커널 헤더 arch/x86/include/asm/msr-index.h에서 전체 매크로(Macro)를 확인할 수 있습니다.

주소커널 매크로이름R/W카테고리
0x10MSR_IA32_TSCTime Stamp CounterR/WTSC
0x1BMSR_IA32_APICBASEAPIC Base AddressR/WAPIC
0x3AMSR_IA32_FEAT_CTLFeature ControlR/W*제어
0x3BMSR_IA32_TSC_ADJUSTTSC AdjustR/WTSC
0x48MSR_IA32_SPEC_CTRLSpeculation ControlR/W보안
0x49MSR_IA32_PRED_CMDPrediction CommandW보안
0x8BMSR_IA32_UCODE_REVMicrocode RevisionR시스템
0xCEMSR_PLATFORM_INFOPlatform InfoR주파수
0xE7MSR_IA32_MPERFMax Performance CounterR/W주파수
0xE8MSR_IA32_APERFActual Performance CounterR/W주파수
0xFEMSR_IA32_MTRRCAPMTRR CapabilityRMTRR
0x10AMSR_IA32_ARCH_CAPABILITIESArch CapabilitiesR보안
0x10BMSR_IA32_FLUSH_CMDL1D Flush CommandW보안
0x174MSR_IA32_SYSENTER_CSSYSENTER CSR/W시스콜
0x175MSR_IA32_SYSENTER_ESPSYSENTER ESPR/W시스콜
0x176MSR_IA32_SYSENTER_EIPSYSENTER EIPR/W시스콜
0x186MSR_P6_EVNTSEL0Perf Event Select 0R/WPMU
0x198MSR_IA32_PERF_STATUSPerf StatusR주파수
0x199MSR_IA32_PERF_CTLPerf ControlR/W주파수
0x19AMSR_IA32_CLOCK_MODULATIONClock ModulationR/W
0x19BMSR_IA32_THERM_INTERRUPTThermal InterruptR/W
0x19CMSR_IA32_THERM_STATUSThermal StatusR/W
0x1A0MSR_IA32_MISC_ENABLEMisc EnableR/W제어
0x1A2MSR_IA32_TEMPERATURE_TARGETTemperature Target (TjMax)R
0x1ADMSR_TURBO_RATIO_LIMITTurbo Ratio LimitR주파수
0x1B1MSR_IA32_PACKAGE_THERM_STATUSPackage Thermal StatusR/W
0x200MSR_MTRR_PHYS_BASE(0)MTRR Phys Base 0R/WMTRR
0x250MSR_MTRR_FIX64K_00000MTRR Fixed 64KR/WMTRR
0x277MSR_IA32_CR_PATPage Attribute TableR/WPAT
0x2FFMSR_MTRR_DEF_TYPEMTRR Default TypeR/WMTRR
0x309MSR_CORE_PERF_FIXED_CTR0Fixed CTR: Inst RetiredR/WPMU
0x30AMSR_CORE_PERF_FIXED_CTR1Fixed CTR: Unhalted CyclesR/WPMU
0x30BMSR_CORE_PERF_FIXED_CTR2Fixed CTR: Ref CyclesR/WPMU
0x38DMSR_CORE_PERF_FIXED_CTR_CTRLFixed CTR ControlR/WPMU
0x38EMSR_CORE_PERF_GLOBAL_STATUSGlobal PMU StatusRPMU
0x38FMSR_CORE_PERF_GLOBAL_CTRLGlobal PMU ControlR/WPMU
0x480MSR_IA32_VMX_BASICVMX BasicRVMX
0x48BMSR_IA32_VMX_PROCBASED_CTLS2VMX Secondary ControlsRVMX
0x48CMSR_IA32_VMX_EPT_VPID_CAPEPT/VPID CapabilityRVMX
0x6E0MSR_IA32_TSC_DEADLINETSC DeadlineR/WTSC
0x770MSR_IA32_PM_ENABLEHWP EnableR/WHWP
0x771MSR_IA32_HWP_CAPABILITIESHWP CapabilitiesRHWP
0x774MSR_IA32_HWP_REQUESTHWP RequestR/WHWP
0xC0000080MSR_EFERExtended Feature EnableR/W제어
0xC0000081MSR_STARSYSCALL CS/SSR/W시스콜
0xC0000082MSR_LSTARSYSCALL RIP (64-bit)R/W시스콜
0xC0000084MSR_SYSCALL_MASKSYSCALL RFLAGS MaskR/W시스콜
0xC0000100MSR_FS_BASEFS Base (64-bit)R/W세그먼트
0xC0000101MSR_GS_BASEGS Base (64-bit)R/W세그먼트
0xC0000102MSR_KERNEL_GS_BASEKernel GS Base (SWAPGS)R/W세그먼트
0xC0000103MSR_TSC_AUXTSC Aux (RDTSCP)R/WTSC
0x1B0MSR_IA32_ENERGY_PERF_BIASEnergy Perf Bias (EPB)R/W전력
0x3F8MSR_PKG_C3_RESIDENCYPackage C3 ResidencyRC-State
0x3F9MSR_PKG_C6_RESIDENCYPackage C6 ResidencyRC-State
0x3FAMSR_PKG_C7_RESIDENCYPackage C7 ResidencyRC-State
0x3FCMSR_CORE_C3_RESIDENCYCore C3 ResidencyRC-State
0x3FDMSR_CORE_C6_RESIDENCYCore C6 ResidencyRC-State
0x3FEMSR_CORE_C7_RESIDENCYCore C7 ResidencyRC-State
0x606MSR_RAPL_POWER_UNITRAPL Power UnitRRAPL
0x610MSR_PKG_POWER_LIMITPackage Power Limit (PL1/PL2)R/WRAPL
0x611MSR_PKG_ENERGY_STATUSPackage Energy StatusRRAPL
0x614MSR_PKG_POWER_INFOPackage Power Info (TDP)RRAPL
0x618MSR_DRAM_POWER_LIMITDRAM Power LimitR/WRAPL
0x619MSR_DRAM_ENERGY_STATUSDRAM Energy StatusRRAPL
0x638MSR_PP0_POWER_LIMITCore Power Limit (PP0)R/WRAPL
0x639MSR_PP0_ENERGY_STATUSCore Energy Status (PP0)RRAPL
0x640MSR_PP1_POWER_LIMITGPU Power Limit (PP1)R/WRAPL
0x660MSR_CORE_C1_RESCore C1 ResidencyRC-State
0x772MSR_IA32_HWP_REQUEST_PKGHWP Package RequestR/WHWP
0x773MSR_IA32_HWP_INTERRUPTHWP Interrupt EnableR/WHWP
0x777MSR_IA32_HWP_STATUSHWP StatusR/WHWP
0xC0010015MSR_AMD64_HWCRAMD HW ConfigR/WAMD
0xC0010064MSR_AMD_PSTATE_DEF_BASEAMD P-State Def 0 (P0)RAMD 주파수
0xC0011029MSR_AMD64_DE_CFGAMD Decode ConfigR/WAMD
0xC00102B0MSR_AMD_CPPC_CAP1AMD CPPC CapabilitiesRAMD 주파수
0xC00102B1MSR_AMD_CPPC_ENABLEAMD CPPC EnableR/WAMD 주파수
0xC00102B3MSR_AMD_CPPC_REQAMD CPPC RequestR/WAMD 주파수

MSR 접근 패턴과 API

MSR 접근은 단순한 RDMSR/WRMSR 명령어 실행 이상의 복잡한 계층 구조를 가집니다. 유저 공간에서의 접근 요청이 커널을 거쳐 하드웨어에 도달하기까지의 전체 경로, 안전한 에러 처리 패턴, 그리고 paravirt 환경에서의 차이를 심층적으로 다룹니다.

MSR 접근 아키텍처: 유저 공간 → 커널 → 하드웨어 User Space rdmsr (tool) turbostat /dev/cpu/N/msr (pread/pwrite) ioctl / file_ops Kernel Space (Ring 0) msr_read() rdmsrl_safe() paravirt_read_msr on_each_cpu() ex_handler_msr ↓ native_read_msr() RDMSR opcode CPU Hardware (MSR Bank) ECX=MSR 주소 → EDX:EAX=값 반환 (또는 #GP) #GP(0) if MSR does not exist or reserved bit violation

rdmsrl_safe / wrmsrl_safe 에러 처리 패턴

프로덕션 코드에서는 반드시 _safe 변형을 사용하여 #GP 예외를 안전하게 처리해야 합니다. 특히 CPU 세대별로 지원 여부가 다른 MSR에 접근할 때 필수입니다.

/* arch/x86/kernel/cpu/common.c — 다중 MSR 프로빙 패턴 */
struct msr_probe {
    u32 msr;
    const char *name;
};

static const struct msr_probe security_msrs[] = {
    { MSR_IA32_ARCH_CAPABILITIES, "ARCH_CAP" },
    { MSR_IA32_CORE_CAPABILITIES, "CORE_CAP" },
    { MSR_IA32_TSX_CTRL,          "TSX_CTRL" },
    { 0, NULL },
};

static void probe_security_msrs(void)
{
    const struct msr_probe *p;
    u64 val;
    int err;

    for (p = security_msrs; p->name; p++) {
        err = rdmsrl_safe(p->msr, &val);
        if (err) {
            pr_info("%s: not supported\n", p->name);
            continue;
        }
        pr_info("%s: 0x%016llx\n", p->name, val);
    }
}

on_each_cpu를 이용한 전체 CPU MSR 쓰기

MSR은 코어별로 독립적이므로, 모든 CPU에 동일한 값을 설정하려면 IPI를 사용해야 합니다.

/* 모든 CPU에 MSR 값 브로드캐스트 쓰기 */
struct msr_write_info {
    u32 msr;
    u64 val;
};

static void __msr_write_cpu(void *info)
{
    struct msr_write_info *w = info;
    wrmsrl(w->msr, w->val);
}

static void msr_write_all_cpus(u32 msr, u64 val)
{
    struct msr_write_info info = { .msr = msr, .val = val };

    /* preemption 비활성화 상태에서 IPI 전송 */
    on_each_cpu(__msr_write_cpu, &info, 1);
    pr_info("MSR 0x%x = 0x%llx written to all CPUs\n", msr, val);
}

native_read_msr vs paravirt

Linux 커널은 paravirt ops를 통해 MSR 접근을 가상화할 수 있습니다. 네이티브 환경에서는 직접 RDMSR을 실행하지만, KVM/Xen 게스트에서는 하이퍼바이저가 가로챌 수 있습니다.

/* arch/x86/include/asm/paravirt.h — paravirt MSR 접근 */
static inline u64 paravirt_read_msr(unsigned msr)
{
    return PVOP_CALL1(u64, cpu.read_msr, msr);
}

/* arch/x86/kernel/paravirt.c — 네이티브 구현 */
static u64 native_read_msr(unsigned int msr)
{
    DECLARE_ARGS(val, low, high);
    asm volatile("1: rdmsr\n"
                 "2:\n"
                 _ASM_EXTABLE_TYPE(1b, 2b, EX_TYPE_RDMSR)
                 : EAX_EDX_RET(val, low, high)
                 : "c" (msr));
    return EAX_EDX_VAL(val, low, high);
}

/* Xen PV 환경: MSR 접근이 하이퍼콜로 변환됨 */
/* xen_read_msr_safe() → HYPERVISOR_msr_op() */
팁: CONFIG_PARAVIRT=y 커널에서 MSR 접근은 간접 호출을 통하며, paravirt patching이 부팅 시 네이티브 코드로 대체될 수 있습니다. pv_ops.cpu.read_msrnative_read_msr로 패치되면 오버헤드가 제거됩니다.

Intel TPMI (Topology Aware Register and PM Capsule Interface)

TPMI는 Intel Sapphire Rapids 이후 세대에서 도입된 새로운 레지스터 접근 메커니즘입니다. 기존 MSR이 코어별로 접근해야 하는 한계를 넘어, PCI VSEC(Vendor Specific Extended Capability)를 통해 MMIO 기반으로 다이(die)별·도메인별 전력/성능 레지스터에 접근할 수 있습니다.

Intel TPMI 아키텍처: PCI VSEC → MMIO → Per-Die Feature PCI VSEC Vendor: Intel (8086) Cap ID: DVSEC (0x23) TPMI ID → Feature Table Offset BAR+offset TPMI Header (MMIO) Feature Count Feature Directory 각 Feature의 오프셋/크기 Per-Die Feature Blocks RAPL SST-PP Uncore PEM Linux Kernel TPMI Driver Stack intel_vsec (PCI) intel_tpmi (platform) isst_tpmi (SST) intel_rapl_tpmi (RAPL) 기존 MSR 방식 (레거시) • 코어별 RDMSR/WRMSR • IPI 필요 (cross-CPU) • 가상화 오버헤드 (VM exit) • 패키지/다이 단위 접근 불가 TPMI 방식 (신규) • MMIO 기반, 다이별 직접 접근 • IPI 불필요 • VM exit 없음 (패스스루 가능) • Feature 단위 모듈화

TPMI 드라이버 구조

/* drivers/platform/x86/intel/tpmi.c — TPMI 플랫폼 드라이버 */
struct intel_tpmi_info {
    struct platform_device *pdev;
    struct intel_tpmi_plat_info *plat_info;
    void __iomem *base;         /* MMIO base from PCI BAR */
    u32 feature_count;
    struct intel_tpmi_feature *features;
};

/* Feature ID로 MMIO 주소 조회 */
static void __iomem *tpmi_get_feature_base(
    struct intel_tpmi_info *tpmi,
    int feature_id, int die)
{
    struct intel_tpmi_feature *f;
    int i;

    for (i = 0; i < tpmi->feature_count; i++) {
        f = &tpmi->features[i];
        if (f->id == feature_id)
            return tpmi->base + f->offset +
                   die * f->die_stride;
    }
    return NULL;
}

TPMI Feature Discovery

/* TPMI feature enumeration via PCI VSEC */
static int tpmi_probe(struct platform_device *pdev)
{
    struct intel_tpmi_info *tpmi;
    u32 header, num_entries;
    int i;

    tpmi = devm_kzalloc(&pdev->dev, sizeof(*tpmi), GFP_KERNEL);
    tpmi->base = devm_ioremap_resource(&pdev->dev, ...);

    /* TPMI 헤더에서 feature 수 읽기 */
    header = readl(tpmi->base);
    num_entries = FIELD_GET(TPMI_HDR_NUM_ENTRIES, header);

    for (i = 0; i < num_entries; i++) {
        u32 entry = readl(tpmi->base + 4 + i * 4);
        u16 feature_id = FIELD_GET(TPMI_ENTRY_ID, entry);

        switch (feature_id) {
        case TPMI_ID_RAPL:
            tpmi_create_device("intel_rapl_tpmi", ...);
            break;
        case TPMI_ID_SST:
            tpmi_create_device("isst_tpmi", ...);
            break;
        case TPMI_ID_UNCORE:
            tpmi_create_device("intel_uncore_tpmi", ...);
            break;
        }
    }
    return 0;
}
참고: TPMI는 Intel 서버 플랫폼(Sapphire Rapids, Emerald Rapids, Granite Rapids)에서 주로 사용되며, 클라이언트 플랫폼에서는 아직 일부 기능만 지원됩니다. 커널 6.3부터 CONFIG_INTEL_TPMI=m으로 지원됩니다.

AMD Zen MSR 상세

AMD Zen 아키텍처(Zen 1~Zen 5)는 Intel과 다른 고유 MSR 체계를 가지고 있습니다. 특히 전력 관리에서 PPT(Package Power Tracking), TDP, EDC(Electrical Design Current), THM(Thermal) 제한이 STAPM(Skin Temperature Aware Power Management)과 연동되어 동적으로 부스트 클럭을 제어합니다.

AMD Zen 전력 관리 스택: PPT/TDP/EDC/THM → STAPM → Boost AMD Zen 전력/성능 제한 계층 PPT Package Power Track (전체 패키지 전력) TDP Thermal Design Power (지속 전력 한도) EDC Electrical Design Curr (순간 전류 한도) THM Thermal Limit (Tctl/Tdie 온도) STAPM Controller Skin Temperature Aware Power Management Precision Boost Overdrive (PBO2) Curve Optimizer → per-core V/F 곡선 조정 → 최대 부스트 클럭 결정 주요 AMD MSR CPPC_REQ (0xC00102B3) PSTATE_DEF (0xC0010064) HWCR (0xC0010015) RAPL_PWR_UNIT (0xC0010299) SMU (System Management Unit) 펌웨어가 MSR 값과 센서 데이터를 종합하여 실제 P-State 결정

AMD 주파수 제어 MSR

/* AMD P-State MSR (Zen 2+) */
/* MSR_AMD_PSTATE_DEF_BASE (0xC0010064) + n → P-State n 정의 */
struct amd_pstate_def {
    u64 CpuFid   : 8;   /* [7:0]   CPU 주파수 ID */
    u64 CpuDfsId : 6;   /* [13:8]  CPU DFS(분주) ID */
    u64 CpuVid   : 8;   /* [21:14] CPU 전압 ID */
    u64 IddValue : 8;   /* [29:22] 전류 값 */
    u64 IddDiv   : 2;   /* [31:30] 전류 분배 */
    u64 Rsvd     : 31;  /* [62:32] 예약 */
    u64 PsEn     : 1;   /* [63]    P-State 활성화 */
};

/* 주파수 계산: Core_freq = 200MHz × CpuFid / CpuDfsId */
static u32 amd_pstate_freq(u64 pstate_def)
{
    u32 fid = pstate_def & 0xFF;
    u32 dfs = (pstate_def >> 8) & 0x3F;

    if (dfs == 0)
        return 0;
    return 200 * fid / dfs;  /* MHz */
}

AMD CPPC MSR (Zen 3+)

/* drivers/cpufreq/amd-pstate.c — CPPC MSR 기반 주파수 제어 */
static int amd_pstate_set_epp(struct cpufreq_policy *policy,
                              u8 epp)
{
    u64 cppc_req;
    int ret;

    ret = rdmsrl_safe(MSR_AMD_CPPC_REQ, &cppc_req);
    if (ret)
        return ret;

    /* EPP 필드: bits [31:24] */
    cppc_req &= ~GENMASK_ULL(31, 24);
    cppc_req |= ((u64)epp << 24);

    /* Min/Max perf 설정 */
    cppc_req &= ~GENMASK_ULL(7, 0);   /* min perf */
    cppc_req |= policy->min / 25;    /* 25 MHz 단위 */

    cppc_req &= ~GENMASK_ULL(15, 8);  /* max perf */
    cppc_req |= ((u64)(policy->max / 25)) << 8;

    wrmsrl_on_cpu(smp_processor_id(), MSR_AMD_CPPC_REQ, cppc_req);
    return 0;
}

Precision Boost Overdrive MSR

/* AMD PBO/Curve Optimizer 관련 MSR (비공식, SMU 통신) */
#define MSR_AMD_CPPC_CAP1     0xC00102B0
#define MSR_AMD_CPPC_ENABLE   0xC00102B1
#define MSR_AMD_CPPC_CAP2     0xC00102B2
#define MSR_AMD_CPPC_REQ      0xC00102B3
#define MSR_AMD_CPPC_STATUS   0xC00102B4

/* CPPC Capabilities 읽기 (Zen 3+) */
static void read_amd_cppc_caps(void)
{
    u64 cap1, cap2;

    rdmsrl(MSR_AMD_CPPC_CAP1, cap1);
    pr_info("Highest Perf: %llu\n",  cap1 & 0xFF);
    pr_info("Nominal Perf: %llu\n", (cap1 >> 8) & 0xFF);
    pr_info("Lowest NL:    %llu\n", (cap1 >> 16) & 0xFF);
    pr_info("Lowest Perf:  %llu\n", (cap1 >> 24) & 0xFF);
}
세대별 지원:
  • Zen 1/+: 레거시 P-State MSR만 지원, CPPC는 ACPI 메모리 기반만
  • Zen 2: MSR 기반 CPPC 일부 지원 시작
  • Zen 3: 완전한 MSR 기반 CPPC, amd-pstate 드라이버 사용 가능
  • Zen 4/5: CPPC Preferred Core, Guided Autonomous Mode, EPP 지원

PMU MSR과 perf 연동

Performance Monitoring Unit(PMU)는 하드웨어 이벤트 카운터를 제공하며, Linux의 perf_event 서브시스템이 이를 추상화합니다. 이 섹션에서는 PMU MSR의 정밀한 설정, PEBS(Processor Event-Based Sampling), LBR(Last Branch Record), Intel PT(Processor Trace) 등 고급 프로파일링(Profiling) 기능의 MSR 수준 동작을 다룹니다.

PMU 아키텍처: IA32_PERFEVTSELx → IA32_PMCx → perf_event → NMI PMU Hardware (per-Core) IA32_PERFEVTSELx (이벤트 선택) IA32_PMCx (카운터 값) IA32_PERF_GLOBAL_CTRL IA32_PERF_GLOBAL_STATUS IA32_PEBS_ENABLE MSR_LBR_SELECT 오버플로 PMI (NMI) LVTPC → APIC Linux perf_event Subsystem x86_pmu_ops perf_event_open() ring_buffer PEBS / LBR / PT DS Area (IA32_DS_AREA) Hardware-assisted sampling User Space Tools perf stat perf record perf top BPF mmap ring buffer

IA32_PERF_GLOBAL_CTRL 설정

/* arch/x86/events/intel/core.c — PMU 글로벌 활성화 */
static void intel_pmu_enable_all(int added)
{
    struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
    u64 ctrl;

    /* 활성화된 카운터 비트 마스크 구성 */
    ctrl = cpuc->intel_ctrl_guest_mask |
           cpuc->intel_ctrl_host_mask;

    /* IA32_PERF_GLOBAL_CTRL (0x38F):
     *   Bits [N-1:0]  = GP 카운터 N개 활성화
     *   Bits [34:32]  = Fixed 카운터 3개 활성화
     *   Bit  [35]     = Perf Metrics 활성화 (ICL+)
     */
    wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl);
}

PEBS 구성

/* PEBS (Processor Event-Based Sampling) 설정 */
static void intel_pmu_pebs_enable(struct perf_event *event)
{
    struct hw_perf_event *hwc = &event->hw;
    u64 pebs_enable;

    /* IA32_PEBS_ENABLE (0x3F1):
     *   Bit n = GP 카운터 n에 대해 PEBS 활성화
     *   Bit 32+n = GP 카운터 n에 대한 PEBS output 선택
     */
    rdmsrl(MSR_IA32_PEBS_ENABLE, pebs_enable);
    pebs_enable |= 1ULL << hwc->idx;

    /* DS Area (Debug Store) 설정 — PEBS 레코드 버퍼 */
    /* IA32_DS_AREA (0x600): DS 관리 영역의 선형 주소 */
    wrmsrl(MSR_IA32_DS_AREA, (unsigned long)cpuc->ds);
    wrmsrl(MSR_IA32_PEBS_ENABLE, pebs_enable);
}

/* PEBS 레코드 구조: 정확한 IP, 레지스터 값, 메모리 주소 등 포함 */
struct pebs_basic {
    u64 format_size;
    u64 ip;            /* 정확한 명령어 주소 (EventingIP) */
    u64 applicable_counters;
    u64 tsc;
};

LBR MSR 설정

/* LBR (Last Branch Record) MSR 구성 */
static void intel_pmu_lbr_enable_all(bool pmi)
{
    struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);

    /* MSR_LBR_SELECT (0x1C8): LBR 필터링
     *   Bit 0: CPL_EQ_0  — Ring 0 분기 기록
     *   Bit 1: CPL_NEQ_0 — Ring 3 분기 기록
     *   Bit 2: JCC       — 조건 분기
     *   Bit 3: NEAR_REL_CALL — 근접 상대 콜
     *   Bit 4: NEAR_IND_CALL — 근접 간접 콜
     *   Bit 5: NEAR_RET  — 근접 리턴
     *   Bit 8: NEAR_IND_JMP  — 간접 점프
     */
    wrmsrl(MSR_LBR_SELECT, cpuc->lbr_sel->config);

    /* IA32_DEBUGCTL (0x1D9): LBR/BTS/FREEZE 제어
     *   Bit 0: LBR   — LBR 기록 활성화
     *   Bit 1: BTF   — Branch Trace Flag
     *   Bit 6: TR    — Branch Trace 메시지 활성화
     *   Bit 11: FREEZE_WHILE_SMM
     */
    u64 debugctl;
    rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
    debugctl |= DEBUGCTLMSR_LBR;
    wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
}

Intel PT MSR 구성

/* Intel Processor Trace MSR 설정 */
static void pt_config_start(struct perf_event *event)
{
    u64 ctl = 0;

    /* MSR_IA32_RTIT_CTL (0x570): PT 제어
     *   Bit 0:  TraceEn   — 트레이스 활성화
     *   Bit 1:  CYCEn     — 사이클 패킷 활성화
     *   Bit 2:  OS        — Ring 0 트레이스
     *   Bit 3:  User      — Ring 3 트레이스
     *   Bit 6:  FabricEn  — 패브릭 포트 출력
     *   Bit 8:  TSCEn     — TSC 패킷 삽입
     *   Bit 13: BranchEn  — 분기 패킷 활성화
     */
    ctl |= RTIT_CTL_TRACEEN;
    ctl |= RTIT_CTL_OS | RTIT_CTL_USR;
    ctl |= RTIT_CTL_TSC_EN | RTIT_CTL_BRANCH_EN;

    /* Output: ToPA (Table of Physical Addresses) 모드 */
    wrmsrl(MSR_IA32_RTIT_OUTPUT_BASE, ...);  /* 0x560 */
    wrmsrl(MSR_IA32_RTIT_OUTPUT_MASK, ...);  /* 0x561 */
    wrmsrl(MSR_IA32_RTIT_CTL, ctl);            /* 0x570 */
}
주의: PMU 카운터 수는 CPU 모델에 따라 다릅니다. CPUID.0AH에서 GP 카운터 수(EAX[15:8])와 비트 폭(EAX[23:16])을 확인해야 합니다. Skylake 이상은 일반적으로 8개 GP 카운터, Ice Lake+는 Topdown 메트릭용 Fixed 카운터도 추가됩니다.

주파수 스케일링(Frequency Scaling) MSR

HWP(Hardware-controlled P-States)는 Intel Skylake부터 도입된 자율 주파수 관리 메커니즘으로, 기존 소프트웨어 기반 EIST/SpeedStep을 대체합니다. 커널의 intel_pstate 드라이버가 HWP MSR을 통해 성능 범위와 에너지 선호도를 설정하면, CPU 하드웨어가 자율적으로 최적 주파수를 결정합니다.

HWP MSR (IA32_HWP_REQUEST)

/* drivers/cpufreq/intel_pstate.c — HWP 요청 설정 */
static void intel_pstate_hwp_set(unsigned int cpu)
{
    u64 hwp_req;
    struct cpudata *cpu_data = all_cpu_data[cpu];

    /* MSR_IA32_HWP_REQUEST (0x774) 비트 레이아웃:
     *   [7:0]   Minimum_Performance
     *   [15:8]  Maximum_Performance
     *   [23:16] Desired_Performance (0=자율)
     *   [31:24] Energy_Performance_Preference
     *            0x00 = Maximum Performance
     *            0x80 = Balanced (기본)
     *            0xFF = Maximum Energy Saving
     *   [41:32] Activity_Window
     *   [42]    Package_Control
     */
    hwp_req  = cpu_data->hwp_req_cached;
    hwp_req &= ~HWP_MIN_PERF(~0L);
    hwp_req |= HWP_MIN_PERF(cpu_data->min_perf_ratio);
    hwp_req &= ~HWP_MAX_PERF(~0L);
    hwp_req |= HWP_MAX_PERF(cpu_data->max_perf_ratio);
    hwp_req &= ~HWP_EPP(~0L);
    hwp_req |= HWP_EPP(cpu_data->epp_cached);

    wrmsrl_on_cpu(cpu, MSR_IA32_HWP_REQUEST, hwp_req);
}

MSR_IA32_PERF_CTL / STATUS (레거시 EIST)

/* 레거시 P-State 제어 (HWP 미지원 CPU 또는 passive 모드) */
static void intel_pstate_set_pstate(struct cpudata *cpu,
                                    int pstate)
{
    u64 val;

    /* MSR_IA32_PERF_CTL (0x199):
     *   Bits [15:0]: Target P-State ratio
     *   Bit  [32]:   IDA(Turbo) Engage (0=enable)
     */
    val = (u64)pstate << 8;  /* ratio in [15:8] */
    wrmsrl(MSR_IA32_PERF_CTL, val);

    /* MSR_IA32_PERF_STATUS (0x198, Read-Only):
     *   현재 동작 중인 P-State ratio 반환
     */
    rdmsrl(MSR_IA32_PERF_STATUS, val);
    cpu->pstate.current_pstate = (val >> 8) & 0xFF;
}

Turbo Ratio MSR

/* 코어 수별 최대 터보 배율 읽기 */
static void read_turbo_ratios(void)
{
    u64 val;
    int i;

    /* MSR_TURBO_RATIO_LIMIT (0x1AD):
     *   [7:0]   = 1-core 활성 시 최대 ratio
     *   [15:8]  = 2-core 활성 시 최대 ratio
     *   [23:16] = 3-core 활성 시 최대 ratio
     *   ... 최대 8 코어까지
     */
    rdmsrl(MSR_TURBO_RATIO_LIMIT, val);
    for (i = 0; i < 8; i++) {
        u8 ratio = (val >> (i * 8)) & 0xFF;
        pr_info("%d-core turbo: %u MHz\n",
                i + 1, ratio * 100);
    }

    /* MSR_TURBO_RATIO_LIMIT1 (0x1AE): 코어 9~16 */
    /* MSR_TURBO_RATIO_LIMIT2 (0x1AF): 코어 17~24 */

    /* HWP Capabilities도 확인 */
    rdmsrl(MSR_IA32_HWP_CAPABILITIES, val); /* 0x771 */
    pr_info("HWP Highest: %llu, Guaranteed: %llu\n",
            val & 0xFF, (val >> 8) & 0xFF);
}
EPP 정책 매핑: intel_pstate 드라이버는 /sys/devices/system/cpu/cpufreq/policy*/energy_performance_preference sysfs를 통해 EPP 값을 설정합니다. performance=0, balance_performance=128, balance_power=192, power=255 등이 일반적인 매핑입니다.

가상화 MSR 패스스루

KVM/VMX 환경에서 게스트 VM의 MSR 접근은 MSR 비트맵(MSR bitmap)에 의해 제어됩니다. 비트맵에서 해당 MSR이 "통과(passthrough)"로 설정되면 VM exit 없이 직접 접근하고, "인터셉트"로 설정되면 호스트로 제어가 전환됩니다.

VMX MSR 비트맵: 읽기/쓰기 비트맵과 Exit 제어 Guest VM (Ring 0) RDMSR ECX=0x10 (TSC) WRMSR ECX=0x48 (SPEC_CTRL) MSR Bitmap (4KB) Read Low (0-1FFF) Read High (C000-) Write Low (0-1FFF) Write High (C000-) bit=0: Passthrough (VM exit 없음) bit=1: Intercept (VM exit 발생) bit=0 bit=1 Direct Hardware Access 게스트가 직접 MSR 읽기/쓰기 (고성능) VM Exit → KVM Handler handle_rdmsr() / handle_wrmsr() MSR Emulation / Filtering kvm_set_msr() → 에뮬레이션 or 하드웨어 전달

MSR 비트맵 구성

/* arch/x86/kvm/vmx/vmx.c — MSR 비트맵 설정 */
static void vmx_set_msr_bitmap_read(unsigned long *msr_bitmap,
                                    u32 msr)
{
    /* MSR 비트맵 레이아웃 (4096 bytes = 4 × 1024):
     *   Offset 0x000: Read bitmap  — MSR 0x00000000~0x00001FFF
     *   Offset 0x400: Read bitmap  — MSR 0xC0000000~0xC0001FFF
     *   Offset 0x800: Write bitmap — MSR 0x00000000~0x00001FFF
     *   Offset 0xC00: Write bitmap — MSR 0xC0000000~0xC0001FFF
     */
    if (msr <= 0x1FFF) {
        /* Low range: bit 설정 = intercept */
        __set_bit(msr, msr_bitmap);
    } else if (msr >= 0xC0000000 && msr <= 0xC0001FFF) {
        __set_bit(msr - 0xC0000000,
                  msr_bitmap + 0x400 / sizeof(long));
    }
}

/* Passthrough 예: TSC는 직접 접근 허용 */
vmx_clear_msr_bitmap_read(vmx->vmcs01.msr_bitmap,
                          MSR_IA32_TSC);

/* Intercept 예: SPEC_CTRL은 호스트가 관리 */
vmx_set_msr_bitmap_read(vmx->vmcs01.msr_bitmap,
                         MSR_IA32_SPEC_CTRL);
vmx_set_msr_bitmap_write(vmx->vmcs01.msr_bitmap,
                          MSR_IA32_SPEC_CTRL);

KVM MSR 인터셉트 처리

/* arch/x86/kvm/x86.c — MSR 에뮬레이션 핸들러 */
int kvm_emulate_rdmsr(struct kvm_vcpu *vcpu)
{
    u32 ecx = kvm_rcx_read(vcpu);
    u64 data;
    int r;

    r = kvm_get_msr(vcpu, ecx, &data);
    if (r) {
        /* MSR 읽기 실패 → 게스트에 #GP 주입 */
        kvm_inject_gp(vcpu, 0);
        return 1;
    }

    /* 결과를 EDX:EAX에 설정 */
    kvm_rax_write(vcpu, data & 0xFFFFFFFF);
    kvm_rdx_write(vcpu, data >> 32);
    return kvm_skip_emulated_instruction(vcpu);
}

/* MSR 필터링: 유저 공간 (QEMU)이 접근 가능 MSR 목록 설정 */
/* KVM_X86_SET_MSR_FILTER ioctl로 allow/deny 규칙 지정 */
성능 최적화: MSR passthrough가 많을수록 VM 성능이 좋아집니다. 예를 들어, IA32_TSC, IA32_TSC_AUX, IA32_SPEC_CTRL 등을 passthrough하면 프로파일링 오버헤드가 크게 줄어듭니다. 그러나 보안상 민감한 MSR(예: IA32_APIC_BASE)은 반드시 인터셉트해야 합니다.

보안 감사용 MSR 점검

시스템 보안 감사에서 MSR 상태 확인은 CPU 수준의 보안 기능이 올바르게 활성화되었는지 검증하는 핵심 절차입니다. Spectre/Meltdown 완화, SMEP/SMAP, CET(Control-flow Enforcement Technology) 등의 활성화 여부를 MSR 값으로 직접 확인할 수 있습니다.

Spectre 완화 MSR 검증

/* 보안 감사: Spectre/Meltdown 완화 MSR 점검 모듈 */
#include <linux/module.h>
#include <asm/msr.h>

static void audit_spec_ctrl(void)
{
    u64 spec_ctrl, arch_caps;
    int err;

    /* IA32_SPEC_CTRL (0x48) 점검 */
    err = rdmsrl_safe(MSR_IA32_SPEC_CTRL, &spec_ctrl);
    if (!err) {
        pr_info("IBRS:  %s\n", spec_ctrl & 1 ? "ON" : "OFF");
        pr_info("STIBP: %s\n", spec_ctrl & 2 ? "ON" : "OFF");
        pr_info("SSBD:  %s\n", spec_ctrl & 4 ? "ON" : "OFF");
    }

    /* IA32_ARCH_CAPABILITIES (0x10A) — 하드웨어 완화 */
    err = rdmsrl_safe(MSR_IA32_ARCH_CAPABILITIES, &arch_caps);
    if (!err) {
        pr_info("RDCL_NO (Meltdown immune):  %s\n",
                arch_caps & ARCH_CAP_RDCL_NO ? "YES" : "NO");
        pr_info("IBRS_ALL (enhanced IBRS):   %s\n",
                arch_caps & ARCH_CAP_IBRS_ALL ? "YES" : "NO");
        pr_info("MDS_NO:                     %s\n",
                arch_caps & ARCH_CAP_MDS_NO ? "YES" : "NO");
        pr_info("TAA_NO:                     %s\n",
                arch_caps & ARCH_CAP_TAA_NO ? "YES" : "NO");
        pr_info("SBDR_SSDP_NO:               %s\n",
                arch_caps & ARCH_CAP_SBDR_SSDP_NO ? "YES" : "NO");
    }
}

SMEP/SMAP/CET 확인

/* CR4 기반 보안 기능 확인 (MSR을 통한 간접 검증) */
static void audit_cr4_security(void)
{
    unsigned long cr4 = native_read_cr4();

    pr_info("SMEP (Supervisor Mode Execution Prevention): %s\n",
            cr4 & X86_CR4_SMEP ? "ENABLED" : "DISABLED");
    pr_info("SMAP (Supervisor Mode Access Prevention):    %s\n",
            cr4 & X86_CR4_SMAP ? "ENABLED" : "DISABLED");
    pr_info("CET  (Control-flow Enforcement Technology):   %s\n",
            cr4 & X86_CR4_CET ? "ENABLED" : "DISABLED");
    pr_info("UMIP (User-Mode Instruction Prevention):      %s\n",
            cr4 & X86_CR4_UMIP ? "ENABLED" : "DISABLED");

    /* CET 세부 MSR 확인 */
    if (cr4 & X86_CR4_CET) {
        u64 scet, ucet;
        rdmsrl(MSR_IA32_S_CET, scet);     /* 0x6A2 */
        rdmsrl(MSR_IA32_U_CET, ucet);     /* 0x6A0 */
        pr_info("  S-CET SH_STK_EN: %s, IBT: %s\n",
                scet & 1 ? "ON" : "OFF",
                scet & 4 ? "ON" : "OFF");
        pr_info("  U-CET SH_STK_EN: %s, IBT: %s\n",
                ucet & 1 ? "ON" : "OFF",
                ucet & 4 ? "ON" : "OFF");
    }
}

마이크로코드 리비전 점검

/* 마이크로코드 버전 확인 — 보안 패치 적용 검증 */
static void audit_microcode(void)
{
    u64 ucode_rev;
    struct cpuinfo_x86 *c = &boot_cpu_data;

    /* IA32_BIOS_SIGN_ID (0x8B) — 마이크로코드 리비전 */
    rdmsrl(MSR_IA32_UCODE_REV, ucode_rev);
    pr_info("Microcode revision: 0x%08llx\n",
            ucode_rev >> 32);
    pr_info("CPU: %s Family %xh Model %xh Stepping %x\n",
            c->x86_vendor == X86_VENDOR_INTEL ?
            "Intel" : "AMD",
            c->x86, c->x86_model, c->x86_stepping);

    /* Spectre v2 필요 마이크로코드 확인 예:
     * Intel SKL-SP: 최소 0x02000065 이상 필요
     * Spectre BHI: 최소 0x0300000f 이상 필요
     */
    if ((ucode_rev >> 32) < 0x02000065 &&
        c->x86_model == 0x55) {
        pr_warn("WARNING: Microcode too old for full Spectre mitigation!\n");
    }
}
주의: /sys/devices/system/cpu/vulnerabilities/ 파일은 커널이 판단한 취약점 상태를 보여주지만, MSR을 직접 읽으면 하드웨어 수준의 실제 완화 상태를 확인할 수 있습니다. 두 정보가 불일치하면 커널 버전과 마이크로코드 업데이트가 필요할 수 있습니다.

유저스페이스 MSR 도구

커널 모듈(Kernel Module)을 작성하지 않고도 유저 공간에서 MSR을 읽고 분석할 수 있는 다양한 도구가 있습니다. msr-tools, turbostat, cpupower 등이 내부적으로 /dev/cpu/N/msr을 통해 MSR에 접근합니다.

msr-tools 고급 사용법

# msr-tools: 모든 CPU의 HWP 상태 확인
# IA32_HWP_REQUEST (0x774) 읽기
for cpu in /dev/cpu/*/msr; do
    cpunum=$(echo $cpu | grep -o '[0-9]*')
    val=$(sudo rdmsr -p $cpunum 0x774 2>/dev/null)
    if [ -n "$val" ]; then
        min=$((16#${val:14:2}))
        max=$((16#${val:12:2}))
        epp=$((16#${val:8:2}))
        echo "CPU$cpunum: min=$min max=$max EPP=$epp"
    fi
done

# RAPL 에너지 카운터 읽기 (MSR_PKG_ENERGY_STATUS)
sudo rdmsr -p 0 0x611
# 단위 변환: MSR_RAPL_POWER_UNIT (0x606)의 [12:8] = Energy Unit
unit=$(sudo rdmsr -p 0 0x606)
energy_unit=$(( (16#$unit >> 8) & 0x1F ))
echo "Energy unit: 2^-${energy_unit} Joules"

# MSR 쓰기 예: EPP를 performance(0)로 설정
# 주의: Read-Modify-Write 필수
val=$(sudo rdmsr -p 0 0x774)
# bits [31:24] = EPP, 0으로 설정
new_val=$(printf "0x%016x" $(( (16#$val & ~0xFF000000) | 0x00000000 )))
sudo wrmsr -p 0 0x774 $new_val

turbostat 내부 동작

# turbostat: CPU 주파수/전력/C-State 모니터링
# 내부적으로 읽는 주요 MSR:
#   MSR_IA32_APERF (0xE8)          — Actual Performance
#   MSR_IA32_MPERF (0xE7)          — Maximum Performance
#   MSR_PKG_ENERGY_STATUS (0x611)  — 패키지 에너지
#   MSR_CORE_C3/C6/C7_RESIDENCY    — C-State 체류 시간
#   MSR_PKG_C2/C3/C6/C7_RESIDENCY  — 패키지 C-State

# 기본 실행 (1초 간격)
sudo turbostat --interval 1

# 특정 카운터만 선택
sudo turbostat --show Core,CPU,Avg_MHz,Busy%,Bzy_MHz,PkgWatt

# 프로그램 실행 중 모니터링
sudo turbostat --quiet -- ./benchmark

# CSV 출력 (스크립트 분석용)
sudo turbostat --interval 1 --out turbo.csv -- stress -c 8 -t 30

# Busy%: MPERF/TSC × 100 (실제 CPU 활성 비율)
# Bzy_MHz: TSC_MHz × APERF/MPERF (실제 동작 주파수)

cpupower MSR 접근

# cpupower: cpufreq 드라이버를 통한 간접 MSR 제어

# 현재 드라이버 및 거버너 확인
cpupower frequency-info

# EPP 설정 (HWP MSR을 간접적으로 제어)
sudo cpupower set --perf-bias 0    # 최고 성능
sudo cpupower set --perf-bias 15   # 최대 절전

# 주파수 범위 설정 (MSR_IA32_HWP_REQUEST 간접 변경)
sudo cpupower frequency-set -g performance
sudo cpupower frequency-set -d 2.4GHz -u 4.8GHz

# idle 상태 확인 (C-State 관련)
cpupower idle-info

# 특정 C-State 비활성화
sudo cpupower idle-set -d 3  # C3 이상 비활성화

# 모니터 모드 (MSR 기반 카운터 실시간 표시)
sudo cpupower monitor -m Mperf
권한 요구사항: /dev/cpu/N/msr 접근에는 CAP_SYS_RAWIO 또는 root 권한이 필요합니다. Lockdown LSM이 활성화된 경우(Secure Boot) 쓰기가 차단될 수 있으며, CONFIG_X86_MSR=ymodprobe msr이 선행되어야 합니다.

Per-CPU MSR 스냅샷 기법

MSR은 코어별로 독립적이므로, 시스템 전체 MSR 상태를 파악하려면 모든 CPU에서 동시에 값을 수집해야 합니다. 이 기법은 성능 분석, 전력 감사, 보안 점검에서 핵심적으로 사용됩니다.

Per-CPU MSR 스냅샷 흐름: for_each_online_cpu → smp_call_function_single → rdmsr Snapshot Initiator for_each_online_cpu(cpu) IPI CPU 0: smp_call → rdmsr CPU 1: smp_call → rdmsr CPU N: smp_call → rdmsr Per-CPU Snapshot Array snapshot[0] = { msr_addr, value, cpu } snapshot[1] = { msr_addr, value, cpu } ... snapshot[N] = { msr_addr, value, cpu } TSC 기준 타임스탬프 포함 Analysis & Diff CPU 간 MSR 값 비교 (일관성 확인) 시간별 스냅샷 차이 계산 이상치 탐지 (HWP/SPEC_CTRL 불일치) 보안 경고: CPU별 IBRS/SSBD 불일치 활용 사례 1. 전력 분석: RAPL/HWP 값 CPU별 비교 2. 보안 감사: SPEC_CTRL 전 CPU 일관성 확인 3. 성능 프로파일링: PMU 카운터 동시 수집 4. 디버깅: MTRR/PAT 설정 CPU별 비교

Per-CPU 스냅샷 커널 모듈

/* Per-CPU MSR 스냅샷 수집 모듈 */
#include <linux/module.h>
#include <linux/smp.h>
#include <asm/msr.h>

struct msr_snapshot {
    u32 msr_addr;
    u64 value;
    u64 tsc;
    int cpu;
    int err;
};

static struct msr_snapshot snapshots[NR_CPUS];

static const u32 audit_msrs[] = {
    MSR_IA32_SPEC_CTRL,      /* 0x48 */
    MSR_IA32_ARCH_CAPABILITIES, /* 0x10A */
    MSR_IA32_HWP_REQUEST,   /* 0x774 */
    MSR_IA32_APERF,         /* 0xE8 */
    MSR_IA32_MPERF,         /* 0xE7 */
};

static void __snapshot_msr(void *info)
{
    u32 msr = *(u32 *)info;
    int cpu = smp_processor_id();
    struct msr_snapshot *s = &snapshots[cpu];

    s->msr_addr = msr;
    s->cpu = cpu;
    s->tsc = rdtsc();
    s->err = rdmsrl_safe(msr, &s->value);
}

static void take_msr_snapshot(u32 msr)
{
    int cpu;

    on_each_cpu(__snapshot_msr, &msr, 1);

    for_each_online_cpu(cpu) {
        struct msr_snapshot *s = &snapshots[cpu];
        if (s->err)
            pr_info("CPU%d: MSR 0x%x — not supported\n",
                    cpu, msr);
        else
            pr_info("CPU%d: MSR 0x%x = 0x%016llx (TSC=%llu)\n",
                    cpu, msr, s->value, s->tsc);
    }
}

스냅샷 비교 스크립트

#!/bin/bash
# Per-CPU MSR 스냅샷 비교 (msr-tools 기반)
# 사용법: ./msr-diff.sh <MSR_ADDR>

MSR=${1:-0x48}  # 기본: IA32_SPEC_CTRL
NCPU=$(nproc)
declare -A values

echo "=== MSR 0x$MSR Per-CPU Snapshot ==="
for ((cpu=0; cpu<NCPU; cpu++)); do
    val=$(sudo rdmsr -p $cpu 0x$MSR 2>/dev/null)
    if [ $? -eq 0 ]; then
        values[$cpu]=$val
        echo "CPU$cpu: 0x$val"
    else
        echo "CPU$cpu: (not supported)"
    fi
done

# 일관성 확인
unique=$(printf '%s\n' "${values[@]}" | sort -u | wc -l)
if [ "$unique" -eq 1 ]; then
    echo "[OK] 모든 CPU에서 동일한 값"
else
    echo "[WARNING] CPU별 MSR 값 불일치 감지!"
    printf '%s\n' "${values[@]}" | sort | uniq -c | sort -rn
fi
참고: on_each_cpu()는 IPI를 사용하므로 인터럽트가 활성화된 상태에서만 호출 가능합니다. 또한 CPU 핫플러그가 진행 중일 때는 cpus_read_lock()으로 보호해야 합니다. smp_call_function_single()은 특정 CPU 하나에서만 실행시킬 때 사용합니다.

MSR 오류 처리와 예외

MSR 접근 시 발생할 수 있는 #GP(General Protection) 예외는 커널 크래시의 주요 원인 중 하나입니다. Linux 커널은 exception table 메커니즘을 통해 이를 안전하게 처리하며, 이 섹션에서는 그 내부 동작과 올바른 에러 처리 패턴을 상세히 다룹니다.

존재하지 않는 MSR에서의 #GP 예외

/* #GP(0) 발생 시나리오:
 * 1. 존재하지 않는 MSR 주소에 RDMSR/WRMSR
 * 2. 예약(Reserved) 비트에 1 쓰기
 * 3. Lock 비트가 설정된 MSR에 쓰기 시도
 * 4. 권한 부족 (CPL > 0에서 RDMSR/WRMSR)
 */

/* 안전하지 않은 접근 — 커널 패닉 가능! */
static void unsafe_msr_access(void)
{
    u64 val;
    /* 이 MSR이 존재하지 않으면 #GP → oops → 커널 패닉 */
    rdmsrl(0xDEADBEEF, val);  /* 절대 이렇게 하지 마세요! */
}

/* 안전한 접근 — exception table 활용 */
static int safe_msr_access(void)
{
    u64 val;
    int err;

    err = rdmsrl_safe(0xDEADBEEF, &val);
    if (err) {
        pr_info("MSR 0xDEADBEEF does not exist (err=%d)\n", err);
        return -ENODEV;
    }
    return 0;
}

ex_handler_msr 복구 메커니즘

/* arch/x86/mm/extable.c — MSR 예외 핸들러 */

/* RDMSR에 대한 exception table 항목 처리 */
bool ex_handler_rdmsr_unsafe(const struct exception_table_entry *fixup,
                             struct pt_regs *regs)
{
    /* #GP 발생 시:
     * 1. EAX = 0, EDX = 0 (읽기 결과를 0으로)
     * 2. RIP를 fixup 주소로 변경 (정상 흐름 복귀)
     */
    WARN_ONCE(1, "unchecked MSR access error: RDMSR from 0x%x at %pS\n",
              (unsigned int)regs->cx,
              (void *)regs->ip);

    regs->ax = 0;
    regs->dx = 0;
    regs->ip = ex_fixup_addr(fixup);
    return true;
}

/* rdmsrl_safe의 인라인 어셈블리에서 exception table 생성 */
/*
 * asm volatile("1: rdmsr\n"
 *              "   xor %[err], %[err]\n"
 *              "2:\n"
 *              _ASM_EXTABLE_TYPE_REG(1b, 2b, EX_TYPE_RDMSR_SAFE, %[err])
 *              : [err] "=a" (err), "=d" (high), "=a" (low)
 *              : "c" (msr));
 *
 * exception table: 1b → 2b, type=RDMSR_SAFE
 * #GP at 1b → jump to 2b, err = -EIO
 */

MSR 범위 검증

/* MSR 접근 전 유효성 검증 패턴 */
static bool msr_range_valid(u32 msr)
{
    /* x86 MSR 주소 공간 유효 범위 */
    static const struct {
        u32 start, end;
        const char *desc;
    } valid_ranges[] = {
        { 0x00000000, 0x00001FFF, "Architectural" },
        { 0x40000000, 0x400000FF, "Hyper-V" },
        { 0xC0000000, 0xC0001FFF, "AMD/x86-64" },
        { 0xC0010000, 0xC001FFFF, "AMD-specific" },
    };
    int i;

    for (i = 0; i < ARRAY_SIZE(valid_ranges); i++) {
        if (msr >= valid_ranges[i].start &&
            msr <= valid_ranges[i].end)
            return true;
    }
    return false;
}

/* Read-Modify-Write 안전 패턴 */
static int msr_set_bits_safe(u32 msr, u64 bits_to_set)
{
    u64 val;
    int err;

    err = rdmsrl_safe(msr, &val);
    if (err)
        return err;

    val |= bits_to_set;
    err = wrmsrl_safe(msr, val);
    if (err) {
        pr_warn("Failed to write MSR 0x%x: %d "
                "(possible reserved bit violation)\n",
                msr, err);
    }
    return err;
}
핵심 규칙:
  • 커널 드라이버에서 MSR 접근 시 반드시 _safe 변형을 사용하세요. bare rdmsrl()/wrmsrl()은 해당 MSR이 100% 존재한다고 확신할 때만 사용합니다.
  • MSR 쓰기 전에 항상 Read-Modify-Write 패턴을 사용하세요. 전체 64비트를 덮어쓰면 예약 비트에 잘못된 값이 들어갈 수 있습니다.
  • 새로운 CPU 모델이 추가될 때 MSR 주소나 비트 레이아웃이 변경될 수 있으므로, arch/x86/include/asm/msr-index.h의 매크로를 사용하고 CPUID로 기능 지원 여부를 먼저 확인하세요.

참고 링크

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