当出现 Windows 中热键冲突的时候,你可以使用 OpenArk 来揪出罪魁祸首。比如,运行之后,可以在“系统热键”中看到当前系统中所有的热键:

这是开源软件,可以在下面的地址下载到:
当出现 Windows 中热键冲突的时候,你可以使用 OpenArk 来揪出罪魁祸首。比如,运行之后,可以在“系统热键”中看到当前系统中所有的热键:
这是开源软件,可以在下面的地址下载到:
在上古的电脑时代,计算机使用 Magnetic Core Memory 来作为 RAM 存储设备。
中文名称是“磁芯存储器”。简单的说,这种磁芯有着不同的磁化方向。用这种方式可以记录0 和 1 两种状态。当下方的导线通过电流时,不同磁化方向会对电流有着不同的影响,这样就能通过经过的电流大小得出当前存储的装态。
这种装置可以看作时现代 DRAM 的雏形。有兴趣的朋友还可以阅读下面的文章:
1.https://baike.baidu.com/item/%E7%A3%81%E8%8A%AF%E5%AD%98%E5%82%A8%E5%99%A8/10189808?fr=aladdin 磁芯存储器
2. https://zhuanlan.zhihu.com/p/144628785 磁芯存储:统治存储领域20年
3.http://www.elecfans.com/d/1277911.html 带你了解磁芯存储器
前面介绍过 ESP32 作为蓝牙音频接收端(蓝牙音箱),这里介绍它作为蓝牙音频的播放端。
首先需要确定蓝牙接收器的名称,用笔记本电脑连接后,可以再设备管理器中看到,这里我使用的是一款蓝牙耳机,名称是“JABRA TALK”:
接下来需要安装 ESP32-A2DP-master 这个库。下面的代码是从这个库的Example 中修改而来,代码如下:
/*
Streaming of sound data with Bluetooth to an other Bluetooth device.
We provide the complete sound data as a simple c array which
can be prepared e.g. in the following way
- Open any sound file in Audacity. Make sure that it contains 2 channels
- Select Tracks -> Resample and select 44100
- Export -> Export Audio -> Header Raw ; Signed 16 bit PCM
- Convert to c file e.g. with "xxd -i file_example_WAV_1MG.raw file_example_WAV_1MG.c"
- add the const qualifier to the array definition. E.g const unsigned char file_example_WAV_1MG_raw[] = {
Copyright (C) 2020 Phil Schatzmann
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "BluetoothA2DPSource.h"
#include "StarWars30.h"
BluetoothA2DPSource a2dp_source;
//SoundData *data = new TwoChannelSoundData((Channels*)StarWars10_raw,StarWars10_raw_len/4);
SoundData *data = new OneChannelSoundData((int16_t*)StarWars30_raw, StarWars30_raw_len/2);
void setup() {
Serial.begin(115200);
Serial.println("Start");
a2dp_source.start("JABRA TALK");
}
void loop() {
if (a2dp_source.isConnected()==true) {
Serial.println("Connected!");
if (a2dp_source.hasSoundData()==true) {
Serial.println("Has sound!");
}
else {
Serial.println("No sound!");
a2dp_source.writeData(data);
}
}
else {
Serial.println("Not connected!");
}
delay(2000);
}
特别注意,因为代码有音频数据需要特别选择 Huge APP 模式:
测试使用的板子是 ESP-WROOM-32,特点是价格偏移兼容性还不错(同样的我试验了 TinkerNode,播放时有卡顿,似乎后台有人一直占用 Soc):
测试的视频:
https://www.bilibili.com/video/BV1wa411A7et/
特别注意:连接时需要比较有耐心多次尝试,先将耳机设置为配对模式,然后ESP32上电。从资料来看,这样的搭配似乎有兼容性问题,淘宝上的卖家都不承诺蓝牙音频端能够兼容客户的蓝牙接收端。
========================
2023年5月10日
前面介绍了如何EDK2在编译的最后过程中使用了 GenFds 进行打包。分析的目标是 QEMU 的 BIOS 文件 OVMF.FD,使用 UEFITool NE 打开之后,可以看到有三个 FV ,我们以中间的为例,分析它的生成方法。整体分析过程比较枯燥,对于大多数人来说了解大致的步骤就可以了。
首先,在 FDF 文件中,给出了这个 FV 的GUID 可以看到:
[FV.FVMAIN_COMPACT]
FvNameGuid = 48DB5E17-707C-472D-91CD-1613E7EF51B0
FvAlignment = 16
ERASE_POLARITY = 1
MEMORY_MAPPED = TRUE
STICKY_WRITE = TRUE
LOCK_CAP = TRUE
LOCK_STATUS = TRUE
WRITE_DISABLED_CAP = TRUE
WRITE_ENABLED_CAP = TRUE
WRITE_STATUS = TRUE
WRITE_LOCK_CAP = TRUE
WRITE_LOCK_STATUS = TRUE
READ_DISABLED_CAP = TRUE
READ_ENABLED_CAP = TRUE
READ_STATUS = TRUE
READ_LOCK_CAP = TRUE
READ_LOCK_STATUS = TRUE
上面的FV 是由两个 Section 构成的,一个是 PEIFV,另一个是 DXEFV:
1.下面是对于 PEIFV 的定义:
[FV.PEIFV]
FvNameGuid = 6938079B-B503-4E3D-9D24-B28337A25806
BlockSize = 0x10000
FvAlignment = 16
ERASE_POLARITY = 1
MEMORY_MAPPED = TRUE
STICKY_WRITE = TRUE
LOCK_CAP = TRUE
LOCK_STATUS = TRUE
WRITE_DISABLED_CAP = TRUE
WRITE_ENABLED_CAP = TRUE
WRITE_STATUS = TRUE
WRITE_LOCK_CAP = TRUE
WRITE_LOCK_STATUS = TRUE
READ_DISABLED_CAP = TRUE
READ_ENABLED_CAP = TRUE
READ_STATUS = TRUE
READ_LOCK_CAP = TRUE
READ_LOCK_STATUS = TRUE
APRIORI PEI {
INF MdeModulePkg/Universal/PCD/Pei/Pcd.inf
}
2.下面是 DXEFV 的定义
[FV.DXEFV]
FvForceRebase = FALSE
FvNameGuid = 7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1
BlockSize = 0x10000
FvAlignment = 16
ERASE_POLARITY = 1
MEMORY_MAPPED = TRUE
STICKY_WRITE = TRUE
LOCK_CAP = TRUE
LOCK_STATUS = TRUE
WRITE_DISABLED_CAP = TRUE
WRITE_ENABLED_CAP = TRUE
WRITE_STATUS = TRUE
WRITE_LOCK_CAP = TRUE
WRITE_LOCK_STATUS = TRUE
READ_DISABLED_CAP = TRUE
READ_ENABLED_CAP = TRUE
READ_STATUS = TRUE
READ_LOCK_CAP = TRUE
READ_LOCK_STATUS = TRUE
APRIORI DXE {
INF MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
INF MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
INF OvmfPkg/AmdSevDxe/AmdSevDxe.inf
!if $(SMM_REQUIRE) == FALSE
INF OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
!endif
}
这里可以使用 UEFITool 直接将内容解压出来,例如:
可以用 Z7 查看解压出来的文件,但是7Z 只能看到 PEIFV 看不到DXEFV, 我猜测是因为格式原因,只能看到前面一半:
具体的 DXEFV 和 PEIFV 可以在 \Build\OvmfX64\DEBUG_VS2015x86\FV 下面看到:
有了这两个文件,就可以生成出现在 OVMF.FD 中的 FV 了。为了便于描述,使用下面的流程图:
CMD1: 生成 9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.1fv.sec
GenSec -s EFI_SECTION_FIRMWARE_VOLUME_IMAGE -o d:\i2c\Build\OvmfX64\DEBUG_VS2015x86\FV\Ffs\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792FVMAIN_COMPACT\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.1fv.sec d:\i2c\Build\OvmfX64\DEBUG_VS2015x86\FV\PEIFV.Fv
CMD2: 生成9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.2fv.sec
GenSec -s EFI_SECTION_FIRMWARE_VOLUME_IMAGE -o d:\i2c\Build\OvmfX64\DEBUG_VS2015x86\FV\Ffs\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792FVMAIN_COMPACT\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.2fv.sec d:\i2c\Build\OvmfX64\DEBUG_VS2015x86\FV\DXEFV.Fv
CMD3: 将上面两个文件合成为一个文件 9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.guided.dummy
GenSec –sectionalign 128 –sectionalign 16 -o d:\i2c\Build\OvmfX64\DEBUG_VS2015x86\FV\Ffs\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792FVMAIN_COMPACT\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.guided.dummy d:\i2c\Build\OvmfX64\DEBUG_VS2015x86\FV\Ffs\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792FVMAIN_COMPACT\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.1fv.sec d:\i2c\Build\OvmfX64\DEBUG_VS2015x86\FV\Ffs\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792FVMAIN_COMPACT\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.2fv.sec
CMD4: 对上面的文件进行压缩,生成 9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.tmp 文件,从 11MB 压缩到了1MB左右
LzmaCompress -e -o d:\\i2c\\Build\\OvmfX64\\DEBUG_VS2015x86\\FV\\Ffs\\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792FVMAIN_COMPACT\\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.tmp d:\\i2c\\Build\\OvmfX64\\DEBUG_VS2015x86\\FV\\Ffs\\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792FVMAIN_COMPACT\\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.guided.dummy
CMD5: 继续打包生成 9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.tmp
GenSec -s EFI_SECTION_GUID_DEFINED -g EE4E5898-3914-4259-9D6E-DC7BD79403CF -r PROCESSING_REQUIRED -o d:\i2c\Build\OvmfX64\DEBUG_VS2015x86\FV\Ffs\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792FVMAIN_COMPACT\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.guided d:\i2c\Build\OvmfX64\DEBUG_VS2015x86\FV\Ffs\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792FVMAIN_COMPACT\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.tmp
CMD6:生成 FFS文件,9E21FD93-9C72-4c15-8C4B-E77F1DB2D792.ffs
GenFfs -t EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE -g 9E21FD93-9C72-4c15-8C4B-E77F1DB2D792 -o d:\i2c\Build\OvmfX64\DEBUG_VS2015x86\FV\Ffs\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792FVMAIN_COMPACT\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792.ffs -i d:\i2c\Build\OvmfX64\DEBUG_VS2015x86\FV\Ffs\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792FVMAIN_COMPACT\9E21FD93-9C72-4c15-8C4B-E77F1DB2D792SEC1.guided
CMD7:最终生成 FVMAIN_COMPACT.Fv,用 Beyond Compare 可以看到这个是 OVMF.FD 的一部分。
GenFv -a d:\i2c\Build\OvmfX64\DEBUG_VS2015x86\FV\Ffs\FVMAIN_COMPACT.inf -o d:\i2c\Build\OvmfX64\DEBUG_VS2015x86\FV\FVMAIN_COMPACT.Fv -i d:\i2c\Build\OvmfX64\DEBUG_VS2015x86\FV\FVMAIN_COMPACT.inf
生成的FVMAIN_COMPACT.Fv 就是 OVMF 中间的一部分。
之前介绍过SYN6288 模块【参考1】,这次配合 ESP32 实现随机生成一个时间,然后通过语音播放出来。
硬件使用的是ESP-WROOM-32 ESP-32S模块,语音模块的 RX 连接到 ESP32 的GPIO17:
String DataBuffer[11]={{"零"},{"一"},{"二"},{"三"},{"四"},{"五"},{"六"},{"七"},{"八"},{"九"},{"十"}};
//存放转化后的汉字 Unicode值
char character[40];
#include <HardwareSerial.h>
//根据字符串计算计算出来的送到串口的值
char output[50];
void setup() {
Serial.begin(115200);
Serial2.begin(9600);
delay(1000);
}
void loop() {
Serial.println("generate");
int hh=random(13),mm=random(60);
String timeStr="";
Serial.print("Generate time: ");
Serial.print(hh);Serial.print(":");Serial.println(mm);
if (hh>9) {timeStr+="十";
if (hh>10) {timeStr+=DataBuffer[hh-10];}
}
else {timeStr+=DataBuffer[hh];}
timeStr+="点";
if (mm==0) {timeStr+="整";}
else
if (mm<10) {timeStr+="零";timeStr+=DataBuffer[mm];}
else if (mm%10==0) {timeStr+=DataBuffer[mm/10]; timeStr+="十";}
else {timeStr+=DataBuffer[mm/10];timeStr+="十";timeStr+=DataBuffer[mm%10];}
if(mm!=0) {timeStr+="分";}
//timeStr="十二点五十八分";
//首先输出一次Arduino 原始字符串 UTF8 的值
for (int i =0;i<timeStr.length()*3;i++) {
Serial.print(timeStr[i]&0xFF,HEX);
Serial.print(' ');
}
Serial.println(' ');
//将 UTF8 转化为 Unicode
for (int i =0;i<timeStr.length()/3;i=i+1) {
character[i*2]=((timeStr[i*3]&0xF)<<4)+((timeStr[i*3+1]>>2)&0xF);
character[i*2+1]=((timeStr[i*3+1]&0x3)<<6)+(timeStr[i*3+2]&0x3F);
Serial.print(character[i*2]&0xFF,HEX);
Serial.print(' ');
Serial.print(character[i*2+1]&0xFF,HEX);
Serial.print(' ');
}
Serial.println("");
output[0]=0xFD;
output[1]=(timeStr.length()/3*2+3)>>8;
output[2]=((timeStr.length()/3*2+3)&0xFF);
output[3]=0x01;
output[4]=0x03;
//把字符串定义搬过去
for (int i=0;i<timeStr.length()/3*2;i++) {
output[i+5]=character[i];
}
//计算一个校验和
output[timeStr.length()/3*2+5]=output[0];
for (int i=1;i<timeStr.length()/3*2+5;i++) {
output[timeStr.length()/3*2+5]=output[timeStr.length()/3*2+5] ^ output[i];
}
for (int i =0;i<timeStr.length()/3*2+6;i++) {
Serial.print(output[i]&0xFF,HEX);
Serial.print(' ');
Serial2.write(output[i]);
}
Serial.println(' ');
delay(3000);
}
参考:
1. http://www.lab-z.com/ttssyn/ TTS 真人发音 SYN6288 模块
前面提到过 OVMF 的 Setup 首页版本号显示的代码【参考1】,在 \MdeModulePkg\Application\UiApp\FrontPage.c
SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
Status = Smbios->GetNext (Smbios, &SmbiosHandle, NULL, &Record, NULL);
while (!EFI_ERROR(Status)) {
if (Record->Type == SMBIOS_TYPE_BIOS_INFORMATION) {
Type0Record = (SMBIOS_TABLE_TYPE0 *) Record;
StrIndex = Type0Record->BiosVersion;
GetOptionalStringByIndex ((CHAR8*)((UINT8*)Type0Record + Type0Record->Hdr.Length), StrIndex, &NewString);
FirmwareVersionString = (CHAR16 *) PcdGetPtr (PcdFirmwareVersionString);
if (*FirmwareVersionString != 0x0000 ) {
FreePool (NewString);
NewString = (CHAR16 *) PcdGetPtr (PcdFirmwareVersionString);
UiCustomizeFrontPageBanner (3, TRUE, &NewString);
HiiSetString (gFrontPagePrivate.HiiHandle, STRING_TOKEN (STR_FRONT_PAGE_BIOS_VERSION), NewString, NULL);
} else {
UiCustomizeFrontPageBanner (3, TRUE, &NewString);
HiiSetString (gFrontPagePrivate.HiiHandle, STRING_TOKEN (STR_FRONT_PAGE_BIOS_VERSION), NewString, NULL);
FreePool (NewString);
}
}
就是说出了从 SMBIOS 之外,还可以在 PCD 中直接给定。实验,在\OvmfPkg\OvmfPkgX64.dsc 文件中加入下面的代码
!if $(SMM_REQUIRE) == TRUE
gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire|TRUE
gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmEnableBspElection|FALSE
!endif
[PcdsFixedAtBuild]
#LABZ_Start
gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwareVersionString|L"Galileo 1.0.4"
#LABZ_End
gEfiMdeModulePkgTokenSpaceGuid.PcdStatusCodeMemorySize|1
gEfiMdeModulePkgTokenSpaceGuid.PcdResetOnMemoryTypeInformationChange|FALSE
gEfiMdePkgTokenSpaceGuid.PcdMaximumGuidedExtractHandler|0x10
gEfiMdeModulePkgTokenSpaceGuid.PcdPeiCoreMaxFvSupported|6
编译之后,运行结果如下,可以看到新增加了 “Galileo 1.0.4”字样。
但是,非常奇怪的是按照之前的方法,无法在 FFS 文件中找到对应的字符串。为了进一步研究,打开生成 COD 的功能。在 \MdeModulePkg\Application\UiApp\UiApp.inf 加入下面这样一行:
[BuildOptions]
MSFT:*_*_X64_CC_FLAGS = /FAsc /Od
再次编译,在
\Build\OvmfX64\DEBUG_VS2015x86\X64\MdeModulePkg\Application\UiApp\UiApp\DEBUG\AutoGen.c 有如下定义:
GLOBAL_REMOVE_IF_UNREFERENCED const UINT16 _gPcd_FixedAtBuild_PcdFirmwareVersionString[14] = {71, 97, 108, 105, 108, 101, 111, 32, 49, 46, 48, 46, 52, 0 };
进一步,在 \Build\OvmfX64\DEBUG_VS2015x86\X64\MdeModulePkg\Application\UiApp\UiApp\AutoGen.cod 可以看到下面的定义
; COMDAT _gPcd_FixedAtBuild_PcdFirmwareVersionString
CONST SEGMENT
_gPcd_FixedAtBuild_PcdFirmwareVersionString DW 047H
DW 061H
DW 06cH
DW 069H
DW 06cH
DW 065H
DW 06fH
DW 020H
DW 031H
DW 02eH
DW 030H
DW 02eH
DW 034H
DW 00H
CONST ENDS
特别注意,每一个字符是 2Bytes,因此,就是说和之前不同,这里字符串使用的是 Unicode 编码方式。因此,一个字符会使用2个Bytes来进行保存。再次到 FFS 中搜索,可以看到如下字样:
尝试修改之,然后用前面直接运行 GenFDS 的方法产生的 ROM 文件,运行结果如下:
进一步研究,在【参考2】有介绍,我们定义的gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwareVersionString 是 FixedAtBuild PCD的PCD 变量,编译时会像宏一样直接展开放在代码中。
参考:
1.http://www.lab-z.com/qemusetup/ QEMU Setup 首页研究
2. https://blog.csdn.net/jiangwei0512/article/details/80288001 BIOS/UEFI基础——PCD
带着问题进行学习是掌握一门技术非常有效的方法。
https://hdlbits.01xz.net/ 网站提供了 Verilog 的一些题目,有兴趣的朋友可以尝试进行练习。同时这个网站在提供 Verilog 基础语法教程的同时,还能够在线仿真你的 Verilog 模块,将你的输出与正确的时序比较。
如果你在解题时遇到问题,还可以在知乎“HDLBits 中文导学”专栏中看到解析(比如我经常无法理解题目意思),还可以在评论中参加讨论。
最近研究了一下 OVMF 项目的 Setup 首页界面,实现的方法挺有意思。
第一个问题,具体代码在什么地方,如果我想增加一行字应该如何实现。
经过研究,相关代码在 MdeModulePkg\Application\UiApp\FrontPage.c 中。首先,在 FrontPageVfr.Vfr 定义了8个字符串变量,例如:STR_CUSTOMIZE_BANNER_LINE4_LEFT。 在首页上面左右各有4个。在 UpdateFrontPageBannerStrings() 函数中动态填充这些字符串。例如:从 SMBIOS 中取得CPU 信息显示出来:
if ((Record->Type == SMBIOS_TYPE_PROCESSOR_INFORMATION) && !FoundCpu) {
Type4Record = (SMBIOS_TABLE_TYPE4 *) Record;
//
// The information in the record should be only valid when the CPU Socket is populated.
//
if ((Type4Record->Status & SMBIOS_TYPE4_CPU_SOCKET_POPULATED) == SMBIOS_TYPE4_CPU_SOCKET_POPULATED) {
StrIndex = Type4Record->ProcessorVersion;
GetOptionalStringByIndex ((CHAR8*)((UINT8*)Type4Record + Type4Record->Hdr.Length), StrIndex, &NewString);
UiCustomizeFrontPageBanner (2, TRUE, &NewString);
HiiSetString (gFrontPagePrivate.HiiHandle, STRING_TOKEN (STR_FRONT_PAGE_CPU_MODEL), NewString, NULL);
FreePool (NewString);
ConvertProcessorToString(Type4Record->CurrentSpeed, 6, &NewString);
UiCustomizeFrontPageBanner (2, FALSE, &NewString);
HiiSetString (gFrontPagePrivate.HiiHandle, STRING_TOKEN (STR_FRONT_PAGE_CPU_SPEED), NewString, NULL);
FreePool (NewString);
FoundCpu = TRUE;
}
}
知道了上面的实现,如果想实现增加一行字符串,实现非常简单:在 FrontPageStrings.uni 文件中修改字符串增加内容即可:
#string STR_CUSTOMIZE_BANNER_LINE4_LEFT #language en-US "www.lab-z.com"
#language fr-FR ""
运行结果:
第二个问题,研究一下BIOS 版本号的显示。取得的方法非常类似,同样的文件中,代码如下:
if (Record->Type == SMBIOS_TYPE_BIOS_INFORMATION) {
Type0Record = (SMBIOS_TABLE_TYPE0 *) Record;
StrIndex = Type0Record->BiosVersion;
GetOptionalStringByIndex ((CHAR8*)((UINT8*)Type0Record + Type0Record->Hdr.Length), StrIndex, &NewString);
FirmwareVersionString = (CHAR16 *) PcdGetPtr (PcdFirmwareVersionString);
if (*FirmwareVersionString != 0x0000 ) {
FreePool (NewString);
NewString = (CHAR16 *) PcdGetPtr (PcdFirmwareVersionString);
UiCustomizeFrontPageBanner (3, TRUE, &NewString);
HiiSetString (gFrontPagePrivate.HiiHandle, STRING_TOKEN (STR_FRONT_PAGE_BIOS_VERSION), NewString, NULL);
} else {
UiCustomizeFrontPageBanner (3, TRUE, &NewString);
HiiSetString (gFrontPagePrivate.HiiHandle, STRING_TOKEN (STR_FRONT_PAGE_BIOS_VERSION), NewString, NULL);
FreePool (NewString);
}
}
就是说,如果没有定义 PcdFirmwareVersionString 那么会从 SMBIOS 中抓取BIOS 版本显示出来。
在 \OvmfPkg\SmbiosPlatformDxe\SmbiosPlatformDxe.c 可以看到版本信息定义如下:
#define TYPE0_STRINGS \
"EFI Development Kit II / OVMF\0" /* Vendor */ \
"0.0.0\0" /* BiosVersion */ \
"02/06/2015\0" /* BiosReleaseDate */
可以通过直接修改上面代码的方式来实现修改版本号的目标。作为“寻常不走路”的有志青年,当然不会满足于使用这样的方式来修改版本号。下面介绍直接在二进制上修改这个版本号。
在 Build\OvmfX64\DEBUG_VS2015x86\FV\Ffs\4110465d-5ff3-4f4b-b580-24ed0d06747aSmbiosPlatformDxe 目录下,可以找到 4110465d-5ff3-4f4b-b580-24ed0d06747a.ffs 这个文件。打开文件可以搜索到 “1.2.3”字样:
尝试修改为 “4.5.6” 之后,再次使用之前介绍过的 GenFds ,生成 FD 文件,运行后就可以发现字符发生了变化:
这个实验也从侧面证明了 GenFds 是将需要的 FFS 文件集合在一起,然后压缩放入指定的文件中。
SleepStudy 是微软提供的一个查看功耗相关的功能,在所有的 Windows 10 中都有无需额外下载安装。使用方法:
1.打开 CMD 窗口,特别注意需要管理员权限;
2.运行 powercfg /sleepstudy, 之后会将报告生成在一个 html 文件中
例如,下面就是一个 Pass 的结果,可以看到系统进入了 MS,98% 的 Software Residency和 Hardware Residency。
而这个是一个 Fail 的结果:Software Residency 为0 ,没有获得到 Hardware Residency。
这个报告在进行 Stress 测试的时候非常有用,能够一次性看到每一个Loop的结果,当然如果有问题,还需要运行 PHM 进行分析。
这个报告在进行 Stress 测试的时候非常有用,能够一次性看到每一个Loop的结果,当然如果有问题,还需要运行 PHM 进行分析。
特别注意:
2021年5月19日 最近又在测试 MS 功能,借此机会研究了一下清除 SleepStudy Log 的方法:清除 Event 是无效的,但是可以通过删除 Windows\system32\sleepStudy 目录下的内容来实现清除 SleepStudy Log 的目的。
当你使用 import YourFile.py 之后YourFile.py就会加载到内存中,即使你修改了本地的文件,再次调用也会是修改之前的代码。因此,这里需要用 reload 来强制重新加载。使用一个例子进行说明,首先编写一个简单的代码:
def Test():
print(“Test Script from labz”)
下面的步骤1导入上面的代码,然后运行 Test() 函数。接下来修改上面的代码,在输出的字符串末尾加入字符 2.步骤2中再次Import 然后运行,可以看到没有效果:
之后从 imp 中 import reload 函数(我的 python 是 3.8 , 这里同 2.X 的版本有差别)。再次 reload 代码之后可以看到结果有变化。
总结:可以通过 from imp import reload, 之后每次 reload 你需要的文件即可刷新。