Step to UEFI (145)Crypto 实现的 SHA256

之前我们介绍过在UEFI 下实现MD5【参考1】和SHA-1 【参考2】的方法,这次介绍一下如何计算 SHA256 。同样的, SHA256也是一种 HASH 算法。
与之前不同,这次使用的是前面提到的CryptoPkg,在正常编译完成上述 Package后,会生成一个:CryptRuntimeDxe的EFI文件,我们需要做的是先在Shell 下加载这个 Driver。

sha2561

加载Driver之后,就可以使用EFI_RUNTIME_CRYPT_PROTOCOL :

///
/// Runtime Cryptographic Protocol Structure.
///
typedef struct {
  EFI_RUNTIME_CRYPT_SHA256_GET_CONTEXT_SIZE  Sha256GetContextSize;
  EFI_RUNTIME_CRYPT_SHA256_INIT              Sha256Init;
  EFI_RUNTIME_CRYPT_SHA256_UPDATE            Sha256Update;
  EFI_RUNTIME_CRYPT_SHA256_FINAL             Sha256Final;
  EFI_RUNTIME_CRYPT_RSA_NEW                  RsaNew;
  EFI_RUNTIME_CRYPT_RSA_FREE                 RsaFree;
  EFI_RUNTIME_CRYPT_RSA_SET_KEY              RsaSetKey;
  EFI_RUNTIME_CRYPT_RSA_PKCS1_VERIFY         RsaPkcs1Verify;
} EFI_RUNTIME_CRYPT_PROTOCOL;

 

在 AppPkg 中编写代码如下:

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

#include "RuntimeCrypt.h"

//
// Max Known Digest Size is SHA512 Output (64 bytes) by far
//
#define MAX_DIGEST_SIZE    64

//
// Message string for digest validation
//
CHAR8 *HashData = "www.lab-z.com";

extern EFI_BOOT_SERVICES         *gBS;

///
/// Runtime Cryptographic Protocol GUID.
///
EFI_GUID  gEfiRuntimeCryptProtocolGuid =
                {0xe1475e0c, 0x1746, 0x4802, 
                        { 0x86, 0x2e, 0x1, 0x1c, 0x2c, 0x2d, 0x9d, 0x86 }};
int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
        EFI_RUNTIME_CRYPT_PROTOCOL  *mCryptProtocol = NULL;
        EFI_STATUS                  Status;
        UINT8                       Digest[MAX_DIGEST_SIZE];      
        UINTN    CtxSize;
        VOID     *HashCtx;
        UINTN    DataSize; 
        UINTN    Index;
        
        DataSize = AsciiStrLen (HashData);        
        //
        // Pointer to the runtime cryptographic protocol.
        //
        Status = gBS->LocateProtocol(
                        &gEfiRuntimeCryptProtocolGuid, 
                        NULL, 
                        (VOID **) &mCryptProtocol);
        if (EFI_ERROR(Status)) {
           Print(L"Can't find the runtime cryptographic protocol\n");
           return Status;
        }
        
        Print (L"- SHA256: \n");

        //
        // SHA256 Digest Validation
        //
        ZeroMem (Digest, MAX_DIGEST_SIZE);
        CtxSize = mCryptProtocol->Sha256GetContextSize ();
        HashCtx = AllocatePool (CtxSize);

        Print (L"Init... \n");
        Status  = mCryptProtocol->Sha256Init (HashCtx);
        if (!Status) {
                Print (L"[Fail]\n");
                return EFI_ABORTED;
        }

        Print (L"Update... \n");
        Status  = mCryptProtocol->Sha256Update (HashCtx, HashData, DataSize);
        if (!Status) {
                Print (L"[Fail]\n");
                return EFI_ABORTED;
        }

        Print (L"Finalize... \n");
        Status  = mCryptProtocol->Sha256Final (HashCtx, Digest);
        if (!Status) {
                Print (L"[Fail]\n");
                return EFI_ABORTED;
        }

        for (Index=0;Index<SHA256_DIGEST_SIZE;Index++) {
                Print (L"%2X  ",Digest[Index]);
        }
        Print (L"\n");
        FreePool (HashCtx);

        return EFI_SUCCESS;
}

 

上面的程序中,我们计算的是 “www.lab-z.com”的SHA256值,结果如下:

sha2562

我们可以使用【参考3】提供的在线工具进行计算,结果如下:

sha2563

可以看到,这个Protocol工作正常,能够准确的计算出 SHA256的值。

本文提到的完整的代码下载:
SHA256

参考:
1. 计算MD5 http://www.lab-z.com/uefimd5/
2. SHA-1的实现http://www.lab-z.com/sha1/
3. Hash在线计算、md5计算、sha1计算、sha256计算、sha512计算 https://1024tools.com/hash

Leonrado获得当前串口速率

最近在研究 Loenrado 的USB,在\arduino-1.8.4\hardware\arduino\avr\cores\arduino\CDC.cpp 中发现有趣的代码:

			// We check DTR state to determine if host port is open (bit 0 of lineState).
			if (1200 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
			{
#if MAGIC_KEY_POS != (RAMEND-1)
				// Backup ram value if its not a newer bootloader.
				// This should avoid memory corruption at least a bit, not fully
				if (magic_key_pos != (RAMEND-1)) {
					*(uint16_t *)(RAMEND-1) = *(uint16_t *)magic_key_pos;
				}
#endif
				// Store boot key
				*(uint16_t *)magic_key_pos = MAGIC_KEY;
				wdt_enable(WDTO_120MS);
			}

 

这段代码是用来实现 Leonrado 刷新的:通过设置当前USB 传输速度为 1200,然后让板子重启进入Bootloader响应刷写指令而无需使用板载的 Reset按钮(未来会做更具体的分析)。
_usbLineInfo.dwDTERate 这里就是当前设定的USB 通讯速度,因此我们在这里添加代码根据当前速度设定LED On Off。加入的代码段如下:

#if MAGIC_KEY_POS != (RAMEND-1)
			// For future boards save the key in the inproblematic RAMEND
			// Which is reserved for the main() return value (which will never return)
			if (_updatedLUFAbootloader) {
				// horray, we got a new bootloader!
				magic_key_pos = (RAMEND-1);
			}
#endif
//LABZ_DEBUG_START
if (300 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
{
        digitalWrite(2,HIGH);
        digitalWrite(3,LOW);
        digitalWrite(4,LOW);
        digitalWrite(5,LOW);
        digitalWrite(6,LOW);
        digitalWrite(7,LOW);
}
if (1200 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
{
        digitalWrite(2,LOW);
        digitalWrite(3,HIGH);
        digitalWrite(4,LOW);
        digitalWrite(5,LOW);
        digitalWrite(6,LOW);
        digitalWrite(7,LOW);
}
if (2400 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
{
        digitalWrite(2,LOW);
        digitalWrite(3,LOW);
        digitalWrite(4,HIGH);
        digitalWrite(5,LOW);
        digitalWrite(6,LOW);
        digitalWrite(7,LOW);
}
if (4800 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
{
        digitalWrite(2,LOW);
        digitalWrite(3,LOW);
        digitalWrite(4,LOW);
        digitalWrite(5,HIGH);
        digitalWrite(6,LOW);
        digitalWrite(7,LOW);
}
if (9600 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
{
        digitalWrite(2,LOW);
        digitalWrite(3,LOW);
        digitalWrite(4,LOW);
        digitalWrite(5,LOW);
        digitalWrite(6,HIGH);
        digitalWrite(7,LOW);
}
if (19200 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
{
        digitalWrite(2,LOW);
        digitalWrite(3,LOW);
        digitalWrite(4,LOW);
        digitalWrite(5,LOW);
        digitalWrite(6,LOW);
        digitalWrite(7,HIGH);
}
//LABZ_DEBUG_ENd
			// We check DTR state to determine if host port is open (bit 0 of lineState).
			if (1200 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
			{
#if MAGIC_KEY_POS != (RAMEND-1)
				// Backup ram value if its not a newer bootloader.
				// This should avoid memory corruption at least a bit, not fully
				if (magic_key_pos != (RAMEND-1)) {
					*(uint16_t *)(RAMEND-1) = *(uint16_t *)magic_key_pos;
				}
#endif
				// Store boot key
				*(uint16_t *)magic_key_pos = MAGIC_KEY;
				wdt_enable(WDTO_120MS);
			}

 

这样,当设定不同速度时, D2-D7 上的LED会分别亮起来。

编写一个简单的 Arduino代码

void setup() {
  // put your setup code here, to run once:
        pinMode(2,OUTPUT);
        pinMode(3,OUTPUT);
        pinMode(4,OUTPUT);
        pinMode(5,OUTPUT);
        pinMode(6,OUTPUT);
        pinMode(7,OUTPUT);

        digitalWrite(2,LOW);
        digitalWrite(3,LOW);
        digitalWrite(4,LOW);
        digitalWrite(5,LOW);
        digitalWrite(6,LOW);
        digitalWrite(7,LOW);
}

void loop() {
  // put your main code here, to run repeatedly:

}

 

比如当前的IDE 的 Serial Monitor 设定为 4800, D5上面的LED会亮

arsp

推荐 Windows 下读取 SMBIOS的工具

最近发现一个能够在 Windows下读取 SMBIOS 的代码,有需要的朋友可以参考一下。
我在 UEFI 版本的 Windows 10 X64 下测试成功,编译器为 VS2015。

smbios

具体原理是使用: GetSystemFirmwareTable 来获得系统的SMBIOS信息。其中还有SMBIOS结构体的解析代码。有需要的朋友可以参考一下。
具体代码来自:https://github.com/KunYi/DumpSMBIOS
这里可以下载源程序

DumpSMBIOS-master

此外,还放了一个我编译的 X64 Release EXE版本,有需要的朋友可以试试,

DumpSMBIOS

最后,特别感谢作者KunYi Chen。

USB Usage AC Pan

最近我在查看一款鼠标的 HID 描述符时,遇到一个定义 AC Pan 搞不清楚意思:

ac1

直接看 USB HID协议上面解释也比较简单,看不懂

ac2

鼠标是最普通的款式,上面有三个按键,左右已经滚轮下面的一个按键,此外就只有滚轮了。当然我也没有找到发出 AC Pan 的方法。

想来想去可以使用 Leonardo 来进行验证,把这个值发出来看看系统有什么变化,也能了解功能。

为此,特地修改 Arduino 鼠标的描述符,在  \libraries\Mouse\src\Mouse.cpp 中加入 AC Pan。

 

static const uint8_t _hidReportDescriptor[] PROGMEM = {



  //  Mouse

    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)  // 54

    0x09, 0x02,                    // USAGE (Mouse)

    0xa1, 0x01,                    // COLLECTION (Application)

    0x09, 0x01,                    //   USAGE (Pointer)

    0xa1, 0x00,                    //   COLLECTION (Physical)

    0x85, 0x01,                    //     REPORT_ID (1)

    0x05, 0x09,                    //     USAGE_PAGE (Button)

    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)

    0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)

    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)

    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)

    0x95, 0x03,                    //     REPORT_COUNT (3)

    0x75, 0x01,                    //     REPORT_SIZE (1)

    0x81, 0x02,                    //     INPUT (Data,Var,Abs)

    0x95, 0x01,                    //     REPORT_COUNT (1)

    0x75, 0x05,                    //     REPORT_SIZE (5)

    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)

    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)

    0x09, 0x30,                    //     USAGE (X)

    0x09, 0x31,                    //     USAGE (Y)

    0x09, 0x38,                    //     USAGE (Wheel)

    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)

    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)

    0x75, 0x08,                    //     REPORT_SIZE (8)

    0x95, 0x03,                    //     REPORT_COUNT (3)

    0x81, 0x06,                    //     INPUT (Data,Var,Rel)

    //LABZ_DEBUG_Start

     0x05, 0x0c,        //         USAGE_PAGE (Consumer Devices)

     0x0a, 0x38, 0x02,  //         USAGE (AC Pan)

     0x15, 0x81,        //         LOGICAL_MINIMUM (-127)

     0x25, 0x7f,        //         LOGICAL_MAXIMUM (127)

     0x75, 0x08,        //         REPORT_SIZE (8)

     0x95, 0x01,        //         REPORT_COUNT (1)   

     0x81, 0x06,        //         INPUT (Data,Var,Rel)

    //LABZ_DEBUG_End

    0xc0,                          //   END_COLLECTION

    0xc0,                          // END_COLLECTION

};

 

当然,做了上面的修改之后,每次发出的消息不再是4个Bytes(Button,X,Y,Wheel),而多了一个。相应的下面的函数也要进行修改,多发出一个 Pan值。

void Mouse_::move(signed char x, signed char y, signed char wheel,signed char pan)

{

               uint8_t m[5];

               m[0] = _buttons;

               m[1] = x;

               m[2] = y;

               m[3] = wheel;

        m[4] = pan;

               HID().SendReport(1,m,5);

}

 

 

修改好库之后,再编写一个测试代码,使用 Pin 7 8 拉低来发出 Pan -1 和 +1

 

#include <Mouse.h>



// set pin numbers for the five buttons:

const int upButton = 2;

const int downButton = 3;

const int leftButton = 4;

const int rightButton = 5;

const int mouseButton = 6;

const int pan1 = 7;

const int pan2 = 8;



int range = 5;              // output range of X or Y movement; affects movement speed

int responseDelay = 10;     // response delay of the mouse, in ms





void setup() {

  // initialize the buttons' inputs:

  pinMode(upButton, INPUT_PULLUP);

  pinMode(downButton, INPUT_PULLUP);

  pinMode(leftButton, INPUT_PULLUP);

  pinMode(rightButton, INPUT_PULLUP);

  pinMode(mouseButton, INPUT_PULLUP);

  pinMode(pan1, INPUT_PULLUP);

  pinMode(pan2, INPUT_PULLUP);

  // initialize mouse control:

  Mouse.begin();

}



void loop() {

  // read the buttons:

  int upState = digitalRead(upButton);

  int downState = digitalRead(downButton);

  int rightState = digitalRead(rightButton);

  int leftState = digitalRead(leftButton);

  int clickState = digitalRead(mouseButton);



  // calculate the movement distance based on the button states:

  int  xDistance = (leftState - rightState) * range;

  int  yDistance = (upState - downState) * range;



  // if X or Y is non-zero, move:

  if ((xDistance != 0) || (yDistance != 0)) {

    Mouse.move(xDistance, yDistance, 0,0);

  }



  if(digitalRead(pan1)==LOW) {

    Mouse.move(xDistance, yDistance, 0,1);

    Mouse.move(0, 0, 0,0);

    }

  if(digitalRead(pan2)==LOW) {

    Mouse.move(xDistance, yDistance, 0,-1);

    Mouse.move(0, 0, 0,0);

    }



  // if the mouse button is pressed:

  if (clickState == HIGH) {

    // if the mouse is not pressed, press it:

    if (!Mouse.isPressed(MOUSE_LEFT)) {

      Mouse.press(MOUSE_LEFT);

    }

  }

  // else the mouse button is not pressed:

  else {

    // if the mouse is pressed, release it:

    if (Mouse.isPressed(MOUSE_LEFT)) {

      Mouse.release(MOUSE_LEFT);

    }

  }



  // a delay so the mouse doesn't move too fast:

  delay(responseDelay);

}

 

最终的实验表明, AC Pan 是类似水平方向的滚轮,在使用 Excel 这样的软件经常需要水平方向的滚动用这个功能会很方便。

修改后的完整库下载:

Mouse

 

Step to UEFI (144)CryptoPkg 的使用

UDK2017 提供了一个加密解密库,在\UDK2017\CryptoPkg下面,本文介绍如何配置让这个 Package 能够使用。

crypto

具体配置方法可以在\UDK2017\CryptoPkg\Library\OpensslLib\OpenSSL-HOWTO.txt文件中看到,简单的说就是要去https://www.openssl.org/source/ 下载一套 OpenSll 的Source Code,加在目录中。虽然稳重说明要最新的版本,但是根据我的实验最新版本编译不过(有文件找不到,应该是不同的版本之间架构存在比较大的差别导致的)。经过实验,https://www.openssl.org/source/snapshot/ 下面的openssl-1.1.0-stable-SNAP-20180129.tar.gz 是可以使用的。
安装方法是:解压下载的文件,然后将全部内容解压到UDK2017\CryptoPkg\Library\OpensslLib 目录下的 Openssl 目录中:

crypto2

之后就可以按照正常编译 Package的方法进行编译
build -a X64 -p CryptoPkg\CryptoPkg.dsc
在这个Package中自带了一个测试的 Application,这是一个简单的自检程序,编译之后可以在Nt32环境下运行,结果如下:

crypto3

运行这个 Application 能够表明 CryptPkg 工作正常,对于实现 UEFI 下面加密解密有兴趣的朋友可以更深入的进行研究。

Arduino 控制USB设备(9) FTDI串口

最近又开始玩 USB Host 模块,尝试 FTDI 的 USB 转串口。在实验之前,你需要确定手上的是 FTDI 的芯片,在设备管理器中可以简单的判断:

fdti

在 USB Host 库中,给出了一个 FTDI 的例子,这个例子实现的是 LoopBack 的功能:

#include <cdcftdi.h>
#include <usbhub.h>

#include "pgmstrings.h"

// Satisfy the IDE, which needs to see the include statment in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#include <SPI.h>
#endif

class FTDIAsync : public FTDIAsyncOper
{
public:
    uint8_t OnInit(FTDI *pftdi);
};

uint8_t FTDIAsync::OnInit(FTDI *pftdi)
{
    uint8_t rcode = 0;

    rcode = pftdi->SetBaudRate(115200);

    if (rcode)
    {
        ErrorMessage<uint8_t>(PSTR("SetBaudRate"), rcode);
        return rcode;
    }
    rcode = pftdi->SetFlowControl(FTDI_SIO_DISABLE_FLOW_CTRL);

    if (rcode)
        ErrorMessage<uint8_t>(PSTR("SetFlowControl"), rcode);

    return rcode;
}

USB              Usb;
//USBHub         Hub(&Usb);
FTDIAsync        FtdiAsync;
FTDI             Ftdi(&Usb, &FtdiAsync);

uint32_t next_time;

void setup()
{
  Serial.begin( 115200 );
#if !defined(__MIPSEL__)
  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
  Serial.println("Start");

  if (Usb.Init() == -1)
      Serial.println("OSC did not start.");

  delay( 200 );

  next_time = millis() + 5000;
}

void loop()
{
    Usb.Task();

    if( Usb.getUsbTaskState() == USB_STATE_RUNNING )
    {
        uint8_t  rcode;
        char strbuf[] = "DEADBEEF";
        //char strbuf[] = "The quick brown fox jumps over the lazy dog";
        //char strbuf[] = "This string contains 61 character to demonstrate FTDI buffers"; //add one symbol to it to see some garbage
        Serial.print(".");

        rcode = Ftdi.SndData(strlen(strbuf), (uint8_t*)strbuf);

	if (rcode)
            ErrorMessage<uint8_t>(PSTR("SndData"), rcode);

        delay(50);

        uint8_t  buf[64];

        for (uint8_t i=0; i<64; i++)
            buf[i] = 0;

        uint16_t rcvd = 64;
        rcode = Ftdi.RcvData(&rcvd, buf);

        if (rcode && rcode != hrNAK)
            ErrorMessage<uint8_t>(PSTR("Ret"), rcode);

        // The device reserves the first two bytes of data
        //   to contain the current values of the modem and line status registers.
        if (rcvd > 2)
            Serial.print((char*)(buf+2));

        delay(10);
    }
}

 

使用的时候,需要将模块的 RX 和 TX 接到一起:

ftdt

运行结果:

ftdiloop

接下来试试实现接收,将2个USB 串口接在一起,RX/TX交叉,GND也要接在一起

ft3

最好先在 PC 上确定连接正确能够正常收发:

ftdi1

接收的代码如下:

#include <cdcftdi.h>
#include <usbhub.h>

#include "pgmstrings.h"

// Satisfy the IDE, which needs to see the include statment in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#include <SPI.h>
#endif

class FTDIAsync : public FTDIAsyncOper
{
public:
    uint8_t OnInit(FTDI *pftdi);
};

uint8_t FTDIAsync::OnInit(FTDI *pftdi)
{
    uint8_t rcode = 0;

    rcode = pftdi->SetBaudRate(115200);

    if (rcode)
    {
        ErrorMessage<uint8_t>(PSTR("SetBaudRate"), rcode);
        return rcode;
    }
    rcode = pftdi->SetFlowControl(FTDI_SIO_DISABLE_FLOW_CTRL);

    if (rcode)
        ErrorMessage<uint8_t>(PSTR("SetFlowControl"), rcode);

    return rcode;
}

USB              Usb;
//USBHub         Hub(&Usb);
FTDIAsync        FtdiAsync;
FTDI             Ftdi(&Usb, &FtdiAsync);

uint32_t next_time;

void setup()
{
  Serial.begin( 115200 );
#if !defined(__MIPSEL__)
  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
  Serial.println("Start");

  if (Usb.Init() == -1)
      Serial.println("OSC did not start.");

  delay( 200 );

  next_time = millis() + 5000;
}

void loop()
{
    Usb.Task();

    if( Usb.getUsbTaskState() == USB_STATE_RUNNING )
    {
        uint8_t  rcode;
        uint8_t  buf[64];

        for (uint8_t i=0; i<64; i++)
            buf[i] = 0;

        uint16_t rcvd = 64;
        rcode = Ftdi.RcvData(&rcvd, buf);

        if (rcode && rcode != hrNAK)
            ErrorMessage<uint8_t>(PSTR("Ret"), rcode);

        if (rcvd>2) {
          for (int Index=0;Index<rcvd;Index++) {
              Serial.print(buf[Index],HEX);
              Serial.print(" ");
          }
        }  
    }
}

 

最后,从PC端可以发送,在 Arduino 端可以正常解析出来

fdt3

Step to UEFI (143)Windows 下BootOrder研究

之前介绍了 Shell 下 BootOrder 的一些事情,这次介绍一下 Windows中如何取得和更改这个设定。Windows中相关API是SetFirmwareEnvironmentVariable 和GetFirmwareEnvironmentVariable (还有~Ex版)【参考1】【参考2】。需要注意,只有在 UEFI Based 系统上才可以使用上述API。
上面两个 API 的原型:

DWORD WINAPI GetFirmwareEnvironmentVariable( //取得Variable
_In_ LPCTSTR lpName, //变量名称
_In_ LPCTSTR lpGuid, //变量 GUID
_Out_ PVOID pBuffer, //读取结果放在pBuffer指向的内存中
_In_ DWORD nSize //给出pBuffer内存大小
);

返回值如果为0 ,表示失败,可以用GetLastError获取原因;如果返回值不为0,则是成功,该值应该是成功写入的字节数。

BOOL WINAPI SetFirmwareEnvironmentVariable( //设置 Variable
_In_ LPCTSTR lpName, //变量名称
_In_ LPCTSTR lpGuid, //变量 GUID
_In_ PVOID pBuffer, //要设置的内容放在pBuffer指向的内存中
_In_ DWORD nSize //给出pBuffer 指向内存的大小
);

返回值如果为0 ,表示失败,可以用GetLastError获取原因;如果返回值不为0,则是成功,该值应该是成功写入的字节数。

从上面的原型可以看出来,并没有给出Variable 大小的方法。实验如果给一个很小的Buffer会收到 7A 错误。因此在调用之前最好开一个足够大的空间以便使用(相比Windows可以分配的内存,Variable 小的可怜)。还有特别需要注意的地方是:使用这个 API 除了使用 管理员权限运行,还需要提升权限才可以。具体的代码来自【参考3】。
下面的代码首先用GetFirmwareEnvironmentVariable取得BootOrder的Variable,然后修改顺序,再使用SetFirmwareEnvironmentVariable把修改后的Variable写入,最后再读取显示一次:

// getfwenv.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"

#define VariableGuidStr      "{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}"
#define BootOrderStr         "BootOrder"

DWORD    dwRet = 0;

BOOL adjusttoken()
{
	HANDLE htoken;


	if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &htoken))
	{
		size_t           s = sizeof(TOKEN_PRIVILEGES) + 2 * sizeof(LUID_AND_ATTRIBUTES);
		TOKEN_PRIVILEGES *p = (PTOKEN_PRIVILEGES)malloc(s);


		if (!LookupPrivilegeValue(NULL, SE_SYSTEM_ENVIRONMENT_NAME, &(p->Privileges[0].Luid)) ||
			!LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &(p->Privileges[1].Luid)) ||
			!LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &(p->Privileges[2].Luid)))
		{
			printf("failed to LookupPrivilegeValue error code : %d \r\n", GetLastError());
			free(p);
			return FALSE;
		}
		p->PrivilegeCount = 3;


		for (int i = 0; i < 3; ++i)
		{
			p->Privileges[i].Attributes = SE_PRIVILEGE_ENABLED;
		}

		 
		if (!AdjustTokenPrivileges(htoken, FALSE, p, (DWORD) s, NULL, NULL) || GetLastError() != ERROR_SUCCESS)
		{
			printf("AdjustTokenPrivileges failed! error code : %d \r\n", GetLastError());
			free(p);
			return FALSE;
		}
		//do something here...
		free(p);
	}
	else
	{
		printf("Open process token failed! error code : %d \r\n", GetLastError());
		return FALSE;
	}


	return TRUE;
}

int main()
{
	byte Buffer[2000];
	byte *pBuffer;
	DWORD  iBufferSize=sizeof(Buffer);
	DWORD dwRet;

	adjusttoken();

	pBuffer = Buffer;

	//Get BootOrder
	dwRet = GetFirmwareEnvironmentVariable(
		_T(BootOrderStr),
		_T(VariableGuidStr),
		pBuffer,
		iBufferSize);
	printf("GetFirmwareEnvironmentVariable return value:%x\n", dwRet);
	for (DWORD i = 0; i < dwRet;i++)
		printf("%X ",pBuffer[i]);
	printf("\n");

	//Set boot from shell
	pBuffer[0] = 0;	pBuffer[2] = 4;

	//Set new Bootorder
	dwRet = SetFirmwareEnvironmentVariable(
		_T(BootOrderStr),
		_T(VariableGuidStr),
		pBuffer,
		dwRet);
	printf("SetFirmwareEnvironmentVariable return value:%x\n", dwRet);

	//Check Bootorder again
	dwRet = GetFirmwareEnvironmentVariable(
		_T(BootOrderStr),
		_T(VariableGuidStr),
		pBuffer,
		iBufferSize);
	printf("GetFirmwareEnvironmentVariable again, return value:%x\n", dwRet);

	for (DWORD i = 0; i < dwRet; i++)
		printf("%X ", pBuffer[i]);

	getchar();

    return 0;
}

 

最终运行结果:

winorder

在我的KBL-R HDK上重启之后就会直接进入 Shell (原因可以在前一篇看到)

编译好的X64 Application和Sourcecode
getfwenv

从实验上来看,所有的变量都是可以读取到,但是不知道为什么很多变量在 Windows下无法写(写保护错误)。如果能够做到写入,那么就可以自动的改变Setup的设定。另外,我无法判断这个写保护错误是Windows本身造成的还是BIOS造成的。如果未来有机会会继续研究下去,有兴趣的朋友也可以在下面给我留言说出你的想法。

参考:
1. https://msdn.microsoft.com/en-us/library/windows/desktop/ms724934(v=vs.85).aspx
2. https://msdn.microsoft.com/en-us/library/windows/desktop/ms724325(v=vs.85).aspx

Step to UEFI (142)UEFI 和 Arduino 的 HID 通讯

如果想让 Arduino 和 UEFI 进行交互,可以使用USB串口驱动,比如:【参考1】提供了一个FTDI的UEFI驱动,如果想用在 Arduino 上需要一些修改。此外,可行的方案就是通过USB HID 直接和 Arduino Leonardo进行通讯(Arduino Uno 也是可以的,但是需要修改 16U2的Firmware,比较麻烦和折腾)。本文就介绍一下具体的实现。

首先说一下硬件部分,在一个 Proto Shied上连接了一个串口转USB的小卡,将 TX/RX/GND 三根对应的接在一起就能正常工作了。

ehid1

软件方面,Arduino代码如下使用了NicoHood 的 HID库。代码就是不断检查 HID是否收到消息,如果有,那么就从Serial1 发送出去。

/*

  Copyright (c) 2014-2015 NicoHood

  See the readme for credit to other people.



  Advanced RawHID example



  Shows how to send bytes via RawHID.

  Press a button to send some example values.



  Every received data is mirrored to the host via Serial.



  See HID Project documentation for more information.

  https://github.com/NicoHood/HID/wiki/RawHID-API

*/



#include "HID-Project.h"





// Buffer to hold RawHID data.

// If host tries to send more data than this,

// it will respond with an error.

// If the data is not read until the host sends the next data

// it will also respond with an error and the data will be lost.

uint8_t rawhidData[64];



void setup() {



  Serial1.begin(115200);



  // Set the RawHID OUT report array.

  // Feature reports are also (parallel) possible, see the other example for this.

  RawHID.begin(rawhidData, sizeof(rawhidData));



}



void loop() {



  // Check if there is new data from the RawHID device

  auto bytesAvailable = RawHID.available();

  int c;

  if (bytesAvailable)

  {

    // Mirror data via Serial

    while (bytesAvailable--) {

      c=(RawHID.read()&0xFF);

      if (c / 16 ==0) {Serial1.print("0");}

      Serial1.print(c,HEX);

      Serial1.print("  ");

    }

  }

}

 

 

UEFI  Shell Application的原理就是使用 USBIO枚举系统中的全部USB 设备,找到VID/PID是Arduino Leonardo 的设备,再检测InterfaceClass是否为03 (HID),如果是那么就打开,用UsbSetReportRequest 发送一段随机数过去。完整代码如下:

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

#include <stdlib.h>

extern EFI_BOOT_SERVICES         *gBS;

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

//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;
}

UINTN GetUSB()
{
  EFI_STATUS    Status;
  UINTN         HandleIndex, HandleCount;
  EFI_HANDLE    *DevicePathHandleBuffer = NULL;
  EFI_USB_IO_PROTOCOL          *USBIO;
  EFI_USB_DEVICE_DESCRIPTOR     DeviceDescriptor;
  EFI_USB_INTERFACE_DESCRIPTOR  IfDesc;
  UINT8                         arr[64];
  UINT8                         i;

  
  //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;
      }

    //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 EFI_SUCCESS;
     }
    
    //Find the device which VID and PID is Arduino Leonrado     
    if ((0x2341==DeviceDescriptor.IdVendor) && (0x8036==DeviceDescriptor.IdProduct))
        {
                //Show the PID and VID
                Print(L"Found a Leonrado Device VendorID = %04X, ProductID = %04X\n", 
                        DeviceDescriptor.IdVendor, 
                        DeviceDescriptor.IdProduct);       
                //
                // Get Interface Descriptor
                //
                Status = USBIO->UsbGetInterfaceDescriptor (USBIO, &IfDesc);
                if (EFI_ERROR (Status)) 
                {
                        Print(L"ERROR : Usb Get Interface Descriptor fail.\n");
                        return EFI_SUCCESS;
                }
                
                //Check the Interface Class for HID
                if (0x03 == IfDesc.InterfaceClass) 
                {
                        Print(L"Found HID device, send the data!\n");
  
                        for (i=0;i<64;i++) {
                           arr[i]=(UINT8) rand();
                           Print(L"%2X  ",arr[i]);
                        }
                        Print(L"\n");
                        Status=UsbSetReportRequest (
                                USBIO,
                                IfDesc.InterfaceNumber,
                                0,  //Report ID
                                HID_OUTPUT_REPORT,
                                sizeof(arr),
                                arr);    
                        if (EFI_ERROR (Status)) 
                        {
                                Print(L"Error=[%r]\n",Status);
                                return EFI_SUCCESS;
                        }        
                }
        }        
  }
  gBS->FreePool(DevicePathHandleBuffer);       
  return HandleCount;
}


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

 

运行结果,Shell 下显示:

hidr

PC端串口接收到的数据如下:

ehid3

完整的代码和 X64 EFI 文件下载:

HIDTest

参考:

  1. http://www.lab-z.com/stufdti/ FTDI 串口驱动

AP3216C 模块

AP3216C 模块的核心就是这个芯片本身。这颗芯片集成了光强传感器(ALS:Ambient Light Sensor),接近传感器(PS: Proximity Sensor),还有一个红外LED(IR LED)。这个芯片设计的用途是给手机之类的使用,比如:返回当前环境光强以便调整屏幕亮度;用户接听电话时,将手机放置在耳边后,自动关闭屏幕避免用户误触碰。

ap32

可能是因为模块接线非常简单,我在网上找不到模块的电路图,只能用芯片的DataSheet对照进行查看。
从上到下分别是 :
VLED IR LED的供电,IR LED 电流最高为 20ma。使用 3.3v给模块和IR LED 同时供电时,在IR LED 上串联了一个200欧姆的电阻,这样保证电流不会超过20ma。
GND 地
VCC 模块供电,特别注意:最高 3.6V,对于 Arduino 来说,只能选择 3.3V供电输出
SCL I2C 的 CLOCK,对应 A5
SDA I2C 的 DATA,对应 A4
INT 中断输出,可以通知 Arduino有数据。对于轮询,无需使用。

ap321

参考【参考1】, 可以使用的代码如下:

// Interfacing the AP3216 light / proximity sensor with Arduino UNO

// By RoboRemo
// www.roboremo.com

// Big thanks to ICStation for providing the AP3216 sensor
// http://www.icstation.com/ap3216-ambient-light-sensorals-proximity-sensorps-p-7958.html

// Command examples:
// "write 0x00 0x01\n" - will write value 0x01 to the register 0x00
// "read 0x0C\n" - will read the value from register 0x0C
// "als start\n" - will start streaming the value from the ALS (ambient light sensor)
// "ps start\n" - will start streaming the value from the PS (proximity sensor)
// "stop\n" - will stop streaming the ALS / PS data.

// Commands can be sent using Serial Monitor / Terminal,
// Or using the RoboRemo app from Google Play.

// RoboRemo app can also display a nice plot of the ALS / PS data,
// and also log to a file on the sdcard of the phone.


// Hardware wiring:
// Arduino     AP3216
//             VLED --,
// GND ------- GND   |R| 240 Ohm
// 3.3V ------ VCC ---'
// A5 -------- SCL
// A4 -------- SDA
            


long baud = 115200;

#include <Wire.h>


char cmd[100];
int cmdIndex;

bool als_on = false;
bool ps_on = false;



boolean cmdStartsWith(const char *st) { // checks if cmd starts with st
  for(int i=0; ; i++) {
    if(st[i]==0) return true;
    if(cmd[i]==0) return false;
    if(cmd[i]!=st[i]) return false;;
  }
  return false;
}



int hexCharToInt(char c) {
  if(c>='a') return (c-'a')+10;
  if(c>='A') return (c-'A')+10;
  return c-'0';
}

String hexByteToString(int val) {
  char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8','9', 'A', 'B', 'C', 'D', 'E', 'F'};

  char a = digits[(val/16) %16];
  char b = digits[val%16];
  
  return (String) "" + a + b;
}



void alsStart() {
  AP3216_write(0x00, 0x01);
  als_on = true;
}


void alsPsStop() {
  als_on = false;
  ps_on = false;
  AP3216_write(0x00, 0x00);
}


void psStart() {
  AP3216_write(0x00, 0x02);
  ps_on = true;
}




void AP3216_write(int regAddress, int value) {
  Wire.beginTransmission(0x1E); // I2C Address of AP3216 sensor is 0x1E
  Wire.write(regAddress);
  Wire.write(value);
  Wire.endTransmission(); 
}

int AP3216_read(int regAddress) {
  Wire.beginTransmission(0x1E); // I2C Address of AP3216 sensor is 0x1E
  Wire.write(regAddress);
  Wire.endTransmission();
  Wire.requestFrom(0x1E, 1, true);
  return Wire.read() & 0xFF;
}


void exeCmd() {

  if( cmdStartsWith("read 0x") ) {  // example: read 0x1A
    int a = hexCharToInt(cmd[7]); // '1' -> 1
    int b = hexCharToInt(cmd[8]); // 'A' -> 10
    
    int regAddress = (a*16)+b; // 0x1A = 26

    int regValue = AP3216_read(regAddress);
    
    Serial.print( (String)"reg_0x");
    Serial.print( hexByteToString(regAddress) );
    Serial.print(" = ");
    Serial.print( hexByteToString(regValue) );
    Serial.print("\n");
  }


  if( cmdStartsWith("write 0x") ) {  // example: write 0x1A 0x55
    int a = hexCharToInt(cmd[8]); // '1' -> 1
    int b = hexCharToInt(cmd[9]); // 'A' -> 10
    int regAddress = (a*16)+b; // 0x1A = 26

    a = hexCharToInt(cmd[13]);
    b = hexCharToInt(cmd[14]);
    int regValue = (a*16)+b;
    
    AP3216_write(regAddress, regValue); 

    Serial.print( (String)"reg_0x");
    Serial.print( hexByteToString(regAddress) );
    Serial.print(" <- ");
    Serial.print( hexByteToString(regValue) );
    Serial.print("\n");
  }

  if( cmdStartsWith("als start") ) {
    alsStart();
  }

  if( cmdStartsWith("stop") ) {
    alsPsStop();
  }


  if( cmdStartsWith("ps start") ) {
    psStart();
  }

  
}


void setup() {
  Wire.begin();
  Serial.begin(baud);
  cmdIndex = 0;
}

void loop() {

  while( Serial.available() ) {
    char c = Serial.read();

    if(c=='\n' || c=='\r') {
      cmd[cmdIndex] = 0; // end cmd string with 0
      exeCmd();  // execute the command
      cmdIndex = 0; // reset the cmdIndex
    } else {      
      cmd[cmdIndex] = c; // append c to the cmd string
      if(cmdIndex<99) cmdIndex++;
    } 
  }

  if(als_on) {
    int a = AP3216_read(0x0D); // ALS Data HIGH Byte
    int b = AP3216_read(0x0C); // ALS Data LOW Byte

    long alsValue = a;
    alsValue = alsValue << 8;
    alsValue = alsValue + b;
    
    Serial.print("als ");
    Serial.print(alsValue);
    Serial.print("\n");
    delay(100);
  }


  if(ps_on) {
    int a = AP3216_read(0x0F) & 0b00111111; // PS Data HIGH 6 bits
    int b = AP3216_read(0x0E) & 0b00001111; // PS Data LOW 4 bits
    long psValue = (a << 4) + b;
    Serial.print("ps ");
    Serial.print(psValue);
    Serial.print("\n");
    delay(13);
  }
   
}

 

使用方法:
串口输入 als start 获取当前的光强
串口输入 ps start获得当前接近传感器的数值
串口输入 stop 停止输出

ap322

目前这个模块没有成熟的库供使用,上述代码只是实现一个大概的功能,如果应用在产品上,还需要根据需求对照DataSheet进行详细的调试。
参考:
1. http://www.roboremo.com/reading-ap3216-with-arduino.html

Step to UEFI (141)Windows 重启变关机的实现

最近看到一篇文章【参考1】,提到了修改 ACPI可以做到Windows重启变成关机【参考1】,从原理上说,Windows实现重启的方法是从 ACPI Table的FADT Table中Reset_Reg 和RESET_VALUE中读取到做重启的Port和Value。当需要重启的时候,将 Value写入Port即可。这个事情很多年前我碰到过问题,那还是XP的时代,当时遇到的问题是无法正常 Restart系统,后来研究发现当时的 ACPI Table 写的是向EC 的Port写Command的方式进行重启(同一个Chipset上的BIOS我们尽量会重用),但是恰巧EC没有实现这个功能。最后修改为正常的向 0xCF9 写入 06 即可解决)。类似的,Windows关机也是从 ACPI 中取得Port然后写入特定值。
具体的Table 定义在 ACPI Spec 中:

1. 我们需要确定 RSD PTR
r2s1

2. 找到 Extended System Description Table (Signature 是 “XSDT”)
r2s2

3. 在 XSDT 中查找到 Fixed ACPI Description Table (Signature 是 “FADT”),其中的 RESET_REG金额 RESET_VALUE就是我们需要的
r2s3

4. 将 FADT 中的 RESET_REG 修改为Pm1aCnBlk+1,将RESET_VALUE修改为0x3C(例如,我们读取到的Pm1aCnBlk == 1804h,对这个Port写入 3C00h 能够实现关机操作,那么对 1805h写入 3Ch同样能够实现关机)。特别注意的是,RESET_REG 是一个结构体,其中的 RegisterBitWidth 需要设置为 18 (默认为8)
r2s4

5. 重新计算 FADT 的 Checksum 填写回去。

完整的代码:

/** @file
    A simple, basic, application showing how the Hello application could be
    built using the "Standard C Libraries" from StdLib.

    Copyright (c) 2010 - 2011, 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.

    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 <Library/BaseLib.h>
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/BaseMemoryLib.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include "acpi.h"
#include "acpi61.h"

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_RUNTIME_SERVICES      *gRT;
extern EFI_SYSTEM_TABLE          *gST;


EFI_GUID        gEfiAcpi20TableGuid =   { 0x8868E871, 0xE4F1, 0x11D3, 
                        { 0xBC, 0x22, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 }};
//              
//Calcaulate a checksum
//              
void SetFADTChecksum(EFI_ACPI_6_1_FIXED_ACPI_DESCRIPTION_TABLE       *pFADT) {
        UINT8   c=0;
        UINT8   *p;
        UINT32  i;
        
        p=(UINT8*)pFADT;
        pFADT->Header.Checksum=0;
        for (i=0;i<pFADT->Header.Length;i++)    {
                c=c + *p;
                p++;
        }
        pFADT->Header.Checksum=0-c;
}

/***
  Demonstrates basic workings of the main() function by displaying a
  welcoming message.

  Note that the UEFI command line is composed of 16-bit UCS2 wide characters.
  The easiest way to access the command line parameters is to cast Argv as:
      wchar_t **wArgv = (wchar_t **)Argv;

  @param[in]  Argc    Number of argument tokens pointed to by Argv.
  @param[in]  Argv    Array of Argc pointers to command line tokens.

  @retval  0         The application exited normally.
  @retval  Other     An error occurred.
***/
INTN
EFIAPI
ShellAppMain (
  IN UINTN Argc,
  IN CHAR16 **Argv
  )
{
        EFI_STATUS      Status;
        EFI_ACPI_DESCRIPTION_HEADER                     *XSDT;
        EFI_ACPI_6_1_ROOT_SYSTEM_DESCRIPTION_POINTER    *RSDP;
        UINT8           *p;
        UINTN           Index;
        UINT64          *Entry;
        EFI_ACPI_6_1_FIXED_ACPI_DESCRIPTION_TABLE       *pFADT;
        
        //1. Find RSDP
        Status=EfiGetSystemConfigurationTable(&gEfiAcpi20TableGuid,(VOID**)&RSDP);
        if(EFI_ERROR(Status)) {
                Print(L"Can't find Acpi Table\n");
                return 0;
        }
        
        //2. Find XSDT
        Print(L"RSDP address [%X]\n",RSDP);
        Print(L"XSDT address [%X]\n",RSDP->XsdtAddress);
        XSDT=(EFI_ACPI_DESCRIPTION_HEADER*)RSDP->XsdtAddress;
        Print(L"XSDT information\n");
        p=(UINT8*)XSDT;
        
        //Show some DSDT information
        Print(L" Signature [%c%c%c%c]\n",*p,*(p+1),*(p+2),*(p+3));
        
        //3.Find entries
        Entry=(UINT64*)&XSDT[1];
        Print(L" Entry 0 @[0x%x]\n",Entry);
        for (Index=0;Index<(XSDT->Length-sizeof(EFI_ACPI_DESCRIPTION_HEADER))/8;Index++) {
           //Print(L" Entry [0x%x]",Index);
           p=(UINT8*)(*Entry);
           //You can show every signature here
           //Print(L" [%x][%c%c%c%c]\n",*Entry,*p,*(p+1),*(p+2),*(p+3));
           if ((*p=='F')&&(*(p+1)=='A')&&(*(p+2)=='C')&&(*(p+3)=='P')) {
                   pFADT=(EFI_ACPI_6_1_FIXED_ACPI_DESCRIPTION_TABLE*) p;
                   Print(L"  Found FADT @[0x%X]\n",*Entry);
                   Print(L"      ResetReg[0x%X]\n",pFADT->ResetReg.Address);
                   Print(L"    ResetValue[0x%X]\n",pFADT->ResetValue);
                   Print(L"   XPm1aCntBlk[0x%X]\n",pFADT->XPm1aCntBlk.Address);
                   Print(L" Changing table value\n");
                   pFADT->ResetReg.RegisterBitWidth=16;
                   pFADT->ResetReg.Address=pFADT->XPm1aCntBlk.Address+1;
                   pFADT->ResetValue=0x3C;
                   Print(L"      ResetReg[0x%X]\n",pFADT->ResetReg.Address);
                   Print(L"    ResetValue[0x%X]\n",pFADT->ResetValue);

                   SetFADTChecksum(pFADT);
           }
           Entry++;
        }
        return 0;
}

 

运行结果:
r2s5
执行这个 Application 之后,再Exit进入 Windows(不可以重启进入 Windows),用 RW 检查 Acpi Table 结果如下:
r2s6
最后,我们从菜单上选择 restart,会发现系统没有 reset 而是关机了。这样就实现了重启变关机。

完整的代码下载:
Reset2Shutdown

X64 Shell Application 下载:
r2s

参考:
1. http://blog.csdn.net/miss_lazygoat/article/details/48161645 ACPI table遍历并实现重启变关机