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键盘是通过时间中断来进行检查和处理的.

UEFI编写的迷宫游戏

最近在 GitHub 上看到了一个 UEFI 编写的迷宫游戏(作者 Haodong Liu , https://github.com/liute62/Firmware-UEFI-Maze-Game), 感觉界面不是很漂亮,于是拿过来进行简单的修改:

maze

修改之前的原始代码和资料:
Firmware-UEFI-Maze-Game-master

修改之后的代码
uefimaze

用的按键是上下左右,ECS 还有 PageUp(类似 Enter的功能)。

VS2015 下面编译 SPB 驱动

差不多3年前的这个时候,我写过一篇介绍如何在 VS2013下面编译 Windows Driver Sample的文章【参考1】。今年的这个时候,我再一次尝试在 VS2015下面编译驱动。

For building Windows Sample you need below software

1. Windows 10 RS2: 15063.0.170317-1834.RS2_RELEASE_CLIENTPRO-CORE_OEMRET_X64FRE_EN-US (Note: VS2015 can’t be installed on Windows7)
2. 15063.0.170317-1834.rs2_release_amd64fre_WDK.iso
3. 15063.0.170317-1834.rs2_release_WindowsSDK.iso
4. Visual_Studio_Pro_2015_English.iso

1.Install Windows RS2
2.Install VS2015 (2 Hours),全默认配置

3.Install WDK 到默认的路径下

image001

安装完成后,用 VS2015打开驱动工程文件。编译会出现下面的错误

“—— Build started: Project: SpbTestTool (Exe\SpbTestTool), Configuration: Debug Win32 ——
Building ‘SpbTestTool’ with toolset ‘WindowsApplicationForDrivers10.0’ and the ‘Desktop’ target platform.
TRACKER : error TRK0005: Failed to locate: “CL.exe”. The system cannot find the file specified.

—— Build started: Project: SpbTestTool (Sys\SpbTestTool), Configuration: Debug Win32 ——
Building ‘SpbTestTool’ with toolset ‘WindowsKernelModeDriver10.0’ and the ‘Universal’ target platform.
Stamping .\Debug\\SpbTestTool.inf [Version] section with DriverVer=05/18/2017,18.15.36.668
TRACKER : error TRK0005: Failed to locate: “CL.exe”. The system cannot find the file specified.

========== Build: 0 succeeded, 2 failed, 0 up-to-date, 0 skipped ==========”

根据我的研究,这是因为VS2015默认安装没有带C++编译器(有点莫名其妙)

image002

image003

image004

再进行一次编译:
—— Build started: Project: SpbTestTool (Exe\SpbTestTool), Configuration: Debug x64 ——
Building ‘SpbTestTool’ with toolset ‘WindowsKernelModeDriver10.0’ and the ‘Desktop’ target platform.
command.cpp
command.cpp : fatal error C1083: Cannot open include file: ‘C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\shared\warning.h’: No such file or directory
main.cpp
main.cpp : fatal error C1083: Cannot open include file: ‘C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\shared\warning.h’: No such file or directory
util.cpp
util.cpp : fatal error C1083: Cannot open include file: ‘C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\shared\warning.h’: No such file or directory
Generating Code…
—— Build started: Project: SpbTestTool (Sys\SpbTestTool), Configuration: Debug x64 ——
Building ‘SpbTestTool’ with toolset ‘WindowsKernelModeDriver10.0’ and the ‘Universal’ target platform.
Stamping .\x64\Debug\\SpbTestTool.inf [Version] section with DriverVer=05/18/2017,22.28.17.712
driver.cpp
driver.cpp : fatal error C1083: Cannot open include file: ‘C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\shared\warning.h’: No such file or directory
device.cpp
device.cpp : fatal error C1083: Cannot open include file: ‘C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\shared\warning.h’: No such file or directory
peripheral.cpp
peripheral.cpp : fatal error C1083: Cannot open include file: ‘C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\shared\warning.h’: No such file or directory
Generating Code…
========== Build: 0 succeeded, 2 failed, 0 up-to-date, 0 skipped ==========

这个错误是因为没有安装 SDK 导致的(VS2015自带SDK,但是可能版本和 WDK 的不匹配,所以有问题)。再安装 SDK,之后 Build again
—— Build started: Project: SpbTestTool (Exe\SpbTestTool), Configuration: Debug x64 ——
Building ‘SpbTestTool’ with toolset ‘WindowsKernelModeDriver10.0’ and the ‘Desktop’ target platform.
command.cpp
main.cpp
util.cpp
Generating Code…
SpbTestTool.vcxproj -> C:\spb\SpbTestTool\exe\x64\Debug\SpbTestTool.exe
SpbTestTool.vcxproj -> x64\Debug\SpbTestTool.pdb (Full PDB)
Inf2Cat task was skipped as there were no inf files to process

—— Build started: Project: SpbTestTool (Sys\SpbTestTool), Configuration: Debug x64 ——
Building ‘SpbTestTool’ with toolset ‘WindowsKernelModeDriver10.0’ and the ‘Universal’ target platform.
Stamping .\x64\Debug\\SpbTestTool.inf [Version] section with DriverVer=05/19/2017,0.46.12.487
driver.cpp
device.cpp
peripheral.cpp
Generating Code…
SpbTestTool.vcxproj -> C:\spb\SpbTestTool\sys\x64\Debug\SpbTestTool.sys
SpbTestTool.vcxproj -> x64\Debug\SpbTestTool.pdb (Full PDB)
Done Adding Additional Store
Successfully signed: C:\spb\SpbTestTool\sys\x64\Debug\SpbTestTool.sys

Driver is a Universal Driver.
……………………
Signability test complete.

Errors:
None

Warnings:
None

Catalog generation complete.
C:\spb\SpbTestTool\sys\x64\Debug\SpbTestTool\spbsamples.cat
Done Adding Additional Store
Successfully signed: x64\Debug\SpbTestTool\spbsamples.cat

========== Build: 2 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

成功!

本文提到的驱动是 Windower Driver Sample【参考2】的一部分。

这里我放置一个 SPB 的Driver

SpbTestTool

参考:
1.http://www.lab-z.com/spbtesttool/
2.https://github.com/Microsoft/windows-driver-samples

Shell 下的 I2C 工具

最近写了一个 Shell下的 I2C 访问工具,可以帮助大家进行 Shell下的I2C测试.

image001

主要功能有4个:
1. Read 读取给定的 I2C Bus, Slave Address, Register 的值
2. Write 向指定的 I2C Bus, Slave Address, Register 写入Value
3. Dump 枚举指定的 I2C Bus, Slave Address上面的256 个Register
4. Scan 扫描枚举指定的 I2C Bus,列出所有对于读操作有 act的设备地址(128个)

A. Read 的功能
image002

B. Dump 功能
image003

C. Scan Bus 确定 I2C 设备地址的功能 ( 这是在 KBL-R HDK 板子上接入了一个 I2C Touch 后的扫描结果,可以看到一个设备上有很多个地址)

image004

下载(无 Source Code)

2017年6月17日更新,之前的版本是 0.3 ,当前升级到了 0.4 修改了 write 无法正确写入的问题

zI2c

How to debug ACPI by WinDBG in Winows RS2

自从 Windows 升级到了 RS1 , WinDBG 就无法进行ACPI 的调试了,主要的现象是各种找不到变量函数等等。经过一番研究,终于摸索出来了在 RS2 上进行ACPI 调试的方法。具体步骤如下:
特别注意,第一次实验的时候务必按照顺序进行,否则很可能需要重头再来!
1. Target端安装 Windows RS2, 用户名为ZTX (请使用这个名称,确保后面的操作有参考作用)
image001

2. Target端进入 Windows后,运行 MSConfig

image002image003

3. Target端CMD 串口下(Administrator 权限),运行下面的命令要求系统不检查驱动签名

bcdedit.exe -set TESTSIGNING ON

4. Host 端使用 WinDbg 确保这个步骤能连上,如果连不上请检查USB设定
5. Target端进入 Windows/System32 下搜索 “acpi.sys”,会找到很多结果,我们的目标只是 acpi.sys,需要将系统中的这个文件替换为 checked 版本的。通常系统中会有1个以上的ACPI.SYS,一个是原本的驱动,另外是系统缓存出来的,所有的都需要删除
image004

因为系统保护的缘故,删除动作很满烦,具体操作如下:
5.1 选择第一个 ACPI.SYS,打开 Properties –> Security查看 Users, 注意到是没有Modify 的权限的。
image005

5.2 选择 Advanced, 点击 Owner:TrustedInstaller 后面的Change
image006

输入用户名 ZTX

image007

之后 Owner 会变成 ZTX
image008

返回上一层之后选择 Users
image010

然后使用 edit 按钮,选中 Users 然后再选择 Full Control
image011

出现下面的提示,选择 Yes
image012

之后,你就有权限删除所有的 ACPI.SYS了。然后将 checked 版本的 ACPI.SYS 放置到system32\drivers目录下。
至此就完成了替换,重启 Target端,
重启之后,HOST端的 WinDBG 会出现下面的提示(特别提醒,这里一定要连着WinDBG,否则看到的只能是无尽的BSOD):
image013

输入 g 还会再次停下来
image014
再输入 gh ,即可进入系统。
image015

简单的托盘程序2

基于很早之前(那时候还有 MSN )编写的托盘程序,可以在内存中查找指定的进程(本例中查找的是Notepad.exe),如果找到了就在 memo 中记录当前时间。

编译环境为 Delphi XE2。

totray2

2017/5/30

参考:
1。http://www.lab-z.com/%E3%80%902009%E5%B9%B41%E6%9C%8816%E6%97%A5%E3%80%91delphi-%E5%BD%93%E6%9C%80%E5%B0%8F%E5%8C%96%E6%97%B6%EF%BC%8C%E8%87%AA%E5%8A%A8%E7%BC%A9%E5%87%8F%E5%88%B0%E6%89%98%E7%9B%98%E4%B8%AD/

WDK 提供的 USBView

网上能找到的都是旧的,不支持 usb 3.0。 于是在 MS 的网站上找了一个最新版本的,下面是来自 WDK for Win10, 64位的:

usbview

具体介绍可以看下面:

USBView
USBView (Universal Serial Bus Viewer, Usbview.exe) is a Windows graphical user interface application that enables you to browse all USB controllers and connected USB devices on your computer. USBView works on all versions of Windows.
Where to get USBView

USBView is included in Debugging Tools for Windows.
USBView is also available in the Windows driver samples repository on GitHub.
Using USBView

USBView can enumerate USB host controllers, USB hubs, and attached USB devices. It can also query information about the devices from the registry and through USB requests to the devices.
The main USBView window contains two panes. The left pane displays a connection-oriented tree view, enabling you to select any USB device.
The right pane displays the USB data structures that pertain to the selected USB device. These structures include Device, Configuration, Interface, and Endpoint Descriptors, as well as the current device configuration.

来自:https://msdn.microsoft.com/en-us/library/windows/hardware/ff560019(v=vs.85).aspx

=========================================================
2024年5月28日  更新一个 Win11 的

Leonrado Uno 转接板和修改后的USB Host库

有时候,我们希望在 Leonrado 上使用 Uno 的Shield,但是会发现两者的引脚并不兼容。比如: uno 上的 SPI 位置和 leonrado 不同。

这种情况下就可以使用下面的转接板。
a2u

主要是调整了 i2c 和 spi 使得转接后的能够兼容设计给 uno 的sheild.

a2u2

比如,我直接的应用就是让 USB Host Shield 可以在 Leonrado 上工作。同时,设计上考虑到国内基本上买不到合适的长脚母座,所以使用了母座外加排针的方式, 堆叠之后的样式如下:

a2u3

电路板下载 zLeo2UnoShieldv1.0

之后,我实验编译使用Leonrado 自带USB Keyboard功能的时候遇到奇怪的问题,始终提示没有加入include “keyboard.h”。经过一番研究发现,出现这个问题的原因是:IDE定义USB keyboard的时候定义了HID Class,而USBHost 库在解析USB设备的时候同样定义了一个HID Class,二者存在冲突。
对此,需要修改避免HID Class 的冲突。我选择修改 USB Host库,因为这个毕竟是第三方库。

最终将库中的 HID Class全部修改为 NewHID ,这样我们可以在 Leonrado 同时使用 USB Host 和 模拟键盘鼠标了。

附件是修改之后的版本,亲测有效:

USB_Host_Shield_Library_2.0_Modified