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

从源代码到 FFS 文件

本文会以 BdsDxe.ffs 的生成为例,介绍一下从 EFI 到 FFS的编译过程。
所有的实验都是建立在UDK2015 NT32Pkg的基础上。
首先,要保证NT32能够正常编译运行。之后,在 build目录中取得生成的BIOS文件:nt32.fd。使用 fmmt –v nt32.fd 查看一下这个BIOS的组成。可以看到其中只有一个 FV , 然后有 BdsDxe的ffs。我们可以在\Build\NT32IA32\DEBUG_VS2013\IA32\MdeModulePkg\Universal\BdsDxe\BdsDxe\OUTPUT\ 目录下,找到编译生成的 BdsDxe.efi。 在 \Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe可以看到文件名为6D33944A-EC75-4855-A54D-809C75241F6C.ffs 的文件。就是说我们前面看到的BdsDxe .efi,最终得到了一个ffs文件。
ffs1

ffs2

下面就针对这个过程逐一进行分析。
1. *.PE32 文件的生成。具体的生成命令可以在在6D33944A-EC75-4855-A54D-809C75241F6CSEC2.1.1.1.pe32.txt中看到:
GenSec -s EFI_SECTION_PE32 -o c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6CSEC2.1.1.1.pe32 c:\udk2015\Build\NT32IA32\DEBUG_VS2013\IA32\MdeModulePkg\Universal\BdsDxe\BdsDxe\OUTPUT\BdsDxe.efi
使用工具比较一下生成前后的文件,可以看到只有头部存在一点差异。
ffs2
再查看 GenSec 的源代码,当指定为 EFI_SECTION_PE32 的时候,头的内容是 EFI_SECTION_PE32(0x10)+Length。所以和之前的EFI 相比,增加的是 0x10 和0x13004 (EFI文件的长度)
2. GUIDED文件的生成,在6D33944A-EC75-4855-A54D-809C75241F6CSEC2.1.guided.txt 可以看到, 使用的是 GenSec 工具,作为输入的是 PE32, UI和 VER文件。
GenSec -s EFI_SECTION_GUID_DEFINED –sectionalign 1 –sectionalign 1 –sectionalign 1 -o c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6CSEC2.1.guided c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6CSEC2.1.1.1.pe32 c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6CSEC2.1.2.ui c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6CSEC2.1.3.ver
3. 目前我尚不知道 *.UI文件是如何生成的,希望有懂的朋友指导一下,谢谢!
4. 从6D33944A-EC75-4855-A54D-809C75241F6CSEC2.1.3.ver.txt 文件可以得知*.VER 是这样生成的
GenSec -s EFI_SECTION_VERSION -n 1.0 -o c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6CSEC2.1.3.ver

5. 从6D33944A-EC75-4855-A54D-809C75241F6CSEC2.com.txt 文件可以得知 .COM 的生成方式如下(COM 并非DOS下面的可执行文件,而是 COMPRESS的缩写)。经过了这步,因为压缩的原因,生成的结果比压缩之前小了很多,比较起来也会发现“面目全非”。
GenSec -s EFI_SECTION_COMPRESSION -c PI_STD -o c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6CSEC2.com c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6CSEC2.1.guided
6. 从6D33944A-EC75-4855-A54D-809C75241F6C.ffs.txt 可以看到 FFS是如何生成的:
GenFfs -t EFI_FV_FILETYPE_DRIVER -g 6D33944A-EC75-4855-A54D-809C75241F6C -o c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6C.ffs -i c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6CSEC1.1.dpx -i c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6CSEC2.com
7. 从6D33944A-EC75-4855-A54D-809C75241F6CSEC1.1.dpx.txt,可以看到 dpx 文件的生成过程
GenSec -s EFI_SECTION_DXE_DEPEX -o c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6CSEC1.1.dpx c:\udk2015\Build\NT32IA32\DEBUG_VS2013\IA32\MdeModulePkg\Universal\BdsDxe\BdsDxe\OUTPUT\BdsDxe.depex

8. 接下来是最后一步,使用COM 和 DPX 生成 FFS 文件,从6D33944A-EC75-4855-A54D-809C75241F6C.ffs.txt 文件可以看到操作动作
GenFfs -t EFI_FV_FILETYPE_DRIVER -g 6D33944A-EC75-4855-A54D-809C75241F6C -o c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6C.ffs -i c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6CSEC1.1.dpx -i c:\udk2015\Build\NT32IA32\DEBUG_VS2013\FV\Ffs\6D33944A-EC75-4855-A54D-809C75241F6CBdsDxe\6D33944A-EC75-4855-A54D-809C75241F6CSEC2.com

最后生成的 FFS 和 COM 差别很小
ffs3

了解了整体的BIOS结构,做很多事情,比如:编写无需重新编译代码,直接修改ROM的工具等等。本文只是简单的对于FFS的生成进行了研究,希望这篇文章对于需要了解EDK2 Firmware 结构的朋友有所帮助。

示波器查看串口通讯的波形

虽然使用了串口很久,但是一直没有深入研究波形。最近看了一篇介绍【参考1】,随手用示波器抓了一下波形。
下面是资料中提到的例子。在无数据传输时,串口总线上应该是高电平。当有数据传输时,首先会拉低一个时钟周期,这是 Start Bit。之后是有用的数据,长度是根据双方约定好的。最后可以跟着一个校验位(也可以没用),最终用一个拉高的时钟周期表示传输完成,这是Stop Bit。然后总线继续拉高处于Idle状态。
u1
下面示波器抓取的实际波形,双方的通讯设置如下:
u2
发送0xAA
u3

发送 0x55
u4

最后附上一个常用的对 0x3F8 初始化的代码段

#define PORT 0x3f8 /* COM1 */

void init_serial() {
outb(PORT + 1, 0x00); // Disable all interrupts
outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
outb(PORT + 0, 0x01); // Set divisor to 1 (lo byte) 115200 baud
outb(PORT + 1, 0x00); // (hi byte)
outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit
outb(PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
outb(PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
}

参考:
1.http://www.unm.edu/~zbaker/ece238/slides/UART.pdf

让 Leonorade的键盘有“输出”能力

Arduino Leonarado 和其他型号相比,最大的特点是可以方便的将自身模拟为USB键盘和鼠标。从USB总线的角度来说,数据通讯本身是双向的。从整体角度来说键盘只是输入设备,并没有输出的能力,但是如果仔细观察会发现键盘上有三个指示灯,分别是:NumLock,ScrollLock和CapsLock。这三个指示灯作用如下:
1. Num Lock 是副键盘中数字键盘的开关。在这个键对应的键盘指示灯关闭的情况下,小键盘的按键用来移动光标(上下左右、行首、行尾等等),在这个键对应的键盘指示打开的情况下,即锁定数字键,小键盘的按键用来输入数字;
2. Caps Lock 是大小写锁定键,在这个键对应的键盘指示灯关闭的情况下,键盘输入的字母都是小写,否则键盘输入都为大写;
3. Scroll lock (滚动锁定键)最初是用来设计为DOS下自动滚动屏幕的,但是进入Windows出现图形化的界面之后,这个按键就没有用途了,只是为了兼容等等考虑在通用键盘设计上仍然有所保留。
如果你的电脑有两个以上的键盘,还能观察到一个有趣的现象:在一个键盘上按下上述三个键中的某一个,那么其他的键盘状态也会跟着发生变化。原理上来说,是Windows会将收到的切换信息再“广播”出去,保证所有的键盘状态都是同步的。我用USB逻辑分析仪抓包,当在其他键盘上按下NumLock时,系统会向每一个USB键盘发出广播。比如:下面系统中有3个键盘,当键盘1按下 NumLock 后,系统还会通知全部三个键盘“NumLock状态改变”的消息。
kb1

我们无需关心Windows中消息的格式,对于USB来说,USBHost (Windows的PC),会送出一个 SET Package来通知 USB Device(USB 键盘)。下图是我用USB逻辑分析仪抓包的结果:

kb2

进一步展看查看协议,是发送了 07 给USB 键盘
kb3

上面的原理可以帮助我们在Arduino Leonarado上实现。简单起见,我们的目标是在ArduinoLeonarado 上装上三个LED,让这三个LED和我的 USB键盘上三个LED实现同步。原生的ArduinoIDE 并没有设计这个功能,因此,需要对源代码进行修改:
第一个需要修改的地方是 \arduino\libraries\Keyboard\src\Keyboard.cpp 中的 USBKeyboard的HID 描述符(Descriptor)。增加了下面斜体字表示的部分:
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
//ZT_DEBUG
[i] 0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (1)
0x29, 0x05, // USAGE_MAXIMUM (5)
0x91, 0x02, // OUTPUT (Data,Var,Abs) // LED report
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x91, 0x01, // OUTPUT (Constant) // padding[/i]
//ZT_DEBUG

0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x05, 0x07, // USAGE_PAGE (Keyboard)

当然,具体作用请参考USB HID协议对照解读。简单的解释:修改之前的描述符只有INPUT部分,就是键盘告诉系统“我只能发出数据”;增加部分的作用是键盘告诉系统能够接收HOST过来的状态信息(USB协议中,INPUT指的是设备对主机的方向,OUTPUT指的是主机对设备)。我们的修改是加入“我还能接收”的能力。如果不声明这样的能力,系统不会将数据发送过来,具体实验中,一些USB接口的小键盘上面的LED不会跟随主机状态变化。
下面需要做的就是实际处理数据了。1.6.X 系列的代码和之前的差别很大,对于键盘这部分是分开在 HID 和Keyboard 的类中。

Keyboard 只有输出的代码,通过调用 HID 类来进行发送的处理。因此,我们需要在HID 类中开一个接口。
重新定义如下:

处理部分, 在\arduino\hardware\arduino\avr\libraries\HID\src\HID.cpp 中的 boolHID_::setup(USBSetup& setup)函数中,当我们收到 Set_Report 就是系统发过来的关于LED的设置,我们取下来即可。

对于 HID 开一个接口,直接返回即可

uint8_t HID_::getLedStatus(void)
{
returnled;
}

我们给用户的接口是 Keyboard 类,我们要在上面田间一个读取的接口,

在这只文件中声明 \arduino\libraries\Keyboard\src\Keyboard.h

在s\arduino\libraries\Keyboard\src\Keyboard.cpp 文件中实现

编写一个例子,使用 Arduino 同步显示当前键盘的LED状态:

D5 最左侧灯,D6中间灯,D7 最右侧灯。

#include <Keyboard.h>

uint8_t old=0xFF,n=0xFF;
     
void setup() {
  pinMode(A0, INPUT_PULLUP);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(7,OUTPUT);

  Keyboard.begin();

  Serial.begin(9600);
  while (digitalRead(A0) == HIGH) {
    // do nothing until pin 2 goes low
    delay(500);
  }
}
  
void loop() {

  n=Keyboard.getLedStatus();
  if (n!=old) {
     Serial.println(n);
     old=n;
     if (0!=(n&1) +) {
          digitalWrite(5,HIGH);
      }
     else {
          digitalWrite(5,LOW);
      }
     if (0!=(n&2)) {
          digitalWrite(6,HIGH);
      }
     else {
          digitalWrite(6,LOW);
      }
     if (0!=(n&4)) {
          digitalWrite(7,HIGH);
      }
     else {
          digitalWrite(7,LOW);
      }
  }
  delay(100);
}

 

kb4
工作的视频

Lenorade的按键值

对于 Leonardo 来说,最大的特点就是可以直接模拟USB键盘鼠标。

一般来说,当我们需要发送某一个按键的时候可以直接使用Keyboard.print(‘a’)这样的方式,或者在Keyboard.h 中找到定义好的键值,比如:#define KEY_F1 0xC2。

我最近遇到一个问题:这个文件中定义的键值我无法在其他资料上找到,让人非常恼火,例如:没有一份资料上说 CAPS LOCK值为0xC1,但是使用Keyboard.h中定义的 #define KEY_CAPS_LOCK 0xC1 确实是好用的。最后才想起来应该去看一下对应的代码(开源的优点)。最终,找到的代码如下:

size_t Keyboard_::press(uint8_t k)
{
       uint8_t i;
       if (k >= 136) {                     // it's a non-printing key (not a modifier)
              k = k - 136;
       } else if (k >= 128) {    // it's a modifier key
              _keyReport.modifiers |= (1<<(k-128));
              k = 0;
       } else {                        // it's a printing key
              k = pgm_read_byte(_asciimap + k);
              if (!k) {
                     setWriteError();
                     return 0;
              }
              if (k & 0x80) {                                          // it's a capital letter or other character reached with shift
                     _keyReport.modifiers |= 0x02;  // the left shift modifier
                     k &= 0x7F;
              }
       }
      
       // Add k to the key report only if it's not already present
       // and if there is an empty slot.
       if (_keyReport.keys[0] != k && _keyReport.keys[1] != k &&
              _keyReport.keys[2] != k && _keyReport.keys[3] != k &&
              _keyReport.keys[4] != k && _keyReport.keys[5] != k) {
             
              for (i=0; i<6; i++) {
                     if (_keyReport.keys[i] == 0x00) {
                            _keyReport.keys[i] = k;
                            break;
                     }
              }
              if (i == 6) {
                     setWriteError();
                     return 0;
              }     
       }
       sendReport(&_keyReport);
       return 1;
}

 

就是说,当我们使用KEY_CAPS_LOCK ==0xC1,会先有一个 193-136=57的动作,最后发送出去的实际上是 57(0x39),在USB HID Usage Tables 有下面的定义:

同样的文档中我们可以查到 Scroll Lock == 71(0x47) Num Lock == 83 (0x53)。于是,定义如下:

#define KEY_SCROLL_LOCK 0xCF
#define KEY_NUM_LOCK 0xDB

最终,我们编写一个代码,能让键盘上的三个LED逐次亮灭

#include "Keyboard.h"
 
#define KEY_SCROLL_LOCK   0xCF
#define KEY_NUM_LOCK   0xDB
 
void setup() {
  pinMode(A0, INPUT_PULLUP);
  Keyboard.begin();
  while (digitalRead(A0) == HIGH) {
    // do nothing until pin 2 goes low
    delay(500);
  }
 
}
 
void loop() {
  Keyboard.write(KEY_NUM_LOCK);
  Keyboard.releaseAll();
  delay(1000);
  Keyboard.write(KEY_NUM_LOCK);
  Keyboard.releaseAll();
  delay(1000);
  
  Keyboard.write(KEY_CAPS_LOCK);
  Keyboard.releaseAll();
  delay(1000); 
  Keyboard.write(KEY_CAPS_LOCK);
  Keyboard.releaseAll();
  delay(1000);
 
  Keyboard.write(KEY_SCROLL_LOCK);
  Keyboard.releaseAll();
  delay(1000); 
  Keyboard.write(KEY_SCROLL_LOCK);
  Keyboard.releaseAll();
  delay(1000);
 
}

 

WHQL USB Exposed Port System Test

在 Windows 10 的 WHQL 测试中【参考1】,有一个称为“USB Exposed Port System Test”的项目。这个项目的测试目的是测试当前系统中对外的 USB 3.0接口和2.0接口的配对情况。比如,一个USB3.0 的端口构成如下,实际上可以看作是 2.0的信号 加上 3.0的信号组成的。而在实际设计上, USB2.0 和 3.0 的线并不是配对的,比如 USB 2.0 的 Port 1 可以和 USB 3.0 的Port 2共同组成这个端口。这对于 Chipset 来说是知道的,但是 Windows 并不知道这样的配对方式。

image001

image002

因此,通知 Windows 组合方式的任务就落在了 BIOS身上,更准确的说是通过 ASL 来完成这一工作的。具体来说是通过_UPC和_PLD这两个 Method 完成的【参考2】。具体设定完成之后,可以直接在被测机器上用 USBView进行检查(注意要用支持 USB 3.0的版本)。这个测试要求将全部“暴露”给客户的USB端口上插满USB Hub。 当USB Hub 插入后,会同时在 USB 2.0和3.0 的 Root Port 下出现设备。这样系统就可以得知 ACPI 的Report和真实的硬件能否对应上。
当然,上面并不是本文的重点,对于BIOS工程师来说最困难的bug并不是BIOS本身,而是并非BIOS却要求BIOS工程师来解的问题。
对于这个测试来说,真正的难点在于:需要使用USB IF 认证过的USB Hub来进行测试。起初我们使用了普通的USB Hub,结果始终无法通过测试,最后多方打听,终于找到了支持这个测试的Hub。
京东有售 http://item.jd.com/983704.html#product-detail ,显然价格比普通的USB 3.0 Hub要高出一截(当然,如果你再看一下测试 WHQL使用的 WindowsToGo 的U盘,还会觉得这个是良心价格)。

image003

截至目前(2017/04/22),我还不清楚这种认证过的Hub和普通的Hub有什么差别(错误提示和 container ID 有关系,我查看了资料也没有弄清楚是什么问题)。

亲测有效,希望对进行 WHQL 测试的朋友有所帮助。

参考:
1. https://msdn.microsoft.com/en-us/library/windows/hardware/jj123655(v=vs.85).aspx
2. https://msdn.microsoft.com/windows/hardware/drivers/install/using-acpi-to-configure-usb-ports-on-a-computer#

访问 PCIE 空间的两种方法

本文根据 Intel 文档【参考1】写成。
访问 PCIE 设备配置空间有2种方法: IO Port(0xCF8/0xCFC) 和 MMIO。前者是最传统的方式,能够访问 256Bytes的,很多扩展出来的超过256Bytes的就无能为力了;后者是比较新的方法。
先说一下第一种方法:
image002

比如,我们要访问南桥,Bus:0,Dev:1F,Func:0. 手工计算如下:

Capture

访问时,写为 dport(0xcf8,0x8000F800),写入之后再读取一次dport(0xcfc) 就是结果本例中是 Bus0/Dev 1F/Func 0/ Reg 0 ,PCI设备的 DID、VID
用代码表示就是:
address = BIT31 | ((Bus & 0xFF) << 16) | ((Dev & 0x1F) << 11) | ((Fun & 0x7) << 8) | (Reg & 0xfffffffc); 第二种方法: PCI 的寄存器会映射到内存中,系统中最多有256个Bus, 每个Bus最多有32个Device, 每个Device最多可以有4096个bytes的寄存器. 因此,整个映射最多占用256MB。下面这个图解释这个事情: image003

这么大的位置,是放置在4G以下的位置。一般会以 0xE000 0000为基地址。保险起见,最好查一下你代码中是否有重新定义。或者直接去看 0xE000 0000是否为一个PCI Header。

image004

还是以访问南桥为例子,手工计算如下:
Memory Address = PCI Express* Configuration Space Base Address + (Bus Number x 100000h) + (Device Number x 8000h) + (Function Number x 1000h) +
(Register Offset)

Bus:0,Dev:1F,Func:0 的地址在: 0xE000 0000 + 1Fh x 8000h = 0xE00F8000

参考:
1. http://www.docin.com/p-525417787.html 321090 Accessing PCI Express* Configuration Registers Using Intel® Chipsets