/** @file
  Application to present AArch64 cpu information.

  Based on ARM DDI 0487I.a. Update this information when the
  app is updated with features from subsequent releases.

Copyright (c) 2023, Linaro Ltd. All rights reserved.<BR>

SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Library/UefiLib.h>
#include <Library/ArmLib/AArch64/AArch64Lib.h>

// We cannot assume GCC extensions to be present so let use
// binary numbers via enum.
// Arm ARM uses binary numbers so this way it is more readable.
enum {
  b0000,
  b0001,
  b0010,
  b0011,
  b0100,
  b0101,
  b0110,
  b0111,
  b1000,
  b1001,
  b1010,
  b1011,
  b1100,
  b1101,
  b1110,
  b1111
};

/**
  Print formatted table line.

  Values can be empty if only new description line is needed.

  @param[in] Field       name of system register
  @param[in] Bits        bits of system register
  @param[in] Value       value of those bits
  @param[in] Description meaning of value
**/
VOID
PrintText (
  CONST CHAR8  *Field,
  CONST CHAR8  *Bits,
  CONST CHAR8  *Value,
  CONST CHAR8  *Description
  )
{
  AsciiPrint (" %-16a | %5a | %5a | %a\n", Field, Bits, Value, Description);
}

/**
  Print formatted table line with value printed in binary.

  @param[in] Field       name of system register
  @param[in] Bits        bits of system register
  @param[in] Value       value of those bits
  @param[in] Description meaning of value
**/
VOID
PrintValues (
  CONST CHAR8  *Field,
  CONST CHAR8  *Bits,
  CONST UINT8  Value,
  CONST CHAR8  *Description
  )
{
  STATIC CONST CHAR8  Nibbles[][5] = {
    "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
    "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"
  };

  AsciiPrint (" %-16a | %5a | %5a | %a\n", Field, Bits, Nibbles[Value & 0xf], Description);
}

/**
  Print spacer for results table.
**/
VOID
PrintSpacer (
  VOID
  )
{
  AsciiPrint ("------------------|-------|-------|----------------------------------------------\n");
}

/**
  Handle ID_AA64DFR0_EL1 system register.

  @param[in] Aa64Dfr0  value of ID_AA64DFR0_EL1 system register
**/
VOID
HandleAa64Dfr0 (
  CONST UINT64  Aa64Dfr0
  )
{
  UINT64              Value;
  STATIC CONST CHAR8  RegName[] = "ID_AA64DFR0";
  CONST CHAR8         *Description;
  CONST CHAR8         *Bits;

  Bits  = "3:0 ";
  Value = (Aa64Dfr0 >>  4) & 0xf;
  switch (Value) {
    case b0110:
      Description = "Armv8 debug architecture";
      break;
    case b0111:
      Description = "Armv8 debug architecture with VHE";
      break;
    case b1000:
      Description = "FEAT_Debugv8p2 implemented.";
      break;
    case b1001:
      Description = "FEAT_Debugv8p4 implemented.";
      break;
    case b1010:
      Description = "FEAT_Debugv8p8 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "7:4 ";
  Value = (Aa64Dfr0 >>  4) & 0xf;
  switch (Value) {
    case b0000:
      Description = "Trace unit System registers not implemented.";
      break;
    case b0001:
      Description = "Trace unit System registers implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "11:8 ";
  Value = (Aa64Dfr0 >>  8) & 0xf;
  switch (Value) {
    case b0000:
      Description = "Performance Monitors Extension not implemented.";
      break;
    case b0001:
      Description = "FEAT_PMUv3 implemented.";
      break;
    case b0100:
      Description = "FEAT_PMUv3p1 implemented.";
      break;
    case b0101:
      Description = "FEAT_PMUv3p4 implemented.";
      break;
    case b0110:
      Description = "FEAT_PMUv3p5 implemented.";
      break;
    case b0111:
      Description = "FEAT_PMUv3p7 implemented.";
      break;
    case b1000:
      Description = "FEAT_PMUv3p8 implemented.";
      break;
    case b1111:
      Description = "IMPLEMENTATION DEFINED form of performance monitors supported.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "15:12";
  Value = (Aa64Dfr0 >> 12) & 0xf;
  switch (Value) {
    case b0000:
      Description = "reserved";
      break;
    default:
      Description = "Number of breakpoints, minus 1.";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  // 19:16 reserved

  Bits  = "23:20";
  Value = (Aa64Dfr0 >> 20) & 0xf;
  switch (Value) {
    case b0000:
      Description = "reserved";
      break;
    default:
      Description = "Number of watchpoints, minus 1.";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  // 27:24 reserved

  Bits  = "31:28";
  Value = (Aa64Dfr0 >> 28) & 0xf;
  switch (Value) {
    default:
      Description = "Number of breakpoints that are context-aware, minus 1.";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "35:32";
  Value = (Aa64Dfr0 >> 32) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_SPE not implemented.";
      break;
    case b0001:
      Description = "FEAT_SPE implemented.";
      break;
    case b0010:
      Description = "FEAT_SPEv1p1 implemented.";
      break;
    case b0011:
      Description = "FEAT_SPEv1p2 implemented.";
      break;
    case b0100:
      Description = "FEAT_SPEv1p3 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "39:36";
  Value = (Aa64Dfr0 >> 36) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_DoubleLock implemented.";
      break;
    case b1111:
      Description = "FEAT_DoubleLock not implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "43:40";
  Value = (Aa64Dfr0 >> 40) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_TRF not implemented.";
      break;
    case b0001:
      Description = "FEAT_TRF implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "47:44";
  Value = (Aa64Dfr0 >> 44) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_TRBE not implemented.";
      break;
    case b0001:
      Description = "FEAT_TRBE implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "51:48";
  Value = (Aa64Dfr0 >> 48) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_MTPMU not implemented.";
      break;
    case b0001:
      Description = "FEAT_MTPMU and FEAT_PMUv3 implemented.";
      break;
    case b1111:
      Description = "FEAT_MTPMU not implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "55:52";
  Value = (Aa64Dfr0 >> 52) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_BRBE not implemented.";
      break;
    case b0001:
      Description = "FEAT_BRBE implemented.";
      break;
    case b0010:
      Description = "FEAT_BRBEv1p1 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  // 59:56 reserved

  Bits  = "63:60";
  Value = (Aa64Dfr0 >> 60) & 0xf;
  switch (Value) {
    case b0000:
      Description = "Setting MDCR_EL2.HPMN to zero has CONSTRAINED UNPREDICTABLE behavior.";
      break;
    case b0001:
      Description = "FEAT_HPMN0 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);
}

/**
  Handle ID_AA64ISAR0_EL1 system register.

  @param[in] Aa64Isar0  value of ID_AA64ISAR0_EL1 system register
**/
VOID
HandleAa64Isar0 (
  CONST UINT64  Aa64Isar0
  )
{
  UINT64              Value;
  STATIC CONST CHAR8  RegName[] = "ID_AA64ISAR0";
  CONST CHAR8         *Description;
  CONST CHAR8         *Bits;

  // 3:0 reserved

  Bits  = "7:4 ";
  Value = (Aa64Isar0 >>  4) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_AES, FEAT_PMULL not implemented.";
      break;
    case b0001:
      Description = "FEAT_AES implemented.";
      break;
    case b0010:
      Description = "FEAT_AES and FEAT_PMULL implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "11:8 ";
  Value = (Aa64Isar0 >>  8) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_SHA1 not implemented.";
      break;
    case b0001:
      Description = "FEAT_SHA1 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "15:12";
  Value = (Aa64Isar0 >> 12) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_SHA256, FEAT_SHA512 not implemented.";
      break;
    case b0001:
      Description = "FEAT_SHA256 implemented.";
      break;
    case b0010:
      Description = "FEAT_SHA512 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "19:16";
  Value = (Aa64Isar0 >> 16) & 0xf;
  switch (Value) {
    case b0000:
      Description = "CRC32 not implemented.";
      break;
    case b0001:
      Description = "CRC32 instructions implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "23:20";
  Value = (Aa64Isar0 >> 20) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_LSE not implemented.";
      break;
    case b0010:
      Description = "FEAT_LSE implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "27:24";
  Value = (Aa64Isar0 >> 24) & 0xf;
  switch (Value) {
    case b0000:
      Description = "TME instructions not implemented.";
      break;
    case b0001:
      Description = "TME instructions implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "31:28";
  Value = (Aa64Isar0 >> 28) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_RDM not implemented.";
      break;
    case b0001:
      Description = "FEAT_RDM implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "35:32";
  Value = (Aa64Isar0 >> 32) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_SHA3 not implemented.";
      break;
    case b0001:
      Description = "FEAT_SHA3 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "39:36";
  Value = (Aa64Isar0 >> 36) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_SM3 not implemented.";
      break;
    case b0001:
      Description = "FEAT_SM3 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "43:40";
  Value = (Aa64Isar0 >> 40) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_SM4 not implemented.";
      break;
    case b0001:
      Description = "FEAT_SM4 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "47:44";
  Value = (Aa64Isar0 >> 44) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_DotProd not implemented.";
      break;
    case b0001:
      Description = "FEAT_DotProd implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "51:48";
  Value = (Aa64Isar0 >> 48) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_FHM not implemented.";
      break;
    case b0001:
      Description = "FEAT_FHM implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "55:52";
  Value = (Aa64Isar0 >> 52) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_FlagM/FEAT_FlagM2 not implemented.";
      break;
    case b0001:
      Description = "FEAT_FlagM implemented.";
      break;
    case b0010:
      Description = "FEAT_FlagM2 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "59:56";
  Value = (Aa64Isar0 >> 56) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_TLBIOS/FEAT_TLBIRANGE not implemented.";
      break;
    case b0001:
      Description = "FEAT_TLBIOS implemented.";
      break;
    case b0010:
      Description = "FEAT_TLBIRANGE implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "63:60";
  Value = (Aa64Isar0 >> 60) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_RNG not implemented.";
      break;
    case b0001:
      Description = "FEAT_RNG implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);
}

/**
  Handle ID_AA64ISAR1_EL1 system register.

  @param[in] Aa64Isar1  value of ID_AA64ISAR1_EL1 system register
**/
VOID
HandleAa64Isar1 (
  CONST UINT64  Aa64Isar1
  )
{
  UINT64              Value;
  STATIC CONST CHAR8  RegName[] = "ID_AA64ISAR1";
  CONST CHAR8         *Description;
  CONST CHAR8         *Bits;

  Bits  = "3:0 ";
  Value = (Aa64Isar1 >>  4) & 0xf;
  switch (Value) {
    case b0000:
      Description = "DC CVAP not implemented.";
      break;
    case b0001:
      Description = "FEAT_DPB implemented.";
      break;
    case b0010:
      Description = "FEAT_DPB2 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "7:4 ";
  Value = (Aa64Isar1 >>  4) & 0xf;
  switch (Value) {
    case b0000:
      Description = "Address Authentication  (APA) not implemented.";
      break;
    case b0001:
      Description = "FEAT_PAuth implemented.";
      break;
    case b0010:
      Description = "FEAT_EPAC implemented.";
      break;
    case b0011:
      Description = "FEAT_PAuth2 implemented.";
      break;
    case b0100:
      Description = "FEAT_FPAC implemented.";
      break;
    case b0101:
      Description = "FEAT_FPACCOMBINE implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);
  if (Value > 0) {
    PrintText ("", "", "", "FEAT_PACQARMA5 implemented.");
  }

  Bits  = "11:8 ";
  Value = (Aa64Isar1 >>  8) & 0xf;
  switch (Value) {
    case b0000:
      Description = "Address Authentication  (API) not implemented.";
      break;
    case b0001:
      Description = "FEAT_PAuth implemented.";
      break;
    case b0010:
      Description = "FEAT_EPAC implemented.";
      break;
    case b0011:
      Description = "FEAT_PAuth2 implemented.";
      break;
    case b0100:
      Description = "FEAT_FPAC implemented.";
      break;
    case b0101:
      Description = "FEAT_FPACCOMBINE implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);
  if (Value > 0) {
    PrintText ("", "", "", "FEAT_PACIMP implemented.");
  }

  Bits  = "15:12";
  Value = (Aa64Isar1 >> 12) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_JSCVT not implemented.";
      break;
    case b0001:
      Description = "FEAT_JSCVT implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "19:16";
  Value = (Aa64Isar1 >> 16) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_FCMA not implemented.";
      break;
    case b0001:
      Description = "FEAT_FCMA implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "23:20";
  Value = (Aa64Isar1 >> 20) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_LRCPC (2) not implemented.";
      break;
    case b0001:
      Description = "FEAT_LRCPC implemented.";
      break;
    case b0010:
      Description = "FEAT_LRCPC2 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "27:24";
  Value = (Aa64Isar1 >> 24) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_PACQARMA5 not implemented.";
      break;
    case b0001:
      Description = "FEAT_PACQARMA5 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "31:28";
  Value = (Aa64Isar1 >> 28) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_PACIMP not implemented.";
      break;
    case b0001:
      Description = "FEAT_PACIMP implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "35:32";
  Value = (Aa64Isar1 >> 32) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_FRINTTS not implemented.";
      break;
    case b0001:
      Description = "FEAT_FRINTTS implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "39:36";
  Value = (Aa64Isar1 >> 36) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_SB not implemented.";
      break;
    case b0001:
      Description = "FEAT_SB implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "43:40";
  Value = (Aa64Isar1 >> 40) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_SPECRES not implemented.";
      break;
    case b0001:
      Description = "FEAT_SPECRES implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "47:44";
  Value = (Aa64Isar1 >> 44) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_BF16 not implemented.";
      break;
    case b0001:
      Description = "FEAT_BF16 implemented.";
      break;
    case b0010:
      Description = "FEAT_EBF16 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "51:48";
  Value = (Aa64Isar1 >> 48) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_DGH not implemented.";
      break;
    case b0001:
      Description = "FEAT_DGH implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "55:52";
  Value = (Aa64Isar1 >> 52) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_I8MM not implemented.";
      break;
    case b0001:
      Description = "FEAT_I8MM implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "59:56";
  Value = (Aa64Isar1 >> 56) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_XS not implemented.";
      break;
    case b0001:
      Description = "FEAT_XS implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "63:60";
  Value = (Aa64Isar1 >> 60) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_LS64 not implemented.";
      break;
    case b0001:
      Description = "FEAT_LS64 implemented.";
      break;
    case b0010:
      Description = "FEAT_LS64_V implemented.";
      break;
    case b0011:
      Description = "FEAT_LS64_ACCDATA implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);
}

/**
  Handle ID_AA64ISAR2_EL1 system register.

  @param[in] Aa64Isar2  value of ID_AA64ISAR2_EL1 system register
**/
VOID
HandleAa64Isar2 (
  CONST UINT64  Aa64Isar2
  )
{
  UINT64              Value;
  STATIC CONST CHAR8  RegName[] = "ID_AA64ISAR2";
  CONST CHAR8         *Description;
  CONST CHAR8         *Bits;

  Bits  = "3:0 ";
  Value = (Aa64Isar2 >>  4) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_WFxT not implemented.";
      break;
    case b0010:
      Description = "FEAT_WFxT implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "7:4 ";
  Value = (Aa64Isar2 >>  4) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_RPRES not implemented.";
      break;
    case b0001:
      Description = "FEAT_RPRES implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "11:8 ";
  Value = (Aa64Isar2 >>  8) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_PACQARMA3 not implemented.";
      break;
    case b0001:
      Description = "FEAT_PACQARMA3 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "15:12";
  Value = (Aa64Isar2 >> 12) & 0xf;
  switch (Value) {
    case b0000:
      Description = "Address Authentication  (APA3) not implemented.";
      break;
    case b0001:
      Description = "FEAT_PAuth implemented.";
      break;
    case b0010:
      Description = "FEAT_EPAC implemented.";
      break;
    case b0011:
      Description = "FEAT_PAuth2 implemented.";
      break;
    case b0100:
      Description = "FEAT_FPAC implemented.";
      break;
    case b0101:
      Description = "FEAT_FPACCOMBINE implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "19:16";
  Value = (Aa64Isar2 >> 16) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_MOPS not implemented.";
      break;
    case b0001:
      Description = "FEAT_MOPS implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "23:20";
  Value = (Aa64Isar2 >> 20) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_HBC not implemented.";
      break;
    case b0001:
      Description = "FEAT_HBC implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "27:24";
  Value = (Aa64Isar2 >> 24) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_CONSTPACFIELD not implemented.";
      break;
    case b0001:
      Description = "FEAT_CONSTPACFIELD implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  // 63:28 reserved
}

/**
  Handle ID_AA64MMFR0_EL1 system register.

  @param[in] Aa64Mmfr0  value of ID_AA64MMFR0_EL1 system register
**/
VOID
HandleAa64Mmfr0 (
  CONST UINT64  Aa64Mmfr0
  )
{
  UINT64              Value;
  STATIC CONST CHAR8  RegName[] = "ID_AA64MMFR0";
  CONST CHAR8         *Description;
  CONST CHAR8         *Bits;

  Bits  = "3:0 ";
  Value = Aa64Mmfr0 & 0xf;
  switch (Value) {
    case b0000:
      Description = "32 Bits  (4GB) of physical address range supported.";
      break;
    case b0001:
      Description = "36 Bits  (64GB) of physical address range supported.";
      break;
    case b0010:
      Description = "40 Bits  (1TB) of physical address range supported.";
      break;
    case b0011:
      Description = "42 Bits  (4TB) of physical address range supported.";
      break;
    case b0100:
      Description = "44 Bits  (16TB) of physical address range supported.";
      break;
    case b0101:
      Description = "48 Bits  (256TB) of physical address range supported.";
      break;
    case b0110:
      Description = "52 Bits  (4PB) of physical address range supported.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);
  if (Value == b0110) {
    PrintText ("", "", "", "FEAT_LPA implemented.");
  }

  Bits  = "7:4 ";
  Value = (Aa64Mmfr0 >>  4) & 0xf;
  switch (Value) {
    case b0000:
      Description = "ASID: 8 Bits";
      break;
    case b0010:
      Description = "ASID: 16 Bits";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "11:8 ";
  Value = (Aa64Mmfr0 >>  8) & 0xf;
  switch (Value) {
    case b0000:
      Description = "No mixed-endian support.";
      break;
    case b0001:
      Description = "Mixed-endian support.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  // If mixed-endian is present, check whether supported at EL0
  if (((Aa64Mmfr0 >>  8) & 0xf) != b0000 ) {
    if (((Aa64Mmfr0 >> 16) & 0xf) == b0000 ) {
      PrintValues ("ID_AA64MMFR0", "19:16", b0000, "No mixed-endian support at EL0.");
    }

    if (((Aa64Mmfr0 >> 16) & 0xf) == b0001 ) {
      PrintValues ("ID_AA64MMFR0", "19:16", b0001, "Mixed-endian support at EL0.");
    }
  }

  Bits  = "15:12";
  Value = (Aa64Mmfr0 >> 12) & 0xf;
  switch (Value) {
    case b0000:
      Description = "No support for a distinction between Secure and Non-Secure Memory.";
      break;
    case b0001:
      Description = "Supports a distinction between Secure and Non-Secure Memory.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "31:28";
  Value = (Aa64Mmfr0 >> 28) & 0xf;
  switch (Value) {
    case b0000:
      Description = " 4KB granule supported.";
      break;
    case b1111:
      Description = " 4KB granule not supported.";
      break;
    case b0001:
      Description = " 4KB granule supported for 52-bit address.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "43:40";
  Value = (Aa64Mmfr0 >> 40) & 0xf;
  switch (Value) {
    case b0001:
      Description = " 4KB granule not supported at stage 2.";
      break;
    case b0010:
      Description = " 4KB granule supported at stage 2.";
      break;
    case b0011:
      Description = " 4KB granule supported at stage 2 for 52-bit address.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "23:20";
  Value = (Aa64Mmfr0 >> 20) & 0xf;
  switch (Value) {
    case b0000:
      Description = "16KB granule not supported.";
      break;
    case b0001:
      Description = "16KB granule supported.";
      break;
    case b0010:
      Description = "16KB granule supported for 52-bit address.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "35:32";
  Value = (Aa64Mmfr0 >> 32) & 0xf;
  switch (Value) {
    case b0001:
      Description = "16KB granule not supported at stage 2.";
      break;
    case b0010:
      Description = "16KB granule supported at stage 2.";
      break;
    case b0011:
      Description = "16KB granule supported at stage 2 for 52-bit address.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "27:24";
  Value = (Aa64Mmfr0 >> 24) & 0xf;
  switch (Value) {
    case b0000:
      Description = "64KB granule supported.";
      break;
    case b1111:
      Description = "64KB granule not supported.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "39:36";
  Value = (Aa64Mmfr0 >> 36) & 0xf;
  switch (Value) {
    case b0001:
      Description = "64KB granule not supported at stage 2.";
      break;
    case b0010:
      Description = "64KB granule supported at stage 2.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "47:44";
  Value = (Aa64Mmfr0 >> 44) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_ExS not implemented.";
      break;
    case b0001:
      Description = "FEAT_ExS implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  // 55:48 reserved

  Bits  = "59:56";
  Value = (Aa64Mmfr0 >> 56) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_FGT not implemented.";
      break;
    case b0001:
      Description = "FEAT_FGT implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "63:60";
  Value = (Aa64Mmfr0 >> 60) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_ECV not implemented.";
      break;
    case b0001:
      Description = "FEAT_ECV implemented.";
      break;
    case b0010:
      Description = "FEAT_ECV implemented with extras.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);
}

/**
  Handle ID_AA64MMFR1_EL1 system register.

  @param[in] Aa64Mmfr1  value of ID_AA64MMFR1_EL1 system register
  @param[in] Aa64Pfr0,  value of ID_AA64PFR0_EL1 system register
**/
VOID
HandleAa64Mmfr1 (
  CONST UINT64  Aa64Mmfr1,
  CONST UINT64  Aa64Pfr0
  )
{
  UINT64              Value;
  STATIC CONST CHAR8  RegName[] = "ID_AA64MMFR1";
  CONST CHAR8         *Description;
  CONST CHAR8         *Bits;

  Bits  = "3:0 ";
  Value = Aa64Mmfr1 & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_HAFDBS not implemented.";
      break;
    case b0001:
      Description = "FEAT_HAFDBS implemented without dirty status support.";
      break;
    case b0010:
      Description = "FEAT_HAFDBS implemented with dirty status support.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "7:4 ";
  Value = (Aa64Mmfr1 >>  4) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_VMID16 not implemented.";
      break;
    case b0010:
      Description = "FEAT_VMID16 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "11:8 ";
  Value = (Aa64Mmfr1 >>  8) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_VHE not implemented.";
      break;
    case b0001:
      Description = "FEAT_VHE implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "15:12";
  Value = (Aa64Mmfr1 >> 12) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_HPDS not implemented.";
      break;
    case b0001:
      Description = "FEAT_HPDS implemented.";
      break;
    case b0010:
      Description = "FEAT_HPDS2 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "19:16";
  Value = (Aa64Mmfr1 >> 16) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_LOR not implemented.";
      break;
    case b0001:
      Description = "FEAT_LOR implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "23:20";
  Value = (Aa64Mmfr1 >> 20) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_PAN not implemented.";
      break;
    case b0001:
      Description = "FEAT_PAN implemented.";
      break;
    case b0010:
      Description = "FEAT_PAN2 implemented.";
      break;
    case b0011:
      Description = "FEAT_PAN3 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  // when FEAT_RAS implemented
  if ((((Aa64Pfr0 >> 28) & 0xf) == b0001) ||
      (((Aa64Pfr0 >> 28) & 0xf) == b0010))
  {
    if (((Aa64Mmfr1 >> 24) & 0xf) == b0000 ) {
      PrintValues ("ID_AA64MMFR1", "27:24", b0000, "The PE never generates an SError interrupt due to");
      PrintText ("", "", "", "an External abort on a speculative read.");
    }

    if (((Aa64Mmfr1 >> 24) & 0xf) == b0001 ) {
      PrintValues ("ID_AA64MMFR1", "27:24", b0001, "The PE might generate an SError interrupt due to");
      PrintText ("", "", "", "an External abort on a speculative read.");
    }
  }

  Bits  = "31:28";
  Value = (Aa64Mmfr1 >> 28) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_XNX not implemented.";
      break;
    case b0001:
      Description = "FEAT_XNX implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "35:32";
  Value = (Aa64Mmfr1 >> 32) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_TWED not implemented.";
      break;
    case b0001:
      Description = "FEAT_TWED implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "39:36";
  Value = (Aa64Mmfr1 >> 36) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_ETS not implemented.";
      break;
    case b0001:
      Description = "FEAT_ETS implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "43:40";
  Value = (Aa64Mmfr1 >> 40) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_HCX not implemented.";
      break;
    case b0001:
      Description = "FEAT_HCX implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "47:44";
  Value = (Aa64Mmfr1 >> 44) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_AFP not implemented.";
      break;
    case b0001:
      Description = "FEAT_AFP implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "51:48";
  Value = (Aa64Mmfr1 >> 48) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_nTLBPA not implemented.";
      break;
    case b0001:
      Description = "FEAT_nTLBPA implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "55:52";
  Value = (Aa64Mmfr1 >> 52) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_TIDCP1 not implemented";
      break;
    case b0001:
      Description = "FEAT_TIDCP1 implemented";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "59:56";
  Value = (Aa64Mmfr1 >> 56) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_CMOW not implemented.";
      break;
    case b0001:
      Description = "FEAT_CMOW implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  // 63:60 reserved
}

/**
  Handle ID_AA64MMFR2_EL1 system register.

  @param[in] Aa64Mmfr2  value of ID_AA64MMFR2_EL1 system register
**/
VOID
HandleAa64Mmfr2 (
  CONST UINT64  Aa64Mmfr2
  )
{
  UINT64              Value;
  STATIC CONST CHAR8  RegName[] = "ID_AA64MMFR2";
  CONST CHAR8         *Description;
  CONST CHAR8         *Bits;

  Bits  = "3:0 ";
  Value = (Aa64Mmfr2)       & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_TTCNP not implemented.";
      break;
    case b0001:
      Description = "FEAT_TTCNP implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "7:4 ";
  Value = (Aa64Mmfr2 >>  4) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_UAO not implemented.";
      break;
    case b0001:
      Description = "FEAT_UAO implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "11:8 ";
  Value = (Aa64Mmfr2 >>  8) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_LSMAOC not implemented.";
      break;
    case b0001:
      Description = "FEAT_LSMAOC implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "15:12";
  Value = (Aa64Mmfr2 >> 12) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_IESB not implemented.";
      break;
    case b0001:
      Description = "FEAT_IESB implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "19:16";
  Value = (Aa64Mmfr2 >> 16) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_LVA not implemented.";
      break;
    case b0001:
      Description = "FEAT_LVA implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "23:20";
  Value = (Aa64Mmfr2 >> 20) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_CCIDX not implemented.";
      break;
    case b0001:
      Description = "FEAT_CCIDX implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "27:24";
  Value = (Aa64Mmfr2 >> 24) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_NV not implemented.";
      break;
    case b0001:
      Description = "FEAT_NV implemented.";
      break;
    case b0010:
      Description = "FEAT_NV2 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "31:28";
  Value = (Aa64Mmfr2 >> 28) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_TTST not implemented.";
      break;
    case b0001:
      Description = "FEAT_TTST implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "35:32";
  Value = (Aa64Mmfr2 >> 32) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_LSE2 not implemented.";
      break;
    case b0001:
      Description = "FEAT_LSE2 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "39:36";
  Value = (Aa64Mmfr2 >> 36) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_IDST not implemented.";
      break;
    case b0001:
      Description = "FEAT_IDST implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "43:40";
  Value = (Aa64Mmfr2 >> 40) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_S2FWB not implemented.";
      break;
    case b0001:
      Description = "FEAT_S2FWB implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  // 47:44 reserved

  Bits  = "51:48";
  Value = (Aa64Mmfr2 >> 48) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_TTL not implemented.";
      break;
    case b0001:
      Description = "FEAT_TTL implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "55:52";
  Value = (Aa64Mmfr2 >> 52) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_BBM: Level 0 support for changing block size is supported.";
      break;
    case b0001:
      Description = "FEAT_BBM: Level 1 support for changing block size is supported.";
      break;
    case b0010:
      Description = "FEAT_BBM: Level 2 support for changing block size is supported.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "59:56";
  Value = (Aa64Mmfr2 >> 56) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_EVT not implemented.";
      break;
    case b0001:
      Description = "FEAT_EVT: HCR_EL2.{TOCU, TICAB, TID4} traps are supported.";
      break;
    case b0010:
      Description = "FEAT_EVT: HCR_EL2.{TTLBOS, TTLSBIS, TOCU, TICAB, TID4} traps are supported.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "63:60";
  Value = (Aa64Mmfr2 >> 60) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_E0PD not implemented.";
      break;
    case b0001:
      Description = "FEAT_E0PD implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);
}

/**
  Handle ID_AA64PFR0_EL1 system register.

  @param[in] Aa64Pfr0,  value of ID_AA64PFR0_EL1 system register
  @param[in] Aa64Pfr1,  value of ID_AA64PFR1_EL1 system register
**/
VOID
HandleAa64Pfr0 (
  CONST UINT64  Aa64Pfr0,
  CONST UINT64  Aa64Pfr1
  )
{
  UINT64              Value;
  STATIC CONST CHAR8  RegName[] = "ID_AA64PFR0";
  CONST CHAR8         *Description;
  CONST CHAR8         *Bits;

  Bits  = "3:0 ";
  Value = (Aa64Pfr0)       & 0xf;
  switch (Value) {
    case b0001:
      Description = "EL0 in AArch64 only";
      break;
    case b0010:
      Description = "EL0 in AArch64 and AArch32";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "7:4 ";
  Value = (Aa64Pfr0 >>  4) & 0xf;
  switch (Value) {
    case b0001:
      Description = "EL1 in AArch64 only";
      break;
    case b0010:
      Description = "EL1 in AArch64 and AArch32";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "11:8 ";
  Value = (Aa64Pfr0 >>  8) & 0xf;
  switch (Value) {
    case b0000:
      Description = "EL2 not implemented.";
      break;
    case b0001:
      Description = "EL2 in AArch64 only";
      break;
    case b0010:
      Description = "EL2 in AArch64 and AArch32";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "15:12";
  Value = (Aa64Pfr0 >> 12) & 0xf;
  switch (Value) {
    case b0000:
      Description = "EL3 not implemented.";
      break;
    case b0001:
      Description = "EL3 in AArch64 only";
      break;
    case b0010:
      Description = "EL3 in AArch64 and AArch32";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "19:16";
  Value = (Aa64Pfr0 >> 16) & 0xf;
  switch (Value) {
    case b0000:
      Description = "Floating-point implemented.";
      break;
    case b0001:
      Description = "Floating-point with half-precision support  (FEAT_FP16).";
      break;
    case b1111:
      Description = "Floating-point not implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "23:20";
  Value = (Aa64Pfr0 >> 20) & 0xf;
  switch (Value) {
    case b0000:
      Description = "Advanced SIMD implemented.";
      break;
    case b0001:
      Description = "Advanced SIMD with half precision support  (FEAT_FP16).";
      break;
    case b1111:
      Description = "Advanced SIMD not implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "27:24";
  Value = (Aa64Pfr0 >> 24) & 0xf;
  switch (Value) {
    case b0000:
      Description = "System registers of GIC CPU not implemented.";
      break;
    case b0001:
      Description = "System registers to versions 3.0/4.0 of GIC CPU implemented.";
      break;
    case b0011:
      Description = "System registers to versions 4.1 of GIC CPU implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "31:28";
  Value = (Aa64Pfr0 >> 28) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_RAS not implemented.";
      break;
    case b0001:
      Description = "FEAT_RAS implemented.";
      break;
    case b0010:
      Description = "FEAT_RASv1p1 implemented.";
      // b0010 FEAT_RASv1p1 implemented and, if EL3 is implemented, FEAT_DoubleFault implemented.
      if ((((Aa64Pfr0 >> 12) & 0xf) == b0001) ||
          (((Aa64Pfr0 >> 12) & 0xf) == b0010))
      {
        Description = "FEAT_RASv1p1 implemented. FEAT_DoubleFault implemented.";
      }

      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);
  if (Value == b0001) {
    if (((Aa64Pfr1 >> 12) & 0xf) == b0001 ) {
      PrintValues ("ID_AA64PRF1", "15:12", b0001, "FEAT_RASv1p1 implemented.");
    }
  }

  Bits  = "35:32";
  Value = (Aa64Pfr0 >> 32) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_SVE not implemented.";
      break;
    case b0001:
      Description = "FEAT_SVE implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "39:36";
  Value = (Aa64Pfr0 >> 36) & 0xf;
  switch (Value) {
    case b0000:
      Description = "Secure EL2 not implemented.";
      break;
    case b0001:
      Description = "Secure EL2 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "43:40";
  Value = (Aa64Pfr0 >> 40) & 0xf;
  switch (Value) {
    case b0000:
      if (((Aa64Pfr1 >> 16) & 0xf) == b0000 ) {
        Description = "FEAT_MPAM not implemented.";
      }

      if (((Aa64Pfr1 >> 16) & 0xf) == b0001 ) {
        Description = "FEAT_MPAM v0.1 implemented.";
      }

      break;
    case b0001:
      if (((Aa64Pfr1 >> 16) & 0xf) == b0000 ) {
        Description = "FEAT_MPAM v1.0 implemented.";
      }

      if (((Aa64Pfr1 >> 16) & 0xf) == b0001 ) {
        Description = "FEAT_MPAM v1.1 implemented.";
      }

      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "47:44";
  Value = (Aa64Pfr0 >> 44) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_AMU not implemented.";
      break;
    case b0001:
      Description = "FEAT_AMUv1 implemented.";
      break;
    case b0010:
      Description = "FEAT_AMUv1p1 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "51:48";
  Value = (Aa64Pfr0 >> 48) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_DIT not implemented.";
      break;
    case b0001:
      Description = "FEAT_DIT implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "55:52";
  Value = (Aa64Pfr0 >> 52) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_RME not implemented";
      break;
    case b0001:
      Description = "FEAT_RME implemented";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "59:56";
  Value = (Aa64Pfr0 >> 56) & 0xf;
  switch (Value) {
    case b0000:
      Description = "Not disclosed whether FEAT_CSV2 is implemented.";
      break;
    case b0001:
      Description = "FEAT_CSV2 implemented.";
      break;
    case b0010:
      Description = "FEAT_CSV2_2 implemented.";
      break;
    case b0011:
      Description = "FEAT_CSV2_3 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);
  if (Value == b0001) {
    if (((Aa64Pfr1 >> 32) & 0xf) == b0001 ) {
      PrintValues ("ID_AA64PRF1", "35:32", b0001, "FEAT_CSV2_1p1 implemented.");
    }

    if (((Aa64Pfr1 >> 32) & 0xf) == b0010 ) {
      PrintValues ("ID_AA64PRF1", "35:32", b0010, "FEAT_CSV2_1p2 implemented.");
    }
  }

  Bits  = "63:60";
  Value = (Aa64Pfr0 >> 60) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_CSV3 not implemented.";
      break;
    case b0001:
      Description = "FEAT_CSV3 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);
}

/**
  Handle ID_AA64PFR1_EL1 system register.

  @param[in] Aa64Pfr1  value of ID_AA64PFR1_EL1 system register
**/
VOID
HandleAa64Pfr1 (
  CONST UINT64  Aa64Pfr1
  )
{
  UINT64              Value;
  STATIC CONST CHAR8  RegName[] = "ID_AA64PFR1";
  CONST CHAR8         *Description;
  CONST CHAR8         *Bits;

  Bits  = "3:0 ";
  Value = Aa64Pfr1 & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_BTI not implemented.";
      break;
    case b0001:
      Description = "FEAT_BTI implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "7:4 ";
  Value = (Aa64Pfr1 >>  4) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_SSBS not implemented.";
      break;
    case b0001:
      Description = "FEAT_SSBS implemented.";
      break;
    case b0010:
      Description = "FEAT_SSBS2 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "11:8 ";
  Value = (Aa64Pfr1 >>  8) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_MTE not implemented.";
      break;
    case b0001:
      Description = "FEAT_MTE implemented.";
      break;
    case b0010:
      Description = "FEAT_MTE2 implemented.";
      break;
    case b0011:
      Description = "FEAT_MTE3 implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  // 15:12 is RAS_frac
  // 19:16 is MPAM_frac
  // 23:20 is reserved

  Bits  = "27:24";
  Value = (Aa64Pfr1 >> 24) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_SME not implemented.";
      break;
    case b0001:
      Description = "FEAT_SME implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  Bits  = "31:28";
  Value = (Aa64Pfr1 >> 28) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_RNG_TRAP not implemented.";
      break;
    case b0001:
      Description = "FEAT_RNG_TRAP implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  // 35:32 is CSV2_frac

  Bits  = "39:36";
  Value = (Aa64Pfr1 >> 36) & 0xf;
  switch (Value) {
    case b0000:
      Description = "FEAT_NMI not implemented.";
      break;
    case b0001:
      Description = "FEAT_NMI implemented.";
      break;
    default:
      Description = "unknown";
      break;
  }

  PrintValues (RegName, Bits, Value, Description);

  // 63:40 are reserved
}

/**
  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
  )
{
  UINT64  Aa64Dfr0;
  UINT64  Aa64Isar0;
  UINT64  Aa64Isar1;
  UINT64  Aa64Isar2;
  UINT64  Aa64Mmfr0;
  UINT64  Aa64Mmfr1;
  UINT64  Aa64Mmfr2;
  UINT64  Aa64Pfr0;
  UINT64  Aa64Pfr1;

  Aa64Dfr0  = ArmReadIdAA64Dfr0 ();
  Aa64Isar0 = ArmReadIdAA64Isar0 ();
  Aa64Isar1 = ArmReadIdAA64Isar1 ();
  Aa64Isar2 = ArmReadIdAA64Isar2 ();
  Aa64Mmfr0 = ArmReadIdAA64Mmfr0 ();
  Aa64Mmfr1 = ArmReadIdAA64Mmfr1 ();
  Aa64Mmfr2 = ArmReadIdAA64Mmfr2 ();
  Aa64Pfr0  = ArmReadIdAA64Pfr0 ();
  Aa64Pfr1  = ArmReadIdAA64Pfr1 ();

  AsciiPrint ("ID_AA64MMFR0_EL1 = 0x%016lx\n", Aa64Mmfr0);
  AsciiPrint ("ID_AA64MMFR1_EL1 = 0x%016lx\n", Aa64Mmfr1);
  AsciiPrint ("ID_AA64MMFR2_EL1 = 0x%016lx\n", Aa64Mmfr2);
  AsciiPrint ("ID_AA64PFR0_EL1  = 0x%016lx\n", Aa64Pfr0);
  AsciiPrint ("ID_AA64PFR1_EL1  = 0x%016lx\n", Aa64Pfr1);
  AsciiPrint ("ID_AA64ISAR0_EL1 = 0x%016lx\n", Aa64Isar0);
  AsciiPrint ("ID_AA64ISAR1_EL1 = 0x%016lx\n", Aa64Isar1);
  AsciiPrint ("ID_AA64ISAR2_EL1 = 0x%016lx\n", Aa64Isar2);
  AsciiPrint ("ID_AA64DFR0_EL1  = 0x%016lx\n", Aa64Dfr0);
  AsciiPrint ("\n");

  PrintText ("Register", "Bits", "Value", "Feature");
  PrintSpacer ();

  HandleAa64Mmfr0 (Aa64Mmfr0);
  PrintSpacer ();
  HandleAa64Mmfr1 (Aa64Mmfr1, Aa64Pfr0);
  PrintSpacer ();
  HandleAa64Mmfr2 (Aa64Mmfr2);

  PrintSpacer ();
  HandleAa64Pfr0 (Aa64Pfr0, Aa64Pfr1);
  PrintSpacer ();
  HandleAa64Pfr1 (Aa64Pfr1);

  PrintSpacer ();
  HandleAa64Isar0 (Aa64Isar0);
  PrintSpacer ();
  HandleAa64Isar1 (Aa64Isar1);
  PrintSpacer ();
  HandleAa64Isar2 (Aa64Isar2);

  PrintSpacer ();
  HandleAa64Dfr0 (Aa64Dfr0);

  return EFI_SUCCESS;
}
