MSR 레지스터 (Model-Specific Register)
x86 MSR 종합 가이드: RDMSR/WRMSR 명령어, 시스템 제어/SYSCALL/APIC/PMU/전력/보안/가상화 MSR, MTRR/PAT, Linux 커널 MSR API, AMD 전용 MSR, Spectre 완화, KVM MSR 비트맵, msr-tools 디버깅.
RDMSR/WRMSR 명령어로만 접근할 수 있으며, Ring 0(커널)에서만 사용 가능합니다.
핵심 요약
- MSR — Model-Specific Register. CPU 동작 모드, 성능, 전력, 보안을 제어하는 특수 레지스터입니다.
- RDMSR / WRMSR — MSR을 읽고 쓰는 특권 명령어. ECX에 MSR 번호를 지정합니다.
- 주요 MSR — TSC(타임스탬프), EFER(Long Mode), APIC_BASE, MTRR(메모리 타입), PAT 등이 있습니다.
- rdmsr / wrmsr 도구 — 사용자 공간에서
/dev/cpu/N/msr을 통해 MSR을 읽고 쓸 수 있습니다.
단계별 이해
- MSR 개념 잡기 — 일반 레지스터(범용, 세그먼트)와 달리 MSR은 번호(인덱스)로 식별되는 특수 목적 레지스터입니다.
Intel SDM Vol.4에 수천 개의 MSR이 문서화되어 있습니다.
- 읽기 실습 —
sudo rdmsr 0x10으로 TSC(Time Stamp Counter) 값을 읽어봅니다.msr-tools패키지를 설치하고modprobe msr로 MSR 드라이버를 로드합니다. - 커널 API — 드라이버/커널 코드에서는
rdmsrl()/wrmsrl()함수로 MSR에 접근합니다.잘못된 MSR 쓰기는 시스템 크래시를 유발할 수 있으므로 주의가 필요합니다.
MSR 개요
MSR(Model-Specific Register)은 x86 프로세서에 내장된 특수 레지스터 집합으로, CPU의 동작 모드 제어, 성능 모니터링, 전력 관리, 보안 완화 등 다양한 기능을 담당합니다. "Model-Specific"이라는 이름처럼 원래는 CPU 모델마다 다른 레지스터 세트를 가졌으나, 현재는 많은 MSR이 Intel/AMD 간에 공통으로(architectural) 정의되어 있습니다.
MSR의 역사와 발전
- Intel Pentium (1993): RDMSR/WRMSR 명령어 및 TSC(Time Stamp Counter) 최초 도입
- Pentium Pro (P6, 1995): 성능 모니터링 카운터(PMC), MTRR 도입
- AMD K8 (2003): SYSCALL/SYSRET용 MSR, NX bit(IA32_EFER.NXE) 도입
- Intel Core 이후: Architectural Performance Monitoring, SpeedStep/HWP MSR 확대
- Spectre/Meltdown (2018~): IA32_SPEC_CTRL, IA32_PRED_CMD, IA32_ARCH_CAPABILITIES 등 보안 MSR 대거 추가
MSR 주소 공간
MSR은 32비트 주소 공간(0x00000000 ~ 0xFFFFFFFF)을 사용하며, 각 MSR은 64비트(EDX:EAX) 값을 가집니다. 주소 범위는 대략 다음과 같이 구분됩니다:
| 주소 범위 | 용도 | 예시 |
|---|---|---|
0x00000000 ~ 0x00001FFF | Architectural MSR | IA32_TSC, IA32_APIC_BASE |
0x00000100 ~ 0x000001FF | Fixed/PMC 영역 | IA32_MTRR*, PMC |
0x00000174 ~ 0x00000176 | SYSENTER MSR | IA32_SYSENTER_CS/ESP/EIP |
0x00000186 ~ 0x0000018F | 성능 이벤트 선택 | IA32_PERFEVTSEL0~7 |
0x000001A0 | 기능 제어 | IA32_MISC_ENABLE |
0x00000300 ~ 0x000003FF | Fixed Counter / PMC | IA32_FIXED_CTR0~2 |
0x00000400 ~ 0x00000477 | MC(Machine Check) 뱅크 | IA32_MCi_CTL/STATUS |
0x00000480 ~ 0x0000049F | VMX 기능 보고 | IA32_VMX_BASIC 등 |
0x00000800 ~ 0x000008FF | x2APIC | ICR, LVT 등 |
0xC0000000 ~ 0xC0001FFF | AMD/Long mode MSR | MSR_EFER, MSR_STAR, MSR_LSTAR |
0xC0010000 ~ 0xC001FFFF | AMD 전용 | HWCR, SYSCFG, NB_CFG |
접근 권한
RDMSR/WRMSR 명령어는 Ring 0(커널 모드)에서만 실행 가능합니다. 유저 공간에서 실행하면 #GP(General Protection) 예외가 발생합니다. 다만 RDTSC, RDTSCP, RDPMC 등 일부 전용 명령어는 CR4 비트 설정에 따라 Ring 3에서도 실행할 수 있습니다.
/dev/cpu/<n>/msr 장치 파일 또는 msr-tools 패키지의 rdmsr/wrmsr 유틸리티를 사용합니다. 단, CONFIG_X86_MSR=y로 커널이 빌드되어야 하며, CAP_SYS_RAWIO 권한이 필요합니다.RDMSR / WRMSR 명령어
명령어 형식
| 명령어 | 입력 | 출력 | 설명 |
|---|---|---|---|
RDMSR | ECX = MSR 주소 | EDX:EAX = 64비트 값 | MSR 읽기 |
WRMSR | ECX = MSR 주소, EDX:EAX = 값 | 없음 | MSR 쓰기 |
RDTSC | 없음 | EDX:EAX = TSC 값 | IA32_TSC 전용 읽기 |
RDTSCP | 없음 | EDX:EAX = TSC, ECX = TSC_AUX | 직렬화된 TSC 읽기 |
RDPMC | ECX = PMC 인덱스 | EDX:EAX = 카운터 값 | PMC 전용 읽기 |
직렬화와 순서 보장
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:EDX | bit 5 | MSR 명령어 지원 (RDMSR/WRMSR) |
| CPUID.01H:EDX | bit 4 | TSC 지원 |
| CPUID.01H:ECX | bit 15 | PDCM (IA32_PERF_CAPABILITIES) |
| CPUID.80000001H:EDX | bit 20 | NX bit (IA32_EFER.NXE) |
| CPUID.80000001H:EDX | bit 11 | SYSCALL/SYSRET |
| CPUID.07H:EBX | bit 0 | FSGSBASE 명령어 |
| CPUID.07H:EDX | bit 26 | IA32_SPEC_CTRL / IBRS |
| CPUID.07H:EDX | bit 29 | IA32_ARCH_CAPABILITIES |
시스템 제어 MSR
IA32_EFER (0xC0000080) — Extended Feature Enable Register
Long Mode 활성화와 NX bit 제어를 담당하는 핵심 MSR입니다.
| 비트 | 이름 | 설명 |
|---|---|---|
| 0 | SCE | SYSCALL/SYSRET Enable |
| 8 | LME | Long Mode Enable (IA-32e 활성화) |
| 10 | LMA | Long Mode Active (읽기 전용, CR0.PG로 활성화) |
| 11 | NXE | No-Execute Enable (W^X 보안) |
| 12 | SVME | SVM Enable (AMD-V 전용) |
| 13 | LMSLE | Long Mode Segment Limit Enable (AMD) |
| 14 | FFXSR | Fast FXSAVE/FXRSTOR (AMD) |
| 15 | TCE | Translation 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);
}
IA32_MISC_ENABLE (0x1A0)
다양한 CPU 기능을 활성화/비활성화하는 범용 제어 레지스터입니다.
| 비트 | 이름 | 설명 |
|---|---|---|
| 0 | Fast-Strings Enable | REP MOVSB/STOSB 최적화 |
| 7 | Performance Monitoring Available | PMU 사용 가능 여부 |
| 11 | Branch Trace Storage Unavailable | BTS 비활성화 (읽기 전용) |
| 12 | PEBS Unavailable | PEBS 비활성화 (읽기 전용) |
| 16 | Enhanced SpeedStep Enable | EIST/P-state 전환 활성화 |
| 18 | ENABLE_MONITOR_FSM | MONITOR/MWAIT 활성화 |
| 22 | Limit CPUID Maxval | CPUID 리프 제한 (레거시 OS 호환) |
| 23 | xTPR Message Disable | TPR 메시지 비활성화 |
| 34 | XD Bit Disable | NX bit 전역 비활성화 |
IA32_FEATURE_CONTROL (0x3A)
VMX(가상화) 활성화 및 BIOS 잠금을 제어합니다. BIOS/UEFI 펌웨어가 부팅 시 설정하며, Lock bit이 설정되면 재부팅 전까지 변경 불가합니다.
| 비트 | 이름 | 설명 |
|---|---|---|
| 0 | Lock | 1로 설정 시 이 MSR은 읽기 전용 (재부팅까지) |
| 1 | Enable VMX in SMX | TXT 환경에서 VMX 활성화 |
| 2 | Enable VMX outside SMX | 일반 환경에서 VMX 활성화 |
| 8~14 | SENTER Local Function Enables | TXT SENTER 리프 활성화 |
| 15 | SENTER Global Enable | TXT SENTER 전역 활성화 |
| 17 | SGX Launch Control Enable | SGX Launch Enclave 제어 |
| 18 | SGX Global Enable | SGX 전역 활성화 |
/* 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);
}
SYSCALL / SYSRET MSR
Long Mode에서 SYSCALL/SYSRET 명령어는 시스템 콜 진입/복귀를 위해 3개의 MSR을 사용합니다. 이 MSR들은 IA32_EFER.SCE=1일 때만 유효합니다.
| MSR | 주소 | 용도 |
|---|---|---|
MSR_STAR | 0xC0000081 | SYSCALL의 CS/SS 선택자 (비트 47:32 = kernel CS, 비트 63:48 = user CS) |
MSR_LSTAR | 0xC0000082 | SYSCALL 진입점의 64비트 RIP (Long Mode) |
MSR_CSTAR | 0xC0000083 | SYSCALL 진입점 RIP (Compatibility Mode, Linux 미사용) |
MSR_SYSCALL_MASK | 0xC0000084 | SYSCALL 시 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 실행 시 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_BASE | 0xC0000100 | FS 세그먼트 base (유저: TLS/glibc) |
IA32_GS_BASE | 0xC0000101 | GS 세그먼트 base (현재 활성 GS) |
IA32_KERNEL_GS_BASE | 0xC0000102 | SWAPGS로 교환할 GS base (대기 값) |
SWAPGS 메커니즘
SWAPGS는 IA32_GS_BASE와 IA32_KERNEL_GS_BASE를 원자적으로 교환합니다. 시스템 콜/인터럽트 진입 시 커널은 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");
}
APIC MSR
IA32_APIC_BASE (0x1B)
Local APIC의 기본 주소와 활성화 상태를 제어합니다.
| 비트 | 이름 | 설명 |
|---|---|---|
| 8 | BSP | Bootstrap Processor 플래그 (읽기 전용) |
| 10 | EXTD | x2APIC 모드 활성화 |
| 11 | EN | APIC 전역 활성화 |
| 12~35 | APIC Base | APIC MMIO base 주소 (기본: 0xFEE00000) |
x2APIC 모드 MSR (0x800~0x8FF)
x2APIC 모드에서는 MMIO 대신 MSR을 통해 APIC 레지스터에 접근합니다. 이는 MMIO보다 빠르고 32비트 APIC ID를 지원하여 256 CPU 이상의 시스템을 지원할 수 있습니다.
| MSR 주소 | xAPIC MMIO 오프셋 | 이름 | 접근 |
|---|---|---|---|
0x802 | 0x20 | APIC ID | R |
0x803 | 0x30 | APIC Version | R |
0x808 | 0x80 | Task Priority (TPR) | R/W |
0x80B | 0xB0 | EOI | W |
0x80F | 0xF0 | Spurious Interrupt Vector | R/W |
0x830 | 0x300+0x310 | ICR (64비트 통합) | R/W |
0x832~838 | 0x320~380 | LVT (Timer, Thermal, PMI 등) | R/W |
0x83F | — | Self IPI | W |
/* 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);
}
성능 모니터링 MSR (PMU)
Performance Monitoring Unit(PMU) MSR은 CPU 이벤트(캐시 미스, 분기 예측 실패, 명령어 은퇴 등)를 하드웨어 수준에서 카운팅합니다. perf 도구가 내부적으로 이 MSR들을 프로그래밍합니다.
Architectural PMU (버전 2+)
| MSR | 주소 | 설명 |
|---|---|---|
IA32_PERFEVTSELx | 0x186 + x | 이벤트 선택 레지스터 (이벤트/umask/usr/os/edge 등) |
IA32_PMCx | 0x0C1 + x | 범용 성능 카운터 (48비트) |
IA32_FIXED_CTR0 | 0x309 | Instruction Retired (고정 카운터) |
IA32_FIXED_CTR1 | 0x30A | Unhalted Core Cycles (고정 카운터) |
IA32_FIXED_CTR2 | 0x30B | Unhalted Reference Cycles (고정 카운터) |
IA32_FIXED_CTR_CTRL | 0x38D | 고정 카운터 제어 (OS/USR/PMI 설정) |
IA32_PERF_GLOBAL_CTRL | 0x38F | 전역 카운터 활성화 비트맵 |
IA32_PERF_GLOBAL_STATUS | 0x38E | 오버플로 상태 (PMI 인터럽트 원인) |
IA32_PERF_GLOBAL_OVF_CTRL | 0x390 | 오버플로 상태 클리어 |
IA32_PERFEVTSELx 비트 필드
비트 63 0
┌────────┬───┬───┬───┬────┬───┬───┬───┬──────────┬──────────┐
│Reserved│INV│CNT│ANY│ EN │INT│ PC│ E │ UnitMask │ Event │
│ 63:32 │ 23│22 │21 │ 22 │20 │19 │18 │ 15:8 │ Select │
│ │ │MASK │ │ │ │ │ (umask) │ 7:0 │
└────────┴───┴───┴───┴────┴───┴───┴───┴──────────┴──────────┘
EN (bit 22) = 카운터 활성화
INT (bit 20) = 오버플로 시 PMI 인터럽트 발생
USR (bit 16) = Ring 3 이벤트 카운팅
OS (bit 17) = Ring 0 이벤트 카운팅
/* 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의 전력 소비를 제한하고 측정.
MSR_PLATFORM_INFO (0xCE) — 플랫폼 주파수 정보
CPU의 기본 주파수 비율(ratio), 최소/최대 비율, TDP 관련 정보를 담고 있는 읽기 전용 MSR입니다. intel_pstate 드라이버가 초기화 시 가장 먼저 읽는 MSR입니다.
| 비트 | 필드 | 설명 |
|---|---|---|
| 15:8 | Maximum Non-Turbo Ratio | 기본 주파수 비율 (Base Frequency = BCLK × 이 값). 예: 0x22 = 34 → 3.4GHz (BCLK 100MHz) |
| 23:16 | — | 예약됨 |
| 28 | Programmable Ratio Limit for Turbo | 1이면 MSR_TURBO_RATIO_LIMIT 프로그래밍 가능 |
| 29 | Programmable TDP Limit for Turbo | 1이면 TDP 제한 변경 가능 |
| 30 | Programmable TJ Offset | 1이면 TjMax 오프셋 변경 가능 |
| 40:32 | Maximum Efficiency Ratio | 최소 효율 주파수 비율 (최저 P-State). 예: 0x04 = 400MHz |
| 47:41 | — | 예약됨 |
| 55:48 | Minimum 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:0 | Target Ratio | 목표 주파수 비율 (BCLK × ratio = 목표 주파수) |
| 15:8 | — | 예약됨 (일부 모델에서 VID 인코딩) |
| 16 | IDA/Turbo Disengage | 1이면 해당 코어의 터보 부스트 비활성화 |
| 31:17 | — | 예약됨 |
| 32 | IDA/Turbo Disengage (64-bit) | 일부 모델에서 추가 터보 제어 |
IA32_PERF_STATUS (0x198) — 현재 P-State (읽기 전용)
| 비트 | 필드 | 설명 |
|---|---|---|
| 15:0 | Current 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이어야 동작
*/
}
IA32_MISC_ENABLE(0x1A0)의 비트 16(Enhanced SpeedStep Enable)이 0이면 EIST가 비활성화되어 IA32_PERF_CTL 쓰기가 무시됩니다. BIOS에서 SpeedStep을 활성화해야 합니다.MSR_TURBO_RATIO_LIMIT (0x1AD) — 터보 부스트 비율 제한
활성 코어 수에 따른 최대 터보 부스트 비율을 정의합니다. 활성 코어가 적을수록 더 높은 주파수까지 부스트할 수 있습니다 (열/전력 여유분 활용).
| 비트 | 필드 | 설명 |
|---|---|---|
| 7:0 | 1C Max Turbo Ratio | 1코어 활성 시 최대 비율 (가장 높음) |
| 15:8 | 2C Max Turbo Ratio | 2코어 활성 시 최대 비율 |
| 23:16 | 3C Max Turbo Ratio | 3코어 활성 시 최대 비율 |
| 31:24 | 4C Max Turbo Ratio | 4코어 활성 시 최대 비율 |
| 39:32 | 5C Max Turbo Ratio | 5코어 활성 시 최대 비율 |
| 47:40 | 6C Max Turbo Ratio | 6코어 활성 시 최대 비율 |
| 55:48 | 7C Max Turbo Ratio | 7코어 활성 시 최대 비율 |
| 63:56 | 8C Max Turbo Ratio | 8코어 활성 시 최대 비율 |
/* 터보 비율 파싱 — 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%,PkgWattHWP (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 활성화
| 비트 | 필드 | 설명 |
|---|---|---|
| 0 | HWP_ENABLE | 1로 설정하면 HWP 활성화. 비가역적: 한 번 활성화하면 재부팅 전까지 비활성화 불가. |
| 63:1 | — | 예약됨 (0으로 유지) |
IA32_PM_ENABLE에 1을 쓰면 HWP가 영구(재부팅까지) 활성화됩니다. 다시 0으로 되돌릴 수 없으므로, 커널 드라이버는 부팅 초기에 한 번만 설정합니다.IA32_HWP_CAPABILITIES (0x771) — 하드웨어 성능 범위 (읽기 전용)
CPU가 보고하는 P-State 범위. OS는 이 범위 내에서 HWP_REQUEST를 설정해야 합니다.
| 비트 | 필드 | 설명 |
|---|---|---|
| 7:0 | Highest_Performance | 최대 성능 수준 (터보 포함). 예: 49 → 4.9GHz |
| 15:8 | Guaranteed_Performance | 지속 보장 성능 수준 (Base Freq). 예: 34 → 3.4GHz |
| 23:16 | Most_Efficient_Performance | 최고 에너지 효율 성능 수준. 예: 8 → 800MHz |
| 31:24 | Lowest_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:0 | Minimum_Performance | 허용 최저 성능. 이 이하로 내려가지 않음. 절전 vs 응답 지연 트레이드오프. |
| 15:8 | Maximum_Performance | 허용 최대 성능. 이 이상 올라가지 않음. 전력/발열 제한에 활용. |
| 23:16 | Desired_Performance | 목표 성능 힌트. 0이면 HW가 자율 결정 (자율 모드). |
| 31:24 | Energy_Performance_Preference | EPP 값 (0~255): 0=최대 성능, 128=균형, 255=최대 절전. |
| 41:32 | Activity_Window | 부하 모니터링 창 크기 (10비트: 7비트 값 + 3비트 지수). 0이면 HW 기본값. |
| 42 | Package_Control | 1이면 이 코어의 요청이 패키지 레벨에서 적용 |
| 59:43 | — | 예약됨 |
| 63:60 | — | 예약됨 |
EPP (Energy Performance Preference) 상세
EPP는 HWP에서 성능과 에너지 효율 사이의 균형점을 OS가 CPU에 전달하는 핵심 파라미터입니다. 값이 작을수록 성능 우선, 클수록 절전 우선입니다.
| EPP 값 | Linux 정책 | sysfs 값 | 동작 특성 |
|---|---|---|---|
| 0 | performance | performance | 최대 터보 부스트, 전력 무제한. 고성능 서버/게이밍. |
| 64 | balance_performance | balance_performance | 성능 우선이지만 약간의 절전 고려. 데스크톱 기본값. |
| 128 | default/normal | default | 성능과 절전 균형. 일반 워크로드. |
| 192 | balance_power | balance_power | 절전 우선이지만 합리적 성능 유지. 노트북 배터리 모드. |
| 255 | power | power | 최대 절전, 최저 주파수 유지. 극한 배터리 절약. |
/* 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);
}
}
echo "balance_performance" > /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preferencecat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_available_preferences 로 사용 가능한 값 확인.IA32_HWP_REQUEST_PKG (0x772) — 패키지 레벨 HWP 요청
패키지(소켓) 전체에 대한 HWP 요청을 설정합니다. 코어별 HWP_REQUEST에서 Package_Control 비트(42)가 0인 경우 이 MSR의 값이 기본 적용됩니다.
| 비트 | 필드 | 설명 |
|---|---|---|
| 7:0 | Minimum_Performance | 패키지 전체 최저 성능 |
| 15:8 | Maximum_Performance | 패키지 전체 최대 성능 |
| 23:16 | Desired_Performance | 패키지 전체 목표 성능 |
| 31:24 | Energy_Performance_Preference | 패키지 전체 EPP |
| 41:32 | Activity_Window | 패키지 전체 활동 창 |
IA32_HWP_INTERRUPT (0x773) — HWP 변경 알림 인터럽트
| 비트 | 필드 | 설명 |
|---|---|---|
| 0 | EN_Guaranteed_Performance_Change | Guaranteed Performance가 변경될 때 인터럽트 발생 |
| 1 | EN_Excursion_Minimum | 성능이 Minimum 이하로 떨어질 때 인터럽트 발생 |
| 2 | EN_Highest_Performance_Change | Highest Performance가 변경될 때 인터럽트 발생 |
IA32_HWP_STATUS (0x777) — HWP 상태 피드백
| 비트 | 필드 | 설명 |
|---|---|---|
| 0 | Guaranteed_Performance_Change | 1이면 보장 성능 수준이 변경됨 (W1C: 1 쓰면 클리어) |
| 2 | Excursion_To_Minimum | 1이면 성능이 요청 최소치 이하로 내려갔음 (W1C) |
| 4 | Highest_Performance_Change | 1이면 최대 성능 수준이 변경됨 (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_MPERF | 0xE7 | Maximum Performance 카운터. 최대 비터보 주파수(TSC rate)에 비례하여 증가. C0 상태에서만 증가. |
IA32_APERF | 0xE8 | Actual 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의 Bzy_MHz 컬럼은 base_freq × ΔAPERF/ΔMPERF를, Busy%는 ΔMPERF/ΔTSC × 100을 보여줍니다. Avg_MHz는 base_freq × ΔAPERF/ΔTSC입니다.RAPL (Running Average Power Limit) MSR
RAPL은 Intel Sandy Bridge 이후 도입된 전력 측정 및 제한 메커니즘으로, 패키지/코어/DRAM/GPU의 실시간 전력 소비를 모니터링하고, 전력 상한을 설정할 수 있습니다. 커널의 intel_rapl 드라이버와 powercap 프레임워크가 이 MSR들을 추상화합니다.
RAPL 도메인 (Power Domain)
| 도메인 | 약칭 | 측정/제한 대상 | 주요 MSR 기반 주소 |
|---|---|---|---|
| Package (PKG) | PKG | CPU 패키지 전체 (코어 + Uncore + GPU) | 0x610 ~ 0x614 |
| Power Plane 0 (PP0) | PP0 | CPU 코어 영역만 | 0x638 ~ 0x63A |
| Power Plane 1 (PP1) | PP1 | 내장 GPU (클라이언트 CPU만) | 0x640 ~ 0x642 |
| DRAM | DRAM | 메모리 컨트롤러/DRAM (서버 CPU) | 0x618 ~ 0x61C |
| Platform (PSys) | PSYS | 전체 시스템 (Skylake+, 노트북) | 0x64C ~ 0x650 |
MSR_RAPL_POWER_UNIT (0x606) — 전력 단위
RAPL MSR의 값을 물리 단위로 변환하기 위한 단위 정보입니다. 모든 RAPL 값 해석의 기준이 됩니다.
| 비트 | 필드 | 설명 |
|---|---|---|
| 3:0 | Power Units | 전력 단위 = 1/(2^n) 와트. 보통 n=3 → 0.125W |
| 12:8 | Energy Status Units | 에너지 단위 = 1/(2^n) 줄. 보통 n=14 → ~61μJ |
| 19:16 | Time 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:0 | Power Limit 1 (PL1) | 장기 전력 제한 (Power Units 단위). 예: 600 × 0.125W = 75W TDP |
| 15 | PL1 Enable | 1이면 PL1 활성화 |
| 16 | PL1 Clamping Limitation | 1이면 PL1 미만으로도 클램핑 허용 (더 공격적 제한) |
| 23:17 | PL1 Time Window | PL1 시간 창 (Time Units 사용). 이 시간 동안의 평균 전력이 PL1 이하여야 함 |
| 46:32 | Power Limit 2 (PL2) | 단기 전력 제한. 보통 PL1의 1.25배. 예: 93.75W |
| 47 | PL2 Enable | 1이면 PL2 활성화 |
| 48 | PL2 Clamping Limitation | PL2 클램핑 허용 |
| 55:49 | PL2 Time Window | PL2 시간 창 (보통 ~28ms) |
| 63 | Lock | 1이면 이 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:0 | Total 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 도메인별 MSR 주소 정리
| 용도 | PKG | PP0 (Core) | PP1 (GPU) | DRAM | PSys |
|---|---|---|---|---|---|
| Power Limit | 0x610 | 0x638 | 0x640 | 0x618 | 0x64C |
| Energy Status | 0x611 | 0x639 | 0x641 | 0x619 | 0x64D |
| Power Info | 0x614 | 0x63A | 0x642 | 0x61C | — |
| Perf Status | — | 0x63B | — | 0x61B | — |
| Policy | — | 0x63C | 0x642 | — | — |
/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 유휴 상태 제어와 각 C-State의 레지던시(체류 시간) 측정에 사용됩니다. cpuidle 드라이버(intel_idle)와 turbostat이 이 MSR들을 활용합니다.
MSR_PKG_CST_CONFIG_CONTROL (0xE2) — 패키지 C-State 제어
| 비트 | 필드 | 설명 |
|---|---|---|
| 3:0 | Package C-State Limit | 허용 최대 패키지 C-State. 0=C0, 1=C2, 2=C6, 3=C6N, 7=제한 없음 (모델별 상이) |
| 10 | I/O MWAIT Redirection Enable | 1이면 I/O 명령어를 MWAIT C-State로 리다이렉션 |
| 15 | CFG Lock | 1이면 이 MSR 잠금 (재부팅까지 변경 불가). BIOS가 설정. |
| 25 | C3 State Auto Demotion Enable | 1이면 C3 자동 디모션 활성화 |
| 26 | C1 State Auto Demotion Enable | 1이면 C1 자동 디모션 활성화 |
| 27 | Enable C3 Undemotion | 1이면 C3 언디모션 활성화 |
| 28 | Enable C1 Undemotion | 1이면 C1 언디모션 활성화 |
C-State 레지던시 MSR (코어/패키지)
| MSR | 주소 | 범위 | 설명 |
|---|---|---|---|
MSR_CORE_C1_RES | 0x660 | 코어 | 코어 C1 레지던시 카운터 (TSC 단위) |
MSR_CORE_C3_RESIDENCY | 0x3FC | 코어 | 코어 C3 레지던시 카운터 |
MSR_CORE_C6_RESIDENCY | 0x3FD | 코어 | 코어 C6 레지던시 카운터 |
MSR_CORE_C7_RESIDENCY | 0x3FE | 코어 | 코어 C7 레지던시 카운터 |
MSR_PKG_C2_RESIDENCY | 0x60D | 패키지 | 패키지 C2 레지던시 카운터 |
MSR_PKG_C3_RESIDENCY | 0x3F8 | 패키지 | 패키지 C3 레지던시 카운터 |
MSR_PKG_C6_RESIDENCY | 0x3F9 | 패키지 | 패키지 C6 레지던시 카운터 |
MSR_PKG_C7_RESIDENCY | 0x3FA | 패키지 | 패키지 C7 레지던시 카운터 |
MSR_PKG_C8_RESIDENCY | 0x630 | 패키지 | 패키지 C8 레지던시 (Haswell+) |
MSR_PKG_C9_RESIDENCY | 0x631 | 패키지 | 패키지 C9 레지던시 (Haswell+) |
MSR_PKG_C10_RESIDENCY | 0x632 | 패키지 | 패키지 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:0 | Energy Policy Hint | 0=최대 성능, 7=균형, 15=최대 절전 |
| 63:4 | — | 예약됨 |
| 값 | Linux 상수 | 의미 |
|---|---|---|
| 0 | ENERGY_PERF_BIAS_PERFORMANCE | 최대 성능 — 전력 무시 |
| 4 | ENERGY_PERF_BIAS_BALANCE_PERFORMANCE | 성능 우선 균형 |
| 6 | ENERGY_PERF_BIAS_NORMAL | 일반 (커널 기본값) |
| 8 | ENERGY_PERF_BIAS_BALANCE_POWERSAVE | 절전 우선 균형 |
| 15 | ENERGY_PERF_BIAS_POWERSAVE | 최대 절전 |
/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가 훨씬 효율적이므로 거의 사용되지 않습니다.
| 비트 | 필드 | 설명 |
|---|---|---|
| 0 | Extended On-Demand Clock Modulation Enable | 1이면 fine-grained duty cycle 사용 (3:1 비트 포함 4비트) |
| 3:1 | On-Demand Clock Modulation Duty Cycle | 듀티 사이클: 001=12.5%, 010=25%, ..., 111=87.5% |
| 4 | On-Demand Clock Modulation Enable | 1이면 클럭 변조 활성화 |
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 활성화
| 비트 | 필드 | 설명 |
|---|---|---|
| 0 | CPPC Enable | 1이면 CPPC(하드웨어 자율 P-State) 활성화 |
MSR_AMD_CPPC_CAP1 (0xC00102B0) — CPPC 성능 범위
| 비트 | 필드 | 설명 |
|---|---|---|
| 7:0 | Highest Performance | 최대 성능 수준 (부스트 포함) |
| 15:8 | Nominal Performance | 공칭 성능 수준 (Base Frequency 상당) |
| 23:16 | Lowest Nonlinear Performance | 에너지 효율이 급격히 떨어지기 시작하는 하한 |
| 31:24 | Lowest Performance | 절대 최저 성능 수준 |
MSR_AMD_CPPC_REQ (0xC00102B3) — CPPC 성능 요청
| 비트 | 필드 | 설명 |
|---|---|---|
| 7:0 | Maximum Performance | 허용 최대 성능 |
| 15:8 | Minimum Performance | 허용 최저 성능 |
| 23:16 | Desired Performance | 목표 성능 (0=자율) |
| 31:24 | Energy Performance Preference | EPP (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:0 | CpuFid | CPU Frequency ID |
| 13:8 | CpuDfsId | CPU DFS (Divider) ID |
| 21:14 | CpuVid | CPU Voltage ID |
| 22 | IddDiv | 전류 분배기 |
| 29:23 | IddValue | 전류 값 |
| 63 | PstateEn | 1이면 이 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=passive: OS governor가 목표 주파수 결정 → 드라이버가 CPPC MSR에 변환하여 전달.amd-pstate=active: amd-pstate-epp 드라이버 사용, EPP 기반 하드웨어 자율 관리 (Intel HWP와 유사).amd-pstate=guided: OS가 min/max만 설정, 그 범위 내에서 하드웨어가 자율 결정.cpufreq 드라이버와 MSR 매핑
Linux 커널의 cpufreq 프레임워크는 MSR을 직접 노출하지 않고, sysfs를 통해 추상화합니다. 각 sysfs 파일이 어떤 MSR을 읽고 쓰는지 이해하면 디버깅에 유용합니다.
| sysfs 경로 | Intel MSR | AMD MSR | 설명 |
|---|---|---|---|
scaling_min_freq | HWP_REQUEST[7:0] | CPPC_REQ[15:8] | 최소 주파수 설정 |
scaling_max_freq | HWP_REQUEST[15:8] | CPPC_REQ[7:0] | 최대 주파수 설정 |
scaling_cur_freq | APERF/MPERF 계산 | APERF/MPERF 계산 | 현재 실제 주파수 |
cpuinfo_min_freq | PLATFORM_INFO[40:32] | CPPC_CAP1[31:24] | 하드웨어 최소 주파수 |
cpuinfo_max_freq | TURBO_RATIO_LIMIT[7:0] | CPPC_CAP1[7:0] | 하드웨어 최대 주파수 |
base_frequency | PLATFORM_INFO[15:8] | CPPC_CAP1[15:8] | 기본 주파수 (비터보) |
energy_performance_preference | HWP_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)
열 관리 MSR
CPU 온도 모니터링과 열 스로틀링(thermal throttling) 제어를 위한 MSR입니다.
| MSR | 주소 | 설명 |
|---|---|---|
IA32_THERM_STATUS | 0x19C | 코어별 열 상태 및 온도 읽기 |
IA32_THERM_INTERRUPT | 0x19B | 열 인터럽트 임계값 설정 |
IA32_PACKAGE_THERM_STATUS | 0x1B1 | 패키지 전체 열 상태 |
IA32_PACKAGE_THERM_INTERRUPT | 0x1B2 | 패키지 열 인터럽트 설정 |
IA32_TEMPERATURE_TARGET | 0x1A2 | TjMax (최대 허용 온도, 읽기 전용) |
온도 계산 방법
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_TSC | 0x10 | TSC 카운터 값 (RDMSR 또는 RDTSC로 읽기) |
IA32_TSC_ADJUST | 0x3B | TSC 보정 오프셋 (가상화/핫플러그 시 동기화용) |
IA32_TSC_DEADLINE | 0x6E0 | APIC TSC-Deadline 모드 타이머 목표값 |
IA32_TSC_AUX | 0xC0000103 | RDTSCP로 읽는 보조 값 (보통 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);
}
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을 읽어 불필요한 완화를 건너뜁니다.
| 비트 | 이름 | 의미 |
|---|---|---|
| 0 | RDCL_NO | Meltdown(Rogue Data Cache Load) 면역 |
| 1 | IBRS_ALL | IBRS가 모든 분기에 적용 (Enhanced IBRS) |
| 2 | RSBA | Return Stack Buffer 대안 사용 (retpoline 주의) |
| 3 | SKIP_L1DFL_VMENTRY | VM 진입 시 L1D flush 불필요 |
| 4 | SSB_NO | Speculative Store Bypass 면역 |
| 5 | MDS_NO | Microarchitectural Data Sampling 면역 |
| 6 | IF_PSCHANGE_MC_NO | 페이지 크기 변경 시 MC 면역 |
| 7 | TSX_CTRL | IA32_TSX_CTRL MSR 존재 |
| 8 | TAA_NO | TSX Asynchronous Abort 면역 |
추론 실행 제어 MSR
| MSR | 주소 | 설명 |
|---|---|---|
IA32_SPEC_CTRL | 0x48 | IBRS(bit 0), STIBP(bit 1), SSBD(bit 2) 제어 |
IA32_PRED_CMD | 0x49 | IBPB(bit 0) — 분기 예측기 배리어 (쓰기 전용) |
IA32_FLUSH_CMD | 0x10B | L1D_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);
}
가상화 MSR
Intel VMX 관련 MSR
VMX(Virtual Machine Extensions) 기능을 보고하고 제어하는 MSR입니다. KVM 모듈이 VMX 초기화 시 이 MSR들을 읽어 지원되는 기능을 확인합니다.
| MSR | 주소 | 설명 |
|---|---|---|
IA32_VMX_BASIC | 0x480 | VMCS 크기, 메모리 타입, 기본 기능 |
IA32_VMX_PINBASED_CTLS | 0x481 | Pin-Based VM-Execution Controls 허용 비트 |
IA32_VMX_PROCBASED_CTLS | 0x482 | Primary Processor-Based Controls 허용 비트 |
IA32_VMX_PROCBASED_CTLS2 | 0x48B | Secondary Processor-Based Controls |
IA32_VMX_EXIT_CTLS | 0x483 | VM-Exit Controls 허용 비트 |
IA32_VMX_ENTRY_CTLS | 0x484 | VM-Entry Controls 허용 비트 |
IA32_VMX_EPT_VPID_CAP | 0x48C | EPT/VPID 기능 보고 |
IA32_VMX_CR0_FIXED0/1 | 0x486/487 | VMX 모드에서 CR0 필수/허용 비트 |
IA32_VMX_CR4_FIXED0/1 | 0x488/489 | VMX 모드에서 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 ~ 0x7FF | 0x00000000 ~ 0x00001FFF | bit 0 = read intercept, bit 1 = write intercept |
| 0x800 ~ 0xFFF | 0xC0000000 ~ 0xC0001FFF | 위와 동일 |
| 0x1000 ~ 0x17FF | 0xC0010000 ~ 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;
}
MTRR / PAT MSR
MTRR (Memory Type Range Registers)
MTRR은 물리 메모리 영역의 캐시 정책(Uncacheable, Write-Combining, Write-Through, Write-Back 등)을 설정합니다. 주로 BIOS가 초기화하며, 그래픽 프레임버퍼 등 MMIO 영역에 Write-Combining을 설정합니다.
| MSR | 주소 | 설명 |
|---|---|---|
IA32_MTRRCAP | 0xFE | MTRR 기능: 가변 범위 수, FIX 지원, WC 지원 |
IA32_MTRR_DEF_TYPE | 0x2FF | 기본 메모리 타입 및 MTRR 활성화 |
IA32_MTRR_FIX64K_00000 | 0x250 | 고정 범위: 0x00000~0x7FFFF (64KB 단위) |
IA32_MTRR_FIX16K_80000 | 0x258 | 고정 범위: 0x80000~0xBFFFF (16KB 단위) |
IA32_MTRR_FIX4K_C0000 | 0x268~26F | 고정 범위: 0xC0000~0xFFFFF (4KB 단위) |
IA32_MTRR_PHYSBASEn | 0x200+2n | 가변 범위 n의 base 주소 및 타입 |
IA32_MTRR_PHYSMASKn | 0x201+2n | 가변 범위 n의 마스크 및 유효 비트 |
메모리 타입 인코딩
| 값 | 약칭 | 설명 |
|---|---|---|
| 0 | UC | Uncacheable — 캐시 사용 안 함 |
| 1 | WC | Write-Combining — 쓰기 결합 (프레임버퍼용) |
| 4 | WT | Write-Through — 쓰기 즉시 반영 |
| 5 | WP | Write-Protect — 읽기는 캐시, 쓰기는 UC처럼 |
| 6 | WB | Write-Back — 가장 빠름 (일반 RAM) |
PAT (Page Attribute Table)
PAT는 MTRR의 페이지 단위 확장으로, 페이지 테이블 엔트리의 PAT/PCD/PWT 비트 조합으로 8개 메모리 타입 중 하나를 선택합니다.
| MSR | 주소 | 설명 |
|---|---|---|
IA32_PAT | 0x277 | 8개 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);
}
Linux 커널 MSR API
Linux 커널은 다양한 MSR 접근 래퍼 함수를 제공합니다. 모두 arch/x86/include/asm/msr.h에 정의되어 있습니다.
기본 접근 함수
| 함수 | 서명 | 설명 |
|---|---|---|
rdmsr | rdmsr(msr, low, high) | 32비트 쌍으로 읽기 |
wrmsr | wrmsr(msr, low, high) | 32비트 쌍으로 쓰기 |
rdmsrl | rdmsrl(msr, val) | 64비트 값으로 읽기 |
wrmsrl | wrmsrl(msr, val) | 64비트 값으로 쓰기 |
안전한(safe) 변형
존재하지 않는 MSR에 접근하면 #GP 예외가 발생합니다. safe 변형은 예외를 잡아 에러 코드를 반환합니다.
| 함수 | 반환값 | 설명 |
|---|---|---|
rdmsrl_safe | int (0=성공, -EIO=실패) | 안전한 64비트 읽기 |
wrmsrl_safe | int (0=성공, -EIO=실패) | 안전한 64비트 쓰기 |
rdmsr_safe | int | 안전한 32비트 쌍 읽기 |
wrmsr_safe | int | 안전한 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");
}
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 읽기 |
/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);
AMD 전용 MSR
AMD 프로세서는 0xC0010000~0xC001FFFF 범위에 고유 MSR을 가지고 있습니다.
| MSR | 주소 | 설명 |
|---|---|---|
MSR_AMD64_HWCR | 0xC0010015 | 하드웨어 구성: SMM Lock, TSC Freq Sel, TLB Flush Filter 등 |
MSR_AMD64_SYSCFG | 0xC0010010 | 시스템 구성: MTRR 확장, Tom2 활성화, IORRs 등 |
MSR_AMD64_NB_CFG | 0xC001001F | Northbridge 구성: 초기 APIC ID 크기 등 |
MSR_AMD64_PATCH_LEVEL | 0x0000008B | 마이크로코드 패치 수준 |
MSR_AMD64_PATCH_LOADER | 0xC0010020 | 마이크로코드 로더 트리거 |
MSR_AMD64_OSVW_ID_LENGTH | 0xC0010140 | OS Visible Workarounds 길이 |
MSR_AMD64_OSVW_STATUS | 0xC0010141 | OS Visible Workarounds 상태 |
MSR_AMD64_DE_CFG | 0xC0011029 | Decode Config (LFENCE 직렬화 등) |
MSR_AMD64_LS_CFG | 0xC0011020 | Load/Store Config (SSB 비활성화 등) |
MSR_AMD_PPIN | 0xC00102F1 | Protected 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_CR | 0xC0010114 | SVM 잠금 및 활성화 제어 |
MSR_VM_HSAVE_PA | 0xC0010117 | 호스트 상태 저장 영역 물리 주소 |
MSR_AMD64_SEV | 0xC0010131 | SEV(Secure Encrypted Virtualization) 상태 |
MSR_AMD64_SEV_ES | 0xC0010131 | SEV-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
일반적 함정과 주의사항
- #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에서 전체 매크로를 확인할 수 있습니다.
| 주소 | 커널 매크로 | 이름 | R/W | 카테고리 |
|---|---|---|---|---|
0x10 | MSR_IA32_TSC | Time Stamp Counter | R/W | TSC |
0x1B | MSR_IA32_APICBASE | APIC Base Address | R/W | APIC |
0x3A | MSR_IA32_FEAT_CTL | Feature Control | R/W* | 제어 |
0x3B | MSR_IA32_TSC_ADJUST | TSC Adjust | R/W | TSC |
0x48 | MSR_IA32_SPEC_CTRL | Speculation Control | R/W | 보안 |
0x49 | MSR_IA32_PRED_CMD | Prediction Command | W | 보안 |
0x8B | MSR_IA32_UCODE_REV | Microcode Revision | R | 시스템 |
0xCE | MSR_PLATFORM_INFO | Platform Info | R | 주파수 |
0xE7 | MSR_IA32_MPERF | Max Performance Counter | R/W | 주파수 |
0xE8 | MSR_IA32_APERF | Actual Performance Counter | R/W | 주파수 |
0xFE | MSR_IA32_MTRRCAP | MTRR Capability | R | MTRR |
0x10A | MSR_IA32_ARCH_CAPABILITIES | Arch Capabilities | R | 보안 |
0x10B | MSR_IA32_FLUSH_CMD | L1D Flush Command | W | 보안 |
0x174 | MSR_IA32_SYSENTER_CS | SYSENTER CS | R/W | 시스콜 |
0x175 | MSR_IA32_SYSENTER_ESP | SYSENTER ESP | R/W | 시스콜 |
0x176 | MSR_IA32_SYSENTER_EIP | SYSENTER EIP | R/W | 시스콜 |
0x186 | MSR_P6_EVNTSEL0 | Perf Event Select 0 | R/W | PMU |
0x198 | MSR_IA32_PERF_STATUS | Perf Status | R | 주파수 |
0x199 | MSR_IA32_PERF_CTL | Perf Control | R/W | 주파수 |
0x19A | MSR_IA32_CLOCK_MODULATION | Clock Modulation | R/W | 열 |
0x19B | MSR_IA32_THERM_INTERRUPT | Thermal Interrupt | R/W | 열 |
0x19C | MSR_IA32_THERM_STATUS | Thermal Status | R/W | 열 |
0x1A0 | MSR_IA32_MISC_ENABLE | Misc Enable | R/W | 제어 |
0x1A2 | MSR_IA32_TEMPERATURE_TARGET | Temperature Target (TjMax) | R | 열 |
0x1AD | MSR_TURBO_RATIO_LIMIT | Turbo Ratio Limit | R | 주파수 |
0x1B1 | MSR_IA32_PACKAGE_THERM_STATUS | Package Thermal Status | R/W | 열 |
0x200 | MSR_MTRR_PHYS_BASE(0) | MTRR Phys Base 0 | R/W | MTRR |
0x250 | MSR_MTRR_FIX64K_00000 | MTRR Fixed 64K | R/W | MTRR |
0x277 | MSR_IA32_CR_PAT | Page Attribute Table | R/W | PAT |
0x2FF | MSR_MTRR_DEF_TYPE | MTRR Default Type | R/W | MTRR |
0x309 | MSR_CORE_PERF_FIXED_CTR0 | Fixed CTR: Inst Retired | R/W | PMU |
0x30A | MSR_CORE_PERF_FIXED_CTR1 | Fixed CTR: Unhalted Cycles | R/W | PMU |
0x30B | MSR_CORE_PERF_FIXED_CTR2 | Fixed CTR: Ref Cycles | R/W | PMU |
0x38D | MSR_CORE_PERF_FIXED_CTR_CTRL | Fixed CTR Control | R/W | PMU |
0x38E | MSR_CORE_PERF_GLOBAL_STATUS | Global PMU Status | R | PMU |
0x38F | MSR_CORE_PERF_GLOBAL_CTRL | Global PMU Control | R/W | PMU |
0x480 | MSR_IA32_VMX_BASIC | VMX Basic | R | VMX |
0x48B | MSR_IA32_VMX_PROCBASED_CTLS2 | VMX Secondary Controls | R | VMX |
0x48C | MSR_IA32_VMX_EPT_VPID_CAP | EPT/VPID Capability | R | VMX |
0x6E0 | MSR_IA32_TSC_DEADLINE | TSC Deadline | R/W | TSC |
0x770 | MSR_IA32_PM_ENABLE | HWP Enable | R/W | HWP |
0x771 | MSR_IA32_HWP_CAPABILITIES | HWP Capabilities | R | HWP |
0x774 | MSR_IA32_HWP_REQUEST | HWP Request | R/W | HWP |
0xC0000080 | MSR_EFER | Extended Feature Enable | R/W | 제어 |
0xC0000081 | MSR_STAR | SYSCALL CS/SS | R/W | 시스콜 |
0xC0000082 | MSR_LSTAR | SYSCALL RIP (64-bit) | R/W | 시스콜 |
0xC0000084 | MSR_SYSCALL_MASK | SYSCALL RFLAGS Mask | R/W | 시스콜 |
0xC0000100 | MSR_FS_BASE | FS Base (64-bit) | R/W | 세그먼트 |
0xC0000101 | MSR_GS_BASE | GS Base (64-bit) | R/W | 세그먼트 |
0xC0000102 | MSR_KERNEL_GS_BASE | Kernel GS Base (SWAPGS) | R/W | 세그먼트 |
0xC0000103 | MSR_TSC_AUX | TSC Aux (RDTSCP) | R/W | TSC |
0x1B0 | MSR_IA32_ENERGY_PERF_BIAS | Energy Perf Bias (EPB) | R/W | 전력 |
0x3F8 | MSR_PKG_C3_RESIDENCY | Package C3 Residency | R | C-State |
0x3F9 | MSR_PKG_C6_RESIDENCY | Package C6 Residency | R | C-State |
0x3FA | MSR_PKG_C7_RESIDENCY | Package C7 Residency | R | C-State |
0x3FC | MSR_CORE_C3_RESIDENCY | Core C3 Residency | R | C-State |
0x3FD | MSR_CORE_C6_RESIDENCY | Core C6 Residency | R | C-State |
0x3FE | MSR_CORE_C7_RESIDENCY | Core C7 Residency | R | C-State |
0x606 | MSR_RAPL_POWER_UNIT | RAPL Power Unit | R | RAPL |
0x610 | MSR_PKG_POWER_LIMIT | Package Power Limit (PL1/PL2) | R/W | RAPL |
0x611 | MSR_PKG_ENERGY_STATUS | Package Energy Status | R | RAPL |
0x614 | MSR_PKG_POWER_INFO | Package Power Info (TDP) | R | RAPL |
0x618 | MSR_DRAM_POWER_LIMIT | DRAM Power Limit | R/W | RAPL |
0x619 | MSR_DRAM_ENERGY_STATUS | DRAM Energy Status | R | RAPL |
0x638 | MSR_PP0_POWER_LIMIT | Core Power Limit (PP0) | R/W | RAPL |
0x639 | MSR_PP0_ENERGY_STATUS | Core Energy Status (PP0) | R | RAPL |
0x640 | MSR_PP1_POWER_LIMIT | GPU Power Limit (PP1) | R/W | RAPL |
0x660 | MSR_CORE_C1_RES | Core C1 Residency | R | C-State |
0x772 | MSR_IA32_HWP_REQUEST_PKG | HWP Package Request | R/W | HWP |
0x773 | MSR_IA32_HWP_INTERRUPT | HWP Interrupt Enable | R/W | HWP |
0x777 | MSR_IA32_HWP_STATUS | HWP Status | R/W | HWP |
0xC0010015 | MSR_AMD64_HWCR | AMD HW Config | R/W | AMD |
0xC0010064 | MSR_AMD_PSTATE_DEF_BASE | AMD P-State Def 0 (P0) | R | AMD 주파수 |
0xC0011029 | MSR_AMD64_DE_CFG | AMD Decode Config | R/W | AMD |
0xC00102B0 | MSR_AMD_CPPC_CAP1 | AMD CPPC Capabilities | R | AMD 주파수 |
0xC00102B1 | MSR_AMD_CPPC_ENABLE | AMD CPPC Enable | R/W | AMD 주파수 |
0xC00102B3 | MSR_AMD_CPPC_REQ | AMD CPPC Request | R/W | AMD 주파수 |