1;------------------------------------------------------------------------------
2; @file
3; Sets the CR3 register for 64-bit paging
4;
5; Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.<BR>
6; Copyright (c) 2019, Citrix Systems, Inc.
7; SPDX-License-Identifier: BSD-2-Clause-Patent
8;
9;------------------------------------------------------------------------------
10
11BITS    32
12
13%define PAGE_PRESENT            0x01
14%define PAGE_READ_WRITE         0x02
15%define PAGE_USER_SUPERVISOR    0x04
16%define PAGE_WRITE_THROUGH      0x08
17%define PAGE_CACHE_DISABLE     0x010
18%define PAGE_ACCESSED          0x020
19%define PAGE_DIRTY             0x040
20%define PAGE_PAT               0x080
21%define PAGE_GLOBAL           0x0100
22%define PAGE_2M_MBO            0x080
23%define PAGE_2M_PAT          0x01000
24
25%define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \
26                          PAGE_ACCESSED + \
27                          PAGE_DIRTY + \
28                          PAGE_READ_WRITE + \
29                          PAGE_PRESENT)
30
31%define PAGE_PDP_ATTR (PAGE_ACCESSED + \
32                       PAGE_READ_WRITE + \
33                       PAGE_PRESENT)
34
35; Check if Secure Encrypted Virtualization (SEV) feature is enabled
36;
37; If SEV is enabled then EAX will be at least 32
38; If SEV is disabled then EAX will be zero.
39;
40CheckSevFeature:
41    ; Check if we have a valid (0x8000_001F) CPUID leaf
42    mov       eax, 0x80000000
43    cpuid
44
45    ; This check should fail on Intel or Non SEV AMD CPUs. In future if
46    ; Intel CPUs supports this CPUID leaf then we are guranteed to have exact
47    ; same bit definition.
48    cmp       eax, 0x8000001f
49    jl        NoSev
50
51    ; Check for memory encryption feature:
52    ;  CPUID  Fn8000_001F[EAX] - Bit 1
53    ;
54    mov       eax,  0x8000001f
55    cpuid
56    bt        eax, 1
57    jnc       NoSev
58
59    ; Check if memory encryption is enabled
60    ;  MSR_0xC0010131 - Bit 0 (SEV enabled)
61    mov       ecx, 0xc0010131
62    rdmsr
63    bt        eax, 0
64    jnc       NoSev
65
66    ; Get pte bit position to enable memory encryption
67    ; CPUID Fn8000_001F[EBX] - Bits 5:0
68    ;
69    mov       eax, ebx
70    and       eax, 0x3f
71    jmp       SevExit
72
73NoSev:
74    xor       eax, eax
75
76SevExit:
77    OneTimeCallRet CheckSevFeature
78
79;
80; Modified:  EAX, EBX, ECX, EDX
81;
82SetCr3ForPageTables64:
83
84    OneTimeCall   CheckSevFeature
85    xor     edx, edx
86    test    eax, eax
87    jz      SevNotActive
88
89    ; If SEV is enabled, C-bit is always above 31
90    sub     eax, 32
91    bts     edx, eax
92
93SevNotActive:
94
95    ;
96    ; For OVMF, build some initial page tables at
97    ; PcdOvmfSecPageTablesBase - (PcdOvmfSecPageTablesBase + 0x6000).
98    ;
99    ; This range should match with PcdOvmfSecPageTablesSize which is
100    ; declared in the FDF files.
101    ;
102    ; At the end of PEI, the pages tables will be rebuilt into a
103    ; more permanent location by DxeIpl.
104    ;
105
106    mov     ecx, 6 * 0x1000 / 4
107    xor     eax, eax
108clearPageTablesMemoryLoop:
109    mov     dword[ecx * 4 + PT_ADDR (0) - 4], eax
110    loop    clearPageTablesMemoryLoop
111
112    ;
113    ; Top level Page Directory Pointers (1 * 512GB entry)
114    ;
115    mov     dword[PT_ADDR (0)], PT_ADDR (0x1000) + PAGE_PDP_ATTR
116    mov     dword[PT_ADDR (4)], edx
117
118    ;
119    ; Next level Page Directory Pointers (4 * 1GB entries => 4GB)
120    ;
121    mov     dword[PT_ADDR (0x1000)], PT_ADDR (0x2000) + PAGE_PDP_ATTR
122    mov     dword[PT_ADDR (0x1004)], edx
123    mov     dword[PT_ADDR (0x1008)], PT_ADDR (0x3000) + PAGE_PDP_ATTR
124    mov     dword[PT_ADDR (0x100C)], edx
125    mov     dword[PT_ADDR (0x1010)], PT_ADDR (0x4000) + PAGE_PDP_ATTR
126    mov     dword[PT_ADDR (0x1014)], edx
127    mov     dword[PT_ADDR (0x1018)], PT_ADDR (0x5000) + PAGE_PDP_ATTR
128    mov     dword[PT_ADDR (0x101C)], edx
129
130    ;
131    ; Page Table Entries (2048 * 2MB entries => 4GB)
132    ;
133    mov     ecx, 0x800
134pageTableEntriesLoop:
135    mov     eax, ecx
136    dec     eax
137    shl     eax, 21
138    add     eax, PAGE_2M_PDE_ATTR
139    mov     [ecx * 8 + PT_ADDR (0x2000 - 8)], eax
140    mov     [(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], edx
141    loop    pageTableEntriesLoop
142
143    ;
144    ; Set CR3 now that the paging structures are available
145    ;
146    mov     eax, PT_ADDR (0)
147    mov     cr3, eax
148
149    OneTimeCallRet SetCr3ForPageTables64
150