/** @file
  X86 page table entry definitions.

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

**/

#pragma once

#define IA32_PE_BASE_ADDRESS_MASK_40  0xFFFFFFFFFF000ull
#define IA32_PE_BASE_ADDRESS_MASK_39  0xFFFFFFFFFE000ull

typedef enum {
  Pte   = 1,
  Pde   = 2,
  Pdpte = 3,
  Pml4  = 4,
  Pml5  = 5
} IA32_PAGE_LEVEL;

typedef struct {
  UINT32    Present        : 1;       // 0 = Not present in memory, 1 = Present in memory
  UINT32    ReadWrite      : 1;       // 0 = Read-Only, 1= Read/Write
  UINT32    UserSupervisor : 1;       // 0 = Supervisor, 1=User
  UINT32    Reserved0      : 29;
  UINT32    Reserved1      : 31;
  UINT32    Nx             : 1;        // No Execute bit
} IA32_PAGE_COMMON_ENTRY;

///
/// Format of a non-leaf entry that references a page table entry
///
typedef union {
  struct {
    UINT32    Present                  : 1;  // 0 = Not present in memory, 1 = Present in memory
    UINT32    ReadWrite                : 1;  // 0 = Read-Only, 1= Read/Write
    UINT32    UserSupervisor           : 1;  // 0 = Supervisor, 1=User
    UINT32    WriteThrough             : 1;  // 0 = Write-Back caching, 1=Write-Through caching
    UINT32    CacheDisabled            : 1;  // 0 = Cached, 1=Non-Cached
    UINT32    Accessed                 : 1;  // 0 = Not accessed, 1 = Accessed (set by CPU)
    UINT32    Available0               : 1;  // Ignored
    UINT32    MustBeZero               : 1;  // Must Be Zero
    UINT32    Available2               : 4;  // Ignored
    UINT32    PageTableBaseAddressLow  : 20; // Page Table Base Address Low

    UINT32    PageTableBaseAddressHigh : 20; // Page Table Base Address High
    UINT32    Available3               : 11; // Ignored
    UINT32    Nx                       : 1;  // No Execute bit
  } Bits;
  UINT64    Uint64;
} IA32_PAGE_NON_LEAF_ENTRY;

#define IA32_PNLE_PAGE_TABLE_BASE_ADDRESS(pa)  ((pa)->Uint64 & IA32_PE_BASE_ADDRESS_MASK_40)

///
/// Format of a PML5 Entry (PML5E) that References a PML4 Table
///
typedef IA32_PAGE_NON_LEAF_ENTRY IA32_PML5E;

///
/// Format of a PML4 Entry (PML4E) that References a Page-Directory-Pointer Table
///
typedef IA32_PAGE_NON_LEAF_ENTRY IA32_PML4E;

///
/// Format of a Page-Directory-Pointer-Table Entry (PDPTE) that References a Page Directory
///
typedef IA32_PAGE_NON_LEAF_ENTRY IA32_PDPTE;

///
/// Format of a Page-Directory Entry that References a Page Table
///
typedef IA32_PAGE_NON_LEAF_ENTRY IA32_PDE;

///
/// Format of a leaf entry that Maps a 1-Gbyte or 2-MByte Page
///
typedef union {
  struct {
    UINT32    Present                  : 1;  // 0 = Not present in memory, 1 = Present in memory
    UINT32    ReadWrite                : 1;  // 0 = Read-Only, 1= Read/Write
    UINT32    UserSupervisor           : 1;  // 0 = Supervisor, 1=User
    UINT32    WriteThrough             : 1;  // 0 = Write-Back caching, 1=Write-Through caching
    UINT32    CacheDisabled            : 1;  // 0 = Cached, 1=Non-Cached
    UINT32    Accessed                 : 1;  // 0 = Not accessed, 1 = Accessed (set by CPU)
    UINT32    Dirty                    : 1;  // 0 = Not dirty, 1 = Dirty (set by CPU)
    UINT32    MustBeOne                : 1;  // Page Size. Must Be One
    UINT32    Global                   : 1;  // 0 = Not global, 1 = Global (if CR4.PGE = 1)
    UINT32    Available1               : 3;  // Ignored
    UINT32    Pat                      : 1;  // PAT
    UINT32    PageTableBaseAddressLow  : 19; // Page Table Base Address Low

    UINT32    PageTableBaseAddressHigh : 20; // Page Table Base Address High
    UINT32    Available3               : 7;  // Ignored
    UINT32    ProtectionKey            : 4;  // Protection key
    UINT32    Nx                       : 1;  // No Execute bit
  } Bits;
  UINT64    Uint64;
} IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE;
#define IA32_PLEB_PAGE_TABLE_BASE_ADDRESS(pa)  ((pa)->Uint64 & IA32_PE_BASE_ADDRESS_MASK_39)

///
/// Format of a Page-Directory Entry that Maps a 2-MByte Page
///
typedef IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE IA32_PDE_2M;

///
/// Format of a Page-Directory-Pointer-Table Entry (PDPTE) that Maps a 1-GByte Page
///
typedef IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE IA32_PDPTE_1G;

///
/// Format of a Page-Table Entry that Maps a 4-KByte Page
///
typedef union {
  struct {
    UINT32    Present                  : 1;  // 0 = Not present in memory, 1 = Present in memory
    UINT32    ReadWrite                : 1;  // 0 = Read-Only, 1= Read/Write
    UINT32    UserSupervisor           : 1;  // 0 = Supervisor, 1=User
    UINT32    WriteThrough             : 1;  // 0 = Write-Back caching, 1=Write-Through caching
    UINT32    CacheDisabled            : 1;  // 0 = Cached, 1=Non-Cached
    UINT32    Accessed                 : 1;  // 0 = Not accessed, 1 = Accessed (set by CPU)
    UINT32    Dirty                    : 1;  // 0 = Not dirty, 1 = Dirty (set by CPU)
    UINT32    Pat                      : 1;  // PAT
    UINT32    Global                   : 1;  // 0 = Not global, 1 = Global (if CR4.PGE = 1)
    UINT32    Available1               : 3;  // Ignored
    UINT32    PageTableBaseAddressLow  : 20; // Page Table Base Address Low

    UINT32    PageTableBaseAddressHigh : 20; // Page Table Base Address High
    UINT32    Available3               : 7;  // Ignored
    UINT32    ProtectionKey            : 4;  // Protection key
    UINT32    Nx                       : 1;  // No Execute bit
  } Bits;
  UINT64    Uint64;
} IA32_PTE_4K;
#define IA32_PTE4K_PAGE_TABLE_BASE_ADDRESS(pa)  ((pa)->Uint64 & IA32_PE_BASE_ADDRESS_MASK_40)

///
/// Format of a Page-Directory-Pointer-Table Entry (PDPTE) that References a Page Directory (32bit PAE specific)
///
typedef union {
  struct {
    UINT32    Present                  : 1;  // 0 = Not present in memory, 1 = Present in memory
    UINT32    MustBeZero               : 2;  // Must Be Zero
    UINT32    WriteThrough             : 1;  // 0 = Write-Back caching, 1=Write-Through caching
    UINT32    CacheDisabled            : 1;  // 0 = Cached, 1=Non-Cached
    UINT32    MustBeZero2              : 4;  // Must Be Zero
    UINT32    Available                : 3;  // Ignored
    UINT32    PageTableBaseAddressLow  : 20; // Page Table Base Address Low

    UINT32    PageTableBaseAddressHigh : 20; // Page Table Base Address High
    UINT32    MustBeZero3              : 12; // Must Be Zero
  } Bits;
  UINT64    Uint64;
} IA32_PDPTE_PAE;

typedef union {
  IA32_PAGE_NON_LEAF_ENTRY             Pnle; // To access Pml5, Pml4, Pdpte and Pde.
  IA32_PML5E                           Pml5;
  IA32_PML4E                           Pml4;
  IA32_PDPTE                           Pdpte;
  IA32_PDE                             Pde;

  IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE    PleB; // to access Pdpte1G and Pde2M.
  IA32_PDPTE_1G                        Pdpte1G;
  IA32_PDE_2M                          Pde2M;

  IA32_PTE_4K                          Pte4K;

  IA32_PDPTE_PAE                       PdptePae;
  IA32_PAGE_COMMON_ENTRY               Pce; // To access all common bits in above entries.

  UINT64                               Uint64;
  UINTN                                Uintn;
} IA32_PAGING_ENTRY;
