在前面的章節既然學會了該如何編譯 OvmfPkg, 乾脆來做一隻傳說中每個寫 code 的人都會寫的 Hello World
吧。
1 Package
在 UEFI 裡面,基本一個 Package 會包含著 *.dsc
*.dec
*.inf
還有 source code *.c
與 *.h
所組成。
另外還有 *.fdf
用來描述 BIOS ROM 的 layout 我們後面再說。
*.dsc
用來描述有那些 inf 要被包含在這個 Package
*.dec
一些 include file 的路徑,GUID 的設定
*.inf
一隻 driver/application 的基本單位,也是我們今天要寫的 Hello World
的主角
2 建立第一個 Package - DSC
在抓好的 edk2 的根目錄中,先建立一個目錄名為 SimonPkg
,開啟任何你熟悉的文字編輯程式,在裡面放 SimonPkg.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
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
## @file
# Example package dsc file
#
# Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.<BR>
#
[Defines]
PLATFORM_NAME = SimonPkg
PLATFORM_GUID = B04C9282-097B-4616-8712-1E4E5502A1E7
PLATFORM_VERSION = 0.01
DSC_SPECIFICATION = 0x00010006
OUTPUT_DIRECTORY = Build/SimonPkg
SUPPORTED_ARCHITECTURES = IA32|X64
BUILD_TARGETS = DEBUG|RELEASE|NOOPT
SKUID_IDENTIFIER = DEFAULT
#
# Debug output control
#
!if $(TARGET) == DEBUG
DEFINE DEBUG_ENABLE_OUTPUT = TRUE # Set to TRUE to enable debug output
DEFINE DEBUG_PRINT_ERROR_LEVEL = 0x80000044 # Flags to control amount of debug output
DEFINE DEBUG_PROPERTY_MASK = 0x2F
!else
DEFINE DEBUG_ENABLE_OUTPUT = FALSE # Set to TRUE to enable debug output
DEFINE DEBUG_PRINT_ERROR_LEVEL = 0x80000040 # Flags to control amount of debug output
DEFINE DEBUG_PROPERTY_MASK = 0
!endif
[PcdsFixedAtBuild]
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|$(DEBUG_PROPERTY_MASK)
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|$(DEBUG_PRINT_ERROR_LEVEL)
gEfiMdePkgTokenSpaceGuid.PcdReportStatusCodePropertyMask|0x07
[LibraryClasses]
#
# Entry Point Libraries
#
UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
!if $(DEBUG_ENABLE_OUTPUT)
DebugLib|IntelFrameworkModulePkg/Library/PeiDxeDebugLibReportStatusCode/PeiDxeDebugLibReportStatusCode.inf
ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
!else ## DEBUG_ENABLE_OUTPUT
DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
!endif ## DEBUG_ENABLE_OUTPUT
DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
###################################################################################################
#
# Components Section - list of the modules and components that will be processed by compilation
# tools and the EDK II tools to generate PE32/PE32+/Coff image files.
#
# Note: The EDK II DSC file is not used to specify how compiled binary images get placed
# into firmware volume images. This section is just a list of modules to compile from
# source into UEFI-compliant binaries.
# It is the FDF file that contains information on combining binary files into firmware
# volume images, whose concept is beyond UEFI and is described in PI specification.
# Binary modules do not need to be listed in this section, as they should be
# specified in the FDF file. For example: Shell binary (Shell_Full.efi), FAT binary (Fat.efi),
# Logo (Logo.bmp), and etc.
# There may also be modules listed in this section that are not required in the FDF file,
# When a module listed here is excluded from FDF file, then UEFI-compliant binary will be
# generated for it, but the binary will not be put into any firmware volume.
#
###################################################################################################
[Components]
SimonPkg/Applications/HelloWorld/HelloWorld.inf
##############################################################################
#
# Specify whether we are running in an emulation environment, or not.
# Define EMULATE if we are, else keep the DEFINE commented out.
#
# DEFINE EMULATE = 1
##############################################################################
#
# Include Boilerplate text required for building with the Standard Libraries.
#
##############################################################################
#!include StdLib/StdLib.inc
|
在這支檔案裏面,會區分好幾個區域以 [ ]
組成,在 裡面我們需要注意的有
2.1 DSC - [Defines]
PLATFORM_NAME
取一個獨特的 Package name, 通常會以 Pkg
結尾
PLATFORM_GUID
GUID 是很常見用來作為一個獨特 ID 以避免混淆的做法,建議使用 GUID 產生器不要手動亂修
其他欄位我們還用不到就先照抄
2.2 DSC - [PcdsFixedAtBuild]
先照抄
2.3 DSC - [LibraryClasses]
這邊我們需要告訴 Build tool,我的 Package 需要拉那些 Library 來用,|
前面是 Library 的 BaseName, 後面是實體(Instance)。先照抄這些 Library 就好
2.4 DSC - [Components]
這邊就是重頭戲了,需輸入 你想要編譯的那隻 driver/application,這邊我們先填上
1
|
SimonPkg/Applications/HelloWorld/HelloWorld.inf
|
作為我們的第一支 application。若有多隻 inf 可以每行輸入一隻即可。到這邊 DSC 檔就大致結束。
3 建立第一個 Package - DEC
DEC 檔可以用來設定很多關於這支 Package 通用的一些設定,不過我們現在的 driver 太簡單還不需要太多的東西,先簡單填一下就好。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
## @file
# Example package dec file
#
# Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.<BR>
#
[Defines]
DEC_SPECIFICATION = 0x00010005
PACKAGE_NAME = SimonPkg
PACKAGE_GUID = 39FBA1D6-6DCE-4a75-B150-017A53B4548B
PACKAGE_VERSION = 0.01
[Includes]
Include
|
3.1 DEC - [Defines]
與 DSC 的作法雷同。
3.2 DEC - [Includes]
用來設定當 compile 想要尋找 include header files 的路徑。 先填上 Include 或是先不填也可以。
其餘欄位先不談照貼即可。
4 建立第一個 Package - INF
做完前面的前置步驟,終於到了我們的主角 INF 檔了,還記得前面填在 [Components]
欄位中的 SimonPkg/Applications/HelloWorld/HelloWorld.inf
嗎? 我們就按照這個路徑建立好目錄吧。
提示
注意到了嗎 到目前為止所有的路徑都是 /
喔,為了跨平台也能正常 build code,請習慣使用 /
取代 \
來做為路徑的分隔符號。
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
|
## @file
# A simple, basic, application showing how the Hello application
#
# Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.<BR>
#
[Defines]
INF_VERSION = 0x00010006
BASE_NAME = HelloWorld
FILE_GUID = FDBA06BF-0785-40fe-9266-4A9BE8731C24
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 0.1
ENTRY_POINT = UefiMain
#
# VALID_ARCHITECTURES = IA32 X64
#
[Sources]
HelloWorld.c
[Packages]
MdePkg/MdePkg.dec
[LibraryClasses]
UefiApplicationEntryPoint
UefiLib
|
4.1 INF - [Defines]
基本上跟之前的都一樣, 需要注意的是
MODULE_TYPE
若是要寫一個可以在 shell 下執行的 application,請填上 UEFI_APPLICATION
ENTRY_POINT
指定 entry point 的 function name,先填上 UefiMain
4.2 INF - [Sources]
列出所有需要被 compile 的 *.c
與 *.h
(optional)
4.2 INF - [Packages]
列出所有需要被參考的 definietion,Library, etc …
4.2 INF - [LibraryClasses]
列出所有需要被參考的 library。 該 library 所屬的 Package 需要列在 [Packages]
欄位。
到了這裡,所有用來編譯 Package 的環境已經完成,接下來要來寫程式的本體啦。
5 我的第一支 HelloWorld.c
會看到這邊來,或多或少都應該有一點點 c 語言的底子,所以直接上 code 就不贅述了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# @file
# A simple, basic, application showing how the Hello application
#
# Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.<BR>
#
#include <Library/UefiLib.h>
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
Print(L"Hello World\n");
return EFI_SUCCESS;
}
|
大概就是一個這麼簡單 … 都做完後
1
|
build -a X64 -t GCC5 -p SimonPkg/SimonPkg.dsc -b DEBUG
|
注意到了嗎? 我只是修改了 Package (-p
)的路徑就可以改成編譯我自己的 package 了喔。 如果沒有問題,產生出來的執行檔會放在 Build/SimonPkg/DEBUG_GCC5/X64/SimonPkg/Applications/HelloWorld/HelloWorld/OUTPUT/HelloWorld.efi
拿到真實的電腦跑看看吧!!
小記
在一般近幾年的電腦上,你的 SecureBoot 設定可能是預設啟動的,這會造成你可能無法進入 shell 環境,記得進 BIOS Setup 將 SecureBoot 關閉再試試看喔。