summaryrefslogtreecommitdiffstats
path: root/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c
blob: e66961e12287c64966afd4e4648d6551f852663b (plain)
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
/** @file
  This library implements CpuPageTableLib that are generic for IA32 family CPU.

  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "CpuPageTable.h"

/**
  Return the attribute of a 2M/1G page table entry.

  @param[in] PleB               Pointer to a 2M/1G page table entry.
  @param[in] ParentMapAttribute Pointer to the parent attribute.

  @return Attribute of the 2M/1G page table entry.
**/
UINT64
PageTableLibGetPleBMapAttribute (
  IN IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE  *PleB,
  IN IA32_MAP_ATTRIBUTE                 *ParentMapAttribute
  )
{
  IA32_MAP_ATTRIBUTE  MapAttribute;

  //
  // PageTableBaseAddress cannot be assigned field to field
  // because their bit positions are different in IA32_MAP_ATTRIBUTE and IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE.
  //
  MapAttribute.Uint64 = IA32_PLEB_PAGE_TABLE_BASE_ADDRESS (PleB);

  MapAttribute.Bits.Present        = ParentMapAttribute->Bits.Present & PleB->Bits.Present;
  MapAttribute.Bits.ReadWrite      = ParentMapAttribute->Bits.ReadWrite & PleB->Bits.ReadWrite;
  MapAttribute.Bits.UserSupervisor = ParentMapAttribute->Bits.UserSupervisor & PleB->Bits.UserSupervisor;
  MapAttribute.Bits.Nx             = ParentMapAttribute->Bits.Nx | PleB->Bits.Nx;
  MapAttribute.Bits.WriteThrough   = PleB->Bits.WriteThrough;
  MapAttribute.Bits.CacheDisabled  = PleB->Bits.CacheDisabled;
  MapAttribute.Bits.Accessed       = PleB->Bits.Accessed;

  MapAttribute.Bits.Pat           = PleB->Bits.Pat;
  MapAttribute.Bits.Dirty         = PleB->Bits.Dirty;
  MapAttribute.Bits.Global        = PleB->Bits.Global;
  MapAttribute.Bits.ProtectionKey = PleB->Bits.ProtectionKey;

  return MapAttribute.Uint64;
}

/**
  Return the attribute of a 4K page table entry.

  @param[in] Pte4K              Pointer to a 4K page table entry.
  @param[in] ParentMapAttribute Pointer to the parent attribute.

  @return Attribute of the 4K page table entry.
**/
UINT64
PageTableLibGetPte4KMapAttribute (
  IN IA32_PTE_4K         *Pte4K,
  IN IA32_MAP_ATTRIBUTE  *ParentMapAttribute
  )
{
  IA32_MAP_ATTRIBUTE  MapAttribute;

  MapAttribute.Uint64 = IA32_PTE4K_PAGE_TABLE_BASE_ADDRESS (Pte4K);

  MapAttribute.Bits.Present        = ParentMapAttribute->Bits.Present & Pte4K->Bits.Present;
  MapAttribute.Bits.ReadWrite      = ParentMapAttribute->Bits.ReadWrite & Pte4K->Bits.ReadWrite;
  MapAttribute.Bits.UserSupervisor = ParentMapAttribute->Bits.UserSupervisor & Pte4K->Bits.UserSupervisor;
  MapAttribute.Bits.Nx             = ParentMapAttribute->Bits.Nx | Pte4K->Bits.Nx;
  MapAttribute.Bits.WriteThrough   = Pte4K->Bits.WriteThrough;
  MapAttribute.Bits.CacheDisabled  = Pte4K->Bits.CacheDisabled;
  MapAttribute.Bits.Accessed       = Pte4K->Bits.Accessed;

  MapAttribute.Bits.Pat           = Pte4K->Bits.Pat;
  MapAttribute.Bits.Dirty         = Pte4K->Bits.Dirty;
  MapAttribute.Bits.Global        = Pte4K->Bits.Global;
  MapAttribute.Bits.ProtectionKey = Pte4K->Bits.ProtectionKey;

  return MapAttribute.Uint64;
}

/**
  Return the attribute of a non-leaf page table entry.

  @param[in] Pnle               Pointer to a non-leaf page table entry.
  @param[in] ParentMapAttribute Pointer to the parent attribute.

  @return Attribute of the non-leaf page table entry.
**/
UINT64
PageTableLibGetPnleMapAttribute (
  IN IA32_PAGE_NON_LEAF_ENTRY  *Pnle,
  IN IA32_MAP_ATTRIBUTE        *ParentMapAttribute
  )
{
  IA32_MAP_ATTRIBUTE  MapAttribute;

  MapAttribute.Uint64 = Pnle->Uint64;

  MapAttribute.Bits.Present        = ParentMapAttribute->Bits.Present & Pnle->Bits.Present;
  MapAttribute.Bits.ReadWrite      = ParentMapAttribute->Bits.ReadWrite & Pnle->Bits.ReadWrite;
  MapAttribute.Bits.UserSupervisor = ParentMapAttribute->Bits.UserSupervisor & Pnle->Bits.UserSupervisor;
  MapAttribute.Bits.Nx             = ParentMapAttribute->Bits.Nx | Pnle->Bits.Nx;
  MapAttribute.Bits.WriteThrough   = Pnle->Bits.WriteThrough;
  MapAttribute.Bits.CacheDisabled  = Pnle->Bits.CacheDisabled;
  MapAttribute.Bits.Accessed       = Pnle->Bits.Accessed;
  return MapAttribute.Uint64;
}

/**
  Return TRUE when the page table entry is a leaf entry that points to the physical address memory.
  Return FALSE when the page table entry is a non-leaf entry that points to the page table entries.

  @param[in] PagingEntry Pointer to the page table entry.
  @param[in] Level       Page level where the page table entry resides in.

  @retval TRUE  It's a leaf entry.
  @retval FALSE It's a non-leaf entry.
**/
BOOLEAN
IsPle (
  IN IA32_PAGING_ENTRY  *PagingEntry,
  IN UINTN              Level
  )
{
  //
  // PML5E and PML4E are always non-leaf entries.
  //
  if (Level == 1) {
    return TRUE;
  }

  if (((Level == 3) || (Level == 2))) {
    if (PagingEntry->PleB.Bits.MustBeOne == 1) {
      return TRUE;
    }
  }

  return FALSE;
}

/**
  Recursively parse the non-leaf page table entries.

  @param[in]      PageTableBaseAddress The base address of the 512 non-leaf page table entries in the specified level.
  @param[in]      Level                Page level. Could be 5, 4, 3, 2, 1.
  @param[in]      RegionStart          The base linear address of the region covered by the non-leaf page table entries.
  @param[in]      ParentMapAttribute   The mapping attribute of the parent entries.
  @param[in, out] Map                  Pointer to an array that describes multiple linear address ranges.
  @param[in, out] MapCount             Pointer to a UINTN that hold the actual number of entries in the Map.
  @param[in]      MapCapacity          The maximum number of entries the Map can hold.
  @param[in]      LastEntry            Pointer to last map entry.
  @param[in]      OneEntry             Pointer to a library internal storage that holds one map entry.
                                       It's used when Map array is used up.
**/
VOID
PageTableLibParsePnle (
  IN     UINT64              PageTableBaseAddress,
  IN     UINTN               Level,
  IN     UINT64              RegionStart,
  IN     IA32_MAP_ATTRIBUTE  *ParentMapAttribute,
  IN OUT IA32_MAP_ENTRY      *Map,
  IN OUT UINTN               *MapCount,
  IN     UINTN               MapCapacity,
  IN     IA32_MAP_ENTRY      **LastEntry,
  IN     IA32_MAP_ENTRY      *OneEntry
  )
{
  IA32_PAGING_ENTRY   *PagingEntry;
  UINTN               Index;
  IA32_MAP_ATTRIBUTE  MapAttribute;
  UINT64              RegionLength;

  ASSERT (OneEntry != NULL);

  PagingEntry  = (IA32_PAGING_ENTRY *)(UINTN)PageTableBaseAddress;
  RegionLength = REGION_LENGTH (Level);

  for (Index = 0; Index < 512; Index++, RegionStart += RegionLength) {
    if (PagingEntry[Index].Pce.Present == 0) {
      continue;
    }

    if (IsPle (&PagingEntry[Index], Level)) {
      ASSERT (Level == 1 || Level == 2 || Level == 3);

      if (Level == 1) {
        MapAttribute.Uint64 = PageTableLibGetPte4KMapAttribute (&PagingEntry[Index].Pte4K, ParentMapAttribute);
      } else {
        MapAttribute.Uint64 = PageTableLibGetPleBMapAttribute (&PagingEntry[Index].PleB, ParentMapAttribute);
      }

      if ((*LastEntry != NULL) &&
          ((*LastEntry)->LinearAddress + (*LastEntry)->Length == RegionStart) &&
          (IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&(*LastEntry)->Attribute) + (*LastEntry)->Length
           == IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&MapAttribute)) &&
          (IA32_MAP_ATTRIBUTE_ATTRIBUTES (&(*LastEntry)->Attribute) == IA32_MAP_ATTRIBUTE_ATTRIBUTES (&MapAttribute))
          )
      {
        //
        // Extend LastEntry.
        //
        (*LastEntry)->Length += RegionLength;
      } else {
        if (*MapCount < MapCapacity) {
          //
          // LastEntry points to next map entry in the array.
          //
          *LastEntry = &Map[*MapCount];
        } else {
          //
          // LastEntry points to library internal map entry.
          //
          *LastEntry = OneEntry;
        }

        //
        // Set LastEntry.
        //
        (*LastEntry)->LinearAddress    = RegionStart;
        (*LastEntry)->Length           = RegionLength;
        (*LastEntry)->Attribute.Uint64 = MapAttribute.Uint64;
        (*MapCount)++;
      }
    } else {
      MapAttribute.Uint64 = PageTableLibGetPnleMapAttribute (&PagingEntry[Index].Pnle, ParentMapAttribute);
      PageTableLibParsePnle (
        IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&PagingEntry[Index].Pnle),
        Level - 1,
        RegionStart,
        &MapAttribute,
        Map,
        MapCount,
        MapCapacity,
        LastEntry,
        OneEntry
        );
    }
  }
}

/**
  Parse page table.

  @param[in]      PageTable  Pointer to the page table.
  @param[in]      PagingMode The paging mode.
  @param[out]     Map        Return an array that describes multiple linear address ranges.
  @param[in, out] MapCount   On input, the maximum number of entries that Map can hold.
                             On output, the number of entries in Map.

  @retval RETURN_UNSUPPORTED       PageLevel is not 5 or 4.
  @retval RETURN_INVALID_PARAMETER MapCount is NULL.
  @retval RETURN_INVALID_PARAMETER *MapCount is not 0 but Map is NULL.
  @retval RETURN_BUFFER_TOO_SMALL  *MapCount is too small.
  @retval RETURN_SUCCESS           Page table is parsed successfully.
**/
RETURN_STATUS
EFIAPI
PageTableParse (
  IN     UINTN         PageTable,
  IN     PAGING_MODE   PagingMode,
  OUT  IA32_MAP_ENTRY  *Map,
  IN OUT UINTN         *MapCount
  )
{
  UINTN               MapCapacity;
  IA32_MAP_ATTRIBUTE  NopAttribute;
  IA32_MAP_ENTRY      *LastEntry;
  IA32_MAP_ENTRY      OneEntry;
  UINTN               MaxLevel;

  if ((PagingMode == Paging32bit) || (PagingMode == PagingPae) || (PagingMode >= PagingModeMax)) {
    //
    // 32bit paging is never supported.
    // PAE paging will be supported later.
    //
    return RETURN_UNSUPPORTED;
  }

  if (MapCount == NULL) {
    return RETURN_INVALID_PARAMETER;
  }

  if ((*MapCount != 0) && (Map == NULL)) {
    return RETURN_INVALID_PARAMETER;
  }

  if (PageTable == 0) {
    *MapCount = 0;
    return RETURN_SUCCESS;
  }

  //
  // Page table layout is as below:
  //
  // [IA32_CR3]
  //     |
  //     |
  //     V
  // [IA32_PML5E]
  // ...
  // [IA32_PML5E] --> [IA32_PML4E]
  //                  ...
  //                  [IA32_PML4E] --> [IA32_PDPTE_1G] --> 1G aligned physical address
  //                                   ...
  //                                   [IA32_PDPTE] --> [IA32_PDE_2M] --> 2M aligned physical address
  //                                                    ...
  //                                                    [IA32_PDE] --> [IA32_PTE_4K]  --> 4K aligned physical address
  //                                                                   ...
  //                                                                   [IA32_PTE_4K]  --> 4K aligned physical address
  //

  NopAttribute.Uint64              = 0;
  NopAttribute.Bits.Present        = 1;
  NopAttribute.Bits.ReadWrite      = 1;
  NopAttribute.Bits.UserSupervisor = 1;

  MaxLevel    = (UINT8)(PagingMode >> 8);
  MapCapacity = *MapCount;
  *MapCount   = 0;
  LastEntry   = NULL;
  PageTableLibParsePnle ((UINT64)PageTable, MaxLevel, 0, &NopAttribute, Map, MapCount, MapCapacity, &LastEntry, &OneEntry);

  if (*MapCount > MapCapacity) {
    return RETURN_BUFFER_TOO_SMALL;
  }

  return RETURN_SUCCESS;
}