1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
|
;------------------------------------------------------------------------------
; @file
; Sets the CR3 register for 64-bit paging
;
; Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR>
; Copyright (c) 2017 - 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
; SPDX-License-Identifier: BSD-2-Clause-Patent
;
;------------------------------------------------------------------------------
BITS 32
; common for all levels
%define PAGE_PRESENT 0x01
%define PAGE_READ_WRITE 0x02
%define PAGE_USER_SUPERVISOR 0x04
%define PAGE_WRITE_THROUGH 0x08
%define PAGE_CACHE_DISABLE 0x010
%define PAGE_ACCESSED 0x020
%define PAGE_DIRTY 0x040
%define PAGE_GLOBAL 0x0100
; page table entries (level 1)
%define PAGE_PTE_PAT 0x080
; page directory entries (level 2+)
%define PAGE_PDE_LARGEPAGE 0x080
%define PAGE_PDE_PAT 0x01000
%define PAGE_4K_PDE_ATTR (PAGE_ACCESSED + \
PAGE_DIRTY + \
PAGE_READ_WRITE + \
PAGE_PRESENT)
%define PAGE_PDE_LARGEPAGE_ATTR (PAGE_PDE_LARGEPAGE + \
PAGE_ACCESSED + \
PAGE_DIRTY + \
PAGE_READ_WRITE + \
PAGE_PRESENT)
%define PAGE_PDE_DIRECTORY_ATTR (PAGE_ACCESSED + \
PAGE_READ_WRITE + \
PAGE_PRESENT)
%define TDX_BSP 1
%define TDX_AP 2
%define TDX_AP_5_LEVEL 3
;
; For OVMF, build some initial page tables at
; PcdOvmfSecPageTablesBase - (PcdOvmfSecPageTablesBase + 0x6000).
;
; This range should match with PcdOvmfSecPageTablesSize which is
; declared in the FDF files.
;
; At the end of PEI, the pages tables will be rebuilt into a
; more permanent location by DxeIpl.
;
%macro ClearOvmfPageTables 0
mov ecx, 6 * 0x1000 / 4
xor eax, eax
.clearPageTablesMemoryLoop:
mov dword[ecx * 4 + PT_ADDR (0) - 4], eax
loop .clearPageTablesMemoryLoop
%endmacro
;
; Create page tables for 4-level paging
;
; Argument: upper 32 bits of the page table entries
;
%macro CreatePageTables4Level 1
; indicate 4-level paging
debugShowPostCode 0x41
;
; Top level Page Directory Pointers (1 * 512GB entry)
;
mov dword[PT_ADDR (0)], PT_ADDR (0x1000) + PAGE_PDE_DIRECTORY_ATTR
mov dword[PT_ADDR (4)], %1
;
; Next level Page Directory Pointers (4 * 1GB entries => 4GB)
;
mov dword[PT_ADDR (0x1000)], PT_ADDR (0x2000) + PAGE_PDE_DIRECTORY_ATTR
mov dword[PT_ADDR (0x1004)], %1
mov dword[PT_ADDR (0x1008)], PT_ADDR (0x3000) + PAGE_PDE_DIRECTORY_ATTR
mov dword[PT_ADDR (0x100C)], %1
mov dword[PT_ADDR (0x1010)], PT_ADDR (0x4000) + PAGE_PDE_DIRECTORY_ATTR
mov dword[PT_ADDR (0x1014)], %1
mov dword[PT_ADDR (0x1018)], PT_ADDR (0x5000) + PAGE_PDE_DIRECTORY_ATTR
mov dword[PT_ADDR (0x101C)], %1
;
; Page Table Entries (2048 * 2MB entries => 4GB)
;
mov ecx, 0x800
.pageTableEntriesLoop4Level:
mov eax, ecx
dec eax
shl eax, 21
add eax, PAGE_PDE_LARGEPAGE_ATTR
mov dword[ecx * 8 + PT_ADDR (0x2000 - 8)], eax
mov dword[(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], %1
loop .pageTableEntriesLoop4Level
%endmacro
;
; Check whenever 5-level paging can be used
;
; Argument: jump label for 4-level paging
;
%macro Check5LevelPaging 1
; check for cpuid leaf 0x07
mov eax, 0x00
cpuid
cmp eax, 0x07
jb %1
; check for la57 (aka 5-level paging)
mov eax, 0x07
mov ecx, 0x00
cpuid
bt ecx, 16
jnc %1
; check for cpuid leaf 0x80000001
mov eax, 0x80000000
cpuid
cmp eax, 0x80000001
jb %1
; check for 1g pages
mov eax, 0x80000001
cpuid
bt edx, 26
jnc %1
%endmacro
;
; Create page tables for 5-level paging with gigabyte pages
;
; Argument: upper 32 bits of the page table entries
;
; We have 6 pages available for the early page tables,
; we use four of them:
; PT_ADDR(0) - level 5 directory
; PT_ADDR(0x1000) - level 4 directory
; PT_ADDR(0x2000) - level 2 directory (0 -> 1GB)
; PT_ADDR(0x3000) - level 3 directory
;
; The level 2 directory for the first gigabyte has the same
; physical address in both 4-level and 5-level paging mode,
; SevClearPageEncMaskForGhcbPage depends on this.
;
; The 1 GB -> 4 GB range is mapped using 1G pages in the
; level 3 directory.
;
%macro CreatePageTables5Level 1
; indicate 5-level paging
debugShowPostCode 0x51
; level 5
mov dword[PT_ADDR (0)], PT_ADDR (0x1000) + PAGE_PDE_DIRECTORY_ATTR
mov dword[PT_ADDR (4)], %1
; level 4
mov dword[PT_ADDR (0x1000)], PT_ADDR (0x3000) + PAGE_PDE_DIRECTORY_ATTR
mov dword[PT_ADDR (0x1004)], %1
; level 3 (1x -> level 2, 3x 1GB)
mov dword[PT_ADDR (0x3000)], PT_ADDR (0x2000) + PAGE_PDE_DIRECTORY_ATTR
mov dword[PT_ADDR (0x3004)], %1
mov dword[PT_ADDR (0x3008)], (1 << 30) + PAGE_PDE_LARGEPAGE_ATTR
mov dword[PT_ADDR (0x300c)], %1
mov dword[PT_ADDR (0x3010)], (2 << 30) + PAGE_PDE_LARGEPAGE_ATTR
mov dword[PT_ADDR (0x3014)], %1
mov dword[PT_ADDR (0x3018)], (3 << 30) + PAGE_PDE_LARGEPAGE_ATTR
mov dword[PT_ADDR (0x301c)], %1
;
; level 2 (512 * 2MB entries => 1GB)
;
mov ecx, 0x200
.pageTableEntriesLoop5Level:
mov eax, ecx
dec eax
shl eax, 21
add eax, PAGE_PDE_LARGEPAGE_ATTR
mov dword[ecx * 8 + PT_ADDR (0x2000 - 8)], eax
mov dword[(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], %1
loop .pageTableEntriesLoop5Level
%endmacro
%macro Enable5LevelPaging 0
; set la57 bit in cr4
mov eax, cr4
bts eax, 12
mov cr4, eax
%endmacro
;
; Modified: EAX, EBX, ECX, EDX
;
SetCr3ForPageTables64:
; Check the TDX features.
; If it is TDX APs, then jump to SetCr3 directly.
; In TD guest the initialization is done by BSP, including building
; the page tables. APs will spin on until byte[TDX_WORK_AREA_PGTBL_READY]
; is set.
OneTimeCall CheckTdxFeaturesBeforeBuildPagetables
cmp eax, TDX_BSP
je TdxBspInit
cmp eax, TDX_AP
je SetCr3
%if PG_5_LEVEL
cmp eax, TDX_AP_5_LEVEL
jne CheckForSev
Enable5LevelPaging
jmp SetCr3
CheckForSev:
%endif
; Check whether the SEV is active and populate the SevEsWorkArea
OneTimeCall CheckSevFeatures
cmp byte[WORK_AREA_GUEST_TYPE], 1
jz SevInit
;
; normal (non-CoCo) workflow
;
ClearOvmfPageTables
%if PG_5_LEVEL
Check5LevelPaging Paging4Level
CreatePageTables5Level 0
Enable5LevelPaging
jmp SetCr3
Paging4Level:
%endif
CreatePageTables4Level 0
jmp SetCr3
SevInit:
;
; SEV workflow
;
ClearOvmfPageTables
; If SEV is enabled, the C-bit position is always above 31.
; The mask will be saved in the EDX and applied during the
; the page table build below.
OneTimeCall GetSevCBitMaskAbove31
CreatePageTables4Level edx
; Clear the C-bit from the GHCB page if the SEV-ES is enabled.
OneTimeCall SevClearPageEncMaskForGhcbPage
jmp SetCr3
TdxBspInit:
;
; TDX BSP workflow
;
ClearOvmfPageTables
%if PG_5_LEVEL
Check5LevelPaging Tdx4Level
CreatePageTables5Level 0
OneTimeCall TdxPostBuildPageTables5Level
Enable5LevelPaging
jmp SetCr3
Tdx4Level:
%endif
CreatePageTables4Level 0
OneTimeCall TdxPostBuildPageTables
jmp SetCr3
SetCr3:
;
; common workflow
;
; Set CR3 now that the paging structures are available
;
mov eax, PT_ADDR (0)
mov cr3, eax
OneTimeCallRet SetCr3ForPageTables64
|