Model-Specific Registers
A model-specific register is a control register that, unlike EAX or CR0, has no dedicated instruction or fixed encoding of its own; instead, the entire space of MSRs is reached through exactly two general-purpose instructions, RDMSR and WRMSR, which select which register to touch by number rather than by name. Long Mode’s EFER and the STAR/LSTAR/FMASK registers System Calls configures for the fast syscall path are both MSRs, and both articles use them without covering the mechanism itself, which this article fills in.
Why a separate access mechanism
Section titled “Why a separate access mechanism”Ordinary registers are addressed directly in an instruction’s encoding, which only scales to a small, fixed set: x86-64 has sixteen general-purpose registers, and adding a seventeenth would mean widening every instruction that could reference one. MSRs sidestep this entirely by moving the selector into data rather than encoding: ECX holds a 32-bit index chosen at the moment of the call, so the same two-instruction encoding reaches thousands of possible registers without any of them needing dedicated opcode space, at the cost of an extra register load before every access that a directly-encoded register wouldn’t need.
mov ecx, 0xC0000080 ; MSR index: IA32_EFERrdmsr ; result: EDX:EAX = MSR value (64-bit, split across two 32-bit registers)
or eax, 1 << 8 ; set LMEwrmsr ; writes EDX:EAX back to the same MSR selected by ECXRDMSR and WRMSR both operate on a 64-bit value despite EAX and EDX each only being 32 bits wide: the low half of the value goes in EAX, the high half in EDX, a split that predates 64-bit general-purpose registers existing at all and has stayed fixed for compatibility even now that a single 64-bit register could hold the whole value directly.
Per-core, not shared
Section titled “Per-core, not shared”Each CPU core has its own independent copy of the entire MSR space; writing an MSR on one core has no effect on any other core’s copy of that same MSR index. This is a real, easy-to-miss consequence for anything set up once during boot on the bootstrap processor: EFER, the syscall configuration registers, and FS_BASE/GS_BASE all need to be written again on every additional core as it’s brought up under SMP, not copied from the boot core, since there is no shared state between cores here to copy from in the first place. FS_BASE and GS_BASE in particular are commonly used specifically because they’re per-core: a kernel points GS_BASE at a distinct per-core data structure on each core, then reaches it from any code path through a fixed offset from GS, without needing a separate mechanism to determine which core is currently executing.
Faulting on an unsupported MSR
Section titled “Faulting on an unsupported MSR”Unlike CPUID, which silently returns whatever the highest supported leaf provides when queried out of range, RDMSR or WRMSR against an index the current CPU doesn’t implement raises #GP (general protection fault), an ordinary exception a kernel’s IDT already handles, rather than any special-cased behavior. This is a second, independent reason (beyond checking CPUID feature bits directly) to verify a feature is present before touching the MSR that configures it: a wrmsr to IA32_EFER’s LME bit on a CPU that doesn’t support long mode at all faults exactly the same way as a wrmsr to a completely made-up, nonexistent index would, with no distinction in the fault itself between “wrong index” and “right index, unsupported feature.”
Commonly used MSRs
Section titled “Commonly used MSRs”| MSR | Index | Purpose |
|---|---|---|
IA32_EFER | 0xC0000080 | Long mode enable/active, NX enable |
IA32_APIC_BASE | 0x1B | Local APIC’s physical base address and enable bit |
IA32_FS_BASE | 0xC0000100 | Base address FS-relative addressing resolves against |
IA32_GS_BASE | 0xC0000101 | Base address GS-relative addressing resolves against |
IA32_STAR | 0xC0000081 | Segment selectors loaded on syscall/sysret |
IA32_LSTAR | 0xC0000082 | Entry point address syscall jumps to |
IA32_FMASK | 0xC0000084 | EFLAGS bits cleared on syscall entry |
Note the split between the two numbering ranges above: indices below roughly 0x4000 are architectural, defined identically across implementations, while the 0xC000_0000-prefixed range holds registers AMD introduced for x86-64 and Intel later adopted for compatibility, a naming convention (IA32_ prefix regardless of the numeric range) that has more to do with vendor history than with any functional difference visible from software.
Implementation notes
Section titled “Implementation notes”RDMSR and WRMSR are both ring-0-only instructions with no equivalent of the TSS I/O permission bitmap port I/O has for extending access to ring 3; there is no mechanism to let unprivileged code touch an MSR directly at all, which is part of why per-core data reached via GS_BASE above is normally exposed to userspace only indirectly, through a system call or a kernel-populated read-only page, rather than by letting ring 3 read the MSR itself. A kernel probing which MSRs a given CPU actually implements has no direct enumeration instruction to call, unlike CPUID’s leaf structure; the practical approach is checking the specific CPUID feature bit associated with each MSR’s functionality (the syscall MSRs, for instance, are only meaningful once CPUID has already confirmed the instruction itself is supported) rather than probing MSR indices directly and handling the resulting faults.
References
Section titled “References”- ^ Intel, Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 4: the complete architectural MSR reference.
- ^ AMD, AMD64 Architecture Programmer’s Manual, Volume 2, Appendix A: the AMD-side MSR listing, including the
STAR/LSTAR/FMASKsyscall registers.
See also
Section titled “See also”- Long Mode: the article whose
EFERMSR use this one explains the general mechanism behind. - System Calls: the
STAR/LSTAR/FMASKMSRs configuring the fast syscall path. - CPUID: the companion instruction for checking a feature is supported before touching the MSR that configures it.