UEFI TIPS: 定义一个注释宏

最近看了一下C语言中 Define 的用法,这个可以看成是C 语言的宏定义,在使用时会进行展开。从这个角度来说,可以用它实现编译过程中自动删除代码的功能。

比如下面的代码中,当定义LAB_APP_DEBUG 1 后,编译过程中 zPrint  会被解释成为 “\\” 这样对应的一行就会被注释掉。

#include  <Uefi.h>
#include  <Library/BaseLib.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>

#define LAB_APP_DEBUG 1
#if defined(LAB_APP_DEBUG)
        #define        zPrint   Print
#else   
        #define        zPrint   /\
/
#endif

INTN
EFIAPI
main (
  IN UINTN Argc,
  IN CHAR8 **Argv
  )
{
    zPrint(L"StringAAAA\n");
    zPrint(L"StringBBBB\n");
    
    return EFI_SUCCESS;
}

运行结果,第一个是定义了LAB_APP_DEBUG=1的结果,第二个是删除了这定义的结果

此外,EDK2 中一些宏将一些函数定义为空,在编译时通过当前时 DEBUG 还是 REALSE 进行区分,可以做到和上面相同的效果。

Arduino CH376 模块调试指南

去年的时候,介绍过通过串口来和 CH376 通讯【参考1】,最近又将它拿出来玩,和之前不同,这次是和 ESP32 通讯,没想到遇到了奇怪的问题,以此为契机仔细研读 datasheet 总结如下。

  1. 硬件连接:USB 转串口卡,上面的 5V(必须5V),接VCC;GND 接 GND;TX接 RX; RX 接TX; 特别注意,这种板子上有一个5V转3.3V的ASM1117,就是说5V提供给USB 设备,但是芯片是工作在3.3V 下,这种情况下串口一般都能正常工作,但是如果用 SPI 模式,需要特别注意和单片机的电平匹配问题;
  2. 串口发送 CHECK_EXIST (57 AB 06 AA),正确的回复是 0x55 如果没有回复或者是错误的,请检查硬件连接,还有波特率设定;默认情况下波特率为 9600
测试串口参数如图

3.串口发送CMD_SET_USB_MOD (57 AB 15 06), 模式代码为 06H 时切换到已启用的 USB 主机方式,自动产生 SOF 包,正确回答是CMD_RET_SUCCESS 和 USB_INT_CONNECT (0x51 0x15)

  1. 串口发送 DISK_CONNECT(57 AB 30),检查U盘连接,正确回答是 0x14;如果有问题,请更换U盘或者检查格式,在【参考2】给出了一个Bug:如果保留扇区数>255,在老的芯片上会有问题;
  2. 串口发送 DISK_MOUNT (57 AB 31),初始化U盘,并且检测,正确回答是 0x14;
  3. 串口发送 DISK_CAPACITY (57 AB 3E), 查询U盘容量。查询结果需要发送 RD_USB_DATA0查询(57 AB 27),收到返回值 04 FF 7F 7D 00 ,意思是 4个字节,0x7D7FFF= 8224767个扇区电脑上查看如下(感觉这个命令查询的结果是“U盘上最大的扇区号”,所以数量等于这个值加1):

参考:

  1. https://www.lab-z.com/ardch376/
  2. http://www.wch.cn/bbs/thread-63674-1.html 工作人员回复说老版本芯片会有问题

FT232H UART 速度测试

很早之前入手过一块 FT232H 的板卡,这次进行一下 UART 最高速度的测试。测试条件是 Windows 10 操作系统,系统默认的驱动(VCP)。

FT232H 长江智动产的

FT232 上 TXD 是 Pin13 (AD0), RXD 是Pin14(AD1)

FT232H 做 Uart 通讯时的引脚定义
不同功能下引脚的功能

使用串口工具直接发送 HEX 55 55 55 55 55 55 55 55 55 55 值(这样从信号看起来就是不断变化的0 1 信号),示波器查看结果如下:

上面是FT232H uart 12M (波特率  12 000 000)的结果
上面是 6Mhz 的结果

此外,有一些波特率是无法使用的比如:10Mhz ,测试显示无信号输出。测试板子上的晶振是 12Mhz 的,可能是因为这个原因,所有有些分频是无法出来的。从上面可以看出, FT232H UART 模式下,最高的有效传输速度可以达到 12Mhz*(8/10)=9 600 000 bits/s = 1.444Mbytes/s。

本文数据根据实验得出,有问题欢迎朋友直接指出共同探讨。

参考:

1. http://ftdichip.cn/Support/Documents/DataSheets/ICs/DS_FT232H.pdf

D3hot 和 D3Cold

从 Windows8 开始,操作系统将设备的 D3 状态分为2种: D3hot 和 D3Cold。

设备可以直接从 D0 状态进入D3Hot,D3Cold 只能从 D3Hot 状态进入。 从 D0 到 D3Hot 的状态切换是通过驱动程序来完成的。进入 D3Hot 后,仍然能够在这个设备连接的总线(Bus)上看到这个设备。当这个总线上的设备进入 D3Hot 后,总线必须处于 D0 状态。进入 D3Hot 后,设备可以返回 D0 状态,或者进入 D3Cold 状态。

当设备进入 D3Cold 后,总线上无法检测到这个设备(设备完全断电)。当设备进入 D3Cold 后,它所处的总线可以进入低功耗状态。同时这个设备不会响应总线上的检测动作。进入 D3Cold 后,设备只能返回 D0 状态,不能直接切换为 D3Hot 状态。

参考:

1.https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/device-sleeping-states

2.https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/supporting-d3cold-in-a-driver

3.https://blog.csdn.net/qq_38180524/article/details/106079187

4.http://blog.chinaunix.net/uid-7374279-id-5838168.html

WinDbg 查看OS 版本命令:vertarget

5: kd> vertarget
Windows 10 Kernel Version 19041 MP (8 procs) Free x64
Product: WinNt, suite: TerminalServer SingleUserTS
Edition build lab: 19041.1.amd64fre.vb_release.191206-1406
Machine Name:
Kernel base = 0xfffff805`0da00000 PsLoadedModuleList = 0xfffff805`0e62a2b0
Debug session time: Wed Feb 10 15:55:42.009 2021 (UTC + 8:00)
System Uptime: 7 days 23:02:38.960

参考:

1.https://www.cnblogs.com/yilang/p/12009225.html

Kabylake PCH UART 测试工具 UEFI Shell 版

在 KablyLake/AmberLake的PCH上存在着3个UART,默认情况下使用 UART2(第三个UART,INTEL RVP上使用的就是这个Port作为UEFI Debug Log 输出)。为此,编写一个 UEFI Shell Application 能够让三个 UART 分别发送 “This is PCH UART0/1/2”。在通过串口连接出去的机器上可以收到。这样能够方便的确认硬件连接以及配置是否工作正常。

运行结果:

ZUART UEFI Shell 下运行结果

工具下载(无 SourceCode):

ESP32 S2 进一步测试

春节这几天趁着有空再进一步研究了一下 ESP32 S2 的 USB 玩法。线路连接方法和之前介绍的相同【参考1】,特别注意,这次没有连接 ESP32S2 的5V和 USB 端口的5V,  这是防止 USB 端口上的5V和板子上的5V不同导致的电流倒灌。首先测试 HID的例子:

选择 ESP32 Tiny USB 中的 HID 例子

烧写代码后设备管理器中可以看到多出的 HID 设备。

新出现的 ESP32 S2 模拟出来的设备

我手上的开发板是ESP32-S2-Saola-1R,引脚入下图:

ESP32-S2-Saola-1R 引脚图

调试方法:

打开当前 Verbose 模式

这样,可以在 Arduino 串口监视器中看到 Debug 信息,比如插入时有如下信息:

插入时的Debug 信息

接下来研究一下 MSD (MassStorageDevice,U盘)的例子,在Example->ESP32 TinyUSB->MSC。这个例子需要使用1.5MB的 PSRAM ,对于我入手的板子来说 Flash 是4MB ,PSRAM 是2MB,完全能够满足要求。此外,需要 Enable PSRAM,否则会出现不断重启的问题:

需要Enable PSRAM

烧写之后系统中就会出现如下的硬盘。

很小的一个U盘

测试发现最新版本的 ESPTinyUSB 库似乎有问题,下面是我这边正常使用的老版本的库:

参考:

  1. http://www.lab-z.com/esp32s2/ 支持原生USB 的ESP32 :ESP32 S2
  2. https://www.mischianti.org/wp-content/uploads/2020/11/ESP32-S2-Saola-1MI-pinout-mischianti.png

常见 X86 平台TPM 相关缩写介绍

之前看到过很多人用一些词组来证明中文的简洁和高效,比如:人们看到“炮闩”,通过“闩”就能猜到它的用途。这让我想起最近网上的一个笑话:“老师在群里发消息说明天开始一元二次方程。有家长问。能不能一元三次?打个折?” 缺少必要知识,就无法理解“元”的含义。 “一元二次方程” 的英文名是“quadratic equation with one unknown”,从上面大抵能看明白这个方程中有一个未知数以及它的二次方。所以,懂和不懂是取决于知识的储备和语言文字并没有太多关系。

缩写原始词组解释
ACMAuthenticated Code Module用于验证当前密钥之类的模块,放在 BIOS 中的,这个可以看作是 BootGuard 功能的一部分(换句话说,如果想 Enabled BootGuard 功能,除了在  FIT  中 Enable BootGuard,还要保证BIOS中有这个模块。某些情况下,IBV Enabled Debug 功能后,因为 Size 的原因会关闭 ACM ,这样做出来的Debug BIOS 无法在 Fuse 后,并且Enable BootGuard的板子上运行。现象会是:上电之后马上掉电)。
BtGBootGuardBootGuard 在【参考1】有介绍。
BUPBring-Up主板上电过程,有时候指新打出来板子然后Porting BIOS 的过程。
CSEConverged Security Engine系统上的 CSME 系统
dTPMDiscrete TPM分立式TPM。对应有一颗专用芯片,通常使用 SPI 接口和PCH 相连。Intel PTT 相当于将一个 dTPM 集成到SOC 中。
EKEndorsement Key签署密钥,或背书密码。它是一个TPM平台的不可迁移的解密密钥,它是一个2048bit的RSA密钥对。它生成于平台的生产过程中,代表着每个平台的真实身份,每个平台都拥有唯一的一个。在确定平台所有者时,用于解密所有者的授权数据,还有解密与生成AIK相关的数据。签署密钥从不用作数据加密和签名。签署密钥的主要功能是生成身份证明密钥(AIK)和建立TPM平台的所有者,由TPM的所有者来生成存储根密钥SRK,使用SRK来加密、存储其他的密钥。【参考2】
FIPSFederal Information Processing Standard美国联邦信息处理标准
FITFlash Image Tool有时缩写为 FITC, 是一个用于设置 IFWI 的工具,由Intel 提供,跟随 CSME 分发。
FPFField Programmable Fuses可编程熔丝位。在 PCH 中有一些只能编写一次的熔丝位。可以理解成类似保险丝一样的东西,需要的时候通过编程能够将这个位置写死。比如,Enable PTT之后通过设置对应的熔丝位会使得这个功能无法再次 Disabled。
fTPMFirmware TPM固件模拟TPM。通过在 CPU 上运行的代码来模拟实现 TPM 的功能。为了增加安全性,相关代码是放在 Protected Execution Environment (PEE) 来执行的。
IBBInitial Boot Block这是 UEFI 中的概念,用于校验 BIOS 其余部分。
PEEProtected Execution Environment受保护运行环境,能够独立于普通CPU 运行环境来运行代码。  
PRTCProtected Real Time Clock受保护的时钟,能够抵御 Hammering 和 Replay 攻击。对于这两种攻击介绍如下【参考4】   重放攻击(Replay Attacks)又称重播攻击、回放攻击,是指攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程,破坏认证的正确性。重放攻击可以由发起者,也可以由拦截并重发该数据的敌方进行。攻击者利用网络监听或者其他方式盗取认证凭据,之后再把它重新发给认证服务器。重放攻击在任何网络通过程中都可能发生,是计算机世界黑客常用的攻击方式之一。   重放操作 一个电子商务网站,要求客户对电子订单签名以防止非授权用户下订单。攻击者如要冒充某位客户下订单,最好可以获得他的私钥,如果不成功,攻击者可以监听这位顾客的通信,将顾客以前发送的订单记录下来,然后他就可以直接将这些订单发给网站了。因为这些订单的确是合法客户签名过的,如果网站没有一种识别重放订单的机制,它就会不加犹豫地接收这些订单。
PTPPlatform TPM Profile 
RBEROM Boot Extension   
RNGRandom Number Generator随机数生成器
ROTRoot of Trust根信任节点
RPMBReplay Protected Memory Block 防重放攻击内存块, 将PTT数据存放在独立的 NVM 区域。
RPMCReplay Protection Monotonic Counters防重放攻击单调递增计数器。这个计数器只能增加不能减小,通过这样的方式来对抗重放攻击。
RTCReal Time Clock   
RTMRoot of Trust for Measurement   
RTR  Root of Trust for Reporting 
RTSRoot of Trust for Storage   
TCGTrusted Computing Group  可信计算组织。TCG组织制定了TPM(Trusted Platform Module)的标准,很多安全芯片都是符合这个规范的。而且由于其硬件实现安全防护,正逐渐成为PC,尤其是便携式PC的标准配置。
TPMTrusted Platform Module可信平台模块。
VSCVirtual Smart Cards 虚拟智能卡。常见的公交卡就是一种智能卡。可以看到它能够实现身份验证,支付等等的功能。同样的,虚拟智能卡也能实现这样的功能。可以想象下面的应用场景:通过 TPM 实现了 Virtual Smart Cards 的功能,然后 Windows 就可以访问企业内部的敏感资源。比如,你将一份文档发送到企业内部打印机上,之前需要刷胸卡来完成打印,现在有了 VSC 可以通在打印机上刷笔记本电脑的方式完成打印。

可以看到,上面的很多词,如果不是专业人员,无论是英文还是中文都是需要多加解释的。再比如:“有一个机械中特别重要的装置,叫活塞,英文是piston。不过在中国,这个词最早不叫活塞,而叫“鞲鞴”。来,眼睛别晕,先扶着墙把晚饭吐出来,然后把这个字复制到word里,放大三倍字号。这个词,念“勾背”。据李文、戴吾三两位考证,是中国第一艘火轮船的制造者徐寿发明的,徐寿1871年在《汽机发轫》首次把piston翻成鞲鞴。别看这词冷僻古怪又麻烦,还真是有典故的。“鞲”字意义是革制的皮套,引申成皮制的鼓风机,也就是风箱;“鞴”字就更精妙了,它的意思是:水受压而喷涌而出。唐代皮日休的《通玄子栖宾亭记》:“源内橐籥鞴出琉璃液。”一鼓一压,正是活塞工作之象。徐寿文化水平太高,用这两个字来译piston,真是用典贴切,古今妙译里,也排得上号,就是他妈太麻烦了……所以后来大家普遍都使用“活塞”这个更浅显的词,鞲鞴则只留存在专业领域。”【参考3】

参考:

1.https://www.lab-z.com/btg/ Boot Guard 简介

2.https://www.cnblogs.com/embedded-linux/p/6716740.html TPM Key相关概念

3.https://www.zhihu.com/question/24449484/answer/27850454 作者:马伯庸

4.https://baike.baidu.com/item/%E9%87%8D%E6%94%BE%E6%94%BB%E5%87%BB 重放攻击

5.https://docs.microsoft.com/en-us/windows/security/identity-protection/virtual-smart-cards/virtual-smart-card-overview

6.https://baike.baidu.com/item/%E6%99%BA%E8%83%BD%E5%8D%A1 智能卡

智能卡(Smart Card) :内嵌有微芯片的塑料卡(通常是一张信用卡的大小)的通称。一些智能卡包含一个微电子芯片,智能卡需要通过读写器进行数据交互。智能卡配备有CPU、RAM和I/O,可自行处理数量较多的数据而不会干扰到主机CPU的工作。智能卡还可过滤错误的数据,以减轻主机CPU的负担。适应于端口数目较多且通信速度需求较快的场合。 卡内的集成电路包括中央处理器CPU、可编程只读存储器EEPROM、随机存储器RAM和固化在只读存储器ROM中的卡内操作系统COS(Chip Operating System)。卡中数据分为外部读取和内部处理部分。

智能卡(Smart Card) :内嵌有微芯片的塑料卡(通常是一张信用卡的大小)的通称。一些智能卡包含一个微电子芯片,智能卡需要通过读写器进行数据交互。智能卡配备有CPU、RAM和I/O,可自行处理数量较多的数据而不会干扰到主机CPU的工作。智能卡还可过滤错误的数据,以减轻主机CPU的负担。适应于端口数目较多且通信速度需求较快的场合。 卡内的集成电路包括中央处理器CPU、可编程只读存储器EEPROM、随机存储器RAM和固化在只读存储器ROM中的卡内操作系统COS(Chip Operating System)。卡中数据分为外部读取和内部处理部分。

Step to UEFI (231)直接修改 ROM 中的版本号

某些情况下,我们需要在没有源代码的情况下需要对BIOS ROM 进行直接修改。本文就以修改 OVMF.FD 为例介绍这个过程。

首先介绍一下这次修改的目标,使用下面的命令生成 Debug Log:

qemu-system-x86_64 -bios "ovmf.fd" -debugcon file:debug.log -global isa-debugcon.iobase=0x402

其中有下面这样一个错误:

SecCoreStartupWithStack(0xFFFCC000, 0x820000)
LABZ report checksum error!
Register PPI Notify: DCD0BE23-9586-40F4-B643-06522CED4EDE
Install PPI: 8C8CE578-8A3D-4F1C-9935-896185C32DD3
Install PPI: 5473C07A-3DCB-4DCA-BD6F-1E9689E7349A
The 0th FV start address is 0x00000820000, size is 0x000E0000, handle is 0x820000
Register PPI Notify: 49EDB1C1-BF21-4761-BB12-EB0031AABB39
Register PPI Notify: EA7CA24B-DED5-4DAD-A389-BF827E8F9B38
Install PPI: B9E0ABFE-5979-4914-977F-6DEE78C278A6

我们的目标就是通过修改 OVMF.FD 来去掉这个错误。

使用 UEFITool 打开 OVMF.FD ,使用搜索功能查找错误字符串,可以看到是位于 SecMain 中。这个文件没有压缩,对于我们搜索来说会很方便。

为了研究代码,还需要使用这个工具将SecMain 释放出来:

释放出来的文件是以  “MZ”开头的:

 使用 IDA 来分析解压出来的这个文件,特别注意,因为代码中含有64Bit的指令,必须使用 IDA-64 才能得到结果。

我们需要找到相关的代码,在菜单中选择 View->Open subviews->Strings

先用Ctrl+F 找到前面提到的字符串,在字符串上双击即可跳转到对应的代码:

新版本的 IDA 默认使用图形化反编译结果,看起来并不是很直接,右键切换到 Text View

这里就可以看的很清楚,比较 rax 和 rdx ,如果不相等就输出字符串,如果相等就跳过:

使用 Hex View 查看,74 0F就是 jz short loc_FFFCCE37 这个跳转语句,将这条语句修改为 jmp short loc_FFFCCE37 就不会输出错误提示了。

修改方法是:在 OVMF中搜索 FD FF FF 48 3B C2 74 0F (整个文件中只有一处),找到后将 74 修改为改成 EB (JMP)。这样修改后再次使用 QEMU 启动修改后的 OVMF.FD ,可以在 Debug.log 中看到之前的字符串已经消失。

本文使用的,修改之前的 OVMF 可以在这里下载:

这个信息是在 Omvf/Sec/SecMain.c 中加入如下代码生成的:

//
  // Find PEI Core entry point
  //
  Status = PeCoffLoaderGetEntryPoint ((VOID *) (UINTN) PeiCoreImageBase, (VOID**) PeiCoreEntryPoint);
  if (EFI_ERROR (Status)) {
    *PeiCoreEntryPoint = 0;
  }

//LABZ_Debug_Start
	if (((EFI_PHYSICAL_ADDRESS) FindImageBase -(EFI_PHYSICAL_ADDRESS) FindAndReportEntryPoints)!=0x0) {
		DEBUG ((EFI_D_ERROR, "LABZ report checksum error!\n"));
	}
//LABZ_Debug_End

  return;
}