2 # Script to Build ArmVirtPkg 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
18 from edk2toollib
.utility_functions
import GetHostInfo
20 # ####################################################################################### #
21 # Common Configuration #
22 # ####################################################################################### #
25 class CommonPlatform():
26 ''' Common settings for this platform. Define static data here and use
27 for the different parts of stuart
29 PackagesSupported
= ("ArmVirtPkg",)
30 ArchSupported
= ("AARCH64", "ARM")
31 TargetsSupported
= ("DEBUG", "RELEASE", "NOOPT")
32 Scopes
= ('armvirt', 'edk2-build')
33 WorkspaceRoot
= os
.path
.realpath(os
.path
.join(
34 os
.path
.dirname(os
.path
.abspath(__file__
)), "..", ".."))
36 # ####################################################################################### #
37 # Configuration for Update & Setup #
38 # ####################################################################################### #
41 class SettingsManager(UpdateSettingsManager
, SetupSettingsManager
, PrEvalSettingsManager
):
43 def GetPackagesSupported(self
):
44 ''' return iterable of edk2 packages supported by this build.
45 These should be edk2 workspace relative paths '''
46 return CommonPlatform
.PackagesSupported
48 def GetArchitecturesSupported(self
):
49 ''' return iterable of edk2 architectures supported by this build '''
50 return CommonPlatform
.ArchSupported
52 def GetTargetsSupported(self
):
53 ''' return iterable of edk2 target tags supported by this build '''
54 return CommonPlatform
.TargetsSupported
56 def GetRequiredSubmodules(self
):
57 ''' return iterable containing RequiredSubmodule objects.
58 If no RequiredSubmodules return an empty iterable
62 # intentionally declare this one with recursive false to avoid overhead
63 rs
.append(RequiredSubmodule(
64 "CryptoPkg/Library/OpensslLib/openssl", False))
66 # To avoid maintenance of this file for every new submodule
67 # lets just parse the .gitmodules and add each if not already in list.
68 # The GetRequiredSubmodules is designed to allow a build to optimize
69 # the desired submodules but it isn't necessary for this repository.
70 result
= io
.StringIO()
71 ret
= RunCmd("git", "config --file .gitmodules --get-regexp path", workingdir
=self
.GetWorkspaceRoot(), outstream
=result
)
72 # Cmd output is expected to look like:
73 # submodule.CryptoPkg/Library/OpensslLib/openssl.path CryptoPkg/Library/OpensslLib/openssl
74 # submodule.SoftFloat.path ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3
76 for line
in result
.getvalue().splitlines():
77 _
, _
, path
= line
.partition(" ")
79 if path
not in [x
.path
for x
in rs
]:
80 rs
.append(RequiredSubmodule(path
, True)) # add it with recursive since we don't know
83 def SetArchitectures(self
, list_of_requested_architectures
):
84 ''' Confirm the requests architecture list is valid and configure SettingsManager
85 to run only the requested architectures.
87 Raise Exception if a list_of_requested_architectures is not supported
89 unsupported
= set(list_of_requested_architectures
) - \
90 set(self
.GetArchitecturesSupported())
91 if(len(unsupported
) > 0):
93 "Unsupported Architecture Requested: " + " ".join(unsupported
))
94 logging
.critical(errorString
)
95 raise Exception(errorString
)
96 self
.ActualArchitectures
= list_of_requested_architectures
98 def GetWorkspaceRoot(self
):
99 ''' get WorkspacePath '''
100 return CommonPlatform
.WorkspaceRoot
102 def GetActiveScopes(self
):
103 ''' return tuple containing scopes that should be active for this process '''
105 scopes
= CommonPlatform
.Scopes
106 ActualToolChainTag
= shell_environment
.GetBuildVars().GetValue("TOOL_CHAIN_TAG", "")
108 if GetHostInfo().os
.upper() == "LINUX" and ActualToolChainTag
.upper().startswith("GCC"):
109 if "AARCH64" in self
.ActualArchitectures
:
110 scopes
+= ("gcc_aarch64_linux",)
111 if "ARM" in self
.ActualArchitectures
:
112 scopes
+= ("gcc_arm_linux",)
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
134 return build_these_packages
136 def GetPlatformDscAndConfig(self
) -> tuple:
137 ''' If a platform desires to provide its DSC then Policy 4 will evaluate if
138 any of the changes will be built in the dsc.
140 The tuple should be (<workspace relative path to dsc file>, <input dictionary of dsc key value pairs>)
142 return (os
.path
.join("ArmVirtPkg", "ArmVirtQemu.dsc"), {})
145 # ####################################################################################### #
146 # Actual Configuration for Platform Build #
147 # ####################################################################################### #
150 class PlatformBuilder(UefiBuilder
, BuildSettingsManager
):
152 UefiBuilder
.__init
__(self
)
154 def AddCommandLineOptions(self
, parserObj
):
155 ''' Add command line options to the argparser '''
156 parserObj
.add_argument('-a', "--arch", dest
="build_arch", type=str, default
="AARCH64",
157 help="Optional - Architecture to build. Default = AARCH64")
159 def RetrieveCommandLineOptions(self
, args
):
160 ''' Retrieve command line options from the argparser '''
162 shell_environment
.GetBuildVars().SetValue(
163 "TARGET_ARCH", args
.build_arch
.upper(), "From CmdLine")
165 shell_environment
.GetBuildVars().SetValue(
166 "ACTIVE_PLATFORM", "ArmVirtPkg/ArmVirtQemu.dsc", "From CmdLine")
168 def GetWorkspaceRoot(self
):
169 ''' get WorkspacePath '''
170 return CommonPlatform
.WorkspaceRoot
172 def GetPackagesPath(self
):
173 ''' Return a list of workspace relative paths that should be mapped as edk2 PackagesPath '''
176 def GetActiveScopes(self
):
177 ''' return tuple containing scopes that should be active for this process '''
178 scopes
= CommonPlatform
.Scopes
179 ActualToolChainTag
= shell_environment
.GetBuildVars().GetValue("TOOL_CHAIN_TAG", "")
180 Arch
= shell_environment
.GetBuildVars().GetValue("TARGET_ARCH", "")
182 if GetHostInfo().os
.upper() == "LINUX" and ActualToolChainTag
.upper().startswith("GCC"):
183 if "AARCH64" == Arch
:
184 scopes
+= ("gcc_aarch64_linux",)
186 scopes
+= ("gcc_arm_linux",)
190 ''' Get the name of the repo, platform, or product being build '''
191 ''' Used for naming the log file, among others '''
192 # check the startup nsh flag and if set then rename the log file.
193 # this helps in CI so we don't overwrite the build log since running
194 # uses the stuart_build command.
195 if(shell_environment
.GetBuildVars().GetValue("MAKE_STARTUP_NSH", "FALSE") == "TRUE"):
196 return "ArmVirtPkg_With_Run"
199 def GetLoggingLevel(self
, loggerType
):
200 ''' Get the logging level for a given type
201 base == lowest logging level supported
202 con == Screen logging
203 txt == plain text file logging
204 md == markdown file logging
208 def SetPlatformEnv(self
):
209 logging
.debug("PlatformBuilder SetPlatformEnv")
210 self
.env
.SetValue("PRODUCT_NAME", "ArmVirtQemu", "Platform Hardcoded")
211 self
.env
.SetValue("MAKE_STARTUP_NSH", "FALSE", "Default to false")
212 self
.env
.SetValue("QEMU_HEADLESS", "FALSE", "Default to false")
215 def PlatformPreBuild(self
):
218 def PlatformPostBuild(self
):
221 def FlashRomImage(self
):
222 VirtualDrive
= os
.path
.join(self
.env
.GetValue(
223 "BUILD_OUTPUT_BASE"), "VirtualDrive")
224 os
.makedirs(VirtualDrive
, exist_ok
=True)
225 OutputPath_FV
= os
.path
.join(
226 self
.env
.GetValue("BUILD_OUTPUT_BASE"), "FV")
227 Built_FV
= os
.path
.join(OutputPath_FV
, "QEMU_EFI.fd")
230 with
open(Built_FV
, "ab") as fvfile
:
231 fvfile
.seek(0, os
.SEEK_END
)
232 additional
= b
'\0' * ((64 * 1024 * 1024)-fvfile
.tell())
233 fvfile
.write(additional
)
235 # QEMU must be on that path
237 # Unique Command and Args parameters per ARCH
238 if (self
.env
.GetValue("TARGET_ARCH").upper() == "AARCH64"):
239 cmd
= "qemu-system-aarch64"
241 args
+= " -cpu cortex-a57" # emulate cpu
242 elif(self
.env
.GetValue("TARGET_ARCH").upper() == "ARM"):
243 cmd
= "qemu-system-arm"
245 args
+= " -cpu cortex-a15" # emulate cpu
247 raise NotImplementedError()
250 args
+= " -pflash " + Built_FV
# path to fw
251 args
+= " -m 1024" # 1gb memory
254 # Serial messages out
255 args
+= " -serial stdio"
256 # Mount disk with startup.nsh
257 args
+= f
" -drive file=fat:rw:{VirtualDrive},format=raw,media=disk"
260 if (self
.env
.GetValue("QEMU_HEADLESS").upper() == "TRUE"):
261 args
+= " -display none" # no graphics
263 if (self
.env
.GetValue("MAKE_STARTUP_NSH").upper() == "TRUE"):
264 f
= open(os
.path
.join(VirtualDrive
, "startup.nsh"), "w")
265 f
.write("BOOT SUCCESS !!! \n")
267 f
.write("reset -s\n")
270 ret
= RunCmd(cmd
, args
)
272 if ret
== 0xc0000005:
273 # for some reason getting a c0000005 on successful return