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 # Configuration for Update & Setup #
22 # ####################################################################################### #
23 class SettingsManager(UpdateSettingsManager
, SetupSettingsManager
, PrEvalSettingsManager
):
25 def GetPackagesSupported(self
):
26 ''' return iterable of edk2 packages supported by this build.
27 These should be edk2 workspace relative paths '''
28 return CommonPlatform
.PackagesSupported
30 def GetArchitecturesSupported(self
):
31 ''' return iterable of edk2 architectures supported by this build '''
32 return CommonPlatform
.ArchSupported
34 def GetTargetsSupported(self
):
35 ''' return iterable of edk2 target tags supported by this build '''
36 return CommonPlatform
.TargetsSupported
38 def GetRequiredSubmodules(self
):
39 ''' return iterable containing RequiredSubmodule objects.
40 If no RequiredSubmodules return an empty iterable
44 # intentionally declare this one with recursive false to avoid overhead
45 rs
.append(RequiredSubmodule(
46 "CryptoPkg/Library/OpensslLib/openssl", False))
48 # To avoid maintenance of this file for every new submodule
49 # lets just parse the .gitmodules and add each if not already in list.
50 # The GetRequiredSubmodules is designed to allow a build to optimize
51 # the desired submodules but it isn't necessary for this repository.
52 result
= io
.StringIO()
53 ret
= RunCmd("git", "config --file .gitmodules --get-regexp path", workingdir
=self
.GetWorkspaceRoot(), outstream
=result
)
54 # Cmd output is expected to look like:
55 # submodule.CryptoPkg/Library/OpensslLib/openssl.path CryptoPkg/Library/OpensslLib/openssl
56 # submodule.SoftFloat.path ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3
58 for line
in result
.getvalue().splitlines():
59 _
, _
, path
= line
.partition(" ")
61 if path
not in [x
.path
for x
in rs
]:
62 rs
.append(RequiredSubmodule(path
, True)) # add it with recursive since we don't know
65 def SetArchitectures(self
, list_of_requested_architectures
):
66 ''' Confirm the requests architecture list is valid and configure SettingsManager
67 to run only the requested architectures.
69 Raise Exception if a list_of_requested_architectures is not supported
71 unsupported
= set(list_of_requested_architectures
) - set(self
.GetArchitecturesSupported())
72 if(len(unsupported
) > 0):
73 errorString
= ( "Unsupported Architecture Requested: " + " ".join(unsupported
))
74 logging
.critical( errorString
)
75 raise Exception( errorString
)
76 self
.ActualArchitectures
= list_of_requested_architectures
78 def GetWorkspaceRoot(self
):
79 ''' get WorkspacePath '''
80 return CommonPlatform
.WorkspaceRoot
82 def GetActiveScopes(self
):
83 ''' return tuple containing scopes that should be active for this process '''
84 return CommonPlatform
.Scopes
86 def FilterPackagesToTest(self
, changedFilesList
: list, potentialPackagesList
: list) -> list:
87 ''' Filter other cases that this package should be built
88 based on changed files. This should cover things that can't
89 be detected as dependencies. '''
90 build_these_packages
= []
91 possible_packages
= potentialPackagesList
.copy()
92 for f
in changedFilesList
:
93 # BaseTools files that might change the build
95 if os
.path
.splitext(f
) not in [".txt", ".md"]:
96 build_these_packages
= possible_packages
99 # if the azure pipeline platform template file changed
100 if "platform-build-run-steps.yml" in f
:
101 build_these_packages
= possible_packages
104 return build_these_packages
106 def GetPlatformDscAndConfig(self
) -> tuple:
107 ''' If a platform desires to provide its DSC then Policy 4 will evaluate if
108 any of the changes will be built in the dsc.
110 The tuple should be (<workspace relative path to dsc file>, <input dictionary of dsc key value pairs>)
112 dsc
= CommonPlatform
.GetDscName(",".join(self
.ActualArchitectures
))
113 return (f
"OvmfPkg/{dsc}", {})
116 # ####################################################################################### #
117 # Actual Configuration for Platform Build #
118 # ####################################################################################### #
119 class PlatformBuilder( UefiBuilder
, BuildSettingsManager
):
121 UefiBuilder
.__init
__(self
)
123 def AddCommandLineOptions(self
, parserObj
):
124 ''' Add command line options to the argparser '''
125 parserObj
.add_argument('-a', "--arch", dest
="build_arch", type=str, default
="IA32,X64",
126 help="Optional - CSV of architecture to build. IA32 will use IA32 for Pei & Dxe. "
127 "X64 will use X64 for both PEI and DXE. IA32,X64 will use IA32 for PEI and "
128 "X64 for DXE. default is IA32,X64")
130 def RetrieveCommandLineOptions(self
, args
):
131 ''' Retrieve command line options from the argparser '''
133 shell_environment
.GetBuildVars().SetValue("TARGET_ARCH"," ".join(args
.build_arch
.upper().split(",")), "From CmdLine")
134 dsc
= CommonPlatform
.GetDscName(args
.build_arch
)
135 shell_environment
.GetBuildVars().SetValue("ACTIVE_PLATFORM", f
"OvmfPkg/{dsc}", "From CmdLine")
137 def GetWorkspaceRoot(self
):
138 ''' get WorkspacePath '''
139 return CommonPlatform
.WorkspaceRoot
141 def GetPackagesPath(self
):
142 ''' Return a list of workspace relative paths that should be mapped as edk2 PackagesPath '''
145 def GetActiveScopes(self
):
146 ''' return tuple containing scopes that should be active for this process '''
147 return CommonPlatform
.Scopes
150 ''' Get the name of the repo, platform, or product being build '''
151 ''' Used for naming the log file, among others '''
152 # check the startup nsh flag and if set then rename the log file.
153 # this helps in CI so we don't overwrite the build log since running
154 # uses the stuart_build command.
155 if(shell_environment
.GetBuildVars().GetValue("MAKE_STARTUP_NSH", "FALSE") == "TRUE"):
156 return "OvmfPkg_With_Run"
159 def GetLoggingLevel(self
, loggerType
):
160 ''' Get the logging level for a given type
161 base == lowest logging level supported
162 con == Screen logging
163 txt == plain text file logging
164 md == markdown file logging
168 def SetPlatformEnv(self
):
169 logging
.debug("PlatformBuilder SetPlatformEnv")
170 self
.env
.SetValue("PRODUCT_NAME", "OVMF", "Platform Hardcoded")
171 self
.env
.SetValue("MAKE_STARTUP_NSH", "FALSE", "Default to false")
172 self
.env
.SetValue("QEMU_HEADLESS", "FALSE", "Default to false")
175 def PlatformPreBuild(self
):
178 def PlatformPostBuild(self
):
181 def FlashRomImage(self
):
182 VirtualDrive
= os
.path
.join(self
.env
.GetValue("BUILD_OUTPUT_BASE"), "VirtualDrive")
183 os
.makedirs(VirtualDrive
, exist_ok
=True)
184 OutputPath_FV
= os
.path
.join(self
.env
.GetValue("BUILD_OUTPUT_BASE"), "FV")
186 if (self
.env
.GetValue("QEMU_SKIP") and
187 self
.env
.GetValue("QEMU_SKIP").upper() == "TRUE"):
188 logging
.info("skipping qemu boot test")
192 # QEMU must be on the path
194 cmd
= "qemu-system-x86_64"
195 args
= "-debugcon stdio" # write messages to stdio
196 args
+= " -global isa-debugcon.iobase=0x402" # debug messages out thru virtual io port
197 args
+= " -net none" # turn off network
198 args
+= f
" -drive file=fat:rw:{VirtualDrive},format=raw,media=disk" # Mount disk with startup.nsh
200 if (self
.env
.GetValue("QEMU_HEADLESS").upper() == "TRUE"):
201 args
+= " -display none" # no graphics
203 if (self
.env
.GetBuildValue("SMM_REQUIRE") == "1"):
204 args
+= " -machine q35,smm=on" #,accel=(tcg|kvm)"
207 args
+= " -global driver=cfi.pflash01,property=secure,value=on"
208 args
+= " -drive if=pflash,format=raw,unit=0,file=" + os
.path
.join(OutputPath_FV
, "OVMF_CODE.fd") + ",readonly=on"
209 args
+= " -drive if=pflash,format=raw,unit=1,file=" + os
.path
.join(OutputPath_FV
, "OVMF_VARS.fd")
211 args
+= " -pflash " + os
.path
.join(OutputPath_FV
, "OVMF.fd") # path to firmware
214 if (self
.env
.GetValue("MAKE_STARTUP_NSH").upper() == "TRUE"):
215 f
= open(os
.path
.join(VirtualDrive
, "startup.nsh"), "w")
216 f
.write("BOOT SUCCESS !!! \n")
218 f
.write("reset -s\n")
221 ret
= RunCmd(cmd
, args
)
223 if ret
== 0xc0000005:
224 #for some reason getting a c0000005 on successful return