Cover image for Building a UEFI Development Environment Based on edk2 on Linux

Building a UEFI Development Environment Based on edk2 on Linux

Words 2.8k
Views
Visitors

Timeline

Timeline

2025-01-10

init

This article introduces the complete process of building a UEFI development environment based on edk2 in a Linux environment, including basic environment configuration, source code download, and compilation tool installation. Through HelloWorld and HelloStd examples, it explains in detail the writing of DSC and INF configuration files, the implementation of compilation scripts for x64 and aarch64 platforms, and demonstrates specific methods for running programs using Emulator and QEMU and debugging with GDB.

Basic Environment Setup

This is my hardware environment and operating system

Hardware environment
Hardware environment

Download edk2 source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Install required packages
sudo apt update
sudo apt install git
mkdir -p ~/UEFI
cd UEFI
git clone "https://github.com/tianocore/edk2.git"
cd edk2
# Use this branch
git checkout origin/stable/202408
git submodule update --init --recursive
git branch
# Check if all submodules are properly initialized; if submodules are not fully downloaded, there will be some issues during compilation
git submodule status
cd -
# Download the edk2-libc code, which is mainly for using the C standard library in UEFI development
git clone https://github.com/tianocore/edk2-libc.git
# Create a code folder to store our own code
mkdir -p code

Install compilation tools

1
2
3
4
5
6
7
8
# Download some basic packages
sudo apt-get install python3 python3-distutils uuid-dev build-essential bison flex nasm acpica-tools gcc
# Install the ARM compiler, mainly for compiling 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

Below, through a HelloWorld example, we will implement compiling UEFI code for target platforms x64 or aarch64, support running in Emulator and qemu, and finally debug the program with gdb

Code

1
2
3
4
5
touch HelloWorld.dsc
touch HelloWorld.inf
touch HelloWorld.c
# This command-line tool can generate UUIDs; the UUIDs in the DSC and INF files below are all generated this way
uuidgen

HelloWorld.dsc

The DSC file is a package description file, in whichDefinesall fields are mandatory.

The paths in LibraryClasses can be found using the following command

1
2
3
4
5
cd edk2
# Take UefiApplicationEntryPoint as an example
grep UefiApplicationEntryPoint -r ./ --include=*.inf | grep LIBRARY_CLASS
# Search by GUID
grep -i 752F3136 -r ./ --exclude-dir=Build

The format of LibraryClasses is

1
LibraryClassName|Path/To/LibInstanceName.inf

For a complete explanation of the DSC file, refer to the following link:

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

The INF file is the configuration file for the edk2 app, in which

  • [Defines] This section defines some basic information of the module
    • BASE_NAME the name of the app
    • FILE_GUID can be generated via the uuidgen command; UEFI uses GUIDs to distinguish different modules
    • MODULE_TYPE fill in UEFI here_APPLICATION
    • ENTRY_POINT the name of the main function in the C code
  • [Sources] Module source code, usually .c, .h files
  • [Packages] Packages to be used
  • [LibraryClasses] Libraries to be used

For a complete explanation of the INF file, refer to the following link:

Below is a defined simple module

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;
}

Build script

First, we need to create a script to set environment variables

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
# Project name, also the source file directory of the source code
export PROJ_NAME="HelloWorld"
# dsc file name
export DSC_NAME="HelloWorld"
# inf file name
export INF_NAME="HelloWorld"
# Also the name of the compiled *.efi, defined in the BASE_NAME of the inf
export INF_BASE_NAME="HelloWorld"
# UEFI workspace directory
export UEFI_WORKSPACE="$HOME/UEFI"
# EDK II path
export EDK_PATH="$UEFI_WORKSPACE/edk2"
# EDK II libc path
export EDK_LIBC_PATH="$UEFI_WORKSPACE/edk2-libc"
# Application code path
export APP_PATH="$UEFI_WORKSPACE/code/$PROJ_NAME"
# Build output directory
export PKG_OUTPUT_DIR="$APP_PATH/Build"
# Emulator path
export EMULATOR_PATH="$EDK_PATH/Build/EmulatorX64/DEBUG_GCC5/X64"
# Package path setting, supports multiple paths, separated by colons
export PACKAGES_PATH="$EDK_PATH:$EDK_LIBC_PATH:$APP_PATH"
# Specify Python interpreter
export PYTHON_COMMAND="/usr/bin/python3"
# Confirm settings are complete
echo "Environment variables for $PROJ_NAME project are configured."

Next, write a script to compile our code to the x64 target platform

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
# Here set the -b parameter to DEBUG, use RELEASE when deploying
# -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

Compiling to the aarch64 platform is similar

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

Run

Running the Emulator

Finally, let’s write a script to run it on the emulator that comes with edk2, note thatThis requires you to have a GUI environment, if you only have the command line, skip this step and look at the next section on running with 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 compilation, once compiled you don't need to compile it again
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 run

First compile and install qemu, here I choose version 8.1.5, if yours does not achieve the expected effect you can consider using this version of 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

Next, write a script to run with qemu, some parameters here are for using gdb to debug the program in the next section, but if you just want to run it with qemu it doesn’t matter

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
# Once compiled, there is no need to compile it again
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
# It is incompatible with the default qemu in the Ubuntu 22.04 software source; you need to upgrade the qemu version to 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 enables GDB debugging, listening on 127.0.0.1:1234 by default
# -bios OVMF.fd, specifies the OVMF firmware file, which is a QEMU firmware that supports UEFI.
# -debugcon file:debug.log redirects debug output to the debug.log file.
# -global isa-debugcon.iobase=0x402 configures the I/O base address of the debug console.


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

This script will first compile OVMF (Open Virtual Machine Firmware), OVMF is a firmware based on EDKII that can run under qemu x86-64 virtual machines. This makes debugging and experimenting with UEFI firmware much easier; whether for testing OS booting, or using the (built-in) EFI shell.

OVMF firmware (UEFI implementation for QEMU) is divided into two files:

  • OVMF_CODE.fd: contains the actual UEFI firmware.
  • OVMF_VARS.fd: serves as a ‘template’ for emulating persistent NVRAM storage.
    All virtual machine instances can share the system-wide read-only OVMF from the ovmf package_CODE.fd file, but each instance needs a private, writable OVMF_VARS.fd copy.
    In qemu, you can specify OVMF_CODE.fd and OVMF_VARS.fd separately, or you can use a simplified notation:
1
2
3
4
5
6
7
8
9
# Specify separately
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
# Simplified notation
qemu-system-x86_64 -drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd \
-nographic \
-net none

Run debug-x64.sh, and if all goes well, the following interface will appear, which is the 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>

In this shell, type fs0: (note the English colon), then type HelloWorld.efi to run our program, expecting the output “Hello World!!!”

If pressing BackSpace in the Shell doesn’t work, you can press Ctrl+H instead

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!!!

To exit qemu, pressCTRL+A - X

Below is the aarch64 version
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-

# Once compiled, there is no need to compile it again
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
# It is incompatible with the default qemu in the Ubuntu 22.04 software source; you need to upgrade the qemu version to 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 command
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

Debugging

Debugging UEFI programs with gdb is slightly troublesome, but you can use scripts to automate some operations. The overall process is as follows:

  1. Run debug.sh, then enter the UEFI Shell and run the following code (the same operation as running with qemu in the previous section, mainly to get the driver startup address in _ovmf_dbg/debug.log to get the driver startup address)
  2. Open another terminal and run the following script addr.sh
  3. In _ovmf_dbg/UEFI_Run gdb -x gdb in the Disk directory_commands.txt
  4. Set a breakpoint in gdb, e.g., break UefiMain
  5. Add gdb debug target remote localhost:1234
  6. Run, type c to jump to the first breakpoint
  7. Run your code in the 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)

# Use regular expressions to extract two addresses
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

# Use objdump to get the file header information and extract the File off of .text and .data
text_offset=$(objdump -h "$INF_BASE_NAME.efi" | awk '
/\.text/ {print $6} # Extract the File off of .text
')

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

# Output the extracted results
echo ".text file off: $text_offset"
echo ".data file off: $data_offset"

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

# Use hexadecimal format when outputting the results
printf "text_addr: 0x%X data_addr: 0x%X\n" $text_addr $data_addr

rm -rf gdb_commands.txt

# Create the gdb_commands.txt file and write content to it
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


# Output file content confirmation
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

Another example, using edk-libc to implement calling standard C library programs in UEFI

You can copy HelloWorld.dsc, modify the guid based on it, then remember to modify [Components] to HelloWorld.inf, and finally in the dsc’s [LibraryClasses] add a line of the following code at the end

HelloStd.dsc

1
!include StdLib/StdLib.inc

Next is HelloStd.inf, first [Defines]'s ENTRY_POINT needs to be changed to ShellCEntryLib, [Packages] add these two packages: StdLib/StdLib.dec and ShellPkg/ShellPkg.dec, [LibraryClasses] remove UefiApplicationEntryPoint, add these two libraries: LibC and LibStdio, below is the declaration of 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

Then we can call standard library programs in 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;
}

Next, change the PROJ in env.sh_NAME, DSC_NAME, INF_NAME, INF_BASE_NAME to compile, and the operations like running and debugging are described in HelloWorld

References