目錄

我的第一支 edk2 Application

在前面的章節既然學會了該如何編譯 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 關閉再試試看喔。