/** @file
  UEFI Application to display CPUID leaf information.

  Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
  This program and the accompanying materials
  are licensed and made available under the terms and conditions of the BSD License
  which accompanies this distribution.  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/UefiLib.h>
#include <Register/Cpuid.h>

///
/// Macro used to display the value of a bit field in a register returned by CPUID.
///
#define PRINT_BIT_FIELD(Variable, FieldName) \
  Print (L"%5a%42a: %x\n", #Variable, #FieldName, Variable.Bits.FieldName);

///
/// Macro used to display the value of a register returned by CPUID.
///
#define PRINT_VALUE(Variable, Description) \
  Print (L"%5a%42a: %x\n", #Variable, #Description, Variable);

///
/// Structure for cache description lookup table
///
typedef struct {
  UINT8  CacheDescriptor;
  CHAR8  *Type;
  CHAR8  *Description;
} CPUID_CACHE_INFO_DESCRIPTION;

///
/// Cache description lookup table
///
CPUID_CACHE_INFO_DESCRIPTION  mCpuidCacheInfoDescription[] = {
  { 0x00 , "General"  , "Null descriptor, this byte contains no information" },
  { 0x01 , "TLB"      , "Instruction TLB: 4 KByte pages, 4-way set associative, 32 entries" },
  { 0x02 , "TLB"      , "Instruction TLB: 4 MByte pages, fully associative, 2 entries" },
  { 0x03 , "TLB"      , "Data TLB: 4 KByte pages, 4-way set associative, 64 entries" },
  { 0x04 , "TLB"      , "Data TLB: 4 MByte pages, 4-way set associative, 8 entries" },
  { 0x05 , "TLB"      , "Data TLB1: 4 MByte pages, 4-way set associative, 32 entries" },
  { 0x06 , "Cache"    , "1st-level instruction cache: 8 KBytes, 4-way set associative, 32 byte line size" },
  { 0x08 , "Cache"    , "1st-level instruction cache: 16 KBytes, 4-way set associative, 32 byte line size" },
  { 0x09 , "Cache"    , "1st-level instruction cache: 32KBytes, 4-way set associative, 64 byte line size" },
  { 0x0A , "Cache"    , "1st-level data cache: 8 KBytes, 2-way set associative, 32 byte line size" },
  { 0x0B , "TLB"      , "Instruction TLB: 4 MByte pages, 4-way set associative, 4 entries" },
  { 0x0C , "Cache"    , "1st-level data cache: 16 KBytes, 4-way set associative, 32 byte line size" },
  { 0x0D , "Cache"    , "1st-level data cache: 16 KBytes, 4-way set associative, 64 byte line size" },
  { 0x0E , "Cache"    , "1st-level data cache: 24 KBytes, 6-way set associative, 64 byte line size" },
  { 0x1D , "Cache"    , "2nd-level cache: 128 KBytes, 2-way set associative, 64 byte line size" },
  { 0x21 , "Cache"    , "2nd-level cache: 256 KBytes, 8-way set associative, 64 byte line size" },
  { 0x22 , "Cache"    , "3rd-level cache: 512 KBytes, 4-way set associative, 64 byte line size, 2 lines per sector" },
  { 0x23 , "Cache"    , "3rd-level cache: 1 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector" },
  { 0x24 , "Cache"    , "2nd-level cache: 1 MBytes, 16-way set associative, 64 byte line size" },
  { 0x25 , "Cache"    , "3rd-level cache: 2 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector" },
  { 0x29 , "Cache"    , "3rd-level cache: 4 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector" },
  { 0x2C , "Cache"    , "1st-level data cache: 32 KBytes, 8-way set associative, 64 byte line size" },
  { 0x30 , "Cache"    , "1st-level instruction cache: 32 KBytes, 8-way set associative, 64 byte line size" },
  { 0x40 , "Cache"    , "No 2nd-level cache or, if processor contains a valid 2nd-level cache, no 3rd-level cache" },
  { 0x41 , "Cache"    , "2nd-level cache: 128 KBytes, 4-way set associative, 32 byte line size" },
  { 0x42 , "Cache"    , "2nd-level cache: 256 KBytes, 4-way set associative, 32 byte line size" },
  { 0x43 , "Cache"    , "2nd-level cache: 512 KBytes, 4-way set associative, 32 byte line size" },
  { 0x44 , "Cache"    , "2nd-level cache: 1 MByte, 4-way set associative, 32 byte line size" },
  { 0x45 , "Cache"    , "2nd-level cache: 2 MByte, 4-way set associative, 32 byte line size" },
  { 0x46 , "Cache"    , "3rd-level cache: 4 MByte, 4-way set associative, 64 byte line size" },
  { 0x47 , "Cache"    , "3rd-level cache: 8 MByte, 8-way set associative, 64 byte line size" },
  { 0x48 , "Cache"    , "2nd-level cache: 3MByte, 12-way set associative, 64 byte line size" },
  { 0x49 , "Cache"    , "3rd-level cache: 4MB, 16-way set associative, 64-byte line size (Intel Xeon processor MP, Family 0FH, Model 06H). 2nd-level cache: 4 MByte, 16-way set associative, 64 byte line size" },
  { 0x4A , "Cache"    , "3rd-level cache: 6MByte, 12-way set associative, 64 byte line size" },
  { 0x4B , "Cache"    , "3rd-level cache: 8MByte, 16-way set associative, 64 byte line size" },
  { 0x4C , "Cache"    , "3rd-level cache: 12MByte, 12-way set associative, 64 byte line size" },
  { 0x4D , "Cache"    , "3rd-level cache: 16MByte, 16-way set associative, 64 byte line size" },
  { 0x4E , "Cache"    , "2nd-level cache: 6MByte, 24-way set associative, 64 byte line size" },
  { 0x4F , "TLB"      , "Instruction TLB: 4 KByte pages, 32 entries" },
  { 0x50 , "TLB"      , "Instruction TLB: 4 KByte and 2-MByte or 4-MByte pages, 64 entries" },
  { 0x51 , "TLB"      , "Instruction TLB: 4 KByte and 2-MByte or 4-MByte pages, 128 entries" },
  { 0x52 , "TLB"      , "Instruction TLB: 4 KByte and 2-MByte or 4-MByte pages, 256 entries" },
  { 0x55 , "TLB"      , "Instruction TLB: 2-MByte or 4-MByte pages, fully associative, 7 entries" },
  { 0x56 , "TLB"      , "Data TLB0: 4 MByte pages, 4-way set associative, 16 entries" },
  { 0x57 , "TLB"      , "Data TLB0: 4 KByte pages, 4-way associative, 16 entries" },
  { 0x59 , "TLB"      , "Data TLB0: 4 KByte pages, fully associative, 16 entries" },
  { 0x5A , "TLB"      , "Data TLB0: 2 MByte or 4 MByte pages, 4-way set associative, 32 entries" },
  { 0x5B , "TLB"      , "Data TLB: 4 KByte and 4 MByte pages, 64 entries" },
  { 0x5C , "TLB"      , "Data TLB: 4 KByte and 4 MByte pages,128 entries" },
  { 0x5D , "TLB"      , "Data TLB: 4 KByte and 4 MByte pages,256 entries" },
  { 0x60 , "Cache"    , "1st-level data cache: 16 KByte, 8-way set associative, 64 byte line size" },
  { 0x61 , "TLB"      , "Instruction TLB: 4 KByte pages, fully associative, 48 entries" },
  { 0x63 , "TLB"      , "Data TLB: 2 MByte or 4 MByte pages, 4-way set associative, 32 entries and a separate array with 1 GByte pages, 4-way set associative, 4 entries" },
  { 0x64 , "TLB"      , "Data TLB: 4 KByte pages, 4-way set associative, 512 entries" },
  { 0x66 , "Cache"    , "1st-level data cache: 8 KByte, 4-way set associative, 64 byte line size" },
  { 0x67 , "Cache"    , "1st-level data cache: 16 KByte, 4-way set associative, 64 byte line size" },
  { 0x68 , "Cache"    , "1st-level data cache: 32 KByte, 4-way set associative, 64 byte line size" },
  { 0x6A , "Cache"    , "uTLB: 4 KByte pages, 8-way set associative, 64 entries" },
  { 0x6B , "Cache"    , "DTLB: 4 KByte pages, 8-way set associative, 256 entries" },
  { 0x6C , "Cache"    , "DTLB: 2M/4M pages, 8-way set associative, 128 entries" },
  { 0x6D , "Cache"    , "DTLB: 1 GByte pages, fully associative, 16 entries" },
  { 0x70 , "Cache"    , "Trace cache: 12 K-uop, 8-way set associative" },
  { 0x71 , "Cache"    , "Trace cache: 16 K-uop, 8-way set associative" },
  { 0x72 , "Cache"    , "Trace cache: 32 K-uop, 8-way set associative" },
  { 0x76 , "TLB"      , "Instruction TLB: 2M/4M pages, fully associative, 8 entries" },
  { 0x78 , "Cache"    , "2nd-level cache: 1 MByte, 4-way set associative, 64byte line size" },
  { 0x79 , "Cache"    , "2nd-level cache: 128 KByte, 8-way set associative, 64 byte line size, 2 lines per sector" },
  { 0x7A , "Cache"    , "2nd-level cache: 256 KByte, 8-way set associative, 64 byte line size, 2 lines per sector" },
  { 0x7B , "Cache"    , "2nd-level cache: 512 KByte, 8-way set associative, 64 byte line size, 2 lines per sector" },
  { 0x7C , "Cache"    , "2nd-level cache: 1 MByte, 8-way set associative, 64 byte line size, 2 lines per sector" },
  { 0x7D , "Cache"    , "2nd-level cache: 2 MByte, 8-way set associative, 64byte line size" },
  { 0x7F , "Cache"    , "2nd-level cache: 512 KByte, 2-way set associative, 64-byte line size" },
  { 0x80 , "Cache"    , "2nd-level cache: 512 KByte, 8-way set associative, 64-byte line size" },
  { 0x82 , "Cache"    , "2nd-level cache: 256 KByte, 8-way set associative, 32 byte line size" },
  { 0x83 , "Cache"    , "2nd-level cache: 512 KByte, 8-way set associative, 32 byte line size" },
  { 0x84 , "Cache"    , "2nd-level cache: 1 MByte, 8-way set associative, 32 byte line size" },
  { 0x85 , "Cache"    , "2nd-level cache: 2 MByte, 8-way set associative, 32 byte line size" },
  { 0x86 , "Cache"    , "2nd-level cache: 512 KByte, 4-way set associative, 64 byte line size" },
  { 0x87 , "Cache"    , "2nd-level cache: 1 MByte, 8-way set associative, 64 byte line size" },
  { 0xA0 , "DTLB"     , "DTLB: 4k pages, fully associative, 32 entries" },
  { 0xB0 , "TLB"      , "Instruction TLB: 4 KByte pages, 4-way set associative, 128 entries" },
  { 0xB1 , "TLB"      , "Instruction TLB: 2M pages, 4-way, 8 entries or 4M pages, 4-way, 4 entries" },
  { 0xB2 , "TLB"      , "Instruction TLB: 4KByte pages, 4-way set associative, 64 entries" },
  { 0xB3 , "TLB"      , "Data TLB: 4 KByte pages, 4-way set associative, 128 entries" },
  { 0xB4 , "TLB"      , "Data TLB1: 4 KByte pages, 4-way associative, 256 entries" },
  { 0xB5 , "TLB"      , "Instruction TLB: 4KByte pages, 8-way set associative, 64 entries" },
  { 0xB6 , "TLB"      , "Instruction TLB: 4KByte pages, 8-way set associative, 128 entries" },
  { 0xBA , "TLB"      , "Data TLB1: 4 KByte pages, 4-way associative, 64 entries" },
  { 0xC0 , "TLB"      , "Data TLB: 4 KByte and 4 MByte pages, 4-way associative, 8 entries" },
  { 0xC1 , "STLB"     , "Shared 2nd-Level TLB: 4 KByte/2MByte pages, 8-way associative, 1024 entries" },
  { 0xC2 , "DTLB"     , "DTLB: 4 KByte/2 MByte pages, 4-way associative, 16 entries" },
  { 0xC3 , "STLB"     , "Shared 2nd-Level TLB: 4 KByte /2 MByte pages, 6-way associative, 1536 entries. Also 1GBbyte pages, 4-way, 16 entries." },
  { 0xC4 , "DTLB"     , "DTLB: 2M/4M Byte pages, 4-way associative, 32 entries" },
  { 0xCA , "STLB"     , "Shared 2nd-Level TLB: 4 KByte pages, 4-way associative, 512 entries" },
  { 0xD0 , "Cache"    , "3rd-level cache: 512 KByte, 4-way set associative, 64 byte line size" },
  { 0xD1 , "Cache"    , "3rd-level cache: 1 MByte, 4-way set associative, 64 byte line size" },
  { 0xD2 , "Cache"    , "3rd-level cache: 2 MByte, 4-way set associative, 64 byte line size" },
  { 0xD6 , "Cache"    , "3rd-level cache: 1 MByte, 8-way set associative, 64 byte line size" },
  { 0xD7 , "Cache"    , "3rd-level cache: 2 MByte, 8-way set associative, 64 byte line size" },
  { 0xD8 , "Cache"    , "3rd-level cache: 4 MByte, 8-way set associative, 64 byte line size" },
  { 0xDC , "Cache"    , "3rd-level cache: 1.5 MByte, 12-way set associative, 64 byte line size" },
  { 0xDD , "Cache"    , "3rd-level cache: 3 MByte, 12-way set associative, 64 byte line size" },
  { 0xDE , "Cache"    , "3rd-level cache: 6 MByte, 12-way set associative, 64 byte line size" },
  { 0xE2 , "Cache"    , "3rd-level cache: 2 MByte, 16-way set associative, 64 byte line size" },
  { 0xE3 , "Cache"    , "3rd-level cache: 4 MByte, 16-way set associative, 64 byte line size" },
  { 0xE4 , "Cache"    , "3rd-level cache: 8 MByte, 16-way set associative, 64 byte line size" },
  { 0xEA , "Cache"    , "3rd-level cache: 12MByte, 24-way set associative, 64 byte line size" },
  { 0xEB , "Cache"    , "3rd-level cache: 18MByte, 24-way set associative, 64 byte line size" },
  { 0xEC , "Cache"    , "3rd-level cache: 24MByte, 24-way set associative, 64 byte line size" },
  { 0xF0 , "Prefetch" , "64-Byte prefetching" },
  { 0xF1 , "Prefetch" , "128-Byte prefetching" },
  { 0xFF , "General"  , "CPUID leaf 2 does not report cache descriptor information, use CPUID leaf 4 to query cache parameters" }
};

///
/// The maximum supported CPUID leaf index starting from leaf 0x00000000.
///
UINT32  gMaximumBasicFunction    = CPUID_SIGNATURE;

///
/// The maximum supported CPUID leaf index starting from leaf 0x80000000.
///
UINT32  gMaximumExtendedFunction = CPUID_EXTENDED_FUNCTION;

/**
  Display CPUID_SIGNATURE leaf.

**/
VOID
CpuidSignature (
  VOID
  )
{
  UINT32 Eax;
  UINT32 Ebx;
  UINT32 Ecx;
  UINT32 Edx;
  CHAR8  Signature[13];

  AsmCpuid (CPUID_SIGNATURE, &Eax, &Ebx, &Ecx, &Edx);

  Print (L"CPUID_SIGNATURE (Leaf %08x)\n", CPUID_SIGNATURE);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax, Ebx, Ecx, Edx);
  PRINT_VALUE (Eax, MaximumLeaf);
  *(UINT32 *)(Signature + 0) = Ebx;
  *(UINT32 *)(Signature + 4) = Edx;
  *(UINT32 *)(Signature + 8) = Ecx;
  Signature [12] = 0;
  Print (L"  Signature = %a\n", Signature);

  gMaximumBasicFunction = Eax;
}

/**
  Display CPUID_VERSION_INFO leaf.

**/
VOID
CpuidVersionInfo (
  VOID
  )
{
  CPUID_VERSION_INFO_EAX  Eax;
  CPUID_VERSION_INFO_EBX  Ebx;
  CPUID_VERSION_INFO_ECX  Ecx;
  CPUID_VERSION_INFO_EDX  Edx;
  UINT32                  DisplayFamily;
  UINT32                  DisplayModel;

  if (CPUID_VERSION_INFO > gMaximumBasicFunction) {
    return;
  }

  AsmCpuid (CPUID_VERSION_INFO, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32);

  Print (L"CPUID_VERSION_INFO (Leaf %08x)\n", CPUID_VERSION_INFO);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32);

  DisplayFamily = Eax.Bits.FamilyId;
  if (Eax.Bits.FamilyId == 0x0F) {
    DisplayFamily |= (Eax.Bits.ExtendedFamilyId << 4);
  }

  DisplayModel = Eax.Bits.Model;
  if (Eax.Bits.FamilyId == 0x06 || Eax.Bits.FamilyId == 0x0f) {
    DisplayModel |= (Eax.Bits.ExtendedModelId << 4);
  }

  Print (L"  Family = %x  Model = %x  Stepping = %x\n", DisplayFamily, DisplayModel, Eax.Bits.SteppingId);

  PRINT_BIT_FIELD (Eax, SteppingId);
  PRINT_BIT_FIELD (Eax, Model);
  PRINT_BIT_FIELD (Eax, FamilyId);
  PRINT_BIT_FIELD (Eax, ProcessorType);
  PRINT_BIT_FIELD (Eax, ExtendedModelId);
  PRINT_BIT_FIELD (Eax, ExtendedFamilyId);
  PRINT_BIT_FIELD (Ebx, BrandIndex);
  PRINT_BIT_FIELD (Ebx, CacheLineSize);
  PRINT_BIT_FIELD (Ebx, MaximumAddressableIdsForLogicalProcessors);
  PRINT_BIT_FIELD (Ebx, InitialLocalApicId);
  PRINT_BIT_FIELD (Ecx, SSE3);
  PRINT_BIT_FIELD (Ecx, PCLMULQDQ);
  PRINT_BIT_FIELD (Ecx, DTES64);
  PRINT_BIT_FIELD (Ecx, MONITOR);
  PRINT_BIT_FIELD (Ecx, DS_CPL);
  PRINT_BIT_FIELD (Ecx, VMX);
  PRINT_BIT_FIELD (Ecx, SMX);
  PRINT_BIT_FIELD (Ecx, TM2);
  PRINT_BIT_FIELD (Ecx, SSSE3);
  PRINT_BIT_FIELD (Ecx, CNXT_ID);
  PRINT_BIT_FIELD (Ecx, SDBG);
  PRINT_BIT_FIELD (Ecx, FMA);
  PRINT_BIT_FIELD (Ecx, CMPXCHG16B);
  PRINT_BIT_FIELD (Ecx, xTPR_Update_Control);
  PRINT_BIT_FIELD (Ecx, PDCM);
  PRINT_BIT_FIELD (Ecx, PCID);
  PRINT_BIT_FIELD (Ecx, DCA);
  PRINT_BIT_FIELD (Ecx, SSE4_1);
  PRINT_BIT_FIELD (Ecx, SSE4_2);
  PRINT_BIT_FIELD (Ecx, x2APIC);
  PRINT_BIT_FIELD (Ecx, MOVBE);
  PRINT_BIT_FIELD (Ecx, POPCNT);
  PRINT_BIT_FIELD (Ecx, TSC_Deadline);
  PRINT_BIT_FIELD (Ecx, AESNI);
  PRINT_BIT_FIELD (Ecx, XSAVE);
  PRINT_BIT_FIELD (Ecx, OSXSAVE);
  PRINT_BIT_FIELD (Ecx, AVX);
  PRINT_BIT_FIELD (Ecx, F16C);
  PRINT_BIT_FIELD (Ecx, RDRAND);
  PRINT_BIT_FIELD (Edx, FPU);
  PRINT_BIT_FIELD (Edx, VME);
  PRINT_BIT_FIELD (Edx, DE);
  PRINT_BIT_FIELD (Edx, PSE);
  PRINT_BIT_FIELD (Edx, TSC);
  PRINT_BIT_FIELD (Edx, MSR);
  PRINT_BIT_FIELD (Edx, PAE);
  PRINT_BIT_FIELD (Edx, MCE);
  PRINT_BIT_FIELD (Edx, CX8);
  PRINT_BIT_FIELD (Edx, APIC);
  PRINT_BIT_FIELD (Edx, SEP);
  PRINT_BIT_FIELD (Edx, MTRR);
  PRINT_BIT_FIELD (Edx, PGE);
  PRINT_BIT_FIELD (Edx, MCA);
  PRINT_BIT_FIELD (Edx, CMOV);
  PRINT_BIT_FIELD (Edx, PAT);
  PRINT_BIT_FIELD (Edx, PSE_36);
  PRINT_BIT_FIELD (Edx, PSN);
  PRINT_BIT_FIELD (Edx, CLFSH);
  PRINT_BIT_FIELD (Edx, DS);
  PRINT_BIT_FIELD (Edx, ACPI);
  PRINT_BIT_FIELD (Edx, MMX);
  PRINT_BIT_FIELD (Edx, FXSR);
  PRINT_BIT_FIELD (Edx, SSE);
  PRINT_BIT_FIELD (Edx, SSE2);
  PRINT_BIT_FIELD (Edx, SS);
  PRINT_BIT_FIELD (Edx, HTT);
  PRINT_BIT_FIELD (Edx, TM);
  PRINT_BIT_FIELD (Edx, PBE);
}

/**
  Lookup a cache description string from the mCpuidCacheInfoDescription table.

  @param[in] CacheDescriptor  Cache descriptor value from CPUID_CACHE_INFO.

**/
CPUID_CACHE_INFO_DESCRIPTION *
LookupCacheDescription (
  UINT8  CacheDescriptor
  )
{
  UINTN  NumDescriptors;
  UINTN  Descriptor;

  if (CacheDescriptor == 0x00) {
    return NULL;
  }
  NumDescriptors = sizeof (mCpuidCacheInfoDescription)/sizeof (mCpuidCacheInfoDescription[0]);
  for (Descriptor = 0; Descriptor < NumDescriptors; Descriptor++) {
    if (CacheDescriptor == mCpuidCacheInfoDescription[Descriptor].CacheDescriptor) {
      return &mCpuidCacheInfoDescription[Descriptor];
    }
  }
  return NULL;
}

/**
  Display CPUID_CACHE_INFO leaf for each supported cache descriptor.

**/
VOID
CpuidCacheInfo (
  VOID
  )
{
  CPUID_CACHE_INFO_CACHE_TLB    Eax;
  CPUID_CACHE_INFO_CACHE_TLB    Ebx;
  CPUID_CACHE_INFO_CACHE_TLB    Ecx;
  CPUID_CACHE_INFO_CACHE_TLB    Edx;
  UINTN                         Index;
  CPUID_CACHE_INFO_DESCRIPTION  *CacheDescription;

  if (CPUID_CACHE_INFO > gMaximumBasicFunction) {
    return;
  }

  AsmCpuid (CPUID_CACHE_INFO, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32);

  Print (L"CPUID_CACHE_INFO (Leaf %08x)\n", CPUID_CACHE_INFO);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32);
  if (Eax.Bits.NotValid == 0) {
    //
    // Process Eax.CacheDescriptor[1..3].  Ignore Eax.CacheDescriptor[0]
    //
    for (Index = 1; Index < 4; Index++) {
      CacheDescription = LookupCacheDescription (Eax.CacheDescriptor[Index]);
      if (CacheDescription != NULL) {
        Print (L"  %-8a %a\n",
          CacheDescription->Type,
          CacheDescription->Description
          );
      }
    }
  }
  if (Ebx.Bits.NotValid == 0) {
    //
    // Process Ebx.CacheDescriptor[0..3]
    //
    for (Index = 0; Index < 4; Index++) {
      CacheDescription = LookupCacheDescription (Ebx.CacheDescriptor[Index]);
      if (CacheDescription != NULL) {
        Print (L"  %-8a %a\n",
          CacheDescription->Type,
          CacheDescription->Description
          );
      }
    }
  }
  if (Ecx.Bits.NotValid == 0) {
    //
    // Process Ecx.CacheDescriptor[0..3]
    //
    for (Index = 0; Index < 4; Index++) {
      CacheDescription = LookupCacheDescription (Ecx.CacheDescriptor[Index]);
      if (CacheDescription != NULL) {
        Print (L"  %-8a %a\n",
          CacheDescription->Type,
          CacheDescription->Description
          );
      }
    }
  }
  if (Edx.Bits.NotValid == 0) {
    //
    // Process Edx.CacheDescriptor[0..3]
    //
    for (Index = 0; Index < 4; Index++) {
      CacheDescription = LookupCacheDescription (Edx.CacheDescriptor[Index]);
      if (CacheDescription != NULL) {
        Print (L"  %-8a %a\n",
          CacheDescription->Type,
          CacheDescription->Description
          );
      }
    }
  }
}

/**
  Display CPUID_SERIAL_NUMBER leaf if it is supported.

**/
VOID
CpuidSerialNumber (
  VOID
  )
{
  CPUID_VERSION_INFO_EDX  VersionInfoEdx;
  UINT32                  Ecx;
  UINT32                  Edx;

  Print (L"CPUID_SERIAL_NUMBER (Leaf %08x)\n", CPUID_SERIAL_NUMBER);

  if (CPUID_SERIAL_NUMBER > gMaximumBasicFunction) {
    return;
  }

  AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32);
  if (VersionInfoEdx.Bits.PSN == 0) {
    Print (L"  Not Supported\n");
    return;
  }

  AsmCpuid (CPUID_SERIAL_NUMBER, NULL, NULL, &Ecx, &Edx);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", 0, 0, Ecx, Edx);
  Print (L"  Processor Serial Number = %08x%08x%08x\n", 0, Edx, Ecx);
}

/**
  Display CPUID_CACHE_PARAMS for all supported sub-leafs.

**/
VOID
CpuidCacheParams (
  VOID
  )
{
  UINT32                  CacheLevel;
  CPUID_CACHE_PARAMS_EAX  Eax;
  CPUID_CACHE_PARAMS_EBX  Ebx;
  UINT32                  Ecx;
  CPUID_CACHE_PARAMS_EDX  Edx;

  if (CPUID_CACHE_PARAMS > gMaximumBasicFunction) {
    return;
  }

  CacheLevel = 0;
  do {
    AsmCpuidEx (
      CPUID_CACHE_PARAMS, CacheLevel,
      &Eax.Uint32, &Ebx.Uint32, &Ecx, &Edx.Uint32
      );
    if (Eax.Bits.CacheType != CPUID_CACHE_PARAMS_CACHE_TYPE_NULL) {
      Print (L"CPUID_CACHE_PARAMS (Leaf %08x, Sub-Leaf %08x)\n", CPUID_CACHE_PARAMS, CacheLevel);
      Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx, Edx.Uint32);
      PRINT_BIT_FIELD (Eax, CacheType);
      PRINT_BIT_FIELD (Eax, CacheLevel);
      PRINT_BIT_FIELD (Eax, SelfInitializingCache);
      PRINT_BIT_FIELD (Eax, FullyAssociativeCache);
      PRINT_BIT_FIELD (Eax, MaximumAddressableIdsForLogicalProcessors);
      PRINT_BIT_FIELD (Eax, MaximumAddressableIdsForProcessorCores);
      PRINT_BIT_FIELD (Ebx, LineSize);
      PRINT_BIT_FIELD (Ebx, LinePartitions);
      PRINT_BIT_FIELD (Ebx, Ways);
      PRINT_VALUE     (Ecx, NumberOfSets);
      PRINT_BIT_FIELD (Edx, Invalidate);
      PRINT_BIT_FIELD (Edx, CacheInclusiveness);
      PRINT_BIT_FIELD (Edx, ComplexCacheIndexing);
    }
    CacheLevel++;
  } while (Eax.Bits.CacheType != CPUID_CACHE_PARAMS_CACHE_TYPE_NULL);
}

/**
  Display CPUID_MONITOR_MWAIT leaf.

**/
VOID
CpuidMonitorMwait (
  VOID
  )
{
  CPUID_MONITOR_MWAIT_EAX  Eax;
  CPUID_MONITOR_MWAIT_EBX  Ebx;
  CPUID_MONITOR_MWAIT_ECX  Ecx;
  CPUID_MONITOR_MWAIT_EDX  Edx;

  if (CPUID_MONITOR_MWAIT > gMaximumBasicFunction) {
    return;
  }

  AsmCpuid (CPUID_MONITOR_MWAIT, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32);

  Print (L"CPUID_MONITOR_MWAIT (Leaf %08x)\n", CPUID_MONITOR_MWAIT);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32);

  PRINT_BIT_FIELD (Eax, SmallestMonitorLineSize);
  PRINT_BIT_FIELD (Ebx, LargestMonitorLineSize);
  PRINT_BIT_FIELD (Ecx, ExtensionsSupported);
  PRINT_BIT_FIELD (Ecx, InterruptAsBreak);
  PRINT_BIT_FIELD (Edx, C0States);
  PRINT_BIT_FIELD (Edx, C1States);
  PRINT_BIT_FIELD (Edx, C2States);
  PRINT_BIT_FIELD (Edx, C3States);
  PRINT_BIT_FIELD (Edx, C4States);
  PRINT_BIT_FIELD (Edx, C5States);
  PRINT_BIT_FIELD (Edx, C6States);
  PRINT_BIT_FIELD (Edx, C7States);
}

/**
  Display CPUID_THERMAL_POWER_MANAGEMENT leaf.

**/
VOID
CpuidThermalPowerManagement (
  VOID
  )
{
  CPUID_THERMAL_POWER_MANAGEMENT_EAX  Eax;
  CPUID_THERMAL_POWER_MANAGEMENT_EBX  Ebx;
  CPUID_THERMAL_POWER_MANAGEMENT_ECX  Ecx;

  if (CPUID_THERMAL_POWER_MANAGEMENT > gMaximumBasicFunction) {
    return;
  }

  AsmCpuid (CPUID_THERMAL_POWER_MANAGEMENT, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, NULL);

  Print (L"CPUID_THERMAL_POWER_MANAGEMENT (Leaf %08x)\n", CPUID_THERMAL_POWER_MANAGEMENT);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, 0);

  PRINT_BIT_FIELD (Eax, DigitalTemperatureSensor);
  PRINT_BIT_FIELD (Eax, TurboBoostTechnology);
  PRINT_BIT_FIELD (Eax, ARAT);
  PRINT_BIT_FIELD (Eax, PLN);
  PRINT_BIT_FIELD (Eax, ECMD);
  PRINT_BIT_FIELD (Eax, PTM);
  PRINT_BIT_FIELD (Eax, HWP);
  PRINT_BIT_FIELD (Eax, HWP_Notification);
  PRINT_BIT_FIELD (Eax, HWP_Activity_Window);
  PRINT_BIT_FIELD (Eax, HWP_Energy_Performance_Preference);
  PRINT_BIT_FIELD (Eax, HWP_Package_Level_Request);
  PRINT_BIT_FIELD (Eax, HDC);
  PRINT_BIT_FIELD (Ebx, InterruptThresholds);
  PRINT_BIT_FIELD (Ecx, HardwareCoordinationFeedback);
  PRINT_BIT_FIELD (Ecx, PerformanceEnergyBias);
}

/**
  Display CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS for all supported sub-leafs.

**/
VOID
CpuidStructuredExtendedFeatureFlags (
  VOID
  )
{
  UINT32                                       Eax;
  CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_EBX  Ebx;
  CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_ECX  Ecx;
  UINT32                                       SubLeaf;

  if (CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS > gMaximumBasicFunction) {
    return;
  }

  AsmCpuidEx (
    CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS,
    CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_SUB_LEAF_INFO,
    &Eax, NULL, NULL, NULL
    );
  for (SubLeaf = 0; SubLeaf <= Eax; SubLeaf++) {
    AsmCpuidEx (
      CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS,
      SubLeaf,
      NULL, &Ebx.Uint32, &Ecx.Uint32, NULL
      );
    if (Ebx.Uint32 != 0 || Ecx.Uint32 != 0) {
      Print (L"CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS (Leaf %08x, Sub-Leaf %08x)\n", CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, SubLeaf);
      Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax, Ebx.Uint32, Ecx.Uint32, 0);
      PRINT_BIT_FIELD (Ebx, FSGSBASE);
      PRINT_BIT_FIELD (Ebx, IA32_TSC_ADJUST);
      PRINT_BIT_FIELD (Ebx, SGX);
      PRINT_BIT_FIELD (Ebx, BMI1);
      PRINT_BIT_FIELD (Ebx, HLE);
      PRINT_BIT_FIELD (Ebx, AVX2);
      PRINT_BIT_FIELD (Ebx, FDP_EXCPTN_ONLY);
      PRINT_BIT_FIELD (Ebx, SMEP);
      PRINT_BIT_FIELD (Ebx, BMI2);
      PRINT_BIT_FIELD (Ebx, EnhancedRepMovsbStosb);
      PRINT_BIT_FIELD (Ebx, INVPCID);
      PRINT_BIT_FIELD (Ebx, RTM);
      PRINT_BIT_FIELD (Ebx, RDT_M);
      PRINT_BIT_FIELD (Ebx, DeprecateFpuCsDs);
      PRINT_BIT_FIELD (Ebx, MPX);
      PRINT_BIT_FIELD (Ebx, RDT_A);
      PRINT_BIT_FIELD (Ebx, RDSEED);
      PRINT_BIT_FIELD (Ebx, ADX);
      PRINT_BIT_FIELD (Ebx, SMAP);
      PRINT_BIT_FIELD (Ebx, CLFLUSHOPT);
      PRINT_BIT_FIELD (Ebx, CLWB);
      PRINT_BIT_FIELD (Ebx, IntelProcessorTrace);
      PRINT_BIT_FIELD (Ebx, SHA);
      PRINT_BIT_FIELD (Ecx, PREFETCHWT1);
      PRINT_BIT_FIELD (Ecx, UMIP);
      PRINT_BIT_FIELD (Ecx, PKU);
      PRINT_BIT_FIELD (Ecx, OSPKE);
      PRINT_BIT_FIELD (Ecx, MAWAU);
      PRINT_BIT_FIELD (Ecx, RDPID);
      PRINT_BIT_FIELD (Ecx, SGX_LC);
    }
  }
}

/**
  Display CPUID_DIRECT_CACHE_ACCESS_INFO leaf.

**/
VOID
CpuidDirectCacheAccessInfo (
  VOID
  )
{
  UINT32  Eax;

  if (CPUID_DIRECT_CACHE_ACCESS_INFO > gMaximumBasicFunction) {
    return;
  }

  AsmCpuid (CPUID_DIRECT_CACHE_ACCESS_INFO, &Eax, NULL, NULL, NULL);
  Print (L"CPUID_DIRECT_CACHE_ACCESS_INFO (Leaf %08x)\n", CPUID_DIRECT_CACHE_ACCESS_INFO);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax, 0, 0, 0);
}

/**
  Display CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING leaf.

**/
VOID
CpuidArchitecturalPerformanceMonitoring (
  VOID
  )
{
  CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING_EAX  Eax;
  CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING_EBX  Ebx;
  CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING_EDX  Edx;

  if (CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING > gMaximumBasicFunction) {
    return;
  }

  AsmCpuid (CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING, &Eax.Uint32, &Ebx.Uint32, NULL, &Edx.Uint32);
  Print (L"CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING (Leaf %08x)\n", CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, 0, Edx.Uint32);
  PRINT_BIT_FIELD (Eax, ArchPerfMonVerID);
  PRINT_BIT_FIELD (Eax, PerformanceMonitorCounters);
  PRINT_BIT_FIELD (Eax, PerformanceMonitorCounterWidth);
  PRINT_BIT_FIELD (Eax, EbxBitVectorLength);
  PRINT_BIT_FIELD (Ebx, UnhaltedCoreCycles);
  PRINT_BIT_FIELD (Ebx, InstructionsRetired);
  PRINT_BIT_FIELD (Ebx, UnhaltedReferenceCycles);
  PRINT_BIT_FIELD (Ebx, LastLevelCacheReferences);
  PRINT_BIT_FIELD (Ebx, LastLevelCacheMisses);
  PRINT_BIT_FIELD (Ebx, BranchInstructionsRetired);
  PRINT_BIT_FIELD (Ebx, AllBranchMispredictRetired);
  PRINT_BIT_FIELD (Edx, FixedFunctionPerformanceCounters);
  PRINT_BIT_FIELD (Edx, FixedFunctionPerformanceCounterWidth);
}

/**
  Display CPUID_EXTENDED_TOPOLOGY leafs for all supported levels.

**/
VOID
CpuidExtendedTopology (
  VOID
  )
{
  CPUID_EXTENDED_TOPOLOGY_EAX  Eax;
  CPUID_EXTENDED_TOPOLOGY_EBX  Ebx;
  CPUID_EXTENDED_TOPOLOGY_ECX  Ecx;
  UINT32                       Edx;
  UINT32                       LevelNumber;

  if (CPUID_EXTENDED_TOPOLOGY > gMaximumBasicFunction) {
    return;
  }

  LevelNumber = 0;
  do {
    AsmCpuidEx (
      CPUID_EXTENDED_TOPOLOGY, LevelNumber,
      &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx
      );
    if (Eax.Bits.ApicIdShift != 0) {
      Print (L"CPUID_EXTENDED_TOPOLOGY (Leaf %08x, Sub-Leaf %08x)\n", CPUID_EXTENDED_TOPOLOGY, LevelNumber);
      Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx);
      PRINT_BIT_FIELD (Eax, ApicIdShift);
      PRINT_BIT_FIELD (Ebx, LogicalProcessors);
      PRINT_BIT_FIELD (Ecx, LevelNumber);
      PRINT_BIT_FIELD (Ecx, LevelType);
      PRINT_VALUE     (Edx, x2APIC_ID);
    }
    LevelNumber++;
  } while (Eax.Bits.ApicIdShift != 0);
}

/**
  Display CPUID_EXTENDED_STATE sub-leaf.

**/
VOID
CpuidExtendedStateSubLeaf (
  VOID
  )
{
  CPUID_EXTENDED_STATE_SUB_LEAF_EAX  Eax;
  UINT32                             Ebx;
  CPUID_EXTENDED_STATE_SUB_LEAF_ECX  Ecx;
  UINT32                             Edx;

  AsmCpuidEx (
    CPUID_EXTENDED_STATE, CPUID_EXTENDED_STATE_SUB_LEAF,
    &Eax.Uint32, &Ebx, &Ecx.Uint32, &Edx
    );
  Print (L"CPUID_EXTENDED_STATE (Leaf %08x, Sub-Leaf %08x)\n", CPUID_EXTENDED_STATE, CPUID_EXTENDED_STATE_SUB_LEAF);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx, Ecx.Uint32, Edx);
  PRINT_BIT_FIELD (Eax, XSAVEOPT);
  PRINT_BIT_FIELD (Eax, XSAVEC);
  PRINT_BIT_FIELD (Eax, XGETBV);
  PRINT_BIT_FIELD (Eax, XSAVES);
  PRINT_VALUE     (Ebx, EnabledSaveStateSize_XCR0_IA32_XSS);
  PRINT_BIT_FIELD (Ecx, XCR0);
  PRINT_BIT_FIELD (Ecx, PT);
  PRINT_BIT_FIELD (Ecx, XCR0_1);
  PRINT_VALUE     (Edx, IA32_XSS_Supported_32_63);
}

/**
  Display CPUID_EXTENDED_STATE size and offset information sub-leaf.

**/
VOID
CpuidExtendedStateSizeOffset (
  VOID
  )
{
  UINT32                                Eax;
  UINT32                                Ebx;
  CPUID_EXTENDED_STATE_SIZE_OFFSET_ECX  Ecx;
  UINT32                                Edx;
  UINT32                                SubLeaf;

  for (SubLeaf = CPUID_EXTENDED_STATE_SIZE_OFFSET; SubLeaf < 32; SubLeaf++) {
    AsmCpuidEx (
      CPUID_EXTENDED_STATE, SubLeaf,
      &Eax, &Ebx, &Ecx.Uint32, &Edx
      );
    if (Edx != 0) {
      Print (L"CPUID_EXTENDED_STATE (Leaf %08x, Sub-Leaf %08x)\n", CPUID_EXTENDED_STATE, SubLeaf);
      Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax, Ebx, Ecx.Uint32, Edx);
      PRINT_VALUE     (Eax, FeatureSaveStateSize);
      PRINT_VALUE     (Ebx, FeatureSaveStateOffset);
      PRINT_BIT_FIELD (Ecx, XSS);
      PRINT_BIT_FIELD (Ecx, Compacted);
    }
  }
}

/**
  Display CPUID_EXTENDED_STATE main leaf and sub-leafs.

**/
VOID
CpuidExtendedStateMainLeaf (
  VOID
  )
{
  CPUID_EXTENDED_STATE_MAIN_LEAF_EAX  Eax;
  UINT32                              Ebx;
  UINT32                              Ecx;
  UINT32                              Edx;

  if (CPUID_EXTENDED_STATE > gMaximumBasicFunction) {
    return;
  }

  AsmCpuidEx (
    CPUID_EXTENDED_STATE, CPUID_EXTENDED_STATE_MAIN_LEAF,
    &Eax.Uint32, &Ebx, &Ecx, &Edx
    );
  Print (L"CPUID_EXTENDED_STATE (Leaf %08x, Sub-Leaf %08x)\n", CPUID_EXTENDED_STATE, CPUID_EXTENDED_STATE_MAIN_LEAF);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx, Ecx, Edx);
  PRINT_BIT_FIELD (Eax, x87);
  PRINT_BIT_FIELD (Eax, SSE);
  PRINT_BIT_FIELD (Eax, AVX);
  PRINT_BIT_FIELD (Eax, MPX);
  PRINT_BIT_FIELD (Eax, AVX_512);
  PRINT_BIT_FIELD (Eax, IA32_XSS);
  PRINT_BIT_FIELD (Eax, PKRU);
  PRINT_VALUE     (Ebx, EnabledSaveStateSize);
  PRINT_VALUE     (Ecx, SupportedSaveStateSize);
  PRINT_VALUE     (Edx, XCR0_Supported_32_63);

  CpuidExtendedStateSubLeaf ();
  CpuidExtendedStateSizeOffset ();
}

/**
  Display CPUID_INTEL_RDT_MONITORING enumeration sub-leaf.

**/
VOID
CpuidIntelRdtMonitoringEnumerationSubLeaf (
  VOID
  )
{
  UINT32                                                  Ebx;
  CPUID_INTEL_RDT_MONITORING_ENUMERATION_SUB_LEAF_EDX     Edx;

  if (CPUID_INTEL_RDT_MONITORING > gMaximumBasicFunction) {
    return;
  }

  AsmCpuidEx (
    CPUID_INTEL_RDT_MONITORING, CPUID_INTEL_RDT_MONITORING_ENUMERATION_SUB_LEAF,
    NULL, &Ebx, NULL, &Edx.Uint32
    );
  Print (L"CPUID_INTEL_RDT_MONITORING (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_RDT_MONITORING, CPUID_INTEL_RDT_MONITORING_ENUMERATION_SUB_LEAF);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", 0, Ebx, 0, Edx.Uint32);
  PRINT_VALUE     (Ebx, Maximum_RMID_Range);
  PRINT_BIT_FIELD (Edx, L3CacheRDT_M);
}

/**
  Display CPUID_INTEL_RDT_MONITORING L3 cache capability sub-leaf.

**/
VOID
CpuidIntelRdtMonitoringL3CacheCapabilitySubLeaf (
  VOID
  )
{
  UINT32                                                 Ebx;
  UINT32                                                 Ecx;
  CPUID_INTEL_RDT_MONITORING_L3_CACHE_SUB_LEAF_EDX       Edx;

  if (CPUID_INTEL_RDT_MONITORING > gMaximumBasicFunction) {
    return;
  }

  AsmCpuidEx (
    CPUID_INTEL_RDT_MONITORING, CPUID_INTEL_RDT_MONITORING_L3_CACHE_SUB_LEAF,
    NULL, &Ebx, &Ecx, &Edx.Uint32
    );
  Print (L"CPUID_INTEL_RDT_MONITORING (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_RDT_MONITORING, CPUID_INTEL_RDT_MONITORING_L3_CACHE_SUB_LEAF);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", 0, Ebx, Ecx, Edx.Uint32);
  PRINT_VALUE     (Ebx, OccupancyConversionFactor);
  PRINT_VALUE     (Ecx, Maximum_RMID_Range);
  PRINT_BIT_FIELD (Edx, L3CacheOccupancyMonitoring);
  PRINT_BIT_FIELD (Edx, L3CacheTotalBandwidthMonitoring);
  PRINT_BIT_FIELD (Edx, L3CacheLocalBandwidthMonitoring);
}

/**
  Display CPUID_INTEL_RDT_ALLOCATION L3 cache allocation technology enumeration
  sub-leaf.

**/
VOID
CpuidIntelRdtAllocationL3CacheSubLeaf (
  VOID
  )
{
  CPUID_INTEL_RDT_ALLOCATION_L3_CACHE_SUB_LEAF_EAX  Eax;
  UINT32                                            Ebx;
  CPUID_INTEL_RDT_ALLOCATION_L3_CACHE_SUB_LEAF_ECX  Ecx;
  CPUID_INTEL_RDT_ALLOCATION_L3_CACHE_SUB_LEAF_EDX  Edx;

  AsmCpuidEx (
    CPUID_INTEL_RDT_ALLOCATION, CPUID_INTEL_RDT_ALLOCATION_L3_CACHE_SUB_LEAF,
    &Eax.Uint32, &Ebx, &Ecx.Uint32, &Edx.Uint32
    );
  Print (L"CPUID_INTEL_RDT_ALLOCATION (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_RDT_ALLOCATION, CPUID_INTEL_RDT_ALLOCATION_L3_CACHE_SUB_LEAF);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx, Ecx.Uint32, Edx.Uint32);
  PRINT_BIT_FIELD (Eax, CapacityLength);
  PRINT_VALUE     (Ebx, AllocationUnitBitMap);
  PRINT_BIT_FIELD (Ecx, CosUpdatesInfrequent);
  PRINT_BIT_FIELD (Ecx, CodeDataPrioritization);
  PRINT_BIT_FIELD (Edx, HighestCosNumber);
}

/**
  Display CPUID_INTEL_RDT_ALLOCATION L2 cache allocation technology enumeration
  sub-leaf.

**/
VOID
CpuidIntelRdtAllocationL2CacheSubLeaf (
  VOID
  )
{
  CPUID_INTEL_RDT_ALLOCATION_L2_CACHE_SUB_LEAF_EAX  Eax;
  UINT32                                            Ebx;
  CPUID_INTEL_RDT_ALLOCATION_L2_CACHE_SUB_LEAF_EDX  Edx;

  AsmCpuidEx (
    CPUID_INTEL_RDT_ALLOCATION, CPUID_INTEL_RDT_ALLOCATION_L2_CACHE_SUB_LEAF,
    &Eax.Uint32, &Ebx, NULL, &Edx.Uint32
    );
  Print (L"CPUID_INTEL_RDT_ALLOCATION (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_RDT_ALLOCATION, CPUID_INTEL_RDT_ALLOCATION_L2_CACHE_SUB_LEAF);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx, 0, Edx.Uint32);
  PRINT_BIT_FIELD (Eax, CapacityLength);
  PRINT_VALUE     (Ebx, AllocationUnitBitMap);
  PRINT_BIT_FIELD (Edx, HighestCosNumber);
}

/**
  Display CPUID_INTEL_RDT_ALLOCATION main leaf and sub-leaves.

**/
VOID
CpuidIntelRdtAllocationMainLeaf (
  VOID
  )
{
  CPUID_INTEL_RDT_ALLOCATION_ENUMERATION_SUB_LEAF_EBX  Ebx;

  if (CPUID_INTEL_RDT_ALLOCATION > gMaximumBasicFunction) {
    return;
  }

  AsmCpuidEx (
    CPUID_INTEL_RDT_ALLOCATION, CPUID_INTEL_RDT_ALLOCATION_ENUMERATION_SUB_LEAF,
    NULL, &Ebx.Uint32, NULL, NULL
    );
  Print (L"CPUID_INTEL_RDT_ALLOCATION (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_RDT_ALLOCATION, CPUID_INTEL_RDT_ALLOCATION_ENUMERATION_SUB_LEAF);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", 0, Ebx.Uint32, 0, 0);
  PRINT_BIT_FIELD (Ebx, L3CacheAllocation);
  PRINT_BIT_FIELD (Ebx, L2CacheAllocation);

  CpuidIntelRdtAllocationL3CacheSubLeaf ();
  CpuidIntelRdtAllocationL2CacheSubLeaf ();
}

/**
  Display Sub-Leaf 0 Enumeration of Intel SGX Capabilities.

**/
VOID
CpuidEnumerationOfIntelSgxCapabilities0SubLeaf (
  VOID
  )
{
  CPUID_INTEL_SGX_CAPABILITIES_0_SUB_LEAF_EAX  Eax;
  UINT32                                       Ebx;
  CPUID_INTEL_SGX_CAPABILITIES_0_SUB_LEAF_EDX  Edx;

  AsmCpuidEx (
    CPUID_INTEL_SGX, CPUID_INTEL_SGX_CAPABILITIES_0_SUB_LEAF,
    &Eax.Uint32, &Ebx, NULL, &Edx.Uint32
    );
  Print (L"CPUID_INTEL_SGX (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_SGX, CPUID_INTEL_SGX_CAPABILITIES_0_SUB_LEAF);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx, 0, Edx.Uint32);
  PRINT_BIT_FIELD (Eax, SGX1);
  PRINT_BIT_FIELD (Eax, SGX2);
  PRINT_BIT_FIELD (Edx, MaxEnclaveSize_Not64);
  PRINT_BIT_FIELD (Edx, MaxEnclaveSize_64);
}

/**
  Display Sub-Leaf 1 Enumeration of Intel SGX Capabilities.

**/
VOID
CpuidEnumerationOfIntelSgxCapabilities1SubLeaf (
  VOID
  )
{
  UINT32                                       Eax;
  UINT32                                       Ebx;
  UINT32                                       Ecx;
  UINT32                                       Edx;

  AsmCpuidEx (
    CPUID_INTEL_SGX, CPUID_INTEL_SGX_CAPABILITIES_1_SUB_LEAF,
    &Eax, &Ebx, &Ecx, &Edx
    );
  Print (L"CPUID_INTEL_SGX (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_SGX, CPUID_INTEL_SGX_CAPABILITIES_1_SUB_LEAF);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax, Ebx, Ecx, Edx);
}

/**
  Display Sub-Leaf Index 2 or Higher Enumeration of Intel SGX Resources.

**/
VOID
CpuidEnumerationOfIntelSgxResourcesSubLeaf (
  VOID
  )
{
  CPUID_INTEL_SGX_CAPABILITIES_RESOURCES_SUB_LEAF_EAX  Eax;
  CPUID_INTEL_SGX_CAPABILITIES_RESOURCES_SUB_LEAF_EBX  Ebx;
  CPUID_INTEL_SGX_CAPABILITIES_RESOURCES_SUB_LEAF_ECX  Ecx;
  CPUID_INTEL_SGX_CAPABILITIES_RESOURCES_SUB_LEAF_EDX  Edx;
  UINT32                                               SubLeaf;
 
  SubLeaf = CPUID_INTEL_SGX_CAPABILITIES_RESOURCES_SUB_LEAF;
  do {
    AsmCpuidEx (
      CPUID_INTEL_SGX, SubLeaf,
      &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32
      );
    if (Eax.Bits.SubLeafType == 0x1) {
      Print (L"CPUID_INTEL_SGX (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_SGX, SubLeaf);
      Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32);
      PRINT_BIT_FIELD (Eax, SubLeafType);
      PRINT_BIT_FIELD (Eax, LowAddressOfEpcSection);
      PRINT_BIT_FIELD (Ebx, HighAddressOfEpcSection);
      PRINT_BIT_FIELD (Ecx, EpcSection);
      PRINT_BIT_FIELD (Ecx, LowSizeOfEpcSection);
      PRINT_BIT_FIELD (Edx, HighSizeOfEpcSection);
    }
    SubLeaf++;
  } while (Eax.Bits.SubLeafType == 0x1);
}

/**
  Display Intel SGX Resource Enumeration.

**/
VOID
CpuidEnumerationOfIntelSgx (
  VOID
  )
{
  CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_EBX  Ebx;

  if (CPUID_INTEL_SGX > gMaximumBasicFunction) {
    return;
  }

  AsmCpuidEx (
    CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS,
    CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_SUB_LEAF_INFO,
    NULL, &Ebx.Uint32, NULL, NULL
    );
  if (Ebx.Bits.SGX != 1) {
    //
    // Only if CPUID.(EAX=07H, ECX=0H):EBX.SGX = 1, the processor has support
    // for Intel SGX.
    //
    return;
  }
  
  CpuidEnumerationOfIntelSgxCapabilities0SubLeaf ();
  CpuidEnumerationOfIntelSgxCapabilities1SubLeaf ();
  CpuidEnumerationOfIntelSgxResourcesSubLeaf ();
}

/**
  Display CPUID_INTEL_PROCESSOR_TRACE sub-leafs.

  @param[in] MaximumSubLeaf  Maximum sub-leaf index for CPUID_INTEL_PROCESSOR_TRACE.

**/
VOID
CpuidIntelProcessorTraceSubLeaf (
  UINT32  MaximumSubLeaf
  )
{
  UINT32                                    SubLeaf;
  CPUID_INTEL_PROCESSOR_TRACE_SUB_LEAF_EAX  Eax;
  CPUID_INTEL_PROCESSOR_TRACE_SUB_LEAF_EBX  Ebx;

  for (SubLeaf = CPUID_INTEL_PROCESSOR_TRACE_SUB_LEAF; SubLeaf <= MaximumSubLeaf; SubLeaf++) {
    AsmCpuidEx (
      CPUID_INTEL_PROCESSOR_TRACE, SubLeaf,
      &Eax.Uint32, &Ebx.Uint32, NULL, NULL
      );
    Print (L"CPUID_INTEL_PROCESSOR_TRACE (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_PROCESSOR_TRACE, SubLeaf);
    Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, 0, 0);
    PRINT_BIT_FIELD (Eax, ConfigurableAddressRanges);
    PRINT_BIT_FIELD (Eax, MtcPeriodEncodings);
    PRINT_BIT_FIELD (Ebx, CycleThresholdEncodings);
    PRINT_BIT_FIELD (Ebx, PsbFrequencyEncodings);
  }
}

/**
  Display CPUID_INTEL_PROCESSOR_TRACE main leaf and sub-leafs.

**/
VOID
CpuidIntelProcessorTraceMainLeaf (
  VOID
  )
{
  UINT32                                     Eax;
  CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF_EBX  Ebx;
  CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF_ECX  Ecx;

  if (CPUID_INTEL_PROCESSOR_TRACE > gMaximumBasicFunction) {
    return;
  }

  AsmCpuidEx (
    CPUID_INTEL_PROCESSOR_TRACE, CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF,
    &Eax, &Ebx.Uint32, &Ecx.Uint32, NULL
    );
  Print (L"CPUID_INTEL_PROCESSOR_TRACE (Leaf %08x, Sub-Leaf %08x)\n", CPUID_INTEL_PROCESSOR_TRACE, CPUID_INTEL_PROCESSOR_TRACE_MAIN_LEAF);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax, Ebx.Uint32, Ecx.Uint32, 0);
  PRINT_VALUE     (Eax, MaximumSubLeaf);
  PRINT_BIT_FIELD (Ebx, Cr3Filter);
  PRINT_BIT_FIELD (Ebx, ConfigurablePsb);
  PRINT_BIT_FIELD (Ebx, IpTraceStopFiltering);
  PRINT_BIT_FIELD (Ebx, Mtc);
  PRINT_BIT_FIELD (Ebx, PTWrite);
  PRINT_BIT_FIELD (Ebx, PowerEventTrace);
  PRINT_BIT_FIELD (Ecx, RTIT);
  PRINT_BIT_FIELD (Ecx, ToPA);
  PRINT_BIT_FIELD (Ecx, SingleRangeOutput);
  PRINT_BIT_FIELD (Ecx, TraceTransportSubsystem);
  PRINT_BIT_FIELD (Ecx, LIP);

  CpuidIntelProcessorTraceSubLeaf (Eax);
}

/**
  Display CPUID_TIME_STAMP_COUNTER leaf.

**/
VOID
CpuidTimeStampCounter (
  VOID
  )
{
  UINT32  Eax;
  UINT32  Ebx;
  UINT32  Ecx;

  if (CPUID_TIME_STAMP_COUNTER > gMaximumBasicFunction) {
    return;
  }

  AsmCpuid (CPUID_TIME_STAMP_COUNTER, &Eax, &Ebx, &Ecx, NULL);
  Print (L"CPUID_TIME_STAMP_COUNTER (Leaf %08x)\n", CPUID_TIME_STAMP_COUNTER);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax, Ebx, Ecx, 0);
}

/**
  Display CPUID_PROCESSOR_FREQUENCY leaf.

**/
VOID
CpuidProcessorFrequency (
  VOID
  )
{
  CPUID_PROCESSOR_FREQUENCY_EAX  Eax;
  CPUID_PROCESSOR_FREQUENCY_EBX  Ebx;
  CPUID_PROCESSOR_FREQUENCY_ECX  Ecx;

  if (CPUID_PROCESSOR_FREQUENCY > gMaximumBasicFunction) {
    return;
  }

  AsmCpuid (CPUID_PROCESSOR_FREQUENCY, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, NULL);
  Print (L"CPUID_PROCESSOR_FREQUENCY (Leaf %08x)\n", CPUID_PROCESSOR_FREQUENCY);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, 0);
  PRINT_BIT_FIELD (Eax, ProcessorBaseFrequency);
  PRINT_BIT_FIELD (Ebx, MaximumFrequency);
  PRINT_BIT_FIELD (Ecx, BusFrequency);
}

/**
  Display CPUID_SOC_VENDOR sub-leafs that contain the SoC Vendor Brand String.
  Also display these sub-leafs as a single SoC Vendor Brand String.

**/
VOID
CpuidSocVendorBrandString (
  VOID
  )
{
  CPUID_SOC_VENDOR_BRAND_STRING_DATA  Eax;
  CPUID_SOC_VENDOR_BRAND_STRING_DATA  Ebx;
  CPUID_SOC_VENDOR_BRAND_STRING_DATA  Ecx;
  CPUID_SOC_VENDOR_BRAND_STRING_DATA  Edx;
  //
  // Array to store brand string from 3 brand string leafs with
  // 4 32-bit brand string values per leaf and an extra value to
  // null terminate the string.
  //
  UINT32                              BrandString[3 * 4 + 1];

  AsmCpuidEx (
    CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_BRAND_STRING1,
    &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32
    );
  Print (L"CPUID_SOC_VENDOR (Leaf %08x, Sub-Leaf %08x)\n", CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_BRAND_STRING1);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32);
  BrandString[0] = Eax.Uint32;
  BrandString[1] = Ebx.Uint32;
  BrandString[2] = Ecx.Uint32;
  BrandString[3] = Edx.Uint32;

  AsmCpuidEx (
    CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_BRAND_STRING2,
    &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32
    );
  Print (L"CPUID_SOC_VENDOR (Leaf %08x, Sub-Leaf %08x)\n", CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_BRAND_STRING2);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32);
  BrandString[4] = Eax.Uint32;
  BrandString[5] = Ebx.Uint32;
  BrandString[6] = Ecx.Uint32;
  BrandString[7] = Edx.Uint32;

  AsmCpuidEx (
    CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_BRAND_STRING3,
    &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32
    );
  Print (L"CPUID_SOC_VENDOR (Leaf %08x, Sub-Leaf %08x)\n", CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_BRAND_STRING3);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32);
  BrandString[8]  = Eax.Uint32;
  BrandString[9]  = Ebx.Uint32;
  BrandString[10] = Ecx.Uint32;
  BrandString[11] = Edx.Uint32;

  BrandString[12] = 0;

  Print (L"Vendor Brand String = %a\n", (CHAR8 *)BrandString);
}

/**
  Display CPUID_SOC_VENDOR main leaf and sub-leafs.

**/
VOID
CpuidSocVendor (
  VOID
  )
{
  UINT32                          Eax;
  CPUID_SOC_VENDOR_MAIN_LEAF_EBX  Ebx;
  UINT32                          Ecx;
  UINT32                          Edx;

  if (CPUID_SOC_VENDOR > gMaximumBasicFunction) {
    return;
  }

  AsmCpuidEx (
    CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_MAIN_LEAF,
    &Eax, &Ebx.Uint32, &Ecx, &Edx
    );
  Print (L"CPUID_SOC_VENDOR (Leaf %08x, Sub-Leaf %08x)\n", CPUID_SOC_VENDOR, CPUID_SOC_VENDOR_MAIN_LEAF);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax, Ebx.Uint32, Ecx, Edx);
  if (Eax < 3) {
    Print (L"  Not Supported\n");
    return;
  }
  PRINT_VALUE     (Eax, MaxSOCID_Index);
  PRINT_BIT_FIELD (Ebx, SocVendorId);
  PRINT_BIT_FIELD (Ebx, IsVendorScheme);
  PRINT_VALUE     (Ecx, ProjectID);
  PRINT_VALUE     (Edx, SteppingID);
  CpuidSocVendorBrandString ();
}

/**
  Display CPUID_EXTENDED_FUNCTION leaf.

**/
VOID
CpuidExtendedFunction (
  VOID
  )
{
  UINT32  Eax;

  AsmCpuid (CPUID_EXTENDED_FUNCTION, &Eax, NULL, NULL, NULL);
  Print (L"CPUID_EXTENDED_FUNCTION (Leaf %08x)\n", CPUID_EXTENDED_FUNCTION);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax, 0, 0, 0);
  PRINT_VALUE     (Eax, MaximumExtendedFunction);

  gMaximumExtendedFunction = Eax;
}

/**
  Display CPUID_EXTENDED_CPU_SIG leaf.

**/
VOID
CpuidExtendedCpuSig (
  VOID
  )
{
  UINT32                      Eax;
  CPUID_EXTENDED_CPU_SIG_ECX  Ecx;
  CPUID_EXTENDED_CPU_SIG_EDX  Edx;

  if (CPUID_EXTENDED_CPU_SIG > gMaximumExtendedFunction) {
    return;
  }

  AsmCpuid (CPUID_EXTENDED_CPU_SIG, &Eax, NULL, &Ecx.Uint32, &Edx.Uint32);
  Print (L"CPUID_EXTENDED_CPU_SIG (Leaf %08x)\n", CPUID_EXTENDED_CPU_SIG);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax, 0, Ecx.Uint32, Edx.Uint32);
  PRINT_BIT_FIELD (Ecx, LAHF_SAHF);
  PRINT_BIT_FIELD (Ecx, LZCNT);
  PRINT_BIT_FIELD (Ecx, PREFETCHW);
  PRINT_BIT_FIELD (Edx, SYSCALL_SYSRET);
  PRINT_BIT_FIELD (Edx, NX);
  PRINT_BIT_FIELD (Edx, Page1GB);
  PRINT_BIT_FIELD (Edx, RDTSCP);
  PRINT_BIT_FIELD (Edx, LM);
}

/**
  Display CPUID_BRAND_STRING1, CPUID_BRAND_STRING2 and  CPUID_BRAND_STRING3
  leafs.  Also display these three leafs as a single brand string.

**/
VOID
CpuidProcessorBrandString (
  VOID
  )
{
  CPUID_BRAND_STRING_DATA  Eax;
  CPUID_BRAND_STRING_DATA  Ebx;
  CPUID_BRAND_STRING_DATA  Ecx;
  CPUID_BRAND_STRING_DATA  Edx;
  //
  // Array to store brand string from 3 brand string leafs with
  // 4 32-bit brand string values per leaf and an extra value to
  // null terminate the string.
  //
  UINT32                   BrandString[3 * 4 + 1];

  if (CPUID_BRAND_STRING1 <= gMaximumExtendedFunction) {
    AsmCpuid (CPUID_BRAND_STRING1, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32);
    Print (L"CPUID_BRAND_STRING1 (Leaf %08x)\n", CPUID_BRAND_STRING1);
    Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32);
    BrandString[0] = Eax.Uint32;
    BrandString[1] = Ebx.Uint32;
    BrandString[2] = Ecx.Uint32;
    BrandString[3] = Edx.Uint32;
  }

  if (CPUID_BRAND_STRING2 <= gMaximumExtendedFunction) {
    AsmCpuid (CPUID_BRAND_STRING2, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32);
    Print (L"CPUID_BRAND_STRING2 (Leaf %08x)\n", CPUID_BRAND_STRING2);
    Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32);
    BrandString[4] = Eax.Uint32;
    BrandString[5] = Ebx.Uint32;
    BrandString[6] = Ecx.Uint32;
    BrandString[7] = Edx.Uint32;
  }

  if (CPUID_BRAND_STRING3 <= gMaximumExtendedFunction) {
    AsmCpuid (CPUID_BRAND_STRING3, &Eax.Uint32, &Ebx.Uint32, &Ecx.Uint32, &Edx.Uint32);
    Print (L"CPUID_BRAND_STRING3 (Leaf %08x)\n", CPUID_BRAND_STRING3);
    Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, Ebx.Uint32, Ecx.Uint32, Edx.Uint32);
    BrandString[8]  = Eax.Uint32;
    BrandString[9]  = Ebx.Uint32;
    BrandString[10] = Ecx.Uint32;
    BrandString[11] = Edx.Uint32;
  }

  BrandString[12] = 0;

  Print (L"Brand String = %a\n", (CHAR8 *)BrandString);
}

/**
  Display CPUID_EXTENDED_CACHE_INFO leaf.

**/
VOID
CpuidExtendedCacheInfo (
  VOID
  )
{
  CPUID_EXTENDED_CACHE_INFO_ECX  Ecx;

  if (CPUID_EXTENDED_CACHE_INFO > gMaximumExtendedFunction) {
    return;
  }

  AsmCpuid (CPUID_EXTENDED_CACHE_INFO, NULL, NULL, &Ecx.Uint32, NULL);
  Print (L"CPUID_EXTENDED_CACHE_INFO (Leaf %08x)\n", CPUID_EXTENDED_CACHE_INFO);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", 0, 0, Ecx.Uint32, 0);
  PRINT_BIT_FIELD (Ecx, CacheLineSize);
  PRINT_BIT_FIELD (Ecx, L2Associativity);
  PRINT_BIT_FIELD (Ecx, CacheSize);
}

/**
  Display CPUID_EXTENDED_TIME_STAMP_COUNTER leaf.

**/
VOID
CpuidExtendedTimeStampCounter (
  VOID
  )
{
  CPUID_EXTENDED_TIME_STAMP_COUNTER_EDX  Edx;

  if (CPUID_EXTENDED_TIME_STAMP_COUNTER > gMaximumExtendedFunction) {
    return;
  }

  AsmCpuid (CPUID_EXTENDED_TIME_STAMP_COUNTER, NULL, NULL, NULL, &Edx.Uint32);
  Print (L"CPUID_EXTENDED_TIME_STAMP_COUNTER (Leaf %08x)\n", CPUID_EXTENDED_TIME_STAMP_COUNTER);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", 0, 0, 0, Edx.Uint32);
  PRINT_BIT_FIELD (Edx, InvariantTsc);
}

/**
  Display CPUID_VIR_PHY_ADDRESS_SIZE leaf.

**/
VOID
CpuidVirPhyAddressSize (
  VOID
  )
{
  CPUID_VIR_PHY_ADDRESS_SIZE_EAX  Eax;

  if (CPUID_VIR_PHY_ADDRESS_SIZE > gMaximumExtendedFunction) {
    return;
  }

  AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &Eax.Uint32, NULL, NULL, NULL);
  Print (L"CPUID_VIR_PHY_ADDRESS_SIZE (Leaf %08x)\n", CPUID_VIR_PHY_ADDRESS_SIZE);
  Print (L"  EAX:%08x  EBX:%08x  ECX:%08x  EDX:%08x\n", Eax.Uint32, 0, 0, 0);
  PRINT_BIT_FIELD (Eax, PhysicalAddressBits);
  PRINT_BIT_FIELD (Eax, LinearAddressBits);
}

/**
  The user Entry Point for Application. The user code starts with this function
  as the real entry point for the application.

  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
  @param[in] SystemTable    A pointer to the EFI System Table.

  @retval EFI_SUCCESS       The entry point is executed successfully.
  @retval other             Some error occurs when executing this entry point.

**/
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  Print (L"UEFI CPUID Version 0.5\n");

  CpuidSignature ();
  CpuidVersionInfo ();
  CpuidCacheInfo ();
  CpuidSerialNumber ();
  CpuidCacheParams();
  CpuidMonitorMwait ();
  CpuidThermalPowerManagement ();
  CpuidStructuredExtendedFeatureFlags ();
  CpuidDirectCacheAccessInfo();
  CpuidArchitecturalPerformanceMonitoring ();
  CpuidExtendedTopology ();
  CpuidExtendedStateMainLeaf ();
  CpuidIntelRdtMonitoringEnumerationSubLeaf ();
  CpuidIntelRdtMonitoringL3CacheCapabilitySubLeaf ();
  CpuidIntelRdtAllocationMainLeaf ();
  CpuidEnumerationOfIntelSgx ();
  CpuidIntelProcessorTraceMainLeaf ();
  CpuidTimeStampCounter ();
  CpuidProcessorFrequency ();
  CpuidSocVendor ();
  CpuidExtendedFunction ();
  CpuidExtendedCpuSig ();
  CpuidProcessorBrandString ();
  CpuidExtendedCacheInfo ();
  CpuidExtendedTimeStampCounter ();
  CpuidVirPhyAddressSize ();

  return EFI_SUCCESS;
}
