2 # Script to Build OVMF UEFI firmware
4 # Copyright (c) Microsoft Corporation.
5 # SPDX-License-Identifier: BSD-2-Clause-Patent
11 from edk2toolext
.environment
import shell_environment
12 from edk2toolext
.environment
.uefi_build
import UefiBuilder
13 from edk2toolext
.invocables
.edk2_platform_build
import BuildSettingsManager
14 from edk2toolext
.invocables
.edk2_setup
import SetupSettingsManager
, RequiredSubmodule
15 from edk2toolext
.invocables
.edk2_update
import UpdateSettingsManager
16 from edk2toolext
.invocables
.edk2_pr_eval
import PrEvalSettingsManager
17 from edk2toollib
.utility_functions
import RunCmd
20 # ####################################################################################### #
21 # Common Configuration #
22 # ####################################################################################### #
23 class CommonPlatform():
24 ''' Common settings for this platform. Define static data here and use
25 for the different parts of stuart
27 PackagesSupported
= ("OvmfPkg",)
28 ArchSupported
= ("IA32", "X64")
29 TargetsSupported
= ("DEBUG", "RELEASE", "NOOPT")
30 Scopes
= ('ovmf', 'edk2-build')
31 WorkspaceRoot
= os
.path
.realpath(os
.path
.join(
32 os
.path
.dirname(os
.path
.abspath(__file__
)), "..", ".."))
35 def GetDscName(cls
, ArchCsv
: str) -> str:
36 ''' return the DSC given the architectures requested.
38 ArchCsv: csv string containing all architectures to build
41 if "IA32" in ArchCsv
.upper().split(","):
43 if "X64" in ArchCsv
.upper().split(","):
49 # ####################################################################################### #
50 # Configuration for Update & Setup #
51 # ####################################################################################### #
52 class SettingsManager(UpdateSettingsManager
, SetupSettingsManager
, PrEvalSettingsManager
):
54 def GetPackagesSupported(self
):
55 ''' return iterable of edk2 packages supported by this build.
56 These should be edk2 workspace relative paths '''
57 return CommonPlatform
.PackagesSupported
59 def GetArchitecturesSupported(self
):
60 ''' return iterable of edk2 architectures supported by this build '''
61 return CommonPlatform
.ArchSupported
63 def GetTargetsSupported(self
):
64 ''' return iterable of edk2 target tags supported by this build '''
65 return CommonPlatform
.TargetsSupported
67 def GetRequiredSubmodules(self
):
68 ''' return iterable containing RequiredSubmodule objects.
69 If no RequiredSubmodules return an empty iterable
73 # intentionally declare this one with recursive false to avoid overhead
74 rs
.append(RequiredSubmodule(
75 "CryptoPkg/Library/OpensslLib/openssl", False))
77 # To avoid maintenance of this file for every new submodule
78 # lets just parse the .gitmodules and add each if not already in list.
79 # The GetRequiredSubmodules is designed to allow a build to optimize
80 # the desired submodules but it isn't necessary for this repository.
81 result
= io
.StringIO()
82 ret
= RunCmd("git", "config --file .gitmodules --get-regexp path", workingdir
=self
.GetWorkspaceRoot(), outstream
=result
)
83 # Cmd output is expected to look like:
84 # submodule.CryptoPkg/Library/OpensslLib/openssl.path CryptoPkg/Library/OpensslLib/openssl
85 # submodule.SoftFloat.path ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3
87 for line
in result
.getvalue().splitlines():
88 _
, _
, path
= line
.partition(" ")
90 if path
not in [x
.path
for x
in rs
]:
91 rs
.append(RequiredSubmodule(path
, True)) # add it with recursive since we don't know
94 def SetArchitectures(self
, list_of_requested_architectures
):
95 ''' Confirm the requests architecture list is valid and configure SettingsManager
96 to run only the requested architectures.
98 Raise Exception if a list_of_requested_architectures is not supported
100 unsupported
= set(list_of_requested_architectures
) - set(self
.GetArchitecturesSupported())
101 if(len(unsupported
) > 0):
102 errorString
= ( "Unsupported Architecture Requested: " + " ".join(unsupported
))
103 logging
.critical( errorString
)
104 raise Exception( errorString
)
105 self
.ActualArchitectures
= list_of_requested_architectures
107 def GetWorkspaceRoot(self
):
108 ''' get WorkspacePath '''
109 return CommonPlatform
.WorkspaceRoot
111 def GetActiveScopes(self
):
112 ''' return tuple containing scopes that should be active for this process '''
113 return CommonPlatform
.Scopes
115 def FilterPackagesToTest(self
, changedFilesList
: list, potentialPackagesList
: list) -> list:
116 ''' Filter other cases that this package should be built
117 based on changed files. This should cover things that can't
118 be detected as dependencies. '''
119 build_these_packages
= []
120 possible_packages
= potentialPackagesList
.copy()
121 for f
in changedFilesList
:
122 # BaseTools files that might change the build
124 if os
.path
.splitext(f
) not in [".txt", ".md"]:
125 build_these_packages
= possible_packages
128 # if the azure pipeline platform template file changed
129 if "platform-build-run-steps.yml" in f
:
130 build_these_packages
= possible_packages
133 return build_these_packages
135 def GetPlatformDscAndConfig(self
) -> tuple:
136 ''' If a platform desires to provide its DSC then Policy 4 will evaluate if
137 any of the changes will be built in the dsc.
139 The tuple should be (<workspace relative path to dsc file>, <input dictionary of dsc key value pairs>)
141 dsc
= CommonPlatform
.GetDscName(",".join(self
.ActualArchitectures
))
142 return (f
"OvmfPkg/{dsc}", {})
145 # ####################################################################################### #
146 # Actual Configuration for Platform Build #
147 # ####################################################################################### #
148 class PlatformBuilder( UefiBuilder
, BuildSettingsManager
):
150 UefiBuilder
.__init
__(self
)
152 def AddCommandLineOptions(self
, parserObj
):
153 ''' Add command line options to the argparser '''
154 parserObj
.add_argument('-a', "--arch", dest
="build_arch", type=str, default
="IA32,X64",
155 help="Optional - CSV of architecture to build. IA32 will use IA32 for Pei & Dxe. "
156 "X64 will use X64 for both PEI and DXE. IA32,X64 will use IA32 for PEI and "
157 "X64 for DXE. default is IA32,X64")
159 def RetrieveCommandLineOptions(self
, args
):
160 ''' Retrieve command line options from the argparser '''
162 shell_environment
.GetBuildVars().SetValue("TARGET_ARCH"," ".join(args
.build_arch
.upper().split(",")), "From CmdLine")
163 dsc
= CommonPlatform
.GetDscName(args
.build_arch
)
164 shell_environment
.GetBuildVars().SetValue("ACTIVE_PLATFORM", f
"OvmfPkg/{dsc}", "From CmdLine")
166 def GetWorkspaceRoot(self
):
167 ''' get WorkspacePath '''
168 return CommonPlatform
.WorkspaceRoot
170 def GetPackagesPath(self
):
171 ''' Return a list of workspace relative paths that should be mapped as edk2 PackagesPath '''
174 def GetActiveScopes(self
):
175 ''' return tuple containing scopes that should be active for this process '''
176 return CommonPlatform
.Scopes
179 ''' Get the name of the repo, platform, or product being build '''
180 ''' Used for naming the log file, among others '''
181 # check the startup nsh flag and if set then rename the log file.
182 # this helps in CI so we don't overwrite the build log since running
183 # uses the stuart_build command.
184 if(shell_environment
.GetBuildVars().GetValue("MAKE_STARTUP_NSH", "FALSE") == "TRUE"):
185 return "OvmfPkg_With_Run"
188 def GetLoggingLevel(self
, loggerType
):
189 ''' Get the logging level for a given type
190 base == lowest logging level supported
191 con == Screen logging
192 txt == plain text file logging
193 md == markdown file logging
197 def SetPlatformEnv(self
):
198 logging
.debug("PlatformBuilder SetPlatformEnv")
199 self
.env
.SetValue("PRODUCT_NAME", "OVMF", "Platform Hardcoded")
200 self
.env
.SetValue("MAKE_STARTUP_NSH", "FALSE", "Default to false")
201 self
.env
.SetValue("QEMU_HEADLESS", "FALSE", "Default to false")
204 def PlatformPreBuild(self
):
207 def PlatformPostBuild(self
):
210 def FlashRomImage(self
):
211 VirtualDrive
= os
.path
.join(self
.env
.GetValue("BUILD_OUTPUT_BASE"), "VirtualDrive")
212 os
.makedirs(VirtualDrive
, exist_ok
=True)
213 OutputPath_FV
= os
.path
.join(self
.env
.GetValue("BUILD_OUTPUT_BASE"), "FV")
216 # QEMU must be on the path
218 cmd
= "qemu-system-x86_64"
219 args
= "-debugcon stdio" # write messages to stdio
220 args
+= " -global isa-debugcon.iobase=0x402" # debug messages out thru virtual io port
221 args
+= " -net none" # turn off network
222 args
+= f
" -drive file=fat:rw:{VirtualDrive},format=raw,media=disk" # Mount disk with startup.nsh
224 if (self
.env
.GetValue("QEMU_HEADLESS").upper() == "TRUE"):
225 args
+= " -display none" # no graphics
227 if (self
.env
.GetBuildValue("SMM_REQUIRE") == "1"):
228 args
+= " -machine q35,smm=on" #,accel=(tcg|kvm)"
231 args
+= " -global driver=cfi.pflash01,property=secure,value=on"
232 args
+= " -drive if=pflash,format=raw,unit=0,file=" + os
.path
.join(OutputPath_FV
, "OVMF_CODE.fd") + ",readonly=on"
233 args
+= " -drive if=pflash,format=raw,unit=1,file=" + os
.path
.join(OutputPath_FV
, "OVMF_VARS.fd")
235 args
+= " -pflash " + os
.path
.join(OutputPath_FV
, "OVMF.fd") # path to firmware
238 if (self
.env
.GetValue("MAKE_STARTUP_NSH").upper() == "TRUE"):
239 f
= open(os
.path
.join(VirtualDrive
, "startup.nsh"), "w")
240 f
.write("BOOT SUCCESS !!! \n")
242 f
.write("reset -s\n")
245 ret
= RunCmd(cmd
, args
)
247 if ret
== 0xc0000005:
248 #for some reason getting a c0000005 on successful return