1 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include <cpu/x86/mtrr.h>
5 #include <cpu/x86/msr.h>
6 #include <console/console.h>
7 #include <commonlib/bsd/helpers.h>
10 /* Get first available variable MTRR.
11 * Returns var# if available, else returns -1.
13 int get_free_var_mtrr(void)
19 vcnt
= get_var_mtrr_count();
21 /* Identify the first var mtrr which is not valid. */
22 for (i
= 0; i
< vcnt
; i
++) {
23 maskm
= rdmsr(MTRR_PHYS_MASK(i
));
24 if ((maskm
.lo
& MTRR_PHYS_MASK_VALID
) == 0)
28 /* No free var mtrr. */
33 unsigned int reg
, unsigned int base
, unsigned int size
,
36 /* Bit 32-35 of MTRRphysMask should be set to 1 */
37 /* FIXME: It only support 4G less range */
40 if (type
== MTRR_TYPE_WRBACK
&& !is_cache_sets_power_of_two() && ENV_CACHE_AS_RAM
)
41 printk(BIOS_ERR
, "MTRR Error: Type %x may not be supported due to NEM limitation\n",
43 if (!IS_POWER_OF_2(size
))
44 printk(BIOS_ERR
, "MTRR Error: size %#x is not a power of two\n", size
);
46 printk(BIOS_ERR
, "MTRR Error: size %#x smaller than 4KiB\n", size
);
48 printk(BIOS_ERR
, "MTRR Error: base %#x must be aligned to size %#x\n", base
,
51 basem
.lo
= base
| type
;
53 wrmsr(MTRR_PHYS_BASE(reg
), basem
);
54 maskm
.lo
= ~(size
- 1) | MTRR_PHYS_MASK_VALID
;
55 maskm
.hi
= (1 << (cpu_phys_address_size() - 32)) - 1;
56 wrmsr(MTRR_PHYS_MASK(reg
), maskm
);
59 void clear_all_var_mtrr(void)
61 msr_t mtrr
= { .raw
= 0 };
65 vcnt
= get_var_mtrr_count();
67 for (i
= 0; i
< vcnt
; i
++) {
68 wrmsr(MTRR_PHYS_MASK(i
), mtrr
);
69 wrmsr(MTRR_PHYS_BASE(i
), mtrr
);
73 void var_mtrr_context_init(struct var_mtrr_context
*ctx
)
75 ctx
->max_var_mtrrs
= get_var_mtrr_count();
76 ctx
->used_var_mtrrs
= 0;
79 int var_mtrr_set(struct var_mtrr_context
*ctx
, uintptr_t addr
, size_t size
, int type
)
81 const uint32_t upper_mask
= (1U << (cpu_phys_address_size() - 32)) - 1;
82 /* Utilize additional MTRRs if the specified size is greater than the
83 base address alignment. */
91 if (ctx
->used_var_mtrrs
>= ctx
->max_var_mtrrs
) {
92 printk(BIOS_ERR
, "No more variable MTRRs: %d\n",
97 addr_lsb
= __ffs(addr
);
98 size_msb
= __fls(size
);
100 /* All MTRR entries need to have their base aligned to the mask
101 size. The maximum size is calculated by a function of the
102 min base bit set and maximum size bit set. */
103 if (addr_lsb
> size_msb
)
104 mtrr_size
= 1 << size_msb
;
106 mtrr_size
= 1 << addr_lsb
;
108 base
.hi
= (uint64_t)addr
>> 32;
109 base
.lo
= addr
| type
;
110 mask
.hi
= upper_mask
;
111 mask
.lo
= ~(mtrr_size
- 1) | MTRR_PHYS_MASK_VALID
;
112 ctx
->mtrr
[ctx
->used_var_mtrrs
].base
= base
;
113 ctx
->mtrr
[ctx
->used_var_mtrrs
].mask
= mask
;
114 ctx
->used_var_mtrrs
++;
123 /* Romstage sets up a MTRR context in cbmem and sets up this pointer in postcar stage. */
124 __attribute__((used
, __section__(".module_parameters"))) const volatile uintptr_t post_car_mtrrs
;
126 void commit_mtrr_setup(const struct var_mtrr_context
*ctx
)
128 clear_all_var_mtrr();
130 for (int i
= 0; i
< ctx
->used_var_mtrrs
; i
++) {
131 wrmsr(MTRR_PHYS_BASE(i
), ctx
->mtrr
[i
].base
);
132 wrmsr(MTRR_PHYS_MASK(i
), ctx
->mtrr
[i
].mask
);
135 msr_t mtrr_def_type
= rdmsr(MTRR_DEF_TYPE_MSR
);
136 mtrr_def_type
.lo
&= MTRR_DEF_TYPE_MASK
;
137 mtrr_def_type
.lo
|= MTRR_DEF_TYPE_EN
;
138 wrmsr(MTRR_DEF_TYPE_MSR
, mtrr_def_type
);
141 void postcar_mtrr_setup(void)
143 commit_mtrr_setup((const struct var_mtrr_context
*)post_car_mtrrs
);