From a5f312dc86ca821b30416b599c70a711ffc31a40 Mon Sep 17 00:00:00 2001 From: Michael Turner Date: Fri, 26 May 2023 15:03:39 -0700 Subject: [PATCH] Build and Run support for a Dfci Enabled Device Under Test (#489) ## Description Fixes #470. PRE-REQ. This PR requires the following PR's before this one will work correctly: https://github.com/microsoft/mu_tiano_platforms/pull/473 https://github.com/microsoft/mu_tiano_platforms/pull/471 https://github.com/microsoft/mu_feature_dfci/pull/84 This PR will have to bump DfciPkg to include PR 84 above before it is completed. This change adds the prereqs for DFCI - that is, platform specific libraries required to interface between DFCI and a particular platform. The required DFCI libraries are in PR 471. A convenience PR for booting to FrontPage or with certain USB device mounted is also require (PR 473). - [x] Impacts functionality? - [ ] Impacts security? - [ ] Breaking change? - [ ] Includes tests? - [x] Includes documentation? ## How This Was Tested With all of the prerequisites included, a Q35 Virtual System was used to run the Dfci End to End UnitTest with satisfactory results. That is, all of the test cases ran will full success except Dfci_IntuneSettings - which exhibits some know errors. This is not fixed due to another pending change. ## Integration Instructions N/A --- DfciDutFiles/BuildDfci.py | 25 ++++++ DfciDutFiles/RunDfci.py | 66 +++++++++++++++ .../Plugins/QemuRunner/QemuRunner.py | 82 +++++++++++++++---- Platforms/QemuQ35Pkg/QemuQ35Pkg.dsc | 3 + 4 files changed, 160 insertions(+), 16 deletions(-) create mode 100644 DfciDutFiles/BuildDfci.py create mode 100644 DfciDutFiles/RunDfci.py diff --git a/DfciDutFiles/BuildDfci.py b/DfciDutFiles/BuildDfci.py new file mode 100644 index 0000000000..5fc0ae2c76 --- /dev/null +++ b/DfciDutFiles/BuildDfci.py @@ -0,0 +1,25 @@ +## +# This command make it easier to build with DFCI enabled and match the parameters with RunDfci.py. +# +# See the ReadMe.md file in this directory on how to use the BuildDfci script. +# +# Copyright (c) Microsoft Corporation +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +if __name__ == "__main__": + import os + from pathlib import Path + + dfci_directory = Path(__file__).parent.absolute() + build_directory = dfci_directory.parent.absolute() + platformbuild = os.path.join(build_directory, "Platforms", "QemuQ35Pkg", "PlatformBuild.py") + + args = " BLD_*_GUI_FRONT_PAGE=TRUE" + args += " BLD_*_NETWORK_ALLOW_HTTP_CONNECTIONS=TRUE" + args += " BLD_*_QEMU_CORE_NUM=4" + args += " BLD_*_SMM_ENABLED=TRUE" + args += " --clean" + + cmd = platformbuild + args + os.system(cmd) diff --git a/DfciDutFiles/RunDfci.py b/DfciDutFiles/RunDfci.py new file mode 100644 index 0000000000..e7da665c51 --- /dev/null +++ b/DfciDutFiles/RunDfci.py @@ -0,0 +1,66 @@ +## +# This command make it easier to boot to a Windows device under test. +# +# See the ReadMe.md file in this directory on how to use the RunDfci script. +# +# Copyright (c) Microsoft Corporation +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +if __name__ == "__main__": + import os + import argparse + from pathlib import Path + + parser = argparse.ArgumentParser(description='Start Qemu with DFCI Options') + + # Add a usb drive with OS Install Files, or Dfci Setup files + parse_group = parser.add_mutually_exclusive_group() + parse_group.add_argument("-f", "--frontpage", dest="frontpage", action='store_true', help="Boot to FrontPage") + parse_group.add_argument("-a", "--alt_boot", dest="alt_boot", action='store_true', help="Boot to Alternate source (USB/Network)") + + # Choose to boot to front page, or a usb/network device. + parse_group2 = parser.add_mutually_exclusive_group() + parse_group2.add_argument("-i", "--install", dest="install", action='store_true', help="Add drive with install files") + parse_group2.add_argument("-d", "--dfcisetup", dest="dfcisetup", action='store_true', help="Add DfciSetup directory") + + options = parser.parse_args() + + dfci_directory = Path(__file__).parent.absolute() + build_directory = dfci_directory.parent.absolute() + + platformbuild = os.path.join(build_directory, "Platforms", "QemuQ35Pkg", "PlatformBuild.py") + + dfci_var_store = os.path.join(dfci_directory, "DFCI_DUT_VARS.fd") + + args = " --FlashOnly" + args += " DFCI_VAR_STORE=" + "\"" + dfci_var_store + "\"" + + dfci_files = None + install_files = None + if options.install: + install_files = os.path.join(dfci_directory, "OsInstallFiles.vhd") + args += " INSTALL_FILES=" + "\"" + install_files + "\"" + + if options.dfcisetup: + dfci_files = os.path.join(dfci_directory, "DfciSetup") + args += " DFCI_FILES=" + "\"" + dfci_files + "\"" + + dfci_os_disk = os.path.join(dfci_directory, "Windows.vhd") + + if not os.path.exists(dfci_os_disk): + raise Exception("The Windows.vhd file must exist") + + args += " PATH_TO_OS=" + "\"" + dfci_os_disk + "\"" + + if options.frontpage: + args += " BOOT_TO_FRONT_PAGE=TRUE" + + if options.alt_boot: + args += " ALT_BOOT_ENABLE=TRUE" + + args += " BLD_*_QEMU_CORE_NUM=4" + args += " BLD_*_SMM_ENABLED=TRUE" + + cmd = platformbuild + args + os.system(cmd) diff --git a/Platforms/QemuQ35Pkg/Plugins/QemuRunner/QemuRunner.py b/Platforms/QemuQ35Pkg/Plugins/QemuRunner/QemuRunner.py index 05ec5eff6c..4c8ec93091 100644 --- a/Platforms/QemuQ35Pkg/Plugins/QemuRunner/QemuRunner.py +++ b/Platforms/QemuQ35Pkg/Plugins/QemuRunner/QemuRunner.py @@ -15,6 +15,7 @@ import subprocess import re import io +import shutil from edk2toolext.environment import plugin_manager from edk2toolext.environment.plugintypes import uefi_helper_plugin from edk2toollib import utility_functions @@ -69,15 +70,6 @@ def Runner(env): args += " -global isa-debugcon.iobase=0x402" # Turn off S3 support args += " -global ICH9-LPC.disable_s3=1" - # turn off network - args += " -net none" - # Mount disk with startup.nsh - if os.path.isfile(VirtualDrive): - args += f" -hdd {VirtualDrive}" - elif os.path.isdir(VirtualDrive): - args += f" -drive file=fat:rw:{VirtualDrive},format=raw,media=disk" - else: - logging.critical("Virtual Drive Path Invalid") if env.GetBuildValue("SMM_ENABLED") is None or env.GetBuildValue("SMM_ENABLED").lower() == "true": smm_enabled = "on" @@ -94,29 +86,87 @@ def Runner(env): accel = ",accel=whpx" args += " -machine q35,smm=" + smm_enabled + accel - if env.GetValue("PATH_TO_OS") is not None: + path_to_os = env.GetValue("PATH_TO_OS") + if path_to_os is not None: # Potentially dealing with big daddy, give it more juice... args += " -m 8192" - args += " -hda \"" + env.GetValue("PATH_TO_OS") + "\"" + #args += " -hda \"" + path_to_os + "\"" + args += " -drive format=raw,index=0,media=disk,file=\"" + path_to_os + "\"" else: args += " -m 2048" - args += " -cpu qemu64,+rdrand,umip,+smep,+popcnt" # most compatible x64 CPU model + RDRAND + UMIP + SMEP + POPCNT support (not included by default) + + #args += " -cpu qemu64,+rdrand,umip,+smep,+popcnt" # most compatible x64 CPU model + RDRAND + UMIP + SMEP +POPCNT support (not included by default) + args += " -cpu qemu64,rdrand=on,umip=on,smep=on,pdpe1gb=on,popcnt=on" # most compatible x64 CPU model + RDRAND + UMIP + SMEP + PDPE1GB + POPCNT support (not included by default) + if env.GetBuildValue ("QEMU_CORE_NUM") is not None: args += " -smp " + env.GetBuildValue ("QEMU_CORE_NUM") if smm_enabled == "on": args += " -global driver=cfi.pflash01,property=secure,value=on" args += " -drive if=pflash,format=raw,unit=0,file=" + \ os.path.join(OutputPath_FV, "QEMUQ35_CODE.fd") + ",readonly=on" - args += " -drive if=pflash,format=raw,unit=1,file=" + \ - os.path.join(OutputPath_FV, "QEMUQ35_VARS.fd") + + orig_var_store = os.path.join(OutputPath_FV, "QEMUQ35_VARS.fd") + dfci_var_store =env.GetValue("DFCI_VAR_STORE") + if dfci_var_store is not None: + if not os.path.isfile(dfci_var_store): + shutil.copy(orig_var_store, dfci_var_store) + use_this_varstore = dfci_var_store + else: + use_this_varstore = orig_var_store + args += " -drive if=pflash,format=raw,unit=1,file=" + use_this_varstore # Add XHCI USB controller and mouse args += " -device qemu-xhci,id=usb" args += " -device usb-mouse,id=input0,bus=usb.0,port=1" # add a usb mouse #args += " -device usb-kbd,id=input1,bus=usb.0,port=2" # add a usb keyboar + + dfci_files = env.GetValue("DFCI_FILES") + if dfci_files is not None: + args += f" -drive file=fat:rw:{dfci_files},format=raw,media=disk,if=none,id=dfci_disk" + args += " -device usb-storage,bus=usb.0,drive=dfci_disk" + + install_files = env.GetValue("INSTALL_FILES") + if install_files is not None: + args += f" -drive file={install_files},format=raw,media=disk,if=none,id=install_disk" + args += " -device usb-storage,bus=usb.0,drive=install_disk" + + boot_selection = '' + boot_to_front_page = env.GetValue("BOOT_TO_FRONT_PAGE") + if boot_to_front_page is not None: + if (boot_to_front_page.upper() == "TRUE"): + boot_selection += ",version=Vol+" + + alt_boot_enable = env.GetValue("ALT_BOOT_ENABLE") + if alt_boot_enable is not None: + if alt_boot_enable.upper() == "TRUE": + boot_selection += ",version=Vol-" + + # If DFCI_VAR_STORE is enabled, don't enable the Virtual Drive, and enable the network + dfci_var_store = env.GetValue("DFCI_VAR_STORE") + if dfci_var_store is None: + # turn off network + args += " -net none" + # Mount disk with startup.nsh + if os.path.isfile(VirtualDrive): + args += f" -hdd {VirtualDrive}" + elif os.path.isdir(VirtualDrive): + args += f" -drive file=fat:rw:{VirtualDrive},format=raw,media=disk" + else: + logging.critical("Virtual Drive Path Invalid") + else: + if boot_to_front_page is None: + # Booting to Windows, use a PCI nic + args += " -device e1000,netdev=net0" + else: + # Booting to UEFI, use virtio-net-pci + args += " -device virtio-net-pci,netdev=net0" + + # forward ports for robotframework 8270 and 8271 + args += " -netdev user,id=net0,hostfwd=tcp::8270-:8270,hostfwd=tcp::8271-:8271" + args += " -smbios type=0,vendor=Palindrome,uefi=on" - args += " -smbios type=1,manufacturer=Palindrome,product=MuQemuQ35,serial=42-42-42-42" - args += f" -smbios type=3,manufacturer=Palindrome,version={version},serial=42-42-42-42,asset=Q35,sku=Q35" + args += " -smbios type=1,manufacturer=Palindrome,product=MuQemuQ35,serial=42-42-42-42,uuid=9de555c0-05d7-4aa1-84ab-bb511e3a8bef" + args += f" -smbios type=3,manufacturer=Palindrome,serial=42-42-42-42{boot_selection}" if (env.GetValue("QEMU_HEADLESS").upper() == "TRUE"): args += " -display none" # no graphics diff --git a/Platforms/QemuQ35Pkg/QemuQ35Pkg.dsc b/Platforms/QemuQ35Pkg/QemuQ35Pkg.dsc index defb220cc2..04d034c883 100644 --- a/Platforms/QemuQ35Pkg/QemuQ35Pkg.dsc +++ b/Platforms/QemuQ35Pkg/QemuQ35Pkg.dsc @@ -37,6 +37,9 @@ DEFINE PEI_MM_IPL_ENABLED = TRUE DEFINE GUI_FRONT_PAGE = FALSE + DEFINE NETWORK_HTTP_ENABLE = TRUE + DEFINE NETWORK_ALLOW_HTTP_CONNECTIONS = TRUE + # Configure Shared Crypto !ifndef ENABLE_SHARED_CRYPTO # by default true ENABLE_SHARED_CRYPTO = TRUE