Skip to content

Commit bad34db

Browse files
committed
mm/efi_pt: Retag firmware FB PTEs to UC- so WC MTRRs take effect
1 parent 6e110b5 commit bad34db

4 files changed

Lines changed: 127 additions & 0 deletions

File tree

common/lib/gterm.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <lib/rand.h>
1212
#include <mm/pmm.h>
1313
#include <mm/mtrr.h>
14+
#include <mm/efi_pt.h>
1415
#include <flanterm.h>
1516
#include <flanterm_backends/fb.h>
1617
#include <lib/term.h>
@@ -820,6 +821,9 @@ bool gterm_init(struct fb_info **_fbs, size_t *_fbs_count,
820821
continue;
821822
}
822823
mtrr_wc_add_fb_range(fbs[i].framebuffer_addr, fb_size);
824+
#if defined (__x86_64__) && defined (UEFI)
825+
efi_pt_set_fb_uc_minus(fbs[i].framebuffer_addr, fb_size);
826+
#endif
823827
}
824828
#endif
825829

common/mm/efi_pt.c

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#if defined (__x86_64__) && defined (UEFI)
2+
3+
#include <stdint.h>
4+
#include <stddef.h>
5+
#include <stdbool.h>
6+
#include <mm/efi_pt.h>
7+
#include <sys/cpu.h>
8+
9+
#define PTE_P ((uint64_t)1 << 0)
10+
#define PTE_PWT ((uint64_t)1 << 3)
11+
#define PTE_PCD ((uint64_t)1 << 4)
12+
#define PTE_PS ((uint64_t)1 << 7)
13+
#define PTE_PAT_4K ((uint64_t)1 << 7)
14+
#define PTE_PAT_BIG ((uint64_t)1 << 12)
15+
#define PT_ADDR_MASK ((uint64_t)0x000FFFFFFFFFF000)
16+
17+
#define UCM_MASK_4K (PTE_PWT | PTE_PCD | PTE_PAT_4K)
18+
#define UCM_MASK_BIG (PTE_PWT | PTE_PCD | PTE_PAT_BIG)
19+
#define UCM_VALUE PTE_PCD
20+
21+
#define IA32_PAT_MSR 0x277
22+
#define PAT_TYPE_UCM 0x07
23+
24+
static bool la57_enabled(void) {
25+
uint64_t cr4;
26+
asm volatile ("mov %%cr4, %0" : "=r"(cr4));
27+
return !!(cr4 & ((uint64_t)1 << 12));
28+
}
29+
30+
static bool pat_slot2_is_ucm(void) {
31+
static bool checked = false, ok = false;
32+
if (checked) {
33+
return ok;
34+
}
35+
checked = true;
36+
37+
uint32_t eax, ebx, ecx, edx;
38+
if (!cpuid(1, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 16))) {
39+
return false;
40+
}
41+
42+
ok = ((rdmsr(IA32_PAT_MSR) >> 16) & 0xff) == PAT_TYPE_UCM;
43+
return ok;
44+
}
45+
46+
static uint64_t *walk_to_leaf(uint64_t addr, uint64_t *pg_size, bool *large) {
47+
uint64_t cr3;
48+
asm volatile ("mov %%cr3, %0" : "=r"(cr3));
49+
50+
uint64_t *table = (uint64_t *)(cr3 & PT_ADDR_MASK);
51+
52+
int lvl = la57_enabled() ? 5 : 4;
53+
54+
for (; lvl >= 1; lvl--) {
55+
int shift = (lvl - 1) * 9 + 12;
56+
size_t idx = (addr >> shift) & 0x1ff;
57+
uint64_t e = table[idx];
58+
59+
if (!(e & PTE_P)) {
60+
return NULL;
61+
}
62+
63+
bool is_leaf = (lvl == 1) || ((lvl == 2 || lvl == 3) && (e & PTE_PS));
64+
if (is_leaf) {
65+
*pg_size = (uint64_t)1 << shift;
66+
*large = (lvl != 1);
67+
return &table[idx];
68+
}
69+
70+
table = (uint64_t *)(e & PT_ADDR_MASK);
71+
}
72+
return NULL;
73+
}
74+
75+
void efi_pt_set_fb_uc_minus(uint64_t base, uint64_t size) {
76+
if (size == 0 || !pat_slot2_is_ucm()) {
77+
return;
78+
}
79+
80+
// Clear CR0.WP so supervisor writes hit any firmware PTEs mapped R/O.
81+
uint64_t old_cr0;
82+
asm volatile ("mov %%cr0, %0" : "=r"(old_cr0));
83+
asm volatile ("mov %0, %%cr0" :: "r"(old_cr0 & ~((uint64_t)1 << 16)) : "memory");
84+
85+
uint64_t end = (base + size + 0xfff) & ~(uint64_t)0xfff;
86+
base &= ~(uint64_t)0xfff;
87+
88+
while (base < end) {
89+
uint64_t pg;
90+
bool large;
91+
uint64_t *entry = walk_to_leaf(base, &pg, &large);
92+
if (entry == NULL) {
93+
base += 0x1000;
94+
continue;
95+
}
96+
97+
uint64_t mask = large ? UCM_MASK_BIG : UCM_MASK_4K;
98+
*entry = (*entry & ~mask) | UCM_VALUE;
99+
100+
invlpg(base);
101+
base = (base & ~(pg - 1)) + pg;
102+
}
103+
104+
asm volatile ("mov %0, %%cr0" :: "r"(old_cr0) : "memory");
105+
}
106+
107+
#endif

common/mm/efi_pt.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef MM__EFI_PT_H__
2+
#define MM__EFI_PT_H__
3+
4+
#include <stdint.h>
5+
6+
#if defined (__x86_64__) && defined (UEFI)
7+
8+
void efi_pt_set_fb_uc_minus(uint64_t base, uint64_t size);
9+
10+
#endif
11+
12+
#endif

common/sys/cpu.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ static inline uint64_t rdtsc(void) {
160160
return ((uint64_t)edx << 32) | eax;
161161
}
162162

163+
static inline void invlpg(uintptr_t va) {
164+
asm volatile ("invlpg (%0)" :: "r"(va) : "memory");
165+
}
166+
163167
static inline uint64_t tsc_freq_arch(void) {
164168
uint32_t eax, ebx, ecx, edx;
165169
if (!cpuid(0x15, 0, &eax, &ebx, &ecx, &edx))

0 commit comments

Comments
 (0)