MTRR
MTRR, or Memory-Type Range Registers are a group of x86 Model Specific Registers providing a way to control access and cacheability of physical memory regions. These were introduced in the Intel Pentium Pro (P6) processor, intended to extend and enhance the memory type information provided by page tables (i.e. the write-through and cache disable bits).
The set of registers is provided in two groups: 11 registers for 88 fixed ranges and a number of base-mask pairs for custom range configuration. The exact number of the latter can be known by reading the capabilities register.
While this method is available in most new Intel and AMD processors, preference should be given to Page Attribute Tables instead, as many of the systems usually configure these during early boot from UEFI/BIOS and changing MTRRs which were set up by the firmware may lead to unexpected problems.
Memory types
There are 5 memory types defined for use in MTRRs:
Number | Name | Description |
---|---|---|
0 | UC — Uncacheable | All accesses are uncacheable. Write combining is not allowed. Speculative accesses are not allowed. |
1 | WC — Write-Combining | All accesses are uncacheable. Write combining is allowed. Speculative reads are allowed. |
4 | WT — Writethrough | Reads allocate cache lines on a cache miss. Cache lines are not allocated on a write miss.
Write hits update the cache and main memory. |
5 | WP — Write-Protect | Reads allocate cache lines on a cache miss. All writes update main memory.
Cache lines are not allocated on a write miss. Write hits invalidate the cache line and update main memory. |
6 | WB — Writeback | Reads allocate cache lines on a cache miss, and can allocate to either the shared,
exclusive, or modified state. Writes allocate to the modified state on a cache miss. |
Accessing MTRRs
Before attempting reads/writes to MTRRs, the operating system should first check the availability of this feature by checking if CPUID.01h:EDX[bit 12] is set.
Once the feature is known to be available, the following MSRs can be used for configuration:
IA32_MTRRCAP (MSR 0xFE)
This register describes memory range types and count supported by the implementation. The register is read-only.
Bit(s) | Label | Description |
---|---|---|
63..11 | - | Reserved |
10 | WC | Write-combining type is available |
9 | - | Reserved |
8 | FIX | All fixed-size MTRRs are available |
7..0 | VCNT | Count of variable-size MTRRs supported |
IA32_MTRRdefType (MSR 0x2FF)
Describes the default type used for physical addresses which are outside of any configured memory ranges, as well as whether MTRRs and fixed ranges are enabled.
Bit(s) | Label | Description |
---|---|---|
63..12 | - | Reserved |
11 | E | MTRR enable |
10 | FE | Fixed range enable |
9..8 | - | Reserved |
7..0 | Type | Default memory type |
IA32_MTRRphysBasen and IA32_MTRRphysMaskn registers
These allow defining custom memory ranges using base-mask pairs and have the following addresses:
- IA32_MTRRphysBasen = 0x200 + (n * 2)
- IA32_MTRRphysMaskn = 0x201 + (n * 2)
Where n parameter ranges from 0 to IA32_MTRRdefType[VCNT].
Base register has the following bits:
Bit(s) | Label | Description |
---|---|---|
63..52 | - | Reserved |
51..12 | PhysBase | Range base address |
11..8 | - | Reserved |
7..0 | Type | Range memory type |
"Mask" registers use bits 51:12 to specify the region mask and bit 11 ("Valid" bit) tells if the region is enabled. If your CPU supports address widths of less than 52 bits, the remaining bits are reserved. Writing to these reserved bits will trigger a General Protection Fault.
The processor uses the following algorithm to check if an address is covered by an MTRR:
MaskBase = PhysMask AND PhysBase MaskTarget = PhysMask AND Target_Address[51:12] IF MaskBase == MaskTarget target address is in range ELSE target address is not in range
The mask is calculated like this: ~(size - 1) & ((1 << ADDRESS_WIDTH) - 1)
where ADDRESS WIDTH
is the maximum supported address width of your CPU. Both the mask and base must be
aligned to at least 4K boundary (thus must be shifted to the right by 12 to fit into the 51:12 range) and the size must be equal to
the boundary, on which the range is aligned. So, in order to have, for
example, a 6M-long range, one would need to set up two MTRRs, one
4M and the other 2M.
IA32_MTRRfixsize_base
If supported by implementation, the specification defines a set of 11 registers allowing the configuration of addresses in lowest 1M block.
Each fixed-size range register contains 8 eight-bit type fields and the address block is split in different sizes:
63-56 | 55-48 | 47-40 | 39-32 | 31-24 | 23-16 | 15-8 | 7-0 | Register |
---|---|---|---|---|---|---|---|---|
Address range | ||||||||
70000-7FFFF | 60000-6FFFF | 50000-5FFFF | 40000-4FFFF | 30000-3FFFF | 20000-2FFFF | 10000-1FFFF | 00000-0FFFF | MTRRfix64K_00000 |
9C000-9FFFF | 98000-9BFFF | 94000-97FFF | 90000-93FFF | 8C000-8FFFF | 88000-8BFFF | 84000-87FFF | 80000-83FFF | MTRRfix16K_80000 |
BC000-BFFFF | B8000-BBFFF | B4000-B7FFF | B0000-B3FFF | AC000-AFFFF | A8000-ABFFF | A4000-A7FFF | A0000-A3FFF | MTRRfix16K_A0000 |
C7000-C7FFF | C6000-C6FFF | C5000-C5FFF | C4000-C4FFF | C3000-C3FFF | C2000-C2FFF | C1000-C1FFF | C0000-C0FFF | MTRRfix4K_C0000 |
CF000-CFFFF | CE000-CEFFF | CD000-CDFFF | CC000-CCFFF | CB000-CBFFF | CA000-CAFFF | C9000-C9FFF | C8000-C8FFF | MTRRfix4K_C8000 |
D7000-D7FFF | D6000-D6FFF | D5000-D5FFF | D4000-D4FFF | D3000-D3FFF | D2000-D2FFF | D1000-D1FFF | D0000-D0FFF | MTRRfix4K_D0000 |
DF000-DFFFF | DE000-DEFFF | DD000-DDFFF | DC000-DCFFF | DB000-DBFFF | DA000-DAFFF | D9000-D9FFF | D8000-D8FFF | MTRRfix4K_D8000 |
E7000-E7FFF | E6000-E6FFF | E5000-E5FFF | E4000-E4FFF | E3000-E3FFF | E2000-E2FFF | E1000-E1FFF | E0000-E0FFF | MTRRfix4K_E0000 |
EF000-EFFFF | EE000-EEFFF | ED000-EDFFF | EC000-ECFFF | EB000-EBFFF | EA000-EAFFF | E9000-E9FFF | E8000-E8FFF | MTRRfix4K_E8000 |
F7000-F7FFF | F6000-F6FFF | F5000-F5FFF | F4000-F4FFF | F3000-F3FFF | F2000-F2FFF | F1000-F1FFF | F0000-F0FFF | MTRRfix4K_F0000 |
FF000-FFFFF | FE000-FEFFF | FD000-FDFFF | FC000-FCFFF | FB000-FBFFF | FA000-FAFFF | F9000-F9FFF | F8000-F8FFF | MTRRfix4K_F8000 |
Fixed-size range registers have are:
- MTRRfix64K_00000 — MSR 0x250
- MTRRfix16K_80000 — MSR 0x258
- MTRRfix16K_A0000 — MSR 0x259
- MTRRfix4K_C0000 — MSR 0x268
- MTRRfix4K_C8000 — MSR 0x269
- MTRRfix4K_D0000 — MSR 0x26A
- MTRRfix4K_D8000 — MSR 0x26B
- MTRRfix4K_E0000 — MSR 0x26C
- MTRRfix4K_E8000 — MSR 0x26D
- MTRRfix4K_F0000 — MSR 0x26E
- MTRRfix4K_F8000 — MSR 0x26F
Combining with page-level control
Both MTRR and Page Tables allow setting cache-disable and writethrough attributes for memory regions — there may be cases when such ranges overlap. How MTRR and page entry bits work in combination (if the PAT register wasn't modified):
Without PAT
MTRR type | Page CD bit | Page WT bit | Resulting type |
---|---|---|---|
UC | — | — | UC |
WC | 0 | — | WC |
1 | 0 | WC (impl. dependent) | |
1 | 1 | UC | |
WP | 0 | — | WP |
1 | — | UC | |
WT | 0 | — | WT |
1 | — | UC | |
WB | 0 | 0 | WB |
0 | 1 | WT | |
1 | — | UC |
With PAT
MTRR type | PAT Entry Value | Resulting type |
---|---|---|
UC | UC | UC |
UC- | UC | |
WC | WC | |
WT | UC | |
WB | UC | |
WP | UC | |
WC | UC | UC |
UC- | WC | |
WC | WC | |
WT | UC | |
WB | WC | |
WP | UC | |
WT | UC | UC |
UC- | UC | |
WC | WC | |
WT | WT | |
WB | WT | |
WP | WP | |
WB | UC | UC |
UC- | UC | |
WC | WC | |
WT | WT | |
WB | WB | |
WP | WP | |
WP | UC | UC |
UC- | WC | |
WC | WC | |
WT | WT | |
WB | WP | |
WP | WP |