1.图片放大工具,可以让你轻松放大图片
2.SVG 格式转Png 格式工具。SVG 是一种矢量格式,可以无损的进行放大缩小。
1.图片放大工具,可以让你轻松放大图片
2.SVG 格式转Png 格式工具。SVG 是一种矢量格式,可以无损的进行放大缩小。
在实验中,我发现 QEMU 启动的虚拟机并不支持调出 BootMenu,为此研究一下如何修改BIOS 添加这个功能。
前面的实验中,我们在启动项目中插入了自定义的 Application,主要代码在BdsPlatform.c 中。继续查看这个文件,可以看到分配快捷键的代码:
VOID
PlatformRegisterOptionsAndKeys (
VOID
)
{
EFI_STATUS Status;
EFI_INPUT_KEY Enter;
EFI_INPUT_KEY F2;
EFI_INPUT_KEY Esc;
EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
//
// Register ENTER as CONTINUE key
//
Enter.ScanCode = SCAN_NULL;
Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;
Status = EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);
ASSERT_EFI_ERROR (Status);
//
// Map F2 to Boot Manager Menu
//
F2.ScanCode = SCAN_F2;
F2.UnicodeChar = CHAR_NULL;
Esc.ScanCode = SCAN_ESC;
Esc.UnicodeChar = CHAR_NULL;
Status = EfiBootManagerGetBootManagerMenu (&BootOption);
ASSERT_EFI_ERROR (Status);
Status = EfiBootManagerAddKeyOptionVariable (
NULL, (UINT16) BootOption.OptionNumber, 0, &F2, NULL
);
ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
Status = EfiBootManagerAddKeyOptionVariable (
NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL
);
ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
}
和EmulatorPkg 中的PlatformBdsRegisterStaticBootOptions() 函数进行对比(代码位于 \EmulatorPkg\Library\PlatformBmLib\PlatformBm.c中),可以看到后者注册了多个快捷键对应着不同的功能:
/**
Register the static boot options.
**/
VOID
PlatformBdsRegisterStaticBootOptions (
VOID
)
{
EFI_GRAPHICS_OUTPUT_BLT_PIXEL Black;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL White;
EFI_INPUT_KEY Enter;
EFI_INPUT_KEY F2;
EFI_INPUT_KEY F7;
EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
UINTN OptionNumber;
Black.Blue = Black.Green = Black.Red = Black.Reserved = 0;
White.Blue = White.Green = White.Red = White.Reserved = 0xFF;
//
// Register ENTER as CONTINUE key
//
Enter.ScanCode = SCAN_NULL;
Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;
EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);
//
// Map F2 to Boot Manager Menu
//
F2.ScanCode = SCAN_F2;
F2.UnicodeChar = CHAR_NULL;
EfiBootManagerGetBootManagerMenu (&BootOption);
EfiBootManagerAddKeyOptionVariable (NULL, (UINT16) BootOption.OptionNumber, 0, &F2, NULL);
//
// 3. Boot Device List menu
//
F7.ScanCode = SCAN_F7;
F7.UnicodeChar = CHAR_NULL;
OptionNumber = GetBootManagerMenuAppOption ();
EfiBootManagerAddKeyOptionVariable (NULL, (UINT16)OptionNumber, 0, &F7, NULL);
PrintXY (10, 10, &White, &Black, L"F2 to enter Setup. ");
PrintXY (10, 30, &White, &Black, L"F7 to enter Boot Manager Menu.");
PrintXY (10, 50, &White, &Black, L"Enter to boot directly.");
}
最终修改\OvmfPkg\Library\PlatformBootManagerLib\BdsPlatform.c 代码如下:
//LABZ_Start
EFI_GUID mBootMenuFile = {
0xEEC25BDC, 0x67F2, 0x4D95, { 0xB1, 0xD5, 0xF8, 0x1B, 0x20, 0x39, 0xD1, 0x1D }
};
/**
Check if it's a Device Path pointing to BootManagerMenuApp.
@param DevicePath Input device path.
@retval TRUE The device path is BootManagerMenuApp File Device Path.
@retval FALSE The device path is NOT BootManagerMenuApp File Device Path.
**/
BOOLEAN
IsBootManagerMenuAppFilePath (
EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
EFI_HANDLE FvHandle;
VOID *NameGuid;
EFI_STATUS Status;
Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &DevicePath, &FvHandle);
if (!EFI_ERROR (Status)) {
NameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) DevicePath);
if (NameGuid != NULL) {
return CompareGuid (NameGuid, &mBootMenuFile);
}
}
return FALSE;
}
EFI_DEVICE_PATH *
FvFilePath (
EFI_GUID *FileGuid
)
{
EFI_STATUS Status;
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);
Status = gBS->HandleProtocol (
gImageHandle,
&gEfiLoadedImageProtocolGuid,
(VOID **) &LoadedImage
);
ASSERT_EFI_ERROR (Status);
return AppendDevicePathNode (
DevicePathFromHandle (LoadedImage->DeviceHandle),
(EFI_DEVICE_PATH_PROTOCOL *) &FileNode
);
}
/**
Create one boot option for BootManagerMenuApp.
@param FileGuid Input file guid for the BootManagerMenuApp.
@param Description Description of the BootManagerMenuApp boot option.
@param Position Position of the new load option to put in the ****Order variable.
@param IsBootCategory Whether this is a boot category.
@retval OptionNumber Return the option number info.
**/
UINTN
RegisterBootManagerMenuAppBootOption (
EFI_GUID *FileGuid,
CHAR16 *Description,
UINTN Position,
BOOLEAN IsBootCategory
)
{
EFI_STATUS Status;
EFI_BOOT_MANAGER_LOAD_OPTION NewOption;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
UINTN OptionNumber;
DevicePath = FvFilePath (FileGuid);
Status = EfiBootManagerInitializeLoadOption (
&NewOption,
LoadOptionNumberUnassigned,
LoadOptionTypeBoot,
IsBootCategory ? LOAD_OPTION_ACTIVE : LOAD_OPTION_CATEGORY_APP,
Description,
DevicePath,
NULL,
0
);
ASSERT_EFI_ERROR (Status);
FreePool (DevicePath);
Status = EfiBootManagerAddLoadOptionVariable (&NewOption, Position);
ASSERT_EFI_ERROR (Status);
OptionNumber = NewOption.OptionNumber;
EfiBootManagerFreeLoadOption (&NewOption);
return OptionNumber;
}
/**
Return the boot option number to the BootManagerMenuApp.
If not found it in the current boot option, create a new one.
@retval OptionNumber Return the boot option number to the BootManagerMenuApp.
**/
UINTN
GetBootManagerMenuAppOption (
VOID
)
{
UINTN BootOptionCount;
EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
UINTN Index;
UINTN OptionNumber;
OptionNumber = 0;
BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
for (Index = 0; Index < BootOptionCount; Index++) {
if (IsBootManagerMenuAppFilePath (BootOptions[Index].FilePath)) {
OptionNumber = BootOptions[Index].OptionNumber;
break;
}
}
EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
if (Index >= BootOptionCount) {
//
// If not found the BootManagerMenuApp, create it.
//
OptionNumber = (UINT16) RegisterBootManagerMenuAppBootOption (&mBootMenuFile, L"UEFI BootManagerMenuApp", (UINTN) -1, FALSE);
}
return OptionNumber;
}
//LABZ_End
VOID
PlatformRegisterOptionsAndKeys (
VOID
)
{
EFI_STATUS Status;
EFI_INPUT_KEY Enter;
EFI_INPUT_KEY F2;
EFI_INPUT_KEY F7;
EFI_INPUT_KEY Esc;
EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
UINTN OptionNumber;
//
// Register ENTER as CONTINUE key
//
Enter.ScanCode = SCAN_NULL;
Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;
Status = EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);
ASSERT_EFI_ERROR (Status);
//
// Map F2 to Boot Manager Menu
//
F2.ScanCode = SCAN_F2;
F2.UnicodeChar = CHAR_NULL;
Esc.ScanCode = SCAN_ESC;
Esc.UnicodeChar = CHAR_NULL;
Status = EfiBootManagerGetBootManagerMenu (&BootOption);
ASSERT_EFI_ERROR (Status);
Status = EfiBootManagerAddKeyOptionVariable (
NULL, (UINT16) BootOption.OptionNumber, 0, &F2, NULL
);
ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
Status = EfiBootManagerAddKeyOptionVariable (
NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL
);
ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
//LABZ_Start
//
// 3. Boot Device List menu
//
F7.ScanCode = SCAN_F7;
F7.UnicodeChar = CHAR_NULL;
OptionNumber = GetBootManagerMenuAppOption ();
EfiBootManagerAddKeyOptionVariable (NULL, (UINT16)OptionNumber, 0, &F7, NULL);
//LABZ_End
}
此外,在 \OvmfPkg\OvmfPkgX64.dsc 中添加BootManagerMenuApp.inf如下:
<PcdsFixedAtBuild>
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0xFF
gEfiShellPkgTokenSpaceGuid.PcdShellLibAutoInitialize|FALSE
gEfiMdePkgTokenSpaceGuid.PcdUefiLibMaxPrintBufferSize|8000
}
MdeModulePkg/Application/BootManagerMenuApp/BootManagerMenuApp.inf
!if $(SECURE_BOOT_ENABLE) == TRUE
SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
OvmfPkg/EnrollDefaultKeys/EnrollDefaultKeys.inf
!endif
\OvmfPkg\OvmfPkgX64.fdf修改如下:
!if $(TOOL_CHAIN_TAG) != "XCODE5"
INF ShellPkg/DynamicCommand/TftpDynamicCommand/TftpDynamicCommand.inf
INF ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf
INF OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf
!endif
INF ShellPkg/Application/Shell/Shell.inf
INF MdeModulePkg/Application/BootManagerMenuApp/BootManagerMenuApp.inf
INF MdeModulePkg/Logo/LogoDxe.inf
#
# Network modules
#
!if $(E1000_ENABLE)
可以看到整体和之前的“BootMenu插入自己的Application”并无多大差别,只是多了使用EfiBootManagerAddKeyOptionVariable() 函数将 Application 和快捷键关联起来的。
运行结果:
有时候为了确定代码是否编译到,我们需要在代码中添加让编译器报错的指令,对于 MASM 来说是 .ERR。在 NASM 中是”%error”【参考1】。编写例子如下:
segment code
.start:
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,stacktop
%error "assert error here"
mov ax,0x0600
mov bh,0x43
xor cx,cx
mov dh,24
mov dl,79
int 0x10
mov dx,hello
mov ah,9
int 0x21
mov ax,0x4c00
int 0x21
segment data
hello : db 'hello,world',13,10,'$'
segment stack stack
resb 64
stacktop:
编译时会自动报错如下:
此外,从我的实验来看,在 BIOS 代码中的 NASM 代码中随意插入字符并不能使得编译过程报错停止下来,所以如果有需要务必使用上述方法。
参考:
4.9 Reporting User-Defined Errors: %error, %warning, %fatal
The preprocessor directive %error will cause NASM to report an error if it occurs in assembled code. So if other users are going to try to assemble your source files, you can ensure that they define the right macros by means of code like this:
%ifdef F1
; do some setup
%elifdef F2
; do some different setup
%else
%error “Neither F1 nor F2 was defined.”
%endif
Then any user who fails to understand the way your code is supposed to be assembled will be quickly warned of their mistake, rather than having to wait until the program crashes on being run and then not knowing what went wrong.
Similarly, %warning issues a warning, but allows assembly to continue:
%ifdef F1
; do some setup
%elifdef F2
; do some different setup
%else
%warning “Neither F1 nor F2 was defined, assuming F1.”
%define F1
%endif
%error and %warning are issued only on the final assembly pass. This makes them safe to use in conjunction with tests that depend on symbol values.
%fatal terminates assembly immediately, regardless of pass. This is useful when there is no point in continuing the assembly further, and doing so is likely just going to cause a spew of confusing error messages.
It is optional for the message string after %error, %warning or %fatal to be quoted. If it is not, then single-line macros are expanded in it, which can be used to display more information to the user. For example:
%if foo > 64
%assign foo_over foo-64
%error foo is foo_over bytes too large
%endif
之前介绍过的通过WinPE启动,通过软件将安装好的硬盘制作成镜像,然后当有需要时直接恢复的方法。但是更多时候我们出于测试的目的,需要安装最新版本的 Pure OS。 这次介绍的全自动安装也就是无人值守模式安装,就是说只需要启动到安装盘上,Windows会自动完成包括分区格式化在内的全部工作。这是Windows自带的功能,只需要将特定的autounattend.xml 放置在启动盘的根目录下即可。具体做法如下:
1. 在 https://schneegans.de/windows/unattend-generator/ 根据需要生成 autounattend.xml 文件;
2.对于有要求配置 WIFI 的Windows可以在XML 文件中加入
<OOBE>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
</OOBE>
3.将 autounattend.xml 文件放置在安装盘的根目录下,启动安装即可。
这里放一个我制作好的:
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
<settings pass="offlineServicing" />
<settings pass="windowsPE">
<component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<SetupUILanguage>
<UILanguage>en-US</UILanguage>
</SetupUILanguage>
<InputLocale>0409:00000409</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<DiskConfiguration>
<WillShowUI>OnError</WillShowUI>
<Disk wcm:action="add">
<CreatePartitions>
<!-- EFI system partition -->
<CreatePartition wcm:action="add">
<Order>1</Order>
<Type>EFI</Type>
<Size>100</Size>
</CreatePartition>
<!-- Microsoft reserved partition (MSR) -->
<CreatePartition wcm:action="add">
<Order>2</Order>
<Type>MSR</Type>
<Size>16</Size>
</CreatePartition>
<!-- Windows partition -->
<CreatePartition wcm:action="add">
<Order>3</Order>
<Type>Primary</Type>
<Extend>true</Extend>
</CreatePartition>
</CreatePartitions>
<ModifyPartitions>
<!-- EFI system partition -->
<ModifyPartition wcm:action="add">
<Order>1</Order>
<PartitionID>1</PartitionID>
<Label>System</Label>
<Format>FAT32</Format>
</ModifyPartition>
<!-- Microsoft reserved partition (MSR) -->
<ModifyPartition wcm:action="add">
<Order>2</Order>
<PartitionID>2</PartitionID>
</ModifyPartition>
<!-- Windows partition -->
<ModifyPartition wcm:action="add">
<Order>3</Order>
<PartitionID>3</PartitionID>
<Label>Windows</Label>
<Format>NTFS</Format>
<Letter>C</Letter>
</ModifyPartition>
</ModifyPartitions>
<DiskID>0</DiskID>
<WillWipeDisk>true</WillWipeDisk>
</Disk>
</DiskConfiguration>
<ImageInstall>
<OSImage>
<InstallTo>
<DiskID>0</DiskID>
<PartitionID>3</PartitionID>
</InstallTo>
</OSImage>
</ImageInstall>
<UserData>
<ProductKey>
<Key>VK7JG-NPHTM-C97JM-9MPGT-3V66T</Key>
</ProductKey>
<AcceptEula>true</AcceptEula>
</UserData>
</component>
</settings>
<settings pass="generalize" />
<settings pass="specialize" />
<settings pass="auditSystem" />
<settings pass="auditUser" />
<settings pass="oobeSystem">
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<InputLocale>0409:00000409</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<UserAccounts>
<AdministratorPassword>
<Value>password</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
</UserAccounts>
<AutoLogon>
<Username>Administrator</Username>
<Enabled>true</Enabled>
<LogonCount>1</LogonCount>
<Password>
<Value>password</Value>
<PlainText>true</PlainText>
</Password>
</AutoLogon>
<OOBE>
<ProtectYourPC>3</ProtectYourPC>
<HideEULAPage>true</HideEULAPage>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
</OOBE>
</component>
</settings>
</unattend>
安装后对于有一些 Windows 会要求输入密码,对应的 Administrator 密码为 password
虚拟机中测试视频可以在B站看到 ,在VirtualBox上安装 22616.1.220502-1800
本文提到的 autounattend.xml 下载
这次介绍一个比较好玩的:通过 ESP32 创建一个 WebServer ,通过浏览器访问的时候自动播放预先定义好的图片序列。
制作方法:
1.创建一个 640×480的图片,放上彩色文字,便于观察,保存为 JPG 格式
2.旋转文字,再保存成另外一个文件
3.重复上述动作我们能获得4个文件
4.使用 bin2c 工具将四个文件转化为4个C语言h文件
5.文件头定义中做一点调整,图片大小命名为 picN_size, 内容命名为 picN 这种
6.接下来就可以编写我们的代码了
#include "esp_camera.h"
#include <WiFi.h>
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "esp_http_server.h"
#include "pic\ph0.h"
#include "pic\ph1.h"
#include "pic\ph2.h"
#include "pic\ph3.h"
// 这里改成你自己的 WIFI 名称和密码
const char* ssid = "YOUWIFI";
const char* password = "PASSWORD";
#define PART_BOUNDARY "123456789000000000000987654321"
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
httpd_handle_t stream_httpd = NULL;
static esp_err_t stream_handler(httpd_req_t *req) {
esp_err_t res = ESP_OK;
size_t _jpg_buf_len = 0;
uint8_t * _jpg_buf = NULL;
char * part_buf[64];
static int index=0;
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
if (res != ESP_OK) {
return res;
}
while (true) {
// 这里设定一个循环,轮流发送PIC0-4
if (index==0) {
_jpg_buf_len = pic0_size;
_jpg_buf = (uint8_t *)pic0;
} else if (index==1) {
_jpg_buf_len = pic1_size;
_jpg_buf = (uint8_t *)pic1;
} else if (index==2) {
_jpg_buf_len = pic2_size;
_jpg_buf = (uint8_t *)pic2;
} else if (index==3) {
_jpg_buf_len = pic3_size;
_jpg_buf = (uint8_t *)pic3;
}
// 如果发送完PIC3 接来发送PIC0
index=(index+1)%4;
if (res == ESP_OK) {
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
}
if (res == ESP_OK) {
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
}
if (res == ESP_OK) {
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
}
if (res != ESP_OK) {
break;
}
Serial.printf("MJPG: %uB\n",(uint32_t)(_jpg_buf_len));
}
return res;
}
void startPicServer() {
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 80;
httpd_uri_t index_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = stream_handler,
.user_ctx = NULL
};
if (httpd_start(&stream_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(stream_httpd, &index_uri);
}
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
Serial.begin(115200);
Serial.setDebugOutput(false);
// Wi-Fi connection
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("Camera Stream Ready! Go to: http://");
Serial.print(WiFi.localIP());
//启动
startPicServer();
}
void loop() {
delay(1);
}
7.运行之后串口监视器会输出当前 WebServer 的 IP ,将地址在浏览器中打开即可看到结果:
查看 Log 的过程中,发现在 DXE 阶段有错误报出,本文针对这个问题进行了一点研究。
首先,遇到的错误信息如下:
InstallProtocolInterface: BC62157E-3E33-4FEC-9920-2D3B36D750DF 7543E98
ProtectUefiImageCommon - 0x7543B40
- 0x0000000007105000 - 0x0000000000005E60
InstallProtocolInterface: AA0E8BC1-DABC-46B0-A844-37B8169B2BEA 710A9C0
Loading driver 4B28E4C7-FF36-4E10-93CF-A82159E777C5
InstallProtocolInterface: 5B1B31A1-9562-11D2-8E3F-00A0C969723B 7543140
!!!!!!!! InsertImageRecord - Section Alignment(0x20) is not 4K !!!!!!!!
!!!!!!!! Image - c:\buildbs\stable202108org\Build\OvmfX64\DEBUG_VS2015x86\X64\MdeModulePkg\Universal\ResetSystemRuntimeDxe\ResetSystemRuntimeDxe\DEBUG\ResetSystemRuntimeDxe.pdb !!!!!!!!
Loading driver at 0x00007AE1000 EntryPoint=0x00007AE14D4 ResetSystemRuntimeDxe.efi
InstallProtocolInterface: BC62157E-3E33-4FEC-9920-2D3B36D750DF 7543418
ProtectUefiImageCommon - 0x7543140
- 0x0000000007AE1000 - 0x00000000000041A0
接下来定位。输出错误的代码位于 \mdemodulepkg\core\dxe\misc\MemoryAttributesTable.c 中的InsertImageRecord() 函数中:
/**
Insert image record.
@param RuntimeImage Runtime image information
**/
VOID
InsertImageRecord (
IN EFI_RUNTIME_IMAGE_ENTRY *RuntimeImage
)
……………
//
// Get SectionAlignment
//
if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
SectionAlignment = Hdr.Pe32->OptionalHeader.SectionAlignment;
} else {
SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;
}
SetMemoryAttributesTableSectionAlignment (SectionAlignment);
if ((SectionAlignment & (RUNTIME_PAGE_ALLOCATION_GRANULARITY - 1)) != 0) {
DEBUG ((DEBUG_WARN, "!!!!!!!! InsertImageRecord - Section Alignment(0x%x) is not %dK !!!!!!!!\n",
SectionAlignment, RUNTIME_PAGE_ALLOCATION_GRANULARITY >> 10));
PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress);
if (PdbPointer != NULL) {
DEBUG ((DEBUG_WARN, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer));
}
goto Finish;
}
…………
在\MdePkg\Include\X64\ProcessorBind.h 有如下定义:
///
/// Page allocation granularity for x64
///
#define DEFAULT_PAGE_ALLOCATION_GRANULARITY (0x1000)
#define RUNTIME_PAGE_ALLOCATION_GRANULARITY (0x1000)
可以看到,SectionAlignment 来自 Hdr.Pe32Plus->OptionalHeader.SectionAlignment,使用 CFF 查看 ResetSystemRuntimeDxe.efi 这个文件,注意下图中的2个位置。上述代码首先检查ImageType(Subsystem) 是否为EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER,如果是再继续检查SectionAlignment是否为4K,如果不是 4K 对齐那么就进行报错。
ImageType的定义在\MdeModulePkg\Universal\ResetSystemRuntimeDxe\ResetSystemRuntimeDxe.inf 文件中:
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = ResetSystemRuntimeDxe
MODULE_UNI_FILE = ResetSystemRuntimeDxe.uni
FILE_GUID = 4B28E4C7-FF36-4e10-93CF-A82159E777C5
MODULE_TYPE = DXE_RUNTIME_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = InitializeResetSystem
搜索所有 MODULE_TYPE 为 DXE_RUNTIME_DRIVER 的 Module 发现在运行期都有这样的问题。
确定了问题,找到了问题点,接下来就开始分析“为什么出现不满足条件”。
检查\Build\OvmfX64\DEBUG_VS2015x86\X64\MdeModulePkg\Universal\ResetSystemRuntimeDxe\ResetSystemRuntimeDxe\Makefile 文件有下面一行:
DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG
(注意 /ALIGN:32 参数)
尝试修改上面为 /ALIGN:0x1000 修改之后再次编译(需要同样的环境,进入 Debug 目录直接运行 NMAKE,这种方法在之前的实验中有介绍过),生成的EFI如下:
我们需要修改EFI 的生成方式。在\Conf\tools_def.txt 可以看到默认的编译参数:
DEBUG_VS2015x86_X64_DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG
RELEASE_VS2015x86_X64_DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /IGNORE:4254 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /MERGE:.rdata=.data
NOOPT_VS2015x86_X64_DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG
这里我们使用的是DEBUG版,所以需要修改为下面这样(/ALIGN:0x1000)
DEBUG_VS2015x86_X64_DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:0x1000 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG
修改之后重新编译OVMF,得到的ResetSystemRuntimeDxe.efi 和前面手工修改的结果相同。但是这样修改会导致编译最后一步无法通过:
Generating FVMAIN_COMPACT FV
Generating PEIFV FV
##### ['GenFv', '-a', 'c:\\buildbs\\stable202108org\\Build\\OvmfX64\\DEBUG_VS2015x86\\FV\\Ffs\\PEIFV.inf', '-o', 'c:\\buildbs\\stable202108org\\Build\\OvmfX64\\DEBUG_VS2015x86\\FV\\PEIFV.Fv', '-i', 'c:\\buildbs\\stable202108org\\Build\\OvmfX64\\DEBUG_VS2015x86\\FV\\PEIFV.inf']
Return Value = 2
GenFv: ERROR 3000: Invalid
PE image Section-Alignment and File-Alignment do not match : c:\buildbs\stable202108org\Build\OvmfX64\DEBUG_VS2015x86\FV\Ffs\52C05B14-0B98-496c-BC3B-04B50211D680PeiCore\52C05B14-0B98-496c-BC3B-04B50211D680.ffs.
GenFv: ERROR 3000: Invalid
Could not rebase c:\buildbs\stable202108org\Build\OvmfX64\DEBUG_VS2015x86\FV\Ffs\52C05B14-0B98-496c-BC3B-04B50211D680PeiCore\52C05B14-0B98-496c-BC3B-04B50211D680.ffs.
build.py...
: error 7000: Failed to generate FV
build.py...
: error 7000: Failed to execute command
看起来是因为 PE头中 SectionAlignment和FileAlignment 不同导致的,在【参考1】介绍了一个设置 FileAlignment 的参数。同样的,将这个参数加入到 tools_def.txt 文件中:
DEBUG_VS2015x86_X64_DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:0x1000 /FILEALIGN:0x1000 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG
(注意 /FILEALIGN:0x1000 )
再次编译可以得到OVMF.FD ,使用QEMU 运行,Log 中就没有这个错误了。
这样的修改虽然能够解决问题,但是我们并不了解 InsertImageRecord() 函数的用途,后面有机会再继续进行分析。
参考:
1.https://blog.csdn.net/bagboy_taobao_com/article/details/7295575/
在QEMU 的Boot Manager/Menu 中可以看到”EFI Internal Shell” 这个选项,选中之后会跳转到其中执行 Shell.efi,这次就尝试编代码在其中插入能够启动到自己Application的选项。
首先我们研究一下这个选项是如何插入的。在\ovmfpkg\library\platformbootmanagerlib\BdsPlatform.c 的 PlatformBootManagerAfterConsole()函数中有:
//
// Perform some platform specific connect sequence
//
PlatformBdsConnectSequence ();
EfiBootManagerRefreshAllBootOption ();
//
// Register UEFI Shell
//
PlatformRegisterFvBootOption (
&gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE
);
RemoveStaleFvFileOptions ();
SetBootOrderFromQemu ();
其中gUefiShellFileGuid 的定义在 \ShellPkg\ShellPkg.dec
# FILE_GUID as defined in ShellPkg/Application/Shell/Shell.inf
gUefiShellFileGuid = {0x7c04a583, 0x9e3e, 0x4f1c, {0xad, 0x65, 0xe0, 0x52, 0x68, 0xd0, 0xb4, 0xd1}
也可以在\ShellPkg\Application\Shell\Shell.inf 中看到
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = Shell
FILE_GUID = 7C04A583-9E3E-4f1c-AD65-E05268D0B4D1 # gUefiShellFileGuid
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain
使用工具可以看到:
接下来我们着手设计一个自己的 Application 用于验证,需要特别注意的是,这个程序不能使用 SHELL 的任何Protocol和Service,因为调用它的时候没有 Shell。代码非常简单:
1. BootTest.c 如下
#include <Uefi.h>
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
for (int i=0;i<1000;i++) {
SystemTable->ConOut->OutputString(
SystemTable->ConOut,
L"www.lab-z.com\n");
}
return(0);
}
2. BootTest.inf如下:
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = BootTest
FILE_GUID = a912f198-7f0e-4803-b908-b757b806ec89
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 0.1
ENTRY_POINT = UefiMain
#
# VALID_ARCHITECTURES = IA32 X64 IPF
#
[Sources]
BootTest.c
[Packages]
MdePkg/MdePkg.dec
ShellPkg/ShellPkg.dec
[LibraryClasses]
UefiLib
UefiApplicationEntryPoint
DebugLib
EDK2 202108 已经没有 AppPkg , 所以我们直接放在 ShellPkg 中。接下来修改代码如下:
1.\OvmfPkg\OvmfPkgX64.dsc 中,插入 INF 文件
<PcdsFixedAtBuild>
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0xFF
gEfiShellPkgTokenSpaceGuid.PcdShellLibAutoInitialize|FALSE
gEfiMdePkgTokenSpaceGuid.PcdUefiLibMaxPrintBufferSize|8000
}
ShellPkg/Application/BootTest/BootTest.inf
!if $(SECURE_BOOT_ENABLE) == TRUE
SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
OvmfPkg/EnrollDefaultKeys/EnrollDefaultKeys.inf
!endif
2.\OvmfPkg\OvmfPkgX64.fdf 中插入
!if $(TOOL_CHAIN_TAG) != "XCODE5"
INF ShellPkg/DynamicCommand/TftpDynamicCommand/TftpDynamicCommand.inf
INF ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf
INF OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf
!endif
INF ShellPkg/Application/Shell/Shell.inf
INF ShellPkg/Application/BootTest/BootTest.inf
INF MdeModulePkg/Logo/LogoDxe.inf
#
# Network modules
#
!if $(E1000_ENABLE)
FILE DRIVER = 5D695E11-9B3F-4b83-B25F-4A8D5D69BE07 {
SECTION PE32 = Intel3.5/EFIX64/E3522X2.EFI
}
3. \OvmfPkg\Library\PlatformBootManagerLib\BdsPlatform.c 文件做插入 BootMenu的动作
定义一个 GUID,来自 BootTest.inf 中的FILE_GUID
EFI_GUID gBootTest = {
0xa912f198, 0x7f0e, 0x4803,
{ 0xb9, 0x08, 0xb7, 0x57, 0xb8, 0x06, 0xec, 0x89 } };
插入动作:
//
// Perform some platform specific connect sequence
//
PlatformBdsConnectSequence ();
EfiBootManagerRefreshAllBootOption ();
DEBUG((DEBUG_INFO, "LABZTest_Start\n"));
//
// Register a Test Application
//
PlatformRegisterFvBootOption(
&gBootTest, L"LABZTest", LOAD_OPTION_ACTIVE
);
DEBUG((DEBUG_INFO, "LABZTest_End\n"));
//
// Register UEFI Shell
//
PlatformRegisterFvBootOption (
&gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE
);
重新编译之后,可以用工具看到我们插入的 Application:
运行结果:
可以看到 BootMenu 中出现了我们设定的启动选项。
本文提到的用于测试的 BootTest:
最近在编译一个 ESP32 Arduino代码的时候碰到了如下很奇怪的问题:
In file included from c:\users\DaveX\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\gcc8_4_0-esp-2021r2-patch3\xtensa-esp32-elf\include\c++\8.4.0\bits\locale_facets_nonio.h:39,
from c:\users\DaveX\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\gcc8_4_0-esp-2021r2-patch3\xtensa-esp32-elf\include\c++\8.4.0\locale:41,
from c:\users\DaveX\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\gcc8_4_0-esp-2021r2-patch3\xtensa-esp32-elf\include\c++\8.4.0\iomanip:43,
from C:\Users\DaveX\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.4\libraries\BluetoothSerial\src\BTAddress.cpp:15:
c:\users\DaveX\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\gcc8_4_0-esp-2021r2-patch3\xtensa-esp32-elf\include\c++\8.4.0\ctime:64:11: error: '::clock' has not been declared
using ::clock;
^~~~~
c:\users\DaveX\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\gcc8_4_0-esp-2021r2-patch3\xtensa-esp32-elf\include\c++\8.4.0\ctime:65:11: error: '::difftime' has not been declared
using ::difftime;
^~~~~~~~
c:\users\DaveX\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\gcc8_4_0-esp-2021r2-patch3\xtensa-esp32-elf\include\c++\8.4.0\ctime:66:11: error: '::mktime' has not been declared
using ::mktime;
^~~~~~
c:\users\DaveX\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\gcc8_4_0-esp-2021r2-patch3\xtensa-esp32-elf\include\c++\8.4.0\ctime:67:11: error: '::time' has not been declared
using ::time;
^~~~
c:\users\DaveX\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\gcc8_4_0-esp-2021r2-patch3\xtensa-esp32-elf\include\c++\8.4.0\ctime:68:11: error: '::asctime' has not been declared
using ::asctime;
^~~~~~~
c:\users\DaveX\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\gcc8_4_0-esp-2021r2-patch3\xtensa-esp32-elf\include\c++\8.4.0\ctime:69:11: error: '::ctime' has not been declared
using ::ctime;
^~~~~
c:\users\DaveX\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\gcc8_4_0-esp-2021r2-patch3\xtensa-esp32-elf\include\c++\8.4.0\ctime:70:11: error: '::gmtime' has not been declared
using ::gmtime;
^~~~~~
c:\users\DaveX\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\gcc8_4_0-esp-2021r2-patch3\xtensa-esp32-elf\include\c++\8.4.0\ctime:71:11: error: '::localtime' has not been declared
using ::localtime;
^~~~~~~~~
c:\users\DaveX\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\gcc8_4_0-esp-2021r2-patch3\xtensa-esp32-elf\include\c++\8.4.0\ctime:72:11: error: '::strftime' has not been declared
using ::strftime;
^~~~~~~~
经过搜索,有人碰到了类似的问题,产生问题的原因是:使用的代码中有 time.h 这个名称的文件。我的代码中没有,但是使用到的 DFRobot_OLED12864 这个库里面有。解决方法很简单:把这个库中的time.h 改个名称即可。
希望能帮助到碰到同样问题的朋友。
分享两个用于测试的 Windows 11 ,一个是 22616,另外一个是25131。注意:这个是 Insider 版本只建议用于测试不要用于自己使用。
22616.1.220502-1800.NI_RELEASE_CLIENTMULTICOMBINED_UUP_X64FRE_NETFX_EN-US_FIX
链接: https://pan.baidu.com/s/1NstIoGTvPtZrLSi-wIztkA?pwd=f92y 提取码: f92y
25131.1000.220527-1351.RS_PRERELEASE_CLIENTMULTICOMBINED_UUP_X64FRE_NETFX_EN-US_FIX
链接: https://pan.baidu.com/s/1XinTLV3Hs3qvsxWpiDqLaw?pwd=r71h 提取码: r71h
接下来的代码在 \mdemodulepkg\core\dxe\dxemain\DxeMain.c。我们依旧是按照 Debug Log 的顺序研究。
//
// Initialize Memory Services
//
CoreInitializeMemoryServices (&HobStart, &MemoryBaseAddress, &MemoryLength);
这个函数位于\mdemodulepkg\core\dxe\gcd\Gcd.c,初始化内存服务。
/**
External function. Initializes memory services based on the memory
descriptor HOBs. This function is responsible for priming the memory
map, so memory allocations and resource allocations can be made.
The first part of this function can not depend on any memory services
until at least one memory descriptor is provided to the memory services.
@param HobStart The start address of the HOB.
@param MemoryBaseAddress Start address of memory region found to init DXE
core.
@param MemoryLength Length of memory region found to init DXE core.
@retval EFI_SUCCESS Memory services successfully initialized.
**/
EFI_STATUS
CoreInitializeMemoryServices (
IN VOID **HobStart,
OUT EFI_PHYSICAL_ADDRESS *MemoryBaseAddress,
OUT UINT64 *MemoryLength
)
CoreInitializeMemoryServices:
BaseAddress - 0x3F59000 Length - 0x3CA7000
MinimalMemorySizeNeeded - 0x320000
(这里看起来有一个能够运行的最小内存的要求,具体怎么来的还不清楚)
接下来回到 DxeMain 中继续运行,接下来开始创建最重要的2个Table:System_Table 和 Runtime_Services
//
// Allocate the EFI System Table and EFI Runtime Service Table from EfiRuntimeServicesData
// Use the templates to initialize the contents of the EFI System Table and EFI Runtime Services Table
//
gDxeCoreST = AllocateRuntimeCopyPool (sizeof (EFI_SYSTEM_TABLE), &mEfiSystemTableTemplate);
ASSERT (gDxeCoreST != NULL);
gDxeCoreRT = AllocateRuntimeCopyPool (sizeof (EFI_RUNTIME_SERVICES), &mEfiRuntimeServicesTableTemplate);
ASSERT (gDxeCoreRT != NULL);
gDxeCoreST->RuntimeServices = gDxeCoreRT;
InstallProtocolInterface: 5B1B31A1-9562-11D2-8E3F-00A0C969723B 7EC57F8
## Include/Protocol/LoadedImage.h
gEfiLoadedImageProtocolGuid = { 0x5B1B31A1, 0x9562, 0x11D2, { 0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}
//
// Start the Image Services.
//
Status = CoreInitializeImageServices (HobStart);
ASSERT_EFI_ERROR (Status);
\mdemodulepkg\core\dxe\image\Image.c
/**
Add the Image Services to EFI Boot Services Table and install the protocol
interfaces for this image.
@param HobStart The HOB to initialize
@return Status code.
**/
EFI_STATUS
CoreInitializeImageServices (
IN VOID *HobStart
)
其中:
//
// Install the protocol interfaces for this image
//
Status = CoreInstallProtocolInterface (
&Image->Handle,
&gEfiLoadedImageProtocolGuid,
EFI_NATIVE_INTERFACE,
&Image->Info
);
ASSERT_EFI_ERROR (Status);
ProtectUefiImage (&Image->Info, Image->LoadedImageDevicePath);
在\mdemodulepkg\core\dxe\misc\MemoryProtection.c 文件中:
ProtectUefiImageCommon - 0x7EC57F8
- 0x0000000007EA3000 - 0x0000000000027000
(不清楚这个函数的左右,看介绍好像是用于加载 PE 格式,然后设置对应的代码为”Execute Only”)。
接下来返回 DxeMain 中继续:
//
// Log MemoryBaseAddress and MemoryLength again (from
// CoreInitializeMemoryServices()), now that library constructors have
// executed.
//
DEBUG ((DEBUG_INFO, "%a: MemoryBaseAddress=0x%Lx MemoryLength=0x%Lx\n",
__FUNCTION__, MemoryBaseAddress, MemoryLength));
DxeMain: MemoryBaseAddress=0x3F59000 MemoryLength=0x3CA7000
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "HOBLIST address in DXE = 0x%p\n", HobStart));
HOBLIST address in DXE = 0x78EA018
\MdePkg\MdePkg.dec
## Include/Protocol/Decompress.h
gEfiDecompressProtocolGuid = { 0xD8117CFE, 0x94A6, 0x11D4, { 0x9A, 0x3A, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D }}
## Include/Protocol/FirmwareVolumeBlock.h
gEfiFirmwareVolumeBlockProtocolGuid = { 0x8f644fa9, 0xe850, 0x4db1, {0x9c, 0xe2, 0xb, 0x44, 0x69, 0x8e, 0x8d, 0xa4 } }
## Include/Protocol/DevicePath.h
gEfiDevicePathProtocolGuid = { 0x09576E91, 0x6D3F, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}
## Include/Protocol/FirmwareVolume2.h
gEfiFirmwareVolume2ProtocolGuid = { 0x220e73b6, 0x6bdb, 0x4413, { 0x84, 0x5, 0xb9, 0x74, 0xb1, 0x8, 0x61, 0x9a } }
比较特别的是:
InstallProtocolInterface: D8117CFE-94A6-11D4-9A3A-0090273FC14D 7EC5080
InstallProtocolInterface: 8F644FA9-E850-4DB1-9CE2-0B44698E8DA4 78E6CB0
InstallProtocolInterface: 09576E91-6D3F-11D2-8E39-00A0C969723B 78E6D98
InstallProtocolInterface: 220E73B6-6BDB-4413-8405-B974B108619A 78E6630
InstallProtocolInterface: EE4E5898-3914-4259-9D6E-DC7BD79403CF 7EC5D10
最后一个定义在\OvmfPkg\OvmfPkgX64.fdf:
FILE FV_IMAGE = 9E21FD93-9C72-4c15-8C4B-E77F1DB2D792 {
SECTION GUIDED EE4E5898-3914-4259-9D6E-DC7BD79403CF PROCESSING_REQUIRED = TRUE {
#
# These firmware volumes will have files placed in them uncompressed,
# and then both firmware volumes will be compressed in a single
# compression operation in order to achieve better overall compression.
#
SECTION FV_IMAGE = PEIFV
SECTION FV_IMAGE = DXEFV
}
}
代码在\mdemodulepkg\core\dxe\sectionextraction\CoreSectionExtraction.c
/**
Entry point of the section extraction code. Initializes an instance of the
section extraction interface and installs it on a new handle.
@param ImageHandle A handle for the image that is initializing this driver
@param SystemTable A pointer to the EFI system table
@retval EFI_SUCCESS Driver initialized successfully
@retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
**/
EFI_STATUS
EFIAPI
InitializeSectionExtraction (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
(感觉是将 PEI 和 DXE打包在一起)