Step to UEFI (120)UEFI 下控制USB键盘 LED

本文介绍如何在Shell 下实现控制 USB Keyboard 上面的 LED。
代码流程如下: 首先是找到USB键盘,然后获得它上加载的 USB IO Protocol,通过发送 set report request 的放置通知它当前 LED 应该设置的状态即可。
代码有点长如果你是第一次接触,建议先研究之前的获得 USB VID PID的例子.

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include  <Protocol/UsbIo.h>

#include "EfiKey.h"

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_HANDLE					 gImageHandle;

EFI_GUID  gEfiUsbIoProtocolGuid   = 
	{ 0x2B2F68D6, 0x0CD2, 0x44CF, 
		{ 0x8E, 0x8B, 0xBB, 0xA2, 0x0B, 0x1B, 0x5B, 0x75 }};

EFI_GUID  gEfiSimpleTextInputExProtocolGuid = 
	{0xdd9e7534, 0x7762, 0x4698, 
		{ 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa }};
		
//C:\UDK2015\MdePkg\Library\UefiUsbLib\Hid.c
/**
  Set the report descriptor of the specified USB HID interface.

  Submit a USB set HID report request for the USB device specified by UsbIo,
  Interface, ReportId, and ReportType, and set the report descriptor using the
  buffer specified by ReportLength and Report.
  If UsbIo is NULL, then ASSERT().
  If Report is NULL, then ASSERT().

  @param  UsbIo         A pointer to the USB I/O Protocol instance for the specific USB target.
  @param  Interface     The index of the report interface on the USB target.
  @param  ReportId      The identifier of the report to retrieve.
  @param  ReportType    The type of report to retrieve.
  @param  ReportLength  The size, in bytes, of Report.
  @param  Report        A pointer to the report descriptor buffer to set.

  @retval  EFI_SUCCESS       The request executed successfully.
  @retval  EFI_TIMEOUT       A timeout occurred executing the request.
  @retval  EFI_DEVICE_ERROR  The request failed due to a device error.

**/
EFI_STATUS
EFIAPI
UsbSetReportRequest (
  IN EFI_USB_IO_PROTOCOL     *UsbIo,
  IN UINT8                   Interface,
  IN UINT8                   ReportId,
  IN UINT8                   ReportType,
  IN UINT16                  ReportLen,
  IN UINT8                   *Report
  )
{
  UINT32                  Status;
  EFI_STATUS              Result;
  EFI_USB_DEVICE_REQUEST  Request;


  //
  // Fill Device request packet
  //
  Request.RequestType = USB_HID_CLASS_SET_REQ_TYPE;
  Request.Request = EFI_USB_SET_REPORT_REQUEST;
  Request.Value   = (UINT16) ((ReportType << 8) | ReportId);
  Request.Index   = Interface;
  Request.Length  = ReportLen;

  Result = UsbIo->UsbControlTransfer (
                    UsbIo,
                    &Request,
                    EfiUsbDataOut,
                    3000, //PcdGet32 (PcdUsbTransferTimeoutValue),
                    Report,
                    ReportLen,
                    &Status
                    );

  return Result;
}

VOID
RunSetKeyLED (
  IN  USB_KB_DEV    *UsbKeyboardDevice
  )
{
  LED_MAP Led;
  UINT8   ReportId;
  UINT8		i;
  
  for (i=0;i<32;i++)	
  {
	  
		  //
		  // Set each field in Led map.
		  //
		  Led.NumLock    = i      % 2;
		  Led.CapsLock   = (i>>1) % 2;
		  Led.ScrollLock = (i>>2) % 2;
		  Led.Resrvd     = 0;

		  ReportId       = 0;
		  //
		  // Call Set_Report Request to lighten the LED.
		  //
		  UsbSetReportRequest (
			UsbKeyboardDevice->UsbIo,
			UsbKeyboardDevice->InterfaceDescriptor.InterfaceNumber,
			ReportId,
			HID_OUTPUT_REPORT,
			1,
			(UINT8 *) &Led
			);
			gBS->Stall(100000);
	}
}
		
UINTN GetUSB( )
{
  EFI_STATUS  Status;
  UINTN       HandleIndex, HandleCount;
  EFI_HANDLE  *DevicePathHandleBuffer = NULL;
  EFI_USB_IO_PROTOCOL 				*USBIO;
  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL	*SimpleEx;
  USB_KB_DEV                     	*UsbKeyboardDevice;
  EFI_USB_DEVICE_DESCRIPTOR     DeviceDescriptor;
  
  //Get all the Handles that have UsbIO Protocol
  Status = gBS->LocateHandleBuffer(
                  ByProtocol,
                  &gEfiUsbIoProtocolGuid,
                  NULL,
                  &HandleCount,
                  &DevicePathHandleBuffer);
  if (EFI_ERROR(Status)) 
  {
    Print(L"ERROR : Get USBIO count fail.\n");
    return 0;
  }   

  for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) 
  { 
    Status = gBS->HandleProtocol(
                      DevicePathHandleBuffer[HandleIndex],
                      &gEfiUsbIoProtocolGuid,
                      (VOID**)&USBIO);
    if (EFI_ERROR(Status))
      {
        Print(L"ERROR : Open USBIO fail.\n");
        gBS->FreePool(DevicePathHandleBuffer);  
        return 0;
      }
	
	//Check if the Handle has SimpleEx Protocol
 	Status = gBS->OpenProtocol(
		DevicePathHandleBuffer[HandleIndex],
		&gEfiSimpleTextInputExProtocolGuid,
		(VOID**)&SimpleEx,
		gImageHandle,
		NULL,
		EFI_OPEN_PROTOCOL_GET_PROTOCOL);
	//If it has the Protocol, it means it's a USB Keyboard
	if (!EFI_ERROR(Status)) {
		
		//Get USB Device Descriptor
		Status = USBIO->UsbGetDeviceDescriptor(USBIO, &DeviceDescriptor);     
		if (EFI_ERROR(Status))
		{
			Print(L"ERROR : Usb Get Device Descriptor fail.\n");
			gBS->FreePool(DevicePathHandleBuffer);  
			return 0;
		}
      
		//Show the PID and VID
		Print(L"Found a USB Keyboard. VendorID = %04X, ProductID = %04X\n", 
                              DeviceDescriptor.IdVendor, 
                              DeviceDescriptor.IdProduct);       
		
		//Get USB_KB_DEV struct by SimpleEx Protocol
		UsbKeyboardDevice = TEXT_INPUT_EX_USB_KB_DEV_FROM_THIS (SimpleEx);
		//Change LED status
		RunSetKeyLED(UsbKeyboardDevice);
	}
  }
  gBS->FreePool(DevicePathHandleBuffer);       
  return HandleCount;
}


int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  GetUSB( );
  return EFI_SUCCESS;
}

 

我测试的系统接了2个键盘,代码运行结果如下:
usbk1

首先是第一个键盘的 LED乱闪,之后是第二个键盘乱闪。
在实际使用中,如果系统同时接入了2个以上的键盘你会发现他们的LED状态是同步的。比如,你在一个键盘上按下 Num Lock,那么另外的一块键盘上的 Num Lock LED同样也会发生变化。因为软件层有专门的机制来进行“同步”。在 EDK2 的代码中\MdeModulePkg\Bus\Usb\UsbKbDxe\KeyBoard.c 可以看到SetKeyLED 这个函数(本文使用的 RunSetKeyLED 也是脱胎于这个函数),有兴趣的朋友可以仔细研究一下。

/**
  Sets USB keyboard LED state.

  @param  UsbKeyboardDevice  The USB_KB_DEV instance.

**/
VOID
SetKeyLED (
  IN  USB_KB_DEV    *UsbKeyboardDevice
  )

 

完整的代码和 X64 Application下载

kbLEDTest

设计一个文件传输的协议

很多时候,我们需要从串口无损传输一个文件。但是对于串口来说,经常会发生丢失数据或者是数据错误的情况。因此,需要有一个协议来保证传输的正确性。这里设计一个简单的协议。
首先,打开接收端等待传输,发送端传输一个4字节的文件长度。之后的数据都是以N为单位(比如:16字节或者128字节等等)进行发送。

image001

数据包的长度为 N字节。其中有2个字节是用于校验。一个是 Checksum: 将0到N-2加到一起,要等于0;另一个是Index取值从0-255,比如一个包是0,第二个发送过来的就是1,这样一直下去,到255后再返回到0。
在传输阶段,接收端用“N”来表示请发送下一个数据包,接收端对收到的数据进行校验,如果出现错误,发送“R”表示要求发送端重新发送。
设计完成就是代码的实现。我编写了一对Windows 程序用来发送和接受,使用的是 128字节的数据包;此外就是一个Windows发送端,还有接受的Arduino代码。
在Windows下,推荐使用Virtual Serial port Driver 这个软件调试。他能够虚拟出来连通的串口,这样无需在外部的Loopback 直接就可以调用串口非常方便。
image010

我传输了一个200K的内容,之后对比过,发送和接收到的内容是相同的。我将代码直接打包,有兴趣的朋友可以研究一下(Delphi XE2编译)。
image011

这是上位机的发送部分,每次收到N或者R后会根据情况进行发送

procedure TForm2.ComPort1RxChar(Sender: TObject; Count: Integer);
var
  s,t: String;

  aSize,i:integer;
  checksum:byte;
begin
  ComPort1.ReadStr(s, Count);
  if s[Count]='N' then
    begin
      Memo1.Lines.Add('Received N');
      if BytesSend<stream.size then
         begin
           fillchar(Buffer,BUFFERSIZE,0);
           // 读取文件并取得实际读取出来的长度
           aSize:=stream.Read(Buffer,BUFFERSIZE-2);
           //计算Checksum, 就是 Buffer 第一个到倒数第二个加起来要求为0
           checksum:=0;
           for i := 0 to BUFFERSIZE-3 do
             begin
               checksum:=checksum-Buffer[i];
             end;
           //放置Checksum
           Buffer[BUFFERSIZE-2]:=checksum;
           //放置顺序号
           Buffer[BUFFERSIZE-1]:=Index;
           inc(Index);
           ComPort1.Write(Buffer,BUFFERSIZE);
           {t:='';
           for i := 0 to BUFFERSIZE-1 do
             begin
               t:=t+ IntToHex(Buffer[i],2)+' ';
             end;
            Memo1.Lines.Add(t);
           }
           BytesSend:=BytesSend+aSize;
           Form2.Caption:=IntToStr(BytesSend)+'/'+IntToStr(stream.size);
           Form2.Refresh;
         end
      else
         Memo1.Lines.Add('Completed!');
    end;
  //如果收到 R 就再次发送
  if s[Count]='R' then
    begin
      ComPort1.Write(Buffer,BUFFERSIZE);
      {     t:='';
           for i := 0 to BUFFERSIZE-1 do
             begin
               t:=t+ IntToHex(Buffer[i],2)+' ';
             end;
            Memo1.Lines.Add(t);
      }
      Memo1.Lines.Add('R');
    end;

end;

 

Arduino代码如下 (Arduino我没有进行Index的校验)

#include <SoftwareSerial.h>

#define BUFFERSIZE 16
#define TIMEOUT 3000UL
#define DEBUG (1)

//用来 DEBUG 的软串口,用额外的USB转串口线接在Pin11即可
SoftwareSerial mySerial(10, 11); // RX, TX

char buffer[BUFFERSIZE];

unsigned long filesize=0;  //文件大小
byte *p=(byte *)&filesize;
unsigned long total=0;
  
void setup() {
  //收到的文件大小字节数,一共4位
  byte c=0;
  //每次收到的数值
  byte r;
  
  Serial.begin(115200);

  if DEBUG {
      // set the data rate for the SoftwareSerial port
      mySerial.begin(115200);
      mySerial.println("Hello, world?");
  }     

  //如果没有收够4字节,则一直接收
  while (c<4) {
    while (Serial.available() > 0) 
      {
        //比如文件大小为 0x10000, 那么上位机发出来的顺序是
        // 00 00 01 00, 因此这里需要一个顺序的调换
        r=Serial.read();
        *(p+c)=r;
        c++;
        if DEBUG {
            mySerial.print(r);
            mySerial.println("  ");
        }    
      } //while (Serial.available() > 0) 
  } //while (c<4)   

  if DEBUG {
    mySerial.print("filesize=");
    mySerial.println(filesize);
  }
    
  //通知上位机继续发送
  Serial.print('N');
}

void loop() {
  byte buffer[BUFFERSIZE];
  int i;
  byte checksum;
Next:  
  //接收数据的字节数
  int counter=0;
  //接收超时的变量
  unsigned long elsp=millis();
  //如果接收数量小于缓冲区大小并且未超时,那么继续接收
  while ((counter<BUFFERSIZE)&&(millis()-elsp<TIMEOUT))
    {
      while (Serial.available()>0)
        { 
          buffer[counter]=Serial.read();
         if (DEBUG) {
            // mySerial.print(buffer[counter],HEX);
            // mySerial.print(" ");
            // mySerial.println(counter,HEX);
         }   
          counter++;
        } //while      
    }    
  //如果接收数量不足退出上面的循环  
  if (counter!=BUFFERSIZE) {
    //通知上位机重新发送
    Serial.print("R");
    if DEBUG {
        mySerial.print("R");
    }    
  }
  
  //检查接收到的数据校验和
  checksum=0; 
  for (i=0;i<BUFFERSIZE-1;i++) 
    {
      checksum=checksum+buffer[i];
      //Serial.print(buffer[i]);
      //Serial.print("   ");
      if DEBUG {
       //mySerial.print(buffer[i]);
       //mySerial.print("   ");
      } 
    }
  //校验失败通知上位机重新发送  
  if (checksum!=0)  {
       Serial.print("R");
       if DEBUG {
            mySerial.print("R");
       }     
       goto Next;
    }
  
  //如果当前收到的总数据小于文件大小,那么要求上位机继续发送  
  if (total<filesize) 
    {
      Serial.print('N');  
      //有效值只有 BUFFERSIZE-2
      total=total+BUFFERSIZE-2;
      if DEBUG {
            mySerial.print("N");
      }      
    }
    //否则停止
    else {
       if DEBUG {
            mySerial.print("Total received");
            mySerial.print(total);
       }     
      while (1==1) {}
    } //else

}

 

调试的方法是开一个SoftwareSeiral,然后用Pin11接到USB 转串口的RX 上,同时共地。我发送一个 400K 左右的文件:

image012

上面的方法优点是:
1.足够简单容易实现
2.对内存要求低

缺点也是很明显:
1.接收端只能等待外面过来的文件大小,如果这一步骤出现问题,那么后面都会乱掉,换句话说,如果你的通讯信道足够糟糕,那么整体还是不稳定;
2.效率不高,如果使用16位的Buffer,那么有效的数据只有 14字节,14 /16=87.5%。资料上说Uno 的串口默认是 64Byte,如果用这么大的Buffer,效率可以达到 (64-2)/64=96.9%。

附件下载:

Windows内部发送和接受代码和EXE(使用 128字节Buffer,速度挺快)
Receiver
Sender

Windows发送的EXE(16字节Buffer), Arduino 代码
Sender16
receivefile

Step to UEFI (119)OVMF 的 SourceLevelDebug

最近看到一篇介绍 QEMU 下实现 OVMF Source Level Debug的文章【参考1】,然后动手实验了一下。我试验的环境是 VirtualBox 创建的Windows7 32位的虚拟机,就是说我在虚拟机中运行 QEMU 虚拟机。编译器是 VS2013,之前一直在这个下面编译 UDK2015代码。具体的 OVMF 代码, 使用的是很久之前下载的能够编译的代码(最新的无法通过编译)。此外,还需要准备下面的软件:

1. Intel UDK Debugger Tool v1.5
2. WinDBG 6.11.1.404_x86
3. Qemu 0.13.0 (注意,这是特别修改的版本,最新的QEMU不行)

首先要安装 WinDBG,之后再安装 Intel UDK Tool,安装的时候要设置Debug port 如下(安装之后可以修改),其余均使用默认设置

ov1

之后,OVMF 的代码需要修改,有如下几个地方:
1. 在 OVMFPKG/OvmfPkgIa32.dsc 里面的 BUIDL_TARGETS 中加入 NOOPT的编译目标(新版本的 OVMF代码默认含有这个项目,但是因为我的代码比较久远,需要手工添加 )
ov2

2. 在 QemuBootOrderLib.c 中加入 _allmul 的定义(来自 StdLib、LibC、CRTIa32\Imul.c),如果没有加入,在编译时会出现下面的错误提示(没有找到原因,有兴趣的朋友可以试试,然然后告诉我)

Building … c:\ovmf20142\IntelFrameworkModulePkg\Universal\BdsDxe\BdsDxe.inf [I
A32]
“C:\Program Files\Microsoft Visual Studio 12.0\Vc\bin\link.exe” /OUT:c:\
ovmf20142\Build\OvmfIa32\NOOPT_VS2013\IA32\IntelFrameworkModulePkg\Universal\Bds
Dxe\BdsDxe\DEBUG\BdsDxe.dll /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF
=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /MACHINE:X86 /LTCG /DLL /
ENTRY:_ModuleEntryPoint /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /
DRIVER /DEBUG @c:\ovmf20142\Build\OvmfIa32\NOOPT_VS2013\IA32\IntelFrameworkModu
lePkg\Universal\BdsDxe\BdsDxe\OUTPUT\static_library_files.lst
QemuBootOrderLib.lib(ExtraRootBusMap.obj) : error LNK2001: unresolved external s
ymbol __allmul
c:\ovmf20142\Build\OvmfIa32\NOOPT_VS2013\IA32\IntelFrameworkModulePkg\Universal\
BdsDxe\BdsDxe\DEBUG\BdsDxe.dll : fatal error LNK1120: 1 unresolved externals
NMAKE : fatal error U1077: ‘”C:\Program Files\Microsoft Visual Studio 12.0\Vc\bi
n\link.exe”‘ : return code ‘0x460’
Stop.

上面完成之后,即可进行编译,编译指令

Build –a IA32 –p OvmfPkg\OvmfPkgIa32.dsc –b NOOPT –D SOURCE_DEBUG_ENABLED

编译之后生成的文件在 build\Ovmfia32\NOOPT_VS2013 ,文件名是 ovmd.fd
接下来检查了修改 Intel UDK 的配置,在下面这个位置
ov3

其中需要检查 port设置,同时加入你BIOS Source code 的目录。
ov4

检查之后就可以运行 UDK 工具了,他会自动调用起来 WinDbg。
将这个文件放到qemu 的目录下,使用下面的命令启动 QEMU
Qemu-system-x86_64.exe –bios OVMF.fd –serial pipe:qemu_pipe_dbg
最终,可以看到 WinDbg 中出现了Source Code。
ov5

更多的玩法后面会花点时间慢慢研究.

参考:
1.http://www.cnblogs.com/zhongts/p/5789686.html [原创]搭建UEFI调试环境

Python 海龟作图的问题

最近在帮别人看一个 Python 海龟作图的问题.
遇到的第一个问题是: 代码跑起来之后,出现的窗口会死掉

pyth1

经过研究发现产生问题的原因是缺少 turtle.mainloop() 语句(莫名其妙的是教材上根本没有这句话,这简直是误人子弟啊!)

turtle.onscreenclick(draw_kaleido)
turtle.mainloop()

 

完整代码

import turtle
import random

t=turtle.Pen()
t.speed(0)
t.width(3)
turtle.bgcolor("black")
colors=["red", "green", "yellow", "blue", "orange", "purple", "magenta", "white"]

def draw_spiral (x, y,size) :
    t.penup()
    t.setpos (x, y)
    t.pendown()
    for m in range(size):
        t.forward(m)
        t.left(61)
        
def draw_kaleido(x, y):
    print(x,y)
    t.pencolor(random.choice(colors))
    size=random.randint(20, 50)
    draw_spiral(x, y,size)
    draw_spiral(-x, y,size)
    draw_spiral(-x, -y,size)
    draw_spiral(x, -y,size)
    turtle.onscreenclick(draw_kaleido)

turtle.onscreenclick(draw_kaleido)
turtle.mainloop()
但是关闭窗口之后会出现很多错误提示
 pyth2

具体如下:

TclError: invalid command name ".50609928L"
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 1537, in __call__
    return self.func(*args)
  File "C:\Python27\lib\lib-tk\turtle.py", line 699, in eventfun
    fun(x, y)
  File "C:\Python27\test1.py", line 22, in draw_kaleido
    draw_spiral(x, y,size)
  File "C:\Python27\test1.py", line 15, in draw_spiral
    t.forward(m)
  File "C:\Python27\lib\lib-tk\turtle.py", line 1552, in forward
    self._go(distance)
  File "C:\Python27\lib\lib-tk\turtle.py", line 1520, in _go
    self._goto(ende)
  File "C:\Python27\lib\lib-tk\turtle.py", line 2990, in _goto
    screen._pointlist(self.currentLineItem),
  File "C:\Python27\lib\lib-tk\turtle.py", line 760, in _pointlist
    cl = self.cv.coords(item)
  File "<string>", line 1, in coords
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 2295, in coords
    self.tk.call((self._w, 'coords') + args)))
TclError: invalid command name ".50609928L"

 

产生这个问题的原因,根据我的判断是:当前正在绘制图形的时候, onscreenclick 会打断这个进程从而导致问题. 有一种解决方法是在绘制的时候, 用onscreenclick(none) 阻止事件的发生. 但是很明显这样的效果不好.但是除此之外暂时没有其他办法了,

Step to UEFI (117)Shell 下的 CPUID 工具

以前编写过 DOS 下 CPUID的工具,这次偶然在github上看到了 UEFI版本的 CPUID工具【参考1】,于是尝试编写一个 Application。
代码很长,但大部分是根据 BIT 解析信息的部分。

/** @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 "Cpuid.h"
#include  <Library/ShellCEntryLib.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;
}

 

下面是我在 HDK Kabylake-R 上运行得到的结果:
UEFI CPUID Version 0.5
CPUID_SIGNATURE (Leaf 00000000)
EAX:00000016 EBX:756E6547 ECX:6C65746E EDX:49656E69
Eax MaximumLeaf: 16
Signature = GenuineIntel
CPUID_VERSION_INFO (Leaf 00000001)
EAX:000806EA EBX:00100800 ECX:77FAFBFF EDX:BFEBFBFF
Family = 6 Model = 8E Stepping = A
Eax SteppingId: A
Eax Model: E
Eax FamilyId: 6
Eax ProcessorType: 0
Eax ExtendedModelId: 8
Eax ExtendedFamilyId: 0
Ebx BrandIndex: 0
Ebx CacheLineSize: 8
Ebx MaximumAddressableIdsForLogicalProcessors: 10
Ebx InitialLocalApicId: 0
Ecx SSE3: 1
Ecx PCLMULQDQ: 1
Ecx DTES64: 1
Ecx MONITOR: 1
Ecx DS_CPL: 1
Ecx VMX: 1
Ecx SMX: 1
Ecx TM2: 1
Ecx SSSE3: 1
Ecx CNXT_ID: 0
Ecx SDBG: 1
Ecx FMA: 1
Ecx CMPXCHG16B: 1
Ecx xTPR_Update_Control: 1
Ecx PDCM: 1
Ecx PCID: 1
Ecx DCA: 0
Ecx SSE4_1: 1
Ecx SSE4_2: 1
Ecx x2APIC: 1
Ecx MOVBE: 1
Ecx POPCNT: 1
Ecx TSC_Deadline: 1
Ecx AESNI: 1
Ecx XSAVE: 1
Ecx OSXSAVE: 0
Ecx AVX: 1
Ecx F16C: 1
Ecx RDRAND: 1
Edx FPU: 1
Edx VME: 1
Edx DE: 1
Edx PSE: 1
Edx TSC: 1
Edx MSR: 1
Edx PAE: 1
Edx MCE: 1
Edx CX8: 1
Edx APIC: 1
Edx SEP: 1
Edx MTRR: 1
Edx PGE: 1
Edx MCA: 1
Edx CMOV: 1
Edx PAT: 1
Edx PSE_36: 1
Edx PSN: 0
Edx CLFSH: 1
Edx DS: 1
Edx ACPI: 1
Edx MMX: 1
Edx FXSR: 1
Edx SSE: 1
Edx SSE2: 1
Edx SS: 1
Edx HTT: 1
Edx TM: 1
Edx PBE: 1
CPUID_CACHE_INFO (Leaf 00000002)
EAX:76036301 EBX:00F0B6FF ECX:00000000 EDX:00C30000
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
TLB Data TLB: 4 KByte pages, 4-way set associative, 64 entries
TLB Instruction TLB: 2M/4M pages, fully associative, 8 entries
General CPUID leaf 2 does not report cache descriptor information, use CPUID leaf 4 to query cache parameters
TLB Instruction TLB: 4KByte pages, 8-way set associative, 128 entries
Prefetch 64-Byte prefetching
STLB Shared 2nd-Level TLB: 4 KByte /2 MByte pages, 6-way associative, 1536 entries. Also 1GBbyte pages, 4-way, 16 entries.
CPUID_SERIAL_NUMBER (Leaf 00000003)
Not Supported
CPUID_CACHE_PARAMS (Leaf 00000004, Sub-Leaf 00000000)
EAX:1C004121 EBX:01C0003F ECX:0000003F EDX:00000000
Eax CacheType: 1
Eax CacheLevel: 1
Eax SelfInitializingCache: 1
Eax FullyAssociativeCache: 0
Eax MaximumAddressableIdsForLogicalProcessors: 1
Eax MaximumAddressableIdsForProcessorCores: 7
Ebx LineSize: 3F
Ebx LinePartitions: 0
Ebx Ways: 7
Ecx NumberOfSets: 3F
Edx Invalidate: 0
Edx CacheInclusiveness: 0
Edx ComplexCacheIndexing: 0
CPUID_CACHE_PARAMS (Leaf 00000004, Sub-Leaf 00000001)
EAX:1C004122 EBX:01C0003F ECX:0000003F EDX:00000000
Eax CacheType: 2
Eax CacheLevel: 1
Eax SelfInitializingCache: 1
Eax FullyAssociativeCache: 0
Eax MaximumAddressableIdsForLogicalProcessors: 1
Eax MaximumAddressableIdsForProcessorCores: 7
Ebx LineSize: 3F
Ebx LinePartitions: 0
Ebx Ways: 7
Ecx NumberOfSets: 3F
Edx Invalidate: 0
Edx CacheInclusiveness: 0
Edx ComplexCacheIndexing: 0
CPUID_CACHE_PARAMS (Leaf 00000004, Sub-Leaf 00000002)
EAX:1C004143 EBX:00C0003F ECX:000003FF EDX:00000000
Eax CacheType: 3
Eax CacheLevel: 2
Eax SelfInitializingCache: 1
Eax FullyAssociativeCache: 0
Eax MaximumAddressableIdsForLogicalProcessors: 1
Eax MaximumAddressableIdsForProcessorCores: 7
Ebx LineSize: 3F
Ebx LinePartitions: 0
Ebx Ways: 3
Ecx NumberOfSets: 3FF
Edx Invalidate: 0
Edx CacheInclusiveness: 0
Edx ComplexCacheIndexing: 0
CPUID_CACHE_PARAMS (Leaf 00000004, Sub-Leaf 00000003)
EAX:1C03C163 EBX:02C0003F ECX:00001FFF EDX:00000006
Eax CacheType: 3
Eax CacheLevel: 3
Eax SelfInitializingCache: 1
Eax FullyAssociativeCache: 0
Eax MaximumAddressableIdsForLogicalProcessors: F
Eax MaximumAddressableIdsForProcessorCores: 7
Ebx LineSize: 3F
Ebx LinePartitions: 0
Ebx Ways: B
Ecx NumberOfSets: 1FFF
Edx Invalidate: 0
Edx CacheInclusiveness: 1
Edx ComplexCacheIndexing: 1
CPUID_MONITOR_MWAIT (Leaf 00000005)
EAX:00000040 EBX:00000040 ECX:00000003 EDX:11142120
Eax SmallestMonitorLineSize: 40
Ebx LargestMonitorLineSize: 40
Ecx ExtensionsSupported: 1
Ecx InterruptAsBreak: 1
Edx C0States: 0
Edx C1States: 2
Edx C2States: 1
Edx C3States: 2
Edx C4States: 4
Edx C5States: 1
Edx C6States: 1
Edx C7States: 1
CPUID_THERMAL_POWER_MANAGEMENT (Leaf 00000006)
EAX:000027F7 EBX:00000002 ECX:00000009 EDX:00000000
Eax DigitalTemperatureSensor: 1
Eax TurboBoostTechnology: 1
Eax ARAT: 1
Eax PLN: 1
Eax ECMD: 1
Eax PTM: 1
Eax HWP: 1
Eax HWP_Notification: 1
Eax HWP_Activity_Window: 1
Eax HWP_Energy_Performance_Preference: 1
Eax HWP_Package_Level_Request: 0
Eax HDC: 1
Ebx InterruptThresholds: 2
Ecx HardwareCoordinationFeedback: 1
Ecx PerformanceEnergyBias: 1
CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS (Leaf 00000007, Sub-Leaf 00000000)
EAX:00000000 EBX:029C6FBF ECX:00000000 EDX:00000000
Ebx FSGSBASE: 1
Ebx IA32_TSC_ADJUST: 1
Ebx SGX: 1
Ebx BMI1: 1
Ebx HLE: 1
Ebx AVX2: 1
Ebx FDP_EXCPTN_ONLY: 0
Ebx SMEP: 1
Ebx BMI2: 1
Ebx EnhancedRepMovsbStosb: 1
Ebx INVPCID: 1
Ebx RTM: 1
Ebx RDT_M: 0
Ebx DeprecateFpuCsDs: 1
Ebx MPX: 1
Ebx RDT_A: 0
Ebx RDSEED: 1
Ebx ADX: 1
Ebx SMAP: 1
Ebx CLFLUSHOPT: 1
Ebx CLWB: 0
Ebx IntelProcessorTrace: 1
Ebx SHA: 0
Ecx PREFETCHWT1: 0
Ecx UMIP: 0
Ecx PKU: 0
Ecx OSPKE: 0
Ecx MAWAU: 0
Ecx RDPID: 0
Ecx SGX_LC: 0
CPUID_DIRECT_CACHE_ACCESS_INFO (Leaf 00000009)
EAX:00000000 EBX:00000000 ECX:00000000 EDX:00000000
CPUID_ARCHITECTURAL_PERFORMANCE_MONITORING (Leaf 0000000A)
EAX:07300804 EBX:00000000 ECX:00000000 EDX:00000603
Eax ArchPerfMonVerID: 4
Eax PerformanceMonitorCounters: 8
Eax PerformanceMonitorCounterWidth: 30
Eax EbxBitVectorLength: 7
Ebx UnhaltedCoreCycles: 0
Ebx InstructionsRetired: 0
Ebx UnhaltedReferenceCycles: 0
Ebx LastLevelCacheReferences: 0
Ebx LastLevelCacheMisses: 0
Ebx BranchInstructionsRetired: 0
Ebx AllBranchMispredictRetired: 0
Edx FixedFunctionPerformanceCounters: 3
Edx FixedFunctionPerformanceCounterWidth: 30
CPUID_EXTENDED_TOPOLOGY (Leaf 0000000B, Sub-Leaf 00000000)
EAX:00000001 EBX:00000001 ECX:00000100 EDX:00000000
Eax ApicIdShift: 1
Ebx LogicalProcessors: 1
Ecx LevelNumber: 0
Ecx LevelType: 1
Edx x2APIC_ID: 0
CPUID_EXTENDED_TOPOLOGY (Leaf 0000000B, Sub-Leaf 00000001)
EAX:00000004 EBX:00000004 ECX:00000201 EDX:00000000
Eax ApicIdShift: 4
Ebx LogicalProcessors: 4
Ecx LevelNumber: 1
Ecx LevelType: 2
Edx x2APIC_ID: 0
CPUID_EXTENDED_STATE (Leaf 0000000D, Sub-Leaf 00000000)
EAX:0000001F EBX:00000240 ECX:00000440 EDX:00000000
Eax x87: 1
Eax SSE: 1
Eax AVX: 1
Eax MPX: 3
Eax AVX_512: 0
Eax IA32_XSS: 0
Eax PKRU: 0
Ebx EnabledSaveStateSize: 240
Ecx SupportedSaveStateSize: 440
Edx XCR0_Supported_32_63: 0
CPUID_EXTENDED_STATE (Leaf 0000000D, Sub-Leaf 00000001)
EAX:0000000F EBX:00000240 ECX:00000100 EDX:00000000
Eax XSAVEOPT: 1
Eax XSAVEC: 1
Eax XGETBV: 1
Eax XSAVES: 1
Ebx EnabledSaveStateSize_XCR0_IA32_XSS: 240
Ecx XCR0: 0
Ecx PT: 0
Ecx XCR0_1: 0
Edx IA32_XSS_Supported_32_63: 0
CPUID_INTEL_RDT_MONITORING (Leaf 0000000F, Sub-Leaf 00000000)
EAX:00000000 EBX:00000000 ECX:00000000 EDX:00000000
Ebx Maximum_RMID_Range: 0
Edx L3CacheRDT_M: 0
CPUID_INTEL_RDT_MONITORING (Leaf 0000000F, Sub-Leaf 00000001)
EAX:00000000 EBX:00000000 ECX:00000000 EDX:00000000
Ebx OccupancyConversionFactor: 0
Ecx Maximum_RMID_Range: 0
Edx L3CacheOccupancyMonitoring: 0
Edx L3CacheTotalBandwidthMonitoring: 0
Edx L3CacheLocalBandwidthMonitoring: 0
CPUID_INTEL_RDT_ALLOCATION (Leaf 00000010, Sub-Leaf 00000000)
EAX:00000000 EBX:00000000 ECX:00000000 EDX:00000000
Ebx L3CacheAllocation: 0
Ebx L2CacheAllocation: 0
CPUID_INTEL_RDT_ALLOCATION (Leaf 00000010, Sub-Leaf 00000001)
EAX:00000000 EBX:00000000 ECX:00000000 EDX:00000000
Eax CapacityLength: 0
Ebx AllocationUnitBitMap: 0
Ecx CosUpdatesInfrequent: 0
Ecx CodeDataPrioritization: 0
Edx HighestCosNumber: 0
CPUID_INTEL_RDT_ALLOCATION (Leaf 00000010, Sub-Leaf 00000002)
EAX:00000000 EBX:00000000 ECX:00000000 EDX:00000000
Eax CapacityLength: 0
Ebx AllocationUnitBitMap: 0
Edx HighestCosNumber: 0
CPUID_INTEL_SGX (Leaf 00000012, Sub-Leaf 00000000)
EAX:00000000 EBX:00000000 ECX:00000000 EDX:00000000
Eax SGX1: 0
Eax SGX2: 0
Edx MaxEnclaveSize_Not64: 0
Edx MaxEnclaveSize_64: 0
CPUID_INTEL_SGX (Leaf 00000012, Sub-Leaf 00000001)
EAX:00000000 EBX:00000000 ECX:00000000 EDX:00000000
CPUID_INTEL_PROCESSOR_TRACE (Leaf 00000014, Sub-Leaf 00000000)
EAX:00000001 EBX:0000000F ECX:00000007 EDX:00000000
Eax MaximumSubLeaf: 1
Ebx Cr3Filter: 1
Ebx ConfigurablePsb: 1
Ebx IpTraceStopFiltering: 1
Ebx Mtc: 1
Ebx PTWrite: 0
Ebx PowerEventTrace: 0
Ecx RTIT: 1
Ecx ToPA: 1
Ecx SingleRangeOutput: 1
Ecx TraceTransportSubsystem: 0
Ecx LIP: 0
CPUID_INTEL_PROCESSOR_TRACE (Leaf 00000014, Sub-Leaf 00000001)
EAX:02490002 EBX:003F3FFF ECX:00000000 EDX:00000000
Eax ConfigurableAddressRanges: 2
Eax MtcPeriodEncodings: 249
Ebx CycleThresholdEncodings: 3FFF
Ebx PsbFrequencyEncodings: 3F
CPUID_TIME_STAMP_COUNTER (Leaf 00000015)
EAX:00000002 EBX:00000096 ECX:00000000 EDX:00000000
CPUID_PROCESSOR_FREQUENCY (Leaf 00000016)
EAX:00000708 EBX:00000D48 ECX:00000064 EDX:00000000
Eax ProcessorBaseFrequency: 708
Ebx MaximumFrequency: D48
Ecx BusFrequency: 64
CPUID_EXTENDED_FUNCTION (Leaf 80000000)
EAX:80000008 EBX:00000000 ECX:00000000 EDX:00000000
Eax MaximumExtendedFunction: 80000008
CPUID_EXTENDED_CPU_SIG (Leaf 80000001)
EAX:00000000 EBX:00000000 ECX:00000121 EDX:2C100800
Ecx LAHF_SAHF: 1
Ecx LZCNT: 1
Ecx PREFETCHW: 1
Edx SYSCALL_SYSRET: 1
Edx NX: 1
Edx Page1GB: 1
Edx RDTSCP: 1
Edx LM: 1
CPUID_BRAND_STRING1 (Leaf 80000002)
EAX:756E6547 EBX:20656E69 ECX:65746E49 EDX:2952286C
CPUID_BRAND_STRING2 (Leaf 80000003)
EAX:55504320 EBX:30303020 ECX:20402030 EDX:30342E31
CPUID_BRAND_STRING3 (Leaf 80000004)
EAX:007A4847 EBX:00000000 ECX:00000000 EDX:00000000
Brand String = Genuine Intel(R) CPU 0000 @ 1.40GHz
CPUID_EXTENDED_CACHE_INFO (Leaf 80000006)
EAX:00000000 EBX:00000000 ECX:01006040 EDX:00000000
Ecx CacheLineSize: 40
Ecx L2Associativity: 6
Ecx CacheSize: 100
CPUID_EXTENDED_TIME_STAMP_COUNTER (Leaf 80000007)
EAX:00000000 EBX:00000000 ECX:00000000 EDX:00000100
Edx InvariantTsc: 1
CPUID_VIR_PHY_ADDRESS_SIZE (Leaf 80000008)
EAX:00003027 EBX:00000000 ECX:00000000 EDX:00000000
Eax PhysicalAddressBits: 27
Eax LinearAddressBits: 30

编译后 IA32和 X64的Application

cpuidapp

完整的源代码
cpuid

参考:
1.https://raw.githubusercontent.com/tianocore/edk2/master/UefiCpuPkg/Application/Cpuid/Cpuid.c

UEFI编写的 Pong 游戏

来自 https://github.com/Openwide-Ingenierie/Pong-UEFI 的 Pong Game 。进行了一个简单的修改,加入 ESC 退出的功能。

pong

原始代码
Pong-UEFI-master

修改后的代码:
uefipong

编译方法很简单, 和之前提到的Shell下面的 Application一样,将工程放在 AppPkg/Application 下面,然后修改 AppPkg.dsc 文件.最后用

build -a IA32 -p AppPkg\AppPkg.dsc

即可生成 EFI, 可以在 NT32 模拟器环境下进行测试.

uefipong

一个失败的 Arduino 项目

古语云“拳是两扇门,全凭脚打人”,无论是中华武术,跆拳道还是空手道,踢腿都是重要的内容。我的教练踢腿速度很快,通常还没有看清脚就已经提到眼前。因此我打算测试踢腿速度的设备,将这个速度进行量化。

方案是使用触摸来判断,在地面和脚靶上分别放置两个导电的装置然后计算从抬脚到接触到的时间差。进行判断接触的传感器还是 MPR121.。最终东西做出来了,但是意外发现这个方案存在致命缺点最后只能放弃。

image001

image002

image003

完整的代码:

#include <Wire.h>
#include "Adafruit_MPR121.h"

// You can have up to 4 on one i2c bus but one is enough for testing!
Adafruit_MPR121 cap = Adafruit_MPR121();

// Keeps track of the last pins touched
// so we know when buttons are 'released'
uint16_t lasttouched = 0;
uint16_t currtouched = 0;

void showdata(unsigned int value)
{
   //这是数码管要求的数据头信息
    Serial.write(0xff);
    Serial.write(0x00);
    Serial.write(0x04);  //显示四位数值
    if (value>9999) {  // 9999
        Serial.write(0x09);
        Serial.write(0x09);
        Serial.write(0x09);
        Serial.write(0x09);
    } else
        if (value >999) { //9.999
           Serial.write(value / 1000 + 0x80);
           Serial.write((value - value /1000 * 1000) / 100);      
           Serial.write((value - value /100 * 100) / 10); 
           Serial.write(value % 10);
        }
        else
           {
           Serial.write(value / 1000);
           Serial.write((value - value /1000 * 1000) / 100);      
           Serial.write((value - value /100 * 100) / 10); 
           Serial.write(value % 10);
          }
              
      
   //最后一位是亮度
     Serial.write(0);
      
}

// the setup function runs once when you press reset or power the board
void setup() {
  Serial.begin(115200);
 

  // Default address is 0x5A, if tied to 3.3V its 0x5B
  // If tied to SDA its 0x5C and if SCL then 0x5D
  if (!cap.begin(0x5A)) {
    Serial.println("MPR121 not found, check wiring?");
    while (1);
  }
 // Serial.println("MPR121 found!");  
}

int elsp=0;

// the loop function runs over and over again forever
void loop() {
   // Get the currently touched pads
  currtouched = cap.touched();

  //Touch 11 Release -- Start
  //Touch 7  Touch -- End
  //Tpuch 5  Reset

  if (!(currtouched & _BV(11)) && (lasttouched & _BV(11)) ) {
      if (elsp==0) {
       // Serial.println("Start");
        elsp=millis();
      }
    }
  if ((currtouched & _BV(7)) && !(lasttouched & _BV(7)) ) {
      if (elsp!=0) {
           // Serial.println("Stop");
            //Serial.println(millis()-elsp);
            showdata(millis()-elsp);
            elsp=0;
          }
        }

  if (!(currtouched & _BV(5)) && (lasttouched & _BV(5)) ) {
      //Serial.println("reset timer");
      showdata(0);
      elsp=0;
    }

  // reset our state
  lasttouched = currtouched;
  if (elsp!=0) {showdata(millis()-elsp);}

}

 

工作的视频

遇到的问题如下:
1. MPR121 是通过母座插在一块 Shield板上的,但是座子和MPR121接触不好,这对于触摸传感器来说是致命的。最后只得直接从压线座飞线到 MPR121上;
2. 负责接触脚的金属一直有问题。起初使用的是和之前石头剪刀布游戏机一样的纸壳贴锡箔,但是测试发现一脚上去就会碎掉。后来我特地用502沾了一个结果发现感应非常不灵敏,可能是因为面积过大导致的。之前的石头剪刀布游戏机最终人是要站在上面的,有充分的时间来进行感知,而这次的装置,脚接触一次很快就会收回,因此对于准确性和灵敏度要求都会比较高;
3. 因为是触摸感应,所以对线的长度接触之类是比较敏感的,在使用中发现布线会比较麻烦,脚靶那一端经常会被“踢飞”,有了线之后会影响发挥;
综合上述的原因,使用触摸的方案来进行踢腿速度测试是不可行的。

当然我们还是能从这个设备学习到很多,首先是前面的三点失败的总结,此外还有对于那种大的数码管控制,在未来通过其他方法改进,我相信总有一天能够做出测试踢腿速度的设备.

Step to UEFI (118)新指令 RDRand

从 IvyBridge开始, Intel 新加入了 RDRAND 和 RDSEED 两个用于生成随机数的指令。从【参考1】来看二者的差别在于:

The short answer
The decision process for which instruction to use is mercifully simple, and based on what the output will be used for.
• If you wish to seed another pseudorandom number generator (PRNG), use RDSEED
• For all other purposes, use RDRAND
That’s it. RDSEED is intended for seeding a software PRNG of arbitrary width. RDRAND is intended for applications that merely require high-quality random numbers.

简单的说二者的差别就是 “如果打算用来作为其它伪随机数生成器的种子的时候那么就可以考虑RDSEED,不然就使用RNRAND。”【参考2】

在 UDK2015的代码中,有涉及到 RDRAND这个指令的,下面就进行实验。作为参考的代码在 SecurityPkg\RandomNumberGenerator\RngDxe 下面。比较特别的地方是,代码中用到了汇编语言调用这个指令,例如:

;------------------------------------------------------------------------------
;  Generate a 16 bit random number
;  Return TRUE if Rand generated successfully, or FALSE if not
;
;  BOOLEAN EFIAPI RdRand16Step (UINT16 *Rand);   RCX
;------------------------------------------------------------------------------
RdRand16Step  PROC
    ; rdrand   ax                  ; generate a 16 bit RN into ax, CF=1 if RN generated ok, otherwise CF=0
    db     0fh, 0c7h, 0f0h         ; rdrand r16:  "0f c7 /6  ModRM:r/m(w)"
    jb     rn16_ok                 ; jmp if CF=1
    xor    rax, rax                ; reg=0 if CF=0
    ret                            ; return with failure status
rn16_ok:
    mov    [rcx], ax
    mov    rax, 1
    ret
RdRand16Step ENDP

 

而对于 X64 来说,无法实现代码中内嵌汇编,因此,源程序上有一份32的ASM 和一份 64 的 ASM。

INF 文件中也要分开声明两次:

[Sources.common]
  RdRand.c
  RdRand.h
[Sources.IA32]
  IA32/RdRandWord.c
  IA32/AsmRdRand.asm
[Sources.X64]
  X64/RdRandWord.c
  X64/AsmRdRand.asm

 

此外,在使用这个指令之前还需要用CPUID指令来检测当前CPU是否支持,最终代码如下:

/** @file
  Support routines for RDRAND instruction access.

Copyright (c) 2013, 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 "RdRand.h"
//#include "AesCore.h"

//
// Bit mask used to determine if RdRand instruction is supported.
//
#define RDRAND_MASK    0x40000000

/**
  Determines whether or not RDRAND instruction is supported by the host hardware.

  @retval EFI_SUCCESS          RDRAND instruction supported.
  @retval EFI_UNSUPPORTED      RDRAND instruction not supported.

**/
EFI_STATUS
EFIAPI
IsRdRandSupported (
  VOID
  )
{
  EFI_STATUS  Status;
  UINT32      RegEax;
  UINT32      RegEbx;
  UINT32      RegEcx;
  UINT32      RegEdx;
  BOOLEAN     IsIntelCpu;

  Status     = EFI_UNSUPPORTED;
  IsIntelCpu = FALSE;
  
  //
  // Checks whether the current processor is an Intel product by CPUID.
  //
  AsmCpuid (0, &RegEax, &RegEbx, &RegEcx, &RegEdx);
  if ((CompareMem ((CHAR8 *)(&RegEbx), "Genu", 4) == 0) &&
      (CompareMem ((CHAR8 *)(&RegEdx), "ineI", 4) == 0) &&
      (CompareMem ((CHAR8 *)(&RegEcx), "ntel", 4) == 0)) {
    IsIntelCpu = TRUE;
  }

  if (IsIntelCpu) {
    //
    // Determine RDRAND support by examining bit 30 of the ECX register returned by CPUID.
    // A value of 1 indicates that processor supports RDRAND instruction.
    //
    AsmCpuid (1, 0, 0, &RegEcx, 0);

    if ((RegEcx & RDRAND_MASK) == RDRAND_MASK) {
      Status = EFI_SUCCESS;
    }
  }

  return Status;
}

/**
  Calls RDRAND to obtain a 16-bit random number.

  @param[out]  Rand          Buffer pointer to store the random result.
  @param[in]   NeedRetry     Determine whether or not to loop retry.

  @retval EFI_SUCCESS        RDRAND call was successful.
  @retval EFI_NOT_READY      Failed attempts to call RDRAND.

**/
EFI_STATUS
EFIAPI
RdRand16 (
  OUT UINT16       *Rand,
  IN BOOLEAN       NeedRetry
  )
{
  UINT32      Index;
  UINT32      RetryCount;

  if (NeedRetry) {
    RetryCount = RETRY_LIMIT;
  } else {
    RetryCount = 1;
  }

  //
  // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.
  //
  for (Index = 0; Index < RetryCount; Index++) {
    if (RdRand16Step (Rand)) {
      return EFI_SUCCESS;
    }
  }
  
  return EFI_NOT_READY;
}

/**
  Calls RDRAND to obtain a 32-bit random number.

  @param[out]  Rand          Buffer pointer to store the random result.
  @param[in]   NeedRetry     Determine whether or not to loop retry.

  @retval EFI_SUCCESS        RDRAND call was successful.
  @retval EFI_NOT_READY      Failed attempts to call RDRAND.

**/
EFI_STATUS
EFIAPI
RdRand32 (
  OUT UINT32       *Rand,
  IN BOOLEAN       NeedRetry
  )
{
  UINT32      Index;
  UINT32      RetryCount;

  if (NeedRetry) {
    RetryCount = RETRY_LIMIT;
  } else {
    RetryCount = 1;
  }

  //
  // Perform a single call to RDRAND, or enter a loop call until RDRAND succeeds.
  //
  for (Index = 0; Index < RetryCount; Index++) {
    if (RdRand32Step (Rand)) {
      return EFI_SUCCESS;
    }
  }
  
  return EFI_NOT_READY;
}

/**
  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
  )
{
	UINT16	RandNumber1;
	UINT32	RandNumber2;
	
	if	(FALSE==IsRdRandSupported) {
			Print (L"Your CPU doesn't support RdRand\n");
			return EFI_SUCCESS;
	}
	RdRand16(&RandNumber1,TRUE);
	Print (L"Generate a 16 bits number [%X]\n",RandNumber1);
	
	RdRand32(&RandNumber2,TRUE);
	Print (L"Generate a 32 bits number [%X]\n",RandNumber2);
	
    return EFI_SUCCESS;
}

 

特别注意:代码无法在 NT32环境下运行,下面的结果是在 KBL-R HDK 上取得的。

rnrand

IA32和X64的 Application 下载:
RdRandapp

完整的代码下载
RdRand

参考:
1. https://software.intel.com/en-us/blogs/2012/11/17/the-difference-between-rdrand-and-rdseed The Difference Between RDRAND and RDSEED
2. http://blog.yinfupai.com/2914.html

Step to UEFI (116)CTRL+ALT+DEL输出字符

最近看代码,发现比较有意思的地方,在 Keyboard.c 中有处理 USB键盘 Ctrl+Alt+Del的代码。

    //
    // When encountering Ctrl + Alt + Del, then warm reset.
    //
    if (KeyDescriptor->Modifier == EFI_DELETE_MODIFIER) {
      if ((UsbKeyboardDevice->CtrlOn) && (UsbKeyboardDevice->AltOn)) {
        gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
      }
}

 

修改一下即可实现在 Shell下按下 Ctrl+Alt+Del输出一段字符,暂停5s然后重启的功能。

    //
    // When encountering Ctrl + Alt + Del, then warm reset.
    //
    if (KeyDescriptor->Modifier == EFI_DELETE_MODIFIER) {
      if ((UsbKeyboardDevice->CtrlOn) && (UsbKeyboardDevice->AltOn)) {
		//LABZDebug_Start
		gST->ConOut->OutputString(gST->ConOut,L"www.lab-z.com");
		gBS->Stall(5000000UL);
		// LABZDebug _End
        gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
      }
    }

 

再阅读代码,上面的代码在USBParseKey 函数中。这个函数实现的是解析 USB键盘的功能。函数调用者是EfiKey.c 中的

/**
  Timer handler to convert the key from USB.

  @param  Event                    Indicates the event that invoke this function.
  @param  Context                  Indicates the calling context.
**/
VOID
EFIAPI
USBKeyboardTimerHandler (
  IN  EFI_EVENT                 Event,
  IN  VOID                      *Context
  )

 

从名称上看,这是一个时间中断来调用的函数。具体的安装代码如下:

  Status = gBS->CreateEvent (
                  EVT_TIMER | EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  USBKeyboardTimerHandler,
                  UsbKeyboardDevice,
                  &UsbKeyboardDevice->TimerEvent
                  );

 

就是说USB键盘是通过时间中断来进行检查和处理的.