DellaOS引导程序篇

本文参照《UEFI原理与编程》和《一个UEFI引导程序的实现》实现(部分内容有删改),如有描述不清或错误处,请阅读原著或联系本人
2021-11-11

1、环境配置

这里默认已安装centos7(带图形界面,注意不要选择中文)

1.1、centos7没网

cd /etc/sysconfig/network-scripts/
vim ifcfg-ens33
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=dhcp
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=ens33
UUID=86312406-5cb4-4200-9903-2b7745ca4e97
DEVICE=ens33
ONBOOT=yes
systemctl restart network

1.2、更新yum

sudo yum install wget -y
sudo wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.tencent.com/repo/centos7_base.repo
sudo yum clean all
sudo yum makecache

安装依赖

sudo yum install gtk2 gtk2-devel libXt libXt-devel libXpm libXpm-devel SDL SDL-devel xorg-x11-server-devel nasm gcc-c++ glibc-headers libX11-devel libXrandr-devel lrzsz -y

1.3、zip

yum install -y unzip zip

1.4、安装git

yum -y install git

1.5、需要使用下外网编译(clash,需要在windows上安装过)

C:\Users\xumeng03\.config
上传到
/root/.config
订阅
wget https://mymonocloud.com/clash/457240/5K9yNAE6yMnc
mv 5K9yNAE6yMnc config.yaml
gunzip clash-linux-amd64-v1.8.0.gz

修改代理如下图

启动

./clash-linux-amd64-v1.8.0

1.6、edk2-UDK2018

安装依赖

yum install build-essential git uuid-dev iasl nasm libuuid-devel

拉取分支

#git clone https://github.com/tianocore/edk2.git
git clone git://github.com/tianocore/edk2.git

切换分支

cd edk2/
git checkout UDK2018

编译

make -C BaseTools/
BaseTools/BuildEnv
. ./edksetup.sh

修改配置文件

vim Conf/target.txt

target.txt

# ACTIVE_PLATFORM       = Nt32Pkg/Nt32Pkg.dsc
ACTIVE_PLATFORM = OvmfPkg/OvmfPkgX64.dscTARGET                = DEBUG# TARGET_ARCH           = IA32
TARGET_ARCH = X64TOOL_CHAIN_CONF       = Conf/tools_def.txt# TOOL_CHAIN_TAG        = MYTOOLS
TOOL_CHAIN_TAG = GCC48BUILD_RULE_CONF = Conf/build_rule.txt
git submodule update --init --recursive
build

2、编译执行UEFI Shell

2.1、U盘分区处理



[root@localhost ~]# fdisk -l /dev/sdbDisk /dev/sdb: 31.0 GB, 31037849600 bytes, 60620800 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x05353b8cDevice Boot      Start         End      Blocks   Id  System
/dev/sdb1               1  4294967295  2147483647+  ee  GPT
[root@localhost ~]# ls /dev/sdb*
/dev/sdb  /dev/sdb1  /dev/sdb2

2.2、拷贝UEFI应用程序

[root@localhost ~]# mount /dev/sdb1 /mnt/
[root@localhost ~]# mkdir -p /mnt/EFI/BOOT
[root@localhost ~]# cp /root/Desktop/dellaOS/edk2/Build/OvmfX64/DEBUG_GCC48/X64/Shell.efi /mnt/EFI/BOOT/BOOTx64.EFI
[root@localhost ~]# sync
[root@localhost ~]# umount /mnt/

2.3、新建虚拟机



2.4、配置UEFI固件

命令 描述
cd 显示或修改当前目录
cls 清除终端输出信息或改变前景/背景颜色
cp 复制一个或多个文件或目录到目标路径
date 显示或设置当前系统日期
del rm命令的别名
devices 显示UEFI驱动的设备管理列表
devtree 显示UEFI驱动的设备管理树
dh 显示UEFI环境的设备句柄
dir ls命令的别名
dmem 显示系统、IO寄存器、PCI/PCIe配置空间或设备内存空间信息
dmpstore 管理所有UEFI环境变量
drivers 显示UEFI环境里的UEFI驱动列表信息
echo 显示脚本中的打印信息
edit 文件的全屏编辑器
exit 退出UEFI Shell或当前脚本
help 显示UEFI Shell的内建命令列表
mem dmem命令的别名
memmap 显示UEFI环境维护的内存映射
mkdir 创建一个或多个新的目录
mm 显示或修改内存/MMIO/IO/PCI/PCIe地址空间
mode 显示或修改终端设备的显示模式
mv 移动一个或多个文件到目标路径
pci 显示PCI设备列表或PCI/PCIe配置空间
reset 重启系统
rm 删除一个或多个文件或目录
setsize 调整文件大小
time 显示或设置当前系统时间
type 发送文件的内容到标准输出设备
ver 显示UEFI Shell和固件的版本信息
vol 显示文件系统的容量信息

3、UEFI下的 ”Hello,world!“

3.1、helloworld.c

#include <Uefi.h>EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable)
{SystemTable->ConOut->OutputString(SystemTable->ConOut,L"Hello World\n");return EFI_SUCCESS;
}
  • 所有的UEFI程序都要包含头文件Uefi.h,Uefi.h定义了UEFI基本数据类型及核心数据结构
  • UEFI标准应用程序的入口函数为是UefiMain,入口函数名由工程文件UefiMain.inf指定,入口函数的函数名可以变化但函数签名(即返回值类型和参数列表类型)不能变化
  • 大部分UEFI程序的返回值都为EFI_STATUS,本质为无符号长整数
  • EFI_SUCCESS为预定义常量,值为0
  • ImageHandle和SystemTable为参数,ImageHandle表示模块自身加载到内存后生成的Image对象,SystemTable是程序与UEFI内核交互的桥梁,是UEFI内核中一个全局的结构体
  • 字符串“Hello World”前面的大写字母L 用于通知编译器字符串必须按照宽字符保存
  • 向标准输出设备打印字符串是通过SystemTable的ConOut提供的服务完成的。ConOut是EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL的一个实例。而EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL的主要功能是控制字符输出设备。向输出设备打印字符串是通过ConOut 提供的OutputString服务完成的。该服务(函数)的第一个参数是This指针,指向EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL实例(此处为ConOut)本身;第二个参数是Unicode字符串。关于Protocol和This指针将在第4章详细介绍。简而言之,这条打印语句的意义就是通过SystemTable →ConOut →OutputString服务将字符串L“HelloWorld”打印到SystemTable →ConOut所控制的字符输出设备

想要编译UEFI应用程序源代码,必须在源文件所在目录下创建一个后缀名为.inf的工程信息文件

3.2、helloworld.inf

[Defines]INF_VERSION                    = 0x00010005BASE_NAME                      = helloworldFILE_GUID                      = 6987936E-f301-4a63-9661-fc6030dcc899MODULE_TYPE                    = UEFI_APPLICATIONVERSION_STRING                 = 1.0ENTRY_POINT                    = UefiMain[Sources]helloworld.c[Packages]MdePkg/MdePkg.dec[LibraryClasses]UefiApplicationEntryPoint[Protocols][Guids]

3.2.1、[Defines]块

语法格式

属性名 = 属性值

必选属性

变量名 功能描述
INF_VERSION INF标准的版本号,通常将INF_VERSION设置为0x00010005即可
BASE_NAME UEFI程序的名字,不能包含空格
FILE_GUID 所有EDK2的INF文件都必须包含GUID值(格式:8-4-4-4-12)
MODULE_TYPE 程序类型(详情请查阅《UEFI原理与编程 戴正华(著)》)
VERSION_STRING 模块的版本号字符串
ENTRY_POINT 模块的入口函数

3.2.2、[Sources]块

语法格式

块内每一行表示一个文件,文件使用相对路径,根路径是工程文件所在的目录

示例1

[Sources]UefiMain.c

示例2

[Sources]UefiMain.c
[Sources .IA32]Cpu32.c
[Sources .x64]Cpu64.c

3.2.3、[Packages]块

语法格式

块内每一行列出一个文件,文件使用相对路径,若[Sources]块内列出了源文件,则在[Packages]块必须列出MdePkg/MdePkg.dec,并将其放在本块的首行

示例

[Packages]MdePkg/MdePkg.dec

3.2.4、[LibraryClasses]块

语法格式

块内每一行声明一个要链接的库
应用程序工程模块必须链接UefiApplicationEntryPoint库;驱动模块必须链接UefiDriverEntryPoint库

示例

[LibraryClasses]UefiApplicationEntryPoint

3.2.5、[Protocols]块

语法格式

块内每一行声明模块中使用的一个Protocol,严格来说,列出的是Protocol对应的GUID,如果模块未使用任何Protocol,则此块为空

3.2.6、[Guids]块

语法格式

表示全局GUID名字

3.3、重新编译

. ./edksetup.sh

3.4、项目文件放置

在EDK2目录的“AppPkg\Applications”目录下创建名为“helloworld”的目录,并将工程1-1中的helloworld.inf和helloworld.c文件拷贝到helloworld目录里

3.5、AppPkg.dsc

[Defines]PLATFORM_NAME                  = AppPkgPLATFORM_GUID                  = 0458dade-8b6e-4e45-b773-1b27cbda3e06PLATFORM_VERSION               = 0.01DSC_SPECIFICATION              = 0x00010006OUTPUT_DIRECTORY               = Build/AppPkgSUPPORTED_ARCHITECTURES        = IA32|X64|ARM|AARCH64BUILD_TARGETS                  = DEBUG|RELEASE|NOOPTSKUID_IDENTIFIER               = DEFAULT# Debug output controlDEFINE DEBUG_ENABLE_OUTPUT      = FALSE       # Set to TRUE to enable debug outputDEFINE DEBUG_PRINT_ERROR_LEVEL  = 0x80000040  # Flags to control amount of debug outputDEFINE DEBUG_PROPERTY_MASK      = 0[PcdsFeatureFlag][PcdsFixedAtBuild]gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|$(DEBUG_PROPERTY_MASK)gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|$(DEBUG_PRINT_ERROR_LEVEL)[PcdsFixedAtBuild.IPF][LibraryClasses]# Entry Point LibrariesUefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.infShellCEntryLib|ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.infUefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf# Common LibrariesBaseLib|MdePkg/Library/BaseLib/BaseLib.infBaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.infUefiLib|MdePkg/Library/UefiLib/UefiLib.infPrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.infPcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.infMemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.infUefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.infUefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf!if $(DEBUG_ENABLE_OUTPUT)DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.infDebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf!else   ## DEBUG_ENABLE_OUTPUTDebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf!endif  ## DEBUG_ENABLE_OUTPUTDevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.infPeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.infIoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.infPciLib|MdePkg/Library/BasePciLibCf8/BasePciLibCf8.infPciCf8Lib|MdePkg/Library/BasePciCf8Lib/BasePciCf8Lib.infSynchronizationLib|MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.infUefiRuntimeLib|MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.infHiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.infUefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.infPerformanceLib|MdeModulePkg/Library/DxePerformanceLib/DxePerformanceLib.infHobLib|MdePkg/Library/DxeHobLib/DxeHobLib.infFileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.infSortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.infShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.infCacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf[Components]#### Sample Applications.AppPkg/Applications/helloworld/helloworld.infAppPkg/Applications/Hello/Hello.inf        # No LibC includes or functions.AppPkg/Applications/Main/Main.inf          # Simple invocation. No other LibC functions.AppPkg/Applications/Enquire/Enquire.inf    #AppPkg/Applications/ArithChk/ArithChk.inf  #AppPkg/Applications/OrderedCollectionTest/OrderedCollectionTest.inf {<LibraryClasses>OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrderedCollectionRedBlackTreeLib.infDebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.infDebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf<PcdsFeatureFlag>gEfiMdePkgTokenSpaceGuid.PcdValidateOrderedCollection|TRUE<PcdsFixedAtBuild>gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2FgEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80400040}!include StdLib/StdLib.inc
!include AppPkg/Applications/Sockets/Sockets.inc

3.6、target.txt

# ACTIVE_PLATFORM       = Nt32Pkg/Nt32Pkg.dsc
# ACTIVE_PLATFORM = OvmfPkg/OvmfPkgX64.dsc
ACTIVE_PLATFORM       = AppPkg/AppPkg.dscTARGET                = DEBUG# TARGET_ARCH           = IA32
TARGET_ARCH = X64TOOL_CHAIN_CONF       = Conf/tools_def.txt# TOOL_CHAIN_TAG        = MYTOOLS
TOOL_CHAIN_TAG = GCC48BUILD_RULE_CONF = Conf/build_rule.txt

3.7、编译、拷贝程序

[root@localhost edk2]# build
[root@localhost ~]# mount /dev/sdb1 /mnt/
[root@localhost ~]# cp /root/Desktop/dellaOS/edk2/Build/AppPkg/DEBUG_GCC48/X64/helloworld.efi /mnt/
[root@localhost ~]# sync
[root@localhost ~]# umount /mnt/

3.8、运行效果

4、UEFI引导程序

4.1、核心任务

  • 将操作系统内核从存储介质加载到物理内存的指定地址
  • 获取硬件平台的物理地址空间信息
  • 配置图形设备的显示模式
  • 设置处理器的运行环境

5、配置图形设备的显示模式

5.1、图形设备显示协议(EFI_GRAPHICS_OUTPUT_PROTOCOL)

struct_EFI_GRAPHICS_OUTPUT_PROTOCOL{EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE QueryMode;   //查询显示模式EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE SetMode;      //设置显示模式EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT Blt;               //用于将图像传输到屏幕或从屏幕读取图像EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode;            //指向当前显示模式
}
结构体成员 功能描述
QueryMode 查询显示模式
SetMode 设置显示模式
Blt 用于将图像传输到屏幕或从屏幕读取图像
Mode 指向当前显示模式

QueryMode

typedef EFT_STATUS (EFIAPI *EFI_GRAPHICS_ouTPUT_PROToCOL_QUERY_MODE)(INEFI_GRAPHICS_OUTPUT_PROTOCOL *This,                //INEFI_GRAPHICS_OUTPUT_PROTOCOLIN UINT32 ModeNumber,                               //显示器模式编号OUT UINTN *SizeofInfo,                             //Info指向的内存大小OUT EFI_GRAPHICS_oUTPUT_MODE_INFORMATION **Info ); //模式信息

QueryMode(…)用于获得模式ModeNumber 的模式信息。模式信息由Info返回,Info指向的内存由QueryMode负责分配,大小由SizeOfInfo返回。Info的大小之所以由SizeOfInfo决定,是因为EFI_GRAPHICS_OUTPUT_MODE_INFORMATION在不同的UEFI版本中的格式会有所不同,不能由sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)确定大小。调用者负责释放Info指向的内存

5.2、显示模式(EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE)

typedef struct {UINT32 MaxMode;                                      //显示设备支持的模式数量UINT32 Mode;                                       //当前显示模式EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;         //当前显示模式下的模式信息UINTN SizeOfInfo;                                 //Info数据结构大小EFI_PHYSICAL_ADDRESS FrameBufferBase;               //帧缓冲区物理地址大小UINTN FrameBuffersize;                              //帧缓冲区大小
}EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE;
结构体成员 功能描述
UINT32 MaxMode 图形设备支持的图形模式数量
UINT32 Mode 图形设备当前使用的图形模式,有效数值为0~ MaxMode-1
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info 图形模式信息结构(只读)
UINTN SizeOfInfo 图形模式信息结构的长度
EFI_PHYSICAL_ADDRESS FrameBufferBase 图形设备线性帧缓冲区的起始地址
UINTN FrameBufferSize 图形设备线性帧缓冲区的长度

5.3、显示模式信息(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)

typedef struct {UINT32 version;                                      //版本号UINT32 HorizontalResolution;                       //垂直分辨率UINT32 verticalResolution;                           //水平分辨率EFI_GRAPHICS_PIXEL_FORMAT PixelFormat;               //像素格式EFI_PIXEL_BITMASK PixelInformation                    //仅在PixelFormat设置为PixelBitMask格式时有效UINT32 PixelsPerscanLine;                            //每扫描行的像素数
}EFI_GRAPHICS_OUTPUT_MODE_INFORMATION;
结构体成员 功能描述
UINT32 Version 此结构的版本号
UINT32 HorizontalResolution 屏幕像素X坐标轴的长度
UINT32 VerticalResolution 屏幕像素Y坐标轴的长度
EFI_GRAPHICS_PIXEL_FORMAT PixelFormat 像素的物理格式定义
EFI_PIXEL_BITMASK PixelInformation 仅在PixelFormat设置为PixelBitMask格式时有效
UINT32 PixelsPerScanLine 定义扫描线的像素数。

5.4、显示模式信息的像素(EFI_GRAPHICS_PIXEL_FORMAT)

typedef enum {PixelRedGreenBlueReserved8BitPerColor,             //RGBPixelBlueGreenRedReserved8BitPerColor,             //BGRPixelBitMask,                                      //像素掩码PixelBltonly,                                     //只能通过Blt函数访问缓冲区PixelFormatMax
}EFI_GRAPHICS_PIXEL_FORMAT;
枚举成员 功能描述
PixelRedGreenBlueReserved8BitPerColor 一个像素占32位,字节0表示红色,字节1表示绿色,字节2表示蓝色,字节3保留
PixelBlueGreenRedReserved8BitPerColor 一个像素占32位,字节0表示蓝色,字节1表示绿色,字节2表示红色,字节3保留
PixelBitMask 物理帧缓冲区的像素由EFI_PIXEL_BITMASK结构定义
PixelBltOnly 此模式不支持物理帧缓冲区

5.5、EFI_PIXEL_BITMASK

typedef struct {UINT32 RedMask;UINT32 GreenMask;UINT32 BlueMask;UINT32 ReservedMask;
}EFI_PIXEL_BITMASK;
//位掩码中的红色、绿色和蓝色分量值代表颜色深度,每个颜色深度随着其颜色位掩码数值的增加而增加,复位颜色位掩码中的所有位表示最小颜色深度,置位颜色位掩码中的所有位表示最大颜色深度。

5.6、获取显示模式

video.c

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable)
{EFI_GRAPHICS_OUTPUT_PROTOCOL* gGraphicsOutput = 0;EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* Info = 0;UINTN InfoSize = 0;int i = 0;gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid,NULL,(VOID **)&gGraphicsOutput);//EFI_GRAPHICS_OUTPUT_PROTOCOL->Mode为当前显示模式Print(L"Current Mode:%02d,Version:%x,Format:%d,Horizontal:%d,Vertical:%d,ScanLine:%d,FrameBufferBase:%010lx,FrameBufferSize:%010lx\n",gGraphicsOutput->Mode->Mode,gGraphicsOutput->Mode->Info->Version,gGraphicsOutput->Mode->Info->PixelFormat,gGraphicsOutput->Mode->Info->HorizontalResolution,gGraphicsOutput->Mode->Info->VerticalResolution,gGraphicsOutput->Mode->Info->PixelsPerScanLine,gGraphicsOutput->Mode->FrameBufferBase,gGraphicsOutput->Mode->FrameBufferSize);for(i = 0;i < gGraphicsOutput->Mode->MaxMode;i++){gGraphicsOutput->QueryMode(gGraphicsOutput,i,&InfoSize,&Info);Print(L"Mode:%02d,Version:%x,Format:%d,Horizontal:%d,Vertical:%d,ScanLine:%d\n",i,Info->Version,Info->PixelFormat,Info->HorizontalResolution,Info->VerticalResolution,Info->PixelsPerScanLine);gBS->FreePool(Info);}gBS->CloseProtocol(gGraphicsOutput,&gEfiGraphicsOutputProtocolGuid,ImageHandle,NULL);return EFI_SUCCESS;
}

video.inf

[Defines]INF_VERSION                    = 0x00010005BASE_NAME                      = videoFILE_GUID                      = 6987936E-f301-4a63-9661-fc6030dcc833MODULE_TYPE                    = UEFI_APPLICATIONVERSION_STRING                 = 1.0ENTRY_POINT                    = UefiMain[Sources]video.c[Packages]MdePkg/MdePkg.dec[LibraryClasses]UefiApplicationEntryPointUefiLib[Protocols]gEfiGraphicsOutputProtocolGuid[Guids]

5.7、编译

. ./edksetup.sh

5.8、AppPkg.dsc

[Defines]PLATFORM_NAME                  = AppPkgPLATFORM_GUID                  = 0458dade-8b6e-4e45-b773-1b27cbda3e06PLATFORM_VERSION               = 0.01DSC_SPECIFICATION              = 0x00010006OUTPUT_DIRECTORY               = Build/AppPkgSUPPORTED_ARCHITECTURES        = IA32|X64|ARM|AARCH64BUILD_TARGETS                  = DEBUG|RELEASE|NOOPTSKUID_IDENTIFIER               = DEFAULT# Debug output controlDEFINE DEBUG_ENABLE_OUTPUT      = FALSE       # Set to TRUE to enable debug outputDEFINE DEBUG_PRINT_ERROR_LEVEL  = 0x80000040  # Flags to control amount of debug outputDEFINE DEBUG_PROPERTY_MASK      = 0[PcdsFeatureFlag][PcdsFixedAtBuild]gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|$(DEBUG_PROPERTY_MASK)gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|$(DEBUG_PRINT_ERROR_LEVEL)[PcdsFixedAtBuild.IPF][LibraryClasses]# Entry Point LibrariesUefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.infShellCEntryLib|ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.infUefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf# Common LibrariesBaseLib|MdePkg/Library/BaseLib/BaseLib.infBaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.infUefiLib|MdePkg/Library/UefiLib/UefiLib.infPrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.infPcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.infMemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.infUefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.infUefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf!if $(DEBUG_ENABLE_OUTPUT)DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.infDebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf!else   ## DEBUG_ENABLE_OUTPUTDebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf!endif  ## DEBUG_ENABLE_OUTPUTDevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.infPeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.infIoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.infPciLib|MdePkg/Library/BasePciLibCf8/BasePciLibCf8.infPciCf8Lib|MdePkg/Library/BasePciCf8Lib/BasePciCf8Lib.infSynchronizationLib|MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.infUefiRuntimeLib|MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.infHiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.infUefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.infPerformanceLib|MdeModulePkg/Library/DxePerformanceLib/DxePerformanceLib.infHobLib|MdePkg/Library/DxeHobLib/DxeHobLib.infFileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.infSortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.infShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.infCacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf[Components]#### Sample Applications.AppPkg/Applications/helloworld/helloworld.infAppPkg/Applications/video/video.infAppPkg/Applications/Hello/Hello.inf        # No LibC includes or functions.AppPkg/Applications/Main/Main.inf          # Simple invocation. No other LibC functions.AppPkg/Applications/Enquire/Enquire.inf    #AppPkg/Applications/ArithChk/ArithChk.inf  #AppPkg/Applications/OrderedCollectionTest/OrderedCollectionTest.inf {<LibraryClasses>OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrderedCollectionRedBlackTreeLib.infDebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.infDebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf<PcdsFeatureFlag>gEfiMdePkgTokenSpaceGuid.PcdValidateOrderedCollection|TRUE<PcdsFixedAtBuild>gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2FgEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80400040}!include StdLib/StdLib.inc
!include AppPkg/Applications/Sockets/Sockets.inc

5.9、target.txt

# ACTIVE_PLATFORM       = Nt32Pkg/Nt32Pkg.dsc
# ACTIVE_PLATFORM = OvmfPkg/OvmfPkgX64.dsc
ACTIVE_PLATFORM       = AppPkg/AppPkg.dscTARGET                = DEBUG# TARGET_ARCH           = IA32
TARGET_ARCH = X64TOOL_CHAIN_CONF       = Conf/tools_def.txt# TOOL_CHAIN_TAG        = MYTOOLS
TOOL_CHAIN_TAG = GCC48BUILD_RULE_CONF = Conf/build_rule.txt

5.10、编译、拷贝程序

[root@localhost edk2]# build
[root@localhost ~]# mount /dev/sdb1 /mnt/
[root@localhost ~]# cp /root/Desktop/dellaOS/edk2/Build/AppPkg/DEBUG_GCC48/X64/helloworld.efi /mnt/
[root@localhost ~]# sync
[root@localhost ~]# umount /mnt/

5.11、运行效果

5.12、设置显示模式

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable)
{EFI_GRAPHICS_OUTPUT_PROTOCOL* gGraphicsOutput = 0;EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* Info = 0;UINTN InfoSize = 0;int i = 0;gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid,NULL,(VOID **)&gGraphicsOutput);Print(L"Current Mode:%02d,Version:%x,Format:%d,Horizontal:%d,Vertical:%d,ScanLine:%d,FrameBufferBase:%010lx,FrameBufferSize:%010lx\n",gGraphicsOutput->Mode->Mode,gGraphicsOutput->Mode->Info->Version,gGraphicsOutput->Mode->Info->PixelFormat,gGraphicsOutput->Mode->Info->HorizontalResolution,gGraphicsOutput->Mode->Info->VerticalResolution,gGraphicsOutput->Mode->Info->PixelsPerScanLine,gGraphicsOutput->Mode->FrameBufferBase,gGraphicsOutput->Mode->FrameBufferSize);long H_V_Resolution = gGraphicsOutput->Mode->Info->HorizontalResolution * gGraphicsOutput->Mode->Info->VerticalResolution;int MaxResolutionMode = gGraphicsOutput->Mode->Mode;for(i = 0;i < gGraphicsOutput->Mode->MaxMode;i++){gGraphicsOutput->QueryMode(gGraphicsOutput,i,&InfoSize,&Info);Print(L"Mode:%02d,Version:%x,Format:%d,Horizontal:%d,Vertical:%d,ScanLine:%d\n",i,Info->Version,Info->PixelFormat,Info->HorizontalResolution,Info->VerticalResolution,Info->PixelsPerScanLine);if((Info->PixelFormat == 1) && (Info->HorizontalResolution * Info->VerticalResolution > H_V_Resolution)){H_V_Resolution = Info->HorizontalResolution * Info->VerticalResolution;MaxResolutionMode = i;}gBS->FreePool(Info);}gGraphicsOutput->SetMode(gGraphicsOutput,MaxResolutionMode);gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid,NULL,(VOID **)&gGraphicsOutput);Print(L"Current Mode:%02d,Version:%x,Format:%d,Horizontal:%d,Vertical:%d,ScanLine:%d,FrameBufferBase:%010lx,FrameBufferSize:%010lx\n",gGraphicsOutput->Mode->Mode,gGraphicsOutput->Mode->Info->Version,gGraphicsOutput->Mode->Info->PixelFormat,gGraphicsOutput->Mode->Info->HorizontalResolution,gGraphicsOutput->Mode->Info->VerticalResolution,gGraphicsOutput->Mode->Info->PixelsPerScanLine,gGraphicsOutput->Mode->FrameBufferBase,gGraphicsOutput->Mode->FrameBufferSize);gBS->CloseProtocol(gGraphicsOutput,&gEfiGraphicsOutputProtocolGuid,ImageHandle,NULL);return EFI_SUCCESS;
}

SetMode

EFI_STATUS SetMode
(IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,IN UINT32 ModeNumber
);

5.13、运行效果

6、获取硬件平台的物理地址空间信息

6.1、GetMemoryMap

EFI_STATUS GetMemoryMap
(IN OUT UINTN *MemoryMapSize,                               //内存描述符结构数组存储空间的长度IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,                  //内存描述符结构数组存储空间OUT UINTN *MapKey,                                       //当前内存映射键值OUT UINTN *DescriptorSize,                                //内存描述符结构的长度OUT UINT32 *DescriptorVersion                               //内存描述符结构的版本号
);

EFI_MEMORY_DESCRIPTOR

结构体成员 成员类型 功能描述
Type UINT32 内存类型
PhysicalStart EFI_PHYSICAL_ADDRESS 首字节物理地址,必须按4KB对齐
VirtualStart EFI_VIRTUAL_ADDRESS 首字节虚拟地址,必须按4KB对齐
NumberOfPages UINT64 此区域的页面数
Attribute UINT64 内存属性

EFI_MEMORY_TYPE

数值 类型名 功能描述
0 EfiReservedMemoryType 保留
1 EfiLoaderCode 分配给OS加载器的代码
2 EfiLoaderData 分配给OS加载器的数据,应用程序分配内存的默认类型
3 EfiBootServicesCode 引导服务程序的代码区
4 EfiBootServicesData 引导服务程序的数据区,引导服务驱动分配内存的默认类型
5 EfiRuntimeServicesCode 运行时服务程序的代码区
6 EfiRuntimeServicesData 运行时服务程序的数据区,运行时服务驱动分配内存的默认类型
7 EfiConventionalMemory 可分配内存
8 EfiUnusableMemory 内存区域存在错误,不能使用
9 EfiACPIReclaimMemory 用于存放ACPI表
10 EfiACPIMemoryNVS 保留给固件使用
11 EfiMemoryMappedIO MMIO内存,可被运行时服务使用
12 EfiMemoryMappedIOPortSpace MMIO端口,被CPU用于转换内存周期到IO周期
13 EfiPalCode 保留给固件使用
14 EfiPersistentMemory 作为EfiConventionalMemory工作的内存区域

6.2、AllocatePool

EFI_STATUS AllocatePool
(IN EFI_MEMORY_TYPE PoolType,                       //分配的内存类型IN UINTN Size,                                 //分配的内存空间长度OUT VOID **Buffer                                    //分配的内存空间
);

6.4、获取可用物理内存

memory.c

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable)
{EFI_GRAPHICS_OUTPUT_PROTOCOL* gGraphicsOutput = 0;EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* Info = 0;UINTN InfoSize = 0;int i = 0;//配置图形设备的显示模式gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid,NULL,(VOID **)&gGraphicsOutput);Print(L"Current Mode:%02d,Version:%x,Format:%d,Horizontal:%d,Vertical:%d,ScanLine:%d,FrameBufferBase:%010lx,FrameBufferSize:%010lx\n",gGraphicsOutput->Mode->Mode,gGraphicsOutput->Mode->Info->Version,gGraphicsOutput->Mode->Info->PixelFormat,gGraphicsOutput->Mode->Info->HorizontalResolution,gGraphicsOutput->Mode->Info->VerticalResolution,gGraphicsOutput->Mode->Info->PixelsPerScanLine,gGraphicsOutput->Mode->FrameBufferBase,gGraphicsOutput->Mode->FrameBufferSize);long H_V_Resolution = gGraphicsOutput->Mode->Info->HorizontalResolution * gGraphicsOutput->Mode->Info->VerticalResolution;int MaxResolutionMode = gGraphicsOutput->Mode->Mode;for(i = 0;i < gGraphicsOutput->Mode->MaxMode;i++){gGraphicsOutput->QueryMode(gGraphicsOutput,i,&InfoSize,&Info);Print(L"Mode:%02d,Version:%x,Format:%d,Horizontal:%d,Vertical:%d,ScanLine:%d\n",i,Info->Version,Info->PixelFormat,Info->HorizontalResolution,Info->VerticalResolution,Info->PixelsPerScanLine);if((Info->PixelFormat == 1) && (Info->HorizontalResolution * Info->VerticalResolution > H_V_Resolution)){H_V_Resolution = Info->HorizontalResolution * Info->VerticalResolution;MaxResolutionMode = i;}gBS->FreePool(Info);}gGraphicsOutput->SetMode(gGraphicsOutput,MaxResolutionMode);gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid,NULL,(VOID **)&gGraphicsOutput);Print(L"Current Mode:%02d,Version:%x,Format:%d,Horizontal:%d,Vertical:%d,ScanLine:%d,FrameBufferBase:%010lx,FrameBufferSize:%010lx\n",gGraphicsOutput->Mode->Mode,gGraphicsOutput->Mode->Info->Version,gGraphicsOutput->Mode->Info->PixelFormat,gGraphicsOutput->Mode->Info->HorizontalResolution,gGraphicsOutput->Mode->Info->VerticalResolution,gGraphicsOutput->Mode->Info->PixelsPerScanLine,gGraphicsOutput->Mode->FrameBufferBase,gGraphicsOutput->Mode->FrameBufferSize);gBS->CloseProtocol(gGraphicsOutput,&gEfiGraphicsOutputProtocolGuid,ImageHandle,NULL);//获取硬件平台的物理地址空间信息UINTN MemoryMapSize = 0;EFI_MEMORY_DESCRIPTOR* MemoryMap = 0;UINTN MapKey = 0;UINTN DescriptorSize = 0;UINT32 DescriptorVersion = 0;Print(L"Get EFI_MEMORY_DESCRIPTOR Structure\n");gBS->GetMemoryMap(&MemoryMapSize,MemoryMap,&MapKey,&DescriptorSize,&DescriptorVersion);gBS->AllocatePool(EfiRuntimeServicesData,MemoryMapSize,(VOID**)&MemoryMap);gBS->GetMemoryMap(&MemoryMapSize,MemoryMap,&MapKey,&DescriptorSize,&DescriptorVersion);for(i = 0; i< MemoryMapSize / DescriptorSize; i++){EFI_MEMORY_DESCRIPTOR* MMap = (EFI_MEMORY_DESCRIPTOR*) (((CHAR8*)MemoryMap) + i * DescriptorSize);Print(L"MemoryMap %4d %10d (%10lx~%10lx) %016lx\n",MMap->Type,MMap->NumberOfPages,MMap->PhysicalStart,MMap->PhysicalStart + (MMap->NumberOfPages << 12),MMap->Attribute);}gBS->FreePool(MemoryMap);return EFI_SUCCESS;
}

首次执行GetMemoryMap方法时,并没有为内存描述符结构数组分配存储空间,所以第一次调用GetMemoryMap方法是为了取得内存描述符结构数组存储空间的长度。随后马上调用引导服务的AllocatePool方法为内存描述符结构数组分配存储空间

memory.inf

[Defines]INF_VERSION                    = 0x00010005BASE_NAME                      = memoryFILE_GUID                      = 6987936E-f301-4a63-9661-fc6030dcc855MODULE_TYPE                    = UEFI_APPLICATIONVERSION_STRING                 = 1.0ENTRY_POINT                    = UefiMain[Sources]memory.c[Packages]MdePkg/MdePkg.dec[LibraryClasses]UefiApplicationEntryPointUefiLib[Protocols]gEfiGraphicsOutputProtocolGuid[Guids]

6.5、编译

. ./edksetup.sh

6.6、编译、拷贝程序

[root@localhost edk2]# build
[root@localhost ~]# mount /dev/sdb1 /mnt/
[root@localhost ~]# cp /root/Desktop/dellaOS/edk2/Build/AppPkg/DEBUG_GCC48/X64/memory.efi /mnt/
[root@localhost ~]# sync
[root@localhost ~]# umount /mnt/

6.7、运行效果

7、从ESP分区中读取文件里的数据

7.1、ConverthevicePathToText

//返回值    CHAR16* 返回文本字符的设备路径
CHAR16* ConverthevicePathToText
(IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,             //待转换的设备路径IN BOOLEAN DisplayOnly,                                       //文本内容的长短IN BOOLEAN AllowShortcuts                                      //是否使用设备节点的精简文本表达形式
);

7.2、NextDevicePathNode

返回指定设备路径节点的下一个节点的指针

EFI_DEVICE_PATH_PROTOCOL *EFIAPI NexthevicePathNode
(IN CONST VOID *Node                                            //指向设备路径节点结构的指针
);

7.3、OpenVolume

打开一个卷的根目录

/**
EFI_SUCCESS                             已成功打开卷的根目录
EFI_UNSUPPORTED                         卷不支持请求的文件系统类型
EFI_NO_MEDIA                            设备无媒体介质
EFI_DEVICE_ERROR                        设备报错
EFI_VOLUME_CORRUPTED                    文件系统结构已损坏
EFI_ACCESS_DENIED                       服务拒绝访问卷的根目录
EFI_OUT_OF_RESOURCES                    没有资源打开卷的根目录
EFI_MEDIA_CHANGED                       设备的媒体介质发生改变
**/
EFI_STATUS OpenVolume
(IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL*This,                       //根目录所在卷OUT EFI_FILE_PROTOCOL **Root                                    //根目录的句柄
);

7.4、Open

打开指定路径下的文件

/**
EFI_SUCCESS             已打开文件
EFI_NOT_FOUND           文件不存在
EFI_NO_MEDIA            设备无媒体介质
EFI_MEDIA_CHANGED       设备的媒体介质发生改变
EFI_DEVICE_ERROR        设备报错
EFI_VOLUME_CORRUPTED    文件系统结构已损坏
EFI_WRITE_PROTECTED     写保护
EFI_ACCESS_DENIED       服务拒绝访问文件
EFI_OUT_OF_RESOURCES    没有资源打开文件
EFI_VOLUME_FULL         卷已满
**/
EFI_STATUS Open
(IN EFI_FILE_PROTOCOL *This,                                //卷的根目录句柄OUT EFI_FILE_PROTOCOL **NewHandle,                     //目标文件的句柄IN CHAR16 *FileName,                                       //文件路径IN UINT64 OpenMode,                                       //文件的打开模式IN UINT64 Attributes                                       //文件的属性(仅创建文件时有效)
);

7.5、GetInfo

GetInfo方法返回的是一个名为EFI_FILE_INFO的信息结构描述符结构体

/**
EFI_SUCCESS             已获得文件的信息
EFI_UNSUPPORTED         位置的信息类型
EFI_NO_MEDIA            设备无媒体介质
EFI_DEVICE_ERROR        设备报错
EFI_VOLUME_CORRUPTED    文件系统结构已损坏
EFI_BUFFER_TOO_SMALL    信息结构描述符的存储空间过小
**/
EFI_STATUS GetInfo
(IN EFI_FILE_PROTOCOL *This,                                //目标文件的句柄IN EFI_GUID *InformationType,                              //信息类型的GUID值IN OUT UINTN *BufferSize,                                   //信息结构描述符的存储空间长度OUT VOID *Buffer                                            //信息结构描述符的存储空间
);

7.6、EFI_FILE_INFO

结构体成员 成员类型 功能描述
Size UINT64 EFI_FILE_INFO结构的长度
FileSize UINT64 文件的实际长度
PhysicalSize UINT64 文件在存储介质中占用的空间
CreateTime EFI_TIME 文件的创建时间
LastAccessTime EFI_TIME 文件的最后访问时间
ModificationTime EFI_TIME 文件的最后修改时间
Attribute UINT64 文件的属性位
FileName[] CHAR16 文件名

文件属性说明表

属性名 属性值 功能描述
EFI_FILE_READ_ONLY 0x0000000000000001 只读文件
EFI_FILE_HIDDEN 0x0000000000000002 隐藏文件
EFI_FILE_SYSTEM 0x0000000000000004 系统文件
EFI_FILE_RESERVED 0x0000000000000008 保留
EFI_FILE_DIRECTORY 0x0000000000000010 目录
EFI_FILE_ARCHIVE 0x0000000000000020 存档文件
EFI_FILE_VALID_ATTR 0x0000000000000037 有效属性位

7.7、AllocatePages

/**
EFI_SUCCESS                     已成功分配内存页
EFI_OUT_OF_RESOURCES            没有足够的资源分配内存页
EFI_INVALID_PARAMETER           存在无效的参数
EFI_NOT_FOUND                   找不到请求分配的内存页
**/EFI_STATUS AllocatePages
(IN EFI_ALLOCATE_TYPE Type,                         //内存页的分配策略IN EFI_MEMORY_TYPE MemoryType,                        //分配的内存页类型IN UINTN Pages,                                       //分配的连续内存页数IN OUT EFI_PHYSICAL_ADDRESS *Memory                  //内存页的起始物理地址
);

EFI_ALLOCATE_TYPE

数值 类型名 功能描述
0 AllocateAnyPages 分配任何满足请求的页面
1 AllocateMaxAddress 分配任何小于等于Memory参数的页面
2 AllocateAddress 从Memory参数指定的地址分配页面

7.8、Read

/**
EFI_SUCCESS                 已读取数据
EFI_NO_MEDIA                设备无媒体介质
EFI_DEVICE_ERROR            设备报错
EFI_VOLUME_CORRUPTED        文件系统结构已损坏
EFI_BUFFER_TOO_SMALL        数据存储空间过小
**/
EFI_STATUS Read
(IN EFI_FILE_PROTOCOL *This,                        //目标文件的句柄IN OUT UINTN *BufferSize,                          //数据存储空间的长度OUT VOID *Buffer                                 //数据存储空间
);

7.9、file.c

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/DevicePathToText.h>
#include <Protocol/SimpleFileSystem.h>
#include <Protocol/LoadedImage.h>
#include <Guid/FileInfo.h>
#include <Library/DevicePathLib.h>EFI_STATUS PrintNode(EFI_DEVICE_PATH_PROTOCOL * Node)
{Print(L"(%d %d)/", Node->Type, Node->SubType);return 0;
}EFI_DEVICE_PATH_PROTOCOL* WalkthroughDevicePath(EFI_DEVICE_PATH_PROTOCOL* DevPath, EFI_STATUS (*Callbk)(EFI_DEVICE_PATH_PROTOCOL*))
{EFI_DEVICE_PATH_PROTOCOL* pDevPath = DevPath;while(!IsDevicePathEnd (pDevPath)){if(Callbk){EFI_STATUS Status = Callbk(pDevPath);if(Status != 0){if(Status < 0) pDevPath = NULL;break;}}pDevPath = NextDevicePathNode (pDevPath);}return pDevPath;
}EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable)
{EFI_LOADED_IMAGE        *LoadedImage;EFI_DEVICE_PATH         *DevicePath;EFI_FILE_IO_INTERFACE   *Vol;EFI_FILE_HANDLE         RootFs;EFI_FILE_HANDLE         FileHandle;EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* Device2TextProtocol = 0;//获取EFI_DEVICE_PATH_TO_TEXT_PROTOCOL(设备路径)实现句柄gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid,NULL,(VOID**)&Device2TextProtocol);//获取ImageHandle使用的EFI_LOADED_IMAGE_PROTOCOL协议接口的实现句柄gBS->HandleProtocol(ImageHandle,&gEfiLoadedImageProtocolGuid,(VOID*)&LoadedImage);//进一步检索LoadedImage->DeviceHandle句柄使用的EFI_DEVICE_PATH_PROTOCOL协议接口的实现句柄gBS->HandleProtocol(LoadedImage->DeviceHandle,&gEfiDevicePathProtocolGuid,(VOID*)&DevicePath);CHAR16* TextDevicePath = Device2TextProtocol->ConvertDevicePathToText(DevicePath,FALSE,TRUE);Print(L"%s\n",TextDevicePath);if(TextDevicePath)gBS->FreePool(TextDevicePath);//遍历打印路径节点信息WalkthroughDevicePath(DevicePath,PrintNode);Print(L"\n");//获取EFI_SIMPLE_FILE_SYSTEM_PROTOCOL协议接口的实现句柄gBS->HandleProtocol(LoadedImage->DeviceHandle,&gEfiSimpleFileSystemProtocolGuid,(VOID*)&Vol);//借助OpenVolume方法返回的分区句柄RootFs就可随意访问分区里的文件Vol->OpenVolume(Vol,&RootFs);//找到kernel.bin文件RootFs->Open(RootFs,&FileHandle,(CHAR16*)L"kernel.bin",EFI_FILE_MODE_READ,0);EFI_FILE_INFO* FileInfo;UINTN BufferSize = 0;EFI_PHYSICAL_ADDRESS pages = 0x100000;//结构体内存对齐!!!BufferSize = sizeof(EFI_FILE_INFO) * 2;//分配内存gBS->AllocatePool(EfiRuntimeServicesData,BufferSize,(VOID**)&FileInfo);//获取文件信息FileHandle->GetInfo(FileHandle,&gEfiFileInfoGuid,&BufferSize,FileInfo);Print(L"\tFileName:%s\t Size:%d\t FileSize:%d\t Physical Size:%d\n",FileInfo->FileName,FileInfo->Size,FileInfo->FileSize,FileInfo->PhysicalSize);Print(L"Read kernel file to memory\n");//分配内存页(每页4KB)gBS->AllocatePages(AllocateAddress,EfiLoaderData,(FileInfo->FileSize + 0xFFF) / 0x1000,&pages);BufferSize = FileInfo->FileSize;FileHandle->Read(FileHandle,&BufferSize,(VOID*)pages);gBS->FreePool(FileInfo);FileHandle->Close(FileHandle);RootFs->Close(RootFs);gBS->CloseProtocol(LoadedImage->DeviceHandle,&gEfiSimpleFileSystemProtocolGuid,ImageHandle,NULL);gBS->CloseProtocol(LoadedImage->DeviceHandle,&gEfiDevicePathProtocolGuid,ImageHandle,NULL);gBS->CloseProtocol(ImageHandle,&gEfiLoadedImageProtocolGuid,ImageHandle,NULL);gBS->CloseProtocol(Device2TextProtocol,&gEfiDevicePathToTextProtocolGuid,ImageHandle,NULL);return EFI_SUCCESS;
}

7.10、file.inf

[Defines]INF_VERSION                    = 0x00010005BASE_NAME                      = fileFILE_GUID                      = 6987936E-f301-4a63-9661-fc6030dcc858MODULE_TYPE                    = UEFI_APPLICATIONVERSION_STRING                 = 1.0ENTRY_POINT                    = UefiMain[Sources]file.c[Packages]MdePkg/MdePkg.dec[LibraryClasses]UefiApplicationEntryPointUefiLib[Protocols]gEfiDevicePathProtocolGuidgEfiDevicePathToTextProtocolGuidgEfiSimpleFileSystemProtocolGuidgEfiLoadedImageProtocolGuid[Guids]gEfiFileInfoGuid

7.11、运行效果

8、UEFI环境中的64位处理器平台

8.1、x64 平台

  • 处理器会根据Intel用户手册第三卷8.4节的描述进行引导。
  • 处理器运行于64位模式中(IA-32e mode)
  • 开启分页机制,并且UEFI使用的内存空间都进行了内存同一性映射(虚拟地址等于物理地址)。出于保护平台的目的,某些区域可能不具有读、写、执行等属性,甚至可能是未标记的空间。
  • 段选择子配置为平坦型,或不使用。
  • 开启中断,但除了UEFI引导服务的定时功能外,其他中断无响应服务。
  • 复位EFLAGS标志寄存器的方向标志位(DF)
  • 其他通过标志寄存器位处于未定义状态
  • 开辟128KB以上的栈空间。
  • 栈空间必须按照16B对齐,栈空间所在的内存页可标记为不可执行属性
  • 浮点控制字必须初始化为0x037F
  • 多媒体控制字必须初始化为0x1F80
  • CR0.EM=0
  • CR0.TS=0

8.2、使用UEFI的运行时服务

  • 保存所有标记为运行时代码和运行时数据的内存映射
  • 处理器运行在64位模式中
  • 开启分页机制
  • 所有段选择子都配置为平坦型,且虚拟地址与物理地址相等(对于使用SetVirtualAddressMap函数重定位运行时服务的虚拟地址空间,不必遵守此条款)
  • 复位EFLAGS标志寄存器的方向标志位(DF)
  • 开辟4KB以上的栈空间。
  • 栈空间必须按照16B对齐。
  • 浮点控制字必须初始化为0x037F
  • 多媒体控制字必须初始化为0x1F80
  • CR0.EM=0
  • CR0.TS=0
  • 中断的使能与禁止可由调用者自行决定
  • 在启动时加载的ACPI表可以包含在类型为EfiACPIReclaimMemory(推荐)或EfiACPIMemoryNVS的内存中,而ACPI的FACS表必须包含在类型为EfiACPIMemoryNVS的内存空间中。
  • 系统固件不能使用类型为EfiACPIReclaimMemory和EfiACPIMemoryNVS的内存进行虚拟地址映射。
  • EfiACPIReclaimMemory和EfiACPIMemoryNVS类型的内存都是按照4KB边界对齐的,且容量为4KB的整数倍。
  • UEFI要求使用类型为EFI_MEMORY_RUNTIME的内存进行虚拟地址映射时,必须按照4KB边界对齐,且容量为4KB的整数倍。
  • ACPI的内存操作区必须从UEFI的内存映射中继承可缓存属性,如果在系统内存映射时未设置可缓存属性,则ACPI的内存操作区必须从ACPI命名空间中继承可缓存属性。如果在系统内存映射或ACPI命名空间中没有可缓存属性,则必须假定此区域是不可缓存的。
  • 运行时加载ACPI表必须保存在类型为EfiACPIMemoryNVS的内存中,且属性应该设置为可缓存。如果在UEFI内存中没有ACPI表的地址信息,则可以从ACPI内存描述表继承可缓存属性。如果在UEFI内存映射或ACPI内存中都没有可缓存属性,那么ACPI表的地址信息一定是不可缓存的。
  • 通常,在引导服务期间UEFI配置表可以加载的内存类型有EfiRuntimeServicesData、EfiBootServicesData、EfiACPIReclaimMemory和EfiACPIMemoryNVS,而在运行时服务期间UEFI配置表可以加载的内存类型只有EfiRuntimeServicesData(推荐)或EfiACPIMemoryNVS两种。

8.3、默认的调用约定

函数的前四个整型参数是由调用者通过寄存器传入。整型参数从左向右依次使用RCX、RDX、R8、R9四个寄存器,代码清单13是UefiMain函数的调用约定伪代码,其中RCX和RDX寄存器保存着UefiMain函数的参数,而RSP寄存器指向的地址中保存着UefiMain函数的返回地址。调用者使用栈空间向函数传入第五个及以上的参数。所有使用寄存器传入的参数在寄存器里必须进行右对齐,这样可使函数正确处理位操作。调用者负责为数组和字符串分配内存空间,并通过指针将他们传递给函数。如果调用者传递的结构体和联合体的大小为8/16/32/64,那么可以将其看作为整型数,进行参数传递。否则,必须使用指针进行参数传递。

8.4、应用程序中的页表使能与地址空间转换

引导服务定义了一个未开启分页模式(在32位环境下)或已开启分页模式但虚拟地址与物理地址相等的执行环境,本段将描述如何在开启分页模式的时候编写一个应用程序。一些操作系统要求操作系统加载器在引导服务阶段能够进行OS所需的地址空间转换。

如果一个UEFI应用程序使用其自带的页表、GDT或IDT,那么应用程序必须确保固件可与这些数据结构一起执行。当应用程序开启分页模式时,符合UEFI规范的固件有两种方式可以执行。

  • 显式固件调用
  • 通过计时器事件抢占应用程序固件

对于开启页表映射的应用程序,可以在每个UEFI调用之前恢复固件使用的页表映射。但是,由于在页表切换期间存在着抢占的可能性,因此需要开启页表映射的应用程序在页表切换期间关闭中断。如果应用程序捕捉到中断,并在调用UEFI中断处理服务之前能够恢复UEFI固件环境,那么开启页表映射的应用程序可以合法的启用中断。在UEFI中断处理服务执行结束后,将返回到开启页表映射的应用程序中,此时只需恢复应用程序的页表映射即可。

9、系统内核的引导启动

至此,编写BootLoader程序的障碍已经全部扫清,万事皆已具备,那么本章的剩余篇幅就将围绕系统内核在UEFI环境中的引导启动进行实现

通常情况下,引导加载程序向系统内核传递的资源信息和配置信息会相对结构化一些。既然今后可以使用C语言编写引导加载程序,那么采用更加结构化的方式来向系统内核传递物理地址空间信息和图形设备显示模式信息将是非常不错的选择,此举可使资源的管理更加清晰化、结构化,而且结构化的资源更便于扩展,方便以后向内核传递更多资源与配置信息

9.1、BootLoader.c

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Protocol/SimpleFileSystem.h>
#include <Protocol/LoadedImage.h>
#include <Guid/FileInfo.h>//内存描述符
struct DELLA_MEMORY_DESCRIPTOR
{long address;long length;int  type;
}__attribute__((packed));//内存信息
struct DELLA_MEMORY_DESCRIPTOR_INFORMATION
{int Entry_count;struct DELLA_MEMORY_DESCRIPTOR Entry[0];
};//内核信息
struct DELLA_KERNEL_PARAMETER_INFORMATION
{EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Graphics_Info;struct DELLA_MEMORY_DESCRIPTOR_INFORMATION Memory_Info;
};EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable){EFI_STATUS status = EFI_SUCCESS;struct DELLA_KERNEL_PARAMETER_INFORMATION *kernel_parameter_info = NULL;void (*KernelStart)(void);int i = 0;/***************************************************************尝试寻找kernel.bin***************************************************************/EFI_LOADED_IMAGE        *LoadedImage;EFI_FILE_IO_INTERFACE   *SimpleFileSystem;EFI_FILE_HANDLE         RootDirectory;EFI_FILE_HANDLE         FileHandle;EFI_FILE_INFO     *FileInfo;UINTN BufferSize = 0;EFI_PHYSICAL_ADDRESS pages = 0x100000;//通过HandleProtocol获取ImageHandle(UefiMain参数)使用的EFI_LOADED_IMAGE_PROTOCOL(加载影像)的实现句柄gBS->HandleProtocol(ImageHandle,&gEfiLoadedImageProtocolGuid,(VOID*)&LoadedImage);//通过上面已检索出的EFI_LOADED_IMAGE实现句柄下的DeviceHandle,进一步检索EFI_SIMPLE_FILE_SYSTEM_PROTOCOL(文件系统)实现句柄gBS->HandleProtocol(LoadedImage->DeviceHandle,&gEfiSimpleFileSystemProtocolGuid,(VOID*)&SimpleFileSystem);//借助OpenVolume方法返回的分区根目录的句柄SimpleFileSystem->OpenVolume(SimpleFileSystem,&RootDirectory);//使用Open方法打开kernel.bin文件status = RootDirectory->Open(RootDirectory,&FileHandle,(CHAR16*)L"kernel.bin",EFI_FILE_MODE_READ,0);if(EFI_ERROR(status)){Print(L"Open kernel.bin Failed,EFI_STATUS:%a\n",status);return status;}//为结构体分配内存BufferSize = sizeof(EFI_FILE_INFO) * 2;gBS->AllocatePool(EfiRuntimeServicesData,BufferSize,(VOID**)&FileInfo);//获取文件信息FileHandle->GetInfo(FileHandle,&gEfiFileInfoGuid,&BufferSize,FileInfo);Print(L"\tFile Name:%s\t Struct Size:%d\t File Size:%d\t Physical Size:%d\n",FileInfo->FileName,FileInfo->Size,FileInfo->FileSize,FileInfo->PhysicalSize);//从物理地址0x100000处按文件占用的物理内存页面(每个物理内存页面大小为4KB)数量分配页面gBS->AllocatePages(AllocateAddress,EfiLoaderData,(FileInfo->FileSize + 0xFFF) / 0x1000,&pages);//将kernel.bin文件里的数据被读取到内存BufferSize = FileInfo->FileSize;FileHandle->Read(FileHandle,&BufferSize,(VOID*)pages);//释放FileInfo、FileHandle、RootDirectorygBS->FreePool(FileInfo);FileHandle->Close(FileHandle);RootDirectory->Close(RootDirectory);/***************************************************************设置显示模式***************************************************************/EFI_GRAPHICS_OUTPUT_PROTOCOL *gGraphicsOutput = 0;EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info = 0;UINTN InfoSize = 0;pages = 0x60000;//为DELLA_KERNEL_PARAMETER_INFORMATION从0x60000开始分配一个内存页kernel_parameter_info = (struct DELLA_KERNEL_PARAMETER_INFORMATION *)0x60000;gBS->AllocatePages(AllocateAddress,EfiLoaderData,1,&pages);//使用前需调用SetMem清除里面的脏数据gBS->SetMem((void*)kernel_parameter_info,0x1000,0);//使用LocateProtocol方法取得EFI_GRAPHICS_OUTPUT_PROTOCOL协议实现句柄gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid,NULL,(VOID **)&gGraphicsOutput);long HorizontalVerticalResolution = gGraphicsOutput->Mode->Info->HorizontalResolution * gGraphicsOutput->Mode->Info->VerticalResolution;int MaxResolutionMode = gGraphicsOutput->Mode->Mode;//找到最大的分辨率for(i = 0;i < gGraphicsOutput->Mode->MaxMode;i++){gGraphicsOutput->QueryMode(gGraphicsOutput,i,&InfoSize,&Info);if((Info->PixelFormat == 1) && (Info->HorizontalResolution * Info->VerticalResolution > HorizontalVerticalResolution)){HorizontalVerticalResolution = Info->HorizontalResolution * Info->VerticalResolution;MaxResolutionMode = i;}gBS->FreePool(Info);}//设置显示模式gGraphicsOutput->SetMode(gGraphicsOutput,MaxResolutionMode);//拷贝显示模式信息kernel_parameter_info->Graphics_Info->MaxMode = gGraphicsOutput->Mode->MaxMode;kernel_parameter_info->Graphics_Info->Mode = gGraphicsOutput->Mode->Mode;kernel_parameter_info->Graphics_Info->SizeOfInfo = gGraphicsOutput->Mode->SizeOfInfo;kernel_parameter_info->Graphics_Info->FrameBufferBase = gGraphicsOutput->Mode->FrameBufferBase;kernel_parameter_info->Graphics_Info->FrameBufferSize = gGraphicsOutput->Mode->FrameBufferSize;kernel_parameter_info->Graphics_Info->Info->Version = gGraphicsOutput->Mode->Info->Version;kernel_parameter_info->Graphics_Info->Info->HorizontalResolution = gGraphicsOutput->Mode->Info->HorizontalResolution;kernel_parameter_info->Graphics_Info->Info->VerticalResolution = gGraphicsOutput->Mode->Info->VerticalResolution;kernel_parameter_info->Graphics_Info->Info->PixelFormat = gGraphicsOutput->Mode->Info->PixelFormat;kernel_parameter_info->Graphics_Info->Info->PixelInformation = gGraphicsOutput->Mode->Info->PixelInformation;kernel_parameter_info->Graphics_Info->Info->PixelsPerScanLine = gGraphicsOutput->Mode->Info->PixelsPerScanLine;//更新页表中的FrameBuffer地址映射(???Print(L"Map Graphics FrameBufferBase to Virtual Address 0xffff800003000000\n");long * PageTableEntry = (long *)0x103000;for(i = 0;i < (gGraphicsOutput->Mode->FrameBufferSize + 0x200000 - 1) >> 21;i++){*(PageTableEntry + 24 + i) = gGraphicsOutput->Mode->FrameBufferBase | 0x200000 * i | 0x87;Print(L"Page %02d,Address:%018lx,Value:%018lx\n",i,(long)(PageTableEntry + 24 + i),*(PageTableEntry + 24 + i));}/***************************************************************物理地址空间信息***************************************************************/struct DELLA_MEMORY_DESCRIPTOR *E820p = kernel_parameter_info->Memory_Info.Entry;struct DELLA_MEMORY_DESCRIPTOR *LastE820 = NULL;long LastEndAddr = 0;int E820Count = 0;UINTN MemoryMapSize = 0;EFI_MEMORY_DESCRIPTOR* MemoryMap = 0;UINTN MapKey = 0;UINTN DescriptorSize = 0;UINT32 DescriptorVersion = 0;//取得内存描述符结构数组存储空间的长度gBS->GetMemoryMap(&MemoryMapSize,MemoryMap,&MapKey,&DescriptorSize,&DescriptorVersion);MemoryMapSize *= 2;//为内存描述符结构数组分配存储空间gBS->AllocatePool(EfiRuntimeServicesData,MemoryMapSize,(VOID**)&MemoryMap);Print(L"Get MemoryMapSize:%d,DescriptorSize:%d,count:%d\n",MemoryMapSize,DescriptorSize,MemoryMapSize/DescriptorSize);//使用前需调用SetMem清除里面的脏数据gBS->SetMem((void*)MemoryMap,MemoryMapSize,0);//再次获取物理地址空间信息gBS->GetMemoryMap(&MemoryMapSize,MemoryMap,&MapKey,&DescriptorSize,&DescriptorVersion);Print(L"Get MemoryMapSize:%d,DescriptorSize:%d,count:%d\n",MemoryMapSize,DescriptorSize,MemoryMapSize/DescriptorSize);//将物理地址信息转换为E820内存结构(为什么是12,因为对于EFI_MEMORY_DESCRIPTOR来说,其地址已按照按4KB对齐(即1 0000 0000 0000)for(i = 0;i < MemoryMapSize / DescriptorSize;i++){int MemoryType = 0;EFI_MEMORY_DESCRIPTOR* MMap = (EFI_MEMORY_DESCRIPTOR*) ((CHAR8*)MemoryMap + i * DescriptorSize);if(MMap->NumberOfPages == 0)continue;switch(MMap->Type){//保留case EfiReservedMemoryType://MMIO内存,可被运行时服务使用case EfiMemoryMappedIO://MMIO端口,被CPU用于转换内存周期到IO周期case EfiMemoryMappedIOPortSpace://保留给固件使用case EfiPalCode:MemoryType= 2;   //2:ROM or Reservedbreak;//内存区域存在错误,不能使用case EfiUnusableMemory:MemoryType= 5;   //5:Unusablebreak;//用于存放ACPI表case EfiACPIReclaimMemory:MemoryType= 3;  //3:ACPI Reclaim Memorybreak;//分配给OS加载器的代码case EfiLoaderCode://分配给OS加载器的数据,应用程序分配内存的默认类型case EfiLoaderData://引导服务程序的代码区case EfiBootServicesCode://引导服务程序的数据区,引导服务驱动分配内存的默认类型case EfiBootServicesData://运行时服务程序的代码区case EfiRuntimeServicesCode://运行时服务程序的数据区,运行时服务驱动分配内存的默认类型case EfiRuntimeServicesData://可分配内存case EfiConventionalMemory://作为EfiConventionalMemory工作的内存区域case EfiPersistentMemory:MemoryType= 1; //1:RAMbreak;//保留给固件使用case EfiACPIMemoryNVS:MemoryType= 4; //4:ACPI NVS Memorybreak;//无效的UEFI内存类型default:Print(L"Invalid UEFI Memory Type:%4d\n",MMap->Type);continue;}if((LastE820 != NULL) && (LastE820->type == MemoryType) && (MMap->PhysicalStart == LastEndAddr)){LastE820->length += MMap->NumberOfPages << 12;LastEndAddr += MMap->NumberOfPages << 12;}else{E820p->address = MMap->PhysicalStart;E820p->length = MMap->NumberOfPages << 12;E820p->type = MemoryType;LastEndAddr = MMap->PhysicalStart + (MMap->NumberOfPages << 12);LastE820 = E820p;E820p++;E820Count++;         }}//将内存信息保存到KERNEL_BOOT_PARAMETER_INFORMATION结构kernel_parameter_info->Memory_Info.Entry_count = E820Count;LastE820 = kernel_parameter_info->Memory_Info.Entry;//冒泡排序,根据内存地址先后排列E820信息int j = 0;for(i = 0; i< E820Count; i++){struct DELLA_MEMORY_DESCRIPTOR* e820i = LastE820 + i;struct DELLA_MEMORY_DESCRIPTOR MemMap;for(j = i + 1; j< E820Count; j++){struct DELLA_MEMORY_DESCRIPTOR* e820j = LastE820 + j;if(e820i->address > e820j->address){MemMap = *e820i;*e820i = *e820j;*e820j = MemMap;}}}LastE820 = kernel_parameter_info->Memory_Info.Entry;for(i = 0;i < E820Count;i++){Print(L"MemoryMap (%10lx<->%10lx) %4d\n",LastE820->address,LastE820->address+LastE820->length,LastE820->type);LastE820++;}gBS->FreePool(MemoryMap);Print(L"Call ExitBootServices And Jmp to Kernel.\n");gBS->GetMemoryMap(&MemoryMapSize,MemoryMap,&MapKey,&DescriptorSize,&DescriptorVersion);//统一释放资源gBS->CloseProtocol(LoadedImage->DeviceHandle,&gEfiSimpleFileSystemProtocolGuid,ImageHandle,NULL);gBS->CloseProtocol(ImageHandle,&gEfiLoadedImageProtocolGuid,ImageHandle,NULL);gBS->CloseProtocol(gGraphicsOutput,&gEfiGraphicsOutputProtocolGuid,ImageHandle,NULL);/***************************************************************向内核交出控制权***************************************************************///退出UEFI引导服务status = gBS->ExitBootServices(ImageHandle,MapKey);if(EFI_ERROR(status)){Print(L"ExitBootServices: Failed, Memory Map has Changed.\n");return EFI_INVALID_PARAMETER;}//向内核交出控制权KernelStart = (void *)0x100000;KernelStart();return EFI_SUCCESS;
}

9.2、BootLoader.inf

[Defines]INF_VERSION                    = 0x00010005BASE_NAME                      = BootLoaderFILE_GUID                      = 6987936E-f301-4a63-9661-fc6030dcc860MODULE_TYPE                    = UEFI_APPLICATIONVERSION_STRING                 = 1.0ENTRY_POINT                    = UefiMain[Sources]BootLoader.c[Packages]MdePkg/MdePkg.dec[LibraryClasses]UefiApplicationEntryPointUefiLibUefiBootServicesTableLib[Protocols]gEfiGraphicsOutputProtocolGuidgEfiSimpleFileSystemProtocolGuidgEfiLoadedImageProtocolGuid[Guids]gEfiFileInfoGuid

9.3、运行效果

至此,新皇登基!

DellaOS引导程序篇(完结)相关推荐

  1. [算法]力扣刷题-初级算法 - 数组(三)(数组篇完结) [两数之和] [有效的数独] [旋转图像]

    初级算法 - 数组篇完结: 初级算法 - 数组(一): https://blog.csdn.net/weixin_43854928/article/details/121315702 初级算法 - 数 ...

  2. Shell编程进阶篇(完结)

    1.1 for循环语句 在计算机科学中,for循环(英语:for loop)是一种编程语言的迭代陈述,能够让程式码反复的执行. 它跟其他的循环,如while循环,最大的不同,是它拥有一个循环计数器,或 ...

  3. 浅谈.Net异步编程的前世今生----异步函数篇(完结)

    前言 上一篇我们着重讲解了TPL任务并行库,可以看出TPL已经很符合现代API的特性:简洁易用.但它的不足之处在于,使用者难以理解程序的实际执行顺序. 为了解决这些问题,在C# 5.0中,引入了新的语 ...

  4. Cocos2d-x3.0游戏实例之《别救我》第十篇(完结)——用Json配置各类型怪物数据

    现在我们有2种类型的怪物,而且创建的时候是写死在代码里的,这是要作死的节奏~ 所以,必须可配置,不然会累死人的. 笨木头花心贡献,啥?花心?不呢,是用心~ 转载请注明,原文地址: http://www ...

  5. 两个月15斤以上的健康减脂减重法,与饥饿、运动等无关的自我实验的验证方法(第六篇完结,无收费内容)...

    阅读本文前请先看前五篇内容,本文有部分修改,但是涉及前五篇的内容会大量隐藏,只保留关联修改部分,链接如下: 两个月15斤以上的健康减脂减重法,与饥饿.运动等无关的自我实验的验证方法(第五篇) 两个月1 ...

  6. 数学建模清风微信公众号的习题答案(挑战篇-完结)

    以下题目是来自微信公众号数学建模清风老师的题目 以下是本人结合在微信公众号上学到的知识去做的,如有不正确或不足,欢迎指正! Q15.在本章3.3.5小节介绍sort函数时,我们留下了一个问题:如果存在 ...

  7. CSS3基础一篇完结(豪豪的备忘录)

    css索引 绪论 css简介 ⭐css语法规范 css代码风格: css基础选择器 标签选择器: 多类名选择器 id选择器 ⭐通配符选择器 ⭐选择器总结 字体大小 字体粗细 字体的综合写法 字体总结 ...

  8. omapl138移植uboot系列之在线升级(第八篇完结篇)

    uboot在线升级的原理是先通过某种外部接口(如网口.串口.USB等)将目标文件加载到DDR,然后从DDR中读取二进制文件并写到存储介质指定地址,由于639A底板并无网口,唯一与外部通信的接口就是串口 ...

  9. 【Mysql OCP】MySQL OCP备考准备篇~~~~完结版

    已经考过了,前来更新一下备考结果吧: 本打算五一前就去考的,没有提前提交一下公司报销考试费的流程,又碰上五一假期,打算at home or office来着,国内不支持了,打电话问了一圈客服没解决.假 ...

  10. 进阶渲染系列(七)——三向贴图(任意表面纹理化)【进阶篇完结】

    目录 1 没有UV坐标的纹理 1.1 不适用默认UV 1.2 收集表面属性 1.3 定制表面 1.4 没有切线空间 1.5 三向着色器 2 三面纹理化 2.1 基于位置的纹理映射 2.2 组合所有的三 ...

最新文章

  1. 谷歌发布最大语言模型:等于9个GPT-3,训练成本却低得多
  2. linux cat命令源码,每天一个linux命令:cat 命令
  3. oracle登录账号和密码,oracle 登录账号与密码oracle按照中文排序
  4. git 在ssh情况下提交代码
  5. 西门子scl语言编程手册_西门子SCL编程PEEK指令讲解
  6. 实例14:python
  7. 游戏详细设计说明书_宜家的说明书设计脑洞太大了!
  8. 开源 CI/CD 构建框架 TekTon 的深入剖析
  9. linux13位时间戳,Kotlin 处理Linux时间戳
  10. Process插件:typecho加载页面进度条插件
  11. RHEL Linux与CentOS Linux的关系
  12. 计算机系统的确认与验证,确认与验证(指南).pdf
  13. 金蝶EAS,序时簿ListUI只允许选择一行或至少选择一行记录
  14. 殷国辉老师 银行行长经营管理专家
  15. 空间坐标转化——三维转二维
  16. 关于商业智能BI,今天只谈这五点
  17. 最新双十一淘宝查历史最低价流量主小程序源码/亲测
  18. 感谢开源,让我的青春永不褪色!
  19. 题外话之怎么脱离旱鸭子
  20. 【Echarts】在Vue中使用Echarts

热门文章

  1. Java基础——常用对象API(4):集合框架5:Map集合
  2. navigator用于管理浏览器运行环境信息
  3. ubuntu窗口排列和分屏工具
  4. 故障树分析法(FTA)
  5. Copula、CoVaR、Garch、DCC、藤Vine、BEKK、SV、ECM
  6. Drools规则引擎实践直白总结,Java开发教程入门
  7. linux实验2 vi编译器的使用
  8. houdini 体积
  9. H3C交换机堆叠配置
  10. C语言中图形 * 的输出