基本环境搭建

这是我的硬件环境及操作系统

硬件环境

下载 edk2 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 安装需要的软件包
sudo apt update
sudo apt install git
mkdir -p ~/UEFI
cd UEFI
git clone "https://github.com/tianocore/edk2.git"
cd edk2
# 使用这个分支
git checkout origin/stable/202408
git submodule update --init --recursive
git branch
# 查看子模块是否都已正确初始化,如果子模块未下载完毕,编译时会有一些问题
git submodule status
cd -
# 下载edk2-libc的代码,这个主要是为了在UEFI开发中使用c标准库
git clone https://github.com/tianocore/edk2-libc.git
# 创建code文件夹用于存放我们自己的代码
mkdir -p code

安装编译工具

1
2
3
4
5
6
7
8
# 下载一些基本软件包
sudo apt-get install python3 python3-distutils uuid-dev build-essential bison flex nasm acpica-tools gcc
# 安装arm的编译器, 这里主要是为了编译aarch64的
mkdir -p ~/UEFI/toolchain
cd ~/UEFI/toolchain
wget https://developer.arm.com/-/media/Files/downloads/gnu-a/8.2-2019.01/gcc-arm-8.2-2019.01-x86_64-aarch64-elf.tar.xz
tar -xf gcc-arm-8.2-2019.01-x86_64-aarch64-elf.tar.xz
cd -

HelloWorld

下面来通过一个例子 HelloWorld 来实现实现编译 UEFI 代码到目标平台为 x64 或 aarch64, 并支持在 Emulator 和 qemu 中运行, 最后用 gdb 调试程序

代码

1
2
3
4
5
touch HelloWorld.dsc
touch HelloWorld.inf
touch HelloWorld.c
# 这个命令行工具可以生成uuid, 后面的dsc和inf中的uuid都是这样生成的
uuidgen

HelloWorld.dsc

DSC文件是包描述文件,其中Defines中的所有字段都是强制性的。

对于LibraryClasses中的路径可以通过以下命令查找

1
2
3
4
5
cd edk2
# 以UefiApplicationEntryPoint为例
grep UefiApplicationEntryPoint -r ./ --include=*.inf | grep LIBRARY_CLASS
# 通过GUID查找
grep -i 752F3136 -r ./ --exclude-dir=Build

LibraryClasses的格式是

1
LibraryClassName|Path/To/LibInstanceName.inf

对于DSC文件的完整解释,参考以下链接:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[Defines]
DSC_SPECIFICATION = 0x0001001A
PLATFORM_GUID = c08977d4-6e87-42f6-bf5c-4d41cfe7ba53
PLATFORM_VERSION = 0.01
PLATFORM_NAME = HelloWorld
SKUID_IDENTIFIER = DEFAULT
SUPPORTED_ARCHITECTURES = AARCH64|X64
BUILD_TARGETS = DEBUG|RELEASE|NOOPT
OUTPUT_DIRECTORY = $(PKG_OUTPUT_DIR)

[LibraryClasses]
BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf
ShellCEntryLib|ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf
HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf


UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf

DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf

[LibraryClasses.ARM,LibraryClasses.AARCH64]
NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf
NULL|MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf

[LibraryClasses.X64]
RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf

[Components]
HelloWorld.inf

HelloWorld.inf

INF文件是edk2 app的配置文件,其中

  • [Defines] 该节定义了一些模块的基本信息
    • BASE_NAME app的名称
    • FILE_GUID 可以通过命令uuidgen生成,UEFI通过GUID来区分不同的模块
    • MODULE_TYPE 这里填UEFI_APPLICATION
    • ENTRY_POINT c代码中的主函数的名称
  • [Sources] 模块的源代码,一般是.c,.h文件
  • [Packages] 需要使用到的包
  • [LibraryClasses] 需要使用到的库

对于INF文件的完整解释,参考以下链接:

下面是定义的一个简单的模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Variables defined to be used during the build process
[Defines]
INF_VERSION = 1.25
BASE_NAME = HelloWorld
FILE_GUID = 5455334b-dbd9-4f95-b6ed-5ae261a6a0c1
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain

# Source code
[Sources]
HelloWorld.c

# Required packages
[Packages]
MdePkg/MdePkg.dec # Contains Uefi and UefiLib

# Required Libraries
[LibraryClasses]
UefiApplicationEntryPoint # Uefi application entry point
UefiLib # UefiLib
UefiBootServicesTableLib

HelloWorld.c

1
2
3
4
5
6
7
8
9
10
#include <Library/UefiLib.h>
#include <Uefi.h>

EFI_STATUS
EFIAPI
UefiMain(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
Print(L"Hello World!!!\n");
SystemTable->BootServices->Stall(10000000);
return EFI_SUCCESS;
}

编译脚本

首先我们需要创建一个脚本,用于设置环境变量

1
2
touch env.sh
chmod a+x env.sh

env.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/bash
# 项目名称,也是源代码的源文件目录
export PROJ_NAME="HelloWorld"
# dsc文件名
export DSC_NAME="HelloWorld"
# inf文件名
export INF_NAME="HelloWorld"
# 也是编译生成的*.efi的名字,在inf的BASE_NAME中定义
export INF_BASE_NAME="HelloWorld"
# UEFI 工作目录
export UEFI_WORKSPACE="$HOME/UEFI"
# EDK II 路径
export EDK_PATH="$UEFI_WORKSPACE/edk2"
# EDK II libc路径
export EDK_LIBC_PATH="$UEFI_WORKSPACE/edk2-libc"
# 应用代码路径
export APP_PATH="$UEFI_WORKSPACE/code/$PROJ_NAME"
# 构建输出目录
export PKG_OUTPUT_DIR="$APP_PATH/Build"
# 模拟器路径
export EMULATOR_PATH="$EDK_PATH/Build/EmulatorX64/DEBUG_GCC5/X64"
# 包路径设置,支持多个路径,用冒号分隔
export PACKAGES_PATH="$EDK_PATH:$EDK_LIBC_PATH:$APP_PATH"
# 指定 Python 解释器
export PYTHON_COMMAND="/usr/bin/python3"
# 确认设置完成
echo "Environment variables for $PROJ_NAME project are configured."

接下写一个脚本实现编译我们的代码到 x64 目标平台

1
2
touch build-x64.sh
chmod a+x build-x64.sh

build-x64.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
set -e
trap "Exiting" INT

# environment variables
source env.sh

export GCC5=/usr/bin/gcc
cd $EDK_PATH
source edksetup.sh
cd -

# Building BaseTools
make -C $EDK_PATH/BaseTools
# 这里设置-b参数为DEBUG,需要部署时用RELEASE
# -p --platform=
# -m --module=
# -a --arch=
# -b --buildtarget=
# -t --taggname=
build -p $APP_PATH/$DSC_NAME.dsc -m $APP_PATH/$INF_NAME.inf -a X64 -t GCC5 -b DEBUG -D PKG_OUTPUT_DIR=$PKG_OUTPUT_DIR

编译到 aarch64 平台同理

1
2
touch build-aarch64.sh
chmod a+x build-aarch64.sh

build-aarch64.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
set -e
trap "Exiting" INT

# environment variables
source env.sh

export GCC5_AARCH64_PREFIX=$UEFI_WORKSPACE/toolchain/gcc-arm-8.2-2019.01-x86_64-aarch64-elf/bin/aarch64-elf-

cd $EDK_PATH
source edksetup.sh
cd -

# Building BaseTools
make -C $EDK_PATH/BaseTools

build -p $APP_PATH/$DSC_NAME.dsc -m $APP_PATH/$INF_NAME.inf -a AARCH64 -t GCC5 -b DEBUG -D PKG_OUTPUT_DIR=$PKG_OUTPUT_DIR

运行

Emulator 运行

最后我们写一个脚本在 edk2 自带的模拟器上运行一下, 注意这里需要你有 gui 环境, 如果是只有命令行则跳过这一步, 看下面一节用 qemu 运行

1
2
touch run.sh
chmod a+x run.sh

run.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
set -e
trap "Exiting" INT

source env.sh

export GCC5=/usr/bin/gcc
# Emulator编译,编译过一次就不用再编译了
cd $EDK_PATH
source edksetup.sh
build -p $EDK_PATH/EmulatorPkg/EmulatorPkg.dsc -t GCC5 -a X64

sudo mkdir -p $EMULATOR_PATH/UEFI_Disk

sudo cp $APP_PATH/Build/DEBUG_GCC5/X64/$INF_BASE_NAME.efi $EMULATOR_PATH/UEFI_Disk/

cd $EMULATOR_PATH
./Host

qemu 运行

首先编译并安装 qemu,这里我选择 8.1.5 的版本, 如果你的没有达到预期的效果可以考虑使用这个版本的 qemu

1
2
3
4
5
6
7
8
9
10
11
12
git clone https://gitlab.com/qemu-project/qemu.git
cd qemu
git checkout stable-8.1
sudo apt install python3-venv python3-pip python3-setuptools python3-sphinx ninja-build pkg-config libglib2.0-dev libpixman-1-dev
# x86_64
./configure --target-list=x86_64-softmmu
make -j$(nproc)
sudo make install
# aarch64
./configure --target-list=aarch64-softmmu
make -j$(nproc)
sudo make install

接下来写脚本用 qemu 运行, 这里一些参数是为下一节用 gdb 调试程序用的, 但如果你只是想用 qemu 运行一下也不影响

1
2
touch debug.sh
chmod a+x debug.sh

debug-x64.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/bin/bash
set -e
trap "Exiting" INT

# environment variables
source env.sh

export GCC5=/usr/bin/gcc
# 编译过一次就不用再编译了
cd $EDK_PATH
source edksetup.sh
build -a X64 -p OvmfPkg/OvmfPkgX64.dsc -t GCC5 -b DEBUG #-D SOURCE_DEBUG_ENABLE

cd $APP_PATH
mkdir -p _ovmf_dbg
cd _ovmf_dbg
rm -f debug.log
# 与ubuntu22.04软件源默认的qemu不兼容,需要升级qemu版本到v8.1.5
cp $EDK_PATH/Build/OvmfX64/DEBUG_GCC5/FV/OVMF.fd ./

mkdir -p UEFI_Disk
cp $APP_PATH/Build/DEBUG_GCC5/X64/$INF_BASE_NAME.efi ./UEFI_Disk/
cp $APP_PATH/Build/DEBUG_GCC5/X64/$INF_BASE_NAME.debug ./UEFI_Disk/

# -s 启用 GDB 调试,默认监听在 127.0.0.1:1234
# -bios OVMF.fd,指定 OVMF 固件文件,这是一个支持 UEFI 的 QEMU 固件。
# -debugcon file:debug.log 将调试输出重定向到 debug.log 文件。
# -global isa-debugcon.iobase=0x402 配置调试控制台的 I/O 基地址。


qemu-system-x86_64 \
-s \
-bios OVMF.fd \
-drive format=raw,file=fat:rw:UEFI_Disk/ \
-net none \
-debugcon file:debug.log \
-global isa-debugcon.iobase=0x402 \
-nographic

这个脚本首先会编译OVMF(Open Virtual Machine Firmware),OVMF 是一个基于 EDKII 的固件,可以在 qemu x86-64 虚拟机下运行。这使得调试和实验 UEFI 固件变得更加容易;无论是用于测试操作系统启动,还是使用(内置的)EFI shell。

OVMF 固件(用于 QEMU 的 UEFI 实现)被分为两个文件:

  • OVMF_CODE.fd:包含实际的 UEFI 固件。
  • OVMF_VARS.fd:作为一个“模板”用于模拟持久化的 NVRAM 存储。
    所有虚拟机实例可以共享来自 ovmf 包的系统范围内的只读 OVMF_CODE.fd 文件,但每个实例都需要一个私有的、可写的 OVMF_VARS.fd 副本。
    在qemu中,可以分别指定OVMF_CODE.fd和OVMF_VARS.fd,也可以采用简化的写法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 分别指定
    qemu-system-x86_64 -drive if=pflash,format=raw,readonly,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF_CODE.fd \
    -drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF_VARS.fd \
    -nographic \
    -net none
    # 简化写法
    qemu-system-x86_64 -drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd \
    -nographic \
    -net none

运行 debug-x64.sh, 不出意外会出现如下界面, 即 UEFI 的 Shell

1
2
3
4
5
6
7
8
9
10
11
12
UEFI Interactive Shell v2.2
EDK II
UEFI v2.70 (EDK II, 0x00010000)
Mapping table
FS0: Alias(s):HD0a1:;BLK1:
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)
BLK0: Alias(s):
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
BLK2: Alias(s):
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
Press ESC in 2 seconds to skip startup.nsh or any other key to continue.
Shell>

在这个 shell 中输入 fs0:(注意这有一个英文冒号), 然后输入 HelloWorld.efi 运行我们的程序,预期输出”Hello World!!!”

在Shell如果按BackSpace没有反应,可以按Ctrl+H代替

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
UEFI Interactive Shell v2.2
EDK II
UEFI v2.70 (EDK II, 0x00010000)
Mapping table
FS0: Alias(s):HD0a1:;BLK1:
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)
BLK0: Alias(s):
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
BLK2: Alias(s):
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
Press ESC in 2 seconds to skip startup.nsh or any other key to continue.
Shell> fs0:
FS0:\> ls
Directory of: FS0:\
01/08/2025 22:23 82 gdb_commands.txt
01/10/2025 20:22 184,544 HelloWorld.debug
01/10/2025 20:22 5,760 HelloWorld.efi
01/10/2025 12:22 1,391 NvVars
4 File(s) 191,777 bytes
0 Dir(s)
FS0:\> HelloWorld.efi
Hello World!!!

退出qemu按CTRL+A - X

下面是aarch64版本的
debug-aarch64.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/bin/bash
set -e
trap "Exiting" INT

# environment variables
source env.sh

export GCC5_AARCH64_PREFIX=$UEFI_WORKSPACE/toolchain/gcc-arm-8.2-2019.01-x86_64-aarch64-elf/bin/aarch64-elf-

# 编译过一次就不用再编译了
cd $EDK_PATH
source edksetup.sh
build -a AARCH64 -p ArmVirtPkg/ArmVirtQemu.dsc -t GCC5 -b RELEASE

cd $APP_PATH/$INF_NAME
mkdir -p _armvirt_dbg
cd _armvirt_dbg
rm -f debug.log
# 与ubuntu22.04软件源默认的qemu不兼容,需要升级qemu版本到v8.1.5

cp $EDK_PATH/Build/ArmVirtQemu-AARCH64/RELEASE_GCC5/FV/QEMU_EFI.fd ./

mkdir -p UEFI_Disk
cp $APP_PATH/$INF_NAME/Build/DEBUG_GCC5/AARCH64/$INF_BASE_NAME.efi ./UEFI_Disk/
cp $APP_PATH/$INF_NAME/Build/DEBUG_GCC5/AARCH64/$INF_BASE_NAME.debug ./UEFI_Disk/

#qemu命令
qemu-system-aarch64 \
-machine virt,kernel_irqchip=on,gic-version=3 \
-cpu cortex-a57 -m 1G \
-drive format=raw,file=fat:rw:UEFI_Disk/ \
-bios QEMU_EFI.fd \
-net none \
-nographic

调试

gdb 调试 UEFI 程序稍稍有点麻烦, 但可以用脚本自动化一些操作, 总体流程如下:

  1. 运行 debug.sh, 然后进入 UEFI Shell 中运行一下代码(和上一小节用 qemu 运行一样的操作, 这里主要是为了在_ovmf_dbg/debug.log 中拿到 driver 启动的地址)
  2. 开另一个 terminal, 运行下面的脚本 addr.sh
  3. 在_ovmf_dbg/UEFI_Disk 目录下运行 gdb -x gdb_commands.txt
  4. 在 gdb 里面打断点, 比如 break UefiMain
  5. 添加 gdb 调试 target remote localhost:1234
  6. 运行,输入 c 跳到第一个断点位置
  7. 在 UEFI Shell 中运行你的代码

addr-x64.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#!/bin/bash
source env.sh

cd _ovmf_dbg

logfile="debug.log"

line=$(grep -oP "Loading driver at 0x[0-9a-fA-F]+ EntryPoint=0x[0-9a-fA-F]+ $INF_BASE_NAME\.efi" "$logfile" | tail -n 1)

# 使用正则表达式提取两个地址
if [[ $line =~ Loading\ driver\ at\ (0x[0-9a-fA-F]+)\ EntryPoint=(0x[0-9a-fA-F]+)\ $INF_BASE_NAME\.efi ]]; then
address0="${BASH_REMATCH[1]}"
address1="${BASH_REMATCH[2]}"
echo "Loading driver at $address0"
echo "EntryPoint=$address1"
else
echo "Error: No matching line found, maybe you need to run $INF_BASE_NAME in qemu first"
exit 0
fi

cd UEFI_Disk

# 使用 objdump 获取文件头信息并提取 .text 和 .data 的 File off
text_offset=$(objdump -h "$INF_BASE_NAME.efi" | awk '
/\.text/ {print $6} # 提取 .text 的 File off
')

data_offset=$(objdump -h "$INF_BASE_NAME.efi" | awk '
/\.data/ {print $6} # 提取 .data 的 File off
')

# 输出提取的结果
echo ".text file off: $text_offset"
echo ".data file off: $data_offset"

# 计算
text_addr=$((0x${address0#0x} + 0x${text_offset}))
data_addr=$((0x${address0#0x} + 0x${data_offset}))

# 输出结果时使用16进制格式
printf "text_addr: 0x%X data_addr: 0x%X\n" $text_addr $data_addr

rm -rf gdb_commands.txt

# 创建 gdb_commands.txt 文件并写入内容
cat <<EOL > gdb_commands.txt
file ${INF_BASE_NAME}.efi
add-symbol-file ${INF_BASE_NAME}.debug 0x$(printf "%X" $text_addr) -s .data 0x$(printf "%X" $data_addr)
EOL


# 输出文件内容确认
echo "gdb_commands.txt has been created with the following content:"
printf "\n"
cat gdb_commands.txt
printf "\n"
echo "run the following command to debug"
echo "cd _ovmf_dgb/UEFI_Disk"
echo "gdb -x gdb_commands.txt"
echo "break UefiMain"
echo "target remote localhost:1234"
echo "c"

HelloStd

另一个例子, 使用 edk-libc 实现在 UEFI 中调用标准 c 库程序

可以复制 HelloWorld.dsc,在此基础上修改 guid,然后记得修改[Components]为 HelloWorld.inf, 最后在 dsc 的[LibraryClasses]最后添加一行下面的代码

HelloStd.dsc

1
!include StdLib/StdLib.inc

接下来是 HelloStd.inf, 首先[Defines]中的 ENTRY_POINT 要改为 ShellCEntryLib, [Packages]中添加 StdLib/StdLib.dec 和 ShellPkg/ShellPkg.dec 这两个包, [LibraryClasses]中要去掉 UefiApplicationEntryPoint, 添加 LibC 和 LibStdio 这两个库, 下面是 HelloStd.inf 的声明

HelloStd.inf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Variables defined to be used during the build process
[Defines]
INF_VERSION = 1.25
BASE_NAME = HelloStd
FILE_GUID = d0956d2b-c033-45af-8ef2-76c9d30518ec
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = ShellCEntryLib

# Source code
[Sources]
HelloStd.c

# Required packages
[Packages]
MdePkg/MdePkg.dec # Contains Uefi and UefiLib
StdLib/StdLib.dec
ShellPkg/ShellPkg.dec

# Required Libraries
[LibraryClasses]
# UefiApplicationEntryPoint # Uefi application entry point
UefiLib # UefiLib
LibC
LibStdio

接着我们就可以在 UEFI 中调用标准库程序了

HelloStd.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

#include <Library/ShellCEntryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Uefi.h>
#include <stdio.h>
#include <stdlib.h>

int main(IN int Argc, IN char **Argv) {
EFI_TIME curTime;
printf("HelloStd!!!\n");
gBS->Stall(2000);
gRT->GetTime(&curTime, NULL);
printf("Current Time: %d-%d-%d %02d:%02d:%02d\n", curTime.Year, curTime.Month,
curTime.Day, curTime.Hour, curTime.Minute, curTime.Second);
return 0;
}

接着更改下 env.sh 中的 PROJ_NAME, DSC_NAME, INF_NAME, INF_BASE_NAME 即可编译,运行调试等在 HelloWorld 中描述的操作

参考文献